diff --git a/.gitignore b/.gitignore index 85bd82b7ff..d8cacc6b13 100644 --- a/.gitignore +++ b/.gitignore @@ -322,3 +322,6 @@ Kopie von* src/SmartStoreNET.Packager.sln Log.txt src/Presentation/SmartStore.Web/Themes/FlexMuseo/ + +# Used by SSL certificates management tools (e.g. Certify The Web). +/src/Presentation/SmartStore.Web/.well-known diff --git a/CREDITS.txt b/CREDITS.txt index 5ea4c4a6fb..ff0d418111 100644 --- a/CREDITS.txt +++ b/CREDITS.txt @@ -135,11 +135,11 @@ Copyright: Copyright (c) 2007 James Newton-King License: MIT -GeoLite Free by MaxMind +DB-IP Country Lite by Eris Networks S.A.S --------------------------------------------- -Website: http://www.maxmind.com -Copyright: Copyright (c) 2014 Maxmind -License: Creative Commons Attribution-ShareAlike 3.0 Unported License +https://db-ip.com/ +Copyright: Copyright (c) 2020 db-ip.com +License: Creative Commons Attribution 4.0 International License Linearicons by Perxis diff --git a/README.md b/README.md index f8e84aa9bb..51e24bdf95 100644 --- a/README.md +++ b/README.md @@ -1,139 +1,25 @@ -

- - Smartstore.NET - - - -
-
- -

- Smartstore.NET -

-

Ready. Sell. Grow.

- - -

- GitHub release - GitHub All Releases - GitHub repo size - GitHub closed issues - GitHub commit activity - GitHub -

- -

- Try Online ∙ - Docs ∙ - Forum ∙ - Marketplace ∙ - Translations -

-
- -Smartstore.NET is a free, open source, full-featured e-commerce solution for companies of any size. It is web standards compliant and incorporates the newest Microsoft web technology stack. +# Deprecation warning :warning: -**Smartstore.NET includes all essential features to create multilingual and multi-currency stores** targeting desktop or mobile devices and enabling SEO optimized rich product catalogs with support for an unlimited number of products and categories, variants, bundles, datasheets, ESD, discounts, coupons and many more. +This repository is outdated and no longer being actively maintained by the Smartstore devs. +Users & developers are encouraged to check out the successor built on **ASP.NET Core**: +**Smartstore 5**. -A comprehensive set of tools for CRM & CMS, sales, marketing, payment & shipping handling, etc. makes Smartstore.NET a powerful all-in-one solution fulfilling all your needs. - -
-

- Smartstore.NET Demoshop +

+ + Announcing Smartstore 5 +

-
- -**Smartstore.NET delivers a beautiful and configurable shop front-end out-of-the-box**, built with a design approach on the highest level, including components like `Bootstrap 4`, `Sass` and others. The supplied theme _Flex_ is modern, clean and fully responsive, giving buyers the best possible shopping experience on any device. - -The state-of-the-art architecture of Smartstore.NET - with `ASP.NET 4.5` + `MVC 5`, `Entity Framework 6` and Domain Driven Design approach - makes it easy to extend, extremely flexible and essentially fun to work with ;-) - -* **Website:** [http://www.smartstore.com/en/net](http://www.smartstore.com/en/net) -* **Forum:** [http://community.smartstore.com](http://community.smartstore.com) -* **Marketplace:** [http://community.smartstore.com/marketplace](http://community.smartstore.com/marketplace) -* **Translations:** [http://translate.smartstore.com/](http://translate.smartstore.com/) -* **Documentation:** [Smartstore.NET Documentation in English](http://docs.smartstore.com/display/SMNET/SmartStore.NET+Documentation+Home) - -

 

- - -## Highlights - -### Technology & Design - -* State of the art architecture thanks to `ASP.NET 4.5`, `ASP.NET MVC 5`, `Entity Framework 6` and Domain Driven Design -* Easy to extend and extremely flexible thanks to modular design -* Highly scalable thanks to full page caching and web farm support -* A powerful theming engine lets you create themes & skins with minimum effort thanks to theme inheritance -* Point&Click Theme configuration -* Highly professional search framework based on Lucene.NET, delivering ultra fast faceted search results -* Consistent and sophisticated use of modern components such as `jQuery`, `Bootstrap 4`, `Sass` & more in the front and back end. -* Easy shop management thanks to modern and clean UI - -### Features - -* NEW: CMS Page Builder -* NEW: CMS Menu Builder -* Multi-Store support -* Unlimited number of products and categories -* Product Bundles -* RESTful WebApi -* Multi-language and RTL support -* Modern, clean, SEO-optimized and fully responsive Theme based on Bootstrap 4 -* Ultra fast search framework with faceted search support -* Extremely scalable thanks to output caching, REDIS & Microsoft Azure support. -* Trusted Shops precertification -* 100% compliant with German jurisdiction -* Sales-, Customer- & Inventory-management -* Comprehensive CRM features -* Powerful Discount System -* Powerful layered navigation in the shop -* Numerous Payment and Shipping Providers and options -* Sophisticated Marketing & Promotion capabilities (Gift cards, Reward Points, discounts of any type and more) -* Reviews & Ratings -* CMS (Blog, Forum, custom pages & HTML content etc.) -* and many more... - - - -## Project Status - -Smartstore.NET V3.2.0 has been released on 10 May, 2019. The highlights are: - -* (NEW) **Page Builder**: Create fascinating content that will boost your sales. No coding is required thanks to a powerful WYSIWYG editor which utilizes the revolutionary *CSS Grid* system (commercial plugin) -* (NEW) **Menu Builder**: Visual manager for all sorts of menus. Change existing menus or create your own and place them anywhere you want. -* (Perf) **Faster MegaSearch** thanks to Lucene.NET 4.8 -* (Perf) **Huge performance increase** in discount resolution and calculation. - - - -## Try it online - -We have set up a live online demo for you so you are able to test Smartstore.NET without local installation. Get a first impression and test all available features in the front- and in the backend. Please keep in mind that the backend demo is shared and other testers can modify data at the same time. - -* [**Frontend**](http://frontend.smartstore.net/en) (User: demo, PWD: 1234) -* [**Backend**](http://backend.smartstore.net/en/login) (User: demo, PWD: 1234) - - - -## How to install - -* Download the latest stable release from the download tab and unzip it to your web folder -* Setup a website in IIS and point the file directory to your unzipped folder -* Fire up your browser and follow the installation instructions -* Enjoy ;-) - -NOTE: Smartstore.NET 3 requires [Visual C++ Redistributable für Visual Studio 2015](https://www.microsoft.com/en-US/download/details.aspx?id=52685) which is already pre-installed on most systems. If, nevertheless, it is missing on your web server, just download and execute the installer or ask your hosting provider to do that for you. ### System requirements * IIS 7+ * ASP.NET 4.5+ * MS SQL Server 2008 Express (or higher) OR MS SQL Server Compact 4 -* Visual C++ Redistributable für Visual Studio 2015 ([Download](https://www.microsoft.com/en-US/download/details.aspx?id=52685)) +* Visual C++ Redistributable für Visual Studio 2015-2019 ([Download](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads)) * Full Trust ## License -Smartstore.NET Community Edition is released under the [GPLv3 license](http://www.gnu.org/licenses/gpl-3.0.txt). +Smartstore Community Edition is released under the [GPLv3 license](http://www.gnu.org/licenses/gpl-3.0.txt). diff --git a/SmartStoreNET.Tasks.Targets b/SmartStoreNET.Tasks.Targets index 080c78d497..f67aba4987 100644 --- a/SmartStoreNET.Tasks.Targets +++ b/SmartStoreNET.Tasks.Targets @@ -39,7 +39,7 @@ x86 $(BUILD_NUMBER) - 3.2.2 + 4.2.0 $(StageFolder) .$(Version) @@ -97,13 +97,15 @@ - - - - + + + + + + - + diff --git a/assets/ready.set.go.jpg b/assets/ready.set.go.jpg new file mode 100644 index 0000000000..e69e37758c Binary files /dev/null and b/assets/ready.set.go.jpg differ diff --git a/assets/sm4-devices.png b/assets/sm4-devices.png new file mode 100644 index 0000000000..788db43d36 Binary files /dev/null and b/assets/sm4-devices.png differ diff --git a/assets/smartstore-text.png b/assets/smartstore-text.png index 25bf9558fb..0d8cfe6bb8 100644 Binary files a/assets/smartstore-text.png and b/assets/smartstore-text.png differ diff --git a/changelog.md b/changelog.md index 81357cca18..8ba63a78f8 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,346 @@ # Release Notes +## Smartstore 4.2.0 + +### New Features +**Dimension Pricing** (commercial plugin) + +* Freely configurable dimension templates +* Price calculation based on user input for predefined product dimensions +* Shipping rates based on dimension and weight calculation +* Freely configurable shipping formulas for irregular shapes + +**File Manager** (commercial plugin) + +* Freely configurable tabs for product detail pages +* Unlimited upload of documents for each product tab +* CSV Import + +**Digital Product Advisor** (commercial plugin) + +* Product presentation based on configurable questions and predefined answers +* Product filter based on rule builder rules + +### Improvements +* Web API: added RewardPointsHistory entity +* Added canonical HTML link for blog, forum and news page +* Better semaphore handling +* Several OpenTrans improvements +* BMEcat: use stream writer to write large xml files & fixed catalog structure + +### Bugfixes + +* #379 & #264 Login failed if login type is user and email and if both username and email are an email address and are not identical. +* #2197 Web API error "The specified type member 'MergedDataValues' is not supported in LINQ to Entities..." +* Web API: fixes OData Connected Service error when accessing MediaFiles and MediaFolders lists +* PayPal Express: fixed order may have been placed even if DoExpressCheckoutPayment failed +* Roxy Filemanager: icons were not displayed anymore +* Azure CloudBlockBlob stream throws on Length access. Fetch Position instead. +* Fixed the uploaded file of a product attribute is lost as soon as another attribute is selected +* Fixes display problem with uploaded media files which contain a + character + +## Smartstore 4.1.1 +### New Features +* **Page Builder** + * Added newsletter block to subscribe/unsubscribe to the newsletter. +* Rule Builder: added CartItemQuantity rule to specify conditions for the quantity per cart product. +* Added an option to display blog posts and news only for a certain language. +* #2171 Created SEO setting for ExtraAllows. + +### Improvements +* WebDAVModule is removed in Web.config because it disables PUT and DELETE requests (required by Web API). +* Newsletter subscriptions: added filter for customer roles in export profile and backend list. +* #2167 Added an activity log for order editing. +* Inherit ACL and store mappings into all sub-categories, not only into direct sub-categories. +* #2186 MegaMenu: link for background image had no effect. +* Added honeypot protection to blog post comments. + +### Bugfixes +* #2162 A plugin could not be uninstalled if it's the only one installed. +* Sometimes no picture was displayed when navigating in the product details' image gallery. +* Deactivated slugs were falsely always redirected without SSL. +* Selected products could not be deleted in the backend product list. +* Web-API: fixed "failed to serialize the response body" when uploading product images and missing product media file mapping if an equal image was found in catalog folder. +* Categories: fixed missing cache invalidation when inheriting ACL or stores into child categories and products. +* Link Builder: the selected media file was not applied. +* Selecting an attribute of type date caused an error on the product detail page. +* AmazonPay: fixed rare "client ID is a required parameter and is not set" when using the login function. +* Various fixes for anti forgery request handling. +* Used mediasettings value for picture rendering in MegaMenu (pictures weren't displayed in MegaMenu dropdowns). +* Mediamanager uploads were marked as being transient. + +## Smartstore 4.1.0 +### Highlights +* **Web API:** now supports the widely extended version 4 of OData. New endpoints for uploading and managing media files have been added to the API. +* (NEW) **Delivery dates:** product info now shows estimated delivery date (exact date, range, *Not before*, *Not later than*). Merchant can enter optional Min/Max delivery days and also specify business hours. +* (DEV) [VS Extension](https://marketplace.visualstudio.com/items?itemName=SmartStoreAG.Smartstore) to create plugins. Helps developing plugins for Smartstore by creating the basic structure of a Smartstore plugin with all the required files. +* **OCI** provides connection to e-procurement systems via industry standard OCI (commercial plugin) +* **cXmlPunchout** provides connection to e-procurement systems via industry standard cXmlPunchout (commercial plugin) +* **Video** support in product media gallery. +* **Media Gallery** displays a multitude of media files in various styles as Page Builder block. +* (PERF) **~10% faster page rendering** speed thanks to better eager loading & caching strategies + +### Breaking changes +* The **Glimpse** plugin is obsolete and has been removed! Does not work with .NET 4.7+ and hasn't been maintained for 7 years now. +* (DEV) **Web API**: porting to OData v4 has minor incompatibilities with v3. See this [blog post](https://smartstore.com/en/web-api-supports-odata-v4-as-from-smartstore-4-1) for details. +* **Blogs\News**: the direct language association has been removed. Localized content is now entered using the localization editor. +Merging the contents of different languages must be done manually (migration is not possible). +* **PostFinance**: plugin has been extended and renamed. Users of this payment method must activate PostFinance e-Commerce in backend and enter the API access data again. + +### New Features +* Theming: responsive font-sizing and spacing +* **Page Builder** + * #2042 Added gallery block to display a multitude of media files in various styles. + * #2036 Added block to display content slider. + * #2035 Added block to display news. + * #1729 Added option for title and intro inline background color. +* **PostFinance**: added new payment methods "Checkout All-in-one" and "Checkout Flex". +* #1644 Graying out unavailable attribute combinations. +* #1744 Added 'Orders' tab to the product editor to display all orders in which the product has been purchased. +* #1735 Added a setting to open a product directly if the search term matches a SKU, MPN or GTIN. +* #1239 Added configurability for password strength. +* #1678 Determine initial working currency based on IP/country. +* #1697 Added picture and color options to checkout attribute values. +* #1788 Added setting to restrict order amount in general and for customer roles in specific. +* #1978 Added minimum and maximum days to delivery times to show a specific delivery date. +* #1715 Added product attributes selected by customer to RFQ and ask question message. +* (DEV) Full localization support for properties of setting classes. +* #2012 Added paging and sorting for bestsellers report. +* #2014 Added paging and sorting for top customers report. +* #2092 Added meta properties for social media sharing to category, manufacturer, news and blog pages. +* Added option for SameSite mode property of cookies +* #1307 Add 'apple-touch-icon' meta with all common sizes +* #213 Allow special title and meta tags for home + +### Improvements +* (PERF) 10% faster page rendering speed thanks to better eager loading & caching strategies +* #1981 Added grouping for cart rules. +* #1407 Added menu to set order and payment status for orders checked in backend grid. +* #1778 Back button closes offcanvas menu/cart. +* #2069 MegaSearch: product order is applied to all categories in which a product appears. +* #2052 When editing a product, the stock quantity is only updated if it has not changed since the page was loaded. +* #213 Added options for special title and meta tags on home page. +* #1977 CMS Menu Builder: added new builder types for blogs and news. +* Search Log: added option to hide top search terms in instant search. +* CookieManager: Admin area for user defined cookie information +* CookieManager: More compact frontend layout for mobile devices +* Seo optimization for News, Blog & Boards +* (Admin) More filter options for news & blog items +* #1901 News: Made CreatedOn configurable +* #2078 Hidden news items should be rendered opaque if current user is in admin role. +* #1902 Form registration clears values when switching between site languages +* #1984 Render breadcrumb for pages that are part of the main menu. +* #2121 Media Manager: implement direct file "replace" support. +* #1338 Display 'PanGV' in OffCanvas cart +* #1988 Preconnect all external resources and load them async +* #1968 Dropzone: upload the folder structure when a folder is dropped. +* #1996 Page Builder: Added 'Loading behaviour' option to picture block +* #2100 Improved schema for news, blogs and products + +### Bugfixes +* **Media Manager** + * The file date should be displayed in local time and not in UTC. +* **Page Builder** + * Reveal effect on story block conflicted with nested block reveal effects. + * #2068 Fixed RTL display issues with dropdown menus and slider. +* **Export** + * An empty export file was created at the end of an export when using the batch-size option. + * The offset option skipped one record too much. + * The XML export of CountryOfOrigin caused an error. +* Message template attachments were not attached to the email. +* Shipment templates showed all products of the order instead of only those of the shipment. +* EasyCredit: fixed "Could not convert setting 'EasyCreditSettings.LastPurchaseLimitUpdatedOn' to type 'DateTime'". +* #2076 Attributes were not pre-selected for all associated products of a grouped product. +* Customer tab on customer role edit page showed deleted customers. +* #2041 Product with call for price did show as free in product list and grid view. +* #2079 Order item editing: order total was not updated when both unit and item price were changed. +* RSS feeds: relative URLs of objects embedded in HTML are replaced by absolute URLs. +* Rules: automatic category assignments of products were not taken into account in a search index updates. +* Campaign preview did not work if there are no newsletter subscribers. +* Many alt & title attribute fixes for pictures +* #2000 Escape toxic chars in meta attributes +* Google Analytics: Fixed script error in conjunction with CookieManager +* #2085 RTL: searchbar button wasn't aligned correctly +* #2068 RTL: Fixed Page Builder slider positioning, dropdown and other alignments +* #2019 OffCancas compare: hide horizontal scrollbar +* #2146 LimitedToStores property was not set in a rare case. + +## Smartstore 4.0.1 +### New Features +* Check on app startup whether any plugin has changed and refresh all plugin locale resources. + +### Improvements +* Theming: minimal but effective tweaks (rounding, shadows, typo) +* (SEO) (PERF) Preload logo to prevent flashing and web fonts to avoid FOUT on slow internet connection +* **Rule Builder**: + * #1969 The preview grid is now displayed in a modal dialog. + * Added more cart rules: weekday, total quantity of products in cart. + * Added list with example values for browser, operating system and device family rule. +* #1995 The localized ALT text of media files is now respected for all frontend images. +* SEO: most images in frontend now carry `loading="lazy"` attribute which loads images only when they enter visible viewport. +* **Media Manager**: + * Improved image lazy loading behavior. + * The reference table of a file now remains open if its data has been loaded. + * Migration: detect duplicate file names. +* Improved handling of customer roles, e.g. paging for customer role grid. +* (PERF) Use direct SQL to delete guest customers. +* Blog & News items: Unpublished items are visible for shop admins now + + +### Bugfixes +* Order placement validation now also checks whether the selected payment method has been filtered out. +* #1976 The button to cancel recurring payments in my-account was out of function. +* (pb) Flat Box did not invert text color if concrete colors were specified for text parts +* (mm) Store logo is handled by "content" album, not "file" +* (mm) Tracker tracks only the last media file property +* (mm) Migration: `Download` entity was teared down too early in the migration process. +* (perf) Plugin hash calculation was broken in a way that every plugin was redeployed on each app startup +* A cart rule was not applied if more than one cart rule was assigned to an object. +* MegaSearch: fixed the incorrect featured sorting. +* Added missing paging for shipment list. +* #2009 "Show all" link in top brands list is always rendered in new line but should be last item in last row. + + +## Smartstore 4.0.0 +### Highlights +* (NEW) **Media Manager**: Powerful and lightning-fast explorer/manager for media files (commercial plugin exclusively bundled with Pro Edition) +* (NEW) **Rule Builder**: Powerful rule system for visual business rule creation + * Dozens of predefined rules out-of-the-box + * Supports logical operators (AND/OR) and unlimited grouping/nesting + * Cart rules: can be applied to discounts, shipping and payment methods + * Customer rules: used to auto-assign customers to customer groups for 1-to-1 content targeting + * Product rules: used to auto-assign products to categories based on filter definitions (e.g. price, brand, color, stock etc.) +* (NEW) **New permission (ACL) system**: Granular tree-based permission management based on customer groups. Supports inheritance to allow or deny entire permission ranges +* (NEW) **Personalized product recommendations**: Replaces homepage products dynamically with personalized product recommendations based on customer interests. (commercial plugin) +* (NEW) **Geo Blocker**: Restricts shop access based on visitor country or IP address ranges (commercial plugin) +* (NEW) **Dashboard with charts**: New dashboard widgets display key data like orders, customers, bestsellers etc. in nice graphical charts. + +### Breaking changes +* (Dev) *Install\UninstallPermissions* has been removed from *IPermissionService*. No longer needs to be called by plugins. Permissions are now automatically installed\uninstalled. +* (Dev) *IDiscountRequirementRule* and *IShippingMethodFilter* has been removed. Discounts and shipping methods can now be filtered by rule sets. +* (Dev) Customer navigation property *CustomerRoles* has been replaced by *CustomerRoleMappings*, a new entity for role mappings. +* The search index must be rebuilt due to various enhancements. +* (Dev) The wrong name of API endpoint *LocalizedPropertys* has been corrected and renamed to *LocalizedProperties*. + +### New Features +* **Granular permissions**: All permissions of a customer directly visible on the customer edit page. +* DEV: Added Visual Studio extension to create Smartstore Plugins +* GDPR conform Cookie Consent Manager +* SEO: XML Sitemap now includes blog, news and forum +* SEO: do 301 redirect to URL without trailing slash +* SVG support for image uploads. +* Schedule tasks: new property "Priority" runs tasks with higher priority first when multiple tasks are pending. +* #1805 Added XML Sitemap settings to backend UI. +* #1598 Add published property to ProductTag entity. +* #1669 Apply percentage discounts also on tier prices. +* #1618 Implement ACL and multistore capability on menu item level. +* #1683 Menu Builder items: implement support for icon (brand) color. +* #1584 Show bundle item images in order details like in shopping cart details. +* **MegaSearch**: + * Added more text analysis options (e.g. Lucene.Net classic analyzer). + * Added tool to display internal information about a search, such as Lucene.Net terms. + * #1693 Find the grouped product when searching for the SKU, GTIN, MPN of a non individually visible, associated product. + * #1711 Find product when searching for GTIN or MPN of an attribute combination. + * Perf. Added option to ignore attribute filtering property on product level. +* **Web-API**: + * #1809 Added a parameter to start an import after uploading import files. + * #1801 Added endpoints for *ProductPictures*, *ProductCategories* and *ProductManufacturers* to allow updating *DisplayOrder*. + * #1858 Added endpoints for *NewsLetterSubscription*. +* #1714 Direct links to variations on a product. +* #1733 Add ability to hide products from catalog, but not search. +* #1754 Implement hidden boolean setting that controls which catalog search engine implementation should be used in backend. +* #1776 Enable images and color values for search filters to be stored for specification attribute options. +* #1793 Add field for manufacturer bottom description like for categories. +* #1116 Add new field for product condition. +* #1790 ACL support for manufacturers. +* #1616 Add new field for tracking URL to shipment entity to better track shipments. +* (Page Builder) Added a Page Builder block to display blog posts. + +### Improvements +* #1663 Make *MeasureDimension* and *MeasureWeight* localizable. +* #1600 Show an example currency value for custom formatting value changes. +* Card desk instead of a grid for customer addresses on customer edit page. +* Display "price from" in product lists if any attribute combination price exists. +* Reworked blog & news section. +* #1718 Activated ReCaptcha without keys can cause the merchant to lock himself out of the shop. +* #1752 Export: let a provider directly export to a file stream instead of a memory stream. +* #1763 Topic editor should display all menu item nodes that reference the current topic. +* #1665 UI: ACL, discount and store selection should be done via multiple select2. +* Updated UserAgent Parser. +* TinyImage: updated WebP detection patterns. +* **PayPal PLUS**: + * Apply order of payment methods in backend to the list of third-party payment methods in checkout. + * #1848 Send the billing address when redirecting to PayPal. +* Hide cart payment button for payment methods without match of applied rule sets. +* #1839 MegaSearch: Support exact value match for numeric range filters. +* #1920 Hide option prizes if "Call for price" is enabled. +* Debitoor: added an option to force a price type on invoices. +* BeezUp: export product costs. +* #1915 RTL: fixed alignment of product art badge. +* #1738 Sending of mails to customers uses generic message template now. +* #1805 UI: Added XmlSitemap settings to backend +* And many more other minor improvements + +### Bugfixes +* Export: Fixed KeyNotFoundException when batch size was 1. +* #1686 Fixed KeyNotFoundException when payment method friendly name ends with spaces. +* Cart: Fixed ShoppingCartSettings.ShowProductBundleImagesOnShoppingCart hides the bundle item name. +* **MegaSearch**: + * Fixed the preset sorting order of products on a manufacturer page may be wrong depending on catalog settings. + * #1716 Price facet filter bypasses Call for Pricing and shows the approximate price of a product. + * The number of hits for product review facets was wrong in some cases. + * Do not show facets for manufacturers or categories if they are limited to stores or subject to ACL. + * #1823 Ghost facet filter groups will appear if a numeric value is assigned that matches to a different spec option. +* **Page Builder**: + * #1689 Block with z-index < 0 can not be selected in story view - Block tools can reduce z-index below 0. + * #1691 Manually editing/removing block cols/rows does not refresh grid state correctly. + * #1684 Edit Mode rendering bug in Mac Safari + * #1836 Added "Order" property to page Builder block to control rendering order of blocks in HTML output +* **PayPal**: + * Fixed rare exception "Unsupported Media Type" (415) in PayPal PLUS. + * Only process a partially refund IPN when the order refunded amount is zero. Otherwise the order refunded amount will be wrong. +* **Customer import**: + * VatNumber and other fields were ignored. + * Customer roles sometimes inserted several times. + * Changed billing/shipping address was added instead of updating the existing one. + * Region assigned to an address was not updated. +* **Product tags**: + * #1730 Product tag count should filter also based on Visibility. + * Product tag count sometimes not up-to-date due to missing cache clearance. +* **Debitoor**: + * Avoid errors due to invalid quantity unit Id of 0. + * #1952 rework quantity units. + * Sometimes the SKU was missing on invoices. +* Multistore mapping was ignored for manufacturers in sitemap. +* Categories limited to stores were not displayed in tree view. +* Fixed InvalidOperationException in CreatePdfInvoiceAttachment when an order is placed by a guest. +* The ShowDescriptionInSubPages setting should also be applied to the bottom category description. +* #1774 Recaptcha: doesn't work for product reviews, blog and news comments if hidden captcha is activated. +* #1766 Customer FullName is not populated after registration. +* #1765 Web-API: fixed authentication error "Value cannot be null. Parameter name: name" when login type is email. +* Payone: fixed wrong hash value if redirecting option is activated. +* Azure: opening the configuration page resulted in an error. +* Shipping by weight: the surcharge hint was not displayed correctly. +* Off-canvas menu shows wrong product count for brand menu items. +* Images of newly added variants could be deleted automatically due to wrong image transient state. +* #1794 Menu display order is ignored for widget zone header_menu_special_after. +* #1451 RTL: Manage categories tree view. +* Biz-Importer: If the TaxRate table was missing, the assignment of the tax category to the product was not set. +* #1832 Import: adding URL records requires cache to be cleared. +* #1818 The order list summary do not respect all list filters and shows wrong aggregate values. +* #1835 Removing gift card issue. +* #1875 A delivery time cannot be deleted if it is assigned to a variant combination of a deleted product. +* BeezUp: fixes ArgumentNullException, parameter name "source". +* #1743 Avoid redirecting to the account activation page when trying to log in for the first time. +* #1895 The checkout button disappears when moving a product from the shopping cart to the wishlist. +* Menu builder: in case of system menus the template can changes accidentally when saving. +* Structured data: Replace length with depth property. +* #1701 'View All' button from the offcanvas manufacturer menu causes 404 on mobile. +* Added missing sitemap task. +* #1696 ESD: browser freezes when editing file changelog +* And many more other minor fixes... + + ## SmartStore.NET 3.2.2 ### New Features @@ -10,7 +351,7 @@ * New Heidelpay payment plugin. * Shop-Connector: mapping of product SKUs that differ between client shops. * MegaMenu supports configuration of menu items - + ### Improvements * Amazon Pay: PSD2. Strong customer authentication (SCA) upgrade. diff --git a/lib/SmartStore.Licensing/SmartStore.Licensing.dll b/lib/SmartStore.Licensing/SmartStore.Licensing.dll index 17bf4e2ad9..61354aae6a 100644 Binary files a/lib/SmartStore.Licensing/SmartStore.Licensing.dll and b/lib/SmartStore.Licensing/SmartStore.Licensing.dll differ diff --git a/src/AssemblySharedInfo.cs b/src/AssemblySharedInfo.cs index b58492422f..92179ee59a 100644 --- a/src/AssemblySharedInfo.cs +++ b/src/AssemblySharedInfo.cs @@ -7,6 +7,6 @@ [assembly: AssemblyDescription("SmartStore.NET")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("SmartStore AG")] -[assembly: AssemblyCopyright("Copyright © SmartStore AG 2019")] +[assembly: AssemblyCopyright("Copyright © SmartStore AG 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/src/AssemblyVersionInfo.cs b/src/AssemblyVersionInfo.cs index 719cd1d6ff..9ca54df467 100644 --- a/src/AssemblyVersionInfo.cs +++ b/src/AssemblyVersionInfo.cs @@ -9,7 +9,7 @@ // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("3.2.2.0")] +[assembly: AssemblyVersion("4.2.0.0")] -[assembly: AssemblyFileVersion("3.2.2.0")] -[assembly: AssemblyInformationalVersion("3.2.2.0")] +[assembly: AssemblyFileVersion("4.2.0.0")] +[assembly: AssemblyInformationalVersion("4.2.0.0")] diff --git a/src/Libraries/SmartStore.Core/Async/AsyncRunner.cs b/src/Libraries/SmartStore.Core/Async/AsyncRunner.cs index 2751259eef..91ffb0db95 100644 --- a/src/Libraries/SmartStore.Core/Async/AsyncRunner.cs +++ b/src/Libraries/SmartStore.Core/Async/AsyncRunner.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using System.Web.Hosting; @@ -9,429 +8,417 @@ namespace SmartStore.Core.Async { - using EventQueue = ConcurrentQueue>; - - public class AsyncRunner : IDisposable - { - #region Instance members - - private ExclusiveSynchronizationContext CurrentContext; - private SynchronizationContext OldContext; - private int TaskCount; - - private AsyncRunner() - { - OldContext = SynchronizationContext.Current; - CurrentContext = new ExclusiveSynchronizationContext(OldContext); - SynchronizationContext.SetSynchronizationContext(CurrentContext); - } - - public void Dispose() - { - try - { - CurrentContext.BeginMessageLoop(); - } - catch (Exception e) - { - throw e; - } - finally - { - SynchronizationContext.SetSynchronizationContext(OldContext); - } - } - - private void Increment() - { - Interlocked.Increment(ref TaskCount); - } - - private void Decrement() - { - Interlocked.Decrement(ref TaskCount); - if (TaskCount == 0) - { - CurrentContext.EndMessageLoop(); - } - } - - /// - /// Executes an async Task method which has a void return value synchronously - /// - /// Task execute - public void Run(Task task, Action continuation = null) - { - CurrentContext.Post(async _ => - { - try - { - Increment(); - await task; - - continuation?.Invoke(task); - } - catch (Exception e) - { - CurrentContext.InnerException = e; - } - finally - { - Decrement(); - } - }, null); - } - - /// - /// Executes an async Task method which has a TResult return type synchronously - /// - /// Return Type - /// Task to execute - public TResult Run(Task task, Action> continuation = null) - { - var result = default(TResult); - - CurrentContext.Post(async _ => - { - try - { - Increment(); - result = await task; - - continuation?.Invoke(task); - } - catch (Exception e) - { - CurrentContext.InnerException = e; - } - finally - { - Decrement(); - } - }, null); - - return result; - } - - #endregion - - private static readonly BackgroundWorkHost _host = new BackgroundWorkHost(); - - /// - /// Gets the global cancellation token which signals the application shutdown - /// - public static CancellationToken AppShutdownCancellationToken - { - get { return _host.ShutdownCancellationTokenSource.Token; } - } - - public static AsyncRunner Create() - { - return new AsyncRunner(); - } - - /// - /// Executes an async Task method which has a void return value synchronously - /// - /// Task method to execute - public static void RunSync(Func func, Action continuation = null) - { - using (var runner = new AsyncRunner()) - { - runner.Run(func(), continuation); - } - } - - /// - /// Executes an async Task method which has a TResult return type synchronously - /// - /// Return Type - /// Task method to execute - /// - public static TResult RunSync(Func> func, Action> continuation = null) - { - var result = default(TResult); - - using (var runner = new AsyncRunner()) - { - result = runner.Run(func(), continuation); - } - - return result; - } - - public static Task Run( - Action action, - CancellationToken cancellationToken = default(CancellationToken), - TaskCreationOptions options = TaskCreationOptions.LongRunning, - TaskScheduler scheduler = null) - { - Guard.NotNull(action, nameof(action)); - - var ct = _host.CreateCompositeCancellationTokenSource(cancellationToken).Token; - - var t = Task.Factory.StartNew(() => { - var accessor = EngineContext.Current.ContainerManager.ScopeAccessor; - using (accessor.BeginContextAwareScope()) - { - action(accessor.GetLifetimeScope(null), ct); - } - }, ct, options, scheduler ?? TaskScheduler.Default); - - _host.Register(t, ct); - - return t; - } - - public static Task Run( - Action action, - object state, - CancellationToken cancellationToken = default(CancellationToken), - TaskCreationOptions options = TaskCreationOptions.LongRunning, - TaskScheduler scheduler = null) - { - Guard.NotNull(state, nameof(state)); - Guard.NotNull(action, nameof(action)); - - var ct = _host.CreateCompositeCancellationTokenSource(cancellationToken).Token; - - var t = Task.Factory.StartNew((o) => - { - var accessor = EngineContext.Current.ContainerManager.ScopeAccessor; - using (accessor.BeginContextAwareScope()) - { - action(accessor.GetLifetimeScope(null), ct, o); - } - }, state, ct, options, scheduler ?? TaskScheduler.Default); - - _host.Register(t, ct); - - return t; - } - - public static Task Run( - Func function, - CancellationToken cancellationToken = default(CancellationToken), - TaskCreationOptions options = TaskCreationOptions.LongRunning, - TaskScheduler scheduler = null) - { - Guard.NotNull(function, nameof(function)); - - var ct = _host.CreateCompositeCancellationTokenSource(cancellationToken).Token; - - var t = Task.Factory.StartNew(() => - { - var accessor = EngineContext.Current.ContainerManager.ScopeAccessor; - using (accessor.BeginContextAwareScope()) - { - return function(accessor.GetLifetimeScope(null), ct); - } - }, ct, options, scheduler ?? TaskScheduler.Default); - - _host.Register(t, ct); - - return t; - } - - public static Task Run( - Func function, - object state, - CancellationToken cancellationToken = default(CancellationToken), - TaskCreationOptions options = TaskCreationOptions.LongRunning, - TaskScheduler scheduler = null) - { - Guard.NotNull(state, nameof(state)); - Guard.NotNull(function, nameof(function)); - - var ct = _host.CreateCompositeCancellationTokenSource(cancellationToken).Token; - - var t = Task.Factory.StartNew((o) => - { - var accessor = EngineContext.Current.ContainerManager.ScopeAccessor; - using (accessor.BeginContextAwareScope()) - { - return function(accessor.GetLifetimeScope(null), ct, o); - } - }, state, ct, options, scheduler ?? TaskScheduler.Default); - - _host.Register(t, ct); - - return t; - } - - public static Task Run( - Func function, - object state, - CancellationToken cancellationToken = default(CancellationToken)) - { - Guard.NotNull(state, nameof(state)); - Guard.NotNull(function, nameof(function)); - - var ct = _host.CreateCompositeCancellationTokenSource(cancellationToken).Token; - - var accessor = EngineContext.Current.ContainerManager.ScopeAccessor; - var scope = accessor.BeginLifetimeScope(null); - - Task task = null; - - try - { - task = function(scope, ct).ContinueWith(x => - { - scope.Dispose(); - }); - _host.Register(task, ct); - } - catch - { - scope.Dispose(); - } - - return task; - } - - private class ExclusiveSynchronizationContext : SynchronizationContext - { - private bool _done; - private readonly AutoResetEvent _workItemsWaiting = new AutoResetEvent(false); - private readonly EventQueue _items; - - public ExclusiveSynchronizationContext(SynchronizationContext old) - { - if (old is ExclusiveSynchronizationContext oldEx) - { - this._items = oldEx._items; - } - else - { - this._items = new EventQueue(); - } - } - - public Exception InnerException { get; set; } - - public override void Send(SendOrPostCallback d, object state) - { - throw new NotSupportedException("We cannot send to the same thread"); - } - - public override void Post(SendOrPostCallback d, object state) - { - _items.Enqueue(Tuple.Create(d, state)); - _workItemsWaiting.Set(); - } - - public void EndMessageLoop() - { - Post(_ => _done = true, null); - } - - public void BeginMessageLoop() - { - while (!_done) - { - if (!_items.TryDequeue(out var task)) - { - _workItemsWaiting.WaitOne(); - } - else - { - task.Item1(task.Item2); - - if (InnerException != null) // method threw an exeption - { - throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException); - } - } - } - } - - public override SynchronizationContext CreateCopy() - { - return this; - } - } - - } - - internal class BackgroundWorkHost : IRegisteredObject - { - private readonly CancellationTokenSource _shutdownCancellationTokenSource = new CancellationTokenSource(); - private int _numRunningWorkItems; - - public BackgroundWorkHost() - { - HostingEnvironment.RegisterObject(this); - } - - public CancellationTokenSource ShutdownCancellationTokenSource - { - get { return _shutdownCancellationTokenSource; } - } - - public void Stop(bool immediate) - { - int num; - lock (this) - { - _shutdownCancellationTokenSource.Cancel(); - num = _numRunningWorkItems; - } - if (num == 0) - { - FinalShutdown(); - } - } - - public CancellationTokenSource CreateCompositeCancellationTokenSource(CancellationToken userCancellationToken) - { - if (userCancellationToken == CancellationToken.None) - { - return _shutdownCancellationTokenSource; - } - return CancellationTokenSource.CreateLinkedTokenSource(_shutdownCancellationTokenSource.Token, userCancellationToken); - } - - public void Register(Task work, CancellationToken cancellationToken) - { - if (!cancellationToken.IsCancellationRequested) - { - lock (this) - { - if (cancellationToken.IsCancellationRequested) - { - return; - } - _numRunningWorkItems++; - } - - work.ContinueWith( - WorkItemComplete, - CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, - TaskScheduler.Default); - } - } - - private void WorkItemComplete(Task work) - { - int num; - bool isCancellationRequested; - lock (this) - { - num = --_numRunningWorkItems; - isCancellationRequested = _shutdownCancellationTokenSource.IsCancellationRequested; - } - if (num == 0 && isCancellationRequested) - { - FinalShutdown(); - } - } - - private void FinalShutdown() - { - HostingEnvironment.UnregisterObject(this); - } - - } -} + using EventQueue = ConcurrentQueue>; + + public class AsyncRunner : IDisposable + { + #region Instance members + + private readonly ExclusiveSynchronizationContext CurrentContext; + private readonly SynchronizationContext OldContext; + private int TaskCount; + + private AsyncRunner() + { + OldContext = SynchronizationContext.Current; + CurrentContext = new ExclusiveSynchronizationContext(OldContext); + SynchronizationContext.SetSynchronizationContext(CurrentContext); + } + + public void Dispose() + { + SynchronizationContext.SetSynchronizationContext(OldContext); + } + + private void Increment() + { + Interlocked.Increment(ref TaskCount); + } + + private void Decrement() + { + Interlocked.Decrement(ref TaskCount); + if (TaskCount == 0) + { + CurrentContext.EndMessageLoop(); + } + } + + /// + /// Executes an async Task method which has a void return value synchronously + /// + /// Task execute + public void Run(Task task, Action continuation = null) + { + CurrentContext.Post(async _ => + { + try + { + Increment(); + await task; + + continuation?.Invoke(task); + } + catch (Exception e) + { + CurrentContext.InnerException = e; + } + finally + { + Decrement(); + } + }, null); + + CurrentContext.BeginMessageLoop(); + } + + /// + /// Executes an async Task method which has a TResult return type synchronously + /// + /// Return Type + /// Task to execute + public TResult Run(Task task, Action> continuation = null) + { + var result = default(TResult); + var curContext = CurrentContext; + + curContext.Post(async _ => + { + try + { + Increment(); + result = await task; + + continuation?.Invoke(task); + } + catch (Exception e) + { + CurrentContext.InnerException = e; + } + finally + { + Decrement(); + } + }, null); + + curContext.BeginMessageLoop(); + + return result; + } + + #endregion + + private static readonly BackgroundWorkHost _host = new BackgroundWorkHost(); + + /// + /// Gets the global cancellation token which signals the application shutdown + /// + public static CancellationToken AppShutdownCancellationToken => _host.ShutdownCancellationTokenSource.Token; + + public static AsyncRunner Create() + { + return new AsyncRunner(); + } + + /// + /// Executes an async Task method which has a void return value synchronously + /// + /// Task method to execute + public static void RunSync(Func func, Action continuation = null) + { + using (var runner = new AsyncRunner()) + { + runner.Run(func(), continuation); + } + } + + /// + /// Executes an async Task method which has a TResult return type synchronously + /// + /// Return Type + /// Task method to execute + /// + public static TResult RunSync(Func> func, Action> continuation = null) + { + var result = default(TResult); + + using (var runner = new AsyncRunner()) + { + result = runner.Run(func(), continuation); + } + + return result; + } + + public static Task Run( + Action action, + CancellationToken cancellationToken = default(CancellationToken), + TaskCreationOptions options = TaskCreationOptions.LongRunning, + TaskScheduler scheduler = null) + { + Guard.NotNull(action, nameof(action)); + + var ct = _host.CreateCompositeCancellationTokenSource(cancellationToken).Token; + + var t = Task.Factory.StartNew(() => + { + var accessor = EngineContext.Current.ContainerManager.ScopeAccessor; + using (accessor.BeginContextAwareScope()) + { + action(accessor.GetLifetimeScope(null), ct); + } + }, ct, options, scheduler ?? TaskScheduler.Default); + + _host.Register(t, ct); + + return t; + } + + public static Task Run( + Action action, + object state, + CancellationToken cancellationToken = default(CancellationToken), + TaskCreationOptions options = TaskCreationOptions.LongRunning, + TaskScheduler scheduler = null) + { + Guard.NotNull(state, nameof(state)); + Guard.NotNull(action, nameof(action)); + + var ct = _host.CreateCompositeCancellationTokenSource(cancellationToken).Token; + + var t = Task.Factory.StartNew((o) => + { + var accessor = EngineContext.Current.ContainerManager.ScopeAccessor; + using (accessor.BeginContextAwareScope()) + { + action(accessor.GetLifetimeScope(null), ct, o); + } + }, state, ct, options, scheduler ?? TaskScheduler.Default); + + _host.Register(t, ct); + + return t; + } + + public static Task Run( + Func function, + CancellationToken cancellationToken = default(CancellationToken), + TaskCreationOptions options = TaskCreationOptions.LongRunning, + TaskScheduler scheduler = null) + { + Guard.NotNull(function, nameof(function)); + + var ct = _host.CreateCompositeCancellationTokenSource(cancellationToken).Token; + + var t = Task.Factory.StartNew(() => + { + var accessor = EngineContext.Current.ContainerManager.ScopeAccessor; + using (accessor.BeginContextAwareScope()) + { + return function(accessor.GetLifetimeScope(null), ct); + } + }, ct, options, scheduler ?? TaskScheduler.Default); + + _host.Register(t, ct); + + return t; + } + + public static Task Run( + Func function, + object state, + CancellationToken cancellationToken = default(CancellationToken), + TaskCreationOptions options = TaskCreationOptions.LongRunning, + TaskScheduler scheduler = null) + { + Guard.NotNull(state, nameof(state)); + Guard.NotNull(function, nameof(function)); + + var ct = _host.CreateCompositeCancellationTokenSource(cancellationToken).Token; + + var t = Task.Factory.StartNew((o) => + { + var accessor = EngineContext.Current.ContainerManager.ScopeAccessor; + using (accessor.BeginContextAwareScope()) + { + return function(accessor.GetLifetimeScope(null), ct, o); + } + }, state, ct, options, scheduler ?? TaskScheduler.Default); + + _host.Register(t, ct); + + return t; + } + + public static Task Run( + Func function, + object state, + CancellationToken cancellationToken = default(CancellationToken)) + { + Guard.NotNull(state, nameof(state)); + Guard.NotNull(function, nameof(function)); + + var ct = _host.CreateCompositeCancellationTokenSource(cancellationToken).Token; + + var accessor = EngineContext.Current.ContainerManager.ScopeAccessor; + var scope = accessor.BeginLifetimeScope(null); + + Task task = null; + + try + { + task = function(scope, ct).ContinueWith(x => + { + scope.Dispose(); + }); + _host.Register(task, ct); + } + catch + { + scope.Dispose(); + } + + return task; + } + + private class ExclusiveSynchronizationContext : SynchronizationContext + { + private bool _done; + private readonly AutoResetEvent _workItemsWaiting = new AutoResetEvent(false); + private readonly EventQueue _items; + + public ExclusiveSynchronizationContext(SynchronizationContext old) + { + if (old is ExclusiveSynchronizationContext oldEx) + { + this._items = oldEx._items; + } + else + { + this._items = new EventQueue(); + } + } + + public Exception InnerException { get; set; } + + public override void Send(SendOrPostCallback d, object state) + { + throw new NotSupportedException("We cannot send to the same thread"); + } + + public override void Post(SendOrPostCallback d, object state) + { + _items.Enqueue(Tuple.Create(d, state)); + _workItemsWaiting.Set(); + } + + public void EndMessageLoop() + { + Post(_ => _done = true, null); + } + + public void BeginMessageLoop() + { + while (!_done) + { + if (!_items.TryDequeue(out var task)) + { + _workItemsWaiting.WaitOne(); + } + else + { + task.Item1(task.Item2); + + if (InnerException != null) // method threw an exeption + { + throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException); + } + } + } + } + + public override SynchronizationContext CreateCopy() + { + return this; + } + } + } + + internal class BackgroundWorkHost : IRegisteredObject + { + private readonly CancellationTokenSource _shutdownCancellationTokenSource = new CancellationTokenSource(); + private int _numRunningWorkItems; + + public BackgroundWorkHost() + { + HostingEnvironment.RegisterObject(this); + } + + public CancellationTokenSource ShutdownCancellationTokenSource => _shutdownCancellationTokenSource; + + public void Stop(bool immediate) + { + int num; + lock (this) + { + _shutdownCancellationTokenSource.Cancel(); + num = _numRunningWorkItems; + } + if (num == 0) + { + FinalShutdown(); + } + } + + public CancellationTokenSource CreateCompositeCancellationTokenSource(CancellationToken userCancellationToken) + { + if (userCancellationToken == CancellationToken.None) + { + return _shutdownCancellationTokenSource; + } + return CancellationTokenSource.CreateLinkedTokenSource(_shutdownCancellationTokenSource.Token, userCancellationToken); + } + + public void Register(Task work, CancellationToken cancellationToken) + { + if (!cancellationToken.IsCancellationRequested) + { + lock (this) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + _numRunningWorkItems++; + } + + work.ContinueWith( + WorkItemComplete, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + } + } + + private void WorkItemComplete(Task work) + { + int num; + bool isCancellationRequested; + lock (this) + { + num = --_numRunningWorkItems; + isCancellationRequested = _shutdownCancellationTokenSource.IsCancellationRequested; + } + if (num == 0 && isCancellationRequested) + { + FinalShutdown(); + } + } + + private void FinalShutdown() + { + HostingEnvironment.UnregisterObject(this); + } + + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Async/AsyncStateInfo.cs b/src/Libraries/SmartStore.Core/Async/AsyncStateInfo.cs index 8bc3898736..d4f9493575 100644 --- a/src/Libraries/SmartStore.Core/Async/AsyncStateInfo.cs +++ b/src/Libraries/SmartStore.Core/Async/AsyncStateInfo.cs @@ -2,34 +2,34 @@ namespace SmartStore.Core.Async { - [Serializable] - public class AsyncStateInfo - { - // used for serialization compatibility - public static readonly string Version = "1"; + [Serializable] + public class AsyncStateInfo + { + // used for serialization compatibility + public static readonly string Version = "1"; - public object Progress - { - get; - set; - } + public object Progress + { + get; + set; + } - public DateTime CreatedOnUtc - { - get; - set; - } + public DateTime CreatedOnUtc + { + get; + set; + } - public DateTime LastAccessUtc - { - get; - set; - } + public DateTime LastAccessUtc + { + get; + set; + } - public TimeSpan Duration - { - get; - set; - } - } + public TimeSpan Duration + { + get; + set; + } + } } diff --git a/src/Libraries/SmartStore.Core/Async/IAsyncState.cs b/src/Libraries/SmartStore.Core/Async/IAsyncState.cs index bbfa0d3266..232ef299d3 100644 --- a/src/Libraries/SmartStore.Core/Async/IAsyncState.cs +++ b/src/Libraries/SmartStore.Core/Async/IAsyncState.cs @@ -1,25 +1,77 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading; -using System.Threading.Tasks; namespace SmartStore.Core.Async { - public interface IAsyncState - { - bool Exists(string name = null); + /// + /// Stores status information about long-running processes. + /// + public interface IAsyncState + { + /// + /// Checks whether a status object exists. + /// + /// The type of status to check for. + /// The optional identifier. + /// true when the status object exists. + bool Exists(string name = null); + /// + /// Gets the status object. + /// + /// The type of status to retrieve. + /// The optional identifier. + /// The status object instance or null if it didn't exist. T Get(string name = null); + + /// + /// Enumerates all currently available status objects of type . + /// + /// Status type to enumerate. IEnumerable GetAll(); - - void Set(T state, string name = null, bool neverExpires = false); + + /// + /// Sets status information about any long-running process. The key is + . + /// When "Redis" is active the item will be saved in the Redis store so that all nodes in a web farm have access to the object. + /// If an object with the same key already exists it will be removed. + /// + /// The type of status object. + /// The status object instance. Can be anything but should be serializable. + /// The optional identifier. Without identifier, any item of type will be overwritten. + /// The default sliding expiration time is 15 minutes. Pass true to prevent automatic expiration but be sure to remove the item. + void Set(T state, string name = null, bool neverExpires = false); + + /// + /// Updates an existing status object. Call this if your unit of work made any progress. + /// + /// The type of status object. + /// The update action delegate + /// The optional identifier. void Update(Action update, string name = null); + + /// + /// Removes a status object. Any cancellation token source with the same key will also be removed automatically. + /// + /// The type of status object to remove. + /// The optional identifier of the object to remove. + /// true when the object existed and has been removed, false otherwise. bool Remove(string name = null); - CancellationTokenSource GetCancelTokenSource(string name = null); - void SetCancelTokenSource(CancellationTokenSource cancelTokenSource, string name = null); - bool Cancel(string name = null); - } + /// + /// Sets a cancellation token that can cancel long-running processes. + /// The token does not expire automatically, so be sure to remove it once finished. + /// + /// The type of the corresponding status object. However, it is not necessary to save a status object. + /// The token. + /// The optional identifier. + void SetCancelTokenSource(CancellationTokenSource cancelTokenSource, string name = null); + + CancellationTokenSource GetCancelTokenSource(string name = null); + + bool RemoveCancelTokenSource(string name = null); + + bool Cancel(string name = null); + + } } diff --git a/src/Libraries/SmartStore.Core/Async/LocalAsyncState.cs b/src/Libraries/SmartStore.Core/Async/LocalAsyncState.cs index a8fa0ceb0f..ba66c15bf0 100644 --- a/src/Libraries/SmartStore.Core/Async/LocalAsyncState.cs +++ b/src/Libraries/SmartStore.Core/Async/LocalAsyncState.cs @@ -2,203 +2,198 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.Caching; -using System.Text; using System.Threading; -using System.Threading.Tasks; namespace SmartStore.Core.Async { - public partial class LocalAsyncState : IAsyncState - { - private readonly MemoryCache _states = new MemoryCache("SmartStore.AsyncState.Progress"); - private readonly MemoryCache _cancelTokens = new MemoryCache("SmartStore.AsyncState.CancelTokenSources"); - - public virtual bool Exists(string name = null) - { - var value = GetStateInfo(name); - return value != null && !object.Equals(value.Progress, default(T)); - } - - public virtual T Get(string name = null) - { - var value = GetStateInfo(name); - - if (value != null) - { - return (T)value.Progress; - } - - return default(T); - } - - public virtual IEnumerable GetAll() - { - var keyPrefix = BuildKey(null); - return _states - .Where(x => x.Key.StartsWith(keyPrefix)) - .Select(x => x.Value) - .OfType() - .Select(x => x.Progress) - .OfType(); - } - - - public virtual void Set(T state, string name = null, bool neverExpires = false) - { - Guard.NotNull(state, nameof(state)); - - var value = GetStateInfo(name); - - if (value != null) - { - // exists already, so update - if (state != null) - { - value.Progress = state; - } - } - else - { - // add new entry - var duration = neverExpires ? TimeSpan.Zero : TimeSpan.FromMinutes(15); - var policy = new CacheItemPolicy { SlidingExpiration = duration }; - var key = BuildKey(name); - - // On expiration or removal: remove corresponding cancel token also. - policy.RemovedCallback = (x) => OnRemoveCancelTokenSource(key); - - _states.Set(key, new AsyncStateInfo { Progress = state, Duration = duration }, policy); - } - } - - public virtual void Update(Action update, string name = null) - { - Guard.NotNull(update, nameof(update)); - - var value = GetStateInfo(name); - - if (value != null) - { - var state = (T)value.Progress; - if (state != null) - { - update(state); - } - } - } - - public virtual bool Remove(string name = null) - { - var key = BuildKey(name); - - if (OnRemoveStateInfo(key)) - { - OnRemoveCancelTokenSource(key); - return true; - } - - return false; - } - - protected virtual bool OnRemoveStateInfo(string key) - { - return _states.Remove(key) != null; - } - - protected virtual bool OnRemoveCancelTokenSource(string key, bool successive = false) - { - Guard.NotEmpty(key, nameof(key)); - - var token = _cancelTokens.Remove(key) as CancellationTokenSource; - - if (token != null) - { - token.Dispose(); - return true; - } - - return false; - } - - - public CancellationTokenSource GetCancelTokenSource(string name = null) - { - return OnGetCancelTokenSource(BuildKey(name)); - } - - protected virtual CancellationTokenSource OnGetCancelTokenSource(string key, bool successive = false) - { - Guard.NotEmpty(key, nameof(key)); - - var token = _cancelTokens.Get(key); - - if (token != null) - { - return (CancellationTokenSource)token; - } - - return null; - } - - public virtual void SetCancelTokenSource(CancellationTokenSource cancelTokenSource, string name = null) - { - Guard.NotNull(cancelTokenSource, nameof(cancelTokenSource)); - - var key = BuildKey(name); - - if (Exists(name)) - { - OnRemoveCancelTokenSource(key); - } - - var policy = new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(15) }; - - _cancelTokens.Set(key, cancelTokenSource, policy); - } - - public bool Cancel(string name = null) - { - return OnCancel(BuildKey(name)); - } + public partial class LocalAsyncState : IAsyncState + { + private readonly MemoryCache _states = new MemoryCache("SmartStore.AsyncState.Progress"); + private readonly MemoryCache _cancelTokens = new MemoryCache("SmartStore.AsyncState.CancelTokenSources"); + + public virtual bool Exists(string name = null) + { + var value = GetStateInfo(name); + return value != null && !object.Equals(value.Progress, default(T)); + + } + + public virtual T Get(string name = null) + { + var value = GetStateInfo(name); + + if (value != null) + { + return (T)value.Progress; + } + + return default(T); + } + + public virtual IEnumerable GetAll() + { + var keyPrefix = BuildKey(null); + return _states + .Where(x => x.Key.StartsWith(keyPrefix)) + .Select(x => x.Value) + .OfType() + .Select(x => x.Progress) + .OfType(); + } + + + public virtual void Set(T state, string name = null, bool neverExpires = false) + { + Guard.NotNull(state, nameof(state)); + + var value = GetStateInfo(name); + + if (value != null) + { + // exists already, so update + if (state != null) + { + value.Progress = state; + } + } + else + { + // add new entry + var duration = neverExpires ? TimeSpan.Zero : TimeSpan.FromMinutes(15); + var policy = new CacheItemPolicy + { + SlidingExpiration = duration, + Priority = CacheItemPriority.NotRemovable + }; + var key = BuildKey(name); + + // On expiration or removal: remove corresponding cancel token also. + policy.RemovedCallback = (x) => OnRemoveCancelTokenSource(key); + + _states.Set(key, new AsyncStateInfo { Progress = state, Duration = duration }, policy); + } + } + + public virtual void Update(Action update, string name = null) + { + Guard.NotNull(update, nameof(update)); + + var value = GetStateInfo(name); + + if (value != null) + { + var state = (T)value.Progress; + if (state != null) + { + update(state); + } + } + } + + public virtual bool Remove(string name = null) + { + var key = BuildKey(name); + + if (!OnRemoveStateInfo(key)) + { + // Remove corresponding cancel token (if any). + // Because the state does not exist, "RemovedCallBack" + // did not run. + OnRemoveCancelTokenSource(key); + return false; + } + + return true; + } + + protected virtual bool OnRemoveStateInfo(string key) + { + return _states.Remove(key) != null; + } + + public virtual bool RemoveCancelTokenSource(string name = null) + { + return OnRemoveCancelTokenSource(BuildKey(name)); + } + + protected virtual bool OnRemoveCancelTokenSource(string key, bool successive = false) + { + Guard.NotEmpty(key, nameof(key)); + + var token = _cancelTokens.Remove(key) as CancellationTokenSource; + + if (token != null) + { + token.Dispose(); + return true; + } + + return false; + } + + + public CancellationTokenSource GetCancelTokenSource(string name = null) + { + return OnGetCancelTokenSource(BuildKey(name)); + } + + protected virtual CancellationTokenSource OnGetCancelTokenSource(string key, bool successive = false) + { + Guard.NotEmpty(key, nameof(key)); + + return _cancelTokens.Get(key) as CancellationTokenSource; + } + + public virtual void SetCancelTokenSource(CancellationTokenSource cancelTokenSource, string name = null) + { + Guard.NotNull(cancelTokenSource, nameof(cancelTokenSource)); + + var key = BuildKey(name); + + if (Exists(name)) + { + OnRemoveCancelTokenSource(key); + } + + var policy = new CacheItemPolicy { Priority = CacheItemPriority.NotRemovable }; + + _cancelTokens.Set(key, cancelTokenSource, policy); + } + + public bool Cancel(string name = null) + { + return OnCancel(BuildKey(name)); + } + + protected virtual bool OnCancel(string key, bool successive = false) + { + Guard.NotEmpty(key, nameof(key)); + + var cts = OnGetCancelTokenSource(key); + + if (cts != null) + { + cts.Cancel(); + return true; + } + + return false; + } + + + protected virtual AsyncStateInfo GetStateInfo(string name = null) + { + return _states.Get(BuildKey(name)) as AsyncStateInfo; + } - protected virtual bool OnCancel(string key, bool successive = false) - { - Guard.NotEmpty(key, nameof(key)); + protected string BuildKey(string name) + { + return BuildKey(typeof(T), name); + } - var cts = OnGetCancelTokenSource(key); - - if (cts != null) - { - cts.Cancel(); - return true; - } - - return false; - } - - - protected virtual AsyncStateInfo GetStateInfo(string name = null) - { - var key = BuildKey(name); - var value = _states.Get(key) as AsyncStateInfo; - - if (value != null) - { - // ensures that sliding expiration gets updated on cancel token - _cancelTokens.Get(key); - } - - return value; - } - - protected string BuildKey(string name) - { - return BuildKey(typeof(T), name); - } - - protected virtual string BuildKey(Type type, string name) - { - return "{0}{1}".FormatInvariant(type.FullName, name.HasValue() ? ":" + name : ""); - } - } + protected virtual string BuildKey(Type type, string name) + { + return "{0}{1}".FormatInvariant(type.FullName, name.HasValue() ? ":" + name : ""); + } + } } diff --git a/src/Libraries/SmartStore.Core/BaseEntity.cs b/src/Libraries/SmartStore.Core/BaseEntity.cs index 3fd0016909..e2882f9851 100644 --- a/src/Libraries/SmartStore.Core/BaseEntity.cs +++ b/src/Libraries/SmartStore.Core/BaseEntity.cs @@ -1,103 +1,104 @@ 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.CompilerServices; using System.Runtime.Serialization; namespace SmartStore.Core -{ +{ /// /// Base class for entities /// [DataContract] public abstract partial class BaseEntity : IEquatable - { - /// + { + /// /// Gets or sets the entity identifier /// - [DataMember] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + [DataMember] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } - [SuppressMessage("ReSharper", "PossibleNullReferenceException")] - public virtual string GetEntityName() - { - return GetUnproxiedType().Name; - } + [SuppressMessage("ReSharper", "PossibleNullReferenceException")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual string GetEntityName() + { + return GetUnproxiedType().Name; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + 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 + + return ObjectContext.GetObjectType(GetType()); + } + + /// + /// Transient objects are not associated with an item already in storage. For instance, + /// a Product entity is transient if its Id is 0. + /// + public virtual bool IsTransientRecord() + { + return Id == 0; + } + + public override bool Equals(object obj) + { + return this.Equals(obj as BaseEntity); + } - public Type GetUnproxiedType() + bool IEquatable.Equals(BaseEntity other) { - #region Old - //var t = GetType(); - //if (t.AssemblyQualifiedName.StartsWith("System.Data.Entity.")) - //{ - // // it's a proxied type - // t = t.BaseType; - //} - - //return t; - #endregion - - return ObjectContext.GetObjectType(GetType()); - } - - /// - /// Transient objects are not associated with an item already in storage. For instance, - /// a Product entity is transient if its Id is 0. - /// - public virtual bool IsTransientRecord() - { - return Id == 0; - } - - public override bool Equals(object obj) - { - return this.Equals(obj as BaseEntity); - } - - bool IEquatable.Equals(BaseEntity other) - { - return this.Equals(other); - } - - protected virtual bool Equals(BaseEntity other) - { - if (other == null) - return false; - - if (ReferenceEquals(this, other)) - return true; - - if (HasSameNonDefaultIds(other)) - { - var otherType = other.GetUnproxiedType(); - var thisType = GetUnproxiedType(); - return thisType.Equals(otherType); - } - - return false; - } - - [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] - public override int GetHashCode() + return this.Equals(other); + } + + protected virtual bool Equals(BaseEntity other) { - if (IsTransientRecord()) - { - return base.GetHashCode(); - } - else - { - unchecked - { - // It's possible for two objects to return the same hash code based on - // identically valued properties, even if they're of two different types, - // so we include the object's type in the hash calculation - var hashCode = GetUnproxiedType().GetHashCode(); - return (hashCode * 31) ^ Id.GetHashCode(); - } - } + if (other == null) + return false; + + if (ReferenceEquals(this, other)) + return true; + + if (HasSameNonDefaultIds(other)) + { + var otherType = other.GetUnproxiedType(); + var thisType = GetUnproxiedType(); + return thisType.Equals(otherType); + } + + return false; + } + + [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] + public override int GetHashCode() + { + if (IsTransientRecord()) + { + return base.GetHashCode(); + } + else + { + unchecked + { + // It's possible for two objects to return the same hash code based on + // identically valued properties, even if they're of two different types, + // so we include the object's type in the hash calculation + var hashCode = GetUnproxiedType().GetHashCode(); + return (hashCode * 31) ^ Id.GetHashCode(); + } + } } public static bool operator ==(BaseEntity x, BaseEntity y) @@ -110,9 +111,9 @@ public override int GetHashCode() return !(x == y); } - private bool HasSameNonDefaultIds(BaseEntity other) - { - return !this.IsTransientRecord() && !other.IsTransientRecord() && this.Id == other.Id; - } - } + private bool HasSameNonDefaultIds(BaseEntity other) + { + return !this.IsTransientRecord() && !other.IsTransientRecord() && this.Id == other.Id; + } + } } diff --git a/src/Libraries/SmartStore.Core/Caching/CacheScope.cs b/src/Libraries/SmartStore.Core/Caching/CacheScope.cs index b705ab10b6..b983a2a6cf 100644 --- a/src/Libraries/SmartStore.Core/Caching/CacheScope.cs +++ b/src/Libraries/SmartStore.Core/Caching/CacheScope.cs @@ -1,41 +1,34 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; namespace SmartStore.Core.Caching { - public class CacheScope - { - private HashSet _dependencies; + public class CacheScope + { + private HashSet _dependencies; - public CacheScope(string key) - { - Key = key; - } + public CacheScope(string key) + { + Key = key; + } - public string Key { get; private set; } + public string Key { get; private set; } - public void AddDependency(string key) - { - if (key == null) - { - return; - } + public void AddDependency(string key) + { + if (key == null) + { + return; + } - if (_dependencies == null) - { - _dependencies = new HashSet(); - } + if (_dependencies == null) + { + _dependencies = new HashSet(); + } - _dependencies.Add(key); - } + _dependencies.Add(key); + } - public IEnumerable Dependencies - { - get - { - return _dependencies ?? Enumerable.Empty(); - } - } - } + public IEnumerable Dependencies => _dependencies ?? Enumerable.Empty(); + } } diff --git a/src/Libraries/SmartStore.Core/Caching/CacheScopeAccessor.cs b/src/Libraries/SmartStore.Core/Caching/CacheScopeAccessor.cs index b04e0a7d45..b8e23759a9 100644 --- a/src/Libraries/SmartStore.Core/Caching/CacheScopeAccessor.cs +++ b/src/Libraries/SmartStore.Core/Caching/CacheScopeAccessor.cs @@ -5,58 +5,58 @@ namespace SmartStore.Core.Caching { - public class CacheScopeAccessor : ICacheScopeAccessor - { - private readonly Stack _stack = new Stack(); - - public CacheScopeAccessor() - { - } - - public CacheScope Current - { - get - { - _stack.TryPeek(out var result); - return result; - } - } - - public bool HasScope(string key) - { - return _stack.Any(x => x.Key.IsCaseInsensitiveEqual(key)); - } - - public void PropagateKey(string key) - { - var current = Current; - if (current != null) - { - current.AddDependency(key); - } - } - - public IDisposable BeginScope(string key, bool independent = false) - { - if (Current?.Key == key) - { - // Void if parent scope has the same key. - return ActionDisposable.Empty; - } - - _stack.Push(new CacheScope(key)); - - var action = new ActionDisposable(() => - { - _stack.TryPop(out _); - - if (!independent) - { - PropagateKey(key); - } - }); - - return action; - } - } + public class CacheScopeAccessor : ICacheScopeAccessor + { + private readonly Stack _stack = new Stack(); + + public CacheScopeAccessor() + { + } + + public CacheScope Current + { + get + { + _stack.TryPeek(out var result); + return result; + } + } + + public bool HasScope(string key) + { + return _stack.Any(x => x.Key.IsCaseInsensitiveEqual(key)); + } + + public void PropagateKey(string key) + { + var current = Current; + if (current != null) + { + current.AddDependency(key); + } + } + + public IDisposable BeginScope(string key, bool independent = false) + { + if (Current?.Key == key) + { + // Void if parent scope has the same key. + return ActionDisposable.Empty; + } + + _stack.Push(new CacheScope(key)); + + var action = new ActionDisposable(() => + { + _stack.TryPop(out _); + + if (!independent) + { + PropagateKey(key); + } + }); + + return action; + } + } } diff --git a/src/Libraries/SmartStore.Core/Caching/ICacheManager.cs b/src/Libraries/SmartStore.Core/Caching/ICacheManager.cs index 855349106f..43931b9edd 100644 --- a/src/Libraries/SmartStore.Core/Caching/ICacheManager.cs +++ b/src/Libraries/SmartStore.Core/Caching/ICacheManager.cs @@ -9,58 +9,60 @@ namespace SmartStore.Core.Caching /// public interface ICacheManager { - /// - /// Gets a cache item associated with the specified key - /// - /// The type of the item to get - /// The cache item key - /// When true, no attemp will be made to invalidate depending/parent cache entries. - /// Cached item value or null if item with specified key does not exist in the cache - T Get(string key, bool independent = false); + /// + /// Gets a cache item associated with the specified key + /// + /// The type of the item to get + /// The cache item key + /// When true, no attemp will be made to invalidate depending/parent cache entries. + /// Cached item value or null if item with specified key does not exist in the cache + T Get(string key, bool independent = false); - /// - /// Gets a cache item associated with the specified key or adds the item - /// if it doesn't exist in the cache. - /// - /// The type of the item to get or add - /// The cache item key - /// Func which returns value to be added to the cache - /// Absolute expiration time - /// When true, no attemp will be made to invalidate depending/parent cache entries. - /// Cached item value - T Get(string key, Func acquirer, TimeSpan? duration = null, bool independent = false); + /// + /// Gets a cache item associated with the specified key or adds the item + /// if it doesn't exist in the cache. + /// + /// The type of the item to get or add + /// The cache item key + /// Func which returns value to be added to the cache + /// Absolute expiration time + /// When true, no attemp will be made to invalidate depending/parent cache entries. + /// When false, an exception will be thrown when the acquirer tries to acces the same cache item. + /// Cached item value + T Get(string key, Func acquirer, TimeSpan? duration = null, bool independent = false, bool allowRecursion = false); - /// - /// Gets a cache item associated with the specified key or - if it doesn't exist in the cache - - /// adds the item obtained from the asynchronous acquirer . - /// - /// The type of the item to get or add - /// The cache item key - /// Func which returns value to be added to the cache - /// Absolute expiration time - /// When true, no attemp will be made to invalidate depending/parent cache entries. - /// Cached item value - Task GetAsync(string key, Func> acquirer, TimeSpan? duration = null, bool independent = false); + /// + /// Gets a cache item associated with the specified key or - if it doesn't exist in the cache - + /// adds the item obtained from the asynchronous acquirer . + /// + /// The type of the item to get or add + /// The cache item key + /// Func which returns value to be added to the cache + /// Absolute expiration time + /// When true, no attemp will be made to invalidate depending/parent cache entries. + /// When false, an exception will be thrown when the acquirer tries to acces the same cache item. + /// Cached item value + Task GetAsync(string key, Func> acquirer, TimeSpan? duration = null, bool independent = false, bool allowRecursion = false); - /// - /// Gets a hashset associated with the specified key or. If key does not exist, - /// a new set is created and put to cache automatically - /// - /// The set cache item key - /// Optional acquirer callback that is invoked when requested set does not exist yet. - /// Cached item value - ISet GetHashSet(string key, Func> acquirer = null); + /// + /// Gets a hashset associated with the specified key or. If key does not exist, + /// a new set is created and put to cache automatically + /// + /// The set cache item key + /// Optional acquirer callback that is invoked when requested set does not exist yet. + /// Cached item value + ISet GetHashSet(string key, Func> acquirer = null); - /// - /// Adds a cache item with the specified key - /// - /// Key - /// Value - /// Absolute expiration time - /// - /// A list of keys for cache entries that should be removed from the cache whenever this entry changes. - /// - void Put(string key, object value, TimeSpan? duration = null, IEnumerable dependencies = null); + /// + /// Adds a cache item with the specified key + /// + /// Key + /// Value + /// Absolute expiration time + /// + /// A list of keys for cache entries that should be removed from the cache whenever this entry changes. + /// + void Put(string key, object value, TimeSpan? duration = null, IEnumerable dependencies = null); /// /// Gets a value indicating whether the value associated with the specified key is cached @@ -75,44 +77,44 @@ public interface ICacheManager /// /key void Remove(string key); - /// - /// Scans for all keys matching the input pattern - /// - /// A key pattern. Can be null. - /// An array of matching key names - /// - /// Supported glob-style patterns: - /// - h?llo matches hello, hallo and hxllo - /// - h*llo matches hllo and heeeello - /// - h[ae]llo matches hello and hallo, but not hillo - /// - h[^e]llo matches hallo, hbllo, ... but not hello - /// - h[a-b]llo matches hallo and hbllo - /// - IEnumerable Keys(string pattern); + /// + /// Scans for all keys matching the input pattern + /// + /// A key pattern. Can be null. + /// An array of matching key names + /// + /// Supported glob-style patterns: + /// - h?llo matches hello, hallo and hxllo + /// - h*llo matches hllo and heeeello + /// - h[ae]llo matches hello and hallo, but not hillo + /// - h[^e]llo matches hallo, hbllo, ... but not hello + /// - h[a-b]llo matches hallo and hbllo + /// + IEnumerable Keys(string pattern); - /// - /// Removes all entries with keys matching the input pattern - /// - /// Glob pattern - /// Count of removed cache items - /// - /// Supported glob-style patterns: - /// - h?llo matches hello, hallo and hxllo - /// - h*llo matches hllo and heeeello - /// - h[ae]llo matches hello and hallo, but not hillo - /// - h[^e]llo matches hallo, hbllo, ... but not hello - /// - h[a-b]llo matches hallo and hbllo - /// - int RemoveByPattern(string pattern); + /// + /// Removes all entries with keys matching the input pattern + /// + /// Glob pattern + /// Count of removed cache items + /// + /// Supported glob-style patterns: + /// - h?llo matches hello, hallo and hxllo + /// - h*llo matches hllo and heeeello + /// - h[ae]llo matches hello and hallo, but not hillo + /// - h[^e]llo matches hallo, hbllo, ... but not hello + /// - h[a-b]llo matches hallo and hbllo + /// + int RemoveByPattern(string pattern); /// /// Clear all cache data /// void Clear(); - /// - /// Gets a value indicating whether the cache is distributed (e.g. Redis) - /// - bool IsDistributedCache { get; } + /// + /// Gets a value indicating whether the cache is distributed (e.g. Redis) + /// + bool IsDistributedCache { get; } } } diff --git a/src/Libraries/SmartStore.Core/Caching/ICacheScopeAccessor.cs b/src/Libraries/SmartStore.Core/Caching/ICacheScopeAccessor.cs index a640e6b200..20d219e631 100644 --- a/src/Libraries/SmartStore.Core/Caching/ICacheScopeAccessor.cs +++ b/src/Libraries/SmartStore.Core/Caching/ICacheScopeAccessor.cs @@ -2,33 +2,33 @@ namespace SmartStore.Core.Caching { - public interface ICacheScopeAccessor - { - /// - /// Returns the current cache acquire scope (for current thread) - /// - CacheScope Current { get; } + public interface ICacheScopeAccessor + { + /// + /// Returns the current cache acquire scope (for current thread) + /// + CacheScope Current { get; } - /// - /// Whether any scope with the passed exists already. Used to prevent lock recursions. - /// - /// Key - bool HasScope(string key); + /// + /// Whether any scope with the passed exists already. Used to prevent lock recursions. + /// + /// Key + bool HasScope(string key); - /// - /// Notifies the current scope about a cache dependency. - /// - /// - void PropagateKey(string key); + /// + /// Notifies the current scope about a cache dependency. + /// + /// + void PropagateKey(string key); - /// - /// Begins a new cache acquire scope. - /// - /// Key - /// - /// true means that no key propagation will be performed, making the current cache entry independent from its parent. - /// Meaning: parent cache entries will not be auto-invalidated when this entry changes. - /// - IDisposable BeginScope(string key, bool independent = false); - } + /// + /// Begins a new cache acquire scope. + /// + /// Key + /// + /// true means that no key propagation will be performed, making the current cache entry independent from its parent. + /// Meaning: parent cache entries will not be auto-invalidated when this entry changes. + /// + IDisposable BeginScope(string key, bool independent = false); + } } diff --git a/src/Libraries/SmartStore.Core/Caching/IRequestCache.cs b/src/Libraries/SmartStore.Core/Caching/IRequestCache.cs index 2ecd14f0b8..e37a607bff 100644 --- a/src/Libraries/SmartStore.Core/Caching/IRequestCache.cs +++ b/src/Libraries/SmartStore.Core/Caching/IRequestCache.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; namespace SmartStore.Core.Caching { @@ -7,31 +6,31 @@ namespace SmartStore.Core.Caching /// Request cache interface /// public interface IRequestCache - { - /// - /// Gets a cache item associated with the specified key - /// - /// The type of the item to get - /// The cache item key - /// Cached item value or null if item with specified key does not exist in the cache - T Get(string key); - - /// - /// Gets a cache item associated with the specified key or adds the item - /// if it doesn't exist in the cache. - /// - /// The type of the item to get or add - /// The cache item key - /// Func which returns value to be added to the cache - /// Cached item value - T Get(string key, Func acquirer); - - /// - /// Adds a cache item with the specified key - /// - /// Key - /// Value - void Put(string key, object value); + { + /// + /// Gets a cache item associated with the specified key + /// + /// The type of the item to get + /// The cache item key + /// Cached item value or null if item with specified key does not exist in the cache + T Get(string key); + + /// + /// Gets a cache item associated with the specified key or adds the item + /// if it doesn't exist in the cache. + /// + /// The type of the item to get or add + /// The cache item key + /// Func which returns value to be added to the cache + /// Cached item value + T Get(string key, Func acquirer); + + /// + /// Adds a cache item with the specified key + /// + /// Key + /// Value + void Put(string key, object value); /// /// Gets a value indicating whether the value associated with the specified key is cached @@ -46,63 +45,60 @@ public interface IRequestCache /// /key void Remove(string key); - /// - /// Removes items by pattern - /// - /// pattern - void RemoveByPattern(string pattern); - - /// - /// Clear all cache data - /// - void Clear(); - } - - /// - /// For testing purposes - /// - public class NullRequestCache : IRequestCache - { - private static readonly IRequestCache s_instance = new NullRequestCache(); - - public static IRequestCache Instance - { - get { return s_instance; } - } - - public void Clear() - { - } - - public bool Contains(string key) - { - return false; - } - - public T Get(string key) - { - return default(T); - } - - public T Get(string key, Func acquirer) - { - if (acquirer == null) - { - return default(T); - } - return acquirer(); - } - - public void Remove(string key) - { - } - - public void RemoveByPattern(string pattern) - { - } - - public void Put(string key, object value) - { - } - } + /// + /// Removes items by pattern + /// + /// pattern + void RemoveByPattern(string pattern); + + /// + /// Clear all cache data + /// + void Clear(); + } + + /// + /// For testing purposes + /// + public class NullRequestCache : IRequestCache + { + private static readonly IRequestCache s_instance = new NullRequestCache(); + + public static IRequestCache Instance => s_instance; + + public void Clear() + { + } + + public bool Contains(string key) + { + return false; + } + + public T Get(string key) + { + return default(T); + } + + public T Get(string key, Func acquirer) + { + if (acquirer == null) + { + return default(T); + } + return acquirer(); + } + + public void Remove(string key) + { + } + + public void RemoveByPattern(string pattern) + { + } + + public void Put(string key, object value) + { + } + } } diff --git a/src/Libraries/SmartStore.Core/Caching/ISet.cs b/src/Libraries/SmartStore.Core/Caching/ISet.cs index d0bff72bd7..3c2c7ff2b1 100644 --- a/src/Libraries/SmartStore.Core/Caching/ISet.cs +++ b/src/Libraries/SmartStore.Core/Caching/ISet.cs @@ -1,21 +1,20 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace SmartStore.Core.Caching { - public interface ISet : IEnumerable - { - bool Add(string item); - void AddRange(IEnumerable items); - void Clear(); - bool Contains(string item); - bool Remove(string item); - bool Move(string destinationKey, string item); + public interface ISet : IEnumerable + { + bool Add(string item); + void AddRange(IEnumerable items); + void Clear(); + bool Contains(string item); + bool Remove(string item); + bool Move(string destinationKey, string item); - long UnionWith(params string[] keys); - long IntersectWith(params string[] keys); - long ExceptWith(params string[] keys); + long UnionWith(params string[] keys); + long IntersectWith(params string[] keys); + long ExceptWith(params string[] keys); - int Count { get; } - } + int Count { get; } + } } diff --git a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs index a00224784d..332b199c61 100644 --- a/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs +++ b/src/Libraries/SmartStore.Core/Caching/MemoryCacheManager.cs @@ -1,253 +1,254 @@ using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using System.Runtime.Caching; -using System.Threading.Tasks; -using System.Collections.Concurrent; +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; using System.Threading; -using System.Collections.Generic; +using System.Threading.Tasks; +using SmartStore.Core.Infrastructure.DependencyManagement; using SmartStore.Utilities; -using System.Text.RegularExpressions; -using System.Diagnostics; using SmartStore.Utilities.Threading; -using SmartStore.Core.Infrastructure.DependencyManagement; namespace SmartStore.Core.Caching { public partial class MemoryCacheManager : DisposableObject, ICacheManager { - const string LockRecursionExceptionMessage = "Acquiring identical cache items recursively is not supported. Key: {0}"; - - // Wwe put a special string into cache if value is null, - // otherwise our 'Contains()' would always return false, - // which is bad if we intentionally wanted to save NULL values. - public const string FakeNull = "__[NULL]__"; - - private readonly Work _scopeAccessor; - private MemoryCache _cache; - private readonly ConcurrentDictionary _keyLocks = new ConcurrentDictionary(); - - public MemoryCacheManager(Work scopeAccessor) - { - _scopeAccessor = scopeAccessor; - _cache = CreateCache(); - } - - private MemoryCache CreateCache() - { - return new MemoryCache("SmartStore"); - } - - public bool IsDistributedCache - { - get { return false; } - } - - private bool TryGet(string key, bool independent, out T value) - { - value = default(T); - - object obj = _cache.Get(key); - - if (obj != null) - { - // Make the parent scope's entry depend on this - if (!independent) - { - _scopeAccessor.Value.PropagateKey(key); - } - - if (obj.Equals(FakeNull)) - { - return true; - } - - value = (T)obj; - return true; - } - - return false; - } - - public T Get(string key, bool independent = false) - { - TryGet(key, independent, out T value); - return value; - } - - public T Get(string key, Func acquirer, TimeSpan? duration = null, bool independent = false) + const string LockRecursionExceptionMessage = "Acquiring identical cache items recursively is not supported. Key: {0}"; + + // We put a special string into cache if value is null, + // otherwise our 'Contains()' would always return false, + // which is bad if we intentionally wanted to save NULL values. + public const string FakeNull = "__[NULL]__"; + + private readonly Work _scopeAccessor; + private MemoryCache _cache; + + public MemoryCacheManager(Work scopeAccessor) + { + _scopeAccessor = scopeAccessor; + _cache = CreateCache(); + } + + private MemoryCache CreateCache() + { + return new MemoryCache("SmartStore"); + } + + public bool IsDistributedCache => false; + + private bool TryGet(string key, bool independent, out T value) + { + value = default(T); + + object obj = _cache.Get(key); + + if (obj != null) + { + // Make the parent scope's entry depend on this + if (!independent) + { + _scopeAccessor.Value.PropagateKey(key); + } + + if (obj.Equals(FakeNull)) + { + return true; + } + + value = (T)obj; + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Get(string key, bool independent = false) { - if (TryGet(key, independent, out T value)) - { - return value; - } - - if (_scopeAccessor.Value.HasScope(key)) - { - throw new LockRecursionException(LockRecursionExceptionMessage.FormatInvariant(key)); - } - - // Get the (semaphore) locker specific to this key - using (KeyedLock.Lock("cache:" + key, TimeSpan.FromMinutes(1))) - { - // Atomic operation must be outer locked - if (!TryGet(key, independent, out value)) - { - using (_scopeAccessor.Value.BeginScope(key)) - { - value = acquirer(); - Put(key, value, duration, _scopeAccessor.Value.Current.Dependencies); - return value; - } - } - } - - return value; - } - - public async Task GetAsync(string key, Func> acquirer, TimeSpan? duration = null, bool independent = false) - { - if (TryGet(key, independent, out T value)) - { - return value; - } - - if (_scopeAccessor.Value.HasScope(key)) - { - throw new LockRecursionException(LockRecursionExceptionMessage.FormatInvariant(key)); - } - - // Get the async (semaphore) locker specific to this key - using (await KeyedLock.LockAsync("cache:" + key, TimeSpan.FromMinutes(1))) - { - if (!TryGet(key, independent, out value)) - { - using (_scopeAccessor.Value.BeginScope(key)) - { - value = await acquirer(); - Put(key, value, duration, _scopeAccessor.Value.Current.Dependencies); - return value; - } - } - } - - return value; - } - - public void Put(string key, object value, TimeSpan? duration = null, IEnumerable dependencies = null) - { - _cache.Set(key, value ?? FakeNull, GetCacheItemPolicy(duration, dependencies)); - } + TryGet(key, independent, out T value); + return value; + } + + public T Get(string key, Func acquirer, TimeSpan? duration = null, bool independent = false, bool allowRecursion = false) + { + if (TryGet(key, independent, out T value)) + { + return value; + } + + if (!allowRecursion && _scopeAccessor.Value.HasScope(key)) + { + throw new LockRecursionException(LockRecursionExceptionMessage.FormatInvariant(key)); + } + + // Get the (semaphore) locker specific to this key + using (KeyedLock.Lock("cache:" + key, TimeSpan.FromSeconds(5))) + { + // Atomic operation must be outer locked + if (!TryGet(key, independent, out value)) + { + var scope = !allowRecursion ? _scopeAccessor.Value.BeginScope(key) : ActionDisposable.Empty; + using (scope) + { + value = acquirer(); + var dependencies = !allowRecursion ? _scopeAccessor.Value.Current?.Dependencies : (IEnumerable)null; + Put(key, value, duration, dependencies); + return value; + } + } + } + + return value; + } + + public async Task GetAsync(string key, Func> acquirer, TimeSpan? duration = null, bool independent = false, bool allowRecursion = false) + { + if (TryGet(key, independent, out T value)) + { + return value; + } + + if (!allowRecursion && _scopeAccessor.Value.HasScope(key)) + { + throw new LockRecursionException(LockRecursionExceptionMessage.FormatInvariant(key)); + } + + // Get the async (semaphore) locker specific to this key + using (await KeyedLock.LockAsync("cache:" + key, TimeSpan.FromMinutes(1))) + { + if (!TryGet(key, independent, out value)) + { + var scope = !allowRecursion ? _scopeAccessor.Value.BeginScope(key) : ActionDisposable.Empty; + using (scope) + { + value = await acquirer(); + var dependencies = !allowRecursion ? _scopeAccessor.Value.Current?.Dependencies : (IEnumerable)null; + Put(key, value, duration, dependencies); + return value; + } + } + } + + return value; + } + + public void Put(string key, object value, TimeSpan? duration = null, IEnumerable dependencies = null) + { + _cache.Set(key, value ?? FakeNull, GetCacheItemPolicy(duration, dependencies)); + } public bool Contains(string key) { - return _cache.Contains(key); + return _cache.Contains(key); } public void Remove(string key) { - _cache.Remove(key); + _cache.Remove(key); } - public IEnumerable Keys(string pattern) - { - Guard.NotEmpty(pattern, nameof(pattern)); + public IEnumerable Keys(string pattern) + { + Guard.NotEmpty(pattern, nameof(pattern)); - var keys = _cache.AsParallel().Select(x => x.Key); + var keys = _cache.AsParallel().Select(x => x.Key); - if (pattern.IsEmpty() || pattern == "*") - { - return keys.ToArray(); - } + if (pattern.IsEmpty() || pattern == "*") + { + return keys.ToArray(); + } - var wildcard = new Wildcard(pattern, RegexOptions.IgnoreCase); - return keys.Where(x => wildcard.IsMatch(x)).ToArray(); - } + var wildcard = new Wildcard(pattern, RegexOptions.IgnoreCase); + return keys.Where(x => wildcard.IsMatch(x)).ToArray(); + } - public int RemoveByPattern(string pattern) + public int RemoveByPattern(string pattern) { - lock (_cache) - { - var keysToRemove = Keys(pattern); - int count = 0; - - // lock atomic operation - foreach (string key in keysToRemove) - { - _cache.Remove(key); - count++; - } - - return count; - } - } + lock (_cache) + { + var keysToRemove = Keys(pattern); + int count = 0; + + // lock atomic operation + foreach (string key in keysToRemove) + { + _cache.Remove(key); + count++; + } + + return count; + } + } public void Clear() { - // Faster way of clearing cache: https://stackoverflow.com/questions/8043381/how-do-i-clear-a-system-runtime-caching-memorycache - var oldCache = Interlocked.Exchange(ref _cache, CreateCache()); - oldCache.Dispose(); - GC.Collect(); - } - - public virtual ISet GetHashSet(string key, Func> acquirer = null) - { - var result = Get(key, () => - { - var set = new MemorySet(this); - var items = acquirer?.Invoke(); - if (items != null) - { - set.AddRange(items); - } - - return set; - }); - - return result; - } - - private CacheItemPolicy GetCacheItemPolicy(TimeSpan? duration, IEnumerable dependencies) - { - var absoluteExpiration = ObjectCache.InfiniteAbsoluteExpiration; - - if (duration.HasValue) - { - absoluteExpiration = DateTime.UtcNow + duration.Value; - } - - var cacheItemPolicy = new CacheItemPolicy - { - AbsoluteExpiration = absoluteExpiration, - SlidingExpiration = ObjectCache.NoSlidingExpiration - }; - - if (dependencies != null && dependencies.Any()) - { - // INFO: we can only depend on existing items, otherwise this entry will be removed immediately. - dependencies = dependencies.Where(x => x != null && _cache.Contains(x)); - if (dependencies.Any()) - { - cacheItemPolicy.ChangeMonitors.Add(_cache.CreateCacheEntryChangeMonitor(dependencies)); - } - } - - //cacheItemPolicy.RemovedCallback = OnRemoveEntry; - - return cacheItemPolicy; - } - - //private void OnRemoveEntry(CacheEntryRemovedArguments args) - //{ - // if (args.RemovedReason == CacheEntryRemovedReason.ChangeMonitorChanged) - // { - // Debug.WriteLine("MEMCACHE: remove depending entry '{0}'.".FormatInvariant(args.CacheItem.Key)); - // } - //} - - protected override void OnDispose(bool disposing) - { - if (disposing) - _cache.Dispose(); - } - } + // Faster way of clearing cache: https://stackoverflow.com/questions/8043381/how-do-i-clear-a-system-runtime-caching-memorycache + var oldCache = Interlocked.Exchange(ref _cache, CreateCache()); + oldCache.Dispose(); + GC.Collect(); + } + + public virtual ISet GetHashSet(string key, Func> acquirer = null) + { + var result = Get(key, () => + { + var set = new MemorySet(this); + var items = acquirer?.Invoke(); + if (items != null) + { + set.AddRange(items); + } + + return set; + }); + + return result; + } + + private CacheItemPolicy GetCacheItemPolicy(TimeSpan? duration, IEnumerable dependencies) + { + var absoluteExpiration = ObjectCache.InfiniteAbsoluteExpiration; + + if (duration.HasValue) + { + absoluteExpiration = DateTime.UtcNow + duration.Value; + } + + var cacheItemPolicy = new CacheItemPolicy + { + AbsoluteExpiration = absoluteExpiration, + SlidingExpiration = ObjectCache.NoSlidingExpiration + }; + + if (dependencies != null && dependencies.Any()) + { + // INFO: we can only depend on existing items, otherwise this entry will be removed immediately. + dependencies = dependencies.Where(x => x != null && _cache.Contains(x)); + if (dependencies.Any()) + { + cacheItemPolicy.ChangeMonitors.Add(_cache.CreateCacheEntryChangeMonitor(dependencies)); + } + } + + //cacheItemPolicy.RemovedCallback = OnRemoveEntry; + + return cacheItemPolicy; + } + + //private void OnRemoveEntry(CacheEntryRemovedArguments args) + //{ + // if (args.RemovedReason == CacheEntryRemovedReason.ChangeMonitorChanged) + // { + // Debug.WriteLine("MEMCACHE: remove depending entry '{0}'.".FormatInvariant(args.CacheItem.Key)); + // } + //} + + protected override void OnDispose(bool disposing) + { + if (disposing) + _cache.Dispose(); + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Caching/MemorySet.cs b/src/Libraries/SmartStore.Core/Caching/MemorySet.cs index c88cacd703..8c1e13245b 100644 --- a/src/Libraries/SmartStore.Core/Caching/MemorySet.cs +++ b/src/Libraries/SmartStore.Core/Caching/MemorySet.cs @@ -1,64 +1,63 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.Core.Caching { - [Serializable] - internal class MemorySet : HashSet, ISet - { - private readonly ICacheManager _cache; - - public MemorySet(ICacheManager cache) - { - _cache = cache; - } - - public void AddRange(IEnumerable items) - { - base.UnionWith(items); - } - - public bool Move(string destinationKey, string item) - { - if (this.Contains(item)) - { - var target = _cache.GetHashSet(destinationKey); - return target.Add(item); - } - - return false; - } - - public long ExceptWith(params string[] keys) - { - return Combine(x => this.Except(x), keys); - } - - public long IntersectWith(params string[] keys) - { - return Combine(x => this.Intersect(x), keys); - } - - public long UnionWith(params string[] keys) - { - return Combine(x => this.Union(x), keys); - } - - private long Combine(Func, IEnumerable> func, params string[] keys) - { - if (keys.Length == 0) - return 0; - - var other = keys.SelectMany(x => _cache.GetHashSet(x)).Distinct(); - var result = func(other); - - this.Clear(); - this.AddRange(other); - - return this.Count; - } - } + [Serializable] + internal class MemorySet : HashSet, ISet + { + private readonly ICacheManager _cache; + + public MemorySet(ICacheManager cache) + : base(StringComparer.OrdinalIgnoreCase) + { + _cache = cache; + } + + public void AddRange(IEnumerable items) + { + base.UnionWith(items); + } + + public bool Move(string destinationKey, string item) + { + if (this.Contains(item)) + { + var target = _cache.GetHashSet(destinationKey); + return target.Add(item); + } + + return false; + } + + public long ExceptWith(params string[] keys) + { + return Combine(x => this.Except(x), keys); + } + + public long IntersectWith(params string[] keys) + { + return Combine(x => this.Intersect(x), keys); + } + + public long UnionWith(params string[] keys) + { + return Combine(x => this.Union(x), keys); + } + + private long Combine(Func, IEnumerable> func, params string[] keys) + { + if (keys.Length == 0) + return 0; + + var other = keys.SelectMany(x => _cache.GetHashSet(x)).Distinct(); + var result = func(other); + + this.Clear(); + this.AddRange(other); + + return this.Count; + } + } } diff --git a/src/Libraries/SmartStore.Core/Caching/NullCache.cs b/src/Libraries/SmartStore.Core/Caching/NullCache.cs index e75abfd91d..8910d0b004 100644 --- a/src/Libraries/SmartStore.Core/Caching/NullCache.cs +++ b/src/Libraries/SmartStore.Core/Caching/NullCache.cs @@ -1,60 +1,49 @@ using System; -using SmartStore.Utilities; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; namespace SmartStore.Core.Caching { - /// - /// Represents a null cache - /// public partial class NullCache : ICacheManager { - private static readonly ICacheManager s_instance = new NullCache(); + private static readonly ICacheManager s_instance = new NullCache(); - public static ICacheManager Instance - { - get { return s_instance; } - } + public static ICacheManager Instance => s_instance; - public bool IsDistributedCache - { - get { return false; } - } + public bool IsDistributedCache => false; - public T Get(string key, bool independent = false) - { - return default(T); - } + public T Get(string key, bool independent = false) + { + return default(T); + } - public T Get(string key, Func acquirer, TimeSpan? duration = null, bool independent = false) - { - if (acquirer == null) - { - return default(T); - } - return acquirer(); - } + public T Get(string key, Func acquirer, TimeSpan? duration = null, bool independent = false, bool allowRecursion = false) + { + if (acquirer == null) + { + return default(T); + } + return acquirer(); + } - public Task GetAsync(string key, Func> acquirer, TimeSpan? duration = null, bool independent = false) - { - if (acquirer == null) - { - return Task.FromResult(default(T)); - } - return acquirer(); - } + public Task GetAsync(string key, Func> acquirer, TimeSpan? duration = null, bool independent = false, bool allowRecursion = false) + { + if (acquirer == null) + { + return Task.FromResult(default(T)); + } + return acquirer(); + } - public ISet GetHashSet(string key, Func> acquirer = null) - { - return new MemorySet(this); - } + public ISet GetHashSet(string key, Func> acquirer = null) + { + return new MemorySet(this); + } - public void Put(string key, object value, TimeSpan? duration = null, IEnumerable dependencies = null) - { - } + public void Put(string key, object value, TimeSpan? duration = null, IEnumerable dependencies = null) + { + } public bool Contains(string key) { @@ -65,18 +54,18 @@ public void Remove(string key) { } - public IEnumerable Keys(string pattern) - { - return new string[0]; - } + public IEnumerable Keys(string pattern) + { + return new string[0]; + } - public int RemoveByPattern(string pattern) + public int RemoveByPattern(string pattern) { - return 0; + return 0; } public void Clear() { } - } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Caching/OutputCache/DisplayControl.cs b/src/Libraries/SmartStore.Core/Caching/OutputCache/DisplayControl.cs index 9629fee3c5..f43b485741 100644 --- a/src/Libraries/SmartStore.Core/Caching/OutputCache/DisplayControl.cs +++ b/src/Libraries/SmartStore.Core/Caching/OutputCache/DisplayControl.cs @@ -8,244 +8,249 @@ using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Discounts; using SmartStore.Core.Domain.Localization; +using SmartStore.Core.Domain.Media; using SmartStore.Core.Domain.News; using SmartStore.Core.Domain.Topics; using SmartStore.Utilities; namespace SmartStore.Core.Caching { - public delegate IEnumerable DisplayControlHandler(BaseEntity entity, IComponentContext ctx); - - public partial class DisplayControl : IDisplayControl - { - private static readonly ConcurrentDictionary _handlers = new ConcurrentDictionary - { - [typeof(BlogComment)] = (x, c) => new[] { "b" + ((BlogComment)x).BlogPostId }, - [typeof(BlogPost)] = (x, c) => new[] { "b" + x.Id }, - [typeof(Category)] = (x, c) => new[] { "c" + x.Id }, - [typeof(Manufacturer)] = (x, c) => new[] { "m" + x.Id }, - [typeof(ProductBundleItem)] = (x, c) => new[] { "p" + ((ProductBundleItem)x).ProductId }, - [typeof(ProductPicture)] = (x, c) => new[] { "p" + ((ProductPicture)x).ProductId }, - [typeof(ProductSpecificationAttribute)] = (x, c) => new[] { "p" + ((ProductSpecificationAttribute)x).ProductId }, - [typeof(ProductVariantAttributeCombination)]= (x, c) => new[] { "p" + ((ProductVariantAttributeCombination)x).ProductId }, - [typeof(TierPrice)] = (x, c) => new[] { "p" + ((TierPrice)x).ProductId }, - [typeof(CrossSellProduct)] = (x, c) => new[] { "p" + ((CrossSellProduct)x).ProductId1, "p" + ((CrossSellProduct)x).ProductId2 }, - [typeof(RelatedProduct)] = (x, c) => new[] { "p" + ((RelatedProduct)x).ProductId1, "p" + ((RelatedProduct)x).ProductId2 }, - [typeof(ProductCategory)] = (x, c) => new[] { "p" + ((ProductCategory)x).CategoryId, "p" + ((ProductCategory)x).ProductId }, - [typeof(ProductManufacturer)] = (x, c) => new[] { "p" + ((ProductManufacturer)x).ManufacturerId, "p" + ((ProductManufacturer)x).ProductId }, - [typeof(NewsItem)] = (x, c) => new[] { "n" + x.Id }, - [typeof(NewsComment)] = (x, c) => new[] { "n" + ((NewsComment)x).NewsItemId }, - [typeof(Topic)] = (x, c) => new[] { "t" + x.Id }, - [typeof(SpecificationAttributeOption)] = (x, c) => ((SpecificationAttributeOption)x).ProductSpecificationAttributes.Select(y => "p" + y.ProductId), - [typeof(ProductTag)] = (x, c) => ((ProductTag)x).Products.Select(y => "p" + y.Id), - [typeof(Product)] = HandleProduct, - [typeof(SpecificationAttribute)] = HandleSpecificationAttribute, - [typeof(ProductVariantAttributeValue)] = HandleProductVariantAttributeValue, - [typeof(Discount)] = HandleDiscount, - [typeof(LocalizedProperty)] = HandleLocalizedProperty - }; - - private readonly HashSet _entities = new HashSet(); - private readonly IComponentContext _componentContext; - - private bool _isIdle; - private bool? _isUncacheableRequest; - - public DisplayControl(IComponentContext componentContext) - { - _componentContext = componentContext; - } - - #region Static - - public static bool ContainsHandlerFor(Type type) - { - Guard.NotNull(type, nameof(type)); - - return _handlers.ContainsKey(type); - } - - public static void RegisterHandlerFor(Type type, DisplayControlHandler handler) - { - Guard.NotNull(type, nameof(type)); - Guard.NotNull(handler, nameof(handler)); - - _handlers.TryAdd(type, handler); - } - - #endregion - - #region Handlers - - private static IEnumerable HandleProduct(BaseEntity entity, IComponentContext ctx) - { - var product = ((Product)entity); - yield return "p" + entity.Id; - if (product.ProductType == ProductType.GroupedProduct && product.ParentGroupedProductId > 0) - { - yield return "p" + product.ParentGroupedProductId; - } - } - - private static IEnumerable HandleSpecificationAttribute(BaseEntity entity, IComponentContext ctx) - { - // Determine all affected products (which are assigned to this attribute). - var specAttrId = ((SpecificationAttribute)entity).Id; - var affectedProductIds = ctx.Resolve().Set().AsNoTracking() - .Where(x => x.SpecificationAttributeOption.SpecificationAttribute.Id == specAttrId) - .Select(x => x.ProductId) - .Distinct() - .ToList(); - - foreach (var id in affectedProductIds) - { - yield return "p" + id; - } - } - - private static IEnumerable HandleProductVariantAttributeValue(BaseEntity entity, IComponentContext ctx) - { - var pva = ((ProductVariantAttributeValue)entity).ProductVariantAttribute; - if (pva != null) - { - yield return "p" + pva.ProductId; - } - } - - private static IEnumerable HandleDiscount(BaseEntity entity, IComponentContext ctx) - { - var discount = (Discount)entity; - if (discount.DiscountType == DiscountType.AssignedToCategories) - { - foreach (var category in discount.AppliedToCategories) - { - yield return "c" + category.Id; - } - } - else if (discount.DiscountType == DiscountType.AssignedToSkus) - { - foreach (var product in discount.AppliedToProducts) - { - yield return "p" + product.Id; - } - } - } - - private static IEnumerable HandleLocalizedProperty(BaseEntity entity, IComponentContext ctx) - { - var lp = (LocalizedProperty)entity; - string prefix = null; - BaseEntity targetEntity = null; - - var dbContext = ctx.Resolve(); - - switch (lp.LocaleKeyGroup) - { - case nameof(Product): - prefix = "p"; - break; - case nameof(Category): - prefix = "c"; - break; - case nameof(Manufacturer): - prefix = "m"; - break; - case nameof(Topic): - prefix = "t"; - break; - case nameof(SpecificationAttribute): - targetEntity = dbContext.Set().Find(lp.EntityId); - break; - case nameof(SpecificationAttributeOption): - targetEntity = dbContext.Set().Find(lp.EntityId); - break; - case nameof(ProductVariantAttributeValue): - targetEntity = dbContext.Set().Find(lp.EntityId); - break; - } - - if (prefix.HasValue()) - { - yield return prefix + lp.EntityId; - } - else if (targetEntity != null) - { - var tags = ctx.Resolve().GetCacheControlTagsFor(targetEntity); - foreach (var tag in tags) - { - yield return tag; - } - } - } - - #endregion - - public IDisposable BeginIdleScope() - { - _isIdle = true; - return new ActionDisposable(() => _isIdle = false); - } - - public virtual void Announce(BaseEntity entity) - { - if (!_isIdle && entity != null) - { - _entities.Add(entity); - } - } - - public bool IsDisplayed(BaseEntity entity) - { - if (entity == null) - return false; - - return _entities.Contains(entity); - } - - public void MarkRequestAsUncacheable() - { - // First wins: subsequent calls should not be able to cancel this - if (!_isIdle) - _isUncacheableRequest = true; - } - - public bool IsUncacheableRequest - { - get - { - return _isUncacheableRequest.GetValueOrDefault() == true; - } - } - - public virtual IEnumerable GetCacheControlTagsFor(BaseEntity entity) - { - var empty = Enumerable.Empty(); - - if (entity == null || entity.IsTransientRecord()) - { - return empty; - } - - var type = entity.GetUnproxiedType(); - - if (!_handlers.TryGetValue(type, out var handler)) - { - return empty; - } - - return handler.Invoke(entity, _componentContext); - } - - public IEnumerable GetAllCacheControlTags() - { - var tags = _entities - .Where(x => x.Id > 0) - .SelectMany(x => GetCacheControlTagsFor(x)) - .Where(x => x != null) - .Distinct() - .ToArray(); - - return tags; - } - } + public delegate IEnumerable DisplayControlHandler(BaseEntity entity, IComponentContext ctx); + + public partial class DisplayControl : IDisplayControl + { + private static readonly ConcurrentDictionary _handlers = new ConcurrentDictionary + { + [typeof(BlogComment)] = (x, c) => new[] { "b" + ((BlogComment)x).BlogPostId }, + [typeof(BlogPost)] = (x, c) => new[] { "b" + x.Id }, + [typeof(Category)] = (x, c) => new[] { "c" + x.Id }, + [typeof(Manufacturer)] = (x, c) => new[] { "m" + x.Id }, + [typeof(ProductBundleItem)] = (x, c) => new[] { "p" + ((ProductBundleItem)x).ProductId }, + [typeof(ProductMediaFile)] = (x, c) => new[] { "p" + ((ProductMediaFile)x).ProductId }, + [typeof(ProductSpecificationAttribute)] = (x, c) => new[] { "p" + ((ProductSpecificationAttribute)x).ProductId }, + [typeof(ProductVariantAttributeCombination)] = (x, c) => new[] { "p" + ((ProductVariantAttributeCombination)x).ProductId }, + [typeof(TierPrice)] = (x, c) => new[] { "p" + ((TierPrice)x).ProductId }, + [typeof(CrossSellProduct)] = (x, c) => new[] { "p" + ((CrossSellProduct)x).ProductId1, "p" + ((CrossSellProduct)x).ProductId2 }, + [typeof(RelatedProduct)] = (x, c) => new[] { "p" + ((RelatedProduct)x).ProductId1, "p" + ((RelatedProduct)x).ProductId2 }, + [typeof(ProductCategory)] = (x, c) => new[] { "p" + ((ProductCategory)x).CategoryId, "p" + ((ProductCategory)x).ProductId }, + [typeof(ProductManufacturer)] = (x, c) => new[] { "p" + ((ProductManufacturer)x).ManufacturerId, "p" + ((ProductManufacturer)x).ProductId }, + [typeof(NewsItem)] = (x, c) => new[] { "n" + x.Id }, + [typeof(NewsComment)] = (x, c) => new[] { "n" + ((NewsComment)x).NewsItemId }, + [typeof(Topic)] = (x, c) => new[] { "t" + x.Id }, + [typeof(MediaFile)] = (x, c) => new[] { "mf" + x.Id }, + [typeof(SpecificationAttributeOption)] = (x, c) => ((SpecificationAttributeOption)x).ProductSpecificationAttributes.Select(y => "p" + y.ProductId), + [typeof(ProductTag)] = (x, c) => ((ProductTag)x).Products.Select(y => "p" + y.Id), + [typeof(Product)] = HandleProduct, + [typeof(SpecificationAttribute)] = HandleSpecificationAttribute, + [typeof(ProductVariantAttributeValue)] = HandleProductVariantAttributeValue, + [typeof(Discount)] = HandleDiscount, + [typeof(LocalizedProperty)] = HandleLocalizedProperty + }; + + private readonly HashSet _entities = new HashSet(); + private readonly IComponentContext _componentContext; + + private bool _isIdle; + private bool? _isUncacheableRequest; + + public DisplayControl(IComponentContext componentContext) + { + _componentContext = componentContext; + } + + #region Static + + public static bool ContainsHandlerFor(Type type) + { + Guard.NotNull(type, nameof(type)); + + return _handlers.ContainsKey(type); + } + + public static void RegisterHandlerFor(Type type, DisplayControlHandler handler) + { + Guard.NotNull(type, nameof(type)); + Guard.NotNull(handler, nameof(handler)); + + _handlers.TryAdd(type, handler); + } + + #endregion + + #region Handlers + + private static IEnumerable HandleProduct(BaseEntity entity, IComponentContext ctx) + { + var product = ((Product)entity); + yield return "p" + entity.Id; + if (product.ProductType == ProductType.GroupedProduct && product.ParentGroupedProductId > 0) + { + yield return "p" + product.ParentGroupedProductId; + } + } + + private static IEnumerable HandleSpecificationAttribute(BaseEntity entity, IComponentContext ctx) + { + // Determine all affected products (which are assigned to this attribute). + var specAttrId = ((SpecificationAttribute)entity).Id; + var affectedProductIds = ctx.Resolve().Set().AsNoTracking() + .Where(x => x.SpecificationAttributeOption.SpecificationAttribute.Id == specAttrId) + .Select(x => x.ProductId) + .Distinct() + .ToList(); + + foreach (var id in affectedProductIds) + { + yield return "p" + id; + } + } + + private static IEnumerable HandleProductVariantAttributeValue(BaseEntity entity, IComponentContext ctx) + { + var pva = ((ProductVariantAttributeValue)entity).ProductVariantAttribute; + if (pva != null) + { + yield return "p" + pva.ProductId; + } + } + + private static IEnumerable HandleDiscount(BaseEntity entity, IComponentContext ctx) + { + var discount = (Discount)entity; + if (discount.DiscountType == DiscountType.AssignedToCategories) + { + foreach (var category in discount.AppliedToCategories) + { + yield return "c" + category.Id; + } + } + else if (discount.DiscountType == DiscountType.AssignedToSkus) + { + foreach (var product in discount.AppliedToProducts) + { + yield return "p" + product.Id; + } + } + } + + private static IEnumerable HandleLocalizedProperty(BaseEntity entity, IComponentContext ctx) + { + var lp = (LocalizedProperty)entity; + string prefix = null; + BaseEntity targetEntity = null; + + var dbContext = ctx.Resolve(); + + switch (lp.LocaleKeyGroup) + { + case nameof(BlogPost): + prefix = "b"; + break; + case nameof(Product): + prefix = "p"; + break; + case nameof(Category): + prefix = "c"; + break; + case nameof(Manufacturer): + prefix = "m"; + break; + case nameof(NewsItem): + prefix = "n"; + break; + case nameof(Topic): + prefix = "t"; + break; + case nameof(MediaFile): + prefix = "mf"; + break; + case nameof(SpecificationAttribute): + targetEntity = dbContext.Set().Find(lp.EntityId); + break; + case nameof(SpecificationAttributeOption): + targetEntity = dbContext.Set().Find(lp.EntityId); + break; + case nameof(ProductVariantAttributeValue): + targetEntity = dbContext.Set().Find(lp.EntityId); + break; + } + + if (prefix.HasValue()) + { + yield return prefix + lp.EntityId; + } + else if (targetEntity != null) + { + var tags = ctx.Resolve().GetCacheControlTagsFor(targetEntity); + foreach (var tag in tags) + { + yield return tag; + } + } + } + + #endregion + + public IDisposable BeginIdleScope() + { + _isIdle = true; + return new ActionDisposable(() => _isIdle = false); + } + + public virtual void Announce(BaseEntity entity) + { + if (!_isIdle && entity != null) + { + _entities.Add(entity); + } + } + + public bool IsDisplayed(BaseEntity entity) + { + if (entity == null) + return false; + + return _entities.Contains(entity); + } + + public void MarkRequestAsUncacheable() + { + // First wins: subsequent calls should not be able to cancel this + if (!_isIdle) + _isUncacheableRequest = true; + } + + public bool IsUncacheableRequest => _isUncacheableRequest.GetValueOrDefault() == true; + + public virtual IEnumerable GetCacheControlTagsFor(BaseEntity entity) + { + var empty = Enumerable.Empty(); + + if (entity == null || entity.IsTransientRecord()) + { + return empty; + } + + var type = entity.GetUnproxiedType(); + + if (!_handlers.TryGetValue(type, out var handler)) + { + return empty; + } + + return handler.Invoke(entity, _componentContext); + } + + public IEnumerable GetAllCacheControlTags() + { + var tags = _entities + .Where(x => x.Id > 0) + .SelectMany(x => GetCacheControlTagsFor(x)) + .Where(x => x != null) + .Distinct() + .ToArray(); + + return tags; + } + } } diff --git a/src/Libraries/SmartStore.Core/Caching/OutputCache/ICacheableRouteRegistrar.cs b/src/Libraries/SmartStore.Core/Caching/OutputCache/ICacheableRouteRegistrar.cs index b30feeb1e4..4898095adb 100644 --- a/src/Libraries/SmartStore.Core/Caching/OutputCache/ICacheableRouteRegistrar.cs +++ b/src/Libraries/SmartStore.Core/Caching/OutputCache/ICacheableRouteRegistrar.cs @@ -1,21 +1,19 @@ -using System; - -namespace SmartStore.Core.Caching +namespace SmartStore.Core.Caching { - public interface ICacheableRouteRegistrar - { - void RegisterCacheableRoute(params string[] routes); - void RemoveCacheableRoute(params string[] routes); - } + public interface ICacheableRouteRegistrar + { + void RegisterCacheableRoute(params string[] routes); + void RemoveCacheableRoute(params string[] routes); + } - public class NullCacheableRouteRegistrar : ICacheableRouteRegistrar - { - public void RegisterCacheableRoute(params string[] routes) - { - } + public class NullCacheableRouteRegistrar : ICacheableRouteRegistrar + { + public void RegisterCacheableRoute(params string[] routes) + { + } - public void RemoveCacheableRoute(params string[] routes) - { - } - } + public void RemoveCacheableRoute(params string[] routes) + { + } + } } diff --git a/src/Libraries/SmartStore.Core/Caching/OutputCache/IDisplayControl.cs b/src/Libraries/SmartStore.Core/Caching/OutputCache/IDisplayControl.cs index 9646cf649e..f9549bc92c 100644 --- a/src/Libraries/SmartStore.Core/Caching/OutputCache/IDisplayControl.cs +++ b/src/Libraries/SmartStore.Core/Caching/OutputCache/IDisplayControl.cs @@ -1,37 +1,34 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.Core.Caching { - public interface IDisplayControl - { - void Announce(BaseEntity entity); + public interface IDisplayControl + { + void Announce(BaseEntity entity); - bool IsDisplayed(BaseEntity entity); + bool IsDisplayed(BaseEntity entity); - void MarkRequestAsUncacheable(); + void MarkRequestAsUncacheable(); - bool IsUncacheableRequest { get; } + bool IsUncacheableRequest { get; } - IEnumerable GetCacheControlTagsFor(BaseEntity entity); + IEnumerable GetCacheControlTagsFor(BaseEntity entity); - IEnumerable GetAllCacheControlTags(); + IEnumerable GetAllCacheControlTags(); - IDisposable BeginIdleScope(); - } + IDisposable BeginIdleScope(); + } - public static class IDisplayControlExtensions - { - public static void AnnounceRange(this IDisplayControl displayedEntities, IEnumerable entities) - { - if (entities != null) - { - entities.Each(x => displayedEntities.Announce(x)); - } - } - } + public static class IDisplayControlExtensions + { + public static void AnnounceRange(this IDisplayControl displayedEntities, IEnumerable entities) + { + if (entities != null) + { + entities.Each(x => displayedEntities.Announce(x)); + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Caching/OutputCache/IOutputCacheInvalidationObserver.cs b/src/Libraries/SmartStore.Core/Caching/OutputCache/IOutputCacheInvalidationObserver.cs index 234b67ced2..069a0bd62c 100644 --- a/src/Libraries/SmartStore.Core/Caching/OutputCache/IOutputCacheInvalidationObserver.cs +++ b/src/Libraries/SmartStore.Core/Caching/OutputCache/IOutputCacheInvalidationObserver.cs @@ -9,154 +9,151 @@ namespace SmartStore.Core.Caching { - public class ObserveEntityContext - { - public IOutputCacheProvider OutputCacheProvider { get; set; } - public IDisplayControl DisplayControl { get; set; } - public BaseEntity Entity { get; set; } - public IHookedEntity EntityEntry { get; set; } - public bool Handled { get; set; } - public ContainerManager ServiceContainer { get; set; } - } - - /// - /// Allows registration of output cache invalidation handlers - /// - public interface IOutputCacheInvalidationObserver - { - /// - /// Registers an entity observer. The passed observer is responsible for invalidating the output cache - /// by calling one of the invalidation methods in the instance. - /// The observer must then set the property to true - /// to signal the framework that it should skip executing subsequent observers. - /// - /// The observer action - /// - /// The implementation of this interface is singleton scoped. - /// Don't use objects with shorter lifetime in your handler as this will lead to memory leaks. - /// If your handler needs to call service methods, resolve required services - /// with . - /// - void ObserveEntity(Action observer); - - IEnumerable> GetEntityObservers(); - - /// - /// Registers a setting key to be observed by the framework. If the value for the passed - /// setting key changes, the framework calls the handler. - /// The key can either be fully qualified - e.g. "CatalogSettings.ShowProductSku" -, - /// or prefixed - e.g. "CatalogSettings.*". The latter calls the invalidator when ANY CatalogSetting changes. - /// - /// - /// The invalidation action handler. If null is passed, the framework - /// uses the default invalidator, which is . - /// - void ObserveSetting(string settingKey, Action invalidationAction); - - Action GetInvalidationActionForSetting(string settingKey); - } - - public class NullOutputCacheInvalidationObserver : IOutputCacheInvalidationObserver - { - private static readonly IOutputCacheInvalidationObserver _instance = new NullOutputCacheInvalidationObserver(); - - public static IOutputCacheInvalidationObserver Instance - { - get { return _instance; } - } - - public void ObserveEntity(Action observer) - { - } - - public IEnumerable> GetEntityObservers() - { - return Enumerable.Empty>(); - } - - public void ObserveSetting(string settingKey, Action invalidationAction) - { - } - - public Action GetInvalidationActionForSetting(string settingKey) - { - return null; - } - } - - public static class IOutputCacheInvalidationObserverExtensions - { - public static void ObserveSetting(this IOutputCacheInvalidationObserver observer, string settingKey) - { - observer.ObserveSetting(settingKey, null); - } - - /// - /// Registers a concrete setting class to be observed by the framework. If any setting property - /// of changes, the framework will purge the cache. - /// - /// The type of the concrete setting class to observe - /// - /// A property observer precedes a class observer. - /// - public static void ObserveSettings(this IOutputCacheInvalidationObserver observer) - where TSetting : ISettings - { - ObserveSettings(observer, null); - } - - /// - /// Registers a concrete setting class to be observed by the framework. If any setting property - /// of changes, the framework will call the handler. - /// - /// The type of the concrete setting class to observe - /// - /// The invalidation action handler. If null is passed, the framework - /// uses the default invalidator, which is . - /// - /// - /// A property observer precedes a class observer. - /// - public static void ObserveSettings( - this IOutputCacheInvalidationObserver observer, - Action invalidationAction) where TSetting : ISettings - { - var key = typeof(TSetting).Name + ".*"; - observer.ObserveSetting(key, invalidationAction); - } - - /// - /// Registers a setting property to be observed by the framework. If the value for the passed - /// property changes, the framework will purge the cache. - /// - /// The type of the concrete setting class which contains the property - /// The property lambda - public static void ObserveSettingProperty( - this IOutputCacheInvalidationObserver observer, - Expression> propertyAccessor) where TSetting : ISettings - { - ObserveSettingProperty(observer, propertyAccessor, null); - } - - /// - /// Registers a setting property to be observed by the framework. If the value for the passed - /// property changes, the framework will call the handler. - /// - /// The type of the concrete setting class which contains the property - /// The property lambda - /// - /// The invalidation action handler. If null is passed, the framework - /// uses the default invalidator, which is . - /// - public static void ObserveSettingProperty( - this IOutputCacheInvalidationObserver observer, - Expression> propertyAccessor, - Action invalidationAction) where TSetting : ISettings - { - Guard.NotNull(propertyAccessor, nameof(propertyAccessor)); - - var key = TypeHelper.NameOf(propertyAccessor, true); - observer.ObserveSetting(key, invalidationAction); - } - } + public class ObserveEntityContext + { + public IOutputCacheProvider OutputCacheProvider { get; set; } + public IDisplayControl DisplayControl { get; set; } + public BaseEntity Entity { get; set; } + public IHookedEntity EntityEntry { get; set; } + public bool Handled { get; set; } + public ContainerManager ServiceContainer { get; set; } + } + + /// + /// Allows registration of output cache invalidation handlers + /// + public interface IOutputCacheInvalidationObserver + { + /// + /// Registers an entity observer. The passed observer is responsible for invalidating the output cache + /// by calling one of the invalidation methods in the instance. + /// The observer must then set the property to true + /// to signal the framework that it should skip executing subsequent observers. + /// + /// The observer action + /// + /// The implementation of this interface is singleton scoped. + /// Don't use objects with shorter lifetime in your handler as this will lead to memory leaks. + /// If your handler needs to call service methods, resolve required services + /// with . + /// + void ObserveEntity(Action observer); + + IEnumerable> GetEntityObservers(); + + /// + /// Registers a setting key to be observed by the framework. If the value for the passed + /// setting key changes, the framework calls the handler. + /// The key can either be fully qualified - e.g. "CatalogSettings.ShowProductSku" -, + /// or prefixed - e.g. "CatalogSettings.*". The latter calls the invalidator when ANY CatalogSetting changes. + /// + /// + /// The invalidation action handler. If null is passed, the framework + /// uses the default invalidator, which is . + /// + void ObserveSetting(string settingKey, Action invalidationAction); + + Action GetInvalidationActionForSetting(string settingKey); + } + + public class NullOutputCacheInvalidationObserver : IOutputCacheInvalidationObserver + { + private static readonly IOutputCacheInvalidationObserver _instance = new NullOutputCacheInvalidationObserver(); + + public static IOutputCacheInvalidationObserver Instance => _instance; + + public void ObserveEntity(Action observer) + { + } + + public IEnumerable> GetEntityObservers() + { + return Enumerable.Empty>(); + } + + public void ObserveSetting(string settingKey, Action invalidationAction) + { + } + + public Action GetInvalidationActionForSetting(string settingKey) + { + return null; + } + } + + public static class IOutputCacheInvalidationObserverExtensions + { + public static void ObserveSetting(this IOutputCacheInvalidationObserver observer, string settingKey) + { + observer.ObserveSetting(settingKey, null); + } + + /// + /// Registers a concrete setting class to be observed by the framework. If any setting property + /// of changes, the framework will purge the cache. + /// + /// The type of the concrete setting class to observe + /// + /// A property observer precedes a class observer. + /// + public static void ObserveSettings(this IOutputCacheInvalidationObserver observer) + where TSetting : ISettings + { + ObserveSettings(observer, null); + } + + /// + /// Registers a concrete setting class to be observed by the framework. If any setting property + /// of changes, the framework will call the handler. + /// + /// The type of the concrete setting class to observe + /// + /// The invalidation action handler. If null is passed, the framework + /// uses the default invalidator, which is . + /// + /// + /// A property observer precedes a class observer. + /// + public static void ObserveSettings( + this IOutputCacheInvalidationObserver observer, + Action invalidationAction) where TSetting : ISettings + { + var key = typeof(TSetting).Name + ".*"; + observer.ObserveSetting(key, invalidationAction); + } + + /// + /// Registers a setting property to be observed by the framework. If the value for the passed + /// property changes, the framework will purge the cache. + /// + /// The type of the concrete setting class which contains the property + /// The property lambda + public static void ObserveSettingProperty( + this IOutputCacheInvalidationObserver observer, + Expression> propertyAccessor) where TSetting : ISettings + { + ObserveSettingProperty(observer, propertyAccessor, null); + } + + /// + /// Registers a setting property to be observed by the framework. If the value for the passed + /// property changes, the framework will call the handler. + /// + /// The type of the concrete setting class which contains the property + /// The property lambda + /// + /// The invalidation action handler. If null is passed, the framework + /// uses the default invalidator, which is . + /// + public static void ObserveSettingProperty( + this IOutputCacheInvalidationObserver observer, + Expression> propertyAccessor, + Action invalidationAction) where TSetting : ISettings + { + Guard.NotNull(propertyAccessor, nameof(propertyAccessor)); + + var key = TypeHelper.NameOf(propertyAccessor, true); + observer.ObserveSetting(key, invalidationAction); + } + } } diff --git a/src/Libraries/SmartStore.Core/Caching/OutputCache/IOutputCacheProvider.cs b/src/Libraries/SmartStore.Core/Caching/OutputCache/IOutputCacheProvider.cs index 3e2aead128..da9c985476 100644 --- a/src/Libraries/SmartStore.Core/Caching/OutputCache/IOutputCacheProvider.cs +++ b/src/Libraries/SmartStore.Core/Caching/OutputCache/IOutputCacheProvider.cs @@ -1,23 +1,21 @@ -using System; -using System.Collections.Generic; -using SmartStore.Core.Plugins; +using SmartStore.Core.Plugins; namespace SmartStore.Core.Caching { - public interface IOutputCacheProvider : IProvider - { - OutputCacheItem Get(string key); - void Set(string key, OutputCacheItem item); - bool Exists(string key); + public interface IOutputCacheProvider : IProvider + { + OutputCacheItem Get(string key); + void Set(string key, OutputCacheItem item); + bool Exists(string key); - void Remove(params string[] keys); - void RemoveAll(); + void Remove(params string[] keys); + void RemoveAll(); - IPagedList All(int pageIndex, int pageSize, bool withContent = false); - int Count(); + IPagedList All(int pageIndex, int pageSize, bool withContent = false); + int Count(); - int InvalidateByRoute(params string[] routes); - int InvalidateByPrefix(string keyPrefix); - int InvalidateByTag(params string[] tags); - } + int InvalidateByRoute(params string[] routes); + int InvalidateByPrefix(string keyPrefix); + int InvalidateByTag(params string[] tags); + } } diff --git a/src/Libraries/SmartStore.Core/Caching/OutputCache/NullOutputCacheProvider.cs b/src/Libraries/SmartStore.Core/Caching/OutputCache/NullOutputCacheProvider.cs index f9d2ba828a..0e3d290ce7 100644 --- a/src/Libraries/SmartStore.Core/Caching/OutputCache/NullOutputCacheProvider.cs +++ b/src/Libraries/SmartStore.Core/Caching/OutputCache/NullOutputCacheProvider.cs @@ -1,65 +1,61 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using SmartStore.Core.Plugins; namespace SmartStore.Core.Caching { - [SystemName("OutputCacheProvider.Idle")] - [FriendlyName("Idle")] - public class NullOutputCacheProvider : IOutputCacheProvider - { - private static readonly IOutputCacheProvider s_instance = new NullOutputCacheProvider(); - - public static IOutputCacheProvider Instance - { - get { return s_instance; } - } - - public IPagedList All(int pageIndex, int pageSize, bool withContent = false) - { - return new PagedList(new List(), pageIndex, pageSize); - } - - public int Count() - { - return 0; - } - - public bool Exists(string key) - { - return false; - } - - public OutputCacheItem Get(string key) - { - return null; - } - - public int InvalidateByRoute(params string[] routes) - { - return 0; - } - - public int InvalidateByPrefix(string keyPrefix) - { - return 0; - } - - public int InvalidateByTag(params string[] tags) - { - return 0; - } - - public void Remove(params string[] keys) - { - } - - public void RemoveAll() - { - } - - public void Set(string key, OutputCacheItem item) - { - } - } + [SystemName("OutputCacheProvider.Idle")] + [FriendlyName("Idle")] + public class NullOutputCacheProvider : IOutputCacheProvider + { + private static readonly IOutputCacheProvider s_instance = new NullOutputCacheProvider(); + + public static IOutputCacheProvider Instance => s_instance; + + public IPagedList All(int pageIndex, int pageSize, bool withContent = false) + { + return new PagedList(new List(), pageIndex, pageSize); + } + + public int Count() + { + return 0; + } + + public bool Exists(string key) + { + return false; + } + + public OutputCacheItem Get(string key) + { + return null; + } + + public int InvalidateByRoute(params string[] routes) + { + return 0; + } + + public int InvalidateByPrefix(string keyPrefix) + { + return 0; + } + + public int InvalidateByTag(params string[] tags) + { + return 0; + } + + public void Remove(params string[] keys) + { + } + + public void RemoveAll() + { + } + + public void Set(string key, OutputCacheItem item) + { + } + } } diff --git a/src/Libraries/SmartStore.Core/Caching/OutputCache/OutputCacheItem.cs b/src/Libraries/SmartStore.Core/Caching/OutputCache/OutputCacheItem.cs index 6ad1ec3c65..ebb024c737 100644 --- a/src/Libraries/SmartStore.Core/Caching/OutputCache/OutputCacheItem.cs +++ b/src/Libraries/SmartStore.Core/Caching/OutputCache/OutputCacheItem.cs @@ -3,56 +3,53 @@ namespace SmartStore.Core.Caching { - [Serializable] - [DebuggerDisplay("{CacheKey}, Url: {Url}, Query: {QueryString}, Duration: {Duration}, Tags: {Tags}")] - public class OutputCacheItem : ICloneable - { - // used for serialization compatibility - public static readonly string Version = "1"; - - public string CacheKey { get; set; } - public string RouteKey { get; set; } - public DateTime CachedOnUtc { get; set; } - public string Url { get; set; } - public string QueryString { get; set; } - public int Duration { get; set; } - public string[] Tags { get; set; } - - public string Theme { get; set; } - public int StoreId { get; set; } - public int LanguageId { get; set; } - public int CurrencyId { get; set; } - public string CustomerRoles { get; set; } - - public string ContentType { get; set; } - public string Content { get; set; } - - public DateTime ExpiresOnUtc - { - get { return CachedOnUtc.AddSeconds(Duration); } - } - - public bool IsValid(DateTime utcNow) - { - return utcNow < ExpiresOnUtc; - } - - public string JoinTags() - { - if (Tags == null || Tags.Length == 0) - return ""; - - return String.Join(";", Tags); - } - - public OutputCacheItem Clone() - { - return (OutputCacheItem)this.MemberwiseClone(); - } - - object ICloneable.Clone() - { - return this.MemberwiseClone(); - } - } + [Serializable] + [DebuggerDisplay("{CacheKey}, Url: {Url}, Query: {QueryString}, Duration: {Duration}, Tags: {Tags}")] + public class OutputCacheItem : ICloneable + { + // used for serialization compatibility + public static readonly string Version = "1"; + + public string CacheKey { get; set; } + public string RouteKey { get; set; } + public DateTime CachedOnUtc { get; set; } + public string Url { get; set; } + public string QueryString { get; set; } + public int Duration { get; set; } + public string[] Tags { get; set; } + + public string Theme { get; set; } + public int StoreId { get; set; } + public int LanguageId { get; set; } + public int CurrencyId { get; set; } + public string CustomerRoles { get; set; } + + public string ContentType { get; set; } + public string Content { get; set; } + + public DateTime ExpiresOnUtc => CachedOnUtc.AddSeconds(Duration); + + public bool IsValid(DateTime utcNow) + { + return utcNow < ExpiresOnUtc; + } + + public string JoinTags() + { + if (Tags == null || Tags.Length == 0) + return ""; + + return String.Join(";", Tags); + } + + public OutputCacheItem Clone() + { + return (OutputCacheItem)this.MemberwiseClone(); + } + + object ICloneable.Clone() + { + return this.MemberwiseClone(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Caching/RequestCache.cs b/src/Libraries/SmartStore.Core/Caching/RequestCache.cs index 5fb74d2400..601afc2fef 100644 --- a/src/Libraries/SmartStore.Core/Caching/RequestCache.cs +++ b/src/Libraries/SmartStore.Core/Caching/RequestCache.cs @@ -8,127 +8,127 @@ namespace SmartStore.Core.Caching { - public class RequestCache : DisposableObject, IRequestCache - { - const string RegionName = "SmartStoreNET:"; - - private IDictionary _emptyDictionary = new Dictionary(); - - private readonly HttpContextBase _context; - - public RequestCache(HttpContextBase context) - { - _context = context; - } - - public T Get(string key) - { - return Get(key, null); - } - - public T Get(string key, Func acquirer) - { - var items = GetItems(); - - key = BuildKey(key); - - if (items.Contains(key)) - { - return (T)items[key]; - } - - if (acquirer != null) - { - var value = acquirer(); - items.Add(key, value); - return value; - } - - return default(T); - } - - public void Put(string key, object value) - { - var items = GetItems(); - - key = BuildKey(key); - - if (items.Contains(key)) - items[key] = value; - else - items.Add(key, value); - } - - public void Clear() - { - RemoveByPattern("*"); - } - - public bool Contains(string key) - { - return GetItems().Contains(BuildKey(key)); - } - - public void Remove(string key) - { - GetItems().Remove(BuildKey(key)); - } - - public void RemoveByPattern(string pattern) - { - var items = GetItems(); - - var keysToRemove = Keys(pattern).ToArray(); - - foreach (string key in keysToRemove) - { - items.Remove(BuildKey(key)); - } - } - - protected IDictionary GetItems() - { - return _context.Items ?? _emptyDictionary; - } - - public IEnumerable Keys(string pattern) - { - var items = GetItems(); - - if (items.Count == 0) - yield break; - - var prefixLen = RegionName.Length; - - pattern = pattern.NullEmpty() ?? "*"; - var wildcard = new Wildcard(pattern, RegexOptions.IgnoreCase); - - var enumerator = items.GetEnumerator(); - while (enumerator.MoveNext()) - { - if (enumerator.Key is string key) - { - if (key.StartsWith(RegionName)) - { - key = key.Substring(prefixLen); - if (pattern == "*" || wildcard.IsMatch(key)) - { - yield return key; - } - } - } - } - } - - private string BuildKey(string key) - { - return RegionName + key.EmptyNull(); - } - - protected override void OnDispose(bool disposing) - { - if (disposing) - Clear(); - } - } + public class RequestCache : DisposableObject, IRequestCache + { + const string RegionName = "SmartStoreNET:"; + + private readonly IDictionary _emptyDictionary = new Dictionary(); + + private readonly HttpContextBase _context; + + public RequestCache(HttpContextBase context) + { + _context = context; + } + + public T Get(string key) + { + return Get(key, null); + } + + public T Get(string key, Func acquirer) + { + var items = GetItems(); + + key = BuildKey(key); + + if (items.Contains(key)) + { + return (T)items[key]; + } + + if (acquirer != null) + { + var value = acquirer(); + items.Add(key, value); + return value; + } + + return default(T); + } + + public void Put(string key, object value) + { + var items = GetItems(); + + key = BuildKey(key); + + if (items.Contains(key)) + items[key] = value; + else + items.Add(key, value); + } + + public void Clear() + { + RemoveByPattern("*"); + } + + public bool Contains(string key) + { + return GetItems().Contains(BuildKey(key)); + } + + public void Remove(string key) + { + GetItems().Remove(BuildKey(key)); + } + + public void RemoveByPattern(string pattern) + { + var items = GetItems(); + + var keysToRemove = Keys(pattern).ToArray(); + + foreach (string key in keysToRemove) + { + items.Remove(BuildKey(key)); + } + } + + protected IDictionary GetItems() + { + return _context.Items ?? _emptyDictionary; + } + + public IEnumerable Keys(string pattern) + { + var items = GetItems(); + + if (items.Count == 0) + yield break; + + var prefixLen = RegionName.Length; + + pattern = pattern.NullEmpty() ?? "*"; + var wildcard = new Wildcard(pattern, RegexOptions.IgnoreCase); + + var enumerator = items.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (enumerator.Key is string key) + { + if (key.StartsWith(RegionName)) + { + key = key.Substring(prefixLen); + if (pattern == "*" || wildcard.IsMatch(key)) + { + yield return key; + } + } + } + } + } + + private string BuildKey(string key) + { + return RegionName + key.EmptyNull(); + } + + protected override void OnDispose(bool disposing) + { + if (disposing) + Clear(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Collections/ConcurrentMultimap.cs b/src/Libraries/SmartStore.Core/Collections/ConcurrentMultimap.cs index 86d5b00249..ad8945ae12 100644 --- a/src/Libraries/SmartStore.Core/Collections/ConcurrentMultimap.cs +++ b/src/Libraries/SmartStore.Core/Collections/ConcurrentMultimap.cs @@ -1,312 +1,292 @@ using System; using System.Collections; -using System.Collections.Generic; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; -using SmartStore.Utilities; -using System.Threading; namespace SmartStore.Collections { - /// - /// A thread-safe data structure that contains multiple values for each key. - /// - /// The type of key. - /// The type of value. - [JsonConverter(typeof(ConcurrentMultiMapConverter))] - [Serializable] - public class ConcurrentMultimap : IEnumerable>> - { - private readonly ConcurrentDictionary> _dict; - private readonly Func, ICollection> _collectionCreator; - - public ConcurrentMultimap() - : this(null, null, null) - { - } - - public ConcurrentMultimap(IEqualityComparer comparer) - : this(null, comparer, null) - { - } - - public ConcurrentMultimap(IEnumerable>> items) - : this(items, null, null) - { - // for serialization - } - - public ConcurrentMultimap( - IEnumerable>> items, - IEqualityComparer comparer) - : this(items, comparer, null) - { - // for serialization - } - - public ConcurrentMultimap( - IEnumerable>> items, - IEqualityComparer comparer, - Func, ICollection> collectionCreator) - { - _collectionCreator = collectionCreator; - _dict = new ConcurrentDictionary>( - ConvertItems(items), - comparer ?? EqualityComparer.Default); - } - - private IEnumerable>> ConvertItems(IEnumerable>> items) - { - if (items == null) - yield break; - - foreach (var item in items) - { - yield return new KeyValuePair>(item.Key, CreateCollection(item.Value)); - } - } - - protected virtual SyncedCollection CreateCollection(IEnumerable values) - { - var creator = _collectionCreator ?? Multimap.DefaultCollectionCreator; - var col = creator(values ?? Enumerable.Empty()); - - return col.AsSynchronized(); - } - - /// - /// Gets the count of groups/keys. - /// - public int Count - { - get - { - return _dict.Keys.Count; - } - } - - /// - /// Gets the total count of items in all groups. - /// - public int TotalValueCount - { - get - { - return this._dict.Values.Sum(x => x.Count); - } - } - - /// - /// Gets the count of items in the requested group. - /// - public int ValueCount(TKey key) - { - if (_dict.TryGetValue(key, out var col)) - { - return col.Count; - } - - return 0; - } - - /// - /// Gets the collection of values stored under the specified key. - /// - /// The key. - public virtual SyncedCollection this[TKey key] - { - get - { - GetOrCreateValues(key, null, out var col); - return col; - } - } - - /// - /// Gets the collection of keys. - /// - public virtual ICollection Keys - { - get { return _dict.Keys; } - } - - /// - /// Gets all value collections. - /// - public virtual ICollection> Values - { - get { return _dict.Values; } - } - - /// - /// Attempts to add the specified value for the specified key. - /// - /// The key. - /// The value. - public virtual void TryAdd(TKey key, TValue value) - { - if (!GetOrCreateValues(key, new[] { value }, out var col)) - { - col.Add(value); - } - } - - /// - /// Attempts to add the specified values to the specified key. - /// - /// The key. - /// The values. - public virtual void TryAddRange(TKey key, IEnumerable values) - { - Guard.NotNull(values, nameof(values)); - - if (!GetOrCreateValues(key, values, out var col)) - { - col.AddRange(values); - } - } - - /// - /// Attempts to remove the specified value for the specified key. - /// - /// The key. - /// The value. - /// True if such a value existed and was removed; otherwise false. - public virtual bool TryRemove(TKey key, TValue value) - { - if (!_dict.ContainsKey(key)) - return false; - - var col = _dict[key]; - var removed = false; - - removed = col.Remove(value); - if (col.Count == 0) - { - _dict.TryRemove(key, out _); - } - - return removed; - } - - /// - /// Attempts to remove a range of values for the specified key. - /// - /// The key. - /// The values to remove from the group. - /// True if at least one item in group has been removed; otherwise false. - public virtual bool TryRemoveRange(TKey key, IEnumerable values) - { - Guard.NotNull(values, nameof(values)); - - int numRemoved = 0; - - if (_dict.TryGetValue(key, out var col)) - { - numRemoved = col.RemoveRange(values); - } - - return numRemoved > 0; - } - - /// - /// Attempts to remove and return all values for the specified key. - /// - /// The key. - /// True if any such values existed; otherwise false. - public virtual bool TryRemoveAll(TKey key, out SyncedCollection collection) - { - return _dict.TryRemove(key, out collection); - } - - /// - /// Removes all values. - /// - public virtual void Clear() - { - _dict.Clear(); - } - - /// - /// Determines whether the multimap contains any values for the specified key. - /// - /// The key. - /// True if the multimap has one or more values for the specified key, otherwise false. - public virtual bool ContainsKey(TKey key) - { - return _dict.ContainsKey(key); - } - - /// - /// Determines whether the multimap contains the specified value for the specified key. - /// - /// The key. - /// The value. - /// True if the multimap contains such a value; otherwise, false. - public virtual bool ContainsValue(TKey key, TValue value) - { - return _dict.ContainsKey(key) && _dict[key].Contains(value); - } - - /// - /// Returns an enumerator that iterates through the multimap. - /// - /// An object that can be used to iterate through the multimap. - IEnumerator IEnumerable.GetEnumerator() - { - return this.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through the multimap. - /// - /// An object that can be used to iterate through the multimap. - public virtual IEnumerator>> GetEnumerator() - { - foreach (var pair in _dict) - yield return pair; - } - - private bool GetOrCreateValues(TKey key, IEnumerable initial, out SyncedCollection col) - { - // Return true when created - var created = false; - - col = _dict.GetOrAdd(key, k => - { - created = true; - return CreateCollection(initial); - }); - - return created; - } - - #region Static members - - public static ConcurrentMultimap CreateFromLookup(ILookup source) - { - Guard.NotNull(source, nameof(source)); - - var map = new ConcurrentMultimap(); - - foreach (IGrouping group in source) - { - map.TryAddRange(group.Key, group); - } - - return map; - } - - #endregion - } - - public class ConcurrentMultiMapConverter : MultiMapConverter - { - public override bool CanConvert(Type objectType) - { - var canConvert = objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(ConcurrentMultimap<,>); - return canConvert; - } - } + /// + /// A thread-safe data structure that contains multiple values for each key. + /// + /// The type of key. + /// The type of value. + [JsonConverter(typeof(ConcurrentMultiMapConverter))] + [Serializable] + public class ConcurrentMultimap : IEnumerable>> + { + private readonly ConcurrentDictionary> _dict; + private readonly Func, ICollection> _collectionCreator; + + public ConcurrentMultimap() + : this(null, null, null) + { + } + + public ConcurrentMultimap(IEqualityComparer comparer) + : this(null, comparer, null) + { + } + + public ConcurrentMultimap(IEnumerable>> items) + : this(items, null, null) + { + // for serialization + } + + public ConcurrentMultimap( + IEnumerable>> items, + IEqualityComparer comparer) + : this(items, comparer, null) + { + // for serialization + } + + public ConcurrentMultimap( + IEnumerable>> items, + IEqualityComparer comparer, + Func, ICollection> collectionCreator) + { + _collectionCreator = collectionCreator; + _dict = new ConcurrentDictionary>( + ConvertItems(items), + comparer ?? EqualityComparer.Default); + } + + private IEnumerable>> ConvertItems(IEnumerable>> items) + { + if (items == null) + yield break; + + foreach (var item in items) + { + yield return new KeyValuePair>(item.Key, CreateCollection(item.Value)); + } + } + + protected virtual SyncedCollection CreateCollection(IEnumerable values) + { + var creator = _collectionCreator ?? Multimap.DefaultCollectionCreator; + var col = creator(values ?? Enumerable.Empty()); + + return col.AsSynchronized(); + } + + /// + /// Gets the count of groups/keys. + /// + public int Count => _dict.Keys.Count; + + /// + /// Gets the total count of items in all groups. + /// + public int TotalValueCount => this._dict.Values.Sum(x => x.Count); + + /// + /// Gets the count of items in the requested group. + /// + public int ValueCount(TKey key) + { + if (_dict.TryGetValue(key, out var col)) + { + return col.Count; + } + + return 0; + } + + /// + /// Gets the collection of values stored under the specified key. + /// + /// The key. + public virtual SyncedCollection this[TKey key] + { + get + { + GetOrCreateValues(key, null, out var col); + return col; + } + } + + /// + /// Gets the collection of keys. + /// + public virtual ICollection Keys => _dict.Keys; + + /// + /// Gets all value collections. + /// + public virtual ICollection> Values => _dict.Values; + + /// + /// Attempts to add the specified value for the specified key. + /// + /// The key. + /// The value. + public virtual void TryAdd(TKey key, TValue value) + { + if (!GetOrCreateValues(key, new[] { value }, out var col)) + { + col.Add(value); + } + } + + /// + /// Attempts to add the specified values to the specified key. + /// + /// The key. + /// The values. + public virtual void TryAddRange(TKey key, IEnumerable values) + { + Guard.NotNull(values, nameof(values)); + + if (!GetOrCreateValues(key, values, out var col)) + { + col.AddRange(values); + } + } + + /// + /// Attempts to remove the specified value for the specified key. + /// + /// The key. + /// The value. + /// True if such a value existed and was removed; otherwise false. + public virtual bool TryRemove(TKey key, TValue value) + { + if (!_dict.ContainsKey(key)) + return false; + + var col = _dict[key]; + var removed = false; + + removed = col.Remove(value); + if (col.Count == 0) + { + _dict.TryRemove(key, out _); + } + + return removed; + } + + /// + /// Attempts to remove a range of values for the specified key. + /// + /// The key. + /// The values to remove from the group. + /// True if at least one item in group has been removed; otherwise false. + public virtual bool TryRemoveRange(TKey key, IEnumerable values) + { + Guard.NotNull(values, nameof(values)); + + int numRemoved = 0; + + if (_dict.TryGetValue(key, out var col)) + { + numRemoved = col.RemoveRange(values); + } + + return numRemoved > 0; + } + + /// + /// Attempts to remove and return all values for the specified key. + /// + /// The key. + /// True if any such values existed; otherwise false. + public virtual bool TryRemoveAll(TKey key, out SyncedCollection collection) + { + return _dict.TryRemove(key, out collection); + } + + /// + /// Removes all values. + /// + public virtual void Clear() + { + _dict.Clear(); + } + + /// + /// Determines whether the multimap contains any values for the specified key. + /// + /// The key. + /// True if the multimap has one or more values for the specified key, otherwise false. + public virtual bool ContainsKey(TKey key) + { + return _dict.ContainsKey(key); + } + + /// + /// Determines whether the multimap contains the specified value for the specified key. + /// + /// The key. + /// The value. + /// True if the multimap contains such a value; otherwise, false. + public virtual bool ContainsValue(TKey key, TValue value) + { + return _dict.ContainsKey(key) && _dict[key].Contains(value); + } + + /// + /// Returns an enumerator that iterates through the multimap. + /// + /// An object that can be used to iterate through the multimap. + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through the multimap. + /// + /// An object that can be used to iterate through the multimap. + public virtual IEnumerator>> GetEnumerator() + { + foreach (var pair in _dict) + yield return pair; + } + + private bool GetOrCreateValues(TKey key, IEnumerable initial, out SyncedCollection col) + { + // Return true when created + var created = false; + + col = _dict.GetOrAdd(key, k => + { + created = true; + return CreateCollection(initial); + }); + + return created; + } + + #region Static members + + public static ConcurrentMultimap CreateFromLookup(ILookup source) + { + Guard.NotNull(source, nameof(source)); + + var map = new ConcurrentMultimap(); + + foreach (IGrouping group in source) + { + map.TryAddRange(group.Key, group); + } + + return map; + } + + #endregion + } + + public class ConcurrentMultiMapConverter : MultiMapConverter + { + public override bool CanConvert(Type objectType) + { + var canConvert = objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(ConcurrentMultimap<,>); + return canConvert; + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Collections/LazyMultimap.cs b/src/Libraries/SmartStore.Core/Collections/LazyMultimap.cs index a9014b0c80..fd11e92b90 100644 --- a/src/Libraries/SmartStore.Core/Collections/LazyMultimap.cs +++ b/src/Libraries/SmartStore.Core/Collections/LazyMultimap.cs @@ -6,131 +6,128 @@ namespace SmartStore.Collections { - /// - /// Manages data keys and offers a combination of eager and lazy data loading - /// - public class LazyMultimap : Multimap - { - private readonly Func> _load; - private readonly HashSet _loaded; // to avoid database round trips with empty results - private HashSet _collect; - private bool _fullyLoaded; - //private int _roundTripCount; - - /// - /// Constructor - /// - /// int[] keys like Entity.Id, Multimap{int, T}> delegate to load data - /// Keys of eager loaded data - public LazyMultimap(Func> load, IEnumerable collect = null) - { - _load = load; - _loaded = new HashSet(); - - _collect = collect == null ? new HashSet() : new HashSet(collect); - } - - public bool FullyLoaded - { - get { return _fullyLoaded; } - } - - /// - /// Get data. Load it if not already loaded yet. - /// - /// Data key - /// Collection of data - public virtual ICollection GetOrLoad(int key) - { - if (key == 0) - { - return new List(); - } - - if (!_loaded.Contains(key)) - { - Load(new int[] { key }); - } - - // better not override indexer cause of stack overflow risk - var result = base[key]; - - Debug.Assert(_loaded.Contains(key), "Possible missing multimap result for key {0} and type {1}.".FormatInvariant(key, typeof(T).Name), ""); - - return result; - } - - public void LoadAll() - { - Load(_collect); - _fullyLoaded = true; - } - - protected virtual void Load(IEnumerable keys) - { - if (keys == null) - return; - - var loadKeys = (_collect.Count == 0 ? keys : _collect.Concat(keys)) - .Distinct() - .Except(_loaded) - .ToArray(); - - _collect.Clear(); // invalidate, do not load again - - if (loadKeys.Any()) - { - //++_roundTripCount; - //Debug.WriteLine("Round trip {0} of {1}: {2}", _roundTripCount, typeof(T).Name, string.Join(",", loadKeys.OrderBy(x => x))); - - var items = _load(loadKeys); - - _loaded.AddRange(loadKeys); - - if (items != null) - { - foreach (var range in items) - { - base.AddRange(range.Key, range.Value); - } - } - } - } - - /// - /// Collect keys for combined loading - /// - /// Data keys - [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] - public virtual void Collect(IEnumerable keys) - { - if (keys != null && keys.Any()) - { - //_collect = _collect.Union(keys).ToList(); - _collect.UnionWith(keys); - } - } - - /// - /// Collect single key for combined loading - /// - /// Data key - public virtual void Collect(int key) - { - if (key != 0 && !_collect.Contains(key)) - { - _collect.Add(key); - } - } - - public override void Clear() - { - _loaded.Clear(); - _collect.Clear(); - _fullyLoaded = false; - //_roundTripCount = 0; - - base.Clear(); - } - } + /// + /// Manages data keys and offers a combination of eager and lazy data loading + /// + public class LazyMultimap : Multimap + { + private readonly Func> _load; + private readonly HashSet _loaded; // to avoid database round trips with empty results + private readonly HashSet _collect; + private bool _fullyLoaded; + //private int _roundTripCount; + + /// + /// Constructor + /// + /// int[] keys like Entity.Id, Multimap{int, T}> delegate to load data + /// Keys of eager loaded data + public LazyMultimap(Func> load, IEnumerable collect = null) + { + _load = load; + _loaded = new HashSet(); + + _collect = collect == null ? new HashSet() : new HashSet(collect); + } + + public bool FullyLoaded => _fullyLoaded; + + /// + /// Get data. Load it if not already loaded yet. + /// + /// Data key + /// Collection of data + public virtual ICollection GetOrLoad(int key) + { + if (key == 0) + { + return new List(); + } + + if (!_loaded.Contains(key)) + { + Load(new int[] { key }); + } + + // better not override indexer cause of stack overflow risk + var result = base[key]; + + Debug.Assert(_loaded.Contains(key), "Possible missing multimap result for key {0} and type {1}.".FormatInvariant(key, typeof(T).Name), ""); + + return result; + } + + public void LoadAll() + { + Load(_collect); + _fullyLoaded = true; + } + + protected virtual void Load(IEnumerable keys) + { + if (keys == null) + return; + + var loadKeys = (_collect.Count == 0 ? keys : _collect.Concat(keys)) + .Distinct() + .Except(_loaded) + .ToArray(); + + _collect.Clear(); // invalidate, do not load again + + if (loadKeys.Any()) + { + //++_roundTripCount; + //Debug.WriteLine("Round trip {0} of {1}: {2}", _roundTripCount, typeof(T).Name, string.Join(",", loadKeys.OrderBy(x => x))); + + var items = _load(loadKeys); + + _loaded.AddRange(loadKeys); + + if (items != null) + { + foreach (var range in items) + { + base.AddRange(range.Key, range.Value); + } + } + } + } + + /// + /// Collect keys for combined loading + /// + /// Data keys + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + public virtual void Collect(IEnumerable keys) + { + if (keys != null && keys.Any()) + { + //_collect = _collect.Union(keys).ToList(); + _collect.UnionWith(keys); + } + } + + /// + /// Collect single key for combined loading + /// + /// Data key + public virtual void Collect(int key) + { + if (key != 0 && !_collect.Contains(key)) + { + _collect.Add(key); + } + } + + public override void Clear() + { + _loaded.Clear(); + _collect.Clear(); + _fullyLoaded = false; + //_roundTripCount = 0; + + base.Clear(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Collections/MultiMap.cs b/src/Libraries/SmartStore.Core/Collections/MultiMap.cs index 738ffab5fa..91c2957375 100644 --- a/src/Libraries/SmartStore.Core/Collections/MultiMap.cs +++ b/src/Libraries/SmartStore.Core/Collections/MultiMap.cs @@ -2,62 +2,61 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; using Newtonsoft.Json; namespace SmartStore.Collections { - /// - /// A data structure that contains multiple values for each key. - /// - /// The type of key. - /// The type of value. - [JsonConverter(typeof(MultiMapConverter))] - [Serializable] - public class Multimap : IEnumerable>> + /// + /// A data structure that contains multiple values for each key. + /// + /// The type of key. + /// The type of value. + [JsonConverter(typeof(MultiMapConverter))] + [Serializable] + public class Multimap : IEnumerable>> { - private readonly IDictionary> _dict; - private readonly Func, ICollection> _collectionCreator; + private readonly IDictionary> _dict; + private readonly Func, ICollection> _collectionCreator; private readonly bool _isReadonly = false; - internal readonly static Func, ICollection> DefaultCollectionCreator = - x => new List(x ?? Enumerable.Empty()); + internal readonly static Func, ICollection> DefaultCollectionCreator = + x => new List(x ?? Enumerable.Empty()); - public Multimap() + public Multimap() : this(EqualityComparer.Default) { } - public Multimap(IEqualityComparer comparer) + public Multimap(IEqualityComparer comparer) { - _dict = new Dictionary>(comparer ?? EqualityComparer.Default); + _dict = new Dictionary>(comparer ?? EqualityComparer.Default); _collectionCreator = DefaultCollectionCreator; } - public Multimap(Func, ICollection> collectionCreator) - : this(new Dictionary>(), collectionCreator) + public Multimap(Func, ICollection> collectionCreator) + : this(new Dictionary>(), collectionCreator) { } - public Multimap(IEqualityComparer comparer, Func, ICollection> collectionCreator) - : this(new Dictionary>(comparer ?? EqualityComparer.Default), collectionCreator) - { - } + public Multimap(IEqualityComparer comparer, Func, ICollection> collectionCreator) + : this(new Dictionary>(comparer ?? EqualityComparer.Default), collectionCreator) + { + } - internal Multimap(IDictionary> dictionary, Func, ICollection> collectionCreator) + internal Multimap(IDictionary> dictionary, Func, ICollection> collectionCreator) { - Guard.NotNull(dictionary, nameof(dictionary)); - Guard.NotNull(collectionCreator, nameof(collectionCreator)); + Guard.NotNull(dictionary, nameof(dictionary)); + Guard.NotNull(collectionCreator, nameof(collectionCreator)); - _dict = dictionary; + _dict = dictionary; _collectionCreator = collectionCreator; } - protected Multimap(IDictionary> dictionary, bool isReadonly) + protected Multimap(IDictionary> dictionary, bool isReadonly) { - Guard.NotNull(dictionary, nameof(dictionary)); + Guard.NotNull(dictionary, nameof(dictionary)); - _dict = dictionary; + _dict = dictionary; if (isReadonly && dictionary != null) { @@ -70,54 +69,42 @@ protected Multimap(IDictionary> dictionary, bool isRea _isReadonly = isReadonly; } - public Multimap(IEnumerable>> items) - : this(items, null) - { - // for serialization - } - - public Multimap(IEnumerable>> items, IEqualityComparer comparer) - { - // for serialization - Guard.NotNull(items, nameof(items)); - - _dict = new Dictionary>(comparer ?? EqualityComparer.Default); - - if (items != null) - { - foreach (var kvp in items) - { - _dict[kvp.Key] = CreateCollection(kvp.Value); - } - } - } - - protected virtual ICollection CreateCollection(IEnumerable values) - { - return (_collectionCreator ?? DefaultCollectionCreator)(values ?? Enumerable.Empty()); - } - - /// - /// Gets the count of groups/keys. - /// - public int Count + public Multimap(IEnumerable>> items) + : this(items, null) { - get + // for serialization + } + + public Multimap(IEnumerable>> items, IEqualityComparer comparer) + { + // for serialization + Guard.NotNull(items, nameof(items)); + + _dict = new Dictionary>(comparer ?? EqualityComparer.Default); + + if (items != null) { - return this._dict.Keys.Count; + foreach (var kvp in items) + { + _dict[kvp.Key] = CreateCollection(kvp.Value); + } } } + protected virtual ICollection CreateCollection(IEnumerable values) + { + return (_collectionCreator ?? DefaultCollectionCreator)(values ?? Enumerable.Empty()); + } + + /// + /// Gets the count of groups/keys. + /// + public int Count => this._dict.Keys.Count; + /// /// Gets the total count of items in all groups. /// - public int TotalValueCount - { - get - { - return this._dict.Values.Sum(x => x.Count); - } - } + public int TotalValueCount => this._dict.Values.Sum(x => x.Count); /// /// Gets the collection of values stored under the specified key. @@ -142,27 +129,21 @@ public virtual ICollection this[TKey key] /// /// Gets the collection of keys. /// - public virtual ICollection Keys - { - get { return _dict.Keys; } - } + public virtual ICollection Keys => _dict.Keys; - /// - /// Gets all value collections. - /// - public virtual ICollection> Values - { - get { return _dict.Values; } - } + /// + /// Gets all value collections. + /// + public virtual ICollection> Values => _dict.Values; - public IEnumerable Find(TKey key, Expression> predicate) + public IEnumerable Find(TKey key, Func predicate) { - Guard.NotNull(key, nameof(key)); - Guard.NotNull(predicate, nameof(predicate)); + Guard.NotNull(key, nameof(key)); + Guard.NotNull(predicate, nameof(predicate)); - if (_dict.ContainsKey(key)) + if (_dict.ContainsKey(key)) { - return _dict[key].Where(predicate.Compile()); + return _dict[key].Where(predicate); } return Enumerable.Empty(); @@ -187,12 +168,12 @@ public virtual void Add(TKey key, TValue value) /// The values. public virtual void AddRange(TKey key, IEnumerable values) { - if (values == null || !values.Any()) - return; + if (values == null || !values.Any()) + return; - CheckNotReadonly(); + CheckNotReadonly(); - this[key].AddRange(values); + this[key].AddRange(values); } /// @@ -271,7 +252,7 @@ IEnumerator IEnumerable.GetEnumerator() /// An object that can be used to iterate through the multimap. public virtual IEnumerator>> GetEnumerator() { - foreach (KeyValuePair> pair in _dict) + foreach (KeyValuePair> pair in _dict) yield return pair; } @@ -285,9 +266,9 @@ private void CheckNotReadonly() public static Multimap CreateFromLookup(ILookup source) { - Guard.NotNull(source, nameof(source)); + Guard.NotNull(source, nameof(source)); - var map = new Multimap(); + var map = new Multimap(); foreach (IGrouping group in source) { @@ -297,56 +278,56 @@ public static Multimap CreateFromLookup(ILookup sour return map; } - #endregion - } - - public class MultiMapConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - var canConvert = objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Multimap<,>); - return canConvert; - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - // typeof TKey - var keyType = objectType.GetGenericArguments()[0]; - - // typeof TValue - var valueType = objectType.GetGenericArguments()[1]; - - // typeof IEnumerable> - var sequenceType = typeof(IEnumerable<>).MakeGenericType(typeof(KeyValuePair<,>).MakeGenericType(keyType, typeof(IEnumerable<>).MakeGenericType(valueType))); - - // serialize JArray to sequenceType - var list = serializer.Deserialize(reader, sequenceType); - - if (keyType == typeof(string)) - { - // call constructor Multimap(IEnumerable>> items, IEqualityComparer comparer) - // TBD: we always assume string keys to be case insensitive. Serialize it somehow and fetch here! - return Activator.CreateInstance(objectType, new object[] { list, StringComparer.OrdinalIgnoreCase }); - } - else - { - // call constructor Multimap(IEnumerable>> items) - return Activator.CreateInstance(objectType, new object[] { list }); - } - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - writer.WriteStartArray(); - { - var enumerable = value as IEnumerable; - foreach (var item in enumerable) - { - // Json.Net uses a converter for KeyValuePair here - serializer.Serialize(writer, item); - } - } - writer.WriteEndArray(); - } - } + #endregion + } + + public class MultiMapConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + var canConvert = objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Multimap<,>); + return canConvert; + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + // typeof TKey + var keyType = objectType.GetGenericArguments()[0]; + + // typeof TValue + var valueType = objectType.GetGenericArguments()[1]; + + // typeof IEnumerable> + var sequenceType = typeof(IEnumerable<>).MakeGenericType(typeof(KeyValuePair<,>).MakeGenericType(keyType, typeof(IEnumerable<>).MakeGenericType(valueType))); + + // serialize JArray to sequenceType + var list = serializer.Deserialize(reader, sequenceType); + + if (keyType == typeof(string)) + { + // call constructor Multimap(IEnumerable>> items, IEqualityComparer comparer) + // TBD: we always assume string keys to be case insensitive. Serialize it somehow and fetch here! + return Activator.CreateInstance(objectType, new object[] { list, StringComparer.OrdinalIgnoreCase }); + } + else + { + // call constructor Multimap(IEnumerable>> items) + return Activator.CreateInstance(objectType, new object[] { list }); + } + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteStartArray(); + { + var enumerable = value as IEnumerable; + foreach (var item in enumerable) + { + // Json.Net uses a converter for KeyValuePair here + serializer.Serialize(writer, item); + } + } + writer.WriteEndArray(); + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Collections/Querystring.cs b/src/Libraries/SmartStore.Core/Collections/Querystring.cs index ba5a3cc895..84de3dd797 100644 --- a/src/Libraries/SmartStore.Core/Collections/Querystring.cs +++ b/src/Libraries/SmartStore.Core/Collections/Querystring.cs @@ -2,13 +2,14 @@ using System.Diagnostics.CodeAnalysis; using System.Text; using System.Web; +using SmartStore.Utilities.ObjectPools; namespace SmartStore.Collections { - /// - /// http://weblogs.asp.net/bradvincent/archive/2008/10/27/helper-class-querystring-builder-chainable.aspx - /// - public class QueryString : NameValueCollection + /// + /// http://weblogs.asp.net/bradvincent/archive/2008/10/27/helper-class-querystring-builder-chainable.aspx + /// + public class QueryString : NameValueCollection { public QueryString() { @@ -19,27 +20,21 @@ public QueryString(string queryString) FillFromString(queryString); } - public QueryString(NameValueCollection queryString) - : base(queryString) - { - } - - public static QueryString Current + public QueryString(NameValueCollection queryString) + : base(queryString) { - get { return new QueryString().FromCurrent(); } } - public static QueryString CurrentUnvalidated - { - get { return new QueryString().FromCurrent(true); } - } - - /// - /// Extracts a querystring from a full URL - /// - /// the string to extract the querystring from - /// a string representing only the querystring - [SuppressMessage("ReSharper", "StringIndexOfIsCultureSpecific.1")] + public static QueryString Current => new QueryString().FromCurrent(); + + public static QueryString CurrentUnvalidated => new QueryString().FromCurrent(true); + + /// + /// Extracts a querystring from a full URL + /// + /// the string to extract the querystring from + /// a string representing only the querystring + [SuppressMessage("ReSharper", "StringIndexOfIsCultureSpecific.1")] public static string ExtractQuerystring(string s) { if (!string.IsNullOrEmpty(s)) @@ -76,6 +71,7 @@ public QueryString FillFromString(string s, bool urlDecode = false) string[] split = keyValuePair.Split('='); base.Add(split[0], split.Length == 2 ? (urlDecode ? HttpUtility.UrlDecode(split[1]) : split[1]) : ""); } + return this; } @@ -88,21 +84,21 @@ public QueryString FromCurrent(bool unvalidated = false) { if (HttpContext.Current != null) { - if (unvalidated) - { - return FillFromString(HttpContext.Current.Request.Unvalidated.QueryString.ToString(), true); - } - else - { - return FillFromString(HttpContext.Current.Request.QueryString.ToString(), true); - } + if (unvalidated) + { + return FillFromString(HttpContext.Current.Request.Unvalidated.QueryString.ToString(), true); + } + else + { + return FillFromString(HttpContext.Current.Request.QueryString.ToString(), true); + } } base.Clear(); return this; } /// - /// add a name value pair to the collection + /// Add a name value pair to the collection /// /// the name /// the value associated to the name @@ -119,7 +115,7 @@ public QueryString FromCurrent(bool unvalidated = false) /// the value associated to the name /// true if the name is unique within the querystring. This allows us to override existing values /// the QueryString object - public QueryString Add(string name, string value, bool isUnique) + public virtual QueryString Add(string name, string value, bool isUnique) { string existingValue = base[name]; if (string.IsNullOrEmpty(existingValue)) @@ -138,12 +134,12 @@ public QueryString Add(string name, string value, bool isUnique) return this; } - /// - /// Removes a name value pair from the querystring collection - /// - /// name of the querystring value to remove - /// the QueryString object - public new QueryString Remove(string name) + /// + /// Removes a name value pair from the querystring collection + /// + /// name of the querystring value to remove + /// the QueryString object + public new QueryString Remove(string name) { string existingValue = base[name]; if (!string.IsNullOrEmpty(existingValue)) @@ -164,24 +160,18 @@ public QueryString Reset() } /// - /// overrides the default + /// Overrides the default indexer /// /// /// the associated decoded value for the specified name - public new string this[string name] - { - get { return HttpUtility.UrlDecode(base[name]); } - } + public new string this[string name] => HttpUtility.UrlDecode(base[name]); /// /// overrides the default indexer /// /// /// the associated decoded value for the specified index - public new string this[int index] - { - get { return HttpUtility.UrlDecode(base[index]); } - } + public new string this[int index] => HttpUtility.UrlDecode(base[index]); /// /// checks if a name already exists within the query string collection @@ -200,41 +190,43 @@ public bool Contains(string name) /// the encoded querystring as it would appear in a browser public override string ToString() { - return ToString(true); + return ToString(true); } - /// - /// Outputs the querystring object to a string - /// - /// Whether to create entries for each comma-separated value - /// the encoded querystring as it would appear in a browser - public string ToString(bool splitValues) - { - var builder = new StringBuilder(); - for (var i = 0; i < base.Keys.Count; i++) - { - var key = base.Keys[i]; - var value = base[key]; - - if (!string.IsNullOrEmpty(key)) - { - builder.Append((builder.Length == 0) ? "?" : "&"); - - if (splitValues) - { - foreach (string val in value.EmptyNull().Split(',')) - { - builder.Append(HttpUtility.UrlEncode(key)).Append("=").Append(val); - } - } - else - { - builder.Append(HttpUtility.UrlEncode(key)).Append("=").Append(value); - } - } - } - - return builder.ToString(); - } - } + /// + /// Outputs the querystring object to a string + /// + /// Whether to create entries for each comma-separated value + /// the encoded querystring as it would appear in a browser + public string ToString(bool splitValues) + { + var psb = PooledStringBuilder.Rent(); + var builder = (StringBuilder)psb; + + for (var i = 0; i < base.Keys.Count; i++) + { + var key = base.Keys[i]; + var value = base[key]; + + if (!string.IsNullOrEmpty(key)) + { + builder.Append((builder.Length == 0) ? "?" : "&"); + + if (splitValues) + { + foreach (string val in value.EmptyNull().Split(',')) + { + builder.Append(HttpUtility.UrlEncode(key)).Append("=").Append(val); + } + } + else + { + builder.Append(HttpUtility.UrlEncode(key)).Append("=").Append(value); + } + } + } + + return psb.ToStringAndReturn(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Collections/ReferenceEqualityComparer.cs b/src/Libraries/SmartStore.Core/Collections/ReferenceEqualityComparer.cs index b70b4302d2..ca66ab7367 100644 --- a/src/Libraries/SmartStore.Core/Collections/ReferenceEqualityComparer.cs +++ b/src/Libraries/SmartStore.Core/Collections/ReferenceEqualityComparer.cs @@ -1,15 +1,21 @@ -using System; -using System.Collections; +using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; namespace SmartStore.ComponentModel { - public sealed class ReferenceEqualityComparer : IEqualityComparer, IEqualityComparer - { - public static ReferenceEqualityComparer Default { get; } = new ReferenceEqualityComparer(); + public sealed class ReferenceEqualityComparer : IEqualityComparer, IEqualityComparer + { + public static ReferenceEqualityComparer Default { get; } = new ReferenceEqualityComparer(); - public new bool Equals(object x, object y) => ReferenceEquals(x, y); - public int GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj); - } + public new bool Equals(object x, object y) + { + return ReferenceEquals(x, y); + } + + public int GetHashCode(object obj) + { + return RuntimeHelpers.GetHashCode(obj); + } + } } diff --git a/src/Libraries/SmartStore.Core/Collections/SyncedCollection.cs b/src/Libraries/SmartStore.Core/Collections/SyncedCollection.cs index d8100effd8..2122f56ad5 100644 --- a/src/Libraries/SmartStore.Core/Collections/SyncedCollection.cs +++ b/src/Libraries/SmartStore.Core/Collections/SyncedCollection.cs @@ -5,218 +5,218 @@ namespace SmartStore.Collections { - public sealed class SyncedCollection : ICollection - { - // INFO: Don't call it SynchronizedCollection because of framework dupe. - private readonly ICollection _col; - - public SyncedCollection(ICollection wrappedCollection) - : this(wrappedCollection, new object()) - { - } - - public SyncedCollection(ICollection wrappedCollection, object syncRoot) - { - Guard.NotNull(wrappedCollection, nameof(wrappedCollection)); - Guard.NotNull(syncRoot, nameof(syncRoot)); - - _col = wrappedCollection; - SyncRoot = syncRoot; - } - - public object SyncRoot { get; } - - public bool ReadLockFree { get; set; } - - public void AddRange(IEnumerable collection) - { - lock (SyncRoot) - { - _col.AddRange(collection); - } - } - - public void Insert(int index, T item) - { - if (_col is List list) - { - lock (SyncRoot) - { - list.Insert(index, item); - } - } - - throw new NotSupportedException(); - } - - public void InsertRange(int index, IEnumerable values) - { - if (_col is List list) - { - lock (SyncRoot) - { - list.InsertRange(index, values); - } - } - - throw new NotSupportedException(); - } - - public int RemoveRange(IEnumerable values) - { - int numRemoved = 0; - - lock (SyncRoot) - { - foreach (var value in values) - { - if (_col.Remove(value)) - numRemoved++; - } - } - - return numRemoved; - } - - public void RemoveRange(int index, int count) - { - if (_col is List list) - { - lock (SyncRoot) - { - list.RemoveRange(index, count); - } - } - - throw new NotSupportedException(); - } - - public void RemoveAt(int index) - { - lock (SyncRoot) - { - if (_col is List list) - { - list.RemoveAt(index); - } - else - { - var item = _col.ElementAtOrDefault(index); - if (item != null) - { - _col.Remove(item); - } - } - } - } - - public T this[int index] - { - get - { - if (ReadLockFree) - { - return _col.ElementAt(index); - } - else - { - lock (SyncRoot) - { - return _col.ElementAt(index); - } - } - } - } - - #region ICollection - - public int Count - { - get - { - if (ReadLockFree) - { - return _col.Count(); - } - else - { - lock (SyncRoot) - { - return _col.Count(); - } - } - } - } - - public bool IsReadOnly => _col.IsReadOnly; - - public void Add(T item) - { - lock (SyncRoot) - { - _col.Add(item); - } - } - - public void Clear() - { - lock (SyncRoot) - { - _col.Clear(); - } - } - - public bool Contains(T item) - { - if (ReadLockFree) - { - return _col.Contains(item); - } - else - { - lock (SyncRoot) - { - return _col.Contains(item); - } - } - } - - public void CopyTo(T[] array, int arrayIndex) - { - lock (SyncRoot) - { - _col.CopyTo(array, arrayIndex); - } - } - - public bool Remove(T item) - { - lock (SyncRoot) - { - return _col.Remove(item); - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return this.GetEnumerator(); - } - - public IEnumerator GetEnumerator() - { - if (ReadLockFree) - { - return _col.GetEnumerator(); - } - else - { - lock (SyncRoot) - { - return _col.GetEnumerator(); - } - } - } - - #endregion - } + public sealed class SyncedCollection : ICollection + { + // INFO: Don't call it SynchronizedCollection because of framework dupe. + private readonly ICollection _col; + + public SyncedCollection(ICollection wrappedCollection) + : this(wrappedCollection, new object()) + { + } + + public SyncedCollection(ICollection wrappedCollection, object syncRoot) + { + Guard.NotNull(wrappedCollection, nameof(wrappedCollection)); + Guard.NotNull(syncRoot, nameof(syncRoot)); + + _col = wrappedCollection; + SyncRoot = syncRoot; + } + + public object SyncRoot { get; } + + public bool ReadLockFree { get; set; } + + public void AddRange(IEnumerable collection) + { + lock (SyncRoot) + { + _col.AddRange(collection); + } + } + + public void Insert(int index, T item) + { + if (_col is List list) + { + lock (SyncRoot) + { + list.Insert(index, item); + } + } + + throw new NotSupportedException(); + } + + public void InsertRange(int index, IEnumerable values) + { + if (_col is List list) + { + lock (SyncRoot) + { + list.InsertRange(index, values); + } + } + + throw new NotSupportedException(); + } + + public int RemoveRange(IEnumerable values) + { + int numRemoved = 0; + + lock (SyncRoot) + { + foreach (var value in values) + { + if (_col.Remove(value)) + numRemoved++; + } + } + + return numRemoved; + } + + public void RemoveRange(int index, int count) + { + if (_col is List list) + { + lock (SyncRoot) + { + list.RemoveRange(index, count); + } + } + + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + lock (SyncRoot) + { + if (_col is List list) + { + list.RemoveAt(index); + } + else + { + var item = _col.ElementAtOrDefault(index); + if (item != null) + { + _col.Remove(item); + } + } + } + } + + public T this[int index] + { + get + { + if (ReadLockFree) + { + return _col.ElementAt(index); + } + else + { + lock (SyncRoot) + { + return _col.ElementAt(index); + } + } + } + } + + #region ICollection + + public int Count + { + get + { + if (ReadLockFree) + { + return _col.Count(); + } + else + { + lock (SyncRoot) + { + return _col.Count(); + } + } + } + } + + public bool IsReadOnly => _col.IsReadOnly; + + public void Add(T item) + { + lock (SyncRoot) + { + _col.Add(item); + } + } + + public void Clear() + { + lock (SyncRoot) + { + _col.Clear(); + } + } + + public bool Contains(T item) + { + if (ReadLockFree) + { + return _col.Contains(item); + } + else + { + lock (SyncRoot) + { + return _col.Contains(item); + } + } + } + + public void CopyTo(T[] array, int arrayIndex) + { + lock (SyncRoot) + { + _col.CopyTo(array, arrayIndex); + } + } + + public bool Remove(T item) + { + lock (SyncRoot) + { + return _col.Remove(item); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + if (ReadLockFree) + { + return _col.GetEnumerator(); + } + else + { + lock (SyncRoot) + { + return _col.GetEnumerator(); + } + } + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Collections/TopologicalSorter.cs b/src/Libraries/SmartStore.Core/Collections/TopologicalSorter.cs index e8902302c1..1b291e8ac3 100644 --- a/src/Libraries/SmartStore.Core/Collections/TopologicalSorter.cs +++ b/src/Libraries/SmartStore.Core/Collections/TopologicalSorter.cs @@ -6,215 +6,215 @@ namespace SmartStore.Collections { - public interface ITopologicSortable - { - TKey Key { get; } - TKey[] DependsOn { get; } - } - - public static class TopologicalSortExtensions - { - - public static ITopologicSortable[] SortTopological(this ITopologicSortable[] items) - { - return SortTopological(items, null); - } - - public static ITopologicSortable[] SortTopological(this ITopologicSortable[] items, IEqualityComparer comparer) - { - Guard.NotNull(items, nameof(items)); - - var sortedIndexes = SortIndexesTopological(items, comparer); - var sortedList = new List>(sortedIndexes.Length); - - for (var i = 0; i < sortedIndexes.Length; i++) - { - //sortedList[i] = items[sortedIndexes[i]]; - sortedList.Add(items[sortedIndexes[i]]); - } - - return sortedList.ToArray(); - } - - public static int[] SortIndexesTopological(this ITopologicSortable[] items) - { - return SortIndexesTopological(items, null); - } - - public static int[] SortIndexesTopological(this ITopologicSortable[] items, IEqualityComparer comparer) - { - Guard.NotNull(items, nameof(items)); - - if (items.Length == 0) - { - return new int[] { }; - } - - if (comparer == null) - { - comparer = EqualityComparer.Default; - } - - var sorter = new TopologicalSorter(items.Length); - var indexes = new Dictionary(comparer); - - // add vertices - for (int i = 0; i < items.Length; i++) - { - indexes[items[i].Key] = sorter.AddVertex(i); - } - - // add edges - for (int i = 0; i < items.Length; i++) - { - if (items[i].DependsOn != null) - { - for (int j = 0; j < items[i].DependsOn.Length; j++) - { - if (indexes.ContainsKey(items[i].DependsOn[j])) - { - sorter.AddEdge(i, indexes[items[i].DependsOn[j]]); - } - } - } - } - - int[] result = sorter.Sort().Reverse().ToArray(); - return result; - } - } - - public class CyclicDependencyException : Exception - { - public CyclicDependencyException() - : base("Cyclic dependency detected") - { - } - - public CyclicDependencyException(string message) - : base(message) - { - } - } - - internal class TopologicalSorter - { - #region Private Members - - private readonly int[] _vertices; // list of vertices - private readonly int[,] _matrix; // adjacency matrix - private int _numVerts; // current number of vertices - private readonly int[] _sortedArray; - - #endregion - - #region Ctor - - public TopologicalSorter(int size) - { - _vertices = new int[size]; - _matrix = new int[size, size]; - _numVerts = 0; - for (int i = 0; i < size; i++) - { - for (int j = 0; j < size; j++) - { - _matrix[i, j] = 0; - } - } - _sortedArray = new int[size]; // sorted vert labels - } - - #endregion - - #region Public Methods - - public int AddVertex(int vertex) - { - _vertices[_numVerts++] = vertex; - return _numVerts - 1; - } - - public void AddEdge(int start, int end) - { - _matrix[start, end] = 1; - } - - public int[] Sort() // topological sort - { - while (_numVerts > 0) // while vertices remain, - { - // get a vertex with no successors, or -1 - int currentVertex = NoSuccessors(); - if (currentVertex == -1) - { - // must be a cycle - throw new CyclicDependencyException(); - } - - // insert vertex label in sorted array (start at end) - _sortedArray[_numVerts - 1] = _vertices[currentVertex]; - - DeleteVertex(currentVertex); // delete vertex - } - - // vertices all gone; return sortedArray - return _sortedArray; - } - - #endregion - - #region Private Helper Methods - - // returns vert with no successors (or -1 if no such verts) - private int NoSuccessors() - { - for (int row = 0; row < _numVerts; row++) - { - bool isEdge = false; // edge from row to column in adjMat - for (int col = 0; col < _numVerts; col++) - { - if (_matrix[row, col] > 0) // if edge to another, - { - isEdge = true; - break; // this vertex has a successor try another - } - } - if (!isEdge) // if no edges, has no successors - return row; - } - return -1; // no - } - - private void DeleteVertex(int delVert) - { - // if not last vertex, delete from vertexList - if (delVert != _numVerts - 1) - { - for (int j = delVert; j < _numVerts - 1; j++) - _vertices[j] = _vertices[j + 1]; - - for (int row = delVert; row < _numVerts - 1; row++) - MoveRowUp(row, _numVerts); - - for (int col = delVert; col < _numVerts - 1; col++) - MoveColLeft(col, _numVerts - 1); - } - _numVerts--; // one less vertex - } - - private void MoveRowUp(int row, int length) - { - for (int col = 0; col < length; col++) - _matrix[row, col] = _matrix[row + 1, col]; - } - - private void MoveColLeft(int col, int length) - { - for (int row = 0; row < length; row++) - _matrix[row, col] = _matrix[row, col + 1]; - } - - #endregion - } + public interface ITopologicSortable + { + TKey Key { get; } + TKey[] DependsOn { get; } + } + + public static class TopologicalSortExtensions + { + + public static ITopologicSortable[] SortTopological(this ITopologicSortable[] items) + { + return SortTopological(items, null); + } + + public static ITopologicSortable[] SortTopological(this ITopologicSortable[] items, IEqualityComparer comparer) + { + Guard.NotNull(items, nameof(items)); + + var sortedIndexes = SortIndexesTopological(items, comparer); + var sortedList = new List>(sortedIndexes.Length); + + for (var i = 0; i < sortedIndexes.Length; i++) + { + //sortedList[i] = items[sortedIndexes[i]]; + sortedList.Add(items[sortedIndexes[i]]); + } + + return sortedList.ToArray(); + } + + public static int[] SortIndexesTopological(this ITopologicSortable[] items) + { + return SortIndexesTopological(items, null); + } + + public static int[] SortIndexesTopological(this ITopologicSortable[] items, IEqualityComparer comparer) + { + Guard.NotNull(items, nameof(items)); + + if (items.Length == 0) + { + return new int[] { }; + } + + if (comparer == null) + { + comparer = EqualityComparer.Default; + } + + var sorter = new TopologicalSorter(items.Length); + var indexes = new Dictionary(comparer); + + // add vertices + for (int i = 0; i < items.Length; i++) + { + indexes[items[i].Key] = sorter.AddVertex(i); + } + + // add edges + for (int i = 0; i < items.Length; i++) + { + if (items[i].DependsOn != null) + { + for (int j = 0; j < items[i].DependsOn.Length; j++) + { + if (indexes.ContainsKey(items[i].DependsOn[j])) + { + sorter.AddEdge(i, indexes[items[i].DependsOn[j]]); + } + } + } + } + + int[] result = sorter.Sort().Reverse().ToArray(); + return result; + } + } + + public class CyclicDependencyException : Exception + { + public CyclicDependencyException() + : base("Cyclic dependency detected") + { + } + + public CyclicDependencyException(string message) + : base(message) + { + } + } + + internal class TopologicalSorter + { + #region Private Members + + private readonly int[] _vertices; // list of vertices + private readonly int[,] _matrix; // adjacency matrix + private int _numVerts; // current number of vertices + private readonly int[] _sortedArray; + + #endregion + + #region Ctor + + public TopologicalSorter(int size) + { + _vertices = new int[size]; + _matrix = new int[size, size]; + _numVerts = 0; + for (int i = 0; i < size; i++) + { + for (int j = 0; j < size; j++) + { + _matrix[i, j] = 0; + } + } + _sortedArray = new int[size]; // sorted vert labels + } + + #endregion + + #region Public Methods + + public int AddVertex(int vertex) + { + _vertices[_numVerts++] = vertex; + return _numVerts - 1; + } + + public void AddEdge(int start, int end) + { + _matrix[start, end] = 1; + } + + public int[] Sort() // topological sort + { + while (_numVerts > 0) // while vertices remain, + { + // get a vertex with no successors, or -1 + int currentVertex = NoSuccessors(); + if (currentVertex == -1) + { + // must be a cycle + throw new CyclicDependencyException(); + } + + // insert vertex label in sorted array (start at end) + _sortedArray[_numVerts - 1] = _vertices[currentVertex]; + + DeleteVertex(currentVertex); // delete vertex + } + + // vertices all gone; return sortedArray + return _sortedArray; + } + + #endregion + + #region Private Helper Methods + + // returns vert with no successors (or -1 if no such verts) + private int NoSuccessors() + { + for (int row = 0; row < _numVerts; row++) + { + bool isEdge = false; // edge from row to column in adjMat + for (int col = 0; col < _numVerts; col++) + { + if (_matrix[row, col] > 0) // if edge to another, + { + isEdge = true; + break; // this vertex has a successor try another + } + } + if (!isEdge) // if no edges, has no successors + return row; + } + return -1; // no + } + + private void DeleteVertex(int delVert) + { + // if not last vertex, delete from vertexList + if (delVert != _numVerts - 1) + { + for (int j = delVert; j < _numVerts - 1; j++) + _vertices[j] = _vertices[j + 1]; + + for (int row = delVert; row < _numVerts - 1; row++) + MoveRowUp(row, _numVerts); + + for (int col = delVert; col < _numVerts - 1; col++) + MoveColLeft(col, _numVerts - 1); + } + _numVerts--; // one less vertex + } + + private void MoveRowUp(int row, int length) + { + for (int col = 0; col < length; col++) + _matrix[row, col] = _matrix[row + 1, col]; + } + + private void MoveColLeft(int col, int length) + { + for (int row = 0; row < length; row++) + _matrix[row, col] = _matrix[row, col + 1]; + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Collections/TreeNode.cs b/src/Libraries/SmartStore.Core/Collections/TreeNode.cs index 00034d0778..78418736fe 100644 --- a/src/Libraries/SmartStore.Core/Collections/TreeNode.cs +++ b/src/Libraries/SmartStore.Core/Collections/TreeNode.cs @@ -1,258 +1,257 @@ using System; -using System.Collections; using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.IO; using System.Linq; -using System.Linq.Expressions; -using System.Text; +using System.Runtime.CompilerServices; using Newtonsoft.Json; using SmartStore.ComponentModel; namespace SmartStore.Collections { - [JsonConverter(typeof(TreeNodeConverter))] - public class TreeNode : TreeNodeBase> - { - public TreeNode(TValue value) - { - Guard.NotNull(value, nameof(value)); - - Value = value; - } - - public TreeNode(TValue value, IEnumerable children) - : this(value) - { - if (children != null && children.Any()) - { - AppendRange(children); - } - } - - public TreeNode(TValue value, IEnumerable> children) - : this(value) - { - // for serialization - if (children != null && children.Any()) - { - AppendRange(children); - } - } - - public TValue Value - { - get; - private set; - } - - protected override TreeNode CreateInstance() - { - TValue value = this.Value; - - if (value is ICloneable) - { - value = ((ICloneable)value).Clone(); - } - - var clonedNode = new TreeNode(value); - - // Assign or clone Metadata - if (_metadata != null && _metadata.Count > 0) - { - foreach (var kvp in _metadata) - { - var metadataValue = kvp.Value is ICloneable - ? ((ICloneable)kvp.Value).Clone() - : kvp.Value; - clonedNode.SetMetadata(kvp.Key, metadataValue); - } - } - - if (_id != null) - { - clonedNode._id = _id; - } - - return clonedNode; - } - - public TreeNode Append(TValue value, object id = null) - { - var node = new TreeNode(value); - node._id = id; - this.Append(node); - return node; - } - - public void AppendRange(IEnumerable values) - { - values.Each(x => Append(x)); - } - - public void AppendRange(IEnumerable values, Func idSelector) - { - Guard.NotNull(idSelector, nameof(idSelector)); - - values.Each(x => Append(x, idSelector(x))); - } - - public TreeNode Prepend(TValue value, object id = null) - { - var node = new TreeNode(value); - node._id = id; - this.Prepend(node); - return node; - } - - public IEnumerable Flatten(bool includeSelf = true) - { - return this.Flatten(null, includeSelf); - } - - public IEnumerable Flatten(Expression> expression, bool includeSelf = true) - { - IEnumerable list; - if (includeSelf) - { - list = new[] { this.Value }; - } - else - { - list = Enumerable.Empty(); - } - - if (!HasChildren) - return list; - - var result = list.Union(Children.SelectMany(x => x.Flatten())); - if (expression != null) - { - result = result.Where(expression.Compile()); - } - - return result; - } - } - - public class TreeNodeConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - var canConvert = objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(TreeNode<>); - return canConvert; - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - var valueType = objectType.GetGenericArguments()[0]; - var sequenceType = typeof(List<>).MakeGenericType(objectType); - - object objValue = null; - object objChildren = null; - string id = null; - Dictionary metadata = null; - - reader.Read(); - while (reader.TokenType == JsonToken.PropertyName) - { - string a = reader.Value.ToString(); - if (string.Equals(a, "Value", StringComparison.OrdinalIgnoreCase)) - { - reader.Read(); - objValue = serializer.Deserialize(reader, valueType); - } - else if (string.Equals(a, "Metadata", StringComparison.OrdinalIgnoreCase)) - { - reader.Read(); - metadata = serializer.Deserialize>(reader); - } - else if (string.Equals(a, "Children", StringComparison.OrdinalIgnoreCase)) - { - reader.Read(); - objChildren = serializer.Deserialize(reader, sequenceType); - } - else if (string.Equals(a, "Id", StringComparison.OrdinalIgnoreCase)) - { - reader.Read(); - id = serializer.Deserialize(reader); - } - else - { - reader.Skip(); - } - - reader.Read(); - } - - var ctorParams = objChildren != null - ? new object[] { objValue, objChildren } - : new object[] { objValue }; - - var treeNode = Activator.CreateInstance(objectType, ctorParams); - - // Set Metadata - if (metadata != null && metadata.Count > 0) - { - var metadataProp = FastProperty.GetProperty(objectType, "Metadata", PropertyCachingStrategy.Cached); - metadataProp.SetValue(treeNode, metadata); - } - - // Set Id - if (id.HasValue()) - { - var idProp = FastProperty.GetProperty(objectType, "Id", PropertyCachingStrategy.Cached); - idProp.SetValue(treeNode, id); - } - - return treeNode; - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - writer.WriteStartObject(); - { - // Id - if (GetPropValue("Id", value) is object o) - { - writer.WritePropertyName("Id"); - serializer.Serialize(writer, o); - } - - // Value - writer.WritePropertyName("Value"); - serializer.Serialize(writer, GetPropValue("Value", value)); - - // Metadata - if (GetPropValue("Metadata", value) is IDictionary dict && dict.Count > 0) - { - writer.WritePropertyName("Metadata"); - - var typeNameHandling = serializer.TypeNameHandling; - try - { - serializer.TypeNameHandling = TypeNameHandling.All; - serializer.Serialize(writer, dict); - } - finally - { - serializer.TypeNameHandling = typeNameHandling; - } - } - - // Children - if (GetPropValue("HasChildren", value) is bool b && b == true) - { - writer.WritePropertyName("Children"); - serializer.Serialize(writer, GetPropValue("Children", value)); - } - } - writer.WriteEndObject(); - } - - private object GetPropValue(string name, object instance) - { - return FastProperty.GetProperty(instance.GetType(), name, PropertyCachingStrategy.Cached).GetValue(instance); - } - } + [JsonConverter(typeof(TreeNodeConverter))] + public class TreeNode : TreeNodeBase> + { + public TreeNode(TValue value) + { + Guard.NotNull(value, nameof(value)); + + Value = value; + } + + public TreeNode(TValue value, IEnumerable children) + : this(value) + { + if (children != null && children.Any()) + { + AppendRange(children); + } + } + + public TreeNode(TValue value, IEnumerable> children) + : this(value) + { + // for serialization + if (children != null && children.Any()) + { + AppendRange(children); + } + } + + public TValue Value + { + get; + private set; + } + + protected override TreeNode CreateInstance() + { + TValue value = this.Value; + + if (value is ICloneable) + { + value = ((ICloneable)value).Clone(); + } + + var clonedNode = new TreeNode(value); + + // Assign or clone Metadata + if (_metadata != null && _metadata.Count > 0) + { + foreach (var kvp in _metadata) + { + var metadataValue = kvp.Value is ICloneable + ? ((ICloneable)kvp.Value).Clone() + : kvp.Value; + clonedNode.SetMetadata(kvp.Key, metadataValue); + } + } + + if (_id != null) + { + clonedNode._id = _id; + } + + return clonedNode; + } + + public TreeNode Append(TValue value, object id = null) + { + var node = new TreeNode(value); + node._id = id; + this.Append(node); + return node; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AppendRange(IEnumerable values) + { + values.Each(x => Append(x)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AppendRange(IEnumerable values, Func idSelector) + { + Guard.NotNull(idSelector, nameof(idSelector)); + + values.Each(x => Append(x, idSelector(x))); + } + + public TreeNode Prepend(TValue value, object id = null) + { + var node = new TreeNode(value); + node._id = id; + this.Prepend(node); + return node; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IEnumerable Flatten(bool includeSelf = true) + { + return this.Flatten(null, includeSelf); + } + + public IEnumerable Flatten(Func predicate, bool includeSelf = true) + { + IEnumerable list; + if (includeSelf) + { + list = new[] { this.Value }; + } + else + { + list = Enumerable.Empty(); + } + + if (!HasChildren) + return list; + + var result = list.Union(Children.SelectMany(x => x.Flatten())); + if (predicate != null) + { + result = result.Where(predicate); + } + + return result; + } + } + + public class TreeNodeConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + var canConvert = objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(TreeNode<>); + return canConvert; + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + var valueType = objectType.GetGenericArguments()[0]; + var sequenceType = typeof(List<>).MakeGenericType(objectType); + + object objValue = null; + object objChildren = null; + string id = null; + Dictionary metadata = null; + + reader.Read(); + while (reader.TokenType == JsonToken.PropertyName) + { + string a = reader.Value.ToString(); + if (string.Equals(a, "Value", StringComparison.OrdinalIgnoreCase)) + { + reader.Read(); + objValue = serializer.Deserialize(reader, valueType); + } + else if (string.Equals(a, "Metadata", StringComparison.OrdinalIgnoreCase)) + { + reader.Read(); + metadata = serializer.Deserialize>(reader); + } + else if (string.Equals(a, "Children", StringComparison.OrdinalIgnoreCase)) + { + reader.Read(); + objChildren = serializer.Deserialize(reader, sequenceType); + } + else if (string.Equals(a, "Id", StringComparison.OrdinalIgnoreCase)) + { + reader.Read(); + id = serializer.Deserialize(reader); + } + else + { + reader.Skip(); + } + + reader.Read(); + } + + var ctorParams = objChildren != null + ? new object[] { objValue, objChildren } + : new object[] { objValue }; + + var treeNode = Activator.CreateInstance(objectType, ctorParams); + + // Set Metadata + if (metadata != null && metadata.Count > 0) + { + var metadataProp = FastProperty.GetProperty(objectType, "Metadata", PropertyCachingStrategy.Cached); + metadataProp.SetValue(treeNode, metadata); + } + + // Set Id + if (id.HasValue()) + { + var idProp = FastProperty.GetProperty(objectType, "Id", PropertyCachingStrategy.Cached); + idProp.SetValue(treeNode, id); + } + + return treeNode; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteStartObject(); + { + // Id + if (GetPropValue("Id", value) is object o) + { + writer.WritePropertyName("Id"); + serializer.Serialize(writer, o); + } + + // Value + writer.WritePropertyName("Value"); + serializer.Serialize(writer, GetPropValue("Value", value)); + + // Metadata + if (GetPropValue("Metadata", value) is IDictionary dict && dict.Count > 0) + { + writer.WritePropertyName("Metadata"); + + var typeNameHandling = serializer.TypeNameHandling; + try + { + serializer.TypeNameHandling = TypeNameHandling.All; + serializer.Serialize(writer, dict); + } + finally + { + serializer.TypeNameHandling = typeNameHandling; + } + } + + // Children + if (GetPropValue("HasChildren", value) is bool b && b == true) + { + writer.WritePropertyName("Children"); + serializer.Serialize(writer, GetPropValue("Children", value)); + } + } + writer.WriteEndObject(); + } + + private object GetPropValue(string name, object instance) + { + return FastProperty.GetProperty(instance.GetType(), name, PropertyCachingStrategy.Cached).GetValue(instance); + } + } } diff --git a/src/Libraries/SmartStore.Core/Collections/TreeNodeBase.cs b/src/Libraries/SmartStore.Core/Collections/TreeNodeBase.cs index 8264db12e1..64b8570a4e 100644 --- a/src/Libraries/SmartStore.Core/Collections/TreeNodeBase.cs +++ b/src/Libraries/SmartStore.Core/Collections/TreeNodeBase.cs @@ -1,143 +1,223 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; +using System.Runtime.CompilerServices; using Newtonsoft.Json; using SmartStore.Core.Infrastructure; namespace SmartStore.Collections { - public abstract class TreeNodeBase where T : TreeNodeBase - { - private T _parent; - private List _children = new List(); - private int? _depth = null; - private int _index = -1; - - protected object _id; - private IDictionary> _idNodeMap; - - protected IDictionary _metadata; - private readonly static ContextState> _contextState = new ContextState>("TreeNodeBase.ThreadMetadata"); - - public TreeNodeBase() - { - } - - #region Id - - public object Id - { - get - { - return _id; - } - set - { - _id = value; - - if (_parent != null) - { - var map = GetIdNodeMap(); - - if (_id != null && map.ContainsKey(_id)) - { - // Remove old id from map - map.Remove(_id); - } - - if (value != null) - { - map[value] = this; - } - } - } - } - - public T SelectNodeById(object id) - { - if (id == null || IsLeaf) - return null; - - var map = GetIdNodeMap(); - var node = (T)map?.Get(id); - - if (node != null && !this.IsAncestorOfOrSelf(node)) - { - // Found node is NOT a child of this node - return null; - } - - return node; - } - - private IDictionary> GetIdNodeMap() - { - var map = this.Root._idNodeMap; - if (map == null) - { - map = this.Root._idNodeMap = new Dictionary>(); - } - - return map; - } - - #endregion - - #region Metadata - - public IDictionary Metadata - { - get - { - return _metadata ?? (_metadata = new Dictionary()); - } - set - { - _metadata = value; - } - } - - public void SetMetadata(string key, object value) - { - Guard.NotEmpty(key, nameof(key)); - - Metadata[key] = value; - } - - public void SetThreadMetadata(string key, object value) - { - Guard.NotEmpty(key, nameof(key)); - - var state = _contextState.GetState(); - if (state == null) - { - state = new Dictionary(); - _contextState.SetState(state); - } - - state[GetContextKey(this, key)] = value; - } - - public TMetadata GetMetadata(string key, bool recursive = true) - { - Guard.NotEmpty(key, nameof(key)); - - object metadata; - - if (!recursive) - { - return TryGetMetadataForNode(this, key, out metadata) ? (TMetadata)metadata : default(TMetadata); - } - - // recursively search for the metadata value in current node and ancestors - var current = this; - while (!TryGetMetadataForNode(current, key, out metadata)) - { - current = current.Parent; - if (current == null) - break; - } + public abstract class TreeNodeBase where T : TreeNodeBase + { + private T _parent; + private List _children = new List(); + private int? _depth = null; + private int _index = -1; + + protected object _id; + private IDictionary> _idNodeMap; + + protected IDictionary _metadata; + private readonly static ContextState> _contextState = new ContextState>("TreeNodeBase.ThreadMetadata"); + + public TreeNodeBase() + { + } + + #region Id + + private void PropagateNodeId(object value /* Id */, IDictionary> idNodeMap) + { + if (value != null) + { + // We support multi-keys for single nodes + var ids = value as IEnumerable ?? new object[] { value }; + foreach (var id in ids) + { + idNodeMap[id] = this; + } + } + } + + private void RemoveNodeId(object value /* Id */, IDictionary> idNodeMap) + { + if (value != null) + { + // We support multi-keys for single nodes + var ids = value as IEnumerable ?? new object[] { value }; + foreach (var id in ids) + { + if (idNodeMap.ContainsKey(id)) + { + idNodeMap.Remove(id); + } + } + } + } + + /// + /// Responsible for propagating node ids when detaching/attaching nodes + /// + private void FixIdNodeMap(T prevParent, T newParent) + { + ICollection> keyedNodes = null; + + if (prevParent != null) + { + // A node is moved. We need to detach first. + keyedNodes = new List>(); + + // Detach ids from prev map + var prevMap = prevParent.GetIdNodeMap(); + + Traverse(x => + { + // Collect all child node's ids + if (x._id != null) + { + keyedNodes.Add(x); + // Remove from map + RemoveNodeId(x._id, prevMap); + } + }, true); + } + + if (keyedNodes == null && _idNodeMap != null) + { + // An orphan/root node is attached + keyedNodes = _idNodeMap.Values; + } + + if (newParent != null) + { + // Get new *root map + var map = newParent.GetIdNodeMap(); + + // Merge *this map with *root map + if (keyedNodes != null) + { + foreach (var node in keyedNodes) + { + node.PropagateNodeId(node._id, map); + } + + // Get rid of *this map after memorizing keyed nodes + if (_idNodeMap != null) + { + _idNodeMap.Clear(); + _idNodeMap = null; + } + } + + if (prevParent == null && _id != null) + { + // When *this was a root, but is keyed, then *this id + // was most likely missing in the prev id-node-map. + PropagateNodeId(_id, map); + } + } + } + + public object Id + { + get => _id; + set + { + var prevId = _id; + _id = value; + + if (_parent != null) + { + var map = GetIdNodeMap(); + + // Remove old id(s) from map + RemoveNodeId(prevId, map); + + // Set id + PropagateNodeId(value, map); + } + } + } + + public T SelectNodeById(object id) + { + if (id == null || IsLeaf) + return null; + + var map = GetIdNodeMap(); + var node = (T)map?.Get(id); + + if (node != null && !this.IsAncestorOfOrSelf(node)) + { + // Found node is NOT a child of this node + return null; + } + + return node; + } + + private IDictionary> GetIdNodeMap() + { + var map = this.Root._idNodeMap; + if (map == null) + { + map = this.Root._idNodeMap = new Dictionary>(); + } + + return map; + } + + #endregion + + #region Metadata + + public IDictionary Metadata + { + get => _metadata ?? (_metadata = new Dictionary()); + set => _metadata = value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetMetadata(string key, object value) + { + Guard.NotEmpty(key, nameof(key)); + + Metadata[key] = value; + } + + public void SetThreadMetadata(string key, object value) + { + Guard.NotEmpty(key, nameof(key)); + + var state = _contextState.GetState(); + if (state == null) + { + state = new Dictionary(); + _contextState.SetState(state); + } + + state[GetContextKey(this, key)] = value; + } + + public TMetadata GetMetadata(string key, bool recursive = true) + { + Guard.NotEmpty(key, nameof(key)); + + object metadata; + + if (!recursive) + { + return TryGetMetadataForNode(this, key, out metadata) ? (TMetadata)metadata : default(TMetadata); + } + + // recursively search for the metadata value in current node and ancestors + var current = this; + while (!TryGetMetadataForNode(current, key, out metadata)) + { + current = current.Parent; + if (current == null) + break; + } if (metadata != null) { @@ -145,623 +225,476 @@ public TMetadata GetMetadata(string key, bool recursive = true) } return default(TMetadata); - } - - private bool TryGetMetadataForNode(TreeNodeBase node, string key, out object metadata) - { - metadata = null; - - var state = _contextState.GetState(); - - if (state != null) - { - var contextKey = GetContextKey(node, key); - if (state.ContainsKey(contextKey)) - { - metadata = state[contextKey]; - return true; - } - } - - if (node._metadata != null && node._metadata.ContainsKey(key)) - { - metadata = node._metadata[key]; - return true; - } - - return false; - } - - private static string GetContextKey(TreeNodeBase node, string key) - { - return node.GetHashCode().ToString() + key; - } - - #endregion - - private List ChildrenInternal - { - get - { - if (_children == null) - { - _children = new List(); - } - return _children; - } - } - - private void AddChild(T node, bool clone, bool append = true) - { - var newNode = node; - if (clone) - { - newNode = node.Clone(true); - } - newNode.AttachTo((T)this, append ? (int?)null : 0); - } - - private void AttachTo(T newParent, int? index) - { - Guard.NotNull(newParent, nameof(newParent)); - - var prevParent = _parent; - - if (_parent != null) - { - // Detach from parent - _parent.Remove((T)this); - } - - if (index == null) - { - newParent.ChildrenInternal.Add((T)this); - _index = newParent.ChildrenInternal.Count - 1; - } - else - { - newParent.ChildrenInternal.Insert(index.Value, (T)this); - _index = index.Value; - FixIndexes(newParent._children, _index + 1, 1); - } - - _parent = newParent; - - FixIdNodeMap(prevParent, newParent); - } - - /// - /// Responsible for propagating node ids when detaching/attaching nodes - /// - private void FixIdNodeMap(T prevParent, T newParent) - { - ICollection> keyedNodes = null; - - if (prevParent != null) - { - // A node is moved. We need to detach first. - keyedNodes = new List>(); - - // Detach ids from prev map - var prevMap = prevParent.GetIdNodeMap(); - - Traverse(x => - { - // Collect all child node's ids - if (x._id != null) - { - keyedNodes.Add(x); - if (prevMap.ContainsKey(x._id)) - { - // Remove from map - prevMap.Remove(x._id); - } - } - }, true); - } - - if (keyedNodes == null && _idNodeMap != null) - { - // An orphan/root node is attached - keyedNodes = _idNodeMap.Values; - } - - if (newParent != null) - { - // Get new *root map - var map = newParent.GetIdNodeMap(); - - // Merge *this map with *root map - if (keyedNodes != null) - { - foreach (var node in keyedNodes) - { - map[node._id] = node; - } - - // Get rid of *this map after memorizing keyed nodes - if (_idNodeMap != null) - { - _idNodeMap.Clear(); - _idNodeMap = null; - } - } - - if (prevParent == null && _id != null) - { - // When *this was a root, but is keyed, then *this id - // was most likely missing in the prev id-node-map. - map[_id] = (T)this; - } - } - } - - [JsonIgnore] - public T Parent - { - get - { - return _parent; - } - } - - public T this[int i] - { - get - { - return _children?[i]; - } - } - - public IReadOnlyList Children - { - get - { - return ChildrenInternal; - } - } - - [JsonIgnore] - public IEnumerable LeafNodes - { - get - { - return _children != null - ? _children.Where(x => x.IsLeaf) - : Enumerable.Empty(); - } - } - - [JsonIgnore] - public IEnumerable NonLeafNodes - { - get - { - return _children != null - ? _children.Where(x => !x.IsLeaf) - : Enumerable.Empty(); - } - } - - - [JsonIgnore] - public T FirstChild - { - get - { - return _children?.FirstOrDefault(); - } - } - - [JsonIgnore] - public T LastChild - { - get - { - return _children?.LastOrDefault(); - } - } - - [JsonIgnore] - public bool IsLeaf - { - get - { - return _children == null || _children.Count == 0; - } - } - - [JsonIgnore] - public bool HasChildren - { - get - { - return _children == null || _children.Count > 0; - } - } - - [JsonIgnore] - public bool IsRoot - { - get - { - return _parent == null; - } - } - - [JsonIgnore] - public int Index - { - get - { - return _index; - } - } - - /// - /// Root starts with 0 - /// - [JsonIgnore] - public int Depth - { - get - { - if (!_depth.HasValue) - { - var node = this; - int depth = 0; - while (node != null && !node.IsRoot) - { - depth++; - node = node.Parent; - } - _depth = depth; - } - - return _depth.Value; - } - } - - [JsonIgnore] - public T Root - { - get - { - var root = this; - while (root._parent != null) - { - root = root._parent; - } - return (T)root; - } - } - - [JsonIgnore] - public T First - { - get - { - return _parent?._children?.FirstOrDefault(); - } - } - - [JsonIgnore] - public T Last - { - get - { - return _parent?._children?.LastOrDefault(); - } - } - - [JsonIgnore] - public T Next - { - get - { - return _parent?._children?.ElementAtOrDefault(_index + 1); - } - } - - [JsonIgnore] - public T Previous - { - get - { - return _parent?._children?.ElementAtOrDefault(_index - 1); - } - } - - public bool IsDescendantOf(T node) - { - var parent = _parent; - while (parent != null) - { - if (parent == node) - { - return true; - } - parent = parent._parent; - } - - return false; - } - - public bool IsDescendantOfOrSelf(T node) - { - if (node == (T)this) - return true; - - return IsDescendantOf(node); - } - - public bool IsAncestorOf(T node) - { - return node.IsDescendantOf((T)this); - } - - public bool IsAncestorOfOrSelf(T node) - { - if (node == (T)this) - return true; - - return node.IsDescendantOf((T)this); - } - - [JsonIgnore] - public IEnumerable Trail - { - get - { - var trail = new List(); - - var node = (T)this; - do - { - trail.Insert(0, node); - node = node._parent; - } while (node != null); - - return trail; - } - } - - /// - /// Gets the first element that matches the predicate by testing the node itself - /// and traversing up through its ancestors in the tree. - /// - /// predicate - /// The closest node - public T Closest(Expression> predicate) - { - Guard.NotNull(predicate, nameof(predicate)); - - var test = predicate.Compile(); - - if (test((T)this)) - { - return (T)this; - } - - var parent = _parent; - - while (parent != null) - { - if (test(parent)) - { - return parent; - } - parent = parent._parent; - } - - return null; - } - - public T Append(T value) - { - this.AddChild(value, false, true); - return value; - } - - public void AppendRange(IEnumerable values) - { - values.Each(x => Append(x)); - } - - public void AppendChildrenOf(T node) - { - if (node?._children != null) - { - node._children.Each(x => this.AddChild(x, true, true)); - } - } - - public T Prepend(T value) - { - this.AddChild(value, false, false); - return value; - } - - public void InsertAfter(int index) - { - var refNode = _children?.ElementAtOrDefault(index); - if (refNode != null) - { - InsertAfter(refNode); - } - - throw new ArgumentOutOfRangeException(nameof(index)); - } - - public void InsertAfter(T refNode) - { - this.Insert(refNode, true); - } - - public void InsertBefore(int index) - { - var refNode = _children?.ElementAtOrDefault(index); - if (refNode != null) - { - InsertBefore(refNode); - } - - throw new ArgumentOutOfRangeException(nameof(index)); - } - - public void InsertBefore(T refNode) - { - this.Insert(refNode, false); - } - - private void Insert(T refNode, bool after) - { - Guard.NotNull(refNode, nameof(refNode)); - - var refParent = refNode._parent; - if (refParent == null) - { - throw Error.Argument("refNode", "The reference node cannot be a root node and must be attached to the tree."); - } - - AttachTo(refParent, refNode._index + (after ? 1 : 0)); - } - - public T SelectNode(Expression> predicate, bool includeSelf = false) - { - Guard.NotNull(predicate, nameof(predicate)); - - return this.FlattenNodes(predicate, includeSelf).FirstOrDefault(); - } - - /// - /// Selects all nodes (recursively) witch match the given predicate - /// - /// The predicate to match against - /// A readonly collection of node matches - public IEnumerable SelectNodes(Expression> predicate, bool includeSelf = false) - { - Guard.NotNull(predicate, nameof(predicate)); - - return this.FlattenNodes(predicate, includeSelf); - } - - private void FixIndexes(IList list, int startIndex, int summand = 1) - { - if (startIndex < 0 || startIndex >= list.Count) - return; - - for (var i = startIndex; i < list.Count; i++) - { - list[i]._index += summand; - } - } - - public void Remove(T node) - { - Guard.NotNull(node, nameof(node)); - - if (!node.IsRoot) - { - var list = node._parent?._children; - if (list.Remove(node)) - { - node.FixIdNodeMap(node._parent, null); - - FixIndexes(list, node._index, -1); - - node._index = -1; - node._parent = null; - node.Traverse(x => x._depth = null, true); - } - } - } - - public void Clear() - { - Traverse(x => x._depth = null, false); - if (_children != null) - { - _children.Clear(); - } - - FixIdNodeMap(_parent, null); - } - - public void Traverse(Action action, bool includeSelf = false) - { - Guard.NotNull(action, nameof(action)); - - if (includeSelf) - action((T)this); - - if (_children != null) - { - foreach (var child in _children) - child.Traverse(action, true); - } - } - - public void TraverseParents(Action action, bool includeSelf = false) - { - Guard.NotNull(action, nameof(action)); - - if (includeSelf) - action((T)this); - - var parent = _parent; - - while (parent != null) - { - action(parent); - parent = parent._parent; - } - } - - public IEnumerable FlattenNodes(bool includeSelf = true) - { - return this.FlattenNodes(null, includeSelf); - } - - protected IEnumerable FlattenNodes(Expression> predicate, bool includeSelf = true) - { - IEnumerable list; - if (includeSelf) - { - list = new[] { (T)this }; - } - else - { - list = Enumerable.Empty(); - } - - if (_children == null) - return list; - - var result = list.Union(_children.SelectMany(x => x.FlattenNodes())); - if (predicate != null) - { - result = result.Where(predicate.Compile()); - } - - return result; - } - - public T Clone() - { - return Clone(true); - } - - public virtual T Clone(bool deep) - { - var clone = CreateInstance(); - if (deep) - { - clone.AppendChildrenOf((T)this); - } - return clone; - } - - protected abstract T CreateInstance(); - } + } + + private bool TryGetMetadataForNode(TreeNodeBase node, string key, out object metadata) + { + metadata = null; + + var state = _contextState.GetState(); + + if (state != null) + { + var contextKey = GetContextKey(node, key); + if (state.ContainsKey(contextKey)) + { + metadata = state[contextKey]; + return true; + } + } + + if (node._metadata != null && node._metadata.ContainsKey(key)) + { + metadata = node._metadata[key]; + return true; + } + + return false; + } + + private static string GetContextKey(TreeNodeBase node, string key) + { + return node.GetHashCode().ToString() + key; + } + + #endregion + + private List ChildrenInternal + { + get + { + if (_children == null) + { + _children = new List(); + } + return _children; + } + } + + private void AddChild(T node, bool clone, bool append = true) + { + var newNode = node; + if (clone) + { + newNode = node.Clone(true); + } + newNode.AttachTo((T)this, append ? (int?)null : 0); + } + + private void AttachTo(T newParent, int? index) + { + Guard.NotNull(newParent, nameof(newParent)); + + var prevParent = _parent; + + if (_parent != null) + { + // Detach from parent + _parent.Remove((T)this); + } + + if (index == null) + { + newParent.ChildrenInternal.Add((T)this); + _index = newParent.ChildrenInternal.Count - 1; + } + else + { + newParent.ChildrenInternal.Insert(index.Value, (T)this); + _index = index.Value; + FixIndexes(newParent._children, _index + 1, 1); + } + + _parent = newParent; + + FixIdNodeMap(prevParent, newParent); + } + + [JsonIgnore] + public T Parent => _parent; + + public T this[int i] => _children?[i]; + + public IReadOnlyList Children => ChildrenInternal; + + [JsonIgnore] + public IEnumerable LeafNodes => _children != null + ? _children.Where(x => x.IsLeaf) + : Enumerable.Empty(); + + [JsonIgnore] + public IEnumerable NonLeafNodes => _children != null + ? _children.Where(x => !x.IsLeaf) + : Enumerable.Empty(); + + + [JsonIgnore] + public T FirstChild => _children?.FirstOrDefault(); + + [JsonIgnore] + public T LastChild => _children?.LastOrDefault(); + + [JsonIgnore] + public bool IsLeaf => _children == null || _children.Count == 0; + + [JsonIgnore] + public bool HasChildren => _children == null || _children.Count > 0; + + [JsonIgnore] + public bool IsRoot => _parent == null; + + [JsonIgnore] + public int Index => _index; + + /// + /// Root starts with 0 + /// + [JsonIgnore] + public int Depth + { + get + { + if (!_depth.HasValue) + { + var node = this; + int depth = 0; + while (node != null && !node.IsRoot) + { + depth++; + node = node.Parent; + } + _depth = depth; + } + + return _depth.Value; + } + } + + [JsonIgnore] + public T Root + { + get + { + var root = this; + while (root._parent != null) + { + root = root._parent; + } + return (T)root; + } + } + + [JsonIgnore] + public T First => _parent?._children?.FirstOrDefault(); + + [JsonIgnore] + public T Last => _parent?._children?.LastOrDefault(); + + [JsonIgnore] + public T Next => _parent?._children?.ElementAtOrDefault(_index + 1); + + [JsonIgnore] + public T Previous => _parent?._children?.ElementAtOrDefault(_index - 1); + + public bool IsDescendantOf(T node) + { + var parent = _parent; + while (parent != null) + { + if (parent == node) + { + return true; + } + parent = parent._parent; + } + + return false; + } + + public bool IsDescendantOfOrSelf(T node) + { + if (node == (T)this) + return true; + + return IsDescendantOf(node); + } + + public bool IsAncestorOf(T node) + { + return node.IsDescendantOf((T)this); + } + + public bool IsAncestorOfOrSelf(T node) + { + if (node == (T)this) + return true; + + return node.IsDescendantOf((T)this); + } + + [JsonIgnore] + public IEnumerable Trail + { + get + { + var trail = new List(); + + var node = (T)this; + do + { + trail.Insert(0, node); + node = node._parent; + } while (node != null); + + return trail; + } + } + + /// + /// Gets the first element that matches the predicate by testing the node itself + /// and traversing up through its ancestors in the tree. + /// + /// predicate + /// The closest node + public T Closest(Func predicate) + { + Guard.NotNull(predicate, nameof(predicate)); + + var test = predicate; + + if (test((T)this)) + { + return (T)this; + } + + var parent = _parent; + + while (parent != null) + { + if (test(parent)) + { + return parent; + } + parent = parent._parent; + } + + return null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Append(T value) + { + this.AddChild(value, false, true); + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AppendRange(IEnumerable values) + { + values.Each(x => Append(x)); + } + + public void AppendChildrenOf(T node) + { + if (node?._children != null) + { + node._children.Each(x => this.AddChild(x, true, true)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Prepend(T value) + { + this.AddChild(value, false, false); + return value; + } + + public void InsertAfter(int index) + { + var refNode = _children?.ElementAtOrDefault(index); + if (refNode != null) + { + InsertAfter(refNode); + } + + throw new ArgumentOutOfRangeException(nameof(index)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void InsertAfter(T refNode) + { + this.Insert(refNode, true); + } + + public void InsertBefore(int index) + { + var refNode = _children?.ElementAtOrDefault(index); + if (refNode != null) + { + InsertBefore(refNode); + } + + throw new ArgumentOutOfRangeException(nameof(index)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void InsertBefore(T refNode) + { + this.Insert(refNode, false); + } + + private void Insert(T refNode, bool after) + { + Guard.NotNull(refNode, nameof(refNode)); + + var refParent = refNode._parent; + if (refParent == null) + { + throw Error.Argument("refNode", "The reference node cannot be a root node and must be attached to the tree."); + } + + AttachTo(refParent, refNode._index + (after ? 1 : 0)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T SelectNode(Func predicate, bool includeSelf = false) + { + Guard.NotNull(predicate, nameof(predicate)); + + return this.FlattenNodes(predicate, includeSelf).FirstOrDefault(); + } + + /// + /// Selects all nodes (recursively) witch match the given predicate + /// + /// The predicate to match against + /// A readonly collection of node matches + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IEnumerable SelectNodes(Func predicate, bool includeSelf = false) + { + Guard.NotNull(predicate, nameof(predicate)); + + return this.FlattenNodes(predicate, includeSelf); + } + + private void FixIndexes(IList list, int startIndex, int summand = 1) + { + if (startIndex < 0 || startIndex >= list.Count) + return; + + for (var i = startIndex; i < list.Count; i++) + { + list[i]._index += summand; + } + } + + public void Remove(T node) + { + Guard.NotNull(node, nameof(node)); + + if (!node.IsRoot) + { + var list = node._parent?._children; + if (list.Remove(node)) + { + node.FixIdNodeMap(node._parent, null); + + FixIndexes(list, node._index, -1); + + node._index = -1; + node._parent = null; + node.Traverse(x => x._depth = null, true); + } + } + } + + public void Clear() + { + Traverse(x => x._depth = null, false); + if (_children != null) + { + _children.Clear(); + } + + FixIdNodeMap(_parent, null); + } + + public void Traverse(Action action, bool includeSelf = false) + { + Guard.NotNull(action, nameof(action)); + + if (includeSelf) + action((T)this); + + if (_children != null) + { + foreach (var child in _children) + child.Traverse(action, true); + } + } + + public void TraverseParents(Action action, bool includeSelf = false) + { + Guard.NotNull(action, nameof(action)); + + if (includeSelf) + action((T)this); + + var parent = _parent; + + while (parent != null) + { + action(parent); + parent = parent._parent; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public IEnumerable FlattenNodes(bool includeSelf = true) + { + return this.FlattenNodes(null, includeSelf); + } + + protected IEnumerable FlattenNodes(Func predicate, bool includeSelf = true) + { + IEnumerable list; + if (includeSelf) + { + list = new[] { (T)this }; + } + else + { + list = Enumerable.Empty(); + } + + if (_children == null) + return list; + + var result = list.Union(_children.SelectMany(x => x.FlattenNodes())); + if (predicate != null) + { + result = result.Where(predicate); + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Clone() + { + return Clone(true); + } + + public virtual T Clone(bool deep) + { + var clone = CreateInstance(); + if (deep) + { + clone.AppendChildrenOf((T)this); + } + return clone; + } + + protected abstract T CreateInstance(); + } } diff --git a/src/Libraries/SmartStore.Core/Collections/TrimmedBuffer.cs b/src/Libraries/SmartStore.Core/Collections/TrimmedBuffer.cs index db54bf8936..5a07192af5 100644 --- a/src/Libraries/SmartStore.Core/Collections/TrimmedBuffer.cs +++ b/src/Libraries/SmartStore.Core/Collections/TrimmedBuffer.cs @@ -4,103 +4,109 @@ namespace SmartStore.Collections { - public sealed class TrimmedBuffer : ICollection - { - private readonly int _maxSize; - private readonly List _list; - - public TrimmedBuffer(int maxSize) - : this((IEnumerable)null, default(T), maxSize) - { - } - - public TrimmedBuffer(IEnumerable collection, int maxSize) - : this(collection, default(T), maxSize) - { - } - - public TrimmedBuffer(string collection, int maxSize) - : this(collection.NullEmpty()?.Convert>()?.Distinct(), default(T), maxSize) - { - } - - public TrimmedBuffer(string collection, T newItem, int maxSize) - : this(collection.NullEmpty()?.Convert>()?.Distinct(), newItem, maxSize) - { - } - - public TrimmedBuffer(IEnumerable collection, T newItem, int maxSize) - { + public sealed class TrimmedBuffer : ICollection + { + private readonly int _maxSize; + private readonly List _list; + + public TrimmedBuffer(int maxSize) + : this((IEnumerable)null, default(T), maxSize) + { + } + + public TrimmedBuffer(IEnumerable collection, int maxSize) + : this(collection, default(T), maxSize) + { + } + + public TrimmedBuffer(string collection, int maxSize) + : this(collection.NullEmpty()?.Convert>()?.Distinct(), default(T), maxSize) + { + } + + public TrimmedBuffer(string collection, T newItem, int maxSize) + : this(collection.NullEmpty()?.Convert>()?.Distinct(), newItem, maxSize) + { + } + + public TrimmedBuffer(IEnumerable collection, T newItem, int maxSize) + { Guard.IsTrue(maxSize >= 0, nameof(maxSize)); - _maxSize = maxSize; - _list = new List(collection ?? Enumerable.Empty()); - - if (newItem != null) - { - Add(newItem); - } - - Trim(); - } - - private void Trim() - { - if (_maxSize >= 0) - { - while (_list.Count > _maxSize) - _list.RemoveAt(_list.Count - 1); - } - } - - public T this[int index] - { - get - { - return _list[index]; - } - set - { - _list[index] = value; - } - } - - public void Add(T item) - { - var i = _list.IndexOf(item); - if (i > -1) - _list.RemoveAt(i); - - _list.Insert(0, item); - - Trim(); - } - - public void Clear() => _list.Clear(); - - public bool Contains(T item) => _list.Contains(item); - - public void CopyTo(T[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex); - - public bool Remove(T item) => _list.Remove(item); - - public int Count => _list.Count; - - public bool IsReadOnly => false; - - public IEnumerator GetEnumerator() - { - return _list.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _list.GetEnumerator(); - } - - public override string ToString() - { - return string.Join(";", _list); - } - } + _maxSize = maxSize; + _list = new List(collection ?? Enumerable.Empty()); + + if (newItem != null) + { + Add(newItem); + } + + Trim(); + } + + private void Trim() + { + if (_maxSize >= 0) + { + while (_list.Count > _maxSize) + _list.RemoveAt(_list.Count - 1); + } + } + + public T this[int index] + { + get => _list[index]; + set => _list[index] = value; + } + + public void Add(T item) + { + var i = _list.IndexOf(item); + if (i > -1) + _list.RemoveAt(i); + + _list.Insert(0, item); + + Trim(); + } + + public void Clear() + { + _list.Clear(); + } + + public bool Contains(T item) + { + return _list.Contains(item); + } + + public void CopyTo(T[] array, int arrayIndex) + { + _list.CopyTo(array, arrayIndex); + } + + public bool Remove(T item) + { + return _list.Remove(item); + } + + public int Count => _list.Count; + + public bool IsReadOnly => false; + + public IEnumerator GetEnumerator() + { + return _list.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _list.GetEnumerator(); + } + + public override string ToString() + { + return string.Join(";", _list); + } + } } diff --git a/src/Libraries/SmartStore.Core/ComponentModel/FastActivator.cs b/src/Libraries/SmartStore.Core/ComponentModel/FastActivator.cs index 85419958d3..e82db5c515 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/FastActivator.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/FastActivator.cs @@ -1,204 +1,217 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Linq.Expressions; -using System.Collections.Concurrent; +using System.Reflection; namespace SmartStore.ComponentModel { - public class FastActivator - { - private static readonly ConcurrentDictionary _activatorsCache = new ConcurrentDictionary(); - - public FastActivator(ConstructorInfo constructorInfo) - { - Guard.NotNull(constructorInfo, nameof(constructorInfo)); - - Constructor = constructorInfo; - Invoker = MakeFastInvoker(constructorInfo); - ParameterTypes = constructorInfo.GetParameters().Select(p => p.ParameterType).ToArray(); - } - - /// - /// Gets the backing . - /// - public ConstructorInfo Constructor { get; private set; } - - /// - /// Gets the parameter types from the backing - /// - public Type[] ParameterTypes { get; private set; } - - /// - /// Gets the constructor invoker. - /// - public Func Invoker { get; private set; } - - /// - /// Creates an instance of the type using the specified parameters. - /// - /// A reference to the newly created object. - public object Activate(params object[] parameters) - { - return Invoker(parameters); - } - - #region Static - - /// - /// Creates a single fast constructor invoker. The result is not cached. - /// - /// constructorInfo to create invoker for. - /// a fast invoker. - public static Func MakeFastInvoker(ConstructorInfo constructorInfo) - { - var paramsInfo = constructorInfo.GetParameters(); - - var parametersExpression = Expression.Parameter(typeof(object[]), "args"); - var argumentsExpression = new Expression[paramsInfo.Length]; - - for (int paramIndex = 0; paramIndex < paramsInfo.Length; paramIndex++) - { - var indexExpression = Expression.Constant(paramIndex); - var parameterType = paramsInfo[paramIndex].ParameterType; - - var parameterIndexExpression = Expression.ArrayIndex(parametersExpression, indexExpression); - var convertExpression = Expression.Convert(parameterIndexExpression, parameterType); - argumentsExpression[paramIndex] = convertExpression; - - if (!parameterType.GetTypeInfo().IsValueType) - continue; - - var nullConditionExpression = Expression.Equal(parameterIndexExpression, Expression.Constant(null)); - argumentsExpression[paramIndex] = Expression.Condition(nullConditionExpression, Expression.Default(parameterType), convertExpression); - } - - var newExpression = Expression.New(constructorInfo, argumentsExpression); - var lambda = Expression.Lambda>(newExpression, parametersExpression); - - return lambda.Compile(); - } - - /// - /// Creates and caches fast constructor invokers - /// - /// The type to extract fast constructor invokers for - /// A cached array of all public instance constructors from the given type. - /// The parameterless default constructor is always excluded from the list of activators - public static FastActivator[] GetActivators(Type type) - { - return GetActivatorsCore(type); - } - - private static FastActivator[] GetActivatorsCore(Type type) - { - if (!_activatorsCache.TryGetValue(type, out FastActivator[] activators)) - { - var candidates = GetCandidateConstructors(type); - activators = candidates.Select(c => new FastActivator(c)).ToArray(); - _activatorsCache.TryAdd(type, activators); - } - - return activators; - } - - /// - /// Creates an instance of the specified type using the constructor that best matches the specified parameters. - /// - /// The type of object to create. - /// - /// An array of arguments that match in number, order, and type the parameters of the constructor to invoke. - /// If args is an empty array or null, the constructor that takes no parameters (the default constructor) is invoked. - /// - /// A reference to the newly created object. - public static T CreateInstance(params object[] args) - { - return (T)CreateInstance(typeof(T), args); - } - - /// - /// Creates an instance of the specified type using the constructor that best matches the specified parameters. - /// - /// The type of object to create. - /// - /// An array of arguments that match in number, order, and type the parameters of the constructor to invoke. - /// If args is an empty array or null, the constructor that takes no parameters (the default constructor) is invoked. - /// - /// A reference to the newly created object. - public static object CreateInstance(Type type, params object[] args) - { - Guard.NotNull(type, nameof(type)); - - if (args == null || args.Length == 0) - { - // don't struggle with FastActivator: native reflection is really fast with default constructor! - return Activator.CreateInstance(type); - } - - var activators = GetActivatorsCore(type); - var matchingActivator = FindMatchingActivatorCore(activators, type, args); - - if (matchingActivator == null) - { - throw new ArgumentException("No matching contructor was found for the given arguments.", "args"); - } - - return matchingActivator.Activate(args); - } - - public static FastActivator FindMatchingActivator(Type type, params object[] args) - { - var activators = GetActivatorsCore(type); - var matchingActivator = FindMatchingActivatorCore(activators, type, args); - - return matchingActivator; - } - - private static FastActivator FindMatchingActivatorCore(FastActivator[] activators, Type type, object[] args) - { - if (activators.Length == 0) - { - return null; - } - + public class FastActivator + { + private static readonly ConcurrentDictionary _activatorsCache = new ConcurrentDictionary(); + + private Func _invoker; + + public FastActivator(ConstructorInfo constructorInfo) + { + Guard.NotNull(constructorInfo, nameof(constructorInfo)); + + Constructor = constructorInfo; + ParameterTypes = constructorInfo.GetParameters().Select(p => p.ParameterType).ToArray(); + } + + /// + /// Gets the backing . + /// + public ConstructorInfo Constructor { get; private set; } + + /// + /// Gets the parameter types from the backing + /// + public Type[] ParameterTypes { get; private set; } + + /// + /// Gets the constructor invoker. + /// + public Func Invoker + { + get + { + if (_invoker == null) + { + _invoker = MakeFastInvoker(Constructor); + } + + return _invoker; + } + } + + /// + /// Creates an instance of the type using the specified parameters. + /// + /// A reference to the newly created object. + public object Activate(params object[] parameters) + { + return Invoker(parameters); + } + + #region Static + + /// + /// Creates a single fast constructor invoker. The result is not cached. + /// + /// constructorInfo to create invoker for. + /// a fast invoker. + public static Func MakeFastInvoker(ConstructorInfo constructorInfo) + { + var paramsInfo = constructorInfo.GetParameters(); + + var parametersExpression = Expression.Parameter(typeof(object[]), "args"); + var argumentsExpression = new Expression[paramsInfo.Length]; + + for (int paramIndex = 0; paramIndex < paramsInfo.Length; paramIndex++) + { + var indexExpression = Expression.Constant(paramIndex); + var parameterType = paramsInfo[paramIndex].ParameterType; + + var parameterIndexExpression = Expression.ArrayIndex(parametersExpression, indexExpression); + var convertExpression = Expression.Convert(parameterIndexExpression, parameterType); + argumentsExpression[paramIndex] = convertExpression; + + if (!parameterType.GetTypeInfo().IsValueType) + continue; + + var nullConditionExpression = Expression.Equal(parameterIndexExpression, Expression.Constant(null)); + argumentsExpression[paramIndex] = Expression.Condition(nullConditionExpression, Expression.Default(parameterType), convertExpression); + } + + var newExpression = Expression.New(constructorInfo, argumentsExpression); + var lambda = Expression.Lambda>(newExpression, parametersExpression); + + return lambda.Compile(); + } + + /// + /// Creates and caches fast constructor invokers + /// + /// The type to extract fast constructor invokers for + /// A cached array of all public instance constructors from the given type. + /// The parameterless default constructor is always excluded from the list of activators + public static FastActivator[] GetActivators(Type type) + { + return GetActivatorsCore(type); + } + + private static FastActivator[] GetActivatorsCore(Type type) + { + if (!_activatorsCache.TryGetValue(type, out FastActivator[] activators)) + { + var candidates = GetCandidateConstructors(type); + activators = candidates.Select(c => new FastActivator(c)).ToArray(); + _activatorsCache.TryAdd(type, activators); + } + + return activators; + } + + /// + /// Creates an instance of the specified type using the constructor that best matches the specified parameters. + /// + /// The type of object to create. + /// + /// An array of arguments that match in number, order, and type the parameters of the constructor to invoke. + /// If args is an empty array or null, the constructor that takes no parameters (the default constructor) is invoked. + /// + /// A reference to the newly created object. + public static T CreateInstance(params object[] args) + { + return (T)CreateInstance(typeof(T), args); + } + + /// + /// Creates an instance of the specified type using the constructor that best matches the specified parameters. + /// + /// The type of object to create. + /// + /// An array of arguments that match in number, order, and type the parameters of the constructor to invoke. + /// If args is an empty array or null, the constructor that takes no parameters (the default constructor) is invoked. + /// + /// A reference to the newly created object. + public static object CreateInstance(Type type, params object[] args) + { + Guard.NotNull(type, nameof(type)); + + if (args == null || args.Length == 0) + { + // don't struggle with FastActivator: native reflection is really fast with default constructor! + return Activator.CreateInstance(type); + } + + var activators = GetActivatorsCore(type); + var matchingActivator = FindMatchingActivatorCore(activators, type, args); + + if (matchingActivator == null) + { + throw new ArgumentException("No matching contructor was found for the given arguments.", "args"); + } + + return matchingActivator.Activate(args); + } + + public static FastActivator FindMatchingActivator(Type type, params object[] args) + { + var activators = GetActivatorsCore(type); + var matchingActivator = FindMatchingActivatorCore(activators, type, args); + + return matchingActivator; + } + + private static FastActivator FindMatchingActivatorCore(FastActivator[] activators, Type type, object[] args) + { + if (activators.Length == 0) + { + return null; + } + if (activators.Length == 1) - { - // this seems to be bad design, but it's on purpose for performance reasons. - // In nearly ALL cases there is only one constructor. - return activators[0]; - } - - var argTypes = args.Select(x => x.GetType()).ToArray(); - var constructor = type.GetConstructor( - BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, - null, - argTypes, - null); - - if (constructor != null) - { - var matchingActivator = activators.FirstOrDefault(a => a.Constructor == constructor); - return matchingActivator; - } - - return null; - } - - private static IEnumerable GetCandidateConstructors(Type type) - { - var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); - return constructors.Where(c => c.GetParameters().Length > 0); - } - - private static void CheckIsValidType(Type type) - { - if (type.IsAbstract || type.IsInterface) - { - throw new ArgumentException("The type to create activators for must be concrete. Type: {0}".FormatInvariant(type.ToString()), "type"); - } - } - - #endregion - } + { + // this seems to be bad design, but it's on purpose for performance reasons. + // In nearly ALL cases there is only one constructor. + return activators[0]; + } + + var argTypes = args.Select(x => x.GetType()).ToArray(); + var constructor = type.GetConstructor( + BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, + null, + argTypes, + null); + + if (constructor != null) + { + var matchingActivator = activators.FirstOrDefault(a => a.Constructor == constructor); + return matchingActivator; + } + + return null; + } + + private static IEnumerable GetCandidateConstructors(Type type) + { + var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + return constructors.Where(c => c.GetParameters().Length > 0); + + } + + private static void CheckIsValidType(Type type) + { + if (type.IsAbstract || type.IsInterface) + { + throw new ArgumentException("The type to create activators for must be concrete. Type: {0}".FormatInvariant(type.ToString()), "type"); + } + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/ComponentModel/FastInvoker.cs b/src/Libraries/SmartStore.Core/ComponentModel/FastInvoker.cs index ba43358e1a..961f5ca100 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/FastInvoker.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/FastInvoker.cs @@ -1,293 +1,305 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Reflection; using System.Linq.Expressions; -using System.Collections.Concurrent; +using System.Reflection; using SmartStore.Utilities; namespace SmartStore.ComponentModel { - public class FastInvoker - { - private static readonly ConcurrentDictionary _invokersCache = new ConcurrentDictionary(); - - public FastInvoker(MethodInfo methodInfo) - { - Guard.NotNull(methodInfo, nameof(methodInfo)); - - Method = methodInfo; - Invoker = MakeFastInvoker(methodInfo); - ParameterTypes = methodInfo.GetParameters().Select(p => p.ParameterType).ToArray(); - } - - /// - /// Gets the backing . - /// - public MethodInfo Method { get; private set; } - - /// - /// Gets the parameter types from the backing - /// - public Type[] ParameterTypes { get; private set; } - - /// - /// Gets the method invoker. - /// - public Func Invoker { get; private set; } - - /// - /// Invokes the method using the specified parameters. - /// - /// The method invocation result. - public object Invoke(object obj, params object[] parameters) - { - return Invoker(obj, parameters); - } - - #region Static - - /// - /// Creates a single fast method invoker. The result is not cached. - /// - /// Method to create invoker for. - /// The fast method invoker delegate. - public static Func MakeFastInvoker(MethodInfo method) - { - Guard.NotNull(method, nameof(method)); - - var instanceParameterExpression = Expression.Parameter(typeof(object), "instance"); - var argumentsParameterExpression = Expression.Parameter(typeof(object[]), "args"); - var index = 0; - - var argumentExtractionExpressions = - method - .GetParameters() - .Select(parameter => - Expression.Convert( - Expression.ArrayAccess( - argumentsParameterExpression, - Expression.Constant(index++) - ), - parameter.ParameterType - ) - ).ToList(); - - var callExpression = method.IsStatic - ? Expression.Call(method, argumentExtractionExpressions) - : Expression.Call( - Expression.Convert( - instanceParameterExpression, - method.DeclaringType - ), - method, - argumentExtractionExpressions - ); - - var endLabel = Expression.Label(typeof(object)); - - var finalExpression = method.ReturnType == typeof(void) - ? (Expression)Expression.Block( - callExpression, - Expression.Return(endLabel, Expression.Constant(null)), - Expression.Label(endLabel, Expression.Constant(null)) - ) - : Expression.Convert(callExpression, typeof(object)); - - var lambdaExpression = Expression.Lambda>( - finalExpression, - instanceParameterExpression, - argumentsParameterExpression - ); - - var lamdba = lambdaExpression.Compile(); - return lamdba; - } - - /// - /// Invokes a method using the specified object and parameter instances. - /// - /// The objectinstance - /// Method name - /// Argument types of the matching method overload (in exact order) - /// Parameter instances to pass to invocation - /// The method invocation result. - public static object Invoke(object obj, string methodName, Type[] parameterTypes, object[] parameters) - { - Guard.NotNull(obj, nameof(obj)); - - FastInvoker invoker; - - if (parameterTypes == null || parameterTypes.Length == 0) - { - invoker = GetInvoker(obj.GetType(), methodName); - } - else - { - invoker = GetInvoker(obj.GetType(), methodName, parameterTypes); - } - - //var hasAnyNullParam = parameters.Any(x => x == null); - //if (hasAnyNullParam) - //{ - // throw new ArgumentException("When invoking a method with parameter instances, no instance must be null.", nameof(parameters)); - //} - - return invoker.Invoke(obj, parameters ?? Array.Empty()); - } - - /// - /// Creates and caches a fast method invoker. - /// - /// Name of method to create an invoker for. - /// Argument types of method to create an invoker for. - /// The fast method invoker. - public static FastInvoker GetInvoker(string methodName, params Type[] argTypes) - { - return GetInvoker(typeof(T), methodName, argTypes); - } - - /// - /// Creates and caches a fast method invoker. - /// - /// The type to extract fast method invoker for. - /// Name of method to create an invoker for. - /// Argument types of method to create an invoker for. - /// The fast method invoker. - public static FastInvoker GetInvoker(Type type, string methodName, params Type[] argTypes) - { - Guard.NotNull(type, nameof(type)); - Guard.NotEmpty(methodName, nameof(methodName)); - - var cacheKey = MethodKey.Create(type, methodName, argTypes); - - if (!_invokersCache.TryGetValue(cacheKey, out var invoker)) - { - var method = FindMatchingMethod(type, methodName, argTypes); - - if (method == null) - { - throw new MethodAccessException("Could not find a matching method '{0}' in type {1}.".FormatInvariant(methodName, type)); - } - - invoker = new FastInvoker(method); - _invokersCache.TryAdd(cacheKey, invoker); - } - - return invoker; - } - - /// - /// Creates and caches a fast method invoker. - /// - /// Method info instance to create an invoker for. - /// The fast method invoker. - public static FastInvoker GetInvoker(MethodInfo method) - { - Guard.NotNull(method, nameof(method)); - - var cacheKey = MethodKey.Create(method); - - if (!_invokersCache.TryGetValue(cacheKey, out var invoker)) - { - invoker = new FastInvoker(method); - _invokersCache.TryAdd(cacheKey, invoker); - } - - return invoker; - } - - private static MethodInfo FindMatchingMethod(Type type, string methodName, Type[] argTypes) - { - var method = argTypes == null || argTypes.Length == 0 - ? type.GetMethod(methodName) - : type.GetMethod( - methodName, - BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, - null, - argTypes ?? new Type[0], - null); - - return method; - } - - #endregion - - abstract class MethodKey - { - public override bool Equals(object obj) => - throw new NotImplementedException(); - - public override int GetHashCode() => - throw new NotImplementedException(); - - public static bool operator ==(MethodKey left, MethodKey right) => - object.Equals(left, right); - - public static bool operator !=(MethodKey left, MethodKey right) => - !(left == right); - - internal static MethodKey Create(Type type, string methodName, IEnumerable parameterTypes) - { - return new HashMethodKey(type, methodName, parameterTypes); - } - - internal static MethodKey Create(MethodInfo method) - { - return new MethodInfoKey(method); - } - } - - class HashMethodKey : MethodKey, IEquatable - { - private readonly int _hash; - - public HashMethodKey(Type type, string methodName, IEnumerable parameterTypes) - { - _hash = HashCodeCombiner.Start().Add(type).Add(methodName).Add(parameterTypes).CombinedHash; - } - - public override bool Equals(object obj) => - this.Equals(obj as HashMethodKey); - - public bool Equals(HashMethodKey other) - { - if (other == null) - return false; - - return this._hash == other._hash; - } - - public override int GetHashCode() - { - return _hash; - } - } - - class MethodInfoKey : MethodKey, IEquatable - { - private readonly MethodInfo _method; - - public MethodInfoKey(MethodInfo method) - { - _method = method; - } - - public override bool Equals(object obj) => - this.Equals(obj as MethodInfoKey); - - public bool Equals(MethodInfoKey other) - { - if (other == null) - return false; - - return this._method == other._method; - } - - public override int GetHashCode() - { - return _method.GetHashCode(); - } - } - } + public class FastInvoker + { + private static readonly ConcurrentDictionary _invokersCache = new ConcurrentDictionary(); + + public FastInvoker(MethodInfo methodInfo) + { + Guard.NotNull(methodInfo, nameof(methodInfo)); + + Method = methodInfo; + Invoker = MakeFastInvoker(methodInfo); + ParameterTypes = methodInfo.GetParameters().Select(p => p.ParameterType).ToArray(); + } + + /// + /// Gets the backing . + /// + public MethodInfo Method { get; private set; } + + /// + /// Gets the parameter types from the backing + /// + public Type[] ParameterTypes { get; private set; } + + /// + /// Gets the method invoker. + /// + public Func Invoker { get; private set; } + + /// + /// Invokes the method using the specified parameters. + /// + /// The method invocation result. + public object Invoke(object obj, params object[] parameters) + { + return Invoker(obj, parameters); + } + + #region Static + + /// + /// Creates a single fast method invoker. The result is not cached. + /// + /// Method to create invoker for. + /// The fast method invoker delegate. + public static Func MakeFastInvoker(MethodInfo method) + { + Guard.NotNull(method, nameof(method)); + + var instanceParameterExpression = Expression.Parameter(typeof(object), "instance"); + var argumentsParameterExpression = Expression.Parameter(typeof(object[]), "args"); + var index = 0; + + var argumentExtractionExpressions = + method + .GetParameters() + .Select(parameter => + Expression.Convert( + Expression.ArrayAccess( + argumentsParameterExpression, + Expression.Constant(index++) + ), + parameter.ParameterType + ) + ).ToList(); + + var callExpression = method.IsStatic + ? Expression.Call(method, argumentExtractionExpressions) + : Expression.Call( + Expression.Convert( + instanceParameterExpression, + method.DeclaringType + ), + method, + argumentExtractionExpressions + ); + + var endLabel = Expression.Label(typeof(object)); + + var finalExpression = method.ReturnType == typeof(void) + ? (Expression)Expression.Block( + callExpression, + Expression.Return(endLabel, Expression.Constant(null)), + Expression.Label(endLabel, Expression.Constant(null)) + ) + : Expression.Convert(callExpression, typeof(object)); + + var lambdaExpression = Expression.Lambda>( + finalExpression, + instanceParameterExpression, + argumentsParameterExpression + ); + + var lamdba = lambdaExpression.Compile(); + return lamdba; + } + + /// + /// Invokes a method using the specified object and parameter instances. + /// + /// The objectinstance + /// Method name + /// Argument types of the matching method overload (in exact order) + /// Parameter instances to pass to invocation + /// The method invocation result. + public static object Invoke(object obj, string methodName, Type[] parameterTypes, object[] parameters) + { + Guard.NotNull(obj, nameof(obj)); + + FastInvoker invoker; + + if (parameterTypes == null || parameterTypes.Length == 0) + { + invoker = GetInvoker(obj.GetType(), methodName); + } + else + { + invoker = GetInvoker(obj.GetType(), methodName, parameterTypes); + } + + //var hasAnyNullParam = parameters.Any(x => x == null); + //if (hasAnyNullParam) + //{ + // throw new ArgumentException("When invoking a method with parameter instances, no instance must be null.", nameof(parameters)); + //} + + return invoker.Invoke(obj, parameters ?? Array.Empty()); + } + + /// + /// Creates and caches a fast method invoker. + /// + /// Name of method to create an invoker for. + /// Argument types of method to create an invoker for. + /// The fast method invoker. + public static FastInvoker GetInvoker(string methodName, params Type[] argTypes) + { + return GetInvoker(typeof(T), methodName, argTypes); + } + + /// + /// Creates and caches a fast method invoker. + /// + /// The type to extract fast method invoker for. + /// Name of method to create an invoker for. + /// Argument types of method to create an invoker for. + /// The fast method invoker. + public static FastInvoker GetInvoker(Type type, string methodName, params Type[] argTypes) + { + Guard.NotNull(type, nameof(type)); + Guard.NotEmpty(methodName, nameof(methodName)); + + var cacheKey = MethodKey.Create(type, methodName, argTypes); + + if (!_invokersCache.TryGetValue(cacheKey, out var invoker)) + { + var method = FindMatchingMethod(type, methodName, argTypes); + + if (method == null) + { + throw new MethodAccessException("Could not find a matching method '{0}' in type {1}.".FormatInvariant(methodName, type)); + } + + invoker = new FastInvoker(method); + _invokersCache.TryAdd(cacheKey, invoker); + } + + return invoker; + } + + /// + /// Creates and caches a fast method invoker. + /// + /// Method info instance to create an invoker for. + /// The fast method invoker. + public static FastInvoker GetInvoker(MethodInfo method) + { + Guard.NotNull(method, nameof(method)); + + var cacheKey = MethodKey.Create(method); + + if (!_invokersCache.TryGetValue(cacheKey, out var invoker)) + { + invoker = new FastInvoker(method); + _invokersCache.TryAdd(cacheKey, invoker); + } + + return invoker; + } + + private static MethodInfo FindMatchingMethod(Type type, string methodName, Type[] argTypes) + { + var method = argTypes == null || argTypes.Length == 0 + ? type.GetMethod(methodName) + : type.GetMethod( + methodName, + BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, + null, + argTypes ?? new Type[0], + null); + + return method; + } + + #endregion + + abstract class MethodKey + { + public override bool Equals(object obj) + { + throw new NotImplementedException(); + } + + public override int GetHashCode() + { + throw new NotImplementedException(); + } + + public static bool operator ==(MethodKey left, MethodKey right) + { + return object.Equals(left, right); + } + + public static bool operator !=(MethodKey left, MethodKey right) + { + return !(left == right); + } + + internal static MethodKey Create(Type type, string methodName, IEnumerable parameterTypes) + { + return new HashMethodKey(type, methodName, parameterTypes); + } + + internal static MethodKey Create(MethodInfo method) + { + return new MethodInfoKey(method); + } + } + + class HashMethodKey : MethodKey, IEquatable + { + private readonly int _hash; + + public HashMethodKey(Type type, string methodName, IEnumerable parameterTypes) + { + _hash = HashCodeCombiner.Start().Add(type).Add(methodName).Add(parameterTypes).CombinedHash; + } + + public override bool Equals(object obj) + { + return this.Equals(obj as HashMethodKey); + } + + public bool Equals(HashMethodKey other) + { + if (other == null) + return false; + + return this._hash == other._hash; + } + + public override int GetHashCode() + { + return _hash; + } + } + + class MethodInfoKey : MethodKey, IEquatable + { + private readonly MethodInfo _method; + + public MethodInfoKey(MethodInfo method) + { + _method = method; + } + + public override bool Equals(object obj) + { + return this.Equals(obj as MethodInfoKey); + } + + public bool Equals(MethodInfoKey other) + { + if (other == null) + return false; + + return this._method == other._method; + } + + public override int GetHashCode() + { + return _method.GetHashCode(); + } + } + } } diff --git a/src/Libraries/SmartStore.Core/ComponentModel/FastProperty.cs b/src/Libraries/SmartStore.Core/ComponentModel/FastProperty.cs index 7aafa809cd..2f5301595a 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/FastProperty.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/FastProperty.cs @@ -9,664 +9,661 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.CompilerServices; namespace SmartStore.ComponentModel { - public enum PropertyCachingStrategy - { - /// - /// Don't cache FastProperty instances - /// - Uncached = 0, - /// - /// Always cache FastProperty instances - /// - Cached = 1, - /// - /// Always cache FastProperty instances. PLUS cache all other properties of the declaring type. - /// - EagerCached = 2 - } - - public class FastProperty - { - // Delegate type for a by-ref property getter - private delegate TValue ByRefFunc(ref TDeclaringType arg); - - private static readonly MethodInfo CallPropertyGetterOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod("CallPropertyGetter"); - private static readonly MethodInfo CallPropertyGetterByReferenceOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod("CallPropertyGetterByReference"); - private static readonly MethodInfo CallNullSafePropertyGetterOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod("CallNullSafePropertyGetter"); - private static readonly MethodInfo CallNullSafePropertyGetterByReferenceOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod("CallNullSafePropertyGetterByReference"); - private static readonly MethodInfo CallPropertySetterOpenGenericMethod = typeof(FastProperty).GetTypeInfo().GetDeclaredMethod("CallPropertySetter"); - - private static readonly ConcurrentDictionary _singlePropertiesCache = new ConcurrentDictionary(); - - // Using an array rather than IEnumerable, as target will be called on the hot path numerous times. - private static readonly ConcurrentDictionary> _propertiesCache = new ConcurrentDictionary>(); - private static readonly ConcurrentDictionary> _visiblePropertiesCache = new ConcurrentDictionary>(); - - private Action _valueSetter; - private bool? _isPublicSettable; - private bool? _isSequenceType; - - /// - /// Initializes a . - /// This constructor does not cache the helper. For caching, use . - /// - [SuppressMessage("ReSharper", "VirtualMemberCallInContructor")] - public FastProperty(PropertyInfo property) - { - Guard.NotNull(property, nameof(property)); - - Property = property; - Name = property.Name; - ValueGetter = MakeFastPropertyGetter(property); - } - - /// - /// Gets the backing . - /// - public PropertyInfo Property { get; private set; } - - /// - /// Gets (or sets in derived types) the property name. - /// - public virtual string Name { get; protected set; } - - /// - /// Gets the property value getter. - /// - public Func ValueGetter { get; private set; } - - public bool IsPublicSettable - { - get - { - if (!_isPublicSettable.HasValue) - { - _isPublicSettable = Property.CanWrite && Property.GetSetMethod(false) != null; - } - return _isPublicSettable.Value; - } - } - - public bool IsSequenceType - { - get - { - if (!_isSequenceType.HasValue) - { - _isSequenceType = Property.PropertyType != typeof(string) && Property.PropertyType.IsSubClass(typeof(IEnumerable<>)); - } - return _isSequenceType.Value; - } - } - - /// - /// Gets the property value setter. - /// - public Action ValueSetter - { - get - { - if (_valueSetter == null) - { - // We'll allow safe races here. - _valueSetter = MakeFastPropertySetter(Property); - } - - return _valueSetter; - } - } - - /// - /// Returns the property value for the specified . - /// - /// The object whose property value will be returned. - /// The property value. - public object GetValue(object instance) - { - return ValueGetter(instance); - } - - /// - /// Sets the property value for the specified . - /// - /// The object whose property value will be set. - /// The property value. - public void SetValue(object instance, object value) - { - ValueSetter(instance, value); - } - - /// - /// Creates and caches fast property helpers that expose getters for every public get property on the - /// underlying type. - /// - /// the instance to extract property accessors for. - /// A cached array of all public property getters from the underlying type of target instance. - /// - public static IReadOnlyDictionary GetProperties(object instance) - { - return GetProperties(instance.GetType()); - } - - /// - /// Creates and caches fast property helpers that expose getters for every public get property on the - /// specified type. - /// - /// The type to extract property accessors for. - /// A cached array of all public property getters from the type of target instance. - /// - public static IReadOnlyDictionary GetProperties(Type type, PropertyCachingStrategy cachingStrategy = PropertyCachingStrategy.Cached) - { - var propertiesCache = cachingStrategy > PropertyCachingStrategy.Uncached ? _propertiesCache : CreateVolatileCache(); - - return (IReadOnlyDictionary)GetProperties(type, CreateInstance, propertiesCache); - } - - /// - /// - /// Creates and caches fast property helpers that expose getters for every non-hidden get property - /// on the specified type. - /// - /// - /// excludes properties defined on base types that have been - /// hidden by definitions using the new keyword. - /// - /// - /// The instance to extract property accessors for. - /// - /// A cached array of all public property getters from the instance's type. - /// - public static IReadOnlyDictionary GetVisibleProperties(object instance, PropertyCachingStrategy cachingStrategy = PropertyCachingStrategy.Cached) - { - var propertiesCache = cachingStrategy > PropertyCachingStrategy.Uncached ? _propertiesCache : CreateVolatileCache(); - var visiblePropertiesCache = cachingStrategy > PropertyCachingStrategy.Uncached ? _visiblePropertiesCache : CreateVolatileCache(); - - return (IReadOnlyDictionary)GetVisibleProperties(instance.GetType(), CreateInstance, propertiesCache, visiblePropertiesCache); - } - - /// - /// - /// Creates and caches fast property helpers that expose getters for every non-hidden get property - /// on the specified type. - /// - /// - /// excludes properties defined on base types that have been - /// hidden by definitions using the new keyword. - /// - /// - /// The type to extract property accessors for. - /// - /// A cached array of all public property getters from the type. - /// - public static IReadOnlyDictionary GetVisibleProperties(Type type, PropertyCachingStrategy cachingStrategy = PropertyCachingStrategy.Cached) - { - var propertiesCache = cachingStrategy > PropertyCachingStrategy.Uncached ? _propertiesCache : CreateVolatileCache(); - var visiblePropertiesCache = cachingStrategy > PropertyCachingStrategy.Uncached ? _visiblePropertiesCache : CreateVolatileCache(); - - return (IReadOnlyDictionary)GetVisibleProperties(type, CreateInstance, propertiesCache, visiblePropertiesCache); - } - - public static FastProperty GetProperty( - Expression> property, - PropertyCachingStrategy cachingStrategy = PropertyCachingStrategy.Cached) - { - return GetProperty(property.ExtractPropertyInfo(), cachingStrategy); - } - - public static FastProperty GetProperty( - Type type, - string propertyName, - PropertyCachingStrategy cachingStrategy = PropertyCachingStrategy.Cached) - { - Guard.NotNull(type, nameof(type)); - Guard.NotEmpty(propertyName, nameof(propertyName)); - - if (TryGetCachedProperty(type, propertyName, cachingStrategy == PropertyCachingStrategy.EagerCached, out var fastProperty)) - { - return fastProperty; - } - - var key = new PropertyKey(type, propertyName); - if (!_singlePropertiesCache.TryGetValue(key, out fastProperty)) - { - var pi = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase); - if (pi != null) - { - fastProperty = CreateInstance(pi); - if (cachingStrategy > PropertyCachingStrategy.Uncached) - { - _singlePropertiesCache.TryAdd(key, fastProperty); - } - } - } - - return fastProperty; - } - - public static FastProperty GetProperty( - PropertyInfo propertyInfo, - PropertyCachingStrategy cachingStrategy = PropertyCachingStrategy.Cached) - { - Guard.NotNull(propertyInfo, nameof(propertyInfo)); - - if (TryGetCachedProperty(propertyInfo.ReflectedType, propertyInfo.Name, cachingStrategy == PropertyCachingStrategy.EagerCached, out var fastProperty)) - { - return fastProperty; - } - - var key = new PropertyKey(propertyInfo.ReflectedType, propertyInfo.Name); - if (!_singlePropertiesCache.TryGetValue(key, out fastProperty)) - { - fastProperty = CreateInstance(propertyInfo); - if (cachingStrategy > PropertyCachingStrategy.Uncached) - { - _singlePropertiesCache.TryAdd(key, fastProperty); - } - } - - return fastProperty; - } - - private static bool TryGetCachedProperty( - Type type, - string propertyName, - bool eagerCached, - out FastProperty fastProperty) - { - fastProperty = null; - IDictionary allProperties; - - if (eagerCached) - { - allProperties = (IDictionary)GetProperties(type); - allProperties.TryGetValue(propertyName, out fastProperty); - } - - if (fastProperty == null && _propertiesCache.TryGetValue(type, out allProperties)) - { - allProperties.TryGetValue(propertyName, out fastProperty); - } - - return fastProperty != null; - } - - /// - /// Creates a single fast property getter. The result is not cached. - /// - /// propertyInfo to extract the getter for. - /// a fast getter. - /// - /// This method is more memory efficient than a dynamically compiled lambda, and about the - /// same speed. - /// - public static Func MakeFastPropertyGetter(PropertyInfo propertyInfo) - { - Debug.Assert(propertyInfo != null); - - return MakeFastPropertyGetter( - propertyInfo, - CallPropertyGetterOpenGenericMethod, - CallPropertyGetterByReferenceOpenGenericMethod); - } - - /// - /// Creates a single fast property getter which is safe for a null input object. The result is not cached. - /// - /// propertyInfo to extract the getter for. - /// a fast getter. - /// - /// This method is more memory efficient than a dynamically compiled lambda, and about the - /// same speed. - /// - public static Func MakeNullSafeFastPropertyGetter(PropertyInfo propertyInfo) - { - Debug.Assert(propertyInfo != null); - - return MakeFastPropertyGetter( - propertyInfo, - CallNullSafePropertyGetterOpenGenericMethod, - CallNullSafePropertyGetterByReferenceOpenGenericMethod); - } - - private static Func MakeFastPropertyGetter( - PropertyInfo propertyInfo, - MethodInfo propertyGetterWrapperMethod, - MethodInfo propertyGetterByRefWrapperMethod) - { - Debug.Assert(propertyInfo != null); - - // Must be a generic method with a Func<,> parameter - Debug.Assert(propertyGetterWrapperMethod != null); - Debug.Assert(propertyGetterWrapperMethod.IsGenericMethodDefinition); - Debug.Assert(propertyGetterWrapperMethod.GetParameters().Length == 2); - - // Must be a generic method with a ByRefFunc<,> parameter - Debug.Assert(propertyGetterByRefWrapperMethod != null); - Debug.Assert(propertyGetterByRefWrapperMethod.IsGenericMethodDefinition); - Debug.Assert(propertyGetterByRefWrapperMethod.GetParameters().Length == 2); - - var getMethod = propertyInfo.GetMethod; - Debug.Assert(getMethod != null); - Debug.Assert(!getMethod.IsStatic); - Debug.Assert(getMethod.GetParameters().Length == 0); - - // Instance methods in the CLR can be turned into static methods where the first parameter - // is open over "target". This parameter is always passed by reference, so we have a code - // path for value types and a code path for reference types. - if (getMethod.DeclaringType.GetTypeInfo().IsValueType) - { - // Create a delegate (ref TDeclaringType) -> TValue - return MakeFastPropertyGetter( - typeof(ByRefFunc<,>), - getMethod, - propertyGetterByRefWrapperMethod); - } - else - { - // Create a delegate TDeclaringType -> TValue - return MakeFastPropertyGetter( - typeof(Func<,>), - getMethod, - propertyGetterWrapperMethod); - } - } - - private static Func MakeFastPropertyGetter( - Type openGenericDelegateType, - MethodInfo propertyGetMethod, - MethodInfo openGenericWrapperMethod) - { - var typeInput = propertyGetMethod.DeclaringType; - var typeOutput = propertyGetMethod.ReturnType; - - var delegateType = openGenericDelegateType.MakeGenericType(typeInput, typeOutput); - var propertyGetterDelegate = propertyGetMethod.CreateDelegate(delegateType); - - var wrapperDelegateMethod = openGenericWrapperMethod.MakeGenericMethod(typeInput, typeOutput); - var accessorDelegate = wrapperDelegateMethod.CreateDelegate( - typeof(Func), - propertyGetterDelegate); - - return (Func)accessorDelegate; - } - - /// - /// Creates a single fast property setter for reference types. The result is not cached. - /// - /// propertyInfo to extract the setter for. - /// a fast getter. - /// - /// This method is more memory efficient than a dynamically compiled lambda, and about the - /// same speed. This only works for reference types. - /// - public static Action MakeFastPropertySetter(PropertyInfo propertyInfo) - { - Debug.Assert(propertyInfo != null); - Debug.Assert(!propertyInfo.DeclaringType.GetTypeInfo().IsValueType); - - var setMethod = propertyInfo.SetMethod; - Debug.Assert(setMethod != null); - Debug.Assert(!setMethod.IsStatic); - Debug.Assert(setMethod.ReturnType == typeof(void)); - var parameters = setMethod.GetParameters(); - Debug.Assert(parameters.Length == 1); - - // Instance methods in the CLR can be turned into static methods where the first parameter - // is open over "target". This parameter is always passed by reference, so we have a code - // path for value types and a code path for reference types. - var typeInput = setMethod.DeclaringType; - var parameterType = parameters[0].ParameterType; - - // Create a delegate TDeclaringType -> { TDeclaringType.Property = TValue; } - var propertySetterAsAction = - setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, parameterType)); - var callPropertySetterClosedGenericMethod = - CallPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, parameterType); - var callPropertySetterDelegate = - callPropertySetterClosedGenericMethod.CreateDelegate( - typeof(Action), propertySetterAsAction); - - return (Action)callPropertySetterDelegate; - } - - /// - /// Given an object, adds each instance property with a public get method as a key and its - /// associated value to a dictionary. - /// - /// If the object is already an - /// IDictionary{string, object} - /// - /// instance, then a copy - /// is returned. - /// - /// Key selector - /// When true, converts all nested objects to dictionaries also - /// - /// The implementation of FastProperty will cache the property accessors per-type. This is - /// faster when the the same type is used multiple times with ObjectToDictionary. - /// - public static IDictionary ObjectToDictionary(object value, Func keySelector = null, bool deep = false) - { - if (value is IDictionary dictionary) - { - return new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); - } - - dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - - if (value != null) - { - keySelector = keySelector ?? new Func(key => key); - - foreach (var prop in GetProperties(value).Values) - { - var propValue = prop.GetValue(value); - if (deep && propValue != null && prop.Property.PropertyType.IsPlainObjectType()) - { - propValue = ObjectToDictionary(propValue, deep: true); - } - - dictionary[keySelector(prop.Name)] = propValue; - } - } - - return dictionary; - } - - private static FastProperty CreateInstance(PropertyInfo property) - { - return new FastProperty(property); - } - - // Called via reflection - private static object CallPropertyGetter( - Func getter, - object target) - { - return getter((TDeclaringType)target); - } - - // Called via reflection - private static object CallPropertyGetterByReference( - ByRefFunc getter, - object target) - { - var unboxed = (TDeclaringType)target; - return getter(ref unboxed); - } - - // Called via reflection - private static object CallNullSafePropertyGetter( - Func getter, - object target) - { - if (target == null) - { - return null; - } - - return getter((TDeclaringType)target); - } - - // Called via reflection - private static object CallNullSafePropertyGetterByReference( - ByRefFunc getter, - object target) - { - if (target == null) - { - return null; - } - - var unboxed = (TDeclaringType)target; - return getter(ref unboxed); - } - - private static void CallPropertySetter( - Action setter, - object target, - object value) - { - setter((TDeclaringType)target, (TValue)value); - } - - protected static IDictionary GetVisibleProperties( - Type type, - Func createPropertyHelper, - ConcurrentDictionary> allPropertiesCache, - ConcurrentDictionary> visiblePropertiesCache) - { - if (visiblePropertiesCache.TryGetValue(type, out var result)) - { - return result; - } - - // The simple and common case, this is normal POCO object - no need to allocate. - var allPropertiesDefinedOnType = true; - var allProperties = GetProperties(type, createPropertyHelper, allPropertiesCache); - foreach (var propertyHelper in allProperties.Values) - { - if (propertyHelper.Property.DeclaringType != type) - { - allPropertiesDefinedOnType = false; - break; - } - } - - if (allPropertiesDefinedOnType) - { - result = allProperties; - visiblePropertiesCache.TryAdd(type, result); - return result; - } - - // There's some inherited properties here, so we need to check for hiding via 'new'. - var filteredProperties = new List(allProperties.Count); - foreach (var propertyHelper in allProperties.Values) - { - var declaringType = propertyHelper.Property.DeclaringType; - if (declaringType == type) - { - filteredProperties.Add(propertyHelper); - continue; - } - - // If this property was declared on a base type then look for the definition closest to the - // the type to see if we should include it. - var ignoreProperty = false; - - // Walk up the hierarchy until we find the type that actally declares this - // PropertyInfo. - var currentTypeInfo = type.GetTypeInfo(); - var declaringTypeInfo = declaringType.GetTypeInfo(); - while (currentTypeInfo != null && currentTypeInfo != declaringTypeInfo) - { - // We've found a 'more proximal' public definition - var declaredProperty = currentTypeInfo.GetDeclaredProperty(propertyHelper.Name); - if (declaredProperty != null) - { - ignoreProperty = true; - break; - } - - if (currentTypeInfo.BaseType != null) - { - currentTypeInfo = currentTypeInfo.BaseType.GetTypeInfo(); - } - - } - - if (!ignoreProperty) - { - filteredProperties.Add(propertyHelper); - } - } - - result = filteredProperties.ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase); - visiblePropertiesCache.TryAdd(type, result); - return result; - } - - protected static IDictionary GetProperties( - Type type, - Func createPropertyHelper, - ConcurrentDictionary> cache) - { - // Unwrap nullable types. This means Nullable.Value and Nullable.HasValue will not be - // part of the sequence of properties returned by this method. - type = Nullable.GetUnderlyingType(type) ?? type; - - return cache.GetOrAdd(type, Get); - - IDictionary Get(Type t) - { - var candidates = GetCandidateProperties(t); - var fastProperties = candidates.Select(p => createPropertyHelper(p)).ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase); - return fastProperties; - } - } - - internal static IEnumerable GetCandidateProperties(Type type) - { - // We avoid loading indexed properties using the Where statement. - var properties = type.GetRuntimeProperties().Where(IsCandidateProperty); - - var typeInfo = type.GetTypeInfo(); - if (typeInfo.IsInterface) - { - // Reflection does not return information about inherited properties on the interface itself. - properties = properties.Concat(typeInfo.ImplementedInterfaces.SelectMany( - interfaceType => interfaceType.GetRuntimeProperties().Where(IsCandidateProperty))); - } - - return properties; - } - - // Indexed properties are not useful (or valid) for grabbing properties off an object. - private static bool IsCandidateProperty(PropertyInfo property) - { - return property.GetIndexParameters().Length == 0 && - property.GetMethod != null && - property.GetMethod.IsPublic && - !property.GetMethod.IsStatic; - } - - private static ConcurrentDictionary> CreateVolatileCache() - { - return new ConcurrentDictionary>(); - } - - class PropertyKey : Tuple - { - public PropertyKey(Type type, string propertyName) - : base(type, propertyName) - { - } - public Type Type { get { return base.Item1; } } - public string PropertyName { get { return base.Item2; } } - } - } + public enum PropertyCachingStrategy + { + /// + /// Don't cache FastProperty instances + /// + Uncached = 0, + /// + /// Always cache FastProperty instances + /// + Cached = 1, + /// + /// Always cache FastProperty instances. PLUS cache all other properties of the declaring type. + /// + EagerCached = 2 + } + + public abstract class FastProperty + { + private static readonly ConcurrentDictionary _singlePropertiesCache = new ConcurrentDictionary(); + + // Using an array rather than IEnumerable, as target will be called on the hot path numerous times. + private static readonly ConcurrentDictionary> _propertiesCache = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> _visiblePropertiesCache = new ConcurrentDictionary>(); + + private Func _valueGetter; + private Action _valueSetter; + private bool? _isPublicSettable; + private bool? _isSequenceType; + + /// + /// Initializes a . + /// This constructor does not cache the helper. For caching, use . + /// + [SuppressMessage("ReSharper", "VirtualMemberCallInContructor")] + protected FastProperty(PropertyInfo property) + { + Guard.NotNull(property, nameof(property)); + + Property = property; + Name = property.Name; + } + + /// + /// Gets the property value getter. + /// + public Func ValueGetter + { + get + { + if (_valueGetter == null) + { + // We'll allow safe races here. + _valueGetter = MakePropertyGetter(Property); + } + + return _valueGetter; + } + private set => _valueGetter = value; + } + + /// + /// Gets the property value setter. + /// + public Action ValueSetter + { + get + { + if (_valueSetter == null) + { + // We'll allow safe races here. + _valueSetter = MakePropertySetter(Property); + } + + return _valueSetter; + } + private set => _valueSetter = value; + } + + protected abstract Func MakePropertyGetter(PropertyInfo propertyInfo); + + protected abstract Action MakePropertySetter(PropertyInfo propertyInfo); + + /// + /// Gets the backing . + /// + public PropertyInfo Property { get; private set; } + + /// + /// Gets (or sets in derived types) the property name. + /// + public virtual string Name { get; protected set; } + + public bool IsPublicSettable + { + get + { + if (!_isPublicSettable.HasValue) + { + _isPublicSettable = Property.CanWrite && Property.GetSetMethod(false) != null; + } + return _isPublicSettable.Value; + } + } + + public bool IsSequenceType + { + get + { + if (!_isSequenceType.HasValue) + { + _isSequenceType = Property.PropertyType != typeof(string) && Property.PropertyType.IsSubClass(typeof(IEnumerable<>)); + } + return _isSequenceType.Value; + } + } + + /// + /// Returns the property value for the specified . + /// + /// The object whose property value will be returned. + /// The property value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public object GetValue(object instance) + { + return ValueGetter(instance); + } + + /// + /// Sets the property value for the specified . + /// + /// The object whose property value will be set. + /// The property value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetValue(object instance, object value) + { + ValueSetter(instance, value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static FastProperty GetProperty( + Expression> property, + PropertyCachingStrategy cachingStrategy = PropertyCachingStrategy.Cached) + { + return GetProperty(property.ExtractPropertyInfo(), cachingStrategy); + } + + public static FastProperty GetProperty( + Type type, + string propertyName, + PropertyCachingStrategy cachingStrategy = PropertyCachingStrategy.Cached) + { + Guard.NotNull(type, nameof(type)); + Guard.NotEmpty(propertyName, nameof(propertyName)); + + if (TryGetCachedProperty(type, propertyName, cachingStrategy == PropertyCachingStrategy.EagerCached, out var fastProperty)) + { + return fastProperty; + } + + var key = new PropertyKey(type, propertyName); + if (!_singlePropertiesCache.TryGetValue(key, out fastProperty)) + { + var pi = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.IgnoreCase); + if (pi != null) + { + fastProperty = Create(pi); + if (cachingStrategy > PropertyCachingStrategy.Uncached) + { + _singlePropertiesCache.TryAdd(key, fastProperty); + } + } + } + + return fastProperty; + } + + public static FastProperty GetProperty( + PropertyInfo propertyInfo, + PropertyCachingStrategy cachingStrategy = PropertyCachingStrategy.Cached) + { + Guard.NotNull(propertyInfo, nameof(propertyInfo)); + + if (TryGetCachedProperty(propertyInfo.ReflectedType, propertyInfo.Name, cachingStrategy == PropertyCachingStrategy.EagerCached, out var fastProperty)) + { + return fastProperty; + } + + var key = new PropertyKey(propertyInfo.ReflectedType, propertyInfo.Name); + if (!_singlePropertiesCache.TryGetValue(key, out fastProperty)) + { + fastProperty = Create(propertyInfo); + if (cachingStrategy > PropertyCachingStrategy.Uncached) + { + _singlePropertiesCache.TryAdd(key, fastProperty); + } + } + + return fastProperty; + } + + private static bool TryGetCachedProperty( + Type type, + string propertyName, + bool eagerCached, + out FastProperty fastProperty) + { + fastProperty = null; + IDictionary allProperties; + + if (eagerCached) + { + allProperties = (IDictionary)GetProperties(type); + allProperties.TryGetValue(propertyName, out fastProperty); + } + + if (fastProperty == null && _propertiesCache.TryGetValue(type, out allProperties)) + { + allProperties.TryGetValue(propertyName, out fastProperty); + } + + return fastProperty != null; + } + + /// + /// Given an object, adds each instance property with a public get method as a key and its + /// associated value to a dictionary. + /// + /// If the object is already an + /// IDictionary{string, object} + /// + /// instance, then a copy + /// is returned. + /// + /// Key selector + /// When true, converts all nested objects to dictionaries also + /// + /// The implementation of FastProperty will cache the property accessors per-type. This is + /// faster when the the same type is used multiple times with ObjectToDictionary. + /// + public static IDictionary ObjectToDictionary(object value, Func keySelector = null, bool deep = false) + { + if (value is IDictionary dictionary) + { + return new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); + } + + dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (value != null) + { + keySelector = keySelector ?? new Func(key => key); + + foreach (var prop in GetProperties(value.GetType()).Values) + { + var propValue = prop.GetValue(value); + if (deep && propValue != null && prop.Property.PropertyType.IsPlainObjectType()) + { + propValue = ObjectToDictionary(propValue, deep: true); + } + + dictionary[keySelector(prop.Name)] = propValue; + } + } + + return dictionary; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static FastProperty Create(PropertyInfo property) + { + return new DelegatedAccessor(property); + } + + + /// + /// + /// Creates and caches fast property helpers that expose getters for every non-hidden get property + /// on the specified type. + /// + /// + /// excludes properties defined on base types that have been + /// hidden by definitions using the new keyword. + /// + /// + /// The type to extract property accessors for. + /// + /// A cached array of all public property getters from the type. + /// + public static IReadOnlyDictionary GetVisibleProperties(Type type, PropertyCachingStrategy cachingStrategy = PropertyCachingStrategy.Cached) + { + Guard.NotNull(type, nameof(type)); + + // Unwrap nullable types. This means Nullable.Value and Nullable.HasValue will not be + // part of the sequence of properties returned by this method. + type = Nullable.GetUnderlyingType(type) ?? type; + + if (_visiblePropertiesCache.TryGetValue(type, out var result)) + { + return (IReadOnlyDictionary)result; + } + + var visiblePropertiesCache = cachingStrategy > PropertyCachingStrategy.Uncached ? _visiblePropertiesCache : CreateVolatileCache(); + + // The simple and common case, this is normal POCO object - no need to allocate. + var allPropertiesDefinedOnType = true; + var allProperties = GetProperties(type, cachingStrategy); + foreach (var prop in allProperties.Values) + { + if (prop.Property.DeclaringType != type) + { + allPropertiesDefinedOnType = false; + break; + } + } + + if (allPropertiesDefinedOnType) + { + result = (IDictionary)allProperties; + visiblePropertiesCache.TryAdd(type, result); + return allProperties; + } + + // There's some inherited properties here, so we need to check for hiding via 'new'. + var filteredProperties = new List(allProperties.Count); + foreach (var prop in allProperties.Values) + { + var declaringType = prop.Property.DeclaringType; + if (declaringType == type) + { + filteredProperties.Add(prop); + continue; + } + + // If this property was declared on a base type then look for the definition closest to the + // the type to see if we should include it. + var ignoreProperty = false; + + // Walk up the hierarchy until we find the type that actally declares this + // PropertyInfo. + var currentTypeInfo = type.GetTypeInfo(); + var declaringTypeInfo = declaringType.GetTypeInfo(); + while (currentTypeInfo != null && currentTypeInfo != declaringTypeInfo) + { + // We've found a 'more proximal' public definition + var declaredProperty = currentTypeInfo.GetDeclaredProperty(prop.Name); + if (declaredProperty != null) + { + ignoreProperty = true; + break; + } + + if (currentTypeInfo.BaseType != null) + { + currentTypeInfo = currentTypeInfo.BaseType.GetTypeInfo(); + } + + } + + if (!ignoreProperty) + { + filteredProperties.Add(prop); + } + } + + result = filteredProperties.ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase); + visiblePropertiesCache.TryAdd(type, result); + return (IReadOnlyDictionary)result; + } + + /// + /// Creates and caches fast property helpers that expose getters for every public get property on the + /// specified type. + /// + /// The type to extract property accessors for. + /// A cached array of all public property getters from the type of target instance. + /// + public static IReadOnlyDictionary GetProperties(Type type, PropertyCachingStrategy cachingStrategy = PropertyCachingStrategy.Cached) + { + Guard.NotNull(type, nameof(type)); + + // Unwrap nullable types. This means Nullable.Value and Nullable.HasValue will not be + // part of the sequence of properties returned by this method. + type = Nullable.GetUnderlyingType(type) ?? type; + + if (!_propertiesCache.TryGetValue(type, out var props)) + { + if (cachingStrategy == PropertyCachingStrategy.Uncached) + { + props = Get(type); + } + else + { + props = _propertiesCache.GetOrAdd(type, Get); + } + } + + return (IReadOnlyDictionary)props; + + IDictionary Get(Type t) + { + var candidates = GetCandidateProperties(t); + var fastProperties = candidates.Select(p => Create(p)).ToDictionary(x => x.Name, StringComparer.OrdinalIgnoreCase); + CleanDuplicates(t, fastProperties.Keys); + return fastProperties; + } + } + + private static void CleanDuplicates(Type type, IEnumerable propNames) + { + foreach (var name in propNames) + { + var key = new PropertyKey(type, name); + _singlePropertiesCache.TryRemove(key, out _); + } + } + + internal static IEnumerable GetCandidateProperties(Type type) + { + // We avoid loading indexed properties using the Where statement. + var properties = type.GetRuntimeProperties().Where(IsCandidateProperty); + + var typeInfo = type.GetTypeInfo(); + if (typeInfo.IsInterface) + { + // Reflection does not return information about inherited properties on the interface itself. + properties = properties.Concat(typeInfo.ImplementedInterfaces.SelectMany( + interfaceType => interfaceType.GetRuntimeProperties().Where(IsCandidateProperty))); + } + + return properties; + } + + // Indexed properties are not useful (or valid) for grabbing properties off an object. + private static bool IsCandidateProperty(PropertyInfo property) + { + return property.GetIndexParameters().Length == 0 && + property.GetMethod != null && + property.GetMethod.IsPublic && + !property.GetMethod.IsStatic; + } + + private static ConcurrentDictionary> CreateVolatileCache() + { + return new ConcurrentDictionary>(); + } + + class PropertyKey : Tuple + { + public PropertyKey(Type type, string propertyName) + : base(type, propertyName) + { + } + public Type Type => base.Item1; + public string PropertyName => base.Item2; + } + } + + + + [DebuggerDisplay("DelegateAccessor: {Name}")] + internal sealed class DelegatedAccessor : FastProperty + { + // Delegate type for a by-ref property getter + private delegate TValue ByRefFunc(ref TDeclaringType arg); + + private static readonly MethodInfo CallPropertyGetterOpenGenericMethod = typeof(DelegatedAccessor).GetTypeInfo().GetDeclaredMethod("CallPropertyGetter"); + private static readonly MethodInfo CallPropertyGetterByReferenceOpenGenericMethod = typeof(DelegatedAccessor).GetTypeInfo().GetDeclaredMethod("CallPropertyGetterByReference"); + private static readonly MethodInfo CallNullSafePropertyGetterOpenGenericMethod = typeof(DelegatedAccessor).GetTypeInfo().GetDeclaredMethod("CallNullSafePropertyGetter"); + private static readonly MethodInfo CallNullSafePropertyGetterByReferenceOpenGenericMethod = typeof(DelegatedAccessor).GetTypeInfo().GetDeclaredMethod("CallNullSafePropertyGetterByReference"); + private static readonly MethodInfo CallPropertySetterOpenGenericMethod = typeof(DelegatedAccessor).GetTypeInfo().GetDeclaredMethod("CallPropertySetter"); + + public DelegatedAccessor(PropertyInfo property) + : base(property) + { + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override Func MakePropertyGetter(PropertyInfo propertyInfo) + { + Debug.Assert(propertyInfo != null); + + return MakeFastPropertyGetter( + propertyInfo, + CallPropertyGetterOpenGenericMethod, + CallPropertyGetterByReferenceOpenGenericMethod); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override Action MakePropertySetter(PropertyInfo propertyInfo) + { + Debug.Assert(propertyInfo != null); + Debug.Assert(!propertyInfo.DeclaringType.GetTypeInfo().IsValueType); + + var setMethod = propertyInfo.SetMethod; + Debug.Assert(setMethod != null); + Debug.Assert(!setMethod.IsStatic); + Debug.Assert(setMethod.ReturnType == typeof(void)); + var parameters = setMethod.GetParameters(); + Debug.Assert(parameters.Length == 1); + + // Instance methods in the CLR can be turned into static methods where the first parameter + // is open over "target". This parameter is always passed by reference, so we have a code + // path for value types and a code path for reference types. + var typeInput = setMethod.DeclaringType; + var parameterType = parameters[0].ParameterType; + + // Create a delegate TDeclaringType -> { TDeclaringType.Property = TValue; } + var propertySetterAsAction = + setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, parameterType)); + var callPropertySetterClosedGenericMethod = + CallPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, parameterType); + var callPropertySetterDelegate = + callPropertySetterClosedGenericMethod.CreateDelegate( + typeof(Action), propertySetterAsAction); + + return (Action)callPropertySetterDelegate; + } + + /// + /// Creates a single fast property getter which is safe for a null input object. The result is not cached. + /// + /// propertyInfo to extract the getter for. + /// A fast getter. + /// + /// This method is more memory efficient than a dynamically compiled lambda, and about the + /// same speed. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static Func MakeNullSafeFastPropertyGetter(PropertyInfo propertyInfo) + { + Debug.Assert(propertyInfo != null); + + return MakeFastPropertyGetter( + propertyInfo, + CallNullSafePropertyGetterOpenGenericMethod, + CallNullSafePropertyGetterByReferenceOpenGenericMethod); + } + + static Func MakeFastPropertyGetter( + PropertyInfo propertyInfo, + MethodInfo propertyGetterWrapperMethod, + MethodInfo propertyGetterByRefWrapperMethod) + { + Debug.Assert(propertyInfo != null); + + // Must be a generic method with a Func<,> parameter + Debug.Assert(propertyGetterWrapperMethod != null); + Debug.Assert(propertyGetterWrapperMethod.IsGenericMethodDefinition); + Debug.Assert(propertyGetterWrapperMethod.GetParameters().Length == 2); + + // Must be a generic method with a ByRefFunc<,> parameter + Debug.Assert(propertyGetterByRefWrapperMethod != null); + Debug.Assert(propertyGetterByRefWrapperMethod.IsGenericMethodDefinition); + Debug.Assert(propertyGetterByRefWrapperMethod.GetParameters().Length == 2); + + var getMethod = propertyInfo.GetMethod; + Debug.Assert(getMethod != null); + Debug.Assert(!getMethod.IsStatic); + Debug.Assert(getMethod.GetParameters().Length == 0); + + // Instance methods in the CLR can be turned into static methods where the first parameter + // is open over "target". This parameter is always passed by reference, so we have a code + // path for value types and a code path for reference types. + if (getMethod.DeclaringType.GetTypeInfo().IsValueType) + { + // Create a delegate (ref TDeclaringType) -> TValue + return MakeFastPropertyGetter( + typeof(ByRefFunc<,>), + getMethod, + propertyGetterByRefWrapperMethod); + } + else + { + // Create a delegate TDeclaringType -> TValue + return MakeFastPropertyGetter( + typeof(Func<,>), + getMethod, + propertyGetterWrapperMethod); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Func MakeFastPropertyGetter( + Type openGenericDelegateType, + MethodInfo propertyGetMethod, + MethodInfo openGenericWrapperMethod) + { + var typeInput = propertyGetMethod.DeclaringType; + var typeOutput = propertyGetMethod.ReturnType; + + var delegateType = openGenericDelegateType.MakeGenericType(typeInput, typeOutput); + var propertyGetterDelegate = propertyGetMethod.CreateDelegate(delegateType); + + var wrapperDelegateMethod = openGenericWrapperMethod.MakeGenericMethod(typeInput, typeOutput); + var accessorDelegate = wrapperDelegateMethod.CreateDelegate( + typeof(Func), + propertyGetterDelegate); + + return (Func)accessorDelegate; + } + + // Called via reflection + private static object CallPropertyGetter( + Func getter, + object target) + { + return getter((TDeclaringType)target); + } + + // Called via reflection + private static object CallPropertyGetterByReference( + ByRefFunc getter, + object target) + { + var unboxed = (TDeclaringType)target; + return getter(ref unboxed); + } + + // Called via reflection + private static object CallNullSafePropertyGetter( + Func getter, + object target) + { + if (target == null) + { + return null; + } + + return getter((TDeclaringType)target); + } + + // Called via reflection + private static object CallNullSafePropertyGetterByReference( + ByRefFunc getter, + object target) + { + if (target == null) + { + return null; + } + + var unboxed = (TDeclaringType)target; + return getter(ref unboxed); + } + + private static void CallPropertySetter( + Action setter, + object target, + object value) + { + setter((TDeclaringType)target, (TValue)value); + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/ComponentModel/FastPropertyValueProvider.cs b/src/Libraries/SmartStore.Core/ComponentModel/FastPropertyValueProvider.cs new file mode 100644 index 0000000000..2a3a22d7f7 --- /dev/null +++ b/src/Libraries/SmartStore.Core/ComponentModel/FastPropertyValueProvider.cs @@ -0,0 +1,78 @@ +using System; +using System.Reflection; +using Newtonsoft.Json.Serialization; + +namespace SmartStore.ComponentModel +{ + public class FastPropertyValueProvider : IValueProvider + { + private readonly PropertyInfo _pi; + + private IValueProvider _inner; + private Func _getter; + private Action _setter; + + public FastPropertyValueProvider(PropertyInfo pi) + { + _pi = pi; + } + + private void CreateInnerProvider() + { + _inner = new DynamicValueProvider(_pi); + } + + public object GetValue(object target) + { + if (_inner != null) + { + return _inner.GetValue(target); + } + + if (_getter == null) + { + try + { + _getter = FastProperty.GetProperty(_pi, PropertyCachingStrategy.EagerCached).ValueGetter; + if (_getter == null) throw new Exception(); + } + catch + { + CreateInnerProvider(); + return _inner.GetValue(target); + } + } + + return _getter(target); + } + + public void SetValue(object target, object value) + { + if (_inner != null) + { + _inner.SetValue(target, value); + } + + if (_setter == null) + { + try + { + var fastProp = FastProperty.GetProperty(_pi, PropertyCachingStrategy.EagerCached); + if (fastProp.IsPublicSettable) + { + _setter = fastProp.ValueSetter; + } + if (_setter == null) throw new Exception(); + } + catch + { + CreateInnerProvider(); + _inner.SetValue(target, value); + return; + } + } + + _setter(target, value); + } + } +} diff --git a/src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs b/src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs index 17d44dbbac..0bb0f7142d 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/HybridExpando.cs @@ -32,22 +32,22 @@ #endregion using System; +using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Dynamic; +using System.Linq; using System.Reflection; -using System.Collections; using SmartStore.Utilities; namespace SmartStore.ComponentModel { public enum MemberOptMethod - { - Allow, - Disallow - } - - /// + { + Allow, + Disallow + } + + /// /// Class that provides extensible properties and methods to an /// existing object when cast to dynamic. This /// dynamic object stores 'extra' properties in a dictionary or @@ -78,33 +78,33 @@ public class HybridExpando : DynamicObject, IDictionary /// private Type _instanceType; - /// - /// Adjusted property list for the wrapped instance type after white/black-list members has been applied. - /// - private IDictionary _instanceProps; - private readonly static IDictionary EmptyProps = new Dictionary(); - - /// - /// String Dictionary that contains the extra dynamic values - /// stored on this object/instance - /// - /// Using PropertyBag to support XML Serialization of the dictionary - public PropertyBag Properties = new PropertyBag(); - - private readonly HashSet _optMembers; - private readonly MemberOptMethod _optMethod; - - private readonly bool _returnNullWhenFalsy; - - /// - /// This constructor just works off the internal dictionary and any - /// public properties of this object. - /// - /// Note you can subclass HybridExpando. - /// - public HybridExpando(bool returnNullWhenFalsy = false) + /// + /// Adjusted property list for the wrapped instance type after white/black-list members has been applied. + /// + private IDictionary _instanceProps; + private readonly static IDictionary EmptyProps = new Dictionary(); + + /// + /// String Dictionary that contains the extra dynamic values + /// stored on this object/instance + /// + /// Using PropertyBag to support XML Serialization of the dictionary + public PropertyBag Properties = new PropertyBag(); + + private readonly HashSet _optMembers; + private readonly MemberOptMethod _optMethod; + + private readonly bool _returnNullWhenFalsy; + + /// + /// This constructor just works off the internal dictionary and any + /// public properties of this object. + /// + /// Note you can subclass HybridExpando. + /// + public HybridExpando(bool returnNullWhenFalsy = false) { - _returnNullWhenFalsy = returnNullWhenFalsy; + _returnNullWhenFalsy = returnNullWhenFalsy; } /// @@ -117,169 +117,166 @@ public HybridExpando(bool returnNullWhenFalsy = false) /// public HybridExpando(object instance, bool returnNullWhenFalsy = false) { - Guard.NotNull(instance, nameof(instance)); + Guard.NotNull(instance, nameof(instance)); - _returnNullWhenFalsy = returnNullWhenFalsy; - Initialize(instance); + _returnNullWhenFalsy = returnNullWhenFalsy; + Initialize(instance); } - /// - /// Allows passing in an existing instance variable to 'extend' - /// along with a list of member names to allow or disallow. - /// - /// - public HybridExpando(object instance, IEnumerable optMembers, MemberOptMethod optMethod, bool returnNullWhenFalsy = false) - { - Guard.NotNull(instance, nameof(instance)); - - _returnNullWhenFalsy = returnNullWhenFalsy; - Initialize(instance); - - _optMethod = optMethod; - - if (optMembers is HashSet h) - { - _optMembers = h; - } - else - { - _optMembers = new HashSet(optMembers); - } - } - - protected void Initialize(object instance) + /// + /// Allows passing in an existing instance variable to 'extend' + /// along with a list of member names to allow or disallow. + /// + /// + public HybridExpando(object instance, IEnumerable optMembers, MemberOptMethod optMethod, bool returnNullWhenFalsy = false) + { + Guard.NotNull(instance, nameof(instance)); + + _returnNullWhenFalsy = returnNullWhenFalsy; + Initialize(instance); + + _optMethod = optMethod; + + if (optMembers is HashSet h) + { + _optMembers = h; + } + else + { + _optMembers = new HashSet(optMembers); + } + } + + protected void Initialize(object instance) { _instance = instance; - _instanceType = instance?.GetType(); + _instanceType = instance?.GetType(); } - protected object WrappedObject - { - get { return _instance; } - } + protected object WrappedObject => _instance; - public override IEnumerable GetDynamicMemberNames() + public override IEnumerable GetDynamicMemberNames() { - foreach (var kvp in this.Properties.Keys) - { - yield return kvp; - } - - foreach (var kvp in GetInstanceProperties()) - { - if (!this.Properties.ContainsKey(kvp.Key)) - { - yield return kvp.Key; - } - } - } - - - /// - /// Try to retrieve a member by name first from instance properties - /// followed by the collection entries. - /// - /// - /// - /// - public override bool TryGetMember(GetMemberBinder binder, out object result) + foreach (var kvp in this.Properties.Keys) + { + yield return kvp; + } + + foreach (var kvp in GetInstanceProperties()) + { + if (!this.Properties.ContainsKey(kvp.Key)) + { + yield return kvp.Key; + } + } + } + + + /// + /// Try to retrieve a member by name first from instance properties + /// followed by the collection entries. + /// + /// + /// + /// + public override bool TryGetMember(GetMemberBinder binder, out object result) { - return TryGetMemberCore(binder.Name, out result); + return TryGetMemberCore(binder.Name, out result); } - protected virtual bool TryGetMemberCore(string name, out object result) - { - result = null; - bool exists = false; - - // first check the Properties collection for member - if (Properties.ContainsKey(name)) - { - result = Properties[name]; - exists = true; - } - - // Next check for public properties via Reflection - if (!exists && _instance != null) - { - try - { - exists = GetProperty(_instance, name, out result); - } - catch { } - } - - // Falsy check - if (_returnNullWhenFalsy && result != null && !CommonHelper.IsTruthy(result)) - { - result = null; - } - - // failed to retrieve a property - return exists; - } - - - /// - /// Property setter implementation tries to retrieve value from instance - /// first then into this object - /// - /// - /// - /// - public override bool TrySetMember(SetMemberBinder binder, object value) + protected virtual bool TryGetMemberCore(string name, out object result) { - return TrySetMemberCore(binder.Name, value); + result = null; + bool exists = false; + + // first check the Properties collection for member + if (Properties.ContainsKey(name)) + { + result = Properties[name]; + exists = true; + } + + // Next check for public properties via Reflection + if (!exists && _instance != null) + { + try + { + exists = GetProperty(_instance, name, out result); + } + catch { } + } + + // Falsy check + if (_returnNullWhenFalsy && result != null && !CommonHelper.IsTruthy(result)) + { + result = null; + } + + // failed to retrieve a property + return exists; + } + + + /// + /// Property setter implementation tries to retrieve value from instance + /// first then into this object + /// + /// + /// + /// + public override bool TrySetMember(SetMemberBinder binder, object value) + { + return TrySetMemberCore(binder.Name, value); + } + + protected virtual bool TrySetMemberCore(string name, object value) + { + // first check to see if there's a dictionary entry to set + if (Properties.ContainsKey(name)) + { + Properties[name] = value; + return true; + } + + // Check to see if there's a native property to set + if (_instance != null) + { + try + { + bool result = SetProperty(_instance, name, value); + if (result) + return true; + } + catch { } + } + + // no match - set or add to dictionary + Properties[name] = value; + return true; } - protected virtual bool TrySetMemberCore(string name, object value) - { - // first check to see if there's a dictionary entry to set - if (Properties.ContainsKey(name)) - { - Properties[name] = value; - return true; - } - - // Check to see if there's a native property to set - if (_instance != null) - { - try - { - bool result = SetProperty(_instance, name, value); - if (result) - return true; - } - catch { } - } - - // no match - set or add to dictionary - Properties[name] = value; - return true; - } - - /// - /// Dynamic invocation method. Currently allows only for Reflection based - /// operation (no ability to add methods dynamically). - /// - /// - /// - /// - public void Override(string name, object value = null) - { - Guard.NotEmpty(name, nameof(name)); - Properties[name] = value; - } - - /// - /// Dynamic invocation method. Currently allows only for Reflection based - /// operation (no ability to add methods dynamically). - /// - /// - /// - /// - /// - public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) + /// + /// Dynamic invocation method. Currently allows only for Reflection based + /// operation (no ability to add methods dynamically). + /// + /// + /// + /// + public void Override(string name, object value = null) + { + Guard.NotEmpty(name, nameof(name)); + Properties[name] = value; + } + + /// + /// Dynamic invocation method. Currently allows only for Reflection based + /// operation (no ability to add methods dynamically). + /// + /// + /// + /// + /// + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { if (_instance != null) { @@ -306,15 +303,15 @@ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, o /// protected bool GetProperty(object instance, string name, out object result) { - if (GetInstanceProperties().TryGetValue(name, out var fastProp)) - { - result = fastProp.GetValue(instance ?? this); - return true; - } + if (GetInstanceProperties().TryGetValue(name, out var fastProp)) + { + result = fastProp.GetValue(instance ?? this); + return true; + } - result = null; + result = null; return false; - } + } /// /// Reflection helper method to set a property value @@ -325,13 +322,13 @@ protected bool GetProperty(object instance, string name, out object result) /// protected bool SetProperty(object instance, string name, object value) { - if (GetInstanceProperties().TryGetValue(name, out var fastProp)) - { - fastProp.SetValue(instance ?? this, value); - return true; - } + if (GetInstanceProperties().TryGetValue(name, out var fastProp)) + { + fastProp.SetValue(instance ?? this, value); + return true; + } - return false; + return false; } /// @@ -349,7 +346,7 @@ protected bool InvokeMethod(object instance, string name, object[] args, out obj if (mi != null) { result = mi.Invoke(instance ?? this, args); - return true; + return true; } result = null; @@ -357,90 +354,87 @@ protected bool InvokeMethod(object instance, string name, object[] args, out obj } - /// - /// Convenience method that provides a string Indexer - /// to the Properties collection AND the strongly typed - /// properties of the object by name. - /// - /// // dynamic - /// exp["Address"] = "112 nowhere lane"; - /// // strong - /// var name = exp["StronglyTypedProperty"] as string; - /// - /// - /// The getter checks the Properties dictionary first - /// then looks in PropertyInfo for properties. - /// The setter checks the instance properties before - /// checking the Properties dictionary. - /// - /// - /// - /// - public object this[string key] - { - get - { - if (!TryGetMemberCore(key, out var result)) - { - throw new KeyNotFoundException(); - } - - return result; - } - set - { - TrySetMemberCore(key, value); - } - } - - - /// - /// Returns all properties - /// - /// - /// - public IEnumerable> GetProperties(bool includeInstanceProperties = false) + /// + /// Convenience method that provides a string Indexer + /// to the Properties collection AND the strongly typed + /// properties of the object by name. + /// + /// // dynamic + /// exp["Address"] = "112 nowhere lane"; + /// // strong + /// var name = exp["StronglyTypedProperty"] as string; + /// + /// + /// The getter checks the Properties dictionary first + /// then looks in PropertyInfo for properties. + /// The setter checks the instance properties before + /// checking the Properties dictionary. + /// + /// + /// + /// + public object this[string key] { - foreach (var kvp in this.Properties) - { - yield return kvp; - } - - if (includeInstanceProperties) + get + { + if (!TryGetMemberCore(key, out var result)) + { + throw new KeyNotFoundException(); + } + + return result; + } + set => TrySetMemberCore(key, value); + } + + + /// + /// Returns all properties + /// + /// + /// + public IEnumerable> GetProperties(bool includeInstanceProperties = false) + { + foreach (var kvp in this.Properties) + { + yield return kvp; + } + + if (includeInstanceProperties) { foreach (var prop in GetInstanceProperties().Values) - { - if (!this.Properties.ContainsKey(prop.Name)) - { - yield return new KeyValuePair(prop.Name, prop.GetValue(_instance)); - } - } + { + if (!this.Properties.ContainsKey(prop.Name)) + { + yield return new KeyValuePair(prop.Name, prop.GetValue(_instance)); + } + } } } - private IDictionary GetInstanceProperties() - { - if (_instance == null) - { - return EmptyProps; - } + private IDictionary GetInstanceProperties() + { + if (_instance == null) + { + return EmptyProps; + } - if (_instanceProps == null) - { - var props = FastProperty.GetProperties(_instance) as IDictionary; + if (_instanceProps == null) + { + var props = FastProperty.GetProperties(_instance.GetType()) as IDictionary; - if (_optMembers != null) - { - props = props - .Where(x => _optMethod == MemberOptMethod.Allow ? _optMembers.Contains(x.Key) : !_optMembers.Contains(x.Key)) - .ToDictionary(x => x.Key, x => x.Value); - } + if (_optMembers != null) + { + props = props + .Where(x => _optMethod == MemberOptMethod.Allow ? _optMembers.Contains(x.Key) : !_optMembers.Contains(x.Key)) + .ToDictionary(x => x.Key, x => x.Value); + } - _instanceProps = props; - } + _instanceProps = props; + } - return _instanceProps; - } + return _instanceProps; + } /// /// Checks whether a property exists in the Property collection @@ -463,128 +457,98 @@ public bool Contains(KeyValuePair item, bool includeInstanceProp public bool Contains(string propertyName, bool includeInstanceProperties = false) { if (Properties.ContainsKey(propertyName)) - { - return true; - } + { + return true; + } if (includeInstanceProperties) { - return GetInstanceProperties().ContainsKey(propertyName); - } + return GetInstanceProperties().ContainsKey(propertyName); + } return false; } - #region IDictionary - - ICollection IDictionary.Keys - { - get - { - return GetProperties(true).Select(x => x.Key).AsReadOnly(); - } - } - - ICollection IDictionary.Values - { - get - { - return GetProperties(true).Select(x => x.Value).AsReadOnly(); - } - } - - int ICollection>.Count - { - get - { - return GetDynamicMemberNames().Count(); - } - } - - bool ICollection>.IsReadOnly - { - get - { - return false; - } - } - - object IDictionary.this[string key] - { - get - { - return this[key]; - } - - set - { - this[key] = value; - } - } - - bool IDictionary.ContainsKey(string key) - { - return Contains(key, true); - } - - void IDictionary.Add(string key, object value) - { - throw new NotImplementedException(); - } - - bool IDictionary.Remove(string key) - { - throw new NotImplementedException(); - } - - public bool TryGetValue(string key, out object value) - { - value = null; - - if (this.Contains(key, true)) - { - value = this[key]; - return true; - } - - return false; - } - - void ICollection>.Add(KeyValuePair item) - { - throw new NotImplementedException(); - } - - void ICollection>.Clear() - { - throw new NotImplementedException(); - } - - bool ICollection>.Contains(KeyValuePair item) - { - return Contains(item.Key, true); - } - - void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) - { - throw new NotImplementedException(); - } - - bool ICollection>.Remove(KeyValuePair item) - { - throw new NotImplementedException(); - } - - IEnumerator> IEnumerable>.GetEnumerator() - { - return GetProperties(true).GetEnumerator(); + #region IDictionary + + ICollection IDictionary.Keys => GetProperties(true).Select(x => x.Key).AsReadOnly(); + + ICollection IDictionary.Values => GetProperties(true).Select(x => x.Value).AsReadOnly(); + + int ICollection>.Count => GetDynamicMemberNames().Count(); + + bool ICollection>.IsReadOnly => false; + + object IDictionary.this[string key] + { + get => this[key]; + + set => this[key] = value; + } + + bool IDictionary.ContainsKey(string key) + { + return Contains(key, true); + } + + void IDictionary.Add(string key, object value) + { + throw new NotImplementedException(); + } + + bool IDictionary.Remove(string key) + { + throw new NotImplementedException(); + } + + public bool TryGetValue(string key, out object value) + { + value = null; + + if (this.Contains(key, true)) + { + value = this[key]; + return true; + } + + return false; + } + + void ICollection>.Add(KeyValuePair item) + { + throw new NotImplementedException(); + } + + void ICollection>.Clear() + { + throw new NotImplementedException(); + } + + bool ICollection>.Contains(KeyValuePair item) + { + return Contains(item.Key, true); } - IEnumerator IEnumerable.GetEnumerator() - { - return GetProperties(true).GetEnumerator(); - } + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + bool ICollection>.Remove(KeyValuePair item) + { + throw new NotImplementedException(); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return GetProperties(true).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetProperties(true).GetEnumerator(); + } - #endregion - } + #endregion + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/ComponentModel/IMapper.cs b/src/Libraries/SmartStore.Core/ComponentModel/IMapper.cs new file mode 100644 index 0000000000..5b56d46ef9 --- /dev/null +++ b/src/Libraries/SmartStore.Core/ComponentModel/IMapper.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SmartStore.ComponentModel +{ + public interface IMapper + where TFrom : class + where TTo : class + { + /// + /// Maps the specified source object into the destination object. + /// + /// The source object to map from. + /// The destination object to map to. + void Map(TFrom from, TTo to); + } + + public static class IMapperExtensions + { + /// + /// Maps the specified source object to a new object with a type of . + /// + /// The type of the source object. + /// The type of the destination object. + /// The mapper. + /// The source object. + /// The mapped object of type . + public static TTo Map(this IMapper mapper, TFrom from) + where TFrom : class + where TTo : class, new() + { + Guard.NotNull(mapper, nameof(mapper)); + Guard.NotNull(from, nameof(from)); + + var to = Activator.CreateInstance(); + mapper.Map(from, to); + return to; + } + + /// + /// Maps a collection of into an array of . + /// + /// The type of the source objects. + /// The type of the destination objects. + /// The mapper. + /// The source collection. + /// An array of . + public static TTo[] MapArray(this IMapper mapper, IEnumerable from) + where TFrom : class + where TTo : class, new() + { + Guard.NotNull(mapper, nameof(mapper)); + Guard.NotNull(from, nameof(from)); + + return from.Select(x => mapper.Map(x)).ToArray(); + } + + /// + /// Maps a collection of into a list of . + /// + /// The type of the source objects. + /// The type of the destination objects. + /// The mapper. + /// The source collection. + /// A list of . + public static List MapList(this IMapper mapper, IEnumerable from) + where TFrom : class + where TTo : class, new() + { + Guard.NotNull(mapper, nameof(mapper)); + Guard.NotNull(from, nameof(from)); + + return from.Select(x => mapper.Map(x)).ToList(); + } + + /// + /// Maps a collection of into a list of . + /// + /// The type of the source objects. + /// The type of the destination objects. + /// The mapper. + /// The source collection. + /// The destination collection. + /// A list of . + public static void MapCollection(this IMapper mapper, IEnumerable from, ICollection to) + where TFrom : class + where TTo : class, new() + { + Guard.NotNull(mapper, nameof(mapper)); + Guard.NotNull(from, nameof(from)); + Guard.NotNull(to, nameof(to)); + + to.Clear(); + to.AddRange(from.Select(x => mapper.Map(x))); + } + } +} diff --git a/src/Libraries/SmartStore.Core/ComponentModel/MapperFactory.cs b/src/Libraries/SmartStore.Core/ComponentModel/MapperFactory.cs new file mode 100644 index 0000000000..a198bd7d0c --- /dev/null +++ b/src/Libraries/SmartStore.Core/ComponentModel/MapperFactory.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using SmartStore.Core.Infrastructure; + +namespace SmartStore.ComponentModel +{ + public static class MapperFactory + { + private static IDictionary _mapperTypes = null; + private readonly static object _lock = new object(); + + private static void EnsureInitialized() + { + if (_mapperTypes == null) + { + lock (_lock) + { + if (_mapperTypes == null) + { + _mapperTypes = new Dictionary(); + + var typeFinder = EngineContext.Current.Resolve(); + var mapperTypes = typeFinder.FindClassesOfType(typeof(IMapper<,>)); + + RegisterMappers(mapperTypes.ToArray()); + } + } + } + } + + /// + /// For testing purposes + /// + internal static void RegisterMappers(params Type[] mapperTypes) + { + if (_mapperTypes == null) + { + _mapperTypes = new Dictionary(); + } + + foreach (var type in mapperTypes) + { + foreach (var intface in type.GetInterfaces()) + { + intface.IsSubClass(typeof(IMapper<,>), out var impl); + var genericArguments = impl.GetGenericArguments(); + var typePair = new TypePair(genericArguments[0], genericArguments[1]); + _mapperTypes.Add(typePair, type); + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TTo Map(TFrom from) + where TFrom : class + where TTo : class, new() + { + return GetMapper().Map(from); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Map(TFrom from, TTo to) + where TFrom : class + where TTo : class + { + GetMapper().Map(from, to); + } + + public static IMapper GetMapper() + where TFrom : class + where TTo : class + { + EnsureInitialized(); + + var key = new TypePair(typeof(TFrom), typeof(TTo)); + + var implType = _mapperTypes.Get(key); + if (implType != null) + { + var instance = EngineContext.Current.ContainerManager.ResolveUnregistered(implType); + return (IMapper)instance; + } + + return new GenericMapper(); + } + + class TypePair : Tuple + { + public TypePair(Type fromType, Type toType) + : base(fromType, toType) + { + } + + public Type FromType => base.Item1; + public Type ToType => base.Item2; + } + + class GenericMapper : IMapper + where TFrom : class + where TTo : class + { + public void Map(TFrom from, TTo to) + { + MiniMapper.Map(from, to); + } + } + } +} diff --git a/src/Libraries/SmartStore.Core/ComponentModel/MiniMapper.cs b/src/Libraries/SmartStore.Core/ComponentModel/MiniMapper.cs index 40e6412f8f..ad3c8cc012 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/MiniMapper.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/MiniMapper.cs @@ -1,114 +1,118 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Reflection; using System.Globalization; +using System.Linq; +using SmartStore.Core; using SmartStore.Utilities; namespace SmartStore.ComponentModel { - /// - /// A very simple object mapper utility which tries to map properties of the same name between two objects. - /// If matched properties has different types, the mapper tries to convert them. - /// If conversion fails, the property is skipped (no exception is thrown). - /// MiniMapper cannot handle sequence and predefined types. - /// - public static class MiniMapper - { - public static TTo Map(TFrom from, CultureInfo culture = null) - where TFrom : class - where TTo : class - { - var to = Map(from, typeof(TTo), culture); - return (TTo)to; - } - - //public static void Map(TFrom from, TTo to, CultureInfo culture = null) - // where TFrom : class - // where TTo : class - //{ - // Map((object)from, (object)to, culture); - //} - - public static object Map(TFrom from, Type toType, CultureInfo culture = null) - where TFrom : class - { - Guard.NotNull(toType, nameof(toType)); - Guard.HasDefaultConstructor(toType); - - var target = Activator.CreateInstance(toType); - - Map(from, target, culture); - return target; - } - - public static void Map(TFrom from, TTo to, CultureInfo culture = null) - where TFrom : class - where TTo : class - { - Guard.NotNull(from, nameof(from)); - Guard.NotNull(to, nameof(to)); - - if (object.ReferenceEquals(from, to)) - { - // Cannot map the same instance - return; - } - - var fromType = from.GetType(); - var toType = to.GetType(); - - ValidateType(fromType); - ValidateType(toType); - - if (culture == null) - { - culture = CultureInfo.CurrentCulture; - } - - var toProps = GetFastPropertiesFor(toType).ToArray(); - - foreach (var toProp in toProps) - { - var fromProp = FastProperty.GetProperty(fromType, toProp.Name, PropertyCachingStrategy.Uncached); - if (fromProp == null) - { - continue; - } - - object value = null; - try - { - // Get the value from source instance and try to convert it to target props type - value = fromProp.GetValue(from).Convert(toProp.Property.PropertyType, culture); - - // Set it - toProp.SetValue(to, value); - } - catch { } - } - } - - private static IEnumerable GetFastPropertiesFor(Type type) - { - return FastProperty.GetCandidateProperties(type) - .Select(pi => FastProperty.GetProperty(pi, PropertyCachingStrategy.Uncached)) - .Where(pi => pi.IsPublicSettable); - } - - private static void ValidateType(Type type) - { - if (type.IsPredefinedType()) - { - throw new InvalidOperationException("Mapping from or to predefined types is not possible. Type was: {0}".FormatInvariant(type.FullName)); - } - - if (type.IsSequenceType()) - { - throw new InvalidOperationException("Mapping from or to sequence types is not possible. Type was: {0}".FormatInvariant(type.FullName)); - } - } - } + /// + /// A very simple object mapper utility which tries to map properties of the same name between two objects. + /// If matched properties has different types, the mapper tries to convert them. + /// If conversion fails, the property is skipped (no exception is thrown). + /// MiniMapper cannot handle sequence and predefined types. + /// + public static class MiniMapper + { + public static TTo Map(TFrom from, CultureInfo culture = null) + where TFrom : class + where TTo : class, new() + { + Guard.NotNull(from, nameof(from)); + + if (TryMap(from, from.GetType(), typeof(TTo), culture, out var result)) + { + return (TTo)result; + } + + return default(TTo); + } + + public static object Map(TFrom from, Type toType, CultureInfo culture = null) + where TFrom : class + { + Guard.NotNull(from, nameof(from)); + Guard.NotNull(toType, nameof(toType)); + Guard.HasDefaultConstructor(toType); + + TryMap(from, from.GetType(), toType, culture, out var result); + + return result; + } + + public static void Map(TFrom from, TTo to, CultureInfo culture = null) + where TFrom : class + where TTo : class + { + Guard.NotNull(from, nameof(from)); + Guard.NotNull(to, nameof(to)); + + MapComplex(from, to, from.GetType(), culture ?? CultureInfo.CurrentCulture); + } + + private static bool TryMap(object from, Type fromType, Type toType, CultureInfo culture, out object result) + { + if (CommonHelper.TryConvert(from, toType, culture, out result)) + { + return true; + } + + if (fromType.IsPlainObjectType() && toType.IsPlainObjectType()) + { + result = Activator.CreateInstance(toType); + MapComplex(from, result, fromType, culture); + return true; + } + + return false; + } + + private static void MapComplex(object from, object to, Type fromType, CultureInfo culture) + { + if (object.ReferenceEquals(from, to)) + { + // Cannot map same instance or null source + return; + } + + var toType = to.GetType(); + var isEntityMapping = typeof(BaseEntity).IsAssignableFrom(toType); + var toProps = GetFastPropertiesFor(toType).ToArray(); + + foreach (var toProp in toProps) + { + var fromProp = FastProperty.GetProperty(fromType, toProp.Name, PropertyCachingStrategy.Cached); + if (fromProp == null) + { + continue; + } + + var fromPropType = fromProp.Property.PropertyType; + var toPropType = toProp.Property.PropertyType; + var sourceValue = fromProp.GetValue(from); + + if (isEntityMapping && (fromPropType == typeof(int) || fromPropType == typeof(int?)) && toPropType == typeof(int?) && object.Equals(0, sourceValue) && toProp.Name.EndsWith("Id")) + { + // TODO: This is more a hack than a proper solution. Find a more generic way to convert int FK properties... + // Special mapper, that avoids DbUpdate exceptions in cases where + // optional (nullable) int FK properties are 0 instead of "null" + // after mapping model > entity. + toProp.SetValue(to, null); + } + else if (TryMap(sourceValue, fromPropType, toPropType, culture, out var targetValue)) + { + // Set it + toProp.SetValue(to, targetValue); + } + } + } + + private static FastProperty[] GetFastPropertiesFor(Type type) + { + return FastProperty.GetProperties(type, PropertyCachingStrategy.Uncached) + .Values + .Where(pi => pi.IsPublicSettable) + .ToArray(); + } + } } diff --git a/src/Libraries/SmartStore.Core/ComponentModel/PropertyBag.cs b/src/Libraries/SmartStore.Core/ComponentModel/PropertyBag.cs index 328ef76ce0..8b5d5e0db9 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/PropertyBag.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/PropertyBag.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -using System.Xml.Serialization; -using System.Xml; using System.Reflection; +using System.Xml; +using System.Xml.Serialization; namespace SmartStore.ComponentModel { diff --git a/src/Libraries/SmartStore.Core/ComponentModel/SerializationUtils.cs b/src/Libraries/SmartStore.Core/ComponentModel/SerializationUtils.cs index e36995facd..5228dbcd56 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/SerializationUtils.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/SerializationUtils.cs @@ -32,12 +32,12 @@ #endregion using System; +using System.Diagnostics; using System.IO; +using System.Runtime.Serialization.Formatters.Binary; using System.Text; using System.Xml; using System.Xml.Serialization; -using System.Runtime.Serialization.Formatters.Binary; -using System.Diagnostics; namespace SmartStore.ComponentModel { @@ -187,25 +187,25 @@ public static bool SerializeObject(object instance, out string xmlResultString, return true; } - /// - /// Serializes an object instance to a file. - /// - /// the object instance to serialize - /// - /// - /// - public static bool SerializeObject(object instance, out byte[] resultBuffer, bool throwExceptions = false) + /// + /// Serializes an object instance to a file. + /// + /// the object instance to serialize + /// + /// + /// + public static bool SerializeObject(object instance, out byte[] resultBuffer, bool throwExceptions = false) { bool retVal = true; - resultBuffer = null; + resultBuffer = null; - var ms = new MemoryStream(); + var ms = new MemoryStream(); try { var serializer = new BinaryFormatter(); serializer.Serialize(ms, instance); - resultBuffer = ms.ToArray(); - } + resultBuffer = ms.ToArray(); + } catch (Exception ex) { Debug.Write("SerializeObject failed with : " + ex.GetBaseException().Message, "West Wind"); @@ -216,7 +216,7 @@ public static bool SerializeObject(object instance, out byte[] resultBuffer, boo } finally { - ms.Close(); + ms.Close(); } return retVal; diff --git a/src/Libraries/SmartStore.Core/ComponentModel/SmartContractResolver.cs b/src/Libraries/SmartStore.Core/ComponentModel/SmartContractResolver.cs new file mode 100644 index 0000000000..1a4d448f7f --- /dev/null +++ b/src/Libraries/SmartStore.Core/ComponentModel/SmartContractResolver.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using Newtonsoft.Json.Serialization; + +namespace SmartStore.ComponentModel +{ + public class SmartContractResolver : DefaultContractResolver + { + protected override IValueProvider CreateMemberValueProvider(MemberInfo member) + { + if (member is PropertyInfo pi) + { + return new FastPropertyValueProvider(pi); + } + + return base.CreateMemberValueProvider(member); + } + + public static SmartContractResolver Instance { get; } = new SmartContractResolver(); + } +} diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/BooleanConverter.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/BooleanConverter.cs index 11e6c6c941..94b39225b3 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/BooleanConverter.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/BooleanConverter.cs @@ -2,85 +2,74 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.ComponentModel { - [SuppressMessage("ReSharper", "CanBeReplacedWithTryCastAndCheckForNull")] - public class BooleanConverter : TypeConverterBase - { - private readonly HashSet _trueValues; - private readonly HashSet _falseValues; + [SuppressMessage("ReSharper", "CanBeReplacedWithTryCastAndCheckForNull")] + public class BooleanConverter : DefaultTypeConverter + { + private readonly HashSet _trueValues; + private readonly HashSet _falseValues; - public BooleanConverter(string[] trueValues, string[] falseValues) - : base(typeof(bool)) - { - _trueValues = new HashSet(trueValues, StringComparer.OrdinalIgnoreCase); - _falseValues = new HashSet(falseValues, StringComparer.OrdinalIgnoreCase); - } + public BooleanConverter(string[] trueValues, string[] falseValues) + : base(typeof(bool)) + { + _trueValues = new HashSet(trueValues, StringComparer.OrdinalIgnoreCase); + _falseValues = new HashSet(falseValues, StringComparer.OrdinalIgnoreCase); + } - public ICollection TrueValues - { - get { return _trueValues; } - } + public ICollection TrueValues => _trueValues; - public ICollection FalseValues - { - get { return _falseValues; } - } + public ICollection FalseValues => _falseValues; - public override object ConvertFrom(CultureInfo culture, object value) - { - if (value is short) - { - if ((short)value == 0) - { - return false; - } - if ((short)value == 1) - { - return true; - } - } + public override object ConvertFrom(CultureInfo culture, object value) + { + if (value is short) + { + if ((short)value == 0) + { + return false; + } + if ((short)value == 1) + { + return true; + } + } - if (value is string) - { - var str = (string)value; + if (value is string) + { + var str = (string)value; - bool b; - if (bool.TryParse(str, out b)) - { - return b; - } + if (bool.TryParse(str, out var b)) + { + return b; + } - short sh; - if (short.TryParse(str, out sh)) - { - if (sh == 0) - { - return false; - } - if (sh == 1) - { - return true; - } - } + if (short.TryParse(str, out var sh)) + { + if (sh == 0) + { + return false; + } + if (sh == 1) + { + return true; + } + } - str = (str.NullEmpty() ?? string.Empty).Trim(); - if (_trueValues.Contains(str)) - { - return true; - } + str = (str.NullEmpty() ?? string.Empty).Trim(); + if (_trueValues.Contains(str)) + { + return true; + } - if (_falseValues.Contains(str)) - { - return false; - } - } + if (_falseValues.Contains(str)) + { + return false; + } + } - return base.ConvertFrom(culture, value); - } - } + return base.ConvertFrom(culture, value); + } + } } diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/DateTimeConverter.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/DateTimeConverter.cs index b357dcddb8..56eba9b9b2 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/DateTimeConverter.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/DateTimeConverter.cs @@ -1,110 +1,105 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.ComponentModel { - [SuppressMessage("ReSharper", "CanBeReplacedWithTryCastAndCheckForNull")] - public class DateTimeConverter : TypeConverterBase - { - public DateTimeConverter() - : base(typeof(DateTime)) - { - } - - public override bool CanConvertFrom(Type type) - { - return type == typeof(string) - || type == typeof(long) - || type == typeof(double) - || type == typeof(TimeSpan) - || base.CanConvertFrom(type); - } - - public override bool CanConvertTo(Type type) - { - return type == typeof(string) - || type == typeof(long) - || type == typeof(double) - || type == typeof(DateTimeOffset) - || type == typeof(TimeSpan) - || base.CanConvertTo(type); - } - - public override object ConvertFrom(CultureInfo culture, object value) - { - if (value is TimeSpan) - { - var span = (TimeSpan)value; - return new DateTime(span.Ticks); - } - - if (value is string) - { - var str = (string)value; - - DateTime time; - if (DateTime.TryParse(str, culture, DateTimeStyles.None, out time)) - { - return time; - } - - long lng; - if (long.TryParse(str, NumberStyles.None, culture, out lng)) - { - return lng.FromUnixTime(); - } - - double dbl; - if (double.TryParse(str, NumberStyles.AllowDecimalPoint, culture, out dbl)) - { - return DateTime.FromOADate(dbl); - } + [SuppressMessage("ReSharper", "CanBeReplacedWithTryCastAndCheckForNull")] + public class DateTimeConverter : DefaultTypeConverter + { + public DateTimeConverter() + : base(typeof(DateTime)) + { + } + + public override bool CanConvertFrom(Type type) + { + return type == typeof(string) + || type == typeof(long) + || type == typeof(double) + || type == typeof(TimeSpan) + || base.CanConvertFrom(type); + } + + public override bool CanConvertTo(Type type) + { + return type == typeof(string) + || type == typeof(long) + || type == typeof(double) + || type == typeof(DateTimeOffset) + || type == typeof(TimeSpan) + || base.CanConvertTo(type); + } + + public override object ConvertFrom(CultureInfo culture, object value) + { + if (value is TimeSpan) + { + var span = (TimeSpan)value; + return new DateTime(span.Ticks); } - if (value is long) - { - return ((long)value).FromUnixTime(); - } + if (value is string) + { + var str = (string)value; + + DateTime time; + if (DateTime.TryParse(str, culture, DateTimeStyles.None, out time)) + { + return time; + } + + long lng; + if (long.TryParse(str, NumberStyles.None, culture, out lng)) + { + return lng.FromUnixTime(); + } + + double dbl; + if (double.TryParse(str, NumberStyles.AllowDecimalPoint, culture, out dbl)) + { + return DateTime.FromOADate(dbl); + } + } - if (value is double) - { - return DateTime.FromOADate((double)value); - } + if (value is long) + { + return ((long)value).FromUnixTime(); + } - return base.ConvertFrom(culture, value); - } + if (value is double) + { + return DateTime.FromOADate((double)value); + } - public override object ConvertTo(CultureInfo culture, string format, object value, Type to) - { - var time = (DateTime)value; + return base.ConvertFrom(culture, value); + } - if (to == typeof(DateTimeOffset)) - { - return new DateTimeOffset(time); - } + public override object ConvertTo(CultureInfo culture, string format, object value, Type to) + { + var time = (DateTime)value; - if (to == typeof(TimeSpan)) - { - return new TimeSpan(time.Ticks); - } + if (to == typeof(DateTimeOffset)) + { + return new DateTimeOffset(time); + } - if (to == typeof(double)) - { - return time.ToOADate(); - } + if (to == typeof(TimeSpan)) + { + return new TimeSpan(time.Ticks); + } - if (to == typeof(long)) - { - return time.ToUnixTime(); - } + if (to == typeof(double)) + { + return time.ToOADate(); + } + + if (to == typeof(long)) + { + return time.ToUnixTime(); + } - return base.ConvertTo(culture, format, value, to); + return base.ConvertTo(culture, format, value, to); } - } + } } diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/DefaultTypeConverter.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/DefaultTypeConverter.cs new file mode 100644 index 0000000000..a2ca535d1f --- /dev/null +++ b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/DefaultTypeConverter.cs @@ -0,0 +1,111 @@ +using System; +using System.ComponentModel; +using System.Globalization; + +namespace SmartStore.ComponentModel +{ + public class DefaultTypeConverter : ITypeConverter + { + private readonly Lazy _systemConverter; + private readonly Type _type; + private readonly bool _typeIsConvertible; + private readonly bool _typeIsEnum; + + public DefaultTypeConverter(Type type) + { + Guard.NotNull(type, nameof(type)); + + _type = type; + _typeIsConvertible = typeof(IConvertible).IsAssignableFrom(type); + _typeIsEnum = type.IsEnum; + _systemConverter = new Lazy(() => TypeDescriptor.GetConverter(type), true); + } + + public TypeConverter SystemConverter + { + get + { + if (_type == typeof(object)) + { + return null; + } + + return _systemConverter.Value; + } + } + + public virtual bool CanConvertFrom(Type type) + { + // Use Convert.ChangeType if both types are IConvertible + if (_typeIsConvertible && typeof(IConvertible).IsAssignableFrom(type)) + { + return true; + } + + if (SystemConverter != null) + { + return SystemConverter.CanConvertFrom(type); + } + + return false; + } + + public virtual bool CanConvertTo(Type type) + { + // Use Convert.ChangeType if both types are IConvertible + if (_typeIsConvertible && typeof(IConvertible).IsAssignableFrom(type)) + { + return true; + } + + if (type == typeof(string)) + { + return true; + } + + if (SystemConverter != null) + { + return SystemConverter.CanConvertTo(type); + } + + return false; + } + + public virtual object ConvertFrom(CultureInfo culture, object value) + { + // Use Convert.ChangeType if both types are IConvertible + if (!_typeIsEnum && _typeIsConvertible && value is IConvertible) + { + return Convert.ChangeType(value, _type, culture); + } + + if (SystemConverter != null) + { + return SystemConverter.ConvertFrom(null, culture, value); + } + + throw Error.InvalidCast(value.GetType(), _type); + } + + public virtual object ConvertTo(CultureInfo culture, string format, object value, Type to) + { + // Use Convert.ChangeType if both types are IConvertible + if (!_typeIsEnum && _typeIsConvertible && value != null && typeof(IConvertible).IsAssignableFrom(to)) + { + return Convert.ChangeType(value, to, culture); + } + + if (SystemConverter != null) + { + return SystemConverter.ConvertTo(null, culture, value, to); + } + + if (value == null) + { + return string.Empty; + } + + return value.ToString(); + } + } +} diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/DictionaryConverter.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/DictionaryConverter.cs index a7e3c60887..d94b7933e7 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/DictionaryConverter.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/DictionaryConverter.cs @@ -7,58 +7,64 @@ namespace SmartStore.ComponentModel { - public class DictionaryTypeConverter : TypeConverterBase where T : IDictionary - { - public DictionaryTypeConverter() - : base(typeof(object)) - { - } + public class DictionaryTypeConverter : DefaultTypeConverter where T : IDictionary + { + public DictionaryTypeConverter() + : base(typeof(object)) + { + } - public override bool CanConvertFrom(Type type) - { - return !type.IsPredefinedType() && type.IsClass; - } + public override bool CanConvertFrom(Type type) + { + return type.IsPlainObjectType() || type.IsAnonymous(); + } - public override bool CanConvertTo(Type type) - { - return !type.IsPredefinedType() && DictionaryConverter.CanCreateType(type); - } + public override bool CanConvertTo(Type type) + { + return DictionaryConverter.CanCreateType(type); + } - public override object ConvertFrom(CultureInfo culture, object value) - { - // Obj > Dict - var dict = CommonHelper.ObjectToDictionary(value); - var to = typeof(T); + public override object ConvertFrom(CultureInfo culture, object value) + { + // Obj > Dict + var dict = CommonHelper.ObjectToDictionary(value); + var to = typeof(T); - if (to == typeof(RouteValueDictionary)) - { - return new RouteValueDictionary(dict); - } - if (to == typeof(Dictionary)) - { - return (Dictionary)dict; - } - else if (to == typeof(ExpandoObject)) - { - var expando = new ExpandoObject(); - expando.Merge(dict); - return expando; - } - else - { - return dict; - } - } + if (to == typeof(RouteValueDictionary)) + { + return new RouteValueDictionary(dict); + } + else if (to == typeof(Dictionary)) + { + return (Dictionary)dict; + } + else if (to == typeof(ExpandoObject)) + { + var expando = new ExpandoObject(); + expando.Merge(dict); + return expando; + } + else if (to == typeof(HybridExpando)) + { + var expando = new HybridExpando(); + expando.Merge(dict); + return expando; + } + else + { + return dict; + } + } - public override object ConvertTo(CultureInfo culture, string format, object value, Type to) - { - // Dict > Obj - if (value is IDictionary dict) - { - return DictionaryConverter.CreateAndPopulate(to, dict, out var problems); - } + public override object ConvertTo(CultureInfo culture, string format, object value, Type to) + { + // Dict > Obj + if (value is IDictionary dict) + { + return DictionaryConverter.CreateAndPopulate(to, dict, out _); + } - return base.ConvertTo(culture, format, value, to); - } - } + return base.ConvertTo(culture, format, value, to); + } + } } diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/EmailAddressConverter.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/EmailAddressConverter.cs index 5a2c45755b..ca5bf0f744 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/EmailAddressConverter.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/EmailAddressConverter.cs @@ -4,40 +4,40 @@ namespace SmartStore.ComponentModel { - public class EmailAddressConverter : TypeConverterBase - { - public EmailAddressConverter() : base(typeof(object)) - { - } + public class EmailAddressConverter : DefaultTypeConverter + { + public EmailAddressConverter() : base(typeof(object)) + { + } - public override bool CanConvertFrom(Type type) - { - return type == typeof(string); - } + public override bool CanConvertFrom(Type type) + { + return type == typeof(string); + } - public override bool CanConvertTo(Type type) - { - return type == typeof(string); - } + public override bool CanConvertTo(Type type) + { + return type == typeof(string); + } - public override object ConvertFrom(CultureInfo culture, object value) - { - if (value is string str && str.HasValue()) - { - return new EmailAddress(str); - } + public override object ConvertFrom(CultureInfo culture, object value) + { + if (value is string str && str.HasValue()) + { + return new EmailAddress(str); + } - return base.ConvertFrom(culture, value); - } + return base.ConvertFrom(culture, value); + } - public override object ConvertTo(CultureInfo culture, string format, object value, Type to) - { - if (to == typeof(string) && value is EmailAddress address) - { - return address.ToString(); - } + public override object ConvertTo(CultureInfo culture, string format, object value, Type to) + { + if (to == typeof(string) && value is EmailAddress address) + { + return address.ToString(); + } - return base.ConvertTo(culture, format, value, to); - } - } + return base.ConvertTo(culture, format, value, to); + } + } } diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/EnumerableConverter.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/EnumerableConverter.cs index ae60b58488..4f0e17c8ac 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/EnumerableConverter.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/EnumerableConverter.cs @@ -1,153 +1,191 @@ using System; +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; +using SmartStore.Collections; namespace SmartStore.ComponentModel { - [SuppressMessage("ReSharper", "CanBeReplacedWithTryCastAndCheckForNull")] - public class EnumerableConverter : TypeConverterBase - { - private readonly Func, object> _activator; - private readonly ITypeConverter _elementTypeConverter; - - public EnumerableConverter(Type sequenceType) - : base(typeof(object)) - { - _elementTypeConverter = TypeConverterFactory.GetConverter(); - if (_elementTypeConverter == null) - throw new InvalidOperationException("No type converter exists for type " + typeof(T).FullName); - - _activator = CreateSequenceActivator(sequenceType); + [SuppressMessage("ReSharper", "CanBeReplacedWithTryCastAndCheckForNull")] + public class EnumerableConverter : DefaultTypeConverter + { + private readonly Func, object> _activator; + private readonly ITypeConverter _elementTypeConverter; + + public EnumerableConverter(Type sequenceType) + : base(typeof(object)) + { + _elementTypeConverter = TypeConverterFactory.GetConverter(); + if (_elementTypeConverter == null) + throw new InvalidOperationException("No type converter exists for type " + typeof(T).FullName); + + _activator = CreateSequenceActivator(sequenceType); } - [SuppressMessage("ReSharper", "RedundantLambdaSignatureParentheses")] - private static Func, object> CreateSequenceActivator(Type sequenceType) - { - // Default is IEnumerable - Func, object> activator = null; - - var t = sequenceType; - - if (t == typeof(IEnumerable)) - { - activator = (x) => x; - } - else if (t == (typeof(IReadOnlyCollection)) || t == (typeof(IReadOnlyList))) - { - activator = (x) => x.AsReadOnly(); - } - else if (t.IsAssignableFrom(typeof(List))) - { - activator = (x) => x.ToList(); - } - else if (t.IsAssignableFrom(typeof(HashSet))) - { - activator = (x) => new HashSet(x); - } - else if (t.IsAssignableFrom(typeof(Queue))) - { - activator = (x) => new Queue(x); - } - else if (t.IsAssignableFrom(typeof(Stack))) - { - activator = (x) => new Stack(x); - } - else if (t.IsAssignableFrom(typeof(LinkedList))) - { - activator = (x) => new LinkedList(x); - } - else if (t.IsAssignableFrom(typeof(ConcurrentBag))) - { - activator = (x) => new ConcurrentBag(x); - } - else if (t.IsAssignableFrom(typeof(ArraySegment))) - { - activator = (x) => new ArraySegment(x.ToArray()); - } - - if (activator == null) - { - throw new InvalidOperationException("'{0}' is not a valid type for enumerable conversion.".FormatInvariant(sequenceType.FullName)); - } - - return activator; - } - - public override bool CanConvertFrom(Type type) - { - return type == typeof(string) || typeof(IConvertible).IsAssignableFrom(type); - } - - public override bool CanConvertTo(Type type) - { - return type == typeof(string); - } - - public override object ConvertFrom(CultureInfo culture, object value) - { - if (value == null) - { - return _activator(Enumerable.Empty()); - } - - if (value is string) - { - var items = GetStringArray((string)value); - - var result = items - .Select(x => _elementTypeConverter.ConvertFrom(culture, x)) - .Where(x => x != null) - .Cast(); - - return _activator(result); - } - - if (value is IConvertible) - { - var result2 = (new object[] { value }) - .Select(x => Convert.ChangeType(value, typeof(T))) - .Cast(); - - return _activator(result2); - } - - return base.ConvertFrom(culture, value); - } - - public override object ConvertTo(CultureInfo culture, string format, object value, Type to) - { - if (to == typeof(string)) - { - string result = string.Empty; - - if (value is IEnumerable enumerable) - { - // We don't use string.Join() because it doesn't support invariant culture - foreach (var token in enumerable) - { - var str = _elementTypeConverter.ConvertTo(culture, format, token, typeof(string)); - result += str + ","; - } + [SuppressMessage("ReSharper", "RedundantLambdaSignatureParentheses")] + private static Func, object> CreateSequenceActivator(Type sequenceType) + { + // Default is IEnumerable + Func, object> activator = null; + + var t = sequenceType; + + if (t == typeof(IEnumerable)) + { + activator = (x) => x; + } + else if (t == typeof(T[])) + { + activator = (x) => x.ToArray(); + } + else if (t == (typeof(IReadOnlyCollection)) || t == (typeof(IReadOnlyList))) + { + activator = (x) => x.AsReadOnly(); + } + else if (t.IsAssignableFrom(typeof(List))) + { + activator = (x) => x.ToList(); + } + else if (t.IsAssignableFrom(typeof(HashSet))) + { + activator = (x) => new HashSet(x); + } + else if (t.IsAssignableFrom(typeof(Queue))) + { + activator = (x) => new Queue(x); + } + else if (t.IsAssignableFrom(typeof(Stack))) + { + activator = (x) => new Stack(x); + } + else if (t.IsAssignableFrom(typeof(LinkedList))) + { + activator = (x) => new LinkedList(x); + } + else if (t.IsAssignableFrom(typeof(ConcurrentBag))) + { + activator = (x) => new ConcurrentBag(x); + } + else if (t.IsAssignableFrom(typeof(SyncedCollection))) + { + activator = (x) => new SyncedCollection(new List(x)); + } + else if (t.IsAssignableFrom(typeof(ArraySegment))) + { + activator = (x) => new ArraySegment(x.ToArray()); + } + + if (activator == null) + { + throw new InvalidOperationException("'{0}' is not a valid type for enumerable conversion.".FormatInvariant(sequenceType.FullName)); + } + + return activator; + } - result = result.TrimEnd(','); - } + public override bool CanConvertFrom(Type type) + { + if (type.IsSequenceType(out var elementType)) + { + return elementType.IsAssignableFrom(typeof(T)) + || _elementTypeConverter.CanConvertFrom(elementType) + || TypeConverterFactory.GetConverter(elementType).CanConvertTo(typeof(T)); + } - return result; - } + return type == typeof(string) || typeof(IConvertible).IsAssignableFrom(type); + } - return base.ConvertTo(culture, format, value, to); - } + public override bool CanConvertTo(Type type) + { + return type == typeof(string) && _elementTypeConverter.CanConvertTo(type); + } - protected virtual string[] GetStringArray(string input) - { - var result = input.SplitSafe(null); + public override object ConvertFrom(CultureInfo culture, object value) + { + if (value == null) + { + return _activator(Enumerable.Empty()); + } + + var items = value as IEnumerable; + + if (value is string str) + { + items = GetStringArray(str); + } + else if (value is IConvertible c) + { + var result2 = (new object[] { value }) + .Select(x => Convert.ChangeType(value, typeof(T))) + .Cast(); + + return _activator(result2); + } + + if (items != null) + { + items.GetType().IsSequenceType(out var elementType); + var elementConverter = _elementTypeConverter; + var isOtherConverter = false; + var isAssignable = elementType.IsAssignableFrom(typeof(T)); + if (!isAssignable && !elementConverter.CanConvertFrom(elementType)) + { + elementConverter = TypeConverterFactory.GetConverter(elementType); + isOtherConverter = true; + } + + var result = items + .Cast() + .Select(x => + { + if (isAssignable) + return x; + + return !isOtherConverter + ? elementConverter.ConvertFrom(culture, x) + : elementConverter.ConvertTo(culture, null, x, elementType); + }) + .Where(x => x != null) + .Cast(); + + return _activator(result); + } + + return base.ConvertFrom(culture, value); + } - Array.ForEach(result, s => s.Trim()); + public override object ConvertTo(CultureInfo culture, string format, object value, Type to) + { + if (to == typeof(string)) + { + string result = string.Empty; + + if (value is IEnumerable enumerable) + { + // We don't use string.Join() because it doesn't support invariant culture + foreach (var token in enumerable) + { + var str = _elementTypeConverter.ConvertTo(culture, format, token, typeof(string)); + result += str + ","; + } - return result; - } - } + result = result.TrimEnd(','); + } + + return result; + } + + return base.ConvertTo(culture, format, value, to); + } + + protected virtual string[] GetStringArray(string input) + { + var result = input.SplitSafe(null); + Array.ForEach(result, s => s.Trim()); + return result; + } + } } diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/ITypeConverter.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/ITypeConverter.cs index c76ecba218..9323aa70b0 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/ITypeConverter.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/ITypeConverter.cs @@ -3,81 +3,81 @@ namespace SmartStore.ComponentModel { - /// - /// Converts objects. - /// - public interface ITypeConverter - { - /// - /// Returns whether this converter can convert an object of the given type to the type of this converter. - /// - /// A Type that represents the type you want to convert from. - /// true if this converter can perform the conversion; otherwise, false. - bool CanConvertFrom(Type type); + /// + /// Converts objects. + /// + public interface ITypeConverter + { + /// + /// Returns whether this converter can convert an object of the given type to the type of this converter. + /// + /// A Type that represents the type you want to convert from. + /// true if this converter can perform the conversion; otherwise, false. + bool CanConvertFrom(Type type); - /// - /// Returns whether this converter can convert the object to the specified type. - /// - /// A Type that represents the type you want to convert to. - /// true if this converter can perform the conversion; otherwise, false. - bool CanConvertTo(Type type); + /// + /// Returns whether this converter can convert the object to the specified type. + /// + /// A Type that represents the type you want to convert to. + /// true if this converter can perform the conversion; otherwise, false. + bool CanConvertTo(Type type); - /// - /// Converts the given value to the type of this converter. - /// - /// The to use as the current culture. If null is passed, the invariant culture is assumed. - /// The object to convert. - /// An object that represents the converted value. - object ConvertFrom(CultureInfo culture, object value); + /// + /// Converts the given value to the type of this converter. + /// + /// The to use as the current culture. If null is passed, the invariant culture is assumed. + /// The object to convert. + /// An object that represents the converted value. + object ConvertFrom(CultureInfo culture, object value); - /// - /// Converts the given value object to the specified type, using the arguments. - /// - /// The to use as the current culture. If null is passed, the invariant culture is assumed. - /// A standard or custom format expression. - /// The object to convert. - /// The type to convert the value parameter to. - /// An Object that represents the converted value. - object ConvertTo(CultureInfo culture, string format, object value, Type to); - } + /// + /// Converts the given value object to the specified type, using the arguments. + /// + /// The to use as the current culture. If null is passed, the invariant culture is assumed. + /// A standard or custom format expression. + /// The object to convert. + /// The type to convert the value parameter to. + /// An Object that represents the converted value. + object ConvertTo(CultureInfo culture, string format, object value, Type to); + } - public static class ITypeConverterExtensions - { - public static object ConvertFrom(this ITypeConverter converter, object value) - { - return converter.ConvertFrom(CultureInfo.InvariantCulture, value); - } + public static class ITypeConverterExtensions + { + public static object ConvertFrom(this ITypeConverter converter, object value) + { + return converter.ConvertFrom(CultureInfo.InvariantCulture, value); + } - public static object ConvertTo(this ITypeConverter converter, object value, Type to) - { - return converter.ConvertTo(CultureInfo.InvariantCulture, null, value, to); - } + public static object ConvertTo(this ITypeConverter converter, object value, Type to) + { + return converter.ConvertTo(CultureInfo.InvariantCulture, null, value, to); + } - public static object SafeConvert(this ITypeConverter converter, string value) - { - try - { - if (converter != null && value.HasValue() && converter.CanConvertFrom(typeof(string))) - { - return converter.ConvertFrom(value); - } - } - catch (Exception exc) - { - exc.Dump(); - } + public static object SafeConvert(this ITypeConverter converter, string value) + { + try + { + if (converter != null && value.HasValue() && converter.CanConvertFrom(typeof(string))) + { + return converter.ConvertFrom(value); + } + } + catch (Exception exc) + { + exc.Dump(); + } - return null; - } + return null; + } - public static bool IsEqual(this ITypeConverter converter, string value, object compareWith) - { - object convertedObject = converter.SafeConvert(value); + public static bool IsEqual(this ITypeConverter converter, string value, object compareWith) + { + object convertedObject = converter.SafeConvert(value); - if (convertedObject != null && compareWith != null) - return convertedObject.Equals(compareWith); + if (convertedObject != null && compareWith != null) + return convertedObject.Equals(compareWith); - return false; - } - } + return false; + } + } } diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/JObjectConverter.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/JObjectConverter.cs new file mode 100644 index 0000000000..8ab85e5724 --- /dev/null +++ b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/JObjectConverter.cs @@ -0,0 +1,55 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace SmartStore.ComponentModel +{ + [SuppressMessage("ReSharper", "CanBeReplacedWithTryCastAndCheckForNull")] + public class JObjectConverter : DefaultTypeConverter + { + public JObjectConverter() + : base(typeof(JObject)) + { + } + + public override bool CanConvertFrom(Type type) + { + return type == typeof(string) + || type.IsPlainObjectType() + || type.IsAnonymous(); + + } + + public override bool CanConvertTo(Type type) + { + return type == typeof(string); + } + + public override object ConvertFrom(CultureInfo culture, object value) + { + if (value is string json) + { + return JObject.Parse(json); + } + + if (value != null) + { + return JObject.FromObject(value); + } + + return base.ConvertFrom(culture, value); + } + + public override object ConvertTo(CultureInfo culture, string format, object value, Type to) + { + if (value is JObject jobj && to == typeof(string)) + { + return jobj.ToString(Formatting.Indented); + } + + return base.ConvertTo(culture, format, value, to); + } + } +} diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/NullableConverter.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/NullableConverter.cs index 8488e2aa3e..ee97942c56 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/NullableConverter.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/NullableConverter.cs @@ -1,120 +1,116 @@ using System; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.ComponentModel { - [SuppressMessage("ReSharper", "TryCastAlwaysSucceeds")] - public class NullableConverter : TypeConverterBase - { - private readonly bool _underlyingTypeIsConvertible; - - public NullableConverter(Type type) - : base(type) - { - NullableType = type; - UnderlyingType = Nullable.GetUnderlyingType(type); - - if (UnderlyingType == null) - { - throw Error.Argument("type", "Type is not a nullable type."); - } - - _underlyingTypeIsConvertible = typeof(IConvertible).IsAssignableFrom(UnderlyingType) && !UnderlyingType.IsEnum; - UnderlyingTypeConverter = TypeConverterFactory.GetConverter(UnderlyingType); - } - - public Type NullableType - { - get; - private set; - } - - public Type UnderlyingType - { - get; - private set; - } - - public ITypeConverter UnderlyingTypeConverter - { - get; - private set; - } - - public override bool CanConvertFrom(Type type) - { - if (type == this.UnderlyingType) - { - return true; - } - - if (UnderlyingTypeConverter.CanConvertFrom(type)) - { - return true; - } - - if (_underlyingTypeIsConvertible && type != typeof(string) && typeof(IConvertible).IsAssignableFrom(type)) - { - return true; - } - - return false; - } - - public override bool CanConvertTo(Type type) - { - //Console.WriteLine("NullableConverter can convert to {0}: {1}".FormatInvariant(type.Name, UnderlyingTypeConverter.CanConvertTo(type))); - - if (type == this.UnderlyingType) - { - return true; - } - - return UnderlyingTypeConverter.CanConvertTo(type); - } - - public override object ConvertFrom(CultureInfo culture, object value) - { - if ((value == null) || (value.GetType() == this.UnderlyingType)) - { - return value; - } - - if ((value is string) && string.IsNullOrEmpty(value as string)) - { - return null; - } - - if (_underlyingTypeIsConvertible && !(value is string) && value is IConvertible) - { - // num > num? - return Convert.ChangeType(value, UnderlyingType, culture); - } - - return UnderlyingTypeConverter.ConvertFrom(culture, value); - } - - public override object ConvertTo(CultureInfo culture, string format, object value, Type to) - { - if ((to == this.UnderlyingType) && this.NullableType.IsInstanceOfType(value)) - { - return value; - } - - if (value == null) - { - if (to == typeof(string)) - { - return string.Empty; - } - } - - return UnderlyingTypeConverter.ConvertTo(culture, format, value, to); - } - } + [SuppressMessage("ReSharper", "TryCastAlwaysSucceeds")] + public class NullableConverter : DefaultTypeConverter + { + private readonly bool _elementTypeIsConvertible; + + internal NullableConverter(Type type, Type elementType) + : base(type) + { + NullableType = type; + ElementType = elementType ?? Nullable.GetUnderlyingType(type); + + if (ElementType == null) + { + throw Error.Argument("type", "Type is not a nullable type."); + } + + _elementTypeIsConvertible = typeof(IConvertible).IsAssignableFrom(ElementType) && !ElementType.IsEnum; + ElementTypeConverter = TypeConverterFactory.GetConverter(ElementType); + } + + public Type NullableType + { + get; + private set; + } + + public Type ElementType + { + get; + private set; + } + + public ITypeConverter ElementTypeConverter + { + get; + private set; + } + + public override bool CanConvertFrom(Type type) + { + if (type == this.ElementType) + { + return true; + } + + if (ElementTypeConverter.CanConvertFrom(type)) + { + return true; + } + + if (_elementTypeIsConvertible && type != typeof(string) && typeof(IConvertible).IsAssignableFrom(type)) + { + return true; + } + + return false; + } + + public override bool CanConvertTo(Type type) + { + //Console.WriteLine("NullableConverter can convert to {0}: {1}".FormatInvariant(type.Name, UnderlyingTypeConverter.CanConvertTo(type))); + + if (type == this.ElementType) + { + return true; + } + + return ElementTypeConverter.CanConvertTo(type); + } + + public override object ConvertFrom(CultureInfo culture, object value) + { + if ((value == null) || (value.GetType() == this.ElementType)) + { + return value; + } + + if ((value is string) && string.IsNullOrEmpty(value as string)) + { + return null; + } + + if (_elementTypeIsConvertible && !(value is string) && value is IConvertible) + { + // num > num? + return Convert.ChangeType(value, ElementType, culture); + } + + return ElementTypeConverter.ConvertFrom(culture, value); + } + + public override object ConvertTo(CultureInfo culture, string format, object value, Type to) + { + if ((to == this.ElementType) && this.NullableType.IsInstanceOfType(value)) + { + return value; + } + + if (value == null) + { + if (to == typeof(string)) + { + return string.Empty; + } + } + + return ElementTypeConverter.ConvertTo(culture, format, value, to); + } + } } diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/ProductBundleDataConverter.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/ProductBundleDataConverter.cs index 599431116f..e091abe8af 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/ProductBundleDataConverter.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/ProductBundleDataConverter.cs @@ -3,83 +3,82 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; -using System.Linq; using System.Text; using System.Xml.Serialization; using SmartStore.Core.Domain.Catalog; namespace SmartStore.ComponentModel { - [SuppressMessage("ReSharper", "TryCastAlwaysSucceeds")] - public class ProductBundleDataConverter : TypeConverterBase - { - private readonly bool _forList; + [SuppressMessage("ReSharper", "TryCastAlwaysSucceeds")] + public class ProductBundleDataConverter : DefaultTypeConverter + { + private readonly bool _forList; - public ProductBundleDataConverter(bool forList) - : base(typeof(object)) - { - _forList = forList; - } + public ProductBundleDataConverter(bool forList) + : base(typeof(object)) + { + _forList = forList; + } - public override bool CanConvertFrom(Type type) - { - return type == typeof(string); - } + public override bool CanConvertFrom(Type type) + { + return type == typeof(string); + } - public override bool CanConvertTo(Type type) - { - return type == typeof(string); - } + public override bool CanConvertTo(Type type) + { + return type == typeof(string); + } - public override object ConvertFrom(CultureInfo culture, object value) - { - if (value is string) - { - object result = null; - string str = value as string; - if (!string.IsNullOrEmpty(str)) - { - try - { - using (var tr = new StringReader(str)) - { - var serializer = new XmlSerializer(_forList ? typeof(List) : typeof(ProductBundleItemOrderData)); - result = serializer.Deserialize(tr); - } - } - catch - { - // xml error - } - } + public override object ConvertFrom(CultureInfo culture, object value) + { + if (value is string) + { + object result = null; + string str = value as string; + if (!string.IsNullOrEmpty(str)) + { + try + { + using (var tr = new StringReader(str)) + { + var serializer = new XmlSerializer(_forList ? typeof(List) : typeof(ProductBundleItemOrderData)); + result = serializer.Deserialize(tr); + } + } + catch + { + // xml error + } + } - return result; - } + return result; + } - return base.ConvertFrom(culture, value); - } + return base.ConvertFrom(culture, value); + } - public override object ConvertTo(CultureInfo culture, string format, object value, Type to) - { - if (to == typeof(string)) - { - if (value != null && (value is ProductBundleItemOrderData || value is IList)) - { - var sb = new StringBuilder(); - using (var tw = new StringWriter(sb)) - { - var serializer = new XmlSerializer(_forList ? typeof(List) : typeof(ProductBundleItemOrderData)); - serializer.Serialize(tw, value); - return sb.ToString(); - } - } - else - { - return string.Empty; - } - } + public override object ConvertTo(CultureInfo culture, string format, object value, Type to) + { + if (to == typeof(string)) + { + if (value != null && (value is ProductBundleItemOrderData || value is IList)) + { + var sb = new StringBuilder(); + using (var tw = new StringWriter(sb)) + { + var serializer = new XmlSerializer(_forList ? typeof(List) : typeof(ProductBundleItemOrderData)); + serializer.Serialize(tw, value); + return sb.ToString(); + } + } + else + { + return string.Empty; + } + } - return base.ConvertTo(culture, format, value, to); - } - } + return base.ConvertTo(culture, format, value, to); + } + } } diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/ShippingOptionConverter.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/ShippingOptionConverter.cs index 54df72e96b..dbb6b5f094 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/ShippingOptionConverter.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/ShippingOptionConverter.cs @@ -9,75 +9,75 @@ namespace SmartStore.ComponentModel { - [SuppressMessage("ReSharper", "TryCastAlwaysSucceeds")] - public class ShippingOptionConverter : TypeConverterBase - { - private readonly bool _forList; + [SuppressMessage("ReSharper", "TryCastAlwaysSucceeds")] + public class ShippingOptionConverter : DefaultTypeConverter + { + private readonly bool _forList; - public ShippingOptionConverter(bool forList) - : base(typeof(object)) - { - _forList = forList; + public ShippingOptionConverter(bool forList) + : base(typeof(object)) + { + _forList = forList; } - public override bool CanConvertFrom(Type type) - { - return type == typeof(string); - } + public override bool CanConvertFrom(Type type) + { + return type == typeof(string); + } - public override bool CanConvertTo(Type type) - { - return type == typeof(string); - } + public override bool CanConvertTo(Type type) + { + return type == typeof(string); + } - public override object ConvertFrom(CultureInfo culture, object value) - { - if (value is string str) - { - object result = null; - if (!String.IsNullOrEmpty(str)) - { - try - { - using (var tr = new StringReader(str)) - { - var serializer = new XmlSerializer(_forList ? typeof(List) : typeof(ShippingOption)); - result = serializer.Deserialize(tr); - } - } - catch - { - // xml error - } - } + public override object ConvertFrom(CultureInfo culture, object value) + { + if (value is string str) + { + object result = null; + if (!String.IsNullOrEmpty(str)) + { + try + { + using (var tr = new StringReader(str)) + { + var serializer = new XmlSerializer(_forList ? typeof(List) : typeof(ShippingOption)); + result = serializer.Deserialize(tr); + } + } + catch + { + // xml error + } + } - return result; - } + return result; + } - return base.ConvertFrom(culture, value); - } + return base.ConvertFrom(culture, value); + } - public override object ConvertTo(CultureInfo culture, string format, object value, Type to) - { - if (to == typeof(string)) - { - if (value != null && (value is ShippingOption || value is IList)) - { - var sb = new StringBuilder(); - using (var tw = new StringWriter(sb)) - { - var serializer = new XmlSerializer(_forList ? typeof(List) : typeof(ShippingOption)); - serializer.Serialize(tw, value); - return sb.ToString(); - } - } - else - { - return string.Empty; - } - } + public override object ConvertTo(CultureInfo culture, string format, object value, Type to) + { + if (to == typeof(string)) + { + if (value != null && (value is ShippingOption || value is IList)) + { + var sb = new StringBuilder(); + using (var tw = new StringWriter(sb)) + { + var serializer = new XmlSerializer(_forList ? typeof(List) : typeof(ShippingOption)); + serializer.Serialize(tw, value); + return sb.ToString(); + } + } + else + { + return string.Empty; + } + } - return base.ConvertTo(culture, format, value, to); - } - } + return base.ConvertTo(culture, format, value, to); + } + } } diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/TimeSpanConverter.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/TimeSpanConverter.cs index 656355d8e8..c6ec04e190 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/TimeSpanConverter.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/TimeSpanConverter.cs @@ -1,67 +1,62 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.ComponentModel { - [SuppressMessage("ReSharper", "CanBeReplacedWithTryCastAndCheckForNull")] - public class TimeSpanConverter : TypeConverterBase - { - public TimeSpanConverter() - : base(typeof(TimeSpan)) - { - } - - public override bool CanConvertFrom(Type type) - { - return type == typeof(string) - || type == typeof(DateTime) - || base.CanConvertFrom(type); - } - - public override object ConvertFrom(CultureInfo culture, object value) - { - if (value is DateTime) - { - var time = (DateTime)value; - return new TimeSpan(time.Ticks); - } - - if (value is string) - { - var str = (string)value; - - TimeSpan span; - if (TimeSpan.TryParse(str, culture, out span)) - { - return span; - } - - long lng; - if (long.TryParse(str, NumberStyles.None, culture, out lng)) - { - return new TimeSpan(lng.FromUnixTime().Ticks); - } - - double dbl; - if (double.TryParse(str, NumberStyles.None, culture, out dbl)) - { - return new TimeSpan(DateTime.FromOADate(dbl).Ticks); - } - } - - try - { - return (TimeSpan)System.Convert.ChangeType(value, typeof(TimeSpan), culture); - } - catch { } - - return base.ConvertFrom(culture, value); - } - } + [SuppressMessage("ReSharper", "CanBeReplacedWithTryCastAndCheckForNull")] + public class TimeSpanConverter : DefaultTypeConverter + { + public TimeSpanConverter() + : base(typeof(TimeSpan)) + { + } + + public override bool CanConvertFrom(Type type) + { + return type == typeof(string) + || type == typeof(DateTime) + || base.CanConvertFrom(type); + } + + public override object ConvertFrom(CultureInfo culture, object value) + { + if (value is DateTime) + { + var time = (DateTime)value; + return new TimeSpan(time.Ticks); + } + + if (value is string) + { + var str = (string)value; + + TimeSpan span; + if (TimeSpan.TryParse(str, culture, out span)) + { + return span; + } + + long lng; + if (long.TryParse(str, NumberStyles.None, culture, out lng)) + { + return new TimeSpan(lng.FromUnixTime().Ticks); + } + + double dbl; + if (double.TryParse(str, NumberStyles.None, culture, out dbl)) + { + return new TimeSpan(DateTime.FromOADate(dbl).Ticks); + } + } + + try + { + return (TimeSpan)System.Convert.ChangeType(value, typeof(TimeSpan), culture); + } + catch { } + + return base.ConvertFrom(culture, value); + } + } } diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/TypeConverterAdapter.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/TypeConverterAdapter.cs deleted file mode 100644 index b7ed897169..0000000000 --- a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/TypeConverterAdapter.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.ComponentModel; -using System.Globalization; - -namespace SmartStore.ComponentModel -{ - internal class TypeConverterAdapter : TypeConverterBase - { - private readonly TypeConverter _converter; - - public TypeConverterAdapter(TypeConverter converter) - : base(typeof(object)) - { - _converter = converter; - } - - public override bool CanConvertFrom(Type type) - { - return _converter != null && _converter.CanConvertFrom(type); - } - - public override bool CanConvertTo(Type type) - { - return _converter != null && _converter.CanConvertTo(type); - } - - public override object ConvertFrom(CultureInfo culture, object value) - { - return _converter.ConvertFrom(null, culture, value); - } - - public override object ConvertTo(CultureInfo culture, string format, object value, Type to) - { - return _converter.ConvertTo(null, culture, value, to); - } - } -} diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/TypeConverterBase.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/TypeConverterBase.cs deleted file mode 100644 index 24c7c382cd..0000000000 --- a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/TypeConverterBase.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.ComponentModel; -using System.Globalization; - -namespace SmartStore.ComponentModel -{ - public abstract class TypeConverterBase : ITypeConverter - { - private readonly Lazy _systemConverter; - private readonly Type _type; - - protected TypeConverterBase(Type type) - { - Guard.NotNull(type, nameof(type)); - - _type = type; - _systemConverter = new Lazy(() => TypeDescriptor.GetConverter(type), true); - } - - public TypeConverter SystemConverter - { - get - { - if (_type == typeof(object)) - { - return null; - } - - return _systemConverter.Value; - } - } - - public virtual bool CanConvertFrom(Type type) - { - return SystemConverter != null && SystemConverter.CanConvertFrom(type); - } - - public virtual bool CanConvertTo(Type type) - { - return type == typeof(string) || (SystemConverter != null && SystemConverter.CanConvertTo(type)); - } - - public virtual object ConvertFrom(CultureInfo culture, object value) - { - if (SystemConverter != null) - { - return SystemConverter.ConvertFrom(null, culture, value); - } - - throw Error.InvalidCast(value.GetType(), _type); - } - - public virtual object ConvertTo(CultureInfo culture, string format, object value, Type to) - { - if (SystemConverter != null) - { - return SystemConverter.ConvertTo(null, culture, value, to); - } - - if (value == null) - { - return string.Empty; - } - - return value.ToString(); - } - } -} diff --git a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/TypeConverterFactory.cs b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/TypeConverterFactory.cs index ee6c50e474..4db9fd8d90 100644 --- a/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/TypeConverterFactory.cs +++ b/src/Libraries/SmartStore.Core/ComponentModel/TypeConversion/TypeConverterFactory.cs @@ -1,127 +1,117 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.ComponentModel; using System.Dynamic; using System.Web.Routing; +using Newtonsoft.Json.Linq; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Shipping; using SmartStore.Core.Email; namespace SmartStore.ComponentModel { - public static class TypeConverterFactory - { - private static readonly ConcurrentDictionary _typeConverters = new ConcurrentDictionary(); - - static TypeConverterFactory() - { - CreateDefaultConverters(); - } - - private static void CreateDefaultConverters() - { - _typeConverters.TryAdd(typeof(DateTime), new DateTimeConverter()); - _typeConverters.TryAdd(typeof(TimeSpan), new TimeSpanConverter()); - _typeConverters.TryAdd(typeof(bool), new BooleanConverter( - new [] { "yes", "y", "on", "wahr" }, - new [] { "no", "n", "off", "falsch" })); - - ITypeConverter converter = new ShippingOptionConverter(true); - _typeConverters.TryAdd(typeof(IList), converter); - _typeConverters.TryAdd(typeof(List), converter); - _typeConverters.TryAdd(typeof(ShippingOption), new ShippingOptionConverter(false)); - - converter = new ProductBundleDataConverter(true); - _typeConverters.TryAdd(typeof(IList), converter); - _typeConverters.TryAdd(typeof(List), converter); - _typeConverters.TryAdd(typeof(ProductBundleItemOrderData), new ProductBundleDataConverter(false)); - - converter = new DictionaryTypeConverter>(); - _typeConverters.TryAdd(typeof(IDictionary), converter); - _typeConverters.TryAdd(typeof(Dictionary), converter); - _typeConverters.TryAdd(typeof(RouteValueDictionary), new DictionaryTypeConverter()); - _typeConverters.TryAdd(typeof(ExpandoObject), new DictionaryTypeConverter()); - - _typeConverters.TryAdd(typeof(EmailAddress), new EmailAddressConverter()); - } - - public static void RegisterConverter(ITypeConverter typeConverter) - { - RegisterConverter(typeof(T), typeConverter); - } - - public static void RegisterConverter(Type type, ITypeConverter typeConverter) - { - Guard.NotNull(type, nameof(type)); - Guard.NotNull(typeConverter, nameof(typeConverter)); - - _typeConverters.TryAdd(type, typeConverter); + public static class TypeConverterFactory + { + private static readonly ConcurrentDictionary _typeConverters = new ConcurrentDictionary(); + + static TypeConverterFactory() + { + CreateDefaultConverters(); + } + + private static void CreateDefaultConverters() + { + _typeConverters.TryAdd(typeof(DateTime), new DateTimeConverter()); + _typeConverters.TryAdd(typeof(TimeSpan), new TimeSpanConverter()); + _typeConverters.TryAdd(typeof(bool), new BooleanConverter( + new[] { "yes", "y", "on", "wahr" }, + new[] { "no", "n", "off", "falsch" })); + + ITypeConverter converter = new ShippingOptionConverter(true); + _typeConverters.TryAdd(typeof(IList), converter); + _typeConverters.TryAdd(typeof(List), converter); + _typeConverters.TryAdd(typeof(ShippingOption), new ShippingOptionConverter(false)); + + converter = new ProductBundleDataConverter(true); + _typeConverters.TryAdd(typeof(IList), converter); + _typeConverters.TryAdd(typeof(List), converter); + _typeConverters.TryAdd(typeof(ProductBundleItemOrderData), new ProductBundleDataConverter(false)); + + converter = new DictionaryTypeConverter>(); + _typeConverters.TryAdd(typeof(IDictionary), converter); + _typeConverters.TryAdd(typeof(Dictionary), converter); + _typeConverters.TryAdd(typeof(RouteValueDictionary), new DictionaryTypeConverter()); + _typeConverters.TryAdd(typeof(ExpandoObject), new DictionaryTypeConverter()); + _typeConverters.TryAdd(typeof(HybridExpando), new DictionaryTypeConverter()); + + _typeConverters.TryAdd(typeof(EmailAddress), new EmailAddressConverter()); + _typeConverters.TryAdd(typeof(JObject), new JObjectConverter()); + } + + public static IReadOnlyCollection Converters => _typeConverters.Values.AsReadOnly(); + + public static void RegisterConverter(ITypeConverter typeConverter) + { + RegisterConverter(typeof(T), typeConverter); + } + + public static void RegisterConverter(Type type, ITypeConverter typeConverter) + { + Guard.NotNull(type, nameof(type)); + Guard.NotNull(typeConverter, nameof(typeConverter)); + + _typeConverters.TryAdd(type, typeConverter); + } + + public static ITypeConverter RemoveConverter(ITypeConverter typeConverter) + { + return RemoveConverter(typeof(T)); + } + + public static ITypeConverter RemoveConverter(Type type) + { + Guard.NotNull(type, nameof(type)); + + _typeConverters.TryRemove(type, out var converter); + return converter; + } + + public static ITypeConverter GetConverter() + { + return GetConverter(typeof(T)); } - public static ITypeConverter RemoveConverter(ITypeConverter typeConverter) - { - return RemoveConverter(typeof(T)); - } - - public static ITypeConverter RemoveConverter(Type type) - { - Guard.NotNull(type, nameof(type)); - - _typeConverters.TryRemove(type, out var converter); - return converter; - } - - public static ITypeConverter GetConverter() - { - return GetConverter(typeof(T)); - } - - public static ITypeConverter GetConverter(object component) - { - Guard.NotNull(component, nameof(component)); - - return GetConverter(component.GetType()); - } - - public static ITypeConverter GetConverter(Type type) - { - if (type == null) - throw new ArgumentNullException(nameof(type)); - - if (_typeConverters.TryGetValue(type, out var converter)) - { - return converter; - } - - var isGenericType = type.IsGenericType; - if (isGenericType) - { - var definition = type.GetGenericTypeDefinition(); - - // Nullables - if (definition == typeof(Nullable<>)) - { - converter = new NullableConverter(type); - RegisterConverter(type, converter); - return converter; - } - - // Sequence types - var genericArgs = type.GetGenericArguments(); - var isEnumerable = genericArgs.Length == 1 && type.IsSubClass(typeof(IEnumerable<>)); - if (isEnumerable) - { - converter = (ITypeConverter)Activator.CreateInstance(typeof(EnumerableConverter<>).MakeGenericType(genericArgs[0]), type); - RegisterConverter(type, converter); - return converter; - } - } - - // default fallback - converter = new TypeConverterAdapter(TypeDescriptor.GetConverter(type)); - RegisterConverter(type, converter); - return converter; - } - } + public static ITypeConverter GetConverter(object component) + { + Guard.NotNull(component, nameof(component)); + + return GetConverter(component.GetType()); + } + + public static ITypeConverter GetConverter(Type type) + { + Guard.NotNull(type, nameof(type)); + + return _typeConverters.GetOrAdd(type, Get); + + ITypeConverter Get(Type t) + { + // Nullable types + if (type.IsNullable(out Type elementType)) + { + return new NullableConverter(type, elementType); + } + + // Sequence types + if (type.IsSequenceType(out elementType)) + { + var converter = (ITypeConverter)Activator.CreateInstance(typeof(EnumerableConverter<>).MakeGenericType(elementType), type); + return converter; + } + + // Default fallback + return new DefaultTypeConverter(type); + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Configuration/ISettingService.cs b/src/Libraries/SmartStore.Core/Configuration/ISettingService.cs index 2f5f65575c..3ab801f744 100644 --- a/src/Libraries/SmartStore.Core/Configuration/ISettingService.cs +++ b/src/Libraries/SmartStore.Core/Configuration/ISettingService.cs @@ -3,131 +3,155 @@ using System.Linq.Expressions; using SmartStore.Core.Configuration; using SmartStore.Core.Domain.Configuration; -using SmartStore.Core.Plugins; namespace SmartStore.Services.Configuration { + public enum SaveSettingResult + { + Unchanged, + Modified, + Inserted, + Deleted + } + /// /// Setting service interface /// public partial interface ISettingService { - /// - /// Creates a unit of work in which cache eviction is suppressed - /// - /// Specifies whether the cache should be evicted completely on batch disposal - /// A disposable unit of work - IDisposable BeginScope(bool clearCache = true); - - /// - /// Gets a value indicating whether settings have changed during a request, making cache eviction necessary. - /// - /// - /// Cache eviction sets this member to false - /// - bool HasChanges { get; } - - /// + /// + /// Creates a unit of work in which cache eviction is suppressed + /// + /// Specifies whether the cache should be evicted completely on batch disposal + /// A disposable unit of work + IDisposable BeginScope(bool clearCache = true); + + /// + /// Gets a value indicating whether settings have changed during a request, making cache eviction necessary. + /// + /// + /// Cache eviction sets this member to false + /// + bool HasChanges { get; } + + /// /// Gets a setting by identifier /// /// Setting identifier /// Setting Setting GetSettingById(int settingId); + /// + /// Gets a setting by key + /// + /// Unique setting key + /// Store identifier + /// Setting + Setting GetSettingEntityByKey(string key, int storeId = 0); + /// /// Get setting value by key /// /// Type /// Key /// Default value - /// Store identifier - /// A value indicating whether a shared (for all stores) value should be loaded if a value specific for a certain is not found + /// Store identifier + /// A value indicating whether a shared (for all stores) value should be loaded if a value specific for a certain store is not found /// Setting value - T GetSettingByKey(string key, T defaultValue = default(T), int storeId = 0, bool loadSharedValueIfNotFound = false); - - /// - /// Gets all settings - /// - /// Settings - IList GetAllSettings(); - - /// - /// Determines whether a setting exists - /// - /// Entity type - /// Property type - /// Settings - /// Key selector - /// Store identifier - /// true -setting exists; false - does not exist - bool SettingExists(T settings, - Expression> keySelector, int storeId = 0) - where T : ISettings, new(); - - /// - /// Load settings - /// - /// Type - /// Store identifier for which settigns should be loaded - T LoadSetting(int storeId = 0) where T : ISettings, new(); - - /// - /// Load settings - /// - /// Setting class type - /// Store identifier for which settigns should be loaded - ISettings LoadSetting(Type settingType, int storeId = 0); - - /// - /// Set setting value - /// - /// Type - /// Key - /// Value - /// Store identifier - /// A value indicating whether to clear cache after setting update - void SetSetting(string key, T value, int storeId = 0, bool clearCache = true); + T GetSettingByKey(string key, T defaultValue = default(T), int storeId = 0, bool loadSharedValueIfNotFound = false); + + /// + /// Gets all settings + /// + /// Settings + IList GetAllSettings(); + + /// + /// Determines whether a setting exists + /// + /// Entity type + /// Property type + /// Settings + /// Key selector + /// Store identifier + /// true -setting exists; false - does not exist + bool SettingExists(T settings, + Expression> keySelector, int storeId = 0) + where T : ISettings, new(); + + /// + /// Load settings + /// + /// Type + /// Store identifier for which settigns should be loaded + T LoadSetting(int storeId = 0) where T : ISettings, new(); + + /// + /// Load settings + /// + /// Setting class type + /// Store identifier for which settigns should be loaded + ISettings LoadSetting(Type settingType, int storeId = 0); + + /// + /// Set setting value + /// + /// Type + /// Key + /// Value + /// Store identifier + /// A value indicating whether to clear cache after setting update + SaveSettingResult SetSetting(string key, T value, int storeId = 0, bool clearCache = true); /// /// Save settings object /// /// Type - /// Setting instance - /// Store identifier - void SaveSetting(T settings, int storeId = 0) where T : ISettings, new(); - - /// - /// Save settings object - /// - /// Setting instance - /// Store identifier - void SaveSetting(ISettings settings, int storeId = 0); - - /// - /// Save settings object - /// - /// Entity type - /// Property type - /// Settings - /// Key selector - /// Store ID - /// A value indicating whether to clear cache after setting update - void SaveSetting(T settings, - Expression> keySelector, - int storeId = 0, bool clearCache = true) where T : ISettings, new(); - - /// codehint: sm-add - void UpdateSetting(T settings, Expression> keySelector, bool overrideForStore, int storeId = 0) where T : ISettings, new(); - - void InsertSetting(Setting setting, bool clearCache = true); - - void UpdateSetting(Setting setting, bool clearCache = true); - - /// - /// Deletes a setting - /// - /// Setting - void DeleteSetting(Setting setting); + /// Setting instance + /// Store identifier + /// true when any setting property has been modified. + bool SaveSetting(T settings, int storeId = 0) where T : ISettings, new(); + + /// + /// Save settings object + /// + /// Entity type + /// Property type + /// Settings + /// Key selector + /// Store ID + /// A value indicating whether to clear cache after setting update + /// true when the setting property has been modified. + SaveSettingResult SaveSetting( + T settings, + Expression> keySelector, + int storeId = 0, + bool clearCache = true) where T : ISettings, new(); + + /// + /// Updates a setting property + /// + /// Entity type + /// Property type + /// Settings + /// Key selector + /// Store ID + /// true when the setting property has been modified. + SaveSettingResult UpdateSetting( + T settings, + Expression> keySelector, + bool overrideForStore, + int storeId = 0) where T : ISettings, new(); + + void InsertSetting(Setting setting, bool clearCache = true); + + void UpdateSetting(Setting setting, bool clearCache = true); + + /// + /// Deletes a setting + /// + /// Setting + void DeleteSetting(Setting setting); /// /// Delete all settings @@ -135,26 +159,32 @@ void SaveSetting(T settings, /// Type void DeleteSetting() where T : ISettings, new(); - /// - /// Delete settings object - /// - /// Entity type - /// Property type - /// Settings - /// Key selector - /// Store ID - void DeleteSetting(T settings, - Expression> keySelector, int storeId = 0) where T : ISettings, new(); - - /// codehint: sm-add - void DeleteSetting(string key, int storeId = 0); - - /// - /// Deletes all settings with its key beginning with rootKey. - /// - /// codehint: sm-add - /// Number of deleted settings - int DeleteSettings(string rootKey); + /// + /// Delete a settings property from storage + /// + /// Entity type + /// Property type + /// Settings + /// Key selector + /// Store ID + /// true when the setting existed and has been deleted + bool DeleteSetting( + T settings, + Expression> keySelector, + int storeId = 0) where T : ISettings, new(); + + /// + /// Delete a settings property from storage + /// + /// true when the setting existed and has been deleted + bool DeleteSetting(string key, int storeId = 0); + + /// + /// Deletes all settings with its key beginning with rootKey. + /// + /// codehint: sm-add + /// Number of deleted settings + int DeleteSettings(string rootKey); /// /// Clear cache diff --git a/src/Libraries/SmartStore.Core/Configuration/JsonPersistAttribute.cs b/src/Libraries/SmartStore.Core/Configuration/JsonPersistAttribute.cs index 00d96fe9ba..35539a8293 100644 --- a/src/Libraries/SmartStore.Core/Configuration/JsonPersistAttribute.cs +++ b/src/Libraries/SmartStore.Core/Configuration/JsonPersistAttribute.cs @@ -1,17 +1,14 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace SmartStore.Core.Configuration -{ +{ /// /// Marker attribute. Indicates that the settings should /// be persisted as a JSON string rather than splitted /// into single properties. /// [AttributeUsage(AttributeTargets.Class)] - [Obsolete] + [Obsolete] public class JsonPersistAttribute : Attribute { } diff --git a/src/Libraries/SmartStore.Core/Data/DataProviderFactory.cs b/src/Libraries/SmartStore.Core/Data/DataProviderFactory.cs index 34d423474f..368bebf752 100644 --- a/src/Libraries/SmartStore.Core/Data/DataProviderFactory.cs +++ b/src/Libraries/SmartStore.Core/Data/DataProviderFactory.cs @@ -1,20 +1,18 @@ -using System; - -namespace SmartStore.Core.Data +namespace SmartStore.Core.Data { public abstract class DataProviderFactory { protected DataProviderFactory(DataSettings settings) { - Guard.NotNull(settings, nameof(settings)); + Guard.NotNull(settings, nameof(settings)); this.Settings = settings; } - protected DataSettings Settings - { - get; - private set; - } + protected DataSettings Settings + { + get; + private set; + } public abstract IDataProvider LoadDataProvider(); } diff --git a/src/Libraries/SmartStore.Core/Data/DataSettings.cs b/src/Libraries/SmartStore.Core/Data/DataSettings.cs index 146cc96bcb..3f9a7e5b82 100644 --- a/src/Libraries/SmartStore.Core/Data/DataSettings.cs +++ b/src/Libraries/SmartStore.Core/Data/DataSettings.cs @@ -5,362 +5,352 @@ using System.IO; using System.Linq; using System.Threading; -using System.Threading.Tasks; -using System.Web.Hosting; using SmartStore.Utilities; using SmartStore.Utilities.Threading; namespace SmartStore.Core.Data -{ - public partial class DataSettings +{ + public partial class DataSettings { - private static readonly ReaderWriterLockSlim s_rwLock = new ReaderWriterLockSlim(); - private static DataSettings s_current = null; - private static Func s_settingsFactory = new Func(() => new DataSettings()); - private static bool? s_installed = null; - private static bool s_TestMode = false; + private static readonly ReaderWriterLockSlim s_rwLock = new ReaderWriterLockSlim(); + private static DataSettings s_current = null; + private static Func s_settingsFactory = new Func(() => new DataSettings()); + private static bool? s_installed = null; + private static bool s_TestMode = false; - protected const char SEPARATOR = ':'; - protected const string FILENAME = "Settings.txt"; + protected const char SEPARATOR = ':'; + protected const string FILENAME = "Settings.txt"; - private DataSettings() + private DataSettings() { RawDataSettings = new Dictionary(); } - #region Static members - - public static void SetDefaultFactory(Func factory) - { - Guard.NotNull(factory, nameof(factory)); - - lock (s_rwLock.GetWriteLock()) - { - s_settingsFactory = factory; - } - } - - public static DataSettings Current - { - get - { - using (s_rwLock.GetUpgradeableReadLock()) - { - if (s_current == null) - { - using (s_rwLock.GetWriteLock()) - { - if (s_current == null) - { - s_current = s_settingsFactory(); - s_current.Load(); - } - } - } - } - - return s_current; - } - } - - public static bool DatabaseIsInstalled() - { - if (s_TestMode) - return false; - - if (!s_installed.HasValue) - { - s_installed = Current.IsValid(); - } - - return s_installed.Value; - } - - internal static void SetTestMode(bool isTestMode) - { - s_TestMode = isTestMode; - } - - public static void Reload() - { - using (s_rwLock.GetWriteLock()) - { - s_current = null; - s_installed = null; - } - } - - public static void Delete() - { - if (s_current?.TenantPath != null) - { - using (s_rwLock.GetWriteLock()) - { - string filePath = Path.Combine(CommonHelper.MapPath(s_current.TenantPath), FILENAME); - try - { - File.Delete(filePath); - } - finally - { - s_current = null; - s_installed = null; - } - } - } - } - - #endregion - - #region Instance members - - public string TenantName - { - get; - private set; - } - - public string TenantPath - { - get; - private set; - } - - public Version AppVersion - { - get; - set; - } - - public string DataProvider - { - get; - set; - } - - public string ProviderInvariantName - { - get - { - 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"; - } - } - - public string ProviderFriendlyName - { - get - { - if (this.DataProvider.HasValue() && this.DataProvider.IsCaseInsensitiveEqual("sqlserver")) - return "SQL Server"; - - // SqlCe should always be the default provider - return "SQL Server Compact (SQL CE)"; - } - } - - public bool IsSqlServer - { - get - { - return this.DataProvider.HasValue() && this.DataProvider.IsCaseInsensitiveEqual("sqlserver"); - } - } - - public string DataConnectionString - { - get; - set; - } - - public string DataConnectionType - { - get; - set; - } - - public IDictionary RawDataSettings - { - get; - private set; - } + #region Static members + + public static void SetDefaultFactory(Func factory) + { + Guard.NotNull(factory, nameof(factory)); + + lock (s_rwLock.GetWriteLock()) + { + s_settingsFactory = factory; + } + } + + public static DataSettings Current + { + get + { + using (s_rwLock.GetUpgradeableReadLock()) + { + if (s_current == null) + { + using (s_rwLock.GetWriteLock()) + { + if (s_current == null) + { + s_current = s_settingsFactory(); + s_current.Load(); + } + } + } + } + + return s_current; + } + } + + public static bool DatabaseIsInstalled() + { + if (s_TestMode) + return false; + + if (!s_installed.HasValue) + { + s_installed = Current.IsValid(); + } + + return s_installed.Value; + } + + internal static void SetTestMode(bool isTestMode) + { + s_TestMode = isTestMode; + } + + public static void Reload() + { + using (s_rwLock.GetWriteLock()) + { + s_current = null; + s_installed = null; + } + } + + public static void Delete() + { + if (s_current?.TenantPath != null) + { + using (s_rwLock.GetWriteLock()) + { + string filePath = Path.Combine(CommonHelper.MapPath(s_current.TenantPath), FILENAME); + try + { + File.Delete(filePath); + } + finally + { + s_current = null; + s_installed = null; + } + } + } + } + + #endregion + + #region Instance members + + public string TenantName + { + get; + private set; + } + + public string TenantPath + { + get; + private set; + } + + public Version AppVersion + { + get; + set; + } + + public string DataProvider + { + get; + set; + } + + public string ProviderInvariantName + { + get + { + 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"; + } + } + + public string ProviderFriendlyName + { + get + { + if (this.DataProvider.HasValue() && this.DataProvider.IsCaseInsensitiveEqual("sqlserver")) + return "SQL Server"; + + // SqlCe should always be the default provider + return "SQL Server Compact (SQL CE)"; + } + } + + public bool IsSqlServer => this.DataProvider.HasValue() && this.DataProvider.IsCaseInsensitiveEqual("sqlserver"); + + public string DataConnectionString + { + get; + set; + } + + public string DataConnectionType + { + get; + set; + } + + public IDictionary RawDataSettings + { + get; + private set; + } public bool IsValid() { return this.DataProvider.HasValue() && this.DataConnectionString.HasValue(); - } - - public virtual bool Load() - { - using (s_rwLock.GetWriteLock()) - { - this.Reset(); - - var curTenant = ResolveTenant(); - var tenantPath = "~/App_Data/Tenants/" + curTenant; - string filePath = Path.Combine(CommonHelper.MapPath(tenantPath), FILENAME); - - this.TenantName = curTenant; - this.TenantPath = tenantPath; - - if (File.Exists(filePath) && !s_TestMode) - { - string text = File.ReadAllText(filePath); - var settings = ParseSettings(text); - if (settings.Any()) - { - this.RawDataSettings.AddRange(settings); - if (settings.ContainsKey("AppVersion")) - { - this.AppVersion = new Version(settings["AppVersion"]); - } - if (settings.ContainsKey("DataProvider")) - { - this.DataProvider = settings["DataProvider"]; - this.DataConnectionType = this.IsSqlServer - ? typeof(SqlConnection).AssemblyQualifiedName - : typeof(SqlCeConnection).AssemblyQualifiedName; - } - if (settings.ContainsKey("DataConnectionString")) - { - this.DataConnectionString = settings["DataConnectionString"]; - } - - return this.IsValid(); - } - } - - return false; - } - } - - public void Reset() - { - using (s_rwLock.GetWriteLock()) - { - this.RawDataSettings.Clear(); - this.TenantName = null; - this.TenantPath = null; - this.AppVersion = null; - this.DataProvider = null; - this.DataConnectionString = null; - this.DataConnectionType = null; - - s_installed = null; - } - } - - public virtual bool Save() - { - if (!this.IsValid()) - return false; - - using (s_rwLock.GetWriteLock()) - { - string filePath = Path.Combine(CommonHelper.MapPath(this.TenantPath), FILENAME); - if (!File.Exists(filePath)) - { - using (File.Create(filePath)) - { - // we use 'using' to close the file after it's created - } - } - - var text = SerializeSettings(); - File.WriteAllText(filePath, text); - - return true; - } - } - - #endregion - - #region Instance helpers - - protected virtual string ResolveTenant() - { - var tenantsBaseDir = CommonHelper.MapPath("~/App_Data/Tenants"); - var curTenantFile = Path.Combine(tenantsBaseDir, "current.txt"); - - string curTenant = null; - - if (File.Exists(curTenantFile)) - { - curTenant = File.ReadAllText(curTenantFile).EmptyNull().Trim().NullEmpty(); - if (curTenant != curTenant.EmptyNull().ToValidPath()) - { - // File contains invalid path string - curTenant = null; - } - - if (curTenant != null && !Directory.Exists(Path.Combine(tenantsBaseDir, curTenant))) - { - // Specified Tenant directory does not exist - curTenant = null; - } - } - - curTenant = curTenant ?? "Default"; - - var tenantPath = Path.Combine(tenantsBaseDir, curTenant); - - if (curTenant.IsCaseInsensitiveEqual("Default") && !Directory.Exists(tenantPath)) - { - // The Default tenant dir should be created if it doesn't exist - Directory.CreateDirectory(tenantPath); - } - - return curTenant.TrimEnd('/'); - } - - protected virtual IDictionary ParseSettings(string text) - { - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - - if (text.IsEmpty()) - return result; - - var settings = new List(); - using (var reader = new StringReader(text)) - { - string str; - while ((str = reader.ReadLine()) != null) - settings.Add(str); - } - - foreach (var setting in settings) - { - var separatorIndex = setting.IndexOf(SEPARATOR); - if (separatorIndex == -1) - { - continue; - } - string key = setting.Substring(0, separatorIndex).Trim(); - string value = setting.Substring(separatorIndex + 1).Trim(); - - if (key.HasValue() && value.HasValue()) - { - result.Add(key, value); - } - } - - return result; - } - - protected virtual string SerializeSettings() - { - return string.Format("AppVersion: {0}{3}DataProvider: {1}{3}DataConnectionString: {2}{3}", - this.AppVersion.ToString(), - this.DataProvider, - this.DataConnectionString, - Environment.NewLine); - } - - #endregion - - } + } + + public virtual bool Load() + { + using (s_rwLock.GetWriteLock()) + { + this.Reset(); + + var curTenant = ResolveTenant(); + var tenantPath = "~/App_Data/Tenants/" + curTenant; + string filePath = Path.Combine(CommonHelper.MapPath(tenantPath), FILENAME); + + this.TenantName = curTenant; + this.TenantPath = tenantPath; + + if (File.Exists(filePath) && !s_TestMode) + { + string text = File.ReadAllText(filePath); + var settings = ParseSettings(text); + if (settings.Any()) + { + this.RawDataSettings.AddRange(settings); + if (settings.ContainsKey("AppVersion")) + { + this.AppVersion = new Version(settings["AppVersion"]); + } + if (settings.ContainsKey("DataProvider")) + { + this.DataProvider = settings["DataProvider"]; + this.DataConnectionType = this.IsSqlServer + ? typeof(SqlConnection).AssemblyQualifiedName + : typeof(SqlCeConnection).AssemblyQualifiedName; + } + if (settings.ContainsKey("DataConnectionString")) + { + this.DataConnectionString = settings["DataConnectionString"]; + } + + return this.IsValid(); + } + } + + return false; + } + } + + public void Reset() + { + using (s_rwLock.GetWriteLock()) + { + this.RawDataSettings.Clear(); + this.TenantName = null; + this.TenantPath = null; + this.AppVersion = null; + this.DataProvider = null; + this.DataConnectionString = null; + this.DataConnectionType = null; + + s_installed = null; + } + } + + public virtual bool Save() + { + if (!this.IsValid()) + return false; + + using (s_rwLock.GetWriteLock()) + { + string filePath = Path.Combine(CommonHelper.MapPath(this.TenantPath), FILENAME); + if (!File.Exists(filePath)) + { + using (File.Create(filePath)) + { + // we use 'using' to close the file after it's created + } + } + + var text = SerializeSettings(); + File.WriteAllText(filePath, text); + + return true; + } + } + + #endregion + + #region Instance helpers + + protected virtual string ResolveTenant() + { + var tenantsBaseDir = CommonHelper.MapPath("~/App_Data/Tenants"); + var curTenantFile = Path.Combine(tenantsBaseDir, "current.txt"); + + string curTenant = null; + + if (File.Exists(curTenantFile)) + { + curTenant = File.ReadAllText(curTenantFile).EmptyNull().Trim().NullEmpty(); + if (curTenant != curTenant.EmptyNull().ToValidPath()) + { + // File contains invalid path string + curTenant = null; + } + + if (curTenant != null && !Directory.Exists(Path.Combine(tenantsBaseDir, curTenant))) + { + // Specified Tenant directory does not exist + curTenant = null; + } + } + + curTenant = curTenant ?? "Default"; + + var tenantPath = Path.Combine(tenantsBaseDir, curTenant); + + if (curTenant.IsCaseInsensitiveEqual("Default") && !Directory.Exists(tenantPath)) + { + // The Default tenant dir should be created if it doesn't exist + Directory.CreateDirectory(tenantPath); + } + + return curTenant.TrimEnd('/'); + } + + protected virtual IDictionary ParseSettings(string text) + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (text.IsEmpty()) + return result; + + var settings = new List(); + using (var reader = new StringReader(text)) + { + string str; + while ((str = reader.ReadLine()) != null) + settings.Add(str); + } + + foreach (var setting in settings) + { + var separatorIndex = setting.IndexOf(SEPARATOR); + if (separatorIndex == -1) + { + continue; + } + string key = setting.Substring(0, separatorIndex).Trim(); + string value = setting.Substring(separatorIndex + 1).Trim(); + + if (key.HasValue() && value.HasValue()) + { + result.Add(key, value); + } + } + + return result; + } + + protected virtual string SerializeSettings() + { + return string.Format("AppVersion: {0}{3}DataProvider: {1}{3}DataConnectionString: {2}{3}", + this.AppVersion.ToString(), + this.DataProvider, + this.DataConnectionString, + Environment.NewLine); + } + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Data/DbContextScope.cs b/src/Libraries/SmartStore.Core/Data/DbContextScope.cs index 7d3b433e8e..603bdbde8a 100644 --- a/src/Libraries/SmartStore.Core/Data/DbContextScope.cs +++ b/src/Libraries/SmartStore.Core/Data/DbContextScope.cs @@ -1,110 +1,106 @@ using System; -using SmartStore.Core.Infrastructure; -using System.Linq.Expressions; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using System.Threading.Tasks; +using SmartStore.Core.Infrastructure; namespace SmartStore.Core.Data { public class DbContextScope : IDisposable { - private readonly IDbContext _ctx; - private readonly bool _autoDetectChangesEnabled; + private readonly IDbContext _ctx; + private readonly bool _autoDetectChangesEnabled; private readonly bool _proxyCreationEnabled; private readonly bool _validateOnSaveEnabled; - private readonly bool _forceNoTracking; - private readonly bool _hooksEnabled; - private readonly bool _autoCommit; - private readonly bool _lazyLoading; - - - public DbContextScope(IDbContext ctx = null, - bool? autoDetectChanges = null, - bool? proxyCreation = null, - bool? validateOnSave = null, - bool? forceNoTracking = null, - bool? hooksEnabled = null, - bool? autoCommit = null, - bool? lazyLoading = null) + private readonly bool _forceNoTracking; + private readonly bool _hooksEnabled; + private readonly bool _autoCommit; + private readonly bool _lazyLoading; + + + public DbContextScope(IDbContext ctx = null, + bool? autoDetectChanges = null, + bool? proxyCreation = null, + bool? validateOnSave = null, + bool? forceNoTracking = null, + bool? hooksEnabled = null, + bool? autoCommit = null, + bool? lazyLoading = null) { - _ctx = ctx ?? EngineContext.Current.Resolve(); - _autoDetectChangesEnabled = _ctx.AutoDetectChangesEnabled; - _proxyCreationEnabled = _ctx.ProxyCreationEnabled; - _validateOnSaveEnabled = _ctx.ValidateOnSaveEnabled; - _forceNoTracking = _ctx.ForceNoTracking; - _hooksEnabled = _ctx.HooksEnabled; - _autoCommit = _ctx.AutoCommitEnabled; - _lazyLoading = _ctx.LazyLoadingEnabled; - + _ctx = ctx ?? EngineContext.Current.Resolve(); + _autoDetectChangesEnabled = _ctx.AutoDetectChangesEnabled; + _proxyCreationEnabled = _ctx.ProxyCreationEnabled; + _validateOnSaveEnabled = _ctx.ValidateOnSaveEnabled; + _forceNoTracking = _ctx.ForceNoTracking; + _hooksEnabled = _ctx.HooksEnabled; + _autoCommit = _ctx.AutoCommitEnabled; + _lazyLoading = _ctx.LazyLoadingEnabled; + if (autoDetectChanges.HasValue) - _ctx.AutoDetectChangesEnabled = autoDetectChanges.Value; + _ctx.AutoDetectChangesEnabled = autoDetectChanges.Value; if (proxyCreation.HasValue) - _ctx.ProxyCreationEnabled = proxyCreation.Value; + _ctx.ProxyCreationEnabled = proxyCreation.Value; if (validateOnSave.HasValue) - _ctx.ValidateOnSaveEnabled = validateOnSave.Value; - - if (forceNoTracking.HasValue) - _ctx.ForceNoTracking = forceNoTracking.Value; - - if (hooksEnabled.HasValue) - _ctx.HooksEnabled = hooksEnabled.Value; - - if (autoCommit.HasValue) - _ctx.AutoCommitEnabled = autoCommit.Value; - - if (lazyLoading.HasValue) - _ctx.LazyLoadingEnabled = lazyLoading.Value; - } - - public IDbContext DbContext - { - get { return _ctx; } - } - - public void LoadCollection( - TEntity entity, - Expression>> navigationProperty, - bool force = false, - Func, IQueryable> queryAction = null) - where TEntity : BaseEntity - where TCollection : BaseEntity - { - _ctx.LoadCollection(entity, navigationProperty, force, queryAction); - } - - public void LoadReference( - TEntity entity, - Expression> navigationProperty, - bool force = false) - where TEntity : BaseEntity - where TProperty : BaseEntity - { - _ctx.LoadReference(entity, navigationProperty, force); - } - - public int Commit() - { - return _ctx.SaveChanges(); - } - - public async Task CommitAsync() - { - return await _ctx.SaveChangesAsync(); - } - - public void Dispose() + _ctx.ValidateOnSaveEnabled = validateOnSave.Value; + + if (forceNoTracking.HasValue) + _ctx.ForceNoTracking = forceNoTracking.Value; + + if (hooksEnabled.HasValue) + _ctx.HooksEnabled = hooksEnabled.Value; + + if (autoCommit.HasValue) + _ctx.AutoCommitEnabled = autoCommit.Value; + + if (lazyLoading.HasValue) + _ctx.LazyLoadingEnabled = lazyLoading.Value; + } + + public IDbContext DbContext => _ctx; + + public void LoadCollection( + TEntity entity, + Expression>> navigationProperty, + bool force = false, + Func, IQueryable> queryAction = null) + where TEntity : BaseEntity + where TCollection : BaseEntity + { + _ctx.LoadCollection(entity, navigationProperty, force, queryAction); + } + + public void LoadReference( + TEntity entity, + Expression> navigationProperty, + bool force = false) + where TEntity : BaseEntity + where TProperty : BaseEntity + { + _ctx.LoadReference(entity, navigationProperty, force); + } + + public int Commit() + { + return _ctx.SaveChanges(); + } + + public async Task CommitAsync() { - _ctx.AutoDetectChangesEnabled = _autoDetectChangesEnabled; - _ctx.ProxyCreationEnabled = _proxyCreationEnabled; - _ctx.ValidateOnSaveEnabled = _validateOnSaveEnabled; - _ctx.ForceNoTracking = _forceNoTracking; - _ctx.HooksEnabled = _hooksEnabled; - _ctx.AutoCommitEnabled = _autoCommit; - _ctx.LazyLoadingEnabled = _lazyLoading; + return await _ctx.SaveChangesAsync(); } + public void Dispose() + { + _ctx.AutoDetectChangesEnabled = _autoDetectChangesEnabled; + _ctx.ProxyCreationEnabled = _proxyCreationEnabled; + _ctx.ValidateOnSaveEnabled = _validateOnSaveEnabled; + _ctx.ForceNoTracking = _forceNoTracking; + _ctx.HooksEnabled = _hooksEnabled; + _ctx.AutoCommitEnabled = _autoCommit; + _ctx.LazyLoadingEnabled = _lazyLoading; + } } } diff --git a/src/Libraries/SmartStore.Core/Data/DbQuerySettings.cs b/src/Libraries/SmartStore.Core/Data/DbQuerySettings.cs index 3e2318e4f0..3970eeecf2 100644 --- a/src/Libraries/SmartStore.Core/Data/DbQuerySettings.cs +++ b/src/Libraries/SmartStore.Core/Data/DbQuerySettings.cs @@ -1,18 +1,16 @@ -using System; - -namespace SmartStore.Core.Data +namespace SmartStore.Core.Data { - public class DbQuerySettings - { - public DbQuerySettings(bool ignoreAcl, bool ignoreMultiStore) - { - this.IgnoreAcl = ignoreAcl; - this.IgnoreMultiStore = ignoreMultiStore; - } + public class DbQuerySettings + { + public DbQuerySettings(bool ignoreAcl, bool ignoreMultiStore) + { + this.IgnoreAcl = ignoreAcl; + this.IgnoreMultiStore = ignoreMultiStore; + } - public bool IgnoreAcl { get; private set; } - public bool IgnoreMultiStore { get; private set; } + 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; } = new DbQuerySettings(false, false); + } } diff --git a/src/Libraries/SmartStore.Core/Data/EntityState.cs b/src/Libraries/SmartStore.Core/Data/EntityState.cs index 36f55a4c06..0cab007410 100644 --- a/src/Libraries/SmartStore.Core/Data/EntityState.cs +++ b/src/Libraries/SmartStore.Core/Data/EntityState.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.Core.Data { diff --git a/src/Libraries/SmartStore.Core/Data/Hooks/DbSaveHook.cs b/src/Libraries/SmartStore.Core/Data/Hooks/DbSaveHook.cs index 09b74716ac..e38efb2620 100644 --- a/src/Libraries/SmartStore.Core/Data/Hooks/DbSaveHook.cs +++ b/src/Libraries/SmartStore.Core/Data/Hooks/DbSaveHook.cs @@ -2,78 +2,80 @@ namespace SmartStore.Core.Data.Hooks { - public abstract class DbSaveHook : IDbSaveHook where TEntity : class - { - public virtual void OnBeforeSave(IHookedEntity entry) - { - var entity = entry.Entity as TEntity; - switch (entry.InitialState) - { - case EntityState.Added: - OnInserting(entity, entry); - break; - case EntityState.Modified: - OnUpdating(entity, entry); - break; - case EntityState.Deleted: - OnDeleting(entity, entry); - break; - } - } + public abstract class DbSaveHook : IDbSaveHook + where TContext : IDbContext + where TEntity : class + { + public virtual void OnBeforeSave(IHookedEntity entry) + { + var entity = entry.Entity as TEntity; + switch (entry.InitialState) + { + case EntityState.Added: + OnInserting(entity, entry); + break; + case EntityState.Modified: + OnUpdating(entity, entry); + break; + case EntityState.Deleted: + OnDeleting(entity, entry); + break; + } + } - protected virtual void OnInserting(TEntity entity, IHookedEntity entry) - { - throw new NotImplementedException(); - } + protected virtual void OnInserting(TEntity entity, IHookedEntity entry) + { + throw new NotImplementedException(); + } - protected virtual void OnUpdating(TEntity entity, IHookedEntity entry) - { - throw new NotImplementedException(); - } + protected virtual void OnUpdating(TEntity entity, IHookedEntity entry) + { + throw new NotImplementedException(); + } - protected virtual void OnDeleting(TEntity entity, IHookedEntity entry) - { - throw new NotImplementedException(); - } + protected virtual void OnDeleting(TEntity entity, IHookedEntity entry) + { + throw new NotImplementedException(); + } - public virtual void OnBeforeSaveCompleted() - { - } + public virtual void OnBeforeSaveCompleted() + { + } - public virtual void OnAfterSave(IHookedEntity entry) - { - var entity = entry.Entity as TEntity; - switch (entry.InitialState) - { - case EntityState.Added: - OnInserted(entity, entry); - break; - case EntityState.Modified: - OnUpdated(entity, entry); - break; - case EntityState.Deleted: - OnDeleted(entity, entry); - break; - } - } + public virtual void OnAfterSave(IHookedEntity entry) + { + var entity = entry.Entity as TEntity; + switch (entry.InitialState) + { + case EntityState.Added: + OnInserted(entity, entry); + break; + case EntityState.Modified: + OnUpdated(entity, entry); + break; + case EntityState.Deleted: + OnDeleted(entity, entry); + break; + } + } - protected virtual void OnInserted(TEntity entity, IHookedEntity entry) - { - throw new NotImplementedException(); - } + protected virtual void OnInserted(TEntity entity, IHookedEntity entry) + { + throw new NotImplementedException(); + } - protected virtual void OnUpdated(TEntity entity, IHookedEntity entry) - { - throw new NotImplementedException(); - } + protected virtual void OnUpdated(TEntity entity, IHookedEntity entry) + { + throw new NotImplementedException(); + } - protected virtual void OnDeleted(TEntity entity, IHookedEntity entry) - { - throw new NotImplementedException(); - } + protected virtual void OnDeleted(TEntity entity, IHookedEntity entry) + { + throw new NotImplementedException(); + } - public virtual void OnAfterSaveCompleted() - { - } - } + public virtual void OnAfterSaveCompleted() + { + } + } } diff --git a/src/Libraries/SmartStore.Core/Data/Hooks/DefaultDbHookHandler.cs b/src/Libraries/SmartStore.Core/Data/Hooks/DefaultDbHookHandler.cs index 39126f48f8..66b71ad411 100644 --- a/src/Libraries/SmartStore.Core/Data/Hooks/DefaultDbHookHandler.cs +++ b/src/Libraries/SmartStore.Core/Data/Hooks/DefaultDbHookHandler.cs @@ -1,270 +1,257 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using SmartStore.Collections; using SmartStore.Core.Logging; namespace SmartStore.Core.Data.Hooks { - public class DefaultDbHookHandler : IDbHookHandler - { - private readonly IList> _saveHooks; - - private readonly Multimap _hooksRequestCache = new Multimap(); - - // Prevents repetitive hooking of the same entity/state/[pre|post] combination within a single request - private readonly HashSet _hookedEntities = new HashSet(); - - private static HashSet _importantSaveHookTypes; - private readonly static object _lock = new object(); - - // Contains all IDbHook/EntityType/State/Stage combinations in which - // the implementor threw either NotImplementedException or NotSupportedException. - // This boosts performance because these VOID combinations are not processed again - // and frees us mostly from the obligation always to detect changes. - private readonly static HashSet _voidHooks = new HashSet(); - - public DefaultDbHookHandler(IEnumerable> hooks) - { - _saveHooks = hooks.Where(x => x.Metadata.IsLoadHook == false).ToList(); - } - - public ILogger Logger - { - get; - set; - } = NullLogger.Instance; - - public bool HasImportantSaveHooks() - { - if (_importantSaveHookTypes == null) - { - lock (_lock) - { - if (_importantSaveHookTypes == null) - { - _importantSaveHookTypes = new HashSet(); - _importantSaveHookTypes.AddRange(_saveHooks.Where(x => x.Metadata.Important).Select(x => x.Metadata.ImplType)); - } - } - } - - return _importantSaveHookTypes.Any(); - } - - public IEnumerable TriggerPreSaveHooks(IEnumerable entries, bool importantHooksOnly, out bool anyStateChanged) - { - Guard.NotNull(entries, nameof(entries)); - - anyStateChanged = false; - - var processedHooks = new HashSet(); - - if (!entries.Any() || !_saveHooks.Any() || (importantHooksOnly && !this.HasImportantSaveHooks())) - return processedHooks; - - foreach (var entry in entries) - { - var e = entry; // Prevents access to modified closure - - if (HandledAlready(e, HookStage.PreSave)) - { - // Prevent repetitive hooking of the same entity/state/pre combination within a single request - continue; - } - - var hooks = GetSaveHookInstancesFor(e, HookStage.PreSave, importantHooksOnly); - - foreach (var hook in hooks) - { - // call hook - try - { - //Logger.DebugFormat("PRE save hook: {0}, State: {1}, Entity: {2}", hook.GetType().Name, e.InitialState, e.Entity.GetUnproxiedType().Name); - hook.OnBeforeSave(e); - processedHooks.Add(hook); - } - catch (Exception ex) when (ex is NotImplementedException || ex is NotSupportedException) - { - RegisterVoidHook(hook, e.EntityType, e.InitialState, HookStage.PreSave); - } - catch (Exception ex) - { - Logger.ErrorFormat(ex, "PreSaveHook exception ({0})", hook.GetType().FullName); - } - - // change state if applicable - if (e.HasStateChanged) - { - e.InitialState = e.State; - anyStateChanged = true; - } - } - } - - processedHooks.Each(x => x.OnBeforeSaveCompleted()); - - return processedHooks; - } - - public IEnumerable TriggerPostSaveHooks(IEnumerable entries, bool importantHooksOnly) - { - Guard.NotNull(entries, nameof(entries)); - - var processedHooks = new HashSet(); - - if (!entries.Any() || !_saveHooks.Any() || (importantHooksOnly && !this.HasImportantSaveHooks())) - return processedHooks; - - foreach (var entry in entries) - { - var e = entry; // Prevents access to modified closure - - if (HandledAlready(e, HookStage.PostSave)) - { - // Prevent repetitive hooking of the same entity/state/post combination within a single request - continue; - } - - var hooks = GetSaveHookInstancesFor(e, HookStage.PostSave, importantHooksOnly); - - foreach (var hook in hooks) - { - // call hook - try - { - //Logger.DebugFormat("POST save hook: {0}, State: {1}, Entity: {2}", hook.GetType().Name, e.InitialState, e.Entity.GetUnproxiedType().Name); - hook.OnAfterSave(e); - processedHooks.Add(hook); - } - catch (Exception ex) when (ex is NotImplementedException || ex is NotSupportedException) - { - RegisterVoidHook(hook, e.EntityType, e.InitialState, HookStage.PostSave); - } - catch (Exception ex) - { - Logger.ErrorFormat(ex, "PostSaveHook exception ({0})", hook.GetType().FullName); - } - } - } - - processedHooks.Each(x => x.OnAfterSaveCompleted()); - - return processedHooks; - } - - private IEnumerable GetSaveHookInstancesFor(IHookedEntity entry, HookStage stage, bool importantOnly) - { - return GetHookInstancesFor( - entry.EntityType, - entry.InitialState, - stage, - importantOnly, - _saveHooks, - _importantSaveHookTypes); - } - - private IEnumerable GetHookInstancesFor( - Type entityType, - EntityState entityState, - HookStage stage, - bool importantOnly, - IList> hookList, - HashSet importantHookTypes) where THook : IDbHook - { - IEnumerable hooks; - - if (entityType == null) - { - return Enumerable.Empty(); - } - - // For request cache lookup - var requestKey = new RequestHookKey(entityType, entityState, stage, importantOnly); - - if (_hooksRequestCache.ContainsKey(requestKey)) - { - hooks = _hooksRequestCache[requestKey]; - } - else - { - hooks = hookList - // Reduce by entity types which can be processed by this hook - .Where(x => x.Metadata.HookedType.IsAssignableFrom(entityType)) - // When importantOnly, only include hook types with [ImportantAttribute] - .Where(x => !importantOnly || importantHookTypes.Contains(x.Metadata.ImplType)) - // Exclude void hooks (hooks known to be useless for the current EntityType/State/Stage combination) - .Where(x => !_voidHooks.Contains(new HookKey(x.Metadata.ImplType, entityType, entityState, stage))) - .Select(x => x.Value) - .ToArray(); - - _hooksRequestCache.AddRange(requestKey, hooks); - } - - return hooks.Cast(); - } - - private bool HandledAlready(IHookedEntity entry, HookStage stage) - { - var entity = entry.Entity; - - if (entity == null || entity.IsTransientRecord()) - return false; - - var key = new HookedEntityKey(entry.EntityType, entity.Id, entry.InitialState, stage); - if (_hookedEntities.Contains(key)) - { - return true; - } - - _hookedEntities.Add(key); - return false; - } - - private void RegisterVoidHook(IDbHook hook, Type entityType, EntityState entityState, HookStage stage) - { - var hookType = hook.GetType(); - - // Unregister from request cache (if cached) - _hooksRequestCache.Remove(new RequestHookKey(entityType, entityState, stage, false), hook); - _hooksRequestCache.Remove(new RequestHookKey(entityType, entityState, stage, true), hook); - - lock (_lock) - { - // Add to static void hooks set - _voidHooks.Add(new HookKey(hookType, entityType, entityState, stage)); - } - } - - enum HookStage - { - Load, - PreSave, - PostSave - } - - class HookedEntityKey : Tuple - { - public HookedEntityKey(Type entityType, int entityId, EntityState initialState, HookStage stage) - : base(entityType, entityId, initialState, stage) - { - } - } - - class RequestHookKey : Tuple - { - public RequestHookKey(Type entityType, EntityState entityState, HookStage stage, bool importantOnly) - : base(entityType, entityState, stage, importantOnly) - { - } - } - - class HookKey : Tuple - { - public HookKey(Type hookType, Type entityType, EntityState entityState, HookStage stage) - : base(hookType, entityType, entityState, stage) - { - } - } - } + public class DefaultDbHookHandler : IDbHookHandler + { + private readonly IEnumerable> _saveHooks; + + private readonly Multimap _hooksRequestCache = new Multimap(); + + // Prevents repetitive hooking of the same entity/state/[pre|post] combination within a single request + private readonly HashSet _hookedEntities = new HashSet(); + + private static HashSet _importantSaveHookTypes; + private readonly static object _lock = new object(); + + // Contains all IDbSaveHook/EntityType/State/Stage combinations in which + // the implementor threw either NotImplementedException or NotSupportedException. + // This boosts performance because these VOID combinations are not processed again + // and frees us mostly from the obligation always to detect changes. + private readonly static HashSet _voidHooks = new HashSet(); + + public DefaultDbHookHandler(IEnumerable> hooks) + { + _saveHooks = hooks; + } + + public ILogger Logger + { + get; + set; + } = NullLogger.Instance; + + public bool HasImportantSaveHooks() + { + if (_importantSaveHookTypes == null) + { + lock (_lock) + { + if (_importantSaveHookTypes == null) + { + _importantSaveHookTypes = new HashSet(); + _importantSaveHookTypes.AddRange(_saveHooks.Where(x => x.Metadata.Important).Select(x => x.Metadata.ImplType)); + } + } + } + + return _importantSaveHookTypes.Any(); + } + + public IEnumerable TriggerPreSaveHooks(IEnumerable entries, bool importantHooksOnly, out bool anyStateChanged) + { + Guard.NotNull(entries, nameof(entries)); + + anyStateChanged = false; + + var processedHooks = new HashSet(); + + if (!entries.Any() || !_saveHooks.Any() || (importantHooksOnly && !this.HasImportantSaveHooks())) + return processedHooks; + + foreach (var entry in entries) + { + var e = entry; // Prevents access to modified closure + + if (HandledAlready(e, HookStage.PreSave)) + { + // Prevent repetitive hooking of the same entity/state/pre combination within a single request + continue; + } + + var hooks = GetSaveHookInstancesFor(e, HookStage.PreSave, importantHooksOnly); + + foreach (var hook in hooks) + { + // call hook + try + { + //Logger.DebugFormat("PRE save hook: {0}, State: {1}, Entity: {2}", hook.GetType().Name, e.InitialState, e.Entity.GetUnproxiedType().Name); + hook.OnBeforeSave(e); + processedHooks.Add(hook); + } + catch (Exception ex) when (ex is NotImplementedException || ex is NotSupportedException) + { + RegisterVoidHook(hook, e, HookStage.PreSave); + } + catch (Exception ex) + { + Logger.ErrorFormat(ex, "PreSaveHook exception ({0})", hook.GetType().FullName); + } + + // change state if applicable + if (e.HasStateChanged) + { + e.InitialState = e.State; + anyStateChanged = true; + } + } + } + + processedHooks.Each(x => x.OnBeforeSaveCompleted()); + + return processedHooks; + } + + public IEnumerable TriggerPostSaveHooks(IEnumerable entries, bool importantHooksOnly) + { + Guard.NotNull(entries, nameof(entries)); + + var processedHooks = new HashSet(); + + if (!entries.Any() || !_saveHooks.Any() || (importantHooksOnly && !this.HasImportantSaveHooks())) + return processedHooks; + + foreach (var entry in entries) + { + var e = entry; // Prevents access to modified closure + + if (HandledAlready(e, HookStage.PostSave)) + { + // Prevent repetitive hooking of the same entity/state/post combination within a single request + continue; + } + + var hooks = GetSaveHookInstancesFor(e, HookStage.PostSave, importantHooksOnly); + + foreach (var hook in hooks) + { + // call hook + try + { + //Logger.DebugFormat("POST save hook: {0}, State: {1}, Entity: {2}", hook.GetType().Name, e.InitialState, e.Entity.GetUnproxiedType().Name); + hook.OnAfterSave(e); + processedHooks.Add(hook); + } + catch (Exception ex) when (ex is NotImplementedException || ex is NotSupportedException) + { + RegisterVoidHook(hook, e, HookStage.PostSave); + } + catch (Exception ex) + { + Logger.ErrorFormat(ex, "PostSaveHook exception ({0})", hook.GetType().FullName); + } + } + } + + processedHooks.Each(x => x.OnAfterSaveCompleted()); + + return processedHooks; + } + + private IEnumerable GetSaveHookInstancesFor(IHookedEntity entry, HookStage stage, bool importantOnly) + { + if (entry.EntityType == null) + { + return Enumerable.Empty(); + } + + IEnumerable hooks; + + // For request cache lookup + var requestKey = new RequestHookKey(entry, stage, importantOnly); + + if (_hooksRequestCache.ContainsKey(requestKey)) + { + hooks = _hooksRequestCache[requestKey].ToArray(); + } + else + { + hooks = _saveHooks + // Reduce by data context types + .Where(x => x.Metadata.DbContextType.IsAssignableFrom(entry.ContextType)) + // Reduce by entity types which can be processed by this hook + .Where(x => x.Metadata.HookedType.IsAssignableFrom(entry.EntityType)) + // When importantOnly, only include hook types with [ImportantAttribute] + .Where(x => !importantOnly || _importantSaveHookTypes.Contains(x.Metadata.ImplType)) + // Exclude void hooks (hooks known to be useless for the current EntityType/State/Stage combination) + .Where(x => !_voidHooks.Contains(new HookKey(x.Metadata.ImplType, entry, stage))) + .Select(x => x.Value) + .ToArray(); + + _hooksRequestCache.AddRange(requestKey, hooks); + } + + return hooks; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool HandledAlready(IHookedEntity entry, HookStage stage) + { + var entity = entry.Entity; + + if (entity == null || entity.IsTransientRecord()) + return false; + + var key = new HookedEntityKey(entry, stage, entity.Id); + if (_hookedEntities.Contains(key)) + { + return true; + } + + _hookedEntities.Add(key); + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void RegisterVoidHook(IDbSaveHook hook, IHookedEntity entry, HookStage stage) + { + var hookType = hook.GetType(); + + // Unregister from request cache (if cached) + _hooksRequestCache.Remove(new RequestHookKey(entry, stage, false), hook); + _hooksRequestCache.Remove(new RequestHookKey(entry, stage, true), hook); + + lock (_lock) + { + // Add to static void hooks set + _voidHooks.Add(new HookKey(hookType, entry, stage)); + } + } + + enum HookStage + { + PreSave, + PostSave + } + + class HookedEntityKey : Tuple + { + public HookedEntityKey(IHookedEntity entry, HookStage stage, int entityId) + : base(entry.ContextType, entry.EntityType, entityId, entry.InitialState, stage) + { + } + } + + class RequestHookKey : Tuple + { + public RequestHookKey(IHookedEntity entry, HookStage stage, bool importantOnly) + : base(entry.ContextType, entry.EntityType, entry.InitialState, stage, importantOnly) + { + } + } + + class HookKey : Tuple + { + public HookKey(Type hookType, IHookedEntity entry, HookStage stage) + : base(hookType, entry.EntityType, entry.InitialState, stage) + { + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Data/Hooks/HookMetadata.cs b/src/Libraries/SmartStore.Core/Data/Hooks/HookMetadata.cs index f1f81574a2..855787040e 100644 --- a/src/Libraries/SmartStore.Core/Data/Hooks/HookMetadata.cs +++ b/src/Libraries/SmartStore.Core/Data/Hooks/HookMetadata.cs @@ -2,35 +2,41 @@ namespace SmartStore.Core.Data.Hooks { - public class HookMetadata - { - /// - /// The type of entity - /// - public Type HookedType { get; set; } + public class HookMetadata + { + /// + /// The type of entity + /// + public Type HookedType { get; set; } - /// - /// The type of the hook class itself - /// - public Type ImplType { get; set; } + /// + /// The type of the hook class itself + /// + public Type ImplType { get; set; } - public bool IsLoadHook { get; set; } + /// + /// The impl type of to which the hook belongs to. + /// + public Type DbContextType { get; set; } - /// - /// Whether the hook should run in any case, even if hooking has been turned off. - /// - public bool Important { get; set; } + /// + /// Whether the hook should run in any case, even if hooking has been turned off. + /// + public bool Important { get; set; } - public static HookMetadata Create(Type hookedType, bool important = false) where THook : IDbHook - { - Guard.NotNull(hookedType, nameof(hookedType)); + public static HookMetadata Create(Type hookedType, bool important = false) + where THook : IDbSaveHook + where TContext : IDbContext + { + Guard.NotNull(hookedType, nameof(hookedType)); - return new HookMetadata - { - ImplType = typeof(THook), - HookedType = hookedType, - Important = important - }; - } - } + return new HookMetadata + { + ImplType = typeof(THook), + DbContextType = typeof(TContext), + HookedType = hookedType, + Important = important + }; + } + } } diff --git a/src/Libraries/SmartStore.Core/Data/Hooks/HookableAttribute.cs b/src/Libraries/SmartStore.Core/Data/Hooks/HookableAttribute.cs index ed20b256eb..ac757a4c1c 100644 --- a/src/Libraries/SmartStore.Core/Data/Hooks/HookableAttribute.cs +++ b/src/Libraries/SmartStore.Core/Data/Hooks/HookableAttribute.cs @@ -2,21 +2,21 @@ namespace SmartStore.Core.Data.Hooks { - /// - /// Turns hooking for a specific entity type explicitly on or off. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] - public sealed class HookableAttribute : Attribute - { - public HookableAttribute(bool isHookable) - { - IsHookable = IsHookable; - } + /// + /// Turns hooking for a specific entity type explicitly on or off. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] + public sealed class HookableAttribute : Attribute + { + public HookableAttribute(bool isHookable) + { + IsHookable = IsHookable; + } - public bool IsHookable - { - get; - private set; - } - } + public bool IsHookable + { + get; + private set; + } + } } diff --git a/src/Libraries/SmartStore.Core/Data/Hooks/HookedEntity.cs b/src/Libraries/SmartStore.Core/Data/Hooks/HookedEntity.cs index 2175d189d9..11a9944a32 100644 --- a/src/Libraries/SmartStore.Core/Data/Hooks/HookedEntity.cs +++ b/src/Libraries/SmartStore.Core/Data/Hooks/HookedEntity.cs @@ -4,135 +4,135 @@ namespace SmartStore.Core.Data.Hooks { - public interface IHookedEntity - { - /// - /// Gets the hooked entity entry - /// - DbEntityEntry Entry { get; } - - BaseEntity Entity { get; } - - Type EntityType { get; } - - /// - /// Gets or sets the initial (presave) state of the hooked entity. - /// The setter is for internal use only, don't invoke! - /// - EntityState InitialState { get; set; } - - /// - /// Gets or sets the current state of the hooked entity - /// - EntityState State { get; set; } - - /// - /// Gets a value indicating whether the entity state has changed during hooking. - /// - bool HasStateChanged { get; } - - /// - /// Gets a value indicating whether a property has been modified. - /// - /// Name of the property - bool IsPropertyModified(string propertyName); - - /// - /// Gets a value indicating whether the entity is in soft deleted state. - /// This is the case when the entity is an instance of - /// and the value of its Deleted property is true AND has changed since tracking. - /// But when the entity is not in modified state the snapshot comparison is omitted. - /// - bool IsSoftDeleted { get; } - } - - public class HookedEntity : IHookedEntity - { - private Type _entityType; - - public HookedEntity(DbEntityEntry entry) - { - Entry = entry; - InitialState = (EntityState)entry.State; - } - - public DbEntityEntry Entry - { - get; - private set; - } - - public BaseEntity Entity - { - get { return Entry.Entity as BaseEntity; } - } - - public Type EntityType - { - get - { - return _entityType ?? (_entityType = this.Entity?.GetUnproxiedType()); - } - } - - public EntityState InitialState - { - get; - set; - } - - public EntityState State - { - get - { - return (EntityState)Entry.State; - } - set - { - Entry.State = (EfState)((int)value); - } - } - - public bool HasStateChanged - { - get - { - return InitialState != State; - } - } - - public bool IsPropertyModified(string propertyName) - { - Guard.NotEmpty(propertyName, nameof(propertyName)); - - if (State == EntityState.Modified) - { - var prop = Entry.Property(propertyName); - if (prop == null) - { - throw new SmartException($"An entity property '{propertyName}' does not exist."); - } - - return prop.CurrentValue != null && !prop.CurrentValue.Equals(prop.OriginalValue); - } - - return false; - } - - public bool IsSoftDeleted - { - get - { - var entity = Entry.Entity as ISoftDeletable; - if (entity != null) - { - return Entry.State == EfState.Modified - ? entity.Deleted && IsPropertyModified("Deleted") - : entity.Deleted; - } - - return false; - } - } - } + public interface IHookedEntity + { + /// + /// Gets the impl type of the data context that triggered the hook. + /// + Type ContextType { get; } + + /// + /// Gets the hooked entity entry + /// + DbEntityEntry Entry { get; } + + /// + /// Gets the hooked entity instance + /// + BaseEntity Entity { get; } + + /// + /// Gets the unproxied type of the hooked entity instance. + /// + Type EntityType { get; } + + /// + /// Gets or sets the initial (presave) state of the hooked entity. + /// The setter is for internal use only, don't invoke! + /// + EntityState InitialState { get; set; } + + /// + /// Gets or sets the current state of the hooked entity + /// + EntityState State { get; set; } + + /// + /// Gets a value indicating whether the entity state has changed during hooking. + /// + bool HasStateChanged { get; } + + /// + /// Gets a value indicating whether a property has been modified. + /// + /// Name of the property + bool IsPropertyModified(string propertyName); + + /// + /// Gets a value indicating whether the entity is in soft deleted state. + /// This is the case when the entity is an instance of + /// and the value of its Deleted property is true AND has changed since tracking. + /// But when the entity is not in modified state the snapshot comparison is omitted. + /// + bool IsSoftDeleted { get; } + } + + public class HookedEntity : IHookedEntity + { + private Type _entityType; + + public HookedEntity(IDbContext context, DbEntityEntry entry) + : this(context.GetType(), entry) + { + } + + internal HookedEntity(Type contextType, DbEntityEntry entry) + { + ContextType = contextType; + Entry = entry; + InitialState = (EntityState)entry.State; + } + + public Type ContextType + { + get; + } + + public DbEntityEntry Entry + { + get; + } + + public BaseEntity Entity => Entry.Entity as BaseEntity; + + public Type EntityType => _entityType ?? (_entityType = this.Entity?.GetUnproxiedType()); + + public EntityState InitialState + { + get; + set; + } + + public EntityState State + { + get => (EntityState)Entry.State; + set => Entry.State = (EfState)((int)value); + } + + public bool HasStateChanged => InitialState != State; + + public bool IsPropertyModified(string propertyName) + { + Guard.NotEmpty(propertyName, nameof(propertyName)); + + if (State == EntityState.Modified) + { + var prop = Entry.Property(propertyName); + if (prop == null) + { + throw new SmartException($"An entity property '{propertyName}' does not exist."); + } + + return prop.CurrentValue != null && !prop.CurrentValue.Equals(prop.OriginalValue); + } + + return false; + } + + public bool IsSoftDeleted + { + get + { + var entity = Entry.Entity as ISoftDeletable; + if (entity != null) + { + return Entry.State == EfState.Modified + ? entity.Deleted && IsPropertyModified("Deleted") + : entity.Deleted; + } + + return false; + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Data/Hooks/IDbHook.cs b/src/Libraries/SmartStore.Core/Data/Hooks/IDbHook.cs deleted file mode 100644 index 14194258c8..0000000000 --- a/src/Libraries/SmartStore.Core/Data/Hooks/IDbHook.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace SmartStore.Core.Data.Hooks -{ - /// - /// Marker interface for the hooking framework - /// - public interface IDbHook - { - } -} diff --git a/src/Libraries/SmartStore.Core/Data/Hooks/IDbHookHandler.cs b/src/Libraries/SmartStore.Core/Data/Hooks/IDbHookHandler.cs index 9e46634c86..a79af78c99 100644 --- a/src/Libraries/SmartStore.Core/Data/Hooks/IDbHookHandler.cs +++ b/src/Libraries/SmartStore.Core/Data/Hooks/IDbHookHandler.cs @@ -1,54 +1,50 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; namespace SmartStore.Core.Data.Hooks { - public interface IDbHookHandler - { - bool HasImportantSaveHooks(); - - /// - /// Triggers all pre action hooks - /// - /// Entries - /// Whether to trigger only hooks marked with the attribute - /// true if the state of any entry changed - /// The list of actually processed hook instances - IEnumerable TriggerPreSaveHooks(IEnumerable entries, bool importantHooksOnly, out bool anyStateChanged); - - /// - /// Triggers all post action hooks - /// - /// Entries - /// Whether to trigger only hooks marked with the attribute - /// The list of actually processed hook instances - IEnumerable TriggerPostSaveHooks(IEnumerable entries, bool importantHooksOnly); - } - - public sealed class NullDbHookHandler : IDbHookHandler - { - private readonly static IDbHookHandler s_instance = new NullDbHookHandler(); - - public static IDbHookHandler Instance - { - get { return s_instance; } - } - - public bool HasImportantSaveHooks() - { - return false; - } - - public IEnumerable TriggerPreSaveHooks(IEnumerable entries, bool importantHooksOnly, out bool anyStateChanged) - { - anyStateChanged = false; - return Enumerable.Empty(); - } - - public IEnumerable TriggerPostSaveHooks(IEnumerable entries, bool importantHooksOnly) - { - return Enumerable.Empty(); - } - } + public interface IDbHookHandler + { + bool HasImportantSaveHooks(); + + /// + /// Triggers all pre action hooks + /// + /// Entries + /// Whether to trigger only hooks marked with the attribute + /// true if the state of any entry changed + /// The list of actually processed hook instances + IEnumerable TriggerPreSaveHooks(IEnumerable entries, bool importantHooksOnly, out bool anyStateChanged); + + /// + /// Triggers all post action hooks + /// + /// Entries + /// Whether to trigger only hooks marked with the attribute + /// The list of actually processed hook instances + IEnumerable TriggerPostSaveHooks(IEnumerable entries, bool importantHooksOnly); + } + + public sealed class NullDbHookHandler : IDbHookHandler + { + private readonly static IDbHookHandler s_instance = new NullDbHookHandler(); + + public static IDbHookHandler Instance => s_instance; + + public bool HasImportantSaveHooks() + { + return false; + } + + public IEnumerable TriggerPreSaveHooks(IEnumerable entries, bool importantHooksOnly, out bool anyStateChanged) + { + anyStateChanged = false; + return Enumerable.Empty(); + } + + public IEnumerable TriggerPostSaveHooks(IEnumerable entries, bool importantHooksOnly) + { + return Enumerable.Empty(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Data/Hooks/IDbLoadHook.cs b/src/Libraries/SmartStore.Core/Data/Hooks/IDbLoadHook.cs deleted file mode 100644 index 669ffc9dec..0000000000 --- a/src/Libraries/SmartStore.Core/Data/Hooks/IDbLoadHook.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace SmartStore.Core.Data.Hooks -{ - /// - /// A hook that is executed right after an entity has been loaded from database and materialized. - /// - [Obsolete("Not called anymore for performance reasons.")] - public interface IDbLoadHook : IDbHook - { - void OnLoaded(BaseEntity entity); - } -} diff --git a/src/Libraries/SmartStore.Core/Data/Hooks/IDbSaveHook.cs b/src/Libraries/SmartStore.Core/Data/Hooks/IDbSaveHook.cs index 3a1161aa39..f68c7ceab3 100644 --- a/src/Libraries/SmartStore.Core/Data/Hooks/IDbSaveHook.cs +++ b/src/Libraries/SmartStore.Core/Data/Hooks/IDbSaveHook.cs @@ -2,27 +2,38 @@ namespace SmartStore.Core.Data.Hooks { - /// - /// A hook that is executed before and after a database save operation. - /// An implementor should raise or - /// to signal the hook handler - /// that it never should process the hook again for the current - /// EntityType/State/Stage combination. - /// - public interface IDbSaveHook : IDbHook - { - void OnBeforeSave(IHookedEntity entry); + /// + /// A hook that is executed before and after a database save operation. + /// An implementor should raise or + /// to signal the hook handler + /// that it never should process the hook again for the current + /// EntityType/State/Stage combination. + /// + public interface IDbSaveHook + { + void OnBeforeSave(IHookedEntity entry); - void OnAfterSave(IHookedEntity entry); + void OnAfterSave(IHookedEntity entry); - /// - /// Called after all entities in the current unit of work has been handled right before saving changes to the database - /// - void OnBeforeSaveCompleted(); + /// + /// Called after all entities in the current unit of work has been handled right before saving changes to the database + /// + void OnBeforeSaveCompleted(); - /// - /// Called after all entities in the current unit of work has been handled after saving changes to the database - /// - void OnAfterSaveCompleted(); - } + /// + /// Called after all entities in the current unit of work has been handled after saving changes to the database + /// + void OnAfterSaveCompleted(); + } + + /// + /// + /// Restricts the hook to the specified data context implementation type. + /// To restrict to the core data context, implement the parameterless instead. + /// Abstract base types can also be specified in order to bypass restrictions. + /// + public interface IDbSaveHook : IDbSaveHook + where TContext : IDbContext + { + } } diff --git a/src/Libraries/SmartStore.Core/Data/Hooks/ImportantAttribute.cs b/src/Libraries/SmartStore.Core/Data/Hooks/ImportantAttribute.cs index 585988ee7c..d16b434a2a 100644 --- a/src/Libraries/SmartStore.Core/Data/Hooks/ImportantAttribute.cs +++ b/src/Libraries/SmartStore.Core/Data/Hooks/ImportantAttribute.cs @@ -2,11 +2,11 @@ namespace SmartStore.Core.Data.Hooks { - /// - /// Indicates that a hook instance should run in any case, even when hooking has been turned off. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public sealed class ImportantAttribute : Attribute - { - } + /// + /// Indicates that a hook instance should run in any case, even when hooking has been turned off. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class ImportantAttribute : Attribute + { + } } diff --git a/src/Libraries/SmartStore.Core/Data/IDataProvider.cs b/src/Libraries/SmartStore.Core/Data/IDataProvider.cs index e4926ba0b0..81fd7d9108 100644 --- a/src/Libraries/SmartStore.Core/Data/IDataProvider.cs +++ b/src/Libraries/SmartStore.Core/Data/IDataProvider.cs @@ -1,5 +1,4 @@ - -using System.Data.Common; +using System.Data.Common; namespace SmartStore.Core.Data { @@ -8,7 +7,6 @@ namespace SmartStore.Core.Data /// public interface IDataProvider { - /// /// A value indicating whether this data provider supports stored procedures /// @@ -20,9 +18,9 @@ public interface IDataProvider /// Parameter DbParameter GetParameter(); - /// - /// Gets the db provider invariant name (e.g. System.Data.SqlClient) - /// - string ProviderInvariantName { get; } + /// + /// Gets the db provider invariant name (e.g. System.Data.SqlClient) + /// + string ProviderInvariantName { get; } } } diff --git a/src/Libraries/SmartStore.Core/Data/IDbContext.cs b/src/Libraries/SmartStore.Core/Data/IDbContext.cs index f043ccf175..95224e0308 100644 --- a/src/Libraries/SmartStore.Core/Data/IDbContext.cs +++ b/src/Libraries/SmartStore.Core/Data/IDbContext.cs @@ -3,20 +3,18 @@ using System.Data; using System.Data.Common; using System.Data.Entity; -using System.Linq; -using System.Linq.Expressions; using System.Threading.Tasks; namespace SmartStore.Core.Data { - public interface IDbContext + public interface IDbContext { DbSet Set() where TEntity : BaseEntity; int SaveChanges(); - Task SaveChangesAsync(); + Task SaveChangesAsync(); - IList ExecuteStoredProcedureList(string commandText, params object[] parameters) + IList ExecuteStoredProcedureList(string commandText, params object[] parameters) where TEntity : BaseEntity, new(); /// @@ -27,7 +25,7 @@ IList ExecuteStoredProcedureList(string commandText, params ob /// The parameters to apply to the SQL query string. /// Result IEnumerable SqlQuery(string sql, params object[] parameters); - + /// /// Executes the given DDL/DML command against the database. /// @@ -39,72 +37,72 @@ IList ExecuteStoredProcedureList(string commandText, params ob string Alias { get; } - // increasing performance on bulk operations + // Increasing performance on bulk operations bool ProxyCreationEnabled { get; set; } - bool LazyLoadingEnabled { get; set; } - bool AutoDetectChangesEnabled { get; set; } + bool LazyLoadingEnabled { get; set; } + bool AutoDetectChangesEnabled { get; set; } bool ValidateOnSaveEnabled { get; set; } - bool HooksEnabled { get; set; } + bool HooksEnabled { get; set; } bool HasChanges { get; } - /// - /// Gets or sets a value indicating whether entities returned from queries - /// or created from stored procedures - /// should automatically be attached to the DbContext. - /// - /// - /// Set this to true only during long running processes (like export) - /// - bool ForceNoTracking { get; set; } - - /// - /// Gets or sets a value indicating whether database write operations - /// originating from repositories should be committed immediately. - /// - bool AutoCommitEnabled { get; set; } - - /// - /// Detects changes made to the properties and relationships of POCO entities. - /// Please note that normally DetectChanges is called automatically by many of the methods of - /// DbContext and its related classes such that it is rare that this method will need to be called explicitly. - /// However, it may be desirable, usually for performance reasons, to turn off this automatic - /// calling of DetectChanges using the AutoDetectChangesEnabled flag. - /// - void DetectChanges(); - - /// - /// Checks whether the underlying ORM mapper is currently in the process of detecting changes. - /// - /// - bool IsDetectingChanges(); - - /// - /// Gets a value indicating whether the given entity was modified since it has been attached to the context - /// - /// The entity to check - /// true if the entity was modified, false otherwise - bool IsModified(BaseEntity entity); - - /// - /// Determines whether an entity property has changed since it was attached. - /// - /// Entity - /// The property name to check - /// The previous/original property value if change was detected - /// true if property has changed, false otherwise - bool TryGetModifiedProperty(BaseEntity entity, string propertyName, out object originalValue); - - /// - /// Gets a list of modified properties for the specified entity - /// - /// The entity instance for which to get modified properties for - /// - /// A dictionary, where the key is the name of the modified property - /// and the value is its ORIGINAL value (which was tracked when the entity - /// was attached to the context the first time) - /// Returns an empty dictionary if no modification could be detected. - /// - IDictionary GetModifiedProperties(BaseEntity entity); + /// + /// Gets or sets a value indicating whether entities returned from queries + /// or created from stored procedures + /// should automatically be attached to the DbContext. + /// + /// + /// Set this to true only during long running processes (like export) + /// + bool ForceNoTracking { get; set; } + + /// + /// Gets or sets a value indicating whether database write operations + /// originating from repositories should be committed immediately. + /// + bool AutoCommitEnabled { get; set; } + + /// + /// Detects changes made to the properties and relationships of POCO entities. + /// Please note that normally DetectChanges is called automatically by many of the methods of + /// DbContext and its related classes such that it is rare that this method will need to be called explicitly. + /// However, it may be desirable, usually for performance reasons, to turn off this automatic + /// calling of DetectChanges using the AutoDetectChangesEnabled flag. + /// + void DetectChanges(); + + /// + /// Checks whether the underlying ORM mapper is currently in the process of detecting changes. + /// + /// + bool IsDetectingChanges(); + + /// + /// Gets a value indicating whether the given entity was modified since it has been attached to the context + /// + /// The entity to check + /// true if the entity was modified, false otherwise + bool IsModified(BaseEntity entity); + + /// + /// Determines whether an entity property has changed since it was attached. + /// + /// Entity + /// The property name to check + /// The previous/original property value if change was detected + /// true if property has changed, false otherwise + bool TryGetModifiedProperty(BaseEntity entity, string propertyName, out object originalValue); + + /// + /// Gets a list of modified properties for the specified entity + /// + /// The entity instance for which to get modified properties for + /// + /// A dictionary, where the key is the name of the modified property + /// and the value is its ORIGINAL value (which was tracked when the entity + /// was attached to the context the first time) + /// Returns an empty dictionary if no modification could be detected. + /// + IDictionary GetModifiedProperties(BaseEntity entity); /// /// Determines whether the given entity is already attached to the current object context @@ -114,63 +112,67 @@ IList ExecuteStoredProcedureList(string commandText, params ob /// true when the entity is attched already, false otherwise bool IsAttached(TEntity entity) where TEntity : BaseEntity; - /// - /// Attaches an entity to the context or returns an already attached entity (if it was already attached) - /// - /// Type of entity - /// Entity - /// Attached entity - TEntity Attach(TEntity entity) where TEntity : BaseEntity; - - /// - /// Detaches an entity from the current object context if it's attached - /// - /// Type of entity - /// The entity instance to detach - void DetachEntity(TEntity entity) where TEntity : BaseEntity; - - /// - /// Detaches all entities of type TEntity from the current object context - /// - /// When true, only entities in unchanged state get detached. - /// The count of detached entities - int DetachEntities(bool unchangedEntitiesOnly = true) where TEntity : class; - - /// - /// Detaches all entities matching the passed from the current object context - /// - /// When true, only entities in unchanged state get detached. - /// The count of detached entities - int DetachEntities(Func predicate, bool unchangedEntitiesOnly = true); - - /// - /// Change the state of an entity object - /// - /// Type of entity - /// The entity instance - /// The requested new state - void ChangeState(TEntity entity, System.Data.Entity.EntityState requestedState) where TEntity : BaseEntity; - - /// - /// Reloads the entity from the database overwriting any property values with values from the database. - /// The entity will be in the Unchanged state after calling this method. - /// - /// Type of entity - /// The entity instance - void ReloadEntity(TEntity entity) where TEntity : BaseEntity; - - /// - /// Begins a transaction on the underlying store connection using the specified isolation level - /// - /// The database isolation level with which the underlying store transaction will be created - /// A transaction object wrapping access to the underlying store's transaction object - ITransaction BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified); - - /// - /// Enables the user to pass in a database transaction created outside of the Database object if you want the Entity Framework to execute commands within that external transaction. Alternatively, pass in null to clear the framework's knowledge of that transaction. - /// - /// the external transaction - void UseTransaction(DbTransaction transaction); + /// + /// Attaches an entity to the context or returns an already attached entity (if it was already attached) + /// + /// Type of entity + /// Entity + /// Attached entity + TEntity Attach(TEntity entity) where TEntity : BaseEntity; + + /// + /// Detaches an entity from the current object context if it's attached + /// + /// Type of entity + /// The entity instance to detach + /// Whether to scan all navigation properties and detach them recursively also. + /// The count of detached entities + int DetachEntity(TEntity entity, bool deep = false) where TEntity : BaseEntity; + + /// + /// Detaches all entities of type TEntity from the current object context + /// + /// When true, only entities in unchanged state get detached. + /// Whether to scan all navigation properties and detach them recursively also. LazyLoading should be turned off when true. + /// The count of detached entities + int DetachEntities(bool unchangedEntitiesOnly = true, bool deep = false) where TEntity : BaseEntity; + + /// + /// Detaches all entities matching the passed from the current object context + /// + /// When true, only entities in unchanged state will be detached. + /// Whether to scan all navigation properties and detach them recursively also. + /// The count of detached entities + int DetachEntities(Func predicate, bool unchangedEntitiesOnly = true, bool deep = false); + + /// + /// Change the state of an entity object + /// + /// Type of entity + /// The entity instance + /// The requested new state + void ChangeState(TEntity entity, System.Data.Entity.EntityState requestedState) where TEntity : BaseEntity; + + /// + /// Reloads the entity from the database overwriting any property values with values from the database. + /// The entity will be in the Unchanged state after calling this method. + /// + /// Type of entity + /// The entity instance + void ReloadEntity(TEntity entity) where TEntity : BaseEntity; + + /// + /// Begins a transaction on the underlying store connection using the specified isolation level + /// + /// The database isolation level with which the underlying store transaction will be created + /// A transaction object wrapping access to the underlying store's transaction object + ITransaction BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified); + + /// + /// Enables the user to pass in a database transaction created outside of the Database object if you want the Entity Framework to execute commands within that external transaction. Alternatively, pass in null to clear the framework's knowledge of that transaction. + /// + /// the external transaction + void UseTransaction(DbTransaction transaction); } } diff --git a/src/Libraries/SmartStore.Core/Data/IDbContextExtensions.cs b/src/Libraries/SmartStore.Core/Data/IDbContextExtensions.cs index 0d56838cce..430f4eabee 100644 --- a/src/Libraries/SmartStore.Core/Data/IDbContextExtensions.cs +++ b/src/Libraries/SmartStore.Core/Data/IDbContextExtensions.cs @@ -1,205 +1,208 @@ using System; using System.Collections.Generic; -using SmartStore.Core; -using SmartStore.Core.Data; +using System.Data.Entity; using System.Linq; using System.Linq.Expressions; -using System.Data.Entity; +using SmartStore.Core; +using SmartStore.Core.Data; namespace SmartStore { - public static class IDbContextExtensions - { - /// - /// Detaches all entities from the current object context - /// - /// When true, only entities in unchanged state get detached. - /// The count of detached entities - public static int DetachAll(this IDbContext ctx, bool unchangedEntitiesOnly = true) - { - return ctx.DetachEntities(unchangedEntitiesOnly); - } - - public static void DetachEntities(this IDbContext ctx, IEnumerable entities) where TEntity : BaseEntity - { - Guard.NotNull(ctx, nameof(ctx)); - - entities.Each(x => ctx.DetachEntity(x)); - } - - /// - /// Changes the object state to unchanged - /// - /// Type of entity - /// - /// The entity instance - /// true on success, false on failure - public static bool SetToUnchanged(this IDbContext ctx, TEntity entity) where TEntity : BaseEntity - { - try - { - ctx.ChangeState(entity, System.Data.Entity.EntityState.Unchanged); - return true; - } - catch (Exception ex) - { - ex.Dump(); - return false; - } - } - - public static IQueryable QueryForCollection( - this IDbContext ctx, - TEntity entity, - Expression>> navigationProperty) - where TEntity : BaseEntity - where TCollection : BaseEntity - { - Guard.NotNull(entity, nameof(entity)); - Guard.NotNull(navigationProperty, nameof(navigationProperty)); - - var dbContext = ctx as DbContext; - if (dbContext == null) - { - throw new NotSupportedException("The IDbContext instance does not inherit from DbContext (EF)"); - } - - return dbContext.Entry(entity).Collection(navigationProperty).Query(); - } - - public static IQueryable QueryForReference( - this IDbContext ctx, - TEntity entity, - Expression> navigationProperty) - where TEntity : BaseEntity - where TProperty : BaseEntity - { - Guard.NotNull(entity, nameof(entity)); - Guard.NotNull(navigationProperty, nameof(navigationProperty)); - - var dbContext = ctx as DbContext; - if (dbContext == null) - { - throw new NotSupportedException("The IDbContext instance does not inherit from DbContext (EF)"); - } - - return dbContext.Entry(entity).Reference(navigationProperty).Query(); - } - - public static void LoadCollection( - this IDbContext ctx, - TEntity entity, - Expression>> navigationProperty, - bool force = false, - Func, IQueryable> queryAction = null) - where TEntity : BaseEntity - where TCollection : BaseEntity - { - Guard.NotNull(entity, nameof(entity)); - Guard.NotNull(navigationProperty, nameof(navigationProperty)); - - var dbContext = ctx as DbContext; - if (dbContext == null) - { - throw new NotSupportedException("The IDbContext instance does not inherit from DbContext (EF)"); - } - - var entry = dbContext.Entry(entity); - var collection = entry.Collection(navigationProperty); - - // Avoid System.InvalidOperationException: Member 'IsLoaded' cannot be called for property... - if (entry.State == System.Data.Entity.EntityState.Detached) - { - ctx.Attach(entity); - } - - if (force) - { - collection.IsLoaded = false; - } - - if (!collection.IsLoaded) - { - if (queryAction != null || ctx.ForceNoTracking) - { - var query = !ctx.ForceNoTracking - ? collection.Query() - : collection.Query().AsNoTracking(); - - var myQuery = queryAction != null - ? queryAction(query) - : query; - - collection.CurrentValue = myQuery.ToList(); - } - else - { - collection.Load(); - } - - collection.IsLoaded = true; - } - } - - public static void LoadReference( - this IDbContext ctx, - TEntity entity, - Expression> navigationProperty, - bool force = false, - Func, IQueryable> queryAction = null) - where TEntity : BaseEntity - where TProperty : BaseEntity - { - Guard.NotNull(entity, nameof(entity)); - Guard.NotNull(navigationProperty, nameof(navigationProperty)); - - var dbContext = ctx as DbContext; - if (dbContext == null) - { - throw new NotSupportedException("The IDbContext instance does not inherit from DbContext (EF)"); - } - - var entry = dbContext.Entry(entity); - var reference = entry.Reference(navigationProperty); - - // Avoid System.InvalidOperationException: Member 'IsLoaded' cannot be called for property... - if (entry.State == System.Data.Entity.EntityState.Detached) - { - ctx.Attach(entity); - } - - if (force) - { - reference.IsLoaded = false; - } - - if (!reference.IsLoaded) - { - if (queryAction != null || ctx.ForceNoTracking) - { - var query = !ctx.ForceNoTracking - ? reference.Query() - : reference.Query().AsNoTracking(); - - var myQuery = queryAction != null - ? queryAction(query) - : query; - - reference.CurrentValue = myQuery.FirstOrDefault(); - } - else - { - reference.Load(); - } - - reference.IsLoaded = true; - } - } - - public static void AttachRange(this IDbContext ctx, IEnumerable entities) where TEntity : BaseEntity - { - entities.Each(x => ctx.Attach(x)); - } - - } + public static class IDbContextExtensions + { + /// + /// Detaches all entities from the current object context + /// + /// When true, only entities in unchanged state get detached. + /// The count of detached entities + public static int DetachAll(this IDbContext ctx, bool unchangedEntitiesOnly = true) + { + return ctx.DetachEntities(unchangedEntitiesOnly, false); + } + + public static void DetachEntities(this IDbContext ctx, IEnumerable entities, bool deep = false) where TEntity : BaseEntity + { + Guard.NotNull(ctx, nameof(ctx)); + + using (new DbContextScope(ctx, autoDetectChanges: false, lazyLoading: false)) + { + entities.Each(x => ctx.DetachEntity(x, deep)); + } + } + + /// + /// Changes the object state to unchanged + /// + /// Type of entity + /// + /// The entity instance + /// true on success, false on failure + public static bool SetToUnchanged(this IDbContext ctx, TEntity entity) where TEntity : BaseEntity + { + try + { + ctx.ChangeState(entity, System.Data.Entity.EntityState.Unchanged); + return true; + } + catch (Exception ex) + { + ex.Dump(); + return false; + } + } + + public static IQueryable QueryForCollection( + this IDbContext ctx, + TEntity entity, + Expression>> navigationProperty) + where TEntity : BaseEntity + where TCollection : BaseEntity + { + Guard.NotNull(entity, nameof(entity)); + Guard.NotNull(navigationProperty, nameof(navigationProperty)); + + var dbContext = ctx as DbContext; + if (dbContext == null) + { + throw new NotSupportedException("The IDbContext instance does not inherit from DbContext (EF)"); + } + + return dbContext.Entry(entity).Collection(navigationProperty).Query(); + } + + public static IQueryable QueryForReference( + this IDbContext ctx, + TEntity entity, + Expression> navigationProperty) + where TEntity : BaseEntity + where TProperty : BaseEntity + { + Guard.NotNull(entity, nameof(entity)); + Guard.NotNull(navigationProperty, nameof(navigationProperty)); + + var dbContext = ctx as DbContext; + if (dbContext == null) + { + throw new NotSupportedException("The IDbContext instance does not inherit from DbContext (EF)"); + } + + return dbContext.Entry(entity).Reference(navigationProperty).Query(); + } + + public static void LoadCollection( + this IDbContext ctx, + TEntity entity, + Expression>> navigationProperty, + bool force = false, + Func, IQueryable> queryAction = null) + where TEntity : BaseEntity + where TCollection : BaseEntity + { + Guard.NotNull(entity, nameof(entity)); + Guard.NotNull(navigationProperty, nameof(navigationProperty)); + + var dbContext = ctx as DbContext; + if (dbContext == null) + { + throw new NotSupportedException("The IDbContext instance does not inherit from DbContext (EF)"); + } + + var entry = dbContext.Entry(entity); + var collection = entry.Collection(navigationProperty); + + // Avoid System.InvalidOperationException: Member 'IsLoaded' cannot be called for property... + if (entry.State == System.Data.Entity.EntityState.Detached) + { + ctx.Attach(entity); + } + + if (force) + { + collection.IsLoaded = false; + } + + if (!collection.IsLoaded) + { + if (queryAction != null || ctx.ForceNoTracking) + { + var query = !ctx.ForceNoTracking + ? collection.Query() + : collection.Query().AsNoTracking(); + + var myQuery = queryAction != null + ? queryAction(query) + : query; + + collection.CurrentValue = myQuery.ToList(); + } + else + { + collection.Load(); + } + + collection.IsLoaded = true; + } + } + + public static void LoadReference( + this IDbContext ctx, + TEntity entity, + Expression> navigationProperty, + bool force = false, + Func, IQueryable> queryAction = null) + where TEntity : BaseEntity + where TProperty : BaseEntity + { + Guard.NotNull(entity, nameof(entity)); + Guard.NotNull(navigationProperty, nameof(navigationProperty)); + + var dbContext = ctx as DbContext; + if (dbContext == null) + { + throw new NotSupportedException("The IDbContext instance does not inherit from DbContext (EF)"); + } + + var entry = dbContext.Entry(entity); + var reference = entry.Reference(navigationProperty); + + // Avoid System.InvalidOperationException: Member 'IsLoaded' cannot be called for property... + if (entry.State == System.Data.Entity.EntityState.Detached) + { + ctx.Attach(entity); + } + + if (force) + { + reference.IsLoaded = false; + } + + if (!reference.IsLoaded) + { + if (queryAction != null || ctx.ForceNoTracking) + { + var query = !ctx.ForceNoTracking + ? reference.Query() + : reference.Query().AsNoTracking(); + + var myQuery = queryAction != null + ? queryAction(query) + : query; + + reference.CurrentValue = myQuery.FirstOrDefault(); + } + else + { + reference.Load(); + } + + reference.IsLoaded = true; + } + } + + public static void AttachRange(this IDbContext ctx, IEnumerable entities) where TEntity : BaseEntity + { + entities.Each(x => ctx.Attach(x)); + } + + } } diff --git a/src/Libraries/SmartStore.Core/Data/IQueryableExtensions.cs b/src/Libraries/SmartStore.Core/Data/IQueryableExtensions.cs index d9534c06ea..dc5a3eed3e 100644 --- a/src/Libraries/SmartStore.Core/Data/IQueryableExtensions.cs +++ b/src/Libraries/SmartStore.Core/Data/IQueryableExtensions.cs @@ -1,43 +1,42 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Data.Entity; +using System.Linq; using System.Linq.Expressions; using SmartStore.Core; namespace SmartStore { - public static class IQueryableExtensions - { - /// - /// Instructs the repository to eager load entities that may be in the type's association path. - /// - /// A previously created query object which the expansion should be applied to. - /// - /// The path of the child entities to eager load. - /// Deeper paths can be specified by separating the path with dots. - /// - /// A new query object to which the expansion was applied. - public static IQueryable Expand(this IQueryable query, string path) where T : BaseEntity - { - Guard.NotNull(query, "query"); - Guard.NotEmpty(path, "path"); + public static class IQueryableExtensions + { + /// + /// Instructs the repository to eager load entities that may be in the type's association path. + /// + /// A previously created query object which the expansion should be applied to. + /// + /// The path of the child entities to eager load. + /// Deeper paths can be specified by separating the path with dots. + /// + /// A new query object to which the expansion was applied. + public static IQueryable Expand(this IQueryable query, string path) where T : BaseEntity + { + Guard.NotNull(query, "query"); + Guard.NotEmpty(path, "path"); - return query.Include(path); - } + return query.Include(path); + } - /// - /// Instructs the repository to eager load entities that may be in the type's association path. - /// - /// A previously created query object which the expansion should be applied to. - /// The path of the child entities to eager load. - /// A new query object to which the expansion was applied. - public static IQueryable Expand(this IQueryable query, Expression> path) where T : BaseEntity - { - Guard.NotNull(query, "query"); - Guard.NotNull(path, "path"); + /// + /// Instructs the repository to eager load entities that may be in the type's association path. + /// + /// A previously created query object which the expansion should be applied to. + /// The path of the child entities to eager load. + /// A new query object to which the expansion was applied. + public static IQueryable Expand(this IQueryable query, Expression> path) where T : BaseEntity + { + Guard.NotNull(query, "query"); + Guard.NotNull(path, "path"); - return query.Include(path); - } - } + return query.Include(path); + } + } } diff --git a/src/Libraries/SmartStore.Core/Data/IRepository.cs b/src/Libraries/SmartStore.Core/Data/IRepository.cs index 8337168c58..b555b731cf 100644 --- a/src/Libraries/SmartStore.Core/Data/IRepository.cs +++ b/src/Libraries/SmartStore.Core/Data/IRepository.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Threading.Tasks; namespace SmartStore.Core.Data { @@ -21,10 +22,10 @@ public partial interface IRepository where T : BaseEntity /// IQueryable TableUntracked { get; } - /// - /// Provides access to the entities currently being tracked by the context and have not been marked as deleted - /// - ICollection Local { get; } + /// + /// Provides access to the entities currently being tracked by the context and have not been marked as deleted + /// + ICollection Local { get; } /// /// Creates a new instance of an entity of type {T} @@ -39,20 +40,34 @@ public partial interface IRepository where T : BaseEntity /// The resolved entity T GetById(object id); - /// - /// Attaches an entity to the context - /// - /// The entity to attach - /// The entity - T Attach(T entity); + /// + /// Gets an entity by id from the database or the local change tracker asynchronously. + /// + /// The id of the entity. This can also be a composite key. + /// The resolved entity + Task GetByIdAsync(object id); /// - /// Marks the entity instance to be saved to the store. + /// Attaches an entity to the context + /// + /// The entity to attach + /// The entity + T Attach(T entity); + + /// + /// Marks an entity instance to be saved to the store and saves immediately if is true /// /// An entity instance that should be saved to the database. /// Implementors should delegate this to the current void Insert(T entity); + /// + /// Marks an entity instance to be saved to the store and saves immediately if is true. + /// + /// An entity instance that should be saved to the database. + /// Implementors should delegate this to the current + Task InsertAsync(T entity); + /// /// Marks multiple entities to be saved to the store. /// @@ -61,37 +76,72 @@ public partial interface IRepository where T : BaseEntity void InsertRange(IEnumerable entities, int batchSize = 100); /// - /// Marks the changes of an existing entity to be saved to the store. + /// Marks multiple entities to be saved to the store and saves immediately if is true. + /// + /// The list of entity instances to be saved to the database + /// The number of entities to insert before saving to the database (if is true) + Task InsertRangeAsync(IEnumerable entities, int batchSize = 100); + + /// + /// Marks the changes of an existing entity to be saved to the store and saves immediately if is true. /// /// An instance that should be updated in the database. /// Implementors should delegate this to the current void Update(T entity); - /// - /// Marks the changes of existing entities to be saved to the store. - /// - /// A list of entity instances that should be updated in the database. - /// Implementors should delegate this to the current - void UpdateRange(IEnumerable entities); + /// + /// Marks the changes of an existing entity to be saved to the store and saves immediately if is true. + /// + /// An instance that should be updated in the database. + /// Implementors should delegate this to the current + Task UpdateAsync(T entity); + + /// + /// Marks the changes of existing entities to be saved to the store. + /// + /// A list of entity instances that should be updated in the database. + /// Implementors should delegate this to the current + void UpdateRange(IEnumerable entities); /// - /// Marks an existing entity to be deleted from the store. + /// Marks the changes of existing entities to be saved to the store and saves immediately if is true.. + /// + /// A list of entity instances that should be updated in the database. + /// Implementors should delegate this to the current + Task UpdateRangeAsync(IEnumerable entities); + + /// + /// Marks an existing entity to be deleted from the store and saves immediately if is true. /// /// An entity instance that should be deleted from the database. /// Implementors should delegate this to the current void Delete(T entity); - /// - /// Marks existing entities to be deleted from the store. - /// - /// A list of entity instances that should be deleted from the database. - /// Implementors should delegate this to the current - void DeleteRange(IEnumerable entities); + /// + /// Marks an existing entity to be deleted from the store and saves immediately if is true. + /// + /// An entity instance that should be deleted from the database. + /// Implementors should delegate this to the current + Task DeleteAsync(T entity); + + /// + /// Marks existing entities to be deleted from the store. + /// + /// A list of entity instances that should be deleted from the database. + /// Implementors should delegate this to the current + void DeleteRange(IEnumerable entities); + + /// + /// Marks existing entities to be deleted from the store and saves immediately if is true.. + /// + /// A list of entity instances that should be deleted from the database. + /// Implementors should delegate this to the current + Task DeleteRangeAsync(IEnumerable entities); - [Obsolete("Use the extension method from 'SmartStore.Core, SmartStore.Core.Data' instead")] + [Obsolete("Use the extension method from 'SmartStore.Core, SmartStore.Core.Data' instead")] IQueryable Expand(IQueryable query, string path); - [Obsolete("Use the extension method from 'SmartStore.Core, SmartStore.Core.Data' instead")] + [Obsolete("Use the extension method from 'SmartStore.Core, SmartStore.Core.Data' instead")] IQueryable Expand(IQueryable query, Expression> path); /// diff --git a/src/Libraries/SmartStore.Core/Data/ITransaction.cs b/src/Libraries/SmartStore.Core/Data/ITransaction.cs index e20bb41d17..54155bf572 100644 --- a/src/Libraries/SmartStore.Core/Data/ITransaction.cs +++ b/src/Libraries/SmartStore.Core/Data/ITransaction.cs @@ -2,9 +2,9 @@ namespace SmartStore.Core.Data { - public interface ITransaction : IDisposable - { - void Commit(); - void Rollback(); - } + public interface ITransaction : IDisposable + { + void Commit(); + void Rollback(); + } } diff --git a/src/Libraries/SmartStore.Core/Data/RepositoryExtensions.cs b/src/Libraries/SmartStore.Core/Data/RepositoryExtensions.cs index 5041bebcdf..1fefc75eb2 100644 --- a/src/Libraries/SmartStore.Core/Data/RepositoryExtensions.cs +++ b/src/Libraries/SmartStore.Core/Data/RepositoryExtensions.cs @@ -1,20 +1,16 @@ using System; using System.Collections.Generic; +using System.Data.Entity; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; -using System.Text; +using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace SmartStore.Core.Data -{ +{ public static class RepositoryExtensions { - public static T GetFirst(this IRepository rs, Func predicate) where T : BaseEntity - { - return rs.Table.FirstOrDefault(predicate); - } - public static IEnumerable GetMany(this IRepository rs, IEnumerable ids) where T : BaseEntity { foreach (var chunk in ids.Slice(128)) @@ -27,126 +23,140 @@ public static IEnumerable GetMany(this IRepository rs, IEnumerable } } + public static async Task> GetManyAsync(this IRepository rs, IEnumerable ids) where T : BaseEntity + { + var result = new List(); + + foreach (var chunk in ids.Slice(128)) + { + var items = await rs.Table.Where(a => chunk.Contains(a.Id)).ToListAsync(); + result.AddRange(items); + } + + return result; + } + public static void Delete(this IRepository rs, int id) where T : BaseEntity { - Guard.NotZero(id, nameof(id)); - - // Perf: work with stub entity - var entity = rs.Create(); - entity.Id = id; - - // must downcast 'cause of Rhino mocks stub - rs.Context.ChangeState((BaseEntity)entity, System.Data.Entity.EntityState.Deleted); + Guard.NotZero(id, nameof(id)); + + // Perf: work with stub entity + var entity = rs.Create(); + entity.Id = id; + + // must downcast 'cause of Rhino mocks stub + rs.Context.ChangeState((BaseEntity)entity, System.Data.Entity.EntityState.Deleted); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void DeleteRange(this IRepository rs, IEnumerable ids) where T : BaseEntity + { + Guard.NotNull(ids, nameof(ids)); + + ids.Each(id => rs.Delete(id)); + } + + /// + /// Truncates the table + /// + /// Entity type + /// The repository + /// An optional filter + /// + /// false: does not make any attempts to determine dependant entities, just deletes ONLY them (faster). + /// true: loads all entities into the context first and deletes them, along with their dependencies (slower). + /// + /// The total number of affected entities + /// + /// This method turns off auto detection, validation and hooking. + /// + [SuppressMessage("ReSharper", "UnusedVariable")] + public static int DeleteAll(this IRepository rs, Expression> predicate = null, bool cascade = false) where T : BaseEntity + { + var count = 0; + + using (var scope = new DbContextScope(ctx: rs.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false)) + { + var query = rs.Table; + if (predicate != null) + { + query = query.Where(predicate); + } + + if (cascade) + { + var records = query.ToList(); + foreach (var chunk in records.Slice(500)) + { + rs.DeleteRange(chunk.ToList()); + count += rs.Context.SaveChanges(); + } + } + else + { + var ids = query.Select(x => new { x.Id }).ToList(); + foreach (var chunk in ids.Slice(500)) + { + rs.DeleteRange(chunk.Select(x => x.Id)); + count += rs.Context.SaveChanges(); + } + } + } + + return count; + } + + /// + /// Truncates the table asynchronously. + /// + /// Entity type + /// The repository + /// An optional filter + /// + /// false: does not make any attempts to determine dependant entities, just deletes ONLY them (faster). + /// true: loads all entities into the context first and deletes them, along with their dependencies (slower). + /// + /// The total number of affected entities + /// + /// This method turns off auto detection, validation and hooking. + /// + [SuppressMessage("ReSharper", "UnusedVariable")] + public async static Task DeleteAllAsync(this IRepository rs, Expression> predicate = null, bool cascade = false) where T : BaseEntity + { + var count = 0; + + using (var scope = new DbContextScope(ctx: rs.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false)) + { + var query = rs.Table; + if (predicate != null) + { + query = query.Where(predicate); + } + + if (cascade) + { + var records = query.ToList(); + foreach (var chunk in records.Slice(500)) + { + rs.DeleteRange(chunk.ToList()); + count += await rs.Context.SaveChangesAsync(); + } + } + else + { + var ids = query.Select(x => new { x.Id }).ToList(); + foreach (var chunk in ids.Slice(500)) + { + rs.DeleteRange(chunk.Select(x => x.Id)); + count += await rs.Context.SaveChangesAsync(); + } + } + } + + return count; } - public static void DeleteRange(this IRepository rs, IEnumerable ids) where T : BaseEntity - { - Guard.NotNull(ids, nameof(ids)); - - ids.Each(id => rs.Delete(id)); - } - - /// - /// Truncates the table - /// - /// Entity type - /// The repository - /// An optional filter - /// - /// false: does not make any attempts to determine dependant entities, just deletes ONLY them (faster). - /// true: loads all entities into the context first and deletes them, along with their dependencies (slower). - /// - /// The total number of affected entities - /// - /// This method turns off auto detection, validation and hooking. - /// - [SuppressMessage("ReSharper", "UnusedVariable")] - public static int DeleteAll(this IRepository rs, Expression> predicate = null, bool cascade = false) where T : BaseEntity - { - var count = 0; - - using (var scope = new DbContextScope(ctx: rs.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false)) - { - var query = rs.Table; - if (predicate != null) - { - query = query.Where(predicate); - } - - if (cascade) - { - var records = query.ToList(); - foreach (var chunk in records.Slice(500)) - { - rs.DeleteRange(chunk.ToList()); - count += rs.Context.SaveChanges(); - } - } - else - { - var ids = query.Select(x => new { Id = x.Id }).ToList(); - foreach (var chunk in ids.Slice(500)) - { - rs.DeleteRange(chunk.Select(x => x.Id)); - count += rs.Context.SaveChanges(); - } - } - } - - return count; - } - - /// - /// Truncates the table asynchronously. - /// - /// Entity type - /// The repository - /// An optional filter - /// - /// false: does not make any attempts to determine dependant entities, just deletes ONLY them (faster). - /// true: loads all entities into the context first and deletes them, along with their dependencies (slower). - /// - /// The total number of affected entities - /// - /// This method turns off auto detection, validation and hooking. - /// - [SuppressMessage("ReSharper", "UnusedVariable")] - public async static Task DeleteAllAsync(this IRepository rs, Expression> predicate = null, bool cascade = false) where T : BaseEntity - { - var count = 0; - - using (var scope = new DbContextScope(ctx: rs.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false)) - { - var query = rs.Table; - if (predicate != null) - { - query = query.Where(predicate); - } - - if (cascade) - { - var records = query.ToList(); - foreach (var chunk in records.Slice(500)) - { - rs.DeleteRange(chunk.ToList()); - count += await rs.Context.SaveChangesAsync(); - } - } - else - { - var ids = query.Select(x => new { x.Id }).ToList(); - foreach (var chunk in ids.Slice(500)) - { - rs.DeleteRange(chunk.Select(x => x.Id)); - count += await rs.Context.SaveChangesAsync(); - } - } - } - - return count; - } - - public static IQueryable Get( + public static IQueryable Get( this IRepository rs, Expression> predicate = null, Func, IOrderedQueryable> orderBy = null, @@ -161,7 +171,7 @@ public static IQueryable Get( foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { - query = query.Expand(includeProperty.Trim()); + query = query.Expand(includeProperty.Trim()); } if (orderBy != null) diff --git a/src/Libraries/SmartStore.Core/Data/SqlFileTokenizer.cs b/src/Libraries/SmartStore.Core/Data/SqlFileTokenizer.cs index 2575c2f79f..4c04b20bb2 100644 --- a/src/Libraries/SmartStore.Core/Data/SqlFileTokenizer.cs +++ b/src/Libraries/SmartStore.Core/Data/SqlFileTokenizer.cs @@ -1,121 +1,117 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; -using System.Linq; using System.Reflection; using System.Text; using SmartStore.Utilities; +using SmartStore.Utilities.ObjectPools; namespace SmartStore.Core.Data { - - public class SqlFileTokenizer - { - - public SqlFileTokenizer(string fileName, Assembly assembly = null, string location = null) - { - Guard.NotEmpty(fileName, nameof(fileName)); - - this.FileName = fileName; - this.Assembly = assembly; - this.Location = location; - } - - public string FileName - { - get; - private set; - } - - public Assembly Assembly - { - get; - private set; - } - - public string Location - { - get; - private set; - } - - public IEnumerable Tokenize() - { - if (this.Assembly == null) - { - this.Assembly = Assembly.GetExecutingAssembly(); - } - - using (var reader = ReadSqlFile()) - { - string statement; - while ((statement = ReadNextSqlStatement(reader)) != null) - { - yield return statement.EmptyNull(); - } - } - } - - protected virtual StreamReader ReadSqlFile() - { - - var fileName = this.FileName; - - if (fileName.StartsWith("~") || fileName.StartsWith("/")) - { - string path = CommonHelper.MapPath(fileName); - if (!File.Exists(path)) - { - throw new FileNotFoundException("Sql file '{0}' not found".FormatInvariant(this.FileName)); - } - - return new StreamReader(File.OpenRead(path)); - } - - // SQL file is obviously an embedded resource - var assembly = this.Assembly; - var asmName = assembly.FullName.Substring(0, assembly.FullName.IndexOf(',')); - var location = this.Location ?? asmName + ".Sql"; - var name = String.Format("{0}.{1}", location, fileName); - - try - { - var stream = assembly.GetManifestResourceStream(name); - return new StreamReader(stream); - } - catch (Exception ex) - { - throw new FileLoadException("Error while loading embedded sql resource '{0}'".FormatInvariant(name), ex); - } - } - - private string ReadNextSqlStatement(TextReader reader) - { - var sb = new StringBuilder(); - - string lineOfText; - - while (true) - { - lineOfText = reader.ReadLine(); - if (lineOfText == null) - { - if (sb.Length > 0) - return sb.ToString(); - else - return null; - } - - if (lineOfText.TrimEnd().ToUpper() == "GO") - break; - - sb.Append(lineOfText + Environment.NewLine); - } - - return sb.ToString(); - } - - } - + public class SqlFileTokenizer + { + public SqlFileTokenizer(string fileName, Assembly assembly = null, string location = null) + { + Guard.NotEmpty(fileName, nameof(fileName)); + + this.FileName = fileName; + this.Assembly = assembly; + this.Location = location; + } + + public string FileName + { + get; + private set; + } + + public Assembly Assembly + { + get; + private set; + } + + public string Location + { + get; + private set; + } + + public IEnumerable Tokenize() + { + if (this.Assembly == null) + { + this.Assembly = Assembly.GetExecutingAssembly(); + } + + using (var reader = ReadSqlFile()) + { + string statement; + while ((statement = ReadNextSqlStatement(reader)) != null) + { + yield return statement.EmptyNull(); + } + } + } + + protected virtual StreamReader ReadSqlFile() + { + + var fileName = this.FileName; + + if (fileName.StartsWith("~") || fileName.StartsWith("/")) + { + string path = CommonHelper.MapPath(fileName); + if (!File.Exists(path)) + { + throw new FileNotFoundException("Sql file '{0}' not found".FormatInvariant(this.FileName)); + } + + return new StreamReader(File.OpenRead(path)); + } + + // SQL file is obviously an embedded resource + var assembly = this.Assembly; + var asmName = assembly.FullName.Substring(0, assembly.FullName.IndexOf(',')); + var location = this.Location ?? asmName + ".Sql"; + var name = String.Format("{0}.{1}", location, fileName); + + try + { + var stream = assembly.GetManifestResourceStream(name); + return new StreamReader(stream); + } + catch (Exception ex) + { + throw new FileLoadException("Error while loading embedded sql resource '{0}'".FormatInvariant(name), ex); + } + } + + private string ReadNextSqlStatement(TextReader reader) + { + var psb = PooledStringBuilder.Rent(); + var sb = (StringBuilder)psb; + + string lineOfText; + + while (true) + { + lineOfText = reader.ReadLine(); + if (lineOfText == null) + { + if (sb.Length > 0) + return sb.ToString(); + else + return null; + } + + if (lineOfText.TrimEnd().ToUpper() == "GO") + break; + + sb.Append(lineOfText + Environment.NewLine); + } + + return psb.ToStringAndReturn(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Affiliates/Affiliate.cs b/src/Libraries/SmartStore.Core/Domain/Affiliates/Affiliate.cs index c9cd75e4d1..d4a500cd4a 100644 --- a/src/Libraries/SmartStore.Core/Domain/Affiliates/Affiliate.cs +++ b/src/Libraries/SmartStore.Core/Domain/Affiliates/Affiliate.cs @@ -1,8 +1,5 @@ -using System.Collections.Generic; using System.Runtime.Serialization; using SmartStore.Core.Domain.Common; -using SmartStore.Core.Domain.Customers; -using SmartStore.Core.Domain.Orders; namespace SmartStore.Core.Domain.Affiliates { @@ -10,7 +7,7 @@ namespace SmartStore.Core.Domain.Affiliates /// Represents an affiliate /// [DataContract] - public partial class Affiliate : BaseEntity, ISoftDeletable + public partial class Affiliate : BaseEntity, ISoftDeletable { [DataMember] diff --git a/src/Libraries/SmartStore.Core/Domain/Blogs/BlogComment.cs b/src/Libraries/SmartStore.Core/Domain/Blogs/BlogComment.cs index fce406b0b9..22f323bc72 100644 --- a/src/Libraries/SmartStore.Core/Domain/Blogs/BlogComment.cs +++ b/src/Libraries/SmartStore.Core/Domain/Blogs/BlogComment.cs @@ -3,28 +3,28 @@ namespace SmartStore.Core.Domain.Blogs { - /// - /// Represents a blog comment - /// - [DataContract] - public partial class BlogComment : CustomerContent + /// + /// Represents a blog comment + /// + [DataContract] + public partial class BlogComment : CustomerContent { - /// - /// Gets or sets the comment text - /// - [DataMember] - public string CommentText { get; set; } + /// + /// Gets or sets the comment text + /// + [DataMember] + public string CommentText { get; set; } - /// - /// Gets or sets the blog post identifier - /// - [DataMember] - public int BlogPostId { get; set; } + /// + /// Gets or sets the blog post identifier + /// + [DataMember] + public int BlogPostId { get; set; } - /// - /// Gets or sets the blog post - /// - [DataMember] - public virtual BlogPost BlogPost { get; set; } + /// + /// Gets or sets the blog post + /// + [DataMember] + public virtual BlogPost BlogPost { get; set; } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Blogs/BlogExtensions.cs b/src/Libraries/SmartStore.Core/Domain/Blogs/BlogExtensions.cs index e5cbfc2f5c..1a626e9098 100644 --- a/src/Libraries/SmartStore.Core/Domain/Blogs/BlogExtensions.cs +++ b/src/Libraries/SmartStore.Core/Domain/Blogs/BlogExtensions.cs @@ -6,8 +6,8 @@ namespace SmartStore.Core.Domain.Blogs { public static class BlogExtensions { - [SuppressMessage("ReSharper", "LoopCanBeConvertedToQuery")] - public static string[] ParseTags(this BlogPost blogPost) + [SuppressMessage("ReSharper", "LoopCanBeConvertedToQuery")] + public static string[] ParseTags(this BlogPost blogPost) { if (blogPost == null) throw new ArgumentNullException("blogPost"); @@ -18,9 +18,9 @@ public static string[] ParseTags(this BlogPost blogPost) var tags2 = blogPost.Tags.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (var tag2 in tags2) { - var tmp = tag2.Trim(); - if (!String.IsNullOrEmpty(tmp)) - parsedTags.Add(tmp); + var tmp = tag2.Trim(); + if (!String.IsNullOrEmpty(tmp)) + parsedTags.Add(tmp); } } return parsedTags.ToArray(); diff --git a/src/Libraries/SmartStore.Core/Domain/Blogs/BlogPost.cs b/src/Libraries/SmartStore.Core/Domain/Blogs/BlogPost.cs index 0a58154a36..821d502963 100644 --- a/src/Libraries/SmartStore.Core/Domain/Blogs/BlogPost.cs +++ b/src/Libraries/SmartStore.Core/Domain/Blogs/BlogPost.cs @@ -1,123 +1,228 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; using System.Runtime.Serialization; using SmartStore.Core.Domain.Localization; +using SmartStore.Core.Domain.Media; using SmartStore.Core.Domain.Seo; using SmartStore.Core.Domain.Stores; namespace SmartStore.Core.Domain.Blogs { - /// - /// Represents a blog post - /// - [DataContract] - public partial class BlogPost : BaseEntity, ISlugSupported, IStoreMappingSupported + /// + /// Represents a blog post + /// + [DataContract] + public partial class BlogPost : BaseEntity, ISlugSupported, IStoreMappingSupported, ILocalizedEntity { + #region static + + private static readonly List _visibilityAffectingProps = new List + { + nameof(BlogPost.IsPublished), + nameof(BlogPost.StartDateUtc), + nameof(BlogPost.EndDateUtc), + nameof(BlogPost.LimitedToStores) + }; + + public static IReadOnlyCollection GetVisibilityAffectingPropertyNames() + { + return _visibilityAffectingProps; + } + + #endregion + private ICollection _blogComments; - /// - /// Gets or sets the language identifier - /// - [DataMember] - public int LanguageId { get; set; } - - /// - /// Gets or sets the blog post title - /// - [DataMember] - public string Title { get; set; } - - /// - /// Gets or sets the blog post title - /// - [DataMember] - public string Body { get; set; } - - /// - /// Gets or sets a value indicating whether the blog post comments are allowed - /// - [DataMember] - public bool AllowComments { get; set; } - - /// - /// Gets or sets the total number of approved comments - /// The same as if we run newsItem.NewsComments.Where(n => n.IsApproved).Count() - /// We use this property for performance optimization (no SQL command executed) - /// - /// - [DataMember] - public int ApprovedCommentCount { get; set; } - - /// - /// Gets or sets the total number of not approved comments - /// The same as if we run newsItem.NewsComments.Where(n => !n.IsApproved).Count() - /// We use this property for performance optimization (no SQL command executed) - /// - [DataMember] - public int NotApprovedCommentCount { get; set; } - - /// - /// Gets or sets the blog tags - /// - [DataMember] - public string Tags { get; set; } - - /// - /// Gets or sets the blog post start date and time - /// - [DataMember] - public DateTime? StartDateUtc { get; set; } - - /// - /// Gets or sets the blog post end date and time - /// - [DataMember] - public DateTime? EndDateUtc { get; set; } - - /// - /// Gets or sets the meta keywords - /// - [DataMember] - public string MetaKeywords { get; set; } - - /// - /// Gets or sets the meta description - /// - [DataMember] - public string MetaDescription { get; set; } - - /// - /// Gets or sets the meta title - /// - [DataMember] - public string MetaTitle { get; set; } - - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - [DataMember] - public bool LimitedToStores { get; set; } - - /// - /// Gets or sets the date and time of entity creation - /// - [DataMember] - public virtual DateTime CreatedOnUtc { get; set; } - - /// - /// Gets or sets the blog comments - /// - [DataMember] - public virtual ICollection BlogComments + /// + /// Gets or sets a value indicating whether the blog post comments are allowed + /// + [DataMember] + public bool IsPublished { get; set; } + + /// + /// Gets or sets the blog post title + /// + [DataMember] + public string Title { get; set; } + + /// + /// Defines the preview display type of the picture + /// + [DataMember] + public PreviewDisplayType PreviewDisplayType { get; set; } + + /// + /// Gets or sets the media file identifier + /// + [DataMember] + public int? MediaFileId { get; set; } + + /// + /// Gets or sets the media file. + /// + public virtual MediaFile MediaFile { get; set; } + + /// + /// Gets or sets the preview media file identifier + /// + [DataMember] + public int? PreviewMediaFileId { get; set; } + + /// + /// Gets or sets the preview media file. + /// + public virtual MediaFile PreviewMediaFile { get; set; } + + /// + /// Gets or sets background for the blog post + /// + [DataMember] + public string SectionBg { get; set; } + + /// + /// Gets or sets a value indicating whether the blog post has a background image + /// + [DataMember] + public bool DisplayTagsInPreview { get; set; } + + /// + /// Gets or sets the blog post intro + /// + [DataMember] + public string Intro { get; set; } + + /// + /// Gets or sets the blog post title + /// + [DataMember] + public string Body { get; set; } + + /// + /// Gets or sets a value indicating whether the blog post comments are allowed + /// + [DataMember] + public bool AllowComments { get; set; } + + /// + /// Gets or sets the total number of approved comments + /// The same as if we run newsItem.NewsComments.Where(n => n.IsApproved).Count() + /// We use this property for performance optimization (no SQL command executed) + /// + /// + [DataMember] + public int ApprovedCommentCount { get; set; } + + /// + /// Gets or sets the total number of not approved comments + /// The same as if we run newsItem.NewsComments.Where(n => !n.IsApproved).Count() + /// We use this property for performance optimization (no SQL command executed) + /// + [DataMember] + public int NotApprovedCommentCount { get; set; } + + /// + /// Gets or sets the blog tags + /// + [DataMember] + public string Tags { get; set; } + + /// + /// Gets or sets the blog post start date and time + /// + [DataMember] + public DateTime? StartDateUtc { get; set; } + + /// + /// Gets or sets the blog post end date and time + /// + [DataMember] + public DateTime? EndDateUtc { get; set; } + + /// + /// Gets or sets the meta keywords + /// + [DataMember] + public string MetaKeywords { get; set; } + + /// + /// Gets or sets the meta description + /// + [DataMember] + public string MetaDescription { get; set; } + + /// + /// Gets or sets the meta title + /// + [DataMember] + public string MetaTitle { get; set; } + + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + [DataMember] + public bool LimitedToStores { get; set; } + + /// + /// Gets or sets a language identifier for which the blog post should be displayed. + /// + [DataMember] + [Index] + public int? LanguageId { get; set; } + + /// + /// Gets or sets the date and time of entity creation + /// + [DataMember] + public virtual DateTime CreatedOnUtc { get; set; } + + /// + /// Gets or sets the language. + /// + [DataMember] + public virtual Language Language { get; set; } + + /// + /// Gets or sets the blog comments + /// + [DataMember] + public virtual ICollection BlogComments { - get { return _blogComments ?? (_blogComments = new HashSet()); } - protected set { _blogComments = value; } + get => _blogComments ?? (_blogComments = new HashSet()); + protected set => _blogComments = value; } + } - /// - /// Gets or sets the language - /// - [DataMember] - public virtual Language Language { get; set; } + public enum PreviewDisplayType + { + /// + /// No picture will be displayed + /// + Bare = 0, + + /// + /// The detail picture will be displayed + /// + Default = 10, + + /// + /// The preview picture will be displayed + /// + Preview = 20, + + /// + /// The detail picture will be displayed under the section background + /// + DefaultSectionBg = 30, + + /// + /// The preview picture will be displayed under the section background + /// + PreviewSectionBg = 40, + + /// + /// The section background will be displayed + /// + SectionBg = 50 } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Blogs/BlogSettings.cs b/src/Libraries/SmartStore.Core/Domain/Blogs/BlogSettings.cs index e0c7492934..1bba994e1b 100644 --- a/src/Libraries/SmartStore.Core/Domain/Blogs/BlogSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Blogs/BlogSettings.cs @@ -5,29 +5,20 @@ namespace SmartStore.Core.Domain.Blogs { public class BlogSettings : ISettings { - public BlogSettings() - { - Enabled = true; - PostsPageSize = 10; - AllowNotRegisteredUsersToLeaveComments = true; - NumberOfTags = 15; - MaxAgeInDays = 180; - } - - /// + /// /// Gets or sets a value indicating whether blog is enabled /// - public bool Enabled { get; set; } + public bool Enabled { get; set; } = true; /// /// Gets or sets the page size for posts /// - public int PostsPageSize { get; set; } + public int PostsPageSize { get; set; } = 10; /// /// Gets or sets a value indicating whether not registered user can leave comments /// - public bool AllowNotRegisteredUsersToLeaveComments { get; set; } + public bool AllowNotRegisteredUsersToLeaveComments { get; set; } = true; /// /// Gets or sets a value indicating whether to notify about new blog comments @@ -37,16 +28,31 @@ public BlogSettings() /// /// Gets or sets a number of blog tags that appear in the tag cloud /// - public int NumberOfTags { get; set; } + public int NumberOfTags { get; set; } = 15; - /// - /// The maximum age of blog items (in days) for RSS feed - /// - public int MaxAgeInDays { get; set; } + /// + /// The maximum age of blog items (in days) for RSS feed + /// + public int MaxAgeInDays { get; set; } = 180; /// /// Enable the blog RSS feed link in customers browser address bar /// public bool ShowHeaderRssUrl { get; set; } + + /// + /// Gets or sets the meta title for blog index page + /// + public string MetaTitle { get; set; } + + /// + /// Gets or sets the meta description for blog index page + /// + public string MetaDescription { get; set; } + + /// + /// Gets or sets the meta keywords for blog index page + /// + public string MetaKeywords { get; set; } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/AttributeChoiceBehaviour.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/AttributeChoiceBehaviour.cs new file mode 100644 index 0000000000..71a3edd7a5 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/AttributeChoiceBehaviour.cs @@ -0,0 +1,18 @@ +namespace SmartStore.Core.Domain.Catalog +{ + /// + /// Represents the behaviour when selecting product attributes. + /// + public enum AttributeChoiceBehaviour + { + /// + /// Gray out unavailable attributes. + /// + GrayOutUnavailable = 0, + + /// + /// No particular behaviour. + /// + None = 30 + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/BackInStockSubscription.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/BackInStockSubscription.cs index f3bddf1988..6b79d6e2b3 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/BackInStockSubscription.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/BackInStockSubscription.cs @@ -8,10 +8,10 @@ namespace SmartStore.Core.Domain.Catalog /// public partial class BackInStockSubscription : BaseEntity { - /// - /// Gets or sets the store identifier - /// - public int StoreId { get; set; } + /// + /// Gets or sets the store identifier + /// + public int StoreId { get; set; } /// /// Gets or sets the product identifier diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/CatalogSettings.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/CatalogSettings.cs index 394e9e4e9e..88de546eeb 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/CatalogSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/CatalogSettings.cs @@ -1,47 +1,48 @@ using System.Collections.Generic; using SmartStore.Core.Configuration; +using SmartStore.Core.Domain.Directory; namespace SmartStore.Core.Domain.Catalog { - public enum SubCategoryDisplayType - { - Hide = 0, - AboveProductList = 5, - Bottom = 10 - } - - public enum GridColumnSpan - { - Max2Cols = 2, - Max3Cols = 3, - Max4Cols = 4, - Max5Cols = 5, - Max6Cols = 6 - } + public enum SubCategoryDisplayType + { + Hide = 0, + AboveProductList = 5, + Bottom = 10 + } + + public enum GridColumnSpan + { + Max2Cols = 2, + Max3Cols = 3, + Max4Cols = 4, + Max5Cols = 5, + Max6Cols = 6 + } public class CatalogSettings : ISettings { public CatalogSettings() { FileUploadAllowedExtensions = new List(); - AllowProductSorting = true; + AllowProductSorting = true; DefaultSortOrder = ProductSortingEnum.Relevance; - AllowProductViewModeChanging = true; - DefaultViewMode = "grid"; - CategoryBreadcrumbEnabled = true; - ShowShareButton = true; - PageShareCode = "
"; - DefaultProductRatingValue = 5; - NotifyStoreOwnerAboutNewProductReviews = true; - EmailAFriendEnabled = true; - AskQuestionEnabled = true; - RecentlyViewedProductsNumber = 8; - RecentlyViewedProductsEnabled = true; - RecentlyAddedProductsNumber = 100; - RecentlyAddedProductsEnabled = true; - CompareProductsEnabled = true; - SubCategoryDisplayType = SubCategoryDisplayType.AboveProductList; - NumberOfBestsellersOnHomepage = 12; + AllowProductViewModeChanging = true; + DefaultViewMode = "grid"; + CategoryBreadcrumbEnabled = true; + ShowShareButton = true; + PageShareCode = "
"; + DefaultProductRatingValue = 5; + NotifyStoreOwnerAboutNewProductReviews = true; + EmailAFriendEnabled = true; + AskQuestionEnabled = true; + RecentlyViewedProductsNumber = 8; + RecentlyViewedProductsEnabled = true; + RecentlyAddedProductsNumber = 100; + RecentlyAddedProductsEnabled = true; + CompareProductsEnabled = true; + SubCategoryDisplayType = SubCategoryDisplayType.AboveProductList; + NumberOfBestsellersOnHomepage = 12; ShowPopularProductTagsOnHomepage = false; ShowManufacturersOnHomepage = true; ShowManufacturerPictures = true; @@ -50,38 +51,40 @@ public CatalogSettings() ManufacturerItemsToDisplayInOffcanvasMenu = 20; ShowManufacturerInProductDetail = true; ShowManufacturerPicturesInProductDetail = true; - ProductsAlsoPurchasedEnabled = true; - ProductsAlsoPurchasedNumber = 12; - NumberOfProductTags = 15; - DefaultProductListPageSize = 24; - UseSmallProductBoxOnHomePage = true; - DisplayTierPricesWithDiscounts = true; - DefaultPageSizeOptions = "12,24,36,48,72,120"; - AllowCustomersToSelectPageSize = true; - MaximumBackInStockSubscriptions = 200; - FileUploadMaximumSizeBytes = 1024 * 200; //200KB - DisplayAllImagesNumber = 6; - ShowShortDescriptionInGridStyleLists = true; - ShowManufacturerInGridStyleLists = true; - ShowManufacturerLogoInLists = false; - ShowProductOptionsInLists = true; - ShowColorSquaresInLists = true; - ShowDiscountSign = true; - ShowVariantCombinationPriceAdjustment = true; + ProductsAlsoPurchasedEnabled = true; + ProductsAlsoPurchasedNumber = 12; + NumberOfProductTags = 15; + DefaultProductListPageSize = 24; + UseSmallProductBoxOnHomePage = true; + DisplayTierPricesWithDiscounts = true; + DefaultPageSizeOptions = "12,24,36,48,72,120"; + AllowCustomersToSelectPageSize = true; + MaximumBackInStockSubscriptions = 200; + FileUploadMaximumSizeBytes = 1024 * 200; //200KB + DisplayAllImagesNumber = 6; + ShowShortDescriptionInGridStyleLists = true; + ShowManufacturerInGridStyleLists = true; + ShowManufacturerLogoInLists = false; + ShowProductOptionsInLists = true; + ShowColorSquaresInLists = true; + ShowDiscountSign = true; + ShowVariantCombinationPriceAdjustment = true; ShowLinkedAttributeValueImage = true; - EnableDynamicPriceUpdate = true; + EnableDynamicPriceUpdate = true; ShowProductReviewsInProductDetail = true; EnableHtmlTextCollapser = true; - HtmlTextCollapsedHeight = 260; - MostRecentlyUsedCategoriesMaxSize = 6; - MostRecentlyUsedManufacturersMaxSize = 4; - IncludeShortDescriptionInCompareProducts = true; - GridStyleListColumnSpan = GridColumnSpan.Max4Cols; - ShowProductsFromSubcategories = true; + HtmlTextCollapsedHeight = 260; + MostRecentlyUsedCategoriesMaxSize = 6; + MostRecentlyUsedManufacturersMaxSize = 4; + IncludeShortDescriptionInCompareProducts = true; + GridStyleListColumnSpan = GridColumnSpan.Max4Cols; + ShowProductsFromSubcategories = true; ApplyTierPricePercentageToAttributePriceAdjustments = false; AllowDifferingEmailAddressForEmailAFriend = false; - AllowAnonymousUsersToEmailAFriend = false; - AllowAnonymousUsersToReviewProduct = false; + AllowAnonymousUsersToEmailAFriend = false; + AllowAnonymousUsersToReviewProduct = false; + DeliveryTimesInLists = DeliveryTimesPresentation.DateOnly; + DeliveryTimesInProductDetail = DeliveryTimesPresentation.DateOnly; } /// @@ -110,14 +113,14 @@ public CatalogSettings() public bool ShowDimensions { get; set; } /// - /// Gets or sets a value indicating whether to display the delivery time of a product + /// Specifies the presentation of delivery times in product lists. /// - public bool ShowDeliveryTimesInProductLists { get; set; } + public DeliveryTimesPresentation DeliveryTimesInLists { get; set; } /// - /// Gets or sets a value indicating whether to display the delivery time of a product + /// Specifies the presentation of delivery times in product detail pages. /// - public bool ShowDeliveryTimesInProductDetail { get; set; } + public DeliveryTimesPresentation DeliveryTimesInProductDetail { get; set; } /// /// Gets or sets a value indicating whether to display the base price of a product @@ -139,10 +142,10 @@ public CatalogSettings() /// public bool ShowLinkedAttributeValueQuantity { get; set; } - /// - /// Gets or sets a value indicating whether to display the image of linked product at attribute values - /// - public bool ShowLinkedAttributeValueImage { get; set; } + /// + /// Gets or sets a value indicating whether to display the image of linked product at attribute values + /// + public bool ShowLinkedAttributeValueImage { get; set; } /// /// Gets or sets a value indicating how many menu items will be displayed @@ -188,26 +191,26 @@ public CatalogSettings() /// Gets or sets a value indicating whether category breadcrumb is enabled /// public bool CategoryBreadcrumbEnabled { get; set; } - - /// - /// 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; } + /// + /// 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 category/manufacturer description - /// - public bool ShowDescriptionInSubPages { get; set; } + /// + /// An option indicating whether sub pages should display the subcategories + /// + public bool ShowSubCategoriesInSubPages { get; set; } - /// - /// Gets or sets a value indicating whether a 'Share button' is enabled - /// - public bool ShowShareButton { 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 share code (e.g. AddThis button code) @@ -253,7 +256,7 @@ public CatalogSettings() /// Gets or sets a value indicating whether 'ask product question' feature is enabled /// public bool AskQuestionEnabled { get; set; } - + /// /// Gets or sets a value indicating whether customers are allowed to enter a differing email address /// @@ -308,7 +311,7 @@ public CatalogSettings() /// Gets or sets a value indicating whether to show manufacturers in offcanvas menu /// public bool ShowManufacturersInOffCanvas { get; set; } - + /// /// Gets or sets the value indicating how many manufacturers on home page /// @@ -318,7 +321,7 @@ public CatalogSettings() /// Gets or sets the value indicating how many manufacturers in offcanvas menu ///
public int ManufacturerItemsToDisplayInOffcanvasMenu { get; set; } - + /// /// Gets or sets a value indicating whether to show manufacturer pictures or names on home page /// @@ -334,25 +337,30 @@ public CatalogSettings() ///
public bool ShowManufacturerPicturesInProductDetail { get; set; } - /// - /// Gets or sets a value indicating whether to hide manufacturer default pictures - /// - public bool HideManufacturerDefaultPictures { get; set; } + /// + /// Gets or sets a value indicating whether to hide manufacturer default pictures + /// + public bool HideManufacturerDefaultPictures { get; set; } /// /// Gets or sets a value indicating whether to hide manufacturer default pictures /// public bool SortManufacturersAlphabetically { get; set; } - + /// /// Gets or sets a value indicating whether to hide category default pictures /// public bool HideCategoryDefaultPictures { get; set; } - /// - /// Gets or sets a value indicating whether to hide product default pictures - /// - public bool HideProductDefaultPictures { get; set; } + /// + /// Gets or sets a value indicating whether to hide product default pictures + /// + public bool HideProductDefaultPictures { get; set; } + + /// + /// Gets or sets a value whether to display the product condition. + /// + public bool ShowProductCondition { get; set; } /// /// Gets or sets "List of products purchased by other customers who purchased the above" option is enable @@ -369,10 +377,10 @@ public CatalogSettings() /// public bool EnableDynamicPriceUpdate { get; set; } - /// - /// Gets or sets a value indicating whether base price should be rendered for bundle items - /// - public bool BundleItemShowBasePrice { get; set; } + /// + /// Gets or sets a value indicating whether base price should be rendered for bundle items + /// + public bool BundleItemShowBasePrice { get; set; } /// /// Gets or sets a number of product tags that appear in the tag cloud @@ -388,23 +396,23 @@ public CatalogSettings() /// Gets or sets a value indicating whether customers can select page size in product listings /// public bool AllowCustomersToSelectPageSize { get; set; } - - public int DisplayAllImagesNumber { get; set; } - public bool ShowManufacturerInGridStyleLists { get; set; } + public int DisplayAllImagesNumber { get; set; } - /// - /// Whether to show brand logo instead of textual name in product lists - /// - public bool ShowManufacturerLogoInLists { get; set; } + public bool ShowManufacturerInGridStyleLists { get; set; } - public bool ShowShortDescriptionInGridStyleLists { get; set; } + /// + /// Whether to show brand logo instead of textual name in product lists + /// + public bool ShowManufacturerLogoInLists { get; set; } - public bool ShowProductOptionsInLists { get; set; } + public bool ShowShortDescriptionInGridStyleLists { get; set; } - public bool ShowColorSquaresInLists { get; set; } + public bool ShowProductOptionsInLists { get; set; } - public bool HideBuyButtonInLists { get; set; } + public bool ShowColorSquaresInLists { get; set; } + + public bool HideBuyButtonInLists { get; set; } public int? LabelAsNewForMaxDays { get; set; } @@ -456,21 +464,26 @@ public CatalogSettings() ///
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; } + /// + /// 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) /// public bool IgnoreDiscounts { get; set; } + /// + /// Gets or sets whether to also apply percentage discounts in tier prices. + /// + public bool ApplyPercentageDiscountOnTierPrice { get; set; } + /// /// Gets or sets a value indicating whether to ignore featured products (side-wide) /// @@ -490,36 +503,36 @@ public CatalogSettings() /// Gets or sets a list of allowed file extensions for customer uploaded files ///
public List FileUploadAllowedExtensions { get; set; } - - /// - /// Gets or sets a value indicating if html long text should be collapsed - /// - public bool EnableHtmlTextCollapser { get; set; } - /// - /// Gets or sets the height of collapsed text - /// - public int HtmlTextCollapsedHeight { get; set; } + /// + /// Gets or sets a value indicating if html long text should be collapsed + /// + public bool EnableHtmlTextCollapser { get; set; } - /// - /// Gets or sets an identifier for a delivery time dislayed when stock is empty - /// - public int? DeliveryTimeIdForEmptyStock { get; set; } + /// + /// Gets or sets the height of collapsed text + /// + public int HtmlTextCollapsedHeight { get; set; } - /// - /// Gets or sets how many items to display maximally in the most recently used category list - /// - public int MostRecentlyUsedCategoriesMaxSize { get; set; } + /// + /// Gets or sets an identifier for a delivery time dislayed when stock is empty + /// + public int? DeliveryTimeIdForEmptyStock { get; set; } - /// - /// Gets or sets how many items to display maximally in the most recently used manufacturer list - /// - public int MostRecentlyUsedManufacturersMaxSize { get; set; } + /// + /// Gets or sets how many items to display maximally in the most recently used category list + /// + public int MostRecentlyUsedCategoriesMaxSize { get; set; } - /// - /// Gets or sets how many columns per row should be displayed at most in grid style lists on largest screen resolution. - /// - public GridColumnSpan GridStyleListColumnSpan { get; set; } + /// + /// Gets or sets how many items to display maximally in the most recently used manufacturer list + /// + public int MostRecentlyUsedManufacturersMaxSize { get; set; } + + /// + /// Gets or sets how many columns per row should be displayed at most in grid style lists on largest screen resolution. + /// + public GridColumnSpan GridStyleListColumnSpan { get; set; } /// /// Gets or sets a value indicating whether percental values of tierprices should be applied to price adjustments of attributes diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/Category.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/Category.cs index d74e7c0c73..f0364da897 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/Category.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/Category.cs @@ -6,6 +6,7 @@ using System.Runtime.Serialization; using SmartStore.Core.Domain.Discounts; using SmartStore.Core.Domain.Media; +using SmartStore.Rules.Domain; namespace SmartStore.Core.Domain.Catalog { @@ -13,9 +14,10 @@ namespace SmartStore.Core.Domain.Catalog /// Represents a category /// [DataContract] - [DebuggerDisplay("{Id}: {Name} (Parent: {ParentCategoryId})")] - public partial class Category : BaseEntity, ICategoryNode, IAuditable, ISoftDeletable, IPagingOptions + [DebuggerDisplay("{Id}: {Name} (Parent: {ParentCategoryId})")] + public partial class Category : BaseEntity, ICategoryNode, IAuditable, ISoftDeletable, IPagingOptions, IRulesContainer { + private ICollection _ruleSets; private ICollection _appliedDiscounts; /// @@ -24,11 +26,11 @@ public partial class Category : BaseEntity, ICategoryNode, IAuditable, ISoftDele [DataMember] public string Name { get; set; } - /// - /// Gets or sets the full name (category page title) - /// - [DataMember] - public string FullName { get; set; } + /// + /// Gets or sets the full name (category page title) + /// + [DataMember] + public string FullName { get; set; } /// /// Gets or sets the description @@ -36,11 +38,11 @@ public partial class Category : BaseEntity, ICategoryNode, IAuditable, ISoftDele [DataMember] public string Description { get; set; } - /// - /// Gets or sets a description displayed at the bottom of the category page - /// - [DataMember] - public string BottomDescription { get; set; } + /// + /// Gets or sets a description displayed at the bottom of the category page + /// + [DataMember] + public string BottomDescription { get; set; } /// /// Gets or sets the external link expression. If set, any category menu item will navigate to the specified link. @@ -65,7 +67,7 @@ public partial class Category : BaseEntity, ICategoryNode, IAuditable, ISoftDele /// (an optional key for advanced customization) /// [DataMember] - public string Alias { get; set; } + public string Alias { get; set; } /// /// Gets or sets a value of used category template identifier @@ -98,47 +100,47 @@ public partial class Category : BaseEntity, ICategoryNode, IAuditable, ISoftDele public int ParentCategoryId { get; set; } /// - /// Gets or sets the picture identifier + /// Gets or sets the media file identifier /// [DataMember] - public int? PictureId { get; set; } + public int? MediaFileId { get; set; } - /// - /// Gets or sets the picture - /// - [DataMember] - public virtual Picture Picture { get; set; } + /// + /// Gets or sets the media file + /// + [DataMember] + public virtual MediaFile MediaFile { get; set; } /// /// Gets or sets the page size /// [DataMember] - public int? PageSize { get; set; } + public int? PageSize { get; set; } /// /// Gets or sets a value indicating whether customers can select the page size /// [DataMember] - public bool? AllowCustomersToSelectPageSize { get; set; } + public bool? AllowCustomersToSelectPageSize { get; set; } /// /// Gets or sets the available customer selectable page size options /// [DataMember] - public string PageSizeOptions { get; set; } + public string PageSizeOptions { get; set; } - /// - /// Gets or sets the available price ranges - /// - [Obsolete("Price ranges are calculated automatically since version 3")] - [StringLength(400)] - public string PriceRanges { get; set; } + /// + /// Gets or sets the available price ranges + /// + [Obsolete("Price ranges are calculated automatically since version 3")] + [StringLength(400)] + public string PriceRanges { get; set; } - /// - /// Gets or sets a value indicating whether to show the category on home page - /// - [DataMember] - public bool ShowOnHomePage { get; set; } + /// + /// Gets or sets a value indicating whether to show the category on home page + /// + [DataMember] + public bool ShowOnHomePage { get; set; } /// /// Gets or sets a value indicating whether this category has discounts applied @@ -148,19 +150,19 @@ public partial class Category : BaseEntity, ICategoryNode, IAuditable, ISoftDele /// /// [DataMember] - public bool HasDiscountsApplied { get; set; } + public bool HasDiscountsApplied { get; set; } /// /// Gets or sets a value indicating whether the entity is subject to ACL /// [DataMember] - public bool SubjectToAcl { get; set; } + public bool SubjectToAcl { get; set; } - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - [DataMember] - public bool LimitedToStores { get; set; } + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + [DataMember] + public bool LimitedToStores { get; set; } /// /// Gets or sets a value indicating whether the entity is published @@ -198,14 +200,23 @@ public partial class Category : BaseEntity, ICategoryNode, IAuditable, ISoftDele [DataMember] public string DefaultViewMode { get; set; } + /// + /// Gets or sets assigned rule sets. + /// + public virtual ICollection RuleSets + { + get => _ruleSets ?? (_ruleSets = new HashSet()); + protected set => _ruleSets = value; + } + /// /// Gets or sets the collection of applied discounts /// [DataMember] - public virtual ICollection AppliedDiscounts + public virtual ICollection AppliedDiscounts { - get { return _appliedDiscounts ?? (_appliedDiscounts = new HashSet()); } - protected set { _appliedDiscounts = value; } + get => _appliedDiscounts ?? (_appliedDiscounts = new HashSet()); + protected set => _appliedDiscounts = value; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/CategoryNode.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/CategoryNode.cs index c07c9fe69e..19965ea78f 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/CategoryNode.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/CategoryNode.cs @@ -6,35 +6,35 @@ namespace SmartStore.Core.Domain.Catalog { - public interface ICategoryNode : ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported - { - int ParentCategoryId { get; } - string Name { get; } + public interface ICategoryNode : ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported + { + int ParentCategoryId { get; } + string Name { get; } string ExternalLink { get; } string Alias { get; } - int? PictureId { get; } - bool Published { get; } - int DisplayOrder { get; } - DateTime UpdatedOnUtc { get; } - string BadgeText { get; } - int BadgeStyle { get; } - } + int? MediaFileId { get; } + bool Published { get; } + int DisplayOrder { get; } + DateTime UpdatedOnUtc { get; } + string BadgeText { get; } + int BadgeStyle { get; } + } - [Serializable] - public class CategoryNode : ICategoryNode - { - public int Id { get; set; } - public int ParentCategoryId { get; set; } - public string Name { get; set; } + [Serializable] + public class CategoryNode : ICategoryNode + { + public int Id { get; set; } + public int ParentCategoryId { get; set; } + public string Name { get; set; } public string ExternalLink { get; set; } public string Alias { get; set; } - public int? PictureId { get; set; } - public bool Published { get; set; } - public int DisplayOrder { get; set; } - public DateTime UpdatedOnUtc { get; set; } - public string BadgeText { get; set; } - public int BadgeStyle { get; set; } - public bool SubjectToAcl { get; set; } - public bool LimitedToStores { get; set; } - } + public int? MediaFileId { get; set; } + public bool Published { get; set; } + public int DisplayOrder { get; set; } + public DateTime UpdatedOnUtc { get; set; } + public string BadgeText { get; set; } + public int BadgeStyle { get; set; } + public bool SubjectToAcl { get; set; } + public bool LimitedToStores { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/IPagingOptions.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/IPagingOptions.cs index 3a4ce6b720..fb231147c8 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/IPagingOptions.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/IPagingOptions.cs @@ -1,11 +1,9 @@ -using System; - -namespace SmartStore.Core.Domain.Catalog +namespace SmartStore.Core.Domain.Catalog { - public interface IPagingOptions - { - int? PageSize { get; } - bool? AllowCustomersToSelectPageSize { get; } - string PageSizeOptions { get; } - } + public interface IPagingOptions + { + int? PageSize { get; } + bool? AllowCustomersToSelectPageSize { get; } + string PageSizeOptions { get; } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/Manufacturer.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/Manufacturer.cs index 823f5e57fb..68ec18111c 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/Manufacturer.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/Manufacturer.cs @@ -6,103 +6,117 @@ using SmartStore.Core.Domain.Discounts; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.Security; using SmartStore.Core.Domain.Seo; using SmartStore.Core.Domain.Stores; namespace SmartStore.Core.Domain.Catalog { - /// - /// Represents a manufacturer - /// - [DataContract] - public partial class Manufacturer : BaseEntity, IAuditable, ISoftDeletable, ILocalizedEntity, ISlugSupported, IStoreMappingSupported, IPagingOptions - { - private ICollection _appliedDiscounts; - - /// - /// Gets or sets the name - /// - [DataMember] - public string Name { get; set; } + /// + /// Represents a manufacturer + /// + [DataContract] + public partial class Manufacturer : BaseEntity, IAuditable, ISoftDeletable, ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported, IPagingOptions + { + private ICollection _appliedDiscounts; + + /// + /// Gets or sets the name + /// + [DataMember] + public string Name { get; set; } /// /// Gets or sets the description /// [DataMember] - public string Description { get; set; } + public string Description { get; set; } + + /// + /// Gets or sets a description displayed at the bottom of the manufacturer page. + /// + [DataMember] + public string BottomDescription { get; set; } /// /// Gets or sets a value of used manufacturer template identifier /// [DataMember] - public int ManufacturerTemplateId { get; set; } + public int ManufacturerTemplateId { get; set; } /// /// Gets or sets the meta keywords /// [DataMember] - public string MetaKeywords { get; set; } + public string MetaKeywords { get; set; } /// /// Gets or sets the meta description /// [DataMember] - public string MetaDescription { get; set; } + public string MetaDescription { get; set; } /// /// Gets or sets the meta title /// [DataMember] - public string MetaTitle { get; set; } + public string MetaTitle { get; set; } /// - /// Gets or sets the parent picture identifier + /// Gets or sets the parent media file identifier /// [DataMember] - public int? PictureId { get; set; } + public int? MediaFileId { get; set; } - /// - /// Gets or sets the picture - /// - [DataMember] - public virtual Picture Picture { get; set; } + /// + /// Gets or sets the media file + /// + [DataMember] + public virtual MediaFile MediaFile { get; set; } /// /// Gets or sets the page size /// [DataMember] - public int? PageSize { get; set; } + public int? PageSize { get; set; } /// /// Gets or sets a value indicating whether customers can select the page size /// [DataMember] - public bool? AllowCustomersToSelectPageSize { get; set; } + public bool? AllowCustomersToSelectPageSize { get; set; } /// /// Gets or sets the available customer selectable page size options /// [DataMember] - public string PageSizeOptions { get; set; } + public string PageSizeOptions { get; set; } /// /// Gets or sets the available price ranges /// [Obsolete("Price ranges are calculated automatically since version 3")] - [StringLength(400)] - public string PriceRanges { get; set; } + [StringLength(400)] + public string PriceRanges { get; set; } - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - [DataMember] - public bool LimitedToStores { get; set; } + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + [DataMember] + public bool LimitedToStores { get; set; } + + /// + /// Gets or sets a value indicating whether the entity is subject to ACL. + /// + [DataMember] + [Index] + public bool SubjectToAcl { get; set; } /// /// Gets or sets a value indicating whether the entity is published /// [DataMember] - public bool Published { get; set; } + public bool Published { get; set; } /// /// Gets or sets a value indicating whether the entity has been deleted @@ -114,38 +128,38 @@ public partial class Manufacturer : BaseEntity, IAuditable, ISoftDeletable, ILoc /// Gets or sets the display order /// [DataMember] - public int DisplayOrder { get; set; } + public int DisplayOrder { get; set; } /// /// Gets or sets the date and time of instance creation /// [DataMember] - public DateTime CreatedOnUtc { get; set; } + public DateTime CreatedOnUtc { get; set; } /// /// Gets or sets the date and time of instance update /// [DataMember] - public DateTime UpdatedOnUtc { get; set; } - - /// - /// Gets or sets a value indicating whether this manufacturer has discounts applied - /// The same as if we run Manufacturer.AppliedDiscounts.Count > 0 - /// We use this property for performance optimization: - /// if this property is set to false, then we do not need to load Applied Discounts navigation property - /// - /// - [DataMember] - public bool HasDiscountsApplied { get; set; } + public DateTime UpdatedOnUtc { get; set; } - /// - /// Gets or sets the collection of applied discounts - /// - [DataMember] - public virtual ICollection AppliedDiscounts - { - get { return _appliedDiscounts ?? (_appliedDiscounts = new HashSet()); } - protected set { _appliedDiscounts = value; } - } - } + /// + /// Gets or sets a value indicating whether this manufacturer has discounts applied + /// The same as if we run Manufacturer.AppliedDiscounts.Count > 0 + /// We use this property for performance optimization: + /// if this property is set to false, then we do not need to load Applied Discounts navigation property + /// + /// + [DataMember] + public bool HasDiscountsApplied { get; set; } + + /// + /// Gets or sets the collection of applied discounts + /// + [DataMember] + public virtual ICollection AppliedDiscounts + { + get => _appliedDiscounts ?? (_appliedDiscounts = new HashSet()); + protected set => _appliedDiscounts = value; + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/PriceDisplayStyle.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/PriceDisplayStyle.cs index adee8aa61f..1275690ff9 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/PriceDisplayStyle.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/PriceDisplayStyle.cs @@ -7,7 +7,7 @@ namespace SmartStore.Core.Domain.Catalog /// [Flags] public enum PriceDisplayStyle - { + { /// /// Display prices without badges /// diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/PriceDisplayType.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/PriceDisplayType.cs index aeb4c24250..778aba0b13 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/PriceDisplayType.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/PriceDisplayType.cs @@ -1,29 +1,29 @@  namespace SmartStore.Core.Domain.Catalog { - /// - /// Represents types of product prices to display - /// - public enum PriceDisplayType - { - /// - /// The lowest possible price of a product (default) - /// - LowestPrice = 0, + /// + /// Represents types of product prices to display + /// + public enum PriceDisplayType + { + /// + /// The lowest possible price of a product (default) + /// + LowestPrice = 0, - /// - /// The product price initially displayed on the product detail page - /// - PreSelectedPrice = 10, + /// + /// The product price initially displayed on the product detail page + /// + PreSelectedPrice = 10, - /// - /// The product price without associated data like discounts, tier prices, attributes or attribute combinations - /// - PriceWithoutDiscountsAndAttributes = 20, + /// + /// The product price without associated data like discounts, tier prices, attributes or attribute combinations + /// + PriceWithoutDiscountsAndAttributes = 20, - /// - /// Do not display a product price - /// - Hide = 30 - } + /// + /// Do not display a product price + /// + Hide = 30 + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/Product.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/Product.cs index 6f18a1722d..06abbb9bbf 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/Product.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/Product.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Diagnostics; -using System.Linq.Expressions; using System.Runtime.Serialization; using SmartStore.Core.Domain.Directory; using SmartStore.Core.Domain.Discounts; @@ -18,79 +17,87 @@ namespace SmartStore.Core.Domain.Catalog /// Represents a product /// [DataContract] - public partial class Product : BaseEntity, IAuditable, ISoftDeletable, ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported, IMergedData - { - #region static - - private static readonly HashSet _visibilityAffectingProductProps = new HashSet - { - nameof(Product.AvailableEndDateTimeUtc), - nameof(Product.AvailableStartDateTimeUtc), - nameof(Product.Deleted), - nameof(Product.LowStockActivityId), - nameof(Product.LimitedToStores), - nameof(Product.ManageInventoryMethodId), - nameof(Product.MinStockQuantity), - nameof(Product.Published), - nameof(Product.SubjectToAcl), - nameof(Product.VisibleIndividually) - }; - - public static IReadOnlyCollection GetVisibilityAffectingPropertyNames() - { - return _visibilityAffectingProductProps; - } - - #endregion - - private ICollection _productCategories; + public partial class Product : BaseEntity, IAuditable, ISoftDeletable, ILocalizedEntity, ISlugSupported, IAclSupported, IStoreMappingSupported, IMergedData + { + #region static + + private static readonly HashSet _visibilityAffectingProductProps = new HashSet + { + nameof(Product.AvailableEndDateTimeUtc), + nameof(Product.AvailableStartDateTimeUtc), + nameof(Product.Deleted), + nameof(Product.LowStockActivityId), + nameof(Product.LimitedToStores), + nameof(Product.ManageInventoryMethodId), + nameof(Product.MinStockQuantity), + nameof(Product.Published), + nameof(Product.SubjectToAcl), + nameof(Product.Visibility) + }; + + public static IReadOnlyCollection GetVisibilityAffectingPropertyNames() + { + return _visibilityAffectingProductProps; + } + + #endregion + + private ICollection _productCategories; private ICollection _productManufacturers; - private ICollection _productPictures; + private ICollection _productPictures; private ICollection _productReviews; private ICollection _productSpecificationAttributes; private ICollection _productTags; - private ICollection _productVariantAttributes; - private ICollection _productVariantAttributeCombinations; - private ICollection _tierPrices; - private ICollection _appliedDiscounts; - private ICollection _productBundleItems; - + private ICollection _productVariantAttributes; + private ICollection _productVariantAttributeCombinations; + private ICollection _tierPrices; + private ICollection _appliedDiscounts; + private ICollection _productBundleItems; + private int _stockQuantity; private int _backorderModeId; - private string _sku; - private string _gtin; - private string _manufacturerPartNumber; - private decimal _price; - private int? _deliveryTimeId; + private string _sku; + private string _gtin; + private string _manufacturerPartNumber; + private decimal _price; + private int? _deliveryTimeId; private int? _quantityUnitId; - private decimal _length; - private decimal _width; - private decimal _height; - private decimal? _basePriceAmount; - private int? _basePriceBaseAmount; + private decimal _length; + private decimal _width; + private decimal _height; + private decimal? _basePriceAmount; + private int? _basePriceBaseAmount; - public bool MergedDataIgnore { get; set; } - public Dictionary MergedDataValues { get; set; } + public bool MergedDataIgnore { get; set; } + public Dictionary MergedDataValues { get; set; } - /// - /// Gets or sets the product type identifier - /// - [DataMember] - public int ProductTypeId { get; set; } + /// + /// Gets or sets the product type identifier + /// + [DataMember] + public int ProductTypeId { get; set; } - /// - /// Gets or sets the parent product identifier. It's used to identify associated products (only with "grouped" products) - /// - [DataMember] - public int ParentGroupedProductId { get; set; } + /// + /// Gets or sets the parent product identifier. It's used to identify associated products (only with "grouped" products) + /// + [DataMember] + public int ParentGroupedProductId { get; set; } - /// - /// Gets or sets the values indicating whether this product is visible in catalog or search results. - /// It's used when this product is associated to some "grouped" one - /// This way associated products could be accessed/added/etc only from a grouped product details page - /// - [DataMember] - public bool VisibleIndividually { get; set; } + [Obsolete("Use property Visibility instead.")] + public bool VisibleIndividually { get; set; } + + /// + /// Gets or sets the visibility level of the product. + /// + [DataMember] + [Index] + public ProductVisibility Visibility { get; set; } + + /// + /// Gets or sets the condition of the product. + /// + [DataMember] + public ProductCondition Condition { get; set; } /// /// Gets or sets the name @@ -114,658 +121,605 @@ public static IReadOnlyCollection GetVisibilityAffectingPropertyNames() /// Gets or sets the admin comment /// [DataMember] - public string AdminComment { get; set; } + public string AdminComment { get; set; } /// /// Gets or sets a value of used product template identifier /// [DataMember] - public int ProductTemplateId { get; set; } + public int ProductTemplateId { get; set; } /// /// Gets or sets a value indicating whether to show the product on home page /// [DataMember] - public bool ShowOnHomePage { get; set; } + public bool ShowOnHomePage { get; set; } - /// - /// Gets or sets the display order for homepage products - /// - [DataMember] - public int HomePageDisplayOrder { get; set; } + /// + /// Gets or sets the display order for homepage products + /// + [DataMember] + public int HomePageDisplayOrder { get; set; } /// /// Gets or sets the meta keywords /// [DataMember] - public string MetaKeywords { get; set; } + public string MetaKeywords { get; set; } /// /// Gets or sets the meta description /// [DataMember] - public string MetaDescription { get; set; } + public string MetaDescription { get; set; } /// /// Gets or sets the meta title /// [DataMember] - public string MetaTitle { get; set; } + public string MetaTitle { get; set; } /// /// Gets or sets a value indicating whether the product allows customer reviews /// [DataMember] - public bool AllowCustomerReviews { get; set; } + public bool AllowCustomerReviews { get; set; } /// /// Gets or sets the rating sum (approved reviews) /// [DataMember] - public int ApprovedRatingSum { get; set; } + public int ApprovedRatingSum { get; set; } /// /// Gets or sets the rating sum (not approved reviews) /// [DataMember] - public int NotApprovedRatingSum { get; set; } + public int NotApprovedRatingSum { get; set; } /// /// Gets or sets the total rating votes (approved reviews) /// [DataMember] - public int ApprovedTotalReviews { get; set; } + public int ApprovedTotalReviews { get; set; } /// /// Gets or sets the total rating votes (not approved reviews) /// [DataMember] - public int NotApprovedTotalReviews { get; set; } + public int NotApprovedTotalReviews { get; set; } /// /// Gets or sets a value indicating whether the entity is subject to ACL /// [DataMember] - public bool SubjectToAcl { get; set; } + public bool SubjectToAcl { get; set; } - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - [DataMember] - public bool LimitedToStores { get; set; } - - /// - /// Gets or sets the SKU - /// - [DataMember] - public string Sku - { - [DebuggerStepThrough] - get - { - return this.GetMergedDataValue(nameof(Sku), _sku); - } - set - { - _sku = value; - } - } - - /// - /// Gets or sets the manufacturer part number - /// - [DataMember] - [Index] - public string ManufacturerPartNumber - { - [DebuggerStepThrough] - get - { - return this.GetMergedDataValue(nameof(ManufacturerPartNumber), _manufacturerPartNumber); - } - set - { - _manufacturerPartNumber = value; - } - } - - /// - /// Gets or sets the Global Trade Item Number (GTIN). These identifiers include UPC (in North America), EAN (in Europe), JAN (in Japan), and ISBN (for books). - /// - [DataMember] - [Index] - public string Gtin - { - [DebuggerStepThrough] - get - { - return this.GetMergedDataValue(nameof(Gtin), _gtin); - } - set - { - _gtin = value; - } - } - - /// - /// Gets or sets a value indicating whether the product is gift card - /// - [DataMember] - public bool IsGiftCard { get; set; } - - /// - /// Gets or sets the gift card type identifier - /// - [DataMember] - public int GiftCardTypeId { get; set; } + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + [DataMember] + public bool LimitedToStores { get; set; } - /// - /// Gets or sets a value indicating whether the product requires that other products are added to the cart (Product X requires Product Y) - /// - [DataMember] - public bool RequireOtherProducts { get; set; } + /// + /// Gets or sets the SKU + /// + [DataMember] + public string Sku + { + [DebuggerStepThrough] + get => this.GetMergedDataValue(nameof(Sku), _sku); + set => _sku = value; + } - /// - /// Gets or sets a required product identifiers (comma separated) - /// - [DataMember] - public string RequiredProductIds { get; set; } + /// + /// Gets or sets the manufacturer part number + /// + [DataMember] + [Index] + public string ManufacturerPartNumber + { + [DebuggerStepThrough] + get => this.GetMergedDataValue(nameof(ManufacturerPartNumber), _manufacturerPartNumber); + set => _manufacturerPartNumber = value; + } - /// - /// Gets or sets a value indicating whether required products are automatically added to the cart - /// - [DataMember] - public bool AutomaticallyAddRequiredProducts { get; set; } + /// + /// Gets or sets the Global Trade Item Number (GTIN). These identifiers include UPC (in North America), EAN (in Europe), JAN (in Japan), and ISBN (for books). + /// + [DataMember] + [Index] + public string Gtin + { + [DebuggerStepThrough] + get => this.GetMergedDataValue(nameof(Gtin), _gtin); + set => _gtin = value; + } - /// - /// Gets or sets a value indicating whether the product is a download - /// - [DataMember] - public bool IsDownload { get; set; } + /// + /// Gets or sets a value indicating whether the product is gift card + /// + [DataMember] + public bool IsGiftCard { get; set; } - /// - /// Gets or sets the download identifier - /// - [Obsolete("Since version 3.2 more than one download can be assigned to a product. See property Download.EntityId and Download.EntityName.")] - public int DownloadId { get; set; } + /// + /// Gets or sets the gift card type identifier + /// + [DataMember] + public int GiftCardTypeId { get; set; } - /// - /// Gets or sets a value indicating whether this downloadable product can be downloaded unlimited number of times - /// - [DataMember] - public bool UnlimitedDownloads { get; set; } + /// + /// Gets or sets a value indicating whether the product requires that other products are added to the cart (Product X requires Product Y) + /// + [DataMember] + public bool RequireOtherProducts { get; set; } - /// - /// Gets or sets the maximum number of downloads - /// - [DataMember] - public int MaxNumberOfDownloads { get; set; } + /// + /// Gets or sets a required product identifiers (comma separated) + /// + [DataMember] + public string RequiredProductIds { get; set; } - /// - /// Gets or sets the number of days during customers keeps access to the file. - /// - [DataMember] - public int? DownloadExpirationDays { get; set; } + /// + /// Gets or sets a value indicating whether required products are automatically added to the cart + /// + [DataMember] + public bool AutomaticallyAddRequiredProducts { get; set; } - /// - /// Gets or sets the download activation type - /// - [DataMember] - public int DownloadActivationTypeId { get; set; } + /// + /// Gets or sets a value indicating whether the product is a download + /// + [DataMember] + public bool IsDownload { get; set; } - /// - /// Gets or sets a value indicating whether the product has a sample download file - /// - [DataMember] - public bool HasSampleDownload { get; set; } + /// + /// Gets or sets the download identifier + /// + [Obsolete("Since version 3.2 more than one download can be assigned to a product. See property Download.EntityId and Download.EntityName.")] + public int DownloadId { get; set; } - /// - /// Gets or sets the sample download identifier - /// - [DataMember] - public int? SampleDownloadId { get; set; } + /// + /// Gets or sets a value indicating whether this downloadable product can be downloaded unlimited number of times + /// + [DataMember] + public bool UnlimitedDownloads { get; set; } - /// - /// Gets or sets the sample download - /// - [DataMember] - public Download SampleDownload { get; set; } + /// + /// Gets or sets the maximum number of downloads + /// + [DataMember] + public int MaxNumberOfDownloads { get; set; } - /// - /// Gets or sets a value indicating whether the product has user agreement - /// - [DataMember] - public bool HasUserAgreement { get; set; } + /// + /// Gets or sets the number of days during customers keeps access to the file. + /// + [DataMember] + public int? DownloadExpirationDays { get; set; } - /// - /// Gets or sets the text of license agreement - /// - [DataMember] - public string UserAgreementText { get; set; } + /// + /// Gets or sets the download activation type + /// + [DataMember] + public int DownloadActivationTypeId { get; set; } - /// - /// Gets or sets a value indicating whether the product is recurring - /// - [DataMember] - public bool IsRecurring { get; set; } + /// + /// Gets or sets a value indicating whether the product has a sample download file + /// + [DataMember] + public bool HasSampleDownload { get; set; } - /// - /// Gets or sets the cycle length - /// - [DataMember] - public int RecurringCycleLength { get; set; } + /// + /// Gets or sets the sample download identifier + /// + [DataMember] + public int? SampleDownloadId { get; set; } - /// - /// Gets or sets the cycle period - /// - [DataMember] - public int RecurringCyclePeriodId { get; set; } + /// + /// Gets or sets the sample download + /// + [DataMember] + public Download SampleDownload { get; set; } - /// - /// Gets or sets the total cycles - /// - [DataMember] - public int RecurringTotalCycles { get; set; } + /// + /// Gets or sets a value indicating whether the product has user agreement + /// + [DataMember] + public bool HasUserAgreement { get; set; } - /// - /// Gets or sets a value indicating whether the entity is ship enabled - /// - [DataMember] - public bool IsShipEnabled { get; set; } + /// + /// Gets or sets the text of license agreement + /// + [DataMember] + public string UserAgreementText { get; set; } - /// - /// Gets or sets a value indicating whether the entity is free shipping - /// - [DataMember] - public bool IsFreeShipping { get; set; } + /// + /// Gets or sets a value indicating whether the product is recurring + /// + [DataMember] + public bool IsRecurring { get; set; } - /// - /// Gets or sets the additional shipping charge - /// - [DataMember] - public decimal AdditionalShippingCharge { get; set; } + /// + /// Gets or sets the cycle length + /// + [DataMember] + public int RecurringCycleLength { get; set; } - /// - /// Gets or sets a value indicating whether the product is marked as tax exempt - /// - [DataMember] - public bool IsTaxExempt { get; set; } + /// + /// Gets or sets the cycle period + /// + [DataMember] + public int RecurringCyclePeriodId { get; set; } - /// - /// Gets or sets a value indicating whether the product is an electronic service - /// bound to EU VAT regulations for digital goods. - /// - [DataMember] - public bool IsEsd { get; set; } + /// + /// Gets or sets the total cycles + /// + [DataMember] + public int RecurringTotalCycles { get; set; } - /// - /// Gets or sets the tax category identifier - /// - [DataMember] - public int TaxCategoryId { get; set; } + /// + /// Gets or sets a value indicating whether the entity is ship enabled + /// + [DataMember] + public bool IsShipEnabled { get; set; } - /// - /// Gets or sets a value indicating how to manage inventory - /// - [DataMember] - public int ManageInventoryMethodId { get; set; } + /// + /// Gets or sets a value indicating whether the entity is free shipping + /// + [DataMember] + public bool IsFreeShipping { get; set; } - /// - /// Gets or sets the stock quantity - /// - [DataMember] - public int StockQuantity - { - [DebuggerStepThrough] - get - { - return this.GetMergedDataValue(nameof(StockQuantity), _stockQuantity); - } - set - { - _stockQuantity = value; - } - } - - /// - /// Gets or sets a value indicating whether to display stock availability - /// - [DataMember] - public bool DisplayStockAvailability { get; set; } + /// + /// Gets or sets the additional shipping charge + /// + [DataMember] + public decimal AdditionalShippingCharge { get; set; } - /// - /// Gets or sets a value indicating whether to display stock quantity - /// - [DataMember] - public bool DisplayStockQuantity { get; set; } + /// + /// Gets or sets a value indicating whether the product is marked as tax exempt + /// + [DataMember] + public bool IsTaxExempt { get; set; } - /// - /// Gets or sets the minimum stock quantity - /// - [DataMember] - public int MinStockQuantity { get; set; } + /// + /// Gets or sets a value indicating whether the product is an electronic service + /// bound to EU VAT regulations for digital goods. + /// + [DataMember] + public bool IsEsd { get; set; } - /// - /// Gets or sets the low stock activity identifier - /// - [DataMember] - public int LowStockActivityId { get; set; } + /// + /// Gets or sets the tax category identifier + /// + [DataMember] + public int TaxCategoryId { get; set; } - /// - /// Gets or sets the quantity when admin should be notified - /// - [DataMember] - public int NotifyAdminForQuantityBelow { get; set; } + /// + /// Gets or sets a value indicating how to manage inventory + /// + [DataMember] + public int ManageInventoryMethodId { get; set; } - /// - /// Gets or sets a value backorder mode identifier - /// - [DataMember] - public int BackorderModeId + /// + /// Gets or sets the stock quantity + /// + [DataMember] + public int StockQuantity { - [DebuggerStepThrough] - get - { - return this.GetMergedDataValue(nameof(BackorderModeId), _backorderModeId); - } - set - { - _backorderModeId = value; - } + [DebuggerStepThrough] + get => this.GetMergedDataValue(nameof(StockQuantity), _stockQuantity); + set => _stockQuantity = value; } - /// - /// Gets or sets a value indicating whether to back in stock subscriptions are allowed - /// - [DataMember] - public bool AllowBackInStockSubscriptions { get; set; } - - /// - /// Gets or sets the order minimum quantity - /// - [DataMember] - public int OrderMinimumQuantity { get; set; } + /// + /// Gets or sets a value indicating whether to display stock availability + /// + [DataMember] + public bool DisplayStockAvailability { get; set; } - /// - /// Gets or sets the order maximum quantity - /// - [DataMember] - public int OrderMaximumQuantity { get; set; } + /// + /// Gets or sets a value indicating whether to display stock quantity + /// + [DataMember] + public bool DisplayStockQuantity { get; set; } /// - /// Gets or sets the quantity step + /// Gets or sets the minimum stock quantity /// [DataMember] - public int QuantityStep { get; set; } + public int MinStockQuantity { get; set; } /// - /// Gets or sets the quantity control type + /// Gets or sets the low stock activity identifier /// [DataMember] - public QuantityControlType QuantiyControlType { get; set; } + public int LowStockActivityId { get; set; } /// - /// Gets or sets a value to specify whether or not to hide the quantity input control + /// Gets or sets the quantity when admin should be notified /// [DataMember] - public bool HideQuantityControl { get; set; } + public int NotifyAdminForQuantityBelow { get; set; } /// - /// Gets or sets the comma seperated list of allowed quantities. null or empty if any quantity is allowed + /// Gets or sets a value backorder mode identifier /// [DataMember] - public string AllowedQuantities { get; set; } + public int BackorderModeId + { + [DebuggerStepThrough] + get => this.GetMergedDataValue(nameof(BackorderModeId), _backorderModeId); + set => _backorderModeId = value; + } - /// - /// Gets or sets a value indicating whether to disable buy (Add to cart) button - /// - [DataMember] - public bool DisableBuyButton { get; set; } + /// + /// Gets or sets a value indicating whether to back in stock subscriptions are allowed + /// + [DataMember] + public bool AllowBackInStockSubscriptions { get; set; } - /// - /// Gets or sets a value indicating whether to disable "Add to wishlist" button - /// - [DataMember] - public bool DisableWishlistButton { get; set; } + /// + /// Gets or sets the order minimum quantity + /// + [DataMember] + public int OrderMinimumQuantity { get; set; } - /// - /// Gets or sets a value indicating whether this item is available for Pre-Order - /// - [DataMember] - public bool AvailableForPreOrder { get; set; } + /// + /// Gets or sets the order maximum quantity + /// + [DataMember] + public int OrderMaximumQuantity { get; set; } - /// - /// Gets or sets a value indicating whether to show "Call for Pricing" or "Call for quote" instead of price - /// - [DataMember] - public bool CallForPrice { get; set; } + /// + /// Gets or sets the quantity step + /// + [DataMember] + public int QuantityStep { get; set; } - /// - /// Gets or sets the price - /// - [DataMember] - public decimal Price - { - [DebuggerStepThrough] - get - { - return this.GetMergedDataValue(nameof(Price), _price); - } - set - { - _price = value; - } - } - - /// - /// Gets or sets the old price - /// - [DataMember] - public decimal OldPrice { get; set; } + /// + /// Gets or sets the quantity control type + /// + [DataMember] + public QuantityControlType QuantiyControlType { get; set; } - /// - /// Gets or sets the product cost - /// - [DataMember] - public decimal ProductCost { get; set; } + /// + /// Gets or sets a value to specify whether or not to hide the quantity input control + /// + [DataMember] + public bool HideQuantityControl { get; set; } - /// - /// Gets or sets the product special price - /// - [DataMember] - public decimal? SpecialPrice { get; set; } + /// + /// Gets or sets the comma seperated list of allowed quantities. null or empty if any quantity is allowed + /// + [DataMember] + public string AllowedQuantities { get; set; } - /// - /// Gets or sets the start date and time of the special price - /// - [DataMember] - public DateTime? SpecialPriceStartDateTimeUtc { get; set; } + /// + /// Gets or sets a value indicating whether to disable buy (Add to cart) button + /// + [DataMember] + public bool DisableBuyButton { get; set; } - /// - /// Gets or sets the end date and time of the special price - /// - [DataMember] - public DateTime? SpecialPriceEndDateTimeUtc { get; set; } + /// + /// Gets or sets a value indicating whether to disable "Add to wishlist" button + /// + [DataMember] + public bool DisableWishlistButton { get; set; } - /// - /// Gets or sets a value indicating whether a customer enters price - /// - [DataMember] - public bool CustomerEntersPrice { get; set; } + /// + /// Gets or sets a value indicating whether this item is available for Pre-Order + /// + [DataMember] + public bool AvailableForPreOrder { get; set; } - /// - /// Gets or sets the minimum price entered by a customer - /// - [DataMember] - public decimal MinimumCustomerEnteredPrice { get; set; } + /// + /// Gets or sets a value indicating whether to show "Call for Pricing" or "Call for quote" instead of price + /// + [DataMember] + public bool CallForPrice { get; set; } - /// - /// Gets or sets the maximum price entered by a customer - /// - [DataMember] - public decimal MaximumCustomerEnteredPrice { get; set; } - - /// - /// Gets or sets a value indicating whether this product has tier prices configured - /// The same as if we run this.TierPrices.Count > 0 - /// We use this property for performance optimization: - /// if this property is set to false, then we do not need to load tier prices navigation property - /// - /// - [DataMember] - public bool HasTierPrices { get; set; } + /// + /// Gets or sets the price + /// + [DataMember] + public decimal Price + { + [DebuggerStepThrough] + get => this.GetMergedDataValue(nameof(Price), _price); + set => _price = value; + } - /// - /// Gets or sets a value for the lowest attribute combination price override - /// - [DataMember] - public decimal? LowestAttributeCombinationPrice { get; set; } - - /// - /// Gets or sets a value indicating whether this product has discounts applied - /// The same as if we run this.AppliedDiscounts.Count > 0 - /// We use this property for performance optimization: - /// if this property is set to false, then we do not need to load Applied Discounts navifation property - /// - /// - [DataMember] - public bool HasDiscountsApplied { get; set; } + /// + /// Gets or sets the old price + /// + [DataMember] + public decimal OldPrice { get; set; } - /// - /// Gets or sets the weight - /// - [DataMember] - public decimal Weight { get; set; } + /// + /// Gets or sets the product cost + /// + [DataMember] + public decimal ProductCost { get; set; } - /// - /// Gets or sets the length - /// - [DataMember] - public decimal Length - { - [DebuggerStepThrough] - get - { - return this.GetMergedDataValue(nameof(Length), _length); - } - set - { - _length = value; - } - } - - /// - /// Gets or sets the width - /// - [DataMember] - public decimal Width - { - [DebuggerStepThrough] - get - { - return this.GetMergedDataValue(nameof(Width), _width); - } - set - { - _width = value; - } - } - - /// - /// Gets or sets the height - /// - [DataMember] - public decimal Height - { - [DebuggerStepThrough] - get - { - return this.GetMergedDataValue(nameof(Height), _height); - } - set - { - _height = value; - } - } - - /// - /// Gets or sets the available start date and time - /// - [DataMember] - public DateTime? AvailableStartDateTimeUtc { get; set; } + /// + /// Gets or sets the product special price + /// + [DataMember] + public decimal? SpecialPrice { get; set; } - /// - /// Gets or sets the available end date and time - /// - [DataMember] - public DateTime? AvailableEndDateTimeUtc { get; set; } + /// + /// Gets or sets the start date and time of the special price + /// + [DataMember] + public DateTime? SpecialPriceStartDateTimeUtc { get; set; } - /// - /// Gets or sets a display order. This value is used when sorting associated products (used with "grouped" products) - /// - [DataMember] - public int DisplayOrder { get; set; } + /// + /// Gets or sets the end date and time of the special price + /// + [DataMember] + public DateTime? SpecialPriceEndDateTimeUtc { get; set; } + + /// + /// Gets or sets a value indicating whether a customer enters price + /// + [DataMember] + public bool CustomerEntersPrice { get; set; } + + /// + /// Gets or sets the minimum price entered by a customer + /// + [DataMember] + public decimal MinimumCustomerEnteredPrice { get; set; } + + /// + /// Gets or sets the maximum price entered by a customer + /// + [DataMember] + public decimal MaximumCustomerEnteredPrice { get; set; } + + /// + /// Gets or sets a value indicating whether this product has tier prices configured + /// The same as if we run this.TierPrices.Count > 0 + /// We use this property for performance optimization: + /// if this property is set to false, then we do not need to load tier prices navigation property + /// + /// + [DataMember] + public bool HasTierPrices { get; set; } + + /// + /// Gets or sets a value for the lowest attribute combination price override + /// + [DataMember] + public decimal? LowestAttributeCombinationPrice { get; set; } + + /// + /// Gets or sets the behaviour when selecting product attributes. + /// + [DataMember] + public AttributeChoiceBehaviour AttributeChoiceBehaviour { get; set; } + + /// + /// Gets or sets a value indicating whether this product has discounts applied + /// The same as if we run this.AppliedDiscounts.Count > 0 + /// We use this property for performance optimization: + /// if this property is set to false, then we do not need to load Applied Discounts navifation property + /// + /// + [DataMember] + public bool HasDiscountsApplied { get; set; } + + /// + /// Gets or sets the weight + /// + [DataMember] + public decimal Weight { get; set; } + + /// + /// Gets or sets the length + /// + [DataMember] + public decimal Length + { + [DebuggerStepThrough] + get => this.GetMergedDataValue(nameof(Length), _length); + set => _length = value; + } + + /// + /// Gets or sets the width + /// + [DataMember] + public decimal Width + { + [DebuggerStepThrough] + get => this.GetMergedDataValue(nameof(Width), _width); + set => _width = value; + } + + /// + /// Gets or sets the height + /// + [DataMember] + public decimal Height + { + [DebuggerStepThrough] + get => this.GetMergedDataValue(nameof(Height), _height); + set => _height = value; + } + + /// + /// Gets or sets the available start date and time + /// + [DataMember] + public DateTime? AvailableStartDateTimeUtc { get; set; } + + /// + /// Gets or sets the available end date and time + /// + [DataMember] + public DateTime? AvailableEndDateTimeUtc { get; set; } + + /// + /// Gets or sets a display order. This value is used when sorting associated products (used with "grouped" products) + /// + [DataMember] + public int DisplayOrder { get; set; } /// /// Gets or sets a value indicating whether the entity is published /// [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. - /// - [DataMember] - [Index("IX_Product_SystemName_IsSystemProduct", 2)] + /// + /// Gets or sets a value indicating whether the entity is a system product. + /// + [DataMember] + [Index] + [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. - /// - [DataMember] - [Index("IX_Product_SystemName_IsSystemProduct", 1)] - public string SystemName { get; set; } + /// + /// Gets or sets the product system name. + /// + [DataMember] + [Index("IX_Product_SystemName_IsSystemProduct", 1)] + public string SystemName { get; set; } - /// - /// Gets or sets the date and time of product creation - /// - [DataMember] - public DateTime CreatedOnUtc { get; set; } + /// + /// Gets or sets the date and time of product creation + /// + [DataMember] + public DateTime CreatedOnUtc { get; set; } /// /// Gets or sets the date and time of product update /// [DataMember] - public DateTime UpdatedOnUtc { get; set; } + public DateTime UpdatedOnUtc { get; set; } - /// - /// Gets or sets the delivery time identifier - /// - [DataMember] - public int? DeliveryTimeId - { - [DebuggerStepThrough] - get - { - return this.GetMergedDataValue(nameof(DeliveryTimeId), _deliveryTimeId); - } - set - { - _deliveryTimeId = value; - } - } + /// + /// Gets or sets the delivery time identifier + /// + [DataMember] + public int? DeliveryTimeId + { + [DebuggerStepThrough] + get => this.GetMergedDataValue(nameof(DeliveryTimeId), _deliveryTimeId); + set => _deliveryTimeId = value; + } - [DataMember] - public virtual DeliveryTime DeliveryTime { get; set; } + [DataMember] + public virtual DeliveryTime DeliveryTime { get; set; } /// /// Gets or sets the quantity unit identifier @@ -774,123 +728,99 @@ public int? DeliveryTimeId public int? QuantityUnitId { [DebuggerStepThrough] - get - { - return this.GetMergedDataValue(nameof(QuantityUnitId), _quantityUnitId); - } - set - { - _quantityUnitId = value; - } + get => this.GetMergedDataValue(nameof(QuantityUnitId), _quantityUnitId); + set => _quantityUnitId = value; } [DataMember] public virtual QuantityUnit QuantityUnit { get; set; } - /// - /// Gets or sets the customs tariff number - /// - [DataMember] - public string CustomsTariffNumber { get; set; } + /// + /// Gets or sets the customs tariff number + /// + [DataMember] + public string CustomsTariffNumber { get; set; } - /// - /// Gets or sets the country of origin identifier - /// - [DataMember] - public int? CountryOfOriginId { get; set; } + /// + /// Gets or sets the country of origin identifier + /// + [DataMember] + public int? CountryOfOriginId { get; set; } - /// - /// Gets or sets the country of origin - /// - [DataMember] - public virtual Country CountryOfOrigin { get; set; } + /// + /// Gets or sets the country of origin + /// + [DataMember] + public virtual Country CountryOfOrigin { get; set; } - /// - /// Gets or sets if base price quotation (PAnGV) is enabled - /// - [DataMember] - public bool BasePriceEnabled { get; set; } + /// + /// Gets or sets if base price quotation (PAnGV) is enabled + /// + [DataMember] + public bool BasePriceEnabled { get; set; } - /// - /// Measure unit for the base price (e.g. "kg", "g", "qm" etc.) - /// - [DataMember] - public string BasePriceMeasureUnit { get; set; } + /// + /// Measure unit for the base price (e.g. "kg", "g", "qm" etc.) + /// + [DataMember] + public string BasePriceMeasureUnit { get; set; } - /// - /// Amount of product per packing unit in the given measure unit + /// + /// Amount of product per packing unit in the given measure unit /// (e.g. 250 ml shower gel: "0.25" if MeasureUnit = "liter" and BaseAmount = 1) - /// - [DataMember] - public decimal? BasePriceAmount - { - [DebuggerStepThrough] - get - { - return this.GetMergedDataValue(nameof(BasePriceAmount), _basePriceAmount); - } - set - { - _basePriceAmount = value; - } - } - - /// - /// Reference value for the given measure unit - /// (e.g. "1" liter. Formula: [BaseAmount] [MeasureUnit] = [SellingPrice] / [Amount]) - /// - [DataMember] - public int? BasePriceBaseAmount - { - [DebuggerStepThrough] - get - { - return this.GetMergedDataValue(nameof(BasePriceBaseAmount), _basePriceBaseAmount); - } - set - { - _basePriceBaseAmount = value; - } - } + /// + [DataMember] + public decimal? BasePriceAmount + { + [DebuggerStepThrough] + get => this.GetMergedDataValue(nameof(BasePriceAmount), _basePriceAmount); + set => _basePriceAmount = value; + } - [DataMember] - public bool BasePriceHasValue - { - get - { - return BasePriceEnabled && BasePriceAmount.GetValueOrDefault() > 0 && BasePriceBaseAmount.GetValueOrDefault() > 0 && BasePriceMeasureUnit.HasValue(); - } - } - - /// - /// Optional title text of a product bundle - /// - [DataMember] - public string BundleTitleText { get; set; } + /// + /// Reference value for the given measure unit + /// (e.g. "1" liter. Formula: [BaseAmount] [MeasureUnit] = [SellingPrice] / [Amount]) + /// + [DataMember] + public int? BasePriceBaseAmount + { + [DebuggerStepThrough] + get => this.GetMergedDataValue(nameof(BasePriceBaseAmount), _basePriceBaseAmount); + set => _basePriceBaseAmount = value; + } - /// - /// Per item shipping of bundle items - /// - [DataMember] - public bool BundlePerItemShipping { get; set; } + [DataMember] + public bool BasePriceHasValue => BasePriceEnabled && BasePriceAmount.GetValueOrDefault() > 0 && BasePriceBaseAmount.GetValueOrDefault() > 0 && BasePriceMeasureUnit.HasValue(); - /// - /// Per item pricing of bundle items - /// - [DataMember] - public bool BundlePerItemPricing { get; set; } + /// + /// Optional title text of a product bundle + /// + [DataMember] + public string BundleTitleText { get; set; } - /// - /// Per item shopping cart handling of bundle items - /// - [DataMember] - public bool BundlePerItemShoppingCart { get; set; } + /// + /// Per item shipping of bundle items + /// + [DataMember] + public bool BundlePerItemShipping { get; set; } - /// - /// Gets or sets the main picture id - /// - [DataMember] - public int? MainPictureId { get; set; } + /// + /// Per item pricing of bundle items + /// + [DataMember] + public bool BundlePerItemPricing { get; set; } + + /// + /// Per item shopping cart handling of bundle items + /// + [DataMember] + public bool BundlePerItemShoppingCart { get; set; } + + /// + /// Gets or sets the main picture id + /// + [DataMember] + public int? MainPictureId { get; set; } /// /// Gets or sets a value that indictaes whether the product has a preview picture @@ -902,160 +832,118 @@ public bool BasePriceHasValue /// Gets or sets the product type /// [DataMember] - public ProductType ProductType - { - get - { - return (ProductType)this.ProductTypeId; - } - set - { - this.ProductTypeId = (int)value; - } - } - - public string ProductTypeLabelHint - { - get - { - switch (ProductType) - { - case ProductType.SimpleProduct: - return "secondary d-none"; - case ProductType.GroupedProduct: - return "success"; - case ProductType.BundledProduct: - return "info"; - default: - return ""; - } - } - } - - /// - /// Gets or sets the backorder mode - /// - [DataMember] - public BackorderMode BackorderMode - { - get - { - return (BackorderMode)this.BackorderModeId; - } - set - { - this.BackorderModeId = (int)value; - } - } - - /// - /// Gets or sets the download activation type - /// - [DataMember] - public DownloadActivationType DownloadActivationType - { - get - { - return (DownloadActivationType)this.DownloadActivationTypeId; - } - set - { - this.DownloadActivationTypeId = (int)value; - } - } - - /// - /// Gets or sets the gift card type - /// - [DataMember] - public GiftCardType GiftCardType - { - get - { - return (GiftCardType)this.GiftCardTypeId; - } - set - { - this.GiftCardTypeId = (int)value; - } - } - - /// - /// Gets or sets the low stock activity - /// - [DataMember] - public LowStockActivity LowStockActivity - { - get - { - return (LowStockActivity)this.LowStockActivityId; - } - set - { - this.LowStockActivityId = (int)value; - } - } - - /// - /// Gets or sets the value indicating how to manage inventory - /// - [DataMember] - public ManageInventoryMethod ManageInventoryMethod - { - get - { - return (ManageInventoryMethod)this.ManageInventoryMethodId; - } - set - { - this.ManageInventoryMethodId = (int)value; - } - } - - /// - /// Gets or sets the cycle period for recurring products - /// - [DataMember] - public RecurringProductCyclePeriod RecurringCyclePeriod - { - get - { - return (RecurringProductCyclePeriod)this.RecurringCyclePeriodId; - } - set - { - this.RecurringCyclePeriodId = (int)value; - } - } - - /// + public ProductType ProductType + { + get => (ProductType)this.ProductTypeId; + set => this.ProductTypeId = (int)value; + } + + public string ProductTypeLabelHint + { + get + { + switch (ProductType) + { + case ProductType.SimpleProduct: + return "secondary d-none"; + case ProductType.GroupedProduct: + return "success"; + case ProductType.BundledProduct: + return "info"; + default: + return ""; + } + } + } + + /// + /// Gets or sets the backorder mode + /// + [DataMember] + public BackorderMode BackorderMode + { + get => (BackorderMode)this.BackorderModeId; + set => this.BackorderModeId = (int)value; + } + + /// + /// Gets or sets the download activation type + /// + [DataMember] + public DownloadActivationType DownloadActivationType + { + get => (DownloadActivationType)this.DownloadActivationTypeId; + set => this.DownloadActivationTypeId = (int)value; + } + + /// + /// Gets or sets the gift card type + /// + [DataMember] + public GiftCardType GiftCardType + { + get => (GiftCardType)this.GiftCardTypeId; + set => this.GiftCardTypeId = (int)value; + } + + /// + /// Gets or sets the low stock activity + /// + [DataMember] + public LowStockActivity LowStockActivity + { + get => (LowStockActivity)this.LowStockActivityId; + set => this.LowStockActivityId = (int)value; + } + + /// + /// Gets or sets the value indicating how to manage inventory + /// + [DataMember] + public ManageInventoryMethod ManageInventoryMethod + { + get => (ManageInventoryMethod)this.ManageInventoryMethodId; + set => this.ManageInventoryMethodId = (int)value; + } + + /// + /// Gets or sets the cycle period for recurring products + /// + [DataMember] + public RecurringProductCyclePeriod RecurringCyclePeriod + { + get => (RecurringProductCyclePeriod)this.RecurringCyclePeriodId; + set => this.RecurringCyclePeriodId = (int)value; + } + + /// /// Gets or sets the collection of ProductCategory /// [DataMember] public virtual ICollection ProductCategories { - get { return _productCategories ?? (_productCategories = new HashSet()); } - protected set { _productCategories = value; } + get => _productCategories ?? (_productCategories = new HashSet()); + protected set => _productCategories = value; } /// /// Gets or sets the collection of ProductManufacturer /// [DataMember] - public virtual ICollection ProductManufacturers + public virtual ICollection ProductManufacturers { - get { return _productManufacturers ?? (_productManufacturers = new HashSet()); } - protected set { _productManufacturers = value; } + get => _productManufacturers ?? (_productManufacturers = new HashSet()); + protected set => _productManufacturers = value; } /// /// Gets or sets the collection of ProductPicture /// [DataMember] - public virtual ICollection ProductPictures + public virtual ICollection ProductPictures { - get { return _productPictures ?? (_productPictures = new List()); } - protected set { _productPictures = value; } + get => _productPictures ?? (_productPictures = new List()); + protected set => _productPictures = value; } /// @@ -1063,78 +951,78 @@ public virtual ICollection ProductPictures /// public virtual ICollection ProductReviews { - get { return _productReviews ?? (_productReviews = new HashSet()); } - protected set { _productReviews = value; } + get => _productReviews ?? (_productReviews = new HashSet()); + protected set => _productReviews = value; } /// /// Gets or sets the product specification attribute /// [DataMember] - public virtual ICollection ProductSpecificationAttributes + public virtual ICollection ProductSpecificationAttributes { - get { return _productSpecificationAttributes ?? (_productSpecificationAttributes = new HashSet()); } - protected set { _productSpecificationAttributes = value; } + get => _productSpecificationAttributes ?? (_productSpecificationAttributes = new HashSet()); + protected set => _productSpecificationAttributes = value; } /// /// Gets or sets the product tags /// [DataMember] - public virtual ICollection ProductTags + public virtual ICollection ProductTags { - get { return _productTags ?? (_productTags = new HashSet()); } - protected set { _productTags = value; } + get => _productTags ?? (_productTags = new HashSet()); + protected set => _productTags = value; } - /// - /// Gets or sets the product attributes - /// - [DataMember] - public virtual ICollection ProductVariantAttributes - { - get { return _productVariantAttributes ?? (_productVariantAttributes = new HashSet()); } - protected set { _productVariantAttributes = value; } - } - - /// - /// Gets or sets the product attribute combinations - /// - [DataMember] - public virtual ICollection ProductVariantAttributeCombinations - { - get { return _productVariantAttributeCombinations ?? (_productVariantAttributeCombinations = new List()); } - protected set { _productVariantAttributeCombinations = value; } - } - - /// - /// Gets or sets the tier prices - /// - [DataMember] - public virtual ICollection TierPrices - { - get { return _tierPrices ?? (_tierPrices = new HashSet()); } - protected set { _tierPrices = value; } - } - - /// - /// Gets or sets the collection of applied discounts - /// - [DataMember] - public virtual ICollection AppliedDiscounts - { - get { return _appliedDiscounts ?? (_appliedDiscounts = new HashSet()); } - protected set { _appliedDiscounts = value; } - } - - /// - /// Gets or sets the collection of product bundle items - /// - [DataMember] - public virtual ICollection ProductBundleItems - { - get { return _productBundleItems ?? (_productBundleItems = new HashSet()); } - protected set { _productBundleItems = value; } - } + /// + /// Gets or sets the product attributes + /// + [DataMember] + public virtual ICollection ProductVariantAttributes + { + get => _productVariantAttributes ?? (_productVariantAttributes = new HashSet()); + protected set => _productVariantAttributes = value; + } + + /// + /// Gets or sets the product attribute combinations + /// + [DataMember] + public virtual ICollection ProductVariantAttributeCombinations + { + get => _productVariantAttributeCombinations ?? (_productVariantAttributeCombinations = new List()); + protected set => _productVariantAttributeCombinations = value; + } + + /// + /// Gets or sets the tier prices + /// + [DataMember] + public virtual ICollection TierPrices + { + get => _tierPrices ?? (_tierPrices = new HashSet()); + protected set => _tierPrices = value; + } + + /// + /// Gets or sets the collection of applied discounts + /// + [DataMember] + public virtual ICollection AppliedDiscounts + { + get => _appliedDiscounts ?? (_appliedDiscounts = new HashSet()); + protected set => _appliedDiscounts = value; + } + + /// + /// Gets or sets the collection of product bundle items + /// + [DataMember] + public virtual ICollection ProductBundleItems + { + get => _productBundleItems ?? (_productBundleItems = new HashSet()); + protected set => _productBundleItems = value; + } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductAttribute.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductAttribute.cs index 72742f23b3..9043816b92 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductAttribute.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductAttribute.cs @@ -7,50 +7,51 @@ namespace SmartStore.Core.Domain.Catalog { - /// - /// Represents a product attribute - /// - [DataContract] - public partial class ProductAttribute : BaseEntity, ILocalizedEntity, ISearchAlias - { - private ICollection _productAttributeOptionsSets; + /// + /// Represents a product attribute + /// + [DataContract] + public partial class ProductAttribute : BaseEntity, ILocalizedEntity, ISearchAlias + { + private ICollection _productAttributeOptionsSets; /// /// Gets or sets the product attribute alias /// [DataMember] public string Alias { get; set; } - + /// /// Gets or sets the name /// - [DataMember] - public string Name { get; set; } + [DataMember] + public string Name { get; set; } /// /// Gets or sets the description /// [DataMember] - public string Description { get; set; } + public string Description { get; set; } - /// - /// Gets or sets whether the attribute can be filtered - /// - [DataMember] - [Index] - public bool AllowFiltering { get; set; } + /// + /// Gets or sets whether the attribute can be filtered + /// + [DataMember] + [Index] + public bool AllowFiltering { get; set; } - /// - /// Gets or sets the display order - /// - [DataMember] - public int DisplayOrder { get; set; } + /// + /// Gets or sets the display order + /// + [DataMember] + [Index] + public int DisplayOrder { get; set; } /// /// Gets or sets the facet template hint. Only effective in accordance with MegaSearchPlus plugin. /// [DataMember] - public FacetTemplateHint FacetTemplateHint { get; set; } + public FacetTemplateHint FacetTemplateHint { get; set; } /// /// Specifies whether option names should be included in the search index. Only effective in accordance with MegaSearchPlus plugin. @@ -58,20 +59,20 @@ public partial class ProductAttribute : BaseEntity, ILocalizedEntity, ISearchAli [DataMember] public bool IndexOptionNames { get; set; } - /// - /// Gets or sets export mappings. - /// - [DataMember] - public string ExportMappings { get; set; } + /// + /// Gets or sets export mappings. + /// + [DataMember] + public string ExportMappings { get; set; } - /// - /// Gets or sets the prooduct attribute option sets - /// - [DataMember] - public virtual ICollection ProductAttributeOptionsSets - { - get { return _productAttributeOptionsSets ?? (_productAttributeOptionsSets = new HashSet()); } - protected set { _productAttributeOptionsSets = value; } - } - } + /// + /// Gets or sets the prooduct attribute option sets + /// + [DataMember] + public virtual ICollection ProductAttributeOptionsSets + { + get => _productAttributeOptionsSets ?? (_productAttributeOptionsSets = new HashSet()); + protected set => _productAttributeOptionsSets = value; + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductAttributeOption.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductAttributeOption.cs index 7897e23016..0bbe59e24d 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductAttributeOption.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductAttributeOption.cs @@ -5,127 +5,121 @@ namespace SmartStore.Core.Domain.Catalog { - /// - /// Represents a product attribute option - /// - [DataContract] - public partial class ProductAttributeOption : BaseEntity, ILocalizedEntity, ISearchAlias, ICloneable - { - /// - /// Gets or sets the product attribute options set identifier - /// - [DataMember] - public int ProductAttributeOptionsSetId { get; set; } - - /// - /// Gets or sets the option alias - /// - [DataMember] - public string Alias { get; set; } - - /// - /// Gets or sets the option name - /// - [DataMember] - public string Name { get; set; } - - /// - /// Gets or sets the Picture Id - /// - [DataMember] - public int PictureId { get; set; } - - /// - /// Gets or sets the color RGB value (used with "Boxes" attribute type) - /// - [DataMember] - public string Color { get; set; } - - /// - /// Gets or sets the price adjustment - /// - [DataMember] - public decimal PriceAdjustment { get; set; } - - /// - /// Gets or sets the weight adjustment - /// - [DataMember] - public decimal WeightAdjustment { get; set; } - - /// - /// Gets or sets a value indicating whether the option is pre-selected - /// - [DataMember] - public bool IsPreSelected { get; set; } - - /// - /// Gets or sets the display order - /// - [DataMember] - public int DisplayOrder { get; set; } - - /// - /// Gets or sets the type Id - /// - [DataMember] - public int ValueTypeId { get; set; } - - /// - /// Gets or sets the linked product Id - /// - [DataMember] - public int LinkedProductId { get; set; } - - /// - /// Gets or sets the quantity for the linked product - /// - [DataMember] - public int Quantity { get; set; } - - /// - /// Gets the product attribute options set - /// - [DataMember] - public virtual ProductAttributeOptionsSet ProductAttributeOptionsSet { get; set; } - - /// - /// Gets or sets the product attribute value type - /// - [DataMember] - public ProductVariantAttributeValueType ValueType - { - get - { - return (ProductVariantAttributeValueType)this.ValueTypeId; - } - set - { - this.ValueTypeId = (int)value; - } - } - - public ProductVariantAttributeValue Clone() - { - var value = new ProductVariantAttributeValue(); - value.Alias = Alias; - value.Name = Name; - value.PictureId = PictureId; - value.Color = Color; - value.PriceAdjustment = PriceAdjustment; - value.WeightAdjustment = WeightAdjustment; - value.IsPreSelected = IsPreSelected; - value.DisplayOrder = DisplayOrder; - value.ValueTypeId = ValueTypeId; - value.LinkedProductId = LinkedProductId; - value.Quantity = Quantity; - - return value; - } - - object ICloneable.Clone() - { - return this.Clone(); - } - } + /// + /// Represents a product attribute option + /// + [DataContract] + public partial class ProductAttributeOption : BaseEntity, ILocalizedEntity, ISearchAlias, ICloneable + { + /// + /// Gets or sets the product attribute options set identifier + /// + [DataMember] + public int ProductAttributeOptionsSetId { get; set; } + + /// + /// Gets or sets the option alias + /// + [DataMember] + public string Alias { get; set; } + + /// + /// Gets or sets the option name + /// + [DataMember] + public string Name { get; set; } + + /// + /// Gets or sets the media file id + /// + [DataMember] + public int MediaFileId { get; set; } + + /// + /// Gets or sets the color RGB value (used with "Boxes" attribute type) + /// + [DataMember] + public string Color { get; set; } + + /// + /// Gets or sets the price adjustment + /// + [DataMember] + public decimal PriceAdjustment { get; set; } + + /// + /// Gets or sets the weight adjustment + /// + [DataMember] + public decimal WeightAdjustment { get; set; } + + /// + /// Gets or sets a value indicating whether the option is pre-selected + /// + [DataMember] + public bool IsPreSelected { get; set; } + + /// + /// Gets or sets the display order + /// + [DataMember] + public int DisplayOrder { get; set; } + + /// + /// Gets or sets the type Id + /// + [DataMember] + public int ValueTypeId { get; set; } + + /// + /// Gets or sets the linked product Id + /// + [DataMember] + public int LinkedProductId { get; set; } + + /// + /// Gets or sets the quantity for the linked product + /// + [DataMember] + public int Quantity { get; set; } + + /// + /// Gets the product attribute options set + /// + [DataMember] + public virtual ProductAttributeOptionsSet ProductAttributeOptionsSet { get; set; } + + /// + /// Gets or sets the product attribute value type + /// + [DataMember] + public ProductVariantAttributeValueType ValueType + { + get => (ProductVariantAttributeValueType)this.ValueTypeId; + set => this.ValueTypeId = (int)value; + } + + public ProductVariantAttributeValue Clone() + { + var value = new ProductVariantAttributeValue(); + value.Alias = Alias; + value.Name = Name; + value.MediaFileId = MediaFileId; + value.Color = Color; + value.PriceAdjustment = PriceAdjustment; + value.WeightAdjustment = WeightAdjustment; + value.IsPreSelected = IsPreSelected; + value.DisplayOrder = DisplayOrder; + value.ValueTypeId = ValueTypeId; + value.LinkedProductId = LinkedProductId; + value.Quantity = Quantity; + + return value; + } + + object ICloneable.Clone() + { + return this.Clone(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductAttributeOptionsSet.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductAttributeOptionsSet.cs index c6819334d2..4dcbac9d0d 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductAttributeOptionsSet.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductAttributeOptionsSet.cs @@ -3,37 +3,37 @@ namespace SmartStore.Core.Domain.Catalog { - [DataContract] - public partial class ProductAttributeOptionsSet : BaseEntity - { - private ICollection _productAttributeOptions; + [DataContract] + public partial class ProductAttributeOptionsSet : BaseEntity + { + private ICollection _productAttributeOptions; - /// - /// Gets or sets the name - /// - [DataMember] - public string Name { get; set; } + /// + /// Gets or sets the name + /// + [DataMember] + public string Name { get; set; } - /// - /// Gets or sets the product attribute identifier - /// - [DataMember] - public int ProductAttributeId { get; set; } + /// + /// Gets or sets the product attribute identifier + /// + [DataMember] + public int ProductAttributeId { get; set; } - /// - /// Gets the product attribute - /// - [DataMember] - public virtual ProductAttribute ProductAttribute { get; set; } + /// + /// Gets the product attribute + /// + [DataMember] + public virtual ProductAttribute ProductAttribute { get; set; } - /// - /// Gets or sets the prooduct attribute options - /// - [DataMember] - public virtual ICollection ProductAttributeOptions - { - get { return _productAttributeOptions ?? (_productAttributeOptions = new HashSet()); } - protected set { _productAttributeOptions = value; } - } - } + /// + /// Gets or sets the prooduct attribute options + /// + [DataMember] + public virtual ICollection ProductAttributeOptions + { + get => _productAttributeOptions ?? (_productAttributeOptions = new HashSet()); + protected set => _productAttributeOptions = value; + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductBundleData.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductBundleData.cs index 60373a2d15..ea7b41f44c 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductBundleData.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductBundleData.cs @@ -1,29 +1,29 @@ namespace SmartStore.Core.Domain.Catalog { public partial class ProductBundleItemData - { - public ProductBundleItemData(ProductBundleItem item) - { - Item = item; // Can be null. - } + { + public ProductBundleItemData(ProductBundleItem item) + { + Item = item; // Can be null. + } - public ProductBundleItem Item { get; private set; } - public decimal AdditionalCharge { get; set; } - } + public ProductBundleItem Item { get; private set; } + public decimal AdditionalCharge { get; set; } + } - public partial class ProductBundleItemOrderData - { - public int BundleItemId { get; set; } - public int ProductId { get; set; } - public string Sku { get; set; } - public string ProductName { get; set; } - public string ProductSeName { get; set; } - public bool VisibleIndividually { get; set; } - public int Quantity { get; set; } - public decimal PriceWithDiscount { get; set; } - public int DisplayOrder { get; set; } - public string AttributesXml { get; set; } - public string AttributesInfo { get; set; } - public bool PerItemShoppingCart { get; set; } - } + public partial class ProductBundleItemOrderData + { + public int BundleItemId { get; set; } + public int ProductId { get; set; } + public string Sku { get; set; } + public string ProductName { get; set; } + public string ProductSeName { get; set; } + public bool VisibleIndividually { get; set; } + public int Quantity { get; set; } + public decimal PriceWithDiscount { get; set; } + public int DisplayOrder { get; set; } + public string AttributesXml { get; set; } + public string AttributesInfo { get; set; } + public bool PerItemShoppingCart { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductBundleItem.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductBundleItem.cs index 38865d9b70..923e1681e7 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductBundleItem.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductBundleItem.cs @@ -5,141 +5,141 @@ namespace SmartStore.Core.Domain.Catalog { - [DataContract] - public partial class ProductBundleItem : BaseEntity, IAuditable, ILocalizedEntity, ICloneable - { - private ICollection _attributeFilters; - - /// - /// Gets or sets the product identifier - /// - [DataMember] - public int ProductId { get; set; } - - /// - /// Gets or sets the product identifier of the bundle product - /// - [DataMember] - public int BundleProductId { get; set; } - - /// - /// Gets or sets the quantity - /// - [DataMember] - public int Quantity { get; set; } - - /// - /// Gets or sets the discount value - /// - [DataMember] - public decimal? Discount { get; set; } - - /// - /// Gets or sets whether the discount is in percent - /// - [DataMember] - public bool DiscountPercentage { get; set; } - - /// - /// Gets or sets the name value - /// - [DataMember] - public string Name { get; set; } - - /// - /// Gets or sets the name value - /// - [DataMember] - public string ShortDescription { get; set; } - - /// - /// Gets or sets a value indicating whether to filter attributes - /// - [DataMember] - public bool FilterAttributes { get; set; } - - /// - /// Gets or sets a value indicating whether to hide the thumbnail - /// - [DataMember] - public bool HideThumbnail { get; set; } - - /// - /// Gets or sets a value indicating whether the item is visible - /// - [DataMember] - public bool Visible { get; set; } - - /// - /// Gets or sets a value indicating whether the entity is published - /// - [DataMember] - public bool Published { get; set; } - - /// - /// Gets or sets a display order - /// - [DataMember] - public int DisplayOrder { get; set; } - - /// - /// Gets or sets the date and time of product bundle item creation - /// - [DataMember] - public DateTime CreatedOnUtc { get; set; } - - /// - /// Gets or sets the date and time of product bundle item update - /// - [DataMember] - public DateTime UpdatedOnUtc { get; set; } - - /// - /// Gets the product - /// - [DataMember] - public virtual Product Product { get; set; } - - /// - /// Gets the bundle product - /// - [DataMember] - public virtual Product BundleProduct { get; set; } - - /// - /// Gets or sets the collection of attribute filters - /// - public virtual ICollection AttributeFilters - { - get { return _attributeFilters ?? (_attributeFilters = new HashSet()); } - protected set { _attributeFilters = value; } - } - - public ProductBundleItem Clone() - { - var bundleItem = new ProductBundleItem - { - ProductId = this.ProductId, - BundleProductId = this.BundleProductId, - Quantity = this.Quantity, - Discount = this.Discount, - DiscountPercentage = this.DiscountPercentage, - Name = this.Name, - ShortDescription = this.ShortDescription, - FilterAttributes = this.FilterAttributes, - HideThumbnail = this.HideThumbnail, - Visible = this.Visible, - Published = this.Published, - DisplayOrder = this.DisplayOrder, - CreatedOnUtc = this.CreatedOnUtc, - UpdatedOnUtc = this.UpdatedOnUtc - }; - return bundleItem; - } - - object ICloneable.Clone() - { - return this.Clone(); - } - } + [DataContract] + public partial class ProductBundleItem : BaseEntity, IAuditable, ILocalizedEntity, ICloneable + { + private ICollection _attributeFilters; + + /// + /// Gets or sets the product identifier + /// + [DataMember] + public int ProductId { get; set; } + + /// + /// Gets or sets the product identifier of the bundle product + /// + [DataMember] + public int BundleProductId { get; set; } + + /// + /// Gets or sets the quantity + /// + [DataMember] + public int Quantity { get; set; } + + /// + /// Gets or sets the discount value + /// + [DataMember] + public decimal? Discount { get; set; } + + /// + /// Gets or sets whether the discount is in percent + /// + [DataMember] + public bool DiscountPercentage { get; set; } + + /// + /// Gets or sets the name value + /// + [DataMember] + public string Name { get; set; } + + /// + /// Gets or sets the name value + /// + [DataMember] + public string ShortDescription { get; set; } + + /// + /// Gets or sets a value indicating whether to filter attributes + /// + [DataMember] + public bool FilterAttributes { get; set; } + + /// + /// Gets or sets a value indicating whether to hide the thumbnail + /// + [DataMember] + public bool HideThumbnail { get; set; } + + /// + /// Gets or sets a value indicating whether the item is visible + /// + [DataMember] + public bool Visible { get; set; } + + /// + /// Gets or sets a value indicating whether the entity is published + /// + [DataMember] + public bool Published { get; set; } + + /// + /// Gets or sets a display order + /// + [DataMember] + public int DisplayOrder { get; set; } + + /// + /// Gets or sets the date and time of product bundle item creation + /// + [DataMember] + public DateTime CreatedOnUtc { get; set; } + + /// + /// Gets or sets the date and time of product bundle item update + /// + [DataMember] + public DateTime UpdatedOnUtc { get; set; } + + /// + /// Gets the product + /// + [DataMember] + public virtual Product Product { get; set; } + + /// + /// Gets the bundle product + /// + [DataMember] + public virtual Product BundleProduct { get; set; } + + /// + /// Gets or sets the collection of attribute filters + /// + public virtual ICollection AttributeFilters + { + get => _attributeFilters ?? (_attributeFilters = new HashSet()); + protected set => _attributeFilters = value; + } + + public ProductBundleItem Clone() + { + var bundleItem = new ProductBundleItem + { + ProductId = this.ProductId, + BundleProductId = this.BundleProductId, + Quantity = this.Quantity, + Discount = this.Discount, + DiscountPercentage = this.DiscountPercentage, + Name = this.Name, + ShortDescription = this.ShortDescription, + FilterAttributes = this.FilterAttributes, + HideThumbnail = this.HideThumbnail, + Visible = this.Visible, + Published = this.Published, + DisplayOrder = this.DisplayOrder, + CreatedOnUtc = this.CreatedOnUtc, + UpdatedOnUtc = this.UpdatedOnUtc + }; + return bundleItem; + } + + object ICloneable.Clone() + { + return this.Clone(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductBundleItemAttributeFilter.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductBundleItemAttributeFilter.cs index e7ecba5eb3..0a6db35734 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductBundleItemAttributeFilter.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductBundleItemAttributeFilter.cs @@ -2,48 +2,48 @@ namespace SmartStore.Core.Domain.Catalog { - public partial class ProductBundleItemAttributeFilter : BaseEntity, ICloneable - { - /// - /// Gets or sets the product bundle item identifier - /// - public int BundleItemId { get; set; } + public partial class ProductBundleItemAttributeFilter : BaseEntity, ICloneable + { + /// + /// Gets or sets the product bundle item identifier + /// + public int BundleItemId { get; set; } - /// - /// Gets or sets the product attribute identifier - /// - public int AttributeId { get; set; } + /// + /// Gets or sets the product attribute identifier + /// + public int AttributeId { get; set; } - /// - /// Gets or sets the product attribute value identifier - /// - public int AttributeValueId { get; set; } + /// + /// Gets or sets the product attribute value identifier + /// + public int AttributeValueId { get; set; } - /// - /// Gets or sets whether the filtered value is preselected - /// - public bool IsPreSelected { get; set; } + /// + /// Gets or sets whether the filtered value is preselected + /// + public bool IsPreSelected { get; set; } - /// - /// Gets the product bundle item - /// - public virtual ProductBundleItem BundleItem { get; set; } + /// + /// Gets the product bundle item + /// + public virtual ProductBundleItem BundleItem { get; set; } - public ProductBundleItemAttributeFilter Clone() - { - var filter = new ProductBundleItemAttributeFilter() - { - BundleItemId = this.BundleItemId, - AttributeId = this.AttributeId, - AttributeValueId = this.AttributeValueId, - IsPreSelected = this.IsPreSelected - }; - return filter; - } + public ProductBundleItemAttributeFilter Clone() + { + var filter = new ProductBundleItemAttributeFilter() + { + BundleItemId = this.BundleItemId, + AttributeId = this.AttributeId, + AttributeValueId = this.AttributeValueId, + IsPreSelected = this.IsPreSelected + }; + return filter; + } - object ICloneable.Clone() - { - return this.Clone(); - } - } + object ICloneable.Clone() + { + return this.Clone(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductCategory.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductCategory.cs index ee5adfe594..1d027e15b4 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductCategory.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductCategory.cs @@ -13,19 +13,19 @@ public partial class ProductCategory : BaseEntity /// Gets or sets the product identifier /// [DataMember] - public int ProductId { get; set; } + public int ProductId { get; set; } /// /// Gets or sets the category identifier /// [DataMember] - public int CategoryId { get; set; } + public int CategoryId { get; set; } /// /// Gets or sets a value indicating whether the product is featured /// [DataMember] - [Index] + [Index] public bool IsFeaturedProduct { get; set; } /// @@ -34,6 +34,13 @@ public partial class ProductCategory : BaseEntity [DataMember] public int DisplayOrder { get; set; } + /// + /// Indicates whether the mapping is created by the user or by the system. + /// + [DataMember] + [Index] + public bool IsSystemMapping { get; set; } + /// /// Gets the category /// diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductCondition.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductCondition.cs new file mode 100644 index 0000000000..11ee581675 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductCondition.cs @@ -0,0 +1,14 @@ +namespace SmartStore.Core.Domain.Catalog +{ + /// + /// Represents a product condition. + /// + /// + public enum ProductCondition + { + New = 0, + Refurbished = 10, + Used = 20, + Damaged = 30 + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductManufacturer.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductManufacturer.cs index 73edaec790..88babcda90 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductManufacturer.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductManufacturer.cs @@ -3,36 +3,36 @@ namespace SmartStore.Core.Domain.Catalog { - /// - /// Represents a product manufacturer mapping - /// - [DataContract] - public partial class ProductManufacturer : BaseEntity + /// + /// Represents a product manufacturer mapping + /// + [DataContract] + public partial class ProductManufacturer : BaseEntity { /// /// Gets or sets the product identifier /// [DataMember] - public int ProductId { get; set; } + public int ProductId { get; set; } /// /// Gets or sets the manufacturer identifier /// [DataMember] - public int ManufacturerId { get; set; } + public int ManufacturerId { get; set; } /// /// Gets or sets a value indicating whether the product is featured /// [DataMember] - [Index] - public bool IsFeaturedProduct { get; set; } + [Index] + public bool IsFeaturedProduct { get; set; } /// /// Gets or sets the display order /// [DataMember] - public int DisplayOrder { get; set; } + public int DisplayOrder { get; set; } /// /// Gets or sets the manufacturer diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductMediaFile.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductMediaFile.cs new file mode 100644 index 0000000000..ad7175b88e --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductMediaFile.cs @@ -0,0 +1,62 @@ +using System.Runtime.Serialization; +using Newtonsoft.Json; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Core.Domain.Catalog +{ + public interface IMediaFile + { + /// + /// Gets or sets the media identifier + int MediaFileId { get; set; } + + /// + /// Gets or sets the display order + /// + int DisplayOrder { get; set; } + + /// + /// Gets the media file + /// + [JsonIgnore] + MediaFile MediaFile { get; set; } + } + + /// + /// Represents a product media file mapping + /// + [DataContract] + public partial class ProductMediaFile : BaseEntity, IMediaFile + { + /// + /// Gets or sets the product identifier + /// + [DataMember] + public int ProductId { get; set; } + + /// + /// Gets or sets the picture identifier + /// + [DataMember] + public int MediaFileId { get; set; } + + /// + /// Gets or sets the display order + /// + [DataMember] + public int DisplayOrder { get; set; } + + + /// + /// Gets the media file + /// + [DataMember] + public virtual MediaFile MediaFile { get; set; } + + /// + /// Gets the product + /// + [DataMember] + public virtual Product Product { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductPicture.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductPicture.cs deleted file mode 100644 index 0250a5b4f8..0000000000 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductPicture.cs +++ /dev/null @@ -1,41 +0,0 @@ -using SmartStore.Core.Domain.Media; -using System.Runtime.Serialization; - -namespace SmartStore.Core.Domain.Catalog -{ - /// - /// Represents a product picture mapping - /// - [DataContract] - public partial class ProductPicture : BaseEntity - { - /// - /// Gets or sets the product identifier - /// - [DataMember] - public int ProductId { get; set; } - - /// - /// Gets or sets the picture identifier - /// - [DataMember] - public int PictureId { get; set; } - - /// - /// Gets or sets the display order - /// - [DataMember] - public int DisplayOrder { get; set; } - - /// - /// Gets the picture - /// - public virtual Picture Picture { get; set; } - - /// - /// Gets the product - /// - public virtual Product Product { get; set; } - } - -} diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductReview.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductReview.cs index eb52702201..7d45b1e756 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductReview.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductReview.cs @@ -50,8 +50,8 @@ public partial class ProductReview : CustomerContent /// public virtual ICollection ProductReviewHelpfulnessEntries { - get { return _productReviewHelpfulnessEntries ?? (_productReviewHelpfulnessEntries = new HashSet()); } - protected set { _productReviewHelpfulnessEntries = value; } + get => _productReviewHelpfulnessEntries ?? (_productReviewHelpfulnessEntries = new HashSet()); + protected set => _productReviewHelpfulnessEntries = value; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductSortingEnum.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductSortingEnum.cs index 1440fd556f..4d4de9397e 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductSortingEnum.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductSortingEnum.cs @@ -10,10 +10,10 @@ public enum ProductSortingEnum /// Initial = 0, - /// - /// Relevance - /// - Relevance = 1, + /// + /// Relevance + /// + Relevance = 1, /// /// Name: A to Z diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductSpecificationAttribute.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductSpecificationAttribute.cs index eb8595b3af..b168833a78 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductSpecificationAttribute.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductSpecificationAttribute.cs @@ -6,38 +6,38 @@ namespace SmartStore.Core.Domain.Catalog /// Represents a product specification attribute /// [DataContract] - public partial class ProductSpecificationAttribute : BaseEntity + public partial class ProductSpecificationAttribute : BaseEntity { /// /// Gets or sets the product identifier /// [DataMember] - public int ProductId { get; set; } + public int ProductId { get; set; } /// /// Gets or sets the specification attribute identifier /// [DataMember] - public int SpecificationAttributeOptionId { get; set; } + public int SpecificationAttributeOptionId { get; set; } - /// - /// Gets or sets whether the attribute can be filtered. Only effective in accordance with MegaSearchPlus plugin. - /// - [DataMember] - public bool? AllowFiltering { get; set; } + /// + /// Gets or sets whether the attribute can be filtered. Only effective in accordance with MegaSearchPlus plugin. + /// + [DataMember] + public bool? AllowFiltering { get; set; } /// /// Gets or sets whether the attribute will be shown on the product page. /// [DataMember] - public bool? ShowOnProductPage { get; set; } + public bool? ShowOnProductPage { get; set; } /// /// Gets or sets the display order /// [DataMember] - public int DisplayOrder { get; set; } - + public int DisplayOrder { get; set; } + /// /// Gets or sets the product /// diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductTag.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductTag.cs index 153d570195..ace86b58ef 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductTag.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductTag.cs @@ -1,30 +1,38 @@ using System.Collections.Generic; -using SmartStore.Core.Domain.Localization; +using System.ComponentModel.DataAnnotations.Schema; using System.Runtime.Serialization; +using SmartStore.Core.Domain.Localization; namespace SmartStore.Core.Domain.Catalog { /// - /// Represents a product tag + /// Represents a product tag. /// [DataContract] - public partial class ProductTag : BaseEntity, ILocalizedEntity + public partial class ProductTag : BaseEntity, ILocalizedEntity { - private ICollection _products; + private ICollection _products; /// - /// Gets or sets the name + /// Gets or sets the name. /// [DataMember] - public string Name { get; set; } + public string Name { get; set; } - /// - /// Gets or sets the products - /// - public virtual ICollection Products - { - get { return _products ?? (_products = new HashSet()); } - protected set { _products = value; } - } + /// + /// Gets or sets a value indicating whether the entity is published. + /// + [DataMember] + [Index("IX_ProductTag_Published")] + public bool Published { get; set; } = true; + + /// + /// Gets or sets the products. + /// + public virtual ICollection Products + { + get => _products ?? (_products = new HashSet()); + protected set => _products = value; + } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductType.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductType.cs index bf71926304..267c7a8b54 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductType.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductType.cs @@ -1,24 +1,24 @@  namespace SmartStore.Core.Domain.Catalog { - /// - /// Represents a product type - /// - public enum ProductType - { - /// - /// Simple product - /// - SimpleProduct = 5, + /// + /// Represents a product type + /// + public enum ProductType + { + /// + /// Simple product + /// + SimpleProduct = 5, - /// - /// Grouped product - /// - GroupedProduct = 10, + /// + /// Grouped product + /// + GroupedProduct = 10, - /// - /// Bundled product - /// - BundledProduct = 15 - } + /// + /// Bundled product + /// + BundledProduct = 15 + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttribute.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttribute.cs index 96a9e5367b..44f6a4885e 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttribute.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttribute.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Runtime.Serialization; using SmartStore.Core.Domain.Localization; @@ -9,98 +10,112 @@ namespace SmartStore.Core.Domain.Catalog /// Represents a product variant attribute mapping /// [DataContract] - public partial class ProductVariantAttribute : BaseEntity, ILocalizedEntity - { + public partial class ProductVariantAttribute : BaseEntity, ILocalizedEntity + { private ICollection _productVariantAttributeValues; /// /// Gets or sets the product identifier /// [DataMember] - [Index("IX_Product_ProductAttribute_Mapping_ProductId_DisplayOrder", 1)] - public int ProductId { get; set; } + [Index("IX_Product_ProductAttribute_Mapping_ProductId_DisplayOrder", 1)] + public int ProductId { get; set; } /// /// Gets or sets the product attribute identifier /// [DataMember] - public int ProductAttributeId { get; set; } + public int ProductAttributeId { get; set; } /// /// Gets or sets a value a text prompt /// [DataMember] - public string TextPrompt { get; set; } + public string TextPrompt { get; set; } + + /// + /// Gets or sets any custom data. + /// It's not used by Smartstore but is being passed to the choice partial view. + /// + [DataMember] + [MaxLength] + public string CustomData { get; set; } /// /// Gets or sets a value indicating whether the entity is required /// [DataMember] - public bool IsRequired { get; set; } + public bool IsRequired { get; set; } /// /// Gets or sets the attribute control type identifier /// [DataMember] - [Index] - public int AttributeControlTypeId { get; set; } + [Index] + public int AttributeControlTypeId { get; set; } /// /// Gets or sets the display order /// [DataMember] - [Index("IX_Product_ProductAttribute_Mapping_ProductId_DisplayOrder", 2)] - public int DisplayOrder { get; set; } + [Index("IX_Product_ProductAttribute_Mapping_ProductId_DisplayOrder", 2)] + public int DisplayOrder { get; set; } /// /// Gets the attribute control type /// [DataMember] - public AttributeControlType AttributeControlType + public AttributeControlType AttributeControlType { - get - { - return (AttributeControlType)this.AttributeControlTypeId; - } - set + get => (AttributeControlType)this.AttributeControlTypeId; + set => this.AttributeControlTypeId = (int)value; + } + + /// + /// Returns a value whether the attribute has a list of values. + /// + public bool IsListTypeAttribute() + { + switch (AttributeControlType) { - this.AttributeControlTypeId = (int)value; + case AttributeControlType.Checkboxes: + case AttributeControlType.Boxes: + case AttributeControlType.DropdownList: + case AttributeControlType.RadioList: + return true; + default: + return false; } } - public bool IsListTypeAttribute() - { - switch (AttributeControlType) - { - case AttributeControlType.Checkboxes: - case AttributeControlType.Boxes: - case AttributeControlType.DropdownList: - case AttributeControlType.RadioList: - return true; - default: - return false; - } - } + /// + /// Returns a value whether the selection of multiple values is supported. + /// + [NotMapped] + public bool IsMultipleChoice + { + get => AttributeControlType == AttributeControlType.Checkboxes; + } /// /// Gets the product attribute /// [DataMember] - public virtual ProductAttribute ProductAttribute { get; set; } + public virtual ProductAttribute ProductAttribute { get; set; } /// /// Gets the product /// public virtual Product Product { get; set; } - + /// /// Gets the product variant attribute values /// - [DataMember] - public virtual ICollection ProductVariantAttributeValues + [DataMember] + public virtual ICollection ProductVariantAttributeValues { - get { return _productVariantAttributeValues ?? (_productVariantAttributeValues = new HashSet()); } - protected set { _productVariantAttributeValues = value; } + get => _productVariantAttributeValues ?? (_productVariantAttributeValues = new HashSet()); + protected set => _productVariantAttributeValues = value; } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeCombination.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeCombination.cs index e34724a820..d3199afd8c 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeCombination.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeCombination.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations.Schema; using System.Runtime.Serialization; namespace SmartStore.Core.Domain.Catalog { @@ -5,35 +6,37 @@ namespace SmartStore.Core.Domain.Catalog /// Represents a product variant attribute combination /// [DataContract] - public partial class ProductVariantAttributeCombination : BaseEntity + public partial class ProductVariantAttributeCombination : BaseEntity { /// /// Gets or sets the product identifier /// [DataMember] - public int ProductId { get; set; } + public int ProductId { get; set; } /// /// Gets or sets the attributes /// [DataMember] - public string AttributesXml { get; set; } + public string AttributesXml { get; set; } /// /// Gets or sets the stock quantity /// [DataMember] - public int StockQuantity { get; set; } + [Index("IX_StockQuantity_AllowOutOfStockOrders", 1)] + public int StockQuantity { get; set; } /// /// Gets or sets a value indicating whether to allow orders when out of stock /// [DataMember] - public bool AllowOutOfStockOrders { get; set; } - + [Index("IX_StockQuantity_AllowOutOfStockOrders", 2)] + public bool AllowOutOfStockOrders { get; set; } + /// /// Gets the product /// - public virtual Product Product { get; set; } + public virtual Product Product { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeValue.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeValue.cs index f6a9c32444..7804a7f972 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeValue.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeValue.cs @@ -9,103 +9,97 @@ namespace SmartStore.Core.Domain.Catalog /// Represents a product variant attribute value /// [DataContract] - public partial class ProductVariantAttributeValue : BaseEntity, ILocalizedEntity, ISearchAlias - { + public partial class ProductVariantAttributeValue : BaseEntity, ILocalizedEntity, ISearchAlias + { /// /// Gets or sets the product variant attribute mapping identifier /// [DataMember] - [Index("IX_ProductVariantAttributeValue_ProductVariantAttributeId_DisplayOrder", 1)] - public int ProductVariantAttributeId { get; set; } + [Index("IX_ProductVariantAttributeValue_ProductVariantAttributeId_DisplayOrder", 1)] + public int ProductVariantAttributeId { get; set; } /// /// Gets or sets the product variant attribute alias /// [DataMember] - public string Alias { get; set; } + public string Alias { get; set; } /// /// Gets or sets the product variant attribute name /// [DataMember] - [Index] - public string Name { get; set; } + [Index] + public string Name { get; set; } /// - /// Gets or sets the Picture Id + /// Gets or sets the media file id /// [DataMember] - public int PictureId { get; set; } + public int MediaFileId { get; set; } /// /// Gets or sets the color RGB value (used with "Boxes" attribute type) /// - [DataMember] - public string Color { get; set; } + [DataMember] + public string Color { get; set; } /// /// Gets or sets the price adjustment /// [DataMember] - public decimal PriceAdjustment { get; set; } + public decimal PriceAdjustment { get; set; } /// /// Gets or sets the weight adjustment /// [DataMember] - public decimal WeightAdjustment { get; set; } + public decimal WeightAdjustment { get; set; } /// /// Gets or sets a value indicating whether the value is pre-selected /// [DataMember] - public bool IsPreSelected { get; set; } + public bool IsPreSelected { get; set; } /// /// Gets or sets the display order /// [DataMember] - [Index("IX_ProductVariantAttributeValue_ProductVariantAttributeId_DisplayOrder", 2)] - public int DisplayOrder { get; set; } + [Index("IX_ProductVariantAttributeValue_ProductVariantAttributeId_DisplayOrder", 2)] + public int DisplayOrder { get; set; } - /// - /// Gets or sets the type Id - /// - [DataMember] - [Index] - public int ValueTypeId { get; set; } + /// + /// Gets or sets the type Id + /// + [DataMember] + [Index] + public int ValueTypeId { get; set; } - /// - /// Gets or sets the linked product Id - /// - [DataMember] - public int LinkedProductId { get; set; } + /// + /// Gets or sets the linked product Id + /// + [DataMember] + public int LinkedProductId { get; set; } - /// - /// Gets or sets the quantity for the linked product - /// - [DataMember] - public int Quantity { get; set; } + /// + /// Gets or sets the quantity for the linked product + /// + [DataMember] + public int Quantity { get; set; } /// /// Gets the product variant attribute /// [DataMember] - public virtual ProductVariantAttribute ProductVariantAttribute { get; set; } + public virtual ProductVariantAttribute ProductVariantAttribute { get; set; } - /// - /// Gets or sets the product attribute value type - /// - public ProductVariantAttributeValueType ValueType - { - get - { - return (ProductVariantAttributeValueType)this.ValueTypeId; - } - set - { - this.ValueTypeId = (int)value; - } - } + /// + /// Gets or sets the product attribute value type + /// + public ProductVariantAttributeValueType ValueType + { + get => (ProductVariantAttributeValueType)this.ValueTypeId; + set => this.ValueTypeId = (int)value; + } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeValueType.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeValueType.cs index 9fbde986e9..e6e7b67256 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeValueType.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVariantAttributeValueType.cs @@ -1,9 +1,9 @@  namespace SmartStore.Core.Domain.Catalog { - public enum ProductVariantAttributeValueType - { - Simple = 0, - ProductLinkage = 10 - } + public enum ProductVariantAttributeValueType + { + Simple = 0, + ProductLinkage = 10 + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVisibility.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVisibility.cs new file mode 100644 index 0000000000..5feed3d369 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/ProductVisibility.cs @@ -0,0 +1,28 @@ +namespace SmartStore.Core.Domain.Catalog +{ + /// + /// Gradually restricts the visibility of products. + /// + public enum ProductVisibility + { + /// + /// Product is fully visible. + /// + Full = 0, + + /// + /// Product is visible in search results. + /// + SearchResults = 10, + + /// + /// Product is not visible in lists but clickable on product pages. + /// + ProductPage = 20, + + /// + /// Product is not visible but appears on grouped product pages. + /// + Hidden = 30 + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/RelatedProduct.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/RelatedProduct.cs index a22ae92c6a..f523da1c4b 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/RelatedProduct.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/RelatedProduct.cs @@ -5,25 +5,25 @@ namespace SmartStore.Core.Domain.Catalog /// Represents a related product /// [DataContract] - public partial class RelatedProduct : BaseEntity + public partial class RelatedProduct : BaseEntity { /// /// Gets or sets the first product identifier /// [DataMember] - public int ProductId1 { get; set; } + public int ProductId1 { get; set; } /// /// Gets or sets the second product identifier /// [DataMember] - public int ProductId2 { get; set; } + public int ProductId2 { get; set; } /// /// Gets or sets the display order /// [DataMember] - public int DisplayOrder { get; set; } + public int DisplayOrder { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/SmartStoreProductVariantAttributeCombination.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/SmartStoreProductVariantAttributeCombination.cs index e7caa492b1..9b3a3c6463 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/SmartStoreProductVariantAttributeCombination.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/SmartStoreProductVariantAttributeCombination.cs @@ -1,7 +1,8 @@ using System; +using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using SmartStore.Core.Domain.Directory; using System.Runtime.Serialization; +using SmartStore.Core.Domain.Directory; namespace SmartStore.Core.Domain.Catalog { @@ -15,40 +16,42 @@ public ProductVariantAttributeCombination() this.IsActive = true; } - [DataMember] + [DataMember] public string Sku { get; set; } - [DataMember] - public string Gtin { get; set; } + [DataMember] + [Index] + public string Gtin { get; set; } - [DataMember] - public string ManufacturerPartNumber { get; set; } + [DataMember] + [Index] + public string ManufacturerPartNumber { get; set; } - [DataMember] - public decimal? Price { get; set; } + [DataMember] + public decimal? Price { get; set; } - [DataMember] - public decimal? Length { get; set; } + [DataMember] + public decimal? Length { get; set; } - [DataMember] - public decimal? Width { get; set; } + [DataMember] + public decimal? Width { get; set; } - [DataMember] - public decimal? Height { get; set; } + [DataMember] + public decimal? Height { get; set; } - [DataMember] - public decimal? BasePriceAmount { get; set; } + [DataMember] + public decimal? BasePriceAmount { get; set; } - [DataMember] - public int? BasePriceBaseAmount { get; set; } + [DataMember] + public int? BasePriceBaseAmount { get; set; } - [DataMember] - public string AssignedPictureIds { get; set; } + [DataMember] + public string AssignedMediaFileIds { get; set; } - [DataMember] + [DataMember] public int? DeliveryTimeId { get; set; } - [DataMember] + [DataMember] public virtual DeliveryTime DeliveryTime { get; set; } [DataMember] @@ -57,18 +60,19 @@ public ProductVariantAttributeCombination() [DataMember] public virtual QuantityUnit QuantityUnit { get; set; } - [DataMember] - public bool IsActive { get; set; } - //public bool IsDefaultCombination { get; set; } + [DataMember] + [Index] + public bool IsActive { get; set; } + //public bool IsDefaultCombination { get; set; } - public int[] GetAssignedPictureIds() + public int[] GetAssignedMediaIds() { - if (string.IsNullOrEmpty(this.AssignedPictureIds)) + if (string.IsNullOrEmpty(this.AssignedMediaFileIds)) { return new int[] { }; } - var query = from id in this.AssignedPictureIds.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + var query = from id in this.AssignedMediaFileIds.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) let idx = id.ToInt() where idx > 0 select idx; @@ -76,15 +80,15 @@ where idx > 0 return query.Distinct().ToArray(); } - public void SetAssignedPictureIds(int[] ids) + public void SetAssignedMediaIds(int[] ids) { if (ids == null || ids.Length == 0) { - this.AssignedPictureIds = null; + this.AssignedMediaFileIds = null; } else { - this.AssignedPictureIds = String.Join(",", ids); + this.AssignedMediaFileIds = String.Join(",", ids); } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/SpecificationAttribute.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/SpecificationAttribute.cs index c5dd44386d..f867f81ff7 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/SpecificationAttribute.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/SpecificationAttribute.cs @@ -7,56 +7,56 @@ namespace SmartStore.Core.Domain.Catalog { - /// - /// Represents a specification attribute - /// - [DataContract] - public partial class SpecificationAttribute : BaseEntity, ILocalizedEntity, ISearchAlias - { + /// + /// Represents a specification attribute + /// + [DataContract] + public partial class SpecificationAttribute : BaseEntity, ILocalizedEntity, ISearchAlias + { private ICollection _specificationAttributeOptions; /// /// Gets or sets the name /// [DataMember] - public string Name { get; set; } + public string Name { get; set; } - /// - /// Gets or sets the specification attribute alias - /// - [DataMember] - public string Alias { get; set; } + /// + /// Gets or sets the specification attribute alias + /// + [DataMember] + public string Alias { get; set; } - /// - /// Gets or sets the display order - /// - [DataMember] - public int DisplayOrder { get; set; } + /// + /// Gets or sets the display order + /// + [DataMember] + public int DisplayOrder { get; set; } - /// - /// Gets or sets whether the specification attribute will be shown on the product page. - /// - [DataMember] - public bool ShowOnProductPage { get; set; } + /// + /// Gets or sets whether the specification attribute will be shown on the product page. + /// + [DataMember] + public bool ShowOnProductPage { get; set; } - /// - /// Gets or sets whether the specification attribute can be filtered. Only effective in accordance with MegaSearchPlus plugin. - /// - [DataMember] - [Index] - public bool AllowFiltering { get; set; } + /// + /// Gets or sets whether the specification attribute can be filtered. Only effective in accordance with MegaSearchPlus plugin. + /// + [DataMember] + [Index] + public bool AllowFiltering { get; set; } - /// - /// Gets or sets the sorting of facets. Only effective in accordance with MegaSearchPlus plugin. - /// - [DataMember] - public FacetSorting FacetSorting { get; set; } + /// + /// Gets or sets the sorting of facets. Only effective in accordance with MegaSearchPlus plugin. + /// + [DataMember] + public FacetSorting FacetSorting { get; set; } - /// - /// Gets or sets the facet template hint. Only effective in accordance with MegaSearchPlus plugin. - /// - [DataMember] - public FacetTemplateHint FacetTemplateHint { get; set; } + /// + /// Gets or sets the facet template hint. Only effective in accordance with MegaSearchPlus plugin. + /// + [DataMember] + public FacetTemplateHint FacetTemplateHint { get; set; } /// /// Specifies whether option names should be included in the search index. Only effective in accordance with MegaSearchPlus plugin. @@ -68,10 +68,10 @@ public partial class SpecificationAttribute : BaseEntity, ILocalizedEntity, ISea /// Gets or sets the specification attribute options /// [DataMember] - public virtual ICollection SpecificationAttributeOptions + public virtual ICollection SpecificationAttributeOptions { - get { return _specificationAttributeOptions ?? (_specificationAttributeOptions = new HashSet()); } - protected set { _specificationAttributeOptions = value; } + get => _specificationAttributeOptions ?? (_specificationAttributeOptions = new HashSet()); + protected set => _specificationAttributeOptions = value; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/SpecificationAttributeOption.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/SpecificationAttributeOption.cs index 47b5a55cef..01089452b4 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/SpecificationAttributeOption.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/SpecificationAttributeOption.cs @@ -5,58 +5,70 @@ namespace SmartStore.Core.Domain.Catalog { - /// - /// Represents a specification attribute option - /// - [DataContract] - public partial class SpecificationAttributeOption : BaseEntity, ILocalizedEntity, ISearchAlias - { + /// + /// Represents a specification attribute option + /// + [DataContract] + public partial class SpecificationAttributeOption : BaseEntity, ILocalizedEntity, ISearchAlias + { private ICollection _productSpecificationAttributes; /// /// Gets or sets the specification attribute identifier /// [DataMember] - public int SpecificationAttributeId { get; set; } + public int SpecificationAttributeId { get; set; } /// /// Gets or sets the name /// [DataMember] - public string Name { get; set; } + public string Name { get; set; } - /// - /// Gets or sets the specification attribute option alias - /// - [DataMember] - public string Alias { get; set; } + /// + /// Gets or sets the specification attribute option alias + /// + [DataMember] + public string Alias { get; set; } - /// - /// Gets or sets the display order - /// - [DataMember] - public int DisplayOrder { get; set; } + /// + /// Gets or sets the display order + /// + [DataMember] + public int DisplayOrder { get; set; } - /// - /// Gets or sets the number value for range filtering - /// - [DataMember] - public decimal NumberValue { get; set; } + /// + /// Gets or sets the number value for range filtering + /// + [DataMember] + public decimal NumberValue { get; set; } - /// - /// Gets or sets the specification attribute - /// + /// + /// Gets or sets the media file id. + /// [DataMember] - public virtual SpecificationAttribute SpecificationAttribute { get; set; } + public int MediaFileId { get; set; } + + /// + /// Gets or sets the color RGB value. + /// + [DataMember] + public string Color { get; set; } + + /// + /// Gets or sets the specification attribute + /// + [DataMember] + public virtual SpecificationAttribute SpecificationAttribute { get; set; } /// /// Gets or sets the product specification attributes /// [DataMember] - public virtual ICollection ProductSpecificationAttributes + public virtual ICollection ProductSpecificationAttributes { - get { return _productSpecificationAttributes ?? (_productSpecificationAttributes = new HashSet()); } - protected set { _productSpecificationAttributes = value; } + get => _productSpecificationAttributes ?? (_productSpecificationAttributes = new HashSet()); + protected set => _productSpecificationAttributes = value; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Catalog/TierPrice.cs b/src/Libraries/SmartStore.Core/Domain/Catalog/TierPrice.cs index 7186b46490..8458fb7f3f 100644 --- a/src/Libraries/SmartStore.Core/Domain/Catalog/TierPrice.cs +++ b/src/Libraries/SmartStore.Core/Domain/Catalog/TierPrice.cs @@ -7,37 +7,37 @@ namespace SmartStore.Core.Domain.Catalog /// Represents a tier price /// [DataContract] - public partial class TierPrice : BaseEntity + public partial class TierPrice : BaseEntity { /// /// Gets or sets the product identifier /// [DataMember] - public int ProductId { get; set; } + public int ProductId { get; set; } - /// - /// Gets or sets the store identifier (0 - all stores) - /// - [DataMember] - public int StoreId { get; set; } + /// + /// Gets or sets the store identifier (0 - all stores) + /// + [DataMember] + public int StoreId { get; set; } /// /// Gets or sets the customer role identifier /// [DataMember] - public int? CustomerRoleId { get; set; } + public int? CustomerRoleId { get; set; } /// /// Gets or sets the quantity /// [DataMember] - public int Quantity { get; set; } + public int Quantity { get; set; } /// /// Gets or sets the price /// [DataMember] - public decimal Price { get; set; } + public decimal Price { get; set; } /// /// Gets or sets the product diff --git a/src/Libraries/SmartStore.Core/Domain/Cms/MenuItemRecord.cs b/src/Libraries/SmartStore.Core/Domain/Cms/MenuItemRecord.cs index 9aab96cc35..90fc7610e9 100644 --- a/src/Libraries/SmartStore.Core/Domain/Cms/MenuItemRecord.cs +++ b/src/Libraries/SmartStore.Core/Domain/Cms/MenuItemRecord.cs @@ -1,6 +1,8 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using SmartStore.Core.Domain.Localization; +using SmartStore.Core.Domain.Security; +using SmartStore.Core.Domain.Stores; namespace SmartStore.Core.Domain.Cms { @@ -8,7 +10,7 @@ namespace SmartStore.Core.Domain.Cms /// Represents a menu item. /// [Table("MenuItemRecord")] - public class MenuItemRecord : BaseEntity, ILocalizedEntity + public class MenuItemRecord : BaseEntity, ILocalizedEntity, IStoreMappingSupported, IAclSupported { /// /// Gets or sets the menu identifier. @@ -102,6 +104,12 @@ public class MenuItemRecord : BaseEntity, ILocalizedEntity [StringLength(10)] public string Style { get; set; } + /// + /// Gets or sets fontawesome icon color. + /// + [StringLength(100)] + public string IconColor { get; set; } + /// /// Gets or sets HTML id attribute. /// @@ -113,5 +121,17 @@ public class MenuItemRecord : BaseEntity, ILocalizedEntity /// [StringLength(100)] public string CssClass { get; set; } + + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores. + /// + [Index("IX_MenuItem_LimitedToStores")] + public bool LimitedToStores { get; set; } + + /// + /// Gets or sets a value indicating whether the entity is subject to ACL. + /// + [Index("IX_MenuItem_SubjectToAcl")] + public bool SubjectToAcl { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Cms/MenuRecord.cs b/src/Libraries/SmartStore.Core/Domain/Cms/MenuRecord.cs index c8ebedf1c1..19dfe6e581 100644 --- a/src/Libraries/SmartStore.Core/Domain/Cms/MenuRecord.cs +++ b/src/Libraries/SmartStore.Core/Domain/Cms/MenuRecord.cs @@ -22,8 +22,8 @@ public class MenuRecord : BaseEntity, ILocalizedEntity, IStoreMappingSupported, ///
public virtual ICollection Items { - get { return _items ?? (_items = new HashSet()); } - protected set { _items = value; } + get => _items ?? (_items = new HashSet()); + protected set => _items = value; } /// diff --git a/src/Libraries/SmartStore.Core/Domain/Cms/WidgetSettings.cs b/src/Libraries/SmartStore.Core/Domain/Cms/WidgetSettings.cs index 56aa6440a8..0284038e3e 100644 --- a/src/Libraries/SmartStore.Core/Domain/Cms/WidgetSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Cms/WidgetSettings.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; using SmartStore.Core.Configuration; - + namespace SmartStore.Core.Domain.Cms { public class WidgetSettings : ISettings diff --git a/src/Libraries/SmartStore.Core/Domain/Common/Address.cs b/src/Libraries/SmartStore.Core/Domain/Common/Address.cs index c4723d0e6e..ab85c7d04b 100644 --- a/src/Libraries/SmartStore.Core/Domain/Common/Address.cs +++ b/src/Libraries/SmartStore.Core/Domain/Common/Address.cs @@ -1,11 +1,11 @@ using System; -using SmartStore.Core.Domain.Directory; using System.Runtime.Serialization; +using SmartStore.Core.Domain.Directory; namespace SmartStore.Core.Domain.Common { - [DataContract] - public class Address : BaseEntity, ICloneable + [DataContract] + public class Address : BaseEntity, ICloneable { /// /// Gets or sets the first name @@ -23,91 +23,91 @@ public class Address : BaseEntity, ICloneable /// Gets or sets the first name /// [DataMember] - public string FirstName { get; set; } + public string FirstName { get; set; } /// /// Gets or sets the last name /// [DataMember] - public string LastName { get; set; } + public string LastName { get; set; } /// /// Gets or sets the email /// [DataMember] - public string Email { get; set; } + public string Email { get; set; } /// /// Gets or sets the company /// [DataMember] - public string Company { get; set; } + public string Company { get; set; } /// /// Gets or sets the country identifier /// [DataMember] - public int? CountryId { get; set; } + public int? CountryId { get; set; } /// /// Gets or sets the state/province identifier /// [DataMember] - public int? StateProvinceId { get; set; } - + public int? StateProvinceId { get; set; } + /// /// Gets or sets the city /// - [DataMember] - public string City { get; set; } + [DataMember] + public string City { get; set; } /// /// Gets or sets the address 1 /// [DataMember] - public string Address1 { get; set; } + public string Address1 { get; set; } /// /// Gets or sets the address 2 /// [DataMember] - public string Address2 { get; set; } + public string Address2 { get; set; } /// /// Gets or sets the zip/postal code /// [DataMember] - public string ZipPostalCode { get; set; } + public string ZipPostalCode { get; set; } /// /// Gets or sets the phone number /// [DataMember] - public string PhoneNumber { get; set; } + public string PhoneNumber { get; set; } /// /// Gets or sets the fax number /// [DataMember] - public string FaxNumber { get; set; } + public string FaxNumber { get; set; } /// /// Gets or sets the date and time of instance creation /// [DataMember] - public DateTime CreatedOnUtc { get; set; } - + public DateTime CreatedOnUtc { get; set; } + /// /// Gets or sets the country /// - [DataMember] - public virtual Country Country { get; set; } + [DataMember] + public virtual Country Country { get; set; } /// /// Gets or sets the state/province /// [DataMember] - public virtual StateProvince StateProvince { get; set; } + public virtual StateProvince StateProvince { get; set; } public object Clone() { @@ -134,17 +134,11 @@ public object Clone() return addr; } - public static string DefaultAddressFormat - { - get - { - return @"{{ Salutation }} {{ Title }} {{ FirstName }} {{ LastName }} + public static string DefaultAddressFormat => @"{{ Salutation }} {{ Title }} {{ FirstName }} {{ LastName }} {{ Company }} {{ Street1 }} {{ Street2 }} {{ ZipCode }} {{ City }} {{ Country | Upcase }}"; - } - } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Common/AddressSettings.cs b/src/Libraries/SmartStore.Core/Domain/Common/AddressSettings.cs index 6d39656c46..32eec46007 100644 --- a/src/Libraries/SmartStore.Core/Domain/Common/AddressSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Common/AddressSettings.cs @@ -1,31 +1,26 @@ using SmartStore.Core.Configuration; -using SmartStore.Core.Domain.Localization; namespace SmartStore.Core.Domain.Common { - public class AddressSettings : BaseEntity, ISettings, ILocalizedEntity + public class AddressSettings : ISettings { - public AddressSettings() - { - ValidateEmailAddress = false; - SalutationEnabled = false; - TitleEnabled = false; - CompanyEnabled = true; - StreetAddressEnabled = true; - StreetAddressRequired = true; - StreetAddress2Enabled = true; - ZipPostalCodeEnabled = true; - ZipPostalCodeRequired = true; - CityEnabled = true; - CityRequired = true; - CountryEnabled = true; - CountryRequired = true; - StateProvinceEnabled = true; - StateProvinceRequired = false; - PhoneEnabled = true; - PhoneRequired = true; - FaxEnabled = true; - } + public AddressSettings() + { + CompanyEnabled = true; + StreetAddressEnabled = true; + StreetAddressRequired = true; + StreetAddress2Enabled = true; + ZipPostalCodeEnabled = true; + ZipPostalCodeRequired = true; + CityEnabled = true; + CityRequired = true; + CountryEnabled = true; + CountryRequired = true; + StateProvinceEnabled = true; + PhoneEnabled = true; + PhoneRequired = true; + FaxEnabled = true; + } /// /// Gets or sets a value indicating whether email address should be validated @@ -47,7 +42,7 @@ public AddressSettings() /// public bool TitleEnabled { get; set; } - /// + /// /// Gets or sets a value indicating whether 'Company' is enabled /// public bool CompanyEnabled { get; set; } @@ -97,25 +92,25 @@ public AddressSettings() /// public bool CountryEnabled { get; set; } - /// - /// Gets or sets a value indicating whether 'Country' is required - /// - public bool CountryRequired { get; set; } - - /// - /// Gets or sets a value indicating whether 'State / province' is enabled - /// - public bool StateProvinceEnabled { get; set; } - - /// - /// Gets or sets a value indicating whether 'State / province' is required - /// - public bool StateProvinceRequired { get; set; } - - /// - /// Gets or sets a value indicating whether 'Phone number' is enabled - /// - public bool PhoneEnabled { get; set; } + /// + /// Gets or sets a value indicating whether 'Country' is required + /// + public bool CountryRequired { get; set; } + + /// + /// Gets or sets a value indicating whether 'State / province' is enabled + /// + public bool StateProvinceEnabled { get; set; } + + /// + /// Gets or sets a value indicating whether 'State / province' is required + /// + public bool StateProvinceRequired { get; set; } + + /// + /// Gets or sets a value indicating whether 'Phone number' is enabled + /// + public bool PhoneEnabled { get; set; } /// /// Gets or sets a value indicating whether 'Phone number' is required /// diff --git a/src/Libraries/SmartStore.Core/Domain/Common/AdminAreaSettings.cs b/src/Libraries/SmartStore.Core/Domain/Common/AdminAreaSettings.cs index 466c1bbe79..67c4aad894 100644 --- a/src/Libraries/SmartStore.Core/Domain/Common/AdminAreaSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Common/AdminAreaSettings.cs @@ -5,13 +5,13 @@ namespace SmartStore.Core.Domain.Common { public class AdminAreaSettings : ISettings { - public AdminAreaSettings() - { - GridPageSize = 25; - DisplayProductPictures = true; - } - - public int GridPageSize { get; set; } + public AdminAreaSettings() + { + GridPageSize = 25; + DisplayProductPictures = true; + } + + public int GridPageSize { get; set; } public bool DisplayProductPictures { get; set; } } diff --git a/src/Libraries/SmartStore.Core/Domain/Common/CommonSettings.cs b/src/Libraries/SmartStore.Core/Domain/Common/CommonSettings.cs index 523a5dbe52..eb4f050317 100644 --- a/src/Libraries/SmartStore.Core/Domain/Common/CommonSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Common/CommonSettings.cs @@ -4,19 +4,9 @@ namespace SmartStore.Core.Domain.Common { public class CommonSettings : ISettings { - public CommonSettings() - { - UseStoredProceduresIfSupported = true; - AutoUpdateEnabled = true; - EntityPickerPageSize = 48; - MaxScheduleHistoryAgeInDays = 30; - MaxNumberOfScheduleHistoryEntries = 100; - MaxQueuedMessagesAgeInDays = 14; - } - - public bool UseSystemEmailForContactUsForm { get; set; } - - public bool UseStoredProceduresIfSupported { get; set; } + public bool UseSystemEmailForContactUsForm { get; set; } + + public bool UseStoredProceduresIfSupported { get; set; } = true; public bool HideAdvertisementsOnAdminArea { get; set; } @@ -25,26 +15,36 @@ public CommonSettings() /// public bool DisplayJavaScriptDisabledWarning { get; set; } - public bool AutoUpdateEnabled { get; set; } + public bool AutoUpdateEnabled { get; set; } = true; - /// - /// Gets or sets the page size for the entity picker - /// - public int EntityPickerPageSize { get; set; } + /// + /// Gets or sets the page size for the entity picker + /// + public int EntityPickerPageSize { get; set; } = 48; /// /// Gets or sets the maximum age of schedule history entries (in days). /// - public int MaxScheduleHistoryAgeInDays { get; set; } + public int MaxScheduleHistoryAgeInDays { get; set; } = 30; + + /// + /// Gets or sets the maximum age of log entries (in days). + /// + public int MaxLogAgeInDays { get; set; } = 7; /// /// Gets or sets the maximum number of schedule history entries per task. /// - public int MaxNumberOfScheduleHistoryEntries { get; set; } + public int MaxNumberOfScheduleHistoryEntries { get; set; } = 100; /// /// Gets or sets the maximum age of sent queued messages (in days). /// - public int MaxQueuedMessagesAgeInDays { get; set; } + public int MaxQueuedMessagesAgeInDays { get; set; } = 14; + + /// + /// Gets or sets the maximum registration age (in minutes) for automatic deletion of guests customers. + /// + public int MaxGuestsRegistrationAgeInMinutes { get; set; } = 1440; // 1 day (60 * 24). } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Common/CompanyInformationSettings.cs b/src/Libraries/SmartStore.Core/Domain/Common/CompanyInformationSettings.cs index 9c6430e25a..1c46f410b2 100644 --- a/src/Libraries/SmartStore.Core/Domain/Common/CompanyInformationSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Common/CompanyInformationSettings.cs @@ -70,11 +70,6 @@ public class CompanyInformationSettings : ISettings ///
public string Region { get; set; } - /// - /// Gets or sets the country that will be used - /// - public string CountryName { get; set; } - /// /// Gets or sets the state that will be used /// diff --git a/src/Libraries/SmartStore.Core/Domain/Common/GenericAttribute.cs b/src/Libraries/SmartStore.Core/Domain/Common/GenericAttribute.cs index 931d8fbb51..4a8e6d5723 100644 --- a/src/Libraries/SmartStore.Core/Domain/Common/GenericAttribute.cs +++ b/src/Libraries/SmartStore.Core/Domain/Common/GenericAttribute.cs @@ -6,36 +6,36 @@ namespace SmartStore.Core.Domain.Common /// Represents a generic attribute /// [DataContract] - public partial class GenericAttribute : BaseEntity + public partial class GenericAttribute : BaseEntity { /// /// Gets or sets the entity identifier /// [DataMember] - public int EntityId { get; set; } - + public int EntityId { get; set; } + /// /// Gets or sets the key group /// - [DataMember] - public string KeyGroup { get; set; } + [DataMember] + public string KeyGroup { get; set; } /// /// Gets or sets the key /// [DataMember, Index("IX_GenericAttribute_Key")] - public string Key { get; set; } + public string Key { get; set; } /// /// Gets or sets the value /// [DataMember] - public string Value { get; set; } + public string Value { get; set; } - /// - /// Gets or sets the store identifier - /// - [DataMember] - public int StoreId { get; set; } + /// + /// Gets or sets the store identifier + /// + [DataMember] + public int StoreId { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Common/PdfSettings.cs b/src/Libraries/SmartStore.Core/Domain/Common/PdfSettings.cs index bddfb93963..c2abe7564a 100644 --- a/src/Libraries/SmartStore.Core/Domain/Common/PdfSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Common/PdfSettings.cs @@ -5,13 +5,13 @@ namespace SmartStore.Core.Domain.Common { public class PdfSettings : ISettings { - public PdfSettings() - { - Enabled = true; - RenderOrderNotes = true; - } - - /// + public PdfSettings() + { + Enabled = true; + RenderOrderNotes = true; + } + + /// /// Gets or sets a value indicating PDF is supported /// public bool Enabled { get; set; } @@ -31,14 +31,14 @@ public PdfSettings() /// public bool RenderOrderNotes { get; set; } - /// - /// Gets or sets a value indicating whether to attach the order PDF to 'Order Placed (customer)' email - /// - public bool AttachOrderPdfToOrderPlacedEmail { get; set; } + /// + /// Gets or sets a value indicating whether to attach the order PDF to 'Order Placed (customer)' email + /// + public bool AttachOrderPdfToOrderPlacedEmail { get; set; } - /// - /// Gets or sets a value indicating whether to attach the order PDF to 'Order Completed (customer)' email - /// - public bool AttachOrderPdfToOrderCompletedEmail { get; set; } + /// + /// Gets or sets a value indicating whether to attach the order PDF to 'Order Completed (customer)' email + /// + public bool AttachOrderPdfToOrderCompletedEmail { get; set; } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Common/PerformanceSettings.cs b/src/Libraries/SmartStore.Core/Domain/Common/PerformanceSettings.cs index 8acd3065db..13822d6f78 100644 --- a/src/Libraries/SmartStore.Core/Domain/Common/PerformanceSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Common/PerformanceSettings.cs @@ -1,34 +1,39 @@ -using System; -using SmartStore.Core.Configuration; +using SmartStore.Core.Configuration; namespace SmartStore.Core.Domain.Common { - public class PerformanceSettings : ISettings - { - /// - /// The number of entries in a single cache segment - /// when greedy loading is disabled. The larger the catalog, - /// the smaller this value should be. We recommend segment - /// size 500 for catalogs smaller than 100.000 items. - /// - public int CacheSegmentSize { get; set; } = 500; + public class PerformanceSettings : ISettings + { + /// + /// The number of entries in a single cache segment + /// when greedy loading is disabled. The larger the catalog, + /// the smaller this value should be. We recommend segment + /// size 500 for catalogs smaller than 100.000 items. + /// + public int CacheSegmentSize { get; set; } = 500; - /// - /// By default only instant search prefetches translations. - /// All other product listings work against the segmented cache. - /// In very large multilingual catalogs (> 500.000) setting this to true - /// can result in higher request performance as well as less - /// resource usage. - /// - public bool AlwaysPrefetchTranslations { get; set; } + /// + /// By default only instant search prefetches translations. + /// All other product listings work against the segmented cache. + /// In very large multilingual catalogs (> 500.000) setting this to true + /// can result in higher request performance as well as less + /// resource usage. + /// + public bool AlwaysPrefetchTranslations { get; set; } - /// - /// By default only instant search prefetches url slugs. - /// All other product listings work against the segmented cache. - /// In very large catalogs (> 500.000) setting this to true - /// can result in higher request performance as well as less - /// resource usage. - /// - public bool AlwaysPrefetchUrlSlugs { get; set; } - } + /// + /// By default only instant search prefetches url slugs. + /// All other product listings work against the segmented cache. + /// In very large catalogs (> 500.000) setting this to true + /// can result in higher request performance as well as less + /// resource usage. + /// + public bool AlwaysPrefetchUrlSlugs { get; set; } + + /// + /// Maximum number of attribute combinations to be loaded and parsed + /// to make them unavailable for selection on the product detail page. + /// + public int MaxUnavailableAttributeCombinations { get; set; } = 10000; + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Common/SocialSettings.cs b/src/Libraries/SmartStore.Core/Domain/Common/SocialSettings.cs index 1a45382602..773b2c914e 100644 --- a/src/Libraries/SmartStore.Core/Domain/Common/SocialSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Common/SocialSettings.cs @@ -4,6 +4,16 @@ namespace SmartStore.Core.Domain.Common { public class SocialSettings : ISettings { + /// + /// Gets or sets facebook app id + /// + public string FacebookAppId { get; set; } = null; + + /// + /// Gets or sets twitter account site name + /// + public string TwitterSite { get; set; } = null; + /// /// Gets or sets the value that determines whether social links should be show in the footer /// diff --git a/src/Libraries/SmartStore.Core/Domain/Configuration/Setting.cs b/src/Libraries/SmartStore.Core/Domain/Configuration/Setting.cs index 0c981c906a..1ae9d1e412 100644 --- a/src/Libraries/SmartStore.Core/Domain/Configuration/Setting.cs +++ b/src/Libraries/SmartStore.Core/Domain/Configuration/Setting.cs @@ -6,34 +6,34 @@ namespace SmartStore.Core.Domain.Configuration /// Represents a setting /// [DataContract] - [DebuggerDisplay("{Name}: {Value}")] - public partial class Setting : BaseEntity + [DebuggerDisplay("{Name}: {Value}")] + public partial class Setting : BaseEntity { public Setting() { } - public Setting(string name, string value, int storeId = 0) - { + public Setting(string name, string value, int storeId = 0) + { this.Name = name; this.Value = value; - this.StoreId = storeId; + this.StoreId = storeId; } - + /// /// Gets or sets the name /// - [DataMember] - public string Name { get; set; } + [DataMember] + public string Name { get; set; } /// /// Gets or sets the value /// [DataMember] - public string Value { get; set; } + public string Value { get; set; } - /// - /// Gets or sets the store for which this setting is valid. 0 is set when the setting is for all stores - /// - [DataMember] - public int StoreId { get; set; } + /// + /// Gets or sets the store for which this setting is valid. 0 is set when the setting is for all stores + /// + [DataMember] + public int StoreId { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/BestCustomerReportLine.cs b/src/Libraries/SmartStore.Core/Domain/Customers/BestCustomerReportLine.cs deleted file mode 100644 index b9ee4ca4ee..0000000000 --- a/src/Libraries/SmartStore.Core/Domain/Customers/BestCustomerReportLine.cs +++ /dev/null @@ -1,25 +0,0 @@ - -namespace SmartStore.Core.Domain.Customers -{ - - /// - /// Represents a best customer report line - /// - public partial class BestCustomerReportLine - { - /// - /// Gets or sets the customer identifier - /// - public int CustomerId { get; set; } - - /// - /// Gets or sets the order total - /// - public decimal OrderTotal { get; set; } - - /// - /// Gets or sets the order count - /// - public int OrderCount { get; set; } - } -} diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/Customer.cs b/src/Libraries/SmartStore.Core/Domain/Customers/Customer.cs index 0b641ae281..c0582bce24 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/Customer.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/Customer.cs @@ -13,23 +13,23 @@ namespace SmartStore.Core.Domain.Customers /// Represents a customer /// [DataContract] - public partial class Customer : BaseEntity, ISoftDeletable + public partial class Customer : BaseEntity, ISoftDeletable { private ICollection _externalAuthenticationRecords; private ICollection _customerContent; - private ICollection _customerRoles; + private ICollection _customerRoleMappings; private ICollection _shoppingCartItems; private ICollection _orders; private ICollection _rewardPointsHistory; - private ICollection _walletHistory; - private ICollection _returnRequests; + private ICollection _walletHistory; + private ICollection _returnRequests; private ICollection
_addresses; private ICollection _forumTopics; private ICollection _forumPosts; - /// - /// Ctor - /// + /// + /// Ctor + /// public Customer() { this.CustomerGuid = Guid.NewGuid(); @@ -42,40 +42,40 @@ public Customer() [DataMember] public Guid CustomerGuid { get; set; } - /// - /// Gets or sets the username - /// + /// + /// Gets or sets the username + /// [DataMember] public string Username { get; set; } - /// - /// Gets or sets the email - /// + /// + /// Gets or sets the email + /// [DataMember] public string Email { get; set; } - /// - /// Gets or sets the password - /// + /// + /// Gets or sets the password + /// public string Password { get; set; } - /// - /// Gets or sets the password format - /// + /// + /// Gets or sets the password format + /// public int PasswordFormatId { get; set; } - /// - /// Gets or sets the password format - /// + /// + /// Gets or sets the password format + /// public PasswordFormat PasswordFormat { - get { return (PasswordFormat)PasswordFormatId; } - set { this.PasswordFormatId = (int)value; } + get => (PasswordFormat)PasswordFormatId; + set => this.PasswordFormatId = (int)value; } - /// - /// Gets or sets the password salt - /// + /// + /// Gets or sets the password salt + /// public string PasswordSalt { get; set; } /// @@ -94,82 +94,85 @@ public PasswordFormat PasswordFormat /// Gets or sets the affiliate identifier /// [DataMember] - public int AffiliateId { get; set; } - + public int AffiliateId { get; set; } + /// /// Gets or sets a value indicating whether the customer is active /// - [DataMember] - public bool Active { get; set; } + [DataMember] + public bool Active { get; set; } /// /// Gets or sets a value indicating whether the customer has been deleted /// [Index] + [Index("IX_Customer_Deleted_IsSystemAccount", 1)] public bool Deleted { get; set; } /// /// Gets or sets a value indicating whether the customer account is system /// [DataMember] - public bool IsSystemAccount { get; set; } + [Index] + [Index("IX_Customer_Deleted_IsSystemAccount", 2)] + public bool IsSystemAccount { get; set; } /// /// Gets or sets the customer system name /// [DataMember] - [Index] - public string SystemName { get; set; } + [Index] + public string SystemName { get; set; } /// /// Gets or sets the last IP address /// [DataMember, Index("IX_Customer_LastIpAddress")] - public string LastIpAddress { get; set; } + public string LastIpAddress { get; set; } /// /// Gets or sets the date and time of entity creation /// [DataMember, Index("IX_Customer_CreatedOn")] - public DateTime CreatedOnUtc { get; set; } + public DateTime CreatedOnUtc { get; set; } /// /// Gets or sets the date and time of last login /// [DataMember] - public DateTime? LastLoginDateUtc { get; set; } + public DateTime? LastLoginDateUtc { get; set; } /// /// Gets or sets the date and time of last activity /// [DataMember, Index("IX_Customer_LastActivity")] - public DateTime LastActivityDateUtc { get; set; } + public DateTime LastActivityDateUtc { get; set; } - /// - /// For future use - /// - public string Salutation { get; set; } + /// + /// For future use + /// + public string Salutation { get; set; } - [DataMember] - public string Title { get; set; } + [DataMember] + public string Title { get; set; } - [DataMember] - public string FirstName { get; set; } + [DataMember] + public string FirstName { get; set; } - [DataMember] - public string LastName { get; set; } + [DataMember] + public string LastName { get; set; } - [DataMember, Index("IX_Customer_FullName")] - public string FullName { get; set; } + [DataMember, Index("IX_Customer_FullName")] + public string FullName { get; set; } - [DataMember, Index("IX_Customer_Company")] - public string Company { get; set; } + [DataMember, Index("IX_Customer_Company")] + public string Company { get; set; } - [DataMember, Index("IX_Customer_CustomerNumber")] - public string CustomerNumber { get; set; } + [DataMember, Index("IX_Customer_CustomerNumber")] + public string CustomerNumber { get; set; } - [DataMember, Index("IX_Customer_BirthDate")] - public DateTime? BirthDate { get; set; } + [DataMember, Index("IX_Customer_BirthDate")] + public DateTime? BirthDate { get; set; } [DataMember] public string Gender { get; set; } @@ -199,8 +202,8 @@ public PasswordFormat PasswordFormat /// public virtual ICollection ExternalAuthenticationRecords { - get { return _externalAuthenticationRecords ?? (_externalAuthenticationRecords = new HashSet()); } - protected set { _externalAuthenticationRecords = value; } + get => _externalAuthenticationRecords ?? (_externalAuthenticationRecords = new HashSet()); + protected set => _externalAuthenticationRecords = value; } /// @@ -208,18 +211,18 @@ public virtual ICollection ExternalAuthenticationR /// public virtual ICollection CustomerContent { - get { return _customerContent ?? (_customerContent = new HashSet()); } - protected set { _customerContent = value; } + get => _customerContent ?? (_customerContent = new HashSet()); + protected set => _customerContent = value; } - /// - /// Gets or sets the customer roles - /// - [DataMember] - public virtual ICollection CustomerRoles + /// + /// Gets or sets the customer role mappings. + /// + [DataMember] + public virtual ICollection CustomerRoleMappings { - get { return _customerRoles ?? (_customerRoles = new HashSet()); } - protected internal set { _customerRoles = value; } + get => _customerRoleMappings ?? (_customerRoleMappings = new HashSet()); + protected set => _customerRoleMappings = value; } /// @@ -227,74 +230,69 @@ public virtual ICollection CustomerRoles /// public virtual ICollection ShoppingCartItems { - get { return _shoppingCartItems ?? (_shoppingCartItems = new HashSet()); } - protected set { _shoppingCartItems = value; } + get => _shoppingCartItems ?? (_shoppingCartItems = new HashSet()); + protected set => _shoppingCartItems = value; } /// /// Gets or sets orders /// [DataMember] - public virtual ICollection Orders + public virtual ICollection Orders { - get { return _orders ?? (_orders = new HashSet()); } - protected internal set { _orders = value; } + get => _orders ?? (_orders = new HashSet()); + protected internal set => _orders = value; } /// /// Gets or sets reward points history /// + [DataMember] public virtual ICollection RewardPointsHistory { - get { return _rewardPointsHistory ?? (_rewardPointsHistory = new HashSet()); } - protected set { _rewardPointsHistory = value; } + get => _rewardPointsHistory ?? (_rewardPointsHistory = new HashSet()); + protected set => _rewardPointsHistory = value; } - /// - /// Gets or sets the wallet history. - /// - public virtual ICollection WalletHistory - { - get - { - return _walletHistory ?? (_walletHistory = new HashSet()); - } - protected set - { - _walletHistory = value; - } - } + /// + /// Gets or sets the wallet history. + /// + public virtual ICollection WalletHistory + { + get => _walletHistory ?? (_walletHistory = new HashSet()); + protected set => _walletHistory = value; + } /// /// Gets or sets return request of this customer /// [DataMember] - public virtual ICollection ReturnRequests + public virtual ICollection ReturnRequests { - get { return _returnRequests ?? (_returnRequests = new HashSet()); } - protected set { _returnRequests = value; } + get => _returnRequests ?? (_returnRequests = new HashSet()); + protected set => _returnRequests = value; } - + /// /// Default billing address /// - [DataMember] - public virtual Address BillingAddress { get; set; } + [DataMember] + public virtual Address BillingAddress { get; set; } /// /// Default shipping address /// [DataMember] - public virtual Address ShippingAddress { get; set; } + public virtual Address ShippingAddress { get; set; } /// /// Gets or sets customer addresses /// [DataMember] - public virtual ICollection
Addresses + public virtual ICollection
Addresses { - get { return _addresses ?? (_addresses = new HashSet
()); } - protected set { _addresses = value; } + get => _addresses ?? (_addresses = new HashSet
()); + protected set => _addresses = value; } /// @@ -302,8 +300,8 @@ public virtual ICollection
Addresses ///
public virtual ICollection ForumTopics { - get { return _forumTopics ?? (_forumTopics = new HashSet()); } - protected set { _forumTopics = value; } + get => _forumTopics ?? (_forumTopics = new HashSet()); + protected set => _forumTopics = value; } /// @@ -311,69 +309,83 @@ public virtual ICollection ForumTopics /// public virtual ICollection ForumPosts { - get { return _forumPosts ?? (_forumPosts = new HashSet()); } - protected set { _forumPosts = value; } + get => _forumPosts ?? (_forumPosts = new HashSet()); + protected set => _forumPosts = value; + } + + #endregion + + #region Utils + + + /// + /// Gets a string identifier for the customer's roles by joining all role ids + /// + /// true ignores all inactive roles + /// The identifier + public string GetRolesIdent(bool onlyActiveCustomerRoles = true) + { + return string.Join(",", GetRoleIds(onlyActiveCustomerRoles)); + } + + /// + /// Get identifiers of assigned customer roles. + /// + /// true ignores all inactive roles + /// Customer role identifiers. + public int[] GetRoleIds(bool onlyActiveCustomerRoles = true) + { + return CustomerRoleMappings + .Select(x => x.CustomerRole) + .Where(x => !onlyActiveCustomerRoles || x.Active) + .Select(x => x.Id) + .ToArray(); + } + + public virtual void RemoveAddress(Address address) + { + if (this.Addresses.Contains(address)) + { + if (this.BillingAddress == address) this.BillingAddress = null; + if (this.ShippingAddress == address) this.ShippingAddress = null; + + this.Addresses.Remove(address); + } + } + + public void AddRewardPointsHistoryEntry( + int points, + string message = "", + Order usedWithOrder = null, + decimal usedAmount = 0M) + { + int newPointsBalance = this.GetRewardPointsBalance() + points; + + var rewardPointsHistory = new RewardPointsHistory() + { + Customer = this, + UsedWithOrder = usedWithOrder, + Points = points, + PointsBalance = newPointsBalance, + UsedAmount = usedAmount, + Message = message, + CreatedOnUtc = DateTime.UtcNow + }; + + this.RewardPointsHistory.Add(rewardPointsHistory); + } + + /// + /// Gets reward points balance + /// + public int GetRewardPointsBalance() + { + int result = 0; + if (this.RewardPointsHistory.Count > 0) + result = this.RewardPointsHistory.OrderByDescending(rph => rph.CreatedOnUtc).ThenByDescending(rph => rph.Id).FirstOrDefault().PointsBalance; + return result; } - #endregion - - #region Utils - - - /// - /// Gets a string identifier for the customer's roles by joining all role ids - /// - /// true ignores all inactive roles - /// The identifier - public string GetRolesIdent(bool onlyActiveCustomerRoles = true) - { - return string.Join(",", this.CustomerRoles.Where(x => !onlyActiveCustomerRoles || x.Active).Select(x => x.Id)); - } - - public virtual void RemoveAddress(Address address) - { - if (this.Addresses.Contains(address)) - { - if (this.BillingAddress == address) this.BillingAddress = null; - if (this.ShippingAddress == address) this.ShippingAddress = null; - - this.Addresses.Remove(address); - } - } - - public void AddRewardPointsHistoryEntry( - int points, - string message = "", - Order usedWithOrder = null, - decimal usedAmount = 0M) - { - int newPointsBalance = this.GetRewardPointsBalance() + points; - - var rewardPointsHistory = new RewardPointsHistory() - { - Customer = this, - UsedWithOrder = usedWithOrder, - Points = points, - PointsBalance = newPointsBalance, - UsedAmount = usedAmount, - Message = message, - CreatedOnUtc = DateTime.UtcNow - }; - - this.RewardPointsHistory.Add(rewardPointsHistory); - } - - /// - /// Gets reward points balance - /// - public int GetRewardPointsBalance() - { - int result = 0; - if (this.RewardPointsHistory.Count > 0) - result = this.RewardPointsHistory.OrderByDescending(rph => rph.CreatedOnUtc).ThenByDescending(rph => rph.Id).FirstOrDefault().PointsBalance; - return result; - } - - #endregion - } + #endregion + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/CustomerContent.cs b/src/Libraries/SmartStore.Core/Domain/Customers/CustomerContent.cs index 1041f57d17..c3640a7a8c 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/CustomerContent.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/CustomerContent.cs @@ -3,46 +3,46 @@ namespace SmartStore.Core.Domain.Customers { - /// - /// Represents a customer generated content - /// - [DataContract] - public partial class CustomerContent : BaseEntity, IAuditable - { - /// - /// Gets or sets the customer identifier - /// - [DataMember] - public int CustomerId { get; set; } + /// + /// Represents a customer generated content + /// + [DataContract] + public partial class CustomerContent : BaseEntity, IAuditable + { + /// + /// Gets or sets the customer identifier + /// + [DataMember] + public int CustomerId { get; set; } - /// - /// Gets or sets the IP address - /// - [DataMember] - public string IpAddress { get; set; } + /// + /// Gets or sets the IP address + /// + [DataMember] + public string IpAddress { get; set; } - /// - /// Gets or sets a value indicating whether the content is approved - /// - [DataMember] - public bool IsApproved { get; set; } + /// + /// Gets or sets a value indicating whether the content is approved + /// + [DataMember] + public bool IsApproved { get; set; } - /// - /// Gets or sets the date and time of instance creation - /// - [DataMember] - public DateTime CreatedOnUtc { get; set; } + /// + /// Gets or sets the date and time of instance creation + /// + [DataMember] + public DateTime CreatedOnUtc { get; set; } - /// - /// Gets or sets the date and time of instance update - /// - [DataMember] - public DateTime UpdatedOnUtc { get; set; } + /// + /// Gets or sets the date and time of instance update + /// + [DataMember] + public DateTime UpdatedOnUtc { get; set; } - /// - /// Gets or sets the customer - /// - [DataMember] - public virtual Customer Customer { get; set; } + /// + /// Gets or sets the customer + /// + [DataMember] + public virtual Customer Customer { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/CustomerNameFormat.cs b/src/Libraries/SmartStore.Core/Domain/Customers/CustomerNameFormat.cs index c10aa440f4..c505353e62 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/CustomerNameFormat.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/CustomerNameFormat.cs @@ -17,13 +17,13 @@ public enum CustomerNameFormat /// Show full names /// ShowFullNames = 3, - /// - /// Show first name - /// - ShowFirstName = 4, - /// - /// Show shorted name and city - /// - ShowNameAndCity = 5 + /// + /// Show first name + /// + ShowFirstName = 4, + /// + /// Show shorted name and city + /// + ShowNameAndCity = 5 } } diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/CustomerRole.cs b/src/Libraries/SmartStore.Core/Domain/Customers/CustomerRole.cs index beb22d1ae3..f5f8e96436 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/CustomerRole.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/CustomerRole.cs @@ -1,67 +1,95 @@ using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; using System.Runtime.Serialization; using SmartStore.Core.Domain.Security; +using SmartStore.Rules.Domain; namespace SmartStore.Core.Domain.Customers { - /// - /// Represents a customer role - /// - [DataContract] - public partial class CustomerRole : BaseEntity + /// + /// Represents a customer role + /// + [DataContract] + public partial class CustomerRole : BaseEntity, IRulesContainer { - private ICollection _permissionRecords; + private ICollection _permissionRoleMappings; + private ICollection _ruleSets; - /// - /// Gets or sets the customer role name - /// - [DataMember] - public string Name { get; set; } + /// + /// Gets or sets the customer role name + /// + [DataMember] + public string Name { get; set; } + + /// + /// Gets or sets a value indicating whether the customer role is marked as free shiping + /// + [DataMember] + public bool FreeShipping { get; set; } + + /// + /// Gets or sets a value indicating whether the customer role is marked as tax exempt + /// + [DataMember] + public bool TaxExempt { get; set; } - /// - /// Gets or sets a value indicating whether the customer role is marked as free shiping - /// - [DataMember] - public bool FreeShipping { get; set; } + /// + /// Gets or sets the tax display type + /// + [DataMember] + public int? TaxDisplayType { get; set; } - /// - /// Gets or sets a value indicating whether the customer role is marked as tax exempt - /// - [DataMember] - public bool TaxExempt { get; set; } + /// + /// Gets or sets a value indicating whether the customer role is active + /// + [DataMember] + [Index] + public bool Active { get; set; } - /// - /// Gets or sets the tax display type - /// - [DataMember] - public int? TaxDisplayType { get; set; } + /// + /// Gets or sets a value indicating whether the customer role is system + /// + [DataMember] + [Index] + [Index("IX_CustomerRole_SystemName_IsSystemRole", 2)] + public bool IsSystemRole { get; set; } - /// - /// Gets or sets a value indicating whether the customer role is active - /// - [DataMember] - public bool Active { get; set; } + /// + /// Gets or sets the customer role system name + /// + [DataMember] + [Index] + [Index("IX_CustomerRole_SystemName_IsSystemRole", 1)] + public string SystemName { get; set; } - /// - /// Gets or sets a value indicating whether the customer role is system - /// - [DataMember] - public bool IsSystemRole { get; set; } + /// + /// Gets or sets a minimum order amount + /// + [DataMember] + public decimal? OrderTotalMinimum { get; set; } - /// - /// Gets or sets the customer role system name - /// - [DataMember] - public string SystemName { get; set; } + /// + /// Gets or sets a maximum order amount + /// + [DataMember] + public decimal? OrderTotalMaximum { get; set; } + /// + /// Gets or sets the permission role mappings. + /// + public virtual ICollection PermissionRoleMappings + { + get => _permissionRoleMappings ?? (_permissionRoleMappings = new HashSet()); + protected set => _permissionRoleMappings = value; + } /// - /// Gets or sets the permission records + /// Gets or sets assigned rule sets. /// - public virtual ICollection PermissionRecords + public virtual ICollection RuleSets { - get { return _permissionRecords ?? (_permissionRecords = new HashSet()); } - protected set { _permissionRecords = value; } + get => _ruleSets ?? (_ruleSets = new HashSet()); + protected set => _ruleSets = value; } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/CustomerRoleMapping.cs b/src/Libraries/SmartStore.Core/Domain/Customers/CustomerRoleMapping.cs new file mode 100644 index 0000000000..18ff112076 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Customers/CustomerRoleMapping.cs @@ -0,0 +1,46 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.Runtime.Serialization; + +namespace SmartStore.Core.Domain.Customers +{ + /// + /// Represents a customer to customer role mapping. + /// + [DataContract] + [Table("CustomerRoleMapping")] + public partial class CustomerRoleMapping : BaseEntity + { + /// + /// Gets or sets the customer identifier. + /// + [DataMember] + public int CustomerId { get; set; } + + /// + /// Gets or sets the customer role identifier. + /// + [DataMember] + public int CustomerRoleId { get; set; } + + /// + /// Indicates whether the mapping is created by the user or by the system. + /// + [DataMember] + [Index] + public bool IsSystemMapping { get; set; } + + /// + /// Gets or sets the customer. + /// + [DataMember] + [ForeignKey("CustomerId")] + public virtual Customer Customer { get; set; } + + /// + /// Gets or sets the customer role. + /// + [DataMember] + [ForeignKey("CustomerRoleId")] + public virtual CustomerRole CustomerRole { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/CustomerSettings.cs b/src/Libraries/SmartStore.Core/Domain/Customers/CustomerSettings.cs index 20033f0c21..0e4c01cbd5 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/CustomerSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/CustomerSettings.cs @@ -2,29 +2,29 @@ namespace SmartStore.Core.Domain.Customers { - public class CustomerSettings : ISettings + public class CustomerSettings : ISettings { - public CustomerSettings() - { - CustomerLoginType = CustomerLoginType.UsernameOrEmail; + public CustomerSettings() + { + CustomerLoginType = CustomerLoginType.UsernameOrEmail; CustomerNumberMethod = CustomerNumberMethod.Disabled; CustomerNumberVisibility = CustomerNumberVisibility.None; - DefaultPasswordFormat = PasswordFormat.Hashed; - HashedPasswordFormat = "SHA1"; - PasswordMinLength = 6; - UserRegistrationType = UserRegistrationType.Standard; - AvatarMaximumSizeBytes = 512000; - CustomerNameFormat = CustomerNameFormat.ShowFirstName; - CustomerNameFormatMaxLength = 64; - GenderEnabled = true; - DateOfBirthEnabled = true; - CompanyEnabled = true; - NewsletterEnabled = true; - OnlineCustomerMinutes = 20; - StoreLastVisitedPage = true; - FirstNameRequired = false; - LastNameRequired = false; - } + DefaultPasswordFormat = PasswordFormat.Hashed; + HashedPasswordFormat = "SHA1"; + PasswordMinLength = 6; + UserRegistrationType = UserRegistrationType.Standard; + AvatarMaximumSizeBytes = 512000; + CustomerNameFormat = CustomerNameFormat.ShowFirstName; + CustomerNameFormatMaxLength = 64; + GenderEnabled = true; + DateOfBirthEnabled = true; + CompanyEnabled = true; + NewsletterEnabled = true; + OnlineCustomerMinutes = 20; + StoreLastVisitedPage = true; + FirstNameRequired = false; + LastNameRequired = false; + } /// /// Gets or sets a value indicating the customer login type @@ -62,10 +62,25 @@ public CustomerSettings() public string HashedPasswordFormat { get; set; } /// - /// Gets or sets a minimum password length + /// Gets or sets a minimum password length. /// public int PasswordMinLength { get; set; } + /// + /// Specifies the minimum number of digits for a password. + /// + public int MinDigitsInPassword { get; set; } + + /// + /// Specifies the minimum number of special characters for a password (not numeric and not alphabetic). + /// + public int MinSpecialCharsInPassword { get; set; } + + /// + /// Specifies the minimum number of uppercase characters for a password. + /// + public int MinUppercaseCharsInPassword { get; set; } + /// /// User registration type /// @@ -100,7 +115,7 @@ public CustomerSettings() /// Gets or sets a value indicating whether 'New customer' notification message should be sent to a store owner /// public bool NotifyNewCustomerRegistration { get; set; } - + /// /// Gets or sets a value indicating whether to hide 'Downloable products' tab on 'My account' page /// @@ -121,10 +136,10 @@ public CustomerSettings() /// public CustomerNameFormat CustomerNameFormat { get; set; } - /// - /// Gets or sets a value indicating the maximum length of a formatted customer name - /// - public int CustomerNameFormatMaxLength { get; set; } + /// + /// Gets or sets a value indicating the maximum length of a formatted customer name + /// + public int CustomerNameFormatMaxLength { get; set; } /// /// Gets or sets a value indicating whether 'Newsletter' form field is enabled @@ -145,7 +160,7 @@ public CustomerSettings() /// Gets or sets a value indicating we should store last visited page URL for each customer /// public bool StoreLastVisitedPage { get; set; } - + #region Form fields /// @@ -158,27 +173,27 @@ public CustomerSettings() /// public bool TitleEnabled { get; set; } - /// - /// Gets or sets a value indicating whether 'FirstName' is required - /// - public bool FirstNameRequired { get; set; } + /// + /// Gets or sets a value indicating whether 'FirstName' is required + /// + public bool FirstNameRequired { get; set; } - /// - /// Gets or sets a value indicating whether 'LastName' is required - /// - public bool LastNameRequired { get; set; } + /// + /// Gets or sets a value indicating whether 'LastName' is required + /// + public bool LastNameRequired { get; set; } - /// - /// Gets or sets a value indicating whether 'Date of Birth' is enabled - /// - public bool DateOfBirthEnabled { get; set; } + /// + /// Gets or sets a value indicating whether 'Date of Birth' is enabled + /// + public bool DateOfBirthEnabled { get; set; } /// /// Gets or sets a value indicating whether 'Company' is enabled /// public bool CompanyEnabled { get; set; } - /// + /// /// Gets or sets a value indicating whether 'Company' is required /// public bool CompanyRequired { get; set; } @@ -187,7 +202,7 @@ public CustomerSettings() /// Gets or sets a value indicating whether 'Street address' is enabled /// public bool StreetAddressEnabled { get; set; } - + /// /// Gets or sets a value indicating whether 'Street address' is required /// @@ -233,6 +248,11 @@ public CustomerSettings() /// public bool StateProvinceEnabled { get; set; } + /// + /// Gets or sets a value indicating whether 'StateProvince' is required + /// + public bool StateProvinceRequired { get; set; } + /// /// Gets or sets a value indicating whether 'Phone number' is enabled /// @@ -255,9 +275,9 @@ public CustomerSettings() #endregion - /// - /// Identifier of a customer role that new registered customers will be assigned to - /// - public int RegisterCustomerRoleId { get; set; } - } + /// + /// Identifier of a customer role that new registered customers will be assigned to + /// + public int RegisterCustomerRoleId { get; set; } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/ExternalAuthenticationRecord.cs b/src/Libraries/SmartStore.Core/Domain/Customers/ExternalAuthenticationRecord.cs index 2a17563b33..d64523cb89 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/ExternalAuthenticationRecord.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/ExternalAuthenticationRecord.cs @@ -39,7 +39,7 @@ public partial class ExternalAuthenticationRecord : BaseEntity /// Gets or sets the provider /// public string ProviderSystemName { get; set; } - + /// /// Gets or sets the customer /// diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/ExternalAuthenticationSettings.cs b/src/Libraries/SmartStore.Core/Domain/Customers/ExternalAuthenticationSettings.cs index dc1b56b566..58aef070ee 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/ExternalAuthenticationSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/ExternalAuthenticationSettings.cs @@ -9,7 +9,7 @@ public ExternalAuthenticationSettings() { ActiveAuthenticationMethodSystemNames = new List(); } - + public bool AutoRegisterEnabled { get; set; } /// /// Gets or sets an system names of active payment methods diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/PasswordFormat.cs b/src/Libraries/SmartStore.Core/Domain/Customers/PasswordFormat.cs index 4b35797ec7..32a36b8ade 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/PasswordFormat.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/PasswordFormat.cs @@ -1,6 +1,6 @@  -namespace SmartStore.Core.Domain.Customers -{ +namespace SmartStore.Core.Domain.Customers +{ public enum PasswordFormat { Clear = 0, diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/PrivacySettings.cs b/src/Libraries/SmartStore.Core/Domain/Customers/PrivacySettings.cs index 98f362aece..111f52de58 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/PrivacySettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/PrivacySettings.cs @@ -1,47 +1,47 @@ using SmartStore.Core.Configuration; -using SmartStore.Core.Domain.Localization; -using SmartStore.Core.Domain.Orders; namespace SmartStore.Core.Domain.Customers { - public class PrivacySettings : BaseEntity, ISettings, ILocalizedEntity - { - public PrivacySettings() - { - EnableCookieConsent = true; - StoreLastIpAddress = false; - DisplayGdprConsentOnForms = true; - FullNameOnContactUsRequired = false; - } - - /// - /// Specifies whether cookie hint and consent will be displayed to customers in the frontent - /// - public bool EnableCookieConsent { get; set; } - - /// - /// Specifies the text which will be display to customers using the frontend - /// - public string CookieConsentBadgetext { get; set; } - - /// - /// Gets or sets a value indicating whether to store last IP address for each customer - /// - public bool StoreLastIpAddress { get; set; } - - /// - /// Gets or sets a value indicating whether to display a checkbox to the customer where he can agree to privacy terms - /// - public bool DisplayGdprConsentOnForms { get; set; } - - /// - /// Gets or sets a value indicating whether the full name field is required on contact us requests - /// - public bool FullNameOnContactUsRequired { get; set; } - - /// - /// Gets or sets a value indicating whether the full name field is required on product requests - /// - public bool FullNameOnProductRequestRequired { get; set; } - } + public class PrivacySettings : ISettings + { + /// + /// Specifies whether cookie hint and consent will be displayed to customers in the frontent + /// + public bool EnableCookieConsent { get; set; } = true; + + /// + /// Specifies whether the cookie dialog will be display in a modal dialog + /// + public bool ModalCookieConsent { get; set; } = true; + + /// + /// Defines the global SameSiteMode for cookies. + /// + public SameSiteType SameSiteMode { get; set; } = SameSiteType.Lax; + + /// + /// Gets or sets a value indicating whether to store last IP address for each customer + /// + public bool StoreLastIpAddress { get; set; } + + /// + /// Gets or sets a value indicating whether to display a checkbox to the customer where he can agree to privacy terms + /// + public bool DisplayGdprConsentOnForms { get; set; } = true; + + /// + /// Gets or sets a value indicating whether the full name field is required on contact us requests + /// + public bool FullNameOnContactUsRequired { get; set; } + + /// + /// Gets or sets a value indicating whether the full name field is required on product requests + /// + public bool FullNameOnProductRequestRequired { get; set; } + + /// + /// Gets or sets cookie infos (JSON serialized) + /// + public string CookieInfos { get; set; } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/RewardPointsHistory.cs b/src/Libraries/SmartStore.Core/Domain/Customers/RewardPointsHistory.cs index a0fdc20f1d..f4ce336430 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/RewardPointsHistory.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/RewardPointsHistory.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.Serialization; using SmartStore.Core.Domain.Orders; namespace SmartStore.Core.Domain.Customers @@ -6,46 +7,55 @@ namespace SmartStore.Core.Domain.Customers /// /// Represents a reward point history entry /// + [DataContract] public partial class RewardPointsHistory : BaseEntity { /// /// Gets or sets the customer identifier /// + [DataMember] public int CustomerId { get; set; } /// /// Gets or sets the points redeemed/added /// + [DataMember] public int Points { get; set; } /// /// Gets or sets the points balance /// + [DataMember] public int PointsBalance { get; set; } /// /// Gets or sets the used amount /// + [DataMember] public decimal UsedAmount { get; set; } /// /// Gets or sets the message /// + [DataMember] public string Message { get; set; } /// /// Gets or sets the date and time of instance creation /// + [DataMember] public DateTime CreatedOnUtc { get; set; } /// /// Gets or sets the order for which points were redeemed as a payment /// + [DataMember] public virtual Order UsedWithOrder { get; set; } /// /// Gets or sets the customer /// + [DataMember] public virtual Customer Customer { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/RewardPointsSettings.cs b/src/Libraries/SmartStore.Core/Domain/Customers/RewardPointsSettings.cs index f0e99e9502..543a1ffd86 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/RewardPointsSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/RewardPointsSettings.cs @@ -5,16 +5,16 @@ namespace SmartStore.Core.Domain.Customers { public class RewardPointsSettings : ISettings { - public RewardPointsSettings() - { - ExchangeRate = 1; - PointsForPurchases_Amount = 10; - PointsForPurchases_Points = 1; - PointsForPurchases_Awarded = OrderStatus.Complete; - PointsForPurchases_Canceled = OrderStatus.Cancelled; - } - - /// + public RewardPointsSettings() + { + ExchangeRate = 1; + PointsForPurchases_Amount = 10; + PointsForPurchases_Points = 1; + PointsForPurchases_Awarded = OrderStatus.Complete; + PointsForPurchases_Canceled = OrderStatus.Cancelled; + } + + /// /// Gets or sets a value indicating whether Reward Points Program is enabled /// public bool Enabled { get; set; } @@ -24,20 +24,20 @@ public RewardPointsSettings() /// public decimal ExchangeRate { get; set; } - /// - /// Gets or sets a value whether to round down reward points - /// - public bool RoundDownRewardPoints { get; set; } + /// + /// Gets or sets a value whether to round down reward points + /// + public bool RoundDownRewardPoints { get; set; } - /// - /// Gets or sets a number of points awarded for registration - /// - public int PointsForRegistration { get; set; } + /// + /// Gets or sets a number of points awarded for registration + /// + public int PointsForRegistration { get; set; } - /// - /// Gets or sets a number of points awarded for a product review - /// - public int PointsForProductReview { get; set; } + /// + /// Gets or sets a number of points awarded for a product review + /// + public int PointsForProductReview { get; set; } /// /// Gets or sets a number of points awarded for purchases (amount in primary store currency) @@ -48,7 +48,7 @@ public RewardPointsSettings() /// Gets or sets a number of points awarded for purchases /// public int PointsForPurchases_Points { get; set; } - + /// /// Points are awarded when the order status is /// diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/SameSiteType.cs b/src/Libraries/SmartStore.Core/Domain/Customers/SameSiteType.cs new file mode 100644 index 0000000000..de98665704 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Customers/SameSiteType.cs @@ -0,0 +1,28 @@ +namespace SmartStore.Core.Domain.Customers +{ + /// + /// Represents the SameSiteMode for Cookies + /// + public enum SameSiteType + { + /// + /// No SameSite header should be included on requests. + /// + Unspecified = -1, + + /// + /// No mode specified. This setting must be used if the shop runs also in an iframe (SSL must be configured correctly for this). + /// + None = 0, + + /// + /// Cookies are sent with requests on the same website and with cross-site navigation at the highest level. + /// + Lax = 1, + + /// + /// If the value is "Strict" or invalid, cookies will only be sent to requests on the same website. + /// + Strict = 2 + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/SystemCustomerAttributeNames.cs b/src/Libraries/SmartStore.Core/Domain/Customers/SystemCustomerAttributeNames.cs index 2e19a06b50..4a6b1e4c46 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/SystemCustomerAttributeNames.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/SystemCustomerAttributeNames.cs @@ -4,44 +4,44 @@ namespace SmartStore.Core.Domain.Customers public static partial class SystemCustomerAttributeNames { // Form fields - public static string StreetAddress { get { return "StreetAddress"; } } - public static string StreetAddress2 { get { return "StreetAddress2"; } } - public static string ZipPostalCode { get { return "ZipPostalCode"; } } - public static string City { get { return "City"; } } - public static string StateProvinceId { get { return "StateProvinceId"; } } - public static string CountryId { get { return "CountryId"; } } - public static string Phone { get { return "Phone"; } } - public static string Fax { get { return "Fax"; } } - public static string VatNumber { get { return "VatNumber"; } } + public static string StreetAddress => "StreetAddress"; + public static string StreetAddress2 => "StreetAddress2"; + public static string ZipPostalCode => "ZipPostalCode"; + public static string City => "City"; + public static string StateProvinceId => "StateProvinceId"; + public static string CountryId => "CountryId"; + public static string Phone => "Phone"; + public static string Fax => "Fax"; + public static string VatNumber => "VatNumber"; // Other attributes - public static string DiscountCouponCode { get { return "DiscountCouponCode"; } } - public static string GiftCardCouponCodes { get { return "GiftCardCouponCodes"; } } - public static string CheckoutAttributes { get { return "CheckoutAttributes"; } } - public static string AvatarPictureId { get { return "AvatarPictureId"; } } - public static string AvatarColor { get { return "AvatarColor"; } } - public static string ForumPostCount { get { return "ForumPostCount"; } } - public static string Signature { get { return "Signature"; } } - public static string PasswordRecoveryToken { get { return "PasswordRecoveryToken"; } } - public static string AccountActivationToken { get { return "AccountActivationToken"; } } - public static string LastVisitedPage { get { return "LastVisitedPage"; } } - public static string ImpersonatedCustomerId { get { return "ImpersonatedCustomerId"; } } - public static string AdminAreaStoreScopeConfiguration { get { return "AdminAreaStoreScopeConfiguration"; } } - public static string MostRecentlyUsedCategories { get { return "MostRecentlyUsedCategories"; } } - public static string MostRecentlyUsedManufacturers { get { return "MostRecentlyUsedManufacturers"; } } - public static string WalletEnabled { get { return "WalletEnabled"; } } - public static string HasConsentedToGdpr { get { return "HasConsentedToGdpr"; } } + public static string DiscountCouponCode => "DiscountCouponCode"; + public static string GiftCardCouponCodes => "GiftCardCouponCodes"; + public static string CheckoutAttributes => "CheckoutAttributes"; + public static string AvatarPictureId => "AvatarPictureId"; + public static string AvatarColor => "AvatarColor"; + public static string ForumPostCount => "ForumPostCount"; + public static string Signature => "Signature"; + public static string PasswordRecoveryToken => "PasswordRecoveryToken"; + public static string AccountActivationToken => "AccountActivationToken"; + public static string LastVisitedPage => "LastVisitedPage"; + public static string ImpersonatedCustomerId => "ImpersonatedCustomerId"; + public static string AdminAreaStoreScopeConfiguration => "AdminAreaStoreScopeConfiguration"; + public static string MostRecentlyUsedCategories => "MostRecentlyUsedCategories"; + public static string MostRecentlyUsedManufacturers => "MostRecentlyUsedManufacturers"; + public static string WalletEnabled => "WalletEnabled"; + public static string HasConsentedToGdpr => "HasConsentedToGdpr"; // Depends on store - public static string CurrencyId { get { return "CurrencyId"; } } - public static string LanguageId { get { return "LanguageId"; } } - public static string SelectedPaymentMethod { get { return "SelectedPaymentMethod"; } } - public static string SelectedShippingOption { get { return "SelectedShippingOption"; } } - public static string OfferedShippingOptions { get { return "OfferedShippingOptions"; } } - public static string LastContinueShoppingPage { get { return "LastContinueShoppingPage"; } } - public static string NotifiedAboutNewPrivateMessages { get { return "NotifiedAboutNewPrivateMessages"; } } - public static string WorkingThemeName { get { return "WorkingThemeName"; } } - public static string UseRewardPointsDuringCheckout { get { return "UseRewardPointsDuringCheckout"; } } - public static string UseCreditBalanceDuringCheckout { get { return "UseCreditBalanceDuringCheckout"; } } - } + public static string CurrencyId => "CurrencyId"; + public static string LanguageId => "LanguageId"; + public static string SelectedPaymentMethod => "SelectedPaymentMethod"; + public static string SelectedShippingOption => "SelectedShippingOption"; + public static string OfferedShippingOptions => "OfferedShippingOptions"; + public static string LastContinueShoppingPage => "LastContinueShoppingPage"; + public static string NotifiedAboutNewPrivateMessages => "NotifiedAboutNewPrivateMessages"; + public static string WorkingThemeName => "WorkingThemeName"; + public static string UseRewardPointsDuringCheckout => "UseRewardPointsDuringCheckout"; + public static string UseCreditBalanceDuringCheckout => "UseCreditBalanceDuringCheckout"; + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/SystemCustomerNames.cs b/src/Libraries/SmartStore.Core/Domain/Customers/SystemCustomerNames.cs index 48ed1b0dfd..5e52629940 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/SystemCustomerNames.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/SystemCustomerNames.cs @@ -3,8 +3,8 @@ namespace SmartStore.Core.Domain.Customers { public static partial class SystemCustomerNames { - public static string SearchEngine { get { return "SearchEngine"; } } - public static string BackgroundTask { get { return "BackgroundTask"; } } - public static string PdfConverter { get { return "PdfConverter"; } } + public static string SearchEngine => "SearchEngine"; + public static string BackgroundTask => "BackgroundTask"; + public static string PdfConverter => "PdfConverter"; } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/SystemCustomerRoleNames.cs b/src/Libraries/SmartStore.Core/Domain/Customers/SystemCustomerRoleNames.cs index 214fd18191..c70a4b84ab 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/SystemCustomerRoleNames.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/SystemCustomerRoleNames.cs @@ -3,14 +3,14 @@ namespace SmartStore.Core.Domain.Customers { public static partial class SystemCustomerRoleNames { - public static string SuperAdministrators { get { return "SuperAdmins"; } } + public static string SuperAdministrators => "SuperAdmins"; - public static string Administrators { get { return "Administrators"; } } - - public static string ForumModerators { get { return "ForumModerators"; } } + public static string Administrators => "Administrators"; - public static string Registered { get { return "Registered"; } } + public static string ForumModerators => "ForumModerators"; - public static string Guests { get { return "Guests"; } } + public static string Registered => "Registered"; + + public static string Guests => "Guests"; } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/TopCustomerReportLine.cs b/src/Libraries/SmartStore.Core/Domain/Customers/TopCustomerReportLine.cs new file mode 100644 index 0000000000..1859174b8e --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Customers/TopCustomerReportLine.cs @@ -0,0 +1,25 @@ + +namespace SmartStore.Core.Domain.Customers +{ + + /// + /// Represents a best customer report line + /// + public partial class TopCustomerReportLine + { + /// + /// Gets or sets the customer identifier + /// + public int CustomerId { get; set; } + + /// + /// Gets or sets the order total + /// + public decimal OrderTotal { get; set; } + + /// + /// Gets or sets the order count + /// + public int OrderCount { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/WalletHistory.cs b/src/Libraries/SmartStore.Core/Domain/Customers/WalletHistory.cs index d85437c4f7..4befa9f0d9 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/WalletHistory.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/WalletHistory.cs @@ -4,71 +4,71 @@ namespace SmartStore.Core.Domain.Customers { - /// - /// Represents a digital wallet history entry. - /// - public class WalletHistory : BaseEntity - { - /// - /// Gets or sets the store identifier. Should not be zero. - /// - [Index("IX_StoreId_CreatedOn", 0)] - public int StoreId { get; set; } + /// + /// Represents a digital wallet history entry. + /// + public class WalletHistory : BaseEntity + { + /// + /// Gets or sets the store identifier. Should not be zero. + /// + [Index("IX_StoreId_CreatedOn", 0)] + public int StoreId { get; set; } - /// - /// Gets or sets the customer identifier. - /// - public int CustomerId { get; set; } + /// + /// Gets or sets the customer identifier. + /// + public int CustomerId { get; set; } - /// - /// Gets or sets the order identifier. - /// - public int? OrderId { get; set; } + /// + /// Gets or sets the order identifier. + /// + public int? OrderId { get; set; } - /// - /// Gets or sets the amount of the entry. - /// - public decimal Amount { get; set; } + /// + /// Gets or sets the amount of the entry. + /// + public decimal Amount { get; set; } - /// - /// Gets or sets the amount balance when the entry was created. - /// - public decimal AmountBalance { get; set; } + /// + /// Gets or sets the amount balance when the entry was created. + /// + public decimal AmountBalance { get; set; } - /// - /// Gets or sets the amount balance per store when the entry was created. - /// - public decimal AmountBalancePerStore { get; set; } + /// + /// Gets or sets the amount balance per store when the entry was created. + /// + public decimal AmountBalancePerStore { get; set; } - /// - /// Gets or sets the date ehen the entry was created (in UTC). - /// - [Index("IX_StoreId_CreatedOn", 1)] - public DateTime CreatedOnUtc { get; set; } + /// + /// Gets or sets the date ehen the entry was created (in UTC). + /// + [Index("IX_StoreId_CreatedOn", 1)] + public DateTime CreatedOnUtc { get; set; } - /// - /// Gets or sets the reason for posting this entry. - /// - public WalletPostingReason? Reason { get; set; } + /// + /// Gets or sets the reason for posting this entry. + /// + public WalletPostingReason? Reason { get; set; } - /// - /// Gets or sets the message. - /// - public string Message { get; set; } + /// + /// Gets or sets the message. + /// + public string Message { get; set; } - /// - /// Gets or sets the admin comment. - /// - public string AdminComment { get; set; } + /// + /// Gets or sets the admin comment. + /// + public string AdminComment { get; set; } - /// - /// Gets or sets the customer. - /// - public virtual Customer Customer { get; set; } + /// + /// Gets or sets the customer. + /// + public virtual Customer Customer { get; set; } - /// - /// Gets or sets the order for which the wallet entry was used. - /// - public virtual Order Order { get; set; } - } + /// + /// Gets or sets the order for which the wallet entry was used. + /// + public virtual Order Order { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Customers/WalletPostingReason.cs b/src/Libraries/SmartStore.Core/Domain/Customers/WalletPostingReason.cs index 06a829dba8..489bdac561 100644 --- a/src/Libraries/SmartStore.Core/Domain/Customers/WalletPostingReason.cs +++ b/src/Libraries/SmartStore.Core/Domain/Customers/WalletPostingReason.cs @@ -1,38 +1,38 @@ namespace SmartStore.Core.Domain.Customers { - /// - /// Represents the reason for creating a wallet history entry. - /// - public enum WalletPostingReason - { - /// - /// Any administration reason. - /// - Admin = 0, + /// + /// Represents the reason for creating a wallet history entry. + /// + public enum WalletPostingReason + { + /// + /// Any administration reason. + /// + Admin = 0, - /// - /// The Customer has purchased goods which have been paid in part or in full by wallet. - /// - Purchase, + /// + /// The Customer has purchased goods which have been paid in part or in full by wallet. + /// + Purchase, - /// - /// The customer has bought wallet credits. - /// - Refill, + /// + /// The customer has bought wallet credits. + /// + Refill, - /// - /// The admin has refunded the used credit balance. - /// - Refund, + /// + /// The admin has refunded the used credit balance. + /// + Refund, - /// - /// The admin has refunded a part of the used credit balance. - /// - PartialRefund, + /// + /// The admin has refunded a part of the used credit balance. + /// + PartialRefund, - /// - /// The admin has debited the wallet, e.g. because the purchase of credit was cancelled. - /// - Debit - } + /// + /// The admin has debited the wallet, e.g. because the purchase of credit was cancelled. + /// + Debit + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Dashboard/OrderDataPoint.cs b/src/Libraries/SmartStore.Core/Domain/Dashboard/OrderDataPoint.cs new file mode 100644 index 0000000000..bdd8927f4f --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Dashboard/OrderDataPoint.cs @@ -0,0 +1,35 @@ +using System; + +namespace SmartStore.Core.Domain.Dashboard +{ + /// + /// Represents a order chart data point + /// + public partial class OrderDataPoint + { + /// + /// DateTime entity was created (Utc) + /// + public DateTime CreatedOn { get; set; } + + /// + /// Total amount + /// + public decimal OrderTotal { get; set; } + + /// + /// Order status id + /// + public int OrderStatusId { get; set; } + + /// + /// Payment status + /// + public int PaymentStatusId { get; set; } + + /// + /// Shipping status + /// + public int ShippingStatusId { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/DataExchangeEnums.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/DataExchangeEnums.cs index 7a3ab1f8ae..7872dc0072 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/DataExchangeEnums.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/DataExchangeEnums.cs @@ -1,26 +1,26 @@ namespace SmartStore.Core.Domain.DataExchange { - public delegate void ProgressValueSetter(int value, int maximum, string message); + public delegate void ProgressValueSetter(int value, int maximum, string message); - /// - /// Data exchange abortion types - /// - public enum DataExchangeAbortion - { - /// - /// No abortion. Go on with processing. - /// - None = 0, + /// + /// Data exchange abortion types + /// + public enum DataExchangeAbortion + { + /// + /// No abortion. Go on with processing. + /// + None = 0, - /// - /// Break item processing but not the rest of the execution. Typically used for demo limitations. - /// - Soft, + /// + /// Break item processing but not the rest of the execution. Typically used for demo limitations. + /// + Soft, - /// - /// Break processing immediately - /// - Hard - } + /// + /// Break processing immediately + /// + Hard + } } diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/DataExchangeSettings.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/DataExchangeSettings.cs index 4b0bc6d497..6546707890 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/DataExchangeSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/DataExchangeSettings.cs @@ -2,27 +2,27 @@ namespace SmartStore.Core.Domain.DataExchange { - public class DataExchangeSettings : ISettings - { - public DataExchangeSettings() - { - MaxFileNameLength = 50; - ImageDownloadTimeout = 10; - } + public class DataExchangeSettings : ISettings + { + public DataExchangeSettings() + { + MaxFileNameLength = 50; + ImageDownloadTimeout = 10; + } - /// - /// The maximum length of file names (in characters) of files created by the export framework - /// - public int MaxFileNameLength { get; set; } + /// + /// The maximum length of file names (in characters) of files created by the export framework + /// + public int MaxFileNameLength { get; set; } - /// - /// Relative path to a folder with images to be imported - /// - public string ImageImportFolder { get; set; } + /// + /// Relative path to a folder with images to be imported + /// + public string ImageImportFolder { get; set; } - /// - /// The timeout for image download per entity in minutes - /// - public int ImageDownloadTimeout { get; set; } - } + /// + /// The timeout for image download per entity in minutes + /// + public int ImageDownloadTimeout { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportDeployment.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportDeployment.cs index b9e323c670..37df2f481a 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportDeployment.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportDeployment.cs @@ -3,139 +3,127 @@ namespace SmartStore.Core.Domain { - public class ExportDeployment : BaseEntity, ICloneable - { - /// - /// The profile identifier - /// - public int ProfileId { get; set; } - - /// - /// Name of the deployment - /// - public string Name { get; set; } - - /// - /// Whether the deployment is enabled - /// - public bool Enabled { get; set; } - - /// - /// XML with information about the last deployment result - /// - public string ResultInfo { get; set; } - - /// - /// The deployment type identifier - /// - public int DeploymentTypeId { get; set; } - - /// - /// The deployment type - /// - public ExportDeploymentType DeploymentType - { - get - { - return (ExportDeploymentType)DeploymentTypeId; - } - set - { - DeploymentTypeId = (int)value; - } - } - - public string Username { get; set; } - - public string Password { get; set; } - - /// - /// Deployment URL - /// - public string Url { get; set; } - - /// - /// The type identifier of how to transmit via HTTP - /// - public int HttpTransmissionTypeId { get; set; } - - /// - /// The type of how to transmit via HTTP - /// - public ExportHttpTransmissionType HttpTransmissionType - { - get - { - return (ExportHttpTransmissionType)HttpTransmissionTypeId; - } - set - { - HttpTransmissionTypeId = (int)value; - } - } - - /// - /// The file system path - /// - public string FileSystemPath { get; set; } - - /// - /// Path of a subfolder - /// - public string SubFolder { get; set; } - - /// - /// Multiple email addresses can be separated by commas - /// - public string EmailAddresses { get; set; } - - /// - /// Subject of the email - /// - public string EmailSubject { get; set; } - - /// - /// Identifier of the email account - /// - public int EmailAccountId { get; set; } - - /// - /// Whether to use FTP active or passive mode - /// - public bool PassiveMode { get; set; } - - /// - /// Whether to use SSL - /// - public bool UseSsl { get; set; } - - public virtual ExportProfile Profile { get; set; } - - public ExportDeployment Clone() - { - var deployment = new ExportDeployment - { - Name = this.Name, - Enabled = this.Enabled, - DeploymentTypeId = this.DeploymentTypeId, - Username = this.Username, - Password = this.Password, - Url = this.Url, - HttpTransmissionTypeId = this.HttpTransmissionTypeId, - FileSystemPath = this.FileSystemPath, - SubFolder = this.SubFolder, - EmailAddresses = this.EmailAddresses, - EmailSubject = this.EmailSubject, - EmailAccountId = this.EmailAccountId, - PassiveMode = this.PassiveMode, - UseSsl = this.UseSsl - }; - return deployment; - } - - object ICloneable.Clone() - { - return this.Clone(); - } - } + public class ExportDeployment : BaseEntity, ICloneable + { + /// + /// The profile identifier + /// + public int ProfileId { get; set; } + + /// + /// Name of the deployment + /// + public string Name { get; set; } + + /// + /// Whether the deployment is enabled + /// + public bool Enabled { get; set; } + + /// + /// XML with information about the last deployment result + /// + public string ResultInfo { get; set; } + + /// + /// The deployment type identifier + /// + public int DeploymentTypeId { get; set; } + + /// + /// The deployment type + /// + public ExportDeploymentType DeploymentType + { + get => (ExportDeploymentType)DeploymentTypeId; + set => DeploymentTypeId = (int)value; + } + + public string Username { get; set; } + + public string Password { get; set; } + + /// + /// Deployment URL + /// + public string Url { get; set; } + + /// + /// The type identifier of how to transmit via HTTP + /// + public int HttpTransmissionTypeId { get; set; } + + /// + /// The type of how to transmit via HTTP + /// + public ExportHttpTransmissionType HttpTransmissionType + { + get => (ExportHttpTransmissionType)HttpTransmissionTypeId; + set => HttpTransmissionTypeId = (int)value; + } + + /// + /// The file system path + /// + public string FileSystemPath { get; set; } + + /// + /// Path of a subfolder + /// + public string SubFolder { get; set; } + + /// + /// Multiple email addresses can be separated by commas + /// + public string EmailAddresses { get; set; } + + /// + /// Subject of the email + /// + public string EmailSubject { get; set; } + + /// + /// Identifier of the email account + /// + public int EmailAccountId { get; set; } + + /// + /// Whether to use FTP active or passive mode + /// + public bool PassiveMode { get; set; } + + /// + /// Whether to use SSL + /// + public bool UseSsl { get; set; } + + public virtual ExportProfile Profile { get; set; } + + public ExportDeployment Clone() + { + var deployment = new ExportDeployment + { + Name = this.Name, + Enabled = this.Enabled, + DeploymentTypeId = this.DeploymentTypeId, + Username = this.Username, + Password = this.Password, + Url = this.Url, + HttpTransmissionTypeId = this.HttpTransmissionTypeId, + FileSystemPath = this.FileSystemPath, + SubFolder = this.SubFolder, + EmailAddresses = this.EmailAddresses, + EmailSubject = this.EmailSubject, + EmailAccountId = this.EmailAccountId, + PassiveMode = this.PassiveMode, + UseSsl = this.UseSsl + }; + return deployment; + } + + object ICloneable.Clone() + { + return this.Clone(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportEnums.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportEnums.cs index 4bd1648beb..f11b52fff5 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportEnums.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportEnums.cs @@ -2,18 +2,18 @@ namespace SmartStore.Core.Domain.DataExchange { - /// - /// Supported entity types - /// - public enum ExportEntityType - { - Product = 0, - Category, - Manufacturer, - Customer, - Order, - NewsLetterSubscription, - ShoppingCartItem + /// + /// Supported entity types + /// + public enum ExportEntityType + { + Product = 0, + Category, + Manufacturer, + Customer, + Order, + NewsLetterSubscription, + ShoppingCartItem } /// @@ -30,138 +30,138 @@ public enum RelatedEntityType /// Supported deployment types /// public enum ExportDeploymentType - { - FileSystem = 0, - Email, - Http, - Ftp, - PublicFolder - } - - /// - /// Supported HTTP transmission types - /// - public enum ExportHttpTransmissionType - { - SimplePost = 0, - MultipartFormDataPost - } - - /// - /// Controls the merging of various data as product description - /// - public enum ExportDescriptionMerging - { - None = 0, - ShortDescriptionOrNameIfEmpty, - ShortDescription, - Description, - NameAndShortDescription, - NameAndDescription, - ManufacturerAndNameAndShortDescription, - ManufacturerAndNameAndDescription - } - - /// - /// Controls the merging of various data while exporting attribute combinations as products - /// - public enum ExportAttributeValueMerging - { - None = 0, - AppendAllValuesToName - } - - /// - /// Controls data processing and projection items supported by an export provider - /// - [Flags] - public enum ExportFeatures - { - None = 0, - - /// - /// Whether to automatically create a file based public deployment when an export profile is created - /// - CreatesInitialPublicDeployment = 1, - - /// - /// Whether to offer option to include\exclude grouped products - /// - CanOmitGroupedProducts = 1 << 2, - - /// - /// Whether to offer option to export attribute combinations as products - /// - CanProjectAttributeCombinations = 1 << 3, - - /// - /// Whether to offer further options to manipulate the product description - /// - CanProjectDescription = 1 << 4, - - /// - /// Whether to offer option to enter a brand fallback - /// - OffersBrandFallback = 1 << 5, - - /// - /// Whether to offer option to set a picture size and to get the URL of the main image - /// - CanIncludeMainPicture = 1 << 6, - - /// - /// Whether to use SKU as manufacturer part number if MPN is empty - /// - UsesSkuAsMpnFallback = 1 << 7, - - /// - /// Whether to offer option to enter a shipping time fallback - /// - OffersShippingTimeFallback = 1 << 8, - - /// - /// Whether to offer option to enter a shipping costs fallback and a free shipping threshold - /// - OffersShippingCostsFallback = 1 << 9, - - /// - /// Whether to get the calculated old product price - /// - UsesOldPrice = 1 << 10, - - /// - /// Whether to get the calculated special and regular price (ignoring special offers) - /// - UsesSpecialPrice = 1 << 11, - - /// - /// Whether to not automatically send a completion email - /// - CanOmitCompletionMail = 1 << 12, - - /// - /// Whether to provide additional data of attribute combinations - /// - UsesAttributeCombination = 1 << 13, - - /// - /// Whether to export attribute combinations as products including parent product. Only effective with CanProjectAttributeCombinations. - /// - UsesAttributeCombinationParent = 1 << 14, + { + FileSystem = 0, + Email, + Http, + Ftp, + PublicFolder + } + + /// + /// Supported HTTP transmission types + /// + public enum ExportHttpTransmissionType + { + SimplePost = 0, + MultipartFormDataPost + } + + /// + /// Controls the merging of various data as product description + /// + public enum ExportDescriptionMerging + { + None = 0, + ShortDescriptionOrNameIfEmpty, + ShortDescription, + Description, + NameAndShortDescription, + NameAndDescription, + ManufacturerAndNameAndShortDescription, + ManufacturerAndNameAndDescription + } + + /// + /// Controls the merging of various data while exporting attribute combinations as products + /// + public enum ExportAttributeValueMerging + { + None = 0, + AppendAllValuesToName + } + + /// + /// Controls data processing and projection items supported by an export provider + /// + [Flags] + public enum ExportFeatures + { + None = 0, + + /// + /// Whether to automatically create a file based public deployment when an export profile is created + /// + CreatesInitialPublicDeployment = 1, + + /// + /// Whether to offer option to include\exclude grouped products + /// + CanOmitGroupedProducts = 1 << 2, + + /// + /// Whether to offer option to export attribute combinations as products + /// + CanProjectAttributeCombinations = 1 << 3, + + /// + /// Whether to offer further options to manipulate the product description + /// + CanProjectDescription = 1 << 4, + + /// + /// Whether to offer option to enter a brand fallback + /// + OffersBrandFallback = 1 << 5, + + /// + /// Whether to offer option to set a picture size and to get the URL of the main image + /// + CanIncludeMainPicture = 1 << 6, + + /// + /// Whether to use SKU as manufacturer part number if MPN is empty + /// + UsesSkuAsMpnFallback = 1 << 7, + + /// + /// Whether to offer option to enter a shipping time fallback + /// + OffersShippingTimeFallback = 1 << 8, + + /// + /// Whether to offer option to enter a shipping costs fallback and a free shipping threshold + /// + OffersShippingCostsFallback = 1 << 9, + + /// + /// Whether to get the calculated old product price + /// + UsesOldPrice = 1 << 10, + + /// + /// Whether to get the calculated special and regular price (ignoring special offers) + /// + UsesSpecialPrice = 1 << 11, + + /// + /// Whether to not automatically send a completion email + /// + CanOmitCompletionMail = 1 << 12, + + /// + /// Whether to provide additional data of attribute combinations + /// + UsesAttributeCombination = 1 << 13, + + /// + /// Whether to export attribute combinations as products including parent product. Only effective with CanProjectAttributeCombinations. + /// + UsesAttributeCombinationParent = 1 << 14, /// /// Whether to provide extra data units for related data /// UsesRelatedDataUnits = 1 << 15 - } - - /// - /// Possible order status change after order exporting - /// - public enum ExportOrderStatusChange - { - None = 0, - Processing, - Complete - } + } + + /// + /// Possible order status change after order exporting + /// + public enum ExportOrderStatusChange + { + None = 0, + Processing, + Complete + } } diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportFilter.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportFilter.cs index 476c1a5b8c..2c80250e93 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportFilter.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportFilter.cs @@ -3,172 +3,182 @@ namespace SmartStore.Core.Domain.DataExchange { - [Serializable] - public class ExportFilter - { - /// - /// Store identifier; 0 to load all records - /// - public int StoreId { get; set; } + [Serializable] + public class ExportFilter + { + public ExportFilter() + { + Visibility = ProductVisibility.Full; + } - /// - /// Entity created from - /// - public DateTime? CreatedFrom { get; set; } + /// + /// Store identifier; 0 to load all records + /// + public int StoreId { get; set; } - /// - /// Entity created to - /// - public DateTime? CreatedTo { get; set; } + /// + /// Entity created from + /// + public DateTime? CreatedFrom { get; set; } - #region Product + /// + /// Entity created to + /// + public DateTime? CreatedTo { get; set; } - /// - /// Minimum product identifier - /// - public int? IdMinimum { get; set; } + #region Product - /// - /// Maximum product identifier - /// - public int? IdMaximum { get; set; } + /// + /// Minimum product identifier + /// + public int? IdMinimum { get; set; } - /// - /// Minimum price - /// - public decimal? PriceMinimum { get; set; } + /// + /// Maximum product identifier + /// + public int? IdMaximum { get; set; } - /// - /// Maximum price - /// - public decimal? PriceMaximum { get; set; } + /// + /// Minimum price + /// + public decimal? PriceMinimum { get; set; } - /// - /// Minimum product availability - /// - public int? AvailabilityMinimum { get; set; } + /// + /// Maximum price + /// + public decimal? PriceMaximum { get; set; } - /// - /// Maximum product availability - /// - public int? AvailabilityMaximum { get; set; } + /// + /// Minimum product availability + /// + public int? AvailabilityMinimum { get; set; } - /// - /// A value indicating whether to load only published or non published products - /// - public bool? IsPublished { get; set; } + /// + /// Maximum product availability + /// + public int? AvailabilityMaximum { get; set; } - /// - /// Category identifiers - /// - public int[] CategoryIds { get; set; } + /// + /// A value indicating whether to load only published or non published products + /// + public bool? IsPublished { get; set; } - /// - /// A value indicating whether to load products without any catgory mapping - /// - public bool? WithoutCategories { get; set; } + /// + /// Category identifiers + /// + public int[] CategoryIds { get; set; } - /// - /// Manufacturer identifier - /// - public int? ManufacturerId { get; set; } + /// + /// A value indicating whether to load products without any catgory mapping + /// + public bool? WithoutCategories { get; set; } - /// - /// A value indicating whether to load products without any manufacturer mapping - /// - public bool? WithoutManufacturers { get; set; } + /// + /// Manufacturer identifier + /// + public int? ManufacturerId { get; set; } - /// - /// Identifiers of product tag - /// - public int? ProductTagId { get; set; } + /// + /// A value indicating whether to load products without any manufacturer mapping + /// + public bool? WithoutManufacturers { get; set; } - /// - /// A value indicating whether to load products that are marked as featured (relates only to categories and manufacturers) - /// - public bool? FeaturedProducts { get; set; } + /// + /// Identifiers of product tag + /// + public int? ProductTagId { get; set; } - /// - /// Filter by product type - /// - public ProductType? ProductType { get; set; } + /// + /// A value indicating whether to load products that are marked as featured (relates only to categories and manufacturers) + /// + public bool? FeaturedProducts { get; set; } - #endregion + /// + /// Filter by product type + /// + public ProductType? ProductType { get; set; } - #region Order + /// + /// Filter by visibility. + /// + public ProductVisibility? Visibility { get; set; } - /// - /// Filter by order status - /// - public int[] OrderStatusIds { get; set; } + #endregion - /// - /// Filter by payment status - /// - public int[] PaymentStatusIds { get; set; } + #region Order - /// - /// Filter by shipping status - /// - public int[] ShippingStatusIds { get; set; } + /// + /// Filter by order status + /// + public int[] OrderStatusIds { get; set; } - #endregion + /// + /// Filter by payment status + /// + public int[] PaymentStatusIds { get; set; } - #region Customer + /// + /// Filter by shipping status + /// + public int[] ShippingStatusIds { get; set; } - /// - /// Filter by active or inactive customers - /// - public bool? IsActiveCustomer { get; set; } + #endregion - /// - /// Filter by tax exempt customers - /// - public bool? IsTaxExempt { get; set; } + #region Customer - /// - /// Identifiers of customer roles - /// - public int[] CustomerRoleIds { get; set; } + /// + /// Filter by active or inactive customers + /// + public bool? IsActiveCustomer { get; set; } - /// - /// Filter by billing country identifiers - /// - public int[] BillingCountryIds { get; set; } + /// + /// Filter by tax exempt customers + /// + public bool? IsTaxExempt { get; set; } - /// - /// Filter by shipping country identifiers - /// - public int[] ShippingCountryIds { get; set; } + /// + /// Identifiers of customer roles + /// + public int[] CustomerRoleIds { get; set; } - /// - /// Filter by last activity date from - /// - public DateTime? LastActivityFrom { get; set; } + /// + /// Filter by billing country identifiers + /// + public int[] BillingCountryIds { get; set; } - /// - /// Filter by last activity date to - /// - public DateTime? LastActivityTo { get; set; } + /// + /// Filter by shipping country identifiers + /// + public int[] ShippingCountryIds { get; set; } - /// - /// Filter by at least spent amount - /// - public decimal? HasSpentAtLeastAmount { get; set; } + /// + /// Filter by last activity date from + /// + public DateTime? LastActivityFrom { get; set; } - /// - /// Filter by at least placed orders - /// - public int? HasPlacedAtLeastOrders { get; set; } + /// + /// Filter by last activity date to + /// + public DateTime? LastActivityTo { get; set; } - #endregion + /// + /// Filter by at least spent amount + /// + public decimal? HasSpentAtLeastAmount { get; set; } - #region Newsletter Subscription + /// + /// Filter by at least placed orders + /// + public int? HasPlacedAtLeastOrders { get; set; } - /// - /// Filter by active or inactive subscriber - /// - public bool? IsActiveSubscriber { get; set; } + #endregion + + #region Newsletter Subscription + + /// + /// Filter by active or inactive subscriber + /// + public bool? IsActiveSubscriber { get; set; } /// /// Filter by language @@ -184,6 +194,6 @@ public class ExportFilter /// public int? ShoppingCartTypeId { get; set; } - #endregion - } + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProfile.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProfile.cs index 3d75cf06ef..e14dc21145 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProfile.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProfile.cs @@ -5,51 +5,51 @@ namespace SmartStore.Core.Domain { public class ExportProfile : BaseEntity, ICloneable - { - private ICollection _deployments; - - public ExportProfile() - { - Enabled = true; - PerStore = true; - Cleanup = true; - EmailAccountId = 0; - } - - /// - /// The name of the profile - /// - public string Name { get; set; } - - /// - /// The root path of the export folder - /// - public string FolderName { get; set; } - - /// - /// The pattern for file names - /// - public string FileNamePattern { get; set; } - - /// - /// The system name of the profile - /// - public string SystemName { get; set; } - - /// - /// The system name of the export provider - /// - public string ProviderSystemName { get; set; } - - /// - /// Whether the profile is an unremovable system profile - /// - public bool IsSystemProfile { get; set; } - - /// - /// Whether the export profile is enabled - /// - public bool Enabled { get; set; } + { + private ICollection _deployments; + + public ExportProfile() + { + Enabled = true; + PerStore = true; + Cleanup = true; + EmailAccountId = 0; + } + + /// + /// The name of the profile + /// + public string Name { get; set; } + + /// + /// The root path of the export folder + /// + public string FolderName { get; set; } + + /// + /// The pattern for file names + /// + public string FileNamePattern { get; set; } + + /// + /// The system name of the profile + /// + public string SystemName { get; set; } + + /// + /// The system name of the export provider + /// + public string ProviderSystemName { get; set; } + + /// + /// Whether the profile is an unremovable system profile + /// + public bool IsSystemProfile { get; set; } + + /// + /// Whether the export profile is enabled + /// + public bool Enabled { get; set; } /// /// Whether the export related data @@ -61,110 +61,110 @@ public ExportProfile() /// public int SchedulingTaskId { get; set; } - /// - /// XML with filtering information - /// - public string Filtering { get; set; } - - /// - /// XML with projection information - /// - public string Projection { get; set; } - - /// - /// XML with provider specific configuration data - /// - public string ProviderConfigData { get; set; } - - /// - /// XML with information about the last export - /// - public string ResultInfo { get; set; } - - /// - /// The number of records to be skipped - /// - public int Offset { get; set; } - - /// - /// How many records to be loaded per database round-trip - /// - public int Limit { get; set; } - - /// - /// The maximum number of records of one processed batch - /// - public int BatchSize { get; set; } - - /// - /// Whether to start a separate run-through for each store - /// - public bool PerStore { get; set; } - - /// - /// Email Account identifier used to send a notification message an export completes - /// - public int EmailAccountId { get; set; } - - /// - /// Email addresses where to send the notification message - /// - public string CompletedEmailAddresses { get; set; } - - /// - /// Whether to combine and compress the export files in a ZIP archive - /// - public bool CreateZipArchive { get; set; } - - /// - /// Whether to delete unneeded files after deployment - /// - public bool Cleanup { get; set; } - - - /// - /// The scheduling task - /// - public virtual ScheduleTask ScheduleTask { get; set; } - - /// - /// Gets or sets export deployments - /// - public virtual ICollection Deployments - { - get { return _deployments ?? (_deployments = new HashSet()); } - set { _deployments = value; } - } - - public ExportProfile Clone() - { - var profile = new ExportProfile - { - Name = this.Name, - FolderName = null, - FileNamePattern = this.FileNamePattern, - ProviderSystemName = this.ProviderSystemName, - Enabled = this.Enabled, - SchedulingTaskId = 0, - Filtering = this.Filtering, - Projection = this.Projection, - ProviderConfigData = this.ProviderConfigData, - ResultInfo = null, - Offset = this.Offset, - Limit = this.Limit, - BatchSize = this.BatchSize, - PerStore = this.PerStore, - EmailAccountId = this.EmailAccountId, - CompletedEmailAddresses = this.CompletedEmailAddresses, - CreateZipArchive = this.CreateZipArchive, - Cleanup = this.Cleanup - }; - return profile; - } - - object ICloneable.Clone() - { - return this.Clone(); - } - } + /// + /// XML with filtering information + /// + public string Filtering { get; set; } + + /// + /// XML with projection information + /// + public string Projection { get; set; } + + /// + /// XML with provider specific configuration data + /// + public string ProviderConfigData { get; set; } + + /// + /// XML with information about the last export + /// + public string ResultInfo { get; set; } + + /// + /// The number of records to be skipped + /// + public int Offset { get; set; } + + /// + /// How many records to be loaded per database round-trip + /// + public int Limit { get; set; } + + /// + /// The maximum number of records of one processed batch + /// + public int BatchSize { get; set; } + + /// + /// Whether to start a separate run-through for each store + /// + public bool PerStore { get; set; } + + /// + /// Email Account identifier used to send a notification message an export completes + /// + public int EmailAccountId { get; set; } + + /// + /// Email addresses where to send the notification message + /// + public string CompletedEmailAddresses { get; set; } + + /// + /// Whether to combine and compress the export files in a ZIP archive + /// + public bool CreateZipArchive { get; set; } + + /// + /// Whether to delete unneeded files after deployment + /// + public bool Cleanup { get; set; } + + + /// + /// The scheduling task + /// + public virtual ScheduleTask ScheduleTask { get; set; } + + /// + /// Gets or sets export deployments + /// + public virtual ICollection Deployments + { + get => _deployments ?? (_deployments = new HashSet()); + set => _deployments = value; + } + + public ExportProfile Clone() + { + var profile = new ExportProfile + { + Name = this.Name, + FolderName = null, + FileNamePattern = this.FileNamePattern, + ProviderSystemName = this.ProviderSystemName, + Enabled = this.Enabled, + SchedulingTaskId = 0, + Filtering = this.Filtering, + Projection = this.Projection, + ProviderConfigData = this.ProviderConfigData, + ResultInfo = null, + Offset = this.Offset, + Limit = this.Limit, + BatchSize = this.BatchSize, + PerStore = this.PerStore, + EmailAccountId = this.EmailAccountId, + CompletedEmailAddresses = this.CompletedEmailAddresses, + CreateZipArchive = this.CreateZipArchive, + Cleanup = this.Cleanup + }; + return profile; + } + + object ICloneable.Clone() + { + return this.Clone(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProjection.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProjection.cs index fd41da5fb4..ff48168f71 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProjection.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/ExportProjection.cs @@ -4,197 +4,179 @@ namespace SmartStore.Core.Domain.DataExchange { - /// - /// Settings projected onto an export - /// - /// - /// Note possible projection controlling: a) developer controls, b) merchant controls, c) developer controls what the merchant can control - /// - [Serializable] - public class ExportProjection - { - public ExportProjection() - { - OnlyIndividuallyVisibleAssociated = true; - } - - #region All entity types - - /// - /// Store identifier - /// - public int? StoreId { get; set; } - - /// - /// The language to be applied to the export - /// - public int? LanguageId { get; set; } - - /// - /// The currency to be applied to the export - /// - public int? CurrencyId { get; set; } - - /// - /// Customer identifier - /// - public int? CustomerId { get; set; } - - #endregion - - #region Product - - /// - /// Description merging identifier - /// - public int DescriptionMergingId { get; set; } - - /// - /// Decription merging - /// - [XmlIgnore] - public ExportDescriptionMerging DescriptionMerging - { - get - { - return (ExportDescriptionMerging)DescriptionMergingId; - } - set - { - DescriptionMergingId = (int)value; - } - } - - /// - /// Convert HTML decription to plain text - /// - public bool DescriptionToPlainText { get; set; } - - /// - /// Comma separated text to append to the decription - /// - public string AppendDescriptionText { get; set; } - - /// - /// Remove critical characters from the description - /// - public bool RemoveCriticalCharacters { get; set; } - - /// - /// Comma separated list of critical characters - /// - public string CriticalCharacters { get; set; } - - /// - /// The price type for calculating the product price - /// - public PriceDisplayType? PriceType { get; set; } - - /// - /// Convert net to gross prices - /// - public bool ConvertNetToGrossPrices { get; set; } - - /// - /// Fallback for product brand - /// - public string Brand { get; set; } - - /// - /// Number of images per object to be exported - /// - public int? NumberOfPictures { get; set; } - - /// - /// Picture size - /// - public int PictureSize { get; set; } - - /// - /// Fallback for shipping time - /// - public string ShippingTime { get; set; } - - /// - /// Fallback for shipping costs - /// - public decimal? ShippingCosts { get; set; } - - /// - /// Free shipping threshold - /// - public decimal? FreeShippingThreshold { get; set; } - - /// - /// Whether to export attribute combinations as products - /// - public bool AttributeCombinationAsProduct { get; set; } - - /// - /// Identifier for merging attribute values of attribute combinations - /// - public int AttributeCombinationValueMergingId { get; set; } - - /// - /// Merging attribute values of attribute combinations - /// - [XmlIgnore] - public ExportAttributeValueMerging AttributeCombinationValueMerging - { - get - { - return (ExportAttributeValueMerging)AttributeCombinationValueMergingId; - } - set - { - AttributeCombinationValueMergingId = (int)value; - } - } - - /// - /// Whether to export grouped products - /// - public bool NoGroupedProducts { get; set; } - - /// - /// Whether to export associated products that marked as "visible individually". false to load all records, true to load "visible individually" only - /// - public bool OnlyIndividuallyVisibleAssociated { get; set; } - - #endregion - - #region Order - - /// - /// Identifier of the new state for orders - /// - public int OrderStatusChangeId { get; set; } - - /// - /// New state for orders - /// - [XmlIgnore] - public ExportOrderStatusChange OrderStatusChange - { - get - { - return (ExportOrderStatusChange)OrderStatusChangeId; - } - set - { - OrderStatusChangeId = (int)value; - } - } - - #endregion - - #region Shopping Cart Item - - /// - /// Whether to export bundle products - /// - public bool NoBundleProducts { get; set; } - - #endregion - } + /// + /// Settings projected onto an export + /// + /// + /// Note possible projection controlling: a) developer controls, b) merchant controls, c) developer controls what the merchant can control + /// + [Serializable] + public class ExportProjection + { + public ExportProjection() + { + OnlyIndividuallyVisibleAssociated = true; + } + + #region All entity types + + /// + /// Store identifier + /// + public int? StoreId { get; set; } + + /// + /// The language to be applied to the export + /// + public int? LanguageId { get; set; } + + /// + /// The currency to be applied to the export + /// + public int? CurrencyId { get; set; } + + /// + /// Customer identifier + /// + public int? CustomerId { get; set; } + + #endregion + + #region Product + + /// + /// Description merging identifier + /// + public int DescriptionMergingId { get; set; } + + /// + /// Decription merging + /// + [XmlIgnore] + public ExportDescriptionMerging DescriptionMerging + { + get => (ExportDescriptionMerging)DescriptionMergingId; + set => DescriptionMergingId = (int)value; + } + + /// + /// Convert HTML decription to plain text + /// + public bool DescriptionToPlainText { get; set; } + + /// + /// Comma separated text to append to the decription + /// + public string AppendDescriptionText { get; set; } + + /// + /// Remove critical characters from the description + /// + public bool RemoveCriticalCharacters { get; set; } + + /// + /// Comma separated list of critical characters + /// + public string CriticalCharacters { get; set; } + + /// + /// The price type for calculating the product price + /// + public PriceDisplayType? PriceType { get; set; } + + /// + /// Convert net to gross prices + /// + public bool ConvertNetToGrossPrices { get; set; } + + /// + /// Fallback for product brand + /// + public string Brand { get; set; } + + /// + /// Number of images per object to be exported + /// + public int? NumberOfMediaFiles { get; set; } + + /// + /// Picture size + /// + public int PictureSize { get; set; } + + /// + /// Fallback for shipping time + /// + public string ShippingTime { get; set; } + + /// + /// Fallback for shipping costs + /// + public decimal? ShippingCosts { get; set; } + + /// + /// Free shipping threshold + /// + public decimal? FreeShippingThreshold { get; set; } + + /// + /// Whether to export attribute combinations as products + /// + public bool AttributeCombinationAsProduct { get; set; } + + /// + /// Identifier for merging attribute values of attribute combinations + /// + public int AttributeCombinationValueMergingId { get; set; } + + /// + /// Merging attribute values of attribute combinations + /// + [XmlIgnore] + public ExportAttributeValueMerging AttributeCombinationValueMerging + { + get => (ExportAttributeValueMerging)AttributeCombinationValueMergingId; + set => AttributeCombinationValueMergingId = (int)value; + } + + /// + /// Whether to export grouped products + /// + public bool NoGroupedProducts { get; set; } + + /// + /// Whether to export associated products that marked as "visible individually". false to load all records, true to load "visible individually" only + /// + public bool OnlyIndividuallyVisibleAssociated { get; set; } + + #endregion + + #region Order + + /// + /// Identifier of the new state for orders + /// + public int OrderStatusChangeId { get; set; } + + /// + /// New state for orders + /// + [XmlIgnore] + public ExportOrderStatusChange OrderStatusChange + { + get => (ExportOrderStatusChange)OrderStatusChangeId; + set => OrderStatusChangeId = (int)value; + } + + #endregion + + #region Shopping Cart Item + + /// + /// Whether to export bundle products + /// + public bool NoBundleProducts { get; set; } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/ImportEnums.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/ImportEnums.cs index 94bbd69246..399914eb28 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/ImportEnums.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/ImportEnums.cs @@ -2,27 +2,27 @@ namespace SmartStore.Core.Domain.DataExchange { - /// - /// Supported entity types - /// - public enum ImportEntityType - { - Product = 0, - Category, - Customer, - NewsLetterSubscription - } + /// + /// Supported entity types + /// + public enum ImportEntityType + { + Product = 0, + Category, + Customer, + NewsLetterSubscription + } - public enum ImportFileType - { - CSV = 0, - XLSX - } + public enum ImportFileType + { + CSV = 0, + XLSX + } - [Flags] - public enum ImportModeFlags - { - Insert = 1, - Update = 2 - } + [Flags] + public enum ImportModeFlags + { + Insert = 1, + Update = 2 + } } diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/ImportProfile.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/ImportProfile.cs index 4322a88f2f..6c00160ae9 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/ImportProfile.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/ImportProfile.cs @@ -3,62 +3,50 @@ namespace SmartStore.Core.Domain { - public class ImportProfile : BaseEntity - { - /// - /// The name of the profile - /// - public string Name { get; set; } - - /// - /// The name of the folder (file system) - /// - public string FolderName { get; set; } - - /// - /// The identifier of the file type - /// - public int FileTypeId { get; set; } - - /// - /// The file type - /// - public ImportFileType FileType - { - get - { - return (ImportFileType)FileTypeId; - } - set - { - FileTypeId = (int)value; - } - } - - /// - /// The identifier of the entity type - /// - public int EntityTypeId { get; set; } - - /// - /// The entity type - /// - public ImportEntityType EntityType - { - get - { - return (ImportEntityType)EntityTypeId; - } - set - { - EntityTypeId = (int)value; - } - } - - /// - /// Whether the profile is enabled - /// - public bool Enabled { get; set; } + public class ImportProfile : BaseEntity + { + /// + /// The name of the profile + /// + public string Name { get; set; } + + /// + /// The name of the folder (file system) + /// + public string FolderName { get; set; } + + /// + /// The identifier of the file type + /// + public int FileTypeId { get; set; } + + /// + /// The file type + /// + public ImportFileType FileType + { + get => (ImportFileType)FileTypeId; + set => FileTypeId = (int)value; + } + + /// + /// The identifier of the entity type + /// + public int EntityTypeId { get; set; } + + /// + /// The entity type + /// + public ImportEntityType EntityType + { + get => (ImportEntityType)EntityTypeId; + set => EntityTypeId = (int)value; + } + + /// + /// Whether the profile is enabled + /// + public bool Enabled { get; set; } /// /// Whether the import related data @@ -70,49 +58,49 @@ public ImportEntityType EntityType /// public int Skip { get; set; } - /// - /// Maximum number of records to return - /// - public int Take { get; set; } - - /// - /// Whether to only update existing data - /// - public bool UpdateOnly { get; set; } - - /// - /// Name of key fields to identify existing records during import - /// - public string KeyFieldNames { get; set; } - - /// - /// File type specific configuration - /// - public string FileTypeConfiguration { get; set; } - - /// - /// XML with extra data - /// - public string ExtraData { get; set; } - - /// - /// Mapping of import columns - /// - public string ColumnMapping { get; set; } - - /// - /// XML with information about the last import - /// - public string ResultInfo { get; set; } - - /// - /// The scheduling task identifier - /// - public int SchedulingTaskId { get; set; } - - /// - /// The scheduling task - /// - public virtual ScheduleTask ScheduleTask { get; set; } - } + /// + /// Maximum number of records to return + /// + public int Take { get; set; } + + /// + /// Whether to only update existing data + /// + public bool UpdateOnly { get; set; } + + /// + /// Name of key fields to identify existing records during import + /// + public string KeyFieldNames { get; set; } + + /// + /// File type specific configuration + /// + public string FileTypeConfiguration { get; set; } + + /// + /// XML with extra data + /// + public string ExtraData { get; set; } + + /// + /// Mapping of import columns + /// + public string ColumnMapping { get; set; } + + /// + /// XML with information about the last import + /// + public string ResultInfo { get; set; } + + /// + /// The scheduling task identifier + /// + public int SchedulingTaskId { get; set; } + + /// + /// The scheduling task + /// + public virtual ScheduleTask ScheduleTask { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/DataExchange/SyncMapping.cs b/src/Libraries/SmartStore.Core/Domain/DataExchange/SyncMapping.cs index d8067400cb..09230db0a7 100644 --- a/src/Libraries/SmartStore.Core/Domain/DataExchange/SyncMapping.cs +++ b/src/Libraries/SmartStore.Core/Domain/DataExchange/SyncMapping.cs @@ -1,85 +1,81 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; using SmartStore.Core.Data.Hooks; namespace SmartStore.Core.Domain.DataExchange { - /// - /// Holds info about a synchronization operation with an external system - /// - [DataContract] - [Hookable(false)] - public partial class SyncMapping : BaseEntity - { - public SyncMapping() - { - this.SyncedOnUtc = DateTime.UtcNow; - } + /// + /// Holds info about a synchronization operation with an external system + /// + [DataContract] + [Hookable(false)] + public partial class SyncMapping : BaseEntity + { + public SyncMapping() + { + this.SyncedOnUtc = DateTime.UtcNow; + } - /// - /// Gets or sets the entity identifier in SmartStore - /// - [Index("IX_SyncMapping_ByEntity", 0, IsUnique = true)] - [DataMember] - public int EntityId { get; set; } + /// + /// Gets or sets the entity identifier in SmartStore + /// + [Index("IX_SyncMapping_ByEntity", 0, IsUnique = true)] + [DataMember] + public int EntityId { get; set; } - /// - /// Gets or sets the entity's key in the external application - /// - [Index("IX_SyncMapping_BySource", 0, IsUnique = true)] - [DataMember] - public string SourceKey { get; set; } + /// + /// Gets or sets the entity's key in the external application + /// + [Index("IX_SyncMapping_BySource", 0, IsUnique = true)] + [DataMember] + public string SourceKey { get; set; } - /// - /// Gets or sets a name representing the entity type - /// - [Index("IX_SyncMapping_ByEntity", 1, IsUnique = true)] - [Index("IX_SyncMapping_BySource", 1, IsUnique = true)] - [DataMember] - public string EntityName { get; set; } + /// + /// Gets or sets a name representing the entity type + /// + [Index("IX_SyncMapping_ByEntity", 1, IsUnique = true)] + [Index("IX_SyncMapping_BySource", 1, IsUnique = true)] + [DataMember] + public string EntityName { get; set; } - /// - /// Gets or sets a name for the external application - /// - [Index("IX_SyncMapping_ByEntity", 2, IsUnique = true)] - [Index("IX_SyncMapping_BySource", 2, IsUnique = true)] - [DataMember] - public string ContextName { get; set; } + /// + /// Gets or sets a name for the external application + /// + [Index("IX_SyncMapping_ByEntity", 2, IsUnique = true)] + [Index("IX_SyncMapping_BySource", 2, IsUnique = true)] + [DataMember] + public string ContextName { get; set; } - /// - /// Gets or sets an optional content hash reflecting the source model at the time of last sync - /// - [DataMember] - public string SourceHash { get; set; } + /// + /// Gets or sets an optional content hash reflecting the source model at the time of last sync + /// + [DataMember] + public string SourceHash { get; set; } - /// - /// Gets or sets a custom integer value - /// - [DataMember] - public int? CustomInt { get; set; } + /// + /// Gets or sets a custom integer value + /// + [DataMember] + public int? CustomInt { get; set; } - /// - /// Gets or sets a custom string value - /// - [DataMember] - public string CustomString { get; set; } + /// + /// Gets or sets a custom string value + /// + [DataMember] + public string CustomString { get; set; } - /// - /// Gets or sets a custom bool value - /// - [DataMember] - public bool? CustomBool { get; set; } + /// + /// Gets or sets a custom bool value + /// + [DataMember] + public bool? CustomBool { get; set; } - /// - /// Gets or sets the date of the last sync operation - /// - [DataMember] - public DateTime SyncedOnUtc { get; set; } + /// + /// Gets or sets the date of the last sync operation + /// + [DataMember] + public DateTime SyncedOnUtc { get; set; } - } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Directory/Country.cs b/src/Libraries/SmartStore.Core/Domain/Directory/Country.cs index 45c1dacca7..2d7bb62f3e 100644 --- a/src/Libraries/SmartStore.Core/Domain/Directory/Country.cs +++ b/src/Libraries/SmartStore.Core/Domain/Directory/Country.cs @@ -6,11 +6,11 @@ namespace SmartStore.Core.Domain.Directory { - /// - /// Represents a country - /// - [DataContract] - public partial class Country : BaseEntity, ILocalizedEntity, IStoreMappingSupported + /// + /// Represents a country + /// + [DataContract] + public partial class Country : BaseEntity, ILocalizedEntity, IStoreMappingSupported { private ICollection _stateProvinces; @@ -18,75 +18,92 @@ public partial class Country : BaseEntity, ILocalizedEntity, IStoreMappingSuppor /// Gets or sets the name /// [DataMember] - public string Name { get; set; } + public string Name { get; set; } /// /// Gets or sets a value indicating whether billing is allowed to this country /// [DataMember] - public bool AllowsBilling { get; set; } + public bool AllowsBilling { get; set; } /// /// Gets or sets a value indicating whether shipping is allowed to this country /// [DataMember] - public bool AllowsShipping { get; set; } + public bool AllowsShipping { get; set; } /// /// Gets or sets the two letter ISO code /// [DataMember] - public string TwoLetterIsoCode { get; set; } + public string TwoLetterIsoCode { get; set; } /// /// Gets or sets the three letter ISO code /// [DataMember] - public string ThreeLetterIsoCode { get; set; } + public string ThreeLetterIsoCode { get; set; } /// /// Gets or sets the numeric ISO code /// [DataMember] - public int NumericIsoCode { get; set; } + public int NumericIsoCode { get; set; } /// /// Gets or sets a value indicating whether customers in this country must be charged EU VAT /// [DataMember] - public bool SubjectToVat { get; set; } + public bool SubjectToVat { get; set; } /// /// Gets or sets a value indicating whether the entity is published /// [DataMember] - public bool Published { get; set; } + public bool Published { get; set; } /// /// Gets or sets the display order /// [DataMember] - public int DisplayOrder { get; set; } + public int DisplayOrder { get; set; } - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - [DataMember] - public bool LimitedToStores { get; set; } - - /// - /// Gets or sets the international mailing address format - /// - [DataMember, MaxLength] - public string AddressFormat { get; set; } - - /// - /// Gets or sets the state/provinces - /// - public virtual ICollection StateProvinces + /// + /// Gets or sets a value indicating whether the cookie manager should be displayed for visitors of this country. + /// + [DataMember] + public bool DisplayCookieManager { get; set; } = true; + + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + [DataMember] + public bool LimitedToStores { get; set; } + + /// + /// Gets or sets the international mailing address format + /// + [DataMember, MaxLength] + public string AddressFormat { get; set; } + + /// + /// Gets or sets the identfier of the default currency. + /// + [DataMember] + public int? DefaultCurrencyId { get; set; } + + /// + /// Gets or sets the default currency. + /// + public virtual Currency DefaultCurrency { get; set; } + + /// + /// Gets or sets the state/provinces + /// + public virtual ICollection StateProvinces { - get { return _stateProvinces ?? (_stateProvinces = new HashSet()); } - protected set { _stateProvinces = value; } + get => _stateProvinces ?? (_stateProvinces = new HashSet()); + protected set => _stateProvinces = value; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Directory/Currency.cs b/src/Libraries/SmartStore.Core/Domain/Directory/Currency.cs index 048486e36b..e892b06553 100644 --- a/src/Libraries/SmartStore.Core/Domain/Directory/Currency.cs +++ b/src/Libraries/SmartStore.Core/Domain/Directory/Currency.cs @@ -12,78 +12,73 @@ namespace SmartStore.Core.Domain.Directory /// Represents a currency /// [DataContract] - public partial class Currency : BaseEntity, IAuditable, ILocalizedEntity, IStoreMappingSupported + public partial class Currency : BaseEntity, IAuditable, ILocalizedEntity, IStoreMappingSupported, ICloneable { - public Currency() - { - RoundNumDecimals = 2; - } - /// /// Gets or sets the name /// [DataMember] - public string Name { get; set; } + public string Name { get; set; } /// /// Gets or sets the ISO currency code /// [DataMember] - public string CurrencyCode { get; set; } + public string CurrencyCode { get; set; } /// /// Gets or sets the rate /// [DataMember] - public decimal Rate { get; set; } + public decimal Rate { get; set; } /// /// Gets or sets the display locale /// [DataMember] - public string DisplayLocale { get; set; } + public string DisplayLocale { get; set; } /// /// Gets or sets the custom formatting /// [DataMember] - public string CustomFormatting { get; set; } + public string CustomFormatting { get; set; } - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - [DataMember] - public bool LimitedToStores { get; set; } + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + [DataMember] + public bool LimitedToStores { get; set; } /// /// Gets or sets a value indicating whether the entity is published /// [DataMember] - public bool Published { get; set; } + public bool Published { get; set; } /// /// Gets or sets the display order /// [DataMember] - public int DisplayOrder { get; set; } + public int DisplayOrder { get; set; } /// /// Gets or sets the date and time of instance creation /// [DataMember] - public DateTime CreatedOnUtc { get; set; } + public DateTime CreatedOnUtc { get; set; } /// /// Gets or sets the date and time of instance update /// [DataMember] - public DateTime UpdatedOnUtc { get; set; } + public DateTime UpdatedOnUtc { get; set; } - /// - /// Gets or sets the (comma separated) list of domain endings (e.g. country code top-level domains) to which this currency is the default one - /// - [DataMember] - public string DomainEndings { get; set; } + /// + /// Gets or sets the (comma separated) list of domain endings (e.g. country code top-level domains) to which this currency is the default one + /// + [DataMember] + public string DomainEndings { get; set; } #region Rounding @@ -97,7 +92,7 @@ public Currency() /// Gets or sets the number of decimal places to round to /// [DataMember] - public int RoundNumDecimals { get; set; } + public int RoundNumDecimals { get; set; } = 2; /// /// Gets or sets a value indicating whether to round the order total @@ -117,35 +112,45 @@ public Currency() [DataMember] public CurrencyRoundingRule RoundOrderTotalRule { get; set; } - #endregion Rounding - - #region Utils - - private NumberFormatInfo _numberFormat; - - [NotMapped, JsonIgnore, IgnoreDataMember] - public NumberFormatInfo NumberFormat - { - get - { - if (_numberFormat == null && DisplayLocale.HasValue()) - { - try - { - _numberFormat = CultureInfo.CreateSpecificCulture(DisplayLocale).NumberFormat; - } - catch { } - } - - if (_numberFormat == null) - { - _numberFormat = NumberFormatInfo.CurrentInfo; - } - - return _numberFormat; - } - } - - #endregion - } + #endregion + + public Currency Clone() + { + return (Currency)this.MemberwiseClone(); + } + + object ICloneable.Clone() + { + return this.MemberwiseClone(); + } + + #region Utils + + private NumberFormatInfo _numberFormat; + + [NotMapped, JsonIgnore, IgnoreDataMember] + public NumberFormatInfo NumberFormat + { + get + { + if (_numberFormat == null && DisplayLocale.HasValue()) + { + try + { + _numberFormat = CultureInfo.CreateSpecificCulture(DisplayLocale).NumberFormat; + } + catch { } + } + + if (_numberFormat == null) + { + _numberFormat = NumberFormatInfo.CurrentInfo; + } + + return _numberFormat; + } + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Directory/DeliveryTime.cs b/src/Libraries/SmartStore.Core/Domain/Directory/DeliveryTime.cs index 33f66da021..5d54d71d2d 100644 --- a/src/Libraries/SmartStore.Core/Domain/Directory/DeliveryTime.cs +++ b/src/Libraries/SmartStore.Core/Domain/Directory/DeliveryTime.cs @@ -1,43 +1,107 @@ -using System; -using SmartStore.Core.Domain.Localization; using System.Runtime.Serialization; +using SmartStore.Core.Domain.Localization; namespace SmartStore.Core.Domain.Directory { /// - /// Represents a currency + /// Represents a delivery time /// [DataContract] - public partial class DeliveryTime : BaseEntity, ILocalizedEntity + public partial class DeliveryTime : BaseEntity, ILocalizedEntity { /// /// Gets or sets the name /// [DataMember] - public string Name { get; set; } + public string Name { get; set; } /// /// Gets or sets the hex value /// [DataMember] - public string ColorHexValue { get; set; } + public string ColorHexValue { get; set; } /// /// Gets or sets the display locale /// [DataMember] - public string DisplayLocale { get; set; } + public string DisplayLocale { get; set; } /// /// Gets or sets the display order /// [DataMember] - public int DisplayOrder { get; set; } + public int DisplayOrder { get; set; } /// /// Gets or sets the display order /// [DataMember] public bool? IsDefault { get; set; } + + /// + /// Specifies the earliest time of delivery in days. + /// + [DataMember] + public int? MinDays { get; set; } + + /// + /// Specifies the latest time of delivery in days. + /// + [DataMember] + public int? MaxDays { get; set; } + + #region Utils + + /// + /// Intended to be used for submission of products to third parties which require the delivery days as integer e.g. price comparison portals or after sales services. + /// + /// Days as integer or null if no days are set for the current delivery time. + public int? GetMinOrMaxOrAverageDays() + { + if (MinDays > 0 && MaxDays > 0) + { + return ((int)MinDays + (int)MaxDays) / 2; + } + else if (MinDays > 0) + { + return (int)MinDays; + } + else if (MaxDays > 0) + { + return (int)MaxDays; + } + + return null; + } + + #endregion + } + + + /// + /// Represents how to present delivery times. + /// + public enum DeliveryTimesPresentation + { + /// + /// Do not display. + /// + None = 0, + + /// + /// Display label only. + /// + LabelOnly = 5, + + /// + /// Display date only (if possible). + /// + DateOnly = 10, + + /// + /// Display label and date. + /// + LabelAndDate = 15 } } diff --git a/src/Libraries/SmartStore.Core/Domain/Directory/ExchangeRate.cs b/src/Libraries/SmartStore.Core/Domain/Directory/ExchangeRate.cs index 271ac9b2fc..8f8c8e606a 100644 --- a/src/Libraries/SmartStore.Core/Domain/Directory/ExchangeRate.cs +++ b/src/Libraries/SmartStore.Core/Domain/Directory/ExchangeRate.cs @@ -3,45 +3,35 @@ namespace SmartStore.Core.Domain.Directory { - /// - /// Represents an exchange rate - /// - [DebuggerDisplay("{CurrencyCode} {Rate}")] + /// + /// Represents an exchange rate + /// + [DebuggerDisplay("{CurrencyCode} {Rate}")] public partial class ExchangeRate { /// - /// Creates a new instance of the ExchangeRate class + /// Name of the currrency /// - public ExchangeRate() - { - Name = string.Empty; - CurrencyCode = string.Empty; - Rate = 1.0m; - } + public string Name { get; set; } = string.Empty; - /// - /// Name of the currrency - /// - public string Name { get; set; } - - /// - /// The three letter ISO code for the Exchange Rate, e.g. USD - /// - public string CurrencyCode { get; set; } + /// + /// The three letter ISO code for the Exchange Rate, e.g. USD + /// + public string CurrencyCode { get; set; } = string.Empty; - /// - /// Indicates whether the currency is available in the store - /// - public bool IsStoreCurrency { get; set; } + /// + /// Indicates whether the currency is available in the store + /// + public bool IsStoreCurrency { get; set; } - /// - /// The conversion rate of this currency from the base currency - /// - public decimal Rate { get; set; } + /// + /// The conversion rate of this currency from the base currency + /// + public decimal Rate { get; set; } = 1.0m; /// /// When was this exchange rate updated from the data source (the internet data xml feed) /// public DateTime UpdatedOn { get; set; } - } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Directory/MeasureDimension.cs b/src/Libraries/SmartStore.Core/Domain/Directory/MeasureDimension.cs index dfd825307d..cf65992ff9 100644 --- a/src/Libraries/SmartStore.Core/Domain/Directory/MeasureDimension.cs +++ b/src/Libraries/SmartStore.Core/Domain/Directory/MeasureDimension.cs @@ -1,35 +1,36 @@ using System.Runtime.Serialization; +using SmartStore.Core.Domain.Localization; namespace SmartStore.Core.Domain.Directory { - /// - /// Represents a measure dimension - /// - [DataContract] - public partial class MeasureDimension : BaseEntity + /// + /// Represents a measure dimension + /// + [DataContract] + public partial class MeasureDimension : BaseEntity, ILocalizedEntity { - /// - /// Gets or sets the name - /// - [DataMember] - public string Name { get; set; } + /// + /// Gets or sets the name + /// + [DataMember] + public string Name { get; set; } - /// - /// Gets or sets the system keyword - /// - [DataMember] - public string SystemKeyword { get; set; } + /// + /// Gets or sets the system keyword + /// + [DataMember] + public string SystemKeyword { get; set; } - /// - /// Gets or sets the ratio - /// - [DataMember] - public decimal Ratio { get; set; } + /// + /// Gets or sets the ratio + /// + [DataMember] + public decimal Ratio { get; set; } - /// - /// Gets or sets the display order - /// - [DataMember] - public int DisplayOrder { get; set; } + /// + /// Gets or sets the display order + /// + [DataMember] + public int DisplayOrder { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Directory/MeasureWeight.cs b/src/Libraries/SmartStore.Core/Domain/Directory/MeasureWeight.cs index ec9b31da59..ac1bfda3be 100644 --- a/src/Libraries/SmartStore.Core/Domain/Directory/MeasureWeight.cs +++ b/src/Libraries/SmartStore.Core/Domain/Directory/MeasureWeight.cs @@ -1,35 +1,36 @@ using System.Runtime.Serialization; +using SmartStore.Core.Domain.Localization; namespace SmartStore.Core.Domain.Directory { - /// - /// Represents a measure weight - /// - [DataContract] - public partial class MeasureWeight : BaseEntity + /// + /// Represents a measure weight + /// + [DataContract] + public partial class MeasureWeight : BaseEntity, ILocalizedEntity { - /// - /// Gets or sets the name - /// - [DataMember] - public string Name { get; set; } + /// + /// Gets or sets the name + /// + [DataMember] + public string Name { get; set; } - /// - /// Gets or sets the system keyword - /// - [DataMember] - public string SystemKeyword { get; set; } + /// + /// Gets or sets the system keyword + /// + [DataMember] + public string SystemKeyword { get; set; } - /// - /// Gets or sets the ratio - /// - [DataMember] - public decimal Ratio { get; set; } + /// + /// Gets or sets the ratio + /// + [DataMember] + public decimal Ratio { get; set; } - /// - /// Gets or sets the display order - /// - [DataMember] - public int DisplayOrder { get; set; } + /// + /// Gets or sets the display order + /// + [DataMember] + public int DisplayOrder { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Directory/QuantityUnit.cs b/src/Libraries/SmartStore.Core/Domain/Directory/QuantityUnit.cs index 4e671a5384..77320a1777 100644 --- a/src/Libraries/SmartStore.Core/Domain/Directory/QuantityUnit.cs +++ b/src/Libraries/SmartStore.Core/Domain/Directory/QuantityUnit.cs @@ -13,7 +13,7 @@ public partial class QuantityUnit : BaseEntity, ILocalizedEntity /// Gets or sets the name /// [DataMember] - public string Name { get; set; } + public string Name { get; set; } /// /// Gets or sets the name plural. @@ -25,24 +25,24 @@ public partial class QuantityUnit : BaseEntity, ILocalizedEntity /// Gets or sets the description /// [DataMember] - public string Description { get; set; } + public string Description { get; set; } /// /// Gets or sets the display locale /// [DataMember] - public string DisplayLocale { get; set; } + public string DisplayLocale { get; set; } /// /// Gets or sets the display order /// [DataMember] - public int DisplayOrder { get; set; } + public int DisplayOrder { get; set; } /// /// Gets or sets the default quantity unit /// [DataMember] - public bool IsDefault { get; set; } + public bool IsDefault { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Directory/StateProvince.cs b/src/Libraries/SmartStore.Core/Domain/Directory/StateProvince.cs index f349001fa8..0151457ba6 100644 --- a/src/Libraries/SmartStore.Core/Domain/Directory/StateProvince.cs +++ b/src/Libraries/SmartStore.Core/Domain/Directory/StateProvince.cs @@ -1,6 +1,6 @@ -using SmartStore.Core.Domain.Localization; using System.Runtime.Serialization; +using SmartStore.Core.Domain.Localization; namespace SmartStore.Core.Domain.Directory { @@ -8,43 +8,43 @@ namespace SmartStore.Core.Domain.Directory /// Represents a state/province /// [DataContract] - public partial class StateProvince : BaseEntity, ILocalizedEntity + public partial class StateProvince : BaseEntity, ILocalizedEntity { /// /// Gets or sets the country identifier /// [DataMember] - public int CountryId { get; set; } + public int CountryId { get; set; } /// /// Gets or sets the name /// [DataMember] - public string Name { get; set; } + public string Name { get; set; } /// /// Gets or sets the abbreviation /// [DataMember] - public string Abbreviation { get; set; } + public string Abbreviation { get; set; } /// /// Gets or sets a value indicating whether the entity is published /// [DataMember] - public bool Published { get; set; } + public bool Published { get; set; } /// /// Gets or sets the display order /// [DataMember] - public int DisplayOrder { get; set; } + public int DisplayOrder { get; set; } /// /// Gets or sets the country /// [DataMember] - public virtual Country Country { get; set; } + public virtual Country Country { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Discounts/Discount.cs b/src/Libraries/SmartStore.Core/Domain/Discounts/Discount.cs index f7b116da84..416cfdb06d 100644 --- a/src/Libraries/SmartStore.Core/Domain/Discounts/Discount.cs +++ b/src/Libraries/SmartStore.Core/Domain/Discounts/Discount.cs @@ -3,155 +3,144 @@ using System.Diagnostics; using System.Runtime.Serialization; using SmartStore.Core.Domain.Catalog; +using SmartStore.Rules.Domain; namespace SmartStore.Core.Domain.Discounts { - /// - /// Represents a discount - /// - [DataContract] - [DebuggerDisplay("{Name} - {DiscountType}")] - public partial class Discount : BaseEntity + /// + /// Represents a discount + /// + [DataContract] + [DebuggerDisplay("{Name} - {DiscountType}")] + public partial class Discount : BaseEntity, IRulesContainer { - private ICollection _discountRequirements; + private ICollection _ruleSets; private ICollection _appliedToCategories; - private ICollection _appliedToManufacturers; - private ICollection _appliedToProducts; + private ICollection _appliedToManufacturers; + private ICollection _appliedToProducts; /// /// Gets or sets the name /// [DataMember] - public string Name { get; set; } + public string Name { get; set; } /// /// Gets or sets the discount type identifier /// [DataMember] - public int DiscountTypeId { get; set; } + public int DiscountTypeId { get; set; } /// /// Gets or sets a value indicating whether to use percentage /// [DataMember] - public bool UsePercentage { get; set; } + public bool UsePercentage { get; set; } /// /// Gets or sets the discount percentage /// [DataMember] - public decimal DiscountPercentage { get; set; } + public decimal DiscountPercentage { get; set; } /// /// Gets or sets the discount amount /// [DataMember] - public decimal DiscountAmount { get; set; } + public decimal DiscountAmount { get; set; } /// /// Gets or sets the discount start date and time /// [DataMember] - public DateTime? StartDateUtc { get; set; } + public DateTime? StartDateUtc { get; set; } /// /// Gets or sets the discount end date and time /// [DataMember] - public DateTime? EndDateUtc { get; set; } + public DateTime? EndDateUtc { get; set; } /// /// Gets or sets a value indicating whether discount requires coupon code /// [DataMember] - public bool RequiresCouponCode { get; set; } + public bool RequiresCouponCode { get; set; } /// /// Gets or sets the coupon code /// [DataMember] - public string CouponCode { get; set; } + public string CouponCode { get; set; } /// /// Gets or sets the discount limitation identifier /// [DataMember] - public int DiscountLimitationId { get; set; } + public int DiscountLimitationId { get; set; } /// /// Gets or sets the discount limitation times (used when Limitation is set to "N Times Only" or "N Times Per Customer") /// [DataMember] - public int LimitationTimes { get; set; } + public int LimitationTimes { get; set; } /// /// Gets or sets the discount type /// [DataMember] - public DiscountType DiscountType + public DiscountType DiscountType { - get - { - return (DiscountType)this.DiscountTypeId; - } - set - { - this.DiscountTypeId = (int)value; - } + get => (DiscountType)this.DiscountTypeId; + set => this.DiscountTypeId = (int)value; } /// /// Gets or sets the discount limitation /// [DataMember] - public DiscountLimitationType DiscountLimitation + public DiscountLimitationType DiscountLimitation { - get - { - return (DiscountLimitationType)this.DiscountLimitationId; - } - set - { - this.DiscountLimitationId = (int)value; - } + get => (DiscountLimitationType)this.DiscountLimitationId; + set => this.DiscountLimitationId = (int)value; } /// - /// Gets or sets the discount requirement + /// Gets or sets assigned rule sets. /// - public virtual ICollection DiscountRequirements + public virtual ICollection RuleSets { - get { return _discountRequirements ?? (_discountRequirements = new HashSet()); } - protected set { _discountRequirements = value; } + get => _ruleSets ?? (_ruleSets = new HashSet()); + protected set => _ruleSets = value; } /// /// Gets or sets the categories /// [DataMember] - public virtual ICollection AppliedToCategories + public virtual ICollection AppliedToCategories { - get { return _appliedToCategories ?? (_appliedToCategories = new HashSet()); } - protected set { _appliedToCategories = value; } + get => _appliedToCategories ?? (_appliedToCategories = new HashSet()); + protected set => _appliedToCategories = value; } - /// - /// Gets or sets the manufacturers - /// - [DataMember] - public virtual ICollection AppliedToManufacturers - { - get { return _appliedToManufacturers ?? (_appliedToManufacturers = new HashSet()); } - protected set { _appliedToManufacturers = value; } - } - - /// - /// Gets or sets the products - /// - public virtual ICollection AppliedToProducts - { - get { return _appliedToProducts ?? (_appliedToProducts = new HashSet()); } - protected set { _appliedToProducts = value; } - } + /// + /// Gets or sets the manufacturers + /// + [DataMember] + public virtual ICollection AppliedToManufacturers + { + get => _appliedToManufacturers ?? (_appliedToManufacturers = new HashSet()); + protected set => _appliedToManufacturers = value; + } + + /// + /// Gets or sets the products + /// + public virtual ICollection AppliedToProducts + { + get => _appliedToProducts ?? (_appliedToProducts = new HashSet()); + protected set => _appliedToProducts = value; + } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Discounts/DiscountRequirement.cs b/src/Libraries/SmartStore.Core/Domain/Discounts/DiscountRequirement.cs deleted file mode 100644 index 98ed184662..0000000000 --- a/src/Libraries/SmartStore.Core/Domain/Discounts/DiscountRequirement.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace SmartStore.Core.Domain.Discounts -{ - /// - /// Represents a discount requirement - /// - /// TODO: more abstract data structuring cause properties are plugin dependent. - public partial class DiscountRequirement : BaseEntity - { - /// - /// Gets or sets the discount identifier - /// - public int DiscountId { get; set; } - - /// - /// Gets or sets the discount requirement rule system name - /// - public string DiscountRequirementRuleSystemName { get; set; } - - /// - /// Gets or sets the the discount requirement spent amount - customer had spent/purchased x.xx amount (used when requirement is set to "Customer had spent/purchased x.xx amount") - /// - public decimal SpentAmount { get; set; } - - /// - /// Gets or sets the discount requirement - customer's billing country is... (used when requirement is set to "Billing country is") - /// - public int BillingCountryId { get; set; } - - /// - /// Gets or sets the discount requirement - customer's shipping country is... (used when requirement is set to "Shipping country is") - /// - public int ShippingCountryId { get; set; } - - /// - /// Gets or sets the restricted customer role identifier - /// - public int? RestrictedToCustomerRoleId { get; set; } - - /// - /// Gets or sets the restricted product identifiers (comma separated) - /// - public string RestrictedProductIds { get; set; } - - /// - /// Gets or sets the restricted payment methods (comma separated) - /// - public string RestrictedPaymentMethods { get; set; } - - /// - /// Gets or sets the restricted shipping options (comma separated) - /// - public string RestrictedShippingOptions { get; set; } - - /// - /// Gets or sets the restricted store identifier - /// - public int? RestrictedToStoreId { get; set; } - - /// - /// Gets or sets extra data for use with the discount requirement (could be JSON, XML or any arbitrary deserializable string expression) - /// - public string ExtraData { get; set; } - - /// - /// Gets or sets the discount - /// - public virtual Discount Discount { get; set; } - - } -} diff --git a/src/Libraries/SmartStore.Core/Domain/Discounts/DiscountType.cs b/src/Libraries/SmartStore.Core/Domain/Discounts/DiscountType.cs index c70468a740..d260fcabc6 100644 --- a/src/Libraries/SmartStore.Core/Domain/Discounts/DiscountType.cs +++ b/src/Libraries/SmartStore.Core/Domain/Discounts/DiscountType.cs @@ -17,14 +17,14 @@ public enum DiscountType /// Assigned to categories (all products in a category) /// AssignedToCategories = 5, - /// - /// Assigned to manufacturers (all products of a manufacturer) - /// - AssignedToManufacturers = 6, - /// - /// Assigned to shipping - /// - AssignedToShipping = 10, + /// + /// Assigned to manufacturers (all products of a manufacturer) + /// + AssignedToManufacturers = 6, + /// + /// Assigned to shipping + /// + AssignedToShipping = 10, /// /// Assigned to order subtotal /// diff --git a/src/Libraries/SmartStore.Core/Domain/Forums/ForumGroup.cs b/src/Libraries/SmartStore.Core/Domain/Forums/ForumGroup.cs index ae83f4f8f9..3b21ddda01 100644 --- a/src/Libraries/SmartStore.Core/Domain/Forums/ForumGroup.cs +++ b/src/Libraries/SmartStore.Core/Domain/Forums/ForumGroup.cs @@ -58,8 +58,8 @@ public partial class ForumGroup : BaseEntity, IAuditable, IStoreMappingSupported /// public virtual ICollection Forums { - get { return _forums ?? (_forums = new HashSet()); } - protected set { _forums = value; } + get => _forums ?? (_forums = new HashSet()); + protected set => _forums = value; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Forums/ForumPost.cs b/src/Libraries/SmartStore.Core/Domain/Forums/ForumPost.cs index e65c80573d..3098a9cdc0 100644 --- a/src/Libraries/SmartStore.Core/Domain/Forums/ForumPost.cs +++ b/src/Libraries/SmartStore.Core/Domain/Forums/ForumPost.cs @@ -9,7 +9,7 @@ namespace SmartStore.Core.Domain.Forums /// Represents a forum post /// public partial class ForumPost : BaseEntity, IAuditable - { + { private ICollection _forumPostVotes; /// @@ -64,8 +64,8 @@ public partial class ForumPost : BaseEntity, IAuditable /// public virtual ICollection ForumPostVotes { - get { return _forumPostVotes ?? (_forumPostVotes = new HashSet()); } - protected set { _forumPostVotes = value; } + get => _forumPostVotes ?? (_forumPostVotes = new HashSet()); + protected set => _forumPostVotes = value; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Forums/ForumSettings.cs b/src/Libraries/SmartStore.Core/Domain/Forums/ForumSettings.cs index 86a2b5e423..4d03094351 100644 --- a/src/Libraries/SmartStore.Core/Domain/Forums/ForumSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Forums/ForumSettings.cs @@ -4,41 +4,15 @@ namespace SmartStore.Core.Domain.Forums { public class ForumSettings : ISettings { - public ForumSettings() - { - ForumsEnabled = true; - RelativeDateTimeFormattingEnabled = true; - TopicSubjectMaxLength = 450; - PostMaxLength = 4000; - StrippedTopicMaxLength = 45; - TopicsPageSize = 20; - PostsPageSize = 20; - ForumFeedCount = 20; - SearchResultsPageSize = 20; - LatestCustomerPostsPageSize = 20; - AllowSorting = true; - ShowCustomersPostCount = true; - ForumEditor = EditorType.BBCodeEditor; - SignaturesEnabled = true; - PrivateMessagesPageSize = 20; - ForumSubscriptionsPageSize = 20; - PMSubjectMaxLength = 450; - PMTextMaxLength = 4000; - HomePageActiveDiscussionsTopicCount = 5; - ActiveDiscussionsPageTopicCount = 50; - ActiveDiscussionsFeedCount = 25; - AllowCustomersToVoteOnPosts = true; - } - - /// + /// /// Gets or sets a value indicating whether forums are enabled /// - public bool ForumsEnabled { get; set; } + public bool ForumsEnabled { get; set; } = true; /// /// Gets or sets a value indicating whether relative date and time formatting is enabled (e.g. 2 hours ago, a month ago) /// - public bool RelativeDateTimeFormattingEnabled { get; set; } + public bool RelativeDateTimeFormattingEnabled { get; set; } = true; /// /// Gets or sets a value indicating whether customers are allowed to edit posts that they created @@ -68,7 +42,7 @@ public ForumSettings() /// /// Gets or sets a value indicating whether customer can vote on posts /// - public bool AllowCustomersToVoteOnPosts { get; set; } + public bool AllowCustomersToVoteOnPosts { get; set; } = true; /// /// Gets or sets a value indicating whether guests are allowed to vote on posts @@ -78,57 +52,57 @@ public ForumSettings() /// /// Gets or sets maximum length of topic subject /// - public int TopicSubjectMaxLength { get; set; } + public int TopicSubjectMaxLength { get; set; } = 450; /// /// Gets or sets the maximum length for stripped forum topic names /// - public int StrippedTopicMaxLength { get; set; } + public int StrippedTopicMaxLength { get; set; } = 45; /// /// Gets or sets maximum length of post /// - public int PostMaxLength { get; set; } + public int PostMaxLength { get; set; } = 4000; /// /// Gets or sets the page size for topics in forums /// - public int TopicsPageSize { get; set; } + public int TopicsPageSize { get; set; } = 20; /// /// Gets or sets the page size for posts in topics /// - public int PostsPageSize { get; set; } + public int PostsPageSize { get; set; } = 20; /// /// Gets or sets the page size for search result /// - public int SearchResultsPageSize { get; set; } + public int SearchResultsPageSize { get; set; } = 20; /// /// Gets or sets a value indicating whether sorting is enabled. /// - public bool AllowSorting { get; set; } + public bool AllowSorting { get; set; } = true; /// /// Gets or sets the page size for latest customer posts /// - public int LatestCustomerPostsPageSize { get; set; } + public int LatestCustomerPostsPageSize { get; set; } = 20; /// /// Gets or sets a value indicating whether to show customers forum post count /// - public bool ShowCustomersPostCount { get; set; } + public bool ShowCustomersPostCount { get; set; } = true; /// /// Gets or sets a forum editor type /// - public EditorType ForumEditor { get; set; } + public EditorType ForumEditor { get; set; } = EditorType.BBCodeEditor; /// /// Gets or sets a value indicating whether customers are allowed to specify a signature /// - public bool SignaturesEnabled { get; set; } + public bool SignaturesEnabled { get; set; } = true; /// /// Gets or sets a value indicating whether private messages are allowed @@ -143,12 +117,12 @@ public ForumSettings() /// /// Gets or sets the page size for private messages /// - public int PrivateMessagesPageSize { get; set; } + public int PrivateMessagesPageSize { get; set; } = 20; /// /// Gets or sets the page size for (My Account) Forum Subscriptions /// - public int ForumSubscriptionsPageSize { get; set; } + public int ForumSubscriptionsPageSize { get; set; } = 20; /// /// Gets or sets a value indicating whether a customer should be notified about new private messages @@ -158,27 +132,27 @@ public ForumSettings() /// /// Gets or sets maximum length of pm subject /// - public int PMSubjectMaxLength { get; set; } + public int PMSubjectMaxLength { get; set; } = 450; /// /// Gets or sets maximum length of pm message /// - public int PMTextMaxLength { get; set; } + public int PMTextMaxLength { get; set; } = 4000; /// /// Gets or sets the number of items to display for Active Discussions on forums home page /// - public int HomePageActiveDiscussionsTopicCount { get; set; } + public int HomePageActiveDiscussionsTopicCount { get; set; } = 5; /// /// Gets or sets the number of items to display for Active Discussions page /// - public int ActiveDiscussionsPageTopicCount { get; set; } + public int ActiveDiscussionsPageTopicCount { get; set; } = 50; /// /// Gets or sets the number of items to display for Active Discussions RSS Feed /// - public int ActiveDiscussionsFeedCount { get; set; } + public int ActiveDiscussionsFeedCount { get; set; } = 25; /// /// Gets or sets the whether the Active Discussions RSS Feed is enabled @@ -193,6 +167,21 @@ public ForumSettings() /// /// Gets or sets the number of items to display for Forum RSS Feed /// - public int ForumFeedCount { get; set; } + public int ForumFeedCount { get; set; } = 20; + + /// + /// Gets or sets the meta title for forum index page + /// + public string MetaTitle { get; set; } + + /// + /// Gets or sets the meta description for forum index page + /// + public string MetaDescription { get; set; } + + /// + /// Gets or sets the meta keywords for forum index page + /// + public string MetaKeywords { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Forums/ForumTopic.cs b/src/Libraries/SmartStore.Core/Domain/Forums/ForumTopic.cs index 63d6134530..d7059778c1 100644 --- a/src/Libraries/SmartStore.Core/Domain/Forums/ForumTopic.cs +++ b/src/Libraries/SmartStore.Core/Domain/Forums/ForumTopic.cs @@ -8,7 +8,7 @@ namespace SmartStore.Core.Domain.Forums /// Represents a forum topic /// public partial class ForumTopic : BaseEntity, IAuditable - { + { /// /// Gets or sets the forum identifier /// @@ -87,14 +87,8 @@ public partial class ForumTopic : BaseEntity, IAuditable /// public ForumTopicType ForumTopicType { - get - { - return (ForumTopicType)this.TopicTypeId; - } - set - { - this.TopicTypeId = (int)value; - } + get => (ForumTopicType)this.TopicTypeId; + set => this.TopicTypeId = (int)value; } /// diff --git a/src/Libraries/SmartStore.Core/Domain/Forums/PrivateMessage.cs b/src/Libraries/SmartStore.Core/Domain/Forums/PrivateMessage.cs index 0c7bfa9265..3e447d6e17 100644 --- a/src/Libraries/SmartStore.Core/Domain/Forums/PrivateMessage.cs +++ b/src/Libraries/SmartStore.Core/Domain/Forums/PrivateMessage.cs @@ -8,10 +8,10 @@ namespace SmartStore.Core.Domain.Forums /// public partial class PrivateMessage : BaseEntity { - /// - /// Gets or sets the store identifier - /// - public int StoreId { get; set; } + /// + /// Gets or sets the store identifier + /// + public int StoreId { get; set; } /// /// Gets or sets the customer identifier who sent the message diff --git a/src/Libraries/SmartStore.Core/Domain/Localization/ILocalizedEntity.cs b/src/Libraries/SmartStore.Core/Domain/Localization/ILocalizedEntity.cs index 5f022ea319..49f6e41be7 100644 --- a/src/Libraries/SmartStore.Core/Domain/Localization/ILocalizedEntity.cs +++ b/src/Libraries/SmartStore.Core/Domain/Localization/ILocalizedEntity.cs @@ -6,6 +6,6 @@ namespace SmartStore.Core.Domain.Localization /// public interface ILocalizedEntity { - int Id { get; } - } + int Id { get; } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Localization/Language.cs b/src/Libraries/SmartStore.Core/Domain/Localization/Language.cs index 1d4b30386e..f80cb6bb74 100644 --- a/src/Libraries/SmartStore.Core/Domain/Localization/Language.cs +++ b/src/Libraries/SmartStore.Core/Domain/Localization/Language.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using System.Runtime.Serialization; using System.Diagnostics; using System.Globalization; +using System.Runtime.Serialization; using SmartStore.Core.Domain.Stores; namespace SmartStore.Core.Domain.Localization @@ -10,8 +10,8 @@ namespace SmartStore.Core.Domain.Localization /// Represents a language /// [DataContract] - [DebuggerDisplay("{LanguageCulture}")] - public partial class Language : BaseEntity, IStoreMappingSupported + [DebuggerDisplay("{LanguageCulture}")] + public partial class Language : BaseEntity, IStoreMappingSupported { private ICollection _localeStringResources; @@ -19,74 +19,74 @@ public partial class Language : BaseEntity, IStoreMappingSupported /// Gets or sets the name /// [DataMember] - public string Name { get; set; } + public string Name { get; set; } /// /// Gets or sets the language culture (e.g. "en-US") /// [DataMember] - public string LanguageCulture { get; set; } + public string LanguageCulture { get; set; } /// /// Gets or sets the unique SEO code (e.g. "en") /// [DataMember] - public string UniqueSeoCode { get; set; } - + public string UniqueSeoCode { get; set; } + /// /// Gets or sets the flag image file name /// - [DataMember] - public string FlagImageFileName { get; set; } + [DataMember] + public string FlagImageFileName { get; set; } /// /// Gets or sets a value indicating whether the language supports "Right-to-left" /// [DataMember] - public bool Rtl { get; set; } + public bool Rtl { get; set; } - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - [DataMember] - public bool LimitedToStores { get; set; } + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + [DataMember] + public bool LimitedToStores { get; set; } /// /// Gets or sets a value indicating whether the language is published /// [DataMember] - public bool Published { get; set; } + public bool Published { get; set; } /// /// Gets or sets the display order /// [DataMember] - public int DisplayOrder { get; set; } - + public int DisplayOrder { get; set; } + /// /// Gets or sets locale string resources /// public virtual ICollection LocaleStringResources { - get { return _localeStringResources ?? (_localeStringResources = new HashSet()); } - protected set { _localeStringResources = value; } + get => _localeStringResources ?? (_localeStringResources = new HashSet()); + protected set => _localeStringResources = value; } - public string GetTwoLetterISOLanguageName() - { - if (UniqueSeoCode.HasValue()) - { - return UniqueSeoCode; - } + public string GetTwoLetterISOLanguageName() + { + if (UniqueSeoCode.HasValue()) + { + return UniqueSeoCode; + } - try - { - var ci = new CultureInfo(LanguageCulture); - return ci.TwoLetterISOLanguageName; - } - catch { } + try + { + var ci = new CultureInfo(LanguageCulture); + return ci.TwoLetterISOLanguageName; + } + catch { } - return null; - } + return null; + } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Localization/LocalizationSettings.cs b/src/Libraries/SmartStore.Core/Domain/Localization/LocalizationSettings.cs index e523cb11ea..ee8df7af19 100644 --- a/src/Libraries/SmartStore.Core/Domain/Localization/LocalizationSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Localization/LocalizationSettings.cs @@ -4,14 +4,14 @@ namespace SmartStore.Core.Domain.Localization { public class LocalizationSettings : ISettings { - public LocalizationSettings() - { - UseImagesForLanguageSelection = true; - DefaultLanguageRedirectBehaviour = DefaultLanguageRedirectBehaviour.StripSeoCode; - InvalidLanguageRedirectBehaviour = InvalidLanguageRedirectBehaviour.ReturnHttp404; - } - - /// + public LocalizationSettings() + { + UseImagesForLanguageSelection = true; + DefaultLanguageRedirectBehaviour = DefaultLanguageRedirectBehaviour.StripSeoCode; + InvalidLanguageRedirectBehaviour = InvalidLanguageRedirectBehaviour.ReturnHttp404; + } + + /// /// Default admin area language identifier /// public int DefaultAdminLanguageId { get; set; } @@ -22,7 +22,7 @@ public LocalizationSettings() public bool UseImagesForLanguageSelection { get; set; } /// - /// A value indicating whether the browser user lannguage should be detected + /// A value indicating whether the browser user language should be detected /// public bool DetectBrowserUserLanguage { get; set; } @@ -42,6 +42,11 @@ public LocalizationSettings() /// /// This setting is ignored when SeoFriendlyUrlsForLanguagesEnabled is false public InvalidLanguageRedirectBehaviour InvalidLanguageRedirectBehaviour { get; set; } + + /// + /// Whether to display region/country name in language selector (e.g. "Deutsch (Deutschland)" instead of "Deutsch") + /// + public bool DisplayRegionInLanguageSelector { get; set; } } public enum DefaultLanguageRedirectBehaviour diff --git a/src/Libraries/SmartStore.Core/Domain/Localization/LocalizedProperty.cs b/src/Libraries/SmartStore.Core/Domain/Localization/LocalizedProperty.cs index 6996cf8549..521252dd36 100644 --- a/src/Libraries/SmartStore.Core/Domain/Localization/LocalizedProperty.cs +++ b/src/Libraries/SmartStore.Core/Domain/Localization/LocalizedProperty.cs @@ -13,41 +13,41 @@ public partial class LocalizedProperty : BaseEntity /// Gets or sets the entity identifier /// [DataMember] - [Index("IX_LocalizedProperty_Compound", Order = 1)] - public int EntityId { get; set; } + [Index("IX_LocalizedProperty_Compound", Order = 1)] + public int EntityId { get; set; } /// /// Gets or sets the language identifier /// [DataMember] - [Index("IX_LocalizedProperty_Compound", Order = 4)] - public int LanguageId { get; set; } + [Index("IX_LocalizedProperty_Compound", Order = 4)] + public int LanguageId { get; set; } /// /// Gets or sets the locale key group /// [DataMember] - [Index("IX_LocalizedProperty_Compound", Order = 3)] - [Index("IX_LocalizedProperty_LocaleKeyGroup")] - public string LocaleKeyGroup { get; set; } + [Index("IX_LocalizedProperty_Compound", Order = 3)] + [Index("IX_LocalizedProperty_LocaleKeyGroup")] + public string LocaleKeyGroup { get; set; } /// /// Gets or sets the locale key /// [DataMember] - [Index("IX_LocalizedProperty_Compound", Order = 2)] - public string LocaleKey { get; set; } + [Index("IX_LocalizedProperty_Compound", Order = 2)] + public string LocaleKey { get; set; } /// /// Gets or sets the locale value /// [DataMember] - public string LocaleValue { get; set; } - + public string LocaleValue { get; set; } + /// /// Gets the language /// - [DataMember] - public virtual Language Language { get; set; } + [DataMember] + public virtual Language Language { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Localization/LocalizedPropertyCollection.cs b/src/Libraries/SmartStore.Core/Domain/Localization/LocalizedPropertyCollection.cs index 9cd5549e0f..0537f9868e 100644 --- a/src/Libraries/SmartStore.Core/Domain/Localization/LocalizedPropertyCollection.cs +++ b/src/Libraries/SmartStore.Core/Domain/Localization/LocalizedPropertyCollection.cs @@ -1,90 +1,96 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Globalization; namespace SmartStore.Core.Domain.Localization { - public class LocalizedPropertyCollection : IReadOnlyCollection - { - private readonly string _keyGroup; - private readonly IDictionary _dict; - private HashSet _requestedSet; + public class LocalizedPropertyCollection : IReadOnlyCollection + { + private readonly string _keyGroup; + private readonly IDictionary _dict; + private HashSet _requestedSet; - public LocalizedPropertyCollection(string keyGroup, int[] requestedSet, IEnumerable items) - { - Guard.NotEmpty(keyGroup, nameof(keyGroup)); - Guard.NotNull(items, nameof(items)); + public LocalizedPropertyCollection(string keyGroup, int[] requestedSet, IEnumerable items) + { + Guard.NotEmpty(keyGroup, nameof(keyGroup)); + Guard.NotNull(items, nameof(items)); - _keyGroup = keyGroup; - _dict = items.ToDictionarySafe(x => CreateKey(x.LocaleKey, x.EntityId, x.LanguageId), StringComparer.OrdinalIgnoreCase); + _keyGroup = keyGroup; + _dict = items.ToDictionarySafe(x => CreateKey(x.LocaleKey, x.EntityId, x.LanguageId), StringComparer.OrdinalIgnoreCase); - if (requestedSet != null && requestedSet.Length > 0) - { - _requestedSet = new HashSet(requestedSet); - } - } + if (requestedSet != null && requestedSet.Length > 0) + { + _requestedSet = new HashSet(requestedSet); + } + } - public void MergeWith(LocalizedPropertyCollection other) - { - Guard.NotNull(other, nameof(other)); + public void MergeWith(LocalizedPropertyCollection other) + { + Guard.NotNull(other, nameof(other)); - if (!this._keyGroup.IsCaseInsensitiveEqual(other._keyGroup)) - { - throw new InvalidOperationException("Expected keygroup '{0}', but was '{1}'".FormatInvariant(this._keyGroup, other._keyGroup)); - } + if (!this._keyGroup.IsCaseInsensitiveEqual(other._keyGroup)) + { + throw new InvalidOperationException("Expected keygroup '{0}', but was '{1}'".FormatInvariant(this._keyGroup, other._keyGroup)); + } - // Merge dictionary - other._dict.Merge(this._dict, true); + // Merge dictionary + other._dict.Merge(this._dict, true); - // Merge requested set (entity ids) - if (this._requestedSet != null) - { - if (other._requestedSet == null) - { - other._requestedSet = new HashSet(this._requestedSet); - } - else - { - other._requestedSet.AddRange(this._requestedSet); - } - } - } + // Merge requested set (entity ids) + if (this._requestedSet != null) + { + if (other._requestedSet == null) + { + other._requestedSet = new HashSet(this._requestedSet); + } + else + { + other._requestedSet.AddRange(this._requestedSet); + } + } + } - public string GetValue(int languageId, int entityId, string localeKey) - { - return Find(languageId, entityId, localeKey)?.LocaleValue; - } + public string GetValue(int languageId, int entityId, string localeKey) + { + return Find(languageId, entityId, localeKey)?.LocaleValue; + } - public LocalizedProperty Find(int languageId, int entityId, string localeKey) - { - var item = _dict.Get(CreateKey(localeKey, entityId, languageId)); + public LocalizedProperty Find(int languageId, int entityId, string localeKey) + { + var item = _dict.Get(CreateKey(localeKey, entityId, languageId)); - if (item == null && (_requestedSet == null || _requestedSet.Contains(entityId))) - { - // Although the item does not exist in the local dictionary it has been requested - // from the database, which means it does not exist in the db either. - // Avoid the upcoming roundtrip. - return new LocalizedProperty - { - LocaleKeyGroup = _keyGroup, - EntityId = entityId, - LanguageId = languageId, - LocaleKey = localeKey - }; - } + if (item == null && (_requestedSet == null || _requestedSet.Contains(entityId))) + { + // Although the item does not exist in the local dictionary it has been requested + // from the database, which means it does not exist in the db either. + // Avoid the upcoming roundtrip. + return new LocalizedProperty + { + LocaleKeyGroup = _keyGroup, + EntityId = entityId, + LanguageId = languageId, + LocaleKey = localeKey + }; + } - return item; - } + return item; + } - private string CreateKey(string localeKey, int entityId, int languageId) - { - return string.Concat(localeKey, "-", entityId.ToString(CultureInfo.InvariantCulture), "-", languageId); - } + private string CreateKey(string localeKey, int entityId, int languageId) + { + return string.Concat(localeKey, "-", entityId.ToString(CultureInfo.InvariantCulture), "-", languageId); + } - public int Count => _dict.Values.Count; - public IEnumerator GetEnumerator() => _dict.Values.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => _dict.Values.GetEnumerator(); - } + public int Count => _dict.Values.Count; + public IEnumerator GetEnumerator() + { + return _dict.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _dict.Values.GetEnumerator(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Logging/ActivityLogType.cs b/src/Libraries/SmartStore.Core/Domain/Logging/ActivityLogType.cs index eb59856a02..14e36bac16 100644 --- a/src/Libraries/SmartStore.Core/Domain/Logging/ActivityLogType.cs +++ b/src/Libraries/SmartStore.Core/Domain/Logging/ActivityLogType.cs @@ -1,13 +1,11 @@ -using System.Collections.Generic; - -namespace SmartStore.Core.Domain.Logging +namespace SmartStore.Core.Domain.Logging { /// /// Represents an activity log type record /// public partial class ActivityLogType : BaseEntity { - + /// /// Gets or sets the system keyword /// diff --git a/src/Libraries/SmartStore.Core/Domain/Logging/Log.cs b/src/Libraries/SmartStore.Core/Domain/Logging/Log.cs index f1ab2cf330..a907f17dfc 100644 --- a/src/Libraries/SmartStore.Core/Domain/Logging/Log.cs +++ b/src/Libraries/SmartStore.Core/Domain/Logging/Log.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations.Schema; +using SmartStore.Core.Data.Hooks; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Logging; @@ -8,13 +9,14 @@ namespace SmartStore.Core.Domain.Logging /// /// Represents a log record /// + [Hookable(false)] public partial class Log : BaseEntity { - /// - /// Gets or sets the log level identifier - /// - [Index("IX_Log_Level", IsUnique = false)] - public int LogLevelId { get; set; } + /// + /// Gets or sets the log level identifier + /// + [Index("IX_Log_Level", IsUnique = false)] + public int LogLevelId { get; set; } /// /// Gets or sets the short message @@ -51,35 +53,29 @@ public partial class Log : BaseEntity /// public DateTime CreatedOnUtc { get; set; } - /// - /// Gets or sets the logger name - /// - [Index("IX_Log_Logger", IsUnique = false)] - public string Logger { get; set; } + /// + /// Gets or sets the logger name + /// + [Index("IX_Log_Logger", IsUnique = false)] + public string Logger { get; set; } - /// - /// Gets or sets the HTTP method - /// - public string HttpMethod { get; set; } + /// + /// Gets or sets the HTTP method + /// + public string HttpMethod { get; set; } - /// - /// Gets or sets the user name - /// - public string UserName { get; set; } + /// + /// Gets or sets the user name + /// + public string UserName { get; set; } - /// - /// Gets or sets the log level - /// - public LogLevel LogLevel + /// + /// Gets or sets the log level + /// + public LogLevel LogLevel { - get - { - return (LogLevel)this.LogLevelId; - } - set - { - this.LogLevelId = (int)value; - } + get => (LogLevel)this.LogLevelId; + set => this.LogLevelId = (int)value; } /// diff --git a/src/Libraries/SmartStore.Core/Domain/Media/Download.cs b/src/Libraries/SmartStore.Core/Domain/Media/Download.cs index 476c8d4ea3..95c3a6351e 100644 --- a/src/Libraries/SmartStore.Core/Domain/Media/Download.cs +++ b/src/Libraries/SmartStore.Core/Domain/Media/Download.cs @@ -1,5 +1,3 @@ -using NuGet; -using SmartStore.Core.Domain.Catalog; using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -11,82 +9,53 @@ namespace SmartStore.Core.Domain.Media /// Represents a download /// [DataContract] - public partial class Download : BaseEntity, ITransient, IHasMedia - { - /// + public partial class Download : BaseEntity//, ITransient + { + /// /// Gets or sets a GUID /// - [DataMember] - [Index] - public Guid DownloadGuid { get; set; } + [DataMember] + [Index] + public Guid DownloadGuid { get; set; } /// /// Gets or sets a value indicating whether DownloadUrl property should be used /// [DataMember] - public bool UseDownloadUrl { get; set; } + public bool UseDownloadUrl { get; set; } /// /// Gets or sets a download URL /// [DataMember] - public string DownloadUrl { get; set; } - - /// - /// Gets or sets the download binary - /// - [Obsolete("Use property MediaStorage instead")] - public byte[] DownloadBinary { get; set; } + public string DownloadUrl { get; set; } /// - /// The mime-type of the download + /// Gets or sets a value indicating whether the entity transient/preliminary /// - [DataMember] - public string ContentType { get; set; } + [DataMember] + [Index("IX_UpdatedOn_IsTransient", 1)] + public bool IsTransient { get; set; } /// - /// The filename of the download + /// Gets or sets the date and time of instance update /// - [DataMember] - public string Filename { get; set; } + [DataMember] + [Index("IX_UpdatedOn_IsTransient", 0)] + public DateTime UpdatedOnUtc { get; set; } /// - /// Gets or sets the extension + /// Gets or sets the media file identifier /// - [DataMember] - public string Extension { get; set; } + [DataMember] + public int? MediaFileId { get; set; } /// - /// Gets or sets a value indicating whether the download is new + /// Gets or sets the media file /// - [DataMember] - public bool IsNew { get; set; } - - /// - /// Gets or sets a value indicating whether the entity transient/preliminary - /// - [DataMember] - [Index("IX_UpdatedOn_IsTransient", 1)] - public bool IsTransient { get; set; } - - /// - /// Gets or sets the date and time of instance update - /// - [DataMember] - [Index("IX_UpdatedOn_IsTransient", 0)] - public DateTime UpdatedOnUtc { get; set; } - - /// - /// Gets or sets the media storage identifier - /// - [DataMember] - public int? MediaStorageId { get; set; } + [DataMember] + public virtual MediaFile MediaFile { get; set; } - /// - /// Gets or sets the media storage - /// - public virtual MediaStorage MediaStorage { get; set; } - /// /// Gets or sets a value indicating the corresponding entity id /// diff --git a/src/Libraries/SmartStore.Core/Domain/Media/Enums.cs b/src/Libraries/SmartStore.Core/Domain/Media/Enums.cs new file mode 100644 index 0000000000..cb16901a9c --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Media/Enums.cs @@ -0,0 +1,22 @@ +namespace SmartStore.Core.Domain.Media +{ + /// + /// Represents a picture item type + /// + public enum FallbackPictureType + { + NoFallback = 0, + + /// + /// Entities (products, categories, manufacturers) + /// + Entity = 1, + } + + public enum ThumbnailScaleMode + { + Auto, + UseWidth, + UseHeight + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Media/IHasMedia.cs b/src/Libraries/SmartStore.Core/Domain/Media/IHasMedia.cs index 7cee5227c9..f0b70edaba 100644 --- a/src/Libraries/SmartStore.Core/Domain/Media/IHasMedia.cs +++ b/src/Libraries/SmartStore.Core/Domain/Media/IHasMedia.cs @@ -1,15 +1,15 @@ namespace SmartStore.Core.Domain.Media { - public interface IHasMedia - { - /// - /// Gets or sets the media storage identifier - /// - int? MediaStorageId { get; set; } + public interface IHasMedia + { + /// + /// Gets or sets the media storage identifier + /// + int? MediaStorageId { get; set; } - /// - /// Gets or sets the media storage - /// - MediaStorage MediaStorage { get; set; } - } + /// + /// Gets or sets the media storage + /// + MediaStorage MediaStorage { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Media/MediaAlbum.cs b/src/Libraries/SmartStore.Core/Domain/Media/MediaAlbum.cs new file mode 100644 index 0000000000..7d95e78ceb --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Media/MediaAlbum.cs @@ -0,0 +1,25 @@ +using System.Runtime.Serialization; + +namespace SmartStore.Core.Domain.Media +{ + [DataContract] + public partial class MediaAlbum : MediaFolder + { + /// + /// Gets or sets the display name resource key. + /// + public string ResKey { get; set; } + + /// + /// Gets or sets a value indicating whether to include the folder paths in file URL generation. + /// + [DataMember] + public bool IncludePath { get; set; } + + /// + /// Gets or sets the media album display order. + /// + [DataMember] + public int? Order { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Media/MediaFile.cs b/src/Libraries/SmartStore.Core/Domain/Media/MediaFile.cs new file mode 100644 index 0000000000..2681666518 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Media/MediaFile.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Runtime.Serialization; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Localization; + +namespace SmartStore.Core.Domain.Media +{ + [DataContract] + public partial class MediaFile : BaseEntity, ITransient, IHasMedia, IAuditable, ISoftDeletable, ILocalizedEntity + { + #region static + + private static readonly HashSet _outputAffectingProductProps = new HashSet + { + nameof(MediaFile.FolderId), + nameof(MediaFile.Name), + nameof(MediaFile.Alt), + nameof(MediaFile.Title), + nameof(MediaFile.Hidden), + nameof(MediaFile.Deleted) + }; + + public static IReadOnlyCollection GetOutputAffectingPropertyNames() + { + return _outputAffectingProductProps; + } + + #endregion + + private ICollection _productMediaFiles; + private ICollection _tags; + private ICollection _tracks; + + /// + /// Gets or sets the associated folder identifier. + /// + [DataMember] + [Index("IX_Media_MediaType", 0)] + [Index("IX_Media_Extension", 0)] + [Index("IX_Media_PixelSize", 0)] + [Index("IX_Media_Name", 0)] + [Index("IX_Media_Size", 0)] + [Index("IX_Media_UpdatedOnUtc", 0)] + [Index("IX_Media_FolderId", 0)] + public int? FolderId { get; set; } + + /// + /// Gets or sets the associated folder. + /// + [DataMember] + public virtual MediaFolder Folder { get; set; } + + /// + /// Gets or sets the SEO friendly name of the media file including file extension + /// + [DataMember] + [Index("IX_Media_Name", 1)] + public string Name { get; set; } + + /// + /// Gets or sets the localizable image ALT text + /// + [DataMember] + public string Alt { get; set; } + + /// + /// Gets or sets the localizable media file title text + /// + [DataMember] + public string Title { get; set; } + + /// + /// Gets or sets the (dotless) file extension + /// + [DataMember] + [Index("IX_Media_MediaType", 2)] + [Index("IX_Media_Extension", 1)] + public string Extension { get; set; } + + /// + /// Gets or sets the file MIME type + /// + [DataMember] + public string MimeType { get; set; } + + /// + /// Gets or sets the file media type (image, video, audio, document etc.) + /// + [DataMember] + [Index("IX_Media_MediaType", 1)] + public string MediaType { get; set; } + + /// + /// Gets or sets the file size in bytes + /// + [DataMember] + [Index("IX_Media_Size", 1)] + public int Size { get; set; } + + /// + /// Gets or sets the total pixel size of an image (width * height) + /// + [DataMember] + [Index("IX_Media_MediaType", 3)] + [Index("IX_Media_Extension", 2)] + [Index("IX_Media_PixelSize", 1)] + public int? PixelSize { get; set; } + + /// + /// Gets or sets the file metadata as raw JSON dictionary (width, height, video length, EXIF etc.) + /// + [DataMember] + public string Metadata { get; set; } + + /// + /// Gets or sets the image width (if file is an image) + /// + [DataMember] + public int? Width { get; set; } + + /// + /// Gets or sets the image height (if file is an image) + /// + [DataMember] + public int? Height { get; set; } + + /// + /// Gets or sets the date and time of instance update + /// + [DataMember] + public DateTime CreatedOnUtc { get; set; } + + /// + /// Gets or sets the date and time of instance update + /// + [DataMember] + public DateTime UpdatedOnUtc { get; set; } + + /// + /// Gets or sets a value indicating whether the file is transient/preliminary + /// + [DataMember] + public bool IsTransient { get; set; } + + /// + /// Gets or sets a value indicating whether the file has been soft deleted + /// + [Index("IX_Media_MediaType", 4)] + [Index("IX_Media_Extension", 3)] + [Index("IX_Media_PixelSize", 2)] + [Index("IX_Media_Name", 2)] + [Index("IX_Media_Size", 2)] + [Index("IX_Media_UpdatedOnUtc", 2)] + [Index("IX_Media_FolderId", 1)] + public bool Deleted { get; set; } + + /// + /// Gets or sets a value indicating whether the file is hidden + /// + public bool Hidden { get; set; } + + /// + /// Internally used for migration stuff only. + /// 0 = needs migration 'cause existed in previous versions already, 1 = was migrated by migrator, 2 = relations has been detected + /// + public int Version { get; set; } = 2; + + /// + /// Gets or sets the media storage identifier + /// + [DataMember] + public int? MediaStorageId { get; set; } + + /// + /// Gets or sets the media storage + /// + public virtual MediaStorage MediaStorage { get; set; } + + /// + /// Gets the associated tags + /// + [DataMember] + public virtual ICollection Tags + { + get => _tags ?? (_tags = new HashSet()); + protected set => _tags = value; + } + + /// + /// Gets the related entity tracks + /// + [DataMember] + public virtual ICollection Tracks + { + get => _tracks ?? (_tracks = new HashSet()); + protected set => _tracks = value; + } + + /// + /// Gets or sets the product pictures + /// + [DataMember] + public virtual ICollection ProductMediaFiles + { + get => _productMediaFiles ?? (_productMediaFiles = new HashSet()); + protected set => _productMediaFiles = value; + } + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Media/MediaFolder.cs b/src/Libraries/SmartStore.Core/Domain/Media/MediaFolder.cs new file mode 100644 index 0000000000..3598c1bb8d --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Media/MediaFolder.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Runtime.Serialization; + +namespace SmartStore.Core.Domain.Media +{ + [DataContract] + public partial class MediaFolder : BaseEntity + { + private ICollection _files; + private ICollection _children; + + /// + /// Gets or sets the parent folder id. + /// + [DataMember] + [Index("IX_NameParentId", Order = 0, IsUnique = true)] + public int? ParentId { get; set; } + + /// + /// Gets or sets the parent folder. + /// + public virtual MediaFolder Parent { get; set; } + + /// + /// Gets or sets the media folder name. + /// + [DataMember] + [Index("IX_NameParentId", Order = 1, IsUnique = true)] + public string Name { get; set; } + + /// + /// Gets or sets the folder URL part slug. + /// + [DataMember] + public string Slug { get; set; } // TBD: localizable (?) + + /// + /// Gets or sets a value indicating whether the folder can track relations to entities for containing files. + /// + [DataMember] + public bool CanDetectTracks { get; set; } + + /// + /// Gets the child folders. + /// + public virtual ICollection Children + { + get => _children ?? (_children = new HashSet()); + protected set => _children = value; + } + + /// + /// Gets or sets the media folder metadata as raw JSON string. + /// + [DataMember] + public string Metadata { get; set; } + + /// + /// (Perf) gets or sets the total number of files in this folder (excluding files from sub-folders)) + /// + [DataMember] + public int FilesCount { get; set; } + + /// + /// Gets or sets the associated media files. + /// + public virtual ICollection Files + { + get => _files ?? (_files = new HashSet()); + protected set => _files = value; + } + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Media/MediaSettings.cs b/src/Libraries/SmartStore.Core/Domain/Media/MediaSettings.cs index 2bf9d0f3ee..1ed1fbbbea 100644 --- a/src/Libraries/SmartStore.Core/Domain/Media/MediaSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Media/MediaSettings.cs @@ -1,74 +1,171 @@ -using SmartStore.Core.Configuration; +using System.Collections.Generic; +using System.Linq; +using SmartStore.Core.Configuration; namespace SmartStore.Core.Domain.Media { public class MediaSettings : ISettings { - public MediaSettings() - { - AvatarPictureSize = 250; - ProductThumbPictureSize = 250; - CategoryThumbPictureSize = 250; - ManufacturerThumbPictureSize = 250; - ProductDetailsPictureSize = 600; - ProductThumbPictureSizeOnProductDetailsPage = 70; - MessageProductThumbPictureSize = 70; - AssociatedProductPictureSize = 600; - BundledProductPictureSize = 70; - CartThumbPictureSize = ProductThumbPictureSize; - CartThumbBundleItemPictureSize = 32; - MiniCartThumbPictureSize = ProductThumbPictureSize; - VariantValueThumbPictureSize = 70; - MaximumImageSize = 2048; - DefaultPictureZoomEnabled = true; - PictureZoomType = "window"; - DefaultImageQuality = 90; - MultipleThumbDirectories = true; - DefaultThumbnailAspectRatio = 1; - AutoGenerateAbsoluteUrls = true; - } - - public int AvatarPictureSize { get; set; } - public int ProductThumbPictureSize { get; set; } - public int ProductDetailsPictureSize { get; set; } - public int ProductThumbPictureSizeOnProductDetailsPage { get; set; } - public int MessageProductThumbPictureSize { get; set; } - public int AssociatedProductPictureSize { get; set; } - public int BundledProductPictureSize { get; set; } - public int CategoryThumbPictureSize { get; set; } - public int ManufacturerThumbPictureSize { get; set; } - public int CartThumbPictureSize { get; set; } - public int CartThumbBundleItemPictureSize { get; set; } - public int MiniCartThumbPictureSize { get; set; } - public int VariantValueThumbPictureSize { get; set; } - - public bool DefaultPictureZoomEnabled { get; set; } - public string PictureZoomType { get; set; } - - public int MaximumImageSize { get; set; } + public bool DefaultPictureZoomEnabled { get; set; } = true; + public string PictureZoomType { get; set; } = "window"; /// /// Geta or sets a default quality used for image generation /// - public int DefaultImageQuality { get; set; } + public int DefaultImageQuality { get; set; } = 90; - /// - /// Gets or sets the height to width ratio for thumbnails in grid style lists (0.2 - 2) - /// - /// - /// A value greater than 1 indicates, that your product pictures are generally - /// in portrait format, less than 1 indicates landscape format. - /// - public int DefaultThumbnailAspectRatio { get; set; } + /// + /// Gets or sets the height to width ratio for thumbnails in grid style lists (0.2 - 2) + /// + /// + /// A value greater than 1 indicates, that your product pictures are generally + /// in portrait format, less than 1 indicates landscape format. + /// + public float DefaultThumbnailAspectRatio { get; set; } = 1; /// /// Geta or sets a vaue indicating whether single (/media/thumbs/) or multiple (/media/thumbs/0001/ and /media/thumbs/0002/) directories will used for picture thumbs /// - public bool MultipleThumbDirectories { get; set; } + public bool MultipleThumbDirectories { get; set; } = true; + + /// + /// Generates absolute media urls based upon current request uri instead of relative urls. + /// + public bool AutoGenerateAbsoluteUrls { get; set; } = true; + + /// + /// Whether orphaned files should automatically be marked as transient so that the daily cleanup task may delete them. + /// + public bool MakeFilesTransientWhenOrphaned { get; set; } = true; + + /// + /// Gets or sets the maximum size (in KB) of an uploaded media file. The default is 102,400 (100 MB). + /// + public long MaxUploadFileSize { get; set; } = 102400; + + #region Thumb sizes / security + + private HashSet _allowedThumbSizes; + + public const int ThumbnailSizeXxs = 32; + public const int ThumbnailSizeXs = 72; + public const int ThumbnailSizeSm = 128; + public const int ThumbnailSizeMd = 256; + public const int ThumbnailSizeLg = 512; + public const int ThumbnailSizeXl = 600; + public const int ThumbnailSizeXxl = 1024; + public const int MaxImageSize = 2048; + + public int AvatarPictureSize { get; set; } = ThumbnailSizeMd; + public int ProductThumbPictureSize { get; set; } = ThumbnailSizeMd; + public int ProductDetailsPictureSize { get; set; } = ThumbnailSizeXl; + public int ProductThumbPictureSizeOnProductDetailsPage { get; set; } = ThumbnailSizeXs; + public int MessageProductThumbPictureSize { get; set; } = ThumbnailSizeXs; + public int AssociatedProductPictureSize { get; set; } = ThumbnailSizeXl; + public int BundledProductPictureSize { get; set; } = ThumbnailSizeXs; + public int CategoryThumbPictureSize { get; set; } = ThumbnailSizeMd; + public int ManufacturerThumbPictureSize { get; set; } = ThumbnailSizeMd; + public int CartThumbPictureSize { get; set; } = ThumbnailSizeMd; + public int CartThumbBundleItemPictureSize { get; set; } = ThumbnailSizeXxs; + public int MiniCartThumbPictureSize { get; set; } = ThumbnailSizeMd; + public int VariantValueThumbPictureSize { get; set; } = ThumbnailSizeXs; + public int AttributeOptionThumbPictureSize { get; set; } = ThumbnailSizeXs; + + public List AllowedExtraThumbnailSizes { get; set; } + + public int MaximumImageSize { get; set; } = MaxImageSize; + + public int[] GetAllowedThumbnailSizes() + { + EnsureThumbSizeWhitelist(); + return _allowedThumbSizes.OrderBy(x => x).ToArray(); + } + + public bool IsAllowedThumbnailSize(int size) + { + EnsureThumbSizeWhitelist(); + return _allowedThumbSizes.Contains(size); + } + + public int GetNextValidThumbnailSize(int currentSize) + { + var allowedSizes = GetAllowedThumbnailSizes(); + foreach (var size in allowedSizes) + { + if (size >= currentSize) + { + return size; + } + } + + return MaxImageSize; + } + + private void EnsureThumbSizeWhitelist() + { + if (_allowedThumbSizes != null) + return; + + _allowedThumbSizes = new HashSet + { + 48, ThumbnailSizeXxs, ThumbnailSizeXs, ThumbnailSizeSm, ThumbnailSizeMd, ThumbnailSizeLg, ThumbnailSizeXl, ThumbnailSizeXxl, + AvatarPictureSize, + ProductThumbPictureSize, + ProductDetailsPictureSize, + ProductThumbPictureSizeOnProductDetailsPage, + MessageProductThumbPictureSize, + AssociatedProductPictureSize, + BundledProductPictureSize, + CategoryThumbPictureSize, + ManufacturerThumbPictureSize, + CartThumbPictureSize, + CartThumbBundleItemPictureSize, + MiniCartThumbPictureSize, + VariantValueThumbPictureSize, + AttributeOptionThumbPictureSize, + MaxImageSize + }; + + if (AllowedExtraThumbnailSizes?.Count > 0) + { + _allowedThumbSizes.AddRange(AllowedExtraThumbnailSizes); + } + } + + #endregion + + #region Media types + + /// + /// A space separated list of image type file extensions (dotless) + /// + public string ImageTypes { get; set; } + + /// + /// A space separated list of video type file extensions (dotless) + /// + public string VideoTypes { get; set; } + + /// + /// A space separated list of audio type file extensions (dotless) + /// + public string AudioTypes { get; set; } + + /// + /// A space separated list of document type file extensions (dotless) + /// + public string DocumentTypes { get; set; } + + /// + /// A space separated list of text type file extensions (dotless) + /// + public string TextTypes { get; set; } + + /// + /// A space separated list of other types file extensions (dotless) + /// + public string BinTypes { get; set; } - /// - /// Generates absolute media urls based upon current request uri instead of relative urls. - /// - public bool AutoGenerateAbsoluteUrls { get; set; } - } + #endregion + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Media/MediaStorage.cs b/src/Libraries/SmartStore.Core/Domain/Media/MediaStorage.cs index f58d750751..687b3274c8 100644 --- a/src/Libraries/SmartStore.Core/Domain/Media/MediaStorage.cs +++ b/src/Libraries/SmartStore.Core/Domain/Media/MediaStorage.cs @@ -1,10 +1,10 @@ namespace SmartStore.Core.Domain.Media { - public partial class MediaStorage : BaseEntity - { - /// - /// Binary data - /// - public byte[] Data { get; set; } - } + public partial class MediaStorage : BaseEntity + { + /// + /// Binary data + /// + public byte[] Data { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Media/MediaTag.cs b/src/Libraries/SmartStore.Core/Domain/Media/MediaTag.cs new file mode 100644 index 0000000000..4b264cd475 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Media/MediaTag.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SmartStore.Core.Domain.Media +{ + [DataContract] + public partial class MediaTag : BaseEntity + { + private ICollection _mediaFiles; + + /// + /// Gets or sets the media tag name. + /// + [DataMember] + public string Name { get; set; } + + /// + /// Gets or sets the associated media files. + /// + public virtual ICollection MediaFiles + { + get => _mediaFiles ?? (_mediaFiles = new HashSet()); + protected set => _mediaFiles = value; + } + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Media/MediaTrack.cs b/src/Libraries/SmartStore.Core/Domain/Media/MediaTrack.cs new file mode 100644 index 0000000000..359c4c5141 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Media/MediaTrack.cs @@ -0,0 +1,141 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; +using System.Runtime.Serialization; +using SmartStore.Core.Data.Hooks; +using SmartStore.Utilities; + +namespace SmartStore.Core.Domain.Media +{ + public enum MediaTrackOperation + { + Track, + Untrack + } + + [DataContract] + [Hookable(false)] + public partial class MediaTrack : BaseEntity, IEquatable + { + private int _mediaFileId; + private int _entityId; + private string _entityName; + private string _property; + private int? _hashCode; + + /// + /// Gets or sets the media file identifier. + /// + [DataMember] + [Index("IX_MediaTrack_Composite", IsUnique = true, Order = 0)] + public int MediaFileId + { + get => _mediaFileId; + set + { + _mediaFileId = value; + _hashCode = null; + } + } + + /// + /// Gets or sets the media file. + /// + public virtual MediaFile MediaFile { get; set; } + + /// + /// Gets or sets the origin album system name. + /// + [DataMember] + public string Album { get; set; } + + /// + /// Gets or sets the related entity identifier. + /// + [DataMember] + [Index("IX_MediaTrack_Composite", IsUnique = true, Order = 1)] + public int EntityId + { + get => _entityId; + set + { + _entityId = value; + _hashCode = null; + } + } + + /// + /// Gets or sets the related entity set name. + /// + [DataMember] + [Index("IX_MediaTrack_Composite", IsUnique = true, Order = 2)] + public string EntityName + { + get => _entityName; + set + { + _entityName = value; + _hashCode = null; + } + } + + /// + /// Gets or sets the media file property name in the tracked entity. + /// + [DataMember] + [Index("IX_MediaTrack_Composite", IsUnique = true, Order = 3)] + public string Property + { + get => _property; + set + { + _property = value; + _hashCode = null; + } + } + + [NotMapped] + public MediaTrackOperation Operation { get; set; } + + protected override bool Equals(BaseEntity other) + { + return ((IEquatable)this).Equals(other as MediaTrack); + } + + bool IEquatable.Equals(MediaTrack other) + { + if (other == null) + return false; + + if (ReferenceEquals(this, other)) + return true; + + return this.MediaFileId == other.MediaFileId + && this.EntityId == other.EntityId + && string.Equals(this.EntityName, other.EntityName, StringComparison.OrdinalIgnoreCase) + && string.Equals(this.Property, other.Property, StringComparison.OrdinalIgnoreCase); + } + + public override int GetHashCode() + { + if (_hashCode == null) + { + var combiner = HashCodeCombiner + .Start() + .Add(GetUnproxiedType().GetHashCode()) + .Add(this.MediaFileId) + .Add(this.EntityId) + .Add(this.EntityName) + .Add(this.Property); + + _hashCode = combiner.CombinedHash; + } + + return _hashCode.Value; + } + + public override string ToString() + { + return $"MediaTrack (MediaFileId: {MediaFileId}, EntityName: {EntityName}, EntityId: {EntityId}, Property: {Property})"; + } + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Media/Picture.cs b/src/Libraries/SmartStore.Core/Domain/Media/Picture.cs deleted file mode 100644 index 1250ef1d07..0000000000 --- a/src/Libraries/SmartStore.Core/Domain/Media/Picture.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using System.Runtime.Serialization; -using SmartStore.Core.Domain.Catalog; - -namespace SmartStore.Core.Domain.Media -{ - [DataContract] - public partial class Picture : BaseEntity, ITransient, IHasMedia - { - private ICollection _productPictures; - - /// - /// Gets or sets the picture binary - /// - [Obsolete("Use property MediaStorage instead")] - public byte[] PictureBinary { get; set; } - - /// - /// Gets or sets the picture mime type - /// - [DataMember] - public string MimeType { get; set; } - - /// - /// Gets or sets the picture width - /// - [DataMember] - public int? Width { get; set; } - - /// - /// Gets or sets the picture height - /// - [DataMember] - public int? Height { get; set; } - - /// - /// Gets or sets the SEO friednly filename of the picture - /// - [DataMember] - public string SeoFilename { get; set; } - - /// - /// Gets or sets a value indicating whether the picture is new - /// - [DataMember] - public bool IsNew { get; set; } - - /// - /// Gets or sets a value indicating whether the entity transient/preliminary - /// - [DataMember] - [Index("IX_UpdatedOn_IsTransient", 1)] - public bool IsTransient { get; set; } - - /// - /// Gets or sets the date and time of instance update - /// - [DataMember] - [Index("IX_UpdatedOn_IsTransient", 0)] - public DateTime UpdatedOnUtc { get; set; } - - /// - /// Gets or sets the media storage identifier - /// - [DataMember] - public int? MediaStorageId { get; set; } - - /// - /// Gets or sets the media storage - /// - public virtual MediaStorage MediaStorage { get; set; } - - /// - /// Gets or sets the product pictures - /// - [DataMember] - public virtual ICollection ProductPictures - { - get { return _productPictures ?? (_productPictures = new HashSet()); } - protected set { _productPictures = value; } - } - } -} diff --git a/src/Libraries/SmartStore.Core/Domain/Media/PictureType.cs b/src/Libraries/SmartStore.Core/Domain/Media/PictureType.cs deleted file mode 100644 index 94eb05184c..0000000000 --- a/src/Libraries/SmartStore.Core/Domain/Media/PictureType.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace SmartStore.Core.Domain.Media -{ - /// - /// Represents a picture item type - /// - public enum FallbackPictureType - { - NoFallback = 0, - - /// - /// Entities (products, categories, manufacturers) - /// - Entity = 1, - } - - public enum ThumbnailScaleMode - { - Auto, - UseWidth, - UseHeight - } -} diff --git a/src/Libraries/SmartStore.Core/Domain/Messages/Campaign.cs b/src/Libraries/SmartStore.Core/Domain/Messages/Campaign.cs index cbd46df472..cf005cc7b4 100644 --- a/src/Libraries/SmartStore.Core/Domain/Messages/Campaign.cs +++ b/src/Libraries/SmartStore.Core/Domain/Messages/Campaign.cs @@ -1,4 +1,5 @@ using System; +using SmartStore.Core.Domain.Security; using SmartStore.Core.Domain.Stores; namespace SmartStore.Core.Domain.Messages @@ -6,7 +7,7 @@ namespace SmartStore.Core.Domain.Messages /// /// Represents a campaign /// - public partial class Campaign : BaseEntity, IStoreMappingSupported + public partial class Campaign : BaseEntity, IStoreMappingSupported, IAclSupported { /// /// Gets or sets the name @@ -28,9 +29,14 @@ public partial class Campaign : BaseEntity, IStoreMappingSupported /// public DateTime CreatedOnUtc { get; set; } - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - public bool LimitedToStores { get; set; } + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + public bool LimitedToStores { get; set; } + + /// + /// Gets or sets a value indicating whether the entity is subject to ACL. + /// + public bool SubjectToAcl { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Messages/EmailAccount.cs b/src/Libraries/SmartStore.Core/Domain/Messages/EmailAccount.cs index d39d745ef8..2f3e818181 100644 --- a/src/Libraries/SmartStore.Core/Domain/Messages/EmailAccount.cs +++ b/src/Libraries/SmartStore.Core/Domain/Messages/EmailAccount.cs @@ -55,10 +55,10 @@ public string FriendlyName { get { - if (DisplayName.IsEmpty()) - return Email; + if (DisplayName.IsEmpty()) + return Email; - return "{0} ({1})".FormatInvariant(DisplayName, Email); + return "{0} ({1})".FormatInvariant(DisplayName, Email); } } @@ -72,9 +72,9 @@ object ICloneable.Clone() return this.MemberwiseClone(); } - public EmailAddress ToEmailAddress() - { - return new EmailAddress(this.Email, this.DisplayName); - } + public EmailAddress ToEmailAddress() + { + return new EmailAddress(this.Email, this.DisplayName); + } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Messages/EmailAccountSettings.cs b/src/Libraries/SmartStore.Core/Domain/Messages/EmailAccountSettings.cs index 8a4d73f784..5c42f41643 100644 --- a/src/Libraries/SmartStore.Core/Domain/Messages/EmailAccountSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Messages/EmailAccountSettings.cs @@ -9,10 +9,10 @@ public class EmailAccountSettings : ISettings /// public int DefaultEmailAccountId { get; set; } - /// - /// Gets or sets a folder where mail messages should be saved (instead of sending them). - /// For debug and test purposes only. - /// - public string PickupDirectoryLocation { get; set; } - } + /// + /// Gets or sets a folder where mail messages should be saved (instead of sending them). + /// For debug and test purposes only. + /// + public string PickupDirectoryLocation { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Messages/EmailAttachmentStorageLocation.cs b/src/Libraries/SmartStore.Core/Domain/Messages/EmailAttachmentStorageLocation.cs index 3dada46a59..3b86155494 100644 --- a/src/Libraries/SmartStore.Core/Domain/Messages/EmailAttachmentStorageLocation.cs +++ b/src/Libraries/SmartStore.Core/Domain/Messages/EmailAttachmentStorageLocation.cs @@ -2,21 +2,21 @@ namespace SmartStore.Core.Domain.Messages { - public enum EmailAttachmentStorageLocation - { - /// - /// Attachment is embedded as Blob - /// - Blob, + public enum EmailAttachmentStorageLocation + { + /// + /// Attachment is embedded as Blob + /// + Blob, - /// - /// Attachment is a reference to - /// - FileReference, - - /// - /// Attachment is located on disk (physical or virtual path) - /// - Path - } + /// + /// Attachment is a reference to + /// + FileReference, + + /// + /// Attachment is located on disk (physical or virtual path) + /// + Path + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Messages/Events.cs b/src/Libraries/SmartStore.Core/Domain/Messages/Events.cs index a10f7193c1..8e183eeb14 100644 --- a/src/Libraries/SmartStore.Core/Domain/Messages/Events.cs +++ b/src/Libraries/SmartStore.Core/Domain/Messages/Events.cs @@ -4,19 +4,19 @@ namespace SmartStore.Core.Domain.Messages { public class EmailSubscribedEvent : IEquatable { - public EmailSubscribedEvent(string email) + public EmailSubscribedEvent(string email) { Email = email; } - public string Email { get; private set; } + public string Email { get; private set; } - public override bool Equals(object obj) - { - return Equals(obj as EmailSubscribedEvent); - } + public override bool Equals(object obj) + { + return Equals(obj as EmailSubscribedEvent); + } - public bool Equals(EmailSubscribedEvent other) + public bool Equals(EmailSubscribedEvent other) { if (ReferenceEquals(null, other)) return false; @@ -33,20 +33,20 @@ public override int GetHashCode() } public class EmailUnsubscribedEvent : IEquatable - { - public EmailUnsubscribedEvent(string email) + { + public EmailUnsubscribedEvent(string email) { Email = email; } - public string Email { get; private set; } + public string Email { get; private set; } - public override bool Equals(object obj) - { - return Equals(obj as EmailUnsubscribedEvent); - } + public override bool Equals(object obj) + { + return Equals(obj as EmailUnsubscribedEvent); + } - public bool Equals(EmailUnsubscribedEvent other) + public bool Equals(EmailUnsubscribedEvent other) { if (ReferenceEquals(null, other)) return false; diff --git a/src/Libraries/SmartStore.Core/Domain/Messages/MessageTemplate.cs b/src/Libraries/SmartStore.Core/Domain/Messages/MessageTemplate.cs index d939cc684b..b15cfc8557 100644 --- a/src/Libraries/SmartStore.Core/Domain/Messages/MessageTemplate.cs +++ b/src/Libraries/SmartStore.Core/Domain/Messages/MessageTemplate.cs @@ -1,7 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.Web.Mvc; using SmartStore.Core.Domain.Localization; -using SmartStore.Core.Domain.Media; using SmartStore.Core.Domain.Stores; namespace SmartStore.Core.Domain.Messages @@ -11,35 +10,35 @@ namespace SmartStore.Core.Domain.Messages /// public partial class MessageTemplate : BaseEntity, ILocalizedEntity, IStoreMappingSupported { - /// + /// /// Gets or sets the name /// public string Name { get; set; } - [StringLength(500), Required] - public string To { get; set; } + [StringLength(500), Required] + public string To { get; set; } - [StringLength(500)] - public string ReplyTo { get; set; } + [StringLength(500)] + public string ReplyTo { get; set; } - /// - /// A comma separated list of required model types (e.g.: Product, Order, Customer, GiftCard) - /// - [StringLength(500)] - public string ModelTypes { get; set; } + /// + /// A comma separated list of required model types (e.g.: Product, Order, Customer, GiftCard) + /// + [StringLength(500)] + public string ModelTypes { get; set; } - [MaxLength] - public string LastModelTree { get; set; } + [MaxLength] + public string LastModelTree { get; set; } - /// - /// Gets or sets the BCC Email addresses - /// - public string BccEmailAddresses { get; set; } + /// + /// Gets or sets the BCC Email addresses + /// + public string BccEmailAddresses { get; set; } - /// - /// Gets or sets the subject - /// - public string Subject { get; set; } + /// + /// Gets or sets the subject + /// + public string Subject { get; set; } /// /// Gets or sets the body @@ -57,29 +56,29 @@ public partial class MessageTemplate : BaseEntity, ILocalizedEntity, IStoreMappi /// public int EmailAccountId { get; set; } - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - public bool LimitedToStores { get; set; } + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + public bool LimitedToStores { get; set; } - /// - /// Gets or sets a value indicating whether emails derived from the template are only send manually - /// - public bool SendManually { get; set; } + /// + /// Gets or sets a value indicating whether emails derived from the template are only send manually + /// + public bool SendManually { get; set; } - /// - /// Gets or sets the attachment 1 file identifier - /// - public int? Attachment1FileId { get; set; } + /// + /// Gets or sets the attachment 1 file identifier + /// + public int? Attachment1FileId { get; set; } - /// - /// Gets or sets the attachment 2 file identifier - /// - public int? Attachment2FileId { get; set; } + /// + /// Gets or sets the attachment 2 file identifier + /// + public int? Attachment2FileId { get; set; } - /// - /// Gets or sets the attachment 3 file identifier - /// - public int? Attachment3FileId { get; set; } + /// + /// Gets or sets the attachment 3 file identifier + /// + public int? Attachment3FileId { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Messages/MessageTemplateNames.cs b/src/Libraries/SmartStore.Core/Domain/Messages/MessageTemplateNames.cs index d768f0c2dc..83f2dfe962 100644 --- a/src/Libraries/SmartStore.Core/Domain/Messages/MessageTemplateNames.cs +++ b/src/Libraries/SmartStore.Core/Domain/Messages/MessageTemplateNames.cs @@ -1,39 +1,38 @@ -using System; - -namespace SmartStore.Core.Domain.Messages +namespace SmartStore.Core.Domain.Messages { - public class MessageTemplateNames - { - public const string CustomerRegistered = "NewCustomer.Notification"; - public const string CustomerWelcome = "Customer.WelcomeMessage"; - public const string CustomerEmailValidation = "Customer.EmailValidationMessage"; - public const string CustomerPasswordRecovery = "Customer.PasswordRecovery"; - public const string OrderPlacedStoreOwner = "OrderPlaced.StoreOwnerNotification"; - public const string OrderPlacedCustomer = "OrderPlaced.CustomerNotification"; - public const string ShipmentSentCustomer = "ShipmentSent.CustomerNotification"; - public const string ShipmentDeliveredCustomer = "ShipmentDelivered.CustomerNotification"; - public const string OrderCompletedCustomer = "OrderCompleted.CustomerNotification"; - public const string OrderCancelledCustomer = "OrderCancelled.CustomerNotification"; - public const string OrderNoteAddedCustomer = "Customer.NewOrderNote"; - public const string RecurringPaymentCancelledStoreOwner = "RecurringPaymentCancelled.StoreOwnerNotification"; - public const string NewsLetterSubscriptionActivation = "NewsLetterSubscription.ActivationMessage"; - public const string NewsLetterSubscriptionDeactivation = "NewsLetterSubscription.DeactivationMessage"; - public const string ShareProduct = "Service.EmailAFriend"; - public const string ShareWishlist = "Wishlist.EmailAFriend"; - public const string ProductQuestion = "Product.AskQuestion"; - public const string NewReturnRequestStoreOwner = "NewReturnRequest.StoreOwnerNotification"; - public const string ReturnRequestStatusChangedCustomer = "ReturnRequestStatusChanged.CustomerNotification"; - public const string NewForumTopic = "Forums.NewForumTopic"; - public const string NewForumPost = "Forums.NewForumPost"; - public const string NewPrivateMessage = "Customer.NewPM"; - public const string GiftCardCustomer = "GiftCard.Notification"; - public const string ProductReviewStoreOwner = "Product.ProductReview"; - public const string QuantityBelowStoreOwner = "QuantityBelow.StoreOwnerNotification"; - public const string NewVatSubmittedStoreOwner = "NewVATSubmitted.StoreOwnerNotification"; - public const string BlogCommentStoreOwner = "Blog.BlogComment"; - public const string NewsCommentStoreOwner = "News.NewsComment"; - public const string BackInStockCustomer = "Customer.BackInStock"; - public const string SystemCampaign = "System.Campaign"; - public const string SystemContactUs = "System.ContactUs"; - } + public class MessageTemplateNames + { + public const string CustomerRegistered = "NewCustomer.Notification"; + public const string CustomerWelcome = "Customer.WelcomeMessage"; + public const string CustomerEmailValidation = "Customer.EmailValidationMessage"; + public const string CustomerPasswordRecovery = "Customer.PasswordRecovery"; + public const string OrderPlacedStoreOwner = "OrderPlaced.StoreOwnerNotification"; + public const string OrderPlacedCustomer = "OrderPlaced.CustomerNotification"; + public const string ShipmentSentCustomer = "ShipmentSent.CustomerNotification"; + public const string ShipmentDeliveredCustomer = "ShipmentDelivered.CustomerNotification"; + public const string OrderCompletedCustomer = "OrderCompleted.CustomerNotification"; + public const string OrderCancelledCustomer = "OrderCancelled.CustomerNotification"; + public const string OrderNoteAddedCustomer = "Customer.NewOrderNote"; + public const string RecurringPaymentCancelledStoreOwner = "RecurringPaymentCancelled.StoreOwnerNotification"; + public const string NewsLetterSubscriptionActivation = "NewsLetterSubscription.ActivationMessage"; + public const string NewsLetterSubscriptionDeactivation = "NewsLetterSubscription.DeactivationMessage"; + public const string ShareProduct = "Service.EmailAFriend"; + public const string ShareWishlist = "Wishlist.EmailAFriend"; + public const string ProductQuestion = "Product.AskQuestion"; + public const string NewReturnRequestStoreOwner = "NewReturnRequest.StoreOwnerNotification"; + public const string ReturnRequestStatusChangedCustomer = "ReturnRequestStatusChanged.CustomerNotification"; + public const string NewForumTopic = "Forums.NewForumTopic"; + public const string NewForumPost = "Forums.NewForumPost"; + public const string NewPrivateMessage = "Customer.NewPM"; + public const string GiftCardCustomer = "GiftCard.Notification"; + public const string ProductReviewStoreOwner = "Product.ProductReview"; + public const string QuantityBelowStoreOwner = "QuantityBelow.StoreOwnerNotification"; + public const string NewVatSubmittedStoreOwner = "NewVATSubmitted.StoreOwnerNotification"; + public const string BlogCommentStoreOwner = "Blog.BlogComment"; + public const string NewsCommentStoreOwner = "News.NewsComment"; + public const string BackInStockCustomer = "Customer.BackInStock"; + public const string SystemCampaign = "System.Campaign"; + public const string SystemContactUs = "System.ContactUs"; + public const string SystemGeneric = "System.Generic"; + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Messages/NewsLetterSubscription.cs b/src/Libraries/SmartStore.Core/Domain/Messages/NewsLetterSubscription.cs index ff24400987..282267126a 100644 --- a/src/Libraries/SmartStore.Core/Domain/Messages/NewsLetterSubscription.cs +++ b/src/Libraries/SmartStore.Core/Domain/Messages/NewsLetterSubscription.cs @@ -1,43 +1,52 @@ using System; using System.ComponentModel.DataAnnotations.Schema; +using System.Runtime.Serialization; namespace SmartStore.Core.Domain.Messages { /// /// Represents NewsLetterSubscription entity /// + [DataContract] public partial class NewsLetterSubscription : BaseEntity - { + { /// /// Gets or sets the newsletter subscription GUID /// + [DataMember] public Guid NewsLetterSubscriptionGuid { get; set; } /// /// Gets or sets the subcriber email /// - [Index("IX_NewsletterSubscription_Email_StoreId", 1)] + [DataMember] + [Index("IX_NewsletterSubscription_Email_StoreId", 1)] public string Email { get; set; } /// /// Gets or sets a value indicating whether subscription is active /// + [DataMember] + [Index] public bool Active { get; set; } /// /// Gets or sets the date and time when subscription was created /// + [DataMember] public DateTime CreatedOnUtc { get; set; } - /// - /// Gets or sets the store identifier - /// - [Index("IX_NewsletterSubscription_Email_StoreId", 2)] - public int StoreId { get; set; } + /// + /// Gets or sets the store identifier + /// + [DataMember] + [Index("IX_NewsletterSubscription_Email_StoreId", 2)] + public int StoreId { get; set; } /// /// Gets or sets the language identifier /// + [DataMember] public int WorkingLanguageId { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Messages/QueuedEmail.cs b/src/Libraries/SmartStore.Core/Domain/Messages/QueuedEmail.cs index be65e172b7..14ba2d6067 100644 --- a/src/Libraries/SmartStore.Core/Domain/Messages/QueuedEmail.cs +++ b/src/Libraries/SmartStore.Core/Domain/Messages/QueuedEmail.cs @@ -8,7 +8,7 @@ namespace SmartStore.Core.Domain.Messages /// public partial class QueuedEmail : BaseEntity { - private ICollection _attachments; + private ICollection _attachments; /// /// Gets or sets the priority @@ -25,10 +25,10 @@ public partial class QueuedEmail : BaseEntity /// public string To { get; set; } - /// - /// Gets or sets the ReplyTo property - /// - public string ReplyTo { get; set; } + /// + /// Gets or sets the ReplyTo property + /// + public string ReplyTo { get; set; } /// /// Gets or sets the CC @@ -70,23 +70,23 @@ public partial class QueuedEmail : BaseEntity /// public int EmailAccountId { get; set; } - /// - /// Gets or sets a value indicating whether emails are only send manually - /// - public bool SendManually { get; set; } + /// + /// Gets or sets a value indicating whether emails are only send manually + /// + public bool SendManually { get; set; } /// /// Gets the email account /// public virtual EmailAccount EmailAccount { get; set; } - /// - /// Gets or sets the collection of attachments - /// - public virtual ICollection Attachments - { - get { return _attachments ?? (_attachments = new HashSet()); } - protected set { _attachments = value; } - } + /// + /// Gets or sets the collection of attachments + /// + public virtual ICollection Attachments + { + get => _attachments ?? (_attachments = new HashSet()); + protected set => _attachments = value; + } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Messages/QueuedEmailAttachment.cs b/src/Libraries/SmartStore.Core/Domain/Messages/QueuedEmailAttachment.cs index e12b76c32e..1f7a005828 100644 --- a/src/Libraries/SmartStore.Core/Domain/Messages/QueuedEmailAttachment.cs +++ b/src/Libraries/SmartStore.Core/Domain/Messages/QueuedEmailAttachment.cs @@ -1,70 +1,63 @@ -using System; -using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.Media; namespace SmartStore.Core.Domain.Messages { - /// - /// Represents an e-mail attachment - /// - public partial class QueuedEmailAttachment : BaseEntity, IHasMedia - { - /// - /// Gets or sets the queued email identifier - /// - public int QueuedEmailId { get; set; } - - /// - /// Gets or sets the queued email entity instance - /// - public virtual QueuedEmail QueuedEmail { get; set; } - - /// - /// Gets or sets the storage location - /// - public EmailAttachmentStorageLocation StorageLocation { get; set; } - - /// - /// A physical or virtual path to the file (only applicable if location is Path) - /// - public string Path { get; set; } - - /// - /// The id of a record (only applicable if location is FileReference) - /// - public int? FileId { get; set; } - - /// - /// Gets the file object - /// - /// - /// This property is not named Download on purpose, because we're going to rename Download to File in a future release. - /// - public virtual Download File { get; set; } - - /// - /// The attachment's binary data (only applicable if location is Blob) - /// - [Obsolete("Use property MediaStorage instead")] - public byte[] Data { get; set; } - - /// - /// The attachment file name (without path) - /// - public string Name { get; set; } - - /// - /// The attachment file's mime type, e.g. application/pdf - /// - public string MimeType { get; set; } - - /// - /// Gets or sets the media storage identifier - /// - public int? MediaStorageId { get; set; } - - /// - /// Gets or sets the media storage - /// - public virtual MediaStorage MediaStorage { get; set; } - } + /// + /// Represents an e-mail attachment + /// + public partial class QueuedEmailAttachment : BaseEntity, IHasMedia + { + /// + /// Gets or sets the queued email identifier + /// + public int QueuedEmailId { get; set; } + + /// + /// Gets or sets the queued email entity instance + /// + public virtual QueuedEmail QueuedEmail { get; set; } + + /// + /// Gets or sets the storage location + /// + public EmailAttachmentStorageLocation StorageLocation { get; set; } + + /// + /// A physical or virtual path to the file (only applicable if location is Path) + /// + public string Path { get; set; } + + /// + /// The id of a record (only applicable if location is FileReference) + /// + public int? MediaFileId { get; set; } + + /// + /// Gets the file object + /// + /// + /// This property is not named Download on purpose, because we're going to rename Download to File in a future release. + /// + public virtual MediaFile MediaFile { get; set; } + + /// + /// The attachment file name (without path) + /// + public string Name { get; set; } + + /// + /// The attachment file's mime type, e.g. application/pdf + /// + public string MimeType { get; set; } + + /// + /// Gets or sets the media storage identifier (when location is BLOB) + /// + public int? MediaStorageId { get; set; } + + /// + /// Gets or sets the media storage (when location is BLOB) + /// + public virtual MediaStorage MediaStorage { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/News/NewsItem.cs b/src/Libraries/SmartStore.Core/Domain/News/NewsItem.cs index cbcf775f72..954961a9b2 100644 --- a/src/Libraries/SmartStore.Core/Domain/News/NewsItem.cs +++ b/src/Libraries/SmartStore.Core/Domain/News/NewsItem.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; using SmartStore.Core.Domain.Localization; +using SmartStore.Core.Domain.Media; using SmartStore.Core.Domain.Seo; using SmartStore.Core.Domain.Stores; @@ -9,14 +11,26 @@ namespace SmartStore.Core.Domain.News /// /// Represents a news item /// - public partial class NewsItem : BaseEntity, ISlugSupported, IStoreMappingSupported + public partial class NewsItem : BaseEntity, ISlugSupported, IStoreMappingSupported, ILocalizedEntity { - private ICollection _newsComments; + #region static - /// - /// Gets or sets the language identifier - /// - public int LanguageId { get; set; } + private static readonly List _visibilityAffectingProps = new List + { + nameof(NewsItem.Published), + nameof(NewsItem.StartDateUtc), + nameof(NewsItem.EndDateUtc), + nameof(NewsItem.LimitedToStores) + }; + + public static IReadOnlyCollection GetVisibilityAffectingPropertyNames() + { + return _visibilityAffectingProps; + } + + #endregion + + private ICollection _newsComments; /// /// Gets or sets the news title @@ -38,6 +52,25 @@ public partial class NewsItem : BaseEntity, ISlugSupported, IStoreMappingSupport /// public bool Published { get; set; } + /// + /// Gets or sets the media file identifier + /// + public int? MediaFileId { get; set; } + + /// Gets or sets the media file. + /// + public virtual MediaFile MediaFile { get; set; } + + /// + /// Gets or sets the preview media file identifier + /// + public int? PreviewMediaFileId { get; set; } + + /// + /// Gets or sets the preview media file. + /// + public virtual MediaFile PreviewMediaFile { get; set; } + /// /// Gets or sets the news item start date and time /// @@ -60,6 +93,7 @@ public partial class NewsItem : BaseEntity, ISlugSupported, IStoreMappingSupport /// /// public int ApprovedCommentCount { get; set; } + /// /// Gets or sets the total number of not approved comments /// The same as if we run newsItem.NewsComments.Where(n => !n.IsApproved).Count() @@ -68,10 +102,10 @@ public partial class NewsItem : BaseEntity, ISlugSupported, IStoreMappingSupport /// public int NotApprovedCommentCount { get; set; } - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - public bool LimitedToStores { get; set; } + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + public bool LimitedToStores { get; set; } /// /// Gets or sets the date and time of entity creation @@ -93,18 +127,24 @@ public partial class NewsItem : BaseEntity, ISlugSupported, IStoreMappingSupport /// public string MetaTitle { get; set; } + /// + /// Gets or sets a language identifier for which the news item should be displayed. + /// + [Index] + public int? LanguageId { get; set; } + + /// + /// Gets or sets the language. + /// + public virtual Language Language { get; set; } + /// /// Gets or sets the news comments /// public virtual ICollection NewsComments { - get { return _newsComments ?? (_newsComments = new HashSet()); } - protected set { _newsComments = value; } + get => _newsComments ?? (_newsComments = new HashSet()); + protected set => _newsComments = value; } - - /// - /// Gets or sets the language - /// - public virtual Language Language { get; set; } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/News/NewsSettings.cs b/src/Libraries/SmartStore.Core/Domain/News/NewsSettings.cs index ffceb3edab..236b10d1c0 100644 --- a/src/Libraries/SmartStore.Core/Domain/News/NewsSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/News/NewsSettings.cs @@ -4,25 +4,15 @@ namespace SmartStore.Core.Domain.News { public class NewsSettings : ISettings { - public NewsSettings() - { - Enabled = true; - AllowNotRegisteredUsersToLeaveComments = true; - ShowNewsOnMainPage = true; - MainPageNewsCount = 3; - NewsArchivePageSize = 10; - MaxAgeInDays = 180; - } - - /// + /// /// Gets or sets a value indicating whether news are enabled /// - public bool Enabled { get; set; } + public bool Enabled { get; set; } = true; /// /// Gets or sets a value indicating whether not registered user can leave comments /// - public bool AllowNotRegisteredUsersToLeaveComments { get; set; } + public bool AllowNotRegisteredUsersToLeaveComments { get; set; } = true; /// /// Gets or sets a value indicating whether to notify about new news comments @@ -32,26 +22,41 @@ public NewsSettings() /// /// Gets or sets a value indicating whether to show news on the main page /// - public bool ShowNewsOnMainPage { get; set; } + public bool ShowNewsOnMainPage { get; set; } = true; /// /// Gets or sets a value indicating news count displayed on the main page /// - public int MainPageNewsCount { get; set; } + public int MainPageNewsCount { get; set; } = 4; /// /// Gets or sets the page size for news archive /// - public int NewsArchivePageSize { get; set; } + public int NewsArchivePageSize { get; set; } = 9; - /// - /// The maximum age of news (in days) for RSS feed - /// - public int MaxAgeInDays { get; set; } + /// + /// The maximum age of news (in days) for RSS feed + /// + public int MaxAgeInDays { get; set; } = 180; /// /// Enable the news RSS feed link in customers browser address bar /// public bool ShowHeaderRssUrl { get; set; } + + /// + /// Gets or sets the meta title for news index page + /// + public string MetaTitle { get; set; } + + /// + /// Gets or sets the meta description for news index page + /// + public string MetaDescription { get; set; } + + /// + /// Gets or sets the meta keywords for news index page + /// + public string MetaKeywords { get; set; } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/CheckoutAttribute.cs b/src/Libraries/SmartStore.Core/Domain/Orders/CheckoutAttribute.cs index 63cbf58ea8..ca95d9b6df 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/CheckoutAttribute.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/CheckoutAttribute.cs @@ -5,17 +5,17 @@ namespace SmartStore.Core.Domain.Orders { - /// - /// Represents a checkout attribute - /// - public partial class CheckoutAttribute : BaseEntity, ILocalizedEntity, IStoreMappingSupported - { + /// + /// Represents a checkout attribute + /// + public partial class CheckoutAttribute : BaseEntity, ILocalizedEntity, IStoreMappingSupported + { private ICollection _checkoutAttributeValues; - public CheckoutAttribute() - { - this.IsActive = true; - } + public CheckoutAttribute() + { + this.IsActive = true; + } /// /// Gets or sets the name @@ -57,28 +57,36 @@ public CheckoutAttribute() /// public int DisplayOrder { get; set; } - /// - /// Gets or sets whether the checkout attribute is active - /// - public bool IsActive { get; set; } + /// + /// Gets or sets whether the checkout attribute is active + /// + public bool IsActive { get; set; } + + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + public bool LimitedToStores { get; set; } - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - public bool LimitedToStores { get; set; } + /// + /// Gets the attribute control type + /// + public AttributeControlType AttributeControlType + { + get => (AttributeControlType)this.AttributeControlTypeId; + set => this.AttributeControlTypeId = (int)value; + } - /// - /// Gets the attribute control type - /// - public AttributeControlType AttributeControlType + public bool IsListTypeAttribute() { - get + switch (AttributeControlType) { - return (AttributeControlType)this.AttributeControlTypeId; - } - set - { - this.AttributeControlTypeId = (int)value; + case AttributeControlType.Checkboxes: + case AttributeControlType.Boxes: + case AttributeControlType.DropdownList: + case AttributeControlType.RadioList: + return true; + default: + return false; } } @@ -87,9 +95,8 @@ public AttributeControlType AttributeControlType /// public virtual ICollection CheckoutAttributeValues { - get { return _checkoutAttributeValues ?? (_checkoutAttributeValues = new HashSet()); } - protected set { _checkoutAttributeValues = value; } + get => _checkoutAttributeValues ?? (_checkoutAttributeValues = new HashSet()); + protected set => _checkoutAttributeValues = value; } } - } diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/CheckoutAttributeValue.cs b/src/Libraries/SmartStore.Core/Domain/Orders/CheckoutAttributeValue.cs index 065fd323ad..d0f1e09bc0 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/CheckoutAttributeValue.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/CheckoutAttributeValue.cs @@ -1,5 +1,6 @@ - +using System.ComponentModel.DataAnnotations; using SmartStore.Core.Domain.Localization; +using SmartStore.Core.Domain.Media; namespace SmartStore.Core.Domain.Orders { @@ -38,10 +39,25 @@ public partial class CheckoutAttributeValue : BaseEntity, ILocalizedEntity /// public int DisplayOrder { get; set; } + /// + /// Gets or sets the media file identifier. + /// + public int? MediaFileId { get; set; } + + /// + /// Gets or sets the color RGB value (used with "Boxes" attribute type). + /// + [StringLength(100)] + public string Color { get; set; } + + /// + /// Gets or sets the media file. + /// + public virtual MediaFile MediaFile { get; set; } + /// /// Gets or sets the checkout attribute /// public virtual CheckoutAttribute CheckoutAttribute { get; set; } } - } diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/CheckoutEnums.cs b/src/Libraries/SmartStore.Core/Domain/Orders/CheckoutEnums.cs index 4ce792b336..1ba0d1c87e 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/CheckoutEnums.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/CheckoutEnums.cs @@ -1,45 +1,45 @@ namespace SmartStore.Core.Domain.Orders { - /// - /// Setting for newsletter subscription in checkout - /// - public enum CheckoutNewsLetterSubscription - { - /// - /// No newsletter subscription checkbox - /// - None = 0, + /// + /// Setting for newsletter subscription in checkout + /// + public enum CheckoutNewsLetterSubscription + { + /// + /// No newsletter subscription checkbox + /// + None = 0, - /// - /// Deactivated newsletter subscription checkbox - /// - Deactivated, + /// + /// Deactivated newsletter subscription checkbox + /// + Deactivated, - /// - /// Activated newsletter subscription checkbox - /// - Activated - } + /// + /// Activated newsletter subscription checkbox + /// + Activated + } - /// - /// Setting to hand over customer email to third party - /// - public enum CheckoutThirdPartyEmailHandOver - { - /// - /// No third party email hand over checkbox - /// - None = 0, + /// + /// Setting to hand over customer email to third party + /// + public enum CheckoutThirdPartyEmailHandOver + { + /// + /// No third party email hand over checkbox + /// + None = 0, - /// - /// Deactivated third party email hand over checkbox - /// - Deactivated, + /// + /// Deactivated third party email hand over checkbox + /// + Deactivated, - /// - /// Activated third party email hand over checkbox - /// - Activated - } + /// + /// Activated third party email hand over checkbox + /// + Activated + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/Events.cs b/src/Libraries/SmartStore.Core/Domain/Orders/Events.cs index e7b898754c..e85fe0da04 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/Events.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/Events.cs @@ -11,10 +11,7 @@ public OrderPaidEvent(Order order) this._order = order; } - public Order Order - { - get { return _order; } - } + public Order Order => _order; } public class OrderPlacedEvent @@ -26,53 +23,38 @@ public OrderPlacedEvent(Order order) this._order = order; } - public Order Order - { - get { return _order; } - } + public Order Order => _order; } - public class OrderUpdatedEvent - { - private readonly Order _order; + public class OrderUpdatedEvent + { + private readonly Order _order; - public OrderUpdatedEvent(Order order) - { - this._order = order; - } + public OrderUpdatedEvent(Order order) + { + this._order = order; + } - public Order Order - { - get { return _order; } - } - } + public Order Order => _order; + } - public class MigrateShoppingCartEvent - { - private readonly Customer _fromCustomer; - private readonly Customer _toCustomer; - private readonly int _storeId; + public class MigrateShoppingCartEvent + { + private readonly Customer _fromCustomer; + private readonly Customer _toCustomer; + private readonly int _storeId; - public MigrateShoppingCartEvent(Customer fromCustomer, Customer toCustomer, int storeId) - { - _fromCustomer = fromCustomer; - _toCustomer = toCustomer; - _storeId = storeId; - } + public MigrateShoppingCartEvent(Customer fromCustomer, Customer toCustomer, int storeId) + { + _fromCustomer = fromCustomer; + _toCustomer = toCustomer; + _storeId = storeId; + } - public Customer FromCustomer - { - get { return _fromCustomer; } - } + public Customer FromCustomer => _fromCustomer; - public Customer ToCustomer - { - get { return _toCustomer; } - } + public Customer ToCustomer => _toCustomer; - public int StoreId - { - get { return _storeId; } - } - } + public int StoreId => _storeId; + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/GiftCard.cs b/src/Libraries/SmartStore.Core/Domain/Orders/GiftCard.cs index 3f8de5f300..5d4f7542eb 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/GiftCard.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/GiftCard.cs @@ -10,7 +10,7 @@ namespace SmartStore.Core.Domain.Orders public partial class GiftCard : BaseEntity { private ICollection _giftCardUsageHistory; - + /// /// Gets or sets the associated order item identifier /// @@ -70,31 +70,25 @@ public partial class GiftCard : BaseEntity /// Gets or sets the date and time of instance creation /// public DateTime CreatedOnUtc { get; set; } - + /// /// Gets or sets the gift card usage history /// public virtual ICollection GiftCardUsageHistory { - get { return _giftCardUsageHistory ?? (_giftCardUsageHistory = new HashSet()); } - protected set { _giftCardUsageHistory = value; } + get => _giftCardUsageHistory ?? (_giftCardUsageHistory = new HashSet()); + protected set => _giftCardUsageHistory = value; } - + /// /// Gets or sets the gift card type /// - public GiftCardType GiftCardType + public GiftCardType GiftCardType { - get - { - return (GiftCardType)this.GiftCardTypeId; - } - set - { - this.GiftCardTypeId = (int)value; - } + get => (GiftCardType)this.GiftCardTypeId; + set => this.GiftCardTypeId = (int)value; } - + /// /// Gets or sets the associated order item /// @@ -129,15 +123,15 @@ public bool IsGiftCardValid(int storeId) if (!this.IsGiftCardActivated) return false; - if (storeId != 0 && - PurchasedWithOrderItemId.HasValue && PurchasedWithOrderItem != null && - PurchasedWithOrderItem.Order != null) - { - if (PurchasedWithOrderItem.Order.StoreId != storeId) - return false; - } + if (storeId != 0 && + PurchasedWithOrderItemId.HasValue && PurchasedWithOrderItem != null && + PurchasedWithOrderItem.Order != null) + { + if (PurchasedWithOrderItem.Order.StoreId != storeId) + return false; + } - decimal remainingAmount = GetGiftCardRemainingAmount(); + decimal remainingAmount = GetGiftCardRemainingAmount(); if (remainingAmount > decimal.Zero) return true; diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/GiftCardUsageHistory.cs b/src/Libraries/SmartStore.Core/Domain/Orders/GiftCardUsageHistory.cs index da836ce1e7..c1da377335 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/GiftCardUsageHistory.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/GiftCardUsageHistory.cs @@ -26,7 +26,7 @@ public partial class GiftCardUsageHistory : BaseEntity /// Gets or sets the date and time of instance creation /// public DateTime CreatedOnUtc { get; set; } - + /// /// Gets the gift card /// diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/Order.cs b/src/Libraries/SmartStore.Core/Domain/Orders/Order.cs index 44b1afa410..d32337103c 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/Order.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/Order.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Globalization; using System.Runtime.Serialization; -using SmartStore.Core.Domain.Affiliates; using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Discounts; @@ -18,10 +17,10 @@ namespace SmartStore.Core.Domain.Orders /// Represents an order /// [DataContract] - public partial class Order : BaseEntity, IAuditable, ISoftDeletable + public partial class Order : BaseEntity, IAuditable, ISoftDeletable { - private ICollection _walletHistory; - private ICollection _discountUsageHistory; + private ICollection _walletHistory; + private ICollection _discountUsageHistory; private ICollection _giftCardUsageHistory; private ICollection _orderNotes; private ICollection _orderItems; @@ -72,7 +71,7 @@ protected virtual SortedDictionary ParseTaxRates(string taxRat /// Gets or sets the (formatted) order number /// [DataMember] - public string OrderNumber { get; set; } + public string OrderNumber { get; set; } /// /// Gets or sets the order identifier @@ -80,29 +79,29 @@ protected virtual SortedDictionary ParseTaxRates(string taxRat [DataMember] public Guid OrderGuid { get; set; } - /// - /// Gets or sets the store identifier - /// - [DataMember] - public int StoreId { get; set; } + /// + /// Gets or sets the store identifier + /// + [DataMember] + public int StoreId { get; set; } /// /// Gets or sets the customer identifier /// [DataMember] - public int CustomerId { get; set; } + public int CustomerId { get; set; } /// /// Gets or sets the billing address identifier /// [DataMember] - public int BillingAddressId { get; set; } + public int BillingAddressId { get; set; } /// /// Gets or sets the shipping address identifier /// [DataMember] - public int? ShippingAddressId { get; set; } + public int? ShippingAddressId { get; set; } /// /// Gets or sets an order status identifier @@ -114,13 +113,13 @@ protected virtual SortedDictionary ParseTaxRates(string taxRat /// Gets or sets the shipping status identifier /// [DataMember] - public int ShippingStatusId { get; set; } + public int ShippingStatusId { get; set; } /// /// Gets or sets the payment status identifier /// [DataMember] - public int PaymentStatusId { get; set; } + public int PaymentStatusId { get; set; } /// /// Gets or sets the payment method system name @@ -144,7 +143,7 @@ protected virtual SortedDictionary ParseTaxRates(string taxRat /// Gets or sets the customer tax display type identifier /// [DataMember] - public virtual int CustomerTaxDisplayTypeId { get; set; } + public virtual int CustomerTaxDisplayTypeId { get; set; } /// /// Gets or sets the VAT number (the European Union Value Added Tax) @@ -188,11 +187,11 @@ protected virtual SortedDictionary ParseTaxRates(string taxRat [DataMember] public decimal OrderShippingExclTax { get; set; } - /// - /// Gets or sets the tax rate for order shipping - /// - [DataMember] - public decimal OrderShippingTaxRate { get; set; } + /// + /// Gets or sets the tax rate for order shipping + /// + [DataMember] + public decimal OrderShippingTaxRate { get; set; } /// /// Gets or sets the payment method additional fee (incl tax) @@ -206,11 +205,11 @@ protected virtual SortedDictionary ParseTaxRates(string taxRat [DataMember] public decimal PaymentMethodAdditionalFeeExclTax { get; set; } - /// - /// Gets or sets the tax rate for payment method additional fee - /// - [DataMember] - public decimal PaymentMethodAdditionalFeeTaxRate { get; set; } + /// + /// Gets or sets the tax rate for payment method additional fee + /// + [DataMember] + public decimal PaymentMethodAdditionalFeeTaxRate { get; set; } /// /// Gets or sets the tax rates @@ -230,16 +229,16 @@ protected virtual SortedDictionary ParseTaxRates(string taxRat [DataMember] public decimal OrderDiscount { get; set; } - /// - /// Gets or sets the wallet credit amount used to (partially) pay this order. - /// - [DataMember] - public decimal CreditBalance { get; set; } + /// + /// Gets or sets the wallet credit amount used to (partially) pay this order. + /// + [DataMember] + public decimal CreditBalance { get; set; } - /// - /// Gets or sets the order total rounding amount - /// - [DataMember] + /// + /// Gets or sets the order total rounding amount + /// + [DataMember] public decimal OrderTotalRounding { get; set; } /// @@ -254,12 +253,12 @@ protected virtual SortedDictionary ParseTaxRates(string taxRat [DataMember] public decimal RefundedAmount { get; set; } - /// - /// Gets or sets the value indicating whether reward points were earned for this order - /// - [DataMember] + /// + /// Gets or sets the value indicating whether reward points were earned for this order + /// + [DataMember] public bool RewardPointsWereAdded { get; set; } - + /// /// Gets or sets the checkout attribute description /// @@ -282,7 +281,7 @@ protected virtual SortedDictionary ParseTaxRates(string taxRat /// Gets or sets the affiliate identifier /// [DataMember] - public int AffiliateId { get; set; } + public int AffiliateId { get; set; } /// /// Gets or sets the customer IP address @@ -294,7 +293,7 @@ protected virtual SortedDictionary ParseTaxRates(string taxRat /// Gets or sets a value indicating whether storing of credit card number is allowed /// [DataMember] - public bool AllowStoringCreditCardNumber { get; set; } + public bool AllowStoringCreditCardNumber { get; set; } /// /// Gets or sets the card type @@ -382,61 +381,61 @@ protected virtual SortedDictionary ParseTaxRates(string taxRat /// Gets or sets the authorization transaction identifier /// [DataMember] - public string AuthorizationTransactionId { get; set; } + public string AuthorizationTransactionId { get; set; } /// /// Gets or sets the authorization transaction code /// [DataMember] - public string AuthorizationTransactionCode { get; set; } + public string AuthorizationTransactionCode { get; set; } /// /// Gets or sets the authorization transaction result /// [DataMember] - public string AuthorizationTransactionResult { get; set; } + public string AuthorizationTransactionResult { get; set; } /// /// Gets or sets the capture transaction identifier /// [DataMember] - public string CaptureTransactionId { get; set; } + public string CaptureTransactionId { get; set; } /// /// Gets or sets the capture transaction result /// [DataMember] - public string CaptureTransactionResult { get; set; } + public string CaptureTransactionResult { get; set; } /// /// Gets or sets the subscription transaction identifier /// [DataMember] - public string SubscriptionTransactionId { get; set; } + public string SubscriptionTransactionId { get; set; } /// /// Gets or sets the purchase order number /// [DataMember] - public string PurchaseOrderNumber { get; set; } + public string PurchaseOrderNumber { get; set; } /// /// Gets or sets the paid date and time /// [DataMember] - public DateTime? PaidDateUtc { get; set; } - + public DateTime? PaidDateUtc { get; set; } + /// /// Gets or sets the shipping method /// - [DataMember] - public string ShippingMethod { get; set; } + [DataMember] + public string ShippingMethod { get; set; } /// /// Gets or sets the shipping rate computation method identifier /// [DataMember] - public string ShippingRateComputationMethodSystemName { get; set; } + public string ShippingRateComputationMethodSystemName { get; set; } /// /// Gets or sets a value indicating whether the entity has been deleted @@ -448,75 +447,76 @@ protected virtual SortedDictionary ParseTaxRates(string taxRat /// Gets or sets the date and time of order creation /// [DataMember] - public DateTime CreatedOnUtc { get; set; } + public DateTime CreatedOnUtc { get; set; } - /// - /// Gets or sets the date and time when order was updated - /// - [DataMember] - public DateTime UpdatedOnUtc { get; set; } + /// + /// Gets or sets the date and time when order was updated + /// + [DataMember] + public DateTime UpdatedOnUtc { get; set; } - /// - /// Gets or sets the amount of remaing reward points - /// - [DataMember] - public int? RewardPointsRemaining { get; set; } + /// + /// Gets or sets the amount of remaing reward points + /// + [DataMember] + public int? RewardPointsRemaining { get; set; } - /// - /// Gets or sets a value indicating whether a new payment notification arrived (IPN, webhook, callback etc.) - /// - [DataMember] - public bool HasNewPaymentNotification { get; set; } + /// + /// Gets or sets a value indicating whether a new payment notification arrived (IPN, webhook, callback etc.) + /// + [DataMember] + public bool HasNewPaymentNotification { get; set; } - /// - /// Gets or sets a value indicating whether the customer accepted to hand over email address to third party - /// - [DataMember] - public bool AcceptThirdPartyEmailHandOver { get; set; } + /// + /// Gets or sets a value indicating whether the customer accepted to hand over email address to third party + /// + [DataMember] + public bool AcceptThirdPartyEmailHandOver { get; set; } - #endregion + #endregion - #region Navigation properties + #region Navigation properties - /// - /// Gets or sets the customer - /// - [DataMember] + /// + /// Gets or sets the customer + /// + [DataMember] public virtual Customer Customer { get; set; } /// /// Gets or sets the billing address /// [DataMember] - public virtual Address BillingAddress { get; set; } + public virtual Address BillingAddress { get; set; } /// /// Gets or sets the shipping address /// [DataMember] - public virtual Address ShippingAddress { get; set; } - + public virtual Address ShippingAddress { get; set; } + /// /// Gets or sets the reward points history record /// + [DataMember] public virtual RewardPointsHistory RedeemedRewardPointsEntry { get; set; } - /// - /// Gets or sets the wallet history. - /// - public virtual ICollection WalletHistory - { - get { return _walletHistory ?? (_walletHistory = new HashSet()); } - protected set { _walletHistory = value; } - } - - /// - /// Gets or sets discount usage history - /// - public virtual ICollection DiscountUsageHistory + /// + /// Gets or sets the wallet history. + /// + public virtual ICollection WalletHistory + { + get => _walletHistory ?? (_walletHistory = new HashSet()); + protected set => _walletHistory = value; + } + + /// + /// Gets or sets discount usage history + /// + public virtual ICollection DiscountUsageHistory { - get { return _discountUsageHistory ?? (_discountUsageHistory = new HashSet()); } - protected set { _discountUsageHistory = value; } + get => _discountUsageHistory ?? (_discountUsageHistory = new HashSet()); + protected set => _discountUsageHistory = value; } /// @@ -524,38 +524,38 @@ public virtual ICollection DiscountUsageHistory /// public virtual ICollection GiftCardUsageHistory { - get { return _giftCardUsageHistory ?? (_giftCardUsageHistory = new HashSet()); } - protected set { _giftCardUsageHistory = value; } + get => _giftCardUsageHistory ?? (_giftCardUsageHistory = new HashSet()); + protected set => _giftCardUsageHistory = value; } /// /// Gets or sets order notes /// [DataMember] - public virtual ICollection OrderNotes + public virtual ICollection OrderNotes { - get { return _orderNotes ?? (_orderNotes = new HashSet()); } - protected set { _orderNotes = value; } + get => _orderNotes ?? (_orderNotes = new HashSet()); + protected set => _orderNotes = value; } /// /// Gets or sets order items /// [DataMember] - public virtual ICollection OrderItems + public virtual ICollection OrderItems { - get { return _orderItems ?? (_orderItems = new HashSet()); } - protected internal set { _orderItems = value; } + get => _orderItems ?? (_orderItems = new HashSet()); + protected internal set => _orderItems = value; } /// /// Gets or sets shipments /// [DataMember] - public virtual ICollection Shipments + public virtual ICollection Shipments { - get { return _shipments ?? (_shipments = new HashSet()); } - protected set { _shipments = value; } + get => _shipments ?? (_shipments = new HashSet()); + protected set => _shipments = value; } #endregion @@ -566,77 +566,47 @@ public virtual ICollection Shipments /// Gets or sets the order status /// [DataMember] - public OrderStatus OrderStatus + public OrderStatus OrderStatus { - get - { - return (OrderStatus)this.OrderStatusId; - } - set - { - this.OrderStatusId = (int)value; - } + get => (OrderStatus)this.OrderStatusId; + set => this.OrderStatusId = (int)value; } /// /// Gets or sets the payment status /// [DataMember] - public PaymentStatus PaymentStatus + public PaymentStatus PaymentStatus { - get - { - return (PaymentStatus)this.PaymentStatusId; - } - set - { - this.PaymentStatusId = (int)value; - } + get => (PaymentStatus)this.PaymentStatusId; + set => this.PaymentStatusId = (int)value; } /// /// Gets or sets the shipping status /// [DataMember] - public ShippingStatus ShippingStatus + public ShippingStatus ShippingStatus { - get - { - return (ShippingStatus)this.ShippingStatusId; - } - set - { - this.ShippingStatusId = (int)value; - } + get => (ShippingStatus)this.ShippingStatusId; + set => this.ShippingStatusId = (int)value; } /// /// Gets or sets the customer tax display type /// [DataMember] - public TaxDisplayType CustomerTaxDisplayType + public TaxDisplayType CustomerTaxDisplayType { - get - { - return (TaxDisplayType)this.CustomerTaxDisplayTypeId; - } - set - { - this.CustomerTaxDisplayTypeId = (int)value; - } + get => (TaxDisplayType)this.CustomerTaxDisplayTypeId; + set => this.CustomerTaxDisplayTypeId = (int)value; } /// /// Gets the applied tax rates /// - public SortedDictionary TaxRatesDictionary - { - get - { - return ParseTaxRates(this.TaxRates); - } - } - + public SortedDictionary TaxRatesDictionary => ParseTaxRates(this.TaxRates); + #endregion } } diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/OrderItem.cs b/src/Libraries/SmartStore.Core/Domain/Orders/OrderItem.cs index 48ca396073..d81a8017f1 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/OrderItem.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/OrderItem.cs @@ -17,116 +17,116 @@ public partial class OrderItem : BaseEntity /// Gets or sets the order product variant identifier /// [DataMember] - public Guid OrderItemGuid { get; set; } + public Guid OrderItemGuid { get; set; } /// /// Gets or sets the order identifier /// [DataMember] - public int OrderId { get; set; } + public int OrderId { get; set; } /// /// Gets or sets the product identifier /// [DataMember] - public int ProductId { get; set; } + public int ProductId { get; set; } /// /// Gets or sets the quantity /// [DataMember] - public int Quantity { get; set; } + public int Quantity { get; set; } /// /// Gets or sets the unit price in primary store currency (incl tax) /// [DataMember] - public decimal UnitPriceInclTax { get; set; } + public decimal UnitPriceInclTax { get; set; } /// /// Gets or sets the unit price in primary store currency (excl tax) /// [DataMember] - public decimal UnitPriceExclTax { get; set; } + public decimal UnitPriceExclTax { get; set; } /// /// Gets or sets the price in primary store currency (incl tax) /// [DataMember] - public decimal PriceInclTax { get; set; } + public decimal PriceInclTax { get; set; } /// /// Gets or sets the price in primary store currency (excl tax) /// [DataMember] - public decimal PriceExclTax { get; set; } + public decimal PriceExclTax { get; set; } - /// - /// Gets or sets the tax rate - /// - [DataMember] - public decimal TaxRate { get; set; } + /// + /// Gets or sets the tax rate + /// + [DataMember] + public decimal TaxRate { get; set; } /// /// Gets or sets the discount amount (incl tax) /// [DataMember] - public decimal DiscountAmountInclTax { get; set; } + public decimal DiscountAmountInclTax { get; set; } /// /// Gets or sets the discount amount (excl tax) /// [DataMember] - public decimal DiscountAmountExclTax { get; set; } + public decimal DiscountAmountExclTax { get; set; } /// /// Gets or sets the attribute description /// [DataMember] - public string AttributeDescription { get; set; } + public string AttributeDescription { get; set; } /// /// Gets or sets the product variant attributes in XML format /// [DataMember] - public string AttributesXml { get; set; } - + public string AttributesXml { get; set; } + /// /// Gets or sets the download count /// - [DataMember] - public int DownloadCount { get; set; } + [DataMember] + public int DownloadCount { get; set; } /// /// Gets or sets a value indicating whether download is activated /// [DataMember] - public bool IsDownloadActivated { get; set; } + public bool IsDownloadActivated { get; set; } /// /// Gets or sets a license download identifier (in case this is a downloadable product) /// [DataMember] - public int? LicenseDownloadId { get; set; } - + public int? LicenseDownloadId { get; set; } + /// /// Gets or sets the total weight of one item /// It's nullable for compatibility with the previous version where was no such property /// - [DataMember] - public decimal? ItemWeight { get; set; } + [DataMember] + public decimal? ItemWeight { get; set; } - /// - /// Gets or sets extra bundle data - /// - [DataMember] - public string BundleData { get; set; } + /// + /// Gets or sets extra bundle data + /// + [DataMember] + public string BundleData { get; set; } - /// - /// Gets or sets the original product cost - /// - [DataMember] - public decimal ProductCost { get; set; } + /// + /// Gets or sets the original product cost + /// + [DataMember] + public decimal ProductCost { get; set; } /// /// Gets or sets the delivery time at the time of purchase. @@ -144,21 +144,21 @@ public partial class OrderItem : BaseEntity /// Gets the order /// [DataMember] - public virtual Order Order { get; set; } + public virtual Order Order { get; set; } /// /// Gets the product /// [DataMember] - public virtual Product Product { get; set; } + public virtual Product Product { get; set; } /// /// Gets or sets the associated gift card /// public virtual ICollection AssociatedGiftCards { - get { return _associatedGiftCards ?? (_associatedGiftCards = new HashSet()); } - protected set { _associatedGiftCards = value; } + get => _associatedGiftCards ?? (_associatedGiftCards = new HashSet()); + protected set => _associatedGiftCards = value; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/OrderNote.cs b/src/Libraries/SmartStore.Core/Domain/Orders/OrderNote.cs index f191c23b19..f406f42616 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/OrderNote.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/OrderNote.cs @@ -7,31 +7,31 @@ namespace SmartStore.Core.Domain.Orders /// Represents an order note /// [DataContract] - public partial class OrderNote : BaseEntity + public partial class OrderNote : BaseEntity { /// /// Gets or sets the order identifier /// [DataMember] - public int OrderId { get; set; } + public int OrderId { get; set; } /// /// Gets or sets the note /// [DataMember] - public string Note { get; set; } + public string Note { get; set; } /// /// Gets or sets a value indicating whether a customer can see a note /// [DataMember] - public bool DisplayToCustomer { get; set; } + public bool DisplayToCustomer { get; set; } /// /// Gets or sets the date and time of order note creation /// [DataMember] - public DateTime CreatedOnUtc { get; set; } + public DateTime CreatedOnUtc { get; set; } /// /// Gets the order diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/OrderSettings.cs b/src/Libraries/SmartStore.Core/Domain/Orders/OrderSettings.cs index 05e9a49fd4..fce240153c 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/OrderSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/OrderSettings.cs @@ -1,38 +1,44 @@ using SmartStore.Core.Configuration; -using SmartStore.Core.Domain.Localization; namespace SmartStore.Core.Domain.Orders { - public class OrderSettings : BaseEntity, ISettings, ILocalizedEntity + public class OrderSettings : ISettings { - public OrderSettings() - { - IsReOrderAllowed = true; - AnonymousCheckoutAllowed = true; - TermsOfServiceEnabled = true; - ReturnRequestsEnabled = true; - ReturnRequestActions = "Repair,Replacement,Store Credit"; - ReturnRequestReasons = "Received Wrong Product,Wrong Product Ordered,There Was A Problem With The Product"; - NumberOfDaysReturnRequestAvailable = 365; - MinimumOrderPlacementInterval = 30; - OrderListPageSize = 10; - } - - /// + public OrderSettings() + { + IsReOrderAllowed = true; + AnonymousCheckoutAllowed = true; + TermsOfServiceEnabled = true; + ReturnRequestsEnabled = true; + ReturnRequestActions = "Repair,Replacement,Store Credit"; + ReturnRequestReasons = "Received Wrong Product,Wrong Product Ordered,There Was A Problem With The Product"; + NumberOfDaysReturnRequestAvailable = 365; + MinimumOrderPlacementInterval = 30; + OrderListPageSize = 10; + RecurringPaymentListPageSize = 10; + } + + /// /// Gets or sets a value indicating whether customer can make re-order /// public bool IsReOrderAllowed { get; set; } /// - /// Gets or sets a minimum order subtotal amount + /// Gets or sets a minimum order total amount /// - public decimal MinOrderSubtotalAmount { get; set; } + public decimal? OrderTotalMinimum { get; set; } /// - /// Gets or sets a minimum order total amount + /// Gets or sets a maximum order total amount /// - public decimal MinOrderTotalAmount { get; set; } - + public decimal? OrderTotalMaximum { get; set; } + + /// + /// Gets or sets a value indicating how customer group restrictions are applied with each other + /// true - lowest possible total amount span gets applied, false - highest possible total amount span gets applied + /// + public bool MultipleOrderTotalRestrictionsExpandRange { get; set; } + /// /// Gets or sets a value indicating whether anonymous checkout allowed /// @@ -43,11 +49,11 @@ public OrderSettings() /// public bool TermsOfServiceEnabled { get; set; } - /// - /// Gets or sets a value indicating whether "Order completed" page should be skipped - /// - public bool DisableOrderCompletedPage { get; set; } - + /// + /// Gets or sets a value indicating whether "Order completed" page should be skipped + /// + public bool DisableOrderCompletedPage { get; set; } + /// /// Gets or sets a value indicating whether "Return requests" are allowed /// @@ -83,14 +89,19 @@ public OrderSettings() /// public int MinimumOrderPlacementInterval { get; set; } - /// - /// Gets or sets a value indicating whether to display all orders of all stores to a customer - /// - public bool DisplayOrdersOfAllStores { get; set; } + /// + /// Gets or sets a value indicating whether to display all orders of all stores to a customer + /// + public bool DisplayOrdersOfAllStores { get; set; } - /// - /// Page size of the order list - /// - public int OrderListPageSize { get; set; } - } + /// + /// Page size of the order list + /// + public int OrderListPageSize { get; set; } + + /// + /// Page size of the recurring payment list + /// + public int RecurringPaymentListPageSize { get; set; } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/OrganizedShoppingCartItem.cs b/src/Libraries/SmartStore.Core/Domain/Orders/OrganizedShoppingCartItem.cs index 41d38a8d0f..a052f20006 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/OrganizedShoppingCartItem.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/OrganizedShoppingCartItem.cs @@ -1,28 +1,27 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using SmartStore.Core.Domain.Catalog; namespace SmartStore.Core.Domain.Orders { - public partial class OrganizedShoppingCartItem - { - public OrganizedShoppingCartItem(ShoppingCartItem item) - { - Guard.NotNull(item, nameof(item)); + public partial class OrganizedShoppingCartItem + { + public OrganizedShoppingCartItem(ShoppingCartItem item) + { + Guard.NotNull(item, nameof(item)); - Item = item; // must not be null - ChildItems = new List(); - BundleItemData = new ProductBundleItemData(item.BundleItem); - CustomProperties = new Dictionary(); - } + Item = item; // must not be null + ChildItems = new List(); + BundleItemData = new ProductBundleItemData(item.BundleItem); + CustomProperties = new Dictionary(); + } - public ShoppingCartItem Item { get; private set; } - public IList ChildItems { get; set; } - public ProductBundleItemData BundleItemData { get; set; } + public ShoppingCartItem Item { get; private set; } + public IList ChildItems { get; set; } + public ProductBundleItemData BundleItemData { get; set; } - /// - /// Use this dictionary for any custom data required along cart processing - /// - public Dictionary CustomProperties { get; set; } - } + /// + /// Use this dictionary for any custom data required along cart processing + /// + public Dictionary CustomProperties { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/RecurringPayment.cs b/src/Libraries/SmartStore.Core/Domain/Orders/RecurringPayment.cs index 9861dc9302..02fce4bf05 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/RecurringPayment.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/RecurringPayment.cs @@ -50,7 +50,7 @@ public partial class RecurringPayment : BaseEntity, ISoftDeletable /// Gets or sets the date and time of payment creation /// public DateTime CreatedOnUtc { get; set; } - + /// /// Gets the next payment date /// @@ -121,31 +121,31 @@ public DateTime? NextPaymentDate //} //else //{ - if (historyCollection.Count > 0) - { - switch (this.CyclePeriod) - { - case RecurringProductCyclePeriod.Days: - result = this.StartDateUtc.AddDays((double)this.CycleLength * historyCollection.Count); - break; - case RecurringProductCyclePeriod.Weeks: - result = this.StartDateUtc.AddDays((double)(7 * this.CycleLength) * historyCollection.Count); - break; - case RecurringProductCyclePeriod.Months: - result = this.StartDateUtc.AddMonths(this.CycleLength * historyCollection.Count); - break; - case RecurringProductCyclePeriod.Years: - result = this.StartDateUtc.AddYears(this.CycleLength * historyCollection.Count); - break; - default: - throw new SmartException("Not supported cycle period"); - } - } - else + if (historyCollection.Count > 0) + { + switch (this.CyclePeriod) { - if (this.TotalCycles > 0) - result = this.StartDateUtc; + case RecurringProductCyclePeriod.Days: + result = this.StartDateUtc.AddDays((double)this.CycleLength * historyCollection.Count); + break; + case RecurringProductCyclePeriod.Weeks: + result = this.StartDateUtc.AddDays((double)(7 * this.CycleLength) * historyCollection.Count); + break; + case RecurringProductCyclePeriod.Months: + result = this.StartDateUtc.AddMonths(this.CycleLength * historyCollection.Count); + break; + case RecurringProductCyclePeriod.Years: + result = this.StartDateUtc.AddYears(this.CycleLength * historyCollection.Count); + break; + default: + throw new SmartException("Not supported cycle period"); } + } + else + { + if (this.TotalCycles > 0) + result = this.StartDateUtc; + } //} return result; @@ -174,16 +174,10 @@ public int CyclesRemaining /// public RecurringProductCyclePeriod CyclePeriod { - get - { - return (RecurringProductCyclePeriod)this.CyclePeriodId; - } - set - { - this.CyclePeriodId = (int)value; - } + get => (RecurringProductCyclePeriod)this.CyclePeriodId; + set => this.CyclePeriodId = (int)value; } - + @@ -192,9 +186,9 @@ public RecurringProductCyclePeriod CyclePeriod /// public virtual ICollection RecurringPaymentHistory { - get { return _recurringPaymentHistory ?? (_recurringPaymentHistory = new List()); } - protected set { _recurringPaymentHistory = value; } - } + get => _recurringPaymentHistory ?? (_recurringPaymentHistory = new List()); + protected set => _recurringPaymentHistory = value; + } /// /// Gets the initial order diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/RecurringPaymentHistory.cs b/src/Libraries/SmartStore.Core/Domain/Orders/RecurringPaymentHistory.cs index 457bb9806d..4755d07c6a 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/RecurringPaymentHistory.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/RecurringPaymentHistory.cs @@ -16,12 +16,12 @@ public partial class RecurringPaymentHistory : BaseEntity /// Gets or sets the recurring payment identifier /// public int OrderId { get; set; } - + /// /// Gets or sets the date and time of entity creation /// public DateTime CreatedOnUtc { get; set; } - + /// /// Gets the recurring payment /// diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/ReturnRequest.cs b/src/Libraries/SmartStore.Core/Domain/Orders/ReturnRequest.cs index 888c2ac83e..c876feff76 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/ReturnRequest.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/ReturnRequest.cs @@ -4,116 +4,110 @@ namespace SmartStore.Core.Domain.Orders { - /// - /// Represents a return request - /// - [DataContract] - public partial class ReturnRequest : BaseEntity, IAuditable - { - /// - /// Gets or sets the store identifier - /// - [DataMember] - public int StoreId { get; set; } + /// + /// Represents a return request + /// + [DataContract] + public partial class ReturnRequest : BaseEntity, IAuditable + { + /// + /// Gets or sets the store identifier + /// + [DataMember] + public int StoreId { get; set; } /// /// Gets or sets the order item identifier /// [DataMember] - public int OrderItemId { get; set; } + public int OrderItemId { get; set; } /// /// Gets or sets the customer identifier /// [DataMember] - public int CustomerId { get; set; } + public int CustomerId { get; set; } /// /// Gets or sets the quantity /// [DataMember] - public int Quantity { get; set; } + public int Quantity { get; set; } /// /// Gets or sets the reason to return /// [DataMember] - public string ReasonForReturn { get; set; } + public string ReasonForReturn { get; set; } /// /// Gets or sets the requested action /// [DataMember] - public string RequestedAction { get; set; } + public string RequestedAction { get; set; } - /// - /// Gets or sets the date and time when requested action was last updated - /// - [DataMember] - public DateTime? RequestedActionUpdatedOnUtc { get; set; } + /// + /// Gets or sets the date and time when requested action was last updated + /// + [DataMember] + public DateTime? RequestedActionUpdatedOnUtc { get; set; } /// /// Gets or sets the customer comments /// [DataMember] - public string CustomerComments { get; set; } + public string CustomerComments { get; set; } /// /// Gets or sets the staff notes /// [DataMember] - public string StaffNotes { get; set; } + public string StaffNotes { get; set; } - /// - /// Gets or sets the admin comment - /// - [DataMember] - public string AdminComment { get; set; } + /// + /// Gets or sets the admin comment + /// + [DataMember] + public string AdminComment { get; set; } /// /// Gets or sets the return status identifier /// [DataMember] - public int ReturnRequestStatusId { get; set; } + public int ReturnRequestStatusId { get; set; } - /// - /// Gets or sets whether to refund to wallet. - /// - [DataMember] - public bool? RefundToWallet { get; set; } + /// + /// Gets or sets whether to refund to wallet. + /// + [DataMember] + public bool? RefundToWallet { get; set; } - /// - /// Gets or sets the date and time of entity creation - /// - [DataMember] - public DateTime CreatedOnUtc { get; set; } + /// + /// Gets or sets the date and time of entity creation + /// + [DataMember] + public DateTime CreatedOnUtc { get; set; } /// /// Gets or sets the date and time of entity update /// [DataMember] - public DateTime UpdatedOnUtc { get; set; } - + public DateTime UpdatedOnUtc { get; set; } + /// /// Gets or sets the return status /// - [DataMember] - public ReturnRequestStatus ReturnRequestStatus + [DataMember] + public ReturnRequestStatus ReturnRequestStatus { - get - { - return (ReturnRequestStatus)this.ReturnRequestStatusId; - } - set - { - this.ReturnRequestStatusId = (int)value; - } + get => (ReturnRequestStatus)this.ReturnRequestStatusId; + set => this.ReturnRequestStatusId = (int)value; } /// /// Gets or sets the customer /// [DataMember] - public virtual Customer Customer { get; set; } + public virtual Customer Customer { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/ShoppingCartItem.cs b/src/Libraries/SmartStore.Core/Domain/Orders/ShoppingCartItem.cs index a7c3c91273..3ceba8c27d 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/ShoppingCartItem.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/ShoppingCartItem.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Customers; @@ -9,21 +8,21 @@ namespace SmartStore.Core.Domain.Orders /// Represents a shopping cart item /// public partial class ShoppingCartItem : BaseEntity, IAuditable - { - /// - /// Gets or sets the store identifier - /// - public int StoreId { get; set; } + { + /// + /// Gets or sets the store identifier + /// + public int StoreId { get; set; } - /// - /// The parent shopping cart item id - /// - public int? ParentItemId { get; set; } + /// + /// The parent shopping cart item id + /// + public int? ParentItemId { get; set; } - /// - /// Gets or sets ths bundle item identifier - /// - public int? BundleItemId { get; set; } + /// + /// Gets or sets ths bundle item identifier + /// + public int? BundleItemId { get; set; } /// /// Gets or sets the shopping cart type identifier @@ -65,19 +64,13 @@ public partial class ShoppingCartItem : BaseEntity, IAuditable /// public DateTime UpdatedOnUtc { get; set; } - /// - /// Gets or sets the shopping cart type - /// - public ShoppingCartType ShoppingCartType + /// + /// Gets or sets the shopping cart type + /// + public ShoppingCartType ShoppingCartType { - get - { - return (ShoppingCartType)this.ShoppingCartTypeId; - } - set - { - this.ShoppingCartTypeId = (int)value; - } + get => (ShoppingCartType)this.ShoppingCartTypeId; + set => this.ShoppingCartTypeId = (int)value; } /// @@ -90,10 +83,10 @@ public ShoppingCartType ShoppingCartType /// public virtual Customer Customer { get; set; } - /// - /// Gets or sets the product bundle item - /// - public virtual ProductBundleItem BundleItem { get; set; } + /// + /// Gets or sets the product bundle item + /// + public virtual ProductBundleItem BundleItem { get; set; } /// /// Gets a value indicating whether the shopping cart item is free shipping @@ -102,9 +95,9 @@ public bool IsFreeShipping { get { - var product = this.Product; - if (product != null) - return product.IsFreeShipping; + var product = this.Product; + if (product != null) + return product.IsFreeShipping; return true; } } @@ -116,9 +109,9 @@ public bool IsShipEnabled { get { - var product = this.Product; - if (product != null) - return product.IsShipEnabled; + var product = this.Product; + if (product != null) + return product.IsShipEnabled; return false; } } @@ -130,9 +123,9 @@ public bool IsTaxExempt { get { - var product = this.Product; - if (product != null) - return product.IsTaxExempt; + var product = this.Product; + if (product != null) + return product.IsTaxExempt; return false; } } diff --git a/src/Libraries/SmartStore.Core/Domain/Orders/ShoppingCartSettings.cs b/src/Libraries/SmartStore.Core/Domain/Orders/ShoppingCartSettings.cs index e569f371b7..0c9fec1eca 100644 --- a/src/Libraries/SmartStore.Core/Domain/Orders/ShoppingCartSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Orders/ShoppingCartSettings.cs @@ -1,32 +1,33 @@ using SmartStore.Core.Configuration; -using SmartStore.Core.Domain.Localization; +using SmartStore.Core.Domain.Directory; namespace SmartStore.Core.Domain.Orders { - public class ShoppingCartSettings : BaseEntity, ISettings, ILocalizedEntity - { - public ShoppingCartSettings() - { - MaximumShoppingCartItems = 1000; - MaximumWishlistItems = 1000; - MoveItemsFromWishlistToCart = true; - ShowItemsFromWishlistToCartButton = true; - ShowProductImagesOnShoppingCart = true; - ShowProductBundleImagesOnShoppingCart = true; - ShowProductImagesOnWishList = true; - ShowProductBundleImagesOnWishList = true; - ShowDiscountBox = true; - ShowGiftCardBox = true; + public class ShoppingCartSettings : ISettings + { + public ShoppingCartSettings() + { + MaximumShoppingCartItems = 1000; + MaximumWishlistItems = 1000; + MoveItemsFromWishlistToCart = true; + ShowItemsFromWishlistToCartButton = true; + ShowProductImagesOnShoppingCart = true; + ShowProductBundleImagesOnShoppingCart = true; + ShowProductImagesOnWishList = true; + ShowProductBundleImagesOnWishList = true; + ShowDiscountBox = true; + ShowGiftCardBox = true; ShowCommentBox = true; - ShowEsdRevocationWaiverBox = true; - CrossSellsNumber = 8; - EmailWishlistEnabled = true; - MiniShoppingCartEnabled = true; - ShowProductImagesInMiniShoppingCart = true; + ShowEsdRevocationWaiverBox = true; + CrossSellsNumber = 8; + EmailWishlistEnabled = true; + MiniShoppingCartEnabled = true; + ShowProductImagesInMiniShoppingCart = true; ShowBasePrice = true; - ShowDeliveryTimes = true; + DeliveryTimesInShoppingCart = DeliveryTimesPresentation.DateOnly; ShowShortDesc = true; AllowAnonymousUsersToEmailWishlist = false; + AddProductsToBasketInSinglePositions = false; } /// @@ -43,7 +44,7 @@ public ShoppingCartSettings() /// Gets or sets a value indicating maximum number of items in the shopping cart /// public int MaximumShoppingCartItems { get; set; } - + /// /// Gets or sets a value indicating maximum number of items in the wishlist /// @@ -59,30 +60,30 @@ public ShoppingCartSettings() /// public bool MoveItemsFromWishlistToCart { get; set; } - /// - /// Gets or sets a value indicating whether to show the items from wishlist to ahopping cart button. - /// - public bool ShowItemsFromWishlistToCartButton { get; set; } + /// + /// Gets or sets a value indicating whether to show the items from wishlist to ahopping cart button. + /// + public bool ShowItemsFromWishlistToCartButton { get; set; } /// /// Gets or sets a value indicating whether to show product image on shopping cart page /// public bool ShowProductImagesOnShoppingCart { get; set; } - /// - /// Gets or sets a value indicating whether to show product bundle images on shopping cart page - /// - public bool ShowProductBundleImagesOnShoppingCart { get; set; } + /// + /// Gets or sets a value indicating whether to show product bundle images on shopping cart page + /// + public bool ShowProductBundleImagesOnShoppingCart { get; set; } /// /// Gets or sets a value indicating whether to show product image on wishlist page /// public bool ShowProductImagesOnWishList { get; set; } - /// - /// Gets or sets a value indicating whether to show product image on wishlist page - /// - public bool ShowProductBundleImagesOnWishList { get; set; } + /// + /// Gets or sets a value indicating whether to show product image on wishlist page + /// + public bool ShowProductBundleImagesOnWishList { get; set; } /// /// Gets or sets a value indicating whether to show discount box on shopping cart page @@ -99,31 +100,31 @@ public ShoppingCartSettings() /// public bool ShowCommentBox { get; set; } - /// - /// Gets or sets a value indicating whether to show a revocation waiver checkbox box for ESD products - /// - public bool ShowEsdRevocationWaiverBox { get; set; } + /// + /// Gets or sets a value indicating whether to show a revocation waiver checkbox box for ESD products + /// + public bool ShowEsdRevocationWaiverBox { get; set; } - /// - /// Gets or sets a value indicating whether to show a checkbox to subscribe to newsletters - /// - public CheckoutNewsLetterSubscription NewsLetterSubscription { get; set; } + /// + /// Gets or sets a value indicating whether to show a checkbox to subscribe to newsletters + /// + public CheckoutNewsLetterSubscription NewsLetterSubscription { get; set; } - /// - /// Gets or sets a value indicating whether to show a checkbox to let the customer accept to hand over email address to third party - /// - public CheckoutThirdPartyEmailHandOver ThirdPartyEmailHandOver { get; set; } + /// + /// Gets or sets a value indicating whether to show a checkbox to let the customer accept to hand over email address to third party + /// + public CheckoutThirdPartyEmailHandOver ThirdPartyEmailHandOver { get; set; } - /// - /// Gets or sets the label to accept to hand over the email to third party - /// - public string ThirdPartyEmailHandOverLabel { get; set; } + /// + /// Gets or sets the label to accept to hand over the email to third party + /// + public string ThirdPartyEmailHandOverLabel { get; set; } + + /// + /// Gets or sets a number of "Cross-sells" on shopping cart page + /// + public int CrossSellsNumber { get; set; } - /// - /// Gets or sets a number of "Cross-sells" on shopping cart page - /// - public int CrossSellsNumber { get; set; } - /// /// Gets or sets a value indicating whether "email a wishlist" feature is enabled /// @@ -133,7 +134,7 @@ public ShoppingCartSettings() /// Gets or sets a value indicating whether to enabled "email a wishlist" for anonymous users. /// public bool AllowAnonymousUsersToEmailWishlist { get; set; } - + /// Gets or sets a value indicating whether mini-shopping cart is enabled /// public bool MiniShoppingCartEnabled { get; set; } @@ -149,9 +150,9 @@ public ShoppingCartSettings() public bool ShowConfirmOrderLegalHint { get; set; } /// - /// Gets or sets a value indicating whether to show delivery times in the order summary + /// Specifies the presentation of delivery times in shopping cart. /// - public bool ShowDeliveryTimes { get; set; } + public DeliveryTimesPresentation DeliveryTimesInShoppingCart { get; set; } /// /// Gets or sets a value indicating whether to show the product short description in the order summary @@ -168,9 +169,14 @@ public ShoppingCartSettings() /// public bool ShowBasePrice { get; set; } - /// - /// Gets or sets a value indicating whether to show the quantity of linked attribute value product + /// + /// Gets or sets a value indicating whether to show the quantity of linked attribute value product + /// + public bool ShowLinkedAttributeValueQuantity { get; set; } + + /// + /// Gets or sets a value indicating whether to add products to the basket in single positions /// - public bool ShowLinkedAttributeValueQuantity { get; set; } + public bool AddProductsToBasketInSinglePositions { get; set; } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Payments/CapturePaymentReason.cs b/src/Libraries/SmartStore.Core/Domain/Payments/CapturePaymentReason.cs index 39d599f41d..96fa8b0323 100644 --- a/src/Libraries/SmartStore.Core/Domain/Payments/CapturePaymentReason.cs +++ b/src/Libraries/SmartStore.Core/Domain/Payments/CapturePaymentReason.cs @@ -1,18 +1,18 @@ namespace SmartStore.Core.Domain.Payments { - /// - /// The reason for automatic capturing of the payment amount. - /// - public enum CapturePaymentReason - { - /// - /// Capture payment because the order has been marked as shipped. - /// - OrderShipped = 0, + /// + /// The reason for automatic capturing of the payment amount. + /// + public enum CapturePaymentReason + { + /// + /// Capture payment because the order has been marked as shipped. + /// + OrderShipped = 0, - /// - /// Capture payment because the order has been marked as delivered. - /// - OrderDelivered - } + /// + /// Capture payment because the order has been marked as delivered. + /// + OrderDelivered + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Payments/PaymentMethod.cs b/src/Libraries/SmartStore.Core/Domain/Payments/PaymentMethod.cs index ba4b86162d..5427dcdd9c 100644 --- a/src/Libraries/SmartStore.Core/Domain/Payments/PaymentMethod.cs +++ b/src/Libraries/SmartStore.Core/Domain/Payments/PaymentMethod.cs @@ -1,26 +1,30 @@ -using System.Runtime.Serialization; +using System.Collections.Generic; +using System.Runtime.Serialization; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Stores; +using SmartStore.Rules.Domain; namespace SmartStore.Core.Domain.Payments { - /// - /// Represents a payment method - /// - [DataContract] - public partial class PaymentMethod : BaseEntity, ILocalizedEntity, IStoreMappingSupported - { - /// - /// Gets or sets the payment method system name - /// - [DataMember] - public string PaymentMethodSystemName { get; set; } + /// + /// Represents a payment method + /// + [DataContract] + public partial class PaymentMethod : BaseEntity, ILocalizedEntity, IStoreMappingSupported, IRulesContainer + { + private ICollection _ruleSets; - /// - /// Gets or sets the full description - /// - [DataMember] - public string FullDescription { get; set; } + /// + /// Gets or sets the payment method system name + /// + [DataMember] + public string PaymentMethodSystemName { get; set; } + + /// + /// Gets or sets the full description + /// + [DataMember] + public string FullDescription { get; set; } /// /// Gets or sets a value indicating whether to round the order total. Also known as "Cash rounding". @@ -29,10 +33,19 @@ public partial class PaymentMethod : BaseEntity, ILocalizedEntity, IStoreMapping [DataMember] public bool RoundOrderTotalEnabled { get; set; } - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - [DataMember] - public bool LimitedToStores { get; set; } - } + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + [DataMember] + public bool LimitedToStores { get; set; } + + /// + /// Gets or sets assigned rule sets. + /// + public virtual ICollection RuleSets + { + get => _ruleSets ?? (_ruleSets = new HashSet()); + protected set => _ruleSets = value; + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Payments/PaymentSettings.cs b/src/Libraries/SmartStore.Core/Domain/Payments/PaymentSettings.cs index 7aad6a5667..5d3533c27d 100644 --- a/src/Libraries/SmartStore.Core/Domain/Payments/PaymentSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Payments/PaymentSettings.cs @@ -8,8 +8,8 @@ public class PaymentSettings : ISettings public PaymentSettings() { ActivePaymentMethodSystemNames = new List(); - AllowRePostingPayments = true; - BypassPaymentMethodSelectionIfOnlyOne = false; + AllowRePostingPayments = true; + BypassPaymentMethodSelectionIfOnlyOne = false; } /// @@ -27,19 +27,19 @@ public PaymentSettings() /// public bool BypassPaymentMethodSelectionIfOnlyOne { get; set; } - /// - /// Gets or sets a value indicating whether we should bypass the payment method info page - /// - public bool BypassPaymentMethodInfo { get; set; } + /// + /// Gets or sets a value indicating whether we should bypass the payment method info page + /// + public bool BypassPaymentMethodInfo { get; set; } - /// - /// Gets or sets the reason for automatic payment capturing - /// - public CapturePaymentReason? CapturePaymentReason { get; set; } + /// + /// Gets or sets the reason for automatic payment capturing + /// + public CapturePaymentReason? CapturePaymentReason { get; set; } - /// - /// Gets or sets the identifier of the currency in which the wallet is kept. - /// - public int WalletCurrencyId { get; set; } - } + /// + /// Gets or sets the identifier of the currency in which the wallet is kept. + /// + public int WalletCurrencyId { get; set; } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Polls/Poll.cs b/src/Libraries/SmartStore.Core/Domain/Polls/Poll.cs index 73c2d2481f..d3d51d806a 100644 --- a/src/Libraries/SmartStore.Core/Domain/Polls/Poll.cs +++ b/src/Libraries/SmartStore.Core/Domain/Polls/Poll.cs @@ -57,20 +57,20 @@ public partial class Poll : BaseEntity, IStoreMappingSupported /// public DateTime? EndDateUtc { get; set; } - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - public bool LimitedToStores { get; set; } - + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + public bool LimitedToStores { get; set; } + /// /// Gets or sets the news comments /// public virtual ICollection PollAnswers { - get { return _pollAnswers ?? (_pollAnswers = new HashSet()); } - protected set { _pollAnswers = value; } + get => _pollAnswers ?? (_pollAnswers = new HashSet()); + protected set => _pollAnswers = value; } - + /// /// Gets or sets the language /// diff --git a/src/Libraries/SmartStore.Core/Domain/Polls/PollAnswer.cs b/src/Libraries/SmartStore.Core/Domain/Polls/PollAnswer.cs index 86fb99cb53..af8dfde475 100644 --- a/src/Libraries/SmartStore.Core/Domain/Polls/PollAnswer.cs +++ b/src/Libraries/SmartStore.Core/Domain/Polls/PollAnswer.cs @@ -28,7 +28,7 @@ public partial class PollAnswer : BaseEntity /// Gets or sets the display order /// public int DisplayOrder { get; set; } - + /// /// Gets or sets the poll /// @@ -39,8 +39,8 @@ public partial class PollAnswer : BaseEntity /// public virtual ICollection PollVotingRecords { - get { return _pollVotingRecords ?? (_pollVotingRecords = new HashSet()); } - protected set { _pollVotingRecords = value; } + get => _pollVotingRecords ?? (_pollVotingRecords = new HashSet()); + protected set => _pollVotingRecords = value; } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Security/AclRecord.cs b/src/Libraries/SmartStore.Core/Domain/Security/AclRecord.cs index 7bd0cf40e3..ff8f04dede 100644 --- a/src/Libraries/SmartStore.Core/Domain/Security/AclRecord.cs +++ b/src/Libraries/SmartStore.Core/Domain/Security/AclRecord.cs @@ -21,17 +21,17 @@ public partial class AclRecord : BaseEntity /// public int CustomerRoleId { get; set; } - /// - /// Gets or sets the customer role - /// - public virtual CustomerRole CustomerRole { get; set; } + /// + /// Gets or sets the customer role + /// + public virtual CustomerRole CustomerRole { get; set; } - /// - /// Gets or sets a value indicating whether the entry is idle - /// - /// - /// An entry is idle when it's related entity has been soft-deleted - /// - public bool IsIdle { get; set; } + /// + /// Gets or sets a value indicating whether the entry is idle + /// + /// + /// An entry is idle when it's related entity has been soft-deleted + /// + public bool IsIdle { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Security/DefaultPermissionRecord.cs b/src/Libraries/SmartStore.Core/Domain/Security/DefaultPermissionRecord.cs index ff123674a2..1cb5af8b56 100644 --- a/src/Libraries/SmartStore.Core/Domain/Security/DefaultPermissionRecord.cs +++ b/src/Libraries/SmartStore.Core/Domain/Security/DefaultPermissionRecord.cs @@ -7,9 +7,9 @@ namespace SmartStore.Core.Domain.Security /// public class DefaultPermissionRecord { - public DefaultPermissionRecord() + public DefaultPermissionRecord() { - this.PermissionRecords = new List(); + PermissionRecords = new List(); } /// diff --git a/src/Libraries/SmartStore.Core/Domain/Security/IAclSupported.cs b/src/Libraries/SmartStore.Core/Domain/Security/IAclSupported.cs index 872f42384d..60661ea0df 100644 --- a/src/Libraries/SmartStore.Core/Domain/Security/IAclSupported.cs +++ b/src/Libraries/SmartStore.Core/Domain/Security/IAclSupported.cs @@ -5,9 +5,9 @@ namespace SmartStore.Core.Domain.Security /// public partial interface IAclSupported { - /// - /// Gets or sets a value indicating whether the entity is subject to ACL - /// - bool SubjectToAcl { get; set; } + /// + /// Gets or sets a value indicating whether the entity is subject to ACL + /// + bool SubjectToAcl { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Security/PermissionNode.cs b/src/Libraries/SmartStore.Core/Domain/Security/PermissionNode.cs new file mode 100644 index 0000000000..4e06bf00ab --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Security/PermissionNode.cs @@ -0,0 +1,19 @@ +using System; + +namespace SmartStore.Core.Domain.Security +{ + public interface IPermissionNode + { + int PermissionRecordId { get; } + string SystemName { get; } + bool? Allow { get; } + } + + [Serializable] + public class PermissionNode : IPermissionNode + { + public int PermissionRecordId { get; set; } + public string SystemName { get; set; } + public bool? Allow { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Security/PermissionRecord.cs b/src/Libraries/SmartStore.Core/Domain/Security/PermissionRecord.cs index 067a942459..aa2f64a941 100644 --- a/src/Libraries/SmartStore.Core/Domain/Security/PermissionRecord.cs +++ b/src/Libraries/SmartStore.Core/Domain/Security/PermissionRecord.cs @@ -1,37 +1,28 @@ using System.Collections.Generic; -using SmartStore.Core.Domain.Customers; +using System.ComponentModel.DataAnnotations.Schema; namespace SmartStore.Core.Domain.Security { /// - /// Represents a permission record + /// Represents a permission record. /// public class PermissionRecord : BaseEntity { - private ICollection _customerRoles; + private ICollection _permissionRoleMappings; /// - /// Gets or sets the permission name - /// - public string Name { get; set; } - - /// - /// Gets or sets the permission system name + /// Gets or sets the permission system name. /// + [Index] public string SystemName { get; set; } - - /// - /// Gets or sets the permission category - /// - public string Category { get; set; } - + /// - /// Gets or sets discount usage history + /// Gets or sets permission role mappings. /// - public virtual ICollection CustomerRoles + public virtual ICollection PermissionRoleMappings { - get { return _customerRoles ?? (_customerRoles = new HashSet()); } - protected set { _customerRoles = value; } - } + get => _permissionRoleMappings ?? (_permissionRoleMappings = new HashSet()); + protected set => _permissionRoleMappings = value; + } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Security/PermissionRoleMapping.cs b/src/Libraries/SmartStore.Core/Domain/Security/PermissionRoleMapping.cs new file mode 100644 index 0000000000..888c667aa6 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Security/PermissionRoleMapping.cs @@ -0,0 +1,39 @@ +using System.ComponentModel.DataAnnotations.Schema; +using SmartStore.Core.Domain.Customers; + +namespace SmartStore.Core.Domain.Security +{ + /// + /// Represents a permission to role mapping. + /// + [Table("PermissionRoleMapping")] + public partial class PermissionRoleMapping : BaseEntity + { + /// + /// Gets or sets whether the permission is granted. + /// + public bool Allow { get; set; } + + /// + /// Gets or sets the permission record id. + /// + public int PermissionRecordId { get; set; } + + /// + /// Gets or sets the permission record. + /// + [ForeignKey("PermissionRecordId")] + public virtual PermissionRecord PermissionRecord { get; set; } + + /// + /// Gets or sets the customer role identifier. + /// + public int CustomerRoleId { get; set; } + + /// + /// Gets or sets the customer role. + /// + [ForeignKey("CustomerRoleId")] + public virtual CustomerRole CustomerRole { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Core/Domain/Security/SecuritySettings.cs b/src/Libraries/SmartStore.Core/Domain/Security/SecuritySettings.cs index 71999bd5ba..66f00dad16 100644 --- a/src/Libraries/SmartStore.Core/Domain/Security/SecuritySettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Security/SecuritySettings.cs @@ -6,21 +6,21 @@ namespace SmartStore.Core.Domain.Security { public class SecuritySettings : ISettings { - public SecuritySettings() - { - EncryptionKey = CommonHelper.GenerateRandomDigitCode(16); - AdminAreaAllowedIpAddresses = new List(); - } + public SecuritySettings() + { + EncryptionKey = CommonHelper.GenerateRandomDigitCode(16); + AdminAreaAllowedIpAddresses = new List(); + } - /// - /// When true, bypasses any SSL redirection on localhost - /// - public bool UseSslOnLocalhost { get; set; } + /// + /// When true, bypasses any SSL redirection on localhost + /// + public bool UseSslOnLocalhost { get; set; } - /// - /// Gets or sets an encryption key - /// - public string EncryptionKey { get; set; } + /// + /// Gets or sets an encryption key + /// + public string EncryptionKey { get; set; } /// /// Gets or sets a list of adminn area allowed IP addresses @@ -32,9 +32,9 @@ public SecuritySettings() /// public bool HideAdminMenuItemsBasedOnPermissions { get; set; } - /// - /// Gets or sets a vaule indicating whether "Honeypot" is enabled to prevent bots from posting forms. - /// - public bool EnableHoneypotProtection { get; set; } + /// + /// Gets or sets a vaule indicating whether "Honeypot" is enabled to prevent bots from posting forms. + /// + public bool EnableHoneypotProtection { get; set; } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Seo/CanonicalHostNameRule.cs b/src/Libraries/SmartStore.Core/Domain/Seo/CanonicalHostNameRule.cs index 7e64e097aa..19fe7a890d 100644 --- a/src/Libraries/SmartStore.Core/Domain/Seo/CanonicalHostNameRule.cs +++ b/src/Libraries/SmartStore.Core/Domain/Seo/CanonicalHostNameRule.cs @@ -1,13 +1,12 @@ namespace SmartStore.Core.Domain.Seo { - - public enum CanonicalHostNameRule + public enum CanonicalHostNameRule { /// /// Doesn't matter (as requested) /// NoRule, - /// + /// /// The www prefix is required (www.myshop.com is default host) /// RequireWww, @@ -16,5 +15,4 @@ public enum CanonicalHostNameRule /// OmitWww } - -} +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Seo/HomePageSettings.cs b/src/Libraries/SmartStore.Core/Domain/Seo/HomePageSettings.cs new file mode 100644 index 0000000000..418b4ed90f --- /dev/null +++ b/src/Libraries/SmartStore.Core/Domain/Seo/HomePageSettings.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using SmartStore.Core.Configuration; + +namespace SmartStore.Core.Domain.Seo +{ + public class HomePageSettings : ISettings + { + public string MetaTitle { get; set; } + public string MetaDescription { get; set; } + public string MetaKeywords { get; set; } + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Seo/ISlugSupported.cs b/src/Libraries/SmartStore.Core/Domain/Seo/ISlugSupported.cs index 6b52764cec..154a2c2b32 100644 --- a/src/Libraries/SmartStore.Core/Domain/Seo/ISlugSupported.cs +++ b/src/Libraries/SmartStore.Core/Domain/Seo/ISlugSupported.cs @@ -6,5 +6,5 @@ namespace SmartStore.Core.Domain.Seo /// public interface ISlugSupported { - } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Seo/SeoSettings.cs b/src/Libraries/SmartStore.Core/Domain/Seo/SeoSettings.cs index 9d1d5f5bce..09c0ca7f5a 100644 --- a/src/Libraries/SmartStore.Core/Domain/Seo/SeoSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Seo/SeoSettings.cs @@ -4,84 +4,75 @@ namespace SmartStore.Core.Domain.Seo { - public class SeoSettings : ISettings + public class SeoSettings : ISettings { - public SeoSettings() - { - PageTitleSeparator = ". "; - PageTitleSeoAdjustment = PageTitleSeoAdjustment.PagenameAfterStorename; - DefaultTitle = "Shop"; - DefaultMetaKeywords = ""; - DefaultMetaDescription = ""; - AllowUnicodeCharsInUrls = false; - CanonicalHostNameRule = CanonicalHostNameRule.NoRule; - LoadAllUrlAliasesOnStartup = true; - - XmlSitemapEnabled = true; - XmlSitemapIncludesCategories = true; - XmlSitemapIncludesManufacturers = true; - XmlSitemapIncludesProducts = true; - XmlSitemapIncludesTopics = true; - - ExtraRobotsDisallows = new List { "/blog/tag/", "/blog/month/", "/producttags/" }; - - ReservedUrlRecordSlugs = new List - { - "admin", - "install", - "recentlyviewedproducts", - "newproducts", - "compareproducts", - "clearcomparelist", - "setproductreviewhelpfulness", - "login", - "register", - "logout", - "cart", - "wishlist", - "emailwishlist", - "checkout", - "contactus", - "passwordrecovery", - "subscribenewsletter", - "blog", - "boards", - "inboxupdate", - "sentupdate", - "news", - "sitemap", - "sitemapseo", - "search", - "config", - "api", - "odata" - }; - - SeoNameCharConversion = string.Join(Environment.NewLine, new List - { - "ä;ae", - "ö;oe", - "ü;ue", - "Ä;Ae", - "Ö;Oe", - "Ü;Ue", - "ß;ss" - }); - } - - public string PageTitleSeparator { get; set; } - public PageTitleSeoAdjustment PageTitleSeoAdjustment { get; set; } - public string DefaultTitle { get; set; } - public string DefaultMetaKeywords { get; set; } - public string DefaultMetaDescription { get; set; } - public string MetaRobotsContent { get; set; } - - public bool ConvertNonWesternChars { get; set; } + public SeoSettings() + { + ExtraRobotsDisallows = new List { "/blog/tag/", "/blog/month/", "/producttags/" }; + ExtraRobotsAllows = new List(); + + ReservedUrlRecordSlugs = new List + { + "admin", + "install", + "recentlyviewedproducts", + "newproducts", + "compareproducts", + "clearcomparelist", + "setproductreviewhelpfulness", + "login", + "register", + "logout", + "cart", + "wishlist", + "emailwishlist", + "checkout", + "contactus", + "passwordrecovery", + "subscribenewsletter", + "blog", + "boards", + "inboxupdate", + "sentupdate", + "news", + "sitemap", + "sitemapseo", + "search", + "config", + "api", + "odata" + }; + + SeoNameCharConversion = string.Join(Environment.NewLine, new List + { + "ä;ae", + "ö;oe", + "ü;ue", + "Ä;Ae", + "Ö;Oe", + "Ü;Ue", + "ß;ss" + }); + } + + public string PageTitleSeparator { get; set; } = ". "; + public PageTitleSeoAdjustment PageTitleSeoAdjustment { get; set; } = PageTitleSeoAdjustment.PagenameAfterStorename; + + /// + /// Gets or sets the default meta title for the shop. + /// + public string MetaTitle { get; set; } = "Shop"; + public string MetaDescription { get; set; } = string.Empty; + public string MetaKeywords { get; set; } = string.Empty; + + public string MetaRobotsContent { get; set; } + + public bool ConvertNonWesternChars { get; set; } public bool AllowUnicodeCharsInUrls { get; set; } - public string SeoNameCharConversion { get; set; } + public string SeoNameCharConversion { get; set; } - public bool CanonicalUrlsEnabled { get; set; } - public CanonicalHostNameRule CanonicalHostNameRule { get; set; } + public bool CanonicalUrlsEnabled { get; set; } + public CanonicalHostNameRule CanonicalHostNameRule { get; set; } = CanonicalHostNameRule.NoRule; /// /// Slugs (sename) reserved for some other needs @@ -90,21 +81,28 @@ public SeoSettings() public List ExtraRobotsDisallows { get; set; } - /// - /// A value indicating whether to load all URL records and active slugs on application startup - /// - public bool LoadAllUrlAliasesOnStartup { get; set; } + public List ExtraRobotsAllows { get; set; } + + /// + /// A value indicating whether to load all URL records and active slugs on application startup + /// + public bool LoadAllUrlAliasesOnStartup { get; set; } = true; + + public bool RedirectLegacyTopicUrls { get; set; } + + #region XML Sitemap - public bool RedirectLegacyTopicUrls { get; set; } + public bool XmlSitemapEnabled { get; set; } = true; + public bool XmlSitemapIncludesCategories { get; set; } = true; + public bool XmlSitemapIncludesManufacturers { get; set; } = true; + public bool XmlSitemapIncludesProducts { get; set; } = true; + public bool XmlSitemapIncludesTopics { get; set; } = true; + public bool XmlSitemapIncludesBlog { get; set; } = true; + public bool XmlSitemapIncludesNews { get; set; } = true; + public bool XmlSitemapIncludesForum { get; set; } = true; - #region XML Sitemap + #endregion - public bool XmlSitemapEnabled { get; set; } - public bool XmlSitemapIncludesCategories { get; set; } - public bool XmlSitemapIncludesManufacturers { get; set; } - public bool XmlSitemapIncludesProducts { get; set; } - public bool XmlSitemapIncludesTopics { get; set; } - #endregion - } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Seo/UrlRecord.cs b/src/Libraries/SmartStore.Core/Domain/Seo/UrlRecord.cs index 8022e4dcfc..11640bc967 100644 --- a/src/Libraries/SmartStore.Core/Domain/Seo/UrlRecord.cs +++ b/src/Libraries/SmartStore.Core/Domain/Seo/UrlRecord.cs @@ -6,36 +6,36 @@ namespace SmartStore.Core.Domain.Seo /// Represents an URL record /// [DataContract] - public partial class UrlRecord : BaseEntity + public partial class UrlRecord : BaseEntity { /// /// Gets or sets the entity identifier /// [DataMember] - public int EntityId { get; set; } + public int EntityId { get; set; } /// /// Gets or sets the entity name /// [DataMember] - public string EntityName { get; set; } + public string EntityName { get; set; } /// /// Gets or sets the slug /// [DataMember] - public string Slug { get; set; } + public string Slug { get; set; } /// /// Gets or sets the value indicating whether the record is active /// [DataMember] - public bool IsActive { get; set; } + public bool IsActive { get; set; } /// /// Gets or sets the language identifier /// [DataMember] - public int LanguageId { get; set; } + public int LanguageId { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Seo/UrlRecordCollection.cs b/src/Libraries/SmartStore.Core/Domain/Seo/UrlRecordCollection.cs index 733e800f7a..3f765dd5d8 100644 --- a/src/Libraries/SmartStore.Core/Domain/Seo/UrlRecordCollection.cs +++ b/src/Libraries/SmartStore.Core/Domain/Seo/UrlRecordCollection.cs @@ -6,90 +6,97 @@ namespace SmartStore.Core.Domain.Seo { public class UrlRecordCollection : IReadOnlyCollection - { - private readonly string _entityName; - private readonly IDictionary _dict; - private HashSet _requestedSet; - - public UrlRecordCollection(string entityName, int[] requestedSet, IEnumerable items) - { - Guard.NotEmpty(entityName, nameof(entityName)); - Guard.NotNull(items, nameof(items)); - - _entityName = entityName; - _dict = items.ToDictionarySafe(x => CreateKey(x.EntityId, x.LanguageId)); - - if (requestedSet != null && requestedSet.Length > 0) - { - _requestedSet = new HashSet(requestedSet); - } - } - - public void MergeWith(UrlRecordCollection other) - { - Guard.NotNull(other, nameof(other)); - - if (!this._entityName.IsCaseInsensitiveEqual(other._entityName)) - { - throw new InvalidOperationException("Expected group '{0}', but was '{1}'".FormatInvariant(this._entityName, other._entityName)); - } - - // Merge dictionary - other._dict.Merge(this._dict, true); - - // Merge requested set (entity ids) - if (this._requestedSet != null) - { - if (other._requestedSet == null) - { - other._requestedSet = new HashSet(this._requestedSet); - } - else - { - other._requestedSet.AddRange(this._requestedSet); - } - } - } - - public string GetSlug(int languageId, int entityId, bool returnDefaultValue = true) - { - var slug = Find(languageId, entityId)?.Slug; - + { + private readonly string _entityName; + private readonly IDictionary _dict; + private HashSet _requestedSet; + + public UrlRecordCollection(string entityName, int[] requestedSet, IEnumerable items) + { + Guard.NotEmpty(entityName, nameof(entityName)); + Guard.NotNull(items, nameof(items)); + + _entityName = entityName; + _dict = items.ToDictionarySafe(x => CreateKey(x.EntityId, x.LanguageId)); + + if (requestedSet != null && requestedSet.Length > 0) + { + _requestedSet = new HashSet(requestedSet); + } + } + + public void MergeWith(UrlRecordCollection other) + { + Guard.NotNull(other, nameof(other)); + + if (!this._entityName.IsCaseInsensitiveEqual(other._entityName)) + { + throw new InvalidOperationException("Expected group '{0}', but was '{1}'".FormatInvariant(this._entityName, other._entityName)); + } + + // Merge dictionary + other._dict.Merge(this._dict, true); + + // Merge requested set (entity ids) + if (this._requestedSet != null) + { + if (other._requestedSet == null) + { + other._requestedSet = new HashSet(this._requestedSet); + } + else + { + other._requestedSet.AddRange(this._requestedSet); + } + } + } + + public string GetSlug(int languageId, int entityId, bool returnDefaultValue = true) + { + var slug = Find(languageId, entityId)?.Slug; + if (returnDefaultValue && languageId != 0 && string.IsNullOrEmpty(slug)) { slug = Find(0, entityId)?.Slug; } return slug; - } - - public UrlRecord Find(int languageId, int entityId) - { - var item = _dict.Get(CreateKey(entityId, languageId)); - - if (item == null && (_requestedSet == null || _requestedSet.Contains(entityId))) - { - // Although the item does not exist in the local dictionary it has been requested - // from the database, which means it does not exist in the db either. - // Avoid the upcoming roundtrip. - return new UrlRecord - { - EntityName = _entityName, - EntityId = entityId, - LanguageId = languageId - }; - } - - return item; - } - - private string CreateKey(int entityId, int languageId) - { - return string.Concat(entityId.ToString(CultureInfo.InvariantCulture), "-", languageId); - } - - public int Count => _dict.Values.Count; - public IEnumerator GetEnumerator() => _dict.Values.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => _dict.Values.GetEnumerator(); - } + } + + public UrlRecord Find(int languageId, int entityId) + { + var item = _dict.Get(CreateKey(entityId, languageId)); + + if (item == null && (_requestedSet == null || _requestedSet.Contains(entityId))) + { + // Although the item does not exist in the local dictionary it has been requested + // from the database, which means it does not exist in the db either. + // Avoid the upcoming roundtrip. + return new UrlRecord + { + EntityName = _entityName, + EntityId = entityId, + LanguageId = languageId + }; + } + + return item; + } + + private string CreateKey(int entityId, int languageId) + { + return string.Concat(entityId.ToString(CultureInfo.InvariantCulture), "-", languageId); + } + + public int Count => _dict.Values.Count; + public IEnumerator GetEnumerator() + { + return _dict.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _dict.Values.GetEnumerator(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Shipping/Shipment.cs b/src/Libraries/SmartStore.Core/Domain/Shipping/Shipment.cs index e189e22b12..d172210689 100644 --- a/src/Libraries/SmartStore.Core/Domain/Shipping/Shipment.cs +++ b/src/Libraries/SmartStore.Core/Domain/Shipping/Shipment.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; -using SmartStore.Core.Domain.Orders; +using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; +using SmartStore.Core.Domain.Orders; namespace SmartStore.Core.Domain.Shipping { @@ -9,7 +10,7 @@ namespace SmartStore.Core.Domain.Shipping /// Represents a shipment /// [DataContract] - public partial class Shipment : BaseEntity + public partial class Shipment : BaseEntity { private ICollection _shipmentItems; @@ -17,44 +18,51 @@ public partial class Shipment : BaseEntity /// Gets or sets the order identifier /// [DataMember] - public int OrderId { get; set; } - + public int OrderId { get; set; } + /// /// Gets or sets the tracking number of this shipment + /// + [DataMember] + public string TrackingNumber { get; set; } + + /// + /// Gets or sets the tracking URL. /// [DataMember] - public string TrackingNumber { get; set; } + [StringLength(2000)] + public string TrackingUrl { get; set; } /// /// Gets or sets the total weight of this shipment - /// It's nullable for compatibility with the previous version of SmartStore.NET where was no such property + /// It's nullable for compatibility with the previous version of Smartstore where was no such property /// [DataMember] - public decimal? TotalWeight { get; set; } + public decimal? TotalWeight { get; set; } /// /// Gets or sets the shipped date and time /// [DataMember] - public DateTime? ShippedDateUtc { get; set; } + public DateTime? ShippedDateUtc { get; set; } /// /// Gets or sets the delivery date and time /// [DataMember] - public DateTime? DeliveryDateUtc { get; set; } + public DateTime? DeliveryDateUtc { get; set; } /// /// Gets or sets the entity creation date /// [DataMember] - public DateTime CreatedOnUtc { get; set; } + public DateTime CreatedOnUtc { get; set; } /// /// Gets the order /// [DataMember] - public virtual Order Order { get; set; } + public virtual Order Order { get; set; } /// /// Gets or sets the shipment items @@ -62,8 +70,8 @@ public partial class Shipment : BaseEntity [DataMember] public virtual ICollection ShipmentItems { - get { return _shipmentItems ?? (_shipmentItems = new HashSet()); } - protected set { _shipmentItems = value; } + get => _shipmentItems ?? (_shipmentItems = new HashSet()); + protected set => _shipmentItems = value; } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Shipping/ShippingMethod.cs b/src/Libraries/SmartStore.Core/Domain/Shipping/ShippingMethod.cs index ca5d7b1207..09cc57c627 100644 --- a/src/Libraries/SmartStore.Core/Domain/Shipping/ShippingMethod.cs +++ b/src/Libraries/SmartStore.Core/Domain/Shipping/ShippingMethod.cs @@ -1,43 +1,56 @@ +using System.Collections.Generic; using System.Runtime.Serialization; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Stores; +using SmartStore.Rules.Domain; namespace SmartStore.Core.Domain.Shipping { - /// - /// Represents a shipping method (used for offline shipping rate computation methods) - /// - [DataContract] - public partial class ShippingMethod : BaseEntity, ILocalizedEntity, IStoreMappingSupported - { + /// + /// Represents a shipping method (used for offline shipping rate computation methods) + /// + [DataContract] + public partial class ShippingMethod : BaseEntity, ILocalizedEntity, IStoreMappingSupported, IRulesContainer + { + private ICollection _ruleSets; + /// /// Gets or sets the name /// [DataMember] - public string Name { get; set; } + public string Name { get; set; } /// /// Gets or sets the description /// [DataMember] - public string Description { get; set; } + public string Description { get; set; } /// /// Gets or sets the display order /// [DataMember] - public int DisplayOrder { get; set; } + public int DisplayOrder { get; set; } - /// - /// Gets or sets whether to ignore charges - /// - [DataMember] - public bool IgnoreCharges { get; set; } + /// + /// Gets or sets whether to ignore charges + /// + [DataMember] + public bool IgnoreCharges { get; set; } - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - [DataMember] - public bool LimitedToStores { get; set; } + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + [DataMember] + public bool LimitedToStores { get; set; } + + /// + /// Gets or sets assigned rule sets. + /// + public virtual ICollection RuleSets + { + get => _ruleSets ?? (_ruleSets = new HashSet()); + protected set => _ruleSets = value; + } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/Shipping/ShippingOption.cs b/src/Libraries/SmartStore.Core/Domain/Shipping/ShippingOption.cs index 5edb06f415..ec5c564e45 100644 --- a/src/Libraries/SmartStore.Core/Domain/Shipping/ShippingOption.cs +++ b/src/Libraries/SmartStore.Core/Domain/Shipping/ShippingOption.cs @@ -1,11 +1,3 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Globalization; -using System.IO; -using System.Text; -using System.Xml.Serialization; - namespace SmartStore.Core.Domain.Shipping { /// @@ -13,10 +5,10 @@ namespace SmartStore.Core.Domain.Shipping /// public partial class ShippingOption { - /// - /// Shipping method identifier - /// - public int ShippingMethodId { get; set; } + /// + /// Shipping method identifier + /// + public int ShippingMethodId { get; set; } /// /// Gets or sets the system name of shipping rate computation method diff --git a/src/Libraries/SmartStore.Core/Domain/Shipping/ShippingSettings.cs b/src/Libraries/SmartStore.Core/Domain/Shipping/ShippingSettings.cs index 1dc2bfbf54..2138f1c115 100644 --- a/src/Libraries/SmartStore.Core/Domain/Shipping/ShippingSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Shipping/ShippingSettings.cs @@ -7,9 +7,11 @@ public class ShippingSettings : ISettings { public ShippingSettings() { - ActiveShippingRateComputationMethodSystemNames = new List() { "Shipping.FixedRate" }; - EstimateShippingEnabled = true; - ReturnValidOptionsIfThereAreAny = true; + ActiveShippingRateComputationMethodSystemNames = new List { "Shipping.FixedRate" }; + EstimateShippingEnabled = true; + ReturnValidOptionsIfThereAreAny = true; + TodayShipmentHour = 14; + DeliveryOnWorkweekDaysOnly = true; } /// @@ -62,5 +64,24 @@ public ShippingSettings() /// Gets or sets a value indicating whether to charge only the highest shipping surcharge of products /// public bool ChargeOnlyHighestProductShippingSurcharge { get; set; } + + #region Delivery Time + + /// + /// Specifies a custom formatting pattern for delivery time dates (e.g. "M"). + /// + public string DeliveryTimesDateFormat { get; set; } + + /// + /// Specifies the hour (value between 1 and 24) by which the order will be shipped the same day. + /// + public int? TodayShipmentHour { get; set; } + + /// + /// Specifies whether delivery takes place only on workweek days. + /// + public bool DeliveryOnWorkweekDaysOnly { get; set; } + + #endregion } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Domain/StoreInformationSettings.cs b/src/Libraries/SmartStore.Core/Domain/StoreInformationSettings.cs index b486b2acc3..ccab383818 100644 --- a/src/Libraries/SmartStore.Core/Domain/StoreInformationSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/StoreInformationSettings.cs @@ -4,12 +4,12 @@ namespace SmartStore.Core.Domain { public class StoreInformationSettings : ISettings { - public StoreInformationSettings() - { - StoreClosedAllowForAdmins = true; - } - - /// + public StoreInformationSettings() + { + StoreClosedAllowForAdmins = true; + } + + /// /// Gets or sets a value indicating whether store is closed /// public bool StoreClosed { get; set; } diff --git a/src/Libraries/SmartStore.Core/Domain/Stores/IStoreMappingSupported.cs b/src/Libraries/SmartStore.Core/Domain/Stores/IStoreMappingSupported.cs index 9303ccab4c..e3aaf3c44c 100644 --- a/src/Libraries/SmartStore.Core/Domain/Stores/IStoreMappingSupported.cs +++ b/src/Libraries/SmartStore.Core/Domain/Stores/IStoreMappingSupported.cs @@ -1,13 +1,13 @@ namespace SmartStore.Core.Domain.Stores { - /// - /// Represents an entity which supports store mapping - /// - public partial interface IStoreMappingSupported - { - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - bool LimitedToStores { get; set; } - } + /// + /// Represents an entity which supports store mapping + /// + public partial interface IStoreMappingSupported + { + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + bool LimitedToStores { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Stores/Store.cs b/src/Libraries/SmartStore.Core/Domain/Stores/Store.cs index d9325aa60a..52d602cb97 100644 --- a/src/Libraries/SmartStore.Core/Domain/Stores/Store.cs +++ b/src/Libraries/SmartStore.Core/Domain/Stores/Store.cs @@ -3,156 +3,186 @@ namespace SmartStore.Core.Domain.Stores { - /// - /// Represents a store - /// - [DataContract] - public partial class Store : BaseEntity - { - /// - /// Gets or sets the store name - /// - [DataMember] - public string Name { get; set; } - - /// - /// Gets or sets the store URL - /// - [DataMember] - public string Url { get; set; } - - /// - /// Gets or sets a value indicating whether SSL is enabled - /// - [DataMember] - public bool SslEnabled { get; set; } - - /// - /// Gets or sets the store secure URL (HTTPS) - /// - [DataMember] - public string SecureUrl { get; set; } - - /// - /// Gets or sets a value indicating whether all pages will be forced to use SSL (no matter of a specified [RequireHttpsByConfigAttribute] attribute) - /// - [DataMember] - public bool ForceSslForAllPages { get; set; } - - /// - /// Gets or sets the comma separated list of possible HTTP_HOST values - /// - [DataMember] - public string Hosts { get; set; } - - /// - /// Gets or sets the logo picture id - /// - [DataMember] - public int LogoPictureId { get; set; } - - /// - /// Gets or sets the display order - /// - [DataMember] - public int DisplayOrder { get; set; } - - /// - /// Gets or sets a store specific id for the HTML body - /// - [DataMember] - public string HtmlBodyId { get; set; } - - /// - /// Gets or sets the CDN host name, if static media content should be served through a CDN. - /// - [DataMember] - public string ContentDeliveryNetwork { get; set; } - - /// - /// Gets or sets the primary store currency identifier - /// - [DataMember] - public int PrimaryStoreCurrencyId { get; set; } - - /// - /// Gets or sets the primary exchange rate currency identifier - /// - [DataMember] - public int PrimaryExchangeRateCurrencyId { get; set; } - - /// - /// Gets or sets the primary store currency - /// - [DataMember] - public virtual Currency PrimaryStoreCurrency { get; set; } - - /// - /// Gets or sets the primary exchange rate currency - /// - [DataMember] - public virtual Currency PrimaryExchangeRateCurrency { get; set; } - - - /// - /// Gets the security mode for the store - /// - public virtual HttpSecurityMode GetSecurityMode(bool? useSsl = null) - { - if (useSsl ?? SslEnabled) - { - if (SecureUrl.HasValue() && Url.HasValue() && !Url.StartsWith("https")) - { - return HttpSecurityMode.SharedSsl; - } - else - { - return HttpSecurityMode.Ssl; - } - } - - return HttpSecurityMode.Unsecured; - } - - private string _secureHost; - private string _unsecureHost; - - /// - /// Gets the store host name - /// - /// - /// If false, returns the default unsecured url. - /// If true, returns the secure url, but only if SSL is enabled for the store. - /// - /// The host name - public string GetHost(bool secure) - { - return secure - ? _secureHost ?? (_secureHost = GetHostInternal(true)) - : _unsecureHost ?? (_unsecureHost = GetHostInternal(false)); - } - - private string GetHostInternal(bool secure) - { - var host = string.Empty; - - if (secure && SslEnabled) - { - if (SecureUrl.HasValue()) - { - host = SecureUrl; - } - else - { - host = Url.Replace("http:/", "https:/"); - } - } - else - { - host = Url; - } - - return host.EnsureEndsWith("/"); - } - } + /// + /// Represents a store + /// + [DataContract] + public partial class Store : BaseEntity + { + /// + /// Gets or sets the store name + /// + [DataMember] + public string Name { get; set; } + + /// + /// Gets or sets the store URL + /// + [DataMember] + public string Url { get; set; } + + /// + /// Gets or sets a value indicating whether SSL is enabled + /// + [DataMember] + public bool SslEnabled { get; set; } + + /// + /// Gets or sets the store secure URL (HTTPS) + /// + [DataMember] + public string SecureUrl { get; set; } + + /// + /// Gets or sets a value indicating whether all pages will be forced to use SSL (no matter of a specified [RequireHttpsByConfigAttribute] attribute) + /// + [DataMember] + public bool ForceSslForAllPages { get; set; } + + /// + /// Gets or sets the comma separated list of possible HTTP_HOST values + /// + [DataMember] + public string Hosts { get; set; } + + /// + /// Gets or sets the logo media file id + /// + [DataMember] + public int LogoMediaFileId { get; set; } + + /// + /// Gets or sets the png icon media file id + /// + [DataMember] + public int? FavIconMediaFileId { get; set; } + + /// + /// Gets or sets the png icon media file id + /// + [DataMember] + public int? PngIconMediaFileId { get; set; } + + /// + /// Gets or sets the apple touch icon media file id + /// + [DataMember] + public int? AppleTouchIconMediaFileId { get; set; } + + /// + /// Gets or sets the ms tile image media file id + /// + [DataMember] + public int? MsTileImageMediaFileId { get; set; } + + /// + /// Gets or sets the ms tile color + /// + [DataMember] + public string MsTileColor { get; set; } + + /// + /// Gets or sets the display order + /// + [DataMember] + public int DisplayOrder { get; set; } + + /// + /// Gets or sets a store specific id for the HTML body + /// + [DataMember] + public string HtmlBodyId { get; set; } + + /// + /// Gets or sets the CDN host name, if static media content should be served through a CDN. + /// + [DataMember] + public string ContentDeliveryNetwork { get; set; } + + /// + /// Gets or sets the primary store currency identifier + /// + [DataMember] + public int PrimaryStoreCurrencyId { get; set; } + + /// + /// Gets or sets the primary exchange rate currency identifier + /// + [DataMember] + public int PrimaryExchangeRateCurrencyId { get; set; } + + /// + /// Gets or sets the primary store currency + /// + [DataMember] + public virtual Currency PrimaryStoreCurrency { get; set; } + + /// + /// Gets or sets the primary exchange rate currency + /// + [DataMember] + public virtual Currency PrimaryExchangeRateCurrency { get; set; } + + + /// + /// Gets the security mode for the store + /// + public virtual HttpSecurityMode GetSecurityMode(bool? useSsl = null) + { + if (useSsl ?? SslEnabled) + { + if (SecureUrl.HasValue() && Url.HasValue() && !Url.StartsWith("https")) + { + return HttpSecurityMode.SharedSsl; + } + else + { + return HttpSecurityMode.Ssl; + } + } + + return HttpSecurityMode.Unsecured; + } + + private string _secureHost; + private string _unsecureHost; + + /// + /// Gets the store host name + /// + /// + /// If false, returns the default unsecured url. + /// If true, returns the secure url, but only if SSL is enabled for the store. + /// + /// The host name + public string GetHost(bool secure) + { + return secure + ? _secureHost ?? (_secureHost = GetHostInternal(true)) + : _unsecureHost ?? (_unsecureHost = GetHostInternal(false)); + } + + private string GetHostInternal(bool secure) + { + var host = string.Empty; + + if (secure && SslEnabled) + { + if (SecureUrl.HasValue()) + { + host = SecureUrl; + } + else + { + host = Url.Replace("http:/", "https:/"); + } + } + else + { + host = Url; + } + + return host.EnsureEndsWith("/"); + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Stores/StoreExtensions.cs b/src/Libraries/SmartStore.Core/Domain/Stores/StoreExtensions.cs index 1185f4b399..80e71808e7 100644 --- a/src/Libraries/SmartStore.Core/Domain/Stores/StoreExtensions.cs +++ b/src/Libraries/SmartStore.Core/Domain/Stores/StoreExtensions.cs @@ -1,50 +1,49 @@ using System; using System.Linq; -using System.Collections.Generic; namespace SmartStore.Core.Domain.Stores { - public static class StoreExtensions - { - /// - /// Indicates whether a store contains a specified host - /// - /// Store - /// Host - /// true - contains, false - no - public static bool ContainsHostValue(this Store store, string host) - { - Guard.NotNull(store, nameof(store)); + public static class StoreExtensions + { + /// + /// Indicates whether a store contains a specified host + /// + /// Store + /// Host + /// true - contains, false - no + public static bool ContainsHostValue(this Store store, string host) + { + Guard.NotNull(store, nameof(store)); - if (string.IsNullOrEmpty(host)) - { - return false; - } + if (string.IsNullOrEmpty(host)) + { + return false; + } - var contains = store.ParseHostValues() - .FirstOrDefault(x => x.Equals(host, StringComparison.InvariantCultureIgnoreCase)) != null; - return contains; - } + var contains = store.ParseHostValues() + .FirstOrDefault(x => x.Equals(host, StringComparison.InvariantCultureIgnoreCase)) != null; + return contains; + } - /// - /// Parse comma-separated hosts - /// - /// Store - /// Comma-separated hosts - public static string[] ParseHostValues(this Store store) - { - Guard.NotNull(store, nameof(store)); + /// + /// Parse comma-separated hosts + /// + /// Store + /// Comma-separated hosts + public static string[] ParseHostValues(this Store store) + { + Guard.NotNull(store, nameof(store)); - if (string.IsNullOrWhiteSpace(store.Hosts)) - { - return Array.Empty(); - } + if (string.IsNullOrWhiteSpace(store.Hosts)) + { + return Array.Empty(); + } - return store.Hosts - .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(host => host.Trim()) - .Where(host => !string.IsNullOrWhiteSpace(host)) - .ToArray(); - } - } + return store.Hosts + .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(host => host.Trim()) + .Where(host => !string.IsNullOrWhiteSpace(host)) + .ToArray(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Stores/StoreMapping.cs b/src/Libraries/SmartStore.Core/Domain/Stores/StoreMapping.cs index 6867cbf737..6f14db3afe 100644 --- a/src/Libraries/SmartStore.Core/Domain/Stores/StoreMapping.cs +++ b/src/Libraries/SmartStore.Core/Domain/Stores/StoreMapping.cs @@ -2,28 +2,28 @@ namespace SmartStore.Core.Domain.Stores { - /// - /// Represents a store mapping record - /// - [DataContract] - public partial class StoreMapping : BaseEntity - { - /// - /// Gets or sets the entity identifier - /// - [DataMember] - public virtual int EntityId { get; set; } + /// + /// Represents a store mapping record + /// + [DataContract] + public partial class StoreMapping : BaseEntity + { + /// + /// Gets or sets the entity identifier + /// + [DataMember] + public virtual int EntityId { get; set; } - /// - /// Gets or sets the entity name - /// - [DataMember] - public virtual string EntityName { get; set; } + /// + /// Gets or sets the entity name + /// + [DataMember] + public virtual string EntityName { get; set; } - /// - /// Gets or sets the store identifier - /// - [DataMember] - public virtual int StoreId { get; set; } - } + /// + /// Gets or sets the store identifier + /// + [DataMember] + public virtual int StoreId { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Tasks/ScheduleTask.cs b/src/Libraries/SmartStore.Core/Domain/Tasks/ScheduleTask.cs index f516fc53da..bf04b013c6 100644 --- a/src/Libraries/SmartStore.Core/Domain/Tasks/ScheduleTask.cs +++ b/src/Libraries/SmartStore.Core/Domain/Tasks/ScheduleTask.cs @@ -6,9 +6,16 @@ namespace SmartStore.Core.Domain.Tasks { + public enum TaskPriority + { + Low = -1, + Normal = 0, + High = 1 + } + [DebuggerDisplay("{Name} (Type: {Type})")] - [Hookable(false)] - public class ScheduleTask : BaseEntity, ICloneable + [Hookable(false)] + public class ScheduleTask : BaseEntity, ICloneable { private ICollection _scheduleTaskHistory; @@ -22,10 +29,10 @@ public class ScheduleTask : BaseEntity, ICloneable /// public string Alias { get; set; } - /// - /// Gets or sets the CRON expression used to calculate future schedules - /// - public string CronExpression { get; set; } + /// + /// Gets or sets the CRON expression used to calculate future schedules + /// + public string CronExpression { get; set; } /// /// Gets or sets the type of appropriate ITask class @@ -39,6 +46,11 @@ public class ScheduleTask : BaseEntity, ICloneable [Index("IX_NextRun_Enabled", 1)] public bool Enabled { get; set; } + /// + /// Gets or sets the task priority. Tasks with higher priority run first when multiple tasks are pending. + /// + public TaskPriority Priority { get; set; } = TaskPriority.Normal; + /// /// Gets or sets the value indicating whether a task should be stopped on some error /// @@ -57,17 +69,17 @@ public class ScheduleTask : BaseEntity, ICloneable /// public bool RunPerMachine { get; set; } - /// - /// Gets a value indicating whether a task is scheduled for execution (Enabled = true and NextRunUtc <= UtcNow and is not running). - /// - public bool IsPending - { - get - { + /// + /// Gets a value indicating whether a task is scheduled for execution (Enabled = true and NextRunUtc <= UtcNow and is not running). + /// + public bool IsPending + { + get + { var result = Enabled && NextRunUtc.HasValue && NextRunUtc <= DateTime.UtcNow && (LastHistoryEntry == null || !LastHistoryEntry.IsRunning); - return result; - } - } + return result; + } + } public ScheduleTaskHistory LastHistoryEntry { get; set; } @@ -76,18 +88,12 @@ public bool IsPending /// public virtual ICollection ScheduleTaskHistory { - get - { - return _scheduleTaskHistory ?? (_scheduleTaskHistory = new HashSet()); - } - protected set - { - _scheduleTaskHistory = value; - } + get => _scheduleTaskHistory ?? (_scheduleTaskHistory = new HashSet()); + protected set => _scheduleTaskHistory = value; } public ScheduleTask Clone() - { + { var task = new ScheduleTask { Name = Name, @@ -102,11 +108,11 @@ public ScheduleTask Clone() }; return task; - } + } - object ICloneable.Clone() - { - return this.Clone(); - } - } + object ICloneable.Clone() + { + return this.Clone(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Tax/AuxiliaryServicesTaxType.cs b/src/Libraries/SmartStore.Core/Domain/Tax/AuxiliaryServicesTaxType.cs index a215f4160c..9ea9566b5f 100644 --- a/src/Libraries/SmartStore.Core/Domain/Tax/AuxiliaryServicesTaxType.cs +++ b/src/Libraries/SmartStore.Core/Domain/Tax/AuxiliaryServicesTaxType.cs @@ -1,29 +1,29 @@ namespace SmartStore.Core.Domain.Tax { - /// - /// Specifies how to calculate the tax of auxiliary services like shipping and payment fees - /// - public enum AuxiliaryServicesTaxType - { - /// - /// Calculate tax of auxiliary services with the tax category specified in settings - /// - SpecifiedTaxCategory = 0, + /// + /// Specifies how to calculate the tax of auxiliary services like shipping and payment fees + /// + public enum AuxiliaryServicesTaxType + { + /// + /// Calculate tax of auxiliary services with the tax category specified in settings + /// + SpecifiedTaxCategory = 0, - /// - /// Calculate tax with the tax rate that has the highest amount in the cart - /// - HighestCartAmount = 10, + /// + /// Calculate tax with the tax rate that has the highest amount in the cart + /// + HighestCartAmount = 10, - /// - /// Calculate tax by the highest tax rate in the cart - /// - HighestTaxRate = 15, + /// + /// Calculate tax by the highest tax rate in the cart + /// + HighestTaxRate = 15, - /// - /// Calculate tax pro rata in accordance with main service (proportion of line subtotal and sum of all line subtotals) - /// - /// commented out cause requires several plugins to be updated and migration of Order.OrderShippingTaxRate and Order.PaymentMethodAdditionalFeeTaxRate - ///ProRata = 20 - } + /// + /// Calculate tax pro rata in accordance with main service (proportion of line subtotal and sum of all line subtotals) + /// + /// commented out cause requires several plugins to be updated and migration of Order.OrderShippingTaxRate and Order.PaymentMethodAdditionalFeeTaxRate + ///ProRata = 20 + } } diff --git a/src/Libraries/SmartStore.Core/Domain/Tax/TaxCategory.cs b/src/Libraries/SmartStore.Core/Domain/Tax/TaxCategory.cs index 7fd0279410..ab09afe7bc 100644 --- a/src/Libraries/SmartStore.Core/Domain/Tax/TaxCategory.cs +++ b/src/Libraries/SmartStore.Core/Domain/Tax/TaxCategory.cs @@ -2,23 +2,23 @@ namespace SmartStore.Core.Domain.Tax { - /// - /// Represents a tax category - /// - [DataContract] - public partial class TaxCategory : BaseEntity + /// + /// Represents a tax category + /// + [DataContract] + public partial class TaxCategory : BaseEntity { - /// - /// Gets or sets the name - /// - [DataMember] - public string Name { get; set; } + /// + /// Gets or sets the name + /// + [DataMember] + public string Name { get; set; } - /// - /// Gets or sets the display order - /// - [DataMember] - public int DisplayOrder { get; set; } + /// + /// Gets or sets the display order + /// + [DataMember] + public int DisplayOrder { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Tax/TaxSettings.cs b/src/Libraries/SmartStore.Core/Domain/Tax/TaxSettings.cs index 28fd36b269..13ad247d4c 100644 --- a/src/Libraries/SmartStore.Core/Domain/Tax/TaxSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Tax/TaxSettings.cs @@ -4,22 +4,22 @@ namespace SmartStore.Core.Domain.Tax { public class TaxSettings : ISettings { - public TaxSettings() - { - TaxBasedOn = TaxBasedOn.BillingAddress; - TaxDisplayType = TaxDisplayType.ExcludingTax; - ActiveTaxProviderSystemName = "Tax.FixedRate"; - DisplayTaxSuffix = true; - PricesIncludeTax = true; - EuVatAllowVatExemption = true; + public TaxSettings() + { + TaxBasedOn = TaxBasedOn.BillingAddress; + TaxDisplayType = TaxDisplayType.ExcludingTax; + ActiveTaxProviderSystemName = "Tax.FixedRate"; + DisplayTaxSuffix = true; + PricesIncludeTax = true; + EuVatAllowVatExemption = true; ShippingPriceIncludesTax = true; - ShowLegalHintsInFooter = true; - ShowLegalHintsInProductDetails = true; - ShowLegalHintsInProductList = true; - AuxiliaryServicesTaxingType = AuxiliaryServicesTaxType.SpecifiedTaxCategory; - } - - /// + ShowLegalHintsInFooter = true; + ShowLegalHintsInProductDetails = true; + ShowLegalHintsInProductList = true; + AuxiliaryServicesTaxingType = AuxiliaryServicesTaxType.SpecifiedTaxCategory; + } + + /// /// Tax based on /// public TaxBasedOn TaxBasedOn { get; set; } @@ -114,15 +114,15 @@ public TaxSettings() /// public int PaymentMethodAdditionalFeeTaxClassId { get; set; } - /// - /// Taxing type for auxiliary services like shipping and payment fees - /// - public AuxiliaryServicesTaxType AuxiliaryServicesTaxingType { get; set; } + /// + /// Taxing type for auxiliary services like shipping and payment fees + /// + public AuxiliaryServicesTaxType AuxiliaryServicesTaxingType { get; set; } - /// - /// Gets or sets a value indicating whether EU VAT (Eupore Union Value Added Tax) is enabled - /// - public bool EuVatEnabled { get; set; } + /// + /// Gets or sets a value indicating whether EU VAT (Eupore Union Value Added Tax) is enabled + /// + public bool EuVatEnabled { get; set; } /// /// Gets or sets a shop country identifier diff --git a/src/Libraries/SmartStore.Core/Domain/Themes/ThemeSettings.cs b/src/Libraries/SmartStore.Core/Domain/Themes/ThemeSettings.cs index aff5ac1f4b..4b1b127369 100644 --- a/src/Libraries/SmartStore.Core/Domain/Themes/ThemeSettings.cs +++ b/src/Libraries/SmartStore.Core/Domain/Themes/ThemeSettings.cs @@ -1,16 +1,15 @@ -using System; -using SmartStore.Core.Configuration; +using SmartStore.Core.Configuration; namespace SmartStore.Core.Domain.Themes { public class ThemeSettings : ISettings { - public ThemeSettings() - { - DefaultTheme = "Flex"; - AllowCustomerToSelectTheme = true; - AssetCachingEnabled = 2; - } + public ThemeSettings() + { + DefaultTheme = "Flex"; + AllowCustomerToSelectTheme = true; + AssetCachingEnabled = 2; + } /// /// Gets or sets a value indicating whether @@ -23,35 +22,35 @@ public ThemeSettings() /// public int BundleOptimizationEnabled { get; set; } - /// - /// Gets or sets a value indicating whether - /// asset caching is enabled - /// - /// - /// 0: Auto (decide based on debug mode) - /// 1: Disabled - /// 2: Enabled > default - /// - public int AssetCachingEnabled { get; set; } + /// + /// Gets or sets a value indicating whether + /// asset caching is enabled + /// + /// + /// 0: Auto (decide based on debug mode) + /// 1: Disabled + /// 2: Enabled > default + /// + public int AssetCachingEnabled { get; set; } - /// - /// Gets or sets a default store theme for desktops - /// - public string DefaultTheme { get; set; } + /// + /// Gets or sets a default store theme for desktops + /// + public string DefaultTheme { get; set; } /// /// Gets or sets a value indicating whether customers are allowed to select a theme /// public bool AllowCustomerToSelectTheme { get; set; } - /// - /// Gets or sets a value indicating whether user's theme choice should be saved in a cookie - /// - /// - /// If false, user's theme choice is associated to the account, - /// which may be undesirable when, for example, multiple users share a guest account. - /// - public bool SaveThemeChoiceInCookie { get; set; } + /// + /// Gets or sets a value indicating whether user's theme choice should be saved in a cookie + /// + /// + /// If false, user's theme choice is associated to the account, + /// which may be undesirable when, for example, multiple users share a guest account. + /// + public bool SaveThemeChoiceInCookie { get; set; } } } diff --git a/src/Libraries/SmartStore.Core/Domain/Themes/ThemeVariable.cs b/src/Libraries/SmartStore.Core/Domain/Themes/ThemeVariable.cs index 2befa5532e..14540d65d6 100644 --- a/src/Libraries/SmartStore.Core/Domain/Themes/ThemeVariable.cs +++ b/src/Libraries/SmartStore.Core/Domain/Themes/ThemeVariable.cs @@ -1,12 +1,9 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; // codehint: sm-add (whole file) namespace SmartStore.Core.Domain.Themes -{ +{ public class ThemeVariable : BaseEntity { /// @@ -24,10 +21,10 @@ public class ThemeVariable : BaseEntity /// public string Value { get; set; } - /// - /// Gets or sets the store identifier - /// - public int StoreId { get; set; } + /// + /// Gets or sets the store identifier + /// + public int StoreId { get; set; } protected override bool Equals(BaseEntity other) { diff --git a/src/Libraries/SmartStore.Core/Domain/Topics/Topic.cs b/src/Libraries/SmartStore.Core/Domain/Topics/Topic.cs index d05d74c975..a5e7171f8e 100644 --- a/src/Libraries/SmartStore.Core/Domain/Topics/Topic.cs +++ b/src/Libraries/SmartStore.Core/Domain/Topics/Topic.cs @@ -6,31 +6,32 @@ using SmartStore.Core.Domain.Security; using SmartStore.Core.Domain.Seo; using SmartStore.Core.Domain.Stores; +using SmartStore.Core.Plugins; namespace SmartStore.Core.Domain.Topics { - /// - /// Represents a topic - /// - [DataContract] - public partial class Topic : BaseEntity, ILocalizedEntity, ISlugSupported, IStoreMappingSupported, IAclSupported + /// + /// Represents a topic + /// + [DataContract] + public partial class Topic : BaseEntity, ILocalizedEntity, ISlugSupported, IStoreMappingSupported, IAclSupported { - public Topic() - { - IsPublished = true; - } + public Topic() + { + IsPublished = true; + } - /// - /// Gets or sets the name - /// - [DataMember] - public string SystemName { get; set; } + /// + /// Gets or sets the name + /// + [DataMember] + public string SystemName { get; set; } - /// - /// Gets or sets the value indicating whether this topic is deleteable by a user - /// - [DataMember] - public bool IsSystemTopic { get; set; } + /// + /// Gets or sets the value indicating whether this topic is deleteable by a user + /// + [DataMember] + public bool IsSystemTopic { get; set; } /// /// Gets or sets the html id @@ -48,133 +49,138 @@ public Topic() /// Gets or sets the value indicating whether this topic should be included in sitemap /// [DataMember] - public bool IncludeInSitemap { get; set; } - - /// - /// Gets or sets the value indicating whether this topic is password protected - /// - [DataMember] - public bool IsPasswordProtected { get; set; } - - /// - /// Gets or sets the password - /// - [DataMember] - public string Password { get; set; } - - /// - /// Gets or sets the title - /// - [DataMember] - public string Title { get; set; } - - /// - /// Gets or sets the short title (for links) - /// - [DataMember] - public string ShortTitle { get; set; } - - /// - /// Gets or sets the intro - /// - [DataMember] - public string Intro { get; set; } - - /// - /// Gets or sets the body - /// - [DataMember] - public string Body { get; set; } - - /// - /// Gets or sets the meta keywords - /// - [DataMember] - public string MetaKeywords { get; set; } - - /// - /// Gets or sets the meta description - /// - [DataMember] - public string MetaDescription { get; set; } - - /// - /// Gets or sets the meta title - /// - [DataMember] - public string MetaTitle { get; set; } - - /// - /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores - /// - [DataMember] - public bool LimitedToStores { get; set; } - - /// - /// Gets or sets a value indicating whether the topic should also be rendered as a generic html widget - /// - [DataMember] - public bool RenderAsWidget { get; set; } - - /// - /// Gets or sets the widget zone name - /// - [DataMember] - public string WidgetZone { get; set; } - - /// - /// Gets or sets a value indicating whether the content should be surrounded by a topic block wrapper - /// - [DataMember] - public bool? WidgetWrapContent { get; set; } - - /// - /// Gets or sets a value indicating whether the title should be displayed in the widget block - /// - [DataMember] - public bool WidgetShowTitle { get; set; } - - /// - /// Gets or sets a value indicating whether the widget block should have borders - /// - [DataMember] - public bool WidgetBordered { get; set; } - - /// - /// Gets or sets the sort order (relevant for widgets) - /// - [DataMember] - public int Priority { get; set; } - - /// - /// Gets or sets the title tag - /// - [DataMember] - public string TitleTag { get; set; } - - /// - /// Gets or sets a value indicating whether the entity is subject to ACL - /// - [DataMember] - public bool SubjectToAcl { get; set; } - - /// - /// Gets or sets a value indicating whether the topic page is published - /// - [DataMember] - public bool IsPublished { get; set; } - - /// - /// Helper function which gets the comma-separated WidgetZone property as list of strings - /// - /// - public IEnumerable GetWidgetZones() + public bool IncludeInSitemap { get; set; } + + /// + /// Gets or sets the value indicating whether this topic is password protected + /// + [DataMember] + public bool IsPasswordProtected { get; set; } + + /// + /// Gets or sets the password + /// + [DataMember] + public string Password { get; set; } + + /// + /// Gets or sets the title + /// + [DataMember] + public string Title { get; set; } + + /// + /// Gets or sets the short title (for links) + /// + [DataMember] + public string ShortTitle { get; set; } + + /// + /// Gets or sets the intro + /// + [DataMember] + public string Intro { get; set; } + + /// + /// Gets or sets the body + /// + [DataMember] + public string Body { get; set; } + + /// + /// Gets or sets the meta keywords + /// + [DataMember] + public string MetaKeywords { get; set; } + + /// + /// Gets or sets the meta description + /// + [DataMember] + public string MetaDescription { get; set; } + + /// + /// Gets or sets the meta title + /// + [DataMember] + public string MetaTitle { get; set; } + + /// + /// Gets or sets a value indicating whether the entity is limited/restricted to certain stores + /// + [DataMember] + public bool LimitedToStores { get; set; } + + /// + /// Gets or sets a value indicating whether the topic should also be rendered as a generic html widget + /// + [DataMember] + public bool RenderAsWidget { get; set; } + + /// + /// Gets or sets the widget zone name + /// + [DataMember] + public string WidgetZone { get; set; } + + /// + /// Gets or sets a value indicating whether the content should be surrounded by a topic block wrapper + /// + [DataMember] + public bool? WidgetWrapContent { get; set; } + + /// + /// Gets or sets a value indicating whether the title should be displayed in the widget block + /// + [DataMember] + public bool WidgetShowTitle { get; set; } + + /// + /// Gets or sets a value indicating whether the widget block should have borders + /// + [DataMember] + public bool WidgetBordered { get; set; } + + /// + /// Gets or sets the sort order (relevant for widgets) + /// + [DataMember] + public int Priority { get; set; } + + /// + /// Gets or sets the title tag + /// + [DataMember] + public string TitleTag { get; set; } + + /// + /// Gets or sets a value indicating whether the entity is subject to ACL + /// + [DataMember] + public bool SubjectToAcl { get; set; } + + /// + /// Gets or sets a value indicating whether the topic page is published + /// + [DataMember] + public bool IsPublished { get; set; } + + /// + /// Gets or sets a value indicating whether the topic set a cookie and the cookie type + /// + public CookieType? CookieType { get; set; } + + /// + /// Helper function which gets the comma-separated WidgetZone property as list of strings + /// + /// + public IEnumerable GetWidgetZones() { if (this.WidgetZone.IsEmpty()) { return Enumerable.Empty(); } - + return this.WidgetZone.Trim().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()); } } diff --git a/src/Libraries/SmartStore.Core/Email/DefaultEmailSender.cs b/src/Libraries/SmartStore.Core/Email/DefaultEmailSender.cs index 5492f4631f..8973a9f3c9 100644 --- a/src/Libraries/SmartStore.Core/Email/DefaultEmailSender.cs +++ b/src/Libraries/SmartStore.Core/Email/DefaultEmailSender.cs @@ -1,10 +1,8 @@ using System; -using System.Collections.Generic; +using System.IO; using System.Linq; -using System.Text; using System.Net.Mail; using System.Net.Mime; -using System.IO; using System.Threading.Tasks; using SmartStore.Core.Domain.Messages; @@ -12,34 +10,34 @@ namespace SmartStore.Core.Email { public class DefaultEmailSender : IEmailSender { - private readonly EmailAccountSettings _emailAccountSettings; - - public DefaultEmailSender(EmailAccountSettings emailAccountSettings) - { - _emailAccountSettings = emailAccountSettings; - } - - /// - /// Builds System.Net.Mail.Message - /// - /// SmartStore.Email.Message - /// System.Net.Mail.Message - protected virtual MailMessage BuildMailMessage(EmailMessage original) + private readonly EmailAccountSettings _emailAccountSettings; + + public DefaultEmailSender(EmailAccountSettings emailAccountSettings) + { + _emailAccountSettings = emailAccountSettings; + } + + /// + /// Builds System.Net.Mail.Message + /// + /// SmartStore.Email.Message + /// System.Net.Mail.Message + protected virtual MailMessage BuildMailMessage(EmailMessage original) { MailMessage msg = new MailMessage(); - if (String.IsNullOrEmpty(original.Subject)) - { - throw new MailSenderException("Required subject is missing!"); - } - - msg.Subject = original.Subject; - msg.IsBodyHtml = original.BodyFormat == MailBodyFormat.Html; + if (String.IsNullOrEmpty(original.Subject)) + { + throw new MailSenderException("Required subject is missing!"); + } + + msg.Subject = original.Subject; + msg.IsBodyHtml = original.BodyFormat == MailBodyFormat.Html; if (original.AltText.HasValue()) { - msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(original.AltText, new ContentType("text/html"))); - msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(original.Body, new ContentType("text/plain"))); + msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(original.Body, new ContentType("text/html"))); + msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(original.AltText, new ContentType("text/plain"))); } else { @@ -50,65 +48,65 @@ protected virtual MailMessage BuildMailMessage(EmailMessage original) msg.From = original.From.ToMailAddress(); - msg.To.AddRange(original.To.Where(x => x.Address.HasValue()).Select(x => x.ToMailAddress())); - msg.CC.AddRange(original.Cc.Where(x => x.Address.HasValue()).Select(x => x.ToMailAddress())); - msg.Bcc.AddRange(original.Bcc.Where(x => x.Address.HasValue()).Select(x => x.ToMailAddress())); - msg.ReplyToList.AddRange(original.ReplyTo.Where(x => x.Address.HasValue()).Select(x => x.ToMailAddress())); + msg.To.AddRange(original.To.Where(x => x.Address.HasValue()).Select(x => x.ToMailAddress())); + msg.CC.AddRange(original.Cc.Where(x => x.Address.HasValue()).Select(x => x.ToMailAddress())); + msg.Bcc.AddRange(original.Bcc.Where(x => x.Address.HasValue()).Select(x => x.ToMailAddress())); + msg.ReplyToList.AddRange(original.ReplyTo.Where(x => x.Address.HasValue()).Select(x => x.ToMailAddress())); - msg.Attachments.AddRange(original.Attachments); + msg.Attachments.AddRange(original.Attachments); if (original.Headers != null) - msg.Headers.AddRange(original.Headers); - + msg.Headers.AddRange(original.Headers); + msg.Priority = original.Priority; return msg; } - #region IMailSender Members + #region IMailSender Members + + public void SendEmail(SmtpContext context, EmailMessage message) + { + Guard.NotNull(context, nameof(context)); + Guard.NotNull(message, nameof(message)); + + using (var msg = this.BuildMailMessage(message)) + { + using (var client = context.ToSmtpClient()) + { + ApplySettings(client); + client.Send(msg); + } + } + } + + public async Task SendEmailAsync(SmtpContext context, EmailMessage message) + { + Guard.NotNull(context, nameof(context)); + Guard.NotNull(message, nameof(message)); + + using (var msg = this.BuildMailMessage(message)) + { + using (var client = context.ToSmtpClient()) + { + ApplySettings(client); + await client.SendMailAsync(msg); + } + } + } - public void SendEmail(SmtpContext context, EmailMessage message) + private void ApplySettings(SmtpClient client) { - Guard.NotNull(context, nameof(context)); - Guard.NotNull(message, nameof(message)); - - using (var msg = this.BuildMailMessage(message)) - { - using (var client = context.ToSmtpClient()) - { - ApplySettings(client); - client.Send(msg); - } - } + var pickupDirLocation = _emailAccountSettings.PickupDirectoryLocation; + if (pickupDirLocation.HasValue() && client.DeliveryMethod != SmtpDeliveryMethod.SpecifiedPickupDirectory && Path.IsPathRooted(pickupDirLocation)) + { + client.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory; + client.PickupDirectoryLocation = pickupDirLocation; + client.EnableSsl = false; + } } - public async Task SendEmailAsync(SmtpContext context, EmailMessage message) - { - Guard.NotNull(context, nameof(context)); - Guard.NotNull(message, nameof(message)); - - using (var msg = this.BuildMailMessage(message)) - { - using (var client = context.ToSmtpClient()) - { - ApplySettings(client); - await client.SendMailAsync(msg); - } - } - } - - private void ApplySettings(SmtpClient client) - { - var pickupDirLocation = _emailAccountSettings.PickupDirectoryLocation; - if (pickupDirLocation.HasValue() && client.DeliveryMethod != SmtpDeliveryMethod.SpecifiedPickupDirectory && Path.IsPathRooted(pickupDirLocation)) - { - client.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory; - client.PickupDirectoryLocation = pickupDirLocation; - client.EnableSsl = false; - } - } - - #endregion - } + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Email/EmailAddress.cs b/src/Libraries/SmartStore.Core/Email/EmailAddress.cs index 8699174caf..b8820eb0d2 100644 --- a/src/Libraries/SmartStore.Core/Email/EmailAddress.cs +++ b/src/Libraries/SmartStore.Core/Email/EmailAddress.cs @@ -1,56 +1,52 @@ -using System; -using System.Net.Mail; +using System.Net.Mail; namespace SmartStore.Core.Email { public class EmailAddress { - private readonly MailAddress _inner; + private readonly MailAddress _inner; - public EmailAddress(string address) + public EmailAddress(string address) { - _inner = new MailAddress(address); + _inner = new MailAddress(address); } public EmailAddress(string address, string displayName) { - _inner = new MailAddress(address, displayName); - } + _inner = new MailAddress(address, displayName); + } - public EmailAddress(MailAddress address) - { - _inner = address; - } + public EmailAddress(MailAddress address) + { + _inner = address; + } - public string Address - { - get { return _inner.Address; } - } + public string Address => _inner.Address; - public string DisplayName - { - get { return _inner.DisplayName; } - } + public string DisplayName => _inner.DisplayName; - public string User - { - get { return _inner.User; } - } + public string User => _inner.User; - public string Host - { - get { return _inner.Host; } - } + public string Host => _inner.Host; - public override int GetHashCode() => _inner.GetHashCode(); + public override int GetHashCode() + { + return _inner.GetHashCode(); + } - public override string ToString() => _inner.ToString(); + public override string ToString() + { + return _inner.ToString(); + } - public MailAddress ToMailAddress() => _inner; + public MailAddress ToMailAddress() + { + return _inner; + } - public static implicit operator string(EmailAddress obj) - { - return obj.ToString(); - } - } + public static implicit operator string(EmailAddress obj) + { + return obj.ToString(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Email/EmailMessage.cs b/src/Libraries/SmartStore.Core/Email/EmailMessage.cs index cdc83e6efb..0249348cfe 100644 --- a/src/Libraries/SmartStore.Core/Email/EmailMessage.cs +++ b/src/Libraries/SmartStore.Core/Email/EmailMessage.cs @@ -1,72 +1,72 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; -using System.Net.Mail; using System.IO; -using System.Text; using System.Net; +using System.Net.Mail; +using System.Text; namespace SmartStore.Core.Email { public class EmailMessage : ICloneable { - public EmailMessage() - { - this.BodyFormat = MailBodyFormat.Html; - this.Priority = MailPriority.Normal; - - this.To = new List(); - this.Cc = new List(); - this.Bcc = new List(); - this.ReplyTo = new List(); - - this.Attachments = new List(); - - this.Headers = new NameValueCollection(); - } - - public EmailMessage(string to, string subject, string body, string from) - : this() - { - Guard.NotEmpty(to, nameof(to)); - Guard.NotEmpty(from, nameof(from)); - Guard.NotEmpty(subject, nameof(subject)); - Guard.NotEmpty(body, nameof(body)); - - this.To.Add(new EmailAddress(to)); - this.Subject = subject; - this.Body = body; - this.From = new EmailAddress(from); - } - - public EmailMessage(EmailAddress to, string subject, string body, EmailAddress from) - : this() - { - Guard.NotNull(to, nameof(to)); - Guard.NotNull(from, nameof(from)); - Guard.NotEmpty(subject, nameof(subject)); - Guard.NotEmpty(body, nameof(body)); - - this.To.Add(to); - this.Subject = subject; - this.Body = body; - this.From = from; - } - - public EmailAddress From { get; set; } + public EmailMessage() + { + this.BodyFormat = MailBodyFormat.Html; + this.Priority = MailPriority.Normal; + + this.To = new List(); + this.Cc = new List(); + this.Bcc = new List(); + this.ReplyTo = new List(); + + this.Attachments = new List(); + + this.Headers = new NameValueCollection(); + } + + public EmailMessage(string to, string subject, string body, string from) + : this() + { + Guard.NotEmpty(to, nameof(to)); + Guard.NotEmpty(from, nameof(from)); + Guard.NotEmpty(subject, nameof(subject)); + Guard.NotEmpty(body, nameof(body)); + + this.To.Add(new EmailAddress(to)); + this.Subject = subject; + this.Body = body; + this.From = new EmailAddress(from); + } + + public EmailMessage(EmailAddress to, string subject, string body, EmailAddress from) + : this() + { + Guard.NotNull(to, nameof(to)); + Guard.NotNull(from, nameof(from)); + Guard.NotEmpty(subject, nameof(subject)); + Guard.NotEmpty(body, nameof(body)); + + this.To.Add(to); + this.Subject = subject; + this.Body = body; + this.From = from; + } + + public EmailAddress From { get; set; } public string Subject { get; set; } public string Body { get; set; } public string AltText { get; set; } - public MailBodyFormat BodyFormat { get; set; } + public MailBodyFormat BodyFormat { get; set; } public MailPriority Priority { get; set; } public ICollection To { get; private set; } - public ICollection Cc { get; private set; } - public ICollection Bcc { get; private set; } - public ICollection ReplyTo { get; private set; } + public ICollection Cc { get; private set; } + public ICollection Bcc { get; private set; } + public ICollection ReplyTo { get; private set; } - public ICollection Attachments { get; private set; } + public ICollection Attachments { get; private set; } public NameValueCollection Headers { get; private set; } @@ -95,19 +95,19 @@ public EmailMessage Clone() { var clone = new EmailMessage(); - clone.Attachments.AddRange(this.Attachments); - clone.To.AddRange(this.To); - clone.Cc.AddRange(this.Cc); - clone.Bcc.AddRange(this.Bcc); - clone.ReplyTo.AddRange(this.ReplyTo); - clone.Headers.AddRange(this.Headers); + clone.Attachments.AddRange(this.Attachments); + clone.To.AddRange(this.To); + clone.Cc.AddRange(this.Cc); + clone.Bcc.AddRange(this.Bcc); + clone.ReplyTo.AddRange(this.ReplyTo); + clone.Headers.AddRange(this.Headers); - clone.AltText = this.AltText; - clone.Body = this.Body; - clone.BodyFormat = this.BodyFormat; - clone.From = this.From; - clone.Priority = this.Priority; - clone.Subject = this.Subject; + clone.AltText = this.AltText; + clone.Body = this.Body; + clone.BodyFormat = this.BodyFormat; + clone.From = this.From; + clone.Priority = this.Priority; + clone.Subject = this.Subject; return clone; } @@ -120,9 +120,9 @@ object ICloneable.Clone() #endregion } - public enum MailBodyFormat - { - Text, - Html - } + public enum MailBodyFormat + { + Text, + Html + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Email/IEmailSender.cs b/src/Libraries/SmartStore.Core/Email/IEmailSender.cs index a2740cb877..9850acfa80 100644 --- a/src/Libraries/SmartStore.Core/Email/IEmailSender.cs +++ b/src/Libraries/SmartStore.Core/Email/IEmailSender.cs @@ -1,14 +1,13 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace SmartStore.Core.Email -{ +{ /// /// contract for email sender /// public interface IEmailSender { void SendEmail(SmtpContext smtpContext, EmailMessage message); - Task SendEmailAsync(SmtpContext smtpContext, EmailMessage message); + Task SendEmailAsync(SmtpContext smtpContext, EmailMessage message); } } diff --git a/src/Libraries/SmartStore.Core/Email/SmtpContext.cs b/src/Libraries/SmartStore.Core/Email/SmtpContext.cs index e1fde3c68a..ba9b71014d 100644 --- a/src/Libraries/SmartStore.Core/Email/SmtpContext.cs +++ b/src/Libraries/SmartStore.Core/Email/SmtpContext.cs @@ -1,7 +1,5 @@ using System; using System.Net; -using System.Net.Configuration; -using System.Configuration; using System.Net.Mail; using SmartStore.Core.Domain.Messages; @@ -12,81 +10,81 @@ public class SmtpContext public SmtpContext(string host, int port = 25) { - Guard.NotEmpty(host, nameof(host)); - Guard.IsPositive(port, nameof(port)); - - this.Host = host; + Guard.NotEmpty(host, nameof(host)); + Guard.IsPositive(port, nameof(port)); + + this.Host = host; this.Port = port; } - public SmtpContext(EmailAccount account) - { - Guard.NotNull(account, nameof(account)); - - this.Host = account.Host; - this.Port = account.Port; - this.EnableSsl = account.EnableSsl; - this.Password = account.Password; - this.UseDefaultCredentials = account.UseDefaultCredentials; - this.Username = account.Username; - } - - public bool UseDefaultCredentials - { - get; - set; - } - - public string Host - { - get; - set; - } - - public int Port - { - get; - set; - } - - public string Username - { - get; - set; - } - - public string Password - { - get; - set; - } - - public bool EnableSsl - { - get; - set; - } - - public SmtpClient ToSmtpClient() - { - var smtpClient = new SmtpClient(this.Host, this.Port); - - smtpClient.UseDefaultCredentials = this.UseDefaultCredentials; - smtpClient.EnableSsl = this.EnableSsl; - smtpClient.Timeout = 10000; - - if (this.UseDefaultCredentials) - { - smtpClient.Credentials = CredentialCache.DefaultNetworkCredentials; - } - else - { - if (!String.IsNullOrEmpty(this.Username)) - smtpClient.Credentials = new NetworkCredential(this.Username, this.Password); - } - - return smtpClient; - } + public SmtpContext(EmailAccount account) + { + Guard.NotNull(account, nameof(account)); + + this.Host = account.Host; + this.Port = account.Port; + this.EnableSsl = account.EnableSsl; + this.Password = account.Password; + this.UseDefaultCredentials = account.UseDefaultCredentials; + this.Username = account.Username; + } + + public bool UseDefaultCredentials + { + get; + set; + } + + public string Host + { + get; + set; + } + + public int Port + { + get; + set; + } + + public string Username + { + get; + set; + } + + public string Password + { + get; + set; + } + + public bool EnableSsl + { + get; + set; + } + + public SmtpClient ToSmtpClient() + { + var smtpClient = new SmtpClient(this.Host, this.Port); + + smtpClient.UseDefaultCredentials = this.UseDefaultCredentials; + smtpClient.EnableSsl = this.EnableSsl; + smtpClient.Timeout = 10000; + + if (this.UseDefaultCredentials) + { + smtpClient.Credentials = CredentialCache.DefaultNetworkCredentials; + } + else + { + if (!String.IsNullOrEmpty(this.Username)) + smtpClient.Credentials = new NetworkCredential(this.Username, this.Password); + } + + return smtpClient; + } } } diff --git a/src/Libraries/SmartStore.Core/Events/AmbigousConsumerException.cs b/src/Libraries/SmartStore.Core/Events/AmbigousConsumerException.cs index 18b72d94da..453ddd7406 100644 --- a/src/Libraries/SmartStore.Core/Events/AmbigousConsumerException.cs +++ b/src/Libraries/SmartStore.Core/Events/AmbigousConsumerException.cs @@ -3,11 +3,11 @@ namespace SmartStore.Core.Events { - public sealed class AmbigousConsumerException : Exception - { - public AmbigousConsumerException(MethodInfo method1, MethodInfo method2) - : base("Ambigous consumer methods detected in '{0}'. Method 1: '{1}', Method 2: '{2}'.".FormatInvariant(method1.DeclaringType.FullName, method1.ToString(), method2.ToString())) - { - } - } + public sealed class AmbigousConsumerException : Exception + { + public AmbigousConsumerException(MethodInfo method1, MethodInfo method2) + : base("Ambigous consumer methods detected in '{0}'. Method 1: '{1}', Method 2: '{2}'.".FormatInvariant(method1.DeclaringType.FullName, method1.ToString(), method2.ToString())) + { + } + } } diff --git a/src/Libraries/SmartStore.Core/Events/CommonMessages/AppInitScheduledTasksEvent.cs b/src/Libraries/SmartStore.Core/Events/CommonMessages/AppInitScheduledTasksEvent.cs index 348b5fac89..5058307cfb 100644 --- a/src/Libraries/SmartStore.Core/Events/CommonMessages/AppInitScheduledTasksEvent.cs +++ b/src/Libraries/SmartStore.Core/Events/CommonMessages/AppInitScheduledTasksEvent.cs @@ -1,16 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Web; -using System.Web.Mvc; +using System.Collections.Generic; using SmartStore.Core.Domain.Tasks; namespace SmartStore.Core.Events { - /// - /// to initialize scheduled tasks in Application_Start - /// - public class AppInitScheduledTasksEvent - { - public IList ScheduledTasks { get; set; } - } + /// + /// to initialize scheduled tasks in Application_Start + /// + public class AppInitScheduledTasksEvent + { + public IList ScheduledTasks { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Events/CommonMessages/AppRegisterGlobalFiltersEvent.cs b/src/Libraries/SmartStore.Core/Events/CommonMessages/AppRegisterGlobalFiltersEvent.cs index d825cc079d..6fd6e8d6fe 100644 --- a/src/Libraries/SmartStore.Core/Events/CommonMessages/AppRegisterGlobalFiltersEvent.cs +++ b/src/Libraries/SmartStore.Core/Events/CommonMessages/AppRegisterGlobalFiltersEvent.cs @@ -1,14 +1,12 @@ -using System; -using System.Web; -using System.Web.Mvc; +using System.Web.Mvc; namespace SmartStore.Core.Events { - /// - /// to register global filters in Application_Start - /// - public class AppRegisterGlobalFiltersEvent - { - public GlobalFilterCollection Filters { get; set; } - } + /// + /// to register global filters in Application_Start + /// + public class AppRegisterGlobalFiltersEvent + { + public GlobalFilterCollection Filters { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Events/CommonMessages/AppStartedEvent.cs b/src/Libraries/SmartStore.Core/Events/CommonMessages/AppStartedEvent.cs index c1f2880780..95576aea9f 100644 --- a/src/Libraries/SmartStore.Core/Events/CommonMessages/AppStartedEvent.cs +++ b/src/Libraries/SmartStore.Core/Events/CommonMessages/AppStartedEvent.cs @@ -1,10 +1,9 @@ -using System; -using System.Web; +using System.Web; namespace SmartStore.Core.Events { - public class AppStartedEvent - { - public HttpContextBase HttpContext { get; set; } - } + public class AppStartedEvent + { + public HttpContextBase HttpContext { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Events/ConsumeContext.cs b/src/Libraries/SmartStore.Core/Events/ConsumeContext.cs index d52607845a..dae3684721 100644 --- a/src/Libraries/SmartStore.Core/Events/ConsumeContext.cs +++ b/src/Libraries/SmartStore.Core/Events/ConsumeContext.cs @@ -1,23 +1,21 @@ -using System; - -namespace SmartStore.Core.Events +namespace SmartStore.Core.Events { - /// - /// Wrapper/Envelope for event message objects. For now, only wraps - /// the message. There may be other context data in future. - /// - /// Type of message. - public class ConsumeContext - { - public ConsumeContext(TMessage message) - { - Message = message; - } + /// + /// Wrapper/Envelope for event message objects. For now, only wraps + /// the message. There may be other context data in future. + /// + /// Type of message. + public class ConsumeContext + { + public ConsumeContext(TMessage message) + { + Message = message; + } - public TMessage Message - { - get; - private set; - } - } + public TMessage Message + { + get; + private set; + } + } } diff --git a/src/Libraries/SmartStore.Core/Events/ConsumerDescriptor.cs b/src/Libraries/SmartStore.Core/Events/ConsumerDescriptor.cs index f990c1e61c..25302b3771 100644 --- a/src/Libraries/SmartStore.Core/Events/ConsumerDescriptor.cs +++ b/src/Libraries/SmartStore.Core/Events/ConsumerDescriptor.cs @@ -1,50 +1,44 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using SmartStore.ComponentModel; using SmartStore.Core.Plugins; namespace SmartStore.Core.Events { - /// - /// Contains metadata about a consumer / event handler method. - /// - public class ConsumerDescriptor - { - public ConsumerDescriptor() - { - } + /// + /// Contains metadata about a consumer / event handler method. + /// + public class ConsumerDescriptor + { + public ConsumerDescriptor() + { + } - public ConsumerDescriptor(EventConsumerMetadata metadata) - { - ContainerType = metadata.ContainerType; - PluginDescriptor = metadata.PluginDescriptor; - } + public ConsumerDescriptor(EventConsumerMetadata metadata) + { + ContainerType = metadata.ContainerType; + PluginDescriptor = metadata.PluginDescriptor; + } - public bool WithEnvelope { get; set; } - public bool IsAsync { get; set; } - public bool FireForget { get; set; } - public PluginDescriptor PluginDescriptor { get; set; } + public bool WithEnvelope { get; set; } + public bool IsAsync { get; set; } + public bool FireForget { get; set; } + public PluginDescriptor PluginDescriptor { get; set; } - public Type MessageType { get; set; } - public Type ContainerType { get; set; } + public Type MessageType { get; set; } + public Type ContainerType { get; set; } - public MethodInfo Method { get; set; } - public ParameterInfo MessageParameter { get; set; } + public MethodInfo Method { get; set; } + public ParameterInfo MessageParameter { get; set; } - /// - /// All method parameters except - /// or empty array if is the only parameter. - /// - public ParameterInfo[] Parameters { get; set; } + /// + /// All method parameters except + /// or empty array if is the only parameter. + /// + public ParameterInfo[] Parameters { get; set; } - public override string ToString() - { - return "MessageType: {0} - Consumer: {1}.{2}".FormatCurrent(MessageType.Name, ContainerType.FullName, Method.Name); - } - } + public override string ToString() + { + return "MessageType: {0} - Consumer: {1}.{2}".FormatCurrent(MessageType.Name, ContainerType.FullName, Method.Name); + } + } } diff --git a/src/Libraries/SmartStore.Core/Events/ConsumerInvoker.cs b/src/Libraries/SmartStore.Core/Events/ConsumerInvoker.cs index 9eb50ecb00..e382cae1b3 100644 --- a/src/Libraries/SmartStore.Core/Events/ConsumerInvoker.cs +++ b/src/Libraries/SmartStore.Core/Events/ConsumerInvoker.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using System.Web; using Autofac; using SmartStore.ComponentModel; using SmartStore.Core.Async; @@ -11,183 +10,183 @@ namespace SmartStore.Core.Events { - public class ConsumerInvoker : IConsumerInvoker - { - private readonly IConsumerResolver _resolver; - - public ConsumerInvoker(IConsumerResolver resolver) - { - _resolver = resolver; - - Logger = NullLogger.Instance; - } - - public ILogger Logger { get; set; } - - public void Invoke(ConsumerDescriptor descriptor, IConsumer consumer, ConsumeContext envelope) where TMessage : class - { - var d = descriptor; - var p = descriptor.WithEnvelope ? (object)envelope : envelope.Message; - var fastInvoker = FastInvoker.GetInvoker(d.Method); - - if (!d.FireForget && !d.IsAsync) - { - // The all synch case - try - { - InvokeCore(); - } - catch (Exception ex) - { - HandleException(ex, d); - } - } - else if (!d.FireForget && d.IsAsync) - { - //// The awaitable Task case - //BeginInvoke((Task)InvokeCore(cancelToken: AsyncRunner.AppShutdownCancellationToken), EndInvoke, d); - - // For now we must go with the AsyncRunner, the above call to BeginInvoke (APM > TPL) does not always - // guarantee that the task is awaited and throws exceptions especially when EF is involved. - using (var runner = AsyncRunner.Create()) - { - try - { - runner.Run((Task)InvokeCore(cancelToken: AsyncRunner.AppShutdownCancellationToken)); - } - catch (Exception ex) - { - HandleException(ex, d); - } - } - } - else if (d.FireForget && !d.IsAsync) - { - // A synch method should be executed async (without awaiting) - AsyncRunner.Run((c, ct, state) => InvokeCore(c, ct), d) - .ContinueWith(t => EndInvoke(t), /*TaskContinuationOptions.OnlyOnFaulted*/ TaskContinuationOptions.None) - .ConfigureAwait(false); - } - else if (d.FireForget && d.IsAsync) - { - // An async (Task) method should be executed without awaiting - AsyncRunner.Run((c, ct) => (Task)InvokeCore(c, ct), d) - .ContinueWith(t => EndInvoke(t), TaskContinuationOptions.OnlyOnFaulted) - .ConfigureAwait(false); - } - - object InvokeCore(IComponentContext c = null, CancellationToken cancelToken = default(CancellationToken)) - { - if (d.Parameters.Length == 0) - { - // Only one method param: the message! - return fastInvoker.Invoke(consumer, p); - } - - var parameters = new object[d.Parameters.Length + 1]; - parameters[0] = p; - - int i = 0; - foreach (var obj in ResolveParameters(c, d, cancelToken).ToArray()) - { - i++; - parameters[i] = obj; - } - - return fastInvoker.Invoke(consumer, parameters); - } - } - - private void HandleException(Exception ex, ConsumerDescriptor descriptor) - { - if (ex is AggregateException ae) - { - ae.InnerExceptions.Each(x => Logger.Error(x)); - } - else - { - Logger.Error(ex); - } - - if (!descriptor.FireForget) - { - throw ex; - } - } - - protected internal virtual IEnumerable ResolveParameters( - IComponentContext container, - ConsumerDescriptor descriptor, - CancellationToken cancelToken) - { - foreach (var p in descriptor.Parameters) - { - if (p.ParameterType == typeof(CancellationToken)) - { - yield return cancelToken; - } - else - { - yield return _resolver.ResolveParameter(p, container); - } - } - } - - #region APM > TPL pattern (does not always work stable) - - /// - /// Wraps a into the Begin method of an APM pattern. - /// - /// The task to wrap. - /// The callback method passed into the Begin method of the APM pattern. - /// The state passed into the Begin method of the APM pattern. - /// The asynchronous operation, to be returned by the Begin method of the APM pattern. - protected IAsyncResult BeginInvoke(Task task, AsyncCallback callback, ConsumerDescriptor descriptor) - { - var options = TaskCreationOptions.RunContinuationsAsynchronously; - var tcs = new TaskCompletionSource(descriptor, options); - - // "_ =" to discard 'async/await' compiler warning - _ = AwaitCompletionAsync(task, callback, tcs, descriptor); - - return tcs.Task; - } - - private async Task AwaitCompletionAsync( - Task task, - AsyncCallback callback, - TaskCompletionSource tcs, - ConsumerDescriptor descriptor) - { - try - { - await task; - tcs.TrySetResult(null); - } - catch (OperationCanceledException ex) - { - tcs.TrySetCanceled(ex.CancellationToken); - } - catch (Exception ex) - { - tcs.TrySetException(ex); - } - finally - { - callback?.Invoke(tcs.Task); - } - } - - protected virtual void EndInvoke(IAsyncResult asyncResult) - { - var task = (Task)asyncResult; - - if (task.IsFaulted && task.Exception != null) - { - HandleException(task.Exception, (ConsumerDescriptor)asyncResult.AsyncState); - } - } - - #endregion - } + public class ConsumerInvoker : IConsumerInvoker + { + private readonly IConsumerResolver _resolver; + + public ConsumerInvoker(IConsumerResolver resolver) + { + _resolver = resolver; + + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + public void Invoke(ConsumerDescriptor descriptor, IConsumer consumer, ConsumeContext envelope) where TMessage : class + { + var d = descriptor; + var p = descriptor.WithEnvelope ? (object)envelope : envelope.Message; + var fastInvoker = FastInvoker.GetInvoker(d.Method); + + if (!d.FireForget && !d.IsAsync) + { + // The all synch case + try + { + InvokeCore(); + } + catch (Exception ex) + { + HandleException(ex, d); + } + } + else if (!d.FireForget && d.IsAsync) + { + //// The awaitable Task case + //BeginInvoke((Task)InvokeCore(cancelToken: AsyncRunner.AppShutdownCancellationToken), EndInvoke, d); + + // For now we must go with the AsyncRunner, the above call to BeginInvoke (APM > TPL) does not always + // guarantee that the task is awaited and throws exceptions especially when EF is involved. + using (var runner = AsyncRunner.Create()) + { + try + { + runner.Run((Task)InvokeCore(cancelToken: AsyncRunner.AppShutdownCancellationToken)); + } + catch (Exception ex) + { + HandleException(ex, d); + } + } + } + else if (d.FireForget && !d.IsAsync) + { + // A synch method should be executed async (without awaiting) + AsyncRunner.Run((c, ct, state) => InvokeCore(c, ct), d) + .ContinueWith(t => EndInvoke(t), /*TaskContinuationOptions.OnlyOnFaulted*/ TaskContinuationOptions.None) + .ConfigureAwait(false); + } + else if (d.FireForget && d.IsAsync) + { + // An async (Task) method should be executed without awaiting + AsyncRunner.Run((c, ct) => (Task)InvokeCore(c, ct), d) + .ContinueWith(t => EndInvoke(t), TaskContinuationOptions.OnlyOnFaulted) + .ConfigureAwait(false); + } + + object InvokeCore(IComponentContext c = null, CancellationToken cancelToken = default(CancellationToken)) + { + if (d.Parameters.Length == 0) + { + // Only one method param: the message! + return fastInvoker.Invoke(consumer, p); + } + + var parameters = new object[d.Parameters.Length + 1]; + parameters[0] = p; + + int i = 0; + foreach (var obj in ResolveParameters(c, d, cancelToken).ToArray()) + { + i++; + parameters[i] = obj; + } + + return fastInvoker.Invoke(consumer, parameters); + } + } + + private void HandleException(Exception ex, ConsumerDescriptor descriptor) + { + if (ex is AggregateException ae) + { + ae.InnerExceptions.Each(x => Logger.Error(x, $"Error invoking event consumer '{descriptor}'.")); + } + else + { + Logger.Error(ex, $"Error invoking event consumer '{descriptor}'."); + } + + if (!descriptor.FireForget) + { + throw ex; + } + } + + protected internal virtual IEnumerable ResolveParameters( + IComponentContext container, + ConsumerDescriptor descriptor, + CancellationToken cancelToken) + { + foreach (var p in descriptor.Parameters) + { + if (p.ParameterType == typeof(CancellationToken)) + { + yield return cancelToken; + } + else + { + yield return _resolver.ResolveParameter(p, container); + } + } + } + + #region APM > TPL pattern (does not always work stable) + + /// + /// Wraps a into the Begin method of an APM pattern. + /// + /// The task to wrap. + /// The callback method passed into the Begin method of the APM pattern. + /// The state passed into the Begin method of the APM pattern. + /// The asynchronous operation, to be returned by the Begin method of the APM pattern. + protected IAsyncResult BeginInvoke(Task task, AsyncCallback callback, ConsumerDescriptor descriptor) + { + var options = TaskCreationOptions.RunContinuationsAsynchronously; + var tcs = new TaskCompletionSource(descriptor, options); + + // "_ =" to discard 'async/await' compiler warning + _ = AwaitCompletionAsync(task, callback, tcs, descriptor); + + return tcs.Task; + } + + private async Task AwaitCompletionAsync( + Task task, + AsyncCallback callback, + TaskCompletionSource tcs, + ConsumerDescriptor descriptor) + { + try + { + await task; + tcs.TrySetResult(null); + } + catch (OperationCanceledException ex) + { + tcs.TrySetCanceled(ex.CancellationToken); + } + catch (Exception ex) + { + tcs.TrySetException(ex); + } + finally + { + callback?.Invoke(tcs.Task); + } + } + + protected virtual void EndInvoke(IAsyncResult asyncResult) + { + var task = (Task)asyncResult; + + if (task.IsFaulted && task.Exception != null) + { + HandleException(task.Exception, (ConsumerDescriptor)asyncResult.AsyncState); + } + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Events/EventConsumerMetadata.cs b/src/Libraries/SmartStore.Core/Events/EventConsumerMetadata.cs index 0c233e9da1..d6e5927392 100644 --- a/src/Libraries/SmartStore.Core/Events/EventConsumerMetadata.cs +++ b/src/Libraries/SmartStore.Core/Events/EventConsumerMetadata.cs @@ -3,10 +3,10 @@ namespace SmartStore.Core.Events { - public class EventConsumerMetadata - { - public bool IsActive { get; set; } - public Type ContainerType { get; set; } - public PluginDescriptor PluginDescriptor { get; set; } - } + public class EventConsumerMetadata + { + public bool IsActive { get; set; } + public Type ContainerType { get; set; } + public PluginDescriptor PluginDescriptor { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Events/FireForgetAttribute.cs b/src/Libraries/SmartStore.Core/Events/FireForgetAttribute.cs index a5076473c9..f5992a9bd9 100644 --- a/src/Libraries/SmartStore.Core/Events/FireForgetAttribute.cs +++ b/src/Libraries/SmartStore.Core/Events/FireForgetAttribute.cs @@ -2,19 +2,19 @@ namespace SmartStore.Core.Events { - /// - /// Specifies whether an event consumer method should be invoked asynchronously without awaiting. - /// This can be advantegous in long running processes, because the current request thread will not 'block' - /// until method completion. - /// - /// - /// Use this with caution and only if you know what you are doing :-) - /// A class containing a fire & forget consumer should NOT take dependencies on request scoped services, because task continuation - /// happens on another thread and context gets lost. Pass required dependencies as method parameters instead, the consumer invoker - /// will spawn a new private context for the unit of work and resolve dependencies from this context. - /// - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] - public sealed class FireForgetAttribute : Attribute - { - } + /// + /// Specifies whether an event consumer method should be invoked asynchronously without awaiting. + /// This can be advantegous in long running processes, because the current request thread will not 'block' + /// until method completion. + /// + /// + /// Use this with caution and only if you know what you are doing :-) + /// A class containing a fire & forget consumer should NOT take dependencies on request scoped services, because task continuation + /// happens on another thread and context gets lost. Pass required dependencies as method parameters instead, the consumer invoker + /// will spawn a new private context for the unit of work and resolve dependencies from this context. + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class FireForgetAttribute : Attribute + { + } } diff --git a/src/Libraries/SmartStore.Core/Events/IConsumer.cs b/src/Libraries/SmartStore.Core/Events/IConsumer.cs index da2ff66d89..f5fa00be1d 100644 --- a/src/Libraries/SmartStore.Core/Events/IConsumer.cs +++ b/src/Libraries/SmartStore.Core/Events/IConsumer.cs @@ -2,44 +2,44 @@ namespace SmartStore.Core.Events { - /// - /// Marker interface for classes that contain one or more - /// event message handlers. A handler is a public instance method following some conventions. - /// The name of the handler method must be one of: - /// - /// void Handle(TMessage msg) - /// void HandleEvent(TMessage msg) - /// void Consume(TMessage msg) - /// Task HandleAsync(TMessage msg) - /// Task HandleEventAsync(TMessage msg) - /// Task ConsumeAsync(TMessage msg) - /// - /// Alternatively, the TMessage param can be the generic type. - /// - /// The decides how to call the method based on its signature: - /// - /// void method are invoked synchronously. - /// methods are invoked asynchronously and awaited. - /// The makes the method run in the background without awaiting. - /// - /// - /// - /// You can declare additional dependency parameters in the handler method, e.g.: - /// - /// Task HandleEventAsync(SomeEvent message, IDbContext dbContext, ICacheManager cache, CancellationToken cancelToken) - /// - /// Order does not matter. The invoker will auto-resolve appropriate instances and pass them to the method. But any unregistered - /// dependency or a primitive type will result in an exception (except of which always resolves to - /// ). - /// - /// - public interface IConsumer - { - } + /// + /// Marker interface for classes that contain one or more + /// event message handlers. A handler is a public instance method following some conventions. + /// The name of the handler method must be one of: + /// + /// void Handle(TMessage msg) + /// void HandleEvent(TMessage msg) + /// void Consume(TMessage msg) + /// Task HandleAsync(TMessage msg) + /// Task HandleEventAsync(TMessage msg) + /// Task ConsumeAsync(TMessage msg) + /// + /// Alternatively, the TMessage param can be the generic type. + /// + /// The decides how to call the method based on its signature: + /// + /// void method are invoked synchronously. + /// methods are invoked asynchronously and awaited. + /// The makes the method run in the background without awaiting. + /// + /// + /// + /// You can declare additional dependency parameters in the handler method, e.g.: + /// + /// Task HandleEventAsync(SomeEvent message, IDbContext dbContext, ICacheManager cache, CancellationToken cancelToken) + /// + /// Order does not matter. The invoker will auto-resolve appropriate instances and pass them to the method. But any unregistered + /// dependency or a primitive type will result in an exception (except of which always resolves to + /// ). + /// + /// + public interface IConsumer + { + } - [Obsolete("Implement the non-generic 'IConsumer' interface instead.")] - public interface IConsumer : IConsumer - { - void HandleEvent(T message); - } + [Obsolete("Implement the non-generic 'IConsumer' interface instead.")] + public interface IConsumer : IConsumer + { + void HandleEvent(T message); + } } diff --git a/src/Libraries/SmartStore.Core/Events/IConsumerInvoker.cs b/src/Libraries/SmartStore.Core/Events/IConsumerInvoker.cs index 85036e7528..18a6fd082a 100644 --- a/src/Libraries/SmartStore.Core/Events/IConsumerInvoker.cs +++ b/src/Libraries/SmartStore.Core/Events/IConsumerInvoker.cs @@ -1,12 +1,10 @@ -using System; - -namespace SmartStore.Core.Events +namespace SmartStore.Core.Events { - /// - /// Responsible for invoking event message handler methods. - /// - public interface IConsumerInvoker - { - void Invoke(ConsumerDescriptor descriptor, IConsumer consumer, ConsumeContext envelope) where TMessage : class; - } + /// + /// Responsible for invoking event message handler methods. + /// + public interface IConsumerInvoker + { + void Invoke(ConsumerDescriptor descriptor, IConsumer consumer, ConsumeContext envelope) where TMessage : class; + } } diff --git a/src/Libraries/SmartStore.Core/Events/IConsumerRegistry.cs b/src/Libraries/SmartStore.Core/Events/IConsumerRegistry.cs index 4339d6fb4f..a23e6e97de 100644 --- a/src/Libraries/SmartStore.Core/Events/IConsumerRegistry.cs +++ b/src/Libraries/SmartStore.Core/Events/IConsumerRegistry.cs @@ -1,13 +1,12 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace SmartStore.Core.Events { - /// - /// A registry for fast lookup. - /// - public interface IConsumerRegistry - { - IEnumerable GetConsumers(object message); - } + /// + /// A registry for fast lookup. + /// + public interface IConsumerRegistry + { + IEnumerable GetConsumers(object message); + } } diff --git a/src/Libraries/SmartStore.Core/Events/IConsumerResolver.cs b/src/Libraries/SmartStore.Core/Events/IConsumerResolver.cs index 7a7be293d7..48d940e611 100644 --- a/src/Libraries/SmartStore.Core/Events/IConsumerResolver.cs +++ b/src/Libraries/SmartStore.Core/Events/IConsumerResolver.cs @@ -1,16 +1,15 @@ -using System; -using System.Reflection; +using System.Reflection; using Autofac; namespace SmartStore.Core.Events { - /// - /// Resolves the concrete class instance which contains - /// the requested consumer. - /// - public interface IConsumerResolver - { - IConsumer Resolve(ConsumerDescriptor descriptor); - object ResolveParameter(ParameterInfo p, IComponentContext c = null); - } + /// + /// Resolves the concrete class instance which contains + /// the requested consumer. + /// + public interface IConsumerResolver + { + IConsumer Resolve(ConsumerDescriptor descriptor); + object ResolveParameter(ParameterInfo p, IComponentContext c = null); + } } diff --git a/src/Libraries/SmartStore.Core/Events/IEventPublisher.cs b/src/Libraries/SmartStore.Core/Events/IEventPublisher.cs index 21f0f6ebe6..f4cbbd9724 100644 --- a/src/Libraries/SmartStore.Core/Events/IEventPublisher.cs +++ b/src/Libraries/SmartStore.Core/Events/IEventPublisher.cs @@ -1,9 +1,7 @@ -using System; - -namespace SmartStore.Core.Events +namespace SmartStore.Core.Events { - public interface IEventPublisher - { - void Publish(T message) where T : class; - } + public interface IEventPublisher + { + void Publish(T message) where T : class; + } } diff --git a/src/Libraries/SmartStore.Core/Events/NullEventPublisher.cs b/src/Libraries/SmartStore.Core/Events/NullEventPublisher.cs index 07d7ae6e11..18c6cea799 100644 --- a/src/Libraries/SmartStore.Core/Events/NullEventPublisher.cs +++ b/src/Libraries/SmartStore.Core/Events/NullEventPublisher.cs @@ -1,18 +1,15 @@  namespace SmartStore.Core.Events { - public class NullEventPublisher : IEventPublisher - { - private readonly static IEventPublisher s_instance = new NullEventPublisher(); + public class NullEventPublisher : IEventPublisher + { + private readonly static IEventPublisher s_instance = new NullEventPublisher(); - public static IEventPublisher Instance - { - get { return s_instance; } - } + public static IEventPublisher Instance => s_instance; - public void Publish(T eventMessage) where T : class - { - // Noop - } - } + public void Publish(T eventMessage) where T : class + { + // Noop + } + } } diff --git a/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs index 5bd474e782..bae926e8e1 100644 --- a/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/CollectionExtensions.cs @@ -1,5 +1,5 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; +using System.Runtime.CompilerServices; using SmartStore.Collections; namespace SmartStore @@ -11,34 +11,35 @@ public static void AddRange(this ICollection initial, IEnumerable other if (other == null) return; - if (initial is List list) - { - list.AddRange(other); - return; - } + if (initial is List list) + { + list.AddRange(other); + return; + } - foreach (var local in other) - { - initial.Add(local); - } + foreach (var local in other) + { + initial.Add(local); + } } - public static SyncedCollection AsSynchronized(this ICollection source) - { - return AsSynchronized(source, new object()); - } + public static SyncedCollection AsSynchronized(this ICollection source) + { + return AsSynchronized(source, new object()); + } - public static SyncedCollection AsSynchronized(this ICollection source, object syncRoot) - { - if (source is SyncedCollection sc) - { - return sc; - } + public static SyncedCollection AsSynchronized(this ICollection source, object syncRoot) + { + if (source is SyncedCollection sc) + { + return sc; + } - return new SyncedCollection(source, syncRoot); - } + return new SyncedCollection(source, syncRoot); + } - public static bool IsNullOrEmpty(this ICollection source) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNullOrEmpty(this ICollection source) { return source == null || source.Count == 0; } diff --git a/src/Libraries/SmartStore.Core/Extensions/ConversionExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/ConversionExtensions.cs index 58992963be..4689401f35 100644 --- a/src/Libraries/SmartStore.Core/Extensions/ConversionExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/ConversionExtensions.cs @@ -4,89 +4,94 @@ using System.Globalization; using System.IO; using System.IO.Compression; +using System.Runtime.CompilerServices; using System.Runtime.Serialization.Formatters.Binary; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using SmartStore.ComponentModel; using SmartStore.Utilities; +using SmartStore.Utilities.ObjectPools; namespace SmartStore { - public static class ConversionExtensions + public static class ConversionExtensions { #region Object + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T Convert(this object value) { - return (T)(Convert(value, typeof(T)) ?? default(T)); - } + if (!CommonHelper.TryConvert(value, typeof(T), CultureInfo.InvariantCulture, out object result)) + { + return default(T); + } - public static T Convert(this object value, T defaultValue) - { - return (T)(Convert(value, typeof(T)) ?? defaultValue); - } + return (T)result; + } - public static T Convert(this object value, CultureInfo culture) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Convert(this object value, T defaultValue) { - return (T)(Convert(value, typeof(T), culture) ?? default(T)); - } + if (!CommonHelper.TryConvert(value, typeof(T), CultureInfo.InvariantCulture, out object result)) + { + return defaultValue; + } - public static T Convert(this object value, T defaultValue, CultureInfo culture) - { - return (T)(Convert(value, typeof(T), culture) ?? defaultValue); - } + return (T)result; + } - public static object Convert(this object value, Type to) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Convert(this object value, CultureInfo culture) { - return value.Convert(to, CultureInfo.InvariantCulture); + if (!CommonHelper.TryConvert(value, typeof(T), culture, out object result)) + { + return default(T); + } + + return (T)result; } - public static object Convert(this object value, Type to, CultureInfo culture) - { - if (to == null) - throw new ArgumentNullException(nameof(to)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Convert(this object value, T defaultValue, CultureInfo culture) + { + if (!CommonHelper.TryConvert(value, typeof(T), culture, out object result)) + { + return defaultValue; + } - if (value == null || value == DBNull.Value || to.IsInstanceOfType(value)) - { - return value == DBNull.Value ? null : value; - } + return (T)result; + } - Type from = value.GetType(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static object Convert(this object value, Type to) + { + if (!CommonHelper.TryConvert(value, to, CultureInfo.InvariantCulture, out object result)) + { + return null; + //throw Error.InvalidCast(value?.GetType(), to); + } + + return result; + } - if (culture == null) - { - culture = CultureInfo.InvariantCulture; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static object Convert(this object value, Type to, CultureInfo culture) + { + if (!CommonHelper.TryConvert(value, to, culture, out object result)) + { + return null; + //throw Error.InvalidCast(value?.GetType(), to); } - // get a converter for 'to' (value -> to) - var converter = TypeConverterFactory.GetConverter(to); - if (converter != null && converter.CanConvertFrom(from)) - { - return converter.ConvertFrom(culture, value); - } - - // try the other way round with a 'from' converter (to <- from) - converter = TypeConverterFactory.GetConverter(from); - if (converter != null && converter.CanConvertTo(to)) - { - return converter.ConvertTo(culture, null, value, to); - } - - // use Convert.ChangeType if both types are IConvertible - if (value is IConvertible && typeof(IConvertible).IsAssignableFrom(to)) - { - return System.Convert.ChangeType(value, to, culture); - } - - throw Error.InvalidCast(from, to); - } + return result; + } #endregion #region int + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static char ToHex(this int value) { if (value <= 9) @@ -101,51 +106,57 @@ public static char ToHex(this int value) #region String + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ToInt(this string value, int defaultValue = 0) { - if (CommonHelper.TryConvert(value, out int result)) - { - return result; - } + if (CommonHelper.TryConvert(value, typeof(int), CultureInfo.InvariantCulture, out object result)) + { + return (int)result; + } - return defaultValue; + return defaultValue; } - public static char ToChar(this string value, bool unescape = false, char defaultValue = '\0') - { - if (value.HasValue() && char.TryParse(unescape ? Regex.Unescape(value) : value, out char result)) - { - return result; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static char ToChar(this string value, bool unescape = false, char defaultValue = '\0') + { + if (value.HasValue() && char.TryParse(unescape ? Regex.Unescape(value) : value, out char result)) + { + return result; + } - return defaultValue; - } + return defaultValue; + } - public static float ToFloat(this string value, float defaultValue = 0) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float ToFloat(this string value, float defaultValue = 0) { - if (CommonHelper.TryConvert(value, out float result)) - { - return result; - } + if (CommonHelper.TryConvert(value, typeof(float), CultureInfo.InvariantCulture, out object result)) + { + return (float)result; + } - return defaultValue; + return defaultValue; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool ToBool(this string value, bool defaultValue = false) { - if (CommonHelper.TryConvert(value, out bool result)) - { - return result; - } + if (CommonHelper.TryConvert(value, typeof(bool), out object result)) + { + return (bool)result; + } - return defaultValue; + return defaultValue; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static DateTime? ToDateTime(this string value, DateTime? defaultValue) { return value.ToDateTime(null, defaultValue); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static DateTime? ToDateTime(this string value, string[] formats, DateTime? defaultValue) { return value.ToDateTime(formats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowWhiteSpaces, defaultValue); @@ -171,25 +182,25 @@ public static bool ToBool(this string value, bool defaultValue = false) return defaultValue; } - /// - /// Parse ISO-8601 UTC timestamp including milliseconds. - /// - /// - /// Dublicate can be found in HmacAuthentication class. - /// - public static DateTime? ToDateTimeIso8601(this string value) - { - if (value.HasValue()) - { - if (DateTime.TryParseExact(value, "o", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out DateTime dt)) - return dt; - - if (DateTime.TryParseExact(value, "yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt)) - return dt; - } - - return null; - } + /// + /// Parse ISO-8601 UTC timestamp including milliseconds. + /// + /// + /// Dublicate can be found in HmacAuthentication class. + /// + public static DateTime? ToDateTimeIso8601(this string value) + { + if (value.HasValue()) + { + if (DateTime.TryParseExact(value, "o", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out DateTime dt)) + return dt; + + if (DateTime.TryParseExact(value, "yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out dt)) + return dt; + } + + return null; + } [DebuggerStepThrough] public static Version ToVersion(this string value, Version defaultVersion = null) @@ -210,74 +221,114 @@ public static Version ToVersion(this string value, Version defaultVersion = null public static byte[] ToByteArray(this Stream stream) { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - if (stream is MemoryStream mem) - { - if (mem.TryGetBuffer(out var buffer)) - { - return buffer.Array; - } - - return mem.ToArray(); - } - else - { - using (var streamReader = new MemoryStream()) - { - stream.CopyTo(streamReader); - return streamReader.GetBuffer(); - } - } - } - - public static async Task ToByteArrayAsync(this Stream stream) - { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - if (stream is MemoryStream mem) - { - if (mem.TryGetBuffer(out var buffer)) - { - return buffer.Array; - } - - return mem.ToArray(); - } - else - { - using (var streamReader = new MemoryStream()) - { - await stream.CopyToAsync(streamReader); - return streamReader.ToArray(); - } - } - } - - public static string AsString(this Stream stream) - { - return stream.AsString(Encoding.UTF8); - } - - public static Task AsStringAsync(this Stream stream) - { - return stream.AsStringAsync(Encoding.UTF8); - } - - public static string AsString(this Stream stream, Encoding encoding) + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + if (stream is MemoryStream mem) + { + if (mem.TryGetBuffer(out var buffer)) + { + return buffer.Array; + } + return mem.ToArray(); + } + else + { + try + { + var len = stream.Length; + if (len > Int32.MaxValue) + { + return ToByteArrayCopy(stream); + } + + var buffer = new byte[(int)len]; + stream.Read(buffer, 0, (int)len); + return buffer; + } + catch + { + return ToByteArrayCopy(stream); + } + } + } + + public static async Task ToByteArrayAsync(this Stream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + if (stream is MemoryStream mem) + { + if (mem.TryGetBuffer(out var buffer)) + { + return buffer.Array; + } + return mem.ToArray(); + } + else + { + try + { + var len = stream.Length; + if (len > Int32.MaxValue) + { + return await ToByteArrayCopyAsync(stream); + } + + var buffer = new byte[(int)len]; + stream.Read(buffer, 0, (int)len); + return buffer; + } + catch + { + return await ToByteArrayCopyAsync(stream); + } + } + } + + private static byte[] ToByteArrayCopy(Stream stream, int capacity = 0) { - if (encoding == null) - throw new ArgumentNullException(nameof(encoding)); + using (var memStream = new MemoryStream(capacity)) + { + stream.CopyTo(memStream); + return memStream.ToArray(); + } + } - // convert stream to string - string result; + private static async Task ToByteArrayCopyAsync(Stream stream, int capacity = 0) + { + using (var memStream = new MemoryStream(capacity)) + { + await stream.CopyToAsync(memStream); + return memStream.ToArray(); + } + } - if (stream.CanSeek) - { - stream.Position = 0; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string AsString(this Stream stream) + { + return stream.AsString(Encoding.UTF8); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Task AsStringAsync(this Stream stream) + { + return stream.AsStringAsync(Encoding.UTF8); + } + + public static string AsString(this Stream stream, Encoding encoding) + { + if (encoding == null) + throw new ArgumentNullException(nameof(encoding)); + + // convert stream to string + string result; + + if (stream.CanSeek) + { + stream.Position = 0; + } using (StreamReader sr = new StreamReader(stream, encoding)) { @@ -287,37 +338,37 @@ public static string AsString(this Stream stream, Encoding encoding) return result; } - public static Task AsStringAsync(this Stream stream, Encoding encoding) - { - if (encoding == null) - throw new ArgumentNullException(nameof(encoding)); + public static Task AsStringAsync(this Stream stream, Encoding encoding) + { + if (encoding == null) + throw new ArgumentNullException(nameof(encoding)); - // convert stream to string - Task result; + // convert stream to string + Task result; - if (stream.CanSeek) - { - stream.Position = 0; - } + if (stream.CanSeek) + { + stream.Position = 0; + } - using (StreamReader sr = new StreamReader(stream, encoding)) - { - result = sr.ReadToEndAsync(); - } + using (StreamReader sr = new StreamReader(stream, encoding)) + { + result = sr.ReadToEndAsync(); + } - return result; - } + return result; + } - #endregion + #endregion - #region ByteArray + #region ByteArray - /// - /// Converts a byte array into an object. - /// - /// Object to deserialize. May be null. - /// Deserialized object, or null if input was null. - public static object ToObject(this byte[] bytes) + /// + /// Converts a byte array into an object. + /// + /// Object to deserialize. May be null. + /// Deserialized object, or null if input was null. + public static object ToObject(this byte[] bytes) { if (bytes == null) return null; @@ -333,18 +384,18 @@ public static Stream ToStream(this byte[] bytes) return new MemoryStream(bytes); } - /// - /// Computes the MD5 hash of a byte array - /// - /// The byte array to compute the hash for - /// The hash value - [DebuggerStepThrough] - public static string Hash(this byte[] value, bool toBase64 = false) + /// + /// Computes the MD5 hash of a byte array + /// + /// The byte array to compute the hash for + /// The hash value + [DebuggerStepThrough] + public static string Hash(this byte[] value, bool toBase64 = false) { - if (value == null) - throw new ArgumentNullException(nameof(value)); + if (value == null) + throw new ArgumentNullException(nameof(value)); - using (MD5 md5 = MD5.Create()) + using (MD5 md5 = MD5.Create()) { if (toBase64) @@ -354,7 +405,8 @@ public static string Hash(this byte[] value, bool toBase64 = false) } else { - StringBuilder sb = new StringBuilder(); + var psb = PooledStringBuilder.Rent(); + var sb = (StringBuilder)psb; byte[] hashBytes = md5.ComputeHash(value); foreach (byte b in hashBytes) @@ -362,54 +414,55 @@ public static string Hash(this byte[] value, bool toBase64 = false) sb.Append(b.ToString("x2").ToLower()); } - return sb.ToString(); + return psb.ToStringAndReturn(); } } } - /// - /// Compresses the input buffer with - /// - /// Decompressed input - /// The compressed result - public static byte[] Zip(this byte[] buffer) - { - if (buffer == null) - throw new ArgumentNullException(nameof(buffer)); - - using (var compressedStream = new MemoryStream()) - using (var zipStream = new GZipStream(compressedStream, CompressionMode.Compress)) - { - zipStream.Write(buffer, 0, buffer.Length); - zipStream.Close(); - return compressedStream.ToArray(); - } - } - - /// - /// Decompresses the input buffer with decompression - /// - /// Compressed input - /// The decompressed result - public static byte[] UnZip(this byte[] buffer) - { - if (buffer == null) - throw new ArgumentNullException(nameof(buffer)); - - using (var compressedStream = new MemoryStream(buffer)) - using (var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress)) - using (var resultStream = new MemoryStream()) - { - zipStream.CopyTo(resultStream); - return resultStream.ToArray(); - } - } - - #endregion - - #region Enumerable: Collections/List/Dictionary... - - public static T ToObject(this IDictionary values) where T : class + /// + /// Compresses the input buffer with + /// + /// Decompressed input + /// The compressed result + public static byte[] Zip(this byte[] buffer) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer)); + + using (var compressedStream = new MemoryStream()) + using (var zipStream = new GZipStream(compressedStream, CompressionMode.Compress)) + { + zipStream.Write(buffer, 0, buffer.Length); + zipStream.Close(); + return compressedStream.ToArray(); + } + } + + /// + /// Decompresses the input buffer with decompression + /// + /// Compressed input + /// The decompressed result + public static byte[] UnZip(this byte[] buffer) + { + if (buffer == null) + throw new ArgumentNullException(nameof(buffer)); + + using (var compressedStream = new MemoryStream(buffer)) + using (var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress)) + using (var resultStream = new MemoryStream()) + { + zipStream.CopyTo(resultStream); + return resultStream.ToArray(); + } + } + + #endregion + + #region Enumerable: Collections/List/Dictionary... + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ToObject(this IDictionary values) where T : class { return (T)values.ToObject(typeof(T)); } diff --git a/src/Libraries/SmartStore.Core/Extensions/DateTimeExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/DateTimeExtensions.cs index 072e55229d..517bab695a 100644 --- a/src/Libraries/SmartStore.Core/Extensions/DateTimeExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/DateTimeExtensions.cs @@ -1,20 +1,19 @@ using System; -using System.Xml; using System.Globalization; -using TimeZone = System.TimeZoneInfo; -using System.Text.RegularExpressions; +using System.Runtime.CompilerServices; namespace SmartStore { public static class DateTimeExtensions { - public static readonly DateTime BeginOfEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + public static readonly DateTime BeginOfEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); /// /// Converts a nullable date/time value to UTC. /// /// The nullable date/time /// The nullable date/time in UTC + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static DateTime? ToUniversalTime(this DateTime? value) { return value.HasValue ? value.Value.ToUniversalTime() : (DateTime?)null; @@ -25,6 +24,7 @@ public static class DateTimeExtensions /// /// The nullable UTC date/time /// The nullable UTC date/time as local time + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static DateTime? ToLocalTime(this DateTime? value) { return value.HasValue ? value.Value.ToLocalTime() : (DateTime?)null; @@ -102,7 +102,7 @@ public static DateTime GetEvenMinuteDateBefore(this DateTime? value) public static long ToJavaScriptTicks(this DateTime value) { DateTimeOffset utcDateTime = value.ToUniversalTime(); - long javaScriptTicks = (utcDateTime.Ticks - BeginOfEpoch.Ticks) / (long)10000; + long javaScriptTicks = (utcDateTime.Ticks - BeginOfEpoch.Ticks) / (long)10000; return javaScriptTicks; } @@ -133,66 +133,79 @@ public static DateTime GetLastDayOfMonth(this DateTime value) return dtTo; } - public static DateTime ToEndOfTheDay(this DateTime value) - { - if (value != null) - return new DateTime(value.Year, value.Month, value.Day, 23, 59, 59); - return value; - } - - public static DateTime? ToEndOfTheDay(this DateTime? value) - { - return (value.HasValue ? value.Value.ToEndOfTheDay() : value); - } - - /// - /// Epoch time. Number of seconds since midnight (UTC) on 1st January 1970. - /// - public static long ToUnixTime(this DateTime value) - { - return Convert.ToInt64((value.ToUniversalTime() - BeginOfEpoch).TotalSeconds); - } - - /// - /// UTC date based on number of seconds since midnight (UTC) on 1st January 1970. - /// - public static DateTime FromUnixTime(this long unixTime) - { - return BeginOfEpoch.AddSeconds(unixTime); - } - - /// - /// Converts a DateTime to a string with native digits - /// - public static string ToNativeString(this DateTime value) - { - return value.ToNativeString(null, null); - } - - /// - /// Converts a DateTime to a string with native digits - /// - public static string ToNativeString(this DateTime value, IFormatProvider provider) - { - return value.ToNativeString(null, provider); - } - - /// - /// Converts a DateTime to a string with native digits - /// - public static string ToNativeString(this DateTime value, string format) - { - return value.ToNativeString(format, null); - } - - /// - /// Converts a DateTime to a string with native digits - /// - public static string ToNativeString(this DateTime value, string format, IFormatProvider provider) - { - provider = provider ?? CultureInfo.CurrentCulture; - return value.ToString(format, provider).ReplaceNativeDigits(provider); - } + public static DateTime ToEndOfTheDay(this DateTime value) + { + if (value != null) + return new DateTime(value.Year, value.Month, value.Day, 23, 59, 59); + return value; + } + + public static DateTime? ToEndOfTheDay(this DateTime? value) + { + return (value.HasValue ? value.Value.ToEndOfTheDay() : value); + } + + /// + /// Epoch time. Number of seconds since midnight (UTC) on 1st January 1970. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long ToUnixTime(this DateTime value) + { + return Convert.ToInt64((value.ToUniversalTime() - BeginOfEpoch).TotalSeconds); + } + + /// + /// UTC date based on number of seconds since midnight (UTC) on 1st January 1970. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DateTime FromUnixTime(this long unixTime) + { + return BeginOfEpoch.AddSeconds(unixTime); + } + + /// + /// Converts a DateTime to a string with native digits + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToNativeString(this DateTime value) + { + return value.ToNativeString(null, null); + } + + /// + /// Converts a DateTime to a string with native digits + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToNativeString(this DateTime value, IFormatProvider provider) + { + return value.ToNativeString(null, provider); + } + + /// + /// Converts a DateTime to a string with native digits + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToNativeString(this DateTime value, string format) + { + return value.ToNativeString(format, null); + } + + /// + /// Converts a DateTime to a string with native digits + /// + public static string ToNativeString(this DateTime value, string format, IFormatProvider provider) + { + provider = provider ?? CultureInfo.CurrentCulture; + return value.ToString(format, provider).ReplaceNativeDigits(provider); + } + + /// + /// Converts a DateTime to ISO 8601 string + /// + public static string ToIso8601String(this DateTime value) + { + return value.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); + } } } diff --git a/src/Libraries/SmartStore.Core/Extensions/DictionaryExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/DictionaryExtensions.cs index 3a3edf2f83..24b46dddf9 100644 --- a/src/Libraries/SmartStore.Core/Extensions/DictionaryExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/DictionaryExtensions.cs @@ -1,11 +1,8 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web; -using System.Web.Routing; -using System.Globalization; using System.Dynamic; +using System.Linq; +using System.Runtime.CompilerServices; using SmartStore.Utilities; namespace SmartStore @@ -24,20 +21,23 @@ public static void AddRange(this IDictionary values, } } - public static void Merge(this IDictionary instance, string key, object value, bool replaceExisting = true) + public static IDictionary Merge(this IDictionary instance, string key, object value, bool replaceExisting = true) { if (replaceExisting || !instance.ContainsKey(key)) { instance[key] = value; } + + return instance; } - public static void Merge(this IDictionary instance, object values, bool replaceExisting = true) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IDictionary Merge(this IDictionary instance, object values, bool replaceExisting = true) { - instance.Merge(CommonHelper.ObjectToDictionary(values), replaceExisting); + return instance.Merge(CommonHelper.ObjectToDictionary(values), replaceExisting); } - public static void Merge(this IDictionary instance, IDictionary from, bool replaceExisting = true) + public static IDictionary Merge(this IDictionary instance, IDictionary from, bool replaceExisting = true) { foreach (var kvp in from) { @@ -46,50 +46,58 @@ public static void Merge(this IDictionary instance, instance[kvp.Key] = kvp.Value; } } + + return instance; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IDictionary AppendInValue(this IDictionary instance, string key, string separator, string value) + { + return AddInValue(instance, key, separator, value, false); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IDictionary PrependInValue(this IDictionary instance, string key, string separator, string value) + { + return AddInValue(instance, key, separator, value, true); } - public static void AppendInValue(this IDictionary instance, string key, string separator, string value) + private static IDictionary AddInValue(IDictionary instance, string key, string separator, string value, bool prepend = false) { - AddInValue(instance, key, separator, value, false); - } + if (!instance.TryGetValue(key, out var obj)) + { + instance[key] = value; + } + else + { + var arr = obj.ToString().Trim().Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries).AsEnumerable(); + var arrValue = value.Trim().Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries).AsEnumerable(); + + arr = prepend ? arrValue.Union(arr) : arr.Union(arrValue); + + instance[key] = string.Join(separator, arr); + } + + return instance; + + } - public static void PrependInValue(this IDictionary instance, string key, string separator, string value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TValue Get(this IDictionary instance, TKey key) { - AddInValue(instance, key, separator, value, true); + if (instance == null) + throw new ArgumentNullException(nameof(instance)); + + instance.TryGetValue(key, out var val); + return val; } - private static void AddInValue(IDictionary instance, string key, string separator, string value, bool prepend = false) - { - if (!instance.TryGetValue(key, out var obj)) - { - instance[key] = value; - } - else - { - var arr = obj.ToString().Trim().Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries).AsEnumerable(); - var arrValue = value.Trim().Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries).AsEnumerable(); - - arr = prepend ? arrValue.Union(arr) : arr.Union(arrValue); - - instance[key] = string.Join(separator, arr); - } - } - - public static TValue Get(this IDictionary instance, TKey key) - { - if (instance == null) - throw new ArgumentNullException(nameof(instance)); - - instance.TryGetValue(key, out var val); - return val; - } - - public static ExpandoObject ToExpandoObject(this IDictionary source, bool castIfPossible = false) + public static ExpandoObject ToExpandoObject(this IDictionary source, bool castIfPossible = false) { - if (source == null) - throw new ArgumentNullException(nameof(source)); + if (source == null) + throw new ArgumentNullException(nameof(source)); - if (castIfPossible && source is ExpandoObject) + if (castIfPossible && source is ExpandoObject) { return source as ExpandoObject; } @@ -101,42 +109,42 @@ public static ExpandoObject ToExpandoObject(this IDictionary sou } - public static bool TryAdd(this Dictionary source, TKey key, TValue value, bool updateIfExists = false) - { - if (source == null || key == null) - { - return false; - } - - if (source.ContainsKey(key)) - { - if (updateIfExists) - { - source[key] = value; - return true; - } - } - else - { - source.Add(key, value); - return true; - } - - return false; - } - - public static bool TryRemove(this Dictionary source, TKey key, out TValue value) - { - value = default(TValue); - - if (source != null && key != null && source.TryGetValue(key, out value)) - { - source.Remove(key); - return true; - } - - return false; - } + public static bool TryAdd(this IDictionary source, TKey key, TValue value, bool updateIfExists = false) + { + if (source == null || key == null) + { + return false; + } + + if (source.ContainsKey(key)) + { + if (updateIfExists) + { + source[key] = value; + return true; + } + } + else + { + source.Add(key, value); + return true; + } + + return false; + } + + public static bool TryRemove(this IDictionary source, TKey key, out TValue value) + { + value = default(TValue); + + if (source != null && key != null && source.TryGetValue(key, out value)) + { + source.Remove(key); + return true; + } + + return false; + } } } diff --git a/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs index cb529dcace..f867372efa 100644 --- a/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/EnumerableExtensions.cs @@ -2,147 +2,147 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Web; using SmartStore.Collections; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using SmartStore.Core; +using SmartStore.Utilities.ObjectPools; namespace SmartStore { - public static class CollectionSlicer - { - /// - /// Slices the iteration over an enumerable by the given slice sizes. - /// - /// - /// The source sequence to slice - /// - /// Slice sizes. At least one size is required. Multiple sizes result in differently sized slices, - /// whereat the last size is used for the "rest" (if any) - /// - /// The sliced enumerable - public static IEnumerable> Slice(this IEnumerable source, params int[] sizes) - { - if (!sizes.Any(step => step != 0)) - { - throw new InvalidOperationException("Can't slice a collection with step length 0."); - } - - return new Slicer(source.GetEnumerator(), sizes).Slice(); - } - } - - internal sealed class Slicer - { - private readonly IEnumerator _iterator; - private readonly int[] _sizes; - private volatile bool _hasNext; - private volatile int _currentSize; - private volatile int _index; - - public Slicer(IEnumerator iterator, int[] sizes) - { - _iterator = iterator; - _sizes = sizes; - _index = 0; - _currentSize = 0; - _hasNext = true; - } - - public int Index - { - get { return _index; } - } - - public IEnumerable> Slice() - { - var length = _sizes.Length; - var index = 1; - var size = 0; - - for (var i = 0; _hasNext; ++i) - { - if (i < length) - { - size = _sizes[i]; - _currentSize = size - 1; - } - - while (_index < index && _hasNext) - { - _hasNext = MoveNext(); - } - - if (_hasNext) - { - yield return new List(SliceInternal()); - index += size; - } - } - } - - private IEnumerable SliceInternal() - { - if (_currentSize == -1) yield break; - yield return _iterator.Current; - - for (var count = 0; count < _currentSize && _hasNext; ++count) - { - _hasNext = MoveNext(); - - if (_hasNext) - { - yield return _iterator.Current; - } - } - } - - private bool MoveNext() - { - ++_index; - return _iterator.MoveNext(); - } - } - - [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] - public static class EnumerableExtensions - { - #region Nested classes - - private static class DefaultReadOnlyCollection - { - private static ReadOnlyCollection defaultCollection; - - [SuppressMessage("ReSharper", "ConvertIfStatementToNullCoalescingExpression")] - internal static ReadOnlyCollection Empty - { - get - { - if (defaultCollection == null) - { - defaultCollection = new ReadOnlyCollection(new T[0]); - } - return defaultCollection; - } - } - } - - #endregion - - #region IEnumerable - - /// - /// Performs an action on each item while iterating through a list. - /// This is a handy shortcut for foreach(item in list) { ... } - /// - /// The type of the items. - /// The list, which holds the objects. - /// The action delegate which is called on each item while iterating. - [DebuggerStepThrough] - public static void Each(this IEnumerable source, Action action) + public static class CollectionSlicer + { + /// + /// Slices the iteration over an enumerable by the given slice sizes. + /// + /// + /// The source sequence to slice + /// + /// Slice sizes. At least one size is required. Multiple sizes result in differently sized slices, + /// whereat the last size is used for the "rest" (if any) + /// + /// The sliced enumerable + public static IEnumerable> Slice(this IEnumerable source, params int[] sizes) + { + if (!sizes.Any(step => step != 0)) + { + throw new InvalidOperationException("Can't slice a collection with step length 0."); + } + + return new Slicer(source.GetEnumerator(), sizes).Slice(); + } + } + + internal sealed class Slicer + { + private readonly IEnumerator _iterator; + private readonly int[] _sizes; + private volatile bool _hasNext; + private volatile int _currentSize; + private volatile int _index; + + public Slicer(IEnumerator iterator, int[] sizes) + { + _iterator = iterator; + _sizes = sizes; + _index = 0; + _currentSize = 0; + _hasNext = true; + } + + public int Index => _index; + + public IEnumerable> Slice() + { + var length = _sizes.Length; + var index = 1; + var size = 0; + + for (var i = 0; _hasNext; ++i) + { + if (i < length) + { + size = _sizes[i]; + _currentSize = size - 1; + } + + while (_index < index && _hasNext) + { + _hasNext = MoveNext(); + } + + if (_hasNext) + { + yield return new List(SliceInternal()); + index += size; + } + } + } + + private IEnumerable SliceInternal() + { + if (_currentSize == -1) yield break; + yield return _iterator.Current; + + for (var count = 0; count < _currentSize && _hasNext; ++count) + { + _hasNext = MoveNext(); + + if (_hasNext) + { + yield return _iterator.Current; + } + } + } + + private bool MoveNext() + { + ++_index; + return _iterator.MoveNext(); + } + } + + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + public static class EnumerableExtensions + { + #region Nested classes + + private static class DefaultReadOnlyCollection + { + private static ReadOnlyCollection defaultCollection; + + [SuppressMessage("ReSharper", "ConvertIfStatementToNullCoalescingExpression")] + internal static ReadOnlyCollection Empty + { + get + { + if (defaultCollection == null) + { + defaultCollection = new ReadOnlyCollection(new T[0]); + } + return defaultCollection; + } + } + } + + #endregion + + #region IEnumerable + + /// + /// Performs an action on each item while iterating through a list. + /// This is a handy shortcut for foreach(item in list) { ... } + /// + /// The type of the items. + /// The list, which holds the objects. + /// The action delegate which is called on each item while iterating. + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Each(this IEnumerable source, Action action) { foreach (T t in source) { @@ -150,202 +150,208 @@ public static void Each(this IEnumerable source, Action action) } } - /// - /// Performs an action on each item while iterating through a list. - /// This is a handy shortcut for foreach(item in list) { ... } - /// - /// The type of the items. - /// The list, which holds the objects. - /// The action delegate which is called on each item while iterating. - [DebuggerStepThrough] - public static void Each(this IEnumerable source, Action action) - { - int i = 0; - foreach (T t in source) - { - action(t, i++); - } - } + /// + /// Performs an action on each item while iterating through a list. + /// This is a handy shortcut for foreach(item in list) { ... } + /// + /// The type of the items. + /// The list, which holds the objects. + /// The action delegate which is called on each item while iterating. + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Each(this IEnumerable source, Action action) + { + int i = 0; + foreach (T t in source) + { + action(t, i++); + } + } public static ReadOnlyCollection AsReadOnly(this IEnumerable source) { - if (source == null || !source.Any()) - return DefaultReadOnlyCollection.Empty; - - if (source is ReadOnlyCollection readOnly) - { - return readOnly; - } - else if (source is List list) - { - return list.AsReadOnly(); - } - - return new ReadOnlyCollection(source.ToList()); + if (source == null || !source.Any()) + return DefaultReadOnlyCollection.Empty; + + if (source is ReadOnlyCollection readOnly) + { + return readOnly; + } + else if (source is List list) + { + return list.AsReadOnly(); + } + + return new ReadOnlyCollection(source.ToList()); } - /// - /// Converts an enumerable to a dictionary while tolerating duplicate entries (last wins) - /// - /// source - /// keySelector - /// Result as dictionary - public static Dictionary ToDictionarySafe( - this IEnumerable source, - Func keySelector) - { - return source.ToDictionarySafe(keySelector, new Func(src => src), null); - } - - /// - /// Converts an enumerable to a dictionary while tolerating duplicate entries (last wins) - /// - /// source - /// keySelector - /// comparer - /// Result as dictionary - public static Dictionary ToDictionarySafe( - this IEnumerable source, - Func keySelector, - IEqualityComparer comparer) - { - return source.ToDictionarySafe(keySelector, new Func(src => src), comparer); - } - - /// - /// Converts an enumerable to a dictionary while tolerating duplicate entries (last wins) - /// - /// source - /// keySelector - /// elementSelector - /// Result as dictionary - public static Dictionary ToDictionarySafe( - this IEnumerable source, - Func keySelector, - Func elementSelector) - { - return source.ToDictionarySafe(keySelector, elementSelector, null); - } - - /// - /// Converts an enumerable to a dictionary while tolerating duplicate entries (last wins) - /// - /// source - /// keySelector - /// elementSelector - /// comparer - /// Result as dictionary - public static Dictionary ToDictionarySafe( - this IEnumerable source, - Func keySelector, - Func elementSelector, - IEqualityComparer comparer) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - - if (keySelector == null) - throw new ArgumentNullException(nameof(keySelector)); - - if (elementSelector == null) - throw new ArgumentNullException(nameof(elementSelector)); - - var dictionary = new Dictionary(comparer); - - foreach (var local in source) - { - dictionary[keySelector(local)] = elementSelector(local); - } - - return dictionary; - } - - /// The distinct by. - /// The source. - /// The key selector. - /// Source type - /// Key type - /// the unique list - public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) - where TKey : IEquatable - { - return source.Distinct(GenericEqualityComparer.CompareMember(keySelector)); - } - - /// - /// Orders a collection of entities by a specific ID sequence - /// - /// Entity type - /// The entity collection to sort - /// The IDs to order by - /// The sorted entity collection - public static IEnumerable OrderBySequence(this IEnumerable source, IEnumerable ids) where TEntity : BaseEntity - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - - if (ids == null) - throw new ArgumentNullException(nameof(ids)); - - var sorted = from id in ids - join entity in source on id equals entity.Id - select entity; - - return sorted; - } - - public static string StrJoin(this IEnumerable source, string separator) - { - return string.Join(separator, source); - } - - #endregion - - #region Multimap - - public static Multimap ToMultimap( + /// + /// Converts an enumerable to a dictionary while tolerating duplicate entries (last wins) + /// + /// source + /// keySelector + /// Result as dictionary + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Dictionary ToDictionarySafe( + this IEnumerable source, + Func keySelector) + { + return source.ToDictionarySafe(keySelector, new Func(src => src), null); + } + + /// + /// Converts an enumerable to a dictionary while tolerating duplicate entries (last wins) + /// + /// source + /// keySelector + /// comparer + /// Result as dictionary + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Dictionary ToDictionarySafe( + this IEnumerable source, + Func keySelector, + IEqualityComparer comparer) + { + return source.ToDictionarySafe(keySelector, new Func(src => src), comparer); + } + + /// + /// Converts an enumerable to a dictionary while tolerating duplicate entries (last wins) + /// + /// source + /// keySelector + /// elementSelector + /// Result as dictionary + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Dictionary ToDictionarySafe( + this IEnumerable source, + Func keySelector, + Func elementSelector) + { + return source.ToDictionarySafe(keySelector, elementSelector, null); + } + + /// + /// Converts an enumerable to a dictionary while tolerating duplicate entries (last wins) + /// + /// source + /// keySelector + /// elementSelector + /// comparer + /// Result as dictionary + public static Dictionary ToDictionarySafe( + this IEnumerable source, + Func keySelector, + Func elementSelector, + IEqualityComparer comparer) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + if (keySelector == null) + throw new ArgumentNullException(nameof(keySelector)); + + if (elementSelector == null) + throw new ArgumentNullException(nameof(elementSelector)); + + var dictionary = new Dictionary(comparer); + + foreach (var local in source) + { + dictionary[keySelector(local)] = elementSelector(local); + } + + return dictionary; + } + + /// The distinct by. + /// The source. + /// The key selector. + /// Source type + /// Key type + /// the unique list + public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) + where TKey : IEquatable + { + return source.Distinct(GenericEqualityComparer.CompareMember(keySelector)); + } + + /// + /// Orders a collection of entities by a specific ID sequence + /// + /// Entity type + /// The entity collection to sort + /// The IDs to order by + /// The sorted entity collection + public static IEnumerable OrderBySequence(this IEnumerable source, IEnumerable ids) where TEntity : BaseEntity + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + + if (ids == null) + throw new ArgumentNullException(nameof(ids)); + + var sorted = from id in ids + join entity in source on id equals entity.Id + select entity; + + return sorted; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string StrJoin(this IEnumerable source, string separator) + { + return string.Join(separator, source); + } + + #endregion + + #region Multimap + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Multimap ToMultimap( this IEnumerable source, Func keySelector, Func valueSelector) { - return source.ToMultimap(keySelector, valueSelector, null); + return source.ToMultimap(keySelector, valueSelector, null); } - public static Multimap ToMultimap( - this IEnumerable source, - Func keySelector, - Func valueSelector, - IEqualityComparer comparer) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); + public static Multimap ToMultimap( + this IEnumerable source, + Func keySelector, + Func valueSelector, + IEqualityComparer comparer) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); - if (keySelector == null) - throw new ArgumentNullException(nameof(keySelector)); + if (keySelector == null) + throw new ArgumentNullException(nameof(keySelector)); - if (valueSelector == null) - throw new ArgumentNullException(nameof(valueSelector)); + if (valueSelector == null) + throw new ArgumentNullException(nameof(valueSelector)); - var map = new Multimap(comparer); + var map = new Multimap(comparer); - foreach (var item in source) - { - map.Add(keySelector(item), valueSelector(item)); - } + foreach (var item in source) + { + map.Add(keySelector(item), valueSelector(item)); + } - return map; - } + return map; + } - #endregion + #endregion - #region NameValueCollection + #region NameValueCollection - public static void AddRange(this NameValueCollection initial, NameValueCollection other) + public static void AddRange(this NameValueCollection initial, NameValueCollection other) { - if (initial == null) - throw new ArgumentNullException(nameof(initial)); + if (initial == null) + throw new ArgumentNullException(nameof(initial)); - if (other == null) + if (other == null) return; foreach (var item in other.AllKeys) @@ -354,44 +360,45 @@ public static void AddRange(this NameValueCollection initial, NameValueCollectio } } - /// - /// Builds an URL query string - /// - /// Name value collection - /// Encoding type. Can be null. - /// Whether to encode keys and values - /// The query string without leading a question mark - public static string BuildQueryString(this NameValueCollection nvc, Encoding encoding, bool encode = true) - { - var sb = new StringBuilder(); - - if (nvc != null) - { - foreach (string str in nvc) - { - if (sb.Length > 0) - sb.Append('&'); - - if (!encode) - sb.Append(str); - else if (encoding == null) - sb.Append(HttpUtility.UrlEncode(str)); - else - sb.Append(HttpUtility.UrlEncode(str, encoding)); - - sb.Append('='); - - if (!encode) - sb.Append(nvc[str]); - else if (encoding == null) - sb.Append(HttpUtility.UrlEncode(nvc[str])); - else - sb.Append(HttpUtility.UrlEncode(nvc[str], encoding)); - } - } - - return sb.ToString(); - } + /// + /// Builds an URL query string + /// + /// Name value collection + /// Encoding type. Can be null. + /// Whether to encode keys and values + /// The query string without leading a question mark + public static string BuildQueryString(this NameValueCollection nvc, Encoding encoding, bool encode = true) + { + var psb = PooledStringBuilder.Rent(); + var sb = (StringBuilder)psb; + + if (nvc != null) + { + foreach (string str in nvc) + { + if (sb.Length > 0) + sb.Append('&'); + + if (!encode) + sb.Append(str); + else if (encoding == null) + sb.Append(HttpUtility.UrlEncode(str)); + else + sb.Append(HttpUtility.UrlEncode(str, encoding)); + + sb.Append('='); + + if (!encode) + sb.Append(nvc[str]); + else if (encoding == null) + sb.Append(HttpUtility.UrlEncode(nvc[str])); + else + sb.Append(HttpUtility.UrlEncode(nvc[str], encoding)); + } + } + + return psb.ToStringAndReturn(); + } #endregion @@ -423,36 +430,36 @@ public static int Remove(this IList list, Func selector) return count; } - #endregion + #endregion - #region Stack + #region Stack - public static bool TryPeek(this Stack stack, out T value) - { - value = default(T); + public static bool TryPeek(this Stack stack, out T value) + { + value = default(T); - if (stack.Count > 0) - { - value = stack.Peek(); - return true; - } + if (stack.Count > 0) + { + value = stack.Peek(); + return true; + } - return false; - } + return false; + } - public static bool TryPop(this Stack stack, out T value) - { - value = default(T); + public static bool TryPop(this Stack stack, out T value) + { + value = default(T); - if (stack.Count > 0) - { - value = stack.Pop(); - return true; - } + if (stack.Count > 0) + { + value = stack.Pop(); + return true; + } - return false; - } + return false; + } - #endregion - } + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Extensions/ExceptionExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/ExceptionExtensions.cs index 4f674a61cf..70f1e966e0 100644 --- a/src/Libraries/SmartStore.Core/Extensions/ExceptionExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/ExceptionExtensions.cs @@ -5,17 +5,17 @@ namespace SmartStore { - public static class ExceptionExtensions - { - public static bool IsFatal(this Exception ex) - { - return ex is StackOverflowException || - ex is OutOfMemoryException || - ex is AccessViolationException || - ex is AppDomainUnloadedException || - ex is ThreadAbortException || - ex is SecurityException || - ex is SEHException; - } - } + public static class ExceptionExtensions + { + public static bool IsFatal(this Exception ex) + { + return ex is StackOverflowException || + ex is OutOfMemoryException || + ex is AccessViolationException || + ex is AppDomainUnloadedException || + ex is ThreadAbortException || + ex is SecurityException || + ex is SEHException; + } + } } diff --git a/src/Libraries/SmartStore.Core/Extensions/ExpressionExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/ExpressionExtensions.cs new file mode 100644 index 0000000000..4d27f78734 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Extensions/ExpressionExtensions.cs @@ -0,0 +1,48 @@ +using System; +using System.Linq.Expressions; +using System.Reflection; +using SmartStore.ComponentModel; + +namespace SmartStore +{ + public static class ExpressionExtensions + { + public static FastPropertyInvoker CompileFast( + this Expression> expression, + PropertyCachingStrategy cachingStrategy = PropertyCachingStrategy.Cached) + { + if (!(expression.Body is MemberExpression member)) + { + throw new ArgumentException($"Expression body must refer to a property.", nameof(expression)); + } + + if (!(member.Member is PropertyInfo pi)) + { + throw new ArgumentException($"Expression body member must refer to a property.", nameof(expression)); + } + + var fastProp = FastProperty.GetProperty(pi, cachingStrategy); + return new FastPropertyInvoker(fastProp); + } + } + + public sealed class FastPropertyInvoker + { + internal FastPropertyInvoker(FastProperty prop) + { + Property = prop; + } + + public FastProperty Property { get; private set; } + + public TProp Invoke(T obj) + { + return (TProp)Property.GetValue(obj); + } + + public static implicit operator Func(FastPropertyInvoker obj) + { + return obj.Invoke; + } + } +} diff --git a/src/Libraries/SmartStore.Core/Extensions/HtmlTextWriterExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/HtmlTextWriterExtensions.cs index acd2f75109..df752203cd 100644 --- a/src/Libraries/SmartStore.Core/Extensions/HtmlTextWriterExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/HtmlTextWriterExtensions.cs @@ -1,10 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Web.UI; namespace SmartStore -{ +{ public static class HtmlTextWriterExtensions { public static void AddAttributes(this HtmlTextWriter writer, IDictionary attributes) diff --git a/src/Libraries/SmartStore.Core/Extensions/HttpExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/HttpExtensions.cs index 59851619db..a2618ee834 100644 --- a/src/Libraries/SmartStore.Core/Extensions/HttpExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/HttpExtensions.cs @@ -2,289 +2,342 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Net; using System.Linq; -using System.Text; +using System.Net; +using System.Runtime.CompilerServices; using System.Web; using System.Web.Caching; +using System.Web.Mvc; using System.Web.Security; -using SmartStore.Core.Infrastructure; using SmartStore.Core; -using System.Web.Mvc; using SmartStore.Core.Fakes; +using SmartStore.Core.Infrastructure; namespace SmartStore { - public static class HttpExtensions - { - private const string CACHE_REGION_NAME = "SmartStoreNET:"; - - private static readonly List> _sslHeaders = new List> - { - new Tuple("HTTP_CLUSTER_HTTPS", "on"), - new Tuple("X-Forwarded-Proto", "https"), - new Tuple("x-arr-ssl", null), - new Tuple("X-Forwarded-Protocol", "https"), - new Tuple("X-Forwarded-Ssl", "on"), - new Tuple("X-Url-Scheme", "https") - }; - - /// - /// Tries to get the instance without throwing exceptions - /// - /// The instance or null. - public static HttpRequestBase SafeGetHttpRequest(this HttpContext httpContext) - { - if (httpContext == null) - { - return null; - } - - return SafeGetHttpRequest(new HttpContextWrapper(httpContext)); - } - - /// - /// Tries to get the instance without throwing exceptions - /// - /// The instance or null. - public static HttpRequestBase SafeGetHttpRequest(this HttpContextBase httpContext) - { - if (httpContext == null) - { - return null; - } - - if (httpContext.Handler != null || httpContext is FakeHttpContext) - { - // Having a handler means we're most likely in the MVC routing pipeline. - return httpContext.Request; - } - - try - { - return httpContext.Request; - } - catch - { - return null; - } - } - - /// - /// Returns wether the specified url is local to the host or not - /// - /// - /// - /// - public static bool IsAppLocalUrl(this HttpRequestBase request, string url) - { - if (string.IsNullOrWhiteSpace(url)) - { - return false; - } - - if (url.StartsWith("~/")) - { - return true; - } - - if (url.StartsWith("//") || url.StartsWith("/\\")) - { - return false; - } - - // at this point when the url starts with "/" it is local - if (url.StartsWith("/")) - { - return true; - } - - // at this point, check for a fully qualified url - try - { - var uri = new Uri(url); - if (uri.Authority.Equals(request.Headers["Host"], StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - // finally, check the base url from the settings - var storeContext = EngineContext.Current.Resolve(); - if (storeContext != null) - { - var baseUrl = storeContext.CurrentStore.Url; - if (baseUrl.HasValue()) - { - if (uri.Authority.Equals(new Uri(baseUrl).Authority, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - } - - return false; - } - catch - { - // mall-formed url e.g, "abcdef" - return false; - } - } - - /// - /// Gets a value which indicates whether the HTTP connection uses secure sockets (HTTPS protocol). - /// Works with Cloud's load balancers. - /// - /// - /// - public static bool IsSecureConnection(this HttpRequest request) + public static class HttpExtensions + { + const string CacheRegionName = "SmartStoreNET:"; + const string RememberPathKey = "AppRelativeCurrentExecutionFilePath.Original"; + + private static readonly List> _sslHeaders = new List> + { + new Tuple("HTTP_CLUSTER_HTTPS", "on"), + new Tuple("HTTP_X_FORWARDED_PROTO", "https"), + new Tuple("X-Forwarded-Proto", "https"), + new Tuple("x-arr-ssl", null), + new Tuple("X-Forwarded-Protocol", "https"), + new Tuple("X-Forwarded-Ssl", "on"), + new Tuple("X-Url-Scheme", "https") + }; + + /// + /// Tries to get the instance without throwing exceptions + /// + /// The instance or null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HttpRequestBase SafeGetHttpRequest(this HttpContext httpContext) + { + if (httpContext == null) + { + return null; + } + + return SafeGetHttpRequest(new HttpContextWrapper(httpContext)); + } + + /// + /// Tries to get the instance without throwing exceptions + /// + /// The instance or null. + public static HttpRequestBase SafeGetHttpRequest(this HttpContextBase httpContext) + { + if (httpContext == null) + { + return null; + } + + if (httpContext.Handler != null || httpContext is FakeHttpContext) + { + // Having a handler means we're most likely in the MVC routing pipeline. + return httpContext.Request; + } + + try + { + return httpContext.Request; + } + catch + { + return null; + } + } + + /// + /// Returns wether the specified url is local to the host or not + /// + /// + /// + /// + public static bool IsAppLocalUrl(this HttpRequestBase request, string url) + { + if (string.IsNullOrWhiteSpace(url)) + { + return false; + } + + url = url.Trim(); + + if (url.StartsWith("~/")) + { + return true; + } + + if (url.StartsWith("//") || url.StartsWith("/\\")) + { + return false; + } + + // At this point when the url starts with "/" it is local + if (url.StartsWith("/")) + { + return true; + } + + // At this point, check for a fully qualified url + try + { + var uri = new Uri(url); + + if (!uri.Scheme.Equals(Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) && !uri.Scheme.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + if (uri.Authority.Equals(request.Headers["Host"], StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + // Finally, check the base url from the settings + var storeContext = EngineContext.Current.Resolve(); + if (storeContext != null) + { + var baseUrl = storeContext.CurrentStore.Url; + if (baseUrl.HasValue()) + { + if (uri.Authority.Equals(new Uri(baseUrl).Authority, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + } + + return false; + } + catch + { + // mall-formed url e.g, "abcdef" + return false; + } + } + + /// + /// Gets a value which indicates whether the HTTP connection uses secure sockets (HTTPS protocol). + /// Works with Cloud's load balancers. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsHttps(this HttpRequest request) + { + return IsHttps(new HttpRequestWrapper(request)); + } + + /// + /// Gets a value which indicates whether the HTTP connection uses secure sockets (HTTPS protocol). + /// Works with Cloud's load balancers. + /// + public static bool IsHttps(this HttpRequestBase request) + { + if (request.IsSecureConnection) + { + return true; + } + + foreach (var tuple in _sslHeaders) + { + var serverVar = request.ServerVariables[tuple.Item1]; + if (serverVar != null) + { + return tuple.Item2 == null || tuple.Item2.Equals(serverVar, StringComparison.OrdinalIgnoreCase); + } + } + + return false; + } + + /// + /// Gets a value which indicates whether the current request requests a static resource, like .txt, .pdf, .js, .css etc. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsStaticResourceRequested(this HttpContext context) { - return IsSecureConnection(new HttpRequestWrapper(request)); + return IsStaticResourceRequested(new HttpContextWrapper(context)); + } + + /// + /// Gets a value which indicates whether the current request requests a static resource, like .txt, .pdf, .js, .css etc. + /// + public static bool IsStaticResourceRequested(this HttpContextBase context) + { + if (context?.Request == null) + return false; + + return context.GetItem( + "IsStaticResourceRequested", + () => WebHelper.IsStaticResourceRequested(context.Request), + true); + } + + [SuppressMessage("ReSharper", "PossibleNullReferenceException")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetFormsAuthenticationCookie(this HttpWebRequest webRequest, HttpRequestBase httpRequest) + { + CopyCookie(webRequest, httpRequest, FormsAuthentication.FormsCookieName); + } + + [SuppressMessage("ReSharper", "PossibleNullReferenceException")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetAnonymousIdentCookie(this HttpWebRequest webRequest, HttpRequestBase httpRequest) + { + CopyCookie(webRequest, httpRequest, "SMARTSTORE.ANONYMOUS"); + } + + [SuppressMessage("ReSharper", "PossibleNullReferenceException")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetVisitorCookie(this HttpWebRequest webRequest, HttpRequestBase httpRequest) + { + CopyCookie(webRequest, httpRequest, "SMARTSTORE.VISITOR"); + } + + private static void CopyCookie(HttpWebRequest webRequest, HttpRequestBase sourceHttpRequest, string cookieName) + { + Guard.NotNull(webRequest, nameof(webRequest)); + Guard.NotNull(sourceHttpRequest, nameof(sourceHttpRequest)); + Guard.NotEmpty(cookieName, nameof(cookieName)); + + var sourceCookie = sourceHttpRequest.Cookies[cookieName]; + if (sourceCookie == null) + return; + + var sendCookie = new Cookie(sourceCookie.Name, sourceCookie.Value, sourceCookie.Path, sourceHttpRequest.Url.Host); + + if (webRequest.CookieContainer == null) + { + webRequest.CookieContainer = new CookieContainer(); + } + + webRequest.CookieContainer.Add(sendCookie); + } + + public static string BuildScopedKey(this Cache cache, string key) + { + return key.HasValue() ? CacheRegionName + key : null; + } + + public static T GetOrAdd(this Cache cache, string key, Func acquirer, TimeSpan? duration = null) + { + Guard.NotEmpty(key, nameof(key)); + Guard.NotNull(acquirer, nameof(acquirer)); + + object obj = cache.Get(key); + + if (obj != null) + { + return (T)obj; + } + + var value = acquirer(); + + var absoluteExpiration = Cache.NoAbsoluteExpiration; + if (duration.HasValue) + { + absoluteExpiration = DateTime.UtcNow + duration.Value; + } + + cache.Insert(key, value, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration); + + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RememberAppRelativePath(this HttpContextBase httpContext) + { + httpContext.Items[RememberPathKey] = httpContext.Request.AppRelativeCurrentExecutionFilePath; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetOriginalAppRelativePath(this HttpContextBase httpContext) + { + return GetItem(httpContext, RememberPathKey, forceCreation: false) ?? httpContext.Request.AppRelativeCurrentExecutionFilePath; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T GetItem(this HttpContext httpContext, string key, Func factory = null, bool forceCreation = true) + { + return GetItem(new HttpContextWrapper(httpContext), key, factory, forceCreation); + } + + public static T GetItem(this HttpContextBase httpContext, string key, Func factory = null, bool forceCreation = true) + { + Guard.NotEmpty(key, nameof(key)); + + var items = httpContext?.Items; + if (items == null) + { + return default(T); + } + + if (items.Contains(key)) + { + return (T)items[key]; + } + else + { + if (forceCreation) + { + var item = items[key] = (factory ?? (() => Activator.CreateInstance())).Invoke(); + return (T)item; + } + else + { + return default(T); + } + } + } + + public static void RemoveByPattern(this Cache cache, string pattern) + { + var keys = cache.AllKeys(pattern); + + foreach (var key in keys.ToArray()) + { + cache.Remove(key); + } + } + + public static string[] AllKeys(this Cache cache, string pattern) + { + pattern = pattern == "*" ? CacheRegionName : pattern; + + var keys = from entry in HttpRuntime.Cache.AsParallel().Cast() + let key = entry.Key.ToString() + where key.StartsWith(pattern, StringComparison.OrdinalIgnoreCase) + select key; + + return keys.ToArray(); } - /// - /// Gets a value which indicates whether the HTTP connection uses secure sockets (HTTPS protocol). - /// Works with Cloud's load balancers. - /// - /// - /// - public static bool IsSecureConnection(this HttpRequestBase request) - { - if (request.IsSecureConnection) - { - return true; - } - - foreach (var tuple in _sslHeaders) - { - var serverVar = request.ServerVariables[tuple.Item1]; - if (serverVar != null) - { - return tuple.Item2 == null || tuple.Item2.Equals(serverVar, StringComparison.OrdinalIgnoreCase); - } - } - - return false; - } - - [SuppressMessage("ReSharper", "PossibleNullReferenceException")] - public static void SetFormsAuthenticationCookie(this HttpWebRequest webRequest, HttpRequestBase httpRequest) - { - CopyCookie(webRequest, httpRequest, FormsAuthentication.FormsCookieName); - } - - [SuppressMessage("ReSharper", "PossibleNullReferenceException")] - public static void SetAnonymousIdentCookie(this HttpWebRequest webRequest, HttpRequestBase httpRequest) - { - CopyCookie(webRequest, httpRequest, "SMARTSTORE.ANONYMOUS"); - } - - private static void CopyCookie(HttpWebRequest webRequest, HttpRequestBase sourceHttpRequest, string cookieName) - { - Guard.NotNull(webRequest, nameof(webRequest)); - Guard.NotNull(sourceHttpRequest, nameof(sourceHttpRequest)); - Guard.NotEmpty(cookieName, nameof(cookieName)); - - var sourceCookie = sourceHttpRequest.Cookies[cookieName]; - if (sourceCookie == null) - return; - - var sendCookie = new Cookie(sourceCookie.Name, sourceCookie.Value, sourceCookie.Path, sourceHttpRequest.Url.Host); - - if (webRequest.CookieContainer == null) - { - webRequest.CookieContainer = new CookieContainer(); - } - - webRequest.CookieContainer.Add(sendCookie); - } - - public static string BuildScopedKey(this Cache cache, string key) - { - return key.HasValue() ? CACHE_REGION_NAME + key : null; - } - - public static T GetOrAdd(this Cache cache, string key, Func acquirer, TimeSpan? duration = null) - { - Guard.NotEmpty(key, nameof(key)); - Guard.NotNull(acquirer, nameof(acquirer)); - - object obj = cache.Get(key); - - if (obj != null) - { - return (T)obj; - } - - var value = acquirer(); - - var absoluteExpiration = Cache.NoAbsoluteExpiration; - if (duration.HasValue) - { - absoluteExpiration = DateTime.UtcNow + duration.Value; - } - - cache.Insert(key, value, null, Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration); - - return value; - } - - public static T GetItem(this HttpContext httpContext, string key, Func factory = null, bool forceCreation = true) - { - return GetItem(new HttpContextWrapper(httpContext), key, factory, forceCreation); - } - - public static T GetItem(this HttpContextBase httpContext, string key, Func factory = null, bool forceCreation = true) - { - Guard.NotEmpty(key, nameof(key)); - - var items = httpContext?.Items; - if (items == null) - { - return default(T); - } - - if (items.Contains(key)) - { - return (T)items[key]; - } - else - { - if (forceCreation) - { - var item = items[key] = (factory ?? (() => Activator.CreateInstance())).Invoke(); - return (T)item; - } - else - { - return default(T); - } - } - } - - public static void RemoveByPattern(this Cache cache, string pattern) - { - var keys = cache.AllKeys(pattern); - - foreach (var key in keys.ToArray()) - { - cache.Remove(key); - } - } - - public static string[] AllKeys(this Cache cache, string pattern) - { - pattern = pattern == "*" ? CACHE_REGION_NAME : pattern; - - var keys = from entry in HttpRuntime.Cache.AsParallel().Cast() - let key = entry.Key.ToString() - where key.StartsWith(pattern, StringComparison.OrdinalIgnoreCase) - select key; - - return keys.ToArray(); - } - - public static ControllerContext GetRootControllerContext(this ControllerContext controllerContext) + public static ControllerContext GetRootControllerContext(this ControllerContext controllerContext) { Guard.NotNull(controllerContext, nameof(controllerContext)); diff --git a/src/Libraries/SmartStore.Core/Extensions/IOExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/IOExtensions.cs index 2104c9c3ee..c4a8f10848 100644 --- a/src/Libraries/SmartStore.Core/Extensions/IOExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/IOExtensions.cs @@ -1,38 +1,65 @@ using System; -using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; namespace SmartStore { - public static class IOExtensions - { - public static bool IsFileLocked(this FileInfo file) - { - if (file == null) - return false; - - FileStream stream = null; - - try - { - stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None); - } - catch (IOException) - { - // the file is unavailable because it is: - // still being written to - // or being processed by another thread - // or does not exist (has already been processed) - return true; - } - finally - { - if (stream != null) - stream.Close(); - } - - //file is not locked - return false; - } - } + public static class IOExtensions + { + public static bool WaitForUnlock(this FileInfo file, int timeoutMs = 1000) + { + Guard.NotNull(file, nameof(file)); + + var wait = TimeSpan.FromMilliseconds(50); + var attempts = Math.Floor(timeoutMs / wait.TotalMilliseconds); + + try + { + for (var i = 0; i < attempts; i++) + { + if (!IsFileLocked(file)) + { + return true; + } + + Task.Delay(wait).Wait(); + } + + return false; + } + catch + { + return false; + } + } + + public static bool IsFileLocked(this FileInfo file) + { + if (file == null) + return false; + + FileStream stream = null; + + try + { + stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None); + } + catch (IOException) + { + // The file is unavailable because it is: + // still being written to + // or being processed by another thread + // or does not exist (has already been processed) + return true; + } + finally + { + if (stream != null) + stream.Close(); + } + + // File is not locked + return false; + } + } } diff --git a/src/Libraries/SmartStore.Core/Extensions/LinqExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/LinqExtensions.cs index ebb5faccf2..0fb5786eaa 100644 --- a/src/Libraries/SmartStore.Core/Extensions/LinqExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/LinqExtensions.cs @@ -1,52 +1,53 @@ using System; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.CompilerServices; namespace SmartStore { public static class LinqExtensions { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static PropertyInfo ExtractPropertyInfo(this LambdaExpression propertyAccessor) { return propertyAccessor.ExtractMemberInfo() as PropertyInfo; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FieldInfo ExtractFieldInfo(this LambdaExpression propertyAccessor) { return propertyAccessor.ExtractMemberInfo() as FieldInfo; } - [SuppressMessage("ReSharper", "CanBeReplacedWithTryCastAndCheckForNull")] - public static MemberInfo ExtractMemberInfo(this LambdaExpression propertyAccessor) + [SuppressMessage("ReSharper", "CanBeReplacedWithTryCastAndCheckForNull")] + public static MemberInfo ExtractMemberInfo(this LambdaExpression propertyAccessor) { - if (propertyAccessor == null) - throw new ArgumentNullException(nameof(propertyAccessor)); + if (propertyAccessor == null) + throw new ArgumentNullException(nameof(propertyAccessor)); - MemberInfo info; + MemberInfo info; try { MemberExpression operand; // o => o.PropertyOrField LambdaExpression expression = propertyAccessor; - // If the property is not an Object, then the member access expression will be wrapped in a conversion expression - // (object)o.PropertyOrField - - if (expression.Body is UnaryExpression body) - { - // o.PropertyOrField - operand = (MemberExpression)body.Operand; - } - else - { - // o.PropertyOrField - operand = (MemberExpression)expression.Body; - } - - // Member - MemberInfo member = operand.Member; + // If the property is not an Object, then the member access expression will be wrapped in a conversion expression + // (object)o.PropertyOrField + + if (expression.Body is UnaryExpression body) + { + // o.PropertyOrField + operand = (MemberExpression)body.Operand; + } + else + { + // o.PropertyOrField + operand = (MemberExpression)expression.Body; + } + + // Member + MemberInfo member = operand.Member; info = member; } catch (Exception e) diff --git a/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs index 3241c7d8fa..dc7cac2c34 100644 --- a/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/MiscExtensions.cs @@ -1,29 +1,32 @@ using System; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Text; +using SmartStore.Utilities.ObjectPools; namespace SmartStore { public static class MiscExtensions { - public static void Dump(this Exception exception) - { - try - { - exception.StackTrace.Dump(); - exception.Message.Dump(); - } - catch { } - } - - public static string ToAllMessages(this Exception exception, bool includeStackTrace = false) - { - var sb = new StringBuilder(); - - while (exception != null) - { - if (!sb.ToString().EmptyNull().Contains(exception.Message)) - { + public static void Dump(this Exception exception) + { + try + { + exception.StackTrace.Dump(); + exception.Message.Dump(); + } + catch { } + } + + public static string ToAllMessages(this Exception exception, bool includeStackTrace = false) + { + var psb = PooledStringBuilder.Rent(); + var sb = (StringBuilder)psb; + + while (exception != null) + { + if (!sb.ToString().EmptyNull().Contains(exception.Message)) + { if (includeStackTrace) { if (sb.Length > 0) @@ -37,66 +40,69 @@ public static string ToAllMessages(this Exception exception, bool includeStackTr { sb.Grow(exception.Message, " * "); } - } + } - exception = exception.InnerException; - } + exception = exception.InnerException; + } - return sb.ToString(); - } + return psb.ToStringAndReturn(); + } - public static string ToElapsedMinutes(this Stopwatch watch) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToElapsedMinutes(this Stopwatch watch) { - return "{0:0.0}".FormatWith(TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds).TotalMinutes); - } + return "{0:0.0}".FormatWith(TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds).TotalMinutes); + } - public static string ToElapsedSeconds(this Stopwatch watch) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string ToElapsedSeconds(this Stopwatch watch) { - return "{0:0.0}".FormatWith(TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds).TotalSeconds); - } + return "{0:0.0}".FormatWith(TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds).TotalSeconds); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNullOrDefault(this T? value) where T : struct { return default(T).Equals(value.GetValueOrDefault()); } - /// - /// Converts bytes into a hex string. - /// - public static string ToHexString(this byte[] bytes, int length = 0) - { + /// + /// Converts bytes into a hex string. + /// + public static string ToHexString(this byte[] bytes, int length = 0) + { if (bytes == null || bytes.Length <= 0) { return ""; } - var sb = new StringBuilder(); + var sb = new StringBuilder(); - foreach (byte b in bytes) - { - sb.Append(b.ToString("x2")); + foreach (byte b in bytes) + { + sb.Append(b.ToString("x2")); if (length > 0 && sb.Length >= length) { break; } - } - - return sb.ToString(); - } - - /// - /// Append grow if string builder is empty. Append delimiter and grow otherwise. - /// - /// Target string builder - /// Value to append - /// Delimiter to use - public static void Grow(this StringBuilder sb, string grow, string delimiter) - { + } + + return sb.ToString(); + } + + /// + /// Append grow if string builder is empty. Append delimiter and grow otherwise. + /// + /// Target string builder + /// Value to append + /// Delimiter to use + public static void Grow(this StringBuilder sb, string grow, string delimiter) + { Guard.NotNull(delimiter, nameof(delimiter)); - if (!string.IsNullOrWhiteSpace(grow)) - { + if (!string.IsNullOrWhiteSpace(grow)) + { if (sb.Length <= 0) { sb.Append(grow); @@ -105,12 +111,13 @@ public static void Grow(this StringBuilder sb, string grow, string delimiter) { sb.AppendFormat("{0}{1}", delimiter, grow); } - } - } + } + } - public static string SafeGet(this string[] arr, int index) - { - return arr != null && index < arr.Length ? arr[index] : ""; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string SafeGet(this string[] arr, int index) + { + return arr != null && index < arr.Length ? arr[index] : ""; + } } } diff --git a/src/Libraries/SmartStore.Core/Extensions/NumericExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/NumericExtensions.cs index e85c5c9a5f..16d04db508 100644 --- a/src/Libraries/SmartStore.Core/Extensions/NumericExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/NumericExtensions.cs @@ -1,75 +1,80 @@ using System; using System.Globalization; +using System.Runtime.CompilerServices; using SmartStore.Core.Domain.Directory; namespace SmartStore { public static class NumericExtensions - { - #region int + { + #region int - public static int GetRange(this int id, int size, out int lower) - { - lower = 0; - - // max 1000 values per cache item - var range = (int)Math.Ceiling((decimal)id / size) * size; + public static (int lower, int upper) GetRange(this int id, int size = 500) + { + // Max [size] values per cache item + var lower = (int)Math.Floor((decimal)id / size) * size; + var upper = lower + (size - 1); - lower = range - (size - 1); + return (lower, upper); + } - return range; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int? ZeroToNull(this int? value) + { + return value <= 0 ? null : value; + } - #endregion + #endregion - #region decimal + #region decimal - /// - /// Calculates the tax (percentage) from a gross and a net value. - /// - /// Gross value - /// Net value - /// Rounding decimal number - /// Tax percentage - public static decimal ToTaxPercentage(this decimal inclTax, decimal exclTax, int? decimals = null) - { - if (exclTax == decimal.Zero) - { - return decimal.Zero; - } + /// + /// Calculates the tax (percentage) from a gross and a net value. + /// + /// Gross value + /// Net value + /// Rounding decimal number + /// Tax percentage + public static decimal ToTaxPercentage(this decimal inclTax, decimal exclTax, int? decimals = null) + { + if (exclTax == decimal.Zero) + { + return decimal.Zero; + } - var result = ((inclTax / exclTax) - 1.0M) * 100.0M; + var result = ((inclTax / exclTax) - 1.0M) * 100.0M; - return (decimals.HasValue ? Math.Round(result, decimals.Value) : result); - } + return (decimals.HasValue ? Math.Round(result, decimals.Value) : result); + } /// /// Converts to smallest currency uint, e.g. cents /// /// Handling of the midway between two numbers. "ToEven" round down, "AwayFromZero" round up. /// Smallest currency unit + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int ToSmallestCurrencyUnit(this decimal value, MidpointRounding midpoint = MidpointRounding.AwayFromZero) - { - var result = Math.Round(value * 100, 0, midpoint); - return Convert.ToInt32(result); - } - - /// - /// Round decimal to the nearest multiple of denomination - /// - /// Value to round - /// Denomination - /// Handling of the midway between two numbers. "ToEven" round down, "AwayFromZero" round up. - /// Rounded value - public static decimal RoundToNearest(this decimal value, decimal denomination, MidpointRounding midpoint = MidpointRounding.AwayFromZero) - { - if (denomination == decimal.Zero) - { - return value; - } - - return Math.Round(value / denomination, midpoint) * denomination; - } + { + return Convert.ToInt32(Math.Round(value * 100, 0, midpoint)); + } + + /// + /// Round decimal to the nearest multiple of denomination + /// + /// Value to round + /// Denomination + /// Handling of the midway between two numbers. "ToEven" round down, "AwayFromZero" round up. + /// Rounded value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal RoundToNearest(this decimal value, decimal denomination, MidpointRounding midpoint = MidpointRounding.AwayFromZero) + { + if (denomination == decimal.Zero) + { + return value; + } + + return Math.Round(value / denomination, midpoint) * denomination; + } /// /// Round decimal up or down to the nearest multiple of denomination @@ -78,6 +83,7 @@ public static decimal RoundToNearest(this decimal value, decimal denomination, M /// Denomination /// true round to, false round down /// Rounded value + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static decimal RoundToNearest(this decimal value, decimal denomination, bool roundUp) { if (denomination == decimal.Zero) @@ -133,10 +139,10 @@ public static decimal RoundToNearest(this decimal value, Currency currency, out /// Rounded value public static decimal RoundIfEnabledFor(this decimal value, Currency currency) { - if (currency == null) - throw new ArgumentNullException(nameof(currency)); + if (currency == null) + throw new ArgumentNullException(nameof(currency)); - if (currency.RoundOrderItemsEnabled) + if (currency.RoundOrderItemsEnabled) { return Math.Round(value, currency.RoundNumDecimals); } @@ -150,11 +156,12 @@ public static decimal RoundIfEnabledFor(this decimal value, Currency currency) /// Value to round /// Rounding decimal number /// Rounded and formated value + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string FormatInvariant(this decimal value, int decimals = 2) - { - return Math.Round(value, decimals).ToString("0.00", CultureInfo.InvariantCulture); - } + { + return Math.Round(value, decimals).ToString("0.00", CultureInfo.InvariantCulture); + } - #endregion - } + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Extensions/ProcessExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/ProcessExtensions.cs new file mode 100644 index 0000000000..a14e6e5213 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Extensions/ProcessExtensions.cs @@ -0,0 +1,41 @@ +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace SmartStore.Core +{ + public static class ProcessExtensions + { + public static async Task RunAsync(this Process process, CancellationToken cancellationToken = default(CancellationToken)) + { + Guard.NotNull(process, nameof(process)); + + process.Start(); + return await WaitForExitAsync(process, cancellationToken); + } + + public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default(CancellationToken)) + { + Guard.NotNull(process, nameof(process)); + + process.EnableRaisingEvents = true; + + var tcs = new TaskCompletionSource(); + + process.Exited += (sender, args) => + { + tcs.TrySetResult(process.ExitCode); + }; + + if (process.HasExited) + { + return process.ExitCode; + } + + using (var registration = cancellationToken.Register(() => tcs.TrySetCanceled(cancellationToken))) + { + return await tcs.Task.ConfigureAwait(false); + } + } + } +} diff --git a/src/Libraries/SmartStore.Core/Extensions/RegexExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/RegexExtensions.cs index 02bc3d5ab5..0e29ac7926 100644 --- a/src/Libraries/SmartStore.Core/Extensions/RegexExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/RegexExtensions.cs @@ -5,41 +5,41 @@ namespace SmartStore { - public static class RegexExtensions - { - public static string ReplaceGroup(this Regex regex, string input, string groupName, string replacement) - { - return ReplaceGroupInternal(regex, input, replacement, m => m.Groups[groupName]); - } - - public static string ReplaceGroup(this Regex regex, string input, int groupNum, string replacement) - { - return ReplaceGroupInternal(regex, input, replacement, m => m.Groups[groupNum]); - } - - private static string ReplaceGroupInternal(this Regex regex, string input, string replacement, Func groupGetter) - { - return regex.Replace(input, match => - { - var group = groupGetter(match); - var sb = new StringBuilder(); - var previousCaptureEnd = 0; - - foreach (var capture in group.Captures.Cast()) - { - var currentCaptureEnd = capture.Index + capture.Length - match.Index; - var currentCaptureLength = capture.Index - match.Index - previousCaptureEnd; - - sb.Append(match.Value.Substring(previousCaptureEnd, currentCaptureLength)); - sb.Append(replacement); - - previousCaptureEnd = currentCaptureEnd; - } - - sb.Append(match.Value.Substring(previousCaptureEnd)); - - return sb.ToString(); - }); - } - } + public static class RegexExtensions + { + public static string ReplaceGroup(this Regex regex, string input, string groupName, string replacement) + { + return ReplaceGroupInternal(regex, input, replacement, m => m.Groups[groupName]); + } + + public static string ReplaceGroup(this Regex regex, string input, int groupNum, string replacement) + { + return ReplaceGroupInternal(regex, input, replacement, m => m.Groups[groupNum]); + } + + private static string ReplaceGroupInternal(this Regex regex, string input, string replacement, Func groupGetter) + { + return regex.Replace(input, match => + { + var group = groupGetter(match); + var sb = new StringBuilder(); + var previousCaptureEnd = 0; + + foreach (var capture in group.Captures.Cast()) + { + var currentCaptureEnd = capture.Index + capture.Length - match.Index; + var currentCaptureLength = capture.Index - match.Index - previousCaptureEnd; + + sb.Append(match.Value.Substring(previousCaptureEnd, currentCaptureLength)); + sb.Append(replacement); + + previousCaptureEnd = currentCaptureEnd; + } + + sb.Append(match.Value.Substring(previousCaptureEnd)); + + return sb.ToString(); + }); + } + } } diff --git a/src/Libraries/SmartStore.Core/Extensions/RouteExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/RouteExtensions.cs index 845d7a9a79..94ad48d31e 100644 --- a/src/Libraries/SmartStore.Core/Extensions/RouteExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/RouteExtensions.cs @@ -1,58 +1,57 @@ -using System; -using System.Web.Mvc; +using System.Web.Mvc; using System.Web.Routing; // use base SmartStore Namespace to ensure the extension methods are always available namespace SmartStore { - public static class RouteExtensions - { - public static string GetAreaName(this RouteData routeData) - { - if (routeData.DataTokens.TryGetValue("area", out object area)) - { - return (area as string); - } - - return routeData.Route.GetAreaName(); - } - - public static string GetAreaName(this RouteBase route) - { - var area = route as IRouteWithArea; - if (area != null) - { - return area.Area; - } - - var route2 = route as Route; - if ((route2 != null) && (route2.DataTokens != null)) - { - return (route2.DataTokens["area"] as string); - } - - return null; - } + public static class RouteExtensions + { + public static string GetAreaName(this RouteData routeData) + { + if (routeData.DataTokens.TryGetValue("area", out object area)) + { + return (area as string); + } + + return routeData.Route.GetAreaName(); + } + + public static string GetAreaName(this RouteBase route) + { + var area = route as IRouteWithArea; + if (area != null) + { + return area.Area; + } + + var route2 = route as Route; + if ((route2 != null) && (route2.DataTokens != null)) + { + return (route2.DataTokens["area"] as string); + } + + return null; + } /// /// Generates an identifier for the given route in the form "[{area}.]{controller}.{action}" /// public static string GenerateRouteIdentifier(this RouteData routeData) - { - string area = routeData.GetAreaName(); - string controller = routeData.GetRequiredString("controller"); - string action = routeData.GetRequiredString("action"); + { + string area = routeData.GetAreaName(); + string controller = routeData.GetRequiredString("controller"); + string action = routeData.GetRequiredString("action"); - return "{0}{1}.{2}".FormatInvariant(area.HasValue() ? area + "." : "", controller, action); - } + return "{0}{1}.{2}".FormatInvariant(area.HasValue() ? area + "." : "", controller, action); + } - public static bool IsRouteEqual(this RouteData routeData, string controller, string action) - { - if (routeData == null) - return false; + public static bool IsRouteEqual(this RouteData routeData, string controller, string action) + { + if (routeData == null) + return false; - return routeData.GetRequiredString("controller").IsCaseInsensitiveEqual(controller) && routeData.GetRequiredString("action").IsCaseInsensitiveEqual(action); - } + return routeData.GetRequiredString("controller").IsCaseInsensitiveEqual(controller) && routeData.GetRequiredString("action").IsCaseInsensitiveEqual(action); + } - } + } } diff --git a/src/Libraries/SmartStore.Core/Extensions/StreamExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/StreamExtensions.cs index 6b286561a0..2e3a6e94ba 100644 --- a/src/Libraries/SmartStore.Core/Extensions/StreamExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/StreamExtensions.cs @@ -1,90 +1,117 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web; -using System.IO; using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using SmartStore.Core.IO; namespace SmartStore { - public static class StreamExtensions - { - public static StreamReader ToStreamReader(this Stream stream, bool leaveOpen) - { - return new StreamReader(stream, Encoding.UTF8, true, 0x400, leaveOpen); - } - - public static StreamReader ToStreamReader(this Stream stream, Encoding encoding, bool detectEncoding, int bufferSize, bool leaveOpen) - { - return new StreamReader(stream, encoding, detectEncoding, bufferSize, leaveOpen); - } - - public static bool ToFile(this Stream srcStream, string path) + public static class StreamExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static StreamReader ToStreamReader(this Stream stream, bool leaveOpen) { - if (srcStream == null) - return false; + return new StreamReader(stream, Encoding.UTF8, true, 0x400, leaveOpen); + } - const int BuffSize = 32768; - var result = true; - Stream dstStream = null; - var buffer = new byte[BuffSize]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static StreamReader ToStreamReader(this Stream stream, Encoding encoding, bool detectEncoding, int bufferSize, bool leaveOpen) + { + return new StreamReader(stream, encoding, detectEncoding, bufferSize, leaveOpen); + } - try + public static Stream MakeSeekable(this Stream stream) + { + if (stream.CanSeek) + { + Debug.WriteLine("The stream is seekable already."); + } + + return new SeekableReadOnlyStream(stream, (int)stream.Length); + } + + public static bool ToFile(this Stream srcStream, string path) + { + if (srcStream == null) + return false; + + const int BuffSize = 32768; + var result = true; + Stream dstStream = null; + var buffer = new byte[BuffSize]; + + try { using (dstStream = File.Open(path, FileMode.Create)) { - int len; + int len; while ((len = srcStream.Read(buffer, 0, BuffSize)) > 0) { dstStream.Write(buffer, 0, len); } - } + } + } + catch + { + result = false; } - catch + finally { - result = false; - } - finally - { - if (dstStream != null) - { - dstStream.Close(); - dstStream.Dispose(); - } - } - - return (result && File.Exists(path)); - } - - public static bool ContentsEqual(this Stream src, Stream other) + if (dstStream != null) + { + dstStream.Close(); + dstStream.Dispose(); + } + } + + return (result && File.Exists(path)); + } + + public static bool ContentsEqual(this Stream src, Stream other, bool? forceLengthCompare = null) { - if (src == null) - throw new ArgumentNullException(nameof(src)); + if (src == null) + throw new ArgumentNullException(nameof(src)); - if (other == null) - throw new ArgumentNullException(nameof(other)); + if (other == null) + throw new ArgumentNullException(nameof(other)); - if (src.Length != other.Length) - return false; + if (src == other) + { + // This is not merely an optimization, as incrementing one stream's position + // should not affect the position of the other. + return true; + } - const int intSize = sizeof(Int64); - const int bufferSize = 2048; + // This is not 100% correct, as a stream can be non-seekable but still have a known + // length (but hopefully the opposite can never happen). I don't know how to check + // if the length is available without throwing an exception if it's not. + if ((!forceLengthCompare.HasValue && src.CanSeek && other.CanSeek) || (forceLengthCompare == true)) + { + if (src.Length != other.Length) + { + return false; + } + } + + const int intSize = sizeof(Int64); + const int bufferSize = 1024 * intSize; // 2048; var buffer1 = new byte[bufferSize]; var buffer2 = new byte[bufferSize]; - + while (true) { 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) return true; 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)) @@ -92,9 +119,10 @@ public static bool ContentsEqual(this Stream src, Stream other) return false; } } + + return true; } } + } - } - } diff --git a/src/Libraries/SmartStore.Core/Extensions/StringExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/StringExtensions.cs index 211fb926af..efea7d0778 100644 --- a/src/Libraries/SmartStore.Core/Extensions/StringExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/StringExtensions.cs @@ -1,29 +1,29 @@ using System; using System.Collections.Generic; -using System.Text; -using System.Globalization; -using System.Security.Cryptography; -using System.Web; -using System.Text.RegularExpressions; -using System.IO; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; using System.Linq; -using AngleSharp.Dom; -using AngleSharp.Parser.Html; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Web; using SmartStore.Core.Html; +using SmartStore.Utilities.ObjectPools; namespace SmartStore { public static class StringExtensions { - public const string CarriageReturnLineFeed = "\r\n"; + public const string CarriageReturnLineFeed = "\r\n"; public const string Empty = ""; public const char CarriageReturn = '\r'; public const char LineFeed = '\n'; public const char Tab = '\t'; - private delegate void ActionLine(TextWriter textWriter, string line); + private delegate void ActionLine(TextWriter textWriter, string line); #region Char extensions @@ -73,22 +73,22 @@ internal static void WriteCharAsUnicode(char c, TextWriter writer) writer.Write(h4); } - public static char TryRemoveDiacritic(this char c) - { - var normalized = c.ToString().Normalize(NormalizationForm.FormD); - if (normalized.Length > 1) - { - return normalized[0]; - } + public static char TryRemoveDiacritic(this char c) + { + var normalized = c.ToString().Normalize(NormalizationForm.FormD); + if (normalized.Length > 1) + { + return normalized[0]; + } - return c; - } + return c; + } - #endregion + #endregion - #region String extensions + #region String extensions - [DebuggerStepThrough] + [DebuggerStepThrough] public static T ToEnum(this string value, T defaultValue) { if (!value.HasValue()) @@ -117,54 +117,60 @@ public static string ToSafe(this string value, string defaultValue = null) } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string EmptyNull(this string value) { return (value ?? string.Empty).Trim(); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string NullEmpty(this string value) { return (string.IsNullOrEmpty(value)) ? null : value; } - /// - /// Formats a string to an invariant culture - /// - /// The format string. - /// The objects. - /// - [DebuggerStepThrough] + /// + /// Formats a string to an invariant culture + /// + /// The format string. + /// The objects. + /// + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string FormatInvariant(this string format, params object[] objects) { return string.Format(CultureInfo.InvariantCulture, format, objects); } - /// - /// Formats a string to the current culture. - /// - /// The format string. - /// The objects. - /// - [DebuggerStepThrough] + /// + /// Formats a string to the current culture. + /// + /// The format string. + /// The objects. + /// + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string FormatCurrent(this string format, params object[] objects) { return string.Format(CultureInfo.CurrentCulture, format, objects); } - /// - /// Formats a string to the current UI culture. - /// - /// The format string. - /// The objects. - /// - [DebuggerStepThrough] + /// + /// Formats a string to the current UI culture. + /// + /// The format string. + /// The objects. + /// + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string FormatCurrentUI(this string format, params object[] objects) { return string.Format(CultureInfo.CurrentUICulture, format, objects); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string FormatWith(this string format, params object[] args) { return FormatWith(format, CultureInfo.CurrentCulture, args); @@ -176,29 +182,31 @@ public static string FormatWith(this string format, IFormatProvider provider, pa return string.Format(provider, format, args); } - /// - /// Determines whether this instance and another specified System.String object have the same value. - /// - /// The string to check equality. - /// The comparing with string. - /// - /// true if the value of the comparing parameter is the same as this string; otherwise, false. - /// - [DebuggerStepThrough] + /// + /// Determines whether this instance and another specified System.String object have the same value. + /// + /// The string to check equality. + /// The comparing with string. + /// + /// true if the value of the comparing parameter is the same as this string; otherwise, false. + /// + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsCaseSensitiveEqual(this string value, string comparing) { return string.CompareOrdinal(value, comparing) == 0; } - /// - /// Determines whether this instance and another specified System.String object have the same value. - /// - /// The string to check equality. - /// The comparing with string. - /// - /// true if the value of the comparing parameter is the same as this string; otherwise, false. - /// - [DebuggerStepThrough] + /// + /// Determines whether this instance and another specified System.String object have the same value. + /// + /// The string to check equality. + /// The comparing with string. + /// + /// true if the value of the comparing parameter is the same as this string; otherwise, false. + /// + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsCaseInsensitiveEqual(this string value, string comparing) { return string.Compare(value, comparing, StringComparison.OrdinalIgnoreCase) == 0; @@ -208,25 +216,26 @@ public static bool IsCaseInsensitiveEqual(this string value, string comparing) /// Determines whether the string is null, empty or all whitespace. /// [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsEmpty(this string value) { - return string.IsNullOrWhiteSpace(value); + return string.IsNullOrWhiteSpace(value); } - /// - /// Determines whether the string is all white space. Empty string will return false. - /// - /// The string to test whether it is all white space. - /// - /// true if the string is all white space; otherwise, false. - /// - [DebuggerStepThrough] + /// + /// Determines whether the string is all white space. Empty string will return false. + /// + /// The string to test whether it is all white space. + /// + /// true if the string is all white space; otherwise, false. + /// + [DebuggerStepThrough] public static bool IsWhiteSpace(this string value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); + if (value == null) + throw new ArgumentNullException(nameof(value)); - if (value.Length == 0) + if (value.Length == 0) return false; for (int i = 0; i < value.Length; i++) @@ -239,78 +248,79 @@ public static bool IsWhiteSpace(this string value) } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool HasValue(this string value) { return !string.IsNullOrWhiteSpace(value); } - /// to get equivalent result to PHPs md5 function call Hash("my value", Encoding.ASCII, false). + /// to get equivalent result to PHPs md5 function call Hash("my value", Encoding.ASCII, false). [DebuggerStepThrough] - public static string Hash(this string value, Encoding encoding, bool toBase64 = false) + public static string Hash(this string value, Encoding encoding, bool toBase64 = false) { - if (value.IsEmpty()) - return value; + if (value.IsEmpty()) + return value; using (var md5 = MD5.Create()) { - byte[] data = encoding.GetBytes(value); + byte[] data = encoding.GetBytes(value); - if (toBase64) + if (toBase64) { - byte[] hash = md5.ComputeHash(data); - return Convert.ToBase64String(hash); - } - else + byte[] hash = md5.ComputeHash(data); + return Convert.ToBase64String(hash); + } + else { - return md5.ComputeHash(data).ToHexString().ToLower(); - } + return md5.ComputeHash(data).ToHexString().ToLower(); + } } } - /// - /// Mask by replacing characters with asterisks. - /// - /// The string - /// Number of characters to leave untouched. - /// The mask string - [DebuggerStepThrough] - public static string Mask(this string value, int length) - { - if (value.HasValue()) - return value.Substring(0, length) + new String('*', value.Length - length); + /// + /// Mask by replacing characters with asterisks. + /// + /// The string + /// Number of characters to leave untouched. + /// The mask string + [DebuggerStepThrough] + public static string Mask(this string value, int length) + { + if (value.HasValue()) + return value.Substring(0, length) + new String('*', value.Length - length); - return value; - } + return value; + } private static bool IsWebUrlInternal(string value, bool schemeIsOptional) { - if (String.IsNullOrEmpty(value)) - return false; + if (String.IsNullOrEmpty(value)) + return false; - value = value.Trim().ToLowerInvariant(); + value = value.Trim().ToLowerInvariant(); - if (schemeIsOptional && value.StartsWith("//")) - { - value = "http:" + value; - } + if (schemeIsOptional && value.StartsWith("//")) + { + value = "http:" + value; + } - return Uri.IsWellFormedUriString(value, UriKind.Absolute) && - (value.StartsWith("http://") || value.StartsWith("https://") || value.StartsWith("ftp://")); - } + return Uri.IsWellFormedUriString(value, UriKind.Absolute) && + (value.StartsWith("http://") || value.StartsWith("https://") || value.StartsWith("ftp://")); + } - [DebuggerStepThrough] - public static bool IsWebUrl(this string value) - { - return IsWebUrlInternal(value, false); - } + [DebuggerStepThrough] + public static bool IsWebUrl(this string value) + { + return IsWebUrlInternal(value, false); + } - [DebuggerStepThrough] - public static bool IsWebUrl(this string value, bool schemeIsOptional) - { - return IsWebUrlInternal(value, schemeIsOptional); - } + [DebuggerStepThrough] + public static bool IsWebUrl(this string value, bool schemeIsOptional) + { + return IsWebUrlInternal(value, schemeIsOptional); + } - [DebuggerStepThrough] + [DebuggerStepThrough] public static bool IsEmail(this string value) { return !String.IsNullOrEmpty(value) && RegularExpressions.IsEmail.IsMatch(value.Trim()); @@ -328,19 +338,19 @@ public static bool IsNumeric(this string value) RegularExpressions.IsNumeric.IsMatch(value); } - /// - /// Ensures that a string only contains numeric values - /// - /// Input string - /// Input string with only numeric values, empty string if input is null or empty - [DebuggerStepThrough] - public static string EnsureNumericOnly(this string str) - { - if (String.IsNullOrEmpty(str)) - return string.Empty; + /// + /// Ensures that a string only contains numeric values + /// + /// Input string + /// Input string with only numeric values, empty string if input is null or empty + [DebuggerStepThrough] + public static string EnsureNumericOnly(this string str) + { + if (String.IsNullOrEmpty(str)) + return string.Empty; - return new String(str.Where(c => Char.IsDigit(c)).ToArray()); - } + return new String(str.Where(c => Char.IsDigit(c)).ToArray()); + } [DebuggerStepThrough] public static bool IsAlpha(this string value) @@ -357,12 +367,12 @@ public static bool IsAlphaNumeric(this string value) [DebuggerStepThrough] public static string Truncate(this string value, int maxLength, string suffix = "") { - if (suffix == null) - throw new ArgumentNullException(nameof(suffix)); + if (suffix == null) + throw new ArgumentNullException(nameof(suffix)); Guard.IsPositive(maxLength, nameof(maxLength)); - int subStringLength = maxLength - suffix.Length; + int subStringLength = maxLength - suffix.Length; if (subStringLength <= 0) throw Error.Argument(nameof(maxLength), "Length of suffix string is greater or equal to maximumLength"); @@ -382,138 +392,142 @@ public static string Truncate(this string value, int maxLength, string suffix = } } - /// - /// Removes all redundant whitespace (empty lines, double space etc.). - /// Use ~! literal to keep whitespace wherever necessary. - /// - /// Input - /// The compacted string - public static string Compact(this string input, bool removeEmptyLines = false) - { - Guard.NotNull(input, nameof(input)); - - var sb = new StringBuilder(); - var lines = GetLines(input.Trim(), true, removeEmptyLines).ToArray(); - - foreach (var line in lines) - { - var len = line.Length; - var sbLine = new StringBuilder(len); - var isChar = false; - var isLiteral = false; // When we detect the ~! literal - int i = 0; - var eof = false; - - for (i = 0; i < len; i++) - { - var c = line[i]; - - eof = i == len - 1; - - if (Char.IsWhiteSpace(c)) - { - // Space, Tab etc. - if (isChar) - { - // If last char not empty, append the space. - sbLine.Append(' '); - } - - isLiteral = false; - isChar = false; - } - else - { - // Char or Literal (~!) - - isLiteral = c == '~' && !eof && line[i + 1] == '!'; - isChar = true; - - if (isLiteral) - { - sbLine.Append(' '); - i++; // skip next "!" char - } - else - { - sbLine.Append(c); - } - } - } - - // Append the compacted and trimmed line - sb.AppendLine(sbLine.ToString().Trim().Trim(',')); - } - - return sb.ToString().Trim(); - } - - /// - /// Splits the input string by carriage return. - /// - /// The string to split - /// A sequence with string items per line - public static IEnumerable GetLines(this string input, bool trimLines = false, bool removeEmptyLines = false) - { - if (input.IsEmpty()) - { - yield break; - } - - using (var sr = new StringReader(input)) - { - string line; - while ((line = sr.ReadLine()) != null) - { - if (trimLines) - { - line = line.Trim(); - } - - if (removeEmptyLines && IsEmpty(line)) - { - continue; - } - - yield return line; - } - } - } - - /// - /// Ensure that a string starts with a string. - /// - /// The target string - /// The string the target string should start with - /// The resulting string - [DebuggerStepThrough] - public static string EnsureStartsWith(this string value, string startsWith) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - if (startsWith == null) - throw new ArgumentNullException(nameof(startsWith)); - - return value.StartsWith(startsWith) ? value : (startsWith + value); - } - - /// - /// Ensures the target string ends with the specified string. - /// - /// The target. - /// The value. - /// The target string with the value string at the end. - [DebuggerStepThrough] + /// + /// Removes all redundant whitespace (empty lines, double space etc.). + /// Use ~! literal to keep whitespace wherever necessary. + /// + /// Input + /// The compacted string + public static string Compact(this string input, bool removeEmptyLines = false) + { + Guard.NotNull(input, nameof(input)); + + var psb = PooledStringBuilder.Rent(); + var sb = (StringBuilder)psb; + var lines = GetLines(input.Trim(), true, removeEmptyLines).ToArray(); + + foreach (var line in lines) + { + var len = line.Length; + var psbLine = PooledStringBuilder.Rent(); + var sbLine = (StringBuilder)psbLine; + var isChar = false; + var isLiteral = false; // When we detect the ~! literal + int i = 0; + var eof = false; + + for (i = 0; i < len; i++) + { + var c = line[i]; + + eof = i == len - 1; + + if (Char.IsWhiteSpace(c)) + { + // Space, Tab etc. + if (isChar) + { + // If last char not empty, append the space. + sbLine.Append(' '); + } + + isLiteral = false; + isChar = false; + } + else + { + // Char or Literal (~!) + + isLiteral = c == '~' && !eof && line[i + 1] == '!'; + isChar = true; + + if (isLiteral) + { + sbLine.Append(' '); + i++; // skip next "!" char + } + else + { + sbLine.Append(c); + } + } + } + + // Append the compacted and trimmed line + sb.AppendLine(sbLine.ToString().Trim().Trim(',')); + psbLine.Return(); + } + + return psb.ToStringAndReturn().Trim(); + } + + /// + /// Splits the input string by carriage return. + /// + /// The string to split + /// A sequence with string items per line + public static IEnumerable GetLines(this string input, bool trimLines = false, bool removeEmptyLines = false) + { + if (input.IsEmpty()) + { + yield break; + } + + using (var sr = new StringReader(input)) + { + string line; + while ((line = sr.ReadLine()) != null) + { + if (trimLines) + { + line = line.Trim(); + } + + if (removeEmptyLines && IsEmpty(line)) + { + continue; + } + + yield return line; + } + } + } + + /// + /// Ensure that a string starts with a string. + /// + /// The target string + /// The string the target string should start with + /// The resulting string + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string EnsureStartsWith(this string value, string startsWith) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + if (startsWith == null) + throw new ArgumentNullException(nameof(startsWith)); + + return value.StartsWith(startsWith) ? value : (startsWith + value); + } + + /// + /// Ensures the target string ends with the specified string. + /// + /// The target. + /// The value. + /// The target string with the value string at the end. + [DebuggerStepThrough] public static string EnsureEndsWith(this string value, string endWith) { - if (value == null) - throw new ArgumentNullException(nameof(value)); + if (value == null) + throw new ArgumentNullException(nameof(value)); - if (endWith == null) - throw new ArgumentNullException(nameof(endWith)); + if (endWith == null) + throw new ArgumentNullException(nameof(endWith)); - if (value.Length >= endWith.Length) + if (value.Length >= endWith.Length) { if (string.Compare(value, value.Length - endWith.Length, endWith, 0, endWith.Length, StringComparison.OrdinalIgnoreCase) == 0) return value; @@ -528,57 +542,65 @@ public static string EnsureEndsWith(this string value, string endWith) } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string UrlEncode(this string value) { return HttpUtility.UrlEncode(value); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string UrlDecode(this string value) { return HttpUtility.UrlDecode(value); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string AttributeEncode(this string value) { return HttpUtility.HtmlAttributeEncode(value); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string HtmlEncode(this string value) { return HttpUtility.HtmlEncode(value); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string HtmlDecode(this string value) { return HttpUtility.HtmlDecode(value); } - [Obsolete("The 'removeTags' parameter is not supported anymore. Use the parameterless method instead.")] - public static string RemoveHtml(this string source, ICollection removeTags) - { - return RemoveHtml(source); - } - - public static string RemoveHtml(this string source) - { - return HtmlUtils.StripTags(source).Trim().HtmlDecode(); - } - - /// - /// Replaces pascal casing with spaces. For example "CustomerId" would become "Customer Id". - /// Strings that already contain spaces are ignored. - /// - /// String to split - /// The string after being split - [DebuggerStepThrough] + [Obsolete("The 'removeTags' parameter is not supported anymore. Use the parameterless method instead.")] + public static string RemoveHtml(this string source, ICollection removeTags) + { + return RemoveHtml(source); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string RemoveHtml(this string source) + { + return HtmlUtils.StripTags(source).Trim().HtmlDecode(); + } + + /// + /// Replaces pascal casing with spaces. For example "CustomerId" would become "Customer Id". + /// Strings that already contain spaces are ignored. + /// + /// String to split + /// The string after being split + [DebuggerStepThrough] public static string SplitPascalCase(this string value) { - var sb = new StringBuilder(); + var psb = PooledStringBuilder.Rent(); + var sb = (StringBuilder)psb; char[] ca = value.ToCharArray(); + sb.Append(ca[0]); for (int i = 1; i < ca.Length - 1; i++) @@ -596,74 +618,74 @@ public static string SplitPascalCase(this string value) sb.Append(ca[ca.Length - 1]); } - return sb.ToString(); - } - - /// - /// Splits a string into a string array - /// - /// String value to split - /// If null then value is searched for a common delimiter like pipe, semicolon or comma - /// String array - [DebuggerStepThrough] - public static string[] SplitSafe(this string value, string separator) - { - if (string.IsNullOrEmpty(value)) - return new string[0]; - - // Do not use separator.IsEmpty() here because whitespace like " " is a valid separator. - // an empty separator "" returns array with value. - if (separator == null) - { - for (var i = 0; i < value.Length; i++) - { - var c = value[i]; - if (c == ';' || c == ',' || c == '|') - { - return value.Split(new char[] { c }, StringSplitOptions.RemoveEmptyEntries); - } - if (c == '\r' && (i + 1) < value.Length & value[i + 1] == '\n') - { - return value.GetLines(false, true).ToArray(); - } - } - - return new string[] { value }; - } - else - { - return value.Split(new string[] { separator }, StringSplitOptions.RemoveEmptyEntries); - } - } - - /// Splits a string into two strings - /// true: success, false: failure - [DebuggerStepThrough] + return psb.ToStringAndReturn(); + } + + /// + /// Splits a string into a string array + /// + /// String value to split + /// If null then value is searched for a common delimiter like pipe, semicolon or comma + /// String array + [DebuggerStepThrough] + public static string[] SplitSafe(this string value, string separator) + { + if (string.IsNullOrEmpty(value)) + return new string[0]; + + // Do not use separator.IsEmpty() here because whitespace like " " is a valid separator. + // an empty separator "" returns array with value. + if (separator == null) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + if (c == ';' || c == ',' || c == '|') + { + return value.Split(new char[] { c }, StringSplitOptions.RemoveEmptyEntries); + } + if (c == '\r' && (i + 1) < value.Length & value[i + 1] == '\n') + { + return value.GetLines(false, true).ToArray(); + } + } + + return new string[] { value }; + } + else + { + return value.Split(new string[] { separator }, StringSplitOptions.RemoveEmptyEntries); + } + } + + /// Splits a string into two strings + /// true: success, false: failure + [DebuggerStepThrough] [SuppressMessage("ReSharper", "StringIndexOfIsCultureSpecific.1")] - public static bool SplitToPair(this string value, out string leftPart, out string rightPart, string delimiter, bool splitAfterLast = false) - { - leftPart = value; - rightPart = ""; + public static bool SplitToPair(this string value, out string leftPart, out string rightPart, string delimiter, bool splitAfterLast = false) + { + leftPart = value; + rightPart = ""; - if (string.IsNullOrEmpty(value) || string.IsNullOrEmpty(delimiter)) - { - return false; - } + if (string.IsNullOrEmpty(value) || string.IsNullOrEmpty(delimiter)) + { + return false; + } - var idx = splitAfterLast - ? value.LastIndexOf(delimiter) - : value.IndexOf(delimiter); + var idx = splitAfterLast + ? value.LastIndexOf(delimiter) + : value.IndexOf(delimiter); - if (idx == -1) - { - return false; - } + if (idx == -1) + { + return false; + } - leftPart = value.Substring(0, idx); - rightPart = value.Substring(idx + delimiter.Length); + leftPart = value.Substring(0, idx); + rightPart = value.Substring(idx + delimiter.Length); - return true; - } + return true; + } [DebuggerStepThrough] public static string EncodeJsString(this string value) @@ -674,15 +696,19 @@ public static string EncodeJsString(this string value) [DebuggerStepThrough] public static string EncodeJsString(this string value, char delimiter, bool appendDelimiters) { - var sb = new StringBuilder(value != null ? value.Length : 16); + var psb = PooledStringBuilder.Rent(); + var sb = (StringBuilder)psb; using (var w = new StringWriter(sb, CultureInfo.InvariantCulture)) { EncodeJsString(w, value, delimiter, appendDelimiters); - return w.ToString(); + var result = w.ToString(); + psb.Return(); + return result; } } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsEnclosedIn(this string value, string enclosedIn) { return value.IsEnclosedIn(enclosedIn, StringComparison.CurrentCulture); @@ -711,17 +737,20 @@ public static bool IsEnclosedIn(this string value, string enclosedIn, StringComp } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsEnclosedIn(this string value, string start, string end) { return value.IsEnclosedIn(start, end, StringComparison.CurrentCulture); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsEnclosedIn(this string value, string start, string end, StringComparison comparisonType) { return value.StartsWith(start, comparisonType) && value.EndsWith(end, comparisonType); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string RemoveEncloser(this string value, string encloser) { return value.RemoveEncloser(encloser, StringComparison.CurrentCulture); @@ -740,6 +769,7 @@ public static string RemoveEncloser(this string value, string encloser, StringCo return value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string RemoveEncloser(this string value, string start, string end) { return value.RemoveEncloser(start, end, StringComparison.CurrentCulture); @@ -755,237 +785,258 @@ public static string RemoveEncloser(this string value, string start, string end, return value; } - /// Debug.WriteLine + /// Debug.WriteLine [DebuggerStepThrough] - public static void Dump(this string value, bool appendMarks = false) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Dump(this string value, bool appendMarks = false) { - Debug.WriteLine(value); - Debug.WriteLineIf(appendMarks, "------------------------------------------------"); - } + Debug.WriteLine(value); + Debug.WriteLineIf(appendMarks, "------------------------------------------------"); + } - /// Smart way to create a HTML attribute with a leading space. - /// Name of the attribute. - /// - /// - [SuppressMessage("ReSharper", "StringCompareIsCultureSpecific.3")] - public static string ToAttribute(this string value, string name, bool htmlEncode = true) + /// Smart way to create a HTML attribute with a leading space. + /// Name of the attribute. + /// + /// + [SuppressMessage("ReSharper", "StringCompareIsCultureSpecific.3")] + public static string ToAttribute(this string value, string name, bool htmlEncode = true) { - if (name.IsEmpty()) - return ""; + if (name.IsEmpty()) + return ""; - if (value == "" && name != "value" && !name.StartsWith("data")) - return ""; + if (value == "" && name != "value" && !name.StartsWith("data")) + return ""; - if (name == "maxlength" && (value == "" || value == "0")) - return ""; + if (name == "maxlength" && (value == "" || value == "0")) + return ""; - if (name == "checked" || name == "disabled" || name == "multiple") + if (name == "checked" || name == "disabled" || name == "multiple") { - if (value == "" || string.Compare(value, "false", true) == 0) - return ""; - value = (string.Compare(value, "true", true) == 0 ? name : value); - } - - if (name.StartsWith("data")) - name = name.Insert(4, "-"); - - return string.Format(" {0}=\"{1}\"", name, htmlEncode ? HttpUtility.HtmlEncode(value) : value); - } - - /// - /// Appends grow and uses delimiter if the string is not empty. - /// + if (value == "" || string.Compare(value, "false", true) == 0) + return ""; + value = (string.Compare(value, "true", true) == 0 ? name : value); + } + + if (name.StartsWith("data")) + name = name.Insert(4, "-"); + + return string.Format(" {0}=\"{1}\"", name, htmlEncode ? HttpUtility.HtmlEncode(value) : value); + } + + /// + /// Appends grow and uses delimiter if the string is not empty. + /// + [DebuggerStepThrough] + public static string Grow(this string value, string grow, string delimiter) + { + if (string.IsNullOrEmpty(value)) + return (string.IsNullOrEmpty(grow) ? "" : grow); + + if (string.IsNullOrEmpty(grow)) + return (string.IsNullOrEmpty(value) ? "" : value); + + return value + delimiter + grow; + } + + /// + /// Left-pads a string. Always returns empty string if source is null or empty. + /// [DebuggerStepThrough] - public static string Grow(this string value, string grow, string delimiter) + public static string LeftPad(this string value, string format = null, char pad = ' ', int count = 1) { - if (string.IsNullOrEmpty(value)) - return (string.IsNullOrEmpty(grow) ? "" : grow); + if (string.IsNullOrEmpty(value)) + return string.Empty; - if (string.IsNullOrEmpty(grow)) - return (string.IsNullOrEmpty(value) ? "" : value); + Guard.NotNull(pad, nameof(pad)); - return string.Concat(value, delimiter, grow); - } - - /// - /// Returns n/a if string is empty else self. - /// + if (count < 1) + return value; + + var left = new String(pad, count); + var right = value; + + if (!string.IsNullOrWhiteSpace(format)) + { + right = string.Format(CultureInfo.InvariantCulture, format, value); + } + + return left + right; + } + + /// + /// Returns n/a if string is empty else self. + /// [DebuggerStepThrough] - public static string NaIfEmpty(this string value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string NaIfEmpty(this string value) { - return (string.IsNullOrWhiteSpace(value) ? "n/a" : value); - } + return (string.IsNullOrWhiteSpace(value) ? "n/a" : value); + } - /// - /// Replaces substring with position x1 to x2 by replaceBy. - /// + /// + /// Replaces substring with position x1 to x2 by replaceBy. + /// [DebuggerStepThrough] - public static string Replace(this string value, int x1, int x2, string replaceBy = null) + public static string Replace(this string value, int x1, int x2, string replaceBy = null) { - if (!string.IsNullOrWhiteSpace(value) && x1 > 0 && x2 > x1 && x2 < value.Length) + if (!string.IsNullOrWhiteSpace(value) && x1 > 0 && x2 > x1 && x2 < value.Length) { - return value.Substring(0, x1) + (replaceBy.EmptyNull()) + value.Substring(x2 + 1); - } - - return value; - } - - [DebuggerStepThrough] - public static string Replace(this string value, string oldValue, string newValue, StringComparison comparisonType) - { - try - { - int startIndex = 0; - while (true) - { - startIndex = value.IndexOf(oldValue, startIndex, comparisonType); - if (startIndex == -1) - break; - - value = value.Substring(0, startIndex) + newValue + value.Substring(startIndex + oldValue.Length); - - startIndex += newValue.Length; - } - } - catch (Exception exc) - { - exc.Dump(); - } - - return value; - } - - /// - /// Replaces digits in a string with culture native digits (if digit substitution for culture is required) - /// - [DebuggerStepThrough] - public static string ReplaceNativeDigits(this string value, IFormatProvider provider = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - provider = provider ?? NumberFormatInfo.CurrentInfo; - var nfi = NumberFormatInfo.GetInstance(provider); - - if (nfi.DigitSubstitution == DigitShapes.None) - { - return value; - } - - var nativeDigits = nfi.NativeDigits; - var rg = new Regex(@"\d"); - - var result = rg.Replace(value, m => nativeDigits[m.Value.ToInt()]); - return result; - } - - [DebuggerStepThrough] - public static string TrimSafe(this string value) - { - return (value.HasValue() ? value.Trim() : value); - } + return value.Substring(0, x1) + (replaceBy.EmptyNull()) + value.Substring(x2 + 1); + } + + return value; + } [DebuggerStepThrough] - public static string Slugify(this string value, bool allowSpace = false, char[] allowChars = null) + public static string Replace(this string value, string oldValue, string newValue, StringComparison comparisonType) { - string res = string.Empty; + try + { + int startIndex = 0; + while (true) + { + startIndex = value.IndexOf(oldValue, startIndex, comparisonType); + if (startIndex == -1) + break; - try + value = value.Substring(0, startIndex) + newValue + value.Substring(startIndex + oldValue.Length); + + startIndex += newValue.Length; + } + } + catch (Exception exc) { - if (!string.IsNullOrWhiteSpace(value)) + exc.Dump(); + } + + return value; + } + + /// + /// Replaces digits in a string with culture native digits (if digit substitution for culture is required) + /// + [DebuggerStepThrough] + public static string ReplaceNativeDigits(this string value, IFormatProvider provider = null) + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + provider = provider ?? NumberFormatInfo.CurrentInfo; + var nfi = NumberFormatInfo.GetInstance(provider); + + if (nfi.DigitSubstitution == DigitShapes.None) + { + return value; + } + + var nativeDigits = nfi.NativeDigits; + var rg = new Regex(@"\d"); + + var result = rg.Replace(value, m => nativeDigits[m.Value.ToInt()]); + return result; + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string TrimSafe(this string value) + { + return (value.HasValue() ? value.Trim() : value); + } + + [DebuggerStepThrough] + public static string Slugify(this string value, bool allowSpace = false, char[] allowChars = null) + { + string res = string.Empty; + var psb = PooledStringBuilder.Rent(); + + try + { + if (!string.IsNullOrWhiteSpace(value)) { - var sb = new StringBuilder(); - bool space = false; - char ch; + var sb = (StringBuilder)psb; + bool space = false; + char ch; - for (int i = 0; i < value.Length; ++i) + for (int i = 0; i < value.Length; ++i) { - ch = value[i]; + ch = value[i]; + + if (ch == ' ' || ch == '-') + { + if (allowSpace && ch == ' ') + sb.Append(' '); + else if (!space) + sb.Append('-'); + space = true; + continue; + } + + space = false; - if (ch == ' ' || ch == '-') + if ((ch >= 48 && ch <= 57) || (ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122) || ch == '_') { - if (allowSpace && ch == ' ') - sb.Append(' '); - else if (!space) - sb.Append('-'); - space = true; - continue; - } - - space = false; - - if ((ch >= 48 && ch <= 57) || (ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122) || ch == '_') - { - sb.Append(ch); - continue; - } - - if (allowChars != null && allowChars.Contains(ch)) + sb.Append(ch); + continue; + } + + if (allowChars != null && allowChars.Contains(ch)) + { + sb.Append(ch); + continue; + } + + if ((int)ch >= 128) { - sb.Append(ch); - continue; - } - - if ((int)ch >= 128) - { - switch (ch) - { - case 'ä': sb.Append("ae"); break; - case 'ö': sb.Append("oe"); break; - case 'ü': sb.Append("ue"); break; - case 'ß': sb.Append("ss"); break; - case 'Ä': sb.Append("AE"); break; - case 'Ö': sb.Append("OE"); break; - case 'Ü': sb.Append("UE"); break; - default: - var c2 = ch.TryRemoveDiacritic(); - if ((c2 >= 'a' && c2 <= 'z') || (c2 >= 'A' && c2 <= 'Z')) - { - sb.Append(c2); - } - break; - } - } - } // for - - if (sb.Length > 0) + switch (ch) + { + case 'ä': sb.Append("ae"); break; + case 'ö': sb.Append("oe"); break; + case 'ü': sb.Append("ue"); break; + case 'ß': sb.Append("ss"); break; + case 'Ä': sb.Append("AE"); break; + case 'Ö': sb.Append("OE"); break; + case 'Ü': sb.Append("UE"); break; + default: + var c2 = ch.TryRemoveDiacritic(); + if ((c2 >= 'a' && c2 <= 'z') || (c2 >= 'A' && c2 <= 'Z')) + { + sb.Append(c2); + } + break; + } + } + } // for + + if (sb.Length > 0) { - res = sb.ToString().Trim(new char[] { ' ', '-' }); - - Regex pat = new Regex(@"(-{2,})"); // remove double SpaceChar - res = pat.Replace(res, "-"); - res = res.Replace("__", "_"); - } - } - } - catch (Exception ex) + res = sb.ToString().Trim(new char[] { ' ', '-' }); + + Regex pat = new Regex(@"(-{2,})"); // remove double SpaceChar + res = pat.Replace(res, "-"); + res = res.Replace("__", "_"); + } + } + } + catch (Exception ex) { - ex.Dump(); - } + ex.Dump(); + } + finally + { + psb.Return(); + } - return (res.Length > 0 ? res : "null"); - } + return (res.Length > 0 ? res : "null"); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string SanitizeHtmlId(this string value) { - return System.Web.Mvc.TagBuilder.CreateSanitizedId(value); + return System.Web.Mvc.TagBuilder.CreateSanitizedId(value); } - public static string Sha(this string value, Encoding encoding) - { - if (!string.IsNullOrWhiteSpace(value)) - { - using (var sha1 = new SHA1CryptoServiceProvider()) - { - byte[] data = encoding.GetBytes(value); - return sha1.ComputeHash(data).ToHexString(); - } - } - - return string.Empty; - } - [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsMatch(this string input, string pattern, RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Multiline) { return Regex.IsMatch(input, pattern, options); @@ -998,122 +1049,109 @@ public static bool IsMatch(this string input, string pattern, out Match match, R return match.Success; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string RegexRemove(this string input, string pattern, RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Multiline) { return Regex.Replace(input, pattern, string.Empty, options); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string RegexReplace(this string input, string pattern, string replacement, RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Multiline) { return Regex.Replace(input, pattern, replacement, options); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string ToValidFileName(this string input, string replacement = "-") { - return input.ToValidPathInternal(false, replacement); + return string.Join( + replacement ?? "-", + input.ToSafe().Split(Path.GetInvalidFileNameChars())); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string ToValidPath(this string input, string replacement = "-") { - return input.ToValidPathInternal(true, replacement); - } - - private static string ToValidPathInternal(this string input, bool isPath, string replacement) - { - var invalidChars = new HashSet(isPath ? Path.GetInvalidPathChars() : Path.GetInvalidFileNameChars()); - - replacement = replacement ?? "-"; - - var sb = new StringBuilder(); - foreach (var c in input.ToSafe()) - { - if (invalidChars.Contains(c)) - { - sb.Append(replacement); - } - else - { - sb.Append(c); - } - } - - return sb.ToString(); - } - - [DebuggerStepThrough] - public static int[] ToIntArray(this string s) - { - return Array.ConvertAll(s.SplitSafe(","), v => int.Parse(v.Trim())); - } - - [DebuggerStepThrough] - public static bool ToIntArrayContains(this string s, int value, bool defaultValue) - { - if (s == null) - return defaultValue; - var arr = s.ToIntArray(); - if (arr == null || arr.Count() <= 0) - return defaultValue; - - return arr.Contains(value); - } - - [DebuggerStepThrough] - public static string RemoveInvalidXmlChars(this string s) - { - if (s.IsEmpty()) - return s; - - return Regex.Replace(s, @"[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD]", "", RegexOptions.Compiled); - } - - [DebuggerStepThrough] - public static string ReplaceCsvChars(this string s) - { - if (s.IsEmpty()) - { - return ""; - } - - s = s.Replace(';', ','); - s = s.Replace('\r', ' '); - s = s.Replace('\n', ' '); - return s.Replace("'", ""); - } - - [DebuggerStepThrough] - public static string HighlightKeywords(this string input, string keywords, string preMatch = "", string postMatch = "") - { + return string.Join( + replacement ?? "-", + input.ToSafe().Split(Path.GetInvalidPathChars())); + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int[] ToIntArray(this string s) + { + return Array.ConvertAll(s.SplitSafe(","), v => int.Parse(v.Trim())); + } + + [DebuggerStepThrough] + public static bool ToIntArrayContains(this string s, int value, bool defaultValue) + { + if (s == null) + return defaultValue; + var arr = s.ToIntArray(); + if (arr == null || arr.Count() <= 0) + return defaultValue; + + return arr.Contains(value); + } + + [DebuggerStepThrough] + public static string RemoveInvalidXmlChars(this string s) + { + if (s.IsEmpty()) + return s; + + return Regex.Replace(s, @"[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD]", "", RegexOptions.Compiled); + } + + [DebuggerStepThrough] + public static string ReplaceCsvChars(this string s) + { + if (s.IsEmpty()) + { + return ""; + } + + s = s.Replace(';', ','); + s = s.Replace('\r', ' '); + s = s.Replace('\n', ' '); + return s.Replace("'", ""); + } + + [DebuggerStepThrough] + public static string HighlightKeywords(this string input, string keywords, string preMatch = "", string postMatch = "") + { Guard.NotNull(preMatch, nameof(preMatch)); Guard.NotNull(postMatch, nameof(postMatch)); - if (string.IsNullOrWhiteSpace(input) || string.IsNullOrWhiteSpace(keywords)) - { - return input; - } + if (string.IsNullOrWhiteSpace(input) || string.IsNullOrWhiteSpace(keywords)) + { + return input; + } - var pattern = String.Join("|", keywords.Trim().Split(' ', '-') - .Select(x => x.Trim()) - .Where(x => !string.IsNullOrWhiteSpace(x)) - .Select(x => Regex.Escape(x)) - .Distinct()); + var pattern = String.Join("|", keywords.Trim().Split(' ', '-') + .Select(x => x.Trim()) + .Where(x => !string.IsNullOrWhiteSpace(x)) + .Select(x => Regex.Escape(x)) + .Distinct()); - if (!string.IsNullOrWhiteSpace(pattern)) - { - var rg = new Regex(pattern, RegexOptions.IgnoreCase); - input = rg.Replace(input, m => preMatch + m.Value.EmptyNull().HtmlEncode() + postMatch); - } + if (!string.IsNullOrWhiteSpace(pattern)) + { + var rg = new Regex(pattern, RegexOptions.IgnoreCase); + input = rg.Replace(input, m => preMatch + m.Value.EmptyNull().HtmlEncode() + postMatch); + } - return input; - } + return input; + } - #endregion + #endregion - #region Helper + #region Helper - private static void EncodeJsChar(TextWriter writer, char c, char delimiter) + private static void EncodeJsChar(TextWriter writer, char c, char delimiter) { switch (c) { diff --git a/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs index 5e5af9464c..5308d447ff 100644 --- a/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/TypeExtensions.cs @@ -1,65 +1,88 @@ using System; +using System.Collections; using System.Collections.Generic; -using System.Linq; -using System.Reflection; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Collections; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; namespace SmartStore { public static class TypeExtensions { private static readonly Type[] s_predefinedTypes = new Type[] { typeof(string), typeof(decimal), typeof(DateTime), typeof(TimeSpan), typeof(Guid) }; - private static readonly Type[] s_predefinedGenericTypes = new Type[] { typeof(Nullable<>) }; public static string AssemblyQualifiedNameWithoutVersion(this Type type) { - if (type == null) - throw new ArgumentNullException(nameof(type)); + if (type == null) + throw new ArgumentNullException(nameof(type)); - if (type.AssemblyQualifiedName != null) - { - var strArray = type.AssemblyQualifiedName.Split(new char[] { ',' }); - return string.Format("{0}, {1}", strArray[0].Trim(), strArray[1].Trim()); - } + if (type.AssemblyQualifiedName != null) + { + var strArray = type.AssemblyQualifiedName.Split(new char[] { ',' }); + return string.Format("{0}, {1}", strArray[0].Trim(), strArray[1].Trim()); + } - return null; + return null; } - public static bool IsNumericType(this Type type) - { - switch (Type.GetTypeCode(type)) - { - case TypeCode.Byte: - case TypeCode.SByte: - case TypeCode.UInt16: - case TypeCode.UInt32: - case TypeCode.UInt64: - case TypeCode.Int16: - case TypeCode.Int32: - case TypeCode.Int64: - case TypeCode.Decimal: - case TypeCode.Double: - case TypeCode.Single: - return true; - case TypeCode.Object: - if (type.IsNullable(out var innerType)) - { - return innerType.IsNumericType(); - } - return false; - default: - return false; - } - } - - public static bool IsSequenceType(this Type type) + public static bool IsNumericType(this Type type) { - if (type == typeof(string)) - return false; + switch (Type.GetTypeCode(type)) + { + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + case TypeCode.Decimal: + case TypeCode.Double: + case TypeCode.Single: + return true; + case TypeCode.Object: + if (type.IsNullable(out var innerType)) + { + return innerType.IsNumericType(); + } + return false; + default: + return false; + } + } - return type.IsArray || typeof(IEnumerable).IsAssignableFrom(type); + public static bool IsSequenceType(this Type type) + { + if (type == typeof(string)) + return false; + + return type.IsArray || typeof(IEnumerable).IsAssignableFrom(type); + } + + public static bool IsSequenceType(this Type type, out Type elementType) + { + elementType = null; + + if (type == typeof(string)) + return false; + + if (type.IsArray) + { + elementType = type.GetElementType(); + } + else if (type.IsSubClass(typeof(IEnumerable<>), out var implType)) + { + var genericArgs = implType.GetGenericArguments(); + if (genericArgs.Length == 1) + { + elementType = genericArgs[0]; + } + } + + return elementType != null; } public static bool IsPredefinedSimpleType(this Type type) @@ -98,7 +121,7 @@ public static bool IsPredefinedGenericType(this Type type) return false; } - return s_predefinedGenericTypes.Any(t => t == type); + return type == typeof(Nullable<>); } public static bool IsPredefinedType(this Type type) @@ -107,16 +130,17 @@ public static bool IsPredefinedType(this Type type) { return (string.Compare(type.FullName, "System.Xml.Linq.XElement", StringComparison.Ordinal) == 0); } - + return true; } - public static bool IsPlainObjectType(this Type type) - { - return type.IsClass && !type.IsSequenceType() && !type.IsPredefinedType(); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPlainObjectType(this Type type) + { + return type.IsClass && !type.IsSequenceType() && !type.IsPredefinedType(); + } - public static bool IsInteger(this Type type) + public static bool IsInteger(this Type type) { switch (Type.GetTypeCode(type)) { @@ -147,18 +171,17 @@ public static Type GetNonNullableType(this Type type) return wrappedType; } - public static bool IsNullable(this Type type, out Type wrappedType) + public static bool IsNullable(this Type type, out Type elementType) { - wrappedType = null; - - if (type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) - wrappedType = type.GetGenericArguments()[0]; + if (type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) + elementType = type.GetGenericArguments()[0]; else - wrappedType = type; + elementType = type; - return wrappedType != type; + return elementType != type; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsEnumType(this Type type) { return type.GetNonNullableType().IsEnum; @@ -166,10 +189,10 @@ public static bool IsEnumType(this Type type) public static bool IsConstructable(this Type type) { - if (type == null) - throw new ArgumentNullException(nameof(type)); + if (type == null) + throw new ArgumentNullException(nameof(type)); - if (type.IsAbstract || type.IsInterface || type.IsArray || type.IsGenericTypeDefinition || type == typeof(void)) + if (type.IsAbstract || type.IsInterface || type.IsArray || type.IsGenericTypeDefinition || type == typeof(void)) return false; if (!HasDefaultConstructor(type)) @@ -201,30 +224,32 @@ public static bool IsAnonymous(this Type type) [DebuggerStepThrough] public static bool HasDefaultConstructor(this Type type) { - if (type == null) - throw new ArgumentNullException(nameof(type)); + if (type == null) + throw new ArgumentNullException(nameof(type)); - if (type.IsValueType) + if (type.IsValueType) return true; return type.GetConstructors(BindingFlags.Instance | BindingFlags.Public) .Any(ctor => ctor.GetParameters().Length == 0); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsSubClass(this Type type, Type check) { - return IsSubClass(type, check, out Type _); - } + return IsSubClass(type, check, out _); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsSubClass(this Type type, Type check, out Type implementingType) { - if (type == null) - throw new ArgumentNullException(nameof(type)); + if (type == null) + throw new ArgumentNullException(nameof(type)); - if (check == null) - throw new ArgumentNullException(nameof(check)); + if (check == null) + throw new ArgumentNullException(nameof(check)); - return IsSubClassInternal(type, type, check, out implementingType); + return IsSubClassInternal(type, type, check, out implementingType); } private static bool IsSubClassInternal(Type initialType, Type currentType, Type check, out Type implementingType) @@ -255,6 +280,7 @@ private static bool IsSubClassInternal(Type initialType, Type currentType, Type { if (IsSubClassInternal(initialType, currentType.GetGenericTypeDefinition(), check, out implementingType)) { + // INFO: this seems to be wrong! Don't we need the implementing base type (?!?) implementingType = currentType; return true; } @@ -438,20 +464,21 @@ public static TAttribute GetAttribute(this ICustomAttributeProvider return null; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool HasAttribute(this ICustomAttributeProvider target, bool inherits) where TAttribute : Attribute { return target.IsDefined(typeof(TAttribute), inherits); } - /// - /// Given a particular MemberInfo, return the custom attributes of the - /// given type on that member. - /// - /// Type of attribute to retrieve. - /// The member to look at. - /// True to include attributes inherited from base classes. - /// Array of found attributes. - public static TAttribute[] GetAttributes(this ICustomAttributeProvider target, bool inherits) where TAttribute : Attribute + /// + /// Given a particular MemberInfo, return the custom attributes of the + /// given type on that member. + /// + /// Type of attribute to retrieve. + /// The member to look at. + /// True to include attributes inherited from base classes. + /// Array of found attributes. + public static TAttribute[] GetAttributes(this ICustomAttributeProvider target, bool inherits) where TAttribute : Attribute { if (target.IsDefined(typeof(TAttribute), inherits)) { @@ -498,8 +525,8 @@ public static TAttribute[] GetAllAttributes(this MemberInfo member, return attributes.ToArray(); } - [SuppressMessage("ReSharper", "SuspiciousTypeConversion.Global")] - internal static IEnumerable SortAttributesIfPossible(IEnumerable attributes) + [SuppressMessage("ReSharper", "SuspiciousTypeConversion.Global")] + internal static IEnumerable SortAttributesIfPossible(IEnumerable attributes) where TAttribute : Attribute { if (typeof(IOrdered).IsAssignableFrom(typeof(TAttribute))) @@ -547,7 +574,7 @@ internal static Type FindIEnumerable(this Type seqType) if (seqType.IsGenericType) { - var args = seqType.GetGenericArguments(); + var args = seqType.GetGenericArguments(); foreach (var arg in args) { var ienum = typeof(IEnumerable<>).MakeGenericType(arg); diff --git a/src/Libraries/SmartStore.Core/Extensions/XPathExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/XPathExtensions.cs index d44109c7f2..d22ed21181 100644 --- a/src/Libraries/SmartStore.Core/Extensions/XPathExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/XPathExtensions.cs @@ -1,98 +1,97 @@ using System; -using System.ComponentModel; using System.Globalization; using System.Xml.XPath; using SmartStore.Core.Domain.Localization; namespace SmartStore { - public static class XPathExtensions - { - /// - /// Safe way to get the value of a xpath navigator item - /// - /// Type - /// The node - /// Optional xpath - /// Optional default value - /// For localized values. Xpath scheme: Localized/xpath[@culture = 'language.LanguageCulture'] - /// If null is passed, CultureInfo.InvariantCulture is used. - /// The value - public static T GetValue(this XPathNavigator node, string xpath = null, T defaultValue = default(T), Language language = null, CultureInfo culture = null) - { - try - { - if (node != null) - { - if (xpath.IsEmpty()) - { - if (node.Value.HasValue()) - return node.Value.Convert(culture); + public static class XPathExtensions + { + /// + /// Safe way to get the value of a xpath navigator item + /// + /// Type + /// The node + /// Optional xpath + /// Optional default value + /// For localized values. Xpath scheme: Localized/xpath[@culture = 'language.LanguageCulture'] + /// If null is passed, CultureInfo.InvariantCulture is used. + /// The value + public static T GetValue(this XPathNavigator node, string xpath = null, T defaultValue = default(T), Language language = null, CultureInfo culture = null) + { + try + { + if (node != null) + { + if (xpath.IsEmpty()) + { + if (node.Value.HasValue()) + return node.Value.Convert(culture); - return defaultValue; - } + return defaultValue; + } - if (language != null) - xpath = "Localized/{0}[@culture = '{1}']".FormatWith(xpath, language.LanguageCulture.EmptyNull().ToLower()); + if (language != null) + xpath = "Localized/{0}[@culture = '{1}']".FormatWith(xpath, language.LanguageCulture.EmptyNull().ToLower()); - var n = node.SelectSingleNode(xpath); + var n = node.SelectSingleNode(xpath); - if (n != null && n.Value.HasValue()) - return n.Value.Convert(culture); - } - } - catch (Exception exc) - { - exc.Dump(); - } + if (n != null && n.Value.HasValue()) + return n.Value.Convert(culture); + } + } + catch (Exception exc) + { + exc.Dump(); + } - return defaultValue; - } + return defaultValue; + } - /// - /// Safe way to get a string value of a xpath navigator item - /// - /// The node - /// Optional xpath - /// Optional default value - /// The value - public static string GetString(this XPathNavigator node, string xpath = null, string defaultValue = null) - { - // note: better not merge GetString(...) and GetValue(...) - try - { - if (node != null) - { - if (xpath.IsEmpty()) - return node.Value; + /// + /// Safe way to get a string value of a xpath navigator item + /// + /// The node + /// Optional xpath + /// Optional default value + /// The value + public static string GetString(this XPathNavigator node, string xpath = null, string defaultValue = null) + { + // note: better not merge GetString(...) and GetValue(...) + try + { + if (node != null) + { + if (xpath.IsEmpty()) + return node.Value; - var n = node.SelectSingleNode(xpath); + var n = node.SelectSingleNode(xpath); - if (n != null) - return n.Value; - } - } - catch (Exception exc) - { - exc.Dump(); - } + if (n != null) + return n.Value; + } + } + catch (Exception exc) + { + exc.Dump(); + } - return defaultValue; - } + return defaultValue; + } - /// - /// Safe way to get a localized string value of a xpath navigator item - /// - /// The node - /// For localized values. Xpath scheme: Localized/xpath[@culture = 'language.LanguageCulture'] - /// Optional xpath - /// Optional default value - /// The value - public static string GetString(this XPathNavigator node, Language language, string xpath, string defaultValue = null) - { - return node.GetString( - "Localized/{0}[@culture = '{1}']".FormatWith(xpath.EmptyNull(), language.LanguageCulture.EmptyNull().ToLower()), - defaultValue); - } - } + /// + /// Safe way to get a localized string value of a xpath navigator item + /// + /// The node + /// For localized values. Xpath scheme: Localized/xpath[@culture = 'language.LanguageCulture'] + /// Optional xpath + /// Optional default value + /// The value + public static string GetString(this XPathNavigator node, Language language, string xpath, string defaultValue = null) + { + return node.GetString( + "Localized/{0}[@culture = '{1}']".FormatWith(xpath.EmptyNull(), language.LanguageCulture.EmptyNull().ToLower()), + defaultValue); + } + } } diff --git a/src/Libraries/SmartStore.Core/Extensions/XmlNodeExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/XmlNodeExtensions.cs index 601c4cef6d..8450b5f8f1 100644 --- a/src/Libraries/SmartStore.Core/Extensions/XmlNodeExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/XmlNodeExtensions.cs @@ -1,68 +1,68 @@ using System; -using System.ComponentModel; using System.Globalization; using System.Xml; namespace SmartStore { - public static class XmlNodeExtensions - { - /// Safe way to get inner text of an attribute. - public static T GetAttributeText(this XmlNode node, string attributeName, T defaultValue = default(T)) { - try + public static class XmlNodeExtensions + { + /// Safe way to get inner text of an attribute. + public static T GetAttributeText(this XmlNode node, string attributeName, T defaultValue = default(T)) + { + try { - if (node != null && attributeName.HasValue()) + if (node != null && attributeName.HasValue()) { - XmlAttribute attr = node.Attributes[attributeName]; - if (attr != null) + XmlAttribute attr = node.Attributes[attributeName]; + if (attr != null) { - return attr.InnerText.Convert(); - } - } - } - catch (Exception exc) + return attr.InnerText.Convert(); + } + } + } + catch (Exception exc) { - exc.Dump(); - } + exc.Dump(); + } - return defaultValue; - } + return defaultValue; + } - /// Safe way to get inner text of an attribute. - public static string GetAttributeText(this XmlNode node, string attributeName) + /// Safe way to get inner text of an attribute. + public static string GetAttributeText(this XmlNode node, string attributeName) { - return node.GetAttributeText(attributeName, null); - } + return node.GetAttributeText(attributeName, null); + } - /// Safe way to get inner text of a node. - public static T GetText(this XmlNode node, string xpath = null, T defaultValue = default(T), CultureInfo culture = null) + /// Safe way to get inner text of a node. + public static T GetText(this XmlNode node, string xpath = null, T defaultValue = default(T), CultureInfo culture = null) { - try + try { - if (node != null) + if (node != null) { - if (xpath.IsEmpty()) - return node.InnerText.Convert(); + if (xpath.IsEmpty()) + return node.InnerText.Convert(); - var n = node.SelectSingleNode(xpath); + var n = node.SelectSingleNode(xpath); - if (n != null && n.InnerText.HasValue()) - return n.InnerText.Convert(culture); + if (n != null && n.InnerText.HasValue()) + return n.InnerText.Convert(culture); } - } - catch (Exception exc) + } + catch (Exception exc) { - exc.Dump(); - } + exc.Dump(); + } - return defaultValue; - } + return defaultValue; + } - /// Safe way to get inner text of a node. - public static string GetText(this XmlNode node, string xpath = null, string defaultValue = default(string)) + /// Safe way to get inner text of a node. + public static string GetText(this XmlNode node, string xpath = null, string defaultValue = default(string)) { - return node.GetText(xpath, defaultValue); - } - } + return node.GetText(xpath, defaultValue); + } + } } diff --git a/src/Libraries/SmartStore.Core/Extensions/XmlWriterExtensions.cs b/src/Libraries/SmartStore.Core/Extensions/XmlWriterExtensions.cs index ca4e1c2c71..da9685432b 100644 --- a/src/Libraries/SmartStore.Core/Extensions/XmlWriterExtensions.cs +++ b/src/Libraries/SmartStore.Core/Extensions/XmlWriterExtensions.cs @@ -1,53 +1,52 @@ -using System; -using System.Xml; +using System.Xml; using SmartStore.Core.Domain.Localization; namespace SmartStore { - public static class XmlWriterExtensions - { - public static void WriteCData(this XmlWriter writer, string name, string value, string prefix = null, string ns = null) + public static class XmlWriterExtensions + { + public static void WriteCData(this XmlWriter writer, string name, string value, string prefix = null, string ns = null) { - if (name.HasValue() && value != null) + if (name.HasValue() && value != null) { - if (prefix == null && ns == null) - writer.WriteStartElement(name); - else - writer.WriteStartElement(prefix, name, ns); - - writer.WriteCData(value.RemoveInvalidXmlChars()); - - writer.WriteEndElement(); - } - } - - /// - /// Created a simple or CData node element - /// - /// The writer - /// Node name - /// Node value - /// The language. Its culture is always converted to lowercase! - /// Whether to create simple or CData node - public static void Write(this XmlWriter writer, string name, string value, Language language = null, bool asCData = false) - { - if (name.HasValue() && value != null) - { - if (language != null && value.IsEmpty()) - return; // do not create too many empty nodes for empty localized values - - writer.WriteStartElement(name); - - if (language != null) - writer.WriteAttributeString("culture", language.LanguageCulture.EmptyNull().ToLower()); - - if (asCData) - writer.WriteCData(value.RemoveInvalidXmlChars()); - else - writer.WriteString(value.RemoveInvalidXmlChars()); - - writer.WriteEndElement(); - } - } - } + if (prefix == null && ns == null) + writer.WriteStartElement(name); + else + writer.WriteStartElement(prefix, name, ns); + + writer.WriteCData(value.RemoveInvalidXmlChars()); + + writer.WriteEndElement(); + } + } + + /// + /// Created a simple or CData node element + /// + /// The writer + /// Node name + /// Node value + /// The language. Its culture is always converted to lowercase! + /// Whether to create simple or CData node + public static void Write(this XmlWriter writer, string name, string value, Language language = null, bool asCData = false) + { + if (name.HasValue() && value != null) + { + if (language != null && value.IsEmpty()) + return; // do not create too many empty nodes for empty localized values + + writer.WriteStartElement(name); + + if (language != null) + writer.WriteAttributeString("culture", language.LanguageCulture.EmptyNull().ToLower()); + + if (asCData) + writer.WriteCData(value.RemoveInvalidXmlChars()); + else + writer.WriteString(value.RemoveInvalidXmlChars()); + + writer.WriteEndElement(); + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Fakes/FakeController.cs b/src/Libraries/SmartStore.Core/Fakes/FakeController.cs index 1ff087bb77..2e2e4ee851 100644 --- a/src/Libraries/SmartStore.Core/Fakes/FakeController.cs +++ b/src/Libraries/SmartStore.Core/Fakes/FakeController.cs @@ -2,7 +2,7 @@ namespace SmartStore.Core.Fakes { - public class FakeController : Controller - { - } + public class FakeController : Controller + { + } } diff --git a/src/Libraries/SmartStore.Core/Fakes/FakeHttpContext.cs b/src/Libraries/SmartStore.Core/Fakes/FakeHttpContext.cs index 68302e9a61..6da257d71a 100644 --- a/src/Libraries/SmartStore.Core/Fakes/FakeHttpContext.cs +++ b/src/Libraries/SmartStore.Core/Fakes/FakeHttpContext.cs @@ -26,35 +26,35 @@ public static FakeHttpContext Root() return new FakeHttpContext("~/"); } - public FakeHttpContext(string relativeUrl) - : this(relativeUrl, null, null, null, null, null, null) - { - } + public FakeHttpContext(string relativeUrl) + : this(relativeUrl, null, null, null, null, null, null) + { + } public FakeHttpContext(string relativeUrl, string method) : this(relativeUrl, method, null, null, null, null, null, null) { } - public FakeHttpContext(string relativeUrl, - IPrincipal principal, - NameValueCollection formParams, - NameValueCollection queryStringParams, - HttpCookieCollection cookies, - SessionStateItemCollection sessionItems, - NameValueCollection serverVariables) + public FakeHttpContext(string relativeUrl, + IPrincipal principal, + NameValueCollection formParams, + NameValueCollection queryStringParams, + HttpCookieCollection cookies, + SessionStateItemCollection sessionItems, + NameValueCollection serverVariables) : this(relativeUrl, null, principal, formParams, queryStringParams, cookies, sessionItems, serverVariables) { } - public FakeHttpContext(string relativeUrl, - string method, - IPrincipal principal, - NameValueCollection formParams, - NameValueCollection queryStringParams, - HttpCookieCollection cookies, - SessionStateItemCollection sessionItems, - NameValueCollection serverVariables) + public FakeHttpContext(string relativeUrl, + string method, + IPrincipal principal, + NameValueCollection formParams, + NameValueCollection queryStringParams, + HttpCookieCollection cookies, + SessionStateItemCollection sessionItems, + NameValueCollection serverVariables) { _relativeUrl = relativeUrl; _method = method; @@ -70,28 +70,16 @@ public FakeHttpContext(string relativeUrl, Handler = new FakeHttpHandler(); } - public override IHttpHandler Handler { get; set; } + public override IHttpHandler Handler { get; set; } - public override HttpRequestBase Request - { - get - { - return _request ?? new FakeHttpRequest(_relativeUrl, _method, _formParams, _queryStringParams, _cookies, _serverVariables); - } - } + public override HttpRequestBase Request => _request ?? new FakeHttpRequest(_relativeUrl, _method, _formParams, _queryStringParams, _cookies, _serverVariables); public void SetRequest(HttpRequestBase request) { _request = request; } - public override HttpResponseBase Response - { - get - { - return _response ?? new FakeHttpResponse(); - } - } + public override HttpResponseBase Response => _response ?? new FakeHttpResponse(); public void SetResponse(HttpResponseBase response) { @@ -100,22 +88,13 @@ public void SetResponse(HttpResponseBase response) public override IPrincipal User { - get { return _principal; } - set { _principal = value; } + get => _principal; + set => _principal = value; } - public override HttpSessionStateBase Session - { - get { return new FakeHttpSessionState(_sessionItems ?? new SessionStateItemCollection()); } - } + public override HttpSessionStateBase Session => new FakeHttpSessionState(_sessionItems ?? new SessionStateItemCollection()); - public override System.Collections.IDictionary Items - { - get - { - return _items; - } - } + public override System.Collections.IDictionary Items => _items; public override bool SkipAuthorization { get; set; } diff --git a/src/Libraries/SmartStore.Core/Fakes/FakeHttpRequest.cs b/src/Libraries/SmartStore.Core/Fakes/FakeHttpRequest.cs index ee5ecc7928..5bc4db024a 100644 --- a/src/Libraries/SmartStore.Core/Fakes/FakeHttpRequest.cs +++ b/src/Libraries/SmartStore.Core/Fakes/FakeHttpRequest.cs @@ -16,100 +16,60 @@ public class FakeHttpRequest : HttpRequestBase private readonly Uri _url; private readonly Uri _urlReferrer; private readonly string _httpMethod; - private RequestContext _requestContext; + private RequestContext _requestContext; - public FakeHttpRequest(string relativeUrl, Uri url, Uri urlReferrer) - : this(relativeUrl, HttpVerbs.Get.ToString("g"), url, urlReferrer, null, null, null, null) - { - } + public FakeHttpRequest(string relativeUrl, Uri url, Uri urlReferrer) + : this(relativeUrl, HttpVerbs.Get.ToString("g"), url, urlReferrer, null, null, null, null) + { + } - public FakeHttpRequest(string relativeUrl, - string method, + public FakeHttpRequest(string relativeUrl, + string method, NameValueCollection formParams, - NameValueCollection queryStringParams, - HttpCookieCollection cookies, - NameValueCollection serverVariables) + NameValueCollection queryStringParams, + HttpCookieCollection cookies, + NameValueCollection serverVariables) { _httpMethod = method; _relativeUrl = relativeUrl; - _formParams = formParams; - _queryStringParams = queryStringParams; - _cookies = cookies; - _serverVariables = serverVariables; - //ensure collections are not null - if (_formParams == null) - _formParams = new NameValueCollection(); - if (_queryStringParams == null) - _queryStringParams = new NameValueCollection(); - if (_cookies == null) - _cookies = new HttpCookieCollection(); - if (_serverVariables == null) - _serverVariables = new NameValueCollection(); + _formParams = formParams ?? new NameValueCollection(); + _queryStringParams = queryStringParams ?? new SmartStore.Collections.QueryString().FillFromString(relativeUrl); + _cookies = cookies ?? new HttpCookieCollection(); + _serverVariables = serverVariables ?? new NameValueCollection(); } - public FakeHttpRequest(string relativeUrl, - string method, - Uri url, - Uri urlReferrer, - NameValueCollection formParams, - NameValueCollection queryStringParams, - HttpCookieCollection cookies, - NameValueCollection serverVariables) - : this(relativeUrl, method, formParams, queryStringParams, cookies, serverVariables) - { - _url = url; - _urlReferrer = urlReferrer; - } - - public override NameValueCollection ServerVariables + public FakeHttpRequest(string relativeUrl, + string method, + Uri url, + Uri urlReferrer, + NameValueCollection formParams, + NameValueCollection queryStringParams, + HttpCookieCollection cookies, + NameValueCollection serverVariables) + : this(relativeUrl, method, formParams, queryStringParams, cookies, serverVariables) { - get - { - return _serverVariables; - } + _url = url; + _urlReferrer = urlReferrer; } - public override NameValueCollection Form - { - get { return _formParams; } - } + public override NameValueCollection ServerVariables => _serverVariables; - public override NameValueCollection QueryString - { - get { return _queryStringParams; } - } + public override NameValueCollection Form => _formParams; - public override HttpCookieCollection Cookies - { - get { return _cookies; } - } + public override NameValueCollection QueryString => _queryStringParams; - public override string AppRelativeCurrentExecutionFilePath - { - get { return _relativeUrl; } - } + public override HttpCookieCollection Cookies => _cookies; - public override Uri Url - { - get - { - return _url ?? new Uri("http://tempuri.org"); - } - } + public override string AppRelativeCurrentExecutionFilePath => _relativeUrl; - public override Uri UrlReferrer - { - get - { - return _urlReferrer ?? new Uri("http://tempuri.org"); - } - } + public override Uri Url => _url ?? new Uri("http://tempuri.org"); - public override string PathInfo - { - get { return ""; } - } + public override Uri UrlReferrer => _urlReferrer ?? new Uri("http://tempuri.org"); + + public override string PathInfo => ""; + + public override string Path => _url?.AbsolutePath ?? ApplicationPath ?? "/"; public override string ApplicationPath { @@ -123,37 +83,22 @@ public override string ApplicationPath } } - public override string HttpMethod - { - get - { - return _httpMethod; - } - } + public override string HttpMethod => _httpMethod; - public override string UserHostAddress + public override string UserHostAddress => null; + + public override string RawUrl => this.ApplicationPath; + public override bool IsSecureConnection => _url?.Scheme?.EmptyNull().StartsWith("https", StringComparison.OrdinalIgnoreCase) == true; + public override bool IsAuthenticated => false; + public override string[] UserLanguages => new string[] { }; + public override string UserAgent => "Smartstore"; + public override bool IsLocal => false; + + public override RequestContext RequestContext { - get { return null; } - } + get => _requestContext ?? new RequestContext(); - public override string RawUrl => this.ApplicationPath; - public override bool IsSecureConnection => _url?.Scheme?.EmptyNull().StartsWith("https", StringComparison.OrdinalIgnoreCase) == true; - public override bool IsAuthenticated => false; - public override string[] UserLanguages => new string[] { }; - public override string UserAgent => "SmartStore.NET"; - public override bool IsLocal => false; - - public override RequestContext RequestContext - { - get - { - return _requestContext ?? new RequestContext(); - } - - set - { - _requestContext = value; - } - } - } + set => _requestContext = value; + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Fakes/FakeHttpResponse.cs b/src/Libraries/SmartStore.Core/Fakes/FakeHttpResponse.cs index 299b379037..4b5719ed6e 100644 --- a/src/Libraries/SmartStore.Core/Fakes/FakeHttpResponse.cs +++ b/src/Libraries/SmartStore.Core/Fakes/FakeHttpResponse.cs @@ -6,18 +6,15 @@ namespace SmartStore.Core.Fakes public class FakeHttpResponse : HttpResponseBase { private readonly HttpCookieCollection _cookies; - private readonly StringBuilder _outputString = new StringBuilder(); + private readonly StringBuilder _outputString = new StringBuilder(); public FakeHttpResponse() { this._cookies = new HttpCookieCollection(); - } - - public string ResponseOutput - { - get { return _outputString.ToString(); } } + public string ResponseOutput => _outputString.ToString(); + public override int StatusCode { get; set; } public override string RedirectLocation { get; set; } @@ -32,12 +29,6 @@ public override string ApplyAppPathModifier(string virtualPath) return virtualPath; } - public override HttpCookieCollection Cookies - { - get - { - return _cookies; - } - } + public override HttpCookieCollection Cookies => _cookies; } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Fakes/FakeHttpSessionState.cs b/src/Libraries/SmartStore.Core/Fakes/FakeHttpSessionState.cs index 97bd46062e..6d45307e06 100644 --- a/src/Libraries/SmartStore.Core/Fakes/FakeHttpSessionState.cs +++ b/src/Libraries/SmartStore.Core/Fakes/FakeHttpSessionState.cs @@ -14,20 +14,14 @@ public FakeHttpSessionState(SessionStateItemCollection sessionItems) _sessionItems = sessionItems; } - public override int Count - { - get { return _sessionItems.Count; } - } + public override int Count => _sessionItems.Count; - public override NameObjectCollectionBase.KeysCollection Keys - { - get { return _sessionItems.Keys; } - } + public override NameObjectCollectionBase.KeysCollection Keys => _sessionItems.Keys; public override object this[string name] { - get { return _sessionItems[name]; } - set { _sessionItems[name] = value; } + get => _sessionItems[name]; + set => _sessionItems[name] = value; } public bool Exists(string key) @@ -37,8 +31,8 @@ public bool Exists(string key) public override object this[int index] { - get { return _sessionItems[index]; } - set { _sessionItems[index] = value; } + get => _sessionItems[index]; + set => _sessionItems[index] = value; } public override void Add(string name, object value) diff --git a/src/Libraries/SmartStore.Core/Fakes/FakeIdentity.cs b/src/Libraries/SmartStore.Core/Fakes/FakeIdentity.cs index 2ba265b28a..a4d9ba0d6c 100644 --- a/src/Libraries/SmartStore.Core/Fakes/FakeIdentity.cs +++ b/src/Libraries/SmartStore.Core/Fakes/FakeIdentity.cs @@ -12,20 +12,11 @@ public FakeIdentity(string userName) _name = userName; } - public string AuthenticationType - { - get { throw new NotImplementedException(); } - } + public string AuthenticationType => throw new NotImplementedException(); - public bool IsAuthenticated - { - get { return !String.IsNullOrEmpty(_name); } - } + public bool IsAuthenticated => !String.IsNullOrEmpty(_name); - public string Name - { - get { return _name; } - } + public string Name => _name; } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Fakes/FakePrincipal.cs b/src/Libraries/SmartStore.Core/Fakes/FakePrincipal.cs index 276931651e..6205f1ca4b 100644 --- a/src/Libraries/SmartStore.Core/Fakes/FakePrincipal.cs +++ b/src/Libraries/SmartStore.Core/Fakes/FakePrincipal.cs @@ -15,10 +15,7 @@ public FakePrincipal(IIdentity identity, string[] roles) } - public IIdentity Identity - { - get { return _identity; } - } + public IIdentity Identity => _identity; public bool IsInRole(string role) { diff --git a/src/Libraries/SmartStore.Core/GenericEqualityComparer.cs b/src/Libraries/SmartStore.Core/GenericEqualityComparer.cs index dd66b7b1a7..e96d02f721 100644 --- a/src/Libraries/SmartStore.Core/GenericEqualityComparer.cs +++ b/src/Libraries/SmartStore.Core/GenericEqualityComparer.cs @@ -5,48 +5,48 @@ namespace SmartStore.Core { public sealed partial class GenericEqualityComparer : IEqualityComparer { - private readonly Func _equals; - private readonly Func _getHashcode; + private readonly Func _equals; + private readonly Func _getHashcode; - public GenericEqualityComparer(Func equals, Func getHashcode) - { - _getHashcode = getHashcode; - _equals = equals; - } + public GenericEqualityComparer(Func equals, Func getHashcode) + { + _getHashcode = getHashcode; + _equals = equals; + } - public static GenericEqualityComparer CompareMember(Func memberExpression) where TMember : IEquatable - { - return new GenericEqualityComparer( - (x, y) => memberExpression.Invoke(x).Equals((TMember)memberExpression.Invoke(y)), - x => - { - var invoked = memberExpression.Invoke(x); - return !ReferenceEquals(invoked, default(TMember)) ? invoked.GetHashCode() : 0; - }); - } + public static GenericEqualityComparer CompareMember(Func memberExpression) where TMember : IEquatable + { + return new GenericEqualityComparer( + (x, y) => memberExpression.Invoke(x).Equals((TMember)memberExpression.Invoke(y)), + x => + { + var invoked = memberExpression.Invoke(x); + return !ReferenceEquals(invoked, default(TMember)) ? invoked.GetHashCode() : 0; + }); + } - /// - /// Determines whether the specified objects are equal. - /// - /// - /// true if the specified objects are equal; otherwise, false. - /// - /// The first object of type to compare.The second object of type to compare. - public bool Equals(T x, T y) - { - return _equals.Invoke(x, y); - } + /// + /// Determines whether the specified objects are equal. + /// + /// + /// true if the specified objects are equal; otherwise, false. + /// + /// The first object of type to compare.The second object of type to compare. + public bool Equals(T x, T y) + { + return _equals.Invoke(x, y); + } - /// - /// Returns a hash code for the specified object. - /// - /// - /// A hash code for the specified object. - /// - /// The for which a hash code is to be returned.The type of is a reference type and is null. - public int GetHashCode(T obj) - { - return _getHashcode.Invoke(obj); - } - } + /// + /// Returns a hash code for the specified object. + /// + /// + /// A hash code for the specified object. + /// + /// The for which a hash code is to be returned.The type of is a reference type and is null. + public int GetHashCode(T obj) + { + return _getHashcode.Invoke(obj); + } + } } diff --git a/src/Libraries/SmartStore.Core/Html/BBCodeHelper.cs b/src/Libraries/SmartStore.Core/Html/BBCodeHelper.cs index b4e9c92eb0..268e22b441 100644 --- a/src/Libraries/SmartStore.Core/Html/BBCodeHelper.cs +++ b/src/Libraries/SmartStore.Core/Html/BBCodeHelper.cs @@ -11,7 +11,7 @@ public partial class BBCodeHelper private static readonly Regex regexUnderLine = new Regex(@"\[u\](.+?)\[/u\]", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex regexUrl1 = new Regex(@"\[url\=([^\]]+)\]([^\]]+)\[/url\]", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex regexUrl2 = new Regex(@"\[url\](.+?)\[/url\]", RegexOptions.Compiled | RegexOptions.IgnoreCase); - private static readonly Regex regexQuote = new Regex(@"\[quote(=.+?)?\](.+?)\[/quote\]", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex regexQuote = new Regex(@"\[quote(=.+?)?\](.+?)\[/quote\]", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// /// Formats the text @@ -25,13 +25,13 @@ public partial class BBCodeHelper /// A value indicating whether to replace Quote /// Formatted text public static string ToHtml( - string text, - bool replaceBold = true, - bool replaceItalic = true, + string text, + bool replaceBold = true, + bool replaceItalic = true, bool replaceUnderline = true, - bool replaceUrl = true, - bool replaceCode = true, - bool replaceQuote = true) + bool replaceUrl = true, + bool replaceCode = true, + bool replaceQuote = true) { if (String.IsNullOrEmpty(text)) return string.Empty; @@ -70,29 +70,29 @@ public static string ToHtml( if (replaceQuote) { - while (regexQuote.IsMatch(text)) - { - text = regexQuote.Replace(text, (m) => - { - var from = m.Groups[1].Value; - var quote = m.Groups[2].Value; - - if (quote.IsEmpty()) - { - return ""; - } - - string result = ""; - if (from.HasValue()) - { - result += "{0}:".FormatCurrent(from.Substring(1)); - } - - result += "
{0}
".FormatCurrent(quote); - - return result; - }); - } + while (regexQuote.IsMatch(text)) + { + text = regexQuote.Replace(text, (m) => + { + var from = m.Groups[1].Value; + var quote = m.Groups[2].Value; + + if (quote.IsEmpty()) + { + return ""; + } + + string result = ""; + if (from.HasValue()) + { + result += "{0}:".FormatCurrent(from.Substring(1)); + } + + result += "
{0}
".FormatCurrent(quote); + + return result; + }); + } } diff --git a/src/Libraries/SmartStore.Core/Html/CodeFormatter/CLikeFormat.cs b/src/Libraries/SmartStore.Core/Html/CodeFormatter/CLikeFormat.cs index 4a4f948734..122f9a2c43 100644 --- a/src/Libraries/SmartStore.Core/Html/CodeFormatter/CLikeFormat.cs +++ b/src/Libraries/SmartStore.Core/Html/CodeFormatter/CLikeFormat.cs @@ -17,32 +17,26 @@ * be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. - */ + */ #endregion namespace SmartStore.Core.Html.CodeFormatter { - /// - /// Provides a base class for formatting languages similar to C. - /// + /// + /// Provides a base class for formatting languages similar to C. + /// public abstract partial class CLikeFormat : CodeFormat - { - /// - /// Regular expression string to match single line and multi-line - /// comments (// and /* */). - /// - protected override string CommentRegex - { - get { return @"/\*.*?\*/|//.*?(?=\r|\n)"; } - } + { + /// + /// Regular expression string to match single line and multi-line + /// comments (// and /* */). + /// + protected override string CommentRegex => @"/\*.*?\*/|//.*?(?=\r|\n)"; - /// - /// Regular expression string to match string and character literals. - /// - protected override string StringRegex - { - get { return @"@?""""|@?"".*?(?!\\).""|''|'.*?(?!\\).'"; } - } - } + /// + /// Regular expression string to match string and character literals. + /// + protected override string StringRegex => @"@?""""|@?"".*?(?!\\).""|''|'.*?(?!\\).'"; + } } diff --git a/src/Libraries/SmartStore.Core/Html/CodeFormatter/CSharpFormat.cs b/src/Libraries/SmartStore.Core/Html/CodeFormatter/CSharpFormat.cs index 1822293a6d..85fb7ed828 100644 --- a/src/Libraries/SmartStore.Core/Html/CodeFormatter/CSharpFormat.cs +++ b/src/Libraries/SmartStore.Core/Html/CodeFormatter/CSharpFormat.cs @@ -17,7 +17,7 @@ * be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. - */ + */ #endregion namespace SmartStore.Core.Html.CodeFormatter @@ -26,36 +26,24 @@ namespace SmartStore.Core.Html.CodeFormatter /// Generates color-coded HTML 4.01 from C# source code. ///
public partial class CSharpFormat : CLikeFormat - { - /// - /// The list of C# keywords. - /// - protected override string Keywords - { - get - { - return "abstract as base bool break byte case catch char " - + "checked class const continue decimal default delegate do double else " - + "enum event explicit extern false finally fixed float for foreach goto " - + "if implicit in int interface internal is lock long namespace new null " - + "object operator out override partial params private protected public readonly " - + "ref return sbyte sealed short sizeof stackalloc static string struct " - + "switch this throw true try typeof uint ulong unchecked unsafe ushort " - + "using value virtual void volatile where while yield"; - } - } + { + /// + /// The list of C# keywords. + /// + protected override string Keywords => "abstract as base bool break byte case catch char " + + "checked class const continue decimal default delegate do double else " + + "enum event explicit extern false finally fixed float for foreach goto " + + "if implicit in int interface internal is lock long namespace new null " + + "object operator out override partial params private protected public readonly " + + "ref return sbyte sealed short sizeof stackalloc static string struct " + + "switch this throw true try typeof uint ulong unchecked unsafe ushort " + + "using value virtual void volatile where while yield"; - /// - /// The list of C# preprocessors. - /// - protected override string Preprocessors - { - get - { - return "#if #else #elif #endif #define #undef #warning " - + "#error #line #region #endregion #pragma"; - } - } - } + /// + /// The list of C# preprocessors. + /// + protected override string Preprocessors => "#if #else #elif #endif #define #undef #warning " + + "#error #line #region #endregion #pragma"; + } } diff --git a/src/Libraries/SmartStore.Core/Html/CodeFormatter/CodeFormat.cs b/src/Libraries/SmartStore.Core/Html/CodeFormatter/CodeFormat.cs index efaef284bc..9fb8944681 100644 --- a/src/Libraries/SmartStore.Core/Html/CodeFormatter/CodeFormat.cs +++ b/src/Libraries/SmartStore.Core/Html/CodeFormatter/CodeFormat.cs @@ -17,7 +17,7 @@ * be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. - */ + */ #endregion using System.IO; @@ -26,144 +26,138 @@ namespace SmartStore.Core.Html.CodeFormatter { - /// - /// Provides a base class for formatting most programming languages. - /// + /// + /// Provides a base class for formatting most programming languages. + /// public abstract partial class CodeFormat : SourceFormat - { - /// - /// Must be overridden to provide a list of keywords defined in - /// each language. - /// - /// - /// Keywords must be separated with spaces. - /// - protected abstract string Keywords - { - get; - } + { + /// + /// Must be overridden to provide a list of keywords defined in + /// each language. + /// + /// + /// Keywords must be separated with spaces. + /// + protected abstract string Keywords + { + get; + } - /// - /// Can be overridden to provide a list of preprocessors defined in - /// each language. - /// - /// - /// Preprocessors must be separated with spaces. - /// - protected virtual string Preprocessors - { - get { return ""; } - } + /// + /// Can be overridden to provide a list of preprocessors defined in + /// each language. + /// + /// + /// Preprocessors must be separated with spaces. + /// + protected virtual string Preprocessors => ""; - /// - /// Must be overridden to provide a regular expression string - /// to match strings literals. - /// - protected abstract string StringRegex - { - get; - } + /// + /// Must be overridden to provide a regular expression string + /// to match strings literals. + /// + protected abstract string StringRegex + { + get; + } - /// - /// Must be overridden to provide a regular expression string - /// to match comments. - /// - protected abstract string CommentRegex - { - get; - } + /// + /// Must be overridden to provide a regular expression string + /// to match comments. + /// + protected abstract string CommentRegex + { + get; + } - /// - /// Determines if the language is case sensitive. - /// - /// true if the language is case sensitive, false - /// otherwise. The default is true. - /// - /// A case-insensitive language formatter must override this - /// property to return false. - /// - public virtual bool CaseSensitive - { - get { return true; } - } + /// + /// Determines if the language is case sensitive. + /// + /// true if the language is case sensitive, false + /// otherwise. The default is true. + /// + /// A case-insensitive language formatter must override this + /// property to return false. + /// + public virtual bool CaseSensitive => true; - /// - protected CodeFormat() - { - //generate the keyword and preprocessor regexes from the keyword lists - Regex r; - r = new Regex(@"\w+|-\w+|#\w+|@@\w+|#(?:\\(?:s|w)(?:\*|\+)?\w+)+|@\\w\*+"); - string regKeyword = r.Replace(Keywords, @"(?<=^|\W)$0(?=\W)"); - string regPreproc = r.Replace(Preprocessors, @"(?<=^|\s)$0(?=\s|$)"); - r = new Regex(@" +"); - regKeyword = r.Replace(regKeyword, @"|"); - regPreproc = r.Replace(regPreproc, @"|"); + /// + protected CodeFormat() + { + //generate the keyword and preprocessor regexes from the keyword lists + Regex r; + r = new Regex(@"\w+|-\w+|#\w+|@@\w+|#(?:\\(?:s|w)(?:\*|\+)?\w+)+|@\\w\*+"); + string regKeyword = r.Replace(Keywords, @"(?<=^|\W)$0(?=\W)"); + string regPreproc = r.Replace(Preprocessors, @"(?<=^|\s)$0(?=\s|$)"); + r = new Regex(@" +"); + regKeyword = r.Replace(regKeyword, @"|"); + regPreproc = r.Replace(regPreproc, @"|"); - if (regPreproc.Length == 0) - { - regPreproc = "(?!.*)_{37}(? 0) - { - regAll.Append(")|("); - regAll.Append(regPreproc); - } - regAll.Append(")|("); - regAll.Append(regKeyword); - regAll.Append(")"); + regAll.Append("("); + regAll.Append(CommentRegex); + regAll.Append(")|("); + regAll.Append(StringRegex); + if (regPreproc.Length > 0) + { + regAll.Append(")|("); + regAll.Append(regPreproc); + } + regAll.Append(")|("); + regAll.Append(regKeyword); + regAll.Append(")"); - RegexOptions caseInsensitive = CaseSensitive ? 0 : RegexOptions.IgnoreCase; - CodeRegex = new Regex(regAll.ToString(), RegexOptions.Singleline | caseInsensitive); - } + RegexOptions caseInsensitive = CaseSensitive ? 0 : RegexOptions.IgnoreCase; + CodeRegex = new Regex(regAll.ToString(), RegexOptions.Singleline | caseInsensitive); + } - /// - /// Called to evaluate the HTML fragment corresponding to each - /// matching token in the code. - /// - /// The resulting from a - /// single regular expression match. - /// A string containing the HTML code fragment. - protected override string MatchEval(Match match) - { - if(match.Groups[1].Success) //comment - { + /// + /// Called to evaluate the HTML fragment corresponding to each + /// matching token in the code. + /// + /// The resulting from a + /// single regular expression match. + /// A string containing the HTML code fragment. + protected override string MatchEval(Match match) + { + if (match.Groups[1].Success) //comment + { var reader = new StringReader(match.ToString()); - string line; + string line; var sb = new StringBuilder(); - while ((line = reader.ReadLine()) != null) - { - if(sb.Length > 0) - { - sb.Append("\n"); - } - sb.Append(""); - sb.Append(line); - sb.Append(""); - } - return sb.ToString(); - } - if(match.Groups[2].Success) //string literal - { - return "" + match.ToString() + ""; - } - if(match.Groups[3].Success) //preprocessor keyword - { - return "" + match.ToString() + ""; - } - if(match.Groups[4].Success) //keyword - { - return "" + match.ToString() + ""; - } - System.Diagnostics.Debug.Assert(false, "None of the above!"); - return ""; //none of the above - } - } + while ((line = reader.ReadLine()) != null) + { + if (sb.Length > 0) + { + sb.Append("\n"); + } + sb.Append(""); + sb.Append(line); + sb.Append(""); + } + return sb.ToString(); + } + if (match.Groups[2].Success) //string literal + { + return "" + match.ToString() + ""; + } + if (match.Groups[3].Success) //preprocessor keyword + { + return "" + match.ToString() + ""; + } + if (match.Groups[4].Success) //keyword + { + return "" + match.ToString() + ""; + } + System.Diagnostics.Debug.Assert(false, "None of the above!"); + return ""; //none of the above + } + } } diff --git a/src/Libraries/SmartStore.Core/Html/CodeFormatter/CodeFormatHelper.cs b/src/Libraries/SmartStore.Core/Html/CodeFormatter/CodeFormatHelper.cs index e58e51d428..ff56b0f0d5 100644 --- a/src/Libraries/SmartStore.Core/Html/CodeFormatter/CodeFormatHelper.cs +++ b/src/Libraries/SmartStore.Core/Html/CodeFormatter/CodeFormatHelper.cs @@ -4,9 +4,9 @@ namespace SmartStore.Core.Html.CodeFormatter { - /// - /// Represents a code format helper - /// + /// + /// Represents a code format helper + /// public partial class CodeFormatHelper { #region Fields @@ -32,8 +32,8 @@ private static string CodeEvaluatorSimple(Match match) options.Language = "c#"; options.Code = match.Groups["inner"].Value; options.DisplayLineNumbers = false; - options.Title =string.Empty; - options.AlternateLineNumbers =false; + options.Title = string.Empty; + options.AlternateLineNumbers = false; string result = match.Value; result = Highlight(options, result); diff --git a/src/Libraries/SmartStore.Core/Html/CodeFormatter/HtmlFormat.cs b/src/Libraries/SmartStore.Core/Html/CodeFormatter/HtmlFormat.cs index 718ce0db1e..3592cb6354 100644 --- a/src/Libraries/SmartStore.Core/Html/CodeFormatter/HtmlFormat.cs +++ b/src/Libraries/SmartStore.Core/Html/CodeFormatter/HtmlFormat.cs @@ -17,7 +17,7 @@ * be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. - */ + */ #endregion using System.IO; @@ -26,124 +26,124 @@ namespace SmartStore.Core.Html.CodeFormatter { - /// - /// Generates color-coded HTML 4.01 from HTML/XML/ASPX source code. - /// - /// - /// - /// This implementation assumes that code inside <script> blocks - /// is JavaScript, and code inside <% %> blocks is C#. - /// - /// The default tab width is set to 2 characters in this class. - /// + /// + /// Generates color-coded HTML 4.01 from HTML/XML/ASPX source code. + /// + /// + /// + /// This implementation assumes that code inside <script> blocks + /// is JavaScript, and code inside <% %> blocks is C#. + /// + /// The default tab width is set to 2 characters in this class. + /// public partial class HtmlFormat : SourceFormat - { - private readonly CSharpFormat csf; //to format embedded C# code + { + private readonly CSharpFormat csf; //to format embedded C# code private readonly JavaScriptFormat jsf; //to format client-side JavaScript code private readonly Regex attribRegex; - /// - public HtmlFormat() - { - const string regJavaScript = @"(?<=<script(?:\s.*?)?>).+?(?=</script>)"; - const string regComment = @"<!--.*?-->"; - const string regAspTag = @"<%@.*?%>|<%|%>"; - const string regAspCode = @"(?<=<%).*?(?=%>)"; - const string regTagDelimiter = @"(?:</?!?\??(?!%)|(? + public HtmlFormat() + { + const string regJavaScript = @"(?<=<script(?:\s.*?)?>).+?(?=</script>)"; + const string regComment = @"<!--.*?-->"; + const string regAspTag = @"<%@.*?%>|<%|%>"; + const string regAspCode = @"(?<=<%).*?(?=%>)"; + const string regTagDelimiter = @"(?:</?!?\??(?!%)|(? - /// Called to evaluate the HTML fragment corresponding to each - /// attribute's name/value in the code. - /// - /// The resulting from a - /// single regular expression match. - /// A string containing the HTML code fragment. - private string AttributeMatchEval(Match match) - { - if(match.Groups[1].Success) //attribute value - return "" + match.ToString() + ""; + csf = new CSharpFormat(); + jsf = new JavaScriptFormat(); + } - if(match.Groups[2].Success) //attribute name - return "" + match.ToString() + ""; + /// + /// Called to evaluate the HTML fragment corresponding to each + /// attribute's name/value in the code. + /// + /// The resulting from a + /// single regular expression match. + /// A string containing the HTML code fragment. + private string AttributeMatchEval(Match match) + { + if (match.Groups[1].Success) //attribute value + return "" + match.ToString() + ""; - return match.ToString(); - } + if (match.Groups[2].Success) //attribute name + return "" + match.ToString() + ""; - /// - /// Called to evaluate the HTML fragment corresponding to each - /// matching token in the code. - /// - /// The resulting from a - /// single regular expression match. - /// A string containing the HTML code fragment. - protected override string MatchEval(Match match) - { - if(match.Groups[1].Success) //JavaScript code - { - //string s = match.ToString(); - return jsf.FormatSubCode(match.ToString()); - } - if(match.Groups[2].Success) //comment - { + return match.ToString(); + } + + /// + /// Called to evaluate the HTML fragment corresponding to each + /// matching token in the code. + /// + /// The resulting from a + /// single regular expression match. + /// A string containing the HTML code fragment. + protected override string MatchEval(Match match) + { + if (match.Groups[1].Success) //JavaScript code + { + //string s = match.ToString(); + return jsf.FormatSubCode(match.ToString()); + } + if (match.Groups[2].Success) //comment + { var reader = new StringReader(match.ToString()); - string line; + string line; var sb = new StringBuilder(); - while ((line = reader.ReadLine()) != null) - { - if(sb.Length > 0) - { - sb.Append("\n"); - } - sb.Append(""); - sb.Append(line); - sb.Append(""); - } - return sb.ToString(); - } - if(match.Groups[3].Success) //asp tag - { - return "" + match.ToString() + ""; - } - if(match.Groups[4].Success) //asp C# code - { - return csf.FormatSubCode(match.ToString()); - } - if(match.Groups[5].Success) //tag delimiter - { - return "" + match.ToString() + ""; - } - if(match.Groups[6].Success) //html tagname - { - return "" + match.ToString() + ""; - } - if(match.Groups[7].Success) //attributes - { - return attribRegex.Replace(match.ToString(), - new MatchEvaluator(this.AttributeMatchEval)); - } - if(match.Groups[8].Success) //entity - { - return "" + match.ToString() + ""; - } - return match.ToString(); - } - } + while ((line = reader.ReadLine()) != null) + { + if (sb.Length > 0) + { + sb.Append("\n"); + } + sb.Append(""); + sb.Append(line); + sb.Append(""); + } + return sb.ToString(); + } + if (match.Groups[3].Success) //asp tag + { + return "" + match.ToString() + ""; + } + if (match.Groups[4].Success) //asp C# code + { + return csf.FormatSubCode(match.ToString()); + } + if (match.Groups[5].Success) //tag delimiter + { + return "" + match.ToString() + ""; + } + if (match.Groups[6].Success) //html tagname + { + return "" + match.ToString() + ""; + } + if (match.Groups[7].Success) //attributes + { + return attribRegex.Replace(match.ToString(), + new MatchEvaluator(this.AttributeMatchEval)); + } + if (match.Groups[8].Success) //entity + { + return "" + match.ToString() + ""; + } + return match.ToString(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Html/CodeFormatter/JavaScriptFormat.cs b/src/Libraries/SmartStore.Core/Html/CodeFormatter/JavaScriptFormat.cs index 9f65a2bd50..6319d104c2 100644 --- a/src/Libraries/SmartStore.Core/Html/CodeFormatter/JavaScriptFormat.cs +++ b/src/Libraries/SmartStore.Core/Html/CodeFormatter/JavaScriptFormat.cs @@ -17,7 +17,7 @@ * be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. - */ + */ #endregion namespace SmartStore.Core.Html.CodeFormatter @@ -26,35 +26,23 @@ namespace SmartStore.Core.Html.CodeFormatter /// Generates color-coded HTML 4.01 from JavaSctript source code. /// public partial class JavaScriptFormat : CLikeFormat - { - /// - /// The list of JavaScript keywords. - /// - protected override string Keywords - { - get - { - return "var function abstract as base bool break byte case catch char " - + "checked class const continue decimal default delegate do double else " - + "enum event explicit extern false finally fixed float for foreach goto " - + "if implicit in int interface internal is lock long namespace new null " - + "object operator out override params private protected public readonly " - + "ref return sbyte sealed short sizeof stackalloc static string struct " - + "switch this throw true try typeof uint ulong unchecked unsafe ushort " - + "using virtual void while"; - } - } + { + /// + /// The list of JavaScript keywords. + /// + protected override string Keywords => "var function abstract as base bool break byte case catch char " + + "checked class const continue decimal default delegate do double else " + + "enum event explicit extern false finally fixed float for foreach goto " + + "if implicit in int interface internal is lock long namespace new null " + + "object operator out override params private protected public readonly " + + "ref return sbyte sealed short sizeof stackalloc static string struct " + + "switch this throw true try typeof uint ulong unchecked unsafe ushort " + + "using virtual void while"; - /// - /// Use the pre-processor color to mark directives that start with @. - /// - protected override string Preprocessors - { - get - { - return @"@\w*"; - } - } - } + /// + /// Use the pre-processor color to mark directives that start with @. + /// + protected override string Preprocessors => @"@\w*"; + } } diff --git a/src/Libraries/SmartStore.Core/Html/CodeFormatter/MshFormat.cs b/src/Libraries/SmartStore.Core/Html/CodeFormatter/MshFormat.cs index fbe7acbee1..17964eb843 100644 --- a/src/Libraries/SmartStore.Core/Html/CodeFormatter/MshFormat.cs +++ b/src/Libraries/SmartStore.Core/Html/CodeFormatter/MshFormat.cs @@ -1,52 +1,34 @@ namespace SmartStore.Core.Html.CodeFormatter { - /// - /// Generates color-coded HTML 4.01 from MSH (code name Monad) source code. - /// + /// + /// Generates color-coded HTML 4.01 from MSH (code name Monad) source code. + /// public partial class MshFormat : CodeFormat - { - /// - /// Regular expression string to match single line comments (#). - /// - protected override string CommentRegex - { - get { return @"#.*?(?=\r|\n)"; } - } + { + /// + /// Regular expression string to match single line comments (#). + /// + protected override string CommentRegex => @"#.*?(?=\r|\n)"; - /// - /// Regular expression string to match string and character literals. - /// - protected override string StringRegex - { - get { return @"@?""""|@?"".*?(?!\\).""|''|'.*?(?!\\).'"; } - } + /// + /// Regular expression string to match string and character literals. + /// + protected override string StringRegex => @"@?""""|@?"".*?(?!\\).""|''|'.*?(?!\\).'"; - /// - /// The list of MSH keywords. - /// - protected override string Keywords - { - get - { - return "function filter global script local private if else" - + " elseif for foreach in while switch continue break" - + " return default param begin process end throw trap"; - } - } + /// + /// The list of MSH keywords. + /// + protected override string Keywords => "function filter global script local private if else" + + " elseif for foreach in while switch continue break" + + " return default param begin process end throw trap"; - /// - /// Use preprocessors property to hilight operators. - /// - protected override string Preprocessors - { - get - { - return "-band -bor -match -notmatch -like -notlike -eq -ne" - + " -gt -ge -lt -le -is -imatch -inotmatch -ilike" - + " -inotlike -ieq -ine -igt -ige -ilt -ile"; - } - } + /// + /// Use preprocessors property to hilight operators. + /// + protected override string Preprocessors => "-band -bor -match -notmatch -like -notlike -eq -ne" + + " -gt -ge -lt -le -is -imatch -inotmatch -ilike" + + " -inotlike -ieq -ine -igt -ige -ilt -ile"; - } + } } diff --git a/src/Libraries/SmartStore.Core/Html/CodeFormatter/SourceFormat.cs b/src/Libraries/SmartStore.Core/Html/CodeFormatter/SourceFormat.cs index a92c4d20f4..2b5b232219 100644 --- a/src/Libraries/SmartStore.Core/Html/CodeFormatter/SourceFormat.cs +++ b/src/Libraries/SmartStore.Core/Html/CodeFormatter/SourceFormat.cs @@ -17,7 +17,7 @@ * be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. - */ + */ #endregion using System; @@ -28,238 +28,238 @@ namespace SmartStore.Core.Html.CodeFormatter { - /// - /// Provides a base implementation for all code formatters. - /// - /// - /// - /// To display the formatted code on your web site, the web page must - /// refer to a stylesheet that defines the formatting for the different - /// CSS classes generated by CSharpFormat: - /// .csharpcode, pre, .rem, .kwrd, .str, .op, .preproc, .alt, .lnum. - /// - /// - /// Note that if you have multi-line comments in your source code - /// (like /* ... */), the "line numbers" or "alternate line background" - /// options will generate code that is not strictly HTML 4.01 compliant. - /// The code will still look good with IE5+ or Mozilla 0.8+. - /// - /// + /// + /// Provides a base implementation for all code formatters. + /// + /// + /// + /// To display the formatted code on your web site, the web page must + /// refer to a stylesheet that defines the formatting for the different + /// CSS classes generated by CSharpFormat: + /// .csharpcode, pre, .rem, .kwrd, .str, .op, .preproc, .alt, .lnum. + /// + /// + /// Note that if you have multi-line comments in your source code + /// (like /* ... */), the "line numbers" or "alternate line background" + /// options will generate code that is not strictly HTML 4.01 compliant. + /// The code will still look good with IE5+ or Mozilla 0.8+. + /// + /// public abstract partial class SourceFormat - { - /// - protected SourceFormat() - { - _tabSpaces = 4; - _lineNumbers = false; - _alternate = false; - _embedStyleSheet = false; - } + { + /// + protected SourceFormat() + { + _tabSpaces = 4; + _lineNumbers = false; + _alternate = false; + _embedStyleSheet = false; + } - private byte _tabSpaces; + private byte _tabSpaces; - /// - /// Gets or sets the tabs width. - /// - /// The number of space characters to substitute for tab - /// characters. The default is 4, unless overridden is a - /// derived class. - public byte TabSpaces - { - get { return _tabSpaces; } - set { _tabSpaces = value; } - } + /// + /// Gets or sets the tabs width. + /// + /// The number of space characters to substitute for tab + /// characters. The default is 4, unless overridden is a + /// derived class. + public byte TabSpaces + { + get => _tabSpaces; + set => _tabSpaces = value; + } - private bool _lineNumbers; + private bool _lineNumbers; - /// - /// Enables or disables line numbers in output. - /// - /// When true, line numbers are generated. - /// The default is false. - public bool LineNumbers - { - get { return _lineNumbers; } - set { _lineNumbers = value; } - } + /// + /// Enables or disables line numbers in output. + /// + /// When true, line numbers are generated. + /// The default is false. + public bool LineNumbers + { + get => _lineNumbers; + set => _lineNumbers = value; + } - private bool _alternate; + private bool _alternate; - /// - /// Enables or disables alternating line background. - /// - /// When true, lines background is alternated. - /// The default is false. - public bool Alternate - { - get { return _alternate; } - set { _alternate = value; } - } + /// + /// Enables or disables alternating line background. + /// + /// When true, lines background is alternated. + /// The default is false. + public bool Alternate + { + get => _alternate; + set => _alternate = value; + } - private bool _embedStyleSheet; + private bool _embedStyleSheet; - /// - /// Enables or disables the embedded CSS style sheet. - /// - /// When true, the CSS <style> element is included - /// in the HTML output. The default is false. - public bool EmbedStyleSheet - { - get { return _embedStyleSheet; } - set { _embedStyleSheet = value; } - } + /// + /// Enables or disables the embedded CSS style sheet. + /// + /// When true, the CSS <style> element is included + /// in the HTML output. The default is false. + public bool EmbedStyleSheet + { + get => _embedStyleSheet; + set => _embedStyleSheet = value; + } - /// Transform source code to HTML 4.01. - /// - /// - /// Transforms a source code stream to HTML 4.01. - /// - /// Source code stream. - /// A string containing the HTML formatted code. - public string FormatCode(Stream source) - { + /// Transform source code to HTML 4.01. + /// + /// + /// Transforms a source code stream to HTML 4.01. + /// + /// Source code stream. + /// A string containing the HTML formatted code. + public string FormatCode(Stream source) + { var reader = new StreamReader(source); - string s = reader.ReadToEnd(); - reader.Close(); - return FormatCode(s, _lineNumbers, _alternate, _embedStyleSheet, false); - } + string s = reader.ReadToEnd(); + reader.Close(); + return FormatCode(s, _lineNumbers, _alternate, _embedStyleSheet, false); + } - /// - /// Transforms a source code string to HTML 4.01. - /// - /// A string containing the HTML formatted code. - public string FormatCode(string source) - { - return FormatCode(source, _lineNumbers, _alternate, _embedStyleSheet, false); - } + /// + /// Transforms a source code string to HTML 4.01. + /// + /// A string containing the HTML formatted code. + public string FormatCode(string source) + { + return FormatCode(source, _lineNumbers, _alternate, _embedStyleSheet, false); + } - /// - /// Allows formatting a part of the code in a different language, - /// for example a JavaScript block inside an HTML file. - /// - public string FormatSubCode(string source) - { - return FormatCode(source, false, false, false, true); - } + /// + /// Allows formatting a part of the code in a different language, + /// for example a JavaScript block inside an HTML file. + /// + public string FormatSubCode(string source) + { + return FormatCode(source, false, false, false, true); + } - /// - /// Gets the CSS stylesheet as a stream. - /// - /// A text of the CSS definitions. - public static Stream GetCssStream() - { - return Assembly.GetExecutingAssembly().GetManifestResourceStream( - "Manoli.Utils.CSharpFormat.csharp.css"); - } + /// + /// Gets the CSS stylesheet as a stream. + /// + /// A text of the CSS definitions. + public static Stream GetCssStream() + { + return Assembly.GetExecutingAssembly().GetManifestResourceStream( + "Manoli.Utils.CSharpFormat.csharp.css"); + } - /// - /// Gets the CSS stylesheet as a string. - /// - /// A string containing the CSS definitions. - public static string GetCssString() - { + /// + /// Gets the CSS stylesheet as a string. + /// + /// A string containing the CSS definitions. + public static string GetCssString() + { var reader = new StreamReader(GetCssStream()); - return reader.ReadToEnd(); - } + return reader.ReadToEnd(); + } - private Regex codeRegex; + private Regex codeRegex; - /// - /// The regular expression used to capture language tokens. - /// - protected Regex CodeRegex - { - get { return codeRegex; } - set { codeRegex = value; } - } + /// + /// The regular expression used to capture language tokens. + /// + protected Regex CodeRegex + { + get => codeRegex; + set => codeRegex = value; + } - /// - /// Called to evaluate the HTML fragment corresponding to each - /// matching token in the code. - /// - /// The resulting from a - /// single regular expression match. - /// A string containing the HTML code fragment. - protected abstract string MatchEval(Match match); + /// + /// Called to evaluate the HTML fragment corresponding to each + /// matching token in the code. + /// + /// The resulting from a + /// single regular expression match. + /// A string containing the HTML code fragment. + protected abstract string MatchEval(Match match); - //does the formatting job - private string FormatCode(string source, bool lineNumbers, - bool alternate, bool embedStyleSheet, bool subCode) - { - //replace special characters + //does the formatting job + private string FormatCode(string source, bool lineNumbers, + bool alternate, bool embedStyleSheet, bool subCode) + { + //replace special characters var sb = new StringBuilder(source); - if(!subCode) - { - sb.Replace("&", "&"); - sb.Replace("<", "<"); - sb.Replace(">", ">"); - sb.Replace("\t", string.Empty.PadRight(_tabSpaces)); - } - - //color the code - source = codeRegex.Replace(sb.ToString(), new MatchEvaluator(this.MatchEval)); + if (!subCode) + { + sb.Replace("&", "&"); + sb.Replace("<", "<"); + sb.Replace(">", ">"); + sb.Replace("\t", string.Empty.PadRight(_tabSpaces)); + } - sb = new StringBuilder(); - - if (embedStyleSheet) - { - sb.Append("\n"); - } + //color the code + source = codeRegex.Replace(sb.ToString(), new MatchEvaluator(this.MatchEval)); - if (lineNumbers || alternate) //we have to process the code line by line - { - if(!subCode) - sb.Append("
\n"); + sb = new StringBuilder(); + + if (embedStyleSheet) + { + sb.Append("\n"); + } + + if (lineNumbers || alternate) //we have to process the code line by line + { + if (!subCode) + sb.Append("
\n"); var reader = new StringReader(source); - int i = 0; - string spaces = " "; - string line; - while ((line = reader.ReadLine()) != null) - { - i++; - if (alternate && ((i % 2) == 1)) - { - sb.Append("
");
-					}
-					else
-					{
-						sb.Append("
");
-					}
+                int i = 0;
+                string spaces = "    ";
+                string line;
+                while ((line = reader.ReadLine()) != null)
+                {
+                    i++;
+                    if (alternate && ((i % 2) == 1))
+                    {
+                        sb.Append("
");
+                    }
+                    else
+                    {
+                        sb.Append("
");
+                    }
+
+                    if (lineNumbers)
+                    {
+                        var order = (int)Math.Log10(i);
+                        sb.Append(""
+                            + spaces.Substring(0, 3 - order) + i.ToString()
+                            + ":  ");
+                    }
+
+                    if (line.Length == 0)
+                        sb.Append(" ");
+                    else
+                        sb.Append(line);
+                    sb.Append("
\n"); + } + reader.Close(); + if (!subCode) + sb.Append("
"); + } + else + { + //have to use a
 because IE below ver 6 does not understand 
+                //the "white-space: pre" CSS value
+                if (!subCode)
+                    sb.Append("
\n");
+                sb.Append(source);
+                if (!subCode)
+                    sb.Append("
"); + } - if(lineNumbers) - { - var order = (int)Math.Log10(i); - sb.Append("" - + spaces.Substring(0, 3 - order) + i.ToString() - + ": "); - } - - if(line.Length == 0) - sb.Append(" "); - else - sb.Append(line); - sb.Append("
\n"); - } - reader.Close(); - if(!subCode) - sb.Append("
"); - } - else - { - //have to use a
 because IE below ver 6 does not understand 
-				//the "white-space: pre" CSS value
-				if(!subCode)
-					sb.Append("
\n");
-				sb.Append(source);
-				if(!subCode)
-					sb.Append("
"); - } - - return sb.ToString(); - } + return sb.ToString(); + } - } + } } diff --git a/src/Libraries/SmartStore.Core/Html/CodeFormatter/TsqlFormat.cs b/src/Libraries/SmartStore.Core/Html/CodeFormatter/TsqlFormat.cs index 74f58beed1..6aee1b6e6a 100644 --- a/src/Libraries/SmartStore.Core/Html/CodeFormatter/TsqlFormat.cs +++ b/src/Libraries/SmartStore.Core/Html/CodeFormatter/TsqlFormat.cs @@ -19,127 +19,100 @@ * be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. - */ + */ #endregion namespace SmartStore.Core.Html.CodeFormatter { - /// - /// Generates color-coded T-SQL source code. - /// + /// + /// Generates color-coded T-SQL source code. + /// public partial class TsqlFormat : CodeFormat - { - /// - /// Regular expression string to match single line - /// comments (--). - /// - protected override string CommentRegex - { - get - { - return @"(?:--\s).*?(?=\r|\n)"; - } - } + { + /// + /// Regular expression string to match single line + /// comments (--). + /// + protected override string CommentRegex => @"(?:--\s).*?(?=\r|\n)"; - /// - /// Regular expression string to match string literals. - /// - protected override string StringRegex - { - get - { - return @"''|'.*?'"; - } - } + /// + /// Regular expression string to match string literals. + /// + protected override string StringRegex => @"''|'.*?'"; - /// - /// Returns false, since T-SQL is not case sensitive. - /// - public override bool CaseSensitive - { - get { return false; } - } + /// + /// Returns false, since T-SQL is not case sensitive. + /// + public override bool CaseSensitive => false; - /// - /// The list of T-SQL keywords. - /// - protected override string Keywords - { - get - { - return "absolute action ada add admin after aggregate " - + "alias all allocate alter and any are array as asc " - + "assertion at authorization avg backup before begin " - + "between binary bit bit_length blob boolean both breadth " - + "break browse bulk by call cascade cascaded case cast " - + "catalog char char_length character character_length " - + "check checkpoint class clob close clustered coalesce " - + "collate collation column commit completion compute " - + "connect connection constraint constraints constructor " - + "contains containstable continue convert corresponding " - + "count create cross cube current current_date current_path " - + "current_role current_time current_timestamp current_user " - + "cursor cycle data database date day dbcc deallocate dec " - + "decimal declare default deferrable deferred delete deny " - + "depth deref desc describe descriptor destroy destructor " - + "deterministic diagnostics dictionary disconnect disk " - + "distinct distributed domain double drop dummy dump " - + "dynamic each else end end-exec equals errlvl escape " - + "every except exception exec execute exists exit external " - + "extract false fetch file fillfactor first float for " - + "foreign fortran found free freetext freetexttable from " - + "full function general get global go goto grant group " - + "grouping having holdlock host hour identity identity_insert " - + "identitycol if ignore immediate in include index indicator " - + "initialize initially inner inout input insensitive insert " - + "int integer intersect interval into is isolation iterate " - + "join key kill language large last lateral leading left " - + "less level like limit lineno load local localtime localtimestamp " - + "locator lower map match max min minute modifies modify " - + "module month names national natural nchar nclob new next " - + "no nocheck nonclustered none not null nullif numeric object " - + "octet_length of off offsets old on only open opendatasource " - + "openquery openrowset openxml operation option or order " - + "ordinality out outer output over overlaps pad parameter " - + "parameters partial pascal path percent plan position " - + "postfix precision prefix preorder prepare preserve " - + "primary print prior privileges proc procedure " - + "public raiserror read reads readtext real reconfigure " - + "recursive ref references referencing relative replication " - + "restore restrict result return returns revoke right role " - + "rollback rollup routine row rowcount rowguidcol rows rule " - + "save savepoint schema scope scroll search second section " - + "select sequence session session_user set sets setuser " - + "shutdown size smallint some space specific specifictype " - + "sql sqlca sqlcode sqlerror sqlexception sqlstate sqlwarning " - + "start state statement static statistics structure substring " - + "sum system_user table temporary terminate textsize than then " - + "time timestamp timezone_hour timezone_minute to top trailing " - + "tran transaction translate translation treat trigger trim " - + "true truncate tsequal under union unique unknown unnest " - + "update updatetext upper usage use user using value values " - + "varchar variable varying view waitfor when whenever where " - + "while with without work write writetext year zone"; - } - } + /// + /// The list of T-SQL keywords. + /// + protected override string Keywords => "absolute action ada add admin after aggregate " + + "alias all allocate alter and any are array as asc " + + "assertion at authorization avg backup before begin " + + "between binary bit bit_length blob boolean both breadth " + + "break browse bulk by call cascade cascaded case cast " + + "catalog char char_length character character_length " + + "check checkpoint class clob close clustered coalesce " + + "collate collation column commit completion compute " + + "connect connection constraint constraints constructor " + + "contains containstable continue convert corresponding " + + "count create cross cube current current_date current_path " + + "current_role current_time current_timestamp current_user " + + "cursor cycle data database date day dbcc deallocate dec " + + "decimal declare default deferrable deferred delete deny " + + "depth deref desc describe descriptor destroy destructor " + + "deterministic diagnostics dictionary disconnect disk " + + "distinct distributed domain double drop dummy dump " + + "dynamic each else end end-exec equals errlvl escape " + + "every except exception exec execute exists exit external " + + "extract false fetch file fillfactor first float for " + + "foreign fortran found free freetext freetexttable from " + + "full function general get global go goto grant group " + + "grouping having holdlock host hour identity identity_insert " + + "identitycol if ignore immediate in include index indicator " + + "initialize initially inner inout input insensitive insert " + + "int integer intersect interval into is isolation iterate " + + "join key kill language large last lateral leading left " + + "less level like limit lineno load local localtime localtimestamp " + + "locator lower map match max min minute modifies modify " + + "module month names national natural nchar nclob new next " + + "no nocheck nonclustered none not null nullif numeric object " + + "octet_length of off offsets old on only open opendatasource " + + "openquery openrowset openxml operation option or order " + + "ordinality out outer output over overlaps pad parameter " + + "parameters partial pascal path percent plan position " + + "postfix precision prefix preorder prepare preserve " + + "primary print prior privileges proc procedure " + + "public raiserror read reads readtext real reconfigure " + + "recursive ref references referencing relative replication " + + "restore restrict result return returns revoke right role " + + "rollback rollup routine row rowcount rowguidcol rows rule " + + "save savepoint schema scope scroll search second section " + + "select sequence session session_user set sets setuser " + + "shutdown size smallint some space specific specifictype " + + "sql sqlca sqlcode sqlerror sqlexception sqlstate sqlwarning " + + "start state statement static statistics structure substring " + + "sum system_user table temporary terminate textsize than then " + + "time timestamp timezone_hour timezone_minute to top trailing " + + "tran transaction translate translation treat trigger trim " + + "true truncate tsequal under union unique unknown unnest " + + "update updatetext upper usage use user using value values " + + "varchar variable varying view waitfor when whenever where " + + "while with without work write writetext year zone"; - /// - /// Use the pre-processor color to mark keywords that start with @@. - /// - protected override string Preprocessors - { - get - { - return @"@@CONNECTIONS @@CPU_BUSY @@CURSOR_ROWS @@DATEFIRST " - + "@@DBTS @@ERROR @@FETCH_STATUS @@IDENTITY @@IDLE " - + "@@IO_BUSY @@LANGID @@LANGUAGE @@LOCK_TIMEOUT " - + "@@MAX_CONNECTIONS @@MAX_PRECISION @@NESTLEVEL @@OPTIONS " - + "@@PACK_RECEIVED @@PACK_SENT @@PACKET_ERRORS @@PROCID " - + "@@REMSERVER @@ROWCOUNT @@SERVERNAME @@SERVICENAME @@SPID " - + "@@TEXTSIZE @@TIMETICKS @@TOTAL_ERRORS @@TOTAL_READ " - + "@@TOTAL_WRITE @@TRANCOUNT @@VERSION"; - } - } - } + /// + /// Use the pre-processor color to mark keywords that start with @@. + /// + protected override string Preprocessors => @"@@CONNECTIONS @@CPU_BUSY @@CURSOR_ROWS @@DATEFIRST " + + "@@DBTS @@ERROR @@FETCH_STATUS @@IDENTITY @@IDLE " + + "@@IO_BUSY @@LANGID @@LANGUAGE @@LOCK_TIMEOUT " + + "@@MAX_CONNECTIONS @@MAX_PRECISION @@NESTLEVEL @@OPTIONS " + + "@@PACK_RECEIVED @@PACK_SENT @@PACKET_ERRORS @@PROCID " + + "@@REMSERVER @@ROWCOUNT @@SERVERNAME @@SERVICENAME @@SPID " + + "@@TEXTSIZE @@TIMETICKS @@TOTAL_ERRORS @@TOTAL_READ " + + "@@TOTAL_WRITE @@TRANCOUNT @@VERSION"; + } } diff --git a/src/Libraries/SmartStore.Core/Html/CodeFormatter/VisualBasicFormat.cs b/src/Libraries/SmartStore.Core/Html/CodeFormatter/VisualBasicFormat.cs index 36fa1da139..b4a28e8503 100644 --- a/src/Libraries/SmartStore.Core/Html/CodeFormatter/VisualBasicFormat.cs +++ b/src/Libraries/SmartStore.Core/Html/CodeFormatter/VisualBasicFormat.cs @@ -17,80 +17,59 @@ * be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. - */ + */ #endregion namespace SmartStore.Core.Html.CodeFormatter { - /// - /// Generates color-coded HTML 4.01 from Visual Basic source code. - /// + /// + /// Generates color-coded HTML 4.01 from Visual Basic source code. + /// public partial class VisualBasicFormat : CodeFormat - { - /// - /// Determines if the language is case sensitive. - /// - /// Always true, since VB is not case sensitive. - public override bool CaseSensitive - { - get { return false; } - } + { + /// + /// Determines if the language is case sensitive. + /// + /// Always true, since VB is not case sensitive. + public override bool CaseSensitive => false; - /// - /// Regular expression string to match comments (' and REM). - /// - protected override string CommentRegex - { - get { return @"(?:'|REM\s).*?(?=\r|\n)"; } - } + /// + /// Regular expression string to match comments (' and REM). + /// + protected override string CommentRegex => @"(?:'|REM\s).*?(?=\r|\n)"; - /// - /// Regular expression string to match string and character literals. - /// - protected override string StringRegex - { - get { return @"""""|"".*?"""; } - } + /// + /// Regular expression string to match string and character literals. + /// + protected override string StringRegex => @"""""|"".*?"""; - /// - /// The list of VB keywords. - /// - protected override string Keywords - { - get - { - return "AddHandler AddressOf AndAlso Alias And Ansi As Assembly " - + "Auto Boolean ByRef Byte ByVal Call Case Catch " - + "CBool CByte CChar CDate CDec CDbl Char CInt " - + "Class CLng CObj Const CShort CSng CStr CType " - + "Date Decimal Declare Default Delegate Dim DirectCast Do " - + "Double Each Else ElseIf End EndIf Enum Erase Error " - + "Event Exit False Finally For Friend Function Get " - + "GetType Global GoSub GoTo Handles If Implements Imports In Inherits " - + "Integer Interface Is Let Lib Like Long Loop " - + "Me Mod Module MustInherit MustOverride MyBase MyClass Namespace " - + "Narrowing New Next Not Nothing NotInheritable NotOverridable Object On " - + "Operator Option Optional Or OrElse Overloads Overridable Overrides ParamArray " - + "Preserve Private Property Protected Public RaiseEvent ReadOnly ReDim " - + "REM RemoveHandler Resume Return Select Set Shadows Shared " - + "Short Single Static Step Stop String Structure Sub " - + "SyncLock Then Throw To True Try TryCast TypeOf Unicode " - + "Until Variant Wend When While With Widening WithEvents WriteOnly Xor"; - } - } + /// + /// The list of VB keywords. + /// + protected override string Keywords => "AddHandler AddressOf AndAlso Alias And Ansi As Assembly " + + "Auto Boolean ByRef Byte ByVal Call Case Catch " + + "CBool CByte CChar CDate CDec CDbl Char CInt " + + "Class CLng CObj Const CShort CSng CStr CType " + + "Date Decimal Declare Default Delegate Dim DirectCast Do " + + "Double Each Else ElseIf End EndIf Enum Erase Error " + + "Event Exit False Finally For Friend Function Get " + + "GetType Global GoSub GoTo Handles If Implements Imports In Inherits " + + "Integer Interface Is Let Lib Like Long Loop " + + "Me Mod Module MustInherit MustOverride MyBase MyClass Namespace " + + "Narrowing New Next Not Nothing NotInheritable NotOverridable Object On " + + "Operator Option Optional Or OrElse Overloads Overridable Overrides ParamArray " + + "Preserve Private Property Protected Public RaiseEvent ReadOnly ReDim " + + "REM RemoveHandler Resume Return Select Set Shadows Shared " + + "Short Single Static Step Stop String Structure Sub " + + "SyncLock Then Throw To True Try TryCast TypeOf Unicode " + + "Until Variant Wend When While With Widening WithEvents WriteOnly Xor"; - /// - /// The list of VB preprocessors. - /// - protected override string Preprocessors - { - get - { - return @"#\s*Const #\s*If #\s*Else #\s*ElseIf #\s*End\s+If " - + @"#\s*ExternalSource #\s*End\s+ExternalSource " - + @"#\s*Region #\s*End\s+Region"; - } - } - } + /// + /// The list of VB preprocessors. + /// + protected override string Preprocessors => @"#\s*Const #\s*If #\s*Else #\s*ElseIf #\s*End\s+If " + + @"#\s*ExternalSource #\s*End\s+ExternalSource " + + @"#\s*Region #\s*End\s+Region"; + } } diff --git a/src/Libraries/SmartStore.Core/Html/HtmlUtils.cs b/src/Libraries/SmartStore.Core/Html/HtmlUtils.cs index f3239a1b15..2428d3c4aa 100644 --- a/src/Libraries/SmartStore.Core/Html/HtmlUtils.cs +++ b/src/Libraries/SmartStore.Core/Html/HtmlUtils.cs @@ -1,15 +1,15 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Web; -using System.Linq; using AngleSharp; -using AngleSharp.Parser.Html; -using AngleSharp.Parser.Css; -using System.Collections.Generic; using AngleSharp.Dom; -using AngleSharp.Dom.Html; +using AngleSharp.Extensions; +using AngleSharp.Parser.Html; using Ganss.XSS; +using SmartStore.Utilities.ObjectPools; namespace SmartStore.Core.Html { @@ -18,96 +18,96 @@ namespace SmartStore.Core.Html ///
public partial class HtmlUtils { - private readonly static Regex _paragraphStartRegex = new Regex("

", RegexOptions.IgnoreCase); + private readonly static Regex _paragraphStartRegex = new Regex("

", RegexOptions.IgnoreCase); private readonly static Regex _paragraphEndRegex = new Regex("

", RegexOptions.IgnoreCase); - //private static Regex ampRegex = new Regex("&(?!(?:#[0-9]{2,4};|[a-z0-9]+;))", RegexOptions.Compiled | RegexOptions.IgnoreCase); - - /// - /// Formats the text - /// - /// Text - /// A value indicating whether to strip tags - /// A value indicating whether HTML is allowed - /// A value indicating whether HTML is allowed - /// A value indicating whether BBCode is allowed - /// A value indicating whether to resolve links - /// A value indicating whether to add "noFollow" tag - /// Formatted text - [Obsolete("Use specific formatter methods instead, e.g. StripTags(), SanitizeHtml() etc.")] - public static string FormatText( - string text, - bool stripTags, - bool convertPlainTextToHtml, - bool allowHtml, - bool allowBBCode, - bool resolveLinks, - bool addNoFollowTag) - { - - if (String.IsNullOrEmpty(text)) - return string.Empty; - - try - { - if (stripTags) - { - text = StripTags(text); - } - - if (allowHtml) - { - text = SanitizeHtml(text); - } - else - { - text = HttpUtility.HtmlEncode(text); - } - - if (convertPlainTextToHtml) - { - text = ConvertPlainTextToHtml(text); - } - - if (allowBBCode) - { - text = BBCodeHelper.ToHtml(text, true, true, true, true, true, true); - } - - if (resolveLinks) - { - text = ResolveLinksHelper.FormatText(text); - } - - if (addNoFollowTag) - { - //add noFollow tag. not implemented - } - } - catch (Exception exc) - { - text = string.Format("Text cannot be formatted. Error: {0}", exc.Message); - } - - return text; - } - - public static string SanitizeHtml(string html, bool isFragment = true) + //private static Regex ampRegex = new Regex("&(?!(?:#[0-9]{2,4};|[a-z0-9]+;))", RegexOptions.Compiled | RegexOptions.IgnoreCase); + + /// + /// Formats the text + /// + /// Text + /// A value indicating whether to strip tags + /// A value indicating whether HTML is allowed + /// A value indicating whether HTML is allowed + /// A value indicating whether BBCode is allowed + /// A value indicating whether to resolve links + /// A value indicating whether to add "noFollow" tag + /// Formatted text + [Obsolete("Use specific formatter methods instead, e.g. StripTags(), SanitizeHtml() etc.")] + public static string FormatText( + string text, + bool stripTags, + bool convertPlainTextToHtml, + bool allowHtml, + bool allowBBCode, + bool resolveLinks, + bool addNoFollowTag) + { + + if (String.IsNullOrEmpty(text)) + return string.Empty; + + try + { + if (stripTags) + { + text = StripTags(text); + } + + if (allowHtml) + { + text = SanitizeHtml(text); + } + else + { + text = HttpUtility.HtmlEncode(text); + } + + if (convertPlainTextToHtml) + { + text = ConvertPlainTextToHtml(text); + } + + if (allowBBCode) + { + text = BBCodeHelper.ToHtml(text, true, true, true, true, true, true); + } + + if (resolveLinks) + { + text = ResolveLinksHelper.FormatText(text); + } + + if (addNoFollowTag) + { + //add noFollow tag. not implemented + } + } + catch (Exception exc) + { + text = string.Format("Text cannot be formatted. Error: {0}", exc.Message); + } + + return text; + } + + public static string SanitizeHtml(string html, bool isFragment = true) { if (string.IsNullOrEmpty(html)) return string.Empty; - var sanitizer = new HtmlSanitizer(); + var sanitizer = new HtmlSanitizer(); - if (isFragment) - { - return sanitizer.Sanitize(html); - } - else - { - return sanitizer.SanitizeDocument(html); - } + if (isFragment) + { + return sanitizer.Sanitize(html); + } + else + { + return sanitizer.SanitizeDocument(html); + } } - + /// /// Strips tags /// @@ -115,27 +115,89 @@ public static string SanitizeHtml(string html, bool isFragment = true) /// Formatted text public static string StripTags(string html) { - if (html.IsEmpty()) - return string.Empty; + if (html.IsEmpty()) + return string.Empty; + + var removeTags = new HashSet(StringComparer.OrdinalIgnoreCase) { "script", "style", "svg", "img" }; + var parser = new HtmlParser(); - var ignoreTags = new HashSet(StringComparer.OrdinalIgnoreCase) { "script", "style", "svg", "img" }; - var parser = new HtmlParser(); - var doc = parser.Parse(html); + using (var doc = parser.Parse(html)) + { + List removeElements = new List(); + + foreach (var el in doc.All) + { + if (removeTags.Contains(el.TagName)) + { + removeElements.Add(el); + } + } - List removeElements = new List(); + foreach (var el in removeElements) + { + el.Remove(); + } - foreach (var ignoreTag in ignoreTags) - { - removeElements.AddRange(doc.QuerySelectorAll(ignoreTag)); - } + return doc.Body.TextContent; + } + } - foreach (var el in removeElements) - { - el.Remove(); - } + /// + /// Checks whether HTML code only contains whitespace stuff ( 

]]>) + ///
+ public static bool IsEmptyHtml(string html) + { + if (html.IsEmpty()) + { + return true; + } + + if (html.Length > 500) + { + // (perf) we simply assume content if length is larger + return false; + } + + var parser = new HtmlParser(); + using (var doc = parser.Parse(html)) + { + foreach (var el in doc.All) + { + switch (el.TagName.ToLower()) + { + case "html": + case "head": + case "br": + continue; + case "body": + if (el.ChildElementCount > 0) + { + continue; + } + else + { + return el.Text().Trim().IsEmpty(); + } + case "p": + case "div": + case "span": + var text = el.Text().Trim(); + if (text.IsEmpty() || text == " ") + { + continue; + } + else + { + return false; + } + default: + return false; + } + } - return doc.Body.TextContent; - } + return true; + } + } /// /// Replace anchor text (remove a tag from the following url Name and output only the string "Name") @@ -169,7 +231,7 @@ public static string ConvertPlainTextToHtml(string text) return text; } - + /// /// Converts HTML to plain text /// @@ -217,32 +279,33 @@ public static string ConvertPlainTextToTable(string text, string tableCssClass = return string.Empty; } - var builder = new StringBuilder(); + var psb = PooledStringBuilder.Rent(); + var builder = (StringBuilder)psb; builder.AppendFormat("", tableCssClass.HasValue() ? "class='" + tableCssClass + "'" : ""); - lines.Where(x => x.HasValue()).Each(x => - { - builder.Append(""); - var tokens = x.Split(new char[] { ':' }, 2); - - if (tokens.Length > 1) - { - builder.AppendFormat("{0}", tokens[0]); - builder.AppendFormat("{0}", tokens[1]); - } - else - { - builder.Append(" "); - builder.AppendFormat("{0}",tokens[0]); - } - - builder.Append(""); - }); + lines.Where(x => x.HasValue()).Each(x => + { + builder.Append(""); + var tokens = x.Split(new char[] { ':' }, 2); + + if (tokens.Length > 1) + { + builder.AppendFormat("{0}", tokens[0]); + builder.AppendFormat("{0}", tokens[1]); + } + else + { + builder.Append(" "); + builder.AppendFormat("{0}", tokens[0]); + } + + builder.Append(""); + }); builder.Append(""); - return builder.ToString(); + return psb.ToStringAndReturn(); } /// @@ -273,35 +336,35 @@ public static string ConvertPlainTextToParagraph(string text) return builder.ToString(); } - /// - /// Converts all occurences of pixel-based inline font-size expression to relative 'em' - /// - /// - /// - /// - public static string RelativizeFontSizes(string html, int baseFontSizePx = 16) - { - Guard.NotEmpty(html, nameof(html)); - Guard.IsPositive(baseFontSizePx, nameof(baseFontSizePx)); - - var parser = new HtmlParser(new AngleSharp.Configuration().WithCss()); - var doc = parser.Parse(html); - - var nodes = doc.QuerySelectorAll("*[style]"); - foreach (var node in nodes) - { - if (node.Style.FontSize is string s && s.EndsWith("px")) - { - var size = s.Substring(0, s.Length - 2).Convert(); - if (size > 0) - { - //node.Style.FontSize = Math.Round(((double)size / (double)baseFontSizePx), 4) + "em"; - node.Style.FontSize = "{0}em".FormatInvariant(Math.Round(((double)size / (double)baseFontSizePx), 4)); - } - } - } - - return doc.Body.InnerHtml; - } + /// + /// Converts all occurences of pixel-based inline font-size expression to relative 'em' + /// + /// + /// + /// + public static string RelativizeFontSizes(string html, int baseFontSizePx = 16) + { + Guard.NotEmpty(html, nameof(html)); + Guard.IsPositive(baseFontSizePx, nameof(baseFontSizePx)); + + var parser = new HtmlParser(new AngleSharp.Configuration().WithCss()); + var doc = parser.Parse(html); + + var nodes = doc.QuerySelectorAll("*[style]"); + foreach (var node in nodes) + { + if (node.Style.FontSize is string s && s.EndsWith("px")) + { + var size = s.Substring(0, s.Length - 2).Convert(); + if (size > 0) + { + //node.Style.FontSize = Math.Round(((double)size / (double)baseFontSizePx), 4) + "em"; + node.Style.FontSize = "{0}em".FormatInvariant(Math.Round(((double)size / (double)baseFontSizePx), 4)); + } + } + } + + return doc.Body.InnerHtml; + } } } diff --git a/src/Libraries/SmartStore.Core/HttpSecurityMode.cs b/src/Libraries/SmartStore.Core/HttpSecurityMode.cs index 5317183015..f159ce2466 100644 --- a/src/Libraries/SmartStore.Core/HttpSecurityMode.cs +++ b/src/Libraries/SmartStore.Core/HttpSecurityMode.cs @@ -1,6 +1,4 @@ -using System; - -namespace SmartStore.Core +namespace SmartStore.Core { public enum HttpSecurityMode { diff --git a/src/Libraries/SmartStore.Core/IActivatable.cs b/src/Libraries/SmartStore.Core/IActivatable.cs index b42cb554fc..48f59e9969 100644 --- a/src/Libraries/SmartStore.Core/IActivatable.cs +++ b/src/Libraries/SmartStore.Core/IActivatable.cs @@ -1,9 +1,7 @@ -using System; - -namespace SmartStore.Core +namespace SmartStore.Core { - public interface IActivatable - { - bool IsActive { get; } - } + public interface IActivatable + { + bool IsActive { get; } + } } diff --git a/src/Libraries/SmartStore.Core/IAuditable.cs b/src/Libraries/SmartStore.Core/IAuditable.cs index 60f4342efc..cb6c94ac78 100644 --- a/src/Libraries/SmartStore.Core/IAuditable.cs +++ b/src/Libraries/SmartStore.Core/IAuditable.cs @@ -2,16 +2,16 @@ namespace SmartStore.Core { - public interface IAuditable - { - /// - /// Gets or sets the date and time of entity creation - /// - DateTime CreatedOnUtc { get; set; } + public interface IAuditable + { + /// + /// Gets or sets the date and time of entity creation + /// + DateTime CreatedOnUtc { get; set; } - /// - /// Gets or sets the date and time of entity update - /// - DateTime UpdatedOnUtc { get; set; } - } + /// + /// Gets or sets the date and time of entity update + /// + DateTime UpdatedOnUtc { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/IMergedData.cs b/src/Libraries/SmartStore.Core/IMergedData.cs index 8233a55987..2cf39de6dd 100644 --- a/src/Libraries/SmartStore.Core/IMergedData.cs +++ b/src/Libraries/SmartStore.Core/IMergedData.cs @@ -5,43 +5,43 @@ namespace SmartStore.Core { - public interface IMergedData - { - bool MergedDataIgnore { get; set; } - Dictionary MergedDataValues { get; } - } + public interface IMergedData + { + bool MergedDataIgnore { get; set; } + Dictionary MergedDataValues { get; } + } - public static class IMergedDataExtensions - { - public static T GetMergedDataValue(this IMergedData mergedData, string key, T defaultValue) - { - if (mergedData.MergedDataValues == null) - return defaultValue; + public static class IMergedDataExtensions + { + public static T GetMergedDataValue(this IMergedData mergedData, string key, T defaultValue) + { + if (mergedData.MergedDataValues == null) + return defaultValue; - if (mergedData.MergedDataIgnore) - return defaultValue; + if (mergedData.MergedDataIgnore) + return defaultValue; - if (mergedData is BaseEntity && HostingEnvironment.IsHosted) - { - // This is absolutely bad coding! But I don't see any alternatives. - // When the passed object is a (EF)-trackable entity, - // we cannot return the merged value while EF performs - // change detection, because entity properties could be set to modified, - // where in fact nothing has changed. - var dbContext = EngineContext.Current.Resolve(); - if (dbContext.IsDetectingChanges()) - { - return defaultValue; - } - } + if (mergedData is BaseEntity && HostingEnvironment.IsHosted) + { + // This is absolutely bad coding! But I don't see any alternatives. + // When the passed object is a (EF)-trackable entity, + // we cannot return the merged value while EF performs + // change detection, because entity properties could be set to modified, + // where in fact nothing has changed. + var dbContext = EngineContext.Current.Resolve(); + if (dbContext.IsDetectingChanges()) + { + return defaultValue; + } + } - object value; - if (mergedData.MergedDataValues.TryGetValue(key, out value)) - { - return (T)value; - } + object value; + if (mergedData.MergedDataValues.TryGetValue(key, out value)) + { + return (T)value; + } - return defaultValue; - } - } + return defaultValue; + } + } } diff --git a/src/Libraries/SmartStore.Core/IO/DirectoryHasher.cs b/src/Libraries/SmartStore.Core/IO/DirectoryHasher.cs new file mode 100644 index 0000000000..91e96b0ed5 --- /dev/null +++ b/src/Libraries/SmartStore.Core/IO/DirectoryHasher.cs @@ -0,0 +1,154 @@ +using System; +using System.Globalization; +using System.IO; +using System.Text; +using System.Web.Hosting; +using SmartStore.Core.Data; +using SmartStore.Utilities; + +namespace SmartStore.Core.IO +{ + public class DirectoryHasher + { + private static readonly string _appRootPath; + private static readonly string _defaultStoragePath; + + private readonly string _path; + private readonly string _searchPattern; + private readonly bool _deep; + private readonly string _storagePath; + + private int? _lastHash; + private int? _currentHash; + private string _lookupKey; + + static DirectoryHasher() + { + _appRootPath = HostingEnvironment.ApplicationPhysicalPath; + _defaultStoragePath = Path.Combine(CommonHelper.MapPath(DataSettings.Current.TenantPath), "Hash"); + Directory.CreateDirectory(_defaultStoragePath); + } + + public DirectoryHasher(string path) + : this(path, "*", false, null) + { + } + + public DirectoryHasher(string path, string searchPattern, bool deep = false, string storagePath = null) + { + Guard.NotEmpty(path, nameof(path)); + + if (!path.StartsWith(_appRootPath, StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException($"Path must be contained within current app root path '{_appRootPath}'", nameof(path)); + } + + _path = path; + _searchPattern = searchPattern ?? "*"; + _deep = deep; + _storagePath = storagePath ?? _defaultStoragePath; + } + + public bool HasChanged => LastHash != CurrentHash; + + public int? LastHash + { + get + { + if (_lastHash == null) + { + _lastHash = ReadLastHash(); + } + + return _lastHash == -1 ? (int?)null : _lastHash.Value; + } + } + + public int CurrentHash => (_currentHash ?? (_currentHash = ComputeHash())).Value; + + public string LookupKey => _lookupKey ?? (_lookupKey = BuildLookupKey()); + + public void Refresh() + { + _currentHash = null; + } + + //public void Reset() + //{ + // _lastHash = -1; + + // var path = Path.Combine(_storagePath, LookupKey + ".hash"); + // if (File.Exists(path)) + // { + // File.Delete(path); + // } + //} + + public void Persist() + { + if (LastHash == CurrentHash) + return; + + var path = Path.Combine(_storagePath, LookupKey + ".hash"); + File.WriteAllText(path, CurrentHash.ToString(CultureInfo.InvariantCulture), Encoding.UTF8); + _lastHash = CurrentHash; + } + + protected virtual int ComputeHash() + { + var hash = 0; + var di = new DirectoryInfo(_path); + + if (di.Exists) + { + var files = di.GetFiles(_searchPattern, _deep ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); + if (files.Length > 0) + { + // Calculate current hash + var hashCombiner = new HashCodeCombiner(); + foreach (var file in files) + { + hashCombiner.Add(file); + } + + hash = hashCombiner.CombinedHash; + } + } + + return hash; + } + + protected virtual int ReadLastHash() + { + var path = Path.Combine(_storagePath, LookupKey + ".hash"); + var hash = File.Exists(path) + ? ConvertHash(File.ReadAllText(path, Encoding.UTF8)) + : -1; + + return hash; + } + + protected virtual string BuildLookupKey() + { + var key = PathHelper.MakeRelativePath(_appRootPath, _path, "_").ToLower(); + + if (_deep) + key += "_d"; + + if (_searchPattern.HasValue() && _searchPattern != "*") + key += "_" + _searchPattern.ToLower().ToValidFileName("x"); + + return key.Trim(new char[] { '/', '\\' }); + } + + private static int ConvertHash(string val) + { + if (Int32.TryParse(val, NumberStyles.Integer, CultureInfo.InvariantCulture, out var outVal)) + { + return outVal; + } + + return 0; + } + } +} diff --git a/src/Libraries/SmartStore.Core/IO/IFile.cs b/src/Libraries/SmartStore.Core/IO/IFile.cs index 6d86299fc8..ca64f714e6 100644 --- a/src/Libraries/SmartStore.Core/IO/IFile.cs +++ b/src/Libraries/SmartStore.Core/IO/IFile.cs @@ -7,66 +7,66 @@ namespace SmartStore.Core.IO { public interface IFile { - /// - /// The path relative to the storage root - /// - string Path { get; } + /// + /// The path relative to the storage root + /// + string Path { get; } - /// - /// The path without the file part, but with trailing slash - /// - string Directory { get; } + /// + /// The path without the file part, but with trailing slash + /// + string Directory { get; } - /// - /// File name including extension - /// - string Name { get; } + /// + /// File name including extension + /// + string Name { get; } - /// - /// File name excluding extension - /// - string Title { get; } + /// + /// File name excluding extension + /// + string Title { get; } - /// - /// Size in bytes - /// - long Size { get; } + /// + /// Size in bytes + /// + long Size { get; } - /// - /// Expressed as UTC time - /// - DateTime LastUpdated { get; } + /// + /// Expressed as UTC time + /// + DateTime LastUpdated { get; } - /// - /// File extension including dot - /// - string Extension { get; } + /// + /// File extension including dot + /// + string Extension { get; } - /// - /// Dimensions, if the file is an image. - /// - Size Dimensions { get; } + /// + /// Dimensions, if the file is an image. + /// + Size Dimensions { get; } - bool Exists { get; } + bool Exists { get; } - /// - /// Creates a stream for reading from the file. - /// - Stream OpenRead(); + /// + /// Creates a stream for reading from the file. + /// + Stream OpenRead(); /// /// Creates a stream for writing to the file. /// Stream OpenWrite(); - /// - /// Creates a stream for writing to the file, and truncates the existing content. - /// - Stream CreateFile(); + /// + /// Creates a stream for writing to the file, and truncates the existing content. + /// + Stream CreateFile(); - /// - /// Asynchronously creates a stream for writing to the file, and truncates the existing content. - /// - Task CreateFileAsync(); - } + /// + /// Asynchronously creates a stream for writing to the file, and truncates the existing content. + /// + Task CreateFileAsync(); + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/IO/IFileSystem.cs b/src/Libraries/SmartStore.Core/IO/IFileSystem.cs index c90b886ffb..74598c9391 100644 --- a/src/Libraries/SmartStore.Core/IO/IFileSystem.cs +++ b/src/Libraries/SmartStore.Core/IO/IFileSystem.cs @@ -7,97 +7,108 @@ namespace SmartStore.Core.IO { public interface IFileSystem { - /// - /// Checks whether the underlying storage is remote, like 'Azure' for example. - /// - bool IsCloudStorage { get; } - - /// - /// Gets the root path - /// - string Root { get; } - - /// - /// Retrieves the public URL for a given file within the storage provider. - /// - /// The relative path within the storage provider. - /// - /// If true and the storage is in the cloud, returns the actual remote cloud URL to the resource. - /// If false, retrieves an app relative URL to delegate further processing to the media middleware (which can handle remote files) - /// - /// The public URL. - string GetPublicUrl(string path, bool forCloud = false); - - /// - /// Retrieves the path within the storage provider for a given public url. - /// - /// The virtual or public url of a file. - /// The storage path or null if the media is not in a correct format. - string GetStoragePath(string url); - - /// - /// Checks if the given file exists within the storage provider. - /// - /// The relative path within the storage provider. - /// True if the file exists; False otherwise. - bool FileExists(string path); - - /// - /// Checks if the given folder exists within the storage provider. - /// - /// The relative path within the storage provider. - /// True if the folder exists; False otherwise. - bool FolderExists(string path); - - /// - /// Retrieves a file within the storage provider. - /// - /// The relative path to the file within the storage provider. - /// The file. - /// If the file is not found. - IFile GetFile(string path); - - /// - /// Retrieves a folder within the storage provider. - /// - /// The relative path to the folder within the storage provider. - /// The folder. - /// If the folder is not found. - IFolder GetFolder(string path); - - /// - /// Retrieves a folder for file path within the storage provider. - /// - /// The relative path to the file within the storage provider. - /// The folder for the file. - /// If the file or the folder is not found. - IFolder GetFolderForFile(string path); - - /// - /// Retrieves the count of files within a path. - /// - /// The relative path to the folder in which to retrieve file count. - /// The file pattern to match - /// Optional. Files matching the predicate are excluded. - /// Whether to count files in all subfolders also - /// Total count of files. - long CountFiles(string path, string pattern, Func predicate, bool deep = true); - - /// - /// Performs a deep search for files within a path. - /// - /// The relative path to the folder in which to process file search. - /// The file pattern to match - /// Whether to search in all subfolders also - /// Matching file names - IEnumerable SearchFiles(string path, string pattern, bool deep = true); - - /// - /// Lists the files within a storage provider's path. - /// - /// The relative path to the folder which files to list. - /// The list of files in the folder. - IEnumerable ListFiles(string path); + /// + /// Checks whether the underlying storage is remote, like 'Azure' for example. + /// + bool IsCloudStorage { get; } + + /// + /// Gets the root path + /// + string Root { get; } + + /// + /// Retrieves the public URL for a given file within the storage provider. + /// + /// The file to resolve the public url for. + /// + /// If true and the storage is in the cloud, returns the actual remote cloud URL to the resource. + /// If false, retrieves an app relative URL to delegate further processing to the media middleware (which can handle remote files) + /// + /// The public URL. + string GetPublicUrl(IFile file, bool forCloud = false); + + /// + /// Retrieves the public URL for a given file within the storage provider. + /// + /// The relative path within the storage provider. + /// + /// If true and the storage is in the cloud, returns the actual remote cloud URL to the resource. + /// If false, retrieves an app relative URL to delegate further processing to the media middleware (which can handle remote files) + /// + /// The public URL. + string GetPublicUrl(string path, bool forCloud = false); + + /// + /// Retrieves the path within the storage provider for a given public url. + /// + /// The virtual or public url of a file. + /// The storage path or null if the media is not in a correct format. + string GetStoragePath(string url); + + /// + /// Checks if the given file exists within the storage provider. + /// + /// The relative path within the storage provider. + /// True if the file exists; False otherwise. + bool FileExists(string path); + + /// + /// Checks if the given folder exists within the storage provider. + /// + /// The relative path within the storage provider. + /// True if the folder exists; False otherwise. + bool FolderExists(string path); + + /// + /// Retrieves a file within the storage provider. + /// + /// The relative path to the file within the storage provider. + /// The file. + /// If the file is not found. + IFile GetFile(string path); + + /// + /// Retrieves a folder within the storage provider. + /// + /// The relative path to the folder within the storage provider. + /// The folder. + /// If the folder is not found. + IFolder GetFolder(string path); + + /// + /// Retrieves a folder for file path within the storage provider. + /// + /// The relative path to the file within the storage provider. + /// The folder for the file. + /// If the file or the folder is not found. + IFolder GetFolderForFile(string path); + + /// + /// Retrieves the count of files within a path. + /// + /// The relative path to the folder in which to retrieve file count. + /// The file pattern to match + /// Optional. Files not matching the predicate are excluded. + /// Whether to count files in all subfolders also + /// Total count of files. + long CountFiles(string path, string pattern, Func predicate, bool deep = true); + + /// + /// Performs a deep search for files within a path. + /// + /// The relative path to the folder in which to process file search. + /// The file pattern to match + /// Whether to search in all subfolders also + /// Matching file names + IEnumerable SearchFiles(string path, string pattern, bool deep = true); + + /// + /// Lists the files within a storage provider's path. + /// + /// The relative path to the folder which files to list. + /// The list of files in the folder. + IEnumerable ListFiles(string path); /// /// Lists the folders within a storage provider's path. @@ -110,8 +121,8 @@ public interface IFileSystem /// Creates a folder in the storage provider. /// /// The relative path to the folder to be created. - /// If the folder already exists. - void CreateFolder(string path); + /// The folder instance. + IFolder CreateFolder(string path); /// /// Deletes a folder in the storage provider. @@ -120,12 +131,32 @@ public interface IFileSystem /// If the folder doesn't exist. void DeleteFolder(string path); - /// - /// Renames a folder in the storage provider. - /// - /// The relative path to the folder to be renamed. - /// The relative path to the new folder. - void RenameFolder(string path, string newPath); + /// + /// Renames a folder in the storage provider. + /// + /// The relative path to the folder to be renamed. + /// The relative path to the new folder. + void RenameFolder(string path, string newPath); + + /// + /// Copies a folder and all its content to another folder. + /// + /// Path of source folder + /// Path of destination folder + /// Whether to overwrite existing files + void CopyFolder(string path, string destinationPath, bool overwrite = true); + + /// + /// Checks whether the name of the file is unique within its directory. + /// When given file exists, this method appends [1...n] to the file title until + /// the check returns false. + /// + /// The path of file to check + /// The new unique path, or null if method returns false + /// + /// false when does not exist yet. true otherwise. + /// + bool CheckUniqueFileName(string path, out string newPath); /// /// Deletes a file in the storage provider. @@ -134,12 +165,12 @@ public interface IFileSystem /// If the file doesn't exist. void DeleteFile(string path); - /// - /// Renames a file in the storage provider. - /// - /// The relative path to the file to be renamed. - /// The relative path to the new file. - void RenameFile(string path, string newPath); + /// + /// Renames a file in the storage provider. + /// + /// The relative path to the file to be renamed. + /// The relative path to the new file. + void RenameFile(string path, string newPath); /// /// Creates a file in the storage provider. @@ -149,43 +180,44 @@ public interface IFileSystem /// The created file. IFile CreateFile(string path); - /// - /// Asynchronously creates a file in the storage provider. - /// - /// The relative path to the file to be created. - /// If the file already exists. - /// The created file. - Task CreateFileAsync(string path); - - /// - /// Copies a file in the storage provider. - /// - /// The relative path to the file to be copied. - /// The relative path to the new file. - void CopyFile(string path, string newPath); - - /// - /// Saves a stream in the storage provider. - /// - /// The relative path to the file to be created. - /// The stream to be saved. - /// If the stream can't be saved due to access permissions. - void SaveStream(string path, Stream inputStream); - - /// - /// Asynchronously saves a stream in the storage provider. - /// - /// The relative path to the file to be created. - /// The stream to be saved. - /// If the stream can't be saved due to access permissions. - Task SaveStreamAsync(string path, Stream inputStream); - - /// - /// Combines to paths. - /// - /// The parent path. - /// The child path. - /// The combined path. - string Combine(string path1, string path2); - } + /// + /// Asynchronously creates a file in the storage provider. + /// + /// The relative path to the file to be created. + /// If the file already exists. + /// The created file. + Task CreateFileAsync(string path); + + /// + /// Copies a file in the storage provider. + /// + /// The relative path to the file to be copied. + /// The relative path to the new file. + /// Whether to overwrite a file with same name. + void CopyFile(string path, string newPath, bool overwrite = false); + + /// + /// Saves a stream in the storage provider. If the file already exists, it will be overwritten. + /// + /// The relative path to the file to be created. + /// The stream to be saved. + /// If the stream can't be saved due to access permissions. + void SaveStream(string path, Stream inputStream); + + /// + /// Asynchronously saves a stream in the storage provider. If the file already exists, it will be overwritten. + /// + /// The relative path to the file to be created. + /// The stream to be saved. + /// If the stream can't be saved due to access permissions. + Task SaveStreamAsync(string path, Stream inputStream); + + /// + /// Combines two paths. + /// + /// The parent path. + /// The child path. + /// The combined path. + string Combine(string path1, string path2); + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/IO/IFileSystemExtensions.cs b/src/Libraries/SmartStore.Core/IO/IFileSystemExtensions.cs index 755aee74e1..ee815cc1e8 100644 --- a/src/Libraries/SmartStore.Core/IO/IFileSystemExtensions.cs +++ b/src/Libraries/SmartStore.Core/IO/IFileSystemExtensions.cs @@ -1,289 +1,225 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; namespace SmartStore.Core.IO { - public static class IFileSystemExtensions - { - public static void WriteAllText(this IFileSystem fileSystem, string path, string contents) - { - Guard.NotEmpty(path, nameof(path)); - Guard.NotEmpty(contents, nameof(contents)); - - if (fileSystem.FileExists(path)) - { - fileSystem.DeleteFile(path); - } - - var file = fileSystem.CreateFile(path); - using (var stream = file.OpenWrite()) - using (var streamWriter = new StreamWriter(stream)) - { - streamWriter.Write(contents); - } - } - - public static async Task WriteAllTextAsync(this IFileSystem fileSystem, string path, string contents) - { - Guard.NotEmpty(path, nameof(path)); - Guard.NotEmpty(contents, nameof(contents)); - - if (fileSystem.FileExists(path)) - { - fileSystem.DeleteFile(path); - } - - var file = fileSystem.CreateFile(path); - using (var stream = file.OpenWrite()) - using (var streamWriter = new StreamWriter(stream)) - { - await streamWriter.WriteAsync(contents); - } - } - - public static string ReadAllText(this IFileSystem fileSystem, string path) - { - Guard.NotEmpty(path, nameof(path)); - - if (!fileSystem.FileExists(path)) - { - return String.Empty; - } - - var file = fileSystem.GetFile(path); - using (var stream = file.OpenRead()) - using (var streamReader = new StreamReader(stream)) - { - return streamReader.ReadToEnd(); - } - } - - public static async Task ReadAllTextAsync(this IFileSystem fileSystem, string path) - { - Guard.NotEmpty(path, nameof(path)); - - if (!fileSystem.FileExists(path)) - { - return String.Empty; - } - - var file = fileSystem.GetFile(path); - using (var stream = file.OpenRead()) - using (var streamReader = new StreamReader(stream)) - { - return await streamReader.ReadToEndAsync(); - } - } - - - public static void WriteAllBytes(this IFileSystem fileSystem, string path, byte[] contents) - { - Guard.NotEmpty(path, nameof(path)); - Guard.NotNull(contents, nameof(contents)); - - if (fileSystem.FileExists(path)) - { - fileSystem.DeleteFile(path); - } - - var file = fileSystem.CreateFile(path); - using (var stream = file.OpenWrite()) - { - stream.Write(contents, 0, contents.Length); - } - } - - public static async Task WriteAllBytesAsync(this IFileSystem fileSystem, string path, byte[] contents) - { - Guard.NotEmpty(path, nameof(path)); - Guard.NotNull(contents, nameof(contents)); - - if (fileSystem.FileExists(path)) - { - fileSystem.DeleteFile(path); - } - - var file = await fileSystem.CreateFileAsync(path); - using (var stream = file.OpenWrite()) - { - await stream.WriteAsync(contents, 0, contents.Length); - } - } - - public static byte[] ReadAllBytes(this IFileSystem fileSystem, string path) - { - Guard.NotEmpty(path, nameof(path)); - - if (!fileSystem.FileExists(path)) - { - return null; - } - - var file = fileSystem.GetFile(path); - using (var stream = file.OpenRead()) - { - return stream.ToByteArray(); - } - } - - public static async Task ReadAllBytesAsync(this IFileSystem fileSystem, string path) - { - Guard.NotEmpty(path, nameof(path)); - - if (!fileSystem.FileExists(path)) - { - return null; - } - - var file = fileSystem.GetFile(path); - using (var stream = file.OpenRead()) - { - return await stream.ToByteArrayAsync(); - } - } - - - /// - /// Tries to save a stream in the storage provider. - /// - /// The relative path to the file to be created. - /// The stream to be saved. - /// True if success; False otherwise. - public static bool TrySaveStream(this IFileSystem fileSystem, string path, Stream inputStream) - { - try - { - if (fileSystem.FileExists(path)) - { - return false; - } - - fileSystem.SaveStream(path, inputStream); - } - catch - { - return false; - } - - return true; - } - - /// - /// Asynchronously tries to save a stream in the storage provider. - /// - /// The relative path to the file to be created. - /// The stream to be saved. - /// True if success; False otherwise. - public static async Task TrySaveStreamAsync(this IFileSystem fileSystem, string path, Stream inputStream) - { - try - { - if (fileSystem.FileExists(path)) - { - return false; - } - - await fileSystem.SaveStreamAsync(path, inputStream); - } - catch - { - return false; - } - - return true; - } - - /// - /// Tries to create a folder in the storage provider. - /// - /// The relative path to the folder to be created. - /// True if success; False otherwise. - public static bool TryCreateFolder(this IFileSystem fileSystem, string path) - { - try - { - // prevent unnecessary exception - if (fileSystem.FolderExists(path)) - { - return false; - } - - fileSystem.CreateFolder(path); - } - catch - { - return false; - } - - return true; - } - - /// - /// Checks whether the name of the file is unique within its directory. - /// When given file exists, this method appends [1...n] to the file title until - /// the check returns false. - /// - /// The path of file to check - /// An object containing the unique file's info, or null if method returns false - /// - /// false when does not exist yet. true otherwise. - /// - public static bool CheckFileUniqueness(this IFileSystem fileSystem, string path, out IFile uniqueFile) - { - Guard.NotEmpty(path, nameof(path)); - - uniqueFile = null; - - var file = fileSystem.GetFile(path); - if (!file.Exists) - { - return false; - } - - var pattern = string.Concat(file.Title, "-*", file.Extension); - var dir = file.Directory; - var files = new HashSet(fileSystem.SearchFiles(dir, pattern, false).Select(x => Path.GetFileName(x))); - - int i = 1; - while (true) - { - var newFileName = string.Concat(file.Title, "-", i, file.Extension); - if (!files.Contains(newFileName)) - { - // Found our gap - uniqueFile = fileSystem.GetFile(string.Concat(dir, newFileName)); - return true; - } - - i++; - } - } - - /// - /// Retrieves the count of files within a path. - /// - /// The relative path to the folder in which to retrieve file count. - /// The file pattern to match - /// Whether to count files in all subfolders also - /// Total count of files. - public static long CountFiles(this IFileSystem fileSystem, string path, string pattern, bool deep = true) - { - return fileSystem.CountFiles(path, pattern, null, deep); - } - - /// - /// Retrieves the count of files within a path. - /// - /// The relative path to the folder in which to retrieve file count. - /// Whether to count files in all subfolders also - /// Total count of files. - public static long CountFiles(this IFileSystem fileSystem, string path, bool deep = true) - { - return fileSystem.CountFiles(path, "*", null, deep); - } - } + public static class IFileSystemExtensions + { + public static void WriteAllText(this IFileSystem fileSystem, string path, string contents, Encoding encoding = null) + { + Guard.NotEmpty(path, nameof(path)); + Guard.NotEmpty(contents, nameof(contents)); + + if (fileSystem.FileExists(path)) + { + fileSystem.DeleteFile(path); + } + + var file = fileSystem.CreateFile(path); + + using (var stream = file.OpenWrite()) + using (var streamWriter = new StreamWriter(stream, encoding ?? new UTF8Encoding(false, true))) + { + streamWriter.Write(contents); + } + } + + public static async Task WriteAllTextAsync(this IFileSystem fileSystem, string path, string contents, Encoding encoding = null) + { + Guard.NotEmpty(path, nameof(path)); + Guard.NotEmpty(contents, nameof(contents)); + + if (fileSystem.FileExists(path)) + { + fileSystem.DeleteFile(path); + } + + var file = fileSystem.CreateFile(path); + using (var stream = file.OpenWrite()) + using (var streamWriter = new StreamWriter(stream, encoding ?? new UTF8Encoding(false, true))) + { + await streamWriter.WriteAsync(contents); + } + } + + public static string ReadAllText(this IFileSystem fileSystem, string path) + { + Guard.NotEmpty(path, nameof(path)); + + if (!fileSystem.FileExists(path)) + { + return String.Empty; + } + + var file = fileSystem.GetFile(path); + using (var stream = file.OpenRead()) + using (var streamReader = new StreamReader(stream, Encoding.UTF8)) + { + return streamReader.ReadToEnd(); + } + } + + public static async Task ReadAllTextAsync(this IFileSystem fileSystem, string path) + { + Guard.NotEmpty(path, nameof(path)); + + if (!fileSystem.FileExists(path)) + { + return String.Empty; + } + + var file = fileSystem.GetFile(path); + using (var stream = file.OpenRead()) + using (var streamReader = new StreamReader(stream, Encoding.UTF8)) + { + return await streamReader.ReadToEndAsync(); + } + } + + + public static void WriteAllBytes(this IFileSystem fileSystem, string path, byte[] contents) + { + Guard.NotEmpty(path, nameof(path)); + Guard.NotNull(contents, nameof(contents)); + + if (fileSystem.FileExists(path)) + { + fileSystem.DeleteFile(path); + } + + var file = fileSystem.CreateFile(path); + using (var stream = file.OpenWrite()) + { + stream.Write(contents, 0, contents.Length); + } + } + + public static async Task WriteAllBytesAsync(this IFileSystem fileSystem, string path, byte[] contents) + { + Guard.NotEmpty(path, nameof(path)); + Guard.NotNull(contents, nameof(contents)); + + if (fileSystem.FileExists(path)) + { + fileSystem.DeleteFile(path); + } + + var file = await fileSystem.CreateFileAsync(path); + using (var stream = file.OpenWrite()) + { + await stream.WriteAsync(contents, 0, contents.Length); + } + } + + public static byte[] ReadAllBytes(this IFileSystem fileSystem, string path) + { + Guard.NotEmpty(path, nameof(path)); + + if (!fileSystem.FileExists(path)) + { + return null; + } + + var file = fileSystem.GetFile(path); + using (var stream = file.OpenRead()) + { + return stream.ToByteArray(); + } + } + + public static async Task ReadAllBytesAsync(this IFileSystem fileSystem, string path) + { + Guard.NotEmpty(path, nameof(path)); + + if (!fileSystem.FileExists(path)) + { + return null; + } + + var file = fileSystem.GetFile(path); + using (var stream = file.OpenRead()) + { + return await stream.ToByteArrayAsync(); + } + } + + + /// + /// Tries to save a stream in the storage provider. + /// + /// The relative path to the file to be created. + /// The stream to be saved. + /// True if success; False otherwise. + public static bool TrySaveStream(this IFileSystem fileSystem, string path, Stream inputStream) + { + try + { + if (fileSystem.FileExists(path)) + { + return false; + } + + fileSystem.SaveStream(path, inputStream); + } + catch + { + return false; + } + + return true; + } + + /// + /// Asynchronously tries to save a stream in the storage provider. + /// + /// The relative path to the file to be created. + /// The stream to be saved. + /// True if success; False otherwise. + public static async Task TrySaveStreamAsync(this IFileSystem fileSystem, string path, Stream inputStream) + { + try + { + if (fileSystem.FileExists(path)) + { + return false; + } + + await fileSystem.SaveStreamAsync(path, inputStream); + } + catch + { + return false; + } + + return true; + } + + /// + /// Retrieves the count of files within a path. + /// + /// The relative path to the folder in which to retrieve file count. + /// The file pattern to match + /// Whether to count files in all subfolders also + /// Total count of files. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long CountFiles(this IFileSystem fileSystem, string path, string pattern, bool deep = true) + { + return fileSystem.CountFiles(path, pattern, null, deep); + } + + /// + /// Retrieves the count of files within a path. + /// + /// The relative path to the folder in which to retrieve file count. + /// Whether to count files in all subfolders also + /// Total count of files. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long CountFiles(this IFileSystem fileSystem, string path, bool deep = true) + { + return fileSystem.CountFiles(path, "*", null, deep); + } + } } diff --git a/src/Libraries/SmartStore.Core/IO/IFolder.cs b/src/Libraries/SmartStore.Core/IO/IFolder.cs index a911def2c8..49eda06377 100644 --- a/src/Libraries/SmartStore.Core/IO/IFolder.cs +++ b/src/Libraries/SmartStore.Core/IO/IFolder.cs @@ -2,13 +2,36 @@ namespace SmartStore.Core.IO { - public interface IFolder + public interface IFolder { - string Path { get; } + /// + /// The path relative to the storage root + /// + string Path { get; } + + /// + /// The foldername + /// string Name { get; } - long Size { get; } - bool Exists { get; } - DateTime LastUpdated { get; } - IFolder Parent { get; } - } + + /// + /// Size sum of all containing files - including those in subfolders - in bytes + /// + long Size { get; } + + /// + /// Whether the folder exists + /// + bool Exists { get; } + + /// + /// Expressed as UTC time + /// + DateTime LastUpdated { get; } + + /// + /// The parent folder + /// + IFolder Parent { get; } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/IO/ImageHeader.cs b/src/Libraries/SmartStore.Core/IO/ImageHeader.cs index c359438b7d..231e5b3fe6 100644 --- a/src/Libraries/SmartStore.Core/IO/ImageHeader.cs +++ b/src/Libraries/SmartStore.Core/IO/ImageHeader.cs @@ -4,370 +4,440 @@ using System.IO; using System.Linq; using System.Text; +using System.Xml; namespace SmartStore.Core.IO { - /// - /// Taken from http://stackoverflow.com/questions/111345/getting-image-dimensions-without-reading-the-entire-file/111349 - /// Minor improvements including supporting unsigned 16-bit integers when decoding Jfif and added logic - /// to load the image using new Bitmap if reading the headers fails - /// - public static class ImageHeader - { - internal class UnknownImageFormatException : ArgumentException - { - public UnknownImageFormatException(string paramName = "", Exception e = null) - : base("Could not recognise image format.", paramName, e) - { - } - } - - private static Dictionary> _imageFormatDecoders = new Dictionary>() - { - { new byte[] { 0x42, 0x4D }, DecodeBitmap }, - { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif }, - { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif }, - { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng }, - // { new byte[] { 0xff, 0xd8 }, DecodeJfif }, - //{ new byte[] { 0xff, 0xd8, 0xff, 0xe0 }, DecodeJpeg }, - //{ new byte[] { 0xff }, DecodeJpeg2 }, - }; - - private static int _maxMagicBytesLength = 0; - - static ImageHeader() - { - _maxMagicBytesLength = _imageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; - } - - /// - /// Gets the dimensions of an image. - /// - /// The path of the image to get the dimensions for. - /// The dimensions of the specified image. - public static Size GetDimensions(string path) - { - if (!File.Exists(path)) - { - throw new FileNotFoundException("File '{0}' does not exist.".FormatInvariant(path)); - } - - var mime = MimeTypes.MapNameToMimeType(path); - return GetDimensions(File.OpenRead(path), mime, false); - } - - /// - /// Gets the dimensions of an image. - /// - /// The bytes of the image to get the dimensions for. - /// The MIME type of the image. Can be null. - /// The dimensions of the specified image. - public static Size GetDimensions(byte[] buffer, string mime = null) - { - if (buffer == null || buffer.Length == 0) - { - return Size.Empty; - } - - return GetDimensions(new MemoryStream(buffer), mime, false); - } - - /// - /// Gets the dimensions of an image. - /// - /// The stream of the image to get the dimensions for. - /// If false, the passed stream will get disposed - /// The dimensions of the specified image. - public static Size GetDimensions(Stream input, bool leaveOpen = true) - { - return GetDimensions(input, null, leaveOpen); - } - - /// - /// Gets the dimensions of an image. - /// - /// The stream of the image to get the dimensions for. - /// The MIME type of the image. Can be null. - /// If false, the passed stream will get disposed - /// The dimensions of the specified image. - public static Size GetDimensions(Stream input, string mime, bool leaveOpen = true) - { - Guard.NotNull(input, nameof(input)); - - var gdip = false; - - if (!input.CanSeek || input.Length == 0) - { - return Size.Empty; - } - - try - { - if (mime == "image/jpeg") - { - // Reading JPEG header does not work reliably - gdip = true; - return GetDimensionsByGdip(input); - } - - using (var reader = new BinaryReader(input, Encoding.Unicode, true)) - { - return GetDimensions(reader); - } - } - catch (Exception ex) - { - if (gdip) - { - throw ex; - } - - // something went wrong with fast image access, - // so get original size the classic way - try - { - input.Seek(0, SeekOrigin.Begin); - return GetDimensionsByGdip(input); - } - catch - { - throw ex; - } - } - finally - { - if (!leaveOpen) - { - input.Dispose(); - } - } - } - - /// - /// Gets the dimensions of an image. - /// - /// The path of the image to get the dimensions of. - /// The dimensions of the specified image. - /// The image was of an unrecognised format. - public static Size GetDimensions(BinaryReader binaryReader) - { - byte[] magicBytes = new byte[_maxMagicBytesLength]; - for (int i = 0; i < _maxMagicBytesLength; i += 1) - { - magicBytes[i] = binaryReader.ReadByte(); - foreach (var kvPair in _imageFormatDecoders) - { - if (StartsWith(magicBytes, kvPair.Key)) - { - return kvPair.Value(binaryReader); - } - } - } - - throw new UnknownImageFormatException("binaryReader"); - } - - private static Size GetDimensionsByGdip(Stream input) - { - using (var b = Image.FromStream(input, false, false)) - { - return new Size(b.Width, b.Height); - } - } - - private static bool StartsWith(byte[] thisBytes, byte[] thatBytes) - { - for (int i = 0; i < thatBytes.Length; i += 1) - { - if (thisBytes[i] != thatBytes[i]) - { - return false; - } - } - - return true; - } - - private static short ReadLittleEndianInt16(BinaryReader binaryReader) - { - byte[] bytes = new byte[sizeof(short)]; - - for (int i = 0; i < sizeof(short); i += 1) - { - bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte(); - } - return BitConverter.ToInt16(bytes, 0); - } - - private static ushort ReadLittleEndianUInt16(BinaryReader binaryReader) - { - byte[] bytes = new byte[sizeof(ushort)]; - - for (int i = 0; i < sizeof(ushort); i += 1) - { - bytes[sizeof(ushort) - 1 - i] = binaryReader.ReadByte(); - } - return BitConverter.ToUInt16(bytes, 0); - } - - private static int ReadLittleEndianInt32(BinaryReader binaryReader) - { - byte[] bytes = new byte[sizeof(int)]; - for (int i = 0; i < sizeof(int); i += 1) - { - bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte(); - } - return BitConverter.ToInt32(bytes, 0); - } - - private static Size DecodeBitmap(BinaryReader binaryReader) - { - binaryReader.ReadBytes(16); - int width = binaryReader.ReadInt32(); - int height = binaryReader.ReadInt32(); - return new Size(width, height); - } - - private static Size DecodeGif(BinaryReader binaryReader) - { - int width = binaryReader.ReadInt16(); - int height = binaryReader.ReadInt16(); - return new Size(width, height); - } - - private static Size DecodePng(BinaryReader binaryReader) - { - binaryReader.ReadBytes(8); - int width = ReadLittleEndianInt32(binaryReader); - int height = ReadLittleEndianInt32(binaryReader); - return new Size(width, height); - } - - #region Experiments - - private static Size DecodeJpeg(BinaryReader reader) - { - // For JPEGs, we need to read the first 12 bytes of each chunk. - // We'll read those 12 bytes at buf+2...buf+14, i.e. overwriting the existing buf. - - var buf = (new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }).Concat(reader.ReadBytes(20)).ToArray(); - - using (var f = new MemoryStream(buf)) - { - if (buf[6] == (byte)'J' && buf[7] == (byte)'F' && buf[8] == (byte)'I' && buf[9] == (byte)'F') - { - var len = buf.Length; - long pos = 2; - while (buf[2] == 0xFF) - { - if (buf[3] == 0xC0 || buf[3] == 0xC1 || buf[3] == 0xC2 || buf[3] == 0xC3 || buf[3] == 0xC9 || buf[3] == 0xCA || buf[3] == 0xCB) break; - pos += 2 + (buf[4] << 8) + buf[5]; - if (pos + 12 > len) break; - //fseek(f, pos, SEEK_SET); - f.Seek(pos, SeekOrigin.Begin); - //fread(buf + 2, 1, 12, f); - f.Read(buf, 0, 12); - } - } - } - - // JPEG: (first two bytes of buf are first two bytes of the jpeg file; rest of buf is the DCT frame - if (buf[0] == 0xFF && buf[1] == 0xD8 && buf[2] == 0xFF) - { - var height = (buf[7] << 8) + buf[8]; - var width = (buf[9] << 8) + buf[10]; - - return new Size(width, height); - } - - throw new UnknownImageFormatException(); - } - - private static Size DecodeJpeg2(BinaryReader reader) - { - bool found = false; - bool eof = false; - - while (!found || eof) - { - // read 0xFF and the type - //reader.ReadByte(); - byte type = reader.ReadByte(); - - // get length - int len = 0; - switch (type) - { - // start and end of the image - case 0xD8: - case 0xD9: - len = 0; - break; - - // restart interval - case 0xDD: - len = 2; - break; - - // the next two bytes is the length - default: - int lenHi = reader.ReadByte(); - int lenLo = reader.ReadByte(); - len = (lenHi << 8 | lenLo) - 2; - break; - } - - // EOF? - if (type == 0xD9) - eof = true; - - // process the data - if (len > 0) - { - // read the data - byte[] data = reader.ReadBytes(len); - - // this is what we are looking for - if (type == 0xC0) - { - int width = data[1] << 8 | data[2]; - int height = data[3] << 8 | data[4]; - return new Size(width, height); - } - } - } - - throw new UnknownImageFormatException(); - } - - private static Size DecodeJfif(BinaryReader reader) - { - while (reader.ReadByte() == 0xff) - { - byte marker = reader.ReadByte(); - short chunkLength = ReadLittleEndianInt16(reader); - if (marker == 0xc0) - { - reader.ReadByte(); - int height = ReadLittleEndianInt16(reader); - int width = ReadLittleEndianInt16(reader); - return new Size(width, height); - } - - if (chunkLength < 0) - { - ushort uchunkLength = (ushort)chunkLength; - reader.ReadBytes(uchunkLength - 2); - } - else - { - reader.ReadBytes(chunkLength - 2); - } - } - - throw new UnknownImageFormatException(); - } - - #endregion - } + /// + /// Taken from http://stackoverflow.com/questions/111345/getting-image-dimensions-without-reading-the-entire-file/111349 + /// Minor improvements including supporting unsigned 16-bit integers when decoding Jfif and added logic + /// to load the image using new Bitmap if reading the headers fails + /// + public static class ImageHeader + { + internal class UnknownImageFormatException : ArgumentException + { + public UnknownImageFormatException(string paramName = "", Exception e = null) + : base("Could not recognise image format.", paramName, e) + { + } + } + + private static readonly Dictionary> _imageFormatDecoders = new Dictionary>() + { + { new byte[] { 0x42, 0x4D }, DecodeBitmap }, + { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif }, + { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif }, + { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng }, + { new byte[] { 0xff, 0xd8 }, DecodeJpeg }, + //{ new byte[] { 0xff, 0xd8, 0xff, 0xe0 }, DecodeJpeg }, + //{ new byte[] { 0xff }, DecodeJpeg2 } + }; + + private static readonly int _maxMagicBytesLength = 0; + + static ImageHeader() + { + _maxMagicBytesLength = _imageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; + } + + /// + /// Gets the dimensions of an image. + /// + /// The path of the image to get the dimensions for. + /// The dimensions of the specified image. + public static Size GetDimensions(string path) + { + if (!File.Exists(path)) + { + throw new FileNotFoundException("File '{0}' does not exist.".FormatInvariant(path)); + } + + var mime = MimeTypes.MapNameToMimeType(path); + return GetDimensions(File.OpenRead(path), mime, false); + } + + /// + /// Gets the dimensions of an image. + /// + /// The bytes of the image to get the dimensions for. + /// The MIME type of the image. Can be null. + /// The dimensions of the specified image. + public static Size GetDimensions(byte[] buffer, string mime = null) + { + if (buffer == null || buffer.Length == 0) + { + return Size.Empty; + } + + return GetDimensions(new MemoryStream(buffer), mime, false); + } + + /// + /// Gets the dimensions of an image. + /// + /// The stream of the image to get the dimensions for. + /// If false, the passed stream will get disposed + /// The dimensions of the specified image. + public static Size GetDimensions(Stream input, bool leaveOpen = true) + { + return GetDimensions(input, null, leaveOpen); + } + + /// + /// Gets the dimensions of an image. + /// + /// The stream of the image to get the dimensions for. + /// The MIME type of the image. Can be null. + /// If false, the passed stream will get disposed + /// The dimensions of the specified image. + public static Size GetDimensions(Stream input, string mime, bool leaveOpen = true) + { + Guard.NotNull(input, nameof(input)); + + var gdip = false; + + if (!input.CanSeek || input.Length == 0) + { + return Size.Empty; + } + + try + { + //if (mime == "image/jpeg") + //{ + // // Reading JPEG header does not work reliably + // gdip = true; + // return GetDimensionsByGdip(input); + //} + if (mime == "image/svg+xml") + { + return GetDimensionsFromSvg(input); + } + + using (var reader = new BinaryReader(input, Encoding.Unicode, true)) + { + return GetDimensions(reader); + } + } + catch (Exception ex) + { + if (gdip) + { + throw ex; + } + + // something went wrong with fast image access, + // so get original size the classic way + try + { + input.Seek(0, SeekOrigin.Begin); + return GetDimensionsByGdip(input); + } + catch + { + throw ex; + } + } + finally + { + if (!leaveOpen) + { + input.Dispose(); + } + else + { + input.Seek(0, SeekOrigin.Begin); + } + } + } + + /// + /// Gets the dimensions of an image. + /// + /// The path of the image to get the dimensions of. + /// The dimensions of the specified image. + /// The image was of an unrecognised format. + public static Size GetDimensions(BinaryReader binaryReader) + { + byte[] magicBytes = new byte[_maxMagicBytesLength]; + for (int i = 0; i < _maxMagicBytesLength; i += 1) + { + magicBytes[i] = binaryReader.ReadByte(); + foreach (var kvPair in _imageFormatDecoders) + { + if (StartsWith(magicBytes, kvPair.Key)) + { + var size = kvPair.Value(binaryReader); + if (size.IsEmpty) + { + break; + } + else + { + return size; + } + } + } + } + + throw new UnknownImageFormatException("binaryReader"); + } + + private static Size GetDimensionsByGdip(Stream input) + { + using (var b = Image.FromStream(input, false, false)) + { + return new Size(b.Width, b.Height); + } + } + + private static Size GetDimensionsFromSvg(Stream input) + { + using (var reader = XmlReader.Create(input)) + { + while (reader.Read()) + { + if (reader.IsStartElement()) + { + if (reader.Name == "svg") + { + var width = reader["width"]; + var height = reader["height"]; + + var size = new Size(width.ToInt(), height.ToInt()); + if (size.Width == 0 || size.Height == 0) + { + var viewBox = reader["viewBox"]; + if (viewBox.HasValue()) + { + var arrViewBox = viewBox.Trim().Split(' '); + if (arrViewBox.Length == 4) + { + size = new Size(arrViewBox[2].ToInt(), arrViewBox[3].ToInt()); + } + } + } + + return size; + } + } + } + } + + return Size.Empty; + } + + private static bool StartsWith(byte[] thisBytes, byte[] thatBytes) + { + for (int i = 0; i < thatBytes.Length; i += 1) + { + if (thisBytes[i] != thatBytes[i]) + { + return false; + } + } + + return true; + } + + private static short ReadLittleEndianInt16(BinaryReader binaryReader) + { + byte[] bytes = new byte[sizeof(short)]; + + for (int i = 0; i < sizeof(short); i += 1) + { + bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte(); + } + return BitConverter.ToInt16(bytes, 0); + } + + private static ushort ReadLittleEndianUInt16(BinaryReader binaryReader) + { + byte[] bytes = new byte[sizeof(ushort)]; + + for (int i = 0; i < sizeof(ushort); i += 1) + { + bytes[sizeof(ushort) - 1 - i] = binaryReader.ReadByte(); + } + return BitConverter.ToUInt16(bytes, 0); + } + + private static int ReadLittleEndianInt32(BinaryReader binaryReader) + { + byte[] bytes = new byte[sizeof(int)]; + for (int i = 0; i < sizeof(int); i += 1) + { + bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte(); + } + return BitConverter.ToInt32(bytes, 0); + } + + private static Size DecodeBitmap(BinaryReader binaryReader) + { + binaryReader.ReadBytes(16); + int width = binaryReader.ReadInt32(); + int height = binaryReader.ReadInt32(); + return new Size(width, height); + } + + private static Size DecodeGif(BinaryReader binaryReader) + { + int width = binaryReader.ReadInt16(); + int height = binaryReader.ReadInt16(); + return new Size(width, height); + } + + private static Size DecodePng(BinaryReader binaryReader) + { + binaryReader.ReadBytes(8); + int width = ReadLittleEndianInt32(binaryReader); + int height = ReadLittleEndianInt32(binaryReader); + return new Size(width, height); + } + + private static Size DecodeJpeg(BinaryReader reader) + { + string state = "started"; + while (true) + { + byte[] c; + if (state == "started") + { + c = reader.ReadBytes(1); + state = (c[0] == 0xFF) ? "sof" : "started"; + } + else if (state == "sof") + { + c = reader.ReadBytes(1); + if (c[0] >= 0xe0 && c[0] <= 0xef) + { + state = "skipframe"; + } + else if ((c[0] >= 0xC0 && c[0] <= 0xC3) || (c[0] >= 0xC5 && c[0] <= 0xC7) || (c[0] >= 0xC9 && c[0] <= 0xCB) || (c[0] >= 0xCD && c[0] <= 0xCF)) + { + state = "readsize"; + } + else if (c[0] == 0xFF) + { + state = "sof"; + } + else + { + state = "skipframe"; + } + } + else if (state == "skipframe") + { + c = reader.ReadBytes(2); + int skip = ReadInt(c) - 2; + reader.ReadBytes(skip); + state = "started"; + } + else if (state == "readsize") + { + c = reader.ReadBytes(7); + var width = ReadInt(new[] { c[5], c[6] }); + var height = ReadInt(new[] { c[3], c[4] }); + return new Size(width, height); + } + } + + throw new UnknownImageFormatException(); + } + + private static int ReadInt(byte[] chars) + { + return (chars[0] << 8) + chars[1]; + } + + #region Experiments + + //private static Size DecodeJpeg2(BinaryReader reader) + //{ + // bool found = false; + // bool eof = false; + + // while (!found || eof) + // { + // // read 0xFF and the type + // //reader.ReadByte(); + // byte type = reader.ReadByte(); + + // // get length + // int len = 0; + // switch (type) + // { + // // start and end of the image + // case 0xD8: + // case 0xD9: + // len = 0; + // break; + + // // restart interval + // case 0xDD: + // len = 2; + // break; + + // // the next two bytes is the length + // default: + // int lenHi = reader.ReadByte(); + // int lenLo = reader.ReadByte(); + // len = (lenHi << 8 | lenLo) - 2; + // break; + // } + + // // EOF? + // if (type == 0xD9) + // eof = true; + + // // process the data + // if (len > 0) + // { + // // read the data + // byte[] data = reader.ReadBytes(len); + + // // this is what we are looking for + // if (type == 0xC0) + // { + // int width = data[1] << 8 | data[2]; + // int height = data[3] << 8 | data[4]; + // return new Size(width, height); + // } + // } + // } + + // throw new UnknownImageFormatException(); + //} + + //private static Size DecodeJfif(BinaryReader reader) + //{ + // while (reader.ReadByte() == 0xff) + // { + // byte marker = reader.ReadByte(); + // short chunkLength = ReadLittleEndianInt16(reader); + // if (marker == 0xc0) + // { + // reader.ReadByte(); + // int height = ReadLittleEndianInt16(reader); + // int width = ReadLittleEndianInt16(reader); + // return new Size(width, height); + // } + + // if (chunkLength < 0) + // { + // ushort uchunkLength = (ushort)chunkLength; + // reader.ReadBytes(uchunkLength - 2); + // } + // else + // { + // reader.ReadBytes(chunkLength - 2); + // } + // } + + // throw new UnknownImageFormatException(); + //} + + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/IO/LocalFileSystem.cs b/src/Libraries/SmartStore.Core/IO/LocalFileSystem.cs index 9591f17d81..45bb9e8c77 100644 --- a/src/Libraries/SmartStore.Core/IO/LocalFileSystem.cs +++ b/src/Libraries/SmartStore.Core/IO/LocalFileSystem.cs @@ -10,640 +10,717 @@ namespace SmartStore.Core.IO { - public class LocalFileSystem : IFileSystem - { - private string _root; - private string _publicPath; // /Shop/base - private string _storagePath; // C:\SMNET\base - private bool _isCloudStorage; // When public URL is outside of current app - - public LocalFileSystem() - : this(string.Empty, string.Empty) - { - } - - protected internal LocalFileSystem(string basePath) - : this(basePath, string.Empty) - { - } - - protected internal LocalFileSystem(string basePath, string publicPath) - { - basePath = basePath.EmptyNull(); - - var pathIsAbsolute = PathHelper.IsAbsolutePhysicalPath(basePath); - - NormalizeStoragePath(ref basePath, pathIsAbsolute); - - _publicPath = NormalizePublicPath(publicPath, basePath, pathIsAbsolute); - - _root = basePath; - } - - public bool IsCloudStorage - { - get { return _isCloudStorage; } - } - - private void NormalizeStoragePath(ref string basePath, bool basePathIsAbsolute) - { - if (basePathIsAbsolute) - { - // Path is fully qualified (UNC or absolute with drive letter) - - // In this case this is our root path... - basePath = basePath.TrimEnd(Path.DirectorySeparatorChar); - - // ...AND our physical storage path - _storagePath = basePath; - } - else - { - // Path is relative to the app root - basePath = basePath.TrimEnd('/').EnsureStartsWith("/"); - _storagePath = CommonHelper.MapPath("~" + basePath, false); - } - } - - private string NormalizePublicPath(string publicPath, string basePath, bool basePathIsAbsolute) - { - publicPath = publicPath.EmptyNull(); - - if (basePathIsAbsolute) - { - if (publicPath.IsEmpty() || (!publicPath.StartsWith("~/") && !publicPath.IsWebUrl(true))) - { - var streamMedia = CommonHelper.GetAppSetting("sm:StreamRemoteMedia", true); - if (!streamMedia) - { - throw new ArgumentException(@"When the base path is a fully qualified path and remote media streaming is disabled, + public class LocalFileSystem : IFileSystem + { + private readonly string _root; + private readonly string _publicPath; // /Shop/base + private string _storagePath; // C:\SMNET\base + private bool _isCloudStorage; // When public URL is outside of current app + + public LocalFileSystem() + : this(string.Empty, string.Empty) + { + } + + protected internal LocalFileSystem(string basePath) + : this(basePath, string.Empty) + { + } + + protected internal LocalFileSystem(string basePath, string publicPath) + { + basePath = basePath.EmptyNull(); + + var pathIsAbsolute = PathHelper.IsAbsolutePhysicalPath(basePath); + + NormalizeStoragePath(ref basePath, pathIsAbsolute); + + _publicPath = NormalizePublicPath(publicPath, basePath, pathIsAbsolute); + + _root = basePath; + } + + public bool IsCloudStorage => _isCloudStorage; + + private void NormalizeStoragePath(ref string basePath, bool basePathIsAbsolute) + { + if (basePathIsAbsolute) + { + // Path is fully qualified (UNC or absolute with drive letter) + + // In this case this is our root path... + basePath = basePath.TrimEnd(Path.DirectorySeparatorChar); + + // ...AND our physical storage path + _storagePath = basePath; + } + else + { + // Path is relative to the app root + basePath = basePath.TrimEnd('/').EnsureStartsWith("/"); + _storagePath = CommonHelper.MapPath("~" + basePath, false); + } + } + + private string NormalizePublicPath(string publicPath, string basePath, bool basePathIsAbsolute) + { + publicPath = publicPath.EmptyNull(); + + if (basePathIsAbsolute) + { + if (publicPath.IsEmpty() || (!publicPath.StartsWith("~/") && !publicPath.IsWebUrl(true))) + { + var streamMedia = CommonHelper.GetAppSetting("sm:StreamRemoteMedia", true); + if (!streamMedia) + { + throw new ArgumentException(@"When the base path is a fully qualified path and remote media streaming is disabled, the public path must not be empty, and either be a fully qualified URL or a virtual path (e.g.: ~/Media)", nameof(publicPath)); - } - } - } - - var appVirtualPath = HostingEnvironment.IsHosted - ? HostingEnvironment.ApplicationVirtualPath.TrimEnd('/') - : string.Empty; - - if (publicPath.StartsWith("~/")) - { - // prepend application virtual path - return appVirtualPath + publicPath.Substring(1); - } - - if (publicPath.IsEmpty() && !basePathIsAbsolute) - { - // > /MyAppRoot/Media - return appVirtualPath + basePath; - } - - _isCloudStorage = true; - - return publicPath; - } - - public string Root - { - get { return _root; } - } - - /// - /// Maps a relative path into the storage path. - /// - /// The relative path to be mapped. - /// The relative path combined with the storage path. - protected virtual string MapStorage(string path) - { - var mappedPath = string.IsNullOrEmpty(path) ? _storagePath : Path.Combine(_storagePath, Fix(path)); - return ValidatePath(_storagePath, mappedPath); - } - - /// - /// Maps a relative path into the public path. - /// - /// The relative path to be mapped. - /// The relative path combined with the public path in an URL friendly format ('/' character for directory separator). - protected virtual string MapPublic(string path) - { - return string.IsNullOrEmpty(path) ? _publicPath : HttpUtility.UrlDecode(Path.Combine(_publicPath, path).Replace(Path.DirectorySeparatorChar, '/')); - } - - static string Fix(string path) - { - return string.IsNullOrEmpty(path) - ? "" - : Path.DirectorySeparatorChar != '/' - ? path.Replace('/', Path.DirectorySeparatorChar).TrimStart('/', '\\') - : path.TrimStart('/', '\\'); - } - - public string GetPublicUrl(string path, bool forCloud = false) - { - return MapPublic(path); - } - - public virtual string GetStoragePath(string url) - { - if (url.HasValue()) - { - if (url.StartsWith("~/")) - { - url = VirtualPathUtility.ToAbsolute(url); - } - - if (url.StartsWith(_publicPath)) - { - return HttpUtility.UrlDecode(url.Substring(_publicPath.Length).Replace('/', Path.DirectorySeparatorChar)); - } - } - - return null; - } - - public bool FileExists(string path) - { - return File.Exists(MapStorage(path)); - } - - public bool FolderExists(string path) - { - return Directory.Exists(MapStorage(path)); - } - - public IFile GetFile(string path) - { - var fileInfo = new FileInfo(MapStorage(path)); - return new LocalFile(Fix(path), fileInfo); - } - - public IFolder GetFolder(string path) - { - var directoryInfo = new DirectoryInfo(MapStorage(path)); - return new LocalFolder(Fix(path), directoryInfo); - } - - public IFolder GetFolderForFile(string path) - { - var fileInfo = new FileInfo(MapStorage(path)); - if (!fileInfo.Exists) - { - throw new ArgumentException("File " + path + " does not exist"); - } - - if (!fileInfo.Directory.Exists) - { - throw new ArgumentException("Folder " + path + " does not exist"); - } - - // get relative path of the folder - var folderPath = Path.GetDirectoryName(path); - - return new LocalFolder(Fix(folderPath), fileInfo.Directory); - } - - public long CountFiles(string path, string pattern, Func predicate, bool deep = true) - { - var files = SearchFiles(path, pattern, deep).AsParallel(); - - if (predicate != null) - { - return files.Count(predicate); - } - else - { - return files.Count(); - } - } - - public IEnumerable SearchFiles(string path, string pattern, bool deep = true) - { - // Get relative from absolute path - var index = _storagePath.EmptyNull().Length; - - return Directory.EnumerateFiles(MapStorage(path), pattern, deep ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly) - .Select(x => x.Substring(index)); - } - - public IEnumerable ListFiles(string path) - { - var directoryInfo = new DirectoryInfo(MapStorage(path)); - - if (!directoryInfo.Exists) - { - throw new ArgumentException("Directory " + path + " does not exist"); - } - - return directoryInfo - .EnumerateFiles() - .Where(fi => !IsHidden(fi)) - .Select(fi => new LocalFile(Path.Combine(Fix(path), fi.Name), fi)); - } - - public IEnumerable ListFolders(string path) - { - var directoryInfo = new DirectoryInfo(MapStorage(path)); - - if (!directoryInfo.Exists) - { - try - { - directoryInfo.Create(); - } - catch (Exception ex) - { - if (ex.IsFatal()) - { - throw; - } - throw new ArgumentException(string.Format("The folder could not be created at path: {0}. {1}", path, ex)); - } - } - - return directoryInfo - .EnumerateDirectories() - .Where(di => !IsHidden(di)) - .Select(di => new LocalFolder(Path.Combine(Fix(path), di.Name), di)); - } - - private static bool IsHidden(FileSystemInfo di) - { - return (di.Attributes & FileAttributes.Hidden) != 0; - } - - public void CreateFolder(string path) - { - var directoryInfo = new DirectoryInfo(MapStorage(path)); - - if (directoryInfo.Exists) - { - throw new ArgumentException("Directory " + path + " already exists"); - } - - Directory.CreateDirectory(directoryInfo.FullName); - } - - public void DeleteFolder(string path) - { - var directoryInfo = new DirectoryInfo(MapStorage(path)); - - if (!directoryInfo.Exists) - { - throw new ArgumentException("Directory " + path + " does not exist"); - } - - directoryInfo.Delete(true); - } - - public void RenameFolder(string path, string newPath) - { - var sourceDirectory = new DirectoryInfo(MapStorage(path)); - if (!sourceDirectory.Exists) - { - throw new ArgumentException("Directory " + path + "does not exist"); - } - - var targetDirectory = new DirectoryInfo(MapStorage(newPath)); - if (targetDirectory.Exists) - { - throw new ArgumentException("Directory " + newPath + " already exists"); - } - - Directory.Move(sourceDirectory.FullName, targetDirectory.FullName); - } - - public IFile CreateFile(string path) - { - var fileInfo = new FileInfo(MapStorage(path)); - - if (fileInfo.Exists) - { - throw new ArgumentException("File " + path + " already exists"); - } - - // ensure the directory exists - var dirName = Path.GetDirectoryName(fileInfo.FullName); - if (!Directory.Exists(dirName)) - { - Directory.CreateDirectory(dirName); - } - - File.WriteAllBytes(fileInfo.FullName, new byte[0]); - - return new LocalFile(Fix(path), fileInfo); - } - - public async Task CreateFileAsync(string path) - { - var fileInfo = new FileInfo(MapStorage(path)); - - if (fileInfo.Exists) - { - throw new ArgumentException("File " + path + " already exists"); - } - - // ensure the directory exists - var dirName = Path.GetDirectoryName(fileInfo.FullName); - if (!Directory.Exists(dirName)) - { - Directory.CreateDirectory(dirName); - } - - using (FileStream stream = new FileStream(fileInfo.FullName, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 4096, useAsync: true)) - { - await stream.WriteAsync(new byte[0], 0, 0); - } - - return new LocalFile(Fix(path), fileInfo); - } - - public void DeleteFile(string path) - { - var fileInfo = new FileInfo(MapStorage(path)); - - if (fileInfo.Exists) - { - fileInfo.Delete(); - } - } - - public void RenameFile(string path, string newPath) - { - var sourceFileInfo = new FileInfo(MapStorage(path)); - if (!sourceFileInfo.Exists) - { - throw new ArgumentException("File " + path + " does not exist"); - } - - var targetFileInfo = new FileInfo(MapStorage(newPath)); - if (targetFileInfo.Exists) - { - throw new ArgumentException("File " + newPath + " already exists"); - } - - File.Move(sourceFileInfo.FullName, targetFileInfo.FullName); - } - - public void CopyFile(string path, string newPath) - { - var sourceFileInfo = new FileInfo(MapStorage(path)); - if (!sourceFileInfo.Exists) - { - throw new ArgumentException("File " + path + " does not exist"); - } - - var targetFileInfo = new FileInfo(MapStorage(newPath)); - if (targetFileInfo.Exists) - { - throw new ArgumentException("File " + newPath + " already exists"); - } - - File.Copy(sourceFileInfo.FullName, targetFileInfo.FullName); - } - - public void SaveStream(string path, Stream inputStream) - { - // Create the file. - // The CreateFile method will map the still relative path - var file = CreateFile(path); - - using (var outputStream = file.OpenWrite()) - { - var buffer = new byte[8192]; - for (;;) - { - var length = inputStream.Read(buffer, 0, buffer.Length); - if (length <= 0) - break; - outputStream.Write(buffer, 0, length); - } - } - } - - public async Task SaveStreamAsync(string path, Stream inputStream) - { - // Create the file. - // The CreateFile method will map the still relative path - var file = await CreateFileAsync(path); - - using (var outputStream = file.OpenWrite()) - { - var buffer = new byte[8192]; - for (;;) - { - var length = await inputStream.ReadAsync(buffer, 0, buffer.Length); - if (length <= 0) - break; - await outputStream.WriteAsync(buffer, 0, length); - } - } - } - - public string Combine(string path1, string path2) - { - return Path.Combine(path1, path2); - } - - /// - /// Determines if a path lies within the base path boundaries. - /// If not, an exception is thrown. - /// - /// The base path which boundaries are not to be transposed. - /// The path to determine. - /// The mapped path if valid. - /// If the path is invalid. - internal static string ValidatePath(string basePath, string mappedPath) - { - bool valid = false; - string error = null; - - try - { - // Check that we are indeed within the storage directory boundaries - valid = Path.GetFullPath(mappedPath).StartsWith(Path.GetFullPath(basePath), StringComparison.OrdinalIgnoreCase); - } - catch (Exception exception) - { - // Make sure that if invalid for medium trust we give a proper exception - valid = false; - error = exception.Message; - } - - if (!valid) - { - throw new ArgumentException($"{error ?? "Invalid path."} mappedPath: {mappedPath.NaIfEmpty()}"); - } - - return mappedPath; - } - - - private class LocalFile : IFile - { - private readonly string _path; - private readonly FileInfo _fileInfo; - private Size? _dimensions; - - public LocalFile(string path, FileInfo fileInfo) - { - _path = path; - _fileInfo = fileInfo; - } - - public string Path - { - get { return _path; } - } - - public string Directory - { - get { return _path.Substring(0, _path.Length - Name.Length); } - } - - public string Name - { - get { return _fileInfo.Name; } - } - - public string Title - { - get { return System.IO.Path.GetFileNameWithoutExtension(_fileInfo.Name); } - } - - public long Size - { - get { return _fileInfo.Length; } - } - - public DateTime LastUpdated - { - get { return _fileInfo.LastWriteTimeUtc; } - } - - public string Extension - { - get { return _fileInfo.Extension; } - } - - public Size Dimensions - { - get - { - if (_dimensions == null) - { - try - { - var mime = MimeTypes.MapNameToMimeType(_fileInfo.Name); - _dimensions = ImageHeader.GetDimensions(OpenRead(), mime, false); - } - catch - { - _dimensions = new Size(); - } - } - - return _dimensions.Value; - } - } - - public bool Exists - { - get { return _fileInfo.Exists; } - } - - public Stream OpenRead() - { - return new FileStream(_fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true); - } - - public Stream OpenWrite() - { - return new FileStream(_fileInfo.FullName, FileMode.Open, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true); - } - - public Stream CreateFile() - { - return new FileStream(_fileInfo.FullName, FileMode.Truncate, FileAccess.ReadWrite, FileShare.Read, bufferSize: 4096, useAsync: true); - } - - public Task CreateFileAsync() - { - return Task.Run(() => CreateFile()); - } - } - - private class LocalFolder : IFolder - { - private readonly string _path; - private readonly DirectoryInfo _directoryInfo; - - public LocalFolder(string path, DirectoryInfo directoryInfo) - { - _path = path; - _directoryInfo = directoryInfo; - } - - public string Path - { - get { return _path; } - } - - public string Name - { - get { return _directoryInfo.Name; } - } - - public DateTime LastUpdated - { - get { return _directoryInfo.LastWriteTimeUtc; } - } - - public long Size - { - get { return GetDirectorySize(_directoryInfo); } - } - - public bool Exists - { - get { return _directoryInfo.Exists; } - } - - public IFolder Parent - { - get - { - if (_directoryInfo.Parent != null) - { - return new LocalFolder(System.IO.Path.GetDirectoryName(_path), _directoryInfo.Parent); - } - throw new ArgumentException("Directory " + _directoryInfo.Name + " does not have a parent directory"); - } - } - - private static long GetDirectorySize(DirectoryInfo directoryInfo) - { - long size = 0; - - FileInfo[] fileInfos = directoryInfo.GetFiles(); - foreach (FileInfo fileInfo in fileInfos) - { - if (!IsHidden(fileInfo)) - { - size += fileInfo.Length; - } - } - DirectoryInfo[] directoryInfos = directoryInfo.GetDirectories(); - foreach (DirectoryInfo dInfo in directoryInfos) - { - if (!IsHidden(dInfo)) - { - size += GetDirectorySize(dInfo); - } - } - - return size; - } - } - - } + } + } + } + + var appVirtualPath = HostingEnvironment.IsHosted + ? HostingEnvironment.ApplicationVirtualPath.TrimEnd('/') + : string.Empty; + + if (publicPath.StartsWith("~/")) + { + // prepend application virtual path + return appVirtualPath + publicPath.Substring(1); + } + + if (publicPath.IsEmpty() && !basePathIsAbsolute) + { + // > /MyAppRoot/Media + return appVirtualPath + basePath; + } + + _isCloudStorage = true; + + return publicPath; + } + + public string Root => _root; + + /// + /// Maps a relative path into the storage path. + /// + /// The relative path to be mapped. + /// The relative path combined with the storage path. + protected virtual string MapStorage(string path) + { + var mappedPath = string.IsNullOrEmpty(path) ? _storagePath : Path.Combine(_storagePath, Fix(path)); + return ValidatePath(_storagePath, mappedPath); + } + + /// + /// Maps a relative path into the public path. + /// + /// The relative path to be mapped. + /// The relative path combined with the public path in an URL friendly format ('/' character for directory separator). + protected virtual string MapPublic(string path) + { + return string.IsNullOrEmpty(path) ? _publicPath : HttpUtility.UrlDecode(Path.Combine(_publicPath, path).Replace(Path.DirectorySeparatorChar, '/')); + } + + static string Fix(string path) + { + return string.IsNullOrEmpty(path) + ? "" + : Path.DirectorySeparatorChar != '/' + ? path.Replace('/', Path.DirectorySeparatorChar).TrimStart('/', '\\') + : path.TrimStart('/', '\\'); + } + + public string GetPublicUrl(IFile file, bool forCloud = false) + { + return MapPublic(file.Path); + } + + public string GetPublicUrl(string path, bool forCloud = false) + { + return MapPublic(path); + } + + public virtual string GetStoragePath(string url) + { + if (url.HasValue()) + { + if (url.StartsWith("~/")) + { + url = VirtualPathUtility.ToAbsolute(url); + } + + if (url.StartsWith(_publicPath)) + { + return HttpUtility.UrlDecode(url.Substring(_publicPath.Length).Replace('/', Path.DirectorySeparatorChar)); + } + } + + return null; + } + + public bool FileExists(string path) + { + return File.Exists(MapStorage(path)); + } + + public bool FolderExists(string path) + { + return Directory.Exists(MapStorage(path)); + } + + public IFile GetFile(string path) + { + return new LocalFile(path, MapStorage(path)); + } + + public IFolder GetFolder(string path) + { + return new LocalFolder(path, MapStorage(path)); + } + + public IFolder GetFolderForFile(string path) + { + var fileInfo = new FileInfo(MapStorage(path)); + if (!fileInfo.Exists) + { + throw new FileNotFoundException("File " + path + " does not exist"); + } + + if (!fileInfo.Directory.Exists) + { + throw new DirectoryNotFoundException("Folder " + path + " does not exist"); + } + + // Get relative path of the folder + var folderPath = Path.GetDirectoryName(path); + + return new LocalFolder(folderPath, fileInfo.Directory); + } + + public long CountFiles(string path, string pattern, Func predicate, bool deep = true) + { + var files = SearchFiles(path, pattern, deep).AsParallel(); + + if (predicate != null) + { + return files.Count(predicate); + } + else + { + return files.Count(); + } + } + + public IEnumerable SearchFiles(string path, string pattern, bool deep = true) + { + // Get relative from absolute path + var index = _storagePath.EmptyNull().Length; + + return Directory + .EnumerateFiles(MapStorage(path), pattern, deep ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly) + .Select(x => x.Substring(index)); + } + + public IEnumerable ListFiles(string path) + { + var directoryInfo = new DirectoryInfo(MapStorage(path)); + + if (!directoryInfo.Exists) + { + throw new DirectoryNotFoundException("Directory " + path + " does not exist"); + } + + return directoryInfo + .EnumerateFiles() + .Where(fi => !IsHidden(fi)) + .Select(fi => new LocalFile(Path.Combine(path, fi.Name), fi)); + } + + public IEnumerable ListFolders(string path) + { + var directoryInfo = new DirectoryInfo(MapStorage(path)); + + if (!directoryInfo.Exists) + { + try + { + directoryInfo.Create(); + } + catch (Exception ex) + { + if (ex.IsFatal()) + { + throw; + } + throw new ArgumentException(string.Format("The folder could not be created at path: {0}. {1}", path, ex)); + } + } + + return directoryInfo + .EnumerateDirectories() + .Where(di => !IsHidden(di)) + .Select(di => new LocalFolder(Path.Combine(path, di.Name), di)); + } + + private static bool IsHidden(FileSystemInfo di) + { + return (di.Attributes & FileAttributes.Hidden) != 0; + } + + public IFolder CreateFolder(string path) + { + return new LocalFolder(path, Directory.CreateDirectory(MapStorage(path))); + } + + public void DeleteFolder(string path) + { + var directoryInfo = new DirectoryInfo(MapStorage(path)); + + if (!directoryInfo.Exists) + { + throw new DirectoryNotFoundException("Directory " + path + " does not exist."); + } + + directoryInfo.Delete(true); + } + + public void RenameFolder(string path, string newPath) + { + var sourceDirectory = new DirectoryInfo(MapStorage(path)); + if (!sourceDirectory.Exists) + { + throw new DirectoryNotFoundException("Directory " + path + "does not exist."); + } + + var targetDirectory = new DirectoryInfo(MapStorage(newPath)); + if (targetDirectory.Exists) + { + throw new ArgumentException("Directory " + newPath + " already exists."); + } + + Directory.Move(sourceDirectory.FullName, targetDirectory.FullName); + } + + public void CopyFolder(string path, string destinationPath, bool overwrite = true) + { + var sourceDirectory = new DirectoryInfo(MapStorage(path)); + if (!sourceDirectory.Exists) + { + throw new DirectoryNotFoundException("Directory " + path + "does not exist."); + } + + var targetPath = Combine(MapStorage(destinationPath), sourceDirectory.Name); + var destDirectory = new DirectoryInfo(targetPath); + if (!overwrite && destDirectory.Exists) + { + throw new ArgumentException("Directory " + destinationPath + " already exists."); + } + + if (!destDirectory.Exists) + { + destDirectory.Create(); + } + + FileSystemHelper.CopyDirectory(sourceDirectory, destDirectory, overwrite); + } + + public bool CheckUniqueFileName(string path, out string newPath) + { + Guard.NotEmpty(path, nameof(path)); + + newPath = null; + + var file = GetFile(path); + if (!file.Exists) + { + return false; + } + + var pattern = string.Concat(file.Title, "-*", file.Extension); + var dir = file.Directory; + var files = new HashSet(SearchFiles(dir, pattern, false).Select(x => Path.GetFileName(x)), StringComparer.OrdinalIgnoreCase); + + int i = 1; + while (true) + { + var fileName = string.Concat(file.Title, "-", i, file.Extension); + if (!files.Contains(fileName)) + { + // Found our gap + newPath = Combine(dir, fileName); + return true; + } + + i++; + } + } + + public IFile CreateFile(string path) + { + var fileInfo = new FileInfo(MapStorage(path)); + + if (fileInfo.Exists) + { + throw new ArgumentException("File " + path + " already exists"); + } + + // Ensure the directory exists + var dirName = Path.GetDirectoryName(fileInfo.FullName); + if (!Directory.Exists(dirName)) + { + Directory.CreateDirectory(dirName); + } + + File.WriteAllBytes(fileInfo.FullName, new byte[0]); + + fileInfo.Refresh(); + return new LocalFile(path, fileInfo); + } + + public async Task CreateFileAsync(string path) + { + var fileInfo = new FileInfo(MapStorage(path)); + + if (fileInfo.Exists) + { + throw new ArgumentException("File " + path + " already exists"); + } + + // ensure the directory exists + var dirName = Path.GetDirectoryName(fileInfo.FullName); + if (!Directory.Exists(dirName)) + { + Directory.CreateDirectory(dirName); + } + + using (FileStream stream = new FileStream(fileInfo.FullName, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 4096, useAsync: true)) + { + await stream.WriteAsync(new byte[0], 0, 0); + } + + fileInfo.Refresh(); + return new LocalFile(path, fileInfo); + } + + public void DeleteFile(string path) + { + var fileInfo = new FileInfo(MapStorage(path)); + + if (fileInfo.Exists) + { + WaitForUnlockAndExecute(fileInfo, x => x.Delete()); + } + } + + public void RenameFile(string path, string newPath) + { + var sourceFileInfo = new FileInfo(MapStorage(path)); + if (!sourceFileInfo.Exists) + { + throw new FileNotFoundException("File " + path + " does not exist."); + } + + var targetFileInfo = new FileInfo(MapStorage(newPath)); + if (targetFileInfo.Exists) + { + throw new ArgumentException("File " + newPath + " already exists."); + } + + WaitForUnlockAndExecute(sourceFileInfo, x => File.Move(x.FullName, targetFileInfo.FullName)); + } + + public void CopyFile(string path, string newPath, bool overwrite = false) + { + var sourceFileInfo = new FileInfo(MapStorage(path)); + if (!sourceFileInfo.Exists) + { + throw new FileNotFoundException("File " + path + " does not exist"); + } + + var targetPath = MapStorage(newPath); + if (!overwrite) + { + var targetFileInfo = new FileInfo(targetPath); + if (targetFileInfo.Exists) + { + throw new ArgumentException("File " + newPath + " already exists"); + } + } + + WaitForUnlockAndExecute(sourceFileInfo, x => File.Copy(x.FullName, targetPath, overwrite)); + } + + public void SaveStream(string path, Stream inputStream) + { + using (var outputStream = File.OpenWrite(MapStorage(path))) + { + outputStream.SetLength(0); + inputStream.CopyTo(outputStream); + } + } + + public async Task SaveStreamAsync(string path, Stream inputStream) + { + using (var outputStream = File.OpenWrite(MapStorage(path))) + { + outputStream.SetLength(0); + await inputStream.CopyToAsync(outputStream); + } + } + + public string Combine(string path1, string path2) + { + return Path.Combine(path1, path2); + } + + private static void WaitForUnlockAndExecute(FileInfo fi, Action action) + { + try + { + action(fi); + } + catch (IOException) + { + if (!fi.WaitForUnlock(250)) + { + throw; + } + + action(fi); + } + } + + /// + /// Determines if a path lies within the base path boundaries. + /// If not, an exception is thrown. + /// + /// The base path which boundaries are not to be transposed. + /// The path to determine. + /// The mapped path if valid. + /// If the path is invalid. + internal static string ValidatePath(string basePath, string mappedPath) + { + bool valid = false; + string error = null; + + try + { + // Check that we are indeed within the storage directory boundaries + valid = Path.GetFullPath(mappedPath).StartsWith(Path.GetFullPath(basePath), StringComparison.OrdinalIgnoreCase); + } + catch (Exception exception) + { + // Make sure that if invalid for medium trust we give a proper exception + valid = false; + error = exception.Message; + } + + if (!valid) + { + throw new ArgumentException($"{error ?? "Invalid path."} mappedPath: {mappedPath.NaIfEmpty()}"); + } + + return mappedPath; + } + + public class LocalFile : IFile + { + private readonly string _localPath; + private readonly string _relativePath; + private string _name; + private FileInfo _fileInfo; + private Size? _dimensions; + + public LocalFile(string relativePath, string localPath) + { + _relativePath = Fix(relativePath); + _localPath = localPath; + } + + public LocalFile(string relativePath, FileInfo fileInfo) + { + _relativePath = Fix(relativePath); + _localPath = fileInfo.FullName; + _fileInfo = fileInfo; + } + + private FileInfo GetFileInfo() + { + return _fileInfo ?? (_fileInfo = new FileInfo(_localPath)); + } + + public string Path => _relativePath; + + public string Directory => _relativePath.Substring(0, _relativePath.Length - Name.Length); + + public string Name => _fileInfo?.Name ?? _name ?? (_name = System.IO.Path.GetFileName(_localPath)); + + public string Title => System.IO.Path.GetFileNameWithoutExtension(_localPath); + + public long Size => GetFileInfo().Length; + + public DateTime LastUpdated => GetFileInfo().LastWriteTimeUtc; + + public string Extension => _fileInfo?.Extension ?? System.IO.Path.GetExtension(_localPath); + + public Size Dimensions + { + get + { + if (_dimensions == null) + { + try + { + var mime = MimeTypes.MapNameToMimeType(Name); + _dimensions = ImageHeader.GetDimensions(OpenRead(), mime, false); + } + catch + { + _dimensions = new Size(); + } + } + + return _dimensions.Value; + } + internal set => _dimensions = value; + } + + public bool Exists + { + get + { + if (_fileInfo != null) + return _fileInfo.Exists; + + return System.IO.File.Exists(_localPath); + } + } + + public Stream OpenRead() + { + return new FileStream(_localPath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true); + } + + public Stream OpenWrite() + { + var fi = GetFileInfo(); + if (!fi.Directory.Exists) + { + System.IO.Directory.CreateDirectory(fi.Directory.FullName); + } + + return new FileStream(_localPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true); + } + + public Stream CreateFile() + { + return new FileStream(_localPath, FileMode.Truncate, FileAccess.ReadWrite, FileShare.Read, bufferSize: 4096, useAsync: true); + } + + public Task CreateFileAsync() + { + return Task.Run(() => CreateFile()); + } + } + + public class LocalFolder : IFolder + { + private readonly string _localPath; + private readonly string _relativePath; + private string _name; + private DirectoryInfo _dirInfo; + + public LocalFolder(string relativePath, string localPath) + { + _relativePath = Fix(relativePath); + _localPath = localPath; + } + + public LocalFolder(string relativePath, DirectoryInfo dirInfo) + { + _relativePath = Fix(relativePath); + _localPath = dirInfo.FullName; + _dirInfo = dirInfo; + } + + private DirectoryInfo GetDirInfo() + { + return _dirInfo ?? (_dirInfo = new DirectoryInfo(_localPath)); + } + + public string Path => _relativePath; + + public string Name => _dirInfo?.Name ?? _name ?? (_name = GetDirName(_relativePath)); + + public DateTime LastUpdated => GetDirInfo().LastWriteTimeUtc; + + public long Size => GetDirectorySize(GetDirInfo()); + + public bool Exists + { + get + { + if (_dirInfo != null) + return _dirInfo.Exists; + + return System.IO.Directory.Exists(_localPath); + } + } + + public IFolder Parent + { + get + { + var parent = GetDirInfo().Parent; + + if (parent != null) + { + return new LocalFolder(System.IO.Path.GetDirectoryName(_relativePath), parent); + } + + throw new InvalidOperationException("Directory " + _dirInfo.Name + " does not have a parent directory"); + } + } + + private static string GetDirName(string fullPath) + { + if (fullPath.Length > 3) + { + string path = fullPath.TrimEnd(System.IO.Path.DirectorySeparatorChar); + return System.IO.Path.GetFileName(path); + } + + return fullPath; + } + + + + private static long GetDirectorySize(DirectoryInfo directoryInfo) + { + long size = 0; + + FileInfo[] fileInfos = directoryInfo.GetFiles(); + foreach (FileInfo fileInfo in fileInfos) + { + if (!IsHidden(fileInfo)) + { + size += fileInfo.Length; + } + } + DirectoryInfo[] directoryInfos = directoryInfo.GetDirectories(); + foreach (DirectoryInfo dInfo in directoryInfos) + { + if (!IsHidden(dInfo)) + { + size += GetDirectorySize(dInfo); + } + } + + return size; + } + } + + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/IO/LockFile/ILockFile.cs b/src/Libraries/SmartStore.Core/IO/LockFile/ILockFile.cs index 1856377808..32b901619a 100644 --- a/src/Libraries/SmartStore.Core/IO/LockFile/ILockFile.cs +++ b/src/Libraries/SmartStore.Core/IO/LockFile/ILockFile.cs @@ -1,19 +1,15 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.Core.IO { - /// - /// Represents a Lock File acquired on the file system - /// - /// - /// The instance needs to be disposed in order to release the lock explicitly - /// - public interface ILockFile : IDisposable - { - void Release(); - } + /// + /// Represents a Lock File acquired on the file system + /// + /// + /// The instance needs to be disposed in order to release the lock explicitly + /// + public interface ILockFile : IDisposable + { + void Release(); + } } diff --git a/src/Libraries/SmartStore.Core/IO/LockFile/ILockFileManager.cs b/src/Libraries/SmartStore.Core/IO/LockFile/ILockFileManager.cs index 9cd529bf1a..f3f04237cb 100644 --- a/src/Libraries/SmartStore.Core/IO/LockFile/ILockFileManager.cs +++ b/src/Libraries/SmartStore.Core/IO/LockFile/ILockFileManager.cs @@ -1,28 +1,26 @@ -using System; - -namespace SmartStore.Core.IO +namespace SmartStore.Core.IO { - /// - /// Abstraction for application-wide lock files creation. - /// - /// - /// All virtual paths passed in or returned are relative to "~/App_Data". - /// - public interface ILockFileManager - { - /// - /// Attempts to acquire an exclusive lock file. - /// - /// The filename of the lock file to create relative to ~/App_Data. - /// A reference to the lock file object if the lock is granted. - /// true if the lock is granted; otherwise, false. - bool TryAcquireLock(string path, out ILockFile lockFile); + /// + /// Abstraction for application-wide lock files creation. + /// + /// + /// All virtual paths passed in or returned are relative to "~/App_Data". + /// + public interface ILockFileManager + { + /// + /// Attempts to acquire an exclusive lock file. + /// + /// The filename of the lock file to create relative to ~/App_Data. + /// A reference to the lock file object if the lock is granted. + /// true if the lock is granted; otherwise, false. + bool TryAcquireLock(string path, out ILockFile lockFile); - /// - /// Wether a lock file is already existing. - /// - /// The filename of the lock file to test. - /// true if the lock file exists; otherwise, false. - bool IsLocked(string path); - } + /// + /// Wether a lock file is already existing. + /// + /// The filename of the lock file to test. + /// true if the lock file exists; otherwise, false. + bool IsLocked(string path); + } } diff --git a/src/Libraries/SmartStore.Core/IO/LockFile/LockFile.cs b/src/Libraries/SmartStore.Core/IO/LockFile/LockFile.cs index 1693635837..f4277071bc 100644 --- a/src/Libraries/SmartStore.Core/IO/LockFile/LockFile.cs +++ b/src/Libraries/SmartStore.Core/IO/LockFile/LockFile.cs @@ -1,54 +1,53 @@ -using System; +using System.IO; using System.Threading; -using System.IO; using SmartStore.Utilities.Threading; namespace SmartStore.Core.IO { - public class LockFile : ILockFile - { - private readonly string _path; - private readonly string _content; - private readonly IVirtualFolder _folder; - private readonly ReaderWriterLockSlim _rwLock; - private bool _released; + public class LockFile : ILockFile + { + private readonly string _path; + private readonly string _content; + private readonly IVirtualFolder _folder; + private readonly ReaderWriterLockSlim _rwLock; + private bool _released; - public LockFile(IVirtualFolder folder, string path, string content, ReaderWriterLockSlim rwLock) - { - _folder = folder; - _content = content; - _rwLock = rwLock; - _path = path; + public LockFile(IVirtualFolder folder, string path, string content, ReaderWriterLockSlim rwLock) + { + _folder = folder; + _content = content; + _rwLock = rwLock; + _path = path; - // create the physical lock file - _folder.CreateTextFile(_path, content); - } + // create the physical lock file + _folder.CreateTextFile(_path, content); + } - public void Dispose() - { - Release(); - } + public void Dispose() + { + Release(); + } - public void Release() - { - using (_rwLock.GetWriteLock()) - { - if (_released || !File.Exists(_folder.MapPath(_path))) - { - // nothing to do, might happen if re-granted or already released - // INFO: VirtualPathProvider caches file existence info, so not very reliable here. - return; - } + public void Release() + { + using (_rwLock.GetWriteLock()) + { + if (_released || !File.Exists(_folder.MapPath(_path))) + { + // nothing to do, might happen if re-granted or already released + // INFO: VirtualPathProvider caches file existence info, so not very reliable here. + return; + } - _released = true; + _released = true; - // check it has not been granted in the meantime - var current = _folder.ReadFile(_path); - if (current == _content) - { - _folder.DeleteFile(_path); - } - } - } - } + // check it has not been granted in the meantime + var current = _folder.ReadFile(_path); + if (current == _content) + { + _folder.DeleteFile(_path); + } + } + } + } } diff --git a/src/Libraries/SmartStore.Core/IO/LockFile/LockFileManager.cs b/src/Libraries/SmartStore.Core/IO/LockFile/LockFileManager.cs index 20d00e3441..0965d810f2 100644 --- a/src/Libraries/SmartStore.Core/IO/LockFile/LockFileManager.cs +++ b/src/Libraries/SmartStore.Core/IO/LockFile/LockFileManager.cs @@ -1,95 +1,95 @@ using System; using System.Globalization; -using System.Threading; using System.IO; +using System.Threading; using SmartStore.Utilities.Threading; namespace SmartStore.Core.IO { - public class LockFileManager : ILockFileManager - { - private readonly IApplicationEnvironment _env; - private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); + public class LockFileManager : ILockFileManager + { + private readonly IApplicationEnvironment _env; + private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); + + static LockFileManager() + { + Expiration = TimeSpan.FromMinutes(10); + } - static LockFileManager() - { - Expiration = TimeSpan.FromMinutes(10); - } + public LockFileManager(IApplicationEnvironment env) + { + _env = env; + } - public LockFileManager(IApplicationEnvironment env) - { - _env = env; - } + public static TimeSpan Expiration + { + get; + private set; + } - public static TimeSpan Expiration - { - get; - private set; - } + public bool TryAcquireLock(string path, out ILockFile lockFile) + { + lockFile = null; - public bool TryAcquireLock(string path, out ILockFile lockFile) - { - lockFile = null; + if (!_rwLock.TryEnterWriteLock(0)) + { + return false; + } - if (!_rwLock.TryEnterWriteLock(0)) - { - return false; - } + try + { + if (IsLockedInternal(path)) + { + return false; + } - try - { - if (IsLockedInternal(path)) - { - return false; - } - - lockFile = new LockFile(_env.TenantFolder, path, DateTime.UtcNow.ToString("u"), _rwLock); - return true; - } - catch - { - // an error occured while reading/creating the lock file - return false; - } - finally - { - _rwLock.ExitWriteLock(); - } - } + lockFile = new LockFile(_env.TenantFolder, path, DateTime.UtcNow.ToString("u"), _rwLock); + return true; + } + catch + { + // an error occured while reading/creating the lock file + return false; + } + finally + { + _rwLock.ExitWriteLock(); + } + } - public bool IsLocked(string path) - { - using (_rwLock.GetWriteLock()) - { - try - { - return IsLockedInternal(path); - } - catch - { - // an error occured while reading the file - return true; - } - } - } + public bool IsLocked(string path) + { + using (_rwLock.GetWriteLock()) + { + try + { + return IsLockedInternal(path); + } + catch + { + // an error occured while reading the file + return true; + } + } + } - private bool IsLockedInternal(string path) - { - // INFO: VirtualPathProvider caches file existence info, so not very reliable here. - if (File.Exists(_env.TenantFolder.MapPath(path))) - { - var content = _env.TenantFolder.ReadFile(path); + private bool IsLockedInternal(string path) + { + // INFO: VirtualPathProvider caches file existence info, so not very reliable here. + if (File.Exists(_env.TenantFolder.MapPath(path))) + { + var content = _env.TenantFolder.ReadFile(path); - if (DateTime.TryParse(content, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var creationUtc)) - { - // if expired the file is not removed - // it should be automatically as there is a finalizer in LockFile - // or the next taker can do it, unless it also fails, again - return creationUtc.ToUniversalTime().Add(Expiration) > DateTime.UtcNow; - } - } + if (DateTime.TryParse(content, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var creationUtc)) + { + // if expired the file is not removed + // it should be automatically as there is a finalizer in LockFile + // or the next taker can do it, unless it also fails, again + return creationUtc.ToUniversalTime().Add(Expiration) > DateTime.UtcNow; + } + } - return false; - } - } + return false; + } + } } diff --git a/src/Libraries/SmartStore.Core/IO/MimeTypes.cs b/src/Libraries/SmartStore.Core/IO/MimeTypes.cs index 02ecb8dd82..50c8e79c3c 100644 --- a/src/Libraries/SmartStore.Core/IO/MimeTypes.cs +++ b/src/Libraries/SmartStore.Core/IO/MimeTypes.cs @@ -1,765 +1,766 @@ using System; -using System.IO; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.IO; using Microsoft.Win32; namespace SmartStore.Core.IO -{ +{ public static class MimeTypes { - private static readonly char[] _pathSeparatorChars = new [] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, Path.VolumeSeparatorChar }; - private static readonly Dictionary _mimeMap; - private static readonly Dictionary _extMap; + private static readonly char[] _pathSeparatorChars = new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar, Path.VolumeSeparatorChar }; + private static readonly Dictionary _mimeMap; + private static readonly Dictionary _extMap; - const string DefaultMimeType = "application/octet-stream"; + const string DefaultMimeType = "application/octet-stream"; - static MimeTypes() - { - // maps both ways, - // extension -> mime type - // and - // mime type -> extension - // - // any mime type on left side not pre-loaded on right side, are added automatically. - // Some mime types can map to multiple extensions, so to get a deterministic mapping, - // add those to the dictionary specifcially - // - // Some added based on http://www.iana.org/assignments/media-types/media-types.xhtml - // which lists mime types, but not extensions - // + static MimeTypes() + { + // maps both ways, + // extension -> mime type + // and + // mime type -> extension + // + // any mime type on left side not pre-loaded on right side, are added automatically. + // Some mime types can map to multiple extensions, so to get a deterministic mapping, + // add those to the dictionary specifcially + // + // Some added based on http://www.iana.org/assignments/media-types/media-types.xhtml + // which lists mime types, but not extensions + // - _mimeMap = new Dictionary(StringComparer.OrdinalIgnoreCase) - { + _mimeMap = new Dictionary(StringComparer.OrdinalIgnoreCase) + { #region Mime types > extension map {"application/fsharp-script", ".fsx"}, - {"application/msaccess", ".adp"}, - {"application/msword", ".doc"}, - {"application/octet-stream", ".bin"}, - {"application/onenote", ".one"}, - {"application/postscript", ".eps"}, - {"application/step", ".step"}, - {"application/vnd.ms-excel", ".xls"}, - {"application/vnd.ms-powerpoint", ".ppt"}, - {"application/vnd.ms-works", ".wks"}, - {"application/vnd.visio", ".vsd"}, - {"application/x-director", ".dir"}, - {"application/x-shockwave-flash", ".swf"}, - {"application/x-x509-ca-cert", ".cer"}, - {"application/x-zip-compressed", ".zip"}, - {"application/xhtml+xml", ".xhtml"}, - {"application/xml", ".xml"}, // anomoly, .xml -> text/xml, but application/xml -> many thingss, but all are xml, so safest is .xml + {"application/msaccess", ".adp"}, + {"application/msword", ".doc"}, + {"application/octet-stream", ".bin"}, + {"application/onenote", ".one"}, + {"application/postscript", ".eps"}, + {"application/step", ".step"}, + {"application/vnd.ms-excel", ".xls"}, + {"application/vnd.ms-powerpoint", ".ppt"}, + {"application/vnd.ms-works", ".wks"}, + {"application/vnd.visio", ".vsd"}, + {"application/x-director", ".dir"}, + {"application/x-shockwave-flash", ".swf"}, + {"application/x-x509-ca-cert", ".cer"}, + {"application/x-zip-compressed", ".zip"}, + {"application/xhtml+xml", ".xhtml"}, + {"application/xml", ".xml"}, // anomoly, .xml -> text/xml, but application/xml -> many thingss, but all are xml, so safest is .xml {"audio/aac", ".AAC"}, - {"audio/aiff", ".aiff"}, - {"audio/basic", ".snd"}, - {"audio/mid", ".midi"}, - {"audio/wav", ".wav"}, - {"audio/x-m4a", ".m4a"}, - {"audio/x-mpegurl", ".m3u"}, - {"audio/x-pn-realaudio", ".ra"}, - {"audio/x-smd", ".smd"}, - {"image/bmp", ".bmp"}, - {"image/jpeg", ".jpg"}, - {"image/pict", ".pic"}, - {"image/png", ".png"}, //Defined in [RFC-2045], [RFC-2048] + {"audio/aiff", ".aiff"}, + {"audio/basic", ".snd"}, + {"audio/mid", ".midi"}, + {"audio/wav", ".wav"}, + {"audio/x-m4a", ".m4a"}, + {"audio/x-mpegurl", ".m3u"}, + {"audio/x-pn-realaudio", ".ra"}, + {"audio/x-smd", ".smd"}, + {"image/bmp", ".bmp"}, + {"image/jpeg", ".jpg"}, + {"image/pict", ".pic"}, + {"image/png", ".png"}, //Defined in [RFC-2045], [RFC-2048] {"image/x-png", ".png"}, //See https://www.w3.org/TR/PNG/#A-Media-type :"It is recommended that implementations also recognize the media type "image/x-png"." {"image/tiff", ".tiff"}, - {"image/x-macpaint", ".mac"}, - {"image/x-quicktime", ".qti"}, - {"message/rfc822", ".eml"}, - {"text/html", ".html"}, - {"text/plain", ".txt"}, - {"text/scriptlet", ".wsc"}, - {"text/xml", ".xml"}, - {"video/3gpp", ".3gp"}, - {"video/3gpp2", ".3gp2"}, - {"video/mp4", ".mp4"}, - {"video/mpeg", ".mpg"}, - {"video/quicktime", ".mov"}, - {"video/vnd.dlna.mpeg-tts", ".m2t"}, - {"video/x-dv", ".dv"}, - {"video/x-la-asf", ".lsf"}, - {"video/x-ms-asf", ".asf"}, - {"x-world/x-vrml", ".xof"}, + {"image/x-macpaint", ".mac"}, + {"image/x-quicktime", ".qti"}, + {"message/rfc822", ".eml"}, + {"text/html", ".html"}, + {"text/plain", ".txt"}, + {"text/scriptlet", ".wsc"}, + {"text/xml", ".xml"}, + {"video/3gpp", ".3gp"}, + {"video/3gpp2", ".3gp2"}, + {"video/mp4", ".mp4"}, + {"video/mpeg", ".mpg"}, + {"video/quicktime", ".mov"}, + {"video/vnd.dlna.mpeg-tts", ".m2t"}, + {"video/x-dv", ".dv"}, + {"video/x-la-asf", ".lsf"}, + {"video/x-ms-asf", ".asf"}, + {"x-world/x-vrml", ".xof"}, #endregion }; - _extMap = new Dictionary(StringComparer.OrdinalIgnoreCase) - { + _extMap = new Dictionary(StringComparer.OrdinalIgnoreCase) + { #region Extension > mime type map {".323", "text/h323"}, - {".3g2", "video/3gpp2"}, - {".3gp", "video/3gpp"}, - {".3gp2", "video/3gpp2"}, - {".3gpp", "video/3gpp"}, - {".7z", "application/x-7z-compressed"}, - {".aa", "audio/audible"}, - {".AAC", "audio/aac"}, - {".aaf", "application/octet-stream"}, - {".aax", "audio/vnd.audible.aax"}, - {".ac3", "audio/ac3"}, - {".aca", "application/octet-stream"}, - {".accda", "application/msaccess.addin"}, - {".accdb", "application/msaccess"}, - {".accdc", "application/msaccess.cab"}, - {".accde", "application/msaccess"}, - {".accdr", "application/msaccess.runtime"}, - {".accdt", "application/msaccess"}, - {".accdw", "application/msaccess.webapplication"}, - {".accft", "application/msaccess.ftemplate"}, - {".acx", "application/internet-property-stream"}, - {".AddIn", "text/xml"}, - {".ade", "application/msaccess"}, - {".adobebridge", "application/x-bridge-url"}, - {".adp", "application/msaccess"}, - {".ADT", "audio/vnd.dlna.adts"}, - {".ADTS", "audio/aac"}, - {".afm", "application/octet-stream"}, - {".ai", "application/postscript"}, - {".aif", "audio/aiff"}, - {".aifc", "audio/aiff"}, - {".aiff", "audio/aiff"}, - {".air", "application/vnd.adobe.air-application-installer-package+zip"}, - {".amc", "application/mpeg"}, - {".anx", "application/annodex"}, - {".apk", "application/vnd.android.package-archive" }, - {".application", "application/x-ms-application"}, - {".art", "image/x-jg"}, - {".asa", "application/xml"}, - {".asax", "application/xml"}, - {".ascx", "application/xml"}, - {".asd", "application/octet-stream"}, - {".asf", "video/x-ms-asf"}, - {".ashx", "application/xml"}, - {".asi", "application/octet-stream"}, - {".asm", "text/plain"}, - {".asmx", "application/xml"}, - {".aspx", "application/xml"}, - {".asr", "video/x-ms-asf"}, - {".asx", "video/x-ms-asf"}, - {".atom", "application/atom+xml"}, - {".au", "audio/basic"}, - {".avi", "video/x-msvideo"}, - {".axa", "audio/annodex"}, - {".axs", "application/olescript"}, - {".axv", "video/annodex"}, - {".bas", "text/plain"}, - {".bcpio", "application/x-bcpio"}, - {".bin", "application/octet-stream"}, - {".bmp", "image/bmp"}, - {".c", "text/plain"}, - {".cab", "application/octet-stream"}, - {".caf", "audio/x-caf"}, - {".calx", "application/vnd.ms-office.calx"}, - {".cat", "application/vnd.ms-pki.seccat"}, - {".cc", "text/plain"}, - {".cd", "text/plain"}, - {".cdda", "audio/aiff"}, - {".cdf", "application/x-cdf"}, - {".cer", "application/x-x509-ca-cert"}, - {".cfg", "text/plain"}, - {".chm", "application/octet-stream"}, - {".class", "application/x-java-applet"}, - {".clp", "application/x-msclip"}, - {".cmd", "text/plain"}, - {".cmx", "image/x-cmx"}, - {".cnf", "text/plain"}, - {".cod", "image/cis-cod"}, - {".config", "application/xml"}, - {".contact", "text/x-ms-contact"}, - {".coverage", "application/xml"}, - {".cpio", "application/x-cpio"}, - {".cpp", "text/plain"}, - {".crd", "application/x-mscardfile"}, - {".crl", "application/pkix-crl"}, - {".crt", "application/x-x509-ca-cert"}, - {".cs", "text/plain"}, - {".csdproj", "text/plain"}, - {".csh", "application/x-csh"}, - {".csproj", "text/plain"}, - {".css", "text/css"}, - {".csv", "text/csv"}, - {".cur", "application/octet-stream"}, - {".cxx", "text/plain"}, - {".dat", "application/octet-stream"}, - {".datasource", "application/xml"}, - {".dbproj", "text/plain"}, - {".dcr", "application/x-director"}, - {".def", "text/plain"}, - {".deploy", "application/octet-stream"}, - {".der", "application/x-x509-ca-cert"}, - {".dgml", "application/xml"}, - {".dib", "image/bmp"}, - {".dif", "video/x-dv"}, - {".dir", "application/x-director"}, - {".disco", "text/xml"}, - {".divx", "video/divx"}, - {".dll", "application/x-msdownload"}, - {".dll.config", "text/xml"}, - {".dlm", "text/dlm"}, - {".doc", "application/msword"}, - {".docm", "application/vnd.ms-word.document.macroEnabled.12"}, - {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, - {".dot", "application/msword"}, - {".dotm", "application/vnd.ms-word.template.macroEnabled.12"}, - {".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"}, - {".dsp", "application/octet-stream"}, - {".dsw", "text/plain"}, - {".dtd", "text/xml"}, - {".dtsConfig", "text/xml"}, - {".dv", "video/x-dv"}, - {".dvi", "application/x-dvi"}, - {".dwf", "drawing/x-dwf"}, - {".dwg", "application/acad"}, - {".dwp", "application/octet-stream"}, - {".dxf", "application/x-dxf" }, - {".dxr", "application/x-director"}, - {".eml", "message/rfc822"}, - {".emz", "application/octet-stream"}, - {".eot", "application/vnd.ms-fontobject"}, - {".eps", "application/postscript"}, - {".etl", "application/etl"}, - {".etx", "text/x-setext"}, - {".evy", "application/envoy"}, - {".exe", "application/octet-stream"}, - {".exe.config", "text/xml"}, - {".fdf", "application/vnd.fdf"}, - {".fif", "application/fractals"}, - {".filters", "application/xml"}, - {".fla", "application/octet-stream"}, - {".flac", "audio/flac"}, - {".flr", "x-world/x-vrml"}, - {".flv", "video/x-flv"}, - {".fsscript", "application/fsharp-script"}, - {".fsx", "application/fsharp-script"}, - {".generictest", "application/xml"}, - {".gif", "image/gif"}, - {".gpx", "application/gpx+xml"}, - {".group", "text/x-ms-group"}, - {".gsm", "audio/x-gsm"}, - {".gtar", "application/x-gtar"}, - {".gz", "application/x-gzip"}, - {".h", "text/plain"}, - {".hdf", "application/x-hdf"}, - {".hdml", "text/x-hdml"}, - {".hhc", "application/x-oleobject"}, - {".hhk", "application/octet-stream"}, - {".hhp", "application/octet-stream"}, - {".hlp", "application/winhlp"}, - {".hpp", "text/plain"}, - {".hqx", "application/mac-binhex40"}, - {".hta", "application/hta"}, - {".htc", "text/x-component"}, - {".htm", "text/html"}, - {".html", "text/html"}, - {".htt", "text/webviewhtml"}, - {".hxa", "application/xml"}, - {".hxc", "application/xml"}, - {".hxd", "application/octet-stream"}, - {".hxe", "application/xml"}, - {".hxf", "application/xml"}, - {".hxh", "application/octet-stream"}, - {".hxi", "application/octet-stream"}, - {".hxk", "application/xml"}, - {".hxq", "application/octet-stream"}, - {".hxr", "application/octet-stream"}, - {".hxs", "application/octet-stream"}, - {".hxt", "text/html"}, - {".hxv", "application/xml"}, - {".hxw", "application/octet-stream"}, - {".hxx", "text/plain"}, - {".i", "text/plain"}, - {".ico", "image/x-icon"}, - {".ics", "application/octet-stream"}, - {".idl", "text/plain"}, - {".ief", "image/ief"}, - {".iii", "application/x-iphone"}, - {".inc", "text/plain"}, - {".inf", "application/octet-stream"}, - {".ini", "text/plain"}, - {".inl", "text/plain"}, - {".ins", "application/x-internet-signup"}, - {".ipa", "application/x-itunes-ipa"}, - {".ipg", "application/x-itunes-ipg"}, - {".ipproj", "text/plain"}, - {".ipsw", "application/x-itunes-ipsw"}, - {".iqy", "text/x-ms-iqy"}, - {".isp", "application/x-internet-signup"}, - {".ite", "application/x-itunes-ite"}, - {".itlp", "application/x-itunes-itlp"}, - {".itms", "application/x-itunes-itms"}, - {".itpc", "application/x-itunes-itpc"}, - {".IVF", "video/x-ivf"}, - {".jar", "application/java-archive"}, - {".java", "application/octet-stream"}, - {".jck", "application/liquidmotion"}, - {".jcz", "application/liquidmotion"}, - {".jfif", "image/pjpeg"}, - {".jnlp", "application/x-java-jnlp-file"}, - {".jpb", "application/octet-stream"}, - {".jpe", "image/jpeg"}, - {".jpeg", "image/jpeg"}, - {".jpg", "image/jpeg"}, - {".js", "application/javascript"}, - {".json", "application/json"}, - {".jsx", "text/jscript"}, - {".jsxbin", "text/plain"}, - {".latex", "application/x-latex"}, - {".library-ms", "application/windows-library+xml"}, - {".lit", "application/x-ms-reader"}, - {".loadtest", "application/xml"}, - {".lpk", "application/octet-stream"}, - {".lsf", "video/x-la-asf"}, - {".lst", "text/plain"}, - {".lsx", "video/x-la-asf"}, - {".lzh", "application/octet-stream"}, - {".m13", "application/x-msmediaview"}, - {".m14", "application/x-msmediaview"}, - {".m1v", "video/mpeg"}, - {".m2t", "video/vnd.dlna.mpeg-tts"}, - {".m2ts", "video/vnd.dlna.mpeg-tts"}, - {".m2v", "video/mpeg"}, - {".m3u", "audio/x-mpegurl"}, - {".m3u8", "audio/x-mpegurl"}, - {".m4a", "audio/m4a"}, - {".m4b", "audio/m4b"}, - {".m4p", "audio/m4p"}, - {".m4r", "audio/x-m4r"}, - {".m4v", "video/x-m4v"}, - {".mac", "image/x-macpaint"}, - {".mak", "text/plain"}, - {".man", "application/x-troff-man"}, - {".manifest", "application/x-ms-manifest"}, - {".map", "text/plain"}, - {".master", "application/xml"}, - {".mbox", "application/mbox"}, - {".mda", "application/msaccess"}, - {".mdb", "application/x-msaccess"}, - {".mde", "application/msaccess"}, - {".mdp", "application/octet-stream"}, - {".me", "application/x-troff-me"}, - {".mfp", "application/x-shockwave-flash"}, - {".mht", "message/rfc822"}, - {".mhtml", "message/rfc822"}, - {".mid", "audio/mid"}, - {".midi", "audio/mid"}, - {".mix", "application/octet-stream"}, - {".mk", "text/plain"}, - {".mk3d", "video/x-matroska-3d"}, - {".mka", "audio/x-matroska"}, - {".mkv", "video/x-matroska"}, - {".mmf", "application/x-smaf"}, - {".mno", "text/xml"}, - {".mny", "application/x-msmoney"}, - {".mod", "video/mpeg"}, - {".mov", "video/quicktime"}, - {".movie", "video/x-sgi-movie"}, - {".mp2", "video/mpeg"}, - {".mp2v", "video/mpeg"}, - {".mp3", "audio/mpeg"}, - {".mp4", "video/mp4"}, - {".mp4v", "video/mp4"}, - {".mpa", "video/mpeg"}, - {".mpe", "video/mpeg"}, - {".mpeg", "video/mpeg"}, - {".mpf", "application/vnd.ms-mediapackage"}, - {".mpg", "video/mpeg"}, - {".mpp", "application/vnd.ms-project"}, - {".mpv2", "video/mpeg"}, - {".mqv", "video/quicktime"}, - {".ms", "application/x-troff-ms"}, - {".msg", "application/vnd.ms-outlook"}, - {".msi", "application/octet-stream"}, - {".mso", "application/octet-stream"}, - {".mts", "video/vnd.dlna.mpeg-tts"}, - {".mtx", "application/xml"}, - {".mvb", "application/x-msmediaview"}, - {".mvc", "application/x-miva-compiled"}, - {".mxp", "application/x-mmxp"}, - {".nc", "application/x-netcdf"}, - {".nsc", "video/x-ms-asf"}, - {".nws", "message/rfc822"}, - {".ocx", "application/octet-stream"}, - {".oda", "application/oda"}, - {".odb", "application/vnd.oasis.opendocument.database"}, - {".odc", "application/vnd.oasis.opendocument.chart"}, - {".odf", "application/vnd.oasis.opendocument.formula"}, - {".odg", "application/vnd.oasis.opendocument.graphics"}, - {".odh", "text/plain"}, - {".odi", "application/vnd.oasis.opendocument.image"}, - {".odl", "text/plain"}, - {".odm", "application/vnd.oasis.opendocument.text-master"}, - {".odp", "application/vnd.oasis.opendocument.presentation"}, - {".ods", "application/vnd.oasis.opendocument.spreadsheet"}, - {".odt", "application/vnd.oasis.opendocument.text"}, - {".oga", "audio/ogg"}, - {".ogg", "audio/ogg"}, - {".ogv", "video/ogg"}, - {".ogx", "application/ogg"}, - {".one", "application/onenote"}, - {".onea", "application/onenote"}, - {".onepkg", "application/onenote"}, - {".onetmp", "application/onenote"}, - {".onetoc", "application/onenote"}, - {".onetoc2", "application/onenote"}, - {".opus", "audio/ogg"}, - {".orderedtest", "application/xml"}, - {".osdx", "application/opensearchdescription+xml"}, - {".otf", "application/font-sfnt"}, - {".otg", "application/vnd.oasis.opendocument.graphics-template"}, - {".oth", "application/vnd.oasis.opendocument.text-web"}, - {".otp", "application/vnd.oasis.opendocument.presentation-template"}, - {".ots", "application/vnd.oasis.opendocument.spreadsheet-template"}, - {".ott", "application/vnd.oasis.opendocument.text-template"}, - {".oxt", "application/vnd.openofficeorg.extension"}, - {".p10", "application/pkcs10"}, - {".p12", "application/x-pkcs12"}, - {".p7b", "application/x-pkcs7-certificates"}, - {".p7c", "application/pkcs7-mime"}, - {".p7m", "application/pkcs7-mime"}, - {".p7r", "application/x-pkcs7-certreqresp"}, - {".p7s", "application/pkcs7-signature"}, - {".pbm", "image/x-portable-bitmap"}, - {".pcast", "application/x-podcast"}, - {".pct", "image/pict"}, - {".pcx", "application/octet-stream"}, - {".pcz", "application/octet-stream"}, - {".pdf", "application/pdf"}, - {".pfb", "application/octet-stream"}, - {".pfm", "application/octet-stream"}, - {".pfx", "application/x-pkcs12"}, - {".pgm", "image/x-portable-graymap"}, - {".pic", "image/pict"}, - {".pict", "image/pict"}, - {".pkgdef", "text/plain"}, - {".pkgundef", "text/plain"}, - {".pko", "application/vnd.ms-pki.pko"}, - {".pls", "audio/scpls"}, - {".pma", "application/x-perfmon"}, - {".pmc", "application/x-perfmon"}, - {".pml", "application/x-perfmon"}, - {".pmr", "application/x-perfmon"}, - {".pmw", "application/x-perfmon"}, - {".png", "image/png"}, - {".pnm", "image/x-portable-anymap"}, - {".pnt", "image/x-macpaint"}, - {".pntg", "image/x-macpaint"}, - {".pnz", "image/png"}, - {".pot", "application/vnd.ms-powerpoint"}, - {".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"}, - {".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"}, - {".ppa", "application/vnd.ms-powerpoint"}, - {".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"}, - {".ppm", "image/x-portable-pixmap"}, - {".pps", "application/vnd.ms-powerpoint"}, - {".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"}, - {".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"}, - {".ppt", "application/vnd.ms-powerpoint"}, - {".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"}, - {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, - {".prf", "application/pics-rules"}, - {".prm", "application/octet-stream"}, - {".prx", "application/octet-stream"}, - {".ps", "application/postscript"}, - {".psc1", "application/PowerShell"}, - {".psd", "application/octet-stream"}, - {".psess", "application/xml"}, - {".psm", "application/octet-stream"}, - {".psp", "application/octet-stream"}, - {".pst", "application/vnd.ms-outlook"}, - {".pub", "application/x-mspublisher"}, - {".pwz", "application/vnd.ms-powerpoint"}, - {".qht", "text/x-html-insertion"}, - {".qhtm", "text/x-html-insertion"}, - {".qt", "video/quicktime"}, - {".qti", "image/x-quicktime"}, - {".qtif", "image/x-quicktime"}, - {".qtl", "application/x-quicktimeplayer"}, - {".qxd", "application/octet-stream"}, - {".ra", "audio/x-pn-realaudio"}, - {".ram", "audio/x-pn-realaudio"}, - {".rar", "application/x-rar-compressed"}, - {".ras", "image/x-cmu-raster"}, - {".rat", "application/rat-file"}, - {".rc", "text/plain"}, - {".rc2", "text/plain"}, - {".rct", "text/plain"}, - {".rdlc", "application/xml"}, - {".reg", "text/plain"}, - {".resx", "application/xml"}, - {".rf", "image/vnd.rn-realflash"}, - {".rgb", "image/x-rgb"}, - {".rgs", "text/plain"}, - {".rm", "application/vnd.rn-realmedia"}, - {".rmi", "audio/mid"}, - {".rmp", "application/vnd.rn-rn_music_package"}, - {".roff", "application/x-troff"}, - {".rpm", "audio/x-pn-realaudio-plugin"}, - {".rqy", "text/x-ms-rqy"}, - {".rtf", "application/rtf"}, - {".rtx", "text/richtext"}, - {".rvt", "application/octet-stream" }, - {".ruleset", "application/xml"}, - {".s", "text/plain"}, - {".safariextz", "application/x-safari-safariextz"}, - {".scd", "application/x-msschedule"}, - {".scr", "text/plain"}, - {".sct", "text/scriptlet"}, - {".sd2", "audio/x-sd2"}, - {".sdp", "application/sdp"}, - {".sea", "application/octet-stream"}, - {".searchConnector-ms", "application/windows-search-connector+xml"}, - {".setpay", "application/set-payment-initiation"}, - {".setreg", "application/set-registration-initiation"}, - {".settings", "application/xml"}, - {".sgimb", "application/x-sgimb"}, - {".sgml", "text/sgml"}, - {".sh", "application/x-sh"}, - {".shar", "application/x-shar"}, - {".shtml", "text/html"}, - {".sit", "application/x-stuffit"}, - {".sitemap", "application/xml"}, - {".skin", "application/xml"}, - {".skp", "application/x-koan" }, - {".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12"}, - {".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"}, - {".slk", "application/vnd.ms-excel"}, - {".sln", "text/plain"}, - {".slupkg-ms", "application/x-ms-license"}, - {".smd", "audio/x-smd"}, - {".smi", "application/octet-stream"}, - {".smx", "audio/x-smd"}, - {".smz", "audio/x-smd"}, - {".snd", "audio/basic"}, - {".snippet", "application/xml"}, - {".snp", "application/octet-stream"}, - {".sol", "text/plain"}, - {".sor", "text/plain"}, - {".spc", "application/x-pkcs7-certificates"}, - {".spl", "application/futuresplash"}, - {".spx", "audio/ogg"}, - {".src", "application/x-wais-source"}, - {".srf", "text/plain"}, - {".SSISDeploymentManifest", "text/xml"}, - {".ssm", "application/streamingmedia"}, - {".sst", "application/vnd.ms-pki.certstore"}, - {".stl", "application/vnd.ms-pki.stl"}, - {".sv4cpio", "application/x-sv4cpio"}, - {".sv4crc", "application/x-sv4crc"}, - {".svc", "application/xml"}, - {".svg", "image/svg+xml"}, - {".swf", "application/x-shockwave-flash"}, - {".step", "application/step"}, - {".stp", "application/step"}, - {".t", "application/x-troff"}, - {".tar", "application/x-tar"}, - {".tcl", "application/x-tcl"}, - {".testrunconfig", "application/xml"}, - {".testsettings", "application/xml"}, - {".tex", "application/x-tex"}, - {".texi", "application/x-texinfo"}, - {".texinfo", "application/x-texinfo"}, - {".tgz", "application/x-compressed"}, - {".thmx", "application/vnd.ms-officetheme"}, - {".thn", "application/octet-stream"}, - {".tif", "image/tiff"}, - {".tiff", "image/tiff"}, - {".tlh", "text/plain"}, - {".tli", "text/plain"}, - {".toc", "application/octet-stream"}, - {".tr", "application/x-troff"}, - {".trm", "application/x-msterminal"}, - {".trx", "application/xml"}, - {".ts", "video/vnd.dlna.mpeg-tts"}, - {".tsv", "text/tab-separated-values"}, - {".ttf", "application/font-sfnt"}, - {".tts", "video/vnd.dlna.mpeg-tts"}, - {".txt", "text/plain"}, - {".u32", "application/octet-stream"}, - {".uls", "text/iuls"}, - {".user", "text/plain"}, - {".ustar", "application/x-ustar"}, - {".vb", "text/plain"}, - {".vbdproj", "text/plain"}, - {".vbk", "video/mpeg"}, - {".vbproj", "text/plain"}, - {".vbs", "text/vbscript"}, - {".vcf", "text/x-vcard"}, - {".vcproj", "application/xml"}, - {".vcs", "text/plain"}, - {".vcxproj", "application/xml"}, - {".vddproj", "text/plain"}, - {".vdp", "text/plain"}, - {".vdproj", "text/plain"}, - {".vdx", "application/vnd.ms-visio.viewer"}, - {".vml", "text/xml"}, - {".vscontent", "application/xml"}, - {".vsct", "text/xml"}, - {".vsd", "application/vnd.visio"}, - {".vsi", "application/ms-vsi"}, - {".vsix", "application/vsix"}, - {".vsixlangpack", "text/xml"}, - {".vsixmanifest", "text/xml"}, - {".vsmdi", "application/xml"}, - {".vspscc", "text/plain"}, - {".vss", "application/vnd.visio"}, - {".vsscc", "text/plain"}, - {".vssettings", "text/xml"}, - {".vssscc", "text/plain"}, - {".vst", "application/vnd.visio"}, - {".vstemplate", "text/xml"}, - {".vsto", "application/x-ms-vsto"}, - {".vsw", "application/vnd.visio"}, - {".vsx", "application/vnd.visio"}, - {".vtt", "text/vtt"}, - {".vtx", "application/vnd.visio"}, - {".wasm", "application/wasm"}, - {".wav", "audio/wav"}, - {".wave", "audio/wav"}, - {".wax", "audio/x-ms-wax"}, - {".wbk", "application/msword"}, - {".wbmp", "image/vnd.wap.wbmp"}, - {".wcm", "application/vnd.ms-works"}, - {".wdb", "application/vnd.ms-works"}, - {".wdp", "image/vnd.ms-photo"}, - {".webarchive", "application/x-safari-webarchive"}, - {".webm", "video/webm"}, - {".webp", "image/webp"}, /* https://en.wikipedia.org/wiki/WebP */ + {".3g2", "video/3gpp2"}, + {".3gp", "video/3gpp"}, + {".3gp2", "video/3gpp2"}, + {".3gpp", "video/3gpp"}, + {".7z", "application/x-7z-compressed"}, + {".aa", "audio/audible"}, + {".AAC", "audio/aac"}, + {".aaf", "application/octet-stream"}, + {".aax", "audio/vnd.audible.aax"}, + {".ac3", "audio/ac3"}, + {".aca", "application/octet-stream"}, + {".accda", "application/msaccess.addin"}, + {".accdb", "application/msaccess"}, + {".accdc", "application/msaccess.cab"}, + {".accde", "application/msaccess"}, + {".accdr", "application/msaccess.runtime"}, + {".accdt", "application/msaccess"}, + {".accdw", "application/msaccess.webapplication"}, + {".accft", "application/msaccess.ftemplate"}, + {".acx", "application/internet-property-stream"}, + {".AddIn", "text/xml"}, + {".ade", "application/msaccess"}, + {".adobebridge", "application/x-bridge-url"}, + {".adp", "application/msaccess"}, + {".ADT", "audio/vnd.dlna.adts"}, + {".ADTS", "audio/aac"}, + {".afm", "application/octet-stream"}, + {".ai", "application/postscript"}, + {".aif", "audio/aiff"}, + {".aifc", "audio/aiff"}, + {".aiff", "audio/aiff"}, + {".air", "application/vnd.adobe.air-application-installer-package+zip"}, + {".amc", "application/mpeg"}, + {".anx", "application/annodex"}, + {".apk", "application/vnd.android.package-archive" }, + {".application", "application/x-ms-application"}, + {".art", "image/x-jg"}, + {".asa", "application/xml"}, + {".asax", "application/xml"}, + {".ascx", "application/xml"}, + {".asd", "application/octet-stream"}, + {".asf", "video/x-ms-asf"}, + {".ashx", "application/xml"}, + {".asi", "application/octet-stream"}, + {".asm", "text/plain"}, + {".asmx", "application/xml"}, + {".aspx", "application/xml"}, + {".asr", "video/x-ms-asf"}, + {".asx", "video/x-ms-asf"}, + {".atom", "application/atom+xml"}, + {".au", "audio/basic"}, + {".avi", "video/x-msvideo"}, + {".axa", "audio/annodex"}, + {".axs", "application/olescript"}, + {".axv", "video/annodex"}, + {".bas", "text/plain"}, + {".bcpio", "application/x-bcpio"}, + {".bin", "application/octet-stream"}, + {".bmp", "image/bmp"}, + {".c", "text/plain"}, + {".cab", "application/octet-stream"}, + {".caf", "audio/x-caf"}, + {".calx", "application/vnd.ms-office.calx"}, + {".cat", "application/vnd.ms-pki.seccat"}, + {".cc", "text/plain"}, + {".cd", "text/plain"}, + {".cdda", "audio/aiff"}, + {".cdf", "application/x-cdf"}, + {".cer", "application/x-x509-ca-cert"}, + {".cfg", "text/plain"}, + {".chm", "application/octet-stream"}, + {".class", "application/x-java-applet"}, + {".clp", "application/x-msclip"}, + {".cmd", "text/plain"}, + {".cmx", "image/x-cmx"}, + {".cnf", "text/plain"}, + {".cod", "image/cis-cod"}, + {".config", "application/xml"}, + {".contact", "text/x-ms-contact"}, + {".coverage", "application/xml"}, + {".cpio", "application/x-cpio"}, + {".cpp", "text/plain"}, + {".crd", "application/x-mscardfile"}, + {".crl", "application/pkix-crl"}, + {".crt", "application/x-x509-ca-cert"}, + {".cs", "text/plain"}, + {".csdproj", "text/plain"}, + {".csh", "application/x-csh"}, + {".csproj", "text/plain"}, + {".css", "text/css"}, + {".csv", "text/csv"}, + {".cur", "application/octet-stream"}, + {".cxx", "text/plain"}, + {".dat", "application/octet-stream"}, + {".datasource", "application/xml"}, + {".dbproj", "text/plain"}, + {".dcr", "application/x-director"}, + {".def", "text/plain"}, + {".deploy", "application/octet-stream"}, + {".der", "application/x-x509-ca-cert"}, + {".dgml", "application/xml"}, + {".dib", "image/bmp"}, + {".dif", "video/x-dv"}, + {".dir", "application/x-director"}, + {".disco", "text/xml"}, + {".divx", "video/divx"}, + {".dll", "application/x-msdownload"}, + {".dll.config", "text/xml"}, + {".dlm", "text/dlm"}, + {".doc", "application/msword"}, + {".docm", "application/vnd.ms-word.document.macroEnabled.12"}, + {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, + {".dot", "application/msword"}, + {".dotm", "application/vnd.ms-word.template.macroEnabled.12"}, + {".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"}, + {".dsp", "application/octet-stream"}, + {".dsw", "text/plain"}, + {".dtd", "text/xml"}, + {".dtsConfig", "text/xml"}, + {".dv", "video/x-dv"}, + {".dvi", "application/x-dvi"}, + {".dwf", "drawing/x-dwf"}, + {".dwg", "application/acad"}, + {".dwp", "application/octet-stream"}, + {".dxf", "application/x-dxf" }, + {".dxr", "application/x-director"}, + {".eml", "message/rfc822"}, + {".emz", "application/octet-stream"}, + {".eot", "application/vnd.ms-fontobject"}, + {".eps", "application/postscript"}, + {".etl", "application/etl"}, + {".etx", "text/x-setext"}, + {".evy", "application/envoy"}, + {".exe", "application/octet-stream"}, + {".exe.config", "text/xml"}, + {".fdf", "application/vnd.fdf"}, + {".fif", "application/fractals"}, + {".filters", "application/xml"}, + {".fla", "application/octet-stream"}, + {".flac", "audio/flac"}, + {".flr", "x-world/x-vrml"}, + {".flv", "video/x-flv"}, + {".fsscript", "application/fsharp-script"}, + {".fsx", "application/fsharp-script"}, + {".generictest", "application/xml"}, + {".gif", "image/gif"}, + {".gpx", "application/gpx+xml"}, + {".group", "text/x-ms-group"}, + {".gsm", "audio/x-gsm"}, + {".gtar", "application/x-gtar"}, + {".gz", "application/x-gzip"}, + {".h", "text/plain"}, + {".hdf", "application/x-hdf"}, + {".hdml", "text/x-hdml"}, + {".hhc", "application/x-oleobject"}, + {".hhk", "application/octet-stream"}, + {".hhp", "application/octet-stream"}, + {".hlp", "application/winhlp"}, + {".hpp", "text/plain"}, + {".hqx", "application/mac-binhex40"}, + {".hta", "application/hta"}, + {".htc", "text/x-component"}, + {".htm", "text/html"}, + {".html", "text/html"}, + {".htt", "text/webviewhtml"}, + {".hxa", "application/xml"}, + {".hxc", "application/xml"}, + {".hxd", "application/octet-stream"}, + {".hxe", "application/xml"}, + {".hxf", "application/xml"}, + {".hxh", "application/octet-stream"}, + {".hxi", "application/octet-stream"}, + {".hxk", "application/xml"}, + {".hxq", "application/octet-stream"}, + {".hxr", "application/octet-stream"}, + {".hxs", "application/octet-stream"}, + {".hxt", "text/html"}, + {".hxv", "application/xml"}, + {".hxw", "application/octet-stream"}, + {".hxx", "text/plain"}, + {".i", "text/plain"}, + {".ico", "image/x-icon"}, + {".ics", "application/octet-stream"}, + {".idl", "text/plain"}, + {".ief", "image/ief"}, + {".iii", "application/x-iphone"}, + {".inc", "text/plain"}, + {".inf", "application/octet-stream"}, + {".ini", "text/plain"}, + {".inl", "text/plain"}, + {".ins", "application/x-internet-signup"}, + {".ipa", "application/x-itunes-ipa"}, + {".ipg", "application/x-itunes-ipg"}, + {".ipproj", "text/plain"}, + {".ipsw", "application/x-itunes-ipsw"}, + {".iqy", "text/x-ms-iqy"}, + {".isp", "application/x-internet-signup"}, + {".ite", "application/x-itunes-ite"}, + {".itlp", "application/x-itunes-itlp"}, + {".itms", "application/x-itunes-itms"}, + {".itpc", "application/x-itunes-itpc"}, + {".IVF", "video/x-ivf"}, + {".jar", "application/java-archive"}, + {".java", "application/octet-stream"}, + {".jck", "application/liquidmotion"}, + {".jcz", "application/liquidmotion"}, + {".jfif", "image/pjpeg"}, + {".jnlp", "application/x-java-jnlp-file"}, + {".jpb", "application/octet-stream"}, + {".jpe", "image/jpeg"}, + {".jpeg", "image/jpeg"}, + {".jpg", "image/jpeg"}, + {".js", "application/javascript"}, + {".json", "application/json"}, + {".jsx", "text/jscript"}, + {".jsxbin", "text/plain"}, + {".latex", "application/x-latex"}, + {".library-ms", "application/windows-library+xml"}, + {".lit", "application/x-ms-reader"}, + {".loadtest", "application/xml"}, + {".lpk", "application/octet-stream"}, + {".lsf", "video/x-la-asf"}, + {".lst", "text/plain"}, + {".lsx", "video/x-la-asf"}, + {".lzh", "application/octet-stream"}, + {".m13", "application/x-msmediaview"}, + {".m14", "application/x-msmediaview"}, + {".m1v", "video/mpeg"}, + {".m2t", "video/vnd.dlna.mpeg-tts"}, + {".m2ts", "video/vnd.dlna.mpeg-tts"}, + {".m2v", "video/mpeg"}, + {".m3u", "audio/x-mpegurl"}, + {".m3u8", "audio/x-mpegurl"}, + {".m4a", "audio/m4a"}, + {".m4b", "audio/m4b"}, + {".m4p", "audio/m4p"}, + {".m4r", "audio/x-m4r"}, + {".m4v", "video/x-m4v"}, + {".mac", "image/x-macpaint"}, + {".mak", "text/plain"}, + {".man", "application/x-troff-man"}, + {".manifest", "application/x-ms-manifest"}, + {".map", "text/plain"}, + {".master", "application/xml"}, + {".mbox", "application/mbox"}, + {".mda", "application/msaccess"}, + {".mdb", "application/x-msaccess"}, + {".mde", "application/msaccess"}, + {".mdp", "application/octet-stream"}, + {".me", "application/x-troff-me"}, + {".mfp", "application/x-shockwave-flash"}, + {".mht", "message/rfc822"}, + {".mhtml", "message/rfc822"}, + {".mid", "audio/mid"}, + {".midi", "audio/mid"}, + {".mix", "application/octet-stream"}, + {".mk", "text/plain"}, + {".mk3d", "video/x-matroska-3d"}, + {".mka", "audio/x-matroska"}, + {".mkv", "video/x-matroska"}, + {".mmf", "application/x-smaf"}, + {".mno", "text/xml"}, + {".mny", "application/x-msmoney"}, + {".mod", "video/mpeg"}, + {".mov", "video/quicktime"}, + {".movie", "video/x-sgi-movie"}, + {".mp2", "video/mpeg"}, + {".mp2v", "video/mpeg"}, + {".mp3", "audio/mpeg"}, + {".mp4", "video/mp4"}, + {".mp4v", "video/mp4"}, + {".mpa", "video/mpeg"}, + {".mpe", "video/mpeg"}, + {".mpeg", "video/mpeg"}, + {".mpf", "application/vnd.ms-mediapackage"}, + {".mpg", "video/mpeg"}, + {".mpp", "application/vnd.ms-project"}, + {".mpv2", "video/mpeg"}, + {".mqv", "video/quicktime"}, + {".ms", "application/x-troff-ms"}, + {".msg", "application/vnd.ms-outlook"}, + {".msi", "application/octet-stream"}, + {".mso", "application/octet-stream"}, + {".mts", "video/vnd.dlna.mpeg-tts"}, + {".mtx", "application/xml"}, + {".mvb", "application/x-msmediaview"}, + {".mvc", "application/x-miva-compiled"}, + {".mxp", "application/x-mmxp"}, + {".nc", "application/x-netcdf"}, + {".nsc", "video/x-ms-asf"}, + {".nws", "message/rfc822"}, + {".ocx", "application/octet-stream"}, + {".oda", "application/oda"}, + {".odb", "application/vnd.oasis.opendocument.database"}, + {".odc", "application/vnd.oasis.opendocument.chart"}, + {".odf", "application/vnd.oasis.opendocument.formula"}, + {".odg", "application/vnd.oasis.opendocument.graphics"}, + {".odh", "text/plain"}, + {".odi", "application/vnd.oasis.opendocument.image"}, + {".odl", "text/plain"}, + {".odm", "application/vnd.oasis.opendocument.text-master"}, + {".odp", "application/vnd.oasis.opendocument.presentation"}, + {".ods", "application/vnd.oasis.opendocument.spreadsheet"}, + {".odt", "application/vnd.oasis.opendocument.text"}, + {".oga", "audio/ogg"}, + {".ogg", "audio/ogg"}, + {".ogv", "video/ogg"}, + {".ogx", "application/ogg"}, + {".one", "application/onenote"}, + {".onea", "application/onenote"}, + {".onepkg", "application/onenote"}, + {".onetmp", "application/onenote"}, + {".onetoc", "application/onenote"}, + {".onetoc2", "application/onenote"}, + {".opus", "audio/ogg"}, + {".orderedtest", "application/xml"}, + {".osdx", "application/opensearchdescription+xml"}, + {".otf", "application/font-sfnt"}, + {".otg", "application/vnd.oasis.opendocument.graphics-template"}, + {".oth", "application/vnd.oasis.opendocument.text-web"}, + {".otp", "application/vnd.oasis.opendocument.presentation-template"}, + {".ots", "application/vnd.oasis.opendocument.spreadsheet-template"}, + {".ott", "application/vnd.oasis.opendocument.text-template"}, + {".oxt", "application/vnd.openofficeorg.extension"}, + {".p10", "application/pkcs10"}, + {".p12", "application/x-pkcs12"}, + {".p7b", "application/x-pkcs7-certificates"}, + {".p7c", "application/pkcs7-mime"}, + {".p7m", "application/pkcs7-mime"}, + {".p7r", "application/x-pkcs7-certreqresp"}, + {".p7s", "application/pkcs7-signature"}, + {".pbm", "image/x-portable-bitmap"}, + {".pcast", "application/x-podcast"}, + {".pct", "image/pict"}, + {".pcx", "application/octet-stream"}, + {".pcz", "application/octet-stream"}, + {".pdf", "application/pdf"}, + {".pfb", "application/octet-stream"}, + {".pfm", "application/octet-stream"}, + {".pfx", "application/x-pkcs12"}, + {".pgm", "image/x-portable-graymap"}, + {".pic", "image/pict"}, + {".pict", "image/pict"}, + {".pjp", "image/jpeg"}, + {".pkgdef", "text/plain"}, + {".pkgundef", "text/plain"}, + {".pko", "application/vnd.ms-pki.pko"}, + {".pls", "audio/scpls"}, + {".pma", "application/x-perfmon"}, + {".pmc", "application/x-perfmon"}, + {".pml", "application/x-perfmon"}, + {".pmr", "application/x-perfmon"}, + {".pmw", "application/x-perfmon"}, + {".png", "image/png"}, + {".pnm", "image/x-portable-anymap"}, + {".pnt", "image/x-macpaint"}, + {".pntg", "image/x-macpaint"}, + {".pnz", "image/png"}, + {".pot", "application/vnd.ms-powerpoint"}, + {".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"}, + {".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"}, + {".ppa", "application/vnd.ms-powerpoint"}, + {".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"}, + {".ppm", "image/x-portable-pixmap"}, + {".pps", "application/vnd.ms-powerpoint"}, + {".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"}, + {".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"}, + {".ppt", "application/vnd.ms-powerpoint"}, + {".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"}, + {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, + {".prf", "application/pics-rules"}, + {".prm", "application/octet-stream"}, + {".prx", "application/octet-stream"}, + {".ps", "application/postscript"}, + {".psc1", "application/PowerShell"}, + {".psd", "application/octet-stream"}, + {".psess", "application/xml"}, + {".psm", "application/octet-stream"}, + {".psp", "application/octet-stream"}, + {".pst", "application/vnd.ms-outlook"}, + {".pub", "application/x-mspublisher"}, + {".pwz", "application/vnd.ms-powerpoint"}, + {".qht", "text/x-html-insertion"}, + {".qhtm", "text/x-html-insertion"}, + {".qt", "video/quicktime"}, + {".qti", "image/x-quicktime"}, + {".qtif", "image/x-quicktime"}, + {".qtl", "application/x-quicktimeplayer"}, + {".qxd", "application/octet-stream"}, + {".ra", "audio/x-pn-realaudio"}, + {".ram", "audio/x-pn-realaudio"}, + {".rar", "application/x-rar-compressed"}, + {".ras", "image/x-cmu-raster"}, + {".rat", "application/rat-file"}, + {".rc", "text/plain"}, + {".rc2", "text/plain"}, + {".rct", "text/plain"}, + {".rdlc", "application/xml"}, + {".reg", "text/plain"}, + {".resx", "application/xml"}, + {".rf", "image/vnd.rn-realflash"}, + {".rgb", "image/x-rgb"}, + {".rgs", "text/plain"}, + {".rm", "application/vnd.rn-realmedia"}, + {".rmi", "audio/mid"}, + {".rmp", "application/vnd.rn-rn_music_package"}, + {".roff", "application/x-troff"}, + {".rpm", "audio/x-pn-realaudio-plugin"}, + {".rqy", "text/x-ms-rqy"}, + {".rtf", "application/rtf"}, + {".rtx", "text/richtext"}, + {".rvt", "application/octet-stream" }, + {".ruleset", "application/xml"}, + {".s", "text/plain"}, + {".safariextz", "application/x-safari-safariextz"}, + {".scd", "application/x-msschedule"}, + {".scr", "text/plain"}, + {".sct", "text/scriptlet"}, + {".sd2", "audio/x-sd2"}, + {".sdp", "application/sdp"}, + {".sea", "application/octet-stream"}, + {".searchConnector-ms", "application/windows-search-connector+xml"}, + {".setpay", "application/set-payment-initiation"}, + {".setreg", "application/set-registration-initiation"}, + {".settings", "application/xml"}, + {".sgimb", "application/x-sgimb"}, + {".sgml", "text/sgml"}, + {".sh", "application/x-sh"}, + {".shar", "application/x-shar"}, + {".shtml", "text/html"}, + {".sit", "application/x-stuffit"}, + {".sitemap", "application/xml"}, + {".skin", "application/xml"}, + {".skp", "application/x-koan" }, + {".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12"}, + {".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"}, + {".slk", "application/vnd.ms-excel"}, + {".sln", "text/plain"}, + {".slupkg-ms", "application/x-ms-license"}, + {".smd", "audio/x-smd"}, + {".smi", "application/octet-stream"}, + {".smx", "audio/x-smd"}, + {".smz", "audio/x-smd"}, + {".snd", "audio/basic"}, + {".snippet", "application/xml"}, + {".snp", "application/octet-stream"}, + {".sol", "text/plain"}, + {".sor", "text/plain"}, + {".spc", "application/x-pkcs7-certificates"}, + {".spl", "application/futuresplash"}, + {".spx", "audio/ogg"}, + {".src", "application/x-wais-source"}, + {".srf", "text/plain"}, + {".SSISDeploymentManifest", "text/xml"}, + {".ssm", "application/streamingmedia"}, + {".sst", "application/vnd.ms-pki.certstore"}, + {".stl", "application/vnd.ms-pki.stl"}, + {".sv4cpio", "application/x-sv4cpio"}, + {".sv4crc", "application/x-sv4crc"}, + {".svc", "application/xml"}, + {".svg", "image/svg+xml"}, + {".swf", "application/x-shockwave-flash"}, + {".step", "application/step"}, + {".stp", "application/step"}, + {".t", "application/x-troff"}, + {".tar", "application/x-tar"}, + {".tcl", "application/x-tcl"}, + {".testrunconfig", "application/xml"}, + {".testsettings", "application/xml"}, + {".tex", "application/x-tex"}, + {".texi", "application/x-texinfo"}, + {".texinfo", "application/x-texinfo"}, + {".tgz", "application/x-compressed"}, + {".thmx", "application/vnd.ms-officetheme"}, + {".thn", "application/octet-stream"}, + {".tif", "image/tiff"}, + {".tiff", "image/tiff"}, + {".tlh", "text/plain"}, + {".tli", "text/plain"}, + {".toc", "application/octet-stream"}, + {".tr", "application/x-troff"}, + {".trm", "application/x-msterminal"}, + {".trx", "application/xml"}, + {".ts", "video/vnd.dlna.mpeg-tts"}, + {".tsv", "text/tab-separated-values"}, + {".ttf", "application/font-sfnt"}, + {".tts", "video/vnd.dlna.mpeg-tts"}, + {".txt", "text/plain"}, + {".u32", "application/octet-stream"}, + {".uls", "text/iuls"}, + {".user", "text/plain"}, + {".ustar", "application/x-ustar"}, + {".vb", "text/plain"}, + {".vbdproj", "text/plain"}, + {".vbk", "video/mpeg"}, + {".vbproj", "text/plain"}, + {".vbs", "text/vbscript"}, + {".vcf", "text/x-vcard"}, + {".vcproj", "application/xml"}, + {".vcs", "text/plain"}, + {".vcxproj", "application/xml"}, + {".vddproj", "text/plain"}, + {".vdp", "text/plain"}, + {".vdproj", "text/plain"}, + {".vdx", "application/vnd.ms-visio.viewer"}, + {".vml", "text/xml"}, + {".vscontent", "application/xml"}, + {".vsct", "text/xml"}, + {".vsd", "application/vnd.visio"}, + {".vsi", "application/ms-vsi"}, + {".vsix", "application/vsix"}, + {".vsixlangpack", "text/xml"}, + {".vsixmanifest", "text/xml"}, + {".vsmdi", "application/xml"}, + {".vspscc", "text/plain"}, + {".vss", "application/vnd.visio"}, + {".vsscc", "text/plain"}, + {".vssettings", "text/xml"}, + {".vssscc", "text/plain"}, + {".vst", "application/vnd.visio"}, + {".vstemplate", "text/xml"}, + {".vsto", "application/x-ms-vsto"}, + {".vsw", "application/vnd.visio"}, + {".vsx", "application/vnd.visio"}, + {".vtt", "text/vtt"}, + {".vtx", "application/vnd.visio"}, + {".wasm", "application/wasm"}, + {".wav", "audio/wav"}, + {".wave", "audio/wav"}, + {".wax", "audio/x-ms-wax"}, + {".wbk", "application/msword"}, + {".wbmp", "image/vnd.wap.wbmp"}, + {".wcm", "application/vnd.ms-works"}, + {".wdb", "application/vnd.ms-works"}, + {".wdp", "image/vnd.ms-photo"}, + {".webarchive", "application/x-safari-webarchive"}, + {".webm", "video/webm"}, + {".webp", "image/webp"}, /* https://en.wikipedia.org/wiki/WebP */ {".webtest", "application/xml"}, - {".wiq", "application/xml"}, - {".wiz", "application/msword"}, - {".wks", "application/vnd.ms-works"}, - {".WLMP", "application/wlmoviemaker"}, - {".wlpginstall", "application/x-wlpg-detect"}, - {".wlpginstall3", "application/x-wlpg3-detect"}, - {".wm", "video/x-ms-wm"}, - {".wma", "audio/x-ms-wma"}, - {".wmd", "application/x-ms-wmd"}, - {".wmf", "application/x-msmetafile"}, - {".wml", "text/vnd.wap.wml"}, - {".wmlc", "application/vnd.wap.wmlc"}, - {".wmls", "text/vnd.wap.wmlscript"}, - {".wmlsc", "application/vnd.wap.wmlscriptc"}, - {".wmp", "video/x-ms-wmp"}, - {".wmv", "video/x-ms-wmv"}, - {".wmx", "video/x-ms-wmx"}, - {".wmz", "application/x-ms-wmz"}, - {".woff", "application/font-woff"}, - {".woff2", "application/font-woff2"}, - {".wpl", "application/vnd.ms-wpl"}, - {".wps", "application/vnd.ms-works"}, - {".wri", "application/x-mswrite"}, - {".wrl", "x-world/x-vrml"}, - {".wrz", "x-world/x-vrml"}, - {".wsc", "text/scriptlet"}, - {".wsdl", "text/xml"}, - {".wvx", "video/x-ms-wvx"}, - {".x", "application/directx"}, - {".xaf", "x-world/x-vrml"}, - {".xaml", "application/xaml+xml"}, - {".xap", "application/x-silverlight-app"}, - {".xbap", "application/x-ms-xbap"}, - {".xbm", "image/x-xbitmap"}, - {".xdr", "text/plain"}, - {".xht", "application/xhtml+xml"}, - {".xhtml", "application/xhtml+xml"}, - {".xla", "application/vnd.ms-excel"}, - {".xlam", "application/vnd.ms-excel.addin.macroEnabled.12"}, - {".xlc", "application/vnd.ms-excel"}, - {".xld", "application/vnd.ms-excel"}, - {".xlk", "application/vnd.ms-excel"}, - {".xll", "application/vnd.ms-excel"}, - {".xlm", "application/vnd.ms-excel"}, - {".xls", "application/vnd.ms-excel"}, - {".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"}, - {".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"}, - {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, - {".xlt", "application/vnd.ms-excel"}, - {".xltm", "application/vnd.ms-excel.template.macroEnabled.12"}, - {".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"}, - {".xlw", "application/vnd.ms-excel"}, - {".xml", "text/xml"}, - {".xmp", "application/octet-stream" }, - {".xmta", "application/xml"}, - {".xof", "x-world/x-vrml"}, - {".XOML", "text/plain"}, - {".xpm", "image/x-xpixmap"}, - {".xps", "application/vnd.ms-xpsdocument"}, - {".xrm-ms", "text/xml"}, - {".xsc", "application/xml"}, - {".xsd", "text/xml"}, - {".xsf", "text/xml"}, - {".xsl", "text/xml"}, - {".xslt", "text/xml"}, - {".xsn", "application/octet-stream"}, - {".xss", "application/xml"}, - {".xspf", "application/xspf+xml"}, - {".xtp", "application/octet-stream"}, - {".xwd", "image/x-xwindowdump"}, - {".z", "application/x-compress"}, - {".zip", "application/zip"}, + {".wiq", "application/xml"}, + {".wiz", "application/msword"}, + {".wks", "application/vnd.ms-works"}, + {".WLMP", "application/wlmoviemaker"}, + {".wlpginstall", "application/x-wlpg-detect"}, + {".wlpginstall3", "application/x-wlpg3-detect"}, + {".wm", "video/x-ms-wm"}, + {".wma", "audio/x-ms-wma"}, + {".wmd", "application/x-ms-wmd"}, + {".wmf", "application/x-msmetafile"}, + {".wml", "text/vnd.wap.wml"}, + {".wmlc", "application/vnd.wap.wmlc"}, + {".wmls", "text/vnd.wap.wmlscript"}, + {".wmlsc", "application/vnd.wap.wmlscriptc"}, + {".wmp", "video/x-ms-wmp"}, + {".wmv", "video/x-ms-wmv"}, + {".wmx", "video/x-ms-wmx"}, + {".wmz", "application/x-ms-wmz"}, + {".woff", "application/font-woff"}, + {".woff2", "application/font-woff2"}, + {".wpl", "application/vnd.ms-wpl"}, + {".wps", "application/vnd.ms-works"}, + {".wri", "application/x-mswrite"}, + {".wrl", "x-world/x-vrml"}, + {".wrz", "x-world/x-vrml"}, + {".wsc", "text/scriptlet"}, + {".wsdl", "text/xml"}, + {".wvx", "video/x-ms-wvx"}, + {".x", "application/directx"}, + {".xaf", "x-world/x-vrml"}, + {".xaml", "application/xaml+xml"}, + {".xap", "application/x-silverlight-app"}, + {".xbap", "application/x-ms-xbap"}, + {".xbm", "image/x-xbitmap"}, + {".xdr", "text/plain"}, + {".xht", "application/xhtml+xml"}, + {".xhtml", "application/xhtml+xml"}, + {".xla", "application/vnd.ms-excel"}, + {".xlam", "application/vnd.ms-excel.addin.macroEnabled.12"}, + {".xlc", "application/vnd.ms-excel"}, + {".xld", "application/vnd.ms-excel"}, + {".xlk", "application/vnd.ms-excel"}, + {".xll", "application/vnd.ms-excel"}, + {".xlm", "application/vnd.ms-excel"}, + {".xls", "application/vnd.ms-excel"}, + {".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"}, + {".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"}, + {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, + {".xlt", "application/vnd.ms-excel"}, + {".xltm", "application/vnd.ms-excel.template.macroEnabled.12"}, + {".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"}, + {".xlw", "application/vnd.ms-excel"}, + {".xml", "text/xml"}, + {".xmp", "application/octet-stream" }, + {".xmta", "application/xml"}, + {".xof", "x-world/x-vrml"}, + {".XOML", "text/plain"}, + {".xpm", "image/x-xpixmap"}, + {".xps", "application/vnd.ms-xpsdocument"}, + {".xrm-ms", "text/xml"}, + {".xsc", "application/xml"}, + {".xsd", "text/xml"}, + {".xsf", "text/xml"}, + {".xsl", "text/xml"}, + {".xslt", "text/xml"}, + {".xsn", "application/octet-stream"}, + {".xss", "application/xml"}, + {".xspf", "application/xspf+xml"}, + {".xtp", "application/octet-stream"}, + {".xwd", "image/x-xwindowdump"}, + {".z", "application/x-compress"}, + {".zip", "application/zip"}, #endregion }; - // Shuffle entries - ShuffleMap(_mimeMap, _extMap); - ShuffleMap(_extMap, _mimeMap); + // Shuffle entries + ShuffleMap(_mimeMap, _extMap); + ShuffleMap(_extMap, _mimeMap); - // Complement maps with entries from registry - try - { - using (var key = Registry.ClassesRoot.OpenSubKey(@"MIME\Database\Content Type", false)) - { - if (key != null) - { - var subKeyNames = key.GetSubKeyNames(); - foreach (var contentType in subKeyNames) - { - if (!_mimeMap.ContainsKey(contentType)) - { - // Our map does not contain this entry. We gonna add it. - using (var subKey = key.OpenSubKey(contentType, false)) - { - object extension = subKey.GetValue("Extension", null); - if (extension != null) - { - // MimeType > Extension - var ext = extension.ToString(); - _mimeMap.Add(contentType, ext); + // Complement maps with entries from registry + try + { + using (var key = Registry.ClassesRoot.OpenSubKey(@"MIME\Database\Content Type", false)) + { + if (key != null) + { + var subKeyNames = key.GetSubKeyNames(); + foreach (var contentType in subKeyNames) + { + if (!_mimeMap.ContainsKey(contentType)) + { + // Our map does not contain this entry. We gonna add it. + using (var subKey = key.OpenSubKey(contentType, false)) + { + object extension = subKey.GetValue("Extension", null); + if (extension != null) + { + // MimeType > Extension + var ext = extension.ToString(); + _mimeMap.Add(contentType, ext); - // Vice versa, Extension > MimeType - if (!_extMap.ContainsKey(ext)) - { - _extMap.Add(ext, contentType); - } - } - } - } - } - } - } - } - catch { } - } + // Vice versa, Extension > MimeType + if (!_extMap.ContainsKey(ext)) + { + _extMap.Add(ext, contentType); + } + } + } + } + } + } + } + } + catch { } + } - public static string MapNameToMimeType(string fileNameOrExtension) + public static string MapNameToMimeType(string fileNameOrExtension) { - var fileName = GetFileName(fileNameOrExtension); + var fileName = GetFileName(fileNameOrExtension); - for (int i = 0; i < fileName.Length; i++) - { - if ((fileName[i] == '.') && _extMap.TryGetValue(fileName.Substring(i), out var str)) - { - return str; - } - } + for (int i = 0; i < fileName.Length; i++) + { + if ((fileName[i] == '.') && _extMap.TryGetValue(fileName.Substring(i), out var str)) + { + return str; + } + } - return DefaultMimeType; + return DefaultMimeType; } /// @@ -773,34 +774,34 @@ public static string MapMimeTypeToExtension(string mimeType) if (mimeType.IsEmpty()) return null; - if (_mimeMap.TryGetValue(mimeType, out var extension)) - { - return extension.TrimStart('.'); - } + if (_mimeMap.TryGetValue(mimeType, out var extension)) + { + return extension.TrimStart('.'); + } - return null; + return null; } - private static void ShuffleMap(Dictionary source, Dictionary target) - { - foreach (var kvp in source) - { - if (!target.ContainsKey(kvp.Value)) - { - target.Add(kvp.Value, kvp.Key); - } - } - } + private static void ShuffleMap(Dictionary source, Dictionary target) + { + foreach (var kvp in source) + { + if (!target.ContainsKey(kvp.Value)) + { + target.Add(kvp.Value, kvp.Key); + } + } + } - private static string GetFileName(string path) - { - var startIndex = path.LastIndexOfAny(_pathSeparatorChars); - if (startIndex < 0) - { - return path; - } + private static string GetFileName(string path) + { + var startIndex = path.LastIndexOfAny(_pathSeparatorChars); + if (startIndex < 0) + { + return path; + } - return path.Substring(startIndex); - } - } + return path.Substring(startIndex); + } + } } diff --git a/src/Libraries/SmartStore.Core/IO/SeekableReadOnlyStream.cs b/src/Libraries/SmartStore.Core/IO/SeekableReadOnlyStream.cs new file mode 100644 index 0000000000..041c4ce451 --- /dev/null +++ b/src/Libraries/SmartStore.Core/IO/SeekableReadOnlyStream.cs @@ -0,0 +1,228 @@ +using System; +using System.IO; + +namespace SmartStore.Core.IO +{ + public class SeekableReadOnlyStream : Stream + { + private long _underlyingPosition; + private readonly byte[] _seekBackBuffer; + private int _seekBackBufferCount; + private int _seekBackBufferIndex; + private readonly Stream _underlyingStream; + + public SeekableReadOnlyStream(Stream underlyingStream, int seekBackBufferSize) + { + if (!underlyingStream.CanRead) + throw new Exception("Provided stream " + underlyingStream + " is not readable"); + _underlyingStream = underlyingStream; + _seekBackBuffer = new byte[seekBackBufferSize]; + } + + public override bool CanRead => true; + public override bool CanSeek => true; + + public override int Read(byte[] buffer, int offset, int count) + { + int copiedFromBackBufferCount = 0; + if (_seekBackBufferIndex < _seekBackBufferCount) + { + copiedFromBackBufferCount = Math.Min(count, _seekBackBufferCount - _seekBackBufferIndex); + Buffer.BlockCopy(_seekBackBuffer, _seekBackBufferIndex, buffer, offset, copiedFromBackBufferCount); + offset += copiedFromBackBufferCount; + count -= copiedFromBackBufferCount; + _seekBackBufferIndex += copiedFromBackBufferCount; + } + int bytesReadFromUnderlying = 0; + if (count > 0) + { + bytesReadFromUnderlying = _underlyingStream.Read(buffer, offset, count); + if (bytesReadFromUnderlying > 0) + { + _underlyingPosition += bytesReadFromUnderlying; + + var copyToBufferCount = Math.Min(bytesReadFromUnderlying, _seekBackBuffer.Length); + var copyToBufferOffset = Math.Min(_seekBackBufferCount, _seekBackBuffer.Length - copyToBufferCount); + var bufferBytesToMove = Math.Min(_seekBackBufferCount - 1, copyToBufferOffset); + + if (bufferBytesToMove > 0) + Buffer.BlockCopy(_seekBackBuffer, _seekBackBufferCount - bufferBytesToMove, _seekBackBuffer, 0, bufferBytesToMove); + Buffer.BlockCopy(buffer, offset, _seekBackBuffer, copyToBufferOffset, copyToBufferCount); + _seekBackBufferCount = Math.Min(_seekBackBuffer.Length, _seekBackBufferCount + copyToBufferCount); + _seekBackBufferIndex = _seekBackBufferCount; + } + } + return copiedFromBackBufferCount + bytesReadFromUnderlying; + } + + public override long Seek(long offset, SeekOrigin origin) + { + if (origin == SeekOrigin.End) + return SeekFromEnd((int)Math.Max(0, -offset)); + + var relativeOffset = origin == SeekOrigin.Current + ? offset + : offset - Position; + + if (relativeOffset == 0) + return Position; + else if (relativeOffset > 0) + return SeekForward(relativeOffset); + else + return SeekBackwards(-relativeOffset); + } + + private long SeekForward(long origOffset) + { + long offset = origOffset; + var seekBackBufferLength = _seekBackBuffer.Length; + + int backwardSoughtBytes = _seekBackBufferCount - _seekBackBufferIndex; + int seekForwardInBackBuffer = (int)Math.Min(offset, backwardSoughtBytes); + offset -= seekForwardInBackBuffer; + _seekBackBufferIndex += seekForwardInBackBuffer; + + if (offset > 0) + { + // first completely fill seekBackBuffer to remove special cases from while loop below + if (_seekBackBufferCount < seekBackBufferLength) + { + var maxRead = seekBackBufferLength - _seekBackBufferCount; + if (offset < maxRead) + maxRead = (int)offset; + var bytesRead = _underlyingStream.Read(_seekBackBuffer, _seekBackBufferCount, maxRead); + _underlyingPosition += bytesRead; + _seekBackBufferCount += bytesRead; + _seekBackBufferIndex = _seekBackBufferCount; + if (bytesRead < maxRead) + { + if (_seekBackBufferCount < offset) + throw new NotSupportedException("Reached end of stream seeking forward " + origOffset + " bytes"); + return Position; + } + offset -= bytesRead; + } + + // now alternate between filling tempBuffer and seekBackBuffer + bool fillTempBuffer = true; + var tempBuffer = new byte[seekBackBufferLength]; + while (offset > 0) + { + var maxRead = offset < seekBackBufferLength ? (int)offset : seekBackBufferLength; + var bytesRead = _underlyingStream.Read(fillTempBuffer ? tempBuffer : _seekBackBuffer, 0, maxRead); + _underlyingPosition += bytesRead; + var bytesReadDiff = maxRead - bytesRead; + offset -= bytesRead; + if (bytesReadDiff > 0 /* reached end-of-stream */ || offset == 0) + { + if (fillTempBuffer) + { + if (bytesRead > 0) + { + Buffer.BlockCopy(_seekBackBuffer, bytesRead, _seekBackBuffer, 0, bytesReadDiff); + Buffer.BlockCopy(tempBuffer, 0, _seekBackBuffer, bytesReadDiff, bytesRead); + } + } + else + { + if (bytesRead > 0) + Buffer.BlockCopy(_seekBackBuffer, 0, _seekBackBuffer, bytesReadDiff, bytesRead); + Buffer.BlockCopy(tempBuffer, bytesRead, _seekBackBuffer, 0, bytesReadDiff); + } + if (offset > 0) + throw new NotSupportedException("Reached end of stream seeking forward " + origOffset + " bytes"); + } + fillTempBuffer = !fillTempBuffer; + } + } + return Position; + } + + private long SeekBackwards(long offset) + { + var intOffset = (int)offset; + if (offset > int.MaxValue || intOffset > _seekBackBufferIndex) + throw new NotSupportedException("Cannot currently seek backwards more than " + _seekBackBufferIndex + " bytes"); + _seekBackBufferIndex -= intOffset; + return Position; + } + + private long SeekFromEnd(long offset) + { + var intOffset = (int)offset; + var seekBackBufferLength = _seekBackBuffer.Length; + if (offset > int.MaxValue || intOffset > seekBackBufferLength) + throw new NotSupportedException("Cannot seek backwards from end more than " + seekBackBufferLength + " bytes"); + + // first completely fill seekBackBuffer to remove special cases from while loop below + if (_seekBackBufferCount < seekBackBufferLength) + { + var maxRead = seekBackBufferLength - _seekBackBufferCount; + var bytesRead = _underlyingStream.Read(_seekBackBuffer, _seekBackBufferCount, maxRead); + _underlyingPosition += bytesRead; + _seekBackBufferCount += bytesRead; + _seekBackBufferIndex = Math.Max(0, _seekBackBufferCount - intOffset); + if (bytesRead < maxRead) + { + if (_seekBackBufferCount < intOffset) + throw new NotSupportedException("Could not seek backwards from end " + intOffset + " bytes"); + return Position; + } + } + else + { + _seekBackBufferIndex = _seekBackBufferCount; + } + + // now alternate between filling tempBuffer and seekBackBuffer + bool fillTempBuffer = true; + var tempBuffer = new byte[seekBackBufferLength]; + while (true) + { + var bytesRead = _underlyingStream.Read(fillTempBuffer ? tempBuffer : _seekBackBuffer, 0, seekBackBufferLength); + _underlyingPosition += bytesRead; + var bytesReadDiff = seekBackBufferLength - bytesRead; + if (bytesReadDiff > 0) // reached end-of-stream + { + if (fillTempBuffer) + { + if (bytesRead > 0) + { + Buffer.BlockCopy(_seekBackBuffer, bytesRead, _seekBackBuffer, 0, bytesReadDiff); + Buffer.BlockCopy(tempBuffer, 0, _seekBackBuffer, bytesReadDiff, bytesRead); + } + } + else + { + if (bytesRead > 0) + Buffer.BlockCopy(_seekBackBuffer, 0, _seekBackBuffer, bytesReadDiff, bytesRead); + Buffer.BlockCopy(tempBuffer, bytesRead, _seekBackBuffer, 0, bytesReadDiff); + } + _seekBackBufferIndex -= intOffset; + return Position; + } + fillTempBuffer = !fillTempBuffer; + } + } + + public override long Position + { + get => _underlyingPosition - (_seekBackBufferCount - _seekBackBufferIndex); + set => Seek(value, SeekOrigin.Begin); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + _underlyingStream.Close(); + base.Dispose(disposing); + } + + public override bool CanTimeout => _underlyingStream.CanTimeout; + public override bool CanWrite => _underlyingStream.CanWrite; + public override long Length => _underlyingStream.Length; + public override void SetLength(long value) { _underlyingStream.SetLength(value); } + public override void Write(byte[] buffer, int offset, int count) { _underlyingStream.Write(buffer, offset, count); } + public override void Flush() { _underlyingStream.Flush(); } + } +} diff --git a/src/Libraries/SmartStore.Core/IO/SymLink/FileSystemInfoExtensions.cs b/src/Libraries/SmartStore.Core/IO/SymLink/FileSystemInfoExtensions.cs index 2c7ed00c92..0c5032b1d4 100644 --- a/src/Libraries/SmartStore.Core/IO/SymLink/FileSystemInfoExtensions.cs +++ b/src/Libraries/SmartStore.Core/IO/SymLink/FileSystemInfoExtensions.cs @@ -1,29 +1,39 @@ -using System; -using System.IO; +using System.IO; using SmartStore.Core.IO; namespace SmartStore { - public static class FileSystemInfoExtensions - { - /// - /// Determines whether this file system entry is a symbolic link. - /// - /// The directory or file in question. - /// true if the entry is a symbolic link, false otherwise. - public static bool IsSymbolicLink(this FileSystemInfo fsi) - { - return SymbolicLink.IsSymbolicLink(fsi); - } + public static class FileSystemInfoExtensions + { + /// + /// Determines whether this file system entry is a symbolic link. + /// + /// The directory or file in question. + /// true if the entry is a symbolic link, false otherwise. + public static bool IsSymbolicLink(this FileSystemInfo fsi) + { + return SymbolicLink.IsSymbolicLink(fsi, out _); + } - /// - /// Returns the full path to the target of a symbolic link or mount. - /// - /// The symbolic link in question. - /// The path to the target. - public static string GetFinalPathName(this FileSystemInfo fsi) - { - return SymbolicLink.GetFinalPathName(fsi.FullName); - } - } + /// + /// Determines whether this file system entry is a symbolic link. + /// + /// The directory or file in question. + /// The final target path name if the entry is a symbolic link or null otherwise + /// true if the entry is a symbolic link, false otherwise. + public static bool IsSymbolicLink(this FileSystemInfo fsi, out string finalPathName) + { + return SymbolicLink.IsSymbolicLink(fsi, out finalPathName); + } + + /// + /// Returns the full path to the target of a symbolic link or mount. + /// + /// The symbolic link in question. + /// The path to the target. + public static string GetFinalPathName(this FileSystemInfo fsi) + { + return SymbolicLink.GetFinalPathName(fsi.FullName); + } + } } diff --git a/src/Libraries/SmartStore.Core/IO/SymLink/SymbolicLink.cs b/src/Libraries/SmartStore.Core/IO/SymLink/SymbolicLink.cs index 09567cf1eb..77e25d52df 100644 --- a/src/Libraries/SmartStore.Core/IO/SymLink/SymbolicLink.cs +++ b/src/Libraries/SmartStore.Core/IO/SymLink/SymbolicLink.cs @@ -6,98 +6,111 @@ namespace SmartStore.Core.IO { - internal static class SymbolicLink - { - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - private static extern IntPtr CreateFile( - [MarshalAs(UnmanagedType.LPTStr)] string filename, - [MarshalAs(UnmanagedType.U4)] uint access, - [MarshalAs(UnmanagedType.U4)] FileShare share, - IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero - [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, - [MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes, - IntPtr templateFile); - - //[DllImport("kernel32.dll", EntryPoint = "CreateSymbolicLinkW", CharSet = CharSet.Unicode, SetLastError = true)] - //private static extern bool CreateSymbolicLink( - // [In] string lpSymlinkFileName, - // [In] string lpTargetFileName, - // [In] int dwFlags); - - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - private static extern uint GetFinalPathNameByHandle( - IntPtr hFile, - [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, - uint cchFilePath, - uint dwFlags); - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - static extern bool CloseHandle(IntPtr hObject); - - private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; - private const uint FILE_READ_EA = 0x0008; - - private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); - - public static bool IsSymbolicLink(FileSystemInfo fsi) - { - Guard.NotNull(fsi, nameof(fsi)); - - if (!fsi.Exists) - return false; - - if (fsi.Attributes.HasFlag(FileAttributes.ReparsePoint)) - { - var target = GetFinalPathName(fsi.FullName); - if (target.HasValue()) - { - return !string.Equals(target.TrimEnd('\\'), fsi.FullName.TrimEnd('\\'), StringComparison.OrdinalIgnoreCase); - } - } - - return false; - } - - public static string GetFinalPathName(string path) - { - Guard.NotEmpty(path, nameof(path)); - - var h = CreateFile(path, - FILE_READ_EA, - FileShare.ReadWrite | FileShare.Delete, - IntPtr.Zero, - FileMode.Open, - FILE_FLAG_BACKUP_SEMANTICS, - IntPtr.Zero); - - if (h == INVALID_HANDLE_VALUE) - { - throw new Win32Exception(Marshal.GetLastWin32Error()); - } - - try - { - var sb = new StringBuilder(1024); - var res = GetFinalPathNameByHandle(h, sb, 1024, 0); - if (res == 0) - { - throw new Win32Exception(Marshal.GetLastWin32Error()); - } - - var result = sb.ToString(); - - if (result.Length >= 4 && result.StartsWith(@"\\?\")) - { - return result.Substring(4);// remove "\\?\" - } - - return result; - } - finally - { - CloseHandle(h); - } - } - } + internal static class SymbolicLink + { + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern IntPtr CreateFile( + [MarshalAs(UnmanagedType.LPTStr)] string filename, + [MarshalAs(UnmanagedType.U4)] uint access, + [MarshalAs(UnmanagedType.U4)] FileShare share, + IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero + [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, + [MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes, + IntPtr templateFile); + + //[DllImport("kernel32.dll", EntryPoint = "CreateSymbolicLinkW", CharSet = CharSet.Unicode, SetLastError = true)] + //private static extern bool CreateSymbolicLink( + // [In] string lpSymlinkFileName, + // [In] string lpTargetFileName, + // [In] int dwFlags); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern uint GetFinalPathNameByHandle( + IntPtr hFile, + [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, + uint cchFilePath, + uint dwFlags); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool CloseHandle(IntPtr hObject); + + private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; + private const uint FILE_READ_EA = 0x0008; + + private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); + + public static bool IsSymbolicLink(FileSystemInfo fsi, out string finalPathName) + { + Guard.NotNull(fsi, nameof(fsi)); + + finalPathName = null; + + // FileSystemInfo.Exists has some problems with symbolic links + var exists = (fsi is DirectoryInfo) + ? Directory.Exists(fsi.FullName) + : File.Exists(fsi.FullName); + + if (!exists) + return false; + + if (fsi.Attributes.HasFlag(FileAttributes.ReparsePoint)) + { + var target = GetFinalPathName(fsi.FullName); + if (target.HasValue()) + { + if (!string.Equals(target.TrimEnd('\\'), fsi.FullName.TrimEnd('\\'), StringComparison.OrdinalIgnoreCase)) + { + finalPathName = target; + return true; + } + + return false; + } + } + + return false; + } + + public static string GetFinalPathName(string path) + { + Guard.NotEmpty(path, nameof(path)); + + var h = CreateFile(path, + FILE_READ_EA, + FileShare.ReadWrite | FileShare.Delete, + IntPtr.Zero, + FileMode.Open, + FILE_FLAG_BACKUP_SEMANTICS, + IntPtr.Zero); + + if (h == INVALID_HANDLE_VALUE) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + try + { + var sb = new StringBuilder(1024); + var res = GetFinalPathNameByHandle(h, sb, 1024, 0); + if (res == 0) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + var result = sb.ToString(); + + if (result.Length >= 4 && result.StartsWith(@"\\?\")) + { + return result.Substring(4);// remove "\\?\" + } + + return result; + } + finally + { + CloseHandle(h); + } + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/IO/VirtualPath/DefaultVirtualPathProvider.cs b/src/Libraries/SmartStore.Core/IO/VirtualPath/DefaultVirtualPathProvider.cs index 2d78ea0447..a7eea24f4e 100644 --- a/src/Libraries/SmartStore.Core/IO/VirtualPath/DefaultVirtualPathProvider.cs +++ b/src/Libraries/SmartStore.Core/IO/VirtualPath/DefaultVirtualPathProvider.cs @@ -2,160 +2,160 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Web.Hosting; -using System.Web.Caching; -using SmartStore.Utilities; using System.Web; +using System.Web.Caching; +using System.Web.Hosting; using SmartStore.Core.Logging; +using SmartStore.Utilities; namespace SmartStore.Core.IO { - public class DefaultVirtualPathProvider : IVirtualPathProvider - { - private readonly ILogger _logger; - - public DefaultVirtualPathProvider(ILogger logger) - { - _logger = logger; - } - - public virtual string Combine(params string[] paths) - { - return Path.Combine(paths).Replace(Path.DirectorySeparatorChar, '/'); - } - - public virtual bool DirectoryExists(string virtualPath) - { - return HostingEnvironment.VirtualPathProvider.DirectoryExists(virtualPath); - } - - public virtual bool FileExists(string virtualPath) - { - return HostingEnvironment.VirtualPathProvider.FileExists(virtualPath); - } - - public virtual CacheDependency GetCacheDependency(string virtualPath, IEnumerable dependencies, DateTime utcStart) - { - return HostingEnvironment.VirtualPathProvider.GetCacheDependency(virtualPath, dependencies, utcStart); - } - - public virtual string GetCacheKey(string virtualPath) - { - return HostingEnvironment.VirtualPathProvider.GetCacheKey(virtualPath); - } - - public virtual IEnumerable ListDirectories(string virtualPath) - { - return HostingEnvironment - .VirtualPathProvider - .GetDirectory(virtualPath) - .Directories - .OfType() - .Select(d => VirtualPathUtility.ToAppRelative(d.VirtualPath)); - } - - public virtual IEnumerable ListFiles(string virtualPath) - { - return HostingEnvironment - .VirtualPathProvider - .GetDirectory(virtualPath) - .Files - .OfType() - .Select(f => VirtualPathUtility.ToAppRelative(f.VirtualPath)); - } - - public virtual string GetFileHash(string virtualPath, IEnumerable dependencies) - { - return HostingEnvironment.VirtualPathProvider.GetFileHash(virtualPath, dependencies); - } - - public virtual string MapPath(string virtualPath) - { - return CommonHelper.MapPath(virtualPath); - } - - public virtual string Normalize(string virtualPath) - { - return HostingEnvironment.VirtualPathProvider.GetFile(virtualPath).VirtualPath; - } - - public virtual Stream OpenFile(string virtualPath) - { - return HostingEnvironment.VirtualPathProvider.GetFile(virtualPath).Open(); - } - - public virtual string ToAppRelative(string virtualPath) - { - if (IsMalformedVirtualPath(virtualPath)) - return null; - - try - { - string result = VirtualPathUtility.ToAppRelative(virtualPath); - - // In some cases, ToAppRelative doesn't normalize the path. In those cases, - // the path is invalid. - // Example: - // ApplicationPath: /Foo - // VirtualPath : ~/Bar/../Blah/Blah2 - // Result : /Blah/Blah2 <= that is not an app relative path! - if (!result.StartsWith("~/")) - { - _logger.Info("Path '{0}' cannot be made app relative: Path returned ('{1}') is not app relative.".FormatCurrent(virtualPath, result)); - return null; - } - return result; - } - catch (Exception e) - { - // The initial path might have been invalid (e.g. path indicates a path outside the application root) - _logger.Info(e, "Path '{0}' cannot be made app relative".FormatCurrent(virtualPath)); - return null; - } - } - - /// - /// We want to reject path that contains ".." going outside of the application root. - /// ToAppRelative does that already, but we want to do the same while avoiding exceptions. - /// - /// Note: This method doesn't detect all cases of malformed paths, it merely checks - /// for *some* cases of malformed paths, so this is not a replacement for full virtual path - /// verification through VirtualPathUtilty methods. - /// In other words, !IsMalformed does *not* imply "IsWellformed". - /// - public bool IsMalformedVirtualPath(string virtualPath) - { - if (string.IsNullOrEmpty(virtualPath)) - return true; - - if (virtualPath.IndexOf("..", StringComparison.OrdinalIgnoreCase) >= 0) - { - virtualPath = virtualPath.Replace(Path.DirectorySeparatorChar, '/'); - string rootPrefix = virtualPath.StartsWith("~/") ? "~/" : virtualPath.StartsWith("/") ? "/" : ""; - if (!string.IsNullOrEmpty(rootPrefix)) - { - string[] terms = virtualPath.Substring(rootPrefix.Length).Split('/'); - int depth = 0; - foreach (var term in terms) - { - if (term == "..") - { - if (depth == 0) - { - _logger.Info("Path '{0}' cannot be made app relative: Too many '..'".FormatCurrent(virtualPath)); - return true; - } - depth--; - } - else - { - depth++; - } - } - } - } - - return false; - } - } + public class DefaultVirtualPathProvider : IVirtualPathProvider + { + private readonly ILogger _logger; + + public DefaultVirtualPathProvider(ILogger logger) + { + _logger = logger; + } + + public virtual string Combine(params string[] paths) + { + return Path.Combine(paths).Replace(Path.DirectorySeparatorChar, '/'); + } + + public virtual bool DirectoryExists(string virtualPath) + { + return HostingEnvironment.VirtualPathProvider.DirectoryExists(virtualPath); + } + + public virtual bool FileExists(string virtualPath) + { + return HostingEnvironment.VirtualPathProvider.FileExists(virtualPath); + } + + public virtual CacheDependency GetCacheDependency(string virtualPath, IEnumerable dependencies, DateTime utcStart) + { + return HostingEnvironment.VirtualPathProvider.GetCacheDependency(virtualPath, dependencies, utcStart); + } + + public virtual string GetCacheKey(string virtualPath) + { + return HostingEnvironment.VirtualPathProvider.GetCacheKey(virtualPath); + } + + public virtual IEnumerable ListDirectories(string virtualPath) + { + return HostingEnvironment + .VirtualPathProvider + .GetDirectory(virtualPath) + .Directories + .OfType() + .Select(d => VirtualPathUtility.ToAppRelative(d.VirtualPath)); + } + + public virtual IEnumerable ListFiles(string virtualPath) + { + return HostingEnvironment + .VirtualPathProvider + .GetDirectory(virtualPath) + .Files + .OfType() + .Select(f => VirtualPathUtility.ToAppRelative(f.VirtualPath)); + } + + public virtual string GetFileHash(string virtualPath, IEnumerable dependencies) + { + return HostingEnvironment.VirtualPathProvider.GetFileHash(virtualPath, dependencies); + } + + public virtual string MapPath(string virtualPath) + { + return CommonHelper.MapPath(virtualPath); + } + + public virtual string Normalize(string virtualPath) + { + return HostingEnvironment.VirtualPathProvider.GetFile(virtualPath).VirtualPath; + } + + public virtual Stream OpenFile(string virtualPath) + { + return HostingEnvironment.VirtualPathProvider.GetFile(virtualPath).Open(); + } + + public virtual string ToAppRelative(string virtualPath) + { + if (IsMalformedVirtualPath(virtualPath)) + return null; + + try + { + string result = VirtualPathUtility.ToAppRelative(virtualPath); + + // In some cases, ToAppRelative doesn't normalize the path. In those cases, + // the path is invalid. + // Example: + // ApplicationPath: /Foo + // VirtualPath : ~/Bar/../Blah/Blah2 + // Result : /Blah/Blah2 <= that is not an app relative path! + if (!result.StartsWith("~/")) + { + _logger.Info("Path '{0}' cannot be made app relative: Path returned ('{1}') is not app relative.".FormatCurrent(virtualPath, result)); + return null; + } + return result; + } + catch (Exception e) + { + // The initial path might have been invalid (e.g. path indicates a path outside the application root) + _logger.Info(e, "Path '{0}' cannot be made app relative".FormatCurrent(virtualPath)); + return null; + } + } + + /// + /// We want to reject path that contains ".." going outside of the application root. + /// ToAppRelative does that already, but we want to do the same while avoiding exceptions. + /// + /// Note: This method doesn't detect all cases of malformed paths, it merely checks + /// for *some* cases of malformed paths, so this is not a replacement for full virtual path + /// verification through VirtualPathUtilty methods. + /// In other words, !IsMalformed does *not* imply "IsWellformed". + /// + public bool IsMalformedVirtualPath(string virtualPath) + { + if (string.IsNullOrEmpty(virtualPath)) + return true; + + if (virtualPath.IndexOf("..", StringComparison.OrdinalIgnoreCase) >= 0) + { + virtualPath = virtualPath.Replace(Path.DirectorySeparatorChar, '/'); + string rootPrefix = virtualPath.StartsWith("~/") ? "~/" : virtualPath.StartsWith("/") ? "/" : ""; + if (!string.IsNullOrEmpty(rootPrefix)) + { + string[] terms = virtualPath.Substring(rootPrefix.Length).Split('/'); + int depth = 0; + foreach (var term in terms) + { + if (term == "..") + { + if (depth == 0) + { + _logger.Info("Path '{0}' cannot be made app relative: Too many '..'".FormatCurrent(virtualPath)); + return true; + } + depth--; + } + else + { + depth++; + } + } + } + } + + return false; + } + } } diff --git a/src/Libraries/SmartStore.Core/IO/VirtualPath/IVirtualFolder.cs b/src/Libraries/SmartStore.Core/IO/VirtualPath/IVirtualFolder.cs index e4177560ad..b363e0f4bd 100644 --- a/src/Libraries/SmartStore.Core/IO/VirtualPath/IVirtualFolder.cs +++ b/src/Libraries/SmartStore.Core/IO/VirtualPath/IVirtualFolder.cs @@ -1,65 +1,63 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; using System.Threading.Tasks; namespace SmartStore.Core.IO { - /// - /// Abstraction over the virtual files/directories of a web site. - /// - public interface IVirtualFolder - { - IVirtualPathProvider VirtualPathProvider { get; } - - /// - /// Virtual root path, e.g. ~/App_Data or ~/Themes - /// - string RootPath { get; } - - string GetVirtualPath(string relativePath); - - string MapPath(string relativePath); - string Combine(params string[] paths); - - bool DirectoryExists(string relativePath); - bool FileExists(string relativePath); - - IEnumerable ListDirectories(string relativePath); - IEnumerable ListFiles(string relativePath, bool deep = false); - string GetDirectoryName(string relativePath); - - Stream OpenFile(string relativePath); - void CreateTextFile(string relativePath, string content); - Task CreateTextFileAsync(string relativePath, string content); - Stream CreateFile(string relativePath); - void CreateDirectory(string relativePath); - - void DeleteFile(string relativePath); - void DeleteDirectory(string relativePath); - - string ReadFile(string relativePath); - Task ReadFileAsync(string relativePath); - void CopyFile(string relativePath, Stream destination); - - DateTime GetFileLastWriteTimeUtc(string relativePath); - } - - public static class IVirtualFolderExtensions - { - public static bool TryDeleteDirectory(this IVirtualFolder folder, string relativePath) - { - try - { - folder.DeleteDirectory(relativePath); - return true; - } - catch - { - return false; - } - } - } + /// + /// Abstraction over the virtual files/directories of a web site. + /// + public interface IVirtualFolder + { + IVirtualPathProvider VirtualPathProvider { get; } + + /// + /// Virtual root path, e.g. ~/App_Data or ~/Themes + /// + string RootPath { get; } + + string GetVirtualPath(string relativePath); + + string MapPath(string relativePath); + string Combine(params string[] paths); + + bool DirectoryExists(string relativePath); + bool FileExists(string relativePath); + + IEnumerable ListDirectories(string relativePath); + IEnumerable ListFiles(string relativePath, bool deep = false); + string GetDirectoryName(string relativePath); + + Stream OpenFile(string relativePath); + void CreateTextFile(string relativePath, string content); + Task CreateTextFileAsync(string relativePath, string content); + Stream CreateFile(string relativePath); + void CreateDirectory(string relativePath); + + void DeleteFile(string relativePath); + void DeleteDirectory(string relativePath); + + string ReadFile(string relativePath); + Task ReadFileAsync(string relativePath); + void CopyFile(string relativePath, Stream destination); + + DateTime GetFileLastWriteTimeUtc(string relativePath); + } + + public static class IVirtualFolderExtensions + { + public static bool TryDeleteDirectory(this IVirtualFolder folder, string relativePath) + { + try + { + folder.DeleteDirectory(relativePath); + return true; + } + catch + { + return false; + } + } + } } diff --git a/src/Libraries/SmartStore.Core/IO/VirtualPath/IVirtualPathProvider.cs b/src/Libraries/SmartStore.Core/IO/VirtualPath/IVirtualPathProvider.cs index 8fd1c36c90..2a7d3717f0 100644 --- a/src/Libraries/SmartStore.Core/IO/VirtualPath/IVirtualPathProvider.cs +++ b/src/Libraries/SmartStore.Core/IO/VirtualPath/IVirtualPathProvider.cs @@ -1,41 +1,40 @@ using System; using System.Collections.Generic; -using System.Linq; using System.IO; using System.Web.Caching; namespace SmartStore.Core.IO -{ +{ public interface IVirtualPathProvider { - string MapPath(string virtualPath); - string Combine(params string[] paths); - string Normalize(string virtualPath); - string ToAppRelative(string virtualPath); + string MapPath(string virtualPath); + string Combine(params string[] paths); + string Normalize(string virtualPath); + string ToAppRelative(string virtualPath); - bool DirectoryExists(string virtualPath); - bool FileExists(string virtualPath); + bool DirectoryExists(string virtualPath); + bool FileExists(string virtualPath); - CacheDependency GetCacheDependency(string virtualPath, IEnumerable dependencies, DateTime utcStart); - string GetCacheKey(string virtualPath); - string GetFileHash(string virtualPath, IEnumerable dependencies); + CacheDependency GetCacheDependency(string virtualPath, IEnumerable dependencies, DateTime utcStart); + string GetCacheKey(string virtualPath); + string GetFileHash(string virtualPath, IEnumerable dependencies); - IEnumerable ListDirectories(string virtualPath); - IEnumerable ListFiles(string virtualPath); + IEnumerable ListDirectories(string virtualPath); + IEnumerable ListFiles(string virtualPath); - Stream OpenFile(string virtualPath); + Stream OpenFile(string virtualPath); } - public static class IVirtualPathProviderExtensions - { - public static string GetFileHash(this IVirtualPathProvider vpp, string virtualPath) - { - return vpp.GetFileHash(virtualPath, new[] { virtualPath }); - } + public static class IVirtualPathProviderExtensions + { + public static string GetFileHash(this IVirtualPathProvider vpp, string virtualPath) + { + return vpp.GetFileHash(virtualPath, new[] { virtualPath }); + } - public static CacheDependency GetCacheDependency(this IVirtualPathProvider vpp, string virtualPath, DateTime utcStart) - { - return vpp.GetCacheDependency(virtualPath, new[] { virtualPath }, utcStart); - } - } + public static CacheDependency GetCacheDependency(this IVirtualPathProvider vpp, string virtualPath, DateTime utcStart) + { + return vpp.GetCacheDependency(virtualPath, new[] { virtualPath }, utcStart); + } + } } diff --git a/src/Libraries/SmartStore.Core/IO/VirtualPath/VirtualFolder.cs b/src/Libraries/SmartStore.Core/IO/VirtualPath/VirtualFolder.cs index 6355143bc6..0798c23a4d 100644 --- a/src/Libraries/SmartStore.Core/IO/VirtualPath/VirtualFolder.cs +++ b/src/Libraries/SmartStore.Core/IO/VirtualPath/VirtualFolder.cs @@ -7,231 +7,225 @@ namespace SmartStore.Core.IO { - public class VirtualFolder : IVirtualFolder - { - private readonly IVirtualPathProvider _vpp; - private readonly ILogger _logger; - private readonly string _root; - - public VirtualFolder(string root, IVirtualPathProvider vpp) - : this(root, vpp, NullLogger.Instance) - { - } - - public VirtualFolder(string root, IVirtualPathProvider vpp, ILogger logger) - { - Guard.NotEmpty(root, nameof(root)); - Guard.NotNull(vpp, nameof(vpp)); - Guard.NotNull(logger, nameof(logger)); - - if (!root.StartsWith("~/")) - { - throw new ArgumentException("Root path must be a valid application relative path starting with '~/'", nameof(root)); - } - - _root = root.Replace(Path.DirectorySeparatorChar, '/').EnsureEndsWith("/"); - _vpp = vpp; - _logger = logger; - } - - public IVirtualPathProvider VirtualPathProvider - { - get { return _vpp; } - } - - public string RootPath - { - get { return _root; } - } - - public virtual string MapPath(string relativePath) - { - return _vpp.MapPath(GetVirtualPath(relativePath)); - } - - public virtual string Combine(params string[] paths) - { - return _vpp.Combine(paths); - } - - public virtual bool DirectoryExists(string relativePath) - { - return _vpp.DirectoryExists(GetVirtualPath(relativePath)); - } - - public virtual bool FileExists(string relativePath) - { - return _vpp.FileExists(GetVirtualPath(relativePath)); - } - - public virtual IEnumerable ListDirectories(string relativePath) - { - var path = GetVirtualPath(relativePath); - - if (!_vpp.DirectoryExists(path)) - { - return Enumerable.Empty(); - } - - return _vpp.ListDirectories(path).Select(x => x.Substring(_root.Length)); - } - - public virtual IEnumerable ListFiles(string relativePath, bool deep = false) - { - var path = GetVirtualPath(relativePath); - - if (!_vpp.DirectoryExists(path)) - { - return Enumerable.Empty(); - } - - var files = _vpp.ListFiles(path).Select(x => x.Substring(_root.Length)); - - if (deep) - { - return files.Concat(ListDirectories(path).SelectMany(d => ListFiles(d, true))); - } - - return files; - } - - public virtual string GetDirectoryName(string relativePath) - { - return Path.GetDirectoryName(relativePath).Replace(Path.DirectorySeparatorChar, '/'); - } - - public virtual Stream OpenFile(string relativePath) - { - return _vpp.OpenFile(GetVirtualPath(relativePath)); - } - - public void CreateTextFile(string relativePath, string content) - { - Guard.NotEmpty(relativePath, nameof(relativePath)); - - using (var stream = CreateFile(relativePath)) - { - using (var tw = new StreamWriter(stream)) - { - tw.Write(content); - } - } - } - - public async Task CreateTextFileAsync(string relativePath, string content) - { - Guard.NotEmpty(relativePath, nameof(relativePath)); - - using (var stream = CreateFile(relativePath)) - { - using (var tw = new StreamWriter(stream)) - { - await tw.WriteAsync(content); - } - } - } - - public virtual Stream CreateFile(string relativePath) - { - var filePath = MapPath(relativePath); - var folderPath = Path.GetDirectoryName(filePath); - - if (!Directory.Exists(folderPath)) - { - Directory.CreateDirectory(folderPath); - } - - return File.Create(filePath); - } - - public virtual DateTime GetFileLastWriteTimeUtc(string relativePath) - { - return File.GetLastWriteTimeUtc(MapPath(relativePath)); - } - - public virtual void DeleteFile(string relativePath) - { - File.Delete(MapPath(relativePath)); - } - - public virtual void CreateDirectory(string relativePath) - { - Directory.CreateDirectory(MapPath(relativePath)); - } - - public virtual void DeleteDirectory(string relativePath) - { - Directory.Delete(MapPath(relativePath), true); - } - - public virtual string ReadFile(string relativePath) - { - Guard.NotEmpty(relativePath, nameof(relativePath)); - - var path = GetVirtualPath(relativePath); - - if (!_vpp.FileExists(path)) - { - return null; - } - - using (var stream = _vpp.OpenFile(path)) - { - using (var reader = new StreamReader(stream)) - { - return reader.ReadToEnd(); - } - } - } - - public virtual async Task ReadFileAsync(string relativePath) - { - Guard.NotEmpty(relativePath, nameof(relativePath)); - - var path = GetVirtualPath(relativePath); - - if (!_vpp.FileExists(path)) - { - return null; - } - - using (var stream = _vpp.OpenFile(path)) - { - using (var reader = new StreamReader(stream)) - { - return await reader.ReadToEndAsync(); - } - } - } - - public virtual void CopyFile(string relativePath, Stream destination) - { - Guard.NotEmpty(relativePath, nameof(relativePath)); - Guard.NotNull(destination, nameof(destination)); - - var path = GetVirtualPath(relativePath); - - if (!_vpp.FileExists(path)) - { - throw Error.InvalidOperation("File '{0}' does not exist".FormatInvariant(path)); - } - - using (var stream = _vpp.OpenFile(path)) - { - stream.CopyTo(destination); - } - } - - public string GetVirtualPath(string relativePath) - { - Guard.NotNull(relativePath, nameof(relativePath)); - - if (relativePath.StartsWith("~/")) - { - return relativePath; - } - - return _root + relativePath.EmptyNull().Replace(Path.DirectorySeparatorChar, '/').TrimStart('/'); - } - } + public class VirtualFolder : IVirtualFolder + { + private readonly IVirtualPathProvider _vpp; + private readonly ILogger _logger; + private readonly string _root; + + public VirtualFolder(string root, IVirtualPathProvider vpp) + : this(root, vpp, NullLogger.Instance) + { + } + + public VirtualFolder(string root, IVirtualPathProvider vpp, ILogger logger) + { + Guard.NotEmpty(root, nameof(root)); + Guard.NotNull(vpp, nameof(vpp)); + Guard.NotNull(logger, nameof(logger)); + + if (!root.StartsWith("~/")) + { + throw new ArgumentException("Root path must be a valid application relative path starting with '~/'", nameof(root)); + } + + _root = root.Replace(Path.DirectorySeparatorChar, '/').EnsureEndsWith("/"); + _vpp = vpp; + _logger = logger; + } + + public IVirtualPathProvider VirtualPathProvider => _vpp; + + public string RootPath => _root; + + public virtual string MapPath(string relativePath) + { + return _vpp.MapPath(GetVirtualPath(relativePath)); + } + + public virtual string Combine(params string[] paths) + { + return _vpp.Combine(paths); + } + + public virtual bool DirectoryExists(string relativePath) + { + return _vpp.DirectoryExists(GetVirtualPath(relativePath)); + } + + public virtual bool FileExists(string relativePath) + { + return _vpp.FileExists(GetVirtualPath(relativePath)); + } + + public virtual IEnumerable ListDirectories(string relativePath) + { + var path = GetVirtualPath(relativePath); + + if (!_vpp.DirectoryExists(path)) + { + return Enumerable.Empty(); + } + + return _vpp.ListDirectories(path).Select(x => x.Substring(_root.Length)); + } + + public virtual IEnumerable ListFiles(string relativePath, bool deep = false) + { + var path = GetVirtualPath(relativePath); + + if (!_vpp.DirectoryExists(path)) + { + return Enumerable.Empty(); + } + + var files = _vpp.ListFiles(path).Select(x => x.Substring(_root.Length)); + + if (deep) + { + return files.Concat(ListDirectories(path).SelectMany(d => ListFiles(d, true))); + } + + return files; + } + + public virtual string GetDirectoryName(string relativePath) + { + return Path.GetDirectoryName(relativePath).Replace(Path.DirectorySeparatorChar, '/'); + } + + public virtual Stream OpenFile(string relativePath) + { + return _vpp.OpenFile(GetVirtualPath(relativePath)); + } + + public void CreateTextFile(string relativePath, string content) + { + Guard.NotEmpty(relativePath, nameof(relativePath)); + + using (var stream = CreateFile(relativePath)) + { + using (var tw = new StreamWriter(stream)) + { + tw.Write(content); + } + } + } + + public async Task CreateTextFileAsync(string relativePath, string content) + { + Guard.NotEmpty(relativePath, nameof(relativePath)); + + using (var stream = CreateFile(relativePath)) + { + using (var tw = new StreamWriter(stream)) + { + await tw.WriteAsync(content); + } + } + } + + public virtual Stream CreateFile(string relativePath) + { + var filePath = MapPath(relativePath); + var folderPath = Path.GetDirectoryName(filePath); + + if (!Directory.Exists(folderPath)) + { + Directory.CreateDirectory(folderPath); + } + + return File.Create(filePath); + } + + public virtual DateTime GetFileLastWriteTimeUtc(string relativePath) + { + return File.GetLastWriteTimeUtc(MapPath(relativePath)); + } + + public virtual void DeleteFile(string relativePath) + { + File.Delete(MapPath(relativePath)); + } + + public virtual void CreateDirectory(string relativePath) + { + Directory.CreateDirectory(MapPath(relativePath)); + } + + public virtual void DeleteDirectory(string relativePath) + { + Directory.Delete(MapPath(relativePath), true); + } + + public virtual string ReadFile(string relativePath) + { + Guard.NotEmpty(relativePath, nameof(relativePath)); + + var path = GetVirtualPath(relativePath); + + if (!_vpp.FileExists(path)) + { + return null; + } + + using (var stream = _vpp.OpenFile(path)) + { + using (var reader = new StreamReader(stream)) + { + return reader.ReadToEnd(); + } + } + } + + public virtual async Task ReadFileAsync(string relativePath) + { + Guard.NotEmpty(relativePath, nameof(relativePath)); + + var path = GetVirtualPath(relativePath); + + if (!_vpp.FileExists(path)) + { + return null; + } + + using (var stream = _vpp.OpenFile(path)) + { + using (var reader = new StreamReader(stream)) + { + return await reader.ReadToEndAsync(); + } + } + } + + public virtual void CopyFile(string relativePath, Stream destination) + { + Guard.NotEmpty(relativePath, nameof(relativePath)); + Guard.NotNull(destination, nameof(destination)); + + var path = GetVirtualPath(relativePath); + + if (!_vpp.FileExists(path)) + { + throw Error.InvalidOperation("File '{0}' does not exist".FormatInvariant(path)); + } + + using (var stream = _vpp.OpenFile(path)) + { + stream.CopyTo(destination); + } + } + + public string GetVirtualPath(string relativePath) + { + Guard.NotNull(relativePath, nameof(relativePath)); + + if (relativePath.StartsWith("~/")) + { + return relativePath; + } + + return _root + relativePath.EmptyNull().Replace(Path.DirectorySeparatorChar, '/').TrimStart('/'); + } + } } diff --git a/src/Libraries/SmartStore.Core/IPageable.cs b/src/Libraries/SmartStore.Core/IPageable.cs index ce270c8e4e..d0d046c4c0 100644 --- a/src/Libraries/SmartStore.Core/IPageable.cs +++ b/src/Libraries/SmartStore.Core/IPageable.cs @@ -1,12 +1,12 @@ using System; -using System.Runtime.Serialization; using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; using System.Threading.Tasks; namespace SmartStore.Core -{ +{ /// /// A collection of objects that has been split into pages. /// @@ -77,43 +77,48 @@ public interface IPageable : IEnumerable } - /// - /// Paged list interface - /// - public interface IPagedList : IPageable, IList - { - /// - /// Gets underlying query without any paging applied - /// - IQueryable SourceQuery { get; } - - /// - /// Allows modification of the underlying query before it is executed. - /// - /// The alteration function. The underlying query is passed, the modified query should be returned. - /// The current instance for chaining - IPagedList AlterQuery(Func, IQueryable> alterer); - - /// - /// Applies the initial paging arguments to the passed query - /// - /// The query - /// A query with applied paging args - IQueryable ApplyPaging(IQueryable query); - - /// - /// Loads the data synchronously. - /// - /// When true, always reloads data. When false, first checks to see whether data has been loaded already and skips if so. - /// Returns itself for chaining. - IPagedList Load(bool force = false); - - /// - /// Loads the data asynchronously. - /// - /// When true, always reloads data. When false, first checks to see whether data has been loaded already and skips if so. - /// Returns itself for chaining. - Task> LoadAsync(bool force = false); - } + /// + /// Paged list interface + /// + public interface IPagedList : IPageable, IList + { + /// + /// Gets underlying query without any paging applied + /// + IQueryable SourceQuery { get; } + + /// + /// Allows modification of the underlying query before it is executed. + /// + /// The alteration function. The underlying query is passed, the modified query should be returned. + /// The current instance for chaining + IPagedList AlterQuery(Func, IQueryable> alterer); + + /// + /// Applies the initial paging arguments to the passed query + /// + /// The query + /// A query with applied paging args + IQueryable ApplyPaging(IQueryable query); + + /// + /// Loads the data synchronously. + /// + /// When true, always reloads data. When false, first checks to see whether data has been loaded already and skips if so. + /// Returns itself for chaining. + IPagedList Load(bool force = false); + + /// + /// Loads the data asynchronously. + /// + /// When true, always reloads data. When false, first checks to see whether data has been loaded already and skips if so. + /// Returns itself for chaining. + Task> LoadAsync(bool force = false); + + /// + /// The total number of items asynchronously. + /// + Task GetTotalCountAsync(); + } } diff --git a/src/Libraries/SmartStore.Core/ISoftDeletable.cs b/src/Libraries/SmartStore.Core/ISoftDeletable.cs index 88fe0be01b..2dd6ec0c6a 100644 --- a/src/Libraries/SmartStore.Core/ISoftDeletable.cs +++ b/src/Libraries/SmartStore.Core/ISoftDeletable.cs @@ -1,10 +1,7 @@ -using System; -using System.Collections.Generic; - -namespace SmartStore.Core +namespace SmartStore.Core { - public interface ISoftDeletable - { - bool Deleted { get; } - } + public interface ISoftDeletable + { + bool Deleted { get; } + } } diff --git a/src/Libraries/SmartStore.Core/IStoreContext.cs b/src/Libraries/SmartStore.Core/IStoreContext.cs index 1802ddbf23..6ff76ed3c7 100644 --- a/src/Libraries/SmartStore.Core/IStoreContext.cs +++ b/src/Libraries/SmartStore.Core/IStoreContext.cs @@ -2,44 +2,44 @@ namespace SmartStore.Core { - /// - /// Store context - /// - public interface IStoreContext - { - /// - /// Sets a store override to be used for the current request - /// - /// The store override or null to remove the override - void SetRequestStore(int? storeId); + /// + /// Store context + /// + public interface IStoreContext + { + /// + /// Sets a store override to be used for the current request + /// + /// The store override or null to remove the override + void SetRequestStore(int? storeId); - /// - /// Sets a store override to be used for the current user's session (e.g. for preview mode) - /// - /// The store override or null to remove the override - void SetPreviewStore(int? storeId); + /// + /// Sets a store override to be used for the current user's session (e.g. for preview mode) + /// + /// The store override or null to remove the override + void SetPreviewStore(int? storeId); - /// - /// Gets the store override for the current request - /// - /// The store override or null - int? GetRequestStore(); + /// + /// Gets the store override for the current request + /// + /// The store override or null + int? GetRequestStore(); - /// - /// Gets the store override for the current session - /// - /// The store override or null - int? GetPreviewStore(); - - /// - /// Gets or sets the current store - /// - /// Setter is for virtualization and testing purposes - Store CurrentStore { get; set; } + /// + /// Gets the store override for the current session + /// + /// The store override or null + int? GetPreviewStore(); - /// - /// IsSingleStoreMode ? 0 : CurrentStore.Id - /// - int CurrentStoreIdIfMultiStoreMode { get; } - } -} + /// + /// Gets or sets the current store + /// + /// Setter is for virtualization and testing purposes + Store CurrentStore { get; set; } + + /// + /// IsSingleStoreMode ? 0 : CurrentStore.Id + /// + int CurrentStoreIdIfMultiStoreMode { get; } + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/ITransient.cs b/src/Libraries/SmartStore.Core/ITransient.cs index 761dcee833..d07cf7c224 100644 --- a/src/Libraries/SmartStore.Core/ITransient.cs +++ b/src/Libraries/SmartStore.Core/ITransient.cs @@ -1,9 +1,7 @@ -using System; - -namespace SmartStore +namespace SmartStore { - public interface ITransient - { - bool IsTransient { get; set; } - } + public interface ITransient + { + bool IsTransient { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/IWebHelper.cs b/src/Libraries/SmartStore.Core/IWebHelper.cs index 2d089bd64d..31cd154105 100644 --- a/src/Libraries/SmartStore.Core/IWebHelper.cs +++ b/src/Libraries/SmartStore.Core/IWebHelper.cs @@ -2,9 +2,6 @@ namespace SmartStore.Core { - /// - /// Represents a common helper - /// public partial interface IWebHelper { /// @@ -13,21 +10,21 @@ public partial interface IWebHelper /// URL referrer string GetUrlReferrer(); - /// - /// Gets a unique client identifier - /// - /// A unique identifier - /// - /// The client identifier is a hashed combination of client ip address and user agent. - /// This method returns null if IP or user agent (or both) cannot be determined. - /// - string GetClientIdent(); - - /// - /// Get context IP address - /// - /// URL referrer - string GetCurrentIpAddress(); + /// + /// Gets a unique client identifier + /// + /// A unique identifier + /// + /// The client identifier is a hashed combination of client ip address and user agent. + /// This method returns null if IP or user agent (or both) cannot be determined. + /// + string GetClientIdent(); + + /// + /// Get context IP address + /// + /// URL referrer + string GetCurrentIpAddress(); /// /// Gets this page name @@ -49,7 +46,7 @@ public partial interface IWebHelper /// /// true - secured, false - not secured bool IsCurrentConnectionSecured(); - + /// /// Gets server variable by name /// @@ -87,7 +84,7 @@ public partial interface IWebHelper /// .ashx /// bool IsStaticResource(HttpRequest request); - + /// /// Maps a virtual path to a physical disk path. /// @@ -112,7 +109,7 @@ public partial interface IWebHelper /// Query string to remove /// New url string RemoveQueryString(string url, string queryString); - + /// /// Gets query string value by name /// diff --git a/src/Libraries/SmartStore.Core/Infrastructure/AppDomainTypeFinder.cs b/src/Libraries/SmartStore.Core/Infrastructure/AppDomainTypeFinder.cs index 4ed72318a1..6c785c9785 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/AppDomainTypeFinder.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/AppDomainTypeFinder.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; -using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; @@ -18,37 +16,34 @@ namespace SmartStore.Core.Infrastructure /// public class AppDomainTypeFinder : ITypeFinder { - private static object s_lock = new object(); + private static readonly object s_lock = new object(); - private readonly Regex _systemAssembliesPattern = new Regex(@"^System|^mscorlib|^Microsoft", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); + private readonly Regex _systemAssembliesPattern = new Regex(@"^System|^mscorlib|^Microsoft", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); - private bool _ignoreReflectionErrors = true; - private bool _loadAppDomainAssemblies = true; + private readonly bool _ignoreReflectionErrors = true; + private bool _loadAppDomainAssemblies = true; private IList _customAssemblyNames = new List(); - public AppDomainTypeFinder() - { - Logger = NullLogger.Instance; - } - - public ILogger Logger - { - get; - set; - } + public AppDomainTypeFinder() + { + Logger = NullLogger.Instance; + } - public virtual AppDomain App + public ILogger Logger { - get { return AppDomain.CurrentDomain; } + get; + set; } + public virtual AppDomain App => AppDomain.CurrentDomain; + /// - /// Gets or sets wether SmartStore should iterate assemblies in the app domain when loading SmartStore types. Loading patterns are applied when loading these assemblies. - /// + /// Gets or sets wether SmartStore should iterate assemblies in the app domain when loading SmartStore types. Loading patterns are applied when loading these assemblies. + /// public bool LoadAppDomainAssemblies { - get { return _loadAppDomainAssemblies; } - set { _loadAppDomainAssemblies = value; } + get => _loadAppDomainAssemblies; + set => _loadAppDomainAssemblies = value; } /// @@ -56,8 +51,8 @@ public bool LoadAppDomainAssemblies /// public IList CustomAssemblyNames { - get { return _customAssemblyNames; } - set { _customAssemblyNames = value; } + get => _customAssemblyNames; + set => _customAssemblyNames = value; } public IEnumerable FindClassesOfType(Type assignTypeFrom, IEnumerable assemblies, bool onlyConcreteClasses = true) @@ -66,94 +61,94 @@ public IEnumerable FindClassesOfType(Type assignTypeFrom, IEnumerable GetAssemblies(bool ignoreInactivePlugins = false) - { - var assemblies = new HashSet(); + public virtual IEnumerable GetAssemblies(bool ignoreInactivePlugins = false) + { + var assemblies = new HashSet(); - var addedAssemblyNames = new HashSet(StringComparer.OrdinalIgnoreCase); + var addedAssemblyNames = new HashSet(StringComparer.OrdinalIgnoreCase); - if (_loadAppDomainAssemblies) - { - AddAppDomainAssemblies(addedAssemblyNames, assemblies); - } + if (_loadAppDomainAssemblies) + { + AddAppDomainAssemblies(addedAssemblyNames, assemblies); + } - AddCustomAssemblies(addedAssemblyNames, assemblies); + AddCustomAssemblies(addedAssemblyNames, assemblies); - if (ignoreInactivePlugins) - { - return assemblies.Where(x => PluginManager.IsActivePluginAssembly(x)).AsReadOnly(); - } + if (ignoreInactivePlugins) + { + return assemblies.Where(x => PluginManager.IsActivePluginAssembly(x)).AsReadOnly(); + } - return assemblies.AsReadOnly(); - } + return assemblies.AsReadOnly(); + } - /// - /// Iterates all assemblies in the AppDomain and if its name matches the configured patterns adds it to our list. - /// - /// - /// - private void AddAppDomainAssemblies(HashSet addedAssemblyNames, HashSet assemblies) + /// + /// Iterates all assemblies in the AppDomain and if its name matches the configured patterns adds it to our list. + /// + /// + /// + private void AddAppDomainAssemblies(HashSet addedAssemblyNames, HashSet assemblies) { - var domainAssemblies = AppDomain.CurrentDomain.GetAssemblies(); + var domainAssemblies = AppDomain.CurrentDomain.GetAssemblies(); - foreach (var assembly in domainAssemblies) + foreach (var assembly in domainAssemblies) { if (Matches(assembly)) { @@ -175,50 +170,50 @@ protected virtual void AddCustomAssemblies(HashSet addedAssemblyNames, H { if (!addedAssemblyNames.Contains(assemblyName)) { - try - { - var assembly = Assembly.Load(assemblyName); - assemblies.Add(assembly); - Logger.DebugFormat("Loaded custom assembly '{0}'", assemblyName); - } - finally - { - addedAssemblyNames.Add(assemblyName); - } + try + { + var assembly = Assembly.Load(assemblyName); + assemblies.Add(assembly); + Logger.DebugFormat("Loaded custom assembly '{0}'", assemblyName); + } + finally + { + addedAssemblyNames.Add(assemblyName); + } } } } - /// - /// Checks whether an assembly is one of the shipped system or 3rd party assemblies that don't need to be investigated. - /// - /// The assembly to check. - /// True if the assembly should be loaded into SmartStore. - public virtual bool Matches(Assembly assembly) - { + /// + /// Checks whether an assembly is one of the shipped system or 3rd party assemblies that don't need to be investigated. + /// + /// The assembly to check. + /// True if the assembly should be loaded into SmartStore. + public virtual bool Matches(Assembly assembly) + { var name = assembly.FullName; - // Check if passed assembly is a system assembly - if (_systemAssembliesPattern.IsMatch(name)) - { - return false; - } - - // Check if the passed assembly starts with "SmartStore." - if (name.StartsWith("SmartStore.", StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - // Check if any of the referenced assemblies of the passed assembly start with "SmartStore." - // In this case it's obviously a SmartStore plugin. - if (assembly.GetReferencedAssemblies().Any(x => x.FullName.StartsWith("SmartStore.", StringComparison.OrdinalIgnoreCase))) - { - return true; - } - - return false; - } + // Check if passed assembly is a system assembly + if (_systemAssembliesPattern.IsMatch(name)) + { + return false; + } + + // Check if the passed assembly starts with "SmartStore." + if (name.StartsWith("SmartStore.", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + // Check if any of the referenced assemblies of the passed assembly start with "SmartStore." + // In this case it's obviously a SmartStore plugin. + if (assembly.GetReferencedAssemblies().Any(x => x.FullName.StartsWith("SmartStore.", StringComparison.OrdinalIgnoreCase))) + { + return true; + } + + return false; + } protected virtual bool DoesTypeImplementOpenGeneric(Type type, Type openGeneric) { @@ -236,11 +231,11 @@ protected virtual bool DoesTypeImplementOpenGeneric(Type type, Type openGeneric) return false; } - catch + catch { return false; } } - + } -} +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Infrastructure/ApplicationEnvironment.cs b/src/Libraries/SmartStore.Core/Infrastructure/ApplicationEnvironment.cs index f2582a6096..15ba56ebc8 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/ApplicationEnvironment.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/ApplicationEnvironment.cs @@ -6,42 +6,31 @@ namespace SmartStore.Core { - public class ApplicationEnvironment : IApplicationEnvironment - { - public ApplicationEnvironment(IVirtualPathProvider vpp, ILogger logger) - { - WebRootFolder = new VirtualFolder("~/", vpp, logger); - AppDataFolder = new VirtualFolder("~/App_Data/", vpp, logger); - ThemesFolder = new VirtualFolder(CommonHelper.GetAppSetting("sm:ThemesBasePath", "~/Themes/"), vpp, logger); - PluginsFolder = new VirtualFolder("~/Plugins/", vpp, logger); + public class ApplicationEnvironment : IApplicationEnvironment + { + public ApplicationEnvironment(IVirtualPathProvider vpp, ILogger logger) + { + WebRootFolder = new VirtualFolder("~/", vpp, logger); + AppDataFolder = new VirtualFolder("~/App_Data/", vpp, logger); + ThemesFolder = new VirtualFolder(CommonHelper.GetAppSetting("sm:ThemesBasePath", "~/Themes/"), vpp, logger); + PluginsFolder = new VirtualFolder("~/Plugins/", vpp, logger); - if (DataSettings.Current.IsValid()) - { - TenantFolder = new VirtualFolder(DataSettings.Current.TenantPath, vpp, logger); - } - } + if (DataSettings.Current.IsValid()) + { + TenantFolder = new VirtualFolder(DataSettings.Current.TenantPath, vpp, logger); + } + } - public virtual string MachineName - { - get - { - return Environment.MachineName; - } - } + public virtual string MachineName => Environment.MachineName; - public virtual string EnvironmentIdentifier - { - get - { - // use the current host and the process id as two servers could run on the same machine - return Environment.MachineName + "-" + System.Diagnostics.Process.GetCurrentProcess().Id; - } - } + public virtual string EnvironmentIdentifier => + // use the current host and the process id as two servers could run on the same machine + Environment.MachineName + "-" + System.Diagnostics.Process.GetCurrentProcess().Id; - public virtual IVirtualFolder WebRootFolder { get; private set; } - public virtual IVirtualFolder AppDataFolder { get; private set; } - public virtual IVirtualFolder ThemesFolder { get; private set; } - public virtual IVirtualFolder PluginsFolder { get; private set; } - public virtual IVirtualFolder TenantFolder { get; private set; } - } + public virtual IVirtualFolder WebRootFolder { get; private set; } + public virtual IVirtualFolder AppDataFolder { get; private set; } + public virtual IVirtualFolder ThemesFolder { get; private set; } + public virtual IVirtualFolder PluginsFolder { get; private set; } + public virtual IVirtualFolder TenantFolder { get; private set; } + } } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/ApplicationStart.cs b/src/Libraries/SmartStore.Core/Infrastructure/ApplicationStart.cs new file mode 100644 index 0000000000..14b51e277c --- /dev/null +++ b/src/Libraries/SmartStore.Core/Infrastructure/ApplicationStart.cs @@ -0,0 +1,180 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using System.Web.Mvc.Filters; +using SmartStore.Core.Logging; + +namespace SmartStore.Core.Infrastructure +{ + /// + /// Activated and executed BEFORE Application_Start. Don't use any dependencies here, they are not bootstrapped yet. + /// Only invoke low-level code like registering an HttpModule. + /// + public interface IPreApplicationStart + { + void Start(); + } + + /// + /// Activated and executed DURING Application_Start. Don't use request scoped dependencies here, because is still null at this stage. + /// + public interface IApplicationStart + { + void Start(); + int Order { get; } + } + + /// + /// Activated and executed once AFTER Application_Start with the very first request and very early in the request lifecycle. + /// Invoke app initialization code here that depends on other services like IDbContext etc. + /// + public interface IPostApplicationStart + { + void Start(HttpContextBase httpContext); + + /// + /// Called when an error occurred and is false. + /// + /// The error + /// true when current attempt count is less than , false otherwise. + void OnFail(Exception exception, bool willRetry); + + int Order { get; } + + /// + /// Whether to throw any error and stop execution of subsequent tasks. + /// If this is false, the task will be executed times max when an error occurs. + /// For every error will be invoked to give you the chance to do some logging or fix things. + /// After that, the task will be removed from the queue. + /// + bool ThrowOnError { get; } + + /// + /// The number of maximum execution attempts before this task is removed from the queue. + /// Has no effect if is true. + /// + int MaxAttempts { get; } + } + + public sealed class PostApplicationStartFilter : IAuthenticationFilter + { + private readonly static object _lock = new object(); + private static bool _initializing = false; + private static List _starterModuleInfos; + + public void OnAuthentication(AuthenticationContext filterContext) + { + var request = filterContext?.HttpContext?.Request; + if (request == null) + return; + + if (filterContext.IsChildAction) + return; + + lock (_lock) + { + if (!_initializing) + { + _initializing = true; + + var pendingModules = GetStarterModuleInfos(); + + var modules = pendingModules + .Select(x => new StarterModule + { + Info = x, + Instance = EngineContext.Current.ContainerManager.ResolveUnregistered(x.ModuleType) as IPostApplicationStart + }) + //.Where(x => x.Info.Attempts < Math.Max(1, x.Instance.MaxAttempts)) + .OrderBy(x => x.Instance.Order) + .ToArray(); + + foreach (var module in modules) + { + var info = module.Info; + var instance = module.Instance; + var maxAttempts = Math.Max(1, instance.MaxAttempts); + var fail = false; + + try + { + info.Attempts++; + instance.Start(filterContext.HttpContext); + } + catch (Exception ex) + { + fail = true; + if (instance.ThrowOnError) + { + if (info.Attempts <= maxAttempts) + { + // Don't pollute event log + var logger = EngineContext.Current.Resolve().CreateLogger(); + logger.ErrorFormat(ex, "Error while executing post startup task '{0}': {1}", info.ModuleType, ex.Message); + } + _initializing = false; + throw; + } + else + { + instance.OnFail(ex, info.Attempts < maxAttempts); + } + } + finally + { + var tooManyFailures = info.Attempts >= maxAttempts; + var canRemove = !fail || (!instance.ThrowOnError && tooManyFailures); + + if (canRemove) + { + pendingModules.Remove(info); + } + } + } + + if (pendingModules.Count == 0) + { + // No more pending starter modules anymore. + // Don't run this filter from now on. + GlobalFilters.Filters.Remove(this); + } + + _initializing = false; + } + } + } + + private static List GetStarterModuleInfos() + { + if (_starterModuleInfos == null) + { + var typeFinder = EngineContext.Current.Resolve(); + var starterTypes = typeFinder.FindClassesOfType(true, true); + _starterModuleInfos = starterTypes + .Select(x => new StarterModuleInfo { ModuleType = x }) + .ToList(); + } + + return _starterModuleInfos; + } + + public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext) + { + // Noop + } + + class StarterModuleInfo + { + public Type ModuleType { get; set; } + public int Attempts { get; set; } + } + + class StarterModule + { + public StarterModuleInfo Info { get; set; } + public IPostApplicationStart Instance { get; set; } + } + } +} diff --git a/src/Libraries/SmartStore.Core/Infrastructure/ComparableObject.cs b/src/Libraries/SmartStore.Core/Infrastructure/ComparableObject.cs index 153405382b..9463d4a094 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/ComparableObject.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/ComparableObject.cs @@ -1,15 +1,20 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; -using SmartStore.Utilities; -using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; using SmartStore.ComponentModel; +using SmartStore.Utilities; namespace SmartStore { + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] + public sealed class ObjectSignatureAttribute : Attribute + { + } + /// /// Provides a standard base class for facilitating sophisticated comparison of objects. /// @@ -18,7 +23,7 @@ public abstract class ComparableObject { private readonly HashSet _extraSignatureProperties = new HashSet(StringComparer.OrdinalIgnoreCase); - private static readonly ConcurrentDictionary _signaturePropertyNames = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary _signaturePropertyNames = new ConcurrentDictionary(); public override bool Equals(object obj) { @@ -46,19 +51,19 @@ public override int GetHashCode() var signatureProperties = GetSignatureProperties().ToArray(); Type t = this.GetType(); - var combiner = HashCodeCombiner.Start(); + var combiner = HashCodeCombiner.Start(); - // It's possible for two objects to return the same hash code based on - // identically valued properties, even if they're of two different types, - // so we include the object's type in the hash calculation - combiner.Add(t.GetHashCode()); + // It's possible for two objects to return the same hash code based on + // identically valued properties, even if they're of two different types, + // so we include the object's type in the hash calculation + combiner.Add(t.GetHashCode()); foreach (var prop in signatureProperties) { var value = prop.GetValue(this); if (value != null) - combiner.Add(value.GetHashCode()); + combiner.Add(value.GetHashCode()); } if (signatureProperties.Length > 0) @@ -113,17 +118,17 @@ protected virtual bool HasSameSignatureAs(ComparableObject compareTo) /// public IEnumerable GetSignatureProperties() { - var type = GetType(); - var propertyNames = GetSignaturePropertyNamesCore(); - - foreach (var name in propertyNames) - { - var fastProperty = FastProperty.GetProperty(type, name); - if (fastProperty != null) - { - yield return fastProperty; - } - } + var type = GetType(); + var propertyNames = GetSignaturePropertyNamesCore(); + + foreach (var name in propertyNames) + { + var fastProperty = FastProperty.GetProperty(type, name); + if (fastProperty != null) + { + yield return fastProperty; + } + } } /// @@ -133,22 +138,22 @@ public IEnumerable GetSignatureProperties() protected virtual string[] GetSignaturePropertyNamesCore() { Type type = this.GetType(); - string[] names; + string[] names; - if (!_signaturePropertyNames.TryGetValue(type, out names)) - { - names = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) - .Where(p => Attribute.IsDefined(p, typeof(ObjectSignatureAttribute), true)) - .Select(p => p.Name) - .ToArray(); + if (!_signaturePropertyNames.TryGetValue(type, out names)) + { + names = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(p => Attribute.IsDefined(p, typeof(ObjectSignatureAttribute), true)) + .Select(p => p.Name) + .ToArray(); - _signaturePropertyNames.TryAdd(type, names); - } + _signaturePropertyNames.TryAdd(type, names); + } - if (_extraSignatureProperties.Count == 0) - { - return names; - } + if (_extraSignatureProperties.Count == 0) + { + return names; + } return names.Union(_extraSignatureProperties).ToArray(); } @@ -163,12 +168,11 @@ protected void RegisterSignatureProperty(string propertyName) { Guard.NotEmpty(propertyName, nameof(propertyName)); - _extraSignatureProperties.Add(propertyName); + _extraSignatureProperties.Add(propertyName); } } - /// /// Generic version of . /// @@ -198,5 +202,4 @@ public virtual bool Equals(T other) return base.Equals(other); } } - } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/ContextState.cs b/src/Libraries/SmartStore.Core/Infrastructure/ContextState.cs index e59bd5f44a..ae9af5f59e 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/ContextState.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/ContextState.cs @@ -4,86 +4,86 @@ namespace SmartStore.Core.Infrastructure { - /// - /// Holds some state for the current HttpContext or thread - /// - /// The type of data to store - public class ContextState where T : class - { - private readonly string _name; - private readonly Func _defaultValue; + /// + /// Holds some state for the current HttpContext or thread + /// + /// The type of data to store + public class ContextState where T : class + { + private readonly string _name; + private readonly Func _defaultValue; - public ContextState(string name) - { - _name = name; - } + public ContextState(string name) + { + _name = name; + } - public ContextState(string name, Func defaultValue) - { - _name = name; - _defaultValue = defaultValue; - } + public ContextState(string name, Func defaultValue) + { + _name = name; + _defaultValue = defaultValue; + } - public T GetState() - { - var key = BuildKey(); + public T GetState() + { + var key = BuildKey(); - if (HttpContext.Current == null) - { - var data = CallContext.GetData(key); - - if (data == null) - { - if (_defaultValue != null) - { - CallContext.SetData(key, data = _defaultValue()); - return data as T; - } - } - - return data as T; - } + if (HttpContext.Current == null) + { + var data = CallContext.GetData(key); - if (HttpContext.Current.Items[key] == null) - { - HttpContext.Current.Items[key] = _defaultValue?.Invoke(); - } + if (data == null) + { + if (_defaultValue != null) + { + CallContext.SetData(key, data = _defaultValue()); + return data as T; + } + } - return HttpContext.Current.Items[key] as T; - } + return data as T; + } - public void SetState(T state) - { - if (HttpContext.Current == null) - { - CallContext.SetData(BuildKey(), state); - } - else - { - HttpContext.Current.Items[BuildKey()] = state; - } - } + if (HttpContext.Current.Items[key] == null) + { + HttpContext.Current.Items[key] = _defaultValue?.Invoke(); + } - public void RemoveState() - { - var key = BuildKey(); + return HttpContext.Current.Items[key] as T; + } - if (HttpContext.Current == null) - { - CallContext.FreeNamedDataSlot(key); - } - else - { - if (HttpContext.Current.Items.Contains(key)) - { - HttpContext.Current.Items.Remove(key); - } - } - } + public void SetState(T state) + { + if (HttpContext.Current == null) + { + CallContext.SetData(BuildKey(), state); + } + else + { + HttpContext.Current.Items[BuildKey()] = state; + } + } - private string BuildKey() - { - return "__ContextState." + _name; - } - } + public void RemoveState() + { + var key = BuildKey(); + + if (HttpContext.Current == null) + { + CallContext.FreeNamedDataSlot(key); + } + else + { + if (HttpContext.Current.Items.Contains(key)) + { + HttpContext.Current.Items.Remove(key); + } + } + } + + private string BuildKey() + { + return "__ContextState." + _name; + } + } } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/AutofacRequestLifetimeHttpModule.cs b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/AutofacRequestLifetimeHttpModule.cs index f101841492..d32030bcbd 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/AutofacRequestLifetimeHttpModule.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/AutofacRequestLifetimeHttpModule.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Web; using Autofac.Integration.Mvc; @@ -10,68 +9,68 @@ namespace SmartStore.Core.Infrastructure.DependencyManagement /// that creates a nested lifetime scope for each HTTP request. /// public class AutofacRequestLifetimeHttpModule : IHttpModule - { - public void Init(HttpApplication context) - { - Guard.NotNull(context, nameof(context)); + { + public void Init(HttpApplication context) + { + Guard.NotNull(context, nameof(context)); - context.EndRequest += OnEndRequest; - } + context.EndRequest += OnEndRequest; + } - public static void OnEndRequest(object sender, EventArgs e) - { - if (LifetimeScopeProvider != null) - { - LifetimeScopeProvider.EndLifetimeScope(); - } + public static void OnEndRequest(object sender, EventArgs e) + { + if (LifetimeScopeProvider != null) + { + LifetimeScopeProvider.EndLifetimeScope(); + } - // Dispose all other disposable object in HttpContext.Items - PurgeContextItems(sender as HttpApplication); - } + // Dispose all other disposable object in HttpContext.Items + PurgeContextItems(sender as HttpApplication); + } - private static void PurgeContextItems(HttpApplication app) - { - var items = app?.Context?.Items; + private static void PurgeContextItems(HttpApplication app) + { + var items = app?.Context?.Items; - if (items != null) - { - int size = items.Count; - if (size > 0) - { - var keys = new object[size]; - items.Keys.CopyTo(keys, 0); + if (items != null) + { + int size = items.Count; + if (size > 0) + { + var keys = new object[size]; + items.Keys.CopyTo(keys, 0); - for (int i = 0; i < size; i++) - { - var obj = items[keys[i]] as IDisposable; - if (obj != null) - { - try - { - obj.Dispose(); - } - catch { } - } - } - } - } - } + for (int i = 0; i < size; i++) + { + var obj = items[keys[i]] as IDisposable; + if (obj != null) + { + try + { + obj.Dispose(); + } + catch { } + } + } + } + } + } - public static void SetLifetimeScopeProvider(ILifetimeScopeProvider lifetimeScopeProvider) - { - LifetimeScopeProvider = lifetimeScopeProvider ?? throw new ArgumentNullException("lifetimeScopeProvider"); - } + public static void SetLifetimeScopeProvider(ILifetimeScopeProvider lifetimeScopeProvider) + { + LifetimeScopeProvider = lifetimeScopeProvider ?? throw new ArgumentNullException("lifetimeScopeProvider"); + } - internal static ILifetimeScopeProvider LifetimeScopeProvider - { - get; - private set; - } + internal static ILifetimeScopeProvider LifetimeScopeProvider + { + get; + private set; + } - public void Dispose() - { - } + public void Dispose() + { + } - } + } } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs index 8a934935e0..dbbef7fe1a 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ContainerManager.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Autofac; using Autofac.Builder; using SmartStore.ComponentModel; @@ -15,45 +16,45 @@ namespace SmartStore.Core.Infrastructure.DependencyManagement public class ContainerManager { private readonly IContainer _container; - private readonly ConcurrentDictionary _cachedActivators = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _cachedActivators = new ConcurrentDictionary(); public ContainerManager(IContainer container) { _container = container; } - public IContainer Container - { - get { return _container; } - } + public IContainer Container => _container; [DebuggerStepThrough] public T Resolve(object key = null, ILifetimeScope scope = null) where T : class { if (key == null) { - return (scope ?? Scope()).Resolve(); + return (scope ?? Scope()).Resolve(); } - return (scope ?? Scope()).ResolveKeyed(key); + return (scope ?? Scope()).ResolveKeyed(key); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public T ResolveNamed(string name, ILifetimeScope scope = null) where T : class { - return (scope ?? Scope()).ResolveNamed(name); + return (scope ?? Scope()).ResolveNamed(name); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public object Resolve(Type type, ILifetimeScope scope = null) { - return (scope ?? Scope()).Resolve(type); + return (scope ?? Scope()).Resolve(type); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public object ResolveNamed(string name, Type type, ILifetimeScope scope = null) { - return (scope ?? Scope()).ResolveNamed(name, type); + return (scope ?? Scope()).ResolveNamed(name, type); } [DebuggerStepThrough] @@ -61,10 +62,10 @@ public T[] ResolveAll(object key = null, ILifetimeScope scope = null) { if (key == null) { - return (scope ?? Scope()).Resolve>().ToArray(); + return (scope ?? Scope()).Resolve>().ToArray(); } - return (scope ?? Scope()).ResolveKeyed>(key).ToArray(); + return (scope ?? Scope()).ResolveKeyed>(key).ToArray(); } public T ResolveUnregistered(ILifetimeScope scope = null) where T : class @@ -72,143 +73,138 @@ public T ResolveUnregistered(ILifetimeScope scope = null) where T : class return ResolveUnregistered(typeof(T), scope) as T; } - public object ResolveUnregistered(Type type, ILifetimeScope scope = null) - { - object[] parameterInstances = null; - - if (!_cachedActivators.TryGetValue(type, out FastActivator activator)) - { - var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); - foreach (var constructor in constructors) - { - var parameterTypes = constructor.GetParameters().Select(p => p.ParameterType).ToArray(); - if (TryResolveAll(parameterTypes, out parameterInstances, scope)) - { - activator = new FastActivator(constructor); - _cachedActivators.TryAdd(type, activator); - break; - } - } - } - - if (activator != null) - { - if (parameterInstances == null) - { - TryResolveAll(activator.ParameterTypes, out parameterInstances, scope); + public object ResolveUnregistered(Type type, ILifetimeScope scope = null) + { + object[] parameterInstances = null; + + if (!_cachedActivators.TryGetValue(type, out FastActivator activator)) + { + var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); + foreach (var constructor in constructors) + { + var parameterTypes = constructor.GetParameters().Select(p => p.ParameterType).ToArray(); + if (TryResolveAll(parameterTypes, out parameterInstances, scope)) + { + activator = new FastActivator(constructor); + _cachedActivators.TryAdd(type, activator); + break; + } } + } - if (parameterInstances != null) - { - return activator.Activate(parameterInstances); - } - } + if (activator != null) + { + if (parameterInstances == null) + { + TryResolveAll(activator.ParameterTypes, out parameterInstances, scope); + } + + if (parameterInstances != null) + { + return activator.Activate(parameterInstances); + } + } throw new SmartException("No constructor for {0} was found that had all the dependencies satisfied.".FormatInvariant(type.Name.NaIfEmpty())); } - private bool TryResolveAll(Type[] types, out object[] instances, ILifetimeScope scope = null) - { - instances = null; + private bool TryResolveAll(Type[] types, out object[] instances, ILifetimeScope scope = null) + { + instances = null; - try - { - var instances2 = new object[types.Length]; + try + { + var instances2 = new object[types.Length]; - for (int i = 0; i < types.Length; i++) - { - var service = Resolve(types[i], scope); - if (service == null) - { - return false; - } + for (int i = 0; i < types.Length; i++) + { + var service = Resolve(types[i], scope); + if (service == null) + { + return false; + } - instances2[i] = service; - } + instances2[i] = service; + } - instances = instances2; - return true; - } - catch (Exception ex) - { - _container.Resolve().GetLogger(this.GetType()).Error(ex); - return false; - } - } + instances = instances2; + return true; + } + catch (Exception ex) + { + _container.Resolve().GetLogger(this.GetType()).Error(ex); + return false; + } + } [DebuggerStepThrough] public bool TryResolve(Type serviceType, ILifetimeScope scope, out object instance) { - instance = null; + instance = null; - try - { - return (scope ?? Scope()).TryResolve(serviceType, out instance); - } - catch - { - return false; - } + try + { + return (scope ?? Scope()).TryResolve(serviceType, out instance); + } + catch + { + return false; + } } [DebuggerStepThrough] public bool TryResolve(ILifetimeScope scope, out T instance) - { - instance = default(T); + where T : class + { + instance = default(T); - try - { - return (scope ?? Scope()).TryResolve(out instance); - } - catch - { - return false; - } - } + try + { + return (scope ?? Scope()).TryResolve(out instance); + } + catch + { + return false; + } + } - public bool IsRegistered(Type serviceType, ILifetimeScope scope = null) + public bool IsRegistered(Type serviceType, ILifetimeScope scope = null) { - return (scope ?? Scope()).IsRegistered(serviceType); + return (scope ?? Scope()).IsRegistered(serviceType); } - public object ResolveOptional(Type serviceType, ILifetimeScope scope = null) + public object ResolveOptional(Type serviceType, ILifetimeScope scope = null) { - return (scope ?? Scope()).ResolveOptional(serviceType); + return (scope ?? Scope()).ResolveOptional(serviceType); } - public T InjectProperties(T instance, ILifetimeScope scope = null) - { - return (scope ?? Scope()).InjectProperties(instance); - } + public T InjectProperties(T instance, ILifetimeScope scope = null) + { + return (scope ?? Scope()).InjectProperties(instance); + } - public T InjectUnsetProperties(T instance, ILifetimeScope scope = null) - { - return (scope ?? Scope()).InjectUnsetProperties(instance); - } + public T InjectUnsetProperties(T instance, ILifetimeScope scope = null) + { + return (scope ?? Scope()).InjectUnsetProperties(instance); + } [DebuggerStepThrough] public ILifetimeScope Scope() { - var scope = _container.Resolve().GetLifetimeScope(null); - return scope ?? _container; + var scope = _container.Resolve().GetLifetimeScope(null); + return scope ?? _container; } - public ILifetimeScopeAccessor ScopeAccessor - { - get - { - return _container.Resolve(); - } - } + public ILifetimeScopeAccessor ScopeAccessor => _container.Resolve(); } public static class ContainerManagerExtensions { - public static IRegistrationBuilder WithNullCache(this IRegistrationBuilder registration) where TReflectionActivatorData : ReflectionActivatorData - { - return registration.WithParameter(Autofac.Core.ResolvedParameter.ForNamed("null")); - } + public static IRegistrationBuilder WithNullCache(this IRegistrationBuilder registration) where TReflectionActivatorData : ReflectionActivatorData + { + return registration.WithParameter(Autofac.Core.ResolvedParameter.ForNamed("null")); + } } } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/DefaultLifetimeScopeAccessor.cs b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/DefaultLifetimeScopeAccessor.cs index 1c7fed864c..b4b0886ac5 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/DefaultLifetimeScopeAccessor.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/DefaultLifetimeScopeAccessor.cs @@ -1,88 +1,99 @@ using System; -using System.Diagnostics; using System.Web; using Autofac; -using Autofac.Core.Lifetime; -using SmartStore.Core.Data; -using SmartStore.Core.Logging; using SmartStore.Utilities; namespace SmartStore.Core.Infrastructure.DependencyManagement { - public class DefaultLifetimeScopeAccessor : ILifetimeScopeAccessor - { - private ContextState _state; - private readonly ILifetimeScope _rootContainer; - internal static readonly object ScopeTag = "AutofacWebRequest"; - - public DefaultLifetimeScopeAccessor(ILifetimeScope rootContainer) - { - Guard.NotNull(rootContainer, nameof(rootContainer)); - - //rootContainer.ChildLifetimeScopeBeginning += OnScopeBeginning; - - this._rootContainer = rootContainer; - this._state = new ContextState("CustomLifetimeScopeProvider.WorkScope"); - } - - public ILifetimeScope ApplicationContainer - { - get { return _rootContainer; } - } - - public IDisposable BeginContextAwareScope() - { - var disposer = HttpContext.Current != null - ? ActionDisposable.Empty - : new ActionDisposable(() => this.EndLifetimeScope()); - - return disposer; - } - - public void EndLifetimeScope() - { - try - { - var scope = _state.GetState(); - if (scope != null) - { - scope.Dispose(); - _state.RemoveState(); - } - } - catch { } - } - - public ILifetimeScope GetLifetimeScope(Action configurationAction) - { - var scope = _state.GetState(); - if (scope == null) - { - _state.SetState((scope = BeginLifetimeScope(configurationAction))); - //scope.CurrentScopeEnding += OnScopeEnding; - } - - return scope; - } - - //private void OnScopeBeginning(object sender, LifetimeScopeBeginningEventArgs args) - //{ - // bool isWeb = System.Web.HttpContext.Current != null; - // Debug.WriteLine("Scope Begin, Web: " + isWeb); - //} - - //private void OnScopeEnding(object sender, LifetimeScopeEndingEventArgs args) - //{ - // bool isWeb = System.Web.HttpContext.Current != null; - // Debug.WriteLine("Scope END, Web: " + isWeb); - //} - - public ILifetimeScope BeginLifetimeScope(Action configurationAction) - { - return (configurationAction == null) - ? _rootContainer.BeginLifetimeScope(ScopeTag) - : _rootContainer.BeginLifetimeScope(ScopeTag, configurationAction); - } - - } + public class DefaultLifetimeScopeAccessor : ILifetimeScopeAccessor + { + class ContextAwareScope : IDisposable + { + private readonly Action _disposer; + + public ContextAwareScope(Action disposer) + { + _disposer = disposer; + } + + public void Dispose() + { + _disposer?.Invoke(); + } + } + + private readonly ContextState _state; + private readonly ILifetimeScope _rootContainer; + internal static readonly object ScopeTag = "AutofacWebRequest"; + + public DefaultLifetimeScopeAccessor(ILifetimeScope rootContainer) + { + Guard.NotNull(rootContainer, nameof(rootContainer)); + + //rootContainer.ChildLifetimeScopeBeginning += OnScopeBeginning; + + _rootContainer = rootContainer; + _state = new ContextState("CustomLifetimeScopeProvider.WorkScope"); + } + + public ILifetimeScope ApplicationContainer => _rootContainer; + + public IDisposable BeginContextAwareScope() + { + // Stack-like behaviour for Non-HttpContext thread: + // Only the first call returns a disposer, all nested calls to this method are void. + return HttpContext.Current != null + ? (IDisposable)ActionDisposable.Empty + : new ContextAwareScope( + _state.GetState() == null + ? this.EndLifetimeScope + : (Action)null); + } + + public void EndLifetimeScope() + { + try + { + var scope = _state.GetState(); + if (scope != null) + { + scope.Dispose(); + _state.RemoveState(); + } + } + catch { } + } + + public ILifetimeScope GetLifetimeScope(Action configurationAction) + { + var scope = _state.GetState(); + if (scope == null) + { + _state.SetState((scope = BeginLifetimeScope(configurationAction))); + //scope.CurrentScopeEnding += OnScopeEnding; + } + + return scope; + } + + //private void OnScopeBeginning(object sender, LifetimeScopeBeginningEventArgs args) + //{ + // bool isWeb = System.Web.HttpContext.Current != null; + // Debug.WriteLine("Scope Begin, Web: " + isWeb); + //} + + //private void OnScopeEnding(object sender, LifetimeScopeEndingEventArgs args) + //{ + // bool isWeb = System.Web.HttpContext.Current != null; + // Debug.WriteLine("Scope END, Web: " + isWeb); + //} + + public ILifetimeScope BeginLifetimeScope(Action configurationAction) + { + return (configurationAction == null) + ? _rootContainer.BeginLifetimeScope(ScopeTag) + : _rootContainer.BeginLifetimeScope(ScopeTag, configurationAction); + } + + } } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/DefaultLifetimeScopeProvider.cs b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/DefaultLifetimeScopeProvider.cs index 78421250f6..eab1548dc0 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/DefaultLifetimeScopeProvider.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/DefaultLifetimeScopeProvider.cs @@ -4,32 +4,29 @@ namespace SmartStore.Core.Infrastructure.DependencyManagement { - public class DefaultLifetimeScopeProvider : ILifetimeScopeProvider - { - private readonly ILifetimeScopeAccessor _accessor; + public class DefaultLifetimeScopeProvider : ILifetimeScopeProvider + { + private readonly ILifetimeScopeAccessor _accessor; - public DefaultLifetimeScopeProvider(ILifetimeScopeAccessor accessor) - { - Guard.NotNull(accessor, nameof(accessor)); + public DefaultLifetimeScopeProvider(ILifetimeScopeAccessor accessor) + { + Guard.NotNull(accessor, nameof(accessor)); - this._accessor = accessor; - AutofacRequestLifetimeHttpModule.SetLifetimeScopeProvider(this); - } + this._accessor = accessor; + AutofacRequestLifetimeHttpModule.SetLifetimeScopeProvider(this); + } - public ILifetimeScope ApplicationContainer - { - get { return _accessor.ApplicationContainer; } - } + public ILifetimeScope ApplicationContainer => _accessor.ApplicationContainer; - public void EndLifetimeScope() - { - _accessor.EndLifetimeScope(); - } + public void EndLifetimeScope() + { + _accessor.EndLifetimeScope(); + } - public ILifetimeScope GetLifetimeScope(Action configurationAction) - { - return _accessor.GetLifetimeScope(configurationAction); - } + public ILifetimeScope GetLifetimeScope(Action configurationAction) + { + return _accessor.GetLifetimeScope(configurationAction); + } - } + } } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ILifetimeScopeAccessor.cs b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ILifetimeScopeAccessor.cs index ee09073226..da03f8fba5 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ILifetimeScopeAccessor.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/ILifetimeScopeAccessor.cs @@ -1,52 +1,51 @@ using System; -using System.Collections.Generic; using Autofac; namespace SmartStore.Core.Infrastructure.DependencyManagement { - public interface ILifetimeScopeAccessor - { - /// - /// Gets the global, application-wide container. - /// - ILifetimeScope ApplicationContainer { get; } + public interface ILifetimeScopeAccessor + { + /// + /// Gets the global, application-wide container. + /// + ILifetimeScope ApplicationContainer { get; } - /// - /// Ends the current lifetime scope. - /// - void EndLifetimeScope(); + /// + /// Ends the current lifetime scope. + /// + void EndLifetimeScope(); - /// - /// Gets a nested lifetime scope that services can be resolved from - /// - /// A configuration action that will execute during lifetime scope creation. - /// - /// A new or existing nested lifetime scope. - /// - ILifetimeScope GetLifetimeScope(Action configurationAction); + /// + /// Gets a nested lifetime scope that services can be resolved from + /// + /// A configuration action that will execute during lifetime scope creation. + /// + /// A new or existing nested lifetime scope. + /// + ILifetimeScope GetLifetimeScope(Action configurationAction); - /// - /// Either creates a new lifetime scope when HttpContext.Current is null, - /// OR returns the current http context scoped lifetime. - /// - /// - /// A disposable object which does nothing when internal lifetime scope is bound to the http context, - /// OR ends the lifetime scope otherwise. - /// - /// - /// This method is intended for usage in background threads or tasks. There may be situations where HttpContext is present, - /// especially when a task was started with TaskScheduler.FromCurrentSynchronizationContext(). In this case it may not be - /// desirable to create a new scope, but use the existing, http context bound scope instead. - /// - IDisposable BeginContextAwareScope(); + /// + /// Either creates a new lifetime scope when HttpContext.Current is null, + /// OR returns the current http context scoped lifetime. + /// + /// + /// A disposable object which does nothing when internal lifetime scope is bound to the http context, + /// OR ends the lifetime scope otherwise. + /// + /// + /// This method is intended for usage in background threads or tasks. There may be situations where HttpContext is present, + /// especially when a task was started with TaskScheduler.FromCurrentSynchronizationContext(). In this case it may not be + /// desirable to create a new scope, but use the existing, http context bound scope instead. + /// + IDisposable BeginContextAwareScope(); - /// - /// Creates a nested lifetime scope that services can be resolved from - /// - /// A configuration action that will execute during lifetime scope creation. - /// - /// A new nested lifetime scope. - /// - ILifetimeScope BeginLifetimeScope(Action configurationAction); - } + /// + /// Creates a nested lifetime scope that services can be resolved from + /// + /// A configuration action that will execute during lifetime scope creation. + /// + /// A new nested lifetime scope. + /// + ILifetimeScope BeginLifetimeScope(Action configurationAction); + } } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/Work.cs b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/Work.cs index afcba925eb..7c2bf558aa 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/Work.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/DependencyManagement/Work.cs @@ -2,21 +2,15 @@ namespace SmartStore.Core.Infrastructure.DependencyManagement { - public class Work where T : class - { - private readonly Func, T> _resolve; + public class Work where T : class + { + private readonly Func, T> _resolve; - public Work(Func, T> resolve) - { - _resolve = resolve; - } + public Work(Func, T> resolve) + { + _resolve = resolve; + } - public T Value - { - get - { - return _resolve(this); - } - } - } + public T Value => _resolve(this); + } } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/DisposableObject.cs b/src/Libraries/SmartStore.Core/Infrastructure/DisposableObject.cs index e3b01f2e42..34dc6784b2 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/DisposableObject.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/DisposableObject.cs @@ -5,7 +5,6 @@ namespace SmartStore { - public abstract class DisposableObject : IDisposable { private bool _isDisposed; @@ -13,7 +12,7 @@ public abstract class DisposableObject : IDisposable public virtual bool IsDisposed { [DebuggerStepThrough] - get { return _isDisposed; } + get => _isDisposed; } [DebuggerStepThrough] @@ -37,11 +36,11 @@ protected void CheckDisposed(string errorMessage) [DebuggerStepThrough] public void Dispose() { - if (!_isDisposed) - { - Dispose(true); - GC.SuppressFinalize(this); - } + if (!_isDisposed) + { + Dispose(true); + GC.SuppressFinalize(this); + } } protected void Dispose(bool disposing) @@ -104,7 +103,5 @@ protected static void DisposeMember(object member) { Dispose(false); } - } - } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/EngineContext.cs b/src/Libraries/SmartStore.Core/Infrastructure/EngineContext.cs index e610c7b134..2a40869ee4 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/EngineContext.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/EngineContext.cs @@ -1,8 +1,6 @@ using System; using System.Configuration; -using System.Diagnostics; using System.Runtime.CompilerServices; -using SmartStore.Core.Configuration; using SmartStore.Utilities; namespace SmartStore.Core.Infrastructure @@ -35,29 +33,29 @@ public static void Replace(IEngine engine) { Singleton.Instance = engine; } - + /// /// Creates a factory instance and adds a http application injecting facility. /// /// A new factory public static IEngine CreateEngineInstance() { - var engineTypeSetting = CommonHelper.GetAppSetting("sm:EngineType"); - if (engineTypeSetting.HasValue()) + var engineTypeSetting = CommonHelper.GetAppSetting("sm:EngineType"); + if (engineTypeSetting.HasValue()) { - var engineType = Type.GetType(engineTypeSetting); + var engineType = Type.GetType(engineTypeSetting); if (engineType == null) - { - throw new ConfigurationErrorsException("The type '" + engineType + "' could not be found. Please check the configuration at /configuration/appSettings/add[@key=sm:EngineType] or check for missing assemblies."); - } - + { + throw new ConfigurationErrorsException("The type '" + engineType + "' could not be found. Please check the configuration at /configuration/appSettings/add[@key=sm:EngineType] or check for missing assemblies."); + } + if (!typeof(IEngine).IsAssignableFrom(engineType)) - { - throw new ConfigurationErrorsException("The type '" + engineType + "' doesn't implement 'SmartStore.Core.Infrastructure.IEngine' and cannot be configured in /configuration/appSettings/add[@key=sm:EngineType] for that purpose."); - } + { + throw new ConfigurationErrorsException("The type '" + engineType + "' doesn't implement 'SmartStore.Core.Infrastructure.IEngine' and cannot be configured in /configuration/appSettings/add[@key=sm:EngineType] for that purpose."); + } - return Activator.CreateInstance(engineType) as IEngine; + return Activator.CreateInstance(engineType) as IEngine; } return new SmartStoreEngine(); diff --git a/src/Libraries/SmartStore.Core/Infrastructure/Error.cs b/src/Libraries/SmartStore.Core/Infrastructure/Error.cs index 6ef52f2275..bb09490f09 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/Error.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/Error.cs @@ -1,32 +1,35 @@ using System; -using System.Globalization; using System.Diagnostics; +using System.Globalization; +using System.Runtime.CompilerServices; namespace SmartStore { public static class Error { - [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception Application(string message, params object[] args) { return new ApplicationException(message.FormatCurrent(args)); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception Application(Exception innerException, string message, params object[] args) { return new ApplicationException(message.FormatCurrent(args), innerException); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception ArgumentNullOrEmpty(Func arg) { - var argName = arg.Method.Name; - return new ArgumentException("String parameter '{0}' cannot be null or all whitespace.", argName); + return new ArgumentException("String parameter '{0}' cannot be null or all whitespace.", arg.Method.Name); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception ArgumentNull(string argName) { return new ArgumentNullException(argName); @@ -41,35 +44,38 @@ public static Exception ArgumentNull(Func arg) } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception ArgumentOutOfRange(Func arg) { - var argName = arg.Method.Name; - return new ArgumentOutOfRangeException(argName); + return new ArgumentOutOfRangeException(arg.Method.Name); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception ArgumentOutOfRange(string argName) { return new ArgumentOutOfRangeException(argName); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception ArgumentOutOfRange(string argName, string message, params object[] args) { return new ArgumentOutOfRangeException(argName, String.Format(CultureInfo.CurrentCulture, message, args)); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception Argument(string argName, string message, params object[] args) { return new ArgumentException(String.Format(CultureInfo.CurrentCulture, message, args), argName); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception Argument(Func arg, string message, params object[] args) { - var argName = arg.Method.Name; - return new ArgumentException(message.FormatCurrent(args), argName); + return new ArgumentException(message.FormatCurrent(args), arg.Method.Name); } [DebuggerStepThrough] @@ -79,6 +85,7 @@ public static Exception InvalidOperation(string message, params object[] args) } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception InvalidOperation(string message, Exception innerException, params object[] args) { return new InvalidOperationException(message.FormatCurrent(args), innerException); @@ -91,6 +98,7 @@ public static Exception InvalidOperation(string message, Func member) } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception InvalidOperation(string message, Exception innerException, Func member) { Guard.NotNull(message, "message"); @@ -106,42 +114,49 @@ public static Exception InvalidCast(Type fromType, Type toType) } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception InvalidCast(Type fromType, Type toType, Exception innerException) { - return new InvalidCastException("Cannot convert from type '{0}' to '{1}'.".FormatCurrent(fromType.FullName, toType.FullName), innerException); + return new InvalidCastException("Cannot convert from type '{0}' to '{1}'.".FormatCurrent(fromType?.FullName ?? "NULL", toType.FullName), innerException); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception NotSupported() { return new NotSupportedException(); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception NotImplemented() { return new NotImplementedException(); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception ObjectDisposed(string objectName) { return new ObjectDisposedException(objectName); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception ObjectDisposed(string objectName, string message, params object[] args) { return new ObjectDisposedException(objectName, String.Format(CultureInfo.CurrentCulture, message, args)); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception NoElements() { return new InvalidOperationException("Sequence contains no elements."); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Exception MoreThanOneElement() { return new InvalidOperationException("Sequence contains more than one element."); diff --git a/src/Libraries/SmartStore.Core/Infrastructure/Guard.cs b/src/Libraries/SmartStore.Core/Infrastructure/Guard.cs index a1d8f6c73a..15aaef296c 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/Guard.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/Guard.cs @@ -1,167 +1,190 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Linq.Expressions; +using System.Runtime.CompilerServices; namespace SmartStore { public class Guard { - internal const string AgainstMessage = "Assertion evaluation failed with 'false'."; - internal const string ImplementsMessage = "Type '{0}' must implement type '{1}'."; - internal const string InheritsFromMessage = "Type '{0}' must inherit from type '{1}'."; - internal const string IsTypeOfMessage = "Type '{0}' must be of type '{1}'."; - internal const string IsEqualMessage = "Compared objects must be equal."; - internal const string IsPositiveMessage = "Argument '{0}' must be a positive value. Value: '{1}'."; - internal const string IsTrueMessage = "True expected for '{0}' but the condition was False."; - internal const string NotNegativeMessage = "Argument '{0}' cannot be a negative value. Value: '{1}'."; + const string AgainstMessage = "Assertion evaluation failed with 'false'."; + const string ImplementsMessage = "Type '{0}' must implement type '{1}'."; + const string InheritsFromMessage = "Type '{0}' must inherit from type '{1}'."; + const string IsTypeOfMessage = "Type '{0}' must be of type '{1}'."; + const string IsEqualMessage = "Compared objects must be equal."; + const string IsPositiveMessage = "Argument '{0}' must be a positive value. Value: '{1}'."; + const string IsTrueMessage = "True expected for '{0}' but the condition was False."; + const string NotNegativeMessage = "Argument '{0}' cannot be a negative value. Value: '{1}'."; + const string NotEmptyStringMessage = "String parameter '{0}' cannot be null or all whitespace."; + const string NotEmptyColMessage = "Collection cannot be null and must contain at least one item."; + const string NotEmptyGuidMessage = "Argument '{0}' cannot be an empty guid."; + const string InRangeMessage = "The argument '{0}' must be between '{1}' and '{2}'."; + const string NotOutOfLengthMessage = "Argument '{0}' cannot be more than {1} characters long."; + const string NotZeroMessage = "Argument '{0}' must be greater or less than zero. Value: '{1}'."; + const string IsEnumTypeMessage = "Type '{0}' must be a valid Enum type."; + const string IsEnumTypeMessage2 = "The value of the argument '{0}' provided for the enumeration '{1}' is invalid."; + const string IsSubclassOfMessage = "Type '{0}' must be a subclass of type '{1}'."; + const string HasDefaultConstructorMessage = "The type '{0}' must have a default parameterless constructor."; private Guard() { } - #region 3.0 - - [DebuggerStepThrough] - public static void NotNull(object arg, string argName) - { - if (arg == null) - throw new ArgumentNullException(argName); - } - - [DebuggerStepThrough] - public static void NotEmpty(string arg, string argName) - { - if (string.IsNullOrWhiteSpace(arg)) - throw Error.Argument(argName, "String parameter '{0}' cannot be null or all whitespace.", argName); - } - - [DebuggerStepThrough] - 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."); - } - - [DebuggerStepThrough] - public static void NotEmpty(Guid arg, string argName) - { - if (arg == Guid.Empty) - throw Error.Argument(argName, "Argument '{0}' cannot be an empty guid.", argName); - } - - [DebuggerStepThrough] - public static void InRange(T arg, T min, T max, string argName) where T : struct, IComparable - { - if (arg.CompareTo(min) < 0 || arg.CompareTo(max) > 0) - throw Error.ArgumentOutOfRange(argName, "The argument '{0}' must be between '{1}' and '{2}'.", argName, min, max); - } - - [DebuggerStepThrough] - public static void NotOutOfLength(string arg, int maxLength, string argName) - { - if (arg.Trim().Length > maxLength) - { - throw Error.Argument(argName, "Argument '{0}' cannot be more than {1} characters long.", argName, maxLength); - } - } - - [DebuggerStepThrough] - public static void NotNegative(T arg, string argName, string message = NotNegativeMessage) where T : struct, IComparable - { - if (arg.CompareTo(default(T)) < 0) - throw Error.ArgumentOutOfRange(argName, message.FormatInvariant(argName, arg)); - } - - [DebuggerStepThrough] - public static void NotZero(T arg, string argName) where T : struct, IComparable - { - if (arg.CompareTo(default(T)) == 0) - throw Error.ArgumentOutOfRange(argName, "Argument '{0}' must be greater or less than zero. Value: '{1}'.", argName, arg); - } - - [DebuggerStepThrough] - public static void Against(bool assertion, string message = AgainstMessage) where TException : Exception - { - if (assertion) - throw (TException)Activator.CreateInstance(typeof(TException), message); - } - - [DebuggerStepThrough] - public static void Against(Func assertion, string message = AgainstMessage) where TException : Exception - { - //Execute the lambda and if it evaluates to true then throw the exception. - if (assertion()) - throw (TException)Activator.CreateInstance(typeof(TException), message); - } - - [DebuggerStepThrough] - public static void IsPositive(T arg, string argName, string message = IsPositiveMessage) where T : struct, IComparable - { - if (arg.CompareTo(default(T)) < 1) - throw Error.ArgumentOutOfRange(argName, message.FormatInvariant(argName)); - } - - [DebuggerStepThrough] - public static void IsTrue(bool arg, string argName, string message = IsTrueMessage) - { - if (!arg) - throw Error.Argument(argName, message.FormatInvariant(argName)); - } - - [DebuggerStepThrough] - public static void IsEnumType(Type arg, string argName) - { - NotNull(arg, argName); - - if (!arg.IsEnum) - throw Error.Argument(argName, "Type '{0}' must be a valid Enum type.", arg.FullName); - } - - [DebuggerStepThrough] - public static void IsEnumType(Type enumType, object arg, string argName) - { - NotNull(arg, argName); - - if (!Enum.IsDefined(enumType, arg)) - { - throw Error.ArgumentOutOfRange(argName, "The value of the argument '{0}' provided for the enumeration '{1}' is invalid.", argName, enumType.FullName); - } - } - - [DebuggerStepThrough] - public static void NotDisposed(DisposableObject arg, string argName) - { - NotNull(arg, argName); - - if (arg.IsDisposed) - throw Error.ObjectDisposed(argName); - } - - [DebuggerStepThrough] - public static void PagingArgsValid(int indexArg, int sizeArg, string indexArgName, string sizeArgName) - { - NotNegative(indexArg, indexArgName, "PageIndex cannot be below 0"); - - if (indexArg > 0) - { - // if pageIndex is specified (> 0), PageSize CANNOT be 0 - IsPositive(sizeArg, sizeArgName, "PageSize cannot be below 1 if a PageIndex greater 0 was provided."); - } - else - { - // pageIndex 0 actually means: take all! - NotNegative(sizeArg, sizeArgName); - } - } - - #endregion + #region 3.0 [DebuggerStepThrough] - [Obsolete("Use NotNull() with nameof operator instead")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void NotNull(object arg, string argName) + { + if (arg == null) + throw new ArgumentNullException(argName); + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void NotEmpty(string arg, string argName) + { + if (string.IsNullOrWhiteSpace(arg)) + throw Error.Argument(argName, NotEmptyStringMessage, argName); + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void NotEmpty(ICollection arg, string argName) + { + if (arg == null || !arg.Any()) + throw Error.Argument(argName, NotEmptyColMessage); + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void NotEmpty(Guid arg, string argName) + { + if (arg == Guid.Empty) + throw Error.Argument(argName, NotEmptyGuidMessage, argName); + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InRange(T arg, T min, T max, string argName) where T : struct, IComparable + { + if (arg.CompareTo(min) < 0 || arg.CompareTo(max) > 0) + throw Error.ArgumentOutOfRange(argName, InRangeMessage, argName, min, max); + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void NotOutOfLength(string arg, int maxLength, string argName) + { + if (arg.Trim().Length > maxLength) + { + throw Error.Argument(argName, NotOutOfLengthMessage, argName, maxLength); + } + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void NotNegative(T arg, string argName, string message = NotNegativeMessage) where T : struct, IComparable + { + if (arg.CompareTo(default(T)) < 0) + throw Error.ArgumentOutOfRange(argName, message.FormatInvariant(argName, arg)); + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void NotZero(T arg, string argName) where T : struct, IComparable + { + if (arg.CompareTo(default(T)) == 0) + throw Error.ArgumentOutOfRange(argName, NotZeroMessage, argName, arg); + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Against(bool assertion, string message = AgainstMessage) where TException : Exception + { + if (assertion) + throw (TException)Activator.CreateInstance(typeof(TException), message); + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Against(Func assertion, string message = AgainstMessage) where TException : Exception + { + //Execute the lambda and if it evaluates to true then throw the exception. + if (assertion()) + throw (TException)Activator.CreateInstance(typeof(TException), message); + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void IsPositive(T arg, string argName, string message = IsPositiveMessage) where T : struct, IComparable + { + if (arg.CompareTo(default(T)) < 1) + throw Error.ArgumentOutOfRange(argName, message.FormatInvariant(argName)); + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void IsTrue(bool arg, string argName, string message = IsTrueMessage) + { + if (!arg) + throw Error.Argument(argName, message.FormatInvariant(argName)); + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void IsEnumType(Type arg, string argName) + { + NotNull(arg, argName); + + if (!arg.IsEnum) + throw Error.Argument(argName, IsEnumTypeMessage, arg.FullName); + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void IsEnumType(Type enumType, object arg, string argName) + { + NotNull(arg, argName); + + if (!Enum.IsDefined(enumType, arg)) + { + throw Error.ArgumentOutOfRange(argName, IsEnumTypeMessage2, argName, enumType.FullName); + } + } + + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void NotDisposed(DisposableObject arg, string argName) + { + NotNull(arg, argName); + + if (arg.IsDisposed) + throw Error.ObjectDisposed(argName); + } + + [DebuggerStepThrough] + public static void PagingArgsValid(int indexArg, int sizeArg, string indexArgName, string sizeArgName) + { + NotNegative(indexArg, indexArgName, "PageIndex cannot be below 0"); + + if (indexArg > 0) + { + // if pageIndex is specified (> 0), PageSize CANNOT be 0 + IsPositive(sizeArg, sizeArgName, "PageSize cannot be below 1 if a PageIndex greater 0 was provided."); + } + else + { + // pageIndex 0 actually means: take all! + NotNegative(sizeArg, sizeArgName); + } + } + + #endregion + + [DebuggerStepThrough] + [Obsolete("Use NotNull() with nameof operator instead")] public static void ArgumentNotNull(object arg, string argName) { if (arg == null) @@ -169,27 +192,27 @@ public static void ArgumentNotNull(object arg, string argName) } [DebuggerStepThrough] - [Obsolete("Use NotNull() with nameof operator instead")] - public static void ArgumentNotNull(Func arg) + [Obsolete("Use NotNull() with nameof operator instead")] + public static void ArgumentNotNull(Func arg) { if (arg() == null) throw new ArgumentNullException(GetParamName(arg)); } [DebuggerStepThrough] - [Obsolete("Use NotEmpty() with nameof operator instead")] - public static void ArgumentNotEmpty(Func arg) + [Obsolete("Use NotEmpty() with nameof operator instead")] + public static void ArgumentNotEmpty(Func arg) { - if (arg().IsEmpty()) - { - var argName = GetParamName(arg); - throw Error.Argument(argName, "String parameter '{0}' cannot be null or all whitespace.", argName); - } + if (arg().IsEmpty()) + { + var argName = GetParamName(arg); + throw Error.Argument(argName, "String parameter '{0}' cannot be null or all whitespace.", argName); + } } [DebuggerStepThrough] - [Obsolete("Use NotEmpty() with nameof operator instead")] - public static void ArgumentNotEmpty(string arg, string argName) + [Obsolete("Use NotEmpty() with nameof operator instead")] + public static void ArgumentNotEmpty(string arg, string argName) { if (arg.IsEmpty()) throw Error.Argument(argName, "String parameter '{0}' cannot be null or all whitespace.", argName); @@ -202,6 +225,7 @@ public static void InheritsFrom(Type type) } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void InheritsFrom(Type type, string message) { if (type.BaseType != typeof(TBase)) @@ -209,6 +233,7 @@ public static void InheritsFrom(Type type, string message) } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Implements(Type type, string message = ImplementsMessage) { if (!typeof(TInterface).IsAssignableFrom(type)) @@ -221,7 +246,7 @@ public static void IsSubclassOf(Type type) var baseType = typeof(TBase); if (!baseType.IsSubClass(type)) { - throw new InvalidOperationException("Type '{0}' must be a subclass of type '{1}'.".FormatInvariant(type.FullName, baseType.FullName)); + throw new InvalidOperationException(IsSubclassOfMessage.FormatInvariant(type.FullName, baseType.FullName)); } } @@ -232,6 +257,7 @@ public static void IsTypeOf(object instance) } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void IsTypeOf(object instance, string message) { if (!(instance is TType)) @@ -252,29 +278,15 @@ public static void HasDefaultConstructor() } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void HasDefaultConstructor(Type t) { if (!t.HasDefaultConstructor()) - throw Error.InvalidOperation("The type '{0}' must have a default parameterless constructor.", t.FullName); - } - - - [DebuggerStepThrough] - [SuppressMessage("ReSharper", "UnusedMember.Local")] - private static string GetParamName(Expression> expression) - { - var name = string.Empty; - MemberExpression body = expression.Body as MemberExpression; - - if (body != null) - { - name = body.Member.Name; - } - - return name; + throw Error.InvalidOperation(HasDefaultConstructorMessage, t.FullName); } [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static string GetParamName(Func expression) { return expression.Method.Name; diff --git a/src/Libraries/SmartStore.Core/Infrastructure/IApplicationEnvironment.cs b/src/Libraries/SmartStore.Core/Infrastructure/IApplicationEnvironment.cs index c105628a46..4dbf9dc23a 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/IApplicationEnvironment.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/IApplicationEnvironment.cs @@ -1,17 +1,16 @@ -using System; -using SmartStore.Core.IO; +using SmartStore.Core.IO; namespace SmartStore.Core { - public interface IApplicationEnvironment - { - string MachineName { get; } - string EnvironmentIdentifier { get; } + public interface IApplicationEnvironment + { + string MachineName { get; } + string EnvironmentIdentifier { get; } - IVirtualFolder WebRootFolder { get; } - IVirtualFolder AppDataFolder { get; } - IVirtualFolder ThemesFolder { get; } - IVirtualFolder PluginsFolder { get; } - IVirtualFolder TenantFolder { get; } - } + IVirtualFolder WebRootFolder { get; } + IVirtualFolder AppDataFolder { get; } + IVirtualFolder ThemesFolder { get; } + IVirtualFolder PluginsFolder { get; } + IVirtualFolder TenantFolder { get; } + } } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/ICloneable.cs b/src/Libraries/SmartStore.Core/Infrastructure/ICloneable.cs index 6b82c27d85..5c23d126c2 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/ICloneable.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/ICloneable.cs @@ -2,7 +2,6 @@ namespace SmartStore { - /// /// Generic variant of . /// @@ -15,5 +14,4 @@ public interface ICloneable : ICloneable /// The cloned instance new T Clone(); } - } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/IEngine.cs b/src/Libraries/SmartStore.Core/Infrastructure/IEngine.cs index 4454dfc343..cbe93c2a1d 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/IEngine.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/IEngine.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using SmartStore.Core.Configuration; using SmartStore.Core.Infrastructure.DependencyManagement; namespace SmartStore.Core.Infrastructure @@ -14,17 +12,17 @@ namespace SmartStore.Core.Infrastructure public interface IEngine { ContainerManager ContainerManager { get; } - + /// /// Initialize components and plugins in the SmartStore environment. /// void Initialize(); - /// - /// Determines whether the app has been installed, successfully bootstrapped - /// AND the very first HTTP request has been issued. - /// - bool IsFullyInitialized { get; set; } + /// + /// Determines whether the app has been installed, successfully bootstrapped + /// AND the very first HTTP request has been issued. + /// + bool IsFullyInitialized { get; set; } T Resolve(string name = null) where T : class; diff --git a/src/Libraries/SmartStore.Core/Infrastructure/IOrdered.cs b/src/Libraries/SmartStore.Core/Infrastructure/IOrdered.cs index 087ee4505d..ca93fcbd3d 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/IOrdered.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/IOrdered.cs @@ -1,6 +1,4 @@ -using System; - -namespace SmartStore +namespace SmartStore { public interface IOrdered { diff --git a/src/Libraries/SmartStore.Core/Infrastructure/IStartupTask.cs b/src/Libraries/SmartStore.Core/Infrastructure/IStartupTask.cs deleted file mode 100644 index 6451488f0a..0000000000 --- a/src/Libraries/SmartStore.Core/Infrastructure/IStartupTask.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace SmartStore.Core.Infrastructure -{ - public interface IStartupTask - { - void Execute(); - - int Order { get; } - } -} diff --git a/src/Libraries/SmartStore.Core/Infrastructure/ITypeFinder.cs b/src/Libraries/SmartStore.Core/Infrastructure/ITypeFinder.cs index a198846e58..e9f44832e4 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/ITypeFinder.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/ITypeFinder.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; @@ -12,51 +11,47 @@ namespace SmartStore.Core.Infrastructure /// public interface ITypeFinder { - ///// - ///// Gets all SmartStore specific assemblies (core & all plugins) - ///// - ///// Indicates whether uninstalled plugin's assemblies should be ignored - ///// A list of assemblies that should be loaded by the SmartStore factory. - IEnumerable GetAssemblies(bool ignoreInactivePlugins = false); - IEnumerable FindClassesOfType(Type assignTypeFrom, IEnumerable assemblies, bool onlyConcreteClasses = true); + ///// + ///// Gets all SmartStore specific assemblies (core & all plugins) + ///// + ///// Indicates whether uninstalled plugin's assemblies should be ignored + ///// A list of assemblies that should be loaded by the SmartStore factory. + IEnumerable GetAssemblies(bool ignoreInactivePlugins = false); + IEnumerable FindClassesOfType(Type assignTypeFrom, IEnumerable assemblies, bool onlyConcreteClasses = true); } - public class NullTypeFinder : ITypeFinder - { - private static readonly ITypeFinder s_instance = new NullTypeFinder(); - - public static ITypeFinder Instance - { - get { return s_instance; } - } - - public IEnumerable GetAssemblies(bool ignoreInactivePlugins = false) - { - return Enumerable.Empty(); - } - - public IEnumerable FindClassesOfType(Type assignTypeFrom, IEnumerable assemblies, bool onlyConcreteClasses = true) - { - return Enumerable.Empty(); - } - } - - public static class ITypeFinderExtensions - { - public static IEnumerable FindClassesOfType(this ITypeFinder finder, bool onlyConcreteClasses = true, bool ignoreInactivePlugins = false) - { - return finder.FindClassesOfType(typeof(T), finder.GetAssemblies(ignoreInactivePlugins), onlyConcreteClasses); - } - - public static IEnumerable FindClassesOfType(this ITypeFinder finder, Type assignTypeFrom, bool onlyConcreteClasses = true, bool ignoreInactivePlugins = false) - { - return finder.FindClassesOfType(assignTypeFrom, finder.GetAssemblies(ignoreInactivePlugins), onlyConcreteClasses); - } - - public static IEnumerable FindClassesOfType(this ITypeFinder finder, IEnumerable assemblies, bool onlyConcreteClasses = true) - { - return finder.FindClassesOfType(typeof(T), assemblies, onlyConcreteClasses); - } - - } + public class NullTypeFinder : ITypeFinder + { + private static readonly ITypeFinder s_instance = new NullTypeFinder(); + + public static ITypeFinder Instance => s_instance; + + public IEnumerable GetAssemblies(bool ignoreInactivePlugins = false) + { + return Enumerable.Empty(); + } + + public IEnumerable FindClassesOfType(Type assignTypeFrom, IEnumerable assemblies, bool onlyConcreteClasses = true) + { + return Enumerable.Empty(); + } + } + + public static class ITypeFinderExtensions + { + public static IEnumerable FindClassesOfType(this ITypeFinder finder, bool onlyConcreteClasses = true, bool ignoreInactivePlugins = false) + { + return finder.FindClassesOfType(typeof(T), finder.GetAssemblies(ignoreInactivePlugins), onlyConcreteClasses); + } + + public static IEnumerable FindClassesOfType(this ITypeFinder finder, Type assignTypeFrom, bool onlyConcreteClasses = true, bool ignoreInactivePlugins = false) + { + return finder.FindClassesOfType(assignTypeFrom, finder.GetAssemblies(ignoreInactivePlugins), onlyConcreteClasses); + } + + public static IEnumerable FindClassesOfType(this ITypeFinder finder, IEnumerable assemblies, bool onlyConcreteClasses = true) + { + return finder.FindClassesOfType(typeof(T), assemblies, onlyConcreteClasses); + } + } } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/ObjectSignatureAttribute.cs b/src/Libraries/SmartStore.Core/Infrastructure/ObjectSignatureAttribute.cs deleted file mode 100644 index 42b53f80c4..0000000000 --- a/src/Libraries/SmartStore.Core/Infrastructure/ObjectSignatureAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace SmartStore -{ - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] - public sealed class ObjectSignatureAttribute : Attribute - { - } -} diff --git a/src/Libraries/SmartStore.Core/Infrastructure/RegularExpressions.cs b/src/Libraries/SmartStore.Core/Infrastructure/RegularExpressions.cs index cd098742ce..f28d4bbab9 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/RegularExpressions.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/RegularExpressions.cs @@ -1,12 +1,9 @@ -using System; -using System.Text; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; namespace SmartStore { public static class RegularExpressions { - internal static readonly string ValidRealPattern = "^([-]|[.]|[-.]|[0-9])[0-9]*[.]*[0-9]+$"; internal static readonly string ValidIntegerPattern = "^([-]|[0-9])[0-9]*$"; @@ -16,24 +13,23 @@ public static class RegularExpressions public static readonly Regex IsAlpha = new Regex("[^a-zA-Z]", RegexOptions.Compiled); public static readonly Regex IsAlphaNumeric = new Regex("[^a-zA-Z0-9]", RegexOptions.Compiled); public static readonly Regex IsNotNumber = new Regex("[^0-9.-]", RegexOptions.Compiled); - public static readonly Regex IsPositiveInteger = new Regex(@"\d{1,10}", RegexOptions.Compiled); + public static readonly Regex IsPositiveInteger = new Regex(@"^\d{1,10}", RegexOptions.Compiled); public static readonly Regex IsNumeric = new Regex("(" + ValidRealPattern + ")|(" + ValidIntegerPattern + ")", RegexOptions.Compiled); - // //public static readonly Regex IsWebUrl = new Regex(@"(http|https)://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?", RegexOptions.Singleline | RegexOptions.Compiled); - ///// see https://msdn.microsoft.com/en-us/library/ms998267.aspx - //public static readonly Regex IsWebUrl = new Regex(@"^(ht|f)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&%\$#_\=~]*)?$", RegexOptions.Singleline | RegexOptions.Compiled); + // //public static readonly Regex IsWebUrl = new Regex(@"(http|https)://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?", RegexOptions.Singleline | RegexOptions.Compiled); + ///// see https://msdn.microsoft.com/en-us/library/ms998267.aspx + //public static readonly Regex IsWebUrl = new Regex(@"^(ht|f)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&%\$#_\=~]*)?$", RegexOptions.Singleline | RegexOptions.Compiled); - public static readonly Regex IsEmail = new Regex("^(?:[\\w\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\`\\{\\|\\}\\~]+\\.)*[\\w\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\`\\{\\|\\}\\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\-](?!\\.)){0,61}[a-zA-Z0-9]?\\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\\[(?:(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\.){3}(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\]))$", RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); + public static readonly Regex IsEmail = new Regex("^(?:[\\w\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\`\\{\\|\\}\\~]+\\.)*[\\w\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\`\\{\\|\\}\\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\\-](?!\\.)){0,61}[a-zA-Z0-9]?\\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\\[(?:(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\.){3}(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\]))$", RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); public static readonly Regex IsGuid = new Regex(@"\{?[a-fA-F0-9]{8}(?:-(?:[a-fA-F0-9]){4}){3}-[a-fA-F0-9]{12}\}?", RegexOptions.Compiled); public static readonly Regex IsBase64Guid = new Regex(@"[a-zA-Z0-9+/=]{22,24}", RegexOptions.Compiled); public static readonly Regex IsCultureCode = new Regex(@"^[a-z]{2}(-[A-Z]{2})?$", RegexOptions.Singleline | RegexOptions.Compiled); - public static readonly Regex IsYearRange = new Regex(@"^(\d{4})-(\d{4})$", RegexOptions.Compiled); + public static readonly Regex IsYearRange = new Regex(@"^(\d{4})-(\d{4})$", RegexOptions.Compiled); - public static readonly Regex IsIban = new Regex(@"[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}([a-zA-Z0-9]?){0,16}", RegexOptions.Singleline | RegexOptions.Compiled); - public static readonly Regex IsBic = new Regex(@"([a-zA-Z]{4}[a-zA-Z]{2}[a-zA-Z0-9]{2}([a-zA-Z0-9]{3})?)", RegexOptions.Singleline | RegexOptions.Compiled); + public static readonly Regex IsIban = new Regex(@"[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}([a-zA-Z0-9]?){0,16}", RegexOptions.Singleline | RegexOptions.Compiled); + public static readonly Regex IsBic = new Regex(@"([a-zA-Z]{4}[a-zA-Z]{2}[a-zA-Z0-9]{2}([a-zA-Z0-9]{3})?)", RegexOptions.Singleline | RegexOptions.Compiled); } - } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/Singleton.cs b/src/Libraries/SmartStore.Core/Infrastructure/Singleton.cs index 66c50bd5fa..a28b7c56f8 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/Singleton.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/Singleton.cs @@ -14,10 +14,12 @@ public class Singleton : Singleton { static T instance; - /// The singleton instance for the specified type T. Only one instance (at the time) of this object for each type of T. + /// + /// The singleton instance for the specified type T. Only one instance (at the time) of this object for each type of T. + /// public static T Instance { - get { return instance; } + get => instance; set { instance = value; @@ -38,10 +40,7 @@ static SingletonList() } /// The singleton instance for the specified type T. Only one instance (at the time) of this list for each type of T. - public new static IList Instance - { - get { return Singleton>.Instance; } - } + public new static IList Instance => Singleton>.Instance; } /// @@ -57,10 +56,7 @@ static SingletonDictionary() } /// The singleton instance for the specified type T. Only one instance (at the time) of this dictionary for each type of T. - public new static IDictionary Instance - { - get { return Singleton>.Instance; } - } + public new static IDictionary Instance => Singleton>.Instance; } /// @@ -76,9 +72,6 @@ static Singleton() static readonly IDictionary allSingletons; /// Dictionary of type to singleton instances. - public static IDictionary AllSingletons - { - get { return allSingletons; } - } + public static IDictionary AllSingletons => allSingletons; } } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/SmartStoreEngine.cs b/src/Libraries/SmartStore.Core/Infrastructure/SmartStoreEngine.cs index 0029454b28..5fabdf7d55 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/SmartStoreEngine.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/SmartStoreEngine.cs @@ -17,147 +17,134 @@ public class SmartStoreEngine : IEngine { private ContainerManager _containerManager; - public SmartStoreEngine() - { - Logger = NullLogger.Instance; - } - - protected ILogger Logger - { - get; - private set; - } - - protected virtual void RunStartupTasks() + public SmartStoreEngine() { - var typeFinder = _containerManager.Resolve(); - var startUpTaskTypes = typeFinder.FindClassesOfType(ignoreInactivePlugins: true); - var startUpTasks = new List(); + Logger = NullLogger.Instance; + } + + protected ILogger Logger + { + get; + private set; + } + + protected virtual void RunStartupTasks() + { + var typeFinder = _containerManager.Resolve(); + var startUpTaskTypes = typeFinder.FindClassesOfType(ignoreInactivePlugins: true); + var startUpTasks = new List(); foreach (var startUpTaskType in startUpTaskTypes) { - startUpTasks.Add((IStartupTask)Activator.CreateInstance(startUpTaskType)); + startUpTasks.Add((IApplicationStart)Activator.CreateInstance(startUpTaskType)); + } + + // execute tasks async grouped by order + var groupedTasks = startUpTasks.OrderBy(st => st.Order).ToLookup(x => x.Order); + foreach (var tasks in groupedTasks) + { + Parallel.ForEach(tasks, task => + { + try + { + Logger.DebugFormat("Executing startup task '{0}'", task.GetType().FullName); + task.Start(); + } + catch (Exception ex) + { + Logger.ErrorFormat(ex, "Error while executing startup task '{0}'", task.GetType().FullName); + } + }); + } + } + + protected virtual ITypeFinder CreateTypeFinder() + { + return new WebAppTypeFinder(); + } + + protected virtual object CreateDependencyResolver(IContainer container) + { + var scopeProvider = container.Resolve(); + var dependencyResolver = new AutofacDependencyResolver(container, scopeProvider); + return dependencyResolver; + } + + protected virtual ContainerManager RegisterDependencies() + { + var typeFinder = CreateTypeFinder(); + + // core dependencies + var builder = new ContainerBuilder(); + builder.RegisterInstance(this).As(); + builder.RegisterInstance(typeFinder).As(); + + // Autofac + builder.Register(x => new DefaultLifetimeScopeAccessor(x.Resolve())).As().SingleInstance(); + builder.Register(x => new DefaultLifetimeScopeProvider(x.Resolve())).As().SingleInstance(); + builder.Register(x => new AutofacDependencyResolver(x.Resolve(), x.Resolve())).As().SingleInstance(); + + // Logging dependencies should be available very early + builder.RegisterModule(new LoggingModule()); + + // Register dependencies provided by other assemblies + var registrarTypes = typeFinder.FindClassesOfType(); + var registrarInstances = new List(); + foreach (var type in registrarTypes) + { + registrarInstances.Add((IDependencyRegistrar)Activator.CreateInstance(type)); + } + + // Sort + registrarInstances = registrarInstances.OrderBy(t => t.Order).ToList(); + foreach (var registrar in registrarInstances) + { + var type = registrar.GetType(); + Debug.WriteLine("Executing dependency registrar '{0}'.".FormatInvariant(type.FullName)); + registrar.Register(builder, typeFinder, PluginManager.IsActivePluginAssembly(type.Assembly)); } - // execute tasks async grouped by order - var groupedTasks = startUpTasks.OrderBy(st => st.Order).ToLookup(x => x.Order); - foreach (var tasks in groupedTasks) - { - Parallel.ForEach(tasks, task => - { - try - { - Logger.DebugFormat("Executing startup task '{0}'", task.GetType().FullName); - task.Execute(); - } - catch (Exception ex) - { - Logger.ErrorFormat(ex, "Error while executing startup task '{0}'", task.GetType().FullName); - } - }); - } + var container = builder.Build(); + _containerManager = new ContainerManager(container); + + // MVC dependency resolver + DependencyResolver.SetResolver(container.Resolve()); + + // Logger + this.Logger = container.Resolve().GetLogger("SmartStore.Bootstrapper"); + ((AppDomainTypeFinder)typeFinder).Logger = this.Logger; + + return _containerManager; } - protected virtual ITypeFinder CreateTypeFinder() - { - return new WebAppTypeFinder(); - } - - protected virtual object CreateDependencyResolver(IContainer container) - { - var scopeProvider = container.Resolve(); - var dependencyResolver = new AutofacDependencyResolver(container, scopeProvider); - return dependencyResolver; - } - - protected virtual ContainerManager RegisterDependencies() - { - var builder = new ContainerBuilder(); - var container = builder.Build(); - var typeFinder = CreateTypeFinder(); - - _containerManager = new ContainerManager(container); - - // core dependencies - builder = new ContainerBuilder(); - builder.RegisterInstance(this).As(); - builder.RegisterInstance(typeFinder).As(); - - // Autofac - var lifetimeScopeAccessor = new DefaultLifetimeScopeAccessor(container); - var lifetimeScopeProvider = new DefaultLifetimeScopeProvider(lifetimeScopeAccessor); - builder.RegisterInstance(lifetimeScopeAccessor).As(); - builder.RegisterInstance(lifetimeScopeProvider).As(); - - var dependencyResolver = new AutofacDependencyResolver(container, lifetimeScopeProvider); - builder.RegisterInstance(dependencyResolver); - DependencyResolver.SetResolver(dependencyResolver); - - // Logging dependencies should be available very early - builder.RegisterModule(new LoggingModule()); - -#pragma warning disable 612, 618 - builder.Update(container); -#pragma warning restore 612, 618 - - // Propagate logger - var logger = container.Resolve().GetLogger("SmartStore.Bootstrapper"); - this.Logger = logger; - ((AppDomainTypeFinder)typeFinder).Logger = logger; - - // Register dependencies provided by other assemblies - builder = new ContainerBuilder(); - var registrarTypes = typeFinder.FindClassesOfType(); - var registrarInstances = new List(); - foreach (var type in registrarTypes) - { - registrarInstances.Add((IDependencyRegistrar)Activator.CreateInstance(type)); - } - - // Sort - registrarInstances = registrarInstances.OrderBy(t => t.Order).ToList(); - foreach (var registrar in registrarInstances) - { - var type = registrar.GetType(); - logger.DebugFormat("Executing dependency registrar '{0}'", type.FullName); - registrar.Register(builder, typeFinder, PluginManager.IsActivePluginAssembly(type.Assembly)); - } - -#pragma warning disable 612, 618 - builder.Update(container); -#pragma warning restore 612, 618 - - return _containerManager; - } - /// /// Initialize components and plugins /// public void Initialize() { - RegisterDependencies(); + RegisterDependencies(); - if (DataSettings.DatabaseIsInstalled()) - { - RunStartupTasks(); - } + if (DataSettings.DatabaseIsInstalled()) + { + RunStartupTasks(); + } } - public bool IsFullyInitialized - { - get; - set; - } + public bool IsFullyInitialized + { + get; + set; + } [DebuggerStepThrough] - public T Resolve(string name = null) where T : class - { + public T Resolve(string name = null) where T : class + { if (name.HasValue()) { return ContainerManager.ResolveNamed(name); } return ContainerManager.Resolve(); - } + } [DebuggerStepThrough] public object Resolve(Type type, string name = null) @@ -175,14 +162,8 @@ public T[] ResolveAll() return ContainerManager.ResolveAll(); } - public IContainer Container - { - get { return _containerManager.Container; } - } + public IContainer Container => _containerManager.Container; - public ContainerManager ContainerManager - { - get { return _containerManager; } - } - } + public ContainerManager ContainerManager => _containerManager; + } } diff --git a/src/Libraries/SmartStore.Core/Infrastructure/WebAppTypeFinder.cs b/src/Libraries/SmartStore.Core/Infrastructure/WebAppTypeFinder.cs index a437eebd7d..4b6981b2ef 100644 --- a/src/Libraries/SmartStore.Core/Infrastructure/WebAppTypeFinder.cs +++ b/src/Libraries/SmartStore.Core/Infrastructure/WebAppTypeFinder.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; -using System.IO; using System.Linq; using System.Reflection; using System.Web; @@ -18,27 +16,21 @@ namespace SmartStore.Core.Infrastructure /// public class WebAppTypeFinder : AppDomainTypeFinder { - private bool _ensureBinFolderAssembliesLoaded; + private readonly Lazy> _allAssembliesResolver; + private readonly Lazy> _activeAssembliesResolver; - private readonly Lazy> _allAssembliesResolver; - private readonly Lazy> _activeAssembliesResolver; - - public WebAppTypeFinder() + public WebAppTypeFinder() { - _ensureBinFolderAssembliesLoaded = CommonHelper.GetAppSetting("sm:EnableDynamicDiscovery", true); - _allAssembliesResolver = new Lazy>(() => this.GetAssembliesInternal(false)); - _activeAssembliesResolver = new Lazy>(() => this.GetAssembliesInternal(true)); - } + EnsureBinFolderAssembliesLoaded = CommonHelper.GetAppSetting("sm:EnableDynamicDiscovery", true); + _allAssembliesResolver = new Lazy>(() => this.GetAssembliesInternal(false)); + _activeAssembliesResolver = new Lazy>(() => this.GetAssembliesInternal(true)); + } /// /// Gets or sets wether assemblies in the bin folder of the web application should be explicitly checked for being loaded on application load. /// This is needed in situations where plugins need to be loaded into the AppDomain after the application has been reloaded. /// - public bool EnsureBinFolderAssembliesLoaded - { - get { return _ensureBinFolderAssembliesLoaded; } - set { _ensureBinFolderAssembliesLoaded = value; } - } + public bool EnsureBinFolderAssembliesLoaded { get; set; } /// /// Gets a physical disk path of \Bin directory @@ -58,102 +50,105 @@ public virtual string GetBinDirectory() } } - public override IEnumerable GetAssemblies(bool ignoreInactivePlugins = false) - { - if (ignoreInactivePlugins) - { - return _activeAssembliesResolver.Value; - } - else - { - return _allAssembliesResolver.Value; - } - } - - private IEnumerable GetAssembliesInternal(bool ignoreInactivePlugins) - { - if (ignoreInactivePlugins) - { - var allAssemblies = _allAssembliesResolver.Value; - return allAssemblies.Where(x => PluginManager.IsActivePluginAssembly(x)).AsReadOnly(); - } - - if (this.EnsureBinFolderAssembliesLoaded) - { - LoadPluginAssemblies(); - } - - return base.GetAssemblies(false); - } - - /// - /// Makes sure matching plugin assemblies are loaded into the app domain. - /// - protected virtual void LoadPluginAssemblies() - { - var alreadyLoadedAssemblyNames = new HashSet( - AppDomain.CurrentDomain.GetAssemblies().Select(x => x.FullName), - StringComparer.OrdinalIgnoreCase); - - var pluginAssemblies = new HashSet(PluginManager.ReferencedPlugins.Select(x => x.Assembly.Assembly)); - - foreach (var assembly in pluginAssemblies) - { - try - { - if (!alreadyLoadedAssemblyNames.Contains(assembly.FullName)) - { - App.Load(assembly.FullName); - Logger.DebugFormat("Loaded plugin assembly '{0}'", assembly.FullName); - } - } - catch (Exception ex) - { - Logger.Error(ex); - } - } - } - - #region Obsolete - - ///// - ///// Makes sure matching assemblies in the supplied folder are loaded into the app domain. - ///// - ///// The physical path to a directory containing dlls to load into the app domain. - //protected virtual void LoadCandidateAssembliesFromFolder(string path) - //{ - // var alreadyLoadedAssemblyNames = new HashSet(); - // var appDomainAssemblies = base.GetAssemblies(); - - // foreach (var a in appDomainAssemblies) - // { - // alreadyLoadedAssemblyNames.Add(a.FullName); - // } - - // if (!Directory.Exists(path)) - // { - // return; - // } - - // var dlls = Directory.GetFiles(path, "*.dll"); - - // foreach (string dllPath in dlls) - // { - // try - // { - // var an = AssemblyName.GetAssemblyName(dllPath); - // if (!alreadyLoadedAssemblyNames.Contains(an.FullName) && Matches(an.FullName)) - // { - // App.Load(an); - // } - // } - // catch (BadImageFormatException ex) - // { - // Trace.TraceError(ex.ToString()); - // } - // } - //} - - #endregion - } + public override IEnumerable GetAssemblies(bool ignoreInactivePlugins = false) + { + if (ignoreInactivePlugins) + { + return _activeAssembliesResolver.Value; + } + else + { + return _allAssembliesResolver.Value; + } + } + + private IEnumerable GetAssembliesInternal(bool ignoreInactivePlugins) + { + if (ignoreInactivePlugins) + { + var allAssemblies = _allAssembliesResolver.Value; + return allAssemblies.Where(x => PluginManager.IsActivePluginAssembly(x)).AsReadOnly(); + } + + if (this.EnsureBinFolderAssembliesLoaded) + { + LoadPluginAssemblies(); + } + + return base.GetAssemblies(false); + } + + /// + /// Makes sure matching plugin assemblies are loaded into the app domain. + /// + protected virtual void LoadPluginAssemblies() + { + var alreadyLoadedAssemblyNames = new HashSet( + AppDomain.CurrentDomain.GetAssemblies().Select(x => x.FullName), + StringComparer.OrdinalIgnoreCase); + + var pluginAssemblies = new HashSet(PluginManager.ReferencedPlugins.Select(x => x.Assembly.Assembly)); + + foreach (var assembly in pluginAssemblies) + { + try + { + if (assembly == null) + continue; + + if (!alreadyLoadedAssemblyNames.Contains(assembly.FullName)) + { + App.Load(assembly.FullName); + Logger.DebugFormat("Loaded plugin assembly '{0}'", assembly.FullName); + } + } + catch (Exception ex) + { + Logger.Error(ex); + } + } + } + + #region Obsolete + + ///// + ///// Makes sure matching assemblies in the supplied folder are loaded into the app domain. + ///// + ///// The physical path to a directory containing dlls to load into the app domain. + //protected virtual void LoadCandidateAssembliesFromFolder(string path) + //{ + // var alreadyLoadedAssemblyNames = new HashSet(); + // var appDomainAssemblies = base.GetAssemblies(); + + // foreach (var a in appDomainAssemblies) + // { + // alreadyLoadedAssemblyNames.Add(a.FullName); + // } + + // if (!Directory.Exists(path)) + // { + // return; + // } + + // var dlls = Directory.GetFiles(path, "*.dll"); + + // foreach (string dllPath in dlls) + // { + // try + // { + // var an = AssemblyName.GetAssemblyName(dllPath); + // if (!alreadyLoadedAssemblyNames.Contains(an.FullName) && Matches(an.FullName)) + // { + // App.Load(an); + // } + // } + // catch (BadImageFormatException ex) + // { + // Trace.TraceError(ex.ToString()); + // } + // } + //} + + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Linq/Expanders/IPathExpander.cs b/src/Libraries/SmartStore.Core/Linq/Expanders/IPathExpander.cs index aaf66dc839..6c29db695c 100644 --- a/src/Libraries/SmartStore.Core/Linq/Expanders/IPathExpander.cs +++ b/src/Libraries/SmartStore.Core/Linq/Expanders/IPathExpander.cs @@ -1,10 +1,9 @@ using System; -using System.Linq; using System.Linq.Expressions; namespace SmartStore.Linq { - + /// /// Instructs the unit of work to eager load entities. /// diff --git a/src/Libraries/SmartStore.Core/Linq/Expanders/LambdaPathExpander.cs b/src/Libraries/SmartStore.Core/Linq/Expanders/LambdaPathExpander.cs index ce8c87c8b1..85df064f60 100644 --- a/src/Libraries/SmartStore.Core/Linq/Expanders/LambdaPathExpander.cs +++ b/src/Libraries/SmartStore.Core/Linq/Expanders/LambdaPathExpander.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using System.Reflection; using SmartStore.Utilities; @@ -16,13 +15,7 @@ public LambdaPathExpander() _expands = new List(); } - public IList Expands - { - get - { - return _expands; - } - } + public IList Expands => _expands; public virtual void Expand(Expression> path) { @@ -61,7 +54,7 @@ public virtual void Expand(Type type, string path) if (prop == null) throw new ArgumentException("The property or member '{0}' does not exist in type '{1}'.".FormatInvariant(member, t.FullName)); - Type memberType = prop.PropertyType; + Type memberType = prop.PropertyType; DoExpand(t, member); @@ -104,7 +97,7 @@ private void DoExpand(Type type, string path) var entityParam = Expression.Parameter(type, "x"); // {x} path = String.Concat("x.", path.Trim('.')); - var expression = System.Linq.Dynamic.DynamicExpression.ParseLambda( + var expression = System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda( new ParameterExpression[] { entityParam }, typeof(object), path.Trim('.')); diff --git a/src/Libraries/SmartStore.Core/Linq/Expanders/StringPathExpander.cs b/src/Libraries/SmartStore.Core/Linq/Expanders/StringPathExpander.cs index ca3f29bc24..77c816ad54 100644 --- a/src/Libraries/SmartStore.Core/Linq/Expanders/StringPathExpander.cs +++ b/src/Libraries/SmartStore.Core/Linq/Expanders/StringPathExpander.cs @@ -7,7 +7,7 @@ namespace SmartStore.Linq { - + public class StringPathExpander : IPathExpander { private readonly IList _expands; @@ -17,13 +17,7 @@ public StringPathExpander() _expands = new List(); } - public IList Expands - { - get - { - return _expands; - } - } + public IList Expands => _expands; public virtual void Expand(Expression> path) { diff --git a/src/Libraries/SmartStore.Core/Linq/ExpressionStarter.cs b/src/Libraries/SmartStore.Core/Linq/ExpressionStarter.cs new file mode 100644 index 0000000000..da2f6bc694 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Linq/ExpressionStarter.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq.Expressions; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; + +namespace SmartStore.Linq +{ + /// + /// ExpressionStarter{T} which eliminates the default 1=0 or 1=1 stub expressions + /// + /// The type + public class ExpressionStarter + { + private Expression> _predicate; + + internal ExpressionStarter() + : this(false) + { + } + + internal ExpressionStarter(bool defaultExpression) + { + if (defaultExpression) + DefaultExpression = f => true; + else + DefaultExpression = f => false; + } + + internal ExpressionStarter(Expression> exp) : this(false) + { + _predicate = exp; + } + + /// The actual Predicate. It can only be set by calling Start. + private Expression> Predicate => (IsStarted || !UseDefaultExpression) ? _predicate : DefaultExpression; + + /// Determines if the predicate is started. + public bool IsStarted => _predicate != null; + + /// A default expression to use only when the expression is null + public bool UseDefaultExpression => DefaultExpression != null; + + /// The default expression + public Expression> DefaultExpression { get; set; } + + /// Set the Expression predicate + /// The first expression + public Expression> Start(Expression> exp) + { + if (IsStarted) + throw new Exception("Predicate cannot be started again."); + + return _predicate = exp; + } + + public Expression> Or(Expression> expr2) + { + Guard.NotNull(expr2, nameof(expr2)); + return (IsStarted) ? _predicate = Predicate.Or(expr2) : Start(expr2); + } + + /// And + public Expression> And(Expression> expr2) + { + Guard.NotNull(expr2, nameof(expr2)); + return (IsStarted) ? _predicate = Predicate.And(expr2) : Start(expr2); + } + + /// Show predicate string + public override string ToString() + { + return Predicate?.ToString(); + } + + #region Implicit Operators + + /// + /// Allows this object to be implicitely converted to an Expression{Func{T, bool}}. + /// + /// + public static implicit operator Expression>(ExpressionStarter right) + { + return right?.Predicate; + } + + /// + /// Allows this object to be implicitely converted to an Expression{Func{T, bool}}. + /// + /// + public static implicit operator Func(ExpressionStarter right) + { + return right == null ? null : (right.IsStarted || right.UseDefaultExpression) ? right.Predicate.Compile() : null; + } + + /// + /// Allows this object to be implicitely converted to an Expression{Func{T, bool}}. + /// + /// + public static implicit operator ExpressionStarter(Expression> right) + { + return right == null ? null : new ExpressionStarter(right); + } + + #endregion + + #region Implement Expression methods and properties + + public Func Compile() + { + return Predicate.Compile(); + } + + public Func Compile(DebugInfoGenerator debugInfoGenerator) + { + return Predicate.Compile(debugInfoGenerator); + } + + public Expression> Update(Expression body, IEnumerable parameters) + { + return Predicate.Update(body, parameters); + } + + #endregion + + #region Implement LamdaExpression methods and properties + + public Expression Body => Predicate.Body; + + public ExpressionType NodeType => Predicate.NodeType; + + public ReadOnlyCollection Parameters => Predicate.Parameters; + + public Type Type => Predicate.Type; + + public string Name => Predicate.Name; + + public Type ReturnType => Predicate.ReturnType; + + public bool TailCall => Predicate.TailCall; + + public void CompileToMethod(MethodBuilder method) + { + Predicate.CompileToMethod(method); + } + + public void CompileToMethod(MethodBuilder method, DebugInfoGenerator debugInfoGenerator) + { + Predicate.CompileToMethod(method, debugInfoGenerator); + } + + #endregion + + #region Implement Expression methods and properties + + public virtual bool CanReduce => Predicate.CanReduce; + + #endregion + } +} diff --git a/src/Libraries/SmartStore.Core/Linq/Expressions/MemberAccessPathVisitor.cs b/src/Libraries/SmartStore.Core/Linq/Expressions/MemberAccessPathVisitor.cs index e2369449a4..2378763d0d 100644 --- a/src/Libraries/SmartStore.Core/Linq/Expressions/MemberAccessPathVisitor.cs +++ b/src/Libraries/SmartStore.Core/Linq/Expressions/MemberAccessPathVisitor.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; diff --git a/src/Libraries/SmartStore.Core/Linq/LinqContainsPredicateBuilder.cs b/src/Libraries/SmartStore.Core/Linq/LinqContainsPredicateBuilder.cs index 909318e2c3..dc6879aac8 100644 --- a/src/Libraries/SmartStore.Core/Linq/LinqContainsPredicateBuilder.cs +++ b/src/Libraries/SmartStore.Core/Linq/LinqContainsPredicateBuilder.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; namespace SmartStore.Linq diff --git a/src/Libraries/SmartStore.Core/Linq/PredicateBuilder.cs b/src/Libraries/SmartStore.Core/Linq/PredicateBuilder.cs index 06101c7c6d..3041ea8726 100644 --- a/src/Libraries/SmartStore.Core/Linq/PredicateBuilder.cs +++ b/src/Libraries/SmartStore.Core/Linq/PredicateBuilder.cs @@ -1,35 +1,112 @@ using System; -using System.Linq; using System.Linq.Expressions; namespace SmartStore.Linq { + /// The Predicate Operator + public enum PredicateOperator + { + /// The "Or" + Or, + /// The "And" + And + } + public static class PredicateBuilder { + private class RebindParameterVisitor : ExpressionVisitor + { + private readonly ParameterExpression _oldParameter; + private readonly ParameterExpression _newParameter; + + public RebindParameterVisitor(ParameterExpression oldParameter, ParameterExpression newParameter) + { + _oldParameter = oldParameter; + _newParameter = newParameter; + } + + protected override Expression VisitParameter(ParameterExpression node) + { + if (node == _oldParameter) + { + return _newParameter; + } + return base.VisitParameter(node); + } + } + + /// Start an expression + public static ExpressionStarter New(Expression> expr = null) + { + return new ExpressionStarter(expr); + } + + /// Create an expression with a stub expression true or false to use when the expression is not yet started. + public static ExpressionStarter New(bool defaultExpression) + { + return new ExpressionStarter(defaultExpression); + } + + [Obsolete("Use PredicateBuilder.New() instead.")] public static Expression> True() { return f => true; } + [Obsolete("Use PredicateBuilder.New() instead.")] public static Expression> False() { return f => false; } - public static Expression> Or(this Expression> expr1, - Expression> expr2) + public static Expression> And(this Expression> expr1, Expression> expr2) + { + Guard.NotNull(expr1, nameof(expr1)); + Guard.NotNull(expr2, nameof(expr2)); + + var expr2Body = new RebindParameterVisitor(expr2.Parameters[0], expr1.Parameters[0]).Visit(expr2.Body); + return Expression.Lambda>(Expression.AndAlso(expr1.Body, expr2Body), expr1.Parameters); + } + + public static Expression> Or(this Expression> expr1, Expression> expr2) { - var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast()); - return Expression.Lambda> - (Expression.Or(expr1.Body, invokedExpr), expr1.Parameters); + Guard.NotNull(expr1, nameof(expr1)); + Guard.NotNull(expr2, nameof(expr2)); + + var expr2Body = new RebindParameterVisitor(expr2.Parameters[0], expr1.Parameters[0]).Visit(expr2.Body); + return Expression.Lambda>(Expression.OrElse(expr1.Body, expr2Body), expr1.Parameters); + } + + /// + /// Extends the specified source Predicate with another Predicate and the specified PredicateOperator. + /// + /// The type + /// The source Predicate. + /// The second Predicate. + /// The Operator (can be "And" or "Or"). + /// Expression{Func{T, bool}} + public static Expression> Extend(this Expression> first, Expression> second, PredicateOperator op = PredicateOperator.Or) + { + Guard.NotNull(first, nameof(first)); + Guard.NotNull(second, nameof(second)); + + return op == PredicateOperator.Or ? first.Or(second) : first.And(second); } - public static Expression> And(this Expression> expr1, - Expression> expr2) + /// + /// Extends the specified source Predicate with another Predicate and the specified PredicateOperator. + /// + /// The type + /// The source Predicate. + /// The second Predicate. + /// The Operator (can be "And" or "Or"). + /// Expression{Func{T, bool}} + public static Expression> Extend(this ExpressionStarter first, Expression> second, PredicateOperator op = PredicateOperator.Or) { - var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast()); - return Expression.Lambda> - (Expression.And(expr1.Body, invokedExpr), expr1.Parameters); + Guard.NotNull(first, nameof(first)); + Guard.NotNull(second, nameof(second)); + + return op == PredicateOperator.Or ? first.Or(second) : first.And(second); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Localization/ILocalizationFileResolver.cs b/src/Libraries/SmartStore.Core/Localization/ILocalizationFileResolver.cs index e734c92653..905030ede6 100644 --- a/src/Libraries/SmartStore.Core/Localization/ILocalizationFileResolver.cs +++ b/src/Libraries/SmartStore.Core/Localization/ILocalizationFileResolver.cs @@ -1,44 +1,42 @@ -using System; - -namespace SmartStore.Core.Localization +namespace SmartStore.Core.Localization { - /// - /// Responsible for finding a localization file for client scripts - /// - public interface ILocalizationFileResolver - { - /// - /// Tries to find a matching localization file for a given culture in the following order - /// (assuming is 'de-DE', is 'lang-*.js' and is 'en-US'): - /// - /// Exact match > lang-de-DE.js - /// Neutral culture > lang-de.js - /// Any region for language > lang-de-CH.js - /// Exact match for fallback culture > lang-en-US.js - /// Neutral fallback culture > lang-en.js - /// Any region for fallback language > lang-en-GB.js - /// - /// - /// The ISO culture code to get a localization file for, e.g. 'de-DE' - /// The virtual path to search in - /// The pattern to match, e.g. 'lang-*.js'. The wildcard char MUST exist. - /// - /// Whether caching should be enabled. If false, no attempt is made to read from cache, nor writing the result to the cache. - /// Cache duration is 24 hours. Automatic eviction on file change is NOT performed. - /// - /// Optional. - /// Result - LocalizationFileResolveResult Resolve( - string culture, - string virtualPath, - string pattern, - bool cache = true, - string fallbackCulture = "en"); - } + /// + /// Responsible for finding a localization file for client scripts + /// + public interface ILocalizationFileResolver + { + /// + /// Tries to find a matching localization file for a given culture in the following order + /// (assuming is 'de-DE', is 'lang-*.js' and is 'en-US'): + /// + /// Exact match > lang-de-DE.js + /// Neutral culture > lang-de.js + /// Any region for language > lang-de-CH.js + /// Exact match for fallback culture > lang-en-US.js + /// Neutral fallback culture > lang-en.js + /// Any region for fallback language > lang-en-GB.js + /// + /// + /// The ISO culture code to get a localization file for, e.g. 'de-DE' + /// The virtual path to search in + /// The pattern to match, e.g. 'lang-*.js'. The wildcard char MUST exist. + /// + /// Whether caching should be enabled. If false, no attempt is made to read from cache, nor writing the result to the cache. + /// Cache duration is 24 hours. Automatic eviction on file change is NOT performed. + /// + /// Optional. + /// Result + LocalizationFileResolveResult Resolve( + string culture, + string virtualPath, + string pattern, + bool cache = true, + string fallbackCulture = "en"); + } - public class LocalizationFileResolveResult - { - public string Culture { get; set; } - public string VirtualPath { get; set; } - } + public class LocalizationFileResolveResult + { + public string Culture { get; set; } + public string VirtualPath { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Localization/LocalizationFileResolver.cs b/src/Libraries/SmartStore.Core/Localization/LocalizationFileResolver.cs index fc02435c1e..7a97e459b3 100644 --- a/src/Libraries/SmartStore.Core/Localization/LocalizationFileResolver.cs +++ b/src/Libraries/SmartStore.Core/Localization/LocalizationFileResolver.cs @@ -1,152 +1,148 @@ using System; -using System.Collections.Generic; using System.Globalization; using System.IO; -using System.Linq; -using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; using System.Web; using SmartStore.Core.Caching; using SmartStore.Utilities; namespace SmartStore.Core.Localization { - public class LocalizationFileResolver : ILocalizationFileResolver - { - private readonly ICacheManager _cache; - - public LocalizationFileResolver(ICacheManager cache) - { - _cache = cache; - } - - public LocalizationFileResolveResult Resolve( - string culture, - string virtualPath, - string pattern, - bool cache = true, - string fallbackCulture = "en") - { - Guard.NotEmpty(culture, nameof(culture)); - Guard.NotEmpty(virtualPath, nameof(virtualPath)); - Guard.NotEmpty(pattern, nameof(pattern)); - - if (pattern.IndexOf('*') < 0) - { - throw new ArgumentException("The pattern must contain a wildcard char for substitution, e.g. 'lang-*.js'.", nameof(pattern)); - } - - virtualPath = FixPath(virtualPath); - var cacheKey = "core:locfile:" + virtualPath.ToLower() + pattern + "/" + culture; - string result = null; - - if (cache && _cache.Contains(cacheKey)) - { - result = _cache.Get(cacheKey); - return result != null ? CreateResult(result, virtualPath, pattern) : null; - } - - if (!LocalizationHelper.IsValidCultureCode(culture)) - { - throw new ArgumentException($"'{culture}' is not a valid culture code.", nameof(culture)); - } - - var ci = CultureInfo.GetCultureInfo(culture); - var directory = new DirectoryInfo(CommonHelper.MapPath(virtualPath, false)); - - if (!directory.Exists) - { - throw new DirectoryNotFoundException($"Path '{virtualPath}' does not exist."); - } - - // 1: Match passed culture - result = ResolveMatchingFile(ci, directory, pattern); - - if (result == null && fallbackCulture.HasValue() && culture != fallbackCulture) - { - if (!LocalizationHelper.IsValidCultureCode(fallbackCulture)) - { - throw new ArgumentException($"'{culture}' is not a valid culture code.", nameof(fallbackCulture)); - } - - // 2: Match fallback culture - ci = CultureInfo.GetCultureInfo(fallbackCulture); - result = ResolveMatchingFile(ci, directory, pattern); - } - - if (cache) - { - _cache.Put(cacheKey, result, TimeSpan.FromHours(24)); - } - - if (result.HasValue()) - { - return CreateResult(result, virtualPath, pattern); - } - - return null; - } - - private string ResolveMatchingFile(CultureInfo ci, DirectoryInfo directory, string pattern) - { - string result = null; - - // 1: Exact match - // ----------------------------------------------------- - var fileName = pattern.Replace("*", ci.Name); - if (File.Exists(Path.Combine(directory.FullName, fileName))) - { - result = ci.Name; - } - - // 2: Match neutral culture, e.g. de-DE > de - // ----------------------------------------------------- - if (result == null && !ci.IsNeutralCulture && ci.Parent != null) - { - ci = ci.Parent; - fileName = pattern.Replace("*", ci.Name); - if (File.Exists(Path.Combine(directory.FullName, fileName))) - { - result = ci.Name; - } - } - - // 2: Match any region, e.g. de-DE > de-CH - // ----------------------------------------------------- - if (result == null && ci.IsNeutralCulture) - { - // Convert pattern to Regex: "lang-*.js" > "^lang.(.+?).js$" - var rgPattern = "^" + pattern.Replace("*", @"(.+?)") + "$"; - var rgFileName = new Regex(rgPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); - - foreach (var fi in directory.EnumerateFiles(pattern.Replace("*", ci.Name + "-*"), SearchOption.TopDirectoryOnly)) - { - var culture = rgFileName.Match(fi.Name).Groups[1].Value; - if (LocalizationHelper.IsValidCultureCode(culture)) - { - result = culture; - break; - } - } - } - - return result; - } - - private string FixPath(string virtualPath) - { - return VirtualPathUtility.ToAppRelative(virtualPath).EnsureEndsWith("/"); - } - - private LocalizationFileResolveResult CreateResult(string culture, string virtualPath, string pattern) - { - var fileName = pattern.Replace("*", culture); - return new LocalizationFileResolveResult - { - Culture = culture, - VirtualPath = VirtualPathUtility.ToAbsolute(virtualPath + fileName) - }; - } - } + public class LocalizationFileResolver : ILocalizationFileResolver + { + private readonly ICacheManager _cache; + + public LocalizationFileResolver(ICacheManager cache) + { + _cache = cache; + } + + public LocalizationFileResolveResult Resolve( + string culture, + string virtualPath, + string pattern, + bool cache = true, + string fallbackCulture = "en") + { + Guard.NotEmpty(culture, nameof(culture)); + Guard.NotEmpty(virtualPath, nameof(virtualPath)); + Guard.NotEmpty(pattern, nameof(pattern)); + + if (pattern.IndexOf('*') < 0) + { + throw new ArgumentException("The pattern must contain a wildcard char for substitution, e.g. 'lang-*.js'.", nameof(pattern)); + } + + virtualPath = FixPath(virtualPath); + var cacheKey = "core:locfile:" + virtualPath.ToLower() + pattern + "/" + culture; + string result = null; + + if (cache && _cache.Contains(cacheKey)) + { + result = _cache.Get(cacheKey); + return result != null ? CreateResult(result, virtualPath, pattern) : null; + } + + if (!LocalizationHelper.IsValidCultureCode(culture)) + { + throw new ArgumentException($"'{culture}' is not a valid culture code.", nameof(culture)); + } + + var ci = CultureInfo.GetCultureInfo(culture); + var directory = new DirectoryInfo(CommonHelper.MapPath(virtualPath, false)); + + if (!directory.Exists) + { + throw new DirectoryNotFoundException($"Path '{virtualPath}' does not exist."); + } + + // 1: Match passed culture + result = ResolveMatchingFile(ci, directory, pattern); + + if (result == null && fallbackCulture.HasValue() && culture != fallbackCulture) + { + if (!LocalizationHelper.IsValidCultureCode(fallbackCulture)) + { + throw new ArgumentException($"'{culture}' is not a valid culture code.", nameof(fallbackCulture)); + } + + // 2: Match fallback culture + ci = CultureInfo.GetCultureInfo(fallbackCulture); + result = ResolveMatchingFile(ci, directory, pattern); + } + + if (cache) + { + _cache.Put(cacheKey, result, TimeSpan.FromHours(24)); + } + + if (result.HasValue()) + { + return CreateResult(result, virtualPath, pattern); + } + + return null; + } + + private string ResolveMatchingFile(CultureInfo ci, DirectoryInfo directory, string pattern) + { + string result = null; + + // 1: Exact match + // ----------------------------------------------------- + var fileName = pattern.Replace("*", ci.Name); + if (File.Exists(Path.Combine(directory.FullName, fileName))) + { + result = ci.Name; + } + + // 2: Match neutral culture, e.g. de-DE > de + // ----------------------------------------------------- + if (result == null && !ci.IsNeutralCulture && ci.Parent != null) + { + ci = ci.Parent; + fileName = pattern.Replace("*", ci.Name); + if (File.Exists(Path.Combine(directory.FullName, fileName))) + { + result = ci.Name; + } + } + + // 2: Match any region, e.g. de-DE > de-CH + // ----------------------------------------------------- + if (result == null && ci.IsNeutralCulture) + { + // Convert pattern to Regex: "lang-*.js" > "^lang.(.+?).js$" + var rgPattern = "^" + pattern.Replace("*", @"(.+?)") + "$"; + var rgFileName = new Regex(rgPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); + + foreach (var fi in directory.EnumerateFiles(pattern.Replace("*", ci.Name + "-*"), SearchOption.TopDirectoryOnly)) + { + var culture = rgFileName.Match(fi.Name).Groups[1].Value; + if (LocalizationHelper.IsValidCultureCode(culture)) + { + result = culture; + break; + } + } + } + + return result; + } + + private string FixPath(string virtualPath) + { + return VirtualPathUtility.ToAppRelative(virtualPath).EnsureEndsWith("/"); + } + + private LocalizationFileResolveResult CreateResult(string culture, string virtualPath, string pattern) + { + var fileName = pattern.Replace("*", culture); + return new LocalizationFileResolveResult + { + Culture = culture, + VirtualPath = VirtualPathUtility.ToAbsolute(virtualPath + fileName) + }; + } + } } diff --git a/src/Libraries/SmartStore.Core/Localization/LocalizationHelper.cs b/src/Libraries/SmartStore.Core/Localization/LocalizationHelper.cs index c4bbff8a75..1449395774 100644 --- a/src/Libraries/SmartStore.Core/Localization/LocalizationHelper.cs +++ b/src/Libraries/SmartStore.Core/Localization/LocalizationHelper.cs @@ -2,10 +2,11 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Runtime.CompilerServices; namespace SmartStore.Core.Localization { - public static class LocalizationHelper + public static class LocalizationHelper { private readonly static HashSet _cultureCodes = new HashSet( @@ -13,75 +14,161 @@ public static class LocalizationHelper .Select(x => x.IetfLanguageTag) .Where(x => !string.IsNullOrWhiteSpace(x)), StringComparer.OrdinalIgnoreCase); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsValidCultureCode(string locale) { return locale.HasValue() && _cultureCodes.Contains(locale); } - /// - /// Enumerates all parent cultures, excluding the top-most invariant culture - /// - /// The ISO culture code, e.g. de-DE, en-US or just en - /// Parent cultures - public static IEnumerable EnumerateParentCultures(string locale) - { - if (locale.IsEmpty() || !_cultureCodes.Contains(locale)) - { - return Enumerable.Empty(); - } - - return EnumerateParentCultures(CultureInfo.GetCultureInfo(locale)); - } - - /// - /// Enumerates all parent cultures, excluding the top-most invariant culture - /// - /// The culture info to enumerate parents for - /// Parent cultures - public static IEnumerable EnumerateParentCultures(CultureInfo culture) - { - if (culture == null) - { - yield break; - } - - while (culture.Parent.TwoLetterISOLanguageName != "iv") - { - yield return culture.Parent; - culture = culture.Parent; - } - } - - public static string GetLanguageNativeName(string locale) - { - try - { - if (locale.HasValue()) - { - var info = CultureInfo.GetCultureInfoByIetfLanguageTag(locale); - if (info != null) - return info.NativeName; - } - } - catch { } - - return null; - } - - public static string GetCurrencySymbol(string locale) - { - try - { - if (locale.HasValue()) - { - var info = new RegionInfo(locale); - if (info != null) - return info.CurrencySymbol; - } - } - catch { } - - return null; - } - } + /// + /// Enumerates all parent cultures, excluding the top-most invariant culture + /// + /// The ISO culture code, e.g. de-DE, en-US or just en + /// Parent cultures + public static IEnumerable EnumerateParentCultures(string locale) + { + if (locale.IsEmpty() || !_cultureCodes.Contains(locale)) + { + return Enumerable.Empty(); + } + + return EnumerateParentCultures(CultureInfo.GetCultureInfo(locale)); + } + + /// + /// Enumerates all parent cultures, excluding the top-most invariant culture + /// + /// The culture info to enumerate parents for + /// Parent cultures + public static IEnumerable EnumerateParentCultures(CultureInfo culture) + { + if (culture == null) + { + yield break; + } + + while (culture.Parent.TwoLetterISOLanguageName != "iv") + { + yield return culture.Parent; + culture = culture.Parent; + } + } + + public static bool TryGetCultureInfoForLocale(string locale, out CultureInfo culture) + { + culture = null; + + try + { + culture = CultureInfo.GetCultureInfoByIetfLanguageTag(locale); + return culture != null; + } + catch + { + return false; + } + } + + public static string GetLanguageNativeName(string locale) + { + if (TryGetCultureInfoForLocale(locale, out var culture)) + { + return culture.NativeName; + } + + return null; + } + + public static string GetLanguageNativeName(string locale, bool englishName, bool shortName = false) + { + try + { + if (locale.HasValue()) + { + var info = CultureInfo.GetCultureInfoByIetfLanguageTag(locale); + if (info != null) + { + if (shortName) + { + info = info.Parent; + } + + if (englishName) + { + return info.EnglishName; + } + else + { + return info.NativeName; + } + + } + } + } + catch + { + } + + return null; + } + + public static string NormalizeLanguageDisplayName(string languageName, bool stripRegion = false, CultureInfo culture = null) + { + if (string.IsNullOrEmpty(languageName) || languageName.Length == 0) + { + return languageName; + } + + // First char to upper. + if (char.IsLower(languageName[0])) + { + languageName = (culture ?? CultureInfo.InvariantCulture).TextInfo.ToTitleCase(languageName); + } + + var bracketIndex = languageName.IndexOfAny(new[] { '(', '[' }); + var hasRegion = bracketIndex > -1; + var endBracket = ')'; + + if (hasRegion) + { + if (languageName[bracketIndex] == '[') + { + endBracket = ']'; + } + + if (stripRegion) + { + languageName = languageName.Substring(0, bracketIndex).TrimEnd(); + } + } + + // Remove everything after ',' within Region part + if (hasRegion && !stripRegion) + { + var commaIndex = languageName.IndexOf(','); + if (commaIndex > -1) + { + languageName = languageName.Substring(0, commaIndex) + endBracket; + } + } + + return languageName; + } + + public static string GetCurrencySymbol(string locale) + { + try + { + if (locale.HasValue()) + { + var info = new RegionInfo(locale); + if (info != null) + return info.CurrencySymbol; + } + } + catch { } + + return null; + } + } } diff --git a/src/Libraries/SmartStore.Core/Localization/LocalizedString.cs b/src/Libraries/SmartStore.Core/Localization/LocalizedString.cs index 5965d20b51..82dc95d9ee 100644 --- a/src/Libraries/SmartStore.Core/Localization/LocalizedString.cs +++ b/src/Libraries/SmartStore.Core/Localization/LocalizedString.cs @@ -3,8 +3,8 @@ namespace SmartStore.Core.Localization { - [Serializable] - public class LocalizedString : IHtmlString + [Serializable] + public class LocalizedString : IHtmlString { private readonly string _localized; private readonly string _textHint; @@ -30,48 +30,33 @@ public static LocalizedString TextOrDefault(string text, LocalizedString default return new LocalizedString(text); } - public string TextHint - { - get { return _textHint; } - } + public string TextHint => _textHint; - public object[] Args - { - get { return _args; } - } + public object[] Args => _args; - public string Text - { - get { return _localized; } - } + public string Text => _localized; + + /// + /// Returns a js encoded string which already contains delimiters. + /// + public IHtmlString JsText => System.Web.Mvc.MvcHtmlString.Create(_localized.EncodeJsString()); - /// - /// Returns a js encoded string which already contains delimiters. - /// - public IHtmlString JsText - { - get - { - return System.Web.Mvc.MvcHtmlString.Create(_localized.EncodeJsString()); - } - } - - public static implicit operator string(LocalizedString obj) + public static implicit operator string(LocalizedString obj) { return obj.Text; } - public static implicit operator LocalizedString(string obj) - { - return new LocalizedString(obj); - } + public static implicit operator LocalizedString(string obj) + { + return new LocalizedString(obj); + } public override string ToString() { return _localized; } - public string ToHtmlString() + public string ToHtmlString() { return _localized; } diff --git a/src/Libraries/SmartStore.Core/Localization/Localizer.cs b/src/Libraries/SmartStore.Core/Localization/Localizer.cs index bb96ea085b..9b173a53b1 100644 --- a/src/Libraries/SmartStore.Core/Localization/Localizer.cs +++ b/src/Libraries/SmartStore.Core/Localization/Localizer.cs @@ -1,5 +1,5 @@ namespace SmartStore.Core.Localization { public delegate LocalizedString Localizer(string key, params object[] args); - public delegate LocalizedString LocalizerEx(string key, int languageId, params object[] args); + public delegate LocalizedString LocalizerEx(string key, int languageId, params object[] args); } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Localization/NullLocalizer.cs b/src/Libraries/SmartStore.Core/Localization/NullLocalizer.cs index 0b43a01c1d..fb77afe3ec 100644 --- a/src/Libraries/SmartStore.Core/Localization/NullLocalizer.cs +++ b/src/Libraries/SmartStore.Core/Localization/NullLocalizer.cs @@ -1,26 +1,18 @@ -using System; - -namespace SmartStore.Core.Localization +namespace SmartStore.Core.Localization { - public static class NullLocalizer - { - private static readonly Localizer _instance; - private static readonly LocalizerEx _instanceEx; + public static class NullLocalizer + { + private static readonly Localizer _instance; + private static readonly LocalizerEx _instanceEx; - static NullLocalizer() - { - _instance = (format, args) => new LocalizedString((args == null || args.Length == 0) ? format : string.Format(format, args)); - _instanceEx = (format, languageId, args) => new LocalizedString((args == null || args.Length == 0) ? format : string.Format(format, args)); - } + static NullLocalizer() + { + _instance = (format, args) => new LocalizedString((args == null || args.Length == 0) ? format : string.Format(format, args)); + _instanceEx = (format, languageId, args) => new LocalizedString((args == null || args.Length == 0) ? format : string.Format(format, args)); + } - public static Localizer Instance - { - get { return _instance; } - } + public static Localizer Instance => _instance; - public static LocalizerEx InstanceEx - { - get { return _instanceEx; } - } - } + public static LocalizerEx InstanceEx => _instanceEx; + } } diff --git a/src/Libraries/SmartStore.Core/Logging/IChronometer.cs b/src/Libraries/SmartStore.Core/Logging/IChronometer.cs index fb02512a32..ce8e9064d2 100644 --- a/src/Libraries/SmartStore.Core/Logging/IChronometer.cs +++ b/src/Libraries/SmartStore.Core/Logging/IChronometer.cs @@ -3,44 +3,41 @@ namespace SmartStore.Core.Logging { - public interface IChronometer : IDisposable - { - void StepStart(string key, string message); - void StepStop(string key); - } - - public static class IChronometerExtensions - { - public static IDisposable Step(this IChronometer chronometer, string message) - { - Guard.NotEmpty(message, nameof(message)); - - var key = "step" + CommonHelper.GenerateRandomDigitCode(10); - - chronometer.StepStart(key, message); - return new ActionDisposable(() => chronometer.StepStop(key)); - } - } - - public class NullChronometer : IChronometer - { - private static readonly IChronometer _instance = new NullChronometer(); - - public static IChronometer Instance - { - get { return _instance; } - } - - public void StepStart(string key, string message) - { - } - - public void StepStop(string key) - { - } - - public void Dispose() - { - } - } + public interface IChronometer : IDisposable + { + void StepStart(string key, string message); + void StepStop(string key); + } + + public static class IChronometerExtensions + { + public static IDisposable Step(this IChronometer chronometer, string message) + { + Guard.NotEmpty(message, nameof(message)); + + var key = "step" + CommonHelper.GenerateRandomDigitCode(10); + + chronometer.StepStart(key, message); + return new ActionDisposable(() => chronometer.StepStop(key)); + } + } + + public class NullChronometer : IChronometer + { + private static readonly IChronometer _instance = new NullChronometer(); + + public static IChronometer Instance => _instance; + + public void StepStart(string key, string message) + { + } + + public void StepStop(string key) + { + } + + public void Dispose() + { + } + } } diff --git a/src/Libraries/SmartStore.Core/Logging/ICustomerActivityService.cs b/src/Libraries/SmartStore.Core/Logging/ICustomerActivityService.cs index 3ff8526a96..8ae000f45b 100644 --- a/src/Libraries/SmartStore.Core/Logging/ICustomerActivityService.cs +++ b/src/Libraries/SmartStore.Core/Logging/ICustomerActivityService.cs @@ -5,10 +5,10 @@ namespace SmartStore.Core.Logging { - /// - /// Customer activity service interface - /// - public partial interface ICustomerActivityService + /// + /// Customer activity service interface + /// + public partial interface ICustomerActivityService { /// /// Inserts an activity log type item @@ -74,27 +74,27 @@ ActivityLog InsertActivity(string systemKeyword, /// Activity log void DeleteActivity(ActivityLog activityLog); - /// - /// Gets all activity log items - /// - /// Log item creation from; null to load all customers - /// Log item creation to; null to load all customers - /// Customer identifier; null to load all customers - /// Activity log type identifier - /// Page index - /// Page size - /// Customer email - /// Customer system name - /// Activity log collection - IPagedList GetAllActivities( - DateTime? createdOnFrom, + /// + /// Gets all activity log items + /// + /// Log item creation from; null to load all customers + /// Log item creation to; null to load all customers + /// Customer identifier; null to load all customers + /// Activity log type identifier + /// Page index + /// Page size + /// Customer email + /// Customer system name + /// Activity log collection + IPagedList GetAllActivities( + DateTime? createdOnFrom, DateTime? createdOnTo, - int? customerId, + int? customerId, int activityLogTypeId, - int pageIndex, - int pageSize, - string email = null, - bool? customerSystemAccount = null); + int pageIndex, + int pageSize, + string email = null, + bool? customerSystemAccount = null); /// /// Gets an activity log item @@ -103,16 +103,16 @@ IPagedList GetAllActivities( /// Activity log item ActivityLog GetActivityById(int activityLogId); - /// - /// Gets activity logs be identifier - /// - /// Activity log identifiers - /// List of activity logs - IList GetActivityByIds(int[] activityLogIds); + /// + /// Gets activity logs be identifier + /// + /// Activity log identifiers + /// List of activity logs + IList GetActivityByIds(int[] activityLogIds); - /// - /// Clears activity log - /// - void ClearAllActivities(); + /// + /// Clears activity log + /// + void ClearAllActivities(); } } diff --git a/src/Libraries/SmartStore.Core/Logging/ILogger.cs b/src/Libraries/SmartStore.Core/Logging/ILogger.cs index edec0f8dd0..3d985cb8bb 100644 --- a/src/Libraries/SmartStore.Core/Logging/ILogger.cs +++ b/src/Libraries/SmartStore.Core/Logging/ILogger.cs @@ -2,25 +2,25 @@ namespace SmartStore.Core.Logging { - /// - /// Interface that all loggers implement - /// - public partial interface ILogger + /// + /// Interface that all loggers implement + /// + public partial interface ILogger { - /// - /// Checks if this logger is enabled for a given passed as parameter. - /// - /// true if this logger is enabled for level, otherwise false - /// Result - bool IsEnabledFor(LogLevel level); + /// + /// Checks if this logger is enabled for a given passed as parameter. + /// + /// true if this logger is enabled for level, otherwise false + /// Result + bool IsEnabledFor(LogLevel level); - /// - /// Generates a logging event for the specified level using the message and exception - /// - /// The level of the message to be logged - /// The exception to log, including its stack trace. Pass null to not log an exception - /// The message object to log - /// An Object array containing zero or more objects to format. Can be null. - void Log(LogLevel level, Exception exception, string message, object[] args); + /// + /// Generates a logging event for the specified level using the message and exception + /// + /// The level of the message to be logged + /// The exception to log, including its stack trace. Pass null to not log an exception + /// The message object to log + /// An Object array containing zero or more objects to format. Can be null. + void Log(LogLevel level, Exception exception, string message, object[] args); } } diff --git a/src/Libraries/SmartStore.Core/Logging/ILoggerFactory.cs b/src/Libraries/SmartStore.Core/Logging/ILoggerFactory.cs index 89f159f187..31ed223fec 100644 --- a/src/Libraries/SmartStore.Core/Logging/ILoggerFactory.cs +++ b/src/Libraries/SmartStore.Core/Logging/ILoggerFactory.cs @@ -2,18 +2,18 @@ namespace SmartStore.Core.Logging { - public interface ILoggerFactory - { - ILogger GetLogger(Type type); - ILogger GetLogger(string name); - void FlushAll(); - } + public interface ILoggerFactory + { + ILogger GetLogger(Type type); + ILogger GetLogger(string name); + void FlushAll(); + } - public static class ILoggerFactoryExtensions - { - public static ILogger CreateLogger(this ILoggerFactory factory) where T : class - { - return factory.GetLogger(typeof(T)); - } - } + public static class ILoggerFactoryExtensions + { + public static ILogger CreateLogger(this ILoggerFactory factory) where T : class + { + return factory.GetLogger(typeof(T)); + } + } } diff --git a/src/Libraries/SmartStore.Core/Logging/LogLevel.cs b/src/Libraries/SmartStore.Core/Logging/LogLevel.cs index 8792dbee72..d370399f51 100644 --- a/src/Libraries/SmartStore.Core/Logging/LogLevel.cs +++ b/src/Libraries/SmartStore.Core/Logging/LogLevel.cs @@ -8,7 +8,7 @@ public enum LogLevel Debug = 10, Information = 20, Warning = 30, - Error = 40 , + Error = 40, Fatal = 50 } } diff --git a/src/Libraries/SmartStore.Core/Logging/LoggingExtensions.cs b/src/Libraries/SmartStore.Core/Logging/LoggingExtensions.cs index 2d7d6e82ee..40081ea027 100644 --- a/src/Libraries/SmartStore.Core/Logging/LoggingExtensions.cs +++ b/src/Libraries/SmartStore.Core/Logging/LoggingExtensions.cs @@ -1,72 +1,195 @@ using System; +using System.Runtime.CompilerServices; namespace SmartStore.Core.Logging { public static class LoggingExtensions { - public static bool IsDebugEnabled(this ILogger l) => l.IsEnabledFor(LogLevel.Debug); - public static bool IsInfoEnabled(this ILogger l) => l.IsEnabledFor(LogLevel.Information); - public static bool IsWarnEnabled(this ILogger l) => l.IsEnabledFor(LogLevel.Warning); - public static bool IsErrorEnabled(this ILogger l) => l.IsEnabledFor(LogLevel.Error); - public static bool IsFatalEnabled(this ILogger l) => l.IsEnabledFor(LogLevel.Fatal); - - - - public static void Debug(this ILogger l, string msg) => FilteredLog(l, LogLevel.Debug, null, msg, null); - public static void Debug(this ILogger l, Func msgFactory) => FilteredLog(l, LogLevel.Debug, null, msgFactory); - public static void Debug(this ILogger l, Exception ex, string msg) => FilteredLog(l, LogLevel.Debug, ex, msg, null); - public static void DebugFormat(this ILogger l, string msg, params object[] args) => FilteredLog(l, LogLevel.Debug, null, msg, args); - public static void DebugFormat(this ILogger l, Exception ex, string msg, params object[] args) => FilteredLog(l, LogLevel.Debug, ex, msg, args); - - public static void Info(this ILogger l, string msg) => FilteredLog(l, LogLevel.Information, null, msg, null); - public static void Info(this ILogger l, Func msgFactory) => FilteredLog(l, LogLevel.Information, null, msgFactory); - public static void Info(this ILogger l, Exception ex, string msg) => FilteredLog(l, LogLevel.Information, ex, msg, null); - public static void InfoFormat(this ILogger l, string msg, params object[] args) => FilteredLog(l, LogLevel.Information, null, msg, args); - public static void InfoFormat(this ILogger l, Exception ex, string msg, params object[] args) => FilteredLog(l, LogLevel.Information, ex, msg, args); - - public static void Warn(this ILogger l, string msg) => FilteredLog(l, LogLevel.Warning, null, msg, null); - public static void Warn(this ILogger l, Func msgFactory) => FilteredLog(l, LogLevel.Warning, null, msgFactory); - public static void Warn(this ILogger l, Exception ex, string msg) => FilteredLog(l, LogLevel.Warning, ex, msg, null); - public static void WarnFormat(this ILogger l, string msg, params object[] args) => FilteredLog(l, LogLevel.Warning, null, msg, args); - public static void WarnFormat(this ILogger l, Exception ex, string msg, params object[] args) => FilteredLog(l, LogLevel.Warning, ex, msg, args); - - public static void Error(this ILogger l, string msg) => FilteredLog(l, LogLevel.Error, null, msg, null); - public static void Error(this ILogger l, Func msgFactory) => FilteredLog(l, LogLevel.Error, null, msgFactory); - public static void Error(this ILogger l, Exception ex) => FilteredLog(l, LogLevel.Error, ex, null, null); - public static void Error(this ILogger l, Exception ex, string msg) => FilteredLog(l, LogLevel.Error, ex, msg, null); - public static void ErrorFormat(this ILogger l, string msg, params object[] args) => FilteredLog(l, LogLevel.Error, null, msg, args); - public static void ErrorFormat(this ILogger l, Exception ex, string msg, params object[] args) => FilteredLog(l, LogLevel.Error, ex, msg, args); - - public static void Fatal(this ILogger l, string msg) => FilteredLog(l, LogLevel.Fatal, null, msg, null); - public static void Fatal(this ILogger l, Func msgFactory) => FilteredLog(l, LogLevel.Fatal, null, msgFactory); - public static void Fatal(this ILogger l, Exception ex) => FilteredLog(l, LogLevel.Fatal, ex, null, null); - public static void Fatal(this ILogger l, Exception ex, string msg) => FilteredLog(l, LogLevel.Fatal, ex, msg, null); - public static void FatalFormat(this ILogger l, string msg, params object[] args) => FilteredLog(l, LogLevel.Fatal, null, msg, args); - public static void FatalFormat(this ILogger l, Exception ex, string msg, params object[] args) => FilteredLog(l, LogLevel.Fatal, ex, msg, args); - - public static void ErrorsAll(this ILogger logger, Exception exception) - { - while (exception != null) - { - FilteredLog(logger, LogLevel.Error, exception, exception.Message, null); - exception = exception.InnerException; - } - } - - private static void FilteredLog(ILogger logger, LogLevel level, Exception exception, string message, object[] objects) - { - if (logger.IsEnabledFor(level)) - { - logger.Log(level, exception, message, objects); - } - } - - private static void FilteredLog(ILogger logger, LogLevel level, Exception exception, Func messageFactory) - { - if (logger.IsEnabledFor(level)) - { - logger.Log(level, exception, messageFactory(), null); - } - } - } + public static bool IsDebugEnabled(this ILogger l) + { + return l.IsEnabledFor(LogLevel.Debug); + } + + public static bool IsInfoEnabled(this ILogger l) + { + return l.IsEnabledFor(LogLevel.Information); + } + + public static bool IsWarnEnabled(this ILogger l) + { + return l.IsEnabledFor(LogLevel.Warning); + } + + public static bool IsErrorEnabled(this ILogger l) + { + return l.IsEnabledFor(LogLevel.Error); + } + + public static bool IsFatalEnabled(this ILogger l) + { + return l.IsEnabledFor(LogLevel.Fatal); + } + + public static void Debug(this ILogger l, string msg) + { + FilteredLog(l, LogLevel.Debug, null, msg, null); + } + + public static void Debug(this ILogger l, Func msgFactory) + { + FilteredLog(l, LogLevel.Debug, null, msgFactory); + } + + public static void Debug(this ILogger l, Exception ex, string msg) + { + FilteredLog(l, LogLevel.Debug, ex, msg, null); + } + + public static void DebugFormat(this ILogger l, string msg, params object[] args) + { + FilteredLog(l, LogLevel.Debug, null, msg, args); + } + + public static void DebugFormat(this ILogger l, Exception ex, string msg, params object[] args) + { + FilteredLog(l, LogLevel.Debug, ex, msg, args); + } + + public static void Info(this ILogger l, string msg) + { + FilteredLog(l, LogLevel.Information, null, msg, null); + } + + public static void Info(this ILogger l, Func msgFactory) + { + FilteredLog(l, LogLevel.Information, null, msgFactory); + } + + public static void Info(this ILogger l, Exception ex, string msg) + { + FilteredLog(l, LogLevel.Information, ex, msg, null); + } + + public static void InfoFormat(this ILogger l, string msg, params object[] args) + { + FilteredLog(l, LogLevel.Information, null, msg, args); + } + + public static void InfoFormat(this ILogger l, Exception ex, string msg, params object[] args) + { + FilteredLog(l, LogLevel.Information, ex, msg, args); + } + + public static void Warn(this ILogger l, string msg) + { + FilteredLog(l, LogLevel.Warning, null, msg, null); + } + + public static void Warn(this ILogger l, Func msgFactory) + { + FilteredLog(l, LogLevel.Warning, null, msgFactory); + } + + public static void Warn(this ILogger l, Exception ex, string msg) + { + FilteredLog(l, LogLevel.Warning, ex, msg, null); + } + + public static void WarnFormat(this ILogger l, string msg, params object[] args) + { + FilteredLog(l, LogLevel.Warning, null, msg, args); + } + + public static void WarnFormat(this ILogger l, Exception ex, string msg, params object[] args) + { + FilteredLog(l, LogLevel.Warning, ex, msg, args); + } + + public static void Error(this ILogger l, string msg) + { + FilteredLog(l, LogLevel.Error, null, msg, null); + } + + public static void Error(this ILogger l, Func msgFactory) + { + FilteredLog(l, LogLevel.Error, null, msgFactory); + } + + public static void Error(this ILogger l, Exception ex) + { + FilteredLog(l, LogLevel.Error, ex, null, null); + } + + public static void Error(this ILogger l, Exception ex, string msg) + { + FilteredLog(l, LogLevel.Error, ex, msg, null); + } + + public static void ErrorFormat(this ILogger l, string msg, params object[] args) + { + FilteredLog(l, LogLevel.Error, null, msg, args); + } + + public static void ErrorFormat(this ILogger l, Exception ex, string msg, params object[] args) + { + FilteredLog(l, LogLevel.Error, ex, msg, args); + } + + public static void Fatal(this ILogger l, string msg) + { + FilteredLog(l, LogLevel.Fatal, null, msg, null); + } + + public static void Fatal(this ILogger l, Func msgFactory) + { + FilteredLog(l, LogLevel.Fatal, null, msgFactory); + } + + public static void Fatal(this ILogger l, Exception ex) + { + FilteredLog(l, LogLevel.Fatal, ex, null, null); + } + + public static void Fatal(this ILogger l, Exception ex, string msg) + { + FilteredLog(l, LogLevel.Fatal, ex, msg, null); + } + + public static void FatalFormat(this ILogger l, string msg, params object[] args) + { + FilteredLog(l, LogLevel.Fatal, null, msg, args); + } + + public static void FatalFormat(this ILogger l, Exception ex, string msg, params object[] args) + { + FilteredLog(l, LogLevel.Fatal, ex, msg, args); + } + + public static void ErrorsAll(this ILogger logger, Exception exception) + { + while (exception != null) + { + FilteredLog(logger, LogLevel.Error, exception, exception.Message, null); + exception = exception.InnerException; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void FilteredLog(ILogger logger, LogLevel level, Exception exception, string message, object[] objects) + { + if (logger.IsEnabledFor(level)) + { + logger.Log(level, exception, message, objects); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void FilteredLog(ILogger logger, LogLevel level, Exception exception, Func messageFactory) + { + if (logger.IsEnabledFor(level)) + { + logger.Log(level, exception, messageFactory(), null); + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Logging/LoggingModule.cs b/src/Libraries/SmartStore.Core/Logging/LoggingModule.cs index 131da553b9..34ca8cea69 100644 --- a/src/Libraries/SmartStore.Core/Logging/LoggingModule.cs +++ b/src/Libraries/SmartStore.Core/Logging/LoggingModule.cs @@ -1,143 +1,143 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using Autofac; using Autofac.Core; using Autofac.Core.Activators.Reflection; +using Autofac.Core.Registration; using SmartStore.ComponentModel; using SmartStore.Core.Data; namespace SmartStore.Core.Logging { - public class LoggingModule : Autofac.Module - { - protected override void Load(ContainerBuilder builder) - { - builder.RegisterType().As().SingleInstance(); - - builder.RegisterType().As().SingleInstance(); - - // call GetLogger in response to the request for an ILogger implementation - if (DataSettings.DatabaseIsInstalled()) - { - builder.Register(GetContextualLogger).As().ExternallyOwned(); - } - else - { - // the install logger should append to a rolling text file only. - builder.Register(GetInstallLogger).As().ExternallyOwned(); - } - } - - protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) - { - bool hasCtorLogger = false; - bool hasPropertyLogger = false; - - FastProperty[] loggerProperties = null; - - var ra = registration.Activator as ReflectionActivator; - if (ra != null) - { - // // Look for ctor parameters of type "ILogger" - var ctors = ra.ConstructorFinder.FindConstructors(ra.LimitType); - var loggerParameters = ctors.SelectMany(ctor => ctor.GetParameters()).Where(pi => pi.ParameterType == typeof(ILogger)); - hasCtorLogger = loggerParameters.Any(); - - // Autowire properties - // Look for settable properties of type "ILogger" - loggerProperties = ra.LimitType - .GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance) - .Select(p => new - { - PropertyInfo = p, - p.PropertyType, - IndexParameters = p.GetIndexParameters(), - Accessors = p.GetAccessors(false) - }) - .Where(x => x.PropertyType == typeof(ILogger)) // must be a logger - .Where(x => x.IndexParameters.Count() == 0) // must not be an indexer - .Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)) //must have get/set, or only set - .Select(x => new FastProperty(x.PropertyInfo)) - .ToArray(); - - hasPropertyLogger = loggerProperties.Length > 0; - - // Ignore components known to be without logger dependencies - if (!hasCtorLogger && !hasPropertyLogger) - return; - - if (hasPropertyLogger) - { - registration.Metadata.Add("LoggerProperties", loggerProperties); - } - } - - if (hasCtorLogger) - { - registration.Preparing += (sender, args) => - { - var logger = GetLoggerFor(args.Component.Activator.LimitType, args.Context); - args.Parameters = new[] { TypedParameter.From(logger) }.Concat(args.Parameters); - }; - } - - if (hasPropertyLogger) - { - registration.Activating += (sender, args) => - { - var logger = GetLoggerFor(args.Component.Activator.LimitType, args.Context); - var loggerProps = args.Component.Metadata.Get("LoggerProperties") as FastProperty[]; - if (loggerProps != null) - { - foreach (var prop in loggerProps) - { - prop.SetValue(args.Instance, logger); - } - } - }; - } - } - - private ILogger GetLoggerFor(Type componentType, IComponentContext ctx) - { - return ctx.Resolve(new TypedParameter(typeof(Type), componentType)); - } - - private static ILogger GetContextualLogger(IComponentContext context, IEnumerable parameters) - { - // return an ILogger in response to Resolve(componentTypeParameter) - var loggerFactory = context.Resolve(); - - Type containingType = null; - - if (parameters != null && parameters.Any()) - { - if (parameters.Any(x => x is TypedParameter)) - { - containingType = parameters.TypedAs(); - } - else if (parameters.Any(x => x is NamedParameter)) - { - containingType = parameters.Named("Autofac.AutowiringPropertyInjector.InstanceType"); - } - } - - if (containingType != null) - { - return loggerFactory.GetLogger(containingType); - } - else - { - return loggerFactory.GetLogger("SmartStore"); - } - } - - private static ILogger GetInstallLogger(IComponentContext context) - { - return context.Resolve().GetLogger("Install"); - } - } + public class LoggingModule : Autofac.Module + { + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().As().SingleInstance(); + + builder.RegisterType().As().SingleInstance(); + + // call GetLogger in response to the request for an ILogger implementation + if (DataSettings.DatabaseIsInstalled()) + { + builder.Register(GetContextualLogger).As().ExternallyOwned(); + } + else + { + // the install logger should append to a rolling text file only. + builder.Register(GetInstallLogger).As().ExternallyOwned(); + } + } + + protected override void AttachToComponentRegistration(IComponentRegistryBuilder componentRegistry, IComponentRegistration registration) + { + bool hasCtorLogger = false; + bool hasPropertyLogger = false; + + FastProperty[] loggerProperties = null; + + var ra = registration.Activator as ReflectionActivator; + if (ra != null) + { + // // Look for ctor parameters of type "ILogger" + var ctors = ra.ConstructorFinder.FindConstructors(ra.LimitType); + var loggerParameters = ctors.SelectMany(ctor => ctor.GetParameters()).Where(pi => pi.ParameterType == typeof(ILogger)); + hasCtorLogger = loggerParameters.Any(); + + // Autowire properties + // Look for settable properties of type "ILogger" + loggerProperties = ra.LimitType + .GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance) + .Select(p => new + { + PropertyInfo = p, + p.PropertyType, + IndexParameters = p.GetIndexParameters(), + Accessors = p.GetAccessors(false) + }) + .Where(x => x.PropertyType == typeof(ILogger)) // must be a logger + .Where(x => x.IndexParameters.Count() == 0) // must not be an indexer + .Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)) //must have get/set, or only set + .Select(x => FastProperty.Create(x.PropertyInfo)) + .ToArray(); + + hasPropertyLogger = loggerProperties.Length > 0; + + // Ignore components known to be without logger dependencies + if (!hasCtorLogger && !hasPropertyLogger) + return; + + if (hasPropertyLogger) + { + registration.Metadata.Add("LoggerProperties", loggerProperties); + } + } + + if (hasCtorLogger) + { + registration.Preparing += (sender, args) => + { + var logger = GetLoggerFor(args.Component.Activator.LimitType, args.Context); + args.Parameters = new[] { TypedParameter.From(logger) }.Concat(args.Parameters); + }; + } + + if (hasPropertyLogger) + { + registration.Activating += (sender, args) => + { + var logger = GetLoggerFor(args.Component.Activator.LimitType, args.Context); + var loggerProps = args.Component.Metadata.Get("LoggerProperties") as FastProperty[]; + if (loggerProps != null) + { + foreach (var prop in loggerProps) + { + prop.SetValue(args.Instance, logger); + } + } + }; + } + } + + private ILogger GetLoggerFor(Type componentType, IComponentContext ctx) + { + return ctx.Resolve(new TypedParameter(typeof(Type), componentType)); + } + + private static ILogger GetContextualLogger(IComponentContext context, IEnumerable parameters) + { + // return an ILogger in response to Resolve(componentTypeParameter) + var loggerFactory = context.Resolve(); + + Type containingType = null; + + if (parameters != null && parameters.Any()) + { + if (parameters.Any(x => x is TypedParameter)) + { + containingType = parameters.TypedAs(); + } + else if (parameters.Any(x => x is NamedParameter)) + { + containingType = parameters.Named("Autofac.AutowiringPropertyInjector.InstanceType"); + } + } + + if (containingType != null) + { + return loggerFactory.GetLogger(containingType); + } + else + { + return loggerFactory.GetLogger("SmartStore"); + } + } + + private static ILogger GetInstallLogger(IComponentContext context) + { + return context.Resolve().GetLogger("Install"); + } + } } diff --git a/src/Libraries/SmartStore.Core/Logging/Notifier.cs b/src/Libraries/SmartStore.Core/Logging/Notifier.cs index ed2a00ba94..04afde9843 100644 --- a/src/Libraries/SmartStore.Core/Logging/Notifier.cs +++ b/src/Libraries/SmartStore.Core/Logging/Notifier.cs @@ -1,52 +1,65 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Runtime.CompilerServices; using SmartStore.Core.Localization; namespace SmartStore.Core.Logging { - public interface INotifier - { - void Add(NotifyType type, LocalizedString message, bool durable = true); - ICollection Entries { get; } - } - - public class Notifier : INotifier - { - private readonly HashSet _entries = new HashSet(); - - public void Add(NotifyType type, LocalizedString message, bool durable = true) - { - _entries.Add(new NotifyEntry { Type = type, Message = message, Durable = durable }); - } - - public ICollection Entries - { - get { return _entries; } - } - } - - public static class INotifierExtension - { - public static void Information(this INotifier notifier, LocalizedString message, bool durable = true) - { - notifier.Add(NotifyType.Info, message, durable); - } - - public static void Success(this INotifier notifier, LocalizedString message, bool durable = true) - { - notifier.Add(NotifyType.Success, message, durable); - } - - public static void Warning(this INotifier notifier, LocalizedString message, bool durable = true) - { - notifier.Add(NotifyType.Warning, message, durable); - } - - public static void Error(this INotifier notifier, LocalizedString message, bool durable = true) - { - notifier.Add(NotifyType.Error, message, durable); - } - } + public interface INotifier + { + void Add(NotifyType type, LocalizedString message, bool durable = true); + ICollection Entries { get; } + } + + public class Notifier : INotifier + { + private readonly HashSet _entries = new HashSet(); + + public void Add(NotifyType type, LocalizedString message, bool durable = true) + { + _entries.Add(new NotifyEntry { Type = type, Message = message, Durable = durable }); + } + + public ICollection Entries => _entries; + } + + public static class INotifierExtension + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Information(this INotifier notifier, LocalizedString message, bool durable = true) + { + notifier.Add(NotifyType.Info, message, durable); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Success(this INotifier notifier, LocalizedString message, bool durable = true) + { + notifier.Add(NotifyType.Success, message, durable); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Warning(this INotifier notifier, LocalizedString message, bool durable = true) + { + notifier.Add(NotifyType.Warning, message, durable); + } + + public static void Error(this INotifier notifier, Exception exception, bool durable = true) + { + if (exception == null) + return; + + while (exception.InnerException != null) + { + exception = exception.InnerException; + } + + notifier.Add(NotifyType.Error, exception.Message, durable); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Error(this INotifier notifier, LocalizedString message, bool durable = true) + { + notifier.Add(NotifyType.Error, message, durable); + } + } } diff --git a/src/Libraries/SmartStore.Core/Logging/NotifyEntry.cs b/src/Libraries/SmartStore.Core/Logging/NotifyEntry.cs index 1196fc4790..a1bf825772 100644 --- a/src/Libraries/SmartStore.Core/Logging/NotifyEntry.cs +++ b/src/Libraries/SmartStore.Core/Logging/NotifyEntry.cs @@ -1,28 +1,27 @@ using System; -using System.Collections.Generic; using SmartStore.Core.Localization; namespace SmartStore.Core.Logging { - public enum NotifyType - { - Info, - Success, - Warning, - Error - } + public enum NotifyType + { + Info, + Success, + Warning, + Error + } - [Serializable] - public class NotifyEntry : ComparableObject - { - [ObjectSignature] - public NotifyType Type { get; set; } + [Serializable] + public class NotifyEntry : ComparableObject + { + [ObjectSignature] + public NotifyType Type { get; set; } - [ObjectSignature] - public LocalizedString Message { get; set; } + [ObjectSignature] + public LocalizedString Message { get; set; } - public bool Durable { get; set; } - } + public bool Durable { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Logging/NullLogger.cs b/src/Libraries/SmartStore.Core/Logging/NullLogger.cs index ffdb5139a5..1ca0bf46cb 100644 --- a/src/Libraries/SmartStore.Core/Logging/NullLogger.cs +++ b/src/Libraries/SmartStore.Core/Logging/NullLogger.cs @@ -1,35 +1,35 @@ using System; -using SmartStore.Core.Domain.Customers; -using SmartStore.Core.Domain.Logging; namespace SmartStore.Core.Logging { public partial class NullLogger : ILogger { - private static readonly ILogger s_instance = new NullLogger(); + private static readonly ILogger s_instance = new NullLogger(); + + public static ILogger Instance => s_instance; - public static ILogger Instance - { - get - { - return s_instance; - } - } - public bool IsEnabledFor(LogLevel level) { return false; } - public void Log(LogLevel level, Exception exception, string message, object[] args) - { - } - } + public void Log(LogLevel level, Exception exception, string message, object[] args) + { + } + } + + public class NullLoggerFactory : ILoggerFactory + { + public ILogger GetLogger(Type type) + { + return NullLogger.Instance; + } + + public ILogger GetLogger(string name) + { + return NullLogger.Instance; + } - public class NullLoggerFactory : ILoggerFactory - { - public ILogger GetLogger(Type type) => NullLogger.Instance; - public ILogger GetLogger(string name) => NullLogger.Instance; - public void FlushAll() { } - } + public void FlushAll() { } + } } diff --git a/src/Libraries/SmartStore.Core/Logging/TraceLogger.cs b/src/Libraries/SmartStore.Core/Logging/TraceLogger.cs index 10da774027..073e3d5d3d 100644 --- a/src/Libraries/SmartStore.Core/Logging/TraceLogger.cs +++ b/src/Libraries/SmartStore.Core/Logging/TraceLogger.cs @@ -3,119 +3,156 @@ using System.IO; using System.Linq; using System.Text; -using SmartStore.Core.Domain.Customers; -using SmartStore.Core.Domain.Logging; using SmartStore.Utilities; namespace SmartStore.Core.Logging { - public class TraceLogger : DisposableObject, ILogger - { - private readonly TraceSource _traceSource; - private readonly StreamWriter _streamWriter; - - public TraceLogger() : this(CommonHelper.MapPath("~/App_Data/SmartStore.log")) - { - } - - public TraceLogger(string fileName) - { - Guard.NotEmpty(fileName, nameof(fileName)); - - _traceSource = new TraceSource("SmartStore"); - _traceSource.Switch = new SourceSwitch("LogSwitch", "Error"); - _traceSource.Listeners.Remove("Default"); - - var console = new ConsoleTraceListener(false); - console.Filter = new EventTypeFilter(SourceLevels.All); - console.Name = "console"; - - _traceSource.Listeners.Add(console); - - var textListener = new TextWriterTraceListener(fileName); - textListener.Filter = new EventTypeFilter(SourceLevels.All); - textListener.TraceOutputOptions = TraceOptions.DateTime; - - try - { - // force UTF-8 encoding (even if the text just contains ANSI characters) - var append = File.Exists(fileName); - _streamWriter = new StreamWriter(fileName, append, Encoding.UTF8); - - textListener.Writer = _streamWriter; - - _traceSource.Listeners.Add(textListener); - } - catch (IOException) - { - // file is locked by another process - } - - // Allow the trace source to send messages to - // listeners for all event types. Currently only - // error messages or higher go to the listeners. - // Messages must get past the source switch to - // get to the listeners, regardless of the settings - // for the listeners. - _traceSource.Switch.Level = SourceLevels.All; - } - - public bool IsEnabledFor(LogLevel level) - { - return true; - } - - public void Log(LogLevel level, Exception exception, string message, object[] args) - { - var type = LogLevelToEventType(level); - - if (exception != null && !exception.IsFatal()) - { - message = message.Grow(exception.ToString(), Environment.NewLine); - } - - if (message.HasValue()) - { - var msg = args != null && args.Any() - ? message.FormatInvariant(args) - : message; - - _traceSource.TraceEvent(type, (int)type, "{0}: {1}".FormatCurrent(type.ToString().ToUpper(), msg)); - } - } - - private TraceEventType LogLevelToEventType(LogLevel level) - { - switch (level) - { - case LogLevel.Debug: - return TraceEventType.Verbose; - case LogLevel.Error: - return TraceEventType.Error; - case LogLevel.Fatal: - return TraceEventType.Critical; - case LogLevel.Warning: - return TraceEventType.Warning; - default: - return TraceEventType.Information; - } - } - - public void Flush() - { - _traceSource.Flush(); - } - - protected override void OnDispose(bool disposing) - { - _traceSource.Flush(); - _traceSource.Close(); - - if (_streamWriter != null) - { - _streamWriter.Close(); - _streamWriter.Dispose(); - } - } - } + public class TraceLogger : DisposableObject, ILogger + { + private readonly TraceSource _traceSource; + private readonly StreamWriter _streamWriter; + + public TraceLogger() : this(GetDefaultFileName()) + { + } + + private static string GetDefaultFileName() + { + var di = new DirectoryInfo(CommonHelper.MapPath("~/App_Data/Logs")); + if (!di.Exists) + { + di.Create(); + } + + var datePart = DateTime.Now.ToString("yyyy-MM"); + var fileName = "SmartStore-{0}.log".FormatInvariant(datePart); + + return Path.Combine(di.FullName, fileName); + } + + public TraceLogger(string fileName) + { + Guard.NotEmpty(fileName, nameof(fileName)); + + _traceSource = new TraceSource("SmartStore"); + _traceSource.Switch = new SourceSwitch("LogSwitch", "Error"); + _traceSource.Listeners.Remove("Default"); + + if (CommonHelper.IsDevEnvironment) + { + var defaultListener = new DefaultTraceListener() + { + Name = "Debugger", + Filter = new EventTypeFilter(SourceLevels.All), + TraceOutputOptions = TraceOptions.DateTime + }; + _traceSource.Listeners.Add(defaultListener); + } + + var textListener = new TextWriterTraceListener(fileName) + { + Name = "File", + Filter = new EventTypeFilter(SourceLevels.All), + TraceOutputOptions = TraceOptions.DateTime + }; + + try + { + // force UTF-8 encoding (even if the text just contains ANSI characters) + var append = File.Exists(fileName); + _streamWriter = new StreamWriter(fileName, append, Encoding.UTF8); + + textListener.Writer = _streamWriter; + + _traceSource.Listeners.Add(textListener); + } + catch (IOException) + { + // file is locked by another process + } + + // Allow the trace source to send messages to + // listeners for all event types. Currently only + // error messages or higher go to the listeners. + // Messages must get past the source switch to + // get to the listeners, regardless of the settings + // for the listeners. + _traceSource.Switch.Level = SourceLevels.All; + } + + public bool IsEnabledFor(LogLevel level) + { + return _traceSource.Switch.ShouldTrace(LogLevelToEventType(level)); + } + + public void Log(LogLevel level, Exception exception, string message, object[] args) + { + var type = LogLevelToEventType(level); + + if (exception != null && !exception.IsFatal()) + { + message = message.Grow(exception.ToString(), Environment.NewLine); + } + + if (message.HasValue()) + { + var msg = args != null && args.Any() + ? message.FormatCurrent(args) + : message; + + _traceSource.TraceEvent(type, (int)type, msg); + } + } + + private TraceEventType LogLevelToEventType(LogLevel level) + { + switch (level) + { + case LogLevel.Debug: + return TraceEventType.Verbose; + case LogLevel.Error: + return TraceEventType.Error; + case LogLevel.Fatal: + return TraceEventType.Critical; + case LogLevel.Warning: + return TraceEventType.Warning; + default: + return TraceEventType.Information; + } + } + + //private LogLevel EventTypeToLogLevel(TraceEventType eventType) + //{ + // switch (eventType) + // { + // case TraceEventType.Verbose: + // return LogLevel.Debug; + // case TraceEventType.Error: + // return LogLevel.Error; + // case TraceEventType.Critical: + // return LogLevel.Fatal; + // case TraceEventType.Warning: + // return LogLevel.Warning; + // default: + // return LogLevel.Information; + // } + //} + + public void Flush() + { + _traceSource.Flush(); + } + + protected override void OnDispose(bool disposing) + { + _traceSource.Flush(); + _traceSource.Close(); + + if (_streamWriter != null) + { + _streamWriter.Close(); + _streamWriter.Dispose(); + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Logging/log4net/Log4netLogger.cs b/src/Libraries/SmartStore.Core/Logging/log4net/Log4netLogger.cs index 6bd1f49e02..be5b37a6b1 100644 --- a/src/Libraries/SmartStore.Core/Logging/log4net/Log4netLogger.cs +++ b/src/Libraries/SmartStore.Core/Logging/log4net/Log4netLogger.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Globalization; using System.Web; -using log4net; using log4net.Core; using log4net.Util; using SmartStore.Core.Data; @@ -12,149 +11,146 @@ namespace SmartStore.Core.Logging { - [DebuggerDisplay("Logger: {Name}")] - public class Log4netLogger : ILogger - { - private static readonly Type _declaringType = typeof(Log4netLogger); - private static readonly ContextState> _state; - - private readonly log4net.Core.ILogger _logger; - - static Log4netLogger() - { - _state = new ContextState>("Log4netLogger.LogicalThreadContext", () => new Dictionary(StringComparer.OrdinalIgnoreCase)); - } - - public Log4netLogger(log4net.Core.ILogger logger) - { - Guard.NotNull(logger, nameof(logger)); - - _logger = logger; - } - - public string Name - { - get { return _logger.Name; } - } - - public bool IsEnabledFor(LogLevel level) - { - return _logger.IsEnabledFor(ConvertLevel(level)); - } - - public void Log(LogLevel level, Exception exception, string message, object[] args) - { - var logLevel = ConvertLevel(level); - - if (message == null && exception != null) - { - message = exception.Message; - } - - object messageObj = message; - - if (args != null && args.Length > 0) - { - messageObj = new SystemStringFormat(CultureInfo.CurrentUICulture, message, args); - } - - var loggingEvent = new LoggingEvent( - _declaringType, - _logger.Repository, - _logger.Name, - logLevel, - messageObj, - exception); - - loggingEvent.Properties["LevelId"] = (int)level; - TryAddExtendedThreadInfo(loggingEvent); - - _logger.Log(loggingEvent); - } - - protected internal void TryAddExtendedThreadInfo(LoggingEvent loggingEvent) - { - var isAppInitialized = EngineContext.Current.IsFullyInitialized; - - // Don't knowingly run into exception - var httpRequest = isAppInitialized ? HttpContext.Current.SafeGetHttpRequest() : null; - - if (httpRequest == null) - { - loggingEvent.Properties["CustomerId"] = DBNull.Value; - loggingEvent.Properties["Url"] = DBNull.Value; - loggingEvent.Properties["Referrer"] = DBNull.Value; - loggingEvent.Properties["HttpMethod"] = DBNull.Value; - loggingEvent.Properties["Ip"] = DBNull.Value; - - return; - } - - var props = _state.GetState(); - - // Load the log4net thread with additional properties if they are available - var threadInfoMissing = !props.ContainsKey("sm:ThreadInfoAdded"); - - if (threadInfoMissing) - { - using (new ActionDisposable(() => props["sm:ThreadInfoAdded"] = true)) - { - if (DataSettings.DatabaseIsInstalled() && isAppInitialized) - { - var container = EngineContext.Current.ContainerManager; - - // CustomerId - if (container.TryResolve(null, out IWorkContext workContext)) - { - try - { - props["CustomerId"] = workContext.CurrentCustomer.Id; - } - catch - { - props["CustomerId"] = DBNull.Value; - } - } - - - // Url & stuff - if (container.TryResolve(null, out IWebHelper webHelper)) - { - try - { - props["Url"] = webHelper.GetThisPageUrl(true); - props["Referrer"] = webHelper.GetUrlReferrer(); - props["HttpMethod"] = httpRequest?.HttpMethod; - props["Ip"] = webHelper.GetCurrentIpAddress(); - } - catch { } - } - } - } - } - - loggingEvent.Properties["CustomerId"] = props.Get("CustomerId") ?? DBNull.Value; - loggingEvent.Properties["Url"] = props.Get("Url") ?? DBNull.Value; - loggingEvent.Properties["Referrer"] = props.Get("Referrer") ?? DBNull.Value; - loggingEvent.Properties["HttpMethod"] = props.Get("HttpMethod") ?? DBNull.Value; - loggingEvent.Properties["Ip"] = props.Get("Ip") ?? DBNull.Value; - } - - private static Level ConvertLevel(LogLevel level) - { - switch (level) - { - case LogLevel.Information: - return Level.Info; - case LogLevel.Warning: - return Level.Warn; - case LogLevel.Error: - return Level.Error; - case LogLevel.Fatal: - return Level.Fatal; - default: - return Level.Debug; - } - } - } + [DebuggerDisplay("Logger: {Name}")] + public class Log4netLogger : ILogger + { + private static readonly Type _declaringType = typeof(Log4netLogger); + private static readonly ContextState> _state; + + private readonly log4net.Core.ILogger _logger; + + static Log4netLogger() + { + _state = new ContextState>("Log4netLogger.LogicalThreadContext", () => new Dictionary(StringComparer.OrdinalIgnoreCase)); + } + + public Log4netLogger(log4net.Core.ILogger logger) + { + Guard.NotNull(logger, nameof(logger)); + + _logger = logger; + } + + public string Name => _logger.Name; + + public bool IsEnabledFor(LogLevel level) + { + return _logger.IsEnabledFor(ConvertLevel(level)); + } + + public void Log(LogLevel level, Exception exception, string message, object[] args) + { + var logLevel = ConvertLevel(level); + + if (message == null && exception != null) + { + message = exception.Message; + } + + object messageObj = message; + + if (args != null && args.Length > 0) + { + messageObj = new SystemStringFormat(CultureInfo.CurrentUICulture, message, args); + } + + var loggingEvent = new LoggingEvent( + _declaringType, + _logger.Repository, + _logger.Name, + logLevel, + messageObj, + exception); + + loggingEvent.Properties["LevelId"] = (int)level; + TryAddExtendedThreadInfo(loggingEvent); + + _logger.Log(loggingEvent); + } + + protected internal void TryAddExtendedThreadInfo(LoggingEvent loggingEvent) + { + var isAppInitialized = EngineContext.Current.IsFullyInitialized; + + // Don't knowingly run into exception + var httpRequest = isAppInitialized ? HttpContext.Current.SafeGetHttpRequest() : null; + + if (httpRequest == null) + { + loggingEvent.Properties["CustomerId"] = DBNull.Value; + loggingEvent.Properties["Url"] = DBNull.Value; + loggingEvent.Properties["Referrer"] = DBNull.Value; + loggingEvent.Properties["HttpMethod"] = DBNull.Value; + loggingEvent.Properties["Ip"] = DBNull.Value; + + return; + } + + var props = _state.GetState(); + + // Load the log4net thread with additional properties if they are available + var threadInfoMissing = !props.ContainsKey("sm:ThreadInfoAdded"); + + if (threadInfoMissing) + { + using (new ActionDisposable(() => props["sm:ThreadInfoAdded"] = true)) + { + if (DataSettings.DatabaseIsInstalled() && isAppInitialized) + { + var container = EngineContext.Current.ContainerManager; + + // CustomerId + if (container.TryResolve(null, out IWorkContext workContext)) + { + try + { + props["CustomerId"] = workContext.CurrentCustomer.Id; + } + catch + { + props["CustomerId"] = DBNull.Value; + } + } + + + // Url & stuff + if (container.TryResolve(null, out IWebHelper webHelper)) + { + try + { + props["Url"] = webHelper.GetThisPageUrl(true); + props["Referrer"] = webHelper.GetUrlReferrer(); + props["HttpMethod"] = httpRequest?.HttpMethod; + props["Ip"] = webHelper.GetCurrentIpAddress(); + } + catch { } + } + } + } + } + + loggingEvent.Properties["CustomerId"] = props.Get("CustomerId") ?? DBNull.Value; + loggingEvent.Properties["Url"] = props.Get("Url") ?? DBNull.Value; + loggingEvent.Properties["Referrer"] = props.Get("Referrer") ?? DBNull.Value; + loggingEvent.Properties["HttpMethod"] = props.Get("HttpMethod") ?? DBNull.Value; + loggingEvent.Properties["Ip"] = props.Get("Ip") ?? DBNull.Value; + } + + private static Level ConvertLevel(LogLevel level) + { + switch (level) + { + case LogLevel.Information: + return Level.Info; + case LogLevel.Warning: + return Level.Warn; + case LogLevel.Error: + return Level.Error; + case LogLevel.Fatal: + return Level.Fatal; + default: + return Level.Debug; + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Logging/log4net/Log4netLoggerFactory.cs b/src/Libraries/SmartStore.Core/Logging/log4net/Log4netLoggerFactory.cs index 6b6f26299d..b949429081 100644 --- a/src/Libraries/SmartStore.Core/Logging/log4net/Log4netLoggerFactory.cs +++ b/src/Libraries/SmartStore.Core/Logging/log4net/Log4netLoggerFactory.cs @@ -1,16 +1,13 @@ using System; using System.Collections.Concurrent; using System.Data; -using System.Diagnostics; using System.IO; using System.Linq; using System.Web.Hosting; using log4net; using log4net.Appender; using log4net.Config; -using log4net.Core; using log4net.Repository; -using log4net.Util; using SmartStore.Core.Data; using SmartStore.Utilities; @@ -22,21 +19,21 @@ public class Log4netLoggerFactory : ILoggerFactory, IRegisteredObject public Log4netLoggerFactory() { - if (HostingEnvironment.IsHosted) - { - var configFile = GetConfigFile(CommonHelper.GetAppSetting("log4net.Config", @"Config\log4net.config")); + if (HostingEnvironment.IsHosted) + { + var configFile = GetConfigFile(CommonHelper.GetAppSetting("log4net.Config", @"Config\log4net.config")); - XmlConfigurator.ConfigureAndWatch(configFile); + XmlConfigurator.ConfigureAndWatch(configFile); - var repository = LogManager.GetRepository(); - repository.ConfigurationChanged += OnConfigurationChanged; - TryConfigureDbAppender(repository); + var repository = LogManager.GetRepository(); + repository.ConfigurationChanged += OnConfigurationChanged; + TryConfigureDbAppender(repository); - HostingEnvironment.RegisterObject(this); - } + HostingEnvironment.RegisterObject(this); + } } - private void OnConfigurationChanged(object sender, EventArgs e) + private void OnConfigurationChanged(object sender, EventArgs e) { _loggerCache.Clear(); TryConfigureDbAppender(sender as ILoggerRepository); @@ -98,33 +95,33 @@ public void FlushAll() } - #region IRegisteredObject - - public void Stop(bool immediate) - { - RemoveEmptyLogFiles(); - HostingEnvironment.UnregisterObject(this); - } - - internal static void RemoveEmptyLogFiles() - { - var fileAppenders = LogManager.GetRepository()?.GetAppenders()?.OfType(); - if (fileAppenders != null) - { - foreach (var appender in fileAppenders) - { - // Delete log file if it's empty - var logFile = new FileInfo(appender.File); - if (logFile.Exists && logFile.Length <= 0) - { - logFile.Delete(); - } - } - } - } - - #endregion - } + #region IRegisteredObject + + public void Stop(bool immediate) + { + RemoveEmptyLogFiles(); + HostingEnvironment.UnregisterObject(this); + } + + internal static void RemoveEmptyLogFiles() + { + var fileAppenders = LogManager.GetRepository()?.GetAppenders()?.OfType(); + if (fileAppenders != null) + { + foreach (var appender in fileAppenders) + { + // Delete log file if it's empty + var logFile = new FileInfo(appender.File); + if (logFile.Exists && logFile.Length <= 0) + { + logFile.Delete(); + } + } + } + } + + #endregion + } //public class DbAppender : AdoNetAppender //{ diff --git a/src/Libraries/SmartStore.Core/Logging/log4net/SmartFileAppender.cs b/src/Libraries/SmartStore.Core/Logging/log4net/SmartFileAppender.cs index e27eab6420..c9f11411f2 100644 --- a/src/Libraries/SmartStore.Core/Logging/log4net/SmartFileAppender.cs +++ b/src/Libraries/SmartStore.Core/Logging/log4net/SmartFileAppender.cs @@ -1,11 +1,10 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using log4net.Appender; using log4net.Util; namespace SmartStore.Core.Logging { - public class SmartFileAppender : RollingFileAppender + public class SmartFileAppender : RollingFileAppender { /// /// Dictionary of already known suffixes (based on previous attempts) for a given filename. @@ -21,7 +20,18 @@ public class SmartFileAppender : RollingFileAppender /// Maximum number of suffixes recorded before a cleanup happens to recycle memory. /// private const int MaxSuffixes = 100; - + + /// + /// Prevents empty log files. + /// The sequence from log4Net source is as below: + /// - The first call to OpenFile() is because of ActivateOptions() called from FileAppender's constructor. + /// - When log message is generated, AppenderSkeleton's DoAppend() calls PreAppendCheck() + /// - PreAppendCheck() is overridden in TextWriterAppender, the base of FileAppender. + /// - The overridden PreAppendCheck() calls virtual PrepareWriter if the file is not yet open. + /// - PrepareWriter() of FileAppender calls SafeOpenFile() which inturn calls OpenFile() + /// + private bool _isFirstCall = true; + /// /// Opens the log file adding an incremental suffix to the filename if required due to an opening failure (usually, locking). /// @@ -29,6 +39,12 @@ public class SmartFileAppender : RollingFileAppender /// Boolean flag indicating weather the log file should be appended if it already exists. protected override void OpenFile(string fileName, bool append) { + if (_isFirstCall) + { + _isFirstCall = false; + return; + } + lock (this) { bool fileOpened = false; diff --git a/src/Libraries/SmartStore.Core/Money.cs b/src/Libraries/SmartStore.Core/Money.cs index 33244b92ef..88fe208a27 100644 --- a/src/Libraries/SmartStore.Core/Money.cs +++ b/src/Libraries/SmartStore.Core/Money.cs @@ -1,140 +1,124 @@ using System; -using System.Collections.Generic; using System.Globalization; using System.Runtime.Serialization; using SmartStore.Core.Domain.Directory; namespace SmartStore { - public class Money : IConvertible, IFormattable, IComparable, IComparable, IEquatable - { - public Money(Currency currency) - : this(0m, currency) - { - } - - public Money(float amount, Currency currency) - : this((decimal)amount, currency, false) - { - } - - public Money(double amount, Currency currency) - : this((decimal)amount, currency, false) - { - } - - public Money(decimal amount, Currency currency) - : this(amount, currency, false) - { - } - - public Money(decimal amount, Currency currency, bool hideCurrency) - { - Guard.NotNull(currency, nameof(currency)); - - Amount = amount; - Currency = currency; - HideCurrency = hideCurrency; - } - - [IgnoreDataMember] - public bool HideCurrency - { - get; - internal set; - } - - [IgnoreDataMember] - public Currency Currency - { - get; - internal set; - } - - /// - /// Gets the number of decimal digits for the associated currency. - /// - public int DecimalDigits - { - get => string.Equals(Currency?.CurrencyCode, "btc", StringComparison.OrdinalIgnoreCase) ? 8 : Currency.NumberFormat.CurrencyDecimalDigits; - } - - /// - /// The internal unrounded raw amount - /// - public decimal Amount - { - get; - set; - } - - /// - /// Rounds the amount to the number of significant decimal digits - /// of the associated currency using MidpointRounding.AwayFromZero. - /// - public decimal RoundedAmount - { - get - { - return decimal.Round(Amount, DecimalDigits, MidpointRounding.AwayFromZero); - } - } - - /// - /// Truncates the amount to the number of significant decimal digits - /// of the associated currency. - /// - public decimal TruncatedAmount - { - get => (decimal)((long)Math.Truncate(Amount * DecimalDigits)) / DecimalDigits; - } - - /// - /// The formatted amount - /// - public string Formatted - { - get => ToString(true, false); - } - - private static void GuardCurrenciesAreEqual(Money a, Money b) - { - if (a.Currency != b.Currency) - throw new InvalidOperationException("Cannot operate on money values with different currencies."); - } - - #region Compare - - public override int GetHashCode() - { - if (Amount == 0) - return 0; - - return Amount.GetHashCode() ^ Currency.GetHashCode(); - } - - public int CompareTo(Money other) - { - return ((IComparable)this).CompareTo(other); - } - - int IComparable.CompareTo(object obj) - { - if (obj == null || !(obj is Money)) - return 1; - - Money other = (Money)obj; - - if (this.Amount == other.Amount) - return 0; - if (this.Amount < other.Amount) - return -1; - - return 1; - } - - public override bool Equals(object obj) - { + public class Money : IConvertible, IFormattable, IComparable, IComparable, IEquatable + { + public Money(Currency currency) + : this(0m, currency) + { + } + + public Money(float amount, Currency currency) + : this((decimal)amount, currency, false) + { + } + + public Money(double amount, Currency currency) + : this((decimal)amount, currency, false) + { + } + + public Money(decimal amount, Currency currency) + : this(amount, currency, false) + { + } + + public Money(decimal amount, Currency currency, bool hideCurrency) + { + Guard.NotNull(currency, nameof(currency)); + + Amount = amount; + Currency = currency; + HideCurrency = hideCurrency; + } + + [IgnoreDataMember] + public bool HideCurrency + { + get; + internal set; + } + + [IgnoreDataMember] + public Currency Currency + { + get; + internal set; + } + + /// + /// Gets the number of decimal digits for the associated currency. + /// + public int DecimalDigits => string.Equals(Currency?.CurrencyCode, "btc", StringComparison.OrdinalIgnoreCase) ? 8 : Currency.NumberFormat.CurrencyDecimalDigits; + + /// + /// The internal unrounded raw amount + /// + public decimal Amount + { + get; + set; + } + + /// + /// Rounds the amount to the number of significant decimal digits + /// of the associated currency using MidpointRounding.AwayFromZero. + /// + public decimal RoundedAmount => decimal.Round(Amount, DecimalDigits); + + /// + /// Truncates the amount to the number of significant decimal digits + /// of the associated currency. + /// + public decimal TruncatedAmount => (decimal)((long)Math.Truncate(Amount * DecimalDigits)) / DecimalDigits; + + /// + /// The formatted amount + /// + public string Formatted => ToString(true, false); + + private static void GuardCurrenciesAreEqual(Money a, Money b) + { + if (a.Currency != b.Currency) + throw new InvalidOperationException("Cannot operate on money values with different currencies."); + } + + #region Compare + + public override int GetHashCode() + { + if (Amount == 0) + return 0; + + return Amount.GetHashCode() ^ Currency.GetHashCode(); + } + + public int CompareTo(Money other) + { + return ((IComparable)this).CompareTo(other); + } + + int IComparable.CompareTo(object obj) + { + if (obj == null || !(obj is Money)) + return 1; + + Money other = (Money)obj; + + if (this.Amount == other.Amount) + return 0; + if (this.Amount < other.Amount) + return -1; + + return 1; + } + + public override bool Equals(object obj) + { // Prevent stack overflow. if (obj != null) { @@ -144,340 +128,613 @@ public override bool Equals(object obj) return false; } - bool IEquatable.Equals(Money other) - { - if (other == null) - return false; - - if (ReferenceEquals(this, other)) - return true; - - if (other.Amount == 0 && this.Amount == 0) - return true; - - return other.Amount == this.Amount && other.Currency == this.Currency; - } - - public static bool operator ==(Money a, Money b) => a.Equals(b); - public static bool operator !=(Money a, Money b) => !a.Equals(b); - - public static bool operator >(Money a, Money b) - { - GuardCurrenciesAreEqual(a, b); - return a.Amount > b.Amount; - } - - public static bool operator <(Money a, Money b) - { - GuardCurrenciesAreEqual(a, b); - return a.Amount < b.Amount; - } - - public static bool operator <=(Money a, Money b) - { - GuardCurrenciesAreEqual(a, b); - return a.Amount <= b.Amount; - - } - - public static bool operator >=(Money a, Money b) - { - GuardCurrenciesAreEqual(a, b); - return a.Amount >= b.Amount; - - } - - public static bool operator ==(Money a, int b) => a.Amount == b; - public static bool operator !=(Money a, int b) => a.Amount != b; - public static bool operator >(Money a, int b) => a.Amount > b; - public static bool operator <(Money a, int b) => a.Amount < b; - public static bool operator <=(Money a, int b) => a.Amount <= b; - public static bool operator >=(Money a, int b) => a.Amount >= b; - - public static bool operator ==(Money a, float b) => a.Amount == (decimal)b; - public static bool operator !=(Money a, float b) => a.Amount != (decimal)b; - public static bool operator >(Money a, float b) => a.Amount > (decimal)b; - public static bool operator <(Money a, float b) => a.Amount < (decimal)b; - public static bool operator <=(Money a, float b) => a.Amount <= (decimal)b; - public static bool operator >=(Money a, float b) => a.Amount >= (decimal)b; - - public static bool operator ==(Money a, double b) => a.Amount == (decimal)b; - public static bool operator !=(Money a, double b) => a.Amount != (decimal)b; - public static bool operator >(Money a, double b) => a.Amount > (decimal)b; - public static bool operator <(Money a, double b) => a.Amount < (decimal)b; - public static bool operator <=(Money a, double b) => a.Amount <= (decimal)b; - public static bool operator >=(Money a, double b) => a.Amount >= (decimal)b; - - public static bool operator ==(Money a, decimal b) => a.Amount == b; - public static bool operator !=(Money a, decimal b) => a.Amount != b; - public static bool operator >(Money a, decimal b) => a.Amount > b; - public static bool operator <(Money a, decimal b) => a.Amount < b; - public static bool operator <=(Money a, decimal b) => a.Amount <= b; - public static bool operator >=(Money a, decimal b) => a.Amount >= b; - - #endregion - - #region Format - - string IFormattable.ToString(string format, IFormatProvider formatProvider) - { - return this.ToString(!HideCurrency, false); - } - - string IConvertible.ToString(IFormatProvider provider) - { - return this.ToString(!HideCurrency, false); - } - - public override string ToString() - { - return this.ToString(!HideCurrency, false); - } - - public string ToString(bool showCurrency) - { - return this.ToString(showCurrency, false); - } - - public string ToString(bool showCurrency, bool useISOCodeAsSymbol) - { - var fmt = Currency.NumberFormat; - - if (Currency.CustomFormatting.HasValue()) - { - return RoundedAmount.ToString(Currency.CustomFormatting, fmt); - } - else - { - if (!showCurrency || useISOCodeAsSymbol) - { - fmt = (NumberFormatInfo)Currency.NumberFormat.Clone(); - fmt.CurrencySymbol = !showCurrency ? "" : Currency.CurrencyCode; - } - - return RoundedAmount.ToString("C", fmt); - } - } - - #endregion - - #region Convert - - // For truthy checks in templating - public static explicit operator bool(Money money) => money.Amount != 0; - public static explicit operator string(Money money) => money.ToString(true, false); - public static explicit operator byte(Money money) => System.Convert.ToByte(money.Amount); - public static explicit operator decimal(Money money) => money.Amount; - public static explicit operator double(Money money) => System.Convert.ToDouble(money.Amount); - public static explicit operator float(Money money) => System.Convert.ToSingle(money.Amount); - public static explicit operator int(Money money) => System.Convert.ToInt32(money.Amount); - public static explicit operator long(Money money) => System.Convert.ToInt64(money.Amount); - public static explicit operator sbyte(Money money) => System.Convert.ToSByte(money.Amount); - public static explicit operator short(Money money) => System.Convert.ToInt16(money.Amount); - public static explicit operator ushort(Money money) => System.Convert.ToUInt16(money.Amount); - public static explicit operator uint(Money money) => System.Convert.ToUInt32(money.Amount); - public static explicit operator ulong(Money money) => System.Convert.ToUInt64(money.Amount); - - TypeCode IConvertible.GetTypeCode() => TypeCode.Decimal; - object IConvertible.ToType(Type conversionType, IFormatProvider provider) => System.Convert.ChangeType(this.Amount, conversionType, provider); - bool IConvertible.ToBoolean(IFormatProvider provider) => Amount != 0; - char IConvertible.ToChar(IFormatProvider provider) => throw Error.InvalidCast(typeof(Money), typeof(char)); - DateTime IConvertible.ToDateTime(IFormatProvider provider) => throw Error.InvalidCast(typeof(Money), typeof(DateTime)); - byte IConvertible.ToByte(IFormatProvider provider) => (byte)this.Amount; - decimal IConvertible.ToDecimal(IFormatProvider provider) => this.Amount; - double IConvertible.ToDouble(IFormatProvider provider) => (double)this.Amount; - short IConvertible.ToInt16(IFormatProvider provider) => (short)this.Amount; - int IConvertible.ToInt32(IFormatProvider provider) => (int)this.Amount; - long IConvertible.ToInt64(IFormatProvider provider) => (long)this.Amount; - sbyte IConvertible.ToSByte(IFormatProvider provider) => (sbyte)this.Amount; - float IConvertible.ToSingle(IFormatProvider provider) => (float)this.Amount; - ushort IConvertible.ToUInt16(IFormatProvider provider) => (ushort)this.Amount; - uint IConvertible.ToUInt32(IFormatProvider provider) => (uint)this.Amount; - ulong IConvertible.ToUInt64(IFormatProvider provider) => (ulong)this.Amount; - - #endregion - - #region Add - - public static Money operator ++(Money a) - { - a.Amount++; - return a; - } - - public static Money operator +(Money a, Money b) - { - GuardCurrenciesAreEqual(a, b); - return new Money(a.Amount + b.Amount, a.Currency); - } - - public static Money operator +(Money a, int b) => a + (decimal)b; - public static Money operator +(Money a, float b) => a + (decimal)b; - public static Money operator +(Money a, double b) => a + (decimal)b; - public static Money operator +(Money a, decimal b) => new Money(a.Amount + b, a.Currency); - - #endregion - - #region Substract - - public static Money operator --(Money a) - { - a.Amount--; - return a; - } - - public static Money operator -(Money a, Money b) - { - GuardCurrenciesAreEqual(a, b); - return new Money(a.Amount - b.Amount, a.Currency); - } - - public static Money operator -(Money a, int b) => a + (decimal)b; - public static Money operator -(Money a, float b) => a + (decimal)b; - public static Money operator -(Money a, double b) => a + (decimal)b; - public static Money operator -(Money a, decimal b) => new Money(a.Amount - b, a.Currency); - - #endregion - - #region Multiply - - public static Money operator *(Money a, Money b) - { - GuardCurrenciesAreEqual(a, b); - return new Money(a.Amount - b.Amount, a.Currency); - } - - public static Money operator *(Money a, int b) => a * (decimal)b; - public static Money operator *(Money a, float b) => a * (decimal)b; - public static Money operator *(Money a, double b) => a * (decimal)b; - public static Money operator *(Money a, decimal b) => new Money(a.Amount * b, a.Currency); - - #endregion - - #region Divide - - public static Money operator /(Money a, Money b) - { - GuardCurrenciesAreEqual(a, b); - return new Money(a.Amount / b.Amount, a.Currency); - } - - public static Money operator /(Money a, int b) => a / (decimal)b; - public static Money operator /(Money a, float b) => a / (decimal)b; - public static Money operator /(Money a, double b) => a / (decimal)b; - public static Money operator /(Money a, decimal b) => new Money(a.Amount / b, a.Currency); - - #endregion - - #region Exchange & Math - - ///// - ///// Rounds the amount if enabled for the currency or if is true - ///// - ///// Round also if disabled for the currency - ///// A new instance with the rounded amount - //public Money Round(bool force = false) - //{ - - //} - - public Money ConvertTo(Currency toCurrency) - { - if (Currency == toCurrency) - return this; - - return new Money((Amount * Currency.Rate) / toCurrency.Rate, toCurrency); - } - - /// - /// Evenly distributes the amount over n parts, resolving remainders that occur due to rounding - /// errors, thereby garuanteeing the postcondition: result->sum(r|r.amount) = this.amount and - /// x elements in result are greater than at least one of the other elements, where x = amount mod n. - /// - /// Number of parts over which the amount is to be distibuted. - /// Array with distributed Money amounts. - public Money[] Allocate(int n) - { - var cents = Math.Pow(10, DecimalDigits); - var lowResult = ((long)Math.Truncate((double)Amount / n * cents)) / cents; - var highResult = lowResult + 1.0d / cents; - var results = new Money[n]; - var remainder = (int)(((double)Amount * cents) % n); - - for (var i = 0; i < remainder; i++) - results[i] = new Money((decimal)highResult, Currency); - - for (var i = remainder; i < n; i++) - results[i] = new Money((decimal)lowResult, Currency); - - return results; - } - - /// - /// Gets the ratio of one money to another. - /// - /// The numerator of the operation. - /// The denominator of the operation. - /// A decimal from 0.0 to 1.0 of the ratio between the two money values. - public static decimal GetRatio(Money numerator, Money denominator) - { - if (numerator == 0) - return 0; - - if (denominator == 0) - throw new DivideByZeroException("Attempted to divide by zero!"); - - GuardCurrenciesAreEqual(numerator, denominator); - - return numerator.Amount / denominator.Amount; - } - - /// - /// Gets the smallest money, given the two values. - /// - /// The first money to compare. - /// The second money to compare. - /// The smallest money value of the arguments. - public static Money Min(Money a, Money b) - { - GuardCurrenciesAreEqual(a, b); - - if (a == b) - return a; - else if (a > b) - return b; - else - return a; - } - - /// - /// Gets the largest money, given the two values. - /// - /// The first money to compare. - /// The second money to compare. - /// The largest money value of the arguments. - public static Money Max(Money a, Money b) - { - GuardCurrenciesAreEqual(a, b); - - if (a == b) - return a; - else if (a > b) - return a; - else - return b; - } - - /// - /// Gets the absolute value of the . - /// - /// The value of money to convert. - /// The money value as an absolute value. - public static Money Abs(Money value) - { - return new Money(Math.Abs(value.Amount), value.Currency); - } - - #endregion - } + bool IEquatable.Equals(Money other) + { + if (other == null) + return false; + + if (ReferenceEquals(this, other)) + return true; + + if (other.Amount == 0 && this.Amount == 0) + return true; + + return other.Amount == this.Amount && other.Currency == this.Currency; + } + + public static bool operator ==(Money a, Money b) + { + return a.Equals(b); + } + + public static bool operator !=(Money a, Money b) + { + return !a.Equals(b); + } + + public static bool operator >(Money a, Money b) + { + GuardCurrenciesAreEqual(a, b); + return a.Amount > b.Amount; + } + + public static bool operator <(Money a, Money b) + { + GuardCurrenciesAreEqual(a, b); + return a.Amount < b.Amount; + } + + public static bool operator <=(Money a, Money b) + { + GuardCurrenciesAreEqual(a, b); + return a.Amount <= b.Amount; + + } + + public static bool operator >=(Money a, Money b) + { + GuardCurrenciesAreEqual(a, b); + return a.Amount >= b.Amount; + + } + + public static bool operator ==(Money a, int b) + { + return a.Amount == b; + } + + public static bool operator !=(Money a, int b) + { + return a.Amount != b; + } + + public static bool operator >(Money a, int b) + { + return a.Amount > b; + } + + public static bool operator <(Money a, int b) + { + return a.Amount < b; + } + + public static bool operator <=(Money a, int b) + { + return a.Amount <= b; + } + + public static bool operator >=(Money a, int b) + { + return a.Amount >= b; + } + + public static bool operator ==(Money a, float b) + { + return a.Amount == (decimal)b; + } + + public static bool operator !=(Money a, float b) + { + return a.Amount != (decimal)b; + } + + public static bool operator >(Money a, float b) + { + return a.Amount > (decimal)b; + } + + public static bool operator <(Money a, float b) + { + return a.Amount < (decimal)b; + } + + public static bool operator <=(Money a, float b) + { + return a.Amount <= (decimal)b; + } + + public static bool operator >=(Money a, float b) + { + return a.Amount >= (decimal)b; + } + + public static bool operator ==(Money a, double b) + { + return a.Amount == (decimal)b; + } + + public static bool operator !=(Money a, double b) + { + return a.Amount != (decimal)b; + } + + public static bool operator >(Money a, double b) + { + return a.Amount > (decimal)b; + } + + public static bool operator <(Money a, double b) + { + return a.Amount < (decimal)b; + } + + public static bool operator <=(Money a, double b) + { + return a.Amount <= (decimal)b; + } + + public static bool operator >=(Money a, double b) + { + return a.Amount >= (decimal)b; + } + + public static bool operator ==(Money a, decimal b) + { + return a.Amount == b; + } + + public static bool operator !=(Money a, decimal b) + { + return a.Amount != b; + } + + public static bool operator >(Money a, decimal b) + { + return a.Amount > b; + } + + public static bool operator <(Money a, decimal b) + { + return a.Amount < b; + } + + public static bool operator <=(Money a, decimal b) + { + return a.Amount <= b; + } + + public static bool operator >=(Money a, decimal b) + { + return a.Amount >= b; + } + + #endregion + + #region Format + + string IFormattable.ToString(string format, IFormatProvider formatProvider) + { + return this.ToString(!HideCurrency, false); + } + + string IConvertible.ToString(IFormatProvider provider) + { + return this.ToString(!HideCurrency, false); + } + + public override string ToString() + { + return this.ToString(!HideCurrency, false); + } + + public string ToString(bool showCurrency) + { + return this.ToString(showCurrency, false); + } + + public string ToString(bool showCurrency, bool useISOCodeAsSymbol) + { + var fmt = Currency.NumberFormat; + + if (Currency.CustomFormatting.HasValue()) + { + return RoundedAmount.ToString(Currency.CustomFormatting, fmt); + } + else + { + if (!showCurrency || useISOCodeAsSymbol) + { + fmt = (NumberFormatInfo)Currency.NumberFormat.Clone(); + fmt.CurrencySymbol = !showCurrency ? "" : Currency.CurrencyCode; + } + + return RoundedAmount.ToString("C", fmt); + } + } + + #endregion + + #region Convert + + // For truthy checks in templating + public static explicit operator bool(Money money) + { + return money.Amount != 0; + } + + public static explicit operator string(Money money) + { + return money.ToString(true, false); + } + + public static explicit operator byte(Money money) + { + return System.Convert.ToByte(money.Amount); + } + + public static explicit operator decimal(Money money) + { + return money.Amount; + } + + public static explicit operator double(Money money) + { + return System.Convert.ToDouble(money.Amount); + } + + public static explicit operator float(Money money) + { + return System.Convert.ToSingle(money.Amount); + } + + public static explicit operator int(Money money) + { + return System.Convert.ToInt32(money.Amount); + } + + public static explicit operator long(Money money) + { + return System.Convert.ToInt64(money.Amount); + } + + public static explicit operator sbyte(Money money) + { + return System.Convert.ToSByte(money.Amount); + } + + public static explicit operator short(Money money) + { + return System.Convert.ToInt16(money.Amount); + } + + public static explicit operator ushort(Money money) + { + return System.Convert.ToUInt16(money.Amount); + } + + public static explicit operator uint(Money money) + { + return System.Convert.ToUInt32(money.Amount); + } + + public static explicit operator ulong(Money money) + { + return System.Convert.ToUInt64(money.Amount); + } + + TypeCode IConvertible.GetTypeCode() + { + return TypeCode.Decimal; + } + + object IConvertible.ToType(Type conversionType, IFormatProvider provider) + { + return System.Convert.ChangeType(this.Amount, conversionType, provider); + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Amount != 0; + } + + char IConvertible.ToChar(IFormatProvider provider) + { + throw Error.InvalidCast(typeof(Money), typeof(char)); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + throw Error.InvalidCast(typeof(Money), typeof(DateTime)); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return (byte)this.Amount; + } + + decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return this.Amount; + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return (double)this.Amount; + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return (short)this.Amount; + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return (int)this.Amount; + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return (long)this.Amount; + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return (sbyte)this.Amount; + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return (float)this.Amount; + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return (ushort)this.Amount; + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return (uint)this.Amount; + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return (ulong)this.Amount; + } + + #endregion + + #region Add + + public static Money operator ++(Money a) + { + a.Amount++; + return a; + } + + public static Money operator +(Money a, Money b) + { + GuardCurrenciesAreEqual(a, b); + return new Money(a.Amount + b.Amount, a.Currency); + } + + public static Money operator +(Money a, int b) + { + return a + (decimal)b; + } + + public static Money operator +(Money a, float b) + { + return a + (decimal)b; + } + + public static Money operator +(Money a, double b) + { + return a + (decimal)b; + } + + public static Money operator +(Money a, decimal b) + { + return new Money(a.Amount + b, a.Currency); + } + + #endregion + + #region Substract + + public static Money operator --(Money a) + { + a.Amount--; + return a; + } + + public static Money operator -(Money a, Money b) + { + GuardCurrenciesAreEqual(a, b); + return new Money(a.Amount - b.Amount, a.Currency); + } + + public static Money operator -(Money a, int b) + { + return a + (decimal)b; + } + + public static Money operator -(Money a, float b) + { + return a + (decimal)b; + } + + public static Money operator -(Money a, double b) + { + return a + (decimal)b; + } + + public static Money operator -(Money a, decimal b) + { + return new Money(a.Amount - b, a.Currency); + } + + #endregion + + #region Multiply + + public static Money operator *(Money a, Money b) + { + GuardCurrenciesAreEqual(a, b); + return new Money(a.Amount - b.Amount, a.Currency); + } + + public static Money operator *(Money a, int b) + { + return a * (decimal)b; + } + + public static Money operator *(Money a, float b) + { + return a * (decimal)b; + } + + public static Money operator *(Money a, double b) + { + return a * (decimal)b; + } + + public static Money operator *(Money a, decimal b) + { + return new Money(a.Amount * b, a.Currency); + } + + #endregion + + #region Divide + + public static Money operator /(Money a, Money b) + { + GuardCurrenciesAreEqual(a, b); + return new Money(a.Amount / b.Amount, a.Currency); + } + + public static Money operator /(Money a, int b) + { + return a / (decimal)b; + } + + public static Money operator /(Money a, float b) + { + return a / (decimal)b; + } + + public static Money operator /(Money a, double b) + { + return a / (decimal)b; + } + + public static Money operator /(Money a, decimal b) + { + return new Money(a.Amount / b, a.Currency); + } + + #endregion + + #region Exchange & Math + + ///// + ///// Rounds the amount if enabled for the currency or if is true + ///// + ///// Round also if disabled for the currency + ///// A new instance with the rounded amount + //public Money Round(bool force = false) + //{ + + //} + + public Money ConvertTo(Currency toCurrency) + { + if (Currency == toCurrency) + return this; + + return new Money((Amount * Currency.Rate) / toCurrency.Rate, toCurrency); + } + + /// + /// Evenly distributes the amount over n parts, resolving remainders that occur due to rounding + /// errors, thereby garuanteeing the postcondition: result->sum(r|r.amount) = this.amount and + /// x elements in result are greater than at least one of the other elements, where x = amount mod n. + /// + /// Number of parts over which the amount is to be distibuted. + /// Array with distributed Money amounts. + public Money[] Allocate(int n) + { + var cents = Math.Pow(10, DecimalDigits); + var lowResult = ((long)Math.Truncate((double)Amount / n * cents)) / cents; + var highResult = lowResult + 1.0d / cents; + var results = new Money[n]; + var remainder = (int)(((double)Amount * cents) % n); + + for (var i = 0; i < remainder; i++) + results[i] = new Money((decimal)highResult, Currency); + + for (var i = remainder; i < n; i++) + results[i] = new Money((decimal)lowResult, Currency); + + return results; + } + + /// + /// Gets the ratio of one money to another. + /// + /// The numerator of the operation. + /// The denominator of the operation. + /// A decimal from 0.0 to 1.0 of the ratio between the two money values. + public static decimal GetRatio(Money numerator, Money denominator) + { + if (numerator == 0) + return 0; + + if (denominator == 0) + throw new DivideByZeroException("Attempted to divide by zero!"); + + GuardCurrenciesAreEqual(numerator, denominator); + + return numerator.Amount / denominator.Amount; + } + + /// + /// Gets the smallest money, given the two values. + /// + /// The first money to compare. + /// The second money to compare. + /// The smallest money value of the arguments. + public static Money Min(Money a, Money b) + { + GuardCurrenciesAreEqual(a, b); + + if (a == b) + return a; + else if (a > b) + return b; + else + return a; + } + + /// + /// Gets the largest money, given the two values. + /// + /// The first money to compare. + /// The second money to compare. + /// The largest money value of the arguments. + public static Money Max(Money a, Money b) + { + GuardCurrenciesAreEqual(a, b); + + if (a == b) + return a; + else if (a > b) + return a; + else + return b; + } + + /// + /// Gets the absolute value of the . + /// + /// The value of money to convert. + /// The money value as an absolute value. + public static Money Abs(Money value) + { + return new Money(Math.Abs(value.Amount), value.Currency); + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/ExtensionDescriptor.cs b/src/Libraries/SmartStore.Core/Packaging/ExtensionDescriptor.cs index 2d014ace46..f9c6c83a14 100644 --- a/src/Libraries/SmartStore.Core/Packaging/ExtensionDescriptor.cs +++ b/src/Libraries/SmartStore.Core/Packaging/ExtensionDescriptor.cs @@ -1,36 +1,34 @@ using System; -using System.Collections.Generic; -using System.Linq; namespace SmartStore.Core.Packaging { - public class ExtensionDescriptor - { - /// - /// Virtual path base, "~/Themes" or "~/Plugins" - /// - public string Location { get; set; } + public class ExtensionDescriptor + { + /// + /// Virtual path base, "~/Themes" or "~/Plugins" + /// + public string Location { get; set; } - /// - /// Folder name under virtual path base - /// - public string Id { get; set; } + /// + /// Folder name under virtual path base + /// + public string Id { get; set; } - /// - /// The extension type: Plugin | Theme - /// - public string ExtensionType { get; set; } + /// + /// The extension type: Plugin | Theme + /// + public string ExtensionType { get; set; } - // extension metadata - public string Name { get; set; } - public string Path { get; set; } - public string Description { get; set; } - public Version Version { get; set; } - public Version MinAppVersion { get; set; } - public string Author { get; set; } - public string WebSite { get; set; } - public string Tags { get; set; } - } + // extension metadata + public string Name { get; set; } + public string Path { get; set; } + public string Description { get; set; } + public Version Version { get; set; } + public Version MinAppVersion { get; set; } + public string Author { get; set; } + public string WebSite { get; set; } + public string Tags { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/FolderUpdater.cs b/src/Libraries/SmartStore.Core/Packaging/FolderUpdater.cs index d02cc71cce..988efe48c4 100644 --- a/src/Libraries/SmartStore.Core/Packaging/FolderUpdater.cs +++ b/src/Libraries/SmartStore.Core/Packaging/FolderUpdater.cs @@ -1,115 +1,114 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.IO; +using System.Linq; using SmartStore.Core.Logging; using SmartStore.Utilities; namespace SmartStore.Core.Packaging { - public interface IFolderUpdater - { - void Backup(DirectoryInfo existingFolder, DirectoryInfo backupfolder, params string[] ignorePatterns); - void Restore(DirectoryInfo backupfolder, DirectoryInfo existingFolder); - } - - [SuppressMessage("ReSharper", "NotAccessedField.Local")] - public class FolderUpdater : IFolderUpdater - { - public class FolderContent - { - public DirectoryInfo Folder { get; set; } - public IEnumerable Files { get; set; } - } - - private readonly ILogger _logger; - - public FolderUpdater(ILogger logger) - { - _logger = logger; - } - - public void Backup(DirectoryInfo existingFolder, DirectoryInfo backupfolder, params string[] ignorePatterns) - { - var ignores = ignorePatterns.Select(x => new Wildcard(x)); - CopyFolder(GetFolderContent(existingFolder, ignores), backupfolder); - } - - public void Restore(DirectoryInfo backupfolder, DirectoryInfo existingFolder) - { - CopyFolder(GetFolderContent(backupfolder, Enumerable.Empty()), existingFolder); - } - - private void CopyFolder(FolderContent source, DirectoryInfo dest) - { - foreach (var file in source.Files) - { - CopyFile(source.Folder, file, dest); - } - } - - private void CopyFile(DirectoryInfo sourceFolder, string fileName, DirectoryInfo destinationFolder) - { - var sourceFile = new FileInfo(Path.Combine(sourceFolder.FullName, fileName)); - var destFile = new FileInfo(Path.Combine(destinationFolder.FullName, fileName)); - - // If destination file exist, overwrite only if changed - if (destFile.Exists) - { - if (sourceFile.Length == destFile.Length) - { - var source = File.ReadAllBytes(sourceFile.FullName); - var dest = File.ReadAllBytes(destFile.FullName); - if (source.SequenceEqual(dest)) - { - return; - } - } - } - - // Create destination directory - if (!destFile.Directory.Exists) - { - destFile.Directory.Create(); - } - - File.Copy(sourceFile.FullName, destFile.FullName, true); - } - - private FolderContent GetFolderContent(DirectoryInfo folder, IEnumerable ignores) - { - var files = new List(); - GetFolderContent(folder, "", files, ignores); - return new FolderContent { Folder = folder, Files = files }; - } - - [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] - private void GetFolderContent(DirectoryInfo folder, string prefix, List files, IEnumerable ignores) - { - if (!folder.Exists) - return; - - if (ignores.Any(w => w.IsMatch(prefix))) - return; - - foreach (var file in folder.GetFiles()) - { - var path = Path.Combine(prefix, file.Name); - var ignore = ignores.Any(w => w.IsMatch(path)); - if (!ignore) - { - files.Add(path); - } - } - - foreach (var child in folder.GetDirectories()) - { - GetFolderContent(child, Path.Combine(prefix, child.Name), files, ignores); - } - } - - } + public interface IFolderUpdater + { + void Backup(DirectoryInfo existingFolder, DirectoryInfo backupfolder, params string[] ignorePatterns); + void Restore(DirectoryInfo backupfolder, DirectoryInfo existingFolder); + } + + [SuppressMessage("ReSharper", "NotAccessedField.Local")] + public class FolderUpdater : IFolderUpdater + { + public class FolderContent + { + public DirectoryInfo Folder { get; set; } + public IEnumerable Files { get; set; } + } + + private readonly ILogger _logger; + + public FolderUpdater(ILogger logger) + { + _logger = logger; + } + + public void Backup(DirectoryInfo existingFolder, DirectoryInfo backupfolder, params string[] ignorePatterns) + { + var ignores = ignorePatterns.Select(x => new Wildcard(x)); + CopyFolder(GetFolderContent(existingFolder, ignores), backupfolder); + } + + public void Restore(DirectoryInfo backupfolder, DirectoryInfo existingFolder) + { + CopyFolder(GetFolderContent(backupfolder, Enumerable.Empty()), existingFolder); + } + + private void CopyFolder(FolderContent source, DirectoryInfo dest) + { + foreach (var file in source.Files) + { + CopyFile(source.Folder, file, dest); + } + } + + private void CopyFile(DirectoryInfo sourceFolder, string fileName, DirectoryInfo destinationFolder) + { + var sourceFile = new FileInfo(Path.Combine(sourceFolder.FullName, fileName)); + var destFile = new FileInfo(Path.Combine(destinationFolder.FullName, fileName)); + + // If destination file exist, overwrite only if changed + if (destFile.Exists) + { + if (sourceFile.Length == destFile.Length) + { + var source = File.ReadAllBytes(sourceFile.FullName); + var dest = File.ReadAllBytes(destFile.FullName); + if (source.SequenceEqual(dest)) + { + return; + } + } + } + + // Create destination directory + if (!destFile.Directory.Exists) + { + destFile.Directory.Create(); + } + + File.Copy(sourceFile.FullName, destFile.FullName, true); + } + + private FolderContent GetFolderContent(DirectoryInfo folder, IEnumerable ignores) + { + var files = new List(); + GetFolderContent(folder, "", files, ignores); + return new FolderContent { Folder = folder, Files = files }; + } + + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + private void GetFolderContent(DirectoryInfo folder, string prefix, List files, IEnumerable ignores) + { + if (!folder.Exists) + return; + + if (ignores.Any(w => w.IsMatch(prefix))) + return; + + foreach (var file in folder.GetFiles()) + { + var path = Path.Combine(prefix, file.Name); + var ignore = ignores.Any(w => w.IsMatch(path)); + if (!ignore) + { + files.Add(path); + } + } + + foreach (var child in folder.GetDirectories()) + { + GetFolderContent(child, Path.Combine(prefix, child.Name), files, ignores); + } + } + + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/IPackageBuilder.cs b/src/Libraries/SmartStore.Core/Packaging/IPackageBuilder.cs index ed9f9fcde6..259ca2e6ae 100644 --- a/src/Libraries/SmartStore.Core/Packaging/IPackageBuilder.cs +++ b/src/Libraries/SmartStore.Core/Packaging/IPackageBuilder.cs @@ -1,13 +1,12 @@ -using System; -using System.IO; -using SmartStore.Core.Themes; +using System.IO; using SmartStore.Core.Plugins; +using SmartStore.Core.Themes; namespace SmartStore.Core.Packaging { - public interface IPackageBuilder - { - Stream BuildPackage(PluginDescriptor pluginDescriptor); - Stream BuildPackage(ThemeManifest themeManifest); - } + public interface IPackageBuilder + { + Stream BuildPackage(PluginDescriptor pluginDescriptor); + Stream BuildPackage(ThemeManifest themeManifest); + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/IPackageInstaller.cs b/src/Libraries/SmartStore.Core/Packaging/IPackageInstaller.cs index 44005d3956..187858ab75 100644 --- a/src/Libraries/SmartStore.Core/Packaging/IPackageInstaller.cs +++ b/src/Libraries/SmartStore.Core/Packaging/IPackageInstaller.cs @@ -1,13 +1,11 @@ -using System; -using System.IO; -using NuGet; +using System.IO; namespace SmartStore.Core.Packaging { - public interface IPackageInstaller - { - //PackageInfo Install(string packageId, string version, string location, string applicationFolder); - PackageInfo Install(Stream packageStream, string location, string applicationPath); - void Uninstall(string packageId, string applicationFolder); - } + public interface IPackageInstaller + { + //PackageInfo Install(string packageId, string version, string location, string applicationFolder); + PackageInfo Install(Stream packageStream, string location, string applicationPath); + void Uninstall(string packageId, string applicationFolder); + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/IPackageManager.cs b/src/Libraries/SmartStore.Core/Packaging/IPackageManager.cs index 2478640a74..c497d2be95 100644 --- a/src/Libraries/SmartStore.Core/Packaging/IPackageManager.cs +++ b/src/Libraries/SmartStore.Core/Packaging/IPackageManager.cs @@ -1,15 +1,13 @@ -using System; -using System.IO; -using NuGet; +using System.IO; namespace SmartStore.Core.Packaging { - public interface IPackageManager - { - PackageInfo Install(Stream packageStream, string location, string applicationPath); - void Uninstall(string packageId, string applicationPath); + public interface IPackageManager + { + PackageInfo Install(Stream packageStream, string location, string applicationPath); + void Uninstall(string packageId, string applicationPath); - PackagingResult BuildPluginPackage(string pluginName); - PackagingResult BuildThemePackage(string themeName); - } -} + PackagingResult BuildPluginPackage(string pluginName); + PackagingResult BuildThemePackage(string themeName); + } +} diff --git a/src/Libraries/SmartStore.Core/Packaging/NuGet/ExtensionReferenceRepository.cs b/src/Libraries/SmartStore.Core/Packaging/NuGet/ExtensionReferenceRepository.cs index 8a06699b59..644b6b15a7 100644 --- a/src/Libraries/SmartStore.Core/Packaging/NuGet/ExtensionReferenceRepository.cs +++ b/src/Libraries/SmartStore.Core/Packaging/NuGet/ExtensionReferenceRepository.cs @@ -1,107 +1,100 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; +using NuGet; using SmartStore.Core.Plugins; using SmartStore.Core.Themes; -using NuGet; namespace SmartStore.Core.Packaging { - internal abstract class ExtensionReferenceRepository : PackageRepositoryBase - { - - public ExtensionReferenceRepository(IProjectSystem project, IPackageRepository sourceRepository) - { - Guard.NotNull(project, nameof(project)); - Guard.NotNull(sourceRepository, nameof(sourceRepository)); - - Project = project; - SourceRepository = sourceRepository; - } - - public IProjectSystem Project - { - get; - set; - } - - public IPackageRepository SourceRepository - { - get; - set; - } - - public override void AddPackage(IPackage package) { } - - public override void RemovePackage(IPackage package) { } - - - public override string Source - { - get { return Project.Root; } - } - - public override bool SupportsPrereleasePackages - { - get { return true; } - } - } - - /// - /// This repository implementation informs about what plugin packages are already installed. - /// - internal class PluginReferenceRepository : ExtensionReferenceRepository - { - private readonly IList _descriptors; - - public PluginReferenceRepository(IProjectSystem project, IPackageRepository sourceRepository, IPluginFinder pluginFinder) - : base(project, sourceRepository) - { - _descriptors = pluginFinder.GetPluginDescriptors().ToList(); - } - - public override IQueryable GetPackages() - { - IEnumerable repositoryPackages = SourceRepository.GetPackages().ToList(); - IEnumerable packages = from plugin in _descriptors - let id = PackagingUtils.BuildPackageId(plugin.SystemName, "Plugin") - let version = plugin.Version != null ? new SemanticVersion(plugin.Version) : null - let package = repositoryPackages.FirstOrDefault(p => p.Id == id && (version == null || p.Version == version)) - where package != null - select package; - - return packages.AsQueryable(); - } - - } - - /// - /// This repository implementation informs about what theme packages are already installed. - /// - internal class ThemeReferenceRepository : ExtensionReferenceRepository - { - private readonly ICollection _themeManifests; - - public ThemeReferenceRepository(IProjectSystem project, IPackageRepository sourceRepository, IThemeRegistry themeRegistry) - : base(project, sourceRepository) - { - _themeManifests = themeRegistry.GetThemeManifests(true); - } - - public override IQueryable GetPackages() - { - IEnumerable repositoryPackages = SourceRepository.GetPackages().ToList(); - IEnumerable packages = from theme in _themeManifests - let id = PackagingUtils.BuildPackageId(theme.ThemeName, "Theme") - let version = theme.Version != null ? new SemanticVersion(theme.Version) : null - let package = repositoryPackages.FirstOrDefault(p => p.Id == id && (version == null || p.Version == version)) - where package != null - select package; - - return packages.AsQueryable(); - } - - } + internal abstract class ExtensionReferenceRepository : PackageRepositoryBase + { + + public ExtensionReferenceRepository(IProjectSystem project, IPackageRepository sourceRepository) + { + Guard.NotNull(project, nameof(project)); + Guard.NotNull(sourceRepository, nameof(sourceRepository)); + + Project = project; + SourceRepository = sourceRepository; + } + + public IProjectSystem Project + { + get; + set; + } + + public IPackageRepository SourceRepository + { + get; + set; + } + + public override void AddPackage(IPackage package) { } + + public override void RemovePackage(IPackage package) { } + + + public override string Source => Project.Root; + + public override bool SupportsPrereleasePackages => true; + } + + /// + /// This repository implementation informs about what plugin packages are already installed. + /// + internal class PluginReferenceRepository : ExtensionReferenceRepository + { + private readonly IList _descriptors; + + public PluginReferenceRepository(IProjectSystem project, IPackageRepository sourceRepository, IPluginFinder pluginFinder) + : base(project, sourceRepository) + { + _descriptors = pluginFinder.GetPluginDescriptors().ToList(); + } + + public override IQueryable GetPackages() + { + IEnumerable repositoryPackages = SourceRepository.GetPackages().ToList(); + IEnumerable packages = from plugin in _descriptors + let id = PackagingUtils.BuildPackageId(plugin.SystemName, "Plugin") + let version = plugin.Version != null ? new SemanticVersion(plugin.Version) : null + let package = repositoryPackages.FirstOrDefault(p => p.Id == id && (version == null || p.Version == version)) + where package != null + select package; + + return packages.AsQueryable(); + } + + } + + /// + /// This repository implementation informs about what theme packages are already installed. + /// + internal class ThemeReferenceRepository : ExtensionReferenceRepository + { + private readonly ICollection _themeManifests; + + public ThemeReferenceRepository(IProjectSystem project, IPackageRepository sourceRepository, IThemeRegistry themeRegistry) + : base(project, sourceRepository) + { + _themeManifests = themeRegistry.GetThemeManifests(true); + } + + public override IQueryable GetPackages() + { + IEnumerable repositoryPackages = SourceRepository.GetPackages().ToList(); + IEnumerable packages = from theme in _themeManifests + let id = PackagingUtils.BuildPackageId(theme.ThemeName, "Theme") + let version = theme.Version != null ? new SemanticVersion(theme.Version) : null + let package = repositoryPackages.FirstOrDefault(p => p.Id == id && (version == null || p.Version == version)) + where package != null + select package; + + return packages.AsQueryable(); + } + + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/NuGet/FileBasedProjectSystem.cs b/src/Libraries/SmartStore.Core/Packaging/NuGet/FileBasedProjectSystem.cs index a32cae4e91..22e5a3c580 100644 --- a/src/Libraries/SmartStore.Core/Packaging/NuGet/FileBasedProjectSystem.cs +++ b/src/Libraries/SmartStore.Core/Packaging/NuGet/FileBasedProjectSystem.cs @@ -1,97 +1,85 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Reflection; using System.Runtime.Versioning; using NuGet; namespace SmartStore.Core.Packaging { - internal class FileBasedProjectSystem : PhysicalFileSystem, IProjectSystem - { - - public FileBasedProjectSystem(string root) - : base(root) - { - } - - public void AddFrameworkReference(string name) - { - throw new NotSupportedException(); - } - - public void AddImport(string targetPath, ProjectImportLocation location) - { - throw new NotSupportedException(); - } - - public void AddReference(string referencePath, Stream stream) - { - throw new NotSupportedException(); - } - - public bool FileExistsInProject(string path) - { - return FileExists(path); - } - - public bool IsBindingRedirectSupported - { - get { return false; } - } - - public bool IsSupportedFile(string path) - { - return true; - } - - public string ProjectName - { - get { return Root; } - } - - protected virtual string GetReferencePath(string name) - { - return Path.Combine("bin", name); - } - - public bool ReferenceExists(string name) - { - string path = GetReferencePath(name); - return FileExists(path); - } - - public void RemoveImport(string targetPath) - { - throw new NotSupportedException(); - } - - public void RemoveReference(string name) - { - throw new NotSupportedException(); - } - - public string ResolvePath(string path) - { - return GetFullPath(path); - } - - public FrameworkName TargetFramework - { - get { return VersionUtility.DefaultTargetFramework; } - } - - public dynamic GetPropertyValue(string propertyName) - { - if (propertyName == null) - { - return null; - } - - // Return empty string for the root namespace of this project. - return propertyName.Equals("RootNamespace", StringComparison.OrdinalIgnoreCase) ? String.Empty : null; - } - } + internal class FileBasedProjectSystem : PhysicalFileSystem, IProjectSystem + { + + public FileBasedProjectSystem(string root) + : base(root) + { + } + + public void AddFrameworkReference(string name) + { + throw new NotSupportedException(); + } + + public void AddImport(string targetPath, ProjectImportLocation location) + { + throw new NotSupportedException(); + } + + public void AddReference(string referencePath, Stream stream) + { + throw new NotSupportedException(); + } + + public bool FileExistsInProject(string path) + { + return FileExists(path); + } + + public bool IsBindingRedirectSupported => false; + + public bool IsSupportedFile(string path) + { + return true; + } + + public string ProjectName => Root; + + protected virtual string GetReferencePath(string name) + { + return Path.Combine("bin", name); + } + + public bool ReferenceExists(string name) + { + string path = GetReferencePath(name); + return FileExists(path); + } + + public void RemoveImport(string targetPath) + { + throw new NotSupportedException(); + } + + public void RemoveReference(string name) + { + throw new NotSupportedException(); + } + + public string ResolvePath(string path) + { + return GetFullPath(path); + } + + public FrameworkName TargetFramework => VersionUtility.DefaultTargetFramework; + + public dynamic GetPropertyValue(string propertyName) + { + if (propertyName == null) + { + return null; + } + + // Return empty string for the root namespace of this project. + return propertyName.Equals("RootNamespace", StringComparison.OrdinalIgnoreCase) ? String.Empty : null; + } + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/NuGet/NugetLogger.cs b/src/Libraries/SmartStore.Core/Packaging/NuGet/NugetLogger.cs index f2c4c8a65f..8a2942f601 100644 --- a/src/Libraries/SmartStore.Core/Packaging/NuGet/NugetLogger.cs +++ b/src/Libraries/SmartStore.Core/Packaging/NuGet/NugetLogger.cs @@ -5,37 +5,37 @@ namespace SmartStore.Core.Packaging { - internal class NugetLogger : NuGet.ILogger - { - private readonly Log.ILogger _logger; + internal class NugetLogger : NuGet.ILogger + { + private readonly Log.ILogger _logger; - public NugetLogger(Log.ILogger logger) - { - _logger = logger; - } + public NugetLogger(Log.ILogger logger) + { + _logger = logger; + } - public void Log(MessageLevel level, string message, params object[] args) - { - switch (level) - { - case MessageLevel.Debug: - _logger.Debug(String.Format(message, args)); - break; - case MessageLevel.Error: - _logger.Error(String.Format(message, args)); - break; - case MessageLevel.Info: - _logger.Info(String.Format(message, args)); - break; - case MessageLevel.Warning: - _logger.Warn(String.Format(message, args)); - break; - } - } + public void Log(MessageLevel level, string message, params object[] args) + { + switch (level) + { + case MessageLevel.Debug: + _logger.Debug(String.Format(message, args)); + break; + case MessageLevel.Error: + _logger.Error(String.Format(message, args)); + break; + case MessageLevel.Info: + _logger.Info(String.Format(message, args)); + break; + case MessageLevel.Warning: + _logger.Warn(String.Format(message, args)); + break; + } + } - public FileConflictResolution ResolveFileConflict(string message) - { - return FileConflictResolution.OverwriteAll; - } - } + public FileConflictResolution ResolveFileConflict(string message) + { + return FileConflictResolution.OverwriteAll; + } + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/NuGet/NullSourceRepository.cs b/src/Libraries/SmartStore.Core/Packaging/NuGet/NullSourceRepository.cs index 5d98420378..24d9271ae6 100644 --- a/src/Libraries/SmartStore.Core/Packaging/NuGet/NullSourceRepository.cs +++ b/src/Libraries/SmartStore.Core/Packaging/NuGet/NullSourceRepository.cs @@ -1,34 +1,24 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Linq; using NuGet; -using SmartStore.Utilities; namespace SmartStore.Core.Packaging { - /// - /// This repository implementation fakes a source (remote) repository - /// - internal class NullSourceRepository : PackageRepositoryBase - { - public override IQueryable GetPackages() - { - return Enumerable.Empty().AsQueryable(); - } + /// + /// This repository implementation fakes a source (remote) repository + /// + internal class NullSourceRepository : PackageRepositoryBase + { + public override IQueryable GetPackages() + { + return Enumerable.Empty().AsQueryable(); + } - public override string Source - { - get { return string.Empty; } - } + public override string Source => string.Empty; - public override bool SupportsPrereleasePackages - { - get { return true; } - } + public override bool SupportsPrereleasePackages => true; - public override void AddPackage(IPackage package) { } + public override void AddPackage(IPackage package) { } - public override void RemovePackage(IPackage package) { } - } + public override void RemovePackage(IPackage package) { } + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/PackageBuilder.cs b/src/Libraries/SmartStore.Core/Packaging/PackageBuilder.cs index c156852da3..d3d23fda59 100644 --- a/src/Libraries/SmartStore.Core/Packaging/PackageBuilder.cs +++ b/src/Libraries/SmartStore.Core/Packaging/PackageBuilder.cs @@ -1,201 +1,189 @@ using System; using System.Collections.Generic; -using System.Linq; using System.IO; +using System.Linq; +using System.Runtime.Versioning; +using NuGet; +using SmartStore.Core.IO; using SmartStore.Core.Plugins; using SmartStore.Core.Themes; -using NuGet; using NuGetPackageBuilder = NuGet.PackageBuilder; -using SmartStore.Core.IO; -using System.Runtime.Versioning; namespace SmartStore.Core.Packaging { - public class PackageBuilder : IPackageBuilder - { - private readonly IApplicationEnvironment _env; + public class PackageBuilder : IPackageBuilder + { + private readonly IApplicationEnvironment _env; - public PackageBuilder(IApplicationEnvironment env) - { - this._env = env; - } + public PackageBuilder(IApplicationEnvironment env) + { + this._env = env; + } - private static readonly string[] _ignoredThemeExtensions = new[] { + private static readonly string[] _ignoredThemeExtensions = new[] { "obj", "pdb", "exclude" }; - private static readonly string[] _ignoredThemePaths = new[] { + private static readonly string[] _ignoredThemePaths = new[] { "/obj/" }; - private static bool IgnoreFile(string filePath) - { - return String.IsNullOrEmpty(filePath) || - _ignoredThemePaths.Any(filePath.Contains) || - _ignoredThemeExtensions.Contains(Path.GetExtension(filePath).NullEmpty() ?? ""); - } - - - public Stream BuildPackage(PluginDescriptor pluginDescriptor) - { - return BuildPackage(pluginDescriptor.ConvertToExtensionDescriptor()); - } - - public Stream BuildPackage(ThemeManifest themeManifest) - { - return BuildPackage(themeManifest.ConvertToExtensionDescriptor()); - } - - private Stream BuildPackage(ExtensionDescriptor extensionDescriptor) - { - var context = new BuildContext(); - BeginPackage(context); - try - { - EstablishPaths(context, _env.WebRootFolder, extensionDescriptor.Id, extensionDescriptor.ExtensionType); - SetCoreProperties(context, extensionDescriptor); - EmbedFiles(context); - } - finally - { - EndPackage(context); - } - - if (context.Stream.CanSeek) - { - context.Stream.Seek(0, SeekOrigin.Begin); - } - - return context.Stream; - } - - private static void BeginPackage(BuildContext context) - { - context.Stream = new MemoryStream(); - context.Builder = new NuGetPackageBuilder(); - } - - private static void EndPackage(BuildContext context) - { - context.Builder.Save(context.Stream); - } - - private static void SetCoreProperties(BuildContext context, ExtensionDescriptor extensionDescriptor) - { - context.Builder.Id = PackagingUtils.BuildPackageId(extensionDescriptor.Id, extensionDescriptor.ExtensionType); - context.Builder.Version = new SemanticVersion(extensionDescriptor.Version); - context.Builder.Title = extensionDescriptor.Name ?? extensionDescriptor.Id; - context.Builder.Description = extensionDescriptor.Description.NullEmpty() ?? "No Description"; - context.Builder.Authors.Add(extensionDescriptor.Author ?? "Unknown"); - - if (Uri.IsWellFormedUriString(extensionDescriptor.WebSite, UriKind.Absolute)) - { - context.Builder.ProjectUrl = new Uri(extensionDescriptor.WebSite); - } - } - - private static void EstablishPaths(BuildContext context, IVirtualFolder webRootFolder, string extensionName, string extensionType = "Plugin") - { - context.SourceFolder = webRootFolder; - if (extensionType.IsCaseInsensitiveEqual("theme")) - { - context.SourcePath = "Themes/" + extensionName + "/"; - context.TargetPath = "\\Content\\Themes\\" + extensionName + "\\"; - } - else - { - context.SourcePath = "Plugins/" + extensionName + "/"; - context.TargetPath = "\\Content\\Plugins\\" + extensionName + "\\"; - } - } - - private static void EmbedFiles(BuildContext context) - { - var basePath = context.SourcePath; - foreach (var path in context.SourceFolder.ListFiles(context.SourcePath, true)) - { - // skip ignores files - if (IgnoreFile(path)) - { - continue; - } - // full virtual path given but we need the relative path so it can be put into - // the package that way (the package itself is the logical base path). - // Get it by stripping the basePath off including the slash. - var relativePath = path.Replace(basePath, ""); - EmbedVirtualFile(context, relativePath); - } - } - - private static void EmbedVirtualFile(BuildContext context, string relativePath) - { - var file = new VirtualPackageFile( - context.SourceFolder, - context.SourcePath + relativePath, - context.TargetPath + relativePath); - context.Builder.Files.Add(file); - } - - - #region Nested type: BuildContext - - private class BuildContext - { - public Stream Stream { get; set; } - public NuGetPackageBuilder Builder { get; set; } - - public IVirtualFolder SourceFolder { get; set; } - public string SourcePath { get; set; } - public string TargetPath { get; set; } - } - - #endregion - - #region Nested type: VirtualPackageFile - - private class VirtualPackageFile : IPackageFile - { - private readonly IVirtualFolder _webRootFolder; - private readonly string _relativePath; - private readonly string _packagePath; - - public VirtualPackageFile(IVirtualFolder webRootFolder, string relativePath, string packagePath) - { - _webRootFolder = webRootFolder; - _relativePath = relativePath; - _packagePath = packagePath; - } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Supposed to return an open stream.")] - public Stream GetStream() - { - var stream = new MemoryStream(); - _webRootFolder.CopyFile(_relativePath, stream); - stream.Seek(0, SeekOrigin.Begin); - return stream; - } - - public string Path - { - get { return _packagePath; } - } - - public string EffectivePath - { - get { return _packagePath; } - } - - public FrameworkName TargetFramework - { - get { return null; } - } - - public IEnumerable SupportedFrameworks - { - get { return Enumerable.Empty(); } - } - } - - #endregion - } + private static bool IgnoreFile(string filePath) + { + return String.IsNullOrEmpty(filePath) || + _ignoredThemePaths.Any(filePath.Contains) || + _ignoredThemeExtensions.Contains(Path.GetExtension(filePath).NullEmpty() ?? ""); + } + + + public Stream BuildPackage(PluginDescriptor pluginDescriptor) + { + return BuildPackage(pluginDescriptor.ConvertToExtensionDescriptor()); + } + + public Stream BuildPackage(ThemeManifest themeManifest) + { + return BuildPackage(themeManifest.ConvertToExtensionDescriptor()); + } + + private Stream BuildPackage(ExtensionDescriptor extensionDescriptor) + { + var context = new BuildContext(); + BeginPackage(context); + try + { + EstablishPaths(context, _env.WebRootFolder, extensionDescriptor.Id, extensionDescriptor.ExtensionType); + SetCoreProperties(context, extensionDescriptor); + EmbedFiles(context); + } + finally + { + EndPackage(context); + } + + if (context.Stream.CanSeek) + { + context.Stream.Seek(0, SeekOrigin.Begin); + } + + return context.Stream; + } + + private static void BeginPackage(BuildContext context) + { + context.Stream = new MemoryStream(); + context.Builder = new NuGetPackageBuilder(); + } + + private static void EndPackage(BuildContext context) + { + context.Builder.Save(context.Stream); + } + + private static void SetCoreProperties(BuildContext context, ExtensionDescriptor extensionDescriptor) + { + context.Builder.Id = PackagingUtils.BuildPackageId(extensionDescriptor.Id, extensionDescriptor.ExtensionType); + context.Builder.Version = new SemanticVersion(extensionDescriptor.Version); + context.Builder.Title = extensionDescriptor.Name ?? extensionDescriptor.Id; + context.Builder.Description = extensionDescriptor.Description.NullEmpty() ?? "No Description"; + context.Builder.Authors.Add(extensionDescriptor.Author ?? "Unknown"); + + if (Uri.IsWellFormedUriString(extensionDescriptor.WebSite, UriKind.Absolute)) + { + context.Builder.ProjectUrl = new Uri(extensionDescriptor.WebSite); + } + } + + private static void EstablishPaths(BuildContext context, IVirtualFolder webRootFolder, string extensionName, string extensionType = "Plugin") + { + context.SourceFolder = webRootFolder; + if (extensionType.IsCaseInsensitiveEqual("theme")) + { + context.SourcePath = "Themes/" + extensionName + "/"; + context.TargetPath = "\\Content\\Themes\\" + extensionName + "\\"; + } + else + { + context.SourcePath = "Plugins/" + extensionName + "/"; + context.TargetPath = "\\Content\\Plugins\\" + extensionName + "\\"; + } + } + + private static void EmbedFiles(BuildContext context) + { + var basePath = context.SourcePath; + foreach (var path in context.SourceFolder.ListFiles(context.SourcePath, true)) + { + // skip ignores files + if (IgnoreFile(path)) + { + continue; + } + // full virtual path given but we need the relative path so it can be put into + // the package that way (the package itself is the logical base path). + // Get it by stripping the basePath off including the slash. + var relativePath = path.Replace(basePath, ""); + EmbedVirtualFile(context, relativePath); + } + } + + private static void EmbedVirtualFile(BuildContext context, string relativePath) + { + var file = new VirtualPackageFile( + context.SourceFolder, + context.SourcePath + relativePath, + context.TargetPath + relativePath); + context.Builder.Files.Add(file); + } + + + #region Nested type: BuildContext + + private class BuildContext + { + public Stream Stream { get; set; } + public NuGetPackageBuilder Builder { get; set; } + + public IVirtualFolder SourceFolder { get; set; } + public string SourcePath { get; set; } + public string TargetPath { get; set; } + } + + #endregion + + #region Nested type: VirtualPackageFile + + private class VirtualPackageFile : IPackageFile + { + private readonly IVirtualFolder _webRootFolder; + private readonly string _relativePath; + private readonly string _packagePath; + + public VirtualPackageFile(IVirtualFolder webRootFolder, string relativePath, string packagePath) + { + _webRootFolder = webRootFolder; + _relativePath = relativePath; + _packagePath = packagePath; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Supposed to return an open stream.")] + public Stream GetStream() + { + var stream = new MemoryStream(); + _webRootFolder.CopyFile(_relativePath, stream); + stream.Seek(0, SeekOrigin.Begin); + return stream; + } + + public string Path => _packagePath; + + public string EffectivePath => _packagePath; + + public FrameworkName TargetFramework => null; + + public IEnumerable SupportedFrameworks => Enumerable.Empty(); + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/PackageInfo.cs b/src/Libraries/SmartStore.Core/Packaging/PackageInfo.cs index 2de699d27a..410c992ec2 100644 --- a/src/Libraries/SmartStore.Core/Packaging/PackageInfo.cs +++ b/src/Libraries/SmartStore.Core/Packaging/PackageInfo.cs @@ -1,17 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace SmartStore.Core.Packaging +namespace SmartStore.Core.Packaging { - public class PackageInfo - { - public string Id { get; set; } - public string Name { get; set; } - public string Version { get; set; } - public string Type { get; set; } - public string Path { get; set; } - public ExtensionDescriptor ExtensionDescriptor { get; set; } - } + public class PackageInfo + { + public string Id { get; set; } + public string Name { get; set; } + public string Version { get; set; } + public string Type { get; set; } + public string Path { get; set; } + public ExtensionDescriptor ExtensionDescriptor { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/PackageInstaller.cs b/src/Libraries/SmartStore.Core/Packaging/PackageInstaller.cs index 935b00e542..98e98970dd 100644 --- a/src/Libraries/SmartStore.Core/Packaging/PackageInstaller.cs +++ b/src/Libraries/SmartStore.Core/Packaging/PackageInstaller.cs @@ -1,299 +1,296 @@ using System; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Linq; using NuGet; -using NuGetPackageManager = NuGet.PackageManager; +using SmartStore.Core.IO; +using SmartStore.Core.Localization; using SmartStore.Core.Logging; -using Log = SmartStore.Core.Logging; using SmartStore.Core.Plugins; using SmartStore.Core.Themes; -using SmartStore.Core.IO; -using SmartStore.Core; -using SmartStore.Core.Localization; +using Log = SmartStore.Core.Logging; namespace SmartStore.Core.Packaging { - public class PackageInstaller : IPackageInstaller - { - private readonly IVirtualPathProvider _virtualPathProvider; - private readonly IPluginFinder _pluginFinder; - private readonly IThemeRegistry _themeRegistry; - private readonly IFolderUpdater _folderUpdater; - private readonly INotifier _notifier; - private readonly Log.ILogger _logger; - - public PackageInstaller( - IVirtualPathProvider virtualPathProvider, - IPluginFinder pluginFinder, - IThemeRegistry themeRegistry, - IFolderUpdater folderUpdater, - INotifier notifier, - Log.ILogger logger) - { - _virtualPathProvider = virtualPathProvider; - _pluginFinder = pluginFinder; - _themeRegistry = themeRegistry; - _folderUpdater = folderUpdater; - _notifier = notifier; - _logger = logger; - } - - public Localizer T { get; set; } - - //public PackageInfo Install(string packageId, string version, string location, string applicationFolder) - //{ - // // instantiates the appropriate package repository - // IPackageRepository packageRepository = PackageRepositoryFactory.Default.CreateRepository(location); - - // // gets an IPackage instance from the repository - // var packageVersion = String.IsNullOrEmpty(version) ? null : new SemanticVersion(version); - // var package = packageRepository.FindPackage(packageId, packageVersion); - // if (package == null) - // { - // throw new ArgumentException("The specified package could not be found, id:{0} version:{1}".FormatCurrent(packageId, version.IsEmpty() ? "No version" : version)); - // } - - // return InstallPackage(package, packageRepository, location, applicationFolder); - //} - - public PackageInfo Install(Stream packageStream, string location, string applicationPath) - { - Guard.NotNull(packageStream, nameof(packageStream)); - - IPackage package; - try - { - package = new ZipPackage(packageStream); - - } - catch (Exception ex) - { - throw new SmartException(T("Admin.Packaging.StreamError"), ex); - } - - // instantiates the appropriate package repository - var packageRepository = new NullSourceRepository(); - return InstallPackage(package, packageRepository, location, applicationPath); - } - - /// - /// Tries to install the package - /// - /// The package to install - /// The repository - /// The virtual location of the package file, usually ~/App_Data - /// The virtual app root path, usually ~/ - /// An instance of type - protected PackageInfo InstallPackage(IPackage package, IPackageRepository packageRepository, string location, string applicationPath) - { - bool previousInstalled; - - // 1. See if extension was previous installed and backup its folder if so - try - { - previousInstalled = BackupExtensionFolder(package.ExtensionFolder(), package.ExtensionId()); - } - catch (Exception exception) - { - throw new SmartException(T("Admin.Packaging.BackupError"), exception); - } - - if (previousInstalled) - { - // 2. If extension is installed, need to un-install first - try - { - UninstallExtensionIfNeeded(package); - } - catch (Exception exception) - { - throw new SmartException(T("Admin.Packaging.UninstallError"), exception); - } - } - - var packageInfo = ExecuteInstall(package, packageRepository, location, applicationPath); - - // check if the new package is compatible with current SmartStore version - var descriptor = package.GetExtensionDescriptor(packageInfo.Type); - - if (descriptor != null) - { - packageInfo.ExtensionDescriptor = descriptor; - - if (!PluginManager.IsAssumedCompatible(descriptor.MinAppVersion)) - { - if (previousInstalled) - { - // restore the previous version - RestoreExtensionFolder(package.ExtensionFolder(), package.ExtensionId()); - } - else - { - // just uninstall the new package - Uninstall(package.Id, _virtualPathProvider.MapPath("~\\")); - } - - var msg = T("Admin.Packaging.IsIncompatible", SmartStoreVersion.CurrentFullVersion); - _logger.Error(msg); - throw new SmartException(msg); - } - } - - return packageInfo; - } - - /// - /// Executes a package installation. - /// - /// The package to install. - /// The repository for the package. - /// The source location. - /// The path where to install the package. - /// The package information. - protected PackageInfo ExecuteInstall(IPackage package, IPackageRepository packageRepository, string sourceLocation, string targetPath) - { - var logger = new NugetLogger(_logger); - - var project = new FileBasedProjectSystem(targetPath) { Logger = logger }; - - IPackageRepository referenceRepository; - if (package.IsTheme()) - { - referenceRepository = new ThemeReferenceRepository(project, packageRepository, _themeRegistry); - } - else - { - referenceRepository = new PluginReferenceRepository(project, packageRepository, _pluginFinder); - } - - var projectManager = new ProjectManager( - packageRepository, - new DefaultPackagePathResolver(targetPath), - project, - referenceRepository - ) { Logger = logger }; - - // add the package to the project - projectManager.AddPackageReference(package, true, false); - - return new PackageInfo - { - Id = package.Id, - Name = package.Title ?? package.Id, - Version = package.Version.ToString(), - Type = package.IsTheme() ? "Theme" : "Plugin", - Path = targetPath - }; - } - - public void Uninstall(string packageId, string applicationFolder) - { - string extensionFullPath = string.Empty; - - if (packageId.StartsWith(PackagingUtils.GetExtensionPrefix("Theme"))) - { - extensionFullPath = _virtualPathProvider.MapPath("~/Themes/" + packageId.Substring(PackagingUtils.GetExtensionPrefix("Theme").Length)); - } - else if (packageId.StartsWith(PackagingUtils.GetExtensionPrefix("Plugin"))) - { - extensionFullPath = _virtualPathProvider.MapPath("~/Plugins/" + packageId.Substring(PackagingUtils.GetExtensionPrefix("Plugin").Length)); - } - - if (string.IsNullOrEmpty(extensionFullPath) || !System.IO.Directory.Exists(extensionFullPath)) - { - throw new SmartException(T("Admin.Packaging.NotFound", packageId)); - } - - // If the package was not installed through nuget we still need to try to uninstall it by removing its directory - if (Directory.Exists(extensionFullPath)) - { - Directory.Delete(extensionFullPath, true); - } - } - - [SuppressMessage("ReSharper", "UnusedMethodReturnValue.Local")] - private bool RestoreExtensionFolder(string extensionFolder, string extensionId) - { - var virtualSource = _virtualPathProvider.Combine("~", extensionFolder, extensionId); - var source = new DirectoryInfo(_virtualPathProvider.MapPath(virtualSource)); - - if (source.Exists) - { - var tempPath = _virtualPathProvider.Combine("~/App_Data", "_Backup", extensionFolder, extensionId); - string localTempPath = null; - for (int i = 0; i < 1000; i++) - { - localTempPath = _virtualPathProvider.MapPath(tempPath) + (i == 0 ? "" : "." + i.ToString()); - if (!System.IO.Directory.Exists(localTempPath)) - { - System.IO.Directory.CreateDirectory(localTempPath); - break; - } - localTempPath = null; - } - - if (localTempPath == null) - { - throw new SmartException(T("Admin.Packaging.TooManyBackups", tempPath)); - } - - var backupFolder = new DirectoryInfo(localTempPath); - _folderUpdater.Restore(backupFolder, source); - _notifier.Information(T("Admin.Packaging.RestoreSuccess", virtualSource)); - - return true; - } - - return false; - } - - private bool BackupExtensionFolder(string extensionFolder, string extensionId) - { - var source = new DirectoryInfo(_virtualPathProvider.MapPath(_virtualPathProvider.Combine("~", extensionFolder, extensionId))); - - if (source.Exists) - { - var tempPath = _virtualPathProvider.Combine("~/App_Data", "_Backup", extensionFolder, extensionId); - string localTempPath = null; - for (int i = 0; i < 1000; i++) - { - localTempPath = _virtualPathProvider.MapPath(tempPath) + (i == 0 ? "" : "." + i.ToString()); - if (!System.IO.Directory.Exists(localTempPath)) - { - System.IO.Directory.CreateDirectory(localTempPath); - break; - } - localTempPath = null; - } - - if (localTempPath == null) - { - throw new SmartException(T("Admin.Packaging.TooManyBackups", tempPath)); - } - - var backupFolder = new DirectoryInfo(localTempPath); - _folderUpdater.Backup(source, backupFolder); - _notifier.Information(T("Admin.Packaging.BackupSuccess", backupFolder.Name)); - - return true; - } - - return false; - } - - private void UninstallExtensionIfNeeded(IPackage package) - { - // Nuget requires to un-install the currently installed packages if the new - // package is the same version or an older version - try - { - Uninstall(package.Id, _virtualPathProvider.MapPath("~\\")); - //_notifier.Information("Successfully un-installed local package {0}".FormatInvariant(package.ExtensionId())); - } - catch { } - } - } + public class PackageInstaller : IPackageInstaller + { + private readonly IVirtualPathProvider _virtualPathProvider; + private readonly IPluginFinder _pluginFinder; + private readonly IThemeRegistry _themeRegistry; + private readonly IFolderUpdater _folderUpdater; + private readonly INotifier _notifier; + private readonly Log.ILogger _logger; + + public PackageInstaller( + IVirtualPathProvider virtualPathProvider, + IPluginFinder pluginFinder, + IThemeRegistry themeRegistry, + IFolderUpdater folderUpdater, + INotifier notifier, + Log.ILogger logger) + { + _virtualPathProvider = virtualPathProvider; + _pluginFinder = pluginFinder; + _themeRegistry = themeRegistry; + _folderUpdater = folderUpdater; + _notifier = notifier; + _logger = logger; + } + + public Localizer T { get; set; } + + //public PackageInfo Install(string packageId, string version, string location, string applicationFolder) + //{ + // // instantiates the appropriate package repository + // IPackageRepository packageRepository = PackageRepositoryFactory.Default.CreateRepository(location); + + // // gets an IPackage instance from the repository + // var packageVersion = String.IsNullOrEmpty(version) ? null : new SemanticVersion(version); + // var package = packageRepository.FindPackage(packageId, packageVersion); + // if (package == null) + // { + // throw new ArgumentException("The specified package could not be found, id:{0} version:{1}".FormatCurrent(packageId, version.IsEmpty() ? "No version" : version)); + // } + + // return InstallPackage(package, packageRepository, location, applicationFolder); + //} + + public PackageInfo Install(Stream packageStream, string location, string applicationPath) + { + Guard.NotNull(packageStream, nameof(packageStream)); + + IPackage package; + try + { + package = new ZipPackage(packageStream); + + } + catch (Exception ex) + { + throw new SmartException(T("Admin.Packaging.StreamError"), ex); + } + + // instantiates the appropriate package repository + var packageRepository = new NullSourceRepository(); + return InstallPackage(package, packageRepository, location, applicationPath); + } + + /// + /// Tries to install the package + /// + /// The package to install + /// The repository + /// The virtual location of the package file, usually ~/App_Data + /// The virtual app root path, usually ~/ + /// An instance of type + protected PackageInfo InstallPackage(IPackage package, IPackageRepository packageRepository, string location, string applicationPath) + { + bool previousInstalled; + + // 1. See if extension was previous installed and backup its folder if so + try + { + previousInstalled = BackupExtensionFolder(package.ExtensionFolder(), package.ExtensionId()); + } + catch (Exception exception) + { + throw new SmartException(T("Admin.Packaging.BackupError"), exception); + } + + if (previousInstalled) + { + // 2. If extension is installed, need to un-install first + try + { + UninstallExtensionIfNeeded(package); + } + catch (Exception exception) + { + throw new SmartException(T("Admin.Packaging.UninstallError"), exception); + } + } + + var packageInfo = ExecuteInstall(package, packageRepository, location, applicationPath); + + // check if the new package is compatible with current SmartStore version + var descriptor = package.GetExtensionDescriptor(packageInfo.Type); + + if (descriptor != null) + { + packageInfo.ExtensionDescriptor = descriptor; + + if (!PluginManager.IsAssumedCompatible(descriptor.MinAppVersion)) + { + if (previousInstalled) + { + // restore the previous version + RestoreExtensionFolder(package.ExtensionFolder(), package.ExtensionId()); + } + else + { + // just uninstall the new package + Uninstall(package.Id, _virtualPathProvider.MapPath("~\\")); + } + + var msg = T("Admin.Packaging.IsIncompatible", SmartStoreVersion.CurrentFullVersion); + _logger.Error(msg); + throw new SmartException(msg); + } + } + + return packageInfo; + } + + /// + /// Executes a package installation. + /// + /// The package to install. + /// The repository for the package. + /// The source location. + /// The path where to install the package. + /// The package information. + protected PackageInfo ExecuteInstall(IPackage package, IPackageRepository packageRepository, string sourceLocation, string targetPath) + { + var logger = new NugetLogger(_logger); + + var project = new FileBasedProjectSystem(targetPath) { Logger = logger }; + + IPackageRepository referenceRepository; + if (package.IsTheme()) + { + referenceRepository = new ThemeReferenceRepository(project, packageRepository, _themeRegistry); + } + else + { + referenceRepository = new PluginReferenceRepository(project, packageRepository, _pluginFinder); + } + + var projectManager = new ProjectManager( + packageRepository, + new DefaultPackagePathResolver(targetPath), + project, + referenceRepository + ) + { Logger = logger }; + + // add the package to the project + projectManager.AddPackageReference(package, true, false); + + return new PackageInfo + { + Id = package.Id, + Name = package.Title ?? package.Id, + Version = package.Version.ToString(), + Type = package.IsTheme() ? "Theme" : "Plugin", + Path = targetPath + }; + } + + public void Uninstall(string packageId, string applicationFolder) + { + string extensionFullPath = string.Empty; + + if (packageId.StartsWith(PackagingUtils.GetExtensionPrefix("Theme"))) + { + extensionFullPath = _virtualPathProvider.MapPath("~/Themes/" + packageId.Substring(PackagingUtils.GetExtensionPrefix("Theme").Length)); + } + else if (packageId.StartsWith(PackagingUtils.GetExtensionPrefix("Plugin"))) + { + extensionFullPath = _virtualPathProvider.MapPath("~/Plugins/" + packageId.Substring(PackagingUtils.GetExtensionPrefix("Plugin").Length)); + } + + if (string.IsNullOrEmpty(extensionFullPath) || !System.IO.Directory.Exists(extensionFullPath)) + { + throw new SmartException(T("Admin.Packaging.NotFound", packageId)); + } + + // If the package was not installed through nuget we still need to try to uninstall it by removing its directory + if (Directory.Exists(extensionFullPath)) + { + Directory.Delete(extensionFullPath, true); + } + } + + [SuppressMessage("ReSharper", "UnusedMethodReturnValue.Local")] + private bool RestoreExtensionFolder(string extensionFolder, string extensionId) + { + var virtualSource = _virtualPathProvider.Combine("~", extensionFolder, extensionId); + var source = new DirectoryInfo(_virtualPathProvider.MapPath(virtualSource)); + + if (source.Exists) + { + var tempPath = _virtualPathProvider.Combine("~/App_Data", "_Backup", extensionFolder, extensionId); + string localTempPath = null; + for (int i = 0; i < 1000; i++) + { + localTempPath = _virtualPathProvider.MapPath(tempPath) + (i == 0 ? "" : "." + i.ToString()); + if (!System.IO.Directory.Exists(localTempPath)) + { + System.IO.Directory.CreateDirectory(localTempPath); + break; + } + localTempPath = null; + } + + if (localTempPath == null) + { + throw new SmartException(T("Admin.Packaging.TooManyBackups", tempPath)); + } + + var backupFolder = new DirectoryInfo(localTempPath); + _folderUpdater.Restore(backupFolder, source); + _notifier.Information(T("Admin.Packaging.RestoreSuccess", virtualSource)); + + return true; + } + + return false; + } + + private bool BackupExtensionFolder(string extensionFolder, string extensionId) + { + var source = new DirectoryInfo(_virtualPathProvider.MapPath(_virtualPathProvider.Combine("~", extensionFolder, extensionId))); + + if (source.Exists) + { + var tempPath = _virtualPathProvider.Combine("~/App_Data", "_Backup", extensionFolder, extensionId); + string localTempPath = null; + for (int i = 0; i < 1000; i++) + { + localTempPath = _virtualPathProvider.MapPath(tempPath) + (i == 0 ? "" : "." + i.ToString()); + if (!System.IO.Directory.Exists(localTempPath)) + { + System.IO.Directory.CreateDirectory(localTempPath); + break; + } + localTempPath = null; + } + + if (localTempPath == null) + { + throw new SmartException(T("Admin.Packaging.TooManyBackups", tempPath)); + } + + var backupFolder = new DirectoryInfo(localTempPath); + _folderUpdater.Backup(source, backupFolder); + _notifier.Information(T("Admin.Packaging.BackupSuccess", backupFolder.Name)); + + return true; + } + + return false; + } + + private void UninstallExtensionIfNeeded(IPackage package) + { + // Nuget requires to un-install the currently installed packages if the new + // package is the same version or an older version + try + { + Uninstall(package.Id, _virtualPathProvider.MapPath("~\\")); + //_notifier.Information("Successfully un-installed local package {0}".FormatInvariant(package.ExtensionId())); + } + catch { } + } + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/PackageManager.cs b/src/Libraries/SmartStore.Core/Packaging/PackageManager.cs index 2d1285007f..0e399621c1 100644 --- a/src/Libraries/SmartStore.Core/Packaging/PackageManager.cs +++ b/src/Libraries/SmartStore.Core/Packaging/PackageManager.cs @@ -1,98 +1,95 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.IO; -using NuGet; -using Log = SmartStore.Core.Logging; using SmartStore.Core.Plugins; using SmartStore.Core.Themes; +using Log = SmartStore.Core.Logging; namespace SmartStore.Core.Packaging { - public class PackageManager : IPackageManager - { - private readonly IPluginFinder _pluginFinder; - private readonly IThemeRegistry _themeRegistry; - private readonly IPackageBuilder _packageBuilder; - private readonly IPackageInstaller _packageInstaller; - private readonly Log.ILogger _logger; + public class PackageManager : IPackageManager + { + private readonly IPluginFinder _pluginFinder; + private readonly IThemeRegistry _themeRegistry; + private readonly IPackageBuilder _packageBuilder; + private readonly IPackageInstaller _packageInstaller; + private readonly Log.ILogger _logger; - public PackageManager( - IPluginFinder pluginFinder, - IThemeRegistry themeRegistry, - IPackageBuilder packageBuilder, - IPackageInstaller packageInstaller, - Log.ILogger logger) - { - _pluginFinder = pluginFinder; - _themeRegistry = themeRegistry; - _packageBuilder = packageBuilder; - _packageInstaller = packageInstaller; - _logger = logger; - } + public PackageManager( + IPluginFinder pluginFinder, + IThemeRegistry themeRegistry, + IPackageBuilder packageBuilder, + IPackageInstaller packageInstaller, + Log.ILogger logger) + { + _pluginFinder = pluginFinder; + _themeRegistry = themeRegistry; + _packageBuilder = packageBuilder; + _packageInstaller = packageInstaller; + _logger = logger; + } - private PackageInfo DoInstall(Func installer) - { - try - { - return installer(); - } - catch (SmartException) - { - throw; - } - catch (Exception exception) - { - var message = "There was an error installing the requested package. " + - "This can happen if the server does not have write access to the '~/Plugins' or '~/Themes' folder of the web site. " + - "If the site is running in shared hosted environement, adding write access to these folders sometimes needs to be done manually through the Hoster control panel. " + - "Once Themes and Plugins have been installed, it is recommended to remove write access to these folders."; - throw new SmartException(message, exception); - } - } + private PackageInfo DoInstall(Func installer) + { + try + { + return installer(); + } + catch (SmartException) + { + throw; + } + catch (Exception exception) + { + var message = "There was an error installing the requested package. " + + "This can happen if the server does not have write access to the '~/Plugins' or '~/Themes' folder of the web site. " + + "If the site is running in shared hosted environement, adding write access to these folders sometimes needs to be done manually through the Hoster control panel. " + + "Once Themes and Plugins have been installed, it is recommended to remove write access to these folders."; + throw new SmartException(message, exception); + } + } - public PackageInfo Install(Stream packageStream, string location, string applicationPath) - { - return DoInstall(() => _packageInstaller.Install(packageStream, location, applicationPath)); - } + public PackageInfo Install(Stream packageStream, string location, string applicationPath) + { + return DoInstall(() => _packageInstaller.Install(packageStream, location, applicationPath)); + } - public void Uninstall(string packageId, string applicationPath) - { - _packageInstaller.Uninstall(packageId, applicationPath); - } + public void Uninstall(string packageId, string applicationPath) + { + _packageInstaller.Uninstall(packageId, applicationPath); + } - public PackagingResult BuildPluginPackage(string pluginName) - { - var pluginDescriptor = _pluginFinder.GetPluginDescriptorBySystemName(pluginName); - if (pluginDescriptor == null) - { - return null; - } - return new PackagingResult - { - ExtensionType = "Plugin", - PackageName = pluginDescriptor.FolderName, - PackageVersion = pluginDescriptor.Version.ToString(), - PackageStream = _packageBuilder.BuildPackage(pluginDescriptor) - }; - } + public PackagingResult BuildPluginPackage(string pluginName) + { + var pluginDescriptor = _pluginFinder.GetPluginDescriptorBySystemName(pluginName); + if (pluginDescriptor == null) + { + return null; + } + return new PackagingResult + { + ExtensionType = "Plugin", + PackageName = pluginDescriptor.FolderName, + PackageVersion = pluginDescriptor.Version.ToString(), + PackageStream = _packageBuilder.BuildPackage(pluginDescriptor) + }; + } - public PackagingResult BuildThemePackage(string themeName) - { - var themeManifest = _themeRegistry.GetThemeManifest(themeName); - if (themeName == null) - { - return null; - } - return new PackagingResult - { - ExtensionType = "Theme", - PackageName = themeManifest.ThemeName, - PackageVersion = themeManifest.Version, - PackageStream = _packageBuilder.BuildPackage(themeManifest) - }; - } - } + public PackagingResult BuildThemePackage(string themeName) + { + var themeManifest = _themeRegistry.GetThemeManifest(themeName); + if (themeName == null) + { + return null; + } + return new PackagingResult + { + ExtensionType = "Theme", + PackageName = themeManifest.ThemeName, + PackageVersion = themeManifest.Version, + PackageStream = _packageBuilder.BuildPackage(themeManifest) + }; + } + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/PackagingResult.cs b/src/Libraries/SmartStore.Core/Packaging/PackagingResult.cs index 69e054e009..312e737439 100644 --- a/src/Libraries/SmartStore.Core/Packaging/PackagingResult.cs +++ b/src/Libraries/SmartStore.Core/Packaging/PackagingResult.cs @@ -2,12 +2,12 @@ namespace SmartStore.Core.Packaging { - public class PackagingResult - { - public string ExtensionType { get; set; } + public class PackagingResult + { + public string ExtensionType { get; set; } - public string PackageName { get; set; } - public string PackageVersion { get; set; } - public Stream PackageStream { get; set; } - } + public string PackageName { get; set; } + public string PackageVersion { get; set; } + public Stream PackageStream { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/PackagingUtils.cs b/src/Libraries/SmartStore.Core/Packaging/PackagingUtils.cs index 0d50f65851..cbaa6e0cd0 100644 --- a/src/Libraries/SmartStore.Core/Packaging/PackagingUtils.cs +++ b/src/Libraries/SmartStore.Core/Packaging/PackagingUtils.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Web.Hosting; @@ -9,140 +8,140 @@ namespace SmartStore.Core.Packaging { - public static class PackagingUtils - { - public static string GetExtensionPrefix(string extensionType) - { - return string.Format("SmartStore.{0}.", extensionType); - } - - public static string BuildPackageId(string extensionName, string extensionType) - { - return GetExtensionPrefix(extensionType) + extensionName; - } - - internal static bool IsTheme(this IPackage package) - { - return IsTheme(package.Id); - } - - public static bool IsTheme(this PackageInfo info) - { - return IsTheme(info.Id); - } - - internal static string ExtensionFolder(this IPackage package) - { - return ExtensionFolder(package.IsTheme()); - } - - internal static string ExtensionId(this IPackage package) - { - return ExtensionId(package.IsTheme(), package.Id); - } - - public static string ExtensionId(this PackageInfo info) - { - return ExtensionId(info.IsTheme(), info.Id); - } - - public static bool IsTheme(string packageId) - { - return packageId.StartsWith(GetExtensionPrefix("Theme")); - } - - private static string ExtensionFolder(bool isTheme) - { - return isTheme ? "Themes" : "Plugins"; - } - - private static string ExtensionId(bool isTheme, string packageId) - { - return isTheme ? - packageId.Substring(GetExtensionPrefix("Theme").Length) : - packageId.Substring(GetExtensionPrefix("Plugin").Length); - } - - internal static ExtensionDescriptor ConvertToExtensionDescriptor(this PluginDescriptor pluginDescriptor) - { - // TODO: (pkg) Add Icons to extension manifests - var descriptor = new ExtensionDescriptor - { - ExtensionType = "Plugin", - Location = "~/Plugins", - Path = pluginDescriptor.PhysicalPath, - Id = pluginDescriptor.FolderName, - Author = pluginDescriptor.Author, - MinAppVersion = pluginDescriptor.MinAppVersion, - Version = pluginDescriptor.Version, - Name = pluginDescriptor.FriendlyName, - Description = pluginDescriptor.Description, - WebSite = pluginDescriptor.Url, // TODO: (pkg) Add author url to plugin manifests, - Tags = string.Empty // TODO: (pkg) Add tags to plugin manifests, - }; - - return descriptor; - } - - internal static ExtensionDescriptor ConvertToExtensionDescriptor(this ThemeManifest themeManifest) - { - var descriptor = new ExtensionDescriptor - { - ExtensionType = "Theme", - Location = "~/Themes", - Path = themeManifest.Path, - Id = themeManifest.ThemeName, - Author = themeManifest.Author.HasValue() ? themeManifest.Author : "[Unknown]", - MinAppVersion = SmartStoreVersion.Version, // TODO: (pkg) Add SupportedVersion to theme manifests - Version = new Version(themeManifest.Version), - Name = themeManifest.ThemeTitle, - Description = string.Empty, // TODO: (pkg) Add description to theme manifests - WebSite = themeManifest.Url, - Tags = string.Empty // TODO: (pkg) Add tags to theme manifests, - }; - - return descriptor; - } - - internal static ExtensionDescriptor GetExtensionDescriptor(this IPackage package, string extensionType) - { - bool isTheme = extensionType.IsCaseInsensitiveEqual("Theme"); - - IPackageFile packageFile = package.GetFiles().FirstOrDefault(file => - { - var fileName = Path.GetFileName(file.Path); - return fileName != null && fileName.Equals(isTheme ? "theme.config" : "Description.txt", StringComparison.OrdinalIgnoreCase); - }); - - ExtensionDescriptor descriptor = null; - - if (packageFile != null) - { - var filePath = packageFile.EffectivePath; - if (filePath.HasValue()) - { - filePath = Path.Combine(HostingEnvironment.MapPath("~/"), filePath); - if (isTheme) - { - var themeManifest = ThemeManifest.Create(Path.GetDirectoryName(filePath)); - if (themeManifest != null) - { - descriptor = themeManifest.ConvertToExtensionDescriptor(); - } - } - else // is a Plugin - { - var pluginDescriptor = PluginFileParser.ParsePluginDescriptionFile(filePath); - if (pluginDescriptor != null) - { - descriptor = pluginDescriptor.ConvertToExtensionDescriptor(); - } - } - } - } - - return descriptor; - } - } + public static class PackagingUtils + { + public static string GetExtensionPrefix(string extensionType) + { + return string.Format("SmartStore.{0}.", extensionType); + } + + public static string BuildPackageId(string extensionName, string extensionType) + { + return GetExtensionPrefix(extensionType) + extensionName; + } + + internal static bool IsTheme(this IPackage package) + { + return IsTheme(package.Id); + } + + public static bool IsTheme(this PackageInfo info) + { + return IsTheme(info.Id); + } + + internal static string ExtensionFolder(this IPackage package) + { + return ExtensionFolder(package.IsTheme()); + } + + internal static string ExtensionId(this IPackage package) + { + return ExtensionId(package.IsTheme(), package.Id); + } + + public static string ExtensionId(this PackageInfo info) + { + return ExtensionId(info.IsTheme(), info.Id); + } + + public static bool IsTheme(string packageId) + { + return packageId.StartsWith(GetExtensionPrefix("Theme")); + } + + private static string ExtensionFolder(bool isTheme) + { + return isTheme ? "Themes" : "Plugins"; + } + + private static string ExtensionId(bool isTheme, string packageId) + { + return isTheme ? + packageId.Substring(GetExtensionPrefix("Theme").Length) : + packageId.Substring(GetExtensionPrefix("Plugin").Length); + } + + internal static ExtensionDescriptor ConvertToExtensionDescriptor(this PluginDescriptor pluginDescriptor) + { + // TODO: (pkg) Add Icons to extension manifests + var descriptor = new ExtensionDescriptor + { + ExtensionType = "Plugin", + Location = "~/Plugins", + Path = pluginDescriptor.PhysicalPath, + Id = pluginDescriptor.FolderName, + Author = pluginDescriptor.Author, + MinAppVersion = pluginDescriptor.MinAppVersion, + Version = pluginDescriptor.Version, + Name = pluginDescriptor.FriendlyName, + Description = pluginDescriptor.Description, + WebSite = pluginDescriptor.Url, // TODO: (pkg) Add author url to plugin manifests, + Tags = string.Empty // TODO: (pkg) Add tags to plugin manifests, + }; + + return descriptor; + } + + internal static ExtensionDescriptor ConvertToExtensionDescriptor(this ThemeManifest themeManifest) + { + var descriptor = new ExtensionDescriptor + { + ExtensionType = "Theme", + Location = "~/Themes", + Path = themeManifest.Path, + Id = themeManifest.ThemeName, + Author = themeManifest.Author.HasValue() ? themeManifest.Author : "[Unknown]", + MinAppVersion = SmartStoreVersion.Version, // TODO: (pkg) Add SupportedVersion to theme manifests + Version = new Version(themeManifest.Version), + Name = themeManifest.ThemeTitle, + Description = string.Empty, // TODO: (pkg) Add description to theme manifests + WebSite = themeManifest.Url, + Tags = string.Empty // TODO: (pkg) Add tags to theme manifests, + }; + + return descriptor; + } + + internal static ExtensionDescriptor GetExtensionDescriptor(this IPackage package, string extensionType) + { + bool isTheme = extensionType.IsCaseInsensitiveEqual("Theme"); + + IPackageFile packageFile = package.GetFiles().FirstOrDefault(file => + { + var fileName = Path.GetFileName(file.Path); + return fileName != null && fileName.Equals(isTheme ? "theme.config" : "Description.txt", StringComparison.OrdinalIgnoreCase); + }); + + ExtensionDescriptor descriptor = null; + + if (packageFile != null) + { + var filePath = packageFile.EffectivePath; + if (filePath.HasValue()) + { + filePath = Path.Combine(HostingEnvironment.MapPath("~/"), filePath); + if (isTheme) + { + var themeManifest = ThemeManifest.Create(Path.GetDirectoryName(filePath)); + if (themeManifest != null) + { + descriptor = themeManifest.ConvertToExtensionDescriptor(); + } + } + else // is a Plugin + { + var pluginDescriptor = PluginFileParser.ParsePluginDescriptionFile(filePath); + if (pluginDescriptor != null) + { + descriptor = pluginDescriptor.ConvertToExtensionDescriptor(); + } + } + } + } + + return descriptor; + } + } } diff --git a/src/Libraries/SmartStore.Core/Packaging/Readme.txt b/src/Libraries/SmartStore.Core/Packaging/Readme.txt index 58566c0667..2052796b87 100644 --- a/src/Libraries/SmartStore.Core/Packaging/Readme.txt +++ b/src/Libraries/SmartStore.Core/Packaging/Readme.txt @@ -1,4 +1,4 @@  -Inspiration for the SmartStore.NET packaging infrastructure comes from Orchard (http://orchardproject.net/), +Inspiration for the Smartstore packaging infrastructure comes from Orchard (http://orchardproject.net/), a fantastic ASP.NET MVC based open source CMS from the Outercurve Foundation. Most of the types here were copied over and refactored accordingly. \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Packaging/Updater/AppUpdater.cs b/src/Libraries/SmartStore.Core/Packaging/Updater/AppUpdater.cs index a645c43d9a..0e5cae0836 100644 --- a/src/Libraries/SmartStore.Core/Packaging/Updater/AppUpdater.cs +++ b/src/Libraries/SmartStore.Core/Packaging/Updater/AppUpdater.cs @@ -14,216 +14,222 @@ namespace SmartStore.Core.Packaging { public sealed class AppUpdater : DisposableObject - { - public const string UpdatePackagePath = "~/App_Data/Update"; - - private static readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); - private TraceLogger _logger; - - #region Package update - - [SuppressMessage("ReSharper", "RedundantAssignment")] - public bool InstallablePackageExists() - { - string packagePath = null; - var package = FindPackage(false, out packagePath); - - if (package == null) - return false; - - if (!ValidatePackage(package)) - return false; - - if (!CheckEnvironment()) - return false; - - return true; - } - - internal bool TryUpdateFromPackage() - { - // NEVER EVER (!!!) make an attempt to auto-update in a dev environment!!!!!!! - if (CommonHelper.IsDevEnvironment) - return false; - - using (_rwLock.GetUpgradeableReadLock()) - { - try - { - string packagePath = null; - var package = FindPackage(true, out packagePath); - - if (package == null) - return false; - - if (!ValidatePackage(package)) - return false; - - if (!CheckEnvironment()) - return false; - - using (_rwLock.GetWriteLock()) - { - Backup(); - - var info = ExecuteUpdate(package); - - if (info != null) - { - var newPath = packagePath + ".applied"; - if (File.Exists(newPath)) - { - File.Delete(packagePath); - } - else - { - File.Move(packagePath, newPath); - } - } - - return info != null; - } - } - catch (Exception ex) - { - _logger.Error(ex, "An error occured while updating the application"); - return false; - } - } - } - - private TraceLogger CreateLogger(IPackage package) - { - var logFile = Path.Combine(CommonHelper.MapPath(UpdatePackagePath, false), "Updater.{0}.log".FormatInvariant(package.Version.ToString())); - return new TraceLogger(logFile); - } - - private IPackage FindPackage(bool createLogger, out string path) - { - path = null; - var dir = CommonHelper.MapPath(UpdatePackagePath, false); - - if (!Directory.Exists(dir)) - return null; - - var files = Directory.GetFiles(dir, "SmartStore.*.nupkg", SearchOption.TopDirectoryOnly); - - // TODO: allow more than one package in folder and return newest - if (files.Length == 0 || files.Length > 1) - return null; - - try - { - path = files[0]; - IPackage package = new ZipPackage(files[0]); - if (createLogger) - { - _logger = CreateLogger(package); - _logger.Info("Found update package '{0}'".FormatInvariant(package.GetFullName())); - } - return package; - } - catch { } - - return null; - } - - private bool ValidatePackage(IPackage package) - { - if (package.Id != "SmartStore") - return false; - - var currentVersion = new SemanticVersion(SmartStoreVersion.Version); - return package.Version > currentVersion; - } - - private bool CheckEnvironment() - { - // TODO: Check it :-) - return true; - } - - private void Backup() - { - var source = new DirectoryInfo(CommonHelper.MapPath("~/")); - - var tempPath = CommonHelper.MapPath("~/App_Data/_Backup/App/SmartStore"); - string localTempPath = null; - for (int i = 0; i < 50; i++) - { - localTempPath = tempPath + (i == 0 ? "" : "." + i.ToString()); - if (!Directory.Exists(localTempPath)) - { - Directory.CreateDirectory(localTempPath); - break; - } - localTempPath = null; - } - - if (localTempPath == null) - { - var exception = new SmartException("Too many backups in '{0}'.".FormatInvariant(tempPath)); - _logger.Error(exception); - throw exception; - } - - var backupFolder = new DirectoryInfo(localTempPath); - var folderUpdater = new FolderUpdater(_logger); - folderUpdater.Backup(source, backupFolder, "App_Data", "Media"); - - _logger.Info("Backup successfully created in folder '{0}'.".FormatInvariant(localTempPath)); - } - - private PackageInfo ExecuteUpdate(IPackage package) - { - var appPath = CommonHelper.MapPath("~/"); - - var logger = new NugetLogger(_logger); - - var project = new FileBasedProjectSystem(appPath) { Logger = logger }; - - var nullRepository = new NullSourceRepository(); - - var projectManager = new ProjectManager( - nullRepository, - new DefaultPackagePathResolver(appPath), - project, - nullRepository - ) { Logger = logger }; - - // Perform the update - projectManager.AddPackageReference(package, true, false); - - var info = new PackageInfo - { - Id = package.Id, - Name = package.Title ?? package.Id, - Version = package.Version.ToString(), - Type = "App", - Path = appPath - }; - - _logger.Info("Update '{0}' successfully executed.".FormatInvariant(info.Name)); - - return info; - } - - #endregion - - #region Migrations - - internal void ExecuteMigrations() - { - TryMigrateDefaultTenant(); - - if (!DataSettings.DatabaseIsInstalled()) - return; - - var currentVersion = SmartStoreVersion.Version; - var prevVersion = DataSettings.Current.AppVersion ?? new Version(1, 0); + { + public const string UpdatePackagePath = "~/App_Data/Update"; + + private static readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); + private TraceLogger _logger; + + #region Package update + + [SuppressMessage("ReSharper", "RedundantAssignment")] + public bool InstallablePackageExists() + { + string packagePath = null; + var package = FindPackage(false, out packagePath); + + if (package == null) + return false; + + if (!ValidatePackage(package)) + return false; + + if (!CheckEnvironment()) + return false; + + return true; + } + + internal bool TryUpdateFromPackage() + { + // NEVER EVER (!!!) make an attempt to auto-update in a dev environment!!!!!!! + if (CommonHelper.IsDevEnvironment) + return false; + + using (_rwLock.GetUpgradeableReadLock()) + { + try + { + string packagePath = null; + var package = FindPackage(true, out packagePath); + + if (package == null) + return false; + + if (!ValidatePackage(package)) + return false; + + if (!CheckEnvironment()) + return false; + + using (_rwLock.GetWriteLock()) + { + Backup(); + + var info = ExecuteUpdate(package); + + if (info != null) + { + var newPath = packagePath + ".applied"; + if (File.Exists(newPath)) + { + File.Delete(packagePath); + } + else + { + File.Move(packagePath, newPath); + } + } + + return info != null; + } + } + catch (Exception ex) + { + _logger.Error(ex, "An error occured while updating the application"); + return false; + } + } + } + + private TraceLogger CreateLogger(IPackage package) + { + var logFile = Path.Combine(CommonHelper.MapPath(UpdatePackagePath, false), "Updater.{0}.log".FormatInvariant(package.Version.ToString())); + return new TraceLogger(logFile); + } + + private IPackage FindPackage(bool createLogger, out string path) + { + path = null; + var dir = CommonHelper.MapPath(UpdatePackagePath, false); + + if (!Directory.Exists(dir)) + return null; + + var files = Directory.GetFiles(dir, "SmartStore.*.nupkg", SearchOption.TopDirectoryOnly); + + // TODO: allow more than one package in folder and return newest + if (files.Length == 0 || files.Length > 1) + return null; + + try + { + path = files[0]; + IPackage package = new ZipPackage(files[0]); + if (createLogger) + { + _logger = CreateLogger(package); + _logger.Info("Found update package '{0}'".FormatInvariant(package.GetFullName())); + } + return package; + } + catch { } + + return null; + } + + private bool ValidatePackage(IPackage package) + { + if (package.Id != "SmartStore") + return false; + + var currentVersion = new SemanticVersion(SmartStoreVersion.Version); + return package.Version > currentVersion; + } + + private bool CheckEnvironment() + { + // TODO: Check it :-) + return true; + } + + private void Backup() + { + var source = new DirectoryInfo(CommonHelper.MapPath("~/")); + + var tempPath = CommonHelper.MapPath("~/App_Data/_Backup/App/SmartStore"); + string localTempPath = null; + for (int i = 0; i < 50; i++) + { + localTempPath = tempPath + (i == 0 ? "" : "." + i.ToString()); + if (!Directory.Exists(localTempPath)) + { + Directory.CreateDirectory(localTempPath); + break; + } + localTempPath = null; + } + + if (localTempPath == null) + { + var exception = new SmartException("Too many backups in '{0}'.".FormatInvariant(tempPath)); + _logger.Error(exception); + throw exception; + } + + var backupFolder = new DirectoryInfo(localTempPath); + var folderUpdater = new FolderUpdater(_logger); + folderUpdater.Backup(source, backupFolder, "App_Data", "Media"); + + _logger.Info("Backup successfully created in folder '{0}'.".FormatInvariant(localTempPath)); + } + + private PackageInfo ExecuteUpdate(IPackage package) + { + var appPath = CommonHelper.MapPath("~/"); + + var logger = new NugetLogger(_logger); + + var project = new FileBasedProjectSystem(appPath) { Logger = logger }; + + var nullRepository = new NullSourceRepository(); + + var projectManager = new ProjectManager( + nullRepository, + new DefaultPackagePathResolver(appPath), + project, + nullRepository + ) + { Logger = logger }; + + // Perform the update + projectManager.AddPackageReference(package, true, false); + + var info = new PackageInfo + { + Id = package.Id, + Name = package.Title ?? package.Id, + Version = package.Version.ToString(), + Type = "App", + Path = appPath + }; + + _logger.Info("Update '{0}' successfully executed.".FormatInvariant(info.Name)); + + return info; + } + + #endregion + + #region Migrations + + internal void ExecuteMigrations() + { + if (!DataSettings.DatabaseIsInstalled()) + return; + + var currentVersion = SmartStoreVersion.Version; + var prevVersion = DataSettings.Current.AppVersion ?? new Version(1, 0); + + if (prevVersion < new Version(3, 0, 0)) + { + throw new ApplicationException($"Smartstore {currentVersion} does not support automatic upgrade from version {prevVersion}. Please upgrade to version 3.x first."); + } + + TryMigrateDefaultTenant(); if (prevVersion >= currentVersion) - return; + return; if (prevVersion < new Version(2, 1)) { @@ -239,144 +245,144 @@ internal void ExecuteMigrations() } DataSettings.Current.AppVersion = currentVersion; - DataSettings.Current.Save(); - } - - private bool TryMigrateDefaultTenant() - { - // We introduced basic multi-tenancy in V3 [...] - - if (!IsPreTenancyVersion()) - { - return false; - } - - var tenantDir = Directory.CreateDirectory(CommonHelper.MapPath("~/App_Data/Tenants/Default")); - var tenantTempDir = tenantDir.CreateSubdirectory("_temp"); - - var appDataDir = CommonHelper.MapPath("~/App_Data"); - - // Move Settings.txt - File.Move(Path.Combine(appDataDir, "Settings.txt"), Path.Combine(tenantDir.FullName, "Settings.txt")); - - // Move InstalledPlugins.txt - File.Move(Path.Combine(appDataDir, "InstalledPlugins.txt"), Path.Combine(tenantDir.FullName, "InstalledPlugins.txt")); - - // Move SmartStore.db.sdf - var path = Path.Combine(appDataDir, "SmartStore.db.sdf"); - if (File.Exists(path)) - { - File.Move(path, Path.Combine(tenantDir.FullName, "SmartStore.db.sdf")); - } - - Func moveTenantFolder = (sourceFolder, targetFolder) => - { - var sourcePath = Path.Combine(appDataDir, sourceFolder); - - if (Directory.Exists(sourcePath)) - { - Directory.Move(sourcePath, Path.Combine(tenantDir.FullName, targetFolder ?? sourceFolder)); - return true; - } - - return false; - }; - - // Move tenant specific Folders - moveTenantFolder("ImportProfiles", null); - moveTenantFolder("ExportProfiles", null); - moveTenantFolder("Indexing", null); - moveTenantFolder("Lucene", null); - moveTenantFolder("_temp\\BizBackups", null); - moveTenantFolder("_temp\\ShopConnector", null); - - // Move all media files and folders to new subfolder "Default" - var mediaInfos = (new DirectoryInfo(CommonHelper.MapPath("~/Media"))).EnumerateFileSystemInfos().Where(x => !x.Name.IsCaseInsensitiveEqual("Default")); - var mediaFiles = mediaInfos.OfType(); - var mediaDirs = mediaInfos.OfType().ToArray(); - var tenantMediaDir = new DirectoryInfo(CommonHelper.MapPath("~/Media/Default")); - if (!tenantMediaDir.Exists) - { - tenantMediaDir.Create(); - } - - foreach (var dir in mediaDirs) - { - dir.MoveTo(Path.Combine(tenantMediaDir.FullName, dir.Name)); - } - - foreach (var file in mediaFiles) - { - file.MoveTo(Path.Combine(tenantMediaDir.FullName, file.Name)); - } - - return true; - } - - private bool IsPreTenancyVersion() - { - var appDataDir = CommonHelper.MapPath("~/App_Data"); - - return File.Exists(Path.Combine(appDataDir, "Settings.txt")) - && File.Exists(Path.Combine(appDataDir, "InstalledPlugins.txt")) - && !Directory.Exists(Path.Combine(appDataDir, "Tenants\\Default")); - } - - private void MigrateInitial() - { - var installedPlugins = PluginFileParser.ParseInstalledPluginsFile(); - if (installedPlugins.Count == 0) - return; - - var renamedPlugins = new List(); - - var pluginRenameMap = new Dictionary - { - { "CurrencyExchange.ECB", null /* null means: remove it */ }, - { "CurrencyExchange.MoneyConverter", null }, - { "ExternalAuth.OpenId", null }, - { "Tax.Free", null }, - { "Api.WebApi", "SmartStore.WebApi" }, - { "DiscountRequirement.MustBeAssignedToCustomerRole", "SmartStore.DiscountRules" }, - { "DiscountRequirement.HadSpentAmount", "SmartStore.DiscountRules" }, - { "DiscountRequirement.HasAllProducts", "SmartStore.DiscountRules" }, - { "DiscountRequirement.HasOneProduct", "SmartStore.DiscountRules" }, - { "DiscountRequirement.Store", "SmartStore.DiscountRules" }, - { "DiscountRequirement.BillingCountryIs", "SmartStore.DiscountRules" }, - { "DiscountRequirement.ShippingCountryIs", "SmartStore.DiscountRules" }, - { "DiscountRequirement.HasPaymentMethod", "SmartStore.DiscountRules.HasPaymentMethod" }, - { "DiscountRequirement.HasShippingOption", "SmartStore.DiscountRules.HasShippingOption" }, - { "DiscountRequirement.PurchasedAllProducts", "SmartStore.DiscountRules.PurchasedProducts" }, - { "DiscountRequirement.PurchasedOneProduct", "SmartStore.DiscountRules.PurchasedProducts" }, - { "PromotionFeed.Froogle", "SmartStore.GoogleMerchantCenter" }, - { "PromotionFeed.Billiger", "SmartStore.Billiger" }, - { "PromotionFeed.ElmarShopinfo", "SmartStore.ElmarShopinfo" }, - { "PromotionFeed.Guenstiger", "SmartStore.Guenstiger" }, - { "Payments.AccardaKar", "SmartStore.AccardaKar" }, - { "Payments.AmazonPay", "SmartStore.AmazonPay" }, - { "Developer.DevTools", "SmartStore.DevTools" }, - { "ExternalAuth.Facebook", "SmartStore.FacebookAuth" }, - { "ExternalAuth.Twitter", "SmartStore.TwitterAuth" }, - { "SMS.Clickatell", "SmartStore.Clickatell" }, - { "Widgets.GoogleAnalytics", "SmartStore.GoogleAnalytics" }, - { "Misc.DemoShop", "SmartStore.DemoShopControlling" }, - { "Admin.OrderNumberFormatter", "SmartStore.OrderNumberFormatter" }, - { "Admin.Debitoor", "SmartStore.Debitoor" }, + DataSettings.Current.Save(); + } + + private bool TryMigrateDefaultTenant() + { + // We introduced basic multi-tenancy in V3 [...] + + if (!IsPreTenancyVersion()) + { + return false; + } + + var tenantDir = Directory.CreateDirectory(CommonHelper.MapPath("~/App_Data/Tenants/Default")); + var tenantTempDir = tenantDir.CreateSubdirectory("_temp"); + + var appDataDir = CommonHelper.MapPath("~/App_Data"); + + // Move Settings.txt + File.Move(Path.Combine(appDataDir, "Settings.txt"), Path.Combine(tenantDir.FullName, "Settings.txt")); + + // Move InstalledPlugins.txt + File.Move(Path.Combine(appDataDir, "InstalledPlugins.txt"), Path.Combine(tenantDir.FullName, "InstalledPlugins.txt")); + + // Move SmartStore.db.sdf + var path = Path.Combine(appDataDir, "SmartStore.db.sdf"); + if (File.Exists(path)) + { + File.Move(path, Path.Combine(tenantDir.FullName, "SmartStore.db.sdf")); + } + + Func moveTenantFolder = (sourceFolder, targetFolder) => + { + var sourcePath = Path.Combine(appDataDir, sourceFolder); + + if (Directory.Exists(sourcePath)) + { + Directory.Move(sourcePath, Path.Combine(tenantDir.FullName, targetFolder ?? sourceFolder)); + return true; + } + + return false; + }; + + // Move tenant specific Folders + moveTenantFolder("ImportProfiles", null); + moveTenantFolder("ExportProfiles", null); + moveTenantFolder("Indexing", null); + moveTenantFolder("Lucene", null); + moveTenantFolder("_temp\\BizBackups", null); + moveTenantFolder("_temp\\ShopConnector", null); + + // Move all media files and folders to new subfolder "Default" + var mediaInfos = (new DirectoryInfo(CommonHelper.MapPath("~/Media"))).EnumerateFileSystemInfos().Where(x => !x.Name.IsCaseInsensitiveEqual("Default")); + var mediaFiles = mediaInfos.OfType(); + var mediaDirs = mediaInfos.OfType().ToArray(); + var tenantMediaDir = new DirectoryInfo(CommonHelper.MapPath("~/Media/Default")); + if (!tenantMediaDir.Exists) + { + tenantMediaDir.Create(); + } + + foreach (var dir in mediaDirs) + { + dir.MoveTo(Path.Combine(tenantMediaDir.FullName, dir.Name)); + } + + foreach (var file in mediaFiles) + { + file.MoveTo(Path.Combine(tenantMediaDir.FullName, file.Name)); + } + + return true; + } + + private bool IsPreTenancyVersion() + { + var appDataDir = CommonHelper.MapPath("~/App_Data"); + + return File.Exists(Path.Combine(appDataDir, "Settings.txt")) + && File.Exists(Path.Combine(appDataDir, "InstalledPlugins.txt")) + && !Directory.Exists(Path.Combine(appDataDir, "Tenants\\Default")); + } + + private void MigrateInitial() + { + var installedPlugins = PluginFileParser.ParseInstalledPluginsFile(); + if (installedPlugins.Count == 0) + return; + + var renamedPlugins = new List(); + + var pluginRenameMap = new Dictionary + { + { "CurrencyExchange.ECB", null /* null means: remove it */ }, + { "CurrencyExchange.MoneyConverter", null }, + { "ExternalAuth.OpenId", null }, + { "Tax.Free", null }, + { "Api.WebApi", "SmartStore.WebApi" }, + { "DiscountRequirement.MustBeAssignedToCustomerRole", "SmartStore.DiscountRules" }, + { "DiscountRequirement.HadSpentAmount", "SmartStore.DiscountRules" }, + { "DiscountRequirement.HasAllProducts", "SmartStore.DiscountRules" }, + { "DiscountRequirement.HasOneProduct", "SmartStore.DiscountRules" }, + { "DiscountRequirement.Store", "SmartStore.DiscountRules" }, + { "DiscountRequirement.BillingCountryIs", "SmartStore.DiscountRules" }, + { "DiscountRequirement.ShippingCountryIs", "SmartStore.DiscountRules" }, + { "DiscountRequirement.HasPaymentMethod", "SmartStore.DiscountRules.HasPaymentMethod" }, + { "DiscountRequirement.HasShippingOption", "SmartStore.DiscountRules.HasShippingOption" }, + { "DiscountRequirement.PurchasedAllProducts", "SmartStore.DiscountRules.PurchasedProducts" }, + { "DiscountRequirement.PurchasedOneProduct", "SmartStore.DiscountRules.PurchasedProducts" }, + { "PromotionFeed.Froogle", "SmartStore.GoogleMerchantCenter" }, + { "PromotionFeed.Billiger", "SmartStore.Billiger" }, + { "PromotionFeed.ElmarShopinfo", "SmartStore.ElmarShopinfo" }, + { "PromotionFeed.Guenstiger", "SmartStore.Guenstiger" }, + { "Payments.AccardaKar", "SmartStore.AccardaKar" }, + { "Payments.AmazonPay", "SmartStore.AmazonPay" }, + { "Developer.DevTools", "SmartStore.DevTools" }, + { "ExternalAuth.Facebook", "SmartStore.FacebookAuth" }, + { "ExternalAuth.Twitter", "SmartStore.TwitterAuth" }, + { "SMS.Clickatell", "SmartStore.Clickatell" }, + { "Widgets.GoogleAnalytics", "SmartStore.GoogleAnalytics" }, + { "Misc.DemoShop", "SmartStore.DemoShopControlling" }, + { "Admin.OrderNumberFormatter", "SmartStore.OrderNumberFormatter" }, + { "Admin.Debitoor", "SmartStore.Debitoor" }, { "Widgets.ETracker", "SmartStore.ETracker" }, { "Payments.PayPalDirect", "SmartStore.PayPal" }, { "Payments.PayPalStandard", "SmartStore.PayPal" }, { "Payments.PayPalExpress", "SmartStore.PayPal" }, - { "Developer.Glimpse", "SmartStore.Glimpse" }, - { "Import.Biz", "SmartStore.BizImporter" }, - { "Payments.Sofortueberweisung", "SmartStore.Sofortueberweisung" }, - { "Payments.PostFinanceECommerce", "SmartStore.PostFinanceECommerce" }, - { "Misc.MailChimp", "SmartStore.MailChimp" }, - { "Mobile.SMS.Verizon", "SmartStore.Verizon" }, - { "Widgets.LivePersonChat", "SmartStore.LivePersonChat" }, - { "Payments.CashOnDelivery", "SmartStore.OfflinePayment" }, - { "Payments.Invoice", "SmartStore.OfflinePayment" }, - { "Payments.PayInStore", "SmartStore.OfflinePayment" }, - { "Payments.Prepayment", "SmartStore.OfflinePayment" }, + { "Developer.Glimpse", "SmartStore.Glimpse" }, + { "Import.Biz", "SmartStore.BizImporter" }, + { "Payments.Sofortueberweisung", "SmartStore.Sofortueberweisung" }, + { "Payments.PostFinanceECommerce", "SmartStore.PostFinanceECommerce" }, + { "Misc.MailChimp", "SmartStore.MailChimp" }, + { "Mobile.SMS.Verizon", "SmartStore.Verizon" }, + { "Widgets.LivePersonChat", "SmartStore.LivePersonChat" }, + { "Payments.CashOnDelivery", "SmartStore.OfflinePayment" }, + { "Payments.Invoice", "SmartStore.OfflinePayment" }, + { "Payments.PayInStore", "SmartStore.OfflinePayment" }, + { "Payments.Prepayment", "SmartStore.OfflinePayment" }, { "Payments.IPaymentCreditCard", "SmartStore.IPayment" }, { "Payments.IPaymentDirectDebit", "SmartStore.IPayment" }, { "Payments.AuthorizeNet", "SmartStore.AuthorizeNet" }, @@ -384,43 +390,43 @@ private void MigrateInitial() { "Shipping.CanadaPost", "SmartStore.CanadaPost" }, { "Shipping.Fedex", "SmartStore.Fedex" }, { "Shipping.UPS", "SmartStore.UPS" }, - { "Payments.Manual", "SmartStore.OfflinePayment" }, + { "Payments.Manual", "SmartStore.OfflinePayment" }, { "Shipping.USPS", "SmartStore.USPS" }, { "Widgets.TrustedShopsSeal", "SmartStore.TrustedShops" }, { "Widgets.TrustedShopsCustomerReviews", "SmartStore.TrustedShops" }, { "Widgets.TrustedShopsCustomerProtection", "SmartStore.TrustedShops" }, { "Shipping.ByWeight", "SmartStore.ShippingByWeight" }, - { "Payments.DirectDebit", "SmartStore.OfflinePayment" }, - { "Tax.FixedRate", "SmartStore.Tax" }, - { "Tax.CountryStateZip", "SmartStore.Tax" }, + { "Payments.DirectDebit", "SmartStore.OfflinePayment" }, + { "Tax.FixedRate", "SmartStore.Tax" }, + { "Tax.CountryStateZip", "SmartStore.Tax" }, { "Shipping.ByTotal", "SmartStore.Shipping" }, { "Shipping.FixedRate", "SmartStore.Shipping" } - }; - - foreach (var name in installedPlugins) - { - if (pluginRenameMap.ContainsKey(name)) - { - string newName = pluginRenameMap[name]; - if (newName != null && !renamedPlugins.Contains(newName)) - { - renamedPlugins.Add(newName); - } - } - else - { - renamedPlugins.Add(name); - } - } - - PluginFileParser.SaveInstalledPluginsFile(renamedPlugins); - } + }; + + foreach (var name in installedPlugins) + { + if (pluginRenameMap.ContainsKey(name)) + { + string newName = pluginRenameMap[name]; + if (newName != null && !renamedPlugins.Contains(newName)) + { + renamedPlugins.Add(newName); + } + } + else + { + renamedPlugins.Add(name); + } + } + + PluginFileParser.SaveInstalledPluginsFile(renamedPlugins); + } private void DeleteSearchIndex() { - var tenantPath = CommonHelper.MapPath(DataSettings.Current.TenantPath); + var tenantPath = CommonHelper.MapPath(DataSettings.Current.TenantPath); - try + try { var indexingDir = new DirectoryInfo(Path.Combine(tenantPath, "Indexing")); if (indexingDir.Exists) @@ -441,19 +447,19 @@ private void DeleteSearchIndex() catch { } } - #endregion - - protected override void OnDispose(bool disposing) - { - if (disposing) - { - if (_logger != null) - { - _logger.Dispose(); - _logger = null; - } - } - } - } + #endregion + + protected override void OnDispose(bool disposing) + { + if (disposing) + { + if (_logger != null) + { + _logger.Dispose(); + _logger = null; + } + } + } + } } diff --git a/src/Libraries/SmartStore.Core/PagedList.cs b/src/Libraries/SmartStore.Core/PagedList.cs index 87af23cda8..96539bf667 100644 --- a/src/Libraries/SmartStore.Core/PagedList.cs +++ b/src/Libraries/SmartStore.Core/PagedList.cs @@ -28,13 +28,12 @@ protected PagedListBase(int pageIndex, int pageSize, int totalItemsCount) this.TotalCount = totalItemsCount; } - // only here for compat reasons with nc public void LoadPagedList(IPagedList pagedList) { this.Init(pagedList); } - public void Init(IPageable pageable) + protected void Init(IPageable pageable) { Guard.NotNull(pageable, "pageable"); @@ -63,24 +62,18 @@ public int TotalCount public int PageNumber { - get - { - return this.PageIndex + 1; - } - set - { - this.PageIndex = value - 1; - } + get => this.PageIndex + 1; + set => this.PageIndex = value - 1; } public int TotalPages { - get + get { - if (this.PageSize == 0) - return 0; + if (this.PageSize == 0) + return 0; - var total = this.TotalCount / this.PageSize; + var total = this.TotalCount / this.PageSize; if (this.TotalCount % this.PageSize > 0) total++; @@ -89,53 +82,17 @@ public int TotalPages } } - public bool HasPreviousPage - { - get - { - return this.PageIndex > 0; - } - } + public bool HasPreviousPage => this.PageIndex > 0; - public bool HasNextPage - { - get - { - return (this.PageIndex < (this.TotalPages - 1)); - } - } + public bool HasNextPage => (this.PageIndex < (this.TotalPages - 1)); - public int FirstItemIndex - { - get - { - return (this.PageIndex * this.PageSize) + 1; - } - } + public int FirstItemIndex => (this.PageIndex * this.PageSize) + 1; - public int LastItemIndex - { - get - { - return Math.Min(this.TotalCount, ((this.PageIndex * this.PageSize) + this.PageSize)); - } - } + public int LastItemIndex => Math.Min(this.TotalCount, ((this.PageIndex * this.PageSize) + this.PageSize)); - public bool IsFirstPage - { - get - { - return (this.PageIndex <= 0); - } - } + public bool IsFirstPage => (this.PageIndex <= 0); - public bool IsLastPage - { - get - { - return (this.PageIndex >= (this.TotalPages - 1)); - } - } + public bool IsLastPage => (this.PageIndex >= (this.TotalPages - 1)); public virtual IEnumerator GetEnumerator() { @@ -146,19 +103,19 @@ public virtual IEnumerator GetEnumerator() public class PagedList : PagedListBase { public PagedList(int pageIndex, int pageSize, int totalItemsCount) - : base(pageIndex, pageSize, totalItemsCount) + : base(pageIndex, pageSize, totalItemsCount) { } - public static PagedList Create(IEnumerable source, int pageIndex, int pageSize) - { - return new PagedList(source, pageIndex, pageSize); - } + public static PagedList Create(IEnumerable source, int pageIndex, int pageSize) + { + return new PagedList(source, pageIndex, pageSize); + } - public static PagedList Create(IEnumerable source, int pageIndex, int pageSize, int totalCount) - { - return new PagedList(source, pageIndex, pageSize, totalCount); - } - } + public static PagedList Create(IEnumerable source, int pageIndex, int pageSize, int totalCount) + { + return new PagedList(source, pageIndex, pageSize, totalCount); + } + } } diff --git a/src/Libraries/SmartStore.Core/PagedList`T.cs b/src/Libraries/SmartStore.Core/PagedList`T.cs index 2a84c165b4..3796a1350a 100644 --- a/src/Libraries/SmartStore.Core/PagedList`T.cs +++ b/src/Libraries/SmartStore.Core/PagedList`T.cs @@ -1,184 +1,189 @@ using System; +using System.Collections; using System.Collections.Generic; -using System.Linq; +using System.Collections.ObjectModel; using System.Data.Entity; using System.Data.Entity.Infrastructure; -using System.Collections; -using System.Collections.ObjectModel; +using System.Linq; using System.Threading.Tasks; namespace SmartStore.Core { - public class PagedList : IPagedList, IReadOnlyList, IReadOnlyCollection + public class PagedList : IPagedList { - private bool _isEfQuery; - private bool _queryIsPagedAlready; - private int? _totalCount; + private bool _isEfQuery; + private bool _queryIsPagedAlready; + private int? _totalCount; - private List _list; + private List _list; - /// The 0-based page index - public PagedList(IEnumerable source, int pageIndex, int pageSize) - { - Guard.NotNull(source, "source"); + /// The 0-based page index + public PagedList(IEnumerable source, int pageIndex, int pageSize) + { + Guard.NotNull(source, "source"); - Init(source.AsQueryable(), pageIndex, pageSize, null); - } + Init(source.AsQueryable(), pageIndex, pageSize, null); + } - /// The 0-based page index - public PagedList(IEnumerable source, int pageIndex, int pageSize, int totalCount) + /// The 0-based page index + public PagedList(IEnumerable source, int pageIndex, int pageSize, int totalCount) { Guard.NotNull(source, "source"); Init(source.AsQueryable(), pageIndex, pageSize, totalCount); } - private void Init(IQueryable source, int pageIndex, int pageSize, int? totalCount) - { - Guard.NotNull(source, nameof(source)); - Guard.PagingArgsValid(pageIndex, pageSize, "pageIndex", "pageSize"); - - SourceQuery = source; - PageIndex = pageIndex; - PageSize = pageSize; - - _totalCount = totalCount; - _queryIsPagedAlready = totalCount.HasValue; - _isEfQuery = source.Provider is IDbAsyncQueryProvider; - } - - private void EnsureIsLoaded() - { - if (_list == null) - { - if (_totalCount == null) - { - _totalCount = SourceQuery.Count(); - } - - if (_queryIsPagedAlready) - { - _list = SourceQuery.ToList(); - } - else - { - _list = ApplyPaging(SourceQuery).ToList(); - } - } - } - - private async Task EnsureIsLoadedAsync() - { - if (_list == null) - { - if (_totalCount == null) - { - _totalCount = await SourceQuery.CountAsync(); - } - - if (_queryIsPagedAlready) - { - _list = await SourceQuery.ToListAsync(); - } - else - { - _list = await ApplyPaging(SourceQuery).ToListAsync(); - } - } - } - - #region IPageable Members - - public IQueryable SourceQuery { get; private set; } - - 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))); - - return this; - } - - public IQueryable ApplyPaging(IQueryable query) - { - if (PageIndex == 0 && PageSize == int.MaxValue) - { - // Paging unnecessary - return query; - } - else - { - var skip = PageIndex * PageSize; - if (_isEfQuery) - { - return query.Skip(() => skip).Take(() => PageSize); - } - else - { - return query.Skip(skip).Take(PageSize); - } - } - } - - public IPagedList Load(bool force = false) - { - // Returns instance for chaining. - if (force && _list != null) - { - _list.Clear(); - _list = null; - } - - EnsureIsLoaded(); - - return this; - } - - public async Task> LoadAsync(bool force = false) - { - // Returns instance for chaining. - if (force && _list != null) - { - _list.Clear(); - _list = null; - } - - await EnsureIsLoadedAsync(); - - return this; - } - - public int PageIndex { get; set; } - - public int PageSize { get; set; } - - public int TotalCount + private void Init(IQueryable source, int pageIndex, int pageSize, int? totalCount) { - get - { - if (!_totalCount.HasValue) - { - _totalCount = SourceQuery.Count(); - } - - return _totalCount.Value; - } - set - { - _totalCount = value; - } + Guard.NotNull(source, nameof(source)); + Guard.PagingArgsValid(pageIndex, pageSize, "pageIndex", "pageSize"); + + SourceQuery = source; + PageIndex = pageIndex; + PageSize = pageSize; + + _totalCount = totalCount; + _queryIsPagedAlready = totalCount.HasValue; + _isEfQuery = source.Provider is IDbAsyncQueryProvider; } - public int PageNumber + private void EnsureIsLoaded() { - get + if (_list == null) { - return this.PageIndex + 1; + if (_totalCount == null) + { + _totalCount = SourceQuery.Count(); + } + + if (_queryIsPagedAlready) + { + _list = SourceQuery.ToList(); + } + else + { + _list = ApplyPaging(SourceQuery).ToList(); + } } - set + } + + private async Task EnsureIsLoadedAsync() + { + if (_list == null) + { + if (_totalCount == null) + { + _totalCount = await SourceQuery.CountAsync(); + } + + if (_queryIsPagedAlready) + { + _list = await SourceQuery.ToListAsync(); + } + else + { + _list = await ApplyPaging(SourceQuery).ToListAsync(); + } + } + } + + #region IPageable Members + + public IQueryable SourceQuery { get; private set; } + + 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))); + + return this; + } + + public IQueryable ApplyPaging(IQueryable query) + { + if (PageIndex == 0 && PageSize == int.MaxValue) + { + // Paging unnecessary + return query; + } + else + { + var skip = PageIndex * PageSize; + if (_isEfQuery) + { + return skip == 0 + ? query.Take(() => PageSize) + : query.Skip(() => skip).Take(() => PageSize); + } + else + { + return skip == 0 + ? query.Take(PageSize) + : query.Skip(skip).Take(PageSize); + } + } + } + + public IPagedList Load(bool force = false) + { + // Returns instance for chaining. + if (force && _list != null) + { + _list.Clear(); + _list = null; + } + + EnsureIsLoaded(); + + return this; + } + + public async Task> LoadAsync(bool force = false) + { + // Returns instance for chaining. + if (force && _list != null) + { + _list.Clear(); + _list = null; + } + + await EnsureIsLoadedAsync(); + + return this; + } + + public int PageIndex { get; set; } + + public int PageSize { get; set; } + + public async Task GetTotalCountAsync() + { + if (!_totalCount.HasValue) + { + _totalCount = await SourceQuery.CountAsync(); + } + + return _totalCount.Value; + } + + public int TotalCount + { + get { - this.PageIndex = value - 1; + if (!_totalCount.HasValue) + { + _totalCount = SourceQuery.Count(); + } + + return _totalCount.Value; } + set => _totalCount = value; + } + + public int PageNumber + { + get => this.PageIndex + 1; + set => this.PageIndex = value - 1; } public int TotalPages @@ -194,170 +199,131 @@ public int TotalPages } } - public bool HasPreviousPage + public bool HasPreviousPage => this.PageIndex > 0; + + public bool HasNextPage => (this.PageIndex < (this.TotalPages - 1)); + + public int FirstItemIndex => (this.PageIndex * this.PageSize) + 1; + + public int LastItemIndex => Math.Min(this.TotalCount, ((this.PageIndex * this.PageSize) + this.PageSize)); + + public bool IsFirstPage => (this.PageIndex <= 0); + + public bool IsLastPage => (this.PageIndex >= (this.TotalPages - 1)); + + #endregion + + #region IList Members + + public void Add(T item) + { + EnsureIsLoaded(); + _list.Add(item); + } + + public void Clear() { - get + if (_list != null) { - return this.PageIndex > 0; + _list.Clear(); + _list = null; } } - public bool HasNextPage + public bool Contains(T item) { - get - { - return (this.PageIndex < (this.TotalPages - 1)); - } + EnsureIsLoaded(); + return _list.Contains(item); } - public int FirstItemIndex + public void CopyTo(T[] array, int arrayIndex) { - get + EnsureIsLoaded(); + _list.CopyTo(array, arrayIndex); + } + + public bool Remove(T item) + { + if (_list != null) { - return (this.PageIndex * this.PageSize) + 1; + return _list.Remove(item); } + + return false; } - public int LastItemIndex + public int Count { get { - return Math.Min(this.TotalCount, ((this.PageIndex * this.PageSize) + this.PageSize)); + EnsureIsLoaded(); + return _list.Count; } } - public bool IsFirstPage + public bool IsReadOnly => false; + + public int IndexOf(T item) { - get + EnsureIsLoaded(); + return _list.IndexOf(item); + } + + public void Insert(int index, T item) + { + EnsureIsLoaded(); + _list.Insert(index, item); + } + + public void RemoveAt(int index) + { + if (_list != null) { - return (this.PageIndex <= 0); + _list.RemoveAt(index); } } - public bool IsLastPage + public T this[int index] { get { - return (this.PageIndex >= (this.TotalPages - 1)); + EnsureIsLoaded(); + return _list[index]; } + set + { + EnsureIsLoaded(); + _list[index] = value; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + public IEnumerator GetEnumerator() + { + EnsureIsLoaded(); + return _list.GetEnumerator(); + } + + #endregion + + #region Utils + + public void AddRange(IEnumerable collection) + { + EnsureIsLoaded(); + _list.AddRange(collection); + } + + public ReadOnlyCollection AsReadOnly() + { + EnsureIsLoaded(); + return _list.AsReadOnly(); } - #endregion - - #region IList Members - - public void Add(T item) - { - EnsureIsLoaded(); - _list.Add(item); - } - - public void Clear() - { - if (_list != null) - { - _list.Clear(); - _list = null; - } - } - - public bool Contains(T item) - { - EnsureIsLoaded(); - return _list.Contains(item); - } - - public void CopyTo(T[] array, int arrayIndex) - { - EnsureIsLoaded(); - _list.CopyTo(array, arrayIndex); - } - - public bool Remove(T item) - { - if (_list != null) - { - return _list.Remove(item); - } - - return false; - } - - public int Count - { - get - { - EnsureIsLoaded(); - return _list.Count; - } - } - - public bool IsReadOnly - { - get { return false; } - } - - public int IndexOf(T item) - { - EnsureIsLoaded(); - return _list.IndexOf(item); - } - - public void Insert(int index, T item) - { - EnsureIsLoaded(); - _list.Insert(index, item); - } - - public void RemoveAt(int index) - { - if (_list != null) - { - _list.RemoveAt(index); - } - } - - public T this[int index] - { - get - { - EnsureIsLoaded(); - return _list[index]; - } - set - { - EnsureIsLoaded(); - _list[index] = value; - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return this.GetEnumerator(); - } - - public IEnumerator GetEnumerator() - { - EnsureIsLoaded(); - return _list.GetEnumerator(); - } - - #endregion - - #region Utils - - public void AddRange(IEnumerable collection) - { - EnsureIsLoaded(); - _list.AddRange(collection); - } - - public ReadOnlyCollection AsReadOnly() - { - EnsureIsLoaded(); - return _list.AsReadOnly(); - } - - #endregion - } + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Plugins/BasePlugin.cs b/src/Libraries/SmartStore.Core/Plugins/BasePlugin.cs index 80ae85fd5a..560532bcd6 100644 --- a/src/Libraries/SmartStore.Core/Plugins/BasePlugin.cs +++ b/src/Libraries/SmartStore.Core/Plugins/BasePlugin.cs @@ -10,7 +10,7 @@ public abstract class BasePlugin : IPlugin /// /// Install plugin /// - public virtual void Install() + public virtual void Install() { PluginManager.MarkPluginAsInstalled(this.PluginDescriptor.SystemName); } @@ -18,7 +18,7 @@ public virtual void Install() /// /// Uninstall plugin /// - public virtual void Uninstall() + public virtual void Uninstall() { PluginManager.MarkPluginAsUninstalled(this.PluginDescriptor.SystemName); } diff --git a/src/Libraries/SmartStore.Core/Plugins/IConfigurable.cs b/src/Libraries/SmartStore.Core/Plugins/IConfigurable.cs index 5a0308eb79..aaee54810c 100644 --- a/src/Libraries/SmartStore.Core/Plugins/IConfigurable.cs +++ b/src/Libraries/SmartStore.Core/Plugins/IConfigurable.cs @@ -1,19 +1,18 @@ -using System; -using System.Web.Routing; +using System.Web.Routing; namespace SmartStore.Core.Plugins { - /// - /// Marks a concrete provider or plugin implementation as configurable through the backend - /// - public interface IConfigurable - { - /// - /// Gets a route for provider or plugin configuration - /// - /// Action name - /// Controller name - /// Route values - void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues); - } + /// + /// Marks a concrete provider or plugin implementation as configurable through the backend + /// + public interface IConfigurable + { + /// + /// Gets a route for provider or plugin configuration + /// + /// Action name + /// Controller name + /// Route values + void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues); + } } diff --git a/src/Libraries/SmartStore.Core/Plugins/ICookiePublisher.cs b/src/Libraries/SmartStore.Core/Plugins/ICookiePublisher.cs new file mode 100644 index 0000000000..c069257aed --- /dev/null +++ b/src/Libraries/SmartStore.Core/Plugins/ICookiePublisher.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; +using SmartStore.Core.Domain.Localization; + +namespace SmartStore.Core.Plugins +{ + /// + /// Marks a module as a cookie publisher. + /// + public interface ICookiePublisher + { + /// + /// Gets the cookie info of the cookie publisher (e.g. plugin or other module). + /// + IEnumerable GetCookieInfo(); + } + + /// + /// Plugin cookie infos. + /// + public class CookieInfo : BaseEntity, ILocalizedEntity + { + /// + /// Name of the plugin. + /// + [DataMember] + public string Name { get; set; } + + /// + /// Description of the cookie (e.g. purpose of using the cookie). + /// + [DataMember] + public string Description { get; set; } + + /// + /// SelectedStoreIds + /// + [DataMember] + public int[] SelectedStoreIds { get; set; } + + /// + /// Type of the cookie. + /// + [DataMember] + public CookieType CookieType { get; set; } + } + + /// + /// Type of the cookie. + /// + public enum CookieType + { + Required, + Analytics, + ThirdParty + } +} diff --git a/src/Libraries/SmartStore.Core/Plugins/IPluginFinder.cs b/src/Libraries/SmartStore.Core/Plugins/IPluginFinder.cs index 51afd082ee..8240e3f247 100644 --- a/src/Libraries/SmartStore.Core/Plugins/IPluginFinder.cs +++ b/src/Libraries/SmartStore.Core/Plugins/IPluginFinder.cs @@ -3,51 +3,51 @@ namespace SmartStore.Core.Plugins { - /// - /// Plugin finder - /// + /// + /// Plugin finder + /// public interface IPluginFinder { - /// - /// Gets plugins - /// - /// The type of plugins to get. - /// A value indicating whether to load only installed plugins - /// Plugins - IEnumerable GetPlugins(bool installedOnly = true) where T : class, IPlugin; + /// + /// Gets plugins + /// + /// The type of plugins to get. + /// A value indicating whether to load only installed plugins + /// Plugins + IEnumerable GetPlugins(bool installedOnly = true) where T : class, IPlugin; - /// - /// Get plugin descriptors - /// - /// A value indicating whether to load only installed plugins - /// Plugin descriptors - IEnumerable GetPluginDescriptors(bool installedOnly = true); + /// + /// Get plugin descriptors + /// + /// A value indicating whether to load only installed plugins + /// Plugin descriptors + IEnumerable GetPluginDescriptors(bool installedOnly = true); - /// - /// Get plugin descriptors - /// - /// The type of plugin to get. - /// A value indicating whether to load only installed plugins - /// Plugin descriptors - IEnumerable GetPluginDescriptors(bool installedOnly = true) where T : class, IPlugin; + /// + /// Get plugin descriptors + /// + /// The type of plugin to get. + /// A value indicating whether to load only installed plugins + /// Plugin descriptors + IEnumerable GetPluginDescriptors(bool installedOnly = true) where T : class, IPlugin; PluginDescriptor GetPluginDescriptorByAssembly(Assembly assembly, bool installedOnly = true); - /// - /// Get a plugin descriptor by its system name - /// - /// Plugin system name - /// A value indicating whether to load only installed plugins - /// >Plugin descriptor + /// + /// Get a plugin descriptor by its system name + /// + /// Plugin system name + /// A value indicating whether to load only installed plugins + /// >Plugin descriptor PluginDescriptor GetPluginDescriptorBySystemName(string systemName, bool installedOnly = true); - /// - /// Get a plugin descriptor by its system name - /// - /// The type of plugin to get. - /// Plugin system name - /// A value indicating whether to load only installed plugins - /// >Plugin descriptor + /// + /// Get a plugin descriptor by its system name + /// + /// The type of plugin to get. + /// Plugin system name + /// A value indicating whether to load only installed plugins + /// >Plugin descriptor PluginDescriptor GetPluginDescriptorBySystemName(string systemName, bool installedOnly = true) where T : class, IPlugin; } } diff --git a/src/Libraries/SmartStore.Core/Plugins/IPreApplicationStart.cs b/src/Libraries/SmartStore.Core/Plugins/IPreApplicationStart.cs deleted file mode 100644 index a543f3dc0e..0000000000 --- a/src/Libraries/SmartStore.Core/Plugins/IPreApplicationStart.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SmartStore.Core.Plugins -{ - public interface IPreApplicationStart - { - void Start(); - } -} diff --git a/src/Libraries/SmartStore.Core/Plugins/LoadPluginResult.cs b/src/Libraries/SmartStore.Core/Plugins/LoadPluginResult.cs index 2e51056df1..c153efcdd9 100644 --- a/src/Libraries/SmartStore.Core/Plugins/LoadPluginResult.cs +++ b/src/Libraries/SmartStore.Core/Plugins/LoadPluginResult.cs @@ -1,16 +1,12 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; +using System.IO; namespace SmartStore.Core.Plugins { - public class LoadPluginResult - { - public FileInfo DescriptionFile { get; set; } - public PluginDescriptor Descriptor { get; set; } - public bool IsIncompatible { get; set; } - public bool Success { get; set; } - } + public class LoadPluginResult + { + public FileInfo DescriptionFile { get; set; } + public PluginDescriptor Descriptor { get; set; } + public bool IsIncompatible { get; set; } + public bool Success { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Plugins/PluginDescriptor.cs b/src/Libraries/SmartStore.Core/Plugins/PluginDescriptor.cs index ab6a32754e..d27c5ad168 100644 --- a/src/Libraries/SmartStore.Core/Plugins/PluginDescriptor.cs +++ b/src/Libraries/SmartStore.Core/Plugins/PluginDescriptor.cs @@ -1,41 +1,39 @@ using System; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Linq; using System.Reflection; +using System.Runtime.Serialization; using System.Xml; using SmartStore.Core.Infrastructure; -using System.Linq; -using SmartStore.Utilities; -using System.Runtime.Serialization; namespace SmartStore.Core.Plugins { - /// - /// Information about an assembly referenced by a plugin. - /// - public sealed class AssemblyReference - { - /// - /// File in ASP.NET temp folder - /// - public FileInfo File { get; internal set; } - /// - /// The original assembly file that a shadow copy was made from it - /// - public FileInfo OriginalFile { get; internal set; } - /// - /// The assembly that has been shadow copied and was loaded into the AppDomain - /// - public Assembly Assembly { get; internal set; } - - /// - /// Exception thrown during activation/probing - /// - internal Exception ActivationException { get; set; } - } - - [DataContract] + /// + /// Information about an assembly referenced by a plugin. + /// + public sealed class AssemblyReference + { + /// + /// File in ASP.NET temp folder + /// + public FileInfo File { get; internal set; } + /// + /// The original assembly file that a shadow copy was made from it + /// + public FileInfo OriginalFile { get; internal set; } + /// + /// The assembly that has been shadow copied and was loaded into the AppDomain + /// + public Assembly Assembly { get; internal set; } + + /// + /// Exception thrown during activation/probing + /// + internal Exception ActivationException { get; set; } + } + + [DataContract] public class PluginDescriptor : IComparable { private string _resourceRootKey; @@ -47,13 +45,13 @@ public PluginDescriptor() MinAppVersion = SmartStoreVersion.Version; } - // Unit tests - public PluginDescriptor(Assembly referencedAssembly, FileInfo originalAssemblyFile, Type pluginClrType) - : this() - { - Assembly = new AssemblyReference { Assembly = referencedAssembly, OriginalFile = originalAssemblyFile, File = originalAssemblyFile }; - PluginClrType = pluginClrType; - } + // Unit tests + public PluginDescriptor(Assembly referencedAssembly, FileInfo originalAssemblyFile, Type pluginClrType) + : this() + { + Assembly = new AssemblyReference { Assembly = referencedAssembly, OriginalFile = originalAssemblyFile, File = originalAssemblyFile }; + PluginClrType = pluginClrType; + } /// /// Plugin file name @@ -65,29 +63,29 @@ public PluginDescriptor(Assembly referencedAssembly, FileInfo originalAssemblyFi /// public string PhysicalPath { get; set; } - /// - /// Gets the folder name - /// - [DataMember] - public string FolderName { get; internal set; } - - /// - /// The virtual path of the runtime plugin - /// - public string VirtualPath { get; set; } - - /// - /// Gets the file name of the brand image (without path) - /// or an empty string if no image is specified - /// - public string BrandImageFileName + /// + /// Gets the folder name + /// + [DataMember] + public string FolderName { get; internal set; } + + /// + /// The virtual path of the runtime plugin + /// + public string VirtualPath { get; set; } + + /// + /// Gets the file name of the brand image (without path) + /// or an empty string if no image is specified + /// + public string BrandImageFileName { get { if (_brandImageFileName == null) { // "null" means we haven't checked yet! - var filesToCheck = new [] { "branding.png", "branding.gif", "branding.jpg", "branding.jpeg" }; + var filesToCheck = new[] { "branding.png", "branding.gif", "branding.jpg", "branding.jpeg" }; var dir = this.PhysicalPath; foreach (var file in filesToCheck) { @@ -112,151 +110,143 @@ public string BrandImageFileName /// public Type PluginClrType { get; set; } - /// - /// The plugin assembly. - /// - public AssemblyReference Assembly { get; internal set; } = new AssemblyReference(); + /// + /// The plugin assembly. + /// + public AssemblyReference Assembly { get; internal set; } = new AssemblyReference(); - /// - /// List of assemblies files found in the plugin folder, except the main plugin assembly. - /// - internal AssemblyReference[] ReferencedLocalAssemblies { get; set; } = new AssemblyReference[0]; + /// + /// List of assemblies files found in the plugin folder, except the main plugin assembly. + /// + internal AssemblyReference[] ReferencedLocalAssemblies { get; set; } = new AssemblyReference[0]; - /// - /// Gets or sets the plugin group - /// - [DataMember] - public string Group { get; internal set; } + /// + /// Gets or sets the plugin group + /// + [DataMember] + public string Group { get; internal set; } - public bool IsInKnownGroup - { - get - { - return PluginFileParser.KnownGroups.Contains(this.Group, StringComparer.OrdinalIgnoreCase); - } - } + public bool IsInKnownGroup => PluginFileParser.KnownGroups.Contains(this.Group, StringComparer.OrdinalIgnoreCase); /// /// Gets or sets the friendly name /// [DataMember] - public string FriendlyName { get; set; } + public string FriendlyName { get; set; } /// /// Gets or sets the system name /// [DataMember] - public string SystemName { get; set; } + public string SystemName { get; set; } /// /// Gets the plugin description /// [DataMember] - public string Description { get; set; } + public string Description { get; set; } /// /// Gets or sets the version /// [DataMember] - public Version Version { get; set; } + public Version Version { get; set; } /// /// Gets or sets the minimum supported app version /// [DataMember] - public Version MinAppVersion { get; set; } + public Version MinAppVersion { get; set; } /// /// Gets or sets the author /// [DataMember] - public string Author { get; set; } + public string Author { get; set; } - /// - /// Gets or sets the project/marketplace url - /// - [DataMember] - public string Url { get; set; } + /// + /// Gets or sets the project/marketplace url + /// + [DataMember] + public string Url { get; set; } /// /// Gets or sets the display order /// [DataMember] - public int DisplayOrder { get; set; } + public int DisplayOrder { get; set; } /// /// Gets or sets a value indicating whether the plugin is installed /// [DataMember] - public bool Installed { get; set; } + public bool Installed { get; set; } - /// - /// Gets a value indicating whether the plugin is incompatible with the current application version - /// - [DataMember] - public bool Incompatible { get; set; } - - /// - /// Gets or sets the value indicating whether the plugin is configurable - /// - /// - /// A plugin is configurable when it implements the interface - /// - [DataMember] - public bool IsConfigurable { get; set; } - - /// - /// Gets or sets the root key of string resources. - /// - /// - /// Tries to get it from first entry of resource XML file if not specified. - /// In that case the first resource name should not contain a dot if it's not part of the root key. - /// Otherwise you get the wrong root key. - /// - public string ResourceRootKey - { - get { - if (_resourceRootKey == null) - { - _resourceRootKey = ""; - - try - { - // Try to get root-key from first entry of XML file - var localizationDir = new DirectoryInfo(Path.Combine(PhysicalPath, "Localization")); - - if (localizationDir.Exists) - { - var localizationFile = localizationDir.EnumerateFiles("*.xml").FirstOrDefault(); - if (localizationFile != null) - { - XmlDocument doc = new XmlDocument(); - doc.Load(localizationFile.FullName); - var key = doc.SelectSingleNode(@"//Language/LocaleResource")?.Attributes["Name"]?.InnerText; - if (key.HasValue() && key.Contains('.')) - { - _resourceRootKey = key.Substring(0, key.LastIndexOf('.')); - } - } - } - } - catch (Exception ex) - { - ex.Dump(); - } - } - - return _resourceRootKey; - } - set { - _resourceRootKey = value; - } - } + /// + /// Gets a value indicating whether the plugin is incompatible with the current application version + /// + [DataMember] + public bool Incompatible { get; set; } + + /// + /// Gets or sets the value indicating whether the plugin is configurable + /// + /// + /// A plugin is configurable when it implements the interface + /// + [DataMember] + public bool IsConfigurable { get; set; } + + /// + /// Gets or sets the root key of string resources. + /// + /// + /// Tries to get it from first entry of resource XML file if not specified. + /// In that case the first resource name should not contain a dot if it's not part of the root key. + /// Otherwise you get the wrong root key. + /// + public string ResourceRootKey + { + get + { + if (_resourceRootKey == null) + { + _resourceRootKey = ""; + + try + { + // Try to get root-key from first entry of XML file + var localizationDir = new DirectoryInfo(Path.Combine(PhysicalPath, "Localization")); + + if (localizationDir.Exists) + { + var localizationFile = localizationDir.EnumerateFiles("*.xml").FirstOrDefault(); + if (localizationFile != null) + { + XmlDocument doc = new XmlDocument(); + doc.Load(localizationFile.FullName); + var key = doc.SelectSingleNode(@"//Language/LocaleResource")?.Attributes["Name"]?.InnerText; + if (key.HasValue() && key.Contains('.')) + { + _resourceRootKey = key.Substring(0, key.LastIndexOf('.')); + } + } + } + } + catch (Exception ex) + { + ex.Dump(); + } + } + + return _resourceRootKey; + } + set => _resourceRootKey = value; + } public T Instance() where T : class, IPlugin { - object instance; - if (!EngineContext.Current.ContainerManager.TryResolve(PluginClrType, null, out instance)) + if (!EngineContext.Current.ContainerManager.TryResolve(PluginClrType, null, out var instance)) { // Not registered instance = EngineContext.Current.ContainerManager.ResolveUnregistered(PluginClrType); @@ -274,20 +264,20 @@ public IPlugin Instance() return Instance(); } - [SuppressMessage("ReSharper", "StringCompareToIsCultureSpecific")] - public int CompareTo(PluginDescriptor other) + [SuppressMessage("ReSharper", "StringCompareToIsCultureSpecific")] + public int CompareTo(PluginDescriptor other) { - if (DisplayOrder != other.DisplayOrder) - return DisplayOrder.CompareTo(other.DisplayOrder); - else if (FriendlyName != null) - return FriendlyName.CompareTo(other.FriendlyName); - return 0; + if (DisplayOrder != other.DisplayOrder) + return DisplayOrder.CompareTo(other.DisplayOrder); + else if (FriendlyName != null) + return FriendlyName.CompareTo(other.FriendlyName); + return 0; } - public string GetSettingKey(string name) - { - return "PluginSetting.{0}.{1}".FormatWith(SystemName, name); - } + public string GetSettingKey(string name) + { + return "PluginSetting.{0}.{1}".FormatWith(SystemName, name); + } public override string ToString() { diff --git a/src/Libraries/SmartStore.Core/Plugins/PluginFileParser.cs b/src/Libraries/SmartStore.Core/Plugins/PluginFileParser.cs index 10e0ce284f..1ac124939e 100644 --- a/src/Libraries/SmartStore.Core/Plugins/PluginFileParser.cs +++ b/src/Libraries/SmartStore.Core/Plugins/PluginFileParser.cs @@ -17,12 +17,12 @@ internal class GroupComparer : Comparer { public override int Compare(string x, string y) { - return Array.FindIndex( KnownGroups, s => s == x) - Array.FindIndex(KnownGroups, s => s == y); + return Array.FindIndex(KnownGroups, s => s == x) - Array.FindIndex(KnownGroups, s => s == y); } } - - internal readonly static string[] KnownGroups = new string[] - { + + internal readonly static string[] KnownGroups = new string[] + { "Admin", "Marketing", "Payment", @@ -30,47 +30,47 @@ public override int Compare(string x, string y) "Tax", "Analytics", "CMS", - "Media", + "Media", "SEO", "Data", "Globalization", "Api", - "Mobile", + "Mobile", "Social", - "Security", + "Security", "Developer", - "Sales", - "Design", - "Performance", - "B2B", - "Storefront", - "Law" + "Sales", + "Design", + "Performance", + "B2B", + "Storefront", + "Law" }; public readonly static IComparer KnownGroupComparer = new GroupComparer(); - public readonly static string InstalledPluginsFilePath; + public readonly static string InstalledPluginsFilePath; - static PluginFileParser() - { - InstalledPluginsFilePath = Path.Combine(CommonHelper.MapPath(DataSettings.Current.TenantPath), "InstalledPlugins.txt"); - } + static PluginFileParser() + { + InstalledPluginsFilePath = Path.Combine(CommonHelper.MapPath(DataSettings.Current.TenantPath), "InstalledPlugins.txt"); + } - public static HashSet ParseInstalledPluginsFile(string filePath = null) + public static HashSet ParseInstalledPluginsFile(string filePath = null) { - filePath = filePath ?? InstalledPluginsFilePath; + filePath = filePath ?? InstalledPluginsFilePath; - var lines = new HashSet(StringComparer.OrdinalIgnoreCase); + var lines = new HashSet(StringComparer.OrdinalIgnoreCase); - // Read and parse the file + // Read and parse the file if (!File.Exists(filePath)) - return lines; + return lines; var text = File.ReadAllText(filePath); if (text.IsEmpty()) - { - return lines; - } - + { + return lines; + } + using (var reader = new StringReader(text)) { string str; @@ -88,10 +88,10 @@ public static HashSet ParseInstalledPluginsFile(string filePath = null) public static void SaveInstalledPluginsFile(ICollection pluginSystemNames, string filePath = null) { - if (pluginSystemNames == null || pluginSystemNames.Count == 0) + if (pluginSystemNames == null) return; - filePath = filePath ?? InstalledPluginsFilePath; + filePath = filePath ?? InstalledPluginsFilePath; string result = ""; foreach (var sn in pluginSystemNames) @@ -108,9 +108,9 @@ public static PluginDescriptor ParsePluginDescriptionFile(string filePath) if (String.IsNullOrEmpty(text)) return descriptor; - string dirName = Path.GetDirectoryName(filePath); - descriptor.PhysicalPath = dirName; - descriptor.FolderName = new DirectoryInfo(dirName).Name; + string dirName = Path.GetDirectoryName(filePath); + descriptor.PhysicalPath = dirName; + descriptor.FolderName = new DirectoryInfo(dirName).Name; var settings = new List(); using (var reader = new StringReader(text)) @@ -171,9 +171,9 @@ public static PluginDescriptor ParsePluginDescriptionFile(string filePath) case "Author": descriptor.Author = value; break; - case "Url": - descriptor.Url = value; - break; + case "Url": + descriptor.Url = value; + break; case "DisplayOrder": { int displayOrder; @@ -184,31 +184,31 @@ public static PluginDescriptor ParsePluginDescriptionFile(string filePath) case "FileName": descriptor.PluginFileName = value; break; - case "ResourceRootKey": - descriptor.ResourceRootKey = value; - break; + case "ResourceRootKey": + descriptor.ResourceRootKey = value; + break; } } - if (IsKnownGroup(group)) - { - descriptor.Group = group; - } - else - { - descriptor.Group = "Misc"; - } + if (IsKnownGroup(group)) + { + descriptor.Group = group; + } + else + { + descriptor.Group = "Misc"; + } return descriptor; } - private static bool IsKnownGroup(string group) - { - if (group.IsEmpty()) - return false; - return KnownGroups.Contains(group, StringComparer.OrdinalIgnoreCase); - } - + private static bool IsKnownGroup(string group) + { + if (group.IsEmpty()) + return false; + return KnownGroups.Contains(group, StringComparer.OrdinalIgnoreCase); + } + public static void SavePluginDescriptionFile(PluginDescriptor plugin) { if (plugin == null) @@ -232,10 +232,10 @@ public static void SavePluginDescriptionFile(PluginDescriptor plugin) keyValues.Add(new KeyValuePair("Version", plugin.Version.ToString())); keyValues.Add(new KeyValuePair("MinAppVersion", string.Join(",", plugin.MinAppVersion))); keyValues.Add(new KeyValuePair("Author", plugin.Author)); - keyValues.Add(new KeyValuePair("Url", plugin.Url)); + keyValues.Add(new KeyValuePair("Url", plugin.Url)); keyValues.Add(new KeyValuePair("DisplayOrder", plugin.DisplayOrder.ToString())); keyValues.Add(new KeyValuePair("FileName", plugin.PluginFileName)); - keyValues.Add(new KeyValuePair("ResourceRootKey", plugin.ResourceRootKey)); + keyValues.Add(new KeyValuePair("ResourceRootKey", plugin.ResourceRootKey)); var sb = new StringBuilder(); for (int i = 0; i < keyValues.Count; i++) @@ -243,10 +243,11 @@ public static void SavePluginDescriptionFile(PluginDescriptor plugin) var key = keyValues[i].Key; var value = keyValues[i].Value; sb.AppendFormat("{0}: {1}", key, value); - if (i != keyValues.Count -1) + if (i != keyValues.Count - 1) sb.Append(Environment.NewLine); } - //save the file + + // Save the file File.WriteAllText(filePath, sb.ToString()); } } diff --git a/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs b/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs index 4eddd033dd..86b20a756d 100644 --- a/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs +++ b/src/Libraries/SmartStore.Core/Plugins/PluginFinder.cs @@ -7,130 +7,130 @@ namespace SmartStore.Core.Plugins { public class PluginFinder : IPluginFinder { - private IList _plugins; - private readonly IDictionary _nameMap = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - private readonly IDictionary _assemblyMap = new Dictionary(); - - private static readonly object _lock = new object(); - - private PluginFinder() - { - lock (_lock) - { - LoadPlugins(); - } - } - - public static IPluginFinder Current { get; } = new PluginFinder(); - - private void LoadPlugins() - { - if (_plugins != null) - return; - - var plugins = PluginManager.ReferencedPlugins.ToList(); - plugins.Sort(); //sort - _plugins = plugins; - - foreach (var plugin in plugins) - { - _nameMap[plugin.SystemName] = plugin; - if (plugin.Assembly.Assembly != null) - { - _assemblyMap[plugin.Assembly.Assembly] = plugin; - } - } - } - - /// - /// Gets plugins - /// - /// The type of plugins to get. - /// A value indicating whether to load only installed plugins - /// Plugins - public virtual IEnumerable GetPlugins(bool installedOnly = true) where T : class, IPlugin + private IList _plugins; + private readonly IDictionary _nameMap = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly IDictionary _assemblyMap = new Dictionary(); + + private static readonly object _lock = new object(); + + private PluginFinder() + { + lock (_lock) + { + LoadPlugins(); + } + } + + public static IPluginFinder Current { get; } = new PluginFinder(); + + private void LoadPlugins() + { + if (_plugins != null) + return; + + var plugins = PluginManager.ReferencedPlugins.ToList(); + plugins.Sort(); //sort + _plugins = plugins; + + foreach (var plugin in plugins) + { + _nameMap[plugin.SystemName] = plugin; + if (plugin.Assembly.Assembly != null) + { + _assemblyMap[plugin.Assembly.Assembly] = plugin; + } + } + } + + /// + /// Gets plugins + /// + /// The type of plugins to get. + /// A value indicating whether to load only installed plugins + /// Plugins + public virtual IEnumerable GetPlugins(bool installedOnly = true) where T : class, IPlugin { foreach (var plugin in _plugins) if (typeof(T).IsAssignableFrom(plugin.PluginClrType)) if (!installedOnly || plugin.Installed) - yield return plugin.Instance(); + yield return plugin.Instance(); } - /// - /// Get plugin descriptors - /// - /// A value indicating whether to load only installed plugins - /// Plugin descriptors - public virtual IEnumerable GetPluginDescriptors(bool installedOnly = true) + /// + /// Get plugin descriptors + /// + /// A value indicating whether to load only installed plugins + /// Plugin descriptors + public virtual IEnumerable GetPluginDescriptors(bool installedOnly = true) { foreach (var plugin in _plugins) if (!installedOnly || plugin.Installed) - yield return plugin; + yield return plugin; } - /// - /// Get plugin descriptors - /// - /// The type of plugin to get. - /// A value indicating whether to load only installed plugins - /// Plugin descriptors - public virtual IEnumerable GetPluginDescriptors(bool installedOnly = true) - where T : class, IPlugin + /// + /// Get plugin descriptors + /// + /// The type of plugin to get. + /// A value indicating whether to load only installed plugins + /// Plugin descriptors + public virtual IEnumerable GetPluginDescriptors(bool installedOnly = true) + where T : class, IPlugin { foreach (var plugin in _plugins) if (typeof(T).IsAssignableFrom(plugin.PluginClrType)) if (!installedOnly || plugin.Installed) - yield return plugin; + yield return plugin; } public virtual PluginDescriptor GetPluginDescriptorByAssembly(Assembly assembly, bool installedOnly = true) { - if (assembly != null && _assemblyMap.TryGetValue(assembly, out var descriptor)) - { - if (!installedOnly || descriptor.Installed) - return descriptor; - } + if (assembly != null && _assemblyMap.TryGetValue(assembly, out var descriptor)) + { + if (!installedOnly || descriptor.Installed) + return descriptor; + } - return null; + return null; } - /// - /// Get a plugin descriptor by its system name - /// - /// Plugin system name - /// A value indicating whether to load only installed plugins - /// >Plugin descriptor + /// + /// Get a plugin descriptor by its system name + /// + /// Plugin system name + /// A value indicating whether to load only installed plugins + /// >Plugin descriptor public virtual PluginDescriptor GetPluginDescriptorBySystemName(string systemName, bool installedOnly = true) { - if (systemName.HasValue() && _nameMap.TryGetValue(systemName, out var descriptor)) - { - if (!installedOnly || descriptor.Installed) - return descriptor; - } + if (systemName.HasValue() && _nameMap.TryGetValue(systemName, out var descriptor)) + { + if (!installedOnly || descriptor.Installed) + return descriptor; + } - return null; + return null; } - /// - /// Get a plugin descriptor by its system name - /// - /// The type of plugin to get. - /// Plugin system name - /// A value indicating whether to load only installed plugins - /// >Plugin descriptor + /// + /// Get a plugin descriptor by its system name + /// + /// The type of plugin to get. + /// Plugin system name + /// A value indicating whether to load only installed plugins + /// >Plugin descriptor public virtual PluginDescriptor GetPluginDescriptorBySystemName(string systemName, bool installedOnly = true) where T : class, IPlugin { - if (systemName.HasValue() && _nameMap.TryGetValue(systemName, out var descriptor)) - { - if (!installedOnly || descriptor.Installed) - { - if (typeof(T).IsAssignableFrom(descriptor.PluginClrType)) - return descriptor; - } - } - - return null; + if (systemName.HasValue() && _nameMap.TryGetValue(systemName, out var descriptor)) + { + if (!installedOnly || descriptor.Installed) + { + if (typeof(T).IsAssignableFrom(descriptor.PluginClrType)) + return descriptor; + } + } + + return null; } - + } -} +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Plugins/PluginManager.Cache.cs b/src/Libraries/SmartStore.Core/Plugins/PluginManager.Cache.cs index dff9c225b5..b7a84dedc6 100644 --- a/src/Libraries/SmartStore.Core/Plugins/PluginManager.Cache.cs +++ b/src/Libraries/SmartStore.Core/Plugins/PluginManager.Cache.cs @@ -1,327 +1,299 @@ -using SmartStore.Core.Data; -using SmartStore.Utilities; -using System; +using System; using System.Collections.Generic; -using System.Globalization; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; -using System.Diagnostics; +using SmartStore.Core.Data; +using SmartStore.Core.IO; +using SmartStore.Core.Logging; +using SmartStore.Utilities; namespace SmartStore.Core.Plugins { - public partial class PluginManager - { - private static int? _lastPluginsHash; - private static ICollection _lastPluginAssemblies; - - /// - /// This checks if any of our plugins have changed, if so it will go - /// - /// - /// Returns true if there were changes to plugins and a cleanup was required, otherwise false is returned. - /// - private static bool DetectAndCleanStalePlugins(IEnumerable plugins) - { - var currentHash = ComputePluginsHash(plugins); - var lastHash = GetLastPluginsHash(); - - // Check if anything has been changed, or if we are in debug mode then always perform cleanup - if (currentHash != lastHash) - { - Debug.WriteLine("Plugin changes detected in hash"); - - var lastAssemblies = GetLastPluginsAssemblies(); - - // We need to read the old assembly list and clean them out from the shadow copy folder - var staleFiles = lastAssemblies - .Select(x => new FileInfo(Path.Combine(_shadowCopyDir.FullName, x))) - .ToArray(); - - // If that fails, then we will try to remove it the hard way by renaming it to .delete if it doesn't delete nicely - if (staleFiles.Length > 5) - { - staleFiles.AsParallel().ForAll(f => TryDeleteStaleFile(f)); - } - else - { - staleFiles.Each(f => TryDeleteStaleFile(f)); - } - - return true; - } - - // No plugin changes found - return false; - } - - /// - /// Loads the hash code of the last loaded plugins from disk - /// - /// - private static int? GetLastPluginsHash() - { - if (_lastPluginsHash == null) - { - var filePath = Path.Combine(CommonHelper.MapPath(DataSettings.Current.TenantPath), "plugins.hash"); - if (!File.Exists(filePath)) - { - _lastPluginsHash = 0; - } - else - { - var hash = File.ReadAllText(filePath, Encoding.UTF8); - _lastPluginsHash = ConvertPluginsHash(hash); - } - } - - if (_lastPluginsHash == 0) - { - return null; - } - - return _lastPluginsHash; - } - - private static void SavePluginsHash(int hashCode) - { - var filePath = Path.Combine(CommonHelper.MapPath(DataSettings.Current.TenantPath), "plugins.hash"); - File.WriteAllText(filePath, hashCode.ToString(CultureInfo.InvariantCulture), Encoding.UTF8); - _lastPluginsHash = null; - } - - /// - /// Loads the file names of the last shadow copied plugin assemblies from disk - /// - /// - private static ICollection GetLastPluginsAssemblies() - { - if (_lastPluginAssemblies == null) - { - var filePath = Path.Combine(CommonHelper.MapPath(DataSettings.Current.TenantPath), "plugins.assemblies"); - - if (!File.Exists(filePath)) - { - _lastPluginAssemblies = new List(); - } - else - { - var list = new HashSet(StringComparer.OrdinalIgnoreCase); - - var file = File.ReadAllText(filePath, Encoding.UTF8); - var sr = new StringReader(file); - while (true) - { - var f = sr.ReadLine(); - if (f != null && f.HasValue()) - { - list.Add(f); - } - else - { - break; - } - } - - _lastPluginAssemblies = list; - } - } - - return _lastPluginAssemblies; - } - - private static void SavePluginsAssemblies(IEnumerable plugins) - { - var list = new HashSet(StringComparer.OrdinalIgnoreCase); - - foreach (var p in plugins) - { - p.ReferencedLocalAssemblies.Each(x => list.Add(x.OriginalFile.Name)); - - if (p.Assembly.OriginalFile != null) - { - list.Add(p.Assembly.OriginalFile.Name); - } - } - - var sb = new StringBuilder(); - foreach (var f in list) - { - sb.AppendLine(f); - } - - var filePath = Path.Combine(CommonHelper.MapPath(DataSettings.Current.TenantPath), "plugins.assemblies"); - File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8); - - _lastPluginAssemblies = null; - } - - /// - /// Returns the hash for the passed plugins - /// - /// - private static int ComputePluginsHash(IEnumerable plugins) - { - var hashCombiner = new HashCodeCombiner(); - - // Add each *.dll, Description.txt, web.config to the hash - foreach (var p in plugins) - { - if (p.Assembly.OriginalFile != null) - { - hashCombiner.Add(p.Assembly.OriginalFile); - } - - p.ReferencedLocalAssemblies.Each(x => hashCombiner.Add(x.OriginalFile)); - - hashCombiner.Add(Path.Combine(p.PhysicalPath, "Description.txt")); - hashCombiner.Add(Path.Combine(p.PhysicalPath, "web.config")); - } - - return hashCombiner.CombinedHash; - } - - /// - /// Tries to delete the file. If it doesn't go nicely, we'll force rename it to .delete, if that already exists and we cannot - /// remove the .delete file then we rename to Guid + .delete - /// - /// - /// If it deletes nicely return true, otherwise false - private static bool TryDeleteStaleFile(FileInfo f) - { - Debug.WriteLine("Trying to delete shadow copied file " + f.FullName); - - // This is a special case: people may have been usign the CodeGen folder before so we need to cleanup those - // files too, even if we are no longer using it - if (CleanupDeletePluginFiles(new FileInfo(f.FullName + ".delete"))) - return true; - - try - { - f.Delete(); - return true; - } - catch { } - - // If the file doesn't delete, then create the .delete file for it, hopefully the BuildManager will clean up next time - var newName = GetNewDeleteName(f); - - try - { - File.Move(f.FullName, newName); - } - catch (UnauthorizedAccessException) - { - throw new UnauthorizedAccessException(string.Format("Access to the path '{0}' is denied, ensure that read, write and modify permissions are allowed.", Path.GetDirectoryName(newName))); - } - catch (IOException) - { - Debug.WriteLine(f.FullName + " rename failed, cannot remove stale plugin"); - throw; - } - - // Make it an empty file - try - { - (new StreamWriter(newName)).Close(); - } - catch { } - - Debug.WriteLine("Stale plugin " + f.FullName + " failed to cleanup successfully. A .delete file has been created for it"); - - return false; - } - - /// - /// Returns the .delete file name for a particular file. If just the file name + .delete already exists and is locked, - /// then this returns a Guid + .delete format. - /// - /// - /// - private static string GetNewDeleteName(FileInfo pluginFile) - { - var deleteName = pluginFile.FullName + ".delete"; - - try - { - if (File.Exists(deleteName)) - File.Delete(deleteName); - } - catch - { - Debug.WriteLine("Cannot remove file " + deleteName + ". It is locked, renaming to .delete file with GUID"); - // Cannot delete, so we will need to use a GUID - deleteName = pluginFile.FullName + Guid.NewGuid().ToString("N") + ".delete"; - } - - return deleteName; - } - - /// - /// When given a .delete file, this will attempt to delete the base file that the .delete was created from - /// and then the .delete file itself. if both are removed, then it will return true. - /// - /// - /// - private static bool CleanupDeletePluginFiles(FileInfo f) - { - if (f.Extension != ".delete") - return false; - - var baseFileName = Path.GetDirectoryName(f.FullName) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(f.FullName); - - // NOTE: this is to remove our old GUID named files... we shouldn't have any of these but sometimes things are just locked - var base64Files = f.Directory.GetFiles(Path.GetFileName(baseFileName) + "???????????????????????????????????" + ".delete").ToList(); - Func delFile = x => - { - if (x.IsEmpty()) - return true; - - if (File.Exists(x)) - { - try - { - File.Delete(x); - } - catch - { - return false; - } - } - return true; - }; - - // Always try deleting old guid named files - foreach (var x in base64Files) - { - delFile(x.FullName); - } - - // Try to delete the .dll file - if (!delFile(baseFileName)) - { - return false; - } - - try - { - // Now try to remove the .delete file - f.Delete(); - } - catch { } - - return true; - } - - internal static int ConvertPluginsHash(string val) - { - int outVal; - if (Int32.TryParse(val, NumberStyles.Integer, CultureInfo.InvariantCulture, out outVal)) - { - return outVal; - } - - return 0; - } - } + public partial class PluginManager + { + #region PluginsHasher + + class PluginsHasher : DirectoryHasher + { + private readonly IEnumerable _plugins; + + public PluginsHasher(IEnumerable plugins) + : base(CommonHelper.MapPath(PluginsLocation), null, false, CommonHelper.MapPath(DataSettings.Current.TenantPath)) + { + _plugins = plugins; + } + + protected override int ComputeHash() + { + var hashCombiner = new HashCodeCombiner(); + + // Add each *.dll, Description.txt, web.config to the hash + var arrPlugins = _plugins.OrderBy(x => x.FolderName).ToArray(); + foreach (var p in arrPlugins) + { + if (p.Assembly.OriginalFile != null) + { + hashCombiner.Add(p.Assembly.OriginalFile); + } + + p.ReferencedLocalAssemblies.Each(x => hashCombiner.Add(x.OriginalFile)); + + hashCombiner.Add(Path.Combine(p.PhysicalPath, "Description.txt")); + hashCombiner.Add(Path.Combine(p.PhysicalPath, "web.config")); + hashCombiner.Add(p.Installed); + } + + return hashCombiner.CombinedHash; + } + } + + #endregion + + private static ICollection _lastPluginAssemblies; + + /// + /// true when any plugin file has been changed since previous application start. + /// + public static bool PluginChangeDetected { get; private set; } + + /// + /// This checks if any of our plugins have changed, if so it will go + /// + /// + /// Returns true if there were changes to plugins and a cleanup was required, otherwise false is returned. + /// + private static bool DetectAndCleanStalePlugins(IEnumerable plugins, PluginsHasher hasher) + { + // Check if anything has been changed, or if we are in debug mode then always perform cleanup + if (hasher.HasChanged) + { + PluginChangeDetected = true; + Logger.DebugFormat("Plugin changes detected in hash."); + + var lastAssemblies = GetLastPluginsAssemblies(); + + // We need to read the old assembly list and clean them out from the shadow copy folder + var staleFiles = lastAssemblies + .Select(x => new FileInfo(Path.Combine(_shadowCopyDir.FullName, x))) + .ToArray(); + + // If that fails, then we will try to remove it the hard way by renaming it to .delete if it doesn't delete nicely + if (staleFiles.Length > 5) + { + staleFiles.AsParallel().ForAll(f => TryDeleteStaleFile(f)); + } + else + { + staleFiles.Each(f => TryDeleteStaleFile(f)); + } + + return true; + } + + // No plugin changes found + return false; + } + + /// + /// Loads the file names of the last shadow copied plugin assemblies from disk + /// + /// + private static ICollection GetLastPluginsAssemblies() + { + if (_lastPluginAssemblies == null) + { + var filePath = Path.Combine(CommonHelper.MapPath(DataSettings.Current.TenantPath), "plugins.assemblies"); + + if (!File.Exists(filePath)) + { + _lastPluginAssemblies = new List(); + } + else + { + var list = new HashSet(StringComparer.OrdinalIgnoreCase); + + var file = File.ReadAllText(filePath, Encoding.UTF8); + var sr = new StringReader(file); + while (true) + { + var f = sr.ReadLine(); + if (f != null && f.HasValue()) + { + list.Add(f); + } + else + { + break; + } + } + + _lastPluginAssemblies = list; + } + } + + return _lastPluginAssemblies; + } + + private static void SavePluginsAssemblies(IEnumerable plugins) + { + var list = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (var p in plugins) + { + p.ReferencedLocalAssemblies.Each(x => list.Add(x.OriginalFile.Name)); + + if (p.Assembly.OriginalFile != null) + { + list.Add(p.Assembly.OriginalFile.Name); + } + } + + var sb = new StringBuilder(); + foreach (var f in list) + { + sb.AppendLine(f); + } + + var filePath = Path.Combine(CommonHelper.MapPath(DataSettings.Current.TenantPath), "plugins.assemblies"); + File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8); + + _lastPluginAssemblies = null; + } + + /// + /// Tries to delete the file. If it doesn't go nicely, we'll force rename it to .delete, if that already exists and we cannot + /// remove the .delete file then we rename to Guid + .delete + /// + /// + /// If it deletes nicely return true, otherwise false + private static bool TryDeleteStaleFile(FileInfo f) + { + Debug.WriteLine("Trying to delete shadow copied file " + f.FullName); + + // This is a special case: people may have been usign the CodeGen folder before so we need to cleanup those + // files too, even if we are no longer using it + if (CleanupDeletePluginFiles(new FileInfo(f.FullName + ".delete"))) + return true; + + try + { + f.Delete(); + return true; + } + catch { } + + // If the file doesn't delete, then create the .delete file for it, hopefully the BuildManager will clean up next time + var newName = GetNewDeleteName(f); + + try + { + File.Move(f.FullName, newName); + } + catch (UnauthorizedAccessException) + { + var ex = new UnauthorizedAccessException(string.Format("Access to the path '{0}' is denied, ensure that read, write and modify permissions are allowed.", Path.GetDirectoryName(newName))); + Logger.Error(ex.Message); + throw ex; + } + catch (IOException) + { + Logger.Error(f.FullName + " rename failed, cannot remove stale plugin"); + throw; + } + + // Make it an empty file + try + { + (new StreamWriter(newName)).Close(); + } + catch { } + + Logger.Error("Stale plugin " + f.FullName + " failed to cleanup successfully. A .delete file has been created for it."); + + return false; + } + + /// + /// Returns the .delete file name for a particular file. If just the file name + .delete already exists and is locked, + /// then this returns a Guid + .delete format. + /// + /// + /// + private static string GetNewDeleteName(FileInfo pluginFile) + { + var deleteName = pluginFile.FullName + ".delete"; + + try + { + if (File.Exists(deleteName)) + File.Delete(deleteName); + } + catch + { + Logger.Error("Cannot remove file " + deleteName + ".It is locked, renaming to.delete file with GUID"); + // Cannot delete, so we will need to use a GUID + deleteName = pluginFile.FullName + Guid.NewGuid().ToString("N") + ".delete"; + } + + return deleteName; + } + + /// + /// When given a .delete file, this will attempt to delete the base file that the .delete was created from + /// and then the .delete file itself. if both are removed, then it will return true. + /// + /// + /// + private static bool CleanupDeletePluginFiles(FileInfo f) + { + if (f.Extension != ".delete") + return false; + + var baseFileName = Path.GetDirectoryName(f.FullName) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(f.FullName); + + // NOTE: this is to remove our old GUID named files... we shouldn't have any of these but sometimes things are just locked + var base64Files = f.Directory.GetFiles(Path.GetFileName(baseFileName) + "???????????????????????????????????" + ".delete").ToList(); + Func delFile = x => + { + if (x.IsEmpty()) + return true; + + if (File.Exists(x)) + { + try + { + File.Delete(x); + } + catch + { + return false; + } + } + return true; + }; + + // Always try deleting old guid named files + foreach (var x in base64Files) + { + delFile(x.FullName); + } + + // Try to delete the .dll file + if (!delFile(baseFileName)) + { + return false; + } + + try + { + // Now try to remove the .delete file + f.Delete(); + } + catch { } + + return true; + } + } } diff --git a/src/Libraries/SmartStore.Core/Plugins/PluginManager.cs b/src/Libraries/SmartStore.Core/Plugins/PluginManager.cs index c7c4d6fe4e..d8a3f158af 100644 --- a/src/Libraries/SmartStore.Core/Plugins/PluginManager.cs +++ b/src/Libraries/SmartStore.Core/Plugins/PluginManager.cs @@ -5,25 +5,23 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Threading; +using System.Runtime.InteropServices; using System.Web; using System.Web.Compilation; -using System.Runtime.InteropServices; using Microsoft.Web.Infrastructure.DynamicModuleHelper; +using SmartStore.Core.Data; +using SmartStore.Core.Infrastructure; using SmartStore.Core.Infrastructure.DependencyManagement; -using SmartStore.Core.Plugins; +using SmartStore.Core.Logging; using SmartStore.Core.Packaging; +using SmartStore.Core.Plugins; using SmartStore.Utilities; -using SmartStore.Utilities.Threading; -using SmartStore.Core.Data; -using System.Threading.Tasks; // Contributor: Umbraco (http://www.umbraco.com). Thanks a lot! // SEE THIS POST for full details of what this does //http://shazwazza.com/post/Developing-a-plugin-framework-in-ASPNET-with-medium-trust.aspx [assembly: PreApplicationStartMethod(typeof(PluginManager), "Initialize")] - namespace SmartStore.Core.Plugins { /// @@ -31,365 +29,343 @@ namespace SmartStore.Core.Plugins /// public partial class PluginManager { - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool SetDllDirectory(string lpPathName); - - private static readonly object _lock = new object(); - private static readonly string _pluginsPath = "~/Plugins"; - private static DirectoryInfo _shadowCopyDir; - private static readonly ConcurrentDictionary _referencedPlugins = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - private static HashSet _inactiveAssemblies = new HashSet(); - - /// - /// Returns the virtual path of the plugins folder relative to the application - /// - public static string PluginsLocation - { - get { return _pluginsPath; } - } - - /// - /// Returns a collection of all referenced plugin assemblies that have been shadow copied - /// - public static IEnumerable ReferencedPlugins - { - get - { - return _referencedPlugins.Values; - } - // for unit testing purposes - internal set - { - foreach (var x in value) - { - if (!_referencedPlugins.ContainsKey(x.SystemName)) - { - _referencedPlugins[x.SystemName] = x; - } - } - } - } - - /// - /// Returns a collection of all plugins which are not compatible with the current version - /// - public static IEnumerable IncompatiblePlugins - { - get; - // for unit testing purposes - internal set; - } + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool SetDllDirectory(string lpPathName); + + private static readonly object _lock = new object(); + private static DirectoryInfo _shadowCopyDir; + private static readonly ConcurrentDictionary _referencedPlugins = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private static readonly HashSet _inactiveAssemblies = new HashSet(); + + private static ILogger Logger { get; set; } = NullLogger.Instance; + + private static IDisposable ActivateLogger() + { + var logger = new TraceLogger(); + Logger = logger; + return new ActionDisposable(() => + { + logger.Dispose(); + Logger = NullLogger.Instance; + }); + } + + /// + /// Returns the virtual path of the plugins folder relative to the application + /// + public static string PluginsLocation { get; } = "~/Plugins"; + + /// + /// Returns a collection of all referenced plugin assemblies that have been shadow copied + /// + public static IEnumerable ReferencedPlugins + { + get => _referencedPlugins.Values; + // for unit testing purposes + internal set + { + foreach (var x in value) + { + if (!_referencedPlugins.ContainsKey(x.SystemName)) + { + _referencedPlugins[x.SystemName] = x; + } + } + } + } + + /// + /// Returns a collection of all plugins which are not compatible with the current version + /// + public static IEnumerable IncompatiblePlugins + { + get; + // for unit testing purposes + internal set; + } /// /// Initialize /// public static void Initialize() { - var isFullTrust = WebHelper.GetTrustLevel() == AspNetHostingPermissionLevel.Unrestricted; - if (!isFullTrust) - { - throw new ApplicationException("SmartStore.NET requires Full Trust mode. Please enable Full Trust for your web site or contact your hosting provider."); - } - - using (var updater = new AppUpdater()) - { - // update from NuGet package, if it exists and is valid - if (updater.TryUpdateFromPackage()) - { - // [...] - } - - // execute migrations - updater.ExecuteMigrations(); - } - - // adding a process-specific environment path (either bin/x86 or bin/x64) - // ensures that unmanaged native dependencies can be resolved successfully. - SetNativeDllPath(); - - DynamicModuleUtility.RegisterModule(typeof(AutofacRequestLifetimeHttpModule)); - - var incompatiblePlugins = (new HashSet(StringComparer.OrdinalIgnoreCase)).AsSynchronized(); - var inactiveAssemblies = _inactiveAssemblies.AsSynchronized(); - var dirty = false; - - var watch = Stopwatch.StartNew(); - - _shadowCopyDir = new DirectoryInfo(AppDomain.CurrentDomain.DynamicDirectory); - - var plugins = LoadPluginDescriptors().ToArray(); - var compatiblePlugins = plugins.Where(x => !x.Incompatible).ToArray(); - - var ms = watch.ElapsedMilliseconds; - Debug.WriteLine("INIT PLUGINS (LoadPluginDescriptors). Time elapsed: {0} ms.".FormatCurrentUI(ms)); - - // If plugins state is dirty, we copy files over to the dynamic folder, - // otherwise we just reference the previously copied file. - dirty = DetectAndCleanStalePlugins(compatiblePlugins); - - // Perf: Initialize/probe all plugins in parallel - plugins.AsParallel().ForAll(x => - { - // Deploy to ASP.NET dynamic folder. - DeployPlugin(x, dirty); - - // Finalize - FinalizePlugin(x); - }); - - //// Retry when failed, because during parallel execution assembly loading MAY fail. - //// Therefore we retry initialization for failed plugins, but sequentially this time. - //foreach (var p in plugins) - //{ - // // INFO: this seems redundant, but it's ok: - // // DeployPlugin() only probes assemblies that are not loaded yet. - // DeployPlugin(p, dirty); - - // // Finalize - // FinalizePlugin(p); - //} - - if (dirty && DataSettings.DatabaseIsInstalled()) - { - // Save current hash of all deployed plugins to disk - var hash = ComputePluginsHash(_referencedPlugins.Values.OrderBy(x => x.FolderName).ToArray()); - SavePluginsHash(hash); - - // Save names of all deployed assemblies to disk (so we can nuke them later) - SavePluginsAssemblies(_referencedPlugins.Values); - } - - IncompatiblePlugins = incompatiblePlugins.AsReadOnly(); - - ms = watch.ElapsedMilliseconds; - Debug.WriteLine("INIT PLUGINS (Deployment complete). Time elapsed: {0} ms.".FormatCurrentUI(ms)); - - void DeployPlugin(PluginDescriptor p, bool shadowCopy) - { - if (p.Incompatible) - { - // Do nothing if plugin is incompatible - return; - } - - // First copy referenced local assemblies (if any) - for (int i = 0; i < p.ReferencedLocalAssemblies.Length; ++i) - { - var refAr = p.ReferencedLocalAssemblies[i]; - if (refAr.Assembly == null) - { - Probe(refAr, p, shadowCopy); - } - } - - // Then copy main plugin assembly - var ar = p.Assembly; - if (ar.Assembly == null) - { - Probe(ar, p, shadowCopy); - if (ar.Assembly != null) - { - // Activate (even if uninstalled): Find IPlugin, IPreApplicationStart, IConfigurable etc. - ActivatePlugin(p); - } - } - } - - void FinalizePlugin(PluginDescriptor p) - { - _referencedPlugins[p.SystemName] = p; - - if (p.Incompatible) - { - incompatiblePlugins.Add(p.SystemName); - return; - } - - var firstFailedAssembly = p.ReferencedLocalAssemblies.FirstOrDefault(x => x.ActivationException != null); - if (firstFailedAssembly == null && p.Assembly.ActivationException != null) - { - firstFailedAssembly = p.Assembly; - } - if (firstFailedAssembly != null) - { - Debug.WriteLine(firstFailedAssembly.ActivationException.Message); - p.Incompatible = true; - incompatiblePlugins.Add(p.SystemName); - } - - if ((!p.Installed || firstFailedAssembly != null) && p.Assembly.Assembly != null) - { - inactiveAssemblies.Add(p.Assembly.Assembly); - } - } - } - - /// - /// Loads and parses the descriptors of all installed plugins - /// - /// All descriptors - private static IEnumerable LoadPluginDescriptors() - { - // TODO: Add verbose exception handling / raising here since this is happening on app startup and could - // prevent app from starting altogether - - var pluginsDir = new DirectoryInfo(CommonHelper.MapPath(_pluginsPath)); - - if (!pluginsDir.Exists) - { - pluginsDir.Create(); - return Enumerable.Empty(); - } - - // Determine all plugin folders: ~/Plugins/{SystemName} - var allPluginDirs = pluginsDir.EnumerateDirectories().ToArray() - .Where(x => !x.Name.IsMatch("bin") && !x.Name.IsMatch("_Backup")) - .OrderBy(x => x.Name) - .ToArray(); - - var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile().AsSynchronized(); - - // Load/activate all plugins - return allPluginDirs - .AsParallel() - .AsOrdered() - .Select(d => LoadPluginDescriptor(d, installedPluginSystemNames)) - .Where(x => x != null); - } - - ///// - ///// Loads and parses the descriptors of all installed plugins - ///// - ///// All descriptors - //private static Task ReadPluginDescriptors(BlockingCollection bag) - //{ - // // TODO: Add verbose exception handling / raising here since this is happening on app startup and could - // // prevent app from starting altogether - - // return Task.Factory.StartNew(() => - // { - // var pluginsDir = new DirectoryInfo(CommonHelper.MapPath(_pluginsPath)); - - // if (!pluginsDir.Exists) - // { - // pluginsDir.Create(); - // } - // else - // { - // // Determine all plugin folders: ~/Plugins/{SystemName} - // var allPluginDirs = pluginsDir.EnumerateDirectories().ToArray() - // .Where(x => !x.Name.IsMatch("bin") && !x.Name.IsMatch("_Backup")) - // .OrderBy(x => x.Name) - // .ToArray(); - - // var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile().AsSynchronized(); - - // // Load/activate all plugins - // allPluginDirs - // .AsParallel() - // .AsOrdered() - // .Select(d => LoadPluginDescriptor(d, installedPluginSystemNames)) - // .Where(x => x != null) - // .ForAll(x => bag.Add(x)); - // } - - // bag.CompleteAdding(); - // }); - //} - - private static PluginDescriptor LoadPluginDescriptor(DirectoryInfo d, ICollection installedPluginSystemNames) - { - var descriptionFile = new FileInfo(Path.Combine(d.FullName, "Description.txt")); - if (!descriptionFile.Exists) - { - return null; - } - - // Load descriptor file (Description.txt) - var descriptor = PluginFileParser.ParsePluginDescriptionFile(descriptionFile.FullName); - - // Some validation - if (descriptor.SystemName.IsEmpty()) - { - throw new SmartException("The plugin descriptor '{0}' does not define a plugin system name. Try assigning the plugin a unique name and recompile.".FormatInvariant(descriptionFile.FullName)); - } - - if (descriptor.PluginFileName.IsEmpty()) - { - throw new SmartException("The plugin descriptor '{0}' does not define a plugin assembly file name. Try assigning the plugin a file name and recompile.".FormatInvariant(descriptionFile.FullName)); - } - - descriptor.VirtualPath = _pluginsPath + "/" + descriptor.FolderName; - - // Set 'Installed' property - descriptor.Installed = installedPluginSystemNames.Contains(descriptor.SystemName); - - // Ensure that version of plugin is valid - if (!IsAssumedCompatible(descriptor)) - { - // Set 'Incompatible' property and return - descriptor.Incompatible = true; - return descriptor; - } - - var skipDlls = new HashSet(new[] { "log4net.dll" }, StringComparer.OrdinalIgnoreCase); - - // Get list of all DLLs in plugin folders (not in 'bin' or '_Backup'!) - var pluginBinaries = descriptionFile.Directory.EnumerateFiles("*.dll", SearchOption.AllDirectories).ToArray() - .Where(x => IsPackagePluginFolder(x.Directory) && !skipDlls.Contains(x.Name)) - .OrderBy(x => x.Name) - .ToDictionarySafe(x => x.Name, StringComparer.OrdinalIgnoreCase); - - // Set 'OriginalAssemblyFile' property - descriptor.Assembly.OriginalFile = pluginBinaries.Get(descriptor.PluginFileName); - - if (descriptor.Assembly.OriginalFile == null) - { - throw new SmartException("The main assembly '{0}' for plugin '{1}' could not be found.".FormatInvariant(descriptor.PluginFileName, descriptor.SystemName)); - } - - // Load all other referenced local assemblies now - var otherAssemblyFiles = pluginBinaries - .Where(x => !x.Key.IsCaseInsensitiveEqual(descriptor.PluginFileName)) - .Select(x => x.Value); - - descriptor.ReferencedLocalAssemblies = otherAssemblyFiles.Select(x => new AssemblyReference { OriginalFile = x }).ToArray(); - - return descriptor; - } - - private static void ActivatePlugin(PluginDescriptor plugin) - { - // Init plugin type (only one plugin per assembly is allowed) - bool pluginFound = false; - bool preStarterFound = !plugin.Installed; - var exportedTypes = plugin.Assembly.Assembly.GetExportedTypes(); - - foreach (var t in exportedTypes) - { - if (typeof(IPlugin).IsAssignableFrom(t) && !t.IsInterface && t.IsClass && !t.IsAbstract) - { - plugin.PluginClrType = t; - plugin.IsConfigurable = typeof(IConfigurable).IsAssignableFrom(t); - pluginFound = true; - } - else if (plugin.Installed && typeof(IPreApplicationStart).IsAssignableFrom(t) && !t.IsInterface && t.IsClass && !t.IsAbstract && t.HasDefaultConstructor()) - { - try - { - var preStarter = Activator.CreateInstance(t) as IPreApplicationStart; - preStarter.Start(); - } - catch { } - preStarterFound = true; - } - - if (pluginFound && preStarterFound) - { - break; - } - } - } + using (ActivateLogger()) + { + Logger.Debug("===================== APPINIT START"); + InitializeCore(); + Logger.Debug("===================== APPINIT END"); + } + } + + private static void InitializeCore() + { + var isFullTrust = WebHelper.GetTrustLevel() == AspNetHostingPermissionLevel.Unrestricted; + if (!isFullTrust) + { + throw new ApplicationException("Smartstore requires Full Trust mode. Please enable Full Trust for your web site or contact your hosting provider."); + } + + using (var updater = new AppUpdater()) + { + // update from NuGet package, if it exists and is valid + if (updater.TryUpdateFromPackage()) + { + // [...] + } + + // execute migrations + updater.ExecuteMigrations(); + } + + // adding a process-specific environment path (either bin/x86 or bin/x64) + // ensures that unmanaged native dependencies can be resolved successfully. + SetNativeDllPath(); + + DynamicModuleUtility.RegisterModule(typeof(AutofacRequestLifetimeHttpModule)); + + var incompatiblePlugins = (new HashSet(StringComparer.OrdinalIgnoreCase)).AsSynchronized(); + var inactiveAssemblies = _inactiveAssemblies.AsSynchronized(); + var dirty = false; + + var watch = Stopwatch.StartNew(); + + _shadowCopyDir = new DirectoryInfo(AppDomain.CurrentDomain.DynamicDirectory); + + var plugins = LoadPluginDescriptors().ToArray(); + var compatiblePlugins = plugins.Where(x => !x.Incompatible).ToArray(); + var hasher = new PluginsHasher(compatiblePlugins); + + Logger.DebugFormat("Loaded plugin descriptors. {0} total, {1} incompatible.", plugins.Length, plugins.Length - compatiblePlugins.Length); + + var ms = watch.ElapsedMilliseconds; + Logger.DebugFormat("INIT PLUGINS (LoadPluginDescriptors). Time elapsed: {0} ms.", ms); + + // If plugins state is dirty, we copy files over to the dynamic folder, + // otherwise we just reference the previously copied file. + dirty = DetectAndCleanStalePlugins(compatiblePlugins, hasher); + + // Perf: Initialize/probe all plugins in parallel + plugins.AsParallel().ForAll(x => + { + // Deploy to ASP.NET dynamic folder. + DeployPlugin(x, dirty); + + // Finalize + FinalizePlugin(x); + }); + + //// Retry when failed, because during parallel execution assembly loading MAY fail. + //// Therefore we retry initialization for failed plugins, but sequentially this time. + //foreach (var p in plugins) + //{ + // // INFO: this seems redundant, but it's ok: + // // DeployPlugin() only probes assemblies that are not loaded yet. + // DeployPlugin(p, dirty); + + // // Finalize + // FinalizePlugin(p); + //} + + if (dirty && DataSettings.DatabaseIsInstalled()) + { + // Save current hash of all deployed plugins to disk + hasher.Persist(); + + // Save names of all deployed assemblies to disk (so we can nuke them later) + SavePluginsAssemblies(_referencedPlugins.Values); + } + + IncompatiblePlugins = incompatiblePlugins.AsReadOnly(); + + ms = watch.ElapsedMilliseconds; + Logger.DebugFormat("INIT PLUGINS (Deployment complete). Time elapsed: {0} ms.", ms); + + void DeployPlugin(PluginDescriptor p, bool shadowCopy) + { + if (p.Incompatible) + { + // Do nothing if plugin is incompatible + return; + } + + // First copy referenced local assemblies (if any) + for (int i = 0; i < p.ReferencedLocalAssemblies.Length; ++i) + { + var refAr = p.ReferencedLocalAssemblies[i]; + if (refAr.Assembly == null) + { + Probe(refAr, p, shadowCopy); + } + } + + // Then copy main plugin assembly + var ar = p.Assembly; + if (ar.Assembly == null) + { + Probe(ar, p, shadowCopy); + if (ar.Assembly != null) + { + // Activate (even if uninstalled): Find IPlugin, IPreApplicationStart, IConfigurable etc. + ActivatePlugin(p); + } + } + } + + void FinalizePlugin(PluginDescriptor p) + { + _referencedPlugins[p.SystemName] = p; + + if (p.Incompatible) + { + incompatiblePlugins.Add(p.SystemName); + return; + } + + var firstFailedAssembly = p.ReferencedLocalAssemblies.FirstOrDefault(x => x.ActivationException != null); + if (firstFailedAssembly == null && p.Assembly.ActivationException != null) + { + firstFailedAssembly = p.Assembly; + } + if (firstFailedAssembly != null) + { + Logger.ErrorFormat("Assembly probing failed for '{0}': {1}", firstFailedAssembly.File?.Name.EmptyNull(), firstFailedAssembly.ActivationException.Message); + p.Incompatible = true; + incompatiblePlugins.Add(p.SystemName); + } + + if ((!p.Installed || firstFailedAssembly != null) && p.Assembly.Assembly != null) + { + inactiveAssemblies.Add(p.Assembly.Assembly); + } + } + } + + /// + /// Loads and parses the descriptors of all installed plugins + /// + /// All descriptors + private static IEnumerable LoadPluginDescriptors() + { + // TODO: Add verbose exception handling / raising here since this is happening on app startup and could + // prevent app from starting altogether + + var pluginsDir = new DirectoryInfo(CommonHelper.MapPath(PluginsLocation)); + + if (!pluginsDir.Exists) + { + pluginsDir.Create(); + return Enumerable.Empty(); + } + + // Determine all plugin folders: ~/Plugins/{SystemName} + var allPluginDirs = pluginsDir.EnumerateDirectories().ToArray() + .Where(x => !x.Name.IsMatch("bin") && !x.Name.IsMatch("_Backup")) + .OrderBy(x => x.Name) + .ToArray(); + + var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile().AsSynchronized(); + + // Load/activate all plugins + return allPluginDirs + .AsParallel() + .AsOrdered() + .Select(d => LoadPluginDescriptor(d, installedPluginSystemNames)) + .Where(x => x != null); + } + + private static PluginDescriptor LoadPluginDescriptor(DirectoryInfo d, ICollection installedPluginSystemNames) + { + var descriptionFile = new FileInfo(Path.Combine(d.FullName, "Description.txt")); + if (!descriptionFile.Exists) + { + return null; + } + + // Load descriptor file (Description.txt) + var descriptor = PluginFileParser.ParsePluginDescriptionFile(descriptionFile.FullName); + + // Some validation + if (descriptor.SystemName.IsEmpty()) + { + throw new SmartException("The plugin descriptor '{0}' does not define a plugin system name. Try assigning the plugin a unique name and recompile.".FormatInvariant(descriptionFile.FullName)); + } + + if (descriptor.PluginFileName.IsEmpty()) + { + throw new SmartException("The plugin descriptor '{0}' does not define a plugin assembly file name. Try assigning the plugin a file name and recompile.".FormatInvariant(descriptionFile.FullName)); + } + + descriptor.VirtualPath = PluginsLocation + "/" + descriptor.FolderName; + + // Set 'Installed' property + descriptor.Installed = installedPluginSystemNames.Contains(descriptor.SystemName); + + // Ensure that version of plugin is valid + if (!IsAssumedCompatible(descriptor)) + { + // Set 'Incompatible' property and return + descriptor.Incompatible = true; + return descriptor; + } + + var skipDlls = new HashSet(new[] { "log4net.dll" }, StringComparer.OrdinalIgnoreCase); + + // Get list of all DLLs in plugin folders (not in 'bin' or '_Backup'!) + var pluginBinaries = descriptionFile.Directory.EnumerateFiles("*.dll", SearchOption.AllDirectories).ToArray() + .Where(x => IsPackagePluginFolder(x.Directory) && !skipDlls.Contains(x.Name)) + .OrderBy(x => x.Name) + .ToDictionarySafe(x => x.Name, StringComparer.OrdinalIgnoreCase); + + // Set 'OriginalAssemblyFile' property + descriptor.Assembly.OriginalFile = pluginBinaries.Get(descriptor.PluginFileName); + + if (descriptor.Assembly.OriginalFile == null) + { + throw new SmartException("The main assembly '{0}' for plugin '{1}' could not be found.".FormatInvariant(descriptor.PluginFileName, descriptor.SystemName)); + } + + // Load all other referenced local assemblies now + var otherAssemblyFiles = pluginBinaries + .Where(x => !x.Key.IsCaseInsensitiveEqual(descriptor.PluginFileName)) + .Select(x => x.Value); + + descriptor.ReferencedLocalAssemblies = otherAssemblyFiles.Select(x => new AssemblyReference { OriginalFile = x }).ToArray(); + + return descriptor; + } + + private static void ActivatePlugin(PluginDescriptor plugin) + { + // Init plugin type (only one plugin per assembly is allowed) + bool pluginFound = false; + bool preStarterFound = !plugin.Installed; + var exportedTypes = plugin.Assembly.Assembly.GetExportedTypes(); + + foreach (var t in exportedTypes) + { + if (typeof(IPlugin).IsAssignableFrom(t) && !t.IsInterface && t.IsClass && !t.IsAbstract) + { + plugin.PluginClrType = t; + plugin.IsConfigurable = typeof(IConfigurable).IsAssignableFrom(t); + pluginFound = true; + } + else if (plugin.Installed && typeof(IPreApplicationStart).IsAssignableFrom(t) && !t.IsInterface && t.IsClass && !t.IsAbstract && t.HasDefaultConstructor()) + { + try + { + var preStarter = Activator.CreateInstance(t) as IPreApplicationStart; + preStarter.Start(); + } + catch { } + preStarterFound = true; + } + + if (pluginFound && preStarterFound) + { + break; + } + } + } /// /// Mark plugin as installed @@ -397,17 +373,17 @@ private static void ActivatePlugin(PluginDescriptor plugin) /// Plugin system name public static void MarkPluginAsInstalled(string systemName) { - Guard.NotEmpty(systemName, nameof(systemName)); + Guard.NotEmpty(systemName, nameof(systemName)); - var installedPluginSystemNames = GetInstalledPluginNames(); + var installedPluginSystemNames = GetInstalledPluginNames(); - bool installed = installedPluginSystemNames.Contains(systemName); - if (!installed) - { - installedPluginSystemNames.Add(systemName); - } + bool installed = installedPluginSystemNames.Contains(systemName); + if (!installed) + { + installedPluginSystemNames.Add(systemName); + } - PluginFileParser.SaveInstalledPluginsFile(installedPluginSystemNames); + PluginFileParser.SaveInstalledPluginsFile(installedPluginSystemNames); } /// @@ -416,16 +392,16 @@ public static void MarkPluginAsInstalled(string systemName) /// Plugin system name public static void MarkPluginAsUninstalled(string systemName) { - Guard.NotEmpty(systemName, nameof(systemName)); + Guard.NotEmpty(systemName, nameof(systemName)); - var installedPluginSystemNames = GetInstalledPluginNames(); - bool installed = installedPluginSystemNames.Contains(systemName); - if (installed) - { - installedPluginSystemNames.Remove(systemName); - } + var installedPluginSystemNames = GetInstalledPluginNames(); + bool installed = installedPluginSystemNames.Contains(systemName); + if (installed) + { + installedPluginSystemNames.Remove(systemName); + } - PluginFileParser.SaveInstalledPluginsFile(installedPluginSystemNames); + PluginFileParser.SaveInstalledPluginsFile(installedPluginSystemNames); } /// @@ -438,20 +414,20 @@ public static void MarkAllPluginsAsUninstalled() File.Delete(filePath); } - private static ICollection GetInstalledPluginNames() - { - var filePath = PluginFileParser.InstalledPluginsFilePath; - if (!File.Exists(filePath)) - { - using (File.Create(filePath)) - { - // We use 'using' to close the file after it's created - } - } - - var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile(); - return installedPluginSystemNames; - } + private static ICollection GetInstalledPluginNames() + { + var filePath = PluginFileParser.InstalledPluginsFilePath; + if (!File.Exists(filePath)) + { + using (File.Create(filePath)) + { + // We use 'using' to close the file after it's created + } + } + + var installedPluginSystemNames = PluginFileParser.ParseInstalledPluginsFile(); + return installedPluginSystemNames; + } /// /// Gets a value indicating whether a plugin is assumed @@ -466,68 +442,68 @@ private static ICollection GetInstalledPluginNames() /// true when the plugin is assumed to be compatible public static bool IsAssumedCompatible(PluginDescriptor descriptor) { - Guard.NotNull(descriptor, nameof(descriptor)); + Guard.NotNull(descriptor, nameof(descriptor)); - return IsAssumedCompatible(descriptor.MinAppVersion); + return IsAssumedCompatible(descriptor.MinAppVersion); } - /// - /// Gets a value indicating whether the given min. required app version is assumed - /// to be compatible with the current app version - /// - /// - /// A plugin is generally compatible when both app version and plugin's - /// MinorAppVersion are equal, OR - when app version is greater - it is - /// assumed to be compatible when no breaking changes occured since MinorAppVersion. - /// - /// The min. app version to check for - /// true when the extension's version is assumed to be compatible - public static bool IsAssumedCompatible(Version minAppVersion) - { - Guard.NotNull(minAppVersion, nameof(minAppVersion)); - - if (SmartStoreVersion.Version == minAppVersion) - { - return true; - } - - if (SmartStoreVersion.Version < minAppVersion) - { - return false; - } - - bool compatible = true; - - foreach (var version in SmartStoreVersion.BreakingChangesHistory) - { - if (version > minAppVersion) - { - // there was a breaking change in a version greater - // than plugin's MinorAppVersion. - compatible = false; - break; - } - - if (version <= minAppVersion) - { - break; - } - } - - return compatible; - } - - /// - /// Gets a value indicating whether a plugin - /// is registered and installed. - /// - /// The system name of the plugin to check for - /// true if the plugin exists, false otherwise - public static bool PluginExists(string systemName) - { - Guard.NotEmpty(systemName, nameof(systemName)); - return _referencedPlugins.ContainsKey(systemName); - } + /// + /// Gets a value indicating whether the given min. required app version is assumed + /// to be compatible with the current app version + /// + /// + /// A plugin is generally compatible when both app version and plugin's + /// MinorAppVersion are equal, OR - when app version is greater - it is + /// assumed to be compatible when no breaking changes occured since MinorAppVersion. + /// + /// The min. app version to check for + /// true when the extension's version is assumed to be compatible + public static bool IsAssumedCompatible(Version minAppVersion) + { + Guard.NotNull(minAppVersion, nameof(minAppVersion)); + + if (SmartStoreVersion.Version == minAppVersion) + { + return true; + } + + if (SmartStoreVersion.Version < minAppVersion) + { + return false; + } + + bool compatible = true; + + foreach (var version in SmartStoreVersion.BreakingChangesHistory) + { + if (version > minAppVersion) + { + // there was a breaking change in a version greater + // than plugin's MinorAppVersion. + compatible = false; + break; + } + + if (version <= minAppVersion) + { + break; + } + } + + return compatible; + } + + /// + /// Gets a value indicating whether a plugin + /// is registered and installed. + /// + /// The system name of the plugin to check for + /// true if the plugin exists, false otherwise + public static bool PluginExists(string systemName) + { + Guard.NotEmpty(systemName, nameof(systemName)); + return _referencedPlugins.ContainsKey(systemName); + } /// /// Gets a value indicating whether the plugin assembly @@ -540,32 +516,34 @@ public static bool IsActivePluginAssembly(Assembly assembly) return !_inactiveAssemblies.Contains(assembly); } - private static void SetPrivateEnvPath() - { - string envPath = Environment.GetEnvironmentVariable("PATH"); + private static void SetPrivateEnvPath() + { + string envPath = Environment.GetEnvironmentVariable("PATH"); - if (Environment.Is64BitProcess) - { - envPath = envPath.EnsureEndsWith(";") + Path.Combine(AppDomain.CurrentDomain.RelativeSearchPath, "amd64"); - envPath = envPath.EnsureEndsWith(";") + Path.Combine(AppDomain.CurrentDomain.RelativeSearchPath, "x64"); - } - else - { - envPath = envPath.EnsureEndsWith(";") + Path.Combine(AppDomain.CurrentDomain.RelativeSearchPath, "x86"); - } + if (Environment.Is64BitProcess) + { + envPath = envPath.EnsureEndsWith(";") + Path.Combine(AppDomain.CurrentDomain.RelativeSearchPath, "amd64"); + envPath = envPath.EnsureEndsWith(";") + Path.Combine(AppDomain.CurrentDomain.RelativeSearchPath, "x64"); + } + else + { + envPath = envPath.EnsureEndsWith(";") + Path.Combine(AppDomain.CurrentDomain.RelativeSearchPath, "x86"); + } - Environment.SetEnvironmentVariable("PATH", envPath, EnvironmentVariableTarget.Process); - } + Environment.SetEnvironmentVariable("PATH", envPath, EnvironmentVariableTarget.Process); + } - private static void SetNativeDllPath() - { - var currentDomain = AppDomain.CurrentDomain; - var privateBinPath = currentDomain.SetupInformation.PrivateBinPath.NullEmpty() ?? currentDomain.BaseDirectory; - var dir = Path.Combine(privateBinPath, Environment.Is64BitProcess ? "x64" : "x86"); + private static void SetNativeDllPath() + { + var currentDomain = AppDomain.CurrentDomain; + var privateBinPath = currentDomain.SetupInformation.PrivateBinPath.NullEmpty() ?? currentDomain.BaseDirectory; + var dir = Path.Combine(privateBinPath, Environment.Is64BitProcess ? "x64" : "x86"); - SetDllDirectory(dir); - } + Logger.DebugFormat("Setting native DLL path to '{0}'.", dir); + + SetDllDirectory(dir); + } /// /// Perform file deploy @@ -574,72 +552,74 @@ private static void SetNativeDllPath() /// Reference to the shadow copied Assembly private static Assembly Probe(AssemblyReference ar, PluginDescriptor d, bool shadowCopy) { - var file = ar.OriginalFile; - - try - { - if (file.Directory == null || file.Directory.Parent == null) - { - throw new InvalidOperationException("The plugin directory for the " + file.Name + - " file exists in a folder outside of the allowed SmartStore folder hierarchy"); - } - - ar.File = InitializeFullTrust(file, shadowCopy); - - // Load assembly locked, because concurrent load calls - even with different assemblies - - // will result in strange app init behaviour. - lock (_lock) - { - // We can now register the plugin definition - ar.Assembly = Assembly.Load(AssemblyName.GetAssemblyName(ar.File.FullName)); - - // Add the reference to the build manager - if (ar.Assembly != null) - { - // Loading assembly can fail in parallel loops. - // In this case, we'll probe again later in a sequential loop. - BuildManager.AddReferencedAssembly(ar.Assembly); - } - } - - ar.ActivationException = null; - } - catch (UnauthorizedAccessException) - { - // Throw the exception if its UnauthorizedAccessException as this will - // be because we most likely cannot copy to the dynamic folder. - throw; - } - catch (ReflectionTypeLoadException ex) - { - var msg = string.Empty; - foreach (var e in ex.LoaderExceptions) - { - msg += e.Message + Environment.NewLine; - } - - ar.ActivationException = CreateException(msg, ex); - } - catch (Exception ex) - { - var msg = string.Empty; - for (var e = ex; e != null; e = e.InnerException) - { - msg += e.Message + Environment.NewLine; - } - - ar.ActivationException = CreateException(msg, ex); - } - - return ar.Assembly; - - Exception CreateException(string message, Exception innerException) - { - return new SmartException( - "Error loading plugin '{0}'".FormatInvariant(d.SystemName) + Environment.NewLine + message, - innerException); - } - } + var file = ar.OriginalFile; + + try + { + if (file.Directory == null || file.Directory.Parent == null) + { + throw new InvalidOperationException("The plugin directory for the " + file.Name + + " file exists in a folder outside of the allowed SmartStore folder hierarchy"); + } + + ar.File = InitializeFullTrust(file, shadowCopy); + + // Load assembly locked, because concurrent load calls - even with different assemblies - + // will result in strange app init behaviour. + lock (_lock) + { + // We can now register the plugin definition + ar.Assembly = Assembly.Load(AssemblyName.GetAssemblyName(ar.File.FullName)); + + // Add the reference to the build manager + if (ar.Assembly != null) + { + // Loading assembly can fail in parallel loops. + // In this case, we'll probe again later in a sequential loop. + BuildManager.AddReferencedAssembly(ar.Assembly); + } + } + + ar.ActivationException = null; + } + catch (UnauthorizedAccessException ex) + { + Logger.Error(ex.Message); + + // Throw the exception if its UnauthorizedAccessException as this will + // be because we most likely cannot copy to the dynamic folder. + throw; + } + catch (ReflectionTypeLoadException ex) + { + var msg = string.Empty; + foreach (var e in ex.LoaderExceptions) + { + msg += e.Message + Environment.NewLine; + } + + ar.ActivationException = CreateException(msg, ex); + } + catch (Exception ex) + { + var msg = string.Empty; + for (var e = ex; e != null; e = e.InnerException) + { + msg += e.Message + Environment.NewLine; + } + + ar.ActivationException = CreateException(msg, ex); + } + + return ar.Assembly; + + Exception CreateException(string message, Exception innerException) + { + return new SmartException( + "Error loading plugin '{0}'".FormatInvariant(d.SystemName) + Environment.NewLine + message, + innerException); + } + } /// /// Used to initialize plugins when running in Full Trust @@ -650,36 +630,36 @@ private static FileInfo InitializeFullTrust(FileInfo dll, bool shadowCopy) { var probedDll = new FileInfo(Path.Combine(_shadowCopyDir.FullName, dll.Name)); - // If instructed to not perform the copy, just return the path to where it is supposed to be - if (!shadowCopy && probedDll.Exists) - return probedDll; + // If instructed to not perform the copy, just return the path to where it is supposed to be + if (!shadowCopy && probedDll.Exists) + return probedDll; - try + try { File.Copy(dll.FullName, probedDll.FullName, true); } - catch (UnauthorizedAccessException) - { - throw new UnauthorizedAccessException(string.Format("Access to the path '{0}' is denied, ensure that read, write and modify permissions are allowed.", probedDll.Directory.FullName)); - } - catch (IOException) + catch (UnauthorizedAccessException) + { + throw new UnauthorizedAccessException(string.Format("Access to the path '{0}' is denied, ensure that read, write and modify permissions are allowed.", probedDll.Directory.FullName)); + } + catch (IOException) { - Debug.WriteLine(probedDll.FullName + " is locked, attempting to rename"); + Logger.WarnFormat("'{0}' is locked, attempting to rename.", probedDll.FullName); // This occurs when the files are locked, // For some reason devenv locks plugin files some times and for another crazy reason you are allowed to rename them // Which releases the lock, so that it what we are doing here, once it's renamed, we can re-shadow copy try { - // If all else fails during the cleanup and we cannot copy over so we need to rename with a GUID - var deleteName = GetNewDeleteName(probedDll); - File.Move(probedDll.FullName, deleteName); + // If all else fails during the cleanup and we cannot copy over so we need to rename with a GUID + var deleteName = GetNewDeleteName(probedDll); + File.Move(probedDll.FullName, deleteName); + } + catch (UnauthorizedAccessException) + { + throw new UnauthorizedAccessException(string.Format("Access to the path '{0}' is denied, ensure that read, write and modify permissions are allowed.", probedDll.Directory.FullName)); } - catch (UnauthorizedAccessException) - { - throw new UnauthorizedAccessException(string.Format("Access to the path '{0}' is denied, ensure that read, write and modify permissions are allowed.", probedDll.Directory.FullName)); - } - catch (IOException exc) + catch (IOException exc) { throw new IOException(probedDll.FullName + " rename failed, cannot initialize plugin", exc); } @@ -703,5 +683,5 @@ private static bool IsPackagePluginFolder(DirectoryInfo folder) if (!folder.Parent.Name.Equals("Plugins", StringComparison.InvariantCultureIgnoreCase)) return false; return true; } - } + } } diff --git a/src/Libraries/SmartStore.Core/Plugins/Providers/DependentWidgetsAttribute.cs b/src/Libraries/SmartStore.Core/Plugins/Providers/DependentWidgetsAttribute.cs index 384b6a7627..6fe776760e 100644 --- a/src/Libraries/SmartStore.Core/Plugins/Providers/DependentWidgetsAttribute.cs +++ b/src/Libraries/SmartStore.Core/Plugins/Providers/DependentWidgetsAttribute.cs @@ -1,28 +1,24 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.Core.Plugins { - /// - /// Enables provider developers to specify one or many widgets, which - /// should automatically get (de)activated when the provider gets (de)activated. - /// Useful in scenarios where separate widgets are responsible for the displaying of provider data. - /// - /// - /// A widget should definitely NOT depend on multiple providers as the activation - /// only occurs on a single item base. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public class DependentWidgetsAttribute : Attribute - { - public DependentWidgetsAttribute(params string[] widgetSystemNames) - { - WidgetSystemNames = widgetSystemNames; - } + /// + /// Enables provider developers to specify one or many widgets, which + /// should automatically get (de)activated when the provider gets (de)activated. + /// Useful in scenarios where separate widgets are responsible for the displaying of provider data. + /// + /// + /// A widget should definitely NOT depend on multiple providers as the activation + /// only occurs on a single item base. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public class DependentWidgetsAttribute : Attribute + { + public DependentWidgetsAttribute(params string[] widgetSystemNames) + { + WidgetSystemNames = widgetSystemNames; + } - public string[] WidgetSystemNames { get; private set; } - } + public string[] WidgetSystemNames { get; private set; } + } } diff --git a/src/Libraries/SmartStore.Core/Plugins/Providers/DisplayOrderAttribute.cs b/src/Libraries/SmartStore.Core/Plugins/Providers/DisplayOrderAttribute.cs index 33a8b3b13e..a60b19aeed 100644 --- a/src/Libraries/SmartStore.Core/Plugins/Providers/DisplayOrderAttribute.cs +++ b/src/Libraries/SmartStore.Core/Plugins/Providers/DisplayOrderAttribute.cs @@ -2,14 +2,14 @@ namespace SmartStore.Core.Plugins { - [AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=false)] - public sealed class DisplayOrderAttribute : Attribute - { - public DisplayOrderAttribute(int displayOrder) - { - DisplayOrder = displayOrder; - } + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class DisplayOrderAttribute : Attribute + { + public DisplayOrderAttribute(int displayOrder) + { + DisplayOrder = displayOrder; + } - public int DisplayOrder { get; set; } - } + public int DisplayOrder { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Plugins/Providers/FriendlyNameAttribute.cs b/src/Libraries/SmartStore.Core/Plugins/Providers/FriendlyNameAttribute.cs index ec406c905b..c95cea4e2d 100644 --- a/src/Libraries/SmartStore.Core/Plugins/Providers/FriendlyNameAttribute.cs +++ b/src/Libraries/SmartStore.Core/Plugins/Providers/FriendlyNameAttribute.cs @@ -2,18 +2,18 @@ namespace SmartStore.Core.Plugins { - [AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=false)] - public sealed class FriendlyNameAttribute : Attribute - { - public FriendlyNameAttribute(string name) - { - Guard.NotNull(name, nameof(name)); + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class FriendlyNameAttribute : Attribute + { + public FriendlyNameAttribute(string name) + { + Guard.NotNull(name, nameof(name)); - Name = name; - } + Name = name; + } - public string Name { get; set; } + public string Name { get; set; } - public string Description { get; set; } - } + public string Description { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Plugins/Providers/IProvider.cs b/src/Libraries/SmartStore.Core/Plugins/Providers/IProvider.cs index fae747723b..409e07edd2 100644 --- a/src/Libraries/SmartStore.Core/Plugins/Providers/IProvider.cs +++ b/src/Libraries/SmartStore.Core/Plugins/Providers/IProvider.cs @@ -2,42 +2,33 @@ namespace SmartStore.Core.Plugins { - public interface IProvider - { - } - - public sealed class Provider where TProvider : IProvider - { - private readonly Lazy _lazy; - - public Provider(Lazy lazy) - { - this._lazy = lazy; - } - - public TProvider Value - { - get { return _lazy.Value; } - } - - public ProviderMetadata Metadata - { - get { return _lazy.Metadata; } - } - - public bool IsValueCreated - { - get { return _lazy.IsValueCreated; } - } - - public Lazy ToLazy() - { - return _lazy; - } - - public override string ToString() - { - return _lazy.Metadata.SystemName; - } - } + public interface IProvider + { + } + + public sealed class Provider where TProvider : IProvider + { + private readonly Lazy _lazy; + + public Provider(Lazy lazy) + { + this._lazy = lazy; + } + + public TProvider Value => _lazy.Value; + + public ProviderMetadata Metadata => _lazy.Metadata; + + public bool IsValueCreated => _lazy.IsValueCreated; + + public Lazy ToLazy() + { + return _lazy; + } + + public override string ToString() + { + return _lazy.Metadata.SystemName; + } + } } diff --git a/src/Libraries/SmartStore.Core/Plugins/Providers/IProviderManager.cs b/src/Libraries/SmartStore.Core/Plugins/Providers/IProviderManager.cs index c2a10fe5b8..4585578355 100644 --- a/src/Libraries/SmartStore.Core/Plugins/Providers/IProviderManager.cs +++ b/src/Libraries/SmartStore.Core/Plugins/Providers/IProviderManager.cs @@ -1,17 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; namespace SmartStore.Core.Plugins { - public interface IProviderManager - { - Provider GetProvider(string systemName, int storeId = 0) where TProvider : IProvider; + public interface IProviderManager + { + Provider GetProvider(string systemName, int storeId = 0) where TProvider : IProvider; - Provider GetProvider(string systemName, int storeId = 0); + Provider GetProvider(string systemName, int storeId = 0); - IEnumerable> GetAllProviders(int storeId = 0) where TProvider : IProvider; + IEnumerable> GetAllProviders(int storeId = 0) where TProvider : IProvider; - IEnumerable> GetAllProviders(int storeId = 0); - } + IEnumerable> GetAllProviders(int storeId = 0); + } } diff --git a/src/Libraries/SmartStore.Core/Plugins/Providers/IUserEditable.cs b/src/Libraries/SmartStore.Core/Plugins/Providers/IUserEditable.cs index 8544372396..453b22f101 100644 --- a/src/Libraries/SmartStore.Core/Plugins/Providers/IUserEditable.cs +++ b/src/Libraries/SmartStore.Core/Plugins/Providers/IUserEditable.cs @@ -1,8 +1,6 @@ -using System; - -namespace SmartStore.Core.Plugins +namespace SmartStore.Core.Plugins { - public interface IUserEditable - { - } + public interface IUserEditable + { + } } diff --git a/src/Libraries/SmartStore.Core/Plugins/Providers/IsHiddenAttribute.cs b/src/Libraries/SmartStore.Core/Plugins/Providers/IsHiddenAttribute.cs index 6ba32b9d5a..b2b0036669 100644 --- a/src/Libraries/SmartStore.Core/Plugins/Providers/IsHiddenAttribute.cs +++ b/src/Libraries/SmartStore.Core/Plugins/Providers/IsHiddenAttribute.cs @@ -2,14 +2,14 @@ namespace SmartStore.Core.Plugins { - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public sealed class IsHiddenAttribute : Attribute - { - public IsHiddenAttribute(bool isHidden) - { - IsHidden = isHidden; - } + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class IsHiddenAttribute : Attribute + { + public IsHiddenAttribute(bool isHidden) + { + IsHidden = isHidden; + } - public bool IsHidden { get; set; } - } + public bool IsHidden { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Plugins/Providers/ProviderMetadata.cs b/src/Libraries/SmartStore.Core/Plugins/Providers/ProviderMetadata.cs index 8b806c43fc..ab44fa593b 100644 --- a/src/Libraries/SmartStore.Core/Plugins/Providers/ProviderMetadata.cs +++ b/src/Libraries/SmartStore.Core/Plugins/Providers/ProviderMetadata.cs @@ -4,143 +4,143 @@ namespace SmartStore.Core.Plugins { - public interface IProviderMetadata - { - /// - /// Gets the provider system name - /// - string SystemName { get; } - - /// - /// Gets the resource key pattern for user data (e.g. FriendlyName) - /// - /// - /// Plugins.{1}.{0} > 0 = provider system name, 1 = propertyname - /// - string ResourceKeyPattern { get; } - - /// - /// Gets the provider friendly name - /// - string FriendlyName { get; } - - /// - /// Gets the provider description - /// - string Description { get; } - - /// - /// Gets the provider display order - /// - int DisplayOrder { get; } - } - - public class ProviderMetadata : IProviderMetadata - { - /// - /// Gets or sets the provider type - /// - public Type ProviderType { get; set; } - - /// - /// Gets or sets the provider's group name - /// - public string GroupName { get; set; } - - /// - /// Gets or sets the provider system name - /// - public string SystemName { get; set; } - - /// - /// Gets or sets the resource key pattern for user data (e.g. FriendlyName) - /// - /// - /// Plugins.{1}.{0} > 0 = provider system name, 1 = propertyname - /// - public string ResourceKeyPattern { get; set; } - - /// - /// Gets or sets the setting key pattern for user data (e.g. DisplayOrder) - /// - /// - /// Plugins.{0}.{1} > 0 = provider system name, 1 = propertyname - /// - public string SettingKeyPattern { get; set; } - - /// - /// Gets or sets the provider friendly name - /// - public string FriendlyName { get; set; } - - /// - /// Gets or sets the provider description - /// - public string Description { get; set; } - - /// - /// Gets or sets the display order - /// - public int DisplayOrder { get; set; } - - /// - /// Gets or sets a value indicating whether the provider is configurable (by implementing ) - /// - public bool IsConfigurable { get; set; } - - /// - /// Gets or sets a value indicating whether the provider is editable by the user (by implementing ) - /// - /// - /// A provider is editable if the user is allowed to change display order and/or localize display name - /// - public bool IsEditable { get; set; } - - /// - /// Gets or sets a value indicating whether the provider is hidden (by decorating with ) - /// - public bool IsHidden { get; set; } - - /// - /// Gets or sets flags that reflects what features of export data processing is supported by a provider - /// - public ExportFeatures ExportFeatures { get; set; } - - /// - /// Gets or sets an array of widget system names, which depend on the current provider - /// - /// - /// Dependent widgets get automatically (de)activated when their parent providers get (de)activated - /// - public string[] DependentWidgets { get; set; } - - /// - /// Gets or sets the instance in which the provider is implemented - /// - /// The value is null, if the provider is part of the application core - public PluginDescriptor PluginDescriptor { get; set; } - - public string GetSettingKey(string name) - { - return SettingKeyPattern.FormatWith(SystemName, name); - } - - public override string ToString() - { - return "Provider '{0}' - {1}".FormatCurrent(SystemName, FriendlyName); - } - - public override bool Equals(object obj) - { - var other = obj as ProviderMetadata; - return other != null && - SystemName != null && - SystemName.Equals(other.SystemName); - } - - public override int GetHashCode() - { - return HashCodeCombiner.Start().Add(typeof(ProviderMetadata)).Add(SystemName).CombinedHash; - } - } + public interface IProviderMetadata + { + /// + /// Gets the provider system name + /// + string SystemName { get; } + + /// + /// Gets the resource key pattern for user data (e.g. FriendlyName) + /// + /// + /// Plugins.{1}.{0} > 0 = provider system name, 1 = propertyname + /// + string ResourceKeyPattern { get; } + + /// + /// Gets the provider friendly name + /// + string FriendlyName { get; } + + /// + /// Gets the provider description + /// + string Description { get; } + + /// + /// Gets the provider display order + /// + int DisplayOrder { get; } + } + + public class ProviderMetadata : IProviderMetadata + { + /// + /// Gets or sets the provider type + /// + public Type ProviderType { get; set; } + + /// + /// Gets or sets the provider's group name + /// + public string GroupName { get; set; } + + /// + /// Gets or sets the provider system name + /// + public string SystemName { get; set; } + + /// + /// Gets or sets the resource key pattern for user data (e.g. FriendlyName) + /// + /// + /// Plugins.{1}.{0} > 0 = provider system name, 1 = propertyname + /// + public string ResourceKeyPattern { get; set; } + + /// + /// Gets or sets the setting key pattern for user data (e.g. DisplayOrder) + /// + /// + /// Plugins.{0}.{1} > 0 = provider system name, 1 = propertyname + /// + public string SettingKeyPattern { get; set; } + + /// + /// Gets or sets the provider friendly name + /// + public string FriendlyName { get; set; } + + /// + /// Gets or sets the provider description + /// + public string Description { get; set; } + + /// + /// Gets or sets the display order + /// + public int DisplayOrder { get; set; } + + /// + /// Gets or sets a value indicating whether the provider is configurable (by implementing ) + /// + public bool IsConfigurable { get; set; } + + /// + /// Gets or sets a value indicating whether the provider is editable by the user (by implementing ) + /// + /// + /// A provider is editable if the user is allowed to change display order and/or localize display name + /// + public bool IsEditable { get; set; } + + /// + /// Gets or sets a value indicating whether the provider is hidden (by decorating with ) + /// + public bool IsHidden { get; set; } + + /// + /// Gets or sets flags that reflects what features of export data processing is supported by a provider + /// + public ExportFeatures ExportFeatures { get; set; } + + /// + /// Gets or sets an array of widget system names, which depend on the current provider + /// + /// + /// Dependent widgets get automatically (de)activated when their parent providers get (de)activated + /// + public string[] DependentWidgets { get; set; } + + /// + /// Gets or sets the instance in which the provider is implemented + /// + /// The value is null, if the provider is part of the application core + public PluginDescriptor PluginDescriptor { get; set; } + + public string GetSettingKey(string name) + { + return SettingKeyPattern.FormatWith(SystemName, name); + } + + public override string ToString() + { + return "Provider '{0}' - {1}".FormatCurrent(SystemName, FriendlyName); + } + + public override bool Equals(object obj) + { + var other = obj as ProviderMetadata; + return other != null && + SystemName != null && + SystemName.Equals(other.SystemName); + } + + public override int GetHashCode() + { + return HashCodeCombiner.Start().Add(typeof(ProviderMetadata)).Add(SystemName).CombinedHash; + } + } } diff --git a/src/Libraries/SmartStore.Core/Plugins/Providers/SystemNameAttribute.cs b/src/Libraries/SmartStore.Core/Plugins/Providers/SystemNameAttribute.cs index 4bbb2e7a4e..0946be4c34 100644 --- a/src/Libraries/SmartStore.Core/Plugins/Providers/SystemNameAttribute.cs +++ b/src/Libraries/SmartStore.Core/Plugins/Providers/SystemNameAttribute.cs @@ -2,15 +2,15 @@ namespace SmartStore.Core.Plugins { - [AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=false)] - public sealed class SystemNameAttribute : Attribute - { - public SystemNameAttribute(string name) - { - Guard.NotEmpty(name, nameof(name)); - Name = name; - } + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class SystemNameAttribute : Attribute + { + public SystemNameAttribute(string name) + { + Guard.NotEmpty(name, nameof(name)); + Name = name; + } - public string Name { get; set; } - } + public string Name { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Properties/AssemblyInfo.cs b/src/Libraries/SmartStore.Core/Properties/AssemblyInfo.cs index ddde4c0b4a..7f42ecee30 100644 --- a/src/Libraries/SmartStore.Core/Properties/AssemblyInfo.cs +++ b/src/Libraries/SmartStore.Core/Properties/AssemblyInfo.cs @@ -2,6 +2,8 @@ using System.Runtime.CompilerServices; [assembly: AssemblyTitle("SmartStore.Core")] +[assembly: InternalsVisibleTo("SmartStore.Services")] +[assembly: InternalsVisibleTo("SmartStore.Data")] [assembly: InternalsVisibleTo("SmartStore.Core.Tests")] [assembly: InternalsVisibleTo("SmartStore.Services.Tests")] [assembly: InternalsVisibleTo("SmartStore.Web.Mvc.Tests")] diff --git a/src/Libraries/SmartStore.Core/RouteInfo.cs b/src/Libraries/SmartStore.Core/RouteInfo.cs index 84827ddd44..e98f9d9004 100644 --- a/src/Libraries/SmartStore.Core/RouteInfo.cs +++ b/src/Libraries/SmartStore.Core/RouteInfo.cs @@ -1,29 +1,28 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Web.Routing; using Newtonsoft.Json; namespace SmartStore { - [JsonConverter(typeof(RouteInfoConverter))] - public class RouteInfo - { - public RouteInfo(RouteInfo cloneFrom) - : this(cloneFrom.Action, cloneFrom.Controller, new RouteValueDictionary(cloneFrom.RouteValues)) - { - Guard.NotNull(cloneFrom, nameof(cloneFrom)); - } + [JsonConverter(typeof(RouteInfoConverter))] + public class RouteInfo + { + public RouteInfo(RouteInfo cloneFrom) + : this(cloneFrom.Action, cloneFrom.Controller, new RouteValueDictionary(cloneFrom.RouteValues)) + { + Guard.NotNull(cloneFrom, nameof(cloneFrom)); + } public RouteInfo(string action, object routeValues) : this(action, null, routeValues) { } - public RouteInfo(string action, string controller, object routeValues) - : this(action, controller, new RouteValueDictionary(routeValues)) - { - } + public RouteInfo(string action, string controller, object routeValues) + : this(action, controller, new RouteValueDictionary(routeValues)) + { + } public RouteInfo(string action, IDictionary routeValues) : this(action, null, routeValues) @@ -31,103 +30,100 @@ public RouteInfo(string action, IDictionary routeValues) } public RouteInfo(string action, string controller, IDictionary routeValues) - : this(action, controller, new RouteValueDictionary(routeValues)) - { - Guard.NotNull(routeValues, nameof(routeValues)); - } + : this(action, controller, new RouteValueDictionary(routeValues)) + { + Guard.NotNull(routeValues, nameof(routeValues)); + } public RouteInfo(string action, RouteValueDictionary routeValues) : this(action, null, routeValues) { } - [JsonConstructor] + [JsonConstructor] public RouteInfo(string action, string controller, RouteValueDictionary routeValues) - { - Guard.NotEmpty(action, nameof(action)); - Guard.NotNull(routeValues, nameof(routeValues)); - - this.Action = action; - this.Controller = controller; - this.RouteValues = routeValues; - } - - public string Action - { - get; - private set; - } - - public string Controller - { - get; - private set; - } - - public RouteValueDictionary RouteValues - { - get; - private set; - } - } - - #region JsonConverter - - public class RouteInfoConverter : JsonConverter - { - public override bool CanWrite - { - get { return false; } - } - - public override bool CanConvert(Type objectType) - { - return objectType == typeof(RouteInfo); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - string action = null; - string controller = null; - RouteValueDictionary routeValues = null; - - reader.Read(); - while (reader.TokenType == JsonToken.PropertyName) - { - string a = reader.Value.ToString(); - if (string.Equals(a, "Action", StringComparison.OrdinalIgnoreCase)) - { - reader.Read(); - action = serializer.Deserialize(reader); - } - else if (string.Equals(a, "Controller", StringComparison.OrdinalIgnoreCase)) - { - reader.Read(); - controller = serializer.Deserialize(reader); - } - else if (string.Equals(a, "RouteValues", StringComparison.OrdinalIgnoreCase)) - { - reader.Read(); - routeValues = serializer.Deserialize(reader); - } - else - { - reader.Skip(); - } - - reader.Read(); - } - - var routeInfo = Activator.CreateInstance(objectType, new object[] { action, controller, routeValues }); - - return routeInfo; - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - throw new NotSupportedException(); - } - } - - #endregion + { + Guard.NotEmpty(action, nameof(action)); + Guard.NotNull(routeValues, nameof(routeValues)); + + this.Action = action; + this.Controller = controller; + this.RouteValues = routeValues; + } + + public string Action + { + get; + private set; + } + + public string Controller + { + get; + private set; + } + + public RouteValueDictionary RouteValues + { + get; + private set; + } + } + + #region JsonConverter + + public class RouteInfoConverter : JsonConverter + { + public override bool CanWrite => false; + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(RouteInfo); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + string action = null; + string controller = null; + RouteValueDictionary routeValues = null; + + reader.Read(); + while (reader.TokenType == JsonToken.PropertyName) + { + string a = reader.Value.ToString(); + if (string.Equals(a, "Action", StringComparison.OrdinalIgnoreCase)) + { + reader.Read(); + action = serializer.Deserialize(reader); + } + else if (string.Equals(a, "Controller", StringComparison.OrdinalIgnoreCase)) + { + reader.Read(); + controller = serializer.Deserialize(reader); + } + else if (string.Equals(a, "RouteValues", StringComparison.OrdinalIgnoreCase)) + { + reader.Read(); + routeValues = serializer.Deserialize(reader); + } + else + { + reader.Skip(); + } + + reader.Read(); + } + + var routeInfo = Activator.CreateInstance(objectType, new object[] { action, controller, routeValues }); + + return routeInfo; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotSupportedException(); + } + } + + #endregion } diff --git a/src/Libraries/SmartStore.Core/Rules/Domain/IRulesContainer.cs b/src/Libraries/SmartStore.Core/Rules/Domain/IRulesContainer.cs new file mode 100644 index 0000000000..5314c4ac39 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Rules/Domain/IRulesContainer.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace SmartStore.Rules.Domain +{ + /// + /// Represents an entity which supports rule sets. + /// + public partial interface IRulesContainer + { + /// + /// Gets assigned rule sets. + /// + ICollection RuleSets { get; } + } +} diff --git a/src/Libraries/SmartStore.Core/Rules/Domain/RuleEntity.cs b/src/Libraries/SmartStore.Core/Rules/Domain/RuleEntity.cs index 2f769bae99..587be18e1f 100644 --- a/src/Libraries/SmartStore.Core/Rules/Domain/RuleEntity.cs +++ b/src/Libraries/SmartStore.Core/Rules/Domain/RuleEntity.cs @@ -1,11 +1,6 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; using Newtonsoft.Json; using SmartStore.Core; @@ -30,11 +25,11 @@ public partial class RuleEntity : BaseEntity //public string Member { get; set; } [DataMember] - [Required, StringLength(10)] + [Required, StringLength(20)] public string Operator { get; set; } [DataMember] - [StringLength(400)] + [MaxLength] public string Value { get; set; } [DataMember] @@ -42,9 +37,6 @@ public partial class RuleEntity : BaseEntity public int DisplayOrder { get; set; } [NotMapped] - public bool IsGroup - { - get => RuleType.IsCaseInsensitiveEqual("Group"); - } + public bool IsGroup => RuleType.IsCaseInsensitiveEqual("Group"); } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Rules/Domain/RuleSetEntity.cs b/src/Libraries/SmartStore.Core/Rules/Domain/RuleSetEntity.cs index 9cfe727688..96d4bb591e 100644 --- a/src/Libraries/SmartStore.Core/Rules/Domain/RuleSetEntity.cs +++ b/src/Libraries/SmartStore.Core/Rules/Domain/RuleSetEntity.cs @@ -2,17 +2,24 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; using SmartStore.Core; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.Discounts; +using SmartStore.Core.Domain.Payments; +using SmartStore.Core.Domain.Shipping; namespace SmartStore.Rules.Domain { public partial class RuleSetEntity : BaseEntity, IAuditable { private ICollection _rules; + private ICollection _discounts; + private ICollection _shippingMethods; + private ICollection _paymentMethods; + private ICollection _customerRoles; + private ICollection _categories; [DataMember] [StringLength(200)] @@ -28,11 +35,12 @@ public partial class RuleSetEntity : BaseEntity, IAuditable [Required] [Index("IX_RuleSetEntity_Scope", Order = 1)] public RuleScope Scope { get; set; } - + /// /// True when this set is an internal composite container for rules within another ruleset. /// + [Index] public bool IsSubGroup { get; set; } public LogicalRuleOperator LogicalOperator { get; set; } @@ -45,8 +53,53 @@ public partial class RuleSetEntity : BaseEntity, IAuditable public virtual ICollection Rules { - get { return _rules ?? (_rules = new HashSet()); } - protected internal set { _rules = value; } + get => _rules ?? (_rules = new HashSet()); + protected internal set => _rules = value; + } + + /// + /// Gets or sets assigned discounts. + /// + public virtual ICollection Discounts + { + get => _discounts ?? (_discounts = new HashSet()); + protected set => _discounts = value; + } + + /// + /// Gets or sets assigned shipping methods. + /// + public virtual ICollection ShippingMethods + { + get => _shippingMethods ?? (_shippingMethods = new HashSet()); + protected set => _shippingMethods = value; + } + + /// + /// Gets or sets assigned payment methods. + /// + public virtual ICollection PaymentMethods + { + get => _paymentMethods ?? (_paymentMethods = new HashSet()); + protected set => _paymentMethods = value; + } + + /// + /// Gets or sets assigned customer roles. + /// + public virtual ICollection CustomerRoles + { + get => _customerRoles ?? (_customerRoles = new HashSet()); + protected set => _customerRoles = value; + } + + /// + /// Gets or sets assigned categories. + /// + public virtual ICollection Categories + { + get => _categories ?? (_categories = new HashSet()); + protected set => _categories = value; } } } diff --git a/src/Libraries/SmartStore.Core/Rules/Exceptions/InvalidRuleOperatorException.cs b/src/Libraries/SmartStore.Core/Rules/Exceptions/InvalidRuleOperatorException.cs index 3bb110bf4d..2361ffe53b 100644 --- a/src/Libraries/SmartStore.Core/Rules/Exceptions/InvalidRuleOperatorException.cs +++ b/src/Libraries/SmartStore.Core/Rules/Exceptions/InvalidRuleOperatorException.cs @@ -1,14 +1,10 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.Rules { public sealed class InvalidRuleOperatorException : NotSupportedException { - public InvalidRuleOperatorException(RuleExpression expression) + public InvalidRuleOperatorException(RuleExpression expression) : base("The rule type '{0}' does not support the rule operator '{1}'.".FormatInvariant(expression.Descriptor.Name, expression.Operator)) { // TODO: expression.Descriptor.Name could be null or empty (?) diff --git a/src/Libraries/SmartStore.Core/Rules/Filters/ExpressionHelper.cs b/src/Libraries/SmartStore.Core/Rules/Filters/ExpressionHelper.cs index 81a5f409b3..4ae4d8fa92 100644 --- a/src/Libraries/SmartStore.Core/Rules/Filters/ExpressionHelper.cs +++ b/src/Libraries/SmartStore.Core/Rules/Filters/ExpressionHelper.cs @@ -1,11 +1,8 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; -using System.Linq; using System.Linq.Expressions; using System.Reflection; -using System.Threading.Tasks; namespace SmartStore.Rules.Filters { @@ -125,7 +122,10 @@ public static Expression CreateValueExpression(Type targetType, object value, Cu } else if (value is IConvertible) { - value = Convert.ChangeType(value, nonNullableType, culture ?? CultureInfo.InvariantCulture); + if (typeof(IConvertible).IsAssignableFrom(nonNullableType)) + { + value = Convert.ChangeType(value, nonNullableType, culture ?? CultureInfo.InvariantCulture); + } } } } @@ -151,7 +151,7 @@ public static Expression CreateConstantExpression(object value, Type type = null public static LambdaExpression CreateLambdaExpression(ParameterExpression p, Expression body) { return Expression.Lambda( - new FilterExpressionVisitor(p).Visit(body), + new FilterExpressionVisitor(p).Visit(body), new[] { p }); } } diff --git a/src/Libraries/SmartStore.Core/Rules/Filters/FilterDescriptor.cs b/src/Libraries/SmartStore.Core/Rules/Filters/FilterDescriptor.cs index cba9a28dc7..b27e7df330 100644 --- a/src/Libraries/SmartStore.Core/Rules/Filters/FilterDescriptor.cs +++ b/src/Libraries/SmartStore.Core/Rules/Filters/FilterDescriptor.cs @@ -1,13 +1,7 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using SmartStore.Core.Domain.Customers; -using SmartStore.Core.Domain.Orders; namespace SmartStore.Rules.Filters { @@ -28,7 +22,7 @@ public virtual Expression GetExpression(RuleOperator op, Expression valueExpress } } - public class FilterDescriptor : FilterDescriptor where T : class + public class FilterDescriptor : FilterDescriptor where T : class { public FilterDescriptor(Expression> expression) : base(expression) // TODO { @@ -81,12 +75,12 @@ public override Expression GetExpression(RuleOperator op, Expression valueExpres } } - public class AnyFilterDescriptor : PredicateFilterDescriptor - where T : class + public class AnyFilterDescriptor : PredicateFilterDescriptor + where T : class where TAny : class { public AnyFilterDescriptor( - Expression>> path, + Expression>> path, Expression> anyPredicate) : base("Any", path, anyPredicate) { diff --git a/src/Libraries/SmartStore.Core/Rules/Filters/FilterExpression.cs b/src/Libraries/SmartStore.Core/Rules/Filters/FilterExpression.cs index 9d664f9042..1d3f1df065 100644 --- a/src/Libraries/SmartStore.Core/Rules/Filters/FilterExpression.cs +++ b/src/Libraries/SmartStore.Core/Rules/Filters/FilterExpression.cs @@ -1,7 +1,4 @@ -using System; -using System.Globalization; -using System.Linq; -using System.Linq.Expressions; +using System.Linq.Expressions; namespace SmartStore.Rules.Filters { @@ -18,7 +15,7 @@ protected virtual Expression CreateBodyExpression(ParameterExpression node, bool { return this.Descriptor.GetExpression( this.Operator, - ExpressionHelper.CreateValueExpression(Descriptor.MemberExpression.Body.Type, this.Value), + ExpressionHelper.CreateValueExpression(Descriptor.MemberExpression.Body.Type, this.Value), liftToNull); } } diff --git a/src/Libraries/SmartStore.Core/Rules/Filters/FilterExpressionGroup.cs b/src/Libraries/SmartStore.Core/Rules/Filters/FilterExpressionGroup.cs index 94b002fb7b..a5e13fc09b 100644 --- a/src/Libraries/SmartStore.Core/Rules/Filters/FilterExpressionGroup.cs +++ b/src/Libraries/SmartStore.Core/Rules/Filters/FilterExpressionGroup.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.Rules.Filters { @@ -27,10 +25,7 @@ public FilterExpressionGroup(Type entityType) public bool IsSubGroup { get; set; } public IRuleProvider Provider { get; set; } - public IEnumerable Expressions - { - get => _expressions; - } + public IEnumerable Expressions => _expressions; public void AddExpressions(params IRuleExpression[] expressions) { diff --git a/src/Libraries/SmartStore.Core/Rules/Filters/FilterExpressionVisitor.cs b/src/Libraries/SmartStore.Core/Rules/Filters/FilterExpressionVisitor.cs index d65a823a34..31ab7948b5 100644 --- a/src/Libraries/SmartStore.Core/Rules/Filters/FilterExpressionVisitor.cs +++ b/src/Libraries/SmartStore.Core/Rules/Filters/FilterExpressionVisitor.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Threading.Tasks; +using System.Linq.Expressions; namespace SmartStore.Rules.Filters { diff --git a/src/Libraries/SmartStore.Core/Rules/Filters/QueryableExtensions.cs b/src/Libraries/SmartStore.Core/Rules/Filters/QueryableExtensions.cs index f82b7e60d6..7352727c7b 100644 --- a/src/Libraries/SmartStore.Core/Rules/Filters/QueryableExtensions.cs +++ b/src/Libraries/SmartStore.Core/Rules/Filters/QueryableExtensions.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.Rules.Filters { diff --git a/src/Libraries/SmartStore.Core/Rules/IRuleConstraint.cs b/src/Libraries/SmartStore.Core/Rules/IRuleConstraint.cs index b303e4cf28..7ecb6fbb36 100644 --- a/src/Libraries/SmartStore.Core/Rules/IRuleConstraint.cs +++ b/src/Libraries/SmartStore.Core/Rules/IRuleConstraint.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SmartStore.Rules +namespace SmartStore.Rules { public interface IRuleConstraint { diff --git a/src/Libraries/SmartStore.Core/Rules/IRuleProvider.cs b/src/Libraries/SmartStore.Core/Rules/IRuleProvider.cs index 884118295c..fd0039c0a6 100644 --- a/src/Libraries/SmartStore.Core/Rules/IRuleProvider.cs +++ b/src/Libraries/SmartStore.Core/Rules/IRuleProvider.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using SmartStore.Rules.Domain; namespace SmartStore.Rules @@ -33,12 +30,22 @@ protected virtual void ConvertRule(RuleEntity entity, RuleExpression expression) Guard.NotNull(expression, nameof(expression)); var descriptor = RuleDescriptors.FindDescriptor(entity.RuleType); - if (descriptor == null || descriptor.Scope != this.Scope) + if (descriptor == null) { - // TODO: ErrHandling and EmptynessCheck + // A descriptor for this entity data does not exist. Allow deletion of it. + descriptor = new InvalidRuleDescriptor(Scope) + { + Name = entity.RuleType, + DisplayName = entity.RuleType + }; + } + else if (descriptor.Scope != Scope) + { + throw new SmartException($"Differing rule scope {descriptor.Scope}. Expected {Scope}."); } expression.Id = entity.Id; + expression.RuleSetId = entity.RuleSetId; expression.Descriptor = descriptor; expression.Operator = entity.Operator; expression.RawValue = entity.Value; diff --git a/src/Libraries/SmartStore.Core/Rules/IRuleVisitor.cs b/src/Libraries/SmartStore.Core/Rules/IRuleVisitor.cs index 4c74dd3609..70b7cbfe4b 100644 --- a/src/Libraries/SmartStore.Core/Rules/IRuleVisitor.cs +++ b/src/Libraries/SmartStore.Core/Rules/IRuleVisitor.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using SmartStore.Rules.Domain; +using SmartStore.Rules.Domain; namespace SmartStore.Rules { diff --git a/src/Libraries/SmartStore.Core/Rules/Operators/CommonOperators.cs b/src/Libraries/SmartStore.Core/Rules/Operators/CommonOperators.cs index de57ade318..a732b49424 100644 --- a/src/Libraries/SmartStore.Core/Rules/Operators/CommonOperators.cs +++ b/src/Libraries/SmartStore.Core/Rules/Operators/CommonOperators.cs @@ -1,12 +1,11 @@ -using System; -using System.Linq.Expressions; +using System.Linq.Expressions; using SmartStore.Rules.Filters; namespace SmartStore.Rules.Operators { internal sealed class EqualOperator : RuleOperator { - internal EqualOperator() + internal EqualOperator() : base("=") { } protected override Expression GenerateExpression(Expression left, Expression right, bool liftToNull) @@ -23,7 +22,7 @@ protected override Expression GenerateExpression(Expression left, Expression rig internal sealed class NotEqualOperator : RuleOperator { - internal NotEqualOperator() + internal NotEqualOperator() : base("!=") { } protected override Expression GenerateExpression(Expression left, Expression right, bool liftToNull) @@ -40,26 +39,26 @@ protected override Expression GenerateExpression(Expression left, Expression rig internal sealed class IsNullOperator : RuleOperator { - internal IsNullOperator() + internal IsNullOperator() : base("IsNull") { } protected override Expression GenerateExpression(Expression left, Expression right, bool liftToNull) { return Expression.Equal( - left, + left, ExpressionHelper.CreateConstantExpression(null, GetBodyType(left))); } } internal sealed class IsNotNullOperator : RuleOperator { - internal IsNotNullOperator() + internal IsNotNullOperator() : base("IsNotNull") { } protected override Expression GenerateExpression(Expression left, Expression right, bool liftToNull) { return Expression.NotEqual( - left, + left, ExpressionHelper.CreateConstantExpression(null, GetBodyType(left))); } } diff --git a/src/Libraries/SmartStore.Core/Rules/Operators/ListOperators.cs b/src/Libraries/SmartStore.Core/Rules/Operators/ListOperators.cs index a0c4e17ba7..12b3b240ca 100644 --- a/src/Libraries/SmartStore.Core/Rules/Operators/ListOperators.cs +++ b/src/Libraries/SmartStore.Core/Rules/Operators/ListOperators.cs @@ -1,8 +1,6 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq.Expressions; -using System.Reflection; using SmartStore.Rules.Filters; namespace SmartStore.Rules.Operators @@ -15,7 +13,7 @@ internal NotInOperator() internal class InOperator : RuleOperator { - internal InOperator() + internal InOperator() : this("In", false) { } protected InOperator(string op, bool negate) @@ -41,7 +39,7 @@ protected override Expression GenerateExpression(Expression left /* member expre var containsMethod = ExpressionHelper.GetCollectionContainsMethod(itemType); return Expression.Equal( - Expression.Call(right, containsMethod, left), + Expression.Call(right, containsMethod, left), Negate ? ExpressionHelper.FalseLiteral : ExpressionHelper.TrueLiteral); } @@ -57,4 +55,32 @@ protected override Expression GenerateExpression(Expression left /* member expre // return null; //} } + + + internal class NotAllInOperator : AllInOperator + { + internal NotAllInOperator() + : base("NotAllIn", true) { } + } + + internal class AllInOperator : RuleOperator + { + internal AllInOperator() + : base("AllIn") + { + } + + protected AllInOperator(string op, bool negate) + : base(op) + { + Negate = negate; + } + + private bool Negate { get; set; } + + protected override Expression GenerateExpression(Expression left, Expression right, bool liftToNull) + { + throw new NotImplementedException(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Rules/Operators/NumericOperators.cs b/src/Libraries/SmartStore.Core/Rules/Operators/NumericOperators.cs index be536465b1..0e6435c69e 100644 --- a/src/Libraries/SmartStore.Core/Rules/Operators/NumericOperators.cs +++ b/src/Libraries/SmartStore.Core/Rules/Operators/NumericOperators.cs @@ -1,11 +1,10 @@ -using System; -using System.Linq.Expressions; +using System.Linq.Expressions; namespace SmartStore.Rules.Operators { internal sealed class LessThanOperator : RuleOperator { - internal LessThanOperator() + internal LessThanOperator() : base("<") { } protected override Expression GenerateExpression(Expression left, Expression right, bool liftToNull) @@ -16,7 +15,7 @@ protected override Expression GenerateExpression(Expression left, Expression rig internal sealed class LessThanOrEqualOperator : RuleOperator { - internal LessThanOrEqualOperator() + internal LessThanOrEqualOperator() : base("<=") { } protected override Expression GenerateExpression(Expression left, Expression right, bool liftToNull) @@ -27,7 +26,7 @@ protected override Expression GenerateExpression(Expression left, Expression rig internal sealed class GreaterThanOperator : RuleOperator { - internal GreaterThanOperator() + internal GreaterThanOperator() : base(">") { } protected override Expression GenerateExpression(Expression left, Expression right, bool liftToNull) @@ -38,7 +37,7 @@ protected override Expression GenerateExpression(Expression left, Expression rig internal sealed class GreaterThanOrEqualOperator : RuleOperator { - internal GreaterThanOrEqualOperator() + internal GreaterThanOrEqualOperator() : base(">=") { } protected override Expression GenerateExpression(Expression left, Expression right, bool liftToNull) diff --git a/src/Libraries/SmartStore.Core/Rules/Operators/StringOperators.cs b/src/Libraries/SmartStore.Core/Rules/Operators/StringOperators.cs index 1ffa12af6b..731871e018 100644 --- a/src/Libraries/SmartStore.Core/Rules/Operators/StringOperators.cs +++ b/src/Libraries/SmartStore.Core/Rules/Operators/StringOperators.cs @@ -1,5 +1,4 @@ -using System; -using System.Linq.Expressions; +using System.Linq.Expressions; using SmartStore.Rules.Filters; namespace SmartStore.Rules.Operators @@ -66,28 +65,28 @@ protected override Expression GenerateExpression(Expression left, Expression rig internal sealed class StartsWithOperator : RuleOperator { - internal StartsWithOperator() + internal StartsWithOperator() : base("StartsWith") { } protected override Expression GenerateExpression(Expression left, Expression right, bool liftToNull) { var methodInfo = ExpressionHelper.StringStartsWithMethod; return Expression.Equal( - methodInfo.ToCaseInsensitiveStringMethodCall(left, right, liftToNull), + methodInfo.ToCaseInsensitiveStringMethodCall(left, right, liftToNull), ExpressionHelper.TrueLiteral); } } internal sealed class EndsWithOperator : RuleOperator { - internal EndsWithOperator() + internal EndsWithOperator() : base("EndsWith") { } protected override Expression GenerateExpression(Expression left, Expression right, bool liftToNull) { var methodInfo = ExpressionHelper.StringEndsWithMethod; return Expression.Equal( - methodInfo.ToCaseInsensitiveStringMethodCall(left, right, liftToNull), + methodInfo.ToCaseInsensitiveStringMethodCall(left, right, liftToNull), ExpressionHelper.TrueLiteral); } } @@ -100,7 +99,7 @@ internal NotContainsOperator() internal class ContainsOperator : RuleOperator { - internal ContainsOperator() + internal ContainsOperator() : this("Contains", false) { } protected ContainsOperator(string op, bool negate) @@ -114,7 +113,7 @@ protected ContainsOperator(string op, bool negate) protected override Expression GenerateExpression(Expression left, Expression right, bool liftToNull) { return Expression.Equal( - ExpressionHelper.StringContainsMethod.ToCaseInsensitiveStringMethodCall(left, right, liftToNull), + ExpressionHelper.StringContainsMethod.ToCaseInsensitiveStringMethodCall(left, right, liftToNull), Negate ? ExpressionHelper.FalseLiteral : ExpressionHelper.TrueLiteral); } } diff --git a/src/Libraries/SmartStore.Core/Rules/Rendering/IRuleOptionsProvider.cs b/src/Libraries/SmartStore.Core/Rules/Rendering/IRuleOptionsProvider.cs new file mode 100644 index 0000000000..001a020e31 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Rules/Rendering/IRuleOptionsProvider.cs @@ -0,0 +1,44 @@ +namespace SmartStore.Rules +{ + /// + /// Interface to provide select list options (remote only) for rules. . + /// + public partial interface IRuleOptionsProvider + { + /// + /// Indicates whether this provider can provide select list options for a rule expression. + /// + /// Name of the data source. + /// true can provide options otherwise false. + bool Matches(string dataSource); + + /// + /// Gets options for a rule. + /// + /// The reason for the request. + /// Rule expression + /// Page index if provided options are paged. + /// Page size if provided options are paged. + /// Optional search term entered by user in select control. + /// Rule options result. + RuleOptionsResult GetOptions(RuleOptionsRequestReason reason, IRuleExpression expression, int pageIndex, int pageSize, string searchTerm); + + /// + /// Gets options for a rule. + /// + /// The reason for the request. + /// Rule descriptor. + /// Expression value. + /// Page index if provided options are paged. + /// Page size if provided options are paged. + /// Optional search term entered by user in select control. + /// Rule options result. + RuleOptionsResult GetOptions( + RuleOptionsRequestReason reason, + RuleDescriptor descriptor, + string value, + int pageIndex, + int pageSize, + string searchTerm); + } +} diff --git a/src/Libraries/SmartStore.Core/Rules/Rendering/IRuleTemplateSelector.cs b/src/Libraries/SmartStore.Core/Rules/Rendering/IRuleTemplateSelector.cs new file mode 100644 index 0000000000..4c05286231 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Rules/Rendering/IRuleTemplateSelector.cs @@ -0,0 +1,12 @@ +namespace SmartStore.Rules +{ + public interface IRuleTemplateSelector + { + /// + /// Gets infos about a rule value template. + /// + /// Rule descriptor. + /// Rule value template infos. + RuleTemplateInfo GetTemplate(RuleDescriptor descriptor); + } +} diff --git a/src/Libraries/SmartStore.Core/Rules/Rendering/RuleOptionsResult.cs b/src/Libraries/SmartStore.Core/Rules/Rendering/RuleOptionsResult.cs new file mode 100644 index 0000000000..26e590367e --- /dev/null +++ b/src/Libraries/SmartStore.Core/Rules/Rendering/RuleOptionsResult.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; + +namespace SmartStore.Rules +{ + public enum RuleOptionsRequestReason + { + /// + /// Get options for select list. + /// + SelectListOptions = 0, + + /// + /// Get display names of selected options. + /// + SelectedDisplayNames + } + + + public class RuleOptionsResult + { + public RuleOptionsResult() : this(null) + { + } + + public RuleOptionsResult(params RuleValueSelectListOption[] options) + { + Options = new List(options ?? Enumerable.Empty()); + } + + /// + /// Select list options or display names of selected values, depending on . + /// + public IList Options { get; protected set; } + + /// + /// Indicates whether the provided data is paged. + /// + public bool IsPaged { get; set; } + + /// + /// Indicates whether further data is available. + /// + public bool HasMoreData { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Core/Rules/Rendering/RuleTemplateInfo.cs b/src/Libraries/SmartStore.Core/Rules/Rendering/RuleTemplateInfo.cs new file mode 100644 index 0000000000..d6d5987368 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Rules/Rendering/RuleTemplateInfo.cs @@ -0,0 +1,7 @@ +namespace SmartStore.Rules +{ + public class RuleTemplateInfo + { + public string TemplateName { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Core/Rules/Rendering/RuleTemplateSelector.cs b/src/Libraries/SmartStore.Core/Rules/Rendering/RuleTemplateSelector.cs new file mode 100644 index 0000000000..fe6353cd0f --- /dev/null +++ b/src/Libraries/SmartStore.Core/Rules/Rendering/RuleTemplateSelector.cs @@ -0,0 +1,62 @@ +namespace SmartStore.Rules +{ + public class RuleTemplateSelector : IRuleTemplateSelector + { + public RuleTemplateInfo GetTemplate(RuleDescriptor descriptor) + { + Guard.NotNull(descriptor, nameof(descriptor)); + + var info = new RuleTemplateInfo + { + TemplateName = GetValueTemplateName(descriptor) + }; + + return info; + } + + protected virtual string GetValueTemplateName(RuleDescriptor descriptor) + { + if (descriptor.Metadata.TryGetValue("ValueTemplateName", out var val) && + val is string name && + name.HasValue()) + { + return name; + } + + string templateName; + var type = descriptor.RuleType; + + if (descriptor.SelectList != null || type == RuleType.IntArray || type == RuleType.FloatArray || type == RuleType.StringArray) + { + templateName = "Dropdown"; + } + else if (type == RuleType.Boolean || type == RuleType.NullableBoolean) + { + templateName = "Boolean"; + } + else if (type == RuleType.Int || type == RuleType.NullableInt) + { + templateName = "Int32"; + } + else if (type == RuleType.Float || type == RuleType.NullableFloat) + { + templateName = "Float"; + } + else if (type == RuleType.Money) + { + templateName = "Decimal"; + } + else if (type == RuleType.DateTime || type == RuleType.NullableDateTime) + { + templateName = "DateTime"; + } + else + { + // Fallback to simple text-box. + templateName = "TextBox"; + } + + return "ValueTemplates/" + templateName; + } + } +} diff --git a/src/Libraries/SmartStore.Core/Rules/RuleDescriptor.cs b/src/Libraries/SmartStore.Core/Rules/RuleDescriptor.cs index 60fc7dffda..8bba30e8e6 100644 --- a/src/Libraries/SmartStore.Core/Rules/RuleDescriptor.cs +++ b/src/Libraries/SmartStore.Core/Rules/RuleDescriptor.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.Rules { @@ -13,22 +10,44 @@ public abstract class RuleDescriptor protected RuleDescriptor(RuleScope scope) { Scope = scope; + Constraints = new IRuleConstraint[0]; + Metadata = new Dictionary(); } public RuleScope Scope { get; protected set; } public string Name { get; set; } public string DisplayName { get; set; } public string Description { get; set; } + public string GroupKey { get; set; } + + public bool IsValid => false == (this is InvalidRuleDescriptor); public RuleType RuleType { get; set; } public RuleValueSelectList SelectList { get; set; } + + /// + /// Indicates whether the rule compares the values of two sequences. + /// + public bool IsComparingSequences { get; set; } + public IEnumerable Constraints { get; set; } public IDictionary Metadata { get; } public RuleOperator[] Operators { - get => _operators ?? (_operators = RuleType.GetValidOperators().ToArray()); + get => _operators ?? (_operators = RuleType.GetValidOperators(IsComparingSequences).ToArray()); set => _operators = value; } } + + + public class InvalidRuleDescriptor : RuleDescriptor + { + public InvalidRuleDescriptor(RuleScope scope) + : base(scope) + { + RuleType = RuleType.String; + Constraints = new IRuleConstraint[0]; + } + } } diff --git a/src/Libraries/SmartStore.Core/Rules/RuleDescriptorCollection.cs b/src/Libraries/SmartStore.Core/Rules/RuleDescriptorCollection.cs index 4d24d3a9b2..7255dba078 100644 --- a/src/Libraries/SmartStore.Core/Rules/RuleDescriptorCollection.cs +++ b/src/Libraries/SmartStore.Core/Rules/RuleDescriptorCollection.cs @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.Rules { @@ -29,7 +26,12 @@ protected override string GetKeyForItem(RuleDescriptor item) public RuleDescriptor FindDescriptor(string name) { - return this[name]; + if (name != null && Contains(name)) + { + return this[name]; + } + + return null; } } } diff --git a/src/Libraries/SmartStore.Core/Rules/RuleExpression.cs b/src/Libraries/SmartStore.Core/Rules/RuleExpression.cs index f4d7aa551d..a6bcaf32f2 100644 --- a/src/Libraries/SmartStore.Core/Rules/RuleExpression.cs +++ b/src/Libraries/SmartStore.Core/Rules/RuleExpression.cs @@ -1,26 +1,44 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; +using SmartStore.Utilities; namespace SmartStore.Rules { public interface IRuleExpression { int Id { get; } + int RuleSetId { get; } RuleDescriptor Descriptor { get; } RuleOperator Operator { get; } object Value { get; } string RawValue { get; } + IDictionary Metadata { get; } } public class RuleExpression : IRuleExpression { + public RuleExpression() + { + Metadata = new Dictionary(); + } + public int Id { get; set; } + public int RuleSetId { get; set; } public RuleDescriptor Descriptor { get; set; } public RuleOperator Operator { get; set; } public object Value { get; set; } public string RawValue { get; set; } + public IDictionary Metadata { get; set; } + + public override int GetHashCode() + { + var combiner = HashCodeCombiner + .Start() + .Add(RuleSetId) + .Add(Id) + .Add(Operator) + .Add(RawValue); + + return combiner.CombinedHash; + } } } diff --git a/src/Libraries/SmartStore.Core/Rules/RuleExpressionGroup.cs b/src/Libraries/SmartStore.Core/Rules/RuleExpressionGroup.cs index 9ec260c8cb..06c7d0b244 100644 --- a/src/Libraries/SmartStore.Core/Rules/RuleExpressionGroup.cs +++ b/src/Libraries/SmartStore.Core/Rules/RuleExpressionGroup.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; +using SmartStore.Utilities; namespace SmartStore.Rules { @@ -31,15 +29,21 @@ public class RuleExpressionGroup : RuleExpression, IRuleExpressionGroup public bool IsSubGroup { get; set; } public IRuleProvider Provider { get; set; } - public IEnumerable Expressions - { - get => _expressions; - } + public IEnumerable Expressions => _expressions; public virtual void AddExpressions(params IRuleExpression[] expressions) { Guard.NotNull(expressions, nameof(expressions)); _expressions.AddRange(expressions); } + + public override int GetHashCode() + { + var combiner = HashCodeCombiner + .Start() + .Add(Expressions.Select(x => x.GetHashCode())); + + return combiner.CombinedHash; + } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Rules/RuleFactory.cs b/src/Libraries/SmartStore.Core/Rules/RuleFactory.cs index f8b4aeb45b..5b47ac1617 100644 --- a/src/Libraries/SmartStore.Core/Rules/RuleFactory.cs +++ b/src/Libraries/SmartStore.Core/Rules/RuleFactory.cs @@ -1,20 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Linq; using Autofac; using SmartStore.Rules.Domain; -using SmartStore.Collections; -using SmartStore.Core.Caching; -using SmartStore.Core.Data; namespace SmartStore.Rules { public interface IRuleFactory { - IRuleExpressionGroup CreateExpressionGroup(int ruleSetId, IRuleVisitor visitor); - IRuleExpressionGroup CreateExpressionGroup(RuleSetEntity ruleSet, IRuleVisitor visitor); + IRuleExpressionGroup CreateExpressionGroup(int ruleSetId, IRuleVisitor visitor, bool includeHidden = false); + IRuleExpressionGroup CreateExpressionGroup(RuleSetEntity ruleSet, IRuleVisitor visitor, bool includeHidden = false); } public partial class RuleFactory : IRuleFactory @@ -26,7 +19,7 @@ public RuleFactory(IRuleStorage storage) _storage = storage; } - public IRuleExpressionGroup CreateExpressionGroup(int ruleSetId, IRuleVisitor visitor) + public IRuleExpressionGroup CreateExpressionGroup(int ruleSetId, IRuleVisitor visitor, bool includeHidden = false) { if (ruleSetId <= 0) return null; @@ -40,14 +33,18 @@ public IRuleExpressionGroup CreateExpressionGroup(int ruleSetId, IRuleVisitor vi return null; } - return CreateExpressionGroup(ruleSet, visitor); + return CreateExpressionGroup(ruleSet, visitor, includeHidden); } - public virtual IRuleExpressionGroup CreateExpressionGroup(RuleSetEntity ruleSet, IRuleVisitor visitor) + public virtual IRuleExpressionGroup CreateExpressionGroup(RuleSetEntity ruleSet, IRuleVisitor visitor, bool includeHidden = false) { if (ruleSet.Scope != visitor.Scope) { - // TODO: ErrHandling (ruleSet is for a different scope) + throw new SmartException($"Differing rule scope {ruleSet.Scope}. Expected {visitor.Scope}."); + } + + if (!includeHidden && !ruleSet.IsActive) + { return null; } @@ -63,29 +60,29 @@ public virtual IRuleExpressionGroup CreateExpressionGroup(RuleSetEntity ruleSet, return group; } - private IRuleExpressionGroup CreateExpressionGroup(RuleSetEntity ruleSet, RuleEntity refRule, IRuleVisitor visitor) - { - if (ruleSet.Scope != visitor.Scope) - { - // TODO: ErrHandling (ruleSet is for a different scope) - return null; - } + //private IRuleExpressionGroup CreateExpressionGroup(RuleSetEntity ruleSet, RuleEntity refRule, IRuleVisitor visitor) + //{ + // if (ruleSet.Scope != visitor.Scope) + // { + // // TODO: ErrHandling (ruleSet is for a different scope) + // return null; + // } - var group = visitor.VisitRuleSet(ruleSet); - if (refRule != null) - { - group.RefRuleId = refRule.Id; - } + // var group = visitor.VisitRuleSet(ruleSet); + // if (refRule != null) + // { + // group.RefRuleId = refRule.Id; + // } - var expressions = ruleSet.Rules - .Select(x => CreateExpression(x, visitor)) - .Where(x => x != null) - .ToArray(); + // var expressions = ruleSet.Rules + // .Select(x => CreateExpression(x, visitor)) + // .Where(x => x != null) + // .ToArray(); - group.AddExpressions(expressions); + // group.AddExpressions(expressions); - return group; - } + // return group; + //} private IRuleExpression CreateExpression(RuleEntity ruleEntity, IRuleVisitor visitor) { @@ -94,9 +91,12 @@ private IRuleExpression CreateExpression(RuleEntity ruleEntity, IRuleVisitor vis return visitor.VisitRule(ruleEntity); } - // It's a group, do recursive call + // It's a group, do recursive call. var group = CreateExpressionGroup(ruleEntity.Value.Convert(), visitor); - group.RefRuleId = ruleEntity.Id; + if (group != null) + { + group.RefRuleId = ruleEntity.Id; + } return group; } diff --git a/src/Libraries/SmartStore.Core/Rules/RuleModule.cs b/src/Libraries/SmartStore.Core/Rules/RuleModule.cs index d60fd76a07..680e80118d 100644 --- a/src/Libraries/SmartStore.Core/Rules/RuleModule.cs +++ b/src/Libraries/SmartStore.Core/Rules/RuleModule.cs @@ -1,24 +1,19 @@ using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; using Autofac; -using Autofac.Core; -using Autofac.Core.Activators.Reflection; -using SmartStore.ComponentModel; -using SmartStore.Core.Data; namespace SmartStore.Rules { - public class RuleModule : Autofac.Module - { - protected override void Load(ContainerBuilder builder) - { + public class RuleModule : Autofac.Module + { + protected override void Load(ContainerBuilder builder) + { builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); - // Register provider resolver delegate + // Rendering. + builder.RegisterType().As().InstancePerRequest(); + + // Register provider resolver delegate. builder.Register>(c => { // TODO: register providers explicitly @@ -26,5 +21,5 @@ protected override void Load(ContainerBuilder builder) return key => cc.ResolveKeyed(key); }); } - } + } } diff --git a/src/Libraries/SmartStore.Core/Rules/RuleOperator.cs b/src/Libraries/SmartStore.Core/Rules/RuleOperator.cs index d391bc69e4..84846ea32c 100644 --- a/src/Libraries/SmartStore.Core/Rules/RuleOperator.cs +++ b/src/Libraries/SmartStore.Core/Rules/RuleOperator.cs @@ -1,14 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.Linq; using System.Linq.Expressions; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; -using SmartStore.Core.Utilities; using SmartStore.Rules.Filters; using SmartStore.Rules.Operators; @@ -34,6 +27,8 @@ public abstract class RuleOperator : IEquatable public readonly static RuleOperator IsNotEmpty = new IsNotEmptyOperator(); public readonly static RuleOperator In = new InOperator(); public readonly static RuleOperator NotIn = new NotInOperator(); + public readonly static RuleOperator AllIn = new AllInOperator(); + public readonly static RuleOperator NotAllIn = new NotAllInOperator(); //public readonly static string All = new RuleOperator(); // TODO protected RuleOperator(string op) @@ -46,11 +41,20 @@ protected RuleOperator(string op) public string Operator { get; set; } - public override string ToString() => Operator; + public override string ToString() + { + return Operator; + } - public static implicit operator string(RuleOperator obj) => obj.Operator; + public static implicit operator string(RuleOperator obj) + { + return obj.Operator; + } - public static implicit operator RuleOperator(string obj) => GetOperator(obj); + public static implicit operator RuleOperator(string obj) + { + return GetOperator(obj); + } public static RuleOperator GetOperator(string op) { @@ -62,7 +66,7 @@ public static RuleOperator GetOperator(string op) if (_map.TryGetValue(op, out var instance)) { return instance; - } + } throw new InvalidCastException("No rule operator has been registered for '{0}'.".FormatInvariant(op)); } @@ -96,8 +100,8 @@ public bool Match(object left, object right) public virtual bool Match(TLeft left, object right) { var body = GetExpression( - ExpressionHelper.CreateConstantExpression(left, typeof(TLeft)), - ExpressionHelper.CreateConstantExpression(right), + ExpressionHelper.CreateConstantExpression(left, typeof(TLeft)), + ExpressionHelper.CreateConstantExpression(right), true); var lambda = Expression.Lambda>(body); @@ -161,10 +165,11 @@ private bool TryConvertNullableValue(Expression left, ref Expression right) if (targetType == right.Type) return true; - var leftIsNullable = targetType.IsNullable(out var leftType); + var leftIsNullable = targetType.IsNullable(out _); var rightIsNullObj = ExpressionHelper.IsNullObjectConstantExpression(right); var rightIsList = false; - if (!rightIsNullObj) + + if (!rightIsNullObj && right.Type.IsGenericType) { rightIsList = right.Type.GetGenericTypeDefinition() == typeof(List<>); } @@ -189,7 +194,7 @@ private bool TryConvertNullableValue(Expression left, ref Expression right) if (!handled) { right = Expression.Constant(value, targetType); - } + } } catch { diff --git a/src/Libraries/SmartStore.Core/Rules/RuleScope.cs b/src/Libraries/SmartStore.Core/Rules/RuleScope.cs index 07d9b65b27..3cae6cbd27 100644 --- a/src/Libraries/SmartStore.Core/Rules/RuleScope.cs +++ b/src/Libraries/SmartStore.Core/Rules/RuleScope.cs @@ -1,15 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SmartStore.Rules +namespace SmartStore.Rules { public enum RuleScope { Cart = 0, - OrderItem = 1, + //OrderItem = 1, Customer = 2, Product = 3, } diff --git a/src/Libraries/SmartStore.Core/Rules/RuleStorage.cs b/src/Libraries/SmartStore.Core/Rules/RuleStorage.cs index cb2ceed5f9..8b8226c2bd 100644 --- a/src/Libraries/SmartStore.Core/Rules/RuleStorage.cs +++ b/src/Libraries/SmartStore.Core/Rules/RuleStorage.cs @@ -1,13 +1,11 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Data.Entity; -using System.Threading.Tasks; +using System.Linq; using SmartStore.Core; +using SmartStore.Core.Caching; using SmartStore.Core.Data; using SmartStore.Rules.Domain; -using SmartStore.Core.Caching; namespace SmartStore.Rules { @@ -26,11 +24,15 @@ IPagedList GetAllRuleSets( bool includeSubGroups = false, bool includeHidden = false); + bool ApplyRuleSetMappings(T entity, int[] selectedRuleSetIds) where T : BaseEntity, IRulesContainer; + void InsertRuleSet(RuleSetEntity ruleSet); void UpdateRuleSet(RuleSetEntity ruleSet); void DeleteRuleSet(RuleSetEntity ruleSet); RuleEntity GetRuleById(int id, bool forEdit); + IList GetRulesByIds(int[] ids, bool forEdit); + void InsertRule(RuleEntity rule); void UpdateRule(RuleEntity rule); void DeleteRule(RuleEntity rule); @@ -58,27 +60,26 @@ public RuleSetEntity GetCachedRuleSet(int id) if (id <= 0) return null; - return this.GetRuleSetById(id, false, true); - - //// TODO: uncomment later - //var cacheKey = RULESET_BY_ID_KEY.FormatInvariant(id); - //return _cache.Get(cacheKey, () => - //{ - // using (new DbContextScope(forceNoTracking: true, proxyCreation: false, lazyLoading: false)) - // { - // var ruleSet = this.GetRuleSetById(id, false, true); - - // if (ruleSet != null) - // { - // _rsRuleSets.Context.DetachEntity(ruleSet); - // // TODO: check if the above detach call already detached navigation prop instances - // // TODO: Sort rules (?) - // _rsRuleSets.Context.DetachEntities(ruleSet.Rules); - // } - - // return ruleSet; - // } - //}, TimeSpan.FromHours(8)); + //return this.GetRuleSetById(id, false, true); + + var cacheKey = RULESET_BY_ID_KEY.FormatInvariant(id); + return _cache.Get(cacheKey, () => + { + using (new DbContextScope(forceNoTracking: true, proxyCreation: false, lazyLoading: false)) + { + var ruleSet = this.GetRuleSetById(id, false, true); + + if (ruleSet != null) + { + _rsRuleSets.Context.DetachEntity(ruleSet, true); + //// TODO: check if the above detach call already detached navigation prop instances + //// TODO: Sort rules (?) + //_rsRuleSets.Context.DetachEntities(ruleSet.Rules); + } + + return ruleSet; + } + }, TimeSpan.FromHours(8)); } protected void InvalidateRuleSet(int id) @@ -154,6 +155,10 @@ public IPagedList GetAllRuleSets( query = query.Where(x => !x.IsSubGroup); } + query = query + .OrderByDescending(x => x.IsActive) + .ThenBy(x => x.Scope); + return new PagedList(query, pageIndex, pageSize); } @@ -166,7 +171,54 @@ public RuleEntity GetRuleById(int id, bool forEdit) ? _rsRules.Table : _rsRules.TableUntracked; - return table.FirstOrDefault(x => x.Id == id); + return table + .Include(x => x.RuleSet) + .FirstOrDefault(x => x.Id == id); + } + + public IList GetRulesByIds(int[] ids, bool forEdit) + { + if (ids == null || !ids.Any()) + { + return new List(); + } + + var table = forEdit + ? _rsRules.Table + : _rsRules.TableUntracked; + + var entities = table.Include(x => x.RuleSet) + .Where(x => ids.Contains(x.Id)) + .ToList(); + + return entities; + } + + public virtual bool ApplyRuleSetMappings(T entity, int[] selectedRuleSetIds) where T : BaseEntity, IRulesContainer + { + Guard.NotNull(entity, nameof(entity)); + + var updated = false; + var allRuleSets = GetAllRuleSets(true, false, includeHidden: true).ToDictionary(x => x.Id); + + foreach (var ruleSetId in allRuleSets.Keys) + { + if (selectedRuleSetIds?.Contains(ruleSetId) ?? false) + { + if (!entity.RuleSets.Any(x => x.Id == ruleSetId)) + { + entity.RuleSets.Add(allRuleSets[ruleSetId]); + updated = true; + } + } + else if (entity.RuleSets.Any(x => x.Id == ruleSetId)) + { + entity.RuleSets.Remove(allRuleSets[ruleSetId]); + updated = false; + } + } + + return updated; } #endregion diff --git a/src/Libraries/SmartStore.Core/Rules/RuleType.cs b/src/Libraries/SmartStore.Core/Rules/RuleType.cs index bd6f7c6fc4..cff01d15d1 100644 --- a/src/Libraries/SmartStore.Core/Rules/RuleType.cs +++ b/src/Libraries/SmartStore.Core/Rules/RuleType.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.Rules { @@ -36,9 +33,9 @@ private RuleType(string name, Type clrType) public string Name { get; set; } public Type ClrType { get; set; } - public IEnumerable GetValidOperators() + public IEnumerable GetValidOperators(bool isComparingSequences = false) { - bool isComparable = typeof(IComparable).IsAssignableFrom(ClrType); + var isComparable = typeof(IComparable).IsAssignableFrom(ClrType); var isNullable = ClrType.IsNullable(out var nonNullableType); if (isNullable) @@ -53,7 +50,7 @@ public IEnumerable GetValidOperators() yield return RuleOperator.IsNotEqualTo; } - if (nonNullableType == typeof(int) || nonNullableType == typeof(float) || nonNullableType == typeof(DateTime)) + if (nonNullableType == typeof(int) || nonNullableType == typeof(float) || nonNullableType == typeof(DateTime) || nonNullableType == typeof(decimal)) { yield return RuleOperator.GreaterThanOrEqualTo; yield return RuleOperator.GreaterThan; @@ -73,8 +70,22 @@ public IEnumerable GetValidOperators() if (nonNullableType == typeof(List) || nonNullableType == typeof(List) || nonNullableType == typeof(List)) { - yield return RuleOperator.In; - yield return RuleOperator.NotIn; + if (isComparingSequences) + { + yield return RuleOperator.In; + yield return RuleOperator.NotIn; + yield return RuleOperator.Contains; + yield return RuleOperator.NotContains; + yield return RuleOperator.AllIn; + yield return RuleOperator.NotAllIn; + yield return RuleOperator.IsEqualTo; + yield return RuleOperator.IsNotEqualTo; + } + else + { + yield return RuleOperator.In; + yield return RuleOperator.NotIn; + } } } } diff --git a/src/Libraries/SmartStore.Core/Rules/RuleValueSelectList.cs b/src/Libraries/SmartStore.Core/Rules/RuleValueSelectList.cs index 0e0d1ffd29..4b4b582489 100644 --- a/src/Libraries/SmartStore.Core/Rules/RuleValueSelectList.cs +++ b/src/Libraries/SmartStore.Core/Rules/RuleValueSelectList.cs @@ -1,20 +1,19 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.Rules { public abstract class RuleValueSelectList { public bool Multiple { get; set; } + public bool Tags { get; set; } } public class RuleValueSelectListOption { - public object Value { get; set; } + public string Value { get; set; } public string Text { get; set; } + public string Hint { get; set; } public RuleValueSelectListGroup Group { get; set; } } @@ -45,8 +44,11 @@ public RemoteRuleValueSelectList(string dataSource) Guard.NotEmpty(dataSource, nameof(dataSource)); DataSource = dataSource; - } + } - public string DataSource { get; set; } + /// + /// Name of the data source. + /// + public string DataSource { get; protected set; } } } diff --git a/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs b/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs index e0df140361..f1ce494345 100644 --- a/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs +++ b/src/Libraries/SmartStore.Core/Search/AcquireWriterContext.cs @@ -8,43 +8,43 @@ namespace SmartStore.Core.Search { - public enum AcquirementReason - { - Indexing, - Deleting - } + public enum AcquirementReason + { + Indexing, + Deleting + } - public class AcquireWriterContext - { - public AcquireWriterContext(AcquirementReason reason) - { - Reason = reason; - Languages = new List(); - Currencies = new List(); - StoreMappings = new Multimap(); - CustomerRoleIds = new int[0]; - CustomerRoleMappings = new Multimap(); - DeliveryTimes = new Dictionary(); - Manufacturers = new Dictionary(); - Categories = new Dictionary(); - Translations = new Dictionary(StringComparer.OrdinalIgnoreCase); - CustomProperties = new Dictionary(); - } + public class AcquireWriterContext + { + public AcquireWriterContext(AcquirementReason reason) + { + Reason = reason; + Languages = new List(); + Currencies = new List(); + StoreMappings = new Multimap(); + CustomerRoleIds = new int[0]; + CustomerRoleMappings = new Multimap(); + DeliveryTimes = new Dictionary(); + Manufacturers = new Dictionary(); + Categories = new Dictionary(); + Translations = new Dictionary(StringComparer.OrdinalIgnoreCase); + CustomProperties = new Dictionary(); + } - /// - /// Reason for writer acquirement - /// - public AcquirementReason Reason { get; private set; } + /// + /// Reason for writer acquirement + /// + public AcquirementReason Reason { get; private set; } - /// - /// All languages - /// - public IList Languages { get; set; } + /// + /// All languages + /// + public IList Languages { get; set; } - /// - /// Currency codes used for indexing - /// - public IList Currencies { get; set; } + /// + /// Currency codes used for indexing + /// + public IList Currencies { get; set; } /// /// All stores @@ -56,40 +56,40 @@ public AcquireWriterContext(AcquirementReason reason) /// public Multimap StoreMappings { get; set; } - /// - /// Array of all customer role identifiers - /// - public int[] CustomerRoleIds { get; set; } + /// + /// Array of all customer role identifiers + /// + public int[] CustomerRoleIds { get; set; } - /// - /// Map of product to customer role identifiers if the product is limited to certain customer roles - /// - public Multimap CustomerRoleMappings { get; set; } + /// + /// Map of product to customer role identifiers if the product is limited to certain customer roles + /// + public Multimap CustomerRoleMappings { get; set; } /// /// All manufacturers /// public Dictionary Manufacturers { get; set; } - /// - /// All categories - /// - public Dictionary Categories { get; set; } + /// + /// All categories + /// + public Dictionary Categories { get; set; } - /// - /// All delivery times - /// - public Dictionary DeliveryTimes { get; set; } + /// + /// All delivery times + /// + public Dictionary DeliveryTimes { get; set; } - /// - /// All translations for global scopes (like Category, Manufacturer etc.) - /// - public Dictionary Translations { 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 - /// - public Dictionary CustomProperties { get; set; } + /// + /// Use this dictionary for any custom data required along indexing + /// + public Dictionary CustomProperties { get; set; } public void Clear() { @@ -103,5 +103,5 @@ public void Clear() Translations.Clear(); CustomProperties.Clear(); } - } + } } diff --git a/src/Libraries/SmartStore.Core/Search/DefaultIndexManager.cs b/src/Libraries/SmartStore.Core/Search/DefaultIndexManager.cs index 0fe4f85550..941ab39578 100644 --- a/src/Libraries/SmartStore.Core/Search/DefaultIndexManager.cs +++ b/src/Libraries/SmartStore.Core/Search/DefaultIndexManager.cs @@ -5,22 +5,22 @@ namespace SmartStore.Core.Search { public class DefaultIndexManager : IIndexManager - { - private readonly IEnumerable> _providers; + { + private readonly IEnumerable> _providers; - public DefaultIndexManager(IEnumerable> providers) - { - _providers = providers; - } + public DefaultIndexManager(IEnumerable> providers) + { + _providers = providers; + } - public bool HasAnyProvider(string scope, bool activeOnly = true) - { + public bool HasAnyProvider(string scope, bool activeOnly = true) + { return _providers.Any(x => !activeOnly || x.Value.IsActive(scope)); - } + } - public IIndexProvider GetIndexProvider(string scope, bool activeOnly = true) - { - return _providers.FirstOrDefault(x => !activeOnly || x.Value.IsActive(scope))?.Value; - } - } + public IIndexProvider GetIndexProvider(string scope, bool activeOnly = true) + { + return _providers.FirstOrDefault(x => !activeOnly || x.Value.IsActive(scope))?.Value; + } + } } diff --git a/src/Libraries/SmartStore.Core/Search/Events/IndexSegmentProcessedEvent.cs b/src/Libraries/SmartStore.Core/Search/Events/IndexSegmentProcessedEvent.cs index b235921138..32f6d85bd7 100644 --- a/src/Libraries/SmartStore.Core/Search/Events/IndexSegmentProcessedEvent.cs +++ b/src/Libraries/SmartStore.Core/Search/Events/IndexSegmentProcessedEvent.cs @@ -1,43 +1,42 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace SmartStore.Core.Search { - public class IndexSegmentProcessedEvent - { - public IndexSegmentProcessedEvent(string scope, IEnumerable operations, bool isRebuild) - { - Guard.NotEmpty(scope, nameof(scope)); - Guard.NotNull(operations, nameof(operations)); + public class IndexSegmentProcessedEvent + { + public IndexSegmentProcessedEvent(string scope, IEnumerable operations, bool isRebuild) + { + Guard.NotEmpty(scope, nameof(scope)); + Guard.NotNull(operations, nameof(operations)); - Scope = scope; - IsRebuild = isRebuild; - Operations = operations; - Metadata = new Dictionary(); - } + Scope = scope; + IsRebuild = isRebuild; + Operations = operations; + Metadata = new Dictionary(); + } - public string Scope - { - get; - private set; - } + public string Scope + { + get; + private set; + } - public bool IsRebuild - { - get; - private set; - } + public bool IsRebuild + { + get; + private set; + } - public IEnumerable Operations - { - get; - private set; - } + public IEnumerable Operations + { + get; + private set; + } - public IDictionary Metadata - { - get; - private set; - } - } + public IDictionary Metadata + { + get; + private set; + } + } } diff --git a/src/Libraries/SmartStore.Core/Search/Events/IndexingCompletedEvent.cs b/src/Libraries/SmartStore.Core/Search/Events/IndexingCompletedEvent.cs index b1304aae56..b15bf32629 100644 --- a/src/Libraries/SmartStore.Core/Search/Events/IndexingCompletedEvent.cs +++ b/src/Libraries/SmartStore.Core/Search/Events/IndexingCompletedEvent.cs @@ -1,27 +1,25 @@ -using System; - -namespace SmartStore.Core.Search +namespace SmartStore.Core.Search { - public class IndexingCompletedEvent - { - public IndexingCompletedEvent(IndexInfo indexInfo, bool wasRebuilt) - { - Guard.NotNull(indexInfo, nameof(indexInfo)); + public class IndexingCompletedEvent + { + public IndexingCompletedEvent(IndexInfo indexInfo, bool wasRebuilt) + { + Guard.NotNull(indexInfo, nameof(indexInfo)); - IndexInfo = indexInfo; - WasRebuilt = wasRebuilt; - } + IndexInfo = indexInfo; + WasRebuilt = wasRebuilt; + } - public IndexInfo IndexInfo - { - get; - private set; - } + public IndexInfo IndexInfo + { + get; + private set; + } - public bool WasRebuilt - { - get; - private set; - } - } + public bool WasRebuilt + { + get; + private set; + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Search/Facets/Facet.cs b/src/Libraries/SmartStore.Core/Search/Facets/Facet.cs index 84a88b2109..1fd5f17bcc 100644 --- a/src/Libraries/SmartStore.Core/Search/Facets/Facet.cs +++ b/src/Libraries/SmartStore.Core/Search/Facets/Facet.cs @@ -3,69 +3,69 @@ namespace SmartStore.Core.Search.Facets { - [DebuggerDisplay("Key: {Key}, IsChoice: {IsChoice}, HitCount: {HitCount}, Value: {Value}")] - public class Facet - { - public Facet(FacetValue value) - : this(value.ToString(), value) - { - } + [DebuggerDisplay("Key: {Key}, IsChoice: {IsChoice}, HitCount: {HitCount}, Value: {Value}")] + public class Facet + { + public Facet(FacetValue value) + : this(value.ToString(), value) + { + } - public Facet(string key, FacetValue value) - { - Guard.NotEmpty(key, nameof(key)); - Guard.NotNull(value, nameof(value)); + public Facet(string key, FacetValue value) + { + Guard.NotEmpty(key, nameof(key)); + Guard.NotNull(value, nameof(value)); - Key = key; - Value = value; - Children = new List(); - IsChoice = true; - Published = true; - } + Key = key; + Value = value; + Children = new List(); + IsChoice = true; + Published = true; + } - public string Key - { - get; - private set; - } + public string Key + { + get; + private set; + } - public FacetValue Value - { - get; - private set; - } + public FacetValue Value + { + get; + private set; + } - /// - /// Gets or sets whether the facet can be selected - /// - public bool IsChoice - { - get; - set; - } + /// + /// Gets or sets whether the facet can be selected + /// + public bool IsChoice + { + get; + set; + } - public bool Published - { - get; - set; - } + public bool Published + { + get; + set; + } - public long HitCount - { - get; - set; - } + public long HitCount + { + get; + set; + } - public FacetGroup FacetGroup - { - get; - internal set; - } + public FacetGroup FacetGroup + { + get; + internal set; + } - public IList Children - { - get; - set; - } - } + public IList Children + { + get; + set; + } + } } diff --git a/src/Libraries/SmartStore.Core/Search/Facets/FacetDescriptor.cs b/src/Libraries/SmartStore.Core/Search/Facets/FacetDescriptor.cs index d0e8b5b8e7..4292617e5f 100644 --- a/src/Libraries/SmartStore.Core/Search/Facets/FacetDescriptor.cs +++ b/src/Libraries/SmartStore.Core/Search/Facets/FacetDescriptor.cs @@ -5,131 +5,125 @@ namespace SmartStore.Core.Search.Facets { - public enum FacetSorting - { - HitsDesc, - ValueAsc, - LabelAsc, - DisplayOrder - } - - /// - /// A filter and its selection to be applied, e.g. Color=Red. - /// - [Serializable] - public class FacetDescriptor - { - private readonly List _values; - - public FacetDescriptor(string key) - { - Guard.NotEmpty(key, nameof(key)); - - _values = new List(); - Key = key; - } - - /// - /// Gets the key / field name. - /// - public string Key - { - get; - private set; - } - - /// - /// Gets or sets the label. - /// - public string Label - { - get; - set; - } - - /// - /// Gets the values for this facet. - /// - public ICollection Values - { - get - { - return _values; - } - } - - /// - /// Adds a facet value. - /// - /// Facet value - public FacetDescriptor AddValue(params FacetValue[] values) - { - _values.AddRange(values); - return this; - } - - /// - /// Gets or sets the boolean value operator. - /// - //public ValueOperator Operator - //{ - // get; - // set; - //} - - /// - /// Gets or sets whether selection of multiple values is allowed. - /// - public bool IsMultiSelect - { - get; - set; - } - - /// - /// Gets or sets the minimum number of hits a choice would need to have to be returned. - /// - public int MinHitCount - { - get; - set; - } - - /// - /// Gets or sets the maximum number of choices to return. Default = 0 which means all. - /// - public int MaxChoicesCount - { - get; - set; - } - - /// - /// Gets or sets the result choices sort order. - /// - public FacetSorting OrderBy - { - get; - set; - } - - /// - /// Gets or sets the display order. - /// - public int DisplayOrder - { - get; - set; - } - - public override string ToString() - { - var sb = new StringBuilder(); - - sb.Append("FieldName: ").Append(Key).Append(" "); - sb.Append("Values: " + string.Join(",", _values.Select(x => x.Value.ToString()))).Append(" "); - - return sb.ToString(); - } - } + public enum FacetSorting + { + HitsDesc, + ValueAsc, + LabelAsc, + DisplayOrder + } + + /// + /// A filter and its selection to be applied, e.g. Color=Red. + /// + [Serializable] + public class FacetDescriptor + { + private readonly List _values; + + public FacetDescriptor(string key) + { + Guard.NotEmpty(key, nameof(key)); + + _values = new List(); + Key = key; + } + + /// + /// Gets the key / field name. + /// + public string Key + { + get; + private set; + } + + /// + /// Gets or sets the label. + /// + public string Label + { + get; + set; + } + + /// + /// Gets the values for this facet. + /// + public ICollection Values => _values; + + /// + /// Adds a facet value. + /// + /// Facet value + public FacetDescriptor AddValue(params FacetValue[] values) + { + _values.AddRange(values); + return this; + } + + /// + /// Gets or sets the boolean value operator. + /// + //public ValueOperator Operator + //{ + // get; + // set; + //} + + /// + /// Gets or sets whether selection of multiple values is allowed. + /// + public bool IsMultiSelect + { + get; + set; + } + + /// + /// Gets or sets the minimum number of hits a choice would need to have to be returned. + /// + public int MinHitCount + { + get; + set; + } + + /// + /// Gets or sets the maximum number of choices to return. Default = 0 which means all. + /// + public int MaxChoicesCount + { + get; + set; + } + + /// + /// Gets or sets the result choices sort order. + /// + public FacetSorting OrderBy + { + get; + set; + } + + /// + /// Gets or sets the display order. + /// + public int DisplayOrder + { + get; + set; + } + + public override string ToString() + { + var sb = new StringBuilder(); + + sb.Append("FieldName: ").Append(Key).Append(" "); + sb.Append("Values: " + string.Join(",", _values.Select(x => x.Value.ToString()))).Append(" "); + + return sb.ToString(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Search/Facets/FacetEnums.cs b/src/Libraries/SmartStore.Core/Search/Facets/FacetEnums.cs index 2ca081d194..5e6644f6f5 100644 --- a/src/Libraries/SmartStore.Core/Search/Facets/FacetEnums.cs +++ b/src/Libraries/SmartStore.Core/Search/Facets/FacetEnums.cs @@ -1,20 +1,20 @@ namespace SmartStore.Core.Search.Facets { - public enum FacetTemplateHint - { - /// - /// Render facets as checkboxes - /// - Checkboxes = 0, + public enum FacetTemplateHint + { + /// + /// Render facets as checkboxes + /// + Checkboxes = 0, - /// - /// Custom facet rendering like color or picture boxes - /// - Custom, + /// + /// Custom facet rendering like color or picture boxes + /// + Custom, - /// - /// Render facets as a numeric range filter - /// - NumericRange - } + /// + /// Render facets as a numeric range filter + /// + NumericRange + } } diff --git a/src/Libraries/SmartStore.Core/Search/Facets/FacetExtensions.cs b/src/Libraries/SmartStore.Core/Search/Facets/FacetExtensions.cs index 3951bb1a7e..e4c4333b86 100644 --- a/src/Libraries/SmartStore.Core/Search/Facets/FacetExtensions.cs +++ b/src/Libraries/SmartStore.Core/Search/Facets/FacetExtensions.cs @@ -3,83 +3,94 @@ namespace SmartStore.Core.Search.Facets { - public static class FacetExtensions - { - /// - /// Orders a facet sequence - /// - /// Facets - /// Facet descriptor - /// Ordered facets - public static IOrderedEnumerable OrderBy(this IEnumerable source, FacetDescriptor descriptor) - { - Guard.NotNull(descriptor, nameof(descriptor)); + public static class FacetExtensions + { + /// + /// Orders a facet sequence + /// + /// Facets + /// Facet descriptor + /// Ordered facets + public static IOrderedEnumerable OrderBy(this IEnumerable source, FacetDescriptor descriptor) + { + Guard.NotNull(descriptor, nameof(descriptor)); - return source.OrderBy(descriptor.OrderBy, descriptor.IsMultiSelect); - } + return source.OrderBy(descriptor.OrderBy, descriptor.IsMultiSelect); + } - /// - /// Orders a facet sequence - /// - /// Facets - /// Type of sorting - /// Whether to display selected facets first - /// Ordered facets - public static IOrderedEnumerable OrderBy(this IEnumerable source, FacetSorting sorting, bool selectedFirst = true) - { - Guard.NotNull(source, nameof(source)); + /// + /// Orders a facet sequence + /// + /// Facets + /// Type of sorting + /// Whether to display selected facets first + /// Ordered facets + public static IOrderedEnumerable OrderBy(this IEnumerable source, FacetSorting sorting, bool selectedFirst = true) + { + Guard.NotNull(source, nameof(source)); - switch (sorting) - { - case FacetSorting.ValueAsc: - if (selectedFirst) - return source.OrderByDescending(x => x.Value.IsSelected).ThenBy(x => x.Value.Value); - else - return source.OrderBy(x => x.Value.Value); + switch (sorting) + { + case FacetSorting.ValueAsc: + if (selectedFirst) + return source + .OrderByDescending(x => x.Value.IsSelected) + .ThenBy(x => x.Value.Value); + else + return source.OrderBy(x => x.Value.Value); - case FacetSorting.LabelAsc: - if (selectedFirst) - return source.OrderByDescending(x => x.Value.IsSelected).ThenBy(x => x.Value.Label); - else - return source.OrderBy(x => x.Value.Label); + case FacetSorting.LabelAsc: + if (selectedFirst) + return source + .OrderByDescending(x => x.Value.IsSelected) + .ThenBy(x => x.Value.Label); + else + return source.OrderBy(x => x.Value.Label); - case FacetSorting.DisplayOrder: - if (selectedFirst) - return source.OrderByDescending(x => x.Value.IsSelected).ThenBy(x => x.Value.DisplayOrder); - else - return source.OrderBy(x => x.Value.DisplayOrder); + case FacetSorting.DisplayOrder: + if (selectedFirst) + return source + .OrderByDescending(x => x.Value.IsSelected) + .ThenBy(x => x.Value.DisplayOrder); + else + return source.OrderBy(x => x.Value.DisplayOrder); - default: - if (selectedFirst) - return source.OrderByDescending(x => x.Value.IsSelected).ThenByDescending(x => x.HitCount); - else - return source.OrderByDescending(x => x.HitCount); - } - } + default: + if (selectedFirst) + return source + .OrderByDescending(x => x.Value.IsSelected) + .ThenByDescending(x => x.HitCount) + .ThenBy(x => x.Value.Label); + else + return source + .OrderByDescending(x => x.HitCount) + .ThenBy(x => x.Value.Label); + } + } - /// - /// Removes a facet - /// - /// List of facets - /// Facet value - /// Whether to compare the upper value - public static void RemoveFacet(this IList facets, object value, bool upperValue) - { - Facet facet = null; + /// + /// Removes a facet + /// + /// List of facets + /// Facet value + /// Whether to compare the upper value + public static void RemoveFacet(this IList facets, object value, bool upperValue) + { + Facet facet = null; - if (upperValue) - { - facet = facets.FirstOrDefault(x => x.Value.UpperValue != null && x.Value.UpperValue.Equals(value)); - } - else - { - facet = facets.FirstOrDefault(x => x.Value.Value != null && x.Value.Value.Equals(value)); - } + if (upperValue) + { + facet = facets.FirstOrDefault(x => x.Value.UpperValue != null && x.Value.UpperValue.Equals(value)); + } + else + { + facet = facets.FirstOrDefault(x => x.Value.Value != null && x.Value.Value.Equals(value)); + } - if (facet != null) - { - facets.Remove(facet); - } - } - } + if (facet != null) + { + facets.Remove(facet); + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Search/Facets/FacetGroup.cs b/src/Libraries/SmartStore.Core/Search/Facets/FacetGroup.cs index e6d9ffc48e..35b2f1b56d 100644 --- a/src/Libraries/SmartStore.Core/Search/Facets/FacetGroup.cs +++ b/src/Libraries/SmartStore.Core/Search/Facets/FacetGroup.cs @@ -5,82 +5,82 @@ namespace SmartStore.Core.Search.Facets { - public enum FacetGroupKind - { - Unknown = -1, - Category, - Brand, - Price, - Rating, - DeliveryTime, - Availability, - NewArrivals, - Attribute, - Variant, + public enum FacetGroupKind + { + Unknown = -1, + Category, + Brand, + Price, + Rating, + DeliveryTime, + Availability, + NewArrivals, + Attribute, + Variant, Forum, Customer, Date - } + } - [DebuggerDisplay("Key: {Key}, Label: {Label}, Kind: {Kind}")] - public class FacetGroup - { - private readonly Dictionary _facets; - private FacetGroupKind? _kind; + [DebuggerDisplay("Key: {Key}, Label: {Label}, Kind: {Kind}")] + public class FacetGroup + { + private readonly Dictionary _facets; + private FacetGroupKind? _kind; - public FacetGroup() - : this (string.Empty, string.Empty, string.Empty, false, false, 0, Enumerable.Empty()) - { - } + public FacetGroup() + : this(string.Empty, string.Empty, string.Empty, false, false, 0, Enumerable.Empty()) + { + } - public FacetGroup( + public FacetGroup( string scope, - string key, - string label, - bool isMultiSelect, - bool hasChildren, - int displayOrder, - IEnumerable facets) - { + string key, + string label, + bool isMultiSelect, + bool hasChildren, + int displayOrder, + IEnumerable facets) + { Guard.NotNull(scope, nameof(scope)); Guard.NotNull(key, nameof(key)); - Guard.NotNull(facets, nameof(facets)); + Guard.NotNull(facets, nameof(facets)); Scope = scope; - Key = key; - Label = label; - IsMultiSelect = isMultiSelect; - DisplayOrder = displayOrder; + Key = key; + Label = label; + IsMultiSelect = isMultiSelect; + DisplayOrder = displayOrder; IsScrollable = true; - _facets = new Dictionary(StringComparer.OrdinalIgnoreCase); - - facets.Each(x => - { - x.FacetGroup = this; - x.Children.Each(y => y.FacetGroup = this); - - try - { - _facets.Add(x.Key, x); - } - catch (Exception ex) - { - ex.Dump(); - } - }); - } - - public static FacetGroupKind GetKindByKey(string scope, string key) - { - if (key.StartsWith("attrid")) - { - return FacetGroupKind.Attribute; - } - else if (key.StartsWith("variantid")) - { - return FacetGroupKind.Variant; - } + _facets = new Dictionary(StringComparer.OrdinalIgnoreCase); + + facets.Each(x => + { + x.FacetGroup = this; + x.Children.Each(y => y.FacetGroup = this); + + try + { + _facets.Add(x.Key, x); + } + catch (Exception ex) + { + ex.Dump(); + } + }); + } + + public static FacetGroupKind GetKindByKey(string scope, string key) + { + if (key.StartsWith("attrid")) + { + return FacetGroupKind.Attribute; + } + else if (key.StartsWith("variantid")) + { + return FacetGroupKind.Variant; + } switch (key) { @@ -105,7 +105,7 @@ public static FacetGroupKind GetKindByKey(string scope, string key) return FacetGroupKind.Customer; default: return FacetGroupKind.Unknown; - } + } } public string Scope @@ -115,34 +115,34 @@ public string Scope } public string Key - { - get; - private set; - } - - public string Label - { - get; - private set; - } - - public bool IsMultiSelect - { - get; - private set; - } + { + get; + private set; + } + + public string Label + { + get; + private set; + } + + public bool IsMultiSelect + { + get; + private set; + } public bool HasChildren - { - get; - private set; - } + { + get; + private set; + } - public int DisplayOrder - { - get; - private set; - } + public int DisplayOrder + { + get; + private set; + } public bool IsScrollable { @@ -150,49 +150,43 @@ public bool IsScrollable set; } - public IEnumerable Facets - { - get - { - return _facets.Values; - } - } - - public IEnumerable SelectedFacets - { - get - { - var parents = _facets.Values.Where(x => x.Value.IsSelected); - var children = _facets.Values.SelectMany(x => x.Children).Where(x => x.Value.IsSelected); - - return parents.Concat(children); - } - } - - public Facet GetFacet(string key) - { - Guard.NotEmpty(key, nameof(key)); - - return _facets.Get(key); - } - - public FacetGroupKind Kind - { - get - { - if (_kind == null) - { - _kind = GetKindByKey(Scope, Key); - } - - return _kind.Value; - } - } - - public FacetTemplateHint TemplateHint - { - get; - set; - } - } + public IEnumerable Facets => _facets.Values; + + public IEnumerable SelectedFacets + { + get + { + var parents = _facets.Values.Where(x => x.Value.IsSelected); + var children = _facets.Values.SelectMany(x => x.Children).Where(x => x.Value.IsSelected); + + return parents.Concat(children); + } + } + + public Facet GetFacet(string key) + { + Guard.NotEmpty(key, nameof(key)); + + return _facets.Get(key); + } + + public FacetGroupKind Kind + { + get + { + if (_kind == null) + { + _kind = GetKindByKey(Scope, Key); + } + + return _kind.Value; + } + } + + public FacetTemplateHint TemplateHint + { + get; + set; + } + } } diff --git a/src/Libraries/SmartStore.Core/Search/Facets/FacetValue.cs b/src/Libraries/SmartStore.Core/Search/Facets/FacetValue.cs index 80e54b44af..b3d68a6c20 100644 --- a/src/Libraries/SmartStore.Core/Search/Facets/FacetValue.cs +++ b/src/Libraries/SmartStore.Core/Search/Facets/FacetValue.cs @@ -4,156 +4,150 @@ namespace SmartStore.Core.Search.Facets { - [Serializable] - public class FacetValue : IEquatable, ICloneable - { - public FacetValue() - { - } - - public FacetValue(object value, IndexTypeCode typeCode) - { - Value = value; - TypeCode = typeCode; - IsRange = false; - } - - public FacetValue(object value, object upperValue, IndexTypeCode typeCode, bool includesLower, bool includesUpper) - { - Value = value; - UpperValue = upperValue; - TypeCode = typeCode; - IncludesLower = includesLower; - IncludesUpper = includesUpper; - IsRange = true; - } - - public object Value - { - get; - set; - } - - public object UpperValue - { - get; - set; - } - - public IndexTypeCode TypeCode - { - get; - set; - } - - public bool IncludesLower - { - get; - set; - } - - public bool IncludesUpper - { - get; - set; - } - - public bool IsRange - { - get; - set; - } - - public bool IsSelected - { - get; - set; - } - - public bool IsEmpty - { - get - { - return TypeCode == IndexTypeCode.Empty && Value == null; - } - } - - #region Metadata - - public string Label { get; set; } - - public int ParentId { get; set; } - - public int DisplayOrder { get; set; } - - public FacetSorting? Sorting { get; set; } - - public string PictureUrl { get; set; } - - public string Color { get; set; } - - #endregion - - public override int GetHashCode() - { - if (Value != null && UpperValue != null) - { - var combiner = HashCodeCombiner - .Start() - .Add(Value) - .Add(UpperValue); - - return combiner.CombinedHash; - } - else if (UpperValue != null) - { - return UpperValue.GetHashCode(); - } - else if (Value != null) - { - return Value.GetHashCode(); - } - - return 0; - } - - public bool Equals(FacetValue other) - { - if (other == null || other.TypeCode != TypeCode || other.IsRange != IsRange) - { - return false; - } - - if (other.IsRange) - { - if (other.IncludesLower != IncludesLower || other.IncludesUpper != IncludesUpper) - { - return false; - } - - if (other.Value == null && Value == null && other.UpperValue == null && UpperValue == null) - { - return true; - } - - if (other.UpperValue != null && !other.UpperValue.Equals(UpperValue)) - { - return false; - } - } - - if (other.Value == null && Value == null) - { - return true; - } - - return other.Value != null && other.Value.Equals(Value); - } - - public override bool Equals(object obj) - { - return this.Equals(obj as FacetValue); - } + [Serializable] + public class FacetValue : IEquatable, ICloneable + { + public FacetValue() + { + } + + public FacetValue(object value, IndexTypeCode typeCode) + { + Value = value; + TypeCode = typeCode; + IsRange = false; + } + + public FacetValue(object value, object upperValue, IndexTypeCode typeCode, bool includesLower, bool includesUpper) + { + Value = value; + UpperValue = upperValue; + TypeCode = typeCode; + IncludesLower = includesLower; + IncludesUpper = includesUpper; + IsRange = true; + } + + public object Value + { + get; + set; + } + + public object UpperValue + { + get; + set; + } + + public IndexTypeCode TypeCode + { + get; + set; + } + + public bool IncludesLower + { + get; + set; + } + + public bool IncludesUpper + { + get; + set; + } + + public bool IsRange + { + get; + set; + } + + public bool IsSelected + { + get; + set; + } + + public bool IsEmpty => TypeCode == IndexTypeCode.Empty && Value == null; + + #region Metadata + + public string Label { get; set; } + + public int ParentId { get; set; } + + public int DisplayOrder { get; set; } + + public FacetSorting? Sorting { get; set; } + + public string PictureUrl { get; set; } + + public string Color { get; set; } + + #endregion + + public override int GetHashCode() + { + if (Value != null && UpperValue != null) + { + var combiner = HashCodeCombiner + .Start() + .Add(Value) + .Add(UpperValue); + + return combiner.CombinedHash; + } + else if (UpperValue != null) + { + return UpperValue.GetHashCode(); + } + else if (Value != null) + { + return Value.GetHashCode(); + } + + return 0; + } + + public bool Equals(FacetValue other) + { + if (other == null || other.TypeCode != TypeCode || other.IsRange != IsRange) + { + return false; + } + + if (other.IsRange) + { + if (other.IncludesLower != IncludesLower || other.IncludesUpper != IncludesUpper) + { + return false; + } + + if (other.Value == null && Value == null && other.UpperValue == null && UpperValue == null) + { + return true; + } + + if (other.UpperValue != null && !other.UpperValue.Equals(UpperValue)) + { + return false; + } + } + + if (other.Value == null && Value == null) + { + return true; + } + + return other.Value != null && other.Value.Equals(Value); + } + + public override bool Equals(object obj) + { + return this.Equals(obj as FacetValue); + } protected virtual string ConvertToString(object value) { @@ -179,39 +173,39 @@ protected virtual string ConvertToString(object value) return string.Empty; } - public override string ToString() - { - var result = string.Empty; + public override string ToString() + { + var result = string.Empty; var valueStr = ConvertToString(Value); - if (IsRange) - { + if (IsRange) + { var upperValueStr = ConvertToString(UpperValue); - if (upperValueStr.HasValue()) - { - result = string.Concat(valueStr, "~", upperValueStr); - } - else - { - result = valueStr; - } - } - else - { - result = valueStr; - } - - return result; - } - - public FacetValue Clone() - { - return (FacetValue)this.MemberwiseClone(); - } - - object ICloneable.Clone() - { - return this.MemberwiseClone(); - } - } + if (upperValueStr.HasValue()) + { + result = string.Concat(valueStr, "~", upperValueStr); + } + else + { + result = valueStr; + } + } + else + { + result = valueStr; + } + + return result; + } + + public FacetValue Clone() + { + return (FacetValue)this.MemberwiseClone(); + } + + object ICloneable.Clone() + { + return this.MemberwiseClone(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Search/Filter/CombinedSearchFilter.cs b/src/Libraries/SmartStore.Core/Search/Filter/CombinedSearchFilter.cs index 46f00c7d3c..e984b56574 100644 --- a/src/Libraries/SmartStore.Core/Search/Filter/CombinedSearchFilter.cs +++ b/src/Libraries/SmartStore.Core/Search/Filter/CombinedSearchFilter.cs @@ -3,51 +3,45 @@ namespace SmartStore.Core.Search { - public class CombinedSearchFilter : SearchFilterBase, ICombinedSearchFilter - { - private readonly IList _filters; + public class CombinedSearchFilter : SearchFilterBase, ICombinedSearchFilter + { + private readonly IList _filters; - public CombinedSearchFilter() - { - _filters = new List(); - } + public CombinedSearchFilter() + { + _filters = new List(); + } - public CombinedSearchFilter(IEnumerable filters) - { - Guard.NotNull(filters, nameof(filters)); + public CombinedSearchFilter(IEnumerable filters) + { + Guard.NotNull(filters, nameof(filters)); - _filters = new List(filters); - } + _filters = new List(filters); + } - public ICollection Filters - { - get - { - return _filters; - } - } + public ICollection Filters => _filters; - #region Fluent builder + #region Fluent builder - public CombinedSearchFilter Add(ISearchFilter filter) - { - Guard.NotNull(filter, nameof(filter)); + public CombinedSearchFilter Add(ISearchFilter filter) + { + Guard.NotNull(filter, nameof(filter)); - _filters.Add(filter); + _filters.Add(filter); - return this; - } + return this; + } - #endregion + #endregion - public override string ToString() - { - if (_filters.Any()) - { - return string.Concat("(", string.Join(", ", _filters.Select(x => x.ToString())), ")"); - } + public override string ToString() + { + if (_filters.Any()) + { + return string.Concat("(", string.Join(", ", _filters.Select(x => x.ToString())), ")"); + } - return ""; - } - } + return ""; + } + } } diff --git a/src/Libraries/SmartStore.Core/Search/Filter/ISearchFilter.cs b/src/Libraries/SmartStore.Core/Search/Filter/ISearchFilter.cs index d0adf644f1..ca96bc8536 100644 --- a/src/Libraries/SmartStore.Core/Search/Filter/ISearchFilter.cs +++ b/src/Libraries/SmartStore.Core/Search/Filter/ISearchFilter.cs @@ -1,33 +1,32 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace SmartStore.Core.Search { - public interface ISearchFilter - { - SearchFilterOccurence Occurence { get; } - float Boost { get; } - } + public interface ISearchFilter + { + SearchFilterOccurence Occurence { get; } + float Boost { get; } + } - public interface ICombinedSearchFilter : ISearchFilter - { - ICollection Filters { get; } - } + public interface ICombinedSearchFilter : ISearchFilter + { + ICollection Filters { get; } + } - public interface IAttributeSearchFilter : ISearchFilter - { - string FieldName { get; } - IndexTypeCode TypeCode { get; } - object Term { get; } - bool IsExactMatch { get; } - bool IsNotAnalyzed { get; } - int ParentId { get; } - } + public interface IAttributeSearchFilter : ISearchFilter + { + string FieldName { get; } + IndexTypeCode TypeCode { get; } + object Term { get; } + bool IsExactMatch { get; } + bool IsNotAnalyzed { get; } + int ParentId { get; } + } - public interface IRangeSearchFilter : IAttributeSearchFilter - { - object UpperTerm { get; } - bool IncludesLower { get; } - bool IncludesUpper { get; } - } + public interface IRangeSearchFilter : IAttributeSearchFilter + { + object UpperTerm { get; } + bool IncludesLower { get; } + bool IncludesUpper { get; } + } } diff --git a/src/Libraries/SmartStore.Core/Search/Filter/RangeSearchFilter.cs b/src/Libraries/SmartStore.Core/Search/Filter/RangeSearchFilter.cs index fda82e8457..78b4615ec5 100644 --- a/src/Libraries/SmartStore.Core/Search/Filter/RangeSearchFilter.cs +++ b/src/Libraries/SmartStore.Core/Search/Filter/RangeSearchFilter.cs @@ -1,31 +1,31 @@ namespace SmartStore.Core.Search { - public class RangeSearchFilter : SearchFilter, IRangeSearchFilter - { - public object UpperTerm - { - get; - protected internal set; - } + public class RangeSearchFilter : SearchFilter, IRangeSearchFilter + { + public object UpperTerm + { + get; + protected internal set; + } - public bool IncludesLower - { - get; - protected internal set; - } + public bool IncludesLower + { + get; + protected internal set; + } - public bool IncludesUpper - { - get; - protected internal set; - } + public bool IncludesUpper + { + get; + protected internal set; + } - public override string ToString() - { - return "{0}: {1} - {2}".FormatInvariant( - FieldName, - Term != null ? Term.ToString() : "*", - UpperTerm != null ? UpperTerm.ToString() : "*"); - } - } + public override string ToString() + { + return "{0}: {1} - {2}".FormatInvariant( + FieldName, + Term != null ? Term.ToString() : "*", + UpperTerm != null ? UpperTerm.ToString() : "*"); + } + } } diff --git a/src/Libraries/SmartStore.Core/Search/Filter/SearchFilter.cs b/src/Libraries/SmartStore.Core/Search/Filter/SearchFilter.cs index 332b617af3..aa6ddba8a6 100644 --- a/src/Libraries/SmartStore.Core/Search/Filter/SearchFilter.cs +++ b/src/Libraries/SmartStore.Core/Search/Filter/SearchFilter.cs @@ -2,210 +2,211 @@ namespace SmartStore.Core.Search { - public class SearchFilter : SearchFilterBase, IAttributeSearchFilter - { - protected SearchFilter() - : base() - { - } - - public string FieldName - { - get; - protected set; - } - - public IndexTypeCode TypeCode - { - get; - protected set; - } - - public object Term - { - get; - protected set; - } - - public bool IsExactMatch - { - get; - protected set; - } - - public bool IsNotAnalyzed - { - get; - protected set; - } - - public int ParentId - { - get; - protected set; - } - - #region Fluent builder - - /// - /// Mark a clause as a mandatory match. By default all clauses are optional. - /// - public SearchFilter Mandatory() - { - Occurence = SearchFilterOccurence.Must; - return this; - } - - /// - /// Mark a clause as a forbidden match (MustNot). - /// - public SearchFilter Forbidden() - { - Occurence = SearchFilterOccurence.MustNot; - return this; - } - - /// - /// Specifies whether the clause should be matched exactly, like 'app' won't match 'apple' (applied on string clauses only). - /// - public SearchFilter ExactMatch() - { - IsExactMatch = true; - return this; - } - - /// - /// Specifies that the searched value will not be tokenized (applied on string clauses only) - /// - public SearchFilter NotAnalyzed() - { - IsNotAnalyzed = true; - return this; - } - - /// - /// Applies a parent identifier. - /// - /// Parent identifier - public SearchFilter HasParent(int parentId) - { - ParentId = parentId; - return this; - } - - /// - /// Applies a specific boost to a clause. - /// - /// A value greater than zero, by which the score will be multiplied. - /// If greater than 1, it will improve the weight of a clause - public SearchFilter Weighted(float weight) - { - Boost = weight; - return this; - } - - #endregion - - #region Static factories - - public static CombinedSearchFilter Combined(params ISearchFilter[] filters) - { - var filter = new CombinedSearchFilter(filters); - filter.Occurence = SearchFilterOccurence.Must; - return filter; - } - - public static SearchFilter ByField(string fieldName, string term) - { - return ByField(fieldName, term, IndexTypeCode.String); - } - - public static SearchFilter ByField(string fieldName, int term) - { - return ByField(fieldName, term, IndexTypeCode.Int32); - } - - public static SearchFilter ByField(string fieldName, bool term) - { - return ByField(fieldName, term, IndexTypeCode.Boolean); - } - - public static SearchFilter ByField(string fieldName, double term) - { - return ByField(fieldName, term, IndexTypeCode.Double); - } - - public static SearchFilter ByField(string fieldName, DateTime term) - { - return ByField(fieldName, term, IndexTypeCode.DateTime); - } - - private static SearchFilter ByField(string fieldName, object term, IndexTypeCode typeCode) - { - Guard.NotEmpty(fieldName, nameof(fieldName)); - - return new SearchFilter - { - FieldName = fieldName, - Term = term, - TypeCode = typeCode - }; - } - - - public static RangeSearchFilter ByRange(string fieldName, string lower, string upper, bool includeLower = true, bool includeUpper = true) - { - return ByRange(fieldName, lower, upper, IndexTypeCode.String, includeLower, includeUpper); - } - - public static RangeSearchFilter ByRange(string fieldName, int? lower, int? upper, bool includeLower = true, bool includeUpper = true) - { - return ByRange(fieldName, lower, upper, IndexTypeCode.Int32, includeLower, includeUpper); - } - - public static RangeSearchFilter ByRange(string fieldName, double? lower, double? upper, bool includeLower = true, bool includeUpper = true) - { - return ByRange(fieldName, lower, upper, IndexTypeCode.Double, includeLower, includeUpper); - } - - public static RangeSearchFilter ByRange(string fieldName, DateTime? lower, DateTime? upper, bool includeLower = true, bool includeUpper = true) - { - return ByRange(fieldName, lower, upper, IndexTypeCode.DateTime, includeLower, includeUpper); - } - - private static RangeSearchFilter ByRange( - string fieldName, - object lowerTerm, - object upperTerm, - IndexTypeCode typeCode, - bool includeLower, - bool includeUpper) - { - Guard.NotEmpty(fieldName, nameof(fieldName)); - - return new RangeSearchFilter - { - FieldName = fieldName, - Term = lowerTerm, - UpperTerm = upperTerm, - TypeCode = typeCode, - IncludesLower = includeLower, - IncludesUpper = includeUpper - }; - } - - #endregion - - public override string ToString() - { - return $"{FieldName}: {Term.ToString()}"; - } - } - - public enum SearchFilterOccurence - { - Must = 0, - Should = 1, - MustNot = 2 - } + public class SearchFilter : SearchFilterBase, IAttributeSearchFilter + { + protected SearchFilter() + : base() + { + } + + public string FieldName + { + get; + protected set; + } + + public IndexTypeCode TypeCode + { + get; + protected set; + } + + public object Term + { + get; + protected set; + } + + public bool IsExactMatch + { + get; + protected set; + } + + public bool IsNotAnalyzed + { + get; + protected set; + } + + public int ParentId + { + get; + protected set; + } + + #region Fluent builder + + /// + /// Mark a clause as a mandatory match. By default all clauses are optional. + /// + /// Whether the clause is mandatory or not. + public SearchFilter Mandatory(bool mandatory = true) + { + Occurence = mandatory ? SearchFilterOccurence.Must : SearchFilterOccurence.MustNot; + return this; + } + + /// + /// Mark a clause as a forbidden match (MustNot). + /// + public SearchFilter Forbidden() + { + Occurence = SearchFilterOccurence.MustNot; + return this; + } + + /// + /// Specifies whether the clause should be matched exactly, like 'app' won't match 'apple' (applied on string clauses only). + /// + public SearchFilter ExactMatch() + { + IsExactMatch = true; + return this; + } + + /// + /// Specifies that the searched value will not be tokenized (applied on string clauses only) + /// + public SearchFilter NotAnalyzed() + { + IsNotAnalyzed = true; + return this; + } + + /// + /// Applies a parent identifier. + /// + /// Parent identifier + public SearchFilter HasParent(int parentId) + { + ParentId = parentId; + return this; + } + + /// + /// Applies a specific boost to a clause. + /// + /// A value greater than zero, by which the score will be multiplied. + /// If greater than 1, it will improve the weight of a clause + public SearchFilter Weighted(float weight) + { + Boost = weight; + return this; + } + + #endregion + + #region Static factories + + public static CombinedSearchFilter Combined(params ISearchFilter[] filters) + { + var filter = new CombinedSearchFilter(filters); + filter.Occurence = SearchFilterOccurence.Must; + return filter; + } + + public static SearchFilter ByField(string fieldName, string term) + { + return ByField(fieldName, term, IndexTypeCode.String); + } + + public static SearchFilter ByField(string fieldName, int term) + { + return ByField(fieldName, term, IndexTypeCode.Int32); + } + + public static SearchFilter ByField(string fieldName, bool term) + { + return ByField(fieldName, term, IndexTypeCode.Boolean); + } + + public static SearchFilter ByField(string fieldName, double term) + { + return ByField(fieldName, term, IndexTypeCode.Double); + } + + public static SearchFilter ByField(string fieldName, DateTime term) + { + return ByField(fieldName, term, IndexTypeCode.DateTime); + } + + private static SearchFilter ByField(string fieldName, object term, IndexTypeCode typeCode) + { + Guard.NotEmpty(fieldName, nameof(fieldName)); + + return new SearchFilter + { + FieldName = fieldName, + Term = term, + TypeCode = typeCode + }; + } + + + public static RangeSearchFilter ByRange(string fieldName, string lower, string upper, bool includeLower = true, bool includeUpper = true) + { + return ByRange(fieldName, lower, upper, IndexTypeCode.String, includeLower, includeUpper); + } + + public static RangeSearchFilter ByRange(string fieldName, int? lower, int? upper, bool includeLower = true, bool includeUpper = true) + { + return ByRange(fieldName, lower, upper, IndexTypeCode.Int32, includeLower, includeUpper); + } + + public static RangeSearchFilter ByRange(string fieldName, double? lower, double? upper, bool includeLower = true, bool includeUpper = true) + { + return ByRange(fieldName, lower, upper, IndexTypeCode.Double, includeLower, includeUpper); + } + + public static RangeSearchFilter ByRange(string fieldName, DateTime? lower, DateTime? upper, bool includeLower = true, bool includeUpper = true) + { + return ByRange(fieldName, lower, upper, IndexTypeCode.DateTime, includeLower, includeUpper); + } + + private static RangeSearchFilter ByRange( + string fieldName, + object lowerTerm, + object upperTerm, + IndexTypeCode typeCode, + bool includeLower, + bool includeUpper) + { + Guard.NotEmpty(fieldName, nameof(fieldName)); + + return new RangeSearchFilter + { + FieldName = fieldName, + Term = lowerTerm, + UpperTerm = upperTerm, + TypeCode = typeCode, + IncludesLower = includeLower, + IncludesUpper = includeUpper + }; + } + + #endregion + + public override string ToString() + { + return $"{FieldName}: {Term.ToString()}"; + } + } + + public enum SearchFilterOccurence + { + Must = 0, + Should = 1, + MustNot = 2 + } } diff --git a/src/Libraries/SmartStore.Core/Search/Filter/SearchFilterBase.cs b/src/Libraries/SmartStore.Core/Search/Filter/SearchFilterBase.cs index 29bb1f590b..f8d8ae3d14 100644 --- a/src/Libraries/SmartStore.Core/Search/Filter/SearchFilterBase.cs +++ b/src/Libraries/SmartStore.Core/Search/Filter/SearchFilterBase.cs @@ -1,30 +1,28 @@ -using System; - -namespace SmartStore.Core.Search +namespace SmartStore.Core.Search { - public abstract class SearchFilterBase : ISearchFilter - { - protected SearchFilterBase() - { - Occurence = SearchFilterOccurence.Should; - } + public abstract class SearchFilterBase : ISearchFilter + { + protected SearchFilterBase() + { + Occurence = SearchFilterOccurence.Should; + } - public float Boost - { - get; - protected internal set; - } + public float Boost + { + get; + protected internal set; + } - public SearchFilterOccurence Occurence - { - get; - protected internal set; - } + public SearchFilterOccurence Occurence + { + get; + protected internal set; + } - #region Fluent builder + #region Fluent builder - // [...] + // [...] - #endregion - } + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Search/IIndexDocument.cs b/src/Libraries/SmartStore.Core/Search/IIndexDocument.cs index 72ad6f3ccb..6c3db5a6d1 100644 --- a/src/Libraries/SmartStore.Core/Search/IIndexDocument.cs +++ b/src/Libraries/SmartStore.Core/Search/IIndexDocument.cs @@ -2,53 +2,53 @@ namespace SmartStore.Core.Search { - /// - /// Abstraction of document to be stored in the underlying index providers - /// - public interface IIndexDocument : IEnumerable - { - /// - /// The primary key of the indexed entity - /// - /// Implementors: the id must be persisted internally as an - int Id { get; } + /// + /// Abstraction of document to be stored in the underlying index providers + /// + public interface IIndexDocument : IEnumerable + { + /// + /// The primary key of the indexed entity + /// + /// Implementors: the id must be persisted internally as an + int Id { get; } - /// - /// Identifies the type of a document, can be null - /// - SearchDocumentType? DocumentType { get; } + /// + /// Identifies the type of a document, can be null + /// + SearchDocumentType? DocumentType { get; } - /// - /// The number of fields in this document - /// - int Count { get; } + /// + /// The number of fields in this document + /// + int Count { get; } - /// - /// Adds a field to this document - /// - /// The field to add - void Add(IndexField field); + /// + /// Adds a field to this document + /// + /// The field to add + void Add(IndexField field); - /// - /// Removes a field from this document - /// - /// The name of the field - /// The number of removed fields - /// A document can contain fields with the same name. This method removes ALL fields with the passed name. - int Remove(string name); + /// + /// Removes a field from this document + /// + /// The name of the field + /// The number of removed fields + /// A document can contain fields with the same name. This method removes ALL fields with the passed name. + int Remove(string name); - /// - /// Whether at least one field with the passed name exists in the document - /// - /// The name of the field(s) - /// true if field exists, false otherwise - bool Contains(string name); + /// + /// Whether at least one field with the passed name exists in the document + /// + /// The name of the field(s) + /// true if field exists, false otherwise + bool Contains(string name); - /// - /// Enumerates all fields with the passed name - /// - /// The name of the field(s) - /// The sequence - IEnumerable this[string name] { get; } - } + /// + /// Enumerates all fields with the passed name + /// + /// The name of the field(s) + /// The sequence + IEnumerable this[string name] { get; } + } } diff --git a/src/Libraries/SmartStore.Core/Search/IIndexManager.cs b/src/Libraries/SmartStore.Core/Search/IIndexManager.cs index 72868d85b5..8e4301c158 100644 --- a/src/Libraries/SmartStore.Core/Search/IIndexManager.cs +++ b/src/Libraries/SmartStore.Core/Search/IIndexManager.cs @@ -4,7 +4,7 @@ /// A simple factory for registered search index providers /// public interface IIndexManager - { + { /// /// Whether at least one provider is available, which implements /// @@ -14,12 +14,12 @@ public interface IIndexManager /// Primarily used to skip indexing processes bool HasAnyProvider(string scope, bool activeOnly = true); - /// - /// Returns the instance of the first registered index provider (e.g. a Lucene provider) - /// + /// + /// Returns the instance of the first registered index provider (e.g. a Lucene provider) + /// /// Index scope name - /// Whether only active providers should be queried for - /// The index provider implementation instance - IIndexProvider GetIndexProvider(string scope, bool activeOnly = true); - } + /// Whether only active providers should be queried for + /// The index provider implementation instance + IIndexProvider GetIndexProvider(string scope, bool activeOnly = true); + } } diff --git a/src/Libraries/SmartStore.Core/Search/IIndexOperation.cs b/src/Libraries/SmartStore.Core/Search/IIndexOperation.cs index 27453c616e..553e3f24fc 100644 --- a/src/Libraries/SmartStore.Core/Search/IIndexOperation.cs +++ b/src/Libraries/SmartStore.Core/Search/IIndexOperation.cs @@ -1,35 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SmartStore.Core.Search +namespace SmartStore.Core.Search { - public enum IndexOperationType - { - Index, - Delete - } + public enum IndexOperationType + { + Index, + Delete + } - /// - /// Represents an indexing operation - /// - public interface IIndexOperation - { - /// - /// The type of the operation - /// - IndexOperationType OperationType { get; } + /// + /// Represents an indexing operation + /// + public interface IIndexOperation + { + /// + /// The type of the operation + /// + IndexOperationType OperationType { get; } - /// - /// The document being inserted to or deleted from the index storage - /// - IIndexDocument Document { get; } + /// + /// The document being inserted to or deleted from the index storage + /// + IIndexDocument Document { get; } - /// - /// The database entity from which was created - /// - BaseEntity Entity { get; } - } + /// + /// The database entity from which was created + /// + BaseEntity Entity { get; } + } } diff --git a/src/Libraries/SmartStore.Core/Search/IIndexProvider.cs b/src/Libraries/SmartStore.Core/Search/IIndexProvider.cs index b63ac0261c..6b9464f068 100644 --- a/src/Libraries/SmartStore.Core/Search/IIndexProvider.cs +++ b/src/Libraries/SmartStore.Core/Search/IIndexProvider.cs @@ -2,8 +2,8 @@ namespace SmartStore.Core.Search { - public interface IIndexProvider - { + public interface IIndexProvider + { /// /// Gets a value indicating whether the search index given by scope is active /// @@ -15,32 +15,32 @@ public interface IIndexProvider /// IEnumerable EnumerateIndexes(); - /// - /// Creates an empty document - /// - /// The primary key of the indexed entity - /// Identifies the type of a document, can be null - /// The document instance - IIndexDocument CreateDocument(int id, SearchDocumentType? documentType); + /// + /// Creates an empty document + /// + /// The primary key of the indexed entity + /// Identifies the type of a document, can be null + /// The document instance + IIndexDocument CreateDocument(int id, SearchDocumentType? documentType); - /// - /// Returns a provider specific implementation of the interface - /// which allows interaction with the underlying index store for managing the index and containing documents. - /// - /// The index name - /// The index store - /// - /// This methods always returns an object instance, even if the index does not exist yet. - /// - IIndexStore GetIndexStore(string scope); + /// + /// Returns a provider specific implementation of the interface + /// which allows interaction with the underlying index store for managing the index and containing documents. + /// + /// The index name + /// The index store + /// + /// This methods always returns an object instance, even if the index does not exist yet. + /// + IIndexStore GetIndexStore(string scope); - /// - /// Returns a provider specific implementation of the interface - /// which allows executing queries against an index store. - /// - /// The index store - /// The query to execute against the store - /// The search engine instance - ISearchEngine GetSearchEngine(IIndexStore store, ISearchQuery query); - } + /// + /// Returns a provider specific implementation of the interface + /// which allows executing queries against an index store. + /// + /// The index store + /// The query to execute against the store + /// The search engine instance + ISearchEngine GetSearchEngine(IIndexStore store, ISearchQuery query); + } } diff --git a/src/Libraries/SmartStore.Core/Search/IIndexStore.cs b/src/Libraries/SmartStore.Core/Search/IIndexStore.cs index 36e7fe6a23..6a0d6df230 100644 --- a/src/Libraries/SmartStore.Core/Search/IIndexStore.cs +++ b/src/Libraries/SmartStore.Core/Search/IIndexStore.cs @@ -3,27 +3,27 @@ namespace SmartStore.Core.Search { - public interface IIndexStore - { - /// - /// Creates a new index if it doesn't exist already - /// - void CreateIfNotExists(); + public interface IIndexStore + { + /// + /// Creates a new index if it doesn't exist already + /// + void CreateIfNotExists(); - /// - /// Deletes the index - /// - void Delete(); + /// + /// Deletes the index + /// + void Delete(); - /// - /// Returns the name of the index - /// - string Scope { get; } + /// + /// Returns the name of the index + /// + string Scope { get; } - /// - /// Checks whether the index is already existing or not - /// - bool Exists { get; } + /// + /// Checks whether the index is already existing or not + /// + bool Exists { get; } /// /// Gets the size of the index in bytes. @@ -41,64 +41,64 @@ public interface IIndexStore /// Type of document, use null to get all documents int GetDocumentCount(SearchDocumentType? documentType); - /// - /// Returns every field's name available in the index - /// - IEnumerable GetAllFields(); + /// + /// Returns every field's name available in the index + /// + IEnumerable GetAllFields(); - /// - /// Removes all documents from the index - /// - void Clear(); + /// + /// Removes all documents from the index + /// + void Clear(); - /// - /// Acquires an index writer - /// - /// Provides information about the indexing operation - /// - /// This method creates a transient writer instance which automatically gets released on dispose. - /// - IDisposable AcquireWriter(AcquireWriterContext writerContext); + /// + /// Acquires an index writer + /// + /// Provides information about the indexing operation + /// + /// This method creates a transient writer instance which automatically gets released on dispose. + /// + IDisposable AcquireWriter(AcquireWriterContext writerContext); - /// - /// Adds a set of new documents to the index - /// - /// - /// This method will delete already existing documents before saving them - /// - void SaveDocuments(IEnumerable documents); + /// + /// Adds a set of new documents to the index + /// + /// + /// This method will delete already existing documents before saving them + /// + void SaveDocuments(IEnumerable documents); - /// - /// Removes a set of existing documents from the index - /// - void DeleteDocuments(IEnumerable documents); + /// + /// Removes a set of existing documents from the index + /// + void DeleteDocuments(IEnumerable documents); - /// - /// Removes a set of existing documents from the index - /// - /// Type of document - void DeleteDocuments(SearchDocumentType documentType); - } + /// + /// Removes a set of existing documents from the index + /// + /// Type of document + void DeleteDocuments(SearchDocumentType documentType); + } - public static class IIndexStoreExtensions - { - /// - /// Adds a new document to the index - /// - /// - /// This method will delete a document with the same entity id - if it exists - before saving it. - /// - public static void SaveDocument(this IIndexStore store, IIndexDocument document) - { - store.SaveDocuments(new[] { document }); - } + public static class IIndexStoreExtensions + { + /// + /// Adds a new document to the index + /// + /// + /// This method will delete a document with the same entity id - if it exists - before saving it. + /// + public static void SaveDocument(this IIndexStore store, IIndexDocument document) + { + store.SaveDocuments(new[] { document }); + } - /// - /// Removes an existing document from the index - /// - public static void DeleteDocument(this IIndexStore store, IIndexDocument document) - { - store.DeleteDocuments(new[] { document }); - } - } + /// + /// Removes an existing document from the index + /// + public static void DeleteDocument(this IIndexStore store, IIndexDocument document) + { + store.DeleteDocuments(new[] { document }); + } + } } diff --git a/src/Libraries/SmartStore.Core/Search/ISearchAlias.cs b/src/Libraries/SmartStore.Core/Search/ISearchAlias.cs index a822492057..81f1129163 100644 --- a/src/Libraries/SmartStore.Core/Search/ISearchAlias.cs +++ b/src/Libraries/SmartStore.Core/Search/ISearchAlias.cs @@ -1,12 +1,12 @@ namespace SmartStore.Core.Search { - /// - /// Represents an entity which supports SEO friendly search alias - /// - public interface ISearchAlias - { - int Id { get; set; } + /// + /// Represents an entity which supports SEO friendly search alias + /// + public interface ISearchAlias + { + int Id { get; set; } - string Alias { get; set; } - } + string Alias { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Search/ISearchEngine.cs b/src/Libraries/SmartStore.Core/Search/ISearchEngine.cs index 12235f2d44..96a651765e 100644 --- a/src/Libraries/SmartStore.Core/Search/ISearchEngine.cs +++ b/src/Libraries/SmartStore.Core/Search/ISearchEngine.cs @@ -3,43 +3,43 @@ namespace SmartStore.Core.Search { - public interface ISearchEngine - { - /// - /// Search query - /// - ISearchQuery Query { get; } + public interface ISearchEngine + { + /// + /// Search query + /// + ISearchQuery Query { get; } - /// - /// Get search hit by entity identifier - /// - /// Entity identifier - /// Search hit - ISearchHit Get(int id); + /// + /// Get search hit by entity identifier + /// + /// Entity identifier + /// Search hit + ISearchHit Get(int id); - /// - /// Get total number of search hits - /// - /// Total number of search hits - int Count(); + /// + /// Get total number of search hits + /// + /// Total number of search hits + int Count(); - /// - /// Search - /// - /// Search hits - IEnumerable Search(); + /// + /// Search + /// + /// Search hits + IEnumerable Search(); - /// - /// Gets the facet map for drilldown navigation - /// - /// The facet groups - IDictionary GetFacetMap(); + /// + /// Gets the facet map for drilldown navigation + /// + /// The facet groups + IDictionary GetFacetMap(); - /// - /// Performs spell checking ("Diy you mean?") - /// - /// Suggestions/corrections or an empty array - string[] CheckSpelling(); + /// + /// Performs spell checking ("Diy you mean?") + /// + /// Suggestions/corrections or an empty array + string[] CheckSpelling(); /// /// Highlights chosen terms in a text, extracting the most relevant sections diff --git a/src/Libraries/SmartStore.Core/Search/ISearchHit.cs b/src/Libraries/SmartStore.Core/Search/ISearchHit.cs index af39c9a495..03c09a4299 100644 --- a/src/Libraries/SmartStore.Core/Search/ISearchHit.cs +++ b/src/Libraries/SmartStore.Core/Search/ISearchHit.cs @@ -3,18 +3,18 @@ namespace SmartStore.Core.Search { - public interface ISearchHit - { - int EntityId { get; } - float Score { get; } + public interface ISearchHit + { + int EntityId { get; } + float Score { get; } - int GetInt(string name); - double GetDouble(string name); - bool GetBoolean(string name); - string GetString(string name); - string GetString(string name, string languageSeoCode); - DateTime GetDateTime(string name); + int GetInt(string name); + double GetDouble(string name); + bool GetBoolean(string name); + string GetString(string name); + string GetString(string name, string languageSeoCode); + DateTime GetDateTime(string name); - IEnumerable GetStoredFieldNames(); - } + IEnumerable GetStoredFieldNames(); + } } diff --git a/src/Libraries/SmartStore.Core/Search/ISearchQuery.cs b/src/Libraries/SmartStore.Core/Search/ISearchQuery.cs index 28ba070494..46d60e6761 100644 --- a/src/Libraries/SmartStore.Core/Search/ISearchQuery.cs +++ b/src/Libraries/SmartStore.Core/Search/ISearchQuery.cs @@ -4,50 +4,50 @@ namespace SmartStore.Core.Search { public interface ISearchQuery - { - // Language, Currency & Store - int? LanguageId { get; } - string LanguageCulture { get; } - string CurrencyCode { get; } + { + // Language, Currency & Store + int? LanguageId { get; } + string LanguageCulture { get; } + string CurrencyCode { get; } int? StoreId { get; } // Search term string[] Fields { get; } - string Term { get; } - SearchMode Mode { get; } - bool EscapeTerm { get; } - bool IsFuzzySearch { get; } - - // Filtering - ICollection Filters { get; } - - // Facets - IReadOnlyDictionary FacetDescriptors { get; } - - // Paging - int Skip { get; } - int Take { get; } - - // Sorting - ICollection Sorting { get; } - - /// - /// Maximum number of suggestions returned from spell checker - /// - int SpellCheckerMaxSuggestions { get; } - - /// - /// Defines how many characters must be in the query before suggestions are provided - /// - int SpellCheckerMinQueryLength { get; } - - /// - /// The maximum number of product hits up to which suggestions are provided - /// - int SpellCheckerMaxHitCount { get; } - - // Misc - string Origin { get; } - IDictionary CustomData { get; } - } + string Term { get; } + SearchMode Mode { get; } + bool EscapeTerm { get; } + bool IsFuzzySearch { get; } + + // Filtering + ICollection Filters { get; } + + // Facets + IReadOnlyDictionary FacetDescriptors { get; } + + // Paging + int Skip { get; } + int Take { get; } + + // Sorting + ICollection Sorting { get; } + + /// + /// Maximum number of suggestions returned from spell checker + /// + int SpellCheckerMaxSuggestions { get; } + + /// + /// Defines how many characters must be in the query before suggestions are provided + /// + int SpellCheckerMinQueryLength { get; } + + /// + /// The maximum number of product hits up to which suggestions are provided + /// + int SpellCheckerMaxHitCount { get; } + + // Misc + string Origin { get; } + IDictionary CustomData { get; } + } } diff --git a/src/Libraries/SmartStore.Core/Search/IndexDocument.cs b/src/Libraries/SmartStore.Core/Search/IndexDocument.cs index 0fce13d090..a63c2a9f7f 100644 --- a/src/Libraries/SmartStore.Core/Search/IndexDocument.cs +++ b/src/Libraries/SmartStore.Core/Search/IndexDocument.cs @@ -6,103 +6,97 @@ namespace SmartStore.Core.Search { - public class IndexDocument : IIndexDocument - { - protected readonly Multimap _fields; - - public IndexDocument(int id) - : this(id, null) - { - } - - public IndexDocument(int id, SearchDocumentType? documentType) - { - _fields = new Multimap(StringComparer.OrdinalIgnoreCase); - - Add(new IndexField("id", id).Store()); - - if (documentType.HasValue) - { - Add(new IndexField("doctype", (int)documentType.Value).Store()); - } - } - - public int Id - { - get - { - return (int)_fields["id"].FirstOrDefault().Value; - } - } - - public virtual SearchDocumentType? DocumentType - { - get - { - if (_fields.ContainsKey("doctype")) - { - return (SearchDocumentType)_fields["doctype"].FirstOrDefault().Value; - } - return null; - } - } - - public int Count => _fields.TotalValueCount; - - public virtual void Add(IndexField field) - { - if (field.Name.IsCaseInsensitiveEqual("id") && _fields.ContainsKey("id")) - { - // special treatment for id field: allow only one! - _fields.RemoveAll("id"); - } - - if (field.Name.IsCaseInsensitiveEqual("doctype") && _fields.ContainsKey("doctype")) - { - _fields.RemoveAll("doctype"); - } - - _fields.Add(field.Name, field); - } - - public int Remove(string name) - { - if (_fields.ContainsKey(name)) - { - var num = _fields[name].Count; - _fields.RemoveAll(name); - return num; - } - - return 0; - } - - public bool Contains(string name) - { - return _fields.ContainsKey(name); - } - - public IEnumerable this[string name] - { - get - { - if (_fields.ContainsKey(name)) - { - return _fields[name]; - } - - return Enumerable.Empty(); - } - } - - public IEnumerator GetEnumerator() - { - return _fields.SelectMany(x => x.Value).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } + public class IndexDocument : IIndexDocument + { + protected readonly Multimap _fields; + + public IndexDocument(int id) + : this(id, null) + { + } + + public IndexDocument(int id, SearchDocumentType? documentType) + { + _fields = new Multimap(StringComparer.OrdinalIgnoreCase); + + Add(new IndexField("id", id).Store()); + + if (documentType.HasValue) + { + Add(new IndexField("doctype", (int)documentType.Value).Store()); + } + } + + public int Id => (int)_fields["id"].FirstOrDefault().Value; + + public virtual SearchDocumentType? DocumentType + { + get + { + if (_fields.ContainsKey("doctype")) + { + return (SearchDocumentType)_fields["doctype"].FirstOrDefault().Value; + } + return null; + } + } + + public int Count => _fields.TotalValueCount; + + public virtual void Add(IndexField field) + { + if (field.Name.IsCaseInsensitiveEqual("id") && _fields.ContainsKey("id")) + { + // special treatment for id field: allow only one! + _fields.RemoveAll("id"); + } + + if (field.Name.IsCaseInsensitiveEqual("doctype") && _fields.ContainsKey("doctype")) + { + _fields.RemoveAll("doctype"); + } + + _fields.Add(field.Name, field); + } + + public int Remove(string name) + { + if (_fields.ContainsKey(name)) + { + var num = _fields[name].Count; + _fields.RemoveAll(name); + return num; + } + + return 0; + } + + public bool Contains(string name) + { + return _fields.ContainsKey(name); + } + + public IEnumerable this[string name] + { + get + { + if (_fields.ContainsKey(name)) + { + return _fields[name]; + } + + return Enumerable.Empty(); + } + } + + public IEnumerator GetEnumerator() + { + return _fields.SelectMany(x => x.Value).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Search/IndexField.cs b/src/Libraries/SmartStore.Core/Search/IndexField.cs index 396b9abc8f..3bb2801287 100644 --- a/src/Libraries/SmartStore.Core/Search/IndexField.cs +++ b/src/Libraries/SmartStore.Core/Search/IndexField.cs @@ -2,121 +2,121 @@ namespace SmartStore.Core.Search { - public class IndexField - { - public IndexField(string name, bool value) - : this(name, value, IndexTypeCode.Boolean) - { - } - - public IndexField(string name, int value) - : this(name, value, IndexTypeCode.Int32) - { - } - - public IndexField(string name, double value) - : this(name, value, IndexTypeCode.Double) - { - } - - public IndexField(string name, DateTime value) - : this(name, value, IndexTypeCode.DateTime) - { - } - - public IndexField(string name, string value) - : this(name, value, IndexTypeCode.String) - { - } - - private IndexField(string name, object value, IndexTypeCode typeCode) - { - Guard.NotEmpty(name, nameof(name)); - - Name = name; - Value = value; - TypeCode = typeCode; - } - - public IndexField Store(bool store = true) - { - Stored = store; - return this; - } - - public IndexField Analyze(bool analyze = true) - { - Analyzed = analyze; - return this; - } - - public IndexField RemoveTags(bool removeTags = true) - { - ShouldRemoveTags = removeTags; - return this; - } - - public IndexField Boost(float factor) - { - Boosted = factor; - return this; - } - - public string Name - { - get; - private set; - } - - public object Value - { - get; - private set; - } - - public IndexTypeCode TypeCode - { - get; - private set; - } - - public bool Stored - { - get; - private set; - } - - public bool Analyzed - { - get; - private set; - } - - public bool ShouldRemoveTags - { - get; - private set; - } - - public float Boosted - { - get; - private set; - } - - public override string ToString() - { - return Name + ": " + Value.ToString().NaIfEmpty(); - } - } - - public enum IndexTypeCode - { - Empty = 0, - Boolean = 3, - Int32 = 9, - Double = 14, - DateTime = 16, - String = 18 - } + public class IndexField + { + public IndexField(string name, bool value) + : this(name, value, IndexTypeCode.Boolean) + { + } + + public IndexField(string name, int value) + : this(name, value, IndexTypeCode.Int32) + { + } + + public IndexField(string name, double value) + : this(name, value, IndexTypeCode.Double) + { + } + + public IndexField(string name, DateTime value) + : this(name, value, IndexTypeCode.DateTime) + { + } + + public IndexField(string name, string value) + : this(name, value, IndexTypeCode.String) + { + } + + private IndexField(string name, object value, IndexTypeCode typeCode) + { + Guard.NotEmpty(name, nameof(name)); + + Name = name; + Value = value; + TypeCode = typeCode; + } + + public IndexField Store(bool store = true) + { + Stored = store; + return this; + } + + public IndexField Analyze(bool analyze = true) + { + Analyzed = analyze; + return this; + } + + public IndexField RemoveTags(bool removeTags = true) + { + ShouldRemoveTags = removeTags; + return this; + } + + public IndexField Boost(float factor) + { + Boosted = factor; + return this; + } + + public string Name + { + get; + private set; + } + + public object Value + { + get; + private set; + } + + public IndexTypeCode TypeCode + { + get; + private set; + } + + public bool Stored + { + get; + private set; + } + + public bool Analyzed + { + get; + private set; + } + + public bool ShouldRemoveTags + { + get; + private set; + } + + public float Boosted + { + get; + private set; + } + + public override string ToString() + { + return Name + ": " + Value.ToString().NaIfEmpty(); + } + } + + public enum IndexTypeCode + { + Empty = 0, + Boolean = 3, + Int32 = 9, + Double = 14, + DateTime = 16, + String = 18 + } } diff --git a/src/Libraries/SmartStore.Core/Search/IndexInfo.cs b/src/Libraries/SmartStore.Core/Search/IndexInfo.cs index 7389b7a5cf..b0fbc4b5cc 100644 --- a/src/Libraries/SmartStore.Core/Search/IndexInfo.cs +++ b/src/Libraries/SmartStore.Core/Search/IndexInfo.cs @@ -6,103 +6,103 @@ namespace SmartStore.Core.Search { - public enum IndexingStatus - { - Unavailable = -1, - Idle = 0, - Rebuilding = 1, - Updating = 2 - } - - public class IndexInfo - { - public IndexInfo(string scope) - { + public enum IndexingStatus + { + Unavailable = -1, + Idle = 0, + Rebuilding = 1, + Updating = 2 + } + + public class IndexInfo + { + public IndexInfo(string scope) + { Guard.NotEmpty(scope, nameof(scope)); Scope = scope; - Fields = new string[0]; - } + Fields = new string[0]; + } - public string Scope { get; private set; } - public int DocumentCount { get; set; } + 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; } - // loaded from status file - public DateTime? LastIndexedUtc { get; set; } - public TimeSpan? LastIndexingDuration { get; set; } - public bool IsModified { get; set; } - public IndexingStatus Status { get; set; } - public string Error { get; set; } - - /// - /// Indicates that the index should be rebuilt from scratch, because - /// some global settings have changed (like tax rates for example) - /// - public bool ShouldRebuild { get; set; } - - public string ToXml() - { - return new XDocument( - new XElement("info", - new XElement("status", Status), - new XElement("last-indexed-utc", LastIndexedUtc?.ToString("u")), - new XElement("last-indexing-duration", LastIndexingDuration?.ToString("c")), - new XElement("is-modified", IsModified ? "true" : "false"), - new XElement("error", Error), - new XElement("should-rebuild", ShouldRebuild ? "true" : "false"), - new XElement("document-count", DocumentCount), + // loaded from status file + public DateTime? LastIndexedUtc { get; set; } + public TimeSpan? LastIndexingDuration { get; set; } + public bool IsModified { get; set; } + public IndexingStatus Status { get; set; } + public string Error { get; set; } + + /// + /// Indicates that the index should be rebuilt from scratch, because + /// some global settings have changed (like tax rates for example) + /// + public bool ShouldRebuild { get; set; } + + public string ToXml() + { + return new XDocument( + new XElement("info", + new XElement("status", Status), + new XElement("last-indexed-utc", LastIndexedUtc?.ToString("u")), + new XElement("last-indexing-duration", LastIndexingDuration?.ToString("c")), + new XElement("is-modified", IsModified ? "true" : "false"), + 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())) - )).ToString(); - } + )).ToString(); + } - public static IndexInfo FromXml(string xml, string scope) - { + public static IndexInfo FromXml(string xml, string scope) + { var info = new IndexInfo(scope); - try - { - var doc = XDocument.Parse(xml); - - var lastIndexed = doc.Descendants("last-indexed-utc").FirstOrDefault()?.Value; - if (lastIndexed.HasValue()) - { - info.LastIndexedUtc = lastIndexed.Convert()?.ToUniversalTime(); - } - - var lastDuration = doc.Descendants("last-indexing-duration").FirstOrDefault()?.Value; - if (lastDuration.HasValue()) - { - info.LastIndexingDuration = lastDuration.Convert(); - } - - var isModified = doc.Descendants("is-modified").FirstOrDefault()?.Value; - if (isModified.HasValue()) - { - info.IsModified = isModified.Convert(); - } - else - { - info.IsModified = lastIndexed.HasValue(); - } - - var status = doc.Descendants("status").FirstOrDefault()?.Value; - if (status.HasValue()) - { - info.Status = status.Convert(); - } - - info.Error = doc.Descendants("error").FirstOrDefault()?.Value; - - var documentCount = doc.Descendants("document-count").FirstOrDefault()?.Value; - if (documentCount.HasValue()) - { - info.DocumentCount = documentCount.ToInt(); - } + try + { + var doc = XDocument.Parse(xml); + + var lastIndexed = doc.Descendants("last-indexed-utc").FirstOrDefault()?.Value; + if (lastIndexed.HasValue()) + { + info.LastIndexedUtc = lastIndexed.Convert()?.ToUniversalTime(); + } + + var lastDuration = doc.Descendants("last-indexing-duration").FirstOrDefault()?.Value; + if (lastDuration.HasValue()) + { + info.LastIndexingDuration = lastDuration.Convert(); + } + + var isModified = doc.Descendants("is-modified").FirstOrDefault()?.Value; + if (isModified.HasValue()) + { + info.IsModified = isModified.Convert(); + } + else + { + info.IsModified = lastIndexed.HasValue(); + } + + var status = doc.Descendants("status").FirstOrDefault()?.Value; + if (status.HasValue()) + { + info.Status = status.Convert(); + } + + info.Error = doc.Descendants("error").FirstOrDefault()?.Value; + + var documentCount = doc.Descendants("document-count").FirstOrDefault()?.Value; + if (documentCount.HasValue()) + { + info.DocumentCount = documentCount.ToInt(); + } var indexSize = doc.Descendants("index-size").FirstOrDefault()?.Value; if (indexSize.HasValue() && CommonHelper.TryConvert(indexSize, out long size)) @@ -117,20 +117,20 @@ public static IndexInfo FromXml(string xml, string scope) } var fields = doc.Descendants("fields").FirstOrDefault()?.Value; - if (fields.HasValue()) - { - info.Fields = fields.SplitSafe(", "); - } - - var shouldRebuild = doc.Descendants("should-rebuild").FirstOrDefault()?.Value; - if (shouldRebuild.HasValue()) - { - info.ShouldRebuild = shouldRebuild.Convert(); - } - } - catch { } - - return info; - } - } + if (fields.HasValue()) + { + info.Fields = fields.SplitSafe(", "); + } + + var shouldRebuild = doc.Descendants("should-rebuild").FirstOrDefault()?.Value; + if (shouldRebuild.HasValue()) + { + info.ShouldRebuild = shouldRebuild.Convert(); + } + } + catch { } + + return info; + } + } } diff --git a/src/Libraries/SmartStore.Core/Search/IndexProviderBase.cs b/src/Libraries/SmartStore.Core/Search/IndexProviderBase.cs index 15662c3768..a6e57e771b 100644 --- a/src/Libraries/SmartStore.Core/Search/IndexProviderBase.cs +++ b/src/Libraries/SmartStore.Core/Search/IndexProviderBase.cs @@ -2,8 +2,8 @@ namespace SmartStore.Core.Search { - public abstract class IndexProviderBase : IIndexProvider - { + public abstract class IndexProviderBase : IIndexProvider + { public virtual bool IsActive(string scope) { return true; @@ -11,15 +11,15 @@ public virtual bool IsActive(string scope) public abstract IEnumerable EnumerateIndexes(); - public virtual IIndexDocument CreateDocument(int id, SearchDocumentType? documentType) - { - Guard.IsPositive(id, nameof(id)); + public virtual IIndexDocument CreateDocument(int id, SearchDocumentType? documentType) + { + Guard.IsPositive(id, nameof(id)); - return new IndexDocument(id, documentType); - } + return new IndexDocument(id, documentType); + } - public abstract IIndexStore GetIndexStore(string scope); + public abstract IIndexStore GetIndexStore(string scope); - public abstract ISearchEngine GetSearchEngine(IIndexStore store, ISearchQuery query); - } + public abstract ISearchEngine GetSearchEngine(IIndexStore store, ISearchQuery query); + } } diff --git a/src/Libraries/SmartStore.Core/Search/SearchDocumentType.cs b/src/Libraries/SmartStore.Core/Search/SearchDocumentType.cs index c2597f571d..a919aabc71 100644 --- a/src/Libraries/SmartStore.Core/Search/SearchDocumentType.cs +++ b/src/Libraries/SmartStore.Core/Search/SearchDocumentType.cs @@ -1,17 +1,17 @@ namespace SmartStore.Core.Search { - public enum SearchDocumentType - { - Product = 0, - Category, - Manufacturer, - DeliveryTime, - Attribute, - AttributeValue, - Variant, - VariantValue, + public enum SearchDocumentType + { + Product = 0, + Category, + Manufacturer, + DeliveryTime, + Attribute, + AttributeValue, + Variant, + VariantValue, Customer, Forum, ForumPost - } + } } diff --git a/src/Libraries/SmartStore.Core/Search/SearchEnums.cs b/src/Libraries/SmartStore.Core/Search/SearchEnums.cs index a280714d51..dd85018a4e 100644 --- a/src/Libraries/SmartStore.Core/Search/SearchEnums.cs +++ b/src/Libraries/SmartStore.Core/Search/SearchEnums.cs @@ -1,20 +1,20 @@ namespace SmartStore.Core.Search { - public enum SearchMode - { - /// - /// Term search - /// - ExactMatch = 0, - - /// - /// Prefix term search - /// - StartsWith, + public enum SearchMode + { + /// + /// Term search + /// + ExactMatch = 0, - /// - /// Wildcard search - /// - Contains - } + /// + /// Prefix term search + /// + StartsWith, + + /// + /// Wildcard search + /// + Contains + } } diff --git a/src/Libraries/SmartStore.Core/Search/SearchQuery.cs b/src/Libraries/SmartStore.Core/Search/SearchQuery.cs index 18cfcd3f83..b9e10af195 100644 --- a/src/Libraries/SmartStore.Core/Search/SearchQuery.cs +++ b/src/Libraries/SmartStore.Core/Search/SearchQuery.cs @@ -1,284 +1,304 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using SmartStore.Core.Domain.Directory; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Search.Facets; +using SmartStore.Utilities.ObjectPools; namespace SmartStore.Core.Search { public enum SearchResultFlags - { - WithHits = 1 << 0, - WithFacets = 1 << 1, - WithSuggestions = 1 << 2, - Full = WithHits | WithFacets | WithSuggestions - } - - public class SearchQuery : SearchQuery - { - /// - /// Initializes a new instance of the class without a search term being set - /// - public SearchQuery() - : base((string[])null, null) - { - } - - public SearchQuery(string field, string term, SearchMode mode = SearchMode.Contains, bool escape = false, bool isFuzzySearch = false) - : base(field.HasValue() ? new[] { field } : null, term, mode, escape, isFuzzySearch) - { - } - - public SearchQuery(string[] fields, string term, SearchMode mode = SearchMode.Contains, bool escape = false, bool isFuzzySearch = false) - : base(fields, term, mode, escape, isFuzzySearch) - { - } - } - - public class SearchQuery : ISearchQuery where TQuery : class, ISearchQuery - { - private readonly Dictionary _facetDescriptors; - private Dictionary _customData; - - protected SearchQuery(string[] fields, string term, SearchMode mode = SearchMode.Contains, bool escape = false, bool isFuzzySearch = false) - { - Fields = fields; - Term = term; - Mode = mode; - EscapeTerm = escape; - IsFuzzySearch = isFuzzySearch; - - Filters = new List(); - Sorting = new List(); - _facetDescriptors = new Dictionary(StringComparer.OrdinalIgnoreCase); - - Take = int.MaxValue; - - SpellCheckerMinQueryLength = 4; - SpellCheckerMaxHitCount = 3; - - ResultFlags = SearchResultFlags.WithHits; - } - - // Language, Currency & Store - public int? LanguageId { get; protected set; } - public string LanguageCulture { get; protected set; } - public string CurrencyCode { get; protected set; } + { + WithHits = 1 << 0, + WithFacets = 1 << 1, + WithSuggestions = 1 << 2, + Full = WithHits | WithFacets | WithSuggestions + } + + public class SearchQuery : SearchQuery + { + /// + /// Initializes a new instance of the class without a search term being set + /// + public SearchQuery() + : base((string[])null, null) + { + } + + public SearchQuery(string field, string term, SearchMode mode = SearchMode.Contains, bool escape = false, bool isFuzzySearch = false) + : base(field.HasValue() ? new[] { field } : null, term, mode, escape, isFuzzySearch) + { + } + + public SearchQuery(string[] fields, string term, SearchMode mode = SearchMode.Contains, bool escape = false, bool isFuzzySearch = false) + : base(fields, term, mode, escape, isFuzzySearch) + { + } + } + + public class SearchQuery : ISearchQuery where TQuery : class, ISearchQuery + { + private readonly Dictionary _facetDescriptors; + private Dictionary _customData; + + protected SearchQuery(string[] fields, string term, SearchMode mode = SearchMode.Contains, bool escape = false, bool isFuzzySearch = false) + { + Fields = fields; + Term = term; + Mode = mode; + EscapeTerm = escape; + IsFuzzySearch = isFuzzySearch; + + Filters = new List(); + Sorting = new List(); + _facetDescriptors = new Dictionary(StringComparer.OrdinalIgnoreCase); + + Take = int.MaxValue; + + SpellCheckerMinQueryLength = 4; + SpellCheckerMaxHitCount = 3; + + ResultFlags = SearchResultFlags.WithHits; + } + + // Language, Currency & Store + public int? LanguageId { get; protected set; } + public string LanguageCulture { get; protected set; } + public string CurrencyCode { get; protected set; } public int? StoreId { get; protected set; } // Search term public string[] Fields { get; set; } - public string Term { get; set; } - public bool EscapeTerm { get; protected set; } - public SearchMode Mode { get; protected set; } - public bool IsFuzzySearch { get; protected set; } + public string Term { get; set; } + public bool EscapeTerm { get; protected set; } + public SearchMode Mode { get; protected set; } + public bool IsFuzzySearch { get; protected set; } - // Filtering - public ICollection Filters { get; } + // Filtering + public ICollection Filters { get; } - // Facets - public IReadOnlyDictionary FacetDescriptors - { - get { return _facetDescriptors; } - } + // Facets + public IReadOnlyDictionary FacetDescriptors => _facetDescriptors; - // Paging - public int Skip { get; protected set; } - public int Take { get; protected set; } - public int PageIndex - { - get - { - if (Take == 0) - return 0; + // Paging + public int Skip { get; protected set; } + public int Take { get; protected set; } + public int PageIndex + { + get + { + if (Take == 0) + return 0; - return Math.Max(Skip / Take, 0); - } - } + return Math.Max(Skip / Take, 0); + } + } - // Sorting - public ICollection Sorting { get; } + // Sorting + public ICollection Sorting { get; } - // Spell checker - public int SpellCheckerMaxSuggestions { get; protected set; } - public int SpellCheckerMinQueryLength { get; protected set; } - public int SpellCheckerMaxHitCount { get; protected set; } - - // Result control - public SearchResultFlags ResultFlags { get; protected set; } + // Spell checker + public int SpellCheckerMaxSuggestions { get; protected set; } + public int SpellCheckerMinQueryLength { get; protected set; } + public int SpellCheckerMaxHitCount { get; protected set; } - // Misc - public string Origin { get; protected set; } + // Result control + public SearchResultFlags ResultFlags { get; protected set; } - public IDictionary CustomData - { - get - { - return _customData ?? (_customData = new Dictionary(StringComparer.OrdinalIgnoreCase)); - } - } - - #region Fluent builder - - public virtual TQuery HasStoreId(int id) - { - Guard.NotNegative(id, nameof(id)); - - StoreId = id; - - return (this as TQuery); - } - - public TQuery WithLanguage(Language language) - { - Guard.NotNull(language, nameof(language)); - Guard.NotEmpty(language.LanguageCulture, nameof(language.LanguageCulture)); - - LanguageId = language.Id; - LanguageCulture = language.LanguageCulture; - - return (this as TQuery); - } - - public TQuery WithCurrency(Currency currency) - { - Guard.NotNull(currency, nameof(currency)); - Guard.NotEmpty(currency.CurrencyCode, nameof(currency.CurrencyCode)); + // Misc + public string Origin { get; protected set; } - CurrencyCode = currency.CurrencyCode; + public IDictionary CustomData => _customData ?? (_customData = new Dictionary(StringComparer.OrdinalIgnoreCase)); - return (this as TQuery); - } + #region Fluent builder - public TQuery Slice(int skip, int take) - { - Guard.NotNegative(skip, nameof(skip)); - Guard.NotNegative(take, nameof(take)); + public virtual TQuery HasStoreId(int id) + { + Guard.NotNegative(id, nameof(id)); - Skip = skip; - Take = take; + StoreId = id; - return (this as TQuery); - } + return (this as TQuery); + } - public TQuery CheckSpelling(int maxSuggestions, int minQueryLength = 4, int maxHitCount = 3) - { - Guard.NotNegative(maxSuggestions, nameof(maxSuggestions)); - Guard.IsPositive(minQueryLength, nameof(minQueryLength)); - Guard.IsPositive(maxHitCount, nameof(maxHitCount)); + public TQuery WithLanguage(Language language) + { + Guard.NotNull(language, nameof(language)); + Guard.NotEmpty(language.LanguageCulture, nameof(language.LanguageCulture)); - ResultFlags = ResultFlags | SearchResultFlags.WithSuggestions; + LanguageId = language.Id; + LanguageCulture = language.LanguageCulture; - SpellCheckerMaxSuggestions = maxSuggestions; - SpellCheckerMinQueryLength = minQueryLength; - SpellCheckerMaxHitCount = maxHitCount; + return (this as TQuery); + } - return (this as TQuery); - } + public TQuery WithCurrency(Currency currency) + { + Guard.NotNull(currency, nameof(currency)); + Guard.NotEmpty(currency.CurrencyCode, nameof(currency.CurrencyCode)); - public TQuery WithFilter(ISearchFilter filter) - { - Guard.NotNull(filter, nameof(filter)); + CurrencyCode = currency.CurrencyCode; - Filters.Add(filter); + return (this as TQuery); + } - return (this as TQuery); - } + public TQuery Slice(int skip, int take) + { + Guard.NotNegative(skip, nameof(skip)); + Guard.NotNegative(take, nameof(take)); - public TQuery SortBy(SearchSort sort) - { - Guard.NotNull(sort, nameof(sort)); + Skip = skip; + Take = take; - Sorting.Add(sort); + return (this as TQuery); + } - return (this as TQuery); - } + public TQuery CheckSpelling(int maxSuggestions, int minQueryLength = 4, int maxHitCount = 3) + { + Guard.IsPositive(minQueryLength, nameof(minQueryLength)); + Guard.IsPositive(maxHitCount, nameof(maxHitCount)); - public TQuery BuildHits(bool build) - { - if (build) - { - ResultFlags = ResultFlags | SearchResultFlags.WithHits; - } - else - { - ResultFlags &= ~SearchResultFlags.WithHits; - } + if (maxSuggestions > 0) + { + ResultFlags = ResultFlags | SearchResultFlags.WithSuggestions; + } + else + { + ResultFlags &= ~SearchResultFlags.WithSuggestions; + } + SpellCheckerMaxSuggestions = Math.Max(maxSuggestions, 0); + SpellCheckerMinQueryLength = minQueryLength; + SpellCheckerMaxHitCount = maxHitCount; - return (this as TQuery); - } + return (this as TQuery); + } - public TQuery BuildFacetMap(bool build) - { - if (build) - { - ResultFlags = ResultFlags | SearchResultFlags.WithFacets; - } - else - { - ResultFlags &= ~SearchResultFlags.WithFacets; - } - + public TQuery WithFilter(ISearchFilter filter) + { + Guard.NotNull(filter, nameof(filter)); - return (this as TQuery); - } + Filters.Add(filter); - public TQuery WithFacet(FacetDescriptor facetDescription) - { - Guard.NotNull(facetDescription, nameof(facetDescription)); + return (this as TQuery); + } - if (_facetDescriptors.ContainsKey(facetDescription.Key)) - { - throw new InvalidOperationException("A facet description object with the same key has already been added. Key: {0}".FormatInvariant(facetDescription.Key)); - } + public TQuery SortBy(SearchSort sort) + { + Guard.NotNull(sort, nameof(sort)); - _facetDescriptors.Add(facetDescription.Key, facetDescription); + Sorting.Add(sort); - return (this as TQuery); - } + return (this as TQuery); + } - public TQuery OriginatesFrom(string origin) - { - Guard.NotEmpty(origin, nameof(origin)); + public TQuery BuildHits(bool build) + { + if (build) + { + ResultFlags = ResultFlags | SearchResultFlags.WithHits; + } + else + { + ResultFlags &= ~SearchResultFlags.WithHits; + } - Origin = origin; - return (this as TQuery); - } + return (this as TQuery); + } - #endregion + public TQuery BuildFacetMap(bool build) + { + if (build) + { + ResultFlags = ResultFlags | SearchResultFlags.WithFacets; + } + else + { + ResultFlags &= ~SearchResultFlags.WithFacets; + } - public override string ToString() - { - var sb = new StringBuilder(); - if (Term.HasValue()) - { - var fields = (Fields != null && Fields.Length > 0 ? string.Join(", ", Fields) : "".NaIfEmpty()); + return (this as TQuery); + } - sb.AppendFormat("'{0}' in {1}", Term, fields); + public TQuery WithFacet(FacetDescriptor facetDescription) + { + Guard.NotNull(facetDescription, nameof(facetDescription)); - var parameters = string.Join(" ", EscapeTerm ? "escape" : "", IsFuzzySearch ? "fuzzy" : Mode.ToString()).TrimSafe(); + if (_facetDescriptors.ContainsKey(facetDescription.Key)) + { + throw new InvalidOperationException("A facet description object with the same key has already been added. Key: {0}".FormatInvariant(facetDescription.Key)); + } - if (parameters.HasValue()) - { - sb.AppendFormat(" ({0})", parameters); - } - } + _facetDescriptors.Add(facetDescription.Key, facetDescription); - foreach (var filter in Filters) - { - if (sb.Length > 0) - { - sb.Append(" "); - } + return (this as TQuery); + } - sb.Append(filter.ToString()); - } + public TQuery OriginatesFrom(string origin) + { + Guard.NotEmpty(origin, nameof(origin)); - return sb.ToString(); - } - } + Origin = origin; + + return (this as TQuery); + } + + #endregion + + public override string ToString() + { + var psb = PooledStringBuilder.Rent(); + var sb = (StringBuilder)psb; + + if (Term.HasValue()) + { + var fields = (Fields != null && Fields.Length > 0 ? string.Join(", ", Fields) : "".NaIfEmpty()); + + sb.AppendFormat("'{0}' in {1}", Term, fields); + + var parameters = string.Join(" ", EscapeTerm ? "escape" : "", IsFuzzySearch ? "fuzzy" : Mode.ToString()).TrimSafe(); + + if (parameters.HasValue()) + { + sb.AppendFormat(" ({0})", parameters); + } + } + + foreach (var filter in Filters) + { + if (sb.Length > 0) + { + sb.Append(" "); + } + + sb.Append(filter.ToString()); + } + + return psb.ToStringAndReturn(); + } + + #region Utilities + + protected TQuery CreateFilter(string fieldName, params int[] values) + { + var len = values?.Length ?? 0; + if (len > 0) + { + if (len == 1) + { + return WithFilter(SearchFilter.ByField(fieldName, values[0]).Mandatory().ExactMatch().NotAnalyzed()); + } + + return WithFilter(SearchFilter.Combined(values.Select(x => SearchFilter.ByField(fieldName, x).ExactMatch().NotAnalyzed()).ToArray())); + } + + return this as TQuery; + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/Search/SearchSort.cs b/src/Libraries/SmartStore.Core/Search/SearchSort.cs index 423ad608db..388fd97d3f 100644 --- a/src/Libraries/SmartStore.Core/Search/SearchSort.cs +++ b/src/Libraries/SmartStore.Core/Search/SearchSort.cs @@ -1,82 +1,82 @@ namespace SmartStore.Core.Search { - public class SearchSort - { - private SearchSort(string name, IndexTypeCode typeCode, bool descending) - { - FieldName = name; - TypeCode = typeCode; - Descending = descending; - } + public class SearchSort + { + private SearchSort(string name, IndexTypeCode typeCode, bool descending) + { + FieldName = name; + TypeCode = typeCode; + Descending = descending; + } - public string FieldName - { - get; - private set; - } + public string FieldName + { + get; + private set; + } - /// - /// In this context, actually means Score - /// - public IndexTypeCode TypeCode - { - get; - private set; - } + /// + /// In this context, actually means Score + /// + public IndexTypeCode TypeCode + { + get; + private set; + } - public bool Descending - { - get; - private set; - } + public bool Descending + { + get; + private set; + } - public override string ToString() - { - if (FieldName.IsEmpty()) - { - return "RELEVANCE"; - } - else - { - return "{0} {1}".FormatInvariant(FieldName, Descending ? "DESC" : "ASC"); - } - } + public override string ToString() + { + if (FieldName.IsEmpty()) + { + return "RELEVANCE"; + } + else + { + return "{0} {1}".FormatInvariant(FieldName, Descending ? "DESC" : "ASC"); + } + } - public static SearchSort ByRelevance(bool descending = false) - { - return new SearchSort(null, IndexTypeCode.Empty, descending); - } + public static SearchSort ByRelevance(bool descending = false) + { + return new SearchSort(null, IndexTypeCode.Empty, descending); + } - public static SearchSort ByStringField(string fieldName, bool descending = false) - { - return ByField(fieldName, IndexTypeCode.String, descending); - } + public static SearchSort ByStringField(string fieldName, bool descending = false) + { + return ByField(fieldName, IndexTypeCode.String, descending); + } - public static SearchSort ByIntField(string fieldName, bool descending = false) - { - return ByField(fieldName, IndexTypeCode.Int32, descending); - } + public static SearchSort ByIntField(string fieldName, bool descending = false) + { + return ByField(fieldName, IndexTypeCode.Int32, descending); + } - public static SearchSort ByBooleanField(string fieldName, bool descending = false) - { - return ByField(fieldName, IndexTypeCode.Boolean, descending); - } + public static SearchSort ByBooleanField(string fieldName, bool descending = false) + { + return ByField(fieldName, IndexTypeCode.Boolean, descending); + } - public static SearchSort ByDoubleField(string fieldName, bool descending = false) - { - return ByField(fieldName, IndexTypeCode.Double, descending); - } + public static SearchSort ByDoubleField(string fieldName, bool descending = false) + { + return ByField(fieldName, IndexTypeCode.Double, descending); + } - public static SearchSort ByDateTimeField(string fieldName, bool descending = false) - { - return ByField(fieldName, IndexTypeCode.DateTime, descending); - } + public static SearchSort ByDateTimeField(string fieldName, bool descending = false) + { + return ByField(fieldName, IndexTypeCode.DateTime, descending); + } - private static SearchSort ByField(string fieldName, IndexTypeCode typeCode, bool descending = false) - { - Guard.NotEmpty(fieldName, nameof(fieldName)); + private static SearchSort ByField(string fieldName, IndexTypeCode typeCode, bool descending = false) + { + Guard.NotEmpty(fieldName, nameof(fieldName)); - return new SearchSort(fieldName, typeCode, descending); - } - } + return new SearchSort(fieldName, typeCode, descending); + } + } } diff --git a/src/Libraries/SmartStore.Core/Search/Settings/ForumSearchSettings.cs b/src/Libraries/SmartStore.Core/Search/Settings/ForumSearchSettings.cs index b213e2e56b..8831ea2c2d 100644 --- a/src/Libraries/SmartStore.Core/Search/Settings/ForumSearchSettings.cs +++ b/src/Libraries/SmartStore.Core/Search/Settings/ForumSearchSettings.cs @@ -5,15 +5,15 @@ namespace SmartStore.Core.Search { public class ForumSearchSettings : ISettings - { - public ForumSearchSettings() - { - SearchMode = SearchMode.Contains; - SearchFields = new List { "username", "text" }; + { + public ForumSearchSettings() + { + SearchMode = SearchMode.Contains; + SearchFields = new List { "username", "text" }; DefaultSortOrder = ForumTopicSorting.Relevance; InstantSearchEnabled = true; - InstantSearchNumberOfHits = 10; - InstantSearchTermMinLength = 2; + InstantSearchNumberOfHits = 10; + InstantSearchTermMinLength = 2; FilterMinHitCount = 1; FilterMaxChoicesCount = 20; @@ -22,15 +22,15 @@ public ForumSearchSettings() DateDisplayOrder = 3; } - /// - /// Gets or sets the search mode. - /// - public SearchMode SearchMode { get; set; } + /// + /// Gets or sets the search mode. + /// + public SearchMode SearchMode { get; set; } - /// - /// Gets or sets name of fields to be searched. The name field is always searched. - /// - public List SearchFields { get; set; } + /// + /// Gets or sets name of fields to be searched. The name field is always searched. + /// + public List SearchFields { get; set; } /// /// Gets or sets the default sort order in search results. @@ -42,15 +42,15 @@ public ForumSearchSettings() /// public bool InstantSearchEnabled { get; set; } - /// - /// Gets or sets the number of hits to return when using "instant-search" feature. - /// - public int InstantSearchNumberOfHits { get; set; } + /// + /// Gets or sets the number of hits to return when using "instant-search" feature. + /// + public int InstantSearchNumberOfHits { get; set; } - /// - /// Gets or sets a minimum instant-search term length. - /// - public int InstantSearchTermMinLength { get; set; } + /// + /// Gets or sets a minimum instant-search term length. + /// + public int InstantSearchTermMinLength { get; set; } /// /// Gets or sets the minimum hit count for a filter value. Values with a lower hit count are not displayed. diff --git a/src/Libraries/SmartStore.Core/Search/Settings/SearchSettings.cs b/src/Libraries/SmartStore.Core/Search/Settings/SearchSettings.cs index 0fee2c0bb3..9a581da895 100644 --- a/src/Libraries/SmartStore.Core/Search/Settings/SearchSettings.cs +++ b/src/Libraries/SmartStore.Core/Search/Settings/SearchSettings.cs @@ -4,96 +4,106 @@ namespace SmartStore.Core.Search { - public class SearchSettings : ISettings - { - public SearchSettings() - { - SearchMode = SearchMode.Contains; - SearchFields = new List { "sku", "shortdescription", "tagname", "manufacturer", "category" }; - InstantSearchEnabled = true; - ShowProductImagesInInstantSearch = true; - InstantSearchNumberOfProducts = 10; - InstantSearchTermMinLength = 2; - FilterMinHitCount = 1; - FilterMaxChoicesCount = 20; + public class SearchSettings : ISettings + { + public SearchSettings() + { + SearchMode = SearchMode.Contains; + SearchFields = new List { "sku", "shortdescription", "tagname", "manufacturer", "category" }; + InstantSearchEnabled = true; + ShowProductImagesInInstantSearch = true; + InstantSearchNumberOfProducts = 10; + InstantSearchTermMinLength = 2; + FilterMinHitCount = 1; + FilterMaxChoicesCount = 20; DefaultSortOrder = ProductSortingEnum.Relevance; - BrandDisplayOrder = 1; - PriceDisplayOrder = 2; - RatingDisplayOrder = 3; - DeliveryTimeDisplayOrder = 4; - AvailabilityDisplayOrder = 5; - NewArrivalsDisplayOrder = 6; - } - - /// - /// Gets or sets the search mode - /// - public SearchMode SearchMode { get; set; } - - /// - /// Gets or sets name of fields to be searched. The name field is always searched. - /// - public List SearchFields { get; set; } - - /// - /// Gets or sets a value indicating whether instant-search is enabled - /// - public bool InstantSearchEnabled { get; set; } - - /// - /// Gets or sets a value indicating whether to show product images in instant-search result - /// - public bool ShowProductImagesInInstantSearch { get; set; } - - /// - /// Gets or sets the number of products to return when using "instant-search" feature - /// - public int InstantSearchNumberOfProducts { get; set; } - - /// - /// Gets or sets a minimum instant-search term length - /// - public int InstantSearchTermMinLength { get; set; } - - /// - /// Gets or sets the minimum hit count for a filter value. Values with a lower hit count are not displayed. - /// - public int FilterMinHitCount { get; set; } - - /// - /// Gets or sets the maximum number of filter values to be displayed. - /// - public int FilterMaxChoicesCount { get; set; } + BrandDisplayOrder = 1; + PriceDisplayOrder = 2; + RatingDisplayOrder = 3; + DeliveryTimeDisplayOrder = 4; + AvailabilityDisplayOrder = 5; + NewArrivalsDisplayOrder = 6; + } + + /// + /// Gets or sets the search mode + /// + public SearchMode SearchMode { get; set; } + + /// + /// Gets or sets name of fields to be searched. The name field is always searched. + /// + public List SearchFields { get; set; } + + /// + /// Gets or sets a value indicating whether instant-search is enabled + /// + public bool InstantSearchEnabled { get; set; } + + /// + /// Gets or sets a value indicating whether to show product images in instant-search result + /// + public bool ShowProductImagesInInstantSearch { get; set; } + + /// + /// Gets or sets the number of products to return when using "instant-search" feature + /// + public int InstantSearchNumberOfProducts { get; set; } + + /// + /// Gets or sets a minimum instant-search term length + /// + public int InstantSearchTermMinLength { get; set; } + + /// + /// Gets or sets the minimum hit count for a filter value. Values with a lower hit count are not displayed. + /// + public int FilterMinHitCount { get; set; } + + /// + /// Gets or sets the maximum number of filter values to be displayed. + /// + public int FilterMaxChoicesCount { get; set; } /// /// Gets or sets the default sort order in search results /// public ProductSortingEnum DefaultSortOrder { get; set; } - // TBD: what about area specific searchin setting (product, blog, etc.) - - #region Common facet settings + /// + /// Hidden setting indicating whether to use catalog search instead of Linq search in backend. + /// + public bool UseCatalogSearchInBackend { get; set; } - /// - /// Gets or sets the a value indicating whether to include or exclude not available products by default. - /// - public bool IncludeNotAvailable { get; set; } + /// + /// Specifies whether the product page should be opened directly if the search term matches a SKU, MPN or GTIN. + /// + public bool SearchProductByIdentificationNumber { get; set; } - public bool BrandDisabled { get; set; } - public bool PriceDisabled { get; set; } - public bool RatingDisabled { get; set; } - public bool DeliveryTimeDisabled { get; set; } - public bool AvailabilityDisabled { get; set; } - public bool NewArrivalsDisabled { get; set; } + // TBD: what about area specific searchin setting (product, blog, etc.) - public int BrandDisplayOrder { get; set; } - public int PriceDisplayOrder { get; set; } - public int RatingDisplayOrder { get; set; } - public int DeliveryTimeDisplayOrder { get; set; } - public int AvailabilityDisplayOrder { get; set; } - public int NewArrivalsDisplayOrder { get; set; } + #region Common facet settings - #endregion - } + /// + /// Gets or sets the a value indicating whether to include or exclude not available products by default. + /// + public bool IncludeNotAvailable { get; set; } + + public bool BrandDisabled { get; set; } + public bool PriceDisabled { get; set; } + public bool RatingDisabled { get; set; } + public bool DeliveryTimeDisabled { get; set; } + public bool AvailabilityDisabled { get; set; } + public bool NewArrivalsDisabled { get; set; } + + public int BrandDisplayOrder { get; set; } + public int PriceDisplayOrder { get; set; } + public int RatingDisplayOrder { get; set; } + public int DeliveryTimeDisplayOrder { get; set; } + public int AvailabilityDisplayOrder { get; set; } + public int NewArrivalsDisplayOrder { get; set; } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/Security/IPermissionProvider.cs b/src/Libraries/SmartStore.Core/Security/IPermissionProvider.cs similarity index 86% rename from src/Libraries/SmartStore.Services/Security/IPermissionProvider.cs rename to src/Libraries/SmartStore.Core/Security/IPermissionProvider.cs index 60b6038c2d..6b49af4030 100644 --- a/src/Libraries/SmartStore.Services/Security/IPermissionProvider.cs +++ b/src/Libraries/SmartStore.Core/Security/IPermissionProvider.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using SmartStore.Core.Domain.Security; -namespace SmartStore.Services.Security +namespace SmartStore.Core.Security { public interface IPermissionProvider { diff --git a/src/Libraries/SmartStore.Core/Security/IPermissionService.cs b/src/Libraries/SmartStore.Core/Security/IPermissionService.cs new file mode 100644 index 0000000000..4acfba9ad5 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Security/IPermissionService.cs @@ -0,0 +1,177 @@ +using System.Collections.Generic; +using SmartStore.Collections; +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.Security; + +namespace SmartStore.Core.Security +{ + /// + /// Permission service interface. + /// + public partial interface IPermissionService + { + /// + /// Gets a permission. + /// + /// Permission identifier. + /// Permission. + PermissionRecord GetPermissionById(int permissionId); + + /// + /// Gets a permission. + /// + /// Permission system name. + /// Permission. + PermissionRecord GetPermissionBySystemName(string systemName); + + /// + /// Gets all permissions. + /// + /// Permissions. + IList GetAllPermissions(); + + /// + /// Gets system and display name of all permissions. + /// + /// System and display names. + IDictionary GetAllSystemNames(); + + /// + /// Inserts a permission. + /// + /// Permission. + void InsertPermission(PermissionRecord permission); + + /// + /// Updates a permission. + /// + /// Permission. + void UpdatePermission(PermissionRecord permission); + + /// + /// Deletes a permission. + /// + /// Permission. + void DeletePermission(PermissionRecord permission); + + + /// + /// Gets a permission role mapping. + /// + /// Permission role mapping identifier. + /// Permission role mapping. + PermissionRoleMapping GetPermissionRoleMappingById(int mappingId); + + /// + /// Inserts a permission role mapping. + /// + /// Permission role mapping. + void InsertPermissionRoleMapping(PermissionRoleMapping mapping); + + /// + /// Updates a permission role mapping. + /// + /// Permission role mapping. + void UpdatePermissionRoleMapping(PermissionRoleMapping mapping); + + /// + /// Deletes a permission role mapping. + /// + /// Permission role mapping. + void DeletePermissionRoleMapping(PermissionRoleMapping mapping); + + + /// + /// Installs permissions. Permissions are automatically installed by . + /// + /// Providers whose permissions are to be installed. + /// Whether to remove permissions no longer supported by the providers. + void InstallPermissions(IPermissionProvider[] permissionProviders, bool removeUnusedPermissions = false); + + + /// + /// Authorize permission. + /// + /// Permission. + /// true authorized otherwise false. + bool Authorize(PermissionRecord permission); + + /// + /// Authorize permission. + /// + /// Permission. + /// Customer. + /// true authorized otherwise false. + bool Authorize(PermissionRecord permission, Customer customer); + + /// + /// Authorize permission. + /// + /// Permission record system name. + /// true authorized otherwise false. + bool Authorize(string permissionSystemName); + + /// + /// Authorize permission. + /// + /// Permission record system name. + /// Customer. + /// true authorized otherwise false. + bool Authorize(string permissionSystemName, Customer customer); + + /// + /// Authorize permission by alias permission name. Required if granular permission migration has not yet run. + /// Functional only if the old permission resources still exist in the database. + /// + /// Permission record system name. + /// true authorized otherwise false. + bool AuthorizeByAlias(string permissionSystemName); + + + /// + /// Search all child permissions for an authorization (initial permission included). + /// + /// Permission record system name. + /// true authorization found otherwise false. + bool FindAuthorization(string permissionSystemName); + + /// + /// Search all child permissions for an authorization (initial permission included). + /// + /// Permission record system name. + /// Customer. + /// true authorization found otherwise false. + bool FindAuthorization(string permissionSystemName, Customer customer); + + + /// + /// Gets the permission tree for a customer role. + /// + /// Customer role. + /// Whether to add the permission display names. + /// Permission tree. + TreeNode GetPermissionTree(CustomerRole role, bool addDisplayNames = false); + + /// + /// Gets the permission tree for a customer. + /// + /// Customer. + /// Whether to add the permission display names. + /// Permission tree. + TreeNode GetPermissionTree(Customer customer, bool addDisplayNames = false); + + /// + /// Get display name for a permission system name. + /// + /// Permission record system name. + /// Display name. + string GetDiplayName(string permissionSystemName); + + /// + /// Get detailed unauthorization message. + /// + /// Permission record system name. + /// Detailed unauthorization message + string GetUnauthorizedMessage(string permissionSystemName); + } +} diff --git a/src/Libraries/SmartStore.Core/Security/PermissionAttribute.cs b/src/Libraries/SmartStore.Core/Security/PermissionAttribute.cs new file mode 100644 index 0000000000..b1e4b0399c --- /dev/null +++ b/src/Libraries/SmartStore.Core/Security/PermissionAttribute.cs @@ -0,0 +1,133 @@ +using System; +using System.Linq; +using System.Net; +using System.Text; +using System.Web.Mvc; + +namespace SmartStore.Core.Security +{ + /// + /// Checks request permission for the current customer. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] + public partial class PermissionAttribute : FilterAttribute, IAuthorizationFilter + { + /// + /// e.g. [Permission(PermissionSystemNames.Customer.Read)] + /// + /// The system name of the permission. + /// Whether to show an unauthorization message. + public PermissionAttribute( + string systemName, + bool showUnauthorizedMessage = true) + { + Guard.NotEmpty(systemName, nameof(systemName)); + + SystemName = systemName; + ShowUnauthorizedMessage = showUnauthorizedMessage; + } + + /// + /// The system name of the permission. + /// + public string SystemName { get; private set; } + + /// + /// Whether to show an unauthorization message. + /// + public bool ShowUnauthorizedMessage { get; private set; } + + public IWorkContext WorkContext { get; set; } + public IPermissionService PermissionService { get; set; } + + public virtual void OnAuthorization(AuthorizationContext filterContext) + { + Guard.NotNull(filterContext, nameof(filterContext)); + + if (PermissionService.Authorize(SystemName, WorkContext.CurrentCustomer)) + { + return; + } + + try + { + HandleUnauthorizedRequest(filterContext); + } + catch + { + filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Unauthorized); + } + } + + protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext) + { + var httpContext = filterContext.HttpContext; + var request = httpContext?.Request; + + if (request == null) + { + return; + } + + var message = ShowUnauthorizedMessage + ? PermissionService.GetUnauthorizedMessage(SystemName) + : string.Empty; + + if (request.IsAjaxRequest()) + { + if (message.HasValue()) + { + httpContext.Response.AddHeader("X-Message-Type", "error"); + httpContext.Response.AddHeader("X-Message", message); + } + + if (request.AcceptTypes?.Any(x => x.IsCaseInsensitiveEqual("text/html")) ?? false) + { + filterContext.Result = AccessDeniedResult(message); + } + else + { + filterContext.Result = new JsonResult + { + JsonRequestBehavior = JsonRequestBehavior.AllowGet, + Data = new + { + error = true, + success = false, + controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, + action = filterContext.ActionDescriptor.ActionName, + //message + } + }; + } + } + else + { + if (filterContext.IsChildAction) + { + filterContext.Result = AccessDeniedResult(message); + } + else + { + var urlHelper = new UrlHelper(request.RequestContext); + var url = urlHelper.Action("AccessDenied", "Security", new { pageUrl = request.RawUrl, area = "Admin" }); + + filterContext.Controller.TempData["UnauthorizedMessage"] = message; + filterContext.Result = new RedirectResult(url); + } + } + } + + protected virtual ActionResult AccessDeniedResult(string message) + { + var content = message.HasValue() ? $"
{message}
" : string.Empty; + + return new ContentResult + { + Content = content, + ContentType = "text/html", + ContentEncoding = Encoding.UTF8 + }; + } + } +} diff --git a/src/Libraries/SmartStore.Core/Security/PermissionHelper.cs b/src/Libraries/SmartStore.Core/Security/PermissionHelper.cs new file mode 100644 index 0000000000..7a7752ad63 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Security/PermissionHelper.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace SmartStore.Core.Security +{ + public static class PermissionHelper + { + /// + /// Gets a list of all permission system names for a given type. + /// See as an example. + /// + /// Permission system names. + public static IList GetPermissions(Type type) + { + Guard.NotNull(type, nameof(type)); + + var result = new List(); + GetPermissionsPerType(type); + + return result; + + void GetPermissionsPerType(Type permissionType) + { + var permissions = permissionType + .GetFields(BindingFlags.Public | BindingFlags.Static) + .Where(fi => fi.IsLiteral && !fi.IsInitOnly && fi.FieldType == typeof(string)) + .Select(x => (string)x.GetRawConstantValue()); + + result.AddRange(permissions); + + var nestedTypes = permissionType.GetNestedTypes(BindingFlags.Public | BindingFlags.Static); + nestedTypes.Each(x => GetPermissionsPerType(x)); + } + } + } +} diff --git a/src/Libraries/SmartStore.Core/Security/Permissions.cs b/src/Libraries/SmartStore.Core/Security/Permissions.cs new file mode 100644 index 0000000000..78fb4f8af2 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Security/Permissions.cs @@ -0,0 +1,517 @@ +namespace SmartStore.Core.Security +{ + /// + /// Provides system names of standard permissions. + /// Usage: [Permission(PermissionSystemNames.Customer.Read)] + /// + public static class Permissions + { + public static class Catalog + { + public const string Self = "catalog"; + public const string DisplayPrice = "catalog.displayprice"; + + public static class Product + { + public const string Self = "catalog.product"; + public const string Read = "catalog.product.read"; + public const string Update = "catalog.product.update"; + public const string Create = "catalog.product.create"; + public const string Delete = "catalog.product.delete"; + public const string EditCategory = "catalog.product.editcategory"; + public const string EditManufacturer = "catalog.product.editmanufacturer"; + public const string EditAssociatedProduct = "catalog.product.editassociatedproduct"; + public const string EditBundle = "catalog.product.editbundle"; + public const string EditAttribute = "catalog.product.editattribute"; + public const string EditVariant = "catalog.product.editvariant"; + public const string EditPromotion = "catalog.product.editpromotion"; + public const string EditPicture = "catalog.product.editpicture"; + public const string EditTag = "catalog.product.edittag"; + public const string EditTierPrice = "catalog.product.edittierprice"; + } + + public static class ProductReview + { + public const string Self = "catalog.productreview"; + public const string Read = "catalog.productreview.read"; + public const string Update = "catalog.productreview.update"; + public const string Create = "catalog.productreview.create"; + public const string Delete = "catalog.productreview.delete"; + public const string Approve = "catalog.productreview.Approve"; + } + + public static class Category + { + public const string Self = "catalog.category"; + public const string Read = "catalog.category.read"; + public const string Update = "catalog.category.update"; + public const string Create = "catalog.category.create"; + public const string Delete = "catalog.category.delete"; + public const string EditProduct = "catalog.category.editproduct"; + } + + public static class Manufacturer + { + public const string Self = "catalog.manufacturer"; + public const string Read = "catalog.manufacturer.read"; + public const string Update = "catalog.manufacturer.update"; + public const string Create = "catalog.manufacturer.create"; + public const string Delete = "catalog.manufacturer.delete"; + public const string EditProduct = "catalog.manufacturer.editproduct"; + } + + public static class Variant + { + public const string Self = "catalog.variant"; + public const string Read = "catalog.variant.read"; + public const string Update = "catalog.variant.update"; + public const string Create = "catalog.variant.create"; + public const string Delete = "catalog.variant.delete"; + public const string EditSet = "catalog.variant.editoptionset"; + } + + public static class Attribute + { + public const string Self = "catalog.attribute"; + public const string Read = "catalog.attribute.read"; + public const string Update = "catalog.attribute.update"; + public const string Create = "catalog.attribute.create"; + public const string Delete = "catalog.attribute.delete"; + public const string EditOption = "catalog.attribute.editoption"; + } + } + + public static class Customer + { + public const string Self = "customer"; + public const string Read = "customer.read"; + public const string Update = "customer.update"; + public const string Create = "customer.create"; + public const string Delete = "customer.delete"; + public const string Impersonate = "customer.impersonate"; + public const string EditAddress = "customer.editaddress"; + public const string EditRole = "customer.editcustomerrole"; + public const string SendPm = "customer.sendpm"; + + public static class Role + { + public const string Self = "customer.role"; + public const string Read = "customer.role.read"; + public const string Update = "customer.role.update"; + public const string Create = "customer.role.create"; + public const string Delete = "customer.role.delete"; + } + } + + public static class Order + { + public const string Self = "order"; + public const string Read = "order.read"; + public const string Update = "order.update"; + public const string Create = "order.create"; + public const string Delete = "order.delete"; + public const string EditItem = "order.editorderitem"; + public const string EditShipment = "order.editshipment"; + public const string EditRecurringPayment = "order.editrecurringpayment"; + + public static class GiftCard + { + public const string Self = "order.giftcard"; + public const string Read = "order.giftcard.read"; + public const string Update = "order.giftcard.update"; + public const string Create = "order.giftcard.create"; + public const string Delete = "order.giftcard.delete"; + public const string Notify = "order.giftcard.notify"; + } + + public static class ReturnRequest + { + public const string Self = "order.returnrequest"; + public const string Read = "order.returnrequest.read"; + public const string Update = "order.returnrequest.update"; + public const string Create = "order.returnrequest.create"; + public const string Delete = "order.returnrequest.delete"; + public const string Accept = "order.returnrequest.accept"; + } + } + + public static class Promotion + { + public const string Self = "promotion"; + + public static class Affiliate + { + public const string Self = "promotion.affiliate"; + public const string Read = "promotion.affiliate.read"; + public const string Update = "promotion.affiliate.update"; + public const string Create = "promotion.affiliate.create"; + public const string Delete = "promotion.affiliate.delete"; + } + + public static class Campaign + { + public const string Self = "promotion.campaign"; + public const string Read = "promotion.campaign.read"; + public const string Update = "promotion.campaign.update"; + public const string Create = "promotion.campaign.create"; + public const string Delete = "promotion.campaign.delete"; + } + + public static class Discount + { + public const string Self = "promotion.discount"; + public const string Read = "promotion.discount.read"; + public const string Update = "promotion.discount.update"; + public const string Create = "promotion.discount.create"; + public const string Delete = "promotion.discount.delete"; + } + + public static class Newsletter + { + public const string Self = "promotion.newsletter"; + public const string Read = "promotion.newsletter.read"; + public const string Update = "promotion.newsletter.update"; + public const string Delete = "promotion.newsletter.delete"; + } + } + + public static class Cms + { + public const string Self = "cms"; + + public static class Poll + { + public const string Self = "cms.poll"; + public const string Read = "cms.poll.read"; + public const string Update = "cms.poll.update"; + public const string Create = "cms.poll.create"; + public const string Delete = "cms.poll.delete"; + public const string EditAnswer = "cms.poll.editanswer"; + } + + public static class News + { + public const string Self = "cms.news"; + public const string Read = "cms.news.read"; + public const string Update = "cms.news.update"; + public const string Create = "cms.news.create"; + public const string Delete = "cms.news.delete"; + public const string EditComment = "cms.news.editcomment"; + } + + public static class Blog + { + public const string Self = "cms.blog"; + public const string Read = "cms.blog.read"; + public const string Update = "cms.blog.update"; + public const string Create = "cms.blog.create"; + public const string Delete = "cms.blog.delete"; + public const string EditComment = "cms.blog.editcomment"; + } + + public static class Widget + { + public const string Self = "cms.widget"; + public const string Read = "cms.widget.read"; + public const string Update = "cms.widget.update"; + public const string Activate = "cms.widget.activate"; + } + + public static class Topic + { + public const string Self = "cms.topic"; + public const string Read = "cms.topic.read"; + public const string Update = "cms.topic.update"; + public const string Create = "cms.topic.create"; + public const string Delete = "cms.topic.delete"; + } + + public static class Menu + { + public const string Self = "cms.menu"; + public const string Read = "cms.menu.read"; + public const string Update = "cms.menu.update"; + public const string Create = "cms.menu.create"; + public const string Delete = "cms.menu.delete"; + } + + public static class Forum + { + public const string Self = "cms.forum"; + public const string Read = "cms.forum.read"; + public const string Update = "cms.forum.update"; + public const string Create = "cms.forum.create"; + public const string Delete = "cms.forum.delete"; + } + + public static class MessageTemplate + { + public const string Self = "cms.messagetemplate"; + public const string Read = "cms.messagetemplate.read"; + public const string Update = "cms.messagetemplate.update"; + public const string Create = "cms.messagetemplate.create"; + public const string Delete = "cms.messagetemplate.delete"; + } + } + + public static class Configuration + { + public const string Self = "configuration"; + + public static class Country + { + public const string Self = "configuration.country"; + public const string Read = "configuration.country.read"; + public const string Update = "configuration.country.update"; + public const string Create = "configuration.country.create"; + public const string Delete = "configuration.country.delete"; + } + + public static class Language + { + public const string Self = "configuration.language"; + public const string Read = "configuration.language.read"; + public const string Update = "configuration.language.update"; + public const string Create = "configuration.language.create"; + public const string Delete = "configuration.language.delete"; + public const string EditResource = "configuration.language.editresource"; + } + + public static class Setting + { + public const string Self = "configuration.setting"; + public const string Read = "configuration.setting.read"; + public const string Update = "configuration.setting.update"; + public const string Create = "configuration.setting.create"; + public const string Delete = "configuration.setting.delete"; + } + + public static class PaymentMethod + { + public const string Self = "configuration.paymentmethod"; + public const string Read = "configuration.paymentmethod.read"; + public const string Update = "configuration.paymentmethod.update"; + public const string Activate = "configuration.paymentmethod.activate"; + } + + public static class Authentication + { + public const string Self = "configuration.authentication"; + public const string Read = "configuration.authentication.read"; + public const string Update = "configuration.authentication.update"; + public const string Activate = "configuration.authentication.activate"; + } + + public static class Currency + { + public const string Self = "configuration.currency"; + public const string Read = "configuration.currency.read"; + public const string Update = "configuration.currency.update"; + public const string Create = "configuration.currency.create"; + public const string Delete = "configuration.currency.delete"; + } + + public static class DeliveryTime + { + public const string Self = "configuration.deliverytime"; + public const string Read = "configuration.deliverytime.read"; + public const string Update = "configuration.deliverytime.update"; + public const string Create = "configuration.deliverytime.create"; + public const string Delete = "configuration.deliverytime.delete"; + } + + public static class Theme + { + public const string Self = "configuration.theme"; + public const string Read = "configuration.theme.read"; + public const string Update = "configuration.theme.update"; + public const string Upload = "configuration.theme.upload"; + } + + public static class Measure + { + public const string Self = "configuration.measure"; + public const string Read = "configuration.measure.read"; + public const string Update = "configuration.measure.update"; + public const string Create = "configuration.measure.create"; + public const string Delete = "configuration.measure.delete"; + } + + public static class ActivityLog + { + public const string Self = "configuration.activitylog"; + public const string Read = "configuration.activitylog.read"; + public const string Update = "configuration.activitylog.update"; + public const string Delete = "configuration.activitylog.delete"; + } + + public static class Acl + { + public const string Self = "configuration.acl"; + public const string Read = "configuration.acl.read"; + public const string Update = "configuration.acl.update"; + } + + public static class EmailAccount + { + public const string Self = "configuration.emailaccount"; + public const string Read = "configuration.emailaccount.read"; + public const string Update = "configuration.emailaccount.update"; + public const string Create = "configuration.emailaccount.create"; + public const string Delete = "configuration.emailaccount.delete"; + } + + public static class Store + { + public const string Self = "configuration.store"; + public const string Read = "configuration.store.read"; + public const string Update = "configuration.store.update"; + public const string Create = "configuration.store.create"; + public const string Delete = "configuration.store.delete"; + public const string ReadStats = "configuration.store.readstats"; + } + + public static class Shipping + { + public const string Self = "configuration.shipping"; + public const string Read = "configuration.shipping.read"; + public const string Update = "configuration.shipping.update"; + public const string Create = "configuration.shipping.create"; + public const string Delete = "configuration.shipping.delete"; + public const string Activate = "configuration.shipping.activate"; + } + + public static class Tax + { + public const string Self = "configuration.tax"; + public const string Read = "configuration.tax.read"; + public const string Update = "configuration.tax.update"; + public const string Create = "configuration.tax.create"; + public const string Delete = "configuration.tax.delete"; + public const string Activate = "configuration.tax.activate"; + } + + public static class Plugin + { + public const string Self = "configuration.plugin"; + public const string Read = "configuration.plugin.read"; + public const string Update = "configuration.plugin.update"; + public const string Upload = "configuration.plugin.upload"; + public const string Install = "configuration.plugin.install"; + public const string License = "configuration.plugin.license"; + } + + public static class Export + { + public const string Self = "configuration.export"; + public const string Read = "configuration.export.read"; + public const string Update = "configuration.export.update"; + public const string Create = "configuration.export.create"; + public const string Delete = "configuration.export.delete"; + public const string Execute = "configuration.export.execute"; + } + + public static class Import + { + public const string Self = "configuration.import"; + public const string Read = "configuration.import.read"; + public const string Update = "configuration.import.update"; + public const string Create = "configuration.import.create"; + public const string Delete = "configuration.import.delete"; + public const string Execute = "configuration.import.execute"; + } + } + + public static class System + { + public const string Self = "system"; + public const string AccessBackend = "system.accessbackend"; + public const string AccessShop = "system.accessshop"; + + public static class Log + { + public const string Self = "system.log"; + public const string Read = "system.log.read"; + public const string Delete = "system.log.delete"; + } + + public static class Rule + { + public const string Self = "system.rule"; + public const string Read = "system.rule.read"; + public const string Update = "system.rule.update"; + public const string Create = "system.rule.create"; + public const string Delete = "system.rule.delete"; + public const string Execute = "system.rule.execute"; + } + + public static class Message + { + public const string Self = "system.message"; + public const string Read = "system.message.read"; + public const string Update = "system.message.update"; + public const string Create = "system.message.create"; + public const string Delete = "system.message.delete"; + public const string Send = "system.message.send"; + } + + public static class Maintenance + { + public const string Self = "system.maintenance"; + public const string Read = "system.maintenance.read"; + public const string Execute = "system.maintenance.execute"; + } + + public static class ScheduleTask + { + public const string Self = "system.scheduletask"; + public const string Read = "system.scheduletask.read"; + public const string Update = "system.scheduletask.update"; + public const string Delete = "system.scheduletask.delete"; + public const string Execute = "system.scheduletask.execute"; + } + + public static class UrlRecord + { + public const string Self = "system.urlrecord"; + public const string Read = "system.urlrecord.read"; + public const string Update = "system.urlrecord.update"; + public const string Delete = "system.urlrecord.delete"; + } + } + + public static class Cart + { + public const string Self = "cart"; + public const string Read = "cart.read"; + public const string AccessShoppingCart = "cart.accessshoppingcart"; + public const string AccessWishlist = "cart.accesswishlist"; + + public static class CheckoutAttribute + { + public const string Self = "cart.checkoutattribute"; + public const string Read = "cart.checkoutattribute.read"; + public const string Update = "cart.checkoutattribute.update"; + public const string Create = "cart.checkoutattribute.create"; + public const string Delete = "cart.checkoutattribute.delete"; + } + } + + public static class Media + { + public const string Self = "media"; + public const string Update = "media.update"; + public const string Delete = "media.delete"; + public const string Upload = "media.upload"; + + public static class Download + { + public const string Self = "media.download"; + public const string Read = "media.download.read"; + public const string Update = "media.download.update"; + public const string Create = "media.download.create"; + public const string Delete = "media.download.delete"; + } + } + } +} diff --git a/src/Libraries/SmartStore.Core/Security/SmartStorePrincipal.cs b/src/Libraries/SmartStore.Core/Security/SmartStorePrincipal.cs index 991551ca7c..48d48e2d78 100644 --- a/src/Libraries/SmartStore.Core/Security/SmartStorePrincipal.cs +++ b/src/Libraries/SmartStore.Core/Security/SmartStorePrincipal.cs @@ -6,34 +6,33 @@ namespace SmartStore.Core { - - public class SmartStoreIdentity : ClaimsIdentity - { - [SecuritySafeCritical] - public SmartStoreIdentity(int customerId, string name, string type) - : base(new GenericIdentity(name, type)) - { - CustomerId = customerId; - } - - public int CustomerId { get; private set; } - - public override bool IsAuthenticated { get { return CustomerId != 0; } } - } - - - public class SmartStorePrincipal : IPrincipal - { - public SmartStorePrincipal(Customer customer, string type) - { - this.Identity = new SmartStoreIdentity(customer.Id, customer.Username, type); - } - - public bool IsInRole(string role) - { - return (Identity != null && Identity.IsAuthenticated && role.HasValue() && Roles.IsUserInRole(Identity.Name, role)); - } - - public IIdentity Identity { get; private set; } - } + public class SmartStoreIdentity : ClaimsIdentity + { + [SecuritySafeCritical] + public SmartStoreIdentity(int customerId, string name, string type) + : base(new GenericIdentity(name, type)) + { + CustomerId = customerId; + } + + public int CustomerId { get; private set; } + + public override bool IsAuthenticated => CustomerId != 0; + } + + + public class SmartStorePrincipal : IPrincipal + { + public SmartStorePrincipal(Customer customer, string type) + { + Identity = new SmartStoreIdentity(customer.Id, customer.Username.NullEmpty() ?? customer.Email, type); + } + + public bool IsInRole(string role) + { + return Identity != null && Identity.IsAuthenticated && role.HasValue() && Roles.IsUserInRole(Identity.Name, role); + } + + public IIdentity Identity { get; private set; } + } } diff --git a/src/Libraries/SmartStore.Core/SmartException.cs b/src/Libraries/SmartStore.Core/SmartException.cs index 0b620fa609..d3137ece3f 100644 --- a/src/Libraries/SmartStore.Core/SmartException.cs +++ b/src/Libraries/SmartStore.Core/SmartException.cs @@ -31,9 +31,9 @@ public SmartException(string message) /// The exception message format. /// The exception message arguments. public SmartException(string messageFormat, params object[] args) - : base(string.Format(messageFormat, args)) - { - } + : base(string.Format(messageFormat, args)) + { + } /// /// Initializes a new instance of the Exception class with serialized data. diff --git a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj index 6bc9fc51ad..cd84ae4268 100644 --- a/src/Libraries/SmartStore.Core/SmartStore.Core.csproj +++ b/src/Libraries/SmartStore.Core/SmartStore.Core.csproj @@ -1,6 +1,5 @@  - Debug AnyCPU @@ -11,7 +10,7 @@ Properties SmartStore.Core SmartStore.Core - v4.6.1 + v4.7.2 512 @@ -66,19 +65,18 @@ ..\..\packages\AngleSharp.0.9.11\lib\net45\AngleSharp.dll - - ..\..\packages\Autofac.4.9.0\lib\net45\Autofac.dll + + ..\..\packages\Autofac.5.2.0\lib\net461\Autofac.dll - - ..\..\packages\Autofac.Mvc5.4.0.2\lib\net45\Autofac.Integration.Mvc.dll - True + + ..\..\packages\Autofac.Mvc5.5.0.0\lib\net461\Autofac.Integration.Mvc.dll - ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + ..\..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll True - ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + ..\..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.SqlServer.dll True @@ -88,6 +86,9 @@ ..\..\packages\log4net.2.0.8\lib\net45-full\log4net.dll True + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + True ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll @@ -95,8 +96,8 @@ ..\..\packages\Microsoft.Web.Xdt.3.0.0\lib\net40\Microsoft.Web.XmlTransform.dll - - ..\..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll ..\..\packages\NuGet.Core.2.14.0\lib\net40-Client\NuGet.Core.dll @@ -112,16 +113,18 @@ True - - ..\..\packages\System.Linq.Dynamic.1.0.7\lib\net40\System.Linq.Dynamic.dll - True + + ..\..\packages\System.Linq.Dynamic.Core.1.2.1\lib\net46\System.Linq.Dynamic.Core.dll + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + - - ..\..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll @@ -180,9 +183,14 @@ + + + + + @@ -192,8 +200,7 @@ - - + @@ -201,16 +208,17 @@ - - + + + @@ -218,12 +226,15 @@ + + + @@ -240,15 +251,22 @@ + + + + + + + @@ -264,9 +282,11 @@ + + @@ -280,7 +300,9 @@ + + @@ -295,6 +317,7 @@ + @@ -305,6 +328,7 @@ + @@ -454,6 +478,7 @@ + @@ -461,6 +486,11 @@ + + + + + @@ -468,16 +498,13 @@ - - - @@ -519,6 +546,11 @@ + + + + + @@ -546,7 +578,7 @@ - + @@ -573,7 +605,7 @@ - + @@ -601,7 +633,6 @@ - @@ -627,7 +658,6 @@ - @@ -668,7 +698,7 @@ - + @@ -690,12 +720,12 @@ - + - + @@ -722,6 +752,10 @@ + + + + @@ -759,12 +793,7 @@ - - - - - - + diff --git a/src/Libraries/SmartStore.Core/SmartStoreVersion.cs b/src/Libraries/SmartStore.Core/SmartStoreVersion.cs index e5bc5e5e48..b34a1f8625 100644 --- a/src/Libraries/SmartStore.Core/SmartStoreVersion.cs +++ b/src/Libraries/SmartStore.Core/SmartStoreVersion.cs @@ -1,35 +1,34 @@ using System; -using System.Linq; using System.Collections.Generic; +using System.Linq; using System.Reflection; -using SmartStore; namespace SmartStore.Core { - public class HelpTopic - { - public static HelpTopic CronExpressions = new HelpTopic("cron", "Managing+Scheduled+Tasks#ManagingScheduledTasks-Cron", "Geplante+Aufgaben+verwalten#GeplanteAufgabenverwalten-CronAusdruck"); - - public HelpTopic(string name, string enPath, string dePath) - { - Guard.NotEmpty(name, nameof(name)); - Guard.NotEmpty(enPath, nameof(enPath)); - Guard.NotEmpty(dePath, nameof(dePath)); - - Name = name; - EnPath = enPath; - DePath = dePath; - } - - public string Name { get; private set; } - public string EnPath { get; private set; } - public string DePath { get; private set; } - } - - public static class SmartStoreVersion + public class HelpTopic + { + public static HelpTopic CronExpressions = new HelpTopic("cron", "Managing+Scheduled+Tasks#ManagingScheduledTasks-Cron", "Geplante+Aufgaben+verwalten#GeplanteAufgabenverwalten-CronAusdruck"); + + public HelpTopic(string name, string enPath, string dePath) + { + Guard.NotEmpty(name, nameof(name)); + Guard.NotEmpty(enPath, nameof(enPath)); + Guard.NotEmpty(dePath, nameof(dePath)); + + Name = name; + EnPath = enPath; + DePath = dePath; + } + + public string Name { get; private set; } + public string EnPath { get; private set; } + public string DePath { get; private set; } + } + + public static class SmartStoreVersion { private static readonly Version s_infoVersion = new Version("1.0.0.0"); - private static readonly List s_breakingChangesHistory = new List + private static readonly List s_breakingChangesHistory = new List { // IMPORTANT: Add app versions from low to high // NOTE: do not specify build & revision unless you have good reasons for it. @@ -38,20 +37,25 @@ public static class SmartStoreVersion new Version("1.2"), new Version("1.2.1"), new Version("2.0"), - new Version("2.1"), - new Version("2.2"), + new Version("2.1"), + new Version("2.2"), new Version("2.5"), - new Version("3.0"), - new Version("3.1"), - new Version("3.1.5"), + new Version("3.0"), + new Version("3.1"), + new Version("3.1.5"), new Version("3.2"), new Version("3.2.1"), - new Version("3.2.2") + new Version("3.2.2"), + new Version("4.0.0"), + new Version("4.0.1"), + new Version("4.1.0"), + new Version("4.1.1"), + new Version("4.2.0") }; - private const string HELP_BASEURL = "https://docs.smartstore.com/display/"; + private const string HELP_BASEURL = "https://docs.smartstore.com/display/"; - static SmartStoreVersion() + static SmartStoreVersion() { s_breakingChangesHistory.Reverse(); @@ -60,80 +64,56 @@ static SmartStoreVersion() if (infoVersionAttr != null) { s_infoVersion = new Version(infoVersionAttr.InformationalVersion); - } + } } /// /// Gets the app version /// - public static string CurrentVersion - { - get - { - return "{0}.{1}".FormatInvariant(s_infoVersion.Major, s_infoVersion.Minor); - } - } + public static string CurrentVersion => "{0}.{1}".FormatInvariant(s_infoVersion.Major, s_infoVersion.Minor); /// /// Gets the app full version /// - public static string CurrentFullVersion + public static string CurrentFullVersion => s_infoVersion.ToString(); + + public static Version Version => s_infoVersion; + + public static string GenerateHelpUrl(string languageCode, HelpTopic topic) { - get - { - return s_infoVersion.ToString(); - } + Guard.NotEmpty(languageCode, nameof(languageCode)); + Guard.NotNull(topic, nameof(topic)); + + var path = languageCode.IsCaseInsensitiveEqual("de") ? topic.DePath : topic.EnPath; + return GenerateHelpUrl(languageCode, path); } - public static Version Version + public static string GenerateHelpUrl(string languageCode, string path) { - get - { - return s_infoVersion; - } + Guard.NotEmpty(languageCode, nameof(languageCode)); + + return String.Concat( + HELP_BASEURL, + GetUserGuideSpaceKey(languageCode), + "/", + path.EmptyNull().Trim().TrimStart('/', '\\')); } - public static string GenerateHelpUrl(string languageCode, HelpTopic topic) - { - Guard.NotEmpty(languageCode, nameof(languageCode)); - Guard.NotNull(topic, nameof(topic)); - - var path = languageCode.IsCaseInsensitiveEqual("de") ? topic.DePath : topic.EnPath; - return GenerateHelpUrl(languageCode, path); - } - - public static string GenerateHelpUrl(string languageCode, string path) - { - Guard.NotEmpty(languageCode, nameof(languageCode)); - - return String.Concat( - HELP_BASEURL, - GetUserGuideSpaceKey(languageCode), - "/", - path.EmptyNull().Trim().TrimStart('/', '\\')); - } - - public static string GetUserGuideSpaceKey(string languageCode) - { - return languageCode.IsCaseInsensitiveEqual("de") - ? "SDDE32" - : "SMNET32"; - } + public static string GetUserGuideSpaceKey(string languageCode) + { + return languageCode.IsCaseInsensitiveEqual("de") + ? "SDDE40" + : "SMNET40"; + } /// - /// Gets a list of SmartStore.NET versions in which breaking changes occured, + /// Gets a list of Smartstore versions in which breaking changes occured, /// which could lead to plugins malfunctioning. /// /// /// A plugin's MinAppVersion is checked against this list to assume /// it's compatibility with the current app version. /// - internal static IEnumerable BreakingChangesHistory - { - get - { - return s_breakingChangesHistory.AsEnumerable(); - } - } + internal static IEnumerable BreakingChangesHistory => s_breakingChangesHistory.AsEnumerable(); } } diff --git a/src/Libraries/SmartStore.Core/Templating/DefaultTemplateManager.cs b/src/Libraries/SmartStore.Core/Templating/DefaultTemplateManager.cs index 8395d04364..482d2ea87c 100644 --- a/src/Libraries/SmartStore.Core/Templating/DefaultTemplateManager.cs +++ b/src/Libraries/SmartStore.Core/Templating/DefaultTemplateManager.cs @@ -4,66 +4,66 @@ namespace SmartStore.Templating { - public partial class DefaultTemplateManager : ITemplateManager - { - private readonly ConcurrentDictionary _templates; - private readonly ITemplateEngine _engine; + public partial class DefaultTemplateManager : ITemplateManager + { + private readonly ConcurrentDictionary _templates; + private readonly ITemplateEngine _engine; - public DefaultTemplateManager(ITemplateEngine engine) - { - _templates = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - _engine = engine; - } + public DefaultTemplateManager(ITemplateEngine engine) + { + _templates = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + _engine = engine; + } - public IReadOnlyDictionary All() - { - return _templates; - } + public IReadOnlyDictionary All() + { + return _templates; + } - public bool Contains(string name) - { - Guard.NotEmpty(name, nameof(name)); + public bool Contains(string name) + { + Guard.NotEmpty(name, nameof(name)); - return _templates.ContainsKey(name); - } + return _templates.ContainsKey(name); + } - public ITemplate Get(string name) - { - Guard.NotEmpty(name, nameof(name)); + public ITemplate Get(string name) + { + Guard.NotEmpty(name, nameof(name)); - _templates.TryGetValue(name, out var template); - return template; - } + _templates.TryGetValue(name, out var template); + return template; + } - public void Put(string name, ITemplate template) - { - Guard.NotEmpty(name, nameof(name)); - Guard.NotNull(template, nameof(template)); + public void Put(string name, ITemplate template) + { + Guard.NotEmpty(name, nameof(name)); + Guard.NotNull(template, nameof(template)); - _templates[name] = template; - } + _templates[name] = template; + } - public ITemplate GetOrAdd(string name, Func sourceFactory) - { - Guard.NotEmpty(name, nameof(name)); - Guard.NotNull(sourceFactory, nameof(sourceFactory)); + public ITemplate GetOrAdd(string name, Func sourceFactory) + { + Guard.NotEmpty(name, nameof(name)); + Guard.NotNull(sourceFactory, nameof(sourceFactory)); - return _templates.GetOrAdd(name, key => - { - return _engine.Compile(sourceFactory()); - }); - } + return _templates.GetOrAdd(name, key => + { + return _engine.Compile(sourceFactory()); + }); + } - public bool TryRemove(string name, out ITemplate template) - { - Guard.NotEmpty(name, nameof(name)); + public bool TryRemove(string name, out ITemplate template) + { + Guard.NotEmpty(name, nameof(name)); - return _templates.TryRemove(name, out template); - } + return _templates.TryRemove(name, out template); + } - public void Clear() - { - _templates.Clear(); - } - } + public void Clear() + { + _templates.Clear(); + } + } } diff --git a/src/Libraries/SmartStore.Core/Templating/ITemplate.cs b/src/Libraries/SmartStore.Core/Templating/ITemplate.cs index 3014ba0e20..c1a796f060 100644 --- a/src/Libraries/SmartStore.Core/Templating/ITemplate.cs +++ b/src/Libraries/SmartStore.Core/Templating/ITemplate.cs @@ -2,26 +2,26 @@ namespace SmartStore.Templating { - /// - /// A compiled template intended to be stored in a singleton storage. - /// - public interface ITemplate - { - /// - /// Gets the original template source - /// - string Source { get; } + /// + /// A compiled template intended to be stored in a singleton storage. + /// + public interface ITemplate + { + /// + /// Gets the original template source + /// + string Source { get; } - /// - /// Renders the template in - /// - /// - /// The model object which contains the data for the template. - /// Can be a subclass of , - /// a plain class object, or an anonymous type. - /// - /// Provider to use for formatting numbers, dates, money etc. - /// The processed template result - string Render(object model, IFormatProvider formatProvider); - } + /// + /// Renders the template in + /// + /// + /// The model object which contains the data for the template. + /// Can be a subclass of , + /// a plain class object, or an anonymous type. + /// + /// Provider to use for formatting numbers, dates, money etc. + /// The processed template result + string Render(object model, IFormatProvider formatProvider); + } } diff --git a/src/Libraries/SmartStore.Core/Templating/ITemplateEngine.cs b/src/Libraries/SmartStore.Core/Templating/ITemplateEngine.cs index 56e5868375..c6f6092cbb 100644 --- a/src/Libraries/SmartStore.Core/Templating/ITemplateEngine.cs +++ b/src/Libraries/SmartStore.Core/Templating/ITemplateEngine.cs @@ -4,50 +4,50 @@ namespace SmartStore.Templating { - /// - /// Represents a model object for testing purposes. A test model - /// becomes necessary when the database does not contain any data - /// for the previews/tested model. - /// - public interface ITestModel - { - /// - /// The name of the model to use as hash key, e.g. Product, Order, Customer etc. - /// - string ModelName { get; } - } + /// + /// Represents a model object for testing purposes. A test model + /// becomes necessary when the database does not contain any data + /// for the previews/tested model. + /// + public interface ITestModel + { + /// + /// The name of the model to use as hash key, e.g. Product, Order, Customer etc. + /// + string ModelName { get; } + } - /// - /// Responsible for compiling and rendering the templates - /// - public interface ITemplateEngine - { - /// - /// Compiles a template for faster rendering - /// - /// The template source - /// Compiled template - ITemplate Compile(string source); + /// + /// Responsible for compiling and rendering the templates + /// + public interface ITemplateEngine + { + /// + /// Compiles a template for faster rendering + /// + /// The template source + /// Compiled template + ITemplate Compile(string source); - /// - /// Directly renders a template source - /// - /// The template source - /// - /// The model object which contains the data for the template. - /// Can be a subclass of , - /// a plain class object, or an anonymous type. - /// - /// Provider to use for formatting numbers, dates, money etc. - /// The processed template result - string Render(string source, object model, IFormatProvider formatProvider); + /// + /// Directly renders a template source + /// + /// The template source + /// + /// The model object which contains the data for the template. + /// Can be a subclass of , + /// a plain class object, or an anonymous type. + /// + /// Provider to use for formatting numbers, dates, money etc. + /// The processed template result + string Render(string source, object model, IFormatProvider formatProvider); - /// - /// Creates a test model for the passed entity to be used during preview and test. - /// - /// The entity to create a test model for. - /// The model prefix - /// An object which implements and contains some test data for the declared properties of - ITestModel CreateTestModelFor(BaseEntity entity, string modelPrefix); - } + /// + /// Creates a test model for the passed entity to be used during preview and test. + /// + /// The entity to create a test model for. + /// The model prefix + /// An object which implements and contains some test data for the declared properties of + ITestModel CreateTestModelFor(BaseEntity entity, string modelPrefix); + } } diff --git a/src/Libraries/SmartStore.Core/Templating/ITemplateManager.cs b/src/Libraries/SmartStore.Core/Templating/ITemplateManager.cs index affe05aeb1..80e75d44c2 100644 --- a/src/Libraries/SmartStore.Core/Templating/ITemplateManager.cs +++ b/src/Libraries/SmartStore.Core/Templating/ITemplateManager.cs @@ -3,58 +3,58 @@ namespace SmartStore.Templating { - /// - /// Responsible for managing all compiled templates - /// - public interface ITemplateManager - { - /// - /// Gets all compiled templates - /// - /// - IReadOnlyDictionary All(); + /// + /// Responsible for managing all compiled templates + /// + public interface ITemplateManager + { + /// + /// Gets all compiled templates + /// + /// + IReadOnlyDictionary All(); - /// - /// Checks whether a compiled template exists in the storage - /// - /// Template name - /// true when the template exists - bool Contains(string name); + /// + /// Checks whether a compiled template exists in the storage + /// + /// Template name + /// true when the template exists + bool Contains(string name); - /// - /// Gets a compiled template matching the passed name - /// - /// Template name to get. - /// An instance of or null - ITemplate Get(string name); + /// + /// Gets a compiled template matching the passed name + /// + /// Template name to get. + /// An instance of or null + ITemplate Get(string name); - /// - /// Saves a compiled template in the storage. Any existing template gets overridden. - /// - /// The name to use as template key - /// The compiled template - void Put(string name, ITemplate template); + /// + /// Saves a compiled template in the storage. Any existing template gets overridden. + /// + /// The name to use as template key + /// The compiled template + void Put(string name, ITemplate template); - /// - /// Either gets a template with the passed name, or compiles and stores - /// a template if it does not exist yet. - /// - /// Template name - /// The factory used to generate/obtain the template source. - /// The compiled template - ITemplate GetOrAdd(string name, Func sourceFactory); + /// + /// Either gets a template with the passed name, or compiles and stores + /// a template if it does not exist yet. + /// + /// Template name + /// The factory used to generate/obtain the template source. + /// The compiled template + ITemplate GetOrAdd(string name, Func sourceFactory); - /// - /// Attempts to remove and return the compiled template that has the specified name. - /// - /// The name of the template to remove and return. - /// The removed template or null - /// true if the template was removed, false otherwise. - bool TryRemove(string name, out ITemplate template); + /// + /// Attempts to remove and return the compiled template that has the specified name. + /// + /// The name of the template to remove and return. + /// The removed template or null + /// true if the template was removed, false otherwise. + bool TryRemove(string name, out ITemplate template); - /// - /// Removes all compiled templates from the storage. - /// - void Clear(); - } + /// + /// Removes all compiled templates from the storage. + /// + void Clear(); + } } diff --git a/src/Libraries/SmartStore.Core/Templating/NullTemplateEngine.cs b/src/Libraries/SmartStore.Core/Templating/NullTemplateEngine.cs index 04f166dbf9..ec3a7d99b9 100644 --- a/src/Libraries/SmartStore.Core/Templating/NullTemplateEngine.cs +++ b/src/Libraries/SmartStore.Core/Templating/NullTemplateEngine.cs @@ -3,47 +3,47 @@ namespace SmartStore.Templating { - public class NullTemplateEngine : ITemplateEngine - { - private readonly static ITemplateEngine _instance = new NullTemplateEngine(); - - public static ITemplateEngine Instance => _instance; - - public ITemplate Compile(string template) - { - return new NullTemplate(template); - } - - public string Render(string source, object data, IFormatProvider formatProvider) - { - return source; - } - - public ITestModel CreateTestModelFor(BaseEntity entity, string modelPrefix) - { - return new NullTestModel(); - } - - internal class NullTestModel : ITestModel - { - public string ModelName => "TestModel"; - } - - internal class NullTemplate : ITemplate - { - private readonly string _source; - - public NullTemplate(string source) - { - _source = source; - } - - public string Source => _source; - - public string Render(object data, IFormatProvider formatProvider) - { - return _source; - } - } - } + public class NullTemplateEngine : ITemplateEngine + { + private readonly static ITemplateEngine _instance = new NullTemplateEngine(); + + public static ITemplateEngine Instance => _instance; + + public ITemplate Compile(string template) + { + return new NullTemplate(template); + } + + public string Render(string source, object data, IFormatProvider formatProvider) + { + return source; + } + + public ITestModel CreateTestModelFor(BaseEntity entity, string modelPrefix) + { + return new NullTestModel(); + } + + internal class NullTestModel : ITestModel + { + public string ModelName => "TestModel"; + } + + internal class NullTemplate : ITemplate + { + private readonly string _source; + + public NullTemplate(string source) + { + _source = source; + } + + public string Source => _source; + + public string Render(object data, IFormatProvider formatProvider) + { + return _source; + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Themes/DefaultThemeRegistry.cs b/src/Libraries/SmartStore.Core/Themes/DefaultThemeRegistry.cs index 0c90853524..0c9aaa55e5 100644 --- a/src/Libraries/SmartStore.Core/Themes/DefaultThemeRegistry.cs +++ b/src/Libraries/SmartStore.Core/Themes/DefaultThemeRegistry.cs @@ -8,7 +8,6 @@ using System.Threading; using SmartStore.Collections; using SmartStore.Core.Events; -using SmartStore.Core.Infrastructure.DependencyManagement; using SmartStore.Core.Logging; using SmartStore.Utilities; @@ -16,535 +15,519 @@ namespace SmartStore.Core.Themes { public partial class DefaultThemeRegistry : DisposableObject, IThemeRegistry { - private readonly bool _enableMonitoring; - private readonly string _themesBasePath; - private readonly IEventPublisher _eventPublisher; - private readonly IApplicationEnvironment _env; - private readonly ConcurrentDictionary _themes = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); - private readonly ConcurrentDictionary _eventQueue = new ConcurrentDictionary(); + private readonly bool _enableMonitoring; + private readonly string _themesBasePath; + private readonly IEventPublisher _eventPublisher; + private readonly IApplicationEnvironment _env; + private readonly ConcurrentDictionary _themes = new ConcurrentDictionary(StringComparer.InvariantCultureIgnoreCase); + private readonly ConcurrentDictionary _eventQueue = new ConcurrentDictionary(); - private readonly Regex _fileFilterPattern = new Regex(@"^\.(config|png|gif|jpg|jpeg|css|less|js|cshtml|svg|json)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private readonly Regex _fileFilterPattern = new Regex(@"^\.(config|png|gif|jpg|jpeg|css|scss|js|cshtml|svg|json)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); - private FileSystemWatcher _monitorFolders; - private FileSystemWatcher _monitorFiles; + private FileSystemWatcher _monitorFolders; + private FileSystemWatcher _monitorFiles; - public DefaultThemeRegistry(IEventPublisher eventPublisher, IApplicationEnvironment env, bool? enableMonitoring, string themesBasePath, bool autoLoadThemes) + public DefaultThemeRegistry(IEventPublisher eventPublisher, IApplicationEnvironment env, bool? enableMonitoring, string themesBasePath, bool autoLoadThemes) { - _enableMonitoring = enableMonitoring ?? CommonHelper.GetAppSetting("sm:MonitorThemesFolder", true); - _eventPublisher = eventPublisher; - _env = env; - _themesBasePath = themesBasePath.NullEmpty() ?? _env.ThemesFolder.RootPath; + _enableMonitoring = enableMonitoring ?? CommonHelper.GetAppSetting("sm:MonitorThemesFolder", true); + _eventPublisher = eventPublisher; + _env = env; + _themesBasePath = themesBasePath.NullEmpty() ?? _env.ThemesFolder.RootPath; - Logger = NullLogger.Instance; + Logger = NullLogger.Instance; - if (autoLoadThemes) - { - // load all themes initially - ReloadThemes(); - } + if (autoLoadThemes) + { + // load all themes initially + ReloadThemes(); + } - CreateFileSystemWatchers(); + CreateFileSystemWatchers(); - // start FS watcher - this.StartMonitoring(false); + // start FS watcher + this.StartMonitoring(false); } - public ILogger Logger { get; set; } + public ILogger Logger { get; set; } - #region IThemeRegistry + #region IThemeRegistry - public bool ThemeManifestExists(string themeName) + public bool ThemeManifestExists(string themeName) { if (themeName.IsEmpty()) return false; - ThemeManifest manifest; - if (_themes.TryGetValue(themeName, out manifest)) - { - return manifest.State == ThemeManifestState.Active; - } + ThemeManifest manifest; + if (_themes.TryGetValue(themeName, out manifest)) + { + return manifest.State == ThemeManifestState.Active; + } - return false; + return false; } - public ThemeManifest GetThemeManifest(string themeName) - { - ThemeManifest value; - if (themeName.HasValue() && _themes.TryGetValue(themeName, out value)) - { - return value; - } - return null; - } - - public ICollection GetThemeManifests(bool includeHidden = false) - { - var allThemes = _themes.Values; - - if (includeHidden) - { - return allThemes.AsReadOnly(); - } - else - { - return allThemes.Where(x => x.State == ThemeManifestState.Active).AsReadOnly(); - } - } - - public void AddThemeManifest(ThemeManifest manifest) - { - AddThemeManifestInternal(manifest, false); - } - - private void AddThemeManifestInternal(ThemeManifest manifest, bool isInit) - { - Guard.NotNull(manifest, nameof(manifest)); - - if (!isInit) - { - TryRemoveManifest(manifest.ThemeName); - } - - ThemeManifest baseManifest = null; - if (manifest.BaseThemeName != null) - { - if (!_themes.TryGetValue(manifest.BaseThemeName, out baseManifest)) - { - manifest.State = ThemeManifestState.MissingBaseTheme; - } - } - - manifest.BaseTheme = baseManifest; - var added = _themes.TryAdd(manifest.ThemeName, manifest); - if (added && !isInit) - { - // post process - var children = GetChildrenOf(manifest.ThemeName, false); - foreach (var child in children) - { - child.BaseTheme = manifest; - child.State = ThemeManifestState.Active; - } - } - } - - [SuppressMessage("ReSharper", "AssignmentInConditionalExpression")] - private bool TryRemoveManifest(string themeName) - { - bool result; - ThemeManifest existing; - if (result = _themes.TryRemove(themeName, out existing)) - { - _eventPublisher.Publish(new ThemeTouchedEvent(themeName)); - - existing.BaseTheme = null; - - // set all direct children as broken - var children = GetChildrenOf(themeName, false); - foreach (var child in children) - { - child.BaseTheme = null; - child.State = ThemeManifestState.MissingBaseTheme; - _eventPublisher.Publish(new ThemeTouchedEvent(child.ThemeName)); - } - } - - return result; - } - - public bool IsChildThemeOf(string themeName, string baseTheme) - { - if (themeName.IsEmpty() && baseTheme.IsEmpty()) - { - return false; - } - - if (themeName.Equals(baseTheme, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - var current = GetThemeManifest(themeName); - if (current == null) - return false; - - while (current.BaseThemeName != null) - { - if (baseTheme.Equals(current.BaseThemeName, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - if (!_themes.TryGetValue(current.BaseThemeName, out current)) - { - return false; - } - //currentBaseName = current.BaseThemeName; - } - - return false; - } - - public IEnumerable GetChildrenOf(string themeName, bool deep = true) - { - Guard.NotEmpty(themeName, nameof(themeName)); - - if (!ThemeManifestExists(themeName)) - Enumerable.Empty(); - - var derivedThemes = _themes.Values.Where(x => x.BaseThemeName != null && !x.ThemeName.IsCaseInsensitiveEqual(themeName)); - if (!deep) - { - derivedThemes = derivedThemes.Where(x => x.BaseThemeName.IsCaseInsensitiveEqual(themeName)); - } - else - { - derivedThemes = derivedThemes.Where(x => IsChildThemeOf(x.ThemeName, themeName)); - } - - return derivedThemes; - } - - public void ReloadThemes() - { - _themes.Clear(); - - var folder = _env.ThemesFolder; - var folderDatas = new List(); - var dirs = folder.ListDirectories(""); - - // create folder (meta)datas first - foreach (var path in dirs) - { - try - { - var folderData = ThemeManifest.CreateThemeFolderData(folder.MapPath(path), _themesBasePath); - if (folderData != null) - { - folderDatas.Add(folderData); - } - } - catch (Exception ex) - { - Logger.Error(ex, "Unable to create folder data for folder '{0}'".FormatCurrent(path)); - } - } - - // perform topological sort (BaseThemes first...) - IEnumerable sortedThemeFolders; - try - { - sortedThemeFolders = folderDatas.ToArray().SortTopological(StringComparer.OrdinalIgnoreCase).Cast(); - } - catch (CyclicDependencyException) - { - var ex = new CyclicDependencyException("Cyclic theme dependencies detected. Please check the 'baseTheme' attribute of your themes and ensure that they do not reference themselves (in)directly."); - Logger.Error(ex); - throw ex; - } - catch - { - throw; - } - - // create theme manifests - foreach (var themeFolder in sortedThemeFolders) - { - try - { - var manifest = ThemeManifest.Create(themeFolder); - if (manifest != null) - { - AddThemeManifestInternal(manifest, true); - } - } - catch (Exception ex) - { - Logger.Error(ex, "Unable to create manifest for theme '{0}'".FormatCurrent(themeFolder.FolderName)); - } - } - } + public ThemeManifest GetThemeManifest(string themeName) + { + ThemeManifest value; + if (themeName.HasValue() && _themes.TryGetValue(themeName, out value)) + { + return value; + } + return null; + } + + public ICollection GetThemeManifests(bool includeHidden = false) + { + var allThemes = _themes.Values; + + if (includeHidden) + { + return allThemes.AsReadOnly(); + } + else + { + return allThemes.Where(x => x.State == ThemeManifestState.Active).AsReadOnly(); + } + } + + public void AddThemeManifest(ThemeManifest manifest) + { + AddThemeManifestInternal(manifest, false); + } + + private void AddThemeManifestInternal(ThemeManifest manifest, bool isInit) + { + Guard.NotNull(manifest, nameof(manifest)); + + if (!isInit) + { + TryRemoveManifest(manifest.ThemeName); + } + + ThemeManifest baseManifest = null; + if (manifest.BaseThemeName != null) + { + if (!_themes.TryGetValue(manifest.BaseThemeName, out baseManifest)) + { + manifest.State = ThemeManifestState.MissingBaseTheme; + } + } + + manifest.BaseTheme = baseManifest; + var added = _themes.TryAdd(manifest.ThemeName, manifest); + if (added && !isInit) + { + // post process + var children = GetChildrenOf(manifest.ThemeName, false); + foreach (var child in children) + { + child.BaseTheme = manifest; + child.State = ThemeManifestState.Active; + } + } + } + + [SuppressMessage("ReSharper", "AssignmentInConditionalExpression")] + private bool TryRemoveManifest(string themeName) + { + bool result; + ThemeManifest existing; + if (result = _themes.TryRemove(themeName, out existing)) + { + _eventPublisher.Publish(new ThemeTouchedEvent(themeName)); + + existing.BaseTheme = null; + + // set all direct children as broken + var children = GetChildrenOf(themeName, false); + foreach (var child in children) + { + child.BaseTheme = null; + child.State = ThemeManifestState.MissingBaseTheme; + _eventPublisher.Publish(new ThemeTouchedEvent(child.ThemeName)); + } + } + + return result; + } + + public bool IsChildThemeOf(string themeName, string baseTheme) + { + if (themeName.IsEmpty() && baseTheme.IsEmpty()) + { + return false; + } + + if (themeName.Equals(baseTheme, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + var current = GetThemeManifest(themeName); + if (current == null) + return false; + + while (current.BaseThemeName != null) + { + if (baseTheme.Equals(current.BaseThemeName, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + if (!_themes.TryGetValue(current.BaseThemeName, out current)) + { + return false; + } + //currentBaseName = current.BaseThemeName; + } + + return false; + } + + public IEnumerable GetChildrenOf(string themeName, bool deep = true) + { + Guard.NotEmpty(themeName, nameof(themeName)); + + if (!ThemeManifestExists(themeName)) + return Enumerable.Empty(); + + var derivedThemes = _themes.Values.Where(x => x.BaseThemeName != null && !x.ThemeName.IsCaseInsensitiveEqual(themeName)); + if (!deep) + { + derivedThemes = derivedThemes.Where(x => x.BaseThemeName.IsCaseInsensitiveEqual(themeName)); + } + else + { + derivedThemes = derivedThemes.Where(x => IsChildThemeOf(x.ThemeName, themeName)); + } + + return derivedThemes; + } + + public void ReloadThemes() + { + _themes.Clear(); + + var folder = _env.ThemesFolder; + var folderDatas = new List(); + var dirs = folder.ListDirectories(""); + + // create folder (meta)datas first + foreach (var path in dirs) + { + try + { + var folderData = ThemeManifest.CreateThemeFolderData(folder.MapPath(path), _themesBasePath); + if (folderData != null) + { + folderDatas.Add(folderData); + } + } + catch (Exception ex) + { + Logger.Error(ex, "Unable to create folder data for folder '{0}'".FormatCurrent(path)); + } + } + + // perform topological sort (BaseThemes first...) + IEnumerable sortedThemeFolders; + try + { + sortedThemeFolders = folderDatas.ToArray().SortTopological(StringComparer.OrdinalIgnoreCase).Cast(); + } + catch (CyclicDependencyException) + { + var ex = new CyclicDependencyException("Cyclic theme dependencies detected. Please check the 'baseTheme' attribute of your themes and ensure that they do not reference themselves (in)directly."); + Logger.Error(ex); + throw ex; + } + catch + { + throw; + } + + // create theme manifests + foreach (var themeFolder in sortedThemeFolders) + { + try + { + var manifest = ThemeManifest.Create(themeFolder); + if (manifest != null) + { + AddThemeManifestInternal(manifest, true); + } + } + catch (Exception ex) + { + Logger.Error(ex, "Unable to create manifest for theme '{0}'".FormatCurrent(themeFolder.FolderName)); + } + } + } + + #endregion + + #region Monitoring & Events + + private void CreateFileSystemWatchers() + { + _monitorFiles = new FileSystemWatcher(); + _monitorFiles.Path = CommonHelper.MapPath(_themesBasePath); + _monitorFiles.InternalBufferSize = 32768; // 32 instead of the default 8 KB + _monitorFiles.Filter = "*.*"; + _monitorFiles.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.FileName; + _monitorFiles.IncludeSubdirectories = true; + _monitorFiles.Changed += (s, e) => OnThemeFileChanged(e.Name, e.FullPath, ThemeFileChangeType.Modified); + _monitorFiles.Deleted += (s, e) => OnThemeFileChanged(e.Name, e.FullPath, ThemeFileChangeType.Deleted); + _monitorFiles.Created += (s, e) => OnThemeFileChanged(e.Name, e.FullPath, ThemeFileChangeType.Created); + _monitorFiles.Renamed += (s, e) => + { + OnThemeFileChanged(e.OldName, e.OldFullPath, ThemeFileChangeType.Deleted); + OnThemeFileChanged(e.Name, e.FullPath, ThemeFileChangeType.Created); + }; + + _monitorFolders = new FileSystemWatcher(); + _monitorFolders.Path = CommonHelper.MapPath(_themesBasePath); + _monitorFolders.Filter = "*"; + _monitorFolders.NotifyFilter = NotifyFilters.DirectoryName; + _monitorFolders.IncludeSubdirectories = false; + _monitorFolders.Renamed += (s, e) => OnThemeFolderRenamed(e.Name, e.FullPath, e.OldName, e.OldFullPath); + _monitorFolders.Deleted += (s, e) => OnThemeFolderDeleted(e.Name, e.FullPath); + } + + public void StartMonitoring(bool force) + { + var shouldStart = force || _enableMonitoring; + + if (shouldStart && !_monitorFiles.EnableRaisingEvents) + _monitorFiles.EnableRaisingEvents = true; + if (shouldStart && !_monitorFolders.EnableRaisingEvents) + _monitorFolders.EnableRaisingEvents = true; + } + + public void StopMonitoring() + { + if (_monitorFiles.EnableRaisingEvents) + _monitorFiles.EnableRaisingEvents = false; + if (_monitorFolders.EnableRaisingEvents) + _monitorFolders.EnableRaisingEvents = false; + } + + private bool ShouldThrottleEvent(EventThrottleKey key) + { + Timer timer; + if (_eventQueue.TryGetValue(key, out timer)) + { + // do nothing. The same event was published a tick ago. + return true; + } + + _eventQueue[key] = new Timer(RemoveFromEventQueue, key, 500, Timeout.Infinite); + return false; + } + + private void RemoveFromEventQueue(object key) + { + Timer timer; + if (_eventQueue.TryRemove((EventThrottleKey)key, out timer)) + { + timer.Dispose(); + } + } + + private void OnThemeFileChanged(string name, string fullPath, ThemeFileChangeType changeType) + { + // Enable event throttling by allowing the very same event to be published only all 500 ms. + var throttleKey = new EventThrottleKey(name, changeType); + if (ShouldThrottleEvent(throttleKey)) + { + return; + } + + if (!_fileFilterPattern.IsMatch(Path.GetExtension(name))) + return; + + var idx = name.IndexOf('\\'); + if (idx < 0) + { + // must be a subfolder of "~/Themes/" + return; + } + + var themeName = name.Substring(0, idx); + var relativePath = name.Substring(themeName.Length + 1).Replace('\\', '/'); + var isConfigFile = relativePath.IsCaseInsensitiveEqual("theme.config"); + + if (changeType == ThemeFileChangeType.Modified && !isConfigFile) + { + // Monitor changes only for root theme.config + return; + } + + BaseThemeChangedEventArgs baseThemeChangedArgs = null; + + if (isConfigFile) + { + // config file changes always result in refreshing the corresponding theme manifest + var di = new DirectoryInfo(Path.GetDirectoryName(fullPath)); + + string oldBaseThemeName = null; + var oldManifest = this.GetThemeManifest(di.Name); + if (oldManifest != null) + { + oldBaseThemeName = oldManifest.BaseThemeName; + } + + try + { + // FS watcher in conjunction with some text editors fires change events twice and locks the file. + // Let's wait max. 250ms till the lock is gone (hopefully). + var fi = new FileInfo(fullPath); + fi.WaitForUnlock(250); + + var newManifest = ThemeManifest.Create(di.FullName, _themesBasePath); + if (newManifest != null) + { + this.AddThemeManifestInternal(newManifest, false); + + if (!oldBaseThemeName.IsCaseInsensitiveEqual(newManifest.BaseThemeName)) + { + baseThemeChangedArgs = new BaseThemeChangedEventArgs + { + ThemeName = newManifest.ThemeName, + BaseTheme = newManifest.BaseTheme != null ? newManifest.BaseTheme.ThemeName : null, + OldBaseTheme = oldBaseThemeName + }; + } + + Logger.Debug("Changed theme manifest for '{0}'".FormatCurrent(name)); + } + else + { + // something went wrong (most probably no 'theme.config'): remove the manifest + TryRemoveManifest(di.Name); + } + } + catch (Exception ex) + { + Logger.Error(ex, "Could not touch theme manifest '{0}': {1}".FormatCurrent(name, ex.Message)); + TryRemoveManifest(di.Name); + } + } + + if (baseThemeChangedArgs != null) + { + RaiseBaseThemeChanged(baseThemeChangedArgs); + } + + RaiseThemeFileChanged(new ThemeFileChangedEventArgs + { + ChangeType = changeType, + FullPath = fullPath, + ThemeName = themeName, + RelativePath = relativePath, + IsConfigurationFile = isConfigFile + }); + } + + private void OnThemeFolderRenamed(string name, string fullPath, string oldName, string oldFullPath) + { + TryRemoveManifest(oldName); + + try + { + var newManifest = GetThemeManifest(name); + if (newManifest != null) + { + this.AddThemeManifestInternal(newManifest, false); + Logger.Debug("Changed theme manifest for '{0}'".FormatCurrent(name)); + } + } + catch (Exception ex) + { + Logger.Error(ex, "Could not touch theme manifest '{0}'".FormatCurrent(name)); + } + + RaiseThemeFolderRenamed(new ThemeFolderRenamedEventArgs + { + FullPath = fullPath, + Name = name, + OldFullPath = oldFullPath, + OldName = oldName + }); + } + + private void OnThemeFolderDeleted(string name, string fullPath) + { + TryRemoveManifest(name); + + RaiseThemeFolderDeleted(new ThemeFolderDeletedEventArgs + { + FullPath = fullPath, + Name = name + }); + } + + public event EventHandler ThemeFileChanged; + protected void RaiseThemeFileChanged(ThemeFileChangedEventArgs e) + { + if (ThemeFileChanged != null) + ThemeFileChanged(this, e); + } + + public event EventHandler ThemeFolderRenamed; + protected void RaiseThemeFolderRenamed(ThemeFolderRenamedEventArgs e) + { + if (ThemeFolderRenamed != null) + ThemeFolderRenamed(this, e); + } + + public event EventHandler ThemeFolderDeleted; + protected void RaiseThemeFolderDeleted(ThemeFolderDeletedEventArgs e) + { + if (ThemeFolderDeleted != null) + ThemeFolderDeleted(this, e); + } + + public event EventHandler BaseThemeChanged; + protected void RaiseBaseThemeChanged(BaseThemeChangedEventArgs e) + { + if (BaseThemeChanged != null) + BaseThemeChanged(this, e); + } #endregion - #region Monitoring & Events - - private void CreateFileSystemWatchers() - { - _monitorFiles = new FileSystemWatcher(); - _monitorFiles.Path = CommonHelper.MapPath(_themesBasePath); - _monitorFiles.InternalBufferSize = 32768; // 32 instead of the default 8 KB - _monitorFiles.Filter = "*.*"; - _monitorFiles.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.FileName; - _monitorFiles.IncludeSubdirectories = true; - _monitorFiles.Changed += (s, e) => OnThemeFileChanged(e.Name, e.FullPath, ThemeFileChangeType.Modified); - _monitorFiles.Deleted += (s, e) => OnThemeFileChanged(e.Name, e.FullPath, ThemeFileChangeType.Deleted); - _monitorFiles.Created += (s, e) => OnThemeFileChanged(e.Name, e.FullPath, ThemeFileChangeType.Created); - _monitorFiles.Renamed += (s, e) => - { - OnThemeFileChanged(e.OldName, e.OldFullPath, ThemeFileChangeType.Deleted); - OnThemeFileChanged(e.Name, e.FullPath, ThemeFileChangeType.Created); - }; - - _monitorFolders = new FileSystemWatcher(); - _monitorFolders.Path = CommonHelper.MapPath(_themesBasePath); - _monitorFolders.Filter = "*"; - _monitorFolders.NotifyFilter = NotifyFilters.DirectoryName; - _monitorFolders.IncludeSubdirectories = false; - _monitorFolders.Renamed += (s, e) => OnThemeFolderRenamed(e.Name, e.FullPath, e.OldName, e.OldFullPath); - _monitorFolders.Deleted += (s, e) => OnThemeFolderDeleted(e.Name, e.FullPath); - } - - public void StartMonitoring(bool force) - { - var shouldStart = force || _enableMonitoring; - - if (shouldStart && !_monitorFiles.EnableRaisingEvents) - _monitorFiles.EnableRaisingEvents = true; - if (shouldStart && !_monitorFolders.EnableRaisingEvents) - _monitorFolders.EnableRaisingEvents = true; - } - - public void StopMonitoring() - { - if (_monitorFiles.EnableRaisingEvents) - _monitorFiles.EnableRaisingEvents = false; - if (_monitorFolders.EnableRaisingEvents) - _monitorFolders.EnableRaisingEvents = false; - } - - private bool ShouldThrottleEvent(EventThrottleKey key) - { - Timer timer; - if (_eventQueue.TryGetValue(key, out timer)) - { - // do nothing. The same event was published a tick ago. - return true; - } - - _eventQueue[key] = new Timer(RemoveFromEventQueue, key, 500, Timeout.Infinite); - return false; - } - - private void RemoveFromEventQueue(object key) - { - Timer timer; - if (_eventQueue.TryRemove((EventThrottleKey)key, out timer)) - { - timer.Dispose(); - } - } - - private void WaitForUnlock(string fullPath) - { - try - { - var fi = new FileInfo(fullPath); - for (var i = 0; i < 5; i++) - { - if (!fi.IsFileLocked()) - { - return; - } - Thread.Sleep(50); - } - } - finally { } - } - - private void OnThemeFileChanged(string name, string fullPath, ThemeFileChangeType changeType) - { - // Enable event throttling by allowing the very same event to be published only all 500 ms. - var throttleKey = new EventThrottleKey(name, changeType); - if (ShouldThrottleEvent(throttleKey)) - { - return; - } - - if (!_fileFilterPattern.IsMatch(Path.GetExtension(name))) - return; - - var idx = name.IndexOf('\\'); - if (idx < 0) - { - // must be a subfolder of "~/Themes/" - return; - } - - var themeName = name.Substring(0, idx); - var relativePath = name.Substring(themeName.Length + 1).Replace('\\', '/'); - var isConfigFile = relativePath.IsCaseInsensitiveEqual("theme.config"); - - if (changeType == ThemeFileChangeType.Modified && !isConfigFile) - { - // Monitor changes only for root theme.config - return; - } - - BaseThemeChangedEventArgs baseThemeChangedArgs = null; - - if (isConfigFile) - { - // config file changes always result in refreshing the corresponding theme manifest - var di = new DirectoryInfo(Path.GetDirectoryName(fullPath)); - - string oldBaseThemeName = null; - var oldManifest = this.GetThemeManifest(di.Name); - if (oldManifest != null) - { - oldBaseThemeName = oldManifest.BaseThemeName; - } - - try - { - // FS watcher in conjunction with some text editors fires change events twice and locks the file. - // Let's wait max. 250 ms till the lock is gone (hopefully). - WaitForUnlock(fullPath); - - var newManifest = ThemeManifest.Create(di.FullName, _themesBasePath); - if (newManifest != null) - { - this.AddThemeManifestInternal(newManifest, false); - - if (!oldBaseThemeName.IsCaseInsensitiveEqual(newManifest.BaseThemeName)) - { - baseThemeChangedArgs = new BaseThemeChangedEventArgs - { - ThemeName = newManifest.ThemeName, - BaseTheme = newManifest.BaseTheme != null ? newManifest.BaseTheme.ThemeName : null, - OldBaseTheme = oldBaseThemeName - }; - } - - Logger.Debug("Changed theme manifest for '{0}'".FormatCurrent(name)); - } - else - { - // something went wrong (most probably no 'theme.config'): remove the manifest - TryRemoveManifest(di.Name); - } - } - catch (Exception ex) - { - Logger.Error(ex, "Could not touch theme manifest '{0}': {1}".FormatCurrent(name, ex.Message)); - TryRemoveManifest(di.Name); - } - } - - if (baseThemeChangedArgs != null) - { - RaiseBaseThemeChanged(baseThemeChangedArgs); - } - - RaiseThemeFileChanged(new ThemeFileChangedEventArgs - { - ChangeType = changeType, - FullPath = fullPath, - ThemeName = themeName, - RelativePath = relativePath, - IsConfigurationFile = isConfigFile - }); - } - - private void OnThemeFolderRenamed(string name, string fullPath, string oldName, string oldFullPath) - { - TryRemoveManifest(oldName); - - try - { - var newManifest = GetThemeManifest(name); - if (newManifest != null) - { - this.AddThemeManifestInternal(newManifest, false); - Logger.Debug("Changed theme manifest for '{0}'".FormatCurrent(name)); - } - } - catch (Exception ex) - { - Logger.Error(ex, "Could not touch theme manifest '{0}'".FormatCurrent(name)); - } - - RaiseThemeFolderRenamed(new ThemeFolderRenamedEventArgs - { - FullPath = fullPath, - Name = name, - OldFullPath = oldFullPath, - OldName = oldName - }); - } - - private void OnThemeFolderDeleted(string name, string fullPath) - { - TryRemoveManifest(name); - - RaiseThemeFolderDeleted(new ThemeFolderDeletedEventArgs - { - FullPath = fullPath, - Name = name - }); - } - - public event EventHandler ThemeFileChanged; - protected void RaiseThemeFileChanged(ThemeFileChangedEventArgs e) - { - if (ThemeFileChanged != null) - ThemeFileChanged(this, e); - } - - public event EventHandler ThemeFolderRenamed; - protected void RaiseThemeFolderRenamed(ThemeFolderRenamedEventArgs e) - { - if (ThemeFolderRenamed != null) - ThemeFolderRenamed(this, e); - } - - public event EventHandler ThemeFolderDeleted; - protected void RaiseThemeFolderDeleted(ThemeFolderDeletedEventArgs e) - { - if (ThemeFolderDeleted != null) - ThemeFolderDeleted(this, e); - } - - public event EventHandler BaseThemeChanged; - protected void RaiseBaseThemeChanged(BaseThemeChangedEventArgs e) - { - if (BaseThemeChanged != null) - BaseThemeChanged(this, e); - } - - #endregion - - #region Disposable - - protected override void OnDispose(bool disposing) - { - if (disposing) - { - if (_monitorFiles != null) - { - _monitorFiles.EnableRaisingEvents = false; - _monitorFiles.Dispose(); - _monitorFiles = null; - } - - if (_monitorFolders != null) - { - _monitorFolders.EnableRaisingEvents = false; - _monitorFolders.Dispose(); - _monitorFolders = null; - } - } - } - - #endregion - - private class EventThrottleKey : Tuple - { - public EventThrottleKey(string name, ThemeFileChangeType changeType) - : base(name, changeType) - { - } - } - } + #region Disposable + + protected override void OnDispose(bool disposing) + { + if (disposing) + { + if (_monitorFiles != null) + { + _monitorFiles.EnableRaisingEvents = false; + _monitorFiles.Dispose(); + _monitorFiles = null; + } + + if (_monitorFolders != null) + { + _monitorFolders.EnableRaisingEvents = false; + _monitorFolders.Dispose(); + _monitorFolders = null; + } + } + } + + #endregion + + private class EventThrottleKey : Tuple + { + public EventThrottleKey(string name, ThemeFileChangeType changeType) + : base(name, changeType) + { + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Themes/IThemeContext.cs b/src/Libraries/SmartStore.Core/Themes/IThemeContext.cs index 1162ab331d..7398f169a7 100644 --- a/src/Libraries/SmartStore.Core/Themes/IThemeContext.cs +++ b/src/Libraries/SmartStore.Core/Themes/IThemeContext.cs @@ -2,7 +2,7 @@ namespace SmartStore.Core.Themes { /// - /// Work context + /// Theme context /// public interface IThemeContext { @@ -11,29 +11,29 @@ public interface IThemeContext /// string WorkingThemeName { get; set; } - /// - /// Sets a theme override to be used for the current request - /// - /// The theme override or null to remove the override - void SetRequestTheme(string theme); + /// + /// Sets a theme override to be used for the current request + /// + /// The theme override or null to remove the override + void SetRequestTheme(string theme); - /// - /// Sets a theme override to be used for the current user's session (e.g. for preview mode) - /// - /// The theme override or null to remove the override - void SetPreviewTheme(string theme); + /// + /// Sets a theme override to be used for the current user's session (e.g. for preview mode) + /// + /// The theme override or null to remove the override + void SetPreviewTheme(string theme); - /// - /// Gets the theme override for the current request - /// - /// The theme override or null - string GetRequestTheme(); + /// + /// Gets the theme override for the current request + /// + /// The theme override or null + string GetRequestTheme(); - /// - /// Gets the theme override for the current session - /// - /// The theme override or null - string GetPreviewTheme(); + /// + /// Gets the theme override for the current session + /// + /// The theme override or null + string GetPreviewTheme(); /// /// Gets or sets the manifest of the current working theme diff --git a/src/Libraries/SmartStore.Core/Themes/IThemeFileResolver.cs b/src/Libraries/SmartStore.Core/Themes/IThemeFileResolver.cs index 6c76b038ab..9e2f5d879a 100644 --- a/src/Libraries/SmartStore.Core/Themes/IThemeFileResolver.cs +++ b/src/Libraries/SmartStore.Core/Themes/IThemeFileResolver.cs @@ -1,9 +1,7 @@ -using System; - -namespace SmartStore.Core.Themes +namespace SmartStore.Core.Themes { - public interface IThemeFileResolver - { - InheritedThemeFileResult Resolve(string virtualPath); - } + public interface IThemeFileResolver + { + InheritedThemeFileResult Resolve(string virtualPath); + } } diff --git a/src/Libraries/SmartStore.Core/Themes/IThemeRegistry.cs b/src/Libraries/SmartStore.Core/Themes/IThemeRegistry.cs index 94bade5e85..c095783f33 100644 --- a/src/Libraries/SmartStore.Core/Themes/IThemeRegistry.cs +++ b/src/Libraries/SmartStore.Core/Themes/IThemeRegistry.cs @@ -5,130 +5,130 @@ namespace SmartStore.Core.Themes { public partial interface IThemeRegistry { - - /// - /// Gets all registered theme manifests - /// - /// Specifies whether inactive themes should also be included in the return list - /// A collection of manifests - ICollection GetThemeManifests(bool includeHidden = false); - - /// - /// Gets a single theme manifest by theme name - /// - /// The name of the theme to get a manifest for - /// A ThemeManifest instance or null, if theme is not registered - ThemeManifest GetThemeManifest(string themeName); - - /// - /// Gets a value indicating whether a theme is registered - /// - /// The theme name to check - /// true if theme exists, false otherwise + + /// + /// Gets all registered theme manifests + /// + /// Specifies whether inactive themes should also be included in the return list + /// A collection of manifests + ICollection GetThemeManifests(bool includeHidden = false); + + /// + /// Gets a single theme manifest by theme name + /// + /// The name of the theme to get a manifest for + /// A ThemeManifest instance or null, if theme is not registered + ThemeManifest GetThemeManifest(string themeName); + + /// + /// Gets a value indicating whether a theme is registered + /// + /// The theme name to check + /// true if theme exists, false otherwise bool ThemeManifestExists(string themeName); - /// - /// Registers a theme manifest - /// - /// The theme manifest to register - /// If an equal theme exists already, it gets removed first. - void AddThemeManifest(ThemeManifest manifest); - - /// - /// Gets a value indicating whether a theme is a child of another theme - /// - /// The name of the theme to test - /// The name of the base theme - /// true when is based on , false othwerise - /// - /// This method walks up the complete hierarchy chain of to determine the result. - /// - bool IsChildThemeOf(string themeName, string baseTheme); - - /// - /// Gets all derived child themes - /// - /// The name of the theme to get the children for - /// When true, the method gets all child themes in the hierarchy chain, otherwise it only returns direct children. - /// The manifests of matching themes - IEnumerable GetChildrenOf(string themeName, bool deep = true); - - /// - /// Starts/resumes raising file system events - /// - /// - /// When true, monitoring gets started regardless of the global setting (web.config > appSettings > sm:MonitorThemesFolder), - /// otherwise this method does nothing when the setting is false. - /// - void StartMonitoring(bool force); - - /// - /// Stops/pauses raising file system events - /// - void StopMonitoring(); - - /// - /// Clears all parsed theme manifests and reloads them - /// - void ReloadThemes(); - - /// - /// Event raised when an inheritable (static) theme file has been created or deleted, - /// OR when the theme.config file has been modified. - /// - event EventHandler ThemeFileChanged; - - /// - /// Event raised when a theme folder has been renamed. - /// - event EventHandler ThemeFolderRenamed; - - /// - /// Event raised when a theme folder has been deleted. - /// - event EventHandler ThemeFolderDeleted; - - /// - /// Event raised when a theme's base theme changes. - /// - event EventHandler BaseThemeChanged; + /// + /// Registers a theme manifest + /// + /// The theme manifest to register + /// If an equal theme exists already, it gets removed first. + void AddThemeManifest(ThemeManifest manifest); + + /// + /// Gets a value indicating whether a theme is a child of another theme + /// + /// The name of the theme to test + /// The name of the base theme + /// true when is based on , false othwerise + /// + /// This method walks up the complete hierarchy chain of to determine the result. + /// + bool IsChildThemeOf(string themeName, string baseTheme); + + /// + /// Gets all derived child themes + /// + /// The name of the theme to get the children for + /// When true, the method gets all child themes in the hierarchy chain, otherwise it only returns direct children. + /// The manifests of matching themes + IEnumerable GetChildrenOf(string themeName, bool deep = true); + + /// + /// Starts/resumes raising file system events + /// + /// + /// When true, monitoring gets started regardless of the global setting (web.config > appSettings > sm:MonitorThemesFolder), + /// otherwise this method does nothing when the setting is false. + /// + void StartMonitoring(bool force); + + /// + /// Stops/pauses raising file system events + /// + void StopMonitoring(); + + /// + /// Clears all parsed theme manifests and reloads them + /// + void ReloadThemes(); + + /// + /// Event raised when an inheritable (static) theme file has been created or deleted, + /// OR when the theme.config file has been modified. + /// + event EventHandler ThemeFileChanged; + + /// + /// Event raised when a theme folder has been renamed. + /// + event EventHandler ThemeFolderRenamed; + + /// + /// Event raised when a theme folder has been deleted. + /// + event EventHandler ThemeFolderDeleted; + + /// + /// Event raised when a theme's base theme changes. + /// + event EventHandler BaseThemeChanged; + } + + + public class ThemeFileChangedEventArgs : EventArgs + { + public string FullPath { get; set; } + public string ThemeName { get; set; } + public string RelativePath { get; set; } + public bool IsConfigurationFile { get; set; } + public ThemeFileChangeType ChangeType { get; set; } } + public class ThemeFolderRenamedEventArgs : EventArgs + { + public string FullPath { get; set; } + public string Name { get; set; } + public string OldFullPath { get; set; } + public string OldName { get; set; } + } - public class ThemeFileChangedEventArgs : EventArgs - { - public string FullPath { get; set; } - public string ThemeName { get; set; } - public string RelativePath { get; set; } - public bool IsConfigurationFile { get; set; } - public ThemeFileChangeType ChangeType { get; set; } - } - - public class ThemeFolderRenamedEventArgs : EventArgs - { - public string FullPath { get; set; } - public string Name { get; set; } - public string OldFullPath { get; set; } - public string OldName { get; set; } - } - - public class ThemeFolderDeletedEventArgs : EventArgs - { - public string FullPath { get; set; } - public string Name { get; set; } - } - - public class BaseThemeChangedEventArgs : EventArgs - { - public string ThemeName { get; set; } - public string BaseTheme { get; set; } - public string OldBaseTheme { get; set; } - } - - public enum ThemeFileChangeType - { - Created, - Deleted, - Modified - } + public class ThemeFolderDeletedEventArgs : EventArgs + { + public string FullPath { get; set; } + public string Name { get; set; } + } + + public class BaseThemeChangedEventArgs : EventArgs + { + public string ThemeName { get; set; } + public string BaseTheme { get; set; } + public string OldBaseTheme { get; set; } + } + + public enum ThemeFileChangeType + { + Created, + Deleted, + Modified + } } diff --git a/src/Libraries/SmartStore.Core/Themes/InheritedThemeFileResult.cs b/src/Libraries/SmartStore.Core/Themes/InheritedThemeFileResult.cs index 77d708c5c8..99d38490e9 100644 --- a/src/Libraries/SmartStore.Core/Themes/InheritedThemeFileResult.cs +++ b/src/Libraries/SmartStore.Core/Themes/InheritedThemeFileResult.cs @@ -1,44 +1,42 @@ -using System; - -namespace SmartStore.Core.Themes +namespace SmartStore.Core.Themes { - public class InheritedThemeFileResult - { - /// - /// The unrooted relative path of the file (without ~/Themes/ThemeName/) - /// - public string RelativePath { get; set; } - - /// - /// The original virtual path - /// - public string OriginalVirtualPath { get; set; } - - /// - /// The result virtual path (the path in which the file is actually located) - /// - public string ResultVirtualPath { get; set; } - - /// - /// The result physical path (the path in which the file is actually located) - /// - public string ResultPhysicalPath { get; set; } - - /// - /// The name of the requesting theme - /// - public string OriginalThemeName { get; set; } - - /// - /// The name of the resulting theme where the file is actually located - /// - public string ResultThemeName { get; set; } - - public bool IsBased { get; set; } - - /// - /// The query string, e.g. '?base' - /// - public string Query { get; set; } - } + public class InheritedThemeFileResult + { + /// + /// The unrooted relative path of the file (without ~/Themes/ThemeName/) + /// + public string RelativePath { get; set; } + + /// + /// The original virtual path + /// + public string OriginalVirtualPath { get; set; } + + /// + /// The result virtual path (the path in which the file is actually located) + /// + public string ResultVirtualPath { get; set; } + + /// + /// The result physical path (the path in which the file is actually located) + /// + public string ResultPhysicalPath { get; set; } + + /// + /// The name of the requesting theme + /// + public string OriginalThemeName { get; set; } + + /// + /// The name of the resulting theme where the file is actually located + /// + public string ResultThemeName { get; set; } + + public bool IsBased { get; set; } + + /// + /// The query string, e.g. '?base' + /// + public string Query { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Themes/ThemeFolderData.cs b/src/Libraries/SmartStore.Core/Themes/ThemeFolderData.cs index e557505cc9..62f866de60 100644 --- a/src/Libraries/SmartStore.Core/Themes/ThemeFolderData.cs +++ b/src/Libraries/SmartStore.Core/Themes/ThemeFolderData.cs @@ -3,29 +3,26 @@ namespace SmartStore.Core.Themes { - internal class ThemeFolderData : ITopologicSortable - { - public string FolderName { get; set; } - public string FullPath { get; set; } - public bool IsSymbolicLink { get; set; } - public string VirtualBasePath { get; set; } - public XmlDocument Configuration { get; set; } - public string BaseTheme { get; set; } + internal class ThemeFolderData : ITopologicSortable + { + public string FolderName { get; set; } + public string FullPath { get; set; } + public bool IsSymbolicLink { get; set; } + public string VirtualBasePath { get; set; } + public XmlDocument Configuration { get; set; } + public string BaseTheme { get; set; } - string ITopologicSortable.Key - { - get { return FolderName; } - } + string ITopologicSortable.Key => FolderName; - string[] ITopologicSortable.DependsOn - { - get - { - if (BaseTheme == null) - return null; + string[] ITopologicSortable.DependsOn + { + get + { + if (BaseTheme == null) + return null; - return new [] { BaseTheme }; - } - } - } + return new[] { BaseTheme }; + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Themes/ThemeManifest.cs b/src/Libraries/SmartStore.Core/Themes/ThemeManifest.cs index dbaca7e630..f107635a81 100644 --- a/src/Libraries/SmartStore.Core/Themes/ThemeManifest.cs +++ b/src/Libraries/SmartStore.Core/Themes/ThemeManifest.cs @@ -1,348 +1,336 @@ using System; using System.Collections.Generic; +using System.IO; using System.Xml; using SmartStore.Collections; -using System.IO; namespace SmartStore.Core.Themes { - public class ThemeManifest : ComparableObject + public class ThemeManifest : ComparableObject { internal ThemeManifest() { } - #region Methods + #region Methods - public static ThemeManifest Create(string themePath, string virtualBasePath = "~/Themes/") - { - Guard.NotEmpty(themePath, nameof(themePath)); + public static ThemeManifest Create(string themePath, string virtualBasePath = "~/Themes/") + { + Guard.NotEmpty(themePath, nameof(themePath)); - var folderData = CreateThemeFolderData(themePath, virtualBasePath); - if (folderData != null) - { - return Create(folderData); - } + var folderData = CreateThemeFolderData(themePath, virtualBasePath); + if (folderData != null) + { + return Create(folderData); + } - return null; - } + return null; + } - internal static ThemeManifest Create(ThemeFolderData folderData) - { - Guard.NotNull(folderData, nameof(folderData)); + internal static ThemeManifest Create(ThemeFolderData folderData) + { + Guard.NotNull(folderData, nameof(folderData)); - var materializer = new ThemeManifestMaterializer(folderData); - var manifest = materializer.Materialize(); + var materializer = new ThemeManifestMaterializer(folderData); + var manifest = materializer.Materialize(); - return manifest; - } + return manifest; + } - internal static ThemeFolderData CreateThemeFolderData(string themePath, string virtualBasePath = "~/Themes/") - { - if (themePath.IsEmpty()) - return null; + internal static ThemeFolderData CreateThemeFolderData(string themePath, string virtualBasePath = "~/Themes/") + { + if (themePath.IsEmpty()) + return null; - virtualBasePath = virtualBasePath.EnsureEndsWith("/"); + virtualBasePath = virtualBasePath.EnsureEndsWith("/"); - var themeDirectory = new DirectoryInfo(themePath); + var themeDirectory = new DirectoryInfo(themePath); - var isSymLink = themeDirectory.IsSymbolicLink(); - if (isSymLink) - { - themeDirectory = new DirectoryInfo(themeDirectory.GetFinalPathName()); - } + var isSymLink = themeDirectory.IsSymbolicLink(out var finalPathName); + if (isSymLink) + { + themeDirectory = new DirectoryInfo(finalPathName); + } - var themeConfigFile = new FileInfo(System.IO.Path.Combine(themeDirectory.FullName, "theme.config")); + var themeConfigFile = new FileInfo(System.IO.Path.Combine(themeDirectory.FullName, "theme.config")); - if (themeConfigFile.Exists) - { - var doc = new XmlDocument(); - doc.Load(themeConfigFile.FullName); + if (themeConfigFile.Exists) + { + var doc = new XmlDocument(); + doc.Load(themeConfigFile.FullName); - Guard.Against(doc.DocumentElement == null, "The theme configuration document must have a root element."); + Guard.Against(doc.DocumentElement == null, "The theme configuration document must have a root element."); - var root = doc.DocumentElement; + var root = doc.DocumentElement; - var baseTheme = root.GetAttribute("baseTheme").TrimSafe().NullEmpty(); - if (baseTheme != null && baseTheme.IsCaseInsensitiveEqual(themeDirectory.Name)) - { - // Don't let theme point to itself! - baseTheme = null; - } + var baseTheme = root.GetAttribute("baseTheme").TrimSafe().NullEmpty(); + if (baseTheme != null && baseTheme.IsCaseInsensitiveEqual(themeDirectory.Name)) + { + // Don't let theme point to itself! + baseTheme = null; + } - return new ThemeFolderData - { - FolderName = themeDirectory.Name, - FullPath = themeDirectory.FullName, - IsSymbolicLink = isSymLink, - Configuration = doc, - VirtualBasePath = virtualBasePath, - BaseTheme = baseTheme - }; - } + return new ThemeFolderData + { + FolderName = themeDirectory.Name, + FullPath = themeDirectory.FullName, + IsSymbolicLink = isSymLink, + Configuration = doc, + VirtualBasePath = virtualBasePath, + BaseTheme = baseTheme + }; + } - return null; - } + return null; + } - #endregion + #endregion - #region Properties + #region Properties - public XmlElement ConfigurationNode - { - get; - protected internal set; - } + public XmlElement ConfigurationNode + { + get; + protected internal set; + } /// /// Gets the virtual theme base path (e.g.: ~/Themes) /// - public string Location - { - get; - protected internal set; - } - - /// - /// Determines whether the theme directory is a symbolic link to another target. - /// - public bool IsSymbolicLink - { - get; - protected internal set; - } - - /// - /// Gets the physical path of the theme. In case of a symbolic link, - /// returns the link's target path. - /// - public string Path - { - get; - protected internal set; - } - - public string PreviewImageUrl - { - get; - protected internal set; - } - - public string PreviewText - { - get; - protected internal set; - } - - [ObjectSignature] - public string ThemeName - { - get; - protected internal set; - } - - public string BaseThemeName - { - get; - internal set; - } - - public ThemeManifest BaseTheme - { - get; - internal set; - } - - public string ThemeTitle - { - get; - protected internal set; - } - - public string Author - { - get; - protected internal set; - } - - public string Url - { - get; - protected internal set; - } - - public string Version - { - get; - protected internal set; - } - - private IDictionary _variables; - public IDictionary Variables - { - get - { - if (this.BaseTheme == null) - { - return _variables; - } - - var baseVars = this.BaseTheme.Variables; - var mergedVars = new Dictionary(baseVars, StringComparer.OrdinalIgnoreCase); - var newVars = new List(); - - foreach (var localVar in _variables) - { - if (mergedVars.ContainsKey(localVar.Key)) - { - // Overridden var in child: update existing. - var baseVar = mergedVars[localVar.Key]; - mergedVars[localVar.Key] = new ThemeVariableInfo - { - Name = baseVar.Name, - Type = baseVar.Type, - SelectRef = baseVar.SelectRef, - DefaultValue = localVar.Value.DefaultValue, - Manifest = localVar.Value.Manifest - }; - } - else - { - // New var in child: add to temp list. - newVars.Add(localVar.Value); - } - } - - var merged = new Dictionary(StringComparer.OrdinalIgnoreCase); - - foreach (var newVar in newVars) - { - // New child theme vars must come first in final list - // to avoid wrong references in existing vars - merged.Add(newVar.Name, newVar); - } - - foreach (var kvp in mergedVars) - { - merged.Add(kvp.Key, kvp.Value); - } - - return merged; - } - internal set - { - _variables = value; - } - } - - private Multimap _selects; - public Multimap Selects - { - get - { - if (this.BaseTheme == null) - { - return _selects; - } - - var baseSelects = this.BaseTheme.Selects; - var merged = new Multimap(); - baseSelects.Each(x => merged.AddRange(x.Key, x.Value)); - foreach (var localSelect in _selects) - { - if (!merged.ContainsKey(localSelect.Key)) - { - // New Select in child: add to list. - merged.AddRange(localSelect.Key, localSelect.Value); - } - else - { - // Do nothing: we don't support overriding Selects - } - } - - return merged; - } - internal set - { - _selects = value; - } - } - - private ThemeManifestState _state; - public ThemeManifestState State - { - get - { - if (_state == ThemeManifestState.Active) - { - // active state does not mean, that it actually IS active: check state of base themes! - var baseTheme = this.BaseTheme; - while (baseTheme != null) - { - if (baseTheme.State != ThemeManifestState.Active) - { - return baseTheme.State; - } - baseTheme = baseTheme.BaseTheme; - } - } - - return _state; - } - protected internal set - { - _state = value; - } - } - - internal string FullPath + public string Location + { + get; + protected internal set; + } + + /// + /// Determines whether the theme directory is a symbolic link to another target. + /// + public bool IsSymbolicLink + { + get; + protected internal set; + } + + /// + /// Gets the physical path of the theme. In case of a symbolic link, + /// returns the link's target path. + /// + public string Path + { + get; + protected internal set; + } + + public string PreviewImageUrl + { + get; + protected internal set; + } + + public string PreviewText + { + get; + protected internal set; + } + + [ObjectSignature] + public string ThemeName { - get { return System.IO.Path.Combine(this.Path, "theme.config"); } - } - - public override string ToString() - { - return "{0} (Parent: {1}, State: {2})".FormatInvariant(ThemeName, BaseThemeName ?? "-", State.ToString()); - } - - #endregion - - #region Dispose - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected void Dispose(bool disposing) - { - if (disposing) - { - BaseTheme = null; - if (_variables != null) - { - foreach (var pair in _variables) - { - pair.Value.Dispose(); - } - _variables.Clear(); - } - } - } - - ~ThemeManifest() + get; + protected internal set; + } + + public string BaseThemeName + { + get; + internal set; + } + + public ThemeManifest BaseTheme + { + get; + internal set; + } + + public string ThemeTitle + { + get; + protected internal set; + } + + public string Author + { + get; + protected internal set; + } + + public string Url + { + get; + protected internal set; + } + + public string Version + { + get; + protected internal set; + } + + private IDictionary _variables; + public IDictionary Variables + { + get + { + if (this.BaseTheme == null) + { + return _variables; + } + + var baseVars = this.BaseTheme.Variables; + var mergedVars = new Dictionary(baseVars, StringComparer.OrdinalIgnoreCase); + var newVars = new List(); + + foreach (var localVar in _variables) + { + if (mergedVars.ContainsKey(localVar.Key)) + { + // Overridden var in child: update existing. + var baseVar = mergedVars[localVar.Key]; + mergedVars[localVar.Key] = new ThemeVariableInfo + { + Name = baseVar.Name, + Type = baseVar.Type, + SelectRef = baseVar.SelectRef, + DefaultValue = localVar.Value.DefaultValue, + Manifest = localVar.Value.Manifest + }; + } + else + { + // New var in child: add to temp list. + newVars.Add(localVar.Value); + } + } + + var merged = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var newVar in newVars) + { + // New child theme vars must come first in final list + // to avoid wrong references in existing vars + merged.Add(newVar.Name, newVar); + } + + foreach (var kvp in mergedVars) + { + merged.Add(kvp.Key, kvp.Value); + } + + return merged; + } + internal set => _variables = value; + } + + private Multimap _selects; + public Multimap Selects + { + get + { + if (this.BaseTheme == null) + { + return _selects; + } + + var baseSelects = this.BaseTheme.Selects; + var merged = new Multimap(); + baseSelects.Each(x => merged.AddRange(x.Key, x.Value)); + foreach (var localSelect in _selects) + { + if (!merged.ContainsKey(localSelect.Key)) + { + // New Select in child: add to list. + merged.AddRange(localSelect.Key, localSelect.Value); + } + else + { + // Do nothing: we don't support overriding Selects + } + } + + return merged; + } + internal set => _selects = value; + } + + private ThemeManifestState _state; + public ThemeManifestState State + { + get + { + if (_state == ThemeManifestState.Active) + { + // active state does not mean, that it actually IS active: check state of base themes! + var baseTheme = this.BaseTheme; + while (baseTheme != null) + { + if (baseTheme.State != ThemeManifestState.Active) + { + return baseTheme.State; + } + baseTheme = baseTheme.BaseTheme; + } + } + + return _state; + } + protected internal set => _state = value; + } + + internal string FullPath => System.IO.Path.Combine(this.Path, "theme.config"); + + public override string ToString() + { + return "{0} (Parent: {1}, State: {2})".FormatInvariant(ThemeName, BaseThemeName ?? "-", State.ToString()); + } + + #endregion + + #region Dispose + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected void Dispose(bool disposing) + { + if (disposing) + { + BaseTheme = null; + if (_variables != null) + { + foreach (var pair in _variables) + { + pair.Value.Dispose(); + } + _variables.Clear(); + } + } + } + + ~ThemeManifest() { Dispose(false); } - #endregion - } + #endregion + } - public enum ThemeManifestState - { - MissingBaseTheme = -1, - Active = 0, - } + public enum ThemeManifestState + { + MissingBaseTheme = -1, + Active = 0, + } } diff --git a/src/Libraries/SmartStore.Core/Themes/ThemeManifestMaterializer.cs b/src/Libraries/SmartStore.Core/Themes/ThemeManifestMaterializer.cs index 98f1058002..371dc9cd34 100644 --- a/src/Libraries/SmartStore.Core/Themes/ThemeManifestMaterializer.cs +++ b/src/Libraries/SmartStore.Core/Themes/ThemeManifestMaterializer.cs @@ -2,39 +2,38 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Text; using System.Xml; using SmartStore.Collections; namespace SmartStore.Core.Themes -{ +{ internal class ThemeManifestMaterializer { private readonly ThemeManifest _manifest; public ThemeManifestMaterializer(ThemeFolderData folderData) { - Guard.NotNull(folderData, nameof(folderData)); + Guard.NotNull(folderData, nameof(folderData)); _manifest = new ThemeManifest(); - _manifest.ThemeName = folderData.FolderName; - _manifest.BaseThemeName = folderData.BaseTheme; + _manifest.ThemeName = folderData.FolderName; + _manifest.BaseThemeName = folderData.BaseTheme; _manifest.Location = folderData.VirtualBasePath; _manifest.Path = folderData.FullPath; - _manifest.IsSymbolicLink = folderData.IsSymbolicLink; + _manifest.IsSymbolicLink = folderData.IsSymbolicLink; _manifest.ConfigurationNode = folderData.Configuration.DocumentElement; } - + public ThemeManifest Materialize() { var root = _manifest.ConfigurationNode; - _manifest.ThemeTitle = root.GetAttribute("title").NullEmpty() ?? _manifest.ThemeName; + _manifest.ThemeTitle = root.GetAttribute("title").NullEmpty() ?? _manifest.ThemeName; _manifest.PreviewImageUrl = root.GetAttribute("previewImageUrl").NullEmpty() ?? "~/Themes/{0}/preview.png".FormatCurrent(_manifest.ThemeName); _manifest.PreviewText = root.GetAttribute("previewText").ToSafe(); _manifest.Author = root.GetAttribute("author").ToSafe(); - _manifest.Url = root.GetAttribute("url").ToSafe(); + _manifest.Url = root.GetAttribute("url").ToSafe(); _manifest.Version = root.GetAttribute("version").ToSafe().HasValue() ? root.GetAttribute("version") : "1.0"; _manifest.Selects = MaterializeSelects(); @@ -43,9 +42,9 @@ public ThemeManifest Materialize() return _manifest; } - [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] - [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] - private Multimap MaterializeSelects() + [SuppressMessage("ReSharper", "AssignNullToNotNullAttribute")] + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + private Multimap MaterializeSelects() { var selects = new Multimap(); var root = _manifest.ConfigurationNode; @@ -115,15 +114,14 @@ private ThemeVariableInfo MaterializeVariable(XmlElement xel) string type = xel.GetAttribute("type").ToSafe("String"); - string selectRef = null; - var varType = ConvertVarType(type, xel, out selectRef); + var varType = ConvertVarType(type, xel, out var selectRef); - if (varType != ThemeVariableType.String && value.IsEmpty()) - { - throw new SmartException("A value is required for non-string 'Var' elements. Affected: '{0}' - element: {1}", _manifest.FullPath, xel.OuterXml); - } + if (varType != ThemeVariableType.String && value.IsEmpty()) + { + throw new SmartException("A value is required for non-string 'Var' elements. Affected: '{0}' - element: {1}", _manifest.FullPath, xel.OuterXml); + } - var info = new ThemeVariableInfo + var info = new ThemeVariableInfo { Name = name, DefaultValue = value, diff --git a/src/Libraries/SmartStore.Core/Themes/ThemeSwitchedEvent.cs b/src/Libraries/SmartStore.Core/Themes/ThemeSwitchedEvent.cs index c076712e4b..25a50006c9 100644 --- a/src/Libraries/SmartStore.Core/Themes/ThemeSwitchedEvent.cs +++ b/src/Libraries/SmartStore.Core/Themes/ThemeSwitchedEvent.cs @@ -1,6 +1,4 @@ -using System; - -namespace SmartStore.Core.Themes +namespace SmartStore.Core.Themes { public class ThemeSwitchedEvent { diff --git a/src/Libraries/SmartStore.Core/Themes/ThemeTouchedEvent.cs b/src/Libraries/SmartStore.Core/Themes/ThemeTouchedEvent.cs index cdef95b538..7d8622181b 100644 --- a/src/Libraries/SmartStore.Core/Themes/ThemeTouchedEvent.cs +++ b/src/Libraries/SmartStore.Core/Themes/ThemeTouchedEvent.cs @@ -1,15 +1,13 @@ -using System; - -namespace SmartStore.Core.Themes +namespace SmartStore.Core.Themes { - public class ThemeTouchedEvent - { - public ThemeTouchedEvent(string themeName) - { - Guard.NotEmpty(themeName, nameof(themeName)); - this.ThemeName = themeName; - } - - public string ThemeName { get; set; } - } + public class ThemeTouchedEvent + { + public ThemeTouchedEvent(string themeName) + { + Guard.NotEmpty(themeName, nameof(themeName)); + this.ThemeName = themeName; + } + + public string ThemeName { get; set; } + } } diff --git a/src/Libraries/SmartStore.Core/Themes/ThemeVariableInfo.cs b/src/Libraries/SmartStore.Core/Themes/ThemeVariableInfo.cs index 0fbb373a9d..f6609f6cc0 100644 --- a/src/Libraries/SmartStore.Core/Themes/ThemeVariableInfo.cs +++ b/src/Libraries/SmartStore.Core/Themes/ThemeVariableInfo.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace SmartStore.Core.Themes -{ +namespace SmartStore.Core.Themes +{ /// /// Represents deserialized metadata for a theme variable /// diff --git a/src/Libraries/SmartStore.Core/Themes/ThemeVariableType.cs b/src/Libraries/SmartStore.Core/Themes/ThemeVariableType.cs index f07e5cf7ef..a36cb21465 100644 --- a/src/Libraries/SmartStore.Core/Themes/ThemeVariableType.cs +++ b/src/Libraries/SmartStore.Core/Themes/ThemeVariableType.cs @@ -1,6 +1,4 @@ -using System; - -namespace SmartStore.Core.Themes +namespace SmartStore.Core.Themes { public enum ThemeVariableType { diff --git a/src/Libraries/SmartStore.Core/Utilities/ActionDisposable.cs b/src/Libraries/SmartStore.Core/Utilities/ActionDisposable.cs index dd99242538..b568462120 100644 --- a/src/Libraries/SmartStore.Core/Utilities/ActionDisposable.cs +++ b/src/Libraries/SmartStore.Core/Utilities/ActionDisposable.cs @@ -2,7 +2,6 @@ namespace SmartStore.Utilities { - /// /// Allows action to be executed when it is disposed /// @@ -14,7 +13,7 @@ public struct ActionDisposable : IDisposable public ActionDisposable(Action action) { - Guard.NotNull(action, nameof(action)); + Guard.NotNull(action, nameof(action)); _action = action; } @@ -23,8 +22,5 @@ public void Dispose() { _action(); } - } - - } diff --git a/src/Libraries/SmartStore.Core/Utilities/CommonHelper.cs b/src/Libraries/SmartStore.Core/Utilities/CommonHelper.cs index 1fb1f2240d..0b1cd338e5 100644 --- a/src/Libraries/SmartStore.Core/Utilities/CommonHelper.cs +++ b/src/Libraries/SmartStore.Core/Utilities/CommonHelper.cs @@ -3,381 +3,445 @@ using System.Collections.Generic; using System.Configuration; using System.Dynamic; -using System.Linq; using System.Globalization; using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization.Formatters.Binary; using System.Security.Cryptography; +using System.Text; using System.Web.Hosting; using System.Web.Mvc; -using SmartStore.ComponentModel; -using System.Text; using Newtonsoft.Json; -using System.Runtime.Serialization.Formatters.Binary; +using SmartStore.ComponentModel; namespace SmartStore.Utilities { public static partial class CommonHelper { - private static bool? _isDevEnvironment; - - [ThreadStatic] - private static Random _random; - - private static Random GetRandomizer() - { - if (_random == null) - { - _random = new Random(); - } - - return _random; - } - - /// + private static bool? _isDevEnvironment; + + [ThreadStatic] + private static Random _random; + + private static Random GetRandomizer() + { + if (_random == null) + { + _random = new Random(); + } + + return _random; + } + + /// /// Generate random digit code /// /// Length /// Result string public static string GenerateRandomDigitCode(int length) { - var buffer = new int[length]; - for (int i = 0; i < length; ++i) - { - buffer[i] = GetRandomizer().Next(10); - } - - return string.Join("", buffer); - } - - /// - /// Returns a random number within the range to - 1. - /// - /// Minimum number - /// Maximum number (exclusive!). - /// Random integer number. - public static int GenerateRandomInteger(int min = 0, int max = 2147483647) - { - var randomNumberBuffer = new byte[10]; - new RNGCryptoServiceProvider().GetBytes(randomNumberBuffer); - return new Random(BitConverter.ToInt32(randomNumberBuffer, 0)).Next(min, max); - } - - /// - /// Maps a virtual path to a physical disk path. - /// - /// The path to map. E.g. "~/bin" - /// Specifies if the app root should be resolved when mapped directory does not exist - /// The physical path. E.g. "c:\inetpub\wwwroot\bin" - /// - /// This method is able to resolve the web application root - /// even when it's called during design-time (e.g. from EF design-time tools). - /// - public static string MapPath(string path, bool findAppRoot = true) - { - if (path == null) - throw new ArgumentNullException(nameof(path)); - - if (HostingEnvironment.IsHosted) - { - // hosted - return HostingEnvironment.MapPath(path); - } - else - { - // not hosted. For example, running in unit tests or EF tooling - string baseDirectory = AppDomain.CurrentDomain.BaseDirectory; - path = path.Replace("~/", "").TrimStart('/').Replace('/', '\\'); - - var testPath = Path.Combine(baseDirectory, path); - - if (findAppRoot /* && !Directory.Exists(testPath)*/) - { - // most likely we're in unit tests or design-mode (EF migration scaffolding)... - // find solution root directory first - var dir = FindSolutionRoot(baseDirectory); - - // concat the web root - if (dir != null) - { - baseDirectory = Path.Combine(dir.FullName, "Presentation\\SmartStore.Web"); - testPath = Path.Combine(baseDirectory, path); - } - } - - return testPath; - } - } - - public static bool IsDevEnvironment - { - get - { - if (!_isDevEnvironment.HasValue) - { - _isDevEnvironment = IsDevEnvironmentInternal(); - } - - return _isDevEnvironment.Value; - } - } - - private static bool IsDevEnvironmentInternal() - { - if (!HostingEnvironment.IsHosted) - return true; - - if (HostingEnvironment.IsDevelopmentEnvironment) - return true; - - if (System.Diagnostics.Debugger.IsAttached) - return true; - - // if there's a 'SmartStore.NET.sln' in one of the parent folders, - // then we're likely in a dev environment - if (FindSolutionRoot(HostingEnvironment.MapPath("~/")) != null) - return true; - - return false; - } - - private static DirectoryInfo FindSolutionRoot(string currentDir) - { - var dir = Directory.GetParent(currentDir); - while (true) - { - if (dir == null || IsSolutionRoot(dir)) - break; - - dir = dir.Parent; - } - - return dir; - } - - private static bool IsSolutionRoot(DirectoryInfo dir) - { - return File.Exists(Path.Combine(dir.FullName, "SmartStoreNET.sln")); - } - - public static bool TryConvert(object value, out T convertedValue) - { - return TryConvert(value, CultureInfo.InvariantCulture, out convertedValue); - } - - public static bool TryConvert(object value, CultureInfo culture, out T convertedValue) - { - return TryAction(delegate - { - return value.Convert(culture); - }, out convertedValue); - } - - public static bool TryConvert(object value, Type to, out object convertedValue) - { - return TryConvert(value, to, CultureInfo.InvariantCulture, out convertedValue); - } - - public static bool TryConvert(object value, Type to, CultureInfo culture, out object convertedValue) - { - return TryAction(delegate { return value.Convert(to, culture); }, out convertedValue); - } - - public static ExpandoObject ToExpando(object value) - { - Guard.NotNull(value, nameof(value)); - - var anonymousDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(value); - IDictionary expando = new ExpandoObject(); - foreach (var item in anonymousDictionary) - { - expando.Add(item); - } - return (ExpandoObject)expando; - } - - public static IDictionary ObjectToDictionary(object obj) - { - if (obj == null) - throw new ArgumentNullException(nameof(obj)); - - return FastProperty.ObjectToDictionary( - obj, - key => key.Replace("_", "-").Replace("@", "")); - } - - /// - /// Gets a setting from the application's web.config appSettings node - /// - /// The type to convert the setting value to - /// The key of the setting - /// The default value to return if the setting does not exist - /// The casted setting value - public static T GetAppSetting(string key, T defValue = default(T)) - { - Guard.NotEmpty(key, nameof(key)); - - var setting = ConfigurationManager.AppSettings[key]; - - if (setting == null) - { - return defValue; - } - - return setting.Convert(); - } - - public static bool HasConnectionString(string connectionStringName) - { - var conString = ConfigurationManager.ConnectionStrings[connectionStringName]; - if (conString != null && conString.ConnectionString.HasValue()) - { - return true; - } - - return false; - } - - private static bool TryAction(Func func, out T output) - { - Guard.NotNull(func, nameof(func)); - - try - { - output = func(); - return true; - } - catch - { - output = default(T); - return false; - } - } - - public static bool IsTruthy(object value) - { - if (value == null) - return false; - - switch (value) - { - case string x: - return x.HasValue(); - case bool x: - return x == true; - case DateTime x: - return x > DateTime.MinValue; - case TimeSpan x: - return x > TimeSpan.MinValue; - case Guid x: - return x != Guid.Empty; - case IComparable x: - return x.CompareTo(0) != 0; - case IEnumerable x: - return x.Any(); - case IEnumerable x: - return x.GetEnumerator().MoveNext(); - } - - if (value.GetType().IsNullable(out var wrappedType)) - { - return IsTruthy(Convert.ChangeType(value, wrappedType)); - } - - return true; - } - - public static long GetObjectSizeInBytes(object obj, HashSet instanceLookup = null) - { - if (obj == null) - return 0; - - var type = obj.GetType(); - var genericArguments = type.GetGenericArguments(); - - long size = 0; - - if (obj is string str) - { - size = Encoding.Default.GetByteCount(str); - } - else if (obj is StringBuilder sb) - { - size = Encoding.Default.GetByteCount(sb.ToString()); - } - else if (type.IsEnum) - { - size = System.Runtime.InteropServices.Marshal.SizeOf(Enum.GetUnderlyingType(type)); - } - else if (type.IsPredefinedSimpleType() || type.IsPredefinedGenericType()) - { - //size = System.Runtime.InteropServices.Marshal.SizeOf(Nullable.GetUnderlyingType(type) ?? type); // crashes often - size = 8; // mean/average - } - else if (obj is Stream stream) - { - size = stream.Length; - } - else if (obj is IDictionary dic) - { - foreach (var item in dic.Values) - { - size += GetObjectSizeInBytes(item, instanceLookup); - } - } - else if (obj is IEnumerable e) - { - foreach (var item in e) - { - size += GetObjectSizeInBytes(item, instanceLookup); - } - } - else - { - if (instanceLookup == null) - { - instanceLookup = new HashSet(ReferenceEqualityComparer.Default); - } - - if (!type.IsValueType && instanceLookup.Contains(obj)) - { - return 0; - } - - instanceLookup.Add(obj); - - var serialized = false; - - if (type.IsSerializable && genericArguments.All(x => x.IsSerializable)) - { - try - { - using (var s = new MemoryStream()) - { - var formatter = new BinaryFormatter(); - formatter.Serialize(s, obj); - size = s.Length; - - serialized = true; - } - } - catch { } - } - - if (!serialized) - { - // Serialization failed or is not supported: make JSON. - var json = JsonConvert.SerializeObject(obj, new JsonSerializerSettings - { - DateFormatHandling = DateFormatHandling.IsoDateFormat, - DateTimeZoneHandling = DateTimeZoneHandling.Utc, - MaxDepth = 10, - ReferenceLoopHandling = ReferenceLoopHandling.Ignore - }); - size = Encoding.Default.GetByteCount(json); - } - } - - return size; - } - } + var buffer = new int[length]; + for (int i = 0; i < length; ++i) + { + buffer[i] = GetRandomizer().Next(10); + } + + return string.Join("", buffer); + } + + /// + /// Returns a random number within the range to - 1. + /// + /// Minimum number + /// Maximum number (exclusive!). + /// Random integer number. + public static int GenerateRandomInteger(int min = 0, int max = 2147483647) + { + var randomNumberBuffer = new byte[10]; + new RNGCryptoServiceProvider().GetBytes(randomNumberBuffer); + return new Random(BitConverter.ToInt32(randomNumberBuffer, 0)).Next(min, max); + } + + /// + /// Maps a virtual path to a physical disk path. + /// + /// The path to map. E.g. "~/bin" + /// Specifies if the app root should be resolved when mapped directory does not exist + /// The physical path. E.g. "c:\inetpub\wwwroot\bin" + /// + /// This method is able to resolve the web application root + /// even when it's called during design-time (e.g. from EF design-time tools). + /// + public static string MapPath(string path, bool findAppRoot = true) + { + if (path == null) + throw new ArgumentNullException(nameof(path)); + + if (HostingEnvironment.IsHosted) + { + // hosted + return HostingEnvironment.MapPath(path); + } + else + { + // not hosted. For example, running in unit tests or EF tooling + string baseDirectory = AppDomain.CurrentDomain.BaseDirectory; + path = path.Replace("~/", "").TrimStart('/').Replace('/', '\\'); + + var testPath = Path.Combine(baseDirectory, path); + + if (findAppRoot /* && !Directory.Exists(testPath)*/) + { + // most likely we're in unit tests or design-mode (EF migration scaffolding)... + // find solution root directory first + var dir = FindSolutionRoot(baseDirectory); + + // concat the web root + if (dir != null) + { + baseDirectory = Path.Combine(dir.FullName, "Presentation\\SmartStore.Web"); + testPath = Path.Combine(baseDirectory, path); + } + } + + return testPath; + } + } + + public static bool IsDevEnvironment + { + get + { + if (!_isDevEnvironment.HasValue) + { + _isDevEnvironment = IsDevEnvironmentInternal(); + } + + return _isDevEnvironment.Value; + } + } + + private static bool IsDevEnvironmentInternal() + { + if (!HostingEnvironment.IsHosted) + return true; + + if (HostingEnvironment.IsDevelopmentEnvironment) + return true; + + if (System.Diagnostics.Debugger.IsAttached) + return true; + + // if there's a 'SmartStore.NET.sln' in one of the parent folders, + // then we're likely in a dev environment + if (FindSolutionRoot(HostingEnvironment.MapPath("~/")) != null) + return true; + + return false; + } + + private static DirectoryInfo FindSolutionRoot(string currentDir) + { + var dir = Directory.GetParent(currentDir); + while (true) + { + if (dir == null || IsSolutionRoot(dir)) + break; + + dir = dir.Parent; + } + + return dir; + } + + private static bool IsSolutionRoot(DirectoryInfo dir) + { + return File.Exists(Path.Combine(dir.FullName, "SmartStoreNET.sln")); + } + + public static bool TryConvert(object value, out T convertedValue) + { + convertedValue = default(T); + + if (TryConvert(value, typeof(T), CultureInfo.InvariantCulture, out object result)) + { + if (result != null) + convertedValue = (T)result; + return true; + } + + return false; + } + + public static bool TryConvert(object value, CultureInfo culture, out T convertedValue) + { + convertedValue = default(T); + + if (TryConvert(value, typeof(T), culture, out object result)) + { + if (result != null) + convertedValue = (T)result; + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryConvert(object value, Type to, out object convertedValue) + { + return TryConvert(value, to, CultureInfo.InvariantCulture, out convertedValue); + } + + public static bool TryConvert(object value, Type to, CultureInfo culture, out object convertedValue) + { + if (to == null) + throw new ArgumentNullException(nameof(to)); + + convertedValue = null; + + if (value == null || value == DBNull.Value) + { + return to == typeof(string) || to.IsPredefinedSimpleType() == false; + } + + if (to != typeof(object) && to.IsInstanceOfType(value)) + { + convertedValue = value; + return true; + } + + Type from = value.GetType(); + + if (culture == null) + { + culture = CultureInfo.InvariantCulture; + } + + try + { + // Get a converter for 'to' (value -> to) + var converter = TypeConverterFactory.GetConverter(to); + if (converter != null && converter.CanConvertFrom(from)) + { + convertedValue = converter.ConvertFrom(culture, value); + return true; + } + + // Try the other way round with a 'from' converter (to <- from) + converter = TypeConverterFactory.GetConverter(from); + if (converter != null && converter.CanConvertTo(to)) + { + convertedValue = converter.ConvertTo(culture, null, value, to); + return true; + } + } + catch + { + return false; + } + + return false; + } + + public static ExpandoObject ToExpando(object value) + { + Guard.NotNull(value, nameof(value)); + + var anonymousDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(value); + IDictionary expando = new ExpandoObject(); + foreach (var item in anonymousDictionary) + { + expando.Add(item); + } + return (ExpandoObject)expando; + } + + public static IDictionary ObjectToDictionary(object obj) + { + if (obj == null) + return new Dictionary(); + + return FastProperty.ObjectToDictionary( + obj, + key => key.Replace("_", "-").Replace("@", "")); + } + + /// + /// Gets a setting from the application's web.config appSettings node + /// + /// The type to convert the setting value to + /// The key of the setting + /// The default value to return if the setting does not exist + /// The casted setting value + public static T GetAppSetting(string key, T defValue = default(T)) + { + Guard.NotEmpty(key, nameof(key)); + + var setting = ConfigurationManager.AppSettings[key]; + + if (setting == null) + { + return defValue; + } + + return setting.Convert(); + } + + public static bool HasConnectionString(string connectionStringName) + { + var conString = ConfigurationManager.ConnectionStrings[connectionStringName]; + if (conString != null && conString.ConnectionString.HasValue()) + { + return true; + } + + return false; + } + + private static bool TryAction(Func func, out T output) + { + Guard.NotNull(func, nameof(func)); + + try + { + output = func(); + return true; + } + catch + { + output = default(T); + return false; + } + } + + public static bool IsTruthy(object value) + { + if (value == null) + return false; + + switch (value) + { + case string x: + return x.HasValue(); + case bool x: + return x == true; + case DateTime x: + return x > DateTime.MinValue; + case TimeSpan x: + return x > TimeSpan.MinValue; + case Guid x: + return x != Guid.Empty; + case IComparable x: + return x.CompareTo(0) != 0; + case IEnumerable x: + return x.Any(); + case IEnumerable x: + return x.GetEnumerator().MoveNext(); + } + + if (value.GetType().IsNullable(out var wrappedType)) + { + return IsTruthy(Convert.ChangeType(value, wrappedType)); + } + + return true; + } + + public static long GetObjectSizeInBytes(object obj, HashSet instanceLookup = null) + { + if (obj == null) + return 0; + + var type = obj.GetType(); + var genericArguments = type.GetGenericArguments(); + + long size = 0; + + if (obj is string str) + { + size = Encoding.Default.GetByteCount(str); + } + else if (obj is StringBuilder sb) + { + size = Encoding.Default.GetByteCount(sb.ToString()); + } + else if (type.IsEnum) + { + size = System.Runtime.InteropServices.Marshal.SizeOf(Enum.GetUnderlyingType(type)); + } + else if (type.IsPredefinedSimpleType() || type.IsPredefinedGenericType()) + { + //size = System.Runtime.InteropServices.Marshal.SizeOf(Nullable.GetUnderlyingType(type) ?? type); // crashes often + size = 8; // mean/average + } + else if (obj is Stream stream) + { + size = stream.Length; + } + else if (obj is IDictionary dic) + { + foreach (var item in dic.Values) + { + size += GetObjectSizeInBytes(item, instanceLookup); + } + } + else if (obj is IEnumerable e) + { + foreach (var item in e) + { + size += GetObjectSizeInBytes(item, instanceLookup); + } + } + else + { + if (instanceLookup == null) + { + instanceLookup = new HashSet(ReferenceEqualityComparer.Default); + } + + if (!type.IsValueType && instanceLookup.Contains(obj)) + { + return 0; + } + + instanceLookup.Add(obj); + + var serialized = false; + + if (type.IsSerializable && genericArguments.All(x => x.IsSerializable)) + { + try + { + using (var s = new MemoryStream()) + { + var formatter = new BinaryFormatter(); + formatter.Serialize(s, obj); + size = s.Length; + + serialized = true; + } + } + catch { } + } + + if (!serialized) + { + // Serialization failed or is not supported: make JSON. + var json = JsonConvert.SerializeObject(obj, new JsonSerializerSettings + { + ContractResolver = SmartContractResolver.Instance, + DateFormatHandling = DateFormatHandling.IsoDateFormat, + DateTimeZoneHandling = DateTimeZoneHandling.Utc, + MaxDepth = 10, + ReferenceLoopHandling = ReferenceLoopHandling.Ignore + }); + size = Encoding.Default.GetByteCount(json); + } + } + + return size; + } + } } diff --git a/src/Libraries/SmartStore.Core/Utilities/DictionaryConverter.cs b/src/Libraries/SmartStore.Core/Utilities/DictionaryConverter.cs index a9da555717..80417db07f 100644 --- a/src/Libraries/SmartStore.Core/Utilities/DictionaryConverter.cs +++ b/src/Libraries/SmartStore.Core/Utilities/DictionaryConverter.cs @@ -1,10 +1,10 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Collections; using System.Reflection; using System.Runtime.Serialization; +using System.Text; using SmartStore.ComponentModel; namespace SmartStore.Utilities @@ -77,7 +77,7 @@ public static class DictionaryConverter { public static bool CanCreateType(Type itemType) { - return itemType.IsClass && itemType.GetConstructor(Type.EmptyTypes) != null; + return itemType.IsPlainObjectType() && itemType.GetConstructor(Type.EmptyTypes) != null; } public static T CreateAndPopulate(IDictionary source, out ICollection problems) @@ -140,16 +140,14 @@ public static void Populate(IDictionary source, object target, o { foreach (var fastProp in FastProperty.GetProperties(t).Values) { - object value; - var pi = fastProp.Property; + var pi = fastProp.Property; - if (!pi.PropertyType.IsPredefinedSimpleType() && source.TryGetValue(pi.Name, out value) && value is IDictionary) + if (!pi.PropertyType.IsPredefinedSimpleType() && source.TryGetValue(pi.Name, out var value) && value is IDictionary) { var nestedValue = fastProp.GetValue(target); - ICollection nestedProblems; populated = populated.Concat(new object[] { target }).ToArray(); - Populate((IDictionary)value, nestedValue, out nestedProblems, populated); + Populate((IDictionary)value, nestedValue, out var nestedProblems, populated); if (nestedProblems != null && nestedProblems.Any()) { @@ -199,7 +197,7 @@ private static object RetrieveArrayValues(PropertyInfo arrayProp, IDictionary problems) { - var pi = prop.Property; + var pi = prop.Property; - if (!pi.CanWrite) + if (!pi.CanWrite) return; try { - if (value != null && !Equals(value, "")) + if (value != null && !Equals(value, string.Empty)) { Type destType = pi.PropertyType; diff --git a/src/Libraries/SmartStore.Core/Utilities/FileDownloadManager.cs b/src/Libraries/SmartStore.Core/Utilities/FileDownloadManager.cs index 40572c1b8b..b54d69e003 100644 --- a/src/Libraries/SmartStore.Core/Utilities/FileDownloadManager.cs +++ b/src/Libraries/SmartStore.Core/Utilities/FileDownloadManager.cs @@ -15,149 +15,150 @@ namespace SmartStore.Utilities { - public class FileDownloadManager - { - private const int _bufferSize = 16384; - - private readonly HttpRequestBase _httpRequest; - - public FileDownloadManager(HttpRequestBase httpRequest) - { - _httpRequest = httpRequest; - } - - /// - /// Downloads a single file synchronously - /// - /// The URL to download the file from (either a fully qualified URL or an app relative/absolute path) - /// Specifies whether the FormsAuthenticationCookie should be sent - /// Timeout in milliseconds - /// Specifiers whether the file is located on the local server - public FileDownloadResponse DownloadFile(string url, bool sendAuthCookie = false, int? timeout = null, bool isLocal = false) - { - Guard.NotEmpty(url, nameof(url)); - - url = WebHelper.GetAbsoluteUrl(url, _httpRequest); - - HttpWebRequest req; - - if (isLocal) - { - req = WebHelper.CreateHttpRequestForSafeLocalCall(new Uri(url)); - } - else - { - req = WebRequest.CreateHttp(url); - req.UserAgent = "SmartStore.NET"; - } - - if (timeout.HasValue) - { - req.Timeout = timeout.Value; - } - - if (sendAuthCookie) - { - req.SetFormsAuthenticationCookie(_httpRequest); - req.SetAnonymousIdentCookie(_httpRequest); - } - - using (var resp = (HttpWebResponse)req.GetResponse()) - using (var stream = resp.GetResponseStream()) - { - if (resp.StatusCode == HttpStatusCode.OK) - { - var data = stream.ToByteArray(); - if (data != null && data.Length != 0) - { - string fileName = null; - - var contentDisposition = resp.Headers["Content-Disposition"]; - if (contentDisposition.HasValue()) - { - var cd = new ContentDisposition(contentDisposition); - fileName = cd.FileName; - } - - if (fileName.IsEmpty()) - { - try - { - var uri = new Uri(url); - fileName = Path.GetFileName(uri.LocalPath); - } - catch { } - } - - return new FileDownloadResponse(data, fileName, resp.ContentType); - } - } - } - - return null; - } - - /// - /// Starts asynchronous download of files and saves them to disk - /// - /// Download context - /// Items to be downloaded - public async Task DownloadAsync(FileDownloadManagerContext context, IEnumerable items) - { - await DownloadFilesAsync(context, items); - } - - private async Task DownloadFilesAsync(FileDownloadManagerContext context, IEnumerable items) - { - try - { - using (var client = new HttpClient()) - { - client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue(); - client.DefaultRequestHeaders.CacheControl.NoCache = true; - client.DefaultRequestHeaders.Add("Connection", "Keep-alive"); - - if (context.Timeout.TotalMilliseconds > 0 && context.Timeout != Timeout.InfiniteTimeSpan) - client.Timeout = context.Timeout; - - IEnumerable downloadTasksQuery = - from item in items - select ProcessUrl(context, client, item); - - // now execute the bunch - List downloadTasks = downloadTasksQuery.ToList(); - - while (downloadTasks.Count > 0) - { - // identify the first task that completes - Task firstFinishedTask = await Task.WhenAny(downloadTasks); - - // process only once - downloadTasks.Remove(firstFinishedTask); - - await firstFinishedTask; - } - } - } - catch (Exception ex) - { + public class FileDownloadManager + { + private const int _bufferSize = 16384; + + private readonly HttpRequestBase _httpRequest; + + public FileDownloadManager(HttpRequestBase httpRequest) + { + _httpRequest = httpRequest; + } + + /// + /// Downloads a single file synchronously + /// + /// The URL to download the file from (either a fully qualified URL or an app relative/absolute path) + /// Specifies whether the FormsAuthenticationCookie should be sent + /// Timeout in milliseconds + /// Specifiers whether the file is located on the local server + public FileDownloadResponse DownloadFile(string url, bool sendAuthCookie = false, int? timeout = null, bool isLocal = false) + { + Guard.NotEmpty(url, nameof(url)); + + url = WebHelper.GetAbsoluteUrl(url, _httpRequest); + + HttpWebRequest req; + + if (isLocal) + { + req = WebHelper.CreateHttpRequestForSafeLocalCall(new Uri(url)); + } + else + { + req = WebRequest.CreateHttp(url); + req.UserAgent = "Smartstore"; + } + + if (timeout.HasValue) + { + req.Timeout = timeout.Value; + } + + if (sendAuthCookie) + { + req.SetFormsAuthenticationCookie(_httpRequest); + req.SetAnonymousIdentCookie(_httpRequest); + req.SetVisitorCookie(_httpRequest); + } + + using (var resp = (HttpWebResponse)req.GetResponse()) + using (var stream = resp.GetResponseStream()) + { + if (resp.StatusCode == HttpStatusCode.OK) + { + var data = stream.ToByteArray(); + if (data != null && data.Length != 0) + { + string fileName = null; + + var contentDisposition = resp.Headers["Content-Disposition"]; + if (contentDisposition.HasValue()) + { + var cd = new ContentDisposition(contentDisposition); + fileName = cd.FileName; + } + + if (fileName.IsEmpty()) + { + try + { + var uri = new Uri(url); + fileName = Path.GetFileName(uri.LocalPath); + } + catch { } + } + + return new FileDownloadResponse(data, fileName, resp.ContentType); + } + } + } + + return null; + } + + /// + /// Starts asynchronous download of files and saves them to disk + /// + /// Download context + /// Items to be downloaded + public async Task DownloadAsync(FileDownloadManagerContext context, IEnumerable items) + { + await DownloadFilesAsync(context, items); + } + + private async Task DownloadFilesAsync(FileDownloadManagerContext context, IEnumerable items) + { + try + { + using (var client = new HttpClient()) + { + client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue(); + client.DefaultRequestHeaders.CacheControl.NoCache = true; + client.DefaultRequestHeaders.Add("Connection", "Keep-alive"); + + if (context.Timeout.TotalMilliseconds > 0 && context.Timeout != Timeout.InfiniteTimeSpan) + client.Timeout = context.Timeout; + + IEnumerable downloadTasksQuery = + from item in items + select ProcessUrl(context, client, item); + + // now execute the bunch + List downloadTasks = downloadTasksQuery.ToList(); + + while (downloadTasks.Count > 0) + { + // identify the first task that completes + Task firstFinishedTask = await Task.WhenAny(downloadTasks); + + // process only once + downloadTasks.Remove(firstFinishedTask); + + await firstFinishedTask; + } + } + } + catch (Exception ex) + { if (context.Logger != null) { context.Logger.ErrorsAll(ex); } - } - } - - private async Task ProcessUrl(FileDownloadManagerContext context, HttpClient client, FileDownloadManagerItem item) - { - try - { - var count = 0; - var canceled = false; - var bytes = new byte[_bufferSize]; - - using (var response = await client.GetAsync(item.Url)) - { + } + } + + private async Task ProcessUrl(FileDownloadManagerContext context, HttpClient client, FileDownloadManagerItem item) + { + try + { + var count = 0; + var canceled = false; + var bytes = new byte[_bufferSize]; + + using (var response = await client.GetAsync(item.Url)) + { if (response.IsSuccessStatusCode) { if (response.Content.Headers.ContentType != null) @@ -199,15 +200,15 @@ private async Task ProcessUrl(FileDownloadManagerContext context, HttpClient cli item.ErrorMessage = response.StatusCode.ToString(); } } - } - catch (Exception ex) - { - try - { - item.Success = false; - item.ErrorMessage = ex.ToAllMessages(); - - var webExc = ex.InnerException as WebException; + } + catch (Exception ex) + { + try + { + item.Success = false; + item.ErrorMessage = ex.ToAllMessages(); + + var webExc = ex.InnerException as WebException; if (webExc != null) { item.ExceptionStatus = webExc.Status; @@ -217,135 +218,129 @@ private async Task ProcessUrl(FileDownloadManagerContext context, HttpClient cli { context.Logger.Error(ex, item.ToString()); } - } - catch { } - } - } - } - - - public class FileDownloadResponse - { - public FileDownloadResponse(byte[] data, string fileName, string contentType) - { - Guard.NotNull(data, nameof(data)); - - Data = data; - FileName = fileName; - ContentType = contentType; - } - - /// - /// The downloaded file's byte array - /// - public byte[] Data { get; private set; } - - /// - /// The file name - /// - public string FileName { get; private set; } - - /// - /// The mime type opf the downloaded file - /// - public string ContentType { get; private set; } - } - - public class FileDownloadManagerContext - { - /// - /// Optional logger to log errors - /// - public ILogger Logger { get; set; } - - /// - /// Cancellation token - /// - public CancellationToken CancellationToken { get; set; } - - /// - /// Timeout for the HTTP client - /// - public TimeSpan Timeout { get; set; } - } - - public class FileDownloadManagerItem - { - /// - /// Identifier of the item - /// - public int Id { get; set; } - - /// - /// New identifier of the downloaded item - /// - public int NewId { get; set; } - - /// - /// Display order of the item - /// - public int DisplayOrder { get; set; } - - /// - /// Download URL - /// - public string Url { get; set; } - - /// - /// Absolute path for saving the item - /// - public string Path { get; set; } - - /// - /// File name without file extension - /// - public string FileName { get; set; } - - /// - /// Mime type of the item - /// - public string MimeType { get; set; } - - /// - /// Whether the operation succeeded - /// - public bool? Success { get; set; } - - /// - /// Exception status if an exception of type WebException occurred - /// - public WebExceptionStatus ExceptionStatus { get; set; } - - /// - /// Error message - /// - public string ErrorMessage { get; set; } - - /// - /// Whether the operation timed out - /// - public bool HasTimedOut - { - get - { - return ExceptionStatus == WebExceptionStatus.Timeout || ExceptionStatus == WebExceptionStatus.RequestCanceled; - } - } - - /// - /// Use dictionary for any required extra data - /// - public IDictionary CustomProperties { get; set; } - - public override string ToString() - { - var str = "Result: {0} {1}{2}, {3}".FormatInvariant( - Success, - ExceptionStatus.ToString(), - ErrorMessage.HasValue() ? " ({0})".FormatInvariant(ErrorMessage) : "", - Path); - - return str; - } - } + } + catch { } + } + } + } + + + public class FileDownloadResponse + { + public FileDownloadResponse(byte[] data, string fileName, string contentType) + { + Guard.NotNull(data, nameof(data)); + + Data = data; + FileName = fileName; + ContentType = contentType; + } + + /// + /// The downloaded file's byte array + /// + public byte[] Data { get; private set; } + + /// + /// The file name + /// + public string FileName { get; private set; } + + /// + /// The mime type opf the downloaded file + /// + public string ContentType { get; private set; } + } + + public class FileDownloadManagerContext + { + /// + /// Optional logger to log errors + /// + public ILogger Logger { get; set; } + + /// + /// Cancellation token + /// + public CancellationToken CancellationToken { get; set; } + + /// + /// Timeout for the HTTP client + /// + public TimeSpan Timeout { get; set; } + } + + public class FileDownloadManagerItem + { + /// + /// Identifier of the item + /// + public int Id { get; set; } + + /// + /// New identifier of the downloaded item + /// + public int NewId { get; set; } + + /// + /// Display order of the item + /// + public int DisplayOrder { get; set; } + + /// + /// Download URL + /// + public string Url { get; set; } + + /// + /// Absolute path for saving the item + /// + public string Path { get; set; } + + /// + /// Name of downloaded file (including extension) + /// + public string FileName { get; set; } + + /// + /// Mime type of the item + /// + public string MimeType { get; set; } + + /// + /// Whether the operation succeeded + /// + public bool? Success { get; set; } + + /// + /// Exception status if an exception of type WebException occurred + /// + public WebExceptionStatus ExceptionStatus { get; set; } + + /// + /// Error message + /// + public string ErrorMessage { get; set; } + + /// + /// Whether the operation timed out + /// + public bool HasTimedOut => ExceptionStatus == WebExceptionStatus.Timeout || ExceptionStatus == WebExceptionStatus.RequestCanceled; + + /// + /// Use dictionary for any required extra data + /// + public IDictionary CustomProperties { get; set; } + + public override string ToString() + { + var str = "Result: {0} {1}{2}, {3}".FormatInvariant( + Success, + ExceptionStatus.ToString(), + ErrorMessage.HasValue() ? " ({0})".FormatInvariant(ErrorMessage) : "", + Path); + + return str; + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Utilities/FileSystemHelper.cs b/src/Libraries/SmartStore.Core/Utilities/FileSystemHelper.cs index 2ce13789c5..3eafe1afae 100644 --- a/src/Libraries/SmartStore.Core/Utilities/FileSystemHelper.cs +++ b/src/Libraries/SmartStore.Core/Utilities/FileSystemHelper.cs @@ -1,321 +1,319 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading; using SmartStore.Core.Data; namespace SmartStore.Utilities { public static class FileSystemHelper - { - private static readonly string _pathTemp = CommonHelper.MapPath(CommonHelper.GetAppSetting("sm:TempDirectory", "~/App_Data/_temp")); - private static readonly string _pathTempTenant = CommonHelper.MapPath(DataSettings.Current.TenantPath + "/_temp"); - - /// - /// Returns physical path to application temp directory - /// - /// Name of a sub directory to be created and returned (optional) - public static string TempDir(string subDirectory = null) - { - return TempDirInternal(_pathTemp, subDirectory); - } - - /// - /// Returns physical path to current tenant temp directory - /// - /// Name of a sub directory to be created and returned (optional) - public static string TempDirTenant(string subDirectory = null) - { - return TempDirInternal(_pathTempTenant, subDirectory); - } - - private static string TempDirInternal(string path, string subDirectory = null) - { - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path); - } - - if (subDirectory.HasValue()) - { - path = Path.Combine(path, subDirectory); - - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path); - } - } - - return path; - } + { + private static readonly string _pathTemp = CommonHelper.MapPath(CommonHelper.GetAppSetting("sm:TempDirectory", "~/App_Data/_temp")); + private static readonly string _pathTempTenant = CommonHelper.MapPath(DataSettings.Current.TenantPath + "/_temp"); + + /// + /// Returns physical path to application temp directory + /// + /// Name of a sub directory to be created and returned (optional) + public static string TempDir(string subDirectory = null) + { + return TempDirInternal(_pathTemp, subDirectory); + } + + /// + /// Returns physical path to current tenant temp directory + /// + /// Name of a sub directory to be created and returned (optional) + public static string TempDirTenant(string subDirectory = null) + { + return TempDirInternal(_pathTempTenant, subDirectory); + } + + private static string TempDirInternal(string path, string subDirectory = null) + { + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + if (subDirectory.HasValue()) + { + path = Path.Combine(path, subDirectory); + + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + } + + return path; + } /// /// Safe way to cleanup temporary directories. Should be called via scheduled task. /// public static void ClearTempDirectories() - { - var olderThan = TimeSpan.FromHours(5); - - ClearDirectory(new DirectoryInfo(TempDir()), false, olderThan); - ClearDirectory(new DirectoryInfo(TempDirTenant()), false, olderThan); - } - - /// - /// Safe way to delete all directory content - /// - /// Directory path - /// Delete dir too if it doesn't contain any entries after deletion anymore - /// Delete only files older than this TimeSpan - /// Name of files not to be deleted - public static void ClearDirectory(string path, bool deleteIfEmpfy, TimeSpan olderThan, params string[] exceptFileNames) - { - if (path.IsEmpty()) - return; - - ClearDirectory(new DirectoryInfo(path), deleteIfEmpfy, olderThan, exceptFileNames); - } - - /// - /// Safe way to delete all directory content - /// - /// Directory path - /// Delete dir too if it doesn't contain any entries after deletion anymore - /// Name of files not to be deleted - public static void ClearDirectory(string path, bool deleteIfEmpfy, params string[] exceptFileNames) - { - if (path.IsEmpty()) - return; - - ClearDirectory(new DirectoryInfo(path), deleteIfEmpfy, TimeSpan.Zero, exceptFileNames); - } - - /// - /// Safe way to delete all directory content - /// - /// Directory info object - /// Delete dir too if it doesn't contain any entries after deletion anymore - /// Name of files not to be deleted - public static void ClearDirectory(DirectoryInfo dir, bool deleteIfEmpfy, params string[] exceptFileNames) - { - Guard.NotNull(dir, nameof(dir)); - - ClearDirectory(dir, deleteIfEmpfy, TimeSpan.Zero, exceptFileNames); - } - - /// - /// Safe way to delete all directory content - /// - /// Directory info object - /// Delete dir too if it doesn't contain any entries after deletion anymore - /// Delete only files older than this TimeSpan - /// Name of files not to be deleted - public static void ClearDirectory(DirectoryInfo dir, bool deleteIfEmpfy, TimeSpan olderThan, params string[] exceptFileNames) - { - Guard.NotNull(dir, nameof(dir)); - - if (!dir.Exists) - return; - - var olderThanDate = DateTime.UtcNow.Subtract(olderThan); - - for (int i = 0; i < 10; i++) - { - try - { - foreach (var fsi in dir.EnumerateFileSystemInfos()) - { - if (fsi is FileInfo file) - { - if (file.LastWriteTimeUtc >= olderThanDate) - continue; - - if (exceptFileNames.Any(x => x.IsCaseInsensitiveEqual(file.Name))) - continue; - - if (file.IsReadOnly) - { - file.IsReadOnly = false; - } - - file.Delete(); - } - else if (fsi is DirectoryInfo subDir) - { - ClearDirectory(subDir, true, olderThan, exceptFileNames); - } - - } - - break; - } - catch (Exception ex) - { - ex.Dump(); - } - } - - if (deleteIfEmpfy) - { - try - { - if (!dir.EnumerateFileSystemInfos().Any()) - { - dir.Delete(true); - } - } - catch (Exception ex) - { - ex.Dump(); - } - } - } - - /// - /// Safe way to delete a file. - /// - public static bool DeleteFile(string path) - { - if (path.IsEmpty()) - return true; - - try - { - if (Directory.Exists(path)) - { - throw new UnauthorizedAccessException("Deleting folders not possible due to security reasons: {0}".FormatWith(path)); - } - - // No exception, if file doesn't exists - File.Delete(path); - return true; - } - catch (Exception ex) - { - ex.Dump(); - return false; - } - } - - /// - /// Safe way to copy a file. - /// - public static bool CopyFile(string sourcePath, string destinationPath, bool overwrite = true, bool deleteSource = false) - { - bool result = true; - try - { - File.Copy(sourcePath, destinationPath, overwrite); - - if (deleteSource) - DeleteFile(sourcePath); - } - catch (Exception exc) - { - result = false; - exc.Dump(); - } - - return result; - } - - /// - /// Safe way to copy a directory and all content. - /// - /// Source directory - /// Target directory - /// Whether to override existing files - public static bool CopyDirectory(DirectoryInfo source, DirectoryInfo target, bool overwrite = true) - { - if (target.FullName.Contains(source.FullName)) - { - // Cannot copy a folder into itself. - return false; - } - - var result = true; - - foreach (FileInfo fi in source.GetFiles()) - { - try - { - fi.CopyTo(Path.Combine(target.ToString(), fi.Name), overwrite); - } - catch (Exception ex) - { - result = false; - ex.Dump(); - } - } - - foreach (DirectoryInfo sourceSubDir in source.GetDirectories()) - { - try - { - DirectoryInfo targetSubDir = target.CreateSubdirectory(sourceSubDir.Name); - CopyDirectory(sourceSubDir, targetSubDir, overwrite); - } - catch (Exception ex) - { - result = false; - ex.Dump(); - } - } - - return result; - } - - /// - /// Creates a non existing directory name - /// - /// Path of a directory - /// Default name for directory. null to use a guid. - /// Non existing directory name - public static string CreateNonExistingDirectoryName(string directoryPath, string defaultName) - { - if (defaultName.IsEmpty()) - defaultName = Guid.NewGuid().ToString(); - - if (directoryPath.IsEmpty() || !Directory.Exists(directoryPath)) - return defaultName; - - var newName = defaultName; - - for (int i = 1; i < 999999 && Directory.Exists(Path.Combine(directoryPath, newName)); ++i) - { - newName = defaultName + i.ToString(); - } - - return newName; - } - - /// - /// Safe way to count files in a directory - /// - /// A directory path - /// File count - public static int CountFiles(string directoryPath) - { - try - { - return Directory.GetFiles(directoryPath).Length; - } - catch { } - - return 0; - } - - /// - /// Safe way to empty a file - /// - /// File path - public static void ClearFile(string path) - { - try - { - if (path.HasValue()) File.WriteAllText(path, ""); - } - catch { } - } - } + { + var olderThan = TimeSpan.FromHours(5); + + ClearDirectory(new DirectoryInfo(TempDir()), false, olderThan); + ClearDirectory(new DirectoryInfo(TempDirTenant()), false, olderThan); + } + + /// + /// Safe way to delete all directory content + /// + /// Directory path + /// Delete dir too if it doesn't contain any entries after deletion anymore + /// Delete only files older than this TimeSpan + /// Name of files not to be deleted + public static void ClearDirectory(string path, bool deleteIfEmpfy, TimeSpan olderThan, params string[] exceptFileNames) + { + if (path.IsEmpty()) + return; + + ClearDirectory(new DirectoryInfo(path), deleteIfEmpfy, olderThan, exceptFileNames); + } + + /// + /// Safe way to delete all directory content + /// + /// Directory path + /// Delete dir too if it doesn't contain any entries after deletion anymore + /// Name of files not to be deleted + public static void ClearDirectory(string path, bool deleteIfEmpfy, params string[] exceptFileNames) + { + if (path.IsEmpty()) + return; + + ClearDirectory(new DirectoryInfo(path), deleteIfEmpfy, TimeSpan.Zero, exceptFileNames); + } + + /// + /// Safe way to delete all directory content + /// + /// Directory info object + /// Delete dir too if it doesn't contain any entries after deletion anymore + /// Name of files not to be deleted + public static void ClearDirectory(DirectoryInfo dir, bool deleteIfEmpfy, params string[] exceptFileNames) + { + Guard.NotNull(dir, nameof(dir)); + + ClearDirectory(dir, deleteIfEmpfy, TimeSpan.Zero, exceptFileNames); + } + + /// + /// Safe way to delete all directory content + /// + /// Directory info object + /// Delete dir too if it doesn't contain any entries after deletion anymore + /// Delete only files older than this TimeSpan + /// Name of files not to be deleted + public static void ClearDirectory(DirectoryInfo dir, bool deleteIfEmpfy, TimeSpan olderThan, params string[] exceptFileNames) + { + Guard.NotNull(dir, nameof(dir)); + + if (!dir.Exists) + return; + + var olderThanDate = DateTime.UtcNow.Subtract(olderThan); + + for (int i = 0; i < 10; i++) + { + try + { + foreach (var fsi in dir.EnumerateFileSystemInfos()) + { + if (fsi is FileInfo file) + { + if (file.LastWriteTimeUtc >= olderThanDate) + continue; + + if (exceptFileNames.Any(x => x.IsCaseInsensitiveEqual(file.Name))) + continue; + + if (file.IsReadOnly) + { + file.IsReadOnly = false; + } + + file.Delete(); + } + else if (fsi is DirectoryInfo subDir) + { + ClearDirectory(subDir, true, olderThan, exceptFileNames); + } + + } + + break; + } + catch (Exception ex) + { + ex.Dump(); + } + } + + if (deleteIfEmpfy) + { + try + { + if (!dir.EnumerateFileSystemInfos().Any()) + { + dir.Delete(true); + } + } + catch (Exception ex) + { + ex.Dump(); + } + } + } + + /// + /// Safe way to delete a file. + /// + public static bool DeleteFile(string path) + { + if (path.IsEmpty()) + return true; + + try + { + if (Directory.Exists(path)) + { + throw new UnauthorizedAccessException("Deleting folders not possible due to security reasons: {0}".FormatWith(path)); + } + + // No exception, if file doesn't exists + File.Delete(path); + return true; + } + catch (Exception ex) + { + ex.Dump(); + return false; + } + } + + /// + /// Safe way to copy a file. + /// + public static bool CopyFile(string sourcePath, string destinationPath, bool overwrite = true, bool deleteSource = false) + { + bool result = true; + try + { + File.Copy(sourcePath, destinationPath, overwrite); + + if (deleteSource) + DeleteFile(sourcePath); + } + catch (Exception exc) + { + result = false; + exc.Dump(); + } + + return result; + } + + /// + /// Safe way to copy a directory and all content. + /// + /// Source directory + /// Target directory + /// Whether to overwrite existing files + public static bool CopyDirectory(DirectoryInfo source, DirectoryInfo target, bool overwrite = true) + { + if (target.FullName.EnsureEndsWith("\\").StartsWith(source.FullName.EnsureEndsWith("\\"), StringComparison.CurrentCultureIgnoreCase)) + { + // Cannot copy a folder into itself. + return false; + } + + var result = true; + + foreach (FileInfo fi in source.GetFiles()) + { + try + { + fi.CopyTo(Path.Combine(target.ToString(), fi.Name), overwrite); + } + catch (Exception ex) + { + result = false; + ex.Dump(); + } + } + + foreach (DirectoryInfo sourceSubDir in source.GetDirectories()) + { + try + { + DirectoryInfo targetSubDir = target.CreateSubdirectory(sourceSubDir.Name); + CopyDirectory(sourceSubDir, targetSubDir, overwrite); + } + catch (Exception ex) + { + result = false; + ex.Dump(); + } + } + + return result; + } + + /// + /// Creates a non existing directory name + /// + /// Path of a directory + /// Default name for directory. null to use a guid. + /// Non existing directory name + public static string CreateNonExistingDirectoryName(string directoryPath, string defaultName) + { + if (defaultName.IsEmpty()) + defaultName = Guid.NewGuid().ToString(); + + if (directoryPath.IsEmpty() || !Directory.Exists(directoryPath)) + return defaultName; + + var newName = defaultName; + + for (int i = 1; i < 999999 && Directory.Exists(Path.Combine(directoryPath, newName)); ++i) + { + newName = defaultName + i.ToString(); + } + + return newName; + } + + /// + /// Safe way to count files in a directory + /// + /// A directory path + /// File count + public static int CountFiles(string directoryPath) + { + try + { + return Directory.GetFiles(directoryPath).Length; + } + catch { } + + return 0; + } + + /// + /// Safe way to empty a file + /// + /// File path + public static void ClearFile(string path) + { + try + { + if (path.HasValue()) File.WriteAllText(path, ""); + } + catch { } + } + } } diff --git a/src/Libraries/SmartStore.Core/Utilities/HashCodeCombiner.cs b/src/Libraries/SmartStore.Core/Utilities/HashCodeCombiner.cs index 585f13a9ea..a3627f2a66 100644 --- a/src/Libraries/SmartStore.Core/Utilities/HashCodeCombiner.cs +++ b/src/Libraries/SmartStore.Core/Utilities/HashCodeCombiner.cs @@ -11,127 +11,126 @@ Copied over from Microsoft.Framework.Internal namespace SmartStore.Utilities { - public struct HashCodeCombiner - { - private long _combinedHash64; - - public int CombinedHash - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return _combinedHash64.GetHashCode(); } - } - - public string CombinedHashString - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return _combinedHash64.GetHashCode().ToString("x", CultureInfo.InvariantCulture); } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private HashCodeCombiner(long seed) - { - _combinedHash64 = seed; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public HashCodeCombiner Add(IEnumerable e) - { - if (e == null) - { - Add(0); - } - else - { - var count = 0; - foreach (object o in e) - { - Add(o); - count++; - } - Add(count); - } - return this; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator int (HashCodeCombiner self) - { - return self.CombinedHash; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public HashCodeCombiner Add(int i) - { - _combinedHash64 = ((_combinedHash64 << 5) + _combinedHash64) ^ i; - return this; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public HashCodeCombiner Add(string s) - { - var hashCode = (s != null) ? s.GetHashCode() : 0; - return Add(hashCode); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public HashCodeCombiner Add(object o) - { - var hashCode = (o != null) ? o.GetHashCode() : 0; - return Add(hashCode); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public HashCodeCombiner Add(DateTime d) - { - return Add(d.GetHashCode()); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public HashCodeCombiner Add(TValue value, IEqualityComparer comparer) - { - var hashCode = value != null ? comparer.GetHashCode(value) : 0; - return Add(hashCode); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public HashCodeCombiner Add(FileSystemInfo fi, bool deep = true) - { - Guard.NotNull(fi, nameof(fi)); - - if (!fi.Exists) - return this; - - Add(fi.FullName.ToLower()); - Add(fi.CreationTimeUtc); - Add(fi.LastWriteTimeUtc); - - if (fi is FileInfo file) - { - Add(file.Length.GetHashCode()); - } - - if (fi is DirectoryInfo dir) - { - foreach (var f in dir.GetFiles()) - { - Add(f); - } - if (deep) - { - foreach (var s in dir.GetDirectories()) - { - Add(s); - } - } - } - - return this; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static HashCodeCombiner Start() - { - return new HashCodeCombiner(0x1505L); - } - } + public struct HashCodeCombiner + { + private long _combinedHash64; + + public int CombinedHash + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _combinedHash64.GetHashCode(); + } + + public string CombinedHashString + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _combinedHash64.GetHashCode().ToString("x", CultureInfo.InvariantCulture); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private HashCodeCombiner(long seed) + { + _combinedHash64 = seed; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HashCodeCombiner Add(IEnumerable e) + { + if (e == null) + { + Add(0); + } + else + { + var count = 0; + foreach (object o in e) + { + Add(o); + count++; + } + Add(count); + } + return this; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator int(HashCodeCombiner self) + { + return self.CombinedHash; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HashCodeCombiner Add(int i) + { + _combinedHash64 = ((_combinedHash64 << 5) + _combinedHash64) ^ i; + return this; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HashCodeCombiner Add(string s) + { + var hashCode = (s != null) ? s.GetHashCode() : 0; + return Add(hashCode); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HashCodeCombiner Add(object o) + { + var hashCode = (o != null) ? o.GetHashCode() : 0; + return Add(hashCode); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HashCodeCombiner Add(DateTime d) + { + return Add(d.GetHashCode()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public HashCodeCombiner Add(TValue value, IEqualityComparer comparer) + { + var hashCode = value != null ? comparer.GetHashCode(value) : 0; + return Add(hashCode); + } + + public HashCodeCombiner Add(FileSystemInfo fi, bool deep = true) + { + Guard.NotNull(fi, nameof(fi)); + + if (!fi.Exists) + return this; + + Add(fi.FullName.ToLower()); + Add(fi.CreationTimeUtc); + Add(fi.LastWriteTimeUtc); + + if (fi is FileInfo file) + { + Add(file.Length.GetHashCode()); + } + + if (fi is DirectoryInfo dir) + { + foreach (var f in dir.GetFiles()) + { + Add(f); + } + if (deep) + { + foreach (var s in dir.GetDirectories()) + { + Add(s); + } + } + } + + return this; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static HashCodeCombiner Start() + { + return new HashCodeCombiner(0x1505L); + } + } } diff --git a/src/Libraries/SmartStore.Core/Utilities/ImagingHelper.cs b/src/Libraries/SmartStore.Core/Utilities/ImagingHelper.cs index 3d6ef4d3c5..2833cac71f 100644 --- a/src/Libraries/SmartStore.Core/Utilities/ImagingHelper.cs +++ b/src/Libraries/SmartStore.Core/Utilities/ImagingHelper.cs @@ -3,65 +3,65 @@ namespace SmartStore.Utilities { - public static class ImagingHelper - { - public static int GetPerceivedBrightness(string htmlColor) - { + public static class ImagingHelper + { + public static int GetPerceivedBrightness(string htmlColor) + { if (String.IsNullOrEmpty(htmlColor)) htmlColor = "#ffffff"; - + return GetPerceivedBrightness(ColorTranslator.FromHtml(htmlColor)); - } + } - /// - /// Calculates the perceived brightness of a color. - /// - /// The color - /// - /// A number in the range of 0 (black) to 255 (White). - /// For text contrast colors, an optimal cutoff value is 130. - /// - public static int GetPerceivedBrightness(Color color) - { - return (int)Math.Sqrt( - color.R * color.R * .241 + - color.G * color.G * .691 + - color.B * color.B * .068); - } + /// + /// Calculates the perceived brightness of a color. + /// + /// The color + /// + /// A number in the range of 0 (black) to 255 (White). + /// For text contrast colors, an optimal cutoff value is 130. + /// + public static int GetPerceivedBrightness(Color color) + { + return (int)Math.Sqrt( + color.R * color.R * .241 + + color.G * color.G * .691 + + color.B * color.B * .068); + } - /// - /// Recalculates an image size while keeping aspect ratio - /// - /// Original size - /// New max size - /// The rescaled size - public static Size Rescale(Size original, int maxSize) - { - Guard.IsPositive(maxSize, nameof(maxSize)); + /// + /// Recalculates an image size while keeping aspect ratio + /// + /// Original size + /// New max size + /// The rescaled size + public static Size Rescale(Size original, int maxSize) + { + Guard.IsPositive(maxSize, nameof(maxSize)); - return Rescale(original, new Size(maxSize, maxSize)); - } + return Rescale(original, new Size(maxSize, maxSize)); + } - /// - /// Recalculates an image size while keeping aspect ratio - /// - /// Original size - /// New max size - /// The rescaled size - public static Size Rescale(Size original, Size maxSize) - { - if (original.IsEmpty || maxSize.IsEmpty || (original.Width <= maxSize.Width && original.Height <= maxSize.Height)) - { - return original; - } + /// + /// Recalculates an image size while keeping aspect ratio + /// + /// Original size + /// New max size + /// The rescaled size + public static Size Rescale(Size original, Size maxSize) + { + if (original.IsEmpty || maxSize.IsEmpty || (original.Width <= maxSize.Width && original.Height <= maxSize.Height)) + { + return original; + } - // Figure out the ratio - double ratioX = (double)maxSize.Width / (double)original.Width; - double ratioY = (double)maxSize.Height / (double)original.Height; - // use whichever multiplier is smaller - double ratio = ratioX < ratioY ? ratioX : ratioY; + // Figure out the ratio + double ratioX = (double)maxSize.Width / (double)original.Width; + double ratioY = (double)maxSize.Height / (double)original.Height; + // use whichever multiplier is smaller + double ratio = ratioX < ratioY ? ratioX : ratioY; - return new Size(Convert.ToInt32(original.Width * ratio), Convert.ToInt32(original.Height * ratio)); - } - } + return new Size(Convert.ToInt32(original.Width * ratio), Convert.ToInt32(original.Height * ratio)); + } + } } diff --git a/src/Libraries/SmartStore.Core/Utilities/Inflector.cs b/src/Libraries/SmartStore.Core/Utilities/Inflector.cs index a9f1c73e39..dfac59b3a9 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Inflector.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Inflector.cs @@ -4,7 +4,6 @@ namespace SmartStore.Utilities { - /// /// Implementation of the Inflector in Ruby that transforms words from singular to plural, /// class names to table names, modularized class names to ones without, and class names to foreign keys @@ -12,14 +11,14 @@ namespace SmartStore.Utilities // TODO: Inflector ist leider Englisch! Irgend 'ne Chance das zu lokalisieren??!! public static class Inflector { - #region fields + #region fields - private static readonly Regex HtmlCaseRegex = new Regex( - "(? _plurals = new List(); + private static readonly List _plurals = new List(); private static readonly List _singulars = new List(); private static readonly List _uncountables = new List(); @@ -116,7 +115,7 @@ public string Apply(string word) } #endregion - #region private rule buolder methods + #region private rule builder methods private static void AddIrregular(string singular, string plural) { AddPlural("(" + singular[0] + ")" + singular.Substring(1) + "$", "$1" + plural.Substring(1)); @@ -186,7 +185,7 @@ public static string Singularize(string word) public static string Titleize(string word) { return Regex.Replace(Humanize(Underscore(word)), @"\b([a-z])", - delegate(Match match) + delegate (Match match) { return match.Captures[0].Value.ToUpper(); }); @@ -211,7 +210,7 @@ public static string Humanize(string lowercaseAndUnderscoredWord) public static string Pascalize(string lowercaseAndUnderscoredWord) { return Regex.Replace(lowercaseAndUnderscoredWord, "(?:^|_)(.)", - delegate(Match match) + delegate (Match match) { return match.Groups[1].Value.ToUpper(); }); @@ -227,17 +226,17 @@ public static string Camelize(string lowercaseAndUnderscoredWord) return Uncapitalize(Pascalize(lowercaseAndUnderscoredWord)); } - public static string Handleize(string input) - { - return HtmlCaseRegex.Replace(input, "-$1$2").ToLowerInvariant(); - } - - /// - /// Makes an underscored form from the expression in the string. - /// - /// string. The word to underscore. - /// string. The word with underscore seperators. - public static string Underscore(string pascalCasedWord) + public static string Handleize(string input) + { + return HtmlCaseRegex.Replace(input, "-$1$2").ToLowerInvariant(); + } + + /// + /// Makes an underscored form from the expression in the string. + /// + /// string. The word to underscore. + /// string. The word with underscore seperators. + public static string Underscore(string pascalCasedWord) { return Regex.Replace( Regex.Replace( @@ -303,7 +302,7 @@ public static string Dasherize(string underscoredWord) { return underscoredWord.Replace('_', '-'); } + #endregion } - } diff --git a/src/Libraries/SmartStore.Core/Utilities/ObjectDumper.cs b/src/Libraries/SmartStore.Core/Utilities/ObjectDumper.cs index 788859fa99..da44bc6362 100644 --- a/src/Libraries/SmartStore.Core/Utilities/ObjectDumper.cs +++ b/src/Libraries/SmartStore.Core/Utilities/ObjectDumper.cs @@ -5,26 +5,26 @@ namespace SmartStore.Core.Utilities { - public static class ObjectDumper - { - public static void Dump(object value, TextWriter writer) - { - writer.WriteLine(Dump(value)); - } + public static class ObjectDumper + { + public static void Dump(object value, TextWriter writer) + { + writer.WriteLine(Dump(value)); + } - public static string Dump(object value) - { - return JsonConvert.SerializeObject(value, Formatting.Indented); - } + public static string Dump(object value) + { + return JsonConvert.SerializeObject(value, Formatting.Indented); + } - public static void ToConsole(object value) - { - Console.WriteLine(Dump(value)); - } + public static void ToConsole(object value) + { + Console.WriteLine(Dump(value)); + } - public static void ToDebug(object value) - { - Debug.WriteLine(Dump(value)); - } - } + public static void ToDebug(object value) + { + Debug.WriteLine(Dump(value)); + } + } } diff --git a/src/Libraries/SmartStore.Core/Utilities/ObjectPools/IPooledObject.cs b/src/Libraries/SmartStore.Core/Utilities/ObjectPools/IPooledObject.cs new file mode 100644 index 0000000000..902051f3a3 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Utilities/ObjectPools/IPooledObject.cs @@ -0,0 +1,7 @@ +namespace SmartStore.Utilities.ObjectPools +{ + public interface IPooledObject + { + bool Return(); + } +} diff --git a/src/Libraries/SmartStore.Core/Utilities/ObjectPools/LeakTrackingObjectPool.cs b/src/Libraries/SmartStore.Core/Utilities/ObjectPools/LeakTrackingObjectPool.cs new file mode 100644 index 0000000000..bfd40d991f --- /dev/null +++ b/src/Libraries/SmartStore.Core/Utilities/ObjectPools/LeakTrackingObjectPool.cs @@ -0,0 +1,62 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace SmartStore.Utilities.ObjectPools +{ + public class LeakTrackingObjectPool : ObjectPool where T : class, IPooledObject + { + private readonly ConditionalWeakTable _trackers = new ConditionalWeakTable(); + private readonly ObjectPool _inner; + + public LeakTrackingObjectPool(ObjectPool inner) + { + Guard.NotNull(inner, nameof(inner)); + + _inner = inner; + } + + public override T Rent() + { + var value = _inner.Rent(); + _trackers.Add(value, new Tracker()); + return value; + } + + public override bool Return(T obj) + { + if (_trackers.TryGetValue(obj, out var tracker)) + { + _trackers.Remove(obj); + tracker.Dispose(); + } + + return _inner.Return(obj); + } + + private class Tracker : IDisposable + { + private readonly string _stack; + private bool _disposed; + + public Tracker() + { + _stack = Environment.StackTrace; + } + + public void Dispose() + { + _disposed = true; + GC.SuppressFinalize(this); + } + + ~Tracker() + { + if (!_disposed && !Environment.HasShutdownStarted) + { + Debug.Fail($"{typeof(T).Name} was leaked. Created at: {Environment.NewLine}{_stack}"); + } + } + } + } +} diff --git a/src/Libraries/SmartStore.Core/Utilities/ObjectPools/ObjectPool.cs b/src/Libraries/SmartStore.Core/Utilities/ObjectPools/ObjectPool.cs new file mode 100644 index 0000000000..29cdf9e79f --- /dev/null +++ b/src/Libraries/SmartStore.Core/Utilities/ObjectPools/ObjectPool.cs @@ -0,0 +1,168 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace SmartStore.Utilities.ObjectPools +{ + /// + /// A pool of objects. + /// + /// The type of objects to pool. + /// + /// This implementation keeps a cache of retained objects. This means that if objects are returned + /// when the pool has already reached "poolSize" objects they will be available to be Garbage Collected. + /// + public class ObjectPool : IDisposable where T : class, IPooledObject + { + private volatile bool _isDisposed; + private readonly Func _activator; + + // Storage for the pool objects. The first item is stored in a dedicated field because we + // expect to be able to satisfy most requests from it. + internal protected T _firstItem; + internal protected readonly ObjectHolder[] _items; + + /// + /// Creates an instance of . + /// + /// The factory that creates an instance of + /// The maximum number of objects to retain in the pool. Default: ProcessorCount * 4. + public ObjectPool(Func activator = null, int? poolSize = null) + { + // -1 due to _firstItem + _items = new ObjectHolder[(poolSize ?? Environment.ProcessorCount * 4) - 1]; + _activator = activator ?? (() => Activator.CreateInstance()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + protected virtual T Create() + { + if (_isDisposed) + { + throw Error.ObjectDisposed(GetType().Name); + } + + return _activator(); + } + + /// + /// Gets an object from the pool if one is available, otherwise creates one. + /// + /// A . + public virtual T Rent() + { + // PERF: Examine the first element. If that fails, RentSlow will look at the remaining elements. + // Note that the initial read is optimistically not synchronized. That is intentional. + // We will interlock only when we have a candidate. In a worst case we may miss some + // recently returned objects. Not a big deal. + var item = _firstItem; + + if (item == null || item != Interlocked.CompareExchange(ref _firstItem, null, item)) + { + item = RentSlow(); + } + + return item; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private T RentSlow() + { + var items = _items; + for (var i = 0; i < items.Length; i++) + { + var item = items[i].Value; + if (item != null && item == Interlocked.CompareExchange(ref items[i].Value, null, item)) + { + return item; + } + } + + return Create(); + } + + /// + /// Return an object to the pool. + /// + /// The object to return to the pool. + /// Whether free slots were available and the object has been returned to the pool. + public virtual bool Return(T obj) + { + if (_isDisposed) + { + DisposeItem(obj); + return false; + } + + bool returned; + if (_firstItem == null) + { + // Intentionally not using interlocked here. + // In a worst case scenario two objects may be stored into same slot. + // It is very unlikely to happen and will only mean that one of the objects will get collected. + _firstItem = obj; + returned = true; + } + else + { + returned = ReturnSlow(obj); + } + + if (!returned) + { + DisposeItem(obj); + } + + return returned; + } + + private bool ReturnSlow(T obj) + { + var items = _items; + for (var i = 0; i < items.Length; ++i) + { + if (items[i].Value == null) + { + // Intentionally not using interlocked here. + // In a worst case scenario two objects may be stored into same slot. + // It is very unlikely to happen and will only mean that one of the objects will get collected. + items[i].Value = obj; + return true; + } + } + + return false; + } + + public void Dispose() + { + _isDisposed = true; + + DisposeItem(_firstItem); + _firstItem = null; + + var items = _items; + for (var i = 0; i < items.Length; i++) + { + DisposeItem(items[i].Value); + items[i].Value = null; + } + } + + private void DisposeItem(T item) + { + if (item is IDisposable disposable) + { + disposable.Dispose(); + } + } + + // PERF: the struct wrapper avoids array-covariance-checks from the runtime when assigning to elements of the array. + [DebuggerDisplay("{Value}")] + internal protected struct ObjectHolder + { + public T Value; + } + } +} diff --git a/src/Libraries/SmartStore.Core/Utilities/ObjectPools/PooledStringBuilder.cs b/src/Libraries/SmartStore.Core/Utilities/ObjectPools/PooledStringBuilder.cs new file mode 100644 index 0000000000..fca491cc22 --- /dev/null +++ b/src/Libraries/SmartStore.Core/Utilities/ObjectPools/PooledStringBuilder.cs @@ -0,0 +1,146 @@ +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace SmartStore.Utilities.ObjectPools +{ + public sealed class PooledStringBuilder : IPooledObject + { + public readonly StringBuilder Builder; + private readonly ObjectPool _pool; + + private PooledStringBuilder(ObjectPool pool, int? capacity, int? maxCapacity) + { + Debug.Assert(pool != null); + + InitialCapacity = capacity ?? DefaultInitialCapacity; + MaxCapacity = maxCapacity ?? DefaultMaxCapacity; + Builder = new StringBuilder(InitialCapacity); + + _pool = pool; + } + + #region Static + + /// + /// The default initial capacity of builder. + /// + public const int DefaultInitialCapacity = 100; + + /// + /// The default maximum capacity of builder. + /// + public const int DefaultMaxCapacity = 4 * 1024; + + private static readonly ObjectPool _defaultPool = CreatePool(); + + public static ObjectPool CreatePool(int? capacity = null, int? maxCapacity = null, int? poolSize = null) + { + ObjectPool pool = null; + pool = new ObjectPool(() => new PooledStringBuilder(pool, capacity, maxCapacity), poolSize); + return pool; + } + + public static PooledStringBuilder Rent(string value = null) + { + var builder = _defaultPool.Rent(); + Debug.Assert(builder.Builder.Length == 0); + + if (!string.IsNullOrEmpty(value)) + { + builder.Builder.Append(value); + } + + return builder; + } + + #endregion + + #region Instance + + public int InitialCapacity { get; private set; } + + public int MaxCapacity { get; private set; } + + public int Length => this.Builder.Length; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public StringBuilder Append(string value) + { + return this.Builder.Append(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public StringBuilder Append(object value) + { + return this.Builder.Append(value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public StringBuilder AppendLine() + { + return this.Builder.AppendLine(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public StringBuilder AppendFormat(string format, object arg0) + { + return this.Builder.AppendFormat(format, arg0); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public StringBuilder AppendFormat(string format, params object[] args) + { + return this.Builder.AppendFormat(format, args); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public StringBuilder AppendLine(string value) + { + return this.Builder.AppendLine(value); + } + + public bool Return() + { + var builder = this.Builder; + + // Do not return builders that are too large. + if (builder.Capacity <= MaxCapacity) + { + builder.Clear(); + _pool.Return(this); + return true; + } + + return false; + } + + public override string ToString() + { + return this.Builder.ToString(); + } + + public string ToStringAndReturn() + { + var result = this.Builder.ToString(); + this.Return(); + + return result; + } + + public string ToStringAndReturn(int startIndex, int length) + { + var result = this.Builder.ToString(startIndex, length); + this.Return(); + + return result; + } + + public static implicit operator StringBuilder(PooledStringBuilder obj) + { + return obj.Builder; + } + + #endregion + } +} diff --git a/src/Libraries/SmartStore.Core/Utilities/PathHelper.cs b/src/Libraries/SmartStore.Core/Utilities/PathHelper.cs index d1cacbacbf..18159409ee 100644 --- a/src/Libraries/SmartStore.Core/Utilities/PathHelper.cs +++ b/src/Libraries/SmartStore.Core/Utilities/PathHelper.cs @@ -1,202 +1,221 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; namespace SmartStore.Utilities { - public static class PathHelper - { - private static readonly char[] _invalidPathChars; - private static readonly char[] _invalidFileNameChars; - private static readonly Regex _invalidCharsPattern; - - static PathHelper() - { - _invalidPathChars = Path.GetInvalidPathChars(); - _invalidFileNameChars = Path.GetInvalidFileNameChars(); - - var invalidChars = Regex.Escape(new string(_invalidPathChars) + new string(_invalidFileNameChars)); - _invalidCharsPattern = new Regex(string.Format(@"[{0}]+", invalidChars)); - } - - /// - /// Checks whether path is empty or starts with '/' or '\' - /// - public static bool IsRootedPath(string basepath) - { - return (string.IsNullOrEmpty(basepath) || basepath[0] == '/' || basepath[0] == '\\'); - } - - /// - /// Checks whether path is starts with '~/' - /// - public static bool IsAppRelativePath(string path) - { - if (path == null) - return false; - - int len = path.Length; - - // Empty string case - if (len == 0) return false; - - // It must start with ~ - if (path[0] != '~') - return false; - - // Single character case: "~" - if (len == 1) - return true; - - // If it's longer, checks if it starts with "~/" or "~\" - return path[1] == '\\' || path[1] == '/'; - } - - /// - /// Checks if virtual path contains a protocol, which is referred to as a scheme in the - /// URI spec. - /// - public static bool HasScheme(string virtualPath) - { - // URIs have the format :, e.g. mailto:user@ms.com, - // http://server/, nettcp://server/, etc. The cannot contain slashes. - // The virtualPath passed to this method may be absolute or relative. Although - // ':' is only allowed in the if it is encoded, the - // virtual path that we're receiving here may be decoded, so it is impossible - // for us to determine if virtualPath has a scheme. We will be conservative - // and err on the side of assuming it has a scheme when we cannot tell for certain. - // To do this, we first check for ':'. If not found, then it doesn't have a scheme. - // If ':' is found, then as long as we find a '/' before the ':', it cannot be - // a scheme because schemes don't contain '/'. Otherwise, we will assume it has a - // scheme. - int indexOfColon = virtualPath.IndexOf(':'); - if (indexOfColon == -1) - return false; - int indexOfSlash = virtualPath.IndexOf('/'); - return (indexOfSlash == -1 || indexOfColon < indexOfSlash); - } - - /// - /// Ensures that a path is a valid app root path by checking whether it starts - /// with '~/' and prepending it if needed and by replacing '\' with '/' - /// - /// Relative path - /// Normalized root path - public static string NormalizeAppRelativePath(string path) - { - if (path.IsEmpty()) - return path; - - path = path.Replace('\\', '/'); - - if (!path.StartsWith("~/")) - { - if (path.StartsWith("~")) - path = path.Substring(1); - - path = (path.StartsWith("/") ? "~" : "~/") + path; - } - - return path; - } - - /// - /// Checks whether a path is a safe app root path. - /// - /// Relative path - public static bool IsSafeAppRootPath(string path) - { - if (path.EmptyNull().Length > 2 && !path.IsCaseInsensitiveEqual("con") && !HasInvalidPathChars(path)) - { - try - { - var mappedPath = CommonHelper.MapPath(path); - var appPath = CommonHelper.MapPath("~/"); - return !mappedPath.IsCaseInsensitiveEqual(appPath); - } - catch { } - } - - return false; - } - - /// - /// Replaces all occurences of any illegal path or file name char by '-' - /// - /// Path/File name - /// Sanitized path/file name - public static string SanitizeFileName(string name) - { - if (name.IsEmpty()) - return name; - - return _invalidCharsPattern.Replace(name, "-"); - } - - public static bool HasInvalidPathChars(string path, bool checkWildcardChars = false) - { - if (path == null) - return false; - - return path.IndexOfAny(_invalidPathChars) >= 0 - || (checkWildcardChars && ContainsWildcardChars(path, 0)); - } - - public static bool HasInvalidFileNameChars(string fileName, bool checkWildcardChars = false) - { - if (fileName == null) - return false; - - return fileName.IndexOfAny(_invalidFileNameChars) >= 0 - || (checkWildcardChars && ContainsWildcardChars(fileName, 0)); - } - - private static bool ContainsWildcardChars(string path, int startIndex = 0) - { - for (int i = startIndex; i < path.Length; i++) - { - switch (path[i]) - { - case '*': - case '?': - return true; - } - } - - return false; - } - - /// - /// Checks whether the given path is a fully qualified absolute path (either UNC or rooted with drive letter) - /// - /// Path to check - /// true if path is fully qualified - public static bool IsAbsolutePhysicalPath(string path) - { - if ((path == null) || (path.Length < 3)) - { - return false; - } - - return (((path[1] == ':') && IsDirectorySeparatorChar(path[2])) || IsUncSharePath(path)); - } - - internal static bool IsUncSharePath(string path) => - (((path.Length > 2) && IsDirectorySeparatorChar(path[0])) && IsDirectorySeparatorChar(path[1])); - - - private static bool IsDirectorySeparatorChar(char ch) - { - if (ch != '\\') - { - return (ch == '/'); - } - - return true; - } - } + public static class PathHelper + { + private static readonly char[] _invalidPathChars; + private static readonly char[] _invalidFileNameChars; + private static readonly Regex _invalidCharsPattern; + + static PathHelper() + { + _invalidPathChars = Path.GetInvalidPathChars(); + _invalidFileNameChars = Path.GetInvalidFileNameChars(); + + var invalidChars = Regex.Escape(new string(_invalidPathChars) + new string(_invalidFileNameChars)); + _invalidCharsPattern = new Regex(string.Format(@"[{0}]+", invalidChars)); + } + + /// + /// Checks whether path is empty or starts with '/' or '\' + /// + public static bool IsRootedPath(string basepath) + { + return (string.IsNullOrEmpty(basepath) || basepath[0] == '/' || basepath[0] == '\\'); + } + + /// + /// Checks whether path starts with '~/' + /// + public static bool IsAppRelativePath(string path) + { + if (path == null) + return false; + + int len = path.Length; + + // Empty string case + if (len == 0) return false; + + // It must start with ~ + if (path[0] != '~') + return false; + + // Single character case: "~" + if (len == 1) + return true; + + // If it's longer, checks if it starts with "~/" or "~\" + return path[1] == '\\' || path[1] == '/'; + } + + /// + /// Determines the relative path from to + /// + /// From path + /// To path + /// Directory separator + /// The relative path + public static string MakeRelativePath(string fromPath, string toPath, string sep = "\\") + { + var fromParts = fromPath.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries); + var toParts = toPath.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries); + + var matchedParts = fromParts + .Zip(toParts, (x, y) => string.Compare(x, y, true) == 0) + .TakeWhile(x => x).Count(); + + return string.Join("", Enumerable.Range(0, fromParts.Length - matchedParts) + .Select(x => ".." + sep)) + + string.Join(sep, toParts.Skip(matchedParts)); + } + + /// + /// Checks if virtual path contains a protocol, which is referred to as a scheme in the + /// URI spec. + /// + public static bool HasScheme(string virtualPath) + { + // URIs have the format :, e.g. mailto:user@ms.com, + // http://server/, nettcp://server/, etc. The cannot contain slashes. + // The virtualPath passed to this method may be absolute or relative. Although + // ':' is only allowed in the if it is encoded, the + // virtual path that we're receiving here may be decoded, so it is impossible + // for us to determine if virtualPath has a scheme. We will be conservative + // and err on the side of assuming it has a scheme when we cannot tell for certain. + // To do this, we first check for ':'. If not found, then it doesn't have a scheme. + // If ':' is found, then as long as we find a '/' before the ':', it cannot be + // a scheme because schemes don't contain '/'. Otherwise, we will assume it has a + // scheme. + int indexOfColon = virtualPath.IndexOf(':'); + if (indexOfColon == -1) + return false; + int indexOfSlash = virtualPath.IndexOf('/'); + return (indexOfSlash == -1 || indexOfColon < indexOfSlash); + } + + /// + /// Ensures that a path is a valid app root path by checking whether it starts + /// with '~/' and prepending it if needed and by replacing '\' with '/' + /// + /// Relative path + /// Normalized root path + public static string NormalizeAppRelativePath(string path) + { + if (path.IsEmpty()) + return path; + + path = path.Replace('\\', '/'); + + if (!path.StartsWith("~/")) + { + if (path.StartsWith("~")) + path = path.Substring(1); + + path = (path.StartsWith("/") ? "~" : "~/") + path; + } + + return path; + } + + /// + /// Checks whether a path is a safe app root path. + /// + /// Relative path + public static bool IsSafeAppRootPath(string path) + { + if (path.EmptyNull().Length > 2 && !path.IsCaseInsensitiveEqual("con") && !HasInvalidPathChars(path)) + { + try + { + var mappedPath = CommonHelper.MapPath(path); + var appPath = CommonHelper.MapPath("~/"); + return !mappedPath.IsCaseInsensitiveEqual(appPath); + } + catch { } + } + + return false; + } + + /// + /// Replaces all occurences of any illegal path or file name char by '-' + /// + /// Path/File name + /// Sanitized path/file name + public static string SanitizeFileName(string name) + { + if (name.IsEmpty()) + return name; + + return _invalidCharsPattern.Replace(name, "-"); + } + + public static bool HasInvalidPathChars(string path, bool checkWildcardChars = false) + { + if (path == null) + return false; + + return path.IndexOfAny(_invalidPathChars) >= 0 + || (checkWildcardChars && ContainsWildcardChars(path, 0)); + } + + public static bool HasInvalidFileNameChars(string fileName, bool checkWildcardChars = false) + { + if (fileName == null) + return false; + + return fileName.IndexOfAny(_invalidFileNameChars) >= 0 + || (checkWildcardChars && ContainsWildcardChars(fileName, 0)); + } + + private static bool ContainsWildcardChars(string path, int startIndex = 0) + { + for (int i = startIndex; i < path.Length; i++) + { + switch (path[i]) + { + case '*': + case '?': + return true; + } + } + + return false; + } + + /// + /// Checks whether the given path is a fully qualified absolute path (either UNC or rooted with drive letter) + /// + /// Path to check + /// true if path is fully qualified + public static bool IsAbsolutePhysicalPath(string path) + { + if ((path == null) || (path.Length < 3)) + { + return false; + } + + return (((path[1] == ':') && IsDirectorySeparatorChar(path[2])) || IsUncSharePath(path)); + } + + internal static bool IsUncSharePath(string path) + { + return (((path.Length > 2) && IsDirectorySeparatorChar(path[0])) && IsDirectorySeparatorChar(path[1])); + } + + private static bool IsDirectorySeparatorChar(char ch) + { + if (ch != '\\') + { + return (ch == '/'); + } + + return true; + } + } } diff --git a/src/Libraries/SmartStore.Core/Utilities/Prettifier.cs b/src/Libraries/SmartStore.Core/Utilities/Prettifier.cs index d0d4cc2307..95e9c181a5 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Prettifier.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Prettifier.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Linq; using System.Text; using System.Xml; @@ -10,8 +8,8 @@ namespace SmartStore.Utilities { public static class Prettifier { - [SuppressMessage("ReSharper", "RedundantAssignment")] - public static string BytesToString(long bytes) + [SuppressMessage("ReSharper", "RedundantAssignment")] + public static string BytesToString(long bytes) { double result = bytes; double dsize = bytes; @@ -46,7 +44,7 @@ public static string PrettifyXML(string xml) { if (xml.IsEmpty() || xml.IsWhiteSpace()) return xml; - + // first read the xml ignoring whitespace using (var xmlReader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings { IgnoreWhitespace = true, CheckCharacters = false })) { @@ -60,9 +58,6 @@ public static string PrettifyXML(string xml) var result = sb.ToString(); return result; } - } - } - } diff --git a/src/Libraries/SmartStore.Core/Utilities/Range.cs b/src/Libraries/SmartStore.Core/Utilities/Range.cs index 42780fbe9a..7b10f3e6c3 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Range.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Range.cs @@ -4,7 +4,6 @@ namespace SmartStore.Utilities { - /// /// Helper class with shortcut methods for managing enumerables. /// Useful for inlining object generation in tests @@ -135,5 +134,4 @@ public static int[] Array(int count) return array; } } - } diff --git a/src/Libraries/SmartStore.Core/Utilities/Retry.cs b/src/Libraries/SmartStore.Core/Utilities/Retry.cs index 916b64e3b2..a7ec849ce6 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Retry.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Retry.cs @@ -3,140 +3,140 @@ namespace SmartStore.Utilities { - /// - /// Provides functionality to automatically try the given piece of logic some number of times before re-throwing the exception. - /// This is useful for any piece of code which may experience transient failures. Be cautious of passing code with two distinct - /// actions given that if the second or subsequent piece of logic fails, the first will also be retried upon each retry. - /// - public static class Retry - { - /// - /// Executes an action with retry logic. - /// - /// The action to be executed. - /// The maximum number of attempts. - /// Timespan to wait between attempts of the operation - /// The callback executed when an attempt is failed. - public static void Run(Action operation, int attempts, TimeSpan? wait = null, Action onFailed = null) - { - Guard.NotNull(operation, nameof(operation)); - - Run(Operation, attempts, wait, onFailed); - - bool Operation() - { - operation(); - return true; - } - } - - /// - /// Executes a function with retry logic. - /// - /// The function to be executed. - /// The maximum number of attempts. - /// Timespan to wait between attempts of the operation - /// The callback executed when an attempt is failed. - public static T Run(Func operation, int attempts, TimeSpan? wait, Action onFailed = null) - { - Guard.NotNull(operation, nameof(operation)); - - if (attempts < 1) - { - throw new ArgumentOutOfRangeException(nameof(attempts), attempts, "The maximum number of attempts must not be less than 1."); - } - - var attempt = 0; - - while (true) - { - if (attempt > 0 && wait != null) - { - Task.Delay(wait.Value).Wait(); - } - - try - { - // Call the function passed in by the caller. - return operation(); - } - catch (Exception ex) - { - attempt++; - - onFailed?.Invoke(attempt, ex); - - if (attempt >= attempts) - { - throw; - } - } - } - } - - - /// - /// Executes an asynchronous action with retry logic. - /// - /// The asynchronous action to be executed. - /// The maximum number of attempts. - /// Timespan to wait between attempts of the operation - /// The callback executed when an attempt is failed. - public static async Task RunAsync(Func operation, int attempts, TimeSpan? wait = null, Action onFailed = null) - { - Guard.NotNull(operation, nameof(operation)); - - async Task wrapper() - { - await operation(); - return true; - } - - await RunAsync(wrapper, attempts, wait, onFailed); - } - - /// - /// Executes an asynchronous function with retry logic. - /// - /// The asynchronous function to be executed. - /// The maximum number of attempts. - /// Timespan to wait between attempts of the operation - /// The callback executed when an attempt is failed. - public static async Task RunAsync(Func> operation, int attempts, TimeSpan? wait, Action onFailed = null) - { - Guard.NotNull(operation, nameof(operation)); - - if (attempts < 1) - { - throw new ArgumentOutOfRangeException(nameof(attempts), attempts, "The maximum number of attempts must not be less than 1."); - } - - var attempt = 0; - - while (true) - { - if (attempt > 0 && wait != null) - { - await Task.Delay(wait.Value); - } - - try - { - // Call the function passed in by the caller. - return await operation(); - } - catch (Exception ex) - { - attempt++; - - onFailed?.Invoke(attempt, ex); - - if (attempt >= attempts) - { - throw; - } - } - } - } - } + /// + /// Provides functionality to automatically try the given piece of logic some number of times before re-throwing the exception. + /// This is useful for any piece of code which may experience transient failures. Be cautious of passing code with two distinct + /// actions given that if the second or subsequent piece of logic fails, the first will also be retried upon each retry. + /// + public static class Retry + { + /// + /// Executes an action with retry logic. + /// + /// The action to be executed. + /// The maximum number of attempts. + /// Timespan to wait between attempts of the operation + /// The callback executed when an attempt is failed. + public static void Run(Action operation, int attempts, TimeSpan? wait = null, Action onFailed = null) + { + Guard.NotNull(operation, nameof(operation)); + + Run(Operation, attempts, wait, onFailed); + + bool Operation() + { + operation(); + return true; + } + } + + /// + /// Executes a function with retry logic. + /// + /// The function to be executed. + /// The maximum number of attempts. + /// Timespan to wait between attempts of the operation + /// The callback executed when an attempt is failed. + public static T Run(Func operation, int attempts, TimeSpan? wait, Action onFailed = null) + { + Guard.NotNull(operation, nameof(operation)); + + if (attempts < 1) + { + throw new ArgumentOutOfRangeException(nameof(attempts), attempts, "The maximum number of attempts must not be less than 1."); + } + + var attempt = 0; + + while (true) + { + if (attempt > 0 && wait != null) + { + Task.Delay(wait.Value).Wait(); + } + + try + { + // Call the function passed in by the caller. + return operation(); + } + catch (Exception ex) + { + attempt++; + + onFailed?.Invoke(attempt, ex); + + if (attempt >= attempts) + { + throw; + } + } + } + } + + + /// + /// Executes an asynchronous action with retry logic. + /// + /// The asynchronous action to be executed. + /// The maximum number of attempts. + /// Timespan to wait between attempts of the operation + /// The callback executed when an attempt is failed. + public static async Task RunAsync(Func operation, int attempts, TimeSpan? wait = null, Action onFailed = null) + { + Guard.NotNull(operation, nameof(operation)); + + async Task wrapper() + { + await operation(); + return true; + } + + await RunAsync(wrapper, attempts, wait, onFailed); + } + + /// + /// Executes an asynchronous function with retry logic. + /// + /// The asynchronous function to be executed. + /// The maximum number of attempts. + /// Timespan to wait between attempts of the operation + /// The callback executed when an attempt is failed. + public static async Task RunAsync(Func> operation, int attempts, TimeSpan? wait, Action onFailed = null) + { + Guard.NotNull(operation, nameof(operation)); + + if (attempts < 1) + { + throw new ArgumentOutOfRangeException(nameof(attempts), attempts, "The maximum number of attempts must not be less than 1."); + } + + var attempt = 0; + + while (true) + { + if (attempt > 0 && wait != null) + { + await Task.Delay(wait.Value); + } + + try + { + // Call the function passed in by the caller. + return await operation(); + } + catch (Exception ex) + { + attempt++; + + onFailed?.Invoke(attempt, ex); + + if (attempt >= attempts) + { + throw; + } + } + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Utilities/SeoHelper.cs b/src/Libraries/SmartStore.Core/Utilities/SeoHelper.cs index 721b1a38b8..b3d32b4486 100644 --- a/src/Libraries/SmartStore.Core/Utilities/SeoHelper.cs +++ b/src/Libraries/SmartStore.Core/Utilities/SeoHelper.cs @@ -2,28 +2,31 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Runtime.CompilerServices; using System.Text; +using SmartStore.Utilities.ObjectPools; namespace SmartStore.Utilities { - public static class SeoHelper - { - private static readonly object _lock = new object(); - private static Dictionary _userSeoCharacterTable; - - /// - /// Get SEO friendly string - /// - /// String to be converted - /// A value indicating whether non western chars should be converted - /// A value indicating whether Unicode chars are allowed - /// Raw data of semicolon separated char conversions - /// SEO friendly string - [SuppressMessage("ReSharper", "PossibleNullReferenceException")] - public static string GetSeName(string name, bool convertNonWesternChars, bool allowUnicodeChars, string charConversions = null) - { + public static class SeoHelper + { + private static readonly object _lock = new object(); + private static Dictionary _userSeoCharacterTable; + + /// + /// Get SEO friendly string + /// + /// String to be converted + /// A value indicating whether non western chars should be converted + /// A value indicating whether Unicode chars are allowed + /// Raw data of semicolon separated char conversions + /// SEO friendly string + [SuppressMessage("ReSharper", "PossibleNullReferenceException")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetSeName(string name, bool convertNonWesternChars, bool allowUnicodeChars, string charConversions = null) + { return GetSeName(name, convertNonWesternChars, allowUnicodeChars, true, charConversions); - } + } /// /// Get SEO friendly string @@ -36,10 +39,10 @@ public static string GetSeName(string name, bool convertNonWesternChars, bool al /// SEO friendly string [SuppressMessage("ReSharper", "PossibleNullReferenceException")] public static string GetSeName( - string name, - bool convertNonWesternChars, - bool allowUnicodeChars, - bool allowForwardSlash, + string name, + bool convertNonWesternChars, + bool allowUnicodeChars, + bool allowForwardSlash, string charConversions = null) { // Return empty value if text is null @@ -56,7 +59,8 @@ public static string GetSeName( name = name.ToLowerInvariant(); var len = name.Length; - var sb = new StringBuilder(len); + var psb = PooledStringBuilder.Rent(); + var sb = (StringBuilder)psb; var prevdash = false; char c; @@ -121,37 +125,42 @@ public static string GetSeName( } if (prevdash) - return sb.ToString().Substring(0, sb.Length - 1).Trim('/'); + { + len = sb.Length; + return psb.ToStringAndReturn().Substring(0, len - 1).Trim('/'); + } else - return sb.ToString().Trim('/'); + { + return psb.ToStringAndReturn().Trim('/'); + } } public static void ResetUserSeoCharacterTable() - { - if (_userSeoCharacterTable != null) - { - _userSeoCharacterTable.Clear(); - _userSeoCharacterTable = null; - } - } - - private static void InitializeUserSeoCharacterTable(string charConversions) - { - lock (_lock) - { - if (_userSeoCharacterTable == null) - { - _userSeoCharacterTable = new Dictionary(); - - foreach (var conversion in charConversions.SplitSafe(Environment.NewLine)) - { - if (conversion.SplitToPair(out var strLeft, out var strRight, ";") && strLeft.HasValue() && !_userSeoCharacterTable.ContainsKey(strLeft[0])) - { - _userSeoCharacterTable.Add(strLeft[0], strRight); - } - } - } - } - } - } + { + if (_userSeoCharacterTable != null) + { + _userSeoCharacterTable.Clear(); + _userSeoCharacterTable = null; + } + } + + private static void InitializeUserSeoCharacterTable(string charConversions) + { + lock (_lock) + { + if (_userSeoCharacterTable == null) + { + _userSeoCharacterTable = new Dictionary(); + + foreach (var conversion in charConversions.SplitSafe(Environment.NewLine)) + { + if (conversion.SplitToPair(out var strLeft, out var strRight, ";") && strLeft.HasValue() && !_userSeoCharacterTable.ContainsKey(strLeft[0])) + { + _userSeoCharacterTable.Add(strLeft[0], strRight); + } + } + } + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Utilities/SmartSyndicationFeed.cs b/src/Libraries/SmartStore.Core/Utilities/SmartSyndicationFeed.cs index fe2a4dc1b4..b850c568ea 100644 --- a/src/Libraries/SmartStore.Core/Utilities/SmartSyndicationFeed.cs +++ b/src/Libraries/SmartStore.Core/Utilities/SmartSyndicationFeed.cs @@ -7,78 +7,77 @@ namespace SmartStore.Utilities { - public class SmartSyndicationFeed : SyndicationFeed - { - public SmartSyndicationFeed(Uri feedAlternateLink, string title, string description = null) - : base(title, description ?? title, feedAlternateLink, null, DateTime.UtcNow) - { - } + public class SmartSyndicationFeed : SyndicationFeed + { + public SmartSyndicationFeed(Uri feedAlternateLink, string title, string description = null) + : base(title, description ?? title, feedAlternateLink, null, DateTime.UtcNow) + { + } - public static string UrlAtom { get { return "http://www.w3.org/2005/Atom"; } } - public static string UrlPurlContent { get { return "http://purl.org/rss/1.0/modules/content/"; } } + public static string UrlAtom => "http://www.w3.org/2005/Atom"; + public static string UrlPurlContent => "http://purl.org/rss/1.0/modules/content/"; - public void AddNamespaces(bool purlContent) - { - this.AttributeExtensions.Add(new XmlQualifiedName("atom", XNamespace.Xmlns.NamespaceName), UrlAtom); + public void AddNamespaces(bool purlContent) + { + AttributeExtensions.Add(new XmlQualifiedName("atom", XNamespace.Xmlns.NamespaceName), UrlAtom); - if (purlContent) - { - this.AttributeExtensions.Add(new XmlQualifiedName("content", XNamespace.Xmlns.ToString()), UrlPurlContent); - } - } + if (purlContent) + { + AttributeExtensions.Add(new XmlQualifiedName("content", XNamespace.Xmlns.ToString()), UrlPurlContent); + } + } - public void Init(string selfLink, Language language = null) - { - this.ElementExtensions.Add( - new XElement(((XNamespace)UrlAtom) + "link", - new XAttribute("href", selfLink), - new XAttribute("rel", "self"), - new XAttribute("type", "application/rss+xml"))); + public void Init(string selfLink, Language language = null) + { + ElementExtensions.Add( + new XElement(((XNamespace)UrlAtom) + "link", + new XAttribute("href", selfLink), + new XAttribute("rel", "self"), + new XAttribute("type", "application/rss+xml"))); - if (language != null) - { - this.Language = language.LanguageCulture.EmptyNull().ToLower(); - } - } + if (language != null) + { + Language = language.LanguageCulture.EmptyNull().ToLower(); + } + } - public SyndicationItem CreateItem(string title, string synopsis, string url, DateTimeOffset published, string contentEncoded = null) - { - var item = new SyndicationItem( - title.RemoveInvalidXmlChars().EmptyNull(), - synopsis.RemoveInvalidXmlChars().EmptyNull(), - new Uri(url), - url, - published); + public SyndicationItem CreateItem(string title, string synopsis, string url, DateTimeOffset published, string contentEncoded = null) + { + var item = new SyndicationItem( + title.RemoveInvalidXmlChars().EmptyNull(), + synopsis.RemoveInvalidXmlChars().EmptyNull(), + new Uri(url), + url, + published); - if (contentEncoded != null) - { - item.ElementExtensions.Add("encoded", UrlPurlContent, contentEncoded.RemoveInvalidXmlChars().EmptyNull()); - } + if (contentEncoded != null) + { + item.ElementExtensions.Add("encoded", UrlPurlContent, contentEncoded.RemoveInvalidXmlChars().EmptyNull()); + } - return item; - } + return item; + } - public bool AddEnclosure(SyndicationItem item, Picture picture, string pictureUrl) - { - if (picture != null && pictureUrl.HasValue()) - { - // 0 omits the length attribute but that invalidates the feed - long pictureLength = 10000; + public bool AddEnclosure(SyndicationItem item, MediaFile file, string fileUrl) + { + if (file != null && fileUrl.HasValue()) + { + // We do not have the size behind fileUrl but the original file size should be fine. + long fileLength = file.Size; + if (fileLength <= 0) + { + // 0 omits the length attribute but that invalidates the feed! + fileLength = 10000; + } - if ((picture.MediaStorageId ?? 0) != 0) - { - // TODO: (mc) But what about other storage provider? - // do not care about storage provider - pictureLength = picture.MediaStorage.Data.LongLength; - } + var enclosure = SyndicationLink.CreateMediaEnclosureLink(new Uri(fileUrl), file.MimeType.EmptyNull(), fileLength); - var enclosure = SyndicationLink.CreateMediaEnclosureLink(new Uri(pictureUrl), picture.MimeType.EmptyNull(), pictureLength); + item.Links.Add(enclosure); - item.Links.Add(enclosure); + return true; + } - return true; - } - return false; - } - } + return false; + } + } } diff --git a/src/Libraries/SmartStore.Core/Utilities/StringTokenizer.cs b/src/Libraries/SmartStore.Core/Utilities/StringTokenizer.cs index 4e863c3e87..72cd4bf8b6 100644 --- a/src/Libraries/SmartStore.Core/Utilities/StringTokenizer.cs +++ b/src/Libraries/SmartStore.Core/Utilities/StringTokenizer.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections; +using System.Collections; using System.Collections.Generic; -using System.Linq; -using System.Text; namespace SmartStore.Utilities { - public class StringTokenizer : IEnumerable { private const string _defaultDelim = " \t\n\r\f"; @@ -91,5 +87,4 @@ IEnumerator IEnumerable.GetEnumerator() #endregion } - } diff --git a/src/Libraries/SmartStore.Core/Utilities/Threading/KeyedLock.cs b/src/Libraries/SmartStore.Core/Utilities/Threading/KeyedLock.cs index e08a6deb1c..adaaec5992 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Threading/KeyedLock.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Threading/KeyedLock.cs @@ -6,114 +6,122 @@ namespace SmartStore.Utilities.Threading { - public sealed class KeyedLock - { - private static readonly TimeSpan _noTimeout = TimeSpan.FromMilliseconds(-1); - private static readonly ConcurrentDictionary _locks = new ConcurrentDictionary(); - - private int _waiterCount; - - private KeyedLock(object key) - { - Key = key; - Semaphore = new SemaphoreSlim(1, 1); - } - - private object Key { get; set; } - private SemaphoreSlim Semaphore { get; set; } - - public bool HasWaiters - { - get { return _waiterCount > 1; } - } - - public int WaiterCount - { - get { return _waiterCount; } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void IncrementCount() => Interlocked.Increment(ref _waiterCount); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void DecrementCount() => Interlocked.Decrement(ref _waiterCount); - - public static bool IsLockHeld(object key) - { - return _locks.ContainsKey(key); - } - - public static IDisposable Lock(object key) - { - return Lock(key, _noTimeout); - } - - public static IDisposable Lock(object key, TimeSpan timeout) - { - var keyedLock = GetOrCreateLock(key); - keyedLock.Semaphore.Wait(timeout); - return new Releaser(keyedLock); - } - - public static Task LockAsync(object key) - { - return LockAsync(key, _noTimeout, CancellationToken.None); - } - - public static Task LockAsync(object key, TimeSpan timeout) - { - return LockAsync(key, timeout, CancellationToken.None); - } - - public static Task LockAsync(object key, CancellationToken cancellationToken) - { - return LockAsync(key, _noTimeout, cancellationToken); - } - - public static Task LockAsync(object key, TimeSpan timeout, CancellationToken cancellationToken) - { - var keyedLock = GetOrCreateLock(key); - var wait = keyedLock.Semaphore.WaitAsync(timeout, cancellationToken); - - return wait.IsCompleted - ? Task.FromResult(new Releaser(keyedLock) as IDisposable) - : wait.ContinueWith((t, s) => new Releaser((KeyedLock)s) as IDisposable, - keyedLock, - CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, - TaskScheduler.Default); - } - - private static KeyedLock GetOrCreateLock(object key) - { - Guard.NotNull(key, nameof(key)); - - var item = _locks.GetOrAdd(key, k => new KeyedLock(key)); - item.IncrementCount(); - return item; - } - - sealed class Releaser : IDisposable - { - private KeyedLock _item; - - public Releaser(KeyedLock item) - { - _item = item; - } - - public void Dispose() - { - _item.DecrementCount(); - if (_item.WaiterCount == 0) - { - // Remove from dict - _locks.TryRemove(_item.Key, out _); - } - - _item.Semaphore.Release(); - _item = null; - } - } - } + public sealed class KeyedLock + { + private static readonly TimeSpan _noTimeout = TimeSpan.FromMilliseconds(-1); + private static readonly ConcurrentDictionary _locks = new ConcurrentDictionary(); + + private int _waiterCount; + + private KeyedLock(object key) + { + Key = key; + Semaphore = new SemaphoreSlim(1, 1); + } + + private object Key { get; set; } + private SemaphoreSlim Semaphore { get; set; } + + public bool HasWaiters => _waiterCount > 1; + + public int WaiterCount => _waiterCount; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void IncrementCount() + { + Interlocked.Increment(ref _waiterCount); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DecrementCount() + { + Interlocked.Decrement(ref _waiterCount); + } + + public static bool IsLockHeld(object key) + { + return _locks.ContainsKey(key); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IDisposable Lock(object key) + { + return Lock(key, _noTimeout); + } + + public static IDisposable Lock(object key, TimeSpan timeout) + { + var keyedLock = GetOrCreateLock(key); + keyedLock.Semaphore.Wait(timeout); + return new Releaser(keyedLock); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Task LockAsync(object key) + { + return LockAsync(key, _noTimeout, CancellationToken.None); + } + + public static Task LockAsync(object key, TimeSpan timeout) + { + return LockAsync(key, timeout, CancellationToken.None); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Task LockAsync(object key, CancellationToken cancellationToken) + { + return LockAsync(key, _noTimeout, cancellationToken); + } + + public static Task LockAsync(object key, TimeSpan timeout, CancellationToken cancellationToken) + { + var keyedLock = GetOrCreateLock(key); + var wait = keyedLock.Semaphore.WaitAsync(timeout, cancellationToken); + + return wait.IsCompleted + ? Task.FromResult(new Releaser(keyedLock) as IDisposable) + : wait.ContinueWith((t, s) => new Releaser((KeyedLock)s) as IDisposable, + keyedLock, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously, + TaskScheduler.Default); + } + + private static KeyedLock GetOrCreateLock(object key) + { + Guard.NotNull(key, nameof(key)); + + var item = _locks.GetOrAdd(key, k => new KeyedLock(key)); + item.IncrementCount(); + return item; + } + + sealed class Releaser : IDisposable + { + private KeyedLock _item; + + public Releaser(KeyedLock item) + { + _item = item; + } + + public void Dispose() + { + _item.DecrementCount(); + if (_item.WaiterCount == 0) + { + // Remove from dict + _locks.TryRemove(_item.Key, out _); + } + + if (_item.Semaphore.CurrentCount == 0) + { + _item.Semaphore.Release(); + } + + //_item.Semaphore.Dispose(); + _item = null; + } + } + } } diff --git a/src/Libraries/SmartStore.Core/Utilities/Threading/LockExtensions.cs b/src/Libraries/SmartStore.Core/Utilities/Threading/LockExtensions.cs index 4f634920e0..4e4abcf113 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Threading/LockExtensions.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Threading/LockExtensions.cs @@ -1,6 +1,7 @@ using System; -using System.Threading; using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; namespace SmartStore.Utilities.Threading { @@ -10,6 +11,7 @@ public static class LockExtensions /// Acquires a disposable reader lock that can be used with a using statement. /// [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IDisposable GetReadLock(this ReaderWriterLockSlim rwLock) { return rwLock.GetReadLock(-1); @@ -42,6 +44,7 @@ public static IDisposable GetReadLock(this ReaderWriterLockSlim rwLock, int mill /// Acquires a disposable and upgradeable reader lock that can be used with a using statement. /// [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IDisposable GetUpgradeableReadLock(this ReaderWriterLockSlim rwLock) { return rwLock.GetUpgradeableReadLock(-1); @@ -74,6 +77,7 @@ public static IDisposable GetUpgradeableReadLock(this ReaderWriterLockSlim rwLoc /// Acquires a disposable writer lock that can be used with a using statement. /// [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static IDisposable GetWriteLock(this ReaderWriterLockSlim rwLock) { return rwLock.GetWriteLock(-1); diff --git a/src/Libraries/SmartStore.Core/Utilities/Throttle.cs b/src/Libraries/SmartStore.Core/Utilities/Throttle.cs index d18b698186..a203d90faf 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Throttle.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Throttle.cs @@ -1,72 +1,70 @@ using System; -using System.Collections.Generic; using System.Collections.Concurrent; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Runtime.CompilerServices; namespace SmartStore.Core.Utilities { - public static class Throttle - { - private readonly static ConcurrentDictionary _checks = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + public static class Throttle + { + private readonly static ConcurrentDictionary _checks = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - /// - /// Performs a throttled check. - /// - /// Identifier for the check process - /// Interval between actual checks - /// The check factory - /// Check result - public static bool Check(string key, TimeSpan interval, Func check) - { - return Check(key, interval, false, check); - } + /// + /// Performs a throttled check. + /// + /// Identifier for the check process + /// Interval between actual checks + /// The check factory + /// Check result + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Check(string key, TimeSpan interval, Func check) + { + return Check(key, interval, false, check); + } - /// - /// Performs a throttled check. - /// - /// Identifier for the check process - /// Interval between actual checks - /// - /// The check factory - /// Check result - public static bool Check(string key, TimeSpan interval, bool recheckWhenFalse, Func check) - { - Guard.NotEmpty(key, nameof(key)); - Guard.NotNull(check, nameof(check)); + /// + /// Performs a throttled check. + /// + /// Identifier for the check process + /// Interval between actual checks + /// + /// The check factory + /// Check result + public static bool Check(string key, TimeSpan interval, bool recheckWhenFalse, Func check) + { + Guard.NotEmpty(key, nameof(key)); + Guard.NotNull(check, nameof(check)); - bool added = false; - var now = DateTime.UtcNow; + bool added = false; + var now = DateTime.UtcNow; - var entry = _checks.GetOrAdd(key, x => - { - added = true; - return new CheckEntry { Value = check(), NextCheckUtc = (now + interval) }; - }); + var entry = _checks.GetOrAdd(key, x => + { + added = true; + return new CheckEntry { Value = check(), NextCheckUtc = (now + interval) }; + }); - if (added) - { - return entry.Value; - } + if (added) + { + return entry.Value; + } - var ok = entry.Value; - var isOverdue = (!ok && recheckWhenFalse) || (now > entry.NextCheckUtc); + var ok = entry.Value; + var isOverdue = (!ok && recheckWhenFalse) || (now > entry.NextCheckUtc); - if (isOverdue) - { - // Check is overdue: recheck - ok = check(); - _checks.TryUpdate(key, new CheckEntry { Value = ok, NextCheckUtc = (now + interval) }, entry); - } + if (isOverdue) + { + // Check is overdue: recheck + ok = check(); + _checks.TryUpdate(key, new CheckEntry { Value = ok, NextCheckUtc = (now + interval) }, entry); + } - return ok; - } + return ok; + } - class CheckEntry - { - public bool Value { get; set; } - public DateTime NextCheckUtc { get; set; } - } - } + class CheckEntry + { + public bool Value { get; set; } + public DateTime NextCheckUtc { get; set; } + } + } } diff --git a/src/Libraries/SmartStore.Core/Utilities/TypeHelper.cs b/src/Libraries/SmartStore.Core/Utilities/TypeHelper.cs index 2fb3f5bc09..6fbef73089 100644 --- a/src/Libraries/SmartStore.Core/Utilities/TypeHelper.cs +++ b/src/Libraries/SmartStore.Core/Utilities/TypeHelper.cs @@ -1,11 +1,4 @@ using System; -using System.Collections.Generic; -using System.Reflection; -using System.Collections; -using System.ComponentModel; -using System.Linq; -using System.Globalization; -using System.IO; using System.Linq.Expressions; namespace SmartStore.Utilities @@ -37,25 +30,25 @@ public static Type GetElementType(Type type) return type; } - /// - /// Exctracts and returns the name of a property accessor lambda - /// - /// The containing type - /// The accessor lambda - /// When true, returns the result as '[TyoeName].[PropertyName]'. - /// The property name - public static string NameOf(Expression> propertyAccessor, bool includeTypeName = false) - { - Guard.NotNull(propertyAccessor, nameof(propertyAccessor)); + /// + /// Exctracts and returns the name of a property accessor lambda + /// + /// The containing type + /// The accessor lambda + /// When true, returns the result as '[TyoeName].[PropertyName]'. + /// The property name + public static string NameOf(Expression> propertyAccessor, bool includeTypeName = false) + { + Guard.NotNull(propertyAccessor, nameof(propertyAccessor)); - var name = propertyAccessor.ExtractPropertyInfo().Name; + var name = propertyAccessor.ExtractPropertyInfo().Name; - if (includeTypeName) - { - return typeof(T).Name + "." + name; - } + if (includeTypeName) + { + return typeof(T).Name + "." + name; + } - return name; - } + return name; + } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Core/Utilities/Wildcard.cs b/src/Libraries/SmartStore.Core/Utilities/Wildcard.cs index 9e7f21007e..3878d286fa 100644 --- a/src/Libraries/SmartStore.Core/Utilities/Wildcard.cs +++ b/src/Libraries/SmartStore.Core/Utilities/Wildcard.cs @@ -1,8 +1,9 @@ using System; -using System.Text; using System.Linq; +using System.Text; using System.Text.RegularExpressions; using System.Threading; +using SmartStore.Utilities.ObjectPools; namespace SmartStore.Utilities { @@ -19,18 +20,18 @@ public class Wildcard : Regex /// private static bool m_isForward; - private readonly string _pattern; + private readonly string _pattern; - /// - /// Initializes a new instance of the class. - /// - /// The wildcard pattern. - /// - /// Specifies whether number ranges (e.g. 1234-5678) should - /// be converted to a regular expression pattern. - /// - public Wildcard(string pattern, bool parseNumberRanges = false) - : this(pattern, RegexOptions.None, parseNumberRanges) + /// + /// Initializes a new instance of the class. + /// + /// The wildcard pattern. + /// + /// Specifies whether number ranges (e.g. 1234-5678) should + /// be converted to a regular expression pattern. + /// + public Wildcard(string pattern, bool parseNumberRanges = false) + : this(pattern, RegexOptions.None, parseNumberRanges) { } @@ -43,25 +44,19 @@ public Wildcard(string pattern, bool parseNumberRanges = false) /// be converted to a regular expression pattern. /// /// The regular expression options. - public Wildcard(string pattern, RegexOptions options, bool parseNumberRanges = false) - : this(WildcardToRegex(pattern, parseNumberRanges), options, Timeout.InfiniteTimeSpan) + public Wildcard(string pattern, RegexOptions options, bool parseNumberRanges = false) + : this(WildcardToRegex(pattern, parseNumberRanges), options, Timeout.InfiniteTimeSpan) { - + } - internal Wildcard(string parsedPattern, RegexOptions options, TimeSpan matchTimeout) - : base(parsedPattern, options, matchTimeout) - { - _pattern = parsedPattern; - } + internal Wildcard(string parsedPattern, RegexOptions options, TimeSpan matchTimeout) + : base(parsedPattern, options, matchTimeout) + { + _pattern = parsedPattern; + } - public string Pattern - { - get - { - return _pattern; - } - } + public string Pattern => _pattern; /// /// Searches all number range terms and converts them @@ -73,26 +68,26 @@ private static string WildcardToRegex(string pattern, bool parseNumberRanges) { m_isForward = true; - // Replace ? with . and * with .* - // Prepend ^, append $ - // Escape all chars except []^ - pattern = ToGlobPattern(pattern); - - // convert the number ranges into regular expression - if (parseNumberRanges) - { - var re = new Regex("[0-9]+-[0-9]+"); - MatchCollection collection = re.Matches(pattern); - foreach (Match match in collection) - { - string[] split = match.Value.Split(new char[] { '-' }); - int leadingZeroesCount = split[0].TakeWhile(x => x == '0').Count(); - int min = Int32.Parse(split[0]); - int max = Int32.Parse(split[1]); - - pattern = pattern.Replace(match.Value, ConvertNumberRange(min, max, leadingZeroesCount)); - } - } + // Replace ? with . and * with .* + // Prepend ^, append $ + // Escape all chars except []^ + pattern = ToGlobPattern(pattern); + + // convert the number ranges into regular expression + if (parseNumberRanges) + { + var re = new Regex("[0-9]+-[0-9]+"); + MatchCollection collection = re.Matches(pattern); + foreach (Match match in collection) + { + string[] split = match.Value.Split(new char[] { '-' }); + int leadingZeroesCount = split[0].TakeWhile(x => x == '0').Count(); + int min = Int32.Parse(split[0]); + int max = Int32.Parse(split[1]); + + pattern = pattern.Replace(match.Value, ConvertNumberRange(min, max, leadingZeroesCount)); + } + } return pattern; } @@ -113,43 +108,42 @@ private static string ConvertNumberRange(int min, int max, int leadingZeroesCoun string prefix = new String('0', leadingZeroesCount); // for leading zeroes string pattern = string.Empty; - if (min > -1 && max - min < 10) - { - // special treatment: the below function has issues with too small ranges - for (var i = min; i <= max; i++) - { - pattern += prefix + i.ToString() + (i < max ? "|" : ""); - } - } - else - { - - int currentValue = min; - int tempMax = 0; - int radix = 1; - - while (m_isForward || radix != 0) - { - tempMax = GetNextMaximum(currentValue, max, radix); - - if (tempMax >= currentValue) - { - pattern += prefix + ParseRange(currentValue, tempMax, radix); - if (!(!m_isForward && radix == 1)) - { - pattern += "|"; - } - } - radix += (m_isForward ? 1 : -1); - currentValue = tempMax + 1; - } - } + if (min > -1 && max - min < 10) + { + // special treatment: the below function has issues with too small ranges + for (var i = min; i <= max; i++) + { + pattern += prefix + i.ToString() + (i < max ? "|" : ""); + } + } + else + { + + int currentValue = min; + int radix = 1; + + while (m_isForward || radix != 0) + { + int tempMax = GetNextMaximum(currentValue, max, radix); + + if (tempMax >= currentValue) + { + pattern += prefix + ParseRange(currentValue, tempMax, radix); + if (!(!m_isForward && radix == 1)) + { + pattern += "|"; + } + } + radix += (m_isForward ? 1 : -1); + currentValue = tempMax + 1; + } + } //add a negative look behind and a negative look ahead in order //to avoid that 122-321 is found in 2308. return @"((? /// Gets the next maximum value in condition to the current value. /// @@ -161,7 +155,7 @@ private static string ConvertNumberRange(int min, int max, int leadingZeroesCoun /// private static int GetNextMaximum(int currentValue, int maximum, int radix) { - //backward + //backward if (!m_isForward) { if (radix != 1) @@ -179,7 +173,7 @@ private static int GetNextMaximum(int currentValue, int maximum, int radix) radix--; tempMax = ((maximum / (int)Math.Pow(10, radix))) * (int)Math.Pow(10, radix) - 1; } - + return tempMax; } @@ -216,12 +210,12 @@ private static string GetRangePattern(int beginDigit, int endDigit, int count) { if (count == 0) { - return string.Empty; + return string.Empty; } if (beginDigit == endDigit) { - return beginDigit.ToString(); + return beginDigit.ToString(); } string pattern = string.Format("[{0}-{1}]", beginDigit, endDigit); @@ -244,27 +238,27 @@ private static int ExtractDigit(int value, int digit) return Int32.Parse(value.ToString()[digit].ToString()); } - #region Escaping + #region Escaping - /* -------------------------------------------------------------- + /* -------------------------------------------------------------- Stuff here partly copied over from .NET's internal RegexParser class and modified for performance reasons: we don't want to escape '[^]' chars, but Regex.Escape() does. Besides, wen need '*' and '?' as wildcard chars. -------------------------------------------------------------- */ - const byte W = 6; // wildcard char - const byte Q = 5; // quantifier - const byte S = 4; // ordinary stopper - const byte Z = 3; // ScanBlank stopper - const byte X = 2; // whitespace - const byte E = 1; // should be escaped + const byte W = 6; // wildcard char + const byte Q = 5; // quantifier + const byte S = 4; // ordinary stopper + const byte Z = 3; // ScanBlank stopper + const byte X = 2; // whitespace + const byte E = 1; // should be escaped - /* + /* * For categorizing ASCII characters. */ - private static readonly byte[] _category = new byte[] - { + private static readonly byte[] _category = new byte[] + { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 6 7 8 9 A B C D E F 0,0,0,0,0,0,0,0,0,X,X,0,X,X,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @@ -273,83 +267,85 @@ Stuff here partly copied over from .NET's internal RegexParser 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,S,0,0,0, // ' a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,Q,S,0,0,0 - }; - - private static bool IsMetachar(char ch) - { - return (ch <= '|' && _category[ch] >= E); - } - - private static bool IsGlob(char ch) - { - return (ch <= '|' && _category[ch] >= W); - } - - private static string ToGlobPattern(string input) - { - Guard.NotNull(input, nameof(input)); - - for (int i = 0; i < input.Length; i++) - { - if (IsMetachar(input[i])) - { - var sb = new StringBuilder("^"); - char ch = input[i]; - int lastpos; - - sb.Append(input, 0, i); - do - { - if (IsGlob(ch)) - { - sb.Append('.'); // '?' > '.' - if (ch == '*') sb.Append('*'); // '*' > '.*' - } - else - { - sb.Append('\\'); - switch (ch) - { - case '\n': - ch = 'n'; - break; - case '\r': - ch = 'r'; - break; - case '\t': - ch = 't'; - break; - case '\f': - ch = 'f'; - break; - } - sb.Append(ch); - } - - i++; - lastpos = i; - - while (i < input.Length) - { - ch = input[i]; - if (IsMetachar(ch)) - break; - - i++; - } - - sb.Append(input, lastpos, i - lastpos); - } while (i < input.Length); - - sb.Append('$'); - return sb.ToString(); - } - } - - return '^' + input + '$'; - } - - #endregion - } + }; + + private static bool IsMetachar(char ch) + { + return (ch <= '|' && _category[ch] >= E); + } + + private static bool IsGlob(char ch) + { + return (ch <= '|' && _category[ch] >= W); + } + + private static string ToGlobPattern(string input) + { + Guard.NotNull(input, nameof(input)); + + for (int i = 0; i < input.Length; i++) + { + if (IsMetachar(input[i])) + { + var psb = PooledStringBuilder.Rent("^"); + var sb = (StringBuilder)psb; + + char ch = input[i]; + int lastpos; + + sb.Append(input, 0, i); + do + { + if (IsGlob(ch)) + { + sb.Append('.'); // '?' > '.' + if (ch == '*') sb.Append('*'); // '*' > '.*' + } + else + { + sb.Append('\\'); + switch (ch) + { + case '\n': + ch = 'n'; + break; + case '\r': + ch = 'r'; + break; + case '\t': + ch = 't'; + break; + case '\f': + ch = 'f'; + break; + } + sb.Append(ch); + } + + i++; + lastpos = i; + + while (i < input.Length) + { + ch = input[i]; + if (IsMetachar(ch)) + break; + + i++; + } + + sb.Append(input, lastpos, i - lastpos); + } while (i < input.Length); + + sb.Append('$'); + return psb.ToStringAndReturn(); + } + } + + return '^' + input + '$'; + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/WebHelper.cs b/src/Libraries/SmartStore.Core/WebHelper.cs index e1a5c32510..14a332af68 100644 --- a/src/Libraries/SmartStore.Core/WebHelper.cs +++ b/src/Libraries/SmartStore.Core/WebHelper.cs @@ -1,8 +1,13 @@ using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Configuration; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Web; @@ -12,33 +17,28 @@ using SmartStore.Core.Domain.Stores; using SmartStore.Core.Infrastructure; using SmartStore.Utilities; -using System.Net; -using System.Text; -using System.Collections.Generic; -using System.Collections.Concurrent; -using System.Net.Sockets; namespace SmartStore.Core { public partial class WebHelper : IWebHelper { - private static object s_lock = new object(); - private static bool? s_optimizedCompilationsEnabled; - private static AspNetHostingPermissionLevel? s_trustLevel; - private static readonly Regex s_staticExts = new Regex(@"(.*?)\.(css|js|png|jpg|jpeg|gif|webp|liquid|bmp|html|htm|xml|pdf|doc|xls|rar|zip|7z|ico|eot|svg|ttf|woff|woff2|otf)", RegexOptions.Compiled | RegexOptions.IgnoreCase); - private static readonly Regex s_htmlPathPattern = new Regex(@"(?<=(?:href|src)=(?:""|'))(?!https?://)(?[^(?:""|')]+)", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Multiline); - private static readonly Regex s_cssPathPattern = new Regex(@"url\('(?.+)'\)", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Multiline); - private static ConcurrentDictionary s_safeLocalHostNames = new ConcurrentDictionary(); - - private readonly HttpContextBase _httpContext; + private static readonly object s_lock = new object(); + private static bool? s_optimizedCompilationsEnabled; + private static AspNetHostingPermissionLevel? s_trustLevel; + private static readonly Regex s_staticExts = new Regex(@"(.*?)\.(css|js|png|jpg|jpeg|gif|webp|liquid|bmp|html|htm|xml|txt|pdf|doc|xls|rar|zip|7z|ico|eot|svg|ttf|woff|woff2|otf|json)", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex s_htmlPathPattern = new Regex(@"(?<=(?:href|src)=(?:""|'))(?!https?://)(?[^(?:""|')]+)", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Multiline); + private static readonly Regex s_cssPathPattern = new Regex(@"url\('(?.+)'\)", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Multiline); + private static readonly ConcurrentDictionary s_safeLocalHostNames = new ConcurrentDictionary(); + + private readonly HttpContextBase _httpContext; private bool? _isCurrentConnectionSecured; - private string _storeHost; - private string _storeHostSsl; - private string _ipAddress; - private bool? _appPathPossiblyAppended; - private bool? _appPathPossiblyAppendedSsl; + private string _storeHost; + private string _storeHostSsl; + private string _ipAddress; + private bool? _appPathPossiblyAppended; + private bool? _appPathPossiblyAppendedSsl; - private Store _currentStore; + private Store _currentStore; public WebHelper(HttpContextBase httpContext) { @@ -50,57 +50,57 @@ public virtual string GetUrlReferrer() return _httpContext.SafeGetHttpRequest()?.UrlReferrer?.ToString() ?? string.Empty; } - public virtual string GetClientIdent() - { - var ipAddress = this.GetCurrentIpAddress(); - var userAgent = _httpContext.SafeGetHttpRequest()?.UserAgent.EmptyNull(); - - if (ipAddress.HasValue() && userAgent.HasValue()) - { - return (ipAddress + userAgent).GetHashCode().ToString(); - } - - return null; - } - - public virtual string GetCurrentIpAddress() - { - if (_ipAddress != null) - { - return _ipAddress; - } - - var httpRequest = _httpContext.SafeGetHttpRequest(); - if (httpRequest == null) - { - return string.Empty; - } - - var vars = httpRequest.ServerVariables; - - var keysToCheck = new string[] - { - "HTTP_CLIENT_IP", - "HTTP_X_FORWARDED_FOR", - "HTTP_X_FORWARDED", - "HTTP_X_CLUSTER_CLIENT_IP", - "HTTP_FORWARDED_FOR", - "HTTP_FORWARDED", - "REMOTE_ADDR", - "HTTP_CF_CONNECTING_IP" - }; - - string result = null; + public virtual string GetClientIdent() + { + var ipAddress = this.GetCurrentIpAddress(); + var userAgent = _httpContext.SafeGetHttpRequest()?.UserAgent.EmptyNull(); + + if (ipAddress.HasValue() && userAgent.HasValue()) + { + return (ipAddress + userAgent).GetHashCode().ToString(); + } + + return null; + } + + public virtual string GetCurrentIpAddress() + { + if (_ipAddress != null) + { + return _ipAddress; + } + + var httpRequest = _httpContext.SafeGetHttpRequest(); + if (httpRequest == null) + { + return string.Empty; + } + + var vars = httpRequest.ServerVariables; + + var keysToCheck = new string[] + { + "HTTP_CLIENT_IP", + "HTTP_X_FORWARDED_FOR", + "HTTP_X_FORWARDED", + "HTTP_X_CLUSTER_CLIENT_IP", + "HTTP_FORWARDED_FOR", + "HTTP_FORWARDED", + "REMOTE_ADDR", + "HTTP_CF_CONNECTING_IP" + }; + + string result = null; IPAddress ipv6 = null; - foreach (var key in keysToCheck) - { - var ipString = vars[key]; + foreach (var key in keysToCheck) + { + var ipString = vars[key]; + + if (!string.IsNullOrEmpty(ipString)) + { + var arrStrings = ipString.Split(','); - if (!string.IsNullOrEmpty(ipString)) - { - var arrStrings = ipString.Split(','); - // Iterate list from end to start (IPv6 addresses usually have precedence) for (int i = arrStrings.Length - 1; i >= 0; i--) { @@ -118,21 +118,26 @@ public virtual string GetCurrentIpAddress() break; } } + } + + if (!string.IsNullOrEmpty(result)) + { + break; } - } - } + } + } if (string.IsNullOrEmpty(result) && ipv6 != null) { - result = ipv6.ToString() == "::1" + result = ipv6.ToString() == "::1" ? "127.0.0.1" : ipv6.MapToIPv4().ToString(); } - return (_ipAddress = result.EmptyNull()); - } + return (_ipAddress = result.EmptyNull()); + } - public virtual string GetThisPageUrl(bool includeQueryString) + public virtual string GetThisPageUrl(bool includeQueryString) { bool useSsl = IsCurrentConnectionSecured(); return GetThisPageUrl(includeQueryString, useSsl); @@ -141,41 +146,44 @@ public virtual string GetThisPageUrl(bool includeQueryString) public virtual string GetThisPageUrl(bool includeQueryString, bool useSsl) { string url = string.Empty; - var httpRequest = _httpContext.SafeGetHttpRequest(); + var httpRequest = _httpContext.SafeGetHttpRequest(); - if (httpRequest == null) + if (httpRequest == null) return url; - if (includeQueryString) + var authority = httpRequest.Url.GetLeftPart(UriPartial.Authority); + var path = httpRequest.RawUrl; + var schemeChanges = useSsl != IsCurrentConnectionSecured(); + + if (!schemeChanges && includeQueryString) { - string storeHost = GetStoreHost(useSsl, out bool appPathPossiblyAppended).TrimEnd('/'); + // Return as is + return authority + path; + } - string rawUrl; + if (schemeChanges) + { + authority = GetStoreHost(useSsl, out bool appPathPossiblyAppended).TrimEnd('/'); if (appPathPossiblyAppended) { - rawUrl = httpRequest.AppRelativeCurrentExecutionFilePath.TrimStart('~'); - - if (httpRequest.Url != null && httpRequest.Url.Query != null) - { - rawUrl += httpRequest.Url.Query; - } - } - else - { - rawUrl = httpRequest.RawUrl; + path = _httpContext.GetOriginalAppRelativePath().TrimStart('~'); + if (includeQueryString && httpRequest.Url?.Query != null) + { + path += httpRequest.Url.Query; + } } - - url = storeHost + rawUrl; } - else + + if (!includeQueryString) { - if (httpRequest.Url != null) - { - url = httpRequest.Url.GetLeftPart(UriPartial.Path); - } + var queryIndex = path.IndexOf('?'); + if (queryIndex > -1) + { + path = path.Substring(0, queryIndex); + } } - return url; + return authority + path; } public virtual bool IsCurrentConnectionSecured() @@ -183,23 +191,23 @@ public virtual bool IsCurrentConnectionSecured() if (!_isCurrentConnectionSecured.HasValue) { _isCurrentConnectionSecured = false; - var httpRequest = _httpContext.SafeGetHttpRequest(); - if (httpRequest != null) + var httpRequest = _httpContext.SafeGetHttpRequest(); + if (httpRequest != null) { - _isCurrentConnectionSecured = httpRequest.IsSecureConnection(); + _isCurrentConnectionSecured = httpRequest.IsHttps(); } } return _isCurrentConnectionSecured.Value; } - + public virtual string ServerVariables(string name) { - return _httpContext.SafeGetHttpRequest()?.ServerVariables[name].EmptyNull(); + return _httpContext.SafeGetHttpRequest()?.ServerVariables[name].EmptyNull(); } - [SuppressMessage("ReSharper", "UnusedMember.Local")] - private string GetHostPart(string url) + [SuppressMessage("ReSharper", "UnusedMember.Local")] + private string GetHostPart(string url) { var uri = new Uri(url); var host = uri.GetComponents(UriComponents.Scheme | UriComponents.Host, UriFormat.Unescaped); @@ -208,14 +216,14 @@ private string GetHostPart(string url) private string GetStoreHost(bool useSsl, out bool appPathPossiblyAppended) { - string cached = useSsl ? _storeHostSsl : _storeHost; - if (cached != null) - { - appPathPossiblyAppended = useSsl ? _appPathPossiblyAppendedSsl.Value : _appPathPossiblyAppended.Value; - return cached; - } - - appPathPossiblyAppended = false; + string cached = useSsl ? _storeHostSsl : _storeHost; + if (cached != null) + { + appPathPossiblyAppended = useSsl ? _appPathPossiblyAppendedSsl.Value : _appPathPossiblyAppended.Value; + return cached; + } + + appPathPossiblyAppended = false; var result = ""; var httpHost = ServerVariables("HTTP_HOST"); @@ -235,86 +243,86 @@ private string GetStoreHost(bool useSsl, out bool appPathPossiblyAppended) } else { - // Let's resolve IWorkContext here. - // Do not inject it via contructor because it'll cause circular references - - if (_currentStore == null) - { - if (EngineContext.Current.ContainerManager.TryResolve(null, out IStoreContext storeContext)) // Unit test safe! - { - _currentStore = storeContext.CurrentStore; - if (_currentStore == null) - throw new Exception("Current store cannot be loaded"); - } - } - - if (_currentStore != null) - { - var securityMode = _currentStore.GetSecurityMode(); - - if (httpHost.IsEmpty()) - { - // HTTP_HOST variable is not available. - // It's possible only when HttpContext is not available (for example, running in a schedule task) - result = _currentStore.Url.EnsureEndsWith("/"); - - appPathPossiblyAppended = true; - } - - if (useSsl) - { - if (securityMode == HttpSecurityMode.SharedSsl) - { - // Secure URL for shared ssl specified. - // So a store owner doesn't want it to be resolved automatically. - // In this case let's use the specified secure URL - result = _currentStore.SecureUrl.EmptyNull(); - - if (!result.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) - { - result = "https://" + result; - } - - appPathPossiblyAppended = true; - } - else - { - // Secure URL is not specified. - // So a store owner wants it to be resolved automatically. - result = result.Replace("http:/", "https:/"); - } - } - else // no ssl - { - if (securityMode == HttpSecurityMode.SharedSsl) - { - // SSL is enabled in this store and shared ssl URL is specified. - // So a store owner doesn't want it to be resolved automatically. - // In this case let's use the specified non-secure URL - - result = _currentStore.Url; - appPathPossiblyAppended = true; - } - } - } - } - - // cache results for request - result = result.EnsureEndsWith("/"); - if (useSsl) - { - _storeHostSsl = result; - _appPathPossiblyAppendedSsl = appPathPossiblyAppended; - } - else - { - _storeHost = result; - _appPathPossiblyAppended = appPathPossiblyAppended; - } + // Let's resolve IWorkContext here. + // Do not inject it via contructor because it'll cause circular references + + if (_currentStore == null) + { + if (EngineContext.Current.ContainerManager.TryResolve(null, out IStoreContext storeContext)) // Unit test safe! + { + _currentStore = storeContext.CurrentStore; + if (_currentStore == null) + throw new Exception("Current store cannot be loaded"); + } + } + + if (_currentStore != null) + { + var securityMode = _currentStore.GetSecurityMode(); + + if (httpHost.IsEmpty()) + { + // HTTP_HOST variable is not available. + // It's possible only when HttpContext is not available (for example, running in a schedule task) + result = _currentStore.Url.EnsureEndsWith("/"); + + appPathPossiblyAppended = true; + } + + if (useSsl) + { + if (securityMode == HttpSecurityMode.SharedSsl) + { + // Secure URL for shared ssl specified. + // So a store owner doesn't want it to be resolved automatically. + // In this case let's use the specified secure URL + result = _currentStore.SecureUrl.EmptyNull(); + + if (!result.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) + { + result = "https://" + result; + } + + appPathPossiblyAppended = true; + } + else + { + // Secure URL is not specified. + // So a store owner wants it to be resolved automatically. + result = result.Replace("http:/", "https:/"); + } + } + else // no ssl + { + if (securityMode == HttpSecurityMode.SharedSsl) + { + // SSL is enabled in this store and shared ssl URL is specified. + // So a store owner doesn't want it to be resolved automatically. + // In this case let's use the specified non-secure URL + + result = _currentStore.Url; + appPathPossiblyAppended = true; + } + } + } + } + + // cache results for request + result = result.EnsureEndsWith("/"); + if (useSsl) + { + _storeHostSsl = result; + _appPathPossiblyAppendedSsl = appPathPossiblyAppended; + } + else + { + _storeHost = result; + _appPathPossiblyAppended = appPathPossiblyAppended; + } return result; } - + public virtual string GetStoreLocation() { bool useSsl = IsCurrentConnectionSecured(); @@ -330,7 +338,7 @@ public virtual string GetStoreLocation(bool useSsl) result = result.Substring(0, result.Length - 1); } - var httpRequest = _httpContext.SafeGetHttpRequest(); + var httpRequest = _httpContext.SafeGetHttpRequest(); if (httpRequest != null) { @@ -339,8 +347,8 @@ public virtual string GetStoreLocation(bool useSsl) { // in a shared ssl scenario the user defined https url could contain // the app path already. In this case we must not append. - result = result + appPath; - } + result += appPath; + } } if (!result.EndsWith("/")) @@ -350,485 +358,485 @@ public virtual string GetStoreLocation(bool useSsl) return result; } - + public virtual bool IsStaticResource(HttpRequest request) { - return IsStaticResourceRequested(new HttpRequestWrapper(request)); + return IsStaticResourceRequested(new HttpRequestWrapper(request)); + } + + public static bool IsStaticResourceRequested(HttpRequest request) + { + Guard.NotNull(request, nameof(request)); + return s_staticExts.IsMatch(request.Path); + } + + public static bool IsStaticResourceRequested(HttpRequestBase request) + { + // unit testable + Guard.NotNull(request, nameof(request)); + return s_staticExts.IsMatch(request.Path); } - public static bool IsStaticResourceRequested(HttpRequest request) - { - Guard.NotNull(request, nameof(request)); - return s_staticExts.IsMatch(request.Path); - } - - public static bool IsStaticResourceRequested(HttpRequestBase request) - { - // unit testable - Guard.NotNull(request, nameof(request)); - return s_staticExts.IsMatch(request.Path); - } - public virtual string MapPath(string path) { - return CommonHelper.MapPath(path, false); + return CommonHelper.MapPath(path, false); } - + public virtual string ModifyQueryString(string url, string queryStringModification, string anchor) { - url = url.EmptyNull(); - queryStringModification = queryStringModification.EmptyNull(); - - string curAnchor = null; - - var hsIndex = url.LastIndexOf('#'); - if (hsIndex >= 0) - { - curAnchor = url.Substring(hsIndex); - url = url.Substring(0, hsIndex); - } - - var parts = url.Split(new[] { '?' }); - var current = new QueryString(parts.Length == 2 ? parts[1] : ""); - var modify = new QueryString(queryStringModification); - - foreach (var nv in modify.AllKeys) - { - current.Add(nv, modify[nv], true); - } - - var result = string.Concat( - parts[0], - current.ToString(false), - anchor.NullEmpty() == null ? (curAnchor == null ? "" : "#" + curAnchor) : "#" + anchor - ); - - return result; + url = url.EmptyNull(); + queryStringModification = queryStringModification.EmptyNull(); + + string curAnchor = null; + + var hsIndex = url.LastIndexOf('#'); + if (hsIndex >= 0) + { + curAnchor = url.Substring(hsIndex); + url = url.Substring(0, hsIndex); + } + + var parts = url.Split(new[] { '?' }); + var current = new QueryString(parts.Length == 2 ? parts[1] : ""); + var modify = new QueryString(queryStringModification); + + foreach (var nv in modify.AllKeys) + { + current.Add(nv, modify[nv], true); + } + + var result = string.Concat( + parts[0], + current.ToString(false), + anchor.NullEmpty() == null ? (curAnchor == null ? "" : "#" + curAnchor) : "#" + anchor + ); + + return result; } public virtual string RemoveQueryString(string url, string queryString) { - var parts = url.SplitSafe("?"); + var parts = url.SplitSafe("?"); - var current = new QueryString(parts.Length == 2 ? parts[1] : ""); + var current = new QueryString(parts.Length == 2 ? parts[1] : ""); - if (current.Count > 0 && queryString.HasValue()) - { - current.Remove(queryString); - } + if (current.Count > 0 && queryString.HasValue()) + { + current.Remove(queryString); + } - var result = string.Concat(parts[0], current.ToString(false)); - return result; + var result = string.Concat(parts[0], current.ToString(false)); + return result; } - + public virtual T QueryString(string name) { - var queryParam = _httpContext.SafeGetHttpRequest()?.QueryString[name]; + var queryParam = _httpContext.SafeGetHttpRequest()?.QueryString[name]; - if (!string.IsNullOrEmpty(queryParam)) - { - return queryParam.Convert(); - } + if (!string.IsNullOrEmpty(queryParam)) + { + return queryParam.Convert(); + } return default(T); } - + public virtual void RestartAppDomain(bool makeRedirect = false, string redirectUrl = "", bool aggressive = false) { - HttpRuntime.UnloadAppDomain(); - - if (aggressive) - { - // When plugins are (un)installed, 'aggressive' is always true. - if (OptimizedCompilationsEnabled) - { - // Very hackish: - // If optimizedCompilations is on per web.config, touching top-level resources - // like global.asax or bin folder is meaningless, 'cause ASP.NET skips these for - // hash calculation. This way we can throw in plugins like crazy without invalidating - // ASP.NET temp files, which boosts app startup performance dramatically. - // Unfortunately, MVC keeps a controller cache file in the temp files folder, which NEVER - // gets nuked, unless the 'compilation' element in web.config is changed. - // We MUST delete this file in order to ensure that it gets re-created with our new controller types in it. - DeleteMvcTypeCacheFiles(); - } - else - { - // Without optimizedCompilations, touching anything in the bin folder nukes ASP.NET temp folder completely, - // including compiled views, MVC cache files etc. - TryWriteBinFolder(); - } - } - else - { - // without this, MVC may fail resolving controllers for newly installed plugins after IIS restart - Thread.Sleep(250); - } + HttpRuntime.UnloadAppDomain(); + + if (aggressive) + { + // When plugins are (un)installed, 'aggressive' is always true. + if (OptimizedCompilationsEnabled) + { + // Very hackish: + // If optimizedCompilations is on per web.config, touching top-level resources + // like global.asax or bin folder is meaningless, 'cause ASP.NET skips these for + // hash calculation. This way we can throw in plugins like crazy without invalidating + // ASP.NET temp files, which boosts app startup performance dramatically. + // Unfortunately, MVC keeps a controller cache file in the temp files folder, which NEVER + // gets nuked, unless the 'compilation' element in web.config is changed. + // We MUST delete this file in order to ensure that it gets re-created with our new controller types in it. + DeleteMvcTypeCacheFiles(); + } + else + { + // Without optimizedCompilations, touching anything in the bin folder nukes ASP.NET temp folder completely, + // including compiled views, MVC cache files etc. + TryWriteBinFolder(); + } + } + else + { + // without this, MVC may fail resolving controllers for newly installed plugins after IIS restart + Thread.Sleep(250); + } // If setting up plugins requires an AppDomain restart, it's very unlikely the // current request can be processed correctly. So, we redirect to the same URL, so that the // new request will come to the newly started AppDomain. if (_httpContext != null && makeRedirect) { - if (_httpContext.Request.RequestType == "GET") - { - if (string.IsNullOrEmpty(redirectUrl)) - { - redirectUrl = GetThisPageUrl(true); - } - _httpContext.Response.Redirect(redirectUrl, true /*endResponse*/); - } - else - { - // Don't redirect posts... - _httpContext.Response.ContentType = "text/html"; - _httpContext.Response.WriteFile("~/refresh.html"); - _httpContext.Response.End(); - } + if (_httpContext.Request.RequestType == "GET") + { + if (string.IsNullOrEmpty(redirectUrl)) + { + redirectUrl = GetThisPageUrl(true); + } + _httpContext.Response.Redirect(redirectUrl, true /*endResponse*/); + } + else + { + // Don't redirect posts... + _httpContext.Response.ContentType = "text/html"; + _httpContext.Response.WriteFile("~/refresh.html"); + _httpContext.Response.End(); + } + } + } + + private void DeleteMvcTypeCacheFiles() + { + try + { + var userCacheDir = Path.Combine(HttpRuntime.CodegenDir, "UserCache"); + + File.Delete(Path.Combine(userCacheDir, "MVC-ControllerTypeCache.xml")); + File.Delete(Path.Combine(userCacheDir, "MVC-AreaRegistrationTypeCache.xml")); + } + catch { } + } + + private bool TryWriteBinFolder() + { + try + { + var binMarker = MapPath("~/bin/HostRestart"); + Directory.CreateDirectory(binMarker); + + using (var stream = File.CreateText(Path.Combine(binMarker, "marker.txt"))) + { + stream.WriteLine("Restart on '{0}'", DateTime.UtcNow); + stream.Flush(); + } + return true; + } + catch + { + return false; + } + } + + internal static bool OptimizedCompilationsEnabled + { + get + { + if (!s_optimizedCompilationsEnabled.HasValue) + { + var section = (CompilationSection)ConfigurationManager.GetSection("system.web/compilation"); + s_optimizedCompilationsEnabled = section.OptimizeCompilations; + } + + return s_optimizedCompilationsEnabled.Value; } } - private void DeleteMvcTypeCacheFiles() - { - try - { - var userCacheDir = Path.Combine(HttpRuntime.CodegenDir, "UserCache"); - - File.Delete(Path.Combine(userCacheDir, "MVC-ControllerTypeCache.xml")); - File.Delete(Path.Combine(userCacheDir, "MVC-AreaRegistrationTypeCache.xml")); - } - catch { } - } - - private bool TryWriteBinFolder() - { - try - { - var binMarker = MapPath("~/bin/HostRestart"); - Directory.CreateDirectory(binMarker); - - using (var stream = File.CreateText(Path.Combine(binMarker, "marker.txt"))) - { - stream.WriteLine("Restart on '{0}'", DateTime.UtcNow); - stream.Flush(); - } - return true; - } - catch - { - return false; - } - } - - internal static bool OptimizedCompilationsEnabled - { - get - { - if (!s_optimizedCompilationsEnabled.HasValue) - { - var section = (CompilationSection)ConfigurationManager.GetSection("system.web/compilation"); - s_optimizedCompilationsEnabled = section.OptimizeCompilations; - } - - return s_optimizedCompilationsEnabled.Value; - } - } - - /// - /// Finds the trust level of the running application (http://blogs.msdn.com/dmitryr/archive/2007/01/23/finding-out-the-current-trust-level-in-asp-net.aspx) - /// - /// The current trust level. - public static AspNetHostingPermissionLevel GetTrustLevel() - { - if (!s_trustLevel.HasValue) - { - // set minimum - s_trustLevel = AspNetHostingPermissionLevel.None; - - // determine maximum - foreach (AspNetHostingPermissionLevel trustLevel in - new [] { + /// + /// Finds the trust level of the running application (http://blogs.msdn.com/dmitryr/archive/2007/01/23/finding-out-the-current-trust-level-in-asp-net.aspx) + /// + /// The current trust level. + public static AspNetHostingPermissionLevel GetTrustLevel() + { + if (!s_trustLevel.HasValue) + { + // set minimum + s_trustLevel = AspNetHostingPermissionLevel.None; + + // determine maximum + foreach (AspNetHostingPermissionLevel trustLevel in + new[] { AspNetHostingPermissionLevel.Unrestricted, AspNetHostingPermissionLevel.High, AspNetHostingPermissionLevel.Medium, AspNetHostingPermissionLevel.Low, - AspNetHostingPermissionLevel.Minimal + AspNetHostingPermissionLevel.Minimal }) - { - try - { - new AspNetHostingPermission(trustLevel).Demand(); - s_trustLevel = trustLevel; - break; //we've set the highest permission we can - } - catch (System.Security.SecurityException) - { - continue; - } - } - } - return s_trustLevel.Value; - } - - /// - /// Prepends protocol and host to all (relative) urls in a html string - /// - /// The html string - /// Request object - /// The transformed result html - /// - /// All html attributed named src and href are affected, also occurences of url('path') within embedded stylesheets. - /// - public static string MakeAllUrlsAbsolute(string html, HttpRequestBase request) - { - Guard.NotNull(request, nameof(request)); - - if (request.Url == null) - { - return html; - } - - return MakeAllUrlsAbsolute(html, request.Url.Scheme, request.Url.Authority); - } - - /// - /// Prepends protocol and host to all (relative) urls in a html string - /// - /// The html string - /// The protocol to prepend, e.g. http - /// The host name to prepend, e.g. www.mysite.com - /// The transformed result html - /// - /// All html attributed named src and href are affected, also occurences of url('path') within embedded stylesheets. - /// - public static string MakeAllUrlsAbsolute(string html, string protocol, string host) - { - Guard.NotEmpty(html, nameof(html)); - Guard.NotEmpty(protocol, nameof(protocol)); - Guard.NotEmpty(host, nameof(host)); - - string baseUrl = protocol.EnsureEndsWith("://") + host.TrimEnd('/'); - - MatchEvaluator evaluator = (match) => - { - var url = match.Groups["url"].Value; - return "{0}{1}".FormatCurrent(baseUrl, url.EnsureStartsWith("/")); - }; - - html = s_htmlPathPattern.Replace(html, evaluator); - html = s_cssPathPattern.Replace(html, evaluator); - - return html; - } - - /// - /// Prepends protocol and host to the given (relative) url - /// - /// Changes the protocol if passed. - [SuppressMessage("ReSharper", "AccessToModifiedClosure")] - public static string GetAbsoluteUrl(string url, HttpRequestBase request, bool enforceScheme = false, string protocol = null) - { - Guard.NotEmpty(url, nameof(url)); - Guard.NotNull(request, nameof(request)); - - if (request.Url == null) - { - return url; - } - - if (url.Contains("://")) - { - return url; - } - - protocol = protocol ?? request.Url.Scheme; - - if (url.StartsWith("//")) - { - return enforceScheme - ? String.Concat(protocol, ":", url) - : url; - } - - if (url.StartsWith("~")) - { - url = VirtualPathUtility.ToAbsolute(url); - } - - url = string.Format("{0}://{1}{2}", protocol, request.Url.Authority, url); - return url; - } - - public static string GetPublicIPAddress() - { - string result = string.Empty; - - try - { - using (var client = new WebClient()) - { - client.Headers["User-Agent"] = "Mozilla/4.0 (Compatible; Windows NT 5.1; MSIE 6.0) (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"; - try - { - byte[] arr = client.DownloadData("http://checkip.amazonaws.com/"); - string response = Encoding.UTF8.GetString(arr); - result = response.Trim(); - } - catch { } - } - } - catch { } - - var checkers = new string[] - { - "https://ipinfo.io/ip", - "https://api.ipify.org", - "https://icanhazip.com", - "https://wtfismyip.com/text", - "http://bot.whatismyipaddress.com/" - }; - - if (string.IsNullOrEmpty(result)) - { - using (var client = new WebClient()) - { - foreach (var checker in checkers) - { - try - { - result = client.DownloadString(checker).Replace("\n", ""); - if (!string.IsNullOrEmpty(result)) - { - break; - } - } - catch { } - } - } - } - - if (string.IsNullOrEmpty(result)) - { - try - { - var url = "http://checkip.dyndns.org"; - var req = WebRequest.Create(url); - using (var resp = req.GetResponse()) - { - using (var sr = new StreamReader(resp.GetResponseStream())) - { - var response = sr.ReadToEnd().Trim(); - var a = response.Split(':'); - var a2 = a[1].Substring(1); - var a3 = a2.Split('<'); - result = a3[0]; - } - } - } - catch { } - } - - return result; - } - - public static HttpWebRequest CreateHttpRequestForSafeLocalCall(Uri requestUri) - { - Guard.NotNull(requestUri, nameof(requestUri)); - - var safeHostName = GetSafeLocalHostName(requestUri); - - var uri = requestUri; - - if (!requestUri.Host.Equals(safeHostName, StringComparison.OrdinalIgnoreCase)) - { - var url = String.Format("{0}://{1}{2}", - requestUri.Scheme, - requestUri.IsDefaultPort ? safeHostName : safeHostName + ":" + requestUri.Port, - requestUri.PathAndQuery); - uri = new Uri(url); - } - - var request = WebRequest.CreateHttp(uri); - request.ServerCertificateValidationCallback += (sender, cert, chain, errors) => true; - request.ServicePoint.Expect100Continue = false; - request.UserAgent = "SmartStore.NET {0}".FormatInvariant(SmartStoreVersion.CurrentFullVersion); - - return request; - } - - private static string GetSafeLocalHostName(Uri requestUri) - { - return s_safeLocalHostNames.GetOrAdd(requestUri.Port, (port) => - { - // first try original host - if (TestHost(requestUri, requestUri.Host, 5000)) - { - return requestUri.Host; - } - - // try loopback - var hostName = Dns.GetHostName(); - var hosts = new List { "localhost", hostName, "127.0.0.1" }; - foreach (var host in hosts) - { - if (TestHost(requestUri, host, 500)) - { - return host; - } - } - - // try local IP addresses - hosts.Clear(); - var ipAddresses = Dns.GetHostAddresses(hostName).Where(x => x.AddressFamily == AddressFamily.InterNetwork).Select(x => x.ToString()); - hosts.AddRange(ipAddresses); - - foreach (var host in hosts) - { - if (TestHost(requestUri, host, 500)) - { - return host; - } - } - - // None of the hosts are callable. WTF? - return requestUri.Host; - }); - } - - private static bool TestHost(Uri originalUri, string host, int timeout) - { - var url = String.Format("{0}://{1}/taskscheduler/noop", - originalUri.Scheme, - originalUri.IsDefaultPort ? host : host + ":" + originalUri.Port); - var uri = new Uri(url); - - var request = WebRequest.CreateHttp(uri); - request.ServerCertificateValidationCallback += (sender, cert, chain, errors) => true; - request.ServicePoint.Expect100Continue = false; - request.UserAgent = "SmartStore.NET"; - request.Timeout = timeout; - - HttpWebResponse response = null; - - try - { - response = (HttpWebResponse)request.GetResponse(); - if (response.StatusCode == HttpStatusCode.OK) - { - return true; - } - } - catch - { - // try the next host - } - finally - { - if (response != null) - response.Dispose(); - } - - return false; - } - } + { + try + { + new AspNetHostingPermission(trustLevel).Demand(); + s_trustLevel = trustLevel; + break; //we've set the highest permission we can + } + catch (System.Security.SecurityException) + { + continue; + } + } + } + return s_trustLevel.Value; + } + + /// + /// Prepends protocol and host to all (relative) urls in a html string + /// + /// The html string + /// Request object + /// The transformed result html + /// + /// All html attributed named src and href are affected, also occurences of url('path') within embedded stylesheets. + /// + public static string MakeAllUrlsAbsolute(string html, HttpRequestBase request) + { + Guard.NotNull(request, nameof(request)); + + if (request.Url == null) + { + return html; + } + + return MakeAllUrlsAbsolute(html, request.Url.Scheme, request.Url.Authority); + } + + /// + /// Prepends protocol and host to all (relative) urls in a html string + /// + /// The html string + /// The protocol to prepend, e.g. http + /// The host name to prepend, e.g. www.mysite.com + /// The transformed result html + /// + /// All html attributed named src and href are affected, also occurences of url('path') within embedded stylesheets. + /// + public static string MakeAllUrlsAbsolute(string html, string protocol, string host) + { + Guard.NotEmpty(html, nameof(html)); + Guard.NotEmpty(protocol, nameof(protocol)); + Guard.NotEmpty(host, nameof(host)); + + string baseUrl = protocol.EnsureEndsWith("://") + host.TrimEnd('/'); + + MatchEvaluator evaluator = (match) => + { + var url = match.Groups["url"].Value; + return "{0}{1}".FormatCurrent(baseUrl, url.EnsureStartsWith("/")); + }; + + html = s_htmlPathPattern.Replace(html, evaluator); + html = s_cssPathPattern.Replace(html, evaluator); + + return html; + } + + /// + /// Prepends protocol and host to the given (relative) url + /// + /// Changes the protocol if passed. + [SuppressMessage("ReSharper", "AccessToModifiedClosure")] + public static string GetAbsoluteUrl(string url, HttpRequestBase request, bool enforceScheme = false, string protocol = null) + { + Guard.NotEmpty(url, nameof(url)); + Guard.NotNull(request, nameof(request)); + + if (request.Url == null) + { + return url; + } + + if (url.Contains("://")) + { + return url; + } + + protocol = protocol ?? request.Url.Scheme; + + if (url.StartsWith("//")) + { + return enforceScheme + ? String.Concat(protocol, ":", url) + : url; + } + + if (url.StartsWith("~")) + { + url = VirtualPathUtility.ToAbsolute(url); + } + + url = string.Format("{0}://{1}{2}", protocol, request.Url.Authority, url); + return url; + } + + public static string GetPublicIPAddress() + { + string result = string.Empty; + + try + { + using (var client = new WebClient()) + { + client.Headers["User-Agent"] = "Mozilla/4.0 (Compatible; Windows NT 5.1; MSIE 6.0) (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"; + try + { + byte[] arr = client.DownloadData("http://checkip.amazonaws.com/"); + string response = Encoding.UTF8.GetString(arr); + result = response.Trim(); + } + catch { } + } + } + catch { } + + var checkers = new string[] + { + "https://ipinfo.io/ip", + "https://api.ipify.org", + "https://icanhazip.com", + "https://wtfismyip.com/text", + "http://bot.whatismyipaddress.com/" + }; + + if (string.IsNullOrEmpty(result)) + { + using (var client = new WebClient()) + { + foreach (var checker in checkers) + { + try + { + result = client.DownloadString(checker).Replace("\n", ""); + if (!string.IsNullOrEmpty(result)) + { + break; + } + } + catch { } + } + } + } + + if (string.IsNullOrEmpty(result)) + { + try + { + var url = "http://checkip.dyndns.org"; + var req = WebRequest.Create(url); + using (var resp = req.GetResponse()) + { + using (var sr = new StreamReader(resp.GetResponseStream())) + { + var response = sr.ReadToEnd().Trim(); + var a = response.Split(':'); + var a2 = a[1].Substring(1); + var a3 = a2.Split('<'); + result = a3[0]; + } + } + } + catch { } + } + + return result; + } + + public static HttpWebRequest CreateHttpRequestForSafeLocalCall(Uri requestUri) + { + Guard.NotNull(requestUri, nameof(requestUri)); + + var safeHostName = GetSafeLocalHostName(requestUri); + + var uri = requestUri; + + if (!requestUri.Host.Equals(safeHostName, StringComparison.OrdinalIgnoreCase)) + { + var url = String.Format("{0}://{1}{2}", + requestUri.Scheme, + requestUri.IsDefaultPort ? safeHostName : safeHostName + ":" + requestUri.Port, + requestUri.PathAndQuery); + uri = new Uri(url); + } + + var request = WebRequest.CreateHttp(uri); + request.ServerCertificateValidationCallback += (sender, cert, chain, errors) => true; + request.ServicePoint.Expect100Continue = false; + request.UserAgent = "Smartstore {0}".FormatInvariant(SmartStoreVersion.CurrentFullVersion); + + return request; + } + + private static string GetSafeLocalHostName(Uri requestUri) + { + return s_safeLocalHostNames.GetOrAdd(requestUri.Port, (port) => + { + // first try original host + if (TestHost(requestUri, requestUri.Host, 5000)) + { + return requestUri.Host; + } + + // try loopback + var hostName = Dns.GetHostName(); + var hosts = new List { "localhost", hostName, "127.0.0.1" }; + foreach (var host in hosts) + { + if (TestHost(requestUri, host, 500)) + { + return host; + } + } + + // try local IP addresses + hosts.Clear(); + var ipAddresses = Dns.GetHostAddresses(hostName).Where(x => x.AddressFamily == AddressFamily.InterNetwork).Select(x => x.ToString()); + hosts.AddRange(ipAddresses); + + foreach (var host in hosts) + { + if (TestHost(requestUri, host, 500)) + { + return host; + } + } + + // None of the hosts are callable. WTF? + return requestUri.Host; + }); + } + + private static bool TestHost(Uri originalUri, string host, int timeout) + { + var url = String.Format("{0}://{1}/taskscheduler/noop", + originalUri.Scheme, + originalUri.IsDefaultPort ? host : host + ":" + originalUri.Port); + var uri = new Uri(url); + + var request = WebRequest.CreateHttp(uri); + request.ServerCertificateValidationCallback += (sender, cert, chain, errors) => true; + request.ServicePoint.Expect100Continue = false; + request.UserAgent = "Smartstore"; + request.Timeout = timeout; + + HttpWebResponse response = null; + + try + { + response = (HttpWebResponse)request.GetResponse(); + if (response.StatusCode == HttpStatusCode.OK) + { + return true; + } + } + catch + { + // try the next host + } + finally + { + if (response != null) + response.Dispose(); + } + + return false; + } + } } diff --git a/src/Libraries/SmartStore.Core/XmlHelper.cs b/src/Libraries/SmartStore.Core/XmlHelper.cs index 47c806b66b..cc5f65923b 100644 --- a/src/Libraries/SmartStore.Core/XmlHelper.cs +++ b/src/Libraries/SmartStore.Core/XmlHelper.cs @@ -7,184 +7,184 @@ namespace SmartStore.Core { - /// - /// Xml helper class - /// - public partial class XmlHelper - { - private readonly static Regex s_xmlEncodePattern = new Regex(@"[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD]", RegexOptions.Compiled); - - #region Methods - - /// - /// XML Encode - /// - /// String - /// Encoded string - public static string XmlEncode(string str) - { - if (str == null) - return null; - - str = s_xmlEncodePattern.Replace(str, ""); - return XmlEncodeAsIs(str); - } - - /// - /// XML Encode as is - /// - /// String - /// Encoded string - public static string XmlEncodeAsIs(string str) - { - if (str == null) - return null; - - using (var sw = new StringWriter()) - { - using (var xwr = new XmlTextWriter(sw)) - { - xwr.WriteString(str); - return sw.ToString(); - } - } - } - - /// - /// Encodes an attribute - /// - /// Attribute - /// Encoded attribute - public static string XmlEncodeAttribute(string str) - { - if (str == null) - return null; - str = s_xmlEncodePattern.Replace(str, ""); - return XmlEncodeAttributeAsIs(str); - } - - /// - /// Encodes an attribute as is - /// - /// Attribute - /// Encoded attribute - public static string XmlEncodeAttributeAsIs(string str) - { - return XmlEncodeAsIs(str).Replace("\"", """); - } - - /// - /// Decodes an attribute - /// - /// Attribute - /// Decoded attribute - public static string XmlDecode(string str) - { - var sb = new StringBuilder(str); - return sb.Replace(""", "\"").Replace("'", "'").Replace("<", "<").Replace(">", ">").Replace("&", "&").ToString(); - } - - /// - /// Serializes a datetime - /// - /// Datetime - /// Serialized datetime - public static string SerializeDateTime(DateTime dateTime) - { - var xmlS = new XmlSerializer(typeof(DateTime)); - var sb = new StringBuilder(); - using (var sw = new StringWriter(sb)) - { - xmlS.Serialize(sw, dateTime); - return sb.ToString(); - } - } - - /// - /// Deserializes a datetime - /// - /// Datetime - /// Deserialized datetime - public static DateTime DeserializeDateTime(string dateTime) - { - var xmlS = new XmlSerializer(typeof(DateTime)); - using (var sr = new StringReader(dateTime)) - { - object test = xmlS.Deserialize(sr); - return (DateTime)test; - } - } - - /// - /// Serializes an object instance to a XML formatted string - /// - /// Object type - /// Object instance - /// XML string - public static string Serialize(T instance) - { - return Serialize(instance, typeof(T)); - } - - /// - /// Serializes an object instance to a XML formatted string - /// - /// Object instance - /// Object type - /// XML string - public static string Serialize(object instance, Type type) - { - if (instance == null) - return null; - - Guard.NotNull(type, nameof(type)); - - using (var writer = new StringWriter()) - { - var xmlSerializer = new XmlSerializer(type); - - xmlSerializer.Serialize(writer, instance); - return writer.ToString(); - } - } - - /// - /// Deserializes a XML formatted string to an object instance - /// - /// Object type - /// XML string - /// Object instance - public static T Deserialize(string xml) - { - return (T)Deserialize(xml, typeof(T)); - } - - /// - /// Deserializes a XML formatted string to an object instance - /// - /// XML string - /// Object type - /// Object instance - public static object Deserialize(string xml, Type type) - { - Guard.NotNull(type, nameof(type)); - - try - { - if (xml.HasValue()) - { - using (var reader = new StringReader(xml)) - { - var serializer = new XmlSerializer(type); - return serializer.Deserialize(reader); - } - } - } - catch { } - - return Activator.CreateInstance(type); - } - - #endregion - } + /// + /// Xml helper class + /// + public partial class XmlHelper + { + private readonly static Regex s_xmlEncodePattern = new Regex(@"[^\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD]", RegexOptions.Compiled); + + #region Methods + + /// + /// XML Encode + /// + /// String + /// Encoded string + public static string XmlEncode(string str) + { + if (str == null) + return null; + + str = s_xmlEncodePattern.Replace(str, ""); + return XmlEncodeAsIs(str); + } + + /// + /// XML Encode as is + /// + /// String + /// Encoded string + public static string XmlEncodeAsIs(string str) + { + if (str == null) + return null; + + using (var sw = new StringWriter()) + { + using (var xwr = new XmlTextWriter(sw)) + { + xwr.WriteString(str); + return sw.ToString(); + } + } + } + + /// + /// Encodes an attribute + /// + /// Attribute + /// Encoded attribute + public static string XmlEncodeAttribute(string str) + { + if (str == null) + return null; + str = s_xmlEncodePattern.Replace(str, ""); + return XmlEncodeAttributeAsIs(str); + } + + /// + /// Encodes an attribute as is + /// + /// Attribute + /// Encoded attribute + public static string XmlEncodeAttributeAsIs(string str) + { + return XmlEncodeAsIs(str).Replace("\"", """); + } + + /// + /// Decodes an attribute + /// + /// Attribute + /// Decoded attribute + public static string XmlDecode(string str) + { + var sb = new StringBuilder(str); + return sb.Replace(""", "\"").Replace("'", "'").Replace("<", "<").Replace(">", ">").Replace("&", "&").ToString(); + } + + /// + /// Serializes a datetime + /// + /// Datetime + /// Serialized datetime + public static string SerializeDateTime(DateTime dateTime) + { + var xmlS = new XmlSerializer(typeof(DateTime)); + var sb = new StringBuilder(); + using (var sw = new StringWriter(sb)) + { + xmlS.Serialize(sw, dateTime); + return sb.ToString(); + } + } + + /// + /// Deserializes a datetime + /// + /// Datetime + /// Deserialized datetime + public static DateTime DeserializeDateTime(string dateTime) + { + var xmlS = new XmlSerializer(typeof(DateTime)); + using (var sr = new StringReader(dateTime)) + { + object test = xmlS.Deserialize(sr); + return (DateTime)test; + } + } + + /// + /// Serializes an object instance to a XML formatted string + /// + /// Object type + /// Object instance + /// XML string + public static string Serialize(T instance) + { + return Serialize(instance, typeof(T)); + } + + /// + /// Serializes an object instance to a XML formatted string + /// + /// Object instance + /// Object type + /// XML string + public static string Serialize(object instance, Type type) + { + if (instance == null) + return null; + + Guard.NotNull(type, nameof(type)); + + using (var writer = new StringWriter()) + { + var xmlSerializer = new XmlSerializer(type); + + xmlSerializer.Serialize(writer, instance); + return writer.ToString(); + } + } + + /// + /// Deserializes a XML formatted string to an object instance + /// + /// Object type + /// XML string + /// Object instance + public static T Deserialize(string xml) + { + return (T)Deserialize(xml, typeof(T)); + } + + /// + /// Deserializes a XML formatted string to an object instance + /// + /// XML string + /// Object type + /// Object instance + public static object Deserialize(string xml, Type type) + { + Guard.NotNull(type, nameof(type)); + + try + { + if (xml.HasValue()) + { + using (var reader = new StringReader(xml)) + { + var serializer = new XmlSerializer(type); + return serializer.Deserialize(reader); + } + } + } + catch { } + + return Activator.CreateInstance(type); + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Core/app.config b/src/Libraries/SmartStore.Core/app.config index f9acd6487c..67916a0819 100644 --- a/src/Libraries/SmartStore.Core/app.config +++ b/src/Libraries/SmartStore.Core/app.config @@ -8,16 +8,24 @@ - - - - - + + + + + + + + + + + + + - + diff --git a/src/Libraries/SmartStore.Core/packages.config b/src/Libraries/SmartStore.Core/packages.config index 19d5cd9737..efb4850fdf 100644 --- a/src/Libraries/SmartStore.Core/packages.config +++ b/src/Libraries/SmartStore.Core/packages.config @@ -1,19 +1,21 @@  - - - + + + + - + - - + + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Caching/CacheTransactionInterceptor.cs b/src/Libraries/SmartStore.Data/Caching/CacheTransactionInterceptor.cs index a78b4ae4a6..6cea03e354 100644 --- a/src/Libraries/SmartStore.Data/Caching/CacheTransactionInterceptor.cs +++ b/src/Libraries/SmartStore.Data/Caching/CacheTransactionInterceptor.cs @@ -4,119 +4,118 @@ using System.Data; using System.Data.Common; using System.Data.Entity.Infrastructure.Interception; -using System.Linq; namespace SmartStore.Data.Caching { - public class CacheTransactionInterceptor : IDbTransactionInterceptor - { - private readonly ConcurrentDictionary> _affectedSetsInTransaction = new ConcurrentDictionary>(); - private readonly IDbCache _cache; - - public CacheTransactionInterceptor(IDbCache cache) - { - Guard.NotNull(cache, nameof(cache)); - - _cache = cache; - } - - public virtual bool GetItem(DbTransaction transaction, string key, out object value) - { - if (transaction == null) - { - return _cache.TryGet(key, out value); - } - - value = null; - - return false; - } - - public virtual void PutItem(DbTransaction transaction, string key, object value, IEnumerable dependentEntitySets, TimeSpan? duration) - { - if (transaction == null) - { - _cache.Put(key, value, dependentEntitySets, duration); - } - } - - public virtual void InvalidateSets(DbTransaction transaction, IEnumerable entitySets) - { - if (transaction == null) - { - _cache.InvalidateSets(entitySets); - _cache.RequestInvalidateSets(entitySets); - } - else - { - AddAffectedEntitySets(transaction, entitySets); - } - } - - protected void AddAffectedEntitySets(DbTransaction transaction, IEnumerable affectedEntitySets) - { - Guard.NotNull(transaction, nameof(transaction)); - Guard.NotNull(affectedEntitySets, nameof(affectedEntitySets)); - - var entitySets = _affectedSetsInTransaction.GetOrAdd(transaction, new List()); - entitySets.AddRange(affectedEntitySets); - } - - private IEnumerable RemoveAffectedEntitySets(DbTransaction transaction) - { - List affectedEntitySets; - - _affectedSetsInTransaction.TryRemove(transaction, out affectedEntitySets); - - return affectedEntitySets; - } - - public void Committed(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) - { - var entitySets = RemoveAffectedEntitySets(transaction); - - if (entitySets != null) - { - _cache.InvalidateSets(entitySets); - _cache.RequestInvalidateSets(entitySets); - } - } - - public void Committing(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) - { - } - - public void ConnectionGetting(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) - { - } - - public void ConnectionGot(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) - { - } - - public void Disposed(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) - { - } - - public void Disposing(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) - { - } - - public void IsolationLevelGetting(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) - { - } - - public void IsolationLevelGot(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) - { - } - - public void RolledBack(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) - { - RemoveAffectedEntitySets(transaction); - } - - public void RollingBack(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) - { - } - } + public class CacheTransactionInterceptor : IDbTransactionInterceptor + { + private readonly ConcurrentDictionary> _affectedSetsInTransaction = new ConcurrentDictionary>(); + private readonly IDbCache _cache; + + public CacheTransactionInterceptor(IDbCache cache) + { + Guard.NotNull(cache, nameof(cache)); + + _cache = cache; + } + + public virtual bool GetItem(DbTransaction transaction, string key, out object value) + { + if (transaction == null) + { + return _cache.TryGet(key, out value); + } + + value = null; + + return false; + } + + public virtual void PutItem(DbTransaction transaction, string key, object value, IEnumerable dependentEntitySets, TimeSpan? duration) + { + if (transaction == null) + { + _cache.Put(key, value, dependentEntitySets, duration); + } + } + + public virtual void InvalidateSets(DbTransaction transaction, IEnumerable entitySets) + { + if (transaction == null) + { + _cache.InvalidateSets(entitySets); + _cache.RequestInvalidateSets(entitySets); + } + else + { + AddAffectedEntitySets(transaction, entitySets); + } + } + + protected void AddAffectedEntitySets(DbTransaction transaction, IEnumerable affectedEntitySets) + { + Guard.NotNull(transaction, nameof(transaction)); + Guard.NotNull(affectedEntitySets, nameof(affectedEntitySets)); + + var entitySets = _affectedSetsInTransaction.GetOrAdd(transaction, new List()); + entitySets.AddRange(affectedEntitySets); + } + + private IEnumerable RemoveAffectedEntitySets(DbTransaction transaction) + { + List affectedEntitySets; + + _affectedSetsInTransaction.TryRemove(transaction, out affectedEntitySets); + + return affectedEntitySets; + } + + public void Committed(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) + { + var entitySets = RemoveAffectedEntitySets(transaction); + + if (entitySets != null) + { + _cache.InvalidateSets(entitySets); + _cache.RequestInvalidateSets(entitySets); + } + } + + public void Committing(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) + { + } + + public void ConnectionGetting(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) + { + } + + public void ConnectionGot(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) + { + } + + public void Disposed(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) + { + } + + public void Disposing(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) + { + } + + public void IsolationLevelGetting(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) + { + } + + public void IsolationLevelGot(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) + { + } + + public void RolledBack(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) + { + RemoveAffectedEntitySets(transaction); + } + + public void RollingBack(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) + { + } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/CachingCommand.cs b/src/Libraries/SmartStore.Data/Caching/CachingCommand.cs index 93dda75187..bd205c5956 100644 --- a/src/Libraries/SmartStore.Data/Caching/CachingCommand.cs +++ b/src/Libraries/SmartStore.Data/Caching/CachingCommand.cs @@ -3,7 +3,6 @@ using System.ComponentModel; using System.Data; using System.Data.Common; -using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading; @@ -11,396 +10,327 @@ namespace SmartStore.Data.Caching { - [DesignerCategory("")] - internal sealed class CachingCommand : DbCommand - { - private readonly DbCommand _command; - private readonly CommandTreeFacts _commandTreeFacts; - private readonly CacheTransactionInterceptor _cacheTransactionInterceptor; - private readonly DbCachingPolicy _policy; - - public CachingCommand( - DbCommand command, - CommandTreeFacts commandTreeFacts, - CacheTransactionInterceptor cacheTransactionInterceptor, - DbCachingPolicy policy) - { - Guard.NotNull(command, nameof(command)); - Guard.NotNull(commandTreeFacts, nameof(commandTreeFacts)); - Guard.NotNull(cacheTransactionInterceptor, nameof(cacheTransactionInterceptor)); - - _command = command; - _commandTreeFacts = commandTreeFacts; - _cacheTransactionInterceptor = cacheTransactionInterceptor; - _policy = policy; - } - - internal CommandTreeFacts CommandTreeFacts - { - get { return _commandTreeFacts; } - } - - internal CacheTransactionInterceptor CacheTransactionInterceptor - { - get { return _cacheTransactionInterceptor; } - } - - internal DbCachingPolicy CachingPolicy - { - get { return _policy; } - } - - internal DbCommand WrappedCommand - { - get { return _command; } - } - - private bool IsCacheable - { - get - { - return _commandTreeFacts.IsQuery && - (IsQueryCached || !_commandTreeFacts.UsesNonDeterministicFunctions - && _policy.CanBeCached(_commandTreeFacts.AffectedEntitySets, CommandText, - Parameters.Cast() - .Select(p => new KeyValuePair(p.ParameterName, p.Value)))); - } - } - - private bool IsQueryCached - { - get - { - return SingletonQueries.Current.IsQueryCached(_commandTreeFacts.MetadataWorkspace, CommandText); - } - } - - public override void Cancel() - { - _command.Cancel(); - } - - public override string CommandText - { - get - { - return _command.CommandText; - } - set - { - _command.CommandText = value; - } - } - - public override int CommandTimeout - { - get - { - return _command.CommandTimeout; - } - set - { - _command.CommandTimeout = value; - } - } - - public override CommandType CommandType - { - get - { - return _command.CommandType; - } - set - { - _command.CommandType = value; - } - } - - protected override DbParameter CreateDbParameter() - { - return _command.CreateParameter(); - } - - protected override DbConnection DbConnection - { - get - { - return _command.Connection; - } - set - { - _command.Connection = value; - } - } - - protected override DbParameterCollection DbParameterCollection - { - get { return _command.Parameters; } - } - - protected override DbTransaction DbTransaction - { - get - { - return _command.Transaction; - } - set - { - _command.Transaction = value; - } - } - - public override bool DesignTimeVisible - { - get - { - return _command.DesignTimeVisible; - } - set - { - _command.DesignTimeVisible = value; - } - } - - protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) - { - if (!IsCacheable) - { - var result = _command.ExecuteReader(behavior); - - if (!_commandTreeFacts.IsQuery) - { - _cacheTransactionInterceptor.InvalidateSets(Transaction, _commandTreeFacts.AffectedEntitySets.Select(s => s.Name)); - } - - return result; - } - - var key = CreateKey(); - - object value; - if (_cacheTransactionInterceptor.GetItem(Transaction, key, out value)) - { - return new CachingDataReader((CachedRecords)value); - } - - using (var reader = _command.ExecuteReader(behavior)) - { - var queryResults = new List(); - - while (reader.Read()) - { - var values = new object[reader.FieldCount]; - reader.GetValues(values); - queryResults.Add(values); - } - - return HandleCaching(reader, key, queryResults); - } - } - - protected async override Task ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) - { - if (!IsCacheable) - { - var result = await _command.ExecuteReaderAsync(behavior, cancellationToken); - - if (!_commandTreeFacts.IsQuery) - { - _cacheTransactionInterceptor.InvalidateSets(Transaction, _commandTreeFacts.AffectedEntitySets.Select(s => s.Name)); - } - - return result; - } - - var key = CreateKey(); - - object value; - if (_cacheTransactionInterceptor.GetItem(Transaction, key, out value)) - { - return new CachingDataReader((CachedRecords)value); - } - - using (var reader = await _command.ExecuteReaderAsync(behavior, cancellationToken)) - { - var queryResults = new List(); - - while (await reader.ReadAsync(cancellationToken)) - { - var values = new object[reader.FieldCount]; - reader.GetValues(values); - queryResults.Add(values); - } - - return HandleCaching(reader, key, queryResults); - } - } - - private DbDataReader HandleCaching(DbDataReader reader, string key, List queryResults) - { - var cachedResult = new CachedRecords - { - TableMetadata = GetTableMetadata(reader), - Records = queryResults, - RecordsAffected = reader.RecordsAffected - }; - - int minCacheableRows, maxCachableRows; - _policy.GetCacheableRows(_commandTreeFacts.AffectedEntitySets, out minCacheableRows, out maxCachableRows); - - if (IsQueryCached || (queryResults.Count >= minCacheableRows && queryResults.Count <= maxCachableRows)) - { - TimeSpan? duration = _policy.GetExpirationTimeout(_commandTreeFacts.AffectedEntitySets); - - _cacheTransactionInterceptor.PutItem( - Transaction, - key, - cachedResult, - _commandTreeFacts.AffectedEntitySets.Select(s => s.Name), - duration); - } - - return new CachingDataReader(cachedResult); - } - - protected override void Dispose(bool disposing) - { - _command.GetType() - .GetMethod("Dispose", BindingFlags.Instance | BindingFlags.NonPublic) - .Invoke(_command, new object[] { disposing }); - } - - private static ColumnMetadata[] GetTableMetadata(DbDataReader reader) - { - var columnMetadata = new ColumnMetadata[reader.FieldCount]; - - for (var i = 0; i < reader.FieldCount; i++) - { - columnMetadata[i] = new ColumnMetadata - { - Name = reader.GetName(i), - DataTypeName = reader.GetDataTypeName(i), - DataType = reader.GetFieldType(i) - }; - } - - return columnMetadata; - } - - public override int ExecuteNonQuery() - { - var recordsAffected = _command.ExecuteNonQuery(); - - InvalidateSetsForNonQuery(recordsAffected); - - return recordsAffected; - } - - public override async Task ExecuteNonQueryAsync(CancellationToken cancellationToken) - { - var recordsAffected = await _command.ExecuteNonQueryAsync(cancellationToken); - - InvalidateSetsForNonQuery(recordsAffected); - - return recordsAffected; - } - - private void InvalidateSetsForNonQuery(int recordsAffected) - { - if (recordsAffected > 0 && _commandTreeFacts.AffectedEntitySets.Any()) - { - _cacheTransactionInterceptor.InvalidateSets(Transaction, _commandTreeFacts.AffectedEntitySets.Select(s => s.Name)); - } - } - - public override object ExecuteScalar() - { - if (!IsCacheable) - { - return _command.ExecuteScalar(); - } - - var key = CreateKey(); - - object value; - - if (_cacheTransactionInterceptor.GetItem(Transaction, key, out value)) - { - return value; - } - - value = _command.ExecuteScalar(); - - // TODO: somehow determine expiration - TimeSpan? duration = null; - - _cacheTransactionInterceptor.PutItem( - Transaction, - key, - value, - _commandTreeFacts.AffectedEntitySets.Select(s => s.Name), - duration); - - return value; - } - - public async override Task ExecuteScalarAsync(CancellationToken cancellationToken) - { - if (!IsCacheable) - { - return await _command.ExecuteScalarAsync(cancellationToken); - } - - var key = CreateKey(); - - if (_cacheTransactionInterceptor.GetItem(Transaction, key, out var value)) - { - return value; - } - - value = await _command.ExecuteScalarAsync(cancellationToken); - - // TODO: somehow determine expiration - TimeSpan? duration = null; - - _cacheTransactionInterceptor.PutItem( - Transaction, - key, - value, - _commandTreeFacts.AffectedEntitySets.Select(s => s.Name), - duration); - - return value; - } - - public override void Prepare() - { - _command.Prepare(); - } - - public override UpdateRowSource UpdatedRowSource - { - get - { - return _command.UpdatedRowSource; - } - set - { - _command.UpdatedRowSource = value; - } - } - - private string CreateKey() - { - return - string.Format( - "{0}_{1}_{2}", - Connection.Database, - CommandText, - string.Join( - "_", - Parameters.Cast() - .Select(p => string.Format("{0}={1}", p.ParameterName, p.Value)))); - } - } + [DesignerCategory("")] + internal sealed class CachingCommand : DbCommand + { + private readonly DbCommand _command; + private readonly CommandTreeFacts _commandTreeFacts; + private readonly CacheTransactionInterceptor _cacheTransactionInterceptor; + private readonly DbCachingPolicy _policy; + + public CachingCommand( + DbCommand command, + CommandTreeFacts commandTreeFacts, + CacheTransactionInterceptor cacheTransactionInterceptor, + DbCachingPolicy policy) + { + Guard.NotNull(command, nameof(command)); + Guard.NotNull(commandTreeFacts, nameof(commandTreeFacts)); + Guard.NotNull(cacheTransactionInterceptor, nameof(cacheTransactionInterceptor)); + + _command = command; + _commandTreeFacts = commandTreeFacts; + _cacheTransactionInterceptor = cacheTransactionInterceptor; + _policy = policy; + } + + internal CommandTreeFacts CommandTreeFacts => _commandTreeFacts; + + internal CacheTransactionInterceptor CacheTransactionInterceptor => _cacheTransactionInterceptor; + + internal DbCachingPolicy CachingPolicy => _policy; + + internal DbCommand WrappedCommand => _command; + + private bool IsCacheable => _commandTreeFacts.IsQuery && + (IsQueryCached || !_commandTreeFacts.UsesNonDeterministicFunctions + && _policy.CanBeCached(_commandTreeFacts.AffectedEntitySets, CommandText, + Parameters.Cast() + .Select(p => new KeyValuePair(p.ParameterName, p.Value)))); + + private bool IsQueryCached => SingletonQueries.Current.IsQueryCached(_commandTreeFacts.MetadataWorkspace, CommandText); + + public override void Cancel() + { + _command.Cancel(); + } + + public override string CommandText + { + get => _command.CommandText; + set => _command.CommandText = value; + } + + public override int CommandTimeout + { + get => _command.CommandTimeout; + set => _command.CommandTimeout = value; + } + + public override CommandType CommandType + { + get => _command.CommandType; + set => _command.CommandType = value; + } + + protected override DbParameter CreateDbParameter() + { + return _command.CreateParameter(); + } + + protected override DbConnection DbConnection + { + get => _command.Connection; + set => _command.Connection = value; + } + + protected override DbParameterCollection DbParameterCollection => _command.Parameters; + + protected override DbTransaction DbTransaction + { + get => _command.Transaction; + set => _command.Transaction = value; + } + + public override bool DesignTimeVisible + { + get => _command.DesignTimeVisible; + set => _command.DesignTimeVisible = value; + } + + protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) + { + if (!IsCacheable) + { + var result = _command.ExecuteReader(behavior); + + if (!_commandTreeFacts.IsQuery) + { + _cacheTransactionInterceptor.InvalidateSets(Transaction, _commandTreeFacts.AffectedEntitySets.Select(s => s.Name)); + } + + return result; + } + + var key = CreateKey(); + + object value; + if (_cacheTransactionInterceptor.GetItem(Transaction, key, out value)) + { + return new CachingDataReader((CachedRecords)value); + } + + using (var reader = _command.ExecuteReader(behavior)) + { + var queryResults = new List(); + + while (reader.Read()) + { + var values = new object[reader.FieldCount]; + reader.GetValues(values); + queryResults.Add(values); + } + + return HandleCaching(reader, key, queryResults); + } + } + + protected async override Task ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) + { + if (!IsCacheable) + { + var result = await _command.ExecuteReaderAsync(behavior, cancellationToken); + + if (!_commandTreeFacts.IsQuery) + { + _cacheTransactionInterceptor.InvalidateSets(Transaction, _commandTreeFacts.AffectedEntitySets.Select(s => s.Name)); + } + + return result; + } + + var key = CreateKey(); + + object value; + if (_cacheTransactionInterceptor.GetItem(Transaction, key, out value)) + { + return new CachingDataReader((CachedRecords)value); + } + + using (var reader = await _command.ExecuteReaderAsync(behavior, cancellationToken)) + { + var queryResults = new List(); + + while (await reader.ReadAsync(cancellationToken)) + { + var values = new object[reader.FieldCount]; + reader.GetValues(values); + queryResults.Add(values); + } + + return HandleCaching(reader, key, queryResults); + } + } + + private DbDataReader HandleCaching(DbDataReader reader, string key, List queryResults) + { + var cachedResult = new CachedRecords + { + TableMetadata = GetTableMetadata(reader), + Records = queryResults, + RecordsAffected = reader.RecordsAffected + }; + + int minCacheableRows, maxCachableRows; + _policy.GetCacheableRows(_commandTreeFacts.AffectedEntitySets, out minCacheableRows, out maxCachableRows); + + if (IsQueryCached || (queryResults.Count >= minCacheableRows && queryResults.Count <= maxCachableRows)) + { + TimeSpan? duration = _policy.GetExpirationTimeout(_commandTreeFacts.AffectedEntitySets); + + _cacheTransactionInterceptor.PutItem( + Transaction, + key, + cachedResult, + _commandTreeFacts.AffectedEntitySets.Select(s => s.Name), + duration); + } + + return new CachingDataReader(cachedResult); + } + + protected override void Dispose(bool disposing) + { + _command.GetType() + .GetMethod("Dispose", BindingFlags.Instance | BindingFlags.NonPublic) + .Invoke(_command, new object[] { disposing }); + } + + private static ColumnMetadata[] GetTableMetadata(DbDataReader reader) + { + var columnMetadata = new ColumnMetadata[reader.FieldCount]; + + for (var i = 0; i < reader.FieldCount; i++) + { + columnMetadata[i] = new ColumnMetadata + { + Name = reader.GetName(i), + DataTypeName = reader.GetDataTypeName(i), + DataType = reader.GetFieldType(i) + }; + } + + return columnMetadata; + } + + public override int ExecuteNonQuery() + { + var recordsAffected = _command.ExecuteNonQuery(); + + InvalidateSetsForNonQuery(recordsAffected); + + return recordsAffected; + } + + public override async Task ExecuteNonQueryAsync(CancellationToken cancellationToken) + { + var recordsAffected = await _command.ExecuteNonQueryAsync(cancellationToken); + + InvalidateSetsForNonQuery(recordsAffected); + + return recordsAffected; + } + + private void InvalidateSetsForNonQuery(int recordsAffected) + { + if (recordsAffected > 0 && _commandTreeFacts.AffectedEntitySets.Any()) + { + _cacheTransactionInterceptor.InvalidateSets(Transaction, _commandTreeFacts.AffectedEntitySets.Select(s => s.Name)); + } + } + + public override object ExecuteScalar() + { + if (!IsCacheable) + { + return _command.ExecuteScalar(); + } + + var key = CreateKey(); + + object value; + + if (_cacheTransactionInterceptor.GetItem(Transaction, key, out value)) + { + return value; + } + + value = _command.ExecuteScalar(); + + // TODO: somehow determine expiration + TimeSpan? duration = null; + + _cacheTransactionInterceptor.PutItem( + Transaction, + key, + value, + _commandTreeFacts.AffectedEntitySets.Select(s => s.Name), + duration); + + return value; + } + + public async override Task ExecuteScalarAsync(CancellationToken cancellationToken) + { + if (!IsCacheable) + { + return await _command.ExecuteScalarAsync(cancellationToken); + } + + var key = CreateKey(); + + if (_cacheTransactionInterceptor.GetItem(Transaction, key, out var value)) + { + return value; + } + + value = await _command.ExecuteScalarAsync(cancellationToken); + + // TODO: somehow determine expiration + TimeSpan? duration = null; + + _cacheTransactionInterceptor.PutItem( + Transaction, + key, + value, + _commandTreeFacts.AffectedEntitySets.Select(s => s.Name), + duration); + + return value; + } + + public override void Prepare() + { + _command.Prepare(); + } + + public override UpdateRowSource UpdatedRowSource + { + get => _command.UpdatedRowSource; + set => _command.UpdatedRowSource = value; + } + + private string CreateKey() + { + return + string.Format( + "{0}_{1}_{2}", + Connection.Database, + CommandText, + string.Join( + "_", + Parameters.Cast() + .Select(p => string.Format("{0}={1}", p.ParameterName, p.Value)))); + } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/CachingCommandDefinition.cs b/src/Libraries/SmartStore.Data/Caching/CachingCommandDefinition.cs index 872f49658e..0f9e2754c5 100644 --- a/src/Libraries/SmartStore.Data/Caching/CachingCommandDefinition.cs +++ b/src/Libraries/SmartStore.Data/Caching/CachingCommandDefinition.cs @@ -5,43 +5,34 @@ namespace SmartStore.Data.Caching { - internal class CachingCommandDefinition : DbCommandDefinition - { - private readonly DbCommandDefinition _commandDefintion; - private readonly CommandTreeFacts _commandTreeFacts; - private readonly CacheTransactionInterceptor _cacheTransactionInterceptor; - private readonly DbCachingPolicy _policy; + internal class CachingCommandDefinition : DbCommandDefinition + { + private readonly DbCommandDefinition _commandDefintion; + private readonly CommandTreeFacts _commandTreeFacts; + private readonly CacheTransactionInterceptor _cacheTransactionInterceptor; + private readonly DbCachingPolicy _policy; - public CachingCommandDefinition( - DbCommandDefinition commandDefinition, - CommandTreeFacts commandTreeFacts, - CacheTransactionInterceptor cacheTransactionInterceptor, - DbCachingPolicy policy) - { - _commandDefintion = commandDefinition; - _commandTreeFacts = commandTreeFacts; - _cacheTransactionInterceptor = cacheTransactionInterceptor; - _policy = policy; - } + public CachingCommandDefinition( + DbCommandDefinition commandDefinition, + CommandTreeFacts commandTreeFacts, + CacheTransactionInterceptor cacheTransactionInterceptor, + DbCachingPolicy policy) + { + _commandDefintion = commandDefinition; + _commandTreeFacts = commandTreeFacts; + _cacheTransactionInterceptor = cacheTransactionInterceptor; + _policy = policy; + } - public bool IsQuery - { - get { return _commandTreeFacts.IsQuery; } - } + public bool IsQuery => _commandTreeFacts.IsQuery; - public bool IsCacheable - { - get { return _commandTreeFacts.IsQuery && !_commandTreeFacts.UsesNonDeterministicFunctions; } - } + public bool IsCacheable => _commandTreeFacts.IsQuery && !_commandTreeFacts.UsesNonDeterministicFunctions; - public ReadOnlyCollection AffectedEntitySets - { - get { return _commandTreeFacts.AffectedEntitySets; } - } + public ReadOnlyCollection AffectedEntitySets => _commandTreeFacts.AffectedEntitySets; - public override DbCommand CreateCommand() - { - return new CachingCommand(_commandDefintion.CreateCommand(), _commandTreeFacts, _cacheTransactionInterceptor, _policy); - } - } + public override DbCommand CreateCommand() + { + return new CachingCommand(_commandDefintion.CreateCommand(), _commandTreeFacts, _cacheTransactionInterceptor, _policy); + } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/CachingDataReader.cs b/src/Libraries/SmartStore.Data/Caching/CachingDataReader.cs index 9f1f1fc3b0..b2467cdbbb 100644 --- a/src/Libraries/SmartStore.Data/Caching/CachingDataReader.cs +++ b/src/Libraries/SmartStore.Data/Caching/CachingDataReader.cs @@ -3,266 +3,247 @@ using System.Collections.Generic; using System.Data; using System.Data.Common; -using System.Diagnostics; namespace SmartStore.Data.Caching { - internal class CachingDataReader : DbDataReader - { - private enum State - { - BOF, - Reading, - EOF, - Closed, - Disposed - } - - private State _state; - private readonly ColumnMetadata[] _tableMetadata; - private readonly int _recordsAffected; - - // TODO: multiple resultsets? - private readonly IEnumerator _resultRowsEnumerator; - - internal CachingDataReader(CachedRecords cachedResult) - { - Guard.NotNull(cachedResult, nameof(cachedResult)); - - _tableMetadata = cachedResult.TableMetadata; - _recordsAffected = cachedResult.RecordsAffected; - _resultRowsEnumerator = cachedResult.Records.GetEnumerator(); - _state = State.BOF; - } - - public override void Close() - { - EnsureNotDisposed(); - - _state = State.Closed; - } - - public override int Depth - { - get { throw new NotImplementedException(); } - } - - public override int FieldCount - { - get { return _tableMetadata.Length; } - } - - public override bool GetBoolean(int ordinal) - { - return GetValue(ordinal).Convert(); - } - - public override byte GetByte(int ordinal) - { - return GetValue(ordinal).Convert(); - } - - public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length) - { - throw new NotImplementedException(); - } - - public override char GetChar(int ordinal) - { - throw new NotImplementedException(); - } - - public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length) - { - throw new NotImplementedException(); - } - - public override string GetDataTypeName(int ordinal) - { - return _tableMetadata[ordinal].DataTypeName; - } - - public override DateTime GetDateTime(int ordinal) - { - return GetValue(ordinal).Convert(); - } - - public override decimal GetDecimal(int ordinal) - { - return GetValue(ordinal).Convert(); - } - - public override double GetDouble(int ordinal) - { - return (double)GetValue(ordinal); - } - - public override IEnumerator GetEnumerator() - { - throw new NotImplementedException(); - } - - public override Type GetFieldType(int ordinal) - { - return _tableMetadata[ordinal].DataType; - } - - public override float GetFloat(int ordinal) - { - return GetValue(ordinal).Convert(); - } - - public override Guid GetGuid(int ordinal) - { - return GetValue(ordinal).Convert(); - } - - public override short GetInt16(int ordinal) - { - return GetValue(ordinal).Convert(); - } - - public override int GetInt32(int ordinal) - { - return GetValue(ordinal).Convert(); - } - - public override long GetInt64(int ordinal) - { - return GetValue(ordinal).Convert(); - } - - public override string GetName(int ordinal) - { - return _tableMetadata[ordinal].Name; - } - - public override int GetOrdinal(string name) - { - throw new NotImplementedException(); - } - - public override DataTable GetSchemaTable() - { - throw new NotSupportedException(); - } - - public override string GetString(int ordinal) - { - return GetValue(ordinal).Convert(); - } - - public override object GetValue(int ordinal) - { - EnsureReading(); - - return _resultRowsEnumerator.Current[ordinal]; - } - - public override int GetValues(object[] values) - { - throw new NotImplementedException(); - } - - public override bool HasRows - { - get { throw new NotImplementedException(); } - } - - public override bool IsClosed - { - get - { - EnsureNotDisposed(); - - return _state == State.Closed; - } - } - - public override bool IsDBNull(int ordinal) - { - return GetValue(ordinal) == DBNull.Value || GetValue(ordinal) == null; - } - - public override bool NextResult() - { - // TODO: Multiple resultsets - return false; - } - - public override bool Read() - { - EnsureNotClosed(); - - var result = _resultRowsEnumerator.MoveNext(); - - _state = result ? State.Reading : State.EOF; - - return result; - } - - public override int RecordsAffected - { - get { return _recordsAffected; } - } - - public override object this[string name] - { - get { throw new NotImplementedException(); } - } - - public override object this[int ordinal] - { - get { throw new NotImplementedException(); } - } - - private void EnsureReading() - { - EnsureNotClosed(); - EnsureNotBOF(); - EnsureNotEOF(); - } - - private void EnsureNotClosed() - { - if (IsClosed) - { - throw new InvalidOperationException("Reader has already been closed."); - } - } - - private void EnsureNotDisposed() - { - if (_state == State.Disposed) - { - throw new InvalidOperationException("Object has already been disposed."); - } - } - - private void EnsureNotBOF() - { - if (_state == State.BOF) - { - throw new InvalidOperationException("The operation is invalid before reading any data."); - } - } - - private void EnsureNotEOF() - { - if (_state == State.EOF) - { - throw new InvalidOperationException("The operation is invalid after reading all data."); - } - } - - protected override void Dispose(bool disposing) - { - // base.Dispose() will call Close() - base.Dispose(disposing); - - _resultRowsEnumerator.Dispose(); - - _state = State.Disposed; - } - } + internal class CachingDataReader : DbDataReader + { + private enum State + { + BOF, + Reading, + EOF, + Closed, + Disposed + } + + private State _state; + private readonly ColumnMetadata[] _tableMetadata; + private readonly int _recordsAffected; + + // TODO: multiple resultsets? + private readonly IEnumerator _resultRowsEnumerator; + + internal CachingDataReader(CachedRecords cachedResult) + { + Guard.NotNull(cachedResult, nameof(cachedResult)); + + _tableMetadata = cachedResult.TableMetadata; + _recordsAffected = cachedResult.RecordsAffected; + _resultRowsEnumerator = cachedResult.Records.GetEnumerator(); + _state = State.BOF; + } + + public override void Close() + { + EnsureNotDisposed(); + + _state = State.Closed; + } + + public override int Depth => throw new NotImplementedException(); + + public override int FieldCount => _tableMetadata.Length; + + public override bool GetBoolean(int ordinal) + { + return GetValue(ordinal).Convert(); + } + + public override byte GetByte(int ordinal) + { + return GetValue(ordinal).Convert(); + } + + public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length) + { + throw new NotImplementedException(); + } + + public override char GetChar(int ordinal) + { + throw new NotImplementedException(); + } + + public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length) + { + throw new NotImplementedException(); + } + + public override string GetDataTypeName(int ordinal) + { + return _tableMetadata[ordinal].DataTypeName; + } + + public override DateTime GetDateTime(int ordinal) + { + return GetValue(ordinal).Convert(); + } + + public override decimal GetDecimal(int ordinal) + { + return GetValue(ordinal).Convert(); + } + + public override double GetDouble(int ordinal) + { + return (double)GetValue(ordinal); + } + + public override IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + public override Type GetFieldType(int ordinal) + { + return _tableMetadata[ordinal].DataType; + } + + public override float GetFloat(int ordinal) + { + return GetValue(ordinal).Convert(); + } + + public override Guid GetGuid(int ordinal) + { + return GetValue(ordinal).Convert(); + } + + public override short GetInt16(int ordinal) + { + return GetValue(ordinal).Convert(); + } + + public override int GetInt32(int ordinal) + { + return GetValue(ordinal).Convert(); + } + + public override long GetInt64(int ordinal) + { + return GetValue(ordinal).Convert(); + } + + public override string GetName(int ordinal) + { + return _tableMetadata[ordinal].Name; + } + + public override int GetOrdinal(string name) + { + throw new NotImplementedException(); + } + + public override DataTable GetSchemaTable() + { + throw new NotSupportedException(); + } + + public override string GetString(int ordinal) + { + return GetValue(ordinal).Convert(); + } + + public override object GetValue(int ordinal) + { + EnsureReading(); + + return _resultRowsEnumerator.Current[ordinal]; + } + + public override int GetValues(object[] values) + { + throw new NotImplementedException(); + } + + public override bool HasRows => throw new NotImplementedException(); + + public override bool IsClosed + { + get + { + EnsureNotDisposed(); + + return _state == State.Closed; + } + } + + public override bool IsDBNull(int ordinal) + { + return GetValue(ordinal) == DBNull.Value || GetValue(ordinal) == null; + } + + public override bool NextResult() + { + // TODO: Multiple resultsets + return false; + } + + public override bool Read() + { + EnsureNotClosed(); + + var result = _resultRowsEnumerator.MoveNext(); + + _state = result ? State.Reading : State.EOF; + + return result; + } + + public override int RecordsAffected => _recordsAffected; + + public override object this[string name] => throw new NotImplementedException(); + + public override object this[int ordinal] => throw new NotImplementedException(); + + private void EnsureReading() + { + EnsureNotClosed(); + EnsureNotBOF(); + EnsureNotEOF(); + } + + private void EnsureNotClosed() + { + if (IsClosed) + { + throw new InvalidOperationException("Reader has already been closed."); + } + } + + private void EnsureNotDisposed() + { + if (_state == State.Disposed) + { + throw new InvalidOperationException("Object has already been disposed."); + } + } + + private void EnsureNotBOF() + { + if (_state == State.BOF) + { + throw new InvalidOperationException("The operation is invalid before reading any data."); + } + } + + private void EnsureNotEOF() + { + if (_state == State.EOF) + { + throw new InvalidOperationException("The operation is invalid after reading all data."); + } + } + + protected override void Dispose(bool disposing) + { + // base.Dispose() will call Close() + base.Dispose(disposing); + + _resultRowsEnumerator.Dispose(); + + _state = State.Disposed; + } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/CachingProviderServices.cs b/src/Libraries/SmartStore.Data/Caching/CachingProviderServices.cs index 92bc8be666..06085ea2c7 100644 --- a/src/Libraries/SmartStore.Data/Caching/CachingProviderServices.cs +++ b/src/Libraries/SmartStore.Data/Caching/CachingProviderServices.cs @@ -5,130 +5,130 @@ using System.Data.Entity.Core.Common.CommandTrees; using System.Data.Entity.Core.Metadata.Edm; using System.Data.Entity.Spatial; -using SmartStore.Utilities; using SmartStore.Data.Setup; +using SmartStore.Utilities; namespace SmartStore.Data.Caching { - public class CachingProviderServices : DbProviderServices - { - private readonly DbProviderServices _providerServices; - private readonly CacheTransactionInterceptor _cacheTransactionInterceptor; - private readonly DbCachingPolicy _policy; - - public CachingProviderServices(DbProviderServices providerServices, CacheTransactionInterceptor cacheTransactionInterceptor, DbCachingPolicy policy = null) - { - _providerServices = providerServices; - _cacheTransactionInterceptor = cacheTransactionInterceptor; - _policy = policy ?? new DbCachingPolicy(); - } - - internal bool HasPendingMigrations - { - get - { - if (CommonHelper.IsDevEnvironment) - { - // Don't bother with database cache when we're in (IDE) debug mode - // and migrations are about to be executed: EF throws exceptions during migration - // on purpose, which are harmless in release mode, but really annoying and confusing - // in debug mode. Namely, execution will break within our 'CacheTransactionInterceptor', - // and we don't want that. - return DbSeedingMigrator.IsMigrating; - } - - return false; - } - } - - protected override DbCommandDefinition CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree) - { - var commandDefinition = _providerServices.CreateCommandDefinition(providerManifest, commandTree); - - return HasPendingMigrations - ? commandDefinition - : new CachingCommandDefinition( - commandDefinition, - new CommandTreeFacts(commandTree), - _cacheTransactionInterceptor, - _policy); - } - - protected override DbProviderManifest GetDbProviderManifest(string manifestToken) - { - return _providerServices.GetProviderManifest(manifestToken); - } - - protected override string GetDbProviderManifestToken(DbConnection connection) - { - return _providerServices.GetProviderManifestToken(connection); - } - - protected override void DbCreateDatabase(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) - { - _providerServices.CreateDatabase(connection, commandTimeout, storeItemCollection); - } - - protected override string DbCreateDatabaseScript(string providerManifestToken, StoreItemCollection storeItemCollection) - { - return _providerServices.CreateDatabaseScript(providerManifestToken, storeItemCollection); - } - - protected override bool DbDatabaseExists(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) - { - return _providerServices.DatabaseExists(connection, commandTimeout, storeItemCollection); - } - - protected override void DbDeleteDatabase(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) - { - _providerServices.DeleteDatabase(connection, commandTimeout, storeItemCollection); - } - - protected override void SetDbParameterValue(DbParameter parameter, TypeUsage parameterType, object value) - { - _providerServices.SetParameterValue(parameter, parameterType, value); - } - - protected override DbSpatialDataReader GetDbSpatialDataReader(DbDataReader fromReader, string manifestToken) - { - return _providerServices.GetSpatialDataReader(fromReader, manifestToken); - } + public class CachingProviderServices : DbProviderServices + { + private readonly DbProviderServices _providerServices; + private readonly CacheTransactionInterceptor _cacheTransactionInterceptor; + private readonly DbCachingPolicy _policy; + + public CachingProviderServices(DbProviderServices providerServices, CacheTransactionInterceptor cacheTransactionInterceptor, DbCachingPolicy policy = null) + { + _providerServices = providerServices; + _cacheTransactionInterceptor = cacheTransactionInterceptor; + _policy = policy ?? new DbCachingPolicy(); + } + + internal bool HasPendingMigrations + { + get + { + if (CommonHelper.IsDevEnvironment) + { + // Don't bother with database cache when we're in (IDE) debug mode + // and migrations are about to be executed: EF throws exceptions during migration + // on purpose, which are harmless in release mode, but really annoying and confusing + // in debug mode. Namely, execution will break within our 'CacheTransactionInterceptor', + // and we don't want that. + return DbSeedingMigrator.IsMigrating; + } + + return false; + } + } + + protected override DbCommandDefinition CreateDbCommandDefinition(DbProviderManifest providerManifest, DbCommandTree commandTree) + { + var commandDefinition = _providerServices.CreateCommandDefinition(providerManifest, commandTree); + + return HasPendingMigrations + ? commandDefinition + : new CachingCommandDefinition( + commandDefinition, + new CommandTreeFacts(commandTree), + _cacheTransactionInterceptor, + _policy); + } + + protected override DbProviderManifest GetDbProviderManifest(string manifestToken) + { + return _providerServices.GetProviderManifest(manifestToken); + } + + protected override string GetDbProviderManifestToken(DbConnection connection) + { + return _providerServices.GetProviderManifestToken(connection); + } + + protected override void DbCreateDatabase(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) + { + _providerServices.CreateDatabase(connection, commandTimeout, storeItemCollection); + } + + protected override string DbCreateDatabaseScript(string providerManifestToken, StoreItemCollection storeItemCollection) + { + return _providerServices.CreateDatabaseScript(providerManifestToken, storeItemCollection); + } + + protected override bool DbDatabaseExists(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) + { + return _providerServices.DatabaseExists(connection, commandTimeout, storeItemCollection); + } + + protected override void DbDeleteDatabase(DbConnection connection, int? commandTimeout, StoreItemCollection storeItemCollection) + { + _providerServices.DeleteDatabase(connection, commandTimeout, storeItemCollection); + } + + protected override void SetDbParameterValue(DbParameter parameter, TypeUsage parameterType, object value) + { + _providerServices.SetParameterValue(parameter, parameterType, value); + } + + protected override DbSpatialDataReader GetDbSpatialDataReader(DbDataReader fromReader, string manifestToken) + { + return _providerServices.GetSpatialDataReader(fromReader, manifestToken); + } #pragma warning disable 618, 672 - protected override DbSpatialServices DbGetSpatialServices(string manifestToken) - { - return _providerServices.GetSpatialServices(manifestToken); - } + protected override DbSpatialServices DbGetSpatialServices(string manifestToken) + { + return _providerServices.GetSpatialServices(manifestToken); + } #pragma warning restore 618, 672 - public override object GetService(Type type, object key) - { - return _providerServices.GetService(type, key); - } - - public override IEnumerable GetServices(Type type, object key) - { - return _providerServices.GetServices(type, key); - } - - public override void RegisterInfoMessageHandler(DbConnection connection, Action handler) - { - _providerServices.RegisterInfoMessageHandler(connection, handler); - } - - public override DbCommandDefinition CreateCommandDefinition(DbCommand prototype) - { - var command = prototype as CachingCommand; - - var commandDefinition = - _providerServices.CreateCommandDefinition( - command != null - ? command.WrappedCommand - : prototype); - - return command != null - ? new CachingCommandDefinition(commandDefinition, command.CommandTreeFacts, command.CacheTransactionInterceptor, command.CachingPolicy) - : commandDefinition; - } - } + public override object GetService(Type type, object key) + { + return _providerServices.GetService(type, key); + } + + public override IEnumerable GetServices(Type type, object key) + { + return _providerServices.GetServices(type, key); + } + + public override void RegisterInfoMessageHandler(DbConnection connection, Action handler) + { + _providerServices.RegisterInfoMessageHandler(connection, handler); + } + + public override DbCommandDefinition CreateCommandDefinition(DbCommand prototype) + { + var command = prototype as CachingCommand; + + var commandDefinition = + _providerServices.CreateCommandDefinition( + command != null + ? command.WrappedCommand + : prototype); + + return command != null + ? new CachingCommandDefinition(commandDefinition, command.CommandTreeFacts, command.CacheTransactionInterceptor, command.CachingPolicy) + : commandDefinition; + } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/CommandTreeFacts.cs b/src/Libraries/SmartStore.Data/Caching/CommandTreeFacts.cs index f9410dd8e9..37e4bba8d6 100644 --- a/src/Libraries/SmartStore.Data/Caching/CommandTreeFacts.cs +++ b/src/Libraries/SmartStore.Data/Caching/CommandTreeFacts.cs @@ -9,111 +9,111 @@ namespace SmartStore.Data.Caching { - internal class CommandTreeFacts - { - private static readonly HashSet NonDeterministicFunctions = new HashSet( - new[] { - "Edm.CurrentDateTime", - "Edm.CurrentUtcDateTime", - "Edm.CurrentDateTimeOffsets", - "Edm.NewGuid", - - "SqlServer.NEWID", - "SqlServer.GETDATE", - "SqlServer.GETUTCDATE", - "SqlServer.SYSDATETIME", - "SqlServer.SYSUTCDATETIME", - "SqlServer.SYSDATETIMEOFFSET", - "SqlServer.CURRENT_USER", - "SqlServer.CURRENT_TIMESTAMP", - "SqlServer.HOST_NAME", - "SqlServer.USER_NAME" - }, - StringComparer.OrdinalIgnoreCase); - - public CommandTreeFacts(DbCommandTree commandTree) - { - IsQuery = commandTree is DbQueryCommandTree; - - var visitor = new CommandTreeVisitor(); - - if (commandTree.CommandTreeKind == DbCommandTreeKind.Function) - { - var edmFunction = ((DbFunctionCommandTree)commandTree).EdmFunction; - - var containerMapping = - commandTree.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace).GetItems().Single(); - - var entitySetMappings = - containerMapping.EntitySetMappings.Where( - esm => esm.ModificationFunctionMappings.Any( - mfm => mfm.DeleteFunctionMapping.Function == edmFunction || - mfm.InsertFunctionMapping.Function == edmFunction || - mfm.UpdateFunctionMapping.Function == edmFunction)); - - AffectedEntitySets = - (from esm in entitySetMappings - from etm in esm.EntityTypeMappings - from mappingFragment in etm.Fragments.Where(f => f.StoreEntitySet != null) - select mappingFragment.StoreEntitySet).Cast().ToList().AsReadOnly(); - } - else - { - if (commandTree.CommandTreeKind == DbCommandTreeKind.Query) - { - ((DbQueryCommandTree)commandTree).Query.Accept(visitor); - } - else - { - Debug.Assert(commandTree is DbModificationCommandTree, "Unexpected command tree kind"); - - ((DbModificationCommandTree)commandTree).Target.Expression.Accept(visitor); - } - - AffectedEntitySets = new ReadOnlyCollection(visitor.EntitySets); - UsesNonDeterministicFunctions = - visitor.Functions.Any(f => NonDeterministicFunctions.Contains( - string.Format("{0}.{1}", f.NamespaceName, f.Name))); - } - - MetadataWorkspace = commandTree.MetadataWorkspace; - } - - // testing only - internal CommandTreeFacts(ReadOnlyCollection affectedEntitySets, bool isQuery, bool usesNonDeterministicFunctions, MetadataWorkspace metadataWorkspace = null) - { - AffectedEntitySets = affectedEntitySets; - IsQuery = isQuery; - UsesNonDeterministicFunctions = usesNonDeterministicFunctions; - MetadataWorkspace = metadataWorkspace ?? new MetadataWorkspace(); - } - - public ReadOnlyCollection AffectedEntitySets { get; private set; } - - public bool IsQuery { get; private set; } - - public bool UsesNonDeterministicFunctions { get; private set; } - - public MetadataWorkspace MetadataWorkspace { get; private set; } - - private class CommandTreeVisitor : BasicCommandTreeVisitor - { - public readonly List EntitySets = new List(); - public readonly List Functions = new List(); - - public override void Visit(DbScanExpression expression) - { - EntitySets.Add(expression.Target); - - base.Visit(expression); - } - - public override void Visit(DbFunctionExpression expression) - { - Functions.Add(expression.Function); - - base.Visit(expression); - } - } - } + internal class CommandTreeFacts + { + private static readonly HashSet NonDeterministicFunctions = new HashSet( + new[] { + "Edm.CurrentDateTime", + "Edm.CurrentUtcDateTime", + "Edm.CurrentDateTimeOffsets", + "Edm.NewGuid", + + "SqlServer.NEWID", + "SqlServer.GETDATE", + "SqlServer.GETUTCDATE", + "SqlServer.SYSDATETIME", + "SqlServer.SYSUTCDATETIME", + "SqlServer.SYSDATETIMEOFFSET", + "SqlServer.CURRENT_USER", + "SqlServer.CURRENT_TIMESTAMP", + "SqlServer.HOST_NAME", + "SqlServer.USER_NAME" + }, + StringComparer.OrdinalIgnoreCase); + + public CommandTreeFacts(DbCommandTree commandTree) + { + IsQuery = commandTree is DbQueryCommandTree; + + var visitor = new CommandTreeVisitor(); + + if (commandTree.CommandTreeKind == DbCommandTreeKind.Function) + { + var edmFunction = ((DbFunctionCommandTree)commandTree).EdmFunction; + + var containerMapping = + commandTree.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace).GetItems().Single(); + + var entitySetMappings = + containerMapping.EntitySetMappings.Where( + esm => esm.ModificationFunctionMappings.Any( + mfm => mfm.DeleteFunctionMapping.Function == edmFunction || + mfm.InsertFunctionMapping.Function == edmFunction || + mfm.UpdateFunctionMapping.Function == edmFunction)); + + AffectedEntitySets = + (from esm in entitySetMappings + from etm in esm.EntityTypeMappings + from mappingFragment in etm.Fragments.Where(f => f.StoreEntitySet != null) + select mappingFragment.StoreEntitySet).Cast().ToList().AsReadOnly(); + } + else + { + if (commandTree.CommandTreeKind == DbCommandTreeKind.Query) + { + ((DbQueryCommandTree)commandTree).Query.Accept(visitor); + } + else + { + Debug.Assert(commandTree is DbModificationCommandTree, "Unexpected command tree kind"); + + ((DbModificationCommandTree)commandTree).Target.Expression.Accept(visitor); + } + + AffectedEntitySets = new ReadOnlyCollection(visitor.EntitySets); + UsesNonDeterministicFunctions = + visitor.Functions.Any(f => NonDeterministicFunctions.Contains( + string.Format("{0}.{1}", f.NamespaceName, f.Name))); + } + + MetadataWorkspace = commandTree.MetadataWorkspace; + } + + // testing only + internal CommandTreeFacts(ReadOnlyCollection affectedEntitySets, bool isQuery, bool usesNonDeterministicFunctions, MetadataWorkspace metadataWorkspace = null) + { + AffectedEntitySets = affectedEntitySets; + IsQuery = isQuery; + UsesNonDeterministicFunctions = usesNonDeterministicFunctions; + MetadataWorkspace = metadataWorkspace ?? new MetadataWorkspace(); + } + + public ReadOnlyCollection AffectedEntitySets { get; private set; } + + public bool IsQuery { get; private set; } + + public bool UsesNonDeterministicFunctions { get; private set; } + + public MetadataWorkspace MetadataWorkspace { get; private set; } + + private class CommandTreeVisitor : BasicCommandTreeVisitor + { + public readonly List EntitySets = new List(); + public readonly List Functions = new List(); + + public override void Visit(DbScanExpression expression) + { + EntitySets.Add(expression.Target); + + base.Visit(expression); + } + + public override void Visit(DbFunctionExpression expression) + { + Functions.Add(expression.Function); + + base.Visit(expression); + } + } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/DbCacheEntry.cs b/src/Libraries/SmartStore.Data/Caching/DbCacheEntry.cs index 0734a6652d..3be735d59f 100644 --- a/src/Libraries/SmartStore.Data/Caching/DbCacheEntry.cs +++ b/src/Libraries/SmartStore.Data/Caching/DbCacheEntry.cs @@ -3,39 +3,39 @@ namespace SmartStore.Data.Caching { - [Serializable] - public class DbCacheEntry - { - public string Key { get; set; } - public DateTime CachedOnUtc { get; set; } - public TimeSpan? Duration { get; set; } - public string[] EntitySets { get; set; } - public object Value { get; set; } + [Serializable] + public class DbCacheEntry + { + public string Key { get; set; } + public DateTime CachedOnUtc { get; set; } + public TimeSpan? Duration { get; set; } + public string[] EntitySets { get; set; } + public object Value { get; set; } - public bool HasExpired(DateTime utcNow) - { - if (Duration.HasValue) - { - return utcNow > (CachedOnUtc + Duration.Value); - } + public bool HasExpired(DateTime utcNow) + { + if (Duration.HasValue) + { + return utcNow > (CachedOnUtc + Duration.Value); + } - return false; - } - } + return false; + } + } - [Serializable] - public class CachedRecords - { - public List Records { get; set; } - public int RecordsAffected { get; set; } - public ColumnMetadata[] TableMetadata { get; set; } - } + [Serializable] + public class CachedRecords + { + public List Records { get; set; } + public int RecordsAffected { get; set; } + public ColumnMetadata[] TableMetadata { get; set; } + } - [Serializable] - public class ColumnMetadata - { - public string Name { get; set; } - public string DataTypeName { get; set; } - public Type DataType { get; set; } - } + [Serializable] + public class ColumnMetadata + { + public string Name { get; set; } + public string DataTypeName { get; set; } + public Type DataType { get; set; } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/DbCacheExtensions.cs b/src/Libraries/SmartStore.Data/Caching/DbCacheExtensions.cs index e055db6a8c..b25df7670f 100644 --- a/src/Libraries/SmartStore.Data/Caching/DbCacheExtensions.cs +++ b/src/Libraries/SmartStore.Data/Caching/DbCacheExtensions.cs @@ -7,241 +7,243 @@ using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Infrastructure; +using SmartStore.Utilities.ObjectPools; namespace SmartStore.Data.Caching { - public static class DbCacheExtensions - { - internal static IDbCache GetDbCacheInstance() - { - try - { - return EngineContext.Current.Resolve(); - } - catch - { - return new NullDbCache(); - } - } - - #region Request Cache - - /// - /// Returns the result of from the (HTTP)-REQUEST cache if available, - /// otherwise the query is materialized and cached before being returned. Cache result invalidation - /// is performed automatically by the underlying interceptor. - /// - /// The type of the data in the data source. - /// The query to be materialized. - /// The result of the query. - public static List ToListCached(this IQueryable source) - { - return ToListCached(source, null); - } - - /// - /// Returns the result of from the (HTTP)-REQUEST cache if available, - /// otherwise the query is materialized and cached before being returned. Cache result invalidation - /// is performed automatically by the underlying interceptor. - /// - /// The type of the data in the data source. - /// The query to be materialized. - /// A custom cache key to use instead of the computed one. - /// The result of the query. - /// - /// This overload is slightly faster, because the key does not need to be generated from the passed - /// - public static List ToListCached(this IQueryable source, string customKey) - { - var entry = GetCacheEntry(source, customKey, () => source.ToList()); - if (entry == null) - { - return source.ToList(); - } - - return (List)entry.Value; - } - - /// - /// Returns the first element of from the (HTTP)-REQUEST cache if available, - /// otherwise the query is materialized and cached before being returned. Cache result invalidation - /// is performed automatically by the underlying interceptor. - /// - /// The type of the data in the data source. - /// The query to be materialized. - /// The result of the query. - public static T FirstOrDefaultCached(this IQueryable source) - { - return FirstOrDefaultCached(source, null); - } - - /// - /// Returns the first element of from the (HTTP)-REQUEST cache if available, - /// otherwise the query is materialized and cached before being returned. Cache result invalidation - /// is performed automatically by the underlying interceptor. - /// - /// The type of the data in the data source. - /// The query to be materialized. - /// A custom cache key to use instead of the computed one. - /// The result of the query. - /// - /// This overload is slightly faster, because the key does not need to be generated from the passed - /// - public static T FirstOrDefaultCached(this IQueryable source, string customKey) - { - var entry = GetCacheEntry(source, customKey, () => source.FirstOrDefault()); - if (entry == null) - { - return source.FirstOrDefault(); - } - - return (T)entry.Value; - } - - internal static DbCacheEntry GetCacheEntry(this IQueryable source, string customKey, Func valueFactory) - { - // (Perf) Checking here means higher perf, as keys and entitysets does not need to be evaluated - var cache = GetDbCacheInstance(); - if (!cache.Enabled) - { - return null; - } - - Guard.NotNull(source, nameof(source)); - Guard.NotNull(valueFactory, nameof(valueFactory)); - - var cacheKey = new CacheKey(source, customKey); - if (cacheKey.Key.IsEmpty()) - { - return null; - } - - if (!cache.RequestTryGet(cacheKey.Key, out var entry)) - { - entry = cache.RequestPut(cacheKey.Key, valueFactory(), cacheKey.AffectedEntitySets); - } - //else - //{ - // Debug.WriteLine("FromRequestCache: " + cacheKey.Key); - //} - - return entry; - } - - #endregion - - #region Repository extensions - - /// - /// Gets an entity by id from the database, the local change tracker, or the (HTTP)-REQUEST cache. - /// Cache result invalidation is performed automatically by the underlying interceptor. - /// - /// The id of the entity. This can also be a composite key. - /// The resolved entity - public static T GetByIdCached(this IRepository rs, object id) - where T : BaseEntity - { - return GetByIdCached(rs, id, null); - } - - /// - /// Gets an entity by id from the database, the local change tracker, or the (HTTP)-REQUEST cache. - /// Cache result invalidation is performed automatically by the underlying interceptor. - /// - /// The id of the entity. This can also be a composite key. - /// A custom cache key to use instead of the computed one. - /// The resolved entity - /// - /// This overload is slightly faster, because the key does not need to be generated. - /// - public static T GetByIdCached(this IRepository rs, object id, string customKey) - where T : BaseEntity - { - var entry = rs.Table.GetCacheEntry(customKey, () => rs.GetById(id)); - if (entry == null) - { - return rs.GetById(id); - } - - return (T)entry.Value; - } - - #endregion - - /// - /// Forces query results to be cached. - /// Allows caching results for queries using non-deterministic functions. - /// - /// Query element type. - /// Query whose results will be cached. Must not be null. - public static IQueryable Cached(this IQueryable source) - where T : class - { - Guard.NotNull(source, nameof(source)); - - // TODO: implement Uncached() - - var objectQuery = DbCacheUtil.GetObjectQuery(source) ?? source as ObjectQuery; - - if (objectQuery != null) - { - SingletonQueries.Current.AddCachedQuery( - objectQuery.Context.MetadataWorkspace, - objectQuery.ToTraceString()); - } - - return source; - } - - private class CacheKey - { - public CacheKey(IQueryable source, string customKey = null) - { - Guard.NotNull(source, nameof(source)); - - GenerateKeyAndAffectedEntitySets(source, customKey); - } - - private void GenerateKeyAndAffectedEntitySets(IQueryable source, string customKey = null) - { - var objectQuery = DbCacheUtil.GetObjectQuery(source) ?? source as ObjectQuery; - - var key = customKey.NullEmpty(); - if (objectQuery != null && string.IsNullOrEmpty(key)) - { - var commandInfo = objectQuery.GetCommandInfo(); - - var sb = new StringBuilder(); - sb.AppendLine(commandInfo.Sql); - - foreach (DbParameter parameter in commandInfo.Parameters) - { - sb.Append(parameter.ParameterName); - sb.Append(";"); - sb.Append(parameter.Value); - sb.AppendLine(";"); - } - - key = sb.ToString(); - } - - Key = key; - AffectedEntitySets = objectQuery != null - ? objectQuery.GetAffectedEntitySets() - : new string[0]; - } - - public string Key - { - get; - private set; - } - - public string[] AffectedEntitySets - { - get; - private set; - } - } - } + public static class DbCacheExtensions + { + internal static IDbCache GetDbCacheInstance() + { + try + { + return EngineContext.Current.Resolve(); + } + catch + { + return new NullDbCache(); + } + } + + #region Request Cache + + /// + /// Returns the result of from the (HTTP)-REQUEST cache if available, + /// otherwise the query is materialized and cached before being returned. Cache result invalidation + /// is performed automatically by the underlying interceptor. + /// + /// The type of the data in the data source. + /// The query to be materialized. + /// The result of the query. + public static List ToListCached(this IQueryable source) + { + return ToListCached(source, null); + } + + /// + /// Returns the result of from the (HTTP)-REQUEST cache if available, + /// otherwise the query is materialized and cached before being returned. Cache result invalidation + /// is performed automatically by the underlying interceptor. + /// + /// The type of the data in the data source. + /// The query to be materialized. + /// A custom cache key to use instead of the computed one. + /// The result of the query. + /// + /// This overload is slightly faster, because the key does not need to be generated from the passed + /// + public static List ToListCached(this IQueryable source, string customKey) + { + var entry = GetCacheEntry(source, customKey, () => source.ToList()); + if (entry == null) + { + return source.ToList(); + } + + return (List)entry.Value; + } + + /// + /// Returns the first element of from the (HTTP)-REQUEST cache if available, + /// otherwise the query is materialized and cached before being returned. Cache result invalidation + /// is performed automatically by the underlying interceptor. + /// + /// The type of the data in the data source. + /// The query to be materialized. + /// The result of the query. + public static T FirstOrDefaultCached(this IQueryable source) + { + return FirstOrDefaultCached(source, null); + } + + /// + /// Returns the first element of from the (HTTP)-REQUEST cache if available, + /// otherwise the query is materialized and cached before being returned. Cache result invalidation + /// is performed automatically by the underlying interceptor. + /// + /// The type of the data in the data source. + /// The query to be materialized. + /// A custom cache key to use instead of the computed one. + /// The result of the query. + /// + /// This overload is slightly faster, because the key does not need to be generated from the passed + /// + public static T FirstOrDefaultCached(this IQueryable source, string customKey) + { + var entry = GetCacheEntry(source, customKey, () => source.FirstOrDefault()); + if (entry == null) + { + return source.FirstOrDefault(); + } + + return (T)entry.Value; + } + + internal static DbCacheEntry GetCacheEntry(this IQueryable source, string customKey, Func valueFactory) + { + // (Perf) Checking here means higher perf, as keys and entitysets does not need to be evaluated + var cache = GetDbCacheInstance(); + if (!cache.Enabled) + { + return null; + } + + Guard.NotNull(source, nameof(source)); + Guard.NotNull(valueFactory, nameof(valueFactory)); + + var cacheKey = new CacheKey(source, customKey); + if (cacheKey.Key.IsEmpty()) + { + return null; + } + + if (!cache.RequestTryGet(cacheKey.Key, out var entry)) + { + entry = cache.RequestPut(cacheKey.Key, valueFactory(), cacheKey.AffectedEntitySets); + } + //else + //{ + // Debug.WriteLine("FromRequestCache: " + cacheKey.Key); + //} + + return entry; + } + + #endregion + + #region Repository extensions + + /// + /// Gets an entity by id from the database, the local change tracker, or the (HTTP)-REQUEST cache. + /// Cache result invalidation is performed automatically by the underlying interceptor. + /// + /// The id of the entity. This can also be a composite key. + /// The resolved entity + public static T GetByIdCached(this IRepository rs, object id) + where T : BaseEntity + { + return GetByIdCached(rs, id, null); + } + + /// + /// Gets an entity by id from the database, the local change tracker, or the (HTTP)-REQUEST cache. + /// Cache result invalidation is performed automatically by the underlying interceptor. + /// + /// The id of the entity. This can also be a composite key. + /// A custom cache key to use instead of the computed one. + /// The resolved entity + /// + /// This overload is slightly faster, because the key does not need to be generated. + /// + public static T GetByIdCached(this IRepository rs, object id, string customKey) + where T : BaseEntity + { + var entry = rs.Table.GetCacheEntry(customKey, () => rs.GetById(id)); + if (entry == null) + { + return rs.GetById(id); + } + + return (T)entry.Value; + } + + #endregion + + /// + /// Forces query results to be cached. + /// Allows caching results for queries using non-deterministic functions. + /// + /// Query element type. + /// Query whose results will be cached. Must not be null. + public static IQueryable Cached(this IQueryable source) + where T : class + { + Guard.NotNull(source, nameof(source)); + + // TODO: implement Uncached() + + var objectQuery = DbCacheUtil.GetObjectQuery(source) ?? source as ObjectQuery; + + if (objectQuery != null) + { + SingletonQueries.Current.AddCachedQuery( + objectQuery.Context.MetadataWorkspace, + objectQuery.ToTraceString()); + } + + return source; + } + + private class CacheKey + { + public CacheKey(IQueryable source, string customKey = null) + { + Guard.NotNull(source, nameof(source)); + + GenerateKeyAndAffectedEntitySets(source, customKey); + } + + private void GenerateKeyAndAffectedEntitySets(IQueryable source, string customKey = null) + { + var objectQuery = DbCacheUtil.GetObjectQuery(source) ?? source as ObjectQuery; + + var key = customKey.NullEmpty(); + if (objectQuery != null && string.IsNullOrEmpty(key)) + { + var commandInfo = objectQuery.GetCommandInfo(); + + var psb = PooledStringBuilder.Rent(); + var sb = (StringBuilder)psb; + sb.AppendLine(commandInfo.Sql); + + foreach (DbParameter parameter in commandInfo.Parameters) + { + sb.Append(parameter.ParameterName); + sb.Append(";"); + sb.Append(parameter.Value); + sb.AppendLine(";"); + } + + key = psb.ToStringAndReturn(); + } + + Key = key; + AffectedEntitySets = objectQuery != null + ? objectQuery.GetAffectedEntitySets() + : new string[0]; + } + + public string Key + { + get; + private set; + } + + public string[] AffectedEntitySets + { + get; + private set; + } + } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/DbCacheUtil.cs b/src/Libraries/SmartStore.Data/Caching/DbCacheUtil.cs index 86cab90ae7..aa426e1762 100644 --- a/src/Libraries/SmartStore.Data/Caching/DbCacheUtil.cs +++ b/src/Libraries/SmartStore.Data/Caching/DbCacheUtil.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Data.Common; -using System.Data.Entity; using System.Data.Entity.Core.EntityClient; using System.Data.Entity.Core.Metadata.Edm; using System.Data.Entity.Core.Objects; @@ -12,86 +11,86 @@ namespace SmartStore.Data.Caching { - internal static class DbCacheUtil - { - public static ObjectQuery GetObjectQuery(this IQueryable source) - { - var dbQuery = source as DbQuery; - - if (dbQuery != null) - { - var internalQueryProperty = FastProperty.GetProperty(source.GetType(), "InternalQuery"); - var internalQuery = internalQueryProperty.GetValue(source); - - var objectQueryProperty = FastProperty.GetProperty(internalQuery.GetType(), "ObjectQuery"); - var objectQuery = objectQueryProperty.GetValue(internalQuery); - - return objectQuery as ObjectQuery; - } - - return null; - } - - public static CommandInfo GetCommandInfo(this ObjectQuery objectQuery) - { - var stateField = objectQuery.GetType().BaseType.GetField("_state", BindingFlags.NonPublic | BindingFlags.Instance); - var state = stateField.GetValue(objectQuery); - var getExecutionPlanMethod = state.GetType().GetMethod("GetExecutionPlan", BindingFlags.NonPublic | BindingFlags.Instance); - var getExecutionPlan = getExecutionPlanMethod.Invoke(state, new object[] { null }); - var prepareEntityCommandMethod = getExecutionPlan.GetType().GetMethod("PrepareEntityCommand", BindingFlags.NonPublic | BindingFlags.Instance); - - var sql = ""; - - using (var entityCommand = (EntityCommand)prepareEntityCommandMethod.Invoke(getExecutionPlan, new object[] { objectQuery.Context, objectQuery.Parameters })) - { - var getCommandDefinitionMethod = entityCommand.GetType().GetMethod("GetCommandDefinition", BindingFlags.NonPublic | BindingFlags.Instance); - var getCommandDefinition = getCommandDefinitionMethod.Invoke(entityCommand, new object[0]); - - var prepareEntityCommandBeforeExecutionMethod = getCommandDefinition.GetType().GetMethod("PrepareEntityCommandBeforeExecution", BindingFlags.NonPublic | BindingFlags.Instance); - var prepareEntityCommandBeforeExecution = (DbCommand)prepareEntityCommandBeforeExecutionMethod.Invoke(getCommandDefinition, new object[] { entityCommand }); - - sql = prepareEntityCommandBeforeExecution.CommandText; - var parameters = prepareEntityCommandBeforeExecution.Parameters; - - return new CommandInfo(sql, parameters); - } - } - - public static string[] GetAffectedEntitySets(this ObjectQuery objectQuery) - { - var result = new HashSet(); - - var entityType = objectQuery.GetResultType().EdmType as EntityType; - - if (entityType != null) - { - result.Add(entityType.Name); - foreach (var nav in entityType.NavigationProperties) - { - var edmType = nav.TypeUsage.EdmType; - if (edmType is CollectionType) - { - result.Add(((CollectionType)edmType).TypeUsage.EdmType.Name); - } - else - { - result.Add(edmType.Name); - } - } - } - - return result.ToArray(); - } - - internal class CommandInfo : Tuple - { - public CommandInfo(string sql, DbParameterCollection parameters) - : base(sql, parameters) - { - } - - public string Sql { get { return base.Item1; } } - public DbParameterCollection Parameters { get { return base.Item2; } } - } - } + internal static class DbCacheUtil + { + public static ObjectQuery GetObjectQuery(this IQueryable source) + { + var dbQuery = source as DbQuery; + + if (dbQuery != null) + { + var internalQueryProperty = FastProperty.GetProperty(source.GetType(), "InternalQuery"); + var internalQuery = internalQueryProperty.GetValue(source); + + var objectQueryProperty = FastProperty.GetProperty(internalQuery.GetType(), "ObjectQuery"); + var objectQuery = objectQueryProperty.GetValue(internalQuery); + + return objectQuery as ObjectQuery; + } + + return null; + } + + public static CommandInfo GetCommandInfo(this ObjectQuery objectQuery) + { + var stateField = objectQuery.GetType().BaseType.GetField("_state", BindingFlags.NonPublic | BindingFlags.Instance); + var state = stateField.GetValue(objectQuery); + var getExecutionPlanMethod = state.GetType().GetMethod("GetExecutionPlan", BindingFlags.NonPublic | BindingFlags.Instance); + var getExecutionPlan = getExecutionPlanMethod.Invoke(state, new object[] { null }); + var prepareEntityCommandMethod = getExecutionPlan.GetType().GetMethod("PrepareEntityCommand", BindingFlags.NonPublic | BindingFlags.Instance); + + var sql = ""; + + using (var entityCommand = (EntityCommand)prepareEntityCommandMethod.Invoke(getExecutionPlan, new object[] { objectQuery.Context, objectQuery.Parameters })) + { + var getCommandDefinitionMethod = entityCommand.GetType().GetMethod("GetCommandDefinition", BindingFlags.NonPublic | BindingFlags.Instance); + var getCommandDefinition = getCommandDefinitionMethod.Invoke(entityCommand, new object[0]); + + var prepareEntityCommandBeforeExecutionMethod = getCommandDefinition.GetType().GetMethod("PrepareEntityCommandBeforeExecution", BindingFlags.NonPublic | BindingFlags.Instance); + var prepareEntityCommandBeforeExecution = (DbCommand)prepareEntityCommandBeforeExecutionMethod.Invoke(getCommandDefinition, new object[] { entityCommand }); + + sql = prepareEntityCommandBeforeExecution.CommandText; + var parameters = prepareEntityCommandBeforeExecution.Parameters; + + return new CommandInfo(sql, parameters); + } + } + + public static string[] GetAffectedEntitySets(this ObjectQuery objectQuery) + { + var result = new HashSet(); + + var entityType = objectQuery.GetResultType().EdmType as EntityType; + + if (entityType != null) + { + result.Add(entityType.Name); + foreach (var nav in entityType.NavigationProperties) + { + var edmType = nav.TypeUsage.EdmType; + if (edmType is CollectionType) + { + result.Add(((CollectionType)edmType).TypeUsage.EdmType.Name); + } + else + { + result.Add(edmType.Name); + } + } + } + + return result.ToArray(); + } + + internal class CommandInfo : Tuple + { + public CommandInfo(string sql, DbParameterCollection parameters) + : base(sql, parameters) + { + } + + public string Sql => base.Item1; + public DbParameterCollection Parameters => base.Item2; + } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/DbCachingPolicy.cs b/src/Libraries/SmartStore.Data/Caching/DbCachingPolicy.cs index 9dae29bd05..7223f8d33a 100644 --- a/src/Libraries/SmartStore.Data/Caching/DbCachingPolicy.cs +++ b/src/Libraries/SmartStore.Data/Caching/DbCachingPolicy.cs @@ -4,7 +4,6 @@ using System.Data.Entity.Core.Metadata.Edm; using System.Linq; using SmartStore.Core.Domain.Catalog; -using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Directory; using SmartStore.Core.Domain.Discounts; using SmartStore.Core.Domain.Localization; @@ -21,7 +20,7 @@ namespace SmartStore.Data.Caching { - /* TODO: (mc) + /* TODO: (mc) * ======================== * 1. Let developers register custom caching policies for single entities (from plugins) * 2. Caching policies should contain expiration info and cacheable rows count @@ -29,81 +28,80 @@ namespace SmartStore.Data.Caching * 4. Backend: Let users purge the cache */ - public partial class DbCachingPolicy - { - private static readonly HashSet _cacheableSets = new HashSet - { - typeof(AclRecord).Name, - typeof(ActivityLogType).Name, - typeof(CategoryTemplate).Name, - typeof(CheckoutAttribute).Name, - typeof(CheckoutAttributeValue).Name, - typeof(Country).Name, - typeof(Currency).Name, - typeof(CustomerRole).Name, + public partial class DbCachingPolicy + { + private static readonly HashSet _cacheableSets = new HashSet + { + typeof(AclRecord).Name, + typeof(ActivityLogType).Name, + typeof(CategoryTemplate).Name, + typeof(CheckoutAttribute).Name, + typeof(CheckoutAttributeValue).Name, + typeof(Country).Name, + typeof(Currency).Name, + //typeof(CustomerRole).Name, typeof(DeliveryTime).Name, - typeof(Discount).Name, - typeof(DiscountRequirement).Name, - typeof(EmailAccount).Name, - typeof(Language).Name, - typeof(ManufacturerTemplate).Name, - typeof(MeasureDimension).Name, - typeof(MeasureWeight).Name, - typeof(MessageTemplate).Name, - typeof(PaymentMethod).Name, - typeof(PermissionRecord).Name, - typeof(ProductTemplate).Name, - typeof(QuantityUnit).Name, - typeof(ShippingMethod).Name, - typeof(StateProvince).Name, - typeof(Store).Name, - typeof(TaxCategory).Name, - typeof(ThemeVariable).Name, - typeof(Topic).Name - }; + typeof(Discount).Name, + typeof(EmailAccount).Name, + typeof(Language).Name, + typeof(ManufacturerTemplate).Name, + typeof(MeasureDimension).Name, + typeof(MeasureWeight).Name, + typeof(MessageTemplate).Name, + typeof(PaymentMethod).Name, + typeof(PermissionRecord).Name, + typeof(ProductTemplate).Name, + typeof(QuantityUnit).Name, + typeof(ShippingMethod).Name, + typeof(StateProvince).Name, + typeof(Store).Name, + typeof(TaxCategory).Name, + typeof(ThemeVariable).Name, + typeof(Topic).Name + }; - /// - /// Determines whether the specified command definition can be cached. - /// - /// Entity sets affected by the command. - /// SQL statement for the command. - /// Command parameters. - /// - /// true when the specified command definition can be cached; otherwise, false. - /// - protected internal virtual bool CanBeCached( - ReadOnlyCollection affectedEntitySets, - string sql, - IEnumerable> parameters) - { - var entitySets = affectedEntitySets.Select(x => x.Name); - var result = entitySets.All(x => _cacheableSets.Contains(x)); - return result; - } + /// + /// Determines whether the specified command definition can be cached. + /// + /// Entity sets affected by the command. + /// SQL statement for the command. + /// Command parameters. + /// + /// true when the specified command definition can be cached; otherwise, false. + /// + protected internal virtual bool CanBeCached( + ReadOnlyCollection affectedEntitySets, + string sql, + IEnumerable> parameters) + { + var entitySets = affectedEntitySets.Select(x => x.Name); + var result = entitySets.All(x => _cacheableSets.Contains(x)); + return result; + } - /// - /// Gets the minimum and maximum number cacheable rows for a given command definition. - /// - /// Entity sets affected by the command. - /// The minimum number of cacheable rows. - /// The maximum number of cacheable rows. - protected internal virtual void GetCacheableRows( - ReadOnlyCollection affectedEntitySets, - out int minCacheableRows, - out int maxCacheableRows) - { - minCacheableRows = 0; - maxCacheableRows = 5000; // int.MaxValue; - } + /// + /// Gets the minimum and maximum number cacheable rows for a given command definition. + /// + /// Entity sets affected by the command. + /// The minimum number of cacheable rows. + /// The maximum number of cacheable rows. + protected internal virtual void GetCacheableRows( + ReadOnlyCollection affectedEntitySets, + out int minCacheableRows, + out int maxCacheableRows) + { + minCacheableRows = 0; + maxCacheableRows = 5000; // int.MaxValue; + } - /// - /// Gets the expiration timeout for a given command definition. - /// - /// Entity sets affected by the command. - /// The absolute TTL - protected internal virtual TimeSpan? GetExpirationTimeout(ReadOnlyCollection affectedEntitySets) - { - return TimeSpan.FromDays(1); - } - } + /// + /// Gets the expiration timeout for a given command definition. + /// + /// Entity sets affected by the command. + /// The absolute TTL + protected internal virtual TimeSpan? GetExpirationTimeout(ReadOnlyCollection affectedEntitySets) + { + return TimeSpan.FromDays(1); + } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/EfDbCache.cs b/src/Libraries/SmartStore.Data/Caching/EfDbCache.cs index 67d4561746..adfb712a9a 100644 --- a/src/Libraries/SmartStore.Data/Caching/EfDbCache.cs +++ b/src/Libraries/SmartStore.Data/Caching/EfDbCache.cs @@ -12,365 +12,362 @@ namespace SmartStore.Data.Caching { - public partial class EfDbCache : IDbCache - { - // Entity sets to be never cached or invalidated - private static readonly HashSet _toxicSets = new HashSet - { - typeof(ScheduleTask).Name, - typeof(ScheduleTaskHistory).Name, - typeof(Log).Name, - typeof(ActivityLog).Name, - typeof(QueuedEmail).Name - }; - - private const string KEYPREFIX = "efcache:"; - private readonly object _lock = new object(); - - private bool _enabled; - - private readonly ICacheManager _cache; - private readonly Work _requestCache; - - public EfDbCache(ICacheManager innerCache, Work requestCache) - { - _cache = innerCache; - _requestCache = requestCache; - - _enabled = true; - } - - public bool Enabled - { - get - { - return _enabled; - } - set - { - if (_enabled == value) - return; - - lock (_lock) - { - if (_enabled == false && value == true) - { - // When cache was disabled previously and gets enabled, - // we should clear the cache, because no invalidation has been performed - // during disabled state. We would deal with stale data otherwise. - Clear(); - } - _enabled = value; - } - } - } - - private bool IsToxic(IEnumerable entitySets) - { - return entitySets.Any(x => _toxicSets.Contains(x)); - } - - #region Request Scoped - - public virtual bool RequestTryGet(string key, out DbCacheEntry value) - { - value = null; - - if (!Enabled) - { - return false; - } - - key = HashKey(key); - - value = _requestCache.Value.Get(key); - return value != null; - } - - public virtual DbCacheEntry RequestPut(string key, object value, string[] dependentEntitySets) - { - if (!Enabled || IsToxic(dependentEntitySets)) - { - return null; - } - - key = HashKey(key); - - var entry = new DbCacheEntry - { - Key = key, - Value = value, - EntitySets = dependentEntitySets, - CachedOnUtc = DateTime.UtcNow - }; - - _requestCache.Value.Put(key, entry); - - foreach (var entitySet in entry.EntitySets) - { - var lookup = RequestGetLookupSet(entitySet); - lookup.Add(key); - } - - return entry; - } - - public virtual void RequestInvalidateSets(IEnumerable entitySets) - { - Guard.NotNull(entitySets, nameof(entitySets)); - - if (!Enabled || !entitySets.Any() || IsToxic(entitySets)) - return; - - var sets = entitySets.Distinct().ToArray(); - - var itemsToInvalidate = new HashSet(); - - foreach (var entitySet in sets) - { - var lookup = RequestGetLookupSet(entitySet, false); - if (lookup != null) - { - itemsToInvalidate.UnionWith(lookup); - } - } - - foreach (var key in itemsToInvalidate) - { - RequestInvalidateItem(key); - } - } - - public virtual void RequestInvalidateItem(string key) - { - Guard.NotEmpty(key, nameof(key)); - - var cache = _requestCache.Value; - - var entry = cache.Get(key); - if (entry != null) - { - // remove item itself from cache - cache.Remove(key); - - // remove this key in all lookups - foreach (var set in entry.EntitySets) - { - var lookup = RequestGetLookupSet(set, false); - if (lookup != null) - { - lookup.Remove(entry.Key); - } - } - } - } - - private HashSet RequestGetLookupSet(string entitySet, bool create = true) - { - var key = GetLookupKeyFor(entitySet); - - if (create) - { - return _requestCache.Value.Get(key, () => new HashSet()); - } - else - { - return _requestCache.Value.Get>(key); - } - } - - #endregion - - public virtual bool TryGet(string key, out object value) - { - value = null; - - if (!Enabled) - { - return false; - } - - key = HashKey(key); - var now = DateTime.UtcNow; - - var entry = _cache.Get(key, independent: true); - - if (entry != null) - { - if (entry.HasExpired(now)) - { - using (KeyedLock.Lock(key)) - { - InvalidateItemUnlocked(entry); - } - } - else - { - value = entry.Value; - return true; - } - } - - return false; - } - - public virtual DbCacheEntry Put(string key, object value, IEnumerable dependentEntitySets, TimeSpan? duration) - { - if (!Enabled || IsToxic(dependentEntitySets)) - { - return null; - } - - key = HashKey(key); - - using (KeyedLock.Lock(key)) - { - var entitySets = dependentEntitySets.Distinct().ToArray(); - var entry = new DbCacheEntry - { - Key = key, - Value = value, - EntitySets = entitySets, - CachedOnUtc = DateTime.UtcNow, - Duration = duration - }; - - _cache.Put(key, entry); - - foreach (var entitySet in entitySets) - { - var lookup = GetLookupSet(entitySet); - lookup.Add(key); - } - - return entry; - } - } - - public void Clear() - { - _cache.RemoveByPattern(KEYPREFIX + "*"); - _requestCache.Value.RemoveByPattern(KEYPREFIX + "*"); - } - - public virtual void InvalidateSets(IEnumerable entitySets) - { - Guard.NotNull(entitySets, nameof(entitySets)); - - if (!Enabled || !entitySets.Any() || IsToxic(entitySets)) - return; - - var sets = entitySets.Distinct().ToArray(); - - lock (_lock) - { - var itemsToInvalidate = new HashSet(); - - foreach (var entitySet in sets) - { - var lookup = GetLookupSet(entitySet, false); - if (lookup != null) - { - itemsToInvalidate.UnionWith(lookup); - } - } - - foreach (var key in itemsToInvalidate) - { - InvalidateItemUnlocked(key); - } - } - } - - public virtual void InvalidateItem(string key) - { - if (!Enabled) - { - return; - } - - Guard.NotEmpty(key, nameof(key)); - - using (KeyedLock.Lock(key)) - { - InvalidateItemUnlocked(key); - } - } - - private void InvalidateItemUnlocked(string key) - { - if (!Enabled) - { - return; - } - - if (_cache.Contains(key)) - { - var entry = _cache.Get(key, true); - if (entry != null) - { - InvalidateItemUnlocked(entry); - } - } - } - - protected void InvalidateItemUnlocked(DbCacheEntry entry) - { - // remove item itself from cache - _cache.Remove(entry.Key); - - // remove this key in all lookups - foreach (var set in entry.EntitySets) - { - var lookup = GetLookupSet(set, false); - if (lookup != null) - { - lookup.Remove(entry.Key); - } - } - } - - private ISet GetLookupSet(string entitySet, bool create = true) - { - var key = GetLookupKeyFor(entitySet); - - if (create) - { - return _cache.GetHashSet(key); - } - else - { - if (_cache.Contains(key)) - { - return _cache.GetHashSet(key); - } - } - - return null; - } - - private string GetLookupKeyFor(string entitySet) - { - return KEYPREFIX + "lookup:" + entitySet; - } - - private static string HashKey(string key) - { - // Looking up large Keys can be expensive (comparing Large Strings), so if keys are large, hash them, otherwise if keys are short just use as-is - if (key.Length <= 128) - return KEYPREFIX + "data:" + key; - - using (var sha = new SHA1CryptoServiceProvider()) - { - try - { - return KEYPREFIX + "data:" + Convert.ToBase64String(sha.ComputeHash(Encoding.UTF8.GetBytes(key))); - } - catch - { - return KEYPREFIX + "data:" + key; - } - } - } - } -} + public partial class EfDbCache : IDbCache + { + // Entity sets to be never cached or invalidated + private static readonly HashSet _toxicSets = new HashSet + { + typeof(ScheduleTask).Name, + typeof(ScheduleTaskHistory).Name, + typeof(Log).Name, + typeof(ActivityLog).Name, + typeof(QueuedEmail).Name + }; + + private const string KEYPREFIX = "efcache:"; + private readonly object _lock = new object(); + + private bool _enabled; + + private readonly ICacheManager _cache; + private readonly Work _requestCache; + + public EfDbCache(ICacheManager innerCache, Work requestCache) + { + _cache = innerCache; + _requestCache = requestCache; + + _enabled = true; + } + + public bool Enabled + { + get => _enabled; + set + { + if (_enabled == value) + return; + + lock (_lock) + { + if (_enabled == false && value == true) + { + // When cache was disabled previously and gets enabled, + // we should clear the cache, because no invalidation has been performed + // during disabled state. We would deal with stale data otherwise. + Clear(); + } + _enabled = value; + } + } + } + + private bool IsToxic(IEnumerable entitySets) + { + return entitySets.Any(x => _toxicSets.Contains(x)); + } + + #region Request Scoped + + public virtual bool RequestTryGet(string key, out DbCacheEntry value) + { + value = null; + + if (!Enabled) + { + return false; + } + + key = HashKey(key); + + value = _requestCache.Value.Get(key); + return value != null; + } + + public virtual DbCacheEntry RequestPut(string key, object value, string[] dependentEntitySets) + { + if (!Enabled || IsToxic(dependentEntitySets)) + { + return null; + } + + key = HashKey(key); + + var entry = new DbCacheEntry + { + Key = key, + Value = value, + EntitySets = dependentEntitySets, + CachedOnUtc = DateTime.UtcNow + }; + + _requestCache.Value.Put(key, entry); + + foreach (var entitySet in entry.EntitySets) + { + var lookup = RequestGetLookupSet(entitySet); + lookup.Add(key); + } + + return entry; + } + + public virtual void RequestInvalidateSets(IEnumerable entitySets) + { + Guard.NotNull(entitySets, nameof(entitySets)); + + if (!Enabled || !entitySets.Any() || IsToxic(entitySets)) + return; + + var sets = entitySets.Distinct().ToArray(); + + var itemsToInvalidate = new HashSet(); + + foreach (var entitySet in sets) + { + var lookup = RequestGetLookupSet(entitySet, false); + if (lookup != null) + { + itemsToInvalidate.UnionWith(lookup); + } + } + + foreach (var key in itemsToInvalidate) + { + RequestInvalidateItem(key); + } + } + + public virtual void RequestInvalidateItem(string key) + { + Guard.NotEmpty(key, nameof(key)); + + var cache = _requestCache.Value; + + var entry = cache.Get(key); + if (entry != null) + { + // remove item itself from cache + cache.Remove(key); + + // remove this key in all lookups + foreach (var set in entry.EntitySets) + { + var lookup = RequestGetLookupSet(set, false); + if (lookup != null) + { + lookup.Remove(entry.Key); + } + } + } + } + + private HashSet RequestGetLookupSet(string entitySet, bool create = true) + { + var key = GetLookupKeyFor(entitySet); + + if (create) + { + return _requestCache.Value.Get(key, () => new HashSet()); + } + else + { + return _requestCache.Value.Get>(key); + } + } + + #endregion + + public virtual bool TryGet(string key, out object value) + { + value = null; + + if (!Enabled) + { + return false; + } + + key = HashKey(key); + var now = DateTime.UtcNow; + + var entry = _cache.Get(key, independent: true); + + if (entry != null) + { + if (entry.HasExpired(now)) + { + using (KeyedLock.Lock(key)) + { + InvalidateItemUnlocked(entry); + } + } + else + { + value = entry.Value; + return true; + } + } + + return false; + } + + public virtual DbCacheEntry Put(string key, object value, IEnumerable dependentEntitySets, TimeSpan? duration) + { + if (!Enabled || IsToxic(dependentEntitySets)) + { + return null; + } + + key = HashKey(key); + + using (KeyedLock.Lock(key)) + { + var entitySets = dependentEntitySets.Distinct().ToArray(); + var entry = new DbCacheEntry + { + Key = key, + Value = value, + EntitySets = entitySets, + CachedOnUtc = DateTime.UtcNow, + Duration = duration + }; + + _cache.Put(key, entry); + + foreach (var entitySet in entitySets) + { + var lookup = GetLookupSet(entitySet); + lookup.Add(key); + } + + return entry; + } + } + + public void Clear() + { + _cache.RemoveByPattern(KEYPREFIX + "*"); + _requestCache.Value.RemoveByPattern(KEYPREFIX + "*"); + } + + public virtual void InvalidateSets(IEnumerable entitySets) + { + Guard.NotNull(entitySets, nameof(entitySets)); + + if (!Enabled || !entitySets.Any() || IsToxic(entitySets)) + return; + + var sets = entitySets.Distinct().ToArray(); + + lock (_lock) + { + var itemsToInvalidate = new HashSet(); + + foreach (var entitySet in sets) + { + var lookup = GetLookupSet(entitySet, false); + if (lookup != null) + { + itemsToInvalidate.UnionWith(lookup); + } + } + + foreach (var key in itemsToInvalidate) + { + InvalidateItemUnlocked(key); + } + } + } + + public virtual void InvalidateItem(string key) + { + if (!Enabled) + { + return; + } + + Guard.NotEmpty(key, nameof(key)); + + using (KeyedLock.Lock(key)) + { + InvalidateItemUnlocked(key); + } + } + + private void InvalidateItemUnlocked(string key) + { + if (!Enabled) + { + return; + } + + if (_cache.Contains(key)) + { + var entry = _cache.Get(key, true); + if (entry != null) + { + InvalidateItemUnlocked(entry); + } + } + } + + protected void InvalidateItemUnlocked(DbCacheEntry entry) + { + // remove item itself from cache + _cache.Remove(entry.Key); + + // remove this key in all lookups + foreach (var set in entry.EntitySets) + { + var lookup = GetLookupSet(set, false); + if (lookup != null) + { + lookup.Remove(entry.Key); + } + } + } + + private ISet GetLookupSet(string entitySet, bool create = true) + { + var key = GetLookupKeyFor(entitySet); + + if (create) + { + return _cache.GetHashSet(key); + } + else + { + if (_cache.Contains(key)) + { + return _cache.GetHashSet(key); + } + } + + return null; + } + + private string GetLookupKeyFor(string entitySet) + { + return KEYPREFIX + "lookup:" + entitySet; + } + + private static string HashKey(string key) + { + // Looking up large Keys can be expensive (comparing Large Strings), so if keys are large, hash them, otherwise if keys are short just use as-is + if (key.Length <= 128) + return KEYPREFIX + "data:" + key; + + using (var sha = new SHA1CryptoServiceProvider()) + { + try + { + return KEYPREFIX + "data:" + Convert.ToBase64String(sha.ComputeHash(Encoding.UTF8.GetBytes(key))); + } + catch + { + return KEYPREFIX + "data:" + key; + } + } + } + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Caching/EfDbModelStore.cs b/src/Libraries/SmartStore.Data/Caching/EfDbModelStore.cs index 393becfbf3..83fe2f2d99 100644 --- a/src/Libraries/SmartStore.Data/Caching/EfDbModelStore.cs +++ b/src/Libraries/SmartStore.Data/Caching/EfDbModelStore.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Data.Entity.Infrastructure; using System.Diagnostics; using System.IO; @@ -9,52 +8,52 @@ namespace SmartStore.Data.Caching { - public sealed class EfDbModelStore : DefaultDbModelStore - { - public EfDbModelStore(string location) - : base(location) - { - } - - public override DbCompiledModel TryLoad(Type contextType) - { - string path = GetFilePath(contextType); - - if (File.Exists(path)) - { - var cachedModelCreatedOn = File.GetLastWriteTimeUtc(path); - var localAssemblyFile = FindLocalAssemblyFile(contextType.Assembly); - if (localAssemblyFile == null || !localAssemblyFile.Exists || localAssemblyFile.LastWriteTimeUtc > cachedModelCreatedOn) - { - File.Delete(path); - Debug.WriteLine("Cached db model obsolete. Re-creating cached db model edmx."); - } - } - else - { - Debug.WriteLine("No cached db model found. Creating cached db model edmx."); - } - - return base.TryLoad(contextType); - } - - private FileInfo FindLocalAssemblyFile(Assembly assembly) - { - // ASP.NET loads assemblies from its dynamic temp folder, - // where DLLs get redeployed on every app startup, which - // makes it impossible for us to check for updated DLLs. - // Therefore we'll find passed assembly in the app folder for datetime checking. - var file = new FileInfo(assembly.Location); - - if (file.Name.IsCaseInsensitiveEqual("SmartStore.Data.dll")) - { - return new FileInfo(Path.Combine(CommonHelper.MapPath("~/bin"), file.Name)); - } - - var pluginSystemName = Path.GetFileNameWithoutExtension(file.Name); - var pluginDescriptor = PluginFinder.Current.GetPluginDescriptorBySystemName(pluginSystemName, false); - - return pluginDescriptor?.Assembly?.OriginalFile; - } - } + public sealed class EfDbModelStore : DefaultDbModelStore + { + public EfDbModelStore(string location) + : base(location) + { + } + + public override DbCompiledModel TryLoad(Type contextType) + { + string path = GetFilePath(contextType); + + if (File.Exists(path)) + { + var cachedModelCreatedOn = File.GetLastWriteTimeUtc(path); + var localAssemblyFile = FindLocalAssemblyFile(contextType.Assembly); + if (localAssemblyFile == null || !localAssemblyFile.Exists || localAssemblyFile.LastWriteTimeUtc > cachedModelCreatedOn) + { + File.Delete(path); + Debug.WriteLine("Cached db model obsolete. Re-creating cached db model edmx."); + } + } + else + { + Debug.WriteLine("No cached db model found. Creating cached db model edmx."); + } + + return base.TryLoad(contextType); + } + + private FileInfo FindLocalAssemblyFile(Assembly assembly) + { + // ASP.NET loads assemblies from its dynamic temp folder, + // where DLLs get redeployed on every app startup, which + // makes it impossible for us to check for updated DLLs. + // Therefore we'll find passed assembly in the app folder for datetime checking. + var file = new FileInfo(assembly.Location); + + if (file.Name.IsCaseInsensitiveEqual("SmartStore.Data.dll")) + { + return new FileInfo(Path.Combine(CommonHelper.MapPath("~/bin"), file.Name)); + } + + var pluginSystemName = Path.GetFileNameWithoutExtension(file.Name); + var pluginDescriptor = PluginFinder.Current.GetPluginDescriptorBySystemName(pluginSystemName, false); + + return pluginDescriptor?.Assembly?.OriginalFile; + } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/EfMappingViewCache.cs b/src/Libraries/SmartStore.Data/Caching/EfMappingViewCache.cs index 80720306ed..02b105b920 100644 --- a/src/Libraries/SmartStore.Data/Caching/EfMappingViewCache.cs +++ b/src/Libraries/SmartStore.Data/Caching/EfMappingViewCache.cs @@ -1,56 +1,50 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Data.Entity.Infrastructure.MappingViews; +using System.Collections.Generic; using System.Data.Entity.Core.Metadata.Edm; +using System.Data.Entity.Infrastructure.MappingViews; +using System.Linq; namespace SmartStore.Data.Caching { - public class EfMappingViewCache : DbMappingViewCache - { - private readonly string _hash; - private readonly Dictionary _views; - - public EfMappingViewCache(EfMappingView cachedView) - { - _hash = cachedView.Hash; - _views = cachedView.Views; - } - - public EfMappingViewCache(string hash, Dictionary views) - { - Guard.NotEmpty(hash, nameof(hash)); - Guard.NotNull(views, nameof(views)); - - _hash = hash; - _views = views.ToDictionary(kvp => GetExtentFullName(kvp.Key), kvp => kvp.Value); - } - - public override string MappingHashValue - { - get { return _hash; } - } - - public override DbMappingView GetView(EntitySetBase extent) - { - _views.TryGetValue(GetExtentFullName(extent), out var mappingView); - return mappingView; - } - - internal static string GetExtentFullName(EntitySetBase entitySet) - { - return entitySet.EntityContainer.Name + "." + entitySet.Name; - } - } - - /// - /// For JSON serialization - /// - public sealed class EfMappingView - { - public string Hash { get; set; } - public Dictionary Views { get; set; } - } + public class EfMappingViewCache : DbMappingViewCache + { + private readonly string _hash; + private readonly Dictionary _views; + + public EfMappingViewCache(EfMappingView cachedView) + { + _hash = cachedView.Hash; + _views = cachedView.Views; + } + + public EfMappingViewCache(string hash, Dictionary views) + { + Guard.NotEmpty(hash, nameof(hash)); + Guard.NotNull(views, nameof(views)); + + _hash = hash; + _views = views.ToDictionary(kvp => GetExtentFullName(kvp.Key), kvp => kvp.Value); + } + + public override string MappingHashValue => _hash; + + public override DbMappingView GetView(EntitySetBase extent) + { + _views.TryGetValue(GetExtentFullName(extent), out var mappingView); + return mappingView; + } + + internal static string GetExtentFullName(EntitySetBase entitySet) + { + return entitySet.EntityContainer.Name + "." + entitySet.Name; + } + } + + /// + /// For JSON serialization + /// + public sealed class EfMappingView + { + public string Hash { get; set; } + public Dictionary Views { get; set; } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/EfMappingViewCacheFactory.cs b/src/Libraries/SmartStore.Data/Caching/EfMappingViewCacheFactory.cs index 021da40be7..85c0d10238 100644 --- a/src/Libraries/SmartStore.Data/Caching/EfMappingViewCacheFactory.cs +++ b/src/Libraries/SmartStore.Data/Caching/EfMappingViewCacheFactory.cs @@ -13,158 +13,158 @@ namespace SmartStore.Data.Caching { - public class EfMappingViewCacheFactory : DbMappingViewCacheFactory - { - private readonly static ConcurrentDictionary _lookup = - new ConcurrentDictionary(); - - private readonly string _viewFilePath; - private readonly static object _lock = new object(); - - private EfMappingViewCacheFactory(string viewFilePath) - { - _viewFilePath = viewFilePath; - } - - public static void SetContext() - where TContext : DbContext, new() - { - using (var ctx = new TContext()) - { - SetContext(ctx); - } - } - - /// - /// Sets a for a mapping represented - /// by a derived class. - /// - /// A derived class instance containing - /// mapping to set the view cache factory for. - /// View cache factory - /// - /// This method must be called before EntityFramework generates views for the mapping - /// (which typically happens on the first query). - /// cannot be set more than once for the same - /// . - /// - public static void SetContext(DbContext ctx) - { - Guard.NotNull(ctx, nameof(ctx)); - - var objectContext = ((IObjectContextAdapter)ctx).ObjectContext; - var itemCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace); - var type = ctx.GetType(); - var filePath = Path.Combine(CommonHelper.MapPath("~/App_Data/EfCache"), type.Namespace + "." + type.Name + ".views"); - var factory = new EfMappingViewCacheFactory(filePath); - - itemCollection.MappingViewCacheFactory = factory; - _lookup.TryAdd(factory, itemCollection); - } - - /// - /// Returns a instance for the . - /// - /// View cache factory to return for. - /// A instance for the . - public static StorageMappingItemCollection GetMappingItemCollection(DbMappingViewCacheFactory viewCacheFactory) - { - Guard.NotNull(viewCacheFactory, nameof(viewCacheFactory)); - - if (!_lookup.TryGetValue(viewCacheFactory, out var mappingItemCollection)) - { - throw new InvalidOperationException("No StorageMappingItemCollection instance found for the provided DbMappingViewCacheFactory."); - } - - return mappingItemCollection; - } - - /// - /// Creates a new instance of the class for - /// the given and . - /// - /// The name of the conceptual model container. - /// The name of the store model container. - /// A new instance of the class. - public override DbMappingViewCache Create(string conceptualModelContainerName, string storeModelContainerName) - { - var mappingItemCollection = GetMappingItemCollection(); - - if (mappingItemCollection == null) - { - throw new InvalidOperationException("View cache not set for this mapping item collection"); - } - - var hash = mappingItemCollection.ComputeMappingHashValue(conceptualModelContainerName, storeModelContainerName); - - var cachedViews = Load(); - if (cachedViews != null && cachedViews.Hash == hash) - { - return new EfMappingViewCache(cachedViews); - } - - var views = GenerateViews(mappingItemCollection, conceptualModelContainerName, storeModelContainerName); - - cachedViews = CreateCachedViews(hash, views); - Save(cachedViews); - - return new EfMappingViewCache(hash, views); - } - - // virtual for mocking - internal virtual StorageMappingItemCollection GetMappingItemCollection() - { - return GetMappingItemCollection(this); - } - - // virtual for mocking - internal virtual Dictionary GenerateViews( - StorageMappingItemCollection mappingItemCollection, - string conceptualModelContainerName, - string storeModelContainerName) - { - var errors = new List(); - return mappingItemCollection.GenerateViews(conceptualModelContainerName, storeModelContainerName, errors); - } - - private EfMappingView CreateCachedViews(string hash, Dictionary views) - { - return new EfMappingView - { - Hash = hash, - Views = views.ToDictionary(k => EfMappingViewCache.GetExtentFullName(k.Key), v => v.Value) - }; - } - - private EfMappingView Load() - { - try - { - lock (_lock) - { - if (File.Exists(_viewFilePath)) - { - var json = File.ReadAllText(_viewFilePath); - return JsonConvert.DeserializeObject(json); - } - } - } - catch { } - - return null; - } - - private void Save(EfMappingView view) - { - try - { - lock (_lock) - { - var json = JsonConvert.SerializeObject(view); - File.WriteAllText(_viewFilePath, json); - } - } - catch { } - } - } + public class EfMappingViewCacheFactory : DbMappingViewCacheFactory + { + private readonly static ConcurrentDictionary _lookup = + new ConcurrentDictionary(); + + private readonly string _viewFilePath; + private readonly static object _lock = new object(); + + private EfMappingViewCacheFactory(string viewFilePath) + { + _viewFilePath = viewFilePath; + } + + public static void SetContext() + where TContext : DbContext, new() + { + using (var ctx = new TContext()) + { + SetContext(ctx); + } + } + + /// + /// Sets a for a mapping represented + /// by a derived class. + /// + /// A derived class instance containing + /// mapping to set the view cache factory for. + /// View cache factory + /// + /// This method must be called before EntityFramework generates views for the mapping + /// (which typically happens on the first query). + /// cannot be set more than once for the same + /// . + /// + public static void SetContext(DbContext ctx) + { + Guard.NotNull(ctx, nameof(ctx)); + + var objectContext = ((IObjectContextAdapter)ctx).ObjectContext; + var itemCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace); + var type = ctx.GetType(); + var filePath = Path.Combine(CommonHelper.MapPath("~/App_Data/EfCache"), type.Namespace + "." + type.Name + ".views"); + var factory = new EfMappingViewCacheFactory(filePath); + + itemCollection.MappingViewCacheFactory = factory; + _lookup.TryAdd(factory, itemCollection); + } + + /// + /// Returns a instance for the . + /// + /// View cache factory to return for. + /// A instance for the . + public static StorageMappingItemCollection GetMappingItemCollection(DbMappingViewCacheFactory viewCacheFactory) + { + Guard.NotNull(viewCacheFactory, nameof(viewCacheFactory)); + + if (!_lookup.TryGetValue(viewCacheFactory, out var mappingItemCollection)) + { + throw new InvalidOperationException("No StorageMappingItemCollection instance found for the provided DbMappingViewCacheFactory."); + } + + return mappingItemCollection; + } + + /// + /// Creates a new instance of the class for + /// the given and . + /// + /// The name of the conceptual model container. + /// The name of the store model container. + /// A new instance of the class. + public override DbMappingViewCache Create(string conceptualModelContainerName, string storeModelContainerName) + { + var mappingItemCollection = GetMappingItemCollection(); + + if (mappingItemCollection == null) + { + throw new InvalidOperationException("View cache not set for this mapping item collection"); + } + + var hash = mappingItemCollection.ComputeMappingHashValue(conceptualModelContainerName, storeModelContainerName); + + var cachedViews = Load(); + if (cachedViews != null && cachedViews.Hash == hash) + { + return new EfMappingViewCache(cachedViews); + } + + var views = GenerateViews(mappingItemCollection, conceptualModelContainerName, storeModelContainerName); + + cachedViews = CreateCachedViews(hash, views); + Save(cachedViews); + + return new EfMappingViewCache(hash, views); + } + + // virtual for mocking + internal virtual StorageMappingItemCollection GetMappingItemCollection() + { + return GetMappingItemCollection(this); + } + + // virtual for mocking + internal virtual Dictionary GenerateViews( + StorageMappingItemCollection mappingItemCollection, + string conceptualModelContainerName, + string storeModelContainerName) + { + var errors = new List(); + return mappingItemCollection.GenerateViews(conceptualModelContainerName, storeModelContainerName, errors); + } + + private EfMappingView CreateCachedViews(string hash, Dictionary views) + { + return new EfMappingView + { + Hash = hash, + Views = views.ToDictionary(k => EfMappingViewCache.GetExtentFullName(k.Key), v => v.Value) + }; + } + + private EfMappingView Load() + { + try + { + lock (_lock) + { + if (File.Exists(_viewFilePath)) + { + var json = File.ReadAllText(_viewFilePath); + return JsonConvert.DeserializeObject(json); + } + } + } + catch { } + + return null; + } + + private void Save(EfMappingView view) + { + try + { + lock (_lock) + { + var json = JsonConvert.SerializeObject(view); + File.WriteAllText(_viewFilePath, json); + } + } + catch { } + } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/IDbCache.cs b/src/Libraries/SmartStore.Data/Caching/IDbCache.cs index 086fdf447c..74aa51c1a7 100644 --- a/src/Libraries/SmartStore.Data/Caching/IDbCache.cs +++ b/src/Libraries/SmartStore.Data/Caching/IDbCache.cs @@ -4,127 +4,127 @@ namespace SmartStore.Data.Caching { - /// - /// Interface to be implemented by cache implementations. - /// - public interface IDbCache - { - /// - /// Controls whether query caching is enabled or idle - /// - bool Enabled { get; set; } - - /// - /// Tries to the get cached entry by key. - /// - /// The cache key. - /// The retrieved value. - /// A value of true if entry was found in the cache, false otherwise. - bool TryGet(string key, out object value); - - /// - /// Adds the specified entry to the cache. - /// - /// The entry key. - /// The entry value. - /// The list of dependent entity sets. - /// The absolute expiration. - DbCacheEntry Put(string key, object value, IEnumerable dependentEntitySets, TimeSpan? duration); - - /// - /// Invalidates all cache entries which are dependent on any of the specified entity sets. - /// - /// The entity sets. - void InvalidateSets(IEnumerable entitySets); - - /// - /// Invalidates cache entry with a given key. - /// - /// The cache key. - void InvalidateItem(string key); - - /// - /// Deletes all items from static and request cache - /// - void Clear(); - - /// - /// Tries to the get cached entry by key from (HTTP) request scope - /// - /// The cache key. - /// The retrieved value. - /// A value of true if entry was found in the cache, false otherwise. - bool RequestTryGet(string key, out DbCacheEntry value); - - /// - /// Adds the specified entry to the request scoped cache. - /// - /// The entry key. - /// The entry value. - /// The list of dependent entity sets. - DbCacheEntry RequestPut(string key, object value, string[] dependentEntitySets); - - /// - /// Invalidates all request scoped cache entries which are dependent on any of the specified entity sets. - /// - /// The entity sets. - void RequestInvalidateSets(IEnumerable entitySets); - - /// - /// Invalidates request scoped cache entry with a given key. - /// - /// The cache key. - void RequestInvalidateItem(string key); - } - - public class NullDbCache : IDbCache - { - public bool Enabled - { - get; - set; - } - - public bool TryGet(string key, out object value) - { - value = null; - return false; - } - - public DbCacheEntry Put(string key, object value, IEnumerable dependentEntitySets, TimeSpan? duration) - { - return new DbCacheEntry { Key = key, Value = value, CachedOnUtc = DateTime.UtcNow, EntitySets = dependentEntitySets.ToArray(), Duration = duration }; - } - - public void InvalidateSets(IEnumerable entitySets) - { - } - - public void InvalidateItem(string key) - { - } - - public void Clear() - { - } - - public bool RequestTryGet(string key, out DbCacheEntry value) - { - value = null; - return false; - } - - public DbCacheEntry RequestPut(string key, object value, string[] dependentEntitySets) - { - return new DbCacheEntry { Key = key, Value = value, CachedOnUtc = DateTime.UtcNow, EntitySets = dependentEntitySets.ToArray() }; - } - - public void RequestInvalidateSets(IEnumerable entitySets) - { - } - - public void RequestInvalidateItem(string key) - { - } - } + /// + /// Interface to be implemented by cache implementations. + /// + public interface IDbCache + { + /// + /// Controls whether query caching is enabled or idle + /// + bool Enabled { get; set; } + + /// + /// Tries to the get cached entry by key. + /// + /// The cache key. + /// The retrieved value. + /// A value of true if entry was found in the cache, false otherwise. + bool TryGet(string key, out object value); + + /// + /// Adds the specified entry to the cache. + /// + /// The entry key. + /// The entry value. + /// The list of dependent entity sets. + /// The absolute expiration. + DbCacheEntry Put(string key, object value, IEnumerable dependentEntitySets, TimeSpan? duration); + + /// + /// Invalidates all cache entries which are dependent on any of the specified entity sets. + /// + /// The entity sets. + void InvalidateSets(IEnumerable entitySets); + + /// + /// Invalidates cache entry with a given key. + /// + /// The cache key. + void InvalidateItem(string key); + + /// + /// Deletes all items from static and request cache + /// + void Clear(); + + /// + /// Tries to the get cached entry by key from (HTTP) request scope + /// + /// The cache key. + /// The retrieved value. + /// A value of true if entry was found in the cache, false otherwise. + bool RequestTryGet(string key, out DbCacheEntry value); + + /// + /// Adds the specified entry to the request scoped cache. + /// + /// The entry key. + /// The entry value. + /// The list of dependent entity sets. + DbCacheEntry RequestPut(string key, object value, string[] dependentEntitySets); + + /// + /// Invalidates all request scoped cache entries which are dependent on any of the specified entity sets. + /// + /// The entity sets. + void RequestInvalidateSets(IEnumerable entitySets); + + /// + /// Invalidates request scoped cache entry with a given key. + /// + /// The cache key. + void RequestInvalidateItem(string key); + } + + public class NullDbCache : IDbCache + { + public bool Enabled + { + get; + set; + } + + public bool TryGet(string key, out object value) + { + value = null; + return false; + } + + public DbCacheEntry Put(string key, object value, IEnumerable dependentEntitySets, TimeSpan? duration) + { + return new DbCacheEntry { Key = key, Value = value, CachedOnUtc = DateTime.UtcNow, EntitySets = dependentEntitySets.ToArray(), Duration = duration }; + } + + public void InvalidateSets(IEnumerable entitySets) + { + } + + public void InvalidateItem(string key) + { + } + + public void Clear() + { + } + + public bool RequestTryGet(string key, out DbCacheEntry value) + { + value = null; + return false; + } + + public DbCacheEntry RequestPut(string key, object value, string[] dependentEntitySets) + { + return new DbCacheEntry { Key = key, Value = value, CachedOnUtc = DateTime.UtcNow, EntitySets = dependentEntitySets.ToArray() }; + } + + public void RequestInvalidateSets(IEnumerable entitySets) + { + } + + public void RequestInvalidateItem(string key) + { + } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/QueryRegistrar.cs b/src/Libraries/SmartStore.Data/Caching/QueryRegistrar.cs index e094a0b521..3421c4651e 100644 --- a/src/Libraries/SmartStore.Data/Caching/QueryRegistrar.cs +++ b/src/Libraries/SmartStore.Data/Caching/QueryRegistrar.cs @@ -1,55 +1,54 @@ -using System; -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Data.Entity.Core.Metadata.Edm; namespace SmartStore.Data.Caching { - internal class QueryRegistrar - { - private readonly ConcurrentDictionary> _queries = new ConcurrentDictionary>(); - - public void AddQuery(MetadataWorkspace workspace, string sql) - { - Guard.NotNull(workspace, nameof(workspace)); - Guard.NotEmpty(sql, nameof(sql)); - - var queries = _queries.GetOrAdd(workspace, new HashSet()); - lock (queries) - { - queries.Add(sql); - } - } - - public bool RemoveQuery(MetadataWorkspace workspace, string sql) - { - Guard.NotNull(workspace, nameof(workspace)); - Guard.NotEmpty(sql, nameof(sql)); - - HashSet queries; - if (_queries.TryGetValue(workspace, out queries)) - { - lock (queries) - { - return queries.Remove(sql); - } - } - - return false; - } - - public bool ContainsQuery(MetadataWorkspace workspace, string sql) - { - Guard.NotNull(workspace, nameof(workspace)); - Guard.NotEmpty(sql, nameof(sql)); - - HashSet queries; - if (_queries.TryGetValue(workspace, out queries)) - { - return queries.Contains(sql); - } - - return false; - } - } + internal class QueryRegistrar + { + private readonly ConcurrentDictionary> _queries = new ConcurrentDictionary>(); + + public void AddQuery(MetadataWorkspace workspace, string sql) + { + Guard.NotNull(workspace, nameof(workspace)); + Guard.NotEmpty(sql, nameof(sql)); + + var queries = _queries.GetOrAdd(workspace, new HashSet()); + lock (queries) + { + queries.Add(sql); + } + } + + public bool RemoveQuery(MetadataWorkspace workspace, string sql) + { + Guard.NotNull(workspace, nameof(workspace)); + Guard.NotEmpty(sql, nameof(sql)); + + HashSet queries; + if (_queries.TryGetValue(workspace, out queries)) + { + lock (queries) + { + return queries.Remove(sql); + } + } + + return false; + } + + public bool ContainsQuery(MetadataWorkspace workspace, string sql) + { + Guard.NotNull(workspace, nameof(workspace)); + Guard.NotEmpty(sql, nameof(sql)); + + HashSet queries; + if (_queries.TryGetValue(workspace, out queries)) + { + return queries.Contains(sql); + } + + return false; + } + } } diff --git a/src/Libraries/SmartStore.Data/Caching/SingletonQueries.cs b/src/Libraries/SmartStore.Data/Caching/SingletonQueries.cs index 6f7ecb37e4..07c742e82d 100644 --- a/src/Libraries/SmartStore.Data/Caching/SingletonQueries.cs +++ b/src/Libraries/SmartStore.Data/Caching/SingletonQueries.cs @@ -2,29 +2,29 @@ namespace SmartStore.Data.Caching { - public sealed class SingletonQueries - { - public static readonly SingletonQueries Current = new SingletonQueries(); + public sealed class SingletonQueries + { + public static readonly SingletonQueries Current = new SingletonQueries(); - private readonly QueryRegistrar _cachedQueries = new QueryRegistrar(); + private readonly QueryRegistrar _cachedQueries = new QueryRegistrar(); - private SingletonQueries() - { - } + private SingletonQueries() + { + } - public void AddCachedQuery(MetadataWorkspace workspace, string sql) - { - _cachedQueries.AddQuery(workspace, sql); - } + public void AddCachedQuery(MetadataWorkspace workspace, string sql) + { + _cachedQueries.AddQuery(workspace, sql); + } - public bool RemoveCachedQuery(MetadataWorkspace workspace, string sql) - { - return _cachedQueries.RemoveQuery(workspace, sql); - } + public bool RemoveCachedQuery(MetadataWorkspace workspace, string sql) + { + return _cachedQueries.RemoveQuery(workspace, sql); + } - public bool IsQueryCached(MetadataWorkspace workspace, string sql) - { - return _cachedQueries.ContainsQuery(workspace, sql); - } - } + public bool IsQueryCached(MetadataWorkspace workspace, string sql) + { + return _cachedQueries.ContainsQuery(workspace, sql); + } + } } diff --git a/src/Libraries/SmartStore.Data/EfDataProviderFactory.cs b/src/Libraries/SmartStore.Data/EfDataProviderFactory.cs index 4f7652f4ab..e526f2cbec 100644 --- a/src/Libraries/SmartStore.Data/EfDataProviderFactory.cs +++ b/src/Libraries/SmartStore.Data/EfDataProviderFactory.cs @@ -1,28 +1,26 @@ -using System; -using SmartStore.Core; -using SmartStore.Core.Data; +using SmartStore.Core.Data; namespace SmartStore.Data { public partial class EfDataProviderFactory : DataProviderFactory { - public EfDataProviderFactory() - : this(DataSettings.Current) - { - } - - public EfDataProviderFactory(DataSettings settings) - : base(settings) + public EfDataProviderFactory() + : this(DataSettings.Current) + { + } + + public EfDataProviderFactory(DataSettings settings) + : base(settings) { } public override IDataProvider LoadDataProvider() { var providerName = Settings.DataProvider; - if (providerName.IsEmpty()) - { - throw new SmartException("Data Settings doesn't contain a providerName"); - } + if (providerName.IsEmpty()) + { + throw new SmartException("Data Settings doesn't contain a providerName"); + } switch (providerName.ToLowerInvariant()) { diff --git a/src/Libraries/SmartStore.Data/EfRepository.cs b/src/Libraries/SmartStore.Data/EfRepository.cs index 0d23c3df36..04934f60d3 100644 --- a/src/Libraries/SmartStore.Data/EfRepository.cs +++ b/src/Libraries/SmartStore.Data/EfRepository.cs @@ -1,13 +1,13 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Data.Entity; using System.Data.Entity.Validation; using System.Linq; using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; using SmartStore.Core; using SmartStore.Core.Data; -using SmartStore.Data.Caching; using EfState = System.Data.Entity.EntityState; namespace SmartStore.Data @@ -28,103 +28,99 @@ public virtual IQueryable Table { get { - if (_context.ForceNoTracking) - { - return this.Entities.AsNoTracking(); - } + if (_context.ForceNoTracking) + { + return this.Entities.AsNoTracking(); + } - return this.Entities; + return this.Entities; } } public virtual IQueryable TableUntracked { - get - { - return this.Entities.AsNoTracking(); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.Entities.AsNoTracking(); } - public virtual ICollection Local - { - get - { - return this.Entities.Local; - } - } + public virtual ICollection Local + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.Entities.Local; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual T Create() { return this.Entities.Create(); } - public virtual T GetById(object id) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual T GetById(object id) { return this.Entities.Find(id); } - public virtual T Attach(T entity) - { - return this.Entities.Attach(entity); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual Task GetByIdAsync(object id) + { + return this.Entities.FindAsync(id); + } - public virtual void Insert(T entity) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual T Attach(T entity) { - Guard.NotNull(entity, nameof(entity)); + return this.Entities.Attach(entity); + } - this.Entities.Add(entity); + public virtual void Insert(T entity) + { + Guard.NotNull(entity, nameof(entity)); - if (this.AutoCommitEnabledInternal) - { - _context.SaveChanges(); - } + this.Entities.Add(entity); + if (this.AutoCommitEnabledInternal) + _context.SaveChanges(); } - public virtual void InsertRange(IEnumerable entities, int batchSize = 100) + public virtual async Task InsertAsync(T entity) { + Guard.NotNull(entity, nameof(entity)); + + this.Entities.Add(entity); + if (this.AutoCommitEnabledInternal) + await _context.SaveChangesAsync(); + } + + public virtual void InsertRange(IEnumerable entities, int batchSize = 100) + { + Guard.NotNull(entities, nameof(entities)); + try { - Guard.NotNull(entities, nameof(entities)); + foreach (var batch in entities.Slice(batchSize <= 0 ? int.MaxValue : batchSize)) + { + this.Entities.AddRange(batch); + if (this.AutoCommitEnabledInternal) + _context.SaveChanges(); + } + } + catch (DbEntityValidationException ex) + { + throw ex; + } + } + + public virtual async Task InsertRangeAsync(IEnumerable entities, int batchSize = 100) + { + Guard.NotNull(entities, nameof(entities)); - if (entities.Any()) + try + { + foreach (var batch in entities.Slice(batchSize <= 0 ? int.MaxValue : batchSize)) { - if (batchSize <= 0) - { - // insert all in one step - this.Entities.AddRange(entities); - if (this.AutoCommitEnabledInternal) - { - _context.SaveChanges(); - } - } - else - { - int i = 1; - bool saved = false; - foreach (var entity in entities) - { - this.Entities.Add(entity); - saved = false; - if (i % batchSize == 0) - { - if (this.AutoCommitEnabledInternal) - { - _context.SaveChanges(); - } - i = 0; - saved = true; - } - i++; - } - - if (!saved) - { - if (this.AutoCommitEnabledInternal) - { - _context.SaveChanges(); - } - } - } + this.Entities.AddRange(batch); + if (this.AutoCommitEnabledInternal) + await _context.SaveChangesAsync(); } } catch (DbEntityValidationException ex) @@ -133,86 +129,128 @@ public virtual void InsertRange(IEnumerable entities, int batchSize = 100) } } - public virtual void Update(T entity) + public virtual void Update(T entity) { - Guard.NotNull(entity, nameof(entity)); + Guard.NotNull(entity, nameof(entity)); - ChangeStateToModifiedIfApplicable(entity); + ChangeStateToModifiedIfApplicable(entity); + if (this.AutoCommitEnabledInternal) + _context.SaveChanges(); + } + + public virtual async Task UpdateAsync(T entity) + { + Guard.NotNull(entity, nameof(entity)); - if (this.AutoCommitEnabledInternal) - { - _context.SaveChanges(); - } + ChangeStateToModifiedIfApplicable(entity); + if (this.AutoCommitEnabledInternal) + await _context.SaveChangesAsync(); } - public virtual void UpdateRange(IEnumerable entities) - { - Guard.NotNull(entities, nameof(entities)); - - foreach (var entity in entities) - { - ChangeStateToModifiedIfApplicable(entity); - } - - if (this.AutoCommitEnabledInternal) - { - _context.SaveChanges(); - } - } - - private void ChangeStateToModifiedIfApplicable(T entity) - { - if (entity.IsTransientRecord()) - return; - - var entry = InternalContext.Entry(entity); - - if (entry.State == EfState.Detached) - { - // Entity was detached before or was explicitly constructed. - // This unfortunately sets all properties to modified. - entry.State = EfState.Modified; - } - else if (entry.State == EfState.Unchanged) - { - // We simply do nothing here, because it is ensured now that DetectChanges() - // gets implicitly called prior SaveChanges(). - - //if (this.AutoCommitEnabledInternal && !ctx.Configuration.AutoDetectChangesEnabled) - //{ - // _context.DetectChanges(); - //} - } - } - - public virtual void Delete(T entity) + public virtual void UpdateRange(IEnumerable entities) { - Guard.NotNull(entity, nameof(entity)); + Guard.NotNull(entities, nameof(entities)); - InternalContext.Entry(entity).State = EfState.Deleted; + foreach (var entity in entities) + { + ChangeStateToModifiedIfApplicable(entity); + } - if (this.AutoCommitEnabledInternal) - { - _context.SaveChanges(); - } + if (this.AutoCommitEnabledInternal) + { + _context.SaveChanges(); + } } - public virtual void DeleteRange(IEnumerable entities) - { - Guard.NotNull(entities, nameof(entities)); + public virtual async Task UpdateRangeAsync(IEnumerable entities) + { + Guard.NotNull(entities, nameof(entities)); - foreach (var entity in entities) - { - InternalContext.Entry(entity).State = EfState.Deleted; - } + foreach (var entity in entities) + { + ChangeStateToModifiedIfApplicable(entity); + } - if (this.AutoCommitEnabledInternal) - { - _context.SaveChanges(); - } - } + if (this.AutoCommitEnabledInternal) + { + await _context.SaveChangesAsync(); + } + } - [Obsolete("Use the extension method from 'SmartStore.Core, SmartStore.Core.Data' instead")] + private void ChangeStateToModifiedIfApplicable(T entity) + { + if (entity.IsTransientRecord()) + return; + + var entry = InternalContext.Entry(entity); + + if (entry.State == EfState.Detached) + { + // Entity was detached before or was explicitly constructed. + // This unfortunately sets all properties to modified. + entry.State = EfState.Modified; + } + else if (entry.State == EfState.Unchanged) + { + // We simply do nothing here, because it is ensured now that DetectChanges() + // gets implicitly called prior SaveChanges(). + + //if (this.AutoCommitEnabledInternal && !ctx.Configuration.AutoDetectChangesEnabled) + //{ + // _context.DetectChanges(); + //} + } + } + + public virtual void Delete(T entity) + { + Guard.NotNull(entity, nameof(entity)); + + InternalContext.Entry(entity).State = EfState.Deleted; + if (this.AutoCommitEnabledInternal) + _context.SaveChanges(); + } + + public virtual async Task DeleteAsync(T entity) + { + Guard.NotNull(entity, nameof(entity)); + + InternalContext.Entry(entity).State = EfState.Deleted; + if (this.AutoCommitEnabledInternal) + await _context.SaveChangesAsync(); + } + + public virtual void DeleteRange(IEnumerable entities) + { + Guard.NotNull(entities, nameof(entities)); + + foreach (var entity in entities) + { + InternalContext.Entry(entity).State = EfState.Deleted; + } + + if (this.AutoCommitEnabledInternal) + { + _context.SaveChanges(); + } + } + + public virtual async Task DeleteRangeAsync(IEnumerable entities) + { + Guard.NotNull(entities, nameof(entities)); + + foreach (var entity in entities) + { + InternalContext.Entry(entity).State = EfState.Deleted; + } + + if (this.AutoCommitEnabledInternal) + { + await _context.SaveChangesAsync(); + } + } + + [Obsolete("Use the extension method from 'SmartStore.Core, SmartStore.Core.Data' instead")] public IQueryable Expand(IQueryable query, string path) { Guard.NotNull(query, "query"); @@ -221,7 +259,7 @@ public IQueryable Expand(IQueryable query, string path) return query.Include(path); } - [Obsolete("Use the extension method from 'SmartStore.Core, SmartStore.Core.Data' instead")] + [Obsolete("Use the extension method from 'SmartStore.Core, SmartStore.Core.Data' instead")] public IQueryable Expand(IQueryable query, Expression> path) { Guard.NotNull(query, "query"); @@ -230,29 +268,17 @@ public IQueryable Expand(IQueryable query, Expression _context; public bool? AutoCommitEnabled { get; set; } - private bool AutoCommitEnabledInternal - { - get - { - return this.AutoCommitEnabled ?? _context.AutoCommitEnabled; - } - } + private bool AutoCommitEnabledInternal => this.AutoCommitEnabled ?? _context.AutoCommitEnabled; #endregion #region Helpers - protected internal ObjectContextBase InternalContext - { - get { return _context as ObjectContextBase; } - } + protected internal ObjectContextBase InternalContext => _context as ObjectContextBase; private DbSet Entities { diff --git a/src/Libraries/SmartStore.Data/Extensions/DataReaderExtensions.cs b/src/Libraries/SmartStore.Data/Extensions/DataReaderExtensions.cs index 3f974eb075..49d4d26460 100644 --- a/src/Libraries/SmartStore.Data/Extensions/DataReaderExtensions.cs +++ b/src/Libraries/SmartStore.Data/Extensions/DataReaderExtensions.cs @@ -3,142 +3,141 @@ using System; using System.Collections.Generic; using System.Data; -using System.Reflection; -using System.Linq; using System.Dynamic; +using System.Linq; using SmartStore.ComponentModel; namespace SmartStore.Data { public static class DataReaderExtensions { - public static object GetValue(this IDataReader reader, string columnName) - { - try - { - if (reader != null && !reader.IsClosed && columnName.HasValue()) - { - int ordinal = reader.GetOrdinal(columnName); - return reader.GetValue(ordinal); - } - } - catch (Exception ex) - { - ex.Dump(); - } - - return null; - } - - public static IEnumerable MapSequence(this IDataReader reader, params string[] fieldsToSkip) - where T : new() - { - Guard.NotNull(reader, nameof(reader)); - - while (reader.Read()) - { - yield return reader.Map(fieldsToSkip); - } - } - - public static IEnumerable MapSequence(this IDataReader reader, params string[] fieldsToSkip) - { - Guard.NotNull(reader, nameof(reader)); - + public static object GetValue(this IDataReader reader, string columnName) + { + try + { + if (reader != null && !reader.IsClosed && columnName.HasValue()) + { + int ordinal = reader.GetOrdinal(columnName); + return reader.GetValue(ordinal); + } + } + catch (Exception ex) + { + ex.Dump(); + } + + return null; + } + + public static IEnumerable MapSequence(this IDataReader reader, params string[] fieldsToSkip) + where T : new() + { + Guard.NotNull(reader, nameof(reader)); + while (reader.Read()) - { - yield return reader.Map(fieldsToSkip); - } - } - - /// - /// Populates an object from a single DataReader row using - /// fast reflection routines by matching the DataReader fields to a public property on - /// the object passed in. Unmatched properties are left unchanged. - /// - /// You need to pass in a data reader located on the active row you want - /// to serialize. - /// - /// Instance of the DataReader to read data from. Should be located on the correct record (Read() should have been called on it before calling this method) - /// An array of reader field names to ignore - public static T Map(this IDataReader reader, params string[] fieldsToSkip) - where T : new() - { - if (reader.IsClosed) - throw new InvalidOperationException("Data reader cannot be used because it's already closed"); - - var instance = new T(); - MapObject(reader, instance, fieldsToSkip); - return instance; - } - - public static dynamic Map(this IDataReader reader, params string[] fieldsToSkip) - { - if (reader.IsClosed) - throw new InvalidOperationException("Data reader cannot be used because it's already closed"); - - dynamic instance = new ExpandoObject(); - MapDictionary(reader, instance, fieldsToSkip); - return instance; - } - - public static void Map(this IDataReader reader, object instance, params string[] fieldsToSkip) - { - Guard.NotNull(instance, "instance"); - - if (reader.IsClosed) - throw new InvalidOperationException("Data reader cannot be used because it's already closed"); - - var dict = instance as IDictionary; - - if (dict != null) - { - MapDictionary(reader, dict, fieldsToSkip); + { + yield return reader.Map(fieldsToSkip); } - else - { - MapObject(reader, instance, fieldsToSkip); - } - } - - private static void MapObject(IDataReader reader, object instance, params string[] fieldsToSkip) - { - var fastProperties = FastProperty.GetProperties(instance); - - if (fastProperties.Count == 0) - return; - - for (int i = 0; i < reader.FieldCount; i++) - { - string name = reader.GetName(i); - if (fastProperties.ContainsKey(name)) - { - var fastProp = fastProperties[name]; - - if (fieldsToSkip.Contains(name)) - continue; - - if ((fastProp != null) && fastProp.Property.CanWrite) - { - var dbValue = reader.GetValue(i); - fastProp.SetValue(instance, (dbValue == DBNull.Value) ? null : dbValue); - } - } - } - } - - private static void MapDictionary(IDataReader reader, IDictionary instance, params string[] fieldsToSkip) - { - for (int i = 0; i < reader.FieldCount; i++) - { - string name = reader.GetName(i); - - if (fieldsToSkip.Contains(name)) - continue; - - var dbValue = reader.GetValue(i); - instance[name] = (dbValue == DBNull.Value) ? null : dbValue; + } + + public static IEnumerable MapSequence(this IDataReader reader, params string[] fieldsToSkip) + { + Guard.NotNull(reader, nameof(reader)); + + while (reader.Read()) + { + yield return reader.Map(fieldsToSkip); + } + } + + /// + /// Populates an object from a single DataReader row using + /// fast reflection routines by matching the DataReader fields to a public property on + /// the object passed in. Unmatched properties are left unchanged. + /// + /// You need to pass in a data reader located on the active row you want + /// to serialize. + /// + /// Instance of the DataReader to read data from. Should be located on the correct record (Read() should have been called on it before calling this method) + /// An array of reader field names to ignore + public static T Map(this IDataReader reader, params string[] fieldsToSkip) + where T : new() + { + if (reader.IsClosed) + throw new InvalidOperationException("Data reader cannot be used because it's already closed"); + + var instance = new T(); + MapObject(reader, instance, fieldsToSkip); + return instance; + } + + public static dynamic Map(this IDataReader reader, params string[] fieldsToSkip) + { + if (reader.IsClosed) + throw new InvalidOperationException("Data reader cannot be used because it's already closed"); + + dynamic instance = new ExpandoObject(); + MapDictionary(reader, instance, fieldsToSkip); + return instance; + } + + public static void Map(this IDataReader reader, object instance, params string[] fieldsToSkip) + { + Guard.NotNull(instance, "instance"); + + if (reader.IsClosed) + throw new InvalidOperationException("Data reader cannot be used because it's already closed"); + + var dict = instance as IDictionary; + + if (dict != null) + { + MapDictionary(reader, dict, fieldsToSkip); + } + else + { + MapObject(reader, instance, fieldsToSkip); + } + } + + private static void MapObject(IDataReader reader, object instance, params string[] fieldsToSkip) + { + var fastProperties = FastProperty.GetProperties(instance.GetType()); + + if (fastProperties.Count == 0) + return; + + for (int i = 0; i < reader.FieldCount; i++) + { + string name = reader.GetName(i); + if (fastProperties.ContainsKey(name)) + { + var fastProp = fastProperties[name]; + + if (fieldsToSkip.Contains(name)) + continue; + + if ((fastProp != null) && fastProp.Property.CanWrite) + { + var dbValue = reader.GetValue(i); + fastProp.SetValue(instance, (dbValue == DBNull.Value) ? null : dbValue); + } + } + } + } + + private static void MapDictionary(IDataReader reader, IDictionary instance, params string[] fieldsToSkip) + { + for (int i = 0; i < reader.FieldCount; i++) + { + string name = reader.GetName(i); + + if (fieldsToSkip.Contains(name)) + continue; + + var dbValue = reader.GetValue(i); + instance[name] = (dbValue == DBNull.Value) ? null : dbValue; } - } - } + } + } } diff --git a/src/Libraries/SmartStore.Data/Extensions/DbContextExtensions.cs b/src/Libraries/SmartStore.Data/Extensions/DbContextExtensions.cs index bf528fd1e0..1dace89f64 100644 --- a/src/Libraries/SmartStore.Data/Extensions/DbContextExtensions.cs +++ b/src/Libraries/SmartStore.Data/Extensions/DbContextExtensions.cs @@ -1,97 +1,109 @@ -using System.Linq; -using System.Data.Entity; +using System.Data.Entity; using System.Data.Entity.Infrastructure; -using SmartStore.Core; using System.Diagnostics; +using System.Linq; +using SmartStore.Core; using SmartStore.Core.Data; namespace SmartStore { - public class SqlServerInfo - { - public string ProductVersion { get; set; } - public string PatchLevel { get; set; } - public string ProductEdition { get; set; } - public string ClrVersion { get; set; } - public string DefaultCollation { get; set; } - public string Instance { get; set; } - public int Lcid { get; set; } - public string ServerName { get; set; } - } - - public static class DbContextExtensions - { - - public static bool ColumnExists(this DbContext context, string tableName, string columnName) - { - if (context != null && tableName.HasValue() && columnName.HasValue()) - { - //string sql = @"Select column_name From INFORMATION_SCHEMA.COLUMNS Where table_name = @tableName And column_name = @columnName"; - - //string col = ((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery(sql, - // new SqlParameter("@tableName", tableName), new SqlParameter("@columnName", columnName)).FirstOrDefault(); - - string sql = @"Select column_name From INFORMATION_SCHEMA.COLUMNS Where table_name = {0} And column_name = {1}"; - string col = ((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery(sql, tableName, columnName).FirstOrDefault(); - return col.HasValue(); - } - - return false; - } - - public static void ColumnEnsure(this DbContext context, string tableName, string columnName, string columnDataType) - { - if (!context.ColumnExists(tableName, columnName)) - { - //context.Database.ExecuteSqlCommand("ALTER TABLE dbo.[{0}] ADD {1} {2}".FormatWith(tableName, columnName, columnDataType)); - context.Database.ExecuteSqlCommand("ALTER TABLE {0} ADD {1} {2}".FormatWith(tableName, columnName, columnDataType)); - } - } - - public static void ColumnDelete(this DbContext context, string tableName, string columnName) - { - if (context.ColumnExists(tableName, columnName)) - { - context.Database.ExecuteSqlCommand("ALTER TABLE {0} DROP COLUMN {1}".FormatWith(tableName, columnName)); - } - } - - public static bool DatabaseExists(this DbContext context, string databaseName) - { - if (context != null && databaseName.HasValue()) { - //string sql = @"Select database_id From sys.databases Where Name = @databaseName"; - - //int databaseID = ((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery(sql, new SqlParameter("@databaseName", databaseName)).FirstOrDefault(); - - string sql = @"Select database_id From sys.databases Where Name = {0}"; - - int databaseID = ((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery(sql, databaseName).FirstOrDefault(); - - return databaseID > 0; - } - - return false; - } - - public static int InsertInto(this DbContext context, string sql, params object[] parameters) - { - return (int)context.Database.SqlQuery(sql + "; SELECT @@IDENTITY;", parameters).FirstOrDefault(); - } - - public static int Execute(this DbContext context, string sql, params object[] parameters) - { - return context.Database.ExecuteSqlCommand(sql, parameters); - } - - [Conditional("DEBUG")] - [DebuggerStepThrough] - public static void DumpAttachedEntities(this DbContext context) - { - context.ChangeTracker.Entries() - .Where(x => x.State != System.Data.Entity.EntityState.Detached) - .ToList() - .ForEach(x => "{0} {1} {2}".FormatInvariant((x.Entity as BaseEntity).Id, x.State.ToString(), x.Entity.GetType().Name).Dump()); - } + public class SqlServerInfo + { + public string ProductVersion { get; set; } + public string PatchLevel { get; set; } + public string ProductEdition { get; set; } + public string ClrVersion { get; set; } + public string DefaultCollation { get; set; } + public string Instance { get; set; } + public int Lcid { get; set; } + public string ServerName { get; set; } + } + + public static class DbContextExtensions + { + public static bool ColumnExists(this DbContext context, string tableName, string columnName) + { + if (context != null && tableName.HasValue() && columnName.HasValue()) + { + //string sql = @"Select column_name From INFORMATION_SCHEMA.COLUMNS Where table_name = @tableName And column_name = @columnName"; + + //string col = ((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery(sql, + // new SqlParameter("@tableName", tableName), new SqlParameter("@columnName", columnName)).FirstOrDefault(); + + string sql = @"Select column_name From INFORMATION_SCHEMA.COLUMNS Where table_name = {0} And column_name = {1}"; + string col = ((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery(sql, tableName, columnName).FirstOrDefault(); + return col.HasValue(); + } + + return false; + } + + public static void ColumnEnsure(this DbContext context, string tableName, string columnName, string columnDataType) + { + if (!context.ColumnExists(tableName, columnName)) + { + //context.Database.ExecuteSqlCommand("ALTER TABLE dbo.[{0}] ADD {1} {2}".FormatWith(tableName, columnName, columnDataType)); + context.Database.ExecuteSqlCommand("ALTER TABLE {0} ADD {1} {2}".FormatWith(tableName, columnName, columnDataType)); + } + } + + public static void ColumnDelete(this DbContext context, string tableName, string columnName) + { + if (context.ColumnExists(tableName, columnName)) + { + context.Database.ExecuteSqlCommand("ALTER TABLE {0} DROP COLUMN {1}".FormatWith(tableName, columnName)); + } + } + + public static bool TableExists(this DbContext context, string tableName) + { + if (context != null && tableName.HasValue()) + { + var sql = @"Select table_name From INFORMATION_SCHEMA.TABLES Where table_name = {0}"; + var table = ((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery(sql, tableName).FirstOrDefault(); + return table.HasValue(); + } + + return false; + } + + public static bool DatabaseExists(this DbContext context, string databaseName) + { + if (context != null && databaseName.HasValue()) + { + //string sql = @"Select database_id From sys.databases Where Name = @databaseName"; + + //int databaseID = ((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery(sql, new SqlParameter("@databaseName", databaseName)).FirstOrDefault(); + + string sql = @"Select database_id From sys.databases Where Name = {0}"; + + int databaseID = ((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery(sql, databaseName).FirstOrDefault(); + + return databaseID > 0; + } + + return false; + } + + public static int InsertInto(this DbContext context, string sql, params object[] parameters) + { + return (int)context.Database.SqlQuery(sql + "; SELECT @@IDENTITY;", parameters).FirstOrDefault(); + } + + public static int Execute(this DbContext context, string sql, params object[] parameters) + { + return context.Database.ExecuteSqlCommand(sql, parameters); + } + + [Conditional("DEBUG")] + [DebuggerStepThrough] + public static void DumpAttachedEntities(this DbContext context) + { + context.ChangeTracker.Entries() + .Where(x => x.State != System.Data.Entity.EntityState.Detached) + .ToList() + .ForEach(x => "{0} {1} {2}".FormatInvariant((x.Entity as BaseEntity).Id, x.State.ToString(), x.Entity.GetType().Name).Dump()); + } [Conditional("DEBUG")] [DebuggerStepThrough] @@ -123,8 +135,8 @@ public static void CreateSqlTimeout(this DbContext context) } internal static SqlServerInfo GetSqlServerInfo(this DbContext context) - { - string sql = @"SELECT + { + string sql = @"SELECT SERVERPROPERTY('productversion') as 'ProductVersion', SERVERPROPERTY('productlevel') as 'PatchLevel', SERVERPROPERTY('edition') as 'ProductEdition', @@ -134,8 +146,8 @@ internal static SqlServerInfo GetSqlServerInfo(this DbContext context) SERVERPROPERTY('lcid') as 'Lcid', SERVERPROPERTY('servername') as 'ServerName'"; - return context.Database.SqlQuery(sql).FirstOrDefault(); - } + return context.Database.SqlQuery(sql).FirstOrDefault(); + } - } + } } diff --git a/src/Libraries/SmartStore.Data/Extensions/DbEntityEntryExtensions.cs b/src/Libraries/SmartStore.Data/Extensions/DbEntityEntryExtensions.cs index 9d5688cac9..1543a8ba41 100644 --- a/src/Libraries/SmartStore.Data/Extensions/DbEntityEntryExtensions.cs +++ b/src/Libraries/SmartStore.Data/Extensions/DbEntityEntryExtensions.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Data.Entity.Infrastructure; using System.Linq; using SmartStore.Core.Data; @@ -7,185 +6,185 @@ namespace SmartStore.Data { - public static class DbEntityEntryExtensions - { - public static void ReloadEntity(this DbEntityEntry entry) - { - try - { - entry.Reload(); - } - catch - { - // Can occur when entity has been detached in the meantime (for whatever fucking reasons) - if (entry.State == EfState.Detached) - { - entry.State = EfState.Unchanged; - entry.Reload(); - } - } - } - - /// - /// Gets a dictionary with modified properties for the specified entity - /// - /// The entity entry instance for which to get modified properties for - /// - /// A dictionary, where the key is the name of the modified property - /// and the value is its ORIGINAL value (which was tracked when the entity - /// was attached to the context the first time) - /// Returns an empty dictionary if no modification could be detected. - /// - public static IDictionary GetModifiedProperties(this DbEntityEntry entry, IDbContext ctx) - { - var props = GetModifiedPropertyEntries(entry, ctx).ToDictionary(k => k.Name, v => v.OriginalValue); - - //System.Diagnostics.Debug.WriteLine("GetModifiedProperties: " + String.Join(", ", props.Select(x => x.Key))); - - return props; - } - - /// - /// Checks whether an entity entry has any modified property. - /// Only entities in state are scanned for changes. - /// Merged values provided by the are ignored. - /// - /// The entry instance - /// The data context - /// true if any property has changed, false otherwise - public static bool HasChanges(this DbEntityEntry entry, IDbContext ctx) - { - var hasChanges = GetModifiedPropertyEntries(entry, ctx).Any(); - return hasChanges; - } - - internal static IEnumerable GetModifiedPropertyEntries(this DbEntityEntry entry, IDbContext ctx) - { - // Be aware of the entity state. you cannot get modified properties for detached entities. - EnsureChangesDetected(entry, ctx); - - if (entry.State != EfState.Modified) - { - yield break; - } - - foreach (var name in entry.CurrentValues.PropertyNames) - { - var prop = entry.Property(name); - if (prop != null && PropIsModified(prop, ctx)) - { - // INFO: under certain conditions DbPropertyEntry.IsModified returns true, even when values are equal - yield return prop; - } - } - } - - public static bool IsPropertyModified(this DbEntityEntry entry, IDbContext ctx, string propertyName) - { - object originalValue; - return TryGetModifiedProperty(entry, ctx, propertyName, out originalValue); - } - - public static bool TryGetModifiedProperty(this DbEntityEntry entry, IDbContext ctx, string propertyName, out object originalValue) - { - Guard.NotEmpty(propertyName, nameof(propertyName)); - - EnsureChangesDetected(entry, ctx); - - originalValue = null; - - if (entry.State != EfState.Modified) - { - return false; - } - - var prop = entry.Property(propertyName); - if (prop != null && PropIsModified(prop, ctx)) - { - // INFO: under certain conditions DbPropertyEntry.IsModified returns true, even when values are equal - originalValue = prop.OriginalValue; - return true; - } - - return false; - } - - private static void EnsureChangesDetected(DbEntityEntry entry, IDbContext ctx) - { - var state = entry.State; - - if (ctx.AutoDetectChangesEnabled && state == EfState.Modified) - return; - - if ((ctx as ObjectContextBase)?.IsInSaveOperation == true) - return; - - if (state == EfState.Unchanged || state == EfState.Modified) - { - // When AutoDetectChanges is off we cannot be sure whether the entity is really unchanged, - // because no detection was performed to verify this. - DetectChangesInProperties(entry, ctx); - } - } - - public static void DetectChangesInProperties(this DbEntityEntry entry, IDbContext ctx) - { - ctx.DetectChanges(); - - #region Experimental - - //// ChangeDetection for single entity: calls DbEntityEntry.InternalEntry > ObjectStateEntry._stateEntry.DetectChangesInProperties(bool) - //var invoked = false; - - //try - //{ - // var internalEntry = FastProperty.GetProperty(entry.GetType(), "InternalEntry").GetValue(entry); - // if (internalEntry != null) - // { - // var objectStateEntry = FastProperty.GetProperty(internalEntry.GetType(), "ObjectStateEntry").GetValue(internalEntry); - // if (objectStateEntry != null) - // { - // var innerStateEntry = objectStateEntry.GetType().GetField("_stateEntry", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(objectStateEntry); - // if (innerStateEntry != null) - // { - // var dcMethod = innerStateEntry.GetType().GetMethod("DetectChangesInProperties", BindingFlags.NonPublic | BindingFlags.Instance); - // if (dcMethod != null) - // { - // //var requiresScalarChangeTracking = (bool)FastProperty.GetProperty(objectStateEntry.GetType(), "RequiresScalarChangeTracking").GetValue(objectStateEntry); - // //var requiresComplexChangeTracking = (bool)FastProperty.GetProperty(objectStateEntry.GetType(), "RequiresComplexChangeTracking").GetValue(objectStateEntry); - // dcMethod.Invoke(innerStateEntry, new object[] { false }); - // invoked = true; - // } - // } - // } - // } - //} - //finally - //{ - // if (!invoked) ctx.DetectChanges(); - //} - - #endregion - } - - private static bool PropIsModified(DbPropertyEntry prop, IDbContext ctx) - { - // INFO: "CurrentValue" cannot be used for entities in the Deleted state. - // INFO: "OriginalValues" cannot be used for entities in the Added state. - //return !AreEqual(prop.CurrentValue, prop.OriginalValue); - return ctx.AutoDetectChangesEnabled - ? prop.IsModified - : !AreEqual(prop.CurrentValue, prop.OriginalValue); - } - - private static bool AreEqual(object cur, object orig) - { - if (cur == null && orig == null) - return true; - - return orig != null - ? orig.Equals(cur) - : cur.Equals(orig); - } - } + public static class DbEntityEntryExtensions + { + public static void ReloadEntity(this DbEntityEntry entry) + { + try + { + entry.Reload(); + } + catch + { + // Can occur when entity has been detached in the meantime (for whatever fucking reasons) + if (entry.State == EfState.Detached) + { + entry.State = EfState.Unchanged; + entry.Reload(); + } + } + } + + /// + /// Gets a dictionary with modified properties for the specified entity + /// + /// The entity entry instance for which to get modified properties for + /// + /// A dictionary, where the key is the name of the modified property + /// and the value is its ORIGINAL value (which was tracked when the entity + /// was attached to the context the first time) + /// Returns an empty dictionary if no modification could be detected. + /// + public static IDictionary GetModifiedProperties(this DbEntityEntry entry, IDbContext ctx) + { + var props = GetModifiedPropertyEntries(entry, ctx).ToDictionary(k => k.Name, v => v.OriginalValue); + + //System.Diagnostics.Debug.WriteLine("GetModifiedProperties: " + String.Join(", ", props.Select(x => x.Key))); + + return props; + } + + /// + /// Checks whether an entity entry has any modified property. + /// Only entities in state are scanned for changes. + /// Merged values provided by the are ignored. + /// + /// The entry instance + /// The data context + /// true if any property has changed, false otherwise + public static bool HasChanges(this DbEntityEntry entry, IDbContext ctx) + { + var hasChanges = GetModifiedPropertyEntries(entry, ctx).Any(); + return hasChanges; + } + + internal static IEnumerable GetModifiedPropertyEntries(this DbEntityEntry entry, IDbContext ctx) + { + // Be aware of the entity state. you cannot get modified properties for detached entities. + EnsureChangesDetected(entry, ctx); + + if (entry.State != EfState.Modified) + { + yield break; + } + + foreach (var name in entry.CurrentValues.PropertyNames) + { + var prop = entry.Property(name); + if (prop != null && PropIsModified(prop, ctx)) + { + // INFO: under certain conditions DbPropertyEntry.IsModified returns true, even when values are equal + yield return prop; + } + } + } + + public static bool IsPropertyModified(this DbEntityEntry entry, IDbContext ctx, string propertyName) + { + object originalValue; + return TryGetModifiedProperty(entry, ctx, propertyName, out originalValue); + } + + public static bool TryGetModifiedProperty(this DbEntityEntry entry, IDbContext ctx, string propertyName, out object originalValue) + { + Guard.NotEmpty(propertyName, nameof(propertyName)); + + EnsureChangesDetected(entry, ctx); + + originalValue = null; + + if (entry.State != EfState.Modified) + { + return false; + } + + var prop = entry.Property(propertyName); + if (prop != null && PropIsModified(prop, ctx)) + { + // INFO: under certain conditions DbPropertyEntry.IsModified returns true, even when values are equal + originalValue = prop.OriginalValue; + return true; + } + + return false; + } + + private static void EnsureChangesDetected(DbEntityEntry entry, IDbContext ctx) + { + var state = entry.State; + + if (ctx.AutoDetectChangesEnabled && state == EfState.Modified) + return; + + if ((ctx as ObjectContextBase)?.IsInSaveOperation == true) + return; + + if (state == EfState.Unchanged || state == EfState.Modified) + { + // When AutoDetectChanges is off we cannot be sure whether the entity is really unchanged, + // because no detection was performed to verify this. + DetectChangesInProperties(entry, ctx); + } + } + + public static void DetectChangesInProperties(this DbEntityEntry entry, IDbContext ctx) + { + ctx.DetectChanges(); + + #region Experimental + + //// ChangeDetection for single entity: calls DbEntityEntry.InternalEntry > ObjectStateEntry._stateEntry.DetectChangesInProperties(bool) + //var invoked = false; + + //try + //{ + // var internalEntry = FastProperty.GetProperty(entry.GetType(), "InternalEntry").GetValue(entry); + // if (internalEntry != null) + // { + // var objectStateEntry = FastProperty.GetProperty(internalEntry.GetType(), "ObjectStateEntry").GetValue(internalEntry); + // if (objectStateEntry != null) + // { + // var innerStateEntry = objectStateEntry.GetType().GetField("_stateEntry", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(objectStateEntry); + // if (innerStateEntry != null) + // { + // var dcMethod = innerStateEntry.GetType().GetMethod("DetectChangesInProperties", BindingFlags.NonPublic | BindingFlags.Instance); + // if (dcMethod != null) + // { + // //var requiresScalarChangeTracking = (bool)FastProperty.GetProperty(objectStateEntry.GetType(), "RequiresScalarChangeTracking").GetValue(objectStateEntry); + // //var requiresComplexChangeTracking = (bool)FastProperty.GetProperty(objectStateEntry.GetType(), "RequiresComplexChangeTracking").GetValue(objectStateEntry); + // dcMethod.Invoke(innerStateEntry, new object[] { false }); + // invoked = true; + // } + // } + // } + // } + //} + //finally + //{ + // if (!invoked) ctx.DetectChanges(); + //} + + #endregion + } + + private static bool PropIsModified(DbPropertyEntry prop, IDbContext ctx) + { + // INFO: "CurrentValue" cannot be used for entities in the Deleted state. + // INFO: "OriginalValues" cannot be used for entities in the Added state. + //return !AreEqual(prop.CurrentValue, prop.OriginalValue); + return ctx.AutoDetectChangesEnabled + ? prop.IsModified + : !AreEqual(prop.CurrentValue, prop.OriginalValue); + } + + private static bool AreEqual(object cur, object orig) + { + if (cur == null && orig == null) + return true; + + return orig != null + ? orig.Equals(cur) + : cur.Equals(orig); + } + } } diff --git a/src/Libraries/SmartStore.Data/Extensions/ExceptionExtensions.cs b/src/Libraries/SmartStore.Data/Extensions/ExceptionExtensions.cs index a0159326b1..6f685642a4 100644 --- a/src/Libraries/SmartStore.Data/Extensions/ExceptionExtensions.cs +++ b/src/Libraries/SmartStore.Data/Extensions/ExceptionExtensions.cs @@ -5,75 +5,75 @@ namespace SmartStore { - public static class ExceptionExtensions - { - /// - /// Checks whether the inner exception indicates uniqueness violation - /// (is 2627 = Unique constraint error, OR is 547 = Constraint check violation, OR is 2601 = Duplicated key row error) - /// - /// The exception wrapper - /// - public static bool IsUniquenessViolationException(this DbUpdateException exception) - { - var sqlException = exception?.InnerException as SqlException; + public static class ExceptionExtensions + { + /// + /// Checks whether the inner exception indicates uniqueness violation + /// (is 2627 = Unique constraint error, OR is 547 = Constraint check violation, OR is 2601 = Duplicated key row error) + /// + /// The exception wrapper + /// + public static bool IsUniquenessViolationException(this DbUpdateException exception) + { + var sqlException = exception?.InnerException as SqlException; - if (sqlException == null) - sqlException = exception?.InnerException?.InnerException as SqlException; + if (sqlException == null) + sqlException = exception?.InnerException?.InnerException as SqlException; - if (sqlException == null) - return false; + if (sqlException == null) + return false; - switch (sqlException.Number) - { - case 2627: // Unique constraint error - case 547: // Constraint check violation - case 2601: // Duplicated key row error - return true; - default: - return false; - } - } + switch (sqlException.Number) + { + case 2627: // Unique constraint error + case 547: // Constraint check violation + case 2601: // Duplicated key row error + return true; + default: + return false; + } + } - /// - /// Checks whether the inner exception indicates a deadlock error - /// - /// The exception wrapper - /// - public static bool IsDeadlockException(this EntityCommandExecutionException exception) - { - var sqlException = exception?.InnerException as SqlException; + /// + /// Checks whether the inner exception indicates a deadlock error + /// + /// The exception wrapper + /// + public static bool IsDeadlockException(this EntityCommandExecutionException exception) + { + var sqlException = exception?.InnerException as SqlException; - if (sqlException == null) - sqlException = exception?.InnerException?.InnerException as SqlException; + if (sqlException == null) + sqlException = exception?.InnerException?.InnerException as SqlException; - if (sqlException == null) - return false; + if (sqlException == null) + return false; - return sqlException.IsDeadlockException(); - } + return sqlException.IsDeadlockException(); + } - /// - /// Checks whether the exception indicates a deadlock error (1205) - /// - /// The exception wrapper - /// - public static bool IsDeadlockException(this SqlException exception) - { - if (exception == null) - return false; + /// + /// Checks whether the exception indicates a deadlock error (1205) + /// + /// The exception wrapper + /// + public static bool IsDeadlockException(this SqlException exception) + { + if (exception == null) + return false; - return exception.Number == 1205; - } + return exception.Number == 1205; + } - /// - /// Checks whether the exception indicates attaching an already attached entity (HResult -2146233079) - /// "Attaching an entity of type 'x' failed because another entity of the same type already has the same primary key value." - /// - /// Invalid operation exception - /// - public static bool IsAlreadyAttachedEntityException(this InvalidOperationException exception) - { - return exception != null && exception.HResult == -2146233079; - } - } + /// + /// Checks whether the exception indicates attaching an already attached entity (HResult -2146233079) + /// "Attaching an entity of type 'x' failed because another entity of the same type already has the same primary key value." + /// + /// Invalid operation exception + /// + public static bool IsAlreadyAttachedEntityException(this InvalidOperationException exception) + { + return exception != null && exception.HResult == -2146233079; + } + } } diff --git a/src/Libraries/SmartStore.Data/Extensions/IDbContextExtensions.cs b/src/Libraries/SmartStore.Data/Extensions/IDbContextExtensions.cs index 3cce9d4032..68b043a6a5 100644 --- a/src/Libraries/SmartStore.Data/Extensions/IDbContextExtensions.cs +++ b/src/Libraries/SmartStore.Data/Extensions/IDbContextExtensions.cs @@ -10,21 +10,20 @@ using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Data; -using EfState = System.Data.Entity.EntityState; -namespace SmartStore +namespace SmartStore { - public static class IDbContextExtensions - { - /// + public static class IDbContextExtensions + { + /// /// Loads the database copy. /// /// /// The context. /// The current copy. /// - public static T LoadDatabaseCopy(this IDbContext context, T currentCopy) where T : BaseEntity - { + public static T LoadDatabaseCopy(this IDbContext context, T currentCopy) where T : BaseEntity + { return InnerGetCopy(context, currentCopy, e => e.GetDatabaseValues()); } @@ -35,152 +34,152 @@ public static T LoadDatabaseCopy(this IDbContext context, T currentCopy) wher /// The context. /// The current copy. /// - public static T LoadOriginalCopy(this IDbContext context, T currentCopy) where T : BaseEntity - { + public static T LoadOriginalCopy(this IDbContext context, T currentCopy) where T : BaseEntity + { return InnerGetCopy(context, currentCopy, e => e.OriginalValues); } - public static DbEntityEntry GetEntry(this IDbContext context, T entity) where T : BaseEntity - { - var entry = CastOrThrow(context).Entry(entity); - return entry; - } - - public static bool IsPropertyModified(this IDbContext ctx, T entity, Expression> propertySelector) - where T : BaseEntity - { - object originalValue; - return TryGetModifiedProperty(ctx, entity, propertySelector, out originalValue); - } - - public static bool TryGetModifiedProperty(this IDbContext ctx, T entity, Expression> propertySelector, out object originalValue) - where T : BaseEntity - { - Guard.NotNull(entity, nameof(entity)); - Guard.NotNull(propertySelector, nameof(propertySelector)); - - var propertyName = propertySelector.ExtractMemberInfo().Name; - return ctx.TryGetModifiedProperty(entity, propertyName, out originalValue); - } - - /// - /// Executes the DBCC SHRINKDATABASE(0) command against the SQL Server (Express) database - /// - /// The context - /// true, when the operation completed successfully. - public static bool ShrinkDatabase(this IDbContext context) - { - if (DataSettings.Current.IsSqlServer) - { - try - { - context.ExecuteSqlCommand("DBCC SHRINKDATABASE(0)", true); - return true; - } - catch { } - } - - return false; - } - - /// - /// Executes sql by using SQL-Server Management Objects which supports GO statements. - /// - public static int ExecuteSqlThroughSmo(this IDbContext ctx, string sql) - { - Guard.NotEmpty(sql, "sql"); - - int result = 0; - - try - { - bool isSqlServer = DataSettings.Current.IsSqlServer; - - if (!isSqlServer) - { - result = ctx.ExecuteSqlCommand(sql); - } - else - { - using (var sqlConnection = new SqlConnection(ObjectContextBase.GetConnectionString())) - { - var serverConnection = new ServerConnection(sqlConnection); - var server = new Server(serverConnection); - - result = server.ConnectionContext.ExecuteNonQuery(sql); - } - } - } - catch (Exception) - { - // remove the GO statements - sql = Regex.Replace(sql, @"\r{0,1}\n[Gg][Oo]\r{0,1}\n", "\n"); - - result = ctx.ExecuteSqlCommand(sql); - } - return result; - } - - #region Utils - - private static bool IsInSaveOperation(this IDbContext context) - { - return (context as ObjectContextBase)?.IsInSaveOperation == true; - } - - private static T InnerGetCopy(IDbContext context, T currentCopy, Func, DbPropertyValues> func) where T : BaseEntity - { - // Get the database context - var dbContext = CastOrThrow(context); - - // Get the entity tracking object - DbEntityEntry entry = GetEntityOrDefault(currentCopy, dbContext); - - // The output - T output = null; - - // Try and get the values - if (entry != null) - { - DbPropertyValues dbPropertyValues = func(entry); - if (dbPropertyValues != null) - { - output = dbPropertyValues.ToObject() as T; - } - } - - return output; - } - - /// - /// Gets the entity or return null. - /// - /// - /// The current copy. - /// The db context. - /// - private static DbEntityEntry GetEntityOrDefault(T currentCopy, DbContext dbContext) where T : BaseEntity - { - return dbContext.ChangeTracker.Entries().Where(e => e.Entity == currentCopy).FirstOrDefault(); - } - - private static DbContext CastOrThrow(IDbContext context) - { - return CastOrThrow(context); - } - - private static T CastOrThrow(IDbContext context) where T : DbContext - { - var dbContext = (context as T); - - if (dbContext == null) - { - throw new InvalidOperationException("Context does not support operation."); - } - - return dbContext; - } - - #endregion - } + public static DbEntityEntry GetEntry(this IDbContext context, T entity) where T : BaseEntity + { + var entry = CastOrThrow(context).Entry(entity); + return entry; + } + + public static bool IsPropertyModified(this IDbContext ctx, T entity, Expression> propertySelector) + where T : BaseEntity + { + object originalValue; + return TryGetModifiedProperty(ctx, entity, propertySelector, out originalValue); + } + + public static bool TryGetModifiedProperty(this IDbContext ctx, T entity, Expression> propertySelector, out object originalValue) + where T : BaseEntity + { + Guard.NotNull(entity, nameof(entity)); + Guard.NotNull(propertySelector, nameof(propertySelector)); + + var propertyName = propertySelector.ExtractMemberInfo().Name; + return ctx.TryGetModifiedProperty(entity, propertyName, out originalValue); + } + + /// + /// Executes the DBCC SHRINKDATABASE(0) command against the SQL Server (Express) database + /// + /// The context + /// true, when the operation completed successfully. + public static bool ShrinkDatabase(this IDbContext context) + { + if (DataSettings.Current.IsSqlServer) + { + try + { + context.ExecuteSqlCommand("DBCC SHRINKDATABASE(0)", true); + return true; + } + catch { } + } + + return false; + } + + /// + /// Executes sql by using SQL-Server Management Objects which supports GO statements. + /// + public static int ExecuteSqlThroughSmo(this IDbContext ctx, string sql) + { + Guard.NotEmpty(sql, "sql"); + + int result = 0; + + try + { + bool isSqlServer = DataSettings.Current.IsSqlServer; + + if (!isSqlServer) + { + result = ctx.ExecuteSqlCommand(sql); + } + else + { + using (var sqlConnection = new SqlConnection(ObjectContextBase.GetConnectionString())) + { + var serverConnection = new ServerConnection(sqlConnection); + var server = new Server(serverConnection); + + result = server.ConnectionContext.ExecuteNonQuery(sql); + } + } + } + catch (Exception) + { + // remove the GO statements + sql = Regex.Replace(sql, @"\r{0,1}\n[Gg][Oo]\r{0,1}\n", "\n"); + + result = ctx.ExecuteSqlCommand(sql); + } + return result; + } + + #region Utils + + private static bool IsInSaveOperation(this IDbContext context) + { + return (context as ObjectContextBase)?.IsInSaveOperation == true; + } + + private static T InnerGetCopy(IDbContext context, T currentCopy, Func, DbPropertyValues> func) where T : BaseEntity + { + // Get the database context + var dbContext = CastOrThrow(context); + + // Get the entity tracking object + DbEntityEntry entry = GetEntityOrDefault(currentCopy, dbContext); + + // The output + T output = null; + + // Try and get the values + if (entry != null) + { + DbPropertyValues dbPropertyValues = func(entry); + if (dbPropertyValues != null) + { + output = dbPropertyValues.ToObject() as T; + } + } + + return output; + } + + /// + /// Gets the entity or return null. + /// + /// + /// The current copy. + /// The db context. + /// + private static DbEntityEntry GetEntityOrDefault(T currentCopy, DbContext dbContext) where T : BaseEntity + { + return dbContext.ChangeTracker.Entries().Where(e => e.Entity == currentCopy).FirstOrDefault(); + } + + private static DbContext CastOrThrow(IDbContext context) + { + return CastOrThrow(context); + } + + private static T CastOrThrow(IDbContext context) where T : DbContext + { + var dbContext = (context as T); + + if (dbContext == null) + { + throw new InvalidOperationException("Context does not support operation."); + } + + return dbContext; + } + + #endregion + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Extensions/MiscExtensions.cs b/src/Libraries/SmartStore.Data/Extensions/MiscExtensions.cs index 5feec4be90..2756eb5a5e 100644 --- a/src/Libraries/SmartStore.Data/Extensions/MiscExtensions.cs +++ b/src/Libraries/SmartStore.Data/Extensions/MiscExtensions.cs @@ -2,11 +2,11 @@ namespace SmartStore { - public static class MiscExtensions - { - public static bool IsEntityFrameworkProvider(this IQueryProvider provider) - { - return provider.GetType().FullName == "System.Data.Objects.ELinq.ObjectQueryProvider"; - } - } + public static class MiscExtensions + { + public static bool IsEntityFrameworkProvider(this IQueryProvider provider) + { + return provider.GetType().FullName == "System.Data.Objects.ELinq.ObjectQueryProvider"; + } + } } diff --git a/src/Libraries/SmartStore.Data/IEfDataProvider.cs b/src/Libraries/SmartStore.Data/IEfDataProvider.cs index c0ebf1a63b..f6fc6e8cc7 100644 --- a/src/Libraries/SmartStore.Data/IEfDataProvider.cs +++ b/src/Libraries/SmartStore.Data/IEfDataProvider.cs @@ -1,5 +1,4 @@ -using System.Data.Entity; -using System.Data.Entity.Infrastructure; +using System.Data.Entity.Infrastructure; using SmartStore.Core.Data; namespace SmartStore.Data diff --git a/src/Libraries/SmartStore.Data/Mapping/Blogs/BlogCommentMap.cs b/src/Libraries/SmartStore.Data/Mapping/Blogs/BlogCommentMap.cs index 1372394cee..99f73ff900 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Blogs/BlogCommentMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Blogs/BlogCommentMap.cs @@ -11,7 +11,7 @@ public BlogCommentMap() //commented because it's already configured by CustomerContentMap class //this.HasKey(pr => pr.Id); - this.Property(bc => bc.CommentText).IsMaxLength(); + this.Property(bc => bc.CommentText).IsMaxLength(); this.HasRequired(bc => bc.BlogPost) .WithMany(bp => bp.BlogComments) .HasForeignKey(bc => bc.BlogPostId); diff --git a/src/Libraries/SmartStore.Data/Mapping/Blogs/BlogPostMap.cs b/src/Libraries/SmartStore.Data/Mapping/Blogs/BlogPostMap.cs index f4b9a491db..bca23be8d1 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Blogs/BlogPostMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Blogs/BlogPostMap.cs @@ -7,16 +7,29 @@ public partial class BlogPostMap : EntityTypeConfiguration { public BlogPostMap() { - this.ToTable("BlogPost"); - this.HasKey(bp => bp.Id); - this.Property(bp => bp.Title).IsRequired(); - this.Property(bp => bp.Body).IsRequired().IsMaxLength(); - this.Property(bp => bp.MetaKeywords).HasMaxLength(400); - this.Property(bp => bp.MetaTitle).HasMaxLength(400); + ToTable("BlogPost"); + HasKey(bp => bp.Id); + Property(bp => bp.Title).IsRequired(); + Property(bp => bp.Body).IsRequired().IsMaxLength(); + Property(bp => bp.MetaKeywords).HasMaxLength(400); + Property(bp => bp.MetaTitle).HasMaxLength(400); + Property(bp => bp.MediaFileId).HasColumnName("MediaFileId"); + Property(bp => bp.PreviewMediaFileId).HasColumnName("PreviewMediaFileId"); - this.HasRequired(bp => bp.Language) + HasOptional(bp => bp.Language) .WithMany() - .HasForeignKey(bp => bp.LanguageId).WillCascadeOnDelete(true); + .HasForeignKey(bp => bp.LanguageId) + .WillCascadeOnDelete(false); + + HasOptional(bp => bp.MediaFile) + .WithMany() + .HasForeignKey(bp => bp.MediaFileId) + .WillCascadeOnDelete(false); + + HasOptional(bp => bp.PreviewMediaFile) + .WithMany() + .HasForeignKey(bp => bp.PreviewMediaFileId) + .WillCascadeOnDelete(false); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/BackInStockSubscriptionMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/BackInStockSubscriptionMap.cs index bcf2e9e4dc..3767cac539 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/BackInStockSubscriptionMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/BackInStockSubscriptionMap.cs @@ -9,12 +9,12 @@ public BackInStockSubscriptionMap() { this.ToTable("BackInStockSubscription"); this.HasKey(x => x.Id); - + this.HasRequired(x => x.Product) .WithMany() .HasForeignKey(x => x.ProductId) .WillCascadeOnDelete(true); - + this.HasRequired(x => x.Customer) .WithMany() .HasForeignKey(x => x.CustomerId) diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/CategoryMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/CategoryMap.cs index c69a6f88d4..21705b06ae 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/CategoryMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/CategoryMap.cs @@ -7,21 +7,27 @@ public partial class CategoryMap : EntityTypeConfiguration { public CategoryMap() { - this.ToTable("Category"); - this.HasKey(c => c.Id); - this.Property(c => c.Name).IsRequired().HasMaxLength(400); - this.Property(c => c.FullName).HasMaxLength(400); - this.Property(c => c.BottomDescription).IsMaxLength(); - this.Property(c => c.ExternalLink).HasMaxLength(255).IsOptional(); - this.Property(c => c.Description).IsMaxLength(); - this.Property(c => c.MetaKeywords).HasMaxLength(400); - this.Property(c => c.MetaTitle).HasMaxLength(400); - this.Property(c => c.PageSizeOptions).HasMaxLength(200).IsOptional(); - this.Property(c => c.Alias).HasMaxLength(100); - this.HasOptional(p => p.Picture) - .WithMany() - .HasForeignKey(p => p.PictureId) - .WillCascadeOnDelete(false); + ToTable("Category"); + HasKey(c => c.Id); + Property(c => c.Name).IsRequired().HasMaxLength(400); + Property(c => c.FullName).HasMaxLength(400); + Property(c => c.BottomDescription).IsMaxLength(); + Property(c => c.ExternalLink).HasMaxLength(255).IsOptional(); + Property(c => c.Description).IsMaxLength(); + Property(c => c.MetaKeywords).HasMaxLength(400); + Property(c => c.MetaTitle).HasMaxLength(400); + Property(c => c.PageSizeOptions).HasMaxLength(200).IsOptional(); + Property(c => c.Alias).HasMaxLength(100); + Property(c => c.MediaFileId).HasColumnName("MediaFileId"); + + HasOptional(p => p.MediaFile) + .WithMany() + .HasForeignKey(p => p.MediaFileId) + .WillCascadeOnDelete(false); + + HasMany(d => d.RuleSets) + .WithMany(rs => rs.Categories) + .Map(m => m.ToTable("RuleSet_Category_Mapping")); } } } diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ManufacturerMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ManufacturerMap.cs index d7e22fbf22..f74f5cd16d 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/ManufacturerMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/ManufacturerMap.cs @@ -7,17 +7,20 @@ public partial class ManufacturerMap : EntityTypeConfiguration { public ManufacturerMap() { - this.ToTable("Manufacturer"); - this.HasKey(m => m.Id); - this.Property(m => m.Name).IsRequired().HasMaxLength(400); - this.Property(m => m.Description).IsMaxLength(); - this.Property(m => m.MetaKeywords).HasMaxLength(400); - this.Property(m => m.MetaTitle).HasMaxLength(400); - this.Property(m => m.PageSizeOptions).HasMaxLength(200).IsOptional(); - this.HasOptional(p => p.Picture) - .WithMany() - .HasForeignKey(p => p.PictureId) - .WillCascadeOnDelete(false); + ToTable("Manufacturer"); + HasKey(m => m.Id); + Property(m => m.Name).IsRequired().HasMaxLength(400); + Property(m => m.Description).IsMaxLength(); + Property(c => c.BottomDescription).IsMaxLength(); + Property(m => m.MetaKeywords).HasMaxLength(400); + Property(m => m.MetaTitle).HasMaxLength(400); + Property(m => m.PageSizeOptions).HasMaxLength(200).IsOptional(); + Property(m => m.MediaFileId).HasColumnName("MediaFileId"); + + HasOptional(p => p.MediaFile) + .WithMany() + .HasForeignKey(p => p.MediaFileId) + .WillCascadeOnDelete(false); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductAttributeMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductAttributeMap.cs index 466c56ebd7..fafd737864 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductAttributeMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductAttributeMap.cs @@ -11,7 +11,7 @@ public ProductAttributeMap() HasKey(pa => pa.Id); Property(pa => pa.Alias).HasMaxLength(100); Property(pa => pa.Name).IsRequired(); - Property(pa => pa.ExportMappings).IsMaxLength(); - } + Property(pa => pa.ExportMappings).IsMaxLength(); + } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductAttributeOptionMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductAttributeOptionMap.cs index 7f2207bc9e..5580f04f00 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductAttributeOptionMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductAttributeOptionMap.cs @@ -3,24 +3,25 @@ namespace SmartStore.Data.Mapping.Catalog { - public partial class ProductAttributeOptionMap : EntityTypeConfiguration - { - public ProductAttributeOptionMap() - { - ToTable("ProductAttributeOption"); - HasKey(x => x.Id); - Property(x => x.Name).HasMaxLength(4000); - Property(x => x.Alias).HasMaxLength(100); - Property(x => x.Color).HasMaxLength(100); + public partial class ProductAttributeOptionMap : EntityTypeConfiguration + { + public ProductAttributeOptionMap() + { + ToTable("ProductAttributeOption"); + HasKey(x => x.Id); + Property(x => x.Name).HasMaxLength(4000); + Property(x => x.Alias).HasMaxLength(100); + Property(x => x.Color).HasMaxLength(100); + Property(x => x.MediaFileId).HasColumnName("MediaFileId"); - Property(x => x.PriceAdjustment).HasPrecision(18, 4); - Property(x => x.WeightAdjustment).HasPrecision(18, 4); + Property(x => x.PriceAdjustment).HasPrecision(18, 4); + Property(x => x.WeightAdjustment).HasPrecision(18, 4); - Ignore(x => x.ValueType); + Ignore(x => x.ValueType); - HasRequired(x => x.ProductAttributeOptionsSet) - .WithMany(x => x.ProductAttributeOptions) - .HasForeignKey(x => x.ProductAttributeOptionsSetId); - } - } + HasRequired(x => x.ProductAttributeOptionsSet) + .WithMany(x => x.ProductAttributeOptions) + .HasForeignKey(x => x.ProductAttributeOptionsSetId); + } + } } diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductAttributeOptionsSetMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductAttributeOptionsSetMap.cs index e01b2224dc..e33b6081a5 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductAttributeOptionsSetMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductAttributeOptionsSetMap.cs @@ -3,17 +3,17 @@ namespace SmartStore.Data.Mapping.Catalog { - public partial class ProductAttributeOptionsSetMap : EntityTypeConfiguration - { - public ProductAttributeOptionsSetMap() - { - ToTable("ProductAttributeOptionsSet"); - HasKey(x => x.Id); - Property(x => x.Name).HasMaxLength(400); + public partial class ProductAttributeOptionsSetMap : EntityTypeConfiguration + { + public ProductAttributeOptionsSetMap() + { + ToTable("ProductAttributeOptionsSet"); + HasKey(x => x.Id); + Property(x => x.Name).HasMaxLength(400); - HasRequired(x => x.ProductAttribute) - .WithMany(x => x.ProductAttributeOptionsSets) - .HasForeignKey(x => x.ProductAttributeId); - } - } + HasRequired(x => x.ProductAttribute) + .WithMany(x => x.ProductAttributeOptionsSets) + .HasForeignKey(x => x.ProductAttributeId); + } + } } diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductBundleItemAttributeFilterMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductBundleItemAttributeFilterMap.cs index f7297412db..7da5ef6420 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductBundleItemAttributeFilterMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductBundleItemAttributeFilterMap.cs @@ -3,17 +3,17 @@ namespace SmartStore.Data.Mapping.Catalog { - public partial class ProductBundleItemAttributeFilterMap : EntityTypeConfiguration - { - public ProductBundleItemAttributeFilterMap() - { - this.ToTable("ProductBundleItemAttributeFilter"); - this.HasKey(biaf => biaf.Id); + public partial class ProductBundleItemAttributeFilterMap : EntityTypeConfiguration + { + public ProductBundleItemAttributeFilterMap() + { + this.ToTable("ProductBundleItemAttributeFilter"); + this.HasKey(biaf => biaf.Id); - this.HasRequired(biaf => biaf.BundleItem) - .WithMany(pbi => pbi.AttributeFilters) - .HasForeignKey(biaf => biaf.BundleItemId) - .WillCascadeOnDelete(true); - } - } + this.HasRequired(biaf => biaf.BundleItem) + .WithMany(pbi => pbi.AttributeFilters) + .HasForeignKey(biaf => biaf.BundleItemId) + .WillCascadeOnDelete(true); + } + } } diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductBundleItemMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductBundleItemMap.cs index 262883235b..0e4022315c 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductBundleItemMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductBundleItemMap.cs @@ -3,26 +3,26 @@ namespace SmartStore.Data.Mapping.Catalog { - public partial class ProductBundleItemMap : EntityTypeConfiguration - { - public ProductBundleItemMap() - { - this.ToTable("ProductBundleItem"); - this.HasKey(pbi => pbi.Id); + public partial class ProductBundleItemMap : EntityTypeConfiguration + { + public ProductBundleItemMap() + { + this.ToTable("ProductBundleItem"); + this.HasKey(pbi => pbi.Id); - this.Property(pbi => pbi.Discount).HasPrecision(18, 4).IsOptional(); - this.Property(pbi => pbi.Name).HasMaxLength(400); - this.Property(pbi => pbi.ShortDescription).IsMaxLength(); - - this.HasRequired(pbi => pbi.Product) - .WithMany() - .HasForeignKey(pbi => pbi.ProductId) - .WillCascadeOnDelete(false); // SQL Server does not support multiple cascade deletes + this.Property(pbi => pbi.Discount).HasPrecision(18, 4).IsOptional(); + this.Property(pbi => pbi.Name).HasMaxLength(400); + this.Property(pbi => pbi.ShortDescription).IsMaxLength(); - this.HasRequired(pbi => pbi.BundleProduct) - .WithMany(p => p.ProductBundleItems) - .HasForeignKey(pbi => pbi.BundleProductId) - .WillCascadeOnDelete(true); - } - } + this.HasRequired(pbi => pbi.Product) + .WithMany() + .HasForeignKey(pbi => pbi.ProductId) + .WillCascadeOnDelete(false); // SQL Server does not support multiple cascade deletes + + this.HasRequired(pbi => pbi.BundleProduct) + .WithMany(p => p.ProductBundleItems) + .HasForeignKey(pbi => pbi.BundleProductId) + .WillCascadeOnDelete(true); + } + } } diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductCategoryMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductCategoryMap.cs index e152d7c5ca..d4d26e1ff8 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductCategoryMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductCategoryMap.cs @@ -9,7 +9,7 @@ public ProductCategoryMap() { this.ToTable("Product_Category_Mapping"); this.HasKey(pc => pc.Id); - + this.HasRequired(pc => pc.Category) .WithMany() .HasForeignKey(pc => pc.CategoryId); diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductManufacturerMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductManufacturerMap.cs index bb903e023e..a707bed719 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductManufacturerMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductManufacturerMap.cs @@ -9,7 +9,7 @@ public ProductManufacturerMap() { this.ToTable("Product_Manufacturer_Mapping"); this.HasKey(pm => pm.Id); - + this.HasRequired(pm => pm.Manufacturer) .WithMany() .HasForeignKey(pm => pm.ManufacturerId); diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductMap.cs index 042db6e7f2..387c86a13a 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductMap.cs @@ -14,66 +14,66 @@ public ProductMap() this.Property(p => p.MetaTitle).HasMaxLength(400); this.Property(p => p.FullDescription).IsMaxLength(); - this.Property(p => p.Sku).HasMaxLength(400); - this.Property(p => p.ManufacturerPartNumber).HasMaxLength(400); - this.Property(p => p.Gtin).HasMaxLength(400); - this.Property(p => p.AdditionalShippingCharge).HasPrecision(18, 4); - this.Property(p => p.Price).HasPrecision(18, 4); - this.Property(p => p.OldPrice).HasPrecision(18, 4); - this.Property(p => p.ProductCost).HasPrecision(18, 4); - this.Property(p => p.SpecialPrice).HasPrecision(18, 4); - this.Property(p => p.MinimumCustomerEnteredPrice).HasPrecision(18, 4); - this.Property(p => p.MaximumCustomerEnteredPrice).HasPrecision(18, 4); - this.Property(p => p.Weight).HasPrecision(18, 4); - this.Property(p => p.Length).HasPrecision(18, 4); - this.Property(p => p.Width).HasPrecision(18, 4); - this.Property(p => p.Height).HasPrecision(18, 4); - this.Property(p => p.LowestAttributeCombinationPrice).HasPrecision(18, 4); - this.Property(p => p.RequiredProductIds).HasMaxLength(1000); - this.Property(p => p.AllowedQuantities).HasMaxLength(1000); - this.Property(p => p.CustomsTariffNumber).HasMaxLength(30); - this.Property(p => p.SystemName).HasMaxLength(400); + this.Property(p => p.Sku).HasMaxLength(400); + this.Property(p => p.ManufacturerPartNumber).HasMaxLength(400); + this.Property(p => p.Gtin).HasMaxLength(400); + this.Property(p => p.AdditionalShippingCharge).HasPrecision(18, 4); + this.Property(p => p.Price).HasPrecision(18, 4); + this.Property(p => p.OldPrice).HasPrecision(18, 4); + this.Property(p => p.ProductCost).HasPrecision(18, 4); + this.Property(p => p.SpecialPrice).HasPrecision(18, 4); + this.Property(p => p.MinimumCustomerEnteredPrice).HasPrecision(18, 4); + this.Property(p => p.MaximumCustomerEnteredPrice).HasPrecision(18, 4); + this.Property(p => p.Weight).HasPrecision(18, 4); + this.Property(p => p.Length).HasPrecision(18, 4); + this.Property(p => p.Width).HasPrecision(18, 4); + this.Property(p => p.Height).HasPrecision(18, 4); + this.Property(p => p.LowestAttributeCombinationPrice).HasPrecision(18, 4); + this.Property(p => p.RequiredProductIds).HasMaxLength(1000); + this.Property(p => p.AllowedQuantities).HasMaxLength(1000); + this.Property(p => p.CustomsTariffNumber).HasMaxLength(30); + this.Property(p => p.SystemName).HasMaxLength(400); - this.HasOptional(p => p.DeliveryTime) - .WithMany() - .HasForeignKey(p => p.DeliveryTimeId) - .WillCascadeOnDelete(false); + this.HasOptional(p => p.DeliveryTime) + .WithMany() + .HasForeignKey(p => p.DeliveryTimeId) + .WillCascadeOnDelete(false); this.HasOptional(p => p.QuantityUnit) .WithMany() .HasForeignKey(p => p.QuantityUnitId) .WillCascadeOnDelete(false); - this.HasOptional(p => p.SampleDownload) - .WithMany() - .HasForeignKey(p => p.SampleDownloadId) - .WillCascadeOnDelete(false); + this.HasOptional(p => p.SampleDownload) + .WithMany() + .HasForeignKey(p => p.SampleDownloadId) + .WillCascadeOnDelete(false); - this.HasOptional(p => p.CountryOfOrigin) - .WithMany() - .HasForeignKey(p => p.CountryOfOriginId) - .WillCascadeOnDelete(false); + this.HasOptional(p => p.CountryOfOrigin) + .WithMany() + .HasForeignKey(p => p.CountryOfOriginId) + .WillCascadeOnDelete(false); - this.Ignore(p => p.ProductType); - this.Ignore(p => p.ProductTypeLabelHint); - this.Ignore(p => p.BackorderMode); - this.Ignore(p => p.DownloadActivationType); - this.Ignore(p => p.GiftCardType); - this.Ignore(p => p.LowStockActivity); - this.Ignore(p => p.ManageInventoryMethod); - this.Ignore(p => p.RecurringCyclePeriod); - this.Ignore(p => p.MergedDataIgnore); - this.Ignore(p => p.MergedDataValues); + this.Ignore(p => p.ProductType); + this.Ignore(p => p.ProductTypeLabelHint); + this.Ignore(p => p.BackorderMode); + this.Ignore(p => p.DownloadActivationType); + this.Ignore(p => p.GiftCardType); + this.Ignore(p => p.LowStockActivity); + this.Ignore(p => p.ManageInventoryMethod); + this.Ignore(p => p.RecurringCyclePeriod); + this.Ignore(p => p.MergedDataIgnore); + this.Ignore(p => p.MergedDataValues); - this.Property(p => p.BasePriceMeasureUnit).HasMaxLength(50); - this.Property(p => p.BasePriceAmount).HasPrecision(18, 4).IsOptional(); - this.Ignore(p => p.BasePriceHasValue); + this.Property(p => p.BasePriceMeasureUnit).HasMaxLength(50); + this.Property(p => p.BasePriceAmount).HasPrecision(18, 4).IsOptional(); + this.Ignore(p => p.BasePriceHasValue); - this.Property(p => p.BundleTitleText).HasMaxLength(400); + this.Property(p => p.BundleTitleText).HasMaxLength(400); - this.HasMany(p => p.ProductTags) - .WithMany(pt => pt.Products) - .Map(m => m.ToTable("Product_ProductTag_Mapping")); + this.HasMany(p => p.ProductTags) + .WithMany(pt => pt.Products) + .Map(m => m.ToTable("Product_ProductTag_Mapping")); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductMediaFileMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductMediaFileMap.cs new file mode 100644 index 0000000000..7996718c6c --- /dev/null +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductMediaFileMap.cs @@ -0,0 +1,25 @@ +using System.Data.Entity.ModelConfiguration; +using SmartStore.Core.Domain.Catalog; + +namespace SmartStore.Data.Mapping.Catalog +{ + public partial class ProductMediaFileMap : EntityTypeConfiguration + { + public ProductMediaFileMap() + { + ToTable("Product_MediaFile_Mapping"); + HasKey(pp => pp.Id); + Property(pp => pp.MediaFileId).HasColumnName("MediaFileId"); + + HasRequired(pp => pp.MediaFile) + .WithMany(p => p.ProductMediaFiles) + .HasForeignKey(pp => pp.MediaFileId) + .WillCascadeOnDelete(true); + + HasRequired(pp => pp.Product) + .WithMany(p => p.ProductPictures) + .HasForeignKey(pp => pp.ProductId) + .WillCascadeOnDelete(true); + } + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductPictureMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductPictureMap.cs deleted file mode 100644 index 5a2853b445..0000000000 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductPictureMap.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Data.Entity.ModelConfiguration; -using SmartStore.Core.Domain.Catalog; - -namespace SmartStore.Data.Mapping.Catalog -{ - public partial class ProductPictureMap : EntityTypeConfiguration - { - public ProductPictureMap() - { - this.ToTable("Product_Picture_Mapping"); - this.HasKey(pp => pp.Id); - - this.HasRequired(pp => pp.Picture) - .WithMany(p => p.ProductPictures) - .HasForeignKey(pp => pp.PictureId); - - - this.HasRequired(pp => pp.Product) - .WithMany(p => p.ProductPictures) - .HasForeignKey(pp => pp.ProductId); - } - } -} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductReviewMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductReviewMap.cs index 4376c5d6aa..ce61d806b7 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductReviewMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductReviewMap.cs @@ -10,7 +10,7 @@ public ProductReviewMap() this.ToTable("ProductReview"); //commented because it's already configured by CustomerContentMap class //this.HasKey(pr => pr.Id); - this.Property(m => m.ReviewText).IsMaxLength(); + this.Property(m => m.ReviewText).IsMaxLength(); this.HasRequired(pr => pr.Product) .WithMany(p => p.ProductReviews) .HasForeignKey(pr => pr.ProductId); diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductVariantAttributeCombinationMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductVariantAttributeCombinationMap.cs index e788d2ea1b..7e91dbe2a0 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductVariantAttributeCombinationMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductVariantAttributeCombinationMap.cs @@ -7,28 +7,28 @@ public partial class ProductVariantAttributeCombinationMap : EntityTypeConfigura { public ProductVariantAttributeCombinationMap() { - this.ToTable("ProductVariantAttributeCombination"); - this.HasKey(pvac => pvac.Id); + ToTable("ProductVariantAttributeCombination"); + HasKey(pvac => pvac.Id); - this.Property(pvac => pvac.Sku).HasMaxLength(400); - this.Property(pvac => pvac.ManufacturerPartNumber).HasMaxLength(400); - this.Property(pvac => pvac.Gtin).HasMaxLength(400); - this.Property(pvac => pvac.Price).HasPrecision(18, 4); - this.Property(pvac => pvac.AssignedPictureIds).HasMaxLength(1000); - this.Property(pvac => pvac.Length).HasPrecision(18, 4); - this.Property(pvac => pvac.Width).HasPrecision(18, 4); - this.Property(pvac => pvac.Height).HasPrecision(18, 4); - this.Property(pvac => pvac.BasePriceAmount).HasPrecision(18, 4); + Property(pvac => pvac.Sku).HasMaxLength(400); + Property(pvac => pvac.ManufacturerPartNumber).HasMaxLength(400); + Property(pvac => pvac.Gtin).HasMaxLength(400); + Property(pvac => pvac.Price).HasPrecision(18, 4); + Property(pvac => pvac.AssignedMediaFileIds).HasMaxLength(1000).HasColumnName("AssignedMediaFileIds"); + Property(pvac => pvac.Length).HasPrecision(18, 4); + Property(pvac => pvac.Width).HasPrecision(18, 4); + Property(pvac => pvac.Height).HasPrecision(18, 4); + Property(pvac => pvac.BasePriceAmount).HasPrecision(18, 4); - this.HasRequired(pvac => pvac.Product) + HasRequired(pvac => pvac.Product) .WithMany(pvac => pvac.ProductVariantAttributeCombinations) .HasForeignKey(pvac => pvac.ProductId); - this.HasOptional(pvac => pvac.DeliveryTime) + HasOptional(pvac => pvac.DeliveryTime) .WithMany() .HasForeignKey(pvac => pvac.DeliveryTimeId) .WillCascadeOnDelete(false); - this.HasOptional(pvac => pvac.QuantityUnit) + HasOptional(pvac => pvac.QuantityUnit) .WithMany() .HasForeignKey(pvac => pvac.QuantityUnitId) .WillCascadeOnDelete(false); diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductVariantAttributeMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductVariantAttributeMap.cs index 6f8e7d9ba5..8bc170faa6 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductVariantAttributeMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductVariantAttributeMap.cs @@ -7,14 +7,14 @@ public partial class ProductVariantAttributeMap : EntityTypeConfiguration pva.Id); - this.Ignore(pva => pva.AttributeControlType); + this.Ignore(pva => pva.AttributeControlType); this.HasRequired(pva => pva.Product) .WithMany(pva => pva.ProductVariantAttributes) .HasForeignKey(pva => pva.ProductId); - + this.HasRequired(pva => pva.ProductAttribute) .WithMany() .HasForeignKey(pva => pva.ProductAttributeId); diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductVariantAttributeValueMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductVariantAttributeValueMap.cs index 91e67b30d8..5bc9a202ff 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductVariantAttributeValueMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/ProductVariantAttributeValueMap.cs @@ -7,18 +7,18 @@ public partial class ProductVariantAttributeValueMap : EntityTypeConfiguration

pvav.Id); - this.Property(pvav => pvav.Alias).HasMaxLength(100); - this.Property(pvav => pvav.Name); - this.Property(pvav => pvav.Color).HasMaxLength(100); + ToTable("ProductVariantAttributeValue"); + HasKey(pvav => pvav.Id); + Property(pvav => pvav.Alias).HasMaxLength(100); + Property(pvav => pvav.Name); + Property(pvav => pvav.Color).HasMaxLength(100); + Property(pvav => pvav.MediaFileId).HasColumnName("MediaFileId"); + Property(pvav => pvav.PriceAdjustment).HasPrecision(18, 4); + Property(pvav => pvav.WeightAdjustment).HasPrecision(18, 4); - this.Property(pvav => pvav.PriceAdjustment).HasPrecision(18, 4); - this.Property(pvav => pvav.WeightAdjustment).HasPrecision(18, 4); + Ignore(pvav => pvav.ValueType); - this.Ignore(pvav => pvav.ValueType); - - this.HasRequired(pvav => pvav.ProductVariantAttribute) + HasRequired(pvav => pvav.ProductVariantAttribute) .WithMany(pva => pva.ProductVariantAttributeValues) .HasForeignKey(pvav => pvav.ProductVariantAttributeId); } diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/SpecificationAttributeMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/SpecificationAttributeMap.cs index 21a0500f6e..ed57c5b86d 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/SpecificationAttributeMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/SpecificationAttributeMap.cs @@ -10,7 +10,7 @@ public SpecificationAttributeMap() this.ToTable("SpecificationAttribute"); this.HasKey(sa => sa.Id); this.Property(sa => sa.Name).IsRequired(); - this.Property(sa => sa.Alias).HasMaxLength(30); - } + this.Property(sa => sa.Alias).HasMaxLength(30); + } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Catalog/SpecificationAttributeOptionMap.cs b/src/Libraries/SmartStore.Data/Mapping/Catalog/SpecificationAttributeOptionMap.cs index fb45eb80f1..a7f7d5a2f5 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Catalog/SpecificationAttributeOptionMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Catalog/SpecificationAttributeOptionMap.cs @@ -3,18 +3,20 @@ namespace SmartStore.Data.Mapping.Catalog { - public partial class SpecificationAttributeOptionMap : EntityTypeConfiguration + public partial class SpecificationAttributeOptionMap : EntityTypeConfiguration { public SpecificationAttributeOptionMap() { - this.ToTable("SpecificationAttributeOption"); - this.HasKey(sao => sao.Id); - this.Property(sao => sao.Name).IsRequired(); - this.Property(sao => sao.Alias).HasMaxLength(30); + ToTable("SpecificationAttributeOption"); + HasKey(sao => sao.Id); + Property(sao => sao.Name).IsRequired(); + Property(sao => sao.Alias).HasMaxLength(30); + Property(sao => sao.Color).HasMaxLength(100); + Property(sao => sao.MediaFileId).HasColumnName("MediaFileId"); - Property(soa => soa.NumberValue).HasPrecision(18, 4); + Property(soa => soa.NumberValue).HasPrecision(18, 4); - this.HasRequired(sao => sao.SpecificationAttribute) + HasRequired(sao => sao.SpecificationAttribute) .WithMany(sa => sa.SpecificationAttributeOptions) .HasForeignKey(sao => sao.SpecificationAttributeId); } diff --git a/src/Libraries/SmartStore.Data/Mapping/Configuration/SettingMap.cs b/src/Libraries/SmartStore.Data/Mapping/Configuration/SettingMap.cs index 5ccbd5f5fa..0892840b65 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Configuration/SettingMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Configuration/SettingMap.cs @@ -7,10 +7,13 @@ public partial class SettingMap : EntityTypeConfiguration { public SettingMap() { - this.ToTable("Setting"); - this.HasKey(s => s.Id); - this.Property(s => s.Name).IsRequired().HasMaxLength(200); - this.Property(s => s.Value).IsRequired().IsMaxLength(); //.HasMaxLength(2000); + ToTable("Setting"); + HasKey(x => x.Id); + Property(x => x.Name).IsRequired().HasMaxLength(400); + Property(x => x.Value).IsRequired().IsMaxLength(); //.HasMaxLength(2000); + + HasIndex(x => x.Name).HasName("IX_Setting_Name"); + HasIndex(x => x.StoreId).HasName("IX_Setting_StoreId"); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Customers/CustomerMap.cs b/src/Libraries/SmartStore.Data/Mapping/Customers/CustomerMap.cs index 06582eade9..817228436a 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Customers/CustomerMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Customers/CustomerMap.cs @@ -8,34 +8,31 @@ public partial class CustomerMap : EntityTypeConfiguration { public CustomerMap() { - this.ToTable("Customer"); - this.HasKey(c => c.Id); - this.Property(u => u.Username).HasMaxLength(500); - this.Property(u => u.Email).HasMaxLength(500); - this.Property(u => u.SystemName).HasMaxLength(500); - this.Property(u => u.Password).HasMaxLength(500); - this.Property(u => u.PasswordSalt).HasMaxLength(500); - this.Property(u => u.LastIpAddress).HasMaxLength(100); + ToTable("Customer"); + HasKey(c => c.Id); + Property(u => u.Username).HasMaxLength(500); + Property(u => u.Email).HasMaxLength(500); + Property(u => u.SystemName).HasMaxLength(500); + Property(u => u.Password).HasMaxLength(500); + Property(u => u.PasswordSalt).HasMaxLength(500); + Property(u => u.LastIpAddress).HasMaxLength(100); - this.Property(u => u.Title).HasMaxLength(100); - this.Property(u => u.Salutation).HasMaxLength(50); - this.Property(u => u.FirstName).HasMaxLength(225); - this.Property(u => u.LastName).HasMaxLength(225); - this.Property(u => u.FullName).HasMaxLength(450); - this.Property(u => u.Company).HasMaxLength(255); - this.Property(u => u.CustomerNumber).HasMaxLength(100); + Property(u => u.Title).HasMaxLength(100); + Property(u => u.Salutation).HasMaxLength(50); + Property(u => u.FirstName).HasMaxLength(225); + Property(u => u.LastName).HasMaxLength(225); + Property(u => u.FullName).HasMaxLength(450); + Property(u => u.Company).HasMaxLength(255); + Property(u => u.CustomerNumber).HasMaxLength(100); - this.Ignore(u => u.PasswordFormat); + Ignore(u => u.PasswordFormat); - this.HasMany(c => c.CustomerRoles) - .WithMany() - .Map(m => m.ToTable("Customer_CustomerRole_Mapping")); - - this.HasMany

(c => c.Addresses) + HasMany
(c => c.Addresses) .WithMany() .Map(m => m.ToTable("CustomerAddresses")); - this.HasOptional
(c => c.BillingAddress); - this.HasOptional
(c => c.ShippingAddress); + + HasOptional
(c => c.BillingAddress); + HasOptional
(c => c.ShippingAddress); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Customers/CustomerRoleMap.cs b/src/Libraries/SmartStore.Data/Mapping/Customers/CustomerRoleMap.cs index a7682d0375..35a76ed019 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Customers/CustomerRoleMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Customers/CustomerRoleMap.cs @@ -7,10 +7,14 @@ public partial class CustomerRoleMap : EntityTypeConfiguration { public CustomerRoleMap() { - this.ToTable("CustomerRole"); - this.HasKey(cr => cr.Id); - this.Property(cr => cr.Name).IsRequired().HasMaxLength(255); - this.Property(cr => cr.SystemName).HasMaxLength(255); + ToTable("CustomerRole"); + HasKey(cr => cr.Id); + Property(cr => cr.Name).IsRequired().HasMaxLength(255); + Property(cr => cr.SystemName).HasMaxLength(255); + + HasMany(cr => cr.RuleSets) + .WithMany(rs => rs.CustomerRoles) + .Map(m => m.ToTable("RuleSet_CustomerRole_Mapping")); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Customers/WalletHistoryMap.cs b/src/Libraries/SmartStore.Data/Mapping/Customers/WalletHistoryMap.cs index 1db98316dc..336620879c 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Customers/WalletHistoryMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Customers/WalletHistoryMap.cs @@ -3,27 +3,27 @@ namespace SmartStore.Data.Mapping.Customers { - public partial class WalletHistoryMap : EntityTypeConfiguration - { - public WalletHistoryMap() - { - ToTable("WalletHistory"); - HasKey(x => x.Id); + public partial class WalletHistoryMap : EntityTypeConfiguration + { + public WalletHistoryMap() + { + ToTable("WalletHistory"); + HasKey(x => x.Id); - Property(x => x.Amount).HasPrecision(18, 4); - Property(x => x.AmountBalance).HasPrecision(18, 4); - Property(x => x.AmountBalancePerStore).HasPrecision(18, 4); - Property(x => x.Message).HasMaxLength(1000); - Property(x => x.AdminComment).HasMaxLength(4000); + Property(x => x.Amount).HasPrecision(18, 4); + Property(x => x.AmountBalance).HasPrecision(18, 4); + Property(x => x.AmountBalancePerStore).HasPrecision(18, 4); + Property(x => x.Message).HasMaxLength(1000); + Property(x => x.AdminComment).HasMaxLength(4000); - HasRequired(x => x.Customer) - .WithMany(x => x.WalletHistory) - .HasForeignKey(x => x.CustomerId); + HasRequired(x => x.Customer) + .WithMany(x => x.WalletHistory) + .HasForeignKey(x => x.CustomerId); - HasOptional(x => x.Order) - .WithMany(x => x.WalletHistory) - .HasForeignKey(x => x.OrderId) - .WillCascadeOnDelete(false); - } - } + HasOptional(x => x.Order) + .WithMany(x => x.WalletHistory) + .HasForeignKey(x => x.OrderId) + .WillCascadeOnDelete(false); + } + } } diff --git a/src/Libraries/SmartStore.Data/Mapping/DataExchange/ExportDeploymentMap.cs b/src/Libraries/SmartStore.Data/Mapping/DataExchange/ExportDeploymentMap.cs index 2c49aa7d7a..a850cb8fc6 100644 --- a/src/Libraries/SmartStore.Data/Mapping/DataExchange/ExportDeploymentMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/DataExchange/ExportDeploymentMap.cs @@ -3,29 +3,29 @@ namespace SmartStore.Data.Mapping.DataExchange { - public class ExportDeploymentMap : EntityTypeConfiguration - { - public ExportDeploymentMap() - { - this.ToTable("ExportDeployment"); - this.HasKey(x => x.Id); + public class ExportDeploymentMap : EntityTypeConfiguration + { + public ExportDeploymentMap() + { + this.ToTable("ExportDeployment"); + this.HasKey(x => x.Id); - this.Property(x => x.Name).IsRequired().HasMaxLength(100); - this.Property(x => x.ResultInfo).IsMaxLength(); - this.Property(x => x.Username).HasMaxLength(400); - this.Property(x => x.Password).HasMaxLength(400); - this.Property(x => x.Url).HasMaxLength(4000); - this.Property(x => x.FileSystemPath).HasMaxLength(400); - this.Property(x => x.SubFolder).HasMaxLength(400); - this.Property(x => x.EmailAddresses).HasMaxLength(4000); - this.Property(x => x.EmailSubject).HasMaxLength(400); + this.Property(x => x.Name).IsRequired().HasMaxLength(100); + this.Property(x => x.ResultInfo).IsMaxLength(); + this.Property(x => x.Username).HasMaxLength(400); + this.Property(x => x.Password).HasMaxLength(400); + this.Property(x => x.Url).HasMaxLength(4000); + this.Property(x => x.FileSystemPath).HasMaxLength(400); + this.Property(x => x.SubFolder).HasMaxLength(400); + this.Property(x => x.EmailAddresses).HasMaxLength(4000); + this.Property(x => x.EmailSubject).HasMaxLength(400); - this.Ignore(x => x.DeploymentType); + this.Ignore(x => x.DeploymentType); - this.HasRequired(x => x.Profile) - .WithMany(x => x.Deployments) - .HasForeignKey(x => x.ProfileId) - .WillCascadeOnDelete(true); - } - } + this.HasRequired(x => x.Profile) + .WithMany(x => x.Deployments) + .HasForeignKey(x => x.ProfileId) + .WillCascadeOnDelete(true); + } + } } diff --git a/src/Libraries/SmartStore.Data/Mapping/DataExchange/ExportProfileMap.cs b/src/Libraries/SmartStore.Data/Mapping/DataExchange/ExportProfileMap.cs index adacc3c4c6..87574080f1 100644 --- a/src/Libraries/SmartStore.Data/Mapping/DataExchange/ExportProfileMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/DataExchange/ExportProfileMap.cs @@ -3,28 +3,28 @@ namespace SmartStore.Data.Mapping.DataExchange { - public partial class ExportProfileMap : EntityTypeConfiguration - { - public ExportProfileMap() - { - this.ToTable("ExportProfile"); - this.HasKey(x => x.Id); + public partial class ExportProfileMap : EntityTypeConfiguration + { + public ExportProfileMap() + { + this.ToTable("ExportProfile"); + this.HasKey(x => x.Id); - this.Property(x => x.Name).IsRequired().HasMaxLength(100); - this.Property(x => x.FolderName).IsRequired().HasMaxLength(400); - this.Property(x => x.SystemName).HasMaxLength(400); - this.Property(x => x.ProviderSystemName).IsRequired().HasMaxLength(4000); - this.Property(x => x.Filtering).IsMaxLength(); - this.Property(x => x.Projection).IsMaxLength(); - this.Property(x => x.ProviderConfigData).IsMaxLength(); - this.Property(x => x.ResultInfo).IsMaxLength(); - this.Property(x => x.CompletedEmailAddresses).HasMaxLength(400); - this.Property(x => x.FileNamePattern).HasMaxLength(400); + this.Property(x => x.Name).IsRequired().HasMaxLength(100); + this.Property(x => x.FolderName).IsRequired().HasMaxLength(400); + this.Property(x => x.SystemName).HasMaxLength(400); + this.Property(x => x.ProviderSystemName).IsRequired().HasMaxLength(4000); + this.Property(x => x.Filtering).IsMaxLength(); + this.Property(x => x.Projection).IsMaxLength(); + this.Property(x => x.ProviderConfigData).IsMaxLength(); + this.Property(x => x.ResultInfo).IsMaxLength(); + this.Property(x => x.CompletedEmailAddresses).HasMaxLength(400); + this.Property(x => x.FileNamePattern).HasMaxLength(400); - this.HasRequired(x => x.ScheduleTask) - .WithMany() - .HasForeignKey(x => x.SchedulingTaskId) - .WillCascadeOnDelete(false); - } - } + this.HasRequired(x => x.ScheduleTask) + .WithMany() + .HasForeignKey(x => x.SchedulingTaskId) + .WillCascadeOnDelete(false); + } + } } diff --git a/src/Libraries/SmartStore.Data/Mapping/DataExchange/ImportProfileMap.cs b/src/Libraries/SmartStore.Data/Mapping/DataExchange/ImportProfileMap.cs index 02515fd268..d2402d6303 100644 --- a/src/Libraries/SmartStore.Data/Mapping/DataExchange/ImportProfileMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/DataExchange/ImportProfileMap.cs @@ -3,28 +3,28 @@ namespace SmartStore.Data.Mapping.DataExchange { - public partial class ImportProfileMap : EntityTypeConfiguration - { - public ImportProfileMap() - { - this.ToTable("ImportProfile"); - this.HasKey(x => x.Id); + public partial class ImportProfileMap : EntityTypeConfiguration + { + public ImportProfileMap() + { + this.ToTable("ImportProfile"); + this.HasKey(x => x.Id); - this.Property(x => x.Name).IsRequired().HasMaxLength(100); - this.Property(x => x.FolderName).IsRequired().HasMaxLength(100); - this.Property(x => x.KeyFieldNames).HasMaxLength(1000); - this.Property(x => x.FileTypeConfiguration).IsMaxLength(); - this.Property(x => x.ExtraData).IsMaxLength(); - this.Property(x => x.ColumnMapping).IsMaxLength(); - this.Property(x => x.ResultInfo).IsMaxLength(); + this.Property(x => x.Name).IsRequired().HasMaxLength(100); + this.Property(x => x.FolderName).IsRequired().HasMaxLength(100); + this.Property(x => x.KeyFieldNames).HasMaxLength(1000); + this.Property(x => x.FileTypeConfiguration).IsMaxLength(); + this.Property(x => x.ExtraData).IsMaxLength(); + this.Property(x => x.ColumnMapping).IsMaxLength(); + this.Property(x => x.ResultInfo).IsMaxLength(); - this.Ignore(x => x.FileType); - this.Ignore(x => x.EntityType); + this.Ignore(x => x.FileType); + this.Ignore(x => x.EntityType); - this.HasRequired(x => x.ScheduleTask) - .WithMany() - .HasForeignKey(x => x.SchedulingTaskId) - .WillCascadeOnDelete(false); - } - } + this.HasRequired(x => x.ScheduleTask) + .WithMany() + .HasForeignKey(x => x.SchedulingTaskId) + .WillCascadeOnDelete(false); + } + } } diff --git a/src/Libraries/SmartStore.Data/Mapping/DataExchange/SyncMappingMap.cs b/src/Libraries/SmartStore.Data/Mapping/DataExchange/SyncMappingMap.cs index c5430692f0..498a39be7d 100644 --- a/src/Libraries/SmartStore.Data/Mapping/DataExchange/SyncMappingMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/DataExchange/SyncMappingMap.cs @@ -3,18 +3,18 @@ namespace SmartStore.Data.Mapping.DataExchange { - public partial class SyncMappingMap : EntityTypeConfiguration - { - public SyncMappingMap() - { - this.ToTable("SyncMapping"); - this.HasKey(x => x.Id); + public partial class SyncMappingMap : EntityTypeConfiguration + { + public SyncMappingMap() + { + this.ToTable("SyncMapping"); + this.HasKey(x => x.Id); - this.Property(x => x.EntityName).IsRequired().HasMaxLength(100); - this.Property(x => x.ContextName).IsRequired().HasMaxLength(100); - this.Property(x => x.SourceKey).IsRequired().HasMaxLength(150); - this.Property(x => x.SourceHash).HasMaxLength(40); - this.Property(x => x.CustomString).IsMaxLength(); - } - } + this.Property(x => x.EntityName).IsRequired().HasMaxLength(100); + this.Property(x => x.ContextName).IsRequired().HasMaxLength(100); + this.Property(x => x.SourceKey).IsRequired().HasMaxLength(150); + this.Property(x => x.SourceHash).HasMaxLength(40); + this.Property(x => x.CustomString).IsMaxLength(); + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Directory/CountryMap.cs b/src/Libraries/SmartStore.Data/Mapping/Directory/CountryMap.cs index 01d6ec5720..ba4ce8536b 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Directory/CountryMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Directory/CountryMap.cs @@ -7,11 +7,16 @@ public partial class CountryMap : EntityTypeConfiguration { public CountryMap() { - this.ToTable("Country"); - this.HasKey(c =>c.Id); - this.Property(c => c.Name).IsRequired().HasMaxLength(100); - this.Property(c =>c.TwoLetterIsoCode).HasMaxLength(2); - this.Property(c =>c.ThreeLetterIsoCode).HasMaxLength(3); + ToTable("Country"); + HasKey(c => c.Id); + Property(c => c.Name).IsRequired().HasMaxLength(100); + Property(c => c.TwoLetterIsoCode).HasMaxLength(2); + Property(c => c.ThreeLetterIsoCode).HasMaxLength(3); + + HasOptional(x => x.DefaultCurrency) + .WithMany() + .HasForeignKey(x => x.DefaultCurrencyId) + .WillCascadeOnDelete(false); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Directory/CurrencyMap.cs b/src/Libraries/SmartStore.Data/Mapping/Directory/CurrencyMap.cs index b5f2dabb34..32a9ef1701 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Directory/CurrencyMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Directory/CurrencyMap.cs @@ -8,13 +8,13 @@ public partial class CurrencyMap : EntityTypeConfiguration public CurrencyMap() { this.ToTable("Currency"); - this.HasKey(c =>c.Id); + this.HasKey(c => c.Id); this.Property(c => c.Name).IsRequired().HasMaxLength(50); this.Property(c => c.CurrencyCode).IsRequired().HasMaxLength(5); this.Property(c => c.DisplayLocale).HasMaxLength(50); this.Property(c => c.CustomFormatting).HasMaxLength(50); - this.Property(c => c.Rate).HasPrecision(18, 8); // // With virtual currencies (e.g. BitCoin) being so precise, we need to store rates up to 8 decimal places - this.Property(c => c.DomainEndings).HasMaxLength(1000); + this.Property(c => c.Rate).HasPrecision(18, 8); // // With virtual currencies (e.g. BitCoin) being so precise, we need to store rates up to 8 decimal places + this.Property(c => c.DomainEndings).HasMaxLength(1000); this.Property(o => o.RoundOrderTotalDenominator).HasPrecision(18, 4); } } diff --git a/src/Libraries/SmartStore.Data/Mapping/Discounts/DiscountMap.cs b/src/Libraries/SmartStore.Data/Mapping/Discounts/DiscountMap.cs index 7ba5ff7390..0136595340 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Discounts/DiscountMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Discounts/DiscountMap.cs @@ -3,35 +3,35 @@ namespace SmartStore.Data.Mapping.Discounts { - public partial class DiscountMap : EntityTypeConfiguration + public partial class DiscountMap : EntityTypeConfiguration { public DiscountMap() { - this.ToTable("Discount"); - this.HasKey(d => d.Id); - this.Property(d => d.Name).IsRequired().HasMaxLength(200); - this.Property(d => d.CouponCode).HasMaxLength(100); - this.Property(d => d.DiscountPercentage).HasPrecision(18, 4); - this.Property(d => d.DiscountAmount).HasPrecision(18, 4); + ToTable("Discount"); + HasKey(d => d.Id); + Property(d => d.Name).IsRequired().HasMaxLength(200); + Property(d => d.CouponCode).HasMaxLength(100); + Property(d => d.DiscountPercentage).HasPrecision(18, 4); + Property(d => d.DiscountAmount).HasPrecision(18, 4); - this.Ignore(d => d.DiscountType); - this.Ignore(d => d.DiscountLimitation); + Ignore(d => d.DiscountType); + Ignore(d => d.DiscountLimitation); - this.HasMany(dr => dr.DiscountRequirements) - .WithRequired(d => d.Discount) - .HasForeignKey(dr => dr.DiscountId); + HasMany(d => d.RuleSets) + .WithMany(rs => rs.Discounts) + .Map(m => m.ToTable("RuleSet_Discount_Mapping")); - this.HasMany(dr => dr.AppliedToCategories) + HasMany(dr => dr.AppliedToCategories) .WithMany(c => c.AppliedDiscounts) .Map(m => m.ToTable("Discount_AppliedToCategories")); - this.HasMany(dr => dr.AppliedToManufacturers) - .WithMany(x => x.AppliedDiscounts) - .Map(m => m.ToTable("Discount_AppliedToManufacturers")); + HasMany(dr => dr.AppliedToManufacturers) + .WithMany(x => x.AppliedDiscounts) + .Map(m => m.ToTable("Discount_AppliedToManufacturers")); - this.HasMany(dr => dr.AppliedToProducts) + HasMany(dr => dr.AppliedToProducts) .WithMany(p => p.AppliedDiscounts) - .Map(m => m.ToTable("Discount_AppliedToProducts")); + .Map(m => m.ToTable("Discount_AppliedToProducts")); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Discounts/DiscountRequirementMap.cs b/src/Libraries/SmartStore.Data/Mapping/Discounts/DiscountRequirementMap.cs deleted file mode 100644 index 50908b4318..0000000000 --- a/src/Libraries/SmartStore.Data/Mapping/Discounts/DiscountRequirementMap.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Data.Entity.ModelConfiguration; -using SmartStore.Core.Domain.Discounts; - -namespace SmartStore.Data.Mapping.Discounts -{ - public partial class DiscountRequirementMap : EntityTypeConfiguration - { - public DiscountRequirementMap() - { - this.ToTable("DiscountRequirement"); - this.HasKey(dr => dr.Id); - - this.Property(dr => dr.SpentAmount).HasPrecision(18, 4); - } - } -} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Discounts/DiscountUsageHistoryMap.cs b/src/Libraries/SmartStore.Data/Mapping/Discounts/DiscountUsageHistoryMap.cs index 82028ea8e9..2ef6b8b368 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Discounts/DiscountUsageHistoryMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Discounts/DiscountUsageHistoryMap.cs @@ -9,7 +9,7 @@ public DiscountUsageHistoryMap() { this.ToTable("DiscountUsageHistory"); this.HasKey(duh => duh.Id); - + this.HasRequired(duh => duh.Discount) .WithMany() .HasForeignKey(duh => duh.DiscountId); diff --git a/src/Libraries/SmartStore.Data/Mapping/Forums/ForumGroupMap.cs b/src/Libraries/SmartStore.Data/Mapping/Forums/ForumGroupMap.cs index 6296b387d1..c838c6d893 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Forums/ForumGroupMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Forums/ForumGroupMap.cs @@ -10,7 +10,7 @@ public ForumGroupMap() this.ToTable("Forums_Group"); this.HasKey(fg => fg.Id); this.Property(fg => fg.Name).IsRequired().HasMaxLength(200); - this.Property(fg => fg.Description).IsMaxLength(); + this.Property(fg => fg.Description).IsMaxLength(); } } } diff --git a/src/Libraries/SmartStore.Data/Mapping/Forums/ForumMap.cs b/src/Libraries/SmartStore.Data/Mapping/Forums/ForumMap.cs index 8d2f0d5db4..92ed155ac8 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Forums/ForumMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Forums/ForumMap.cs @@ -10,8 +10,8 @@ public ForumMap() this.ToTable("Forums_Forum"); this.HasKey(f => f.Id); this.Property(f => f.Name).IsRequired().HasMaxLength(200); - this.Property(f => f.Description).IsMaxLength(); - + this.Property(f => f.Description).IsMaxLength(); + this.HasRequired(f => f.ForumGroup) .WithMany(fg => fg.Forums) .HasForeignKey(f => f.ForumGroupId); diff --git a/src/Libraries/SmartStore.Data/Mapping/Localization/LanguageMap.cs b/src/Libraries/SmartStore.Data/Mapping/Localization/LanguageMap.cs index 966095b639..ee9b04ec9a 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Localization/LanguageMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Localization/LanguageMap.cs @@ -13,7 +13,7 @@ public LanguageMap() this.Property(l => l.LanguageCulture).IsRequired().HasMaxLength(20); this.Property(l => l.UniqueSeoCode).HasMaxLength(2); this.Property(l => l.FlagImageFileName).HasMaxLength(50); - + } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Localization/LocalizedPropertyMap.cs b/src/Libraries/SmartStore.Data/Mapping/Localization/LocalizedPropertyMap.cs index bd223a711e..0caaa0f2cd 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Localization/LocalizedPropertyMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Localization/LocalizedPropertyMap.cs @@ -13,7 +13,7 @@ public LocalizedPropertyMap() this.Property(lp => lp.LocaleKeyGroup).IsRequired().HasMaxLength(400); this.Property(lp => lp.LocaleKey).IsRequired().HasMaxLength(400); this.Property(lp => lp.LocaleValue).IsRequired().IsMaxLength(); - + this.HasRequired(lp => lp.Language) .WithMany() .HasForeignKey(lp => lp.LanguageId); diff --git a/src/Libraries/SmartStore.Data/Mapping/Logging/LogMap.cs b/src/Libraries/SmartStore.Data/Mapping/Logging/LogMap.cs index ec282bd4db..54c139fe45 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Logging/LogMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Logging/LogMap.cs @@ -10,16 +10,16 @@ public LogMap() this.ToTable("Log"); this.HasKey(l => l.Id); this.Property(l => l.ShortMessage).IsRequired().HasMaxLength(4000); - this.Property(l => l.Logger).IsRequired().HasMaxLength(400); - this.Property(l => l.FullMessage).IsMaxLength(); + this.Property(l => l.Logger).IsRequired().HasMaxLength(400); + this.Property(l => l.FullMessage).IsMaxLength(); this.Property(l => l.IpAddress).HasMaxLength(200); - this.Property(l => l.UserName).HasMaxLength(100); - this.Property(l => l.HttpMethod).HasMaxLength(10); - this.Property(l => l.PageUrl).HasMaxLength(1500); - this.Property(l => l.ReferrerUrl).HasMaxLength(1500); + this.Property(l => l.UserName).HasMaxLength(100); + this.Property(l => l.HttpMethod).HasMaxLength(10); + this.Property(l => l.PageUrl).HasMaxLength(1500); + this.Property(l => l.ReferrerUrl).HasMaxLength(1500); + + this.Ignore(l => l.LogLevel); - this.Ignore(l => l.LogLevel); - this.HasOptional(l => l.Customer) .WithMany() .HasForeignKey(l => l.CustomerId) diff --git a/src/Libraries/SmartStore.Data/Mapping/Media/DownloadMap.cs b/src/Libraries/SmartStore.Data/Mapping/Media/DownloadMap.cs index 9fb82872d6..2b94e64c0c 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Media/DownloadMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Media/DownloadMap.cs @@ -10,14 +10,10 @@ public DownloadMap() this.ToTable("Download"); this.HasKey(p => p.Id); -#pragma warning disable 612, 618 - this.Property(p => p.DownloadBinary).IsMaxLength(); -#pragma warning restore 612, 618 - - HasOptional(x => x.MediaStorage) - .WithMany() - .HasForeignKey(x => x.MediaStorageId) - .WillCascadeOnDelete(false); - } + HasOptional(x => x.MediaFile) + .WithMany() + .HasForeignKey(x => x.MediaFileId) + .WillCascadeOnDelete(false); + } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Media/MediaFileMap.cs b/src/Libraries/SmartStore.Data/Mapping/Media/MediaFileMap.cs new file mode 100644 index 0000000000..e95f99ddeb --- /dev/null +++ b/src/Libraries/SmartStore.Data/Mapping/Media/MediaFileMap.cs @@ -0,0 +1,41 @@ +using System.Data.Entity.ModelConfiguration; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Data.Mapping.Media +{ + public partial class MediaFileMap : EntityTypeConfiguration + { + public MediaFileMap() + { + ToTable("MediaFile"); + HasKey(x => x.Id); + + Property(x => x.Name).HasMaxLength(300).HasColumnName("Name"); + Property(x => x.Extension).HasMaxLength(50); + Property(x => x.MimeType).IsRequired().HasMaxLength(100); + Property(x => x.MediaType).IsRequired().HasMaxLength(20); + Property(x => x.Alt).HasMaxLength(400); + Property(x => x.Title).HasMaxLength(400); + Property(x => x.Metadata).IsMaxLength(); + + HasOptional(x => x.Folder) + .WithMany(x => x.Files) + .HasForeignKey(x => x.FolderId) + .WillCascadeOnDelete(false); + + HasOptional(x => x.MediaStorage) + .WithMany() + .HasForeignKey(x => x.MediaStorageId) + .WillCascadeOnDelete(false); + + HasMany(x => x.Tags) + .WithMany(t => t.MediaFiles) + .Map(m => m.ToTable("MediaFile_Tag_Mapping")); + + HasMany(x => x.Tracks) + .WithRequired(x => x.MediaFile) + .HasForeignKey(x => x.MediaFileId) + .WillCascadeOnDelete(true); + } + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Media/MediaFolderMap.cs b/src/Libraries/SmartStore.Data/Mapping/Media/MediaFolderMap.cs new file mode 100644 index 0000000000..3873f15a54 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Mapping/Media/MediaFolderMap.cs @@ -0,0 +1,30 @@ +using System.Data.Entity.ModelConfiguration; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Data.Mapping.Media +{ + public partial class MediaFolderMap : EntityTypeConfiguration + { + public MediaFolderMap() + { + ToTable("MediaFolder"); + HasKey(c => c.Id); + Property(c => c.Name).IsRequired().HasMaxLength(255); + Property(c => c.Slug).HasMaxLength(255); + Property(c => c.Metadata).IsMaxLength(); + + HasOptional(x => x.Parent) + .WithMany(x => x.Children) + .HasForeignKey(x => x.ParentId) + .WillCascadeOnDelete(false); + } + } + + public partial class MediaAlbumMap : EntityTypeConfiguration + { + public MediaAlbumMap() + { + Property(c => c.ResKey).HasMaxLength(255); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Mapping/Media/MediaStorageMap.cs b/src/Libraries/SmartStore.Data/Mapping/Media/MediaStorageMap.cs index a1d68963d3..eaa13b5aac 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Media/MediaStorageMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Media/MediaStorageMap.cs @@ -3,13 +3,13 @@ namespace SmartStore.Data.Mapping.Media { - public partial class MediaStorageMap : EntityTypeConfiguration - { - public MediaStorageMap() - { - ToTable("MediaStorage"); - HasKey(x => x.Id); - Property(x => x.Data).IsRequired().IsMaxLength(); - } - } + public partial class MediaStorageMap : EntityTypeConfiguration + { + public MediaStorageMap() + { + ToTable("MediaStorage"); + HasKey(x => x.Id); + Property(x => x.Data).IsRequired().IsMaxLength(); + } + } } diff --git a/src/Libraries/SmartStore.Data/Mapping/Media/MediaTagMap.cs b/src/Libraries/SmartStore.Data/Mapping/Media/MediaTagMap.cs new file mode 100644 index 0000000000..8a3faf1072 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Mapping/Media/MediaTagMap.cs @@ -0,0 +1,17 @@ +using System.Data.Entity.ModelConfiguration; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Data.Mapping.Media +{ + public partial class MediaTagMap : EntityTypeConfiguration + { + public MediaTagMap() + { + ToTable("MediaTag"); + HasKey(c => c.Id); + Property(c => c.Name).IsRequired().HasMaxLength(100); + + HasIndex(x => x.Name).HasName("IX_MediaTag_Name"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Mapping/Media/MediaTrackMap.cs b/src/Libraries/SmartStore.Data/Mapping/Media/MediaTrackMap.cs new file mode 100644 index 0000000000..ec2f551d08 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Mapping/Media/MediaTrackMap.cs @@ -0,0 +1,26 @@ +using System.Data.Entity.ModelConfiguration; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Data.Mapping.Media +{ + public partial class MediaTrackMap : EntityTypeConfiguration + { + public MediaTrackMap() + { + ToTable("MediaTrack"); + HasKey(x => x.Id); + + Property(x => x.EntityId); + Property(x => x.EntityName).IsRequired().HasMaxLength(255); + Property(x => x.Property).HasMaxLength(255); + Property(x => x.Album).IsRequired().HasMaxLength(50); + + HasRequired(x => x.MediaFile) + .WithMany(x => x.Tracks) + .HasForeignKey(x => x.MediaFileId) + .WillCascadeOnDelete(true); + + HasIndex(x => x.Album).HasName("IX_Album"); + } + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Media/PictureMap.cs b/src/Libraries/SmartStore.Data/Mapping/Media/PictureMap.cs deleted file mode 100644 index a16181c408..0000000000 --- a/src/Libraries/SmartStore.Data/Mapping/Media/PictureMap.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Data.Entity.ModelConfiguration; -using SmartStore.Core.Domain.Media; - -namespace SmartStore.Data.Mapping.Media -{ - public partial class PictureMap : EntityTypeConfiguration - { - public PictureMap() - { - this.ToTable("Picture"); - this.HasKey(p => p.Id); - -#pragma warning disable 612, 618 - this.Property(p => p.PictureBinary).IsMaxLength(); -#pragma warning restore 612, 618 - - this.Property(p => p.MimeType).IsRequired().HasMaxLength(40); - this.Property(p => p.SeoFilename).HasMaxLength(300); - - HasOptional(x => x.MediaStorage) - .WithMany() - .HasForeignKey(x => x.MediaStorageId) - .WillCascadeOnDelete(false); - } - } -} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Messages/MessageTemplateMap.cs b/src/Libraries/SmartStore.Data/Mapping/Messages/MessageTemplateMap.cs index aa12ec200f..67d301bc0c 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Messages/MessageTemplateMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Messages/MessageTemplateMap.cs @@ -11,7 +11,7 @@ public MessageTemplateMap() this.HasKey(mt => mt.Id); this.Property(mt => mt.Name).IsRequired().HasMaxLength(200); - this.Property(mt => mt.Body).IsMaxLength(); + this.Property(mt => mt.Body).IsMaxLength(); this.Property(mt => mt.BccEmailAddresses).HasMaxLength(200); this.Property(mt => mt.Subject).HasMaxLength(1000); this.Property(mt => mt.EmailAccountId).IsRequired(); diff --git a/src/Libraries/SmartStore.Data/Mapping/Messages/QueuedEmailAttachmentMap.cs b/src/Libraries/SmartStore.Data/Mapping/Messages/QueuedEmailAttachmentMap.cs index 4f04e1ae13..8ac9b15950 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Messages/QueuedEmailAttachmentMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Messages/QueuedEmailAttachmentMap.cs @@ -3,36 +3,32 @@ namespace SmartStore.Data.Mapping.Messages { - public class QueuedEmailAttachmentMap : EntityTypeConfiguration - { - public QueuedEmailAttachmentMap() - { - this.ToTable("QueuedEmailAttachment"); - this.HasKey(x => x.Id); + public class QueuedEmailAttachmentMap : EntityTypeConfiguration + { + public QueuedEmailAttachmentMap() + { + ToTable("QueuedEmailAttachment"); + HasKey(x => x.Id); - this.Property(x => x.Path).IsOptional().HasMaxLength(1000); + Property(x => x.Path).IsOptional().HasMaxLength(1000); -#pragma warning disable 612, 618 - this.Property(x => x.Data).IsMaxLength(); -#pragma warning restore 612, 618 + Property(x => x.Name).IsRequired().HasMaxLength(200); + Property(x => x.MimeType).IsRequired().HasMaxLength(200); - this.HasOptional(x => x.File) - .WithMany() - .HasForeignKey(x => x.FileId) - .WillCascadeOnDelete(true); + HasRequired(x => x.QueuedEmail) + .WithMany(qe => qe.Attachments) + .HasForeignKey(x => x.QueuedEmailId) + .WillCascadeOnDelete(true); - this.Property(x => x.Name).IsRequired().HasMaxLength(200); - this.Property(x => x.MimeType).IsRequired().HasMaxLength(200); + HasOptional(x => x.MediaFile) + .WithMany() + .HasForeignKey(x => x.MediaFileId) + .WillCascadeOnDelete(false); - this.HasRequired(x => x.QueuedEmail) - .WithMany(qe => qe.Attachments) - .HasForeignKey(x => x.QueuedEmailId) - .WillCascadeOnDelete(true); - - HasOptional(x => x.MediaStorage) - .WithMany() - .HasForeignKey(x => x.MediaStorageId) - .WillCascadeOnDelete(false); - } - } + HasOptional(x => x.MediaStorage) + .WithMany() + .HasForeignKey(x => x.MediaStorageId) + .WillCascadeOnDelete(true); + } + } } diff --git a/src/Libraries/SmartStore.Data/Mapping/Messages/QueuedEmailMap.cs b/src/Libraries/SmartStore.Data/Mapping/Messages/QueuedEmailMap.cs index a7cfe9ea32..9ef6a7ebe2 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Messages/QueuedEmailMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Messages/QueuedEmailMap.cs @@ -12,7 +12,7 @@ public QueuedEmailMap() this.Property(qe => qe.From).IsRequired().HasMaxLength(500); this.Property(qe => qe.To).IsRequired().HasMaxLength(500); - this.Property(qe => qe.ReplyTo).HasMaxLength(500); + this.Property(qe => qe.ReplyTo).HasMaxLength(500); this.Property(qe => qe.CC).HasMaxLength(500); this.Property(qe => qe.Bcc).HasMaxLength(500); this.Property(qe => qe.Subject).HasMaxLength(1000); diff --git a/src/Libraries/SmartStore.Data/Mapping/News/NewsCommentMap.cs b/src/Libraries/SmartStore.Data/Mapping/News/NewsCommentMap.cs index a0296203ae..13317df3ff 100644 --- a/src/Libraries/SmartStore.Data/Mapping/News/NewsCommentMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/News/NewsCommentMap.cs @@ -10,7 +10,7 @@ public NewsCommentMap() this.ToTable("NewsComment"); //commented because it's already configured by CustomerContentMap class //this.HasKey(pr => pr.Id); - this.Property(nc => nc.CommentText).IsMaxLength(); + this.Property(nc => nc.CommentText).IsMaxLength(); this.HasRequired(nc => nc.NewsItem) .WithMany(n => n.NewsComments) .HasForeignKey(nc => nc.NewsItemId); diff --git a/src/Libraries/SmartStore.Data/Mapping/News/NewsItemMap.cs b/src/Libraries/SmartStore.Data/Mapping/News/NewsItemMap.cs index 4fa1ce98dc..5e99db7272 100644 --- a/src/Libraries/SmartStore.Data/Mapping/News/NewsItemMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/News/NewsItemMap.cs @@ -7,17 +7,30 @@ public partial class NewsItemMap : EntityTypeConfiguration { public NewsItemMap() { - this.ToTable("News"); - this.HasKey(bp => bp.Id); - this.Property(bp => bp.Title).IsRequired(); - this.Property(bp => bp.Short).IsRequired(); - this.Property(bp => bp.Full).IsRequired().IsMaxLength(); - this.Property(bp => bp.MetaKeywords).HasMaxLength(400); - this.Property(bp => bp.MetaTitle).HasMaxLength(400); + ToTable("News"); + HasKey(x => x.Id); + Property(x => x.Title).IsRequired(); + Property(x => x.Short).IsRequired(); + Property(x => x.Full).IsRequired().IsMaxLength(); + Property(x => x.MetaKeywords).HasMaxLength(400); + Property(x => x.MetaTitle).HasMaxLength(400); + Property(x => x.MediaFileId).HasColumnName("MediaFileId"); + Property(x => x.PreviewMediaFileId).HasColumnName("PreviewMediaFileId"); - this.HasRequired(bp => bp.Language) + HasOptional(x => x.Language) .WithMany() - .HasForeignKey(bp => bp.LanguageId).WillCascadeOnDelete(true); + .HasForeignKey(x => x.LanguageId) + .WillCascadeOnDelete(false); + + HasOptional(bp => bp.MediaFile) + .WithMany() + .HasForeignKey(bp => bp.MediaFileId) + .WillCascadeOnDelete(false); + + HasOptional(bp => bp.PreviewMediaFile) + .WithMany() + .HasForeignKey(bp => bp.PreviewMediaFileId) + .WillCascadeOnDelete(false); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Orders/CheckoutAttributeValueMap.cs b/src/Libraries/SmartStore.Data/Mapping/Orders/CheckoutAttributeValueMap.cs index ea65bca068..bab207962c 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Orders/CheckoutAttributeValueMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Orders/CheckoutAttributeValueMap.cs @@ -7,13 +7,18 @@ public partial class CheckoutAttributeValueMap : EntityTypeConfiguration cav.Id); - this.Property(cav => cav.Name).IsRequired().HasMaxLength(400); - this.Property(cav => cav.PriceAdjustment).HasPrecision(18, 4); - this.Property(cav => cav.WeightAdjustment).HasPrecision(18, 4); + ToTable("CheckoutAttributeValue"); + HasKey(cav => cav.Id); + Property(cav => cav.Name).IsRequired().HasMaxLength(400); + Property(cav => cav.PriceAdjustment).HasPrecision(18, 4); + Property(cav => cav.WeightAdjustment).HasPrecision(18, 4); - this.HasRequired(cav => cav.CheckoutAttribute) + HasOptional(x => x.MediaFile) + .WithMany() + .HasForeignKey(x => x.MediaFileId) + .WillCascadeOnDelete(false); + + HasRequired(cav => cav.CheckoutAttribute) .WithMany(ca => ca.CheckoutAttributeValues) .HasForeignKey(cav => cav.CheckoutAttributeId); } diff --git a/src/Libraries/SmartStore.Data/Mapping/Orders/OrderItemMap.cs b/src/Libraries/SmartStore.Data/Mapping/Orders/OrderItemMap.cs index ea68047512..f07ec73c2b 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Orders/OrderItemMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Orders/OrderItemMap.cs @@ -7,21 +7,21 @@ public partial class OrderItemMap : EntityTypeConfiguration { public OrderItemMap() { - this.ToTable("OrderItem"); + this.ToTable("OrderItem"); this.HasKey(orderItem => orderItem.Id); this.Property(orderItem => orderItem.AttributeDescription); this.Property(orderItem => orderItem.AttributesXml).IsMaxLength(); - this.Property(orderItem => orderItem.BundleData).IsMaxLength(); + this.Property(orderItem => orderItem.BundleData).IsMaxLength(); this.Property(orderItem => orderItem.UnitPriceInclTax).HasPrecision(18, 4); this.Property(orderItem => orderItem.UnitPriceExclTax).HasPrecision(18, 4); this.Property(orderItem => orderItem.PriceInclTax).HasPrecision(18, 4); this.Property(orderItem => orderItem.PriceExclTax).HasPrecision(18, 4); - this.Property(orderItem => orderItem.TaxRate).HasPrecision(18, 4); + this.Property(orderItem => orderItem.TaxRate).HasPrecision(18, 4); this.Property(orderItem => orderItem.DiscountAmountInclTax).HasPrecision(18, 4); this.Property(orderItem => orderItem.DiscountAmountExclTax).HasPrecision(18, 4); this.Property(orderItem => orderItem.ItemWeight).HasPrecision(18, 4); - this.Property(orderItem => orderItem.ProductCost).HasPrecision(18, 4); + this.Property(orderItem => orderItem.ProductCost).HasPrecision(18, 4); this.HasRequired(orderItem => orderItem.Order) .WithMany(o => o.OrderItems) diff --git a/src/Libraries/SmartStore.Data/Mapping/Orders/OrderMap.cs b/src/Libraries/SmartStore.Data/Mapping/Orders/OrderMap.cs index a0378d4303..43d7722631 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Orders/OrderMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Orders/OrderMap.cs @@ -16,24 +16,24 @@ public OrderMap() this.Property(o => o.OrderSubTotalDiscountExclTax).HasPrecision(18, 4); this.Property(o => o.OrderShippingInclTax).HasPrecision(18, 4); this.Property(o => o.OrderShippingExclTax).HasPrecision(18, 4); - this.Property(o => o.OrderShippingTaxRate).HasPrecision(18, 4); + this.Property(o => o.OrderShippingTaxRate).HasPrecision(18, 4); this.Property(o => o.PaymentMethodAdditionalFeeInclTax).HasPrecision(18, 4); this.Property(o => o.PaymentMethodAdditionalFeeExclTax).HasPrecision(18, 4); - this.Property(o => o.PaymentMethodAdditionalFeeTaxRate).HasPrecision(18, 4); + this.Property(o => o.PaymentMethodAdditionalFeeTaxRate).HasPrecision(18, 4); this.Property(o => o.OrderTax).HasPrecision(18, 4); this.Property(o => o.OrderDiscount).HasPrecision(18, 4); - this.Property(o => o.CreditBalance).HasPrecision(18, 4); - this.Property(o => o.OrderTotalRounding).HasPrecision(18, 4); + this.Property(o => o.CreditBalance).HasPrecision(18, 4); + this.Property(o => o.OrderTotalRounding).HasPrecision(18, 4); this.Property(o => o.OrderTotal).HasPrecision(18, 4); this.Property(o => o.RefundedAmount).HasPrecision(18, 4); - this.Property(o => o.OrderNumber).IsOptional(); + this.Property(o => o.OrderNumber).IsOptional(); this.Ignore(o => o.OrderStatus); this.Ignore(o => o.PaymentStatus); this.Ignore(o => o.ShippingStatus); this.Ignore(o => o.CustomerTaxDisplayType); this.Ignore(o => o.TaxRatesDictionary); - + this.HasRequired(o => o.Customer) .WithMany(c => c.Orders) .HasForeignKey(o => o.CustomerId); diff --git a/src/Libraries/SmartStore.Data/Mapping/Orders/ReturnRequestMap.cs b/src/Libraries/SmartStore.Data/Mapping/Orders/ReturnRequestMap.cs index 5f963c41b0..67776065ba 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Orders/ReturnRequestMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Orders/ReturnRequestMap.cs @@ -11,7 +11,7 @@ public ReturnRequestMap() this.HasKey(rr => rr.Id); this.Property(rr => rr.ReasonForReturn).IsRequired(); this.Property(rr => rr.RequestedAction).IsRequired(); - this.Property(rr => rr.AdminComment).HasMaxLength(4000); + this.Property(rr => rr.AdminComment).HasMaxLength(4000); this.Ignore(rr => rr.ReturnRequestStatus); diff --git a/src/Libraries/SmartStore.Data/Mapping/Orders/ShoppingCartItemMap.cs b/src/Libraries/SmartStore.Data/Mapping/Orders/ShoppingCartItemMap.cs index 2fb5426404..661c658e1d 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Orders/ShoppingCartItemMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Orders/ShoppingCartItemMap.cs @@ -11,7 +11,7 @@ public ShoppingCartItemMap() this.HasKey(sci => sci.Id); this.Property(sci => sci.CustomerEnteredPrice).HasPrecision(18, 4); - this.Property(sci => sci.AttributesXml).IsMaxLength(); + this.Property(sci => sci.AttributesXml).IsMaxLength(); this.Ignore(sci => sci.ShoppingCartType); this.Ignore(sci => sci.IsFreeShipping); @@ -26,10 +26,10 @@ public ShoppingCartItemMap() .WithMany() .HasForeignKey(sci => sci.ProductId); - this.HasOptional(sci => sci.BundleItem) - .WithMany() - .HasForeignKey(sci => sci.BundleItemId) - .WillCascadeOnDelete(false); + this.HasOptional(sci => sci.BundleItem) + .WithMany() + .HasForeignKey(sci => sci.BundleItemId) + .WillCascadeOnDelete(false); } } } diff --git a/src/Libraries/SmartStore.Data/Mapping/Payments/PaymentMethodMap.cs b/src/Libraries/SmartStore.Data/Mapping/Payments/PaymentMethodMap.cs index 4af965451a..01675e8f87 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Payments/PaymentMethodMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Payments/PaymentMethodMap.cs @@ -3,16 +3,20 @@ namespace SmartStore.Data.Mapping.Payments { - public partial class PaymentMethodMap : EntityTypeConfiguration - { - public PaymentMethodMap() - { - this.ToTable("PaymentMethod"); - this.HasKey(x => x.Id); + public partial class PaymentMethodMap : EntityTypeConfiguration + { + public PaymentMethodMap() + { + ToTable("PaymentMethod"); + HasKey(x => x.Id); - this.Property(x => x.PaymentMethodSystemName).IsRequired().HasMaxLength(4000); + Property(x => x.PaymentMethodSystemName).IsRequired().HasMaxLength(4000); - this.Property(x => x.FullDescription).HasMaxLength(4000); - } - } + Property(x => x.FullDescription).HasMaxLength(4000); + + HasMany(pm => pm.RuleSets) + .WithMany(rs => rs.PaymentMethods) + .Map(m => m.ToTable("RuleSet_PaymentMethod_Mapping")); + } + } } diff --git a/src/Libraries/SmartStore.Data/Mapping/Polls/PollMap.cs b/src/Libraries/SmartStore.Data/Mapping/Polls/PollMap.cs index f647b338d3..1f2eaf2e54 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Polls/PollMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Polls/PollMap.cs @@ -10,7 +10,7 @@ public PollMap() this.ToTable("Poll"); this.HasKey(p => p.Id); this.Property(p => p.Name).IsRequired(); - + this.HasRequired(p => p.Language) .WithMany() .HasForeignKey(p => p.LanguageId).WillCascadeOnDelete(true); diff --git a/src/Libraries/SmartStore.Data/Mapping/Security/AclRecordMap.cs b/src/Libraries/SmartStore.Data/Mapping/Security/AclRecordMap.cs index 6604508ab4..7c3f705d9a 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Security/AclRecordMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Security/AclRecordMap.cs @@ -11,12 +11,12 @@ public AclRecordMap() this.HasKey(lp => lp.Id); this.Property(lp => lp.EntityName).IsRequired().HasMaxLength(400); - this.Property(x => x.IsIdle).IsRequired(); + this.Property(x => x.IsIdle).IsRequired(); - this.HasRequired(tp => tp.CustomerRole) - .WithMany() - .HasForeignKey(tp => tp.CustomerRoleId) - .WillCascadeOnDelete(true); + this.HasRequired(tp => tp.CustomerRole) + .WithMany() + .HasForeignKey(tp => tp.CustomerRoleId) + .WillCascadeOnDelete(true); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Security/PermissionRecordMap.cs b/src/Libraries/SmartStore.Data/Mapping/Security/PermissionRecordMap.cs index 86c2c41c38..145234dee3 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Security/PermissionRecordMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Security/PermissionRecordMap.cs @@ -7,15 +7,9 @@ public partial class PermissionRecordMap : EntityTypeConfiguration pr.Id); - this.Property(pr => pr.Name).IsRequired(); - this.Property(pr => pr.SystemName).IsRequired().HasMaxLength(255); - this.Property(pr => pr.Category).IsRequired().HasMaxLength(255); - - this.HasMany(pr => pr.CustomerRoles) - .WithMany(cr => cr.PermissionRecords) - .Map(m => m.ToTable("PermissionRecord_Role_Mapping")); + ToTable("PermissionRecord"); + HasKey(pr => pr.Id); + Property(pr => pr.SystemName).IsRequired().HasMaxLength(255); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Shipping/ShipmentItemMap.cs b/src/Libraries/SmartStore.Data/Mapping/Shipping/ShipmentItemMap.cs index 8a32be4a89..e8108ae002 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Shipping/ShipmentItemMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Shipping/ShipmentItemMap.cs @@ -7,7 +7,7 @@ public partial class ShipmentItemMap : EntityTypeConfiguration { public ShipmentItemMap() { - this.ToTable("ShipmentItem"); + this.ToTable("ShipmentItem"); this.HasKey(si => si.Id); this.HasRequired(si => si.Shipment) diff --git a/src/Libraries/SmartStore.Data/Mapping/Shipping/ShipmentMap.cs b/src/Libraries/SmartStore.Data/Mapping/Shipping/ShipmentMap.cs index 6ce4aeb5d7..056cd938de 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Shipping/ShipmentMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Shipping/ShipmentMap.cs @@ -11,7 +11,7 @@ public ShipmentMap() this.HasKey(s => s.Id); this.Property(s => s.TotalWeight).HasPrecision(18, 4); - + this.HasRequired(s => s.Order) .WithMany(o => o.Shipments) .HasForeignKey(s => s.OrderId); diff --git a/src/Libraries/SmartStore.Data/Mapping/Shipping/ShippingMethodMap.cs b/src/Libraries/SmartStore.Data/Mapping/Shipping/ShippingMethodMap.cs index 723fe6fbce..457c769e63 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Shipping/ShippingMethodMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Shipping/ShippingMethodMap.cs @@ -3,7 +3,7 @@ namespace SmartStore.Data.Mapping.Shipping { - public class ShippingMethodMap : EntityTypeConfiguration + public class ShippingMethodMap : EntityTypeConfiguration { public ShippingMethodMap() { @@ -11,6 +11,10 @@ public ShippingMethodMap() HasKey(sm => sm.Id); Property(sm => sm.Name).IsRequired().HasMaxLength(400); + + HasMany(sm => sm.RuleSets) + .WithMany(rs => rs.ShippingMethods) + .Map(m => m.ToTable("RuleSet_ShippingMethod_Mapping")); } } } diff --git a/src/Libraries/SmartStore.Data/Mapping/Stores/StoreMap.cs b/src/Libraries/SmartStore.Data/Mapping/Stores/StoreMap.cs index 6e8cdad339..8691c9755d 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Stores/StoreMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Stores/StoreMap.cs @@ -3,27 +3,28 @@ namespace SmartStore.Data.Mapping.Stores { - public partial class StoreMap : EntityTypeConfiguration - { - public StoreMap() - { - this.ToTable("Store"); - this.HasKey(s => s.Id); - this.Property(s => s.Name).IsRequired().HasMaxLength(400); - this.Property(s => s.Url).IsRequired().HasMaxLength(400); - this.Property(s => s.ContentDeliveryNetwork).HasMaxLength(400); - this.Property(s => s.SecureUrl).HasMaxLength(400); - this.Property(s => s.Hosts).HasMaxLength(1000); + public partial class StoreMap : EntityTypeConfiguration + { + public StoreMap() + { + ToTable("Store"); + HasKey(s => s.Id); + Property(s => s.Name).IsRequired().HasMaxLength(400); + Property(s => s.Url).IsRequired().HasMaxLength(400); + Property(s => s.ContentDeliveryNetwork).HasMaxLength(400); + Property(s => s.SecureUrl).HasMaxLength(400); + Property(s => s.Hosts).HasMaxLength(1000); + Property(x => x.LogoMediaFileId).HasColumnName("LogoMediaFileId"); - this.HasRequired(s => s.PrimaryStoreCurrency) - .WithMany() - .HasForeignKey(s => s.PrimaryStoreCurrencyId) - .WillCascadeOnDelete(false); + HasRequired(s => s.PrimaryStoreCurrency) + .WithMany() + .HasForeignKey(s => s.PrimaryStoreCurrencyId) + .WillCascadeOnDelete(false); - this.HasRequired(s => s.PrimaryExchangeRateCurrency) - .WithMany() - .HasForeignKey(s => s.PrimaryExchangeRateCurrencyId) - .WillCascadeOnDelete(false); - } - } + HasRequired(s => s.PrimaryExchangeRateCurrency) + .WithMany() + .HasForeignKey(s => s.PrimaryExchangeRateCurrencyId) + .WillCascadeOnDelete(false); + } + } } diff --git a/src/Libraries/SmartStore.Data/Mapping/Stores/StoreMappingMap.cs b/src/Libraries/SmartStore.Data/Mapping/Stores/StoreMappingMap.cs index bea098ab0c..9d98b89f75 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Stores/StoreMappingMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Stores/StoreMappingMap.cs @@ -3,14 +3,14 @@ namespace SmartStore.Data.Mapping.Stores { - public partial class StoreMappingMap : EntityTypeConfiguration - { - public StoreMappingMap() - { - this.ToTable("StoreMapping"); - this.HasKey(sm => sm.Id); + public partial class StoreMappingMap : EntityTypeConfiguration + { + public StoreMappingMap() + { + this.ToTable("StoreMapping"); + this.HasKey(sm => sm.Id); - this.Property(sm => sm.EntityName).IsRequired().HasMaxLength(400); - } - } + this.Property(sm => sm.EntityName).IsRequired().HasMaxLength(400); + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Mapping/Tasks/ScheduleTaskHistoryMap.cs b/src/Libraries/SmartStore.Data/Mapping/Tasks/ScheduleTaskHistoryMap.cs index 817cabd4e6..c582f53566 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Tasks/ScheduleTaskHistoryMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Tasks/ScheduleTaskHistoryMap.cs @@ -11,7 +11,7 @@ public ScheduleTaskHistoryMap() HasKey(x => x.Id); Property(x => x.MachineName).IsRequired().HasMaxLength(400); Property(x => x.Error); - Property(x => x.ProgressMessage).HasMaxLength(1000); + Property(x => x.ProgressMessage).HasMaxLength(1000); HasRequired(x => x.ScheduleTask) .WithMany(x => x.ScheduleTaskHistory) diff --git a/src/Libraries/SmartStore.Data/Mapping/Tasks/ScheduleTaskMap.cs b/src/Libraries/SmartStore.Data/Mapping/Tasks/ScheduleTaskMap.cs index c3854ec515..1e3c8f8818 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Tasks/ScheduleTaskMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Tasks/ScheduleTaskMap.cs @@ -11,10 +11,10 @@ public ScheduleTaskMap() HasKey(t => t.Id); Property(t => t.Name).HasMaxLength(500).IsRequired(); Property(t => t.Type).HasMaxLength(800).IsRequired(); - Property(t => t.Alias).HasMaxLength(500); - Property(t => t.CronExpression).HasMaxLength(1000); + Property(t => t.Alias).HasMaxLength(500); + Property(t => t.CronExpression).HasMaxLength(1000); - Ignore(t => t.IsPending); + Ignore(t => t.IsPending); Ignore(t => t.LastHistoryEntry); } } diff --git a/src/Libraries/SmartStore.Data/Mapping/Themes/ThemeVariableMap.cs b/src/Libraries/SmartStore.Data/Mapping/Themes/ThemeVariableMap.cs index 03be4cf827..bab6575777 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Themes/ThemeVariableMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Themes/ThemeVariableMap.cs @@ -1,17 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; - + using System.Data.Entity.ModelConfiguration; using SmartStore.Core.Domain.Themes; namespace SmartStore.Data.Mapping.Themes { - + public class ThemeVariableMap : EntityTypeConfiguration { public ThemeVariableMap() - { + { this.ToTable("ThemeVariable"); this.HasKey(t => t.Id); this.Property(t => t.Theme).HasMaxLength(400); diff --git a/src/Libraries/SmartStore.Data/Mapping/Topics/TopicMap.cs b/src/Libraries/SmartStore.Data/Mapping/Topics/TopicMap.cs index 4e2af6ab58..acc15e4e16 100644 --- a/src/Libraries/SmartStore.Data/Mapping/Topics/TopicMap.cs +++ b/src/Libraries/SmartStore.Data/Mapping/Topics/TopicMap.cs @@ -9,10 +9,10 @@ public TopicMap() { this.ToTable("Topic"); this.HasKey(t => t.Id); - this.Property(t => t.ShortTitle).HasMaxLength(50); - this.Property(t => t.Intro).HasMaxLength(255); - this.Property(t => t.Body).IsMaxLength(); - //this.Property(t => t.IsPublished).HasColumnAnnotation("defaultValue", true); - } + this.Property(t => t.ShortTitle).HasMaxLength(50); + this.Property(t => t.Intro).HasMaxLength(255); + this.Property(t => t.Body).IsMaxLength(); + //this.Property(t => t.IsPublished).HasColumnAnnotation("defaultValue", true); + } } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201403112331027_Initial.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201403112331027_Initial.Designer.cs deleted file mode 100644 index 4e2deeda2e..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403112331027_Initial.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.0.2-21211")] - public sealed partial class Initial : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(Initial)); - - string IMigrationMetadata.Id - { - get { return "201403112331027_Initial"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201403112331027_Initial.cs b/src/Libraries/SmartStore.Data/Migrations/201403112331027_Initial.cs deleted file mode 100644 index de838e8eca..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403112331027_Initial.cs +++ /dev/null @@ -1,2104 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using System.Web.Hosting; - using SmartStore.Core.Data; - using SmartStore.Data.Setup; - - public partial class Initial : DbMigration - { - public override void Up() - { - if (DbMigrationContext.Current.SuppressInitialCreate()) - return; - - #region auto-generated - - CreateTable( - "dbo.ProductBundleItemAttributeFilter", - c => new - { - Id = c.Int(nullable: false, identity: true), - BundleItemId = c.Int(nullable: false), - AttributeId = c.Int(nullable: false), - AttributeValueId = c.Int(nullable: false), - IsPreSelected = c.Boolean(nullable: false), - }) - .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), - }) - .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 - { - Id = c.Int(nullable: false, identity: true), - ProductTypeId = c.Int(nullable: false), - ParentGroupedProductId = c.Int(nullable: false), - VisibleIndividually = c.Boolean(nullable: false), - Name = c.String(nullable: false, maxLength: 400), - ShortDescription = c.String(maxLength: 4000), - FullDescription = c.String(), - AdminComment = c.String(maxLength: 4000), - ProductTemplateId = c.Int(nullable: false), - ShowOnHomePage = c.Boolean(nullable: false), - MetaKeywords = c.String(maxLength: 400), - MetaDescription = c.String(maxLength: 4000), - MetaTitle = c.String(maxLength: 400), - AllowCustomerReviews = c.Boolean(nullable: false), - ApprovedRatingSum = c.Int(nullable: false), - NotApprovedRatingSum = c.Int(nullable: false), - ApprovedTotalReviews = c.Int(nullable: false), - NotApprovedTotalReviews = c.Int(nullable: false), - SubjectToAcl = c.Boolean(nullable: false), - LimitedToStores = c.Boolean(nullable: false), - Sku = c.String(maxLength: 400), - ManufacturerPartNumber = c.String(maxLength: 400), - Gtin = c.String(maxLength: 400), - IsGiftCard = c.Boolean(nullable: false), - GiftCardTypeId = c.Int(nullable: false), - RequireOtherProducts = c.Boolean(nullable: false), - RequiredProductIds = c.String(maxLength: 1000), - AutomaticallyAddRequiredProducts = c.Boolean(nullable: false), - IsDownload = c.Boolean(nullable: false), - DownloadId = c.Int(nullable: false), - UnlimitedDownloads = c.Boolean(nullable: false), - MaxNumberOfDownloads = c.Int(nullable: false), - DownloadExpirationDays = c.Int(), - DownloadActivationTypeId = c.Int(nullable: false), - HasSampleDownload = c.Boolean(nullable: false), - SampleDownloadId = c.Int(), - HasUserAgreement = c.Boolean(nullable: false), - UserAgreementText = c.String(maxLength: 4000), - IsRecurring = c.Boolean(nullable: false), - RecurringCycleLength = c.Int(nullable: false), - RecurringCyclePeriodId = c.Int(nullable: false), - RecurringTotalCycles = c.Int(nullable: false), - IsShipEnabled = c.Boolean(nullable: false), - IsFreeShipping = c.Boolean(nullable: false), - AdditionalShippingCharge = c.Decimal(nullable: false, precision: 18, scale: 4), - IsTaxExempt = c.Boolean(nullable: false), - TaxCategoryId = c.Int(nullable: false), - ManageInventoryMethodId = c.Int(nullable: false), - StockQuantity = c.Int(nullable: false), - DisplayStockAvailability = c.Boolean(nullable: false), - DisplayStockQuantity = c.Boolean(nullable: false), - MinStockQuantity = c.Int(nullable: false), - LowStockActivityId = c.Int(nullable: false), - NotifyAdminForQuantityBelow = c.Int(nullable: false), - BackorderModeId = c.Int(nullable: false), - AllowBackInStockSubscriptions = c.Boolean(nullable: false), - OrderMinimumQuantity = c.Int(nullable: false), - OrderMaximumQuantity = c.Int(nullable: false), - AllowedQuantities = c.String(maxLength: 1000), - DisableBuyButton = c.Boolean(nullable: false), - DisableWishlistButton = c.Boolean(nullable: false), - AvailableForPreOrder = c.Boolean(nullable: false), - CallForPrice = c.Boolean(nullable: false), - Price = c.Decimal(nullable: false, precision: 18, scale: 4), - OldPrice = c.Decimal(nullable: false, precision: 18, scale: 4), - ProductCost = c.Decimal(nullable: false, precision: 18, scale: 4), - SpecialPrice = c.Decimal(precision: 18, scale: 4), - SpecialPriceStartDateTimeUtc = c.DateTime(), - SpecialPriceEndDateTimeUtc = c.DateTime(), - CustomerEntersPrice = c.Boolean(nullable: false), - MinimumCustomerEnteredPrice = c.Decimal(nullable: false, precision: 18, scale: 4), - MaximumCustomerEnteredPrice = c.Decimal(nullable: false, precision: 18, scale: 4), - HasTierPrices = c.Boolean(nullable: false), - HasDiscountsApplied = c.Boolean(nullable: false), - Weight = c.Decimal(nullable: false, precision: 18, scale: 4), - Length = c.Decimal(nullable: false, precision: 18, scale: 4), - Width = c.Decimal(nullable: false, precision: 18, scale: 4), - Height = c.Decimal(nullable: false, precision: 18, scale: 4), - AvailableStartDateTimeUtc = c.DateTime(), - AvailableEndDateTimeUtc = c.DateTime(), - DisplayOrder = c.Int(nullable: false), - Published = c.Boolean(nullable: false), - Deleted = c.Boolean(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - UpdatedOnUtc = c.DateTime(nullable: false), - DeliveryTimeId = c.Int(), - BasePriceEnabled = c.Boolean(nullable: false), - BasePriceMeasureUnit = c.String(maxLength: 50), - BasePriceAmount = c.Decimal(precision: 18, scale: 4), - BasePriceBaseAmount = c.Int(), - BundleTitleText = c.String(maxLength: 400), - BundlePerItemShipping = c.Boolean(nullable: false), - BundlePerItemPricing = c.Boolean(nullable: false), - BundlePerItemShoppingCart = c.Boolean(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.DeliveryTime", t => t.DeliveryTimeId) - .ForeignKey("dbo.Download", t => t.SampleDownloadId) - .Index(t => t.DeliveryTimeId) - .Index(t => t.SampleDownloadId); - - CreateTable( - "dbo.Discount", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 200), - DiscountTypeId = c.Int(nullable: false), - UsePercentage = c.Boolean(nullable: false), - DiscountPercentage = c.Decimal(nullable: false, precision: 18, scale: 4), - DiscountAmount = c.Decimal(nullable: false, precision: 18, scale: 4), - StartDateUtc = c.DateTime(), - EndDateUtc = c.DateTime(), - RequiresCouponCode = c.Boolean(nullable: false), - CouponCode = c.String(maxLength: 100), - DiscountLimitationId = c.Int(nullable: false), - LimitationTimes = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.Category", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 400), - Description = c.String(maxLength: 4000), - Alias = c.String(maxLength: 100), - CategoryTemplateId = c.Int(nullable: false), - MetaKeywords = c.String(maxLength: 400), - MetaDescription = c.String(maxLength: 4000), - MetaTitle = c.String(maxLength: 400), - ParentCategoryId = c.Int(nullable: false), - PictureId = c.Int(), - PageSize = c.Int(nullable: false), - AllowCustomersToSelectPageSize = c.Boolean(nullable: false), - PageSizeOptions = c.String(maxLength: 200), - PriceRanges = c.String(maxLength: 400), - ShowOnHomePage = c.Boolean(nullable: false), - HasDiscountsApplied = c.Boolean(nullable: false), - SubjectToAcl = c.Boolean(nullable: false), - LimitedToStores = c.Boolean(nullable: false), - Published = c.Boolean(nullable: false), - Deleted = 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.Picture", t => t.PictureId) - .Index(t => t.PictureId); - - CreateTable( - "dbo.Picture", - c => new - { - Id = c.Int(nullable: false, identity: true), - PictureBinary = c.Binary(), - MimeType = c.String(nullable: false, maxLength: 40), - SeoFilename = c.String(maxLength: 300), - IsNew = c.Boolean(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.Product_Picture_Mapping", - c => new - { - Id = c.Int(nullable: false, identity: true), - ProductId = c.Int(nullable: false), - PictureId = c.Int(nullable: false), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Picture", t => t.PictureId, cascadeDelete: true) - .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) - .Index(t => t.PictureId) - .Index(t => t.ProductId); - - CreateTable( - "dbo.DiscountRequirement", - c => new - { - Id = c.Int(nullable: false, identity: true), - DiscountId = c.Int(nullable: false), - DiscountRequirementRuleSystemName = c.String(maxLength: 4000), - SpentAmount = c.Decimal(nullable: false, precision: 18, scale: 4), - BillingCountryId = c.Int(nullable: false), - ShippingCountryId = c.Int(nullable: false), - RestrictedToCustomerRoleId = c.Int(), - RestrictedProductIds = c.String(maxLength: 4000), - RestrictedPaymentMethods = c.String(maxLength: 4000), - RestrictedShippingOptions = c.String(maxLength: 4000), - RestrictedToStoreId = c.Int(), - ExtraData = c.String(maxLength: 4000), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Discount", t => t.DiscountId, cascadeDelete: true) - .Index(t => t.DiscountId); - - CreateTable( - "dbo.DeliveryTime", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 50), - ColorHexValue = c.String(nullable: false, maxLength: 50), - DisplayLocale = c.String(maxLength: 50), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.Product_Category_Mapping", - c => new - { - Id = c.Int(nullable: false, identity: true), - ProductId = c.Int(nullable: false), - CategoryId = c.Int(nullable: false), - IsFeaturedProduct = c.Boolean(nullable: false), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Category", t => t.CategoryId, cascadeDelete: true) - .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) - .Index(t => t.CategoryId) - .Index(t => t.ProductId); - - CreateTable( - "dbo.Product_Manufacturer_Mapping", - c => new - { - Id = c.Int(nullable: false, identity: true), - ProductId = c.Int(nullable: false), - ManufacturerId = c.Int(nullable: false), - IsFeaturedProduct = c.Boolean(nullable: false), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Manufacturer", t => t.ManufacturerId, cascadeDelete: true) - .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) - .Index(t => t.ManufacturerId) - .Index(t => t.ProductId); - - CreateTable( - "dbo.Manufacturer", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 400), - Description = c.String(maxLength: 4000), - ManufacturerTemplateId = c.Int(nullable: false), - MetaKeywords = c.String(maxLength: 400), - MetaDescription = c.String(maxLength: 4000), - MetaTitle = c.String(maxLength: 400), - PictureId = c.Int(), - PageSize = c.Int(nullable: false), - AllowCustomersToSelectPageSize = c.Boolean(nullable: false), - PageSizeOptions = c.String(maxLength: 200), - PriceRanges = c.String(maxLength: 400), - LimitedToStores = c.Boolean(nullable: false), - Published = c.Boolean(nullable: false), - Deleted = 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.Picture", t => t.PictureId) - .Index(t => t.PictureId); - - CreateTable( - "dbo.CustomerContent", - c => new - { - Id = c.Int(nullable: false, identity: true), - CustomerId = c.Int(nullable: false), - IpAddress = c.String(maxLength: 200), - IsApproved = c.Boolean(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - UpdatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) - .Index(t => t.CustomerId); - - CreateTable( - "dbo.Customer", - c => new - { - 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), - PasswordFormatId = c.Int(nullable: false), - PasswordSalt = c.String(maxLength: 4000), - AdminComment = c.String(maxLength: 4000), - 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), - CreatedOnUtc = c.DateTime(nullable: false), - LastLoginDateUtc = c.DateTime(), - LastActivityDateUtc = c.DateTime(nullable: false), - BillingAddress_Id = c.Int(), - ShippingAddress_Id = c.Int(), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Address", t => t.BillingAddress_Id) - .ForeignKey("dbo.Address", t => t.ShippingAddress_Id) - .Index(t => t.BillingAddress_Id) - .Index(t => t.ShippingAddress_Id); - - CreateTable( - "dbo.Address", - 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), - 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), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Country", t => t.CountryId) - .ForeignKey("dbo.StateProvince", t => t.StateProvinceId) - .Index(t => t.CountryId) - .Index(t => t.StateProvinceId); - - CreateTable( - "dbo.Country", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 100), - AllowsBilling = c.Boolean(nullable: false), - AllowsShipping = c.Boolean(nullable: false), - TwoLetterIsoCode = c.String(maxLength: 2), - ThreeLetterIsoCode = c.String(maxLength: 3), - NumericIsoCode = c.Int(nullable: false), - SubjectToVat = c.Boolean(nullable: false), - Published = c.Boolean(nullable: false), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.ShippingMethod", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 400), - Description = c.String(maxLength: 4000), - DisplayOrder = c.Int(nullable: false), - IgnoreCharges = c.Boolean(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.StateProvince", - c => new - { - Id = c.Int(nullable: false, identity: true), - CountryId = c.Int(nullable: false), - Name = c.String(nullable: false, maxLength: 100), - Abbreviation = c.String(maxLength: 100), - Published = c.Boolean(nullable: false), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Country", t => t.CountryId, cascadeDelete: true) - .Index(t => t.CountryId); - - CreateTable( - "dbo.BlogPost", - c => new - { - Id = c.Int(nullable: false, identity: true), - LanguageId = c.Int(nullable: false), - Title = c.String(nullable: false, maxLength: 4000), - Body = c.String(nullable: false, maxLength: 4000), - AllowComments = c.Boolean(nullable: false), - ApprovedCommentCount = c.Int(nullable: false), - NotApprovedCommentCount = c.Int(nullable: false), - Tags = c.String(maxLength: 4000), - StartDateUtc = c.DateTime(), - EndDateUtc = c.DateTime(), - MetaKeywords = c.String(maxLength: 400), - MetaDescription = c.String(maxLength: 4000), - MetaTitle = c.String(maxLength: 400), - LimitedToStores = c.Boolean(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Language", t => t.LanguageId, cascadeDelete: true) - .Index(t => t.LanguageId); - - CreateTable( - "dbo.Language", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 100), - LanguageCulture = c.String(nullable: false, maxLength: 20), - UniqueSeoCode = c.String(maxLength: 2), - FlagImageFileName = c.String(maxLength: 50), - Rtl = c.Boolean(nullable: false), - LimitedToStores = c.Boolean(nullable: false), - Published = c.Boolean(nullable: false), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.LocaleStringResource", - c => new - { - Id = c.Int(nullable: false, identity: true), - LanguageId = c.Int(nullable: false), - ResourceName = c.String(nullable: false, maxLength: 200), - ResourceValue = c.String(nullable: false, maxLength: 4000), - IsFromPlugin = c.Boolean(), - IsTouched = c.Boolean(), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Language", t => t.LanguageId, cascadeDelete: true) - .Index(t => t.LanguageId); - - CreateTable( - "dbo.News", - c => new - { - Id = c.Int(nullable: false, identity: true), - LanguageId = c.Int(nullable: false), - Title = c.String(nullable: false, maxLength: 4000), - Short = c.String(nullable: false, maxLength: 4000), - Full = c.String(nullable: false, maxLength: 4000), - Published = c.Boolean(nullable: false), - StartDateUtc = c.DateTime(), - EndDateUtc = c.DateTime(), - AllowComments = c.Boolean(nullable: false), - ApprovedCommentCount = c.Int(nullable: false), - NotApprovedCommentCount = c.Int(nullable: false), - LimitedToStores = c.Boolean(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - MetaKeywords = c.String(maxLength: 400), - MetaDescription = c.String(maxLength: 4000), - MetaTitle = c.String(maxLength: 400), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Language", t => t.LanguageId, cascadeDelete: true) - .Index(t => t.LanguageId); - - CreateTable( - "dbo.PollAnswer", - c => new - { - Id = c.Int(nullable: false, identity: true), - PollId = c.Int(nullable: false), - Name = c.String(nullable: false, maxLength: 4000), - NumberOfVotes = c.Int(nullable: false), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Poll", t => t.PollId, cascadeDelete: true) - .Index(t => t.PollId); - - CreateTable( - "dbo.Poll", - c => new - { - Id = c.Int(nullable: false, identity: true), - LanguageId = c.Int(nullable: false), - Name = c.String(nullable: false, maxLength: 4000), - SystemKeyword = c.String(maxLength: 4000), - Published = c.Boolean(nullable: false), - ShowOnHomePage = c.Boolean(nullable: false), - AllowGuestsToVote = c.Boolean(nullable: false), - DisplayOrder = c.Int(nullable: false), - StartDateUtc = c.DateTime(), - EndDateUtc = c.DateTime(), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Language", t => t.LanguageId, cascadeDelete: true) - .Index(t => t.LanguageId); - - CreateTable( - "dbo.CustomerRole", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 255), - FreeShipping = c.Boolean(nullable: false), - TaxExempt = c.Boolean(nullable: false), - TaxDisplayType = c.Int(), - Active = c.Boolean(nullable: false), - IsSystemRole = c.Boolean(nullable: false), - SystemName = c.String(maxLength: 255), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.PermissionRecord", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 4000), - SystemName = c.String(nullable: false, maxLength: 255), - Category = c.String(nullable: false, maxLength: 255), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.ExternalAuthenticationRecord", - c => new - { - Id = c.Int(nullable: false, identity: true), - CustomerId = c.Int(nullable: false), - Email = c.String(maxLength: 4000), - ExternalIdentifier = c.String(maxLength: 4000), - ExternalDisplayIdentifier = c.String(maxLength: 4000), - OAuthToken = c.String(maxLength: 4000), - OAuthAccessToken = c.String(maxLength: 4000), - ProviderSystemName = c.String(maxLength: 4000), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) - .Index(t => t.CustomerId); - - CreateTable( - "dbo.Forums_Post", - c => new - { - Id = c.Int(nullable: false, identity: true), - TopicId = c.Int(nullable: false), - CustomerId = c.Int(nullable: false), - Text = c.String(nullable: false, maxLength: 4000), - IPAddress = c.String(maxLength: 100), - CreatedOnUtc = c.DateTime(nullable: false), - UpdatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Customer", t => t.CustomerId) - .ForeignKey("dbo.Forums_Topic", t => t.TopicId, cascadeDelete: true) - .Index(t => t.CustomerId) - .Index(t => t.TopicId); - - CreateTable( - "dbo.Forums_Topic", - c => new - { - Id = c.Int(nullable: false, identity: true), - ForumId = c.Int(nullable: false), - CustomerId = c.Int(nullable: false), - TopicTypeId = c.Int(nullable: false), - Subject = c.String(nullable: false, maxLength: 450), - NumPosts = c.Int(nullable: false), - Views = c.Int(nullable: false), - LastPostId = c.Int(nullable: false), - LastPostCustomerId = c.Int(nullable: false), - LastPostTime = c.DateTime(), - CreatedOnUtc = c.DateTime(nullable: false), - UpdatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Customer", t => t.CustomerId) - .ForeignKey("dbo.Forums_Forum", t => t.ForumId, cascadeDelete: true) - .Index(t => t.CustomerId) - .Index(t => t.ForumId); - - CreateTable( - "dbo.Forums_Forum", - c => new - { - Id = c.Int(nullable: false, identity: true), - ForumGroupId = c.Int(nullable: false), - Name = c.String(nullable: false, maxLength: 200), - Description = c.String(maxLength: 4000), - NumTopics = c.Int(nullable: false), - NumPosts = c.Int(nullable: false), - LastTopicId = c.Int(nullable: false), - LastPostId = c.Int(nullable: false), - LastPostCustomerId = c.Int(nullable: false), - LastPostTime = c.DateTime(), - DisplayOrder = c.Int(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - UpdatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Forums_Group", t => t.ForumGroupId, cascadeDelete: true) - .Index(t => t.ForumGroupId); - - CreateTable( - "dbo.Forums_Group", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 200), - Description = c.String(maxLength: 4000), - DisplayOrder = c.Int(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - UpdatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "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), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Address", t => t.BillingAddressId) - .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) - .ForeignKey("dbo.Address", t => t.ShippingAddressId) - .Index(t => t.BillingAddressId) - .Index(t => t.CustomerId) - .Index(t => t.ShippingAddressId); - - CreateTable( - "dbo.DiscountUsageHistory", - c => new - { - Id = c.Int(nullable: false, identity: true), - DiscountId = c.Int(nullable: false), - OrderId = c.Int(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Discount", t => t.DiscountId, cascadeDelete: true) - .ForeignKey("dbo.Order", t => t.OrderId, cascadeDelete: true) - .Index(t => t.DiscountId) - .Index(t => t.OrderId); - - CreateTable( - "dbo.GiftCardUsageHistory", - c => new - { - Id = c.Int(nullable: false, identity: true), - GiftCardId = c.Int(nullable: false), - UsedWithOrderId = c.Int(nullable: false), - UsedValue = c.Decimal(nullable: false, precision: 18, scale: 4), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.GiftCard", t => t.GiftCardId, cascadeDelete: true) - .ForeignKey("dbo.Order", t => t.UsedWithOrderId, cascadeDelete: true) - .Index(t => t.GiftCardId) - .Index(t => t.UsedWithOrderId); - - CreateTable( - "dbo.GiftCard", - c => new - { - Id = c.Int(nullable: false, identity: true), - PurchasedWithOrderItemId = c.Int(), - 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), - IsRecipientNotified = c.Boolean(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.OrderItem", t => t.PurchasedWithOrderItemId) - .Index(t => t.PurchasedWithOrderItemId); - - CreateTable( - "dbo.OrderItem", - c => new - { - Id = c.Int(nullable: false, identity: true), - OrderItemGuid = c.Guid(nullable: false), - OrderId = c.Int(nullable: false), - ProductId = c.Int(nullable: false), - Quantity = c.Int(nullable: false), - UnitPriceInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), - UnitPriceExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), - PriceInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), - PriceExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), - DiscountAmountInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), - DiscountAmountExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), - AttributeDescription = c.String(maxLength: 4000), - AttributesXml = c.String(maxLength: 4000), - DownloadCount = c.Int(nullable: false), - IsDownloadActivated = c.Boolean(nullable: false), - LicenseDownloadId = c.Int(), - ItemWeight = c.Decimal(precision: 18, scale: 4), - BundleData = c.String(), - ProductCost = c.Decimal(nullable: false, precision: 18, scale: 4), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Order", t => t.OrderId, cascadeDelete: true) - .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) - .Index(t => t.OrderId) - .Index(t => t.ProductId); - - CreateTable( - "dbo.OrderNote", - c => new - { - Id = c.Int(nullable: false, identity: true), - OrderId = c.Int(nullable: false), - Note = c.String(nullable: false, maxLength: 4000), - DisplayToCustomer = c.Boolean(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Order", t => t.OrderId, cascadeDelete: true) - .Index(t => t.OrderId); - - CreateTable( - "dbo.RewardPointsHistory", - c => new - { - Id = c.Int(nullable: false, identity: true), - CustomerId = c.Int(nullable: false), - Points = c.Int(nullable: false), - PointsBalance = c.Int(nullable: false), - UsedAmount = c.Decimal(nullable: false, precision: 18, scale: 4), - Message = c.String(maxLength: 4000), - CreatedOnUtc = c.DateTime(nullable: false), - UsedWithOrder_Id = c.Int(), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) - .ForeignKey("dbo.Order", t => t.UsedWithOrder_Id) - .Index(t => t.CustomerId) - .Index(t => t.UsedWithOrder_Id); - - CreateTable( - "dbo.Shipment", - c => new - { - Id = c.Int(nullable: false, identity: true), - OrderId = c.Int(nullable: false), - TrackingNumber = c.String(maxLength: 4000), - TotalWeight = c.Decimal(precision: 18, scale: 4), - ShippedDateUtc = c.DateTime(), - DeliveryDateUtc = c.DateTime(), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Order", t => t.OrderId, cascadeDelete: true) - .Index(t => t.OrderId); - - CreateTable( - "dbo.ShipmentItem", - c => new - { - Id = c.Int(nullable: false, identity: true), - ShipmentId = c.Int(nullable: false), - OrderItemId = c.Int(nullable: false), - Quantity = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Shipment", t => t.ShipmentId, cascadeDelete: true) - .Index(t => t.ShipmentId); - - CreateTable( - "dbo.ReturnRequest", - c => new - { - Id = c.Int(nullable: false, identity: true), - StoreId = c.Int(nullable: false), - OrderItemId = c.Int(nullable: false), - CustomerId = c.Int(nullable: false), - Quantity = c.Int(nullable: false), - ReasonForReturn = c.String(nullable: false, maxLength: 4000), - RequestedAction = c.String(nullable: false, maxLength: 4000), - CustomerComments = c.String(maxLength: 4000), - StaffNotes = c.String(maxLength: 4000), - ReturnRequestStatusId = c.Int(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - UpdatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) - .Index(t => t.CustomerId); - - CreateTable( - "dbo.ShoppingCartItem", - c => new - { - Id = c.Int(nullable: false, identity: true), - StoreId = c.Int(nullable: false), - ParentItemId = c.Int(), - BundleItemId = c.Int(), - ShoppingCartTypeId = c.Int(nullable: false), - CustomerId = c.Int(nullable: false), - ProductId = c.Int(nullable: false), - AttributesXml = c.String(maxLength: 4000), - CustomerEnteredPrice = c.Decimal(nullable: false, precision: 18, scale: 4), - Quantity = c.Int(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - UpdatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.ProductBundleItem", t => t.BundleItemId) - .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) - .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) - .Index(t => t.BundleItemId) - .Index(t => t.CustomerId) - .Index(t => t.ProductId); - - CreateTable( - "dbo.Product_SpecificationAttribute_Mapping", - c => new - { - Id = c.Int(nullable: false, identity: true), - ProductId = c.Int(nullable: false), - SpecificationAttributeOptionId = c.Int(nullable: false), - AllowFiltering = c.Boolean(nullable: false), - ShowOnProductPage = c.Boolean(nullable: false), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) - .ForeignKey("dbo.SpecificationAttributeOption", t => t.SpecificationAttributeOptionId, cascadeDelete: true) - .Index(t => t.ProductId) - .Index(t => t.SpecificationAttributeOptionId); - - CreateTable( - "dbo.SpecificationAttributeOption", - c => new - { - Id = c.Int(nullable: false, identity: true), - SpecificationAttributeId = c.Int(nullable: false), - Name = c.String(nullable: false, maxLength: 4000), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.SpecificationAttribute", t => t.SpecificationAttributeId, cascadeDelete: true) - .Index(t => t.SpecificationAttributeId); - - CreateTable( - "dbo.SpecificationAttribute", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 4000), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.ProductTag", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 400), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.ProductVariantAttributeCombination", - c => new - { - Id = c.Int(nullable: false, identity: true), - Sku = c.String(maxLength: 400), - Gtin = c.String(maxLength: 400), - ManufacturerPartNumber = c.String(maxLength: 400), - Length = c.Decimal(precision: 18, scale: 4), - Width = c.Decimal(precision: 18, scale: 4), - Height = c.Decimal(precision: 18, scale: 4), - BasePriceAmount = c.Decimal(precision: 18, scale: 4), - BasePriceBaseAmount = c.Int(), - AssignedPictureIds = c.String(maxLength: 1000), - DeliveryTimeId = c.Int(), - IsActive = c.Boolean(nullable: false), - ProductId = c.Int(nullable: false), - AttributesXml = c.String(maxLength: 4000), - StockQuantity = c.Int(nullable: false), - AllowOutOfStockOrders = c.Boolean(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.DeliveryTime", t => t.DeliveryTimeId) - .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) - .Index(t => t.DeliveryTimeId) - .Index(t => t.ProductId); - - CreateTable( - "dbo.Product_ProductAttribute_Mapping", - c => new - { - Id = c.Int(nullable: false, identity: true), - ProductId = c.Int(nullable: false), - ProductAttributeId = c.Int(nullable: false), - TextPrompt = c.String(maxLength: 4000), - IsRequired = c.Boolean(nullable: false), - AttributeControlTypeId = c.Int(nullable: false), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) - .ForeignKey("dbo.ProductAttribute", t => t.ProductAttributeId, cascadeDelete: true) - .Index(t => t.ProductId) - .Index(t => t.ProductAttributeId); - - CreateTable( - "dbo.ProductAttribute", - c => new - { - Id = c.Int(nullable: false, identity: true), - Alias = c.String(maxLength: 100), - Name = c.String(nullable: false, maxLength: 4000), - Description = c.String(maxLength: 4000), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.ProductVariantAttributeValue", - c => new - { - Id = c.Int(nullable: false, identity: true), - ProductVariantAttributeId = c.Int(nullable: false), - Alias = c.String(maxLength: 100), - Name = c.String(maxLength: 4000), - ColorSquaresRgb = c.String(maxLength: 100), - PriceAdjustment = c.Decimal(nullable: false, precision: 18, scale: 4), - WeightAdjustment = c.Decimal(nullable: false, precision: 18, scale: 4), - IsPreSelected = c.Boolean(nullable: false), - DisplayOrder = c.Int(nullable: false), - ValueTypeId = c.Int(nullable: false), - LinkedProductId = c.Int(nullable: false), - Quantity = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Product_ProductAttribute_Mapping", t => t.ProductVariantAttributeId, cascadeDelete: true) - .Index(t => t.ProductVariantAttributeId); - - CreateTable( - "dbo.Download", - c => new - { - Id = c.Int(nullable: false, identity: true), - DownloadGuid = c.Guid(nullable: false), - UseDownloadUrl = c.Boolean(nullable: false), - DownloadUrl = c.String(maxLength: 4000), - DownloadBinary = c.Binary(), - ContentType = c.String(maxLength: 4000), - Filename = c.String(maxLength: 4000), - Extension = c.String(maxLength: 4000), - IsNew = c.Boolean(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.TierPrice", - c => new - { - Id = c.Int(nullable: false, identity: true), - ProductId = c.Int(nullable: false), - StoreId = c.Int(nullable: false), - CustomerRoleId = c.Int(), - Quantity = c.Int(nullable: false), - Price = c.Decimal(nullable: false, precision: 18, scale: 4), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.CustomerRole", t => t.CustomerRoleId, cascadeDelete: true) - .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) - .Index(t => t.CustomerRoleId) - .Index(t => t.ProductId); - - CreateTable( - "dbo.Store", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 400), - Url = c.String(nullable: false, maxLength: 400), - SslEnabled = c.Boolean(nullable: false), - SecureUrl = c.String(maxLength: 400), - Hosts = c.String(maxLength: 1000), - LogoPictureId = c.Int(nullable: false), - DisplayOrder = c.Int(nullable: false), - HtmlBodyId = c.String(maxLength: 4000), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.StoreMapping", - c => new - { - Id = c.Int(nullable: false, identity: true), - EntityId = c.Int(nullable: false), - EntityName = c.String(nullable: false, maxLength: 400), - StoreId = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.ThemeVariable", - c => new - { - Id = c.Int(nullable: false, identity: true), - Theme = c.String(maxLength: 400), - Name = c.String(maxLength: 400), - Value = c.String(maxLength: 2000), - StoreId = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.BackInStockSubscription", - c => new - { - Id = c.Int(nullable: false, identity: true), - StoreId = c.Int(nullable: false), - ProductId = c.Int(nullable: false), - CustomerId = c.Int(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) - .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) - .Index(t => t.CustomerId) - .Index(t => t.ProductId); - - CreateTable( - "dbo.ManufacturerTemplate", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 400), - ViewPath = c.String(nullable: false, maxLength: 400), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.CategoryTemplate", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 400), - ViewPath = c.String(nullable: false, maxLength: 400), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.ProductTemplate", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 400), - ViewPath = c.String(nullable: false, maxLength: 400), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.GenericAttribute", - c => new - { - Id = c.Int(nullable: false, identity: true), - EntityId = c.Int(nullable: false), - KeyGroup = c.String(nullable: false, maxLength: 400), - Key = c.String(nullable: false, maxLength: 400), - Value = c.String(nullable: false, maxLength: 4000), - StoreId = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.AclRecord", - c => new - { - Id = c.Int(nullable: false, identity: true), - EntityId = c.Int(nullable: false), - EntityName = c.String(nullable: false, maxLength: 400), - CustomerRoleId = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.UrlRecord", - c => new - { - Id = c.Int(nullable: false, identity: true), - EntityId = c.Int(nullable: false), - EntityName = c.String(nullable: false, maxLength: 400), - Slug = c.String(nullable: false, maxLength: 400), - IsActive = c.Boolean(nullable: false), - LanguageId = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.ScheduleTask", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 4000), - Seconds = c.Int(nullable: false), - Type = c.String(nullable: false, maxLength: 4000), - Enabled = c.Boolean(nullable: false), - StopOnError = c.Boolean(nullable: false), - LastStartUtc = c.DateTime(), - LastEndUtc = c.DateTime(), - LastSuccessUtc = c.DateTime(), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.Affiliate", - c => new - { - Id = c.Int(nullable: false, identity: true), - AddressId = c.Int(nullable: false), - Deleted = c.Boolean(nullable: false), - Active = c.Boolean(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Address", t => t.AddressId) - .Index(t => t.AddressId); - - CreateTable( - "dbo.MeasureWeight", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 100), - SystemKeyword = c.String(nullable: false, maxLength: 100), - Ratio = c.Decimal(nullable: false, precision: 18, scale: 8), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.MeasureDimension", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 100), - SystemKeyword = c.String(nullable: false, maxLength: 100), - Ratio = c.Decimal(nullable: false, precision: 18, scale: 8), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.Currency", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 50), - CurrencyCode = c.String(nullable: false, maxLength: 5), - Rate = c.Decimal(nullable: false, precision: 18, scale: 4), - DisplayLocale = c.String(maxLength: 50), - CustomFormatting = c.String(maxLength: 50), - LimitedToStores = c.Boolean(nullable: false), - Published = c.Boolean(nullable: false), - DisplayOrder = c.Int(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - UpdatedOnUtc = c.DateTime(nullable: false), - DomainEndings = c.String(maxLength: 1000), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.Forums_Subscription", - c => new - { - Id = c.Int(nullable: false, identity: true), - SubscriptionGuid = c.Guid(nullable: false), - CustomerId = c.Int(nullable: false), - ForumId = c.Int(nullable: false), - TopicId = c.Int(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Customer", t => t.CustomerId) - .Index(t => t.CustomerId); - - CreateTable( - "dbo.Forums_PrivateMessage", - c => new - { - Id = c.Int(nullable: false, identity: true), - StoreId = c.Int(nullable: false), - FromCustomerId = c.Int(nullable: false), - ToCustomerId = c.Int(nullable: false), - Subject = c.String(nullable: false, maxLength: 450), - Text = c.String(nullable: false, maxLength: 4000), - IsRead = c.Boolean(nullable: false), - IsDeletedByAuthor = c.Boolean(nullable: false), - IsDeletedByRecipient = c.Boolean(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Customer", t => t.FromCustomerId) - .ForeignKey("dbo.Customer", t => t.ToCustomerId) - .Index(t => t.FromCustomerId) - .Index(t => t.ToCustomerId); - - CreateTable( - "dbo.ActivityLog", - c => new - { - Id = c.Int(nullable: false, identity: true), - ActivityLogTypeId = c.Int(nullable: false), - CustomerId = c.Int(nullable: false), - Comment = c.String(nullable: false, maxLength: 4000), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.ActivityLogType", t => t.ActivityLogTypeId, cascadeDelete: true) - .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) - .Index(t => t.ActivityLogTypeId) - .Index(t => t.CustomerId); - - CreateTable( - "dbo.ActivityLogType", - c => new - { - Id = c.Int(nullable: false, identity: true), - SystemKeyword = c.String(nullable: false, maxLength: 100), - Name = c.String(nullable: false, maxLength: 200), - Enabled = c.Boolean(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.Campaign", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 4000), - Subject = c.String(nullable: false, maxLength: 4000), - Body = c.String(nullable: false, maxLength: 4000), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.EmailAccount", - c => new - { - Id = c.Int(nullable: false, identity: true), - Email = c.String(nullable: false, maxLength: 255), - DisplayName = c.String(maxLength: 255), - Host = c.String(nullable: false, maxLength: 255), - Port = c.Int(nullable: false), - Username = c.String(nullable: false, maxLength: 255), - Password = c.String(nullable: false, maxLength: 255), - EnableSsl = c.Boolean(nullable: false), - UseDefaultCredentials = c.Boolean(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.MessageTemplate", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 200), - BccEmailAddresses = c.String(maxLength: 200), - Subject = c.String(maxLength: 1000), - Body = c.String(maxLength: 4000), - IsActive = c.Boolean(nullable: false), - EmailAccountId = c.Int(nullable: false), - LimitedToStores = c.Boolean(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.NewsLetterSubscription", - c => new - { - Id = c.Int(nullable: false, identity: true), - NewsLetterSubscriptionGuid = c.Guid(nullable: false), - Email = c.String(nullable: false, maxLength: 255), - Active = c.Boolean(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.QueuedEmail", - c => new - { - Id = c.Int(nullable: false, identity: true), - Priority = c.Int(nullable: false), - From = c.String(nullable: false, maxLength: 500), - FromName = c.String(maxLength: 500), - To = c.String(nullable: false, maxLength: 500), - ToName = c.String(maxLength: 500), - ReplyTo = c.String(maxLength: 500), - ReplyToName = c.String(maxLength: 500), - CC = c.String(maxLength: 500), - Bcc = c.String(maxLength: 500), - Subject = c.String(maxLength: 1000), - Body = c.String(), - CreatedOnUtc = c.DateTime(nullable: false), - SentTries = c.Int(nullable: false), - SentOnUtc = c.DateTime(), - EmailAccountId = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.EmailAccount", t => t.EmailAccountId, cascadeDelete: true) - .Index(t => t.EmailAccountId); - - CreateTable( - "dbo.CheckoutAttribute", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 400), - TextPrompt = c.String(maxLength: 4000), - IsRequired = c.Boolean(nullable: false), - ShippableProductRequired = c.Boolean(nullable: false), - IsTaxExempt = c.Boolean(nullable: false), - TaxCategoryId = c.Int(nullable: false), - AttributeControlTypeId = c.Int(nullable: false), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.CheckoutAttributeValue", - c => new - { - Id = c.Int(nullable: false, identity: true), - CheckoutAttributeId = c.Int(nullable: false), - Name = c.String(nullable: false, maxLength: 400), - PriceAdjustment = c.Decimal(nullable: false, precision: 18, scale: 4), - WeightAdjustment = c.Decimal(nullable: false, precision: 18, scale: 4), - IsPreSelected = c.Boolean(nullable: false), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.CheckoutAttribute", t => t.CheckoutAttributeId, cascadeDelete: true) - .Index(t => t.CheckoutAttributeId); - - CreateTable( - "dbo.RecurringPaymentHistory", - c => new - { - Id = c.Int(nullable: false, identity: true), - RecurringPaymentId = c.Int(nullable: false), - OrderId = c.Int(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.RecurringPayment", t => t.RecurringPaymentId, cascadeDelete: true) - .Index(t => t.RecurringPaymentId); - - CreateTable( - "dbo.RecurringPayment", - c => new - { - Id = c.Int(nullable: false, identity: true), - CycleLength = c.Int(nullable: false), - CyclePeriodId = c.Int(nullable: false), - TotalCycles = c.Int(nullable: false), - StartDateUtc = c.DateTime(nullable: false), - IsActive = c.Boolean(nullable: false), - Deleted = c.Boolean(nullable: false), - InitialOrderId = c.Int(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Order", t => t.InitialOrderId) - .Index(t => t.InitialOrderId); - - CreateTable( - "dbo.LocalizedProperty", - c => new - { - Id = c.Int(nullable: false, identity: true), - EntityId = c.Int(nullable: false), - LanguageId = c.Int(nullable: false), - LocaleKeyGroup = c.String(nullable: false, maxLength: 400), - LocaleKey = c.String(nullable: false, maxLength: 400), - LocaleValue = c.String(nullable: false, maxLength: 4000), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Language", t => t.LanguageId, cascadeDelete: true) - .Index(t => t.LanguageId); - - CreateTable( - "dbo.RelatedProduct", - c => new - { - Id = c.Int(nullable: false, identity: true), - ProductId1 = c.Int(nullable: false), - ProductId2 = c.Int(nullable: false), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.CrossSellProduct", - c => new - { - Id = c.Int(nullable: false, identity: true), - ProductId1 = c.Int(nullable: false), - ProductId2 = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.Log", - c => new - { - Id = c.Int(nullable: false, identity: true), - LogLevelId = c.Int(nullable: false), - ShortMessage = c.String(nullable: false, maxLength: 4000), - FullMessage = c.String(maxLength: 4000), - IpAddress = c.String(maxLength: 200), - CustomerId = c.Int(), - PageUrl = c.String(maxLength: 4000), - ReferrerUrl = c.String(maxLength: 4000), - CreatedOnUtc = c.DateTime(nullable: false), - UpdatedOnUtc = c.DateTime(), - Frequency = c.Int(nullable: false), - ContentHash = c.String(maxLength: 40), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) - .Index(t => t.CustomerId); - - CreateTable( - "dbo.Setting", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 200), - Value = c.String(nullable: false, maxLength: 4000), - StoreId = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.TaxCategory", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 400), - DisplayOrder = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.Topic", - c => new - { - Id = c.Int(nullable: false, identity: true), - SystemName = c.String(maxLength: 4000), - IncludeInSitemap = c.Boolean(nullable: false), - IsPasswordProtected = c.Boolean(nullable: false), - Password = c.String(maxLength: 4000), - Title = c.String(maxLength: 4000), - Body = c.String(maxLength: 4000), - MetaKeywords = c.String(maxLength: 4000), - MetaDescription = c.String(maxLength: 4000), - MetaTitle = c.String(maxLength: 4000), - LimitedToStores = c.Boolean(nullable: false), - RenderAsWidget = c.Boolean(nullable: false), - WidgetZone = c.String(maxLength: 4000), - WidgetShowTitle = c.Boolean(nullable: false), - WidgetBordered = c.Boolean(nullable: false), - Priority = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.Discount_AppliedToCategories", - c => new - { - Discount_Id = c.Int(nullable: false), - Category_Id = c.Int(nullable: false), - }) - .PrimaryKey(t => new { t.Discount_Id, t.Category_Id }) - .ForeignKey("dbo.Discount", t => t.Discount_Id, cascadeDelete: true) - .ForeignKey("dbo.Category", t => t.Category_Id, cascadeDelete: true) - .Index(t => t.Discount_Id) - .Index(t => t.Category_Id); - - CreateTable( - "dbo.Discount_AppliedToProducts", - c => new - { - Discount_Id = c.Int(nullable: false), - Product_Id = c.Int(nullable: false), - }) - .PrimaryKey(t => new { t.Discount_Id, t.Product_Id }) - .ForeignKey("dbo.Discount", t => t.Discount_Id, cascadeDelete: true) - .ForeignKey("dbo.Product", t => t.Product_Id, cascadeDelete: true) - .Index(t => t.Discount_Id) - .Index(t => t.Product_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 }) - .ForeignKey("dbo.ShippingMethod", t => t.ShippingMethod_Id, cascadeDelete: true) - .ForeignKey("dbo.Country", t => t.Country_Id, cascadeDelete: true) - .Index(t => t.ShippingMethod_Id) - .Index(t => t.Country_Id); - - CreateTable( - "dbo.CustomerAddresses", - c => new - { - Customer_Id = c.Int(nullable: false), - Address_Id = c.Int(nullable: false), - }) - .PrimaryKey(t => new { t.Customer_Id, t.Address_Id }) - .ForeignKey("dbo.Customer", t => t.Customer_Id, cascadeDelete: true) - .ForeignKey("dbo.Address", t => t.Address_Id, cascadeDelete: true) - .Index(t => t.Customer_Id) - .Index(t => t.Address_Id); - - CreateTable( - "dbo.PermissionRecord_Role_Mapping", - c => new - { - PermissionRecord_Id = c.Int(nullable: false), - CustomerRole_Id = c.Int(nullable: false), - }) - .PrimaryKey(t => new { t.PermissionRecord_Id, t.CustomerRole_Id }) - .ForeignKey("dbo.PermissionRecord", t => t.PermissionRecord_Id, cascadeDelete: true) - .ForeignKey("dbo.CustomerRole", t => t.CustomerRole_Id, cascadeDelete: true) - .Index(t => t.PermissionRecord_Id) - .Index(t => t.CustomerRole_Id); - - CreateTable( - "dbo.Customer_CustomerRole_Mapping", - c => new - { - Customer_Id = c.Int(nullable: false), - CustomerRole_Id = c.Int(nullable: false), - }) - .PrimaryKey(t => new { t.Customer_Id, t.CustomerRole_Id }) - .ForeignKey("dbo.Customer", t => t.Customer_Id, cascadeDelete: true) - .ForeignKey("dbo.CustomerRole", t => t.CustomerRole_Id, cascadeDelete: true) - .Index(t => t.Customer_Id) - .Index(t => t.CustomerRole_Id); - - CreateTable( - "dbo.Product_ProductTag_Mapping", - c => new - { - Product_Id = c.Int(nullable: false), - ProductTag_Id = c.Int(nullable: false), - }) - .PrimaryKey(t => new { t.Product_Id, t.ProductTag_Id }) - .ForeignKey("dbo.Product", t => t.Product_Id, cascadeDelete: true) - .ForeignKey("dbo.ProductTag", t => t.ProductTag_Id, cascadeDelete: true) - .Index(t => t.Product_Id) - .Index(t => t.ProductTag_Id); - - CreateTable( - "dbo.ProductReviewHelpfulness", - c => new - { - Id = c.Int(nullable: false), - ProductReviewId = c.Int(nullable: false), - WasHelpful = c.Boolean(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.CustomerContent", t => t.Id) - .ForeignKey("dbo.ProductReview", t => t.ProductReviewId) - .Index(t => t.Id) - .Index(t => t.ProductReviewId); - - CreateTable( - "dbo.BlogComment", - c => new - { - Id = c.Int(nullable: false), - CommentText = c.String(maxLength: 4000), - BlogPostId = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.CustomerContent", t => t.Id) - .ForeignKey("dbo.BlogPost", t => t.BlogPostId, cascadeDelete: true) - .Index(t => t.Id) - .Index(t => t.BlogPostId); - - CreateTable( - "dbo.NewsComment", - c => new - { - Id = c.Int(nullable: false), - CommentTitle = c.String(maxLength: 4000), - CommentText = c.String(maxLength: 4000), - NewsItemId = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.CustomerContent", t => t.Id) - .ForeignKey("dbo.News", t => t.NewsItemId, cascadeDelete: true) - .Index(t => t.Id) - .Index(t => t.NewsItemId); - - CreateTable( - "dbo.PollVotingRecord", - c => new - { - Id = c.Int(nullable: false), - PollAnswerId = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.CustomerContent", t => t.Id) - .ForeignKey("dbo.PollAnswer", t => t.PollAnswerId, cascadeDelete: true) - .Index(t => t.Id) - .Index(t => t.PollAnswerId); - - CreateTable( - "dbo.ProductReview", - c => new - { - Id = c.Int(nullable: false), - ProductId = c.Int(nullable: false), - Title = c.String(maxLength: 4000), - ReviewText = c.String(maxLength: 4000), - Rating = c.Int(nullable: false), - HelpfulYesTotal = c.Int(nullable: false), - HelpfulNoTotal = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.CustomerContent", t => t.Id) - .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) - .Index(t => t.Id) - .Index(t => t.ProductId); - - #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"); - } - - #endregion - } - - public override void Down() - { - #region Custom - - this.SqlFileOrResource("Indexes.Inverse.sql"); - if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) - { - // do not execute in unit tests - this.SqlFileOrResource("Indexes.SqlServer.Inverse.sql"); - this.SqlFileOrResource("StoredProcedures.Inverse.sql"); - } - - #endregion - - #region auto-generated - - DropForeignKey("dbo.ProductReview", "ProductId", "dbo.Product"); - DropForeignKey("dbo.ProductReview", "Id", "dbo.CustomerContent"); - DropForeignKey("dbo.PollVotingRecord", "PollAnswerId", "dbo.PollAnswer"); - DropForeignKey("dbo.PollVotingRecord", "Id", "dbo.CustomerContent"); - DropForeignKey("dbo.NewsComment", "NewsItemId", "dbo.News"); - DropForeignKey("dbo.NewsComment", "Id", "dbo.CustomerContent"); - DropForeignKey("dbo.BlogComment", "BlogPostId", "dbo.BlogPost"); - DropForeignKey("dbo.BlogComment", "Id", "dbo.CustomerContent"); - DropForeignKey("dbo.ProductReviewHelpfulness", "ProductReviewId", "dbo.ProductReview"); - DropForeignKey("dbo.ProductReviewHelpfulness", "Id", "dbo.CustomerContent"); - DropForeignKey("dbo.Log", "CustomerId", "dbo.Customer"); - DropForeignKey("dbo.LocalizedProperty", "LanguageId", "dbo.Language"); - DropForeignKey("dbo.RecurringPaymentHistory", "RecurringPaymentId", "dbo.RecurringPayment"); - DropForeignKey("dbo.RecurringPayment", "InitialOrderId", "dbo.Order"); - DropForeignKey("dbo.CheckoutAttributeValue", "CheckoutAttributeId", "dbo.CheckoutAttribute"); - DropForeignKey("dbo.QueuedEmail", "EmailAccountId", "dbo.EmailAccount"); - DropForeignKey("dbo.ActivityLog", "CustomerId", "dbo.Customer"); - DropForeignKey("dbo.ActivityLog", "ActivityLogTypeId", "dbo.ActivityLogType"); - DropForeignKey("dbo.Forums_PrivateMessage", "ToCustomerId", "dbo.Customer"); - DropForeignKey("dbo.Forums_PrivateMessage", "FromCustomerId", "dbo.Customer"); - DropForeignKey("dbo.Forums_Subscription", "CustomerId", "dbo.Customer"); - DropForeignKey("dbo.Affiliate", "AddressId", "dbo.Address"); - DropForeignKey("dbo.BackInStockSubscription", "ProductId", "dbo.Product"); - DropForeignKey("dbo.BackInStockSubscription", "CustomerId", "dbo.Customer"); - DropForeignKey("dbo.ProductBundleItemAttributeFilter", "BundleItemId", "dbo.ProductBundleItem"); - DropForeignKey("dbo.ProductBundleItem", "ProductId", "dbo.Product"); - DropForeignKey("dbo.ProductBundleItem", "BundleProductId", "dbo.Product"); - DropForeignKey("dbo.TierPrice", "ProductId", "dbo.Product"); - DropForeignKey("dbo.TierPrice", "CustomerRoleId", "dbo.CustomerRole"); - DropForeignKey("dbo.Product", "SampleDownloadId", "dbo.Download"); - DropForeignKey("dbo.ProductVariantAttributeValue", "ProductVariantAttributeId", "dbo.Product_ProductAttribute_Mapping"); - DropForeignKey("dbo.Product_ProductAttribute_Mapping", "ProductAttributeId", "dbo.ProductAttribute"); - DropForeignKey("dbo.Product_ProductAttribute_Mapping", "ProductId", "dbo.Product"); - DropForeignKey("dbo.ProductVariantAttributeCombination", "ProductId", "dbo.Product"); - DropForeignKey("dbo.ProductVariantAttributeCombination", "DeliveryTimeId", "dbo.DeliveryTime"); - DropForeignKey("dbo.Product_ProductTag_Mapping", "ProductTag_Id", "dbo.ProductTag"); - DropForeignKey("dbo.Product_ProductTag_Mapping", "Product_Id", "dbo.Product"); - DropForeignKey("dbo.Product_SpecificationAttribute_Mapping", "SpecificationAttributeOptionId", "dbo.SpecificationAttributeOption"); - DropForeignKey("dbo.SpecificationAttributeOption", "SpecificationAttributeId", "dbo.SpecificationAttribute"); - DropForeignKey("dbo.Product_SpecificationAttribute_Mapping", "ProductId", "dbo.Product"); - DropForeignKey("dbo.ShoppingCartItem", "ProductId", "dbo.Product"); - DropForeignKey("dbo.ShoppingCartItem", "CustomerId", "dbo.Customer"); - DropForeignKey("dbo.ShoppingCartItem", "BundleItemId", "dbo.ProductBundleItem"); - DropForeignKey("dbo.Customer", "ShippingAddress_Id", "dbo.Address"); - DropForeignKey("dbo.ReturnRequest", "CustomerId", "dbo.Customer"); - DropForeignKey("dbo.Order", "ShippingAddressId", "dbo.Address"); - DropForeignKey("dbo.ShipmentItem", "ShipmentId", "dbo.Shipment"); - DropForeignKey("dbo.Shipment", "OrderId", "dbo.Order"); - DropForeignKey("dbo.RewardPointsHistory", "UsedWithOrder_Id", "dbo.Order"); - DropForeignKey("dbo.RewardPointsHistory", "CustomerId", "dbo.Customer"); - DropForeignKey("dbo.OrderNote", "OrderId", "dbo.Order"); - DropForeignKey("dbo.GiftCardUsageHistory", "UsedWithOrderId", "dbo.Order"); - DropForeignKey("dbo.GiftCardUsageHistory", "GiftCardId", "dbo.GiftCard"); - DropForeignKey("dbo.GiftCard", "PurchasedWithOrderItemId", "dbo.OrderItem"); - DropForeignKey("dbo.OrderItem", "ProductId", "dbo.Product"); - DropForeignKey("dbo.OrderItem", "OrderId", "dbo.Order"); - DropForeignKey("dbo.DiscountUsageHistory", "OrderId", "dbo.Order"); - DropForeignKey("dbo.DiscountUsageHistory", "DiscountId", "dbo.Discount"); - DropForeignKey("dbo.Order", "CustomerId", "dbo.Customer"); - DropForeignKey("dbo.Order", "BillingAddressId", "dbo.Address"); - DropForeignKey("dbo.Forums_Post", "TopicId", "dbo.Forums_Topic"); - DropForeignKey("dbo.Forums_Topic", "ForumId", "dbo.Forums_Forum"); - DropForeignKey("dbo.Forums_Forum", "ForumGroupId", "dbo.Forums_Group"); - DropForeignKey("dbo.Forums_Topic", "CustomerId", "dbo.Customer"); - DropForeignKey("dbo.Forums_Post", "CustomerId", "dbo.Customer"); - DropForeignKey("dbo.ExternalAuthenticationRecord", "CustomerId", "dbo.Customer"); - DropForeignKey("dbo.Customer_CustomerRole_Mapping", "CustomerRole_Id", "dbo.CustomerRole"); - DropForeignKey("dbo.Customer_CustomerRole_Mapping", "Customer_Id", "dbo.Customer"); - DropForeignKey("dbo.PermissionRecord_Role_Mapping", "CustomerRole_Id", "dbo.CustomerRole"); - DropForeignKey("dbo.PermissionRecord_Role_Mapping", "PermissionRecord_Id", "dbo.PermissionRecord"); - DropForeignKey("dbo.PollAnswer", "PollId", "dbo.Poll"); - DropForeignKey("dbo.Poll", "LanguageId", "dbo.Language"); - DropForeignKey("dbo.News", "LanguageId", "dbo.Language"); - DropForeignKey("dbo.BlogPost", "LanguageId", "dbo.Language"); - DropForeignKey("dbo.LocaleStringResource", "LanguageId", "dbo.Language"); - DropForeignKey("dbo.CustomerContent", "CustomerId", "dbo.Customer"); - DropForeignKey("dbo.Customer", "BillingAddress_Id", "dbo.Address"); - DropForeignKey("dbo.CustomerAddresses", "Address_Id", "dbo.Address"); - DropForeignKey("dbo.CustomerAddresses", "Customer_Id", "dbo.Customer"); - DropForeignKey("dbo.Address", "StateProvinceId", "dbo.StateProvince"); - DropForeignKey("dbo.Address", "CountryId", "dbo.Country"); - DropForeignKey("dbo.StateProvince", "CountryId", "dbo.Country"); - DropForeignKey("dbo.ShippingMethodRestrictions", "Country_Id", "dbo.Country"); - DropForeignKey("dbo.ShippingMethodRestrictions", "ShippingMethod_Id", "dbo.ShippingMethod"); - DropForeignKey("dbo.Product_Manufacturer_Mapping", "ProductId", "dbo.Product"); - DropForeignKey("dbo.Product_Manufacturer_Mapping", "ManufacturerId", "dbo.Manufacturer"); - DropForeignKey("dbo.Manufacturer", "PictureId", "dbo.Picture"); - DropForeignKey("dbo.Product_Category_Mapping", "ProductId", "dbo.Product"); - DropForeignKey("dbo.Product_Category_Mapping", "CategoryId", "dbo.Category"); - DropForeignKey("dbo.Product", "DeliveryTimeId", "dbo.DeliveryTime"); - DropForeignKey("dbo.DiscountRequirement", "DiscountId", "dbo.Discount"); - DropForeignKey("dbo.Discount_AppliedToProducts", "Product_Id", "dbo.Product"); - DropForeignKey("dbo.Discount_AppliedToProducts", "Discount_Id", "dbo.Discount"); - DropForeignKey("dbo.Discount_AppliedToCategories", "Category_Id", "dbo.Category"); - DropForeignKey("dbo.Discount_AppliedToCategories", "Discount_Id", "dbo.Discount"); - DropForeignKey("dbo.Category", "PictureId", "dbo.Picture"); - DropForeignKey("dbo.Product_Picture_Mapping", "ProductId", "dbo.Product"); - DropForeignKey("dbo.Product_Picture_Mapping", "PictureId", "dbo.Picture"); - DropIndex("dbo.ProductReview", new[] { "ProductId" }); - DropIndex("dbo.ProductReview", new[] { "Id" }); - DropIndex("dbo.PollVotingRecord", new[] { "PollAnswerId" }); - DropIndex("dbo.PollVotingRecord", new[] { "Id" }); - DropIndex("dbo.NewsComment", new[] { "NewsItemId" }); - DropIndex("dbo.NewsComment", new[] { "Id" }); - DropIndex("dbo.BlogComment", new[] { "BlogPostId" }); - DropIndex("dbo.BlogComment", new[] { "Id" }); - DropIndex("dbo.ProductReviewHelpfulness", new[] { "ProductReviewId" }); - DropIndex("dbo.ProductReviewHelpfulness", new[] { "Id" }); - DropIndex("dbo.Log", new[] { "CustomerId" }); - DropIndex("dbo.LocalizedProperty", new[] { "LanguageId" }); - DropIndex("dbo.RecurringPaymentHistory", new[] { "RecurringPaymentId" }); - DropIndex("dbo.RecurringPayment", new[] { "InitialOrderId" }); - DropIndex("dbo.CheckoutAttributeValue", new[] { "CheckoutAttributeId" }); - DropIndex("dbo.QueuedEmail", new[] { "EmailAccountId" }); - DropIndex("dbo.ActivityLog", new[] { "CustomerId" }); - DropIndex("dbo.ActivityLog", new[] { "ActivityLogTypeId" }); - DropIndex("dbo.Forums_PrivateMessage", new[] { "ToCustomerId" }); - DropIndex("dbo.Forums_PrivateMessage", new[] { "FromCustomerId" }); - DropIndex("dbo.Forums_Subscription", new[] { "CustomerId" }); - DropIndex("dbo.Affiliate", new[] { "AddressId" }); - DropIndex("dbo.BackInStockSubscription", new[] { "ProductId" }); - DropIndex("dbo.BackInStockSubscription", new[] { "CustomerId" }); - DropIndex("dbo.ProductBundleItemAttributeFilter", new[] { "BundleItemId" }); - DropIndex("dbo.ProductBundleItem", new[] { "ProductId" }); - DropIndex("dbo.ProductBundleItem", new[] { "BundleProductId" }); - DropIndex("dbo.TierPrice", new[] { "ProductId" }); - DropIndex("dbo.TierPrice", new[] { "CustomerRoleId" }); - DropIndex("dbo.Product", new[] { "SampleDownloadId" }); - DropIndex("dbo.ProductVariantAttributeValue", new[] { "ProductVariantAttributeId" }); - DropIndex("dbo.Product_ProductAttribute_Mapping", new[] { "ProductAttributeId" }); - DropIndex("dbo.Product_ProductAttribute_Mapping", new[] { "ProductId" }); - DropIndex("dbo.ProductVariantAttributeCombination", new[] { "ProductId" }); - DropIndex("dbo.ProductVariantAttributeCombination", new[] { "DeliveryTimeId" }); - DropIndex("dbo.Product_ProductTag_Mapping", new[] { "ProductTag_Id" }); - DropIndex("dbo.Product_ProductTag_Mapping", new[] { "Product_Id" }); - DropIndex("dbo.Product_SpecificationAttribute_Mapping", new[] { "SpecificationAttributeOptionId" }); - DropIndex("dbo.SpecificationAttributeOption", new[] { "SpecificationAttributeId" }); - DropIndex("dbo.Product_SpecificationAttribute_Mapping", new[] { "ProductId" }); - DropIndex("dbo.ShoppingCartItem", new[] { "ProductId" }); - DropIndex("dbo.ShoppingCartItem", new[] { "CustomerId" }); - DropIndex("dbo.ShoppingCartItem", new[] { "BundleItemId" }); - DropIndex("dbo.Customer", new[] { "ShippingAddress_Id" }); - DropIndex("dbo.ReturnRequest", new[] { "CustomerId" }); - DropIndex("dbo.Order", new[] { "ShippingAddressId" }); - DropIndex("dbo.ShipmentItem", new[] { "ShipmentId" }); - DropIndex("dbo.Shipment", new[] { "OrderId" }); - DropIndex("dbo.RewardPointsHistory", new[] { "UsedWithOrder_Id" }); - DropIndex("dbo.RewardPointsHistory", new[] { "CustomerId" }); - DropIndex("dbo.OrderNote", new[] { "OrderId" }); - DropIndex("dbo.GiftCardUsageHistory", new[] { "UsedWithOrderId" }); - DropIndex("dbo.GiftCardUsageHistory", new[] { "GiftCardId" }); - DropIndex("dbo.GiftCard", new[] { "PurchasedWithOrderItemId" }); - DropIndex("dbo.OrderItem", new[] { "ProductId" }); - DropIndex("dbo.OrderItem", new[] { "OrderId" }); - DropIndex("dbo.DiscountUsageHistory", new[] { "OrderId" }); - DropIndex("dbo.DiscountUsageHistory", new[] { "DiscountId" }); - DropIndex("dbo.Order", new[] { "CustomerId" }); - DropIndex("dbo.Order", new[] { "BillingAddressId" }); - DropIndex("dbo.Forums_Post", new[] { "TopicId" }); - DropIndex("dbo.Forums_Topic", new[] { "ForumId" }); - DropIndex("dbo.Forums_Forum", new[] { "ForumGroupId" }); - DropIndex("dbo.Forums_Topic", new[] { "CustomerId" }); - DropIndex("dbo.Forums_Post", new[] { "CustomerId" }); - DropIndex("dbo.ExternalAuthenticationRecord", new[] { "CustomerId" }); - DropIndex("dbo.Customer_CustomerRole_Mapping", new[] { "CustomerRole_Id" }); - DropIndex("dbo.Customer_CustomerRole_Mapping", new[] { "Customer_Id" }); - DropIndex("dbo.PermissionRecord_Role_Mapping", new[] { "CustomerRole_Id" }); - DropIndex("dbo.PermissionRecord_Role_Mapping", new[] { "PermissionRecord_Id" }); - DropIndex("dbo.PollAnswer", new[] { "PollId" }); - DropIndex("dbo.Poll", new[] { "LanguageId" }); - DropIndex("dbo.News", new[] { "LanguageId" }); - DropIndex("dbo.BlogPost", new[] { "LanguageId" }); - DropIndex("dbo.LocaleStringResource", new[] { "LanguageId" }); - DropIndex("dbo.CustomerContent", new[] { "CustomerId" }); - DropIndex("dbo.Customer", new[] { "BillingAddress_Id" }); - DropIndex("dbo.CustomerAddresses", new[] { "Address_Id" }); - DropIndex("dbo.CustomerAddresses", new[] { "Customer_Id" }); - DropIndex("dbo.Address", new[] { "StateProvinceId" }); - DropIndex("dbo.Address", new[] { "CountryId" }); - DropIndex("dbo.StateProvince", new[] { "CountryId" }); - DropIndex("dbo.ShippingMethodRestrictions", new[] { "Country_Id" }); - DropIndex("dbo.ShippingMethodRestrictions", new[] { "ShippingMethod_Id" }); - DropIndex("dbo.Product_Manufacturer_Mapping", new[] { "ProductId" }); - DropIndex("dbo.Product_Manufacturer_Mapping", new[] { "ManufacturerId" }); - DropIndex("dbo.Manufacturer", new[] { "PictureId" }); - DropIndex("dbo.Product_Category_Mapping", new[] { "ProductId" }); - DropIndex("dbo.Product_Category_Mapping", new[] { "CategoryId" }); - DropIndex("dbo.Product", new[] { "DeliveryTimeId" }); - DropIndex("dbo.DiscountRequirement", new[] { "DiscountId" }); - DropIndex("dbo.Discount_AppliedToProducts", new[] { "Product_Id" }); - DropIndex("dbo.Discount_AppliedToProducts", new[] { "Discount_Id" }); - DropIndex("dbo.Discount_AppliedToCategories", new[] { "Category_Id" }); - DropIndex("dbo.Discount_AppliedToCategories", new[] { "Discount_Id" }); - DropIndex("dbo.Category", new[] { "PictureId" }); - DropIndex("dbo.Product_Picture_Mapping", new[] { "ProductId" }); - DropIndex("dbo.Product_Picture_Mapping", new[] { "PictureId" }); - DropTable("dbo.ProductReview"); - DropTable("dbo.PollVotingRecord"); - DropTable("dbo.NewsComment"); - DropTable("dbo.BlogComment"); - DropTable("dbo.ProductReviewHelpfulness"); - DropTable("dbo.Product_ProductTag_Mapping"); - DropTable("dbo.Customer_CustomerRole_Mapping"); - DropTable("dbo.PermissionRecord_Role_Mapping"); - DropTable("dbo.CustomerAddresses"); - DropTable("dbo.ShippingMethodRestrictions"); - DropTable("dbo.Discount_AppliedToProducts"); - DropTable("dbo.Discount_AppliedToCategories"); - DropTable("dbo.Topic"); - DropTable("dbo.TaxCategory"); - DropTable("dbo.Setting"); - DropTable("dbo.Log"); - DropTable("dbo.CrossSellProduct"); - DropTable("dbo.RelatedProduct"); - DropTable("dbo.LocalizedProperty"); - DropTable("dbo.RecurringPayment"); - DropTable("dbo.RecurringPaymentHistory"); - DropTable("dbo.CheckoutAttributeValue"); - DropTable("dbo.CheckoutAttribute"); - DropTable("dbo.QueuedEmail"); - DropTable("dbo.NewsLetterSubscription"); - DropTable("dbo.MessageTemplate"); - DropTable("dbo.EmailAccount"); - DropTable("dbo.Campaign"); - DropTable("dbo.ActivityLogType"); - DropTable("dbo.ActivityLog"); - DropTable("dbo.Forums_PrivateMessage"); - DropTable("dbo.Forums_Subscription"); - DropTable("dbo.Currency"); - DropTable("dbo.MeasureDimension"); - DropTable("dbo.MeasureWeight"); - DropTable("dbo.Affiliate"); - DropTable("dbo.ScheduleTask"); - DropTable("dbo.UrlRecord"); - DropTable("dbo.AclRecord"); - DropTable("dbo.GenericAttribute"); - DropTable("dbo.ProductTemplate"); - DropTable("dbo.CategoryTemplate"); - DropTable("dbo.ManufacturerTemplate"); - DropTable("dbo.BackInStockSubscription"); - DropTable("dbo.ThemeVariable"); - DropTable("dbo.StoreMapping"); - DropTable("dbo.Store"); - DropTable("dbo.TierPrice"); - DropTable("dbo.Download"); - DropTable("dbo.ProductVariantAttributeValue"); - DropTable("dbo.ProductAttribute"); - DropTable("dbo.Product_ProductAttribute_Mapping"); - DropTable("dbo.ProductVariantAttributeCombination"); - DropTable("dbo.ProductTag"); - DropTable("dbo.SpecificationAttribute"); - DropTable("dbo.SpecificationAttributeOption"); - DropTable("dbo.Product_SpecificationAttribute_Mapping"); - DropTable("dbo.ShoppingCartItem"); - DropTable("dbo.ReturnRequest"); - DropTable("dbo.ShipmentItem"); - DropTable("dbo.Shipment"); - DropTable("dbo.RewardPointsHistory"); - DropTable("dbo.OrderNote"); - DropTable("dbo.OrderItem"); - DropTable("dbo.GiftCard"); - DropTable("dbo.GiftCardUsageHistory"); - DropTable("dbo.DiscountUsageHistory"); - DropTable("dbo.Order"); - DropTable("dbo.Forums_Group"); - DropTable("dbo.Forums_Forum"); - DropTable("dbo.Forums_Topic"); - DropTable("dbo.Forums_Post"); - DropTable("dbo.ExternalAuthenticationRecord"); - DropTable("dbo.PermissionRecord"); - DropTable("dbo.CustomerRole"); - DropTable("dbo.Poll"); - DropTable("dbo.PollAnswer"); - DropTable("dbo.News"); - DropTable("dbo.LocaleStringResource"); - DropTable("dbo.Language"); - DropTable("dbo.BlogPost"); - DropTable("dbo.StateProvince"); - DropTable("dbo.ShippingMethod"); - DropTable("dbo.Country"); - DropTable("dbo.Address"); - DropTable("dbo.Customer"); - DropTable("dbo.CustomerContent"); - DropTable("dbo.Manufacturer"); - DropTable("dbo.Product_Manufacturer_Mapping"); - DropTable("dbo.Product_Category_Mapping"); - DropTable("dbo.DeliveryTime"); - DropTable("dbo.DiscountRequirement"); - DropTable("dbo.Product_Picture_Mapping"); - DropTable("dbo.Picture"); - DropTable("dbo.Category"); - DropTable("dbo.Discount"); - DropTable("dbo.Product"); - DropTable("dbo.ProductBundleItem"); - DropTable("dbo.ProductBundleItemAttributeFilter"); - - #endregion - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201403112331027_Initial.resx b/src/Libraries/SmartStore.Data/Migrations/201403112331027_Initial.resx deleted file mode 100644 index 5b7a6b785e..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403112331027_Initial.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201403210038385_AclPerf.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201403210038385_AclPerf.Designer.cs deleted file mode 100644 index 80b2d5689b..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403210038385_AclPerf.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class AclPerf : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(AclPerf)); - - string IMigrationMetadata.Id - { - get { return "201403210038385_AclPerf"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201403210038385_AclPerf.cs b/src/Libraries/SmartStore.Data/Migrations/201403210038385_AclPerf.cs deleted file mode 100644 index 3d312a1163..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403210038385_AclPerf.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class AclPerf : DbMigration - { - public override void Up() - { - AddColumn("dbo.AclRecord", "IsIdle", c => c.Boolean(nullable: false, defaultValue: false)); - - CreateIndex("AclRecord", "IsIdle", false, "IX_AclRecord_IsIdle", false); - CreateIndex("Category", "SubjectToAcl", false, "IX_Category_SubjectToAcl", false); - CreateIndex("Product", "SubjectToAcl", false, "IX_Product_SubjectToAcl", false); - CreateIndex("UrlRecord", new[] { "EntityId", "EntityName", "LanguageId", "IsActive" }, false, "IX_UrlRecord_Custom_1", false); - } - - public override void Down() - { - DropIndex("UrlRecord", "IX_UrlRecord_Custom_1"); - DropIndex("Product", "IX_Product_SubjectToAcl"); - DropIndex("Category", "IX_Category_SubjectToAcl"); - DropIndex("AclRecord", "IX_AclRecord_IsIdle"); - - DropColumn("dbo.AclRecord", "IsIdle"); - } - - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201403210038385_AclPerf.resx b/src/Libraries/SmartStore.Data/Migrations/201403210038385_AclPerf.resx deleted file mode 100644 index fff04595ca..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403210038385_AclPerf.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201403211655284_CurrencyMorePrecision.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201403211655284_CurrencyMorePrecision.Designer.cs deleted file mode 100644 index f6c260644e..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403211655284_CurrencyMorePrecision.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class CurrencyMorePrecision : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(CurrencyMorePrecision)); - - string IMigrationMetadata.Id - { - get { return "201403211655284_CurrencyMorePrecision"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201403211655284_CurrencyMorePrecision.cs b/src/Libraries/SmartStore.Data/Migrations/201403211655284_CurrencyMorePrecision.cs deleted file mode 100644 index eec02c0a19..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403211655284_CurrencyMorePrecision.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class CurrencyMorePrecision : DbMigration, IDataSeeder - { - public override void Up() - { - AlterColumn("dbo.Currency", "Rate", c => c.Decimal(nullable: false, precision: 18, scale: 8)); - } - - public override void Down() - { - AlterColumn("dbo.Currency", "Rate", c => c.Decimal(nullable: false, precision: 18, scale: 4)); - } - - public void Seed(SmartObjectContext context) - { - context.MigrateSettings(x => { - x.Add("catalogsettings.showvariantcombinationpriceadjustment", true); - x.Add("catalogsettings.enabledynamicpriceupdate", true); - }); - } - - public bool RollbackOnFailure - { - get { return false; } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201403211655284_CurrencyMorePrecision.resx b/src/Libraries/SmartStore.Data/Migrations/201403211655284_CurrencyMorePrecision.resx deleted file mode 100644 index 2c5a08ede4..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403211655284_CurrencyMorePrecision.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201403251610279_AboutPage.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201403251610279_AboutPage.Designer.cs deleted file mode 100644 index 5e8af394b0..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403251610279_AboutPage.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class AboutPage : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(AboutPage)); - - string IMigrationMetadata.Id - { - get { return "201403251610279_AboutPage"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201403251610279_AboutPage.cs b/src/Libraries/SmartStore.Data/Migrations/201403251610279_AboutPage.cs deleted file mode 100644 index 83c4392ecf..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403251610279_AboutPage.cs +++ /dev/null @@ -1,60 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class AboutPage : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Common.TaskSuccessfullyProcessed", - "The task was successfully processed.", - "Der Vorgang wurde erfolgreich ausgefhrt."); - - builder.AddOrUpdate("Common.Description.Hint", - "Description", - "Beschreibung"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.HideBuyButtonInLists", - "Hide buy-button in product lists", - "Verberge Kaufen-Button in Produktlisten", - "Check to hide the buy-button in product lists.", - "Legt fest, ob der Kaufen-Button in Produktlisten ausgeblendet werden soll."); - - builder.AddOrUpdate("Admin.Common.About", - "About", - "ber"); - - builder.AddOrUpdate("Admin.Common.License", - "License", - "Lizenz"); - - builder.AddOrUpdate("Admin.Help.NopCommerceNote", - "SmartStore.NET is a derivation of the ASP.NET open source e-commerce solution {0}.", - "SmartStore.NET ist ein Derivat der ASP.NET Open-Source E-Commerce-Lsung {0}."); - - builder.AddOrUpdate("Admin.Help.OtherWorkNote", - "SmartStore.NET includes works distributed under the licenses listed below. Please refer to the specific resources for more detailed information about the authors, copyright notices and licenses.", - "SmartStore.NET beinhaltet Werke, die unter den unten aufgefhrten Lizenzen vertrieben werden. Bitte beachten Sie die betreffenden Ressourcen fr ausfhrlichere Informationen ber Autoren, Copyright-Vermerke und Lizenzen."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201403251610279_AboutPage.resx b/src/Libraries/SmartStore.Data/Migrations/201403251610279_AboutPage.resx deleted file mode 100644 index 37d1899727..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403251610279_AboutPage.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201403262015448_ImportExcel.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201403262015448_ImportExcel.Designer.cs deleted file mode 100644 index 2cd01fbd4e..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403262015448_ImportExcel.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class ImportExcel : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ImportExcel)); - - string IMigrationMetadata.Id - { - get { return "201403262015448_ImportExcel"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201403262015448_ImportExcel.cs b/src/Libraries/SmartStore.Data/Migrations/201403262015448_ImportExcel.cs deleted file mode 100644 index aa992d333a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403262015448_ImportExcel.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class ImportExcel : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - - builder.AddOrUpdate("Common.Cancelled", - "Cancelled", - "Abgebrochen"); - - builder.AddOrUpdate("Admin.Common.ImportFromExcel.InProgress", - "The import is being performed in the background now. You can view the progress or the result of the last completed import in the import dialog at any time.", - "Der Import luft jetzt im Hintergrund . Sie knnen den Fortschritt bzw. das Ergebnis des letzten Importvorganges jederzeit im Import-Dialog einsehen."); - - builder.AddOrUpdate("Admin.Common.ImportFromExcel.LastResultTitle", - "Last import: {0}{1}.", - "Letzter Import: {0}{1}."); - - builder.AddOrUpdate("Admin.Common.ImportFromExcel.ProcessedCount", - "{0} of {1} rows processed.", - "{0} von {1} Zeilen verarbeitet."); - - builder.AddOrUpdate("Admin.Common.ImportFromExcel.QuickStats", - "{0} new, {1} updated - with {2} warning(s) and {3} error(s).", - "{0} neu, {1} aktualisiert - bei {2} Warnung(en) und {3} Fehler(n)."); - - builder.AddOrUpdate("Admin.Common.ImportFromExcel.ActiveSince", - "Active since: {0}.", - "Aktiv seit: {0}."); - - builder.AddOrUpdate("Admin.Common.ImportFromExcel.CancelPrompt", - "Do you really want to cancel the import? Products imported so far will not be removed.", - "Soll der aktive Importvorgang wirklich abgebrochen werden? Bislang importierte Produkte werden nicht gelscht."); - - builder.AddOrUpdate("Admin.Common.ImportFromExcel.Cancel", - "Cancel import", - "Import abbrechen"); - - builder.AddOrUpdate("Admin.Common.ImportFromExcel.Cancelled", - "Import process has been cancelled", - "Import wurde abgebrochen"); - - builder.AddOrUpdate("Admin.Common.ImportFromExcel.DownloadReport", - "Download full report...", - "Vollstndigen Bericht runterladen..."); - - builder.AddOrUpdate("Admin.Common.ImportFromExcel.NoReportAvailable", - "No report available", - "Kein Bericht verfgbar"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201403262015448_ImportExcel.resx b/src/Libraries/SmartStore.Data/Migrations/201403262015448_ImportExcel.resx deleted file mode 100644 index 0e85523a88..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201403262015448_ImportExcel.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201404032144400_Packaging.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201404032144400_Packaging.Designer.cs deleted file mode 100644 index 4787776402..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404032144400_Packaging.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class Packaging : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(Packaging)); - - string IMigrationMetadata.Id - { - get { return "201404032144400_Packaging"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201404032144400_Packaging.cs b/src/Libraries/SmartStore.Data/Migrations/201404032144400_Packaging.cs deleted file mode 100644 index 74cf935268..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404032144400_Packaging.cs +++ /dev/null @@ -1,144 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class Packaging : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - context.MigrateSettings(x => - { - x.Add("ShoppingCartSettings.ShowDeliveryTimes", true); - x.Add("ShoppingCartSettings.ShowShortDesc", true); - x.Add("ShoppingCartSettings.ShowBasePrice", false); - }); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - - builder.AddOrUpdate("Admin.Packaging.UploadTheme", - "Upload theme", - "Theme hochladen"); - - builder.AddOrUpdate("Admin.Packaging.UploadPlugin", - "Upload plugin", - "Plugin hochladen"); - - builder.AddOrUpdate("Admin.Packaging.Dialog.File", - "Package file", - "Paket Datei"); - - builder.AddOrUpdate("Admin.Packaging.Dialog.Upload", - "Upload & Install", - "Hochladen & installieren"); - - builder.AddOrUpdate("Admin.Packaging.Dialog.ThemeInfo", - "Choose the theme package file (SmartStore.Theme.*.nupkg) to upload to your server. The package will be automatically extracted and installed. If an older version of the theme already exists, it will be backed up for you.", - "Whlen Sie die Theme Paket-Datei (SmartStore.Theme.*.nupkg), die Sie auf den Server hochladen mchten. Das Paket wird autom. extrahiert und installiert. Wenn eine ltere Version des Themes bereits existiert, wird eine Sicherungskopie davon erstellt."); - - builder.AddOrUpdate("Admin.Packaging.Dialog.PluginInfo", - "Choose a plugin package file (SmartStore.Plugin.*.nupkg) to upload to your server. The package will be automatically extracted and installed. If an older version of the plugin already exists, it will be backed up for you.", - "Whlen Sie die Plugin Paket-Datei (SmartStore.Plugin.*.nupkg), die Sie auf den Server hochladen mchten. Das Paket wird autom. extrahiert und installiert. Wenn eine ltere Version des Plugins bereits existiert, wird eine Sicherungskopie davon erstellt."); - - builder.AddOrUpdate("Admin.Packaging.NotAPackage", - "Package file is invalid. Please upload a 'SmartStore.*.nupkg' file.", - "Paket-Datei ist ungltig. Bitte laden Sie eine 'SmartStore.*.nupkg' Datei hoch."); - - builder.AddOrUpdate("Admin.Packaging.InstallSuccess", - "Package was uploaded and installed successfully.", - "Paket wurde hochgeladen und erfolgreich installiert."); - - builder.AddOrUpdate("Admin.Packaging.StreamError", - "Unable to create NuGet package from stream.", - "Stream kann nicht in ein NuGet Paket konvertiert werden."); - - builder.AddOrUpdate("Admin.Packaging.BackupError", - "Unable to backup existing local package directory.", - "Kann bereits vorhandenen Paket-Ordner nicht sichern."); - - builder.AddOrUpdate("Admin.Packaging.UninstallError", - "Unable to un-install local package before updating.", - "Kann bereits vorhandenes Paket nicht deinstallieren."); - - builder.AddOrUpdate("Admin.Packaging.IsIncompatible", - "The package is not compatible the current app version {0}. Please update SmartStore.NET or install another version of this package.", - "Das Paket ist nicht kompatibel mit der aktuallen Programmversion {0}. Bitte aktualisieren Sie SmartStore.NET oder nutzen Sie eine andere, kompatible Paket-Version."); - - builder.AddOrUpdate("Admin.Packaging.NotFound", - "Package not found: {0}", - "Paket nicht gefunden: {0}"); - - builder.AddOrUpdate("Admin.Packaging.TooManyBackups", - "Backup folder {0} has too many backups subfolder (limit is 1.000)", - "Sicherungsordner {0} enthlt zu viele Unterordner (Obergrenze ist 1.000)"); - - builder.AddOrUpdate("Admin.Packaging.RestoreSuccess", - "Successfully restored local package to local folder '{0}'.", - "Lokales Paket im Ordner '{0}' erfolgreich wiederhergestellt."); - - builder.AddOrUpdate("Admin.Packaging.BackupSuccess", - "Successfully backed up local package to local folder '{0}'", - "Lokales Paket im Ordner '{0}' erfolgreich gesichert."); - - builder.AddOrUpdate("Admin.Catalog.Products.Fields.IsShipEnabled") - .Value("de", "Versand erforderlich"); - - - - builder.Delete("Admin.Configuration.Plugins.Description.Step5"); - - builder.AddOrUpdate("Admin.Configuration.Plugins.Description", - "Install or update plugins", - "Plugins installieren oder aktualisieren"); - - builder.AddOrUpdate("Admin.Configuration.Plugins.Description.Step1", - "Use the package uploader or upload the plugin manually - eg. via FTP - to the /Plugins folder in your SmartStore.NET directory.", - "Verwenden Sie den Paket Uploader oder laden Sie das Plugin manuell - bspw. per FTP - in den /Plugins Ordner hoch."); - - builder.AddOrUpdate("Admin.Configuration.Plugins.Description.Step2", - "With manual uploads: restart your application (or click Reload list of plugins button).", - "Bei manuellem Upload: starten Sie die Anwendung neu (oder klicken Sie Plugin-Liste erneut laden)."); - - builder.AddOrUpdate("Admin.Configuration.Plugins.Description.Step3", - "Scroll down the list to find the newly installed plugin.", - "Scrollen Sie runter, um die neu installierten Plugins zu sehen."); - - builder.AddOrUpdate("Admin.Configuration.Plugins.Description.Step4", - "Click the Install button to install the plugin.", - "Klicken Sie Installieren, um das Plugin zu installieren."); - - - - builder.AddOrUpdate("Products.Bundle.BundleIncludes") - .Value("Bundle includes") - .Value("de", "Besteht aus"); - - builder.AddOrUpdate("Basket.Bundle.BundleIncludes") - .Value("Bundle includes") - .Value("de", "Besteht aus"); - - builder.AddOrUpdate("Products.Bundle.YouSave") - .Value("Bundle for just {0} instead of {1}") - .Value("de", "Im Set fr nur {0} statt {1}"); - - - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201404032144400_Packaging.resx b/src/Libraries/SmartStore.Data/Migrations/201404032144400_Packaging.resx deleted file mode 100644 index 0e85523a88..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404032144400_Packaging.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - H4sIAAAAAAAEAOy92XIcOdIueD9m8w4yXZ1zrI9UUneZ9d9WNcdIipRovxY2k5L+7htaMBIk0YqMyIqFSx+bJ5uLeaR5hQEQG/bAFkum0spMlQw4PmwOh8PhcPx//8//+9v/etokLx5AXsAs/f3lm1e/vHwB0jhbw/Tu95dVefs///ryf/1f/+f/8dvpevP04ltL92dMh3Kmxe8v78ty+7fXr4v4Hmyi4tUGxnlWZLflqzjbvI7W2eu3v/zyH6/fvHkNEMRLhPXixW+XVVrCDSB/oD9PsjQG27KKkk/ZGiRF8x2lrAjqi8/RBhTbKAa/v1xtorxclVkOXr2Lyujli6MERqgaK5DcvnwRpWlWRiWq5N++FmBV5ll6t9qiD1Fy9bwFiO42SgrQVP5vPblpO355i9vxus/YQsVVUWYbS8A3f2465jWf3al7X3Ydh7ruFHVx+YxbTbrv95cXebau4vK4StcJOC/B5qgsc3hTleAMJiXIX77gK/G3kyTHAEyvn5CuR2QwfXWCRiDJ7l4NQf/pBQfwp46X3r76Bf/3pxcnVVJWOfg9BVWZR8mfXlxUNwmM/xM8X2U/QPp7WiUJ3UDURJTGfECfUE22IC+fL8Ft0+zz9csXr9l8r/mMXTYqT93w87T889uXLz6jwqObBHT8Q3USadV7kII8KsH6IipRg1OMAcgICKVzZfWdNlyqHqnr9GBA36Kk8kY7Ly5ygKYniFH3tFDHWZaAKB0G+xw9wDvS0cp+e/niEiSEpLiH21oUDLLkNZ3/LM82l1liMEmobNerrMpjVOOrzD7vVZTfgZJt7W+v+zlrN5NDTt3DXFWX1XSW74yoOzsQ2N+rqKm7D8o7WMQZWpVblHcghpsoefkCzd0YNtrBX1++WMURxvyLKd4FQFMkLaM7YD3zOUT8b4uBFnakoLx88Sl6+gjSu/Ie1egXpJKcwSewbr80qF9TiPQZlKnMq8FCVvdZXr4DRZzDbb0MKwtEPwMUWEuGTlAUvr30Aa7B1X21uUkjmPiCfUMDj6h9YYhwKO4dhL/IVNskev6Sr7G24sPuJznAAuBL+rWMO5ZHX64gZjJLsK/btQeYcoHjVo9irmWuXapclrl2ibRb1JsSzBp8zeXRNJIlHV6/OXrZmq1rjF0zjBpgXnVdpW0VDX/14qBUaMRj3UW4OF9d4CLKUZnv86zaooqE0S+aReA8XcMHuEZ79OR5IUu5ZanTr+2oflOWd7TewPQk22xAr8eNVljLtWCDlmT/LScanMcv6YdsAy4C6IqfQBmhKf6Y5etidJ0RFzblMOPyrmCZhJhCAwyVJNnjCRH9IL8EDxA8eiuoR9ttnj2A9SWSnendqtr48c3nrAyM2MJdIfGecI32rmM40FV18y+A5l92FHvr+R/hBpa4cmQl8x7i1Y9q/EkXpdVtFGOFIUerXvkZbXn6TcFoxb5HLDZ6IefFe3hbnkS594apxQmhW1yCPyqYgy/lPerxWvh7c0qD2asqOmn95pcwQq3CmmwJY6zJHK3XXB2823RevMse0ySL/Le7DY7vyH1Nk3qCt4DebUSjUk+4L7cCpqP9qYE5fdrCnKjV76JnHtMM4igu4QOBCMH2H6JiFSEdB4QaVRZNqN5gbb4WID+6ywGg1TzXyjBgV+BpfL3xvLgEcZXX6L7SowE6eY4TUFfKV8bRiBcgh5n37OswydpPgD3nynmxuofb0xRn8WbH8+IMDT8G3AYYEiRPIZ55UdIintxjA4S5Wdm6+lfR0+kT2oV4zwUEdIJ2MndZ/uw75kg7QbuY8/QBzSoEhzT2e39GQspZ/COYtR8bUgni0UMEUV6YUKieBlppTZ3XGpgGbPnH7LFuNV4kEJrvoCDdHt4+k533WZa3dTwGaOPkeUYUxT8ybOjGLhHeB7p4I4cRz+u+RJuHbr/qrQwQczwaJbipNmEGqUaMnsIhkg4A6wYMggn0TDQXcAWPq+fjqix7u4DH3MLU32Fxn8CiDAPaTP4EIOZFIpk5WXHFPEGKNYGDsf/REQ0SfPH4kqzHLaDZU5xkhcXBqu2ysEU4UWLZEHPMVYm21+1xlvSEyxzrNF17ILWWqNMUH4cF4a9GbDHIYGSmaATbpGWifcMVBPWc9D9jjor2eL842m4T6K+Ffgfw7n68OcLuDYLDf4frEdE/jNs33RLgO9E7IL9ZHu6QP5zfAUiAi+/acp0OxAbCB5A/48yWFonjqACNbA+yH+3wPoGoqHKAdC6dUeLXEKpaV+bRJqQPVAeLf7DQZl1LTvPJUc+AaSaM3bnxdgA5diAIZRFgQHFvhMZc3We1oQFJr3Depc261q1zUieONvW6ob7KeiNy78WhphJcazSktp409JTWeaBcs4SC8wmTrvI7YYkc/WR65xVDVyd3z59BlyaFp5BlixpzEtluqhvUGp00HkA8iaoBAp1jxenTNW3daUJN/WVkqjZIaR3bcQEJjrYJDY2m9hyFquI8mWOdu9NhdZVrEk2NWQJVhTkqx/qS/d0tjAkJ7ciqrr88i6Y9+gyq9g3kcmzvVXSnbdw1QyfKUypZKU5pGkdp+i3KYZSWXbNPss0NTKPG4qeuviafZoAMcqlGySSr41Dx0FYNt2itbROd28WfRGq4kCcVGZGlUPIiR2bLjrTZQVLdLlnW3UKi0MEihaxLjf1d+1sQ1g6vnabY/Tr4vKrLGvAAfTuOB2g7MiH8Ar4WeAMS6G6L+rZMcHNPW5TtVtf6sLIxJjlYgBr7kUPOxpenOMmqbZaeIE7xttkISPITozAHRmRkiDcemWW+PNoj4c6z9ToY2htfZQP7G3E3S2fQbZB7OmFZ0hLbLk6SffsY2/zhNggWAeNtfgPVcD524xlohDyHpB0yQnVTpNRel0/azazH7ZMW4rAUq8ua5zLGpPcicISI0cV3y2uhbkQc7jAEaVl9JSqUY1dj47E8I8FXWlbw38CvbOY2RnGV1SEVeGxnT4sG5wvrFOShnBt4dlxG6Z3WFyfYre6Al4tGOP5e8GWOpR2i/mR3wN2OoRz07OGjKIlSbmyPq8WmtPb9mUVL1NeYTxPUT4HA77pzWwNrhfMTWMPoVZP/oG1qxEndRccwjfLeNbj5y0TNGPLn2gCMqV1SRrlcDLIzmIBUr0z/OdD9sM/g0VqOBj0rE6eqnGLwrIyb127TlsH0D1ZwmMZTBUJSqdPT6QROS5bzbFCcsagmjeUMDnvKPVTXEPFFJEajIEcvFN5hFmvnDemuAPOP7/bLKgGr56IEmwH7Uhh7wmoLRj/SOIZJgv3McCH+loTuflwYuEtQoL6Nycavi9iA5q2lpaKH6SS8bnseZvCoQqNnzDz1fbkpC25HY9j6EbrkZp9uOU6nT0hQ1fFvQ9d08KhhzOMF5S5UexjhtvYwfo8Oiw4SK/hy5ysa6LDaqMsaWAgMPbmtz2+TLP8Ankgk28lLbxTTjxlec8b2YnfVgm33WQHO5Tikw6xRlxVopxXqAOS8OAMRHohWQZjRnqtcqXoONfC67onVbtfdD8WmSCQcYQvn4ic+VN8gu7gL0XHbXzbQaAf5MLp8oLv7p5ARLLca3nBgM+mvOTB/KOahOsMI8sPnroZJ/YPIkkBC5CA9Ds4/IkMdXHOW5ZpzcKbZQWeag3/JHvmX6E652MVdPOOSpQuKgpQoxG6jvrzov8+ocYLpCC/wXf+mfOLd0oiYkywt8enWFLr8kPQOZcLGHTdJzMg6srNfp3wAyfa2Sv4BChKHMQjY58wFy0eHt735q9DbFfeDnW4qN32RgqJAc3XosruQ55qbz4oWKXPoLzers3mdHbSz2kX8tDpH9+uwP1GX1fbR+wp2pda/HQLdDjhnBYr7d7qhXl4ar5iLqCjwBmR04dsWdJblm8jf46dBW0XJ+OvGpA9iBIxEe3R7CxMYYKNKopx672YCadbnRe0QchQzL805+8xP513yMSrK8+3Reo32O+P7BgTdN+Cqf8zuYOp4oxbnb2PlKiHc3dvrLlWoDK3wv6bIKN9wIVX0DhdJbFWdxumnG3tdNXlaSV1ZEnWFOTrbWvO7Dl21G5rrXqcR683TCEqXktD2pgDtuzTQ3RyppLcZCnVns2S2fX36hFWaKDmqynus1NThZi5BXFsHJS3Q5ZAOg1EGYUzMctkOEFIDqs1FVijuonTJ0oaIqUKtJSROVbzKtjDW1JGkqyvJJstrydHYVpOYmOQ1JEnSyrEpQr24ZNsqXQK0y0ixjxVQDTBDIq2inEKoqoLMvsqPUb6+yGBaFh9gUaqO/SV0itqr6SRt0BBbB/dpfBCN1hqBWCL+OBq1AOQJbUUgHYJRHb+Pp5J2v5JI6Hs1pdfWvutQ+5092mhk6asG4LCrV5d1BvOinExzn6SgoV1/oK1BttlG6fME5cid0gd2ZIg7cMjMB5jGtqd6J9QTDyPaA8jUfDNVQW9HL+ifcIt1pSgZiFAUyBR0n6Vg8KW9QI+ntk+MLXy/rd7J1HNIuhS2C21H0y+AXJKwaPPp1ms1PUm1leMoxSoyBMqKslR+EYHa7vLw1m8wDsuzuqyBBdM0XI6LR0bRGD68LagELFQM86vH7CPAnXheZAOC9m0AgXR1nwNgWuCfAxSIBC3IYcwV5vku7bfI28QbzjdkBAdS8fJYd21Nuvmhaa77zLU84iJ1DFNLtkODWaw3pLTkVjSLEe6S5UxKIFZeSuW1j2P7w2HJaAG4oTssHBo5svfepOG8w87vUtTX9euY9k50BlKJEixTyCNe/bMQYW7zm9VOPTRCBukwvdVlBbqqPpN6eXOTgwcYDYiJMLEgF6246Damjuu5MPm1q76Xy1V3pBnA86rBOsx5zZxvusr7TpiB+0QYX/hzHJQRsd5evdc2eF4e/ohfdVqk9AVwmtbHSXbXeWhZT2mcu3hFYSzDj7upzCS+0bjx2ARsPUHVLi8NopSlqL6+7gl7dpKlC6wkJfJmo7oqHjyEAQ5rgbqsj1F6V+F37RdyP8E2flO2DnHE5HTRrGZ278tS7crW4J1InpS0VcazMjxo/fTVyIJvnhdGDlc/l3iJcKQjPGqdkltRnFZD3vipXTJNq9rKZmU1CWpPxdaRSZQu1yyF11rdV8J6rSaBlOC/CfGrFuewZKvLmsfW0o5M0+vaLd8Y5aPcf1RgBaY4rDtLorvzDWosDv880N1Bwnxdlvv7RMAI1q469lo9GJegIKJNLsxllFKJqSUUxLue2uuMSwbtLVUlmAcJOyzrAgRwJX09IEJGehmxLX0oTOJIm6TzAs+ui6S6g6kgRAav3WVVLBM+QVSnQEKBV6nMRIiTULhQ3H8OFSqBglyGtY2pne80/B4VTQPDnclesDfcp7wcr73/P3yn3okBP4PHwt2ki3O/oiCWwWOtRXeSyBpTmo9xT+PbE+HMxy2ilNGpgb3uCXu2lqULTCwl8mbZuiru/IrzHxQldVk7bj1e3Wd5iOloWewZEV1TlxpsRzePkfZntLkvyqJ6MJoHaZnblqVbEmXbFCFRurqyFLZXTKj1WW5pcdICeLuKVlVw2zllSfItK8mWLCahday1AQxRvOKBlqHD4lodpcWjgzOPemvTYcr3NVxHXNP01GZGTSbuYDS0fvtmqmoe415DHBRBPR+O7EM6klpUX0j8cov4r19hF/RkXYZFhGIaNlOkpmGnHpUknW50unWoQG66KoID+ssJIQyggUxxlhNeEuIgG9RlhdokziMf6pBkjaY7/m3sYJu0oM+jk83XexJA5irDknLGU8F5dqNuajsRrzKVnUmQCmh3Vb0XimrBbLhyyASwbGXx8v7H0EFc/zHQQRA7C9C3v/46irUrByDYpe1Q0TgRUCOCMA43BAOyMEj8zTZwZs38nrLeJGqm6egaSxmQb2BRDITF46nUAf70lKIk0pP7aYMctstFYxBXOUIVOuAgn5zl06gK3jyysX+nbvSSPaJzBpvEgp5jNuedJrEuUqaXtqEDPsxuDa8HunU4TVS2dpTr1t3CCWJWtUU2ismEJX/BvEy4b5qijuIYFMU0BV7gG8tolzlmaHG3m5wjhQzmhaxdoGEnWdvF93UQrCRv0ccYPohQdVkkOLH3q8GB5HAgVybbfdPF8J3zMPEedv6muHXYbl5yaCJ7m1awj6k9UEWaUFZJKji3upoUkb88ayrjJdAIxkGiqcsivbQYiYZHCwP4AjXx8nTOI4bXhuwPM5sw/j7V/wbBoycEjtHsFMFAgRNqiFu8Wu7anRbsx4Jg90aCVNYq3lGwWhOGKtgQSWtHfuqqVhP4LwGe0v8g+NVlkQ56n2fVduRj4ZEuV03pjfi5f/nEq6OCLA5YggbZh+zjEvFzPTTcz2G1NL+miThpTqXJpTlN4C/Nm0p4iXSCcZDrGiGz99L4J5zjmie3TOe39LEtmQBwmt/NYFhP7fq5rvp/h1mtLot00ERPd5CyvN8GJg1eimGBffnR27rAvuxl+SgP6V4cy7UKVpEwaBfRM773ERSsDlQ94auy3eWNKs9BGj9P8oRPW9hl1DtmvgMx3OBn7C9y9ItIsZdv/vryxQrHKPj95V+d5wLrOeU7Tt+ickqxsqpuSiQSk/M0Tq4wrFFn/cW6WUxhp0/TFHaFC0NjQ15inqqFTKHTtLSROtO0sCls7JYx8goJdohX7Sg5A2DsZqpLHrvNCBtLrPGjSpKRHJ1V2kkwbilkvo1WBNJIq3QN1kebUVtCP7H6HeQAsV2AyN73IP6RVeVRifjopirBlJtCofDivzYTPB3ZrMmhbtgc3d7CBKI5GUxx3o7eB+RqCtb28bOxOUDy6wTxFqtUOLMUQqL9w8cbSFzlSfRToW/Gu0MfFT/AWjUko7bw5OFh/Gc6cUGnT1uYkx32pywt7ycu8x8gGr8/6flVv6/zDtxA7wsYFNRRTNbND1mynoA/xIInYkyq4OMo/THJzpArcxIRQ5d5fjJlcd3TNtMVeX4Tja9cYM9NNP/qYJ5XeZQWUYx/9qv05EVPwr2qwi9BUSXjh1E7ibbY2Dtth4uFTtRatJfv1OVpm3xR5fF9VIApjcwXEXQNRMU/Qjn2uDTF4a0yfiq+qi36k1s234EElHO/v6SO6s9Y2KXHVYS9rnnC/shKli4cS0uJbB2gtE5adQky/yw2RVE1Z6+s1nzxtUAbyQ+wKDPFc3IywuvmAK6vrJpKOArUkNq+mPAe3pK9xmAjZITXXwuw/g7Le6Exw9RCowyy2DaO5MIRsTT8TQJmCfXnkoTK8ulONftcB9BR1QwnK2rWJ8lrRqXb1uwSrAHYgDVtcTpVvpRIUw0yxSCx0JjhHNavOaO6q0Oytalit7Mp0nenqWSXWm2NpLFAyUs7jkAh9HgqL18huSB0eIu2himkAvPgbKAuqzvC8jRC1lLN15I5jr7SHxaYrrB9joFFtv0ozBU9ta2+0MiUMRQEo4qzkstppsu1BVcnIhnaYZqry2r7y3eGMitoCDDmvYzgZ18jCZS2N8213T7HgKLbfhTmpZ7aVqBwitYUSrtRg+Q6nZfA8RcyB8GiLqu1J1FSQfYYgpl0CuHkNPJp+nnRVpaEpooCWGlawJOs2k5k7L1EnbGFaIQnsWZ1pU0TTWQFUryPnaJldVHTNOsT2mxR0SVHK+e86LkjIzFRlmqH9DNA2S/JRmYnYQE3bY1ckmrbc63KI7ZIQapclVX0XksyVUOv2wOHl2JMNuOok7w9+4Po+Rf101i+MH+voqb1XpuOFJYXOYxH9/XsChrdnXWK1kzSktb2UOtwYzeJLW3sts3iKTmth+S77DFNsmgd4DGe86IFC6Zmf0QMnBagxbXcomBp+h3Au3uLrcXAPaUqXScAqUHR+AfwtQA+IUHDQvG3UpE5Koosxo6t61aZkJ9KBNRiVJrZkNbjb3+1OAiUHqdIDgqNFcZ6VAeq1VHJKtYkaqrWUvjrfZ9JJHsvvQ9DHPQ+dVlBtLXP1IsD04Waa6+5Zb0zxiL3nAOSwOzgXTrdJAfzTpNNcvDtFYJXgneYgxq+C3SNue7wEBjHURKlMfDcNhWjX1SaysA1kmTQOprJvFFkbmc6OkFsaInDnwgFdtgxaU3A46DWzcblfYHG46Xz8TnIP3VZQXSQqzyKf6Aen8hBmdzzDLu7IzwDXN2e34EEPoD82TH75LqPsfMbP+kVvnG2LnlqR02a4rqXAWIFGQKlkx5L5RVGh4YMIZQORvHhGUn6KYinm+yQexp79uBkCDkPVPNVPlsc9yuIHdNL8Ad+8c7dQMDAHOaBZh6ECNYUbBKE2i6FORy6BFGRpWdZXnPT9GaQhn8BMXsHOSBwHI/++fGxXSjK6Pb2czZFaBJGRIQJfbULUQUHNqZUnyi2pDIKyfZNSua5Z8uIjnOCZLmf2wCPdFggdDMywAJxEeWNmmB50FYfizlkpIc4hCNhMENeGO+HeYLdoCkJcsRD+PR/NMNfmLV7F0Rxz9wKJZ2VUtc0Pa2qK8kkCruaNuhVWqEg2VqiJBqut7NhU3dMKpQiOS1V0QxXOcjZaQOy2qK5dtu8J9bJApeDnaiMkuzulRb3sDyOLs/lHf9ly8aBcFwrcBihM5ighgV4B7p+3b5pdogH7j2CejvNci2ry6a8UQbxgVWjXNYmTg2fOLRWD2faBToUy37RQvntHrRNdRacOtiD3LQVeCM/SDOut8oYEkzeTYq3ouee6sJD8f5Sw08uyrU8bbkKHEoBtM8u6oYOGCMIv+Bi7yDwfgbRpONexbHr9FNOOLL1mLY+O7Wr6M5/W4ZADlPLeWr9JdDDR0NrtXZVvu6HspAst3Syah1laELw5rcoh1HaB84+yTY3MI08NeJh8AMvq8ta/agCsPLAFfMS6s4SwxTyKUqr2yjGg5hfoNEedN4KU2ydN5Tf1ne4Dgf2IeyFoagAxBBv64drCIt/sNBG5y5HRQHvUsT4kAz9+XrgtfgQY976x2FzvO1FroLcKPM2Yu3ksQ4SVfGPMMctxMb4pSq/3BLQ+pzVulPVMbuoEdatsppF55rFEFZg06wqg5Zx/pAnFgZlqw2aBrlcGhvynIMvJ7xactBFRhdqDUwwI98VeCrRp812/MjcOPrJHxXM/e8bU1MlLfMsCeGTMNPJCT+FLESMrVxxPifhec6lPVJbhGke2xbqDX0GTeWBSSg97ZZUmuNaKXkH+0CbX7WptQMJsesNuJgcVhGDVeQogdHAJiCApJ7JvDpa9A5fTamJpBlaXSKwB25Xl6XoN38fjh2aRAOeeVmS5as/qigHxeXdzeiNqo0k63+hmVDfchnJb7G+MThBQecFQkILOYgDxMHx0CE5JCIaQqi1H2H6AzuZzhkmzFbbWo6eZah3milrTitSG1PJYfX5BNYwetUCHNYZzbxt+sg7pt/XPgjW1zzxFici1OgBxo5hGvVPz7V/mRQ3tFClJRoMDDt6W85gAtIpVt/TJ9SmIry+KlumPoNHO34yFjFXEOTNLQRnDbfDOIgZdVmBVuEgl4han3+8rlke84Q55Ah872XwZgVuqFSx6Fj3miXtVQc5haAcKMhCHlH0RUjMhEKipoZBzhUII7rEV8Bfivp/B3mhLmsiVxxeh9FqGyMVuiqS0xTTe+/BViBGvBKmEQMuCFmhvcQd6ED+Y3aXdQ4As51x8G0vN8lxtn4O/qyqnfD5FJFrYZ4yqEE5iCJ1WXX3+/JfjTKPWHPQmsw16HuwAWTff5O4LIgkf/GKgTlwo7os0lGjy/dAfGpg6NOU8jbMIjIm9x9H8Y/zlLgq0Y9ve+wmFYiHKRF0gEfboIYK7zBHNFEF60mv3A/RCvuvwQwh94qqwiQ7xwFS43YE2VXSvs5XYIMUVi/vAhncQZCoy5pHO/sGwSOq6v30JbvuiYz5GTEiuMvy5wC8zEMd+PjAx5PxcSPcA7Axh3Tg4gMXT8bFZChg7OW3mG02WfqKRzqwsbqsMOYj1KL3eVZtp2dLVPIMs3DANDCSG+aYpoKjOLkEceb0cjQx7CO4Vx3IYcaNPePmNNhqT6itvfzO10nXhsD+E19zH6bOXnX5D/y8z/y8Sqq76UsNdSf3Y5TeVdHdWAvDKr4H6yoBV1Hxw+UABWUrXtEgh7mkLmue6x1o/c7SteezW4H8B63FRhjXhDLbfklP8zzzfoLvY0TCrOelwxM2OO9punbMuariGBTFYG5znfD2FibQzaLQ5S36n4eZry7raL3O0eB5+5GABAS4LuG4Mqlfx61bJz0N6djjuqPqzz+EROHEQ6TwO+MAUYH4r30Xy5rv38EcxPgZt1cM0oH11WUNLHqm16NsRf5zUYIN6odHsj+YuvhL3HWGTrZ/XZ6xrmHud2hxadzs/WdKB3aYLIfJsk+T5aTKc5DGLi8S95OkBTlMDufJ8etI1rB6YE4QhK7wkSYGGHtefMwwgH+3mtgUz7J8gwadit8/Vnkf4QYiFrvKandjX4WZTLDifkkXlRf0NAzfRiLb0EYXjWxwN31jqYx4rdp4ukUSjOKVAHUQ0uqy6H7yvtYbyqGRjKB3xK5sC+NddK0U+FfqVKmmEjblGlLPeHnwAfXFJ7TNJ0+yuE5YFucwWzWzNYT3MuahUDP1KguFhPjzXyDWRdf7yzgaI47sN72pHAf5i7z1k/OiMTEePx9V5b2/vZwCvER66xZSMWVcMUeSoTQbKyKh0GLlmqWnA54oySRxTdS0tj7p/dQxqT1Nrax7TzRUc4rSawkgpmn06WPmcrkQ5brD78dTKAfpry6L6iZc5GKun9TvNE8vQ0cSLFw3y49JepprgZ46MFGTiUcnGtqgz2TSBcmEiixdW9vgoqTuxhDiBNMfRIq6rJlNyQMmwrfjlOrkpmBxj2WzjeCdiwGjWZ2LVy3GgXWdeWcs75zBbcpIBeMoEju1xBpPmFPE+8lRHNfPfrhPGhrnMHHUZZF+0kndX0c5HWls6UMi37BwfVk45Mz0LbzIcv7dGlvDfoFGMkwH2dY9KooBHWCkkuvVeFV4h4HEQSXBbYRmNZJaZC6gXCMt8o3Q8bjk14ktDuogudRlzaMuHsdxvbTUHm1Ad0hmWgVfJSNQzKxASsU0rvX0+u4f7tnjwNlYSnwGj8VHgJnc82SzExZyxIPM0MgMaY95H3XOpESFmUlTaPV/r0AF1k03uTM8BXPgcnVZFznMcu9Ys9gGqHX1GWV9xaUO+oqFWO2usukbd5VN0rRLsE2eg7TPqJxJ2nRyMnoRSLUbvYz9UuaC+rGtcKT7HALPy24YRlGhETVK5eELa8+SHMBQa9o1S9wfwqhohIMYJaHXYczJPYh/ZJXXu2X1e6yvBKjDOq4ua0CwjnT5eScft1yhCbXF9Bd1mKRQuOfFVfR0+gSo3nCFQkBtKLJgjzQv/ClPYbZrnoSU015LZE8vF03zCE8/Gmf0eu5RXkpA4Xl4C29IQeF7zHeWzCOSDw/IzS65ppdZvG5nLeycZNYlDhiF+foiesYs8AEW+I6bu9BSAB6klrosvst8hRYZiYVefuDbKp1nCh66FjP388w0jzDPjDMGnWfhJthhZmmY+DlOQL00e84GDHQBcpit/W9NlFFC8HwtHzi0DZ6SIawxoc7pAkU9OU8hPsFfsiSjq2gkxa7ZHGrRxRAOyiuW2tZZWLn+Ty2W+S2btTx3EsvkRjX8N3n8l3SLk+cxwSDErwTAg3hWlxUmXqFb1D0JDrldP1/83K78uYqeKpauUhi1IymVPsLMuu7Je3mjphIEqYbUU9VLyGxonxBxDgHPAh0Eibqsi/ZNnjd+IqDDeTuPcdXc1JhnRYGmReLPZTzUgc+WymcWWo3PhczDRcyBtfLuI3gAifed9/ssL7vwCVPf7ThD5OEKH9jMbbtwkiP7zipvtQ75v98NPIYbpiMQk4M8B/kUZU0Yc2jIwwv8UdUx17x28Vla4m1gVOhfXHHpOrertKortNqrs+GuzK5AE53L4W2W9BbeVXm9Y21wDiJfXdY8Vw/28GkTyifCKYD90ysK4cCxzhy7qw9TkbBeLpyD8xX1/w5so5n7JCLAAPMEUgrTOKnW4DxdQVRk1Nnb3D232luMKKEM4mhgcC0yTF9cwVIb1jNMKZO4CH8CZdRElNCp++EKeweou1UTlDfNWAWOiXoJUiSSj4rvcF0fsnih1Sj/zNLx+6EuCm2VH5l+96v5cYYXqADyweHejWqpOyqKLIZEiLM2ogtInmW+bv7PrTqn6fpFG+NLRt7vP+pxqoOL0ZRo0NBiB7doeUPV+v3l/xBaPVhGd3RHlSEHf/OSXyO/pPWJ8Qt8/Iz9zE6iIo7WYt+jfluzX8huur7pjXY0BVqoYVqKazBMY7iNEqMmcLkN13Bcu64cPuUd2OLJl5ZG42RSgSaLvB5dcVzvDXXWb68pDrRjzMYCbco0/HPpYzAm98S6WMbyGZNtwgyMyY6TEWO2Vu2ZGLPdGA7KSp5Qxoz9PtWcDQVcc8n4y6tXbwRkJ0ZSVWICFlL16y5INbR3ra9OHW3RoGDVq2kNvqGmGnBdJhlTtfQ2TKUtQ8Jghozr1TXNTLfpmDbLeN3SlWAu9gP0SfujuYGEXZIMukWWy6Zngq5XpvWT9KuEbngQncSaUddNIOOMusikHm3+ufW4a8Q38AHkz+QwZ0DDYog1OpyD8sZCy3iNrudoy6euNtNpYdJ+NuIqKt/cnNWpA92SNMACQgYNh7noZ8piLJbRxewUlK2YjkuV42VSBfpy7CK41HAjy9OPzKM7vJdVtWEGBt3B3eynKK1uI7Ipygd3tDJiGWPSdDZcKcWfY3erq8gEfKXr513Y5TZMzTSD4YkBsaTMqJGCrjw3WKSE/3RlLUYuDrZoOgE5OJ4mVaHzLYqzDVd0WZ6J+HmHV3ddO2Zi4B1c5UkgG5jefQLlfba+vgQIDWLnhRNsG9AaH4ezypiYzWXDvwblyfZRJDGgNXJVIqUODdsDYhRw3cIrKy2jlvYLTWjVLdISzHti/gmtbcEEU1k7RkZbyTrLbJO4cW8fZEaOTsaGnae8OQPyqOasF04tV9RhAuZR9OkusQ0reoaGmZ0tYVmIxZYwklZKhmcnaX0mZCppX5uUz2Sc70C8vWjQR69XcYBIKj0U7647WByKi8gSxjJjWpe2H8MkQVpLW8BgNVn64L3AwZt3hTi7HHqjuUXT31sZqi+fQdcfDa1LtwjFyBYxRbfPr0ANtWICiTU0XkbrIXV7bhZ5VYchqJ1J0faC1Ly/5a/iIW0uGb/KMtgwrb5ACef2gQqWxrlGTZmAfY3G0KQedDCQWXj4OMnuLrKiHOZbgVLGqy2RDX+KwDvFk8rqT8CHyjHZGd5rHuS97hhHxyU8sYoDu1d+7ZhQgJfwoYrBl8GHqhZMxIqq8TEpvs0zt0X+EjxA8PgBJNvbKkmxzsskDBnnB7Jr7PRCTgdj/VDpart907rFcbVdw6Yz5BuOs4VNv844G/vjt6fOS7AZVgQEShlTt0Q2TCwC75QioKz+BFypHJOdUARw7du1o2McHZfwxCoOdFAEpPASPlQx+DL4UNWCiVhRNT4mxbd55lMEsiQZloIMlXRZRwRWSzgDuFOST1r1KdZi2RjshMTDNT9Ki0fsBZAJwRlYpqDoVHxWk9hyGw0s0wwlDLwMXpNUfCJuk4yFkYqH6GfltW9ZSUxVcZavrymO0bGHIo+KB2lyW05UFaXgSjm7L4M3B5oyEZ8OjJ0pz9a55uNckG9ggWPdNC1pTf64oeqzMX02Kf9yOaz4V1+a5mQIE4xwgmjWSXLy4OeHM3XG6ROOJBQlR1V5j3kxJoncACn7xii3rKt0GW26z6wCO3XoaNWkCaSk1Rib1Gf248izLK82xOw6yOAiqYybOyob1pVAW/FpEIccdSUm4Cx15+4OG5HQc4Z8xNIqGakJhmfJSRz4TKwkr8VUvCTv4N1hpmvyb/1MhXawKUIlG1lzEA0qYR+qbotbM1X1n4rxJONhUnSfawlCrOYaAyFTN3kM8VUjq5hvoXwnqfq0Ao8ZD2O+W4D6RfHLsJZENTi8CkaBq7hPytcLYUFFGybV4cTxMSmeZJiNFeu31Qw9iWXEMlZswiSbs6EU2NyBOJD+pqvFBIyk612T4tmcM3PU4H6AJQvIRbtp85DXfTKu28kdQxtn6it+66V9RbGLK6biEm0uXaQyOoNLPDd5gZrQY8vjUqOmTMC0RmNoUo/ZQ5VJW1JLPit+4l5UHZl72RdZh2T2QvmWacRcTMuMm0kluoeD51vciZuOnkc5OuXybuv3x+PuDgsqaj7VCi+OxS4x21C4E4FyBIbbxYAmytpPynY7GLzkPbwtTyLsEIGqfB8VYP0dlvc9B6nYZSCfjC3bLDZcOVSMSizKuD/c3XbDWk3Ae4bDYMSJUoTZGZNRIjoWGuIXaS4dU7pqjfoCJeypmgXzy1CjpkzI09oxNKlHm2dZPPyVnmJ2jMxknYyb2VJ3RxE1b8xcTC0dT5PKMBnnVVs/ZyUw2SP1dEqVFZNYq6wU7u6wpqLmUymr4lgsf490CR7R9LnIEEDRzp9B27suk4wNJfQ2DKktbqes9CYtmYBbTcZvJyz4soaYKQKDOY3FnnT/Y1OQ24TxCnqEA1aSW3P6TmLJVDEzba89cqi7s7zIKz7BfJWPw/IXl7be9Z65ZZUhvmCodUxna4uTFyCL5afg6uWwoLQJE3KidIxMyu8A5nUvaCP2mnmscNQBnQ14ZJ+gd15as6omU+nOqj425antAjxXLkFZ5Sl+9QqY3IyQk8vVAYrSTnOWFrFjOrOuDZNoy7px2gk9ua+0odxTZQh+aS+kALTSTDJS7EmU12vZcZWuE6A9m9HkkWspLLmdpqIuSn1+SLVhtMXCoGaTqCGDQ2FSiz7XjCoy15LBZUOZY3Qm3M31Y7AZc/DrTq4iQiuG3CpUGUbn1F30tRhqxBxsuoOeF2xsvCEOlVIPRk50Dpe4k4ypbcEEXKkdox1iydUWxPC2uf1/VJY5vKlKYMqi+twalpVndGDhgRrsIEubtWg6Fjcb411geXlLvmzxn9cKjlSu7vZYUgVDA2OlbDhUR2ZjNpqW808Vj+ZOobG484aRZVGKsNCFRMvgbrJd372zLTnaahnPNfnUn3/G+bd99hXLhG/c51+NM/csbJflq+hOEx9OpNXMG4cZwiCrVTCUHC7iWYP5LcphlJbdsJxkmxuYEsJrNGPgA8ifr+BGE9fWDkfTcRoIhz41rpDs7iXd8NGssI4VnU4u2I6pSc3ofHNPfl37DDd0BhCL4Pgd3t9ZNGsZU2MHd3qKVrnOAgfWD8Dv+8Tky+HsPWTnYWuFKcA8DK41SQht3DWWn8MCYTveFpNgMQYHvnHfoqTqWihwqyVrasEsZgnBCTBV9PVRT5uhWbvY2WPU4PlmkhF/WMwqHmLuyXW9ijbbBLzLHtMki9R3cOXkga0JHLhsg9vWc+zNraIu0zGioqeN7FZMztk47AqCHLU6BkxsfCWHycllHNZR2vCYAt4yUD/hs7klqb4pE7CofqhMKkDnWwCDDm0XBcoR2HIXN4HK2k/KhLu70aMcR+ufhnYLVT7Neqx2zzVYmZXl7RKzGrZluiV+aBRNasJkXBA72zPydCzswrwhdUt1VWbhvX0Qn91+6gwmJY5DPHyhwgLDiCe57F4sqqmKywWMBcre4RbOMReGWcBcJs96seM4in+cp6syi3+sqpsizmHt/TB4v2Moo2wqKPLYzIDBYnfq0odpayZgcdPxtNm0LY6rh5SNgXwT8/Qu6s6GbZmRoXdQizm6vYUJ+gKuhy7ECpQylu2IbJhURJ784QplFSbgJmXHmpQ9941/8mKL3fKuzqJ8lMdV/GmKmulxu7nX4uHO34lVGLX3Ac2YT4j18Wv0Z3m2GeQ7TR755oomt9tKqQuanu0MajPJNmew801qQedbCvNdZbas1+cYlfGoYmZnO7Eu0zOd2O0mdehzzaekIQX7AQ3Ox+zumvqNB1OtrqnzSBW3nsRKddOUIlPiuMovbo9h0J4pNMLhsTPSDdmsi2DfQVEpIx6JYXfTsKNrwcS8uZOq498rUIH16SaCyTX59yjWP3emyiDjSorWhiuVRUg4k6nz4rhzqCUTcOjQeJlUgc43X8yyexD/yCreEVD4rI5hZgggjWkmzWsV4cy0dJn4Fdq4OE63bd4EnG873kbyms88Y3DJuMpRV9xdRM8k6Op5CjHuUJhrTS55qEk2g120SV1h5nGeg+zAjCozAVcajYBRPah8i+HCNqK5wDamPKICMOFNp6cEDIuXcOvQ1JhfKNu2bgb2HxpvkyrxeWebDR+zOErgv8G6reP1xyi9q7DdSsWA6iwyjheobXhdU5SEu7uaL46rh9sxAR8Pj5tJJdo8M3KsgRFiyPhgaXRwNTYswtN/JjvDjtgXTlGe8hnlKVEOkLe3XzZRXn65+ReIS5wEntB4x6RiUZpmJUH529cCnCQ5ZoPi95dlXokiE0OvQGnocFi8fFFnsfIQFJh2oFCjUkxhNWCDEO1j0DKM/v33AZCTqAR3WQ6BDKVJfB5uDYzLKpdiNEmmHaJDYiiMuwdHbYc5wFqCrqcosmFoKgyJFJOJAGPWcO1AsDQG41HTf4rS6jYi3aWbHDTZIPQQphVYK6ukzNetCQMgjQeItOM6p5mhimAWUM0Ckjbc6W0c+0+gvM/WMiSWYhgQyUl8q+ABrRjSmjEExp1NBLJ8MnAkg5DHSXZ3kRVSrDZtEKTViGQgvUo6BIKVM7Aq8Z7gEhRkuZQCSugGwT+Dx0Il+tu04TmZJclRWjwqpmKXagSkgjBmAqxj6Digvnc5VBOQb2BRoLUc7ceyXMrzPM0g6OkTWpbTKDmqynuskNSR39QF6OgHCyPeQCoO7hLNYK6yLYyVOCTVBChSIZjV4n2eVVtlLUjqIBAxMMkwGmud4cpLvcgql6wSwmExK3nvVY4uf7LXEF0HaNZ/KoFBvaJuAoNfFFXC1O+6DsCILwnK+0v64qDBmqfSrPon1AxBVD3GPvY22FrqqR55M5mXlAbrxsbDl9ePf7fATD2Th7bUKGqqoKpDbdCE0JS2Rxsr1akw82JMO4/E3FT2FAl2aQakCcamKUAbfM+tYIvSTIswwbYGlUZnsah8c446tIg0kTOk60YXgGQApLueLkOhQgUMKuKZfEtKEsyyf4qIjFCiNOnDTbpHe1TSpTdyDY4hGNbg5Tc5pAq96jaOxY7xCmy2SSTnRxmdqf3iWQfM0xiLGA0mRzKsV4AUIF7TTkieZnjnGydqvbhLHIT5mmtgusRhNo/vwbpKwFVU/JCyOZU+3LT2Toi0af0lmyHeA1GB+Ok7gHf3Um2AITCFeweROlIopglPY7Aly3OQxgrjQ504rIkJ9xqUewCryct6EMtnAuutPciznfucnGspr0ZzKGJE1sPV/raD4mSzjeCdtPPatOEdLOU7Jd2xMg5sgyxH+lUrOFkSI3PGR1CibfMQv8gpBwugHNBkqIyP4NCA8N430pERnaVsYZVKjMoNbHAbIjv0Vm28FE4NlmWYYJsZ0qhTTnmVJafSg5XFvLnWnHuwFMNDmGdFgTImGkiexqD1UqlkIo3Qr1Kl4NVJw7pd9KQ9BujTh/tbaY2SG6Ko0z3Vmcx1d/JCEUvPZjpK/vhRiLbBZeiOmrvWSA6GhEPNQdT2xJlGVcAx55yI0qGXWlYb7iXZjXlte7ir8oF6ibsKL6KG6KWWcXVcJNCo28CTyvqEnkua7hCgxmWX1uh5fbTdJhCsrzK6nmKnaOnVrdJlk3UWdZqs6SstqqTf1EMQpOd6/jTpt47apn1tppB91mGOO+W6guVH4Zouk2YwaKEsX4COk8JK+k7ezlACnn3RRi3eNS/fyMSw/IEbA44YBpN1Eeu6EKpvOhna6yfK/hFpB5slZNH0k6HUVyKPLcX4gof1BYHUvFXDGoNjd02jNNAmQZ3iIKVTN0ZGLusiztdF0z1SxEnUTqZk1vlGyVDqPIMMoMyqYTHzbhwsRNKlWvRA/To8R6Xkdg0dnqt+HTnNlGV9nK4vQVHmiOnBmnKxEnvRIJe6scOZZX0quGtputOgBNmy0bfYv1tpV6/rziVN0pNSQk3TZPTS/uK80XTdJcUcuYfa2FGavuFJ1C3gKGX9QbkdarqCB5qoEzjPQHVXsITD7WCH1rtbWDhJ5+i5zsUO0brUUxWVWCJEKo0BQSCWWiN6X1edMULEkvSKspN9OuQYJgmScZ3LrKZXOFKD5rA5AvQPBzhRJzWOsf0FGk0vCbTDreKz6Pqpd+M16C4BWCaHVAPg0F8yN9v+Zpyk0/QZ1A3U5pN1n8JRWNOH+iIkHUk5NHt3ZOtFre08kUjdGoFW1kmUX7emY0SoCTrjJNuQy6K9e7m8PwQ6fTt4cu+ZJwWV9I+yq923LpfgAYLHDyDZ3lZJiqUjk6DZxQzlHNxwDAB496phMRrppirGobtb13/t3BSJ1K0TaGX9RV1G0HSUCDXy3MQFtvze34mQ94dAp28HT+7NRVJQSf8ou9plbmZJomUUlkAzBWg6qbGgvh+im0UMxMiM0d9sua5vqMibTpPoa05Rqprf3bMZ6AQaSmYWkfWkYxd8y0qiP2BXumv6to+8O1Tk+vYocvmLXQ22ot8UQ+DSe9wNomvuDpOkA/U5NO3UZpRym3gFStePenzNstW0Ndxub7ATFZQGW7PBTrPc6k3eSborZdqtn1lGdZON8st6dODSnKaXzYocebvY3bTT9q6ESt0ukVjWb/T9P00nScCm6BHiqjXcJRzZQDNYamWntP5jQ73CwU3RLdf0VUZFn9A0Ay2gSBW9EQ13Aw0i6QPmamZI5mhuhmo5o6YxGse6LWF4osaSd4bQo15Cg75gqxMbFJ3JXKfaEkJ0UHAqDpF3sEPf1KHlhi26Ujp1Y2Tksr5pLxBr+kUKNbIVty5TJ085iqHq66SocR9MJTtlt62v+0AxancweQZ1s7T5dO5g/JVuA98weRka37DRerK5G2/YjZKYncPtY0N2jtCBbJjOQWZ2nYLE3qLsL55kYP70lMpZOGwu45Gm6QKNW4lIZFJ5jQuJdUdM4y7SxlS4vkA1vo8KsP4Oy3sqRoLYNUNZ1I0byCnrNioehKbXhoBV7BTKuCgLdXHdh6tQ96E8w3BDpfl0/WchmfRlSLpSOUahevIrPaDG3cnmsmwvk3nMjmULGl3u4YglA6KfIhmQUz2lUuI14VOGJB6FNGYXSEKsaNVRLb26Sbpssp5SRIjRdJq2hJH1WFnZgzN0OJNdYx2njSWu61g5+m2SAzFlF3IU6naxhCrfS+ntDB3OmBOTDjF03QcuUncCSzjcBoZe1yXDKpocUuY7p+pl561z6wc7aFHgCYf2wBy9136axxrZqMDEkxqQ5lJKnYCQZZBLBTbqlVbqSEFHFtp95YdZSElrcFZkwEiWp08TsxMfTOyaDiosk0dqcp0IUeaSyyYhCJpWPqnB1ZtJJsBy+F7UzUs1sUUjdbPTs/+mmqNCwRr7hJLWol0aa4Vnj01ju2B9zobvCHGEGjcBGX1Y37lpu0ge6s+gywYyDjZWn19z1UoZxnC4gwfKHPn6lSZ247Uq4qJkejvAaOanPZpUKOjjWOoEhEMFZAq1GVMEnyf6gJy2k0eL5srd+h6ecJppK2I8pgqm8ggewIQtVQ4ZQzbYBTS1d+QAGksto+r6h+oVTRRV02gLxhCDPWCKpOlpfSzZ4UEwrsJ0kR10VRpev01ye/XK8Eo+5phMqkTxFXHofvc+d+hop95dRpdqdSPjvM6N12pBY/S2VuvhaUbs//q5VkWixVjocay7SQtnMUZtkEn7gdLXQD1og1ziodGsos02AX0sbrVSw1EO6yJsBm/VhoOTLZ59tHHv/uniijOO67L+UVCqGyTPIOsfOvK5pocUgBP42Pcla5YxkcikKZqlyrpbJl2OKENp/XN4hVdmGZwVqpya2aax0VqVMHVnWnWjSweO2XUzdRr3TuLAMYRNdovGq1GMulp8H9Km5zWFT3WMoXhyQXuaMZhH3QVDWaWhApQPSOguxw8VNPJJh6p8jaAYymLfWI3QCNqp0wiQ7k2Ga83Jrkikbo5AK402RD0ToQs3JGCNfIorPLQwfIvLdrKqMylvq5jzkgZ85KnJPidxfZZnG13X6ch1sl6ZS76wcI9gaJcRNfS0XXeVWXQcRWzctj5P4E6jgEfuMup9kGvhrRCJ/NKQa6SPOpdUpjEvpOikmgZXJt+E51KCdp+O2aR0Zg3TsZhTT03FWtQrJ9fsiy9i/yhp1U1SZZH1E/sei6aflKCSvuJeufH3EJM+tHItedtF4jFmmlfj72UIIXXDUL0mo/MvMy1PxqaSR3EC+DGyL8Vcn6ewhFGicanWZdA5IGryyZ0bhXdutP6NOvhxvful7/hci2/wDHemMq95w1UQJl1segvAsERJrw8OqmvUQfpVouGQg3JqdZPVmZTBBvnHlDQdqkEfOUrT0ApuuHIPrdiDK/UYK/Rvr2sA7CUXwRTkXdpvr/GjhJuo+fDba0QSg21ZRcmnbA2Sok1oX+/sczZfXqy2UYzF+f9cvXzxtEnS4veX92W5/dvr1wWBLl5tYJxnRXZbvoqzzetonb1++8sv//H6zZvXmxrjdczsg3/jatuVhOYWGm0uFccPXoMzmBflu6iMbqIC9fjJeiOQrTZRXn65+ReIS+Iu+MTJkt+6Xm4LNLMMyvbwGAjrmC0S/t34p+Fa4JaAV7i6g4ZDHrzv+TPUGVhwkH4BFmZAERKBrtDEi/J22jVNP1+jrsySapP2f/O8q87dV4DHYVPMEbuG8IBMggMe0T2UoF2qOfJ5cZEDJDgADpLOdSKbJGL+9pobYZ6/XgsMxkkCnpPd+DwwY4fg5KlYtymYB6E+204DBaKQaI779yoiPcUC9l/NkfoYGDRS/9Ue6QKghQ9JXqx4yDDpdHN0/C+LV38xR1jdZ3n5DlAvedJoYqo5ci1a6Q0JjSymmiN/gGtwdV9tblLyXigNyyWZY36DBSSvY9No3UeLeVLdJLC450Uc9dmKd7ZJ9NxstziuoVLMEU9ygB/U/JJ+LWMWkU0xR/y6XSsQ2ZSlCfUgotxdgE8stnFLFKK7TbLAjHLUMhK6rHubVQBX0FhPyPN0DR/gGqnfybN0crIEeyI6qyRRAguJFtrdegPTJswxp9kxKfb81Tw0reIxKtmqfx+/pB/Q3u5CWDb5NHPUT6CM/hM8P9ZhMGlMNsUOUTlaQqId7hUs+UWJ+mwx8kmSPXYOW+S+F9d6OYVFCdttnj2A9WWEgwKvcAhCBl5MtpirWTkAL6ewr/1VVkaJvH+kFE5tUBeiJLKYNRXZ0F9lRzGnG7Ep5ogf4QaWuEJkLeNqLCRa1PRHxVUQf7CYH9RTZmi9KT8jtY9XlFQ05qW8R9zEYtZfbHa+fdwldtvbf7eoT5NHtqTzaeaozfuoX8p77ObYuoDQ2HIK6xJ6ZUCOz6RbzN8Kya0IxypGasDRes2h8XN5kNpmdHt/Z3Z0++8WKn+Thx9Z+ruFcp4m9fSknJkZFV2SbjP/nurp9OVWgS+nsO+N06ctzIkZ9130XMh7hqexL4UcCRME2dxSU1lsXKOCd5FnNq9isoU0ZXLytRdTrWr9tUB79bscAFFzFFMt+JPOeIXtzyx7isk287I7T+InJpVgI7+aTCfPcQI+gvSuvOclmIzCtYQLkMNMGEcVjUMpRMEgMIIkllHY9DwOkHKaRmh3JghFJskG8wzxQRt4hQdl02z2Q2uIZ3KUtLlP7vEBE783UlHZ1P8qejp9Qvugkq88lWCOhzK1Dy/zLMIlWelTaC91nj6g2YYy1y+b8uhKIgtJhf1R5bZaLsnaVEbyHz1EMIluYCLAq6ncSpI3Qk5hMQ4w1aCLqRaafPZYt71xfuIHV5ZutcOBt8/EknCW5W39jgHaVwq7HDWhxTFCFP/IsAUUH8wKxwh8ouVeWe1vLWyazV2z1WUSSy4aWripNvJxl1PYlhA9DZXAU1j2G1g3WSG/rEiSrWYdXjOOq+fjqix5I4uYao38HRb3CSxKDTxPYtEztaxJAGL3C7SHEg36cgoLwz7ayZCsMOZWLzbFxrInQFljfEnWEpj+q7Wd8YQ87yixMNYJFmsQjvERJZLasSluiKsyyrHvBcCRF4SDET2lW4mn6dqoPJ7OgsMa0+Bpij08ZIwmI7Ba9bDgYVCAjH20hFa7V8PydIRWuyj6qim3hdLdQtVido8YHG23CeT1bSmBOf53AO/uuQnXfrPQOiR7Jfvd0Xe45kGaTxb9JWnPB+v2dLJaP801ZA5l6Sa4kmjOo+agB+EgAYLXUPdxN4+/NW3tgvYINiguzUZXLkCzAEi25WKqA/InEBVVDr6msFSgMxQOJRxtRJ8cIdEBF//QYtMEtm5O5KhOtG0JidbuU/VbEXKTiILEsQzcB/oiOgrnVvThOrUtockW41LSPyTk41OiekbJwKlEnXUcrxL8r5/vRFtjqaWdS7OyJ6v87LikpTjwtbll4odPs7GwNRqHuBdgUswRG81CwKO/W58FFidZtc1S7K0uPQtk0i1WeQWqG1o7CuSgm5z7qPiVpbA8X69PlOpAfcL5Op24GKnXGJmh9AVgc7HX2qodxJ4662LFnspNyNWhK4GRYGYjn2xMR3Uvqny4ZOkHdys9Vu3+qDqhEVMtkCFxZhEg+882tbwDK/hv3srXfXV0Lyuusvoyhxx/iNa+/l9kpnkh0dL0eRmld7wwZhLmdmQc2/izO45kSzQ1HHz3x/Ddr6Wcp+9+DeLiu6/KOY660RR3DNMof5aK+zbJxsq+AXUoE9ak3n61EA8gO4MJSAWdiEmw8ZD4DLhz4ubTctivPmsKw4UMlvtFkql5MuA1wJCqjLu4ndlm1Gx163gHIcxHFKCHJUmLMg5rtUWrttduaFRDLqsErJ6LEmzEjZwBudXBrNyiwyRYmEphkmCrJ84n7ibEVBvNtHFok0NLkm2MPEWJ9GWi09GhgkWXRjWdS2kXSgdzGYVTCXV8ktr3TV0KR+VSUjsA0h2OhsxtlIgw0A1PR2BhOHwq8wgLKM5u2H9ejkhmnsvwksWaV0ZMhLA2+1JtWyhvln8ATyQ2BG95ZZKsl3USBEeU2HTSHqsKrYtTGHsri+Zidh1EWL7qqbLMudnkzosztAGv+gtEgn86n7z/3EpfvAvCsDSgO9PqUZbPuHT9Ja75TNqBgZ0YOBznerLsHLzqrwaEPuKie0F1RKWiORxT6bEOh0m7d5h0OEQ5HKKw6bZ+G/wL0QGUszpOhbtapsq/fIVMIoqtxXDdetFHlP5ugUbin3BIzTcbV/lke1sl/wAFuQXM+8xzida4nzM1bJcWfsoMcPd5gX9/uf1vgpMSO2f+u4uzEgsxFa+3xQo7Xeq7xUZh2z3lwlSp/2yz6WhD3fC7jf77zynYLWXwcZLdtaG77PlSm3sknqyLEwUek2BxPoKacJEVYihR6vvso8SsdI2kSyXvHdkumlqoUdfPunzFKtonWlzxioqmNdw9L+r77OP4GTwW7rNNm3vc2SYqKmzKfPMXd4osKDb9ffZxv8iS5FuG1ahLEGe58GatybwdhBhpvqJyj9LiUdQC2JTFbVE8PcsVjxNYKGtTa2nvK6jQ0+oUq6sweSoY8/qvFie4GyHKcvPJxrhSFNiCxltV2q/2SGdZvokkYWf5VHvkVZTwMQaYFAtDVvBwq6HDCHWvIAqR/ukECzwcOIY30TXfpjcfnRe1/073zBbbY1yihdeMwonIzVvoY4Q0VPm+ikuaczeEq/Ixu4Op9BqYmGqH3AYcUoILBItZq/o3Qn3WKsXjpwZLlTLnOCsVeVZGZH3qs93Ii1D912nXKCSOt1H6LGi09Uer64cyXzo3/7wS8Tv69gDTWJDRQqJFHYXgTye2wZ5qtnvDr2vtV2ukt1KktzZI/4RbvN2PEvHuJ5dkoRXcZymQRRZmEizmTxsSlZs//ecppPxcej2ZA943RuuZ5KLWq3Iu9TCdHG8WjYev5OSzT7LFlMeN4NMszkEes4+gLNFGssjE6SemWiDf5wDosCXpFmNUoc0VjKXIfJrD3blvEe8CzqTMdDy7ay45LUO2vtQ+0oPFchAiQwBLlSWhHXPCH86f36Woq+vguvwJE5u0HM6kdTBPxqShXPhSn38s43I4ZTfAcnlzk4OH+sFPbmFjUg5i14i52/MzP75uURzPJuVZx+Hm9vlcIS4y9X1a15DjbM1t1Oovtt50talR+uRPl2SB2RzQN3lPROuanMJCFvQP4agLURJZjFF0x3VK/cVqi77oWEs/p2frWF6SO7fl7l8E95HhqmfQDWS4OutSFeW2xidVQq7WS5eCLtHiSC6Ff1RgBSQ7TS7JwpyURHfnG1QfHHJBYpYVky0cCUvOpEo+zD8Hf2qdrL7vuCpz4kVQZFXuu++QIbrMcyOYXdDb2tqL04lNsUeUXI3lkqzubOXZ5iKp7vj36dgUq8PlrIqFeUV9XswsaD2D/Di/RXH045ry+fXl7UzI+7acwlt/sli9qoRbYuov86wFS9fkDzs5XTnL0/h/zh3ZXNfOO09GzwtNHY6jg6cq8zjrAi5R5tQ59Z6pfebzW1byc49L2mPdHHe8P/c58t1uaiL+nFc73TVSU+aP1yXNpFWMEvyVLPjvK1CUSDvHM0uiD7DJcx7sLUWzmtmdHUc1C+PSjpE83Nrl2Zdqj1O//Or67qvCidr1JdZmWohxVfm0qR2pWx9nMt5S7+c6ZWzX57nWY5BvYFEgpbe+b+O5NnNoLuv0IMRS52BYl/cudBi7n+q+LoaDTp9KfGEmOarKe1QojIkDQQhu0iE7cJYd3DhcFvaqeQiX7rZXzte4T24hr8/I0u3RGxE/VIiEzLysL3hgr7IfgNvc098t0Y7iGBSFCpNJtdCcsf8R0g5V8kKWvpjZfpbl1cbf4aWDcZjHmrzjTNqrbAtjHqL7ONfkF68W294pPr+QR6q4WMRtqp0LGkT4krBFgLlBcFwnhyLzOLODlMhDdB9nmx24C2Qv5zEJ1t7qUkd1SyNgLT15+1/z1RzpGwSPHEzzye52mSwqCP3dHk01krJ0e3QS8VmKW6ccZJadzIr8hZWrnJpURL3Ps2orlVNdyi5fNfjcrT28ZGk/zyGk8NSUqk9MwkFgGfLMIdbjSGobEQEB1DaC4yoOFZmXau9a/mWpA3e/JH3mx9h1t9vztCLfOOxMCpPdGmcSLPHEUEPUZ5ujPcmbMQ7vxITdITXXopu9vhAXUEi1Oc6tj5sU0JJky3HB1/gqAZdLsq+vHFZMtTD11e8byYGFRGvc+nqt0o6oIrLnuJMqz0EaP0seDJdS2JRQ57uM+PN5NsW+zuzBomrOiFQWO/ColEkc6rMtX1c3JQ7re57GCaqYjL15CscSTp+GSugo7EsgoYm7J+l0bZFTepaobZuc0rbERiRo2sZTOJagaQtP4Sg9kBSGWFOLkjMApA0yIA9RtrSpBuRWbghYmAiXGduvlmMkHxeX0W75UQLXJ9nWTowdTn+3uZhxW6VrsJY91sin2aA+Rvn6IoNpWXwHOUBjy7uSKUgsVoJ7EP/IqvKoLHN4U5VAuWPRU3qUWPzXJhkoqiaxX99UToaydAunmsDRDjvddKvQWbfWLn1Yc0aCD23hkDg4QSwiW4T1lDZuGPladFrqv9ohiUpa/9USSdJmtxZ+ioofYK3vTRWNXZ1PHh7eijWuv9ohnT5tYU7cNz5laXkvggoErvj/AJGkl/l0Nw5+B3MQl+/ADeTvk6iIbIwpXbYmeueHLJEYVlRUPiXJOEhN5VTScZT+EDcjUgJnfHGySgnc8M9P1NA4zQm1jd2mQu7SndDPbyLe0McnWsyDCml0Ofw3mURXeZQWUYx/CsuOhs6/NJGF9JT+JV6CouJjJw/R2siuLb5xr+lPOYVPCbIWqamsTtw7HUzTIA2ZzWWGPL6PCqC0IkoJbHY9UO6LzyTYW6ya+GpSe1WbZo+Kt0M4qGtV1iuo1spknMnmYCFMbOudC0TSbva+Fkhv/wCL0v8pYwmkS1RQM5hxTPudfYjnCOq75eZYajq2FIS7xlzv4S3R3QMylwTShbnMYMZhrrZsHoX+bnE8WID1d1jeS5lMSLTDlQTGoD7/BIwbhlc9+HOyC8uNskFxi+TlHzWVPffLTkj4NAvdV2IptLcQnhdtDcilrkjQCKQE9m1Hm6KtbCcgS7exb8ZwCxE3iRoTl+SAKbn3wadZaH0gxTqtqNhR323RJBVkEiysU6AohDvB3Ucbbuq7PSN3SwRukhDssVTtJEYA5xDHAEGavCM6ieACFY4dfdL0+uRFwAd3/15FpJdZqP6rheKRwpI8wi09GRRTHZCl535iqk1PqurrWld1PV3r2O5d6mVRWlkFiWsZ0uorSCyW+sGjNd8jNc1RmvMR2rvsMU2yaC2JssQl2SwwbValuiIhsPAmRoyWFqDFEFyVxWSLuiO59x3Au3v+/S/qu4VjWZWuE4BXB86ljPpuLRdP8H0/mWSsE5a1rH4mYXy8l1UM47qsyvOOuaz6L4GfhRgtn93Cslxl3eOUMj9iOnmPNTzafyKMvUdEdDH3GKGMw6lhPWjrJnBSqflmi3KMUvCbFBKwLsnOXiTbhdPfp9+D7dwMwmcbdbhIn1nToji+ozPlO9ihJPlVHsU/UMNkR2l8mgUqdl2T6SlMguV5F5AfzPFpVqdX8AHk8nc6hcSfYPb4GxloJI9ZNKWpoStTcjrbfncwXMjnprXd195AMJsOU1Z5egn+IHH6PJUXCspJbdHmH4mNAl0dCs1AYRWpcPaqSxAVWXqW5fVo8TZyLtEGlww7IHt33sQhJDpcsJGGrBZTbTgnur39LIaapb/btJ9ifvl1JgXJFAucUg3etduaq/uM/HmCRFeIZZNFc1o6hyCWLfcuorxZ+MXbd3SKrXVLhsim2KgKfR/LDmJl6bNteQOeUoxh2W1bhVgY5GBNzPPydrMUc6xVB3nX8s1qC2J42wRk7MfeLxSpDtolLqkd3kieIQGnn7wlX7ZSJ9sBWsv7D2cwQZMPSl/WptKsROjjl7TpBlUMcSbZ2qa8OyH2dYPlqVBokF2UCyu4kRQNaR3M+H/ygCv7wYlj8GAw7tuh8Du7xg2N/CWPxwZY2RGO+zIuzTz3SM87Lt+iHCIltpsJJ9nmBqaR/6oxjO8+jlagI60gPypuscAfLPxQS/5xwvqLxflclFa3UYxvXOVoWyu9f6misXBAAekdf9W2/WaO8h2ueZDmkznGB8kR0Ad7N5WoAGT3JzsoFRIdcPEPLTZNYKFBFwW8S9HOFZLRPOcfSJOl259e4dCEwoUbLs3GOUr2WEf/dX8MEEhOxT/kNgIuyXLP9KUqv9wSiCakm7h1EkmWvsaMsrCEW012cVvfZFJuZWTpFkfw4KlEn8Rngajvdk75f1Qwl/nit98dpvNJlpZ5lshsqSqa/Ve7w865AJNt8ll2lMBIEJvk06TbNseoqcsS3OTW4SjSu77PGEyEK+BGleN8HRQiWEa2a9yM8mb56o8qykFxeXfDHWPwiZY3Lo7W/6qKkvi/iZcu6ESL7QfZJqiAxVSbtewiByuQgFji+c8kTbHWqBDJdJAtjEyCza2E9Ac+RJPqL0LiHjootRcuPKM0NCgukRmUWceRcW154hU6NsXKWbnN+jXn9mF8mn09BUhPvGOYRkKIJS7NRn6mJRpVSYQ5OsEc7wwmIBWkev/VHAk/yZYWgpZCfbaRjZ/BIy8TyafFzOMrCHKyrvhN5A7GYSZr8i5/2xk6xjl+dFXlstKmzeG0IfEoUbqQzHXqhnvd85AN/3Q5U5Pnm/tgRbnyCMuN3ZqwKpLTNLpJeIWP/m6BBuIqB0KdqM8WJnLx+Z4P1m/3ZHdZZ0TmNDs2aU6N9kO5SY6z9TNfRfr7sqZm8ynADG3IXCeqMvs487Uunsfov9oiifOf/j7e0jWXhnIPNoDYDJBg8dRSaCgXTUWffxz2IYVyVuf605RLhiQKl3UErh1huOMo/nGekvMkOsimH+spQB2Y0BhppGP/UB7qAZXwsL7eO3fhkPZxuAIbpE74nnbIEB1Y1QxmqVoyflX2IuK9Nvqve3x2doIG6i7Ln8NwE4/mwEnDEAcuWhwXtT6HQZiIA/NwgTyw0O6w0HuQghzGgQ7xeTSXmKiDEEvfRP4neK4fXGWQ+q9WSALIYU8h5eOjOLkEceYbzLeDceBcTd6ls2xYu8c4pvbz4nydCI6e9bfFsOHXPAgbdjAObKjJ+3Ox4SqpuHuA9Zc5vItVb6rp31Kby5gc34N1lYCrqPjhaUymkFyMydrsS9UmV2j2pbzzfPfRwi4pnN3bHtpLz5BcDpDKbPslPc3zLBf0gD7BZjaQQB55KRiA2BQ7xNN0LcVrv1vWr4pjUBTyGlJpi5m13TuKnhpQC+OiAanzjjNZFQ+BOz0AHupxINmSoV4w5rJogqioclD7JXqaMmkoFxumPv9ipTx5gQptyB6xqsVKRTbJHPMS3y9ksZpPe2yBaMb/HdzU/l9BuLFDc2dIDcSBJ/ecJ0+qPAdp7BvWuIFxiWWszrpU5mtrLD54w6ZYsR7/wk3kFLb7Y4YShSdXmSRb68ZZlm+isoR8qBkx1cbnfQORsnGVNY52nM87l2hxElzdJLC453Ub6vOcflZLilalbHW2iWCKNhLEvYpz0WaSFiPCEAtWm3DuFQKcg0wzwBhHuNFlitcLxNS5HCNIB/FQ3UcLy0G2hTGP033cY0eNi5y8f9KEkPc9D6WxnI5D9QAj8Xog96GzPNuouJtPs+FMFSabYjW3/wXiUpjS9UeLmoEnydVzO2vxJYgkF84jy2OOxuZw/Fw/380DCslO2N2bdEp4imKPJQaxzKCEj5mn3zQF5HR2qMk9ku2sL1Ia0EBMns1fsI5Szd9j29jepN1l5sTIwRiUnCN4MakcYaQVbQSrhv921/ZsZTZHw802gnee2n+L4uRYqMq6VENGKI0CXxJiQeoveyywyCu8RzF5+tGP42gkB67TZx+H8yRvE1u/StwYUUQmZhLsruuJt/WsjEdZzkfJIF8szDEFyFOhQf1Xi7pERSEuAv1XW+m9Kvjx6j9bte8duI2qpESzco24EUZJITRWRrKYedvsUQPdbWDBnE5gBhCWunQcx3EteeoDaN6GK0nexWUpnFsULaYFBzIubTo7+kyT8DN4LD6CsgR5OLOtHNNhSpoCjTQzpaWLplwd3bQLuZ0XyN4ogH+vQAXWpLf8GJcCcuBWbe5xWPQih1kO+Ugg/Vc7y6tob7VFEBez/quNvZa30trlFmvRfrM4Bgbb5JmvSPfRGkesEpNgMTlPuCl5YqkqCMrBbqoD4Q9wV2hOX5FHpTkf2u6zHZakatTnqdSVuSxA9yD+kVWh4rQKcC42oWGMpWr4S45TTJ4WxhvYi/oiphxbTWVT76vo6fQJCN3AJFj0avTUXkAWzgXZJAsN7BC12UgcBIi4K8cMIRgmjbIrlC4cX8kIppQ+h+i1OznnLnGkNfz03EX0jEv9AAvE+L7epXJUF2dTY6Rx5h1fPI8mSzdHrx+i5iC7j3u8F+e7LSy3BWCzyeT6c5wA2StDTIIl3gVAW/u1sEKwSTZb5TJKSG5u08Mk2LiBoeFDAwfEnQ+TMofVN9RNq/MU4oMU6QTn0/Z4nhMXdvhvEhAdN8R3XeHxXFYUA4yRDmGDXUZ3u7KtRCP3DOShQfg0B1QFoD2WJGQIk7AYrr8E+GiwfQTAd22jsZxWNj3AWCbnJpTfG34j0H93QHurQHu71zr6SZ4VBdqNJEE4ikdz2QsPQuwvV822kno6Abs5/07o9IuK+ggeQCIsatR3G4Njlpft/QbOyEinWJxcVUkiBWQSLFTEbeNrwXVa/3kup2b8WrwQkr37aLN5vgV5DnIBi0nYr+t4Zzn4oyK3Y7kDzu6zjXM5eQ/lQ1Twm0M6YTEiCn0ooXeM9xrEJSKPKudSz2t+ohB4/UmJ7w6QOnNxCeKuy71UPtk5fZVc5/QcZgzhMsDyfOMMbX3FQxxg+ruNyShOqjU4T1cQ5Y64jbiYanXQ0fgho4RSftwhEszh+3wFSz4YQvNpSq+RT6CMmos7nHLGptghKh9BFRLtcCV9Rn2ezjVVrQWmSDwdFd/h+g6UvCLIplkcC5Ic/8xSrun0d1s0tDN4lPSmkGiLe5xhAc1POj5tTH++sReCo6LIYohj76Tia5GwIJ5H10fbbQIxD1GqgO59SG0+4UnIhlq1vKPVZy2uq8oCrldZlUtftDNaRzpo2YKCO7Ori2c1r6IcTxzHaradZVnN315Lx9uHJTrLlhVD9LlU7KAwUBl1c4u+aF7oKunHCQ3M1IxAHL7Qn2jBus/W15egKHOI1Y8T3MohCWGSW9glMnlqQhNhMVyWJ5twBQRgFoMqe4qPuvemZprWqHVN3VXSBToTqcWIZjVNa3kb7lsR1XP4O8AAAy+pnN9ANzizDfQxTBL0uTOMGow2n0U15DZjzWJ69ikHFnLYOWg/xrSu2EmWriEezxfnxecqSX5/eRslBbBsvTfzXIB8AwscJLMOOX9Nh/fX8tBQTiGYEkdPkxuwl744z9ETwAPw2UCFPdcUCms2eWPMKqocKmljyRpy+CWuNPvCApfgMcrXFxlMy6JxNb3+WoD1d1jeN2ZWna/GYGbRO0PIYsAXgwV5jgCLFYBPhiu8xEVqqBvCCZxWVbfRcIQ8IVQcDtSTj3i0kBKHx14iAw2331/NaTbuzf+voju9biMjF6NDksSexkSLEYF9VRcnk4Rx5YLYSxBS8BWqtbXiQ/4IpiDnSTpjbvOl+7toP+ABje7Ap2wNkqLPh9+s2USkRcU2iskOfg3OYF5gp+voJipATfLyBar7A0RSrz3MagwufyQnSR13sSX4FKXwFhTlVfYDpL+/fPvLm7cvXxwlMCrwiXxy+/LF0yZJi7/FZN5GaZqVpOm/v7wvy+3fXr8uSInFqw2M86zIbstXcbZ5Ha2z1wjrz6/fvHkN1pvXfPYG1gjll/9oUYpizVy6p44kWU45rtJ1As5Rw7v7VGcwKXlZ+tt/AoH72lG/BLcvVLLlt9d8xt8k/Iar9ftLiHubzFDy/CDx5YxwGIkUUwHSgJcvsAjCFyc7MfRaC9+3jyuI7+e/nadr8PT7y/9NMv7txfl/XdN5//SCrI1/e/HLi//buhLMVTWqDq4wxHnCD4u791UD3cAhIPr4xI699oSfLvqXu+2YqcvoxUl1fzpXgsvuVZW/V1HThe5c2Fr5W4w1iOEmSrDQRb8KIj3f/BWNInZ+//3lX+gCylx0IFLho418jAac+C2asrqIhv9t86cPUR7fR2ij8yl6qm8vofr98ottDYmPJnMmzuL/t0309N9tQWsJTsUa8Gj0B7T2Xd1Xm5uUBN9xB/qGxvMm8RoA6rEEdxDWlcidd1lPzIZ/0ZcSYi6xBGOdMK3AbEXyfgniNliA+zheRDmqArn1010e8UNsOB2JX4g0xypKkucpxY4R6DhyB1GHxjxab2DaxZkOANgyThPX0G+osa/Ml/QD2qdfeC4urMNV2EVG8LsK0I2U51XYyh4lSfbYWUbBAwSPXgvY0XaLdnBgjR/MSu9W1cZnvD9nZVC8FoxcMOYa61m/UJBNOKur7Cj2Wv8Fxzp3qNWPKvgUidLqNorLKgc5WhHKz0jj6fWDUKW8RywTGvO8eA9vy5Mo99KKWgz/9bQJZfSlvAf5BePs5FazNjRStzTrxeObXxxEToU3TiWM8VJ9tF5zRXpV/7x4lz2mSRb5Ka0Nht/QfE2TehK2cF4tQ51ez5IvtwKe02awATl92sKc7GDfRc8qRKPdXwNIgiMQQH/u/hAVqwhpDiDEqLJI9tt5Pr9qP2/SWahhOBr40V0OAK1rubSLAapf8AmgceB4bE3sEj950oAwgUd85B2Nd9GFHQmAyIQd8bH14eOZ7lkMH1l2hga1PevxUsrW9ZlSlLRoJ/f4mMLFEGRYdSomnnu9uUB47mOC9A0cviJ9QDMEgdWei557kTKLfwSyymELCcE7eoggygsTCtPD6iKto9PiA9Ng7f2YPdZtbZ7W8RsGpILD22eybT3L8rZ+xwDtanxgj6P4B7lBgc+6PM8R8A4L453XfchGOvcYFLL6oJGBm2oTYmBqvOgpFB5pOFg3UBCE1yQRm2Pi4+r5uCrLfqvtOGUw9XdY3CewKP0Bm7mcAMSXSK4yFlAXvBOkLhMoGPuZdWmA4JL/S7Iet4Bml3BC3nQZqYzVFuFEiXNDjI4iqDK6+GRXcAMGbNK22KfpOiByayk6TUuQF9682EgvBhWMzECNhJu0TKTuX0FQz12/86GoaE/Ziuaujg9cHeZ0tGaz6n5w+O9wPSL6h3H7plseQk/+DjjszA91kBfmVLGNnOixnC7kQFHaOPgA8mc8cPZWCja3j43iOCpAs4R472U7rE8gKqocfE2hYKZg1MFfrZXBroijzZhODl0x+AdblIPRrHYRISc7MsuNr7W68UABOfYJCmFJYABxN4TEW91ntXEiyi3MBsYn8p37y34cyQ8eVL91OKhu+8jfdPu1AGH8gNReRcHX5LYodwliaDqiwxH7LcvNIh8AqTmGKU6yapul2IPXa3kVUFS7flcvM3LCSVZBP0btcfCSaWH6NRY88tgR+yt4XDxkgjuyYFfx0KzXDmQYb5aDDwrjExbG0n8BiVeBg8dwm9FHZcZuSSv4b+DTAsYjp7jKardxHtnJ8tdgfGFtzybKg5mrF9KKL6P0bsDS6+a3G8jxK7ABZYHuOkvZYP+8nr+1JNmT1b5pzTFMo7w7DkLT+oZ8cFtIUGdjnAEpYT8ZQXYGE5AO6Sd/dnH7+gwezWeDrZf4nrHMvLd2xlMBRpR71laMZrsmPiq0s2zTtszB5tnl9B05vmsvqwTQYVcDqNGrLRjdwtCEwWkCafk63TcuQyHA+mBhVxkdj0OJama5aEEvlF6rTkNFAdfPd9WOQ6HB2x5W6OWe6I1O6dXDp09lHuGr2g51M5ds1MnFnoi0QVPJr/ZazkmWZPkH8NTEdA8L3ixe9ftHgQ9oRl4YW8eQ/TK2zaxLKYwyBuX3Ob0qcF6coe1f1V8PmH5LasuB9OWWAxeG4EK6R+3rwOb+abhxD9lwJ04e6H4/HBSEPSg4GPcHa7d7xv2D7VuJs3O27y4gaf2S2J6sO/Rzd5ZacJfTT+/YMhEcw03r86K9wL4PvpTWbLpn/Pm+gl1BVQr/qAAkkLcQiyF7B6588CjF5dbO6YaKbRQMtX8WKoAG04KdZfkm8oxP02KtoiTMzdzgMVoC3dY8ur2FCfTWdsm9QC9VKMACfl7UVv+jmImY5uQiEPb44GNUlMrlwAkxqPTG1fuY3cE0kFcixmtvihpA2pyO0NFz7VZ1AcBnWyEL5mtXHRHBvD7GS6b0DZOdXTFJcNqgczIYmHR5dJvY2WYbpc+BsGSnfyYacJvRa44geBxI8wGmscP2n8vuU5MT6lK05ypO5tObkGBvg4D9E24vsgLHwpB4b7upU/dZCuQxntwi37WxcOZe/8w3HLIHwXZWeg7aYd842GGJaaxollYvPZQAhbjtdPWYfQS4s86LbPAqw1tbvru6zwEwx/+zLT6aIiCHMYftFZTuW+SlCs8YUdZ4prKv2/0sE3YJByehjKbndynqvTrCkYU12ZxDaG1iTxhkPPVuxgXl5iYHDzCSMajvTaBdkGTHSXaHFbk9YdGPUXpX4She1jza5/RiUumRpiDpzCwQ5OX4EEj1gWJtDAwSuLfBOtFcbDeb0n1s3FCQ/SNEvi64i7yAezjmD38gPMXerhUveyJnR1mL2z46qRJy0UV/jGl/VkUOu1ZghF3aWRLdnW9Q1fHVosG+sfeKvSz36+LguDpN7Zi8KnG81ktQSB6C29l5N7t+0/bnIJO7BBZpwaVe626qD46Pm20ukuoOqgI1Gp5+ZlWsZnuPM5PP4LHYo9e4ZufQgBo4eZElDBR+hyUMUhAJvEzl9mfapyxDfz1sMFpY4/sbWZIcpcXj3viF4QY5OEuTXMHteW4isX3z4ltW+oXlH/vuD+q0PeGa2Zf5cNxT+141Mi/MUXaQJTpYOBuyrL6vQFEiLRbPkSV4rS9BA7F2Bca30PdkAg9v4H791V7LDfQWSaiHQRpmxRgKXjVSS72dTVtPUcI/o7iJ6obOR9sB+QYWONjEJYixeNxf5vdZPEaZTN0ldT9g47E+fcK9GiVHVXmP+zUmK/hejfvsd3TC+W22o3VOXd4ICNtIzsDoXzBvXWU/QJi9H4E7imNQFOFAidMGGmAvl3zjSXeW5dVmjw7gr7ItjO2nV5PNLwzF3JPb6Fk9M33hwuQqn0tw2l27hEfmB+GOPZkgpEH2LNpk2/EJgscRl+wZbKz2NNUb/BziGn2uJbGXBemb79vO+JoIroVnvPIGRTHkjmgk6JbnXn03JdA+CZ/3eVZtHSVQk3dcP06nNykCHyt8blYdv6ffAwgUPPdkStVBrIxiltxN8USm5Z7IqJ0QDz8tu9Ut3g9OI20JeEeQ4AUNr6EPyrobSj97D973Hr1nZbhb8N7X6P3uCZOc+KZQxdfEsVkhsJooxgGh6ltygUNcdEGkqjwHafwc7B5yC3gZ9QelA7G3/+o8K9kzKr/e/haVoYUZ2mmXaC4k52mcoKoadsdf3CRxW9jp0zSFXeHCuqjwE7WQKXSaljayYZoWNoWN3TJGsiBZDLG4jpIzAMZuprrksduMsLFMCnO7iYzW6OzQvaY5ailkTo1WBFJhK6QGrEd+jeESPEb5+iJDkr/4DnKAWMsz3t49iH9kVXlUljm8qUoQeg8kFFD81yZQTJpmdVQ4mdm6P4UJOdapw9sgbSROWVi/x69m5ADJkRM0/uwC7jTsCIX2d/EbCFylYBqb0D7PEMrFD7BWdZ13TU8eHsJEysFgp09bmJMtxacs7Z+jD4j7DxCFaTvNl+8gEm7lO3DTv8Pt5jLYwTSh8j5kyTrQWIngARmBAj+O0h/B9hgcbrApRuOen4SGbOMTBYY9v4kCPedaIZ0sh/+u3+nNo7SIYvbp31Hgg3GFqoBLUFSBwoKeRFt8tTZ854jAAWuN9k2d6hK+6hcVzluA0ObAiwiGuhjFxRsK0qcNJN5S4BiAVW3yGsVWEyDW6hR39NvdytcC6ZwfIFL39iYY2+zP5pGs9qU32fzcXiZgnffwliihe8g6bdPsB6/P6TV+Xwuw/g7Le0cW4rJ7V4W5nB7cDDAls+4Jg7brNzXKaPFyuNKowPE54Wm72t+6P7L56bxoq0puu0Sey3ULhrYM24Aa8iVq7BYiPgmmmnSI4TzzVyDFimSoGtZw4ar3CRQFdaHQ81GAfkQycsa9eCWvm9h7Iv269gR1PJhRXbuY9xnBv1dRMxDusvprCkvyFtbYh1FdQaOft03Rmkla0u5n6vV07CaxpY3dttGOesIf8bzLHtMki9becVTOixYqiOLyEbFgWoAW0+tBbCyVvwN4d++ktpmUcFyl6wQ4vrmtFL0n+DpaGB61W5Y/49gE+7Qsz7KAfqYiPPhdQ2tdk7LuKbSFK3f0Ifp+WWFmd+OsO9XLG5AgHEdJhEOi+6hXxejeGCE3SWF9vGn7hMMzUHz+ER6BwicLm/1513NGSX6VR/EPmN4FPJQivlLjKiXkZAmEOvp6BxL4AHKTd92W8o5POwH2yMrQNcnJYb7OGeD8SLTrjr67t9A9yipPL8EfONDWvgy7//2TQAM3u/4Twi50CaIiS8+yvOaVMEp6w3GA7H8N9vtW3c0HY/V9UeH29nMWyoWZmXAhrqvs3GW81X1GfDhOonyvFht/qXMR5c0C7GXBqe0rbseJdF6/p1/7QfY/Rpxdjs5s3h/PWR3NTpAj9sdG7NH2pSFWoZ0Tc83Ir7aoD2+bsHTdOO6JzJt5Wsj79suWdbM01vm1aH7zF7unn8EEdbdnjNE6vm7Tfb4hdsd+b1LToXsyAeRNDMV7iwk2PQuj7AmL7MwoNELlKrrb4563fBrWtvO+RTlEuk7Hw2gnegPTaJ8k3o/Kpk+N3A1LqH/F1OXRjiitbqMY3+7I0bZKcb3K+zk/knUsk/R3uB4P/MO4R/xRAciewv3Ey6oY/IMtyiVielHAuxRNEkj45nzgKZk3v9gzTHsugIOHOdxqYHL7bM/PC//w8Hu3J0aiMv4RYq9K1P0vVfnllkCSRo7xXLdi0dmTlWZm/mpQPJR6EcE7UDP6RL0v4e0F/UcFcz/PM0rTScs8S/xtfdNoufs2WY4SGAWPux1w6+Ll2ukrEOvLT/sx0IomOksnEcjT0jUXHxpZvbMky1d/VFEOisu7m9D1rDXe9b9Q5xNXorHs6bUnzAQFnRcIaQUSEHs6KIeKPEpmsv8a8xGmP/Chh0zBWI5vRuvOvSeSq21O0Cs/X3uv96+513PjEpggdwWOYRr14UcQ4g354Ci/0hJ1FYYKUkf88HsaSrjiR3jSItTljfPiM3gcYc90hbisPu3cj1k19xFcuFi/+Jk59wP+OrePTSTEzj/kObr58Qkegz1h5wA2e8kiIYp0b8xVkZymmN5LL1qBuMqBZf2MzLv0Cw7BzJcfs7uss45Ov8mXNLPcJMfZ+tkpqJLdBPsU1Q+E7sc8q9voN4g1xigz1npZMV//78EGkE3vzd68ikvaFFqC2I6rCSYThUb1TIY16njMchzFP85TYsSno7ntCduE8FidV/uc3S90ios59FH2FdigdXNvrNajrB34MTtUNSFerTfwyCcT7cvJhzHe3zFufawOQ7y3Q0z6Fcb7dsAYYsOAmlC/+RZ6SBFweP6T6apup5/jKahHcbJXT90vfFuqMVVan6ydrxML9ytjjviaHzhiSkNFUt0FBw3hnefyCom5QSy+B+sqAVdR8WNP+Cycr8sKTb907RV0xuiAz5D1/W3UZbb9kp7meeYVSQm/m7sqo7wMEA8DY52m60BIqyqOQVFYo5mv0u1bPnsyV5zfoQzzHGaAwPy28tXcWAOiospBE6tmP4Z7cOF847JwkscaUMsesa4SGv0Sc6DhOaz9O5gj7yAbHnqHBFDtT3FgowMbOdgTmydhfxb2+dVlP6d+hpfFdmEdU0cQZ875mGEAi04xjwxxluUbNGTUbfVA8B/hBiJ+uMoIf1jcipEcPlU3CSzul+GZupT4FJIWIqUMpkhdRoPp6xJiLHwQ/1SbfTwzpZoU1JF19qNMMmJ+dparbAvj2aJZWRzCkIjbbbTUPWFM/8P8szzbuLMhm9vvplvmXg06r59na3XzLxALd+1Y456DzoGv8YWx7eAbfJHX4ndeNHvp4+f6GcdAYN37Kj54U4gCYgdAnz5m++LYR7UIl+Jgo+EBdtxDp44FGWbGTcySxAy8H2w57j59cG/41gHU2nRu4emz2Ubwbl904oBHJ/I11w0M+4XvzrQnT6U1b4HvCV9IX39jp+Wv9oaVZsM+POVZbNNLE8Hre5HlqqA0pjcc89SysWYVi4piUBq7ANeCc1V4XcfEFzvBbVQlJZp9hANRrlEObMgu8GfzyHNZEI/juJZS9SmeGBBbV4Lhi+mDWy6Xa0tGK8FUYZNoQe97i93RlGo8OT6Dx+IjwMy4h6Y8eeOCGvXGWQH9eXAKhebvFajAuu6B/WCYixxmuecFYWwlGzhRsRfMGNTgeMxabl5lwat6lY1R0UuwTZ7tamsBO0aNT05CI6LVOTTk4tfjoIduKxxZI4fAy3MPgwxWxyiahlpNMDCesbktDGjmNox7EP/Iqr0LpDY42V0cehcZuo+8eIbpL+pLSSEwz4ur6On0CVAtdYFBIO1tOD8VeefCEwrTap9i1wmNczgVECGCx8j3nu6HOHAzzZ5LHE4FP7FxET3jDtmv13T51tnPHhEhwPOGi/f34Fu9J+xw8hwngI1A7zQCGOYCoL2t+t14w61dGSUEzU+DxndEDN5NnewmVYDrBucpxPZrx0d52dyLj1dBvFLhv0l8TVL2nsy3EDcRFbfyDJigz+nFALXL8GgXsTv4kZCdL2VbLBYJYZd6T7QnrHvRRtt548O8HcrbJW+g8qwokKqaHEYwzAhayP198aFDLfkIHkDiJ+tX91letk7GQfxQzhC5KaCRHWnbHOWGPsgd0+vPKBA76qFQcZwRp4I8B3kovFkuihgFg87xo9T4wpjPbqKOUP0hKoYC9wS6aLIC9S2l/ZA8o/ht7EQkG8rk+7OM5QKDV5EbPHvS/7XLsWwU3BbLNE6qNThPVxDBRt3eye2YpHW+Qwmlt1VV5cnn1M4rWIp3Sp2Qgh3zfgJl1LiOC6qKM6Dfk0RyzHB9F/Ca7CVAWlZ+VHyH6zvgdSxXI/wzS8O0sYbDL1oz/eZes+MMi0XPuWTt5GP+ngssyIG8zzrXYlyLAldK35YloR8W0Exhdno8ldXPYEnX33Ir0WcdwwGibaGHncF2MJuidnUs6erb1aDPOcZIEkcItIdAEvw+W5/gprpNThbIeIrWBTqNqqREu54VADwv2/VNsZysXc5RnJUaq0Jr7nAY2xbCdFSbopxGlSnLzX7iO5J09e1q0OccYyQvQL6BBXZpqKNc0hE5XYaVxzOetFS5TmMsLdhSKooQQe7qdm1yY70m+5jz2HfUbSez92gvYEYvf3SbVb7531XkZNRT60g6clSY2yyeVqVRVqCtv1Md6swjjugleIDg8QNItrdVkjouwAEsTCa+FkGGo26w84C02b2q8j0qmh4f4WLYcZLdtZEV9nUsm/YZxWsxMoehPrvICge3vT7nGJMU34P7WcYymH0uNHPgQTgvAR+Ay6Bn+pyjSPAsSb5l+IRt1qjyE0hu1NCjtHh0ObSm846+jO7vCMz7llg42VCPUzDRgIPQ9mE4nV7JrPWAf4CC+OgGgPqcWSKpeP2oKLIYkpHldgH1M6PXzf85rj9N1y/wTgSRN+lNTVYguX3VfftUJSXcJjBGJf/+8s1LfqZ8SWvn3hfYSxjfkjiJijhai72Aar9W1oCpMFsRLomtz/8QiiG+JnWsi5MsLco8Qj0rTm+YxnAbJVwPcHSGggA3rkPkU96BLT48Skt5U01KpJ6LFQvu8LneHuqH315TfGPHTlJrvTCYslH8WdhJ1kPLYad+kZiFnbpDpYBy6ZdXr94IY9cjdceFNFT/cddlivw0VDX8M0uT7ijsaIu6HJ/PN9WHoLheZVUeqzmizcuMY/9xEuEiHEHL6jIyb3VNnoC59EfuijL1x7Kzs9lVlGM/jiDiYo/ZzGrI52AzjWvGbGzWLK87JctkKpuQtjeSzEY9W6Ig6zhsQIzNr4jPz18TquIu7KU9apuUu9ofTQgTbJnlz5iWKbuoCkurw6TvjQyjW2Ujx+Y2IlyjkYcPIH++wtdrlGNLEzGDyiRY7QEtpGEovlA2dRzesFrXqLrNzROdFqfw6l2WYs5VWsZPe6SW8y2y0coXw1c7Y7Gckammt1naWa1mNlp+itLqNiKWs3xCwyVdLAPHJuy6AZNpzS4YMRt2ZLhC3QjfAR1X6CirJE0fhdWsxz+MBLIuls6wKN7bmRVuAcw2/UpnL95mXu24Ky2XAKFBfMW2vmNicFLDIjAjzSdNwn7yC0rqanUUo7Ag1wUTcKLBBS1FwSY3shbCk0PHOpJRV4zzT8GFNlwwG/upr/ZNxXcl2qegej2gpoJradWXxWN0fVneYlN2nqeY5ljw0mys1N7rC8hEA/tHLjgVQeq+7ToDSK+iLnzoNUzrP4cXxwz2U3RClmAqN583XHs3sXsSbEi1bnOw4qH7OMkiw98Gl1VlZDnTNngCrtLefVcUqb8fOxNnDSjIFgJib/nKZoxnYCt1TISpueoYJgnS6OXxIJx4asiJ10bu7S43GJXFdv783NDErLyWN2Khi1dTaWlVurS9WbzaFtksXrPxVR2qfFXm5NZkQRSiLn67krk6AnpE+4+TMJes5mx9pASjsJm8w8ZhM2mzTMql4/nPwmvt1fDd4K+2tkwd+o87z0ddU3aCd9qb47vBO21tmTr0H3eed7qm7ATv4Cvvu8E3uKbsYS35sPP8QpqxM7xSh0e4FittO1IjckldR6EG7edxzvNNRzEAtzQNMSkNk8/HL3xcOzr22KCFkc/NjqeQOA136aIW6urHEo7DgXyXTMGNxlEcVfxpFLZxAcw6dKqvYgTdwP+ETGrNIHMz6EDEyKntnVYSdFFmrqEJMiHzTWrwcmW4xTHbDknAJTLdVBLPm/HmlXSnT/i1mCg5qsp7jBiTRG5JXrTE07WAqZeecOcloLZ5Ngw5Gy+eZXm1IUbZ0IynPl/symSQqK87zxd9W3aHCchzWlNzQf2Gl8AGzef94APJO2WLZoRr8m/95rJ28JhnmfvBaz5PshCRAsUqjMQ6qo4ZkXmMCurrtQQRIqm3/YiNyS8zCB3zsZxW4hDyBSgfqmr7DN2YLDS59mI5rpPqL6RaszERCaM8gpOcTndhnjQlIM2XXfeNq5thUhDb4TOP/k5smqdlminVXHOumV3DbcMzfcUP03+AqDb587U8rtRCw3DRNZfWhyXYm0BcTLNMyp09EpeU1yRzxV5G/AQsZi5T5uIvUsN5Fz7iEbdkjupqKdZgRFe4yXinb8guMczyY5jMwzYTxiuxY5y5o5S8h7flSZSvry8q/DRMAdbfYXmvaIP7MA5cS2prwYD1H8eTJMbj5MkUXVuMeEI6FLOzCKPryBvkOqQjyRpZzaX1mUDpseKAQNxmrfe0GZfFa1/pqbBQbWhJrDaZjuTMZ8yIzqs2fc5KsHw9G9dSrEH9dbd5qG/I8vXsS/CIuP0iQwBFK5x2wj4pqThTHWn6ztsuZa3aCUumjM+CLoID6vhy2GUyMeTKK8ywzBvYD79jsOiVrK2kEKxvxHclJmOgrh3LX8baqhKLkbzerqM2MucIBgc2YbQ4o8YDG4iJjM0SXYZ5D2nb0JTTBbI5HNO/5Dp9Rm2lrPIUP6YDRvA1HkkfpqrMqTZMyh7owHR7dkL77S7wTC9VDuGxlOJlTt02IxU5ifJaZTmu0nUCtMcTzbEKRSk58aJTrdiErxGnkvCJY55oqfpiHAYS2mZSZl/F5bDQTqxSS2CzKdcqJ+6afbkSeGv5Z/BLYKwJT+Sd+Grug/mm/NUWxPC2uX14VJY5vKlKsAM8pq2/rF4qyl3nPn3zdoEV5VX/ssV/Xpu0ixY9w/xgxghjiUZNWw1q2RKOY4lyZyFfCaprrNFmQgqwUOFq3tqxGeenlM3ezDa7wNa1YG6eb1WHq+huOIbZUrSIvs6yutCpe6ItUE2y0BBmDD8mYa6BiDv6MZUM5s/AYqYDPieXIfLZGe1blMMoLTvZepJtbmBKCK/VT8bT92FoIuYeDJNgZRYcrpuMtbTk41zxUnbQqNyma6hJLehqL5n/dmZvvnhGnX7d9eXRubfqimbsLk+aMOL/396V9chtI+G/ssgfMPI+u4DjsbMGbMfxjJNHQdNN9whWtwaS2sf++tUtHnVJTUrqQV4MT9dHFqtYPFWserbW9xxMjr8ScoBAly99DbRZa1z02seDWW7mrscW4q84PQ9GSksYxi6WtdxGXEkzO2BIG55lT2FMuZV2gj3bFaxt1tFdfHxK1W32/ZRmMf4cbwAYZ53hxznnHMlmwdeJBRQvqKXILvoM5a9mC/eJyiv56tTaaNjcS2MdNzYRZMYaWm+0Rfv1WQQ2HuWRMNNbtwGz2v7xYR0jWvCIMM1+NnIoGF2/Oqe97RuS61c3yXPw+gzLFUnC1OjODZmZVwNjt0D/WAnNdHP2Mezb3yRpWceWDO5KvNQ0ZUlGN9IBPx8XZVZW+ey2qsvyb/Hu69vTXZntvt6dH4pdnrT+VlfhuYw03sy3imGu3o8Zk2zKxn9zdrf9ndv2jG7B9fkSm1t7lX755UuSVr+oaKEYzwNDs6Lx12t/6DWKImG29gPSJiR50EUONwWHtxv6/ZmtTq5MV7EuVfJ9qwz6fWWo8UFFb/LsuJyVmMytNc4kXb19WAJJOOqdsRUDuc/+MY+NmMfYFettMap95bdK5e+yQ6T9v+4ofLNh4Yy9gk1bZIerccVaE2rvQuksjKHpQol2MWYTN2FqV3FYX8+qlpy3pprT6lPWn2d1VvvXxzhJo+bflzs6k4UBMjJ1GoRFbEprvNEU4/cgNoWrKoxd6RJJ2OntWy/EyqPafc3OtjeU8zM+bzlIYwJzqcskTgbFopsW0t+J0WcYg0QkFM15dtEVY0rtznkl8eFj/LOJa/j2lNT1egpviG/lbcZWcCibeN0xCx15RDy1ntiMffRRUmmJfPVzsKhkoFBk08KGX51nIH6Ncko0Vrvsavb5LtvFafI/te/bGL2LT4ezc2LWen8A6N09/riIBTrNNhvjUoNYHayqMNbmyiRh2rdwRfvyfbgM599pnyufx3nyas6R3de9T+pbor7/V6VPX87pqQ7oV3fJWHlzoz7YVPV7WW+77rMIK8+aXVcHaH0DbbLLldsO4IszABJ4uF9kc71Ei1zBSnpkEu817dAg/IqalQHDO301k/LrM9AJs3VrMgqv57OSZodX2bE5KApnNa3I0hOZztrwTdF/f0bTFarqSewWNqP6/3WWdHw+6hFOH0Kp4kO5PE03JU/T1CDoBg2ob9tqhvRBfS8mzkdakaXnI521XqXx+zOaj1BVT2K3sBnV/6/9kfH5qEc4fbigV/oMU/I0Hw2CbtCA+ratt/XO0vSvrExOh09ql+V78dHPKrf4kc/mb+zLHeIzmqNozU/iuYZ91T+8PBXfVU6c5waM0639z8u8pLnEyHwd+kaht2pcYxO3cYMw6/pq1Tsr+Z3FdU9dni4wFjeo7i/2Bmr9x8lT7en6nrNMN6JlH7G8rsqUP+tRVZVQeX/Xn+3VmyQvytu4jB/iwv3YV5e6U6Xweeov/2pLTHrKebd7VMf437/sH7LKQuIH2WtRZwpjWippmrAtUuY4S5IRW/1tUnSucU79IwliMFIZDq/iUh2ar/QOh5EEcRipnIqSXXnOFaSingKqqCfKeoDgYgGI/og6UPQ+brJyiPunTmKW5Ko9RKJdZaCoXjOAXBOMoLAub4MMMjUQMm0TVuMgKH33KLHCu4Lv49P5S9z0FTETmSiqHTpS3BamETx3E8ENVHur545XGwEOWxskZEvwoxmxHIZHkg6DgQLVPxA5AeoBBc9vPQVsfk9kqu9T0b1X5WO2B7jYAIiZjeF4ltWwqUz3W7Vjgca8RQc5mhCG4fjpwOE1kiA2I5XhMDoTORxGEsRhpHIcam8adVfmzdmyaLMLANxAGMgZRDKtGC89Hc4jCeJWU/nZUbugcCdFjQjOhRpdwAfhgNctnmraEG/odNOSqSmnRXAyqPyYFEW1ee6vT1x5HAgom4NiOL/+UW1rT3H68lw+1hv8NhcH2goaDrWILsG0rnmDiwx2jQbxbchFJBrwDfY+e0p2GJ+OSDDqEBJOGBOy/g4hqf/3PDs/YUw6IsGpQzCcOo9xh0n3O1R/RxJuYz/X7yYHt110H2vCqI2siWRa8Xvypdo25numFTAMagWMFLaC4Exzk3UjsghoNLQ7RQfTBvkhK6GZVKOhTFoyw+ST+l4J/DFLTmWBdxiIghiDQMH+Czl2jSRszyU6YBmJ73EueKeZCFalRpJvQJkGHVajAWHFs3NqAiLaEFhMGyU7y2EJvLBTHYanzndINjXpSY/Oi+bqi4SDuiNLzGqduF1TWiTt0yYZEdaBDZHorYYu40Pm/cD4k4WIdpHl5rVX3kjm3sqKaD/1PkXQIFlLJlsKEuNdqpYOPqHXuhLcjmgIP+7uggYSuPMZqAwHLf6vw0KjQTw0MntTkIF3ot3v8M1AJrh1bVCDkcH1D2SUjdRK76vyqunIB/BoaNFBnZkQ7sIDi8Xm3n9gSPA6BANPuGm8V8enNAZHKgzjbh5HpPBTAdECF0J9OhBz7hcGnLGDoNYXKdvf1UlVw4yaHF0IeDRwUNyd6C5F7wM0GngvOpIZJp9znIlGg5hoZG6iqErvz6m6j4uv0ERhkMGJwkBwehvj5Ll6G2mg3kYyNxpVXFQD52+VHB6hLbpFB8efCZExvE2qU0QBT0MuhGCrodgruTxXpx14ez6Q4Ku4niq5PWHmWABD3KVMmlntGFjAxGICqHsvC8qOcS2GDTDKNSo8zjWAnFUbEopi1yIYli2IXTCOT3FygLp0JMELRE/lrlKN8DHu1alBBq9KDQQ7DpuepVZgGwGPQgsk+GbwTpWlyplxggGx7wkQlmmLESTHaYBBhbgaAM563Egtrhm5GNCeXNhU5ti5BAOKmiE7i6BxFYDLIQQJXxMh4InNEbRD1gDZJzzzkb/DGsCgH+9MGCt2PVj3uMOPDYBFNjGcFeZZUVSVpzhXFwJanoNiVQ0tSuhiJFmEqv+VyGmxp4D7v57InRHjH4RXikEFz4c6gGOFfLYivljJPlX1n00IQVwI9f0levn0lCZqf591+ETxX5H7wrjROQhZGzo43wLTHwL34EBwvIvFJ1VUU3Dj7SrwIOk+K+OOKg6C+jrdgQQdYX9ZZj6Q03DJp+uohorvDgdXItl3e/n3eyNV3NSrTMkNtPAiOhpxU1sBPMnHGgNAiTYBaIHzzvDAzb2/0qmYC88AEOxUcVYGFduTSlm5r19c/ToQzDXFRE3pYa5bBX3pMNT8ujE312jwZtXAoL/rgLQdzzXPfdN1tuKJet9CHv9gcYtkvyMwRZwhfr/w8OLDDwOgpxNu+zcj/uAnS/S7g/Hf4/a+pCmIbpumi0ltW6K7zsPOlVxSDBfK9rFvhML858GSoFpcYlj13Mf5QYEjQlLMU59vTD39hnOS7diFQloONPc4tJCKmWI1diH/U+pKagGeXRSkSsACIe0EeF9iVEI9GJm92EbmyxB0qTVxhDDAS5VWCuIFyly7mi/2sI6OUxMquosNMZEij2508T1Oo7Zo/F7LgQbbbC2kAuM5ELHlAnH+t13QQ6OmMPmAaHbPW4+hdBaoCeBlPIsFWQVaEUgPoiZ+kIDwYANlYZWYt2tRf72mujs5evsuL4yLDT/DaiSmH1cRtVj3jkBl2COxIArEt2rywsTqBAiLiLcVlelP2KKhXkA9IDCAKqBnea0KyNd200XvrpApoW2If3Gt6/CmHPYoc76IlupwQU2g915aUPThGnz4UkBMnjiY6HHrTW/b5ch7XbAkpACHFkIR+CSIg7304xbU8FuSptVEOtRMKMGChlOByIYuELoPlzRWjUvtYEMOAesdvlEB9rh+uhqgR8ZjvgZAF3QBXCz7AXYjD/a42ixJPKxua5E8lJ6umv5tOakOFxRCBfb7+KYk+vZ9uqj903BSVBcUQlT7AXtTcvzx8sNVlqakmCYghIj66/b2zAS9Xp8nWvvAPmprhIXTIRc20ilhBgcYyiHP/meIaDsU6F/yqW2MrCAhHPKavxWReaFP1gR5N4C1ktEHvKsS3wjJCvILnCMwJeAmVQj6k4g20xOtz8NuglP6MqoRbK8Xt6a1VERF0SC3o7KCIaxJElmkqW1ShJDpqhvihJB6AlAhlOIENGmKar/6Ebfx8OTltWDBBDZ8VEeJYT/UmSJHenQTRF4dwzTZiKEyNhmMjeKWdgv67dmuSrJbW8zFDV2nN5uRqFdLjVgN57HpC4/bJtCJ4FYFxPm/UjHC+zSl4Og9cwWl5iYLEWJaCiceFG0oGv0rcEcOuADhwXCxIwcU28iohYxV5Ek1nd6FegFT/k7v102oY4ifhOvAhvgV3In9NJbzdIEytp/4Ju2C/H+JDi9qH3Er+lidzx7jQu3/TspHjYMrOFfEmzhGWTumWFMUjRc2XxHGuB3rx9UAF/AkCFgSHfGiYG2eVPNZ73exfsxSfueFtdQzxHpjJkQNEmBC1OPUjeXAEHTTRQSiypFbIRIfYmNERNFrKpGExfOjFnZc8IX8Wsc6qukD9uFjwkL4FdoOaXjXe9F4cu/V4xFGY724lCbQU8PBks76SsZOnHsQ6j2S2COfDbymM58RBZKZ8EBkmKkOiG7ZjWQqaOUFF9yCzkax/vs7tNuEHYQz0nOZQCMch7MnAjfjin42wJOoWCMfDj/ajX4mpKgHBVEjAweHGByrq4I4MKJY/+fGpdVAhpUVOHYzBf0rSBQ3V69NGMt1hgURYWuRmLugcc2ohrAfXi8yhQhqNUMAE3UjoXx9WyvNfKoJk7UtrKqNjgA3UgTp5YKjg00TbjwMvQadGkQZhJ8KjmaFQqQBxNiGSojQ0dJXluIqiFt3Xw8wBWG3dY1OCZwdRMX8Ii4pHWyIXoM6Z+hwecVJtBVeReReR1yWFZ6UWrap2aQam6CIEcpWrFK6nkWUIqnXCCxJVQ4Hi5y/St/Fx6dUjfHq8RXaQhILjBU1v11ckIj4cyeCGSIPAfMN70VIZATJH/fn+zc6iQCa4niM/0vEJyZxF+R/2g4vqnP51N1o8asXWiTY6jX3/syLWiYp5Fmrwso3zdyYTim+zA0q1yK6ai7b9ox3WXCSC/KilS0T4r6VSfLRVCTN2OFPTcTA5Ir4H57rqmhIyRARH29ckP+vNk5aibYkmjFipgeueLgQ6BADBc3RcDd45HrueTOVQvQmz46UPih4CIXAWSO6UUUmgbhYFffZBEVo4GtXg5Z7InLyUACzAgEnxjmcJ6Md7XTyC6werA7PKqGMAsSFsIdlxNZySERm+g5XdBSLCwGlFGmkoFKFGDUAOTKaCqjkFzMcHMBkEhGQ6gJweJCWJawEy87RmguXcIOuy70TEabOmOMmYyahiN6ekjKJU8IFjipwsaeP5S4Dp9roPGaY9BmXK6N38XNZ8XpBywYSl6wJ9h2U5T+ZGbRDTy7CR+yA0biAF4brgJKo3A2xOsi8KHPUQa9Pwdclez3ysw515ycnIH9Ubw9eZaeizOOkmh/q3eggXx+i5j6LsPLErYN/Xry6Z8S5gY6eaEIE/SzKJzfw2E0G4ddJWreKCoXHJd60ArUEEFJzM3JGAPcYl9UY3maBlBntVQeRC+MyxfbBgkA7hIF088XhiFYQXMvzIe1/IzWIq6ELawxvUUBmlKYOKuXJZYrtYzKBFgUD6eaLoz6tILidxEW8LjrJX4BVwFfdC6y1SFKcdongEt14UPsYTApeUCk8LdakyFXrKkVfimdt0Ni9yLxaF97rybc7Fyu5+4vfxI3IYJ8yPQp/86KtpFZ91c8qH2g3L9q0Ut0P1Z/V0bU6+b3P9iotml9vXnw6V6WPqv3rVhXJYazipqrzpJoEeGOlPebt6UvWn/+sFvWQnjxkxi3jfVzGL/MyqcOKV+RdtftsEqY1NzX19dmD2r89/XEun85lJbI6PqTG6fLmBc3/5oXT5pvWV7jwIULVzKQSQf1x+u2cpPuh3W/itLBO1FgVryrtN6ne276sBmepDj+Hmj5kJ2FFnfpu1ZM67atB16cKLv443cXf1Jy2fS7UO3WIdz8/1gGOm4snrBK+I0y139wm8SGPj0VXx1i++rOy4f3xx3/+DwDlf+sOvQcA - - - dbo - - \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201404101903014_AttributeCombinationPrice.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201404101903014_AttributeCombinationPrice.Designer.cs deleted file mode 100644 index 8b4dcdd3f5..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404101903014_AttributeCombinationPrice.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class AttributeCombinationPrice : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(AttributeCombinationPrice)); - - string IMigrationMetadata.Id - { - get { return "201404101903014_AttributeCombinationPrice"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201404101903014_AttributeCombinationPrice.cs b/src/Libraries/SmartStore.Data/Migrations/201404101903014_AttributeCombinationPrice.cs deleted file mode 100644 index a930fc6bbd..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404101903014_AttributeCombinationPrice.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class AttributeCombinationPrice : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Product", "LowestAttributeCombinationPrice", c => c.Decimal(precision: 18, scale: 4)); - AddColumn("dbo.ProductVariantAttributeCombination", "Price", c => c.Decimal(precision: 18, scale: 4)); - } - - public override void Down() - { - DropColumn("dbo.ProductVariantAttributeCombination", "Price"); - DropColumn("dbo.Product", "LowestAttributeCombinationPrice"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.AttributeCombinations.DeleteAllCombinations", - "Delete all combinations", - "Alle Kombinationen lschen"); - - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.AttributeCombinations.AskToDeleteAll", - "Would you like to delete all attribute combinations for this product?", - "Mchten Sie smtliche Attribut-Kombinationen fr dieses Produkt lschen?"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201404101903014_AttributeCombinationPrice.resx b/src/Libraries/SmartStore.Data/Migrations/201404101903014_AttributeCombinationPrice.resx deleted file mode 100644 index 96f871d090..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404101903014_AttributeCombinationPrice.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201404170814451_BundleYouSaveLabelFix.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201404170814451_BundleYouSaveLabelFix.Designer.cs deleted file mode 100644 index 793e21d3fb..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404170814451_BundleYouSaveLabelFix.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class BundleYouSaveLabelFix : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(BundleYouSaveLabelFix)); - - string IMigrationMetadata.Id - { - get { return "201404170814451_BundleYouSaveLabelFix"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201404170814451_BundleYouSaveLabelFix.cs b/src/Libraries/SmartStore.Data/Migrations/201404170814451_BundleYouSaveLabelFix.cs deleted file mode 100644 index 621a1fb02b..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404170814451_BundleYouSaveLabelFix.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class BundleYouSaveLabelFix : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Products.Bundle.YouSaveWithoutDiscount", - "Bundle for just {0}", - "Im Set fr nur {0}"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201404170814451_BundleYouSaveLabelFix.resx b/src/Libraries/SmartStore.Data/Migrations/201404170814451_BundleYouSaveLabelFix.resx deleted file mode 100644 index 2f08e50781..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404170814451_BundleYouSaveLabelFix.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201404232319321_LessValidation.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201404232319321_LessValidation.Designer.cs deleted file mode 100644 index e1a3069761..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404232319321_LessValidation.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class LessValidation : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(LessValidation)); - - string IMigrationMetadata.Id - { - get { return "201404232319321_LessValidation"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201404232319321_LessValidation.cs b/src/Libraries/SmartStore.Data/Migrations/201404232319321_LessValidation.cs deleted file mode 100644 index 2cdc065705..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404232319321_LessValidation.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class LessValidation : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Configuration.Themes.Notifications.ConfigureError", - "LESS CSS Parser Error: Your changes were not saved because your configuration would lead to an error in the shop. For details see report.", - "LESS CSS Parser Fehler: Ihre nderungen wurden nicht gespeichert, da Ihre Konfiguration zu einem Fehler im Shop fhren wrde. Details siehe Fehlerbericht."); - - builder.AddOrUpdate("Admin.Configuration.Themes.Validation.ErrorReportTitle", - "LESS parser error report", - "LESS Parser Fehlerbericht"); - - builder.AddOrUpdate("Admin.Configuration.Themes.Validation.RestorePrevValues", - "Restore previous values", - "Vorherige Werte widerherstellen"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201404232319321_LessValidation.resx b/src/Libraries/SmartStore.Data/Migrations/201404232319321_LessValidation.resx deleted file mode 100644 index 96f871d090..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404232319321_LessValidation.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201404292222025_FixMaxLength.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201404292222025_FixMaxLength.Designer.cs deleted file mode 100644 index 64ce8f34d1..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404292222025_FixMaxLength.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class FixMaxLength : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(FixMaxLength)); - - string IMigrationMetadata.Id - { - get { return "201404292222025_FixMaxLength"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201404292222025_FixMaxLength.cs b/src/Libraries/SmartStore.Data/Migrations/201404292222025_FixMaxLength.cs deleted file mode 100644 index 63d5641fdd..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404292222025_FixMaxLength.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class FixMaxLength : DbMigration - { - public override void Up() - { - AlterColumn("dbo.BlogComment", "CommentText", c => c.String()); - AlterColumn("dbo.BlogPost", "Body", c => c.String()); - AlterColumn("dbo.Category", "Description", c => c.String()); - AlterColumn("dbo.Manufacturer", "Description", c => c.String()); - AlterColumn("dbo.ProductReview", "ReviewText", c => c.String()); - AlterColumn("dbo.GenericAttribute", "Value", c => c.String()); - AlterColumn("dbo.Setting", "Value", c => c.String()); - AlterColumn("dbo.Forums_Group", "Description", c => c.String()); - AlterColumn("dbo.Forums_Forum", "Description", c => c.String()); - AlterColumn("dbo.Forums_Post", "Text", c => c.String()); - AlterColumn("dbo.Forums_PrivateMessage", "Text", c => c.String()); - AlterColumn("dbo.LocaleStringResource", "ResourceValue", c => c.String()); - AlterColumn("dbo.LocalizedProperty", "LocaleValue", c => c.String()); - AlterColumn("dbo.ActivityLog", "Comment", c => c.String()); - AlterColumn("dbo.Log", "FullMessage", c => c.String()); - AlterColumn("dbo.Campaign", "Body", c => c.String()); - AlterColumn("dbo.MessageTemplate", "Body", c => c.String()); - AlterColumn("dbo.NewsComment", "CommentText", c => c.String()); - AlterColumn("dbo.News", "Full", c => c.String()); - AlterColumn("dbo.OrderItem", "AttributesXml", c => c.String()); - AlterColumn("dbo.OrderNote", "Note", c => c.String()); - AlterColumn("dbo.ShoppingCartItem", "AttributesXml", c => c.String()); - AlterColumn("dbo.Topic", "Body", c => c.String()); - } - - public override void Down() - { - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201404292222025_FixMaxLength.resx b/src/Libraries/SmartStore.Data/Migrations/201404292222025_FixMaxLength.resx deleted file mode 100644 index 96f871d090..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201404292222025_FixMaxLength.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201405071457441_FixRes1.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201405071457441_FixRes1.Designer.cs deleted file mode 100644 index 22da5c2eb9..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201405071457441_FixRes1.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class FixRes1 : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(FixRes1)); - - string IMigrationMetadata.Id - { - get { return "201405071457441_FixRes1"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201405071457441_FixRes1.cs b/src/Libraries/SmartStore.Data/Migrations/201405071457441_FixRes1.cs deleted file mode 100644 index d73721defa..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201405071457441_FixRes1.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class FixRes1 : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Common.Import").Value("en", "Import"); - builder.AddOrUpdate("Common.Export").Value("en", "Export"); - - builder.AddOrUpdate("Account.Login.ReturningCustomer").Value("de", "Ich bin bereits Kunde"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201405071457441_FixRes1.resx b/src/Libraries/SmartStore.Data/Migrations/201405071457441_FixRes1.resx deleted file mode 100644 index 96f871d090..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201405071457441_FixRes1.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201405080948530_ProductBulkEditInfo.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201405080948530_ProductBulkEditInfo.Designer.cs deleted file mode 100644 index f4b4c35972..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201405080948530_ProductBulkEditInfo.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class ProductBulkEditInfo : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ProductBulkEditInfo)); - - string IMigrationMetadata.Id - { - get { return "201405080948530_ProductBulkEditInfo"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201405080948530_ProductBulkEditInfo.cs b/src/Libraries/SmartStore.Data/Migrations/201405080948530_ProductBulkEditInfo.cs deleted file mode 100644 index 3b8b53be2d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201405080948530_ProductBulkEditInfo.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class ProductBulkEditInfo : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Catalog.BulkEdit.Info", - "Click on the values of the products you want to change within the grid and change them as desired. Accept the changes with a click on [Save changes] or discard them by clicking [Cancel changes]", - "Klicken Sie in der Tabelle auf die Werte der Produkte, die Sie bearbeiten wollen. ndern Sie die Werte wie gewnscht und bernehmen Sie Ihre nderungen mit einem Klick auf [nderungen speichern] oder verwerfen Sie sie mit einem Klick auf [nderungen verwerfen]"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201405080948530_ProductBulkEditInfo.resx b/src/Libraries/SmartStore.Data/Migrations/201405080948530_ProductBulkEditInfo.resx deleted file mode 100644 index 0264b0dae3..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201405080948530_ProductBulkEditInfo.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201405282243489_TaskUpdate.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201405282243489_TaskUpdate.Designer.cs deleted file mode 100644 index 44c041e221..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201405282243489_TaskUpdate.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class TaskUpdate : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(TaskUpdate)); - - string IMigrationMetadata.Id - { - get { return "201405282243489_TaskUpdate"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201405282243489_TaskUpdate.cs b/src/Libraries/SmartStore.Data/Migrations/201405282243489_TaskUpdate.cs deleted file mode 100644 index df3ce9395a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201405282243489_TaskUpdate.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class TaskUpdate : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.ScheduleTask", "LastError", c => c.String(maxLength: 1000)); - } - - public override void Down() - { - DropColumn("dbo.ScheduleTask", "LastError"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Catalog.Products.Variants.ProductVariantAttributes.AttributeCombinations.CombiNotExists", - "The selected attribute combination does not exist yet.", - "Die gewhlte Attribut-Kombination existiert noch nicht."); - - builder.AddOrUpdate("Common.Duration", - "Duration", - "Dauer"); - builder.AddOrUpdate("Common.Never", - "Never", - "Nie"); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.StopOnError", - "Stop on error", - "Bei Fehler anhalten"); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.LastStart", - "Last run", - "Letzte Ausfhrung"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.LastEnd", // Obsolete - "Last end", - "Zuletzt beendet"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.LastSuccess", - "Last success", - "Letzte erfolgreiche Ausfhrung"); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.RunNow", - "Run now", - "Jetzt ausfhren"); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.RunNow.Completed", - "Task execution completed", - "Ausfhrung der Aufgabe abgeschlossen"); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.RunNow.IsRunning", - "Is running...", - "Wird ausgefhrt..."); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.RunNow.Progress", - "Task is now running in the background", - "Aufgabe wird jetzt im Hintergrund ausgefhrt"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201405282243489_TaskUpdate.resx b/src/Libraries/SmartStore.Data/Migrations/201405282243489_TaskUpdate.resx deleted file mode 100644 index 5af2fabdf7..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201405282243489_TaskUpdate.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201406122329031_ContentDeliveryNetwork.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201406122329031_ContentDeliveryNetwork.Designer.cs deleted file mode 100644 index 11b22019cc..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201406122329031_ContentDeliveryNetwork.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class ContentDeliveryNetwork : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ContentDeliveryNetwork)); - - string IMigrationMetadata.Id - { - get { return "201406122329031_ContentDeliveryNetwork"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201406122329031_ContentDeliveryNetwork.cs b/src/Libraries/SmartStore.Data/Migrations/201406122329031_ContentDeliveryNetwork.cs deleted file mode 100644 index 5547dcbb6d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201406122329031_ContentDeliveryNetwork.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class ContentDeliveryNetwork : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Store", "ContentDeliveryNetwork", c => c.String(maxLength: 400)); - } - - public override void Down() - { - DropColumn("dbo.Store", "ContentDeliveryNetwork"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Configuration.Stores.Fields.ContentDeliveryNetwork", - "Content Delivery Network URL", - "Content Delivery Network URL"); - - builder.AddOrUpdate("Admin.Configuration.Stores.Fields.ContentDeliveryNetwork.Hint", - "The URL of your CDN, e.g. https://xxx.cloudfront.net or http://xxx.cloudflare.net. Setting this value will allow the site to serve static content like media through the CDN.", - "Die URL eines CDN, z.B. https://xxx.cloudfront.net oder http://xxx.cloudflare.net. Diese Einstellung bewirkt, dass statische Mediendateien durch das CDN bereitgestellt werden."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201406122329031_ContentDeliveryNetwork.resx b/src/Libraries/SmartStore.Data/Migrations/201406122329031_ContentDeliveryNetwork.resx deleted file mode 100644 index 68e6d18a2d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201406122329031_ContentDeliveryNetwork.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - H4sIAAAAAAAEAOy96XIcObIu+H/M5h1k+nXvtXOlkrrbrE9b1VwjKbJEO1rYTEo6ff7QgpEgiWZkRFYsXPraPNn8mEeaVxgAsWEPbLFkKq3MVMkA8AFwOBwOh8Px//0//++v/+t5k7x6BHkBs/S31+/e/PL6FUjjbA3Tu99eV+Xt//zr6//1f/2f/8evp+vN86vvbb4/4XyoZFr89vq+LLd/e/u2iO/BJirebGCcZ0V2W76Js83baJ29ff/LL//+9t27twBBvEZYr179elmlJdwA8gf68yRLY7Atqyj5nK1BUjTfUcqKoL76Em1AsY1i8Nvr1SbKy1WZ5eDNh6iMXr86SmCEmrECye3rV1GaZmVUokb+7VsBVmWepXerLfoQJVcvW4Dy3UZJAZrG/63PbtqPX97jfrztC7ZQcVWU2cYS8N2fGsK85Ys7kfd1RzhEulNE4vIF95qQ77fXF3m2ruLyuErXCTgvweaoLHN4U5XgDCYlyF+/4hvxt5MkxwAM1U8I6VE2mL45QSOQZHdvhqD/7RUH8G8dL71/8wv+799enVRJWeXgtxRUZR4l//bqorpJYPwf4OUqewDpb2mVJHQHURdRGvMBfUIt2YK8fLkEt023z9evX71ly73lC3bFqDJ1x8/T8k/vX7/6giqPbhLQ8Q9FJNKr30EK8qgE64uoRB1OMQYgIyDUztXVE224Vj1SR/RgQN+jpPJGOy8ucoCmJ4gReVqo4yxLQJQOg32JHuEdIbSSbq9fXYKEZCnu4bYWBYMseU2XP8uzzWWWGEwSqtj1KqvyGLX4KrMvexXld6Bke/vr237O2s3kkFP3MFfVdTXE8p0RNbEDgf29ipq2+6B8gEWcoVW5RfkAYriJktev0NyNYaMd/PX1q1UcYcw/m+JdADRF0jK6A9Yzn0PE/7YYaGFHCsrrV5+j508gvSvvUYt+QSrJGXwG6/ZLg/othUifQYXKvBqsZHWf5eUHUMQ53NbLsLJC9DNAhbVk6ARF4Uulj3ANru6rzU0awcQX7DsaeJTbF4YIh+LeQfiLTLVNopev+RprKz7sfpIDLAC+pt/KuGN59OUKYiazBPu2XXuAKRc4bvUo5lrm2qXKZZlrl0i7Rb2pwazD11wZTSfZrMPrN5dftmbrOmPXDaMOmDdd12hbRcNfvTgoFRrxWJMIV+erC1xEOarz9zyrtqghYfSLZhE4T9fwEa7RHj15WchSblnr9Gs7at+U9R2tNzA9yTYb0Otxo1XWci3YoCXZf8uJBufpa/ox24CLALriZ1BGaIo/Zfm6GF1nxJVNOcy4vitYJiGm0ABDJUn2dEJEP8gvwSMET94K6tF2m2ePYH2JZGd6t6o2fnzzJSsDI7ZwV0i8J1ynvdsYDnRV3fwToPmXHcXeev4nuIElbhxZybyHePVQjT/porS6jWKsMORo1Su/oC1PvykYrdrfEYuNXsl58Tu8LU+i3HvD1OKE0C0uwR8VzMHX8h5RvBb+3pzSYPaqik5av/sljFCrsCZbwhhrMkfrNdcG7z6dFx+ypzTJIv/tboPjO3Lf0qSe4C2gdx/RqNQT7uutgOlof2pgTp+3MCdq9Yfohcc0gziKS/hIIEKw/ceoWEVIxwGhRpVFE5o32JpvBciP7nIAaDXPtTEM2BV4Hl9vPC8uQVzlNbqv9GiATl7iBNSN8pVxNOIFyGHmPfs6TLL2E2DPuXJerO7h9jTFRbzZ8bw4Q8OPAbcBhgTJU4hnXpS0iCf32ABhbla2bv5V9Hz6jHYh3nMBAZ2gncxdlr/4jjnSTtAu5jx9RLMKwSGN/d6fkZByFj8Es/ZjQypBPHqMICoLEwrV00ArbanzWgPTgD3/lD3VvcaLBELzHRSk28PbF7LzPsvyto3HAG2cPM+Iovghw4Zu7BLhfaCLN3IY8bymJdo8dPtVb2WAmOPRKMFNtQkzSDVi9BwOkRAArBswCCbQM9FcwA08rl6Oq7Ls7QIecwvn/gGL+wQWZRjQZvInADEvEsnMyYor5glSrAkcjP2PjmiQ4IvH12Q9bgXNnuIkKywOVm2XhS3CiRLLjphjrkq0vW6Ps6QnXOZYp+naA6m1RJ2m+DgsCH81YotBBiMzRSPYJq0T7RuuIKjnpLfAR2soKMru1O8k29zAtDkAC8iEqM2tF0FxtN0m0F/Z/QHg3f14U5HdggSH/wHXI6J/HJc23UrjK086ID9hEs6XIJx7A0iAi4vccn0bxA7CR5C/4MKWho/jqADNEhJk29vhfQZRUeUAqXY628dfQmiEXZ1Hm5CuVh0s/sFCm5GWOA2QE6UBC1AY83bjVAFy7KcQyvDAgGJqhMZc3We1PQNJr3BOrM261q1zUl+RNvW6yX2V9bbq3llEnUvw4NFktXXYoae0ztHlms0o+Lgw6Sr3FjaToztO7yNj6FHl7mA06DmlcEiy7FFjtSK7WnWHWtuWxtGIz6LqgJDPseH0IZ627XRGTftl2VR9kOZ17McFJDjaLjR5NK3ncqgazmdzbHN3CK1ucp1F02I2g6rBXC7H9pJt5C2MSRbaX1bdfnkRTX/0BVT9Gyjl2N+r6E7buWsmnyhPqWSlOKXzOErT71EOo1S6GdQ2X1NOM0AGpVSjZFLUcah4aKuOW/TWtovO/eIPPDVcyGcVGZHNoeRFLpstO9LWDUlzu2QZuYVEgcBiDhlJjd1q+8sW1n61nabY/Tq41qrrGnA0fT+Oo2k7MiHcD74VeAMS6AqN+lJOcHNPW5XtVtf6TLQxJjlYgBr7kUPJxmWoOMmqbZaeIE7xttkISPKDqTDnUmRkiNMfmWW+PNojYeLZOjcM7Y2vsoH9jbibpQvoNsh9PmFZ0ma2XZwk+/YxtvnDfRAsAsbb/Aaq4XzsLTTQCXkJST9kGdVdkeb2uuPSbmY9Lrm0EIelWF3XPHc+Jr1+gQNRjC6+W14LdfHicFUiSM/qm1eh/McaG4/lGQm+ObOC/wJ+dTOXPoqrrI7cwGM7O3Q0OF9Z3yMP5dzAgeQySu+0Lj/BLo8HvMM0wvH3gu+MLO0Q9Se7au52DOWgZw8fRUmUcmN7XC02pa3vzyzaTH2L+TRB/RQy+N2qbltgrXB+BmsYvWnKH7RNjTipSXQM0yjvPZCbv0zUjCG3sQ3AmNolZZQ7zCA7gwlI9cr0nwJdQ/sCnqzlaNCzMnGqynMMnpVx89pt2jKY/jERDtN4qnhLKnV6Op3Aaclyng2KMxbVpLGcwWFPuYfaGiKMicRoFOTohcI7zGLtvCHkCjD/eLJfVglYvRQl2AzYl8LYE1ZbMPqRxjFMEuxnhivxtyR01/DCwF2CAtE2Jhu/LjAEmreWlooeppPwuu15mMGjKo1eMPPU1/KmrLgdjWHrR+iam3265TidPiNBVYfZDd3SwaOGMY8XlLtQ7WGE29rD+D06LDpIrOA7pG9ooMNqo65rYCEw9OS2Pr9NsvwjeCYBcyevvVFMP2V4zRnbi91VC7bdZwU4l+OQDrNGXVegnVaoA5Dz4gxEeCBaBWFGe65ypeo51MDrus+sdrvufig2RWLGEbZwLn7iQ+0Nsou7EB23/WUDjXaQD6PLB5rcP4WMYLnV8IYDW0h/zYH5QzEP1QVGkB8+dzVM2h9ElgQSIgfpcXD+ERnq4JqzLNecgzPNDjrTHPxL9si/RHfKxS7u4hmXLF1QFKSZQuw26suL/vuMGieYjvAK3/Vv6ifeLY2IOcnSEp9uTaHLD0nvUCZsTLhJQlPWAaT9iPIRJNvbKvkHKEi4xyBgXzIXLB8d3vbmr0JvV9wPdrqp3NAiBUWB5urQZXehzDU3nxU9UpbQX25WF/M6O2hntYv4aXWO7tdhf6Kuq6XR7xXsaq1/O8TTHXDOChRe8HRDPfA0XjUXUVHgDcjowret6CzLN5G/x0+DtoqS8deNSd/dCBjw9uj2FiYwwEaVBFP13s0E0qzPi9oh5ChmHrRz9pmfzrvkU1SU59uj9Rrtd8b3DQi6b8BN/5TdwdTxRi0u34bkVUK4u7fXJFWoDK3wv6ayUb7hQqroHS5msVV1Gqefbux1zeTzStrKZlE3mMtn22p+16FrdpPnutdpxHbzeQSlS5nR9qYA7bs0QG4uq4TaTA41sdlstrQ+fcYqTZQcVeU9VmrqcDOXIK6tg5Ie6EpIh8GogDAmZqVsBwipAdXmIisUd1G6ZGlHxFSh1ZIsTk28yrYw1rSRpKsbySbLW8nlsW0mMTHJW0iSpI1jU4R2ccm2TboEaJeRYh8roBpgJou0ifIcQlMV2eyb/BTl64sMpmXxERal6thfkk/RenU+SR80ma2D+zQ+iEZrjZBZIv64PGoByGe0FYF0CEZ1/D4+l5T8ykwC7dU5vbb2HUHtd/Zoo5GlbxqAw65eXdcZzItyMs19koqGdv2BtgbZZhulLxPUI3dKH9iRIe7AITMfYRrbnuqdUC9JjGgPIFPz3VQVvR+9ov+CW6wrRclAhKJApqD7LAWDD/oFeqO1fcls4ftt9U6mnkPSpbBdaLs8/QLIJQmLNp9uvVbTk1TbOC6n2EQmg7KhbC6/iEAtuTy89RuMw/KsrmtgwTQNl+PikVE0hg9vCyoBCxXD/Oop+wQwEc+LbEDQvg8gkK7ucwBMK/xTgAqRoAU5jLnKPJ+//R55m3jD+YaM4EAqXh7rrq1JNz90nuu+cC2PuEgdw7kl26HBItYbUlpyK7rFCHfJcibNIDZemstrH8fSw2HJaAG4oTssHBo5svfepOG8w87vUkTr+hFOeyc6A6lECZYp5BGv/lmIMLf5zWqnHhohg3SY3uq6Al1Vn0m9vLnJwSOMBsREmFiQi1ZcdBtTx/VcmPzaVd/L5ao70gzgedVgHea8Zs43pPK+E2bgPhHGF/4cB2VErLdX77UNnpeHP+JXnRYpfQGcpvVxkt11HlrWUxqXLt5QGMvw424aM4lvNO48NgFbT1C1y0uDKGUpitbXfcaenWTpAitJM3mzUd0UDx7CAIe1QF3Xpyi9q6I7b6/EUPcTbOM3ZesQR0xOF81qZve+LNWubA3eieRJSVtlPCvDg9ZPX40s+OZ5YeRw9XOJlwhHOsKj1im5FcVpNeSNn9ol07SprWxWNpOg9rnYNjKJ0uWazeG1VveNsF6rSSAl+C+S+U2Lc1iy1XXNY2tpR6ahunbLN0b9qPQfFViBKQ7rzpLo7nyDOovDPw+QO0iYr8tyf58IGMHaVcdeqwfjEhREtMmFuSynVGJqMwriXZ/b64xLBu0tVSWYBwk7LOsCBHAltB4QISO9jNjWPhQmcaRN0nmBZ9dFUt3BVBAig9fusiqWCZ8gqlMgocCrVGYixEkoXCjuP4cKlUBBLsPaxrTOdxr+iIqmg+HOZC/YG+5TXo7X3v8fvlPvxIBfwFPhbtLFpd9QEMvgsdaiO0lkjSnNx5jS+PZEOPNxiyhldGpgr/uMPVvL0gUmlmbyZtm6Ke78issfFCV1XTtuPV7dZ3mI6WhZ7RkRXVPXGmxHN4+R9me0uS/KonowmgfpmduWpVsSZdsUIVG6urI5bK+YUOuz3NLipAXwdhWtquC2c8qS5HtWki1ZTELrWGsDGKJ4wwMtQ4fFrTpKiycHZx711qbDlO9rOEJc0/mpzYw6m7iD0eT12zdTTfMY9xrioAjq+XBkH9KR1KL6QuLXW8R//Qq7oCfrMiwiFNOwmSJ1HnbqUUnS6UanW4cK5KarIjigv5wQwgAayBRnOeElIQ6yQV1XqE3iPPKhDknWaLrj38YOtkkL+jw62Xz9TgLIXGVYUs54KjjPbtRNbSfiVaayMwlSAe2uqvdCUS2YDVcOmQCWrSxe3v8YOojrPwY6CGJnAfr+L38ZxdqVAxDs0naoaJwIqBFBGIcbggFZGCT+Zhs4s2Z+T1lvEjXTdHSNpQzIN7AoBsLi8bnUAf70OUVJpM/upw1y2C4XjUFc5QhVIMBBPjnLp1EVvHlkY/9O3eg1e0TnDDaJBT3HbM47TWJdpEwvbUMHfJjdGl4PdOtwmqhs7SjXvbuFE8SsaqtsFJMJa/6KeZlw3zRVHcUxKIppKrzAN5bRLnPM0OJuNzlHChnMC1m7QMNOsraL7+sgWEnZoo8xfBCh6rpIcGLvV4MDyeFArky2+6aL4TvnYeI97PxNceuw3bzk0ET2Nm1gH1N7oIl0RlkjqeDc6mZSmfzlWdMYL4FGMA4STV0XodJiJBoeLQzgC9TEy9M5jxheG7I/zGzC+Ps0/zsET54QOEazUwQDBU6oIW7xarlrd1qwHwuC3RsJUlmreEfBak0YamCTSdo68lPXtDqD/xLgKf0Pgl9dFyHQ73lWbUc+Fh7pctWU3ohf+pdPvAgVZHHAEjTIPmQfl4if66Hhfg6rpfk1nYmT5lSaXJrTGfyledMIL5FOMA5yXSNk9l4a/4RzXPPklun8lj62JRMATvO7GQzrqV0/11X/7zCr1XURAk30dAepy/ttYNLhpRgW2Jcfva0L7Mtelo/yEPLiWK5VsIaEQbuIXvC9j6BgdaDqCV+V7S5vVHkO0vhlkid82souo94x8wOI4QY/Y3+Ro19Eir1+99fXr1Y4RsFvr//qPBdYzynfcfoelVOKlVV1UyKRmJyncXKFYY2I9WfrbjGVnT5PU9kVrgyNDXmJeaoeMpVO09NG6kzTw6aysXvGyCsk2CFetaPkDICxu6mueew+I2wsscaPKklGcnRWaSfBuLWQ+TZaFUgjrdI1WB9tRu0J/cTqD5ADxHYBInvfg/ghq8qjEvHRTVWCKTeFQuXFf24meDqyWZND3bA5ur2FCURzMpjivB2dBuRqCtb28bOxOUDy6wTxFqtUOLMUQqL9w8cbSNzkSfRTgTbj3aGPigewVg3JqD08eXwc/5lOXNHp8xbmZIf9OUvL+4nr/AeIxqcnPb/q93U+gBvofQGDgjqKybr5MUvWE/CHWPFEjElVfBylD5PsDLk6JxExdJ3nJ1NW1z1tM12V5zfR+MoF9txE868O5nmVR2kRxfhnv0pPXvUk3Kuq/BIUVTJ+GLWTaIuNvdMSXKx0ot6ivXynLk/b5Ysqj++jAkxpZL6IoGsgKv4RyrHHpakOb5XxU/FVbdGf3LL5ASSgnPv9JXVUf8bCLj2uIux1zWfsj6xk6cKxtDSTrQOU1kmrrkHmn8WmKJrm7JXVmi++FWgj+REWZaZ4Tk6W8bo5gOsbq84lHAVqstq+mPA7vCV7jcFOyDJefyvA+gcs74XODOcWOmVQxLZzpBSOiKXhbxIwS2g/lyQ0lk93atmXOoCOqmU4WdGyPkneMirdtmWXYA3ABqxpi9Op8qVEOtcgUwxmFjozXML6NWfUdnVItjZVJDubIn13mkp2adXWSBoLOXlpx2VQCD0+l5evkFwQOrxFW8MUUoF5cDZQ19UdYXkaIWup5mvJHEdf6Q8LTFfYvsTAItt+FOaKPretvtDIlDEUBKOGs5LLaabLtQVXJyIZ2mGaq+tq6eU7Q5kVNAQY815G8LOvkQRKS01zbbcvMaDoth+FeanPbStQOEVrCqXdqENync5L4PgLmYNgUdfV2pMoqSB7DMFMOoVwchr5NP28aBtLQlNFAaw0LeBJVm0nMvZeImJsIRrhSaxZXW3TRBNZgRTvY6foWV3VNN36jDZbVHTJ0eo5L3ruyEhMlKXaIf0MUPZLspHZSVjATXsjl6Ta/lyryog9UmRVrsqq/F5LMtVCr9sDh5diTDbjiEjenv1B9PyL+mksX5i/V1HTe69NRwrLixzGo/t6dhWN7s46RW8m6Ulre6h1uLG7xNY2dt9m8ZSc1kPyQ/aUJlm0DvAYz3nRggVTsz8hBk4L0OJablGwNP0B4N29xdZi4J5Sla4TgNSgaPwD+FoAn5CgYaH4W6nIHBVFFmPH1nWrTMhPJQJqMSrNbEjr8be/WhwESo9TJAeFxgpjPaoDzepyyRrWJGqa1ubw1/u+kEj2Xnofhjjofeq6gmhrX6gXB6YLNddec8t6Z4xF7jkHJIHZwbt0ukkO5p0mm+Tg2ysErwTvMAc1fBfoGnNN8BAYx1ESpTHw3DYVo19UmsrANZJk0DqaybxRZG5nunyC2NBmDn8iFNhhx6Q3AY+DWjcbl/cFGo+XzsfnIP/UdQXRQa7yKH5AFJ/IQZnc8wy7uyM8A1zdnj+ABD6C/MWx+OS6j7HzGz/pFb5xti55akdNOsd1LwPEBjIZlE56bC6vMDo0ZAihdDCKD89IQqcgnm6yQ+5p7NmDkyHkPFDNV/lscdyvIHZML8Ef+MU7dwMBA3OYB5p5ECJYU7BJEGq7FOZw6BJERZaeZXnNTdObQRr+BcTsHeSAwHE8+ufHx3ahKKPb2y/ZFKFJGBERJvTVLkQVHNiYUjRRbEllOSTbN2k2zz1bRnScEyTL/dwGeKTDAqGbkQEWiIsob9QEy4O2+ljMoSA9xCEcCYMZ8sJ4P8wT7AZNSZAjHsKn/6MZ/sKs3bsginvmVijprJS6pvPTqroym0RhV+cNepVWqEi2ligzDbfb2bCpOyYVapGclqryDDc5yNlpA7Laorl227wn1skCl4OdqIyS7O6NFvewPI4uz+WE/7pl40A4rhU4jNAZTFDHArwDXb9u33Q7xAP3HkG9nWa5ltVlU96ogPjAqlEpaxOnhk8cequHMyWBDsWSLloov92DtqvOglMHe5CbtgJv5AdpxvVWGUOCycmkeCt67qkuPBTvLzX85KJcy9PWq8ChFED74qJu6IAxgvALLvYOAu9nEE067lUcu04/5YQjW49p67NTu4ru/LdlCOQwtZyn1p8DPXw0tFZrV+XrfigLyXJLJ6vWUSZPCN78HuUwSvvA2SfZ5gamkadGPAx+4GV1XauHKgArD1wxL6HuLDFMJZ+jtLqNYjyI+QUa7UHnrTDVWtqmBy4JkYaEQvsB1+HAPoa9fRQVgFDO1qnXEBb/YKGNDnGOigLepWgWQcJH5+uBp+dDMFDrbIdt+7a3wgpyPc3bIraTZ0RI7sUPYc5uiMHya1V+vSWg9aGtNVHVAcCoEdYt2ZoV7JrFEJZz06Iq65hx+ZDHHwZ1q62jBqVcOhvy0ISvJ7yOc1BsRhdqDUwwi+EVeC7Rp812/DDfOJTKHxXM/S8vU1MlLfMsCeHgMNMxDD+FLESMrVxxPnThec6lP1LDhmkZ2x7qrYYGXeWBSVw+7f5WWuJaKXkHaaAtr9oh24GE2EIHXEwOq4jBKnKUwGhgExBAUs9kqx0tFIivptSE5QytLhHYA7er61LQzd8hZIcm0YCbX5Zk+eqPKspBcXl3M3qnaiPJ+p9oJtRXZkZygqyvH05Q0XmBkNBCDuIAQXU8dEgOiYiGEGrtJ5g+YI/VOWOO2Wpby9GzDPVOM2XNaUVqAzQ5rD6fwRpGb1qAwzqjmbcNjbwDBH7rI2p9yxNvcSJCjR6t7BimUf+OXfuXSXVDC1VaosHAsKP35QwmIJ1i9T19Rn0qwuursmXqC3iy4ydjEXMFQd4cGzlruB3GQcyo6wq0Cge5kdReIMDrmuUxT5hDjsCXaAavaeCOShWLjnWv2ay96iDPISgHimwhjyj6KiRmQiFR08Ig5wqEEV2CNeAvRf2/g7xQ1zWRXw+vw2i1jZEqXRXJaYrze+/BViBGvBKmEwMuCFmhvREe6ED+U3aXdQ4As51x8H0vN8lxtn6Z4lnaWnNsj32/gPIpyx8Cj66dzPsckattnqKvQTlIQHVdNfl92b5GmUeaOihr5or7PdgAYm64SVzWYVK+eMPAHLhRXRch1OjLSiA+NbAvamp5H2btGpP7j6P44TwlHlL0A+Iem1gF4mFKBB3g0fbFoUJUzBERVcF60rABQ3mFbd9ggZBbVFVlkg3rQFbjfgTZzNL+2ldgg/RkL6cGGdxBkKjrmkc7+w7BE2rq/fQ1u27FjPkZMSK4y/KXALzMQx34+MDHk/FxI9wDsDGHdODiAxdPxsVkKGDs5S6ZbTZZ+oZHOrCxuq4w5iPUo9/zrNpOz5ao5hlm4YBpYCTvzzFNBUdxcgnizOn1a3KegODedCCHGTf2jJvTYKs9GLd2LjxfJ10fArttfMt9mDp705U/8PM+8/Mqqe6mrzXUVeBPUXpXRXdjLQyr+B6sqwRcRcWDywEKKla8oUEOc0ld1zy3StD6naVrz6fDArktWouNMB4RZbb9mp7meeb9jOCniISKz0uHZ3hw2dN07VhyVcUxKArXeunOh3LVMFc/b29hAt2MF13Zov95EDLquo7W6xzxibenDEhAgAshjoug+jHhunfSg5eOPa67XP1Ri5AoHK6IOfyOU0BUIP5rnxGz5vsPMAcxfvXuDYN0YH11XQPrq+kFMNvV5aUowQbR4YlsRaau/hKTztCN+K/Lsws2zP0BrWPNRQL/mdKBHSbLYbLs02Q5qfIcpLHLA879JGlBDpPDeXL8ZSTDWz0wJwhCV/lIEwOMPS8+ZRjAn6wm5suzLN+gQaeeOxirvk9wAxGLXWW1Z7OvwkwmWHG/pKvYC3pJh+8jkW1oT41GNvhFBGOpjHit2nh6YBKM4o0AdRDS6rpoOnlfXA7lO0lG0DsmWbaF8S56cQr8K/XfVOcSNuWarJ4RAeEjosVntM0nL9i4TlgW5zBbNbM1hKM05qFQM/UqC4WE+POfINbFD/zzOBojjl04vVUehzGMvPWT86IxMR6/HFXlvb9pngK8RHrrFlJRc1wxR5KhNBsrYr3QYuWazU+HdFFmk0RuUee1dX/vp45J6+ncyrb3mYZaTuX0WgKIaRp9+pS53GNEpe7QfHtDoRykv7ouiky4ysXcdKmftZ5eho4kWDgyy49J+jzXQn7qwESdTTw60eQN+qooXZFMqMjSta0NLkpqMoYQJzj/QaSo65rZlDxgInw/Tq1OHhEWV2Y22wjeuRgwmtW5eNNiHFjXmXfGcgQa3KaMVDGOk7FTS6zxhDlFvJ8cxXH9sIn7pKFxDhNHXRehk07q/mWU05HGlj4k8g0r19eFg+pM38OLLOdf5rE17BdoJMMQyLbtUVEM6AAj1VyvxqvCO9AlDpsJbiM0q5HUInMBlRppkW+Ejsd9wk5scVAHyaWuax518TiO66Wl9mgDukMy0yb4KhmBooIFUiqm8eKn13f/gNYeB87GUuILeCo+AczkniebnbCQIx5khkZmSCnmfdQ5kxIVZiZNodX/vQIVWDdkcmd4CubA5eq6LnKY5d7RdLENUOvqM8r6imsd9BULsdpdZdN37iqbpGuXYJu8BOmfUT2T9OnkZPQqkGo3eh37pcwF9WNb4Vj+OQSe9+owjKJBI2qUysMX1p4lOYCh1rRrNnN/CKPKIxzEKDN6Hcac3IP4Iau8XmarX5x9I0Ad1nF1XQOCdaR71jv5fOcKTagtzn9RR2QKhXteXEXPp8+AooYrFAJqo54Fe4Z64Y+VCrNd8+ilPO+1RPb0ctG0jPC4pXFBrwct5bUEFJ6H1/6GFBSeYr6zZB6RfHgib3bJNb3M4nU7a2HnJLMucWwqzNcX0QtmgY+wwHfc3IWWAvAgtdR18STzFVpkJBZ6+YHvq3SeKXjoWizczzPTMsI8My4YdJ6Fm2CHmaVh4pc4AfXS7DkbMNAFyGG29r81UUYJwfO1fOAoOnhKhrDGhDqnCxT15DyF+AR/yZKMbqKRFLtmS6hFF5NxUF6xuW2dhZXr/9Rimd+yWctzJ7FMblTDf5HnjQlZnDyPCQbJ/EYAPIhndV1hQiO6BfiT4JDb9fOF6u3qn6vqqcL2KoVRO5JS6SPMrOs+ey9v1LkEQarJ6qnqJWQ2tK+VOEebZ4EOgkRd10X7/M87PxHQ4byfx7hqbmrMs6JA0yLx5zIe6sBnS+UzC63G50Lm4SLmwFp59wk8gsT7zvt9lpdd+ISp73acoezhKh/YzG27cJIj+84qb7UO+b/fDTz3G4YQiMlBnoN8iromjDk05OEF/qjqmGteu/j6Dd+PUaF/3MWFdG5XaVVXaLVXZ8NdmV2BJjqXwzMw6S28q/J6x9rgHES+uq55rh7s4SsqlE+EU6z85zcUwoFjnTl2V9/AImG9XDgHlyvq/x3YRjP3SUSAAeYJpBSmcVKtwXm6gqjKqLO3uXtutbcYUUIZxNHA4FpkGFpcwVIb1jNMLZO4CH8GZdRElNCp++Eq+wCou1UT1DfNWAWOiXoJUiSSj4ofcF0fsnih1Sj/laXj06GuCm2Vnxi6+7X8OMMLVAD54HDvRrXUHRVFFkMixFkb0QUkL0BfN//nVp3TdP2qjfEly97vP+pxqoOL0TnRoKHFDm7R8oaa9dvr/yH0erCO7uiOqkMO/u41v0Z+TesT41f4+Bn7mZ1ERRytRdojuq3ZL2Q3Xd/0RjuaAi3UMC3FNRimMdxGiVEXuNKGazhuXVcPn/IBbPHkS0ujcTJpQFNE3o6uOo56Q8T69S3FgXaM2VigTZmGf5l9DMbkXnMX61g+Y7JdmIEx2XEyYszWqj0TY7Ybw0FZyWeUMWO/TzVnQwHXXDL+8ubNOwHZiZFUjZiAhVR03QWphvau9dWpoy0aFKx6Nb3BN9RUA64rJGOqNr8NU2nrkDCYIeN6kaaZ6TaEaYuMR5auBnOxH4Am7Y/mBhJ2STIgi6yUDWWCrlem7ZPQVZJveBCdxJoR6SaQcUYkMmlHW35uPe4a8Q18BPkLOcwZ0LCYzBodzkF5Y6FlvEa3c7TlU9ea6bQwKZ2NuIoqNzdndepAtyQNsIBQQMNhLvqZshqLZXQxOwVlL6bjUuV4mTSBvhy7CC413Mjy+Ufm0R3ey6r6MAOD7uBu9nOUVrcR2RTlgztaWWYZY9L5bLhSij/H7lbXkAn4SkfnXdjlNkzNdIPhiQGxpCyokYKuPDdYpYT/dHUtRi4O9mg6ATk4niZNocstirMNV3RZmYn4eYdXd10/ZmLgHVzlSSAbmN59BuV9tr6+BAgNYueFE2wb0Bofh4vKmJgtZcO/BvXJ9lEkMaA1clUipQ4N2yNiFHDdwisbLcstpQud0Yos0hrMKTH/hNb2YIKprB0jo61kXWS2Sdy4tw8yI5dPxoadp7w5A/Ko5qwXTi1XtGEC5lHQdJfYhhU9Q8PMzpawLMRiSxhJKyXDs5O0PRMylZTWJvUzBec7EG8vGvTR61UcIGaVHop31x0sDsVFZAljmTGtS9+PYZIgraWtYLCZbP7gVODgzUkhzi4HajS3aPp7K0Pt5Qvo6NHkdSGLUI1sEVOQfX4FaqgXE0isofEyWg+p23OzyKs6DEHtTIq2F6Tl/S1/FQ9pS8n4VVbAhmn1FUo4tw9UsDTONerKBOxrNIYm7aCDgczCw8dJdneRFeUw3wo5ZbzaZrLhTxF4p3hS2fwJ+FA5JjvDe82DvNcd4+i4hM+s4sDulV87JhTgJXyoYvBl8KGqBxOxomp8TKpvy8xtkb8EjxA8fQTJ9rZKUqzzMglDxvmB4ho7vVDSwVg/VLvabt/0bnFcbdex6Qz5huNsYdOvC87G/vjtqfMSbIYVASGnjKnbTDZMLALvlCKgbP4EXKkck51QBHDr27WjYxwdl/CZVRzooAhI4SV8qGLwZfChqgcTsaJqfEyqb8vMpwhkSTIsBZlc0mUdZbBawhnAnZJ80qZPsRbLxmAnJB5u+VFaPGEvgEwIzsAyBZVPxWd1Fltuo4FlmqGEgZfBa5KGT8RtkrEwUvFQ/ll57XtWElNVnOXra4pjdOyhKKPiQTq7LSeqqlJwpZzdl8GbA12ZiE8Hxs6UZ+tS83EuyDewwLFump60Jn/cUfXZmL6YlH+5Elb8q69NczKEM4xwgmhGJHn24OeHMxHj9BlHEoqSo6q8x7wYk0RugJS0MSotI5WuoA35zBqwU4eOVl2aQEpajbFJe2Y/jjzL8mpDzK6DDC5mlXFzl8uGdSXQVnwaxCFH3YgJOEtN3N1hIxJ6zpCP2LxKRmqC4VlyEgc+EyvJWzEVL8kJvDvMdE3+rZ+p0A42lVHJRtYcRINK2Idq2+LWTFX7p2I8yXiYVN2XWoIQq7nGQMjUXR5DfNXIKuZbKN9Jmj6twGPGw5jvFqB+UfwyrCVRHQ6vglHgKu6T8vVCWFDRh0l1OHF8TKonBWZjxfptNUNPYllmGSs2YZLN2VAKbO5AHEh/07ViAkbSUdekerbkzBw1uB9gswXkot20ecjbPhnX7eSOoY0z9Q2/9dK+otjFFVNxibaULlIZXcAlnpu8Qk3oseVxqVFXJmBaozE0acfsocqkPaklnxU/cS+qjsy97IusQzJ7oXzLdGIupmXGzaQR3cPB8y3uxE1Hz6NcPuXybuv3x+PuDgsqWj7VCi+OxS4x21C4EyHnCAy3iwFNlK2flO12MHjJ7/C2PImwQwRq8n1UgPUPWN73HKRil4FyMrZsi9hw5VA1KrEo4/5wd9sNWzUB7xkOgxEnShFmZ0xGiehYaIhfpKV0TOmqNeorlLCnahbML0ONujIhT2vH0KQdbZll8fA3eorZMTJTdDJuZmvdHUXUvDNzMbV0PE0awxScV239kpXAZI/U51OqrDiLtcpK4e4OaypaPpWyKo7F8vdIl+AJTZ+LDAEU7fwZtL3rCsnYUJLfhiG11e2Uld6kJxNwq8n47YQFX9YRM0VgsKSx2JPuf2wqcpswXkGPcMBKcmtOTyQ2mypmpu21Rw51d5YXecMnmK/ycVj+4tK2u94zt6wyxBdMbh3T2dri5BXIYvkpuHo5LCjtwoScKB0jk/o7gHndC9qIvWYeK1zugM4GPLJP0DsvrVnVkql0ZxWNTXlquwDPlUtQVnmKX70CJjcj5Nnl6gCV005zllaxYzqzrg+TaMu6cdoJPblvtKHcUxUIfmkvpAC00kwyUu1JlNdr2XGVrhOgPZvRlJFrKWx2O01FXZX6/JDqw2iLhUHLJlFDBofCpBV9qRlVZK4ng8uGssToTLib68dgN+bg151cRYReDLlVqAqMzqm76Gsx1Ik52HQHPS/Y2HhDHCrNPRg50Tlc4k4yprYHE3Cldox2iCVXWxDD2+b2/1FZ5vCmKoEpi+pLa1hWXtCBhQdasIMsbdaj6VjcbIx3geXlPfm6xX9eKzhSubrbY0kVDA2MlbLh0ByZjdloWs4/VTy6O4XG4s4bRpZFKcJCFxItg7vJdj15Z1tytM0ynmvyqT//jPPv++wrlgnfuM+/GmfuWdguy1fRnSY+nJhXM28cZgiDrFbBUHK4iGcN5vcoh1FadsNykm1uYEoyXqMZAx9B/nIFN5q4tnY4GsJpIBxoatwg2d1LuuOjWWEdGzqdXLAdU5OW0eXmnvy6/hlu6AwgFsHxO7y/s+jWMqbGDu70FL1ynQUOrB+A3/eJyZfD2XvIzsPWClOAeRhca5IQ+rhrLD+HBcJ2vC0mwWIMDnznvkdJ1fVQ4FZL1tSCWcwSghNgqujbo542Q7N2sbPHqMPzzSQj/rCYVTzE3JPrehVttgn4kD2lSRap7+DKswe2JnDgsg1u286xN7eKtkzHiApKG9mtmJKzcdgVBDnqdQyY2PhKDpNnl3FYl9OGxxTwloH6CZ/NLUn1XZmARfVDZdIAutwCGHRouyjkHIEtd3ETqGz9pEy4uxs9ynG0/mlot1CV06zHavdcg5VZWd8uMathX6Zb4odG0aQlTMEFsbM9I0/Hwi7MG1K3VDdlFt7bB/HZ7afOYFLiOMTDFyosMIx4kivuxaKaprhcwFig7B3u4RxzYZgFzGXyrBc7jqP44TxdlVn8sKpuijiHtffD4P2OoYKyqaAoYzMDBqvdqUsfpr2ZgMVNx9Nm07Y4rh5SNgbKTczTu6g7G/ZlRobeQS3m6PYWJugLuB66ECvklLFsl8mGSUXkyR+uUDZhAm5SEtak7rlv/JMXW+yWd3UR5aM8ruJPU9VMj9vNvRYPE38nVmHU30c0Yz4j1sev0Z/l2WaQ7zRl5JsrOrvdVkpd0fRsZ9CaSbY5g8Q3aQVdbinMd5XZsl5fYlTGo6qZne3EtkzPdCLZTdrQl5pPSUMK9iManE/Z3TX1Gw+mWl1Tl5Eqbn0WK9VNU4tMieMav7g9hkF/ptAIh8fOSDdkiy6CfQdFpSzzSAy7m4YdXQ8m5s2dVB3/XoEKrE83EUyuyb9Hsf65M1UBGVdSeW24UlmFhDOZNi+OO4d6MgGHDo2XSRPocvPFLLsH8UNW8Y6Awmd1DDNDAGlMM2lZqwhnprXLxK/Qx8Vxum33JuB82/E2ktd84RmDS8ZVjkhxdxG9kKCr5ynEuENhrjWl5KEm2QJ20SZ1lZnHeQ6yAzNqzARcaTQCRu2gyi2GC9uI5gLbmPKICsCEN52eEjCsXsKtQ1NjfqFs27sZ2H9ovE2axJedbTZ8yuIogf8C67aN15+i9K7CdisVA6qLyDheyG3D65qqJNzdtXxxXD3cjwn4eHjcTBrRlpmRYw2MEEPGB0ujg6uxYRGe/jPZGXbEvnCKypQvqEyJSoC8vf2yifLy680/QVziJPCMxjsmDYvSNCsJyt++FeAkyTEbFL+9LvNKFJkYegVKQ4fD4vWruoiVh6DAtAOVGtViCqsBG4RoH4OWYfTvvw+AnEQluMtyCGQoTeLLcG9gXFa5FKNJMiWIDonJYUweHLUd5gBrCTpKUdmGoakwJFJMJgKMWce1A8HmMRiPOv/nKK1uI0Iu3eSgsw1CD2FagbWySsp83ZowANJ4gEgJ1znNDDUEs4BqFpC0YaK3cew/g/I+W8uQ2BzDgEhO4lsFj2jFkLaMyWBMbCKQ5ZOByzIIeZxkdxdZIcVq0wZBWo1IBtKrpEMgWDkDqxLvCS5BQZZLKaAk3yD4F/BUqER/mzY8J7MkOUqLJ8VU7FKNgFQQxkyAdQwdB9T3LodaAvINLAq0lqP9WJZLeZ7PMwh6+oyW5TRKjqryHiskdeQ3dQW6/IOVEW8gFQd3iWYwV9kWxkockmoCFKkQzFrxe55VW2UrSOogEDEwyTAaa53hyku9yCqXrJKMw2JW8t6rHF3+ZK8hug7QjH4qgUG9om4Cg18UVcLU77oOwIgvCcrpJX1x0GDNU2lW/RNqhiAqirGPvQ32lnqqR95N5iWlwbax8fDl7ePfLTBTz+ShLTWKmiqo6lAfNCE0pf3Rxkp1qsy8GlPikZibSkqRYJdmQJpgbJoKtMH33Cq2qM20ChNsa1BpdBaLxjfnqEOLSBM5Q7pudAFIBkC66+kyFCpUwKAinsm3pCTBrPjniMgIJUqTPtyle7RHJSS9kWtwTIZhDV5+k0Oq0Ktu41jsGK/AZptEcn6U5TO1X7zogPk8xiJGg8llGdYrQAoQr2knJJ9neOcbJ2q9uEschPmWa2C6xGE2j+/BukrAVVQ8SNmcSh/uWnsnRNq1/pLNEO+BqED89APAu3upNsBkMIX7AJE6UiimCZ/HYEuW5yCNFcaHOnFYExPuNSj3AFaTl/Ugls8E1lt7kGc79zk511JejeZQxIish6v9bQfFyWYbwTsp8dq04R0s5Tsl3bEyDmyDLEfoqhWcbBYjc8YnUKJt8xC/yHMOVkA5oMlQGR/BoQHhvW+kIyM6S9nCKpUYlRvY4DZEduit2ngpnBos6zDBNjOkUaec8iZLTqUHG4t5c60592BzDA9hnhUFKphoIPk8Br2XSiUTaYR+lSoFr04a1u2iZ+0xQJ8+TG+lNUpuiKJO91RnMtfdyQuVWXo20+Xkjx+FaBtcge6oueuN5GBIONQcRG1PnGlUBRxzzolyOlCpZbVhKsluzGv7w12VD0Ql7iq8iBqCSi3j6rhIyKPuA59VRhN6LmnIIUCNyy6t0fP6aLtNIFhfZXQ7RaJo86t7pSsmIxZ1mqyhlRZVQjf1EAShXM+fJnTrctv0ry0UkmYd5rhTrqtYfhSuIZm0gEEPZeUCEE4KK6GdvJ+hBDz7oo1avGtevpGJYfkDNwYcMQwmIxHruhCKNp0M7fUTJX3EvIPdEopo6GQo9ZXIY0sxvuJhfUHIat6rYY3BkVzTKA20SVCnOEjzqTsjyy4jEefroiGPFHEStZOpmXW+UTKUuswgAyiLaljMnIyDlUhIqkUPRNfhOSrNbtfR4bnqR8hppizr43R9CYoyR0wP1pSLlUhFg1Lqzg4XltFUcNfSkNOgBtmy0ffYn6y0q9d155ImoaQ0o6ZrsvxSenHeaDpySTFHplAbO0pDGz6LugdcThk9KLdDDSl4oImIwHkGqknBZhzuBzu03mRh4STE0XOdix2idamnGiqxRIi5NAYEIbPUGtH7uuqMESKWhCpKIvsQ5BgmCZJxncushipcVoPusCUC0IcDnIhIjWNsf4FGQyUh73Cv+CI6OvVuvAbkEoBlckg1AA70krnZ9jfjJETTF1B3UFtORj6Fo7CGhvoqJISkHJq9Cdl6UWuJJ2ZS90bIKyMS5detIYwINQExTrINuSzau5fL6SHk0/eDz+4986SgEvooSe2+dbkEjxA8fQTJ9rZKUiwdmQTNLmao5OCGYwDAm6qG1Wikm6oaB3K3rv/auSlmUvdOyCujF3UZQUMoEWrkuYkrbPm9vxMhp4eQT98PPrs3F0lBJfRRktplbmZJomUUNoNmCtD5pMaC+n6IbhYxECMzRn+z5bq+oSLvOp1F33Iqp6r73T2bASLQUDKziIySjiT4npVEf8CudNf0bR85OVTZ9f1RlPIXuxpsBd0UQ+BCPe4G0TV3h0lCQH0JTT+1BaXcJl6B0tFRj69Ztpq+htvtDRJRkdNgazZINMut3uRE0l0p0279zAqqu2xUXkbRgUtzGiqbVTnydrG7aaelriSXul9iZhnd6Pt/GiJJwKagCHHVGiYJl22gG2xuJVFa/7EhqnBwU5Dlmr7KqKAJnWegB1RWBTWiYTLQIBIaMFczQzJHczNUyxl1HqNxrPsShidqLDkxBIp6CQ36gq1ObFD5TOY61ZcQooOCU3GInMAOtKlDyw1bdKX51J2RZZfRpr1ArKGLFGpkK25dp06ecjmGmq+TosY0mEp2ym5bX/eBYtTuYPIC6m5py+ncwfgr3Qa+YfI6NL5ho1GyuRtvSEZJzM7h/rEhO0cgIBumc5CZXacgsbco6cVnGZg/fU7lLBw2l/FI05BA41YiZjJpvMaFxJoQ07iLtDEVri9Qi++jAqx/wPKeipEgkmaoiLpzAyVlZKPiQWioNgSsYqdQxkVZqIvrPlyFmobyAsMdlZbT0c9CMunrkJBSOUahKPmNHlBjcrKlLPvLFB6TsGxFo8s9HLFkQPRTWQbkVJ9TKfGa8ClDEo9CGpMEkhArWnVUm1/dJV0xGaUUEWI0RNPWMLIeK6t7cIYOF7LrrOO0scR1HStHv01yIKYkIZdD3S82o8r3Uno7Q4cz5sSkQwxd94GL1ERgMw73gcmvI8mwiiaHlPnOqajsvHVu/WAHLQp8xqE9MJffaz/NY41sVGDiSQ1Ic2lOnYCQFZBLBTbqlVbqSEFHFtp944dZSJnX4KzIgJEsT58mZic+mNg1HVRYJo/U2XUiRFlKLpuEIGha+aQGV28mmQDL4amom5fqzBad1M1OT/pNNUeFijX2CWVei35prBWeFJvGdsH6nA3fEeIyatwEZPnD+s5NSyJ5qD8Dkg0UHOysvrzmqpUyjOEwgQfqHPn6lSZ247Uq4qJkejvAaOanPZpUKOjjWOoEhEMDZAq1GVMEnyf6gJy2k0eL5srdegpPOM20DTEeUwVTeQQPYMKWKoeMyTZIAjq3d+QAGksto+r2h6KKJoqqabQFY4hBCpgiaSitjyU7PAjGTZgusoOuScPrt0lpL6oMr+RjjsmkShTfEAfyu9PcgdBO1F0GSbW6kXFZ585rtaAxqK3Vevg8I9K/fq5VkWgxFnocazJp4SzGqA0yaT9Q+haoB22QSzw0mlW02Sagj8WtVmq4nMO6CFvAW7Xh4GSLZx9t3Js+XVxxxnFdRh9FTnWH5AVk9KEjn2sopACcwMe+r1mzjImZTLqiWaqsyTLpckQZSuufwyu8ssjgrFCV1Mw2jY3WqoapiWlFRhcCjkm6mYjGvZM4cAxhU9yi82oUI1KL70PaUF5T+VTHGIonF7SnGYNl1CQYKioNFaB8QEJ3OX6oopFPOlT1awTFUBH7zmqERlCiTiNAujcZrjUnu2ImdXeEvNJoQ9QzEbpwQwLWyKe4wkMLw7e4bCerupDytoo5L2nAR56a7HMS12d5ttGRTpddJ+uVpeQLC/cIhnYZUUNPS7qrzIJwVGbjvvVlAhONAh6ZZNT7INfCWyES+aXJrpE+6lJSmca8kKKTahpcmXwTnksJSj4ds0nzmXVMx2JOlJqKtahXTq7ZF19E+ijzqrukKiKjE/sei4ZOSlAJrbhXbvw9xKQPrVxL3naReIyZltX4exlCSN0wVK/J6PzLTOuTsankUZwAfozsSzHX5yksYZRoXKp1BXQOiJpycudG4Z0brX+jDn5c737pOz7X4hs8w8RUljXvuArChMSmtwAMa5RQfXBQXaMO0q8SDYcclOdWd1ldSBlskH9MSUNQDfrIUZqGVnDDlXtoxR5cqcdYoX99WwNgL7kIpiDv0n59ix8l3ETNh1/foiwx2JZVlHzO1iAp2oT29c6+ZPPl1WobxVic/8/V61fPmyQtfnt9X5bbv719WxDo4s0GxnlWZLflmzjbvI3W2dv3v/zy72/fvXu7qTHexsw++FeutV1NaG6h0eZScfzgNTiDeVF+iMroJioQxU/WGyHbahPl5debf4K4JO6Cz5ws+bWjcluhmWVQtofHQFjHbJHw78Y/DbcC9wS8wc0dNBzy4D3lzxAxsOAgdAEWZkAREoGu0MSL8nbaNV0/XyNSZkm1Sfu/ed5Vl+4bwOOwKeaIXUd4QCbBAY/oHkrQLtUc+by4yAESHAAHSeeIyCaJmL++5UaY56+3AoNxkoDnZDc+D8zYITh5KtZtKuZBqM+200CBKCSa4/69igilWMD+qzlSHwODRuq/2iNdALTwIcmLFQ8ZJp1ujo7/ZfHqL+YIq/ssLz8A6iVPGk1MNUeuRSu9IaGRxVRz5I9wDa7uq81NSt4LpWG5JHPM77CA5HVsGq37aDFPqpsEFve8iKM+W/HONolemu0WxzVUijniSQ7wg5pf029lzCKyKeaI37ZrBSKbsjShHkSUuwvwicU27olCdLdJFphRjnpGQpd1b7MK4Io81hPyPF3DR7hG6nfyIp2cbIY9EZ1VkiiBhUQL7W69gWkT5pjT7JgUe/5qHppW8RiVbEXfp6/pR7S3uxCWTT7NHPUzKKP/AC9PdRhMGpNNsUNUjpaQaId7BUt+UaI+W4x8kmRPncMWue/F9V6ew6KG7TbPHsH6MsJBgVc4BCEDLyZbzNWsHICX57Bv/VVWRomcPtIcTn1QV6LMZDFrKrKhv8qOYk43YlPMET/BDSxxg8haxrVYSLRo6UPFNRB/sJgf1FNmaL0pvyC1j1eUVHnMa/kdcROLWX+x2fn2cZfYbW//3aI9TRnZks6nmaM276N+Le+xm2PrAkJjy3NY19ArA3J8Jt1i/lZIbkU4VjFSA47Waw6Nn8uDuW1Gt/d3Zke3/26h8jdl+JGlv1so52lST0/KmZlR0SXpNvPvuZ5OX28V+PIc9tQ4fd7CnJhxP0QvhZwyfB77WsiRMEGQzS11LouNa1TwLvLM5lVMtpCmTEm+9WKqVau/FWivfpcDIGqOYqoFf9IFr7D9mWVPMdlmXnbnSfzEpBJs5FdT6OQlTsAnkN6V97wEk+VwreEC5DATxlGVx6EWomAQGEESy3LYUB4HSDlNI7Q7E4Qik2SDeYb4oA28woOyaTb7oTXEMzlK2tIn9/iAid8bqXLZtP8qej59Rvugkm88lWCOhwq1Dy/zLMIlWelTaC91nj6i2YYK1y+b8ujKTBaSCvujym21XJK1qYyUP3qMYBLdwESAV+dyq0neCXkOi3GAqQZdTLXQ5LOnuu+N8xM/uLJ0qx0OvH0hloSzLG/bdwzQvlLY5agzWhwjRPFDhi2g+GBWOEbgEy33ymp/a2HTbO6ara6TWHLR0MJNtZGPuzyHbQ3R81ANfA5LuoF1UxTyy4ok2WrW4TXjuHo5rsqSN7KIqdbIP2Bxn8Ci1MDzWSwoU8uaBCB2v0B7KNGgL89hYdhHOxlSFMbc6sWm2Fj2BChrjK/JWgLTf7W2M56Q5x0lFsY6wWINwjE+okTSOjbFDXFVRjn2vQA48oJwMKLP6Vbjabo2qo/PZ8FhjWnwNMUeHjJGk2WwWvWw4GFQgIx9tBmtdq+G9ekyWu2i6Kum3BZKdwtVjYnWa1BI41BI+jGY2aov3eMJR9ttAnk9X5rBHP8HgHf33ERvv1lQR7JHs9+V/YBrHqT5ZEEvSX8+WvenWyP04kWTzaEunWBRZprziDvoATxIgOCt1H3czWN3TV+7YEGC7YtLs9HRC9AsPBJzgJjqgPwZREWVg28pLBXoTA6HGo42oi+QkOiAi39osekMtu5V5IhQtKkJidZuW/UbFXJTjCKLYx2YBvoquhzOvejDhGp7QmdbjCtL/4CRjy+L6vkmA2cWddFxvFnwv34+G22LpRZ+Ls3Kjq3y7+OSluI42JaWiR8+zcay12gc4h6ESTFHbDQLAY/+bn0GWZxk1TZLsZe89AySSbdY5RWobmjtKJADdqKmq/iVzWF5rl+fZNUBAoVzfTpxMVKvMW5D6cvD5mKvtZE7iD110cWKPZV7kqsjWQIjwbxHPtmYrGoqqnzHZOkHNy89Vu12qToZElMtkCFxohEg+882rbwDK/gv3rrYfXV0ayuusvoSiRx/KK99+7/KjgSEREuT62WU3vHCmEmY24FybOPP7jiwLdHUcLgzMMadgVrKed4ZqEFc7gyoSo6jbjTVHcM0yl+k4r5NsrHub0AdQoU15bdfLcQDyM5gAlJBJ2ISbDwzvgDufLr5tBz2q8+4wnAhg+V+gWVqngx4/TCkKuMubme2GTVb3TrOQgjzEQXoYUnSoozDWm3Vqu21GxrVkcsqAauXogQbcSNnkN3qQFhu0WESLEylMEmw1ROXE3cTYqqNZto40smhJck2Rp6iRPoy0enoEMWiK6U6n0ttF0rHdlkOpxrquCi1z526Fi6XS03tAEh3OJpsbqNEhIFueLoMFobD5zKPsIDi7Ib95+WIZOaZDi9ZrHndxEQIa4sv1baFymb5R/BMYlLwllcmyXpZJ8F3RIlNJ+2xqtC6VoWxt7JoLmbXQYTlq54qy5ybTe68OEMb8Kq/uCT4xfPJ+8+t9IW/IAxLA7ozrR5l+YxLt19yJYBJOzCwEwOH41xPlp2DV/3VgNBHXDQVVEdUqjyHYyo91uEwafcOkw6HKIdDFDbd1m+Df5k6gHJWx8dwV8tU5ZevkElEsbUYrnsv+ojS3y3QSNwVDqn5ZuMqn2xvq+QfoCC3j3mfeS7RGvdLpobt0sJPmQHuPi/w76+3/01wUmLnzH93cVZiIabi9bZaYadLfbfYKGy7J2SYJvWfbTYdbYgdfrfRf/85BbulDD5Osrs2ZJg9X2pLj8STdXWiwGMSLM5HUBcuskIMYUp9n32UmJWukXSp5J0l20VTCzXq+lnXr1hF+0SLK15R0fSGu+dFfZ99HL+Ap8J9tmlLjzvbREWFTZlv/mKiyIJx099nH/eLLEm+Z1iNugRxlgtv5ZrM20GIkeYrqvcoLZ5ELYBNWdwWxdOzXPEogoWyNrWW9nsFFXpanWJ1FSZPBWNe/9XiBHcjRHduPtkYV4oCW9B4q0r71R7pLMs3kSTcLZ9qj7yKEj62AZNiYcgKHuY1dPii7vVF4YUBOsECDwes4U10zbfpzUfnRe2/0z3vxVKMS7TwmlE4Ebl5C32KkIYq31dxSXPuhnBTPmV3MJVeAxNT7ZDbQEdKcCHDYtaq/m1Sn7VK8eiqwVKlLDnOSkWesxFZn/psN/IiVP912jUKieNtlL4IGm390er6ocyXzs0/r0T8jr49wjQWZLSQaNFGIejUiW2QqZrt3vHrWvvVGum9FOm9DdJ/wS3e7keJePeTS7LQCu6zFMgiGjMJFvOnDcXKzZ/+8xRSfi69nswB7xuj9UxyUetVJZd6mE6ON4vGw1dy8tkn2WLK40bwaRbnIE/ZJ1CWaCNZZOL0E1MtkO9zAHTYknSLMarQ5grGUmQ+zeHu3PeIdwFnUmY6nt01l5yWIVtfah/pwWI5CJEhgKXKktCOOeEP58/vUkTqOqgvf8LEJi2HM2kdzJMxaSgXvtSXH8u4HE7ZDbBc3tzk4LF+aJRb2JiUg9g1Yu72/MyPr1sUx7NJedFxuLl9tleIx0x9n9Y15Dhbcxu1+outN11tapQ+NdQlWWA2B/RN2RPRuibPYSEL+gd41JUoM1mMUXTHEaX+YrVFX3SspZ/Ts3UsL8md23L3L5H7yHDV8+sGMlxddKmKctvikyohV+ulS0GXaHEkl8I/KrACkp0ml2RhTkqiu/MNag8OuSAxy4rJFo6EJWdSJR/mn4M/tU5W33dclTnxIiiyKvfdd8gQXea5Ecwu6G1t68XpxKbYI0quxnJJVne28mxzkVR3/Lt4bIrV4XJWxcK8oj4vZha0nkF+nN+iOPpxTfns+/J2JuRdXU7hrT9ZrF5Vwi0x9Zd51oKla/KHnZyunuVp/D/njmyua+edJ6PnhaYOx9HBU1V4nHUB1yhz6px6z9Q+L/o9K/m5xyXtsW6OCe/PfY58t5uaiD/n1U53jdSU+eN1STNpFaMEfyUL/u8VKEqkneOZJdEH2OQ5D/aWolnN7M6Oo5qFcWnHSB5u7fLiS7XHqV+cdX1vVuFE7foCbDMtxLiqfNrUjtStjzMZb6n3c50ytuvzXOsxyDewKJDSW9+38VybOTSXdXoQYqlzMKzLexc6jN1PdV8Xw0GnzyW+MJMcVeU9qhTGxIEgBDfpkB04yw5uHC4Le9U8hEt3S5XzNabJLeT1GVm6PXoj4ocqkWQzr+srHtir7AFwm3v6uyXaURyDolBhMqkWmjP2P0LaoUpeyNIXM9vPsrza+Du8dDAO81hTdpxJe5VtYcxDdB/nmvzi1WLbO8XnF/JIFReLuE21c0GDCF8StggwNwiO6+RQFB5ndpAaeYju42yzA5NA9nIek2DtrS51VLc0AtbSk7f/NV/Nkb5D8MTBNJ/sbpfJooLQ3+3RVCMpS7dHJxGfpbh1ykFm2cmsyF9YucqpSUXU73lWbaVyqkvZ5asGX7q1h5cs7ec5hBSemlL1iUk4CCxDnjnEehxJbSMiIIDaRnBcxaGi8FLtXcu/LHXg7teEZn6MXZPdnqcV5cZhZ1KZ7NY4k2CJJ4Yaoj7bHO1J3oxxeCcm7A6puRbd7PWFuIBCqs1xbn3cpICWJFuOC77GVwm4XJJ9e+WwYqqFqa9+30gOLCRa49bXa5V2RFUme447qfIcpPGL5MFwaQ6bGupylxF/Ps+m2LeZPVhUzRkxl8UOPCplEof6bMvX1U2Jw/qep3GCGiZjbz6HYw2nz0M1dDnsayChibsn6XR9kef0rFHbN3lO2xobkaDpG5/DsQZNX/gcjtIDSWGINbUoOQNA2iGD7CHqlnbVILuVGwIWJsJlxvar5RjJx8VltFt+lMD1SbatE2OH099tLmbcVukarGWPNfJpNqhPUb6+yGBaFj9ADtDY8q5kiiwWK8E9iB+yqjwqyxzeVCVQ7lj0OT1qLP5zkwxUVWexX99UToaydAunmsDRDjvddKvQWbfWLn1Yc0aCD23hkDg4QSwiW4T1OW3cMPK16LTUf7VDEpW0/qslkqTPbj38HBUPYK2npiqPXZtPHh/fiy2uv9ohnT5vYU7cNz5naXkvggoZXPH/ASIJlfl0Nw7+AHMQlx/ADeTvk6gy2RhTumJN9M6PWSIxrKhy+dQk4yB1LqeajqP0QdyMSDM444uTVZrBDf/8RA2N05xQ29htKuQu3Qn9/CbiDX18osU8qJBGl8N/kUl0lUdpEcX4p7DsaPL51yaykD6nf42XoKj42MlDeW1k1xbfuNfQU57DpwZZj9S5rE7cOx1M0yFNNpvLDHl8HxVAaUWUZrDZ9UC5Lz6TYG+xauKrSe1VbZo9Kt4O4aCuVVmvoFork3Ehm4OFMLGtdy4QSbvZ+1Ygvf0jLEr/p4wlkC5RQc1gxjHtd/YhniOo75abY6np2FIQ7hpz/Q5vie4ekLkkkC7MZQYzDnO1dfMo9HeL48ECrH/A8l7KZEKiHa4kMAb1+Sdg3DC86sGfk11YbpQNilskL/+oc9lzv+yEhE+z0H0llkJ7C+F50baAXOqKBI1AmsG+72hTtJXtBGTpNvbNGG4h4iZRY+KSHDAl9z74NAutD6RYpxUVO+q7LZqkgUyChXUKFIVwJ7j7aMNNPdkzcrdE4CZJhj2Wqp3ECOAc4hggSFN2RCcRXKHCsaNPml6fvAj44O7fq4hQmYXqv1ooHiksySPc0pNBMdUBWXruJ6baUFLVXte2qtvp2sZ271Ivi9LGKrK41iFtviKLxVI/eLTme6SmOUpzPkL7kD2lSRatJVGWuCSbBaYtqlRXJBksvIkRo6UFaDEEV2Ux2aLtSO79APDunn//i/pu4VhWpesE4NWBcymjvlvLxRN8308mGeuEZS2rX0gYH+9lFcO4LqvysmMuq/5L4BchRssXt7AsV1n3OKXMj5hO3mMNj/afCGPvERFdzD1GKONwalgP2roLnFRqvtmiHKMU/CaFBKxLsrMXyXbh9Pfp92A7N4Pw2UYdLtJn1rQoju/oTPkOdihJfpVH8QPqmOwojU+zQMWuazI9hUmwPO8C8oM5Ps3q9Ao+glz+TqeQ+BPMHn8jA43kMYumNDV0dUpOZ9vvDoYL+dy0tvvaGwhm02HKKk8vwR8kTp+n8kJBOakt2vIjsVGgq0OhGSisIhXOXnUJoiJLz7K8Hi3eRs4l2uCSYQdk786bOIREhws20pDVYqoN50S3t1/EULP0d5v+U8wvv86kyDLFAqdUg3fttubqPiN/niDRFWLZZNGcls4hiGXLvYsobxZ+8fYdnWJr3ZIhsik2qkJPY9lBrCx9ti1vwFOKMSy7ba8QC4McrIl5Xt5vNscca9VB3rV8s9qCGN42ARn7sfcLRaqDdolLaoc3kmdIwOkn78nXrdTJdiCv5f2HM5igyQelL2tTaVYi9Olr2pBBFUOcSba2Ke9OiH3dYHkqFBpkF+XCCm4kRUPaBjP+nzzgyn5w4hg8GIz7dij8zq5xQyN/yeOxAVZ2hOO+jEsLzz3S847L9yiHSIntZsJJtrmBaeS/agzju4+jFehIK8hDxS0W+IOFH2rJP05Yf7E4n4vS6jaK8Y2rHG1rpfcvVXksnaAk3k9WTiwgveOv67bfzFF+wDUP0nwyx/goOUb6aO/qEhWAEEF22CokOuDiH1psOoOFFl4U8C5Fu19IOOKcf2RNlm5/AobDGwqXdrg0Gwcr2YMf/df9MWIgWRc/yO0MXJLlvutrVX69JRBNWDhx+yVmWfo6NcriFG5F2kXTQFNIuR2SpVsc44PnEn0Snxaivts59v9RwVzmz99+d5jOJ1la5lkis8eq8uy/6h52zgWYbJPPsqMERoLYJJ8m3fo5Rl5dluAmNxdHkd71nchgIlwBN6oc59ugEMGybLvGzahslq/+qKIcFJd3N9xRCJ9ouWE5Wv+zKkriQyduXehEi+0H2SaogMVUm7XsIgcrkIBYcnuASZpirVEhkukgWxiZBJubDekDPoiT6i9C4h46ObWXNjwjPTQoLtEdlEXHkXFtfeI1PDbFyuG5Lfot5/ZhfJp9OwVIT7xjmEZCmCYuzUZ+piUaVUmUOjrBHO8MJiAVpHr/1RwJP+uWFoKWQn22kY1fwBMvE8mnxczjKwhysq74TeQOxmEma8ouf9sZOk46frhV5fbSps3h+GFjSZ3r5A5T3fOgDv90OZeTl5v7cEa58gjLjd2asCqS0zS6SXiFj/5ugQbiKgdCm6jPFiZy8Qmgj9bv/2R3WWdE5jQ7NmlOjfZjuUmOs/UL30T6u/V63Fq6v4DyKcsfpEuzkGdZAqD5FEAONNlcxYGy+DhSoa6ex+i/2iKJUob+Pt4COZcedA82gFgmkPjy1IVoKBd9SF9+HPYhlXK27frTlAuTJF6YdaywHWG44yh+OE/JqRUdDtSP9RSgDkxojDSSg0IoX/qAqn5Yr/SduxpJe2NcgQ1SWnzPVGSIDqxqBrNUXRy/f3sR8b4h/dc9PqE7QQN1l+UvYbiJR3PgpGGIAxctjota78ggTMSBeThrHlhod1jod5CCHMaBXAV4NJforYMQS99E/gd4qZ+GZZD6r1ZIAshhTyHl46M4uQRx5ht2uINx4FxN2aWzbFi7xzgG/fPifJ0I7qT1t8Ww4bc8CBt2MA5sqCn7c7HhKqm4G4v1lzl8mFWvv+lffZvLmBzfg3WVgKuoePA0JlNILsZkbfGlapMrNPtS3kW/+2hhlxQ8BGxdA6QnVS7HVGW2/Zqe5nmWC3pAn2AzG0jIkbwUDEBsih3iabqW4rXfLdtXxTEoCnkLqTTLNoo0pD4vRgJ0r0d6alMtjIs2pS47zsRXPH/u9Ox5qCeRZMuPevGZyzoKoqLKQe1J6WkWpaFc7KH68otdMci7W2hz94TVNlbCsknmmJf4ViWL1XzaY2tGM/4f4Kb2WAvCjR2aO0NqIA48uec8eVLlOUhj32DODYxLBGd10aUyX9ti8ZkfNsWK9fh3fSKnYOWfMpQoPDTLJNlaSs6yfBOVJeQD7IipNl76G4iUjauscQ3kvPS5RItT5eomgcU9r9tQn+f0DFtSjC5lr7NNBFO0KSGuWpxTOZO0GBGGWLDahHPVEOAcZJoBxjjCja5TvBAhps7lZEEIxEN1Hy2sENkWxjxO93GPnT4ucvLqSxM43/dslcZyOlrVA4zE64Fckc7ybKPibj7NhjNVmGyK1dz+J4hLYUrXHy1aBp4ll+XtLM+XIJJckY8sj0wam8PxS/1oOQ8oJDthdy/xKeGpHHssMYhlBiV8yjx9sCkgp3NITemRbGd9ldIQDGLybL6HdWxu3r1/Y3v3d5eZEyMHY1ByJuHFpHKEkVa0Eawa/ttd23Oa2ZwWN9sI3nlq/y2Kk5OiquhSDRmhNAp8rYkFqb/sscAibw8fxeTBSz+Oo5EcuE5ffBzOk7zIbP0Wc2NEEZmYSbC7YCjeL7QyHmU5H9eDfLEwxxQgT4UO9V8t2hIVhbgI9F9tpfeq4Mer/2zVvw/gNqqSEs3KNeJGGCWF0FlZlsXM22aPGuieBAvmdAIzgLDUpeM4jmvJUx9A8zZcSfIuLkvhXKxoMS04o3Fp09nRZ5qEX8BT8QmUJcjDmW3lmA5T0hRopJkprV005eryTbuQ23mB7I0C+PcKVGBNqOXHuBSQA7dqS4/Dohc5zHLIxy7pv9pZXkV7qy2CuJj1X23stbyV1q602Ir2m8UxMNgmL3xDuo/WOGKTmASLyXnCTckTS1VBUA52Ux0If4C7QnP6ijylzfnjdp/tsCRNoz5Ppa7MZQG6B/FDVoWKLCvAudiEhjGWquEvObIyeVAZb2Av6kudcmx1Lpt2X0XPp89AIAOTYEHV6Lm9zCycC7JJFhrYIc60kTgIECNYjhlCMEwaF1ioXTi+kmWYUvoc4u3u5Jy7xLHh8IN7F9ELrvUjLBDj+3qXylFdnE2NkcaZd3z1PJos3Ry9fn6bg+w+7vFenCdbWG4LwGaTyfWXOAGyd5GYBEu8C4C29mthhWCTbLbKZZSQ0tymh0mwcQNDw4cGDog7HyZlDqtvqJtW5ynEBynSCc6n7fE8Jy7s8F8khDvuiO+6wuO5rCgGGCMdwga72O52/VuJRu4ZyMOM8GkOqApAeyxJ+BEmYTFcfwnw0WD7bIHv2kZjOa1seoCxTM5NWMB3/Eag/+6A9l6B9n6vdfSTPCsKtBtJgnAUj+ayFx6E2F+umm0l9XQCdnP+ndDpF1X1CTyCRFjUqO82BscsL9v7DZyRkU6xOLmqkkQKyCRYqIjbxteCI1r/eS6n5gvUEyGIfPfRZvN8C/Ic5AIWk7Bf1/HOcvBHRW7Hcgec3Wcb53ISJv5jVPCbQzphMSIKfSihd7z4GsQluo+q5FLPa36icHr9SYnvDpA6c3EJCK8rvVQ+2Tl9lVzn9BxmDOEywPJy4wxtfcVDHGD6u43JKE6qNThPVxCVjriNuJhqddDR+CGjhFJ+3CFmmMP3+QqWfDCE5tOUXiOfQRk1F3c45YxNsUNUPtsqJNrhSmhGfZ7ONVWtBaZIPB0VP+D6DpS8IsimWRwLkhL/laVc1+nvtmhoZ/AkoaaQaIt7nGEBzU86Pm1Mf76xF4KjoshiiGPvpOL7lrAgnkfXR9ttAjEPUaqA7kVLbTnhEcsmt2p5R6vPWlxXlRVcr7Iql77BZ7SOdNCyBQUTs2uLZzOvohxPHMdmtsSybOavb6Xj7cMSnWXLiiH6Uip2UBiojMjcoi+aF7pG+nFCAzM1IxCHL/QnWrDus/X1JSjKHGL14wT3ckhCmJQWdolMmTqjibAYrsuTTbgKAjCLQZM9xUdNvamZpjVqXVN3lXSBzsTcYkSzOk9reRumrYjqOfwdYICBlzTOb6AbnNkG+hgmCfrcGUYNRpsvohpym7FmMT1pyoGFHHYO2o8xrRt2kqVriMfz1XnxpUqS317fRkkBLHvvzTwXIN/AAgfJrMPXX9NPBWh5aKikEEyJy09nN2AvfXWeoyeAB+CzgQZ7rikU1mzyxphVVCVU0saSNeTwS1xp9oUFLsFTlK8vMpiWReNqev2tAOsfsLxvzKw6X43BwqJ3hlDEgC8GK/IcARYrAJ8MN3iJi9QQGcIJnFZVt9FwhDIhVBwO1JOPeLSQEofHXiIDDfffX81pNu7N/6+iO71uI8suRockiX0eEy1GBPZVXZxMEsaNC2IvQUjBV6jW1ooP+SOYgpzP0hlzmy/d30X7AQ9odAc+Z2uQFH05/P7NJiI9KrZRTHbwa3AG8wI7XUc3UQHqLK9fobY/QiT12sOsxuDyR3KS1HEX2wyfoxTegqK8yh5A+tvr97+8e//61VECowKfyCe3r189b5K0+FtM5m2UpllJuv7b6/uy3P7t7duC1Fi82cA4z4rstnwTZ5u30Tp7i7D+9Pbdu7dgvXnLF29gjVB++fcWpSjWzKV76kiS5ZTjKl0n4Bx1vLtPdQaTkpelv/4HELivHfVLcPtKJVt+fcsX/FXCb7hZv72GmNpkhpKnDIkvZ4TDSKQ4FyAdeP0KiyB8cbITQ2+18H3/uIp4Ov/tPF2D599e/29S8G+vzv/zmi77b6/I2vi3V7+8+r+tG8FcVaPa4ApDnCf8sLh7XzXQDRwCoo9P7NhrT/jpon8F3I6ZuoJenFTT07kRXHGvpvy9ihoSunNha+VvMdYghpsowUIX/SqI9Hz3VzSK2Pn9t9d/pisoc9GBSIWPNvIxGnDit2jK6iIa/rctnz5GeXwfoY3O5+i5vr2E2vfLL7YtJD6azJk4i//fNtHzf7cFrSU4FWvAo9Mf0dp3dV9tblISfMcd6Dsaz5vEawCoxxLcQVhXInfeZT0xG/5FX0qIucQSjHXCtAKzFcn7JYjbYAHu43gR5agJ5NZPd3nED7HhdCR+IdIcqyhJXqYUO0ag48gdlDs05tF6A9MuznQAwJZxmriGfkONfWW+ph/RPv3Cc3FhHa7CLjKC31UAMlKeV2Ebe5Qk2VNnGQWPEDx5LWBH2y3awYE1fjArvVtVG5/x/pKVQfFaMHLBmOusZ/tCQTbhrK6yo9hr/Rcc69yhVg9V8CkSpdVtFJdVDnK0IpRfkMbT6wehavkdsUxozPPid3hbnkS5l1bUYvivp00oo6/lPcgvGGcnt5a1oZG6pVkvHt/94iByKrxxKmGMl+qj9Zqr0qv558WH7ClNsshPaW0w/IbmW5rUk7CF8+oZIno9S77eCnhOm8EG5PR5C3Oyg/0QvagQjXZ/DSAJjkAA/bn7Y1SsIqQ5gBCjyiLZb+f58qr9vAmxUMdwNPCjuxwAWtdy6RcDVL/gE0DjwPHYmtglfvKkAWECj/jIOxrvogs7EgCRCTviY+vDxzPdsxg+suwMDWp71uOllK3rM6UoadFO7vExhYshyLDpVEw893ZzgfDcxwTpGzh8RfqIZggCqz0XPfciZRY/BLLKYQsJwTt6jCAqCxMK08PqIm2j0+ID02D9/ZQ91X1tntbxGwakgsPbF7JtPcvytn3HAO1qfGCPo/iB3KDAZ12e5wh4h4XxzmsaspHOPQaFrD5oZOCm2oQYmBoveg6FRzoO1g0UBOE1ScTmOPNx9XJclWW/1XacMjj3D1jcJ7Ao/QGbuZwAxJdIrjIWUBe8E6QuEygY+5l1aYDgkv9rsh63gmaXcELedBmpjtUW4USJc0eMjiKoOrr4ZFdwAwZs0rbYp+k6IHJrKTpNS5AX3rzYSC8GFYzMQI2Em7ROpO5fQVDPXS+ZjxZPUJRUDN/NDUzJhmVUZkXtbw/3iuaKkE8v6uiqo1Gb3WUEh/8B1yOifxyXNt2qFFrmdMBhBU6o88Mwh5ltwEaPVXwh55jSzsFHkL/ggbM3jrClfUwjx1EBmpXLewvdYX0GUVHl4FsKBesIo4X+xVoH7ao42ozpW9FVg3+wVTnY6mrPFHKgJDMY+RrJG8cXkGNXpBAGDAYQkyEk3uo+q20iUW5hrTB2BOi8bvbDE2DwfPy9w/l4SyN/i/G3AoRxP1I7MwVfk9uq3CWIocWKjoLstyw3i3wApOb0pzjJqm2WYsdhr+VVQFEZG1yd28jBKlkF/Ri1x8FLpoXF2VjwyENW7K/gcXHMCe4/gz3UQ7NeO5BhnGgOri+MK1qYA4YLSJwZHByV24I+KjP2hlrBfwGfHjCOQMVVVnur88hOBscG4ytr8jZRHsw8zJBWfBmldwMGZjd34UD+ZoENKAv0ElrKBvvndTiuJcmerPZNb45hGuXdKRSa1jfkg9tCgoiNcQakhP1kBNkZTEA6pJ/8ycXb7At4Mp8Nts7pe8Yy814WGk8FGFHuWVsxmu2a+JbRzrJN2zMHm2dX0nfkeNJeVgmgo70GUKNXWzC6haGJvtPE7/L19W88lUKA9THKrjI6DIgS1cxy0YJeKJ1lnYaKAq5fDav9lUKDtxRW6OWe6I1O6UXh0+cyj/ANcYe2mUs26uRiT0TaoKnkL/ZazkmWZPlH8NyEkg8L3ixe9bNLgQ9oRl4YW3+U/TK2zaxLKYwyBvX3Jb0acF6coe1f1d9KmH5LasuB9J2aAxeG4EKaovZtYEv/NNy4h2y4EycPNN0PBwVhDwoOxv3B1u2ecf9g+1bi7Jztu4uDWj9gtifrDv3KnqUW3JX00zu2TODIcNP6vGjvze+DL6U1m+4Zf/5ewa6iKoV/VAASyFuIxZC9A1c+eJTiclnodEOFVAqG2r9GFUCDacHOsnwTeYbFabFWURLmQnDw0DCBLoke3d7CBHpru+Q6opcqFGABPy9qq/9RzARqc3IRCHt88CkqSuVy4IQYVHrj5n3K7mAayCsR47UXVA0gbU5H6KC9dqu6AOCzrZDFELZrjohg3h7jJVP6dMrOrpgkJm7QORkMTLo8uk3sbLON0pdAWLLTPxMNuC3oNUcQPI7f+QjT2GH7zxX3ackJdRfbcxUn8+ldSLD3QcD+C24vsgKH4JB4b7upU/dZCuShpdwC7rUheOZe/8w3HLJ3yHZWeg7aYd852GGJaaxollYvPZQAhbjtdPWUfQKYWOdFNniV4b0t313d5wCY4//JFh9NEZDDmMP2ioX3PfJShWcMZGs8U9lH9X6WCbuEg5NQRtPzuxRRrw6sZGFNNucQWpvYEwYZT72bcUG5ucnBI4xkDOp7E2gXJNlxkt1hRW5PWPRTlN5VOHiYNY/2Jb2YVHqkKUg6MwsEebA+BFJ9oFgbA4PEC26wTjQX282mdB+SNxRk//aRrwvuIi/gHo75wx8IT7G3a8XLnsjZUdbilkYnVUIuuuiPMe3Pqshh1wqMsEs7S6K78w1qOr5aNEgbe6/Yy3K/Lg6Oq9PUjsmrEoeJvQSF5P25nZ13s+s3LT0HmdwlsEgLLvVad1N9cFjebHORVHdQFR/S8PQzq2I123ucmXwBT8UePQI2O4cG1MDJQzBhoPDzL2GQgkjgZSq3P9M+ZRn662GD0cIa39/IkuQoLZ72xi8Md8jBWZqUCm7PcxOJ7VMb37PS7zWAse/+IKLtCdfMvsyH457a96qReWGOsoMs0cHC2ZBl9fcKFCXSYvEcWYLX+hI0EGtXYHwLfU8m8PAG7i9/sddyAz2BEuo9koZZMYaCV43UUm9n09ZTlPDPKG6iuqHz0XZAvoEFDjZxCWIsHveX+X0Wj1EmU3dJ3Q/YeKxPnzFVo+SoKu8xXWOygu/VuM9+Ryec32Y7WufU5Y2AsI3kDIz+FfPWVfYAwuz9CNxRHIOiCAdKnDbQAHu55BtPurMsrzZ7dAB/lW1hbD+9mmJ+YSjmntxGr/mZ6QsXJlf5XILT7tolPDI/CHfsyQQhHbJn0abYjk8QPI64Zs9gY7Wnqd7g5xDX6Estib0sSN99n5TG10RwKzzjlTcoiiF3RCNBtzz36rspgfZJ+PyeZ9XWUQI1Zcf143R6kyLwscKXZtXxe3E+gEDBc0+mVB3Eyihmyd0UT2Ra7omM2gnx8NOyW93j/eA00peAdwQJXtDwGvqgrLuh9LP34H3v0Xs2hrsF732N3u+eMCmJbwpVfEscuxUCq4liHBCqviUXOMRFF0SqynOQxi/B7iG3gJdRf1A6EHv7r86zkj2j8qP296gMLczQTrtEcyE5T+MENdWQHH92k8RtZafP01R2hSvrosJP1EOm0ml62siGaXrYVDZ2zxjJgmQxxOI6Ss4AGLub6prH7jPCxjIpzO0mMlqjs0P3muaotZA5NVoVSIWtkBqwHvk1hkvwFOXriwxJ/uIHyAFiLc94e/cgfsiq/vnx0HsgoYLiPzeBYtI0q6PCyczW/SlMyLFOHd4G6SNxysL6PX41IwdIjpyg8WcXcKdhRyi0v4vfQOAmBdPYhP55hlAuHsBaRTrvlp48PoaJlIPBTp+3MCdbis9Z2j9HHxD3HyAK03eaLz9AJNzKD+Cmf4fbzWWwg2lC5X3MknWgsRLBAzICBX4cpQ/B9hgcbrApRuOen4SGbOMTBYY9v4kCPedaIZ0sh/+q3+nNo7SIYvbp31Hgg3GFqoJLUFSBwoKeRFt8tTY8cUTggK1G+6ZOdQnf9IsKly1AaHPgRQRDXYzi4g0FoWkDibcUOAZgVZu8RrHVBIi1OsUd/Xa38q1AOudHiNS9vQnGNvuzeaSofe1NMT+3lwlY53d4S5TQPWSdtmv2g9eX9Bq/bwVY/4DlvSMLccW9m8JcTg9uBpiSWfeEQdv1mxpltHg5XGlU4Pic8LSk9rfuj2x+Oi/appLbLpHnct2CoS3DNqCGfIk6u4WIT4KpJh1iOM/8FUixIhmqhTVcuOZ9BkVBXSj0fBSgH5GMnHEvXsnrJvaeSL+uP0EdD2ZU1y7mfUbw71XUDIS7rP6WwpK8hTX2YVRX0ejnbVP0ZpKetPuZej0du0tsbWP3bbSjnvBHPB+ypzTJorV3HJXzooUKorh8QiyYFqDF9HoQG0vlHwDe3TupbSY1HFfpOgGOb24rRe8Jvo4WhkftluUvODbBPi3LsyygX6gID37X0FrXpKx7Cm3hyh19iL5fVpjZ3Thronp5AxKE4yiJcEh0H/WqGN0bI+QmKayPN22fcHgGii8/wiNQ+GRhsz/ves4oya/yKH6A6V3AQyniKzWuUkJOlkCoo68PIIGPIDd5120p7/i0E2CPrAxdl5wc5uuSAc6PRLvu6Lt7C92jrPL0EvyBA23ty7D73z8JNHCz6z8h7EKXICqy9CzLa14Jo6Q3HAfI/tdgv29Fbj4Yq++LCre3X7JQLszMhAtxXWXnLuOt7jPiw3ES5Xu12PhLnYsobxZgLwtObV9xO06ky/o9/doPsv8x4uxydGbz/njO6mh2ghyxPzZij7YvDbEK7ZyYa0Z+tUU0vG3C0nXjuCcyb+ZpIaft1y3rZmms82vR/OYvdk8/gwkit2eM0Tq+bkM+3xC7Y783qSHonkwAeRdD8d5igk3Pwih7wiI7MwqNULmK7vaY8pZPw9oS73uUQ6TrdDyMdqI3MI32SeI9VDY0NXI3LKH+FVOXRzuitLqNYny7I0fbKsX1Kt9anNVmo7cCSbvGQv8B1+OBfxzXfyAqAKG8+3GaVTX4B1uVSzj2ooB3KZqBkDDl+cA7Ne9+sefG9tABRyZzuDLBlPbZ+58X/rHn927DjeRw/BBiI0z2El+r8ustgSSdHOMtcMWKtifL2Mz81aB47BhEBO8o0OgT9XiFt4v1HxXM/dzaKDUqLfMs8TckTqNC79tkOUpgFDyod8B9kZffqK9ArG9W7cdAK7roLJ1EIE8z2lx8aGRSz5IsX/1RRTkoLu9uQrez1njX/0TEJ35KYxnrazebCSo6LxDSCiQg9vR+DhXWlMxk/zXmE0wf8ImKTMFYjuNH6yu+J5Kr7U7Q+0Tfepf6b7nXW+YSmCAXEY5hGvWxTRDiDfngKL/SEpEKQwVpI35VPg0lXPELP2kR6mbIefEFPI2wZ7pCXFbbhPZjVs19vhcukDB+w87de6Au7WMTCbHzD3lIb342g8dgT9g5wIGAZJEQRbo35qpITlOc30svWoG4yoFl+4zMu/TzEMHMl5+yu6yzjk6/yZd0s9wkx9n6JVSwqXp9be2sX0D5lOUPfmNjN40/R/Ubp/sxm+s++rFKjTGKXLBevMy1jHuwAWRrfbM3D/uSPoWWU7bjaoLJBNJRvfRhjToesxxH8cN5So4K6IB0e8I2IZxu59VxZ3dtneJuEX0afwU2aHXeG9v4KGsHfo8PNU0IuesNPPL5R/v482GM93eMWzexwxDv7RATusJ4344xQ2wYUBfqZ+tCDykCDs9/Ml3V7Yx1PAX1KE4uQZztTWDHhW9LNQZR6/O783Vi4eRlzBHf8gNHTGmoSKq74KAhfABdHlIxN4jF92BdJeAqKh72hM/CedSs0PRL115xc4yOEQ1Z398SXmbbr+lpnmdewaDw07+rMsrLACE9MNZpug6EtKriGBRFqHbRhHK075uv/+1DR3syC50f6QzzVmiAVwtsJbe5GQhERZWDJpDPfgz34JL8zmVJJi9ZoJ49YS0oNPol5kDDc2T7R0JH3ps2PPQBibbaH+TARgc2crBUNu/l/izs8xeXnaL6jWIW24V1TB1ZnDnnU4YBLIhiHjbjLMs3aMioq/yB4D/BDUT8cJUR/rC41SM51qpuEljcL8OzdinBOyQ9REoZTJEijgbT16XFWPgg/qk2+3gaS3UpqCPu7IekZMT8LDhX2RbGs4X6sjjeIeHI21Cye8KY/m4CZ3m2cWdDtrTfTb3MvRl0WT/P3OrmnyAW7gqyZkMHnQNfQwxjNcI3ECOvxe+8aPbSxy/1G5eBwLrHZ3zwphAFxA6APn3K9sVlkOoRrsXBRsMD7LjvTx0oM8yMm5gliYF5P9hy3H364N7wvQOotVHewodos43g3b7oxAEPZeRrrhsY9mvfnWlP3pFrHkrfE76QPo3HTsu/2BtWmg378JRnsU0vfQRv70WWq4LqmN7QzFPLzpo1LCqKQWnsAlwLzlXhdZ0UX0wFt1GVlGj2EQ5EpUY5sCG7wJ/N189lQTyO41pK1ad4YrRwXQ2Gz8kPbrlcrl0ZrQRThX2iBb3vLXxHU6rx5PgCnopPADPjHpry5J0LatQbZwX058EpFJq/V6AC65oC+8EwFznMcs8LzthKNnCiYi+YMajB8Zi13LzKgjf1KhujoZdgm7zYtdYCdowWn5yERkSrc2jIxa/HQQ/dVjgySA6Bl08gBhlsjlE0ELWaYGA8Y0tbGNDMbRj3IH7Iqr0LBDc42V1chRcZepA8B4fzX9TXnUJgnhdX0fPpM6B66gKDQNp7dn4q8s6FVxSm1T7F3hM653AqIEIEf0DAe7of4tjNNHsucTgY/P7IRfSCCbJfTw3zvbOfPSJCgLcfF+/vwfd6T9jh5CVOABtB32kEMMwFQHvbta/3TxklBM1Pg8a3TwwelZ3sjlaA6wbnKcT2a8cXi9nSi4+EQbxS4b9IfFBS957MtxB3HBX3/QyYoC/pxQC1y/BoV7w7+JGQna97WywWCWGXek+0J6x70cbxeefDvB3K+yVvoPKsKJCqmhxGMMwIWsj9ffGhQz35BB5B4ifrV/dZXrZOxkH8UM5QdlNAIzvStjnKDX2QO6bXn1EgeUShUHGoEaeCPAd5KLxZLooYBbPO8Yvd+MKYz26ijgD6MSqGQgIFumiyAvUtpf2QPKP4bexEjBzK5PuzjOUCw2KRGzx7Qv/a5Vg2Cm6LZRon1RqcpyuIYKNu7+R2TNI636GE0tuqqvLkc+rnFSzFO6VOSMGOeT+DMmpcxwVVxRnQ70klOWY42gW8JnsJkJaVHxU/4PoOeB3L1Qj/laVh+ljD4ee+Gbq5t+w4w2LRcy5ZO/mYv0cDC3Ig77POtRjXosCV5m/rkuQfFtBMZXZ6PFXUz2BJt99yK9EXHcMBou2hh53BdjCbqnZ1LOnm27WgLznGSBJHCLSHQBL8Pluf4K66TU4WyHiK1hU6jaqkRjvKCgCel+36rlhO1q7kKM5KjVWhNXc4jG0LYTqqTVVOo8rU5WY/8R1Juvl2LehLjjGSFyDfwAK7NNTxM+lYny7DyuMZT1qqXqcxllZsKRVFiCB3dbs+ubFeU3zMeew76raT2Xu0FzCjlz+6zSrf/O8qcjLqqXUkXXZUmdssnlalUTagbb9TG+rCI47oJXiE4OkjSLa3VZI6LsABLEwmvhZBhqPusPOAtMW9mvIjKhqKj3Ax7DjJ7trICvs6lk3/jOK1GJnDEM0ussLBba8vOcYkxffgfpaxDGafC80ceBDOS8AH4DKgTF9yFAmeJcn3DJ+wzRqvfgLJjTp6lBZPLofWdNnRl9H9HYF5XykLJxvqcQomGnAQ2j4Mp9Mrn7Ue8A9QEB/dAFBfMkskFa8fFUUWQzKy3C6gfib1uvk/x/Wn6foV3omg7E1605IVSG7fdN8+V0kJtwmMUc2/vX73mp8pX9PaufcV9hLGtyROoiKO1iIVUOvXyhYwDWYbwiWx7fkfQjXE16SOdXGSpUWZR4iy4vSGaQy3UcJRgMtnKAhw5zpEPuUD2OLDo7SUd9WkRuq5W7HiDp+j9hAdfn1L8Y0dO0mt9cJgykbxZ2EnGYWWw079IjELO3WHSgHl0i9v3rwTxq5H6o4Laaj+467LFPlpqGr4Z5Ym3VHY0RaRHJ/PN82HoLheZVUeqzmiLcuMY/9xEuEiHEHL2jIyb3VdnoC59Efuijr1x7Kzs9lVlGM/jiDiYo/ZzGrI52AzjWvGbGzWLK87JctkKpuQtjeSzEY9W6Ig6zhsQIzNr4jPz18TquIu7KU9apuUu9ofTQgTbJnlz5iWKbuoBkubw6TvjQyje2Ujx+Y2IlyjkYePIH+5wtdrlGNLZ2IGlUmw2gNaSMNQfKHs6ji8YbWuUW2bmyc6LU7h1bssxZxrtIyf9kgt53tko5Uvhq92xmI5I1NNb7O0s1rNbLT8HKXVbUQsZ/mEhku6WgaOTdh1AybTm10wYjbsyHCFuhO+Azqu0FE2SZo+CqtZj38YCWRdLV1gUby3MyvcApht+pXOXrzNvNpxV1ouAUKD+IptfcfE4KSGRWBGmk+ahP3kF5TUzepyjMKCHAkm4ESDC1qKik1uZC2EJ4eOdSSjrhjnn4ILbbhgNvZTX+2biu9KtE9B7XpEXQXX0qYvi8fo9rK8xabsPE8x3bHgpdlYqb3XF5CJBvaPXHAqgtR923UGkF5FXfjQa5jWfw4vjhnsp+iELME0bj5vuPZuYvck2JBq3ZZgxUP3cZJFhr8NLmvKyHKm7fAEXKW9+66oUn8/dibOGlCQLQTE3vKVzRjPwFbqmAhTc9UxTBKk0cvjQTjx1JATr43c211uMKqLJf783NDErLyWd2Khi1fTaGlTurS9WbzaHtksXrPxVR2qfFXm5NZkQRSiLn67krm6DPSI9h8nYS5Zy9n2SDOMwmZygo3DZtJumdRLx/Ofhdfaq+G7wV9ta5k29B93no+6ruwE77Q3x3eDd9rWMm3oP+4873Rd2QnewVfed4NvcEvZw1ryYef5hXRjZ3ilDo9wLTbadqRG5JK6jUIL2s/jnOebjmIAbmk6YlIbzj4fv/Bx7ejYY4MWRr40O55C4jTcpYtaqGsfm3EcDuRJMgU3GkdxVPGnUdjGBTDr0Km+ihF0A/8TMqk1g8zNoAMRI6e2d1pJ0EWZuYYmyITMN6nBy5XhFsdsOyQBl8h0U0k8b8abV9KdPuPXYqLkqCrvMWJMErkledEST9cDpl36jDsvAbXds2HI2XjxLMurDTHKhmY89fliVyeDRH3deb7o+7I7TECe05qaC+o3vAQ2aD7vBx9I3ilbNCNck3/rN5e1g8c8y9wPXvN5koWIVCg2YSTWURFmROYxqqhv1xJEiKTd9iM2Jr/MIHTMx3JaiUOyL0D5UDXbZ+jGZKHJtRfLcZ1UfyHNmo2JSBjlEZzkdLoL86QpAWm+7LpvXN0Nk4pYgs88+juxaZ6WaaZUc825ZnYNtw3P9A0/TP8RotbkL9fyuFILDcNFt1zaHjbD3gTiYrplUu/skbikvCaZK/Yy4idgMXOZMhd/kRbOu/ARj7glc1TXSrEFI7rCTcY7fUd2iWGWH8NkHraZMF6JHePMHaXkd3hbnkT5+vqiwk/DFGD9A5b3ij64D+PAtaS2FQxY/3E8SWI8Tp5M0fXFiCekQzE7izC6jrxDrkM6kqyRtVzangmUHisOCMRt1npPW3BZvPaNngoL1YaWxGqT6UjOfMaM6Lxq05esBMvXs3ErxRbUX3ebh/qOLF/PvgRPiNsvMgRQtMJpJ+yTkoYzzZGm77ztUtarnbBkyvgs6CI4oI4vh10mE0OuvMIMy7yB/fA7BoteydpGCsH6RnxXYjIG6vqx/GWsbSqxGMnb7TpqI3OOYHBgE0aLM2o8sIGYyNgs0RWY95C2DU05XSCbwzH9a47oM2orZZWn+DEdMIKv8Uj6MNVkTrVhUvZAB6b7sxPab3eBZ3qpcgiPpRQvc+q2GWnISZTXKstxla4ToD2eaI5VqJySEy861YpN+BZxKgmfOOaJlooW4zCQ0DeTOvsmLoeFdmKVWgKbTblWOXHX7MuVwFvLP4NfAmNNeCLvxFdzH8w39a+2IIa3ze3Do7LM4U1Vgh3gMW37Ze1S5dx17tN3bxdYUd70r1v857VJv2jRM8wPZowwlmjU9NWglW3GcSxR7izkK0F1nTXaTEgBFipczXs7NuP8lLLZm9lmF9i6HszN863qcPX/t3dlPW7jSPivLOYPBPPeu0AmncwGSDLZdGfmUVDbjFuIbDUkOcf++tUtHnVJJiW5d16CtOsji1UsnipWxQc+htlWdhFjm6G26NRnslvQRJqwQ1gx/BhgXEzEHbpPgc78fzAxaYevaWUVfHVD+zPOk/hUDnPrq+z4kJwaYISnjNffw+gg4x2MQZh0Lci3DTItEh7miReqoKDWRgkqaYXe7C3b39WczTdvqMuvu5fa6NpHdUSM67VJiSE+W+t7DibHXwk5QKDLl74G2qw1Lnrt48EsN3PXYwvxZ5yeByOlJQxjF8tabiOupJkdMKQNz7KnMKbcSjvBnu0K1jbr6C4+PqXqNvt+SrMYf443AIyzzvDjnHOOZLPg68QCihfUUmQXfYbyV7OF+0TllXx1am00bO6lsY4bmwgyYw2tN9qi/fosAhuP8kiY6a3bgFlt//iwjhEteESYZj8bORSMrl+d0972Dcn1q5vkOXh9huWKJGFqdOeGzMyrgbFboL+thGa6OfsY9u1vkrSsY0sGdyVeapqyJKMb6YCfj4syK6t8dlvVZfm3ePf17emuzHZf784PxS5PWn+rq/BcRhpv5lvFMFfvx4xJNmXjvzm72/7ObXtGt+D6fInNrb1Kv/zyJUmrX1S0UIzngaFZ0fjrtT/0GkWRMFv7AWkTkjzoIoebgsPbDf3+zFYnV6arWJcq+b5VBv2+MtT4oKI3eXZczkpM5tYaZ5Ku3j4sgSQc9c7YioHcZ3+bx0bMY+yK9bYY1b7yW6Xyd9kh0v5fdxS+2bBwxl7Bpi2yw9W4Yq0JtXehdBbG0HShRLsYs4mbMLWrOKyvZ1VLzltTzWn1Kes/Z3VW+9fHOEmj5t+XOzqThQEyMnUahEVsSmu80RTj9yA2hasqjF3pEknY6e1bL8TKo9p9zc62N5TzMz5vOUhjAnOpyyROBsWimxbS34nRZxiDRCQUzXl20RVjSu3OeSXx4WP8s4lr+PaU1PV6Cm+Ib+VtxlZwKJt43TELHXlEPLWe2Ix99FFSaYl89XOwqGSgUGTTwoZfnWcgfo1ySjRWu+xq9vku28Vp8l+179sYvYtPh7NzYtZ6fwDo3T3+uIgFOs02G+NSg1gdrKow1ubKJGHat3BF+/J9uAzn32mfK5/HefJqzpHd171P6luivv9bpU9fzumpDuhXd8lYeXOjPthU9XtZb7vuswgrz5pdVwdofQNtssuV2w7gizMAEni4X2RzvUSLXMFKemQS7zXt0CD8ipqVAcM7fTWT8usz0AmzdWsyCq/ns5Jmh1fZsTkoCmc1rcjSE5nO2vBN0X9/RtMVqupJ7BY2o/r/dZZ0fD7qEU4fQqniQ7k8TTclT9PUIOgGDahv22qG9EF9LybOR1qRpecjnbVepfH7M5qPUFVPYrewGdX/r/2R8fmoRzh9uKBX+gxT8jQfDYJu0ID6tq239c7S9M+sTE6HT2qX5Xvx0c8qt/iRz+Zv7Msd4jOao2jNT+K5hn3VP7w8Fd9VTpznBozTrf3Py7ykucTIfB36RqG3alxjE7dxgzDr+mrVOyv5ncV1T12eLjAWN6juL/YGav3HyVPt6fqes0w3omUfsbyuypQ/61FVlVB5f9ef7dWbJC/K27iMH+LC/dhXl7pTpfB56i//aEtMesp5t3tUx/ifv+wfsspC4gfZa1FnCmNaKmmasC1S5jhLkhFb/W1SdK5xTv0jCWIwUhkOr+JSHZqv9A6HkQRxGKmcipJdec4VpKKeAqqoJ8p6gOBiAYj+iDpQ9D5usnKI+6dOYpbkqj1Eol1loKheM4BcE4ygsC5vgwwyNRAybRNW4yAoffcoscK7gu/j0/lL3PQVMROZKKodOlLcFqYRPHcTwQ1Ue6vnjlcbAQ5bGyRkS/CjGbEchkeSDoOBAtU/EDkB6gEFz289BWx+T2Sq71PRvVflY7YHuNgAiJmN4XiW1bCpTPdbtWOBxrxFBzmaEIbh+OnA4TWSIDYjleEwOhM5HEYSxGGkchxqbxp1V+bN2bJoswsA3EAYyBlEMq0YLz0dziMJ4lZT+dlRu6BwJ0WNCM6FGl3AB+GA1y2eatoQb+h005KpKadFcDKo/JgURbV57q9PXHkcCCibg2I4v/5RbWtPcfryXD7WG/w2FwfaChoOtYguwbSueYOLDHaNBvFtyEUkGvAN9j57SnYYn45IMOoQEk4YE7L+DiGp//c8Oz9hTDoiwalDMJw6j3GHSfc7VH9HEm5jP9fvJge3XXQfa8KojayJZFrxe/Kl2jbme6YVMAxqBYwUtoLgTHOTdSOyCGg0tDtFB9MG+SEroZlUo6FMWjLD5JP6Xgn8MUtOZYF3GIiCGINAwf4LOXaNJGzPJTpgGYnvcS54p5kIVqVGkm9AmQYdVqMBYcWzc2oCItoQWEwbJTvLYQm8sFMdhqfOd0g2NelJj86L5uqLhIO6I0vMap24XVNaJO3TJhkR1oENkeithi7jQ+b9wPiThYh2keXmtVfeSObeyopoP/U+RdAgWUsmWwoS412qlg4+ode6EtyOaAg/7u6CBhK48xmoDAct/q/DQqNBPDQye1OQgXei3e/wzUAmuHVtUIORwfUPZJSN1Ervq/Kq6cgH8Gho0UGdmRDuwgOLxebef2BI8DoEA0+4abxXx6c0BkcqDONuHkek8FMB0QIXQn06EHPuFwacsYOg1hcp29/VSVXDjJocXQh4NHBQ3J3oLkXvAzQaeC86khkmn3OciUaDmGhkbqKoSu/PqbqPi6/QRGGQwYnCQHB6G+PkuXobaaDeRjI3GlVcVAPnL5UcHqEtukUHx58JkTG8TapTRAFPQy6EYKuh2Cu5PFenHXh7PpDgq7ieKrk9YeZYAEPcpUyaWe0YWMDEYgKoey8Lyo5xLYYNMMo1KjzONYCcVRsSimLXIhiWLYhdMI5PcXKAunQkwQtET+WuUo3wMe7VqUEGr0oNBDsOm56lVmAbAY9CCyT4ZvBOlaXKmXGCAbHvCRCWaYsRJMdpgEGFuBoAznrcSC2uGbkY0J5c2FTm2LkEA4qaITuLoHEVgMshBAlfEyHgic0RtEPWANknPPORv8MawKAf70wYK3Y9WPe4w48NgEU2MZwV5llRVJWnOFcXAlqeg2JVDS1K6GIkWYSq/5XIabGngPu/nsidEeMfhFeKQQXPhzqAY4V8tiK+WMk+VfWfTQhBXAj1/SV6+fSUJmp/n3X4RPFfkfvCuNE5CFkbOjjfAtMfAvfgQHC8i8UnVVRTcOPtKvAg6T4r444qDoL6Ot2BBB1hf1lmPpDTcMmn66iGiu8OB1ci2Xd7+fd7I1Xc1KtMyQ208CI6GnFTWwE8yccaA0CJNgFogfPO8MDNvb/SqZgLzwAQ7FRxVgYV25NKWbmvX1z9OhDMNcVETelhrlsFfekw1Py6MTfXaPBm1cCgv+uAtB3PNc9903W24ol630Ie/2Bxi2S/IzBFnCF+v/Dw4sMPA6CnE277NyP+4CdL9LuD8d/j9r6kKYhum6aLSW1borvOw86VXFIMF8r2sW+EwvznwZKgWlxiWPXcx/lBgSNCUsxTn29MPf2Gc5Lt2IVCWg409zi0kIqZYjV2If9T6kpqAZ5dFKRKwAIh7QR4X2JUQj0Ymb3YRubLEHSpNXGEMMBLlVYK4gXKXLuaL/awjo5TEyq6iw0xkSKPbnTxPU6jtmj8XsuBBttsLaQC4zkQseUCcf63XdBDo6Yw+YBods9bj6F0FqgJ4GU8iwVZBVoRSA+iJn6QgPBgA2VhlZi3a1F/vaa6Ozl6+y4vjIsNP8NqJKYfVxG1WPeOQGXYI7EgCsS3avLCxOoECIuItxWV6U/YoqFeQD0gMIAqoGd5rQrI13bTRe+ukCmhbYh/ca3r8KYc9ihzvoiW6nBBTaD3XlpQ9OEafPhSQEyeOJjocetNb9vlyHtdsCSkAIcWQhH4JIiDvfTjFtTwW5Km1UQ61EwowYKGU4HIhi4Qug+XNFaNS+1gQw4B6x2+UQH2uH66GqBHxmO+BkAXdAFcLPsBdiMP9rjaLEk8rG5rkTyUnq6a/m05qQ4XFEIF9vv4piT69n26qP3TcFJUFxRCVPsBe1Ny/PHyw1WWpqSYJiCEiPrr9vbMBL1enyda+8A+amuEhdMhFzbSKWEGBxjKIc/+Z4hoOxToX/KpbYysICEc8pq/FZF5oU/WBHk3gLWS0Qe8qxLfCMkK8gucIzAl4CZVCPqTiDbTE63Pw26CU/oyqhFsrxe3prVUREXRILejsoIhrEkSWaSpbVKEkOmqG+KEkHoCUCGU4gQ0aYpqv/oRt/Hw5OW1YMEENnxUR4lhP9SZIkd6dBNEXh3DNNmIoTI2GYyN4pZ2C/rt2a5KsltbzMUNXac3m5GoV0uNWA3nsekLj9sm0IngVgXE+b9SMcL7NKXg6D1zBaXmJgsRYloKJx4UbSga/StwRw64AOHBcLEjBxTbyKiFjFXkSTWd3oV6AVP+Tu/XTahjiJ+E68CG+BXcif00lvN0gTK2n/gm7YL8f4kOL2ofcSv6WJ3PHuNC7f9KykeNgys4V8SbOEZZO6ZYUxSNFzZfEca4HevH1QAX8CQIWBId8aJgbZ5U81nvd7F+zFJ+54W11DPEemMmRA0SYELU49SN5cAQdNNFBKLKkVshEh9iY0RE0WsqkYTF86MWdlzwhfxaxzqq6QP24WPCQvgV2g5peNd70Xhy79XjEUZjvbiUJtBTw8GSzvpKxk6cexDqPZLYI58NvKYznxEFkpnwQGSYqQ6IbtmNZCpo5QUX3ILORrH++zu024QdhDPSc5lAIxyHsycCN+OKfjbAk6hYIx8OP9qNfiakqAcFUSMDB4cYHKurgjgwolj/58al1UCGlRU4djMF/StIFDdXr00Yy3WGBRFha5GYu6BxzaiGsB9eLzKFCGo1QwATdSOhfH1bK818qgmTtS2sqo2OADdSBOnlgqODTRNuPAy9Bp0aRBmEnwqOZoVCpAHE2IZKiNDR0leW4iqIW3dfDzAFYbd1jU4JnB1ExfwiLikdbIhegzpn6HB5xUm0FV5F5F5HXJYVnpRatqnZpBqboIgRylasUrqeRZQiqdcILElVDgeLnL9K38XHp1SN8erxFdpCEguMFTW/XVyQiPhzJ4IZIg8B8w3vRUhkBMkf9+f7NzqJAJrieIz/S8QnJnEX5H/aDi+qc/nU3WjxqxdaJNjqNff+zItaJinkWavCyjfN3JhOKb7MDSrXIrpqLtv2jHdZcJIL8qKVLRPivpVJ8tFUJM3Y4U9NxMDkivgfnuuqaEjJEBEfb1yQ/682TlqJtiSaMWKmB654uBDoEAMFzdFwN3jkeu55M5VC9CbPjpQ+KHgIhcBZI7pRRSaBuFgV99kERWjga1eDlnsicvJQALMCASfGOZwnox3tdPILrB6sDs8qoYwCxIWwh2XE1nJIRGb6Dld0FIsLAaUUaaSgUoUYNQA5MpoKqOQXMxwcwGQSEZDqAnB4kJYlrATLztGaC5dwg67LvRMRps6Y4yZjJqGI3p6SMolTwgWOKnCxp4/lLgOn2ug8Zpj0GZcro3fxc1nxekHLBhKXrAn2HZTlP5kZtENPLsJH7IDRuIAXhuuAkqjcDbE6yLwoc9RBr0/B1yV7PfKzDnXnJycgf1RvD15lp6LM46SaH+rd6CBfH6LmPouw8sStg39evLpnxLmBjp5oQgT9LMonN/DYTQbh10lat4oKhccl3rQCtQQQUnMzckYA9xiX1RjeZoGUGe1VB5EL4zLF9sGCQDuEgXTzxeGIVhBcy/Mh7X8jNYiroQtrDG9RQGaUpg4q5clliu1jMoEWBQPp5oujPq0guJ3ERbwuOslfgFXAV90LrLVIUpx2ieAS3XhQ+xhMCl5QKTwt1qTIVesqRV+KZ23Q2L3IvFoX3uvJtzsXK7n7i9/EjchgnzI9Cn/zoq2kVn3VzyofaDcv2rRS3Q/Vn9XRtTr5vc/2Ki2aX29efDpXpY+q/etWFclhrOKmqvOkmgR4Y6U95u3pS9af/6wW9ZCePGTGLeN9XMYv8zKpw4pX5F21+2wSpjU3NfX12YPavz39cS6fzmUlsjo+pMbp8uYFzf/mhdPmm9ZXuPAhQtXMpBJB/XH67Zyk+6Hdb+K0sE7UWBWvKu03qd7bvqwGZ6kOP4eaPmQnYUWd+m7Vkzrtq0HXpwou/jjdxd/UnLZ9LtQ7dYh3Pz/WAY6biyesEr4jTLXf3CbxIY+PRVfHWL76s7Lh/fHHv/4HcMbSyqbBBwA= - - - dbo - - \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201406262150229_Providers.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201406262150229_Providers.Designer.cs deleted file mode 100644 index ade5c06159..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201406262150229_Providers.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class Providers : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(Providers)); - - string IMigrationMetadata.Id - { - get { return "201406262150229_Providers"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201406262150229_Providers.cs b/src/Libraries/SmartStore.Data/Migrations/201406262150229_Providers.cs deleted file mode 100644 index bd18700d09..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201406262150229_Providers.cs +++ /dev/null @@ -1,160 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Linq; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class Providers : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Providers.FriendlyName.CurrencyExchange.ECB", - "ECB exchange rate provider", - "EZB-Wechselkursdienst"); - - builder.AddOrUpdate("Providers.CurrencyExchange.ECB.SetCurrencyToEURO", - "You can use ECB (European central bank) exchange rate provider only when exchange rate currency code is set to EURO", - "Der EZB-Wechselkursdienst kann nur genutzt werden, wenn der Wechselkurs-Whrungscode auf EUR gesetzt ist."); - - builder.AddOrUpdate("Providers.FriendlyName.Tax.Free", - "Free tax rate provider", - "Steuerbefreit"); - - builder.AddOrUpdate("Providers.FriendlyName.CurrencyExchange.MoneyConverter", - "Money converter exchange rate provider", - "Money Converter Wechselkursdienst"); - - var knownGroups = new string[] { "admin", "analytics", "api", "cms", "currencyexchange", "developer", "discountrequirement", "externalauth", "import", "marketing", "misc", "mobile", "payment", "promotionfeed", "security", "seo", "shipping", "social", "tax", "widget" }; - builder.Delete(knownGroups.Select(x => "plugins.knowngroup." + x).ToArray()); - - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Admin", "Administration", "Administration"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Marketing", "Marketing", "Marketing"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Payment", "Payment & Gateways", "Zahlungsschnittstellen"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Shipping", "Shipping & Logistics", "Versand & Logistik"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Tax", "Tax", "Steuern"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Analytics", "Analytics & Stats", "Analyse & Statistiken"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.CMS", "Content Management", "Content Management"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Media", "Media", "Medien"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.SEO", "SEO", "SEO"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Data", "Data", "Daten"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Globalization", "Globalization", "Globalisierung"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Api", "API", "API"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Mobile", "Mobile", "Mobile"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Social", "Social", "Social"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Security", "Security", "Sicherheit"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Developer", "Developer", "Entwickler"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Sales", "Sales", "Vertrieb"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Design", "Design", "Design"); - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Misc", "Miscellaneous", "Sonstige"); - - builder.AddOrUpdate("Admin.Providers.ProvidingPlugin", "Providing plugin", "Bereitstellendes Plugin"); - - builder.AddOrUpdate("Admin.PromotionFeeds", "Promotion feeds", "Promotion Feeds"); - - // some admin menu renaming / new entries - builder.AddOrUpdate("Admin.Configuration.Tax.Providers.Fields.MarkAsPrimaryProvider").Value("de", "Als Standard festlegen"); - builder.AddOrUpdate("Admin.Common.Activate").Value("de", "Aktivieren"); - builder.AddOrUpdate("Admin.Configuration.ActivityLog.ActivityLog").Value("de", "Aktivittslog"); - builder.AddOrUpdate("Admin.Configuration.ActivityLog.ActivityLogTy pe").Value("de", "Aktivittstypen"); - builder.AddOrUpdate("Admin.Configuration.RegionalSettings", - "Regional Settings", - "Regionale Einstellungen"); - builder.AddOrUpdate("Admin.Configuration.Lists", - "Lists", - "Listen"); - - // new product-edit tab - builder.AddOrUpdate("Admin.Catalog.Products.Inventory", - "Inventory", - "Inventar"); - builder.AddOrUpdate("Admin.SaveBeforeEdit", - "In order to proceed, the record must be saved first.", - "Um fortfahren zu knnen, muss der Datensatz zunchst gespeichert werden."); - - // Twisted german resources for tax number and vat id - builder.AddOrUpdate("PDFInvoice.TaxNumber").Value("de", "Steuer-Nr.:"); - builder.AddOrUpdate("PDFInvoice.VatId").Value("de", "Ust-Id:"); - - builder.AddOrUpdate("Account.Login.NewCustomerText").Value("en", "As a registered customer you will be able to shop faster, be up to date on an orders status, and keep track of the orders you have previously made."); - - // Filtering - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.FilterEnabled") - .Value("Activate filter") - .Value("de", "Filterfunktion aktivieren"); - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.FilterEnabled.Hint") - .Value("Activates the filter function for products within categories") - .Value("de", "Aktiviert die Filterfunktion fr Produkte in Warengruppen."); - - // Misc - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.CartSettings") - .Value("Shopping cart") - .Value("de", "Warenkorb"); - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.WishlistSettings") - .Value("Wishlist") - .Value("de", "Wunschzettel"); - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.RoundPricesDuringCalculation") - .Value("Round prices during calculation") - .Value("de", "Preise bei der Berechnung runden"); - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.RoundPricesDuringCalculation.Hint") - .Value("Determines whether the shop calculates with rounded price values (recommended for B2B)") - .Value("de", "Bestimmt, ob der Shop bei Berechnungen gerundete Werte der Preise benutzt (empfohlen fr B2B)"); - builder.AddOrUpdate("ShoppingCart.Weight") - .Value("Weight") - .Value("de", "Gewicht"); - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.ShowWeight") - .Value("Show product weight in shopping cart") - .Value("de", "Zeige Produktgewicht im Warenkorb"); - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.ShowWeight.Hint") - .Value("Determines whether the product weight is shown in shopping cart") - .Value("de", "Legt fest ob das Produktgewicht im Warenkorb angezeigt wird."); - builder.AddOrUpdate("Plugins.ExternalAuth.Facebook.Login") - .Value("Sign in with Facebook") - .Value("de", "Mit Facebook anmelden"); - builder.AddOrUpdate("Plugins.ExternalAuth.Twitter.Login") - .Value("Sign in with Twitter") - .Value("de", "Mit Twitter anmelden"); - - - // Adding some new common provider resources & removing obsolete ones - builder.AddOrUpdate("Common.SystemName").Value("System name").Value("de", "Systemname"); - builder.AddOrUpdate("Common.DisplayOrder").Value("Display order").Value("de", "Reihenfolge"); - builder.AddOrUpdate("Admin.Common.Deactivate").Value("Deactivate").Value("de", "Deaktivieren"); - builder.Delete("Admin.Configuration.Plugins.Fields.IsEnabled"); - - // Tax providers - string prefix = "Admin.Configuration.Tax.Providers."; - builder.Delete(prefix + "Configure", prefix + "Fields", prefix + "Fields.FriendlyName", prefix + "Fields.SystemName"); - // Shipping methods - prefix = "Admin.Configuration.Shipping.Providers."; - builder.Delete(prefix + "DisplayOrder", prefix + "Fields.FriendlyName", prefix + "Fields.SystemName", prefix + "IsActive", prefix + "Fields.IsActive"); - // Widgets - prefix = "Admin.ContentManagement.Widgets."; - builder.Delete(prefix + "BackToList", prefix + "Configure", prefix + "Fields", prefix + "Fields.FriendlyName", prefix + "Fields.SystemName", prefix + "Fields.IsActive", prefix + "Fields.DisplayOrder"); - // ExternalAuth - prefix = "Admin.Configuration.ExternalAuthenticationMethods."; - builder.Delete(prefix + "Fields.DisplayOrder", prefix + "Fields.FriendlyName", prefix + "Fields.SystemName", prefix + "Fields.IsActive"); - // Payment - prefix = "Admin.Configuration.Payment.Methods."; - builder.Delete(prefix + "Fields.DisplayOrder", prefix + "Fields.FriendlyName", prefix + "Fields.SystemName", prefix + "Fields.IsActive"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201406262150229_Providers.resx b/src/Libraries/SmartStore.Data/Migrations/201406262150229_Providers.resx deleted file mode 100644 index 68e6d18a2d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201406262150229_Providers.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201407191010096_BetterReturnRequest.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201407191010096_BetterReturnRequest.Designer.cs deleted file mode 100644 index b62b71ba2d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201407191010096_BetterReturnRequest.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class BetterReturnRequest : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(BetterReturnRequest)); - - string IMigrationMetadata.Id - { - get { return "201407191010096_BetterReturnRequest"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201407191010096_BetterReturnRequest.cs b/src/Libraries/SmartStore.Data/Migrations/201407191010096_BetterReturnRequest.cs deleted file mode 100644 index 66824ec8ed..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201407191010096_BetterReturnRequest.cs +++ /dev/null @@ -1,119 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class BetterReturnRequest : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Order", "UpdatedOnUtc", c => c.DateTime(nullable: false, defaultValue: DateTime.UtcNow)); - AddColumn("dbo.Order", "RewardPointsRemaining", c => c.Int()); - } - - public override void Down() - { - DropColumn("dbo.Order", "UpdatedOnUtc"); - DropColumn("dbo.Order", "RewardPointsRemaining"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - context.Execute("Update [dbo].[Order] Set UpdatedOnUtc = CreatedOnUtc"); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.Delete("Admin.Orders.Products.ReturnRequests", "Admin.Orders.Fields.CancelOrderTotals", "Admin.Orders.Products.AddNew.Note2", - "Admin.Catalog.Products.List.DownloadPDF"); - - builder.AddOrUpdate("Admin.Orders.Products.ReturnRequest", - "Return request", - "Rcksendeauftrag"); - - builder.AddOrUpdate("Admin.Orders.Products.ReturnRequest.Create", - "Create return", - "Rcksendung erstellen"); - - builder.AddOrUpdate("Admin.ReturnRequests.Accept", - "Accept", - "Akzeptieren"); - - builder.AddOrUpdate("Admin.ReturnRequests.Accept.Caption", - "Accept return request", - "Rcksendung akzeptieren"); - - - builder.AddOrUpdate("Admin.Orders.OrderItem.Update.Info", - "Stock quantity old {0}, new {1}.
Reward points old {2}, new {3}.", - "Lagerbestand alt {0}, neu {1}.
Bonuspunkte alt {2}, neu {3}."); - - builder.AddOrUpdate("Admin.Orders.OrderItem.AutoUpdate.AdjustInventory", - "Adjust inventory", - "Lagerbestand anpassen", - "Determines whether to adjust the stock quantity proportionately.", - "Legt fest, ob der Lagerbestand anteilig angepasst werden soll."); - - builder.AddOrUpdate("Admin.Orders.OrderItem.AutoUpdate.UpdateRewardPoints", - "Reduce reward points", - "Bonuspunkte abziehen", - "Determines whether granted reward points should be deducted again proportionately.", - "Legt fest, ob gewhrte Bonuspunkte wieder anteilig abgezogen werden sollen."); - - builder.AddOrUpdate("Admin.Orders.OrderItem.AutoUpdate.UpdateTotals", - "Update totals", - "Summen anpassen", - "Determines whether to update the order totals.", - "Legt fest, ob die Auftragssummen angepasst werden sollen."); - - - builder.AddOrUpdate("Admin.Common.ExportToPdf.All", - "Export to PDF (all)", - "Alles nach PDF exportieren"); - - builder.AddOrUpdate("Admin.Common.ExportToPdf.Selected", - "Export to PDF (selected)", - "Nur Ausgewhlte nach PDF exportieren"); - - - builder.AddOrUpdate("Admin.Catalog.Categories.Delete.Caption", - "Delete category", - "Warengruppe lschen"); - - builder.AddOrUpdate("Admin.Catalog.Categories.Delete.Hint", - "How to treat child categories?", - "Wie soll mit Unterwarengruppen verfahren werden?"); - - builder.AddOrUpdate("Admin.Catalog.Categories.Delete.ResetParentOfChilds", - "Remove the mapping to the parent category.", - "Die Zuordnung zur bergeordneten Warengruppe entfernen."); - - builder.AddOrUpdate("Admin.Catalog.Categories.Delete.DeleteChilds", - "Delete as well.", - "Ebenfalls lschen."); - - - builder.AddOrUpdate("Admin.Configuration.Settings.RewardPoints.PointsForProductReview", - "Points for a product review", - "Punkte fr eine Produkt Rezension", - "Specify number of points awarded for adding an approved product review.", - "Bonuspunkte, die fr das Verfassen einer genehmigten Produkt Rezension gewhrt werden."); - - builder.AddOrUpdate("RewardPoints.Message.EarnedForProductReview", - "Earned reward points for a product review at \"{0}\"", - "Erhaltene Bonuspunkte fr eine Produkt Rezension zu \"{0}\""); - - builder.AddOrUpdate("RewardPoints.Message.ReducedForProductReview", - "Reduced reward points for a product review at \"{0}\"", - "Abgezogene Bonuspunkte fr eine Produkt Rezension zu \"{0}\""); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201407191010096_BetterReturnRequest.resx b/src/Libraries/SmartStore.Data/Migrations/201407191010096_BetterReturnRequest.resx deleted file mode 100644 index c7a10bfac9..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201407191010096_BetterReturnRequest.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201408071158147_MultistorePoll.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201408071158147_MultistorePoll.Designer.cs deleted file mode 100644 index 0d671566a2..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201408071158147_MultistorePoll.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class MultistorePoll : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(MultistorePoll)); - - string IMigrationMetadata.Id - { - get { return "201408071158147_MultistorePoll"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201408071158147_MultistorePoll.cs b/src/Libraries/SmartStore.Data/Migrations/201408071158147_MultistorePoll.cs deleted file mode 100644 index 7b37bbfd6f..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201408071158147_MultistorePoll.cs +++ /dev/null @@ -1,87 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Linq; - using System.Data.Entity.Migrations; - using SmartStore.Core.Domain.Stores; - using SmartStore.Data.Setup; - - public partial class MultistorePoll : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Poll", "LimitedToStores", c => c.Boolean(nullable: false)); - AddColumn("dbo.NewsLetterSubscription", "StoreId", c => c.Int(nullable: false)); - AddColumn("dbo.Campaign", "LimitedToStores", c => c.Boolean(nullable: false)); - - CreateIndex("NewsLetterSubscription", "Email", false, "IX_NewsLetterSubscription_Email", false); - } - - public override void Down() - { - DropIndex("NewsLetterSubscription", "IX_NewsLetterSubscription_Email"); - - DropColumn("dbo.Campaign", "LimitedToStores"); - DropColumn("dbo.NewsLetterSubscription", "StoreId"); - DropColumn("dbo.Poll", "LimitedToStores"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - int storeId = context.SqlQuery("Select Top 1 Id From Store").FirstOrDefault(); - context.Execute("Update [dbo].[NewsLetterSubscription] Set StoreId = {0} Where StoreId = 0".FormatWith(storeId)); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Promotions.NewsLetterSubscriptions.ImportEmailsSuccess", - "{0} email(s) were imported and {1} updated.", - "Es wurden {0} E-Mail(s) importiert und {1} aktualisiert."); - - - builder.AddOrUpdate("Admin.Catalog.Products.Fields.ProductUrl", - "URL of the product page", - "URL der Produktseite", - "The URL of the product page.", - "Die URL der Produktseite."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.EnableHtmlTextCollapser", - "Truncate long texts", - "Langtexte krzen", - "Option to truncate long texts and to only show on click in full length.", - "Option, bei der Langtexte gekrzt und erst auf Klick in voller Lnge angezeigt werden."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.HtmlTextCollapsedHeight", - "Height of truncated long text", - "Hhe des gekrzten Langtextes", - "Determines the height of the truncated long text.", - "Legt die Hhe (in Pixel) des gekrzten Langtextes fest."); - - - builder.AddOrUpdate("Admin.System.SystemInfo.DataProviderFriendlyName", - "Data provider", - "Daten-Provider", - "The name of the data provider.", - "Der Name des Daten-Providers."); - - builder.AddOrUpdate("Admin.System.SystemInfo.AppDate", - "Created on", - "Erstellt am", - "The creation date of this SmartStore.NET version.", - "Das Erstellungsdatum dieser SmartStore.NET Version."); - - builder.AddOrUpdate("Admin.Orders.Fields.OrderWeight", - "The total weight of this order", - "Das Gesamtgewicht des Auftrags", - "The total weight of this order.", - "Das Gesamtgewicht des Auftrags."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201408071158147_MultistorePoll.resx b/src/Libraries/SmartStore.Data/Migrations/201408071158147_MultistorePoll.resx deleted file mode 100644 index ba876748f9..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201408071158147_MultistorePoll.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201408291450443_CheckoutCommmentBox.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201408291450443_CheckoutCommmentBox.Designer.cs deleted file mode 100644 index 50e366aa26..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201408291450443_CheckoutCommmentBox.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class CheckoutCommmentBox : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(CheckoutCommmentBox)); - - string IMigrationMetadata.Id - { - get { return "201408291450443_CheckoutCommmentBox"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201408291450443_CheckoutCommmentBox.cs b/src/Libraries/SmartStore.Data/Migrations/201408291450443_CheckoutCommmentBox.cs deleted file mode 100644 index 90e7a135e2..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201408291450443_CheckoutCommmentBox.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class CheckoutCommmentBox : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public override void Up() - { - AddColumn("dbo.Order", "CustomerOrderComment", c => c.String()); - } - - public override void Down() - { - DropColumn("dbo.Order", "CustomerOrderComment"); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - // add or update resources - builder.AddOrUpdate("ShoppingCart.CommentBox") - .Value("Do you want to tell us something regarding this order?") - .Value("de", "Mchten Sie uns etwas Wichtiges zu Ihrer Bestellung mitteilen?"); - - builder.AddOrUpdate("Admin.Order.CustomerComment.Heading") - .Value("The customer has added the following comment to his order") - .Value("de", "Der Kunde hat folgenden Kommentar fr diese Bestellung hinterlassen"); - - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.ShowCommentBox") - .Value("Show comment box on confirm order page") - .Value("de", "Zeige Kommentarbox auf Bestellabschlussseite"); - - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.ShowCommentBox.Hint") - .Value("Determines whether comment box is displayed on confirm order page") - .Value("de", "Legt fest ob der Kunde auf der Bestellabschlussseite einen Kommentar zu seiner Bestellung hinterlegen kann."); - } - - public bool RollbackOnFailure - { - get { return false; } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201408291450443_CheckoutCommmentBox.resx b/src/Libraries/SmartStore.Data/Migrations/201408291450443_CheckoutCommmentBox.resx deleted file mode 100644 index 080c5fef34..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201408291450443_CheckoutCommmentBox.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201409150936394_CustomerNameFormat.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201409150936394_CustomerNameFormat.Designer.cs deleted file mode 100644 index a4de110fc2..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201409150936394_CustomerNameFormat.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class CustomerNameFormat : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(CustomerNameFormat)); - - string IMigrationMetadata.Id - { - get { return "201409150936394_CustomerNameFormat"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201409150936394_CustomerNameFormat.cs b/src/Libraries/SmartStore.Data/Migrations/201409150936394_CustomerNameFormat.cs deleted file mode 100644 index efb1c46e73..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201409150936394_CustomerNameFormat.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class CustomerNameFormat : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Customers.CustomerNameFormat.ShowFirstName", - "Show first name", - "Vornamen anzeigen"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Customers.CustomerNameFormat.ShowNameAndCity", - "Show shorted name and city", - "Gekrzten Namen und Ort anzeigen"); - - builder.AddOrUpdate("Common.ComingFrom", - "from", - "aus"); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CustomerNameFormatMaxLength", - "Maximum length of the customer name", - "Maximale Lnge des Benutzernamens", - "Determines the maximum length of the displayed customer name.", - "Legt die maximale Lnge des angezeigten Benutzernamens fest."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201409150936394_CustomerNameFormat.resx b/src/Libraries/SmartStore.Data/Migrations/201409150936394_CustomerNameFormat.resx deleted file mode 100644 index 28776adb34..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201409150936394_CustomerNameFormat.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201410091620122_NewRes.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201410091620122_NewRes.Designer.cs deleted file mode 100644 index 0e36edcc1e..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201410091620122_NewRes.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class NewRes : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(NewRes)); - - string IMigrationMetadata.Id - { - get { return "201410091620122_NewRes"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201410091620122_NewRes.cs b/src/Libraries/SmartStore.Data/Migrations/201410091620122_NewRes.cs deleted file mode 100644 index 4c637b9c9d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201410091620122_NewRes.cs +++ /dev/null @@ -1,115 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Linq; - using System.Data.Entity.Migrations; - using SmartStore.Core.Domain.Tasks; - using SmartStore.Data.Setup; - using SmartStore.Core.Domain.Localization; - - public partial class NewRes : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - // update scheduled task types - var table = context.Set(); - - ScheduleTask task; - - task = table.FirstOrDefault(x => x.Name.StartsWith("PromotionFeed.Froogle")); - if (task != null) - { - task.Name = "SmartStore.GoogleMerchantCenter feed file generation"; - task.Type = "SmartStore.GoogleMerchantCenter.StaticFileGenerationTask, SmartStore.GoogleMerchantCenter"; - } - - task = table.FirstOrDefault(x => x.Name.StartsWith("PromotionFeed.Billiger")); - if (task != null) - { - task.Name = "SmartStore.Billiger feed file generation"; - task.Type = "SmartStore.Billiger.StaticFileGenerationTask, SmartStore.Billiger"; - } - - task = table.FirstOrDefault(x => x.Name.StartsWith("PromotionFeed.Guenstiger")); - if (task != null) - { - task.Name = "SmartStore.Guenstiger feed file generation"; - task.Type = "SmartStore.Guenstiger.StaticFileGenerationTask, SmartStore.Guenstiger"; - } - - task = table.FirstOrDefault(x => x.Name.StartsWith("PromotionFeed.ElmarShopinfo")); - if (task != null) - { - task.Name = "SmartStore.ElmarShopinfo feed file generation"; - task.Type = "SmartStore.ElmarShopinfo.StaticFileGenerationTask, SmartStore.ElmarShopinfo"; - } - - task = table.FirstOrDefault(x => x.Name.StartsWith("PromotionFeed.Shopwahl")); - if (task != null) - { - task.Name = "SmartStore.Shopwahl feed file generation"; - task.Type = "SmartStore.Shopwahl.StaticFileGenerationTask, SmartStore.Shopwahl"; - } - - task = table.FirstOrDefault(x => x.Name.StartsWith("MailChimp")); - if (task != null) - { - task.Type = "SmartStore.MailChimp.MailChimpSynchronizationTask, SmartStore.MailChimp"; - } - - context.SaveChanges(); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Promotions").Value("de", "Marketing"); - builder.AddOrUpdate("Admin.Plugins.Manage", - "Manage plugins", - "Plugins verwalten"); - - builder.AddOrUpdate("admin.help.nopcommercenote", - "SmartStore.NET is a fork of the ASP.NET open source e-commerce solution {0}.", - "SmartStore.NET ist ein Fork der ASP.NET Open-Source E-Commerce-Lsung {0}."); - - builder.AddOrUpdate("Payment.ExpirationDate", - "Valid until", - "Gltig bis"); - - builder.Update("Plugins.Payment.CashOnDelivery.PaymentInfoDescription") - .Value("en", "Once your order is placed, you will be contacted by our staff to confirm the order.") - .Value("de", "Sobald Ihre Bestellung abgeschloen ist, werden Sie persnlich von einem unserer Mitarbeiter kontaktiert, um die Bestellung zu besttigen."); - - builder.Update("Plugins.Payment.Invoice.PaymentInfoDescription") - .Value("en", "Once your order is placed, you will be contacted by our staff to confirm the order.") - .Value("de", "Sobald Ihre Bestellung abgeschloen ist, werden Sie persnlich von einem unserer Mitarbeiter kontaktiert, um die Bestellung zu besttigen."); - - builder.Update("Plugins.Payment.DirectDebit.PaymentInfoDescription") - .Value("en", "Once your order is placed, you will be contacted by our staff to confirm the order.") - .Value("de", "Sobald Ihre Bestellung abgeschloen ist, werden Sie persnlich von einem unserer Mitarbeiter kontaktiert, um die Bestellung zu besttigen."); - - builder.Update("Plugins.Payment.PayInStore.PaymentInfoDescription") - .Value("en", "Reserve items at your local store, and pay in store when you pick up your order.") - .Value("de", "Reservieren Sie Produkte und zahlen Sie an der Kasse in unserem Ladenlokal."); - - builder.Update("Plugins.Payment.Prepayment.PaymentInfoDescription") - .Value("en", "Once your order is placed, you will be contacted by our staff to confirm the order.") - .Value("de", "Sobald Ihre Bestellung abgeschloen ist, werden Sie persnlich von einem unserer Mitarbeiter kontaktiert, um die Bestellung zu besttigen."); - - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201410091620122_NewRes.resx b/src/Libraries/SmartStore.Data/Migrations/201410091620122_NewRes.resx deleted file mode 100644 index f00f34bf07..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201410091620122_NewRes.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201411182201537_EuEsd.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201411182201537_EuEsd.Designer.cs deleted file mode 100644 index 8be4a5ab81..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201411182201537_EuEsd.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class EuEsd : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(EuEsd)); - - string IMigrationMetadata.Id - { - get { return "201411182201537_EuEsd"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201411182201537_EuEsd.cs b/src/Libraries/SmartStore.Data/Migrations/201411182201537_EuEsd.cs deleted file mode 100644 index 103035c04a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201411182201537_EuEsd.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class EuEsd : DbMigration - { - public override void Up() - { - AddColumn("dbo.Product", "IsEsd", c => c.Boolean(nullable: false)); - } - - public override void Down() - { - DropColumn("dbo.Product", "IsEsd"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201411182201537_EuEsd.resx b/src/Libraries/SmartStore.Data/Migrations/201411182201537_EuEsd.resx deleted file mode 100644 index 3f005e104b..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201411182201537_EuEsd.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201411182202431_V21Final.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201411182202431_V21Final.Designer.cs deleted file mode 100644 index 85879d8f1d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201411182202431_V21Final.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class V21Final : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(V21Final)); - - string IMigrationMetadata.Id - { - get { return "201411182202431_V21Final"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201411182202431_V21Final.cs b/src/Libraries/SmartStore.Data/Migrations/201411182202431_V21Final.cs deleted file mode 100644 index 022cd442c6..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201411182202431_V21Final.cs +++ /dev/null @@ -1,153 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class V21Final : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - // New stuff from MH - builder.AddOrUpdate("Products.CallForPrice.GoToForm", - "Ask for price", - "Preis anfragen"); - builder.AddOrUpdate("ThemeVar.Alpha.DisplayNavbar", - "Display Mega Menu", - "Zeige Mega Menu"); - - // Tax & VAT stuff - builder.Delete("ShoppingCart.Totals.TaxRateLine"); - builder.AddOrUpdate("ShoppingCart.Totals.SubTotal", - "Subtotal", - "Zwischensumme"); - builder.AddOrUpdate("ShoppingCart.Totals.Tax", - "Tax", - "MwSt"); - builder.AddOrUpdate("ShoppingCart.Totals.TaxRateLineIncl", - "Incl. {0} % Tax", - "inkl. {0} % MwSt"); - builder.AddOrUpdate("ShoppingCart.Totals.TaxRateLineExcl", - "Plus {0} % Tax", - "zzgl. {0} % MwSt"); - builder.AddOrUpdate("Admin.Catalog.Products.Fields.IsEsd", - "Is Electronic Service", - "Ist elektronische Leistung", - "Specifies whether the product is an electronic service bound to EU VAT regulations for digital goods (2008/8/EG directive)", - "Legt fest, ob das Produkt elektronisch vertrieben wird und daher gem EU Richtlinie 2008/8/EG versteuert werden muss."); - - // Some german fixes - builder.AddOrUpdate("Products.ProductHasBeenAddedToTheWishlist").Value("de", "Das Produkt wurde Ihrer Wunschliste hinzugefgt"); - builder.AddOrUpdate("Products.ProductHasBeenAddedToTheWishlist.Link").Value("de", "Das Produkt wurde Ihrer Wunschliste hinzugefgt"); - builder.AddOrUpdate("Products.ProductHasBeenAddedToTheCart").Value("de", "Das Produkt wurde Ihrem Warenkorb hinzugefgt"); - builder.AddOrUpdate("Products.ProductHasBeenAddedToTheCart.Link").Value("de", "Das Produkt wurde Ihrem Warenkorb hinzugefgt"); - builder.AddOrUpdate("Products.ProductNotAddedToTheCart.Link").Value("de", "Produkt konnte Ihrem Warenkorb nicht hinzugefgt werden."); - builder.AddOrUpdate("Products.RecentlyViewedProducts").Value("de", "Zuletzt angesehen"); - - builder.AddOrUpdate("Admin.Catalog.Attributes.CheckoutAttributes.Deleted").Value("de", "Das Attribut wurde erfolgreich gelscht."); - - // Theme inheritance - builder.AddOrUpdate("Admin.Configuration.Themes.IsBasedOn", - "Based on", - "Basiert auf"); - builder.AddOrUpdate("Admin.Configuration.Themes.MissingBaseTheme", - "Error: Base theme '{0}' not found", - "Fehler: Basis-Theme '{0}' nicht gefunden"); - builder.AddOrUpdate("Admin.Configuration.Themes.Reload", - "Reload themes", - "Themes aktualisieren"); - - // Theme Preview - builder.AddOrUpdate("Admin.Configuration.Themes.Preview", - "Preview", - "Vorschau"); - builder.AddOrUpdate("Admin.Configuration.Themes.Theme", - "Theme", - "Theme"); - builder.AddOrUpdate("Admin.Configuration.Themes.PreviewMode", - "Preview Mode", - "Vorschaumodus"); - builder.AddOrUpdate("Admin.Configuration.Themes.ExitPreviewMode", - "Exit preview mode", - "Vorschau beenden"); - - builder.AddOrUpdate("Common.Apply", "Apply", "bernehmen"); - - builder.AddOrUpdate("Common.Reload", "Reload", "Neu laden"); - builder.AddOrUpdate("Common.Refresh", "Refresh", "Aktualisieren"); - - builder.AddOrUpdate("Admin.Orders.PdfInvoice", "Order as PDF", "Auftrag als PDF"); - builder.AddOrUpdate("Order.GetPDFInvoice", "Order as PDF", "Auftrag als PDF"); - - builder.AddOrUpdate("Admin.Orders.Fields.Affiliate", "Affiliate", "Partner"); - builder.AddOrUpdate("Admin.Customers.Customers.Fields.Affiliate", "Affiliate", "Partner"); - - builder.AddOrUpdate("Admin.Configuration.DeliveryTime.BackToList", "Back to delivery times list", "Zurck zur Lieferzeitenliste"); - - builder.AddOrUpdate("Admin.Configuration.Measures.Weights.Description", - "NOTE: if you change your primary weight, then do not forget to update the appropriate ratios of the units.", - "Achtung: Wenn die Standardgewichtseinheit gendert wird, mssen auch die zugehrigen Umrechnungseinheiten (Verhltnis) angepasst werden."); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.Seconds.Positive", - "Seconds should be positive.", - "Sekunden mssen grer als 0 sein."); - - builder.Delete("Admin.Affiliates.Customers.Name"); - - // Avatars - builder.AddOrUpdate("Account.Avatar.MaximumUploadedFileSize", - "Maximum avatar size is {0}", - "Die maximale Gre des Avatars betrgt {0}"); - builder.AddOrUpdate("Account.Avatar.UploadRules", - "Avatar must be in GIF, PNG or JPG format with the maximum size of {0}", - "Ein Avatar muss im GIF-, PNG- oder JPG-Format vorliegen und darf {0} nicht berschreiten."); - - // Misc - builder.AddOrUpdate("Admin.Catalog.Attributes.AttributeControlType", - "Control type", - "Typ"); - builder.AddOrUpdate("Admin.Catalog.Attributes.AttributeControlType.Hint", - "Choose how to display your attribute values.", - "Bestimmt den Steuerelement-Typen fr die Erfassung der Attribut-Werte"); - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.AttributeCombinations.Fields.Sku").Value("#"); - builder.AddOrUpdate("Admin.Catalog.Products.Fields.SeName").Value("en", "URL Alias"); - builder.AddOrUpdate("Admin.Catalog.Manufacturers.Fields.SeName").Value("en", "URL alias"); - builder.AddOrUpdate("Admin.Catalog.Categories.Fields.SeName").Value("en", "URL alias"); - - - // Marketplace - builder.AddOrUpdate("Admin.Marketplace", - "Marketplace", - "Marketplace"); - builder.AddOrUpdate("Admin.Marketplace.News", - "Marketplace News", - "Marketplace News"); - builder.AddOrUpdate("Admin.Marketplace.ComingSoon", - "In the SmartStore.NET Marketplace we offer modules, themes & language packages, which will make your shop better and more successful. Once we are ready to go, you'll be informed about the latest extensions here. Stay tuned...", - "Im SmartStore.NET Marketplace werden Module, Themes & Sprachpakete angeboten, die Ihren Onlineshop besser, flexibler und erfolgreicher machen sollen. Sobald wir die Arbeiten am Marketplace abgeschlossen haben, werden Sie hier ber die neuesten Erweiterungen informiert."); - builder.AddOrUpdate("Admin.Marketplace.Visit", - "Visit Marketplace", - "Zum Marketplace"); - } - - - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201411182202431_V21Final.resx b/src/Libraries/SmartStore.Data/Migrations/201411182202431_V21Final.resx deleted file mode 100644 index 3f005e104b..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201411182202431_V21Final.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201412081934318_V211.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201412081934318_V211.Designer.cs deleted file mode 100644 index 2a0242351c..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201412081934318_V211.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class V211 : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(V211)); - - string IMigrationMetadata.Id - { - get { return "201412081934318_V211"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201412081934318_V211.cs b/src/Libraries/SmartStore.Data/Migrations/201412081934318_V211.cs deleted file mode 100644 index 11b4d61f22..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201412081934318_V211.cs +++ /dev/null @@ -1,256 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Core.Domain.Topics; - using SmartStore.Data.Setup; - using System.Linq; - - public partial class V211 : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - var topic = context.Set().FirstOrDefault(x => x.SystemName == "PageNotFound"); - if (topic != null) - { - context.Set().Remove(topic); - } - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.CanonicalHostNameRule", - "Canonical host name rule", - "Regel fr kanonischen Domnennamen"); - builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.CanonicalHostNameRule.Hint", - "Enforces permanent redirection to a single domain name for a better page rank (e.g. mystore.com > www.mystore.com or vice versa)", - "Erzwingt die permanente Umleitung zu einem einzelnen Domnnennamen fr ein besseres Seitenranking (z.B. meinshop.de > www.meinshop.de oder umgekehrt)"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Seo.CanonicalHostNameRule.NoRule", - "Don't apply", - "Nicht anwenden"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Seo.CanonicalHostNameRule.RequireWww", - "Require www prefix", - "www-Prfix erzwingen"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Seo.CanonicalHostNameRule.OmitWww", - "Omit www prefix", - "www-Prfix weglassen"); - - // order payment row - builder.AddOrUpdate("Admin.Orders.Fields.PartialRefundOffline", - "Partial refund (Offline)", - "Teilerstattung (Offline)"); - builder.AddOrUpdate("Admin.Orders.Fields.Void", - "Cancel", - "Stornieren"); - builder.AddOrUpdate("Admin.Orders.Fields.VoidOffline", - "Cancel (Offline)", - "Stornieren (Offline)"); - - builder.AddOrUpdate("Admin.Orders.Fields.MarkAsPaid.Hint", - "Sets the payment status to 'paid' without contacting the payment provider.", - "Setzt den Zahlungsstatus auf 'Bezahlt' ohne dabei den Zahlungsanbieter zu kontaktieren."); - builder.AddOrUpdate("Admin.Orders.Fields.Capture.Hint", - "Books a previously authorised payment through the payment provider.", - "Zieht eine zuvor reservierte Zahlung ber den Zahlungsanbieter ein."); - builder.AddOrUpdate("Admin.Orders.Fields.Refund.Hint", - "Initiates a refund of the total order value at the payment provider.", - "Leitet eine Rckerstattung des gesamten Auftragswertes beim Zahlungsanbieter ein."); - builder.AddOrUpdate("Admin.Orders.Fields.RefundOffline.Hint", - "Setzt the payment status to 'refunded' without contacting the payment provider.", - "Setzt den Zahlungsstatus auf 'Erstattet', ohne dabei den Zahlungsanbieter zu kontaktieren."); - builder.AddOrUpdate("Admin.Orders.Fields.PartialRefund.Hint", - "Initiates the refund of a partial amount of the total order value at the payment provider.", - "Leitet die Rckerstattung eines Teilbetrages des Auftragswertes beim Zahlungsanbieter ein."); - builder.AddOrUpdate("Admin.Orders.Fields.PartialRefundOffline.Hint", - "Setzt the payment status to 'partially refunded' including the refunded amount without contacting the payment provider.", - "Setzt den Zahlungsstatus auf 'Teilweise erstattet' samt Erstattungsbetrag, ohne dabei den Zahlungsanbieter zu kontaktieren."); - builder.AddOrUpdate("Admin.Orders.Fields.Void.Hint", - "Initiates the cancellation of the payment transaction at the payment provider.", - "Leitet die Stornierung der Zahlungstransaktion beim Zahlungsanbieter ein."); - builder.AddOrUpdate("Admin.Orders.Fields.VoidOffline.Hint", - "Setzt the payment status to 'canceled' without contacting the payment provider.", - "Setzt den Zahlungsstatus auf 'Storniert', ohne dabei den Zahlungsanbieter zu kontaktieren."); - - // Error Handling (404/500) - builder.Delete("PageTitle.PageNotFound"); - builder.AddOrUpdate("ErrorPage.Title", - "Oops!", - "Oops!"); - builder.AddOrUpdate("ErrorPage.Body", - @"We apologize, a server error ocurred while handling your request, this is not a problem with your computer or internet connection. - The details have been sent to our support team and we will investigate the issue very soon.

- In the meantime, please retry your request as it may have been temporary.", - @"Leider ist ein Serverfehler aufgetreten, und das hat nichts mit Ihrem Computer oder Ihrem Internetanschluss zu tun. - Unser Support Team wurde bereits benachrichtigt und wird sich sehr bald um die Behebung kmmern."); - builder.AddOrUpdate("NotFoundPage.Title", - "404", - "404"); - builder.AddOrUpdate("NotFoundPage.Body", - "Sorry! The page you were looking for could not be found.", - "Tut uns leid! Diese Adresse gibt es auf unserer Website nicht."); - - // amazon payment review - builder.AddOrUpdate("Checkout.ConfirmYourOrder", - "Please confirm your order.", - "Bitte besttigen Sie Ihren Auftrag."); - - builder.AddOrUpdate("Checkout.ConfirmHint", - "Please verify the order total and the specifics regarding the billing address and, if required, the shipping address. You can make corrections to your entry anytime by clicking on back. If everything is as it should be, deliver your order to us by clicking confirm.", - "Bitte prfen Sie die Gesamtsumme und die Rechnungsadresse. Bei abweichender Lieferanschrift prfen Sie bitte auch diese. nderungen knnen Sie jederzeit mit einem Klick auf zurck vornehmen. Sind alle Daten richtig, besttigen Sie bitte mit einem Klick auf kaufen Ihre Bestellung."); - - // new setting - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.DeliveryTimeIdForEmptyStock", - "Delivery time dislayed when stock is empty", - "Lieferzeit, die bei einem leerem Lagerbestand angezeigt wird", - "Delivery time to be dislayed when the stock quantity of a product is equal or less 0.", - "Lieferzeit, die angezeigt wird, wenn der Warenbestand des Produktes kleiner gleich 0 ist."); - - // old setting now with ui - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.IncludeFeaturedProductsInNormalLists", - "Show featured products in lists", - "Top-Produkte in Listen anzeigen", - "Specifies whether to display featured products in product and filter lists. Otherwise they only appear in the top featured product list.", - "Legt fest, ob Top-Produkte sowohl in den Produkt- als auch in den Filterlisten angezeigt werden sollen. Ansonsten erscheinen sie nur oberhalb in der Top-Produktliste."); - - // new notification - builder.AddOrUpdate("Admin.Order.NotFound", - "An order with this number was not found.", - "Ein Auftrag mit dieser Nummer wurde nicht gefunden."); - - // PDF - builder.AddOrUpdate("Common.Date", - "Date", - "Datum"); - - builder.Delete( - "PDFInvoice.Address", - "PDFInvoice.Address2", - "PDFInvoice.BillingInformation", - "PDFInvoice.Company", - "PDFInvoice.CreatedOn", - "PDFInvoice.Discount", - "PDFInvoice.Fax", - "PDFInvoice.GiftCardInfo", - "PDFInvoice.Name", - "PDFInvoice.Note", - "PDFInvoice.OrderDate", - "PDFInvoice.OrderTotal", - "PDFInvoice.PaymentMethod", - "PDFInvoice.PaymentMethodAdditionalFee", - "PDFInvoice.Phone", - "PDFInvoice.PurchaseOrderNumber", - "PDFInvoice.RewardPoints", - "PDFInvoice.ShippingInformation", - "PDFInvoice.ShippingMethod", - "PDFInvoice.Sub-Total", - "PDFInvoice.Shipping", - "PDFInvoice.Tax", - "PDFInvoice.TaxRate", - "PDFInvoice.VATNumber"); - - builder.AddOrUpdate("PDFInvoice.Footer.Bankcode").Value("de", "BLZ: {0}"); - builder.AddOrUpdate("PDFInvoice.TaxNumber").Value("en", "Tax Number: {0}"); - builder.AddOrUpdate("PDFInvoice.Order#", - "Order#", - "Auftrag#"); - builder.AddOrUpdate("PDFInvoice.SKU", - "SKU", - "Artikelnr."); - - builder.AddOrUpdate("PDFPackagingSlip.DeliveryDate", - "Delivery date", - "Lieferdatum"); - builder.AddOrUpdate("PDFPackagingSlip.Weight", - "Weight", - "Gewicht"); - builder.AddOrUpdate("PDFPackagingSlip.DeliveryDate", - "Delivery date", - "Lieferdatum"); - builder.AddOrUpdate("PDFPackagingSlip.TrackingNumber", - "Tracking number", - "Trackingnummer"); - builder.AddOrUpdate("PDFPackagingSlip.ShippingMethod", - "Shipping method", - "Versandart"); - builder.AddOrUpdate("PDFPackagingSlip.ProductListHeadline", - "Shipped products", - "Gelieferte Produkte"); - - builder.AddOrUpdate("Admin.Common.ExportToPdf.TooManyItems", - "Too many items! The PDF conversion is limited to 500 items. Please reduce the amount of selected records.", - "Zu viele Objekte! Mehr als 500 Objekte knnen nicht konvertiert werden. Bitte reduzieren Sie die Anzahl ausgewhlter Datenstze."); - builder.AddOrUpdate("Admin.Common.ExportToPdf.TocTitle", - "Table of contents", - "Inhaltsverzeichnis"); - - builder.AddOrUpdate("PDFProductCatalog.Manufacturer", - "Manufacturer", - "Hersteller"); - builder.AddOrUpdate("PDFProductCatalog.Length", - "Legth", - "Lnge"); - builder.AddOrUpdate("PDFProductCatalog.Width", - "Width", - "Breite"); - builder.AddOrUpdate("PDFProductCatalog.Height", - "Height", - "Hhe"); - - builder.AddOrUpdate("PDFProductCatalog.SpecificationAttributes", - "Specification attributes", - "Spezifikation"); - builder.AddOrUpdate("PDFProductCatalog.BundledItems", - "Bundled items", - "Produktset besteht aus"); - builder.AddOrUpdate("PDFProductCatalog.AssociatedProducts", - "Associated products", - "Gruppierte Produkte"); - - - builder.AddOrUpdate("PDFProductCatalog.CompanyEmailAddress", - "Mail", - "E-Mail"); - builder.AddOrUpdate("PDFProductCatalog.CompanyTelephoneNumber", - "Phone", - "Telefon"); - builder.AddOrUpdate("PDFProductCatalog.CompanyFaxNumber", - "Fax", - "Fax"); - builder.AddOrUpdate("PDFProductCatalog.Cover.Address", - "Address", - "Anschrift"); - builder.AddOrUpdate("PDFProductCatalog.Cover.ContactData", - "Contact data", - "Kontaktdaten"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Order.MinOrderSubtotalAmount").Value("de", "Mindestbestellwert Zwischensumme"); - builder.AddOrUpdate("Admin.Configuration.Settings.Order.MinOrderSubtotalAmount.Hint").Value("de", "Legt den Mindestbestellwert der Zwischensumme fest."); - builder.AddOrUpdate("Admin.Configuration.Settings.Order.MinOrderTotalAmount").Value("de", "Mindestbestellwert Gesamtsumme"); - builder.AddOrUpdate("Admin.Configuration.Settings.Order.MinOrderTotalAmount.Hint").Value("de", "Legt den Mindestbestellwert der Gesamtsumme fest."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowProductReviewsInProductDetail", - "Show product reviews in product detail view", - "Zeige Produktbewertungen in der Produktdetailansicht"); - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowProductReviewsInProductDetail.Hint", - "Determines whether product reviews are shown in product detail view.", - "Legt fest, ob Produktbewertungen in der Produktdetailansicht angezeigt werden."); - - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201412081934318_V211.resx b/src/Libraries/SmartStore.Data/Migrations/201412081934318_V211.resx deleted file mode 100644 index 3f005e104b..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201412081934318_V211.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201501191849483_WidgetWrapContent.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201501191849483_WidgetWrapContent.Designer.cs deleted file mode 100644 index 2588652294..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201501191849483_WidgetWrapContent.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class WidgetWrapContent : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(WidgetWrapContent)); - - string IMigrationMetadata.Id - { - get { return "201501191849483_WidgetWrapContent"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201501191849483_WidgetWrapContent.cs b/src/Libraries/SmartStore.Data/Migrations/201501191849483_WidgetWrapContent.cs deleted file mode 100644 index 67db7d913b..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201501191849483_WidgetWrapContent.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class WidgetWrapContent : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Topic", "WidgetWrapContent", c => c.Boolean()); - } - - public override void Down() - { - DropColumn("dbo.Topic", "WidgetWrapContent"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.ContentManagement.Topics.Fields.WidgetWrapContent", - "Add wrapper around content", - "Inhalt mit Container umhllen", - "Adds an HTML wrapper around widget content", - "Umhllt den Widget Inhalt mit einem HTML-Container"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201501191849483_WidgetWrapContent.resx b/src/Libraries/SmartStore.Data/Migrations/201501191849483_WidgetWrapContent.resx deleted file mode 100644 index e64cc40d76..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201501191849483_WidgetWrapContent.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201501221417371_DefaultViewModeForCategories.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201501221417371_DefaultViewModeForCategories.Designer.cs deleted file mode 100644 index 775e8ffd3d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201501221417371_DefaultViewModeForCategories.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class DefaultViewModeForCategories : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(DefaultViewModeForCategories)); - - string IMigrationMetadata.Id - { - get { return "201501221417371_DefaultViewModeForCategories"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201501221417371_DefaultViewModeForCategories.cs b/src/Libraries/SmartStore.Data/Migrations/201501221417371_DefaultViewModeForCategories.cs deleted file mode 100644 index 0ab1511472..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201501221417371_DefaultViewModeForCategories.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Core.Domain.Topics; - using SmartStore.Data.Setup; - using System.Linq; - - public partial class DefaultViewModeForCategories : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Category", "DefaultViewMode", c => c.String()); - } - - public override void Down() - { - DropColumn("dbo.Category", "DefaultViewMode"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.Checkout", - "Checkout", - "Checkout"); - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.ValidateEmailAddress", - "Validate customer email address", - "Prfung der Kunden-Email-Adresse"); - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.ValidateEmailAddress.Hint", - "Determines whether the customers email address is validated during the checkout process", - "Bestimmt ob die Email-Adresse des Kunden im Checkout validiert wird."); - builder.AddOrUpdate("Admin.Address.Fields.EmailMatch", - "Reenter email address", - "Besttigung der E-Mail"); - builder.AddOrUpdate("Address.Fields.EmailMatch", - "Reenter email address", - "Besttigung der E-Mail"); - builder.AddOrUpdate("Admin.Address.Fields.EmailMatch.Required", - "Please reenter your email address", - "Bitte wiederholen Sie die Eingabe Ihrer E-Mail-Adresse"); - builder.AddOrUpdate("Admin.Address.Fields.EmailMatch.MustMatchEmail", - "The email address has to match the email address entered before", - "Die E-Mail-Adresse muss mit der zuvor eingegebenen E-Mail-Adresse bereinstimmen"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201501221417371_DefaultViewModeForCategories.resx b/src/Libraries/SmartStore.Data/Migrations/201501221417371_DefaultViewModeForCategories.resx deleted file mode 100644 index 0910187fe6..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201501221417371_DefaultViewModeForCategories.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201502120858030_Licensing.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201502120858030_Licensing.Designer.cs deleted file mode 100644 index ee292d9cbb..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201502120858030_Licensing.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class Licensing : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(Licensing)); - - string IMigrationMetadata.Id - { - get { return "201502120858030_Licensing"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201502120858030_Licensing.cs b/src/Libraries/SmartStore.Data/Migrations/201502120858030_Licensing.cs deleted file mode 100644 index 618732eb72..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201502120858030_Licensing.cs +++ /dev/null @@ -1,81 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class Licensing : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Common.License", - "License", - "Lizenzieren"); - - builder.AddOrUpdate("Admin.Common.Licensed", - "Licensed", - "Lizenziert"); - - builder.AddOrUpdate("Admin.Common.ResourceNotFound", - "The resource was not found.", - "Die gewnschte Ressource wurde nicht gefunden."); - - builder.AddOrUpdate("Admin.Configuration.Plugins.LicenseActivated", - "The plugin has been successfully licensed.", - "Das Plugin wurde erfolgreich lizenziert."); - - builder.AddOrUpdate("Admin.Configuration.Plugins.LicenseKey", - "License key", - "Lizenzschlssel", - "Please enter the license key for the plugin.", - "Bitte den Lizenzschlssel fr das Plugin eingeben."); - - builder.AddOrUpdate("Admin.Plugins.AddOnLicensing", - "Add-on licensing", - "Add-on Lizenzierung"); - - builder.AddOrUpdate("Admin.Plugins.LicensingDemoRemainingDays", - "Demo {0} day(s) remaining", - "Demo {0} Tag(e) verbleibend"); - - builder.AddOrUpdate("Admin.Plugins.LicensingDemoNotStarted", - "Demo", - "Demo"); - - builder.AddOrUpdate("Admin.Plugins.LicensingDemoExpired", - "Demo expired", - "Demo abgelaufen"); - - builder.AddOrUpdate("Admin.Plugins.LicensingResetStatusCheck", - "Renew check", - "Prfung erneuern"); - - - builder.AddOrUpdate("Admin.Configuration.Payment.CannotActivatePaymentMethod", - "Activating this payment method is forbidden by the plugin.", - "Das Plugin erlaubt keine Aktivierung dieser Zahlungsmethode."); - - builder.AddOrUpdate("Admin.Configuration.Shipping.CannotActivateShippingRateComputationMethod", - "Activating this shipping rate computation method is forbidden by the plugin.", - "Das Plugin erlaubt keine Aktivierung dieser Berechnungsmethode fr Versandkosten."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201502120858030_Licensing.resx b/src/Libraries/SmartStore.Data/Migrations/201502120858030_Licensing.resx deleted file mode 100644 index ee1843014a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201502120858030_Licensing.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201502191643156_QtyUnitsAndOrderItemTaxRate.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201502191643156_QtyUnitsAndOrderItemTaxRate.Designer.cs deleted file mode 100644 index b3c53d818d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201502191643156_QtyUnitsAndOrderItemTaxRate.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class QtyUnitsAndOrderItemTaxRate : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(QtyUnitsAndOrderItemTaxRate)); - - string IMigrationMetadata.Id - { - get { return "201502191643156_QtyUnitsAndOrderItemTaxRate"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201502191643156_QtyUnitsAndOrderItemTaxRate.cs b/src/Libraries/SmartStore.Data/Migrations/201502191643156_QtyUnitsAndOrderItemTaxRate.cs deleted file mode 100644 index 48ccd918a5..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201502191643156_QtyUnitsAndOrderItemTaxRate.cs +++ /dev/null @@ -1,134 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class QtyUnitsAndOrderItemTaxRate : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - CreateTable( - "dbo.QuantityUnit", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 50), - Description = c.String(maxLength: 50), - DisplayLocale = c.String(maxLength: 50), - DisplayOrder = c.Int(nullable: false), - IsDefault = c.Boolean(nullable: false), - }) - .PrimaryKey(t => t.Id); - - AddColumn("dbo.Product", "QuantityUnitId", c => c.Int()); - AddColumn("dbo.Order", "OrderShippingTaxRate", c => c.Decimal(nullable: false, precision: 18, scale: 4)); - AddColumn("dbo.Order", "PaymentMethodAdditionalFeeTaxRate", c => c.Decimal(nullable: false, precision: 18, scale: 4)); - AddColumn("dbo.OrderItem", "TaxRate", c => c.Decimal(nullable: false, precision: 18, scale: 4)); - AddColumn("dbo.ProductVariantAttributeCombination", "QuantityUnitId", c => c.Int()); - AddColumn("dbo.Topic", "TitleTag", c => c.String()); - CreateIndex("dbo.Product", "QuantityUnitId"); - CreateIndex("dbo.ProductVariantAttributeCombination", "QuantityUnitId"); - AddForeignKey("dbo.ProductVariantAttributeCombination", "QuantityUnitId", "dbo.QuantityUnit", "Id"); - AddForeignKey("dbo.Product", "QuantityUnitId", "dbo.QuantityUnit", "Id"); - } - - public override void Down() - { - DropForeignKey("dbo.Product", "QuantityUnitId", "dbo.QuantityUnit"); - DropForeignKey("dbo.ProductVariantAttributeCombination", "QuantityUnitId", "dbo.QuantityUnit"); - DropIndex("dbo.ProductVariantAttributeCombination", new[] { "QuantityUnitId" }); - DropIndex("dbo.Product", new[] { "QuantityUnitId" }); - DropColumn("dbo.Topic", "TitleTag"); - DropColumn("dbo.ProductVariantAttributeCombination", "QuantityUnitId"); - DropColumn("dbo.OrderItem", "TaxRate"); - DropColumn("dbo.Order", "PaymentMethodAdditionalFeeTaxRate"); - DropColumn("dbo.Order", "OrderShippingTaxRate"); - DropColumn("dbo.Product", "QuantityUnitId"); - DropTable("dbo.QuantityUnit"); - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - // QtyUnits - builder.AddOrUpdate("Admin.Configuration.QuantityUnit.AddNew", - "Add new quantity unit", - "Verpackungseinheit hinzufgen"); - builder.AddOrUpdate("Admin.Configuration.QuantityUnit.EditQuantityUnitDetails", - "Edit quantity unit", - "Verpackungseinheit bearbeiten"); - builder.AddOrUpdate("Admin.Configuration.QuantityUnit", - "Quantity units", - "Verpackungseinheiten"); - - builder.AddOrUpdate("Admin.Configuration.QuantityUnit.Fields.Name", - "Name", - "Name"); - builder.AddOrUpdate("Admin.Configuration.QuantityUnit.Fields.Name.Hint", - "Set the name of quantity unit", - "Legt den Namen der Verpackungseinheit fest"); - builder.AddOrUpdate("Admin.Configuration.QuantityUnit.Fields.IsDefault", - "Default quantity unit", - "Standard-Verpackungseinheit"); - builder.AddOrUpdate("Admin.Configuration.QuantityUnit.Fields.IsDefault.Hint", - "Sets the default quantity unit", - "Legt die Standard-Verpackungseinheit fest"); - - - builder.AddOrUpdate("Admin.Configuration.QuantityUnit.Added", - "Quantity unit successfully added", - "Verpackungseinheit wurde erfolgreich zugefgt"); - builder.AddOrUpdate("Admin.Configuration.Quantityunits.Updated", - "Quantity unit successfully updated", - "Verpackungseinheit wurde erfolgreich aktualisiert"); - builder.AddOrUpdate("Admin.Catalog.Products.Fields.QuantityUnit", - "Quantity unit", - "Verpackungseinheit"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowDefaultQuantityUnit", - "Show default quantity unit", - "Zeige die Standard-Verpackungseinheit", - "Show default quantity unit if the product has no quantity unit set.", - "Zeige die Standard-Verpackungseinheit, falls fr das Produkt keine Verpackungseinheit festgelegt ist."); - - - // OrderItemTaxRate - builder.AddOrUpdate("Admin.Orders.Products.AddNew.TaxRate", - "Tax rate", - "Steuersatz", - "The tax rate for the product", - "Die Steuerrate des Produktes"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.MaxFilterItemsToDisplay", - "Maximum filter items", - "Maximale Anzahl Filtereintrge", - "Determines the maximum amount of filter items to display", - "Bestimmt die maximale Anzahl angezeigter Filtereintrge"); - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ExpandAllFilterCriteria", - "Expand all filter groups", - "Alle Filtergruppen aufklappen", - "Determines whether all filter groups should be displayed expanded", - "Legt fest, ob alle Filtergruppen aufgeklappt angezeigt werden sollen"); - - builder.AddOrUpdate("Admin.Common.Export.Wait", - "Please wait while the export is being executed", - "Bitte haben Sie einen Augenblick Geduld, whrend der Export durchgefhrt wird"); - - builder.AddOrUpdate("Admin.ContentManagement.Topics.Fields.TitleTag", - "Title tag", - "Titel-Tag", - "Determines the title tag of the topic", - "Legt das Tag fest, welches fr die berschrift des Topics ausgegeben wird"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201502191643156_QtyUnitsAndOrderItemTaxRate.resx b/src/Libraries/SmartStore.Data/Migrations/201502191643156_QtyUnitsAndOrderItemTaxRate.resx deleted file mode 100644 index 299cfbcddd..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201502191643156_QtyUnitsAndOrderItemTaxRate.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201502202248081_NewIndexesV22.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201502202248081_NewIndexesV22.Designer.cs deleted file mode 100644 index 00b899bd87..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201502202248081_NewIndexesV22.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class NewIndexesV22 : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(NewIndexesV22)); - - string IMigrationMetadata.Id - { - get { return "201502202248081_NewIndexesV22"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201502202248081_NewIndexesV22.cs b/src/Libraries/SmartStore.Data/Migrations/201502202248081_NewIndexesV22.cs deleted file mode 100644 index 3a5409115b..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201502202248081_NewIndexesV22.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class NewIndexesV22 : DbMigration - { - 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.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.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/201502202248081_NewIndexesV22.resx b/src/Libraries/SmartStore.Data/Migrations/201502202248081_NewIndexesV22.resx deleted file mode 100644 index f920141203..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201502202248081_NewIndexesV22.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201502280342294_LocalizedPropertyIndex.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201502280342294_LocalizedPropertyIndex.Designer.cs deleted file mode 100644 index cbae839811..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201502280342294_LocalizedPropertyIndex.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class LocalizedPropertyIndex : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(LocalizedPropertyIndex)); - - string IMigrationMetadata.Id - { - get { return "201502280342294_LocalizedPropertyIndex"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201502280342294_LocalizedPropertyIndex.cs b/src/Libraries/SmartStore.Data/Migrations/201502280342294_LocalizedPropertyIndex.cs deleted file mode 100644 index 40d882b9e4..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201502280342294_LocalizedPropertyIndex.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class LocalizedPropertyIndex : DbMigration - { - public override void Up() - { - DropIndex("dbo.LocalizedProperty", new[] { "LanguageId" }); - CreateIndex("dbo.LocalizedProperty", new[] { "EntityId", "LocaleKey", "LocaleKeyGroup", "LanguageId" }, name: "IX_LocalizedProperty_Compound"); - } - - public override void Down() - { - DropIndex("dbo.LocalizedProperty", "IX_LocalizedProperty_Compound"); - CreateIndex("dbo.LocalizedProperty", "LanguageId"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201502280342294_LocalizedPropertyIndex.resx b/src/Libraries/SmartStore.Data/Migrations/201502280342294_LocalizedPropertyIndex.resx deleted file mode 100644 index 6e085155b0..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201502280342294_LocalizedPropertyIndex.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201503032227054_V22Res.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201503032227054_V22Res.Designer.cs deleted file mode 100644 index cba99355c6..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201503032227054_V22Res.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class V22Res : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(V22Res)); - - string IMigrationMetadata.Id - { - get { return "201503032227054_V22Res"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201503032227054_V22Res.cs b/src/Libraries/SmartStore.Data/Migrations/201503032227054_V22Res.cs deleted file mode 100644 index b66b0d7113..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201503032227054_V22Res.cs +++ /dev/null @@ -1,112 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - using System.Linq; - - public partial class V22Res : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - // Checkout - builder.Delete("Checkout.YourOrderHasBeenSuccessfullyProcessed"); - builder.AddOrUpdate("Checkout.OrderHasBeenReceived", - "Your order has been received", - "Ihre Bestellung ist angekommen"); - builder.AddOrUpdate("Checkout.ThankYou", - "Thank you for your purchase!", - "Vielen Dank fr Ihren Einkauf!"); - builder.AddOrUpdate("Checkout.OrderNumber", - "Your order number", - "Ihre Bestellnummer"); - builder.AddOrUpdate("Checkout.PlacedOrderDetails", - "Order details", - "Bestelldetails"); - builder.AddOrUpdate("Checkout.Continue", - "Continue shopping", - "Weiter einkaufen"); - - // Move pictures - builder.Delete("Admin.Configuration.Settings.Media.PicturesStoredIntoDatabase.Hint"); - builder.AddOrUpdate("Common.Shrink", - "Shrink", - "Verkleinern"); - builder.AddOrUpdate("Common.ShrinkDatabaseSuccessful", - "The database has been successfully shrinked", - "Die Datenbank wurde erfolgreich verkleinert"); - builder.AddOrUpdate("Admin.Configuration.Settings.Media.MovePicturesNote", - "Do not forget to backup your database before changing this option. Please bear in mind that this operation can take several minutes depending on the amount of images.", - "Bitte sichern Sie Ihre Datenbank, ehe Sie Mediendateien verschieben. Dieser Vorgang kann je nach Menge der Bilddaten mehrere Minuten in Anspruch nehmen."); - builder.AddOrUpdate("Admin.Configuration.Settings.Media.MoveToDb", - "Move to database", - "In Datenbank verschieben"); - builder.AddOrUpdate("Admin.Configuration.Settings.Media.MoveToFs", - "Move to file system", - "Ins Dateisystem verschieben"); - - // Misc - builder.AddOrUpdate("Mobile.ViewFullSite", - "Desktop version", - "Desktop Version"); - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.FilterEnabled", - "Display filter sidebar widget", - "Filter Sidebar Widget anzeigen"); - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.FilterEnabled.Hint", - "Displays the filter sidebar widget for products within categories", - "Aktiviert das Filter Sidebar Widget zum Filtern von Produkten in Warengruppen"); - builder.AddOrUpdate("Admin.Catalog.Products.SpecificationAttributes.Fields.ShowOnProductPage.Hint") - .Value("en", "Check to display the attribute in the public product detail page"); - builder.AddOrUpdate("Admin.Catalog.Products.SpecificationAttributes.AddNew", - "Assign a specification attribute", - "Spezifikationsattribut zuordnen"); - builder.AddOrUpdate("Admin.Catalog.Attributes.SpecificationAttributes.Bundled.ShowNotOnProductPage") - .Value("en", "Don't show on product page"); - - builder.AddOrUpdate("Admin.Configuration.Themes.Option.SaveThemeChoiceInCookie", - "Save theme choice in cookie", - "Benutzer Theme in Cookie speichern"); - builder.AddOrUpdate("Admin.Configuration.Themes.Option.SaveThemeChoiceInCookie.Hint", - "If unchecked, user's theme choice is associated to the account, which may be undesirable when, for example, multiple users share a guest account.", - "Wenn nicht gewhlt, wird das Benutzer Theme mit dem Kundenkonto verknpft, was u.U. unerwnscht sein kann, wenn sich bspw. mehrere User einen Gastzugang teilen."); - - builder.AddOrUpdate("ShoppingCart.SelectAttribute", - "Please select '{0}'.", - "Bitte '{0}' auswhlen."); - - builder.AddOrUpdate("ShoppingCart.AttributeError", - "An unknown error has occurred in the examination of the selected product variant.", - "Ein unbekannter Fehler ist bei der Prfung der ausgewhlten Produktvariante aufgetreten."); - - builder.AddOrUpdate("Common.Execution", - "Execution", - "Ausfhrung"); - - builder.AddOrUpdate("Admin.Catalog.Attributes.SpecificationAttributes.OptionsCount", - "Number options", - "Anzahl Optionen"); - - builder.AddOrUpdate("Admin.Common.DeleteSelected", - "Delete selected", - "Ausgewhlte lschen"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201503032227054_V22Res.resx b/src/Libraries/SmartStore.Data/Migrations/201503032227054_V22Res.resx deleted file mode 100644 index 6e085155b0..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201503032227054_V22Res.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201503101851123_PdfConverterUser.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201503101851123_PdfConverterUser.Designer.cs deleted file mode 100644 index 93180e898a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201503101851123_PdfConverterUser.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class PdfConverterUser : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(PdfConverterUser)); - - string IMigrationMetadata.Id - { - get { return "201503101851123_PdfConverterUser"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201503101851123_PdfConverterUser.cs b/src/Libraries/SmartStore.Data/Migrations/201503101851123_PdfConverterUser.cs deleted file mode 100644 index d0a855570c..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201503101851123_PdfConverterUser.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Core.Domain.Customers; - using SmartStore.Data.Setup; - - public partial class PdfConverterUser : DbMigration, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.Set().AddOrUpdate(x => x.SystemName, - new Customer - { - Email = "builtin@pdf-converter-record.com", - CustomerGuid = Guid.NewGuid(), - PasswordFormat = PasswordFormat.Clear, - AdminComment = "Built-in system record used for the PDF converter.", - Active = true, - IsSystemAccount = true, - SystemName = SystemCustomerNames.PdfConverter, - CreatedOnUtc = DateTime.UtcNow, - LastActivityDateUtc = DateTime.UtcNow, - } - ); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201503101851123_PdfConverterUser.resx b/src/Libraries/SmartStore.Data/Migrations/201503101851123_PdfConverterUser.resx deleted file mode 100644 index 6e085155b0..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201503101851123_PdfConverterUser.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201504131412183_NewCategoryProperties.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201504131412183_NewCategoryProperties.Designer.cs deleted file mode 100644 index cf72dff95e..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504131412183_NewCategoryProperties.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class NewCategoryProperties : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(NewCategoryProperties)); - - string IMigrationMetadata.Id - { - get { return "201504131412183_NewCategoryProperties"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201504131412183_NewCategoryProperties.cs b/src/Libraries/SmartStore.Data/Migrations/201504131412183_NewCategoryProperties.cs deleted file mode 100644 index 024cc37051..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504131412183_NewCategoryProperties.cs +++ /dev/null @@ -1,102 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using System.Web.Hosting; - using SmartStore.Core.Data; - using SmartStore.Data.Setup; - - public partial class NewCategoryProperties : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Category", "FullName", c => c.String(maxLength: 400)); - AddColumn("dbo.Category", "BottomDescription", c => c.String()); - AddColumn("dbo.ReturnRequest", "RequestedActionUpdatedOnUtc", c => c.DateTime()); - AddColumn("dbo.ReturnRequest", "AdminComment", c => c.String(maxLength: 4000)); - AddColumn("dbo.CheckoutAttribute", "IsActive", c => c.Boolean(nullable: false)); - } - - public override void Down() - { - DropColumn("dbo.CheckoutAttribute", "IsActive"); - DropColumn("dbo.ReturnRequest", "AdminComment"); - DropColumn("dbo.ReturnRequest", "RequestedActionUpdatedOnUtc"); - DropColumn("dbo.Category", "BottomDescription"); - DropColumn("dbo.Category", "FullName"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Catalog.Categories.Fields.FullName", - "Complete name", - "Vollstndiger Name", - "Complete name displayed as title on the category page.", - "Vollstndiger Name, der als Titel auf der Warengruppenseite angezeigt wird."); - - builder.AddOrUpdate("Admin.Catalog.Categories.Fields.BottomDescription", - "Bottom description", - "Untere Beschreibung", - "Optional second description displayed below products on the category page.", - "Optionale zweite Beschreibung, die auf der Warengruppenseite unterhalb der Produkte angezeigt wird."); - - builder.AddOrUpdate("Admin.Catalog.Products.List.SearchWithoutCategories", - "Without category mapping", - "Ohne Warengruppenzuordnung", - "Filters for products without category mapping.", - "Filtert nach Produkten ohne Warengruppenzuordnung."); - - builder.AddOrUpdate("Admin.Catalog.Products.List.SearchWithoutManufacturers", - "Without manufacturer mapping", - "Ohne Herstellerzuordnung", - "Filters for products without manufacturer mapping.", - "Filtert nach Produkten ohne Herstellerzuordnung."); - - builder.AddOrUpdate("Admin.Common.AdminComment", - "Admin comment", - "Admin-Kommentar", - "Admin comment for internal use. Won't be published.", - "Kommentar fr internen Gebrauch. Wird nicht verffentlicht."); - - builder.AddOrUpdate("Admin.ReturnRequests.Fields.RequestedActionUpdatedOnUtc", - "Last update of requested action", - "Letzte Aktualisierung der angeforderten Aktion", - "Date when the requested action was updated the last time.", - "Datum, an dem die angeforderte Aktion zuletzt gendert wurde."); - - builder.AddOrUpdate("Admin.Common.CreateMutuallyAssociations", - "Create all mutual associations", - "Alle gegenseitigen Zuordnungen erstellen"); - - builder.AddOrUpdate("Admin.Common.AskCreateMutuallyAssociations", - "Do you want to create all mutual associations?", - "Mchten Sie alle gegenseitigen Zuordnungen erstellen?"); - - builder.AddOrUpdate("Admin.Common.CreateMutuallyAssociationsResult", - "There were {0} mutual association(s) created.", - "Es wurden {0} gegenseitige Zuordnung(en) erstellt."); - - builder.AddOrUpdate("Admin.Configuration.Plugins.LicensingInvalidStoreUrl", - "The license key cannot be activated for the entered store URL. Please enter the right store URL in your store details before activating the key.", - "Der Lizenzschlssel kann fr die hinterlegte Shop-URL nicht aktiviert werden. Bitte tragen Sie vor der Aktivierung in den Shop-Details die korrekte Shop-URL ein."); - - builder.AddOrUpdate("Admin.Configuration.Plugins.ConfirmLicensing", - "Please check whether the licensing is done for the right store URL! Proceed with the licensing?", - "Bitte berprfen Sie, ob die Lizenzierung fr die richtige Shop-URL erfolgt! Mit der Lizenzierung fortfahren?"); - - builder.AddOrUpdate("Admin.Common.Unlicensed", - "Unlicensed", - "Unlizenziert"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201504131412183_NewCategoryProperties.resx b/src/Libraries/SmartStore.Data/Migrations/201504131412183_NewCategoryProperties.resx deleted file mode 100644 index 8ebb7814c7..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504131412183_NewCategoryProperties.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - H4sIAAAAAAAEAOy9WXMcO7Im+D5m8x9keupuuy0dqarM6padM20kRR7RrhYWk5Ju3RdaMBIkUYyMyBMLl2qbXzYP85PmLwyA2BDYt1gylS9SMgB8ABwOh8PhcPx//8//++v/et4krx5BXsAs/e31uze/vH4F0jhbw/Tut9dVefs///r6f/1f/+f/8evpevP86nub7084HyqZFr+9vi/L7d/evi3ie7CJijcbGOdZkd2Wb+Js8zZaZ2/f//LLv7999+4tQBCvEdarV79eVmkJN4D8gf48ydIYbMsqSj5na5AUzXeUsiKor75EG1Bsoxj89nq1ifJyVWY5ePMhKqPXr44SGKFmrEBy+/pVlKZZGZWokX/7VoBVmWfp3WqLPkTJ1csWoHy3UVKApvF/67Ob9uOX97gfb/uCLVRcFWW2sQR896eGMG/Z4k7kfd0RDpHuFJG4fMG9JuT77fXfq4h8+pbC8vUrtsK/nSQ5zjyg8AkhM8oG0zcfYA5i9PXlDQ30b6+Y7P/Wccn7N4id3vzyb69OqqSscvBbCqoyj5J/e3VR3SQw/g/wcpU9gPS3tEoSuumo8Sht8AF9usizLcjLl0tw23TofP361dthubdswa4YVabu5nla/un961dfUOXRTQI6zqBIQnr1O0hBHpVgfRGVJchTjAFI/7nambrwv21tiBXRlHr96nP0/Amkd+X9b6//gubQGXwG6/ZD0wBEVjQBUZkyr4CggepKP4AizuG25iHfujVVwWKbRC+fsjhKAnTUqLKv+RrkuiFUI50XH8BthHiyhTnOsgREqQbo17f9jFLOM1TduorL4ypdJ+C8BJujEtHkpirBGUxK3HrruXeCJF2S3b3RQR9mo7yunmj6WtVIHdGDAX2Pksob7by4yAFaBpGUBms73kZgX6JHeEcILaXb61eXICFZinu4rZdcLUte0+XP8mxzmSUGk4Qqdr3KqjzGAiazL3sV5XegDDWTQ07dw1yV19UQy3dG1MQOBNZqPX4oaBmLM6T9tigfQAw3UfL6FZq7MWy08L++frXCa+pvr/9sincB0BRJy+gOWM98O63lz7+EWM1X91lemqkq6GeACmvJ0AmKwpdKH+EaXN1Xm5s0gokv2Hc08DeJ98gR4VDcOwj/0XStkxxgAfA1/VbGHcujL1dwY6/aftuuPcCkCxyzehRzLXPtUuWyzLVLpN2i3tRg1uFrpoyik8Os+vWbyS9as1WdseuGUQfMm65qtK2i4a9eHJQKhXisSYSr89UFLqIc1fl7nlVb1JAw+kWzCJyna/gI11WUJC8LWcota51+bUftm7K+o/UGpifZZgN6PW60ylquBRu0JPtvOdHgPH1NP2YbcBFAV/wMyghN8acsXxej64y4simHGdd3BUulaStMz46SJHs6IaIf5JfgEYInbwX1aLvNs0ewvkSyM71bVRs/vvmSlYERW7grJN4TptPebQwHuqpu/gnQ/MuOYm89/xPcwBI3jqxk3kO8eqjGn3RRWt1GMVYYcrTqlV/QlqffFIxW7e+IxUav5Lz4Hd6WJ1HuvWFqcULoFpfgjwrm4Gt5jyheC39vTmkwe1VFJa3f/RJGqFVYky1hjDWZo/WaaYN3n86LD9lTmmSR/3a3wfEduW9pUk/wFtC7j2hU6gn39ZbDdLQ/NTCnz1uYE7X6Q/TCYppBHMUlfCQQIdj+Y1SsIqTjgFCjOkTjmqdtzbcC5Ed3OQC0mufamAHYFXgeX288Ly5BXOU1uq/0aIBOXuIE1I3ylXE04gXIYeY9+zpMsvYTYM+5cl6s7uH2NMVFvNnxvDhDw48BtwGGBMlTiGdelLSIJ/fYAGFuVrZu/lX0fPqMdiHec+G8OC28yYlac4K2Q3dZ/uLLOEjFQVuh8/QRTU0Eh9T+e39uRBpe/BDsyABbYwni0WMEUVmYUKieVl5hS50XLJgG7Pmn7KnuNV5pEJrvoKANArx9Idv3syxv23gM0O7L86Apih8ybC3H/kvep8J4N4gRz2taoh1It+n11iiITR+NEtxUmzCDVCNGz+EQCQHAugGDYAJlFc0F3MDj6uW4KsveuOAxt3DuH7C4T2BRhgFtJn8CEPMiuT44nnHFPEHaOYGDsf/5Ew0SfAX6mqzHraDZmJxkhcXprO2ysEU4UWLZEXPMVYn26O2ZmPCYzBzrNF17ILXmrNMUn6kF4a9GbA2QwchM0Qi2SetEm48rCOo56S3w0RoKirI7OjzJNjcwbU7RAjIhanPrilAcbbcJ9NeYfwB4dz/eVBzuY4LD/4DrEdE/jkubbqXxlScdkJ8wCeeQEM5HAiTAxc9uuQ4SfAfhI8hfcGFL6wntIG5Z9DgqQLP6BNl2d3ifQVRUOaid38d1Ve7qPNqEdPXqYPGPIbQZaYnTAjnR0ligwpjXG6cOkGM/iVCGjwEopkZozNV9VttTkOAL50TbLIndEin0VWlTr5vcV1lvK++dVeS5OA8iRVZbhyFaGqgcba6HGTkfm0G6zL1mmMnRHaj30TH06HJ3cNJ6bkkcoix71Bi8yIZY3qHWLKZwdGKzyDrA5XNsOH2IqGw7nVHRflE2WR+EeR37cQEJjrILTR5F65kcsoaz2Rzb3B2Cy5tcZ1G0eJhB1mAml2N7yQ70FsYkC+2vK2+/uIiiP+oCsv5pSjn29yq6U3buepCPl6dUslSc0nkcpen3KIdIqRPtI5XNV5RTDJBBKdkomRR1HCoW2qrjFr217aJzv4Y3QhU8OMzIMyGdLuXCQSZbNmTPhhWNZbPyzR3mkDaYyWbbZNqGI2hulyziDC6R4wU+h2j0jT2Q+3spDheDG6W2+3XwQpbXpfHJfT+OT247MiE8Nb4VeK8U6LaR/P5ScKNWW5Xtrtz65LcxmTnYuRormUPJxruqOMmqbZaeIE7xtkxxSOLjtzCnb2RkiH8kmWW+PNojYeLZ+oHotvFXmWYrxm+86QKqvXyfj1uWlJltFyeBiWEMi4S+D5zxwtgi0UA1nI8dqzSdEJcQ9EOUUd4VYW6v60DtvtvjPlALcViK5XVpluKRrsfg2yqBatYZ8Ke7K3GclYhPJ72Eg8P+jL4ytdMo1PWbw4WZID2r79+FcgBsLG2WJ1X4/tQK/gv41T24+lNcZXX8Dhbb2SOnwfk6dB7z2HcYeABdRumd0mcrWAiBgDfZRvBfWPDNoaWdgu9lwAGOVCTo1HcInj6rN1WO0jj04aPDlkV/ACnY3xhbYWsxLWx9f1LVZupbzKZxmjyXwe8uf9sCa939M1jD6E1T/qC4K8RXTaJjmEZ577Le/GUykXR+hhuAMZVL2Cg350F2BhOQqncHfwp0+fELeLKW20FPSPmpKs6hPSFl5rXbtB1g+kfiOEzjqaJ8ydT36XQQpyXLeTZIjqtkk8ZyBof1bdC1NUTwHIH9LcgpFoV3mMXKeUPIFWD+sWS/rBKweilKsNEYzMLYL1ZbMPrp0DFMEuxdiCvxt1x0lz/DwF2CAtE2JhvNLhwJmreWlpEeppPwKnNAmMGjKo1eMPPU9zinrLgdDb21JXTNjV3AcpxOn5GgqoOoT7YX7f0Mxjupke5Clec6bmvPwNvVI6Y6DXRYbeR1aRaCcWKqn2RJln8EzyRM8+S170SYddt9VoAjTgbpMGvkdQXaaYU6cDkvzkCEB6JVEGa0H0tXqp5DDXzt+8xyZ/vuh2RTxGccYQvncjtA194gu7gL3l3fXzbQaAf5MLp8oMn9U8iIIbca3msZFlJfbhn8IZmH8gIjyA+fGzom7Q8iSwIJkYP0WLIf1aSeLRQnHFyBluUKdHDe2UHnnYM/y+75szidcg0Xd/6MS5TOKQrCTCF2G/WVVf99Ro0TTEd4hSM8NPUT75ZGxJxkaYlPt6bQ5XXSO5QJGxNukoCoddhyP6J8BMn2tkr+AQoSZDQI2JfMBctHh7e97y3R2yW3wi0bWqM0tEhBUaC5qgtxwJW5ZuazpEfSEuor7fJiXmcH7ax2ET+tztH9OuxP5HW1NPq9gl2t9W+HKM4a56xA8ShPN9SzYuNVcxEVBd6AjC5824rOsnwT+Xv8NGirKBl/3Zj0tZeAYZaPbm9hAgNsVEn0Xe/dTCDN+ryoHUKO4sEzis4++tN5l3yKivJ8e7Reo/3O+L4BQfcNuOmfsjuYOl5OxuXbGM5SCPdLuTVJJSpDK/yvqWyUbziXynuH81lsVZ3G6acbe1Uz2byCtg6zyBvM5LNtNbvrUDW7yXPd6zR8u9k8nNIlzWh7U4D2XdKQm8kqoPYgh5zYw2y2tD59xipNlBxV5T1WauogQ5cgrq2Dgh6oSgiHwagANyZmpWwHCKkB1eYiKyR3UbpkYUf4VK7VgixOTbzKtjBWtJGkyxs5TBa3kslj20xiYhK3kCQJGzdM4drFJNs26RKgXUaKfayAbIAHWYRNFOfgmirJZt/kpyhfX2QwLYuPsChlx/6CfJLWy/MJ+qDIbNuT1gfRaK3hMgvEH5NHLgDZjNYRnqjAm/KojWwuIfmlmTjay3N6be07gtrv7NFGI0vfNACHXb28rjOYF+VkmvskFel2/YG2BtlmG6UvE9QjdkrX7MgQd+BAqY8wjW1P9U6op0dGtAeQqfluqorej17Rf8Et1pWiRBPsKZAp6D5LgfYZyUAvA7fv5y18vy3fydRzSLgUtgttl6dfAJkkbtFm063XanqSKhvH5OSbOMggbegwl19wpZZcHt76DcZheZbXpVkwTcPzuHhkFI3hw9uCSsBCRa6/eso+AUzE8yLTCNr3AQTS1X0OgGmFfwpQIRK0IIcxU5nno8vfI28TbzjfkBEcSPnLY921NeHmh85z3Reu5RETqUOfW7Ad0hax3pDSklvSrYFwFyxnwgx844W5vPZxQ3o4LBktADN0h4VDIUf23ps0nHfY+V2KaF0//WrvRGcglSjBMoU8YtU/CxHmNr+H2qmHRjhAOkxveV2BrqrPpF7e3OTgEUYaMREm9uSiFRfVxtRxPecmv3LV93K56o40A3heNViHOa+Y8w2pvO+EGbhPhPGFP8dBIBHr7dUDf9rz8vBH/LLTIqkvgNO0Pk6yu85Dy3pK49LFGwpjGX7cTWMm8Y3GnccmYOsJKnd5aRCFLEXR+rrP2LOTKJ1jJWEmbzaqm+LBQxjgsBbI6/oUpXdVdOftlRjqfoJt/KZsHeKIyemiWc3s3pel2pWtwTsRPCRqq4xnZXjQ+sGzkQXfPI+1HK5+LvES4UhHeNQ6JbaiOK2GrPFTuWSaNrWVzdJmEtQ+17CNg0Thcj3M4bVW942wXqtJICX4L5L5TYtzWLLldc1ja2lHpqG6css3Rv2o9B8VWIEpDuvOkujufIM6i8M/a8gdJMzXZbm/TxKMYO2qY6/Vg3EJCiLaxMJclFMoMZUZOfGuzu11xiWC9paqAsyDhNXLugABXAmtNSJkpEcm29p1YRJH2iSdF3h2XSTVHUw5IaK9dpdVsUj4BFGdAgkFVqUyEyFOQuFCcv85VKgECnIZ1rZB63yn4Y+oaDoY7kz2YnjDfcrL8cr7//o79U4M+AU8Fe4mXVz6DQWxDB5rLbqTRNaY0nyMKY1vT4QzH7eIQkanBva6z9iztSidY2JhJm+WrZvizq+4/EFRkte149bj1X2Wh5iOltWeEdE1da3BdnTzGGl/Rpv7oiyqB6N5kJ65bVm6JVG0TeEShavrMIftFRNqfRZbWpy0ANauolQV3HZOWZJ8z0qyJYtJaB1rbQBDFG9YoGXosLhVR2nx5ODMI9/adJjifQ1DiGs6P7WZkWfjdzCKvH77ZqppHuNeQxwUQTUfjuxDOpJaVF9I/HqL+K9fYRf0ZF2GRYRkGjZTpM4znHpUknC60enWoQKZ6SoJDugvJ7gwgAYyxVlOeEmIg2yQ1xVqkziPfKhDkjWa7vi3sYNt0oI+x042X7+TADJXGZaUM54KLmI36rszc9sFEGkt2gEMEoTy3l3z72WsXM4bLkQieS5aqLwuE2DoIDcJMNBBrsvr0p1q/uUvoxjPcgCC3QEPFdwTATUSDeMwQ6ARrUHCebZxOGvm91w6TIJwmo6usZQB+QYWhSbKHptLHi9QnZOXROrsfsolg+1ybxnEVY5QOQIc5JOzfBpVX5xHNvbP3o1es0ewz2CTmNNzzOa80yRWBd700jZUwIfZreD1QJcYpwny1o5y3btbOEEIrLbKRjGZsOavmJcJ901T1VEcg6KYpsILfAEabVrHjFTudjF0pAjErJC1i1vsJGu7cMEOgpWULfqQxQcRKq+LxDr2foQ4kBwO5Bllu2+60F9hDxM+YucvnltHAWclhyJQuGkD+xDdmibSGUWNpGJ9y5tJZfKXZ01jvAQawThINHldhEqLkWh4tDCAL1ATfk/li2J4C8n+bLR5FcCn+d8hePKEwCGfnQIiSHBCDXGLV8tduyOE/VgQ7J5cEMpaybMMVmuCroFNJmHryE9V0+oM/kuAp/Q/CH55XYRAv+dZtR35lHmku1pTOjd+6R9S8SJUkMUBS9Ag+5B9XCJ+rneL+zksl+bXdCZGmlNpYmlOZ/CX5k0jvEQ6wTjIdYWQ2Xtp/BPOccULXqbzW/h2l0gAOM3vZjCsp3b9+lf932FWy+siBJroJRBSl/dTw6TDSzEsDB+S9LYuDB8Ks3zjh5AXh4atgjUkDNpF9IKvkQQFq+NeT/hIbXcXpMpzkMYvk7wI1FZ2GfV+nh9ADDdR8vrVRY5+ESn2+t1fX79a4ZAHv73+q/NcGHpO+Y7T96icUqysqpsSicTkPI2TKwxrRKw/W3drUNnp8zSVXeHK0NiQh52n6uGg0ml62kidaXrYVDZpz1BFFlPZvrKBcESrCMQqQpScATA2TeU1j01gec1jU7vBHz8WJ+Gh0Zm0nevj1kLEymhVIMW7StdgfbQZtSf0w7Q/QA4Q2wWIh34P4oesKo9KxEc3VQmm3PtylRf/uZngwc1G9Qh1L+no9hYmEM3JYPuD7eg0IBd68KYGP7abAyS/ThBvDXUnZ5ZCSLQb/HgDiZs8iRrO0Wa8yANR8QDWsiEZtYcnj4/jP26KKzp93sKcGBI+Z2l5P3Gd/wDR+PSk51f9KtEHcAO975lQUEcxWTc/Zsl6Av7gK56IMamKj6P0YZINMFPnJCKGrvP8ZMrqugeBpqvy/CaaQLloVlOiAHax68ae9xXaEORN3NWrPEqLKMY/e9Vg8qonmTKyyi9BUSXjk/0k2mJD+rQE5yudqLer6qbT0aft8kWVx/dRAaY04F9E0PWWNvte6Njj0lSH9+dI4Gyr+rRkcqvxB5CAcq+eylLshC8BPnqjLgOLjy3k70EMDlOEJ5OE26/ZjP3ppCid80AQZrL1dVP649U1iFzxhimSpjk74LUmnG8F2kx/hEWZSR4iFGW8bs5a+8bKc3Gnvoqstm9t/A5vyX5L2wlRxutvBVj/gOU91xl9bq5TBkVsO0dK4VhqCv4moda49jNJXGPZdKeWfalDL8lahpMlLeuTxC2j0m1bdgnWAGzAmpY1p9I3NulcWqbQZuY6oy9h/Q44ars8mF+bypN9mCJ8sZxKdmnV1kgaczlZacdkkAg9NpeXW5hYEDq8YlzDFEKBefArkdfVnVZ6GmJrqeZrzR3n/ar+wMR0he1LaBbZ9iM3V9S5bfWFRqaMoSAYNXwouZxmulhbcPUXE6Edprm8rpZevjN0sIKGABu8tBL8/G8kgdJS01zb7UtoFN32Izcv1bltBQqjaE2htBt1SKzTeQkcfyFzECzyulrzFiUVRM9omEmnEP5sI3sUnBdtY0kUsiiA0agFPMmq7US250tEjC1EIzyJca2rbZrAMSuQ4n3sFD2rq5qmW5/RZouKSzpaPedFzx0ZCX8zs1l0JAOU/ZJsZHbiFnDT3oglqbI/17IyfI8kWaWrsiy/15JMtdDrosjhjSGTzTgikvcljiB6/kX9qJovzN+rqOm916YjheVFDuPRPW27ikZ3rJ2iN5P0ZGxH4Na2UeuIY5NsWNvYtJvFG3VaL9QP2VOaZNE6wDNR50ULFkyN/4QmSFqAFtdyC4Sl9Q8A7+4tti6aK29Vuk4AUrOi8f0NagF/QuLPheJvqaJ0VBRZjJ2H162yIj71CKglyTQ/nVblb9+1OGgUHtcIDiKNFdJ6VDXN6nKJGtYkKprW5vDXK7+QNxa89EoMcdAr5XUF0Qa/UG9hTBe1sL0xmfXOHovc02okgdnBvnC6CQ7+nSab4GDdK5qzAO8wBxV8F+hGfE3wEBjHURKlMfDclhWjXwabyoA2kmRQOrKJvF1Ebm2qfJzYUGYOf+IU2CHIpDcBj5taNx6Xpyoaj5rOh+gg/+R1BdFBrvIofkAUn8gfm9ylDbu7IzwDXL28P4AEPoL8xbH45LqPsXMdO+klvne2Ln9yR1A6x3UvA/gGDjJInQCHubwiMtGQIYTSweiun5GETkE86USH6NPYy7WTIeQ8kM1X8Wxx3K8gdkwvwR/4LUZ3A8EA5jAPFPMgRNyvYJMg1HYpzOHTJYiKLD3L8pqbpjeDNPwLiNk7yAGBXwPUd5zMhra5qDp+eJdVGd3efsmmiCRztN7AVH8D98+/BHloYyDbwoR/W9BNONcdNUUTyV5alEOw7xRm89xsZkQ5O0GLkJ8/BYt0WNlU8z/AynYR5Y1+Y3lCWJ/nORSkhziEh2UwC2QYt5B5IiGhKQlyxEPYLWI0i2UYpWMXRHHP3JLdxVBKXdP56T2GNJtgpyHPG/SOMVeRaC2RZtK329kiqzrf5WoRHPPK8uibHOTQtwFZbdFcu23e1OtkgcuJVFRGSXb3Rol7WB5Hl+diwn/dDuN1OK4VOMbUGUxQxwK8hY74+ulr2nT7gjpaco9V5RzY3mmWK1ldNOWNCvCPDBuVsrbNKvjEobdqOFMSqFAs6aKE8ts9KLvqLDhVsAe5aSvwRn6UaVw3mzEkmJhMkvfS557q7PlOAKnhJxfFWp6yXgkOpQDaF+d1QweMEYRfcLF3EHg/g2hSca/kvHj6KcedNXtMW5+d2lV0578tQyCHqeU8tf4c6PEv3VqtXJWv+6EsBMstnSxbRwd5QvDm9yiHUdpHVT/JNjcwjTw1Yj34gZflda0eqgCsrLl7X0LVIWiYSj5HaXUbxXgQ8ws02lqvszDVWtqmNbebSENCof2A63BgH8Nem4oKQChn641sCIt/DKGNDnGOigLepWgWQcJH52vVGfS7MMfCrZcgtu1bnjm1Jxf4EqztTbiCXMnzNqbt5PESEpnxQ5hjH2Lr/FqVX28JaH3ea01UeVA1ijlUq71i8bseYnCagGlRmWHNuHzIkxODuuWGVYNSLp11NbHSs9i1s0MMqx7TRV26PSgf4qyJrSy8anjQB0cX6A1MMEPrFXgu0afNdvwo9jg0zx8VzP0vq1PzJS3zLAnhFzLT6RU7hSzEq61MdRakLM+59EdoDzItY9tDtbHVoKssMInzqDQLCEtcSyWvlgbK8jLDgh1ICMtDwMXksIoYrCJHCYw0e6cAknomE/dooV98NaUmzGtodYnAHrhdXpeEbv5+NDs0iTTekVmS5as/qigHxeXdzeidqm1L63+imUA79wf3Ha2vm05Q0XmBkNBCDuIAQZQ8dEgGiYiGEGrtJ5g+YEffOWPY2Wpby9GzDPVOM2XNaUVqA3I5rD6fwRpGb1qAwzqjmLcNjbwDTn7rI6h9yxNvccJDjR6d7himUf82ZPuXSXW6hSot0WBg2NH7cgYTkE6x+p4+oz4V4fVV0TL1BTzZ8ZOxiLmCIG9O25w13A7jIGbkdQVahYNc5GrvXeB1zfF0zNeQGfTukfZ2C+6oULHoWPd6mLVXHcQ5OOVAki3k8UxfhcBMyCUqWqgyBZo7I2JGdAnOgb8U9X8HeSGvayJ3KFaHUWobI1W6KpLTFOf33oOtQIx4JUwnNJ4bWaG8th/Ij+FTdpd1fhOznXGwfS83yXG2fpni1eVac2yPvL+A8inLHwKPrp3M+xyRG4Geoq9BOUhAeV01+X3ZvkaZR5o6KGvmivs92ABibrhJXNZhUr54M4A5cKO8LkKo0ZeVQHxqYF9U1PI+zNo1JvcfR/HDeUq8w1bVDXV05LyJlSAepkTQAR5tXxwqssccEXAlrCeMtqDLy237tAVCblFllQk2rJqsxv0Ispml3dyvwAbpyV5ODSK4gyCR1zWPdvYdgifU1Pvpa3bdihnzM2JEcJflLwF4mYU68PGBjyfj40a4B2BjBunAxQcunoyLyVDA2MtdMttssvQNi3RgY3ldYcxHqEe/51m1nZ4tUc0zzEKNaWAk788xTQVHcXIJ4szpNXVynoDg3nQghxk39oyb02CrPBi3di48XyddHwK7bXzLfZg6e9OVP/DzPvPzKqnupq811DXoT1F6V0V3Yy0Mq/gerKsEXEXFg8sBCipWvKFBDnNJXtc8t0rQ+p2la8+n4gK5LVqLjTAeEWW2/Zqe5nnm/Wzkp4hE2M9LhwcPcNnTdO1YclXFMSgK13rpzody1TBXP29vYQLdjBdd2aL/eRAy8rqO1usc8Ym3pwxIQIALIY6LoPzx6Lp3woOXjj2uu1z9UQuXyB2u8Dn8jlNAVCD+a5+Ns+b7DzAHMX7l8M0A6cD68ro066vpBTDb1eWlKMEG0eGJbEWmrv4Sk87Qjfivy7MLNsz9Aa1jzUUC/5nSgR0my2Gy7NNkOanyHKSxy4Pd/SRpQQ6Tw3ly/GUkw1s9MCcIQlX5SBMDjD0vPmUYwJ+sJubLsyzfoEGnXokYq75PcAMRi11ltWezr8JMJlhxv6Sr2At6gIjtI5FtaE+NRjb4RQRjqYx4rdp4emASjOINB3UQ0vK6aDp5X1wO5TtJRtA7Jlm2hfEuenFy/Cv035Tn4jbliqyeEQHhI6LFZ7TNJw//uE7YIc5htipmawhHacxDoWbqVRYKCfHnP0GsfOh1HI0Rxy6c3iqPwxhG3vrJedGYGI9fjqry3t80TwFeIr11C6moOa6YI8lQmo0lsV5osXI9zE+HdJFmE0Rukee1dX/vp45J6+nc0rb3mXQtp3J6LQHENI0+fcpc7jGiUndovr2hUA7SX14XRSZc5WJuumif6B5Jho4kWBgyi49J+jzXXH7qwESejT86UeQN+hgrXZFIqIjSla0NLkpqMoYQJzj/QaTI65rZlKwxEb4fp1YnjwiLKzObbQTvXAwYzepcvGkxDqzrzDtjOQJptykjVYzjZOzaEhvSyms8/07RVEqO4rh+XsZ9DtI4h3kor4vQSSXE/zLKYUtjmtetIIaVq+vCMXqm7+FFlrPvI9meExRoJMMQyLbtUVFoVIqRaq4X91XhHTcTR+EEtxGa1UgIkrmASo0ksxqh43E9sRNbDNRBcsnrmkf7PI7jemmpHeSA6szNtAm+OkugIGOBdJRpLgXQ67t/fOwpNJsv4Kn4BDCTex6UdsJCjHiQGQqZIaSY98npTEpUmJkUdJMw5g3Ov1egAuuG1u6zhoI5TBV5XRc5zHLvCL/YLql0Pxplkca1av3XQiyZV9n0nbvKJunaJdgmL0H6Z1TPJH06ORm9CqQfjl7HfmmEYdcf/L5ADoHnXT8MI2nQiGqp9EBoaBQTHApRa9r1MHN/MCTLwx0OSTN6HRCd3IP4Iau8XourXwB+w0Ed1nF5XRrBOtLd7518UnSFJtQW57+oo0SFwj0vrqLn02dAUcMVCgG1kdiCPQu+jAdUQ9kJ5KfqrNRQPOgpznstkGG9fDUtwz3caVzQ67FOcS0BhfDhJUOdosNSzHe2zSPaD8//jSQBzSXX9DKL1RGthZ2TzLrEcbcwX19EL5gFPsIC399zF1oSwIPUktfFksxXaJGRWOjFDravwnkm4aFrvnA/z0zLcPPMuGDQeRZugh1mloKJX+IE1Euz52zAQBcgh9na/0ZIGSUEz9eCgiME4SkZwqoT6tAwUESX8xRid4IlSzK6iUZS7HpYQi66Bhm18mqY29YRWrr+Ty2W2S2btTx3Esvktjj8F3m6mZDFyauaYJDMbzjAg3iW1xUm7KNb8EIBDokcMF8Y4q7+uaqeKiSxVBi1IymUPtzMuu6z9/JGnosTpIqsnqpeQmZD+xKLcyT9IdBBkMjrumifNnrnJwI6nPfzGGnNTY15VhRoWiT+XMZCHfhsqXxmodX4XDY9XDLVrJV3n8AjSLzv899nedmFhpj63soZyh6ucs1mbtuFyhzZkVd6Y1fnjH+neco4DCEQk4M8B/kUdU0YT0nnKQb+qOp4cl67+Pp94o9RoX64xoV0bteEZdeDldeCw10HXoEm8pjDEzfpLbyr8nrH2uAcRL68rnnuQezhCzGUb4XTOwDPbyiEA8c6c+yuvu9FQpa5cA4uV9T/HdhGMfdJtAMN8wRSCtM4qdbgPF1BVGXU2dvcPcDaK5UooQziaGBwRzMMLa5gqQxZGqaWSVyNP4MyaqJlqNT9cJV9ANRFrwnqm2asAsd7vQQpEslHxQ+4rg9ZvNBqlP/K0vHpUFf1I4+2jf7Ptd2kPNpqPw3Gza/nxxle4ALIlyD3f0jHriJVCGKXsZCtyUdFkcWQrDZDY9YFJM9wXzf/M8vjabp+1QZaE2XvN0p1J+oIb3RO1CO0KsMtWodRs357/T84wmjr6M4YqTrE4O9es4v517Q+2n6Fz8mxQ9xJVMTRmh8eRLf18AvZ9tf34xEXF0ijgGnJKwswjeE2Soy6wJQ2VDZw67p62JQPYIulRFoajZNJA5oi4nZ01THU0xHr17cUB9oxZmMqN2Wa5rn7URmzqUPEmK1hf+mMOezCDIw5HCcjxmzN7zMxZruD1cpKNqOIGfsNtTkbcrjmkvGXN2/ecchOjCRrxAQsJKPrLkg1tMmu74odbdGgYB2x6Q2+kicbcFUhEVO1+W2YSlmHgMEMGdeLNM1MtyFMW2Q8snQ1mIv9ADRpfzRXrrDvlAFZRKVsKBN0vTJtn4Cugnz6QXQSa0akm0DGGZHIpB1t+bn1uGvEN/AR5C/k1EmjYQ0yK3Q4B+VtCC3iNbqdoy2fqtZMp4UJ6WzEVVS5uTmrUwe6JUnDAlwBBYe56GfSaiyW0cXsFKS9mI5LpeNl0gT6NvAiuNRwI8vmH5lHd3gvK+vDDAy6g7vZz1Fa3UZkU5Rrd7SizCLGpPPZcKUQf47draohE/CVis67sMttmHrQjQFPaMSStKBCCrrynLZKAf+p6lqMXNT2aDoBqR1Pk6bQ5RbF2YYruqjMRPy8w6u7qh8zMfAOrvIkcg9M7z6D8j5bX18ChAaxl8UJtg0ojY/6oiImHpay4V+D+kT7KJIY0Bq5KpFSh4btETEKuG7hpY0W5RbShc5oRRZhDeaUmH9CK3swwVRWjpHRVrIuMtskbvzwtczI5BOxYefSb86ALKo564VTyyVtmIB5JDTdJbYZih7dMA9nS1gWGmILGEkpJcOzk7A9EzKVkNYm9Q8Kzncg3t6I6GP+yziAzyo8FO/uZVgcivPIAsYyY1qXvh/DJEFaS1uBtpnD/MGpwMCbk4KfXQ7UaNz9+gs2uvayBVT06HwJ7cnCVSNaxCRkn1+B0vViAomlGy+j9ZC65jeLvKrjJdSelmh7QVrehyOQ8ZCylIhfRQVsmFZdoYBz+4gKS+Nco65MwL5GY2jSDjpqySw8fJxkdxdZUer5lssp4tU2kw1/8sA7xZPS5k/Ah9Ix2Rnea15Fvu4YR8UlbGYZB3ZPLdsxIQcv4EMZgy+DD2U9mIgVZeNjUn1bZm6L/CV4hODpI0i2t1WSYp13kKAzzmuKK+z0XEkHY72udrndvund4rjarmPTGfINx9nCpl8XnI398Ytd5yXY6BUBLqeIqdtMNkzMA++UIiBt/gRcKR2TnVAEcOvbtaNjHBWXsJllHOigCAjhBXwoY/Bl8KGsBxOxomx8TKpvy8ynCGRJopeCg1zCZR1lsFrCB4A7JfmETZ9iLRaNwU5IPNzyo7R4wl4AGRdFYsgUVD4Zn9VZbLmNBhZphgIGXgavCRo+EbcJxsJIxUP5Z+W171lJTFVxlq+vKY5RsYekjIwH6ey2nCirSsKVYnZfBm9qujIRn2rGzpRn61LzcS7IN7DAQXmanrQmf9xR+dmYupiQf5kSVvyrrk1xMoQzjHCCaEYkcfbg54czEeP0GYc8ipKjqrzHvBiTRGaApLQxKi0ilaqgDfnMGrBTh45WXZpASlqNsUl7Zj+OPMvyakPMrloG57OKuLnLZcO6AmgrPg3ikCNvxAScJSfu7rARiZFnyEfDvFJGaqL2WXISAz4TK4lbMRUviQm8O8x0Tf6t39NQDjaVUcpG1hxEgwrYh2rb4tZMWfunYjzBeJhU3ZdaghCrucZAyNRdHkN81cgy5lso3wmaPq3AG4yHMd8tQP2i+EWvJVEdDq+CUeAy7hPy9UJYUNKHSXU4fnxMqicFZmPF+hE4Q09iUWYRKzbxnM3ZUAhs7kAcSH9TtWICRlJR16T6YcmZOUq7HxhmC8hFu2nzELd9Mq7byR1DG2fqG36Upn3usYsrJuMSZSlVpDK6gEs8N3GFitBjy+NSo65MwLRGY2jSjtlDlQl7Uks+K35inn4dmXuHT8fqZPZC+XbQibmYdjBuJo3oXjieb3EnbjpqHmXySZd3W78/Fnd3WFDS8qlWeH4sdonZdOFOuJwjMNwuBjSRtn5SttvB4CW/w9vyJMIOEajJ91EB1j9ged9zkIxdNOVEbNkWseFKXTUysSji/nB32w1bNQHvGQ6DEScKEWZnzIES0bGQjl+EpVRM6ao1qisUsKdsFswvQ426MiFPK8fQpB1tmWXx8Dd6itkx8qDoZNw8rHV3FFHzzszF1MLxNGnMoOC8auuXrAQme6Q+n1RlxVmsVVYKd3dYU9LyqZRVfiyWv0e6BE9o+lxkCKBo54/W9q4qJGJDQX4bhlRWt1NWepOeTMCtJuO3ExZ8UUfMFAFtSWOxJ9z/2FTkNmG8gh7hgJXk1pyaSMNsspiZttceGdTdWV7EDZ9gvorHYfmLS9vues/csoqOLwa5VUxna4sTVyCK5Sfh6uWwoLALE3KicIxM6u8A5nUvaCP2mnmsMLkDOhuwyD5B77y0ZllLptKdZTQ25antAjxXLkFZ5Sl+9QqY3IwQZxerA1ROO81ZWMWO6cyqPkyiLavGaSf05L7RhnJPViD4pb2QAtBKM8lItSdRXq9lx1W6ToDybEZRRqylDLPbaSryquTnh1QfRlssDFo2iRqiHQqTVvSlZlSRmZ5olw1pidGZcDfXD2035uDXnVxFuF7o3CpkBUbn1F30tdB1Yg423UHPi2FsPB2HCnNrIyc6h0vcScZU9mACrlSO0Q6x5GoLYnjb3P4/Kssc3lQlMGVRdWkFy4oLOrCwpgU7yNJmPZqOxc3GeBdYXtyTr1v857WEI6Wruz2WUMFQwFgpGw7NEdmYjabl/FPFo7tTaCzuvGFkWRQiLHQhUTK4m2xXk3e2JUfZLOO5Jp768884/77PvmKZ8I37/Ktx5p6F7bJ8Fd0p4sPxeRXzxmGGDJDlKhhKDhfxrMH8HuUwSstuWE6yzQ1MScZrNGPgI8hfruBGEdfWDkdBOAWEA02NGyS6e0l3fDQrrGNDp5MLtmNq0jK63NyTX9U/ww2dAcQiOH6H93cW3VrG1NjBnZ5Br/5eoRTEGd9S6DUraJxFTI1BgwTzY9DxORcDUUOXwfGiMTVpGV1uabzvugI4iP0ADL1PAn45Un2PRHnbFb2lzhRgHgZXmuO4Pu4ay89hfbMdb4tJsBhjG9u571FSdT3kuNWSNZVgFrOE4ASYKur2yKeNbtYudvYYdXi+mWTEHxazioWYe3JZbQ1M9X8XK9qiNPmZ1fW90MmvV9Fmm4AP2VOaZJE8soE4e2DuYsBFZsO2naPzlrgtE3KXmNJGpwGDkrNx2BUEOep1DAYvjkg5TJxdxGFdThsek8BbPn9C+GzuNVrdlQlYVD1UJg2gyy2AQXWGCC7nCGy5i+YFaesnZcLdNSFQ7vj1T0OLmKycYj2WX3owWJml9e0Ssxr2ZbolXjeKJi0ZFFwQO9sz8nQs7MK8IXVLeVNm4b19EJ/dTv0MJiWO7q6/pmaBYcSTTHEvFlU0xeVa2wJlr76Hc8wFPQuYy+RZr8sdR/HDeboqs/hhVd0UcQ5rnzLtrTldQdFUkJSxmQHaanfqKp1pbyZgcdPxtNm0LY6rdcqGptzEPL2LurNhX2Zk6B3UYo5ub2GCvoBrXZgBLqeIZbtMNkzKI0/+HJC0CRNwk5SwJnXPHUeFvINlt7zLi0ifOnMVf4qqZnoydO61WE/8nViFUX8f0Yz5jFg/ugPXZ3m20fKdoox4c0Vnt9tKySuanu0MWjPJNkdLfJNW0OWWwnxXmS3r9SVGZTyqmtnZjm/L9EzHk92kDX2p+ZQ0pGA/osH5lN1dU7/xYMrVNXkZoeLWZ7FS3RS1iJQ4pvGL22MY9GcKjVA/dka64bDoIthXKypFmUdi2N007Kh6MDFv7qTq+PcKVGB9uolgck3+PYrVj0jKCoi4ksprw5XSKgScOWjz4rhT15MJOFQ3XiZNoMvNFwnyHsQPWcW6mHKf5ZEhDQGEkSKFZa3iRprWLhK/XB8Xx+m23ZuA823H20hes4VnDNkbVzkixd1F9EJCWZ+nEOPqHg9QlBIH8B0WsIvhq6rMPHp+kB2YUWMm4EqjETBqB1VuMVzYvhPBsY0pj8gATHjT6YEWw+oF3KqbGvMLZdvezcD+uvE2aRJbdrbZ8CmLowT+C6zbNl5/itK7CtutZAwoLyLieC63Da8rqhJwd9fyxXG1vh8T8LF+3Ewa0ZaZkWMNjBA644Ol0cHV2LAIT/+Z7Aw7Yl84JbeoUJkSlQB5e/tlE+Xl15t/grjESeAZjXdMGhalaVYSlL99K8BJkmM2KH57XeYVLzIx9AqUgitbxetXdbL03hvHjEMwnduZqAK9u6FtpUa1mMIqwLQQH2BB9voijDZNC3ISleAuyyEQoTSJL/rewLisciFGk2RKEBXSIIcxefDDGjAHWOVQUYrKpoemIkUJMQdBusw6rhyIYR6D8ajzf47S6jYi5FJNDjqbFlqHaQXWCj4h83ULjAakcScREq7zwNE1BLOAbBaQND3R26dGPoPyPluLkIY59IBI6OIrCo9o+RG2bJDBmNhEuosnA5NFC3mcZHcXWSHEatO0IK16JQLp9VsdCNb0wKrEG4xLUJC1VwgoyKcF/wKeCpnob9P0czJLkqO0eJJMxS7VCEgGYcwEWGFRcUB9iVPXEpBvYFEgxQBt7rJcyPNsHi3o6TNaltMoOarKe6zd1ME55RWo8msrI65FMg7uEs1grrItjKU4JNUEKJIhmLXi9zyrttJWkFQtELFWiTAa05/hyks9mi2WrIKMejEreJJbjC5+Vd0QXQVoRj+ZwOgSzWDwo89SmPrpbQ0M/9irmF7CR2EN1jyZZtW/cmkIIqPY8D1ObW+p19TE3Rw8dqdt2/DJEnH72KdlzNQzcfRhhaImi3ut64MiyrGwP8pw1k6VmVdjSjwSFllKKRKP2AxIET1QUYEyCKRbxRa1mVZhgm0NKgwiZNH45lBWt4g0YTiE60YXzUQD0t11F6FQcQe0ingm3pKSBLPinyMiI6QoTbq+S/doj0pIeiPW4AYZ9Bq8+FqIUKGXXe2x2DFegc02icT8KMpnar94UQGzeYxFjAKTyaLXK0AKEK8pJySbR7/zjRO5XtwlamG+5QqYLlHP5vE9WFcJuIqKByGbU+n6rrUXTIRd62/s6HgPRAXipx8A3t0LtYFBBlO4DxCpI4VkmrB5DLZkeQ7SWGJ8qBP1mhh3SUK6B7CavEN3ZPFMGLp+a3m288UTcy3lImkORSzSarjaeVcrTjbbCN4Jidem6XewlCOWcMc68IbTshyhq1JwDrMYmTM+gRJtm3X8Is6prYDyZhMb/SmHQ92AsK48wpHhPa9sYaVKjMynTLsNEZ2gyzZeEg8JyzpMsM0MadSRqbjJgiNubWMxb64V5x7DHPohzLOiQAUTBSSbx6D3QqlkIo3Qr1Km4NVJet0uelYeA/TpenpLrVFiQxR1VCg7k7nuTl6ozMKzmS4ne5bJhe5gCnTn1l1vBAdD3AmpFrU9vqZRJXCDQ1OU04FKLavpqSS6fq/sD3PvPhCVmHv1PGoIKrWMq+IiLo+8D2xWEU3ouaQgBwc1Lru0Rs/ro+02gWB9ldHt5ImizC/vlaqYiFjUabKCVkpUAd3kQxCEcj1/mtCty23Tv7ZQSJp1mONOua5i8VG4gmTCAgY9FJULQDghrIB24n6GEvDDR8fk4l3xOJlIDIvfIDPgCD2YiERD14VQtOlkaK+fSOnD59V2iyuioJOh1Jcijy3F2Ir1+gKX1bxXeo3BkVzTKA20SVClOAjzyTsjyi4iEeProiCPEHEStXNQ89D5RspQ8jJaBpAWVbCYORm1lQhIqkQPRFf9HBVmt+uofq76EXKaKTv0cbq+BEWZI6YHa8rFiqeiQSl5Z/WFRTTl3LUU5DSoQbRs9D32Jyvt6nXduaQJKCnMqOiaKL+QXow3mopcQsyRKdQGolLQhs0i7wGTU0QPyu1QQQoWaCIiMJ6BclIMM+r7MRxab7IM4QTEUXOdix2i9c+nGiqwRPC5FAYELrPQGtH7uqqMETyWgCpSIvsQ5BgmCZJxncusgipMVoPuDEsEoA8DOBGRGsfY/jaOgkpcXn2v2CIqOvVuvAbk4oBFckg2AA70ErnZ9tfsBERTF5B3UFlORD6Jo7CChuoqBISkHJq9Cdl6USuJx2eS94bLKyIS5detIAwPNQExTrINuXnau5eL6cHlU/eDze4984SgAvpISe2+dbkEjxA8fQTJ9rZKUiwdBwmKXYyupHbDoQHwpqphNQrpJqvGgdyt679ybvKZ5L3j8oroRV1GUBCKhxp5buIKW37v70SI6cHlU/eDze7NRUJQAX2kpHaZm1mSKBllmEExBeh8QmNBfT9ENYsGECMzRn+z5bq+oSLuOp1F3XIqp6z73T0bDRFoKJFZRERJRxJ8z0qiP2BXumv6to+YHLLs6v5ISvmLXQW2hG6SIXChHnOD6Jq5wyQgoLqEop/KgkJu469AqeioxlcsW01fw+32tESU5DTYmmmJZrnVm5xIqitlyq2fWUF5l43KiyiquTSnoLJZlSNvF7ubdkrqCnLJ+8VnFtGNvv+nIJIAbAqKEFctPUmYbJpuDHNLidL6j+mowsBNQZZr+iqjhCZ0Hk0PqKwSakR6MtAgAhoMrmaGZI7mZqiSM+o8RuNY9yUMT9RYYmJwFPUSGvQFW5XYoPKZzHWqLyFEBwUn4xAxgR1oU8ep01t0hfnknRFlF9GmvUCsoIsQamQrbl2nSp4yOXTNV0lRYxpMJTtFt62v+0AxcncwcQF5t5TlVO5g7JVuA98wcR0K37DRKNncjTckoyAAqL5/w/ifIxBwGPNTy8yuU5DYW6T0YrNo5k+fUzoL9eYyFmkaEijcSvhMJo1XuJBYE2Iad5E2psL1BWrxfVSA9Q9Y3lMxEnjS6IrIO6cpKSIbFQ9CQTUdsIydQhkXRaEurvtwFXIaigvoOyosp6KfhWRS1yEgpXSMQlHyGz2gxuQclrLs76DwmIQdVjS63MMRSzSin8qikVN9TqnEa8Kn6CQehTQmCQQhVpTqqDK/vEuqYiJKSSLEKIimrGFkPVZUt3aG6gvZddZx2ljiuo6Vo98mORCTkpDJIe/XMKPM91J4O0OFM+bEpEMMXfeBi+REGGbU92GQX0USvYomhhT5zsmo7Lx1bv1gtRYFNqNuD8zk99pPs1gjGxUG8aQ00lyYUyUgRAXEUmEY9UopdYSgIwvtvvF6FpLmNTgrMmAky9OnidmJDSZ2TQcVFskjeXaVCJGWEssmLgiaUj7JweWbyUGA5fBUVM1LeWaLTqpmpyf9ppqjXMUK+4Q0r0W/FNYKT4pNY7sY+pzp7wgxGRVuAqL8YX3npiWRONSfAck0BbWdVZdXXLWShjHUE1hT58jXrxSxG69lERcF09sBRjE/7dGEQkEdx1IlIBwaIFKozZgi+DxRB+S0nTxKNFfuVlN4wmmmbIjxmEqYyiN4wCBsqXTIBtm0JKBze0cOoLHkMqpufyiqKKKomkZbMIbQUsAUSUFpdSxZ/SAYN2G6yA6qJunXb5PSXlTRr+RjjsmkSpSqIcN3dJyGYwDhRRUaaaaBGTRBMDrMu0RjDZHDDHGfFg5zwYnO83J9W7tSfTUu69x5paI6BrWViimbZ0T618/zShItxkKNY00mJZzFGLVxQO0HSt0C+aBpucRD6TRcFixlv6mAN9M7ZxLV16tos01AH0peTh0mp75LwwLeFGLgRLpfHyzfmz5dWPzBvQsRfSQ55R0SFxDRhw7cr6CQBHCCKyJ9zYolns9k0hXFMm5NlkmXasrOX//Uaz/SItpZISupmG2KIwarGqYmphUZXQg4JulmIhrzzKfmFM2muEXn5ShGpOafN7WhvKLyqU7hJC+GKA/jtGXkJNAVFUa6kL5/oortoKto5IM6Wf0KQaErYt9ZhdAIStRpBEj3pMi1wjGBzyTvDpdXGCyLeuVEFS2LwxrZCYF7J0R/CdF2ssoLSS9bmfOSAnzkqTl8DeX6LM82KtKpsqtkvbSUeGFh3nBRLiNy6GlJd5VZEI7KbNy3vkxgolHAI5OMet7mmnvqRiC/FNkV0kdeSijTBg/8qKSaAlck37jXfoKST8VswnxmHVOxmBOlpmIt6pGe6+GDRTx9pHnlXZIVEdFp+JyQgk5SUAGtmEea/B0che8EXQueJhI4PJqWVbgrGkIIvYhkjyGp3CNN6xOxqeBNpwBuuMOHjq7PU1jCKFHcCFAVkPdcWU7sm8s906R0z1XBj3s5RfgM1TX/hJSemNKy5h2XQZiQ2PQSi2GNAqprB9U1aCb9qJY+YqY4t7zL8kLSWJnsW2AKgirQRw4yplvBDVdu3YqtXanHWKF/fVsDYCfPCKYg79J+fYvf1NxEzYdf36IsMdiWVZR8ztYgKdqE9vHZvmTz5dVqG8VYnP/P1etXz5skLX57fV+W27+9fVsQ6OLNBsZ5VmS35Zs427yN1tnb97/88u9v3717u6kx3saDffCvTGu7mtDcQqPNpOLw12twBvOi/BCV0U1UIIqfrDdcttUmysuvN/8EcUm8XZ8ZWfJrR+W2wlaPoY+P+JHDaVifbEvh340rJa4Rtxq8wU17ozok6yl6hjqJBQLpL5CcY/HFEcAKTZ4ob6dO0/zzNSJHllSbtP+b5T95afzvsHz9xRzhA6CefaSBBgkWeLDYJtFLHVOXQRwmWWM2K70AskkxRzwvPoDbqEpKhvj9Zx7r17cMB7C89pZjNkYCsBxsxN96q7EPz+vQHeaBPeQ4c6NvAIszTDFH7DrCAg4SHPCIbi0F7VJt+PsiB2hhBPgNC5bHB0nL5fPAjB2Ck6di3aZiFoT6bDsNJIhcojluu9wNAfuvVuK9sTwwol34gLIJ0gVAih3SLO74FYhLn3KxXd1neSldcflUc+RatNIbbhqZTzVH/gjX4Oq+2tyk5DlnGpZJMsf8Dgt4w6oH3UeLeVLdJLC4Z0Uc9XlOJeMkB/i946/ptzIeIg5TzBG/bdcSxGHK0oR6EFHuLsAnFtu4JxLR3SZZYEY56hmJLNk9nc2BS/JYT8jzdA0f4RptL5MX4eQcZtgT0VkliRSYS7TQ7tYbmDZR6BnNbpBiz19gs8Uvqct4jEq2ou/T1/RjtgEX3LLJppmjfgZl9B/g5amOUkxjDlPsEKWjxSXa4V7Bkl2UqM8WI58k2VPnkEiu4zK9F+ewqGG7zbNHsL6McMz2FY4QO4Dnky3malZq4MU57Ft/lZVRIqaPMIdTH+SVSDNZzJqKGKyusqOY0Y2GKeaIn+AGlrhBZC1jWswlWrT0oWIaiD9YzA/qpUm03pRfkNrHKkqyPOa1/I64aYhZf7HZ+fZh8Ybb3v67RXuaMqIlnU0zR22er/5a3mM33tbFicYW57CuoVcGxPiDdIv5WyG5FeFQ8kgNOFqvGTR2LmtzW9ntOn9+xnDXfbdQ+Zsy7MjS3y2U8zSppyflrD9Q0QXpNvPvuZ5OX28l+OIc9tQ4fd7CnBxTfIheCjFl2Dz2tRCXB4IgmlvyXBYb16hgr4AMNq98soU0HZRkW8+nWrX6W4H26nc5ALzmyKda8Cdd8AqfrwzZk0+2mZfdeSk7MakEG/nVFDp5iRPwCaR35T0rwUQ5XGu4ADnMuHGU5XGohSgYBIaTxKIcNpTH8atO0wjtzjihOEiywTxDfNDGxWJBh2k2+6E1xDM5StrSJ/f4AJXdG8ly2bT/Kno+fUb7IO58h0qwwTstONqST+YYqOITtCm7y/IXls2YJCudDO3HztNHNGNR4frxahZdmslC2mGfbbG9l0myNreR8kePEUyiG5hw8PJcbjWJOyHOYTEOMFWg86kWu4Hsqe574yDIDq4o3WqXBG9fiDXiLMvb9h0DtDfldkryjBZHEVH8kGErKnZe4I4i2ETL/bb8TgK38Ta/viCvk1iD0dDCTbURj7s4h20N0bOuBjaHJd3AuikK2aVJkGw16/C6c1y9HFdlyTkXcKnWyD9gcZ/AolTAs1ksKFPLmgQgdr9A+zD+UECcw+JwAO2GSFEYMyvgMMXGOshBWWN8TdYCmP6rta3yhLzgK7BS1gkWaxAO4xQlgtYNU9wQV2WUY/8kgIPrcIcr6pxuNZ6ma6P62HwWHNaYF09T7CUiYjRRBqtVDwueAQoQsY8yo9UO2LA+VUarnRh9HZvZhqluassx0XoNCmFEG0E/tJmt+tK9j3O03SaQ3SsIM5jj/wDw7p6Z6O03C+oI9nn2O7sfcM2CNJ8s6CXoz0fr/nRrhFq8KLI51KUSLNJMcx6TBz3EBwngPJ66j7t5dK/oaxcPjrOfMWn2bj3Yi5VFZdNsNP8CNMuZwFDBpzogfwZRUeWgjoQjRB/kcKjhaMN7KXGJDrj4hxKbzmDr+EUOL3lrH5do7VBWP24kNhJJsjjWgWmgrqLL4dyLPr60sid0tsU42fQv3/l42cje/TNws5EXHcfPBv/r6fXetFh49sCkWVnYZZ6HTNJSXBrb0iLxw6bZ2AsbPYbf2QxSzBEbfYXDo79bn44WJ1m1zVJ8P0V4OjpIt9AdJKhuaO0okKN/ovzL+HWYw9LjoD5jqyPLch4HdOJipF5jMofCJ+vNxV5reXcQe/KiSxV72JuNR+m/2iifYa8NHWcl2qxLUQXJNmZOGHGmTfLJxlxXj7XM906UfnCTU2PVbquyUzE+1QIZEickDrL/bNPKO7CC/2Itq91XR7fA4iqrL+GI8XV57dv/VXQcwiVampsvo/SOXTIGCXM7oI5t+NodB8Almln2+86FnHrkjul3CJ4+c1oil7gYhauRnp53OWoQl7scspLjKFtNdccwjfIX4TLSJtmcmGxAHbppeDzSfrUQOyA7gwlIOV1ukGDj4fIFMGf+zaflsF99bhiGCwdY7heLpubJgNdCQ6pI7mJ8ZotZs9Gv47uEMJ5RgB52NCXKOKzVVi0zLrihUR25rBKweilKsOE3oAbZrQ7ZxfasQYLFzhQmCbb54nL8LoVPtdF4GwdHMbQg2cbEVZRIDye6Ih0anXdxledzqe1CeuFAlMOphjoeU+3HKK+FyeVSUzsAwp2TIpvbKBFhoBqeLoOF2fS5zCMsoBiraf95OSJ58LqVlyxWPApmIoSVxZdq2UNls/wjeCaxQli78yDJellfSCifedXOQNbmIZqL0VmLsHzVU2bxc7P1nRdnaGNf9RfKuPsKbPL+cyt9ETMIw9KA7kyrRlk+49LtF1yzGKQdGNiJgcNxrifLzsGr/mpA6GM5mgqyoy9ZnsPxlxrrcEi1e4dUh8OZfTmcmctrpZktJMStr9WxUQjquCXuapms/PIVMoEothbDde95D1n6uwUaiYfDIDXfbK4fJNvbKvkHKMitcPYeApNojfslk8N2aeGnjIa7zwv8++vtf+NctIZz5r+7uGoNIabi9bZabqdLfbfYKGy7p6sGTeo/22w62tBH7G6j//5zCnZLGXycZHdtKDd7vlSWHokn6+p4gTdIsDgfQV24yAo+tCz1ffZRGqx0jaRLBe+72S6aSqhR18+6fskq2iea4/6IiqY3Q0j6++zj+AU8Fe6zTVl63NnGKyrDlPnmLyaKKEg6/X32cb/IkuR7htWoSxBnOfdGt8m81UKMNF9RvUdp8cRrAcOUxW1RPP3qJY+xWChrU2tpv1dQoqfVKVYXgfKUM+b1Xy1OcDdc1O3mk41xpSiwBY21qrRf7ZHOsnwTCcIQs6n2yKuIfZNjmGJhyAoefjd0WKnu1Vfu5Qc6wQIPBwFiTXTNt+nNR+dF7b/TPSs4pBiTaOE1I3EicvMW+hQhDVW8r2KS5twN4aZ8yu5gKrwEx6faIbfBo6TgXIbFrFX9m8g+a5XksWeDpUpacpyVijyjxbM+9dlu5Hmo/uu0axQSx9sofeE02vqjDY7Ql87NP69E/I6+PcI05mQ0l2jRRi6Q14lt4K6a7d6x61r71RrpvRDpvQ3Sf8Et3u5HCX/zlUmy0ArusxSIIk0PEizmTxsil5k//ecppPxcej2ZA973ZeuZ5KLWy0ou9TCdHG8WjYev4OSzT7LFFEfNYNMszkGesk+gLNFGssj46cenWiDf5wCosAXpFmNUoc0VjIXIbJrDnbzvEesCPkiZ6Xh211xyWoZsfal9pMcQy0GI6ACWKktGemYz5JOYdykidR1smT1hGiYthzNpHcyTMWkoF75Ulx/LuBxO2Q2wXN7c5OCxfuCYWdgGKQexa8Tc7fmZH1+3KI5nk+Ki43Bz+1w4F+Oa+j6ta8hxtmY2avUXW2+62tQofAKqS7LAbA7om7InvHVNnMNCFvQPI8krkWayGKPojiFK/cVqi77oSFM/p2frWF6SO7flbkWXnwxvURxkuLzoUhXltsUnVUKu1guXgi7R4kguhX9UYAUEO00mycKclER35xvUHhxyQWCW5ZMtHAlLxqRKPsw/B39qnay+77gqc+JFUGRV7rvvECG6zHMjmF3Q29rW89NpmGKPKLgayyRZ3dnKs81FUt2x7xUOU6wOl7Mq5uYV9Xkxs6D1DPLj/BbF0Y9LXHQXODzEzoS8d8wovPUni9WrSpglpv4yz1qwdE3+sJNT1bM8jf/n3JHNde2882T0vNDU4Tg6eMoKj7Mu4BpFTp1T75naZ1+/ZyU795ikPdbNMeH9uc+R73ZTE/HnvNrprpGaIn+8LmkmrWKUoLJkwf+9AkWJtHM8swT6wDB5zoO9pWtWvrrDzG7yOFpaGFd5jOThLi8uvlQ7n/yFYdf3hSXO2U6u2ahQM934eK1s2tQO2q3vNBlvoVd1nTK2S/Vc6zzIN7AokDJd3+PxXPMZNJf1Xwux1DkY1pW+C0k23Kd1XxfDQafPJb6IkxxV5T2qFMbEMSEEN6mQHTjLDm4cLgt7hT2Eq3hLlfM1psktZPUkUbo9eiPidZUIspnX9RUP7FX2ABijAf3dEu0ojkFRyDAHqRYaOfZrQlqnTF6I0hcz28+yvNr4O9J0MA7zWFF2nEl7lW1hzEJ0H+ea/PyVZdu7yucX4ggYF4u4pbVzwYgIXxK2CDA3CI7r5JAUHmd2kBpZiO7jbLMDk0D0HuEgwdoLXugAb2lcrKUna1dsvpoj4TdNGJjmk40BrChF0Ubo7/ZospEUpdujk0jSQtw65SCz7GRW5C+sXOXUpCLq9zyrtkI51aXs8hWGL93aw0qW9vMcQgpPTaH6NEg4CCxDnjnEkBxJbSMiIIDaRnBcxaGk8FLtXcu/hHXg7teEZn6MXZPdnqcl5cZhZ1KZ6Db6IMESjw9hRH22OTIUvEXj8P5M2B1Sc9262etz8Qa5VJtj4vq4SQItSLYcF3w9sOJwmST79oph+VQLU1/9bpIYmEu0xq2v7UrtiLJM9hx3UuU5SOMXwTPswhw2NdTlLiP23H+YYt/m4cGibM7wuSx24FEpkjjUZ1u+rm5KHC74PI0T1DARe7M5HGs4fdbV0OWwr4GEPO6eulP1RZzTs0Zl38Q5bWtsRIKib2wOxxoUfWFzONaAyvJzT5zDUT4hOQ+xLhglZwAISWaQPUTdQmIaZA9Rt5DMBtmtnCxwEe4KaPvVkj/EXOfCae1sE8D1Sbat4yOu099trrPcVukarEVPXLJpNqhPUb6+yGBaFj9ADtDYsg54kiwW69w9iB+yqjwqyxzeVCWQ7sfUOT1qLP5zk2iqqrPYr94y10xRuoXLUOAYkZ3mvZVo5FtrR0i8L0BCF21QkTg4QSwiUjHUOW2cTPI175LVf7VD4lXQ/qslkqDPbj38HBUPYK2mpiyPXZtPHh/f8y2uv9ohnT5vYU6cUz5naXnPg3IZXPH/ASIBldl0Nw7+AHMQlx/ADWRv4cgy2ZiKumJNzNOPWSIwG8ly+dQk4iB5LqeajqP0gd9qCTM44/OTVZjBDf/8RA6N05xQ24h3MuQu3Qn9/CZizZhsov26QHQSYdRkcQ6LmVYhnTGH/yLT9CqP0iKK8U9uYVPk86+NZ1J1Tv8aL0FRsTGtdXltpOMWR0JQ0FOcw6cGUY/kuaw8FjotT9EhRTabSyZ5fB8VQGqFFWaw2VdB8R2JQYK9xa+Jeye097Vp9qh4w4WD7VZlvUYrrXTGhWwOZsLEHF/S8YnJTusSbCKYcjcgJFkWc0TTboK/FWg/8xEWpf/D2AJIlxizZjDjHOh0VkGWj6nvlkYD4YGBpfjetZhJv8NbsqcJyFwCSBfmMoMZh7naulkU+ruFVCvA+gcs74VMxiXa4QrCrFCffwLGDcOrHvw52fX3RkWiuEXwjpQ8lz33i87F2DQLjV1gQbW3nJ4XbQvIVb6I02OEGez7jjaLW9H+RZRuo43EcAsRN/F6HpPkgCm47cOmWeiqIMWaOK+OUt9t0QQNHCRYWO1AUXA3zLuPNtzUkz0jN4o4bhJk2GOp2kmMAC5BjuGmFGVHdA3CFUrcefqk6fXJi4DPN/+9igiVh1D9VwvFI4UledJdeFrLpzogC89i+VQbSsra69pWeTtd2yg8BHY46m33QPXyKuy0JItrHUIySLJYqAzao0vfI0vFUaXzEeWH7ClNsmgtiP3FJNksVG1RqdojyGDhi44YNi1Ai8E5uvPJFm1H8vMHgHf37Kt01HcLt8QqXScArzKMQyL13Vq+nuDboiIJWycsa3n+QoJLeS/PGMZ1eRaXHXN59l9Kv3CRg764BQu6yronU0Ve6HTyHmuKtNU0jN2IR3QxGxmhjMOpYf2v6y4wUqn5ZotyjFLwSykCsC7Jzu4k2s3T36ffy+3cDMInO3UQU59Z06I4vu405evsoST5VR7FD6hjooNENs0CFbsGivSUQYLlaR8QH0uyaVZnd/AR5OLXY7nEn2D2+BsraCSPWTSlyaKrU3A23X53MICI56a1/dje0DCbDlNWeXoJ/iDRIz2VFwrKSW1Rlh+JjQJdPAvNQGEVqXB2r0sQFVl6luX1aLG2dibRBpcMOyB7d9bEwSU648q9K5QZ7cdNHLidT7Xh1Oj29gsfcJn+bmH1WW9gKnS7G6bYUJqavuLrfJIsu+luM9uin5E/T5DwDbHwD9GcFn8dxLIl90WUN6oLf/uUTrG1z4kQhyk2yk5PY9GRtCh9tk17wPOaMWzTba8QC4McrMlBhbjfwxxzrLYHedfyzWoLYnjbBCTtx94vFK8K2iUurx3eOGIx5PQT9+TrVugkrclreUPmDCZo8nGOqmyalQh9+po2ZJDF5h8kW1vFd+fpCtVgeSoUCmQX5cIKbiRFQ9gGM/6fPODQfnDiGDwYjPt2KPzUrnFDI3/Jo8wBVnaE476MCwvPPdLzjsv3KIdIie1mwkm2uYFp5L9q6PHdx9EKdKQV5KFiFgv8wcIjt2Qf/ay/WJwwRml1G8X4xlyOtrXCG7qyPJbuYAI/MCs3HJDesRe622/mKD/gmgVpPpljfBQchH20d9aJCkCIIDou5hIdcPEPJTadwUILLwp4l6LdLyQccc4+XihKtz/Dw+E9uetLTJr9xhs7SbKobJqN45noGZ3+6/6YRpAEjR/E1gsmyXI397Uqv94SiCbYIr+p47MsffUbZckLt87tosGhKSTdZInSLdwbwHOJPvEPdlHf7S5O/FHBXHRfov3uMJ1PsrTMs0Rk5ZXl2f8NQdg5F2CyTT7LjhIYcWKTfJp0Q+kYz3hZgpvcDB1Fetd3ToOJcAncqHKcbYNEBIuy7Ro3o7JZvvqjinJQXN7dMAcsbKLlNuho/c+qKPnjdS7RYlNDNh8yYD7VZi27yMEKJCAW3KoYJE2x1sgQyXQQLYyDBJsbH+kDPt4T6i9c4h46f7WXWTwjaTQoLtEzpEXHkXFtffw1x2GKlSN4W/RbzuzD2DT7dnKQnnjHMI248GBMmo38TEs0qoLoiHSCOd4ZTEDKSfX+qzkSfiwxLTgthfpsIxu/gCdWJpJPi5nHVxDkZF3xm8gdjMNMVpRd/rYz9OsD+DlkmTNNmzaHO4mNfXau88D61XWv4z/80+W0T1xu7iMf6crDLTd2a8KqSE7T6CZhFT76uwUaiKsccG2iPlsY3vmHtT5av6qV3WWdaZrR7IZJc2q0H8tNcpytX9gm0t+t1+PWfv4FlE9Z/iBcmrk8yxIAzacAcqDJ5ioOpMXHkQp19SxG/9UWiZcy9PfxFsi59KB7sAHEMoHEl6cuREO56EPq8uOwD6mUsW3Xn6ZcmATx2Kxjse0Iwx1H8cN5Sk6t6CCxfqwnAXVgQmOkcdgxmId+QFU/rK/7zl0ZpX08rsAGKS2+ZyoiRAdWNYNZqi6OX5W+iFiPk/7rHp/QnaCBusvylzDcxKI5cJIe4sBFi+Oi1ucyCBMxYB4uoAcW2h0W+h2kIIdxIFcBFs0lOq4WYumbyP8AL/WDywOk/qsVEgdy2FMI+fgoTi5BnPmGde5gHDhXUXbpLBvW7jGOQf+8OF8nnDtp/W0xbPgtD8KGHYwDGyrK/lxsuEoq5h5k/WUOH2bZq4Pq1wbnMibH92BdJeAqKh48jckUkosxWVl8qdrkCs2+lHX87z5a2CU5DwFb1wDhSZXLMVWZbb+mp3me5Zwe0CfYzAYSyCQvOQPQMMUO8TRdC/Ha75btq+IYFIW4hVSaZRt5GlKfFyMBuldLPbWpFsZFm5KXHWfiH63XORpUzn28/2x1USfIQ1mi5Ue++MxlHQVRUeWg9qT0NIvSUC72UHX5xa4Y5DU2tLl7wmrbUMIOk8wxL/FdzSFW82mPrRnN+H+Am9pjLQg3dmjuDKmAOPDknvPkSZXnII19g1w3MC6RreVFl8p8bYv5Z5SGKVasx76bZP82BmavTxlK5B44HiTZWkrOsnwTlSVkw/bwqTZe+huIlI2rrHENZLz0mUSLU+XqJoHFPavbUJ/n9AxbUuQvaa8z/FIo2pQQVy3GqXyQtBgRhliw2oRz1eDgHGSaAcY4wo2uk78QwafO5WRBCMRCdR8trBDZFsYsTvdxj50+LnLyGk7zoIDv2SqN5XS0qgYYidcDuSKd5dlGxt1smg1nyjCHKVZz+58gLrkpXX+0aBl4FlyWt7M8X4JIcEU+sjwyaWwOxy/1U/YsIJfshN29dCiFp3LsscQglhmU8Cnz9MGmgJzOIRWlR7Kd9VUKQzDwybP5HoqCdzvE7d5l5sTIwRiUnEl4MakYYaQVbQSrhv921/acZjanxc02gnee2n+L4uSkKCu6VENGKI0CX2sagtRf5twa+5oYZuJi8mb0UUweGPXjZBrJgZvVxcfhaMFL2tZvaDfGGX5yDBLsLi7y9xatjFJZzsYLIV8szDwFyFOuQ/1Xi7ZERcEvLv1X21VhVbDj1X+26t8HcBtVSYlm+xpxI4ySguusKMti5m2z9w10/2II5nSyo0FY6pJ0HMe15KkPtlnBLUjexeUunOsWLaY5Jzcmbe8Xzy/gqfgEyhLk4czBYkyHKWkKNNLMFNbOm4hV+aZdyO28S6ZTLHfE5fzvFajAmlDdbwJQQA5cryw9Dqtf5DDLIRtbpf9qZxnm7cG2CPyi2H+1sSezVmS70nwr2m8Wx9Rgm7ywDek+WuPwTRokWEzyE2Zqn1iqHJySsZtqxQjCDs3pK/IEOuMv3H22wxI0jfo8ldozl4XqHsQPWRUq8i0H52Kz0mMsdaew5MjP5CFsvBG+qC+dirHluWzafRU9nz4DjgyDBAuqRs/tZWvu3HKYZKHJLTwOdrhd2lIES4BoyGLMECJm0gjIXO3cQZ0ow5Ry7BBZ2G/2zvbIelzl+MHCi+gF1/oRFojxff1oxagubrXGSOPMO7Z6Fk2Ubo5eP8DOQHYf9/icnCVbWG4LwGaTyfWXOAGid6UGCZZ4FyCH2ZpbIYZJNpvuMkpIaWb7NEiwsTyh4UMDB/g91CBlDjt0qDtl5ynERzvCCc6m7fE8J8768F8kWD3uiO+6wuK5rCgGGCMdCwe7wu920V2KRm5UiAOqsGkOqBJAeyxBoJVBwmK4/hLgw8r2gQbftY3GclrZ1ABjGa+bAIjv2I1A/90B7b0E7f1e6+gneVYUaDeSBOEoFs1lL6yF2F+umm0l9XR3dnNzntC9GVX1CTyChFvUqO82psssL9ubHIy5kk6xOAOrkkQIOEiwUBG3jfcHQ7T+81zu2xeoJ1y4/O6jzeb5FuQ5yDmsQcJ+XTw8y8EfFbkHzByVdp9t3OhJQPyPUcFuDumExYgo9KGE3pHxaxCXOEaykks9+fmJAgf2Zy6+O0Dq9MYl9L2q9FL5ZOf0VXJx1XOYMYTLAIvLjTO09WUWfoDp7zYmozip1uA8XUFUOmI24nyq1UFH4xmNEkrxcQefYQ5v7CtYsmEfmk9T+p98BmXUXFFilLNhih2i9IFaLtEOV0Az6vN0zrJyLTBF4umo+AHXd6BkFcFhmsWxICnxX1nKdJ3+bov2I4+2jWolAh0k22KjXceTYKS4RFvc4wwLf3ZCs2lzeB2SDl1Fd4LpTL5Ov1wdFUUWQxwLKeXfG4UF8bS6PtpuE4g5nVJYVC+MKsuxy1ObW6aEoDVyza/+0gquV1mVC99ENFrtOmjRsoeJ2bXFs5lXUY6nt2MzW2JZNvPXt8Lx9mGJzv5mxRB9KRk7SMxoRmRu0RfNC10j/TihgZmaEYiDG/oTLav32fr6EhRlDrGSdIJ7qZMQJqW5veygTJ3RRFjo6/JkE6aCAMxi0GRP8VFTb2qmaU1v19QdL1XgOT43H2GuztPaB/W05VE9h78DDDDwgsb5DXSDM9tAH8MkQZ87863BaLNFZENuM9ZDTE+aMmAhh52B9mNM64YhRX4N8Xi+Oi++VEny2+vbKCmAZe+9mecC5BtY4KCl9XMC1/TTDUoe0pXkglsx+ensBuylrs5z9DjwAHymabDnmkJhzSZvjFlFVkImbSxZQwy/xJVmX1jgEjxF+foig2lZNA6x198KsP4By/vGGKzyKNEW5n1IuCIGfKGtyHMEhlgB+ETf4CUuUjoyhBM4rapuo+FwZUKoOAyoJx+xaCElDou9RAbS999fzWk27s3/V9GdWrcRZeejdZLEPo+JFsMD+6ouTiYJ48YFsZcgpOArVGtrxQbxCKYgZ7N0xtzmS/d30X7AAxrdgc/ZGiRFXw6/R7SJSI+KbRSTHfwanMG8wK7h0U1UgDrL61eo7Y8QSb32yK0xuPyRnCR1HMw2w+cohbegKK+yB5D+9vr9L+/ev351lMCowH4Dye3rV8+bJC3+FpN5G6VpVpKu//b6viy3f3v7tiA1Fm82MM6zIrst38TZ5m20zt4irD+9fffuLVhv3rLFG1gjlF/+vUUpivUgyAB1cNqM89+riHz6lkKGN379D8BxWjvCl+D2lUyO/PqWLfirgLdwE357DTFlyWwkz0gS79IIh9pIcS5AWvb6FRY3+FJoJ3LeKuHr09K6gvQxyuP7CCkin6Pn+g7Eb6//8os15uBQzRi6zHkXDA55GIt+DGxKj2tIbtl7HBmWRJpqQW6gDoQ+YFEyYCNYjqt0nYBzNPO6a4dnMCnZxXxnmbLvH1MRO9H/dp6uwfNvr/83Kfi3V+f/eU2X/bdXZDT/9uqXV/+3dSMGNzrd2WF4b9UPi7keOT577Qk/XbSexbbM1BX04qSans6NYIp7NaVdwXy4sD1majHWIIabKMGrPvpVkOX73V/RKGIh/dvrPzsI4vp0DiD9EGk5d8Cc1R0WuD//Yr1UEFdmxSr33zbR83+3Ba0lOBXcw6PTH5HydXVfbW5SEjXLHeg7Gs+bxGsAqNdT3EFCLc5Dh+WGf9GXEmIusQQb+ipbgdmK5P0SxG10DvdxvIhy1ARyOa67Y+WH2HA6Er8QbV2qKElephQ7RqDjyB2UOzTm0XoD0y7wfADAlnGagKR+Q43dvr6mH7MNuPBcXIZ+iWEXGc49MQAZKQfFsI09SpLsqTPNg0cInrwWsKPtNs8ewRq/oJferaqNz3h/ycqgeC0YuYfPdNazfaEgm/hxV9lR7LX+c/6n7lCrhyr4FInS6jaKyyoHOVoRyi9I4+n1g1C1/I5YJjTmefE7vC1PotxLK2ox/NfTJnbY1/Ie5BcDbzu3lrWxyLqlWS0e3/3iIHIqvHEqYYyX6qP1mqnSq/nnxYfsKU2yyE9pbTD8huZbmtSTsIXz6hkiej1Lvt5yeE6bwQbk9HkLc7KD/RC9yBCNdn8NIIkhQgD9uftjVKwipDmAEKM6RLLfzrPlZft5E2KhjuEw/kd3OQC0ruXSrwFQ/aRXAI0DB0BsQvz4yZMGZBCfx0fe0XgXXXSeAIiD6Dw+tj58Pti9k+Mjy87QoLaHjV5K2bo+1IySFu3kHp+TuRiCDJtOBaH0ocBp4UVCJnSl+6AihQWHiUkf0RRDYLXvredmpszih0BmPWxiIXhHjxFEZWFCYXqYbYRtdFq9YBqsv5+yp7qvzWNdfsOAdHh4+0L2vWdZ3rbvGKBtkQ/scRQ/kNtE+LTW8yACb9Ew3nlNw+EbBx6DQpYvNDJwU21CDEyNFz2HwiMdB+sGCoLwqihic5z5uHo5rsqy36s7Thmc+wcs7hNYlP6AzVxOAOJLJJgHJlQXvBOkbxMoGPvZhWmA4EvH12Q9bgXNNuOEvOY0Uh2rLcKJEueOGJ1lUHV0cQCv4AZojNq22KfpOiBya2o6TUuQF9682EivASoYmYEaCTdpnWi/cAVBPXe9ZD5aPEFRUlG3NzcwJTueUZkVtb89HSyaS24+vaijGI9G7eE2JTj8D7geEf3juLTpVqXQMqcDDitwQh1AhjkNbQOjeqziCzkIFXYOPoL8BQ+cvXVlWNrHtkI7+9m3Y1japx3HUQGaFdTbFtBhfQZRUeWAODKGdaPrqjjajOkk0lWDfwyrcjA61i42dYwEgeXL19rfePCAHPtUhbDEDAAxGULire6z2rgT5WP4MHbuQ/vh0qA96H/vcNDf0sjf9P2tAGH8qOReWcF1g7YqdwliaDmjo577qQeNshEAqTnGKk6yapul2AXfa5nnUGRGD1cvPXJCTFZBP0btcfDSbWE6NxY84uAv+yt4XDyMsDOQLbARtwR2ZDnOSqSBBXdbwjdTQk+Ulu3C+C4dPI4GHoBhjmUuIPEhcfAPbwv6KPjYCW0F/wV8ejDwvyqusvqSAIvsZKZtML4ODwpMVB0zxz6kw19G6Z3GLO/mpR3IzS+w2WmBzllLMUvsm5+3iEjkcth3CJ4+C9QyAyFr7jteS6c90Xea3hzDNMq780BEtRvywW1xQgOIcTSSx36Cg+wMJiDVKVJ/cnEc/AKezGeY7T2DPWOZee99jadWjChLre04zYaVf71tZ9mm7ZmD9bkr6TtyLGkvqwTQ8a0DqOarLRjdxtJE8mpiAfpe22iczkKA9fEOrzI6pJAU1cx204JeSP2enYaKAq7fSaw9x0KDtxSW6Pqe6I2e6kXh0+cyj3C0iTH1JvoMaU9Emtam4xDm4SRLsvwjeG4ezwgLvrxID7aq1J6ZG2fWpSSGHoP6+5JeDTgvztCWsuovmEy/zbXlQPp61IELQ3AhTVH7NgxL/zTcuIdsOMrZS3DTO0X3w+FD2MOHw4GBtnW7d2BwsKdLcZZiTzc/7G9jKjcPB+3HukO/K2qpBXcl/fSO7SAIbbhpfV60IRD2wavVmk33jD9/r2BXUZXCPyoACeQtxGLI3oUt1x6luFzbOt1Q0bGCofbv7wXQYFqwsyzfRJ4RjlqsVdTHplxWlJ9A932Pbm9hAr21XXIx1EsVCrCAnxe11f8oHsTcc3I7CHt88CkqSuly4IQYVHrj5n3K7mAayC8T47VXhQ0gbU5H6ADgdqs6B+CzrRDFI7drDo9g3h7jJVP4DNPOrpgkvnbQORkMTLg8uk3sbLON0pdAWKLTPxMNuC3oNUcQPA7F+gjT2GH7zxT3ackJdSvecxUn8+ldSLD3QcD+C24vsgJHU3FzlBKoQPdZCsRRwtxiJ7bRlOZe/8w3HKI3DXdWemrtsO8c7LDENFY0S6uXHkqAQtz3unrKPgFMrPMi017meG/Ld1f3OQDm+H+yxUdTBOQwZrC9whp+j7xU4RljEhvP1OEDnT/LhF3CwUmwlyDuUkS9OkaWhTXZnENobWJPGGQ89W7GBeXmJgePMNI+iOJwu2gXJNlxkt1hRW5PWPRTlN5VOIybNY/2Jb2YVHikyUk6MwtEttbvIiwOFGtjYJDQzw3WieJqv9mU7qMrh4Ls31HzdcFd5BXkwzF/+APhKfZ2rXjZEzk7ylrc0uikSshFF/Uxpv1ZFTnsWoERdmlnSXR3vkFNx1eLtLSx94q9LPfrMuK4Ok3tmLwqccTfS1AI3rLc2Xk3u37T0lPL5C6hVVpwode6m+qDIyxnm4ukuoOySJ2Gp59ZFcvZ3uPM5At4KvboPbfZOTSgBk7e9AkDhYN3hEEKIoGXqdz+TPuUZeivhw1GC2t8fyNLkqO0eNobvzDcIQdnaVIquD3PTSS2r6Z8z0q/hx3GvvuDiLYnXDP7Mh+Oe2rfq0bmhTnKDrJEBwuRQ5bV3ytQlEiLxXNkCV7ry9RAnFdla69ifKF9T2SBfi/4l7/YK8yBHsYJ4rWKQBq+xxgStjfScL39VlunU8I/o3icqobOR3EC+QYWOG7FJYixpN1f5vdZh0aZTN19dz9g47E+fcZUjZKjqrzHdI2JMrBX4z77dZ9wLqDtaJ1T90ACwjaSMzD6V8xbV9kDCLONJHBHcQyKIhwo8f9AA+zl3W886c6yvNrs0Vn+VbaFsf30aor5RbSYe3IbvfFopi9cmNwKdImdu2v3+cj8INyxJxOEdMieRZtiOz5B8Djimj3jltVOq2rboUOIpC+1JPYyRn33fWgc3zjBrfAM/t6gSIbcEY3E7/LcrO+mBNon4fN7nlVbRwnUlB3XJdTpgY/AJxRfmlXHay6HECh47omUqoNYGcXCuZviiUzLPZFROyEeflp2q3u8H5xG+hLwuiHBCxqpQx3fdTeU/uGVet8r+Z6NYS7Ue9/I97tyTEriS0cV2xLHboXAagIiB4SqL9wFjpbRxaOq8hyk8UuwK80t4GXUn7lqwnj/1XlWDs+o/Kj9PSpDCzO00y7RXEjO0zhBTTUkx5/dJHFb2enzNJVd4cq6APMT9XBQ6TQ9bWTDND1sKpu0Z6gii8lqX9lAjCHBD/HaECVnAIxNU3nNYxNYXvPY1G7ww9wHI3wyOiN2L7COWgsRHaNVgTT1Cmk765Hfr7gET1G+vsjQAlf8ADlArOUZofAexA9ZVR6VZQ5vqhKE3upxFRT/uQkUxadRAiRuebYOY2GCtHVa/zZIH4kbG97G4HdGcoDkyAka/6Ge4jTsCIV26/EbCNykYIop1z/PoNPFA1jLSOfd0pPHxzCxhTDY6fMW5mTn9DlLy/sRcP8BojB9p/nyA0TCrfwAbvq3292cLDuYJrjgxyxZBxorHjwgI1Dgx1H6EGwrxeAGm2I07vlJaMg2olNg2PObKNCC1EhoohSEjBaKvWnQhPhX/Wh0HqVFFA/foR4FPhi7ySq4BEUVKELrSbTFt5zDE4cHDthqtO/sdKLwTb+ocNkChDanXkQwlIc4E/opCE0bSLxXweEYq9pkOIqtK0DY26UcNKh3BJdgE8GUch7Xemtbv674rUCa9keIROjeBO2b/XlFUtS+9qaYn0/TBGFAfoe3RPXeQ9Zpu2Y/eH1Jr/H7VoD1D1jeO7IQU9y7KYMgBsGNH1My654waKtcUKOMVlaHq68SHJ/ju5bU/kc3Ixvdzou2qeQqU+SpS7RgaKO0Dai+X6LObiHik2B6U4cY7trFCqRYyw3VwhouXPM+g6KgLp56Ph7Rj0hGHBjm0kDtvFP2KC5L15+gXiUzqmsX8z43+fcqagbCXVZ/S2FJ3kwb+/Cvq2j0s74pejNJT8Y+m2z3S/V6PTbJhrWNTbvRDtDCH5x9yJ7SJIvW3vF8zosWKohi9AmxeFqAFtPrYXYs9X8AeHfvpBaa1HBcpesEOL79LhXtJ/guYxgetVv2v+AYGfu07M+yQH+hIo343WFs/dqy7km+hSuPtCFyv6w8s/sA10T1ciUlCMdREuHQ/D7qWzG6j0vITVhYuz1t/3B4jowtP8JjZPhYZbM/78vOKMmv8ih+gOldwBM54oE2rlJCjtVAqHO/DyCBjyA3eV9wKe9JtRNgj6wYXZecblvUJQOcT/F249GtBxa6R1nl6SX4Awd825dh97+8FGjgZtd/QtidLkFUZOlZlte8EkZJbzgOkP2vwX7fBdTUW8Dq1g8Tb9j30ZDb2y9ZKJ9z1SvObHxb+0cFaEER4o7WUhxDLBbIjDjenET5Xi2S/tLyIsobxcHL8lTbhdyOWemyfk8n94Psf7w6u/yf+dhjvKsLaHaCHLE/Nu6Ptp8OsXrunJhrRn61RTS8bWIxduO4JzJv5mkhpu3X7dA31nivokTzm7/4ssIZTBC5PQPr1vGpG/L5hqge+71WBUH3ZAKIuxiK9xYTrH0WRtkTFtmZUWiEylV0t8eUt3xa2ZZ436McIl2n42G0i7yBabRPEu+hsqGpkRtmCdWvALs8ehOl1W0U4ys5OdpWSS7b+dbirDYbvYpA2jUW+g+4Hg/847h+D1EBCOXdjwGtqsE/hlW5vEFQFPAuRTMQEqY817zz9M7BtNQeluBwfA5XSQalffb+7XYP+7/Zt2NY2qcd54X/ww97t/FH60H8EGJDTvY0X6vy6y2BJJ0c4+kWycq6J8upL3+1/3d0uf4cEcNbz4DXtN7YceM7dyuYxz6HR/AO2I4+Ue/MeDvM/1HB3M+JkFL+0jLPEn/zp1Txn4BP3muHxXYm79sUPkpgFDzOf8Bdo5c3sK+Yru/j7cdAS7roLAWFtJIlhpTj83Gs0dFElmT56o8qykFxeXcTup31zmH9TzRM9AFv8EOP2s1qgorOC4S0AgmIPb3fAywzQVhav+TwbScV+a+0n2D6gE/DRErZcpyN2vsJeyJX2+4EvSP3rb/G8S1PvKYFDxPk8ssxTKM+ShFCvCEfHGVmWiJSYaggbTyDCUhDCXT8JFlahLqNdF58AU8j7DOvEJfV9rz9mFVzn82Gi3yOH9109/yoS4ewZ3m59gR0sDA/V8NjsCfsHOAwR7BI8CLdG3NVJKcpzu+li61AXOXAsn1Gpnn6PZtgpudP2V3WWbZHMXVY4nwsN8lxtn4JFd2tXl9bG/kXUD5l+YPf2NhN48Zcsyezue6jH6vUGKPIBevFy1zLuAcbQDZCN3vzEjnpU2g5ZTuuJpiD4FCyp4msUcdjluMofjhPyfEKHQFyT9gmhMP0vDru7G7JU9xnoz0prsAGrc57Y7kfZe3AD4iipnHBs72BR3b/al+rP4zx/o5xI/YOQ7y/Q0zoCuN9O2QNsWFAXajf2Qw9pAg4PP+JdFW3E+DxFNSjOLkEcbY3wUoXvi1VGEStzwzP14mFY5wxR3zLDxwxpaEiqe6Cg4bwm3R5EsncIBbfg3WVgKuoeNgTPgvn77NC0y9de8VqMjpGNGR9f0t4mW2/pqd5nnkFIMNvla/KKC8DBDHAWKfpOhDSqopjUBSh2kUTytG+b77+t0+W7cksdH5VOMzjxgGeCbGV3OZmIBAVVQ6a4FH7MdzaJfmdy5JMno5BPXvCWlBo9EvMgYbnyPavGo+8N2146AMSbbU/yIGNDmzkYKlsHvj+WdjnLy47Rfmj6kNsF9YxdWRx5pxPGQawIIp5yJOzLN+gIaPCMASC/wQ3EPHDVUb4w+ImlOBYq7pJYHE/kjfvdGdMPFjQh8c+ZPixMaSIo8H0dWkxFj6If6rNPp7GUl0K6og7+yEpGTE/C85VtoXxbGHaLI53SAj8NnzxnjCmv5vAWZ5t3NlwWNrvvmLm3gy6rJ9nbnXzTxBrIhs66Bz4MmYYqxG+hxl5LX7nRbOXPn6pH5UNBNY9qOSDN4UoIHYA9OlTti8ug1SPcC0ONhoWYMd9fwwfjl4iSxID836w5bj7dO3e8L0DqLVR3sKHaLON4N2+6MQBD2XEa64bGPZrn33aB9wAG7MXeWbxKCZvau0JiwlfjhzO8L/Y22iavb9eegyxTe+PBG/vRZbLYiuZXvbMU8vOmjUsKgqtYHcBrmXwqvC6mYrvuILbqEpKNJEJB6JSo5z9kA3lz+Y26LK2HsdxLaXqA0E+Ir2qBqPbBga7N5cbXEaLylRRt2hB73uhf+xF6Qt4Kj4BzIx7aBUUdy6ofVCyAhpspHDjEq5x1wTwujEbDeOkeAptf8YOqnCJLGOhyRYw/NPfK1CBdT3c+zE7LnKY5Z4Xw7F1UXMSZb8KYVCDY0XrReIqC97Uq2yMhl6CbfJi11oL2DFafHISGhGpIqEhF698hJWvOKJKDoGXLyUGCfJykkInMhD6w9IWhkdz2889iB+yau/C+2knu4uL9SIDV5KnG3H+i/qaWAjM8+Iqej59BlRPXWAQSHs/0W8/MGFwztG97t2n5j5FZeQ653Aiw0MEf3jDW2Qc4haGnYEWT3vGVY7f7bmIXjBB9utpcbZ39rOHRwjw1uvifW3YXu8JO5y8xAkYvjzhNAIY5gKg/fHa1/OqjBKC5qeF45s/Bo9IT3Y/LsBVj/MUYoO/4wvlw9KLj0JCPILhv0hsVlL3nsw34f1Sg+Hj6HF9gnYTaHe39ooMLbm2GaZBf3ZpEPEEl9/cD9OyPzHGYRf9rGvpWI18H6yRzrEFLFbHhMyPeiO5J3P1og0a9c5nKepQ3k+/6zTfMeZZUSDdPDmMYJgRtFjo9sVhE/XkE3gEiZ8CuLrP8rL1aA/i9HSGspsCGhnfts1hf+ij/jFdTI1eSkAUChX0HHEqyHOQh8Kb5VaSUeT0HPxRkduJPtunOtzsx6jQxZ8KdKtpBeorcfsheUbx7NmJgEyUnfxnGcsFxmAj18X2hP61f7toFNwWyzROqjU4T1cQwUbdjs7tbKl1z0QJpbcZWebr6dTPK1jyF5idkIKdjX8GZdTcU+BUFWdAv9fFxJjhaBfwTvYlQFpWflT8gOs74HWWWSP8V5aG6WMN9yOPto3WIGmbORZSuZ8GY+Dey+MMi1jPeRnAy4p0B79sbk9x81eZYEHcK3wW4Bbjml8JhPnbugT59SvHoDK7DQZV1M90TLffco/TFx3DnaXtoYcBxHYwm6p2dSzp5tu1oC85xkgStxa0uUFLy322PsFddZucQyDjKVpX6DSqghrtKMsBeF457btiOVm7kqO4njXmjtYO4zC2LYTpqDZVOY3qoC43w47vSNLNt2tBX3KMkbwA+QYW2LmkjiJLR7x1GVYWz3jSUvU6jbGwYkupyEMEubHe9cmN9ZriY85j31G3nczeo72AGb380W1W+eY/rIy7TGipjqTKjipzm8XTqjTSBrTtd2pDXXjEEb0EjxA8fQTJ9rZKUscFOIDpy8TrJchw1B12HpC2uFdTfkRFQ/ERHIaPk+yujS+yr2PZ9M8oapGRnQ7R7CIrHBwo+5JjTFJ83e9nGctghsPQzIEH4bwEbBg6w9uadclRJHiWJN8zfPQ366sNE0hu1NGjtHhyOU2ny46+jO7vCMz7Vl842VCPUzDRgEMx98Fond66rfWAf4CCeEsHgPqSWSLJeP2oKLIYkpFldgH1Y8HXzf8M15+m61d4J4KyN+lNS1YguX3TfftcJSXcJjBGNf/2+t1rdqZ8TWs361fYXxvfVzmJijha81RArV9LWzBo8LAhTNKwPf+Dq4Y4wdRhWk6ytCjzCFGWn94wjeE2ShgKMPkMBQHuXIfIpnwAW3yqlZbirprUSD36zFfc4TPU1tHh17cU39ixk9Bazw2maBR/FnYSUWg57NQvErOwU3eoFFAu/fLmzTtu7Hqk7riQhuo/7rpMEZ+GyoZ/ZmnSHYUdbRHJseNA03wIiutVVuWxnCPasoNx7D9OIly4I2hRW0bmra7LEzCX+shdUqf6WHZ2NruKcuxgEkRc7DGbWQ35HGymcM2Yjc2a5XWnZJlIZePS9kaS2ahnSxRkHYdpxNj8ivj8/DWhKu7CXsqjtkm5q/3RBKTBlln2jGmZsotqsLA5g/S9kWF0r2zk2NxGhGs08vAR5C9X+N6PdGzpTINBHSRY7QEtpGEovpB2dRzesFrXqLbNzROdFifx6l2WYs40WsRPe6SWsz2y0coXw1c7Y7Gckammt1naWa1mNlp+jtLqNiKWs3xCwyVd7QBumLDrBsxBb3bBiNmw44Ar5J3wHdBxhY60ScL0UVjNevzDSCDraukCi+K9nVnhFsBs06909uJt5tWOudJyCRAaxHd/6zsmBic1Q4TBSLNJk7Cf+IKSvFldjlFYkCHBBJxocEFLUrHJjayF8KTuWEcw6pJx/im40IYLZmM/+dW+qfiuRPsU1K5H1FVwLWz6sniMbu+Qt4YpO89Tg+5Y8NJsrNTe6wvIRJr9IxM1iyB133adAYRXURc+9Aqm9Z/Di2MG+yk6IUsMGjefN1x7N7F7zU6nWrclhuKh+zjJIsPeBhc1ZWQ503Z4Aq5S3n2XVKm+HzsTZ2kUZAsBsbd8ZTPGM7CVPCbC1Fx1DJMEafTieBBOPKVz4rWRe7vLDUZ1DYk/Pzc0YbGuxZ1Y6OLFxPIaNKVL25vFq+2RzeI1G1/VMdRXZU5uTRZEIbpuA+dLmavLQI9o/3ES5hK1fNgeYYZR2Oz/b+/KetzGkfBfWcwfCOa9d4HOuQFyTboz8yiobbZbiG01ZDnH/vqVqItHVbEkk5TUyUuQNousgx/JIkVWwQYLAzNQLQ5fNR3CLFjrnoavA1+dtJoMw4+rx1Gvyiqw070cXwd2Omk1GYYfV4+dXpVVYKd+8r4O3NSS6h9r5Q+rx4tUYzVYacIjJLbQY3sqIEoaGS0Jup/DfM/n9qIHtLSKcLjV5PPhxYxrp8Yec54wmrX1/rQK46CLilpIyacThkGgaZIYaGRHccTwyQrbuACwur7qY0CgOv4XBOlogMwNUEfEyNjnnaNm0EUdc7kGSETwRT3wmgq4xYFtRTPgEkEXa8a7GHjzznSvftRpbNL99bl8qFvcyEJjSV70jEdpoMlFE65+BiTVGwPI2bD4Oi/OB3ko6xt4+PfFnqfWkvLr6nEx6LIeEMg8X7FR0CQXs2DQ/vw0cAAkUFs0EBL5b5Oimuw8LYv10Hntz1EWIsnQFiEQdDDDBAQPi9Eg1xKmEEDu8T0WEi8zTDr8vow740jyBTgfmNiXdF1ICEX3Xkb2a1T/RYo1G4hkGOUAl+Qo30XLtSobaX9Z+924Rg0OI93gM/f+KjbNcUET083lo2Z2D7cLz/TllO7Ef7NKmuJnAseVWmgYLlVyUB6d4MkE4tLU4vCdPRIXiDVgrIyfI34BiPHnlLnwJSWcd+GTN+KWjKheSluCgFfhomFnUGRNgFl+DJN5YBMxXsk44MwdpeRNdl++SItt8ulcp4Y5ie0/WfmA6DC9Gx3PkjoptMaGH8PNJOx+uhAUvS4sTIBdMTtENF8HVmhqlwaaayDJQXkiOD2jEOAJbaP9nq7isrD2RR0KC/WGlgS1aD7SZJxpPTqv2/QhL8Xy/exaSluC5td1Y2hQZPl+9mfxvUL7p7xq4NRNTqs4nwQE18QBy1d/dglptYqTTAhnXhdBhzu+HLhEm4amYkXrlnkD+9V5DBa9knVCWsH6AuaViAagXo/lL2OdqPLECJZ7aq8FRo514KAXBIszyu5YTyBiH0v0Feb9SNuFpowXyOb3Z/o/DKPP6K2U5+JYJ9MRAe4aB/KHFZEN10YreQI+sKrPKrzf/gFP/Fnld3gsdHqZ07fNpSAv0qJxWZ6fj9u9ID9PtJ9VFErgi5daOgompkSGS2IWhvyihdkiDIAs3Tg8BxGXA6FVrFJLgFnMtWoSumZfrixsLf8b/BKAFfGL/CRczf1hvuV/8yg22X37+vC6LIvs7lyKFWCMlB+SC6NcO/po9dYARVj0j4/1nwlHL3XqceOBB4RQUyOhK0PKjjDMSdR0CF06g1LKsjYTYAMLnVz52oYGzi85N18MttknbEqDuTHfuQ636c4dw2wpXsQgMySLWvpEvAVFpREewozhxwBwOSLu0H0KdOavADFuh8+Jsop8dqD9nRZZeiz7ufVFfrjLjpIwwVPGq+9hVCLtHYxWMOpY0C0bBC2SPMwTL9RAQdFGKcqRQhV7yfhbzd588UCNv+5eitG5t+oMeP51rkqqXvpyzHCMakQqFvSCJzk94gZaNPRUsZeGv/XOiRxIPtnZb81TXgc595GkRQh0eexjyMWiMeqxowdYLuas0VTi73R/7kFKaxgGF3GRK9XliNkShsTwJDyFgXKj7Qg8mw3MDevZ3UmOy7Bux3B13l9ykx4e9+Jl/v24z1P8iXBPoJ2/9D8uHA2wevMjQTf+bFi4zURR6bcRWhx2530rScSNvy4xEWQV66XXZFF+fRLB1gd9OMxU6RYAq+VvKecBUcRt4zj8LGSjOFxHbS8SLx9IluTjbjOvD1i2ShymWncuCGZeAeZ0gX6jhGa6OHz0e7nX2b6s490Gf94Qa5oyNKOFtIifzrMJp6782W3WZxTP083Xt8ebMt98vTnfnTZF1twBXcVrCkR4PQc0RrP6txWYZmMc/8Xhbvme2/JAF3F9vgRzc6/S1/f32b76RSSR4s73DPWGhl/X/vh0UIXDbO5H7TJNQtBFDoeCxdtOR/HEVidbp1WsS5V+3ypAv6+Amu5E8rrID/FQojM31ji9aPX4MBTicFQ7YykAuc1/w2Mh8Bi6Yj4Xo/Irv1Umf5fvEuX/dUfhzoZBp/kKZlkUD1fhikkTynehbBYGaKpSLC9GF3ERUFvFZn0+VMWct8bCafYp66+zOIvtq0Oa7RP57/WGzq6jEWnZg7WCKJhShDfuoCi/B8EUbqowuFI14rBT5Zsv7NOD2HzNz+YNOetnfN6yKLUJzC6Nk8wdVIsWLeQdOIc9wwAS0ZA155lVZ4xztzkXlca7T+lPGWv17TGr2/UUchV35U3GRsA6s3DdcVQtfVg8lZ5YDD66yM20Rr76OVikRFApUrSwIaGnAcQvKMdEiDbrzobPd/km3Wf/E9tOxuRdetydrR2z0vs9gdrdw49REGiJrQtjlwZBHWyqMGizdeIw7SScEV++N5fh7nea+8qnsZ9czT6y/br3WXzLxPf/iv3j/Xl/rIOM1l0yNC5P1HtMVb+Xtdt1mydYfSfs2jZA9PVlo69c2XIAX5wBIsYN94sw12kU5QiW0yOjeM+JQ63gTxRWGhne6bNByu+dgVaZpaNJqzzfnZV9vnuRH+RGkTmrKVViT2Qqa+1uivr7E5quUFOPYhcZRvX/P+WnEp+POgqrD5sf41x5Gg8lT9NUr+gCAdTJNhuQPojvp5HzkVIl9nykslab1H5/QvMRaupR7CLDqP5/fR8Zn486CqsPI95KnwAlT/NRr+gCAdTJNp/rne/3f+dldtx9Fpu82LK3fka96Fs+k7/ml1uFT2iOoi0/iucc+Kp/uD6evouC2M/1NFa3dj/HeUlzCch8bfoGpZcKrkHEZZwgTDq+mvXMin9mse6py9MBRnRAtX85T6Dmf5w8Fk/re84yHkRxH7G8kgFg6lFV1RBFd9afb8XrrDiVL9MyvUtP9se+utaNKIFAMn/8qylFA/bcbB7EIf33H9u7vOr59M6O9WNNQzo39ytQSwJ3FUgqd62xknJEY8rCZY6zJBk5m3+ZndqLeFb7QxHEYCh1cHiRlmIn7wRYHIYiiMNQ6jJRtinPhYBM1JWAJuoKeT1AcDEIiP5IWqLkfSrzErH7p07jmBWi2bKiXaVRUb2mEbpE0MJi27y1YpCpRsGzNoEai4Kyd0fFNnhb8X16PN+nsq+ImUinouRQKdmyOIRwc9cpXAPVdCzt8WpSgMPWJGKyJfjRjJwc+ieZFoO+BGq/L3QpUA8oeH7rSkDxu0JH810yzveifMi3ABeTAGJm0rh4ltWwqaD7rfKPoDFvlIMcdRIHw+FDhcVrKILYDKUODsPVJYvDUARxGEpdHOq7O+KmLORO9tTkVwG4gWQgZ5DSIcVwxGpxHoogbnWpe3ZUjkPsSVEpBOdCpZzBB+GAt82eapqAcuh00xRTU05D4dJBFIfsdKpc9e6wxtbHIgF1s6gcnF/9qNzaY7q/PpcP9XaiyUaESkGTQxLRNRzSyRe/yGBXyiC+sviUsAa8pL3NH7MNxqctJBi1FBxOGBOy/ZaC0/6bIj8/YkzaQoJTS+Hg1N5Pt5i0v0Ptt0VMN/ZL/UqzvySM+rE6GeXI6pQOKd5k95XbWGwdUsBkkBQwJVMKgjPNjdeNyCKglKHdydqYSsoPeQnNpEoZyqQpdjD5LL5XCn/Ks2N5wjsMpIIYg4QM/wvZdg1FmM/F2mB1hEiP6cUUJ1a/fRbVduBYbwAFOAUb5bAZNRKnemZWYUBFkwRW06Ti7eWwFIbYrg6jp/Z3SD5J7k6Pzgxp24skB21H1pgkHVuuMRJx+1SmY8M6UBYSvSXLeXzIHDQYf7ISIRdZb5q8fCEd51ZGToWx5ykMgXiSjEYKkmWAa5aWfESvtTVcHlEf7Nz2gvoi0PPpSx0clGjDFgulDOKhFDtPCnLwTLT9HT4ZyBmnrpKqBxncfl+MsuGi9LaqL2RH3oFbQ6MctJlO4jrwwCK/2ecfGCV4HIIRjzhpvBWHx30KjlSYzHXyOFAyPxUQEtgk1KcDNuduYcAZWxTU+sJl+0YcRTXMqMnRJgG3BhaV60x0s0fPA5Qy8Fx0KHYw+VLgTJQyiIlS7Jooqtrb817cpqev0EShFYMThUbhstsQlc+221AG2m0odo1GkZ6qgfOPyHYPkItulIPjTyfhMXyZVbuIEzwN2SQEW4XKeSRXFOK4AU/P+yL4KK4r5ZyeOOZYgIY4Sxk1s5oRt4CJRSegzr0MUucYVyLmAKNcKYXHuULAZ9UEoKLYNRQOlg2Rc8E4PKbZDurSoQheILpS11GqFqzGPjrVisGjUo3COQ5lz1IrsEkBj0KDiPHN4J0oS1E4xglGiH1PgGgdsmgheSwBtFL4CopC4EKPHRfGhpFNA+LJJhvLHNuXYIQsMXh7ETSKA3A4hFDCx0QI8UhxGHLwBOB9wtNDClisARr0451O5lS7Hqxb/MKPSQCrrNO4UFjkp1PV+B7napOAyLOonKaGFiV0MeIsQtX/SmS32JWA/l9X6Nojpj+IWylaKbg/VAlcrJDPVsQXK96nqu6zCaGITUJ9f0muHx/3mdje5i19JtxfkbvKOOgsCp4MLblbAv0+BH6DA6FzX7H4LE7VFCzv1jJukLSflfGLKhYF9XW6JWJ0hPll2fGBnCbnfLpOalL22WF/lYj33Z7//V5LTDf2KJNzAs08iE4GurFSAAEAMGEAUkImgJpxead/TmefX6ml2BWenoDhqeKstFLMJ+Wyst/a2Pa1SLCrKTrVmB52dSujLy2Gyi1y7Jpr0t9mVYjB+649pXnNXXknoF+drXiit2+h9wVgdaPIfLWgqzhB/W7hcasPP0OAHmrY8i9G/f6eLNHvFo3/Hjf9ElkRdZvGq0m5LclNe8PO1pxTDVfKvGMvlcLuz4M1QbPYhWHNc5sWOwGOCE41T32+MPN0Duco7JiVQiIHmnusspCGGYMas5L/KXUmswDPLk6kScAKIXECvC/RGqEejExebBP9ZQi61Op0hDLAS5VGC+IFylRcTVe7X0eHqQlV3aYNMZEij25U9T1Oo6Zqbl/LIg3mbEUygfYciHC5QDr/bhf00EhWJh8QTe554zGUygKFAF7Hs1oQKtCGwPIgZnIPEpA82ECJbBL9dC3pjtdEeyZHu+/8yrja8DMsqTH9uIpoxTh3BBrDHokFMSDuqvErE6sToCyi3lJMpj5hS/p2AfOAhAFMAT3La0xAvrYbr3p7hEwpbZL4V9c4Dpf1sEeZ01U0TIcrqhN676WIqvfH4P2XAmLyxImJHjfe9DZdjrzXBWtCBrDKQhgCnwRxYi/9uAQzPM/2+2oi7VsmjGCQhjMBC0MXKN0FZxqaxrW2aEMOAeMdvtYA9rh+vBmgR8ZDdgjAFnQFXC3zAbbUB3tcrdckHlY3rXAeSo83Tfe2nDSHTRTCBOb7eFkTffs+XtXuaTipqk0UQlXzAbusOfx4+eYq3+9JNXWCECqqr9ubPRP0en2aas0D+6RpEVZOJblQSKuGHhygr4c8+5+gonmhQP2ST7kxvIqEcshr/kZFxwt9siXodgPYKhl9wLspcUeIV9G9wFkKUwou0oTgfRKWMz0SfR68CZfR45iG4V5HR9NcJqKiaJDuKK9iCDRxIovI1kZFCBlvuj5OCGkngCqEUayAJrKq8qsfdeUNT7e+BlkwhbU7qoPG8D3UiSonanQTRF+VxiGyFkNlEBmMjWLXtiv67dm2SbJbG5qLBZ2nN+VIVJulRqxC51H0yONWBjphnKqAdP6PVLTwPrIWHL1nqqLU3GRQhJiWwqkHRRtKhvsV+EUOuAJxg+HiixxQbCOtFTJWkSfTtHZn2gVMMDy+Xxdhjj5+Em4Dk8Sv4lbsp6GepwOUQX7im7RN5P9LdHhVu4hbyadqf/aQnsT2n6x8UDjYiruqeFNHq2vGFJNV0Xhh0w2hjduhfdwMcAVPioA10RHPCtbmyTRf1H5n20ev5XdemMs8faw3x4SokASYENU4dUM9MATdeBWBqHKkK0TSh3CMiCh6shFOWDw/ZnGOC3clv+iYxzRdwD58TBgUfpU2QxredLdoPF3vVeMRJkO7uJY6oSfBwZrW+krGTpy6EepuJDm3fCbhmvZ8WhRIx4QHUoaZ6oDolu1IpoJWXnDAzehslNZ/f4e+NmEG4UzUXCbQCMfJnTsCO+OKujfAk6gYIx8OP9qOfkdIUQ8GokYGThxicMxuCmLDiNL63zfGNgMZVpZxsdtR0b+BWHFz1daYsVwnIIgIW4vE3AXBNaEZAj9uu/AMwmhVDwFMtI2E8vWNVpr5WAiTrUU21UJHgB0pgrzlglMHmybseBhqC2ppEGMQ91RwaqdSiDaAGsswCRE6mvvKkt0Eceru6wEmI+y2atExgbODmNi9iHNqBxuiazOnnkhykk21JnADQTkvpSmoXJZrNfEEmMbHJsda4U1EupPsuk7lSa15fuMizSjjTiYoW7ZJ6XaiGIXTrha7k2ocjsc53RFiTpTRZ8OwARSSm/TwuBdDJgRccYOScF2MfAyN24LkWoiocp+KQbsXC6mMULoPkqbfnLVSTMjqePaIS9Qn1i6byP9qFV5V61izPSt1L9polWCL9tSTWS9mGWWQJ20KI5O54yx+TPU4Z/MuieimXXncJ7z4g9OnkEf4zjohTvId6WNkQ9xcMP7MRAxMVxX/w3NeE/XJPhLis6BN5P97oJWwpKmJ5iKZeLebPVwI6hADBc3+cdPf9fbc83qSjuR1kR8oe1DkIQwC5yNpRxWZXuRiU9zmIwyhEK/dDEpWk8TKcALMCgQ5Mc7hDCzNaKfTqmDtYG14NgkFCpAuBB7iqK1kJ0n0xDC26igtrgSUrEZqQSWhMQ4ArOwr7f4fT6sy4eoMmKYkAZKoAFdpuHUJlGB5Xxq4uFK50G3ZR0HMpCxTLmDp6U2St8eszNI9cbmSqoArOe1iJZzE5aa5i+VIzHK5MbrLozYrt13QuoHUJVuCb6XyMutMDAejpq1xx4KBqXEFLwwEA6XnaZpwZtyZYg56fQq+LpnrkZ91qN0/Wakekto9eJEfT2WRZtX8UHujvX5d8KPbPMHqE6cO/nm5zT0hghK09URTbah7UXfaDI/dpBX8OcrqRlWm8rjGizagklqECzctGwlwjnFZi+ExCyRjaY46iCwrlxm2C0MF4hAmpMVnB7qaQXElgwy3/7WkM7aFLmwxPKKAnDuyDSqZzmWG7aJ9gYiCCWnx2fHEZlDcTA/EXhettELAKuCr7QhrLZJuqVkiXCmUPJh9CFMGL6gUPa3WqJho8xpFXYonOWhOX2Raq5F9Pb67c7GR27/cTtxAGexTpkflr541jdSmr/pZFH3Z1bMmYVn7Q/VntXWtdn7v863Yn+SvV88+n6vaB9H89VKcst3QxFXV5lHI1IpDox3N2+N93u3/DIk6kq64z7lcptu0TK+LMqsD1lfFm8r7lKn45ElNfXx2J7Zvjx/P5eO5rFQWh7u9tru8ekbzv3pmyXzV3EI/+VChEjOrVBAfj8/P2X7by/063Z+MHTXWxIvK+m9E9XvTl9XgLMXuZ9/Sh/zIbKg130vxKI7batB1SahPH4836TcxRbYvJ/FO7NLNz0916Gx58IQ14u4I3exXL7N0V6SHU9vGUL/6s8Lw9vDjP/8HKnQByFXzBwA= - - - dbo - - \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201504171629262_V22Final.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201504171629262_V22Final.Designer.cs deleted file mode 100644 index ddaae8eeae..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504171629262_V22Final.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class V22Final : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(V22Final)); - - string IMigrationMetadata.Id - { - get { return "201504171629262_V22Final"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201504171629262_V22Final.cs b/src/Libraries/SmartStore.Data/Migrations/201504171629262_V22Final.cs deleted file mode 100644 index 58b0e21624..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504171629262_V22Final.cs +++ /dev/null @@ -1,99 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - using System.Linq; - - public partial class V22Final : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Common.Date", - "Date", - "Datum"); - builder.AddOrUpdate("Common.MoreInfo", - "More info", - "Mehr Info"); - builder.AddOrUpdate("Common.Download", - "Download", - "Download"); - - builder.Delete( - "Reviews.Date", - "RewardPoints.Fields.Date", - "Admin.Customers.Customers.RewardPoints.Fields.Date", - "DownloadableProducts.Fields.Date", - "Order.ShipmentStatusEvents.Date", - "PrivateMessages.Inbox.DateColumn", - "PrivateMessages.Sent.DateColumn"); - - builder.AddOrUpdate("Admin.CheckUpdate", - "Check for update", - "Auf Aktualisierung prfen"); - builder.AddOrUpdate("Admin.CheckUpdate.UpdateAvailable", - "Update available", - "Update verfgbar"); - builder.AddOrUpdate("Admin.CheckUpdate.IsUpToDate", - "SmartStore.NET is up to date", - "SmartStore.NET ist auf dem neuesten Stand"); - builder.AddOrUpdate("Admin.CheckUpdate.YourVersion", - "Your version", - "Ihre Version"); - builder.AddOrUpdate("Admin.CheckUpdate.CurrentVersion", - "Current version", - "Aktuelle Version"); - builder.AddOrUpdate("Admin.CheckUpdate.ReleaseNotes", - "Release Notes", - "Release Notes"); - builder.AddOrUpdate("Admin.CheckUpdate.DontNotifyAnymore", - "Don't notify anymore", - "Nicht mehr benachrichtigen"); - - builder.AddOrUpdate("Common.Next", - "Next", - "Weiter"); - builder.AddOrUpdate("Admin.Common.BackToConfiguration", - "Back to configuration", - "Zurck zur Konfiguration"); - builder.AddOrUpdate("Admin.Common.UploadFileSucceeded", - "The file has been successfully uploaded.", - "Die Datei wurde erfolgreich hochgeladen."); - builder.AddOrUpdate("Admin.Common.UploadFileFailed", - "The upload has failed.", - "Der Upload ist leider fehlgeschlagen."); - builder.AddOrUpdate("Admin.Common.ImportAll", - "Import all", - "Alle importieren"); - builder.AddOrUpdate("Admin.Common.ImportSelected", - "Import selected", - "Ausgewhlte importieren"); - builder.AddOrUpdate("Admin.Common.UnknownError", - "An unknown error has occurred.", - "Es ist ein unbekannter Fehler aufgetreten."); - builder.AddOrUpdate("Plugins.Feed.FreeShippingThreshold", - "Free shipping threshold", - "Kostenloser Versand ab", - "Amount as from shipping is free.", - "Betrag, ab dem keine Versandkosten anfallen."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201504171629262_V22Final.resx b/src/Libraries/SmartStore.Data/Migrations/201504171629262_V22Final.resx deleted file mode 100644 index 8ebb7814c7..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504171629262_V22Final.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201504232202590_AutoUpdateRes.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201504232202590_AutoUpdateRes.Designer.cs deleted file mode 100644 index a94627f448..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504232202590_AutoUpdateRes.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class AutoUpdateRes : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(AutoUpdateRes)); - - string IMigrationMetadata.Id - { - get { return "201504232202590_AutoUpdateRes"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201504232202590_AutoUpdateRes.cs b/src/Libraries/SmartStore.Data/Migrations/201504232202590_AutoUpdateRes.cs deleted file mode 100644 index 84dd3e8dbd..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504232202590_AutoUpdateRes.cs +++ /dev/null @@ -1,60 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class AutoUpdateRes : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.CheckUpdate.UpdateNow", - "Update now", - "Jetzt aktualisieren"); - builder.AddOrUpdate("Admin.CheckUpdate.AutoUpdatePossible", - "AutoUpdate possible", - "AutoUpdate mglich"); - builder.AddOrUpdate("Admin.CheckUpdate.AutoUpdateFailure", - "Unknown error during package download. Please try again later.", - "Unbekannter Fehler beim Paket-Download. Bitte versuchen Sie es spter erneut."); - - builder.AddOrUpdate("Admin.CheckUpdate.AutoUpdatePossibleInfo", - @"

This update can be installed automatically. For this SmartStore.NET downloads an installation package - to your webserver, executes it and restarts the application. Before the installation - your shop directory is backed up, except the folders App_Data and Media, as well as the - SQL Server database file. -

-

- Click the Update now button to download and install the package. As an alternative to this, you can - download the package to your local PC further below and perform the installation at a later time manually. -

", - @"

Dieses Update kann automatisch installiert werden. Hierfr ldt SmartStore.NET ein Installationspaket - auf Ihren Webserver herunter, fhrt die Installation durch und startet die Anwendung neu. Vor der Installation wird der - Verzeichnisinhalt Ihres Shops gesichert, mit Ausnahme der Ordner App_Data und Media sowie der - SQL Server Datenbank. -

-

- Klicken Sie die Schaltflche Jetzt aktualisieren, um das Paket downzuloaden und zu installieren. Alternativ hierzu knnen - Sie weiter unten das Paket auf Ihren lokalen PC downloaden und die Installation zu einem spteren Zeitpunkt manuell durchfhren. -

"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201504232202590_AutoUpdateRes.resx b/src/Libraries/SmartStore.Data/Migrations/201504232202590_AutoUpdateRes.resx deleted file mode 100644 index cc80a45d18..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504232202590_AutoUpdateRes.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201504270946381_TempFileCleanupTask.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201504270946381_TempFileCleanupTask.Designer.cs deleted file mode 100644 index faa97538ed..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504270946381_TempFileCleanupTask.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class TempFileCleanupTask : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(TempFileCleanupTask)); - - string IMigrationMetadata.Id - { - get { return "201504270946381_TempFileCleanupTask"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201504270946381_TempFileCleanupTask.cs b/src/Libraries/SmartStore.Data/Migrations/201504270946381_TempFileCleanupTask.cs deleted file mode 100644 index adcf8c1589..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504270946381_TempFileCleanupTask.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Core.Domain.Tasks; - using SmartStore.Data.Setup; - - public partial class TempFileCleanupTask : DbMigration, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.Set().AddOrUpdate(x => x.Type, - new ScheduleTask - { - Name = "Cleanup temporary files", - CronExpression = "30 3 * * *", - Type = "SmartStore.Services.Common.TempFileCleanupTask, SmartStore.Services", - Enabled = true, - StopOnError = false - } - ); - - context.SaveChanges(); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201504270946381_TempFileCleanupTask.resx b/src/Libraries/SmartStore.Data/Migrations/201504270946381_TempFileCleanupTask.resx deleted file mode 100644 index cc80a45d18..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504270946381_TempFileCleanupTask.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201504290937512_SendEmailsManually.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201504290937512_SendEmailsManually.Designer.cs deleted file mode 100644 index 1a16386517..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504290937512_SendEmailsManually.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class SendEmailsManually : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(SendEmailsManually)); - - string IMigrationMetadata.Id - { - get { return "201504290937512_SendEmailsManually"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201504290937512_SendEmailsManually.cs b/src/Libraries/SmartStore.Data/Migrations/201504290937512_SendEmailsManually.cs deleted file mode 100644 index d06f7cea83..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504290937512_SendEmailsManually.cs +++ /dev/null @@ -1,100 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class SendEmailsManually : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Order", "HasNewPaymentNotification", c => c.Boolean(nullable: false)); - AddColumn("dbo.MessageTemplate", "SendManually", c => c.Boolean(nullable: false)); - AddColumn("dbo.QueuedEmail", "SendManually", c => c.Boolean(nullable: false)); - } - - public override void Down() - { - DropColumn("dbo.QueuedEmail", "SendManually"); - DropColumn("dbo.MessageTemplate", "SendManually"); - DropColumn("dbo.Order", "HasNewPaymentNotification"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Common.ErrorSendingEmail", - "Error sending an email", - "Fehler beim Senden einer E-Mail"); - - builder.AddOrUpdate("Admin.System.QueuedEmails.Fields.SendManually", - "Only send manually", - "Nur manuell senden", - "Indicates whether the email should only be send manually.", - "Legt fest, ob die E-Mail ausschlielich manuell gesendet werden soll."); - - builder.AddOrUpdate("Admin.System.QueuedEmails.List.SendManually", - "Load emails manually send only", - "Lade nur manuell zu sendende E-Mails", - "Load emails manually send only.", - "Lade nur manuell zu sendende E-Mails."); - - builder.AddOrUpdate("Admin.System.QueuedEmails.List.LoadNotSent", - "Load not sent emails only", - "Lade nur noch nicht gesendete E-Mails", - "Load not sent emails only.", - "Lade nur noch nicht gesendete E-Mails."); - - builder.AddOrUpdate("Admin.System.QueuedEmails.Requeued", - "The queued email has been requeued successfully.", - "Die Nachricht wurde erfolgreich neu eingereiht."); - - builder.AddOrUpdate("Admin.Common.SendNow", - "Send now", - "Jetzt senden"); - - builder.AddOrUpdate("Admin.ContentManagement.MessageTemplates.Fields.SendManually", - "Only send manually", - "Nur manuell senden", - "Indicates whether emails derived from this message template should only be send manually.", - "Legt fest, ob E-Mails, die von dieser Nachrichtenvorlage abgeleitet sind, ausschlielich manuell gesendet werden sollen."); - - - builder.AddOrUpdate("Admin.Orders.Payments.NewIpn", - "New IPN", - "Neue IPN"); - - builder.AddOrUpdate("Admin.Orders.Payments.NewIpn.Hint", - "A new notification from the payment provider has arrived in the order notes.", - "In den Auftragsnotizen ist eine neue Benachrichtigung vom Zahlungsanbieter eingetroffen."); - - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.SubCategoryDisplayType", - "Show subcategories", - "Unterwarengruppen anzeigen", - "Indicates whether and where to show subcategories on a category page.", - "Legt fest, ob und wo Unterwarengruppen auf einer Warengruppenseite angezeigt werden sollen."); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.SubCategoryDisplayType.Hide", - "Do not display", - "Nicht anzeigen"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.SubCategoryDisplayType.AboveProductList", - "Above product list", - "ber der Produktliste"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.SubCategoryDisplayType.Bottom", - "At bottom of page", - "Am Seitenende"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201504290937512_SendEmailsManually.resx b/src/Libraries/SmartStore.Data/Migrations/201504290937512_SendEmailsManually.resx deleted file mode 100644 index 22e46cf0d4..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201504290937512_SendEmailsManually.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201505052021483_PdfConverterRoleFix.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201505052021483_PdfConverterRoleFix.Designer.cs deleted file mode 100644 index c10a58454d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201505052021483_PdfConverterRoleFix.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class PdfConverterRoleFix : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(PdfConverterRoleFix)); - - string IMigrationMetadata.Id - { - get { return "201505052021483_PdfConverterRoleFix"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201505052021483_PdfConverterRoleFix.cs b/src/Libraries/SmartStore.Data/Migrations/201505052021483_PdfConverterRoleFix.cs deleted file mode 100644 index 1bd58e0760..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201505052021483_PdfConverterRoleFix.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Linq; - using System.Data.Entity; - using System.Data.Entity.Migrations; - using SmartStore.Core.Domain.Customers; - using SmartStore.Data.Setup; - - public partial class PdfConverterRoleFix : DbMigration, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - var pdfUser = context.Set().Include(x => x.CustomerRoles).FirstOrDefault(x => x.SystemName == SystemCustomerNames.PdfConverter); - - if (pdfUser == null) - return; - - if (!pdfUser.CustomerRoles.Any()) - { - var guestRole = pdfUser.CustomerRoles.FirstOrDefault(x => x.SystemName == SystemCustomerRoleNames.Guests); - if (guestRole == null) - { - guestRole = context.Set().FirstOrDefault(x => x.SystemName == SystemCustomerRoleNames.Guests); - if (guestRole != null) - { - pdfUser.CustomerRoles.Add(guestRole); - context.SaveChanges(); - } - } - } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201505052021483_PdfConverterRoleFix.resx b/src/Libraries/SmartStore.Data/Migrations/201505052021483_PdfConverterRoleFix.resx deleted file mode 100644 index 22e46cf0d4..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201505052021483_PdfConverterRoleFix.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201505061813010_CountryMultistore.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201505061813010_CountryMultistore.Designer.cs deleted file mode 100644 index a39bd3bf2f..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201505061813010_CountryMultistore.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class CountryMultistore : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(CountryMultistore)); - - string IMigrationMetadata.Id - { - get { return "201505061813010_CountryMultistore"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201505061813010_CountryMultistore.cs b/src/Libraries/SmartStore.Data/Migrations/201505061813010_CountryMultistore.cs deleted file mode 100644 index 9ba99664ba..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201505061813010_CountryMultistore.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class CountryMultistore : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Country", "LimitedToStores", c => c.Boolean(nullable: false)); - AddColumn("dbo.Forums_Group", "LimitedToStores", c => c.Boolean(nullable: false)); - } - - public override void Down() - { - DropColumn("dbo.Forums_Group", "LimitedToStores"); - DropColumn("dbo.Country", "LimitedToStores"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.ContentManagement.Forums.ForumGroup.Fields.SeName", - "URL-Alias", - "URL-Alias", - "Set a search engine friendly page name e.g. 'the-best-forumgroup' to make your page URL 'http://www.yourStore.com/the-best-forumgroup'. Leave empty to generate it automatically based on the name of the forum group.", - "Legt einen Suchmaschinen-freundlichen Seitennamen fr die Forengruppe fest. 'Tolle Forengruppe' resultiert bspw. in '~/tolle-forengruppe'. Standard ist der Name der Forengruppe."); - - builder.AddOrUpdate("Admin.ContentManagement.Forums.Forum.Fields.SeName", - "URL-Alias", - "URL-Alias", - "Set a search engine friendly page name e.g. 'the-best-forum' to make your page URL 'http://www.yourStore.com/the-best-forum'. Leave empty to generate it automatically based on the name of the forum.", - "Legt einen Suchmaschinen-freundlichen Seitennamen fr das Forum fest. 'Tolles Forum' resultiert bspw. in '~/tolles-forum'. Standard ist der Name des Forums."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201505061813010_CountryMultistore.resx b/src/Libraries/SmartStore.Data/Migrations/201505061813010_CountryMultistore.resx deleted file mode 100644 index 8a4e44f84b..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201505061813010_CountryMultistore.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201505272236124_V222.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201505272236124_V222.Designer.cs deleted file mode 100644 index eaf7caaa90..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201505272236124_V222.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class V222 : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(V222)); - - string IMigrationMetadata.Id - { - get { return "201505272236124_V222"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201505272236124_V222.cs b/src/Libraries/SmartStore.Data/Migrations/201505272236124_V222.cs deleted file mode 100644 index 0798205fb5..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201505272236124_V222.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class V222 : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AlterColumn("dbo.Customer", "SystemName", c => c.String(maxLength: 500)); - CreateIndex("dbo.Product", "Deleted"); - CreateIndex("dbo.Category", "Deleted"); - CreateIndex("dbo.Manufacturer", "Deleted"); - CreateIndex("dbo.Customer", "Deleted"); - CreateIndex("dbo.Customer", "SystemName"); - CreateIndex("dbo.Order", "Deleted"); - } - - public override void Down() - { - DropIndex("dbo.Order", new[] { "Deleted" }); - DropIndex("dbo.Customer", new[] { "SystemName" }); - DropIndex("dbo.Customer", new[] { "Deleted" }); - DropIndex("dbo.Manufacturer", new[] { "Deleted" }); - DropIndex("dbo.Category", new[] { "Deleted" }); - DropIndex("dbo.Product", new[] { "Deleted" }); - AlterColumn("dbo.Customer", "SystemName", c => c.String()); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Products.Availability.IsNotActive", - "Not in assortment", - "Nicht im Sortiment"); - - builder.AddOrUpdate("Address.Fields.Required.Hint", - "* Input elements with asterisk are required and have to be filled out.", - "* Eingabefelder mit Sternchen sind Pflichfelder und mssen ausgefllt werden."); - - builder.AddOrUpdate("Admin.Products.BasePrice.Hint", - "Please keep in mind, the base price is depending on several factors and will therefore only be calculated reliable in the front end.", - "Bitte beachten Sie, dass der tatschliche Grundpreis von verschiedenen Faktoren abhngig ist und erst im Shop zuverlssig berechnet werden kann."); - - builder.AddOrUpdate("Admin.Help.Forums", - "Community", - "Community"); - - builder.AddOrUpdate("Admin.Help.Documentation", - "Documentation", - "Dokumentation"); - - builder.AddOrUpdate("Admin.Common.About", - "About SmartStore.NET", - "ber SmartStore.NET"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201505272236124_V222.resx b/src/Libraries/SmartStore.Data/Migrations/201505272236124_V222.resx deleted file mode 100644 index 671dfc5bbc..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201505272236124_V222.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201506181858349_AclRecordCustomerRole.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201506181858349_AclRecordCustomerRole.Designer.cs deleted file mode 100644 index 387e91d128..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201506181858349_AclRecordCustomerRole.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class AclRecordCustomerRole : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(AclRecordCustomerRole)); - - string IMigrationMetadata.Id - { - get { return "201506181858349_AclRecordCustomerRole"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201506181858349_AclRecordCustomerRole.cs b/src/Libraries/SmartStore.Data/Migrations/201506181858349_AclRecordCustomerRole.cs deleted file mode 100644 index dbc7b21d2b..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201506181858349_AclRecordCustomerRole.cs +++ /dev/null @@ -1,153 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class AclRecordCustomerRole : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - CreateIndex("dbo.AclRecord", "CustomerRoleId"); - AddForeignKey("dbo.AclRecord", "CustomerRoleId", "dbo.CustomerRole", "Id", cascadeDelete: true); - } - - public override void Down() - { - DropForeignKey("dbo.AclRecord", "CustomerRoleId", "dbo.CustomerRole"); - DropIndex("dbo.AclRecord", new[] { "CustomerRoleId" }); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Common.Error.PreProcessPayment", - "Unfortunately the selected payment method caused an error. Please correct your entries, try it again or select another payment method.", - "Die gewhlte Zahlungsart verursachte leider einen Fehler. Bitte korrigieren Sie Ihre Eingaben, versuchen Sie es erneut oder whlen Sie eine andere Zahlungsart."); - - builder.AddOrUpdate("Admin.Configuration.Category.Acl.AssignToSubCategoriesAndProducts", - "Transfer this ACL configuration to children", - "Diese Konfiguration fr Kindelemente bernehmen"); - - builder.AddOrUpdate("Admin.Configuration.Category.Acl.AssignToSubCategoriesAndProducts.Hint", - @"This function assigns the ACL configuration of this category to all subcategories and products included in this category.
- Please keep in mind you have to save changes in the ACL configuration before you can assign them to all subcategories and products.
- Attention: Please keep in mind that existing ACL records will be deleted.", - @"Diese Funktion bernimmt die Zugriffsrecht-Konfiguration dieser Warengruppe fr alle Unterwarengruppen und Produkte.
- Bitte beachten Sie, dass die nderungen der Zugriffsrechte zunchst gespeichert werden mssen, bevor diese fr Unterkategorien und Produkte bernommen werden knnen.
- Vorsicht: Bitte beachten Sie, dass vorhandene Zugriffsrechte berschrieben bzw. gelscht werden."); - - builder.AddOrUpdate("Admin.Configuration.Category.Stores.AssignToSubCategoriesAndProducts", - "Transfer this store configuration to children", - "Diese Konfiguration fr Kindelemente bernehmen"); - - builder.AddOrUpdate("Admin.Configuration.Category.Stores.AssignToSubCategoriesAndProducts.Hint", - @"This function assigns the store configuration of this category to all subcategories and products included in this category.
- Please keep in mind you have to save changes in the store configuration before you can assign them to all subcategories and products.
- Attention: Please keep in mind that existing store mappings will be deleted.", - @"Diese Funktion bernimmt die Shop-Konfiguration dieser Warengruppe fr alle Unterwarengruppen und Produkte.
- Bitte beachten Sie, dass die nderungen an der Store-Konfiguration zunchst gespeichert werden mssen, bevor diese fr Unterkategorien und Produkte bernommen werden knnen.
- Vorsicht: Bitte beachten Sie, dass vorhandene Store-Konfiguration berschrieben bzw. gelscht werden."); - - builder.AddOrUpdate("Admin.Configuration.Acl.NoRolesDefined", - "No customer roles defined", - "Es sind keine Kundengruppen definiert"); - - builder.AddOrUpdate("Admin.Orders.Fields.OrderGuid", - "Order reference number", - "Bestellreferenznummer", - "The internal order reference number. In contrast to the order number it already exists during checkout that is before order creation.", - "Die interne Bestellreferenznummer. Im Gegensatz zur Auftragsnummer existiert diese bereits im Kassenbereich, d.h. vor der eigentlichen Erstelllung des Auftrags."); - - builder.AddOrUpdate("Admin.Configuration.Payment.CannotActivatePaymentMethod") - .Value("de", "Das Plugin erlaubt keine Aktivierung dieser Zahlungsart."); - - builder.AddOrUpdate("Admin.Orders.Fields.PaymentMethod") - .Value("de", "Zahlungsart"); - - builder.AddOrUpdate("Admin.Orders.Fields.PaymentMethod.Hint") - .Value("de", "Die Zahlungsart fr diese Transaktion"); - - builder.AddOrUpdate("Admin.System.Warnings.PaymentMethods.NoActive") - .Value("de", "Es existieren keine aktiven Zahlungsarten."); - - builder.AddOrUpdate("Admin.System.Warnings.PaymentMethods.OK") - .Value("de", "Die Zahlungsarten sind OK."); - - builder.AddOrUpdate("Checkout.NoPaymentMethods") - .Value("de", "Es sind keine Zahlungsarten verfgbar."); - - builder.AddOrUpdate("Checkout.PaymentMethod") - .Value("de", "Zahlungsarten"); - - builder.AddOrUpdate("Order.PaymentMethod") - .Value("de", "Zahlungsart"); - - builder.AddOrUpdate("Admin.Configuration.Payment.CannotActivatePaymentMethod") - .Value("de", "Das Plugin erlaubt keine Aktivierung dieser Zahlungsart."); - - - builder.AddOrUpdate("Admin.Configuration.Payment.Methods.Fields.SupportCapture", - "Supports capture", - "Buchung mglich"); - - builder.AddOrUpdate("Admin.Configuration.Payment.Methods.Fields.SupportPartiallyRefund", - "Supports partial refund", - "Teilerstattung mglich"); - - builder.AddOrUpdate("Admin.Configuration.Payment.Methods.Fields.SupportRefund", - "Supports refund", - "Erstattung mglich"); - - builder.AddOrUpdate("Admin.Configuration.Payment.Methods.Fields.SupportVoid", - "Supports void", - "Stornierung mglich"); - - builder.AddOrUpdate("Admin.Configuration.Payment.Methods.Fields.RecurringPaymentType", - "Recurring payments", - "Wiederkehrende Zahlungen"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowManufacturersOnHomepage", - "Show manufacturers on homepage", - "Zeige Hersteller auf der Homepage", - "Specifies whether manufacturers are displayed on the homepage.", - "Legt fest, ob Hersteller auf der Homepage angezeigt werden."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowManufacturerPictures", - "Show manufacturer pictures on homepage", - "Zeige Herstellerbilder auf der Homepage", - "Specifies whether manufacturers are displayed as images or textual links on the homepage.", - "Legt fest, ob Hersteller auf der Homepage als Bilder oder textuelle Links angezeigt werden."); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CustomerNumberEnabled", - "Customers can enter a customer number", - "Kunden knnen Kundennummer hinterlegen", - "Specifies whether customers can enter a customer number.", - "Legt fest, ob Kunden eine Kundennummer hinterlegen knnen."); - - builder.AddOrUpdate("Account.Fields.CustomerNumber", - "Customer number", - "Kundennummer"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Tax.VatRequired", - "Customers must enter a VATIN", - "Kunden mssen eine Steuernummer angeben", - "Specifies whether customers must enter a VAT identification number.", - "Legt fest, ob Kunden bei der Registrierung eine Steuernummer angeben mssen."); - - builder.AddOrUpdate("Account.Fields.Vat.Required", - "Please enter your VATIN", - "Bitte geben Sie Ihre Steuernummer an"); - - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201506181858349_AclRecordCustomerRole.resx b/src/Libraries/SmartStore.Data/Migrations/201506181858349_AclRecordCustomerRole.resx deleted file mode 100644 index de6d366a44..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201506181858349_AclRecordCustomerRole.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201506211043073_PaymentShippingRestrictions.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.Designer.cs deleted file mode 100644 index e5e31c1659..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class PaymentShippingRestrictions : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(PaymentShippingRestrictions)); - - string IMigrationMetadata.Id - { - get { return "201506211043073_PaymentShippingRestrictions"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.cs b/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.cs deleted file mode 100644 index 7f4f7300a3..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.cs +++ /dev/null @@ -1,88 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class PaymentShippingRestrictions : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - CreateTable( - "dbo.PaymentMethod", - c => new - { - Id = c.Int(nullable: false, identity: true), - PaymentMethodSystemName = c.String(nullable: false, maxLength: 4000), - ExcludedCustomerRoleIds = c.String(maxLength: 500), - ExcludedCountryIds = c.String(maxLength: 2000), - ExcludedShippingMethodIds = c.String(maxLength: 500), - CountryExclusionContextId = c.Int(nullable: false), - MinimumOrderAmount = c.Decimal(precision: 18, scale: 4), - MaximumOrderAmount = c.Decimal(precision: 18, scale: 4), - AmountRestrictionContextId = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .Index(t => t.PaymentMethodSystemName); - - AddColumn("dbo.ShippingMethod", "ExcludedCustomerRoleIds", c => c.String(maxLength: 500)); - AddColumn("dbo.ShippingMethod", "CountryExclusionContextId", c => c.Int(nullable: false)); - } - - public override void Down() - { - DropColumn("dbo.ShippingMethod", "CountryExclusionContextId"); - DropColumn("dbo.ShippingMethod", "ExcludedCustomerRoleIds"); - DropTable("dbo.PaymentMethod"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - context.MigrateSettings(x => - { - x.Add("localizationsettings.loadalllocalizedpropertiesonstartup", true); - x.Add("seosettings.loadallurlaliasesonstartup", true); - }); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Common.Restrictions", - "Restrictions", - "Einschrnkungen"); - - builder.AddOrUpdate("Admin.Common.DeleteAll", - "Delete all", - "Alle lschen"); - - builder.AddOrUpdate("Admin.Common.RecordsDeleted", - "{0} records were deleted.", - "Es wurden {0} Datenstze gelscht."); - - builder.AddOrUpdate("Common.RequestProcessingFailed", - "The request could not be processed.
Controller: {0}, Action: {1}, Reason: {2}.", - "Die Anfrage konnte nicht ausgefhrt werden.
Controller: {0}, Action: {1}, Grund: {2}."); - - builder.AddOrUpdate("Admin.System.Warnings.SitemapReachable.OK", - "The sitemap for the store is reachable.", - "Die Sitemap fr den Shop ist erreichbar."); - - builder.AddOrUpdate("Admin.System.Warnings.SitemapReachable.Wrong", - "The sitemap for the store is not reachable.", - "Die Sitemap fr den Shop ist nicht erreichbar."); - - - builder.Delete("Admin.Configuration.Shipping.Restrictions.Updated"); - builder.Delete("Admin.Configuration.Shipping.Restrictions.Description"); - builder.Delete("Admin.Configuration.Shipping.Restrictions.Country"); - builder.Delete("Admin.Configuration.Shipping.Restrictions"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.resx b/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.resx deleted file mode 100644 index 43c6122456..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201506211043073_PaymentShippingRestrictions.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201506261018157_Merge.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201506261018157_Merge.Designer.cs deleted file mode 100644 index 8d36b523ab..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201506261018157_Merge.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class Merge : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(Merge)); - - string IMigrationMetadata.Id - { - get { return "201506261018157_Merge"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201506261018157_Merge.cs b/src/Libraries/SmartStore.Data/Migrations/201506261018157_Merge.cs deleted file mode 100644 index d5201b31f3..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201506261018157_Merge.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class Merge : DbMigration - { - public override void Up() - { - } - - public override void Down() - { - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201506261018157_Merge.resx b/src/Libraries/SmartStore.Data/Migrations/201506261018157_Merge.resx deleted file mode 100644 index 01a895415e..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201506261018157_Merge.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - H4sIAAAAAAAEAOy923LcOrIo+D4R8w8OP51zYh972d0d0btjrTkhyZKt2LKsVsn27v2ioFhQCS0WWYsXXfrEfNk8zCfNLwwA3kDcbySratWLXSISCSCRmUgkEon/7//5f3/9Xy/r5M0TyAuYpb+9/fDul7dvQBpnS5iufntblff/869v/9f/9X/+H7+eLtcvb360cH/CcKhmWvz29qEsN397/76IH8A6Kt6tYZxnRXZfvouz9ftomb3/+Msv//7+w4f3AKF4i3C9efPrdZWWcA3IH+jPkyyNwaasouRrtgRJ0XxHJQuC9c1ltAbFJorBb28X6ygvF2WWg3efojJ6++YogRHqxgIk92/fRGmalVGJOvm37wVYlHmWrhYb9CFKbl43AMHdR0kBms7/rQc3HccvH/E43vcVW1RxVZTZ2hLhhz81hHnPVnci79uOcIh0p4jE5SseNSHfb2//XkXk0/cUlm/fsA3+7STJMfCAwieEzAgMpu8+wRzE6OvrOxrRv71hwP+t45KP7z6+++XdL//25qRKyioHv6WgKvMo+bc3V9VdAuP/AK832SNIf0urJKG7jjqPygYf0KerPNuAvHy9BvfNgM6Xb9+8H9Z7z1bsqlF16mGep+WfPr59c4kaj+4S0HEGRRIyqs8gBXlUguVVVJYgTzEOQMbPtc60hf9tW0OsiETq7Zuv0csFSFflw29v/4Jk6Ay+gGX7oekAIisSQFSnzCsg6KC60U+giHO4qXnIt21NU7DYJNHrRRZHSYCBGjX2LV+CXDeFakznxSdwHyGebNEcZ1kColSD6Nf3vUQp5ewqel0jBvkKyods6SBoTf3i3QDRQdDkbQ0ItXgtSrDWyN6ff/llFOk7fYmTagmWJ2TSQX6NOOt8WSiFI4R0dO1maGXNX9VNfjQdvFmbiwe42aA2avJPMdpmlKQDWAKQ/VCCl1LPcmq0X2EK19WaqJijNW6jxfcJxHAdJW/fXKE1EDa20l/fvllgzYe4SYs5ehkJc43tGhSI1nHpSgtz3ZZnyyouj6t0iRgbydlRiRq+q0pwBpMSa2ZrdXeCrLgkW73ToT4oQHlbPdF8ZaAjejBEP6Kk8sZ2XiD5QCY+skDB0m7dRsguoye4IoSW0u3tm2uQEJACqbN6O6FlyVu6/lmerbG61wsJVe12kVV5jFeqzL7uTZSvQBlKkkOK7kFW5W01xPKViJrYgZC1Ozo/LMhEj0OubS2+K4BEJC2jFbCWfLsd2Z+DWCeLhywvzbZh6GeABmvN0CmKwpdKX+AS3DxU67s0gokvsh9o4u8S75kjyqF4cFD+o+0jT3KAFcC39HsZdyyPvtzAtf3G4ftm6YFMusAxq0cx1zLXLlUuy1y7RNot6k0LZgO+ZeooBjkE1a/fDLxozVYNxm4YRgMw77qq07aGhr95cTAqFOqxJhFuztcWuIpy1ObnPKs2qCNh7ItmEThPl/AJLqsoSV63ZCm3bHX6tR31b8r2jpZrmJ5ka+xPG72xlmvBGi3J/ltONDnP39Iv2RpcBbAVv4IyQiL+nOVKj1YYmxE3NuU04/ZuYKl024cZ2VGSZM+dOxQ8QfDsbaAebTZ59gSW10h3pqtFtfbjm8usDIyxRXeD1HvCDNq7j+GQLqq7fwIkf9lR7G3nX8A1LHHnyErmPcWLx2p8oYvS6j6KscGQo1WvvERbnn5TMFqznxGLjd7IefEZ3pcnUe69YWrxhLAtrsHvFczBt/IBUbxW/t6c0uDsTRWVtv4Q5ujjqMKWbAljbMkcLZdMH7zHdF58yp7TJIv8t7sNHt+Z+54mtYC3CL3HiGalFrhv9xxOR/9Tg+b0ZQNzYlZ/il5ZnGYojuISPhEUIdj+S1QsImTjgFCzOsTGdU/bm+8FyI9WOQC0mefamQGyG/Ayvt14XlyDuMpr7L7ao0F08honoO6Ur46jMV6BHGbe0tfhJGs/QewpK+cFPrY9TXEVb3Y8L87Q9LfnwN7W3XIJseRFSYvx5AE7IMzdytbdv4leTl/QLsRbFs6L08KbnKg3J2g7tMrwQb7nkXaUoq3QefqERBOha4/oPc3GMosfgx0ZYG8swXj0FEFUFyYUVk8vr7CnzgsWTAOO/CJ7rkeNVxqEzXdS0AYB3r+S7ftZlrd9PAZo9+V50BTFjxn2luPYTO9TYbwbxBjPa1qiHUi36fW2KIhPvwniCDNJNcY6eCMMRkIAsGyQQTCBsYpkAXfwuHo9rsqydy54yBaG/gmLhwQWZRikjfAnADEv0uuD4xlXnCfIOifoYOx//kQjCb4CfUuW4zbQbExOssLidNZ2WdggPFFiORBznIsS7dHbMzHhMZk5rtN06YGpdWedpvhMLQh/NWprgBmMzBSNYpu0TbT5uIGglklvhY/WUFCU3dHhSba+g2lzihaQCVGf21CE4mizSaC/xfwTwNXDeKI43McER/8TLkfE/mVc2nQrja8+6RD5KZNwAQnhYiRAAlzi7LY3QIIfIHwC+SuubOk9oS+/WFY9jgrQrD5Btt0dvq8gKqoc1Bd7FLHXAYzJrs2wYcwdWvxjiNqMtCRogZxoaTxQYdzrTVAHyHGcRCjHxwAppkZonIuHrPanIMUXLoi2WRK7JVIYq9KW3jbQN1nvK++DVeRQXASRAtQ2YIjWBqpAm9shIBdjMyiXhdcMgRzDgfoYHcOILvcAJ23kliQgynJEjcOLbIjlA2rdYopAJxZENgAOzrHj9CGisu80oKL/IjDZGISwjuO4ggSPcggNjKL3DISs4yyYY5+7Q3B5l2sQRY+HALIOM1CO/SU70HsYExA6Xlfef3EVxXjUFWTj09RyHO9NtFIO7nYAx+tTqliqTmkYR236I8ohMupE+0hl9xX1FBNkUEs2SyZVHaeKRW01cIvR2g7ReVzD2+4KHhwC8kxIl0u5cABky4bs2bCisywo390hhLTDDJhtl2kfjqC7XbGIM7hCjhd4CNHsG0cg9/dSHJIeNEZt9+sQhSxvSxOT+3GcmNx2ZkJEanwv8F4p0G0j+f2l4E6ttinbXbn1yW/jMnPwczVeMoeaTXRVcZJVG3zBeek9Mzwm8fFbmNM3MjMkPpJImS+P9pgw8WzjQHTb+JtMsxXjN950BdVevofjliUlsO3iJHAxjOGR0I+Bc14YeyQaVA3nk3Qg6kGIawjGIQKUD0UI7XUdqN13e9wHalEclmJ5W5qleKTrMfi2SqCWdQ786e5KHGcl4tNJL+HglGajr0ytGIW6fnO4MBNkZPX9u1ABgI2nzfKkCt+fWsB/Ab+2B1d/ipuszt/B4naOyGnwfBsGj3nsOwwigK6jdKWM2QqWQiDgTbYR4he2+ObQtp2C72XCAY5UJKHeDwiev6o3VY7aOPTho8OWRX8AKdjfGHthazUt7H1/UtUC9T1myzhLngPwu8vf9sDadv8KljB619Q/GO4K9VWT6BimUd6HrDd/mQiSLs5wDTBO5RI2ys15kJ3BBKTq3cGfAl1+vATP1no76AkpL6piCO0JKSPXbmI7wOmfieMgxlNl+ZKZ79PZIE5LlrM0SI6rZEJjKcFhYxt0fQ2RPEfgfwtyikXhO0ixUm4IuQLIH0v26yoBRqmKw/gvFhsw+unQMUwSHF3Y5iD2vGXYXv4Mg65NkUsM9UF2ZivPSI+m0/Aqd0CYyaMapTNdT9lwOxt6b0volhu/gOU8nb4gRVU/EDHZXrSPMxjvpEa6C1We67itPYNoV4/3ImhEh9VG3pZmIRjnvYiTLMnyL+CFpGmevPWdeELCdp8V4IiTwXSQGnlbgXZaoQ5czoszEOGJaA2EGf3H0pWq51CDWPseWB5s3/2QbIp4wBG2cC63A3T9DbKLu+LD9f11A43toB9G1w80uf8QOmLIrYb3WoaV1JdbBn9I5FBeYQT94XNDx6T/QXRJICVy0B7bHEc1aWQLxQmHUKDtCgU6BO/sYPDOIZ5l9+JZnE65hos7f8YlKucMBSFQiN1GfWXVf59R4wlmI7zBGR6a9kl0S6NiyLt1afl2Cltep71DubAx4SZJiFqnLfcjyheQbO6r5B+gIElGgyC7zFxw+djwtve9JXa75Fa4ZUdrLA0tUlAUSFZ1KQ64OreMPEtGJK2hvtIur+Z1dtBKtYv6aW2O7tdhfyJvq6XR5wp2rda/HbI4a4KzAuWjPF1Tz4qN18xVVBR4AzK68m0bOsvydeQf8dNgW0TJ+OvGpK+9BEyzfHR/DxMYYKNKsu9672YCWdbnRR0QchQPnlF0jtE3iS4J8yDzRVSU55uj5RLtd8aPDQi6b8Bdv8hWMHW8nIzrtzmcpSjcL+XWJJWYDK3yv6XAqNhwrpSPDudBbE2dJuinm3tVN1lYQV+HIPIOM3C2vWZ3HapuNzC3vU3D95uF4YwuKaDtTQE6dklDbgZUQO0BhJzYQzBbWp++YJMmSo6q8gEbNXWSoWsQ195BwQhUNYTTYFSBmxOzWrYThMyAan2VFZK7KF2xcCB8KddrAYhTF2+yDYwVfSTl8k4Oi8W9ZGBsu0lcTOIekiJh54YlXL+YYtsuXQO0y0hxjBWQTfAARNhFMQTXVQmYfZefo3x5lcG0LL7AopQd+wvgJL2XwwnGoAC2HUkbg2i01nDAAvXHwMgVIAtoneGJSrwpz9rIQgnJLwXiaC+H9NradwS139mjjUaWvmsQHHb18rbOYF6Uk8SFY/N1koZ0u/5AW4NsvYnS1wnaEQela3ZkiDtwotQnmMa2p3on1NMjI/oDiGh+mKqhj6M39F9wg22lKNEkewrkCnrIUqB9RjLQy8Dt+3lbvt+W72RqGRIuhe1C28H0CyBTxC3abLn1Wk0LqbJzDCTfxQGAtKNDKL/kSi25PKL1GxyH5VnelmbBNE3P4xKRUTSOD28PKkEWKnP9zXN2ATARz4tMo2g/BlBINw85AKYN/ilAg0jRghzGTGOejy7/iLxdvOFiQ4IFdfhGvyj2sexdtO4WnHAvRcPc9pVr9cYk/tBDC3ZX2irW+1t6IZAMa7BWCFZHIQDfeSGU17ZwSA+HFahFwEzdYR1SqKW9D04Np5fOVymidf2SrHdM3ulLnFRLJOuDi8Sqk68w52yNpJLmsQiQ04QX+zNnAyVL6ckp1CtrHFtoZDd1NbTdPezlAaaDttIyr/dDt/MY33d3OXiCkUbrhcnMuQ1mndO23dE84YRfacR4BaR1B74B4tIaXAeZV8h8QyrvG3MGwSVhbgqc4xSZiPX26vlDbTRB+AAI2VmaNFLCSayPk2zVxa9ZizSuXbyjcGxHlHvTmUkix/HgsYM8nAnZYhSyFEXr2x6wZydROcdKQiBvNqq74sFDGMFhLZC3dRGlqypaecdshrq9YZvdKluGOIBzuoZXM7v3trVd2Rp8J4JnVm2N8awMj7R+Dm5kxTfPUzaHi7HbeMVypANOap0Se1GcVkPWl6tcMk272upmaTcJ1h5q2MdBoXC5HkJ4rdV9J6zXapJmCv6LAL9r8RyWbHlb8/ha2plpqK7c8o3RPqr9ewUWYIqjzLMkWp2v0WBxcmztXY0QV0XL/X2wYQRvV52Zrp6Ma1AQ1SZW5iJIocZUAnLqXQ3tdWQnQu2tVQU4DxpWr+sCpLcltNaokJGe4Gxb1yWRHGmTdF5g6bpKqhVMOSWivZSYVbFI+QQxnQIpBdakMlMhTkrhSnI7PFQiCQrldnjbBr3zFcOfUdEMMFzgy9Xw/v+UqQOU2RH0GQecGPASPBfuLl1c+x2FYjt4rPXoTpJ3ZEr3MaY0vlsSzn3cYhQyOjWxtz1gz9aico6JhUDeLFt3xZ1fcf2DoSRva8e9x4uHLA8hjpbNnhHVNXWrwXZ08zhp/4g+963yqB6c5kFG5rZl6ZZE0TaFKxSurkMI2ws41Pos9rQ4WQGsX0VpKrjtnLIk+ZGVZEsWk8RD1tYARlG8YxFthw2Le3WUFs8OwTzyrU2HU7yvYQhxS8NTmxk5GL+DUcD67ZuprnnMe43iYAiq+XDkGNKRzKL6uua3e8R//Qq7RQ/6ZVhFSMSwEZEaZih6VJFQ3Ohy60SKjLhKUif66wkuSaKBTnHWE14a4qAb5G2F2iTOox/qhG2NpTv+XfVgm7Sgj9WTzddnkl7nJsOacnuuNs6zGx3tQqVyF0C0tWgHMCgQ6nt3y7/XsXI9b7gQifS5aKHyukyAUQe5SYARHfS6vC3dqeZf/jKK8ywHINgN+VCpTxGiRqNhPMwUaFRrkGSnbZbSmvk9lw6TFKWms2usZUC+hkWhyUHIQsmzKaoheU2kBvczLhncLtewQVzlCCtHgIN+ctZPo9qL8+jG/lHA0Vv2SIUaTIg5O8dM5p2EWJWW1MvaUCE+SLeC1wNdYpwmBV47y/Xo7uEECcLaJhvDZMKWv2FeJtw3TVNHcQyKYpoGr/AFaLRpNVLzod8yV14MHSk/M6tk7bI6O+naLpmyg2IldYs+ofNBhcrbIpmgvZ9oDqSHA0VG2e6brvRX2MOkj9j5i+fWOdJZzaFIo27awT6BuaaLNKCok1QmdHk3KSB/fdZ0xkuhERwHjSZvi1BpazQani2MwBdRk5xQFYtieAvJ/my0eTPBp/s/IHj2RIETYjslRJDgCTXFLb5a79odIezHgmD3IIVQ10oerbBaE3QdbICEvSM/VV2rAfyXAE/tf1D88rYIgT7nWbUZ+ZR5pLtaUwY3XvbPzHgRKsjigDVokH3IPi4Rf6xXnXsZlmvzWxqI0eZUmVib0wD+2rzphJdKJzgOel2hZPZeG++ljE8VLVPLkr+6ED6UJtInTuqimVtrTVE/tVb/d1AS8rYIgSZ6doW05f2uMxnwtvgphq92ejsrhq+yWT6oRMiLM81WwToSBttV9IpvpQRFVqfRHvMkScIxJ1WegzR+neT5pbax66gPG/0EYriOkrdvrnL0i2ixtx/++vbNAmdQ+O3tX51lYRiI5TtPP6JySrWyqO5KpBKT8zRObjBaI2L92XpYg8Zw2vopGrvBjaG5Ia9oTzXCQaPTjLTROtOMsGls0pGhhixE2b6xgXJEqwjEJkKUnAEwNk3lLY9NYHnLY1O7wT9+ak/CQ6MzaSvr47ZC1MpoTSDDu0qXYHm0HnUk9CvAP0EOENsFSK/+AOLHrCqPSsRHd1UJptxKc40X/7me4HXTxvQIdc3p6P4eJhDJZLD9wWZ0GpD7QXhTg182zgHSXyeIt4a2kzNLIUx0VP14E4m7PIkZztFmvEQGUfEIlrIpGXWEJ09P478kixs6fdnAnDgSvmZp+TBxm/8A0fj0pOWrfuToE7iD3tdWKFRHMVk3v2TJcgL+4BueiDGpho+j9HGSDTDT5iQqhm7z/GTK5rr3haZr8vwumsC4aFZTYgB2qfDGlvsKbQjyJo3rTR6lRRTjn71pMHnTk4iMrPFrUFTJ+GQ/iTbYkT4twflGJxrtorrrbPRph3xV5fFDVIApHfhXEXS99M2+pjr2vDTN4f05Ujibqj4tmdxr/AkkoNyrl7cUO+FrgI/eqLvFRscWX6LiEjw3XpzLDN+ziQevEvofdQ5PaYRHnkSMblnA/thTVM5FSgiBbGPylHGDdQuikMFhiaRrzoGCrW/oe4F26V9gUWaSBxNFgLfNIW7fWTkUd5ysALV9E+QzvCcbOe0gRIC33wuw/AnLB24wemhuUAZVbAdHauGcbwr+JinhuP4zRVxn2XKnnl3WKaJkPcPFkp71ReKeUeW2PbsGSwDWYEkrsVPpW6A0lJYptMDcYPQ1rJ9fR32XJx1sS3myD0uED8VTxS692hhpYw6S1XYMgETpsVBe4WtiRejw2nKNphAqzEPAiryt7hjU08NbazVfN/E472z1JzGmK2xfQ7PIth85WVFD29oLjU4Zw0Aw6vhQczlJuthacA1EE2E7iLm8rZZevhI6WEFDIBu8CBP8YHEkhdJS09za7WtoDN32IyeXamhbhcIYWlMY7UYDEtt0XgrHX8kcFIu8rdZvRmkF0XMfZtopRKDcyKEK50XbWZItLQrgjWoRnmTVZiKn9jUixgZiB9EUXruutWkS3CxAivexU4ysbmqaYX1Fmy0qf+po7ZwXPXcQ9+Hc/taRHFD2S7KR24lbwE1HI9akyvHcyurwI5KASldlGbzXkkz10OsGyuEtJJPNOCKS9+2QIHb+Vf34my+av1dRM3qvTUcKy6scxqOH8HYNjR6xO8VoJhnJ2BHGrW+jthHHJtmwtbFpN0uY67ThrZ+y5zTJomWA56zOixZZMDP+AglIWoAWr+UWCGvrnwCuHiy2Lpq7dFW6TAAys6LxAxlqBX9C8uSF4m+poXRUFFmMo5KXrbEiPvUIaCXJLD+dVeXv37U4aBQe1wgOIo0N0npWNd3qoEQdawoVXWsh/O3KS/IWhJddiVEc7Ep5W0GswUvqzY7psiu2VzGzPthjK/e0Gk1gdrAvFDfBwb+TsAkO1r2yTgvwHWRQwXeBrtrXBA+B4zhKojQGntuyYvRbZlM50EbSDMpANlG0iyisTQXHqQ0lcPgTp8ABQSajCXjc1IbxuDyp0UTUdDFEB/0nbyuIDXKTR/EjovhEgd7kkm7Y3R3hGeAaPv4JJPAJ5K+O1Se3fYyD61ihl8Te2Yb8yQNBaYjbXgfwHRwASIMAh1BeqZ5olCGU0sHprpdIQqcgkXSiQ/Rp/OVaYQgpBzJ5FUuL434FsWN6DX7Hb0a6OwgGaA5yoJCDEAnFgglBqO1SmMOnaxAVWXqW5TU3Te8GafgXELd3kAMCvw6oL0+ZTW1zA3b8vDGLMrq/v8ymSFFztFzDVH+198+/BHkQZKDbwuSV26Irdq47aoomkr20CEKw7xSCeW42M2KcnaBFyC+egsV0WNlU8h9gZbuK8sa+sTwhrM/zHCrSUxwiwjKYBzJMWMg8KZaQSIIc8RAOixjNYxnG6NgFVdwzt2R3MdRStzQ8vceQggl2GnLYoHeMuYZEa4kUSN9vZ4+s6nyXa0VwzCuD0Xc5yKFvg2SxQbLW3nHvdIHLiVRURkm2eqfEe1geR9fnYsJ/2wwTgTiuFTh51RlM0MACvNmO+Pr5W9oM+4o6WnJPguWcgN9JypWsLhJ5owr8Y8hGtax9swo+cRitGp0pCVRYLOmiROW3e1AO1VlxqtAe9Katwhv58ahxw2zG0GBiMknedZ9b1NnznQBaw08viq08ZbsSPJQBaF+dtw0dcIyg/IKrvYPC+yOoJhX3Ss6Lpxc57qzZQ2x9dmo30cp/W4aQHETLWbT+HOiRMt1arVyVb/upLATLLV0sW0cHMCF480eUwyjt07WfZOs7mEaeFrEe+YGX5W0tHqsArKy5e19C1SFomEa+Rml1H8V4EvMrNNvaqLMwzVr6pjW3m0hHQmH7CZfhkH0Je20qKgChnG00siFa/GOI2ugQ56go4CpFUgQJH50vVWfQH8IcC7dRgti3b3nm1J5c4EuwtjfhCnIlz9uZtpPHS0hlxo9hjn2Ir/NbVX67J0jr815rosqTqlHMoVrtFYvf7RAHZwmYVpU51ozrhzw5MWhb7lg1qOUyWFcXKy3FroMd4rAaMV3VZdiD+iHOmtjGwpuGB3twdIXeoAnmaL0BLyX6tN6Mnx4fp+b5vYK5/2V1Sl7SMs+SEHEhM51esSJkoV5tdaqzImV5zmU8Qn+QaR3bEaqdrQZDZRGTPI9Kt4Cwxq1U82ppoKwvcyzYIQnheQi4mBxWEYNV5CiBkWbvFEBTz+TiHi31i6+l1KR5DW0uEbQHbpe3JaGbfxzNDgmRJjoyS7J88XsV5aC4Xt2NPqjat7T8J5IEOrg/eOxofd10gobOC4QJLeQgDpBEycOGZDAR1RDCrL2A6SMO9J0zh52ttbU9dpah3WlmrDmtSG1CLofV5ytYwuhdi+CwzijktqGRd8LJ730Gte954q1OeFSjZ6c7hmnUPzrZ/mXSnG6hSks0GRjt6GM5gwlIp1h9T1/QmIrw9qpomboEz3b8ZKxibiDIm9M2Zwu3w3FQM/K2Aq3CQS5ytfcu8LrmeDrm68gMevdIe7sFD1RoWHSsezsE7U0HMQRnHEjAQh7P9E0I3IRcoaKHKlegeTAiZkSX5Bz4S1H/d9AX8rYmCodibRiltTFSo4siOU0xvPcebAFixCthBqGJ3MgK5bX9QHEMF9kq6+ImZjvjYMderpPjbPk6xXPOteXYHnlfgvI5yx8Dz66dzvsakRuBnqqvwXLQgPK2avL7sn2NZR5t6mCsmRvuD2ANiLvhLnFZh0n94t0AzYEb5W0RQo2+rATiUwP/oqKVj2HWrjG5/ziKH89TEh22qO6ooyPnTawE40Ekgk7waPviUJk95siAK2E9YbYFHSy37dNWCLlFlTUm2LBqQI3HEWQzS4e534A1spO9ghpE6A6KRN7WPNbZDwieUVcfpm/ZdStmzM+IEcEqy18D8DKL6sDHBz6ejI8b5R6AjRlMBy4+cPFkXEymAsZe4ZLZep2l71hMBzaWtxXGfYRG9DnPqs30bIlankEKNa6BkaI/x3QVHMXJNYgzp9fUyXkCQveuQ3KQuLElbk6HrfJg3Dq48HyZdGPwv7WoPcvueFR6li2G4LbaEjCvHfb33EcKs3dd/YMA7rMALpJqNX2roe5tX0TpqopWY61ki/gBLKsE3ETFo8uJD6pWvKORHGRJ3tY812CQwZGlS8+37QLFWVqrjTAhHGW2+Zae5nnm/c7lRUSeBMhLhxcacN3TdOlYc1HFMSgK13bpwYeKLTG3l+/vYQLdvC1d3aL/eVAy8raOlssc8Yl3aA9IQIAbLI6LoPy163p0YmO1ZY/bDoqyU9lC3kTlIPzOf0BUIP5r37mz5vtPMAcxfpbx3QDTgfXlbWnWV9Mba7ary2tRgjWiwzPZikzd/DUmnWHc81+3z5HZMPcntI41Nx/8JaVDdhCWg7Dsk7CcVHkO0tjlhfFeSFokB+FwFo6/jOQprCfmBKFQNT6SYICx5eIiwwj8yWribz3L8jWadOpZi7Hau4BriFjsJqtDsX0NZiJgxcM23R3foheT2DES3Yb21Ghmg9+cMNbKiNeqtWfIKMFRvONQHZS0vC2aTt43rUMFe5IZ9E6ilm1gvIthpxz/CgNO5VDcplwB6pnCED4hWnxF23zyUpGrwA7xHKRVIa0hIrsxD4WS1JssFCbEn/8EsfJl2nEsRpxscXqvPM67GHnbJ+dF42I8fj2qygd/1zyF8BrZrRtIpflxxTmSDqXZWJKchlYrt0N4OgeNFEyQakYOaxuv34uOSe9paGnfeyBdzylIryWAuKbRp4vM5eIlqrVC8vaOwnLQ/vK2KDLhJrfmao72TfGRdOhIioUhsySmp4O55eDpwB4pmCC6Rw4b9PVYuiGRUhGVK3sbXJXUZAyhTjD8QaXI25rZlaxxEX4cp1WniAiLOz7rTQRXLg6MZnUu3rU4DqzrzDtjBQJptykjNYwTe+zaEhvSy2ssf6dIlJKjOK7fw3GXQRrPQQ7lbRE6qZT4X0Y5bGlc87oVxLBxdVs4qdD0I7zKcvZBJ9tzggLNZBgC2fY9KgqNSTFSy/Xivii8E33itKHgPkJSjZQgkQVUaySd1Sgdj/uUndpiUB00l7yteazP4ziul5Y6QA6oztxMu+BrswTKihbIRpnmUgC9vvsn9A56fr0A6RLnqoiS5HUkjXMJnosLgAXG89C1UzxijAf9o9A/Qop5n8LOZJCFkcqgG44xr6/+vQIVWDa0dpcaCs1BVORtXeUwy73TG2MfpzKUaZQFH7eqjYULsfzeZNMP7iabZGjXYJO8BhmfUTuTjOnkZPQmkK05ehv7ZV2GXX/w4wo5BJ73BjEaSYemM3HdbVLVQdXQWSc4rKLWx9shcH9gJYPhDq2kgF4HVycPIH7MKq9n9+qnlN9xqA42gbwtjZIe6U76Tr7NukACtcHwV3W6rVB4z4ub6OX0BVDUcEWFELUp7YK9r74dL9GG8l/IT/tZraF4GVUMeyvQYb1+Na3DvYBqXNHr1VNxKwGV8OFJSJ3RxFLMV9rmUe2HdxRH0oDmmmt6ncXaiNbKzklnXeMEZpivr6JXzAJfYIHvFborLQnCg9aSt8WSzFdpkZnY0gsn7FiFcibhoVu+ci9npnU4OTOuGFTOwgnYQbIUTPwaJ6Bemj2lASO6AjnMlv43VcooIfh8vTE4cxEWyRAeolCHmYEyzZynEIc5bLMmo7topMVuhzXkqmsAqNVXQ2jbAG3p+j+1Wma3bNb63Ektk1vs8F/kDWxCFqdob4KDAL/jEB7Us7ytMOko3ZIqCvCQjAbz5XPu2p+r6alyO0uVUTuTQu3DSdZtD97rGzkUp0gVoJ6mXkKkoX3SxvlJgiGigyKRt3XVvhH1wU8FdHg+zuOkNXc15llRILFI/LmMRXXgs23lMwurxucS7OHyq2atXF2AJ5B4n2M/ZHnZpayY+j7NGQIP17hmM7fpUniOHGAsvUmsuySw0rwJHYYQiMlBnoN8irYmzPOkizoDv1d1njuvXXz90POXqFC/AORCOrfry7Jry8rryuGuKS9AkxHN4a2g9B6uqrzesTZ4Dipf3tY89zP28KkdKrbC6X2Cl3cUhgPHOnPsrj6URlKpuXAOrlfU/x3YRiH7JAuDhnkCGYVpnFRLcJ4uIGoy6vxt7hFg7VVPVFAGCTQwuDsahhY3sFSmUg3TyiRhy19BGTVZPFTmfrjGPgHq0tgE7U0zV4Hv8V2DFKnko+InXNaHLF7Yaiz/laXj06Fu6mcebRr7n+u7SX201X4ezJvfyI8zvMAF0C9B7hKRgd1EqtTILnMhW5OPiiKLIVlths6sK0jeM79t/meWx9N0+aZNACcC7zdK9SDqzHM0JBoRWpXhBq3DqFu/vf0fHGG0bXRnjFQbYuQf3rKL+be0Ptp+g8/JcUDcSVTE0ZKfHkS35fAL2fbX9/YRFxfIooBpyRsLMI3hJkqMhsDUNjQ2cO+6dtiST2CDtURaGs2TSQeaKuJ+dM0x1NMR69f3FAfaMWbjKjdlmvrPcRmzaUPEmK1jf9sZcziEGRhzOE9GjNm632dizHYHq9WVLKCIGfsNtTkbcnjNNeMv79594DA7MZKsExOwkIyuu6DV0Ca7vit2tEGTgm3EZjT4ep9swlWVREzVwtswlbINAYMZMq4XaRpJtyFMW2U8snQtmKv9ADRpfzRXrnDslAFZRLVsKBN0vTLtn4CuAjj9JDqpNSPSTaDjjEhk0o+2/tx23C3iG/gE8ldy6qSxsAbAChvOwXgbohbxGt3P0ZZPVW+ms8KEdDbiKqre3JzVmQPdkqRhAa6CgsNc7DNpMxbL6NbsFKSjmI5LpfNl0gX6NvBWcKnhRpaFH5lHd3gvKxvDDAy6g7tZnIzjPiKboly7oxUBixiThrPhSiH+OXa3qo5MwFcqOu/CLrdh6sEwBjyhUUvSigot6Mpz2iYF/Kdqa2v0onZE0ylI7XyadIWut1Wcbbiii+pMxM87vLqrxjETA+/gKk8y98B09RWUD9ny9hogbBBHWZxg34DS+aivKmLiYS0b/jVoT7SPIoUBvZGLEhl1aNqeEKOA2xa9tNMiaCFdaEArsghbMKfE/AKtHMEEoqycI6OtZF1lNiFu4vC1zMjAidiwC+k3Z0AWqznrhTPLJX2YgHkkNN0lthmqHt00D6UlLAsNcQsYSaklw7OTsD8TMpWQ1ibtDyrOdyDe3ojo3yKQcQAPKjwU7+5lWByK85gFjGXGtC5jP4ZJgqyWtgFtN4fwwanAoDcnBS9dDtRowv36Cza6/rIVVPToYgntycI1I1rEJGSf34DSjWICjaWbL6P1kLrmN4u+qvMl1JGWaHtBet6nI5DxkLKWiF9FFWyYVt2ggHP7jArbxrlGQ5mAfY3m0KQfdNaSWXj4OMlWV1lR6vmWgxTxagtkw5884p3iSWn3J+BD6ZzsDO81rzXfdoyj4hIWWMaB3RPQdkzIoRfwoYzBt4MPZSOYiBVl82PSfFtnbo/8NXiC4PkLSDb3VZJim3dQoHPOa6or/PRcTQdnva51ud++Gd3WcbXdwKZz5BvOs4VPv644G/vj17/OS7DWGwIcpIipWyAbJuYR75QhIO3+BFwpnZOdMARw79u1o2McFZewwDIOdDAEhOgFfChj8O3gQ9kIJmJF2fyYNN/Wmc8QyJJErwUHUMJlHQFYLeEDhDul+YRdn2ItFs3BTmg83POjtHjGUQAZl0ViyBQUnIzPahBbbqMRiyxDAQNvB68JOj4RtwnmwsjEQ/Cz8tqPrCSuqjjLl7cUx6jYQ1JHxoM0uC0nypqScKWY3beDNzVDmYhPNXNnyrN1rfk4F+RrWOCkPM1IWpc/Hqj8bExdTci/TA0r/lW3pjgZwgAjnCCaEUkMHvz8cCZinL7glEdRclSVD5gXY1LITJCUNka1RaRSVbQhn1kHdurQ0WpIE2hJqzk26c/sx5FnWV6tidtVy+A8qIibOygb1hWgtuLTIAE58k5MwFly4u4OG5EceYZ8NISVMlKTtc+SkxjkM7GSuBdT8ZKYwLvDTLfk3/o9DeVkU4BSNrLmIBqpgH2ovm3dminr/1SMJ5gPk6b7WtugxGquMVAy9ZDHUF81ZhnzbSnfCbo+rcIbzIcx322B+UXxi95KogYc3gSjkMu4T8jXW8KCkjFMasPx82PSPKkwGyvWj8AZRhKLgEWs2ORzNmdDIWLzAOJA9puqFxMwkoq6Js0Pa87MUdr9wBAsIBftps9D3PfJuG4ndwxtnqnv+FGa9rnHLq+YjEuUtVSZyugKLvncxA0qUo9tH5caDWUCpjWaQ5N+zJ6qTDiSWvNZ8RPz9OvI3Dt8Olans7eUbweDmItpB/Nm0onuheP5FncSpqPmUQZOurzbxv2xeHeHBSU9n2qF5+dil5hNl+6EgxyB4XYxoYm095Oy3Q4mL/kM78uTCAdEoC4/RAVY/oTlQ89BMnbR1BOxZVvFhit1zcjUooj7w91tN+zVBLxnOA1GnCjEMDtjDoyIjoV0/CKspWJKV6tR3aCAPWVSML8ONRrKhDytnEOTfrR1touHv9MiZsfIg6qTcfOw1d0xRM0HMxdTC+fTpDODivOarZdZCUz2SD2c1GTFINYmK4V3d1hT0vOpjFV+LrZ/j3QNnpH4XGUIQdHKj9b3rqokYkMBvA1DKpvbKS+9yUgm4FaT+dsJD75oIGaGgLamsdoT7n9sGnITGK+kRzhhJbk1pybSEEyWM9P22iODdXeWF3HHJ5BX8Txs/+LS9rveM7esouOLAbSK6Wx9ceIGRLn8JFy9PSwoHMKEnCicI5P2OwTzhhe0GXvNIlYY6IDBBixmn6R3XlazrCdT2c4yGpvy1GYLIleuQVnlKX71CpjcjBCDi80BCtLOchY2sWM2s2oMk1jLqnnaCTu577Sh3pNVCH5pL6QCtLJMMtLsSZTXa9lxlS4ToDybUdQRWylDcDtLRd6U/PyQGsNoi4VBzyYxQ7RTYdKLvtaMJjIzEu2yIa0xOhPu5vqhHcYc/LqTqwg3Cl1YhazC6Jy6i7EWukHMwaY7GHkxzI2n41AhtDZzonO6xJ1kTOUIJuBK5RztEEsuNiCG983t/6OyzOFdVQJTFlXXVrCsuKIDC2t6sIMsbTai6VjcbI53geXFI/m2wX/eSjhSurrb4xIaGAo0VsaGQ3dEPmYjsZxfVDyGO4XF4s4bRp5FIYYtXUiUDO6m29XknW3JUXbLWNbEoj+/xPmPffYVy4Rv3OWvxjO3FLbL8k20UuSH42EVcuMgIQPMchMMFYfLeNbg/BHlMErLblpOsvUdTAngLZIY+ATy1xu4VuS1tcOjIJwChQNNjTskuntJD3w0L6xjR6fTC7ZzatIzut7cwq8an+GGzgDFVnD8Du/vLIa1HaKxgzs9g1H9vUIliDO+p9BLKmg8WyEagw4J5GMw8DkXA1FHt4PjRXNq0jO63rbxvusK4KD2AzD0Pin47dHqe6TK26HoPXWmCOZhcKU7jhvjrrH8HN432/m2EIKtcbaxg/sRJVU3Qo5bLVlTicxCSgieAKKi7o9cbHRSu7XSYzTg+STJiD8spIpFMbdwWW0NTO1/Fy/aVlnyM5vre2GT3y6i9SYBn7LnNMkieWYDMXhg7mKQi9yGbT9H5y1xXybkLjGljU4DBjVn47AbCHI06hgMXhyRcpgYXMRhHaQNj0nQWz5/Qvhs7jVaPZQJWFQ9VSYdoOttAYPqHBEc5AhsuYvuBWnvJ2XC3XUhUOH49U9Dj5isnmI9ll96MFiZpe3tErMajmW6JV43iyY9GVTcIna2Z+TpWNiFeUPalvKuzMJ7+6A+u536GUxKnN1df03NAocRTzLVvVhU0RWXa21bqHv1I5xDFvQsYK6TZ70udxzFj+fposzix0V1V8Q5rGPKtLfmdBVFoiCpYyMB2mZ36iqd6WgmYHHT+bTZtG0dV+uMDU29iXl6F21nw7HMyNA7aMUcxYngfWIpF4vBRczbQdqwqwS9pYNsflZVj2MCDlXP0854x47u72GCvoBbXRIMDlLIky2QFU9ymCd/rErahSk4SUZYk7bnzvJDXmmzMz7lVaQP8bkuzoqmZnrQdm5LUU/8nbAR0XifkMR8RawfrcDtWZ6ttXynqCPe+tPgdht9eUPTs51BbybZhGuJb9ILut62MN9NZst6fY1RGY9qZna24/syPdPxZDfpQ19rxi1ECZ/Q5Fxkq1vqN55MxT5CWke8mehA7LYT8lZERhzT+S3cVmjHM8neQjt3RrbhsOpWsK9WVYqAR2LY3XQ7qkYwMW/upOn49wpUYHm6jmByS/49itVPnMoqiLiSgrXhSmkTAs4c9HnruFM3kgk4VDdfJl2g682Xp/QBxI9ZxQZAc5/leUsNEQjzmArrWmU1NW1dpH65MW4dp9sObwLOt51vI33NVp4xoXRc5YgUq6volSRaP08hxqt72kJRS5xeeljBLsO0qjHztx2C7MCMOjMBVxrNgFE/qHpbw4XtKyYc25jyiAyBCW86PR9k2LyAW3WiMb9Sth3dDOyvm2+TLrF1Z5OGiyyOEvgvsGz7eHsRpasK+61kDCivIuJ4DtqG1xVNCbi76/nWcbV+HBPwsX7eTDrR1pmRYw2cEDrng6XTwdXZsBX3UGbyM+yIf+GU3PFDdUpUA+Tt3ax1lJff7v4J4hIXgRc03zHpWJSmWUmw/O17AU6SHLNB8dvbMq94lYlRL0ApuFBYvH1TF0tvZXLMOETWLBxfQfmQLUXYBgB6dJoYS2EL2tha20aNWjFFq0CmRfEJFsR1IMLRlmmRnEQlWGU5BCIsTeGrfjQwLqtciKMpMiWICtMAwpg8+BUZmAPMZypKUWB61FRaNCHOQUY6s4ErJ2IIYzAfNfzXKK3uI0IulXDQYFrUOpxWyFo9KmS+br3SIGmiU4SE6wJ6dB3BLCCTAlKmJ3r7ro5c3Q0h9AiRDsf3cZ7Qaibs2QDAmNhksRALAwOiRXmcZKurrBDiasu0SFprTYSkN5d1SLDhCBYl3q9cg4Is5UKEAjgt8kvwXMhUf1uml8ksSY7S4lkiil2pESIZCmMmwPaPigPqgExdT0C+hkWB7Iw6SlHYKwZGi/T0BS3LaZQcVeUDNpbqTLTyBlTw2sZIpJKMg7tCMzQ32QbGUjyk1ARRJMNg1ovPeVZtpL0gpVpExPklwtF4Eg1XXuqFeLFmFQDq1azg/XkxdtFD9cbYVQjN6CdTGF2hGRr8wrkUTf3OvAYN/7KxmF7CF5AN1jyZZdU/6WqIREax4eOz2tFSTweKhzl42VHbt+H7POL+se8omZln4lTbCkNNluRdNwZFSm/heJS5250aM2/GlHgkB7iUUiT5thkiRapMRQPKjKduDVu0ZtqECW5rpMKMWRadb854dYtIk3NGuG50qXs0SLrEDiIsVJINrSGeibekpMCs+teI6AgplqZcP6QHtEclJL0TW3ADAL0FL74DJTToZffYLHaMN2C9SSIxP4rgTP0XryrELIyxilHgZED0dgVIAeI1pUCyMPqdb3s9SLj17W9uadB8zxVoukI9m8cPYFkl4CYqHoVsTpXrh9beVxEOrb8ApOM9EBWIn34CuHoQWgMDAFN0nyAyRwqJmLAwBluyPAdpLHE+1IV6S4y7cyHdA1gJ7zC6WSwJw0hyLc92oX1irqUiLs1REQe3Gl0dC6xVJ+tNBFdC4rVl+h0sFdcl3LEOguu0LEfoqlScQxAjd8YFKNG2WccvYkhtA1RwnPgMgYpf1E0IGxkknBk+kMsWrdSIkYWoabchogN52cZLEnBh2YYJbjNHGnUCK+6y4MRc21nMm0vFuccQQj+FeVYUqGKiQMnCGIxeqJVMtBH6VcoMvLpIb9tFL8pjgL5cT2+pN0rsiKJOHmVnMrfdyQsFLDyb6SDZo1EuTw1ToTsG70YjOBjiDly1WNvTcBqrBN3gDBZBOlCpZTU9lUS5JpTjYZJMBKISk0SCxxqCSi3jqriIg5GPgQUV0YSWJQU5OFTjskvr9Lw92mwSCJY3Gd1PnihKePmoVNVExKJOkxW0UmIV0E0+BUEo1/OnCd06aJvxtZVC0qzDOa7IdQ2Lj8IVJBNWMBihqF4AwgnRCmgnHmcoBT98YU+u3hUv8YnUsPjBPQOO0CMTkWgYuhCKNp0O7e0TKX14WO2wuCoKOhlqfSnmsbUY27DeXuBAzUeltxgcyTWN0UC7BFWGgxBOPhgRuIhETKyLgjxCjJOYnYOWh8E3UoaS19EygLSqgsXMyahtREBSJfZAdNXLqBDcbqB6WfUj5DQiO4xxur0GRZkjpgdLKsSKp6JBLflg9ZVFNOXCtRTkNGhBtGz0I/YnKx3qdduFpAkoKQRUDE0EL6QXE42mIpcQ58gUavNaKWjDgshHwECK6EGFHSpIwSKaiAhMZKCcFENA/TiGU+tNliE6AXHUXOfih2jD/amOCjwRPJTCgcABC70RfayryhnB4xJQRUpkH4IcwyRBOq4LmVVQhQE1GM6wRgD6MAgnIlITGNtf7lFQiYPVj4qtoqJTH8ZrQC4OsUgPySbAgV6iMNv+1p6AaOoK8gEq64nIJwkUVtBQ3YSAkFRAszch2yhqJfF4IPloOFgRkai4bgVheFQTEOMkW5OLrH14uZgeHJx6HCy4t+QJkQroIyW1+9blGjxB8PwFJJv7KkmxdhwUKHYxupraDYcGgTdVDZtRaDdZMw7kbkP/lbLJA8lHx8GK6EVdRlAQikc1smziBlt+7+9EiOnBwanHwYJ7c5EQqYA+UlK7yGaWJEpGGQIoRICGEzoL6vshKikaoBiZMfqbLbf1DRXx0GkQdc8pSNnwu3s2GiLQqERuERElHUnwIyuJ/UCyedO3fcTkkIGrxyOp5a92FbgldJNMgQv1mBtEt8wdJgEB1TUU41RWFHIbfwVKRUc1fsWy1Yw13G5PS0QJpMHWTEs0y63e5ERSXSlTbv3MKsqHbFRfRFHNpTkFlc2aHHm72N20U1JXACUfFw8soht9/09BJAGyKShCQrX0JGHANMMYQkuJ0saP6ajCoJuCLLf0VUYJTWgYzQgoUAk1Ij0ZaCQCGgyuZoZkjuZmqJIzahijeazHEoYnalxiYnAU9VIa9AVbldqg4ExknRpLCNVBoZNxiJjADrSp097pPbpCOPlgROAi2rQXiBV0EaIa2Ytbt6nSpwyErvsqLWpMg6l0p+i29W2fKEYeDiauIB+Wsp4qHIy90m0QGyZuQxEbNholm7vxhmQU5BPVj2+YTnQEAg5TiGqZ2VUEib9FSi8WRCM/PaRUCvXuMhbTNCRQhJXwQCadV4SQWBNimnCRNqfC7RXq8UNUgOVPWD5QORJ40uiqyAenqSkiG5UPQkE1HWIZO4VyLopSXdz26SrkNBRX0A9UWE9FPwvNpG5DQErpHIWi5Hd6Qo3JOaxlOd5B5TEJO2xodL2HM5ZoVD8FotFTPaRU4zXpU3Qaj8I0JgkEKVaU5qgSXj4kVTURpSQZYhREU7Ywsh0ralsrofpKdoN1FBtLvK5z5Ri3SQ7EpCRkIOTjGgLKYi+FtzNUeMYUTDrF0G2fuEhOhCGgfgwDeBVJ9CaaGKUodk5GZeetcxsHq/UosIC6PTAD77WfZnGN7FQY5JPSaHMhpEpBiCqItcIw65VS6wiRjqy0+87rWUgKa3BWZMBIlqdPE7MTm0zslk4qLNJHcnCVCpHWEusmLgmaUj/Jkcs3k4MEy+GpqJJLObDFIFXS6Um/qWSUa1jhn5DCWoxL4a3wpNg0vothzJn+jhADqAgTEMGHjZ2blkTiVH8GJNNU1A5WXV9x1UqaxlBPYE2bI1+/UuRuvJVlXBSItwMahXzaYxMqBXUeS5WCcOiAyKA2Y4rgcqJOyGkrPEpsrtytpvCEYqbsiPGcSpjKI3nAIG2pdMoGYFoS0NDemQNoXHIdVfc/FFUUWVRNsy0Yo9BSwBSTgtLqXLL6STDuwnSZHVRd0q/fJrW9qKJfyceck0mNKFVHhs/yOE3HAIUXVWhMM03MoAuC2WGeORprihwkxF0sHGTBic7zcn3butJ8Na7rPHiloToGtZWGKQszIv3r134lhRZzocZjTSYlOos5avOA2k+UugfySdNyiYfRabgsWOp+UwVvZnfOpKpvF9F6k4A+lbycOgykfkjDCt4UYtCJbL8+Wb43fbq0+IN7FyL6SCDlAxJXENGHTtyvoJAE4QRXRPqWFUs8D2QyFMUybk2WSZdqys9f/9RbP9IqWqmQ1VRIm+KIwaqFqYlpRUYXAo5JupmIxjzzqTlFs6luMXg5FiNS88+b2lBe0fhUp3CSF0OUh3HaOnIS6KoKM11I3z9R5XbQNTTyQZ2sfYWi0FWxH6xCaQQl6jQKpHstRWv1SCDlAxNXEObOot5zUSXPEiOcwOrpHl65VYRv8ECKobCwQrJQb8GoyMLhGjlUg3tNRX9V01alyStJr6SZS5wC+cgKbPhmzO1Znq1VpFOBq1ZEaS3x8su8dKNcbOWopyXdTWZBOArYeGx9ncBEoxCPTDLqEaBb7kEgkYKXg6uUsrSWWNXTzyAplb0cr0i/cW8iBSWfitmEcGYDU7GYE6WmYi3qKaPb4bNOPH2ksPIhyaqI6DR8dElBJylSAa2Yp6z8w0CFryndCh5wEoSFmtZVBHUaohDGWsmejFIFkZq2J2JTwctXAYKVh89B3Z6nsIRRorg3oaogH7mynjiCmXvMShnErEI/7hUe4WNdt/xDW3piSuuaD1yGwoTEpld9DFsUUF07qa6pRemnx/R5RcXQ8iHLK0kzirIvpikIqsA+cio23QpuuHLrVmztSj3GCv3r+xoBDoWNYAryruzX9/jl0XXUfPj1PQKJwaasouRrtgRJ0Ra0T/T2NZsvbxabKMbq/H8u3r55WSdp8dvbh7Lc/O39+4KgLt6tYZxnRXZfvouz9ftomb3/+Msv//7+w4f36xrH+3iwD/6V6W3XEpItNNtMKU4SvgRnMC/KT1EZ3UUFovjJcs2BLdZRXn67+yeISxIT/MLokl87KrcNtnYMfcjGzxwuw/ZkWwv/bgJOcYu41+Ad7to71VFiT9EzNEisEMh4geS0j6+OECyQ8ER5KzpN98+XiBxZUq3T/m+W/+S18b/D+vUXcwyfAPU4Jo1oUGCBDxabJHqtMw8zGIdF1jiblV6Asikxx3hefAL3UZWUDPH7zzyuX98zHMDy2nuO2RgNwHKwEX8360/71oMPgw9QOXC4pv44LD5odPFalGDNc70UyLyd05c4qZZgSTsez7E7k25HCuTQTv2sgrwJqtwe+/DZD2kjAjDztpoeElw4MWSjttnpVoCZt/UVpnBdrYmgH63rfSrdiKjcAnv0osYuKDfHXtdoX16RE0oFtz3qSHvU56WhNNhdlJY1ynH0WN8BFs+wxIKv2oFwjEQXOOAjW30p0q7UZrm9ygGy0wF+eIhdcgdF28vngRk7BCdPtgTXDbNIqM+2YiDByBWa422t7yHC/quVtRnzS0D/1R7TFUD7TLTRWfEGMVc+pe2/eMjyUroB4EvNMdeqlfb/0Zj5UnPMX+AS3DxU67s0gskQLVNkjvMHLOAdu1vpPlrISXWXwOKBVXHU5zn3PCc5wI/Uf0u/lzFjoQ1KzDF+3ywlGIcl26bUg6hydwU+sdrGI5Go7rbIZjeWo5GRdMDECyhcFyQw1gJ5ni7hE1xWUZK8CoVzCLAnqrNKEilirtDCuluuYdo8HcJYdoMSe/4Ca6SPeCtUUGxF3+dv6Re0x77ilk22zGKfB8roP8Drcx2nNNjhDUrsMEpniyu0w3sDS3ZRoj5bzHySZM+dx4LkUGBGL4awaGGzybMnsLyO8EMbC5zWe4CeL7aQ1azUoBdD2Pf+JiujREwfIYTTGOSNSIEspKYi/vOb7ChmbKNhiTnGC7iGJe4QWcuYHnOFFj19rJgO4g82Hpv+eWC03pSXyOxjDSUZjHkrnxE3DXHWX2x2vn0u0+G2t/9u0Z+mjmhJZ8vMseKEWjAH38oHfPeijUulcYshrFvojQEx/kG5hfxWSG9F+P0PZAYcLZcMNlaWtdBWxwjdJSzmHKH7bmHyN3XYmaW/WxjnaVKLJ3XDamCiC8qtPKa1OH27l+AXQ9hT4/RlA3Nyavopei3ElGFh7FshEVgEg0i25FAWG9eoYO/tDTavfLGFNh3UZHvPl1r1+nuB9uqrHADecuRLLfiTrniDj3uH7MkX28hlF77BCiZVYKO/mkonr3ECLkC6Kh9YDSaCcG3hCuQw4+ZRBuPQCjEwCBpOE4sgbCiPj5RO0wjtzjilOCiywXmG+KA9qmKRDsts9kNLiCU5StraJw84noPdG8mgbPp/E72cvqB9EHfcTBXY4DstONqST+Y4UMMnaFO2yvAJ4xAXU2Rlk6H92Hn6hCQWVW7PFDmjTAxkoe3wRRuxv5cpsna3kfpHTxFMojuYcOjlUG4tiQchhrA6K1Vg50stdgPZcz32Jl6ZnVxRudUuCd6/Em/EWZa3/TsGaG/K7ZTkgBZHEVH8mGEvKo6l4o4i2ELL/bb8Ihm38Ta/cyZvk3iDm0Nw8byLIWxbqA/CVS2wEJZ0A8umKmSXJkGxldThdee4ej2uypKLdeJKrTH/hMVDAotSgZ4FsaBMrWsSgNj9Cu3D+EMBMYTF4QDaDZGqMGZWwGGJjXeQQ2WN41uyFKDpv1r7Kk/Is+sCL2VdYLEG4dx7USLo3bDEDeOijHIcLglwRjTucEUN6dbiabo0ao+Fs+Cwxr14muIoERGjiQCsI4QGWICIfZSA1jFDBu2pAK12YnQODWYbpkqvIceJ1mtQCNOQCcahBbYaS/eo2dFmk0B2ryAEMMf/E8DVAyPo7TcL6gj2efY7u59wySJpPlnQSzCeL9bj6dYItXpRgDm0pVIsUqA5j8mDHuKDBHART93H3Ty6V4y1S+LJ+c+YMvuwHhxUz2Jly2ws/wI0y5nAUcGXOmD+CqKiykGdvkyIfQDh0IIoUJUrdMCLfyhx0wC2gV/k8JL39nGF1gFl9Yt0YieRBMSxDUwDdRMdhPMo+kcBlCOhwbYmyKZ/rtQnykb2WKtBmI286jhxNvhfv2iStsfCswemzMrDLos8ZIq2JaSxrS1SP2yZjb+wsWP4nc2gxBxjY69w+Ojv1qejxUlWbXDQ/5KhqajcwnaQYHXD1s4COfonxr+MX4cQlhEH9RlbnQ6cizigC7dG6zUuc+yW8lF7refdQe3Jq26r2sPRbDyW/quN8Rn2FuNxVqLNuhSroNjGzQkjzrVJPtm46+q5lsXeicoPYXJqXHXYquxUjC+1wAxJEBKHsv9s08sVWMB/cfcf26+OYYHFTVZfwhHj18Ha9/+b6DiEK7R0N19H6YpdMgYFcwegju342p0AwG10s+z3nQs59ciV9x8QPH/lrESucGsMrkZ7et7lqJG43OWQ1RzH2GqaO4ZplL8Kl5G2yObEZA3qTHLD45H2q4XaAdkZTEDK2XKDApsIl0vAnPk3n7aH/epzwzBcOMDlfrFoap4MeC00pInkrsZn9pg1G/063VQI5xmF0MOPpsQyDmu1TcucC27YqIFcVwmQJRMxALc6ZBf7swYFFjtTmCTY59tmB2E2plypjcXbBDiKUQuKbVxcdVILbCsOc6ewri45nEtrV9ILByIIpxaG6XlkrTBQLi21EyDcOSnA3GaJKAPV9HQAFm7TlzKPsIJivKb95+1RyYMnCb10seIlRxMlrKy+rZ49VDfLv4AXkiuE9TsPiqyX9S3JLDav2RnI2zzE5uJ01mLYftNT5vFz8/WdF2doY1/1F8q4+wps8f5zK30RMwjD0gjdmVaNZfsZl+6/4JrFoOzAwE4MHI5zPVl2Dl71NwNCH8vRVJAdfclgDsdfalyHQ6rdO6Q6HM7sy+HMXFErjbSQTKO+XsfGIKjzlribZbL622+QCVSxtRquR89HyNLfLbCRfDgMpuabzfWDZHNfJf8ABbkVzt5DYAqt8V5mcrRdWXiR0XD3eYF/f7v/b1yI1lBm/rtLqNYQxVS83jbL7XSp7xYbhU33kt6gS/1nm01Hm/qI3W303/+Yit1SBx8n2apN5WbPl8raI/Fk3Ryv8AYFFucjaAhXWcGnlqW+zz5Lg5Wu0XSp4LlJ20VTiWrU9bNuX7KK9oXmeH9GRTOaIUr6++zzeAmeC3dpU9YeV9p4Q2VYMp/8YqKIkqTT32ef96ssSX5k2IyqX7N1kVstipHkFbV7lBbPvBUwLNm6LYpnXL3kbSgLY21qK+1zBSV2Wl1idREoTzlnXv/V4gR3zWXdbj7ZOFeKAnvQWK9K+9Ue01mWryNBGmK21B7zImKfCBqWWDiygqffDZ1WqnuEmnv5gS6wwIeTALEuuubb9O6j86KO3+leOR1SjCm0iJqRBBG5RQtdRMhCFe+rmKI5d0O4KxfZCqbCS3B8qR3mNnmUFDkHsDVrVf9Eu89aJXl73mCpktYcZ6Uir/rxrE99tpt5HlX/ddo1CqnjTZS+chZt/dEGjzCWzi0+r0T8jr49wTTmdDRXaNFHLpHXiW3irprtPrDrWvvVGtNHIaaPNpj+C27wdj9K+JuvTJGFVfCQpUCUaXpQYCE/bYpcRn76z1No+bnseiID3vdla0lyMetlNbf1MJ0cbxZNhK/g5LMvssUpzprBllmcgzxnF6As0UayyHjx40stMD/kAKhwC8ot5qhCmysYCzGzZQ538n5EbAj4oGSm49ngx6q+h9Iz6aPhU5h+WmmIy0E56RBsq44a6TXhkC//rlJE6jqJM3tyNSyyiWuf5unYMR5cnUvaaHvVU9hoVC6ypq4/liM+3MYggGlxd5eDp/ptesYIGJTs2hI1E3O3Z41+fN1icTzHFVcdh5svonRV4bTuDBb6+7RhNMfZktnU1l9sIw9rt6zwuayuyAJnE8zQ1D3hPZFiCAtd0D8iJW9ECmQxR9GKIUr9xcqdsdVZuf6YUcBjRZTunHuiVV1+OrzF4qDD5VW31fhve3xSJSQNgXAp6Aotji9T+HsFFkCwK2eKLFxvSbQ6X6P+4PQU/NAFxRZBlyXjfiYf5pfBP7RNVt8NXZQ5ibgosir33XeIMLrIuRGaXbDb2t7z4jQssccouEbMFNkcBJ/l2foqqVbs247DEquD+KyKObmiPm+NFLRRVH6c32JxjHkTV90FDg+xMyFvQzMGb/3JYvWqEmaJqb/MsxZsuyV/2Mmp2tk+i/+PuSOb64p+F/Xpefmrw+MYDCurPM66gFsUBcBOvWdqn8j9kZWs7DFFe2ybY8L7c58j3+2mJeLPeXWAYqM1RbGLXdFMVsUoCXjJgv+5AkWJrHMsWQJ7YFg852HltltWO3rUTx/BhrlWgDF5XC0QV99WP5/8NWbXt5glgexOYeyoUiNufG5btmzqYPY2zpzMtzACvS4ZO/x8rnUe5GtY4JCE+s6T55rPYHNZ/7UotlUGw1476NK3Dfdp3det4aDTlxJfWkqOqvIBNQpjEpgQgptUmB04yw7dOFwW9rp/iLD6lirnS0yTe8jaSaJye+yNitc1IgAzb+sbntib7BEwTgP6uyW2ozgGRSHDOSi1sMhxXBOyOmX6QlS+NdJ+luXV2j+QpkPjIMeKuuMI7U22gTGLovs4l/Dz17tt73WfX4mzhVxtxY22nUvcRPiSsEUA2SB4XIVDUnkc6SAtsii6j7NJByaB6O3GQYH1jQHhZQFL52KtPVm/YvPVHBN+/4VB03yycYAVpSgzC/3dHptsJkXl9thJ1m0h3rrkoLPsdFbkr6xc9dSkKupznlUboZ7qSnb5WsZlt/awmqX9PIeSwqIpNJ8GBQeFZcgzh3ybI5ltRAUEMNsIHld1KKm8rf6u7b9Ytk3cvWfnNWQu/ASmnk57WZHUG0dMSGOijACDAkt8fBop6rPNUaTgPSCHN4DC7ryaK++ND4HL+ciV2hw/18dYEtSCYst5wdcOKw4vU2TfXzFavtTChVi/XSVGzBVa462vOEv9kzIge447qfIcpPErf4tADGHTQl3vOmLjCYYl9n0eHljKZIaHstjZR6VI41Cfbfm6uitxyubzNE5Qx0TszUI4toAvOKtb6CDsWyBpp7vnBlVjEUN6tqgcmxjStsVGJSjGxkI4tqAYCwvh2AKqy8ueGMJRPyE9D7GNGSVnAAhJZgAeom0hMQ3AQ7QtJLMBuFXwBq7CXS1tv1ryh5jrXDitlTYBur7Itnd81nv6u801mfsqXYKl6JlRtswG63OUL68ymJbFT5ADNLdsYJ8ExGKdewDxY1aVR2WZw7uqBNJ9nhrSo8XiP9eJpqkaxH71loV8isotQpEC5+nsLO+NxCLfWAdY4n0BUrpo44vUwQliEZGJoYa0CV7Jl3yoV//VDhNvgvZfLTEJxuw2wq9R8QiWamrKYOz6fPL09JHvcf3VDtPpywbmJOjla5aWDzxSDsAV/z9AJKAyW+7GwZ9gDuLyE7iD7O0eGZCNC6qr1uSd/ZIlAneUDMqnJREHyaGcWjqO0kd+qyUEcMbPC6sQwA3/+YkcNS5zwtpmHZRh7sqdsJ/fRax7lC20XxeITSLMXC2GsJC0CtmMOfwXEdObPEqLKMY/uYVNAeffGs+kakj/Fq9BUbF5xXWwNtpxgzMsKOgphvBpQTQiOZRVJERn5SkGpACzubySxw9RAaReWCGAzb4Kiu9eDArsPX5NjkChv68ts8eKN1w44XFV1mu00ktnXMnmwCdM3vddOJaht1HXYB3BlLtZIQExb+NLVFyC52azfpnhiNdYkO9NAbY1x0Hthvt7gfZOX2BR+j+ELkDpklPYDM04h0edB5KVGeq7pYNCeDhhuVTsWt6nz/Ce7J8CMpcApQtzmaEZh7natlks9HcLDVqA5U9YPgiZjCu0wytIFUN9/gMwbhhe9eDPya7wN+YYxS2Cd8PkUPbcLzqDY8ssdgcCb629l/a8aHtAriNGnM0kBLAfO9qYbkR7JVG5jeUTww3EpgZnUzJFDjgFN5bYMgu7GKTY6udNX+q7LTZBBwcFFh5CUBTcLfnuow039WQnxh/PTQKAPdaqncYIEH7kmDJLUXfEMCTcoCR0qC+a3p68Cvhc99+riFB5iKr/amF4pLC8ymEsPhnmSx0wC899+VIbSsr669pXeT9d+yg8cHY4Vm73QPXyKhy0BMS1DSEZJCAWJoP2mNT3eFRxLOp8HPope06TLFoK8pcxRTYLVVtVavYIAGwiZWOQFqDFwQXr88UWfUf68yeAqwf2FULqu0UIZJUuE4BXGSb4kfpurV9P8I1XkYatC7Zreb4kCbK8l2eMxnV5Ftcdc3n2X0ovuexHl24Jj26y7olcUSQ9XbzHliLtoQ3jN+IxuriNjLCMw6lhY73rITBaqflmi+UYleDXXgTIuiI7v5NoN09/n34vt3MShE+R6kSsPlLTYnF8dUtcdbs1+U0exY9oYKJDS7bMAisOQxTZKYMCy5NFID4CZcuszgnhE8jFrwVzhX8A6fF3VtCYPKRoSpdF16bgHLz97uAAEcumtf/Y3tEwmw1TVnl6DX4nGTA9jRcKlZPZoqw/EhsFuuQWmoHCGlLh/F7XICqy9CzL69life1MoQ1eMu2A7N1ZFwdX6IxXHsmhBLSfN3Hyeb7UhlOj+/tLPmk0/d3C67Ncw1QY4jcssaE0Jb7iq4MSkN0M7Zlt0c/InydI+YZY+IfYnBZ/HYrt1txXUd6YLvxNV7rE1j8nwjgssTF2ehqLjqRF5bNt2gOe14zhm25HhVgY5GBJDirE4x5CzLHaHvRdyzeLDYi7yMR+7v3SCatQu+QWtsM3jloMKX7ikXzbCAOyNbCWt3HOYIKEjwuKZcusVOjzt7Qhg+x9gUGxtVd8d57fUE2Wp0GhwOxiXFihG8nQEPbBjP8nT5q0H5w4Bg8G474dSqG1a9zQ6F/ysHSAlR3hcV/GhZXnnul55+VHlENkxHaScJKt72Aa+a8aevzu82iFdKQV5LFiFgv8wSIit2QfLq2/WJwwRml1H8X4dl6OtrXC28AyGMtwMEEcmFUYDkhX7OXx9ps5lp9wySJpPlncnxIchH2xD9aJCkCIIDou5god8OIfStw0gIUVXhRwlaLdLyQccc4+wCgqtz/DwylKuetLTJn9xhsHSbJY2TKbwDPRU0D91/1xjSANGj+KvRdMkeVu7ltVfrsnKJrEjvymjgfZ9tVvlCUv3Dq3iw6HppJ0kyUqtwhvAC8l+sQ/OkZ9t7s48XsFc9F9ifa7gzifZGmZZ4nIyyuD2f8NQViZCyBsk0vZUQIjTm2ST5NuKB1zMm+X4iY3Q0fR3vWd02AqXIJuVD3O9kGigkVgu8bNqG6WL36vohwU16s75oCFLbTcBh0t/1kVJX+8zhVabGrI5kOGmC+1WcuucrAACYgFtyoGRVOsNTKMRBxEC+OgwObGR/qIj/eE9gtXuIfBX+1lFs9MGg0Wl+wZ0qrj6Li2Pf6a47DEKhC8rfo9Z/ZhbJl9PzmUnviOYRpxqciYMhv9mZZoVgWZGOkCc3xnMAEpp9X7r+aY8IOPacFZKdRnG914CZ5ZnUg+bY0c30CQk3XFT5A7NA6SrKi7/dvO0C8d4CedZcE0bdkc4SQ2/tm5zgPrl0i8jv/wT5fTPnG9uY98pCsPt9zYrQmLIjlNo7uENfjo7xbYQFzlgOsT9dnC8c4/DvbF+mWwbJV1rmnGshsWzWnRfinXyXG2fGW7SH+3Xo9b//klKJ+z/FG4NHMw26UAmk8B9EAD5qoOpNXH0Qp18yyO/qstJl7L0N/HWyDnsoMewBoQzwRSX562EI3KxR5S1x+HfUijjG+7/jTlwiTIx2adi21HGO44ih/PU3JqRSek9WM9CVIHJjTGNA47BovQD2jqh41137kro3SMxw1YI6PF90xFhNGBVc3QbKstjl/GvorYiJP+6x6f0J2giVpl+WsYbmKxOXCSHsWBi7aOi9qYyyBMxCDzCAE9sNDusNBnkIIcxoFCBVhsLtlxtSi2fRP5H+C1fjR6gKn/aoWJQ3LYUwj5+ChOrkGc+aZ17tA4cK6i7razbFi/xzgO/fPifJlw4aT1t61hw+95EDbs0DiwoaLuH4sNF0nF3IOsv8wRwyx74VD9suFczuT4ASyrBNxExaOnM5nC5OJMVlbfVmtygaQvZQP/u48WfkkuQsA2NEB4UuVyTFVmm2/paZ5nOWcH9AU20kASmeQl5wAalthhPE2XQnztd8v+VXEMikLcQ6rMso88DanPW6MBuhdSPa2pFo2LNSWvO47gHy2XOZpULny8/2x1USfIo1yi5Ue++MzlHQVRUeWgjqT0dIvSqFz8oer6W7tikJff0ObuGZttQw07LDLHeY3vag5xNZ/22JvRzP8nuK4j1oJwY4fNnSEVKA48uec8eVLlOUhj3yTXDRqXzNbyqtvKfG2P+WeUhiVWrMe+m2T/NgZmr4sMFXKPKQ+KbD0lZ1m+jsoSsml7+FKbKP01RMbGTdaEBjJR+kyhxalydZfA4oG1bajPc0aGbVPmL+moM/wqKdqUkFAtJqh8ULQ1KgyxYLUOF6rBoXPQaQY4xlFudJv8hQi+dK4gC0IgFlX30cILkW1gzOLpPu5x0MdVTl7DaR4U8D1bpXE5Ha2qEYzE64FCkc7ybC3jbrbMhjNlOIclVrL9TxCXnEjXHy16Bl4El+XtPM/XIBJckY8sj0wan8Px61FVPrCeJ0GxE+7upUMpegpijzUG8cyggovMMwabQuR0DqmoPZLvrG9SmIKBL54t9lCUvNshb/cuMyfGHIxByZmEF5OKMYy0oo3g1fDf7tqe08wWtLjeRHDlaf23WJyCFGVVt9WREcqiwNeahkjqL3NujX1dDDNxMXkz+igmD4z6cTKNyYGb1dXH4WjBS9rWb2g3zhleOAYFdhcX+XuLVk6pLGfzhZAvFm6eAuQpN6D+q0VfoqLgF5f+q+2qsCjY+eo/W43vE7iPqqRE0r5E3AijpOAGKwLZGrlt9r6B7l8MkTmd7GgwbOuSdBzHteapD7ZZxS0o3sXlLlzoFq2muSA3pmx+//wCpEt8KyhKEjYX5aBka8T6EjwXF6AsQR7OwSzG6SDkpohGknVh67zTWQU3rWlgF68ynam6I0Hsf69ABZaE6n4CQCFy4Hpl7XFY/SqHWQ7ZbC39VztfM+9htsXAL7P9VxsPNeuXtqvN96L9ZnHwDTbJK9uR7qM1Hr5LgwILIT9hRPvE0ojhzJbdNFRGUHZIpm/Io+rs+t9+tsMl6Br1eW5DaucMnpMHED9mVajsvBw6F7+aHse27ma2OTs1eawbb9av6ouxYtxyKJt+30Qvpy+AI8OgwIKq0Ut7IZw7Wx0WWdiGW56rO9xOclsUS4CMzWKcIVTMpFmauda5w0QRwJR67JD92E96Z3sIPq5y/KjiVfSKW/0CC8T4vrG+Yqwuob/GmMaRO7Z5Fpuo3Bx7/Ug8g7L7uMdn+SzZwnJbADabTK+/xgkQvX01KLDEdwVymC25FWJYZLONL6OE1GY2ZIMCG18Wmj40cYDflQ1K5vCVh7r3dp5CfPwkFHC2bI/lnFwogP8iCfXxQHzXFRafy4pigGOko+tgaQbcLuNLsZFbH+KkL2yZA1YJQntcgmQwg4Kt4fprgA9U20ckfNc2GpfTyqZGMJY7vEnS+IHdCPTfHbB9lGD7uNc2+kmeFQXajSRBOIrF5rIX1qLYX66abSX1DMl2C8WeMAQbNXUBnkDCLWrUdxvXZZaX7W0Txl1Jl1icqlVJIkQ4KLAwETdNhApDtP7zXCHmV2gkXEr/7qPN5vke5DnIOVyDgv26HHmWg98rcleZOXztPtuE+pOk/V+igt0c0gVbo6LQhxJ6Z++vkbjkWpLV3NaTnz9QcsP+zMV3B0id3rik51fV3lY+2Tl7lVyu9ZxmjMJlgsX1xpna+sINP8H0dxuXUZxUS3CeLiCqHTEbcb7U6qCjid5GBaX4uIMHmCNi/AaWbGqK5tOUES1fQRk116gY42xYYodR+oguV2iHV0Az6vP8Ab3XIEXq6aj4CZcrULKG4LDM4liQ1PivLGWGTn+3xfYzjzaNaSVCOii2xY12Hc+CmeIKbfEeZ1j5swLNls0Rx0gGdBOtBOJMvk6/XB0VRRZDnK8p5d9EhQWJ3bo92mwSiDmdMlhUr6Aq67HLUwstM0LQGrnkV39pA7eLrMqF7zYarXYdatGyh4nZ9cWzmzdRjsXbsZstsSy7+et74Xz7sETnf7NiiL6WjB0kbjQjMrfYt5oXuk76cUKDZmpGIAFu6E+0rD5ky9trUJQ5xEbSCR6lTkOY1Ob2soM6NaCJstC35ckmTAMBmMWgy57qo6be1EzTut5uqXtoquR4PDSfBa+Gaf2DetryWD2nv0MYYOIFnfOb6AbPbBN9DJMEfe7ctwazzVaRTbnNXA9xetKUQRZy2hnUfoxp3TFkyC8hns8358VllSS/vb2PkgJYjt6bea5AvoYFTqxaP3lwSz8voeQhXU0uARcDT4MbsJe6Oc/Z45AH4DNNhz3XFArXbPrGmFVkNWTaxpI1xOi3caXZFxa4Bs9RvrzKYFoWTUDs7fcCLH/C8qFxBqsiSrSV+RgSrooBX2gb8pyBIa4AfKLv8DYuUjoyhFM4raluY+FwdUKYOAxSTz5isYXUOCzubWQg/fj9zZxm4978fxOt1LaNCJzPKEoKexgTK4ZH7Gu6OLkkjDsXxF+CMAVfoVpfK3aIRzAFOQvSOXObL93fRfsBT2i0Al+zJUiKvh5+M2kdkREVmygmO/glOIN5gUPDo7uoADXI2zeo708Qab32yK1xuPyenCR1rs4W4GuUwntQlDfZI0h/e/vxlw8f3745SmBU4LiB5P7tm5d1khZ/i4ncRmmalWTov719KMvN396/L0iLxbs1jPOsyO7Ld3G2fh8ts/cI15/ef/jwHizX79nqDVojLL/8e4ulKJaDtAXUwWkzz3+vIvLpewoZ3vj1PwDHae0MX4P7NzI98ut7tuKvAt7CXfjtLcSUJdJInrok0aURTt6RYihAevb2DVY3+FJop3LeK9HXp6V1A+lTlMcPETJEvkYv9R2I397+5RdrnINDNWPUZc6HYHCYh/nyx8BN2XENyS1Hj7PXkmxYLZI7qENCH7AoGbC5JVP77PaEAwdjoo/wFbP7519+sWfL0xdylD/YgZ8vCw0bWfNR10zt/9S18PEX9yaGPtwRxtKMgbSHt/TkGPalZDjCch6+whSuqzURtKM1SWLYYFuCGK6jBK8f6FdBFoIPf0Ush8Udzbpt99HgJ2qpxt460d0oZa4FavPiuEqXiIORvHSXj89gUrIm/c4qhn58TEPscv+383QJXn57+79Jxb+9Of/PW7ruv70hDPC3N7+8+b+tOzG41+3O88Pb6364mEvS4RcZlr32hJ+u2vsFtszUVfTipJqezp1gqnt1pbVjfbiwPWweS6N2Z/QA7RLRXmcFzFndwcz9s/3iSC40KGzd/7aOXv67LdJag1MpfjwG/QVtwW4eqvVdSrLxuSP6gebzLvGaAOqdJ3ckoUz04bWFhn/RlxJiLrFENryxYIXMViXvlyJuc/S4z+NVlKMukCuy3U1LP4wNpyP1C5/gsskANp3aMUI6jt5B0KFxHi3XMO2eyAiAsGWcJnWy31Tj4M9v6Re0E7zyXFyG0clhFxkuSDkAGakw5bCdPUqS7LnbXoMnCJ69FrCjzSbPnsASv/WZrhbV2me+L7MyKL4WGcnGwQzWs3+hUDZ5KW+yo9hr/eei0N1RLR6r4CISpdV9FJdVDnK0IpSXyOLp7YNQrXxGLBMa53nxGd6XJ1HuZRW1OPzX0yaD4LfyAeRXg5hbt561GQm7pVmtHj84uMKOKrxxKmGMl+qj5ZJp0qv758Wn7DlNssjPaG1w+E3N9zSphbBF5zUyRPRaSr7dc/icNoMNktOXDczJDvZT9CrDaLT7axCSTEIEoT93f4mKRYQsBxBiVoeY7LfzbH3Zft6EWGhg+MGRo1UOAG1ruYxrgKh+fDCAxYHToDaJvvz0SYNkkKXLR9/R+K66HF0BMA5ydPn4+rCPv3vRy0eXnaFJbQ8MvIyyZR3aECUttpMHfFru4ggy7DqVitaHAqeFFwmZBLYeByBRipNFpU9IxBCy9vTGy9Irs/gxkFsPu1gIvqOnCKK6MKFwerhthH10Wr1gGmy8F9lzPdbmWUG/aUA2PLx/Jfvesyxv+3cM0LbIB+1xFD+SO4U4ZsPzIAJv0TC+85qGw7dTPCaFLF/N0V6Iianx1Qd4IfCRgYNlgwqC8KYoYnMMfFy9Hldl2e/VHUUGQ/+ExUMCi9IfYSPLCUB8iRTzwIXqgu8E2dsEFYz9/MI0guBLx7dkOW4DzTbjhLw7N1Ibiw3CEyXOAzE6y6Da6LKB3sA10Di1bXGfpsuAmFtX02lagrzw5sVGew2wgpEZqNFwk7aJ9gs3ENSy66Xz0eIJipLKvb++gynZ8YzKrKj/7elg0Vx19RlFnct8NGoPtynB0f+EyxGxfxmXNt2qFFrndIjDKpxQB5BhTkPb9MgUCrnnwT5KYFtOSYUjh08gf8Wzau96Gdb2cbzQ8cD2/RjW9unHcVSAZnn1dhR0uL6CqKhyQGKdw0badk2MG5PXNYN/DJty8EjW8Td1GhWBW8z3KKAJ7wE5DrgK4aYZIMRkCIlv8ZDVnp8oHyPMuYst2o94B20UwEeHKICWRv5+8e8FCBNkJQ/ZCm44tE25axBDtxr9MIKf7dBYIgEwNWdcxUlWbXCk8dJr1ngsMo+IawgfOT4mq6Afo/Z48NJt4Vc3Vjzi/FD7q3hcwo9wpJAtYiNuCRzlcpyVyAILHtOEL6+FFpSW7cIENh3CkQbhgWHObK4gCTBxCB5vK/oY+DhCbQH/BXxGMAjOKm6y+gYBi9nJh9vg+DY8RTAxdcyi/pANfx2lK43P3i2EO1AMYGCf1BZGbu2Ez2LfIsRFFCSXS39A8PxVYLMZaGDzqPNade2JMdSM5himUd6dJCKq3ZEPbisXmkCMR6OW7KUfZGcwAanOyvqTS8jhJXg2l2DbGwp7xjLz3hgbz+YYUZdaO3ma3Sz/+uPOsk07MgfXdFfTd+ZY0l5XCZBfrnfSfosNGN0B02QC7O7Se8VIteFqIZD1+VJvsmFCAQ8fc4/0Shox7TRVFGI620Jo5C2FJRsBT+yNEetF4dOXMo9wtpox7Sb6gGlPVJrW4eOQJuYkS7L8C3hpHt8Ji3z7MsXYmlJ75ouc2ZaSeIEM2u9renXgvDhDW8qqv5oSIKJ2ZA6kL1YduDAEF9IUte/DsPYfhhv3kA1HOZgJ7pen6H44mQh7MnE4TdD2bvdOEw7O9t12tpuHCbQJ25tXyfZjUaIfLbY0kbuafkbJZpDhOpzMnxdtZgWvQJldZdM948/PFewaqlL4ewUgQXkPsRqyD37LtecsLrfBTtdU0q1gWPvHPQOYNy2ysyxfR56Jk1pci6hPfLtdyYMCXSM+ur+HCfQ2hcl9Uy87aezV/byozwuO4kGeP6doBkVWX2WX/bLXXkRFKV1TnHgo6BKAu3eRrWAaKCwU42uvMRugtDl/oZ8osDMNOAQ+GxfRiwl23eExmPfHeN0VPhS3s8sueQEg2KEhZtJgyIRrrJtgZ+tNlL4GwiU6XzQxo9uKXjKC0OM0sU8wjR0cDEx1n56cUDf2PU0BIk8fQiL7GATZf8HNVVbgTC9uoVgCO+ohS4E4g5lbXsc209Pc65/5rkX06urOak+tp/eDg6eXON+KZmn1MmYJohDXzW6eswuAiXVeZNq7JB9t+e7mIQfAHP+fbPEjEQE5jBncXikXf0ReJvNW5Ut2dmwaC/3wJYs/iuxvwylPsGdvVimiXp0KzMv1PdEzLQGeNjHnbtqo2hPmHs/KnXFdvbvLwROMtC9XOdzxmlGhG/PpcZKtsD27Jyx6EaWrCmfas+bRvqYXkwrPjjktbeaIyZb6zZTFyW3tWA2SnbvBdaJIsGAm0n0C7FAo+wcvfWOdt/Ii+CGeIvzJ+xRb3Fa97ImeHWUtbml0UiXkRpH6SNj+3I8cHC7ACJvVsyRana9R1/EdLi1t7MOPr8v9uhI6rk1TR4AvSpyU+RoUgkeHd1buZrdvWnpqmdwlwU2LXHg9wM30wUmws/VVUq2gLJmq4UlyVsVytvc4OroEz8UePbk3O4cGtMDJs0thUOEUKmEwBdHA22nc/pH2Kdthvx42GC1a44syWZIcpcXz3sTY4QE5RKWTWsH9eW4qsX3Y5kdW+r29MfYlK0S0PeGa2Zf5cNxTh6o1Oi/MiX6QJTpYoiKyrH6uQFEiKxbLyDYcMG6nBTL+sSd9xrUnukC/F/zLX+wN5kBvFwWJAEZIGr7HOCRsb2ThescAtzG6hH9GCtCVT52P4QTyNSzwues1iLGm3V/m91mHRhGmLrGAH2LjuT59wVSNkqOqfMB0jYkxsFfzPvvVqXCRsO1snVN3agKibTRnYOzfMG/dZI8gzDaSoDuKY1AU4ZCS+A80wV5ZmIyF7izLq/UeneXfZBsY24tXU80vdcjcwm30DKeZvXBlcsPSJYPxrt2NJPJBuGNPBIQMyJ5Fm2o7LiB4HnHLngni6thdte/QIRfVZa2JvZxRP3zfgscXb3AvPFPwN1gkU+6IjSRK89ys76YG2ifl8znPqo2jBmrqjhsS6vTMSuATistm1fGS5RAKBcueyKg6qJVRPJy7qZ6IWO6JjtoJ9bBv7DaHU70m3n4wLRlLwAucBF/QBCrqnLy7sX8YJinwTXLg2RkmRYF3jgO/S9ykJr6/VLE9cRxWCFxNEuuAqOp7h4ETpHdpwqo8B2n8GuySeIvwOuqPbzWp1//qLJXD4y4/av+IytDKDG3aSyQLyXkaJ6irhuT4s5smbhvDtwanaOwGN9Y9CjDRCAeNTjPSRjdMM8KmsUlHhhqyEFb7xgZqDCl+iNeGKDkDYGyaylsem8DylsemdoM/zNUywiejM2L3pO6orRDVMVoTyFKvkLWzHPnNkWvwHOXLqwwtcMVPkAPEWp6JIx9A/JhV5VFZ5vCuKkHoXSPXQPGf60B5kRojQBLhZxt7FiZ3Xmf1b4KMkUTE4W0MfhsmB0iPnKD5H9opTtOOsNARQn4TgbsUzDDlxueZKLx4BEsZ6bx7evL0FCZbE0Z2+rKBOdk5fc3S8mEEvP8AUZix03z5CSLlVn4ChA994jU7NE1axy9Zsgw0VzzygIxAIT+O0sdgWykGbzARo/Gen4RG2ebICoz2/C4KtCA1GpoYBSGTuOLAHCQQ/6pfAc+jtIji4cPio6APxm6yBq5BUQVKnHsSbfCF6fDE4REH7DXad3Y2UfiuX1W4bgFCu1OvIhgq2JzJgBWEpg1KvFfBCS6r2mU4iq9r7GzE23ugQW8XrsE6gikVpO4QFf4lwk+0Npvbywy75eNBRqKARyXtrvB7gWz7LxBN194kXpz9EU5S1b71pppfQNYEOUw+w3ti7O8h67RDs5+8vqbX/H0vwPInLB8cWYip7t2VQQaG4O6WKZl1Txi0NWeoWUZrucO9XQkenwPDltT+h0Uju/nOi7ar5B5WxFovjkoDbc02ATcM12iwG4gtgVCWWocx3J2RBUixXR2qhzW6cN37CoqCujXr+YpIPyPENpvtTSG7eJg9SirTjSdoHMuM5trVvI+S/r2Kmolw19XfU1iSl/XGPm7sGhr9dHGK0UwykrFPQ9v9Ur1ej02yYWtj0260I7vwR3Wfsuc0yaKldzKi86JFFcQwukAsnhagxSm1CY1WX6T1fwK4enAyC01aOK7SZQI+RWUUxkHYvOWOL2KG4VG7Zf8SJ/jYp2V/lgX6kkqT4ncBs42ky7q3GbfceKS9m/vl5Zk96rgmqlfwKsFwHCURflfAx3wrRo+qCbkJC3sYQPs/HJ6UY+uP8KAcPshZ789DwzNq8ps8ih9hugp4Bkhi3sY1SshBHgh10vgJJPAJ5CZvRG7Lm2CtAOyRF6MbktP9jrpmgPMp3m88uvfAwvYoqzy9Br/jbHX7Mu3+16UCTdzs9k8Iv9M1iIosPcvymlfCGOkNxwGy/zXY77sgNQ1BsLpnxCRL9n3x5P7+MgsV5a56zptNzmv/IgKtKELcCtuWaBOLBTIjoT4nUb5Xi6S/tryK8sZw8PI81X4ht2NWuq7f89f9JPsfr86u/2c+9hjvsgSSTpAj9sfO/dH20yFWz51Tc83MLzaIhm1YXDePe6LzZhYLMW2/bYbRuMZ7FSU2P/nF1yPOYILI7ZkVuE6u3ZDPN7/2yFnjVQTdEwEQDzEU721NpvlZGGVPWGRnZqFRKjfRao8pb/mmtS3xfkQ5RLZOx8NoF3kH02ifNN5jZUNTozDMEqqfMHZ5sSdKq/soxpeAcrStklzv823F2Ww2etKB9Gss7D/hcjzkX8aNe4gKQCjvfgxo1Qz+MWzK5QGFooCrFEkgJEypeyX+g4NrqT0swbkEHa6SDGr77P3b7R6Of7Pvx7C2Tz/OC/9XK/Zu44/Wg/gxxIac7Gm+VeW3e4KSDHKMFHmSlXVPllNf/mr/7+hy+zUijreeAW9pu7Hjxg/uXjCPfQ6PwTvbPPpEPZLjHTD/ewVzvyBCyvhLyzxL/N2fUsN/Aj75qJ0WW0neNxE+SmAU/JGCgLtGr2hgXzVd38fbj4mWDNFZCwppJSsMqcfn41ijo4ksyfLF71WUg+J6dRe6n/XOYflPNE30AW/wQ486zGqChs4LhGkBEhB7Rr8HWGaCsLR+yeH7ThryX2kvYPqIT8NERtn2BBu19xP2RK+2wwl6R+57f43je554iQWPJsjll2OYRn1eJITxjnxw1JlpiUiFUQXp4xlMQBpKoeP31NIi1G2kc5xyZIR95g3istqftx9SNffZbLhc6/jFUPfIj7p2CH+WV2hPwAAL83M1PAd7ws4BDnMEiwSv0r1xLorkNMXwXrbYAsRVDiz7Z+Sapx/jCeZ6vshWWefZHsXVYYnnS7lOjrPla6h8cvX62vrIL0H5nOWPfnNjJ8aNu2ZPpLkeox+r1DhG0QvWi5e5lfEA1oBshO725hl1MqbQesp2Xk1wDpJDyd5VssY6HrMcR/HjeUqOV+ick3vCNiECpue1cWcPS57iPhsdSXED1mh13hvP/ShrB379FHWNS9ftjXjk8K8TRNNVlr8e5nh/57hRe4cp3t8pJnSF8b4dsobYMKAh1I+Ehp5ShDg8/4lsVbcT4PEM1KM4uQZxtjfJSrd8WzqFQ9TwyPF8mVjE1Rkz1Pf8wFBT+jmSahUcaYiwS5c3nMz9afEDWFYJuImKxz3hM6PgC1PHc5YuvVI9GZ1CGrK+vyO9zDbf0tM8z7zyl+F32hdllJcBciBgXKfpMhCmRRXHoChC9YsmlOPxgLn50L6xtidS6PwMcpjXmEXvmtiOwFJzm3uRQFRUOWhyT+3HdGuX5A8uSzJ56waN7BlbQaGxX2MONDyGtn+GeeStbcNDn5Bqq8NJDmx0YCMHR2fzIvkfhX3+4rLRlL8CP8TtwjqmcTDOnHORYQQWRDHPmHKW5Ws0ZVQWh0DoL+AaIn64yQh/WFykEpyKVXcJLB5GCgae7oiKRxb0MbRPGX4ADRniaDJ9I2KMlQ/in2q9j4e51JCCxvHOfsZKZszPg3OTbWA8W5Y3i9MhkkG/zX68J4zpH2VwlmdrdzYc1va77pi5d4Ou6xfYW939E8SaxIgONge+yxnGa4SvcUZei9950eylj1/rV3ADIeveY/LBN4UqIH4A9Oki25eIQ2pEuBUHHw2LYMdDhwxfut5GliQO5v1gy3H36dq94UcHpNZOeYsQpPUmgqt9sYkDHsqI11w3ZDgsfnaxD7gBNmYv8krjUUye5NoTFhM+PDmU8L/Y+2iavb9eewxxm14/Cd7fqyyXpWYyvSuap5aDNetYVBRaxe6CuNbBi8LrYiu+IgvuoyopkSATDkS1Rjn7IRvKP1rUocvaehzHtZaqDwT5hPaqFowuKxjs3lwugBktKlMl7aIVvW8+gGBeWfxSMI6lj5LkdQQhuwTPxQXATL2H3kXx4IL6GSUrqcGGDHcu4Tp3SxDeNu6nYboWT+XvLyBBDTeRhy002QJmofp7BSqwrKd7P6TjKodZ7nk/HXspNSda9qsZRmpwPGm92Nxkwbt6k43R0WuwSV7temuBdowen5yExohMmtAot96ICatfcWKXHAKvmEyMJMgDTgrbykDpD2v7nUOMa0+dPID4Mav2LluhVmm4hHxvZR5O8hIlhr+qb72FwHle3EQvpy+AGqkLGoSkvW7ptz+ZMNfo6LcA3EVzn5JMcoNzOCHiUQR/R8RbZRzSMIaVQIuXSuMqx88QXUWvmCD79VI6Ozp76eExBHi6dutjf9hR7wk7nLzGCRg+pOE0AxjNFUD77KVvJFgZJQSbnzWPbyIZvIk92X29AFdPzlOIDyAcH1wf1t76pCokQhn+i6SaJW3vibwJ77saTB9Hj9sTtJtAu8SlV6JryTXSMB36s0uHSGS6PBFBmJ79iXEyu9hnXU/H6uTHYJ10TpVgsTomRD7qjeSeyOpVmwPrg89S1GH5OP2u03zHmGdFgWzz5DCDYWbQYqHblwBSNJIL8AQSPwNw8ZDlZRthHyQI6wyBmyI0cr5tmuCD0KEHY4a8Gj38gCgUKoc74lSQ5yAPhW+WW1JGieBz8HtFbkv6bJ/q7LlfokKXTivQLasFqK/o7YfmGSXSaCfyS1F+8j/KXG5hSjlyfW1P6F/H24tmwW2xTOOkWoLzdAER2qjb0bmdLbXhoqig9HYjy2JPncZ5A0v+QrUTpmBn7F9BGTX3JjhTxRmh32NpYpzhaBcwGvEaICsrPyp+wuUKeJ1l1hj+K0vDjLFG9zOPNo3VIOmbOS5kcj8P5sB9lMcZVrGechkgWosMBz/Ubk9x80emYEHCNHwW4BbHLb8SCOHbtgTw+pVj0JjdBoOq6uc6pvtvucfpq1p0wXo2PRwgtpPZNLWrc0l3364Hfc0xZpKEtaDNDVpaHrLlCR6qm3AOERmLaN2g06wKWrSjLIfA8wpsPxRLYe1qjjHDrbuj9cM4zG2LwnRWm6acZnXQlptjx3cm6e7b9aCvOcZMXoF8DQscXFJntaVT8LpMK4vPWGipdp3mWNiwpVbkUQS5Qd+NyY31mupjyrHvrNsKs/dsb4FEb//sNqt88x82xl0EWmojqcBRY25SPK1JI+1A23+nPtSVR5zRa/AEwfMXkGzuqyR1XIADuL5Mol6CTEc9YOcJaat7deVnVDQUHyFg+DjJVm2+k32dy2Z8RlmUjPx0iGZXWeEQQNnXHENI8bXBP8pcBnMchmYOPAnnJWDT4hne+qxrjqLBsyT5keGjv1lfkZhAc6OBHqXFs8tpOl139GV0f2dg3qcHw+mGep6CqQacGrpPjuv0dG9tB/wDFCRaOgCqy8wSk4zXj4oiiyGZWWYXUL99fNv8z3D9abp8g3ciCLwpb3qyAMn9u+7b1yop4SaBMWr5t7cf3rKS8i2tw6zf4HhtfF/lJCriaMlTAfV+Ke3BoMPDjjBFw/78D64ZEgRTp405ydKizCNEWV68YRrDTZQwFGDgDBUBHlyHkS35BDb4VCstxUM1aZF6w5pvuMPPUFtHh1/fU3xjx05Cbz03maJZ/KOwk4hC28NO/SIxCzt1h0oB9dIv7/7/9q6sOW4bCf+VrfyAdeXdu1WyHXtdZSdeS04eWZQGklgZDVUcjh3vr1/exNHdaHCIg+O8uKxBA33gY6NxNf75s9F3c0vTdqHc1Pzj1n0KvBuKdX9kbzJthV09NyZvDw4M4hfimF2Xp+oOR8RYV+nH+ccgzsXYgoZk8YytSeUA4KK33BGe9LZsdJjd5FV7wGQVd3HBMHPq8hgwI45mRIPZMLxuypdBIZtRdjGezCU8S9GRTQizuLH4gXh8fAUMxZfAi9xqC4qu8T9DQpp2ZVbfY0rTd0kCg+Io5Rfjw2StXPxY7EWErOn54quovt+0937QvpWJlE5VCpzmgA7ecC1coKr6wYbTuCbJFhsTUxSHnOpNKzDXhIbwdEFhua6RS1SeDK42s2IZEVTh1yzdVq0iL1q2SR3v827lrAq4cCmzVZpTC7a+gKlos4VFzAGOCipwJc7tUL9OBxUJLPcCNef+X8cDObOVKySFvc2McAmALfxI5+7eIo922pWWz6JprWjv/vZ3TBg7NWoLSk/rRUHgB19QwsWaKLxAUDNBACQyLmghjDk3shLBpG1bB+h1pJ9/CBS6oCAa/PCrfaFwVzfzlEaur42qIgNFTwtjsrwqttSSzWNKUccBS9GgNN7rWxFElvmjljWra2n6besAAK+iJt71BGjP/4aTA4P7JxoQEopw8U7DjXcTp9f1bKH1WEN1D9OPQQYZ/TY4JIpnPzMqHABV5N13hCV9PzYSsiwBsoODuFhcufRxBFjhORFCo+pVsd83ET2cD2IRpmyHeF383nbRwOKlGj8+Goa0WBmsRKKDl5bLSxFlKruYwWvUyGXwioarPof6dV11tyaPXUCUjYnzUXBNBHKPzj8GARckuSoPSOAFZrDB/MAMVIvDV34OIQrWxqvh28DXKK0iw/zj5nE0qbIJ7Iw3x7eBnVFaRYb5x81jZ1JlE9hpr7xvAzetpOpmbffD5vHSqbEZrPTpETJTaNee8oiSXkZDgvFnP/v53F5cAS2DIhxuLXk8vOh57eTcY9YVRr222p9GYRh0UVkLKflUQj8I1E0SAo3sLI4YPllpGxMAq21XHwMC1fE/IEidARIboJaMkaHXO508aFLLXLYPJCD4gi54LQVccmDbkAdMEXShPN7ZwIvr6X75q33GJt9fnerHtsW7rlAbkpP2eJQGilw04eY9IKmeCyCjYfFtWZ2eukXZtYGH7y9OPJWWpF83j4tZl+2AoHvnKzQK+sfFDBgMP18GDoAH1JIGQtb92z9RTXae8or13HnDz0EGoo6hKYIn6GCG8QgeFqNZrhRcCCC3e4/5xEsEp8Pvy7AepyNPIPjAxD6n63xCKHj04tivQeOXTqxoIOrSKHs4JEfFLspbq10jwy9bPxvXq8FhpBo8cu9vYtIcFjQhw1w+aqJHuGN6pi/H/EH8p2ikqb5ncF6pRNNwyZKD8qgEF5OIS1GLwzd6Ji4Qa8C34u4jfgCI8X1KLHx1EsYd+LoTcSkjapLSlMDjUbhg2JkV2RJg0s9hEgc2AfOVuAEndpaSd8V9/TqvdtmnU/s0zFHs/ijqR0SH5d1ouZY0SqE0Nv/oz5Ow++lMUEy6sDABdkV0iCixDqzQ0i715GsgyUF5AgQ9TghYCW3Occ9YMS2sfZE/hUSjoZSgFixGWowzpUfjhk2/lrVIP85upTQl6H/dNoZmRdKPsz+Lbw3aP5VNA8fROW1ifRIQXBEHLN/82iWk1SZWMiGcrToIWsLxdOASzA0txYrSLXET+7XvGCQ9ko1CGsn6PL4rEQxAkx7pD2OjqN2KESz30l7zjBxjwUEt8JZnlN2xK4GIvSwxVYi7STumpgyXyObvbfqfNKNHjFbqU3VoH9MRHs4ae4qHJZG10EYpuYAYWNZnE9HvdIEnvFf5Oz0W6l5ixrZlJ8jrvOpDllenw24vyO2JYVtFogR2vORSJ5joEmkhiV7oc0cLs4UfABm6cXjOIqYDoU2MUinALORYtQhd0YcrA1vp78GnAKyAO/KLcBV7Y37gf/0s7or74fbhVV1Xxe2pFhvAGCk/JBdGuXX00eptAYqw6L89t39mHL1k12PHAw8IvlwjoStDypHQz0rUcgid60EpZVmTCbCBRJ0rX1vfwPkhffPZYIvusCkNYmN+DB1u8gd7DrNUoohZZkgWufRCogVJJYcIIWL6MQBclow7dJ8CnfkjQIzb4TFR1pBHB9rveVXkh3ryra/Lp9vi0BFm+JPx8n0YmUi5B6MUOC0L2mWDoEWS+7nihRrIK9ooRTlSyGKnjL/NzM2TB2r4cfdcjMaeqjPg+d9TU9L00pdDgWNUIZKxoBZcpHvEDZQ09GSxU8Pfdn0iB5IX6/227PJGyNmXJA1CoMtDL0Mmi8agy44rwDKZtUZdid/z/WkCKa2hH1yERW6nLkfMgdAnhhfhyQ+Ue20d8Kw3EBvW0cNJTsiw7cBwc9Ffdp0/Pe/Fm/LbYV/m+BXhiUBZf5l+TBwNsHrxkaAaPxoWbgpRNfrdCSUPu/W8VUfEzb/eYcLLKDZJr8gi/XoRydZnfTjMZOkSgFX6U8o4IAo4bXTDTyITxfk46nCQOH0gGZK7nWbeHrBMlThMle5MCGarAswaAv2NEpppcviY5nJvi33d5rv1fr0hlJvSNKOFNIgv59qEVVe+d4t6jeJVfvfn+8N1Xd79eX26Pd5VRX8GdBO3KRDh1TegMZrN363ANHMJ/JPDXfqRW3qgCzg+n4O52KP01d0eeEhz3eULT5ibRFcvxM6/XsTaxazPZtYuru7vi33zi8gCvWUwMVQbmn/d+oXmWRUOs9iJErqnN7wGTjgUDN7mEycXFvGYOm0i1mn0+9oA+mMD1PxBZG+r8ikcSlTmWtykFm0eH5pCHI5yZ6QCkJvyb3gkAo+5KyKGrXXxtTH5h/Ihk/7fdhQebGh0atSolQWKYCeumDS+YhfKZn6AJivFimJUEZOA2iYWgOKhKqTfcoVTdJf135M4id0vT3mxz7p/r+7oF5sUIuVFaqUgCKYk4bVzTdLvXjCFm8oPrmSNOOxk+eKlEnsUd3+WJ/3UpfEz7rcMSsWBmaVBUAerRYvm81ylxZ5+AIloyPJ5etWIuRPvTlWj8cOn/HuXv/f9oWjbXSmNLx7K64y1JIh64bZz8xr6sHhKPZEMPsZs4LRGa/Wzt+yboFKkaH7TjC8DyLqgdMk6rteNhs8P5V2+L/4ndqOM2Yf88HAyZsxS708EcnfPPwZBoCG2KoxZ6gV1sKn8oM3UicN0lDAivtaeXPo7M6zPKy9jPrmZeeSwY/xZfC3Et/+I/fP9aX9oE9e2XTI33q2oT5hqfq/bsOumzLD6VtgNbYDom8qcj/GZcgCnGAAixq2JszA3ahRkCZbTI068Y+JQKfgZhZVChnd6NEitew5lUCZ1NCmV452D2pcPr8unbqLI9GpSldCOTGatnHeSf78gd4Wa2oldYBi1//9UHmvcH40URh/2P4Y5RucOpZXc1KRoggAaZYsGpF/Ft6OjP5KqhPZHMmu5SeX3C/JHqKmd2AWGUfv/9ow77o9GCqMPA950WACllfzRpGiCABplixd6l/v972VdHB6Gs7vcqZ9WL/iUT+evxOVG4QX5KNryTjxj4Kv94epw/CYqYj430RjdOv4c5nbWOSBba9I3K50quGYR01hBWLR8FXXNir9msW3XtdICRnBADX9ZV6DiX3h3xdP2rki5gyjsxahfuqRC7VfV1BDVuNZf7sTbojrWb/I6v82P5mZfW+ta1EByop/+0ZeiSaCu7x7FU/6vn3a3ZdPz+a2ZP8pwQyq3YU/0o6gfyx3ATiuH+GkkNobWq8ymDNYqoFjWWq6SckRjysJljrMkGVmbf1Mch5N/RvtzEcRgLrVweJ3X4qE7hGBwmIsgDnOpzUTFXX2qBGSisQQ00VjI6wGCi0ZA9Ec2EGUf8+5xLXb/tG+RFpXo58hoVylUVK8phDYRlNzuJm+lGGSqUPCsTaDGoKDsPVKxDT5U/JgfTvd511eEJ1KpKDlkSrYsFiHs3FUK24eqR7Lm96pTgJ+tTsRkS/CjGVk5THdADQZTCdT+VGhToP2gYP82loDij4WW5scXZdGRWSeAmOk0Np5189k00P3aBGTQN6+VgxxVEgvDeWfE4DUXQWzmUguH+ayUwWEugjjMpTYO7WEhcV1X3dT52D8SBHADyUDOIKVFinlN1+A8F0Hc2lK7d5TWX0ynKBWCvlAqZ/BBOOBts11Nn1YAdTd9MeVyegqbDqJ6Ko7HZm4wrg6Z+hgkoG4GlYXzL381Ye0h31+d6sd2/tI/qYVKQZNDEtE1LNJ1V4yRj10qg/h2xceM9cF3tDflc3GH8RkKCUYDBYcTxoRsf6DgtP+uKk/PGJOhkOA0UFg4DQfiDSbD71D7QxEzjP3SXgudTiWjcaxKRgWyKqVFinfFfRM2VjuLFDAZJAVMyZSC4Exz43UjMghIZWh3siamHeWvZQ15UqkMZdIXW5h8Ft8ahT+VxaE+4h0GUkGMQUJG/IVMu+YiLOZiTbBGQqTH1GKKE6vfPotmOnBoJ4ACdMFaOWxGhcSqnv40NqCiTgKrqVPx5nLYO5zYrA6jp+Z3yKOo3Jke/bypaS+SHLQdWWORdGy5XCTi9mn3piDWgV0h0VtdOY8P+ZASxp+sRMhF1lsmL19Iy7qV9jCI63oKQyCeJM5IQZ7K4JplIHfotaGGLSKaMvabUdBUBEY+U6mFg5Qy22AhlUE8pGLrSkEJrokOv8MrAyVj1bWjmkAGtz8Vo2y4KL1p6ouuI2/BqaFWDtpMJbEteGDpC831D4wSXA7BiB1WGm/E0/M+B79UmMy28jhTMrcKCAlMEmrrgM15HBhwxgYFNb5w2b4TB9F8ZpRzNEnAqYFBZVsTnRMbmquicxm4LjoXW5h8qXAmUhnERCq2OYqm9u60Fzf58U/IUSjFoKNQKGx2m9MAmnaby0C7zcW2r1Hkx+bD+UMUD49QiK6Vg9+fSsJj+KZoZhFH2A2ZJARbicq6JFdV4nAHrp5PRfBS3FjKWT2x+FiAhlhLcfKseoovwLGoBNS6l0Zq/calFD3AVy6Vwt+5RMBn1We8otj1FBaWPZF1wHh6zosHqEvnIniAGEttS6lKdhxz6VQpBpdKFQrrd9j1LDUC6xTwV6gRMfYMPoi6FpXlO8EIsf0EiNYii5IDyBBAKYXPvEgENvSYiWhMGJk0IJ5MMlfm2LwEI2SJwZuLoGkjgMUhhBJeJkKIHcVhyMETgLeFp+YwMFgDNOjmnUpmVbv9WHf4gR+dAFZZpbGhsCqPx6bxPc7VJAGRZ1BZTQ0NSuhgxBmEmv/VyGxxLAHjv7HQNkfM/yJOpSil4PxQJrCxQratiB0r3lbVuG1CKGKSUPsv2dXz874Qu5tyoC+EfRd5rIyDzqDgyTCQ2yVQz0PgJzgQOvsRi8/i2Ljg7jAv4wTJsK2MH1QxKKjd6YGI0RH6zrJlg5wm52xdZy0pe+1wOkrE27fn798rzxO4LmVyVqCZC9HZTOcqBZBxABMGICVkAqgZh3em+3vm+pVcih3hmQgYkSrOSinFYlIuK/Nyj2lfgwQ7mqJSufSwrVsZfWkwlI6tY8dcs+k0q0QMnnedKPVz9dLFBPXobMMTPX0LXWgAq2tF+jUJVcUF6o8Dj119+N4DdDPElD8Z9adzskS/GzTr97gel3QV0bDJXU0qbMmuhxN2puacarhS+hn7Tins/DxYEzSLWejXPDd59SDAL4JTbaU+T8w8Y8DphB29kk/kQL7HKPNpGBfU6JXWd6mRzAJcuziSJgEr+MQJcL9EaYS6MLJ4sM3UmyHoUKvSEcoAN1V6LYgbKEtxtVztaRydXROquknrw5Eil25k9Vd0o7pq9ljLIPUWbAUygXIdiAi5QLr1wy7oolFXmbxAtLjntctQMgsUAnidldWCUIE2BJZ7MZP9IwHJvX0ogU2irq5l4/KaGNbk6PCdXxlXG76G1WlMX64iWtHWHYHGsEtiXgyIh2r8ysToBCiLqJeKyeQrbNnULmAekNCDKaBreb0JyNt27qoPS8iU0jrJ+upqy+FdPexS5nIVNdPhiqqEq/dSQNWnZfBpp4Bwnjgx0ePand6+y5H7umBNyABGmQ9D4E4QJ16lH1Mww6tiv28c6dQyYQSN1J8JWBg6Q+kxG9TcNK61QevzE9Du4SsNYJfr3c0AXTKen6MAbEFXwNXSL2B3+mCXq9WaxMXqvhXORWl304x3y0lzmEQ+TKDfj+9qonff3VUdr4aTqppEPlTVL7B3Necfz59clfs9qaZK4ENF+XZ7P2eCbq8vU62/YJ/1LcLKySRnCmnUUJMDTPWQa/8LVNQPFMg7+VQYw6tIKIfc5u9VtNzQJ1uCTjeArZLZB1Y3JR4I8SraBzhDYUrBJE0InidhBdOO6FshmrAZPYxpGOF1cDTFMhGVRYMMR3kVfaCJk1mka80pQ4i76aY8IaSdACofRjESmnRVpV/XUbc74WnXVyPzprByRnXWGD6HulDlTM5ugugr01hEVnKozCKDuVHM2mbFdXt2aJLs1p7mbEHj9Gb3JcrNUl+sRLei6IG/2y7RCWNVBaRbf0lFSe/T1YKz9yxVlPJNGoUPt+RPPSjbUDafr8APcsAViBMMZx/kgHIbKa2QuYpWMs1gd6ZdwBeN3fs1CXNM+ZNwG+gk6ypu5H6a6620gDLLT+xJm0Tr70T7V3XMuJV9auZnj/lR7P4o6keJg6m4rcpq6ih19ZxiXVU0X9hyQyjf7dw+bga4wkqKgDXRL56VrG0l03yR+51tH7XWun4hlnmmXG8WhyiReHCIcp66uR6Ygs5dRSCrHBkKkfQ+AiMii17XCCct3jpmsX4X9krroiOOacaEffg3oVGsq7Se0vB6PEWz0vFeOR9hNreLa6kSriQ4WNMYX8nciUsnQuOJJOuUTyfc0pxPyQJpcXggpR9XB2S3HL5kKmnlGQvcjM5Gadfvb9/HJvQknJn8lgn0hePk1hmB+eKKPDfAH1HRvnw4/ejw9VtSiq5gIOrLwIl9fBzRTUFMGFHa9eeNoc1AppVlHOy2VFzfQKy8uXJrzFyuCxBEpK1Fcu6C4FrQDIEfu114BmG0qqYAJtpGUvmujVaauSuEydYCmyrRL8DMFEGecsGpvbkJMx+G3IJc6sUYxDkVnNqqFKINoEYaJiFSR3NvWbKbIFbd17qAyUi7LVvUJXG2FxPbB3FObW+f6NbMqb5cucimShO4gaBHNjtTUI9nbtXEC2AaHpsca/k3ERlOsutalSe15sWNSZqxyzuZoWzZJqXbCWIUTrtK7k6qcTgf5/JAiOkog3tDvwkUsuv86Xkv5pcQcMU1SiJ00d5j6MMW5K2FgCpPTzEo52IhlRFK+0LS8pOzxhMTXXX89Yhz1CfGLpNo/dHKv6rGsuawVmoftNEq3gbtpSuzq5jFySAXbQrtJXPLWrxL9TBr8zaJ6KZt77gvuPEHP59CLuFb6/hYybc8H9M1xH0LZj0zER+mrcr6n2dcE02PpFiHbYTS57BtPP7S7y6i77osUH986yQjdkVNovW3Q433Wvqa6FMsC4+2s70FQe3DT6CPn1xPR91XBr76Rkn2tiqfKHtQ5D4MAj/HMjgV8nWVs01xUzoYQiLeuhmkR10y44EXyB/i5JRLAx+gGRwb+aoM1g7WxsomoUAB0vnAQxi1pcdZMvVdHFN1lBZXAnqrp9OCeoNHW/8wHp8Zlj/wV2UWnBwCX2nJgDdkgJNE3LoESrBnb3q42F6yodsyV8KYb9IsOX+mvu6SvT8UdZHvibOlVAVcyWXnSuE3bK77o2iWd2nON8Z4dtZkZbcLWteTumRL8KFc3sNCC7PhyK/22FPhwNS4gmfmwYFeJ+qbsD44tMQc9PjkfVzSx6N1xqFh+mi8dJG14cHr8nCsq7xo/EMbjU76jbmfbsoMq08suqzPy27uBQmkoJk3+tKIPBW3vxqyYjcpBT87WV2rylQe1zhpA0ovq3DhpjzGAizjnNeif8wCb9H0Kz3EIzPnGXbMwgXiECakxWfn+YqguPSADrf/lTd3TAud2aJ/RAFPDnVtUG8JnWfYMdkZiCiYkBafnU4tguL660jscdF4VQkYBdZqO8BYi7w21Q8RthekVjD7nKUNHlApelotp5RwcY0iD8WLAjRrLLKs1cCxHj/cOdvIw1/2IG6m9LaTu6LyL1/0jbSmb/pZVFPZyxf9e23DD82fzdS1mfl9LHdif+x+ffni86mp/ST6v96IY/EwN/GyafMgupcl50ZHmveH+3Kc/2kSjSRj8fTkdJ3v8jq/quqizdffFN810Wf3EmG3UtMun92K3fvDb6f6+VQ3Koun270yu3z5gub/8oUh88v+EP5xDRUaMYtGBfHb4dWp2O8mud/m+6M2o8aaeN1Y/51ofu/7svk4a/HwfWrp1/LAbGgw3xvxLA675qMb3+A+/na4zr+KJbJ9OYoP4iG/+/6pzRzeLTxhjdg7QjX7yzdF/lDlT8ehjbl+82eD4d3TX//+P1Aj43xvEQgA - - - dbo - - \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201506261756463_PrimaryStoreCurrencyMultiStore.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201506261756463_PrimaryStoreCurrencyMultiStore.Designer.cs deleted file mode 100644 index 11aadff013..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201506261756463_PrimaryStoreCurrencyMultiStore.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class PrimaryStoreCurrencyMultiStore : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(PrimaryStoreCurrencyMultiStore)); - - string IMigrationMetadata.Id - { - get { return "201506261756463_PrimaryStoreCurrencyMultiStore"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201506261756463_PrimaryStoreCurrencyMultiStore.cs b/src/Libraries/SmartStore.Data/Migrations/201506261756463_PrimaryStoreCurrencyMultiStore.cs deleted file mode 100644 index b54d2c2f84..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201506261756463_PrimaryStoreCurrencyMultiStore.cs +++ /dev/null @@ -1,160 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Data.Entity.Migrations; - using System.Linq; - using System.Web.Hosting; - using SmartStore.Core.Data; - using SmartStore.Core.Domain.Configuration; - using SmartStore.Core.Domain.Directory; - using SmartStore.Core.Domain.Stores; - using SmartStore.Data.Setup; - - public partial class PrimaryStoreCurrencyMultiStore : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Store", "PrimaryStoreCurrencyId", c => c.Int(nullable: false, defaultValue: 1)); - AddColumn("dbo.Store", "PrimaryExchangeRateCurrencyId", c => c.Int(nullable: false, defaultValue: 1)); - - // avoid conflicts with foreign key constraint - if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) - { - // what sql-server compact does not support here: - // - Update Set with a select sub-query - // - Select in variable via declare - // - Alter table to check/nocheck a constraint - // so the the foreign key check constraint fails here (and therefore this migration) if there's no currency with id 1. - - Sql("Update dbo.Store Set PrimaryStoreCurrencyId = (Select Min(Id) From dbo.Currency)"); - Sql("Update dbo.Store Set PrimaryExchangeRateCurrencyId = (Select Min(Id) From dbo.Currency)"); - } - - CreateIndex("dbo.Store", "PrimaryStoreCurrencyId"); - CreateIndex("dbo.Store", "PrimaryExchangeRateCurrencyId"); - - AddForeignKey("dbo.Store", "PrimaryExchangeRateCurrencyId", "dbo.Currency", "Id"); - AddForeignKey("dbo.Store", "PrimaryStoreCurrencyId", "dbo.Currency", "Id"); - } - - public override void Down() - { - DropForeignKey("dbo.Store", "PrimaryStoreCurrencyId", "dbo.Currency"); - DropForeignKey("dbo.Store", "PrimaryExchangeRateCurrencyId", "dbo.Currency"); - - DropIndex("dbo.Store", new[] { "PrimaryExchangeRateCurrencyId" }); - DropIndex("dbo.Store", new[] { "PrimaryStoreCurrencyId" }); - - DropColumn("dbo.Store", "PrimaryExchangeRateCurrencyId"); - DropColumn("dbo.Store", "PrimaryStoreCurrencyId"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - var settings = context.Set(); - var primaryStoreCurrencySetting = settings.FirstOrDefault(x => x.Name == "CurrencySettings.PrimaryStoreCurrencyId"); - var primaryExchangeRateCurrencySetting = settings.FirstOrDefault(x => x.Name == "CurrencySettings.PrimaryExchangeRateCurrencyId"); - - int primaryStoreCurrencyId = primaryStoreCurrencySetting.Value.ToInt(); - int primaryExchangeRateCurrencyId = primaryExchangeRateCurrencySetting.Value.ToInt(); - - if (primaryStoreCurrencyId == 0) - primaryStoreCurrencyId = context.Set().First().Id; - - if (primaryExchangeRateCurrencyId == 0) - primaryExchangeRateCurrencyId = primaryStoreCurrencyId; - - var stores = context.Set().ToList(); - - stores.ForEach(x => - { - x.PrimaryStoreCurrencyId = primaryStoreCurrencyId; - x.PrimaryExchangeRateCurrencyId = primaryExchangeRateCurrencyId; - }); - - settings.Remove(primaryStoreCurrencySetting); - settings.Remove(primaryExchangeRateCurrencySetting); - - context.SaveChanges(); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Configuration.Currencies.DeleteOrPublishStoreConflict", - "The currency cannot be deleted or deactivated because it is attached to the store \"{0}\" as primary or exchange rate currency.", - "Die Whrung kann nicht gelscht oder deaktiviert werden, weil sie dem Shop \"{0}\" als Leit- oder Umrechnungswhrung zugeordnet ist."); - - //builder.AddOrUpdate("Admin.Configuration.Currencies.StoreLimitationConflict", - // "The store limitations must include store \"{0}\" because the currency is attached to it as primary or exchange rate currency.", - // "Die Shop-Eingrenzungen mssen den Shop \"{0}\" enthalten, da ihm die Whrung als Leit- oder Umrechnungswhrung zugeordnet ist."); - - builder.AddOrUpdate("Admin.Configuration.Stores.Fields.PrimaryStoreCurrencyId", - "Primary store currency", - "Leitwhrung", - "Specifies the the primary store currency.", - "Legt die Leitwhrung des Shops fest."); - - builder.AddOrUpdate("Admin.Configuration.Stores.Fields.PrimaryExchangeRateCurrencyId", - "Exchange rate currency", - "Umrechnungswhrung", - "Specifies the primary exchange rate currency for this store.", - "Legt die Umrechnungswhrung fr diesen Shop fest."); - - builder.AddOrUpdate("Admin.Configuration.Currencies.Fields.IsPrimaryStoreCurrency", - "Primary currency", - "Leitwhrung"); - - builder.AddOrUpdate("Admin.Configuration.Currencies.Fields.IsPrimaryExchangeRateCurrency", - "Exchange rate currency", - "Umrechnungswhrung"); - - builder.AddOrUpdate("Admin.Configuration.Currencies.Fields.PrimaryStoreCurrencyStores", - "Is primary store currency for", - "Ist Leitwhrung fr", - "A list of stores where the currency is primary store currency.", - "Eine Liste mit Shops, in denen die Whrung Leitwhrung ist."); - - builder.AddOrUpdate("Admin.Configuration.Currencies.Fields.PrimaryExchangeRateCurrencyStores", - "Is exchange rate currency for", - "Ist Umrechnungswhrung fr", - "A list of stores where the currency is primary exchange rate currency.", - "Eine Liste mit Shops, in denen die Whrung Umrechnungswhrung ist."); - - builder.AddOrUpdate("Admin.Configuration.Stores.Fields.SslEnabled", - "SSL", - "SSL", - "Specifies whether the store should be SSL secured.", - "Legt fest, ob der Shop SSL gesichert werden soll."); - - builder.AddOrUpdate("Admin.Configuration.Settings.News.MaxAgeInDays", - "Maximum age (in days)", - "Maximales Alter (in Tagen)", - "Specifies the maximum news age in days. Older news are not exported in the RSS feed.", - "Legt das maximale News-Alter in Tagen fest. ltere News werden im RSS-Feed nicht exportiert."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Blog.MaxAgeInDays", - "Maximum age (in days)", - "Maximales Alter (in Tagen)", - "Specifies the maximum news age in days. Older blog posts are not exported in the RSS feed.", - "Legt das maximale Blog-Alter in Tagen fest. ltere Blog-Eintrge werden im RSS-Feed nicht exportiert."); - - - builder.AddOrUpdate("Admin.Common.Deleted", - "Deleted", - "Gelscht"); - - - builder.Delete("Admin.Configuration.Currencies.CantDeletePrimary"); - builder.Delete("Admin.Configuration.Currencies.CantDeleteExchange"); - builder.Delete("Admin.Configuration.Currencies.Fields.MarkAsPrimaryStoreCurrency"); - builder.Delete("Admin.Configuration.Currencies.Fields.MarkAsPrimaryExchangeRateCurrency"); - builder.Delete("Forum.ForumFeedTitle"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201506261756463_PrimaryStoreCurrencyMultiStore.resx b/src/Libraries/SmartStore.Data/Migrations/201506261756463_PrimaryStoreCurrencyMultiStore.resx deleted file mode 100644 index 7e45cc22c4..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201506261756463_PrimaryStoreCurrencyMultiStore.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201507072138058_MessageTemplateAttachments.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201507072138058_MessageTemplateAttachments.Designer.cs deleted file mode 100644 index cb29f9e05f..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507072138058_MessageTemplateAttachments.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class MessageTemplateAttachments : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(MessageTemplateAttachments)); - - string IMigrationMetadata.Id - { - get { return "201507072138058_MessageTemplateAttachments"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507072138058_MessageTemplateAttachments.cs b/src/Libraries/SmartStore.Data/Migrations/201507072138058_MessageTemplateAttachments.cs deleted file mode 100644 index d83d2520df..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507072138058_MessageTemplateAttachments.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class MessageTemplateAttachments : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.MessageTemplate", "Attachment1FileId", c => c.Int()); - AddColumn("dbo.MessageTemplate", "Attachment2FileId", c => c.Int()); - AddColumn("dbo.MessageTemplate", "Attachment3FileId", c => c.Int()); - } - - public override void Down() - { - DropColumn("dbo.MessageTemplate", "Attachment3FileId"); - DropColumn("dbo.MessageTemplate", "Attachment2FileId"); - DropColumn("dbo.MessageTemplate", "Attachment1FileId"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Common.Replace", - "Replace", - "Ersetzen"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507072138058_MessageTemplateAttachments.resx b/src/Libraries/SmartStore.Data/Migrations/201507072138058_MessageTemplateAttachments.resx deleted file mode 100644 index 3970382b24..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507072138058_MessageTemplateAttachments.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201507092146153_DownloadGuidIndex.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201507092146153_DownloadGuidIndex.Designer.cs deleted file mode 100644 index 0693d8660f..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507092146153_DownloadGuidIndex.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class DownloadGuidIndex : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(DownloadGuidIndex)); - - string IMigrationMetadata.Id - { - get { return "201507092146153_DownloadGuidIndex"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507092146153_DownloadGuidIndex.cs b/src/Libraries/SmartStore.Data/Migrations/201507092146153_DownloadGuidIndex.cs deleted file mode 100644 index 803e4557f6..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507092146153_DownloadGuidIndex.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class DownloadGuidIndex : DbMigration - { - public override void Up() - { - CreateIndex("dbo.Download", "DownloadGuid"); - } - - public override void Down() - { - DropIndex("dbo.Download", new[] { "DownloadGuid" }); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507092146153_DownloadGuidIndex.resx b/src/Libraries/SmartStore.Data/Migrations/201507092146153_DownloadGuidIndex.resx deleted file mode 100644 index 457d21844b..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507092146153_DownloadGuidIndex.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201507102159496_TransientMedia.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201507102159496_TransientMedia.Designer.cs deleted file mode 100644 index d738e053cf..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507102159496_TransientMedia.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class TransientMedia : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(TransientMedia)); - - string IMigrationMetadata.Id - { - get { return "201507102159496_TransientMedia"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507102159496_TransientMedia.cs b/src/Libraries/SmartStore.Data/Migrations/201507102159496_TransientMedia.cs deleted file mode 100644 index f70f8ba8a7..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507102159496_TransientMedia.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Core.Domain.Tasks; - using SmartStore.Data.Setup; - - public partial class TransientMedia : DbMigration, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Download", "IsTransient", c => c.Boolean(nullable: false)); - AddColumn("dbo.Download", "UpdatedOnUtc", c => c.DateTime(nullable: false)); - AddColumn("dbo.Picture", "IsTransient", c => c.Boolean(nullable: false)); - AddColumn("dbo.Picture", "UpdatedOnUtc", c => c.DateTime(nullable: false)); - CreateIndex("dbo.Download", new[] { "UpdatedOnUtc", "IsTransient" }, name: "IX_UpdatedOn_IsTransient"); - CreateIndex("dbo.Picture", new[] { "UpdatedOnUtc", "IsTransient" }, name: "IX_UpdatedOn_IsTransient"); - } - - public override void Down() - { - DropIndex("dbo.Picture", "IX_UpdatedOn_IsTransient"); - DropIndex("dbo.Download", "IX_UpdatedOn_IsTransient"); - DropColumn("dbo.Picture", "UpdatedOnUtc"); - DropColumn("dbo.Picture", "IsTransient"); - DropColumn("dbo.Download", "UpdatedOnUtc"); - DropColumn("dbo.Download", "IsTransient"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.Set().AddOrUpdate(x => x.Type, - new ScheduleTask - { - Name = "Clear transient uploads", - CronExpression = "30 1,13 * * *", - Type = "SmartStore.Services.Media.TransientMediaClearTask, SmartStore.Services", - Enabled = true, - StopOnError = false, - } - ); - - context.SaveChanges(); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507102159496_TransientMedia.resx b/src/Libraries/SmartStore.Data/Migrations/201507102159496_TransientMedia.resx deleted file mode 100644 index 11d76add9f..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507102159496_TransientMedia.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201507132241575_QueuedEmailAttachments.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201507132241575_QueuedEmailAttachments.Designer.cs deleted file mode 100644 index c4e3a3c11d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507132241575_QueuedEmailAttachments.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class QueuedEmailAttachments : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(QueuedEmailAttachments)); - - string IMigrationMetadata.Id - { - get { return "201507132241575_QueuedEmailAttachments"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507132241575_QueuedEmailAttachments.cs b/src/Libraries/SmartStore.Data/Migrations/201507132241575_QueuedEmailAttachments.cs deleted file mode 100644 index e174e25c2c..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507132241575_QueuedEmailAttachments.cs +++ /dev/null @@ -1,107 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Data.Entity.Migrations; - using SmartStore.Core.Domain.Tasks; - using SmartStore.Data.Setup; - - public partial class QueuedEmailAttachments : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - CreateTable( - "dbo.QueuedEmailAttachment", - c => new - { - Id = c.Int(nullable: false, identity: true), - QueuedEmailId = c.Int(nullable: false), - StorageLocation = c.Int(nullable: false), - Path = c.String(maxLength: 1000), - FileId = c.Int(), - Data = c.Binary(), - Name = c.String(nullable: false, maxLength: 200), - MimeType = c.String(nullable: false, maxLength: 200), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.Download", t => t.FileId, cascadeDelete: true) - .ForeignKey("dbo.QueuedEmail", t => t.QueuedEmailId, cascadeDelete: true) - .Index(t => t.QueuedEmailId) - .Index(t => t.FileId); - - } - - public override void Down() - { - DropForeignKey("dbo.QueuedEmailAttachment", "QueuedEmailId", "dbo.QueuedEmail"); - DropForeignKey("dbo.QueuedEmailAttachment", "FileId", "dbo.Download"); - DropIndex("dbo.QueuedEmailAttachment", new[] { "FileId" }); - DropIndex("dbo.QueuedEmailAttachment", new[] { "QueuedEmailId" }); - DropTable("dbo.QueuedEmailAttachment"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - context.Set().AddOrUpdate(x => x.Type, - new ScheduleTask - { - Name = "Clear email queue", - CronExpression = "0 2 * * *", - Type = "SmartStore.Services.Messages.QueuedMessagesClearTask, SmartStore.Services", - Enabled = true, - StopOnError = false, - } - ); - - context.SaveChanges(); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.AttachOrderPdfToOrderPlacedEmail", - "Attach order PDF to 'Order Placed' email", - "Bei Bestelleingang PDF mitsenden", - "Dynamically creates and attaches the order PDF to the 'Order Placed' customer notification email.", - "Erstellt bei Bestelleingang das Auftrags-PDF-Dokument und hngt es der Kunden-Benachrichtigungs-Email an"); - - builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.AttachOrderPdfToOrderCompletedEmail", - "Attach order PDF to 'Order Completed' email", - "Bei Abschluss einer Bestellung PDF mitsenden", - "Dynamically creates and attaches the order PDF to the 'Order Completed' customer notification email.", - "Erstellt bei Abschluss einer Bestellung das Auftrags-PDF-Dokument und hngt es der Kunden-Benachrichtigungs-Email an"); - - builder.AddOrUpdate("Admin.System.QueuedEmails.Fields.Attachments", - "Attachments", - "Anhnge"); - - builder.AddOrUpdate("Admin.System.QueuedEmails.CouldNotDownloadAttachment", - "Could not download e-mail attachment: no data.", - "E-Mail Anhang konnte nicht herunterladen: Daten nicht verfgbar."); - - builder.AddOrUpdate("Admin.System.QueuedEmails.ErrorCreatingAttachment", - "An error occured while creating e-mail attachment", - "Whrend der Erstellung des E-Mail-Anhangs ist ein Fehler aufgetreten"); - - builder.AddOrUpdate("Admin.System.QueuedEmails.ErrorEmptyAttachmentResult", - "The e-mail attachment data could not be downloaded from path '{0}'", - "Daten fr den E-Mail Anhang konnten nicht heruntergeladen werden. Pfad: {0}"); - - builder.AddOrUpdate("Admin.System.QueuedEmails.ErrorNoPdfAttachment", - "The content type of the e-mail attachment must be 'application/pdf'", - "Der Inhaltstyp des E-Mail Anhangs muss 'application/pdf' sein"); - - builder.AddOrUpdate("Admin.System.QueuedEmails.List.AttachmentsCount", - "Number of attachments", - "Anzahl Anhnge"); - - builder.AddOrUpdate("Order.PdfInvoiceFileName", - "order-{0}.pdf", - "bestellung-{0}.pdf"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507132241575_QueuedEmailAttachments.resx b/src/Libraries/SmartStore.Data/Migrations/201507132241575_QueuedEmailAttachments.resx deleted file mode 100644 index 4bcdc56bda..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507132241575_QueuedEmailAttachments.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201507141647299_CustomerTablePerf.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201507141647299_CustomerTablePerf.Designer.cs deleted file mode 100644 index c12151b6d9..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507141647299_CustomerTablePerf.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.0-30225")] - public sealed partial class CustomerTablePerf : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(CustomerTablePerf)); - - string IMigrationMetadata.Id - { - get { return "201507141647299_CustomerTablePerf"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507141647299_CustomerTablePerf.cs b/src/Libraries/SmartStore.Data/Migrations/201507141647299_CustomerTablePerf.cs deleted file mode 100644 index 982225727d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507141647299_CustomerTablePerf.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - 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"); - - AlterColumn("dbo.Customer", "Username", c => c.String(maxLength: 500)); - AlterColumn("dbo.Customer", "Email", c => c.String(maxLength: 500)); - AlterColumn("dbo.Customer", "Password", c => c.String(maxLength: 500)); - AlterColumn("dbo.Customer", "PasswordSalt", c => c.String(maxLength: 500)); - AlterColumn("dbo.Customer", "LastIpAddress", c => c.String(maxLength: 100)); - - // recreate previously dropped indexes - CreateIndex("dbo.Customer", "Email", name: "IX_Customer_Email"); - CreateIndex("dbo.Customer", "Username", name: "IX_Customer_Username"); - } - - public override void Down() - { - //// INFO: (mc) Unnecessary - //AlterColumn("dbo.Customer", "LastIpAddress", c => c.String()); - //AlterColumn("dbo.Customer", "PasswordSalt", c => c.String()); - //AlterColumn("dbo.Customer", "Password", c => c.String()); - //AlterColumn("dbo.Customer", "Email", c => c.String(maxLength: 1000)); - //AlterColumn("dbo.Customer", "Username", c => c.String(maxLength: 1000)); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507141647299_CustomerTablePerf.resx b/src/Libraries/SmartStore.Data/Migrations/201507141647299_CustomerTablePerf.resx deleted file mode 100644 index 5e18dcfce7..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507141647299_CustomerTablePerf.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201507200832223_SortFilterHomepageProducts.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201507200832223_SortFilterHomepageProducts.Designer.cs deleted file mode 100644 index 2f16779b2a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507200832223_SortFilterHomepageProducts.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class SortFilterHomepageProducts : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(SortFilterHomepageProducts)); - - string IMigrationMetadata.Id - { - get { return "201507200832223_SortFilterHomepageProducts"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507200832223_SortFilterHomepageProducts.cs b/src/Libraries/SmartStore.Data/Migrations/201507200832223_SortFilterHomepageProducts.cs deleted file mode 100644 index c1a448572d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507200832223_SortFilterHomepageProducts.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using System.Web.Hosting; - using SmartStore.Core.Data; - using SmartStore.Data.Setup; - - public partial class SortFilterHomepageProducts : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Product", "HomePageDisplayOrder", c => c.Int(nullable: false)); - } - - public override void Down() - { - DropColumn("dbo.Product", "HomePageDisplayOrder"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Common.Unspecified", - "Unspecified", - "Nicht spezifiziert"); - - builder.AddOrUpdate("Admin.Catalog.Products.List.SearchIsPublished", - "Published", - "Verffentlicht", - "Filters for published or unpublished products.", - "Filtert nach verffentlichten bzw. unverffentlichten Produkten."); - - builder.AddOrUpdate("Admin.Catalog.Products.List.SearchHomePageProducts", - "Showed on home page", - "Auf Homepage angezeigt", - "Filters for products displayed or not displayed on homepage.", - "Filtert nach Produkten, die auf der Homepage angezeigt oder nicht angezeigt werden."); - - builder.AddOrUpdate("Admin.Catalog.Products.Fields.HomePageDisplayOrder", - "Homepage display order", - "Homepage Reihenfolge", - "Specifies the display order for products displayed on homepage. 1 represents the first element in the list.", - "Legt die Anzeige-Reihenfolge der Produkte auf der Homepage fest (1 steht bspw. fr das erste Element in der Liste)."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507200832223_SortFilterHomepageProducts.resx b/src/Libraries/SmartStore.Data/Migrations/201507200832223_SortFilterHomepageProducts.resx deleted file mode 100644 index cff0d41d5c..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507200832223_SortFilterHomepageProducts.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - H4sIAAAAAAAEAOy92XIcObIo+D5m8w8yPd07dq5UUneZ9W2rmmskRUq0o4VNUtI554UGRoLMaEZGZMXCpcfmy+ZhPml+YQDEktj3iMjMyhcpGXA4AIfD4e5wOP6//+f//e1/Pa+yV4+wrNIi//31uze/vH4F86RYpPn976+b+u5//O31//o///f/7bfTxer51Y8e7i8YDtXMq99fL+t6/fe3b6tkCVegerNKk7Koirv6TVKs3oJF8fb9L7/8z7fv3r2FCMVrhOvVq98um7xOV5D8gf48KfIErusGZF+KBcyq7jsquSJYX30FK1itQQJ/f321AmV9VRclfPMB1OD1q6MsBagbVzC7e/0K5HlRgxp18u/fK3hVl0V+f7VGH0B2/bKGCO4OZBXsOv/3DbjtOH55j8fxdlOxR5U0VV2sHBG++0tHmLd8dS/yvh4Ih0h3ikhcv+BRE/L9/vofDSCfvudp/foV3+DfT7ISAzMUPiFkRmBp/uZDWsIEfX15QyP6t1cc+L8NXPL+zfs3v7z55d9enTRZ3ZTw9xw2dQmyf3t10dxmafLv8OW6eID573mTZXTXUedRGfMBfbooizUs65dLeNcN6Hzx+tVbtt5bvuJQjarTDvM8r//y/vWrr6hxcJvBgTMokpBRfYQ5LEENFxegrmGZYxyQjF9onWsL/9u3hlgRLanXr76A588wv6+Xv7/+Fa2hs/QZLvoPXQcQWdECRHXqsoGSDuob/QCrpEzXLQ+Ftm1oKq3WGXj5XCQgizBQq8a+lQtYmqZQj+m8+gDvAOLJHs1xUWQQ5AZEv73drCjDOoMNXJyiRZMd1TVIlivELh4L7gusKnAPqzdShIeFp26LIpi5WT0q3BM0CZjHmSWFdps33IwIoI5NoWEuNavo3S+/xFhHZ2kGBaIYlh7ZZ7u1kuagfGE6hn7K+xUkHd9bjtaRyF+Q2oHbnKDlr+AxvSesIJmC168uYUYKq2W67hhKutBvWvCzslhdFplKvhCom6uiKRM8tsIIeg3Ke1jbd5pC5dB3ppZpCNRXy5HQNWQDspbZH4qnPCvAwktML1LwpkdwEMsaOdLR6GOTDq22vx0XMVLte1zfy8xtG1d3i0IlkQpKKeemxXRt9WI0tlBFllSNJsMg4eKMBYuRXC/E4zR0+ozGVOkV2jgtnVdf4dP4amEkZfAga9RtXZRpURK4EO0P71haw2YUHQW3arQdY+iC18X0g7suJhnaJVxnL1HGZ9XOJGM6ORm9ieMkGb2Nq+b2nzCpxzd1jovFy+hbxkkJsYD6ln+vB9Ihgwlepyv3xXGF9+4yhVWg0YrQKDpk2GmJfp0kRZPXwZYzzBdfQN6ALHtxVtGUZshG+a+mMEN6o8LeDOkNF9sB0SQ3jeiGBZYOhIHRmVEsYJD1xHbLX7eh8RyUG8My1Xkwfv11FNdy64I1OW4sG9e39amodDvESCO8KMo6TOoh07Q0GEVj9R1U1VNRLqZv+TTHwFdVsCWO7frWNY92VbIWUK2RzLEL8IJl9xdYLwsft09Xv3rDIDpILR2DUoS6eqlqaLJy/mqrB7oy7HOSNQu4OCGTDku8N54vqtE136FdvMGVL/om38dRgvs2r5BGsUZttOSfYrTdKEkH8AogvqnnYL3yS5qnq2ZFDgKPVq3K0Wm5MElXAEmhixL96iIa/vb61RU+n0TcZMQMnkfC3GK7hBWidVL70sJetpXFoknq4yZfIMZG6wzpqmV629TwLM1qfH7qLO5OQA2y4v6NCfVBAKrb2hAtdA0MRI+G6AfImmBs5xVaH8hoQdY9XMSz+TZ0kxpIJpa8oetvbCaHaoIZ5VI3yLISGoq5dA9rVd1WR6zQFdESOxKyPu4qDAuy4pKYe1uP7wKiJZLXyJAPNQPMWmEML+QS2Xp2wVLRjssQCw6CwtGukVjI6QJeL5vVbU45A3yR/UATf5sFzxwRDtXSQ/iPFu0V1T/7fb0IQKZzatK7h9yzOcE2x3s7fbZIt029a8FuwDdcHc0gWVDz/s3BuwbHuA3DagD2Xdd12lXRCFcvDkqFRjy2JMLNheoCF6BEbX4si2aNOhJHv+g2gfN8kT6mC7+Dm3G2ctejp8n3dtS/Kds7WqzS/KRYtZHFIzfWcy1coS053OREk/P0Lf9UrOBFBF2xxxNPYfgCa4CEBnal63xkcbRQ3NiUjIPbu05rbbh+nJEdZVnxNDhY4WMKn4JV3qP1uiwe4eISSeP8/qpZhc3016KOjLFHd402jIwbdHAf4yHtIjCui6Mk2HL4nK7SGneO7I3BU3z10Iy/6EDe3IEEqyAl2kfrr8iI2kiN0Zr9iFhs9EbOq4/pXX0CymATrMcTQ1u5hH80aQm/1UtE8XY7CeaUDudG+dFJ60gRRUcN1o3rNMG60dFiwfUheEzn1SYQPU5Qc+jMfc+zdoH3CIPHiGalXXDf7gScnh6tDs3p8zotiaL+AbzwOO1QHCV1+khQxGD7T6C6AkhrgrFmlcXmeI0H9QZHJxzdlxDSimPAaf0G2TV8Hl8TPa8uYdKULfZQ6dEhOnlJMth2KlTG0RgvYJkWwatvwEn2foI4cK2cV/gguI3YCGbH8+oMTX9/shys3S0WKV55IOsxniyxS8PeUe3c/WvwfPqM7JrgtXBenVbB5ES9OUEG1n2BQwMCjRiQI6PoPH9ESxOh6w/9g+9CJg/RDiGwuUYwHj2CFNVNMwproN9Y2lPvDSvNI478c/HUjhrvNAhb6KQgAyG9eyEOgbOi7Pt4DJH1FXh0BZKHApvTOCdD8DkztgYxxvOWlsgCGYzeYI2CGP1dWEicSWoxtuEgcTASAsBFh4yK8B5PWUVrAXfwuHk5bup641wIWFsY+mdaLbO0quMg7RZ/BhHzIrnO+G98cZ4g7ZygS5PwEy0aSfQd6Fu2GLeBzjA5oaJpo7dxtUZ4QOY4EHucVzWy0ftTNo9LDTSu03wRgKl3Z53m+JQuCn91YovBDEdmik6wTdomMj6uU9iuyWCBj/ZQWNXDYeRJsbpN8+5cLiIToj73wQ3V0XqdpeEa80+Y3i/HW4qsHRMd/c90MSL2T+PSZthpQuXJgChMmMQ7sYgXdQEz6BO5t70hF+IA00dYvuDKjt4TOumVY9VjUMFu94lidg/4vkBQNSVsE3pporlj3KXs24wbGD2gxT9Y1HakJWEQ5ETL4IGK417vwkRgiSMvYjk+GKSYGrFxXi2L1p8CSndPhzpqqd0Shy1SGv3Sl9500NfFxle+CX9RQwkxSRpQ1xAkWhroQnduWEAhaocpVwXssECeAUabqB/LGDH/kCljLJgixMpxRJ3DixjE6gH1bjFN6BQPohqAAOfZcfoQUdt3GlDTfxmYagxSWM9xXKQEj3YIHYym9xyEquM8mGefh0NwdZdbEE2PWQBVhzkoz/4SC/QubfPC0RHA6v7Lq2jGo6+gGp+hlud4r8G9dnA3DJwoT6lipTilYTyl6Q9Qpkipk9mR2u5r6mkmyKKWapZsqnpOFY/aaeAOo3Udove42Cy3Gh5kAUUmpMuVXMgAubIhfzas6SwPKnaXhVB2mANz7TLtw5F0dyiWcYZQKPCCCCGbffukfsNNF49kx51SO/w6xDWr2zJE+Y6UyrOfmRiRGt8rbCtFur+kvhEV3anVN+VqlTuf/HYuM5+cQq2XzKNmF11VnRTNGl+ZXgTPjIhJfvwW5/SNzAyJjySrLJRHN5gw8VzjQExm/HVhMMVEw5uuoLPlN3DCtqQFdt2cJC6GMTwS5jEIzgtrj0SHquN8dZqroU15Dck4ZIDqoUihgy4Y9XZ3wA2jHsVhK1a3ZdiKR7pwg++/RGrZ5MCf7q7EcVEjPp30Wg9+ymT0nalfRrEu9BwuzEQZWXujL1YAYOdpczypwjeprtJ/wbC2mas/1XXRZgThcXtH5HR4vrHBYwF2h0UE0CXI77UxW9GSEsS8Gxc/fmGLbw5t2yn4XqYwEEhFsvX9SOHTF71R5SmNYx8+epgs5gNIiX1j7YVtxbS095uTqh5o02O+TNDkBYCw7AB9Dzxfx+jqHxR3jfhqSTTWwxAW7978dZy7+LCweCjiL5EuPzo/3xD7hFRcqnII4wkpt679li2DMzy3x2EZT5U3TKW+T6eDeG1Z3qtBcVylWjSOKzhubIOprzHS8Uj8b1FOsSh8h1WsXTdR3kCQkP2yyaBV8uM4/ourNRz9dOg4zTIcXdhnNQ68Zdhf/oyDrk+6SxR1Jt+zk2dkg2aQ8Dp3QJzJoxqlc2dP2XA/G2ZvS+yWO7+A4zydPiNBRT9YOYEtuokzGO+kRmmFas91/PYeJto14J1oGtFht1G3ZdgIxnkn+qTIivITfCaJnydvfSeejna1syIccXKYDqtG3VYkSyvWgct5dQYBnoheQZjRf6zcqTYcahFrvwFWB9sPPxRGkQg4ggnnczvA1N8oVtyFGK4fLhtobAf5MLp8oMn9p5ARLLda3mthK+kvtzB/KNahusII8iPkho5N/6PIkkhC5CA9tjmOatLIFooTDqFA2xUKdAje2cHgnUM8y+7Fs3idcrGbu3jGJSsXFAUpUAxro72yGm5ntHii6QivcIaHrn0S3dKJGPISXl6/nkKXN0nvWC5sTLhJEqK2acvDiPIJZuu7JvtPWJEko1GQfS18cIXo8K73vRV6u+JWuGNHWywdLXJYVWitmlIcCHVuuPWsGJGyhv5Ku7pa0NlBv6p9xE+vcwy/DvaJuq2eRh+bdGi1/e2RxdkQnBUpH6Xp1fJIzVi8gx3pXZSuobOiXIHwiJ8O2xXIxt83Jn0/JmKa5aO7uzRLIxiqJPtusDUTSbM+r9qAkKOEeZjRO0bfJrokzhPPn0FVn6+PFgtk74wfGxDVbsBd/1zcp7nn5WRcv8/hrEThfym3JalCZeiF/w0FRsWGC6VidLgI4qrqdEE/w9zrusnDSvrKgqg7zMG59pq3OnTd7mBuNjqN2G8eRlC6lICuNwXo2CUDuTlQCbUZCDWxWTBXWp8+Y5UGZEdNvcRKTZtk6BImrXdQMgJdDek0WFUQ5sSulusEITWgWV0UleIuylAsHYhYKvRaAuLVxetinSaaPpJydSfZYnkvORjXbhIXk7yHpEjaObZE6BdX7NqlS4isjBzHWEHVBDMg0i7KIYSuKsDcu/wEysVFkeZ19SmtatWxvwRO0Xs1nGQMGmDXkfQxiFZ7jQAsEX8cjFoA8oDOGZ6oxJvqrI08lJT8SiCB9mrIINN+IKi7ZY8MjSJ/0yE4WPXqts7SsqoniQvH6uskDZms/kimQbFag/xlgnbkQekGiwxxB06U+pjmieup3gn19MiI/gCyNN9N1dD70Rv6r3SNdSWQGZI9RXIFLYscGp+RjPTWcP9+3pbb22pLpl1D0q2w32gHmM0GyBUJmzZf7rxX04tU2zkOUuwiA6DsKAsVllypJ1dAtH6H47A9q9sybJi26Xl8IjKqzvER7EElyGJlrr9+Kj5DTMTzqjAI2vcRBNL1soTQtsG/RGgQCVpYpgnXWOCjyz9AsIs3XmxItKCO0OgXjR3L30UbbsFJbSka5mZTuRVvXOIPM7TEujJWcbZv6Y1AMSxmr5DsjlIAsfNSqCCzkKWHxw7UI+Cm7rAPacTS3genxpNL5/c5onX7kmxwTN7pc5I1C7TWmYvEupOvOOds3UolzeMlQE4Tnt3PnC2ELCUnpxCvvHLsIJH9xBWruwfoywymg7QyMm/wQ7fzKN+3tyV8TIFB6sXJzLkNap2X2e6pngiLX6vEBAWkDQe+EeLSOlyHNa9Z8x2pgm/MWQSXxLkpcI5TZCLW26vnD43RBPEDIFRnacpICa9lfZwV90P8mvOSxrWrNxSO7Yhy7zozSeQ4Hjx2kMdTIXuMUpaiaH2zAdywk6xcYCUpUDAbtV0J4CGM4LAXqNv6DPL7BtwHx2zGur3hmt2qWMQ4gPO6htcye7DZ2u9sHb4TyTOrrsp4UcdH2j4HN7Lgm+cpm8PF2G28YjnSASe1T8m9KF67Ie/L1W6Ztl3tZbOymwTrBortI1Mo3a5ZiKC9etMJ572apJlK/0WA3/R4Dlu2uq15fC39zHRU15p8Y7SPav/RwCs4xVHmWQbuz1dosDg5tvGuRoyrovX+PtgwgrerzUzXTsYlrIhokwtzGaRUYmoBBfGuhw46spOhDpaqEpwHCWuWdRHS2xJaG0TISE9w9q2bkkiOZCSdV3h1XWTNfZoLQsR4KbFoEpnwiaI6RRIKvEplJ0K8hMKF4nZ4rEQSFMrt8LYxvQtdhj9B1Q0wXuDLBXv/f8rUAdrsCOaMA14M+BU+Vf4uXVz7DYViO3is9+hOkndkSvcxpjS+WxLPfdxjlDI6NbE3G8ANW8vKBSaWAgWzbNsVf37F9Q+KkrqtHfceXy2LMsZydGz2jIiuqVuNZtHN46T9M/rct8qjenCaRxmZn8kybIkyM0UolO6uLITrBRxqf5Z7Wry0AN6volUV/CynIst+FDUxyRKSeMhZG8Aoqjc8ou3QYXGvjvLqySOYR23aDDjldg1HiBsanjJm1GCiBaOBDbObqa4FzHuL4qAI6vlw5BjSkdSi9rrmtzvEf5sddose9CuwiFAsw26JtDDs0qOKpMuNLndOpMgtV0XqxHA5ISRJtJAp3nIiSEIcZIO6rVhG4jzyoU3Y1mm6499Vj2akRX2snhhfH0l6nesCS8rtudo4jzU62oVKrRVApLXMAmAKpPLeX/PfyFi1nLfciGTyXLZRBV0mwKij3CTAiA5yXd2W6VTz119HcZ6VEEa7IR8r9SlC1Ek0jIebAoNojZLstM9S2jJ/4NZhk6LUdnatpQwsV2lVGXIQ8lDqbIp6SFES6cHDlEsOt881bJg0JcIqEOAgn7zl06j64jyycfMo4OgtB6RCjbaIBT3Hbs17LWJdWtIgbUOH+LC6Nbwe6RLjNCnw+lluR3eXTpAgrG+yU0wmbPkb5mXCfdM0dZQksKqmafACX4BGRquVmI/9lrn2YuhI+Zl5IeuW1dlL1g7JlD0EK6lbbRI6H0Soui2SCTr4ieZIcjhSZJSr3XRhvsIeJ33Ezl88d86RzksOTRp12w5uEpgbukgDyjpJZUJXd5MCCpdnXWeCBBrBcZBo6rYIlbZGouHZwghCEXXJCXWxKJa3kNzPRrs3E0K6/yOFT4EocEJsr4QICjyxprjH18pdtyOE/dgQ3B6kkMpaxaMVTnuCqYMdkLR35Keuay1A+BYQKP0Pgl/dFiHQx7Jo1iOfMo90V2vK4Mavm2dmgggVZXPAEjSKHbKPW8Sf61XnzRpWS/MbGoiT5lSZXJrTAOHSvOtEkEgnOA5yXSNk9l4a7+Uanypapl1L4eJC+lCaTJ54iYtubp0lRfvUWvvfQUio2yIEmujZFdJW8LvOZMDb4qdgX+0Mdlawr7I5PqhEyIszzTbROhIH2wV4wbdSoiJr02iPeZKk4JiTpixhnrxM8vxS39gl2ISNfoBJugLZ61cXJfpFpNjrd397/eoKZ1D4/fXfvNcCG4gVOk8/QD2lWLlqbmskErPzPMmuMVorYv3VeVhMYzht/RSNXePG0NyQV7SnGiHT6DQj7aTONCPsGpt0ZKghh6Xs3hgjHNEukmIVAWRnEI5NU3XLYxNY3fLY1O7wj5/ak/DQ6Ezar/VxWyFiZbQmkOLd5Au4OFqNOhL6FeCfsISI7SKkV1/C5KFo6qMa8dFtU8MpTWmh8eo/VhO8btqpHrGuOR3d3aVZitZkNPtgPToNyP0gbNTgl41LiOTXCeItVnfyZimEiY6qH28icZcnUcMF2oyXyABUD3ChmpJRR3jy+Dj+S7K4odPndVoSR8KXIq+XE7f5nxCMT096fbWPHH2At2nwtRUK1VFC9s1PRbaYgD/EhidiTKrhY5A/TGIAc21OImLoNs9PpmxueF9ouibPb8EEykW3mxIFcEiFN/a6b5BBUHZpXK9LkFcgwT83qsHkTU+yZFSNX8KqycYn+wlYY0f6tAQXG51otFfN7aCjTzvki6ZMlqCCUzrwL0Dqe+mbf0117HnpmsP2ORI466Y9LZnca/wBZrDeq5e3NJbwJcRHb9TdYqtji0+g+gqfOi/O1wLfs0mYVwnDjzrZUxrpkSdZRjc84ObYU1YuREpIgVxj8rRxg20LspBBtkTRNe9Awd439L1CVvqntKoLxYOJMsCb7hB301k1lHCcrAF1fRPkY3pHDDnjIGSAN98ruPiZ1kthMGZoYVAWVVwHR2rhnG8a/iYp4YT+c0VCZ/lyr559bVNEqXqGixU92xTJe0aVu/bsEi4gXMEFLcROlW+B0lBGpjACC4Mx13B+fh31XZ10sC8Vyc6WSB+Kp4p9erW2ksYCJC/tOACF0OOhgsLX5ILQ47XlFk0lFZiHgBV1W8MxaKCHt5VqoW7icd7Z2pzE2O6wmxqGTbb/KKwVPbSrvtDJlDEUBKuOs5LLa6XLtQXfQDQZtsMyV7fV0yt0hTI7aAxkzIsw0Q8WRxIoPTXttd1NDYOi238U1qUe2lWgcIrWFEq71YDkOl2QwAkXMgfBom6r95tRUkH23IeddIoRKDdyqMJ51XeWZEsDEbxRPcKTollP5NS+RMRYp9hBNIXXbmhtmgQ3VzDHduwUI2ubmmZYX5CxReVPHa2d82rDHcR9OLe/dSQHlPuWbOV2EjZw29HIJal2PDeqOuKIFKDKXVkFH7QlUz0MuoFyeAvJxhhHRAq+HRJFz79oH38LRfOPBnSjDzI68rS+KNNk9BDeoaHRI3anGM0kIxk7wrj3bbQ64tgkY1sbm3azhLlOG976oXjKswIsIjxndV71yKKp8Z/RAskr2ON1NIGwtP4J0/ulg+liuEvX5IsMIjULjB/I0Ar4E5InLxZ/KxWlo6oqEhyVvOiVFfmpR0QtSaX5mbSqcP+uw0Gj9LhGchBprZC2s2ro1gAl61hXqOlaDxGuV34lb0EE6ZUYxUGvVLcVRRv8Sr3ZMV12xf4qZrEJ9thKm9YgCewO9qXLTXLw77XYJAfrQVmnJfgOa1DDd5Gu2rcEj4HjGGQgT2CgWVaNfstsKgfaSJJBG8gmi3aRhbXp4ASxoQWOf+IUOSDIZjQRj5v6MB6fJzW6iJohhugg/9RtRdFBrkuQPCCKTxToTS7pxrXuCM9A3/DxDzBLH2H54ll9ct3HOriOX/SK2DvXkD91ICgNcbORAWIHGQBlECALFZTqiUYZQygdnO7mFUnoFCWSTnaIPo2/3LgYYq4D1XqVrxZPewWxY34J/8BvRvo7CBg0h3WgWQcxEopFWwSxzKU4h0+XEFRFflaULTdN7wbp+BcSt3eUA4KwDugvT9lNbXcDdvy8MVc1uLv7WkyRouZosUpz89Xev/4S5UEQRrbFySu3RVfsfC1qiiYKW1oGIbE7pWCBxmZBlLMTtAmFxVPwmA47m279R9jZLkDZ6TeOJ4TteZ5HRXqKY0RYRvNAxgkLmSfFElqSsEQ8hMMiRvNYxlE6dkEUb5hbYV2wUuqGhqdtDCWYxNJQw0a9Yyw0JNtLlEDmfnt7ZHXnu0IrkmNeFYy5y1EOfTskV2u01vo77oMs8DmRAjXIivs3WryH7XF0eS4n/Lc1mwjEc6/AyavO0gwNLMKb7Yivn77l3bAvqKMl/yRY3gn4vVa5ltVlS96qgvgYslUtZ9+shk88RqtHZ0sCHRZHumhRhVkP2qF6C04d2oPcdBV4Iz8eNW6YzRgSTE4mxbvucy91/nwngtQIk4tyLU/brgIPpQC6Vxd1Qw8cIwi/6GLvIPD+DKJJx72K8+Lpl5xw1hywbEMstWtwH26WISSHpeW9tP4a6ZEy016t3ZVvNlNZSbZbuli1jzIwMXjzByhTkG/StZ8Uq9s0B4EasRn5gZfVbV09NBFY2XD3vk51h6BxGvkC8uYOJHgSyws028aoszjNOvqmDbebSEdiYfuZLuIh+xT32hSoIKGcazSyJVr8g0VtdYhzVFXpfY5WUUr46HyhO4N+F+dYuI8SxL59xzOn/uQCX4J1vQlXkSt5wc60nTxeQiIzeYhz7EN8nd+a+tsdQdqe9zoTVZ1UjWIO3W6v2fxuWByCJmBbVeVYs64f8+TEom21Y9Wils9gfV2s9Cr2HSyLw2nEdFWfYTP1Y5w18Y3FVw0P+uDoAr1DE83Reg2fa/RptR4/PT5OzfNHk5bhl9Wp9ZLXZZHFiAuZ6fSKX0IO4tVVpnoLUp7nfMYj9QfZ1nEdod7ZajFUHjHJ86h1C0hr3Cglr5EG2voqx4Ibkhieh4ibyWEXsdhFjrIUGGynCJJ6Jhf3aKlfQjWlLs1rbHWJoD1wu7otBd3C42h2aBEZoiOLrCiv/mhACavL+9vRB9X6lhb/RCuBDu6PHjvaXjedoKHzCmFCGzlMIiRRCtAhOUxENMRQaz+n+QMO9J0zh52rtrU9epal3mmnrHntSNcpLDtXuPf2M+A47DU6yRYn/jPGLYs+KBoznafrOtTLEPVigDH0HA9UuuoH1r1hQTfrWg4hrFwFWEzf6aYJiQ0vFGp6qLPT7SOFMCP63JzHX6r2v4O8ULc1UawC1+r3UneKM1KjV1V2mmP4YAXpCiaIV+IMwnCsWlTaO7WRDhk/F/fFcKg5mwOSH3u9yo6LxcsUb60WeY3WS38e9RXWT0X5MEVwABJML2QJnzRlCfPkJdyzTnCePidLkN9D8lioL2rN/qFsRB5wh0d4o61FhdeZgMX4VWMN9w1SnBnzyDhwxZAYKP1YWNCgjXTTLY+H3fDT2kX58qZHcthR1W0ZdtRfR9nb+okxPFzy6yjZJOyzhf/Ndzv5XGAE4WS1MVjOinKFJp26mzZWe5/TVYpY7LpotdXgYBu8wKrlNjmAtujaMz9GIttO8wWa2egalpt58wWQm7mBVk6H5SCa1W215A/VsFos8xhOHn4Zex/dEq4gcfvdZj4mN6lfvWHQHLhR3RYh1Og2RiQ+tfDza1p5H8dMHZP7j0HycJ6TKM2r5pY6wvX2VyswHpZE1AkezQUeK8POHJmoFawnzXpighUsVGOFmN5oVWMS37QB1HocUfzW9HWTa7hC6nRQcJEM3UGQqNuaRzv7kcIn1NXl9C37Wmz27iM0DfdF+RKBl3lUBz4+8PFkfNwJ9whszGE6cPGBiyfjYjIVaRIUtlysVkX+hsd0YGN1W3HcR2hEH8uiWU/PlqjlGVahwTUwUhT2mK6CoyS7hEmBX61299ji0AGE7s2A5LDixl5xczpstTFwzkG+54vNIVj47WFj2NrAo8qwNTmEYGorwIIs7O9lyCos3gz1DwtwnxfgVdboznFHajVW/oTPIL9vwP1YO9lVsoSLJoPXoHrwOfFB1ao3NJLDWlK3FekmjXusYpEvAt+YxHWn73qsaM26WH/LT8uyCH5v9jMgT3OUtcdLKbjuab7wrHnVJAmsKt926cFPHuRwdHeXZqmft2WoW21+HoSMuq2jxaJEfBIcxQszGOEmmecmqH51vh2dXFnt2eNmgKL0VL5QVFEFiLDzHwgqxH/9e5MBMZcMpgPrq9sy7K+2N0ddd5eXqoYrRIcnYopM3fwlJt3Y0ZfjOTI75v6A9rG88gu3EFbKgOywWA6LZZ8Wy1lRNqvA4CSCo3ojoDqsFQ3XUnT62KRDy+3vmcKKyAwGp80q1mmyiwFOAv9KQ5vUUIL6pwENTFqXPiJafEEKJXmbxnfBsngOq1WzWmPEEGIeirVSr4tYmBB//hMm2rdIx7lehNPrTe//wZn2QLANel51xuzxy1FTL8OdQBTCS6Q5rFMqsYsvzpFkKM3GinQktFi5YeHprCNKMElyETWsa2ToZunY9J6GVvZ9A2TqOQUZtAUQJwj69LnwueKDat2j9faGwnKQ/uq2KDLhJrcmCNz4ivRIMnQkwcKRWXF6PMDcCPD0EbISTHKOrIaN+l4o3ZBMqMjKtb2NLkpaMsYQJxj+IFLUbc3stDB4at6P06rX2ZtDNPlqDdJ7HwdGtztXb3ocB9b15p2xjpyNZspIDeNsMbu2xfKntSEpARzc7mQRBUTBD8uQQ3VYjd6rcSRJfpwkp2jKsu5YE+qSHdh2IXT9R0pbFWm9TxPK1U5CkuBXh8LToUZNHHIF8wW+YQiy7GUkifMVPlWfIV4wgQcYg+CRYzzIH438kVIs+ESDMLZOqPw6StqhOKsy6uY95qWDkyVMHoom6KmC9vmpNwKqw6LRLJpZopZ38j2bq2W6XmP47mpkLLzn1TV4Pn2GFDV8USFE/fXjaG/SbcfrPbG0FrW/jJcamtdk5LA3Ehm2canZ1hFejbGuGPRSjLyViEL48IyGaavmKRa62uYR7Ye3J0aSgPaSa3qZxR8NOAs7L5l1iS+bYr6+AC+YBT6lFQ6U9BdaCoQHqaVuiydZqNAiM7GlIVv8WKXrTMFDN2LlzTqzrSOsM+uKUddZvAV2WFkaJn5JMsg+ie65GjCiC1imxSI81qsGGcEXeOOO3DLDSzKGXyKWCzPSraDzPK1TkG2zJKO7aCXFbtgaatHFABrlFQvtGuKg3P+nFsu8yeYsz73EMkkanv6LvBtGyOIVL0FwEOA3AsKDeFa3FSd1gN8FeAkekkB+vtw7Q/tzNT1VHh6lMOpnUip9hJV1swHfyBs1lCBINaCBql5GVkOfftQ7fRyL6CBI1G1d9Pl834WJgAHP+3mctPauxrKoKrQssnAu41Ed+Gxb+cxBqwkJIz+Ejxv2yvvP8BFmwTd1lkVZD5e+po5IO0Pg8Ro3GHPrId3CyGFFylh8w3JEVNA/1ReHEIjJYVnCcoq2JnxWx3RPDv7RtM+KhV2PIO/vfQKVPlurD+n8LgCoAv+1Af/xAv2vYPcAlUde1/wuvW/K1mLt8BxEvrqteaIy9zAtKhVb4ZVL7vkNheHAsd4cu6tJrUkyAh/OwfWq9r8D22jWPrnHFOlKiMnDn2TNAp7nVylqEgz+Nv8IsAtQVfj+FSqoowQa9AhHp8V1WmtfrozTyiSh8F9gDbp7cDp1P15jHyAVKj5Be9PMVeTo/UuYI5F8VP1MF+0hSxC2Fst/Ffn4dGib+lmCdaf/C323qY9M7Sdm3sJGflzgDS6CfCnToiRSOeg8GQ/sGugyGPvMhXpPblbUjtxeWalrkCzxaRxmV2RMY9d6uyLPq7MM3FdDi/6XOfQtRdvVEU3Q5GYviIb0rLBT8AWubmHZy9WsuH39imjrv7/+RZguBvYszSDxAiCDGA6V3ukrtS+UdLDvxWlqJ4T+eFRVRZISwnQ4/tHABi44Gt7g7nBDO80Xr1pTVV1lY9W2M4etYSk04kJE83SNqIxY6ffX/4cwUKvmhrPhTXMfiqc8K3DqF7aFX968efeaV8S+5W1Ywisc44CDGU9AlYCFuLQQMRfsl26y8AE7kkAV4ps0r0VFL82TdA0y66FwGCyVRdzDoS2+5ANcYymf19bTZ9MJDC/vyNAeR0ITxX57S3GnNdPe0HfjbFiWqWBgWE82ZZuQMCnTZ66NreJS6Uim5VHpfNl0gb80OTenUouNZjE3sUcTZjphS7cqYWfNitkqbtaPaDbRK5lTm75Q1WZj7+688iIlz4vedP8reVoOLmNkFtKFgxVtSNhWgXx+ltUPYQI+1c+TTQe6KlvDmF00hC3T8M/+jsGY3FPBYhvbz5jsEGZgTHaerBhz88L3LIzZH1IYZSUPKGPGzZmJPRsKeO0lI7GheMxejKTqxAQspKLrLki1D2nVqsFHazQp2A3YjSaFlZKRdJVkTNXDuzCVtg0Jg1kybhBpupXuQpi+ynhkGVqwF/sRaNL/6G7VY63TgiyyWi6Uibpf2fZP5hIS4cyT6CXWrEg3gYyzIpFNP/r6c+txN4hv0kdYvpDAIoOGxQBrdDgP5Y1FLeM1up+jbZ+63kynhUnpbMVVVL25OWtQB4YtycACQgUNh/noZ8pmHLbRrbEUlKOYjkuV82XTBTrhy1ZwqaUhy8OPzKM7bMuqxjADg+6gNYsz/90BYhSVRotWBixjTBrOhSul+OewbnUdmYCvdHTeBSu3Y2pmGAxPGMSSsqJGCvrynLFJCf/p2toauWgc0XQC0jifNl2h620VZ1vu6LI6E/HzDu/uunHMxMA7uMuT5Ixpfv8F1sticXMJEbYUB9KeYN+A1vloripjYraWC/9atCezo0hhRG/kVY2UOjRtj4hR4E2PXtlpGbSULjSgE1mkLdhTYv4FrR3BBEtZO0dWpmRbZbZF3D9NbWJGDk7GhsOtTXsG5LHas148tVzRhwmYR0HTXWIbVvSYppldLXFZiMUtYSStlIzPTtL+TMhUUlrbtM9UnO9AvL/0unlkQsUBIqj0UHy4eutwKC5iljCWHdP6jP04zTKktfQNGLvJwkenAofenhTi6vKgRnejY3OH2tRfvoKOHsN1EXeyCM3INjEF2edXoEyjmEBimebLaj+kMjnMIq/alFjtZRpkXpCebzJOqXhIW0vGr7IKLkyrb1DCuZukWdvGuVZDmYB9rebQph90YrpZePg4K+4viqo2860AKePVHsiFP0XEO8WTyu5PwIfKOdkZ3uuetL0ZGEfHJTywigOHd3LdmFBAL+FDFYNvBx+qRjARK6rmx6b5vs7cHvlL+JjCp08wW981WY51XqbA5Jw3VNf46YWaHs56U+tqv303uq3jareBTefIt5xnB59+W3E29sfPup3XcGVWBARIGVP3QC5MLCLeKUVA2f0JuFI5JzuhCODe93vHwDg6LuGBVRzooQhI0Uv4UMXg28GHqhFMxIqq+bFpvq8znyJQZJlZCjJQ0m29yJxujbMId0rySbs+xV4sm4OdkHi450d59YSjAAohURjLFBScis9aEFduoxHLNEMJA28Hr0k6PhG3SebCSsVD8LPy2o+iJq6qpCgXNxTH6NhDUUfFgzS4KyeqmlJwpZzdt4M3DUOZiE8Nc2fLs22t+TgXlqu0whmaupH0Ln88UPXZmL6alH+5Gk78q29NczKEAUY4QbQjkhw8+vnhTMQ4fcZZLUF21NRLzIttBjBugpS0saotI5Wuogv57DqwU4eOTkOaQEo6zbFNf2Y/jjwrymZF3K5GBhdBZdw8QLmwrgS1E59GCchRd2ICzlITd3fYiKRBtuQjFlbJSF1iZkdO4pDPxEryXkzFS3IC7w4z3ZB/2yfTtJNNASrZyJmDaKQS9qH6tnV7pqr/UzGeZD5smt7U2gYh1nKNhZBphzyG+Goxq5hvS/lO0vVpBR4zH9Z8twXqF8UvZi2JGnB8FYxCruI+KV9vCQsqxjCpDifOj03zpMJsrNi+82sZSSwDlrFi92SHPRtKEdsHEEfS33S9mICRdNS1aZ6tOTNHGe0BFiwiF+2mz0Pe98m4bicthj7P1Hec0r9/0XvIK6biEm0tXaYyuoJPPjd5g5rUY9vHpVZDmYBprebQph+zpyqTjqSVfE781C7jqbi3bU3CulKZvaV8ywxiLqZl5s2mE6TCvJs7CdPR8ygHp9zeXeP+eLy7w4KKnk+1w4tzsUvMZkp3IkCOwHC7mNBE2ftJ2W4Hk5d8TO/qE4ADIlCXl6CCi59pvdxwkIpdDPVkbNlXceFKUzMqsSjj/nh32y17NQHvWU6DFSdKMczOmIwSMbCQiV+ktXRM6as16huUsKdqFcwvQ62GMiFPa+fQph99ne3i4e/0EnNjZKbqZNzMtro7iqj9YOZiaul82nSGqTiv2vq1qKGNjbSBU6qsGMRZZaXw7g5rKno+lbIqzsX220iX8Aktn4sCIaj69WP0vesqydhQAu/CkNrmdspLbzOSCbjVZv52woMvG4idImCsaS32pPaPS0N+CyYo6RFOWEluzemJxIKpcma6XnvksO7O9iLv+ATrVT4P27+59P1ubeaeVUx8wUDrmM7VFydvQJbLT8HV28OC0iFMyInSObJpf0Awb3hBn7HXLmKFg44YbMBjDkl6F6Q1q3oyle6sorEtT623IHLlEtZNmeNXr6DNzQg5uFwdoCDdNGdpEzumM+vGMIm2rJunndCTN522lHuqCtEv7cUUgE6aSUGaPQFlu5cdN/kig9qzGU0duZbCgrtpKuqm1OeH1BhG2ywsejaJGmKcCptebGrNqCJzIzFuG8oaozPhbu4fxmHMwa87uYsIozCFVagqjM6puxhrYRrEHGy6g5EXbG48E4dKoY2ZE73TJe4kY2pHMAFXaudoh1jyag2T9K67/X9U12V629TQlkX1tTUsK6/owcKGHuwgS9uNaDoWt5vjXWB5+Ui+rfGfNwqOVO7u7rikCoYGjZOy4dEdmY/ZalnOv1QChjuFxuLPG1aeRSmGLd1ItAzuJ9v15J1ty9F2y3qtyZf+/CsufOyz71g2fOO//lo8c6/Cflu+Bvea/HAirGbdeKwQBrNaBUPF8TKedTh/gDIFeT1My0mxuk1zAniDVkz6CMuX63SlyWvrhkdDOA0KD5pad0h295Ie+GheWM+OTicXXOfUpmd0vbkXv258lgadBYqt4Pgdtu8chrUdS2MHLT2LUf2jQSWIM77nadCqoPFsxdJgOiRZH8zA59wMZB3dDo6XzalNz+h628b7vjuAh9iPwND7JOC3R6rvkSjvh2L21NkimIfBte44YYy7xvJzeN9c59thEWyNs40f3A+QNcMIBW51ZE0tModVQvBEWCr6/qiXjWnVbu3qsRrwfCvJij8cVhWPYu7F5WQa2Or/Pl60rdLkZ1bX90Inv7kCq3UGPxRPeVYAdWYDOXhk7uKQy9yGfT9H5y15XybkLjmlrU4DmJqzcdh1Cks06gQyL44oOUwOLuOwAdKFxxToHZ8/IXw29x6tH8oELKqfKpsO0PW2gEFNjggBcgS23EX3grL3kzLh7roQqHD89qelR0xVT7Mfqy89WOzMyvZ2iVktxzLdFm+aRZueMBW3iJ3dGXk6FvZh3pi6pbors/DePojPwVI/S7MaZ3c3X1NzwGHFk1z1IBbVdMXnWtsWyl7zCOdYC2YWsJfJ816Xq4sSK0bpCpQvp8/JEuT38BLU8KQpURPJizqK2FRTGjOMKzkFBxtbkRpmXd/Hkc7WfZqAM61nwU5aK9FsB4OSP9w4k6kSnyVZ9DPzorQzUzOhlOAO3MfUn43tjkHycJ6jviQPV81tlZRpG2trvE1sqihjQUUdF6Y0NrtTV4xtRzMBb9vOp4sza+u42mSEGepNzNO76FOwHMuMDL2D1t1RkknebVdysRxcxrwDpAu7KtA7HhzMz6r6cUzAofp52plTg6O7uzRDX+CNKTmQACnlyR7IiScFzJM/4qfswhScpCKsTdtzZz8jr1e6KZ/qKsoHSn03Z01TMz30PbemaCb+TuiIaLyPaMV8QawP7uHNWVmsjHynqSN3idLgbg5QdUPTs51FbyZxThqJb9MLut62MN914cp6mxqjMh7VzOxsJ/ZleqYTyW7Th02tGU2IOn1Ek/O5uL+hfuPJ1NgRyjpyY2IAcTMn1K3IlDiu81toVhjHM4ltYZw7K92QrboV7GsUlTLgkRh2N92OuhFMzJs7qTqeLGHyUDR8uL3wWZ0l1xKBNGuutK5TDl3b1mVMLYxx67jbdXgTcLzrfFutAr7yjOnLk6ZEpLi/AC8krf95nmK8podUNLXkyczZCm75zHWN2b8kEkWvterMBFxpNQNW/aDqbQ0X9m/mCGxjyyMqBDa86fVYlWXzEm41LY35hbLr6GZgf9N823SJrzvbavhcJCBL/wUXfR9vPoP8vsHeABUDqqvIOF6AduF1TVMS7h56vnVcbR7HBHxsnjebTvR1ZuRYC9POZNI5mnK+JtxW3HqayXrbEavtlNwoRXVqVAOWfZTdCpT1t9t/wqTGRfAZzXdCOgbyvKgJlr9/r+BJVmI2qH5/XZeNKDIx6itYS66vVq9ftcXKO8ACM/LIYAMXpyuQZkizB8kSbyNyrBJAI/r+EqQM4+YuqX0fDT0zomr7nyRFIx8lXW5E1m26X2C9LKQjZADM6AzR0NIWjFHwro1atWKLVoPMzDpppZymvsyI5ATU8L4oUyjD0hW+mEeTJnVTSnF0RbYE0WFiIKzJg997SkuoWrYSMDNqKoGhFCeTO9Ju4NqJYGEs5qOF/wLy5g4QcukWBw1mRG3C6YSs34OkzDfs9QYkXbyElHBDiImpI5gFVKuAlJmJ3r+ApRZ3LIQZIdr/8M25R6QJSHvGAFgTm2y08sXAgRhRHmfF/UVRSXH1ZUYkvaYrQ7IxNUxIsNINr2ps613CiqhBUoQSOCPyr/CpUon+vsy8JossO8qrJ8VSHEqtEKlQWDMB1h11HNCGCJp6AstVWlVIR2vj5qS94mDMGsgz2pZzkB019RIrmm3OaHUDOnhjYyR2RsXBQ6EdmutinSZKPKTUBhFQYbDrxceyaNbKXpBSIyLiOJTh6Lywljvvd3xE33px5JJVAmgWsx/TO7QRlgsjdgmgPXYdQjv6qQTGUGiH5mtRS0c3FBrRiG+Qy+klfavcYs9TaVabx5ctkagoxj4TbRwt9cinfJjMG6zGvrEvacn7x794ZqeeyZPiaxQ11XMMpjFoku9Lx6N9ZcGrMftmbIlHsvUrKUXS5Nsh0iS11TSgzU3s17BDa7ZN2OB2RirNbefQ+e583NDYkD1FhpnKZGPUoQu5NdldvzSqS+Q+oMIwGK5Y2nTiCyBCQtmXrtxMmCUyUglNb+UqHANgVuHl13KkGr3qapWDyXgNV+sMyBlSBmfrwHjRIeZhrGWMBicHYlYsYA4Rx2pXJA9jNn37GytS23dzmciA5nupQTMUmtk8WcJFk8FrUD1I2ZwqNw+tv0IhHdrmToqJ9yCoED/9hOn9UqoOMAC26D6kSB+pFMuEh7HT202LT3JpwsjEdKisnIfZsGQjtw1xYnJ+o8L37FERv74eXRtYahQEqzVI76XE68ss5peQQiulWBAr58FnWCMj1TTFckjzwPnAIykFxDgxV7TKfV4VAWfU1GXn/SrbRBHP4diGDW47XxN1wCvvsuRA3thZzFALzdEAC2GewrKoKlQx06DkYSxGL139Nqse/apVKlBbZNZ+wLPWU74pN9Nb6bCR+2qog03D8eDNWYo1L6qC7oywheZPYDeH4upKw4n7MDL1eaVwxGvVQH8GTzVAnVjy5+gshdyod8OeLGppx8JaDYypYqCbNbVYpBJacaepMelFzRRzvmvLdHQlV9agSTAmC9LtSKirmzIP4rLnizfDOaNIUQWkenjyCjLaCcegGqIpsEoopUIXgUr9rmGmkizXh3Y8XJKPSFTikniIWGNQqd+DdFwkwKjHwIPKaEJvixpyCKjGZZfexX9ztF5nKVxcF3Q/RaJo4dWj0lWTEYuKndDQSotVQjf1FESh3IY/beg2QLuMr68Uk2YDznGX3NCwPPBDQzJpBYsRyupFIJwUrUwRk44zloBnX35Vi3fNC7EyMSx/CNaCI8zIZCRiA3Vi0WaQoRtTQ0kfEdY4LKGKhk6WUl+JeWwpxjds1hcEUPtRmTUGT3JNozTQ/m+d4iCFUw9GBi4jERfZpSGPFOMkaifTMhtqpmQodR0jAyiraljMnozGRiQk1WKPRFfzGpWCuw3UvFbDCDnNkmUj+m4uYVWXiOnhggooFKloUUs9WHNlGU2F4EQNOS1akG0bmxGHk5UObLwZAjAllJQCaoYmg5fSi4u91JFLinNkCvV5xTS04UHUI+AgZfSggmw1pOARTUQELg5WTQoW0DwOdmqDycKikxBHz3U+foj+YhDVUYknQoTSOBAEYKk3YhPZrXNGiLgkVFESOYQgx2mWIRk3BIhrqMKBWgyHrRGBPhzCiYjUhYFvrgFqqCTAmkfFV9HRaRO0bkEuAbFMDqkmwINesqDyzf1eCdH0FdQD1NaTkU8RFq+hob4JCSGp8P1gQvZ3BrTEE4HUoxFgZUSibjFoCCOimoAYJ8WKnIRsLlPI6SHA6cfBgwevPClSCX2UpPY3XS7hYwqfPsFsfddkOZaOTIHGijHVNBocBgTBVLVsRiPdVM14kLu/6KJdmyKQenQCrIxe1NUbDaFEVCOvTdxgz++bG0Byeghw+nHw4MFcJEUqoY+S1D5rs8gyLaOwAJolQMNJnQXtbSjdKmJQjMwYm3tcN+19LPnQaRB9zylI1fCHW2UGItCoZG4RGSU9SfCjqIn+QLKp03fb5ORQgevHo6gVLnY1uBV0U0yBD/W4+3I33I09CQH1NTTj1FaUcpt44U9HRz1+zbbVjTWetWckogLSwjQzEs3R1JucSLoLlFrTz66ieshW9WUUNVwR1VDZrsmRzcXhXqmWuhIo9bhEYBnd6NuuGiJJkE1BERJ1aSYJB2YYBgutJEofCmqiCoduCrLc0Bd3FTShYQwjoEAV1ABmMtBIJDRgLiLHZI7uHrSWM1oYq3lsxxKHJ1pccmIIFA0SGvR1cp3YoOBs1jo1lhiig0Kn4hA5gT1o0ybINHt0pXDqwcjAZbTpr8tr6CJFNbIXt21TJ085CFP3dVLUmgZTyU5ZboGbTVokdTiYvIJ6WNp6unAwPoGBRWyYvA1NbNholOwyQViSUZJ52Dw+NvHwCARkkw0bmdl3CRJ/i5JePIhh/WwglavQ7C7jMU1DAk1YiQhk03lNCIkzIaYJF+kziNxcoB4vQQUXP9N6SWUEEUljqqIenKGmjGxU9hMN1UyIVewUy7koS+xys0nOoqahvIJ5oNJ6Ovo5SCZ9GxJSKucoFiW/0xNqTU62luN4mcpjEpZtaHS5h/PzGEQ/BWKQUxtIpcTrkgWZJB6FaUwSSBIKadVRLbx6SLpqMkop8iFpiKZtYWQ9Vta2cYWaK7kN1nPZOOL1nSvPuE1yIKYkIQehHhcLqIq9NF+B5PCMuTDphFo3mzRdaiKwgOYxMPA6kphVNDlKWeycisrepnMfB2v0KPCAJhuYgw+yp3lcIzsVmOxpBmkuhdQJCFkFuVRgc7xppY4U6chCe9N5MwspYS3OiiwYyfH0aWJ24lPn3dAptGXySA2uEyHKWnLZJKT808onNXK1McmkE49PRd26VAM7DFK3OgPpN9UaFRrW+CeUsA7j0ngrAik2je+CjTkz3xHiADVhAjL4uLFz05JIntjSgmSGisbB6utrrlopk3aaCWxoc+TrV5pMpTeq/KKS5e2BRrM+3bFJhYI+a6tOQHh0QKZQ2zFF9HWiTz/runi02Hy5W0/hCZeZtiPWc6pgqoDkAUySXuWUMWBGEtDQwZkDaFxqGdX2PxZVNDmDbbMtWKMwUsAWk4bS+szJ5kmw7sJ0mR10XTLv3za1g6hi3snHnJNJlShdR9gHvLymg0ERRBUa00wTw3RBmiiNeRBtrCnyWCH+y8JjLXjReV6u71vXqq/Wdb0Hr1VUx6C2VjHlYUakf/suuKLQYS70eJzJpEXnMEd9Sl/3idL3QD1pRi4JUDottwVH2W8r4O30zplE9c0VWK0zuHlRUk0dDtI8JLZCMIU4dCNnoB1ekmDuXcjoo4BUD0heQUYf+q0LDYUUCCe4IrJpWbPFi0A2Q9Fs485kmXSrpvz87U+z9qOsYlwVqpqa1aY5YnBqYWpiOpHRh4Bjkm4monGP2hpO0VyqOwxejcWK1OJjvi6U1zQ+2SkcfsUHcUC6AuXL6XOyBPk9vATIVBveCZK4no2VNH5eU115YqvClDHZjFe65WxeSopLSvKHNQ1ZaMtBMpViUI1FODa5FA8zaY+BjXXUIzVVleZYUT4zpcsqYmpo5CNiVfuaLcpUxX2wmu0qKlGn2bqGR6mM+rYCUj0weQVp1jbq2Sxd2jY5wgn07eF9qxtN4JAIpBkKDyslC/Xklo4sAq6Rg4SEp6/Ml4RdRZq6kvIypP2K0yAfWYCxD3zdnJXFSkc6HbhOF1PWkit+3LNkWjVPjXpa0l0XDoSjgK3HtqkTmWgU4pFJRr3YdiO83iYT8GpwnVBW1pKLevrNOq2wV+OVyTfhAbuo5NMxmxTObmA6FvOi1GQBtdIn5m4kr9pJAmxt66qHbItCGrWmekdPF45r256M7JLnACOEfbNv5N2c52mdgkxzA0VXQT1ybT15LLjwwp82HFyHftzLUNIXDG/E1wfNxFTWtR+4CoUNiW0vTVm2KKG6cVJ9k7TS7zGaM7TKodVDVldS5mbln5HUEFSDfeSkdqYdyXInMu1Axp1njB3nt7ctAhxUDNIclkPZb2/xg8Ur0H347S0CSeC6bkD2pVjArOoL+pe9NzW7L6+u1iDB4vx/XL1+9bzK8ur318u6Xv/97duKoK7erNKkLKrirn6TFKu3YFG8ff/LL//z7bt3b1ctjrcJY9f9xvV2aAmtLTTbXClOt76AZ2lZ1R9ADW5BhSh+slgJYFcrUNbfbv8Jk5pEVz9zsuS3gcp9g/2DhPRxpThzuAzrR30t/Ltz8eEWiZvsDe7aG92h7IaiZ2iQWCCQ8ULFualYHSG4QosHlP3S6bp/vkDkKLJmlW/+5vlPXRv/y9Zvv9hj+ACpZ35pREyBA760Wmfgpc3hzGFki5xxdju9BGVXYo/xvPoA70CT1RzxN59FXL+95TiA57W3ArNxEoDnYEv+lj5lGcboEpReHG+FZxzWpxrnEXFF9jg7EYY5VFwQQqE93gtQL1lk7Rd7DPgpXH6Y/TeHhYRmn1tA5MuUAudLuoKtPU5j2XzdmoW3ifEIWWuqKBaL5aWuOs6K6tv72KQcHrbEHuP3aoii+V5mLE6+zL2fAspAfMdpDvA7NjKUfZk91u6KmMjqTIGbBMiF1bf5ao8JZyDNK0G+UZ9dNtKvOME9u4mST1uzjpk3miNtm2Gb5VQL+qJMkZFccyy9+erAfGWx4hiPfHHDIG4em6/2mK4LFgf+26W22Iv+mz2WS7jOXviODB+d8YhdYgochM4JJ2tOXGofJwlbnXxwUJ8aYr5xalP/0aEfxYLj2faLAx1KCGq4+JZ/r7khsSUOY8Miu/U4MaPbfHbDJeka9dlBmBPtOyGpBXlpwZc59XCB3zIEWfYidJIq2RohTw81TMrTmDzEvL76OHK+3VTEqfewsUVJxBTY4/tEngqiEbVfHHavouQwtF+c1M9SVJk2X10MuKp6KkpuljZfHeYqB7cZvKr4+dp8dlOvW38FkmoLxI0pyCpRy5aAbM267bzr/ZugIQuXQeWxcg31R1LR6EavXqoaSrQkJZCLzp9kzQIu6DCh80XFWwAKII922uc31U1Q5e7Y2edhlY1IwFzsN9JDggtbRZ1Tmp9uDZiLTyRPV82KuDGPVm16aNY7IpY7YAfPeuyScnvsbY3+hV41oXRw2yOOjCHhQRLKgN1HaDmjHEeObTrA42FLHPiqH4jASHSBBz4SyKBEOpS6+EAuSngFM4gfqOZ9IUzR9vJ5ZMaOwcnTeUlIwzwS6rPrMlBgFApdDjvas0X+nKP/6qTnJ+IWsPnqjukClgmaUBLFIMNJl0950HC1ROaC8nhTLHVyxSLRSkc3cS5ZrtTBakoX8HrZrG5zwaLjiuxx/kir9JY/ix0+OqyT5jZLqyUv4qjPc57oxvf4fF8vFBjZkm0T6lFEub8An1hs45EoRHdf5GKNlWhk5NkoEuMk3RcUMM4L8jxfpI/pQuJjkwLsiehsskyJWCh00O4WqzTvnpjlNDumxJ2/4ArJI1ELlRQ70ffpW/4J2dgXwrbJl7m43No6auEqh3CwJGEN/h2+PLX3lhgbkilxw6jkB6HQDe91WvPbHvXZgbeyrHgafCIkmyc3ejmEQwvrdVk8wsUlwE++XjXcWZuk2EEaFLUBvRzCvffXRQ0yOX2kEF5jUDeiBHI+wLoujhJO+2JL7DF+TldpjTvUX9+lkQqFDj19aLgO4g8uPqG8uQNJ3ZSwRDta/RUplry0UMHYt/IRcROLs/3iYltvXtVhDevNd4f+dHVkSgNf5nKI+0eTlvBbvcRZQPp7quxprgzCuYWNuiHHz5Q7rN8GyS2AX6JFisbRYsFh49eyEdopDHNIB8TFYQ7f3eN4+Jmlvzuo/3nWLk8q1w9jBEjKnXyy7XL6dqfAL4dwp8bp8zotScTiB/BSySnDw7i3Qm5kEQyytaWGctBuQMVnkGJUG7HYQZoyNfnei6VOvcbnjUf3JYSibiqWuh1uDhWvcbi8cMrJFbusy+H6C78wqQIX+dVVOnlJMvgZ5vd8PKwcwreFC1imhTCPKhiPVoiCQdAIklgG4UJ5fGjVngYLQpEpcsF5hvigPwzjkbJlLhbXIsUrGWR97ZMlvg/DW18qKJf+X4Pn02dkaQnh+lSBC77TSqAt+eQQRgaeT5DZd1/gM0wumowtctLJkG12nj+iFYsq96eWglImB3KKck8e5B5lrsjZoUfqHz2CNAO3aSagV0P5tSQfhBzC6TRWg10sdbAGiqd27N39ZX5yZeVOVlJ690L8HWdF2ffvGCLbVLCU1IAOhx0geSiwCwHfRRMOO/hCR3tbnVhGMLztc9Co2ySukO6YXT7vcgjXFtqjdl0LPIQj3eCiqyqEKkqKnVYd3neOm5fjpq6Fu2JCqTPmn2m1zNKq1qDnQRwo08qaDCJ2v0B2mOgZk0M4HD8ga4hUTRM+3J8pcQre5lE54/iWLSRoNl+dvaEnQlQfU+CwB+FXIEAm6R1b4ofxqgYlvm4KcW5+Mc5WC+nX4mm+sGqPh3PgsM69eJrjOBQZo8kAnGOQGCxQxj5aQOeoJIv2dIBOlhidzZUzw3SJXtU40X4NK2lCfMk4jMBOY+lP16uj9TpLeVtBCmCP/ydM75fcQu+/OVBHYue5W3Y/0wWPpPvkQC/JeD45j2fYI/TiRQPm0ZZOsCiB5jyIjxomADMoxFQNH3czOEAz1uE5GcF/xpW5Bw7hpATiNWm2zEXzr2C3nUkcFWKpB+YvEFRNCdtE+lLsDIRHC7JQWKHQAy/+ocVNA7iGlpHDS9HbJxQ6h6zBEsfcyZ1EChDPNjAN9E0MEN6j2DxPqR0JDbY1YTzDxh1277yP6HMP5FFXHSeSB/8bFq/S91h69sCVOXnYVbGNXNG2BE32tWXihy9z8Rd2eoxo2TAl9hg7fUXAR393Ph2tTopmja8VLIS7tGK5g+6gwOqHrZ8FcvRPlH8Vv7IQjhEH7Rlb+zCdEHFAF26N1Otc5tgtFSL2es+7h9hTV91WsYfj5UQsm68uymfcLFDHRY2MdSVWSbGLmzMFgmuTfHJx17VzrYruk5UfwuT0uNrAWNWpmFjqgDklQUgCys1nl17ew6v0X8INy/6rZ1hgdV2013zk+E2w7v3/JjsOEQod3c2X+JkNHiddMHuI68iOr90JANxGN8t+3+pQU4/cr/+RwqcvgpYoFG6NwtVJz8DbIi0Sn9siqprjKFtdc7IUX1yRy4mJayY7pdiBhTy9F1PgEuGy9Vm5unPDOFzI4PK/ujQ1T0a8eBpTRfIX4zN7zDpDPzxBqgRhgB9Ni2Uc1uqbVjkX/LBRA7lsMqhKV2IB7nTILvdnMQUOlmmaZdjn2+cf4QxTodRF4+0CHOWoJcUuLq42bQbWFdnsLLyrSw3n09qF8sKBDMKrBTYBkKoVDsqnpX4CpJaTBsxvlogw0E3PAODgNn2uSyDm8KU+b49Ipk4sA2UxhclHCGurb6tnD9Utyk/wmWQj4f3OTJHztr4lmdnnVTsjeZtZbD5OZyOG7Vc9VR4/P1/feXWGDPtmc6FMuK/AF+8/t9IXMaMwLI3Qn2n1WLafcen+S65ZMGUHBvZi4HicG8iyc/BquBoQ+1iOpoLq6EsFczj+0uM6HFLt3iHV4XBmXw5n5opa6VZL96ZHFOWszVvir5ap6m+/QiYRxc5iuB29GCFLf3fARvLhcJi6by7XD7L1XZP9J6zIrXD+HgJX6Iz3a6FGO5TFXzIG7j6v8O9vd/9NCNFi18x/9wnVYlFMxet9s4KlS313MBTW3Qv1XJc2n12Mjj71EW9tbL7/OQW7oww+zor7PlmcO19qa4/Ek21zosBjChzOR9AQLopKTF5LfZ99lpidrpN0OV4ygZumFtWo+2fbvmIX3RTa4/0Jqm40LEr6++zz+BU+Vf6rTVt73NUmKipsyXzrFxNFload/j77vF8UWfajwGrUJUzwAyoe69aIYqT1ito9yqsnUQtgS7bORAmMq1e8re2grE2tpYnPWLIlTheBIr0jFOOlpnhvEfV1zopyBSSJjvlSd8xXgH9imS1xcGRFT/AbO63U0d1dmqUSjypT4IAPJwHiXXTdt+ndR+dVG7/TP23GUYwrdIiaUQQR+UULfQZIQ5XbVVzRnNYQ7srn4j7NpZfgxFI3zH3yKCVyAWBr9qpudgJP5fs5dt+qlDXH2anO0rKqRdanPrvNvIhq83XaPQqJ4zXIXwSNtv3ogkcaS+cXn1cjfkffHtM8EWS0UOjQRyGR14lr4q6W7d7x+1r/1RnTeymm9y6Y/itdY3MfZOLNV67IQStYFjmUZZpmChzWT58il1s/m89TSPm59HqyBoLvy7YryUetV9Xc1sN0crxZdRG+kpPPTZErTnnWDL7M4RzkqfgM6xoZklUhLj+x1AHzsoRQh1tS7jBHDTKu0kSKmS/zuJP3A8iflu5KZjqejX6sGnooPZM8Yh/bDJNKLC4P4WRCsK0yKnbAT3zuPL/PEanbJM78yRVb5BLXPs3jtGM86TrXaqP11cDFRqPyWWv6+mM54uMZBhFUi9vbEj6mQFy3bMmubVEzMXd/1hjG1z0Wz3NcedVxuPkzyO8bnNadw0J/nzaM5rhYcEZt+8U18rB1y0qfyxqKHHB2wQxd3RPREymHcJAFm0ek1I0ogRzmCNxzRGm/OLkztjor158zCnisiNKdc0/0oitMhvdYPGS4uuq2Kv99j0+ajKQhkG4FQ6HD8WWe/tHAKyixyrkiB9dbBu7PV6g/OD2FOHRJsUPQZc25n8mH+dfgn1ona++GXtUlibioiqYMtTtkGH3WuRWaXdDb+t6Ly4ktcccouUbMFbkcBJ+Vxeoia+75tx3ZEqeD+KJJhHVFfd6aVdBHUYVxfo/FM+ZNXnUXODyGZUJen+YU3vaTw+7VZNwW036ZZy/Ydk3+YMnp2tk+jf/PaZHNdUV/iPoMvPw14PEMhlVVHmdfwC3KAmCntpn6J3J/FDW/9riiPdbNMeHDuc+T73ZTEwnnvDZAsZOastjFoWgmrWKUBLxkw//YwKpG2jleWRJ9gC2e87By2zWrHT3qp49g41wrwJgCrhbIq2+rn0/9GrPvW8yKQHavMHZUqVtuYm5bvmzqYPY+zpzMtzQCvS0ZO/x8rn0elqu0wiEJ7Z2nwD2fw+az/xtRbOsajHvtYEjfxtppw9et4aDT5xpfWsqOmnqJGk0TEpgQg5t0mD04yw3dOFwW97p/jLD6nirnC0yTu5TXk2Tl7tg7EW9qRAJm39Y3PLHXxQPknAb0d0dsR0kCq0qFkyl10MhxXBPSOlXyQla+Nav9rCibVXggzYDGYx1r6o6zaK+LdZrwKIaPcy1+8Xq3673u8wt5tpCLrbjRtnOJmwhfEraIsDYIHt/Foag8zuogLfIoho+zrQ5MAtnbjUyB840B6WUBR+diKz15v2L31R4Tfv+FQ9N9cnGAVbUsMwv93R2baiZl5e7YSdZtKd625CCz3GQWCBdWvnJqUhH1sSyatVRODSW7fC3j67D38JKl/zyHkMJLU6o+MQUHgWXJM4d8myOpbUQERFDbCB5fcaiovK3+ru2/WLZN3L1n5zVkLsIWTDud7mtFUW+cZUIak2UEYAoc8YlppKjPLkeRkveAPN4Aimt5dVfeOx+CkPNRKHU5fm6PsRSoJcWO84KvHTYCXq7Ivb9ytGKpgwuxfbtKjlgodMbbXnFW+idVQO4cd9KUJcyTF/EWgRzCpYW23iXg4wnYEvc+sweWqjUjQjlY9qCWSRzqsytfN7c1Ttl8nicZ6piMvXkIzxbwBWd9CwOEewsk7fTw3KBuLHLIwBa1Y5NDurbYiQTN2HgIzxY0Y+EhPFtAdcW1J4fwlE9IzqdYxwTZGYRSklmAx2hbSkwL8BhtS8lsAe4UvIGrCFdL+6+O/CHnOh9O61ebBN2myLV3YtZ7+rvLNZm7Jl/AheyZUb7MBesTKBcXRZrX1U9YQjS3fGCfAsRhn1vC5KFo6qO6LtPbpoZKO08PGdBi9R+rzNBUC+K+e6tCPmXlDqFIkfN0Dpr3WqGRr50DLLFdgIQuMnyRODhBLCJTMfSQLsEr5UIM9dp8dcMkqqCbr46YJGP2G+EXUD3AhZ6aKhi3Pp88Pr4Xe9x+dcN0+rxOSxL08qXI66WIVADwxf+fEEiozJf7cfCHtIRJ/QHepvztHhWQiwtqqNblnf1UZBJ3lAoqpCUZB6mhvFo6BvmDaGpJAbzxi4tVCuCH//xEjRqXeWHtsw6qMA/lXtjPbwHvHuUL3fcFopNIM1fLIRxWWoN0xjL9F1mm1yXIK5Dgn8LGpoELb01kUj1keIuXsGr4vOImWBfpuMYZFjT0lEOEtCAbkRrKKRJi0PI0A9KAuVxeKZMlqKDSCysFcLGrUvndC6bA3ePX5QiU+vv6Mnes2ODCCY+but2jtV4660ouBz5x8r7vwrEMbUZdwhVIc+FmhQLEvo1PoPoKnzpj/WuBI14TSb43DdjWHAf1Bvf3CtlOn9KqDn8IXYLSJ6ewHZpxDo8GDyS/Zqjvjg4K6eGE41axa3mfPqZ3xH6KyFwSlD7MZYdmHObq2+ax0N8dJGgFFz/TeillMqHQDa8kVQz1+U/AuHF4NYA/J7vC36ljFLdI3g1TQ7lzv+wMji9zsA4k3lp3L+151feAXEcEgs4kBXAfOzJM1zJbSVbuovkk6TrFqoagU3JFHjglN5b4Mge9GOZY6xdVX+q7KzZJB5kCBw8hrCrhlvzw0YWbNmQnyp/ITRKAPZaqg8SIEH7kmTJLU3fEMCTcoCJ0aFM0vT55EfG57n80gFCZRbX56qB45Gl9UaaJ/GRYLPXALD33FUtdKKnqr29f1f307aP0wNnjWLm3gdrtVTpoBYhvG1IyKEAcVAbjMWno8ajmWNT7OPRD8ZRnBVhI8pdxRS4bVV9VqfZIAFwiZROYV7DHIQTri8UOfUfy8ydM75f8K4TUd4cQyCZfZBDvMlzwI/XdWb6e4BuvMgnbFmzX9vyVJMgK3p4xGt/tWV53zO05fCv9KmQ/+uqX8Oi6GJ7IlUXS08V7rCnSHto4fiMRo4/byArLOJwaN9a7HQInlbpvrliOUQl+7UWCbChy8zvJrHn6+/S23M6tIHyK1CZiDVk1PRbPV7fkVbdbkl+XIHlAA5MdWvJlDlhxGKJMT2EKHE8WofwIlC9zOidMH2Epfy1YKPwTrJ5wZwWNKWAVTemyGNqUnIP33z0cIPK16ew/dnc0zKbD1E2ZX8I/SAbMQOWFQuWltmjrj8RGkS65xWaguIpUPL/XJQRVkZ8VZTtbvK+dK3TBS6YdEtudd3EIhd541ZEcWkD3eZMnnxdLXTgV3N19FZNG098dvD6LVZpLQ/zYEhdKU8tXfnVQAbKboT2zbfoF+fMECd8YGz+LzWvzN6HYbsl9AcpOdRFvutIlrv45GUa2xEXZ2dBYdiQtK5/NaI94XjOGb7ofFWJhWMIFOaiQj5uFmGO3Pci7nm+u1jAZIhM3cx+WTliH2ie3sBu+ccRizOUnH8m3tTQg2wDreBvnLM3Q4hOCYvkyJxH69C3vyKB6X4ApdvaK787zG7rJClQoNJh9lAsndCMpGtI+2PH/5EmT9oMTx+DBaNy3Qym0do0bOvlLHpaOsLMjPP7buLTy3DM977z8AGWKlNhhJZwUq9s0B+G7hhm//zw6IR1pB3louM0Cf3CIyK35h0vbLw4njCBv7kCCb+eVyKyV3gZWwTiGg0niwJzCcGB+z18e77/ZY/mZLngk3SeH+1OSg7BP7sE6oIKECLLjYqHQAy/+ocVNAzho4VWV3ufI+k0JR5zzDzDKyt3P8HCKUuH6ElfmbnjjIEkeK1/mEngmewpo83V/XCNIgiYPcu8FV+RozX1r6m93BEWX2FE06kSQbd/9Rtny4u1zu+hw6CopjSxZuUN4A3yu0Sfx0THqu9vFiT+atJTdl+i/eyznkyKvyyKTeXlVMPtvEMRdcxEW2+Sr7ChLgSA2yadJDUrPnMzbJbjJzdBRpHd75zSaCFegG1WO831QiGAZ2K5xM6pblFd/NKCE1eX9LXfAwhc6mkFHi382VS0erwuFDkYNMT5UiMVSl73sooRXMIOJ5FYFUzTFXqPCSJaDbGNkClxufOQP+HhPqr8IhXsY/HWdIksf82OYMBzQeEg+Td3tV1djZ0jHT8GqDuH7sjmOoV38OnOdI7QvGAQdG+CfPqcE8npzu4qVEQAl5y4gHxyYvspOc3Cb8RsF/d0BG0yaEgp9oj47OOzER4U+Ob8oVNwXg0uL2xHYojl3wk/1KjsuFi98F+nvLnpQXiPG7v1uX2H9VJQPvDokh3ESImjBvZDV0ifJF6WwHMa5ldPnZAnye0jSk+kbU4FujWjrOhV6t6wfm9ej9aqq2yrkdC8/+L34IN5K97iSjhf75wIVCjlMmSJXzeGsKFegroVoGbHURTkOezhIuUCb2yytlvzuQX2eU7BuU8CdctQFTgZ4mi/QjHLzwhVtjQgjbNJ9iqCkdWC+upqy+jjSrG2ex7H56opJlI709/Gsl7mM1CVcQeJuQrploKFKo/IxVvX1x2Ef0ih3YNF+mnJDlSTZc06wtyMMdwySh/OcHEXSWYbDWE+B1IMJrTGNw47Rrl1E9MPEvcCwc/eA6cCda7hCik/oQZkMower2qHZVhsCP3d+Afgwos3XPT52PUETdV+UL3G4icfmY4EaURy4aOu4qA+kjcJEHLKAuN4DC+0OC32EOSzTJFL8B4/NJ+WxEcW2G5H/Dl/al8AZTJuvTpgEJAebQsrHR0l2CZMiNFf3gMaDczV1t51l4/o9xjltPa/OF7xbt/+2NWz4vYzChgMaDzbU1P1zseFV1nDu+vbLHIHpqmcr9c9VzuVMTpZw0WTwGlQPgc5kCpOPM1lbfVu1ySu0+nL+Nsfw0cEvKTzA6fr4pjSMwCeGoC7W3/LTsixKQQ/YFLisBpKdpqwFBxBb4obxNF9I8fXfHfvXJAmsKnkPqTLHPoo0pD5vjQQYnr0N1KZ6ND7alLruOAv/aLEo0aQKdwI2n51uX0V5aU22/ag3n7m8oxBUTQnb8NhAtyiNyscfqq+/tTsGec4PGXdPWG1jJSxb5BROkRZCPAX+tMfejG7+P6SoySr4SInH5s+QGhQHntxznjwrymYV75xTQOfBlRY4RjrbpNoUX8IRS+c6oSQE4lENHx1U+GKdJjye4eMen5helOR9kC7FeujBBI3L61xCj2AkXo90jn9WFisVd/NlLpypwsmWOK3tf8KkFpZ0+9HtErF4fdj14jCQXBoGjv7GTmE/fmkfEucRCsVeuIe335ToKYg9lhjErEEFn4vAAEYKkZcTX1N7JMNz06T0UrpYPFvgjiydsUcm411mTow5GoMSh14Qk8oxjLSjjWAShBsqrk7O2SJ+VmuQ3gdq/z0WrwgfVdWtNUEjaRT4whaLpP0y562F0NsfszlYiA4dKQiSReblXjFg2FbWPk4S8iBx513mGUBSvIvLJt75aUuNhDz1KZw0c2XzX8HCL07j0FyQZXyWL6Zka5b1V/hUfYZ1Dct4jio5To9FbotopLUubV10XungHPlcwt7jHRpNt+XtSCTZyRImD0UTKyWWgM5HdTPj2NaNbptTwpEX8rCd0AUuy3GroVz6fQ2eT5+hQAamwIGq4LkP2Bfcd2yRg9jY8gR58ZSMbREsEdKkyXHGEDGTpkYTWhf8VTKAKeXYIeVY2Oqd7fXFpCnxSyYX4AW3GusVaRlWv5ekLTGNs+745nlssnJ77LHe4t05dzFPtrjcFoHNJpPrL0kGZQnnmQJHfBewTIuFsEOwRS4noTXISG3OAcAUuJg5aPrkLzIzJXO4UWLFJZ7naZ2CTLrA+bI9XucknVD6L5LFEg8kdF/h8fnsKBY4xlnrp9GugfhdllBiIzmf5Jfy+DIPrAqE7rgkl/WYgq3h+kuIfe195tbQvY3G5bWz6RGMw+sXfRKNd7whsPnuge29Atv7vdbRT8qiqpA1kkXhKB6bjy1sRLG/XDXbThoY9eMX7TNhlA9q6jN8hJmwqVHfXVyXRVn3AY2cu5IucQgzbLJMipApcFAR193hJUe0zee5opjw46NCPtzho4vxfAfLEpYCLqZgzhOX+KkRz0r8tjzOVMrFoQ6fXaLJSMbbT6DijUO6YGtEFPqAU3wGXohtkfjchVXV3NaTnz9R8onNmUuoBUid3vikT9TV3lY+2Tl9ldzfCJxmjMJnguX1xpnaNqZTnGD6u4vLKMmaBTzPr1JUG3CGuFjqdNABqgoHnqKCWn7cIQK46AttZV5h6L86uBrTms9g0n2aMirrC6xBF6nLKWdsiRtG5ctVQqEbXgnNqM/zx3pdwhyJp6PqZ7q4hzWvCLJlDseCpMZ/FTk3dPq7K7afJVh3qpUMKVPsihtZHU+SmRIKXfEeF1j48wuaL3M6xkWbNP9ky+ar41rGT1iLy5l8nX67OqqqIknJu8vCnoW2UhIfeXO0Xmcp5nRKYdFsVPp6/PbUQ6uUELRHLsTdX9nAzVXRlNJHj6x2uwG1bNvDxBz6EtjNa1Di5e3ZzZ5Yjt387a10vkNYYvC/OTHEppaKHRRuNCsy99i3mheGToZxQodmakYgAW7oT7StLovFzSWs6jLFStIJHqVJQtjUFmxZpk4LaCMszG0FsgnXQARmsehyoPhoqTc10/SutxvqioLuaRwRWnwDp4Xp/YNm2opYA6d/QBhh4iWdC5voDs9sE32cZhn6PLhvLWabr6Kacpe5ZnEG0pRDFnPaOdRhjOncMaTIL1I8n6/Oq69Nlv3++g5kFXQcfTDzXMBylVY48U2bkvKGTv+p5SFTTSHHAwdPg1uwl765wNkTkEfgM0OHA/cUCtds8saaVVQ1VNLGkTXk6Ldxp9kXFriET6BcXBRpXlddQOzN9woufqb1snMG6yJKjJXFGBKhigVfGBsKnAEWVwQ+MXd4GzcpExniCZxeVXfRcIQ6MVQcDmkgH/HYYkocHvc2MpB5/OFqTme4d/9fg3u9biMDF5NWkcINjI0WIyIOVV28XBLWnYviL0GYou9Qva8VO8RBmsOSBxmcud2X4e+q/4AnFNzDL8UCZtWmHs5pvQJkRNUaJMSCX8CztKxwaDi4BRVsQV6/Qn1/TJHU64/cOofLH9lJ1qaD6gG+gDy9g1V9XTzA/PfX73959/71q6MsBRWOG8juXr96XmV59feErFuQ50VNhv7762Vdr//+9m1FWqzerNKkLKrirn6TFKu3YFG8Rbj+8vbdu7dwsXrLV+/QWmH55X/2WKpqkdEzTh2cdvPcv6D+PU853vjt36HAaf0MX8K7Vyo58ttbvuJvEt7CXfj9dYopS1YjeYqERJcCfK87x1CQ9Oz1Kyxu8KXQQeS81aJvT0vbBvJHUCZLgBSRL+C5vQPx++tff3HGyRyqWaOuSzEEQ8DMvpY7Bm5Kj+tI7jh6nCDtDjRZ3SO5TU1I6AMWAwPCBi7avBJ1DZKleGdmZzmRGhvXEr+8/36eL+Dz76//L1Lz76/O/+OGqfxvr8gk/v3VL6/+b+dudNIRsxjNwD6c0L7RpGHSd7/84symZ2n7PokbfdpaKsJYLQ8k4vtW0Vhu0xyUL/9tBZ7/uysmo8h5/4u7zPmSrmD7CkEQYuuV+KF4yrMCLPZk8fXDadOCtA01efpHA1OC8i7FQtERKbKQerwk4tRWHKq7R6HpJ9iLBXt8x4SL47B1F6IgY0IvfHjJ5pKl4oXs9Lnu8nzHwIaMGvg07va2J+tqE8Dhv43gXLoGXcddXmKkFrqf8/Z0XUTv6nUxRkcv4Tp7ceutA9oxenxyEhvjcZLERjmkc4us9rSxhBFkF3tloUWIrxzUKZ4xV2URC/xNIILf+sZIjN2xEvJcvjk3FZGtHaZDM8nkom8TdE/3ZJ/ocqnpdNdff3VXm1qT1qxws7hteO1TUekXuU9/L4qyDllJSOEsZSpTeMeGmOrIiNt0wFdVkHaM9ezW5YCEG+FAVGuEddfl6GgjhvZk4TFjoi8QaCb6r794aDGnz+QiAXP+f76oYu+/QzNt9JWphfceW3HfBBtBNsJYujGQ9rDtQiysZ35rc3YU5OmqWZHd7WhFtpB+24VJugIZ9l6jXxVxQ7/7G2I57GxEs+7afTT4iVpqsfchfH6UspcC7eHGcZMvEAej9TKkPkMWa80fKO6sYNiMz12VousGKVJMVjl/nmdz54Xh4lK0xd9kePbaE3666LMbuDLTUDGIk1p6eneCqx7Ulf4ULYQL+1D3sSTqcEMAlgmacJJiwF9Bs9EonA1tnE5Bc9Lm63NEbE4lGA4Y9Kd0Aa+Xzeo2p0wbH0Q/0HzeZkETcNHcZmm1dBFY4x0QRvVAsPkSnJC5iuT9EsS4hbCd8AKUqAskQdeQ5ykMY8fpSPymj+nC0X0SLnbsfDujyB0EHRvn0WKV5sMbUBEQ9ozTvekRNtX46um3/BOyBC8CN5ceRyzxxN61jrtpCVeuI0wLdek6bmePsqx4Gsx1+JjCp6AN8Wi9LotHuMAvy+b3V80qZJa+FnVUfD0ykluUG2xg/2Kh7I4WroujJEifEO7U+6O6emiiLxGQN3cgqZsSlmiHqb8iDWqzoGO18hGxTGyc59XH9K4+AWWQltXjCN+fu/cQvtVLWF4wN4j9eta/rzBs9Xrx6HPKddRgQ6xOE7z1Hy0WXJNB3T+vhpiVCJEYYVPzPc/aRdijCxoZInq7Sr7dCfi8jMsOyenzOi2JRfwBvKgwuoSbkLzIBGE4d38C1RVAmgiMMassJnf3AF8/JNAMDQyf4xzdlxDSupvnycgGUftab5TwlyFteZg86ZAwOcdD5B2N72LIOB4BI5NxPMR3iM8MhicwQ2TZGZrU/gAiSClbtBc1QNZjO1ni2H8fx5Jl16mHdUIocFoFkZB7jifAXgA5Tn2dP6IlhpD1p0FBml5dJA+R3ITYJiL4jh5BiuqmGYUzwA0k7aPX7pXm0cb7uXhqx9q9wxs2DUiHT+9eiB19VpR9/44hMotC0B6D5IFkSMI3UAIPNrCJhvGdtzRkHwkMmBSyfXVHhTEmpsXXHgjGwEcGDhcdKioAKZoqitgcAx83L8dNXW9sdc8lg6F/ptUyS6s6HGG3ljOI+BIJZsbn4YPvBOnbBFWahPmZaQTRt45v2WLcBjoz44SK7YnextUa4QGZ90CszkaoNoa3Ta7TFYwQZEfjPs0XETH3rqbTvIZlFcyLnfRisMKRGaiTcJO2ieyF6xS2azdI5qPNE1Y19ZLgCt8GwFvJqMyK+t+fNlZd4q6QUbQvs41GbdZMiY7+Z7oYEfuncWkz7EqxZc6AOK7AiXViEOd0tX/siUKh9jy4Rx1sy6mrdOTpIyxf8Ky6u17Y2iGOF/p2s88NTLp2SD+OQQW77TXYUTDg+gJB1ZSQ3NzWhyl6d3fcGL+hGfyDbcrDI9nG87RJYSVusdCjgC5cCJY4gCuGm4ZBiMkQE9/Vsmg9P2ATfh4xom2IVdqP+AljVIHP9d2eRuF+8e8VjBO0pQ4Bi6449E35SxBLtxr9zGOY7tBpIhEwdWdc1UnRrHHk8iJo1kQsKo+Ib0ggOT4mu2AYo27w4K3bwa9uLXjk2a73V/D4hDPhyCNXxFbcEjnK5biokQYWPUYKp+KJvVB6tosTKHUIR2LCDeOc2VykJMDEIxi9rxii4ONotav0XzBkBExwVnVdtDcSeMxePtwOxzf2FMFG1bGLIkQ6/CXI7w0+e7+Q8FgxhXF9UlsYubUTPot9iziXUZDcW/2RwqcvEp3NQgLbR7G3omtPlKFuNDET2FilUPqrR8Q4LFS5bBjUf/EJORwnEU13ELZnLDPvDbTxdI4RZamzk6ezZvcoL18/Mg/X9FAzdOZ40l42GVRf1veSfldrOLoDpnvXYLibHxQj1YerxUC2ef3lumATFAT4mDdIL5QR015TRSGmszfERt5TWGEIBGLvlNggCp8+1yWgEzOOoTfRB0x7ItKMDh+PpLcnRVaUn+Bz95RwXOTbl/fWVZXaM1/kzLqUwgtk0f6mZlAHzqszZFI2m6spESJqR+ZA+mLVgQtjcCFNUfc+sLX/NNy4h2w4ysFMdL88RffDyUTck4nDaYKxd7t3mnBwtu+2s90+TKB/fq57Y30/NqV+VB4q8lAzTClZM+91xVvz51WfWSEoUGZX2XTP+DP2iwvmBLg+t8HM+Yl9sKrS6vql5+mQnRXlCgQmYupxXYHNMz7blYwo0jXio7u7NEuDVWFy3zRITxp7dz+v2vOCPm14SDSDJkuwtsth2XA/g6pW7inzp8LH3ftc3Kd5pLBQjK+/xmyB0uX8hX5w0U01EBCEGC6y9x/duiNisO+P9b4rffZ+Z7dd8p5htENDzKTRkEn3WM+niFZrkEd6L0N6vmijRvcVg9YIQo/Tzj6meeLhYOCqh/TkhLqxH6gKkPX0Liay91GQ/Ve6vigqnOnFLxRLokctixzKM5j55YnsMz3Nvf/ZWy3tKtgT6Wn09L7z8PQS51vVba1ByixBFOO62fVT8RliYp1XhfEuyXtXvrtelhDa4/+LK360RGCZJhzuoJSLP0CQyrxV+Ze9HZvWi559GePPsva34ZQn2iO+9zmiXpsKLMj1PdGzLxGeSrHnblqp2hPmHk/LnXFfvb0t4WMKZIsr9I7XjALdmk+Ps+Ie67N7wqKfQX7f4Ex7zjy6qRnEpNKzY0FK2zlibF5zdDi5bR2rUbJzd7hONAkW7Jb0JgF2LJTX4D6O4287L4If4inin7xPYeL24mVP5Owoe3FPo5MmIzeK9EfC7ud+5ODwCo5grJ5l4P58hbqO73AZaeMefnxZ79eV0HF1mjYC/KrGSZkvYVU05d6o4LPrNz09jUzuk+CmRy69HuCn+uAk2MXqImvuU1UyVcuT5KJJ1GwfcHT0FT5Ve/SE3+wcGlEDJ884xUGFU6jEwRRFAm+ncvtnslO2Q389GBg9WuuLMkWWHeXV097E2OEBeUSlk1rR/Xl+IrF/2OZHUYe9vTH2JStEtD3hmtm3+Xjc04aqdTIvzol+lC06WqIisq1+bGBVIy0Wr5FtOGDcTg1k/GNP+oxrT2SB2Rb89Vd3hTnS20VRIoARko7vMQ4F21tpuMExwH2MLuGfkQJ01VMXojjBcpVW+Nz1EiZY0u4v84fsQ6MspiGxQBhi67k+fcZUBdlRUy8xXROiDOzVvM9+dSpeJGw/W+fUnZqIaDvJGRn7N8xb18UDjGNGEnRHSQKrKh5SEv+BJjgoC5P1ojsryma1R2f518U6TdyXV1ctLHXI3Ivb6hlOO33hwuaGpU8G4127G0nWB+GOPVkgZEDuLNpV2/EFgucRtxyYIK6N3dX7Dj1yUX1tJXGQM+pH6Fvw+OIN7kVgCv4Oi2LKPbGRRGmBxvpuSqB9Ej4fy6JZe0qgru64IaFez6xEPqH42u06QWs5hkDBa0+mVB3Eyigezt0UT2RZ7omM2gnxsG/sNodTvSXefjAtGUvEC5wEX9QEKvqcvLthP7BJCkKTHAR2hktREJzjIOwSN6mJ7y81fE88hxUDV5fEOiKq9t5h5ATpQ5qwpixhnrxEuyTeI7wEm+NbQ+r1v3mvSva4K4zaP0AdW5gho71GayE7z5MMddWSHH/1k8R9Y/jW4BSNXePGhkcBJhoh0+g0I+1kwzQj7BqbdGSoIYfF6t4YI8aQ4E/x3gCyMwjHpqm65bEJrG55bGp3+ONcLSN8MjojDk/qjtoKER2jNYE09QZpO4uR3xy5hE+gXFwUaIOrfsISItYKTBy5hMlD0dRHdV2mt00NY1uNQgPVf6wi5UXqlABFhJ9r7Fmc3HmD1r+OMkYSEYfNGPw2TAmRHDlB88/qKV7TjrDQEUJhE4G7FE0xFcYXmCi8eoALFemCe3ry+BgnWxNGdvq8TktiOX0p8no5At7/hCDO2Gm+/JAi4VZ/gIQPQ+I1BzRdWsdPRbaINFci8oiMQCE/BvlDNFOKwxttidF4z09io+xzZEVGe34LIm1InYQmSkHMJK44MActiH+1r4CXIK9Awj4sPgr6aOymauASVk2kxLknYI0vTMcnjog4Yq+R3TnoRPG7ftHguhWM7U69AGmsYHMuA1YUmnYosa2CE1w2rctwFF/X2NmIt/dAgzYXLuEKpDkVpO4RFf4J4CdaO+P2a4Hd8gmTkSjiUUlvFX6vkG7/KUXTtTeJF2d/hJNUdW+9qxYWkDVBDpOP6R1R9veQdfqhuU/epmbQ/H2v4OJnWi89WYirHtwVJgNDdHfLlMy6JwzaqzPULKO93OPergJPyIFhT+rww6KR3XznVd9Vcg8L8NqLp9BAptk6osFwiQa7TrEmEEtTGzDGuzNyBXOsV8fqYYsuXve+wKqibs0GviKymRGim832ppBbPMweJZUZxhM1jmVGde1i3kdJ/9GAbiL8ZfX3PK3Jy3pjHzcODY1+ujjFaCYZydinob291O7XY5OMbW1s2o12ZBf/qO5D8ZRnBVgEJyM6r3pUURSjz4jF8wr2OJU6odXui6T+T5jeL73UQpsWjpt8kcEPoAZxHITdW+74ImYcHnXb9r/iBB/7tO3PskF/pdKkhF3A7CPpiuFtxi1XHmnv5n55eWaPOm6JGhS8SjAcgwzgdwVC1Ldq9KiamEZY3MMA2v/h8aQcX3+EB+XwQc5qfx4anlGSX5cgeUjz+4hngCTmbVylhBzkwVgnjR9glj7C0uaNyG15E6xfAHvkxRiG5HW/o60Z4XxK9BuP7j1w0D3qpswv4R84W92+THv4dalIEze7/hPD73QJQVXkZ0XZ8kocJb3jOEjsXwt73wepbQiC0z0jLlly6Isnd3dfi1hR7rrnvPnkvO4vItCCIsatsG2JNnHYIAsS6nMCyr3aJMOl5QUoO8UhyPPU+oX8jlnpumHPX28mOfx4dXb5P/Oxx3iXJdDqhCVif+zcH82ejrF77pyY62b+ao1o2IfFDfO4JzJv5mUhp+23NRuNa22raLGFrV98PeIszRC5A7MCt8m1O/KF5tceOWu8jqB7sgDkQ4zFe1uTaX4WRtkTFtmZWeiEyjW432PKO75p7Uq8H6BMka4z8DCyIm/THOyTxHtoXGhqFYZZp/onjH1e7AF5cwcSfAmoRGaV4npfaCvearPVkw6kX2Nh/5kuxkP+ady4B1BBQnn/Y0CnZvAPtimfBxSqKr3P0QpMCVOaXol/5+Fa6g9LcC5Bj6skTO0Q278393D8m3s/2Noh/Tivwl+t2DvDH+0HyUMMg5zYNN+a+tsdQUkGOUaKPMXOuifbaSh/9f8PdLn5AojjbcOAN7TeOHDjO38vWICdI2IIzjaPPlGP5AQHzP/RpGVYECGl/OV1WWTh7k+l4j8Bn7w3TovrSt63JXyUpSD6IwURrcagaOBQMd3ex9uPiVYM0VsKSmmlKowpx+fjWKujiSIryqs/GlDC6vL+NnY/W8th8U80TfQBb/RDjzbMaoKGziuE6QpmMAmMfo+wzURhafOWI/adNBS+035O8wd8GiZTyrYn2Og6hWXretgrwTrbMVK8tND4cUP/Q+q2dgzTOygKIeJZsP0RAJ6DPWHnCH5nyZF1Kdj/wTivquw0x/BB28YVTJoSOvbPyotIvxsSzUv2ubgvBifcKFaZI55P9So7LhYvsVJfIasTMW7vzvsK66eifBjB870C5QtZOn2Kbx/5LcMSGKlDUJ4+o1Hm95DkkwrtnxyZQzcdHllusf9ZJKHHC2W6DPUsbve41zFz1Hfi4nOBETgQxT6a66woV2jKqAiTSOi930GRLM8YL8zv/8szHwqcnO00X6DJDN0C3XSwzi24JwKoHWPYPt/iGEWpc7Y87E3EJVxBYnAjVHsymWRMsRUZ13m1wckkIVO93+WMdTxmOQbJw3lOjvHo3KZ7wjYxAvPndVDMHv4+xb1JOmLnGq7QBr83J0Sj7B34lV3UNSEtfDDikcMMTxBN74vy5TDH+zvHndg7TPH+TjGha5rs22F+DIMBDaF9jDb2lCLE8flPpqv6RRqMp6AeJdklTIq9SYq75WbpFKdZlkfb54uNsyxiUOH38sBQU/o5skbvk/RBGiO81+etMHt/WrKEiyaD16B62BM+swrysT01LPJFUEoxXDNOZ2KcgtbF+lt+WpZFUJ68z4BkhSjrCLk2MK7TfBEJ01WTJLCqYvWLJtTYju3hLb89WYXez23HefVb9n6O6wgcJbe9FwmCqilhl+NsP6bbuCW/89mSyZtKaGRPWAuKjf0Sc+DYR6ljmbYdD31Aoi2v9scPfmCjadnorCib1T6ep1BDivqswezHHGTGwoyo62KdJrMl9HJw0JJk6X2i2z1hzPCDvrOyWPmzIVs77GZb4d8Num5YYHRz+0+YGHLgeYRw4Wt7cQw3fGMPBGmi51Wnzh6/tA+eRkI2PL0Tgm8KUUBUcfTpc7EvQT/UiHArHmYSj2DHT+8tHzXeRpYkPp79YMtxVWWjdv/eA6mzX8whCmC1Bun9vujEEf2i8j3XDxm+VjD7spc4AH3jiR1MeKLY/tkCEHzW+HGSkOcLO9+gmENZ14Llc+lGLdLnIo8Vc0+VJ6YlYSJ7xnmqxSE7Y8kXOKwOZNnLCIvsK3yqPkPM1Hvo5ZAPLqq/Q/pqqJViiDuXCZ27IQhvOjOYzRBAL+Ff3W/GhC+QqBuIzNKPTbaIiU9OljB5KJq9y3wySqzAVub0Ia/aYPgusjEGzvPqGjyfPkNqpD5oEJI+pDZs45kwb9HokR7+S3OfEtYIg/NwQYgoouckDhYZh5QucVegw6tHSVPilOYX4AUTZL9eXeRH5756RAwRnsHa+sMlftR7wg4nL0kG2aS8XjOA0VzAMi3U7wtbHhHVICPYgqLoSLSZxft6k8VkRggvOs/TOgWZ5+ONbO2tvzhHMgqk/yJpq0jbe7LepDHNFtMn0OPmBFkTRZMvgpLmKUKF43Torz4dIpkk1JdN4vTsL5z3wEc/G3o6ViffR+uk93UYh90xI+ujNST3ZK1e9Pec34VsRQOW99NbnfYWY1lUFdLNs8MMxplBh41uXyIU0Eg+w0eYhSmAV8uirG3fKrcLf0LgMR8/P193p0qxz5TGjKmwSn+GKCTJeOdFJMSpsCxhGQvf/9/elT3HbTP5fyWVx32Id9/9bZUsH3GVneiT5Lh2X1j0DCSxQg2nSI6P/euXN3F0NxocgMc4L4k1aKCvH5o4G4tkNeII9javXz2ts5udM31q09v9Hhe2K9OeLo/ciTal1mVEniBbyJu4Qyytk/8svlxh2oDmfPSF2L890MU6+sP6WB526Wkv3h/ukqrZeJjRTdtbuomLoj5oVhWUZy8j94150fM+Kc0EiJNa8nYC5KMo4+5gnp8nxOsGz3t4AW7Tn+08HjO5FdUoK78qPif7R3HWXmbbwv9mBz86ts19zuNjN2pAZOO3VT+/qvhgupavsjrEntkv8yTLz0z33ahTP/robnF21K8+Ks3JqHM+wH0bkfklAOl7XgC9/cuhMHObYEhVz1s6luV3nOOMVUMkKe41PGMBxNWZHaut+lIW302CsWYITzbHWqrJTfVpecr217Wq0zqn2hC7i7YMJ3kV4OhmWaOBM+9YjKo4dtahZpiE4u1yR78OM8G3fRNcr3asJnlV4TVtYedcT8riu0kw1gzhyRuRPydFfbikzVwkp1ma4la9PXanlfhO8jHI2DEqmk14uaI16DQNel31kP34XK+7duazvb2CHr1+73Zf+ZuzXmDHx0gUecVsWi+ed0iDCtDLP0mGtnJAj96Kr4n49rtIjw+n9DDxA+xh6Ytz6sWLO1qFJzukr36WKJ/jorN4gAPDr9Lssb9Qe6m+7PRjXdNnrdNVNrvJigkHKMeaITppfR/kZ/Glt4VD3+ConfC+FHreFeZ1nrZmkAiepelfWb31t2im0Bkid6Xo1aH4NmU3Xa4b/DN6uR5Y9nkJf7Gh9ZO30FCn/xofs5r0tl47DvgfUTSnpT009Ufm2BKG9auiyHZJ49mOw79P4iT27U3isox3T3Wcjd4mxkzvzWH/Sz0bqd+J+nZIszEDzp1IH34bf/x4SsvkmCa7iv+/fv3P3377r1/1LvPnoT1v/Ut9cLu+uHIdF7t4b5qjUmOPiQFKrsiEUKgC/ofBtDkbU2/Pxul1dijKPK4Mbvb65LBLjnGqG0UjZAaIWtehSb3ktTjWu12HktScw7j2K8x84KE5wGaMly8kTLGhFslX11GgKUSyY9UC1Z/h0YZhLBCycFMFRxeLnZ6FYGlsSWEMVeU8x/4UAc0VBovFNKnmYvDr19LaJ36j7v8o5vpy2bfDb7NgTRVYFUQrCoIu0EJhkKXpw+EoPdW8CjiBe96GMyEv/ixwgiy0HjiNU61F4DQczfAYl5pxve67saXh0I3c1Pjj1mMKfKYIc//C0WQ4UHJ1rExeH7/rxE9EEd1lp3xHzPW6uupcb/hxluBiHOSCZAmMrUHlGcBFH1xDeNKHmxaH2X2c18c0vYSLC4aZk8uXgBlxwHExmHWf103FMmjIZpRdTCRzGZ6tMZANCLOEseUH4svja8ah+BR4kQdWZkVX/48urVu93qGf1Fhn7JIEBsVRyi8mhslaucSxpRcRosrzyVeR/7ivb8+ivpWJFKcqBU5zQIdo6AsXqKphsOH0XZNkWxoTwygOuRuzroG5JjSEpwsalusauYzKV4OrzaxYLgiq+dcs3VatFl60rHOIP8TNylk+48KlzFZpTi3Y+gKmos0WFjE7OCqowJU416Fhgw4qElgeBGrO/vcTgZzZyhVWhb3NfOFWALb5v3Tu4W3hr512MfRWVK0ldQaN9qYmY6dGbUHxtF40C/zga764WANFEAhqJpgBiYxrzghjzr3mlWDStq0DeB3x80+BQhcULAY//IL8XLgrq3lKJdfXSlURgaKvC2OyvCq21JLNY0pRxwFLi0Gpvx3vEUSW+aOWe7Jpafht6wAAEzqs3PUEaM/vw6sDg3sXnRESinDLnYbrb/gPjw/ahtZ9DTU8DD/O8pHRc6pAogSOM73CM6CKzCCDsKSzTCyELMsA2SFAXCyuXHy8AKzwzEJzo+pVkqbViB7OqjQJU7ZDvC5xb7toYPFSjb88GrrkkhGsxEo/XlpGTEWUoexiPl69Ri4fr8Vw1b5EclfmTe6BohkQRf3zMyi4BgLZo+OPs4ALklyVByQIAjPYYGFgBqrF4Ss/KrQI1voEK9vAVy+tIsP44+ZxNKiyCez0+Ve2gZ1eWkWG8cfNY2dQZRPYqRPHbAM3taTqZm3zw+bx0qixGay0SYYiU2hXTwVESSujIUH/c5j9fK4XPaClU4TDrSZfDi96dlg5g6d1hVGvrfrTKJwHXVTuX0o+lTAMAnWTzIFGdi5kDJ+s5McrAKttVx8DAuX4nxCkzgBZGqCWvMtzr3c6RdBVLXPZOsiM4Jt1wWsq4FYHtg1FwDWCbq6Idzbwlo10b77Xj8HF6dWpfKpb3DWF2id51RGP0kDN2EcSbj4Ckuq5AHIxLL7N8tNzsyjrG3j4/uLAU2lJ+nXzuBh12Q4Imtcy50ZB+0SnAYPu58vAAfAM6aqBEDX/fZdnpyOKAonEcF738ywfooahKUIg6GCGCQgeFqNRrjWEEEBud4+FxMsCQYfvy3kjTkO+gsEHJvY5rgsJodlHL45+nXX80oi1GIiaxwgCHJKjxi7Ki+VNI90vWz8b16rBYaQafGHvb2LSPC9o5hzm8lGz+Ai3T8/0qYgfxe9JJU3+I4LzSq00DZcsOSiPSnAxibgUtTh8F8/EBWIN6CvuMeIngBg/piyFr0bCZT98zYm4NSNqkNKUIOBRuNmwMyqyJcCsP4fJMrCZMV+JG3CWzlLyLnkor+N8H92c6gfWCrH/nJRPiA7T3Wi5ltRLoTQ2/hgukrD9dCYoBl1YmABdsThElLEOrNBUlwaKNZDkoDwzDHqcEOAJbc7jnr7iurD2Se4KKx0NrQlqs42RJuNM8eiyw6Y/slKsf5xdS2lK0P66bQyNiqx/nH0rvlVov8mqBoo+OG1ifRIQXBEHLN/82iWk1SZWMiGcef0IWobj64HLbGFoKlYUtyyb2K95H3XNX7JeSCNZX8B3JWYD0KDH+j9jvajNihEs91SvBUaOseCgFgTLM8p2rCcQsZclhgrLbtL2qSnnS2Tzzzb9r5rRFxytlKf8UD+mIwKcNQ40HpZE1oY2SskFjIFlfTYx+h0u8MwfVf5Jj4WGlyXHtlkjyHWct0OWV6fDPhXk9kS3rSJRAjtecqkTTHSJtCGJXhhyRwuzRRgAGbpxeI4irgdCm/hKrQFmc36rJqFr8c+Vga3178GvAVgz7shPwtXSG/Md/7uj2CUP3e3Dq7LMky+nUmwAY6T8kFwY5dbRR6u3BSjCov95rP+MOHrJoceOBx4QQoVGQleGlD1hmJWo6RA6N4JSyrImE2ADKw2ufG1DA+enjM1ng23xgE1psDTm+6HDffxoz2G2llHEKDMki1x6IaMFSSWHEcKC6ccAcFky7tA+BZz5M0CM6/AlUVaRLw60v+I8iQ/lEFuvs+cvyaEhjPAn4+X7MDKRcg9GKXBaFrTLBkGLJA9zxQs1UFC0UYpypJDFXjP+NjM3Xz1Q5//unovRpafqDHj++1SVVF76dEhwjCpEMhbUgosMj7iBVg09Wey14W+7MZEDyYuNflsOeT3k7EuSBiHg8rmXIVeLxlmXHT3AcjVrjboSf8XpaQAprWEYXMyL3EZdjpgdYUgMT8JTGCi32jrgWW9gaVgvPpzkDBm2PTDc3Ogvuoufj6l4nX07pFmMXxEeCJT1l+HHlaMBVm95JKjGXwwL94nIK/12QsnDbj1v1RBx8683mAjyFRukV2SRfr2IZOujPhxmsnQrgNX6p5TLgGjGaaMbflYyURyPo3YHidcPJENyt9PM2wOWqRKHqeLOFcHMK8CsQ6B/UEIzXR0+hrnc2yQt63y3wa83zBWmNM1oIQ3iy7k2YdWVH92WvUZRZnk98Eqe4/zHm++7p/jwKG7jUlyf8orF7gcxvO8I1KF9/yM/yDUiqOfp2l8CDcghvcKApNWDF8FQB6wDGs0f/2BiAUwoll/uNfh49/f7QyXL7u+705dilyftWfFN3LpChFffisdoNn8HC9PMZYFgdbhb/wxvfaCbcRx/DuaWHs1f7VLgwV2/y5yBMDeIrl6cH3+9iDXOUZ/NrHFePTwkafWLiGZ682RgqDY0/rr1xAejKhxmSydUaZ7oCTpwwqFg8DafQrqwEY+p0ybGOpV+XytAf6yAGj+K6G2ePc+HEpW5Nm5SizaPD00hDkfZGWsByH32DzxWAo/RFQsOW8vka2XyD9ljJP27dhQ+2NDo1FGjVjbTCHbgikkTauxC2SwM0GSlWKMYVcRVQG0TC0DLoWrOuOUKp8VD1vWT2P2dnfQTtMbPOLYMSgVkZuksaIPVokULeUbWYs8wYEQ0ZOFSr7pgHszdKa80fryJfzS5mN8fkrpdTymZ8eGWzlhLaKkXbjvPsqEPi6fkidXgo8/sTmvky8/BMqmCSpGihU0ZPw0gfkHpkkFer7sYPj9kuzhN/k/sexmjD/Hh8WTMaiTvDwSyu8cfZ0GgIbYqjFkaBHWwqcKgzdSJw7SXcEF8+Z4AhDv/rY/9L2PMv5mxfrerdyu+JuLb7yI9PpzSQ52EuHbJ2Hiz6jlgqvq9rIdd91mE1bfCrmsDRN9Q5nwk05QD2GkGiBg3YM7CXK/RLMtkHI848V4Sh0qB7VxBR4X6fDFE+T0qABljhWBSKi93VCXNHq+z52aeyAxqUpW545jMWjmSIv9+QdEKNbUTu5lhVP/7JtNfedC82BDoLmx/nOegkzuSPEUp2DzrwE8v22I4+kN8KxzDkVRl7nAks5abVH6/oHCEmtqJ3cwwqv9NXmIZCHQXznhlZQKSPIUj2DzrwE8v23Lj7ixN/8rK5PDYHa7kzvu0erPP93T+yqjcKLygEEVb3onnEviqf7g6FN+IlSmJRPdq//M8t+zOwZivGR9irhVhaxRxHasHk5auFl2v4i9YbDtyeVq9mB1Q67/VMhFO27vB4o6hee+tvGlyQ9Wdqqoh8n6ZP9uLt0lelK/jMv4SF+Y+X13rTpRAjqlff2lL0Vxed7sn8Rz/69f9l6zyfPwlNRowopDOTZzE/s1znKRXZRnvntrBOMAWpIP5g6QWQcaMVAbvsQhiN5byVaUVtKplZdUqv9tlJ9CYajHETKWwcOu2tD+K8imD7KeVQ/w0EhtDa1YBUwZrFVAsay1XSTmiMWXhMsdZkozsPSYpMICNRWCPGUotHK7jUjw2Z0gMDmMRxGEstZko2ZWn+hK5aaK+BDRRX8jzAMFFIyD8EXVE0ce4eeeO7Z/6WeAkF0hgBakorymENhGUZxZM3koxyFSh4FmbQI1BQdm7p2IbvKv4MT6cHuLGV0QkUqkoOWRKtiwWIezcVQpbR9UnI2Z/1SnAbqsTMdkS/GhGVg7DNUuDwVACtT8U2hSoOxQc3/oSUPy+0NJ8/7gz+mXWCSBmOo2NZ1l1mwq6X6tBNdTntXKQo0piYThubRm8xiKIzVhq4TAedTM4jEUQh7HUxqE+6yXuyrxZ/Sja97oAbiAZyBmktEgxrsobnMciiFtdao+O0hKaGRSlQjAWSuUMPggHvG12qGlv7qPhpi2mQk5LYdNB5M9JUVTzu36Bz9THIAF1M6hsM4fv1bD2EKdXp/KpnoO2r9uhUtDk4MyCrGGRrrnFi3R2qQzi2xQXEavDN7T32THZYXy6QoJRR8HhhDEh2+8oOO2/y7PTEWPSFRKcOgoLp+4+g8Gk+x1qvytiDmM/1Tcvh0Pl6DhWJaMGsiqlRYp3yUM1bMz3FilgMkgKmJIpBcGZ5sZzI/IRkMpQd7Impg3lH1kJRVKpDGXSFluY3IpvlcI3WXIoC9xhIBXEGCRkjL+QaddYhI25WBOsnhDxmFpMcWL57VZU04FDPQEUYAjWymEzKiRW9fRX6gEVdRJYTZ2KN5fDnsTFZnUYPTW/Q94n5s706JeGTXuR5KDtyBqTpGPL5SIR16fN856YA5tCwltNOY8P+aYZxp+sRMhF1psmL19Iy7qV9kaP63oKQyCeJM5IQV6t4ZqlI3fwWlfDIp2UW94QRSqD+ErF1nl8Bq5Ydr/D8/aMsSY65v4EZlF9ETyD6ks5og8ggzUYilFFuCi9r+qLxpFfwKmhVg56RSWxLXhgGQLN9Q+MElwOwYgdVhrvxfMxjcGeCpPZVh5HSuZWASGBSUJtHbA59x8GnLFBQX1fuGzfiYOoOjIVHE0ScGpgUNnWRMfcgeaq6FgGrouOxRYmn3KciVQGMZGKbYGiqr0/peI+Lv6GAoVSDAYKhcJmtzHTnmm3sQy021hs640iLqqO81kkj0/QEF0rB/ufSsJj+DqpZhEFHIZMEoKtRMVZ27BEQICGWOlwint6jiug26sE1KqURmrtgVKOGqAPSqVwL5QI+KzalE8Uu5bCwrIlsobz52OcPEIuHYvg8N2XWnHb2Jr6YukUMGo1IsYa+wdRliK3IBcjxNbfIVqbkc1kOKa1TRrQ7CaZK3NscI0RssTgDajR1BXACgdCCa91IMSO4jDk4AnA24dS8ygYrAEadAdKJbOqXfegPX5qRSeAVVZpbCjMs6KoGk9xriYJiDyDympqKHajMZsTq6t/lciUpy8BBzF9oW2iE38njlYopeAkRyawsUL2XohtF95+S7/2TyhiklCbCNHV8ZgmYn+fdfSJsG+F9pVx0BkUPBk6crsE6qY+fgwBobOfE7gVRRWCm1PFjGMQ3d4oftrCoKC2WDsihiP07VHLLi9Nztl/jWpS9gLYcB6Gt/nM34RW0ti7rsdxllGZq6nRSOcqBZD2ABMGICVkAqgZJ1CGa4TmIoxcip1DGQgYw0eclVKKDRS5rMxLRqZ9DRLsfIVK5eJhm1sZvjQYSufnLSfRo7dJ3ZekCtR59JZaP+g/3pTQz5hXzPHz40pN8ph80wzv1PsLVXU3s0TqqXDSKCotrhh0Ur3RhzqBjpkGM4hfM0j+VnhwQSJX8qrWShCjnmmOhrPNpn0QSlwp7SB1owZyFhu6ogRW14q8q9+P4OzqwzeZoLtepvyrUX84NU343aDx73F9gN9UROcf7mpS4//orjtvaWrOqUZ8P7QbF+33A7lNAdYEzWIWhjXPfZw/CrBHcKp58vnKzNPP3Jywo1cKiRwo9hhlIQ3jghq9kv+QupBZgEs4BWkSsEJInAC3jZRGqOtDkz+2kXpPCP3UqnSEMsC9pVYL4j7SVFxNV3v4jo6hCVXdpA0RSJErWLL6HsOorpp9rGWQBhtszWQC5XIYMeQC6fwPu6BrZ01l8jrZZM9rV+NkFigE8Dqe1YJQgTYElgcxk72TgOTBOsrMJlGXqaN+nVp0i9v08J1fGVcbvpTXaExftSNa0RbwgcawK4NBDIgP1fiVia8ToCyi3lpMJl9ojIZ2AfOAhAFMAV3SbE1A3r10V73bi6GU1kn8q6vtKzX1sCu601XUTIcrqhJ699KMqg/7ScOWGxE8cWLC49oN79blyO1tsCZkAKMshCHwIIgTe/HjGszwKknTKpAOLRNG0EjDmYCFoTOU7tO7jU3jWhu0IbuAlpVBaQBLteBuBujK+fi2DGALugKuln4dv9EHu2qv1iSu2betcK7Nu5umzzRAmsMkCmECPVtCUxPNhOCuap8ogFTVJAqhqp7OoKk5/nj+5CpLU1JNlSCEinKug3bOBOUymKZam24haluElZNJzhTSqKGmihjqIUkgJqion8yRj8RQwxheRUI5JLdDq6IlXwPZEnRMCGyVzEXh3ZT4QIhX0f6BMxSmFFylCcGDWazBtCP6PIwmbEafxzSM4fXsaFrKRFROFXI4yqsYAk2cPDNNa075YtxNN2SNIe0EUIUwipHepqkq/epH3eaotF1fjSyYwsph71Fj+ED3RJUjOdcNoq9MYxFZyagzigxmyjFrmxX9erZrknRrS3O2oMt4s+mJcrNUj5XoPIo+c79t0t4wVlVAOv9LKkqyp6YWnMtpqqJUbNIoQoSlcOpBuaei8XwFfpADrkCcYDj7IAeU6Upphcxc5ck0nd2ZdgGfJ3f36yrMMWTTwm2gk/hV3MgENtbztIAyyk/sSZtE/neiw6va51+Lbqr52VNciP3npHySOJiK26p4U0epq2eYa6qi2eOmG0Lpt2P7uBngCp4UAWuiPZ6Vus+TaT7JfmfbR63lNy4sZZ4h858lIEokAQKinLVwrAcmJHRXEcgxSA6FSPoQAyMip2LTCCdJoh+zWPuFvZJfdCxjmj59I94nNAq/SusJLu/6UzSejvfK2SmjsV1cS5XQk+BgTeP7SmbSnDoR6k8kWad8OuGW5nxKTlBLwAMpw4Q6INdp15OpFKZnLHAznI3S+vd36GMTekrWSH7ZBurhOLl1RmC+vyPPDfAndbSeDyej7Xq/JcGsBwNRPQMnDtE5FjcFMWFEaf3PG+c2A5lkmHGw21LRv4FYWZTl1piZfScgiEhijGRgBsE1oRkCP3a78AzCaFVNCE20jSR29o1WmrkrhMnWZjbVSnuAmXKFPOWCUwcLE2ZiGbkFuTSIMYhzKji1VSlEG0CNdZiESCTOvWXJboJYdfd1AZORhF22qEsa9SAmtn/EObWDddGtmVN9i3aSTZUmcANBz+Y2pqCew92qiSfAdH5scqwV3kTkcJJd16o8qTVv3LhKMzYJXCOULdukdDuzGIXTrpIEl2ocTmw7fSDEDJSzR8OwCRSiu/j5mIoxBRuuuEZJDF0mJ3kLq/Lw9IdyLhZSGaG0LyRNPzlrPGnSVMdfKzlHfeLbZRL5/1qFV9VY1uzWSu0fbbRKsI/21JVZL2ZxMshFm0J7196yFu9SfZ61eZtEdNMGsYcb/1mT0S95jvMfb77vnuLDo7iNq+H58IgRsLhorUSFYPVVpS78wi8mqWtg8kNP7WIX+I7TmUZo/mBrr1JvS23kISVy+8ZaJ8QujuUhqaYh7qtQ/sxEBGVbFf+heVkTDc8lWYdsCGXIIZvxDFS7s4y+8DRB/f7Vo4jYETeJ/G+FGy83tTXRR5kmXmtgRwuCOkScQB9auhuuOXgGvvoeUvQ2z54pe1DkIQwCP/3UBRXyJaezTXGfORhCIt66GaQHpCLjMSkoHuLkVEgDH7vqAhv5ghXWDtaGZ5NQoADpQuBhHrXhN54i4AUq4PgUty5hHuzRrNZOtnew6LbM5T/mi1ZTDt2pb0NF7w9JmcQpcaCWqoArOe0wLfwC1l17/s7yqtX5xugPDJus7HZB6wZSl2wJPonMe5ZsYgog+c0ve/4fmBpX8MzkP9DbZm0T1ufKppiDDszBA7IeiP0E4G7eZLyTE9XfxevsUJR5nNSPoVTDsEG/PuHVfRZh9YmVJv+87OaekDULmnKi7xTJc1D7m0Me3aQUOBldrclUHdd31eaTXmXigk15yAlYvTivxfCIBd6xahc4iAeqzjPsmHiMNtdARwvPTmy2gNrS01tc7yuvdZkGOrPF8HgCHitr2qBeITvPsGN2N9pcAx0tPDt73AJq66+qsb+IxmtswAfAV9szfGWRV+raz4Pt5TkPZpeT0jEsKZHTSjnlv1vWJPJHeNLAzDoImdbqzGM8/kDnbCPbN641wmCb1h5Vf/mibaQ2fOVlkQ9lL1+0bzx2P1R/VhPWar73MduLtGh+ffni9lTVfhbtX69FkTyOTbys2jyI5jXasdGe5v3hIetnfZpEPUlf3D+6Icp4H5fxVV4m9dMEVfGuGnU2r5c26zP184VfxP794c9TeTyVlcri+UuqzClfvqD5v3xhyPyyvW9Q+FChEjOpVBB/Hl6dknQ/yP02TgttHo01cV1Z/52ofm99WXXNUjz+GFr6IzswG+rM91ocxWFfdbl78XysX/Eu/jzcxV/FFNk+FeKDeIx3P27qJOnNchPWiN0Rqtlfvk7ixzx+Lro2xvrVnxWG98/f//v/AfZ5zWn5QwgA - - - dbo - - \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201507210952098_PaymentMethodDescription.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201507210952098_PaymentMethodDescription.Designer.cs deleted file mode 100644 index 4412803c98..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507210952098_PaymentMethodDescription.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class PaymentMethodDescription : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(PaymentMethodDescription)); - - string IMigrationMetadata.Id - { - get { return "201507210952098_PaymentMethodDescription"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507210952098_PaymentMethodDescription.cs b/src/Libraries/SmartStore.Data/Migrations/201507210952098_PaymentMethodDescription.cs deleted file mode 100644 index b71dd98ac0..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507210952098_PaymentMethodDescription.cs +++ /dev/null @@ -1,66 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class PaymentMethodDescription : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.PaymentMethod", "FullDescription", c => c.String(maxLength: 4000)); - } - - public override void Down() - { - DropColumn("dbo.PaymentMethod", "FullDescription"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Configuration.Payment.Methods.ShortDescription", - "Short description", - "Kurzbeschreibung", - "Specifies a short description of the payment method.", - "Legt eine Kurzbeschreibung der Zahlungsmethode fest."); - - builder.AddOrUpdate("Admin.Configuration.Payment.Methods.FullDescription", - "Full description", - "Langtext", - "Specifies a full description of the payment method. It appears in the payment list in checkout.", - "Legt eine vollstndige Beschreibung der Zahlungsmethode fest. Sie erscheint in der Zahlungsliste im Kassenbereich."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.PriceDisplayType", - "Price display", - "Preisanzeige", - "Specifies whether or what type of price to be displayed in product lists.", - "Legt fest, ob bzw. welcher Typ von Preis in Produktlisten angezeigt werden soll."); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.PriceDisplayType.LowestPrice", - "Minimum feasible price", - "Minimal realisierbarer Preis"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.PriceDisplayType.PreSelectedPrice", - "Price preselected on detail page", - "Auf der Detailseite vorgewhlter Preis"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.PriceDisplayType.PriceWithoutDiscountsAndAttributes", - "Price without discounts and attributes", - "Preis ohne Rabatte und Attribute"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.PriceDisplayType.Hide", - "No price indication", - "Keine Preisanzeige"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507210952098_PaymentMethodDescription.resx b/src/Libraries/SmartStore.Data/Migrations/201507210952098_PaymentMethodDescription.resx deleted file mode 100644 index 04f8e677a2..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507210952098_PaymentMethodDescription.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201507242008201_WebScheduler.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201507242008201_WebScheduler.Designer.cs deleted file mode 100644 index d9e3e0bca9..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507242008201_WebScheduler.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class WebScheduler : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(WebScheduler)); - - string IMigrationMetadata.Id - { - get { return "201507242008201_WebScheduler"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507242008201_WebScheduler.cs b/src/Libraries/SmartStore.Data/Migrations/201507242008201_WebScheduler.cs deleted file mode 100644 index d9a437cb3c..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507242008201_WebScheduler.cs +++ /dev/null @@ -1,97 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class WebScheduler : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.ScheduleTask", "Alias", c => c.String(maxLength: 500)); - AddColumn("dbo.ScheduleTask", "NextRunUtc", c => c.DateTime()); - AddColumn("dbo.ScheduleTask", "IsHidden", c => c.Boolean(nullable: false)); - AddColumn("dbo.ScheduleTask", "ProgressPercent", c => c.Int()); - AddColumn("dbo.ScheduleTask", "ProgressMessage", c => c.String(maxLength: 1000)); - AlterColumn("dbo.ScheduleTask", "Name", c => c.String(nullable: false, maxLength: 500)); - AlterColumn("dbo.ScheduleTask", "Type", c => c.String(nullable: false, maxLength: 800)); - CreateIndex("dbo.ScheduleTask", "Type"); - CreateIndex("dbo.ScheduleTask", new[] { "NextRunUtc", "Enabled" }, name: "IX_NextRun_Enabled"); - CreateIndex("dbo.ScheduleTask", new[] { "LastStartUtc", "LastEndUtc" }, name: "IX_LastStart_LastEnd"); - } - - public override void Down() - { - DropIndex("dbo.ScheduleTask", "IX_LastStart_LastEnd"); - DropIndex("dbo.ScheduleTask", "IX_NextRun_Enabled"); - DropIndex("dbo.ScheduleTask", new[] { "Type" }); - //AlterColumn("dbo.ScheduleTask", "Type", c => c.String(nullable: false)); - //AlterColumn("dbo.ScheduleTask", "Name", c => c.String(nullable: false)); - DropColumn("dbo.ScheduleTask", "ProgressMessage"); - DropColumn("dbo.ScheduleTask", "ProgressPercent"); - DropColumn("dbo.ScheduleTask", "IsHidden"); - DropColumn("dbo.ScheduleTask", "NextRunUtc"); - DropColumn("dbo.ScheduleTask", "Alias"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.Delete("Admin.System.ScheduleTasks.RestartApplication"); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.NextRun", - "Next Run in", - "Nchste Ausfhrung in"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.LastStart", - "Last Run", - "Letzte Ausfhrung"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.AbnormalAbort", - "Abnormally aborted due to application shutdown", - "Abbruch erzwungen durch Herunterfahren der Anwendung"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.UpdateLocked", - "The task can not be edited while it is running.", - "Die Aufgabe kann nicht bearbeitet werden, whrend sie ausgefhrt wird."); - builder.AddOrUpdate("Admin.System.ScheduleTasks.CancellationRequested", - "Cancellation request has been submitted.", - "Abbruchanforderung wurde bermittelt."); - - builder.AddOrUpdate("Common.Waiting", - "Waiting", - "Wartend"); - - builder.AddOrUpdate("Time.Year", "Year", "Jahr"); - builder.AddOrUpdate("Time.Years", "Years", "Jahre"); - builder.AddOrUpdate("Time.Month", "Month", "Monat"); - builder.AddOrUpdate("Time.Months", "Months", "Monate"); - builder.AddOrUpdate("Time.Week", "Week", "Woche"); - builder.AddOrUpdate("Time.Weeks", "Weeks", "Wochen"); - builder.AddOrUpdate("Time.Day", "Day", "Tag"); - builder.AddOrUpdate("Time.Days", "Days", "Tage"); - builder.AddOrUpdate("Time.Hour", "Hour", "Stunde"); - builder.AddOrUpdate("Time.Hours", "Hours", "Stunden"); - builder.AddOrUpdate("Time.Minute", "Minute", "Minute"); - builder.AddOrUpdate("Time.Minutes", "Minutes", "Minuten"); - builder.AddOrUpdate("Time.Second", "Second", "Sekunde"); - builder.AddOrUpdate("Time.Seconds", "Seconds", "Sekunden"); - - builder.AddOrUpdate("Time.DayAbbr", "d", "Tg."); - builder.AddOrUpdate("Time.DaysAbbr", "d", "Tg."); - builder.AddOrUpdate("Time.HourAbbr", "h", "Std."); - builder.AddOrUpdate("Time.HoursAbbr", "h", "Std."); - builder.AddOrUpdate("Time.MinuteAbbr", "min", "Min."); - builder.AddOrUpdate("Time.MinutesAbbr", "min", "Min."); - builder.AddOrUpdate("Time.SecondAbbr", "sec", "Sek."); - builder.AddOrUpdate("Time.SecondsAbbr", "sec", "Sek."); - - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507242008201_WebScheduler.resx b/src/Libraries/SmartStore.Data/Migrations/201507242008201_WebScheduler.resx deleted file mode 100644 index 7795c02827..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507242008201_WebScheduler.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201507250039446_Merge2.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201507250039446_Merge2.Designer.cs deleted file mode 100644 index 8830d1f8ac..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507250039446_Merge2.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class Merge2 : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(Merge2)); - - string IMigrationMetadata.Id - { - get { return "201507250039446_Merge2"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507250039446_Merge2.cs b/src/Libraries/SmartStore.Data/Migrations/201507250039446_Merge2.cs deleted file mode 100644 index a26a4dfe70..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507250039446_Merge2.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class Merge2 : DbMigration - { - public override void Up() - { - } - - public override void Down() - { - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201507250039446_Merge2.resx b/src/Libraries/SmartStore.Data/Migrations/201507250039446_Merge2.resx deleted file mode 100644 index 483dbdf042..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201507250039446_Merge2.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201508042207146_RemoveKeepAlive.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201508042207146_RemoveKeepAlive.Designer.cs deleted file mode 100644 index fbd14a355a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508042207146_RemoveKeepAlive.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class RemoveKeepAlive : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(RemoveKeepAlive)); - - string IMigrationMetadata.Id - { - get { return "201508042207146_RemoveKeepAlive"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201508042207146_RemoveKeepAlive.cs b/src/Libraries/SmartStore.Data/Migrations/201508042207146_RemoveKeepAlive.cs deleted file mode 100644 index 8c0dc62ad6..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508042207146_RemoveKeepAlive.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using System.Web.Hosting; - using Core.Data; - using SmartStore.Data.Setup; - - public partial class RemoveKeepAlive : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) - { - Sql("DELETE FROM [dbo].[ScheduleTask] WHERE [Type] = 'SmartStore.Services.Common.KeepAliveTask, SmartStore.Services'"); - } - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.System.ScheduleTasks", - "Scheduled Tasks", - "Geplante Aufgaben"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201508042207146_RemoveKeepAlive.resx b/src/Libraries/SmartStore.Data/Migrations/201508042207146_RemoveKeepAlive.resx deleted file mode 100644 index 483dbdf042..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508042207146_RemoveKeepAlive.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - H4sIAAAAAAAEAOy92XIcObIo+D5m8w8yPd0ZO1cqqbvM+rZVzTWSIiXa0cImKemc80IDI8FkNCMjsmLh0mP3y+ZhPml+YQDEktj3iMjMyhcpGXA4AIfD4e5wOP6//+f//e1/Pq+yV4+wrNIi//31uze/vH4F86RYpPny99dNffff//b6f/5f//v/9tvpYvX86kcP9xcMh2rm1e+v7+t6/fe3b6vkHq5A9WaVJmVRFXf1m6RYvQWL4u37X375H2/fvXsLEYrXCNerV79dNnmdriD5A/15UuQJXNcNyL4UC5hV3XdUckWwvvoKVrBagwT+/vpqBcr6qi5K+OYDqMHrV0dZClA3rmB29/oVyPOiBjXq5N+/V/CqLot8ebVGH0B2/bKGCO4OZBXsOv/3DbjtOH55j8fxdlOxR5U0VV2sHBG++0tHmLd8dS/yvh4Ih0h3ikhcv+BRE/L9/vofDSCfvudp/foV3+DfT7ISAzMUPiFkRmBp/uZDWsIEfX15QyP6t1cc+L8NXPL+zfs3v7z55d9enTRZ3ZTw9xw2dQmyf3t10dxmafLv8OW6eID573mTZXTXUedRGfMBfbooizUs65dLeNcN6Hzx+tVbtt5bvuJQjarTDvM8r//y/vWrr6hxcJvBgTMokpBRfYQ5LEENFxegrmGZYxyQjF9onWsL/9u3hlgRLanXr76A588wX9b3v7/+Fa2hs/QZLvoPXQcQWdECRHXqsoGSDuob/QCrpEzXLQ+Ftm1oKq3WGXj5XCQgizBQq8a+lQtYmqZQj+m8+gDvAOLJHs1xUWQQ5AZEv73drCjDOoMNXJyiRZMd1TVI7leIXTwW3BdYVWAJqzdShIeFp26LIpi5WT0q3BM0CZjHmSWFdps33IwIoI5NoWHea1bRu19+ibGOztIMCkQxLD2yz3ZrJc1B+cJ0DP2U9ytIOr63HK0jkb8gtQO3OUHLX8FjuiSsIJmC168uYUYKq/t03TGUdKHftOBnZbG6LDKVfCFQN1dFUyZ4bIUR9BqUS1jbd5pC5dB3ppZpCNRXy5HQNWQDspbZH4qnPCvAwktML1LwpkdwEMsaOdLR6GOTDq22vx0XMVLte1zfy8xtG1d3i0IlkQpKKeemxXRt9WI0tlBFllSNJsMg4eKMBYuRXC/E4zR0+ozGVOkV2jgtnVdf4VMoP51X1yVA/SVaXxiq7+sFXmnf8u910uNCuzG8TlemDchHXY2kpB5koLqtizItSgIXopXinVRrcI2iO+FWjTZtDB31uph+cNfFJEO7hOvsJcr4rNqZZEwnJ6M3cZwko7dx1dz+Eyb1+CbYcbF4GX0rOymh79YhIQ3WKcoUVoHGNEKj6JBBAyB6f5IUTV4HW/QwX3wBeQOy7MV5f1aaRxujpJrCPOqNHXvzqDeobAdEk9w0ohsWWDoQBkZn3rGAQVYd2y1/3YbGc1BuDMtU51n59ddRXN6ta9jkULJsXN/Wp6LS7RAjjfCiKOswqYdM5tJgrI3Vd1BVT0W5mL7l0xwDX1XBHgLsb2iPDNCuStYCqjXS6cEFeMGy+wus7wsfd1RXv3rDIDpILR2DUoS6eqlqaLJy/mqrB7oy7HOSNQu4OCGTDku8N54vqtE136FdvMGVL/om38dRgvs2r5BGsUZttOSfYrTdKEkH8AogPrPnYL3yS5qnq2ZFDiiPVq3K0Wm5MElXAEmhixL96iIt/vb61RU+N0XcZMQMnkfC3GK7hBWidVLHo8UZArc7iLZeSn7CtCwWTVIfN/kCrSS0sJFyXKa3TQ3P0qzGB8nO8vUE1CArlm9MqA8SV93WhmihjDYQPRqiHyBrgrGdV2hBIisJJog88YzMDd2kFpmJJW/o+hsjzaGaYLe51A0y5YSGYi7dw1pVt9URK3RFtMSOhKwPQAvDgszGJOZm2uO7gGiJ5DVYwlC7w6yGxnB73iPj0m6zjnZuiFhwEBSOhpTEJE8X8Pq+Wd3mlPfBF9kPNPG3WfDMEeFQ3XsI/9HC3qI6hAMOJg1eVHr3kLtSJ9jmePeqzxbptql3LdgN+IaroxkkC2revzl41yght2FYDcC+67pOuyoa4erFQanQiMeWRLi5UF3gApSozY9l0axRR+LoF90mcJ4v0sd04XdSNM5W7nrWNfnebm34x2nvaLFK85NitaKCbUZrrOdauEJbcrjJiSbn6Vv+qVjBiwi6Yo8nnsLwBdYACQ3su9c55eJoobixKRkHt3ed1tp7C3FGdpRlxdPg0YWPKXwKVnmP1uuyeISLSySN8+VVswqb6a9FHRljj+4abRgZN+jgPsZD2oV8XBdHSbDl8DldpTXuHNkbg6f46qEZf9GBvLkDCVZBSrSP1l+REbWRGqM1+xGx2OiNnFcf07v6BJTBJliPJ4a2cgn/aNISfqvvEcXb7SSYUzqcG+VHJ60jhTAdNVg3rtME60ZHiwXXh+AxnVebiPw40d2hM/c9z9oF3iMMHiOalXbBfbsTcHp6tDo0p8/rtCSK+gfwwuO0Q3GU1OkjQRGD7T+B6gogrQnGmlUWm+N9JtQbHA5xtCwhpBXHgPCADbJr+Dy+JnpeXcKkKVvsodKjQ3TykmSw7VSojKMxXsAyLYJX34CT7P0EceBaOa/wyXMbIhLMjufVGZr+/ig7WLtbLFK88kDWYzy5xy4Ne0e1+40F8Hz6jOya4LVwXp1WweREvTlBBtaywLEIgUYMyJFRdJ4/oqWJ0PVRBoFqY10kD9EOIbC5RjAePYIU1U0zCmug31jaU+8NK80jjvxz8dSOGu80CFvopCADIb17IQ6Bs6Ls+3gMkfUVeHQFkocCm9M4OUXwOTO2BjHG85aWyAIZjN5gjYIY/V0cSpxJajG28SdxMBICwEWHjAopH09ZRWsBd/C4eTlu6nrjXAhYWxj6Z1rdZ2lVx0HaLf4MIuZFcp3x3/jiPEHaOUGXJuEnWjSS6DvQt2wxbgOdYXJChe9Gb+NqjfCAzHEg9jivamSj96dsHrcoaFyn+SIAU+/OOs3xKV0U/urEFoMZjswUnWCbtE1kfFynsF2TwQIf7aGwqofDyJNidZvm3blcRCZEfe6DG6qj9TpLwzXmnzBd3o+3FFk7Jjr6n+liROyfxqXNsNOEypMBUZgwiXdiES/qAmbQJ3Jve0MuxAGmj7B8wZUdvSd09i/Hqseggt3uE8XsHvB9gaBqSthmNlOqk1HyUA1txo3EHtDiHyxqO9KSMAhyomXwQMVxr3dhIrDEkRexHB8MUkyN2Div7ovWnwJKd0+HOmqp3RKHLVIa/dKX3nTQ18XGV74Jf1FDCTFJGlDXECRaGuhCd25YQCFqhylXBeywQJ4BRpuoH8sYMf+QKWMsmCLEynFEncOLGMTqAfVuMU3oFA+iGoAA59lx+hBR23caUNN/GZhqDFJYz3FcpASPdggdjKb3HISq4zyYZ5+HQ3B1l1sQTY9ZAFWHOSjP/hIL9C5tE+TREcDq/suraMajr6Aan6GW53ivwVI7uBsGTpSnVLFSnNIwntL0ByhTpNTJ7Eht9zX1NBNkUUs1SzZVPaeKR+00cIfRug7Re1xsul8ND7KAIhPS5UouZIBc2ZA/G9Z0lgcVu8tCKDvMgbl2mfbhSLo7FMs4QygUeEGEkM2+fXbD4aaLR9bnTqkdfh3imtVtGaJ8R8pp2s9MjEiN7xW2lSLdX1LfiIru1OqbcrXKnU9+O5eZTxKj1kvmUbOLrqpOimaN72gvgmdGxCQ/fotz+kZmhsRHklUWyqMbTJh4rnEgJjP+ujCYYqLhTVfQ2fIbOGFb0gK7bk4SF8MYHgnzGATnhbVHokPVcb46r9bQpryGZBwyQPVQpNBBF4x6uzvghlGP4rAVq9sybMUjXbjB918itWxy4E93V+K4qBGfTnqtB7/pMvrO1C+jWBd6DhdmooysvdEXKwCw87Q5nlThm1RX6b9gWNvM1Z/qumgzgvC4vSNyOjzf2OCxALvDIgLoEuRLbcxWtKQEMe/GxY9f2OKbQ9t2Cr6XKQwEUpH0gD9S+PRFb1R5SuPYh48eJov5AFJi31h7YVsxLe395qSqB9r0mC8TNHkBICw7QN8Dz2dCuvoHxV0jvloSjfVChsUDQH8d5y4+LCxezPhLpMuPe/OOReyTW1GEyCGMJ7ecvPETJwzO8JwjB/EyVT4zlVkxnW7ktZV6rwbFMZpq0Tiu4LgxF6a+xkgTJPELRjldo/AdVrF23UR5DEJC9ssmg1ZZoOP4Va7WcPRTq+M0y3DUY5/eOfD2Y38pNQ66PvswMSCYxNdOHpsNmkHC69wUcSaPapROIj5lw/1smL1AsVvu/BWO83T6jAQV/aLoBDbyJv5hvBMkpXWsPW/y23uYKNyAh7xpRIfdRt2WYSMY5yHvkyIryk/wmSSknrz1nXjb29XOinD0ymE6rBp1W5EsrVgHQefVGQR4InoFYUa/tnKn2nCoxR2ADbD6EsDwQ2EUiYAjmHA+txZM/Y1ixV2I1wjCZQON7SAfRpcPNLn/FDKC5VbL+zZsJf2lG+YPxTpUVxhBfoTcHLLpfxRZEkmIHKTHNsd3TRpxQ3HCIURpu0KUDkFFOxhUdIiz2b04G69TLnZzF8+4ZOWCoiAFimFttFdpw+2MFk80HeEVzjzRtU+ibjoRQ54EzOvXU+jyJukdy4WNCTdJotY2nXoYUT7BbH3XZP8JK5L8NAqyr4UPrhAd3vUeukJvV9xWd+xoi6WjRQ6rCq1VU+oFoc4Nt54VI1LW0F+1V1cLOjvoV7WP+Ol1juHXwT5Rt9XT6GOTDq22vz2ySxuCxiK9Amx4vT1OKxbPgcdt6KwoVyA83qfDdgUyfYqpKNn1p3zVJmLy56O7uzRLI5ipJCdwsC0TSa8+r9pwkKOEeS7S++aATWxJHD76DKr6fH20WCBrZ4LrVDGtBtz1z8UyzT2vTOP6fWZpJQr/q8ItSRUKQy/6bygwKmJdKBVj1kUQV0WnC/kZ5l7XTR5W0lcWRN1hDs6117zNoet2B3Oz0WjEfvMwgsqlBHS9v0BHLhnIzYFKqM1AqInNgrnS+vQZKzQgO2rqe6zStKmPLmHS+gYlI9DVkE6DVQVhTuxquU4QUgOa1UVRKW7IDMXSgYilQq8lIF5dvC7WaaLpIylXd5ItlveSg3HtJnEwyXtIiqSdY0uEfnHFrl26hMjGyHGEFVRNMAMi7aIcQuiqAsy9y0+gXFwUaV5Xn9KqVh36S+AUvVfDScagAXYdSR+BaLXXCMAS8cfBqAUgD+icd4pKB6rOJclDScmvBBJor4YMMuwHgrrb9cjQKPI3HYKDTa9u6ywtq3qSqHCsvk7SkMnoj9MKYrE1yF8maEcekm6wyBB34PStj2meuJ7pnVAPooz4yi1Zmu+mauj96A39V7rGuhLIDCmoIj3ce1/k0Pi4ZaQXkPtX/cZn9iB7W23JtGtIuhX2G+0As9kAuSJh0+bLnfdqepFqO8dBil1kAJQdZaHCUj715AqI1e9wHLZndVuGDdPWy+UTj1F1jo9gDypBFiuf/vVT8RliIp5XhUHQvo8gkK7vSwhtG/xLhAaRoIVlmnCNBT4F/QMEu3jjRYZEC+kIjX3R2LH8TbThDpzUlqJhbjaVW/HGpSMxQ0usK2MVZ/uW3ggUw2L2CsnuKAUQOy+FCjILWXp47EA9Am7qDvuQRiztfWhqPLl0vswRrdv3bYMj8k6fk6xZoLXOXCPWnXzFOWfrVippHi8Bcprw7H7mbCFkKTk5hXjllWMHiewnrljdPUBfZjAdpJWReYOf351H+b69LeFjCgxSL84B9zaodV5mu6d6Iix+rRITFI42HPhGiErrcB3WvGbNd6QKvi9nEVwS557AOU7ciVhvrx5lNEYTxA+AUJ2lKSMlvJb1cVYsh/g15yWNa1dvKBzbEePedWaSuHE8eOwgj6dC9hilLEXR+mYDuGEnWbnASlKgYDZquxLAQxjBYS9Qt/UZ5MsGLINjNmPd3XDNbVUsYhzAeV3Ca5k92Gztd7YO34nk8VdXZbyo4yNtH6kbWfDN88DO4VrsNl6wHOmAk9qn5F4Ur92Q9+Vqt0zbrvayWdlNgnUDxfaRKZRu1yxE0F696YTzXk2STKX/IsBvejyHLVvd1jy+ln5mOqprTb4x2ke1/2jgFZziKPMsA8vzFRosTtltvKsR46Jovb/PSIzg7Wrz0rWTcQkrItrkwlwGKZWYWkBBvOuhg47sZKiDpaoE50HCmmVdhOS2hNYGETLSw6B966YUkiMZSecVXl0XWbNMc0GIGC8lFk0iEz5RVKdIQoFXqexEiJdQuFDcDY+VRoJCuR3eNqZ3ocvwJ6i6AcYLfLlgb/9PmThAmxvBnG/AiwG/wqfK36WLa7+hUGwHj/Ue3UmyjkzpPsaUxndL4rmPe4xSRqcm9mYDuGFrWbnAxFKgYJZtu+LPr7j+QVFSt7Xj3uOr+6KMsRwdmz0jomvqVqNZdPM4af+MPvet8qgenOZRRuZnsgxbosxMEQqluysL4XoBh9qf5Z4WLy2A96toVQU/y6nIsh9FTUyyhCQectYGMIrqDY9oO3RY3KujvHryCOZRmzYDTrldwxHihoanjBk1mGjBaGDD7GaqawHz3qI4KIJ6Phw5hnQktai9rvntDvHfZofdouf8CiwiFMuwWyItDLv0qCLpcqPLndMocstVkTgxXE4IKRItZIq3nAiSEAfZoG4rlpE4j3xoE7Z1mu74d9WjGWn3xdO3/BNSJC6IthhoL2Hj6yNJr3NdYEm5PVcb57FGR7tQqbUCiLSWWQBMgVTe+2v+GxmrlvOWG5FMnss2qqDLBBh1lJsEGNFBrqvbMp1q/vrrKM6zEsJoN+RjpT5FiDqJhvFwU2AQrVGSnfZZSlvmD9w6bFKU2s6utZSB5SqtKkMOQh5KnU1RDylKIj14mHLJ4fa5hg2TpkRYBQIc5JO3fBpVX5xHNm6eBBy95YBUqNEWsaDn2K15r0WsS0sapG3oEB9Wt4bXI11inCYFXj/L7eju0gkShPVNdorJhC1/w7xMuG+apo6SBFbVNA1e4AvQyGi1EvOxXzLXXgwdKT8zL2Tdsjp7ydohmbKHYCV1q01C54MIVbdFMkEHP9AcSQ5HioxytZsudvJ9hDkunjvnSOclhyaNum0HNwnMDV2kAWWdpDKhq7tJAYXLs64zQQKN4DhINHVbhEpbI9HwbGEEoYi65IS6WBTLW0juZ6Pdmwkh3f+RwqdAFDghtldCBAWeWFPc42vlrtsRwn5sCG4PUkhlreLRCqc9wdTBDkjaO/JT17UWIHwLCJT+B8GvbosQ6GNZNOuRT5lHuqs1ZXDj180zM0GEirI5YAkaxQ7Zxy3iz/Wm82YNq6X5DQ3ESXOqTC7NaYBwad51IkikExwHua4RMnsvjfdyjU8VLdOupXBxIX0oTSZPvMRFN7fOkqJ9aq397yAk1G0RAk307AppK/hVZzLgbfFTsK92Bjsr2FfZHB9UIuTFmWabaB2Jg+0CvOBbKVGRtWm0xzxJUnDMSVOWME9eJnl+qW/sEmzCRj/AJF2B7PWrixL9IlLs9bu/vX51hTMo/P76b95rgQ3ECp2nH6CeUqxcNbc1EonZeZ5k1xitFbH+6jwspjGctn6Kxq5xY2huyCvaU42QaXSakXZSZ5oRdo1NOjLUkMNSdm+MEY5oF0mxigCyMwjHpqm65bEJrG55bGp3+MdP7Ul4aHQm7df6uK0QsTJaE0jxbvIFXBytRh0J/QrwT1hCxHYR0qvfw+ShaOqjGvHRbVPDKU1pofHqP1YTvG7aqR6xrjkd3d2lWYrWZDT7YD06Dcj9IGzU4JeNS4jk1wniLVZ38mYphImOqh9vInGXJ1HDBdqMl8gAVA9woZqSUUd48vg4/kuyuKHT53VaEkfClyKv7ydu8z8hGJ+e9PpqHzn6AG/T4GsrFKqjhOybn4psMQF/iA1PxJhUw8cgf5jEAObanETE0G2en0zZ3PC+0HRNnt+CCZSLbjclCuCQCm/sdd8gg6Ds0rhelyCvQIJ/blSDyZueZMmoGr+EVZONT/YTsMaO9GkJLjY60WivmttBR592yBdNmdyDCk7pwL8Aqe+lb/411bHnpWsO2+dI4Kyb9rRkcq/xB5jBeq9e3tJYwpcQH71Rd4utji0+georfOq8OF8LfM8mYV4lDD/qZE9ppEeeZBnd8ICbY09ZuRApIQVyjcnTxg22LchCBtkSRde8AwV739D3Clnpn9KqLhQPJsoAb7pD3E1n1VDCcbIG1PVNkI/pHTHkjIOQAd58r+DiZ1rfC4MxQwuDsqjiOjhSC+d80/A3SQkn9J8rEjrLl3v17GubIkrVM1ys6NmmSN4zqty1Z5dwAeEKLmghdqp8C5SGMjKFEVgYjLmG8/PrqO/qpIN9qUh2tkT6UDxV7NOrtZU0FiB5accBKIQeDxUUviYXhB6vLbdoKqnAPASsqNsajkEDPbytVAt1E4/zztbmJMZ2h93UMGyy/UdhreihXfWFTqaMoSBYdZyVXF4rXa4t+AaiybAdlrm6rZ5eoSuU2UFjIGNehIl+sDiSQOmpaa/tbmoYFN3+o7Au9dCuAoVTtKZQ2q0GJNfpggROuJA5CBZ1W73fjJIKsuc+7KRTjEC5kUMVzqu+syRbGojgjeoRnhTNeiKn9iUixjrFDqIpvHZDa9MkuLmCObZjpxhZ29Q0w/qCjC0qf+po7ZxXG+4g7sO5/a0jOaDct2Qrt5OwgduORi5JteO5UdURR6QAVe7KKvigLZnqYdANlMNbSDbGOCJS8O2QKHr+Rfv4WyiafzSgG32Q0ZGn9UWZJqOH8A4NjR6xO8VoJhnJ2BHGvW+j1RHHJhnb2ti0myXMddrw1g/FU54VYBHhOavzqkcWTY3/jBZIXsEer6MJhKX1T5gu7x1MF8NduiZfZBCpWWD8QIZWwJ+QPHmx+FupKB1VVZHgqORFr6zITz0iakkqzc+kVYX7dx0OGqXHNZKDSGuFtJ1VQ7cGKFnHukJN13qIcL3yK3kLIkivxCgOeqW6rSja4FfqzY7psiv2VzGLTbDHVtq0Bklgd7AvXW6Sg3+vxSY5WA/KOi3Bd1iDGr6LdNW+JXgMHMcgA3kCA82yavRbZlM50EaSDNpANlm0iyysTQcniA0tcPwTp8gBQTajiXjc1Ifx+Dyp0UXUDDFEB/mnbiuKDnJdguQBUXyiQG9ySTeudUd4BvqGj3+AWfoIyxfP6pPrPtbBdfyiV8TeuYb8qQNBaYibjQwQO8gAKIMAWaigVE80yhhC6eB0N69IQqcokXSyQ/Rp/OXGxRBzHajWq3y1eNoriB3zS/gHfjPS30HAoDmsA806iJFQLNoiiGUuxTl8uoSgKvKzomy5aXo3SMe/kLi9oxwQhHVAf3nKbmq7G7Dj5425qsHd3ddiihQ1R4tVmpuv9v71lygPgjCyLU5euS26YudrUVM0UdjSMgiJ3SkFCzQ2C6KcnaBNKCyegsd02Nl06z/CznYByk6/cTwhbM/zPCrSUxwjwjKaBzJOWMg8KZbQkoQl4iEcFjGaxzKO0rELonjD3ArrgpVSNzQ8bWMowSSWhho26h1joSHZXqIEMvfb2yOrO98VWpEc86pgzF2OcujbIblao7XW33EfZIHPiRSoQVYs32jxHrbH0eW5nPDf1mwiEM+9AievOkszNLAIb7Yjvn76lnfDvqCOlvyTYHkn4Pda5VpWly15qwriY8hWtZx9sxo+8RitHp0tCXRYHOmiRRVmPWiH6i04dWgPctNV4I38eNS4YTZjSDA5mRTvus+91PnznQhSI0wuyrU8bbsKPJQC6F5d1A09cIwg/KKLvYPA+zOIJh33Ks6Lp19ywllzwLINsdSuwTLcLENIDkvLe2n9NdIjZaa9Wrsr32ymspJst3Sxah9lYGLw5g9QpiDfpGs/KVa3aQ4CNWIz8gMvq9u6emgisLLh7n2d6g5B4zTyBeTNHUjwJJYXaLaNUWdxmnX0TRtuN5GOxML2M13EQ/Yp7rUpUEFCOddoZEu0+AeL2uoQ56iq0mWOVlFK+Oh8oTuDfhfnWLiPEsS+fcczp/7kAl+Cdb0JV5ErecHOtJ08XkIiM3mIc+xDfJ3fmvrbHUHanvc6E1WdVI1iDt1ur9n8blgcgiZgW1XlWLOuH/PkxKJttWPVopbPYH1drPQq9h0si8NpxHRVn2Ez9WOcNfGNxVcND/rg6AK9QxPN0XoNn2v0abUePz0+Ts3zR5OW4ZfVqfWS12WRxYgLmen0il9CDuLVVaZ6C1Ke53zGI/UH2dZxHaHe2WoxVB4xyfOodQtIa9woJa+RBtr6KseCG5IYnoeIm8lhF7HYRY6yFBhspwiSeiYX92ipX0I1pS7Na2x1iaA9cLu6LQXdwuNodmgRGaIji6wor/5oQAmry+Xt6INqfUuLf6KVQAf3R48dba+bTtDQeYUwoY0cJhGSKAXokBwmIhpiqLWf0/wBB/rOmcPOVdvaHj3LUu+0U9a8dqTrFJadK9x7+xlwHPYanWSLE/8Z45ZFHxSNmc7TdR3qZYh6McAYeo4HKl31A+vesKCbdS2HEFauAiym73TThMSGFwo1PdTZ6faRQpgRfW7O4y9V+99BXqjbmihWgWv1e6k7xRmp0asqO80xfLCCdAUTxCtxBmE4Vi0q7Z3aSIeMn4tlMRxqzuaA5Mder7LjYvEyxVurRV6j9dKfR32F9VNRPkwRHIAE0wtZwidNWcI8eQn3rBOcp8/JPciXkDwW6otas38oG5EH3OER3mhrUeF1JmAxftVYw32DFGfGPDIOXDEkBko/FhY0aCPddMvjYTf8tHZRvrzpkRx2VHVbhh3111H2tn5iDA+X/DpKNgn7bOF/891OPhcYQThZbQyWs6JcoUmn7qaN1d7ndJUiFrsuWm01ONgGL7DqfpscQFt07ZkfI5Ftp/kCzWx0DcvNvPkCyM3cQCunw3IQzeq2WvKHalgtlnkMJw+/jL2P7h6uIHH73WY+JjepX71h0By4Ud0WIdToNkYkPrXw82taeR/HTB2T+49B8nCekyjNq+aWOsL19lcrMB6WRNQJHs0FHivDzhyZqBWsJ816YoIVLFRjhZjeaFVjEt+0AdR6HFH81vR1k2u4Qup0UHCRDN1BkKjbmkc7+5HCJ9TV++lb9rXY7N1HaBqWRfkSgZd5VAc+PvDxZHzcCfcIbMxhOnDxgYsn42IyFWkSFLZcrFZF/obHdGBjdVtx3EdoRB/LollPz5ao5RlWocE1MFIU9piugqMku4RJgV+tdvfY4tABhO7NgOSw4sZecXM6bLUxcM5BvueLzSFY+O1hY9jawKPKsDU5hGBqK8CCLOzvZcgqLN4M9Q8LcJ8X4FXW6M5xR2o1Vv6EzyBfNmA51k52ldzDRZPBa1A9+Jz4oGrVGxrJYS2p2zIGhozCiaZbQrbNGiMii3wR+JIlrqvp6d/GIVCsmNC6WH/LT8uyCH7V9it8ri8bn9dYPgPydEhZe9Y9zRe+rTZJAqvKt12abOOFuZ5Xn9IFWq0RstosSzTaC4i0HMccQn1d80Oo40aeHN3dpVnq5wIb6labnwfJr27raLHAUx4cWg0zGOF6n6dmojQjutHJLYiePW4GKMp44AtFu0GACDuUg6BC/Nc/AhoQCMtgOrC+ui2D0mN7ndd1M36parhCdHgi9uHUzV9i0o0dEjued7lj7g9o884rvxgYYaUMyA6L5bBY9mmxnBVlswqMGCM4qjcCqsNa0XAtRaePTTq03P6eKdaLzGBwLrNinSa7GHUm8K803kwNJah/GtDATILpI6LFYIL5LlgWz2G1alZrjMBOzEOxVup1EQsT4s9/wkT7QOw4d75wzsPpD1Zx+kMQbIOeV50xe/xy1NT34T4zCuEl0hzWKeWX8cU5kgyl2ViRI4YWKzcsPJ0KRgkmyfiihnUN190sHZve09DKvm+ATD2nIIO2AOIEQZ8+Fz73rlCtJVpvbygsB+mvbosiE25yayLzjU97jyRDRxIsHJkVR/oDzI0AT5/rK8Ekh/tq2KiPuNINyYSKrFzb2+iipCVjDHGC4Q8iRd3WzE4Lg6fm/RYdVTqE+K/WIF36ODC63bl60+M4sK4374wVEGk0U0ZqGKfw2bUtlj+iDsnT4OB2J4so4GrCsAw5VIfV6L0aR5Lkx0lyiqYs6441oS5Cx7YLoes/UpBFpPU+TXxdOwlJgp+CCs9RGzWbyxXMF/jaJ8iyl1BcR3UNknts7Lw7S52TcW5qvw+q/RdjbWtZ+RU+VZ8hXuqBRy+DyJRjPEhOdVtyigWfxZAlqROHv46SxSqOPImqdox5h+XkHiYPRRP08kX7mtkbAdVh0WgWzSxB8Dv5PNLVfbpeY/iL9qZtLLzn1TV4Pn2GFDV8USFE/W32aE8cbsdjULH0LbWnj5camseJ5LA3Ehm2cQba1hEeIbKuGPTwkLyViEL48CqLaavmKRa62uYR7YenTEaSgPaSa3qZxR9qOAs7L5l1ie8uY76+AC+YBT6lFQ7x9BdaCoQHqaVuiydZqNAiM7GlwWb8WKXrTMFDN2LlzTqzrSOsM+uKUddZvAV2WFkaJn5JMthuzYGrASO6gGVaLMKj1GqQEXyBVyvJpUC8JGP4JWI5XyPdZzrP0zoF2TZLMrqLVlLshq2hFl0MoFFesdCuwRnK/X9qscybbM7y3Esskxz06b/IM3SELF6RHgQHAX4jIDyIZ3VbcTJR+OVTkOAh7xHMl8ppaH+upqdK66QURv1MSqWPsLJuNuAbeaOGEgSpBjRQ1cvIauiz2XpnI2QRHQSJuq2LPj30uzARMOB5P4+T1t7VWBZVhZZFFs5lPKoDn20rnzloNSEB8IfAd8NeufwMH2EWfMfovihrc8aQkWLpzhB4vMYNxtx6SBQxckCU8haBYTkiKuhffoxDCMTksCxhOUVbE77SZLrhB/9o2lfqwi52kOccP4FKn/zXh3R+VxdUVxa0VxXiXVG4gt17Zh5pgvO7dNmUrcXa4TmIfHVb88ST7mGWXSq2wis14fMbCsOBY705dldzpJM0Cj6cg+tV7X8HttGsfXIDK9JlFpOHP8maBTzPr1LUJBj8bf4RYBegqvDNMVRQRwk06BGOTovrtNY+hBqnlUmC+L/AGnQ3+HTqfrzGPkAqVHyC9qaZq8j3Di5hjkTyUfUzXbSHLEHYWiz/VeTj06Ft6mcJ1p3+L/Tdpj4ytZ+YeQsb+XGBN7gI8qVMi5JI5aDzZDywa6BLiO0zF+o9uVlRO3J72Wa4DoLZFRnT2LXersjz6iwDy2po0f8yh76laLs6ogma3OwF0ZCeFXYKvsDVLSx7uZoVt69fEW3999e/CNPFwOLbMsQLgAxiOFR6p6/UPnjTwUpu17QTQn88qqoiSQlhOhz/aGADFxwNb3B3uKGd5otXramqrrKxatuZw9awFBpxIaJ5ukZURqz0++v/UxioVXPD2fCmuQ/FU54VOGkN28Ivb968e80rYt/yNizhFY5xwMGMJ6BKwEJcWoiYC/ZLN1n4gB1JoArxTZrXoqKX5km6Bpn1UDgMlsoi7uHQFl/yAa6xlM9r6+mz6UR/vUvsyNAeR0ITxX57S3GnNdPe0Lf6bFiWqWBgWE82ZZuQMCnTZ66NreJS6Uim5VHpfNl0gb/uOTenUouNZjE3sUcTZjphS7cqYWfNitkqbtaPaDbRK5lTm75Q1WZj7+688iIlr9XedP8reVoOLmNkFtKFgxVtSNhWgXx+ltUPYQI+1c+TTQe6KlvDmF00hC3T8K9Ij8GY3MvTYhvbz5jsEGZgTHaerBhz82D8LIzZH1IYZSUPKGPGzZmJPRsKeO0lI7GheMxejKTqxAQspKLrLki1D2nVqsFHazQp2A3YjSaFlZKRdJVkTNXDuzCVtg0Jg1kybhBpupXuQpi+ynhkGVqwF/sRaNL/6G7VY63TgiyyWi6Uibpf2fZP5hIS4cyT6CXWrEg3gYyzIpFNP/r6c+txN4hv0kdYvpDAIoOGxQBrdDgP5Y1FLeM1up+jbZ+63kynhUnpbMVVVL25OWtQB4YtycACQgUNh/noZ8pmHLbRrbEUlKOYjkuV82XTBTrhy1ZwqaUhy8OPzKM7bMuqxjADg+6gNYtzFt4BYhSVRotWBixjTBrOhSul+OewbnUdmYCvdHTeBSu3Y2pmGAxPGMSSsqJGCvrynLFJCf/p2toauWgc0XQC0jifNl2h620VZ1vu6LI6E/HzDu/uunHMxMA7uMuT5IxpvvwC6/ticXMJEbYUB9KeYN+A1vloripjYraWC/9atCezo0hhRG/kVY2UOjRtj4hR4E2PXtlpGbSULjSgE1mkLdhTYv4FrR3BBEtZO0dWpmRbZbZF3D+qbWJGDk7GhsOtTXsG5LHas148tVzRhwmYR0HTXWIbVvSYppldLXFZiMUtYSStlIzPTtL+TMhUUlrbtM9UnO9AvL/0unkeQ8UBIqj0UHy4eutwKC5iljCWHdP6jP04zTKktfQNGLvJwkenAofenhTi6vKgRnejY3OH2tRfvoKOHsN1EXeyCM3INjEF2edXoEyjmEBimebLaj+kMjnMIq/alFjtZRpkXpCebzJOqXhIW0vGr7IKLkyrb1DCuZukWdvGuVZDmYB9rebQph90YrpZePg4K5YXRVWb+VaAlPFqD+TCnyLineJJZfcn4EPlnOwM73WP8d4MjKPjEh5YxYHDC79uTCigl/ChisG3gw9VI5iIFVXzY9N8X2duj/wlfEzh0yeYre+aLMc6L1Ngcs4bqmv89EJND2e9qXW1374b3dZxtdvApnPkW86zg0+/rTgb++Nn3c5ruDIrAgKkjKl7IBcmFhHvlCKg7P4EXKmck51QBHDv+71jYBwdl/DAKg70UASk6CV8qGLw7eBD1QgmYkXV/Ng039eZTxEosswsBRko6bZeZE63xlmEOyX5pF2fYi+WzcFOSDzc86O8esJRAIWQKIxlCgpOxWctiCu30YhlmqGEgbeD1yQdn4jbJHNhpeIh+Fl57UdRE1dVUpSLG4pjdOyhqKPiQRrclRNVTSm4Us7u28GbhqFMxKeGubPl2bbWfJwLy1Va4QxN3Uh6lz8eqPpsTF9Nyr9cDSf+1bemORnCACOcINoRSQ4e/fxwJmKcPuOsliA7aup7zIttBjBugpS0saotI5Wuogv57DqwU4eOTkOaQEo6zbFNf2Y/jjwrymZF3K5GBhdBZdw8QLmwrgS1E59GCchRd2ICzlITd3fYiKRBtuQjFlbJSF1iZkdO4pDPxEryXkzFS3IC7w4z3ZB/2yfTtJNNASrZyJmDaKQS9qH6tnV7pqr/UzGeZD5smt7U2gYh1nKNhZBphzyG+Goxq5hvS/lO0vVpBR4zH9Z8twXqF8UvZi2JGnB8FYxCruI+KV9vCQsqxjCpDifOj03zpMJsrNi+82sZSSwDlrFi92SHPRtKEdsHEEfS33S9mICRdNS1aZ6tOTNHGe0BFiwiF+2mz0Pe98m4bicthj7P1Hec0r9/0XvIK6biEm0tXaYyuoJPPjd5g5rUY9vHpVZDmYBprebQph+zpyqTjqSVfE781C7jqbi3bU3CulKZvaV8ywxiLqZl5s2mE6TCvJs7CdPR8ygHp9zeXeP+eLy7w4KKnk+1w4tzsUvMZkp3IkCOwHC7mNBE2ftJ2W4Hk5d8TO/qE4ADIlCX70EFFz/T+n7DQSp2MdSTsWVfxYUrTc2oxKKM++Pdbbfs1QS8ZzkNVpwoxTA7YzJKxMBCJn6R1tIxpa/WqG9Qwp6qVTC/DLUayoQ8rZ1Dm370dbaLh7/TS8yNkZmqk3Ez2+ruKKL2g5mLqaXzadMZpuK8auvXooY2NtIGTqmyYhBnlZXCuzusqej5VMqqOBfbbyNdwie0fC4KhKDq14/R966rJGNDCbwLQ2qb2ykvvc1IJuBWm/nbCQ++bCB2ioCxprXYk9o/Lg35LZigpEc4YSW5NacnEgumypnpeu2Rw7o724u84xOsV/k8bP/m0ve7tZl7VjHxBQOtYzpXX5y8AVkuPwVXbw8LSocwISdK58im/QHBvOEFfcZeu4gVDjpisAGPOSTpXZDWrOrJVLqzisa2PLXegsiVS1g3ZY5fvYI2NyPk4HJ1gIJ005ylTeyYzqwbwyTasm6edkJP3nTaUu6pKkS/tBdTADppJgVp9gSU7V523OSLDGrPZjR15FoKC+6mqaibUp8fUmMYbbOw6NkkaohxKmx6sak1o4rMjcS4bShrjM6Eu7l/GIcxB7/u5C4ijMIUVqGqMDqn7mKshWkQc7DpDkZesLnxTBwqhTZmTvROl7iTjKkdwQRcqZ2jHWLJqzVM0rvu9v9RXZfpbVNDWxbV19awrLyiBwsberCDLG03oulY3G6Od4Hl5SP5tsZ/3ig4Urm7u+OSKhgaNE7Khkd3ZD5mq2U5/1IJGO4UGos/b1h5FqUYtnQj0TK4n2zXk3e2LUfbLeu1Jl/686+48LHPvmPZ8I3/+mvxzL0K+235Giw1+eFEWM268VghDGa1CoaK42U863D+AGUK8nqYlpNidZvmBPAGrZj0EZYv1+lKk9fWDY+GcBoUHjS17pDs7iU98NG8sJ4dnU4uuM6pTc/oenMvft34LA06CxRbwfE7bN85DGs7lsYOWnoWo/pHg0oQZ3zP06BVQePZiqXBdEiyPpiBz7kZyDq6HRwvm1ObntH1to33fXcAD7EfgaH3ScBvj1TfI1HeD8XsqbNFMA+Da91xwhh3jeXn8L65zrfDItgaZxs/uB8ga4YRCtzqyJpaZA6rhOCJsFT0/VEvG9Oq3drVYzXg+VaSFX84rCoexdyLy8k0sNX/fbxoW6XJz6yu74VOfnMFVusMfiie8qwA6swGcvDI3MUhl7kN+36OzlvyvkzIXXJKW50GMDVn47DrFJZo1AlkXhxRcpgcXMZhA6QLjynQOz5/Qvhs7j1aP5QJWFQ/VTYdoOttAYOaHBEC5AhsuYvuBWXvJ2XC3XUhUOH47U9Lj5iqnmY/Vl96sNiZle3tErNajmW6Ld40izY9YSpuETu7M/J0LOzDvDF1S3VXZuG9fRCfg6V+lmY1zu5uvqbmgMOKJ7nqQSyq6YrPtbYtlL3mEc6xFswsYC+T570uVxclVozSFShfTp+Te5Av4SWo4UlToiaSF3UUsammNGYYV3IKDja2IjXMur6PI52t+zQBZ1rPgp20VqLZDgYlf7hxJlMlPkuy6GfmRWlnpmZCKcEduI+pPxvbHYPk4TxHfUkerprbKinTNtbWeJvYVFHGgoo6LkxpbHanrhjbjmYC3radTxdn1tZxtckIM9SbmKd30adgOZYZGXoHrbujJJO8267kYjm4jHkHSBd2VaB3PDiYn1X145iAQ/XztDOnBkd3d2mGvsAbU3IgAVLKkz2QE08KmCd/xE/ZhSk4SUVYm7bnzn5GXq90Uz7VVZQPlPpuzpqmZnroe25N0Uz8ndAR0Xgf0Yr5glgfLOHNWVmsjHynqSN3idLgbg5QdUPTs51FbyZxThqJb9MLut62MN914cp6mxqjMh7VzOxsJ/ZleqYTyW7Th02tGU2IOn1Ek/O5WN5Qv/FkauwIZR25MTGAuJkT6lZkShzX+S00K4zjmcS2MM6dlW7IVt0K9jWKShnwSAy7m25H3Qgm5s2dVB1P7mHyUDR8uL3wWZ0l1xKBNGuutK5TDl3b1mVMLYxx67jbdXgTcLzrfFutAr7yjOnLk6ZEpFhegBeS1v88TzFe00MqmlryZOZsBbd85rrG7F8SiaLXWnVmAq60mgGrflD1toYL+zdzBLax5REVAhve9HqsyrJ5Cbealsb8Qtl1dDOwv2m+bbrE151tNXwuEpCl/4KLvo83n0G+bLA3QMWA6ioyjhegXXhd05SEu4eebx1Xm8cxAR+b582mE32dGTnWwrQzmXSOppyvCbcVt55mst52xGo7JTdKUZ0a1YBlH2W3AmX97fafMKlxEXxG852QjoE8L2qC5e/fK3iSlZgNqt9f12UjikyM+grWkuur1etXbbHyDrDAjDwy2MDF6QqkGdLsQXKPtxE5VgmgEX1/CVKGcXOX1L6Php4ZUbX9T5KikY+SLjci6zbdL7C+L6QjZADM6AzR0NIWjFHwro1atWKLVoPMzDpppZymvsyI5ATUcFmUKZRh6QpfzKNJk7oppTi6IluC6DAxENbkwe89pSVULVsJmBk1lcBQipPJHWk3cO1EsDAW89HCfwF5cwcIuXSLgwYzojbhdELW70FS5hv2egOSLl5CSrghxMTUEcwCqlVAysxE71/AUos7FsKMEO1/+ObcI9IEpD1jAKyJTTZa+WLgQIwoj7NieVFUUlx9mRFJr+nKkGxMDRMSrHTDqxrbepewImqQFKEEzoj8K3yqVKK/LzOvySLLjvLqSbEUh1IrRCoU1kyAdUcdB7QhgqaewHKVVhXS0dq4OWmvOBizBvKMtuUcZEdNfY8VzTZntLoBHbyxMRI7o+LgodAOzXWxThMlHlJqgwioMNj14mNZNGtlL0ipERFxHMpwdF5Yy533Oz6ib704cskqATSL2Y/pHdoIy4URuwTQHrsOoR39VAJjKLRD87WopaMbCo1oxDfI5fSSvlVuseepNKvN48uWSFQUY5+JNo6WeuRTPkzmDVZj39iXtOT94188s1PP5EnxNYqa6jkG0xg0yfel49G+suDVmH0ztsQj2fqVlCJp8u0QaZLaahrQ5ib2a9ihNdsmbHA7I5XmtnPofHc+bmhsyJ4iw0xlsjHq0IXcmuyuXxrVJXIfUGEYDFcsbTrxBRAhoexLV24mzD0yUglNb+UqHANgVuHl13KkGr3qapWDyXgNV+sMyBlSBmfrwHjRIeZhrGWMBicHYlYsYA4Rx2pXJA9jNn37GytS23dzmciA5nupQTMUmtk8uYeLJoPXoHqQsjlVbh5af4VCOrTNnRQT70FQIX76CdPlvVQdYABs0X1IkT5SKZYJD2Ont5sWn+TShJGJ6VBZOQ+zYclGbhvixOT8RoXv2aMifn09ujaw1CgIVmuQLqXE68ss5peQQiulWBAr58FnWCMj1TTFckjzwPnAIykFxDgxV7TKfV4VAWfU1GXn/SrbRBHP4diGDW47XxN1wCvvsuRA3thZzFALzdEAC2GewrKoKlQx06DkYSxGL139Nqse/apVKlBbZNZ+wLPWU74pN9Nb6bCR+2qog03D8eDNWYo1L6qC7oywheZPYDeH4upKw4n7MDL1eaVwxGvVQH8GTzVAnVjy5+gshdyod8OeLGppx8JaDYypYqCbNbVYpBJacaepMelFzRRzvmvLdHQlV9agSTAmC9LtSKirmzIP4rLnizfDOaNIUQWkenjyCjLaCcegGqIpsEoopUIXgUr9rmGmkizXh3Y8XJKPSFTikniIWGNQqd+DdFwkwKjHwIPKaEJvixpyCKjGZZfexX9ztF5nKVxcF3Q/RaJo4dWj0lWTEYuKndDQSotVQjf1FESh3IY/beg2QLuMr68Uk2YDznGX3NCwPPBDQzJpBYsRyupFIJwUrUwRk44zloBnX35Vi3fNC7EyMSx/CNaCI8zIZCRiA3Vi0WaQoRtTQ0kfEdY4LKGKhk6WUl+JeWwpxjds1hcEUPtRmTUGT3JNozTQ/m+d4iCFUw9GBi4jERfZpSGPFOMkaifTMhtqpmQodR0jAyiraljMnozGRiQk1WKPRFfzGpWCuw3UvFbDCDnNkmUj+m4uYVWXiOnhggooFKloUUs9WHNlGU2F4EQNOS1akG0bmxGHk5UObLwZAjAllJQCaoYmg5fSi4u91JFLinNkCvV5xTS04UHUI+AgZfSggmw1pOARTUQELg5WTQoW0DwOdmqDycKikxBHz3U+foj+YhDVUYknQoTSOBAEYKk3YhPZrXNGiLgkVFESOYQgx2mWIRk3BIhrqMKBWgyHrRGBPhzCiYjUhYFvrgFqqCTAmkfFV9HRaRO0bkEuAbFMDqkmwINesqDyzf1eCdH0FdQD1NaTkU8RFq+hob4JCSGp8P1gQvZ3BrTEE4HUoxFgZUSibjFoCCOimoAYJ8WKnIRsLlPI6SHA6cfBgwevPClSCX2UpPY3XS7hYwqfPsFsfddkOZaOTIHGijHVNBocBgTBVLVsRiPdVM14kLu/6KJdmyKQenQCrIxe1NUbDaFEVCOvTdxgz++bG0Byeghw+nHw4MFcJEUqoY+S1D5rs8gyLaOwAJolQMNJnQXtbSjdKmJQjMwYm3tcN+19LPnQaRB9zylI1fCHW2UGItCoZG4RGSU9SfCjqIn+QLKp03fb5ORQgevHo6gVLnY1uBV0U0yBD/W4+3I33I09CQH1NTTj1FaUcpt44U9HRz1+zbbVjTWetWckogLSwjQzEs3R1JucSLoLlFrTz66ieshW9WUUNVwR1VDZrsmRzcXhXqmWuhIo9bhEYBnd6NuuGiJJkE1BERJ1aSYJB2YYBgutJEofCmqiCoduCrLc0Bd3FTShYQwjoEAV1ABmMtBIJDRgLiLHZI7uHrSWM1oYq3lsxxKHJ1pccmIIFA0SGvR1cp3YoOBs1jo1lhiig0Kn4hA5gT1o0ybINHt0pXDqwcjAZbTpr8tr6CJFNbIXt21TJ085CFP3dVLUmgZTyU5ZboGbTVokdTiYvIJ6WNp6unAwPoGBRWyYvA1NbNholOwyQViSUZJ52Dw+NvHwCARkkw0bmdl3CRJ/i5JePIhh/WwglavQ7C7jMU1DAk1YiQhk03lNCIkzIaYJF+kziNxcoB7fgwoufqb1PZURRCSNqYp6cIaaMrJR2U80VDMhVrFTLOeiLLHLzSY5i5qG8grmgUrr6ejnIJn0bUhIqZyjWJT8Tk+oNTnZWo7jZSqPSVi2odHlHs7PYxD9FIhBTm0glRKvSxZkkngUpjFJIEkopFVHtfDqIemqySilyIekIZq2hZH1WFnbxhVqruQ2WM9l44jXd6484zbJgZiShByEelwsoCr20nwFksMz5sKkE2rdbNJ0qYnAAprHwMDrSGJW0eQoZbFzKip7m859HKzRo8ADmmxgDj7InuZxjexUYLKnGaS5FFInIGQV5FKBzfGmlTpSpCML7U3nzSykhLU4K7JgJMfTp4nZiU+dd0On0JbJIzW4ToQoa8llk5DyTyuf1MjVxiSTTjw+FXXrUg3sMEjd6gyk31RrVGhY459QwjqMS+OtCKTYNL4LNubMfEeIA9SECcjg48bOTUsieWJLC5IZKhoHq6+vuWqlTNppJrChzZGvX2kyld6o8otKlrcHGs36dMcmFQr6rK06AeHRAZlCbccU0deJPv2s6+LRYvPlbj2FJ1xm2o5Yz6mCqQKSBzBJepVTxoAZSUBDB2cOoHGpZVTb/1hU0eQMts22YI3CSAFbTBpK6zMnmyfBugvTZXbQdcm8f9vUDqKKeScfc04mVaJ0HWEf8PKaDgZFEFVoTDNNDNMFaaI05kG0sabIY4X4LwuPteBF53m5vm9dq75a1/UevFZRHYPaWsWUhxmR/u274IpCh7nQ43Emkxadwxz1KX3dJ0rfA/WkGbkkQOm03BYcZb+tgLfTO2cS1TdXYLXO4OZFSTV1OEjzkNgKwRTi0I2cgXZ4SYK5dyGjjwJSPSB5BRl96LcuNBRSIJzgisimZc0WLwLZDEWzjTuTZdKtmvLztz/N2o+yinFVqGpqVpvmiMGphamJ6URGHwKOSbqZiMY9ams4RXOp7jB4NRYrUouP+bpQXtP4ZKdw+BUfxAHpCpQvp8/JPciX8BIgU214J0jiejZW0vh5TXXlia0KU8ZkM17plrN5KSkuKckf1jRkoS0HyVSKQTUW4djkUjzMpD0GNtZRj9RUVZpjRfnMlC6riKmhkY+IVe1rtihTFffBararqESdZusaHqUy6tsKSPXA5BWkWduoZ7N0advkCCfQt4f3rW40gUMikGYoPKyULNSTWzqyCLhGDhISnr4yXxJ2FWnqSsrLkPYrToN8ZAHGPvB1c1YWKx3pdOA6XUxZS674cc+SadU8NeppSXddOBCOArYe26ZOZKJRiEcmGfVi243weptMwKvBdUJZWUsu6uk367TCXo1XJt+EB+yikk/HbFI4u4HpWMyLUpMF1EqfmLuRvGonCbC1rasesi0KadSa6h09XTiubXsyskueA4wQ9s2+kXdznqd1CjLNDRRdBfXItfXkseDCC3/acHAd+nEvQ0lfMLwRXx80E1NZ137gKhQ2JLa9NGXZooTqxkn1TdJKv8doztAqh1YPWV1JmZuVf0ZSQ1AN9pGT2pl2JMudyLQDGXeeMXac3962CHBQMUhzWA5lv73FDxavQPfht7cIJIHrugHZl2IBs6ov6F/23tTsvry6WoMEi/P/fvX61fMqy6vfX9/X9frvb99WBHX1ZpUmZVEVd/WbpFi9BYvi7ftffvkfb9+9e7tqcbxNGLvuN663Q0tobaHZ5kpxuvUFPEvLqv4AanALKkTxk8VKALtagbL+dvtPmNQkuvqZkyW/DVTuG+wfJKSPK8WZw2VYP+pr4d+diw+3SNxkb3DX3ugOZTcUPUODxAKBjBcqzk3F6gjBFVo8oOyXTtf98wUiR5E1q3zzN89/6tr4X7Z++8UewwdIPfNLI2IKHPCl1ToDL20OZw4jW+SMs9vpJSi7EnuM59UHeAearOaIv/ks4vrtLccBPK+9FZiNkwA8B1vyt/QpyzBGl6D04ngrPOOwPtU4j4grssfZiTDMoeKCEArt8V6A+p5F1n6xx4CfwuWH2X9zWEho9rkFRL5MKXC+pCvY2uM0ls3XrVl4mxiPkLWmimKxWF7qquOsqL69j03K4WFL7DF+r4Yomu9lxuLky9z7KaAMxHec5gC/YyND2ZfZY+2uiImszhS4SYBcWH2br/aYcAbSvBLkG/XZZSP9ihPcs5so+eSC47oEqGVierKYqAIHrlsv8BP13/LvdcLxHFOyNZKGeUU60sYetp1PJXIuyhSZ8TW36DZfHZZHWay4pUG+uGEQt7fNV3tM1wWLA//tUlvsRf/NHsslXGcvfEeGj854xC4xBQ5i8YSThicutY8TbkWTDw4KXkMMTE6x6z869KNYcDzbfnGgQwkVQootcRgb3lRanxgzus1nN1ySrlGfHbYbYh8kJPkhLy34MqceLvBriyDLXoROUiVbI+TpoYZJeRqTh5jXVx9Hzrebijj1Hl4AURIxBfb4PpHHjGhE7ReH3asoOQztFycFuRSVus1XFxOzqp6KkpulzVeHucrBbQavKn6+Np/dDIDWo4Kk2gJxYwqyShisDGRr1m3n/+9fLQ1ZuAwqj5VrqD+SikY3evVS1VCiJSmBXKySJGsWcEEHMp0vKt5GUQB5tNM+EKpugip3x84+YKtsRALmYmGSHhJc2G7r3Ob8dGvAXLw2ebpqVsTRerRqE1iz/hux3AE7eNZjl5TbY29r9G8Iqwmlg3MwI5osUzrVhcLtEXPGYPggyWfA7iMMnVGOIx83HeDxsCUO/NoPRGBQusADHwnhUCIdSl08NxclvIIZxE9z874bpmh7+TwyY8fg5Om8L6RhHgn12XUZKDAKhS7HPO2pKn/C0391sh8ScWvZfHXHdAHLBE0oid+Q4aTLpzxiubpHZohyDxJLnZzQSLTScV2cM5ordbDG0gW8vm9Wt7lgKXJF9jh/pFV6y59CDx8d1klzm6XVPS/iqM9znmXH9yTtnAN9uB0SQZT7C/CJxTYeiUJ090UuVl6JRkYezCLRXdJ9QQHjvCDP80X6mC4kvjspwJ6IzkC7QKndLVZp3j2uy2l2TIk7f8EVkkeiFiopdqLv07f8E7LdL4Rtky9zceW1ddTCVQ7hYKHCGvw7fHlqb2wxtilT4oZRyQ9CoRve67Tmtz3qswNvZVnxNPhaSB5TbvRyCIcW1uuyeISLS4Afu71quDM8SbGDNChqA3o5hHvvr4saZHL6SCG8xqBuRAnkfDB2XRwlnPbFlthj/Jyu0hp3qL+4TCMVCh16+tBwHcQfXHxNeXMHkropYYl2tPorUix5aaGCsW/lI+ImFmf7xcW23rwnxBrWm+8O/enqyJQGvszlcPiPJi3ht/oe5z/pb+iyp8QyCOcWNuqGHD9T7rB+GyS3AH6DFykaR4sFh41fy0ZopwDUIRESF4E6fHePYOJnlv7uoP7nWbs8qSxHjBEgKXfy9bbL6dudAr8cwp0ap8/rtCSxmh/ASyWnDA/j3gq5i0YwyNaWGspBuwEVnzuLUW3EYgdpytTkey+WOvUan2MeLUsIRd1ULHU7NB0qXuOLAsLpKVfssi6Hiz/8wqQKXORXV+nkJcngZ5gv+UhgOYRvCxewTAthHlUwHq0QBYOgESSxDMKF8vgwrD1lFoQiU+SC8wzxQX/IxiNly1wsrkWKVzLI+ton9/gmEG99qaCcIiPB8+kzsrTEyMhNgQu+00qgLfnkEJ4Gnk+Q2bcs8NkoF6XGFjnpZMg2O88f0YpFlfvTUEEpkwM5xfcnD3KPMlfk7NAj9Y8eQZqB2zQT0Kuh/FqSD0IO4XTKq8EuljpYA8VTO/bu5jY/ubJyJyspvXsh/o6zouz7dwyRbSpYSmpAh8MOkDwU2IWAb+EJhx18oaO9rU6pIxje9tl31G0SV0h3fC+fdzmEawvtEb6uBR7CkW5w0VUVQiAlxU6rDu87x83LcVPXwi05odQZ88+0us/Sqtag50EcKNPKmgwidr9AdpjoGZNDOBw/IGuIVE0T/qIDU+IUFM6jcsbxLVtI0Gy+OntDT4RoQabAYQ/C71+ATNI7tsQP41UNSnzRFuJXCcT4XS2kX4un+cKqPR7OgcM69+JpjuNQZIwmA3CObWKwQBn7aAGdo50s2tMBOllidB5bzgzTpbhV40T7NaykTwFIxmEEdhpLf7peHa3XWcrbClIAe/w/Ybq85xZ6/82BOhI7z92y+5kueCTdJwd6ScbzyXk8wx6hFy8aMI+2dIJFCTTnQXzUMAGYQSGmavi4m8EBmrEOD+kI/jOuzD1wCKdjEC+Is2Uumn8Fu+1M4qgQSz0wf4GgakrYPiEgxc5AeLQgC7EVCj3w4h9a3DSAa2gZObwUvX1CoXPIGixxzJ3cSaQA8WwD00DfxADhPYrNw5zakdBgWxPGM2zcYTfu+4g+90AeddVxInnwv2HxKn2PpWcPXJmTh10V28gVbUvQZF9bJn74Mhd/YafHiJYNU2KPsdNXBHz0d+fT0eqkaNb4usJCuKMrljvoDgqsftj6WSBH/0T5V/ErC+EYcdCesbVP8gkRB3Th1ki9zmWO3VIhYq/3vHuIPXXVbRV7OF5OxLL56qJ8xs1/dVzUyFhXYpUUu7g5UyC4NsknF3ddO9eq6D5Z+SFMTo+rDYxVnYqJpQ6YUxKEJKDcfHbp5RJepf8Sbm72Xz3DAqvror3mI8dvgnXv/zfZcYhQ6OhuvsQPjPA46YLZQ1xHdnztTgDgNrpZ9vtWh5p65N7+jxQ+fRG0RKFwaxSuTnoG3hZpkfjcFlHVHEfZ6pqTJTfjilxOTFxz+CnFDizkic2YApcIl0M+MrHc7zpVnHXC4PK/XDX1qol4NTamEue/0czs0+tcEeHJayUIAzx9WizjsFbftMr94YeNGshlk0FVohYLcKcwALnHjSlwsJ3TLMNe6T7zCmc6C6UuOnkXgilHLSl2ccK1CUOwNsvmpeGdcWo4n9YulFciZBBeLbCpj1StcFA+LfUTILXtNGB+s0SEgW56BgAHx+5zXQIxvzL1eXtEMnWmGiiLKUw+QlhbfVt9j6huUX6CzyRfCu8ZZ4qct/UtyZo/r9oZyR/OYvNxixsxbL/qqfJJ+nkjz6szCLDO2u8swo0Kvnj/uZW+KhqFYWmE/kyrx7L9jEv3X3IRhCk7MLAXA8fj3ECWnYNXw9WA2AeHNBVUh3MqmMMBnR7X4Rht947RDsdH+3J8NFdcTbdauvdWoihnbWYVf7VMVX/7FTKJKHYWw+3oxRhe+rsDNpKxh8PUfXO5IJGt75rsP2FF7q3zNyW4Qme8Xws12qEs/pIxcPd5hX9/u/tvQhAZu2b+D59gMhbFVLzeNytYutR3B0NhfbRYoI2F23aozy5GR5+cibc2Nt//nILdUQYfZ8WyT2fnzpfa2iPxZNucKPCYAofzETSEi6IS0+tS32efJWan6yRdjpdM4KapRTXq/tm2r9hFN4X2eH+CqhsNi5L+Pvs8foVPlf9q09Yed7WJigpbMt/6xUSRJYqnv88+7xdFlv0osBp1CRP8dIzHujWiGGm9onaP8upJ1ALYkq0zUQIj/xXvnjsoa1NraeITo2yJ01WlSC8oxXijKt4rTH2ds6JcAUkqZr7UHfMV4J+/ZkscHFnRUxDHTnx1dHeXZqnEo8oUOODDaYp4F133bXr30XnVxu/0j7pxFOMKHaJmFEFEftFCnwHSUOV2FVc0pzWEu/K5WKa59JqeWOqGuU9vpUQuAGzNXtXNTuCpfD/H7luVsuY4O9VZWla1yPrUZ7eZF1Ftvk67RyFxvAb5i6DRth9d8Ehj6fzi82rE7+jbY5ongowWCh36KKQaO3FNLday3Tt+X+u/OmN6L8X03gXTf6VrbO6DTLybyxU5aAX3RQ5lubCZAof10yfx5dbP5vMUUn4uvZ6sgeAbve1K8lHrVTW39TCdHG9WXYSv5ORzU+SKU57Xgy9zOAd5Kj7DukaGZFWIy08sdcB8X0Kowy0pd5ijBhlXaSLFzJd53Br8AeSPanclMx3PRj9WDT2Unkkesc+MhkklFpeHcDIh2FYZFTvgJz53ni9zROo2zTR/csUWucS1T/Ms7xiP2c612mh9NXCx0ah81pq+/liO+HiGQQTV4va2hI8pENctW7JrW9RMzN2fNYbxdY/F8xxXXnUcbv4M8mWDE89zWOjv04bRHBcLzqhtv7hGHrZuWemDXkORA84umKGreyJ6IuUQDrJg88yVuhElkMMcgSVHlPaLkztjq/OG/TmjgMeKKN0590QvusJkeI/FQ4arq26r8t/3+KTJSBoC6VYwFDocX+bpHw28ghKrnCtycL1lYHm+Qv3BCTTEoUuKHYIua879TD7Mvwb/1DpZezf0qi5JxEVVNGWo3SHD6LPOrdDsgt7W915cTmyJO0bJNWKuyOUg+KwsVhdZs+Rfn2RLnA7iiyYR1hX1eWtWQR9FFcb5PRbPmDd51V3g8BiWCXkfm1N4208Ou1eTcVtM+2WevWDbNfmDJadrZ/s0/j+nRTbXFf0h6jPw8teAxzMYVlV5nH0BtygLgJ3aZuof8f1R1Pza44r2WDfHhA/nPk++201NJJzz2gDFTmrKYheHopm0ilFSBJMN/2MDqxpp53hlSfQBtnjOw8pt16x29KifPoKNc60AYwq4WiCvvq1+PvV70b6vRSsC2b3C2FGlbrmJ2Xf5sqmD2fs4czLf0gj0tmTs8PO59nlYrtIKhyS0d54C93wOm8/+b0SxrWsw7rWDIX0ba6cNX7eGg06fa3xpKTtq6nvUaJqQwIQY3KTD7MFZbujG4bK41/1jhNX3VDlfYJrcpbyeJCt3x96JeFMjEjD7tr7hib0uHiDnNKC/O2I7ShJYVSqcTKmDRo7jmpDWqZIXsvKtWe1nRdmswgNpBjQe61hTd5xFe12s04RHMXyca/GL17td73WfX8izhVxsxY22nUvcRPiSsEWEtUHw+C4OReVxVgdpkUcxfJxtdWASyF6XZAqcbwxILws4Ohdb6cn7Fbuv9pjwCzUcmu6TiwOsqmWZWejv7thUMykrd8dOsm5L8bYlB5nlJrNAuLDylVOTiqiPZdGspXJqKNnlaxlfh72Hlyz95zmEFF6aUvWJKTgILEueOeTbHEltIyIggtpG8PiKQ0XlbfV3bf/Fsm3i7j07ryFzEbZg2ul0XyuKeuMsE9KYLCMAU+CIT0wjRX12OYqUvAfk8QZQXMuru/Le+RCEnI9Cqcvxc3uMpUAtKXacF3ztsBHwckXu/ZWjFUsdXIjt21VyxEKhM972irPSP6kCcue4k6YsYZ68iLcI5BAuLbT1LgEfT8CWuPeZPbBUrRkRysGyB7VM4lCfXfm6ua1xyubzPMlQx2TszUN4toAvOOtbGCDcWyBpp4fnBnVjkUMGtqgdmxzStcVOJGjGxkN4tqAZCw/h2QKqK649OYSnfEJyPsU6JsjOIJSSzAI8RttSYlqAx2hbSmYLcKfgDVxFuFraf3XkDznX+XBav9ok6DZFrr0Ts97T312uydw1+QIuZM+M8mUuWJ9Aubgo0ryufsISornlA/sUIA773D1MHoqmPqrrMr1taqi08/SQAS1W/7HKDE21IO67tyrkU1buEIoUOU/noHmvFRr52jnAEtsFSOgiwxeJgxPEIjIVQw/pErxSLsRQr81XN0yiCrr56ohJMma/EX4B1QNc6KmpgnHr88nj43uxx+1XN0ynz+u0JEEvX4q8vheRCgC++P8TAgmV+XI/Dv6QljCpP8DblL/dowJycUEN1bq8s5+KTOKOUkGFtCTjIDWUV0vHIH8QTS0pgDd+cbFKAfzwn5+oUeMyL6x91kEV5qHcC/v5LeDdo3yh+75AdBJp5mo5hMNKa5DOWKb/Isv0ugR5BRL8U9jYNHDhrYlMqocMb/ESVg2fV9wE6yId1zjDgoaecoiQFmQjUkM5RUIMWp5mQBowl8srZXIPKqj0wkoBXOyqVH73gilw9/h1OQKl/r6+zB0rNrhwwuOmbvdorZfOupLLgU+cvO+7cCxDm1GXcAXSXLhZoQCxb+MTqL7Cp85Y/1rgiNdEku9NA7Y1x0G9wf29QrbTp7Sqwx9Cl6D0ySlsh2acw6PBA8mvGeq7o4NCejjhuFXsWt6nj+kdsZ8iMpcEpQ9z2aEZh7n6tnks9HcHCVrBxc+0vpcymVDohleSKob6/Cdg3Di8GsCfk13h79Qxilsk74apody5X3YGx5c5WAcSb627l/a86ntAriMCQWeSAriPHRmma5mtJCt30XySdJ1iVUPQKbkiD5ySG0t8mYNeDHOs9YuqL/XdFZukg0yBg4cQVpVwS3746MJNG7IT5U/kJgnAHkvVQWJECD/yTJmlqTtiGBJuUBE6tCmaXp+8iPhc9z8aQKjMotp8dVA88rS+KNNEfjIslnpglp77iqUulFT117ev6n769lF64OxxrNzbQO32Kh20AsS3DSkZFCAOKoPxmDT0eFRzLOp9HPqheMqzAiwk+cu4IpeNqq+qVHskAC6RsgnMK9jjEIL1xWKHviP5+ROmy3v+FULqu0MIZJMvMoh3GS74kfruLF9P8I1XmYRtC7Zre/5KEmQFb88Yje/2LK875vYcvpV+FbIfffVLeHRdDE/kyiLp6eI91hRpD20cv5GI0cdtZIVlHE6NG+vdDoGTSt03VyzHqAS/9iJBNhS5+Z1k1jz9fXpbbudWED5FahOxhqyaHovnq1vyqtstya9LkDyggckOLfkyB6w4DFGmpzAFjieLUH4Eypc5nROmj7CUvxYsFP4JVk+4s4LGFLCKpnRZDG1KzsH77x4OEPnadPYfuzsaZtNh6qbML+EfJANmoPJCofJSW7T1R2KjSJfcYjNQXEUqnt/rEoKqyM+Ksp0t3tfOFbrgJdMOie3OuziEQm+86kgOLaD7vMmTz4ulLpwK7u6+ikmj6e8OXp/FKs2lIX5siQulqeUrvzqoANnN0J7ZNv2C/HmChG+MjZ/F5rX5m1Bst+S+AGWnuog3XekSV/+cDCNb4qLsbGgsO5KWlc9mtEc8rxnDN92PCrEwLOGCHFTIx81CzLHbHuRdzzdXa5gMkYmbuQ9LJ6xD7ZNb2A3fOGIx5vKTj+TbWhqQbYB1vI1zlmZo8QlBsXyZkwh9+pZ3ZFC9L8AUO3vFd+f5Dd1kBSoUGsw+yoUTupEUDWkf7Ph/8qRJ+8GJY/BgNO7boRRau8YNnfwlD0tH2NkRHv9tXFp57pmed15+gDJFSuywEk6K1W2ag/Bdw4zffx6dkI60gzw03GaBPzhE5Nb8w6XtF4cTRpA3dyDBt/NKZNZKbwOrYBzDwSRxYE5hODBf8pfH+2/2WH6mCx5J98nh/pTkIOyTe7AOqCAhguy4WCj0wIt/aHHTAA5aeFWlyxxZvynhiHP+AUZZufsZHk5RKlxf4srcDW8cJMlj5ctcAs9kTwFtvu6PawRJ0ORB7r3gihytuW9N/e2OoOgSO4pGnQiy7bvfKFtevH1uFx0OXSWlkSUrdwhvgM81+iQ+OkZ9d7s48UeTlrL7Ev13j+V8UuR1WWQyL68KZv8NgrhrLsJim3yVHWUpEMQm+TSpQemZk3m7BDe5GTqK9G7vnEYT4Qp0o8pxvg8KESwD2zVuRnWL8uqPBpSwulzecgcsfKGjGXS0+GdT1eLxulDoYNQQ40OFWCx12csuSngFM5hIblUwRVPsNSqMZDnINkamwOXGR/6Aj/ek+otQuIfBX9cpsvQxP4YJwwGNh+TT1N1+dTV2hnT8FKzqEL4vm+MY2sWvM9c5QvuCQdCxAf7pc0ogrze3q1gZAVBy7gLywYHpq+w0B7cZv1HQ3x2wwaQpodAn6rODw058VOiT84tCxbIYXFrcjsAWzbkTfqpX2XGxeOG7SH930YPyGjF273f7Cuunonzg1SE5jJMQQQvuhayWPkm+KIXlMM6tnD4n9yBfQpKeTN+YCnRrRFvXqdC7Zf3YvB6tV1XdViGne/nB78UH8Va6x5V0vNg/F6hQyGHKFLlqDmdFuQJ1LUTLiKUuynHYw0HKBdrcZml1z+8e1Oc5Bes2BdwpR13gZICn+QLNKDcvXNHWiDDCJt2nCEpaB+arqymrjyPN2uZ5HJuvrphE6Uh/H896mctIvYcrSNxNSLcMNFRpVD7Gqr7+OOxDGuUOLNpPU26okiR7zgn2doThjkHycJ6To0g6y3AY6ymQejChNaZx2DHatYuIfpi4Fxh27h4wHbhzDVdI8Qk9KJNh9GBVOzTbakPg584vAB9GtPm6x8euJ2iilkX5EoebeGw+FqgRxYGLto6L+kDaKEzEIQuI6z2w0O6w0EeYwzJNIsV/8Nh8Uh4bUWy7Efnv8KV9CZzBtPnqhElAcrAppHx8lGSXMClCc3UPaDw4V1N321k2rt9jnNPW8+p8wbt1+29bw4bfyyhsOKDxYENN3T8XG15lDeeub7/MEZiuerZS/1zlXM7k5B4umgxeg+oh0JlMYfJxJmurb6s2GSP27gqt4Jy/ETJ8dPBtCo94uj7gKQ1F8IlDqIv1t/y0LItS0CU2BQ7zBJ/ry0Z0IdHfXdYnyZdT1gI+tsQN42m+kOLrvzv2r0kSWFXyHlJljn0UZ4T67CIrP6WLBcx5Wdl/dXJcLks0mAtYJpLoTq7QHa80GahQuDXSeHiCOFCz7dH4aLbquuMI4aPFAk+GcD9j89npJlyUV+9kqoBaEZjLUw1B1ZSwDVUOdFHTqHx80/r627p7t08rIkP7CavQ7E7FFjmFtqSFENuCP+2xZ6mb/w8parIKPt7jsfkzpAbFgSf3nCfPirJZxTtzFtB5cKUFjnHYkm5TfJVILJ3rtJgQiEc1fHQwhYp1mvB4ho97fHp9UZK3WjrNNvSQiMbldUakRzASr0eKqTgri5WKu/kyF85U4WRLnNb2P2FSC0u6/ejQM2RMcz0iX1zMwksIJBe4gaPvt1PYj1/aR915hEKxF+7hHT4legpijyUGMWtQweciMJiUQuR1oKKpPZLhuWlSmiBALJ4tiEqWWtojq/QuMyfGHI1BiWM0iEnlGEba0UYwCcINFVdn8WzRV6s1SJeB2n+PxSvaSlV1a03QSBoFvjzHImm/zHmDJPQmzmwOFqJDRwpIZZF5uVcMGLaVtY+ThDwO3XmXeQaQFO/isol3lt1SIyHPrgqn/lzZ/Nfh8OvfOEwaZBmfcY0pcdAR6xok93gNvDtLxQgXSbEP7vd63O9DcP9Fj/svatwzibqv8Kn6DOsalvGcd3KcHoLPFtFI8k/auujQ08E5rn3Jkh/vIG06NWBHIh1P7mHyUDSxUrYJ6HzUWTOObd38tzllIXnBEdtOXWC9HLcayqXf1+D59BkKZGAKHKgKnvsLJYJLky1y2r62OoFjPMVrWwRLhDR+cpwxRMykqfuE1gUfngxgSjl2SIkXtnpnex00aUr80s4FeMGtxnrlXIbV76VzS0zjrDu+eR6brNwee6y3onfOhc6TLS63RWCzyeT6S5JB2YMITIEjvgtYpsVC2CHYIpfT4RpkpDbnFGEKXMwcNH3yF8OZkjlcS7FiNc/ztE5BJl3gfNker3OS7ir9F8myigcSuq/w+Hx2FAsc46z102jXlPwu8yixkZxk8kujfJkHVgVCd1ySy6RMwdZw/SXE5w99ZuHQvY3G5bWz6RGMw+sXfZKXd8Ith+G7B7b3Cmzv91pHPymLqkLWSBaFo3hsPrawEcX+ctVsO2lgJJRfBNSEkU+oqc/wEWbCpkZ9d3FdFmUtvWjFljiEXjZZJkXIFDioiOvuQJcj2ubzXJFd+HFcIV/z8NHFeL6DZQlLARdTMOeJS/zUnWcl/KMhmXS52Nzhs0uEHcnI/AlUvHFIF2yNiEIfcArawAvbLRKfu9qqmtt68vMnSo6yOXMJtQCp0xuf9J662tvKJzunr5I7LYHTjFH4TLC83jhT28a5ihNMf3dxGSVZs4Dn+VWKagPOEBdLnQ46QFXhYFxUUMuPO0QAF32hrcwrDP1XB1djWvMZdrpPU0aqfYE16KKXOeWMLXHDqHxZTSh0wyuhGfV5/vi3S5gj8XRU/UwXS1jziiBb5nAsSGr8V5FzQ6e/u2L7WYJ1p1rJkDLFrriR1fEkmSmh0BXvcYGFP7+g+TKnY1y0SfNPCm2+Oq5l/MS6uJzJ1+m3q6OqKpKUvAsu7FloKyUxozdH63WWYk6nFBbNRqWvx29PPbRKCUF75ELc/ZUN3FwVTSl9lMtqtxtQy7Y9TMyhL4HdvAYlXt6e3eyJ5djN395K5zuEJQb/mxNDbGqp2EHhRrMic499q3lh6GQYJ3RopmYEEuCG/kTb6n2xuLmEVV2mWEk6waM0SQib2oIty9RpAW2EhbmtQDbhGojALBZdDhQfLfWmZpre9XZDXdvQPd0kQotvNLUwvX/QTFsRa+D0DwgjTLykc2ET3eGZbaKP0yxDnwf3rcVs81VUU+4y1yzOQJpyyGJOO4c6jDGdO4YU+UWK5/PVefW1ybLfX9+BrIKOow9mngtYrtIKJwNqU6be0OlptTxkqinkveDgaXAL9tI3Fzh7AvIIfGbocOCeQuGaTd5Ys4qqhkraOLKGHP027jT7wgKX8AmUi4sizeuqC4i9+V7Bxc+0vu+cwbqIEmNlMYZEqGLBF8aGAmeAxRWBT8wd3sZNykSGeAKnV9VdNByhTgwVh0MayEc8tpgSh8e9jQxkHn+4mtMZ7t3/12Cp121k4GIiL1K4gbHRYkTEoaqLl0vCunNR/CUIU/Qdqve1Yoc4SHNY8iCDM7f7Mvxd9R/whIIl/FIsYFZt6uGc6ytARlStQUIs+AU8S8sKh4aDW1DBFuT1K9T3xxRJvf7IrXO4/JGdZG2KrB7gC8jTO1jV18UDzH9//f6Xd+9fvyI50XHcQHb3+tXzKsurvydk3YI8L2oy9N9f39f1+u9v31akxerNKk3Koiru6jdJsXoLFsVbhOsvb9+9ewsXq7d89Q6tFZZf/kePpaoWGT3j1MFpN8//aAD59D1POd747d+hwGn9DF/Cu1cqOfLbW77ibxLewl34/XWKKUtWI3kqh0SXAnyvO8dQkPTs9SssbvCl0EHkvNWib09L2wbyR1Am9wApIl/Ac3sH4vfXv/7ijJM5VLNGXZdiCIaAmX3NeQzclB7Xkdxx9Dhp3B1osrpHcpuakNAHLAYGhA1ctLk2hqwNe8KJ1Ni4lvjl/ffzfAGff3/9f5Oaf391/h83TOV/e0Um8e+vfnn1v5y70UlHzGI0A/twQvuGmIZJ3/3yizOb9ik63OjT1lIRxmp5IBHft4rGcpvmoHz5byvw/H+4YjKKnPe/uMucL+kKti9cBCG2Xokfiqc8K8BiTxZfP5w2LUjbUJOnfzQwJSjvUiwU1Yzmvs6Q+dQ3SsJRbWWluu8Umn72vfizx3dMWDwOz3fxCzIO9cKH13MuWUdeyE6f6y4xegxsyOKBTyHzeV5dlwD1h2iSFBoLMTdED99QSAap986HTZl45LY3+EudYuIHd8m8Q/noBnsilDbRL/57ME7ObFAU3TcbjNRCcXbe26+L6F29Lsbo6CVcZy9uvXVAO0aPT05iYzxOktgoh/yAkXXGNhAzgmxn73vw0tBV08Yb4iaKw299YyTG7lhtglwCQzf9mq0dZoAw2Qmjm5B0T/dkn+gS0ekU/19/dVcrW3+A2Vphcdvw2qei0i9yn/5eFGUdspKQQl7KVMrwjg0B6ZERt/mlr6og6wHbIa2/Bgk3woGo1gjrrktw0oZb7cnCY8ZE377QTPRff/HQYk6fyS0MJnjifFHF3n+HZtrQNVML7z224r4JNvxuhLF0YyDtYduOWKDP/Nbm7GXJ01WzIrvb0YpsIf22C5N0BTLs+ke/KuLDf/c3xHLYU4tm3bX7aPATtdRi7+MfY1EKX0629YPzS0Lstb2UaU+ejpt8gVYIWo9DXrqzNKv5096dFTyb8bmranTdIEWNSfnnzylsYsMwXFz+vPibGM9ee8JPF33qCVdmGioGcVJLT+9OcNWDutIfcYZwYX8PYSyJPVzfaJ8CJvkf/BVAG43F2ZDHuS404t/X54vYnMr+HDDoT+kCXt83q9ucMp18EP1A83mbBU3ARXObpdW9i8Aa7/Q2qodD7zyOKpL3SxDjFsJ2wgtQoi6Q7GlDEq4wjB2nI/GbPqYLR/dMuNix8x2NInf0qqwXzqPFKs2HR8siIOwZp3uEJmyq8b3gb/knZGleBG4uPY5Y4om9CB930xLuw0eYFupGfNzOHmVZ8TS4A+BjCp+CNsSj9bosHuECP4WcL6+aVcgsfS3qqPh6ZCTxKzfYwP7FQtkdXVwXR0mQPiEkPPBHdfXQRF8iIG/uQFI3JSzRDlN/RRrUZkHHauUjYpnYOM+rj+ldfQLKIC2rxxG+P3ePVXyr72F5wVzv9utZ//jFsNXrxaPPKdpRgw2xOk3w1n+0WHBNBnX/vBoCiiJEwoRNzfc8axdhjy5oZIjo7Sr5difg8zIuOySnz+u0JBbxB/CiwugS7kOSVhOE4dz9CVRXAGkiMMasspjc3QN8/ZAoQDQwfE50tCwhXPEBOh4HTgOi9nnpKOFHQ075MHnSIWESwofIOxrfxZAOPgJGJh18iO8Qn0kMb7aGyLIzNKn9AUeQUrZob9GArMd2co8vZvg4lmxDzzavHoVQ4LQKIiH3VlKAvQBynJc8f0RLDCHrT5uCNL26SB4iuQmxTUTwHT2CFNVNMwpngBtI2kev3SvNo433c/HUjrV7ODpsGpAOn969EDv6rCj7/h1DZBaFoD0GyQNJX4WvBwUebGATDeM7b2nIvuAYMClk++qOImNMTIuvPXCMgY8MHC46VFSAUzRVFLE5Bj5uXo6but7Y6p5LBkP/TKv7LK3qcITdWs4g4kskmBmfhw++E6RvE1RpEuZnphFE3zq+ZYtxG+jMjBMqdih6G1drhAdk3gOxOhuh2hgenrlOVzBCEB+N+zRfRMTcu5pO8xqWVTAvdtKLwQpHZqBOwk3aJrIXrlPYrt0gmY82T1jV1DOPK3wbA28lozIr6n9/2lh1WdVCRtE+mzcatVkzJTr6n+liROyfxqXNsCvFljkD4rgCJ9aJQZzT1f4lLgpFzMtg23LqKh15+gjLFzyr7q4XtnaI44W+eu5zPZauHdKPY1DBbnsNdhQMuL5AUDUlJNfq9WGQ3t0dN4ZwaAb/YJvy8Ei28Txtxl6JWyz0KKALF4IlDuCK4aZhEGIyxMR3dV+0nh+wCW+PGNE2xCrtR/yEMarA5251T6Nwv/j3CsYJ2lKHgEVXHPqm/CWIpVuNfoMzTHfoNJEImLozruqkaNY4MnoRNGsiFpVHxDckkBwfk10wjFE3ePDW7eBXtxY88lTk+yt4fMKZcOSRK2Irbokc5XJc1EgDix4jhfMkxV4oPdvFCZQ6hCMx4YZxzmwuUhJg4hGM3lcMUfBxtNpV+i8YMgImOKu6LtobCTxmLx9uh+Mbe4pgo+rYRREiHf4S5EuDz94vJDxWTGFcn9QWRm7thM9i3yLOZRQk92J/pPDpi0Rns5DA9lHsrejaE2WoG03MBEJW+a3+6hExDgtVLiEG9V98Qg4PiYDmSATUHRTu2ZKa94beeDrZiHuNsxOss/b3KKlkPzIP1/1QM3TmeNJeNhlUJ0vw2h2u1nB0B1X3KMeQGyEohqwP54uBbPN00XXBJogI8MFvkF4oI8q9popCTGfPiI28p7DCUArE3in5QRQ+fa5LQGcVHUOvpA/g9kSkGR1iHhmbT4qsKD/B5+4d7LjIty9ps6sqtWe+2pl1KYWXzKL9Tc2gDpxXZ8jkbjZXdyJEHI/MgfTFswMXxuBCmqLufWBr/2m4cQ/ZcJSDq+jnFhTdDyc3cU9uDqctxt7t3mnL4TBitw8j7MMo+rcT2wT7e7Ip9aPyUJGHmmFKyZp5bC7emj+v+swTQYFEu8qme8af+udC3IMDzQmIfbK6GtNDeyC1SmocgPesKFcgMGNVj+sKbB6jitXP6AmcIl29Prq7S7M0WD0md3SDdKexd/zzqj1D6FO5h0SAaDI3a7scxkKfQVXb7TM+cW4xdwjc08/FMs0jRdVifP0tcAuULscz9GOibpqDgCDErpG9berWHRGDfX+st+We+fZjVyZvdUY7U8RMGg2ZdA/2fElrtQZ5pOdMpMePNlp2XzFojSD0OGvvY5onHv4HrnpIT06ohAeBaR3JenoXE9n7KMj+K11fFBVOlOMXySbRru6LHMoTwPml2ewTZcVhbf/9z96oaVfBnkhPoyP4nYcjmPjmqm5rDdJrCaIYt/Wun4rPEBPrvCqMV3Heu/Ld9X0JoT3+v7jiR0sElmnC4Q7KWPkDBGnPW5W+2tvvab3o2YdL/ixrfxsOgaI9UL3MEfXaTGpBnvGJXuWJ8JKNPXfTStWeMPd4Wu6M++rtbQkfUyBbXKGugxkFujWfHmfFEuuze8Kin0G+bHCiQmce3dQMYlLp0bIgpe0cMTaPbToc7LY+1ijJzTtcJ5r8FHZLepM/PBbKa7CME6S6nffoD+EW8Q/mpzBxe/GyJ3J2lL24p9FJk5ELR/oTY/djQXKueAVHMFbPMrA8X6Gu4ytwRtq4Rydf1vt1o3ZcnaYNEL+qcU7rS1gVTbk3Kvjs+k1PTyOT++QH6pFLbw/4qT44h3ixusiaZarKRWt5qFw0iZrtA46OvsKnao9eQJydQyNq4OQVrDiocAaaOJiiSODtVG7/THbKduivBwOjR2t9j6bIsqO8etqbEDw8II+gdVIruj/PTyT27wL9KOqwp0vGvoOFiLYnXDP7Nh+Pe9qotU7mxTnRj7JFR8vzRLbVjw2saqTF4jWyDQeM26mBjH/sSZ9x7YksMNuCv/7qrjBHevopSjAwQtLxPcahYHsrDTc4HLgP1yX8M1KsrnrqQhQnWK7SCp+7XsIES9r9Zf6QfWiUxTTkHQhDbD3Xp8+YqiA7aup7TNeEKAN7Ne+z36yKFwnbz9Y5deUmItpOckbG/g3z1nXxAOOYkQTdUZLAqoqHlMR/oAkOStJkvejOirJZ7dFZ/nWxThP35dVVC8ssMvfitnrF1E5fuNiFizFTXJ0k64Nwx54sEDIgdxbtqu34AsHziFsOzB/Xxu7qfYceqaq+tpI4yBmF0+UGIcAXb3AvAl8w6LAoptwTG8mjFmis76YE2ifh87EsmrWnBOrqjhsS6vVKTeQTiq/drhO0lmMIFLz2ZErVQayM4uHcTfFEluWeyKidEA/7xm5zONVb4u0H05KxRLzASfBFza+iT9m7G/YDm6QgNMlBYGe4FAXBOQ7CLnGTmvj+UsP3xHNYMXB1Oa4jomrvHUbOnz5kEWvKEubJS7RL4j3CS7A5vjVkZv+b96pkj7vCqP0D1LGFGTLaa7QWsvM8yVBXLcnxVz9J3DeGbw1O0dg1bmx4M2CiETKNTjPSTjZMM8KusUlHhhpyWKzujTFiDAn+FO8NIDuDcGyaqlsem8Dqlsemdoc/ztUywiejM+LwIvGorRDRMVoTSFNvkLazGPlJkkv4BMrFRYE2uOonLCFircC8kvcweSia+qiuy/S2qWFsq1FooPqPVaS8SJ0SoIjwc409i5NGb9D611HGSCLisBmDn44pIZIjJ2j+WT3Fa9oRFjpCKGwicJeiKabC+ALziFcPcKEiXXBPTx4f42RrwshOn9dpSSynL0Ve34+A9z8hiDN2mi8/pEi41R8g4cOQeM0BTZfh8VORLSLNlYg8IiNQyI9B/hDNlOLwRltiNN7zk9go+xxZkdGe34JIG1InoYlSEDOfKw7MQQviX+0j6vi9Q5Cw77KPgj4au6kauIRVI6bT9ZRGa3xhOj5xRMQRe43szkEnit/1iwbXrWBsd+oFSGMFm3MZsKLQtEOJbRWc4LJpXYaj+LrGTky8vQcatLlwCVcgzakgdY+o8E8Av3DbGbdfC+yWT5iMRBGPSnqr8HuFdPtPKZquvUm8OPsbnaSqe+tdtbCArAlymHxM74iyv4es0w/NffI2NYPm73sFFz/T+t6ThbjqwV1hMjBEd7dMyax7wqC9OkPNMtrLPe7tKvCEHBj2pA4/LBrZzXde9V0l97AAr714Cg1kmq0jGgyXaLBr/KZ7NE1twBjvzsgVzLFeHauHLbp43fsCq4q6NRv4oMhmRohuNtuTQ27xMHuUVGYYT9Q4lhnVtYt53yz9RwO6ifCX1d/ztCYP74193Dg0NPrp4hSjmWQkY5+G9vZSu1+PTTK2tbFpN9qRXfyjug/FU54VYBGcjOi86lFFUYw+IxbPK9jjVOqEVrsvkvo/Ybq891ILbVo4bvJFBj+AGsRxEHZPveOLmHF41G3b/4oTfOzTtj/LBv2VSpMSdgGzj6Qrhqcbt1x5pL2b++XlmT3quCVqUPAqwXAMMoDfFQhR36rRo2piGmFxDwNo/4fHk3J8/REelMMHOav9eYd4Rkl+XYLkIc2XEc8ASczbuEoJOciDsU4aP8AsfYSlzRuR2/ImWL8A9siLMQzJ635HWzPC+ZToNx7de+Cge9RNmV/CP3C2un2Z9vDrUpEmbnb9J4bf6RKCqsjPirLllThKesdxkNi/Fva+D1LbEASne0ZcsuTQF0/u7r4WsaLcdS9788l53V9EoAVFjFth2xJt4rBBFiTU5wSUe7VJhkvLC1B2ikOQ56n1C/kds9J1w56/3kxy+PHq7PJ/5mOP8S5LoNUJS8T+2Lk/mj0dY/fcOTHXzfzVGtGwD4sb5nFPZN7My0JO229rNhrX2lbRYgtbv/h6xFmaIXIHZgVuk2t35AvNrz1y1ngdQfdkAciHGIv3tibT/CyMsicssjOz0AmVa7DcY8o7vmntSrwfoEyRrjPwMLIib9Mc7JPEe2hcaGoVhlmn+ieMfV7sAXlzBxJ8CahEZpXiel9oK95qs9WTDqRfY2H/mS7GQ/5p3LgHUEFCef9jQKdm8A+2KZ8HFKoqXeZoBaaEKU2vxL/zcC31hyU4l6DHVRKmdojt35t7OP7NvR9s7ZB+nFfhr1bsneGP9oPkIYZBTmyab0397Y6gJIMcI0WeYmfdk+00lL/6/we63HwBxPG2YcAbWm8cuPGdvxcswM4RMQRnm0efqEdyggPm/2jSMiyIkFL+8rossnD3p1Lxn4BP3hunxXUl79sSPspSEP2RgohWY1A0cKiYbu/j7cdEK4boLQWltFIVxpTj83Gs1dFEkRXl1R8NKGF1ubyN3c/Wclj8E00TfcAb/dCjDbOaoKHzCmG6ghlMAqPfI2wzUVjavOWIfScNhe+0n9P8AZ+GyZSy7Qk2uk5h2boe9kqwznaMFC8tNH7c0P+Quq0dw/QOikKIeBZsfwSA52BP2DmC31lyZF0K9n8wzqsqO80xfNC2cQWTpoSO/bPyItLvhkTzkn0ulsXghBvFKnPE86leZcfF4iVW6itkdSLG7d15X2H9VJQPI3i+V6B8IUunT/HtI79lWAIjdQjK02c0ynwJST6p0P7JkTl00+GR5Rb7n0USerxQpstQz+J2j3sdM0d9Jy4+FxiBA1Hso7nOinKFpoyKMImE3vsdFMnyjPHC/P6/PPOhwMnZTvMFmszQLdBNB+vcgnsigNoxhu3zLY5RlDpny8PeRLyHK0gMboRqTyaTjCm2IuM6rzY4mSRkqve7nLGOxyzHIHk4z8kxHp3bdE/YJkZg/rwOitnD36e4N0lH7FzDFdrg9+aEaJS9A7+yi7ompIUPRjxymOEJoumyKF8Oc7y/c9yJvcMU7+8UE7qmyb4d5scwGNAQ2sdoY08pQhyf/2S6ql+kwXgK6lGSXcKk2JukuFtulk5xmmV5tH2+2DjLIgYVfi8PDDWlnyNr9D5JH6Qxwnt93gqz96cl93DRZPAaVA97wmcWDn33iTSHI/3q4S1ByztfBKUswzXFbllIQMID/4sZwd886CI7m7Vo/St8ri+b/KarHhQphvhn/S0/LcsiKPtf1yWp9yBgRB4BEp8BybpR1r59GRCQX6d5cG8Qivh9eedJmSZJYFVFyPNCOkSzTbTz+/PqU7pA8ivwVscSvzd+AcuECs3zuNbTY1KkDBzrqGZ4nXJP9hXvB+TjvGMvexHKdQSOuoi9XxSCqilhl7VvP6bbqEa881EyySthaGRPWK+Pjf0Sc+DYwQFjOWs6HvqABHle7c/JzoGNpmWjs6JsVvt4QkgNKepDHbMf3JEZC3MLXBfrNJktRZ3DkQNJ/9/rYXvCmOFH12dlsfJnQ7Z22F3Nwr8bdN2wUP/m9p8wMWR19AhKxBdR4zjR8R1UEKSJnledOnv80j7hGwnZ8JhUCL4pRAFRxdGnz8W+hLFRI8KteJhJPIIdj0exfKZ7G1mSeBX3gy3HVZWN2v37SD7VWHEtqzVIl/uiE8uo73ksLN9z/ZDhizKzL3uJu9M3Qt7BhCeK7Z8tpMZnjR8nCXmQs/MNilnBdS1YnS1ZaJE+rm0r5p4q81FLwkT2MPlUi0N2qpcvcKAoyLKXEDxHdQ2Se6xDvDtLhSAHN2JvcL2PiOsvjrisBcnX/7+9K2uOG0fSf6XDj/vQ3pndffNshCwf7QjbrZHkdsy+MOgqSGKYKlaQLMueX78keOHIBBIkwKPsl26rkEBeH5I4E+ypeM/qjnuGKzmwcl7XdMC3fonbmE9FqgkX8Qajdqov7+CJYep/3O+zTQ8CXj+S0GqGb7N5TFd0+cB2X7PT2eUrCnLCZ5WZuPhbVDV9ex7ZR5vvitv4++vvTNB0TDNVI91B+Gkf1xmzjQU/nzW+a55TmilNuRHLLHoT3jOJTw4ZvxIx+e2BDm+V7U55/RDBVfyjNsh5vZWqaufee/QWPDxet/oNNFXrM4HD5Y9dyuRU2qM8UDdzxfIkw18FJ26DlXHKW5t0NpUf+yO8ijnbSWoPR6jeHZIyidORT67KtVd/3ZXnAUn+zZPNcd5n0t/AmwiUs6yqPaLLajaRnfBDrVMO+PsR6L/HCMTzv+BXxPxI9l/K6sGY8VkvaSgh/+5NyNGX2By+jinvH81E8kz66lWXneBvUz5FfSt/n3/WSZ8x5llRVGPz9JcH/XjQ4UN3LqcwKk3es28snTYAvHnI8hK5LjBuJ/NNRU5tkLT4dmx3znzvm4U8N0K6rFFZCMhTOcpIFVJZnrPcV3uL5CKjCPYmr98qrnMSTpk+NUkp/4gLW6IDT7tcN6xJhHcekSfINvkmbv4L6+Q/iy9XmOyDnwE/E/s3h9ZIx5tIH8vDLj3t2bvDTVI1G/czunF7S1dxUdSH6aqCcvIycteYFz1vk1JPWzqqJW+nXD6wMm4PH2pDldENTnsuBW7Tn+08HqW5ZtUoK78oPif7ezZpL7Np4f+ygx8dm+Y+5/GxHTUgstHbqh9NlnwwXsuXWR1iJ/bLPMnyiUn6uTr1U63uFidH/eqjwk9/TfkAd21E+pcApO94AfT2L4fEzG2CIVSdtnQsyu84xxmqhkgt3mk4YQHE1Zktq636UhTfTYKhZghP8mMt1eSm+rQ8ZPvLWtVxnVNuiNxFG4ajvApwdLOs1sDEeySDKo6dta8Z5hmAZrmjW4cZ4duuCapXW1ajvCrxGrewM9WTovhuEgw1Q3jyiuWPSVEfLmnyjYnJ0ca4VW2P3GkFvqN8DDJ2jIp6E16uofU6jYNeWz1kP57qddfOPNnbK+jR6/du+5Vv/1cPxsd0aHSMZCKvmI3rxfMOaVABOvlHydBUDujRa/YtYU9/sPR4d0oPIz/AHpa+KKdevLijUXi0Q7rqk0T5HBetxQMcGH6ZZvfdpeFz9WWrHykVAWmdrrLZVVaMOEA51AzRSev7ID+LL70tHPoGR+2EdyVTc8sQr/M0NYNE8CxN/8rqrb9F8/vOELkrRS8OxdOY3XSxbvDP6Pl6YNlHYfzFhsZP3kJDneJseIJu1IuYzTjgX6zgp6U9NPUxc2wJw/pFUWS7hHu25fDPEzuxfXNbur+9G9W3dxXkvz7sf6tnI/Xrbk+HNBuy/Nyw9O734ccPp7RMjmmyq/j/49l//v77356pXebPQ3Pe+rf64HZ9ceUyLnbxXjdHpcYeEwOUXJIJoZAF/A+NKT8bU2/PxulldijKPK4Mrvf65LBLjnGqGkUhJAaIWte+SbXkFTvWu12H0qg5hXF3K1tn3vNQHGAzxovnAqbIUIvE6/ko0CQi0bFygezP8GjDMBYIWbipgqOLxE7NtLA0toQwhqoyzbE/RUBzhcFiMU2ouRj8urW05mHuqP0/irmuXPRt/9ssWJMFlgVRioKgC7RQGGQp+lA4Cg+srwJO4J635kzIiz8LnCALrQdOw1RrETj1RzM8xiU+rld9N7TUH7oRmxp+3HpMgc8UYe5fOJr0B0oujpXJ6+N3rfgJK6Kb7JTvDHO9tq481+t/nCW4aAe5IFkCY6tXeQZwmQ+uITzNh5sWh9ltnNfHNL2EizOGmZPLl4CZ4YDjYjBrP6+bimXQkE0rO5tI5jI8W2Mg6xFmCWPLD8SXx9eMQ/Ex8DIeWJkVXd0/2rRu9XqHelJjnbFLEBgURyo/mxgmauUSx5ZeRIgqzyffWP7jtr49i/pWJJKcKhU4zQEdoqEvXKCqhsGG03dNkG1pTPSjOORuzLoG5orQEJ7OaFiuauQyKl8NrjazYrkgqOZfs3RbtVp40bLOk34X85WzfMaFS5Gt1JxcsPUFTEmbLSxitnCUUIErMdWhYYMOKhJYHgRqzv73E4Gc2YoVVoW9zXzhVgC2+b907uFt4a+dcjH0mlWtJXUGjeamJmGnRm5B8rRaNAv84Gu+uFg9RRAIKiaYAYmEa84IY8q95pVg0ratA3gd8fNPgUIXFCwGP/yC/Fy4K6t5SiXXt0pVFoGirwtjorwytuSSzWNKUscBS4tBqbsd7xFElvmjknuSt9T/tnUAgAkdVu56A2in9+HVgcG9i84ICUm45U7DdTf8+wcWbUPrroYcHvofZ/nIqDlVIFECx5lO4RlQZcwgg7A0Z5lYCFmWAbJDgDhbXLn4eAFY4ZmF5kbVyyRNqxE9nFVpFKZsh3hd4t520UDiJRt/eTS0ySUjWImVfryUjJiSKH3Z2Xy8Oo1cPl6L4ap5ieSmzHnugYIPiKLu+RkUXD2B6NHhx1nABUkuywMSBIEZbLAwMAPVovAVHxVaBGtdgpVt4KuTVpJh+HHzOOpV2QR2uvwr28BOJ60kw/Dj5rHTq7IJ7NSJY7aBm1pSebOW/7B5vHA1NoOVJslQpAvt6qmAKGlk1CTofg6zn0/1oge0tIpQuNXky+FFzQ4rZvC0rjCqtWV/aoXzoMuU+9ckn0wYBoGqSeZAIzkXMoZPUvLjFYDVtquPAcHk+J8QpM4AWRqglrzLc693OkXQVS1z2TrIjOCbdcFrLOBWB7YNRcA1gm6uiDcZeMtGutff68fg4vTiVD7ULe54ofJJXnXEM2kgZ+wzEm4+AhrVcwHkYlh8k+WnR74o6xt4+P5iz1NqSfh187gYdNkOCPhrmXOjoHmiU4NB+/N54AB4hnTVQIj4f9/m2emIokAg0ZzX/jzLh4gz1EUIBB3MMAHBQ2I0yLWGEALI7e6xkHhZIOjQfTlvxOHkKxh8YGJPcV1ICM0+enH066zjFy7WYiDijxEEOCRnGrtIL5bzRtpftn42rlGDwkg2+MLe38SkeV7QzDnMpaNm8RFul57pUxHfsz+SSpr8RwTnlVppGi5RclAemeBsEnFJalH4Lp6JC8Qa0FfcY8RPADF6TFkKX1zCZT98/ETcmhHVS6lLEPAo3GzYGRTZEmDWn8NkGdjMmK/EDThLZyl5m9yVl3G+j65O9QNrBdt/TsoHRIfxbrRcS+qkkBobfgwXSch+mgiKXhcSJkBXLA4RaawDKzTWpYFiDSQ5KM8Mgx4nBHhCm/O4p6u4Lqx9ErvCSkdDa4LabGOk0TiTPLrssOljVrL1j7NrKXUJml+3jaFBkfWPs6/ZU4X2q6xqoOiC0ybWJwHBJXHA8s2vXUJabWIlE8KZ14+gZTi+HrjMFobGYkVyy7KJ/fj7qGv+knVCasn6Ar4rMRuAej3W/xnrROUrRrDcY70WGDnagoNcECzPKNmxnkBEXpboKyy7Sdulppwvkc2vbfpnitEXHK2Up/xQP6bDApw1DjQeFkRWhjZSyRmMgUV9NjH67S/wzB9VfqXHQsPLkmPbjAtyGefNkOXl6bBPmXF7ot1WESiBHS+x1AkmqkTKkEQtDLmjhdkiDIA03Sg8BxHXA6FNfKXWALM5v1Wj0LX450rD1vr34NcArBl35EfhaumN+Zb/zZHtkrv29uFFWebJl1PJNoAxo/yQXBjl1tFnVm8LUIRF//NY/xlR9BJDjx0PNCCECo0GXQlSdoRhVqLGQ2hqBDUpS5pMgA2sNLjStQ0NnJ8yNk8G2+IB26TB0pjvhg638b09h9laRhGDzJAsYumZjBYElRxGCAumHwPAZcm4Y/Yp4MyfAWJUhy+Jsop8caD9FedJfCj72HqZPX5JDpwwwp+MF+/DiETSPRipwGlZ0C4bBC0jeZgrXqiBgqLNpChFClHsNeNvM3Pz1QN1/u/uVIwuPVUnwPOfp6qk8tKnQ4JjVCISsSAXnGV4xA20auiJYq8Nf9uNiRRInm3023LI6yBnX5LUCAGXz70MuVo0zrrs6AGWq1lrVJX4K05PPUjNGobBxbzI5epSxGwJQ2J4FJ7CQLnR1gHPagNLw3rx4SRlyLDtgeHmRn/RTfx4TNmr7OmQZjF+RbgnkNZf+h9XjgZYveWRIBt/MSzcJiyv9NsxKQ+79bwVJ6LmX+eYCPIV66WXZBF+PYtk64M+FGaidCuA1fqnlMuAaMZpoxt+VjJRHI6jtgeJ1w8kTXK308zbA5auEoWp5M4VwcwrwKxDoF8oMTNdHT76udybJC3rfLfBrzfMFaYUzcxCasTnc23Cqis9ui17jaLM8nrglTzG+Y/X33cP8eGeXccluzzlFYvdD8PwviWQh/bdj/Qgx0WQz9M1vwQakEN6hQFJowctgqEOWAc0+B+/MLEAJiTLL/cafLz7+u5QybL7enP6UuzypDkrvolbV4jw8lvxGM3m72BhmrksEKwOd+uf4a0PdDOO46dgbunR/MUuBR7c9bvMGQhzvejyxfnh17NY4xz02cwa58XdXZJWv7BopjdPeoZyQ8OvW098MKhCYbZ0QhX+RE/QgRMOBY23/hTSmY14dJ02Mdap9PtWAfpDBdT4nkVv8uxxPpTIzJVxk1y0eXwoClE4is5YC0Bus1/wWAk8BlcsOGwtk2+Vyd9n95Hw79pR+GBDoZNHjUrZTCPYnismTaixi8lmYYAmKkUaxcgirgJqm1gAWg5Vc8YtVzgtHrIuH9jua3ZST9BqP+PY0iglkOmls6ANVsssWsgzshZ7hgEjoiEJl2rVBfNg7k55pfH9VfyD52J+d0jqdj2lZMaHWypjJaGlWrjtPMuaPiSegidWg48us7tZI19+DpZJFVTKKFrYlPHjAOIXlC4Z5NW6i+HzfbaL0+TfbN/JGL2PD/cnbVYjeL8nEN09/DgLAjWxZWH00iCog00VBm26ThSmnYQL4sv3BCDc+W917H8eY/7NjPXbXb1r9i1hT3+w9Hh3Sg91EuLaJUPjfNWzx1T1e1kPu26zCKtvhV3bBoi+vsz5SKYuB7DTDBARbsBMwlyn0SzLZBSPOPFeEodSge1cQUuF+nwxRPk9KgAZY4Vgkiovd1Qlze4vs0c+TyQGNaHK3HFMZC0dSRF/P6NohZraid3MMKr/fZWprzwoXuQEqgubH+c56OSOJE9RCjbPOvDTybYYjj6yp8IxHAlV5g5HImuxSen3MwpHqKmd2M0Mo/rfxkssPYHqwhmvrIxAkqdwBJtnHfjpZFtu3J2l6V9ZmRzu28OV1HmfUm/2+Z7KXxqVa4VnFKLMlnfiuQS+6h8uDsWTYWVKIFG92v08zy27KRjzNeNDzLUibA0irmP1YNTS1aLrVfQFi21HLk+rF7MDav23WkbCaXs3WNwxNO+9ldc8N1TdqaoaLO+W+bM9e5PkRfkqLuMvcaHv89W1blgJ5Jh69ltTiubyutk9sMf4H8/2X7LK8/GXVGtAi0IqN3Zi+9ePcZJelGW8e2gG4wBbkA7mD5JaBBkyUmm8hyKI3VBKV9WsoFUtK6tG+d0uO4HGlIshZjKFhVu7pf2BlQ8ZZD+lHOKnkNgYWrMK6DJYq4BiWWu5SkoRjSgLlTnO0sjI3mOSAgPYUAT2mL7UwuEyLtk9P0OicRiKIA5Dqc1Eya481ZfIdRN1JaCJukKaBwxcFAKDP6KWKPoQ83fuyP6pnwVOcoYEVpDK5DWJ0CaC9MyCzlsqBplKFDRrG1CjUZjs3VGRDd5W/BAfTncx95UhEslUJjlESrIsFiHs3GUKW0dVJyN6f1UpwG6rEhHZGviZGVk59NcsNQZ9CdR+X2hToO5QcHzrSkDxu0JL893jzuiXWSWAmKk0Np5l1W0q6H6rBtVQn1fKQY4yiYXhsLWl8RqKIDZDqYXDcNRN4zAUQRyGUhuH+qwXuylzvvpRNO91AdxAMpAzSGmRYliV1zgPRRC3utQeHYUlND0oCoVgLBTKCXwQDnjb5FDT3NxHw01TbAo5DYVNB5Y/JkVRze+6BT5dH40E1E2jss0cvlfD2kOcXpzKh3oO2rxuh0phJgdnFsYaFun4LV6kswtlEF9eXESkDs9pb7NjssP4tIUGRi0FhRPGxNh+S0Fp/22enY4Yk7bQwKmlsHBq7zNoTNrfofbbIuIw9lN987I/VI6OY2Uy00BWprRI8Ta5q4aN+d4iBUwGSQFTEqUwcDZzo7kR+QgIZag7SRNTTvkxK6FIKpShTJpiC5Nr9lQpfJUlh7LAHQZSQYxBQsL4C5l2DUXYmIs0weoIEY/JxSZOJL9ds2o6cKgngAwMwUo5bEaJxKqe+ko9oKJKAqupUtHmctiTuNisDqM3ze+Q94mpMz3zS8O6vYzkoO2MNUZJR5bLRSKqT/nznpgDeaHBW7ycxsf4phnG31jJIJex3jh56UJa1q2UN3pc11MIAtEkcUYK8moN1SwtuYPX2hoW6YTc8pooQhnEVyi2zuMzcMWy/R2et2eENdEh9ycwi+qK4BlUV0oRvQcZrEFfjCpCReltVZ9xR34Bp4ZKOegVmcS24IFlCNTXPzBKcDkEI3ZYabxlj8c0BnsqTGZbeRwoiVsFBgl0EtPWAZlz92HAGWsUpu8Lle1bdmBVRzYFR50EnBpoVLY10SF3oL4qOpSB66JDsYXJpxxnIpRBTIRiW6Coau9PKbuNi69QoJCKwUAhUdjsNmTa0+02lIF2G4ptvZHFRdVxPrPk/gEaoivlYP+TSWgMXyXVLKKAw5BOYmArUFHWNiwREKAxrHQ4xT01xxXQ7WUC06qUQmrtgUKOGqAPCqVwLxQI6KyalE8mdg2FhWVDZA3nj8c4uYdcOhTB4bsrteKW29r0xVIpYNQqRIQ19vesLFluQS5GiK2/Q7Q2I+vJcHRr6zSg2XUyV+bY4BojJIlBG1CjqSuAFQ6EEl7rQIgdxSHIQROAtg8l51HQWAM06A6UTGZVu+5Be/zUikoAqyzT2FCYZ0VRNZ7iXHUSEHkaldXUUOxGYzYlVlf/KpEpT1cCDmK6QttEJ/5uOFohlYKTHJHAxgrZezFsu9D2W7q1f4MiOolpEyG6OB7ThO1vs5Y+Yfat0K4yDjqNgiZDS26XQN7Ux48hIHT2cwLXrKhCMD9VTDgG0e6N4qctNArTFmtLRHCEuj1q2eU1k1P2X6OalLwA1p+HoW0+0zehpTT2rutxlGVU4mpqNNC5SgGkPcCEAUgNMgHUhBMo/TVCfRFGLMXOofQEhOEjzkoqxQaKVFb6JSPdvhoJdr5CpnLxsM2tBF9qDIXz85aT6NGbpO5LQgXTefSGWj3oP9yUUM+YV8zx8+NSTeMxed4M7dT7c1l1N7NE8qlwo1FkWlwx6KQ618d0Ah0zDWYQv2YQ/C3xoIJErORVrZUgRj7THPVnm3X7IJS4UspBaq4GchYbuqIEVleKvKvfjeDs6sM3maC7Xrr8q1G/PzVt8LtG49/j6gCfV0TnH+5qmsb/0U173lLXnFLN8P1Qblw03w/kNgVYEzSLXhjWPLdxfs/AHkGp5snnKzNPN3Nzwo5aKSRyoNijlYU0jAtq1Er+Q+pCZgEu4RRGk4AVQuIEuG0kNWK6PjT6YxvJ94TQT61MZ1AGuLfUaGG4jzQWV+PV7r+jQ2hCVddpQwRS5AqWqL7HMKqqZh9raaTBBlszmUC6HGYYcoF0/odd0LUzXtl4nWy055WrcSILFAJ4Hc9qQahAGwLLg5jJ3klA8mAdZWaTyMvUUbdOzdrFbfPwnV4ZVxu+lMc1Nl+1M7SiLOADjWFXBoMYEB+q0Ssbvk6Asoh6azGZeKEx6tsFzAMSBjAFdEmzMYHx7qW76u1ejElplcS/usq+Eq+HXdEdr6JiOlxRmdC7l2ZUvd9P6rfcDMETJzZ4XLnh3bgcub0N1oQMoJWFMAQeBHFiL35cgxleJmlaBdK+ZYMRFNJwJiBhaILSXXq3oWlca402ZBdQsjJIDWCpFtzNAF05H96WAWxhroCrpV7H5/pgV+3lmoZr9k0rlGvz7qbpMg0YzaEThTCBmi2B10QzIbir2iUKMKqqE4VQVU1nwGsOP06fXGVpalRTJgihopjroJkzQbkMxqnWpFuImhZh5USSiUJqNeRUEX09JAnECBXVkznikRjTMIZW0aAcktuhUdGSr8HYEnRMCGzVmIvCuynxgRCtov0DpylsUnCVJgQPZpEG047o8zCasBl9HtMQhtezo2kpE5lyqhiHo7SKIdBEyTPDW3PKF+Nuuj5rjNFOAFUIo2jpbXhV4Vc/6vKj0nZ9FbJgCkuHvQeN4QPdI1WOxFw3iL4ijUVkKaPOIDKYKUevrVf069m2SaNbG5rJgi7jTd4TxWZNPVag8yj6zP2Wp70hrKqAdP6XVKRkT7wWnMtprKKm2KRQhAhL4dSDck9Fw/kK/CAHXMFwgmHyQQ4o05XUijFzlSfTtHYn2gV8ntzdr6swR59NC7eBSuJXcS0T2FDP0wLKIL9hT1on8r8THV7VLv9adFXNzx7igu0/J+WDwEFX3FbFmzpSXTXDHK+KZo8bbwip3w7t42aAK3hSBKyJ9nhS6j5Ppvkk+p1sH7mW37iwlHn6zH+WgCiQBAiIYtbCoR6YkNBdRSDHoHEoZKQPMTAy5FTkjVCSJPoxi7Vf2Cv5RccypunSN+J9QqHwq7Sa4PKmO0Xj6XivmJ0yGtrFtZQJPQkO1tS+r8ZMmmMnQt2JJOuUTyXc0pxPyglqCXggZZhQB+Q6bXuyKYXphAVugrNRWv/+Dn1sQk3JGokv20A9HCe3zgj093fEuQH+pI7S8+FktG3vtySY9WAgU8/AiUN0jsVNYZgworT+541zm8GYZJhwsNtS0b+BSFmUxdaImX1HIMiQxBjJwAyCa0QzBvzY7UIzCKFVOSG0oW0ksbNvtJqZu0LY2NrMplppD9BTrhhPueDUwcKEnlhGbEEsDWIMwzkVnNqqFKINoMY6TGJIJE69ZUluwrDq7usCJiEJu2hRlzTqQUxs/4hTagfrolszp/wW7SibSk3gBoKezeWmMD2Hu1UTj4Dp/NikWCu8iYzDSXJdq/JGrWnjxlWakSdwjVC2ZJOa25nFKJR2pSS4psbhxLbjB0LEQDl7NAybQCG6iR+PKRtSsOGKK5SGocvoJG9hVe6f/pDOxUIqI5T2haTxJ2e1J014dfy1kinqG75dOpH/r1V4VbVlzXat1P7RRqsE+2iPXZn1YhYng5y1KZR37S1r8S7V51mbt0lkbloj9nDjP+MZ/ZLHOP/x+vvuIT7cs+u4Gp73jxgBi4vWSqYQLL+q1IZf+MUkeQ1MfOipWewC33GaaAT+B1l7mXpbaiMPKRm3b6x1QuziWB6S4g1RX4XyZyZDULZV8R+alzVR/1ySdciGUIYcsmnPQDU7y+gLTyPU7149igw74jqR/61w7eWmpib6KNPIaw3kaGGgDhEn0IeWbvprDp6BL7+HFL3Js0eTPUzkIQwCP/3UBhXjS06TTXGbORhCIN66GYQHpCLtMSkoHuLkppAGPnbVBjbjC1ZYO1gbnk1iAgVIFwIP86gNv/EUAS9QAcenqHUN5sEezWrsZHsHy9yWvvxHfNFqzKE7+W2o6N0hKZM4NRyoNVXAlRx3mBZ+AeumOX9nedVqujG6A8M6K7td0LqB1DW2BJ9Epj1LNjIFkPjmlz3/D0yNKzgx+Q/0tlnThPW5sjHmMAfm4AFZDcR+AnA7b9LeyYnq7+JldijKPE7qx1CqYVivX5fw6jaLsPqGlSb/vOzmHpE1C5pyou8UiXNQ+5tDHt0kFTgZXa5JVB3Xd9XmE15looJNesgJWL2Y1mJ4xALvWDULHIYHqqYZdkg8ZjZXT2cWnpzYbAG1hae3qN6XXuvSDTSxxfB4Ah4r422YXiGbZtghu5vZXD2dWXhy9rgF1FZfVSN/EbXX2IAPgK+2Z/jKIq/UNZ8H28tzHswuJqUjWFIgNyvllP9uWZOIH+FRAzPrIGRcqzOP8egDnclGtm9cK4TBNq09qv7iedNIbfjKyyzvy148b954bH+o/qwmrNV870O2Z2nBf33x/PpU1X5kzV+vWJHcD028qNo8MP4a7dBoR/PucJd1sz5Foo6kK+4e3WBlvI/L+CIvk/ppgqp4V406+eulfH2mfr7wC9u/O/x5Ko+nslKZPX5JpTnli+dm/i+eazK/aO4bFD5UqMRMKhXYn4eXpyTd93K/idNCmUdjTVxW1n/Lqt8bX1Zds2T3P/qWPmYHYkOt+V6xIzvsqy53yx6P9SvexZ+Hm/gbGyPbp4K9Z/fx7sdVnSSdLzdhjdgdIZv9xaskvs/jx6JtY6hf/VlheP/4/X//Hxvl9Z21UggA - - - dbo - - \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.Designer.cs deleted file mode 100644 index 532c012d7e..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class ExportFramework : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ExportFramework)); - - string IMigrationMetadata.Id - { - get { return "201508091512101_ExportFramework"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.cs b/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.cs deleted file mode 100644 index 42cabf6f1c..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.cs +++ /dev/null @@ -1,480 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Data.Entity.Migrations; - using System.Web.Hosting; - using SmartStore.Core.Data; - using SmartStore.Core.Domain.Customers; - using SmartStore.Core.Domain.Security; - using SmartStore.Data.Setup; - - public partial class ExportFramework : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - CreateTable( - "dbo.ExportDeployment", - c => new - { - Id = c.Int(nullable: false, identity: true), - ProfileId = c.Int(nullable: false), - Name = c.String(nullable: false, maxLength: 100), - Enabled = c.Boolean(nullable: false), - IsPublic = c.Boolean(nullable: false), - DeploymentTypeId = c.Int(nullable: false), - Username = c.String(maxLength: 400), - Password = c.String(maxLength: 400), - Url = c.String(maxLength: 4000), - FileSystemPath = c.String(maxLength: 400), - EmailAddresses = c.String(maxLength: 4000), - EmailSubject = c.String(maxLength: 400), - EmailAccountId = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.ExportProfile", t => t.ProfileId, cascadeDelete: true) - .Index(t => t.ProfileId); - - CreateTable( - "dbo.ExportProfile", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 100), - FolderName = c.String(nullable: false, maxLength: 100), - ProviderSystemName = c.String(nullable: false, maxLength: 4000), - Enabled = c.Boolean(nullable: false), - SchedulingTaskId = c.Int(nullable: false), - Filtering = c.String(), - Projection = c.String(), - ProviderConfigData = c.String(), - Offset = c.Int(nullable: false), - Limit = c.Int(nullable: false), - BatchSize = c.Int(nullable: false), - PerStore = c.Boolean(nullable: false), - CompletedEmailAddresses = c.String(maxLength: 400), - CreateZipArchive = c.Boolean(nullable: false), - Cleanup = c.Boolean(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.ScheduleTask", t => t.SchedulingTaskId) - .Index(t => t.SchedulingTaskId); - } - - public override void Down() - { - DropForeignKey("dbo.ExportDeployment", "ProfileId", "dbo.ExportProfile"); - DropForeignKey("dbo.ExportProfile", "SchedulingTaskId", "dbo.ScheduleTask"); - DropIndex("dbo.ExportProfile", new[] { "SchedulingTaskId" }); - DropIndex("dbo.ExportDeployment", new[] { "ProfileId" }); - DropTable("dbo.ExportProfile"); - DropTable("dbo.ExportDeployment"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - var permissionMigrator = new PermissionMigrator(context); - - permissionMigrator.AddPermission(new PermissionRecord - { - Name = "Admin area. Manage Exports", - SystemName = "ManageExports", - Category = "Configuration" - }, new string[] { SystemCustomerRoleNames.Administrators }); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Common.Enabled", "Enabled", "Aktiviert"); - builder.AddOrUpdate("Common.Provider", "Provider", "Provider"); - builder.AddOrUpdate("Common.Profile", "Profile", "Profil"); - builder.AddOrUpdate("Common.Partition", "Partition", "Aufteilung"); - builder.AddOrUpdate("Common.Image", "Image", "Bild"); - builder.AddOrUpdate("Common.Filter", "Filter", "Filter"); - builder.AddOrUpdate("Common.Projection", "Projection", "Projektion"); - builder.AddOrUpdate("Common.Deployment", "Deployment", "Bereitstellung"); - builder.AddOrUpdate("Common.Website", "Website", "Web-Seite"); - builder.AddOrUpdate("Common.DetailDescription", "Detail description", "Detailbeschreibung"); - - builder.AddOrUpdate("Admin.Validation.UsernamePassword", "Please enter username and password", "Bitte geben Sie Benutzername und Passwort ein"); - builder.AddOrUpdate("Admin.Validation.Url", "Please enter a valid URL", "Bitte geben Sie eine gltige URL ein"); - builder.AddOrUpdate("Admin.Validation.Name", "Please enter a name", "Bitte geben Sie einen Namen ein"); - builder.AddOrUpdate("Admin.Validation.EmailAddress", "Please enter a valid email address", "Bitte geben Sie eine gltige E-Mail Adresse ein"); - - - builder.AddOrUpdate("Admin.DataExchange.Export.NoProfiles", - "There were no export profiles found.", - "Es wurden keine Exportprofile gefunden."); - - - builder.AddOrUpdate("Admin.DataExchange.Export.ProviderSystemName", - "Provider", - "Provider", - "Specifies the export provider. It is responsible for the individual formatting of the export data.", - "Legt den Export-Provider fest. Er ist fr die individuelle Formatierung der zu exportierenden Daten zustndig."); - - builder.AddOrUpdate("Admin.DataExchange.Export.EntityType", - "Object", - "Objekt", - "The object type the provider processes.", - "Der Objekttyp, den der Provider verarbeitet."); - - - builder.AddOrUpdate("Admin.DataExchange.Export.Name", - "Name of profile", - "Name des Profils", - "Specifies the name of the export profile.", - "Legt den Namen des Exportprofils fest."); - - builder.AddOrUpdate("Admin.DataExchange.Export.FileExtension", - "File type", - "Dateityp", - "The file type of the exported data.", - "Der Dateityp der exportierten Daten."); - - - - builder.AddOrUpdate("Admin.DataExchange.Export.BatchSize", - "Batch size", - "Stapelgre", - "Specifies the maximum number of records per export file. 0 is the default and means that all the records are exported in one file.", - "Legt die maximale Anzahl der Datenstze pro Exportdatei fest. 0 ist der Standard und bedeutet, dass alle Datenstze in eine Datei exportiert werden."); - - builder.AddOrUpdate("Admin.DataExchange.Export.PerStore", - "Per store", - "Per Shop", - "Specifies whether to start a separate run-through for each store. For each shop a new file will be created.", - "Legt fest, ob fr jeden Shop ein separater Verarbeitungsdurchlauf erfolgen soll. Fr jeden Shop wird eine neue Datei erzeugt."); - - builder.AddOrUpdate("Admin.DataExchange.Export.CreateZipArchive", - "Create ZIP archive", - "ZIP-Archiv erstellen", - "Specifies whether to combine the export files in temporary a ZIP archive. The archive remains in the temporary folder of the export profile without further processing.", - "Legt fest, ob die Exportdateien in einem ZIP-Archiv zusammengefasst werden sollen. Das Archiv verbleibt im temporren Ordner des Exportprofils ohne weitere Vearbeitung."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Cleanup", - "Clean up at the end", - "Zum Schluss aufrumen", - "Specifies whether to delete unneeded files after deployment.", - "Legt fest, ob nicht mehr bentigte Dateien nach der Bereitstellung gelscht werden sollen."); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportEntityType.Product", "Product", "Produkt"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportEntityType.Category", "Category", "Warengruppe"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportEntityType.Manufacturer", "Manufacturer", "Hersteller"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportEntityType.Customer", "Customer", "Kunde"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportEntityType.Order", "Order", "Auftrag"); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.StoreId", - "Store", - "Shop", - "Filter by store.", - "Nach Shop filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.CreatedFrom", - "Created from", - "Erstellt von", - "Filter by created date.", - "Nach dem Erstellungsdatum filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.CreatedTo", - "Created to", - "Erstellt bis", - "Filter by created date.", - "Nach dem Erstellungsdatum filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.IdMinimum", - "Product Id from", - "Produkt-ID von", - "Filter by product identifier.", - "Nach der Produkt-ID filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.IdMaximum", - "Product Id to", - "Produkt-ID bis", - "Filter by product identifier.", - "Nach der Produkt-ID filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.PriceMinimum", - "Price from", - "Preis von", - "Filter by price.", - "Nach dem Preis filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.PriceMaximum", - "Price to", - "Preis bis", - "Filter by price.", - "Nach dem Preis filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.AvailabilityMinimum", - "Availability from", - "Verfgbar von", - "Filter by availability quantity.", - "Nach der Verfgbarkeitsmenge filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.AvailabilityMaximum", - "Availability to", - "Verfgbar bis", - "Filter by availability quantity.", - "Nach der Verfgbarkeitsmenge filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.IsPublished", - "Published", - "Verffentlicht", - "Filter by publishing.", - "Nach Verffentlichung filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.CategoryIds", - "Categories", - "Warengruppen", - "Filter by categtories.", - "Nach Warengruppen filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.WithoutCategories", - "Without category mapping", - "Ohne Warengruppenzuordnung", - "Filter by missing category mapping.", - "Nach fehlender Warengruppenzuordnung filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.ManufacturerId", - "Manufacturer", - "Hersteller", - "Filter by manufacturer.", - "Nach Hersteller filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.WithoutManufacturers", - "Without manufacturer mapping", - "Ohne Herstellerzuordnung", - "Filter by missing manufacturer mapping.", - "Nach fehlender Herstellerzuordnung filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.ProductTagId", - "Product tag", - "Produkt-Tag", - "Filter by product tag.", - "Nach Produkt-Tag filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.FeaturedProducts", - "Only featured products", - "Nur empfohlene Produkte", - "Filter by featured products. Is only applied when the filtering by categories and manufacturers.", - "Nach empfohlenen Produkten filtern. Wird nur bei der Filterung nach Warengruppen und Hersteller angewendet."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.ProductType", - "Product type", - "Produkttyp", - "Filter by product type.", - "Nach Produkttyp filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.OrderStatusIds", - "Order status", - "Auftragsstatus", - "Filter by order status.", - "Nach Auftragsstaus filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.PaymentStatusIds", - "Payment status", - "Zahlungsstatus", - "Filter by payment status.", - "Nach Zahlungsstatus filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.ShippingStatusIds", - "Shipping status", - "Versandstatus", - "Filter by shipping status.", - "Nach Versandstatus filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.CustomerRoleIds", - "Customer roles", - "Kundengruppen", - "Filter by customer roles.", - "Nach Kundengruppen filtern."); - - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.StoreId", - "Store", - "Shop", - "Specifies the store to be applied to the export.", - "Legt den auf den Export anzuwendenden Shop fest."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.LanguageId", - "Language", - "Sprache", - "Specifies the language to be applied to the export.", - "Legt die auf den Export anzuwendende Sprache fest."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.CurrencyId", - "Currency", - "Whrung", - "Specifies the currency to be applied to the export.", - "Legt die auf den Export anzuwendende Whrung fest."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.CustomerId", - "Customer ID", - "Kunden-ID", - "Specifies the ID of the customer to be applied to the export. Is taken into account for price calculations for example.", - "Legt die ID des Kunden fest, auf den sich der Export beziehen soll. Wird z.B. bei Preisberechnungen bercksichtigt."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.DescriptionMerging", - "Product description", - "Artikelbeschreibung", - "Specifies what information to use for the description of the product.", - "Legt fest, welche Informationen zur Beschreibung des Artikel wie verwendet werden sollen."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.DescriptionToPlainText", - "Remove HTML from description", - "HTML aus der Beschreibung entfernen", - "Specifies whether to remove all HTML from the product description for the export.", - "Legt fest, ob fr den Export alle HTML-Auszeichnungen aus der Artikelbeschreibung entfernt werden sollen."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.AppendDescriptionText", - "Text to be appended", - "Anzuhngender Text", - "Specifies the text to be attached to the product description. If there are multiple texts then one of it is selected randomly.", - "Legt den an die Artikelbeschreibung anzuhngenden Text fest. Bei mehreren Texten wird einer per Zufall ausgewhlt."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.RemoveCriticalCharacters", - "Remove critical characters", - "Kritische Zeichen entfernen", - "Specifies whether to remove critical characters (like ) from the detail description.", - "Legt fest, ob kritische Zeichen (wie z.B. ) aus der Detailsbeschreibung entfernt werden sollen."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.CriticalCharacters", - "Critical characters", - "Kritische Zeichen", - "List with characters to be removed from the detail description.", - "Liste mit Zeichen, die aus der Detailbeschreibung entfernt werden sollen."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.PriceType", - "Product price", - "Produktpreis", - "Specifies the product price to be exported.", - "Legt den zu exportierenden Produktpreis fest."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.ConvertNetToGrossPrices", - "Convert net into gross prices", - "Netto- in Bruttopreise umrechnen", - "Specifies to convert net into gross prices.", - "Legt fest, dass Netto- in Bruttopreise umgerechnet werden sollen."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.Brand", - "Manufacturer\\Brand", - "Hersteller\\Marke", - "Specifies the manufacturer or brand to be exported, if a product has no manufacturer assigned.", - "Legt den zu exportierenden Hersteller bzw. die Marke fest, wenn fr ein Produkt kein Hersteller zugeordnet ist."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.PictureSize", - "Product picture size", - "Produktbildgre", - "Specifies the size (in pixel) of the product image.", - "Legt die Gre (in Pixel) des Produktbildes fest."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.ShippingTime", - "Shipping time", - "Lieferzeit", - "Specifies the shipping time for products where it is unspecified.", - "Legt die Lieferzeit fr Produkte fest, wo diese nicht angegeben ist."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.ShippingCosts", - "Shipping costs", - "Versandkosten", - "The shipping costs to be exported.", - "Die zu exportierenden Versandkosten."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.FreeShippingThreshold", - "Free shipping threshold", - "Kostenloser Versand ab", - "Specifies the amount as from shipping is free.", - "Legt den Betrag fest, ab dem keine Versandkosten anfallen."); - - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDeploymentType.FileSystem", "File system", "Dateisystem"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDeploymentType.Email", "Email", "E-Mail"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDeploymentType.Http", "HTTP", "HTTP"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDeploymentType.Ftp", "FTP", "FTP"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDescriptionMerging.None", - "Not specified", "Nicht spezifiziert"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDescriptionMerging.ShortDescriptionOrNameIfEmpty", - "Short description or name if empty", "Kurzbeschreibung oder Name falls leer"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDescriptionMerging.ShortDescription", - "Short description", "Kurzbeschreibung"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDescriptionMerging.Description", - "Description", "Detailbeschreibung"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDescriptionMerging.NameAndShortDescription", - "Product name + short description", "Produktname + Kurzbeschreibung"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDescriptionMerging.NameAndDescription", - "Product name + long description", "Produktname + Detailbeschreibung"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDescriptionMerging.ManufacturerAndNameAndShortDescription", - "Manufacturer + Product name + short description", "Hersteller + Produktname + Kurzbeschreibung"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDescriptionMerging.ManufacturerAndNameAndDescription", - "Manufacturer + Product name + long description", "Hersteller + Produktname + Detailbeschreibung"); - - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.Name", - "Name", - "Name", - "Specifies the name of the deployment.", - "Legt den Namen der Bereitstellung fest."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.IsPublic", - "Copy to public folder", - "In ffentlichen Ordner kopieren", - "Specifies whether to copy the exported data into a folder that is accessible through the internet.", - "Legt fest, ob die exportierten Daten in einen bers Internet zugnglichen Ordner kopiert werden sollen."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.FileSystemPath", - "Directory path", - "Ordnerpfad", - "Specifies the path (relative or absolute) where to deploy the data.", - "Legt den Pfad (relativ oder absolut) zu einem Ordner fest, in den die Daten bereitgestellt werden sollen."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.DeploymentType", - "Type of deployment", - "Art der Bereitstellung", - "Specifies the deployment type.", - "Legt die Art Bereitstellung fest."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.Username", - "User name", - "Benutzername", - "Specifies the user name.", - "Legt den Benutzernamen fest."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.Password", - "Password", - "Passwort", - "Specifies the password.", - "Legt das Passwort fest."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.Url", - "URL\\Host", - "URL\\Host", - "Specifies the URL or host name where to send the data.", - "Legt die URL bzw. den Host-Namen fest, an die die Daten bermittelt werden sollen."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.EmailAddresses", - "Email addresses to", - "E-Mail Adressen an", - "Specifies the email addresses where to send the data.", - "Legt die E-Mail Adressen fest, an die die Daten verschickt werden soll."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.EmailSubject", - "Email subject", - "E-Mail Betreff", - "Specifies the subject of the email.", - "Legt den Betreff der verschickten Daten fest."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.EmailAccountId", - "Email account", - "E-Mail Konto", - "Specifies the email account used to sent the data.", - "Legt das E-Mail Konto fest, ber welches die Daten verschickt werden sollen."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.resx b/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.resx deleted file mode 100644 index 3f0be8609e..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508091512101_ExportFramework.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201508121735397_AddSyncMapping.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201508121735397_AddSyncMapping.Designer.cs deleted file mode 100644 index da67ef8766..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508121735397_AddSyncMapping.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class AddSyncMapping : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(AddSyncMapping)); - - string IMigrationMetadata.Id - { - get { return "201508121735397_AddSyncMapping"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201508121735397_AddSyncMapping.cs b/src/Libraries/SmartStore.Data/Migrations/201508121735397_AddSyncMapping.cs deleted file mode 100644 index ce062a376e..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508121735397_AddSyncMapping.cs +++ /dev/null @@ -1,113 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class AddSyncMapping : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - CreateTable( - "dbo.SyncMapping", - c => new - { - Id = c.Int(nullable: false, identity: true), - EntityId = c.Int(nullable: false), - SourceKey = c.String(nullable: false, maxLength: 150), - EntityName = c.String(nullable: false, maxLength: 100), - ContextName = c.String(nullable: false, maxLength: 100), - SourceHash = c.String(maxLength: 40), - CustomInt = c.Int(), - CustomString = c.String(), - CustomBool = c.Boolean(), - SyncedOnUtc = c.DateTime(nullable: false), - }) - .PrimaryKey(t => t.Id) - .Index(t => new { t.EntityId, t.EntityName, t.ContextName }, unique: true, name: "IX_SyncMapping_ByEntity") - .Index(t => new { t.SourceKey, t.EntityName, t.ContextName }, unique: true, name: "IX_SyncMapping_BySource"); - - } - - public override void Down() - { - DropIndex("dbo.SyncMapping", "IX_SyncMapping_BySource"); - DropIndex("dbo.SyncMapping", "IX_SyncMapping_ByEntity"); - DropTable("dbo.SyncMapping"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - string attachHint = "A file that is to be appended to each sent email (eg Terms, Conditions etc.)"; - string attachHintDe = "Eine Datei, die jedem gesendeten E-Mail angehangen werden soll (z.B. AGB, Widerrufsbelehrung etc.)"; - - builder.AddOrUpdate("Admin.ContentManagement.MessageTemplates.Fields.Attachment1FileId", - "Attachment 1", - "Anhang 1", - attachHint, - attachHintDe); - builder.AddOrUpdate("Admin.ContentManagement.MessageTemplates.Fields.Attachment2FileId", - "Attachment 2", - "Anhang 2", - attachHint, - attachHintDe); - builder.AddOrUpdate("Admin.ContentManagement.MessageTemplates.Fields.Attachment3FileId", - "Attachment 3", - "Anhang 3", - attachHint, - attachHintDe); - - builder.AddOrUpdate("Common.FileUploader.EnterUrl", - "Enter URL", - "URL eingeben"); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CustomerNumberEnabled", - "Save customer number", - "Kundennummer speichern", - "Specifies whether customer numbers can be saved.", - "Bestimmt ob Kundennummern hinterlegt werden knnen."); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.DisplayCustomerNumber", - "Display customer numbers in frontend", - "Kundennummern im Frontend anzeigen", - "Specifies whether customer numbers will be displayed to customers in their account area.", - "Bestimmt ob Kunden ihre Kundennummer in Ihrem Account-Bereich einsehen knnen."); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CustomerCanEditNumberIfEmpty", - "Customers can enter a customer number", - "Kunden knnen Kundennummer hinterlegen", - "Specifies whether customers can enter a customer number if the customer number doesn't contain a value yet.", - "Bestimmt ob Kunden eine Kundennummer angeben knnen, wenn fr diese noch kein Wert hinterlegt wurde."); - - builder.AddOrUpdate("Common.FreeShipping", - "Free shipping", - "Versandkostenfrei"); - - builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.ExtraRobotsDisallows", - "Extra Disallows for robots.txt", - "Extra Disallows fr robots.txt", - "Enter additional paths that should be included as Disallow entries in your robots.txt. Each entry has to be entered in a new line.", - "Geben Sie hier zustzliche Pfade an, die als Disallow-Eintrge zur robots.txt hinzugefgt werden sollen. Jeder Eintrag muss in einer neuen Zeile erfolgen."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.DefaultSortOrderMode", - "Default product sort order", - "Standardsortierreihenfolge fr Produkte", - "Specifies the default product sort order.", - "Legt die Standardsortierreihenfolge fr Produkte fest."); - - builder.AddOrUpdate("Common.CustomerNumberAlreadyExists", - "Customer number already exists, please choose another.", - "Die von Ihnen gewhlte Kundennummer existiert bereits. Bitte geben Sie eine andere Kundennummer an."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201508121735397_AddSyncMapping.resx b/src/Libraries/SmartStore.Data/Migrations/201508121735397_AddSyncMapping.resx deleted file mode 100644 index 3299ad9347..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508121735397_AddSyncMapping.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201508142203054_CronExpressions.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201508142203054_CronExpressions.Designer.cs deleted file mode 100644 index 85ac9e4af4..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508142203054_CronExpressions.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class CronExpressions : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(CronExpressions)); - - string IMigrationMetadata.Id - { - get { return "201508142203054_CronExpressions"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201508142203054_CronExpressions.cs b/src/Libraries/SmartStore.Data/Migrations/201508142203054_CronExpressions.cs deleted file mode 100644 index 9f12663ade..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508142203054_CronExpressions.cs +++ /dev/null @@ -1,174 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Linq; - using System.Collections.Generic; - using System.Data.Entity; - using System.Data.Entity.Migrations; - using SmartStore.Core.Domain.Tasks; - using SmartStore.Data.Setup; - - public partial class CronExpressions : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - 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")); - DropColumn("dbo.ScheduleTask", "Seconds"); - } - - public override void Down() - { - AddColumn("dbo.ScheduleTask", "Seconds", c => c.Int(nullable: false)); - DropColumn("dbo.ScheduleTask", "RowVersion"); - DropColumn("dbo.ScheduleTask", "CronExpression"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - // Seconds > CronExpressions - var table = context.Set(); - var tasks = table.ToList(); - - foreach (var task in tasks) - { - if (task.Type.Contains(".QueuedMessagesSendTask")) - { - task.CronExpression = "* * * * *"; // every Minute - } - else if (task.Type.Contains(".DeleteGuestsTask")) - { - task.CronExpression = "*/10 * * * *"; // every 10 Minutes - } - else if (task.Type.Contains(".ClearCacheTask")) - { - task.CronExpression = "0 */4 * * *"; // every 4 hrs - } - else if (task.Type.Contains(".UpdateExchangeRateTask")) - { - task.CronExpression = "0/15 * * * *"; // every 15 Minutes - } - else if (task.Type.Contains(".DeleteLogsTask")) - { - task.CronExpression = "0 1 * * *"; // At 01:00 - } - else if (task.Type.Contains(".TransientMediaClearTask")) - { - task.CronExpression = "30 1,13 * * *"; // At 01:30 and 13:30 - } - else if (task.Type.Contains(".QueuedMessagesClearTask")) - { - task.CronExpression = "0 2 * * *"; // At 02:00 - } - else if (task.Type.Contains(".UpdateRatingWidgetStateTask")) - { - task.CronExpression = "0 3 * * *"; // At 03:00 - } - else if (task.Type.Contains(".MailChimpSynchronizationTask")) - { - task.CronExpression = "0 */1 * * *"; // Every hour - } - else if (task.Type.Contains(".AmazonPay.DataPollingTask")) - { - task.CronExpression = "*/30 * * * *"; // Every 30 minutes - } - else if (task.Type.Contains(".NewsImportTask")) - { - task.CronExpression = "30 */12 * * *"; // At 30 minutes past the hour, every 12 hours - } - else if (task.Type.Contains(".TempFileCleanupTask")) - { - task.CronExpression = "30 3 * * *"; // At 03:30 - } - else if (task.Type.Contains(".BMEcat.FileImportTask")) - { - task.CronExpression = "30 2 * * *"; // At 02:30 - } - else if (task.Type.Contains(".StaticFileGenerationTask")) - { - task.CronExpression = "0 */6 * * *"; // Every 06 hours - } - else - { - task.CronExpression = "0 */1 * * *"; // Every hour - } - } - - context.SaveChanges(); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.Delete( - "Admin.System.ScheduleTasks.Seconds", - "Admin.System.ScheduleTasks.Seconds.Positive", - "Admin.System.ScheduleTasks.RunNow.Completed"); - - builder.AddOrUpdate("Common.Rule", - "Rule", - "Regel"); - builder.AddOrUpdate("Common.Scheduled", - "Scheduled", - "Geplant"); - builder.AddOrUpdate("Common.Unscheduled", - "Unscheduled", - "Ungeplant"); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.CronExpression", - "Cron Expression", - "Cron Ausdruck", - "An expression that defines the schedule for the automatic execution of the task.", - "Ein Ausdruck, der den Zeitplan fr die automatische Ausfhrung der Aufgabe festlegt."); - builder.AddOrUpdate("Admin.System.ScheduleTasks.Enabled.Hint", - "Enables the scheduled execution of the task in accordance with the cron expression", - "Aktiviert die geplante Ausfhrung der Aufgabe gem Cron Ausdruck"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.StopOnError", - "Disable on error", - "Bei Fehler deaktivieren", - "Check the box if the task should be disabled automatically when an error occurs during execution", - "Aktivieren Sie das Kstchen, wenn die Aufgabe bei Auftreten eines Fehlers whrend der Ausfhrung deaktiviert werden soll"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.LastStart.Hint", - "Start date of the last execution", - "Startdatum der letzten Ausfhrung"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.LastSuccess.Hint", - "Start date of the last successful execution", - "Startdatum der letzten erfolgreichen Ausfhrung"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.Duration.Hint", - "Duration of the latest execution ([h]:[min]:[sec])", - "Dauer der letzten Ausfhrung ([Std.]:[Min.]:[Sek.])"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.NextRun.Hint", - "Date of the next scheduled execution", - "Datum der nchsten geplanten Ausfhrung"); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.CronHelp", - "Cron Expressions help", - "Hilfe zu Cron-Ausdrcken"); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.FutureSchedules", - "Future schedules", - "Zuknftige Zeitplne"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.EditTask", - "Edit task", - "Aufgabe bearbeiten"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.ScheduleExecution", - "Schedule execution", - "Ausfhrung planen"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.InvalidCronExpression", - "The cron expression is invalid", - "Der Cron-Ausdruck ist ungltig"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.RunNow.Success", - "The task has been executed successfully", - "Aufgabe wurde erfolgreich ausgefhrt"); - builder.AddOrUpdate("Admin.System.ScheduleTasks.UpdateSuccess", - "The task has been updated successfully", - "Die Aufgabe wurde erfolgreich bearbeitet"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201508142203054_CronExpressions.resx b/src/Libraries/SmartStore.Data/Migrations/201508142203054_CronExpressions.resx deleted file mode 100644 index 62a0f6a2fb..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508142203054_CronExpressions.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201508211346171_Merge1.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201508211346171_Merge1.Designer.cs deleted file mode 100644 index 7884363732..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508211346171_Merge1.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class Merge1 : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(Merge1)); - - string IMigrationMetadata.Id - { - get { return "201508211346171_Merge1"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201508211346171_Merge1.cs b/src/Libraries/SmartStore.Data/Migrations/201508211346171_Merge1.cs deleted file mode 100644 index 146e495730..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508211346171_Merge1.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class Merge1 : DbMigration - { - public override void Up() - { - } - - public override void Down() - { - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201508211346171_Merge1.resx b/src/Libraries/SmartStore.Data/Migrations/201508211346171_Merge1.resx deleted file mode 100644 index 3f0be8609e..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201508211346171_Merge1.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - H4sIAAAAAAAEAOy923IcObIg+L5m+w8yPc2snZFK6m6zPm1VO0ZSpEQ7EsUmKemcfqGBkSAzWpERWXHhpcfmy/ZhP2l/YQHEJXG/R0RmVj5UKRlwOACHw+HucDj+v//n//31fz6vslePsKzSIv/t9bs3v7x+BfOkWKT5w2+vm/r+f/z19f/8v//P/+PX08Xq+dX3Hu5PGA7VzKvfXi/rev23t2+rZAlXoHqzSpOyqIr7+k1SrN6CRfH2/S+//Pvbd+/eQoTiNcL16tWvV01epytI/kB/nhR5Atd1A7IvxQJmVfcdlVwTrK8uwApWa5DA315fr0BZX9dFCd98ADV4/eooSwHqxjXM7l+/Anle1KBGnfzbtwpe12WRP1yv0QeQ3bysIYK7B1kFu87/bQNuO45f3uNxvN1U7FElTVUXK0eE7/7UEeYtX92LvK8HwiHSnSIS1y941IR8v70+fV4XZf0BrrPiZQXz+vUrvtG/nWQlrsBQ+YSQGoGl+Rsexb+94gD/beCR92/ev/nlzS//9uqkyeqmhL/lsKlLkP3bq8vmLkuT/4AvN8VPmP+WN1lGdxx1HZUxH9Cny7JYw7J+uYL33XDOF69fvWXrveUrDtWoOu0Az/P6T+9fv7pAjYO7DA58QRGDjOojzGEJari4BHUNyxzjgISyQutcW+jP+zSD5ib1aPD/ewyIn9G6fP3qC3j+DPOHeolW7C9oJZ6lz3DRf+mwfstTtIxRpbpsoHOrpzkGHrp+XBQZBLkzmvOqnexQPBuOw3hCSYokQ5nryfpnS7IaWABU1VNRLkZv6FuZ6duI0cgZ4ubrl6qGK7QWlqOP6RRJnOxosShhVcFq/OGR9q6bu3/CpJ5ocElSoI3QmZ0vwGP6QGSUXOS8fnUFM1JeLdN1uzUKovt2AD4ri9VVkUl2iB7m9rpoygSvl8IAeAPKB1izPf717WYrstighn557k5d/cPWpG5rnj3lrMgWsJynbfTnY4pab+WXoQ/WAmWeTRXrxIsmQ72+AdXP0M0QSXXEPIQESoKgnzF2w7LAspUot+M3RaYbGRb36UNrJ4zc5Nf7+wrWYXPxOV2lgSiOQZ0sr9N/wTA0l2ipYBkTyqsnxWqdQSSjXHbzCLNxUkIkGf+Rro/KZJk+ho8D12rWzmiUW/Vm76zib9f9Lmzcrvt93bbXneiBWPBouj2gZ8D5jsugFJqGFDRI12C75qxq4GrVGxrJQd9Qt2XYb/8yznZLHEMRmjXsxjAp8kUVJm5xXU1P/7rd+khdrL/mp2VZlKGoLuBzfdXk3+rBY4D2bniTrsxbJ6jQ2kML0LPuab7wbbVJErSp+bZLk02uD8fg0vPqU7pAqzV0gtCfD3gLR/pBQlyIDNfb1f2C/gMPBiPAfdDWov/vDSCfEC4fH+iHtERabFG+vKERHcS/ZlWbxP8Ywu0DrJIyXRvMDcu2DU2l1ToDL5+LBGQRBmrV2NcSmTdhm8559QHeA8STbkLBYZ3Bplf86xokS89Dh05eVG+kCA8LT90WRbBQNwHuCZoEzOPMkiJKPzsjAqjrHqP3LkfaEc9kZzOGpUf5EY7THJQvdn6EIOn4fhzd7wvSTwxaZ6yWldbkmcpXLV3ot2ec+auGEqxIDajMitR1mkLl0HemlmkI1FfLkdA1gsziD8VTnhVg4SWmFyl40yM4iGWNHOlo9LFJh1bb3+4HmT0u6gTQ+5BVRDWat7RvqxejsYXqSZHX3Wnx6GPBYsRwnBynodNnNKZqCv/5eXUBn8IP/29KgPpL2Ym+qL6tF3ilfZV7BqKrq5GU1IMMVLd1WaZFSeCCDq/QTjq9YxG3GsmlaXALFtMP7qaYZGhXcJ29RBmfVTuTjOnkZPQmjpNk9DbMMTCRTLDjYvEy+lbWngR6bR0yZz/SKcoUBrr7MRpFh0YMGxI7sfgC8gZk2Yvz/qw0jzZGifxcM7J5xJ902htUtgOiSW4a0S0LLB0IA6Mz71jAsMAqplv+ug2N56DcGJapzrPyl7+M4vJuXcMmh5Jl4/q2PhWVbocYaYSXRRkYH2MR+ztW383BwCO13J7zXlfBHgLsb2iPDNCuStYCqjXS6cElIKEqX2C9LHzcUV396g2D6CC1dAxKEWreQMnnJGsWcHFCJh2WeG88X4wfyzG0ize48kXf5PtIYeddm9dIo1ijNlryTzHabpSkA3gFEJ/Zc7Be+SXN01WzIgeUR6tW5ei0XJikK4Ck0GWJfnU3zv76+tU1PjdF3GTEDJ5Hwtxiu4IVojWJUI1EizMEbncQ7XWJwV6YlsWiSerjJl+glYQWNlKOy/SuqWEb/ushX09ADbLi4Y0J9UHiqtvaEC2U0QaiR0P0HWRNMLbzCi1IZCXBpPaIM1PaZBu6SS0yE0ve0vU3RppDNcFuc6kbZMoJDcVcuoe1qm6rI1boimiJHQlZH4AWhgWZjUnMzbTH18UIUqF+3mGhRjU0httzSWLkbTbraOeGiAUHQeFoSElM8nQBb5bN6i6nvA++yL6jib/LgmeOCIdqGR5kHC/sLapDOOBg0uBFpXcPuSt1gm2Od6/6bJFum3rXgt2Ab7k6mkGyoOb9m4N3jRJyG4bVAOy7ruu0q6IRrl4clAqNeGxJhJsL1QUuQYna/FgWzRp1JI5+0W0C5/kifUwXfidF42zlrmddk+/t1oZ/nPaOFqs0PylWKyrYZszbtYRr4QptyeEmJ5qcp6/5p2IFLyPoij2eeArDF1gDJDSw7378i6u4sSkZB7d3k9baewtxRnaUZcXT4NGFjyl8ClZ5j9brsniEiyskjfOH62YVNtMXRR0ZY4/uBm0YGTfo4D7GQ9qFfNwUR0mw5UAusePOkb0xeIqvfzbjLzqQN/cgwSpIifbR+gIZURupMVqzHxGLjd7IefUxva9PQBlsgvV4YmgrV/D3Ji3h13qJKN5uJ8Gc0uHcKD86aR0phOmowbpxnSZYNzpaLLg+BI/pvNpE5MeJ7g5OnpVn7QLvEQaPEc1Ku+C+3gs4PT1aHZrT53VaEkX9A3jhcdqhOErq9JGgiMH2n0B1DXBGjFizymJzvM+EeoPDIY4eSghpxTEgPGCD7AY+j6+JnldXMGlKOn2Nv/ToEJ28JBlsOxUq42iMl7BMi+DVN+Akez9BHLhWzit88hwt398Zmv7+KDtYu1ssUrzyQNZjPFlil4a9o9r9xgJ4Pn1Gdk3wWjivTqtgcqLenCAD66HAsQiBRgzIkVF0nj+ipYnQ9VEGgWpjXSQ/ox1CYHONYDx6BCmqm2YU1kC/sbSn3htWmkcc+efiqR013mkQtuCEoUWd3r8Qh8BZUfZ9PIbI+go8ugLJzwKb0zhJb/A5M7YGMcbzlpbIAhmM3mCNghj9XRxKnElqMbbxJ3EwEgLARYcs1ebEiqSsorWAO3jcvBw3db1xLgSsLQz9I62WWVrVcZB2iz+DiHmRXGf8N97Zu5B2TtClSfiJFo0k+g70NVuM20BnmJxQ4bvR27heIzwgcxyIPU6S56c/ZfO4RUHjOs0XAZh6d9Zpjk/povBXJ7YYzHBkpugE26RtIuPjJoXtmgwW+GgPhVU9HEaeFKu7NO/O5SIyIepzH9xQHa3XWRquMf+A6cNyvKXI2jHR0f9IFyNi/zQubYadJlSeDIjChEm8E4t4UReQJO8M3n+3J+RCHGD6CMsXXNnRe0Jn/3Ksegwq2O0+UczuAd8XCKqmhG1mM6U6GSUP1dBm3EjsAS3+waK2Iy0JgyAnWgYPVBz3ehcmAksceRHL8cEgxdSIjfN6WbT+FFC6ezrUUUvtljhskdLol770toO+KTa+8k34ixpKiEnSgLqGINHSQBe6c8sCClE7TLkqYIcF8gww2kT9WMaI+YdMGWPBFCFWjiPqHF7EIFYPqHeLaUKneBDVAAQ4z47Th4javtOAmv7LwFRjkMJ6juMyJXi0Q+hgNL3nIFQd58E8+zwcgqu73IJoeswCqDrMQXn2l1ig92mbII+OAFb3X15FMx59BdX4DLU8x3sDHrSDu2XgRHlKFSvFKQ3jKU2/gzJFSp3MjtR2X1NPM0EWtVSzZFPVc6p41E4Ddxit6xC9x8Wm+9XwIAsoMiFdruRCBsiVDfmzYU1neVCxuyyEssMcmGuXaR+OpLtDsYwzhEKBF0QI2ezbZzccbrp4ZH3ulNrh1yGuWd2WIcp3pJym/czEiNT4VmFbKdL9JfWNqOhOrb4pV6vc+eS3c5n5JDFqvWQeNbvoquqkaNb4jvYieGZETAFvUtnNDImPJKsslEc3mDDxXONATGb8TWEwxUTDm66gs+U3cMK2pAV23ZwkLoYxPBLmMQjOC2uPRIeq43x1Xq2hTXkNyThkgOqhSKGDLhj1dnfADaMexWErVrdl2IpHunCD779EatnkwJ/ursRxUSM+nfRaj+EJozg7U7+MYl3oOVyYifR6ML7RFysAsPO0OZ5U4ZtU4a8JMld/qpuizQjC4/aOyOnwfGWDxwLsDosIoCuQP0zwjmHku3Hx4xe2+ObQtp2C72UKA4FUJD3g9xQ+fdEbVZ7SOPbho4fJYj6AlNg31l7YVkxLe785qeqBNj3mywRNXgAIyw7Q98DzmZCu/kFx14ivlkRjvZBh8QDQn8e5iw8Lixcz/hTp8uPevGMR++RWFCFyCOPJLSdv/MQJgzM858hBvEyVz0xlVkynG3ltpd6rQXGMplo0jis4bsyFqa8x0gRJ/IJRTtcofIdVrF03UR6DkJD9qsmgVRboOH6V6zUc/dTqOM0yHPXYp3cOvP3YX0qNg67PPkwMCCbxtZPHZoNmkPA6N0WcyaMapZOIT9lwPxtmL1Dsljt/heM8nT4jQUW/KDqBjbyJfxjvBElpHWvPm/z2HiYKN+AhbxrRYbdRt2XYCMZ5yPukyIryE3wmCaknb30n3vZ2tbMiHL1ymA6rRt1WJEsr1kHQeXUGAZ6IXkGY0a+t3Kk2HGpxB2ADrL4EMPxQGEUi4AgmnM+tBVN/o1hxl+I1gnDZQGM7yIfR5QNN7j+EjGC51fK+DVtJf+mG+UOxDtUVRpAfITeHbPofRZZEEiIH6bHN8V2TRtxQnHAIUdquEKVDUNEOBhUd4mx2L87G65SL3dzFMy5ZuaAoSIFiWBvtVdpwO6PFE01HeIUzT3Ttk6ibTsSQJwHz+vUUurxJesdyYWPCTZKotU2nHkaUTzBb3zfZf8GKJD+Nguyi8MEVosO73kNX6O2K2+qOHW2xdLTIYVWhtWpKvSDUueXWs2JEyhr6q/bqakFnB/2q9hE/vc4x/DrYJ+q2ehp9bNKh1fa3R3ZpQ9BYpFeADa+3x2nF4jnwuA2dFeUKhMf7dNiuQaZPMRUlu/6Ur9pETP58dH+fZmkEM5XkBA62ZSLp1edVGw5ylDDPRXrfHLCJLYnDR59BVZ+vjxYLZO1McJ0qptWAu/65eEhzzyvTuH6fWVqJwv+qcEtShcLQi/5bCoyKWBdKxZh1EcRV0elCfoa513WTh5X0lQVRd5iDc+01b3Pout3B3G40GrHfPIygcikBXe8v0JFLBnJzoBJqMxBqYrNgrrQ+fcYKDciOmnqJVZo29dEVTFrfoGQEuhrSabCqIMyJXS3XCUJqQLO6LCrFDZmhWDoQsVTotQTEq4s3xTpNNH0k5epOssXyXnIwrt0kDiZ5D0mRtHNsidAvrti1S1cQ2Rg5jrCCqglmQKRdlEMIXVWAuXf5CZSLyyLN6+pTWtWqQ38JnKL3ajjJGDTAriPpIxCt9hoBWCL+OBi1AOQBnfNOUelA1bkkeSgp+ZVAAu3VkEGG/UBQd7seGRpF/qZDcLDp1W2dpWVVTxIVjtXXSRoyGf1xWkEstgb5ywTtyEPSDRYZ4g6cvvUxzRPXM70T6kGUEV+5JUvz3VQNvR+9oX+ka6wrgcyQgirSw73LIofGxy0jvYDcv+o3PrMH2dtqS6ZdQ9KtsN9oB5jNBsgVCZs2X+68V9OLVNs5DlLsIgOg7CgLFZbyqSdXQKx+h+OwPavbMmyYtl4un3iMqnN8BHtQCbJY+fRvnorPEBPxvCoMgvZ9BIF0sywhtG3wTxEaRIIWlmnCNRb4FPR3EOzijRcZEi2kIzT2RWPH8jfRhjtwUluKhrndVG7FG5eOxAwtsa6MVZztW3ojUAyL2Ssku6MUQOy8FCrILGTp4bED9Qi4qTvsQxqxtPehqfHk0vlDjmjdvm8bHJF3+pxkzQKtdeYase7kK845W7dSSfN4CZDThGf3M2cLIUvJySnEK68cO0hkP3HF6u4B+jKD6SCtjMwb/PzuPMr33V0JH1NgkHpxDri3Qa3zMts91RNh8WuVmKBwtOHAN0JUWofrsOY1a74jVfB9OYvgkjj3BM5x4k7Eenv1KKMxmiB+AITqLE0ZKeG1rI+z4mGIX3Ne0rh29YbCsR0x7l1nJokbx4PHDvJ4KmSPUcpSFK1vN4AbdpKVC6wkBQpmo7YrATyEERz2AnVbn0H+0ICH4JjNWHc3XHNbFYsYB3Bel/BaZg82W/udrcN3Inn81VUZL+r4SNtH6kYWfPM8sHO4FruNFyxHOuCk9im5F8VrN+R9udot07arvWxWdpNg3UCxfWQKpds1CxG0V2864bxXkyRT6b8I8Jsez2HLVrc1j6+ln5mO6lqTb4z2Ue3fG3gNpzjKPMvAw/kKDRan7Dbe1YhxUbTe32ckRvB2tXnp2sm4ghURbXJhLoOUSkwtoCDe9dBBR3Yy1MFSVYLzIGHNsi5CcltCa4MIGelh0L51UwrJkYyk8wqvrsuseUhzQYgYLyUWTSITPlFUp0hCgVep7ESIl1C4VNwNj5VGgkK5Hd42pnehy/AHqLoBxgt8uWRv/0+ZOECbG8Gcb8CLAS/gU+Xv0sW131AotoPHeo/uJFlHpnQfY0rjuyXx3Mc9RimjUxN7uwHcsLWsXGBiKVAwy7Zd8edXXP+gKKnb2nHv8fWyKGMsR8dmz4jomrrVaBbdPE7aP6LPfas8qgeneZSR+Zksw5YoM1OEQunuykK4XsCh9me5p8VLC+D9KlpVwc9yKrLse1ETkywhiYectQGMonrDI9oOHRb36iivnjyCedSmzYBTbtdwhLil4SljRg0mWjAa2DC7mepawLy3KA6KoJ4PR44hHUktaq9rfr1H/LfZYbfoOb8CiwjFMuyWSAvDLj2qSLrc6HLnNIrcclUkTgyXE0KKRAuZ4i0ngiTEQTao24plJM4jH9qEbZ2mO/5d9WhG2rJ4+pp/QorEJdEWA+0lbHx9JOl1bgosKbfnauM81uhoFyq1VgCR1jILgCmQynt/zX8jY9Vy3nIjkslz2UYVdJkAo45ykwAjOsh1dVumU82//GUU51kJYbQb8rFSnyJEnUTDeLgpMIjWKMlO+yylLfMHbh02KUptZ9daysBylVaVIQchD6XOpqiHFCWRHjxMueRw+1zDhklTIqwCAQ7yyVs+jaovziMbN08Cjt5yQCrUaItY0HPs1rzXItalJQ3SNnSID6tbw+uRLjFOkwKvn+V2dPfpBAnC+iY7xWTClr9iXibcN01TR0kCq2qaBi/xBWhktFqJ+dgvmWsvho6Un5kXsm5Znb1k7ZBM2UOwkrrVJqHzQYSq2yKZoIMfaI4khyNFRrnaTZc7+T7CHBfPnXOk85JDk0bdtoObBOaGLtKAsk5SmdDV3aSAwuVZ15kggUZwHCSaui1Cpa2RaHi2MIJQRF1yQl0siuUtJPez0e7NhJDuf0/hUyAKnBDbKyGCAk+sKe7xtXLX7QhhPzYEtwcppLJW8WiF055g6mAHJO0d+anrWgsQvgUESv+D4Fe3RQj0sSya9cinzCPd1ZoyuPFi88xMEKGibA5YgkaxQ/Zxi/hjvem8WcNqaX5LA3HSnCqTS3MaIFyad50IEukEx0Gua4TM3kvjvVzjU0XLtGspXFxIH0qTyRMvcdHNrbOkaJ9aa/85CAl1W4RAEz27QtoKftWZDHhb/BTsq53Bzgr2VTbHB5UIeXGm2SZaR+JguwQv+FZKVGRtGu0xT5IUHHPSlCXMk5dJnl/qG7sCm7DRDzBJVyB7/eqyRL+IFHv97q+vX13jDAq/vf6r91pgA7FC5+k7qKcUK9fNXY1EYnaeJ9kNRmtFrD87D4tpDKetn6KxG9wYmhvyivZUI2QanWakndSZZoRdY5OODDXksJTdG2OEI9pFUqwigOwMwrFpqm55bAKrWx6b2h3+8VN7Eh4anUn7tT5uK0SsjNYEUrybfAEXR6tRR0K/AvwDlhCxXYT06kuY/Cya+qhGfHTX1HBKU1povPrP1QSvm3aqR6xrTkf392mWojUZzT5Yj04Dcj8IGzX4ZeMSIvl1gniL1Z28WQphoqPqx5tI3OVJ1HCBNuMlMgDVT7hQTcmoIzx5fBz/JVnc0OnzOi2JI+FLkdfLidv8LwjGpye9vtpHjj7AuzT42gqF6igh++anIltMwB9iwxMxJtXwMch/TmIAc21OImLoNs9PpmxueF9ouibP78AEykW3mxIFcEiFN/a6b5BBUHZpXG9KkFcgwT83qsHkTU+yZFSNX8GqycYn+wlYY0f6tAQXG51otNfN3aCjTzvky6ZMlqCCUzrwL0Hqe+mbf0117HnpmsP2ORI466Y9LZnca/wBZrDeq5e3NJbwFcRHb9TdYqtji0+guoBPnRfnosD3bBLmVcLwo072lEZ65EmW0S0PuDn2lJULkRJSINeYPG3cYNuCLGSQLVF0zTtQsPcNfauQlf4prepC8WCiDPC2O8TddFYNJRwna0Bd3wT5mN4TQ844CBng7bcKLn6k9VIYjBlaGJRFFdfBkVo455uGv0lKOKH/XJHQWb7cq2cXbYooVc9wsaJnmyJ5z6hy155dwQWEK7ighdip8i1QGsrIFEZgYTDmGs7Pr6O+q5MO9qUi2dkS6UPxVLFPr9ZW0liA5KUdB6AQejxUUPiaXBB6vLbcoqmkAvMQsKJuazgGDfTwtlIt1E08zjtbm5MY2x12U8OwyfYfhbWih3bVFzqZMoaCYNVxVnJ5rXS5tuAbiCbDdljm6rZ6eoWuUGYHjYGMeREm+sHiSAKlp6a9trupYVB0+4/CutRDuwoUTtGaQmm3GpBcpwsSOOFC5iBY1G31fjNKKsie+7CTTjEC5UYOVTiv+s6SbGkggjeqR3hSNOuJnNpXiBjrFDuIpvDaDa1Nk+DmGubYjp1iZG1T0wzrCzK2qPypo7VzXm24g7gP5/a3juSAct+SrdxOwgZuOxq5JNWO51ZVRxyRAlS5K6vgg7ZkqodBN1AObyHZGOOISMG3Q6Lo+Zft42+haP7egG70QUZHntaXZZqMHsI7NDR6xO4Uo5lkJGNHGPe+jVZHHJtkbGtj026WMNdpw1s/FE95VoBFhOeszqseWTQ1/jNaIHkFe7yOJhCW1j9g+rB0MF0Md+mafJFBpGaB8QMZWgF/QvLkxeJvpaJ0VFVFgqOSF72yIj/1iKglqTQ/k1YV7t91OGiUHtdIDiKtFdJ2Vg3dGqBkHesKNV3rIcL1ygvyFkSQXolRHPRKdVtRtMEL6s2O6bIr9lcxi02wx1batAZJYHewL11ukoN/r8UmOVgPyjotwXdYgxq+i3TVviV4DBzHIAN5AgPNsmr0W2ZTOdBGkgzaQDZZtIssrE0HJ4gNLXD8E6fIAUE2o4l43NSH8fg8qdFF1AwxRAf5p24rig5yU4LkJ6L4RIHe5JJuXOuO8Az0DR//ALP0EZYvntUn132sg+v4Ra+IvXMN+VMHgtIQtxsZIHaQAVAGAbJQQameaJQxhNLB6W5ekYROUSLpZIfo0/jLjYsh5jpQrVf5avG0VxA75lfwd/xmpL+DgEFzWAeadRAjoVi0RRDLXIpz+HQFQVXkZ0XZctP0bpCOfyFxe0c5IAjrgP7ylN3Udjdgx88bc12D+/uLYooUNUeLVZqbr/b++ZcoD4Iwsi1OXrktumLna1FTNFHY0jIIid0pBQs0NguinJ2gTSgsnoLHdNjZdOs/ws52CcpOv3E8IWzP8zwq0lMcI8IymgcyTljIPCmW0JKEJeIhHBYxmscyjtKxC6J4w9wK64KVUrc0PG1jKMEkloYaNuodY6Eh2V6iBDL329sjqzvfFVqRHPOqYMxdjnLo2yG5XqO11t9xH2SBz4kUqEFWPLzR4j1sj6PLcznhv67ZRCCeewVOXnWWZmhgEd5sR3z99DXvhn1JHS35J8HyTsDvtcq1rC5b8lYVxMeQrWo5+2Y1fOIxWj06WxLosDjSRYsqzHrQDtVbcOrQHuSmq8Ab+fGoccNsxpBgcjIp3nWfe6nz5zsRpEaYXJRredp2FXgoBdC9uqgbeuAYQfhFF3sHgfdHEE067lWcF0+/5ISz5oBlG2Kp3YCHcLMMITksLe+l9edIj5SZ9mrtrny7mcpKst3Sxap9lIGJwZvfQZmCfJOu/aRY3aU5CNSIzcgPvKxu6/pnE4GVDXfv61R3CBqnkS8gb+5BgiexvESzbYw6i9Oso2/acLuJdCQWth/pIh6yT3GvTYEKEsq5RiNbosU/WNRWhzhHVZU+5GgVpYSPzhe6M+h3cY6F+yhB7Nt3PHPqTy7wJVjXm3AVuZIX7EzbyeMlJDKTn3GOfYiv82tTf70nSNvzXmeiqpOqUcyh2+01m98ti0PQBGyrqhxr1vVjnpxYtK12rFrU8hmsr4uVXsW+g2VxOI2YruozbKZ+jLMmvrH4quFBHxxdoHdoojlab+BzjT6t1uOnx8epeX5v0jL8sjq1XvK6LLIYcSEznV7xS8hBvLrKVG9ByvOcz3ik/iDbOq4j1DtbLYbKIyZ5HrVuAWmNW6XkNdJAW1/lWHBDEsPzEHEzOewiFrvIUZYCg+0UQVLP5OIeLfVLqKbUpXmNrS4RtAduV7eloFt4HM0OLSJDdGSRFeX17w0oYXX1cDf6oFrf0uKfaCXQwf3RY0fb66YTNHReIUxoI4dJhCRKATokh4mIhhhq7ec0/4kDfefMYeeqbW2PnmWpd9opa1470k0Ky84V7r39DDgOe41OssWJ/4xxy6IPisZM5+m6DvUyRL0YYAw9xwOVrvqBdW9Z0M26lkMIK1cBFtN3umlCYsMLhZoe6ux0+0ghzIg+N+fxl6r95yAv1G1NFKvAtfqt1J3ijNTodZWd5hg+WEG6hgnilTiDMByrFpX2Tm2kQ8bPxUMxHGrO5oDkx16vsuNi8TLFW6tFXqP10p9HXcD6qSh/ThEcgATTC1nCJ01Zwjx5CfesE5ynz8kS5A+QPBbqi1qzfygbkQfc4RHeamtR4XUmYDF+1VjDfYMUZ8Y8Mg5cMSQGSj8WFjRoI910y+NhN/y0dlG+vOmRHHZUdVuGHfUvo+xt/cQYHi75yyjZJOyzhf/Vdzv5XGAE4WS1MVjOinKFJp26mzZWe5/TVYpY7KZotdXgYBu8wKrlNjmAtujaMz9GIttO8wWa2egalpt58wWQm7mBVk6H5SCa1W215A/VsFos8xhOHn4Zex/dEq4gcfvdZT4mN6lfvWHQHLhR3RYh1Og2RiQ+tfDza1p5H8dMHZP7j0Hy8zwnUZrXzR11hOvtr1ZgPCyJqBM8mgs8VoadOTJRK1hPmvXEBCtYqMYKMb3RqsYkvmkDqPU4ovit6esmN3CF1Omg4CIZuoMgUbc1j3b2PYVPqKvL6Vv2tdjs3UdoGh6K8iUCL/OoDnx84OPJ+LgT7hHYmMN04OIDF0/GxWQq0iQobLlYrYr8DY/pwMbqtuK4j9CIPpZFs56eLVHLM6xCg2tgpCjsMV0FR0l2BZMCv1rt7rHFoQMI3ZsByWHFjb3i5nTYamPgnIN8zxebQ7Dw28PGsLWBR5Vha3IIwdRWgAVZ2N/KkFVYvBnqHxbgPi/A66zRneOO1Gqs/AmfQf7QgIfRdrL7+zRL/eygoW61+XlYSuq2jhaLElbBbzp8gBmMcMfDkz3V70G3o5NvIz173A5Q1A7CF4qbhwAR5pmFoEL8178EFxANxWA6sL66LYPkt73T5Sr5X6oarhAdnoiSMHXzV5h0Y8dFjedi6Jj7Q7qCeeV3ECqslAHZYbEcFss+LZazomxWgWEDBEf1RkB1WCsarqXo9LFJh5bb3zMd+JMZDE5oU6zTZBdDDwT+lQYdqKEE9U8DGphOKn1EtBgeJPddsCyew2rVrNYY0T2Yh2Kt1JsiFibEn/+EifaVwHEC/3Hiq+m96zgHFgi2Qc+rzpg9fjlq6mVRRkR4hTSHdUqlXPDFOZIMpdlYkSiAFiu3LDydD0AJJrn2r4Z1jdnaLB2b3tPQyr5vgEw9pyCDtgDiBEGfPhc+wfeo1gN+m5vCcpD+6rYoMuEmtyY80/i+60gydCTBwpFZca4zwNwK8PThjhJMcsKjho36kh/dkEyoyMq1vY0uSloyxhAnGP4gUtRtzey0MHhq3o/TqlcOA4c4z9UapA8+Doxud67e9DgOrOvNO2NFxRjNlJEaxnkcdm2LjXlZ18HtThZRQHzqsAw5VIfV6L0aR5Lkx0lyiqYs6441oe4asm0XQtd/pIQykdb7NEEW7SQkCX4PJDxRYdQr/dcwX+C7PyDLXkJxHdU1SJbY2Hl3ljpnZNvUfh9U+0/G2tay8gI+VZ8hXuqBRy+DyJRjPEhOdVtyigWfxZAlqROHfxkllUkceRJV7RgzkPlkCZOfRROU/rx90uaNgOqwaDSLZpZIyJ18I+N6ma7XGL67bhUL73l1A55PnyFFDV9UCFF/pTHaO1fb8SJILH1L7enjpYbmhQo57K1Ehm2cgbZ1hJcorCsGvT4hbyWiED6k5jdt1TzFRn7xfSTRfshnP5IEtJdc08ss/lDDWdh5yawrfIEN8/UleMEs8CmtcIinv9BSIDxILXVbPMlChRaZiS0NNuPHKl1nCh66FStv1pltHWGdWVeMus7iLbDDytIw8UuSQfaZZc/VgBFdwjItFuFRajXICL4qDNF1jaYdL8kYfolYztdI95nO87ROQbbNkozuopUUu2VrqEUXA2iUVyy0a3CGcv+fWizzJpuzPPcSyyQRcfov8hYRIYtXpAfBQYDfCAgP4lndVpzryH6XaiV4SFLq+fJ5DO3P1fRUuT2UwqifSan0EVbW7QZ8I2/UUIIg1YAGqnoZWQ19SkPvlFQsooMgUbd12ecIfRcmAgY87+dx0tq7GsuiqtCyyMK5jEd14LNt5TMHrSYkAP4Q+G7YKx8+w0eYBd8xWhZlPVxXmzqW7gyBx2vcYMyth0QRIwdEKW8RGJYjooL++a84hEBMDssSllO0NeFTHaYbfvD3pn2qKOxiB3nT6xOo9BkgfUjnd3VBdWVBe1Uh3hWFa9g9auORKzK/Tx+asrVYOzwHka9ua5540j1MtUjFVvi8SAKe31AYDhzrzbG7miiXpFHw4Rxcr2r/ObCNZu2TG1iRLrOYPPxJ1izgeX6doibB4G/zjwC7BFWFb46hgjpKoEGPcHRa3KS19jW8OK1MEsT/Bdagu8GnU/fjNfYBUqHiE7Q3zVxFvndwBXMkko+qH+miPWQJwtZi+UeRj0+HtqkfJVh3+r/Qd5v6yNR+YuYtbOTHBd7gIsiXMi1KIpWDzpPxwG6ALiuqz1yo9+RmRe3I7WWb4ToIZldkTGPXersiz6uzDDxUQ4v+lzn0LUXb1RFN0ORmL4iG9KywU/AFru5g2cvVrLh7/Ypo67+9/kWYLgYW35YhXgBkEMOh0jt9pfbVgw5WcrumnRD641FVFUlKCNPP0/O6KHG8+H2KEzonS7hoMNtUP7mhneaLV62pqq6ysWrbmcPWMAONuA/ROl0j6iIW+u31/yUM0KqZ4Ux40wzTc64VkYwdqfHxOJIfFZr1NK9FNS3Nk3QNMusOcRgsVT08WUNbfMkHuMYyOq+tiW/Tia4e5mVUU96loWVOLzXR7te3FJfZMB9qKCvaM/ueTfQ8IVZQM94G1p33JO1IOE/L4O/46fj1a96GwrzCcTU4gPYEVAlYiOIcLeBFNK5VD2UynlXPmk0XuiqzserfG9jABbfX3J7pmFVdRcauUmgXntU0J+HaD8VTnhU4uRfbwi9v3szPs+ahTMC15umz6cTZljDtLX372YZlmQoGhvVkU7YJmWil+7x1ktU0kml5VDpfNl3gr8XPzanUYqNZzE3s0YSZTtjSrUrYWbNitoqb9SOaTfRK5tSmL1S12di7i+u4TMnTrrfdv0qeloPLGJmFdOFgRRsStlUgn59l9UOYgE/182Sl1rZVtoYxu6gxW6bhn1wegzG5Z5rFNrafMdkhzMCY7DxZ2lv96+qzMGZ/mGuUlTygjBk3Z8v2bCjgtZeMxIaK4nJSdWICFlLRdRek2oe0atXgozWaFHxc0o0mhZWSkXSVZEzVw7swlbYNCYNZMm4QabqV7kKYvsp4ZBlasBf7EWjS/+iyj2Ct04IssloulIm6X9n2T+YSEuHMk+gl1qxIN4GMsyKRTT/6+nPrcbeIb9JHWL6QAEyDhsUAa3Q4D+WNRS3jNbqfo22fut5Mp4VJ6WzFVVS9uTlrUAeGLcnAAkIFDYf56GfKZhy20a2xFJSjmI5LlfNl0wU6MdZWcKmlIcvDj8yjO2zLqsYwA4PuoDWLc7veA2IUlUaLVgYsY0wazoUrpfjnsG51HZmAr3R03gUrt2NqZhgMTxjEkrKiRgr68pyxSQn/6draGrloHNF0AtI4nzZdoettFWdb7uiyOhPx8w7v7rpxzMTAO7jLkyS2af7wBdbLYnF7BRG2FF84OMG+Aa3z0VxVxsRsLRf+tWhPZkeRwojeyOsaKXVo2h4Ro8DbHr2y0zJoKV1oQCeySFuwp8T8C1o7ggmWsnaOrEzJtspsi7i7km5kRg5OxobD7XZ7BuSx2rNePLVc0YcJmEdB011iG1b0mKaZXS1xWYjFLQuZ10nJ+Owk7c+ETCWltU37TMX5DsT75ACbZ4RUHCCCSg/FhxQFDofiImYJY9kxrc/Yj9MMX1roGzB2k4WPTgUOvT0pxNXlQY3u5tsm14Spv3wFHT2Ga3XuZBGakW1iCrLPr0CZRjGBxDLNl9V+SGW8mUVetakD20uHyLwgPd9k5lPxkLaWjF9lFVyYVt+ghHM3yQW3jXOthjIB+1rNoU0/6ASes/DwcVY8XBZVbeZbAVLGqz2QC3+KiHeKJ5Xdn4APlXOyM7zXPVp+OzCOjkt4YBUHDi+huzGhgF7ChyoG3w4+VI1gIlZUzY9N832duT3yV/AxhU+fYLa+b7Ic67xMgck5b6iu8dMLNT2c9abW1X77bnRbx9VuA5vOkW85zw4+/bbibOyPn788r+HKrAgIkDKm7oFcmFhEvFOKgLL7E3Clck52QhHAve/3joFxdFzCA6s40EMRkKKX8KGKwbeDD1UjmIgVVfNj03xfZz5FoMgysxRkoKTbepE53RpnEe6U5JN2fYq9WDYHOyHxcM+P8uoJRwEUQkJFlikoOBWftSCu3EYjlmmGEgbeDl6TdHwibpPMhZWKh+Bn5bXvRU1cVUlRLm4pjtGxh6KOigdpcFdOVDWl4Eo5u28HbxqGMhGfGubOlmfbWvNxLixXaYUz2XUj6V3+eKDqszF9NSn/cjWc+FffmuZkCAOMcIJoRyQ5ePTzw5mIcfqMs/+C7Kipl5gX20yJ3ARpcrBZ1JYnZFNXdEvOZtOBnTp0dBrSBFLSaY5t+jP7ceRZUTYr4nY1MrgIKuPmAcqFdSWonfg0SkCOuhMTcJaauLvDRiRdvCUfsbBKRuoS2DtyEod8JlaS92IqXpITeHeY6Zb8v31aUjvZFKCSjZw5iEYqYR+qb1u3Z6r6PxXjSebDpulNrW0QYi3XWAiZdshjiK8Ws4r5tpTvJF2fVuAx82HNd1ugflH8YtaSqAHHV8Eo5Cruk/L1lrCgYgyT6nDi/Ng0TyrMxorte+iWkcQyYBkrdk8b2bOhFLF9AHEk/U3XiwkYSUddm+bZmjNzlNEeYMEictFu+jzkfZ+M63bSYujzTH3DT598SlFvypcheZUxU5m0li5TGV3BJ5+bvEFN6rHt41KroUzAtFZzaNOP2VOVSUfSSj4nfmqX8VTc27YmYV2pzN5SvmUGMRfTMvNm0wlSYd7NnYTp6HmUg1Nu765xfzze3WFBRc+n2uHFudglZjOlOxEgR2C4XUxoouz9pGy3g8lLPqb39QnAARGoy0tQwcWPtF5uOEjFLoZ6Mrbsq7hwpakZlViUcX+8u+2WvZqA9yynwYoTpRhmZ0xGiRhYyMQv0lo6pvTVGvUNSthTtQrml6FWQ5mQp7VzaNOPvs528fA3eom5MTJTdTJuZlvdHUXUfjBzMbV0Pm06w1ScV229KGpoYyNt4JQqKwZxVlkpvLvDmoqeT6WsinOx/TbSFXxCy+eyQAiqfv0Yfe+6SjI2lMC7MKS2uZ3y0tuMZAJutZm/nfDgywZipwgYa1qLPan949KQ34IJSnqEE1aSW3N6IrFgqpyZrtceOay7s73IOz7BepXPw/ZvLn2/W5u5ZxUTXzDQOqZz9cXJG5Dl8lNw9fawoHQIE3KidI5s2h8QzBte0GfstYtY4aAjBhvwmEOS3gVpzaqeTKU7q2hsy1PrLYhcuYJ1U+b41StoczNCDi5XByhIN81Z2sSO6cy6MUyiLevmaSf05E2nLeWeqkL0S3sxBaCTZlKQZk9A2e5lx02+yKD2bEZTR66lsOBumoq6KfX5ITWG0TYLi55NooYYp8KmF5taM6rI3EiM24ayxuhMuJv7h3EYc/DrTu4iwihMYRWqCqNz6i7GWpgGMQeb7mDkBZsbz8ShUmhj5kTvdIk7yZjaEUzAldo52iGWvF7DJL3vbv8f1XWZ3jU1tGVRfW0Ny8orerCwoQc7yNJ2I5qOxe3meBdYXj6Sr2v8562CI5W7uzsuqYKhQeOkbHh0R+ZjtlqW8y+VgOFOobH484aVZ1GKYUs3Ei2D+8l2PXln23K03bJea/KlP/+KCx/77DuWDd/4r78Wz9yrsN+Wb8CDJj+cCKtZNx4rhMGsVsFQcbyMZx3O76BMQV4P03JSrO7SnADeohWTPsLy5SZdafLauuHREE6DwoOm1h2S3b2kBz6aF9azo9PJBdc5tekZXW/uxa8bn6VBZ4FiKzh+h+07h2Ftx9LYQUvPYlR/b1AJ4oxveRq0Kmg8W7E0mA5J1gcz8Dk3A1lHt4PjZXNq0zO63rbxvu8O4CH2IzD0Pgn47ZHqeyTK+6GYPXW2COZhcK07ThjjrrH8HN431/l2WARb42zjB/cdZM0wQoFbHVlTi8xhlRA8EZaKvj/qZWNatVu7eqwGPN9KsuIPh1XFo5h7cTmZBrb6v48Xbas0+ZnV9b3QyW+vwWqdwQ/FU54VQJ3ZQA4embs45DK3Yd/P0XlL3pcJuUtOaavTAKbmbBx2k8ISjTqBzIsjSg6Tg8s4bIB04TEFesfnTwifzb1H64cyAYvqp8qmA3S9LWBQkyNCgByBLXfRvaDs/aRMuLsuBCocv/1p6RFT1dPsx+pLDxY7s7K9XWJWy7FMt8WbZtGmJ0zFLWJnd0aejoV9mDembqnuyiy8tw/ic7DUz9KsxtndzdfUHHBY8SRXPYhFNV3xuda2hbLXPMI51oKZBexl8rzX5eqixIpRugLly+lzsgT5A7wCNTxpStRE8qKOIjbVlMYM40pOwcHGVqSGWdf3caSzdZ8m4EzrWbCT1ko028Gg5A83zmSqxGdJFv3MvCjtzNRMKCW4A/cx9Wdju2OQ/DzPUV+Sn9fNXZWUaRtra7xNbKooY0FFHRemNDa7U1eMbUczAW/bzqeLM2vruNpkhBnqTczTu+hTsBzLjAy9g9bdUZJJ3m1XcrEcXMa8A6QLuyrQOx4czM+q+nFMwKH6edqZU4Oj+/s0Q1/grSk5kAAp5ckeyIknBcyTP+Kn7MIUnKQirE3bc2c/I69Xuimf6irKB0p9N2dNUzM99D23pmgm/k7oiGi8j2jFfEGsDx7g7VlZrIx8p6kjd4nS4G4OUHVD07OdRW8mcU4aiW/TC7retjDfTeHKepsaozIe1czsbCf2ZXqmE8lu04dNrRlNiDp9RJPzuXi4pX7jydTYEco6cmNiAHEzJ9StyJQ4rvNbaFYYxzOJbWGcOyvdkK26FexrFJUy4JEYdjfdjroRTMybO6k6nixh8rNo+HB74bM6S64lAmnWXGldpxy6tq3LmFoY49Zxt+vwJuB41/m2WgV85RnTlydNiUjxcAleSFr/8zzFeE0PqWhqyZOZsxXc8pnrGrN/SSSKXmvVmQm40moGrPpB1dsaLuzfzBHYxpZHVAhseNPrsSrL5iXcaloa8wtl19HNwP6m+bbpEl93ttXwuUhAlv4LLvo+3n4G+UODvQEqBlRXkXG8AO3C65qmJNw99HzruNo8jgn42DxvNp3o68zIsRamncmkczTlfE24rbj1NJP1tiNW2ym5UYrq1KgGLPsouxUo6693/4RJjYvgM5rvhHQM5HlREyx/+1bBk6zEbFD99rouG1FkYtTXsO6Qnj6vi7JGI8gKIvSr169aEIqTeBgJU8qQIjLepxlUY+wAjOiukyVcNBm8AdVPGTa63IiMvq8rQ8ZeejYigw1cnK5AmiFTBiRLFQmlgEb0/a1PGcbN5Vn7Php6Zp5X0v8kKRoFo1DlRmSdlvEF1stCOkIGwIzOEP4tbcEY9u/aqFUrtmg1yMysk1bKaerLjEhOQA0fijKVruGu8MU8mjSpm1KKoyuyJYgOEwNhTR78wFVaQtWylYCZUVMZG6U4mWSZdgPXTgQLYzEfLfwXkDf3gJBLtzhoMCNqE04nZP2mK2W+QbkxIOkCRKSEG2JqTB3BLKBaBaTMTPT+yS+1uGMhzAjRho+vCj4i1UfaMwbAmthEs5AvBg7EiPI4Kx4ui0qKqy8zIulVexmSjW1lQoKtDHhdY+P2ClZE75MilMAZkV/Ap0ol+vsy85ossuwor54US3EotUKkQmHNBFhZ1nFAGxNp6gksV2lVIaW0DRSU9oqDsdAs0bacg+yoqZdYs26TZKsb0MEbGyPBQioOHgrt0NwU6zRR4iGlNoiACoNdLz6WRbNW9oKUGhERT6kMR+d2ttx5v+GYhNZtJZesEkCzmP2Y3qONsFwYsUsA7bHrENrRTyUwhkI7NBdFLR3dUGhEIz66LqeX9HF2iz1PpVltXpu2RKKiGPsutnG01Kum8mEyj84a+8Y+HSbvH//Em516Jn8FQKOoqd6fMI1B89qAdDzaZyW8GrNvxpZ45HkCJaXIuwB2iDRZfDUNaJMx+zXs0JptEza4nZFKk/k5dL4LCDA0NqSLkWGmUvcYdehCbk12902N6hK5AKkwDIY7pTad+AKIkFD2pSs3E2aJjFRC0zu5CscAmFV4+T0kqUavukvmYDLewNU6A3KGlMHZOjBedIh5GGsZo8HJgZgVC5hDxLHaFcnDmE3f/oqO1Pbd3J4yoPlWatAMhebe9Nc8pL3Z3JsxsQsEFWKBHzB9WEp3cAbAFt2HFKkQlYKzeRg7Vdu0XiQXO4x8R4fzytmODZ02MsgQyyZnESrE0B4VOXvQo2uDX41rd7UG6YOUeH2ZxfwSUmgFCwtiZe9/hjWyK01TLIc0D5wPjpJSQIxlc0Wr3JpVUXpG5VoWk6AyJxQxJ45t2OC2cw9Rh9DyLkuCBoydxQy10HjzWQjzFJZFVaGKmQYlD2Mxeunqt1n16Fet0lraIrPCAp61zu1NuZneSh+L3L1CHb4qji5v2ZNFqoLsHJOF5k+JNwf36kpDVMAwMvEsVTh+tkLcxwdQiLlTVf6cn6WONeU2p8S3w/Guim4SWNPgxCpqmjGH2kaySTBLiKafDQ+qSQ+Gb88UdNNAq8enriSjnepIW0NATQMSElKH2jGpd8sePmtpx8JaDYypYqCbNbVYpDJ2Yw/cR+I2JgTAlunoSq6sQZNgTBak25FQVzdlHsRlj6Bvh6NokaIKSPXw5BVktBNOyjVEU2CVUEqFLgKVei3FTCVZ/hvteLjEN5GoxCW2EbHGoFKv8+i4SIBRj4EHldGEVsM05BBQjcsu/SnQ7dF6naVwcVPQ/RSJooVXj0pXTUYsKrxGQystVgnd1FMQhXIb/rSh2wDtMr6+UkyaDTjHXXJDw/LYIA3JpBUsRiirF4FwUrQyRUw6zlgCnn0NWS3eNa8my8Sw/HFkC44wI5ORiI3likWbQYZuTFslfURY47CEKho6WUp9JeaxpRjfsFlfEEDtR2XWGDzJNY3SQB+R6BQHKZx6MDJwGYm44D8NeaQYJ1E7mZbZaEQlQ6nrGBlAWVXDYvZkNDYiIakWeyS6mteoFNxtoOa1GkbIaZYsG/R5ewWrukRMDxdUzKlIRYta6sGaK8toKsSvashp0YJs29iMOJysdOzr7RCjK6GkFFAzNBm8lF5ceK6OXFKcI1Ooz7WnoQ0Poh4BBymjBxWHrSEFj2giInCh0mpSsIDmcbBTG0wWFp3Mp6/lOh8/RH9ZjuqoxBMhQmkcCAKw1BuxCf7XOSNEXBKqKIkcQpDjNMuQjBvuEGiowoFaDIetEYE+HMKJiNTdFNhcjdVQSYA1j4qvoqPT5l6DBbkExDI5pJoAD3rJ7h1s7rxLiKavoB6gtp6MfIqbExoa6puQEJK64RFMyP5aiZZ4IpB6NAKsjEjURRcNYURUExDjpFiRk5DNfRs5PQQ4/Th48OCVJ0UqoY+S1P6myxV8TOHTJ5it75ssx9KRKdBYMaaaRoPDgCCYqpbNaKSbqhkPcvd3obRrUwRSj06AldGLup2lIZSIauS1iRvs+X1zSUxODwFOPw4ePJiLpEgl9FGS2mdtFlmmZRQWQLMEaDips6C9MKdbRQyKkRljc9Xvtr2yJx86DaLvOQWpGv5w8dBABBqVzC0io6QnCb4XNdEfyAsD9PVHOTlU4PrxKGqFi10NbgXdFFPgQz3uSuUtd6lTQkB9Dc04tRWl3CbeCdXRUY9fs211Y41n7RmJqIC0MM2MRHM09SYnku6Ordb0s6uoHrJVfXl8oPYWsTZW0KbJkc3F4eqxlroSKPW4RGAZ3egL0RoiSZBNQRES5WsmCQdmGAYLrSRKH3psogqHbgqy3NJ3uxU0oWEMI6BAFdQAZjLQSCQ0YO6qx2SO7qq8ljNaGKt5bMcShydaXHJiCBQNEhp0xgGd2KDgbNY6NZYYooNCp+IQOYE9aNMmjTV7dKVw6sHIwGW06TMqaOgiRTWyF7dtUydPOQhT93VS1JoGU8lOWfqJ203mLHU4mLyCeljaerpwMD7HhUVsmLwNTWzYaJTskoVYklGSjds8PjYZ9wgEZBNwG5nZdwkSf4uSXjyIYf1sIJWr0Owu4zFNQwJNWIkIZNN5TQiJMyGmCRfpk8zcXqIeL0EFFz/SekkljRFJY6qiHpyhpoxsVIIcDdVMiFXsFMu5KMv9c7vJ36OmobyCeaDSejr6OUgmfRsSUirnKBYlv9ETak1OtpbjeJnKYxKWbWh0uYdTOBlEPwVikFMbSKXE6/JJmSQehWlMEkhyTmnVUS28eki6ajJKKVJmaYimbWFkPVbWtnGFmiu5DdZz2Tji9Z0rz7hNciCmJCEHoR4XC6iKvTRfgeTwjLkw6Zxrt5tMbmoisIDmMTDwOpKYVTQ5SlnsnIrK3qZzHwdr9CjwgCYbmIMPsqd5XCM7FZgEewZpLoXUCQhZBblUYNMAaqWOFOnIQnvTeTMLKWEtzoosGMnx9GliduKzK97SWdZl8kgNrhMhylpy2SRkhdTKJzVytTHJZJyPT0XdulQDOwxStzoD6TfVGhUa1vgnlLAO49J4KwIpNo3vgo05M98R4gA1YQIy+Lixc9OSSJ771IJkhorGwerra65aKfO6mglsaHPk61eaZLa3qhS0kuXtgUazPt2xSYWCPrGvTkB4dECmUNsxRfR1os9Q7Lp4tNh8uVtP4QmXmbYj1nOqYKqA5AF9AlWSx1k5ZQyYkQQ0dHDmABqXWka1/Y9FFU1aadtsC9YojBSwxaShtD65tnkSrLswXWYHXZfM+7dN7SCqmHfyMedkUiVK1xH2jTev6WBQBFGFxjTTxDBdkCZKY97MG2uKPFaI/7LwWAtedJ6X6/vWteqrdV3vwWsV1TGorVVMeZgR6U8yJ98qCh3mQo/HmUxadA5z1KeQdp8ofQ/Uk2bkkgCl03JbcJT9tgLeTu+cSVTfXoPVOoObR0fV1OEgzUNiKwRTiEM3cgba4bER5t6FjD4KSPWA5BVk9KGfQ9FQSIFwgisim5Y1W7wIZDMUzTbuTJZJt2rKz9/+NGs/yirGVaGqqVltmiMGpxamJqYTGX0IOCbpZiIa9+6x4RTNpbrD4NVYrEgtvvfsQnlN45OdwuGHnhAHpCtQvpw+J0uQP8ArgEy14SkpievZWEnj5zXVlSe2KkwZk814pVvO5jGtuKQkf1jTkIW2HCRTKQbVWIRjk0vxdpf2GNhYRz1SU1VpjhXlS2S6rCKmhkY+Ila1r9miTFXcB6vZrqISdZqta3i3zKhvKyDVA5NXkGZto15W06VtkyOcQN8e3lO71QQOiUCaofCwUrJQT7zpyCLgGjlISHhqzXxJ2FWkqSspL0ParzgN8pEFGPug3O1ZWax0pNOB63QxZS254sc9g6dV89SopyXdTeFAOArYemybOpGJRiEemWTUC4G3wmuBMgGvBtcJZWUtuain30jUCns1Xpl8Ex5MjEo+HbNJ4ewGpmMxL0pNFlArfdLwVvKKoiTA1rauesi2KKRRa6p3G3XhuLbtycgueX4yQtg3+ybj7Xme1inINDdQdBXUI9fWk8eCCy9KasPBdejHvQwlfTHzVnzt0kxMZV37gatQ2JDY9tKUZYsSqhsn1TdJK/3+pzlDqxxaPWR1JWVuVv7ZUg1BNdhHTmpn2pEsdyLTDmTcecbYcX592yLAQcUgzWE5lP36Fj+xuQLdh1/fIpAErusGZF+KBcyqvqB//H1Ts/vy6noNEizO/8f161fPqyyvfnu9rOv1396+rQjq6s0qTcqiKu7rN0mxegsWxdv3v/zy72/fvXu7anG8TRi77leut0NLaG2h2eZKcbr1BTxLy6r+AGpwBypE8ZPFSgC7XoGy/nr3T5jUJLr6mZMlvw5U7huUPxoqs0FxRawj9TXx787Nh1slrrI3uHvCs6I8sg1lz9BgMQQZN6RYwIwCIblGCwmU/TLqhnK+QKQpsmaVb/7meVFd+7J9UpRHQn22x4X/z6Jpv9hjOM3BXQa5vgwf7fGcV5fNXYYTDTGUGb7aY9rMCJ5+nkxiqT3mbxXO6MZTbPPVYQ5BVT0VJT+Fw1eHPpUZ1x38wb4+fnv1+qWq4eoS1EsWFV/mwBXkec2NY4ZhDq7MEet1Q2SHBOdQ4trP9tVUnlP4MhHrr285GcFLpLeCSOL2Cl7WOUjCy/5l4XAxeCl/UtlaBirrjyMAw4XWWZHh3AQCHvq7k0B+TFG9dqmIWGXl0wvY7hFvNJv4GW9+BsRSJxFSw5I84c5Jj/6zEy3xGk7x/QuOhsN395lBisZ9+oA5Xj4zdLk99q/39xXkBFH/zR7L53SVcki6T/Y4jkGdLK+Rws7ioT470AwxKl7lHKWGr/aYTgocXlX3jy3LdwMlkEM7JQQ1/Ee6PiqTZfrI9VwsdcCcQZDjZJgMwv7j1mwI3eKFeOmG7Qc0Jo/tQF99W3eDoywFHFd2nxykK0yKfMFhGT7a42k92DSS9ssM+0VdrL/mp2VZlNyo6AKHeUKW3lWTf6s5FZ/+7iA1QYX4FjGvgI8tccOI7H8pvv67Y/+aJEHCTN5Dqsyxj+KMUJ9dDK5P6WIBuX1289Vpl33AUhvtEQlxK3JbLFvojrc7XpLjHQq3RhqzcdUh0lgXPW4hjfXVt1Uaf4DD4TVvuVMFDvjSap2Bl/axKQ4jW+SMszuSkKDsSlxW4wd4D5qs5pfj8HmL+Bs2vbpW1yBZhnvjpCi9ON4KzzisTzXOI+KKnDZh7GvFHCouCKHQxf/E+3pcPTxnEhfkmbP/UTTJXI2wcIHzJV1BUe3afN2ahbe5jBKy1lTXbSyWl7rqOCuqb+9jk/JeXKbEyYPbVxUdp1yZez8FlIH4jtMc4Ad3ZSj7MhdznOSyEVmdKXCTALnoQhu+Opgsz6j9SpBv1GeXjfQCv8THbqLkkwuOmxKglgVllilw4Lr1AtRw8VU0ftiSrZE01H4VbWMP286nO1xLixK1zxsb/VeH5VEWK25pkC9uGMTtbfPVwatQcD6Fwq222Iv+mz2WK7jOXviODB+d8YhdYgocxOIJJw1PnDyvCbeiyQcHBU92muVxkHVcLDiebb+4elFlQootcfGJoU2lDd5hvWLDZzdckq5Rn6c69NP0cPEF5A3Ishehk1TJ1gh5eqiBp4gUJp9DRG31ceR8u6mIU+/hBRAlEVNgj+8TeXWZRtR+cdi9ipJ3v5Evux3i0Dqvryt+vjaf3QyA1qOCpNoCcWMKskoYrAxka9ZtF6j4BdbLItAWZFB5rFxD/ZFUNLpR5Zm7CsjFKkmyZgEX9I2rc/6IRwnk0Q6Wf+WLugmq3B17n225pYiyEQmYi4VJekhwYbuti+/jp1sD5uK1ydNVsyKO1qNV+9IW678Ryx2wg2c9dkm5w4EjqXEFq7pMSUyDglA6OAczoskypVNdKNweMWe8tR8k+QzYfYShM8px5OOmAzwetsSBX/uBCAxKF3jgI3dNlEiHUqco0hJewwxZT/wROFe0vXwembFjcPKEoc24YUloc//ZdRkoMAqFLsc87akqf8LTf3WyHxJxa9l8dcfUHbILh+Wy8imPWK6XJFJesQeJpa6xh/QFNDEEUZcnUI35U7qAN8tmdZcLliJXZI/ze1qld/wp9PDRYZ3goPhqyYs46vOcZ9nxPUk750C/7NNYRBDl/gJ8YrEtu27BFblYeSUaGXnZm1xDk+4LChjnBXmeL9LHdCHx3UkB9kR0BtoFSu1usUqRpbJaCcdnbIk7f8EVkkeiFiopdqLv09f8E7LdL4Vtky9zceW1ddTCVQ7hYKHCGvwHfHlqU8swtilT4oZRyQ9CoRvem7Tmtz3qs0uoblY8Db4W8uCKELkrg3BoYb0ui0e4uAI1krDXDXeGJyl2kAZFbUAvh3Dv/U1Rg0xOHymE1xjUjSiBnA/GboqjhNO+2BLHixa4Q32GNeHKBV3o0NOfDddB/MHF15Q39yCpmxKWaEerL5BiyUsLFYx9Kx8RN7E42y8utvXm4WPWsN58d+hPV0emNPBlLofDvzdpCb/WS5yotU8lxp4SyyCcW9ioG3L8TLnD+m2Q3EILP8GKxtFiwWHj17IR2ikAdcjYzEWgDt/dI5iE27fUdwf1P8/a5UmlY2aMAEm5k6+3XU5f7xX45RDu1Dh9XqclidX8AF4qOWV4GPdWSNIcgkF6/1kJ5aDdgIpP8s2oNmKxgzRlagrXFIVSp17jc8yjhxJCUTcVS90OTYeKNzijgXB6yhW7rMshQwm/MKkCF/nVVTp5STL4GeYPfCSwHMK3hUtYpoUwjyoYj1aIgkHQCJJYBuFCeXwYJr1OxRW54DxDfNAfsvFI2TIXi2uR4pUMsr72yRKnLOGtLxWUU2QkeD59RpaWGBm5KXDBd1oJtCWfHMLTwPMJMvseCnw2ykWpsUVOOhmyzc7zR7RiUeX+NFRQyuRATvH9yU+5R5krcnbokfpHjyDNwF2aCejVUH4tyQchh3A65dVgF0sdrIHiqR17l2KOn1xZuZOVlN6/EH/HWVH2/TuGyDYVLCU1oMNhB0h+FtiFgNMFCYcdfKGjva3O/SsY3vZpgtVtEldId3wvn3c5hGsL7RG+rgUewpFucNFVFUIgJcVOqw7vO8fNy3FT18ItOaHUGfOPtFpmaVVr0PMgDpRpZU0GEbtfIjtM9IzJIRyOH5A1RKqmCX/RgSlxCgrnUTnj+JotJGg2X529oSdCtCBT4LAH4Yc6QSbpHVvih5Fcxf6ANmH8fKIYv6uF9GvxNF9YtcfDOXBY5148zXEciozRZADOsU0MFihjHy2gc7STRXs6QCdLjH5whzPDdG/xqHGi/RpW0jcLJeMwAjuNpT9dr47W6yzlbQUpgD3+HzB9WHILvf/mQB2Jnedu2f1IFzyS7pMDvSTj+eQ8nmGP0IsXDZhHWzrBogSa8yA+apgAJFl6uI71H3czOEAz1uHFXzF/IFvmHjiE0zGIF8TZMhfNv4LddiZxVIilHpi/QFA1JWzfOpRiZyA8WpCF2AqFHnjxDy1uGsA1tIwcXorePqHQOWQNljjmTu4kUoB4toFpoG9igPAeRdG6mgB/M0UDtjVhPMPGHXbjvo/ocw/kUVcdJ5IH/z8sXqXvsfTsgStz8rCrYhu5om0Jmuxry8QPX+biL+z0GNGyYUrsMXb6ioCP/u58OlqdFM0aX1dYCHd0xXIH3UGB1Q9bPwvk6J8o/yp+ZSEcIw7aMzakLcgiDujCrZF6ncs8Dcwt23vePcSeuuq2ij0cLydi2Xx1UT7j5r86LmpkrCuxSopd3JzhmRP7uVZF98nKD2FyelxtYKzqVEwsdcCckiAkAeXms0svH6CYtHbz1TMssLop2ms+cvwmWPf+f5UdhwiFju7mK/wSKo+TLpg9xHVkx9fuBABuo5tlv291qKlH7u1/T+HTF0FLFAq3RuHqpGfgbZEWic9tEVXNcZStrjlZcjOuyOXExDWHn1LswEKe2IwpcIlwOeQjE8v9rlPFWScMLv/LVVOvmohXY2Mqcf4bzcw+vc4VEZ68VoIwwNOnxTIOa/VNq9wfftiogVw1/TtCoolsAe4UBiD3uDEFDrZzmuEnUYbMK5zpLJS66ORdCKYctaTYxQnXJgzB2iybl4Z3xqnhfFq7VF6JkEF4tcCmPlK1wkH5tNRPgNS204D5zRIRBrrpGQAcHLvPdQnE/MrU5+0RydSZaqAspjD5CGFt9W31PaK6RfkJPpN8KbxnnCly3ta3JGv+vGpnJH84i83HLW7EsP2qp8on6eeNPK/OIMA6a7+zCDcq+OL951b6qmgUhqUR+jOtHsv2My7df8lFEKbswMBeDByPcwNZdg5eDVcDYh8c0lRQHc6pYA4HdHpch2O03TtGOxwf7cvx0VxxNd1q6d5biaKctZlV/NUyVf3tV8gkothZDLejF2N46e8O2EjGHg5T983lgkS2vm+y/4IVubfO35TgCp3xXhRqtENZ/CVj4O7zCv/+ev/fhCAyds38d59gMhbFVLzeNytYutR3B0Nh3T2NzHVp89nF6OiTM/HWxub7H1OwO8rg46x46NPZufOltvZIPNk2Jwo8psDhfAQN4bKoxPS61PfZZ4nZ6TpJl+MlE7hpalGNun+27St20U2hPd4foOpGw6Kkv88+jxfwqfJfbdra4642UVFhS+Zbv5goskTx9PfZ5/2yyLLvBVajrmCCn47xWLdGFCOtV9TuUV49iVoAW7J1Jkpg5H+HJUBZm1pLE58YZUucripFekEpxhtV8V5h6uucFeUKSFIx86XumK8B//w1W+LgyIqegjh24quj+/s0SyUeVabAAR9OU8S76Lpv07uPzqs2fqd/1I2jGFfoEDWjCCLyixb6DJCGKreruKI5rSHclc/FQ5pLr+mJpW6Y+/RWSuQCwNbsVd3sBJ7K93PsvlUpa46zU52lZVWLrE99dpt5EdXm67R7FBLHa5C/CBpt+9EFjzSWzi8+r0b8jr49pnkiyGih0KGPQqqxE9fUYi3bveP3tf6rM6b3UkzvXTD9I11jcx9k4t1crshBK1gWOZTlwmYKHNZPn8SXWz+bz1NI+bn0erIGgm/0tivJR61X1dzWw3RyvFl1Eb6Sk89NkStOeV4PvszhHOSp+AzrGhmSVSEuP7HUAfOyhFCHW1LuMEcNMq7SRIqZL/O4NfgdyB/V7kpmOp6Nfqwaeig9kzxinxkNk0osLg/hZEKwrTIqdsBPfO48f8gRqds00/zJFVvkEtc+zbO8YzxmO9dqo/XVwMVGo/JZa/r6Yzni4xkGEVSLu7sSPqZAXLdsya5tUTMxd3/WGMbXPRbPc1x51XG4+TPIHxqceJ7DQn+fNozmuFhwRm37xTXysHXLSh/0GooccHbBDF3dE9ETKYdwkAWbZ67UjSiBHOYIPHBEab84uTO2Om/YHzMKeKyI0p1zT/SiK0yG91g8ZLi66rYq/32PT5qMpCGQbgVDocPxZZ7+3sBrKLHKuSIH11sGHs5XqD84gYY4dEmxQ9BlzbmfyYf51+AfWidr74Ze1yWJuKiKpgy1O2QYfda5FZpd0Nv63ovLiS1xxyi5RswVuRwEn5XF6jJrHvjXJ9kSp4P4okmEdUV93ppV0EdRhXF+j8Uz5k1edRc4PIZlQt7H5hTe9pPD7tVk3BbTfplnL9h2Tf5gyena2T6N/49pkc11RX+I+gy8/DXg8QyGVVUeZ1/ALcoCYKe2mfpHfL8XNb/2uKI91s0x4cO5z5PvdlMTCee8NkCxk5qy2MWhaCatYpQUwWTD/9jAqkbaOV5ZEn2ALZ7zsHLbNasdPeqnj2DjXCvAmAKuFsirb6ufT/1etO9r0YpAdq8wdlSpW25i9l2+bOpg9j7OnMy3NAK9LRk7/HyufR6Wq7TCIQntnafAPZ/D5rP/G1Fs6xqMe+1gSN/G2mnD163hoNPnGl9ayo6aeokaTRMSmBCDm3SYPTjLDd04XBb3un+MsPqeKucLTJP7lNeTZOXu2DsRb2pEAmbf1lc8sTfFT8g5DejvjtiOkgRWlQonU+qgkeO4JqR1quSFrHxrVvtZUTar8ECaAY3HOtbUHWfR3hTrNOFRDB/nWvzi9W7Xe93nl/JsIZdbcaNt5xI3Eb4kbBFhbRA8votDUXmc1UFa5FEMH2dbHZgEstclmQLnGwPSywKOzsVWevJ+xe6rPSb8Qg2Hpvvk4gCrallmFvq7OzbVTMrK3bGTrNtSvG3JQWa5ySwQLqx85dSkIupjWTRrqZwaSnb5WsbFsPfwkqX/PIeQwktTqj4xBQeBZckzh3ybI6ltRAREUNsIHl9xqKi8rf6u7b9Ytk3cvWfnNWQuwhZMO53ua0VRb5xlQhqTZQRgChzxiWmkqM8uR5GS94A83gCKa3l1V947H4KQ81EodTl+bo+xFKglxY7zgq8dNgJersi9v3K0YqmDC7F9u0qOWCh0xttecVb6J1VA7hx30pQlzJMX8RaBHMKlhbbeFeDjCdgS9z6zB5aqNSNCOVj2oJZJHOqzK183dzVO2XyeJxnqmIy9eQjPFvAFZ30LA4R7CyTt9PDcoG4scsjAFrVjk0O6ttiJBM3YeAjPFjRj4SE8W0B1xbUnh/CUT0jOp1jHBNkZhFKSWYDHaFtKTAvwGG1LyWwB7hS8gasIV0v7r478Iec6H07rV5sE3abItXdi1nv6u8s1mfsmX8CF7JlRvswF6xMoF5dFmtfVD1hCNLd8YJ8CxGGfW8LkZ9HUR3VdpndNDZV2nh4yoMXqP1eZoakWxH33VoV8ysodQpEi5+kcNO+1QiNfOwdYYrsACV1k+CJxcIJYRKZi6CFdglfKhRjqtfnqhklUQTdfHTFJxuw3wi+g+gkXemqqYNz6fPL4+F7scfvVDdPp8zotSdDLlyKvlyJSAcAX/39BIKEyX+7HwR/SEib1B3iX8rd7VEAuLqihWpd39lORSdxRKqiQlmQcpIbyaukY5D9FU0sK4I1fXKxSAD/85ydq1LjMC2ufdVCFeSj3wn5+B3j3KF/ovi8QnUSauVoO4bDSGqQzlum/yDK9KUFegQT/FDY2DVx4ayKT6iHDW7yCVcPnFTfBukjHNc6woKGnHCKkBdmI1FBOkRCDlqcZkAbM5fJKmSxBBZVeWCmAi12Vyu9eMAXuHr8uR6DU39eXuWPFBhdOeNzU7R6t9dJZV3I58ImT930XjmVoM+oKrkCaCzcrFCD2bXwC1QV86oz1iwJHvCaSfG8asK05DuoN7m8Vsp0+pVUd/hC6BKVPTmE7NOMcHg0eSH7NUN8dHRTSwwnHrWLX8j59TO+J/RSRuSQofZjLDs04zNW3zWOhvztI0AoufqT1UspkQqEbXkmqGOrzH4Bx4/BqAH9OdoW/U8cobpG8G6aGcud+2RkcX+ZgHUi8te5e2vOq7wG5jggEnUkK4D52ZJiuZbaSrNxF80nSdYpVDUGn5Io8cEpuLPFlDnoxzLHWL6q+1HdXbJIOMgUOHkJYVcIt+eGjCzdtyE6UP5GbJAB7LFUHiREh/MgzZZam7ohhSLhBRejQpmh6ffIy4nPdf28AoTKLavPVQfHI0/qyTBP5ybBY6oFZeu4rlrpQUtVf376q++nbR+mBs8excm8DtdurdNAKEN82pGRQgDioDMZj0tDjUc2xqPdx6IfiKc8KsJDkL+OKXDaqvqpS7ZEAuETKJjCvYI9DCNYXix36juTnD5g+LPlXCKnvDiGQTb7IIN5luOBH6ruzfD3BN15lErYt2K7t+YIkyArenjEa3+1ZXnfM7Tl8K70Qsh9d+CU8uimGJ3JlkfR08R5rirSHNo7fSMTo4zaywjIOp8aN9W6HwEml7psrlmNUgl97kSAbitz8TjJrnv4+vS23cysInyK1iVhDVk2PxfPVLXnV7ZbkNyVIfqKByQ4t+TIHrDgMUaanMAWOJ4tQfgTKlzmdE6aPsJS/FiwU/gFWT7izgsYUsIqmdFkMbUrOwfvvHg4Q+dp09h+7Oxpm02Hqpsyv4O8kA2ag8kKh8lJbtPVHYqNIl9xiM1BcRSqe3+sKgqrIz4qynS3e184VuuAl0w6J7c67OIRCb7zqSA4toPu8yZPPi6UunAru7y/EpNH0dwevz2KV5tIQP7bEhdLU8pVfHVSA7GZoz2ybfkH+PEHCN8bGz2Lz2vxNKLZbcl+CslNdxJuudImrf06GkS1xUXY2NJYdScvKZzPaI57XjOGb7keFWBiWcEEOKuTjZiHm2G0P8q7nm+s1TIbIxM3ch6UT1qH2yS3shm8csRhz+clH8nUtDcg2wDrexjlLM7T4hKBYvsxJhD59zTsyqN4XYIqdveK78/yGbrICFQoNZh/lwgndSIqGtA92/D950qT94MQxeDAa9+1QCq1d44ZO/pKHpSPs7AiP/zYurTz3TM87L99BmSIldlgJJ8XqLs1B+K5hxu8/j05IR9pBfjbcZoE/OETk1vzDpe0XhxNGkDf3IMG380pk1kpvA6tgHMPBJHFgTmE4MH/gL4/33+yx/EgXPJLuk8P9KclB2Cf3YB1QQUIE2XGxUOiBF//Q4qYBHLTwqkofcmT9poQjzvkHGGXl7md4OEWpcH2JK3M3vHGQJI+VL3MJPJM9BbT5uj+uESRBk59y7wVX5GjNfW3qr/cERZfYUTTqRJBt3/1G2fLi7XO76HDoKimNLFm5Q3gDfK7RJ/HRMeq728WJ35u0lN2X6L97LOeTIq/LIpN5eVUw+28QxF1zERbb5KvsKEuBIDbJp0kNSs+czNsluMnN0FGkd3vnNJoIV6AbVY7zfVCIYBnYrnEzqluU1783oITV1cMdd8DCFzqaQUeLfzZVLR6vC4UORg0xPlSIxVKXveyyhNcwg4nkVgVTNMVeo8JIloNsY2QKXG585D/x8Z5UfxEK9zD46yZFlj7mxzBhOKDxkHyautuvrsbOkI6fglUdwvdlcxxDu/h15jpHaF8wCDo2wD99Tgnk9eZ2FSsjAErOXUA+ODB9lZ3m4C7jNwr6uwM2mDQlFPpEfXZw2ImPCn1yflGoeCgGlxa3I7BFc+6En+pVdlwsXvgu0t9d9KC8Rozd+90uYP1UlD95dUgO4yRE0IJ7IaulT5IvSmE5jHMrp8/JEuQPkKQn0zemAt0a0dZ1KvRuWT82r0frVVW3VcjpXn7we/FBvJXucSUdL/bPBSoUcpgyRa6aw1lRrkBdC9EyYqmLchz2cJBygTZ3WVot+d2D+jynYN2mgDvlqAucDPA0X6AZ5eaFK9oaEUbYpPsUQUnrwHx1NWX1caRZ2zyPY/PVFZMoHenv41kvcxmpS7iCxN2EdMtAQ5VG5WOs6uuPwz6kUe7Aov005YYqSbLnnGBvRxjuGCQ/z3NyFElnGQ5jPQVSDya0xjQOO0a7dhHRDxP3AsPO3QOmA3du4AopPqEHZTKMHqxqh2ZbbQj83Pkl4MOINl/3+Nj1BE3UQ1G+xOEmHpuPBWpEceCireOiPpA2ChNxyALieg8stDss9BHmsEyTSPEfPDaflMdGFNtuRP4HfGlfAmcwbb46YRKQHGwKKR8fJdkVTIrQXN0DGg/O1dTddpaN6/cY57T1vDpf8G7d/tvWsOG3MgobDmg82FBT94/FhtdZw7nr2y9zBKarnq3UP1c5lyztH70MlKU9Gh9Zqq47DhMr3qT3eos+1jtLMuZTs95cvhEIqqaEbXBcoFOERuXjDdHX31bro33MC6l2T1hoswKLLXI6TE0L4TQVf9pjW6ab/w8parIKdijz2PwZUoPiwJN7zpNnRdms4p1yCOg8uNICx0gnG1Sb4jsYYulc5xOEQDyq4aPDwWKxThMez/Bxj89LLkvyOkCXYDnULUnj8vJK6hFs9yneWVmsVNzNl7lwpgonW+K0tv8Jk1pY0u1HtyuE4uVB12uDQHJlEDh6GzqF/filfUaYRygUe+EeXn5Soqcg9lhiELMGFXwuAsOXKEReLjxN7ZEMz02T0iupYvFsx/bS98rd85juMnNizNEYFAOFMakcw0g72ggmQbihIr0pobkmMdt5/2oN0odA7b/H4nW+r6q6tSZoJI0CX9dgkbRf5oxZDo39ns3BQnToSCFQLDIv94oBw7ay9nGSkOdIO+8yzwCS4l1cNvFOT1pqJNK32Pmy+S9g4PdmcWAeyDI+xw9T4qAj1jVIlngNvDtLxTNVSbEP7vd63O9DcP9Jj/tPatwziboL+FR9hnUNy3jOOzlOD8Fni2gk+SdtXXTo6eAc175kyY93kDadGrAjsTUnS5j8LJpYSYIEdD7qrBnHtm7+25wki7wZhm2nLpRTjlsN5dLvG/B8+gwFMjAFDlQFz30Is+DSZIuctq+tThkWT/HaFsESIXGUHGcMETNpsiihdcGHJwOYUo4dkjCFrd7Z3qNLmhK/7XAJXnCrsd7VlWH1e1vXEtM4645vnscmK7fHHut10p1zofNki8ttEdhsMrn+kmRQloKbKXDEdwnLtFgIOwRb5HI6XIOM1OacIkyBi5mDpk/+Ri1TModrKVas5nme1inIpAucL9vjdU4SrKT/Inn98EBC9xUen8+OYoFjnLV+Gi0w3i98XImNZMGRX1PiyzywKhC645JcX2IKtobrryA+f+hzWYbubTQur51Nj2AcXr/s0wq84w2BzXcPbO8V2N7vtY5+UhZVhayRLApH8dh8bGEjiv3lqtl20sBIKL8IqAkjn1BTn+EjzIRNjfru4rosyroP8uTclXSJQ+hlk2VShEyBg4q47g50OaJtPs8V2YWfYxQyhA4fXYzne1iWsBRwMQVznrjETxZ3VuLXtnHuRi42d/jsEmFHcoB+AhVvHNIFWyOi0Aec9DAw31yLxCfVnKrmtp78/IGu42/OXEItQOr0xiehnK72tvLJzumr5E5L4DRjFD4TLK83ztS2ca7iBNPfXVxGSdYs4Hl+naLagDPExVKngw5QVTgYFxXU8uMOEcBFX2gr8wpD/9XB1ZjWfE6H7tOUkWpfYA266GVOOWNL3DAq3/IRCt3wSmhGfZ4//u0K5kg8HVU/0sUDrHlFkC1zOBYkNf5R5NzQ6e+u2H6UYN2pVjKkTLErbvzeuWSmhEJXvMcFFv78gubLnI5x0SbNP2Kx+eq4lvGjvuJyJl+n366OqqpIUvISrbBnoa2UxIzeHq3XWYo5nVJYNBuVvh6/PfXQKiUE7ZELcfdXNnB7XTSl9BkYq91uQC3b9jAxh74EdvMGlHh5e3azJ5ZjN399K53vEJYY/G9ODLGppWIHhRvNisw99q3mhaGTYZzQoZmaEUiAG/oTbavLYnF7Bau6TLGSdIJHaZIQNrUFW5ap0wLaCAtzW4FswjUQgVksuhwoPlrqTc00vevtlrq2oXssRIQWXwVpYXr/oJm2ItbA6R8QRph4SefCJrrDM9tEH6dZhj4P7luL2earqKbcZa5ZnIE05ZDFnHYOdRhjOncMKfKLFM/nq/Pqosmy317fg6yCjqMPZp5LWK7SCicDapP03dIJEbU8ZKop5L3g4GlwC/bSNxc4ewLyCHxm6HDgnkLhmk3eWLOKqoZK2jiyhhz9Nu40+8ICV/AJlIvLIs3rqguIvf1WwcWPtF52zmBdRImxshhDIlSx4AtjQ4EzwOKKwCfmDm/jJmUiQzyB06vqLhqOUCeGisMhDeQjHltMicPj3kYGMo8/XM3pDPfu3xvwoNdtZOBiIi9SuIGx0WJExKGqi5dLwrpzUfwlCFP0Har3tWKHOEhzWPIggzO3+zL8XfUf8ISCB/ilWMCs2tS7TpZwBciIqjVIiAW/gGdpWeHQcHAHKtiCvH6F+v6YIqnXH7l1Dpffs5OsTZHVA3wBeXoPq/qm+Anz316//+Xd+9evjrIUVDhuILt//ep5leXV3xKybkGeFzUZ+m+vl3W9/tvbtxVpsXqzSpOyqIr7+k1SrN6CRfEW4frT23fv3sLF6i1fvUNrheWXf++xVNUio2ecOjjtI4mf10VZf4DrrJDcVvj1P6DAbf0sX8H7VypZ8utbvuKvEv7C3fjtdYqpS1YkeaCBRJgCfLc7x1CQdPj1Kyxy8MXQQey81aJHf953F/+pVnii/u08X8Dn317/L1Lrb6/O//N2qPhvr8gu+LdXv7z6387Ntwe2bcv5IyiTJUC60Bfw3F7D+O31u19+cUY65B5q8d6ltTOK84o81pmE4NjwSn8jlaKwIy6kcJS5iVZ/ZmlVl2JcizD9w4FxXLwkykyP0hknTlDRypz2SZi4PebTvUTuPEE/5IAZo+ub/Cu2bEaf7VnIvm7F74ngG0XynBUZEoWjoO63VTrQxYFHpxKcWE1YNNiDdwOqn+7bCl8/aHdBEgNxCQ5Q5Ej131bg+b87y8qywKuX6Bhx0JEJRdrcffqAtagoaL/e31ewDtlrSNxJCIJjUCfL6/RfMATJJWJ1vORDmPGkWK3JNUI34e5K8jYS+h/p+gihI5cfA7qcQZDjW1+2KKyFeLeyIF5XfxQZ/hcPIUhMFBesNixyDZMiX1QhC4IkGhW6ZSFSCQ/8b2YEf420OVi0fgGf66smv+2qD/L8nYc8R/yz/pqflmVRhqyxrkvklkGLBd87qFPMTAEj4nYoG7b4DKqaXMD27cuAgPw6zYN7g1DE78s7T8o0SYIEtrQ/HiOj2Ualljkv7PPqU7pA8iuEH9GfD3hnQlteQlwoUiFhqVMQTMPVqKDRWm8tf28A+fQtT/fFL2KxtXg4BKiAY2vUNvPeXUpor2aPg5s64/Lbv3BC/XvQZPUIus3fG9j0Wt6Q0XJPOJEam7s5xVQOsqU6zzFmMZqBvVR7k/vGRxCfeXkxz7QuTKvlQRluaCx3aQ7KFy/LzShy3ntobV/QbinXHJ0QW6/ED8VTnhVgsSeLrx9OmzK1bajJ098bmBKU9ynUKuHu6+xbBftGKSeql/9ZRBPkWejxHRMWj8Pz3d0OGYd64cPrWeYn90J2+lx3j8bFwHZeXcCnsDOJmxKg/lAqoq0RNtysvqWQBJli7F1tD2tB3yXzDuWjG+yJUNrcDPLfg/HDVdFdJxipq0/GZuncFNG7elOM0dEruM5e3HrrgHaMHp+cxMZ4nCSxUdqcm/nojO0l1Qiync2FofZV2GnaeEPc3HDxW98YibE7gYeLFlKerR1mgDAvN0Q3Ieme7sk+0SXp1yn+f/mLu1rZ+gPM1gqL24bXPhWVfpH79PeyKINOs6xCL7w6ZhN74YO4dUxfV0HWA7ZDWn8NEm6EA1GtEdZdl/y1vYq2JwuPGdOoB/bPJEMFc7HkfBH99Gpopr3WZ2rhvU98TNcEezVxhLF0YyDtYduOWKDPTnEzMi9Lnq6aFdndjlZkC+m3XZikK5DhsEj0qyLxje/+ilgOe2rRrLt2Hw1+opZa7P3d0FiUwonbbP3g5jgreynTRuUeN/kCrRC0Hoec/W2MyJ4Ins343FU1um6QosY8h+DPKeyjD2G4uLcF4m9iPHvtCT9d9mk5PaKD24pBnNTS07sTXPWgrvRHnCFc2OdoGEtiD6kt2nNk6gDYK0rCQmNxNuSXJIZeKf59fb6IzamXsQIG/SldwJtls7rLKdPJB9F3NJ93WdAEkAj0ahkWjxnr9Daqh0PvPI4qkvdLEOMWwnbCS1CiLpDM8kOC8jCMHacj8Zs+pgtH90y42LHzHY0id/SqrBfOo8UqzYcH3SMg7Bmne6A3bKpxzrSv+SdkaV4Gbi49jljiiU0SGHfTEnIFRpgWKltg3M4eZVnxNLgD4GMKn4I2xKP1uiwe4eIK4ES/180qZJYuijoqvh4ZeRSHG2xg/2Kh7I4uboqjJEifEJJB+qO6/tlEXyIgb+5BUjclLNEOU18gDWqzoGO18hGxTGyc59XH9L4+AWWQltXjCN+fu4c8v9ZLWF4yqe/8etY/DDps9Xrx6HOKdtRgQ6xOE7z1Hy0WXJNB3T+vhoCiCJEwgXcw86xdhD26oJEhorer5Ou9gM/LuOyQnD6v05JYxB/AiwqjS7gPedCLIAzn7k+gugb4Tk6MWWUxeVw14+qHRAGigeFzoqOHEsIVH6DjceA0IMJPEkcKPxre2wuTJx0S5rG8EHlH47scnsqLgJF5Ki/Ed4jPJKLcKT9Dk9ofcAQpZYs2wwjIemwnS5y0wsexZBt6tnkROoQCp1UQCbl3pAPsBZDjN9vyR7TEELL+tClI06uL5GckNyG2iQi+o0eQorppRuEMcANJ++i1e6V5tPF+Lp7aseKNpnuXL0iHT+9fiB19VpR9/44hMotC0B6D5CdJ7Y1TpwQebGATDeM7b2mITITBuAxSKcj21R1FxpiYFl974BgDHxk4XHSoUsNFXB9VFLE5Bj5uXo6bui6CbmV1uH6k1TJLqzocYbeWM4j4EglmxufhdVUY6dsEVZqE+ZlpBNG3jq/ZYtwGOjPjhIodit7G9RrhAZn3QKzORqg2hkd5b9IVjBDER+M+zRcRMfeuptO8hmUVzIud9GKwwpEZqJNwk7aJ7IWbFLZrN0jmo80TVvVw+HVSrPBtDLyVjMqsqP/9aWPVZZwPGcUPmD4sx1u+rJkSHf2PdDEi9k/j0mbYlWLLnAFxXIET68Qgzulq/0o5hSLmZbBtOXWVjjx9hOULnlV31wtbO8TxQl8997keS9cO6ccxqGC3vQY7CgZcXyComhKSa/X6MEjv7o4bQzg0g3+wTXl4JNt4nvY1I4lbLPQooAsXgiUO4IrhpmEQYjLExHe9LFrPD9iEt0eMaBtilfYjfsIYVeBzt7qnUbhf/FsF4wRtqUPAoisOfVP+EsTSrdapJTHuMrWaSARM3RlXdVI0axwZvQiaNRGLTdpAl5BAcnxMdsEwRt3gwVu3g1/dWvDIn2nbX8HjE86EI49cEVtxS+Qol+OiRhpY9BgpY4I2j4XSs12cQKlDOBITbhjnzOYyJQEmHsHofcUQBR9Hq4XmtGSCs6qbor2RwGP28uF2OL6ypwg2qo5dFCHS4a9A/hA/eWbEmMK4PqktjNzaCZ/FvkWcyyhI7sV+T+HTF4nOZiGB7aPYW9G1J8pQN5qYCYSs8lv92SNiHBaqXEIM6j/5hBweEgHNkQioOyjcsyU17w298XSyEfcaZydYZ+3vUVLJfmQervuhZujM8aS9avr3PWS2rdfucL2GozuougdLh9wIQTFkfThfDGSbZ51vCjZBRIAPfoP0UhlR7jVVFGI6e0Zs5D2FFYZSIPZOyQ+i8OlzXQLP5yDsJRt1ALcnIs3oEPPI2HxSZEX5CT6TFASxkW9f0mZXVWrPfLUz61IKL5lF+5uaQR04r86Qyd1sru5EiDgemQPpi2cHLozBhTRF3fvA1v7DcOMesuEoB1fRzy0ouh9ObuKe3BxOW4y9273TlsNhxG4fRtiHUXR83yXY35NNqR+Vh4o81AxTStbdK4Kx1/x51WeeCAok2lU23TP+1D8X4h4caE5A7JPV1Zge2gOpVVLjALxnRbkCgRmrelzXYPMYVax+Rk/gFOnq9dH9fZqlweoxuaMbpDuNveOfV+0ZQp/KPSQCRJO5WdvlMBbCjxXa7TM+cW4xdwjc08/FQ5pHiqrF+Ppb4BYoXY5nOnLeemQR5RGE2DX98YN/d0QM9v2x3pZ75tuPXfksLas62pkiZtJoyKR7sOdLWqs1yCM9ZyI9frTRsvuKQWsEocdZex/TPPHwP3DVQ3pyQiU8CEzrSNbTu5jI3kdB9o90fVlUOFGOXySbRLtaFjmUJ4DzS7PZJ8qKw9r++5+9UdOugj2RnkZH8DsPRzDxzVXd1hqk1xJEMW7r3TwVnyEm1nlVGK/ivHflu5tlCaE9/j+54kdLBJZpwuEOylj5HQRpz1uVvtrb72m96NmHS/4oa38bDoGiPVD9kCPqtZnUgjzjE73KE+ElG3vuppWqPWHu8bTcGffVu7sSPqZAtrhCXQczCnRrPj3Oigesz+4Ji34G+UODExU68+imZhCTSo+WBSlt54ixeWzT4WC39bFGSW7e4TrR5KewW9Kb/OGxUN6AhzhBqtt5j/4QbhH/YH4KE7cXL3siZ0fZi3sanTQZuXCkPzF2PxYk54rXcARj9SwDD+cr1HV8Bc5IG/fo5Kt6v27UjqvTtAHi1zXOaX0Fq6Ip90YFn12/6elpZHKf/EA9cuntAT/VB+cQL1aXWfOQqnLRWh4qF02iZvuAo6ML+FTt0QuIs3NoRA2cvIIVBxXOQBMHUxQJvJ3K7R/JTtkO/fVgYPRore/RFFl2lFdPexOChwfkEbROakX35/mJxP5doO9FHfZ0ydh3sBDR9oRrZt/m43FPG7XWybw4J/pRtuhoeZ7ItvqxgVWNtFi8RrbhgHE7NZDxjz3pM649kQVmW/Avf3FXmCM9/RQlGBgh6fge41CwvZWGGxwO3IfrEv4ZKVZXPXUhihMsV2mFz12vYIIl7f4yf8g+NMpiGvIOhCG2nuvTZ0xVkB019RLTNSHKwF7N++w3q+JFwvazdU5duYmItpOckbF/xbx1U/yEccxIgu4oSWBVxUNK4j/QBAclabJedGdF2az26Cz/plinifvy6qqFZRaZe3FbvWJqpy9c7sLFmCmuTpL1QbhjTxYIGZA7i3bVdnyB4HnELQfmj2tjd/W+Q49UVRetJA5yRuF0uUEI8MUb3IvAFww6LIop98RG8qgFGuu7KYH2Sfh8LItm7SmBurrjhoR6vVIT+YTiott1gtZyDIGC155MqTqIlVE8nLspnsiy3BMZtRPiYd/YbQ6neku8/WBaMpaIFzgJvqj5VfQpe3fDfmCTFIQmOQjsDJeiIDjHQdglblIT319q+J54DisGri7HdURU7b3DyPnThyxiTVnCPHmJdkm8R3gFNse3hszsf/VelexxVxi1v4M6tjBDRnuN1kJ2nicZ6qolOf7sJ4n7xvCtwSkau8GNDW8GTDRCptFpRtrJhmlG2DU26chQQw6L1b0xRowhwZ/ivQFkZxCOTVN1y2MTWN3y2NTu8Me5Wkb4ZHRGHF4kHrUVIjpGawJp6g3SdhYjP0lyBZ9Aubgs0AZX/YAlRKwVmFdyCZOfRVMf1XWZ3jU1jG01Cg1U/7mKlBepUwIUEX6usWdx0ugNWv86yhhJRBw2Y/DTMSVEcuQEzT+rp3hNO8JCRwiFTQTuUjTFVBhfYB7x6idcqEgX3NOTx8c42ZowstPndVoSy+lLkdfLEfD+FwRxxk7z5YcUCbf6AyR8GBKvOaDpMjx+KrJFpLkSkUdkBAr5Mch/RjOlOLzRlhiN9/wkNso+R1ZktOd3INKG1EloohTEzOeKA3PQgvhX+4g6fu8QJOy77KOgj8ZuqgauYNWI6XQ9pdEaX5iOTxwRccReI7tz0Inid/2ywXUrGNudegnSWMHmXAasKDTtUGJbBSe4bFqX4Si+rrETE2/vgQZtLlzBFUhzKkjdIyr8E8Av3HbG7UWB3fIJk5Eo4lFJbxV+q5Bu/ylF07U3iRdnf6OTVHVvvasWFpA1QQ6Tj+k9Ufb3kHX6oblP3qZm0Px9q+DiR1ovPVmIqx7cFSYDQ3R3y5TMuicM2qsz1Cyjvdzj3q4CT8iBYU/q8MOikd1851XfVXIPC/Dai6fQQKbZOqLBcIUGu8ZvukfT1AaM8e6MXMMc69Wxetiii9e9L7CqqFuzgQ+KbGaE6GazPTnkFg+zR0llhvFEjWOZUV27nPfN0r83oJsIf1n9LU9r8vDe2MeNQ0Ojny5OMZpJRjL2aWhvL7X79dgkY1sbm3ajHdnFP6r7UDzlWQEWwcmIzqseVRTF6DNi8byCPU6lTmi1+yKp/wOmD0svtdCmheMmX2TwA6hBHAdh99Q7vogZh0fdtv0LnOBjn7b9WTboCypNStgFzD6Srhiebtxy5ZH2bu6Xl2f2qOOWqEHBqwTDMcgAflcgRH2rRo+qiWmExT0MoP0fHk/K8fVHeFAOH+Ss9ucd4hkl+U0Jkp9p/hDxDJDEvI2rlJCDPBjrpPEDzNJHWNq8Ebktb4L1C2CPvBjDkLzud7Q1I5xPiX7j0b0HDrpH3ZT5FfwdZ6vbl2kPvy4VaeJm139i+J2uIKiK/KwoW16Jo6R3HAeJ/Wth7/sgtQ1BcLpnxCVLDn3x5P7+oogV5a572ZtPzuv+IgItKGLcCtuWaBOHDbIgoT4noNyrTTJcWl6CslMcgjxPrV/I75iVrhv2/PVmksOPV2eX/zMfe4x3WQKtTlgi9sfO/dHs6Ri7586JuW7mr9eIhn1Y3DCPeyLzZl4Wctp+XbPRuNa2ihZb2PrF1yPO0gyROzArcJtcuyNfaH7tkbPG6wi6JwtAPsRYvLc1meZnYZQ9YZGdmYVOqNyAhz2mvOOb1q7E+w7KFOk6Aw8jK/IuzcE+SbyfjQtNrcIw61T/hLHPiz0gb+5Bgi8BlcisUlzvC23FW222etKB9Gss7D/SxXjIP40b9wAqSCjvfwzo1Az+wTbl84BCVaUPOVqBKWFK0yvx7zxcS/1hCc4l6HGVhKkdYvv35h6Of3PvB1s7pB/nVfirFXtn+KP9IPkZwyAnNs3Xpv56T1CSQY6RIk+xs+7JdhrKX/2/A11uvwDieNsw4C2tNw7c+M7fCxZg54gYgrPNo0/UIznBAfO/N2kZFkRIKX//f3tX1hw3jqT/ykQ/7kN7u2f3zb0Rsmx1K8J2ayS5HbsvDLoKkjhmFWtJlm3Nrx8QvHAkgAQJ8LJfuq1CAnl9SOJMHMs8S8cvf2oH/hPg5FerW1x78ta68EWaxN4fKfA4axx1GnhsmK7v423D0RoVB0dB0Fa6Qp9xfD7EorYmsjTL7/7/HOekuH385FvOeuaw/yd1E7/B633Toz5mNQGj64K2dEdSsht5+t3DZ8YLpO2fHFV2xmj8l/Ztcvxc7YZBg7LlHDa6T0heLz1sKrDOto3kLy109bjh8E3quraPqfeoUwge94LxWwCVDzYCZw/rzsCWda7M/0e3eVekb44V/ajPxh3ZnXPiKB9qFZF/N8TbKtnb7DHrFuGCzMoc2/mjPKSvsv2zr9RXdNZJgdsu570n5dcs/xxg5fsQ58+s67QpvofEb6iVkSd1WJNvvlEtj4+E5ZMaKx/cmIOYDo8s161/L5FwwAtlpgz1Ytvu515D5qhvwsXbrGrAwSj401xXWX6gLuNOmHhqfvA7KED39PHC/PZfnnmdVcnZ3hz31JljP4FuY7BmWXAjAajWcdx3vm4jyKDOeeaBnyI+kQNhE27a1EacyXTyPZBx9SumTSEJme79LudWw4HlVbz7fH1k23h8btONwMbHwfx5FyhmP/4+xb1J/sTOPTnQD/xmdoiCfDuqV3apaEpa+NENBz5meElt+pjlzz98vF0fN2Hvh4u362Jm12S3tc18HxMGqkL9GK1vl9KG/eMPGqsOO2kQboB6sUtvyS7bTFLchU9Lp9jNQm5tX+/7xTKPhwo/5D8ANeU6R3o2r0kOadTH8d4hb4Xho1b7hNhGQDb4lV8/jw1Dz3a4auAIGPzklcQFnbg2qZW24W5rJPhlSCRgT7lQzb5Wwdd367cVAkPv4IQaUTcYep0cyLHYzvLbDxhNC6OrLD8ftriMy6nkNZv67KurzGPjxm732SnZzZZHyGFdiOVobvNrbgSY4/cXrvLsMByGYu1xF2qy4WLwdcedxzx/+ifZWVJvDTg5Ut0W8rPSUV0UikeNRK+LZjj76rl+Z9FTY92LH2PamyIUsKE4/elttpWzBpxGFZcB0yS5gZVvGiLfUl0iJKtfNwLLsENl6+j+1wGNOh9Kdth8PJzi5HErY2LI+gPX7uFv7rDGqtPMs3d7YLFv6DFGhyk8G9h+b/ueQ/r4q92OvZrWrA2qqVtNHJCvNFtHkUPuD6DAPVV6itqEO+j12Kk6B2B5ctxXp3niNH0e085FWca7p2oM8ctVouxEuRm7b+tXj2393bEtdCB5T74Wb0nVcTe4kgMr53VNB3yQETX4rYRLFeEi1mDUTPXFy9d8mPpv90sH44OA148ktJrh22wec0pcPpHd5+y8uaQSQbZhF5kuhT0YUtE3h8Z8tHld3Mff3nwjnKZDmqGNtKcVx31cJ0wJE3wTfXjX3FIuEEW5AcssahPe072ODhk/smX47YEOD8rsznmVLfomfq4Msq0H7WTt3HuP2oKHF4YWv4Ema70ROFw+71Ii5jsd5IGqmRuSJ5n+6VbkNlgZp6y1UW8e3pVxXiKeLpvsuJuHI1TXx6RM4nTgu3hi7cXfSWKXtZN/sYxAjPdG+ht4XBThPsUe0SWdTWTn435UPjLNKUw/Av3XEIHYJX39OX4/kv1dWj0YMj7rJA0l5K/ehBx808Dh65iy/lFPJDfSV2/aK6S/jPkUda38Ov2sEz9jzLOioGPz9IcH/XjQ4UO3lVMYVJO35AtJxw0A756yvMQ+A4074kXJfb4rfX1qds5875uFPDeCyixFLQQkExtkJIpUkuck99XeLAljMIJd5dWDklXiqDHTpzpz2B9xYbuN6mmX647U2Yq2EXmCbJOv4nomt07+vfhygTey2Rnwjdi/PrSGOt6E+lged+l5T66PdwltNu5mdMP2lm7ioqgO09GCcvQyctuYFz3vk1LNLTeoJW+nXN6RMm4OH/p5nblqcFxOe7hNf7bzeJTmltBRVn5RfEz2j2TUXmbdwv9lRz861s19zONTM2rQyIZvq3rZUvDBcC1fZVWIHdkv8yTLR2ZSZupU7+m5Wxwd9elHhZ3+GvMBbtuI1C8BSN/yAujtXw6BmdsEg6s6bumYl99xjtNXDZH/tdVwxAKIqzMbVmv1JS++mwR9zRCeZMda6OSGflqesv1lpeqwzik2hO6iNcNBXgU4ullWaWDkPZJeFcfO2tUMk6u5Xu5o12EG+LZtAuvVhtUgrwq8hi3sjPUkL76bBH3NEJ68IfkhKarDJXVSGD6DzRC3yu2hOy3Hd5CPQcaOUVFtwss1tE6nYdBrqofsx2O97tqZR3t7AT16+d5tvvI3ox631o+RTOSU2bBePO2QRitAK/8gGerKAT16S74k5OsfJD09nNPjwA+wh6UvzKkXL+6oFR7skLb6KFE+xkVj8QAHhl+l2WN7aXirvmz0Q6UiQK3TUZvdZMWAA5R9zRCdtLoP8r340tvCoW9wVE64LomcWwZ5naeuGSSCZ2n6V1Zt/c2ahHGCyE0VvTgWX4fspvN1g39Gt+uBeTP3+4sNtZ+8hYYqxVn/TtCgZ8vqccD/koKdlvbQ1PvMsSUd1i+KItslzLMNhzffTlleTQIeEjrTuNs9kf25WpovPkvIf3Pc/62ajfz2k0DUiHRH0oefxYJ357RMTmmyo3L89tMviqp9g4IMQotSidjkfyhNslMt1cZqnF5mx6LMY2oqtb8mx11yilNIHYkY2b0rg3fNyiWvyanaqzqWoLYYho2EFJaVjDD7josUd2wmefmCwwQGKpRRmrErFhGow3i//iIb5eWfx/p8/t+qg/7VRafLuNjFe7X7UNjvzdL04gMC8YVBsObuey9g4/TC8Gzkmw1o/ziTM9nXGRy6jALRlQlqr7OvxzTrM48xp/Y/is78z59/DoQxUHJBJg1FELR1+k8ANFgvDOOrhUAt4lOG6GMaTyREEKFgkojGCa/DWKg4pjVVcHSh2MnZX+bGFhfGtKqMc+x3EdBcYTBbTONqzga/dn2/ftE5av6vxVxbzvu2+20SrIkCi4JIRUHQBVooDLIkfVCDsv5l7kXACTyHozgT8uL3AifIQsuBU7/8MwucuuNiHuMSG9frlxm6g4B8U/2Pa48p8DlHnftnjibdIbeLEzV5dSS4ET8hRXSXnfOdYa7X1BXnet2PkwQX5XApJEtgbHUqTwAu82FaDU/zgcvZYXYf59XRcS/hYsMwc3L5HDAzHLqeDWbN53VVsQwasillm4lkLsOzJQayDmGWMDb/QHx+fE04FB8CL+MhuknR1f6jSTVZrXfIp8eWGbs4gUFxhPLNxDBeK5c4NvciQkQ9n3wh+fN9daNf61ueSHCqUOA0B3SIhr5woVU1DDacvmucbHNjohvFae7rLWtgLgkN4WlDw3JZI5dR+WJwtZoVyxlBNf2apduq1cyLltXbDQ8xWznLJ1y45NkKzYkFa1/AFLRZwyJmA0cBFXolxjo0bNDRigSWB4Gas//9RCBntnyFRWFvNV+4BYBt+i+de3ib+WsnXVa/JbS1pMrqU98eR+zUiC2IZ4WlokngB6ce0IvVUYQ5eCyaYAIkIlIvaBhjci0sBJO2bR3A6xo/fxcodEHBbPDTJ+2YCnclnadQub5QVUkEir4sjPHyitgSS1aPKUEdByzNBqU2Y4dHEFnmj1I+XNZS99vaAQAmmVm46w2gHd+HFwcG9y46ISQE4eY7DddmHekefbUNrdsaYnjofpzkIyPneYJECRxnWoUnQJUxq5WGpTnzzUzIsgyQHQLEZnHl4uMZYKXPdjY1ql4laXUvFc70NghTtkO8LnFvvWhA8RKNPz8amoS3EazEQj9eUpZeQZSubDMfr1Yjl4/XbLiqX0e6K3OWD6VgA6KofRJLC66OgPdo/+Mk4IIkF+UBCYLADDZYGJiBamH48g+dzYK1NunTOvDVSivI0P+4ehx1qqwCO21OqHVgp5VWkKH/cfXY6VRZBXaqZFbrwE0lqbhZy35YPV6YGqvBSp34LFKFdvVUQJTUMioStD+H2c/HetEDWhpFMNwq8vnwImes5rMKW1cY5dqiP5XCadBlykdukk8kDINA2SRToBGdn12HT1RC9gWA1barrwOCyfHfIUidATI3QC254Kde73SKoIta5rJ1kAnBN+mC11DALQ5sK4qASwTdVBFvNPDmjXRvvlUPVMbpxbl8qlrcsULpk7zoiGfSQMr5aSJcfQQ0qucCyNmweJXl5wNblPUNPP3+YsdTaIn7dfW46HVZDwjYC75To6B+NliBQfPzNnAAPI28aCBE7L+/59n5pEUBR6I4r/l5kg8RY6iKEAg6OsMEBA+KUS/XEkIIILe7x0LiZYagg/fltBGHkS9g8KETe4zrQkJo8tGLo18nHb8wsWYDEXsgJcAhOdPYhfEUGml+WfvZuFoNDCPR4DN7fxWT5mlBM+UwF4+a2Ue4bXqmD0X8SP5IqDT5cwTnlVpoGi5eclAekWAzibgEtTB8Z8/EBWIN6CvuMeI7gBg+psyFLybhvB8+diJuyYjqpFQlCHgUbjLs9IqsCTDLz2EyD2wmzFfiBpy5s5T8njyUl3G+j27O1aOPBdl/TMonjQ7D3Wi5ltRKITTW/xgukqD9NBIUnS4oTICumB0iwlgHVmioSwPFGkhyUJ4JBj1OCPCENudxT1txWVj7wHeFhY6GlgS1ycZIg3EmeHTeYdP7rCTLH2dXUqoS1L+uG0O9IssfZ9+SrxTtNxltoGiD0yrWJwHBBXHA8tWvXUJarWIlE8KZ14+gZTi+HLhMFoaGYkVwy7yJ/dj7qEv+krVCKsn6Ar4rMRmAOj2W/xlrRWUrRrDcQ70WGDnKgoNYECzPKNqxnkCEXpboKsy7Sdumppwukc2PbfqfJKPPOFopz/mxekyHBDhrHGg8zIksDW2Ekg2MgXl9VjH67S7wTB9VfqTH0oaXOce2GRPkMs7rIcur83GfEuP2RLOtwlECO158qRNMZImkIYlcGHJHS2eLMABSdMPw7EVcDoRW8ZVaAsym/FYNQtfsnysFW8vfg18CsCbckR+Eq7k35hv+dyeySx6a24cXZZknn84lWQHGjPJDcuko144+s3prgCIs+p+n6s8Ioxcfeux4wAEhVGg06IqQsiUMsxI1HEJjI6hJWdRkAmxgocEVr21o4HyXsXk02GYP2CYN5sZ8O3S4jx/tOcyWMoroZYZk4Us3MlrgVHIYIcyYfgwAlyXjjtmngDO/B4hhHT4nyij57ED7K86T+Fh2sfUyO3xKjoww0j8Zz9+H4YmEezBCgdOyoF02CFpG8jBXvLQGCoo2k6IYKXixl4y/1czNFw/U6b+7YzE691QdAc9/nGkJ9dKHY6LHqEDEY0Es2GR41Bto0dDjxV4a/tYbEzGQ3Gz0W3PIayFnX5JUCAGXT70MuVg0Trrs6AGWi1lrlJX4K07PHUjNGobBxbTIZepixGwIQ2J4EJ7CQLnW1gHPcgNzw3r24SRmyLDugeHqRn/RXXw4peR19vWYZrH+inBHIKy/dD8uHA2wevMjQTT+bFi4T0hO9dsRIQ+79bwVI8LmX2eYCPIV66QXZOF+3USy9V4fDDNeugXAavlTynlANOG00Q0/C5ko9sdRm4PEyweSIrnbaeb1AUtVCcNUcOeCYOYVYNYh0A+UmJkuDh/dXO4qScsq323w6w1ThSlJM7OQCvF2rk1YdcVHt3mvUZRZXg28kkOcP7/5tnuKj4/kNi7J5TmnLHbPhuF9QyAO7dsf8UGOiSCep6t/CTQgh/QKA5JaD1wE0zpgGdBgf/zAxAyYECw/32vw8e7z9ZHKsvt8d/5U7PKkPiu+iltXGuHFt+J1NKu/g6XTzGWBYHG4W/4Mb3mgm3AcPwZzc4/mL3Yp8OCu32XOQJjrRBcvzve/bmKNs9dnNWucFw8PSUp/IdFEb550DMWG+l/XnvigVwXDbO6EKuyJnqADJz0UFN7qU0gbG/GoOq1irEP1+0IB/Y4CNX4k0VWeHaZDichcGjeJRavHh6QQhiPvjKUA5D77AY+FwKN3xYzD1jL5Qk3+NnuMuH9XjtIPNiQ6cdQolU00gu246qQJNXYx2SwM0HilUKMYUcRFQG0VC0DzoWrKuOUKp9lD1uUT2X3OzvIJWuVnPbYUSgFkaukkaIPVMosW8oysxZ5hwKjREIVLueqMeTB355xq/HgTP7NczNfHpGrXU0pm/XBLZiwltJQL151nWdEHxZPzxGLw0WZ2N2vky8/BMqmCShlFC5syfhhA/ILSJYO8XHc2fL7NdnGa/IvsWxmjt/Hx8azMajjvdwS8u/sfJ0GgIrYojFoaBHWwqcKgTdUJw7SVcEZ8+Z4AhDv/LY/9tzHmX81Yv9nVuyVfEvL1D5KeHs7psUpCXLmkb5ytenaYor+X1bDrPot09a2wa9oA0deVOR/JVOUAdpoBIsQNmFGYazWaZJkM4xEn3nPiUCiwnStoqLQ+nw1Rfo8KQMZYIJiEyvMdVUmzx8vswOaJyKDGVZk6jvGshSMp/O8bilZaUzuxmxhG1b9vMvmVB8mLjEB2Yf3jNAed3JHkKUrB5lkGflrZZsPRe/K1cAxHXJWpwxHPmm9S+H1D4Uhraid2E8Oo+rfxEktHILtwwisrA5DkKRzB5lkGflrZ5ht3Z2n6V1Ymx8fmcCV23ifVm3y+J/MXRuVK4YZClNnyTjznwFf1w8Wx+GpYmeJIZK+2P09zy24MxnzN+DTmWhC2ehGXsXowaOlq1vUq/ILFuiOXp9WLyQG1/FstA+G0vhss7hia9t7KG5YbqupUtAbJ22X+bE+ukrwoX8dl/Cku1H2+qtYdKRv6N99OWV5S4dKs2WKuKTi/qiR3uydyiH/7af8powiIP6VgQ0pEgjhTmz0k1fUYDduuXM+zI7EwrFrYn1NyHxefAX5iMcROpLBwE7OgKdzEYoibSGHlRs5k/+YQJ+lFWca7J40vNXQwf5DUIkif6kvh3RdB7PpSvKpmBa1q2SHKlN/tsjPcMYRiEKAChYVbc1bgHSmfMsh+UjnETyKxMbSma1BlsFYBxbLWcpUUIxpSFixzPUsjI3uPSQodwPoisMd0pRYOl3FJHtnhHIVDXwRx6EttJkp25TmHonhXApqoLcR5wMBFIjD4I2qIoncxe0AQ7Z/qveUkJ5rAClKZvCYQ2kQQ3q9QeQvFIFOBAmdtA2oUCpO9Wyq0wZuK7+Lj+SFmvjJEIpHKJAdPiZbFIoSdu0hh66jyLE/trzIF2G1lIiRbAz8zIyuH7v6qwqArgdrvCm0KVB0Kjm9tCSh+W2gbODavZmu/zDIBOHiUaGw8S9ptKHS/0NkK1OelcpCjSGJh2O8ZKrz6IohNX2rh0J8hVDj0RRCHvtTGoTpER+7KnC0rFfVDaAA3kAzkDFJapOi3OxTOfRHErSq1R0dubVINilwhGAu5cgQfDQd92+hQU6dE0IabutgUcmoKmw4kPyRFQSfO7cqpqo9CAuqmUFknt3RYe4zTi3P5VE3u62cDtVKYyeGpr6mGRTp2PVrT2bkyiC8rLiJUh2e099kp2en4NIUGRg0FhpOOibH9hgLT/u95dj7pmDSFBk4NhYVTc1FEYdL8DrXfFCGHsR+qK63daX3tOFYkMw1kRUqLFL8nD3TYmO8tUsBkkBQwJVIKA2czN5wbNR8BrkzrTtTElFG+z0ooknJlWiZ1sYXJLflKFb7JkmNZ6B0GUkGMQULE+Esz7eqLdGMu1ASrJdR4TCw2cUL57ZbQ6cCxmgASMARL5bAZBRKrehkbe1Lo6lWUSWA1ZSrcXE731rBuVqejN83vNA8/Y2d65iecVXsZyUHbGWsMkg4tl4tEWJ+yd1N1DmSFBm+xchwf42NxOv7GSga5jPWGyYsX0rJuJT1+5LqeghAIJ4kzUjTPAWHN0pA7eK2pYZGOS9qviMKVQXy5Yus8PgNXLJvf4Xl7hlgT7ZOqArOotgieQbWlGNE7kMEadMVaRbAovaf1CXPkJ3BqKJWDXhFJbAseutSL6vqHjhJcDtERO6w03pPDKY3BngqT2VYee0rkVoFBApXEtHWA5tx+GPSMFQrT9wXL9ndyJLQjm4KjSgJODRQq25pon5RRXRXty8B10b7YwuRDrmfClUFMuGKbJn1SQVWTvgzUpC+29Q8SFxTKH0ny+AQNmqVysEeIJDiGrxM6ri/gwKCSGNhyVJjVBktMAmgMaw9OkUhO5wV0RJHAtE4kkVr7BJeOB+gVXCncLzgCPKs6u5WJXU1hYVkTWQPs4RQnj5BL+yI4oLalVtwyW5u+ITIFjFqJCLHq/ZaUJcktyNUR6lbEIVqbkdW8P6q1VRrQ7CqZK3PdcFdHiBIDN8TVZukA1hw0lPDqg4bYURyEHDgBcDtDYsoIeFtIpNHuCYlkVrWrHrTXnyORCWCVRRobCvOsKGjjqZ6rSgIiT6GymhqK3dqYjYnV9F+lZhLSloDzj7bQNvWIvxkOOwil4LSDJ7Cx0uyGGDZCcDsg7Wq8QRGVxLSsH12cTmlC9vdZQ58Q++ZkW1kPOoUCJ0NDbpdA3GbXHwzQ0Nl37m9JQUMwO0CNOJjQ7Fbqzz8oFKZNz4YI4Qh5w9Ky72omx+yIRhUpekmqO6GC2w7GbwsLGftdV8gwC5vI9c2op3OVAsjwoBMGIDXIBFAjzoR0NybVZRG+VHcypCNADB/1rIRS3UARy0q9T6XaVyHRnXgQqVw8bHMrwpcKQ+6qgOagfSQeYucqQMfuRWr5TgP3WhNwtJ4KYD4yL7QA3gZgTZgP+b8QVUabo7+oEHVN64wB0HpXBGhDvZTBNWO4aOFuEvCof3SlMYqBWq+SfC+AqaI78y/UNF5tYM3gbiqMMksknuQ3GkWkNXgZuF1Qe9hwa0BnGp1BgqFD4IEFCV/Jq1oLQYx4Dj3qzqOr9tFQ6pWSDr8zNTTn58V64Nn5urpU5F39doxvVx++1gddfFTlX4z63Ul3g98VGv8el6eArKJ2huqupmmGGN01Z2RVzTHVDN8P6ZZM/f3Q3IABa4JmUQvDmuc+zh8J2CMw1Tz5fGHmaef2TtiRK4VEDhR7lLKQhnFBjVzJf0idySzAxanCaBKwQkicADfEhEZMV74Gf2wj8W6X9lMr0hmUAe6a1VoY7pANxdVwtbvvaB+atKqrtCECqebaHK++xzAqq2YfaymkwQZbE5lAuNBnGHKBdP6HXdBVQVbZeAVwsOel64w8Cy0E9HU8qwWhQtsQWB7ETPZOApIH6ygTm0TcyIjanQzSbH+Yh+/4yoZlQvAiZb1QaLweaWhF2uIBGtNd8wxiQP1QDV/Z8HUClNWotxST8ZdQo65dwDwgYQBTQBdraxMY78u6q97s1pmUlkn8qyvtPLJ6umvVw1WUTKdXVCT07qUJVe92HLtNWUPw1BMbPC7dyq9drrlxD9aEDKCUhTCEPgjqib34cQlmeJWkKQ2kXcsGI0ik4UyAwtAIpdtch33Teq0V2pBdQMqkITSgS4/hbgYoTUD/0BJgC3MFvVpyCgWmjy49gljTkBqhbgWT6sDdNG12CKM5VKIQJpAzXLCa2uwV7qq2yR2MqqpEIVSVU1Cwmv2P4ydXWZoa1RQJQqjI56eo50xQ/olhqtUpMqK6RVg5nmSkkEoNMb1HV0+TuGOAivLZLf7QlGkYg6toUE6Tj6NW0ZJjw9gSdJAMbNWYP8S7KfUDIVxF+wdOUdik4CJNCB7dQw2mHdHnYTRhM/o0pkEMrydH01wmMuXBMQ5HcRVDoAmTG+iuPunlkOPH3XRdph+jnQCqEEZRUhKxqtyvftRlh+nt+kpkwRQWrgP0GsNH/geqHPH5iTT68jQWkYUsSL3IYHYjtbZa0a9nmyaNbq1pRgs6jzdZT+SbNfVYjs6j6BP3W5aqCLGqAtL5X1IREnSxWnD+raGKmmKTRBEiLIVTD8oXFvXnK/QHOeAKhhMMow9yQNnJhFaM2cY8maaxO9IuNfVovy7CHF0GNL0NZBK/iivZ2/p6nhZQevkNe9Iqkf+d6PCqtjnzohs6P3uKC7L/mJRPHAdVcVsVb+oIdeWsgKyqNuPfcEMI/bZvX28GuIInRcCa2h6PSrfoyTQfeL+j7SPW8hsX5jJPl63REhA5kgABkc802dcDk0i6qwjkhTQOhYz0IQZGhjyYrBFMYks/ZrH2C3slv+iYxzRtyk19n5Ao/CotJyW9a0/ReDrey2cUjfp29VqKhJ4EB2sq31dj9tOhE6H2RJJ1yicTrmnOJ+RxtQQ8kDJMqAPy0zY92ZR2dsQCN8LZWlr//g59bEJOoxvxrxFBPVxPbp0RqG8m8XMD/TNIUs+HEwg3vd+SFNiDgUw9Q08conPMbgrDhFFL63/eOLUZjImhEQe7LRX9GwiV+ZpvDZmNeQCCDImnNVmzQXANaMaAH7tdcAZBtCom8Ta0rUnG7RutZuauEDa2NrGpFtoD1KQ8xlMueupgYUJNPcS3wJcGMYbhnIqe2qqURhtAjWWYxJD8HXvLEt2EYdXd1wVMROJ83qIuqe+DmNj+EcfUDtZF12ZO8f3gQTYVmtAbCHrqmJnC9ITxWk08AKbTYxNjrfAmMg4n0XWtyhu1xo0bF2lGluI30rJFm9TcziRGwbQrpEk2NQ6nPh4+EEIGysmjYdgECtFdfDilpE/BpldcojQMXQYneQurcvdci3AuFlJZQ2lfSBp+clZ5hoZV178wM0Z9w7dLJfL/tQqvqrKs2ayV2j/a2irBPtpDV2a9mMXJIJs2RfdduUrSsjpBaFyLd6k+zdq8TSJz0wqxhxv/Gcvolxzi/PnNt91TfHwktzEdnncPTwGLi9ZKphAsvoTVhF/4lStxDYx/nKte7ALf3hppBPYHWnuRel1qax6/Mm7fWOuE2MWxPP7FGsK+5OXPTIagbKviPzTPa6LuiSvrkE1DGXLIpjzdVe8sa1/lGqB++y5WZNgRV4n8b4Urb3vVNbXPdg281oCOFgbqEHFC+xTXXXfNwTPwxRezoqs8O5jsYSIPYRD4cbAmqBjf+hptivvMwRAc8drNwD0xFinPjUHxUE9uCmngc2hNYDO+caZrR9eGZ5OYQAHShcDDNGrDr4BFwBtlwPEpbF2DeXTPqtV2sr2UZm5LXf5Dvnk25NCd+HpYdH1MyiRODQdqTRX0Sg47TAu/kXZXn7+zvHs23hjtgWGVld0u2rqB1DW2BJ9Exj1cNzAFEP8qnD3/D0ytV3Bk8h/o9bu6CeuDdkPMYQ7MwQOyHIj9BOBm3qS8pBRV38XL7FiUeZxUj6HQYVinX5vw6j6LdPUNK03+ednNPSBrFjTl1L5kxc9B7a9SeXSTUOBkdLEmUnW9vos2H/duFxZswlNfwOrFuBbDIxZ46axe4DA8YTbOsH3iMbO5Ojqz8OjEZjOozT3OhvW+8J6baqCRLYbHE/CcHWvD9E7dOMP22d3M5urozMKjs8fNoLb87h76i6i81wd8AHy1PcFXVvOOYf15sL1N6MHsfFI6hCU5crNSTvnv5jUJ/xEeNDCzDkKGtTrxGA8/0BltZPvGtUQYbNPao+ovX9SNVIanXiZ5V/byRf0KaPMD/ZNOWOl87122J2nBfn354vZMax9I/ddrUiSPfRMvaZtHwt4r7httaa6PD1k765Mkakna4vbRDVLG+7iML/IyqZ4moMU7Oupk79uy9Znq+cJPZH99/PNcns4lVZkcPqXCnPLlCzP/ly8UmV/W9w0KHypQMROqAvnz+OqcpPtO7qs4LaR5tK6JS2r93wn9vfYl7ZoleXzuWnqfHZENNeZ7TU7kuKdd7p4cTtU778Wfx7v4Cxki24eCvCWP8e75pkqSzpabdI3YHSGa/eXrJH7M40PRtNHXp39SDO8P3/7n33KJ1/t7hwgA - - - dbo - - \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.Designer.cs deleted file mode 100644 index 0bb342e80d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class ExportFramework1 : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ExportFramework1)); - - string IMigrationMetadata.Id - { - get { return "201509021536425_ExportFramework1"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.cs b/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.cs deleted file mode 100644 index 09f2dc6314..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.cs +++ /dev/null @@ -1,369 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class ExportFramework1 : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.ExportDeployment", "CreateZip", c => c.Boolean(nullable: false)); - AddColumn("dbo.ExportDeployment", "HttpTransmissionTypeId", c => c.Int(nullable: false)); - AddColumn("dbo.ExportDeployment", "HttpTransmissionType", c => c.Int(nullable: false)); - AddColumn("dbo.ExportDeployment", "PassiveMode", c => c.Boolean(nullable: false)); - AddColumn("dbo.ExportDeployment", "UseSsl", c => c.Boolean(nullable: false)); - AddColumn("dbo.ExportProfile", "FileNamePattern", c => c.String(maxLength: 400)); - AddColumn("dbo.ExportProfile", "EmailAccountId", c => c.Int(nullable: false)); - } - - public override void Down() - { - DropColumn("dbo.ExportProfile", "EmailAccountId"); - DropColumn("dbo.ExportProfile", "FileNamePattern"); - DropColumn("dbo.ExportDeployment", "UseSsl"); - DropColumn("dbo.ExportDeployment", "PassiveMode"); - DropColumn("dbo.ExportDeployment", "HttpTransmissionType"); - DropColumn("dbo.ExportDeployment", "HttpTransmissionTypeId"); - DropColumn("dbo.ExportDeployment", "CreateZip"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - context.Execute("DELETE FROM [dbo].[ScheduleTask] WHERE [Type] = 'SmartStore.Billiger.StaticFileGenerationTask, SmartStore.Billiger'"); - context.Execute("DELETE FROM [dbo].[ScheduleTask] WHERE [Type] = 'SmartStore.ElmarShopinfo.StaticFileGenerationTask, SmartStore.ElmarShopinfo'"); - context.Execute("DELETE FROM [dbo].[ScheduleTask] WHERE [Type] = 'SmartStore.Guenstiger.StaticFileGenerationTask, SmartStore.Guenstiger'"); - context.Execute("DELETE FROM [dbo].[ScheduleTask] WHERE [Type] = 'SmartStore.Shopwahl.StaticFileGenerationTask, SmartStore.Shopwahl'"); - - context.MigrateSettings(x => - { - x.DeleteGroup("BilligerSettings"); - x.DeleteGroup("ElmarShopinfoSettings"); - x.DeleteGroup("GuenstigerSettings"); - x.DeleteGroup("ShopwahlSettings"); - }); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Common.ExportSelected", "Export selected", "Ausgewhlte exportieren"); - builder.AddOrUpdate("Admin.Common.ExportAll", "Export all", "Alle exportieren"); - builder.AddOrUpdate("Common.Public", "public", "ffentlich"); - - builder.AddOrUpdate("Admin.System.ScheduleTask", "Scheduled task", "Geplante Aufgabe"); - - builder.AddOrUpdate("Admin.DataExchange.Export.NoExportProvider", - "There were no export provider found.", - "Es wurden keine Export-Provider gefunden."); - - builder.AddOrUpdate("Admin.DataExchange.Export.ProgressInfo", - "{0} of {1} records exported", - "{0} von {1} Datenstzen exportiert"); - - builder.AddOrUpdate("Admin.DataExchange.Export.FileNamePattern", - "Pattern for file names", - "Muster fr Dateinamen", - "Specifies the pattern for creating file names.", - "Legt das Muster fest, nach dem Dateinamen erzeugt werden."); - - builder.AddOrUpdate("Admin.DataExchange.Export.EmailAccountId", - "Email notification", - "E-Mail Benachrichtigung", - "Specifies the email account used to send a notification message of the completion of the export.", - "Legt das E-Mail Konto fest, ber welches eine Benachrichtigung ber die Fertigstellung des Exports verschickt werden soll."); - - builder.AddOrUpdate("Admin.DataExchange.Export.CompletedEmailAddresses", - "Email addresses to", - "E-Mail Adressen an", - "Specifies the email addresses where to send the notification message.", - "Legt die E-Mail Adressen fest, an die die Benachrichtigung geschickt werden soll."); - - builder.AddOrUpdate("Admin.DataExchange.Export.CompletedEmail.Subject", - "Export of profile \"{0}\" has been finished", - "Export von Profil \"{0}\" ist abgeschlossen"); - - builder.AddOrUpdate("Admin.DataExchange.Export.CompletedEmail.Body", - "This is an automatic notification of store \"{0}\" about a recent data export.", - "Dies ist eine automatische Benachrichtung von Shop \"{0}\" ber einen erfolgten Datenexport."); - - builder.AddOrUpdate("Admin.DataExchange.Export.FolderName", - "Folder name", - "Ordnername", - "Specifies the name of the folder where to export the data.", - "Legt den Namen des Ordners fest, in den die Daten exportiert werden."); - - builder.AddOrUpdate("Admin.DataExchange.Export.FolderAndFileName.Validate", - "Please enter a valid folder and file name. Example for file names: %File.Index%-%Profile.Id%-gmc-%Store.Name%", - "Bitte einen gltigen Ordner- und Dateinamen eingeben. Beispiel fr Dateinamen: %File.Index%-%Profile.Id%-gmc-%Store.Name%"); - - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.CreateZip", - "Create ZIP archive", - "ZIP-Archiv erstellen", - "Specifies whether to combine the export files in a ZIP archive and only to deploy the archive.", - "Legt fest, ob die Exportdateien in einem ZIP-Archiv zusammengefasst und nur das Archiv bereitgestellt werden soll."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.AttributeCombinationAsProduct", - "Export attribute combinations", - "Attributkombinationen exportieren", - "Specifies whether to export a standalone product for each active attribute combination.", - "Legt fest, ob fr jede aktive Attributkombination ein eigenstndiges Produkt exportiert werden soll."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.AttributeCombinationValueMerging", - "Attribute values", - "Attributwerte", - "Specifies if and how to further process the attribute values.", - "Legt fest, ob und wie die Werte der Attribute weiter verarbeitet werden sollen."); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportAttributeValueMerging.None", - "Not specified", "Nicht spezifiziert"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportAttributeValueMerging.AppendAllValuesToName", - "Append all values to the product name", "Alle Werte an den Produktnamen anhngen"); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.HttpTransmissionType", - "HTTP transmission type", - "HTTP bertragungsart", - "Specifies how to transmit the export files via HTTP.", - "Legt fest, aus welcher Art die Exportdateien per HTTP bertragen werden sollen."); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportHttpTransmissionType.SimplePost", "Simple POST", "Einfacher POST"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportHttpTransmissionType.MultipartFormDataPost", "Multipart form data POST", "Multipart-Form-Data POST"); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.PassiveMode", - "Passive mode", - "Passiver Modus", - "Specifies whether to exchange data in active or passive mode.", - "Legt fest, ob Daten im aktiven oder passiven Modus ausgetauscht werden sollen."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.UseSsl", - "Use SSL", - "SSL verwenden", - "Specifies whether to use a SSL (Secure Sockets Layer) connection.", - "Legt fest, ob einen SSL (Secure Sockets Layer) Verbindung genutzt werden soll."); - - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.Note", - "Specify individual filters to limit the exported data.", - "Legen Sie individuelle Filter fest, um die zu exportierenden Daten einzugrenzen."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.Note", - "The following information will be taken into account during the export and integrated in the process.", - "Die folgenden Angaben werden beim Export bercksichtigt und an entsprechenden Stellen in den Vorgang eingebunden."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Configuration.Note", - "The following specific information will be taken into account by the provider during the export.", - "Die folgenden spezifischen Angaben werden durch den Provider beim Export bercksichtigt."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Configuration.NotRequired", - "The export provider {0} requires no further configuration.", - "Der Export-Provider {0} bentigt keine weitergehende Konfiguration."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.Note", - "Click Insert to add one or multiple publishing profiles to specify how to further proceed with the export files.", - "Legen Sie ber Hinzufgen ein oder mehrere Verffentlichungsprofile an, um festzulegen wie mit den Exportdateien weiter zu verfahren ist."); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportOrderStatusChange.None", "None", "Keine"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportOrderStatusChange.Processing", "Processing", "Wird bearbeitet"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportOrderStatusChange.Complete", "Complete", "Komplett"); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.OrderStatusChange", - "Change order status to", - "Auftragsstatus ndern in", - "Specifies if and how to change the status of the exported orders.", - "Legt fest, ob und wie der Status der exportierten Auftrge gendert werden soll."); - - builder.AddOrUpdate("Admin.DataExchange.Export.EnableProfileForPreview", - "The export profile is disabled. It must be enabled to preview the export data.", - "Das Exportprofil ist deaktiviert. Fr eine Exportvorschau muss das Exportprofil aktiviert sein."); - - builder.AddOrUpdate("Admin.DataExchange.Export.NoProfilesForProvider", - "There was no export profile of type {0} found. Create now a new export profile.", - "Es wurde kein Exportprofil vom Typ {0} gefunden. Jetzt ein neues Exportprofil anlegen."); - - builder.AddOrUpdate("Admin.DataExchange.Export.ProfileForProvider", - "Export profile", - "Exportprofil", - "The export profile for this export provider.", - "Das Exportprofil fr diesen Export-Provider."); - - - RemoveObsoleteResources(builder); - } - - private void RemoveObsoleteResources(LocaleResourcesBuilder builder) - { - builder.Delete( - "Plugins.Feed.FreeShippingThreshold" - ); - - builder.Delete( - "Plugins.Feed.Billiger.ProductPictureSize", - "Plugins.Feed.Billiger.ProductPictureSize.Hint", - "Plugins.Feed.Billiger.TaskEnabled", - "Plugins.Feed.Billiger.TaskEnabled.Hint", - "Plugins.Feed.Billiger.StaticFileUrl", - "Plugins.Feed.Billiger.StaticFileUrl.Hint", - "Plugins.Feed.Billiger.GenerateStaticFileEachMinutes", - "Plugins.Feed.Billiger.GenerateStaticFileEachMinutes.Hint", - "Plugins.Feed.Billiger.BuildDescription", - "Plugins.Feed.Billiger.BuildDescription.Hint", - "Plugins.Feed.Billiger.Automatic", - "Plugins.Feed.Billiger.DescShort", - "Plugins.Feed.Billiger.DescLong", - "Plugins.Feed.Billiger.DescTitleAndShort", - "Plugins.Feed.Billiger.DescTitleAndLong", - "Plugins.Feed.Billiger.DescManuAndTitleAndShort", - "Plugins.Feed.Billiger.DescManuAndTitleAndLong", - "Plugins.Feed.Billiger.DescriptionToPlainText", - "Plugins.Feed.Billiger.DescriptionToPlainText.Hint", - "Plugins.Feed.Billiger.ShippingCost", - "Plugins.Feed.Billiger.ShippingCost.Hint", - "Plugins.Feed.Billiger.ShippingTime", - "Plugins.Feed.Billiger.ShippingTime.Hint", - "Plugins.Feed.Billiger.Brand", - "Plugins.Feed.Billiger.Brand.Hint", - "Plugins.Feed.Billiger.UseOwnProductNo", - "Plugins.Feed.Billiger.UseOwnProductNo.Hint", - "Plugins.Feed.Billiger.Store", - "Plugins.Feed.Billiger.Store.Hint", - "Plugins.Feed.Billiger.ConvertNetToGrossPrices", - "Plugins.Feed.Billiger.ConvertNetToGrossPrices.Hint", - "Plugins.Feed.Billiger.LanguageId", - "Plugins.Feed.Billiger.LanguageId.Hint", - "Plugins.Feed.Billiger.ConfigSaveNote", - "Plugins.Feed.Billiger.GeneratingNow", - "Plugins.Feed.Billiger.SuccessResult" - ); - - builder.Delete( - "Plugins.Feed.ElmarShopinfo.TaskEnabled", - "Plugins.Feed.ElmarShopinfo.TaskEnabled.Hint", - "Plugins.Feed.ElmarShopinfo.StaticFileUrl", - "Plugins.Feed.ElmarShopinfo.StaticFileUrl.Hint", - "Plugins.Feed.ElmarShopinfo.GenerateStaticFileEachMinutes", - "Plugins.Feed.ElmarShopinfo.GenerateStaticFileEachMinutes.Hint", - "Plugins.Feed.ElmarShopinfo.BuildDescription", - "Plugins.Feed.ElmarShopinfo.BuildDescription.Hint", - "Plugins.Feed.ElmarShopinfo.Automatic", - "Plugins.Feed.ElmarShopinfo.DescShort", - "Plugins.Feed.ElmarShopinfo.DescLong", - "Plugins.Feed.ElmarShopinfo.DescTitleAndShort", - "Plugins.Feed.ElmarShopinfo.DescTitleAndLong", - "Plugins.Feed.ElmarShopinfo.DescManuAndTitleAndShort", - "Plugins.Feed.ElmarShopinfo.DescManuAndTitleAndLong", - "Plugins.Feed.ElmarShopinfo.DescriptionToPlainText", - "Plugins.Feed.ElmarShopinfo.DescriptionToPlainText.Hint", - "Plugins.Feed.ElmarShopinfo.ProductPictureSize", - "Plugins.Feed.ElmarShopinfo.ProductPictureSize.Hint", - "Plugins.Feed.ElmarShopinfo.Currency", - "Plugins.Feed.ElmarShopinfo.Currency.Hint", - "Plugins.Feed.ElmarShopinfo.ShippingTime", - "Plugins.Feed.ElmarShopinfo.ShippingTime.Hint", - "Plugins.Feed.ElmarShopinfo.Brand", - "Plugins.Feed.ElmarShopinfo.Brand.Hint", - "Plugins.Feed.ElmarShopinfo.Store", - "Plugins.Feed.ElmarShopinfo.Store.Hint", - "Plugins.Feed.ElmarShopinfo.ConvertNetToGrossPrices", - "Plugins.Feed.ElmarShopinfo.ConvertNetToGrossPrices.Hint", - "Plugins.Feed.ElmarShopInfo.LanguageId", - "Plugins.Feed.ElmarShopInfo.LanguageId.Hint", - "Plugins.Feed.ElmarShopInfo.General", - "Plugins.Feed.ElmarShopInfo.General.Hint", - "Plugins.Feed.ElmarShopInfo.Automation", - "Plugins.Feed.ElmarShopInfo.Automation.Hint", - "Plugins.Feed.ElmarShopInfo.Address", - "Plugins.Feed.ElmarShopInfo.Address.Hint", - "Plugins.Feed.ElmarShopInfo.Contact", - "Plugins.Feed.ElmarShopInfo.Contact.Hint", - "Plugins.Feed.ElmarShopInfo.Generate", - "Plugins.Feed.ElmarShopInfo.ConfigSaveNote" - ); - - builder.Delete( - "Plugins.Feed.Guenstiger.TaskEnabled", - "Plugins.Feed.Guenstiger.TaskEnabled.Hint", - "Plugins.Feed.Guenstiger.StaticFileUrl", - "Plugins.Feed.Guenstiger.StaticFileUrl.Hint", - "Plugins.Feed.Guenstiger.GenerateStaticFileEachMinutes", - "Plugins.Feed.Guenstiger.GenerateStaticFileEachMinutes.Hint", - "Plugins.Feed.Guenstiger.BuildDescription", - "Plugins.Feed.Guenstiger.BuildDescription.Hint", - "Plugins.Feed.Guenstiger.Automatic", - "Plugins.Feed.Guenstiger.DescShort", - "Plugins.Feed.Guenstiger.DescLong", - "Plugins.Feed.Guenstiger.DescTitleAndShort", - "Plugins.Feed.Guenstiger.DescTitleAndLong", - "Plugins.Feed.Guenstiger.DescManuAndTitleAndShort", - "Plugins.Feed.Guenstiger.DescManuAndTitleAndLong", - "Plugins.Feed.Guenstiger.ProductPictureSize", - "Plugins.Feed.Guenstiger.ProductPictureSize.Hint", - "Plugins.Feed.Guenstiger.Brand", - "Plugins.Feed.Guenstiger.Brand.Hint", - "Plugins.Feed.Guenstiger.ShippingTime", - "Plugins.Feed.Guenstiger.ShippingTime.Hint", - "Plugins.Feed.Guenstiger.Store", - "Plugins.Feed.Guenstiger.Store.Hint", - "Plugins.Feed.Guenstiger.ConvertNetToGrossPrices", - "Plugins.Feed.Guenstiger.ConvertNetToGrossPrices.Hint", - "Plugins.Feed.Guenstiger.LanguageId", - "Plugins.Feed.Guenstiger.LanguageId.Hint", - "Plugins.Feed.Guenstiger.NoSpec", - "Plugins.Feed.Guenstiger.NoSpec.Hint", - "Plugins.Feed.Guenstiger.DescriptionToPlainText", - "Plugins.Feed.Guenstiger.DescriptionToPlainText.Hint", - "Plugins.Feed.Guenstiger.Generate", - "Plugins.Feed.Guenstiger.ConfigSaveNote" - ); - - builder.Delete( - "Plugins.Feed.Shopwahl.TaskEnabled", - "Plugins.Feed.Shopwahl.TaskEnabled.Hint", - "Plugins.Feed.Shopwahl.StaticFileUrl", - "Plugins.Feed.Shopwahl.StaticFileUrl.Hint", - "Plugins.Feed.Shopwahl.GenerateStaticFileEachMinutes", - "Plugins.Feed.Shopwahl.GenerateStaticFileEachMinutes.Hint", - "Plugins.Feed.Shopwahl.ShippingCost", - "Plugins.Feed.Shopwahl.ShippingCost.Hint", - "Plugins.Feed.Shopwahl.ShippingTime", - "Plugins.Feed.Shopwahl.ShippingTime.Hint", - "Plugins.Feed.Shopwahl.Currency", - "Plugins.Feed.Shopwahl.Currency.Hint", - "Plugins.Feed.Shopwahl.ProductPictureSize", - "Plugins.Feed.Shopwahl.ProductPictureSize.Hint", - "Plugins.Feed.Shopwahl.BuildDescription", - "Plugins.Feed.Shopwahl.BuildDescription.Hint", - "Plugins.Feed.Shopwahl.Automatic", - "Plugins.Feed.Shopwahl.DescShort", - "Plugins.Feed.Shopwahl.DescLong", - "Plugins.Feed.Shopwahl.DescTitleAndShort", - "Plugins.Feed.Shopwahl.DescTitleAndLong", - "Plugins.Feed.Shopwahl.DescManuAndTitleAndShort", - "Plugins.Feed.Shopwahl.DescManuAndTitleAndLong", - "Plugins.Feed.Shopwahl.NoSpec", - "Plugins.Feed.Shopwahl.UseOwnProductNo", - "Plugins.Feed.Shopwahl.UseOwnProductNo.Hint", - "Plugins.Feed.Shopwahl.DescriptionToPlainText", - "Plugins.Feed.Shopwahl.DescriptionToPlainText.Hint", - "Plugins.Feed.Shopwahl.Brand", - "Plugins.Feed.Shopwahl.Brand.Hint", - "Plugins.Feed.Shopwahl.ExportFormat", - "Plugins.Feed.Shopwahl.ExportFormat.Hint", - "Plugins.Feed.Shopwahl.Store", - "Plugins.Feed.Shopwahl.Store.Hint", - "Plugins.Feed.Shopwahl.ConvertNetToGrossPrices", - "Plugins.Feed.Shopwahl.ConvertNetToGrossPrices.Hint", - "Plugins.Feed.Shopwahl.LanguageId", - "Plugins.Feed.Shopwahl.LanguageId.Hint", - "Plugins.Feed.Shopwahl.Generate", - "Plugins.Feed.Shopwahl.ConfigSaveNote" - ); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.resx b/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.resx deleted file mode 100644 index e07999aa4a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201509021536425_ExportFramework1.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201509031112324_Merge3.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201509031112324_Merge3.Designer.cs deleted file mode 100644 index b8acae3dd9..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201509031112324_Merge3.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class Merge3 : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(Merge3)); - - string IMigrationMetadata.Id - { - get { return "201509031112324_Merge3"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201509031112324_Merge3.cs b/src/Libraries/SmartStore.Data/Migrations/201509031112324_Merge3.cs deleted file mode 100644 index 24ce9a2d5f..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201509031112324_Merge3.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class Merge3 : DbMigration - { - public override void Up() - { - } - - public override void Down() - { - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201509031112324_Merge3.resx b/src/Libraries/SmartStore.Data/Migrations/201509031112324_Merge3.resx deleted file mode 100644 index e84ddc5393..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201509031112324_Merge3.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201509150931528_ExportFramework2.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.Designer.cs deleted file mode 100644 index 1ff305bd2d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class ExportFramework2 : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ExportFramework2)); - - string IMigrationMetadata.Id - { - get { return "201509150931528_ExportFramework2"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs deleted file mode 100644 index 5bab264538..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.cs +++ /dev/null @@ -1,276 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class ExportFramework2 : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.ExportProfile", "ResultInfo", c => c.String()); - } - - public override void Down() - { - DropColumn("dbo.ExportProfile", "ResultInfo"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - context.Execute("DELETE FROM [dbo].[ScheduleTask] WHERE [Type] = 'SmartStore.GoogleMerchantCenter.StaticFileGenerationTask, SmartStore.GoogleMerchantCenter'"); - context.Execute("DELETE FROM [dbo].[ScheduleTask] WHERE [Type] = 'SmartStore.BMEcat.StaticFileGenerationTask, SmartStore.BMEcat'"); - - context.MigrateSettings(x => - { - x.DeleteGroup("FroogleSettings"); - x.DeleteGroup("BMEcatExportSettings"); - x.DeleteGroup("OpenTransSettings"); - }); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Common.Example", "Example", "Beispiel"); - builder.AddOrUpdate("Common.ShowAll", "Show all", "Alle anzeigen"); - builder.AddOrUpdate("Admin.Common.Selected", "Selected", "Ausgewhlte"); - builder.AddOrUpdate("Admin.Common.Entity", "Object", "Objekt"); - builder.AddOrUpdate("Admin.Common.Placeholder", "Placeholder", "Platzhalter"); - - - builder.AddOrUpdate("Admin.Common.FilesDeleted", - "{0} files were deleted", - "{0} Dateien wurden gelscht"); - - builder.AddOrUpdate("Admin.Common.FoldersDeleted", - "{0} folders were deleted", - "{0} Verzeichnisse wurden gelscht"); - - builder.AddOrUpdate("Admin.Common.ProviderNotLoaded", - "Cannot load the provider {0}.", - "Der Provider {0} konnte nicht geladen werden."); - - builder.AddOrUpdate("Admin.Common.NoEntriesSelected", - "No entries have been selected.", - "Es wurden keine Eintrge ausgewhlt."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Blog.ShowHeaderRSSUrl.Hint", - "Check to enable the blog RSS feed link in customers browser address bar.", - "Legt fest, ob der RSS-Feed-Link in der Adressleiste des Browsers angezeigt werden soll."); - - - builder.AddOrUpdate("Admin.System.SeNames", "SEO Names", "SEO Namen"); - builder.Delete("Admin.System.SeNames.DeleteSelected"); - - builder.AddOrUpdate("Admin.System.SeNames.Name", - "SEO Name", - "SEO Name", - "Specifies the SEO name.", - "Legt den SEO Namen fest."); - - builder.AddOrUpdate("Admin.System.SeNames.EntityId", - "Object ID", - "Objekt-ID", - "Specifies the ID of the associated object.", - "Legt die ID des zugehrigen Objektes fest."); - - builder.AddOrUpdate("Admin.System.SeNames.EntityName", - "Object", - "Objekt", - "Specifies the name of the associated object.", - "Legt den Namen der zugehrigen Objektes fest."); - - builder.AddOrUpdate("Admin.System.SeNames.IsActive", - "Is active", - "Ist aktiv", - "Specifies whether the SEO name is active or inactive.", - "Legt fest, ob der SEO Name aktiv oder inaktiv ist."); - - builder.AddOrUpdate("Admin.System.SeNames.Language", - "Language", - "Sprache", - "Specifies the language of the SEO name.", - "Legt die Sprache des SEO Namens fest."); - - builder.AddOrUpdate("Admin.System.SeNames.SlugsPerEntity", - "Names per object", - "Namen pro Objekt", - "The number of SEO names per object.", - "Die Anzahl der SEO Namen pro Objekt."); - - builder.AddOrUpdate("Admin.System.SeNames.ActiveSlugAlreadyExist", - "Only one active SEO name should be set per language.", - "Pro Sprache darf nur ein aktiver SEO Name festgelegt werden."); - - - builder.AddOrUpdate("Admin.DataExchange.Export.FileNamePatternDescriptions", - "ID of export profil;Folder name of export profil;SEO name of export profil;Store ID;SEO name of store;One based file index;Random number;UTC timestamp", - "ID des Exportprofils;Ordername des Exportprofils;SEO Name des Exportprofils;Shop ID;SEO Name des Shops;Mit 1 beginnender Dateiindex;Zufallszahl;UTC Zeitstempel"); - - builder.AddOrUpdate("Admin.DataExchange.Export.NotPreviewCompatible", - "This option is not taken into account in the preview.", - "Diese Option wird in der Vorschau nicht bercksichtigt."); - - builder.AddOrUpdate("Admin.DataExchange.Export.CloneProfile", - "Apply settings from", - "Einstellungen bernehmen von", - "Specifies an export profile from which to apply the settings.", - "Legt das Exportprofil fest, von welchem die Einstellungen bernommen werden sollen."); - - builder.AddOrUpdate("Admin.DataExchange.Export.NonFileBasedExport.Note", - "The export provider does not explicit support any file type. Therefore, the export provider is responsible for futher deployment of export data.", - "Der Export-Provider untersttzt keinen expliziten Dateityp. Fr eine weitere Bereitstellung der Exportdaten ist daher der Export-Provider verantwortlich."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.NoGroupedProducts", - "Do not export grouped products", - "Keine Gruppenprodukte exportieren", - "Specifies whether to export grouped products. If this option is activated, then the associated products will be exported.", - "Legt fest, ob Gruppenprodukte exportiert werden sollen. Ist diese Option aktiviert, so werden die zur Gruppe gehrenden Produkte exportiert."); - - builder.AddOrUpdate("Admin.DataExchange.Export.NoFiltering", - "There is no filtering available.", - "Mglichkeiten der Filterung stehen nicht zur Verfgung."); - - builder.AddOrUpdate("Admin.DataExchange.Export.NoProjection", - "There is no projection available.", - "Mglichkeiten der Projektion stehen nicht zur Verfgung."); - - builder.AddOrUpdate("Admin.DataExchange.Export.NoPreview", - "There is no preview available for this entity type.", - "Eine Vorschau steht fr diesen Entittstyp nicht zur Verfgung."); - - - builder.Delete( - "Plugins.Feed.Froogle.TaskEnabled", - "Plugins.Feed.Froogle.TaskEnabled.Hint", - "Plugins.Feed.Froogle.StaticFileUrl", - "Plugins.Feed.Froogle.StaticFileUrl.Hint", - "Plugins.Feed.Froogle.GenerateStaticFileEachMinutes", - "Plugins.Feed.Froogle.GenerateStaticFileEachMinutes.Hint", - "Plugins.Feed.Froogle.Currency", - "Plugins.Feed.Froogle.Currency.Hint", - "Plugins.Feed.Froogle.ProductPictureSize", - "Plugins.Feed.Froogle.ProductPictureSize.Hint", - "Plugins.Feed.Froogle.AppendDescriptionText", - "Plugins.Feed.Froogle.AppendDescriptionText.Hint", - "Plugins.Feed.Froogle.BuildDescription", - "Plugins.Feed.Froogle.BuildDescription.Hint", - "Plugins.Feed.Froogle.Automatic", - "Plugins.Feed.Froogle.DescShort", - "Plugins.Feed.Froogle.DescLong", - "Plugins.Feed.Froogle.DescTitleAndShort", - "Plugins.Feed.Froogle.DescTitleAndLong", - "Plugins.Feed.Froogle.DescManuAndTitleAndShort", - "Plugins.Feed.Froogle.DescManuAndTitleAndLong", - "Plugins.Feed.Froogle.UseOwnProductNo", - "Plugins.Feed.Froogle.UseOwnProductNo.Hint", - "Plugins.Feed.Froogle.DescriptionToPlainText", - "Plugins.Feed.Froogle.DescriptionToPlainText.Hint", - "Plugins.Feed.Froogle.Brand", - "Plugins.Feed.Froogle.Brand.Hint", - "Plugins.Feed.Froogle.Store", - "Plugins.Feed.Froogle.Store.Hint", - "Plugins.Feed.Froogle.ConvertNetToGrossPrices", - "Plugins.Feed.Froogle.ConvertNetToGrossPrices.Hint", - "Plugins.Feed.Froogle.LanguageId", - "Plugins.Feed.Froogle.LanguageId.Hint", - "Plugins.Feed.Froogle.Generate", - "Plugins.Feed.Froogle.ConfigSaveNote", - "Plugins.Feed.Froogle.AvailabilityAvailableForOrder", - "Plugins.Feed.Froogle.GridEditNote", - "Plugins.Feed.Froogle.General", - "Plugins.Feed.Froogle.ProductData" - ); - - builder.Delete( - "Plugins.Feed.BMEcat.TaskEnabled", - "Plugins.Feed.BMEcat.TaskEnabled.Hint", - "Plugins.Feed.BMEcat.StaticFileUrl", - "Plugins.Feed.BMEcat.StaticFileUrl.Hint", - "Plugins.Feed.BMEcat.GenerateStaticFileEachMinutes", - "Plugins.Feed.BMEcat.GenerateStaticFileEachMinutes.Hint", - "Plugins.Feed.BMEcat.UseOwnProductNo", - "Plugins.Feed.BMEcat.UseOwnProductNo.Hint", - "Plugins.Feed.BMEcat.ShippingCostAustria", - "Plugins.Feed.BMEcat.ShippingCostAustria.Hint", - "Plugins.Feed.BMEcat.Currency", - "Plugins.Feed.BMEcat.Currency.Hint", - "Plugins.Feed.BMEcat.ProductPictureSize", - "Plugins.Feed.BMEcat.ProductPictureSize.Hint", - "Plugins.Feed.BMEcat.AppendDescriptionText", - "Plugins.Feed.BMEcat.AppendDescriptionText.Hint", - "Plugins.Feed.BMEcat.BuildDescription", - "Plugins.Feed.BMEcat.BuildDescription.Hint", - "Plugins.Feed.BMEcat.Automatic", - "Plugins.Feed.BMEcat.DescShort", - "Plugins.Feed.BMEcat.DescLong", - "Plugins.Feed.BMEcat.DescTitleAndShort", - "Plugins.Feed.BMEcat.DescTitleAndLong", - "Plugins.Feed.BMEcat.DescManuAndTitleAndShort", - "Plugins.Feed.BMEcat.DescManuAndTitleAndLong", - "Plugins.Feed.BMEcat.UseOwnProductNo", - "Plugins.Feed.BMEcat.UseOwnProductNo.Hint", - "Plugins.Feed.BMEcat.DescriptionToPlainText", - "Plugins.Feed.BMEcat.DescriptionToPlainText.Hint", - "Plugins.Feed.BMEcat.ShippingCost", - "Plugins.Feed.BMEcat.ShippingCost.Hint", - "Plugins.Feed.BMEcat.ShippingTime", - "Plugins.Feed.BMEcat.ShippingTime.Hint", - "Plugins.Feed.BMEcat.Brand", - "Plugins.Feed.BMEcat.Brand.Hint", - "Plugins.Feed.BMEcat.Store", - "Plugins.Feed.BMEcat.Store.Hint", - "Plugins.Feed.BMEcat.ConvertNetToGrossPrices", - "Plugins.Feed.BMEcat.ConvertNetToGrossPrices.Hint", - "Plugins.Feed.BMEcat.LanguageId", - "Plugins.Feed.BMEcat.LanguageId.Hint", - "Plugins.Feed.BMEcat.Generate", - "Plugins.Feed.BMEcat.ConfigSaveNote" - ); - - builder.Delete("Plugins.Widgets.OpenTrans.IsLexwareCompatibe"); - builder.Delete("Admin.System.Maintenance.DeleteExportedFolders.TotalDeleted"); - - // Common - builder.AddOrUpdate("StoreClosed", - "We'll be back.", - "Wir sind bald wieder da."); - builder.AddOrUpdate("StoreClosed.Hint", - "We're busy updating our online store for you and will be back soon.", - "Wir aktualisieren gerade das Angebot in unserem Online-Shop. Die Seite ist demnchst wieder verfgbar."); - - builder.AddOrUpdate("Admin.System.SystemInfo.UsedMemorySize", - "Used memory (RAM)", - "Benutzter Speicher (RAM)"); - builder.AddOrUpdate("Admin.System.SystemInfo.GarbageCollect", - "Collect", - "Aufrumen"); - builder.AddOrUpdate("Admin.System.SystemInfo.GarbageCollectSuccessful", - "The memory has been successfully cleaned up.", - "Der Arbeitsspeicher wurde erfolgreich aufgerumt."); - - builder.AddOrUpdate("Admin.Configuration.Themes.NoConfigurationRequired", - "Theme requires no configuration", - "Theme bentigt keine Konfiguration"); - - - builder.AddOrUpdate("Tax.LegalInfoFooter2", - "* All prices {0}, plus shipping", - "* Alle Preise {0}, zzgl. Versandkosten"); - - builder.AddOrUpdate("Tax.LegalInfoProductDetail2", - "{0} {1} {2}plus shipping", - "{0} {1} {2} zzgl. Versandkosten"); - - builder.AddOrUpdate("ShoppingCart.ShippingInfoLink", - "For a complete listing of all shipping costs please click here.", - "Eine vollstndige Liste aller Versandkosten finden Sie hier."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.resx b/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.resx deleted file mode 100644 index f24f629459..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201509150931528_ExportFramework2.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201511271019577_ExportFramework3.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201511271019577_ExportFramework3.Designer.cs deleted file mode 100644 index 766e90a409..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201511271019577_ExportFramework3.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class ExportFramework3 : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ExportFramework3)); - - string IMigrationMetadata.Id - { - get { return "201511271019577_ExportFramework3"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201511271019577_ExportFramework3.cs b/src/Libraries/SmartStore.Data/Migrations/201511271019577_ExportFramework3.cs deleted file mode 100644 index ffbf880d57..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201511271019577_ExportFramework3.cs +++ /dev/null @@ -1,142 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Data.Entity.Migrations; - using Core.Domain.Customers; - using Core.Domain.Security; - using Setup; - - public partial class ExportFramework3 : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.ExportProfile", "SystemName", c => c.String(maxLength: 400)); - AddColumn("dbo.ExportProfile", "IsSystemProfile", c => c.Boolean(nullable: false)); - DropColumn("dbo.PaymentMethod", "ExcludedCustomerRoleIds"); - DropColumn("dbo.PaymentMethod", "ExcludedCountryIds"); - DropColumn("dbo.PaymentMethod", "ExcludedShippingMethodIds"); - DropColumn("dbo.PaymentMethod", "CountryExclusionContextId"); - DropColumn("dbo.PaymentMethod", "MinimumOrderAmount"); - DropColumn("dbo.PaymentMethod", "MaximumOrderAmount"); - DropColumn("dbo.PaymentMethod", "AmountRestrictionContextId"); - DropColumn("dbo.ShippingMethod", "ExcludedCustomerRoleIds"); - DropColumn("dbo.ShippingMethod", "CountryExclusionContextId"); - } - - public override void Down() - { - AddColumn("dbo.ShippingMethod", "CountryExclusionContextId", c => c.Int(nullable: false)); - AddColumn("dbo.ShippingMethod", "ExcludedCustomerRoleIds", c => c.String(maxLength: 500)); - AddColumn("dbo.PaymentMethod", "AmountRestrictionContextId", c => c.Int(nullable: false)); - AddColumn("dbo.PaymentMethod", "MaximumOrderAmount", c => c.Decimal(precision: 18, scale: 4)); - AddColumn("dbo.PaymentMethod", "MinimumOrderAmount", c => c.Decimal(precision: 18, scale: 4)); - AddColumn("dbo.PaymentMethod", "CountryExclusionContextId", c => c.Int(nullable: false)); - AddColumn("dbo.PaymentMethod", "ExcludedShippingMethodIds", c => c.String(maxLength: 500)); - AddColumn("dbo.PaymentMethod", "ExcludedCountryIds", c => c.String(maxLength: 2000)); - AddColumn("dbo.PaymentMethod", "ExcludedCustomerRoleIds", c => c.String(maxLength: 500)); - DropColumn("dbo.ExportProfile", "IsSystemProfile"); - DropColumn("dbo.ExportProfile", "SystemName"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - var permissionMigrator = new PermissionMigrator(context); - - permissionMigrator.AddPermission(new PermissionRecord - { - Name = "Admin area. Manage Url Records", - SystemName = "ManageUrlRecords", - Category = "Configuration" - }, new string[] { SystemCustomerRoleNames.Administrators }); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Common.Export.PDF", "PDF Export", "PDF Export"); - builder.AddOrUpdate("Admin.Common.TemporaryFiles", "Temporary files", "Temporre Dateien"); - builder.AddOrUpdate("Admin.Common.PublicFiles", "Public files", "ffentliche Dateien"); - builder.AddOrUpdate("Admin.Common.Of", "of", "von"); - - builder.AddOrUpdate("Admin.Common.NoTempFilesFound", - "There are no temporary files.", - "Es sind keine temporren Dateien vorhanden."); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportEntityType.NewsLetterSubscription", - "Newsletter Subscribers", - "Newsletter Abonnenten"); - - builder.AddOrUpdate("Admin.DataExchange.Export.SystemName", - "System name of profile", - "Systemname des Profils", - "The system name of the export profile.", - "Der Systemname des Exportprofils."); - - builder.AddOrUpdate("Admin.DataExchange.Export.IsSystemProfile", - "System profile", - "Systemprofil", - "Indicates whether the export profile is a system profile. System profiles cannot be removed.", - "Gibt an, ob es sich bei dem Exportprofil um eine Systemprofil handelt. Systemprofile knnen nicht entfernt werden."); - - builder.AddOrUpdate("Admin.DataExchange.Export.CannotDeleteSystemProfile", - "Cannot delete a system export profile.", - "Ein System-Exportprofil kann nicht gelscht werden."); - - builder.AddOrUpdate("Admin.DataExchange.Export.MissingSystemProfile", - "The system export profile {0} was not found.", - "Das System-Exportprofil {0} wurde nicht gefunden."); - - builder.AddOrUpdate("Admin.DataExchange.Export.ExportFiles", - "Export files", - "Exportdateien"); - - - builder.AddOrUpdate("Admin.Configuration.Payment.Methods.RestrictionNote", - "There were no possibilities found to restrict payment methods.", - "Es wurden keine Mglichkeiten zur Einschrnkung von Zahlungsarten gefunden."); - - builder.AddOrUpdate("Admin.Configuration.Shipping.Methods.RestrictionNote", - "There were no possibilities found to restrict shipping methods.", - "Es wurden keine Mglichkeiten zur Einschrnkung von Versandarten gefunden."); - - - - builder.Delete( - "Admin.Configuration.Payment.Methods.ExcludedCustomerRole", - "Admin.Configuration.Payment.Methods.ExcludedShippingMethod", - "Admin.Configuration.Payment.Methods.ExcludedCountry", - "Admin.Configuration.Payment.Methods.MinimumOrderAmount", - "Admin.Configuration.Payment.Methods.MaximumOrderAmount", - "Admin.Configuration.Restrictions.AmountRestrictionContext", - "Enums.SmartStore.Core.Domain.Common.AmountRestrictionContextType.SubtotalAmount", - "Enums.SmartStore.Core.Domain.Common.AmountRestrictionContextType.TotalAmount", - "Enums.SmartStore.Core.Domain.Common.CountryRestrictionContextType.BillingAddress", - "Enums.SmartStore.Core.Domain.Common.CountryRestrictionContextType.ShippingAddress", - "Admin.Configuration.Shipping.Methods.ExcludedCustomerRole", - "Admin.Configuration.Shipping.Methods.ExcludedCountry", - "Admin.Configuration.Restrictions.CountryExclusionContext", - "Admin.Common.ExportToXml.All", - "Admin.Common.ExportToXml.Selected", - "Admin.Common.ExportToCsv.All", - "Admin.Common.ExportToCsv.Selected", - "Admin.Common.ExportToCsv", - "Admin.Common.ExportToPdf.TocTitle", - "PDFProductCatalog.SKU", - "PDFProductCatalog.Price", - "PDFProductCatalog.Manufacturer", - "PDFProductCatalog.Weight", - "PDFProductCatalog.Length", - "PDFProductCatalog.Width", - "PDFProductCatalog.Height", - "PDFProductCatalog.SpecificationAttributes", - "PDFProductCatalog.BundledItems", - "PDFProductCatalog.AssociatedProducts" - ); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201511271019577_ExportFramework3.resx b/src/Libraries/SmartStore.Data/Migrations/201511271019577_ExportFramework3.resx deleted file mode 100644 index 9c408d2cf0..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201511271019577_ExportFramework3.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201512151526290_ImportFramework.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.Designer.cs deleted file mode 100644 index a112acac53..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class ImportFramework : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ImportFramework)); - - string IMigrationMetadata.Id - { - get { return "201512151526290_ImportFramework"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs deleted file mode 100644 index a7d561921c..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.cs +++ /dev/null @@ -1,421 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Data.Entity.Migrations; - using Core.Domain.Customers; - using Core.Domain.Security; - using Core.Domain.Seo; - using Setup; - - public partial class ImportFramework : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - CreateTable( - "dbo.ImportProfile", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(nullable: false, maxLength: 100), - FolderName = c.String(nullable: false, maxLength: 100), - FileTypeId = c.Int(nullable: false), - EntityTypeId = c.Int(nullable: false), - Enabled = c.Boolean(nullable: false), - Skip = c.Int(nullable: false), - Take = c.Int(nullable: false), - FileTypeConfiguration = c.String(), - ColumnMapping = c.String(), - SchedulingTaskId = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.ScheduleTask", t => t.SchedulingTaskId) - .Index(t => t.SchedulingTaskId); - - } - - public override void Down() - { - DropForeignKey("dbo.ImportProfile", "SchedulingTaskId", "dbo.ScheduleTask"); - DropIndex("dbo.ImportProfile", new[] { "SchedulingTaskId" }); - DropTable("dbo.ImportProfile"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - var permissionMigrator = new PermissionMigrator(context); - var activityLogMigrator = new ActivityLogTypeMigrator(context); - - permissionMigrator.AddPermission(new PermissionRecord - { - Name = "Admin area. Manage Imports", - SystemName = "ManageImports", - Category = "Configuration" - }, new string[] { SystemCustomerRoleNames.Administrators }); - - activityLogMigrator.AddActivityLogType("DeleteOrder", "Delete order", "Auftrag gelscht"); - - context.MigrateSettings(x => - { - var seoSettings = new SeoSettings(); - x.Add("seosettings.seonamecharconversion", seoSettings.SeoNameCharConversion); - }); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Common.RecordsSkip", - "Skip", - "berspringen", - "Specifies the number of records to be skipped.", - "Legt die Anzahl der zu berspringenden Datenstze fest."); - - builder.AddOrUpdate("Common.Unknown", "Unknown", "Unbekannt"); - builder.AddOrUpdate("Common.Unavailable", "Unavailable", "Nicht verfgbar"); - builder.AddOrUpdate("Common.Language", "Language", "Sprache"); - builder.AddOrUpdate("Admin.Common.ImportFile", "Import file", "Importdatei"); - builder.AddOrUpdate("Admin.Common.ImportFiles", "Import files", "Importdateien"); - builder.AddOrUpdate("Admin.Common.CsvConfiguration", "CSV Configuration", "CSV Konfiguration"); - - builder.AddOrUpdate("Admin.Common.RecordsTake", - "Limit", - "Begrenzen", - "Specifies the maximum number of records to be processed.", - "Legt die maximale Anzahl der zu verarbeitenden Datenstze fest."); - - builder.AddOrUpdate("Admin.Common.FileTypeMustEqual", - "The file must be of the type {0}.", - "Die Datei muss vom Typ {0} sein."); - - builder.AddOrUpdate("Admin.DataExchange.Import.NoProfiles", - "There were no import profiles found.", - "Es wurden keine Importprofile gefunden."); - - builder.AddOrUpdate("Admin.DataExchange.Import.Name", - "Name of profile", - "Name des Profils", - "Specifies the name of the import profile.", - "Legt den Namen des Importprofils fest."); - - builder.AddOrUpdate("Admin.DataExchange.Import.ProgressInfo", - "{0} of {1} records processed", - "{0} von {1} Datenstzen verarbeitet"); - - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ImportEntityType.Product", "Product", "Produkt"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ImportEntityType.Customer", "Customer", "Kunde"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ImportEntityType.NewsLetterSubscription", "Newsletter Subscriber", "Newsletter Abonnent"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ImportEntityType.Category", "Category", "Warengruppe"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ImportFileType.CSV", "Delimiter separated values (.csv, .txt, .tab)", "Trennzeichen getrennte Werte (.csv, .txt, .tab)"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ImportFileType.XLSX", "Excel (.xlsx)", "Excel (.xlsx)"); - - builder.AddOrUpdate("Admin.DataExchange.Import.FileUpload", - "Upload import file...", - "Importdatei hochladen..."); - - builder.AddOrUpdate("Admin.DataExchange.Import.MissingImportFile", - "Please upload an import file.", - "Bitte laden Sie eine Importdatei hoch."); - - builder.AddOrUpdate("Admin.DataExchange.Csv.QuoteAllFields", - "Quote all fields", - "Alle Felder in Anfhrungszeichen", - "Specifies whether to set quotation marks around all field values.", - "Legt fest, ob die Werte aller Felder in Anfhrungszeichen gestellt werden sollen."); - - builder.AddOrUpdate("Admin.DataExchange.Csv.TrimValues", - "Trim values", - "berflssige Leerzeichen entfernen", - "Specifies whether to remove space characters at start and end of a field value.", - "Legt fest, ob Leerzeichen am Anfang und am Ende eines Feldwertes entfernt werden sollen."); - - builder.AddOrUpdate("Admin.DataExchange.Csv.SupportsMultiline", - "Supports multilines", - "Mehrzeilen erlaubt", - "Specifies whether field values with multilines are supported.", - "Legt fest, ob mehrzeilige Feldwerte untersttzt werden."); - - builder.AddOrUpdate("Admin.DataExchange.Csv.Delimiter", - "Delimiter", - "Trennzeichen", - "Specifies the field separator.", - "Legt das zu verwendende Trennzeichen fr die Felder fest."); - - builder.AddOrUpdate("Admin.DataExchange.Csv.Quote", - "Quote character", - "Anfhrungszeichen", - "Specifies the quotation character.", - "Legt das zu verwendende Anfhrungszeichen fest."); - - builder.AddOrUpdate("Admin.DataExchange.Csv.Escape", - "Inner quote character", - "Inneres Anfhrungszeichen", - "Specifies the inner quote character used for escaping.", - "Legt das innere Anfhrungszeichen (Escaping) fest."); - - builder.AddOrUpdate("Admin.DataExchange.Csv.Delimiter.Validation", - "Please enter a valid delimiter.", - "Geben Sie bitte ein gltiges Trennzeichen ein."); - - builder.AddOrUpdate("Admin.DataExchange.Csv.Quote.Validation", - "Please enter a valid quote character.", - "Geben Sie bitte ein gltiges Anfhrungszeichen ein."); - - builder.AddOrUpdate("Admin.DataExchange.Csv.Escape.Validation", - "Please enter a valid inner quote character (escaping).", - "Geben Sie bitte ein gltiges, inneres Anfhrungszeichen (Escaping) ein."); - - builder.AddOrUpdate("Admin.DataExchange.Csv.EscapeDelimiter.Validation", - "Delimiter and inner quote character cannot be equal in CSV files.", - "Trennzeichen und inneres Anfhrungszeichen knnen in CSV Dateien nicht gleich sein."); - - builder.AddOrUpdate("Admin.DataExchange.Csv.QuoteDelimiter.Validation", - "Delimiter and quote character cannot be equal in CSV files.", - "Trennzeichen und Anfhrungszeichen knnen in CSV Dateien nicht gleich sein."); - - - builder.AddOrUpdate("Admin.Catalog.Products.Fields.BasePriceMeasureUnit", "Base price measure unit", "Grundpreis Maeinheit"); - builder.AddOrUpdate("Admin.Catalog.Products.Fields.ApprovedRatingSum", "Approved rating sum", "Summe genehmigter Bewertungen"); - builder.AddOrUpdate("Admin.Catalog.Products.Fields.NotApprovedRatingSum", "Not approved rating sum", "Summe nicht genehmigter Bewertungen"); - builder.AddOrUpdate("Admin.Catalog.Products.Fields.ApprovedTotalReviews", "Approved total reviews", "Summe genehmigter Rezensionen"); - builder.AddOrUpdate("Admin.Catalog.Products.Fields.NotApprovedTotalReviews", "Not approved total reviews", "Summe nicht genehmigter Rezensionen"); - builder.AddOrUpdate("Admin.Catalog.Products.Fields.HasTierPrices", "Has tier prices", "Hat Staffelpreise"); - builder.AddOrUpdate("Admin.Catalog.Products.Fields.LowestAttributeCombinationPrice", "Lowest attribute combination price", "Niedrigster Attributkombinationspreis"); - builder.AddOrUpdate("Admin.Catalog.Products.Fields.HasDiscountsApplied", "Has discounts applied", "Hat angewendete Rabatte"); - - builder.AddOrUpdate("Admin.Catalog.Categories.Fields.ParentCategory", "Parent category", "bergeordnete Warengruppe"); - - builder.AddOrUpdate("Admin.Customers.Customers.Fields.CustomerGuid", "Customer GUID", "Kunden GUID"); - builder.AddOrUpdate("Admin.Customers.Customers.Fields.PasswordSalt", "Password salt", "Passwort Salt"); - builder.AddOrUpdate("Admin.Customers.Customers.Fields.IsSystemAccount", "Is system account", "Ist Systemkonto"); - builder.AddOrUpdate("Admin.Customers.Customers.Fields.LastLoginDateUtc", "Last login date", "Letztes Login-Datum"); - - builder.AddOrUpdate("Admin.Promotions.NewsLetterSubscriptions.Fields.NewsLetterSubscriptionGuid", "Subscription GUID", "Abonnement GUID"); - - builder.AddOrUpdate("Admin.DataExchange.ColumnMapping.Note", - "You can optionally set for each field of the import file whether and for which object property the data should be imported. Fields with equal names are always imported as long as they are not explicitly ignored. Not yet selected properties are highlighted in the selection list. It is also possible to define a default value which is applied when the import field is empty. Stored assignments becomes invalid and reset when the delimiter changes.", - "Sie knnen optional fr jedes Feld der Importdatei festlegen, ob und nach welcher Objekteigenschaft dessen Daten zu importieren sind. Gleichnamige Felder werden grundstzlich immer importiert, sofern sie nicht explizit ignoriert werden sollen. Noch nicht ausgewhlte Eigenschaften sind in der Auswahlliste hervorgehoben. Zudem ist die Angabe eines Standardwertes mglich, der angewendet wird, wenn das Importfeld leer ist. Durch nderung des Trennzeichens werden gespeicherte Zuordnungen ungltig und zurckgesetzt."); - - builder.AddOrUpdate("Admin.DataExchange.ColumnMapping.ImportField", "Import Field", "Importfeld"); - builder.AddOrUpdate("Admin.DataExchange.ColumnMapping.EntityProperty", "Object property", "Eigenschaft des Objektes"); - builder.AddOrUpdate("Admin.DataExchange.ColumnMapping.DefaultValue", "Default Value", "Standardwert"); - - - builder.Delete( - "Admin.DataExchange.Export.LastExecution", - "Admin.DataExchange.Export.Offset", - "Admin.DataExchange.Export.Limit", - "Admin.Promotions.NewsLetterSubscriptions.ImportEmailsSuccess", - "Admin.Common.ImportFromCsv", - "Admin.Common.CsvFile", - - "Admin.Common.ImportFromExcel", - "Admin.Common.ExcelFile", - "Admin.Common.ImportFromExcel.InProgress", - "Admin.Common.ImportFromExcel.LastResultTitle", - "Admin.Common.ImportFromExcel.ProcessedCount", - "Admin.Common.ImportFromExcel.QuickStats", - "Admin.Common.ImportFromExcel.ActiveSince", - "Admin.Common.ImportFromExcel.CancelPrompt", - "Admin.Common.ImportFromExcel.Cancel", - "Admin.Common.ImportFromExcel.Cancelled", - "Admin.Common.ImportFromExcel.DownloadReport", - "Admin.Common.ImportFromExcel.NoReportAvailable", - - "Admin.Configuration.ActivityLog.ActivityLog.Fields.ActivityLogTypeColumn", - "Plugins.ExchangeRate.EcbExchange.SetCurrencyToEURO" - ); - - builder.AddOrUpdate("ActivityLog.DeleteOrder", "Deleted order {0}", "Auftrag {0} gelscht"); - - builder.AddOrUpdate("Admin.System.SystemCustomerNames.SearchEngine", "Search Engine", "Suchmaschine"); - builder.AddOrUpdate("Admin.System.SystemCustomerNames.BackgroundTask", "Background Task", "Geplante Aufgabe"); - builder.AddOrUpdate("Admin.System.SystemCustomerNames.PdfConverter", "PDF Converter", "PDF-Konvertierer"); - - builder.AddOrUpdate("Admin.Configuration.ActivityLog.ActivityLog.Fields.CustomerSystemAccount", - "Customer system account", - "Kundensystemkonto", - "Filters results by customer system accounts.", - "Filtert Ergebnisse nach Kundenystemkonten."); - - builder.AddOrUpdate("Admin.Configuration.ActivityLog.ActivityLog.Fields.CustomerEmail", - "Customer Email", - "Kunden-E-Mail", - "Filters results by customer email address.", - "Filtert Ergebnisse nach E-Mail-Adresse der Kunden."); - - builder.AddOrUpdate("Admin.Configuration.Plugins.UnknownError", - "An unknown error occurred when calling a plugin. Please refer to the following message for details.", - "Beim Aufruf eines Plugins ist ein unbekannter Fehler aufgetreten. Details entnehmen Sie bitte der folgenden Meldung."); - - builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.AllowUnicodeCharsInUrls", - "Allow unicode characters", - "Unicode-Zeichen erlauben", - "Check whether SEO names can contain letters that are classified as unicode characters.", - "Legt fest, ob als Unicode-Zeichen eingestufte Buchstaben in SEO relevanten Namen erlaubt sind."); - - builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.SeoNameCharConversion", - "Characters to be converted", - "Zu konvertierende Zeichen", - "Allows an individual conversion of characters for SEO name creation. Enter the old and the new character separated by a semicolon, e.g. ;ae. Each entry has to be entered in a new line.", - "Ermglicht das individuelle Konvertieren von Zeichen bei der Erstellung SEO Namen. Geben Sie hier durch Semikolon getrennt das alte und das neue Zeichen ein, z.B. ;ae. Jeder Eintrag muss in einer neuen Zeile erfolgen."); - - builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.TestSeoNameCreation", - "Check string", - "Zeichenkette prfen", - "Enter any string to check the SEO name creation. Changed settings must be saved before.", - "Geben Sie eine beliebige Zeichenkette ein, um daraus den SEO Namen zu erstellen. Genderte Einstellungen mssen zuvor gespeichert werden."); - - - builder.AddOrUpdate("Admin.System.Warnings.NoPermissionsDefined", - "There are no permissions defined.", - "Es sind keine Zugriffsrechte festgelegt."); - - builder.AddOrUpdate("Admin.System.Warnings.NoCustomerRolesDefined", - "There are no customer roles defined.", - "Es sind keine Kundengruppen festgelegt."); - - builder.AddOrUpdate("Admin.System.Warnings.AccessDeniedToAnonymousRequest", - "Access denied to anonymous request on {0}.", - "Zugriffsverweigerung durch anonyme Anfrage bei {0}."); - - builder.AddOrUpdate("Admin.System.Warnings.AccessDeniedToUser", - "Access denied to user #{0} '{1}' on {2}.", - "Zugriffsverweigerung durch Kunde #{0} '{1}' bei {2}."); - - builder.AddOrUpdate("Admin.Configuration.Countries.CannotDeleteDueToAssociatedAddresses", - "The country cannot be deleted because it has associated addresses.", - "Das Land kann nicht gelscht werden, weil ihm Adressen zugeordnet sind."); - - builder.AddOrUpdate("Admin.Configuration.Countries.States.CantDeleteWithAddresses", - "The state\\province cannot be deleted because it has associated addresses.", - "Das Bundesland\\Region kann nicht gelscht werden, weil ihm Adressen zugeordnet sind."); - - builder.AddOrUpdate("Admin.Configuration.Shipping.Methods.NoMethodsLoaded", - "No shipping methods could be loaded.", - "Es konnten keine Versandarten geladen werden."); - - builder.AddOrUpdate("Admin.System.Warnings.NoShipmentItems", - "No shipment items", - "Keine Versand-Artikel"); - - builder.AddOrUpdate("Admin.System.Warnings.DigitsOnly", - "Please enter digits only.", - "Bitte nur Ziffern eingeben."); - - builder.AddOrUpdate("Account.Register.Errors.CannotRegisterSearchEngine", - "A search engine can't be registered.", - "Eine Suchmaschine kann nicht registriert werden."); - - builder.AddOrUpdate("Account.Register.Errors.CannotRegisterTaskAccount", - "A background task account can't be registered.", - "Das Konto einer geplanten Aufgabe kann nicht registriert werden."); - - builder.AddOrUpdate("Account.Register.Errors.AlreadyRegistered", - "The customer is already registered.", - "Der Kunde ist bereits registriert."); - - builder.AddOrUpdate("Admin.Customers.CustomerRoles.CannotFoundRole", - "The customer role \"{0}\" cannot be found.", - "Die Kundengruppe \"{0}\" wurde nicht gefunden."); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.RegisterCustomerRole", - "Customer role at registrations", - "Kundengruppe bei Registrierungen", - "Specifies a customer role that will be assigned to newly registered customers.", - "Legt eine Kundengruppe fest, die neu registrierten Kunden zugeordnet wird."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Order.DisplayOrdersOfAllStores", - "Display orders of all stores", - "Auftrge aller Shops anzeigen", - "Specifies whether to display the orders of all stores to the customer. If this option is disabled, only the orders of the current store are displayed.", - "Legt fest, ob dem Kunden die Auftrge aller Shops angezeigt werden sollen. Ist diese Option deaktiviert, so werden nur die Auftrge des aktuellen Shops angezeigt."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Order.GiftCards_Deactivated") - .Value("de", "Geschenkgutschein wird deaktiviert, wenn Auftragsstatus..."); - - builder.AddOrUpdate("Admin.Configuration.Languages.Fields.UniqueSeoCode.Required", - "Please select a SEO language code.", - "Bitte legen Sie einen SEO Sprach-Code fest."); - - builder.AddOrUpdate("Admin.Configuration.Languages.Fields.FlagImageFileName", - "Flag image", - "Flaggenbild", - "Specifies the flag image. The files for the flag images must be stored in /Content/Images/flags/.", - "Legt das Flaggenbild fest. Die Dateien der Flaggenbilder mssen in /Content/Images/flags/ liegen."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.HideCategoryDefaultPictures", - "Hide default picture for categories", - "Standardbild bei Warengruppen ausblenden", - "Specifies whether to hide the default image for categories. The default image is shown when no image is assigned to a category.", - "Legt fest, ob das Standardbild bei Warengruppen ausgeblendet werden soll. Das Standardbild wird angezeigt, wenn der Warengruppe kein Bild zugeordnet ist."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.HideProductDefaultPictures", - "Hide default picture for products", - "Standardbild bei Produkten ausblenden", - "Specifies whether to hide the default image for products. The default image is shown when no image is assigned to a product.", - "Legt fest, ob das Standardbild bei Produkten ausgeblendet werden soll. Das Standardbild wird angezeigt, wenn dem Produkt kein Bild zugeordnet ist."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Media.MessageProductThumbPictureSize", - "Thumbnail size of products in emails", - "Thumbnail-Gre von Produkten in E-Mails", - "Specifies the thumbnail image size (pixels) of products in emails. Enter 0 to not display thumbnails.", - "Legt die Thumbnail-Bildgre (in Pixel) von Produkten in E-Mails fest. Geben Sie 0 ein, um keine Thumbnails anzuzeigen."); - - builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.MetaRobotsContent", - "Meta robots", - "Meta Robots", - "Specifies if and how search engines indexing the pages of your store.", - "Legt fest, ob und wie Suchmaschinen die Seiten Ihres Shops indexieren."); - - builder.AddOrUpdate("Providers.ExchangeRate.EcbExchange.SetCurrencyToEURO", - "You can use ECB (European central bank) exchange rate provider only when exchange rate currency code is set to EURO.", - "Der EZB-Wechselkursdienst kann nur genutzt werden, wenn der Wechselkurs-Whrungscode auf EUR gesetzt ist."); - - - builder.AddOrUpdate("Common.Loading", "Loading", "Lade"); - builder.AddOrUpdate("Common.ShowMore", "Show more", "Mehr anzeigen"); - builder.AddOrUpdate("Common.Published", "Published", "Verffentlicht"); - builder.AddOrUpdate("Common.Unpublished", "Unpublished", "Unverffentlicht"); - builder.AddOrUpdate("Common.NotSelectable", "Not selectable", "Nicht auswhlbar"); - - builder.AddOrUpdate("Common.EntityPicker.SinglePickNote", - "Click on an item to select it and OK to apply it.", - "Klicken Sie auf ein Element, um es auszuwhlen und OK, um es zu bernehmen."); - - builder.AddOrUpdate("Common.EntityPicker.MultiPickNote", - "Click on an item to select or deselect it and OK to apply the selection.", - "Klicken Sie auf ein Element, um es aus- bzw. abzuwhlen und OK, um die Auswahl zu bernehmen."); - - builder.AddOrUpdate("Common.EntityPicker.NoMoreItemsFound", - "There were no more items found.", - "Es wurden keine weiteren Elemente gefunden."); - - builder.AddOrUpdate("Admin.Catalog.Products.BundleItems.NotesOnProductBundles", - "Notes on product bundles", - "Hinweise zu Produkt-Bundles"); - - builder.AddOrUpdate("Admin.Catalog.Products.RelatedProducts.AddNew", - "Add cross-selling product", - "Cross-Selling-Produkt hinzufgen"); - - builder.AddOrUpdate("Admin.Catalog.Products.RelatedProducts.SaveBeforeEdit", - "You need to save the product before you can add cross-selling products for this product page.", - "Sie mssen das Produkt speichern, bevor Sie Cross-Selling-Produkte hinzufgen knnen."); - - builder.AddOrUpdate("Admin.Catalog.Products.CrossSells.AddNew", - "Add checkout-selling product", - "Checkout-Selling-Produkt hinzufgen"); - - builder.AddOrUpdate("Admin.Catalog.Products.CrossSells.SaveBeforeEdit", - "You need to save the product before you can add checkout-selling products for this product page.", - "Sie mssen das Produkt speichern, bevor Sie Checkout-Selling-Produkte hinzufgen knnen."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.resx b/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.resx deleted file mode 100644 index fa9e8cc53c..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201512151526290_ImportFramework.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201601262000441_ImportFramework1.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201601262000441_ImportFramework1.Designer.cs deleted file mode 100644 index b8d27247a3..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201601262000441_ImportFramework1.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class ImportFramework1 : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ImportFramework1)); - - string IMigrationMetadata.Id - { - get { return "201601262000441_ImportFramework1"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201601262000441_ImportFramework1.cs b/src/Libraries/SmartStore.Data/Migrations/201601262000441_ImportFramework1.cs deleted file mode 100644 index 8a6384c3e5..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201601262000441_ImportFramework1.cs +++ /dev/null @@ -1,478 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Data.Entity.Migrations; - using Setup; - - public partial class ImportFramework1 : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.ImportProfile", "UpdateOnly", c => c.Boolean(nullable: false)); - AddColumn("dbo.ImportProfile", "KeyFieldNames", c => c.String(maxLength: 1000)); - AddColumn("dbo.ImportProfile", "ResultInfo", c => c.String()); - } - - public override void Down() - { - DropColumn("dbo.ImportProfile", "ResultInfo"); - DropColumn("dbo.ImportProfile", "KeyFieldNames"); - DropColumn("dbo.ImportProfile", "UpdateOnly"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.DataExchange.Import.MultipleFilesSameFileTypeNote", - "For multiple import files please make sure that they are of the same file type and that the content follows the same pattern (e.g. same column headings).", - "Bei mehreren Importdateien ist darauf zu achten, dass diese vom selben Dateityp sind und deren Inhalt demselben Schema folgt (z.B. gleiche Spaltenberschriften)."); - - builder.AddOrUpdate("Admin.DataExchange.Import.ProfileEntitySelectNote", - "Please select an object that you want to import.", - "Whlen Sie bitte ein Objekt aus, das Sie importieren mchten."); - - builder.AddOrUpdate("Admin.DataExchange.Import.ProfileCreationNote", - "Please upload an import file, enter a meaningful name for the import profile and save.", - "Laden Sie bitte eine Importdatei hoch, legen Sie einen aussagekrftigen Namen fr das Importprofil fest und speichern Sie."); - - builder.AddOrUpdate("Admin.DataExchange.Import.AddAnotherFile", - "Add import file...", - "Importdatei hinzufgen..."); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.RunNow.Progress.DataImportTask", - "The task is now running in the background. You will receive an email as soon as it is completed. The progress can be tracked in the import profile list.", - "Die Aufgabe wird jetzt im Hintergrund ausgefhrt. Sie erhalten eine E-Mail, sobald sie abgeschlossen ist. Den Fortschritt knnen Sie in der Importprofilliste verfolgen."); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.RunNow.Progress.DataExportTask", - "The task is now running in the background. You will receive an email as soon as it is completed. The progress can be tracked in the export profile list.", - "Die Aufgabe wird jetzt im Hintergrund ausgefhrt. Sie erhalten eine E-Mail, sobald sie abgeschlossen ist. Den Fortschritt knnen Sie in der Exportprofilliste verfolgen."); - - builder.AddOrUpdate("Admin.DataExchange.Import.DefaultProfileNames", - "My product import;My category import;My customer import;My newsletter subscription import", - "Mein Produktimport;Mein Warengruppenimport;Mein Kundenimport;Mein Newsletter-Abonnement-Import"); - - builder.AddOrUpdate("Admin.DataExchange.Import.LastImportResult", - "Last import result", - "Letztes Importergebnis"); - - builder.AddOrUpdate("Admin.Common.TotalRows", "Total rows", "Zeilen insgesamt"); - builder.AddOrUpdate("Admin.Common.Skipped", "Skipped", "Ausgelassen"); - builder.AddOrUpdate("Admin.Common.NewRecords", "New records", "Neue Datenstze"); - builder.AddOrUpdate("Admin.Common.Updated", "Updated", "Aktualisiert"); - builder.AddOrUpdate("Admin.Common.Warnings", "Warnings", "Warnungen"); - builder.AddOrUpdate("Admin.Common.Errors", "Errors", "Fehler"); - builder.AddOrUpdate("Admin.Common.UnsupportedEntityType", "Unsupported entity type '{0}'", "Nicht untersttzter Entittstyp '{0}'"); - builder.AddOrUpdate("Admin.Common.DataExchange", "Data exchange", "Datenaustausch"); - - builder.AddOrUpdate("Admin.DataExchange.Import.CompletedEmail.Body", - "This is an automatic notification of store \"{0}\" about a recent data import. Summary:", - "Dies ist eine automatische Benachrichtung von Shop \"{0}\" ber einen erfolgten Datenimport. Zusammenfassung:"); - - builder.AddOrUpdate("Admin.DataExchange.Import.CompletedEmail.Subject", - "Import of \"{0}\" has been finished", - "Import von \"{0}\" ist abgeschlossen"); - - builder.AddOrUpdate("Admin.DataExchange.Import.ColumnMapping", - "Assignment of import fields", - "Zuordnung der Importfelder"); - - builder.AddOrUpdate("Admin.DataExchange.Import.SelectTargetProperty", - "Create new assignment here", - "Hier neue Zuordnung vornehmen"); - - builder.AddOrUpdate("Admin.DataExchange.Import.UpdateOnly", - "Only update", - "Nur aktualisieren", - "If this option is enabled, only existing data is updated but no new records are added.", - "Ist diese Option aktiviert, werden nur vorhandene Daten aktualisiert, aber keine neue Datenstze hinzugefgt."); - - builder.AddOrUpdate("Admin.DataExchange.Import.KeyFieldNames", - "Key fields", - "Schlsselfelder", - "Existing records can be identified for updates on the basis of key fields. The key fields are processed in the order how they are defined here.", - "Anhand von Schlsselfeldern knnen vorhandene Datenstze zwecks Aktualisierung identifiziert werden. Die Schlsselfelder werden in der hier festgelegten Reihenfolge verarbeitet."); - - builder.AddOrUpdate("Admin.DataExchange.Import.Validate.OneKeyFieldRequired", - "At least one key field is required.", - "Es ist mindestens ein Schlsselfeld erforderlich."); - - builder.AddOrUpdate("Admin.DataExchange.ColumnMapping.Validate.MultipleMappedIgnored", - "The following object properties were multiple assigned and thus ignored: {0}", - "Die folgenden Objekteigenschaft wurden mehrfach zugeodnet und deshalb ignoriert: {0}"); - - builder.AddOrUpdate("Admin.DataExchange.ColumnMapping.Validate.MappingsReset", - "The stored field assignments are invalid due to the change of the delimiter and were reset.", - "Die gespeicherten Feldzuordnungen sind aufgrund der nderung des Trennzeichens ungltig und wurden zurckgesetzt."); - - - builder.AddOrUpdate("Common.Download.NoDataAvailable", - "Download data is not available anymore.", - "Es sind keine Daten zum Herunterladen mehr verfgbar."); - - builder.AddOrUpdate("Common.Download.NotAvailable", - "Download is not available any more.", - "Der Download ist nicht mehr verfgbar."); - - builder.AddOrUpdate("Common.Download.SampleNotAvailable", - "Sample download is not available anymore.", - "Der Download einer Beispieldatei ist nicht mehr verfgbar."); - - builder.AddOrUpdate("Common.Download.HasNoSample", - "The product variant doesn't have a sample download.", - "Fr die Produktvariante ist der Download einer Beispieldatei nicht verfgbar."); - - builder.AddOrUpdate("Common.Download.NotAllowed", - "Downloads are not allowed.", - "Downloads sind nicht gestattet."); - - builder.AddOrUpdate("Common.Download.MaxNumberReached", - "You have reached the maximum number of downloads {0}.", - "Sie haben die maximale Anzahl an Downloads {0} erreicht."); - - builder.AddOrUpdate("Account.CustomerOrders.NotYourOrder", - "This is not your order.", - "Dieser Auftrag konnte Ihnen nicht zugeordnet werden."); - - builder.AddOrUpdate("Shipping.CouldNotLoadMethod", - "The shipping rate computation method could not be loaded.", - "Die Berechnungsmethode fr Versandkosten konnte nicht geladen werden."); - - builder.AddOrUpdate("Shipping.OneActiveMethodProviderRequired", - "At least one shipping rate computation method provider is required to be active.", - "Mindestens ein Provider zur Berechnung von Versandkosten muss aktiviert sein."); - - builder.AddOrUpdate("Payment.CouldNotLoadMethod", - "The payment method could not be loaded.", - "Die Zahlungsart konnte nicht geladen werden."); - - builder.AddOrUpdate("Payment.MethodNotAvailable", - "The payment method is not available.", - "Die Zahlungsart steht nicht zur Verfgung."); - - builder.AddOrUpdate("Payment.OneActiveMethodProviderRequired", - "At least one payment method provider is required to be active.", - "Mindestens ein Zahlungsart-Provider muss aktiviert sein."); - - builder.AddOrUpdate("Payment.RecurringPaymentNotSupported", - "Recurring payments are not supported by selected payment method.", - "Wiederkehrende Zahlungen sind fr die gewhlte Zahlungsart nicht mglich."); - - builder.AddOrUpdate("Payment.RecurringPaymentNotActive", - "Recurring payment is not active.", - "Wiederkehrende Zahlung ist inaktiv."); - - builder.AddOrUpdate("Payment.RecurringPaymentTypeUnknown", - "The recurring payment type is not supported.", - "Der Typ von wiederkehrender Zahlung wird nicht untersttzt."); - - builder.AddOrUpdate("Payment.CannotCalculateNextPaymentDate", - "The next payment date could not be calculated.", - "Das Datum der nchsten Zahlung kann nicht ermittelt werden."); - - builder.AddOrUpdate("Payment.PayingFailed", - "Unfortunately we can not handle this purchasing via your preferred payment method. Please select an alternate payment option to complete your order.", - "Leider knnen wir diesen Einkauf nicht ber die gewnschte Zahlungsart abwickeln. Bitte whlen Sie eine alternative Zahlungsoption aus, um Ihre Bestellung abzuschlieen."); - - builder.AddOrUpdate("Order.InitialOrderDoesNotExistForRecurringPayment", - "No initial order exists for the recurring payment.", - "Fr die wiederkehrende Zahlung existiert kein Ausgangsauftrag."); - - builder.AddOrUpdate("Order.CannotCalculateShippingTotal", - "The shipping total could not be calculated.", - "Die Versandkosten konnten nicht berechnet werden."); - - builder.AddOrUpdate("Order.CannotCalculateOrderTotal", - "The order total could not be calculated.", - "Die Auftragssumme konnte nicht berechnet werden."); - - builder.AddOrUpdate("Order.BillingAddressMissing", - "The billing address is missing.", - "Die Rechnungsanschrift fehlt."); - - builder.AddOrUpdate("Order.ShippingAddressMissing", - "The shipping address is missing.", - "Die Lieferanschrift fehlt."); - - builder.AddOrUpdate("Order.CountryNotAllowedForBilling", - "The country '{0}' is not allowed for billing.", - "Eine Rechnungslegung ist fr das Land '{0}' unzulssig."); - - builder.AddOrUpdate("Order.CountryNotAllowedForShipping", - "The country '{0}' is not allowed for shipping.", - "Ein Versand ist fr das Land '{0}' unzulssig."); - - builder.AddOrUpdate("Order.NoRecurringProducts", - "There are no recurring products.", - "Keine Abonnements."); - - builder.AddOrUpdate("Order.NotFound", - "The order {0} was not found.", - "Der Auftrag {0} wurde nicht gefunden."); - - builder.AddOrUpdate("Order.CannotCancel", - "Cannot cancel order.", - "Der Auftrag kann nicht storniert werden."); - - builder.AddOrUpdate("Order.CannotMarkCompleted", - "Cannot mark order as completed.", - "Der Auftrag kann nicht als abgeschlossen markiert werden."); - - builder.AddOrUpdate("Order.CannotCapture", - "Cannot capture order.", - "Der Auftrag kann nicht gebucht werden."); - - builder.AddOrUpdate("Order.CannotMarkPaid", - "Cannot mark order as paid.", - "Der Auftrag kann nicht als bezahlt markiert werden."); - - builder.AddOrUpdate("Order.CannotRefund", - "Cannot do refund for order.", - "Eine Rckerstattung ist fr diesen Auftrag nicht mglich."); - - builder.AddOrUpdate("Order.CannotPartialRefund", - "Cannot do partial refund for order.", - "Eine Teilrckerstattung ist fr diesen Auftrag nicht mglich."); - - builder.AddOrUpdate("Order.CannotVoid", - "Cannot do void for order.", - "Eine Stornierung dieses Auftrages ist nicht mglich."); - - builder.AddOrUpdate("Shipment.AlreadyShipped", - "This shipment is already shipped.", - "Diese Sendung wird bereits ausgeliefert."); - - builder.AddOrUpdate("Shipment.AlreadyDelivered", - "This shipment is already delivered.", - "Diese Sendung wird bereits zugestellt."); - - builder.AddOrUpdate("Customer.DoesNotExist", - "The customer does not exist.", - "Der Kunde existiert nicht."); - - builder.AddOrUpdate("Checkout.AnonymousNotAllowed", - "An anonymous checkout is not allowed.", - "Ein anonymer Checkout ist nicht zulssig."); - - builder.AddOrUpdate("Common.Error.InvalidEmail", - "The email address is not valid.", - "Die E-Mail-Adresse ist ungltig."); - - builder.AddOrUpdate("Common.Error.NoActiveLanguage", - "No active language could be loaded.", - "Es wurde keine aktive Sprache gefunden."); - - builder.AddOrUpdate("Common.Error.NoEmailAccount", - "No email account could be loaded.", - "Es wurde kein E-Mail-Konto gefunden."); - - builder.AddOrUpdate("Admin.OrderNotice.RecurringPaymentCancellationError", - "Unable to cancel recurring payment for order {0}.", - "Es ist ein Fehler bei der Stornierung einer wiederkehrenden Zahlung fr Auftrag {0} aufgetreten."); - - builder.AddOrUpdate("Admin.OrderNotice.OrderRefundError", - "Unable to refund order {0}.", - "Es ist ein Fehler bei einer Rckerstattung zu Auftrag {0} aufgetreten."); - - builder.AddOrUpdate("Admin.OrderNotice.OrderPartiallyRefundError", - "Unable to partially refund order {0}.", - "Es ist ein Fehler bei einer Teilerstattung zu Auftrag {0} aufgetreten."); - - builder.AddOrUpdate("Admin.OrderNotice.OrderVoidError", - "Unable to void payment transaction of order {0}.", - "Es ist ein Fehler bei der Stornierung einer Zahlungstransaktion zu Auftrag {0} aufgetreten."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.SortFilterResultsByMatches", - "Sort filter results by number of matches", - "Filterergebnisse nach Trefferanzahl sortieren", - "Specifies to sort filter results by number of matches in descending order. If this option is deactivated then the result is sorted by the display order of the values.", - "Legt fest, das Filterergebnisse absteigend nach der Anzahl an bereinstimmungen sortiert werden. Ist diese Option deaktiviert, so wird in der fr die Werte festgelegten Reihenfolge sortiert."); - - builder.AddOrUpdate("Wishlist.IsDisabled", - "The wishlist is disabled.", - "Die Wunschliste ist deaktiviert."); - - builder.AddOrUpdate("ShoppingCart.IsDisabled", - "The shoping cart is disabled.", - "Der Warenkorb ist deaktiviert."); - - builder.AddOrUpdate("Products.NotFound", - "The product {0} was not found.", - "Das Produkt {0} wurde nicht gefunden."); - - builder.AddOrUpdate("Products.Variants.NotFound", - "The product variant {0} was not found.", - "Die Produktvariante {0} wurde nicht gefunden."); - - builder.AddOrUpdate("Reviews.NotFound", - "The product review {0} was not found.", - "Die Produktbewertung {0} wurde nicht gefunden."); - - builder.AddOrUpdate("Polls.AnswerNotFound", - "The poll answer {0} was not found.", - "Eine Umfrageantwort {0} wurde nicht gefunden."); - - builder.AddOrUpdate("Polls.NotAvailable", - "The poll is not available.", - "Die Umfrage ist nicht verfgbar."); - - builder.AddOrUpdate("Install.LanguageNotRegistered", - "The install language '{0}' is not registered.", - "Die Installationssprache '{0}' ist nicht registriert."); - - builder.AddOrUpdate("Admin.Catalog.Categories.DescriptionToggle", - "Show other description", - "Andere Beschreibung anzeigen"); - - builder.AddOrUpdate("Admin.Catalog.Categories.Fields.Description", - "Top description", - "Obere Beschreibung", - "Description of the category that is displayed above products on the category page.", - "Beschreibung der Warengruppe, die auf der Warengruppenseite oberhalb der Produkte angezeigt wird."); - - builder.AddOrUpdate("Common.CaptchaUnableToVerify", - "The API call to verify a CAPTCHA has failed.", - "Der API-Aufruf zur Prfung eines CAPTCHAs ist fehlgeschlagen."); - - builder.AddOrUpdate("Common.WrongCaptcha", - "Please confirm that you are not a \"robot\".", - "Bitte besttigen Sie, dass Sie kein \"Roboter\" sind."); - - builder.AddOrUpdate("DownloadableProducts.UserAgreementConfirmation", - "Yes, I agree to the user agreement for this product.", - "Ja, ich stimme der Nutzungsvereinbarung fr dieses Produkt zu."); - - builder.AddOrUpdate("DownloadableProducts.HasNoUserAgreement", - "The product has no user agreement.", - "Das Produkt besitzt keine Nutzungsvereinbarung."); - - builder.AddOrUpdate("Checkout.DownloadUserAgreement.PleaseAgree", - "Please agree to the user agreement for downloadable products.", - "Bitte stimmen Sie der Nutzungsvereinbarung fr herunterladbare Produkte zu."); - - - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.OrderConfirmationPage", - "Order confirmation page", - "Bestellabschlussseite"); - - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.ShowEsdRevocationWaiverBox", - "Show revocation waiver box for electronic services", - "Widerrufsverzichtbox fr elektronische Leistungen anzeigen", - "Specifies whether the customer must agree a revocation waiver for electronic services on the order confirmation page.", - "Legt fest, ob der Kunde auf der Bestellabschlussseite einem Widerrufsverzicht fr elektronische Leistungen zustimmen muss."); - - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.ShowCommentBox", - "Show comment box", - "Kommentarbox anzeigen", - "Specifies whether comment box is displayed on the order confirmation page.", - "Legt fest, ob der Kunde auf der Bestellabschlussseite einen Kommentar zu seiner Bestellung hinterlegen kann."); - - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.ShowConfirmOrderLegalHint", - "Show legal hints in order summary", - "Rechtliche Hinweise in der Warenkorbbersicht anzeigen", - "Specifies whether to show hints in order summary on the confirm order page. This text can be altered in the language resources.", - "Legt fest, ob rechtliche Hinweise in der Warenkorbbersicht auf der Bestellabschluseite angezeigt werden. Dieser Text kann in den Sprachresourcen gendert werden."); - - - builder.AddOrUpdate("Checkout.EsdRevocationWaiverConfirmation", - "Yes, I want access to the digital content immediately and know that my right of revocation expires with the access.", - "Ja, ich mchte sofort Zugang zu dem digitalen Inhalt und wei, dass mein Widerrufsrecht mit dem Zugang erlischt."); - - builder.AddOrUpdate("Checkout.EsdRevocationWaiverConfirmation.PleaseAgree", - "Please confirm that you would like access to the digital content immediately.", - "Bitte besttigen Sie, dass Sie sofort Zugang zu dem digitalen Inhalt wnschen."); - - - builder.AddOrUpdate("Admin.Configuration.Settings.DataExchange.MaxFileNameLength", - "Maximum length of file and folder names", - "Maximale Lnge von Datei- und Ordnernamen", - "Specifies the maximum length of file and folder names created during an import or export.", - "Legt die maximale Lnge von Datei- und Ordnernamen fest, die im Rahmen eines Imports\\Exports erzeugt wurden."); - - builder.AddOrUpdate("Admin.Configuration.Settings.DataExchange.ImageImportFolder", - "Image folder (relative path)", - "Bilderordner (relativer Pfad)", - "Specifies a relative path to a folder with images to be imported (e.g. Content\\Images).", - "Legt einen relativen Pfad zu einem Ordner mit zu importierenden Bildern fest (z.B. Inhalt\\Bilder)."); - - builder.AddOrUpdate("Admin.Configuration.Settings.DataExchange.ImageDownloadTimeout", - "Timeout for image download (minutes)", - "Zeitlimit fr Bilder-Download (Minuten)", - "Specifies the timeout for the image download in minutes.", - "Legt das Zeitlimit fr den Bilder-Download in Minuten fest."); - - builder.AddOrUpdate("Admin.System.Maintenance.SqlQuery.Succeeded", - "The SQL command was executed successfully.", - "Die SQL Anweisung wurde erfolgreich ausgefhrt."); - - builder.AddOrUpdate("Admin.DataExchange.Import.KeyFieldNames.Note", - "Please only use the ID field as a key field, if the data sourced from the same database to which it will be imported. Otherwise it is possible that the wrong records are updated.", - "Benutzen Sie das ID-Feld bitte nur dann als Schlsselfeld, wenn die Daten aus der derselben Datenbank stammen, in der sie importiert werden sollen. Ansonsten werden u.U. die falschen Datenstze aktualisiert."); - - builder.AddOrUpdate("Admin.Configuration.Settings.RewardPoints.RoundDownRewardPoints", - "Round down points", - "Punkte abrunden", - "Specifies whether to round down calculated points. Otherwise the bonus points will be rounded up.", - "Legt fest, ob bei der Punkteberechnung abgerundet werden soll. Ansonsten werden Bonuspunkte aufgerundet."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Order.GiftCards_Deactivated", - "Gift card deactivation order status", - "Geschenkgutschein wird deaktiviert, wenn Auftragsstatus..."); - - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.NewsLetterSubscription", - "Subscribe to newsletters", - "Abonnieren von Newslettern", - "Specifies if customers can subscribe to newsletters when ordering and if the checkbox is enabled by default.", - "Legt fest, ob Kunden bei einer Bestellung Newsletter abonnieren knnen und ob die Checkbox standardmig aktiviert ist."); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Orders.CheckoutNewsLetterSubscription.None", "Do not show", "Nicht anzeigen"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Orders.CheckoutNewsLetterSubscription.Deactivated", "Show deactivated", "Deaktiviert anzeigen"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Orders.CheckoutNewsLetterSubscription.Activated", "Show activated", "Aktiviert anzeigen"); - - builder.AddOrUpdate("Common.Options", "Options", "Optionen"); - - builder.AddOrUpdate("Checkout.SubscribeToNewsLetter", - "Subscribed to newsletter", - "Newsletter abonnieren"); - - builder.AddOrUpdate("Admin.OrderNotice.NewsLetterSubscriptionAdded", - "Subscribed to newsletter", - "Newsletter wurde abonniert"); - - builder.AddOrUpdate("Admin.OrderNotice.NewsLetterSubscriptionRemoved", - "Newsletter subscriber has been removed", - "Newsletter-Abonnent wurde entfernt"); - - builder.AddOrUpdate("Admin.Orders.Fields.CaptureTransactionID", - "Capture transaction ID", - "Transaktions-ID fr Buchung", - "Capture transaction identifier received from your payment gateway.", - "Vom Zahlungsanbieter erhaltene Transaktions-ID fr die Buchung."); - - builder.AddOrUpdate("Admin.Orders.Fields.AuthorizationTransactionID", - "Authorization transaction ID", - "Transaktions-ID fr Autorisierung", - "Authorization transaction identifier received from your payment gateway.", - "Vom Zahlungsanbieter erhaltene Transaktions-ID fr die Autorisierung."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.SearchDescriptions", - "Search product description", - "Produktbeschreibung durchsuchen", - "Specifies whether the product description should be included in the search.", - "Legt fest, ob die Produktbeschreibung in der Suche einbezogen werden soll."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.NumberOfPictures", - "Number of pictures", - "Anzahl der Bilder", - "Specifies the number of images per object to be exported.", - "Legt die Anzahl der zu exportierenden Bilder pro Objekt fest."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201601262000441_ImportFramework1.resx b/src/Libraries/SmartStore.Data/Migrations/201601262000441_ImportFramework1.resx deleted file mode 100644 index 4e7843d120..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201601262000441_ImportFramework1.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201603121451066_ThirdPartyEmailHandOver.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201603121451066_ThirdPartyEmailHandOver.Designer.cs deleted file mode 100644 index f7e948a531..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201603121451066_ThirdPartyEmailHandOver.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class ThirdPartyEmailHandOver : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ThirdPartyEmailHandOver)); - - string IMigrationMetadata.Id - { - get { return "201603121451066_ThirdPartyEmailHandOver"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201603121451066_ThirdPartyEmailHandOver.cs b/src/Libraries/SmartStore.Data/Migrations/201603121451066_ThirdPartyEmailHandOver.cs deleted file mode 100644 index 9b5970f35a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201603121451066_ThirdPartyEmailHandOver.cs +++ /dev/null @@ -1,235 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - using SmartStore.Core.Domain.Media; - using System.Linq; - - public partial class ThirdPartyEmailHandOver : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Order", "AcceptThirdPartyEmailHandOver", c => c.Boolean(nullable: false)); - } - - public override void Down() - { - DropColumn("dbo.Order", "AcceptThirdPartyEmailHandOver"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - // Some users have disabled the "TransientMediaClearTask" due to a bug. - // When the task is enabled again, it would delete media files, which are marked as transient - // but are permanent actually. To avoid this, we have to set IsTransient to false. - var transientPictures = context.Set().Where(x => x.IsTransient == true).ToList(); - transientPictures.Each(x => x.IsTransient = false); - - var transientDownloads = context.Set().Where(x => x.IsTransient == true).ToList(); - transientDownloads.Each(x => x.IsTransient = false); - - context.SaveChanges(); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Common.Ignore", "Ignore", "Ignorieren"); - builder.AddOrUpdate("Common.My", "My", "Mein"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Orders.CheckoutThirdPartyEmailHandOver.None", "Do not show", "Nicht anzeigen"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Orders.CheckoutThirdPartyEmailHandOver.Deactivated", "Show deactivated", "Deaktiviert anzeigen"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Orders.CheckoutThirdPartyEmailHandOver.Activated", "Show activated", "Aktiviert anzeigen"); - - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.ThirdPartyEmailHandOver", - "Consent for email transfer to third parties", - "Zustimmung zur E-Mail Weitergabe an Dritte", - "Specifies whether customers can agree to a transferring of their email address to third parties when ordering, and whether the checkbox is enabled by default during checkout.", - "Legt fest, ob Kunden bei einer Bestellung der Weitergabe ihrer E-Mail Adresse an Dritte zustimmen knnen und ob die Checkbox dafr standardmig aktiviert ist."); - - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.ThirdPartyEmailHandOverLabel", - "Text for email transfer consent", - "Text fr E-Mail Weitergabe", - "Specifies the text to be displayed to the customer. Please choose a specific reason, e.g. 'I agree to the transfer and storage of my email address for TrustedShops buyer protection.'", - "Legt den Text fr die Zustimmung zur Weitergabe der E-Mail Adresse an Dritte fest. Whlen Sie bitte einen konkreten Grund fr die Weitergabe, z.B. 'Mit der bermittlung und Speicherung meiner E-Mail-Adresse zur Abwicklung des Kuferschutzes durch Trusted Shops bin ich einverstanden.'"); - - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.ThirdPartyEmailHandOverLabel.Default", - "I agree to the transfer and storage of my email address by third parties.", - "Mit der bermittlung und Speicherung meiner E-Mail-Adresse durch dritte Parteien bin ich einverstanden."); - - builder.AddOrUpdate("Admin.Orders.Fields.AcceptThirdPartyEmailHandOver", - "Accepts transfer of email", - "Akzeptiert Weitergabe der E-Mail", - "Indicates whether the customer has agreed to a transfer of his email address to third parties.", - "Gibt an, ob der Kunde bei der Bestellung einer Weitergabe seiner E-Mail Adresse an Dritte zugestimmt hat oder nicht."); - - builder.AddOrUpdate("Admin.OrderNotice.OrderCaptureError", - "Unable to capture payment for order {0}.", - "Es ist ein Fehler bei der Zahlungsbuchung zu Auftrag {0} aufgetreten."); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDescriptionMerging.None", - "None", "Keine"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowManufacturerPicturesInProductDetail", - "Show manufacturer pictures", - "Bilder von Herstellern anzeigen", - "Specifies whether to show manufacturer pictures on product detail page.", - "Legt fest, ob Herstellerbilder auf der Produktdetailseite angezeigt werden sollen."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.HideManufacturerDefaultPictures", - "Hide default picture for manufacturers", - "Standardbild bei Herstellern ausblenden", - "Specifies whether to hide the default image for manufacturers. The default image is shown when no image is assigned to a manufacturer.", - "Legt fest, ob das Standardbild bei Herstellern ausgeblendet werden soll. Das Standardbild wird angezeigt, wenn dem Hersteller kein Bild zugeordnet ist."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Partition.Note", - "With the following settings you can partition the data to be exported. This includes
  • Skipping the first n records
  • The maximum number of records to be exported
  • The number of records per export file
  • Export data for each shop in a separate file
By default, all data of a store will be exported into one file.", - "Mit den folgenden Einstellungen lassen sich die zu exportierenden Daten aufteilen. Dazu zhlt
  • Das berspringen der ersten n Datenstze
  • Die maximale Anzahl zu exportierender Datenstze
  • Die Anzahl der Datenstze pro Exportdatei
  • Daten von jedem Shop in eine separate Datei exportieren
Standardmig werden alle Daten eines Shops in eine Datei exportiert."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Partition.Validate", - "Partitioning setting values must be greater than or equal to 0.", - "Einstellungen zur Aufteilung mssen grer oder gleich 0 sein."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.IsActiveSubscriber", - "Only active subscribers", - "Nur aktive Abonnenten", - "Filter by active or inactive newsletter subscribers.", - "Nach aktiven bzw. inaktiven Newsletter Abonnenten filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.IsActiveCustomer", - "Only active customers", - "Nur aktive Kunden", - "Filter by active or inactive customers.", - "Nach aktiven bzw. inaktiven Kunden filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.IsTaxExempt", - "Only tax exempt customers", - "Nur MwSt. befreite Kunden", - "Filter by tax exempt customers.", - "Nach MwSt. befreiten Kunden filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.BillingCountryIds", - "Billing countries", - "Rechnungslnder", - "Filter by billing countries.", - "Nach Rechnungslndern filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.ShippingCountryIds", - "Shipping countries", - "Versandlnder", - "Filter by shipping countries.", - "Nach Versandlndern filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.LastActivityFrom", - "Last activity from", - "Zuletzt aktiv von", - "Filter by date of last store activity.", - "Nach dem Datum der letzten Shop Aktivitt filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.LastActivityTo", - "Last active until", - "Zuletzt aktiv bis", - "Filter by date of last store activity.", - "Nach dem Datum der letzten Shop-Aktivitt filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.HasSpentAtLeastAmount", - "Has spent amount x", - "Hat Betrag x ausgegeben", - "Filter by spent amount.", - "Nach dem insgesamt ausgegebenen Betrag filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.HasPlacedAtLeastOrders", - "Has placed x orders", - "Hat x Bestellungen", - "Filter by number of placed orders.", - "Nach der Anzahl der gettigten Bestellungen filtern."); - - builder.AddOrUpdate("Admin.DataExchange.Export.SystemProfileNote", - "The following list contains system profiles, which are provided by plugins such as the Data Export Plugin. You can customize system profiles as desired, but cannot create new ones. These profiles also add additional action buttons. You will find these above data lists, such as the product or order list.", - "Die folgende Liste enthlt Systemprofile, die von Plugins wie bspw. dem Datenexporte Plugin bereitgestellt werden. Sie knnen Systemprofile nach Belieben anpassen, aber keine Neuen erstellen. Fr diese Profile stehen auerdem zustzliche Aktions-Buttons zur Verfgung. Sie finden diese ber den entsprechenden Listen, wie z.B. der Produkt- oder Auftragsliste."); - - builder.AddOrUpdate("Admin.DataExchange.AddNewProfile", - "New profile", - "Neues Profil"); - - builder.AddOrUpdate("Admin.DataExchange.Import.ProfileCreationNote", - "Please select the import object and upload an import file.", - "Whlen Sie bitte das zu importierende Objekt und laden Sie eine Importdatei hoch."); - - builder.Delete("Admin.DataExchange.Import.ProfileEntitySelectNote"); - - builder.AddOrUpdate("Admin.Configuration.Restriction.SaveBeforeEdit", - "You need to save before you can specify restrictions.", - "Sie mssen zunchst speichern, bevor Sie Einschrnkungen festlegen knnen."); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CustomerNumberMethod", - "Customer numbers", - "Kundennummern", - "Specifies whether to assign customer numbers and whether they should be created automatically.", - "Legt fest, ob Kundennummern vergeben werden und ob diese automatisch vergeben werden sollen."); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CustomerNumberVisibility", - "Customer number presentation", - "Darstellung der Kundennummer", - "Specifies the presentation and handling of the customer number to the customer.", - "Legt die Darstellung und Handhabung der Kundennummer gegenber dem Kunden fest."); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Customers.CustomerNumberMethod.Disabled", "Disabled", "Deaktiviert"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Customers.CustomerNumberMethod.Enabled", "Enabled", "Aktiviert"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Customers.CustomerNumberMethod.AutomaticallySet", "Automatically assigned", "Automatisch vergeben"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Customers.CustomerNumberVisibility.None", "Do not display", "Nicht anzeigen"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Customers.CustomerNumberVisibility.Display", "Display", "Anzeigen"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Customers.CustomerNumberVisibility.EditableIfEmpty", "Editable if empty", "Editierbar falls leer"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Customers.CustomerNumberVisibility.Editable", "Always editable", "Immer editierbar"); - - builder.AddOrUpdate("Admin.Common.FileInUse", - "The file is in use and cannot be opened.", - "Die Datei ist in Benutzung und kann daher nicht geffnet werden."); - - - builder.AddOrUpdate("Admin.DataExchange.Export.UserProfilesTitle", - "User profiles", - "Benutzerprofile"); - builder.AddOrUpdate("Admin.DataExchange.Export.SystemProfilesTitle", - "System profiles", - "Systemprofile"); - - builder.AddOrUpdate("Admin.DataExchange.Export.CompletedEmailAddresses", - "Email addresses to", - "E-Mail Adressen an", - "Specifies the email addresses where to send the notification message.", - "Legt die E-Mail Adressen fest, an die die Benachrichtigung geschickt werden soll."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.EmailAddresses", - "Email addresses to", - "E-Mail Adressen an", - "Specifies the email addresses where to send the data.", - "Legt die E-Mail Adressen fest, an die die Daten verschickt werden soll."); - - builder.AddOrUpdate("Admin.Common.FileNotFound", "File not found", "Datei nicht gefunden"); - - builder.AddOrUpdate("Admin.Common.EnterEmailAdress", - "Please enter an email address.", - "Bitte geben Sie eine E-Mail-Adresse ein."); - - builder.AddOrUpdate("Admin.Configuration.EmailAccounts.TestingEmail", - "Testing email functionality.", - "Test der E-Mail-Funktion."); - - builder.AddOrUpdate("Admin.Common.EmailSuccessfullySent", - "The email has been successfully sent.", - "Die E-Mail wurde erfolgreich versendet."); - - builder.AddOrUpdate("Admin.Common.SkipAndTakeGreaterThanOrEqualZero", - "Values for skip and limit must be greater than or equal to 0.", - "Werte fr berspringen und Begrenzen mssen grer oder gleich 0 sein."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201603121451066_ThirdPartyEmailHandOver.resx b/src/Libraries/SmartStore.Data/Migrations/201603121451066_ThirdPartyEmailHandOver.resx deleted file mode 100644 index 43d0084f04..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201603121451066_ThirdPartyEmailHandOver.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201605020640016_GtinMpnIndex.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201605020640016_GtinMpnIndex.Designer.cs deleted file mode 100644 index 90e9ba6504..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605020640016_GtinMpnIndex.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class GtinMpnIndex : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(GtinMpnIndex)); - - string IMigrationMetadata.Id - { - get { return "201605020640016_GtinMpnIndex"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201605020640016_GtinMpnIndex.cs b/src/Libraries/SmartStore.Data/Migrations/201605020640016_GtinMpnIndex.cs deleted file mode 100644 index 98f1936ecc..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605020640016_GtinMpnIndex.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Data.Entity.Migrations; - - public partial class GtinMpnIndex : DbMigration - { - public override void Up() - { - CreateIndex("dbo.Product", "ManufacturerPartNumber"); - CreateIndex("dbo.Product", "Gtin"); - } - - public override void Down() - { - DropIndex("dbo.Product", new[] { "Gtin" }); - DropIndex("dbo.Product", new[] { "ManufacturerPartNumber" }); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201605020640016_GtinMpnIndex.resx b/src/Libraries/SmartStore.Data/Migrations/201605020640016_GtinMpnIndex.resx deleted file mode 100644 index 15bcc90554..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605020640016_GtinMpnIndex.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201605061916117_SwapColumnMappingValues.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201605061916117_SwapColumnMappingValues.Designer.cs deleted file mode 100644 index 1fa094db33..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605061916117_SwapColumnMappingValues.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class SwapColumnMappingValues : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(SwapColumnMappingValues)); - - string IMigrationMetadata.Id - { - get { return "201605061916117_SwapColumnMappingValues"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201605061916117_SwapColumnMappingValues.cs b/src/Libraries/SmartStore.Data/Migrations/201605061916117_SwapColumnMappingValues.cs deleted file mode 100644 index f16431d45b..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605061916117_SwapColumnMappingValues.cs +++ /dev/null @@ -1,105 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Collections.Generic; - using System.Data.Entity.Migrations; - using System.Linq; - using Core.Domain; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; - using Setup; - - public partial class SwapColumnMappingValues : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - var importProfiles = context.Set().Where(x => x.ColumnMapping != null).ToList(); - - foreach (var profile in importProfiles) - { - var dic = new Dictionary>(); - var storeMapping = true; - - try - { - var json = JObject.Parse(profile.ColumnMapping); - - foreach (var kvp in json) - { - dynamic value = kvp.Value; - - var mappedName = (string)value.MappedName; - var property = (string)value.Property; - var defaultValue = (string)value.Default; - - if (mappedName.HasValue()) - { - // break migration because data is already migrated - storeMapping = false; - break; - } - else if (property.HasValue()) - { - if (!kvp.Key.IsCaseInsensitiveEqual(property) || defaultValue.HasValue()) - { - // swap value - dic.Add(property, new Dictionary - { - { "MappedName", kvp.Key }, - { "Default", defaultValue } - }); - } - else - { - // ignore because persisting not required anymore - } - } - else - { - // explicitly ignored property - dic.Add(property, new Dictionary - { - { "MappedName", property }, - { "Default", "[IGNOREPROPERTY]" } - }); - } - } - } - catch - { - storeMapping = true; - dic.Clear(); - } - - if (storeMapping) - { - if (dic.Any()) - profile.ColumnMapping = JsonConvert.SerializeObject(dic); - else - profile.ColumnMapping = null; - } - } - - context.SaveChanges(); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.Delete("Admin.DataExchange.ColumnMapping.Validate.MultipleMappedIgnored"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201605061916117_SwapColumnMappingValues.resx b/src/Libraries/SmartStore.Data/Migrations/201605061916117_SwapColumnMappingValues.resx deleted file mode 100644 index 15bcc90554..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605061916117_SwapColumnMappingValues.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201605111140288_ImportExtraData.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201605111140288_ImportExtraData.Designer.cs deleted file mode 100644 index 6cac518be6..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605111140288_ImportExtraData.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class ImportExtraData : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ImportExtraData)); - - string IMigrationMetadata.Id - { - get { return "201605111140288_ImportExtraData"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201605111140288_ImportExtraData.cs b/src/Libraries/SmartStore.Data/Migrations/201605111140288_ImportExtraData.cs deleted file mode 100644 index 191ace97ba..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605111140288_ImportExtraData.cs +++ /dev/null @@ -1,72 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Data.Entity.Migrations; - using Setup; - - public partial class ImportExtraData : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.ImportProfile", "ExtraData", c => c.String()); - } - - public override void Down() - { - DropColumn("dbo.ImportProfile", "ExtraData"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - context.MigrateSettings(x => - { - x.Add("MediaSettings.VariantValueThumbPictureSize", "20"); - x.Delete("MediaSettings.AutoCompleteSearchThumbPictureSize"); - }); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.DataExchange.Import.NumberOfPictures", - "Number of pictures", - "Anzahl der Bilder", - "Specifies the number of images per object to be imported.", - "Legt die Anzahl der zu importierenden Bilder pro Objekt fest."); - - builder.Update("Admin.Configuration.Settings.Catalog.DefaultPageSizeOptions") - .Value("en", "Number of displayed products per page"); - - builder.AddOrUpdate("Admin.Validation.ValueGreaterZero", - "The value must be greater than 0.", - "Der Wert muss grer 0 sein."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Order.OrderListPageSize", - "Number of displayed orders per page", - "Anzahl der Auftrge pro Seite", - "Specifies the number of displayed orders per page.", - "Legt die Anzahl der dargestellten Auftrge pro Seite fest."); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.RunningError", - "Error while running scheduled task \"{0}\"", - "Fehler beim Ausfhren der Aufgabe \"{0}\""); - - builder.AddOrUpdate("Admin.System.ScheduleTasks.Cancellation", - "The scheduled task \"{0}\" has been canceled", - "Die geplante Aufgabe \"{0}\" wurde abgebrochen"); - - builder.AddOrUpdate("Admin.Common.HttpStatus", - "HTTP status {0} ({1}).", - "HTTP-Status {0} ({1})."); - - builder.AddOrUpdate("Admin.System.Warnings.SitemapReachable.MethodNotAllowed", - "The reachability of the sitemap could not be validated.", - "Die Erreichbarkeit der Sitemap konnte nicht berprft werden."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201605111140288_ImportExtraData.resx b/src/Libraries/SmartStore.Data/Migrations/201605111140288_ImportExtraData.resx deleted file mode 100644 index 738509bbb4..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605111140288_ImportExtraData.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201605191216116_CheckoutAttributeMultiStore.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201605191216116_CheckoutAttributeMultiStore.Designer.cs deleted file mode 100644 index d4625babee..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605191216116_CheckoutAttributeMultiStore.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class CheckoutAttributeMultiStore : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(CheckoutAttributeMultiStore)); - - string IMigrationMetadata.Id - { - get { return "201605191216116_CheckoutAttributeMultiStore"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201605191216116_CheckoutAttributeMultiStore.cs b/src/Libraries/SmartStore.Data/Migrations/201605191216116_CheckoutAttributeMultiStore.cs deleted file mode 100644 index 1b6ba5a31f..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605191216116_CheckoutAttributeMultiStore.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Data.Entity.Migrations; - - public partial class CheckoutAttributeMultiStore : DbMigration - { - public override void Up() - { - AddColumn("dbo.CheckoutAttribute", "LimitedToStores", c => c.Boolean(nullable: false)); - } - - public override void Down() - { - DropColumn("dbo.CheckoutAttribute", "LimitedToStores"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201605191216116_CheckoutAttributeMultiStore.resx b/src/Libraries/SmartStore.Data/Migrations/201605191216116_CheckoutAttributeMultiStore.resx deleted file mode 100644 index d743254b0e..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605191216116_CheckoutAttributeMultiStore.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - H4sIAAAAAAAEAOy923LcuLIo+D4R8w8OP50zsY/ddq+OWLuje05IsmQrti9akmzvvV4UFAuSuMwiq3nRpSfmy+ZhPun8wgHAGwgk7iBZpa4Xu0QkEkAikUgkMhP/6//7/3/7n4/r9MU9Ksokz35/+ebVTy9foCzOV0l2+/vLurr5H39/+T//7//z//jteLV+fPGtg/uZwOGaWfn7y7uq2vz6+nUZ36F1VL5aJ3GRl/lN9SrO16+jVf767U8//fvrN29eI4ziJcb14sVv53VWJWtE/8B/HuVZjDZVHaWf8hVKy/Y7LrmgWF98jtao3EQx+v3lxToqqosqL9Crd1EVvXxxkCYR7sYFSm9evoiyLK+iCnfy168luqiKPLu92OAPUXr5tEEY7iZKS9R2/tcB3HQcP70l43g9VOxQxXVZ5WtLhG9+bgnzmq/uRN6XPeEw6Y4xiasnMmpKvt9fXuabJH75gm/p16O0IFAj0h5R+mKwJHtF65XNf//2ggP6t54p3r765dVPr376txdHdVrVBfo9Q3VVROm/vTirr9Mk/g/0dJn/QNnvWZ2mbE9xX3HZ6AP+dFbkG1RUT+fopu3/6erli9fjeq/5in01pk4zuNOs+vntyxefcePRdYp6RmAIQUf1HmWoiCq0OouqChUZwYEoKYXWubYunsoKrcnvrk3Mf3gdvXzxKXr8iLLb6u73l/jnyxcnySNadV/afnzNErzscKWqqJGuqdMsTusVOs0uEtxktOkaPMzzFEUZMEwNvvIsKsuHvFjhggrFePi+KDuEk9PiMqnS6Sl+mK+eJm/kE6oizNGEbOUsjb1DZVwkm0bgzNDePHP1MVnjZbG6zOmCLn05+RxlK1QclN+T1S2qfLE1WP6ZZ9PToWnqexFt8AZbYSEm9N2k/sVd/jCaN7+RH2LmRkUA+VIkeUGlslq+GwiPy+g28Fz89nrYfdV7cvR4hDeb27x4ctmZo8dXDIb95ixvS7Mt/+2nn4wm2ZK93iXlJo2evhCWt2FUY/65QFVFx2LNO1gk3CS3dUGhX7V49hzkzEFvp+Ggb1Fah9gpLJultNJT14lnP+ZxlCZ/olXXpgP3tjga5hUQ7tlY3lYzHXZTC6hYUXZbR7eWLALgIVOHMH3eF3m9mV9A9+0v1fRc6/tzdJ/cUgaSzOTLF+copQDlXbJp7CniyroawE+KfH2ep9CC7qGuLvK6iMn4ci3oZVRQ9dpNpvTd8hQlLZ69BJG3pdkI30y0XNqZaamu3ImnaB/X/qNGFyg/wjhUrQc4uJ2k0e3pGg/2JEmRhty/mI1Wc8StUt/zWOBDN11L5Z3/OdFVBVfKTCq6m8k4RyWVcaVcgHKQchkqA+xl40iMSqE7oeuunXGogyhoHM69hNXLOl/tqqP1MkeXrvWFjjCnJVldZ2l9m2SCENFVvczrGBI+4bQqf6EA6lZaEeIkFM5QsU5KsjjPUUyN+tYC4QLFNbHXveJx7QWBvK1Al0m2h3+Ti6y3v/wyRduDNXTylqWL94jyNirIsoK3dZ6Hr7gqwxJWQwprWAPutYhZVC4Gw7Z6+YpFtF+9zqt3ohV0UiB0gVl1Q9vzU54vo8fjR7TeeN96YUStIk7wcFOgrnoQV8m99+XTadlItYb5/XAFlY+mQomXDBMLJv7EYSjH3LSLHK9/e4FEqpX0370QkrcV6iyxpCrS+kRMfmEezOhA7sy/ZB/w+jijKr0ftoM0zR/e16is8LnkW155I/SwiQj3RHjdvcOM+bWKO0zkz8tkra17nK0ca/ramtyObUTSgMe0UYGo0o1KIQ1OKfZx7YOsfMDiTNqppvyqEaPjbjFFokjnyr1leIPMS5I3KPbyXCGjMJV2U5Z/rtfXqPhyQyRY6TeAKYy6zfLxW2LQ2oeWoE2fMLmoQUeh9HFQV+xiHHdWAgbKBhmst5xgEXtJCxZRMJnx4jAqUdsBQt1O0e186LSrsyGT9Ro12AKmmn2IbXWc4mWCCGJ+2O8S8rY6Gr2vk77V5rfttWeJ29XdQIa4gjzGs5xO3oqBV3rYhk7yYh1Vvht2h+0iSqvJu36wWifZUb5eMx7D00UylMFsTAc3N0ma4OXiS+0wFqd3KEUB4ig6w9VBHOc14MI9he0qDB99jMrqdHOwWuEjmiqcwdRhRCPxCkQk5ZcMPE9aO5uU1cf8NslcD6i4PuUiLKqlKJwVgpakkruJTvRfMWCDGiCWCrs/AGKrtx4maYqnuZ97VTd5WKCvYxB5hzk4217zmp6q2y3M1aDRiP3mYQQtWwoIqdh+N1Y9aqlBGIaQE1t/M6Xq8fEjUWii9KCu7ohKE1Mg1SlHVQOcBqMKwpyY1bKdIKwG1OuzvKzgsfXF4EDEUqHXAIhTF5tYT3kfabm8k+NiuJccjG036Zkf7iEtAjs3LhH6xRXbdukc4TMGZpE/qIkW7NoIBOwiDCF0VQJm3+WHqFid5UlWlR8SjITcuIP9FuAkvZfDAWNQANuOpLvqNNprBGBA/HEwcgHIA9qKwIu7nNY/wofYU6yUwX3noUDyS4EE2sshvcw6PUEd4pDW6zx71SLYn+nlbZ0kRVkFMt3q1ddZGtId+sO0gllsE2XTh28fkQNaIcbWaK/QKoS/3SdZLB5dNS0yAbCTDatdmm/maujt5A39M9kQXSlKNb78gW6V7/IMNZcfk7d1Ej3O1JLfeVt+kmnWELgVdhttDzNsgFyRsGnz5dZ7NbtIlZ3jIMUujgCkHR1D+RndO3JZ783vkgLFRD171eLYb8/ytjQb5kRhUdQ3pGwNH0EcTcpgzoMP+UdEiHhazhE0dXlXIGTa4M8BGsSCFhVJzDXm6EdTX/8LL7XL/FvkbeLdhtCpuRx2zvEpGM8Bxt1x7SdU3eUSE9IY5mqo3Ii3ZGwQ00MDpyttFevzLbsRSIY12iuA3REEEDsPQnkdC8f0cIkaaRFwU7ffhxRiSb0PTZXpZMZMUuHk0ulthml9dEdWwiRSiREsc8gjXpu0EGFu63us7HoomCNM++Utb0tmZAjrAjiVtnp9XaD7JNKIiTA3wtugBzmdcx33c2HxK3d9L/+t/oY0gBtXi2u/5hVrviWV76I38cYwDQbXea0cbDaY9fwXX1C/jq+b1SRGq/5KJrTHgOzySepa4LSsD9P8tnf4sl7SpHb5isGxHa64bWcu0eP0Xmxk8MSiHM7pt8MIshRD66sBcGAnqFxgJRDIm42arnjwEEGw3wvkbYWK5wuVitey2UB5lF0Mmy2ze6cm6na2Ft8R65bpqIznVXikl9Ht9Emkl4n/M8yTbWrq0De2bXmyw4wscMquiW4EmX0KtqI47Ya88VO5ZQYJH+2xQiGkQiG4XY8h/DL6FPmqjqtzfBpHDy7nuKiKcI9ejfBsh+LXdmlbNkh1Kw3hZlFSz6OKudVzI8oHlG5u6vS/UHmJOSUNguxz7oJLHi7XTD8cK8dy61UPyUTJQQBifBwIZR3cyWJpaZHhw/mxwmIsq3PFrWfJiKQ1xABQs2p+saAd/X2lz/68oJWGpDn/KLQCt0kzNdMEviGk7LekTDD0abZK7pNVHaXpk68esswF2MVdjjXhGfXEE9y/OdubNSyw41q03qQBIvrC5mPp8IS7h9wfaMJErtJjfheXRHeoYKf9Rnu6qNfBjvqBMHboqBLFDdq7j+GQ9h5OB/G25Zq++FFPv+iirL6JYqKCFHgfrbReumGafV8lqgUeppHT8n1yUx1FhfdlT4cnhLZCYqSSAn2p7jDFm+0kwFNgFOeg/GjimoMItZroxiQQEetGB6sV1wfvMZ2W7/KHLM0j/3vyFo/vzH3N0maBdwi9x/ipc4v/ciPgdMxA1KI5ftwkzRtK76InHqcZChooTlGEYPsPUXkRYa0JhZrVMTbLABXcG5JB5OC2QIhVHF07M0I2i9XktDwnmauLAA7RPaKjpzhFTad8ZRyL8QwVSe69+nqcdO+niD3Xyil1KT/OSJUA+TBCJrjF8jQhKy9KO4yNM2Bvv0Zxsia2qbMC/2ofSf77yxcXJNU63j8duh8s38lpeVx6k5N5PtCXcbCKQ+4ls3u8NDG6xufQ++RW5fGPf9RRa+vwEtnNcY1iPLiPElw3SRmsnu5hYE+dN6wkCzjyj/lDM+o2JYm382BeJTdP1CBwkhddHw8RPn35IT6M4h/0fVDyDrh3Gh9yGiQYTxta4hNIf+j11ijooR/PUrKu12EmqcEYPYbDSAmAVi2yBM2grOK1QDp4WD8d1lU1GBc81haB/p6Ud2lSVmGQtos/RZh5sVwf2W+cLz+xdk7RJbG3fWmEJPgO9CVdTdtAezA5otewE7VxscF4otRyIOY4e78Gcnnt4KHA4mr9HBwxdeas46xCRRmEv1qxNcKMJmaKVrDN2iY+fFwmqFmT3gIf76GorA4qLDqv6wod5evrJGuv9QIyIe4zlnk01RtxoU0Tf435O0pu76ZbiuNzTHD035PVhNg/TEubfqfxlSc9Ij9hEu7GIlxwRZg0iVvkJi4OMLlHxROpbGk96dRArIKJF606TbpE7e4T5Njd4/uEorIuEOmSQp0M8ipl3+bBmnWH9BWxPVryY4zajLR1tkoRvdHSWKDCmNeb9s5QQfI1hTJ8jJASaoTGySaaskYsT33ZbIn9Fgk6yXSlVy30ZT7YygfHGDmU4AyjALV1EWSlgcrD52oMKPjzjMplDkpjIEf/pGZW5QnKBLCrlg+k3lUyUJkTkhTelvjdyaQxeGl8rDqzmMJNjAeRDUCAc+w4e4mo7DsLqOg/BCYbAwjrOI6zhOJRDqGFUfSeg5B1nAdz7HN/CR7OrVDpc+fbX3oCvWnTl/bHFmX/4SqK8agryManqeU43ibcQSFQR3CiPGWKpeKUhXGUpt+iIsFKHXSOVHZfUU8xQQa1ZLNkUtVxqnjUVgO3GK3tEJ3HxerpSh4cA4pMyJZLuXAEZJ3IjLsbVnSWBxW7O4aQdpgDs+0ya8MButsXQ5whFAq8IEJ4+TR3qqJTMoxWqe1/7f2a5W1pvHwnetO8m5kQnhpfS3JWivFwA/ikdh0TMQY3anVN2Z7KrW9+lwgtbL2ryqO83uQZm9jN2TIlYJos40k3M9Q/kq4yXx4dMBHi2fqB6I7xl7nmKCYevNkKqrP8ACdsS0pg280JMDFMYZHQj0EwXhhbJFpULefL4yz7NuEawDggQPlQQGi/dDX9Y/LOEUYdiv1WLG9LsxVPFHBD4l8Ctawz4M8XK3GYV5hPZw3rSZNohteZ2mUUKqBnHzAT6JU6EtEXygGwtbRZ3lSRSKqL5E/k1/Yo9Ke8zPGOheKKx+3skdPi+TJ2HpssfRY9kJ5H2a3SZysMDwSOjQvvv7DFkUPbdgse7n5/m+/TbyKsaH1L0MOnSZ5vCH356HBk0V9AAucbYytsI6bhZHX9TVUHxCSp48rE5HQ8gF+Gkq4H1rr7J7RKoldt/b3irhBfDYkOkywqBpf19i+ThaTzM1zTJC7KLWySWHyUnyQpytSng58DBT9+Rg++cvu0vCyirExCREhNk+LS5eZWFCEwhPbmlpM3buJkhNM/58hevJimHpnqWDGfbuS0lTqvBsk1mmzRWK7gsD4Xur4qUiNZX6kxdsEgt2sMvv0qVq4bSq4A648n+3mdIqMHyQMlq9ygyW+t2peYAmXp74NSw6Ab3n/ABwjm+WZLi82Appfw02caZRqNngjz9A/9zNZwNxt6K1Dollt7heU8HT9iQYV1v2jGM/Lg/zDdDZL0dKy8b3Lbe0ZeuB7vm7CI9ruNvC3NRmAYV2B9RZ/mxQf0+C1K6/lbbxXTjznZc6aOqXDVgm3PWYPPtv9Ra8C1XznytgKdtkYe9r7IgiVuiEMGAMl9pRZOOam9qJk5w2SSYhZk3XQ974aSFbq8q9fXGfPqtyuyNnHo9tzgPMerF/lVSMcUDY8YRgZxta7YTUIRJSSvpo8YUtS1vTfhwpqmDYWSGFa0oVMBrUFMW0YDMO96ELOQbqpD6h0c6r0aIm9rIJp3/p6O6MEQUf3e+xG1EqsdjXuJw66hkS6NLFxGlmrXrYEc9lnJAbw1OUz7dSpvK5CGH8p37LQ8wVpPPeQ1XVAfk7/z13OoQdjwACyPG+5/SNaeCDjBPu8S6Kzrb8gdno089pcNLLa9fJhcPrDk/kvIiDG3Gobojyup4/RHf0jWobzCBPLDJ9mASf+DyJJAQmQvPbY5JGRWJ32GE/ZRDdsV1bCPQ9jBOIS9a/4zsg+rHOPGm7voFgeVC4oCCBTitAFnp/E/d8B49zqESvgEOYHAhG8kaJAU4401LED+vCYSqx12iGCsSVxe9QeC0HmkJIcEw+xTpgNT8YnDaNXoTEmgwmJJFyUqL8mpHqqz4FSh3ctNW4Hn/eZDGA/d7ZFg4RLkzbDU7VLpGUkNP7kIkkndrgTPQB+H6oIMdMExgfALLvb2Au+vIJpU3AtLpgWWHC+LfJatz0ntMrr1P5ZhJPul5by0QtlxdXt1sMyukn0UzP7qw5uK7Kn+PKtAvudleVtzPPM7y3u7C70lHPSdG9tXY9TYLB+J0bgpW74Jo/GH285HLA7KMrnN8Crq7krmeBxukZdQTkv67KH/3UsYc+Rw0vzPdRpAa9XIvHCPSFJb55e6+nJDkVK91/6WxvthDFVecs2bGaZVZYY14/oTOFlMl//dYbCuJlbTPOmqtjUp1E2rugxbm3jdWEscD8IjrJRFtNf95G1pzjETBXYaeVeEDOvc7hhSaG9us7nZ7SO+p7HwR7D92pO3FUhxatEEu9Agb7fhT8xD7JNpYadlm5HA24uE2ZeyqshTgm0rEyPZazQuD7sYbuLOCgvPcy7jAe2upnVsR6i+1DAYKo+YhkxZPdBDa1xJJa+WBsr6pk/5qJGEsPAF3Ez2u4jBLjJPnvKFrpImc8P11ZTajCih1SWKds/t8rYkdPP3V9uhRaTxgiU5ey7+qKMClee315MPqrHhrv6FV8IaTZhArnmtfYaG/OKYJzsSUtEQQq39mGQ/mAx1iySysdW2tkfPMtQ7zZQ1px1peHjQMQF4h2C/zyjWbUuj93XSt9r8to0jKPsHHr8W3sl9AFSTbSRdW1NlQScnddS8GDj5WAyynodp6PgRj6kMr69C29Szya9uLPr6N0k9NO8ex178ydsKpB3AyVltw7rcE/KGueC19O9wz6LBDFTzou8YFHrWl4UQlBYJWMjrWcvXh+U9VJkobU/xzQvyH1C6uanTDJWl/wleQBlMrLwgHixtV+gTNO1UtfvmS5MF3PTOdwV+j8p2gOHcGkYdVKn3AoGvuKqCQq+pIVPhddW8GBDv1eVRvnbM0U9qv2JQbAePtZ3Rxa6HMms0jaHH6a+lCKWdErVJub3DCDI6M7FXA+DA1lC5wMQgkDfLOuYp7vl1n5tYY4uJsts6uvW/sw20Bu0Dc4sQy9Gy2RMquuZuNVjihGUecG8ScDQSwjuJxMFmU+T3aNXiOwLcbG3DKPMqPNLAqTOCJpjY57RxG5l0j+1kqXSPpVviADXeYEeF4O46hrA9KzH7M+w14KQF8Lf+SlXBSQsgBq0ii9KDuroje1oTsHaOYsy3LqenLgPQKxXivcqgEEItBX1VhuM1k2x+UnsomeVmdDeJMhwnbJPt1duMLX8hvEy5b56mDuIYH1PnaRD/eZ+sUDHls2RayxgoOFWC5GqoOUhSowrCFmBWy+vEdZIX9fosL11MBLRu+apHsReh8rYu800ShzJLh3C3nf8wc3p2sFoV1AI6sX/ILmRlU8qXfkmBwkQsFSQHAGKrPVIUlG01XWQBoU4O5YpuMkD+8qztjJdAozj2Ek3eFqXS1kg0MlshPJUu6ut/oVglHf82TYDS52YhlH7d/5bgE5inASMqK9ITb5+vFk+oKe7wNXLXzhD1PDaERjxKd4RxMSxrORinPUHXwRYI7B39qepaA+C/BXhK/73gl7dFCfS+yOvNxHnsTPMmb42HPyjVKV97yuQwmwORoEHOIc9xi/hr5YQe1rBcml+xQJw0Z8pgac4C+EvzthNeIp3i2Mt1hZB59tL4Wa7xwPePapEB3yNZiQv+/kgqT5zERTu31pKiSaHT/LcXEvK2KIG0Sc4CXTiQtryjIYI6BHu/6J2kKSZWawj1NlbgJbhRoDMg7wVmizpYR8JgO4ueyG1yUGSfUHWXr6a8SZJwzFFdFCiLn45wzRkabRo7j6pBAVZ7j//deS1cRo/thhrC8PYt0udODChWLurrCovE9DSL00uCNpSrvaqx48d5GrskjeG5iYk701wjHDU6z0hbqTPPCNvGZh0ZbshiKds3NhKOeBdJiIoQpScITU1TectTE1je8tTUbvGr7j8DSrjJmbRb69O2QsXKZE1gxbvOVmhlmxDWupmHqFid5UlWld9RgTDb+fv0Ht2h+EdeDzHecx6lhcZnSaHaqR6hfNgPbm6SNAnwWmR/PthMTgPqVE0ONRj9UYGw/DrCvDXWnZxZCmMiGKafSNLlWdRwgTYTvkJa/kAr2ZRMOsKj+/u3szR0/LhJCmpI+JRnQ5rvmdr8LxRNT092fTXJXd+h6yTA+9Y9qoOY7psf8nQ1A3+IDc/EmEzDh1H2Y5YDMNfmLCKGbfP0aM7maLjKkCdjjiZPr6MZlIt2N6UKYB/COvW6r/GBoEj+pJKGpqiIYvJzUA1mb3qWJSNr/ByVTDLgCSX8hhjS5yW42OhMo72or3sdfd4hn9VFfBeVaE4D/lmUuIYOdhaQ5pg+/by0zZHzORY4m7q5LZndahzogevtvZ5kT8LniFy9MY/1Gl1bfIhIFqLWivM5r/oXzXyJRkJbNtXlXYL7F+HPNFrpQ5Stvtw7nKyk96jjKyDwPpWu0SsecLhThcoFNwwQyNbhT+mU2LQA+SOOSyRdc/ZC7AxPX8voFn1ISvL6AthBCPCqvSEeOiuHEu6qFaC2T3m+T27oKVE7CAjw6muJVt+T6k4YjB5aGJRBFdvB0VokEFTB3zROVOg/VyR0li936hmWGpI82n2xpGdDEdwzpty2Z+dohdAarVgJedyo90BHWSgtU2iBhcHoa1g/XYv7Lo9E7kpFso9LxAcvx8UuvdoYSWMBkpd2HIBE6PFQfmk5QUHo8IRNg6YEBebeG0beVn/H6mk+bqSarw3aS+nT7rbmO+xQQ7PJdh+FtaKGttUXWpkyhYJg1PGx5HJa6bC24OrlBmHbL3N5Wx29fFfoaAcNgax9EmCiW8uJBEpHTXNtd6ihUXS7j8K6VEPbChRO0ZpDaTcaEKzTeQkcfyGzFyzytjqjHCMVoByAZtIphBfexH4Qp2XXWfoObBTA1NUhPMrrzUwW83NMjA3JXz2LSbBvbZ7sORcoI+fYOUbWNDXPsD7hwxZNwTVxO+R5uY47qG1yaWPuRAYo+y3ZyOwkbOCmo4ElqXI8V7I64ogkoNJdWQbvtSUzPfQKb9knSDU5jGMieYeeBNHzzxZ9NYfXfLOkojnSp/YP7hua3B14jtHMMpKp3Zc720ajI05NsnFrU9NuER/aeX1nu7d1AuS4PS07ZMHU+I94gWTDu0WWRyAirZvH2sw5RBOoV2erFGE1K5reS6IR8Ec0CV8o/pYqSgdlmcfE5XnVKSvwrUdALUmm+em0Kn/7rsVFI3hdA1xEGiukiodKBtxn4kMlQqGia2chHirpLy499UqCYq9XytsKog028+Qtkew3YxrnmQ/OHlt5ptVIArOLfXC5ARf/TosNuFj3SmkN4NuvQQXfBYrjbwgeAsdhlEbZ8NaX+13QxKbbuQxoE0kGpSMb5O0CubWp4ASxoQQOf+MU2CHIZDQBr5s6Nx4HWdh51PQ+RHv5J28riA5yWUTxD0zxmbzIaQRw2NMd5Rnk6pv+DqXJPSqeHKvPrvsYO9fxi17ie2fr8id3BGUhrgYZIHZwBCB1AhxDeeWRYlGGEEp7o7t+RVI6BfGkc3lIL4y9XLsYQq4D2XqFV4vjeQWzY3aO/qiR0xsRrYFghGa/DhTrIES2smCLINRxKczl0zmKyjw7yYuGm+Y3g7T8i6jZO8gFgV8H1JFZZlPLP883nbdHFd3ctJEXU1+srNZJpo8b/ttPQV4bGcm2MEnrtih+z/VEzdBEcpaGIIBzJwjmedjMqXJ2hDchP38KHtN+Z1Ot/wA721lUtPqN5Q1hc5/nUJGd4hAelsEskGHcQpbJ34SXJCowDxG3iMkslmGUjl0QxQNzS04XYyl1xcKzZwwpGHDSkMMGjTEWGoL2EimQvt/OFlnV/a7QCnDNK4PRdznIpa+Qv819ExRQ7XdBeVsax2rTB4ptLbboscKf1pvpM5wQB+g/6qQI8GY5saER+JbhQ+E9LS+jx+NHxFDDFRVGdITZ4DYvnoJtxEd5VhV5GkLXCPfewWlJPb2QL8Eme51AEEI0NA62OsOwV4BIHMS1aR3BNm1c0ctcDbcSUKZTfHvBLm9LoNjEj2NNtFNQlfxg9S/MN6z1JLhy3tznzdDQaYkx4WWP4gBeqh4C1VxyzS+zeJXTWtg5Xi3EdUH4us3l5B3WLkG4l1rytniSPedEFvxYJYZLkIeuxMqsKdOsDmDcNKwYdJ2FW2D7laVg4qc4Rc3W7LkaCKIzVCS5d/4J6klD8Xm6Ll5UeNql7i8LnS0C5U48zZIqidJtlmRsF42k2NW4hlx0jQC18moMbWtBk+7/c4tlMfmZpTx3EsuU6fGnj/mtg0TGtW6JfxGDZS+N5W0xZNqmS5xwua23QzBxZAaXMgNzJcAPq1cBJsglFWzQ2wi2IegiAipX9jbMhbZAxhDihMDvRYq8rSYrNO7RQ16oElS/mcZQozEPTfRa73FGgC11LGM+9tsK91ug2vSe335E9yj1f+szL6pw0UCWrZ9g8Nly+Wz6ZK/e68xRn9B5w9yir8X0bhuYyVFRoGKOtmb0t9BwWkGczrLY138kzypyhohK1WtJf3PiIjclRqa8KJWWcMoKfembfQfDQeQ3b5G/ElDt5b+8LZZO3pmNQh2A6Az6m7U2SbylphrlShT4F1yXcihhlSpAvdbsWUGzvPQbr+uCHePZr1bFag3hLUt4KNRKHXI/eCuQ9fW/UKx0zP9lMi+o+TVW4hAVBXBaas3bh0/Ng10BEfYZK31xTiRDWTYG5ehYrFyN4QdBqgATJKkK1takxKZN0feehZb2fQDS9ZyB9LMxdU/lOkj/vm45/NwLf3lb7bHT2/0vzIWY4+2c3EiseGulZ48r4J0VoVA0rQoQfoFC8R1a1Sm6jMofDmxPqpWvWCR7ppe3pTFo/jKNQfMAs4vKvmParG5jzLPjxw3hSHXc6JswIYnNLYC0lb9vkXEYVH03X7LjovBXcj5jje+8drH+fIxoLGdROdY9zlaurdZxjPnEtV2WbNMx2Gn5IVnhhe07QfjPW7IqzhCW40JaUrO6enNwoEGf5w+ttO6HnWQR8Vg4yjPiGkCshZ8oOtrWeM3BHXAQqM37pGjlqsjFKVb4c6enPS6IBwRG96pHst/R5G01pPfV4hosy3iDd0cHol35juMU67NpQDWS7Zvksr7l0asxKHtdD0EAF/YgmJdq+bXwWYX5q77+fgE+5wV4kda387caLNorym5rvDXbzYD5G1WEMZLYJ4SVOGDl2Sse035RTb2o8IjeF3m9mZ+5ccvzNzp6qG8+27PDLYLx6ru8Q2v0LSoSgsrFOkLql69GaPbrTt4WJVQAzp0l6tBvNbwNc16bkvspbhfdjcY9N//tuX1yPrT1F1I6OE2l45VpKMsZOZ5rPMLCrOAPealMGRfI4vIxv83PkpisgO3JhfChWqeH+YpRgaZzhmucx7qEw59R9ZAXPyaf3bMiwYLpiS7ho9au5Z+viuI8fozv8KkAkZeqnFErkvJIG4ET9ZARXilrMRl7dMBi6h5tDfusQ+LM6EfGgUuGNIJSj2UM6peIqO+W9V76LilQTCKzXnVI9juqvC3t/do0BsRmYjTv8v4ySbJU88fw/u66nXzMCQJ/spqYYU/yYo0nnbYwbXu+WXp4oUUWWHm3aPqN5bzMbcdIZdtxtsIzG1zDsjvefIpo4jnPU06LZS+a5W09Azv1hEftwyj+cZrhFuIfnmEDR1EVpfntKwnGPYsGnWDwxj5Eztpg0dcLuPxLWA90/NfBCjq6tkLIJKeyxs7EXKcaUONxnIXIfPopyuqbiNoUiku0xgqF281RK0sgdHtBIm9rmf3pW4IecFfVIXiTtOyqs5ofoNsEpAF4mUe15+M9H8/Gx61wD8DGHKY9F++5eDYupooSedmgVYKcmXiMaM/D8rb6U8WbQKeTt8vcNJnv+EVellgHT/25jEe157Nt5TPzfbRJTvcJVXe5i3dpW798NUK0ZwzFhLKEatJC6ffVSbY3kh3nHWJMZP49cOLBf9SoRqtjzE/pQVVF8Z1j3tk2tKN8BSLc86S8LYZg3vH6uCd4EshV14ipiBGImxEB1FY0qpXCQF4lJwkQzaDZ0/F+yQf9mHh7eGnfE+Vw+5SsEWlzhpblIf2JJGgDXOhXDfhgUJRDCbZEBaitNZRBZdH3US3dEJivhiNha3jZRUf9DCKp9/JZIemKJC+8H6cj7DR/zDRpVetNEiSMOZ9/cJf5LEM7R5v0Kcj4jNqZZUxHR5M3cRjHk7ehz08USA8hTqTTu5CG9HS5wMLtski8nw/AaJxyMjabXxzntf9TJbgTK3J5F6VpPwsBkpv0OzOc4CSwjsCnrjfXKkwHxJJcN6KrMTA4kBGMSscZA3qpNuNuues2LJ69cqNZpqrjxS+TOHy2hlfdqcqwcX0AxPwjPMsLPkeGra9iiWcyDIGsz/ZlqUmVPlHLTXTNRZlai3mReO/QTYRXNd5V6VrAteyQWlzrrzdRcuvi5tbLqw7HXlbJ29JIi6kCc7U65kQNB9I5l3S39vJON3cTaxaRhztCvww5VPvV6LwaJ7KI4lNlo9o1SQPR9An/n9cZM1S+kJDHu8AxLF6nRekZ8Y3DVcRQ+61X7Z+1tY1l5Wf0UH5EZKl7Ouj3IhPGuJec8rZginkn+l/oKBdGnoS1fU0Y4vIJRSVm2uZRbK9I2BGm/XpRrBe1pjHRq10LPxp2Tkg3dUzsdI5vLXO/w8s3K912GGGl9Mj2i2W/WJ7TYjldb/Kiwq3eJE75uUb194tj2xbHSZ5izlmobcwRpM0wEcthMIVJoPQj2fh15DL6gfwwNNkEvmT+50y8SE4SlK7IXzMka+q44ijPbpLbuhh7C05lezh+xMKGdc+bMBlTWq+zPinCxK2doxLL09PsRuUjEqap9oUKjJy8UREuG9P4+Qzg6ni0xVyNwYfLYzmUcH2sAPV7COQpi92zYRDu7LI/vWJQ7TdVxbIOkhKj4Q91xtc302Q+MsrGMdH+TNPGPVYLae2U5pO8d8oPky7EU8tnI5paXZ+m3jNoY2QDFzZz3dkni90sV+ZeMY9EWL5DmzR/cozP4FHsJZq8rXZX8hVpyyzqQPr1adlMdhjD7j8HZd05o1jPuiFOIQbONYESdOp9acI0pM0lGySZbFVtLosoK9cJfR8rxFRAOMeRS1R0wWAOx+HGVhUoqN3kltTgqjrQ9ND2DB5tDTi4MFfAZJUk9+gTk3vSw/PLxX9MlVjpRhYCxW+qVz3wcCaSwQgnIimgn0Pto59tcVR/rzTI2/pr2hbJr5ZUkwsc03jtIGm18/sEE3bREPHTst2qusXr6bITyPDqZ4ISeQjzzhznO/wn2RbnMHR23NOYV2exeM5og/xyc1MiT9926vLlh+IwquK7i+RPTxv+GV7kzQMh2+MQR56NpO9B2+iPwQLQ8FntoIjvQjj1kFq1/clPqosNyhEcsuWlj/EBWlrFLZhxfaRjSY3rciiJKhnAuF6vWVVSk0HitDxJo9uyn1Xf6C1ZS8G0ULw6sJROn8hexizX8bR+QutrVHRCJ82vX76gjyL9/vIngQVGsEQ9wuorIln1UV/pjbpScyxtYQEnz2ZCVJOkOCx7T9DoekTe0oITdJEQ0XlGQ77MpukT7kiywd0lSfvJAEeV35hMwUFZ5nFCSdjtb/SxgUZQ462ZLs+r7o1Frv/H2epFs2yVtYZFPlhGoAp4X6AjwsTES/r3l/+XMHzTBnuByDTYDYFr5M14TLiRL9k7RPawFwdU5yJRUmUcrUSxiSm6Gn9pFw0J2cIaVInZI8kq8YCYZDGet9RmKBwSw3Mm6WTfHF+C9weUkSOizRya9IN9kVPsT98sR0wd7X57zTCrAQ9j/WrV9c2QgcEqUu5loa1ZF25q9/hWOY65mFY5bzvBsViXb3ch4BHoUsq16moQ5/I1bBhX0xrAvOM3sZUt2RArT1P9ih5BgaTIyT5tMfwRwp1aqmDXZ1id4BzsxoLEPT/IygdUXFE+UTEFAyfjswbElttYxAC/QQy8HbwGdHwmbgPmwqRlAr8Yr13g8zRx1Wpye15h3QvrYHGFVkfEJEMz4ci4RF8V4shxLRuuNGgP2gZooU5JsqEYPvUhaqvEB9SrDr200xA0SBcW0IosYAvmlFh+1SpHMMPaVc6RSfttlcUWcWvr1DIjBwexYQtiw4A8VnPW++nVK9Gw48RCkj7MwDwSmu4S24xFj26ax6slLAuNcQOMpJSS4dkJ7M+MTAXS2qT9UcXFGKx/lmu4jJFxgAgKsdbwiJg5bwGYAcYyY1qXsR8mKbn27RrQdnMMH5wKHHpzUoiry4Ea7Xvfw2twuv7yFVT0aGFdyCI0ozAobJ8CpRvFDBJLN19G+yHz7OEi8uowzW/JPYbewCNAQnzZAdkwpIh4p4w90u7PwILSOdkJow/p/VG+plfmPeOouIQHlnFgC2fLhAJ6gA9lDL4dfCgbwUysKJsfk+a7OsuZIJsnfM4S+u7nVfu/3BIJgoMGyRGklVESbgOyTcLIl+dK9RDmsFIq58nIWNlU2RrGbB8IM2Ua/j3eKRiTe8NXbGP7GXM8hAUYczxPRow5PL29zFm3fVpWKyt5QPBE08JYHWV4vOaSMZzhRNaJOU4gErruglR7l5RNFviDDZ4UktSxHY3q/kVVCWKqDt6GqZRtQGdkM8b1Ik270m0I01WZjix9C+ZiPwBNuh/n6I86KVDj4qvtNFTLhjJB9yvT/gF0BeD0k+gk1oxIN4OMMyKRST+6+kvrcVeYb5J7VDzRuH+NhjUCVuhwDsrbGDXEa2w/J9s+Vb2ZTwsD6WzEVUy9pTnrsM5WKTqt0Pqgqorkuq5QEzV1NZToGM4Eh4IPpdUdGNSoK/K9hxnzth4+bEY431qwYQEjy05fa3sWSDsUw7O0rJ7RQvDifK69HTxg68ayBF/Ds2jOy0sfvMUB2TPyfCzswrwhVQt5VxbhvR009LTt9+aG/sirYQKhgoLbXOw/0mYsjulbIyilo5iPS6XzZdKFrs7WcKmhTOThJ+bRHd7KZWNYgEF3UIiSl2ZuImp0LbQWcwgYYkwWzoYrQfxLWM9VHZmBr1R03gUresvUo2GMeEIjlqQVFVLQlee0TQL8p2pra+SidkTzCUjtfJp0ha23VZxtuKNDdWbi5x3e3VXjWIiBd3CXb9u/2KA4uUmaNCS9Gc2UgdW1FawMV3Rgak0PdpC9zUY0H6ObzfEusDw8ki/0rborCUfK2M8BFxhZqEBjFWjo0B0ohMdoWS6/VDyGO8PC8eANk97BGLZ0I1EyuJtsV5N3sS1H2S3jtQYv/eVXnP/YF9+xTPjGff01eJZehd22fBndKrLDiLCBPTZYzHIVDBcHTP7S4PwWFUmUVf20HOXr6ySjgFb+LKZ4FIRToHCgqXGHlnaQse3ofHLBdk5NerZNbjWq8Rke6AxQbAXH7/D5zmJY27E0dvCkZzCqf9QRzRD7NUu8VgWLZyuWxqhDwPoYDXzJzQDq6HZwPDSnJj1j620b77vuAA5iPwBDPycBvz1S/RmJ8m4oekudKYJlGFxpjhPGuGssv4T1zXa+LRbB1hjb+MHR1N5XMm61ZE0lMotVQvEEWCrq/siXjW7Vbu3qMRrwcivJiD8sVhWPYunFZXU0MNX/XaxoW6XJL6yuPwud/OoiIm85vMsfsjSPVlreGoMH5i4OOWQ27Po5OW/BfZmRu2BKG90GjGouxmGXCSrwqEmOVDa3uowJYHCIw3pIGx6ToLfMBE/5bOk9Wj2UGVhUPVUmHWDrbQGD6gwRAuQEbLmL5gVp72dlwt01IZyj+wQ9mNrBxtCKvbcBdNiBuRZ2iRWVI5hv24bnaOdY8gNKNzd1mpGcr2OmMuIgaXUt0zI1nflX3rqcoeEls2VsrR3Y3HyunWcLxm8qLsb+n9FDSWNMtclcBUiIqTsgGyYWEe9UMldp92fgSumcmLS9eDJX0vsu/2fPOCou4YFlHOiQzBVED/ChjMG3gw9lI5iJFWXzY9J8V2f5NPxmD7vB4MET0S/0gNvxY4WKLEoP6uqOkLdxseQelZPSxqg2RCpVRRvymXVgp7LXWw1phvVuNcc2tpHFBMBJXtRrmoNay+AiKMTNPZQN6wKorfg0iDVY3okZOEtO3N1ho8t8k8SGfDSGlTISBbPmJA75QqwE92IuXoIJvDvMdEX/fV/k9UbNSQyglI2sOYhFCrAP07et2zNl/Z+L8YD5MGl6qLUNQqzhGgMh0wx5CvHVYJYx35byHdD1eQXeaD6M+W4L1C+GX/RaEjPg8CoYg1zGfSBfbwkLSsYwqw4nzo9J87TCYqz4pViZP0kHAUOsSOFs2BBEbP4SXSD9TdWLGRhJRV2T5sc1F+Yo7XlgDBaQi3bT5gH3fTau28kTQ5ez/msZ3aIPCe5N8dQnwpcynrKW6tUDtoLL2xBwg4pnDLaPS42GMgPTGs2hST8Wf/YAHEkj+az4qVnGc3Fv0xrAuqDM3lK+HQ1iKaYdzZtJJ2iFZTd3et+l5lEOTrq9216g83h3hwUlPZ9rhxfnYpeYTec+J0BOwHC76DIn7f2sbLeDrnLvk5vqKCpWV2e4y3dRiVbfk+pu4CAZu2jqQWzZVbHhSl0zMrEIcX+4wArDXs3Ae4bTYMSJIIbFGXOkRPQspOMXsJaKKV21RnWDAHvKVsHyMtRoKDPytHIOTfrR1dkuHv7KLjE7Rh5VnY2bx63ujiJqPpilmBqcT5POjCouq7Z+zitkckYa4KQqKwGxVlkZvLvDmpKez6WsinOx/Wekc/SAl89ZjhGU3frR2t5VlSA2BOBtGFLZ3E5Z6U1GMgO3mszfTljwoYGYKQLamsZiDzz/2DTktmDEZm2Sit8lG+p+ribSGAxMBd5CWKX9HmPdne0F7vgM6xWeh+3fXLp+N2fmjlV0fDGCVjGdrS0ObgDKci3h6u1hQXAIM3IiOEcm7fcIlnUvIN3YGHuscNABnQ14zOY+K+HMbsqezKU7y2hsylObLfBcOUdVXWTn6I8amURGwOCwOsBA2mnOYBM7pjOrxjCLtqyap53Qk4dOG8o9WYXgQXshBaCVZpLTZo+iomLerFbezSjqwFrKGNxOU5E3Jb8/lD8pHW6zMOjZLGqIdipMejHUWlBF5kai3TakNSZnwt3cP7TDWIJfd3IXEUahc6uQVZicU3fR10I3iCXYdAc9L87yNP2WV3gUbYA1+XCQlQ+q53rldcB0RBy4VRoiRVMQtw6d3zqGNRjKDDxrMHdGbNvXWk5Jv0Pxj7zmcxYLn+VKuyECUIkH61qp9KatQ9qDMMat43bb4c3A+rbzbaRk8JUXtKbEdYFJcXsWPVEr42mWELy6ex1FLdi2Mq5gZ15RNWZ+sRHkZGbUmVnMJQYzYNQPpt7WcGF3hSewjSmPyBCY8KbT3blh8wC36pbG8kLZdnQLsL9uvk26xNddbDWQab3Hc/8xv71ifhOekS4ARR2I5xkQGz5XtQLZFLnObx1nG4xnBmY2mDuTXnBVt4J9tXY2CHgiht1Nw5pqBDPz5k6a04y4UMd9llznym1b8arBQoy2swxG84lc1NdlXCTNg+FmWdbAKtKUMSy0deoYuKmFUq8pOzMDo+mJvxNsh8d7H1XoEyqJU/7VSZGvtXynqAMnhGfB7dLAyxuan+0MejOHCVVPfJNesPW2hfkuc1vWG2pMynhMM4uzndiX+ZlOJLtJH4Zay50pbm6SFH9BVzqfGgESPE10QFZnCQHz7LmvpF2Y4yQgI6zR2XRhp8GDOOUyQZNBKU6lEDh8Lk3trycl6C2TqS9/VFCPY5bTqWqebPQ4Um85l48qL8jzWck6Kp6OH+O7KLtF53ipHdUFbiJ+kvt+6GqCTiCkkpXnh7YVkHXbvk8jCo37NAMbGs+CSV8UaLaDQekfdpw5qhKeJcfoF+ZFsDNzMyFIcAvuG9VfjO0Oo/jHaYb7Ev+wM7DoKkIsKKljw5TaZnfK8mw6mhl423Q+d8JyIxuMzs9TU29mnt5F50/DsSzI0DvoCvqPGtVodbyOkvSgqqL4jt67nySK45W8CsTEILQNCyuas33afGkO1g9lBubVT5+RITNZ8MTFDOGqGUiszkcsq6BhWEc2HTcBMOmoz1snZ3UjmZdHwfky6QJbbxs4lVlsLIvZiT2WMPMJW7ZVgJ0VK2aruFk9osVELzCnJn1hqi3G3qfrTV5UuG83dPuI79CqTtFlVP6Q8rW8CsTQI2gbRlY0AyWzYHs+jZVB36EZGFBPfJNOtPWS7JbUXIz5jh+tmU9eBX4+1JH5FM0sw3z6Ds3AfHri7xzz4YbSvHFV7thEzRNiBTnjDbD2vAe0A+mhKgZffuvWDWU2npXPmuER/2a+09IxrlM94ToVroGKbtWso6L6cv0vFFekCD1iRoipKS3KsryiWH79WqKjtCD8Uf7+sipqkZkJ6gtUsa9qlS9fNN8ZvmofMRN4lKsePR5FFbrNiwSBWPryJy0u/IuEN0Jo2iItio95HKXJn2jVziDcKR5K37WPUXZbR7cwtrbMrHPooipoDGdJmU/aPQ5Oi/wMFeukLJPuvWUIMQ+jRTp+9lxEOL4b1/UwT1OwV/i7UeUmbFWGooseNhySajhaJK0vBUiT3v1E1xFyppWsmqbMYMW0SWU+oeouB6d8DKFHiKUIwqviHotWsGcjAGNiU3GVVSqatyBalIdpfkseEYRwdWV6bmoMtiArdZZzDYrukRoIx/AulY4+KtFpLDfPkriqCxBHW2RKEBWmEYQxeUg2qaRAa8nsA2B61ChN7lHxdJmswZ6y5aYDHxLkKMbOph2yRduHGZ8kaSURYpo6po0qmWoMY8BbDfynKKtvIjr1qr6zYFrUOpxWyNoOXGxQnNwkMdWFevopegxX0MtJsNoXeoMEik0FvGNj5s2YEu8yAnWvodQU0beoSKJsCK0/ytfXSRbJiKOvpW34H3VEv3zNElDOsOWuo7DoumkTJritkfKdockSLDrfJsTQSeH2jg6UwP39ne70kKACH+NgVaMv1KL5jB5KmezuyrRIjh+xjM2i9KCu7siZrVlEcmVaBa9trH8LGcLMPFBtgkZ6gmPfotYjimQYzHpB36aX9oKWahHRDAgQjjadhKHOwbyRAW+C8FOPGuzACxwwdvgRFUPsKoRm9JMtBOZdKxM05I0HKZrmpQ0NGjG3O0wvMAe8wcFHplMOSa0NkcgoNk6/rR0tkzwVHuYot622b+MMZXD/+ExyuuMGn/MGPHaIKYps0Up3HFnyJS1toVQTMm6SpBKxbMMEt95OMIRug6YCNqLeHBU17qnRNbketPYosFcmvREiOKWSf+z0ptVg2FgpWGcZx6XpqNZFzID0GkKQdKuRuecAV+Lo9kc7k6lcq2CCWTRovhYKNH2hfuNBGcJKllIk8DB6ve4OH+WpXnkNGw5HAAZWqRw2SrQ+61orFHWillhZer90k058iqjElfalLddbsWDnQ9CoJXMgtTheX6L1JpWsAgjO1HD1pELMwxgfQxU4ORADqU7AVgp73xhCP/AiL0tcMVWg5GH0A2+2E7kVdwRgcBgGPFTgUzHoaWSOXoNUf+Zi3LvAM9bIx07Lk+tNlNyCi6gr06+aZmNRLpgxiNHp9COq8GlRt9RhSIM+RyVevd9RcnsHknEEYIruXYK5oZT0lIfRIh05rUAYOSchnTB+ymKVLB6KDc7949th+KzP3+UbIVUMl7u159Ax18Lq68Or4fKRqaO4Rxwq8LfYbO4cRb3e1aEfsvRiU7goN22i83JgmxguYHk/hTGxTAnJXv/qqQhDa8YHVpLSj7+11lEPxj4x6fgb5Cvuhlgkn6aGfJDqihAZgStwBRE1+AFCcmP1J2aepkrWGwMohsLCgZRprt5V1BihmJqLhszVzeU/PHQWRN1zBlI2/N55QEMEFhVABpCSDiQY38tfYRmIZWGMFVHGLUCkikEt+ej0lSHaCS4GCvoZtAAtq2HE/mRl3ROuejcKgJIgoGJoEDxIL86DQkUuEOfEFOoyhyhow4PIR8BBQvRgXGUUpOARzUQEzptFTooxoH4c46n1JssYHUAcNdc5UKgP62U6KpIHgJIPRgSGCMP4ZykIA+ACqCIlsg9BDpM0ZR4WU1GFAzUYzrhGAPpwCGciUuvMNWQJUFBJgNWPiq+iotPgemZALgGxQgsMQa/OYU2pBYpA8oEIsBBpGBc6BU1EVBNrhaTBo3xNPbQHTz6YHgKcehw8uDfDgEgB+khJ7aI1jzzwrnpPPEB5hiEVii9YAVSleUdBlToNY4W0agm6AFTqTLB6KkG5H5Tj4ZI+BKISl9RBxBpEULe2eRUXCTAK+cmBguuLcUtUiWIe1bTs0rmCXB1sNmmCVpc520+RKEp4+ahU1SBiMd7FClopsUJbl3QKglBu4E8TuvXQNuPrKoWkWY9z2iXXNwy7RitIBlYwGCFULwDhQLQA7eBxhhLwV2M3b6l4H8NpxfAIXCHajWT6GBlEorEreyjaSF3I2dd/5SQzqq4dvAkWBYFVrvN6yhs1Ll/vo3CA8LPSdkKvm0irWBBgXNOI4pYk5lqYVoyKrVuR0YWAU5JuVqL1utUQ/iGlmQirHZZQRUExQ21Qinlq7YZvWM9kAqj5qPQM5kiuebiL9SBSHShAOPlgIHCIRFwckYI8IMZZjqOjlseBTVKGktfRMoC0qoLFzMmobQQgqRJ7ILrq1ygIbjdQ/Vr1I+SsGwIcL2ZASE1F7ZDV9RXElcbC6cmsaXNagqsCAK9kYXvAtaUDGsUFpD028MpTHQypugF16AB0+2XGFMHXiTqq03bxKLG5creawjMuM2VHjOdUwlQeZoTOo5jGvsqtCCyY/tzPQHvbEFhcchnV9D8UVRShuKZ2F2MUWgqYYlJQWh2QrJ8E4y7MZ+NRdUm/f5vU9qKKfiefck5mVaJUHRnHnTtNxwiFF1VYTAtNzKgLwOxwcfxTTZHDCnFfFg5rwYnOy3J917pSfTWu6zx4paI6BbWViikPMyH9aSTtlaTQYi7UeKzJpERnMUddSLH9RKl7IJ80LZd4KJ2G24Kl7DcV8GZ650Ki+uoiWm9SNKQQkVOHg9QPaVzBm0IcOkj3G5KkeNOnT4cyfj8PoI8EUj4guAJEHzZhi4JCEoQzxFQMLSu2eBHIZCiKbdyaLLNu1efoPkEPBjoPB6hdAWN4b6c6GOuMJPqA0s1NnWbEU3ZUoKWZvKbhcKUIwlJV3oxibcqacSB3l41J6fAqAslHJ8BC9GLyQykIJaKa2OGVNNg5kQ5pqmB6CHDqcfDg3lwEIgXoIyW1j8u9NshQAqnwMgQrBPC3nz2IUJWGTOl/b1ZRPmSj+hBFNYnWFFQ2a3Jin/0+O5uSugCUfFwiMEQ3NmecgkgAsjkoQvPM6UnCgWmGMYaWEqXLgKejCoduDrJcsenvJDRhYTQjYEAl1Ij0ZGCRADQYpfMLyRxtNkElZzQwRvPYjCUMTzS4YGIIFPUSGmxSRpXYYOBM1jozlhCig0En4xCYwA60oRkFDcLqQDj5YCBwiDZd0kkFXUBUE4fSNW2q5CkHoeu+Sooa02Au2Qll6Lwa0qrLneXhCvJhKeupnOX5NKAGnvNwGwrP+cko2eZTNSRjA205voZfJiRg0wBAPZiZXZcgPehI6cWDaNbPACldhfpzKo9pHhIorEcikEnnFVYja0LMYyvq8vBeneEe30UlWn1Pqjsmr65IGl0V+eA0NSGyMTmEFVTTIZaxU6hTPZQe+WpIcSynIVxBP1Cwnop+FpJJ3QZASukchaLkV3ZCjck5rmU53lHlKQk7bmhyuUeyXGtEPwOikVMDpFTitSm3dRKPwTQlCYC03Ep1VAkvH5KqGkQpSVZxBdGULUysx0Jta1eovpLdYB2XjSVe17lyTJ5FLdFSEnIQ8nGNAWUJsMDYVRWeKRcmm5b+akh2LyfCGFA/hhG8iiR6FQ1GCbn7yqjsfHTukpFpLQo8oO4MzMF7nad5XBMbFUZvEGikOQipEhBQBVgqjF9KUEodEOnEQnvovJ6FpLAGd0UGjGR5+zQzO/EPUGjCy1XgKhEirQXLJuHhDKV8kiOfKzxc6INqXcqBLQapWp2e9JtrjQoNK+wTUliLcSmsFZ4Um8nPJU/Tb3lF0y3T61L2yVTAuUUBrnA1kdfyd2NR4JbkaZWkfHXZEcB3a66Ap3KAHcK0rkK+G6IAiSx7nEe1n5i2By1y4I2hAHrL+OGdq9MsIa9qK45QqgoqhUNRD1ZmhGeDlPqMCv20p3nwWaQr8UkjPTGldc0HLkNhQmLTU79hiwDVtZPqknJ2eJzpSnioSaS5Clw+aEUtMAnt6HkqBTFVeCEdU3irKij5VPoRCGc2MJVW5ESpuXQhHU0MaaGjgXbsc45ZeFtM73oEQ2u8IcBKUhcL7oEXnasFjHxiyo1fULs6KfK1inQqcIW2Jq8Fh11w774p/ZnlqOcl3WVuQTgG2HhsQ53ARGMQT0yy/o29K4URRQRSCFgeFhTXzLN/KmEt4JrYYNK/7qeN4ZFAqjYeqAK8l6UmzrYShDO4NF+Ql/TwuThZR8XT8WN8F2W36BxP0/BWH3DI11ZSnMl1deGHHnJdal89XpCaw2uFYUlJ/zCm4RjacJCjSiGoNkY4Nbkkby0qdQttHflIdVXB5O3SlyMVJNU2NHWee0n7CqOdror9YBUmvKBEncegB77deHWSwNuJAlo+QHkliIayVycVFFQ0MHEwLdPy1fiRSSXtxrBGAxtV0dDNmFpjpACtuIc1J+K20VOfpkzHVrJlDZYEU7Ig2w4Y+i6fMgfijp6+vBq/JS1SVQEtH6a8EkRH/rFOBf0UiKFb/vE72gGC32wop4CWD1BeCY5tM6acAvFslBteNL06654ildENgNUNTqwip9noAVYt2QDMkBRUzoaCar+9brCQm6koyVDRl/32mszEOmo//PYag8RoU9VR+ilfobTsCrrXaYea7ZcXF5soJpcs/+Pi5YvHdZqVv7+8q6rNr69flxR1+WqdxEVe5jfVqzhfv45W+eu3P/3076/fvHm9bnC8jkdH5N+43vYtYeUZH/e5UvKU2gqdJEVZvYuq6Doq8XwcrdYC2AVWwKsv1/9CcUWv6B65Le+3ntRdg20OhiYySJxDAk0Mwh04+d2eU0hTVNd/Rfr0Cgx8Gmh4godFJp+OEDEzLqmHa17EURoVZ+2DqZ3kXeGR52m9zoa/ed6T1754Kiu0Jr/HWNjv5thOszitVwhrlAmuHW24ngmlFpjLs6gsH/BRHhdUiDzIyCGHAMzxd5XHSIev5pgukyrliNl+MsdxmK+exiiaL+YYPqEq+g/09NCYSFhM4xI7jO8Q86I3j3RUaIcXoBnz2RzXx2SNWWt1mXdHdBajUGiO9xxlK1QclN+TFZXPLFq+zBxrU+OfecYNnf1ui+17EW1aXwQI6ajYFvfFXf4AzJRQaIv3MCc3xPyC5sss1nKR5AWWptxa7r9aruXL6BZYzvSriOm315x453eQ18IWwm3o/IZktl1Fj8o3nCx2rR6TYDAz2btUtafZwcS9y3bXepeUmzR6ah0xWEzjkq2ZbfyBOBH5TXSLxGGSpTW3dYKp688YRfvJQlEiROAH0n/cGtb4mOPOJ3+iVdt9X3HA43MRCgY4puGcpg88juGrhWLRpkzicbHfLbARgiCshLU5NUYYuTIHrBKE9riAdTMq2B6u7zNaefG6JFWXCYtLq26rTOx6fFSnzVs3EFv3heZ4v2bJHzW6QDk5oI+xckXmOE/S6PZ0jftDzOri0IFiC9W+Sjl9nnxY/shxVl+nSXnHa8XM52es4DRS5qIqqKt0Se1tAfYxDqPrVqZFM82aD7sHdb0Xl9O4xB4jsGtwRTZmH+IbdZbWtwlncRiX2GC8zOtYWFfM561ZBWeoWCdlmQyJ5XxWAI/Ngfv1KLZ1twtr5hyeHmRxDV+3hoN0qSTNuUflwWXAOerq28o1JwVCXTgip3KMSiwMStHj8SNabzjjHPPZCle7fTcO+BzCUZk5Vur8zWHrvtlI2WZlNa6BY0HLlky9gpeS3HmaekprjMFFQoPVdkEfCSXj26sNiEn6omW0cGIh/5J9wGLwjCaNHnWQK7NYr2maP7ynYeiX+be84peuWDzHuUFuRcNsjhkcfa1i3pTGltjYeFYgPvb7fKe5BeVNFynqK3XgCFlD2SOrPI0EIi3yGLpvc0qez/X6GhVfbr41yY9GqMZFz/jMLkRC+zIiGyntyI5qFNMxZbMMINYcSsJPnIbG+IiLf3+5+W8y3b6duf/uod93d8sz0bprlsfCfrdQWjd9fM+oS8NnGwX4YLMp8nvRzjB8txhngfBetvqSCdvcuMTCTLtZSTCOS2bnUp45D9P8tn20wYEvlbUn4smmuUviajaeKrbAwhcID4Hks+Z7xX5ffJaU78WYCGt1/YkkddOoIKaHz/N6fTWjFxmH/W6BLaoEs0X3zRxL+9jOfyF8fKgi7qpEKLTG+zmXo+3Ltou7meeHfBldiWpSnm/al3D+UGjhyRWV7Wg4Ly7m++LzyDwA5DB1ytrT7iWicBmXLLc7dW8l8eNkv2/dESWMKdxDTZ5bP35fJxINuSmx0BtL8noRf2AevloYbpqQq5HNpvm0hNd2V+ckL9aRqBIIpfaYL6K0grE2JRYmv9U6yTpJNLb2jUqsLkXhi4lRgUUPu6QEPCFHBXNfSrxDKRLiBvqP9pcbfbwldL/RFy51SfkxwmcD+ETLFS15DiVd+ZjfJhloxBVL7TB3mYykyAWArdmrhpQdPnuVJBeJwVYlrTnNTkVDuUTWZz7bzbyIavg67x6FxfEmynhfge6jDR4sTgrBm5X5bHUNUyH87T7JYsCpmSu06KMQdXFkGXHRst0bfl/rvlpjegtiemuD6Z/JhhhaolR0aeSKLLSCuzxDzeUApxSwBRbrJ3qEsDGf55DyS+n1dA34urq3K8lFrZfVnEZWirLNVq7RW+GyfQwOuDAeimxxwu4yfJmFXe0h/4iqChWnJeBRLJZaYL4rEFLhBsqtrgRRkcQgZr7MQm7XNJL5Mv8WcWrnuGTX3Iyf2XV8x+ifUHWXe7ptjnG5RGtpEGyrjJLGGjvGGYfnztPbjCT2uiNJG/g7w3HR9nAmq9t5MiaLyoUv1fWnMqiGU6IDbMPX1wW6b5J3cBvmqGTXxPlCzN3diPrxdYfF8bYZrjoNN4f1wNyOxBVUVWxNmCWgRQ5FFjhbl4u27pFotYMhLGRBXukbkQLZ+HzfckRpvjwfD8x94pKQUYQ7d5Q/67JLBrivd7+en/k2noxEchffFdncNxV4ZDR6nEbcgy4uEhjzVr4lZXKdotNsldwnqzpKU07ugwCzhgPc0RxrknUvllrY2uo0lSIWCpe81+uYCK2xvibexgHFS4czdHXkyicMsd9gNLxFdacuCo86+IDaFQ9hr2Q1PmYX9RrWsJhiJ/VKgh6GsO89dTmD6QNCOI1B3ogUyMFGeBBzV1fjkuWVk4sfNddB8sFifURZfRPFJENFgXe0CroAkcGYt/K+4kPPmy82/gLD0/BjV4Hhu0V/2jqQ0sCX2fie/lEnBfpS3aGi18E4L1QIwrqFQd2A8Y/KLdZvjeUWXvgxUTQOVisOG7+WtdA2s9sl9eZnd/huYXRp6/Azy3638NbK0mZ5MnnHR35bQLnN+nvswpsk+GEIe2ocP26SghrD3kVPJUwZHsa+FeoMQjFAa0sOZaHdROVFhJUtBLMMUGwhTUc1hUt9odSq18TB7+C2QEjUTcVSO2/CvqLokwoU26zL/gk8fmEyBTbyq6109BSn6CPKbqs7XoJBEK4tnKEiyYV5lME4tEIVDIpGkMQQhJV/3F2yOc4ifP4ThOKoyAanPBkDX2blI5KQlRylXe3mykbwGZFALedheVoelwJt6ScbY2KfrJNnM67ISicjhufsHq9YXLm5dOSxS4FszJh5/OMfdURNN7wdc1RkfeFB6x/cR0kaXSepgF4O5dYSPAgYwmIekkyBXSy1OA3kD83YW9dJ4fIBKLc6JSU3T9TecZIXXf8OET6bCiclOaDFhUUU/6AphknmeyHEji+0PG/Ln+QRDt7mr/fI26SmEDy1ybpew/MOQ9i2ED3qWuAhLOmGVm3VhN+agGKrVUf2ncP66bCuKsGtQCi1xvw9Ke/SpKwU6HkQC8o0siZFmN3P8DlMtIzBEBaXBfg0RKsmMR9tNCqxsT8KqKxxfElXAJrhq7U19Ijc0EJ20KbAYg/aoDiJUqB34xI3jP113GWyBi7rlJBuLbYXdtr2eDgLDmvNi8dZhYoSYjQIwGrXI4JnhAVB7KMEtDoBG7anArQ6iV0mqFmHnGTkiqz2c1QOT90f5evrJKPnW2AcWmCrsWCZ2LzudbDZpAl/VgABzPF/R8ntHf8IQvvNgjrAOc/+ZPc9WfFI2k8W9ALG88F6PP0eoRYvCjCHtlSCRQq0pGdfUDeqQFFn25QDRDHW5B4VT2QSBfsZV2aOtdMjv2aJcJPNl9lo/iVqtzPAUCGWOmD+hKKyLhDpmwT7CMKhhYO16GYkFDrgJT+UuFkAC/x1tkoRvbwUrX1CoS3eM1SQkHPYSCQBcWyD0EDdRA/hPIq8MTVh+ascCQu2NW48/cbt5cfTYXFw5JFXncaTh/zr6e/e9hi8e+DKrCzsmFNiTCLBSYMrsu+pDDFUbo8dEj982fNxe2xvIMujvN7kmRgzBJVbub2DWN2wdbNAr/6p8i/j1zGEpcdBc8eGtQXI44At3BqpF+gJLo/3t3bv8S3iLydiGb7aKJ9hA4YO8wof1qVYgWIbM2cSCaZN+snGXNfMtcy7Dyrfu8mpcTWOsbJbMbHUAnNCnZAElMNnm17eoovkT96y2n91dAssL/MLfC6NKxi/Dta+/1+g6xCh0NLcfB5lQjjeqGBxF9eJDV+74wC4jWaW8Mak3TDc3ER1Wn1L0MMnQUsUCrdG4Wqlp2e0SIPEJVpEVnMaZatt7jDJIv7tGa7I5sZkjcTnQ4avFmIH5eT9tUzQ5UYFNh4unxF3599+svK6KaKsTASntVHBHAtv2XCqMOtkhMs9uGruVRMwy29IJW7nwqs7naQ1RTRRuCHMewxCD0ufEss0rNU1LTN/uGFjBnJep0iWFdAA3MoNALa4jQoszs5N/iBJugWx1EYnb10wYdRAsY0RrqzwSYFqs+yLZaITrhzOpbUzaUgEBOHUQvREGKRLSCNrhYNyaambAPBspwBzmyXwVW4QwMKw+1gVERFQnF13+Lw9Ipm5U/WUxQwmFyGsrL6ttkdcNy8+oEfgxVKuyHpbbx6KBff1rugZqwqtuGpuKsk1ZRDNc0DnrnyqcGy//tle/cIYhUJ7DwvYt8JFiYF1le25ZfQXHhPmF0jSChW9kx23h4qlFqbHZIUu7+r1dSbkteWKzHG2iR7G2PqPCxkDn7URb1uEes+DDUsGlvEc9hAiX4tymh1g6AAssuXvZEivbbqBCPn12QIHfFTdkSLtS20scGcFam6KxFjAUdG28Xmgy/wxNpc7fS2G7ddbZBeqblepp+UJFrn1EK/P85VQ/PxVbTbPRRCGZRG6M60ay/YzLtt/IIp1VLZnYCcGDse5niy7BK/6H0NCez2xVJB5Fslg9t5Falx7H6Dd8wHa+77sj83jcjcFjQbQ3iQxdVxmrDcBVDUYtbvSZopv+9U3eCSNVBFuDjWwlgK1MTEIMUJ8ma0zYudDIfFHHBU/Y6VRNVmezxooMLu8cmCFbpoVBffBjP8XiIJ6Dpw4BQ8G474dOtzsGje08pcm5Q+ws2M87ts4WHnpmV52Xr5FRRJlYP6KIPOlwO8+j1ZIJ9pBPDP1+ufQnSfXb4h8Rc8tY8iu5ho4KMvkNkOr3rrCx9sB5VZH7p3JdHFaQu9nD1+XOZsNutJ/rjlfBK7IQk5NkBWSnti+1NWXG4qCahxQVjoRZGt2P5Z1/PY5FpPDjqauvrRuMpeJfbtcBOVSo4374sVG/3lr+FuiKE2i0oXT43bRoNZWkhoRoHJz7CTtD/4k5Ehmv9swcJfWn+fg4bvDdnWUZ1WRp1CaFhnM8z/whl1zARbb7KssREaH5baq7RLc1M9sEunduPMHE+ESdJPKcb4PEhEMge0aN9MgjIs/6qhA5fntNRChwRZaHvMPVv+qy0p8z0IotDi008O1DLFYOo8b5XwqI10O0MY4KrC5e89+SN8wFAqnDHdYKr6rfzTHRxb2r8nYyz151WlkXNfe+zrhjSqjEgt/gbJ/9eZrwdkZ+DL7fgooPfFBuR34Mhv5mVWoyRrIy06mwCowBcjv4JLc4fgRt18KWgrz2UY27hNFiOWWkobJHe4jano0DrJGUXf7D8ZgILJD8LEq7Nw91DxcfJ/NDclCrIyXfukfc9phcWBkedVp+PhjlN3W5EElXkNivlsYYURnV2tHVxr9CAREWm02dcrtrc2XZVwstz1/bOOY27wQDT7s2xdZ4Gxfom3rHolXdDCExRFxeO5W3ogUaHkn3vDurc/bMX6hHYGolUUWpQd1dYcbbf2hzlFMaemzS6gwO+wcduim2U06LUOmfVgmU1kLkebtJ7vDAqHK6YrQ5CbhzRVQuT321uihawQAM2/rC5nYy/wH4pYh+90S20GMdfZShnNUaqUZ3ycrVMhyT0HlW7PaT/KiXp/lpedVd4/GYR0r6k6zaC/zTRLzKPqPSy1+8Y0P60d8zw5WK7wpc3sh83kfi2K7NChbBFgbFI/r4pBUnmZ10BZ5FP3HxVYHIQFkLR8VWBxRmuzO3Omk+2ihlHfSc6yF918t7gMSfBLmbgKaTzaH27IiDYuH2+G7PTbZTELl9thpFjQQb1Oyl1l2MivyF1aucmpWEfW+yOsNKKf6kl1wJlAIlnbv4SVL93kJIUWWJqg+jQr2AsuQZ/YhxBOpbVQEBFDbKB5XcSipPI1M3D4J9ry5eyrr6UKrpnXD91kwzXTarxVJvWmWCW0MiocaFVjiEx0wmM/LXYuGOXm1+cdbG4KQlE8otbn4atJaS1ADxZbzclFFVS3g5Yrs+wujFUstTIhNLnEYsVBojbfJUS61T8qA7DnuqC4KlMVPwOOJIIRNC029cyyUecxsiX2fL6PHdj+CzAtyKBtPPzACk/lsy9f1dZVXUXqaxSnuGMTePIRjC8ePuhZ6CPsWLkn9/vkH1VhgSM8WlWODIW1bbEWCYmw8hGMLirHwEI4t4Lri2oMhHOUTlvMJ0TGj9AQhkGQG4CHaBolpAB6ibZDMBuAWltSmCqeZDl8t+QPmOhdOg7Ohc0W2vSPLGOpf890c2zm6qbMVWkGx13yZDdaHqFid5UlWld9RgfDc8g44EhCLfe4OxT/yeghukJ7z1JAeLYpByxIQ+91b5s4FlVs489zcJGkCJHMcFTho5BuJRr6xdl8i5wIsdPHBF4uDI8wikIqhhrToP64FOEb3X+0wiSro8NUSEzBmtxF+isofaKWmpgzGrs9H9/dvxR43X+0wHT9ukoI6vXzKMz4BBgjgiv+/UARQmS934+B3SYHi6h26Tnh/ORmQjQmqr3YQ0x3kQ54C5igZlE9LEAfJoZxaOoyyH+JRCwRwxi8uVhDADf/pkRw1KXPC2r5mJsXclzthP72OePMoX2i/L1CdpPXXhHeIMYTFSquxzlgkf9JlSqMzohhK6KiC829NZFI1pH+L56gUsjHoYG2k44akwVHQE4bwaQEakRzKyhOi1/IUA1KA2TiZF/FdVCKpFRYEsDlXJbCf+KjA3uLXnLxge19XZo+VHLjwkt7UVbNHK610xpVsLnzCZELehWsZ9hh1jtZRkgnZZiUg5m18iEgkXXtY/5xXfTLJcTsKMAu5F8doU13eJbjHEf5M/YQ/RNnqy71wCFCDbs0VVHfI/1ri89qHpKz835MBULo8KmOGZpoLq7DP5lK5Cl6IWG5Pzkt+IeZ6n9zQM1tA5gJQujCXGZppmKtrm8fCfreQ2iVafU+qO5DJhEI7vMCro8znvwDjhuFVD/6cLdC4VQEZbgEeg5ND2XM/dO/Hl1nszICF2N4yfFp2PaCJJyMgkwkAYD92fBjeQOczqNxG24qTDUkDIOqxXJEDTiBKii+z0MVRRk4aorrNfLfFBnRwVGBhlURlKTxe0H+04aaB7FThhNK8CQDPWKr2EiOAy5Nj4Lui7oSuT6RBibvSUDS/PnkWMLtEuPwNJPMqTdgA3kaLpQ6YwbtmsdSGkrL+uvZV3k/XPoKX3A5X2d0ZqNlewUFLQFzbAMkgAbFQGbRXs75XslPkj+5SLQFZCLgim42qqypVewAAG+/cGGVDIisxTZpQbNF3LD+/A1nk2e8Wbpf0tWOyy3AOl8x3a/l6RKJsIQnbFGzX9owVEc8Teo/GdXuG6065PftvpbTP40CA3FqsUkfHvLt8Ar332eJnrCmyVuEwdiMRo4vZyAjLNJwa1r+8GQInldpvtlgOcUkmpNwaF9nZnaDTPPt9/rPczq0gcnPVpFPyWTUdFpeHvaRVt1uSXxZR/AMPDLoo5csssBLXR0hPGRVY3mYi+NqVL7O6m6TPwoBohcK/wOrxN1awmDxW0Zwmi75N4O69++5gAIHXprX9eGcyI5+jqi4y8pAD8k30M0LlpLYo60/ERoEC60IzUFhFKpzd6xxFZZ6d5EUzW7ytnSu0wUunHdGzO2/iEAqd8cq9R5SA9vMGp5AUS204Nbq5aQ6+HLMO3y2sPqt1koFuheMSG0ozyxcOV5SA7KY70WKbfk7/PMLCN8TGP8bmtPnrUGy35D6LilZ1EaNr2RJb+xyEcVxio+wMNIaupKHyxQ7tW/62YTcqzMKoIA9fCIm4YYgldtu9vAPCwbwEnoDOQeIZ4JhG5JF/OfttZOeFsM0vw1GLQHSdolZUwLjlUDb9vowejx+RQIZRgdWl5RFeO7d58STkIBwXOYi+LX0nT07bUI/k7mi6HEE6BHiFDcYZQnTN+vKa0LqgcUAAc8rH/YtmflJhMStWXBckGLQNnQh1GwdhdbuRM8Q0zbrjmxfP5WL5PnDAmuvCclsANptNrj/FKfqIslsh0pwtsMR3hookF9xwuCLLuy1am09vwhZYmeMCP2QTTnMKFb13miVVEqXgAufLnvE6pxOACz7mt35LnEHksLqVtadZ2EyT4PlDLF7K6gRHyNtbsXeZOQnmYAxKs7d4MSmMYSJjMI1ybp914sTxuGjO08RxRgwVXH/6j1vDQt5yzU2ezSjHcFMf0T1KBade5ruVNb6oQJ+tcYk5RvIiH4hwVGCxcW/g5142Ls+9hL0dwCMRHvvtP9ocaW5QUaBCwDUq2E1Lu5RLCnJzmsXcxQLz2Wa/pC8Xf4hKXmVnC7ZGRNGk6WyekQAJ3Fl0rnnc1Tgm2uuYNsV4LbF0qaUe6rWgYG9y7Zpqd1bQKJZW/vtx/BiXA7vrEGz3Jf9Jka9l3M2X2XCmDOe4xGptB3l7KsCLceU5ioB7vcjypq01Oxw+NSm2hJAtvtgJdx+hLEXPQDxjidHnIPU8BnZoXA6A8roT2SjgJwKcngYIZTiDDHlyM95SnmTxHVrVKbqMyh+eXmQMJhcPMmX1abjG/3B/gLlceCGcfrIRMHl2/LghjCqmXObKLIS/kI7XNhWvreFCsX1vvmTHRcEL/lGBxazhTey8FsUx+93CKhBRL9SiEvCNS+wwHmcrEF/33bJ/NX15GO4hU2bZR3FGmM82W/CHZLXiX0wevlp5Dd4SVj9DRQxcvHOF9nhB64pQaGF/yB++oUJctez3rZH0B3Ea4pH2Ho2TXVhadxoB37TN4xi+2mISNwz2u/0Z+zxPpZnpuzKbhXi6SoWbw+bb1rDh1yIIG/ZoHNhQUfevxYYXac3lOG2+LOLoJ3moQf1Aw1JJ91CGiiQO5I7MY3NJwqdFse2c/R/oqXkPc4Rp+GqFSUBiUx9IJWmdRtLWZLUQH1/eoTX6FhUJUer9mHiEyoGDNfWnYV/aKHdKaj7NeYj8CzFc66jtZWcgP10MDHC9bbUsCHealneZF2UKHuDZ7xbYiFOheGfLfDbH9UF8avyD9Tvj+W1+lsTkcQPgOp8tWjIM4kO1Tg/zlbA/st+tb2u75BafUfWQFz/Ai1sBxsr5HS+4J7pauqczxTA+GMa6lePH+A7rd4g+WqBuTAa6NaKt7ZSvv3k3Npe4DmnVbRVyqvdg3d6BFfNGOiSNJIv9Y44LhZeNRkW25/2TvFhHVZXwD0uIpfPFR0kXaH2dJuUdv3swn5cUrLvgqPMuJ0+EHGcrPKPcvHBFWyPCKJu0nwIoaS2Yq64mrb7tR9nARprd0OwPo/jHaYb7Ff8I56IlQerAUcaYpmGuYHkqAuZVCOx7v2tuEp+irL6J6DmhuERrvA/52vEgjA6saoZmW1W6bwl5u4qPARu+zqE2LHUCaGP8w3ATj83lQKBFseeireOiVpaHYSIOmZPLpwbDnoW2joXOEZmqVTt1vhHJLC6neGQ1gmn4p9eI3kg0pTdO2N5KsFm9zb1z/HRU5GV5gdI0CEfx2Fw2Ni2K58tVS21LTTaB5j1Zz02JReWyJanrTzT1bKOyR3ilQHZRgNJnXoTCrWGPf9SoRiv6gNlBVUXxnX8uDBClA7sY4pmGbZjGeURckZ0xIbpFxAwtsohQaMPivLZkqymdJKI/W/fNYn8UHrWxfc7GX3v8lKyR6EI9fN3GhRdsufktsvk24yQvEj7D5vDVLjBMDAezxSAy3fDVJpyMDyKzqy32ovtmcYmGNukT35H+ozUesUujAgsb4xFnWTyyqX0Yc4ZJ+mH+sDxy8891hH5Z8vrrAq/pS5orjfPv6D/b4QK6xny2uEqhu3YMvnDOl1n1cEWsrFGaPgmdZEq2RsizQ/WT8iwmBzGvrj7RxZz4XK71Q7ntwVqURKMCOx8m0YXJavfKCz7AhX6xuLIuUZEJAxq+2ih+ZSnmzBm+2saMXZT8fA2frcb3Dt1EdVphqbbC3JhEaSkMFgLZmnV7FK03UXLreQvaYXG6AZBV3Vaz7XPeZXc0mXEbGRfoknSMzOV+VIdhW1kb65vN/tlEpfMMABTv4rIJFxI0jfI3lcecu1IpwzhYq95AhhWg2AX3WzXutz64f1bj/lmOeyFR9xk9lB9RVaEinBMTjNNB8Jkimkj+ga2LuadUcPMq/XYJOGY8bO+GR98nFJV1gZq89L6bPoPKactX1t/WDX+KhKTnxKwveLgnVna5nbuAbuf/XYKbLL0FMo/NnSEVKPY8+cx58nS9yQvyXtJN4hs7O0LlwI2a+tvKiid5iqdUxMN+t7t7hPKTs99tffghfOMSW9NYgHRKPxIuSL75YnFJFP3gr4joF9tomS8Zf9Jhv5tjwxLnJEHpivzFnce4IntuOMqzm+S2LoCbcgmIxYw+VkUkXlYzn22COwmCLuSFi+kcFdlcv5V1Wp1mN8JN3vDdgu+aBG24DyRFm6C+CqVbI6gvnrI4TFDTgMglpklVe6Kbk2AhTRd5XcRIyKzBfF4qPIpGPD9WIrpRge1IxZTY7HfbGJtT4U2K4bMtrouqkASQdiW2GA/zPIXwNd9tNMssBs/Jo4KtEQvHj0Rpeoc2aR7g4SYem8vdqhbFNFKi1RuBmLIba9Ofv1IYSk06LWmsMseKw1dbA9A/ea2L+WxxyulnF1ItxdLdvg32zWHyoao2l0WUleuE5j+FaCaD8WtF34atMtocrkUXS77M9noEvktyv0iiNcHbpHHJ0tc4hB+Te/RJyBwxKrBaP4LbQvdty/asIDaHESrn3Wpvc6DkxUu3woJWXNmjQlsjndhLt5AC/Od9gkcnjVoAym022lZ8tazA7bdc4QL2Eu9zq2L28cQK2jjz2WqOiFwVjBTsd/sZb0wbookCKl/KrvDl5qZE3FbTfbO81Qfu8q18H6IqvrtI/uR4mPlsMQN4OdGEc2O691+X3j6P8vWGvmig0iKkQA7K80ER3wm3sWKpBeYURRmfL7T/OP+WfVCWeZxQI6IYPIeKVnVsEg9fsUmeMT0VsXKamkJsHAfPggP6wkpkW0VzV40RBNAWjPZ6ATnEsoTMfa+8O3wZFbcIOowbdZjFZdnZ316D/GDOMhd3Cf2ziSW8wrK1KhLyhvsRWeyNT77CVGlQWzBQjuo0gEIuOWAK9G158g3XQACuMeiyJ+c01JubaTqGvWJktSrVoAgt5hRsYLo3I/W0FbF6Tn+PMMDEA53zm+gWz2ITfZikRHntn/Q0mG2+imzKbeZ6jNOTphyykNPOofZjTOuOYXV7lZD5fHFafq7T9PeXN1Fa8uqmbvTezPMuKanGeHWw2aQJ8XZtEwVpNhV1PZ6NOuguCZEBO6ka8JyrHnUAblJ203PfaIk1tzwRh3TWpfqwYoihlowdzuCMHUZk7rBvNS/0nfTjhBbN3IzQNdv+fxndqo8nELgkhdQAY3IQERH7nj6cCGrcuSCzjTEtpkkYH0llNWR6hOURFEa/jTrkczlqnqOHqFid5UlWlR8S3A+8/3wt0ep7Ut21Xpeq/GDaymJGMKGKAV9oG/KcgTGuAHyi7/A2qp86MoQTON0h3ObsItQJcXjhkHryEY8tpMThcW8jA+nHr2ehztZKfLiiJEMFD9Ibc9sv/d9l96HNg0SugdNyqEfue9YRJUi5iWJqqlmhk6QoK8Jp11GJGpCXL7pLku7arbWs/ZEepc0b0h3ApyhLblBZXeY/UPb7y7c/vXn78gV9BpbE/qU3L188rtOs/DWm0xhlWV7Rof/+8q6qNr++fl3SFstX6yQu8jK/qV7F+fp1tMpfY1w/v37z5jVarV/z1Vu0Rlh++vcOS1muRhl6mEvjzj043yTxmKd++w8kMEPHJOfo5oWMn357zVf8DeBJ0vbvLxNCUrqc6WNmNGdkc3NKoBDt5csXhO3IFWDPeq+V6NkrzaaZ7D4q4ruo+G/r6PG/s/iqQnxxSehtFqf1Cp1mFwlGG206pNfkosmya6dl51KDCyoU09el3dEN/jkBxnmZVGkYijXRwwEQfUJV1IamlMEQjnLpBcIZjnZCMLI7d5yjDAuqg/J7siJbmwemBsM/8yzMGBt034to0z5dJOmbOa6Lu/xhNAfuozzMiRLkuS77fGSMmLPEQYdDzs32FGdvNdVSP3qELXU7K/shqf/yxafo8SPKbqu731/+7aefrJGOg9FMp9R4FrA2VAkBAs93Bt46zED7NKJmJZhpB13MdfB5pE9EJX+iXpV+JjM6xI4wjfCK6a+neLN5/P3l/0Mr/fri9D+vBHpcEd+SvM5W//aCrqVfX7x58f9ad4d9HDh4h/7m0iH6NNjwai3P+2F69jPpmacg63s6VSffBuuk84o3X64tHz2TVaqVu29c5qKl0VGdkqdpNHLdGv3XLPmjRhcob178UyG31QtP0uj2dI273nnkKtH/8pMt/vMq9dEQA6r5zPN87kgmVnGaNd0Eq52jErBg7ey689iO+pq9/PzJYfvp6DmJ4tUhD6iAnZYkRfJZWt8mmcfB77S8zOtYzvYexyLev/CZcKqRRczTwjZmuF9+sUbdn0f9EBvPtfyi8lnNs/e8nBQIdVZ+n43mMno8fkTrjZc9CiNpN6wmKg/cr0yESJcZzMei2ywGyj8eeNyWlI+Qy9P0mTD84ltwaMnap2MKYGwNoh4S++qX7ENOAttuvfj8IE3zh/c1Kiu8f3/LKy9kblorZBOKCnIViGi2ggYPSWpTJWRe7eh9nK0CYXI+I1jJgIOsfOCvzndWEpAB2UuBptaWSIDP9foaFV9uyNoofZh64iNd7+nVXuE8DwZi30G2Y6KhphcjnW5GrjFmRzmz89LBZlPk934bwTj9qVy+mZl/aOYwJ2TWbPrM+LNJrds0VFMrWkJR3iRksdvOQ58wRGkis2a5NnlvWKQyn4dQeE/yYh1VNhdEclwXUVqF7ufBap1kR/l6zdyb+/m3lEHOZAc3N0maYC73I53/iewdouHLIxRy+e0godsTX/cizkSHPmWX/VjoY1RWZvvMG3vkQXcI0tOP+W2ShVKpMT7KYVj2GqA06iMQu2WnOQgIZAqEyQghT0y77ogYzPtjvC2DoaU7uytTV1KjI4ApkwZDBu7BTpjIZW+UhXHsa0OlHbTsrqLXGsHoEXXkzWIHoxFX3acnR4yTmBdB2/X0JiSyt0GQ/TPZnOVlFaXQHa+bResuz1BzTA6C7yR6DIjNY/8zP9RAeRp2VnpO4rlA7Yxlu7V6GyzLIFcfD3nzRsppOYHDw+VdgZA5/p9t8eMlgook5nA7WVubdIKX+bfIS3te0PFhCWPtOH/JX2XtO/nrBvbwD8Ujp7cZpt7RHQk1m4RDWMXkmTDIdJrignvT9XWB7pMIYlDf4/cueIMdpvkt0QmfCYsufv1sFn1kZswwCd0y15taO6WXf2N3bdHiOmKNbi5S+HNehUY5ZMzw3Gm28yJaFYan2qy3LiLPt7MBvXbnOCaeQWl3dlbMtqMhLfjezhS4CzRYg8YyEKx+GL8lZYKhsYBP7pNV++arO29MohRf3NGHJsKutBMMHRpn8AuujnHat6P9pjqcJ1SHI9TpYi+lR5pH59eLFW30EEQBIc8bZrcX9TqQ9hEEX4fsEmuZKTdYz/6FQtnbew7ibYmZufhRB18iUVbfRDEJkyrwDlPBJmXze12HLryvEuA8OWGDp+X75KY6igqv02aHw39nP0d/1EmBvlR3qDgb5TJ0zaBA8Q1Kgva23F5Y1WRyqiQmSsPBasU16dX90/Jd/pCleeRnDGhx+E3N1yxtlm+Hzmtkn7ormy83Aj4nX8kWyfHjJmnehHwXPckwGpkMW4TU2YAi9OfuD1F5EZHk/CFmdYzJ4RKUq+9zC4oHRlzQDm4LhFitz2VcI0SX6DGUj9Q5iuui8LwB6pEcPcUpasSGn7xj8Z2hIsk9l2mPkW7+FK3XwjqlN2f9Cy4+sixUBBIWsjSfWZR22BqLfH8KR3GyjlKS6gv/KmnOrjd/x+dZEsKKt0mHrgdxsDstj0svEjLZXvyYBKs6xGiZ3eMlhpE1F1Kex6oqj3/8o45a04CHJG9OUxTfwX2U4LpJyuD0MIeDfXTavZIs2Hg/5g/NWFuvNr9pwNp/cvNET+AnedH17xDhA5UP2sMo/kHTKpHMfJ7+oeRwR/CdNjTEh4v+WOqlUtDtC89Msq7XISamwRc9hsJHB45WLaoEhVdFMZsT4MP66bCuqlwWnG26ZAj096S8S5Oy8kfYruUUYb7EgnlkLXGyv2J9m6JKYi8rzghB8K3jS7qatoH2mHFEbuOmauNig/FEqfNAjC5OmDb6S5TLZB3i+oPF3V6qBMLcGamOswoVpTcvttJrhBVNzECthJu1TXxeuExQs3a9ZD7ePFFZHVRVkVzXFTrK19dJRk88kzIr7n/3hEDZPiHgM4rvKLm9m275jo8pwdF/T1YTYv8wLW36XSm0zOkRhxU4oe4awniZTByosy2xiuDIk3tUPJFZtTe9jGv7GF46vfRrlvA3oAb9GNf26cdhVKJ2e/U2FPS4PqGorAtEeqdUle1zf/VNHKxZz5HQ20TfDPkxbsrBInlYZ6sUNVllAbOY71VAg/4MFacVWocw04wQEjKExHdxlzeWHyy1J3DE7Hb3Z+J5ofVHcMks1tHI3y7+tSTTGuPBed7I989VCdiCKw5dU+4SZIcTm7R3XOVRXm/yjA0ncDrJC1hC+dJ2k0Qvnuku6MeoAx6ydVvY1c0Dg/bZvLVIic+SLWIjbgnsH3OYV1gDC+5dRZ4oCb1QOrYL42K1d2QaOSqGubM5S6hrikNypK6ij4JP/Nwukj+RzwhGbl3lZX6Bj49xxWN2e0alwfFlfIsQKvMO1eHPo+xWY7N34JCA3ohhbVJb6PO1EzaLUAaabbZ93ER1Wn1L0MMnt4hwc//3RnQ9E2WoHc1hkkVDelxMtWv6wW3nwhNI8GjEkv3qRzlJu57ptKyfXVwOP6MHnxV8Wl4WUVYmvJ+RwVbYr4MrBonXWxbqleXdJX0wmW0syTNbUmDwh4lS1FX0itubTifbhijPTp1oT/vr55MkshuZg+m+r+k7czxpz+sUBX748GKDJjdQtQk64Chv212nc+cLgewclRXW26nuyeaIl2I1M3x1SM+kHuVOU8Ugjp4IOzQ+eaGRdxSWHJQ8sbdKvheFjx+rIiJvtk6pV7IXcM9EpGkNYr/Ya4FHeZoXH9Aj+HaIL/J282oelgl8vzfxxtiu/eZCitxGPRMeWlibai/4XDvBVffqSiBH5njK22X5ZdccIcuGxq0JIpaTFLN57/LlZVH6kKzQ5V29vs6YHIsuiNr48eWtW8/M+uQuknsGafjlmUjoYXyu8rGp6yUce9J6BgJ0aKh64YfrtMRCtblPsFlAtuz1zG5nF97vJfdiJqnB+pp+TzWUJ1jM1UOw7vyi15YD2SD1PReG4EKWovZ9GNf+y3DjM2TDSVxVgnsqMHTf+2qE9dXY+1doe7d7/hV794O/1gGQhvrdJDElZH/CeCZb1MKaEkzbRiY45B9RYvM7oBJB2Jz7PWMOGves7urc3zt9Sp1MRdBnsgDgIYbiva15QXIRRnkmLLIzs9AKlcvo9hlT3vIAZUu8b1GRRBkY//1MiDpBnkM4yeA8uRM9j2hTxvS7R8mbYHcOkjdKR+AcI/9sg2UPyjK5zfAK7A7nE2Tb2ceAc/ZL/6c/Fz7hDLfK/7kO8+ZesDRh9Ezzpa6+3FCUdJBTPEHD8sMz2UOncI1SmDXD+C5tkV8UtNTbIJDprlp51e6Z8KKvgOv+7+ly9SmiHp2DBLxiZ9kruIFvy7nXoY7YJO8F/sTk4fROCtska/bZs5jTR1YVeUow+d3JSJfpDHzyNnjEyXNbwpMEYAc0W3hduPmK6cZV+HlMtGSIzlIQpJWsMKQcX45jzZ6STvPi4o86KlB5fnsdup/N0XX1LzxNbGry8Gnw6CF8hoYcfd0m2WaCsLR+yxH7Thvy32k/JtmPQE9I2R/yzMNVuucCnodc7Ybzvk76huos+aNGCUV5kyDdqye2F9Bl/9DB18LL0RtA4/cybYsvZFA40UNRk5crlMc9GAnuhOz4EfetDOUNtA8mXyaYvM/e+0xE0tKeFcqwSTOHHXmIq4lD9ah2CGu0z1hCJp82ZmksSMpnFMv3nN40pnFkYVCR3HFhMAVxB9zO9I5/pZeft+NFYOBG/a/pl20srokeWWRRelBXd0RENt5M5yjGBHsmIrzblN23c08RfrxmwmK9tX4yW6fMAS8g2tasEBj7F8Jbl/kPFGaFUHQHcYzKMhxS/Oc9PjUXXpljjBfdSV7U6zPy1MzzWGGX+SaJ7ZdXW80v+HHpxW30tKLZGfrsYLXCe2f4rLS7FoRA1wfljmeyQOiA7Fm0rbbjC4TMo7+JuU2kqlaLHDxQPjeS2Otxz2++L4N/jMqK9MLTCt9ikUy5Izaa3MnzJLSbEug5CZ/3RV5vHCVQWzd4VIf/0xmBD1+f213Hay2HEChk7UFK1V6sTOKHt5viiS7LZyKjdkI8PDd2C2jFM+bchnjPg2npWOAQHTfLBsGnvs1f7kJssfNDmxm3PRs75Ovi6vvdMLaJX517IyDwuSSkNS9wWzXfE8dhhcDVJt4NiKrJ4Rs4qXPHnUd1UaAsfoLezXJE3CA8x0LG8PL1786r8jJ6bHcl/xP2t0gSb+guzPChvcJrIT3N4hR3dTJfvlFjx4/zNHZJGusTmc80wlGj84y0lQ3zjLBtbNaR4YYsFqt9YyMxhgV/QvaGKD1BaGqayluemsDylqemdos/TFJ2yieTM6JPimPzVqjomKwJrKnXWNtZTfxOwjl6iIrVWY43uPI7KhBmLT/XkaM7FP/I68G9OvSpUWggWFBspwRIfJRsnUxubpI08U6E12v9myBjpD405BhD3rMoEJYjR3j+x3qK07RjLKR2mIkgXQqmmArj80x1WP5AKxnpvHt6dH//Nhiy48dNUtCT06c8G/I8BMT7XygKM3aWL98lWLhV79D18MC6WxxJj+YgpvvBhzxdBZorEXlARmCQH0bZj2BHKQ5vsCXG4j09Co2yfXYnNNrT6yjQhtRKaKoUtP5/YdZEjZW9IvmzedmZ+M1H8Tip3STog7GbrIFzVDIx+p7SaEPyqIQnjog4YK/xubPXicJ3/awmdUsU2px6FiWhPHm7k2JznAlD0xYlOavgRbipG5PhJLauqbOlbu+FBntcOEfrKMmYTJYO+ZA+RCRSqj3cfs6rPv+fl3t3HKNNdXmX4J5G+DP1F/0QZasv9zZKrvWDhF9LfGj4kGA+eDZPMSz+IiGtat96W83P08t9FRqzzvvkhp4iniHrdEOzn7yhptf8fS3R6ntS3TmyEFfduyuj1+qC23HmZNZnwqCdnsTMstMDRjI8PjeRHan9b6Emth+ell1XaZa9yDP3Q4cMn/k2AU8i53iwGxJ0HEwF7DGGC0a5QBlR2EP1sEEXrnufUFkymca9M1x1M0KVPk+b8wzCr1/Yz0T69eMJ6iCzoLp2tmysfIgAc5LckgaZT32P2Tc0+bXlHKOZZSRTX7N256Vmv56aZOPWpqbdZHeB4e8Au8Q33gHcp2WHKohi9BGzeDakC/J6y5xI/e/T5uamb3o6PpcuFe1HJMIzDI/abftYR3kuGWUW3KApFXXsYCq9qIte3l29bLvyyJpNn5eVZ3F35oaoXl6xFMNhlEbZkN7HSX0rJ3fXCXkIC3vLwNo/rjytbVc2xhPz94Xukg29m30eK29BSX5ZRPGPJLsNeLlInemmVUroDSEKdYXZPZQRCN0cu1C3AJ6RFaMfklPgSFMzwP2UaDee3HpgoXtUdZGRjPLo2SRICRCHFWjiFtd/QtidzlFU5tlJXjS8EkZJbzkO0fOvwXnfBampb4NVABOXYM7znZ3o5oYcfMKgO1itk0ziYsYnNLNOazMSFCHCzbbFjcVig8ypD9FRVDyrTdJfWp5FRas4eFmeGruQ2zUrW9fnavV/t3dtvXHjSvqvDOZxsTjZzOIAi0XOArbjTAwkE4/tZLD7IijddFuIWuqV1El8gP3vS1E3XopXkbolLzNxs1isKn7itVhFd/L469XZx/+Zrz3CvcLAXycq6iD4/oLuhpk9VzfMCS9oNjLOQVfnlplegY3pEnM7ka1sTd9+xD543pQP8ffr74jS1IUNZnKFu/OQF88jHVemy19lbanxGSXDRwYRvvIt5UASlHOYf0UWYSNyuYw+P7ME+f2YLU5NdueifozW+qZv69aG187+6xE5eHfzXuQ9Hqv1RuBw9bxLEZto3KkHaja3qEhyuX+C2Rqivgwg3EZd7JlmMZhs0ofeDdmKkSVVEqeOlz9s7cW/jiAWxz+9yw8b+dIojYD1rEEPCgzWHczY9LnsEiFZ/7oRWDbvI9u8HspF7EuHRax2ZewSjPI6q4ktxlLjLt7OaIM1eYe+onRsMLu8qEwdT4w41tmOvD4nOJlE+v/NIdJ/wPHR7Mn1AflK6YiRiooCFb74zXLsaZQXsqivsLLduEPdJlnl27gUwqZwm3pP3kkkBDEdGGAjYxCtktenJrOvXaAkFNYbndExyadY9NwWxIG9G7I3Aszxt6JvivzoDkO29sicIO5i0HXHxfINlFHEY0ag8g7FIy9J2j385XMT3cYTs/455NI92fuodhsZBZwDT/uJf+3hRMj2WMrcG2b3hPbnFD3E5ZeN9LZ2J/p3h53oBf4e1DuPv7vkGMuz6++nGmKA8xy/JbdmT84uBKYms0WNj/9j2v8PT7t3g9b/wFPB3TmL2uqjsppjbJ0+ZNdFMW4Qb0VyTY0u08hhs1hngiFH3q6y9AzIv66z0dJgFv5leelomTNJcelhf0kEomHj7au8Kd8m+z0aFfwK/3moR41bVOyo9YSDs17HSXJSM1rbu/zbJ1TQw1uRf/va/SIZ+pvQbvXI4es8N91Uct5GwXHbmoZHEH+Gbqtxl6cuLhtM7VELrxu8cktDrJo+Fj8BNSGg7tPzwTtTLw5eDkHEzSNx1f2b7LbmwOkDa1iFJpGbb1Bgxt55MjHhxp1tWB9nGYPt4Qkd0ae4SGpWG0Ea0cmmO43SXVoOcCY8QYTw11rWXMOBhXDeCEiCTFjA9dv4SbBMra/DAS61cxt03TgWw2/p9K3etg/v8kN+m+zqKN3LcAt/Wx3Ty3xPzV/j7lebG8junfkfqPqWF198981tkRzj4pl8Ol2mNZe3RBCXke+aCMvr71jL7IBIWO+x8sHMLMQ0d75vuf8oI6HDtY4qUSDL2969OWSqwHa4eJfXDCyMYu5v8iYvjrjLqMjqntg7PzoBPs/z5zQpn+bw4fd68QVM0D7j5b/O6xj519ked+bYKdBuDfY+Jq9cNzIALf0UIth6+jLefbnJMP/dly06B3l4mT7v++fZvZCmuPR/H2fnx5gstYsHdMRj9maOnIIMB5+SOqeJmHBtNOPAj966h7s/+3i7fdwOez+7eLtdfIdSYtWmpzfSw/10/dLLauG3MVxCD8NFXpb3KE1/9qCfHjQfHOks21sxO62TPBGeMnyY8aseRYB3y/hkxn325xmd0Z7kObmoqnj3tKGH2ZRu9hsMpvI4z26sVnxA9ZEX3bNugcQ086nLVcCbxMWbpKk1xseNDnCPdfmcZHHx7HTYr/0iXZ5IvsdbQNjR04qxy5e4ke/vtkjyYmR4sPqZh3ef45qpwaG8vV9w7l3UhzyEoHfolD7bSWvBNoTEV0Ji7LEcL3c73yxN3vC4jND1vaifW1GfR/33eFh4KJKRUUYwEy/vVpslzM4x3ypbe9x0j7J9feQXp2nfaR49JGlJNzJPgFn22Gn27/bXmO02U782YHmbumR4l/c2L2T+5mY3byXuBUtlDRedZamNbOHCuPG6uS9T888EVPs1eozPaYUHN4JAXCvAd3cVH09xctjK3RH0WTi6TsKTnhszo5lu+lvt8PEm22ciP9rBssvGDC/cmkmweUMqBocfG7dk8cs4Hx72ivXSVB+Hr7WTyGc4ynoJnG7YGXvg9ZtHXv9uyct4IPkDfSvfofrD3aDHA6yc18AoksWo0WPUb2UqCNdsLaLWV4N9hzly/TR+EPC7HwTcUXyb7Td/TqbvUVyeC9RmzdrI96Gbb10C0IUNb3dX4yK0u2mou8YWQ6/x55KVGxpmf8JoUhjdHE9484/bfEw280YsCIbe5GmdUDgIa2z6mqUPL1kffMY/SPqSnMaI8BB/QWPqN57gH7JxK3mM4jcJSvf1X/5fQXWdfpVnj8nhXMTQ1bvTFu36e1XE3lJ4X+Xp+Zh1Tuk+ON6h8pxWN9mjcAnklqmtiXaEpavjHdkvAvn6IV4V3T9nux/Ar9/E2oMlosvnhstg8X/95ab8SLZR//nLA+57lzuQ/FzsEPjm21q8hpdaPHoYcHhMpXrbMNacL0V5TVUFqo6e5si7yO9VGF1/c9cVqDp+xUmaGB2EFjAisdPNqPhADY/7qvA1ojcML/Ncdp1iNI7jPgr8HOL6e73YfY1Oab6lLCDt6t3pJc2j0pdqvuW2h1XoTUneOu7G8GhOqf5nWM66ZQ/pADd+dW501erynNzkptWBr0FUAvuwBFV1eijirDwmJMjieKtCHEe5juGvqjl0sH2GYO5vYnQF5WJewt4oJrCr6D6ufmrAJl/Re+oRuuMdvpUXgOVc8/NgZe6Dlfpfrea+wWzuk289Ghf51wRbJaTX/03ZjlAtRMdc6Ho4MppvFw/iBuPF1+oY/1kPpb6OdzpsNAdH3s55PJ/KfHh8LNEopzJytz+GwWVc7Z7uk3+Omslv8UdIImMtwt+hDqJK4o7bLQHcfHXxqvcCsxt7z5uiODtbrJ/N33yhol2uNXE76SCjLhMuzy8SZ2CwHt0uUEc/a4MNW+7kRBZeoiP0OtmJw1UPcqRaZ9LGQ3TzPO6q/rbcsryyjIw7vWnQqb+BFi0nO57ByFR+gyqW3dzXDBOKqwFRlyLMoW87Fqa92jbl1KtMW26fy9iepMW3k2CoGaInXyclmfy6MBUuXdnxMP5A27ac+pJpzM6SVNVxXyUtvyWchqohe/PW/ZG7bWe2Ta21L2nxrU9o25oherLl3v7vIXa6nJT3jYocN+a2VprWlFIBOvmdZGgqh5wzx66FbSfO0WvgBcyey1/z3uZp+imv40vOmqnAJF/5yI8MK3qRld9cwrPRdUN0Qu003GW/3qr9W/0ekkqMlOroRdQwNMkIaBR2AXfCTYX4LJ6GPt9NzRDguEzzw48CDl99WdvsNi8dnk4PNQOuju7Q1wR9e4vS0+M5zRw3oqvoWEZh58VNV32UKH/FZWvxEAeHtKBb78254qv6mzuafvI23NTu+sPNjpO3QIPM/0blA7Zi6oHVH7klJxnWL8oy3yWkZ7trFBLuvHH2ukMl8UuLuhxHHPivs/0v9fJ1SILUSXSP0se/DT++P6dVckqTHRbhH7++/JX/ZD5kTX7UXy7IvVd9mlHu4r1oDqzGXioDIDkrD0jAyvYvQpP4M0ZF81j/Ks/KqoixucVvPsl2ySlOeXtwhIbDQ61pz5IveY1OKKuv5FV6m7RLZ64S2++b4XpAZ49XLyhQGWAt+SeJYElkWxHQaLFFlLGl24AYo9Mq8CXcctF77jK6Bz4Vqpv52kwvi4WTQE95h6mSjyUMAkjBJBMA0/xOV9K+2SXuAsD6EBcHxG8SB2BIgaDq+B8QpNYAmRugmpPSqcCZp+k6JudaUhZk5IfVT8FEjXXMuv2BaiQKbdtTAVHSyChI0P0cZo407UUPaGkVMZoFMflseOG8U/AWBm9ldhVqnXQS/UqN5cD0KF80CbpgXyO5WD1FEMxxJpgAfQa+VpKGTZyrFoJJ3YIM6HVJP/8QKLRBwWzwk3vpTYW7Kq4Q8djOdiiCvRQXhTFaXhZbbMnqMcWoY4Gl2aDUueh5BNG//e1vL4WeGzh1jpc0p/63tQMA9CpdeNcrQDv+G14cGOw/0QkhwQg3GzB616f+KYhuad3VAM+UJppkeMduSJTA40yn8ASoUrqxS5pUu9/NhCzNAtligNgsrmz6eAZYyZ83TI2qyySt32rCTzucMKWZvqzGvfWiwagt1vjzo4EEBsqqCFZioZNXKzQoSl+2mcmr08hm8poNV51b4DouODppGRmGH1d/0dGrYtLW/Jcd7ZOYhCTNjtr/y+882nLm0qH7bZqbD0ZgVhCuKMwNCGShMEji9DFpsaVdDJzAJ3NCZ0K9+KPACbLQcuA0+NTOs2LqXnZ6HJd0C+fuzS6z0Ol/XPuYAj9JlnX/zKNJ/x714oRNXuc9acU3uFPt6jL9OPw4yeAivAOHZAmMrV7lCcClfvcuaVP9Nnp2mOmuSW2Giw3DzKrL54CZIj7CbDBrp9dVjWXQkk0o28xIZrM8W+JA1iNMM4zNvxCfH18TLsVd4KWMOzEpurp/3KH/PScFqh/Hyo+0lzR2UQKD4jDlmxnDaK1sxrG5DxEi3PPJV1Q8P9Qh0aV9SxMxncoUWO0BLUZDX7iQqhoGG1bzGiXb3Ji4PGf7FNXxDC6qqkg+nyvUBC+NhhLdJEdRAh1Ml055AiXVTC2kQBxyapTZOChG5bqayDDUXg50W6yu5nDU9YNZ37rMDedMdy4IZl4Bpp0tf6JE3ehC8NGfg0iCUy7raIsTGkLWhg62eI1szrUWg6vVTGszgmr6wcru3mfmoarOS/8Yk7unYsKrP7pZhh1bsPYrQEYbIzgsw6mAQYVcibEdGnbQkYoElgeBmnX/+xmBrJulKywKe6uZ4RYAtulnOvvhbRkL8/sT2iWPyY4U9Ucd6wEbLD8kl4xyIwCUqLcGKMKifzjVf0YmetHP8vR4MANCqKe/Cl0NpOwIwzwFdIfQ2HfCKmVN2ocZLHRwNdc2NHB+yLF5NNhmH7BVGsyNeSr7gj5W4FJWEYPMoC82VbqR1QKlksUKYcZIagC4zJyLJH0KdOaPADHTDp8TZXDSmmmB9ikukjir+rH1Kj9+TjJCOLuDiEI2CFpK8i25lagUNZFiSR4nKvytZm++eKBOP++OxejcW3UDeP55jknQ/I9ZIscoQ0RjgS3Y5PAoN9CioUeLvTT8rXdMNIHkZke/NQ95HeT0R5ICIdDlUx9DLhaNkx47eoDlYs4aeSU+xem5B6lawzC4mBa5RF0TMVvCkBh2wlMYKDfaWuCZZzA3rGdfTk74HGOmheHqVn/RfXw8peh1/i1L85jPz0qdwXQEzPlL/+PC0QCrNz8SWOPPhoWHBBVYvzreszwLM+VW7JLphWAiyCzWS8/IQv26iTQugz4mjdHSLQBWy99SzgOiCbeNdviZe6PY5TxeR0C8TlpGhuHH1QfE61UxaWv2gHh9yFarXHuLitSpm2AnTFM2SUgrSC2baW7+iLDrzZW3RNBNtagaDby6wnzgu/5eoSKL04tz9VRzbJyYuOSNix7xVBowcqkJVz8CKtWzAeRsWHyTF+cjiWHsG3jyg4S+TYYT9evqcTHosh4QPOSnZDc1CkijIgzan7eBg0aZ9QAhIv/9vcjPJykKKBKh89qfJ5mISIOiCIGgIzNMQPAYNTTItYQhBJDbvsdC4mWGQce8L6cdcQj5AhYfMrHHdF1ICE2+erHs10nXL0Ss2UD0odgHyfOjWruQNhkm7S9rT+/TqGHSEGvwmXt/FZvmaUEz5TLXHDWzr3C7GJsfy/iA3iZYmuI5goODLjSWKi05KA9LsJloqoxaJu3OHk4VxBrwrdiPET8AxMzHlLnwRSScd+Ijd61LRlQvpShBwEvWybAzKLImwCzfrWMe2Ezo1mEHnLndOn5PHquruNhHt+di9xSXaP9XUj1JdHDvRo3/YScFw2z4MdxIMlXc5V4XI0yAXTE7RJi1DqyQa5cGGmsgyUF5Jlj0WCHAE9qs1z1dxWVh7SP9KSx0NbQkqE22RnLGGdOj8y6b/sgrtPx1di2lKEHz67oxNCiy/HX2HfqG0X6bYwZlNzit4nwSEJwRByxf/dklpNUqTjIhnHmdBDXL8eXAZbJhyBUrTLfM53d4/5Sc6mRUi57JOiHZgH79j+sGUK/H8qexTlRyYgTL7dprgZEjHDiwBWECKtp0rCcQGR9L9BXmvaStxTj5vaPXTFE/r+l/5Yw+42qlOhdZnRERBfA1DrQepkTmljZMyQbWwLQ+q1j99g94ph9VrLC5vrHFCnic9edc2+ZEkKu4qKhUfyHTUmpgwkvELUn4wu2kjxR0M2lzAekiBQitYpZaAsymnKuc0DX7dCVga/l38EsA1oQ38k64mvti/uoJ7b7kZz7kmfCzfAQTKJmhTCyd5m0zqJZatJBBzTT2DANIiYZGwx1fdcZt3+5cYI0Pt/EzOXq8yZKar6cTSNXpNNswt3/jC9d9rCjoY9Qm1ROLwUd3kaHWyFc/Bzs4AJVSihb2hsQNIH5BaXNhwtedDZ81DL7inniXHyLq33VHyk8aODrmxIEvmwSQVKsyaUKdW6hsFgZ3tFImzXEiLgJqq9h5zoeqKfebtnCafavpHz/hYkDy0NkGZFYDFfLM9/78udwVSZNqcuLwH3Tb4nNqtnT1sBB1WgVIsH5f4wq9R2XtwBm9KfLjdChhG+dOw9ii1eODU8ikRbozlgKQh/wnPBYCj6Er5lvVPj4mKf4FRROFZugbZBkNv679fnZQxWh3M7Pfx8Uu5QIb1lpoRwZCNHOEzV50bneTThK+UDRTIDj1+tisRuq6812mVXlRBydPjnHxfP199xRnB3SHv4irc4Gb2D0r4NUSsNDqfjQfZYgI7I1Y80sgTEB6hcFDo4dJQ4oOWAY0yB8/MTEDJhjLzwaGy3j35SbDsuy+BN3kBpl9JMIzIklpVr/YlWm2ih2zDHfL9/lYHugm9AAZg7m5HUH+PKMz2l8f4yS9qKp490Qu298kiqW2fWqoIJADJecSmIEUq884BetldOyTzLj6psSOGuF36uBpDBETHJ4pmGSAo4SXYSwQsuSmCo4uo+Zo+ZaALWoYk6oyrmN/iAHNFgazjWlUzdngd3M85UWFZXvEA2x0v3tC+3OKHuLyi/zhI03E7P2YAvNdJCMDw5ErCfOOUapzGLywOpk02EqYZIdaxhlzuMwPFUYGLu3K9qDC6rQ6qOCG0rzxgQV1GN+vwXL9sOIDAtGFYRZP1n3vBWyUXoabwMc5V+a3eZp+yiuM9/bKpe6ogSm5uu8Pv/DvVQ3Ghzzi62nPxdq6cLqxrsziFpdvnzkAEQoNXuGNOt/qNJgAZmrLW7U5B77qHy6y8pviKJUi4Xu1+3mSUWwUxnydZUnMtSBsDSLOmkP2Kj+SmdJwAKOqTD120U3zaWT73zc0YklNbdXcxDCCE+G6ZgIOmI3YEkmexiWrPMET46eTbb7LnDQ/WA5HVJWphyO6aebShv59Q8OR1NRWzU0Mo/rfYnIlrheFDFPDj9NcBdojydNwBJtnGfjpZJvR2ZZcNN6hrwn69halp8dzmtWRV0z3epL6k+/5ZHIAl98A0YaGMLMesWp7ThwyBTpXh5ZK2uezIcqv9wJkjAWCiam8DGQ5DWuzjmXmYN7OqLWioWoFTliOcFqfw5U9hqZ1s7rGdarn+qPCNVDRuRfme/QmKcrqdVzFn+NSvMepa92jqn+EQ3LTNj9Tndn+Xl9SHeN//Lr/nOO+jj+nQxVhwOEYx9+v4godSNgIkT1dCjZCE2iawv+qDxKBZvoSqIm+UMP+Xb6L0+SfaN/1ONAQQAM1CZDpGo+zw5k8ERPb7IvApvpSE/XQfVWQs9gyPxc7sDWQTKqkQKmR4hYVx6QsMca7Q25BApEEal2k0rTMvt0RWmWLoRZZCp2eeZpCupGfQX1IiQHX7sIC5N0Vylroyg1t1S9DpObqKVQW64kMm1W0p25I20L/lE9ooC+B+PeFOgVqvzVwIOxLQPG7Qt0A2MY5fY+qpxz6dHgCcDjkaHRtVnh4xsPYVzydQt8NVw62yJJoGhxOmIS2hiKomaFU9xV1CynxE+pKwO+nK9SwHzLyCvyHIqiBoVQHM/mEq55tjafa22RXnQuov/sS0ERdoVkPKFrhCBT9EbVE0fuY4Nq4f+pI00mBjvAAB1Kpeo0h1ImA0uQrKp4fkiOkPVsMNspQmFmbjiEsMzhNo7A5TWbbeB8O8E2SVvBEpq1iJJpQy0xSxcclUKhg2VEZ47Kt+D7Ozo8xgbTCOCyVSg6a0lgWjRD61lkKM83vT2iXPCY7sgWion3KbCCjV1kDrmNsF7j6h/btkTg5KsnBuVJZw0k6Y7lsJDLt04cY2q/RhYreIuVm7XyKiyTOhlijV/nxc5LFkn4xqaSQS1lPI++f55j88jFLoGmHLYZkYCncrGNuEs3U2/zf/jviK8oFMpPEGpe8nm3gXlOztOQWGGlr6BYG/cs3cVHQF4ELgr5Ud16ToOK2SMC1PFUGntUMxZpGBqcVoY2hCGqiLtVyv/6Op/MsTi/O1VN9otYMTdLzBDU5JIW6hkY6EmBLsoGhyqB2SXEZGW1iCK3sVI8uVDRkdsJHiGWNKPm3FCb8fy/y80nWSFuoaKml0LTUxm8WGml/h/i3RYZbCjZjrnRPwZKpNhUspUYKOG+vIAVMBkkBUxpKoWhZ3ZpZN0qGF6pM2p1G+xYqay3cSFMmbaQp1jQCZqcUmgOpoIZBQoMzJckWeCiSnSMZbXbZxHfSVuSdxlJoTcpkxQKMyZTDZmRItOrxSSgAFXkSWE2eSncMJCZGEM+DRBrwYEgks21ctoKSERqJYbZqkoYxB3pfQgnjQEJsKY6BHGYC6I+36aDH4hE3XQoec9ME5k01McRVzTUUmiYbIu0tFqSZVCMTTYAor/AyhKVRrEZYQu2GhA0RCWxBWALVypEj1fXhEI9Q7L2hDOy3oVg3PjLvSsWxkSkGx0WGQgvLVLoRoMpgKKaGS/yPhbwRqgxqhCrWraNQhvBmSzW4iyTgukqg0m0RMQ9E9q+fwatSrhzcKrIk2jumHLwFaH+H75Ryg3uGIYCaOC31RfCFYldqInp/6gFr0BdLFTE9NpFGPRIallKCN2cyYotT4gd0PKXwUAKT6U6NB0rD2zCFBCKJ6nbMuOXumFLesEChOu00bfYO1WR7+TUmTwCvLVganZGLvCwx81TeqkgCGlmg0hm5WftI77y5ctDALIn2hBYMjAIc1YJ08JktSGouiLp5baP6IzUmCJJ4hMYUg0dmDIX2iz2e4uQAjVtDEfyFdqW6kalZAakGJZ4CHI94IoNzz3eoqlChGZ5lhLIzUYhWa4K4xOPoXyg5PEF9ypXD6jMkZg2+TjC4S1htkUTRLEWlaZmLSSM0y5VDbXIkuln/OdspJn26FJzzaQLtYTcf2wI44OZJ4ENtnsqoZblVuXJ5m6ZW5V36NB57anITn8GoJjW/+2Ucp+SuXhI6vS/WHSrxEp34bBu4mrXayj3aBAqV51xLhPQNd0fTCj8JkUR1xh1dnE5pgvYPeUufWEghX44IFGYytOR6CToXJYN7bsPr7migMwZl72Jp5t1q7uXKRMo3v9cVYliINhFIZM6qLJXB1Nu/CAXn275UNsn2BAYeivKmmFKZn6JpU/KXYjKsAaQKyAHUNhLpxDBoW2iQegahdl6PBtd3qo7CjX2owL/aoHKBcX74WAS5jz1bU+Ff33Ax8Zd/wapvahr6rYHeLjB1MKNAbyoGiyifSdibQ5jj6XGsjO5bs4uWMasoV1X2soFoqnutoOQEje0gV+ULBe+mfIiLA6ocTNlWlBtAqrBKwWWaEE9jyu+RJQjxCdIvURqVoZcmbqo1z0mihiOsHE0yUkihBvsUpq8neeRiryK7PI+69TlqF/WJcjwxryxXEn7wQRRVP+NQcOE2LgAz2XOUIAaUjyLmlRUjCaCsRL2lmIx+LBP1fAHzgIQBTAE9AGpMoHzXY696uwdVKc2T+FeX20+TerLnX+4qcqaTK8oSeu+lCVXvN5b9UYNi8JQT61cN4IpBqbjkLIVhENQQ8kFQTuylH5dghsskraMj95wVRuBIw5nACEMjlO6Chgys5VoLtCE/Ae7FL8NA9ozX3gzdU03l0lgkCrE85p+bkprSp6QOy2TmJWPUv2gEVsswpWIJzD6fbNa/8AtMth74+rKpzhV5V787jNWrD0cAEdWA5F+M+v0jQEW/CzT+e5w/q28+a9ljYHs1VUf5ilnepJpcKf6dNVFK9oYarAmaRSwMax753G9SzVOfL8w83SWMFXb4SiGRA409QllIw9ighq/kf0idySzA03tw/aiuEBInQIwBhokqaIDzZBux0QGkUy1Lp1AGiFbQaKGIQuCKK3e1pS//IzpcgdQaRtW1KopBFmhl5XET1LwkMRNg1rq4Bx4M3JpEv5qTVgm2rHPtAC9msTLIBk3RL2WH1YHUEiJtiLWMJKgHbQiPKxleNT0eBNJgaJjIBEy4EcWuB6Tzv/OBApmQysoAJc49zwVboZuQQkBex7NaECqkjMDyIGbSfyQgebAPZR6TSALT6I2jqRjMTOpgMzQ3wwgyDndlimA5kkg/4E2aAxvFTYzeLmYGMeDKBh5S8JYEEPKNVnXjthBWcpvYVAv9AkTPUaWLk5w63DGr4B/LnLRSpUGMoXBSklNrlZJoA6ixDJMoAlaZHhYYs5jgHMEg2BdtUZtwXUFMrJ/ETWoH+0TXZk42ApqTTRkWcgNBwdqIKVRB2NZqYgeYTo9NE2uFN5FyOWlcV6u8UmuzdeMizUiiukTSZo1NquYziVFM+DKRcVTM4Wg37gshw4Fy8tEw7D1AdB8fTykaIiLKFecoFUsXLi5js2yRxFycUOU+JGPEOq6LKkso9R5B7k74QqhJUl0eRXKM+oq5SyTyP1uFV7WLm6l0mRKJ5CK7u0zx0T1JzeFHf56Bpk931BX0EB/hLqf7SDy/J5FoauA/OvkTnLlMpAreqvS3NKsYAk0mAW0JN6vAtPam68PTKu0EUIUwihBHl1SlfvWjLomBq9eXIwumMBPRd9AYjtTrqHJEB9WV6EvTaERmQvcOIoMhecXaYkW/PduyVHZrQzNa0Hl6k3yJNFvVF0vReRR94u+WxNc1eDYA0skFd30zwESVJrXgoNGuiqrGJo4ixLAUTj0oyHU0uNfJ/fjgCord22g/PiikNsNFGSLbk2lauxvapaEe3a+LMEcftltuA57Er+JCyPGhnqdNzyC/YisrEvnfyoZXtQv0Ht3i/dlTXKL9X0n1RLUgKq6r4k0dpi4fyp5UlYapdzcE890O/OVmgCt4UgSsKf3ijXIEeDLNR7rfje3D1vI7Lsxlnj7FgGZApEgCDIh0eoShHpj5wF5FIJmBcimkpA+xMFIkbyBMTLIx+DGL9rvQV/KLjnlM0+WJkH8THIVfpflMGvddmAhPrzvoNBjRwFeuJUvoSXCwpjC/KlN2uG6EupAb2i0fT7imPR+TfEQz4IGUYYY6IKlK+yWrcqWMOOA26Gwprf/+Dh0XgM/9onmypCLX7gi8PFGSZb1pv35NJhsPBlJ9GXLiEB/H7KZQbBiltP73jVObAc4VFAGZjICRw7SuAi+y5EsNcHT5lNS8RI8Rw8xILvMNm2MousmSKolTxVpSVcH3OhLOpNROPZrsSOON0a2Vxab0dpHWDaSukhO8CDdLb+UQ5WpI6hQJCZ5Ey6nIFVM5nICqmdLVWaVkfGQ8PJtENXWBdCFmrWnU1qkbXE1ePT9qCenA9BfIMHWwe2Qo/8RwEadMJOHieEenHYveFPlRZQ8VeQiDwBnW2oWNMmHaaFM85BaGoIjXboY+PVyk2L+JRP43bkKKu6amNHudy8ieAlGO4bEdpNT3tbsTlpAOrx3jZUnoXOKV5iRaV3KMi+fr77unODugO2zaIYUasC3RVlIZhc3p1hoEztfG7lDoNHPNtgTMIjfSCOQPY+1Z6nWpLckbp5wMtXVCDH2avHmEkWkSPH9mUuzbdVX8b9/nNRGYIC16k8ADqYJa4bvg7MKvTAh33zyAMMnvNsosEZthTWkUllauGJT1jeijyuYmM43MIMHQwbRhChK6kle1FoIYJo9ZxKbOFW2koFbMJ0BC32ZaUSTqZTiA+doIC3UaNhdncBtzKKhDmgNMtNZ8h8r8aa7mGHLART1rmTEAWu+KADzEfHcUG0UOO7c0FnSiqag+MLrKs7Iq4qSeVfDutF+cdKGYH/JITFAF7O988daviRziNrPLBknirmb9oEvG5cHsdOIOA0tS5GqlrHKEzGoSKgWZKVCYrGWi1UZyDI86IGnbffeYTJYibZxhh2dqanP1dGrhjZ/BzaA2lWfOtPeZ1HTArmQcx/B4AjLzNRsXRcq9cYYdIsWrzdXTqYU3jkQ/g9rtLlJIGWg8q0nqKx5o+29rgplUqqa4I9enX/TYTUyBldHZmoaqy/Vdj/mc4KY1rRvXiZFr3n2jjayPO8MR+j/98q/6qxcNk9rwuJdR0Ze9etEkRW1/wH9WeREf0Pt8j9KS/Prqxd0Z1z6i5q/XqEwOA4tXmGeGSJ7ogWlHc5M91gHfSTpNTqKOpCvu87ZX8T6u4ouiSuq4lrh4h78lku6XeJnU50Gf0f4m+3CuTucKq4yOn1Pm0PnVC3X7r14IMr9qAu2VPlTAYiZYBfQhuzwn6b6X+02cltx2U8biClv/d4R/b/oSf5oVOjz3nP7IM0NGrfnwJhVle/zJPaDjKcXMyg/ZffwVucj2sUTv0CHePd/WabyI04yMib4jWLO/ep3EhyI+li2PoT7+E2N4f/z+X/8P3XNok3u1CAA= - - - dbo - - \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201605201911421_ExportRevision.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201605201911421_ExportRevision.Designer.cs deleted file mode 100644 index e3a2d0014f..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605201911421_ExportRevision.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class ExportRevision : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ExportRevision)); - - string IMigrationMetadata.Id - { - get { return "201605201911421_ExportRevision"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201605201911421_ExportRevision.cs b/src/Libraries/SmartStore.Data/Migrations/201605201911421_ExportRevision.cs deleted file mode 100644 index 903a904222..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605201911421_ExportRevision.cs +++ /dev/null @@ -1,204 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Data.Entity.Migrations; - using System.Linq; - using Core.Domain; - using Core.Domain.DataExchange; - using Setup; - using SmartStore.Utilities; - - public partial class ExportRevision : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.ExportDeployment", "ResultInfo", c => c.String()); - AddColumn("dbo.ExportDeployment", "SubFolder", c => c.String(maxLength: 400)); - AlterColumn("dbo.ExportProfile", "FolderName", c => c.String(nullable: false, maxLength: 400)); - DropColumn("dbo.ExportDeployment", "CreateZip"); - } - - public override void Down() - { - AddColumn("dbo.ExportDeployment", "CreateZip", c => c.Boolean(nullable: false)); - AlterColumn("dbo.ExportProfile", "FolderName", c => c.String(nullable: false, maxLength: 100)); - DropColumn("dbo.ExportDeployment", "SubFolder"); - DropColumn("dbo.ExportDeployment", "ResultInfo"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - // migrate folder name to folder path - var rootPath = "~/App_Data/ExportProfiles/"; - var exportProfiles = context.Set().ToList(); - - foreach (var profile in exportProfiles) - { - if (!profile.FolderName.EmptyNull().StartsWith(rootPath)) - { - profile.FolderName = rootPath + profile.FolderName; - } - } - - context.SaveChanges(); - - // migrate public file system deployment to new public deployment - if (context.ColumnExists("ExportDeployment", "IsPublic")) - { - var fileSystemDeploymentTypeId = (int)ExportDeploymentType.FileSystem; - var publicFolderDeploymentTypeId = (int)ExportDeploymentType.PublicFolder; - - context.ExecuteSqlCommand("Update [ExportDeployment] Set DeploymentTypeId = {0} Where DeploymentTypeId = {1} And IsPublic = 1", - true, null, publicFolderDeploymentTypeId, fileSystemDeploymentTypeId); - - context.ColumnDelete("ExportDeployment", "IsPublic"); - } - - var oldFileManagerPath = CommonHelper.MapPath("~/Content/filemanager"); - FileSystemHelper.ClearDirectory(oldFileManagerPath, true); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.DataExchange.Export.FolderName", - "Folder path", - "Ordnerpfad", - "Specifies the relative path of the folder where to export the data.", - "Legt den relativen Pfad des Ordners fest, in den die Daten exportiert werden."); - - builder.AddOrUpdate("Admin.DataExchange.Export.FileNamePattern.Validate", - "Please enter a valid pattern for file names. Example for file names: %Store.Id%-%Profile.Id%-%File.Index%-%Profile.SeoName%", - "Bitte ein gltiges Muster fr Dateinamen eingeben. Beispiel: %Store.Id%-%Profile.Id%-%File.Index%-%Profile.SeoName%"); - - builder.AddOrUpdate("Admin.DataExchange.Export.FolderName.Validate", - "Please enter a valid, relative folder path for the export data.", - "Bitte einen gltigen, relativen Ordnerpfad fr die zu exportierenden Daten eingeben."); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDeploymentType.Http", "HTTP POST", "HTTP POST"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportDeploymentType.PublicFolder", "Public folder", "ffentlicher Ordner"); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.SubFolder", - "Name of subfolder", - "Name des Unterordners", - "Specifies the name of a subfolder where to publish the data.", - "Legt den Namen eines Unterordners fest, in den die Daten verffentlicht werden sollen."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.ZipUsageNote", - "If there are a large number of export files, it is recommended to use the option Create ZIP archive. This saves time and avoids problems, such as a full email mailbox.", - "Bei einer groen Anzahl an Exportdateien wird empfohlen die Option ZIP-Archiv erstellen zu benutzen. Das spart Zeit und vermeidet Probleme, wie z.B. ein volles E-Mail Postfach."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Cleanup", - "Clean up after successful deployment", - "Nach erfolgreicher Verffentlichung aufrumen", - "Specifies whether to delete unneeded files after all deployments succeeded.", - "Legt fest, ob nicht mehr bentigte Dateien gelscht werden sollen, nachdem alle Verffentlichungen erfolgreich waren."); - - builder.AddOrUpdate("Admin.Common.FtpStatus", - "FTP status {0} ({1}).", - "FTP-Status {0} ({1})."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.CopyFileFailed", - "At least one file could not be copied.", - "Mindestens eine Datei konnte nicht kopiert werden."); - - builder.AddOrUpdate("Admin.Common.LastRun", "Last run", "Letzte Ausfhrung"); - builder.AddOrUpdate("Admin.Common.SuccessfulOn", "Successful on", "Erfolgreich am"); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.Name", - "Name of profile", - "Name des Profils", - "Specifies the name of the publishing profile.", - "Legt den Namen des Verffentlichungsprofils fest."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.ProfilesTitle", - "Publishing profiles", - "Verffentlichungsprofile"); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.NoProfiles", - "There are no publishing profiles.", - "Es liegen keine Verffentlichungsprofile vor."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.Note", - "Click New profile to add one or multiple publishing profiles to specify how to further proceed with the export files.", - "Legen Sie ber Neues Profil ein oder mehrere Verffentlichungsprofile an, um festzulegen wie mit den Exportdateien weiter zu verfahren ist."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.PublishingTarget", - "Publishing target", - "Verffentlichungsziel"); - - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.DeploymentType", - "Publishing type", - "Art der Verffentlichung", - "Specifies the type of publishing.", - "Legt die Art Verffentlichung fest."); - - builder.AddOrUpdate("Common.Publishing", - "Publishing", - "Verffentlichung"); - - builder.AddOrUpdate("Common.NoFilesAvailable", - "There are no files available.", - "Es sind keine Dateien vorhanden."); - - builder.AddOrUpdate("Common.CopyToClipboard", "Copy to clipboard", "In die Zwischenablage kopieren"); - builder.AddOrUpdate("Common.CopyToClipboard.Succeeded", "Copied!", "Kopiert!"); - builder.AddOrUpdate("Common.CopyToClipboard.Failded", "Failed to copy.", "Kopieren ist fehlgeschlagen."); - - builder.AddOrUpdate("Products.NoBundledItems", - "No bundle items available", - "Keine Bundle-Bestandteile vorhanden"); - - builder.AddOrUpdate("Common.NoFileUploaded", - "There was no file uploaded.", - "Es wurde keine Datei hochgeladen."); - - builder.AddOrUpdate("Products.BasePriceInfo", - "Content: {0} {1} ({2} / {3} {1})", - "Inhalt: {0} {1} ({2} / {3} {1})"); - - builder.AddOrUpdate("Products.BasePriceInfo.LanguageInsensitive", - "{0} {1} ({2} / {3} {1})", - "{0} {1} ({2} / {3} {1})"); - - builder.AddOrUpdate("PrivateMessages.Disabled", - "Private messages are disabled.", - "Private Nachrichten sind deaktiviert."); - - builder.AddOrUpdate("Common.MethodNotSupportedForGuests", - "This function is not available for guests.", - "Diese Funktion steht fr Gste nicht zur Verfgung."); - - builder.AddOrUpdate("ContactUs.PrivacyAgreement", - "Privacy consent", - "Einwilligungserklrung Datenschutz"); - - builder.AddOrUpdate("ContactUs.PrivacyAgreement.MustBeAccepted", - "Please agree to the storage of your data.", - "Bitte stimmen Sie der Speicherung Ihrer Daten zu."); - - builder.AddOrUpdate("ContactUs.PrivacyAgreement.DetailText", - "Yes I've read the privacy terms and agree that my data given by me can be stored electronically. My data will thereby only be used to process my inquiry.", - "Ja, ich habe die Datenschutzerklrung zur Kenntnis genommen und bin damit einverstanden, dass die von mir angegebenen Daten elektronisch erhoben und gespeichert werden. Meine Daten werden dabei nur zur Bearbeitung meiner Anfrage genutzt."); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.DisplayPrivacyAgreementOnContactUs", - "Get privacy consent for contact requests", - "Einwilligungserklrung im Kontaktformular fordern", - "Specifies whether a checkbox will be displayed on the contact page which requests the user to agree on storage of his data.", - "Bestimmt ob im Kontaktformular eine Checkbox angezeigt wird, die den Benutzer auffordert der Speicherung seiner Daten zuzustimmen."); - - - builder.Delete( - "Admin.DataExchange.Export.FolderAndFileName.Validate", - "Admin.DataExchange.Export.Deployment.IsPublic", - "Admin.DataExchange.Export.Deployment.CreateZip", - "Admin.Common.TemporaryFiles", - "Admin.Common.NoTempFilesFound"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201605201911421_ExportRevision.resx b/src/Libraries/SmartStore.Data/Migrations/201605201911421_ExportRevision.resx deleted file mode 100644 index a653f94065..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201605201911421_ExportRevision.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201606241315549_AddressEnhancement.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201606241315549_AddressEnhancement.Designer.cs deleted file mode 100644 index 0a53025ed6..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201606241315549_AddressEnhancement.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class AddressEnhancement : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(AddressEnhancement)); - - string IMigrationMetadata.Id - { - get { return "201606241315549_AddressEnhancement"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201606241315549_AddressEnhancement.cs b/src/Libraries/SmartStore.Data/Migrations/201606241315549_AddressEnhancement.cs deleted file mode 100644 index 764c3fa070..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201606241315549_AddressEnhancement.cs +++ /dev/null @@ -1,78 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - - public partial class AddressEnhancement : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Address", "Salutation", c => c.String()); - AddColumn("dbo.Address", "Title", c => c.String()); - } - - public override void Down() - { - DropColumn("dbo.Address", "Title"); - DropColumn("dbo.Address", "Salutation"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - context.SaveChanges(); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Address.Fields.Salutation", "Salutation", "Anrede"); - builder.AddOrUpdate("Address.Fields.Title", "Title", "Titel"); - builder.AddOrUpdate("Account.Fields.Title", "Title", "Titel"); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.AddressFormFields.Salutations", - "Salutations", - "Anreden", - "Comma separated list of salutations (e.g. Mr., Mrs). Define the entries which will populate the dropdown list for salutation when entering addresses.", - "Komma getrennte Liste (z.B. Herr, Frau). Bestimmen Sie die Eintrge fr die Auswahl der Anrede, bei der Adresserfassung."); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.AddressFormFields.SalutationEnabled", - "'Salutation' enabled", - "'Anrede' aktiv", - "Set if 'Salutation' is enabled.", - "Legt fest, ob das Feld 'Anrede' aktiv ist."); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.AddressFormFields.TitleEnabled", - "'Title' enabled", - "'Titel' aktiv", - "Set if 'Title' is enabled.", - "Legt fest, ob das Feld 'Titel' aktiv ist."); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.TitleEnabled", - "'Title' enabled", - "'Titel' aktiv", - "Set if 'Title' is enabled.", - "Legt fest, ob das Feld 'Titel' aktiv ist."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Shipping.SkipShippingIfSingleOption", - "Display shipping options during checkout process only if more then one option is available", - "Versandartauswahl nur anzeigen, wenn mehr als eine Versandart zur Verfgung steht", - "Display shipping options during the checkout process only if more then one shipping option is available.", - "Legt fest, ob die Versandartauswahl nur im Checkout-Prozess angezeigt wird, wenn mehr als eine Versandart zur Verfgung steht"); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.OnlyIndividuallyVisibleAssociated", - "Only individually visible products", - "Nur individuell sichtbare Produkte", - "Specifies whether to only export individually visible associated products.", - "Legt fest, ob nur individuell sichtbare, verknpfte Produkte exportiert werden sollen."); - - builder.Delete("Providers.ExchangeRate.EcbExchange.SetCurrencyToEURO"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201606241315549_AddressEnhancement.resx b/src/Libraries/SmartStore.Data/Migrations/201606241315549_AddressEnhancement.resx deleted file mode 100644 index cb06becfd4..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201606241315549_AddressEnhancement.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201607111649138_FixExportDeploymentIsPublic.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201607111649138_FixExportDeploymentIsPublic.Designer.cs deleted file mode 100644 index 5d73d95c71..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201607111649138_FixExportDeploymentIsPublic.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class FixExportDeploymentIsPublic : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(FixExportDeploymentIsPublic)); - - string IMigrationMetadata.Id - { - get { return "201607111649138_FixExportDeploymentIsPublic"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201607111649138_FixExportDeploymentIsPublic.cs b/src/Libraries/SmartStore.Data/Migrations/201607111649138_FixExportDeploymentIsPublic.cs deleted file mode 100644 index d232a4511a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201607111649138_FixExportDeploymentIsPublic.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Data.Entity.Migrations; - using System.Web.Hosting; - using Core.Data; - - public partial class FixExportDeploymentIsPublic : DbMigration - { - public override void Up() - { - if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) - { - Sql("IF EXISTS(SELECT TOP 1 1 FROM sys.objects o INNER JOIN sys.columns c ON o.object_id = c.object_id WHERE o.name = 'ExportDeployment' AND c.name = 'IsPublic') ALTER TABLE [dbo].[ExportDeployment] DROP COLUMN [IsPublic];"); - } - } - - public override void Down() - { - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201607111649138_FixExportDeploymentIsPublic.resx b/src/Libraries/SmartStore.Data/Migrations/201607111649138_FixExportDeploymentIsPublic.resx deleted file mode 100644 index d65f96aa1d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201607111649138_FixExportDeploymentIsPublic.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201608110948010_MediaStorageData.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201608110948010_MediaStorageData.Designer.cs deleted file mode 100644 index be66100823..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201608110948010_MediaStorageData.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class MediaStorageData : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(MediaStorageData)); - - string IMigrationMetadata.Id - { - get { return "201608110948010_MediaStorageData"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201608110948010_MediaStorageData.cs b/src/Libraries/SmartStore.Data/Migrations/201608110948010_MediaStorageData.cs deleted file mode 100644 index 1cb4b063e3..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201608110948010_MediaStorageData.cs +++ /dev/null @@ -1,341 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity; - using System.Data.Entity.Migrations; - using System.IO; - using System.Linq; - using Core; - using Core.Data; - using Core.Domain.Configuration; - using Core.Domain.Media; - using Core.Domain.Messages; - using Core.IO; - using Setup; - - public partial class MediaStorageData : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - private const int PAGE_SIZE = 200; - - private string GetFileName(int id, string extension, string mimeType) - { - if (extension.IsEmpty()) - extension = MimeTypes.MapMimeTypeToExtension(mimeType); - - var fileName = string.Format("{0}-0{1}", id.ToString("0000000"), extension.EmptyNull().EnsureStartsWith(".")); - return fileName; - } - - public override void Up() - { - CreateTable( - "dbo.MediaStorage", - c => new - { - Id = c.Int(nullable: false, identity: true), - Data = c.Binary(nullable: false), - }) - .PrimaryKey(t => t.Id); - - CreateTable( - "dbo.Discount_AppliedToManufacturers", - c => new - { - Discount_Id = c.Int(nullable: false), - Manufacturer_Id = c.Int(nullable: false), - }) - .PrimaryKey(t => new { t.Discount_Id, t.Manufacturer_Id }) - .ForeignKey("dbo.Discount", t => t.Discount_Id, cascadeDelete: true) - .ForeignKey("dbo.Manufacturer", t => t.Manufacturer_Id, cascadeDelete: true) - .Index(t => t.Discount_Id) - .Index(t => t.Manufacturer_Id); - - AddColumn("dbo.Picture", "MediaStorageId", c => c.Int()); - AddColumn("dbo.Manufacturer", "HasDiscountsApplied", c => c.Boolean(nullable: false)); - AddColumn("dbo.Download", "MediaStorageId", c => c.Int()); - AddColumn("dbo.QueuedEmailAttachment", "MediaStorageId", c => c.Int()); - CreateIndex("dbo.Picture", "MediaStorageId"); - CreateIndex("dbo.Download", "MediaStorageId"); - CreateIndex("dbo.QueuedEmailAttachment", "MediaStorageId"); - AddForeignKey("dbo.Picture", "MediaStorageId", "dbo.MediaStorage", "Id"); - AddForeignKey("dbo.Download", "MediaStorageId", "dbo.MediaStorage", "Id"); - AddForeignKey("dbo.QueuedEmailAttachment", "MediaStorageId", "dbo.MediaStorage", "Id"); - } - - public override void Down() - { - DropForeignKey("dbo.QueuedEmailAttachment", "MediaStorageId", "dbo.MediaStorage"); - DropForeignKey("dbo.Download", "MediaStorageId", "dbo.MediaStorage"); - DropForeignKey("dbo.Discount_AppliedToManufacturers", "Manufacturer_Id", "dbo.Manufacturer"); - DropForeignKey("dbo.Discount_AppliedToManufacturers", "Discount_Id", "dbo.Discount"); - DropForeignKey("dbo.Picture", "MediaStorageId", "dbo.MediaStorage"); - DropIndex("dbo.Discount_AppliedToManufacturers", new[] { "Manufacturer_Id" }); - DropIndex("dbo.Discount_AppliedToManufacturers", new[] { "Discount_Id" }); - DropIndex("dbo.QueuedEmailAttachment", new[] { "MediaStorageId" }); - DropIndex("dbo.Download", new[] { "MediaStorageId" }); - DropIndex("dbo.Picture", new[] { "MediaStorageId" }); - DropColumn("dbo.QueuedEmailAttachment", "MediaStorageId"); - DropColumn("dbo.Download", "MediaStorageId"); - DropColumn("dbo.Manufacturer", "HasDiscountsApplied"); - DropColumn("dbo.Picture", "MediaStorageId"); - DropTable("dbo.Discount_AppliedToManufacturers"); - DropTable("dbo.MediaStorage"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { -#pragma warning disable 612, 618 - context.MigrateLocaleResources(MigrateLocaleResources); - - var mediaStorages = context.Set(); - var fileSystem = new LocalFileSystem(); - var mediaBasePath = $"Media\\{DataSettings.Current.TenantName}"; - var storeMediaInDb = true; - - fileSystem.TryCreateFolder(Path.Combine(mediaBasePath, "Downloads")); - fileSystem.TryCreateFolder(Path.Combine(mediaBasePath, "QueuedEmailAttachment")); - - { - var settings = context.Set(); - - // be careful, this setting does not necessarily exist - var storeInDbSetting = settings.FirstOrDefault(x => x.Name == "Media.Images.StoreInDB"); - if (storeInDbSetting != null) - { - storeMediaInDb = storeInDbSetting.Value.ToBool(true); - - // remove old bool StoreInDB because it's not used anymore - settings.Remove(storeInDbSetting); - } - - // set current media storage provider system name - settings.AddOrUpdate(x => x.Name, new Setting - { - Name = "Media.Storage.Provider", - Value = (storeMediaInDb ? "MediaStorage.SmartStoreDatabase" : "MediaStorage.SmartStoreFileSystem") - }); - - context.SaveChanges(); - } - - using (var scope = new DbContextScope(context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false)) - { - #region Pictures - - if (storeMediaInDb) - { - PageEntities(context, mediaStorages, context.Set().OrderBy(x => x.Id), picture => - { - - if (picture.PictureBinary != null && picture.PictureBinary.LongLength > 0) - { - picture.MediaStorage = new MediaStorage { Data = picture.PictureBinary }; - picture.PictureBinary = null; - } - }); - } - - #endregion - - #region Downloads - - PageEntities(context, mediaStorages, context.Set().OrderBy(x => x.Id), download => - { - if (download.DownloadBinary != null && download.DownloadBinary.LongLength > 0) - { - if (storeMediaInDb) - { - // move binary data - download.MediaStorage = new MediaStorage { Data = download.DownloadBinary }; - } - else - { - // move to file system. it's necessary because from now on DownloadService depends on current storage provider - // and it would not find the binary data anymore if not moved. - var fileName = GetFileName(download.Id, download.Extension, download.ContentType); - var path = fileSystem.Combine(fileSystem.Combine(mediaBasePath, "Downloads"), fileName); - - fileSystem.WriteAllBytes(path, download.DownloadBinary); - } - - download.DownloadBinary = null; - } - }); - - #endregion - - #region Queued email attachments - - var attachmentQuery = context.Set() - .Where(x => x.StorageLocation == EmailAttachmentStorageLocation.Blob) - .OrderBy(x => x.Id); - - PageEntities(context, mediaStorages, attachmentQuery, attachment => - { - if (attachment.Data != null && attachment.Data.LongLength > 0) - { - if (storeMediaInDb) - { - // move binary data - attachment.MediaStorage = new MediaStorage { Data = attachment.Data }; - } - else - { - // move to file system. it's necessary because from now on QueuedEmailService depends on current storage provider - // and it would not find the binary data anymore if do not move it. - var fileName = GetFileName(attachment.Id, Path.GetExtension(attachment.Name.EmptyNull()), attachment.MimeType); - var path = fileSystem.Combine(fileSystem.Combine(mediaBasePath, "QueuedEmailAttachment"), fileName); - - fileSystem.WriteAllBytes(path, attachment.Data); - } - - attachment.Data = null; - } - }); - - #endregion - } - -#pragma warning restore 612, 618 - } - - private void PageEntities( - SmartObjectContext context, - DbSet mediaStorages, - IOrderedQueryable query, - Action moveEntity) where TEntity : BaseEntity, IHasMedia - { - var pageIndex = 0; - IPagedList entities = null; - - do - { - if (entities != null) - { - // detach all entities from previous page to save memory - context.DetachAll(false); - entities.Clear(); - entities = null; - } - - GC.Collect(); - GC.WaitForPendingFinalizers(); - - // load max 200 entities at once - entities = new PagedList(query, pageIndex++, PAGE_SIZE); - - entities.Each(x => moveEntity(x)); - - // save the current batch to database - context.SaveChanges(); - } - while (entities.HasNextPage); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Common.Copy", "Copy", "Kopie"); - builder.AddOrUpdate("Admin.Common.Data", "Data", "Daten"); - - builder.AddOrUpdate("Admin.Media.StorageMovingNotSupported", - "The provider \"{0}\" does not support moving media from one provider to another.", - "Der Provider \"{0}\" untersttzt kein Verschieben von Medien zu anderen Providern."); - - builder.AddOrUpdate("Admin.Media.CannotMoveToSameProvider", - "Media cannot be moved to the same storage device.", - "Medien knnen nicht zum selben Speichermedium verschoben werden."); - - builder.AddOrUpdate("Providers.FriendlyName.MediaStorage.SmartStoreDatabase", - "Database", - "Datenbank"); - - builder.AddOrUpdate("Providers.FriendlyName.MediaStorage.SmartStoreFileSystem", - "File system", - "Dateisystem"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Media.CurrentStorageLocation", - "The current storage location is", - "Der aktuelle Speicherort ist"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Media.StorageProvider", - "New storage location", - "Neuer Speicherort", - "Specifies the new storage location for media file like images.", - "Legt den neuen Speicherort fr Mediendateien wie z.B. Bilder fest."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Media.MediaIsStoredIn", - "Store media in", - "Medien speichern in"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Media.MoveMediaNote", - "Do not forget to backup your database before changing this option. Please bear in mind that this operation can take several minutes depending on the amount of media files.", - "Bitte sichern Sie Ihre Datenbank, ehe Sie Mediendateien verschieben. Dieser Vorgang kann je nach Dateimenge mehrere Minuten in Anspruch nehmen."); - - builder.AddOrUpdate("Admin.Common.MoveNow", "Move now", "Jetzt verschieben"); - - builder.AddOrUpdate("Admin.Media.ProviderFailedToSave", - "The storing of data through storage provider \"{0}\" failed in \"{1}\"", - "Das Speichern von Daten durch den Storage-Provider \"{0}\" ist whrend \"{1}\" fehlgeschlagen"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Tax.AuxiliaryServicesTaxType.SpecifiedTaxCategory", - "Specified tax category", - "Festgelegte Steuerklasse"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Tax.AuxiliaryServicesTaxType.HighestCartAmount", - "Highest amount in cart", - "Hchster Wert im Warenkorb"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Tax.AuxiliaryServicesTaxType.HighestTaxRate", - "Highest tax rate in cart", - "Hchste Steuerrate im Warenkorb"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Tax.AuxiliaryServicesTaxType.ProRata", - "Pro rata in accordance with main service", - "Anteilig gem der Hauptleistung"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Tax.AuxiliaryServicesTaxingType", - "Taxing of auxiliary services", - "Besteuerung von Nebenleistungen", - "Specifies how to calculate the tax amount for auxiliary services like shipping and payment fees.", - "Legt fest, wie die Mehrwertsteuer auf Nebenleistungen (wie z.B. Versandkosten und Zahlartgebhren) berechnet werden soll."); - - - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Discounts.DiscountType.AssignedToManufacturers", - "Assigned to manufacturers", - "Bezogen auf die Hersteller"); - - builder.AddOrUpdate("Admin.Promotions.Discounts.NoObjectsAssigned", - "No objects assigned", - "Keinen Objekten zugeordnet"); - - builder.AddOrUpdate("Admin.Promotions.Discounts.Fields.AppliedToManufacturers", - "Assigned to manufacturers", - "Herstellern zugeordnet"); - - builder.AddOrUpdate("Admin.Promotions.Discounts.NoDiscountsAvailable", - "There are no discounts available. Please create at least one discount before making an assignment.", - "Es sind keine Rabatte verfgbar. Erstellen Sie bitte zunchst mindestens einen Rabatt, bevor Sie eine Zuordung vornehmen."); - - builder.AddOrUpdate("Admin.Plugins.KnownGroup.Performance", "Performance", "Performance"); - - - builder.Delete( - "Admin.Configuration.Settings.Media.PicturesStoredIntoDatabase.Database", - "Admin.Configuration.Settings.Media.PicturesStoredIntoDatabase.FileSystem", - "Admin.Configuration.Settings.Media.MoveToFs", - "Admin.Configuration.Settings.Media.MoveToDb", - "Admin.Configuration.Settings.Media.PicturesStoredIntoDatabase", - "Admin.Configuration.Settings.Media.MovePicturesNote", - "Admin.Catalog.Categories.Discounts.NoDiscounts", - "Admin.Catalog.Products.Discounts.NoDiscounts", - "Admin.Promotions.Discounts.Fields.AppliedToProducts.NoRecords", - "Admin.Promotions.Discounts.Fields.AppliedToCategories.NoRecords"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201608110948010_MediaStorageData.resx b/src/Libraries/SmartStore.Data/Migrations/201608110948010_MediaStorageData.resx deleted file mode 100644 index 83164d490d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201608110948010_MediaStorageData.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201609201852449_log4net.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201609201852449_log4net.Designer.cs deleted file mode 100644 index 52a9ee42ba..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201609201852449_log4net.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class log4net : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(log4net)); - - string IMigrationMetadata.Id - { - get { return "201609201852449_log4net"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201609201852449_log4net.cs b/src/Libraries/SmartStore.Data/Migrations/201609201852449_log4net.cs deleted file mode 100644 index 5eda541bc0..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201609201852449_log4net.cs +++ /dev/null @@ -1,96 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Linq; - using System.Data.Entity.Migrations; - using Setup; - using Core.Data; - using System.Web.Hosting; - - public partial class log4net : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - // Custom START - if (HostingEnvironment.IsHosted) - { - if (DataSettings.Current.IsSqlServer) - { - 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 - { - Sql(@"SET LOCK_TIMEOUT 20000;"); - // SQL CE: DropIndex fails during installation, IF EXISTS not supported. - //DropIndex("Log", "IX_Log_ContentHash"); - Sql(@"DELETE FROM Log;"); - } - } - // Custom END - - AddColumn("dbo.Log", "Logger", c => c.String(nullable: false, maxLength: 400)); - AddColumn("dbo.Log", "HttpMethod", c => c.String(maxLength: 10)); - AddColumn("dbo.Log", "UserName", c => c.String(maxLength: 100)); - AlterColumn("dbo.Log", "ShortMessage", c => c.String(nullable: false, maxLength: 4000)); - AlterColumn("dbo.Log", "PageUrl", c => c.String(maxLength: 1500)); - AlterColumn("dbo.Log", "ReferrerUrl", c => c.String(maxLength: 1500)); - CreateIndex("dbo.Log", "LogLevelId", name: "IX_Log_Level"); - CreateIndex("dbo.Log", "Logger", name: "IX_Log_Logger"); - DropColumn("dbo.Log", "UpdatedOnUtc"); - DropColumn("dbo.Log", "Frequency"); - DropColumn("dbo.Log", "ContentHash"); - } - - public override void Down() - { - AddColumn("dbo.Log", "ContentHash", c => c.String(maxLength: 40)); - AddColumn("dbo.Log", "Frequency", c => c.Int(nullable: false)); - AddColumn("dbo.Log", "UpdatedOnUtc", c => c.DateTime()); - DropIndex("dbo.Log", "IX_Log_Logger"); - DropIndex("dbo.Log", "IX_Log_Level"); - AlterColumn("dbo.Log", "ReferrerUrl", c => c.String()); - AlterColumn("dbo.Log", "PageUrl", c => c.String()); - AlterColumn("dbo.Log", "ShortMessage", c => c.String(nullable: false)); - DropColumn("dbo.Log", "UserName"); - DropColumn("dbo.Log", "HttpMethod"); - DropColumn("dbo.Log", "Logger"); - - // Custom START - if (DataSettings.DatabaseIsInstalled()) - { - CreateIndex("dbo.Log", "ContentHash", name: "IX_Log_ContentHash"); - } - // Custom END - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - var prefix = "Admin.System.Log."; - var deleteKeys = (new[] { "Fields.Frequency", "Fields.ContentHash", "Fields.UpdatedOn", "List.MinFrequency" }).Select(x => prefix + x).ToArray(); - deleteKeys = deleteKeys.Concat(deleteKeys.Select(x => x + ".Hint")).ToArray(); - - builder.Delete(deleteKeys); - - builder.AddOrUpdate("Admin.System.Log.Fields.Logger", - "Logger", - "Logger"); - builder.AddOrUpdate("Admin.System.Log.Fields.HttpMethod", - "HTTP method", - "HTTP Methode"); - builder.AddOrUpdate("Admin.System.Log.Fields.UserName", - "User", - "Benutzer"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201609201852449_log4net.resx b/src/Libraries/SmartStore.Data/Migrations/201609201852449_log4net.resx deleted file mode 100644 index b7e10dc3e8..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201609201852449_log4net.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201609281549565_CategoryBadges.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201609281549565_CategoryBadges.Designer.cs deleted file mode 100644 index 8f80e193a8..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201609281549565_CategoryBadges.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class CategoryBadges : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(CategoryBadges)); - - string IMigrationMetadata.Id - { - get { return "201609281549565_CategoryBadges"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201609281549565_CategoryBadges.cs b/src/Libraries/SmartStore.Data/Migrations/201609281549565_CategoryBadges.cs deleted file mode 100644 index 2e9ff0fd5b..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201609281549565_CategoryBadges.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using Setup; - using System; - using System.Data.Entity.Migrations; - using Core.Domain.Tasks; - - public partial class CategoryBadges : DbMigration, IDataSeeder - { - public override void Up() - { - AddColumn("dbo.Category", "BadgeText", c => c.String(maxLength: 400)); - AddColumn("dbo.Category", "BadgeStyle", c => c.Int(nullable: false)); - } - - public override void Down() - { - DropColumn("dbo.Category", "BadgeStyle"); - DropColumn("dbo.Category", "BadgeText"); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.Set().AddOrUpdate(x => x.Type, - new ScheduleTask - { - Name = "Rebuild XML Sitemap", - CronExpression = "45 3 * * *", - Type = "SmartStore.Services.Seo.RebuildXmlSitemapTask, SmartStore.Services", - Enabled = true, - StopOnError = false - } - ); - - context.SaveChanges(); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201609281549565_CategoryBadges.resx b/src/Libraries/SmartStore.Data/Migrations/201609281549565_CategoryBadges.resx deleted file mode 100644 index 4c5c3ff40d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201609281549565_CategoryBadges.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201611261017137_Search.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201611261017137_Search.Designer.cs deleted file mode 100644 index 69ebef8bbd..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201611261017137_Search.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class Search : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(Search)); - - string IMigrationMetadata.Id - { - get { return "201611261017137_Search"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201611261017137_Search.cs b/src/Libraries/SmartStore.Data/Migrations/201611261017137_Search.cs deleted file mode 100644 index 4b58e2bf10..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201611261017137_Search.cs +++ /dev/null @@ -1,107 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Collections.Generic; - using System.Data.Entity.Migrations; - using System.Linq; - using Core.Domain.Configuration; - using Setup; - - public partial class Search : DbMigration, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - var settings = context.Set(); - var catalogSettings = settings.Where(x => x.Name.StartsWith("CatalogSettings.")).ToList(); - var searchFields = new List(); - var searchSku = false; - var searchDescription = false; - - var searchSettingNames = new Dictionary - { - { "ProductSearchAutoCompleteEnabled", "InstantSearchEnabled" }, - { "ProductSearchAutoCompleteNumberOfProducts", "InstantSearchNumberOfProducts" }, - { "ProductSearchTermMinimumLength", "InstantSearchTermMinLength" }, - { "SearchPageProductsPerPage", "" }, - { "ProductSearchPageSizeOptions", "" }, - { "ProductSearchPageSize", "" }, - { "ProductSearchAllowCustomersToSelectPageSize", "" }, - { "SuppressSkuSearch", "" }, - { "SearchDescriptions", "" }, - { "ShowProductImagesInSearchAutoComplete", "ShowProductImagesInInstantSearch" } - }; - - // migrate CatalogSettings.DefaultSortOrder 'Position' - var sortOrderSettings = settings.Where(x => x.Name == "CatalogSettings.DefaultSortOrder" && x.Value == "Position").ToList(); - foreach (var setting in sortOrderSettings) - { - setting.Value = "Relevance"; - settings.AddOrUpdate(setting); - } - - // rename CatalogSettings.ProductsByTagPageSize - var productsByTagPageSizeSettings = settings.Where(x => x.Name == "CatalogSettings.ProductsByTagPageSize").ToList(); - foreach (var setting in productsByTagPageSizeSettings) - { - setting.Name = "CatalogSettings.DefaultProductListPageSize"; - settings.AddOrUpdate(setting); - } - - context.SaveChanges(); - - // delete obsolete catalog settings - context.MigrateSettings(builder => - { - // migrate catalog settings to search settings - foreach (var kvp in searchSettingNames.Where(x => x.Value.HasValue())) - { - var setting = catalogSettings.FirstOrDefault(x => x.Name.IsCaseInsensitiveEqual("CatalogSettings." + kvp.Key) && x.StoreId == 0); - if (setting != null && setting.Value.HasValue()) - { - if (kvp.Key == "SuppressSkuSearch") - { - if (!setting.Value.ToBool()) - searchSku = true; - } - else if (kvp.Key == "SearchDescriptions") - { - if (setting.Value.ToBool()) - searchDescription = true; - } - else - { - builder.Add("SearchSettings." + kvp.Value, setting.Value); - } - } - } - - // add searchable fields - if (searchSku) - { - searchFields.Add("sku"); - } - if (searchDescription) - { - searchFields.Add("shortdescription"); - searchFields.Add("fulldescription"); - } - - builder.Add("SearchSettings.SearchFields", string.Join(",", searchFields)); - - searchSettingNames.Keys.Each(key => builder.Delete("CatalogSettings." + key)); - }); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201611261017137_Search.resx b/src/Libraries/SmartStore.Data/Migrations/201611261017137_Search.resx deleted file mode 100644 index a6ccd629a9..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201611261017137_Search.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201612061904083_ListPaging.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201612061904083_ListPaging.Designer.cs deleted file mode 100644 index 12528770f1..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201612061904083_ListPaging.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class ListPaging : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ListPaging)); - - string IMigrationMetadata.Id - { - get { return "201612061904083_ListPaging"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201612061904083_ListPaging.cs b/src/Libraries/SmartStore.Data/Migrations/201612061904083_ListPaging.cs deleted file mode 100644 index 1f21f69193..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201612061904083_ListPaging.cs +++ /dev/null @@ -1,114 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Linq; - using System.Data.Entity.Migrations; - using Core.Domain.Catalog; - using Setup; - using Core.Domain.Configuration; - - public partial class ListPaging : DbMigration, IDataSeeder - { - public override void Up() - { - AlterColumn("dbo.Category", "PageSize", c => c.Int()); - AlterColumn("dbo.Category", "AllowCustomersToSelectPageSize", c => c.Boolean()); - AlterColumn("dbo.Manufacturer", "PageSize", c => c.Int()); - AlterColumn("dbo.Manufacturer", "AllowCustomersToSelectPageSize", c => c.Boolean()); - } - - public override void Down() - { - AlterColumn("dbo.Manufacturer", "AllowCustomersToSelectPageSize", c => c.Boolean(nullable: false)); - AlterColumn("dbo.Manufacturer", "PageSize", c => c.Int(nullable: false)); - AlterColumn("dbo.Category", "AllowCustomersToSelectPageSize", c => c.Boolean(nullable: false)); - AlterColumn("dbo.Category", "PageSize", c => c.Int(nullable: false)); - } - - public bool RollbackOnFailure - { - get { return false; } - } - - - public void Seed(SmartObjectContext context) - { - // Set Category paging stuff to null - try - { - var categories = context.Set().ToList(); - foreach (var c in categories) - { - c.PageSize = null; - c.AllowCustomersToSelectPageSize = null; - c.PageSizeOptions = null; - } - - context.SaveChanges(); - context.DetachEntities(); - } - catch { } - - - // Set Manufacturer paging stuff to null - try - { - var manufacturers = context.Set().ToList(); - foreach (var m in manufacturers) - { - m.PageSize = null; - m.AllowCustomersToSelectPageSize = null; - m.PageSizeOptions = null; - } - - context.SaveChanges(); - context.DetachEntities(); - } - catch { } - - - // Set new default paging global settings - try - { - var settings = context.Set().Where(x => x.Name.StartsWith("CatalogSettings.")).ToList(); - foreach (var s in settings) - { - switch (s.Name.ToLowerInvariant()) - { - case "catalogsettings.recentlyaddedproductsnumber": - s.Value = "100"; - break; - case "catalogsettings.recentlyviewedproductsnumber": - s.Value = "8"; - break; - case "catalogsettings.numberofbestsellersonhomepage": - s.Value = "12"; - break; - case "catalogsettings.productsalsopurchasednumber": - s.Value = "12"; - break; - case "catalogsettings.defaultproductlistpagesize": - s.Value = "24"; - break; - case "catalogsettings.defaultpagesizeoptions": - case "catalogsettings.productsbytagpagesizeoptions": - s.Value = "12,24,36,48,72,120"; - break; - case "catalogsettings.allowcustomerstoselectpagesize": - s.Value = "true"; - break; - case "catalogsettings.manufacturersblockitemstodisplay": - s.Value = "8"; - break; - case "catalogsettings.usesmallproductboxonhomepage": - s.Value = "false"; - break; - } - } - - context.SaveChanges(); - } - catch { } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201612061904083_ListPaging.resx b/src/Libraries/SmartStore.Data/Migrations/201612061904083_ListPaging.resx deleted file mode 100644 index 0f75080b69..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201612061904083_ListPaging.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201701121659419_PictureSize.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201701121659419_PictureSize.Designer.cs deleted file mode 100644 index 11d3d07d2a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201701121659419_PictureSize.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class PictureSize : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(PictureSize)); - - string IMigrationMetadata.Id - { - get { return "201701121659419_PictureSize"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201701121659419_PictureSize.cs b/src/Libraries/SmartStore.Data/Migrations/201701121659419_PictureSize.cs deleted file mode 100644 index 4130cdc89d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201701121659419_PictureSize.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class PictureSize : DbMigration - { - public override void Up() - { - AddColumn("dbo.Picture", "Width", c => c.Int()); - AddColumn("dbo.Picture", "Height", c => c.Int()); - } - - public override void Down() - { - DropColumn("dbo.Picture", "Height"); - DropColumn("dbo.Picture", "Width"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201701121659419_PictureSize.resx b/src/Libraries/SmartStore.Data/Migrations/201701121659419_PictureSize.resx deleted file mode 100644 index e241025651..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201701121659419_PictureSize.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201701251835419_SpecificationAttributeAlias.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201701251835419_SpecificationAttributeAlias.Designer.cs deleted file mode 100644 index b8a74acca6..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201701251835419_SpecificationAttributeAlias.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class SpecificationAttributeAlias : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(SpecificationAttributeAlias)); - - string IMigrationMetadata.Id - { - get { return "201701251835419_SpecificationAttributeAlias"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201701251835419_SpecificationAttributeAlias.cs b/src/Libraries/SmartStore.Data/Migrations/201701251835419_SpecificationAttributeAlias.cs deleted file mode 100644 index 381b4af621..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201701251835419_SpecificationAttributeAlias.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class SpecificationAttributeAlias : DbMigration - { - public override void Up() - { - AddColumn("dbo.SpecificationAttributeOption", "Alias", c => c.String(maxLength: 30)); - AddColumn("dbo.SpecificationAttribute", "Alias", c => c.String(maxLength: 30)); - AddColumn("dbo.SpecificationAttribute", "FacetSorting", c => c.Int(nullable: false)); - } - - public override void Down() - { - DropColumn("dbo.SpecificationAttribute", "FacetSorting"); - DropColumn("dbo.SpecificationAttribute", "Alias"); - DropColumn("dbo.SpecificationAttributeOption", "Alias"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201701251835419_SpecificationAttributeAlias.resx b/src/Libraries/SmartStore.Data/Migrations/201701251835419_SpecificationAttributeAlias.resx deleted file mode 100644 index 63dfc803e8..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201701251835419_SpecificationAttributeAlias.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201702061557351_DefaultDeliveryTime.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201702061557351_DefaultDeliveryTime.Designer.cs deleted file mode 100644 index 03447c2048..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702061557351_DefaultDeliveryTime.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class DefaultDeliveryTime : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(DefaultDeliveryTime)); - - string IMigrationMetadata.Id - { - get { return "201702061557351_DefaultDeliveryTime"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201702061557351_DefaultDeliveryTime.cs b/src/Libraries/SmartStore.Data/Migrations/201702061557351_DefaultDeliveryTime.cs deleted file mode 100644 index 8578b1486e..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702061557351_DefaultDeliveryTime.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class DefaultDeliveryTime : DbMigration - { - public override void Up() - { - AddColumn("dbo.DeliveryTime", "IsDefault", c => c.Boolean(nullable: true)); - } - - public override void Down() - { - DropColumn("dbo.DeliveryTime", "IsDefault"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201702061557351_DefaultDeliveryTime.resx b/src/Libraries/SmartStore.Data/Migrations/201702061557351_DefaultDeliveryTime.resx deleted file mode 100644 index cdc3b219c3..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702061557351_DefaultDeliveryTime.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201702081502539_QuantityEnhancements.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201702081502539_QuantityEnhancements.Designer.cs deleted file mode 100644 index d731aaad31..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702081502539_QuantityEnhancements.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class QuantityEnhancements : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(QuantityEnhancements)); - - string IMigrationMetadata.Id - { - get { return "201702081502539_QuantityEnhancements"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201702081502539_QuantityEnhancements.cs b/src/Libraries/SmartStore.Data/Migrations/201702081502539_QuantityEnhancements.cs deleted file mode 100644 index 990bbb1675..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702081502539_QuantityEnhancements.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class QuantityEnhancements : DbMigration - { - public override void Up() - { - AddColumn("dbo.Product", "QuantityStep", c => c.Int(nullable: false)); - AddColumn("dbo.Product", "QuantiyControlType", c => c.Int(nullable: false)); - AddColumn("dbo.Product", "HideQuantityControl", c => c.Boolean(nullable: false)); - } - - public override void Down() - { - DropColumn("dbo.Product", "HideQuantityControl"); - DropColumn("dbo.Product", "QuantiyControlType"); - DropColumn("dbo.Product", "QuantityStep"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201702081502539_QuantityEnhancements.resx b/src/Libraries/SmartStore.Data/Migrations/201702081502539_QuantityEnhancements.resx deleted file mode 100644 index dc49c84e6a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702081502539_QuantityEnhancements.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - H4sIAAAAAAAEAOy923LcOrIo+D4R8w8OP50zsY+97NV9onfHWnNCkiVbsX1Rq2R7735RUCxIYptF1uJFlnpivmwe5pPmFwYAbyCQuINklbpe7BKRSACJRCKRSGT+f//P//vb/3rcpC8eUFEmefb7yzevfnn5AmVxvk6yu99f1tXt//jLy//1f/7v/9tvp+vN44tvHdyvBA7XzMrfX95X1favr1+X8T3aROWrTRIXeZnfVq/ifPM6Wuev3/7yy7+/fvPmNcIoXmJcL178dllnVbJB9A/850mexWhb1VH6KV+jtGy/45IVxfric7RB5TaK0e8vV5uoqFZVXqBX76IqevniKE0i3I0VSm9fvoiyLK+iCnfyr19LtKqKPLtbbfGHKL162iIMdxulJWo7/9cB3HQcv7wl43g9VOxQxXVZ5RtLhG9+bQnzmq/uRN6XPeEw6U4xiasnMmpKvt9fXuXbJH75gm/prydpQaBGpD2h9MVgSfaK1iub//7tBQf0bz1TvH31P1/98uqXf3txUqdVXaDfM1RXRZT+24uL+iZN4v9AT1f5D5T9ntVpyvYU9xWXjT7gTxdFvkVF9XSJbtv+n69fvng9rvear9hXY+o0gzvPql/fvnzxGTce3aSoZwSGEHRU71GGiqhC64uoqlCRERyIklJonWtr9VRWaEN+d21i/sPr6OWLT9HjR5TdVfe/v8Q/X744Sx7RuvvS9uNrluBlhytVRY10TZ1ncVqv0Xm2SnCT0bZr8DjPUxRlwDA1+MqLqCx/5sUaF1QoxsP3RdkhnJwWV0mVTk/x43z9NHkjn1AVYY4mZCtnaewdKuMi2TYCZ4b25pmrj8kGL4v1VU4XdOnLyZcoW6PiqPyerO9Q5YutwfL3PJueDk1T34toizfYCgsxoe8m9Vf3+c/RvPmN/BgzNyoCyJciyQsqldXy3UB4XEV3gefit9fD7qvek6PHE7zZ3OXFk8vOHD2+YjAcNmd5W5pt+U+//GI0yZbs9S4pt2n09IWwvA2jGvPPClUVHYs172CRcJvc1QWFftXiOXCQMwe9nYaDvkVpHWKnsGyW0kpPXSee/ZjHUZr8E627Nh24t8XRMK+A8MDG8raa6bCbWkDFirK7OrqzZBEAD5k6hOnzvsjr7fwCum9/qabnWt+fo4fkjjKQZCZfvrhEKQUo75NtY08RV9b1AH5W5JvLPIUWdA91vcrrIibjy7WgV1FB1Ws3mdJ3y1OUtHgOEkTelmYjfDPRcmlnpqW6cieeon1c+48arVB+gnGoWg9wcDtLo7vzDR7sWZIiDbn/bDZazRG3Sn3PY4EP3XQtlff+50RXFVwpM6nobibjEpVUxpVyAcpBymWoDLCXjSMxKoXuhK67dsahDqKgcTgPElYv63y1q47WyxxdutYXOsKcl2R1XaT1XZIJQkRX9SqvY0j4hNOq/IUCqFtpRYiTULhAxSYpyeK8RDE16lsLhBWKa2Kve8XjOggCeVuBLpNsD/8mF1lv//znKdoerKGTtyxdvCeUt1FBlhW8rfM8fM1VGZawGlJYwxpwr0XMonIxGLbVy1csosPqdV69E62gswKhFWbVLW3PT3m+ih5PH9Fm633rhRG1ijjBw02BuupRXCUP3pdP52Uj1Rrm98MVVD6aCiVeMkwsmPgTh6Ecc9Mucrz+7QUSqVbSfw9CSN5WqLPEkqpI6xMx+YV5MKMDuTP/kn3A6+OCqvR+2I7SNP/5vkZlhc8l3/LKG6GHTUS4J8Lr7h1mzK9V3GEif14lG23d02ztWNPX1uR2bCOSBjymjQpElW5UCmlwSrGPax9l5U8szqSdasqvGzE67hZTJIp0rtxbhjfIvCR5g+IgzxUyClNpP2X553pzg4ovt0SClX4DmMKo2ywfvyUGrX1oCdr0CZOLGnQUSh8Hdc0uxnFnJWCgbJDBessJFrGXtGARBZMZL46jErUdINTtFN3Oh067OhsyWa9Rgy1gqtmH2FbHKV4miCDmh8MuIW+ro9H7OulbbX7bXnuWuF3dDWSIK8hTPMvp5K0YeKWHbegsLzZR5bthd9hWUVpN3vWj9SbJTvLNhvEYnu4lQxnMxnR0e5ukCV4uvtQOY3F6h1IU4B1FZ7g6iuO8Bly4p7BdheGjj1FZnW+P1mt8RFM9ZzB1GNFIvAIRSfklA8+T1s4mZfUxv0sy1wMqrk+5CItqKQpnhaAlqeRuohP91wzYoAaIpcLuD4DY6q3HSZriae7nXtVNHhbo6xhE3mEOzrbXvKan6nYLcz1oNGK/eRhBy5YCQiq2341Vj1pqEIYh5MTW30ypenz6SBSaKD2qq3ui0sQUSHXKUdUAp8GogjAnZrVsJwirAfXmIi8reGx9MTgQsVToNQDi1MXmrae8j7Rc3slxMdxLDsa2m/TMD/eQFoGdG5cI/eKKbbt0ifAZA7PIH9REC3ZtBAJ2EYYQuioBs+/yz6hYX+RJVpUfEoyE3LiD/RbgJL2XwwFjUADbjqS76jTaawRgQPxxMHIByAPaisDVfU7rn+BD7DlWyuC+81Ag+aVAAu3lkF5mnZ6gDu+QNps8e9UiOJzp5W3hw1/dBQd4Fm+qz5KirALZovX6+CwN6awYYVrBa2YbZdO/Rz8hJ85CfCykvROsEP72kGSxeBbXtMi86J1sWK2seTNXQ28nb+jvyZYof1GqeZwQ6Jr8Ps9Qc5szvYyIHmdqyc+AID+aNWsI3Ns7zaGHGXZ0rkjQQvhya+WDXaTKznGQYhdHANKOjqH8bhE6clkrG++SAsVE33zV4jjoG/K2NBvmRO+8qLNL2VpygnjOlMG8IX/mHxEh4nk5xyuwq/sCIdMGfw3QIBa0qEhirjFHx6D65h94qV3l3yJvm/UuvAWbywPpEh/r8Rxg3B3XfkLVfS6xiY1hrofKjXhLxhY+PTRwXNRWsT6wsxuBZFijvQLYHUEAsfMglNc5d0wPl2cwLQJu6g77kEIsqfehqUK3zBgaK5xcOr/LMK1P7slKmEQqMYJlDnnEa5MWIsxtfY+VXQ8Fc4TpsLzlbcmMDGF9GqfSVm9uCvSQ6KxyYa64d0EPcjrnOu7nwuJX7vpeDmn9lW8Av7QW12HNK9Z8SyrfRW/iXmL6ul3nhnO03WLW8198QR1Vvm7Xkxit+jum0C4Qsts0qa+E07I+TvO73oPNekmT2uUrBsdu+Ba3nblCj9O75ZHBE4tyOC/mDiPIUgytrwfAgZ2gcoGVQCBvNmq64sFDBMFhL5C3FeqBYqg7S8tmAwWGdjFstszuHWup29lafCesn6mjMp5X4ZFeRXfTR8Ve5kGjYeBvU1OHvrFdC/wdZmSBY5BNdCPI7FOwFcVpN+SNn8otM8h72B4r9CZWKAS36zGEX4iiIl/XcXWJT+Pop8s5Lqoi3KNXIzy7ofi1XdqVDVLdSkO4WZTUy6hibvXciPIBpdvbOv0vVF5hTkmDIPucu+CSv/9rph9+/Mdy63UPyTz7gwDEB38glPVrVRZLS4sMH85PFRZjWZ1rbj1LRiStIb5oNavm97i1o7+v9DmcF7TSkDTn/6yuwG3S0NM0InEIKfstKRMMfZ6tk4dkXUdp+uSrhyxzAba6z7EmPKOeeIb7N2d7s75z7LgWbbZpgCeKYQPMdHjC3UMeDjRhnuLSY3730IruUMFO+432tKo3wY76gTB26KgSxQ3au4/hkPYeTkfxrgXPXv2op190UVbfRjFRQQq8j1ZaL90wzb6vEtUCD9PIefk+ua1OosL7sqfDE0JbIY++kgJ9qe4xxZvtJEBuM4pzUH40D7WDCLWa6MbkZSXWjY7Wa64P3mM6L9/lP7M0j/zvyVs8vjP3NUubBd4h9B7jp84t/sutgNMxpFKL5vRxmzRJod5FTzxOMxT05TtFEYLtP0TlKsJaEwo1q2Nslg9UcG9ISJSjuwIhVnF07cwI2SxWk/PykoTiLgI4RPeITp7iFDWd8pVxLMYLVCS59+rrcdK9nyL2XCvn1KX8NCNVAgT4CBmxF8vThKy8KO0wNs6Avf0axcmG2KYuCvyrzfr8l5cvViR2PN4/HbofLIDLeXlaepOTyYfoyzhYxSH3ktkDXpoYXeNz6H1yq/L4x9/qqLV1eIns5rhGMR49RAmum6QMVk/3MLCnzhtWkgUc+cf8ZzPqNsaKt/NgXiW3T9QgcJYXXR+PET59+SE+juIfNOEpSWzuHZeInAYJxvOGlvgE0h96vTUKeujHs5Rs6k2YSWowRo/hMHZYVhXahsD0RK5fipymhu/3XWKVhopt1ZZkjbr+tniCvGZC6xZrgmbQ1bEoIB08rp+O66oabCseooVAf0/K+zQpqzBIW9mXIrx28bY2Ml853/3iwwlFl8Te5rURkuAb8Jd0PW0D7bnshN5CT9TGaovxRKnlQMxx9m4d5O7ewUGDxdW6eThi6qx5p1mFijIIf7VSe4QZTcwUrVyftU189rpKULMmvfc7rEKgsjqqsOi8qSt0km9ukqy91QzIhLjPWObR0H3EgzhN/A8M31Fydz/dUhwf44Kj/56sJ8T+YVra9DuNrzzpEfkJk3AXNuHeloQJe7lDXvLiAJMHVDyRypbGo04fxCqYeM+sO0iUqN19glgdenyfUFTWBSJdUqiTQbKM9m0ebVhvUF8R26MlP8aozUhbZ+sU0Qs9jQEuzO1C094FKkj8rVB2nxFSQo3QONnAYdaI5aFMmy2x3yJBH6Gu9LqFvsqHq4LBL0gOJfgCKUBtPSRZaaBycLoeAwruTKNymX/WGMjRPauZVXnAOQHsuuUDqXOZDFTmgyWFtyV+dzJp7H0aF7POKqjwkuNBZAMQ4Bw7zt6hKvvOAir6D4HJxgDCOo7jIqF4lENoYRS95yBkHefBHPvc+wCE86pUuhz69peeQG/bcLT9sUXZf7iKYjzqCrLxaWo5jrd57aEQqCM4UZ4yxVJxysI4StNvUZFgpQ46Ryq7r6inmCCDWrJZMqnqOFU8aquBW4zWdojO42L1dCUPjgFFJmTLpVw4ArKO48ZdjSs6y4OK3R1DSDvMgdl2mbXhAN3tiyHOEAoFXhAhvFy6O1XRKRZIq9T2vw5u3fK2NE7OE+Wo72YmhKPK15KclWI83AAuuV3HRIzBjVpdU7ancuuL7yVeVrbOZeVJXm/zjI1r52yZEjBNFvClmxnqHkpXmS+PDpgI8WzdYHTH+KtccxQTD95sBdVZfoATtiUlsO3m1OPQn83Edrk6qvGMQA2GNIZ3HtVgOJnCzqIfhmCSMbaztKja9Sx/PNu3CdcAxgEByocCQvvFIGqtCR7PxjoUBwVD3pZGwZjoFRV51BSoZd21xHwPYI7zCvPprC1G6zuddT5gS6vqaXjd4+qslUQzZD1rF36od2WHd1uBsj+Sh6Wh/FBbi6fljSF50LdK/smzsYGLWR/S7irHWyqKKx5Vrzqb9eDL2ENxshht9Nh/GWV3Ss+4MDMc+AFmeC+RHX6etmu+BuG8KHbZa+E2worftwT9/DRJjpDQV7wOB0P9NS9wijS2dTdCGI6I2N8HdkBMJESuTIyAyAP4hcHpemB9lviE1kn0qq1/OEgoxFdDouMki4rhXUT7l8lC0nlzbtDIIR3awqY4qow888wew4287cySQ6H8LElRpj4S/RroGe9n9NN3czgvr4ooK5MQb/1CCnS6XAknQ6ETTYUaiwS+O2pl0hiQuTkCysV7IwjI8b7VytVAlMYwhNbVgBPdTpJ5TEJH8cwiOchoeVt4WUUuolm5CG3jQLnvxVw4qMOmbBoVaqqj9nwnCicF1FnwSa74ZfLRUliH9QfT9VURtc5cSDPXLB7rlkVzWLXytpaxyc9qc2Q44WCk3S0j7cGsunNm1YOlce8tjSFs2aGNic6OGXqTIuzIEUKpG3voiyodVC4oSSCQl4oEOEAEcY5k8B1UJqUIoeQKcEThyX5Zp2j1VFZoo9HLAqVa2KLJnQ7bPMKBcsz1IZXCoBuyF17lfZBPvGotNZIBTX8Inj5PBtNo9ESYp09TO1vD3Wzo9aDQLbfqieU8nT5iQcUapma4fBvc16dzlZPukUrHOre9Z/SI0iM7J4vosNvI29JsBIbPwq09rNO8+IAev0VpPX/rrY7+MSd7ztRP4gNm/i3bK37ladLWhD081fW3Yg+4DitO3lYgQ/boYbUvsmDhCuOQcR/kT2QWTrSg9RybOa9CkmIWZF9nejqrJWt0dV9vbrIo8XYta9Nl7I6h5zlaaOTmlI4pGh4xDAjB1bpmNwlFcAh5NX2gCEVdW5MLF81i2ggYkjsrbcSMgBdtTFtGAzDvepAbN91Uh9Q7ONQHNUTe1kA076i1HdGDIaLnAu/U4SVWO5p7mJAGalYWLiNLtevWQA77rOQAz9k4TId1Km8rkIYf6qnKeXmGtZ56yOaxoD4mz27fc6hBtKgBWB4uqv8hWXsi4AT7vEt8K11/Q+7wgVxrAGwH+TC5fGDJ/S8hI8bcahiZbVxJHZ5t9IdkHcorTCA/fGLMmfQ/pCyBQ475SxUY70G+TC5fYMI3N3tB0mY0um6AoKjNw8922CHefk7iK6xf7qGDA0pEgGFIQdOBqfjEYbRqdKYkUGGxpIsSlZfkVA/VWXCq0B7kpq3A885jFMZvJ3Coi18XvimdL7TqDPLELgirkWjyE74gmdTtSvAM9HGoLghaFxwTSNjgsvUgVQ/yz2uTOItiVK3yomK0YbpoxwXOUlW18GChuoC04MWoj8TxOcleRXf+x1aM5CAVnKVCqFdmOjUjWDhziQoAhjz34U1FyHB/nlUgP/CyvK3Vj3pyD6X3eAOY/l0hYxu8wLPdpDOf/tFfyORutqnS1NgsM6NpnLQsE6FpvAF2M3PTUVkmdxleRd1Lzjkyoi6S/uu8pKmO/f3kwphrh0Pyf27SAAq3RuaFSxxNbcFf6urLLUVKtWl7h0jvbFCqZByaRFGmVWWGR+P6E1wxTZf0xGGwriZo0+QgqrY1eUNMq7oMW5ttxFhLHA/C4zEOi+ig+8nbWuQxjpmnesjHMM/n5U3QaFL8og5/BDusPXlbgRSnFk2wCx8SEh1/2mynD4x+XrbvOL3fWDD7UlYVeUqw7WTELXuNxiWbmeEm7qyw8DznMh7Q7mpax3aE6vsYg6HyiKnDuFVWOlrjWip5tTRQ1jfNX6dGEsLCF3AzOewiBrvIPEkhlrkFm+5Jo6+m1L4jD60uUbQHbpe3JaGbvz/fHi0ig0gHqz/qqEDl5d3N5INqbLjrf+CVsEETht35Tq3PMzTk94prsiMhFQ0h1NqPSfaDieuzyDN+W21rd/QsQ73TTFlz2pGGbLuOAb87BId9RrFuWxq9r5O+1ea37Sv7ss9q/LXwDm0AoJpsI+namiopBTmpoyZN7hzxKnT5IcI0dPqIx1SG11ehbeqQiYJWdc9E0acbl6WiAAHEPJ4glJeM7zOOexwxehwHOS9vK5AaBMfus30w7R6vMcxNtqUji/tjaWag4MLsWfd6DDqsTBhCWJoSsJD30EMTgJ1WKFT0UGWLtTVXXKKHBP38gNLtbZ1mqCz9TRUCymBi5QVx1WF8hrupahWElyYLuOmd7wr8HpXtAMP5b4w6qDrHCAS+5qoKJxdNDdlZRVfNiwGxUlKe5BvHEM6k9isGxW7wWNsZXQqBUPabprE5EhMTSjvF45Fye4cRZHRmYq8HwIGtoXKBiUEgb5Z1DEfZ8+shBKXG6BRldzWkzNteTgdag/YvtIsQy9H2WQ0VXXO3Gizw4arCy4GcAcEjoeYgna0dazaJURoJ4R2Y8mi7LfIHtG7xnQD+xLbvafMqPNLAOU6Cxpk8pBZyG5l0j+1kqXSPpVviADXeYEeF4O46hrA9KzH7M+we4aQF8O4NSlXBSQsglrsii9Kjurone1rzMu8SxZhvXU5PXWamVyrEB5VBIYRaCvqqDKcbJqbwpIZfMsvN6G4T5bujsE22d4wztvyF8DLlvnmaOopjfEydp0H850OyRsWUWWu0ljFQcKoEyfVQc5CkRhWELcCslteJ6ywv6s1FXrqYCGjd8lWP4iBC5W1d5dskDmWWDuFXPP9h5vziaL0uqAV0YkeYfQjOrpQv/ZIChYlYKkgOAMRWe6QoKNtqusgCQp0cyhXdZID85VnbGS+BRnEcJJq8LUqlnZFoZLZCuGSt6pt/oFglHf80zUusz81CKP26/y3BJzBPA0ZUVqQn3s5tLZ5QU9zha+SunSHqeWwIjXiU7gjjYljWcjBOe4Kugy0Q2Dv6U9W1BsB/C/CU/gfBL2+LEuh9kdfbiQMamia43pmnDKBUp3ztKZPDbA5EggY5hzzHLeJfKzXUsIbl0vyaBeKkOVMGS3MWwF+at53wEukUx0GuK4TMs5fGz3KNB75/VIsM+B7JSlzw90dSeeIkLtq5tZYUTayg5r+DkJC3RQmkjeYW6MKBtOX97COoQ7B34tYmY31rCA2VsV6GzoC8K8wWdbCOhMHW5pwPiqxJYD/lTZKEY07qokBZ/HSCa87QaNPYZVQNCrDae/wvzmvhKnpsN9QQhrdvkT5IZECxsqpvKiwS0/MsTq8I2lCu9qrGTh/naeyKNNYlUJ5rhKNG5xlpK3XmGWHb2Kwjww1ZLGX7xkbCEe8iCVERovQMoalpKm95agLLW56a2i1+1f1nQAk3OZPaZ3x3aYWKlcmawIp3na3R2jbyrXUzP6NifZEnWVV+RwXCbOfv03tyj+IfeT08Zp/zKC00Pkus2E71COXDfnR7m6RJ5B+1pD8fbCenAXWqJocajP6kQFh+nWDeGutOziyFMREM008k6fIsarhAm+kckKPyB1rLpmTSEZ48PLydpaHTx21SUEPCpzwb4pnP1OZ/oWh6erLrq4li+w7dJAHSmPaojmK6b37I0/UM/CE2PBNjMg0fR9mPWQ7AXJuziBi2zfOTOZujz1WGgCBzNHl+E82gXLS7KVUA+yesU6/7Gh8IiuSfVNLQWBxRTH4OqsHsTc+yZGSNX6KSiXo8oYTfEkP6vAQXG51ptKv6ptfR5x3yRV3E91GJ5jTgX0SJ69PBzgLSHNOnn5e2OXI+xwJnWze3JbNbjd+hFAWIdbe715PsSfgSkas3Jk+d0bXFh4iEW2qtOJ/zqk/d5ks08rRlW13dJ7h/Ef5MXyt9iLL1lweHk5X0HnV8BQTep9I1es0DDneqULnghgEC2Tr8KZ0SmxYgf8RxiaRrzl6IneHpaxndoQ9JSdJMwFGlAMDr9oaYCS0lhRLuqhWgtulW3ye39JSoHQQEeP21ROvvSXUvDEYPLQzKoIrt4Ggt8hBUwd/0najQf65I6Cxf7tQzLDUkAcP7YknPhiK4Z0y5bc8u0RqhDVqzEvK0Ue+BjrJQWqbQAguD0dewTi+M+y5/idyVimQfl4iZPcfFLr3aGkljAZKXdhyAROjxUH7xR0FB6JCrp0FTggLz4A0jb6u/Y/U0HzdSzdcG7aX0aXdb8x12qKHZZLuPYhxHJbStvtDKlCkUBKOOjyWX00qHtQVXLzcI22GZy9vq6OW7Qkc7aAhkbe6DiW4tJxIoHTXNtd2hhkbR7T4K61INbStQOEVrDqXdaECwTuclcPyFzEGwyNvqjHKMVIBiAJpJpxBeeBP7QZyXXWdpwtsogKmrQ3iS19uZLOaXmBhbEqh7FpNg39o80XNWKCPn2DlG1jQ1z7A+4cMWDcE1cTskj17HHdQ2ubQxdyIDlP2WbGR2EjZw09HAklQ5nmtZHXFEElDpriyD99qSmR56PW85BEg1OYxjInk/PQmi518smh6I13yzpKIx0qf2D+4bmtwdeI7RzDKSqd2XO9tGoyNOTbJxa1PTbhEf2nl9Z7vcKAFi3J6XHbJgavxHvECyIUGT5RGISOsmK505h2ge6tXZOkVYzYqm95JoBPwJDcIXir+litJRWeYxcXled8oKfOsRUEuSaX46rcrfvmtx0Qhe1wAXkcYKqSJRyYD7QkxUIhQqunYRIlFJf3HpqVcSFAe9Ut5WEG2wmSdviWS/GdN3nvng7LGTZ1qNJDC72AeXG3Dx77TYgIt1r5DWAL7DGlTwXaB3/A3BQ+A4jtIoG3J9ud8FTWy6ncuANpFkUDqyQd4ukFubCk4QG0rg8DdOgR2CTEYT8Lqpc+NxkIWdR03vQ3SQf/K2guggV0UU/8AUn8mLnL4ADnu6ozyDXH3T36E0eUDFk2P12XUfY+c6ftFLfO9sXf7kjqAsxPUgA8QOjgCkToBjKK84UizKEELpYHTXr0hKpyCedC6J9MLYy7WLIeQ6kK1XeLU4nlcwO2aX6I8aOeWIaA0EIzSHdaBYByGilQVbBKGOS2Euny5RVObZWV403DS/GaTlX0TN3kEuCPw6oH6ZZTa1fHq+6bw9quj2tn15MfXFynqTZPp3w3/6JUi2kZFsCxO0bofe77meqBmaSM7SEARw7gTBPA+bOVXOTvAm5OdPwWM67Gyq9R9gZ7uIila/sbwhbO7zHCqyUxzCwzKYBTKMW8gy8ZvwkkQF5iHiFjGZxTKM0rEPonhgbsnpYiylrll49owhBQNOGnLYoG+MhYagvUQKpO+3s0VWdb8rtAJc88pg9F0OcukrxG9z3wQFVIddUN6WxrHaNEGxrcUWPVb402Y7fYQT4gD9R50UAXKWExsagW8ZPhTe8/Iqejx9RAw1XFFhRCeYDe7y4inYRnySZ1WRpyF0jXD5Ds5L6umFfAk2WXYCQQjRp3Gw1RmGvQZE4iCuTesItmnjil7mariVgDKd4jsIdnlbAsUmTo410U5BVfKj9T8w37DWk+DKeXOfN0ND5yXGhJc9igN4qXoIVHPJNb/M4lVOa2HneLUQ1wXh6zaWk/ezdgnCg9SSt8WT7DkHsuDHKjFcgjx0LVZmTZlmdQDjpmHFoOss3AI7rCwFEz/FKWq2Zs/VQBBdoCLJveNPUE8ais/TdXFV4WmXur8sdLYIFDvxPEuqJEp3WZKxXTSSYtfjGnLRNQLUyqsxtK0FTbr/zy2WxeBnlvLcSSxTpsefPuZ3DhIZ17oj/kUMloM0lrfFkGmXLnHCxbbeDcHEkRlcygzMtQA/rF4FmCCXVLBBbyPYhqCLCKhc2dswF9oCGUOIEwJ/ECnytpqo0LhHP/NCFaD6zTSGGo15aKJsvacZAbbUsYz52G8rPGyBatN7fvcRPaDUP9dnXlT610DGzlWWzZ9h8NmC+Wz7aK/eC81RodC5w9yhr4XKb+PNnwP5uN2iokDFLI0Fdbkg0kH5SGUiQ/qHqtpqkxe8CUGur6U2XpfpHhRESZIpR0qlKJwyRDOJs3k2HLaUJtf5KwHVYX+Rt8XSyTtyUqgDFp1Bf7PZNol31BSkXIkC/4LrUg4lrFIFqNeavShoFJl+X3ddsGM8h9WqWK0hvHEJD4VaqUNsCW8Ftb75B4qVjv9/nszLan5TDnG4igI4RbXm8+OnJiFYQIR9RExfnBPJUJaNQTk6FivXY/hBkCrABEmqgrU1WbFhWfS9Z6GlfR+AdD1nIP1sWF0qXgfp39cth58H4S9vqz3VersXhrlwc7z9kxuhFblceva4BvK4CIWi6VaA8HuIFN+jdZ2iq6j84cD2pFr5ikVyYHp5W5ojsqnpwpa5MbuozEehLCZ5dvq4JRypfpf6JsyTx+aWQdrKX3bI+Ayqvtsv2WlR+Cs5n7HGd1m7PL/9GNG3okXlWPc0W7u2Wscx5hPXdlmyTcdg5+WHZI0Xtu8E4T/vyKq4QFiOC2FPzerqrc2BBn2Z/2yldT/sJIuIR8RJnhHXA5TFT58oOtrWeM3BHXAQqE3+U7R2VeTiFCv8uVPqkBXxsMDoXvVIDjuavK2G9L5aXINlGW/z7uhAtCvfcZxjfTYNqEayfZM4A7Q8ej0GZd0BIAjAIQAE81ItvxY+qzB/1dc/LMDnvABXaX03f6vBXpNF2V2Nt2a7GTDPgUUYI4l9nsgSB688e8VjOiyqqRcVHtH7Iq+38zM3bnn+RkeJAOezPTvcIhivvqt7tEHfoiIhqFysI6R++WqE5rDu5G1RQgXg3FleNfqthrdhzmtTcj/F7aK70XfVzX8Hbp+cD23jvyjdp6bS8co0lOWMHM81DmdhVvCHvFSGpAtkcfmY3+UXSUxWwO7EWvhQbdLjfM2oQNPFY8qzCq+XLqDxZ1T9zIsfk8/uRZFgwfREl/BJa9fyj4dFcZ4+xvf4VIBIJixn1IqgP9JG4EBAZITXylpMRCAdsBgaSFvDPqqRODP6kXHgkiGNoNRjGYP6BTrqu2W9l75LChSTl1+vOiSHHVXelvZ+bRoDYjMxmry/f54kGKt5sr2/uG4nH3OCwJ+sJmbYs7zY4EmnLUzbnm8UIF5okQVW3i8a3mNKF3a/qIH8GKlsO83WeGaDa1h2x5tPEQ1s53nKabEcRLO8rWdgp57wqH0cxT/OM9xC/MPz2cBJVEVpfvdKgvHAokEnGLyxDxETN9jr7gVc/iWsBzr+62AFHV1bIWQQVVljF2IsVQ2o8TguQkRW/RRl9W1EbQrFFdpghcLt5qiVJRC6gyCRt7XM/vQtQT9xV+/nb9lVZzU/QLcBTgPwMo/qwMcHPp6Nj1vhHoCNOUwHLj5w8WxcTBUlkjmhVYKcmXiM6MDD8rb6U8WbQKeTt8vcNJnv+EVellgHT/25jEd14LNd5TPzfbQJftfFtrBmjbZ++WqE6MAYigllCdWEndLvq5MF33mHGBOZfw+cePBvNarR+hTzU3pUVVF87xjXtn3aUb4CER54Ut4WQzDv9/q4J3gSyFXXiKmIEYibEQHUVjSqlcJAXiVnCfCaQbOn4/2Sf/Rj4u3hpX1PFCPuU7JBpM0FWkbrJGp5RDcB8mgAieS9BygjrhvwwRYphxLMkApQW0MqO3KLzo+r6QbBQhsOZlTFdlAMSosxjWrphsR8NRwRW8PLTjzqZ5Cd67BfKSR/keSFdzJAwk7zvyEnrWq9a4I8687nH9xVPsvQLtE2fQoyPqN2ZhnTycnkTRzH8eRt6OM1BdLLiFPt9C61IT1/Vli4XRWJd7oGjMYpSXiz+cVxXvunhsGdWJPLzChN+1kIEOyl35nhgC+BdQQ+VYC5VmE6IJbkuhFdj4HBgYxgVDrOGNBLtRl3y123YfEclBvNMlUdt/48iQNsa4jWnTING9c/CJl/hBd5wccMsfXdLPFMhiGQta2jLDWh6SdquXlttCpTazEvEu8duo3wqsa7Kl0LuJYdUgs3h802Su5c3P56edXhOMgqeVsaaTHVQ2WtjjlRw4F0ziXdz7289c3d5ppF5OGe0S9DDtVhNTqvxonstPhU2ah2TRBFNH1+hed1xgwVPyXk8S7wmx6v06L0jPjG4WpmqP3Wq/av2trGsvIz+ll+RGSpez5Y6EUmjPEgOeVtwRTzTnyw0FEujDwJa/ua8MnPJxSVmGmbJOReL4NHmA7rRbFe1JrGRFnSFk7SdklIN/Ub4ekcAVvmfoeXb1a67TDCSumRHRbLYbE8p8VyvtnmRYVbvU2c4pWN6h8Wx64tjrM8XQfLs2bbNuYI0maYF9xhMIUJKPUj2fp15Cr6gfwwNNEVvmT+50y8SM4SlK7JXzMEr+q44iTPbpO7uhh7T05lezh9xMKGdVecMDhVWm+yPkjExK1dohLL0/PsVuUjEqapNmMHRk5ydoSLTjVOJwJcHY+2mOsx+HB5LIcSro8VoH6JUZ6y2D06COHOLhrWKwbVYVNVLOsgIUIa/lBHwH0zTSQoo+gkE+3PNIzeY7WQ1k5p/iEq1c8dg4WJOrdMo9HU6vo09Z5BGyMbuLCZ684+WexmuTL3inkkwvId2qb5k+N7FR7FQaLJ22p3JV+RtsyiDqRfz6jUDEwZ4nxh4DYTKBSp3ksmTEPaqLlBwuZW1faqiLJyk9BMYCGmAsI5fqNFhRIM5nDQbaxQgZ7va2+Fm2P95C0ZX3cHYgTankEi3ICDC3ONTNZj8oA+MfE8PbzHXHzQVMGqbmVvw/iN+boHHs5VMhjhVCUF9HPKffSzT47qHxQPeVs7bJ+cKOoJEdvkV0uq6eW24Rv4IKHK84cEE3bRZ/fnZbspdovX0+0nkPHWz4wl8hDmnTnOiPhPsi3OYSztuKcx0c5iNZ1R5f9ye1siT/946jbmh+I4quL7VfJPz3uAC7zIm6Qru+NUR1Jx0hzbNvpjsEdsf0+2R0V8H8IxiNQaknH562KDcgQ/+/LSx/hHXlrFLZiBfqRjSQ30ciiJKhnAQF9vGFXyb3WEwZ6I2bPI0+akd16epdFd2c+kc8goEXkwZRMvAiyM0yeyZTGrcjx7n9DmBhXdjG2TLCPnQ5pS6veXvwiTPQJ/h+d7nf/Mevg3In0bSiqoexbFqFrhaaNr252uKxTh1fuKoitfsVgXJOiHpCpJBCFTilKoIwb+jWYGRm4ebR3Az1Y7C7rIM978zr1ylLW04Fwdp/mN6TyRIwA+oiGSjQOZTlZj5PGZJIXpyXuCRteI8paWlE4JUQ8u6NNIs2n6hDuSbHF3SbIPMsBRZSNpdVSWeZxQEnY6HE1S0igjWP2kW9B1l5uV6/9ptn7RbE3KWsNGNtgZoQpY96EjwsTE29bvL/8PYfimDfabPtNgNwSukTfjMeFGvmTvENHTXhzRcwV5TVjG0VpUDTBF1+Mv7aIhTxvxlldi9kiySjSCJFmM5y21GQqHxNCWQjrZN8eXYB0IZcQMYjOHJv1gM/mK/emb5Yipo91vrxlmNeBhfIZYd30zZGCwipR7WWhr1oWb2j++VY5jLqZVzttecCw+r7a7EJA8vpRyrboaxLl8DRvG1bQGMC8LoGnJhlh5mupX9AgKJEVO9mmL4Y8Q7tVSBbs+w+oE52A/FiTu+VFW/kTFNeUTFVMwcDI+a0BsuY1FDPAbxMC7wWtAx2fiNmAuTFom8Ivx2uo+oS6NTUzga6x7YR0srtD6hJgdacQoGZfoq0IcOa5lw5UG7UHbAC3UKUk2FMOnPkTt8fiAet2hl3YaggbpwgJakQVswZwSy69a5QhmWLvKOTJpv62y2CJu7flaZuTgIDZsQWwYkMdqznq/vHolGnacWEjShxmYR0LTfWKbsejRTfN4tYRloTFugJGUUjI8O4H9mZGpQFqbtD+quBiD9en8hgtHGQeIoBBrDckHzXkLwAwwlhnTuoz9OEmJa0PXgLabY/jgVODQm5NCXF0O1KAPHLJqyCKp6y9fQUWPFtaFLEIzCoPC7ilQulHMILF082W0HzLpUheRV8dpfkfuMfQGHgES4ssOyIYhRcR7ZeyRdn8GFpTOyV4YfUjvT/INdQvpGUfFJTywjANbOFsmFNADfChj8N3gQ9kIZmJF2fyYNN/VWc4EmdBEweO8HlJzIQAMGiMbOCtLJIQa4MRx/pGpjgWq3sxhY1TQ2aR5PqvNMpzVJJXrxtLxhJQBQHCQu0aQVkwGtwFZvWHky8s79RDm4E3lPBmZwZsqO8OYbcpKU6bhM8RPwZhcVnmxjd1nzPEQFmDM8TwZMWaXBnMpK0qb7FwrK3lA8Kzcwlgdknm85pIx3N4r68QcZ1sJXfdBqr1LyiYPx9EWTwoJq9uORnWzp6oEMVUHb8NUyjYg64sZ41qQhkQFvo3o9BTatQUBQ6Rg4WzIAeJfYp2pOjLDWlPReT/XGzsimyU3qjfdqhs3Ax2tzDnai07tNmtDoq7KdNTpWzDXuQLQpPtxif6okwI1r5O0nYZq2VAmqLJo2j+ArgCcfhKdZJ0R6WYQekYkMulHV3/pQ9Q15pvkARVPNOyR5ngzAlYcoBxOTmPUEK+x/ZzObqTozXxHIJDORlzF1Fuas47rbJ2i8wptjqqqSG7qCjUPvq+HEh3DmeBQ8KG0ugODGnVFvvcwY97Vk7/NCOdbCzYsYGSw72vtzgJph2JoyJLVM1oIXpzPtbeH1i3dWJbga3gWzXl5aauXOCB7Rp6PhV2YN6RqIe/KIry3h1bWtv3e1tfbmzRMIFRQcJuL8VXajIWNbGcEpXQU83GpdL5MutDV2RkuNZSJPPzEPLrHW7lsDAsw6P4K0ZERd2Q/1HCQtKKCYV0t7tomLW2hO8PC2hHNx8va+TTpCltvpzjbUPhCdWbi5z0WxKpxLMTA+yuQV1sUJ7dJEwiot3iYMrC6toKV4YoOTK3pwR6yt9mI5mN0szneB5aHR/KFZlW9lnCkjP0ccIFvexVorJ76OnQHekRntCyXXyoew51h4XjwhknvYAw7upEoGdxNtqvJu9iWo+yW8VqDl/7yK85/7IvvWCZ8477+GjxLr8JuW76K7hTxmUTYwJfrLGa5CoaLA4ZfanB+i4okyqp+Wk7yzU2SUUAr1wNTPArCKVA40NS4Q0v7Mth2dD65YDunJj3bJQ8I1fgMD3QGKHaC4/f4fGcxrN1YGnt40jMYVRMDvHr6miVeq4LFsxNLY9QhYH2MBr7kZgB1dDc4HppTk56x9XaN9113AAexH4Chn5OA3x2p/oxEeTcUvaXOFMEyDK40xwlj3DeWX8L6ZjvfFotgZ4xt/OBocP1rGbdasqYSmcUqoXgCLBV1f+TLRrdqd3b1GA14uZVkxB8Wq4pHsfTisjoamOr/Lla0ndLkF1bX91snf5f/zNI8WptFzQGhwfd6LaDVS0YQ+WKBc5TdmYG9lLQ2aX+XQudcryKSradnC52AGYMHll4ccsgsLWHf8LIL7suM0gumtNFt06jmYhx2laACj5pEwWazZ8iYAAaHOKyHtOExCXrLXB+Uz5bWAdVDmYFF1VNl0gG23g4wqM7QJUBOwJb7aL6S9n5WJtxfE9UlekjQT1M76xhasfc2gA47MNfCPrGicgTzbdvwHO0dS35A6fa2TjMS1XvMVEYcJK2uZVqmpjP/yluXMzS8ZHaMrbUDm5vPtfNswfhNxcXY/zP6WdLnptpw3QIkxNQdkA0Ti4j3Kly3tPszcKV0TkzaXjxcN+l9F+G5ZxwVl/DAMg50CNcNogf4UMbgu8GHshHMxIqy+TFpvquzfKIVs9SdMHjwVCMLpeg8faxQkUXpUV3dE/I2Lrxc2lApbYxqQ6RSVbQhn1kH9io/idWQZljvVnNsYxtZTACc5UW9oVkGtAwugkLc3EPZsC6A2opPg1iD5Z2YgbPkxN0fNrrKt0lsyEdjWCkjUTBrTuKQL8RKcC/m4iWYwPvDTNf03/dFXm/VnMQAStnImoNYpAD7MH3buT1T1v+5GA+YD5Omh1q7IMQarjEQMs2QpxBfDWYZ8+0o3wFdn1fgjebDmO92QP1i+EWvJTEDDq+CMchl3Afy9Y6woGQMs+pw4vyYNE8rLMaKX4q1edJRCBhiRQpnw4YgYvNco4H0N1UvZmAkFXVNmh/XXJijtOeBMVhALtpPmwfc99m4bi9PDF34+q9ldIc+JLg3xVMfE1/uSKmqpUqAwFZwSRMBN6jIaLB7XGo0lBmY1mgOTfqxeAYEcCSN5LPip2YZz8W9TWsA64Iye0f5djSIpZh2NG8mnaAVlt3c6X2Xmkc5OOn2bnuBzuPdHxaU9HyuHV6ci31iNp37nAA5AcPto8uctPezst0eusq9T26rk6hYX1/gLt9HJVp/T6r7gYNk7KKpB7FlV8WGK3XNyMQixP3hHlYY9moG3jOcBiNOBDEszpgjJaJnIR2/gLVUTOmqNaobBNhTtgqWl6FGQ5mRp5VzaNKPrs5u8fBXdonZMfKo6mzcPG51fxRR88EsxdTgfJp0ZlRxWbX1c14hkzPSACdVWQmItcrK4N0f1pT0fC5lVZyL3T8jXaKfePlc5BhB2a0fre1dVQliQwDehiGVze2Vld5kJDNwq8n87YUFHxqImSKgrWks9sDzj01DbgtGbNYmaP19sqXu52oijcHAUPMthFVY+THW/dle4I7PsF7hedj9zaXrd3Nm7lhFxxcjaBXT2dri4AagKOoSrt4dFgSHMCMngnNk0n6PYFn3AtKNrbHHCgcd0NmAx2zusxLO7KbsyVy6s4zGpjy13QHPlUtU1UV2if6okcnLCBgcVgcYSDvNGWxiz3Rm1Rhm0ZZV87QXevLQaUO5J6sQ/NFeSAFopZnktNmTqKiY9NXKuxlFHVhLGYPbaSrypuT3h/Ls0uE2C4OezaKGaKfCpBdDrQVVZG4k2m1DWmNyJtzP/UM7jCX4dS93EWEUOrcKWYXJOXUffS10g1iCTffQ8+IiT9NveYVH0T6wJh+OsvKnKh20vA4YjogDtwpDpGgK4tah8zvHsAZDmYFnDebOiG37Wssp6fco/pHXfExs4bNcaTdEACrxYF0rld60dUh7EMa4c9xuO7wZWN92vo2UDL7ygtaUuC4wKe4uoidqZTzPEoJXd6+jqAXbVsYV7MwrqsbMLzaCnMyMOjOLucRgBoz6wdTbGS7srvAEtjHlERkCE950ujs3bB7gVt3SWF4o245uAfbXzbdJl/i6i60GMq0PeO4/5nfXzG/CM9IFoKgD8TwDYsPnqlYgmyLX+Z3jbIPxzMDMBnNn0guu6k6wr9bOBgFPxLD7aVhTjWBm3txLc5oRF+q4z5LrXLltJ7IaLMRoe8tgNJ7Iqr4p4yJpEtKbRVkDq0hDxrDQ1qFj4KYWCr2m7MwMjKYn/l6wHR7vQ1ShT6gkTvnXZ0W+0fKdog4cEJ4FtwsDL29ofrYz6M0cJlQ98U16wdbbFea7ym1Zb6gxKeMxzSzOdmJf5mc6kewmfRhqLXemuL1NUvwFXet8agRI8DTRAVmdJQTMs8e+knZhjpOAjLBGZ9OFnQaP4pSLBE0GpTiVQuDwuTS1v56UoLcMpr78UUE9jllOp6p5stHjSL3lXD6qvCDps5JNVDydPsb3UXaHLvFSO6kL3ET8JPf90NUEnUBIJSvPD20rIOu2fZ9GFBr3aQY2NJ4Fk74o0OwGg9I/7DhzVCU8S47RL8yLYGfmZkKQ4BbcN6q/GNsdR/GP8wz3Jf5hZ2DRVYRYUFLHhim1ze6V5dl0NDPwtul87oXlRjYYnZ+npt7MPL2Pzp+GY1mQoffQFfRvNarR+nQTJelRVUXxPb13P0sUxyt5FYiJQWgbFlY0Z5vafGkO1g9lBubVT5+RITNZ8MQFD+ETWicRUX1UaSf1VWdk4lGzADOPRjTZcyXzvi3GndD8mHSGrbcL3HrdDCxWR8+WVdBwpiM/jpsAuHDU553TCnQjmZdnwfky6QJbbxc4lVl8LIvZyTeWMPNJVbZVgJ0VK2anuFk9osVEMTCnJn1hqi3G3uebbV5UuG+3VNmJ79G6TtFVVP6Q8rW8CsTQI2gbRlY0A4VeYXs+jU1M36EZGFBPfJNOtPWS7I7UXIz5Th+tmU9eBU5268h8imaWYT59h2ZgPj3x9475cENp3jjWd2yi5gmxgpzxBlh73gPagfRQFYMvv3XrhjIbz8pnzdAgdTvf2f4U16mecJ0K10BFt2o2UVF9ufkHiitShB4xI8TU8BtlWV5RLH/9WqKTtCD8Uf7+sipqkZkJ6hWq2Bxw5csXzXeGr9qUewKPctWjx5OoQnd5kSAQS1/+pMWFf5HHuBCatkiL4mMeR2nyT7RuZxDuFA+l79rHKLur8WkZxNaWmXUOraqCvjguKfNJu8fBaZFfoGKTlGXSZQeHEPMwWqSsIwGEcOzJoethnqZgr/B3o8rNI2sZiu6tu+GQVMPRImk9f0Ca9M5Suo6QM61k1TRlBiumDYH0CVX3OTjlYwg9QixFEF4VD1i0gj0bARgTm4qrrFLRvAXRojxO8zuS8hLC1ZXpuam5XgBZqbvn0aDoUipBOIYsajr6qESnsdy8SOKqLkAcbZEWBWsRhPCMTa5m1FV1awSh712U1bcRhQXXLVtuPHEkKltSoI2ELwEwPWqUJg+oeLpKNuCw2XJTKg6BphSEZMN32aLtn+ufJWklEa+aOqaNKtl9DGPA9Q28jjcAMFPUqy2Kk9skpopVP2RFI3AFvdAFq32hl6egDFbAOzZm3owp8a4iUJEbSk0RfYuKJMqGqBIn+eYmySIZcfS1tA3/rY7ol69ZAooGttx1FBZdN23CBLc1Ur4zNE6IRefbWDA6wdleT4NCs7+61h1FElTgMyGst/SFWjSf0c9SJm67Mi2S00csFrMoPaqre3IAbBaRXDNXwWsb69OAQ5iZ3OwmaKTHQTYNux5RJMNg1ov3RV5vpb2gpVpENPgHhKONpGKoJjDpYeB9C85yqsEOJJ+BscP5gwyxqxCa0U+2EJiUbiZoSHoTKZomyYwGjZjWAKYXmP7A4BQlUwOHeO6GSGQUG0ee146WiRsMD3MU1lnbt3FwPrh/fBBF3dmFD/cEnmHE6Fy2aKU7jizumJa2UJQVGTdJouhYtmGCW290GKIWgHYHNpiEOSpqKVSja8KcaI1bYK9MeiM8XpZK/rG/p1aDYZ8JwjrL+EmmjmrdYzGQXsPrO91qZC5NwJU4ukrSzmQq1yqYd1waNF8LBZq+UL/xoAxhJUspEngYvV53j0/fVK+8ga2QIwADE1cOGyXa5xpakxZ9PyAx2fRPMkw68SmiElfal7ZcbxKD/W5BC5nMd9rCAnOFNttUsgogOFMr2JMKMQ9jfAxV4ORADKQ6AVsrjIdjCP3Ai7wsccVUgZKH0Q+82U7kJuERgMFhGHB3gU/FoNuSOXoNUv2Zi/EVA89YI4c9LU9utlFyBy6irszAqko3FuWCGYMYnU4/ogqfFnVLHYY06HNU4tX7HSV39yAZRwCm6N4lmBtKSU95GC3SkQcMhJHzONIJ46csVsniodjg3D++aobP+rxjgBFSxXA5FwAOHXPHrL6LvB5uMpk6ikvJoQJ/Jc6GjVLU6/0m+iFLb0mFW3fTJjqXCbaJ4TaXd3oYE8uUkOxdsp6KMLRmfGAlKf34K3Ad9WDsE5OOv46+5q6bRfJpasgHqa4IkRG4T1cQUYMfICQ3Vn9i5mmqZL0xgGIoLBxImeYeX0WNEYqpuWgI2t54EsBDZ0HUPWcgZcPvPRE0RGBRAWQAKelAgvEl/zWWgVgWxlgRZXwMRKoY1JKPTl8Zop3gr6Cgn0EL0LIaRuxPVtbX4br3yQAoCQIqhgbBg/Ti3DFU5AJxTkyhLmiOgjY8iHwEHCRED8bvRkEKHtFMROBcY+SkGAPqxzGeWm+yjNEBxFFznQOF+hftTEdF8gBQ8sGIwBBhGGcvBWEAXABVpET2IchxkqZMTj0VVThQg+GMawSgD4dwJiK1nmFDgAwFlQRY/aj4Kio6DX5sBuQSECu0wBD06rzflFqgCCQfiAALkYbxx1PQREQ1sVZIGjzJN9Tde3ALhOkhwKnHwYN7MwyIFKCPlNQuWnPjejd+HQ6pzhCcQukFwEEluvcNVGnQEDKALpzzoj9tRt6J172XIkAdGFIxJLACSCHeiVJFKBgrdOKQoAtApc48racSFBJGOR4uFkwgKnGxXkSsQTax9t5CxUUCjGJv4UBB2cN4Waq2KR7VtOzSuclcH223aYLWVznbT5EoSnj5qFTVIGIxbtwKWimxQtu6dAocKMfeqanYCISTjwkChyjEOdgqqARinJuruO6aMNa4ig0XjGqGZK8xYmjPU81KEEIOQtCEhj20zSi7SiEp1+OcVq73DcPPCRQkAysYjBCqF4BwIFqAdvA4Q2kR1+OnEVIdYgyn3etH4Ar9wUhxGCODSDR+/hGKNtJnF2zmeTnJjKprB2+CRUFg1XMTPeWNGpev99ETmvCz0nZCrwBLq1gQYFzTiOKWJOZamFaMiq1bkdGFgFOSblai9Qr88GRKSjMRVjssoYqCYoZHDinmqVVovmE9kwmg5qPSM5gjuWblrpEyP349JyWavI52kNKqCjKan0y0jcygYUN90PMhCG43UD0/+hFyVraEHyUaEFJTUTtkdX0FcaUPLvVk1rQ5LcFVr0yvZW9DgbtxBzSKW257bOC9uvrFreqa3aED0BWrGVMEXyfqp8O2i0eJzZW71RSecZkpO2I8pxKm8jgqd27r9IG1/KTMgunPtgy09zmZxSWXUU3/Q1FF8d7b1LZgjEJLAVNMCkqrX73rJ8G4C/PZMVRd0u/fJrW9qKLfyaeck1mVKFVHxsENnKZjhMKLKiymhSZm1AVgdrhgEVNNkcMKcV8WDmvBic7Lcn3XulJ9Na7rPHilojoFtZWKKQ8zIf3pc+1rSaHFXKjxWJNJic5ijrp36/YTpe6BfNK0XOKhdBpuC5ay31TAm+mdc4vqLjKN1okKBlRczkHw4F3fEDdHddcHopvPkep6FW22KRoC+cjZh4PUz/m4gjcLcegg5VhGcgf69EGJxglcAfpIIOUDgitA9GHDJikoJEE4w8umoWWFDiQCmQxFoedYk2VWXeYSPSTop4FSyAFqV8AY3tu1FcY6I4k+oHR7W6cZ8VcfFWhpJq9pOFwpgrBUlTejWJuyZhzI3cVEU7qdi0Dy0QmwEL2YKG0KQomoJnY7Jw12rtxDsDiYHgKcehw8uDcXgUgB+khJ7fPwRfvUVwIpHw5cIcCrl9mf8qqCASpfwZhVlA/ZqD5EUU24QwWVzZqc+OVMHyNRSV0ASj4uERiiGxu5UUEkANkcFKHRHvUk4cA0wxhDS4nSxaHUUYVDNwdZrtkglBKasDCaETCgEmpEejKwSAAajIJqhmSONqankjMaGKN5bMYShicaXDAxBIp6CQ02NKpKbDBwJmudGUsI0cGgk3EITGAH2tC4ngaPW0E4+WAgcIg2XehXBV1AVBM/aG3aVMlTDkLXfZUUNabBXLITipN7PWRKkHvMwxUU5i5VPZXHPB+M18B9Hm5D4T4/GSXbqMaGZGygLcfX8MuEBGwaAKgHM7PrEqQHHSm9eBDN+hkgpatQf07lMc1DAoX1SAQy6bzCamRNiHlsRV007OsL3OP7qETr70l1z0S3FkmjqyIfnKYmRDYmkreCajrEMnYKdaqHgpRfD4HG5TSEK+gHCtZT0c9CMqnbAEgpnaNQlPzKTqgxOce1LMc7qjwlYccNTS73SKx5jehnQDRyaoCUSrw28L1O4jGYpiQBEBxfqY4q4eVDUlWDKCWJ7a8gmrKFifVYqG3tCtVXshus47KxxOs6V44h7KglWkpCDkI+rjGgLAwd+IBVhWfKhckmh7geUk7IiTAG1I9hBK8iiV5Fg1FC/tAyKjsfnbuQgFqLAg+oOwNz8F7naR7XxEaFUSYQjTQHIVUCAqoAS4VxvhKl1AGRTiy0h87rWUgKa3BXZMBIlrdPM7MTnwZG88ZcBa4SIdJasGwS0tco5ZMc+VxvxIU+qNalHNhikKrV6Um/udao0LDCPiGFtRiXwlrhSbGZ/FzyNP2WVzToOb0uZbMgA84tCnCFq4m8lr8biwK3JFqyJPCyy44AZo+6BhJWATuEaV2FfDdEARJZliJLtZ+YtgctciDTVwC9ZZz+6vo8S6okShVHKFUFlcKhqAcrM0LyLqU+o0I/7WkeTE52LSYW0xNTWtd84DIUJiQ2PfUbtghQXTupLoGfhxRp10K6NJHmKnD5oBW1wFDQoyRxCmKq8EI6ppAxLij5VPoRCGc2MJVW5ESpuXQhHU0MaaGjgXbsc45ZyPCndz2CoTXeEGAlqYsFl2ZJ52oBI5+YcuM8htdnRb5RkU4FrtDW5LXgZxdc9kWlP7Mc9byku8otCMcAG49tqBOYaAziiUnWZ7q8VhhRRCCFgOVhQXHNJN9UCWsB18QGkz7HpvYNjwRStfFAFeC9LDVxtpUgnMGlmTwqI09kkk1UPJ0+xvdRdocu8TQNGTOBQ762kuJMrqsLp1vJdUGk9XhBag45Q8OSkv5hTMMxtOEgR5VCUG2McGpySTKeKnULbR35SHVVwRQK0vytCpJqG5o624SkfYXRTlfFfrAKE15Qos5j0AMzqF6fJfB2ooCWD1BeCaKhLPergoKKBiZ+TAu3rHuabVDLdrC6R9vBqDr3c26mF9fjZLpKuo5hjQY5qqKhoTHlxkgBenEJhCfiyFFKY1OGZCvZsglLginZkW0HjL4gnzIH4o5S/F6v4nu0rlN0FZU/IKoqoOXDlFeC6MgnJVbQT4EY8qNgysM8L7ShnAJaPkB5Jfj1oDHlFIhno9yQufn6oku5LKMbAKsbnFhFTrNRomkt2QDMkBRUzoaCar+9brCQu78oyVDRl/32mszEJmo//PYag8RoW9VR+ilfo7TsCros3EPN9suL1TaKyTXW/1i9fPG4SbPy95f3VbX96+vXJUVdvtokcZGX+W31Ks43r6N1/vrtL7/8++s3b15vGhyv45ER4jeut31LzYbJlZKUkWt0lhRl9S6qopuoxPNxst4IYCt8xKm+3PwDxRW9BH3ktrzfelJ3DbZRLpq3V+IcEmhicu/Aye/2JEiaoqepV6RPr8CnZQMNz/CwyOTTESJmxiX1cM1VHKVRcdEmhu4k7xqPPE/rTTb8zfOevPbqqazQhvweY2G/m2M7z+K0XiOssye4drTleiaUWmAuL6Ky/JkXJC92hUjiWQ45BGCOv6s8Rjp8Ncd0lVQpR8z2kzmO43z9NEbRfDHH8AlV0X+gp5+NEYrFNC6xw/gO9YcwEemo0A4vQDPmszmuj8kGs9b6Ku+MICxGodAc7yXK1qg4Kr8nayqfWbR8mTnWpsbf84wbOvvdFtv3Itq23h4Q0lGxLe7Vff4TmCmh0BbvcU7u4PkFzZdZrOUiyQssTbm13H+1XMtX0R2wnOlXEdNvrznxzu8gr4UthNvQ+Q3JbLuKHpX52Cx2rR6TYJI02btUtafZwcS9y3bXepeU2zR6al1dWEzjkp2ZbfyBuGn5TXSLxGGSpTV3dYKpc9UYRfvJQlEiROAH0n/cGdb4mOPOJ/9E67b7vuKAx+ciFAxwTMM5TR94HMNXC8WiDUrF42K/W2AjBEFYCWujlowwcmUOWCUI7XEB62ZUsDtc38cM8+J1STA0ExaXVt1Vmdj1+KROmwymEFv3heZ4v2bJHzVaoZwc0MdYuSJznGdpdHe+wf0hFxfi0IFiC9W+Sjl9nnxY/shxUd+kSXnPa8XM52es4DRSZlUV1Bm9pPa2APsYh9F1K9OimWbNh92Dut6Ly2lcYo8R2DW4IhuzD/E+u0jru4SzOIxLbDBe5XUsrCvm886sggtUbJKyTIbQfT4rgMfmwP16FLu624U1cw4ZHllcw9ed4SBdsE5z7lH5yBlwjrr6rnLNWYFQ9+CTUzlGJRYGpejx9BFttpxxjvlshavdvpsnDhzCUZk5Vupez2HrvtlI2WZlNc6XY0HLlky9gpeS3HmaekprjMFFQoPV9kEfCSXj26sNiEn6omW0cGIh/5J9wGLwgno/jTrIlVms1zTNf76nD/2v8m95xS9dsXiOc4PciobZHDM4+lrFvCmNLbGx8axBfOz3+U5zC8qb7i2ur9SB3yAbyh5Z5WkkEGmRx9B9m1PyfK43N6j4cvutCS81QjUuesZnduGtuS8jsm/RHdlRjWI6pmyWAcSaQ0n4idPQGB9x8e8vt/9Nptu3M/ffPfT77m55Jlp3zfJY2O8WSuu2f0E16tLw2UYBPtpui/xBtDMM3y3GWSC8l62/ZMI2Ny6xMNNu1xKM45LZuZRnzuM0v2vTYjjwpbL2RDzZNHdFXM3GU8UWWPgC4SGQiOF8r9jvi8+SMiOPibBW159IUjeNCmJ6+Dyv11czepFx2O8W2KJKMFt038yxtOmM/gvh40MVcVclQqE13s+5HG1ftlvczSR48mV0JapJeb5pX8L5Q6GFJ1dUtqPhvLiY74vPI5NiyWHqlLWn3UtE4TIuWW536rJR8eNkv+/cESWMKdxDTZ5bP35fJxINuSmx0BtLkh+KPzAPXy0MN82Tq5HNpvm0hNd2V+csLzaRqBIIpfaYV1FawVibEguT33qTZJ0kGlv7RiVWl6LwxcSowKKHXdgHnpCjgrkvJd6hFAnvBvqP9pcb/XtL6H6jL1zqkvJjhM8G8ImWK1ryHEq68jG/SzLQiCuW2mHuYkVJkQsAO7NXDUFRfPYqSbQXg61KWnOanQrLwLqKxIcl7Pd5T2P0cZm4GJnPdrwoohq+zrtr4g1iG2W890L30QYPFnCF4F/LfLa6GKoQ/vaQZDHgZs0VWvRReAdyYvkGpF0Ib/idtvtqjektiOmtDaa/J1ti+olS0cmSK7LQU+7zDDXXFZyawhZYrJ/oEcLGfJ5j31nqpEHXgK/zfbuSXA4asprTSG9RttnKNXpPXbYJAIEr7KHIFifswMOXWewtP/OPqKpQcV4CPs5iqQXm+wIhFW6g3OqSEhVJDGLmyyzkdk3fVl/l3yJOER6X7Jvj8zNzEOgY/ROq7nNPR9IxLpf3YxoEuyqjpK+fHV8+h+fO87uMBHO7J2Ek+FvMcdHucCar23kyJovKhS/V9acy8YZTogNswzc3BXpIgIPYuGTfxPlCzN3d0frxdYfF8f4brjoNN4f1Cd2NUBpUVWyNqiWgRQ5FFjhbJ5C27oloR4QhLGRBXukbkQLZeKHfcURpvjwfn9BDKJWQ7xr37ih/0UUUDeBB4O4wMLN/ABmJxDugK7K5ASvwyOh7dhoDAHS6kcCYt/ItKZObFJ1n6+QhWddRmnJyHwSY9YHCPY36Jln3YqmFra1OUylioXDJm8aOidAG62vi/SBQvPQDi66OXPmEIQ4bjIa3qO7UvQukLkegdsVD2CtZjdfbqt7AGhZT7KReSdDDEPa9p05wMH1ACKcxyBuRAjnYCI9i7upqXLK8crL6UXMdJB8s1keU1bdRTGJmFHhHq6ALEBmMeSvvK/4xfPPFxoOhy/7OOy8M3y3609aBlAa+zMYb9o86KdCX6h4VvQ7G+cVCENYtDOoGjH9UbrF+ayy38MKPiaJxtF5z2Pi1rIW2md0ukDs/u8N3C6NLW4efWfa7hf9YljbLk4k1P/IkA8pt1t9j9+BKgh+GsKfG6eM2Kagx7F30VMKU4WHsW6HuKRQDtLbkUBbaTVSuIqxsIZhlgGIbnw62pnCpL5Ra9Zq4HB7dFQiJuqlYauff2FcUvWSBYpt12ac95BcmU2Ajv9pKJ09xij6i7K665yUYBOHawgUqklyYRxmMQytUwaBoBEkMQVh57N0n29Mswuc/QSiOimxwysND8GVWPiIJWclR2tVurmwEnxEJ1HI+n+flaSnQln6yMSb24UN5NuOKrHQyYnjOHvCKxZWbS0ceuxTIxoyZxz/+VkfUdMPbMUdF1hcetP7RQ5Sk0U2SCujlUG4twYOAISzmIckU2MVSi9NA/rMZe+vMKVw+AOVWp6Tk9onaO87youvfMcJnU+GkJAe0uLCI4h806DGJxS88+uMLLc/b8jRMwsHbPGOTvE1qCsFTm2zqDTzvMIRtC9GjrgUewryFrs6qQlyoznGJLUaapaDIUzF4D1RuoRsla9T1rEXBqUcQgCUfoXWLIeG3aqDYSgqRffi4fjquq0pwsxBKrTF/T8r7NCkrBXoexIIyjexNEV7+F/hcKloKYQiLyxN8OqRVk5h/DzYqsbHHCqiscXxJ1wCa4au1dfiE3FhDduGmwGJP3qI4iVKgd+MSN4z99eRVsgEuL5WQbi22F5ja9ng4Cw5rza2nWYWKEmI0CMBKCyCCeIQFQeyjBLSyCBi2pwK0OpleJahZh5xk5Iqs9BtUVkdVVSQ3dYVO8s1NktHzPjAOLbDVWLBMbPKvHW23acKfnUAAc/zfUXJ3z6epaL9ZUAc499qfdL8nax5J+8mCXsB4PliPp98j1OJFAebQlkqwSIGW9HQM6lYW6F3gLkVpUYw1eUDFE5lEwZ7IldnryF+zRLjZ58tsTkIlarczwHAjljpg/oSisi4Q6ZsE+wjCoYWjjeh2JRQ64CU/lLhZAAv8dbZOEb3MFa2fQqEt3gtUkKAAsNFMAuLYBqGBuokewnkUeWN6w/JXORIWbGfcmvqN28uvqcPi4NgkrzqNZxP519P/v+0xeBfDlVndOGBOiTGJBKcVrsi+pzLEULk9dkj88GXPxw20vZEtT/J6m2fiGyqo3EJ3kGB1w9bNAnWFoMq/jF/HEJYeGM2dI9YWIA8MtnBnpF6gJGkeGdL2Lz0a8R8UsQxfbZTPsA+ojvMKH9alWIFiG0VrfQepQMNnS1yr6on3m2O/2xhgk0gwutJPNobEhgtlfphQ+cGhUY2rcWGW3V+KpRaYE+ouJqAcPtv0ErNc8k/e5tt/dXTgLK/yFT4xxxWMXwdr3/8v0MWVUGhpCL+MMuHh5KhgcWfkiU1y++OquYsGoPBmrv0wKd1GdVp9S9DPT4L+KhTujCrYSk/Pdz0NEpd3PbKa06iBbXPHSRbxeYu4Ipu7nA0Sb6+Hr/tofV+hnGQRzAR9d1Rg4xX1GXF+Iu0nK0+tIsrKRHB0HBUsKQI+oXUSEQ4Hnv7yZTsjANiO+UkBFpODKFBXn0YekH5zkpp+2ZnZaW/8w0jpES73R5hzy+yA8clDHiH2LgwD++TGc6UzmFxWurL6rhqAQpttWCrIDBAymIMRQo3rYCrYP1PB4Yj83I/Iocw2C98Vt1dcTbSbENfGDEKPG2QllonU57Zp2bWaGzZmIJd1imTxwA3ArdxL4ZvcUYHFXUcTp1MS1kwstbGotk+dYNRAsc3lbllh4U0FLZurWHzsJodzae1C+vQYgnBqIXoiDNIFfpS1wkG5tNRNALjdKsDcZokKA9X09AAWDgOPVRGJJ2Pm8+6IZMZXz1MWM5hchLCy+q4eaXDdvPiAHr9FaS14XIyKrFWbjzkuFCU2W7SkunRetiZ53pTYf94ZHm9FX+NNR1zpgliBBnTuhiAVjt23BbXuiTBGodDeCxj2/3VRiGC9Z3c84fwF0YQxwZK0QkX/EITbj8VSi9NMskZX9/XmJhNyUXBF5jjb4GxjbP3Hhc68z/qsuitCvefBhiUDy3gOewiRr0U5zQ4wdAAW2fJse1KrXDcQIUsXW+CAj6pOUqR9qY3SclGgxhAoxu8YFe0anwdyOB1jc/E71WLYfb1F5lrn5lR3Xp5hkVsPMbZ4vhKKn/ENWTvGcBdlAEJ3pl3i2iwk47L9V110HRjYl4HpI/jbJKaPDxjtNgArw6jdmdoU3+6zNzySxr4nWGk1sJZ3io0KJrzz48ts3XY7fw+J5+6o+BkvKtVkeaZqUWB2ydxihW6aFQX3wYz/58+25v+E5Jlw8xR8HIyD98egvixHSW1gUYyqVV5Uwg4xLtkZHm13FppCJYDOgvG4Kyhg5aX5b9l5+RYVSZSB0XWCzJcCv/s8WiGdaG/0jKvuH/F8nsjsIaKpPbd4RvsaCeWoLJO7DJ+eO9dJfnsDyq386fYmDs95SSOncow9fF3m1DlocP+54W6huCILOTVBDF96Fv1SV19uKQqqw0AxM0WQndn9WNbx2+dYTA47mrr60rqJfK1Pkjf24GgysXY3iUoXTo/bR1NhW0lqHoHKzbGTuBz4kxDRnv1uw8BdEhaeg4fvDtsVE9ZZekXKwTxjoww/4UHWXIDFNvsqC2FAWW6r2i3BTT0MJpHejVNoMBEuQTepHOf7IBHBENi+cTN15V39UUcFKi/vbgA/X7bQ8ph/tP5HXVZi9iGh0OLQTg/XMsRi6TwONPOpjHQ5QBvjqMDmYV32Q5pxViic0tF1qVcCfYozH1nY5/6yl3vyqtPIuK6993XCG1VGJRYOlmWfo+xrwdkZ+DL7fgooPfFB8V34Mhv5mVWoiWnKy06mwMolGYis4hJW5fQRt18KWgrz2UY2HkK07GiIFiZfgo8A69E4SDBF3d0/boOP5BwexqmeRLo/gwz3XsTm3mUhVsYCpfR/w9RhcWBkedVp+PhjlN3VgLBhv1uYdsT4GNaxMehrGuCBjdUWVqfcjt18WeaFyq7HzG5ieeSbJmKAeOcxFFngbLORt3VPxIs/GMLi4DmkPJc3IgWyOZ5ME/cj/Cuj5x1LZ6EdgSirRRalR3V1jxttfb8uUUxp6bNLqDA77Bx26KbZTTotQ6Z9WD703wgvF9tPdkcQQpXzNaHJbcIbQaBye+ytKUXXCABm3tYXMrFX+Q/ELUP2uyW2oxjr7KUM56jUSjN+SNaokMVFgcp3ZrWf5UW9uchLzwv0Ho3DOlbUnWbRXuXbJOZR9B+XWvxiUH/rRO4XR+s13pS5vZD5vORmvXdPgilfUrYIsDYoHtfFIak8zeqgLfIo+o+LrQ5CAsgGPyqwOKI0ceO500n30UIp76TnWAvvv1rcMiT4JMzdLzSfbA63ZUUaFg+3w3d7bLKZhMrtsdMIPSDepuQgs+xkVuQvrFzl1Kwi6n2R11tQTvUl++CioBAs7d7DS5bu8xJCiixNUH0aFRwEliHPHCK5TKS2UREQQG2jeFzFoaTyNDJx9yTY8+buqaynC62a1rnfZ8E002m/ViT1plkmtDHoldWowBKf6NbBfF7uWjTMyauNjdvaEIQgT0KpzcVXE3JVghootpyXVRVVtYCXK7LvL4xWLLUwITZxbmHEQqE13iZ+rtQ+KQOy57iTuihQFj8BCWNBCJsWmnqXWCjzmNkS+z5fRY/tfgSZF+RQNv6D4LtO5rMtX9c3VV5F6XkWp7hjEHvzEI4tnD7qWugh7Fu4IvX70OSqscCQni0qxwZD2rbYigTF2HgIxxYUY+EhHFvAdcW1B0M4yics5xOiY0bpGUIgyQzAQ7QNEtMAPETbIJkNwC0sqU0VTjMdvlryB8x1LpwGR9flimx7R5Yx1L/muzm2S3RbZ2u0hl5082U2WH9GxfoiT7Kq/I4KhOeWd8CRgFjsc/co/pHXw5MJ6TlPDenRovgUWgJiv3vL3LmgcgtnntvbJE2A/E+jAgeNfCvRyLfW7kvkXICFLj74YnFwglkEUjHUkBb9x7UAd+v+qx0mUQUdvlpiAsbsNsJPUfkDrdXUlMHY9fnk4eGt2OPmqx2m08dtUlCnl095xofVAAFc8f8XigAq8+VuHPwuKVBcvUM3Ce8vJwOyMUH11Y5iuoN8yFPAHCWD8mkJ4iA5lFNLx1H2QzxqgQDO+MXFCgK44T8/kaMmZU5Y20w7Usx9uRP285uIN4/yhfb7AtVJWn9NeIcYQ1istBrrjEXyT7pM6ZuPKIYCYKrg/FsTmVQN6d/iJSqFGA86WBvpuCXBdRT0hCF8WoBGJIey8oTotTzFgBRgNk7mRXwflUhqhQUBbM5VCewnPiqwt/g1Jy/Y3teV2WMlBy68pLd11ezRSiudcSWbC58wyRP34VqGPUZdok2UZELsRQmIeRsfIvI+rz2sf86rPnDmuB0FmIXci2O0ra7uE9zjCH+mfsIfomz95UE4BKhBd+YKqjvkfy3xee1DUlb++QkAlC5JCszQTHNhFTalI5Wr4IWI5fbkvOQXYq73yS09swVkLgClC3OZoZmGubq2eSzsdwupXaL196S6B5lMKLTDC2TEYz7/CzBuGF714M/ZHhq3KiDDLUByITmUPfdD9358mcXODFiI7S3D52XXAxrOMgLiowAA9mPHh+EtdD6Dym20rTjZkuACoh7LFTngBF5J8WUWujjKyElDVLeZ77bYgA6OCiyskqgshWQP/UcbbhrIThVOKHicAPCMpWovMQK4PDk+fFfUndD1iTQocVcaiubXJy8CRpcIF7+BxHOlARvA22ix1AEzeNcsltpQUtZf177K++naR/CS2+EquzsDNdsrOGgJiGsbIBkkIBYqg/Zq1vdKdoqo1F0AJyAKAVdks1F1VaVqDwBg450bo2wIjyUGXxOKLfqO5ed3IDY9+93C7ZJmzxSzvrPfreXrCXllC0nYpmC3tmesiHie0Hs0rtszXHfK7dl/K6V9Hj8EyK3FKnV0zLvLJ9B7ny1+xpoiaxUOYzcSMbqYjYywTMOpYf3LmyFwUqn9ZovlGJdkQsitcZGd3Qk6zbPf5z/L7d0KIjdXTTgln1XTYXFJYiatutuS/KqI4h94YNBFKV9mgZW4PkJ6yqjA8jYTwdeufJnV3SRNNgOiFQr/BVaPv7GCxeSxiuY0WfRtAnfv3XcHAwi8Nq3tx3sTb/kSVXWRkfQQyDfQzwiVk9qirD8RGwV6WBeagcIqUuHsXpcoKvPsLC+a2eJt7VyhDV467Yie3XkTh1DojFfuPaIEtJ83OISkWGrDqdHtbXPw5Zh1+G5h9Vlvkgx0KxyX2FCaWb7wc0UJyH66Ey226ef0zxMsfENs/GNsTpu/DsVuS+6LqGhVF/F1LVtia5+DMI5LbJSdgcbQlTRUvtihfcczJnajwiyMCpJOQwjEDUMssdse5B3wHMxL4AnoHCSeAY5pRB75l7PfRnZeCLucb45aBKKbFLWiAsYth7Lp91X0ePqIBDKMCqwuLU/w2rnLiychBuG4yEH07Wj2PTltQ6Xe3dNwOYJ0CJDbDcYZQnTNms9NaF3QOCCAOeXjIU+an1RYzIoV1wV5DNo+nQh1GwdhdbuRM8Q0zbrjmxfP5WL54eGANdeF5bYAbDabXH+KU/QRZXfCS3O2wBLfBSqSXHDD4Yos77ZobT68CVtgZY4LnMgmnOYU6vXeeZZUSZSCC5wve8brnE4ALviY3/ktcQaRw+pW1p5mYTNNgucPsXgpqxP8Qt7eir3PzEkwB2NQGr3Fi0lhDBMZg+kr5zatEyeOx0VzniZOM2Ko4PrTf9wZFvKWa27ybEY5hpv6iB5QKjj1Mt+trPFFBfpsjUvMMZKMfCDCUYHFxr2F071sXdK9hL0dwCMRUgj3H22ONLeoKFAh4BoVLGlpx7x1xx+fu2/mWD5U1RYKMcF+t3JbBB60DV93RiTRIOlsXJEAAdtZdK5x29U4JtrbmDbF91li6VJLO1R2oGA5uPZNlbso6KuVVt77cfwYlwO76xDs9qX+WZFvZNzNl9lwpgznuMRqbQfJNRUgQ1x5iSLgHi+yvFlrzQzHT01ILeGJFl/shLt/kSxFz0A8Y4nRxxz1PPZ1aFwOfPK6E9kk4JQATqkAQhnKIMOd3Gy3lOdYfI/WdYquovKHp9cYg8nFY0xZfRqu8T/MH2EuFzKC0082AibPTh+3hFHFEMtcmYXwF8Lv2obetTVUKLbv7ZfstCh4wT8qsJg1vIld1qI4Zr9bnMgi6nVaVAK+cYkdxtNsDeLrvlv2r6aZhuEeMmWWfRRnhPlsswV/SNZrPkPy8NXKS/COsPoFKmLgop0rtMcLWlOEQgt7Q/7zGyrEVct+3xlJfxSnIZKy92ic7MDSutMI+KZtHsfw1RaTuGGw3+3P2Jd5Ko1E35XZLMTzdSrcFDbfdoYNvxZB2LBH48CGirr/Wmy4SmsupmnzZRHHPkliBnVChqWC7KEMFUkcyP2Yx+YSdE+LYtc5+z/QU5P/coRp+GqFSUBiUx8IHWkdNtLWZLUQH1/dow36FhUJUer9mHiEyoGDNfWnYV/aKHdKaj7NeYj8F2K41jHby85AfroYGOB6u2pZEO4wLe8uV2UKHuDZ7xbYiBOheEfLfLa4vRRTi3+wziue3+UXSUySGQDX92zRks8ePlSb9DhfC/sj+93GmymrMGN3wSw+o+pnXvzgnZtgGCtnd7zgnuhq6VJlis/2YBjrVk4f43us3yGapEDdmAx0Z0Rb2ylf//JubC7vOKRVd1XIqfK/uuV9FeNEOgSJJIv9Y44LhUxGoyLb8/5ZXmyiqkr4RBJi6XzvoaQLtL5Jk/Ke3z2Yz0sK1l16AisddU5Sgpxmazyj3LxwRTsjwiibtJ8CKGktmKuuJq2+60fZwEaa/dDsj6P4x3mG+xX/COeiJUHqwFHGmKZhrmBxKQLGUQjsa79vbhKfoqy+jeg5obhCG7wP+drxIIwOrGqGZldVum8JyVXFv/kavs6hNix1Amjf9IfhJh6by4FAi+LARTvHRa0sD8NEHDInl08NhgML7RwLXSIyVet26nxfILO4nN4fqxFMwz+9RvRGoim9ccL2VoLNKhf33vHTSZGX5QqlaRCO4rG5bGxaFM+Xq5balproAc3zIs9NiUXlsiWp60809WyjsqS7UiC7V3/StC5C4c6wx99qVKM1TVh2VFVRfO8f+wJE6cAuhnimYRumcR4RV2RnTIjuEDFDiywiFNqwOK8t2WpKZ4noz9Z9s9gfhSQ2tulr/LXHT8kGiS7Uw1cLTGidRO2s8LThy3ZxOQdbxH5Ld74tPsmLhI/TOXy1e24mPjKzxSCy8vDV5pEa/zTNrrbYi+6bxdUc2qZPfEf6j9Z4xC6NCiwslyecvfLEpvZxzJk76Yf5H/sRfwKuI/TLkpdqK7ymr2jENc5rpP9shwvoGvPZ4oKG6gIxmCedL7Pq4ZrYbqM0fRI6yZTsjJBnh+on5VlMDmJeXX2i6z4x6a51ut32uC5KolGBnWeU6BhltXvlBf9shn6xuAgvUZEJAxq+2qiTZSlG3hm+2r5EW5X8fA2frcb3Dt1GdVphqbbG3JhEaSkMFgLZmXV7Em22UXLnebfaYXG6V5BV3VVj8HPeZfc0JHL73i7Q1esYmcutqw7DrrI21jeb/bN5684zAFC8j8sm3EOjaZS/qfzw3JVKGcbBBvYGMtcAxS6436pxv/XB/asa969y3AuJus/oZ/kRVRUqwrlGwTgdBJ8poonkH9i6GNFKBTev0m8X1mPGw/Z++Al+QlFZF6iJbu+76TOonLZ8Zf1d3fCnCGt6SS4LBL/5xMout3fX2u38v0twk6W3QOaxuTOkAsWBJ585T55vtnlBsi7dJr4vckeoHLhRU39XWfEsT9dQQFP2u92NJhTlnP1u+zIAwjcusTWNBQjS9CPhnt43XywuiaIf/BUR/WL7BudLxp902O/m2LDEOUtQuiZ/cecxrsieG07y7Da5qwvg/l0CYjGjj1URiVfgzGebJ6MEQfeQhnspOiqyuX4r67Q6z26Fm7zhuwXfNWHfcB9I4DdBfRVKd0ZQr56yOMxTqQGRy0spVe2Jbk6CPZRa5XURIyFeB/N5qUdX9B31YyWiGxXYjvRDVN5DQ22+277cORcyWwyfbXGtqkLyLLUrscV4nOcphK/5bqNZZjF4Th4V7IxYOH0kStM7tE3zAOmfeGwud6taFNNIiVZvBF6q3Vqb/vyVwlBqUtjNb5gVSCUUS/f7Ftc3ognJbnBVRFm5SWg0VIhmMhi/VvRt2CqRzaFYdLjky6yuapoTjnBZ0322vSKB75PcL5NoTfBGaVyy9FUO4e3kAX0SYlKMCqzWouC60H3bsX0riN1hhMp5xzrYHSh5sRiosNAWpcSo0NZQJ/bS7bEC/vMhwaOTvocAym3ufFtR2LICd/XLFS5gM/E+uypmH0+soJEzn63miMhVwVDBfref8ca8IZopoPKl1Ksvt7cl4raa7pvlzT5wn2/l/xBV8f0q+SfHw8xnixnAy4mGshvTvf+69PZ5km+2NFeCSouQAtneoP492R4V8b1wIyuWWmBOUZTxkUj7j/Nv2UdlmccJNSSKz/JQ0aqhTUjjazZ8NKan4hWepqbw6o6DZ8EBfWEtsq2iuevGEAJoC0Z7vYAcYllC5r5X3h2+ioo7BB3IjTrM4rLs7G+vQX4wZ5nVfUL/bF4pXmPZWhUJyQZ/QhZ745evMFca1BaMlKM6DaAQpQ6YAn1bnnzDNRCAawy67Mk5DfXmZpqOYa8ZWa0KYihCi9EKG5gu+6SetiJWz+nvEQaYeKBzfhPd4llsoo+TlCivfXJQg9nmq8im3Gauxzg9acohCzntHGo/xrTuGFa31wmZzxfn5ec6TX9/eRulJa9u6kbvzTzvkpJqjNdH222aEI/XNgSRZlNR1+PZqIPuwhsZsJOqAc+56lEH4CZlNz33jZZYc8sTcUhsfDNLruCqyhiDBXNijlE7O80f4576sQiLa3k2uehizVhxyFBLxhwXcMgYI2p32HeaJfpO+nFDi2ZuRuiabf+/iu7Up1gIXBLDbIAxOa+KiH0PqU4ENe5ckNnGmBZTOI0tF7IaMnXT0lIBo9/Fo8ZzsUhcop9Rsb7Ik6wqPyS4H1hN+Vqi9fekum8ddFUB6rSVxZB0QhUDvtA25DkDY1wB+ETf4V08pejIEE7gdLYamyOuUCfEGZdD6slHPLaQEofHvYsMpB+/noU6kzxx94uSDBU8SG/zb7/0f5fdhzasE/EWSMuhHrkW3ESUIOU2iqlFb43OkqKsCKfdRCVqQF6+6O7SutvZ1gD7R3qSNknMOwCsuCe3qKyu8h8o+/3l219++cvLFzQPMXkmmt6+fPG4SbPyrzGdxijL8ooO/feX91W1/evr1yVtsXy1SeIiL/Pb6lWcb15H6/z121/e/Pr6zZvXaL15zVdv0Rph+eXfOyxluR4Fc2J8CzpP8nybxGOe+u0/kMAMHZNcotsXMn767TVf8TeAJ0nbv79MCEnpcqbZ9GjQ0uaCnUAh2suXLwjbkZvinvVeK9GzN99NM9lDVMT3UfHfNtHjf2fxVYWY8kvobRan9RqdZ6sEo422HdIbch9p2bXzsvPiwgUViml6c3d0g0tYgHFeJVUahmLNQ/MAiD6hKmpfMZXBEI6COQbCGY52wrt1d+64RBkWVEfl92RNtjYPTA2Gv+dZmDE26L4X0bbNnSXpmzmu1X3+czQH7qM8zokS5Lku+9B1jJizxEGHQ87N9hRnL7/VUj96hA26eyv7Ian/8sWn6PEjyu6q+99f/umXX6yRjt8tmk6p8SxgbagS3pI83xl46zADbW5OzUow0w665/nB55HmKEv+iXpV+pnM6PDMiGmEV0z/eo43m8ffX/5ftNJfX5z/57VAj2vigpTX2frfXtC19NcXb17839bdYbNTB+/Qn1w6RHPTDWmTed4P07NfSc88BVnf06k6+TZYJ51XvPlybfnomaxSrdx94zIXLY1O6pRcUWnkujX6r1nyR41WKG9STqqQ2+qFZ2l0d77BXe8ct5Xo//yLLf7LKvXREAOq+Ux+SHckE6s4zZpu3jVeohKwYO3tuvPYjvqavfz8xWH76eg5ieLVIQ+ogJ2XJJr2RVrfJZnHwe+8vMrrWM72Hsci3g31mXCqkUXM08I2Zrg//9kadX8e9UNsPNfyi8pnNc/e83JWINRZ+X02mqvo8fQRbbZe9iiMpN2wmoeg4H5lIkS6IHI+Ft1mMVD+8cDjtqR8hFyeps+E4RffgkNL1j5yVwBjaxD1kNhXv2QfcvL+8c6Lz4/SNP/5vkZlhffvb3nlhcxNa4VsQlFBrgIRDWzR4CHxj6qEzKsdvU+zdSBMzmcEKxlwlJU/+avzvZUEZED2UqCptSMS4HO9uUHFl1uyNkofpp74SNd7erVXOM+DgdhE3HZMNNT0YqTz7cg1xuwoZ3ZeOtpui/zBbyMYR8qVyzcz8w8NMueEzJpNnxl/NlGYm4ZqakVLKMrbhCx223noY9QoTWTWLNfGeQ6LVObzEArvWV5sosrmgkiOaxWlVeh+Hq03SXaSbzbMvbmff0sZ5Ex2dHubpAnmcj/S+Z/I3iH6yn2EQi6/HSR0e+LrkidNdOhTdtmPhT5GZWW2z7yxRx50hyA9/ZjfJVkolRrjoxyGZa8BSqM+Ak/87DQHAYFMgTAZIeSJadcdEYN5f4y3ZfAF8t7uyljO151/5k554lEfV6OzienqCYYMVA6cMJFb6CgL43HYPvV3UP+7il6LF6NHF8TDOIsdrFlcdZ+enDDea14EbRf6m5DI3gZB9vdke5GXVZRCl89uprb7PEPN+T3M6o0eA2Lz2JjNT1tQnJG9FeuTuFRQA2jZ7vneltQyyJ3Mz7zJ83NeTuCJcXVfIGSO/1db/HiJoCKJOdxOZuAmHOZV/i3yUusX9MhYwoo8jr/zr7L2nRyJAz89CMUj53cZpt7JPXkDNwmHsIrJM2GQ6TTFBfemm5sCPSTgScbXLrAPbmrHaX5HdMJnwqKL34ubHWTNrCwmb8rM9abWgOrleNndp7S4TlhroIsU/pxXoVEOoTw8d5rdvCFXvQ9UbdY791TQt7MB3YnnOCZeQPGA9lbMtqMhLfheGxW4C/QVCX1kQbD6YfyWlAmGxgI+eUjWbd5id96YRCle3dNkKWFX2hmGDo0z+M1bxzht/nO/qQ7notXhCHW6OEjpkebRORxjRRv9DKKAkBSd2d2q3gTSPoLg65BdYS0z5Qbr2b9QKHt7z1G8K495Vj/q4EskGkIM4h2mgk3K5hfODl14XyXAeXLCBs/L98ltdRIVXqfNDof/zn6J/qiTAn2p7lFxMQqy6BrageIblATtNb69sKrJ5FRJTJSGo/Waa9Kr++flu/xnluaRnzGgxeE3NV+ztFm+HTqvkX3qrmy+3Ar4nJw4WySnj9ukyWv6LnqSYTQyGbYIqRcERejP3R+ichWR5BIhZnWMyeESlKvvcwuKB0Z8447uCoRYrc9lXCNEV+gxlPPWJYrrovC8AeqRnDzFKWrEhp+8Y/FdoCLJPZdpj5Fu/hSt18I6pzdnfQYiH1kW6mkUFrI00FqUdtgai3x/CkdxsolSEoMM/yppMLE3f8HnWfK2Fm+TDl0P4vl3Xp6WXiRkwtD4MQlWdYjRMnvASwwjay6kPI9VVR7/+FsdtaYBD0nenKYovqOHKMF1k5TB6WEOB/votHslWbDxfsx/NmNt3e38pgFr/8ntEz2Bn+VF179jhA9UPmiPo/gHjfdEQgZ6Oq6Swx3Bd97QEB8u+mOpl0pBty88M8mm3oSYmAZf9BgKX4djVaGtPx4a/rHIU4LHSydJ1qjrWYvS29kCrVuMCQqvb+O1TICP66fjuqpy2dN4U7lAoL8n5X2alJU/wlZgpQgvPrz7jExCTkZmfKigqJLYy1Q1QhB8f/ySrqdtoD1LnZArx6naWG0xnih1HojR7RDTRn9TdJVsQtzxsLjbm6NAmDtL3GlWoaL05sVWRI+wookZqBXjs7aJD0VXCWrWrtfGhjUEVFZHVVUkN3WFTvLNTZLRY92kzIr73yVwKNsEDj6j+I6Su/vplu/4LBYc/fdkPSH2D9PSpt+VQsucHnFYgRPqQiWMK83Ez6R25aUoOPLkARVPZFbt7Uvj2j7WpU49/Zol/DWvQT/GtX36cRyVqN1eva0hPa5PKCrrApHeKVVl+8hrfRNHG9Y9JvQ20TdDfoybcjC7HtfZOkVNTF/A9ud739Ggv0DFeYU2IWxRI4SEDCHxre7zxryFpfYE3qbd7v5M3Eu0Thcucd06Gvkb/7+WZFpjPDhPt4M+WZiALbji0DXlLkEMbYe76DTXXuSVJ3m9zTP2zYTTSV7AEsphuJskertOd0E/Rh3wkK3b4vLA/PXTIZa6FilxzLJFbMQtgZ2AjvMKa2DBsUbrO1ABcMe2qp4GTyU3w3UShY8C0C6FML5tBw+y7uRHPUTDXJZdJNQnyCFcVlfR59BBHAxXyT9lnGvtTlde5St8oo0rHrFDaNkOxZfx5U2oSEz0VHEZZXeaWwQH/gjoBBrWSraDrnZ7YUUJZTLaZWvMbVSn1bcE/fzk9hDf/NlBI7ieiXrWjuY4yaIhXDKm2g394LZv4QlkL14lYsnPyuwg6cd2ZAcEK5STPACZTvH81cXV9DP66SNCzsurIsrKhPcvM9iJ+4V4zSDxSq6iXtreXXJ5RPgJrZOozexor6yMa08QyYht4JlIFpLzUi9QAjwAe2YCGXyxZaJQdxW91sl0+vwuPM1mXzE8E36ZxL4S/CjL0P1wmA97mD8cwDV9278D+OF8+tzOp16GEOs7y/ZyZvN8Iqp3I3PwtOhr+iomPGkv6xQFzhK+2qLJ7xPboHFw5CFbg1z3xCQEsktUVlimUsHHJlSSYjW7p+yQXkhfOTpNFYM4eiLs0LwTCY28o7BkE/PE3u4wXhQ+fayKiD3sTWF0Y/2lnolI0yruf7bX20/yNC8+oEcw0Z4v8naXbrIwBnbHChaDrmwtwcZ6o621ofFBIg5Iz4QPFzY4tD5drp3gqnt1JdADvXhKh0K5f9McoXgMbw8niMSTpJjNey9/ryMRedR1dV9vbjImdrgLojYu0vLHs2d2fHIXyT2DNPzyTCT0MD5X+djU9RKOPWk9H7h2aKiK4ofrvMRCtTEXTXKcbtnrmTnkLbzfS9yOTELe9jX9cqOVZ1jM1UMQmvlFry0HPsNri4W5kKWow3XwqPa/GjfSt7S3SUyp08vzA1+G4EuYto3xxyGKkRKbnzpAbm0aLcvzUU/jbdjd5fs//5hyBagI+kwWADzEULy3Mwly9f7qv85kxPJkvmfCds9xZgEzRhSjapUXFSMzw2/QV9HdM+YKS+cWW+J9i4okysCAEs+EqBNEh4VDs84TcdbTfWbKICHuYTdMsDtH3bB3lp7sWfw+vb4/KsvkLsMrsHOcmiB81yGoBHc69s/kvPCJbriz+M9NmEylwYIr0jPcl7r6cktR0kFOkbiL5YdnsodOcXmvuDMLc7u+1zf3E6h2z4QXfQVc939Pl+tPEfU5GiTgNTvLXk+D+Lacex3KpEDe0eNPTPRi71DaTYh7nz2LOX30cUz97sqky3QGPnmrnRbblfzclvAk0RPCmVT8vDl8xXTjzPY8JloyRGcpCNJKVhhSji/HsUaBQ4gf5OqPOipQeXl3E7qfzdF1/Q88TWxCh/BxNekhfIaGHD0pJtlmgrC0fssR+04b8t9pPybZj0CJ9+wPeeYO1V2SlechV7vhvK+TvqE6S/6oUUJR3iZIlyvK1r+t7NPDfC283AgBNH75vFt8IWM6ED0UNYH+QvlzgnEUnJCdPuK+laFcTQ+hGP61QzH0YcmfiWhc2qNF+cDIzGVQ/hjMxG1wVDuEVdxnLCGj6huzNBZo5TN6sfKcMtLT1xJhUJGgmGEwBXmVsJtxa5toA0365SBpc1tcJ4rLRbMbhSEzbSiUu5HPHVIA/iVjdxiLa6LPFlmUHtXVPRGRjcfXJYoxwZ6JCO82Zfft3FOEn26Yx1/epw8yW+fMQTMg2ta8ERj7F8JbV/kPFGaFUHRHcYzKMhxS/OcDPr0XXjEWjBfdWV7UmwuSQ+t5rLCrfJvE9surreb3xGfpxW0UG9vsLH9xtF7jvTN8aOt9e2tJ1wfljmeyQOiA7Fm0rbbnC4TMo7+pu43HrFaLHDxhPjeS2Cs1MwkF7IXgY1RWpBeetwEtFsmUO2KjYVA8T0L7KYGek/B5X+T11lECtXWDv6bxzwkU+PD1ud11vNZyCIFC1h6kVB3EyiT+gPspnuiyfCYyai/Ew3Njt4BWPGPObYj3PJiWjgV+KuRm2SD41F4Fy12ILXZ+aGNItmdjh6g0XH2/G8Y2RKJzbwQEPpeEtOYKt1XzPXEcVghcbYjKgKiaaJeBw5923HlSFwXK4icoIaAj4gbhJRYyhpevf3FelVfRY7sr+Z+wv0WSd4/uwgwf2iu8FtLzLE5xVyfzKRw1dvo4T2NXpLE+5O9MIxw1Os9IW9kwzwjbxmYdGW7IYrHaNzYSY1jwJ2RviNIzhKamqbzlqQksb3lqarf4w4QvpnwyOSP6BPI0b4WKjsmawJp6jbWd9cQRxf//9q6tN25cSf+VwTwuFjM7szjAYpGzgO3YEwO5eOxOBmdfGko33RailnoltRPPr1+KuvFSvIrULXmZiZvFYlXxE6/Fqnv0Ncr3dxme4Iq/UI4wtIa5jlw9od2X7Ny7efveNQoNeHuc2y4CJD5Ktk4mj49xEg9OltKt+k9edCQ+NNU2por8niM8jlzh/mfXKU7djrlUtf10RCWSt4WpoN/AdDjFF7SXmW6wpFfPz797Y3b97RTnZOf0Lkv7eBMe+f4LRX50p3H5OsaDW/kaERy6Q5Jic7Ej88GbLNl76iuRuUcgUMwvo/SLt60Ux9fbJ0bzvb3yzbJJUOGb7e3nyNOE1IzQZFHQ+P/5+SbOeLGXx3/XKesr//1oxwYTDMLeG9xkDdyjgooVMHA0OlXxXPwbR2TsUWq87+zWRP5FvztXdQvk+zj1Lop9efK2O8V6O+PHpg3Laq+CP8LTuT4yDHLWFTqn1nwvNOjtwj06RnEqj4ZnFKoqql5sNZvb91nZxUgc5N6926FTuXmKsaQR/pn4i76J0v2HZ5tFrnXqro8F3jS8iTEOVhNwfPLcXaSqfetNtWGeXu5foTF0/ogfyS5ihdBpVbPvvL7moP77WKD9X3H55AghrvpgUZi8Tt7PccYE60oA2q6TqF52StMh4zPkJrI19fBbqMDnh7dFKyqJ9hcNjEHRMsN7vpPHncg9VvZUPX72tgTsOPp7jPKA0mrB7kvCmp0/8d6hoqAivA+OtNX2CFn0DTxzHmHw6z7slYx+nT5eHWQmXK7dTftW3scD8yrIJnlkHvoes2so+LXlGNqMoknoa9Z2v1TP16FNxrYW2nbB7gL93wG2AXgGP+C+LVpWXhZGbzHE0z5s0aCsv9Wo/1fYGOEkc51jYmHp0H5VvfD0g1G7aR+vUdYSUWbCCZpYUQcH09GLuOh1qcbnvnikj03XdcozuTtzbdRBXrGEw2WURGkf3sdp+VYEd9fxuQnze8tAn39sB562bYMEB6tuiMjd7Dq+vAlH8k0e7b7E6cHj5SJxpgu7KCE3hMjXFWabsMMTuzFmofYDWNEpRqeS08ORuqaH+ynx3Dj46YHF2qM852kV2R6tJkCKh3dYnjpu8vWPj3OnexQVWXqT5TVW/CzSG8Qhsv812O+7MDX1bbB6wMQFmBuY7yd6fKw2Pn7YXeyPcSpxMeMDmlmHtWEGCh/PzebixmIxQWbEh+gqylc1SQ4fLe+ivFk4DDp5qs+F3K5Z6bpDrlbpTh5+vTr5+D/xtUe4Vxj460Qktbq/oLthZs/FDXPCC5qVjHPQ1bllxllgYzrHHFNkK1vRNx+xD563xSb6dv0NUZq6sMFMrnB3HrL8ZaDjynh5tKwtNTyzZfjIIMJXvqZcTIJyDvOvyCJsRC6X0edHtiK/H7PFqcnunFeP0Rrf9HXd2vDa2X89Igfvbt6zvMdjtV4JHK5edgliE5479UDF5g7lcSb3TzBbQ1SXAYTboIs90ywGo0360LshWzHSuIyjxPHyh609+9cRxOL4p7fZYSVfGqURsJ416EGBwbKDGZs+l50jJKtfVwLL+n1kk9dDuYj9zWERq10ZuwSjvE4rYoux1LiL1zPaYE3eomeUOCRbyg5bUvXff7otPhKv8P/+6aZq0ynIX5aXEs8V5a2FEfcqdZLXtwknk7QBvzukDQg42Jq93z4gIE8l+3n/w+Xe6BHlOcpD8PZ6topBfRDdVcy/B1Id/CAG7vLflOUJfgrPjb225vtYwA+K7DJg2IVRpoMbrGQcpVXy+lxm8vUXlEjDerM2OK76GAu3u5w44bczxUqAOfxm9ybPju4wZGsPzGviLgZdd1g84kBZUTxmNSruUTTwoqc5h7h8qSP0eGLWPemcuzd+F5lvJaOAc/BsPzG8PZxq2R6tmXv07J7Q/pygTVR8WUlva9dz/3BYgV7g70G94XFavGfp9bdTBTHAAZBfg1qzJ+cvLot5gg921f5fnk4gDFp/j6eC+3O6baoPyhCPsXX6kF7n+bBBvBHJNc28TCOHPWqVzYYc27vK0jEg/7pOB0uDWfiX5TdHy5xJmk4Pzq5EIBo23r7K2+JNvN+jQQG88J+HatS4Q/mOWk84OBy2nExOn1y0vc++fkI5Pbzl2dfn9hfJ0F+Hp6tGDl9n0smqEgzXCg7b1tQ8gvhktFuN+yxxcTthag9aeN3ilVsSYtX0Mf8BqBEB9ZCcD96ZenFScwiEbh5NrOrfeLc2J1QfWMMq1MnofIMCM/bOk4lrN+xsw/o4yxhsmyd0RJ+iPK5YrQRpRCeb7jRK2Wk5wJnwBBHC36ZZcw0HFsJ5JSAJMmHpbvqcJsEisb7SB7hUDnraW04HDL+hU9B62z68zQ7ZXbyrIo3Pw7X9TXlMLrM9NX8Ne8uTpSUGbvtW/j0qv2b5F999c5fHxyh/IZ9Omy3O5T0UxGXg2yzC8vob1jI9IBKafKh8MDMLMc0fEDTcv5eR0OFaR5XskOVt76IdMt1hM1y8zSoGFkYxd3O5yfIj7jIqOrwn9s4PZ4DP8/w5iYunKd4heL34AiZonzH/X2dVnP/rdI87c+gUaLcGexeRl7orGYDmfgoRbD19Ge2+3KaY/+7LGp2DPLyun/YN9+ReSGNc+r+L0vNjRJba+QYd8Zi9miOnIMPBp7jKyyImjRvMOPDDvfbx8Y8+Xm8fN8Pejy5ebxffo4RYte7plfRwN13/5mW18PsQLqGH4TwrigeUJD960E8Pmg+OdKbwtZid1kmezM/PYyJFkHrLGGvGffbnGZ3RnuRquSjLaPe0osfllG72Gwym8jDPbqxWdEDVkRfds27B0DTzqctVwE3s4k1S1xri40YH6ce6fI7TKH9xOuzXfpEuzzzf4S0g7Og5lDHax1EDCnu7s7UDBKumgL+SceAuj7N8YKi16rmJd9/niqnB5YC9f3LmXdRNFkLQe3RKXuyktWAbQuIrIcn4UI6Xu51vliZviVxmiup+1s/trM8rhwc8LGzyeGDEFszESzDfeim1c8xdy9YetuxA6b46eoySpOs0j56atKQrmSfAjIXsdP8P++vUZrurX6OwvE1dQ7zLe5flMr93sxvAAveCpbKGi9+i0EYJcWFce/88FIn5ZwKq/Ro9RuekxIMbQSCuFeC7u4qOpyg+rOUOC/osHF044UnPjZnRTDf+7Xr42J3Nc5Xv7YDbZR+HF271JFi/ZRUD7Q8N2zL7ZZwPT3/Femmsj8PX2knk0x+p/QacstgZu+f1u0de/2nJy3ggeY++Fm9R9eGu0PMCVs5rgBbJYtToUezXIhGEq7cW28ZnhH0POnD9NHwQ8LsfBNxifJvtd3/Oru9QVJxz1GQgW8n3oZtvXYL5hQ0VeF/hIrTba6g7zwZDr/HnkhYrGmZ/wGhUGN0eT3jzj9t8jFfzVi0Ihm6yZG8f386MNTZ9xdKHt64PPsMfRn2JT0NE2ERf0JD6tUf6h3TYSh6j+CZGyb76y/9rrLbTr7L0MT6c8whyAXDaol1/K/PIWzr0qyw5H9PWOd4Hx3tUnJPyNn0ULoHcst7VUZewdFXcJftFIF8/xOumh5d09x28LzCxdm+J7eVLzaW3OB3vdIP73uUOJDvnOwS+PbcWr+alFo+NOev1jcVQc/4mymuqKlB18DRH3md+K8Po+ru7rkDV4StO0sSbqND5wro9eLsdFKeo5vFQ5r5G9JrhZZbJrlOMxnHcR4GfZVx/qxa7r9EpydaUUaVZvTu96HlU+nRNt9z2sAr1vM7oYTN8jW10YeryON3kvtSBr0GMA/sgB2V52uRRWhxjErJxuFUhjoMcwPC3UR8d2D5qMLzqqfeUvhlb3FC59BthbxS62FV0HzdD1ZcQP6N31Ft5xyt+KycBy6nox7mLr3MXl1dF1fdd/avR3PsHbvx0wHqYz7PnGFsl5OOE26IZ+hqIDrnv9XCiNN0mH8QNxouvxTP+sxpKfZ3+tNioz5W8HQN5Xkx9eHws0CCfM3L1P4TBZVTunh7ivwctEe7wR0gCeM3CHaKK9UrCo9stAdxcef83Pl1gdkOvgRMUpX3wQY8zLO6aZh1YhxelY6G6TLg8v604A4P16HaBOvpZG2zYcqMnsvASxKHTyU4crnqQE9cqaTkeoutXfFfVt+WWUJdlZNzpdYNO/Q20aDnZ8QwGZk3sVbHs5q5mmIhhNYjaBGoOfduyMO3VpimnXmXacvtchvYkLb6dBH3NED35Oi7I5NdG03DpypaH8QfatOXUl0xjdpakqg77Kmn5LeHUVw3Zm3QMnDF6lG5vqb0q6GAnBlc9ZO/euUdasO3Ypqml9iktvvXxfFMzRE823Jv/bSKnm2l536jIcWNuK+FxTSkVoJXfSYa6csgV0dCdju2yaPAOZwZro/nvaO6yJPmUVUFOJ02XYWCMoR8ZVvQiLb66xAik64bohMpjvE0jv1b7N/pt4lIM1+voQlYzNElLaRT7A3fCbYn4VLKGDv91zRDguEyyw/cCDl99WdnsLisc3s33NQOuju7Rc4y+vkHJ6fGcpI7HDIvoWEZh58VNW32QKH9FRWPxEMfCtKBr782pgvz6mzvqfvI23FRvNfp7OycnkxqZ/0LFBlsx8cDqfWbJSYb1i6LIdjHp2faSjMTcrz397lFBnBK3baItDvzX6f6navnaZ+JqJXpAyeMv/Y/vzkkZn5J4h0X458+//cx/Mh/SOknvTxfkVrM6qyp20V40B1ZjL5UBkJyVByRgZfs3oUn8GaO8jtRwlaVFmUfY3OI3H6e7+BQlvD04QsPhodK0Y8mXvEYnlFYOFyq9Tdql06eJ7XfNcD2gs8erXylQGWAt/puEUSWyLQhotNgiytjSdUCM0WkR+BLuMOk9d7F9AD4Vqpv52kwvi4WjQE95Q62SjyUMAkjBJCMA0/zGXtK+2RX9DMC6ifID4jeJPTCkQFB1/HcIUmuATA1QzUnpWODMkmQZk3MlKQsy8sPip2CixjJm3e5AdSsKbdtTAVFSyyhI0P4cZo407UUPaGkUMZoFMflkeOF8j/AWBm9ldiVqXLBi/UqN5cD0KF80CrpgTzK5WB1FEMxxJhgBfQaedJKGTVznZoJJ3YIM6HVJP38XKLRBwWTwk/tgjoW7MioR8cdPd2gL+6DOCmO0vCy22JLFY4pRxwJLk0GpdcD0CKL/+OWX34Se6zm1brU0p+63pQMA9BmeedcrQDv8G54dGOw/0REhwQg3GTA616fuoY9uad3WAM+URppkeLd9SJTA40yr8AioUj5SkDSpdr+bCFmaBbLFALFaXNn08QSwkj9eGRtVl3FSvcSFH+44YUozfVmNe8tFg1FbrPGnRwOJCpWWW1iJmU5ejdCgKF3ZaiavViObyWsyXLVugcu44GilZWTof1z8RUeniklb0192xOTtFJNwTwofhojuPrbAaqJqBGCvMdrfgoBBrmoYQLTamDTFZ02cBhPNM6kGGqD8bj0Y6jaMEZgVhCsKcytm0cNDwcTqY9JiQzsbOIHPKIXOhHrxe4ETZKH5wKn3s55mFd2+5fY4Luk2U+0rfWbx2/249DEFDkIg6/6JR5PujfLFCZu8SoTUiG9wz97WZfqx/3GUwUWI/ADJEhhbncojgEsd6ULSpvq9/OQw012d2wwXK4aZVZdPATNFRJSRYMaErxhvQmMClTAbOaZg6RObPB6LpL35TW60Coua36QQgwlWM89ZY26Ocx2LOs105zSYfAfos0bCVAjUhJGaDIXNTnNRwx50eiGUrWawszmpmOM41yFMM8RNfyY1Pb5GPJVygdedKizXqOhq/3GP/u8c56iKHSK/8Z/T2EUJDIrDlK9mDKO1shnHpj5P3+Kej59R/rKp0gVJ+5YmYjqVKbC7sjMfDX3hQqpqGGxYzWuUbFNj4vKc7hNUhXu6KMs8/nwuUR25f9uX6CY5ihLoYLp0zMsYqWZqIQXikFOjzMZBMSrX1USGvvZ8oNtgdTH3hK4fzPLWZW44Z7pzRjDzCjDtbPkDJepGZ4KP7kpAEpl9Xrc8nNAQslZ0x8NrZNJkSzwbXC1mWpsQVOMPVnYuEPMYqphjW0XqgXme1ANKQEhb4Tk9pJlJs3SFWWFvMUPaDMA2/tBmfwk+j+Ht4YR28WO8I0Xd3nY5YIPlh+SSUa4EgBL1lgBFWPQPp+rPrYle9DN1PR7MgBAqFIZCVwMpW8IwT+PdITQ0boZKWZP2YQYzHVzNtQ0NnO9ybB4MtskHbJUGU2Oeykakj507l1VELzP4DoUqXclqgVLJYoUwYWRRAFxm3iSSPgU683uAmGmHT4kyOInbuED7FOVxlJbd2HqVHT/HKSGc3CNAIRsELSX5mvwIVIqaSDEnFwMV/hazN589UMefd4didOqtugE8/zxHJInMxzSWY5QhorHAFqxyeJQbaNbQo8WeG/6WOyaaQHK1o9+Sh7wWcvojSYEQ6PKxjyFni8ZRjx09wHI2Z428Ep+i5NyBVK1hGFyMi1yiromYDWFIDDvhKQyUa20t8MwzmBrWky8nR/S/n2hhuJzV3+vsa5pk0X66OGqtBOyRTvfjKiKpdeqYtDWnUGrbh+h4ShAsv2snzm6UsOqeEUcI1viTYWEToxzrV+XFkCZRG5oRj2AiyOqmk56Rhfp1Fenuen1MGqOlmwGs5n/UMA2IRjxOsMPP1AcI79HXgjyFWUTg4FZaRob+x8UHDu5UMWlr8sDBXWh7q5zEs4porptgR0znOkqYR0gtm2lu+sj5y80pPEfQjbWoGgy8qsJ04Lv+VqI8jZKLc/lUcayd27gk17Me8VQaMHKpCRc/AirVswHkZFi8yfLzkeR68A08+UFC1ybDifp18bjodVkOCDbZKd6NjQLSqAiD5ud14KBWZjlA2JL//pFn55MUBRSJ0HnNz6NMRKRBUYRA0JEZJiB4jBrq5ZrDEALIbd9jIfEywaBj3pfjjjiEfAaLD5nYQ7ouJIRGX71Y9uuo6xci1mQg+pDvg+RDVK1dSJsMk+aXpadBrNUwaYg1+MS9v4hN87igGXOZa46ayVe4bbDNj0V0QG9iLE3+soWjhM40qCotOSgPS7CasKqMWibtTh5XFcQa8K3YjxHfAcTMx5Sp8EUknHbiI3etc0ZUJ6UoQcBL1tGw0yuyJMDM361jGtiM6NZhB5yp3Tr+iB/Lqyjfb+/O+e4pKtD+r7h8kujg3o0a/8NWCoZZ/2O4kWSsAMydLkaYALticogwax1YIdcuDTTWQJKD8oyw6LFCgCe0Wa972orzwtpH+lOY6WpoTlAbbY3kjDOmR6ddNr3PSjT/dXYlpShB/euyMdQrMv919j36itF+l2EGRTs4LeJ8EhCcEQcsX/zZJaTVIk4yIZx5nQQ1y/H5wGW0YcgVK0y3TOd3+PAUn6qsVLOeyVoh2UCP3Y/LBlCnx/ynsVZUcmIEy+3aa4GRIxw4sAVhAm3adKwnEBkfS3QVpr2krcQ4+b2j10xRP67pf+aMPuFqpTznaZUaEQXwNQ60HqZE5pY2TMkK1sC0PotY/XYPeMYfVaywubyxxQp4nPWnXNtmRJCrKC+pnH8h81NqYMJLxC1J+ML15JEUdDNpcwZ5IwUILWKWmgPMxpyrnNA1+XQlYGv+d/BzANaIN/JOuJr6Yv7qCe2+ZGc+FJ7ws3wEEyiZoUwsHedtM6iWWrSQwe409gwDSImGRsMdX3XCbd/unGOND3fRCzl6vE3jiq+nE0jV6TTbMLd/4wuXfawo6GPUJtUTs8FHe5Gh1shXPwc7OACVUooW9obEDSB+QWlzYcLXnQyfFQyecU+8zQ5b6t9VR8pPGjg65sSBLxsFkFSrMmlCnVuobBYGd7RSJs1xIs4CaovYeU6HqjH3m7Zwmnyr6R8/4WJA8tBZB2QWAxXyzPfh/LnY5XGdgnTk8B902+JzarZ08bAQdVoESLB+z1GJ3qGicuDc3uTZcTyUsI1zp2Fs0eLxwSlk0iLdGXMByCb7AY+ZwKPviulWtY+PcYJ/QduRQjN0DbKM+l+Xfj/bq2K0u5nY7+Nil3CBDSsttCMDIZo4wmYnOre7SUYJXyiaKRCcOn1sViNV3eku08osr4KTx8cof7n+tnuK0gO6x1/E1TnHTexeFPBqCFhotT+ajzJEBPZGrP4lECYgvcLgodbDpCFFB8wDGuSPH5iYABOM5ScDw2W0+3KbYll2X4JucoPMPhLhGZGkNItf7Mo0W8SOWYa7+ft8zA90I3qADMHc1I4gf57RGe2vj1GcXJRltHsil+03sWKpbZ8aKgjkQMm5xHYgxeIzTsF6GR37xBOuvmGoTZambl74GTuBnTuGZpHNjhJ/WyuxUwfiY4iYRANMwSiTJSW8DG+BUCY3VXCUGTVHyzcHbFHjlFSVYR37XUyOtjCYbGyjak4Gv9vjKctLLNsjnqy3D7sntD8naBMVX+SPaGki5hyBKTA/kWBkYDhyJWHexEp1DoMXVieTBhsJ4/RQyThhPqDpocLIwKXwWR9UWJ0WBxXcUJLV/tSgDsP7NVjeKFZ8QCC6MMziybrvvYCN0svwQOFxyl3eXZYkn7IS4725vqs6qmdK3EC6g1T8e1mBcZNt+XraM9amLpy6ri2z8Ajg22cO04RCg63moLPSVoMRYKa2vFWbU+Cr+uEiLb4qjuUpEr5X259HGcUGYczXuajEXDPCVi/ipPmIr7IjmSkNBzCqythjF900n5K4+31FI5bU1FbNjQwjOKmya1bpgJmtLZHkaVyyyjk9Mn5a2aa7GEyyg+VwRFUZeziim2YuAOnfVzQcSU1t1dzIMKr+LSbq4npRyFbW/zjOtbI9kjwNR7B55oGfVrYJHbfJpfU9eo7R1zcoOT2ek7SK4mO615PUH33PJ5MDcKQAiFY0hJn1iFXbU+KQKdC5zTRU0j6fDFF+PWEgY8wQTEzleSDLaVibdCwzB/N6Rq0FDVULcOhzhNPynPfsMTSuy941rlO+VB8VroHy1lU126ObOC/K11EZfY4K8R6nqvWAyu5BF8lzXP9MdWbze3VJdYz++fP+c4b7Ovqc9FWEAYdjHH27ikp0ICFIRPZ0KdgITaBpCv+rOkgEmulKoCa6Qg37t9kuSuK/0b7tcaAhgAZqEiDTNR6lhzPxYRPb7IrAprpSE/XQQ5mTs9giO+c7sDWQTKqkQKmR4g7lx7goMMbbQ25BApEEal2k0rTMvgMTWmWLoRZZCp2eWZJAupGfQX1IiQHX9sIC5N0Wylpoyw1t1S1DpObqKFQW64gMm1W0p25I20L3LFRooCuB+HeFOgUqvzVwIOxKQPHbQt0A2MTMfYfKpwz6dHgCcDjkaHRtlnh4xsPYM55Ooe+GKwdbZEk0DfYnTEJbfRHUTF+q+4rahZT4CbUl4PfTFmrY99mdBf59EdRAX6qDmXzCVc+2xlPtXbwrzznU310JaKK2UMOe9doW2mCLoYZYCrP+VujEESh6f9sQbd9F5CvSqxql58eI1IHGNbYYVJWhMMReFZE9ztERHrxBKhUiGUKdCCiJn1H+somPkK3ZYrBRhsKsb+lY27LupWkUPUyT2Tbehc28iZMSnqS1VYxEE2qZSaoYOAQK1UfQUhl/BU1FzccAUqnkoCltZXk4oV38GO/IhouKUyuTSkavkg+uYywpXP1D82pOnIqV5ODMrKzhJJ2xXDYSmfbpJoJ2h3ShordIuVk7n6I8jtI+Su5Vdvwcp5GkX0wqKeRS1tPI++c5Ir98TGNoImCLIRlYCjfrmJtEM/XW/7f/jviKcoHMJLHGJa9nE3La1CwNuQVGmhq6qbp7sylO010ROEV3pbrToRjld3kM7hyoMvBkqC/WNNK7yAht9EVQE1Wplvv1NzzBplFycS6fqvO7emiSnl6oySEp1DU00pHQcJLtElUGtUuKi63RlonQys4Q6UJFQ2bniYRY1oiSf0Nhwv+PPDufZI00hYqWGgpNS03kcaGR5neIf1NkuMhncz1LV/ksmWqZz1JqpIAzTgtSwGSQFDCloRSKltWtmXWjZHihyqTdabSToPItw43UZdJG6mJNI2BeVaE5kApqGCQ0OMGSbEr7ItmpldH2k03ZKG1F3mkshdakTD43wJhMOWxGhkSrHp8+BVCRJ4HV5Kl0h05iSg/x9EmkAY+hRDLbxmUrKBmhkRhmqyZpAH6g9yWUMA4kxJbiGMhhJoD+MJ0O1y0eqNOl4KE6TWDeVB39XtVcTaFpsibS3plBmkk1MtEEiE8ML0NYGsVqhCXUbkjY4KbAFoQlUK0cOVJdH/aRNMXe68vAfuuLdeMj84pVHBuZYnBcZCi0sEykGwGqDIZiYrjE/5jLG6HKoEaoYt06CqUIb7ZUg7tIAq6rBCrdFhHzQGT/+hm8mOXKwa0iS6K90crAW4Dmd/gGKzO41ehD/4nTUlcEX1+2pSaid6cesAZdsVQR02MTabwuoWEpJXhPJyO2uEnZoOMpgYcSmEx3s9JTGt69KSQQSVR3ccYtt8eU8oYFCtVpp2mz96gi28svTXkCeG3B0uiMnGdFgZkn8lZFEtDIApXOyPXaR3rDzpWDBmZJtCe0YBgW4KgWpIPPbEFSc0HUzWsb1R+pMSGXxCM0phg8MmMotF/s8RTFB2jc6ovgL7Qt1V5nkxWQalDiKeBLbY7I4NzzLSpLlGuGZxmh7EwUotWaICrwOPoXig9PUJ9y5bD6DIlZg69jDO4CVlskUTRLUWla5iLgCM1y5VCbHIlu1n9Jd4pJny4F53yaQHvYzUfSAA64eRL4UJunMmpZblWuXN6mqVV5B0KNf6Ca3MRDcVuRmt/9Mm5acscyCZ3e8+seFXiJTjzEDRzbGm3l/nMChcpPryFC+obbo2mF54JIojrj3l6cTkmM9pusoY8tpND4L8BkZtLQdcwFkq+PBAozMRpyvQStz5TBxbvh/fu2pzP+SjoPUzPnXnMnXybphPlFsxDCQ7SJQCLz1WWpDNYC3YNYcAHQlcpm/Y7AwEFT3hRTKnPTNG1K/lBOhjWAVAE5gNpGIp0YBm0LDVKvQNS++9ve85+qo/Di7yvwj1aotHrcMwQsgvyJAVtT8byg5mLyXOBXVn1T09BPLfR2gamDGQV6UtJbRPlKxN4cwqKDHseK7UNjdtEyZhXlqsoedhBNdY81lJygsR3kqnyg4d2Umyg/oNLBlE1FuQGkCqsUnKcJ8TSm/B5ZghCfIP0Qp1YZemjjplr9mmZbc4SVo0kGCinUYF8CdfUkb3zsVWT3C9t2w4CaXUasHE/MK8uVhN+7EEXVr1gUXLidFMBM9honiAHlo4h5ZcVIAigrUW8uJqPfCm07voB5QMIApoDeP9UmUD5rsle92RSrlOZJ/KvLbfBJPdnrN3cVOdPJFWUJvffSiKp3G8vu7EMxeMqJ9asGcMWgVFxyuMMwCGoI+SAoJ/bSj3Mww2WcVMGhO84KI3Ck4UxghKEBSrcxU3rWcq0F2pCfAPfgmWEge8Vsb4b2papyaSwShVge869tSU3pS1qHZXL7XJJ5sAmslSE6udjQA1IiuuphKLuAZl+D1qtnyfNVB7WZF6XbjjGgOEzpRXC2HvgKtq7OFXlXvz2D1qsPx30R1YDkn4363fNIRb8LNP57nL8zqUcz2RNwezVVVyqKxY1JNblS/Ot6opTs5TxYEzSLWBjWPPIlj0k1T30+qXmYt7uKDwWk8/+xQFd79Yyien/vAxXMzZ/VdwPWDPnpSI0EEwQ3ls1XBNb0jIeZGq291LUCF18pJK6gSV0oC2kYGyDxlfyvVSYyCxDtA9yPqiuExAkQ1oRhoopT4ryK3bIBSaRrWJZOoQwQIKXWQhH4xBVX7mpLg41s6QgpUmsYVdeqKMZ1oZWVh2pR85KEaYFZ60KteDBwYxL9NklaJdh+ybUDvJjFyiArNEW3R+yX3VJLiLQhNgmSOEK0ITxuEXjV9HgQSIOhYVwTcIGO6PWi1BbyOiFXvYpIT7SJPK95IZX1aAHJgyFmGpNIQlDpjaOpGMxM6rBSNDfDWFEOl9CKsFiSmF7gFbUDG8UVp94uZgYx4MqGGFPwloQK841WdeO2EFZyG9lUM/0CRJdspe+gnDrcQb7geM6c5VOlQYyh8P6TU2uVkmgDqDEPkyhC05numo1ZjLChNgjrR1vUJjBfEBPrJ3GT2sE+0aWZk4116GRThoXcQFBYRmIKVbjFpZrYAabjY9PEWuFNpFxOGtfVKq/U2mzdOEszkvhNW2mzxiZV8xnFKCZ8mRhYKuZwXCv3hZDhQDn6aBjoQLyNdqp1YoIJQ3ox8RFc62WPLDqre48/RMdTgnrG8j7nKD2JPmJvd3Fnt+xjGFFlCaXi3HXwwx4hni6pLg+VO0R9xbQtEvmfqMOr2gYHVrphikRykd3dMPkQxqRm/6M/b2PT54DqCnqID3DB1X0knt+oSTQ18Ekf/VnfVCZSRahW+nCbVQyBJpOo3YSbVfRte9N1MbiVdgKoQhhFCBZOqlK/+lGXBPrW68uRBVOYCVveawyHI3dUeUtHDpfoS9NoRGbik/cig3HHxdpiRb8927BUdmtNM1jQaXqTfIk0W9UXS9F5FH3k75YEETd4igTSyQV3fYfEhM4nteDI+K6KqsYmjiLEsBROPSiS/7Z3sZP78sEVFLu3wb58UN4AhosyD4An0zR2N7RLTT24X2dhji43gdwGPIlfxYW8Cn09T5ueXn7FVlYk8r+VDa9qm81ie4f3Z09RgfZ/xeUT1YKouK6KN3WYuny+DlJVmovD3RDMd9vzl5sBruBJEbCm9Is3SoTiyTQf6X43tg9by++4MJV5ujwqmgGRIgkwINI5YPp6YHoXexWBjC3KpZCSPsTCSJGhhjAxSTnjxyza70JfyS86pjFNmwxH/k1wFH6V5tMFkWrSVEDu6pH5v+cr15Il9CQ4WFOYX5V5iVw3Qm0YH+2Wjydc0p6PybCkGfBAyjBDHZA5qvmSVQmhBhxwG3S2lNZ/f4eONcInuNI8W1KRa3cEXp4pyVJ7NV+/Jl2XBwOpvgw5cYiPY3JTKDaMUlr/+8axzQAnRNsC6dqAkcO0rgIvsgxzNXB0SePUvERnGcP0by7zDZtIbXubxmUcJYq1pKqC73UknC6umXo0KeCGG6NdK4tN6e0irRtIXSUneBFulsPPIXJen7luK2SxEy2nIldM5XCWvXpKV6fOk/GR8fBsEtXUBdKFmLXGUVunbnA1efX8qCXkPNRfIMPUwe6RoSQ7/UWcMluOi+MdnVtxe5NnR5U9VOQhDAKnkWwWNsqskINNscksDEERL90MXQ7MrWL/JhL537gJeTzrmtIUnS4jewJETofHdpBS39fuTlhCzs9mjJdl2nSJgZyRUHjxMcpfrr/tnqL0gO6xafs8kcC2RFtJZRQ2cWVjEDgpJbtDoXNp1tsSMFXmQCOQP4y1Z6mXpbYkOaZyMtTWCTH0aZKDEkammT79mUmxb9dV8b99n9ZEYBbI7U0MD6QKaoXvgrMLvzLr5UP99sMkiaUvs+hedhjUkis7/JnHLMy1ZbNuKo3E0soVgzKBEn1UGT5lppEZJBhqmDZMQUNX8qrWTBDD5LbcsunURRspqBXTL5DkvZ6FFcnbGQ5gDk/CQp2a08V33sYcCuqQ5gCTb9bfoTKnpqs5+ryg2461zBgArXdFAB5iDlSKjSKvqVsmITrX37Y6X7vK0qLMo7iahPFmvlvLtdHwN9lWzBEIbId98dYvIR1C57OrLEnuxHq5pcuH6MHsdO4kA0tS5GqlrNI0TWoSKgukKVCYxJGi1QZyDI86IG/mQ/v2Tpalcphh+1d9anN1dGrhjV8NTqA2lerTtPeZ7KDAJm4Yx/B4ApKj1vs8RdbTYYbtk3WozdXRqYU3TgYygdrNplvI2mo8q0nqK96z+29rhJlUqqZ4gKHPgOuxm5gCK6OzNQ1Vl+u7HPM5wU1rWjeuIyPXvPsGG1kfoYgj9H9Y6F/1V7/WTCrD415GeVf26tc6L3XzA/6zPoN6l+1RUpBfX/16f8a1j6j+6zUq4kPP4hXmmaJd1WbPtKW5TR+rGPkkozEnUUvSFrepQFAZ7aMyusjLuIqAiot3+FsiGdeJU051HvQZ7W/TD+fydC6xyuj4OWHO6F/9qm7/1a+CzK/qkIyFDxWwmDFWAX1IL89xsu/kvomSgttuylhcYev/gfDvdV/iT7NEh5eO0/ssNWTUmA9vUlG6x5/cBh1PCWZWfEgfomfkItvHAr1Fh2j3cldlUiQ+RjIm+o5gzf7qdRwd8uhYNDz6+vhPjOH98dv//D8l6nKCI/YIAA== - - - dbo - - \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201702091426405_ProductSellingAbroad.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201702091426405_ProductSellingAbroad.Designer.cs deleted file mode 100644 index a82ba7bb56..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702091426405_ProductSellingAbroad.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class ProductSellingAbroad : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ProductSellingAbroad)); - - string IMigrationMetadata.Id - { - get { return "201702091426405_ProductSellingAbroad"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201702091426405_ProductSellingAbroad.cs b/src/Libraries/SmartStore.Data/Migrations/201702091426405_ProductSellingAbroad.cs deleted file mode 100644 index b8de35829a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702091426405_ProductSellingAbroad.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class ProductSellingAbroad : DbMigration - { - public override void Up() - { - AddColumn("dbo.Product", "CustomsTariffNumber", c => c.String(maxLength: 30)); - AddColumn("dbo.Product", "CountryOfOriginId", c => c.Int()); - CreateIndex("dbo.Product", "CountryOfOriginId"); - AddForeignKey("dbo.Product", "CountryOfOriginId", "dbo.Country", "Id"); - } - - public override void Down() - { - DropForeignKey("dbo.Product", "CountryOfOriginId", "dbo.Country"); - DropIndex("dbo.Product", new[] { "CountryOfOriginId" }); - DropColumn("dbo.Product", "CountryOfOriginId"); - DropColumn("dbo.Product", "CustomsTariffNumber"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201702091426405_ProductSellingAbroad.resx b/src/Libraries/SmartStore.Data/Migrations/201702091426405_ProductSellingAbroad.resx deleted file mode 100644 index 5f91f10192..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702091426405_ProductSellingAbroad.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201702141336312_VariantAttributeValuePictures.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201702141336312_VariantAttributeValuePictures.Designer.cs deleted file mode 100644 index 219cc5f9ee..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702141336312_VariantAttributeValuePictures.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class VariantAttributeValuePictures : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(VariantAttributeValuePictures)); - - string IMigrationMetadata.Id - { - get { return "201702141336312_VariantAttributeValuePictures"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201702141336312_VariantAttributeValuePictures.cs b/src/Libraries/SmartStore.Data/Migrations/201702141336312_VariantAttributeValuePictures.cs deleted file mode 100644 index 3cc8082a4a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702141336312_VariantAttributeValuePictures.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class VariantAttributeValuePictures : DbMigration - { - public override void Up() - { - AddColumn("dbo.ProductVariantAttributeValue", "PictureId", c => c.Int(nullable: false)); - } - - public override void Down() - { - DropColumn("dbo.ProductVariantAttributeValue", "PictureId"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201702141336312_VariantAttributeValuePictures.resx b/src/Libraries/SmartStore.Data/Migrations/201702141336312_VariantAttributeValuePictures.resx deleted file mode 100644 index 0d69708f2a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702141336312_VariantAttributeValuePictures.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201702202112266_VariantAttributeAllowFiltering.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201702202112266_VariantAttributeAllowFiltering.Designer.cs deleted file mode 100644 index 7ed530fbb1..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702202112266_VariantAttributeAllowFiltering.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class VariantAttributeAllowFiltering : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(VariantAttributeAllowFiltering)); - - string IMigrationMetadata.Id - { - get { return "201702202112266_VariantAttributeAllowFiltering"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201702202112266_VariantAttributeAllowFiltering.cs b/src/Libraries/SmartStore.Data/Migrations/201702202112266_VariantAttributeAllowFiltering.cs deleted file mode 100644 index 77d594e60d..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702202112266_VariantAttributeAllowFiltering.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class VariantAttributeAllowFiltering : DbMigration - { - public override void Up() - { - AddColumn("dbo.ProductAttribute", "AllowFiltering", c => c.Boolean(nullable: false, defaultValue: true)); - AddColumn("dbo.ProductAttribute", "DisplayOrder", c => c.Int(nullable: false)); - } - - public override void Down() - { - DropColumn("dbo.ProductAttribute", "DisplayOrder"); - DropColumn("dbo.ProductAttribute", "AllowFiltering"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201702202112266_VariantAttributeAllowFiltering.resx b/src/Libraries/SmartStore.Data/Migrations/201702202112266_VariantAttributeAllowFiltering.resx deleted file mode 100644 index fce18df056..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201702202112266_VariantAttributeAllowFiltering.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201703021146527_ProductAttributeOptionsSet.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201703021146527_ProductAttributeOptionsSet.Designer.cs deleted file mode 100644 index 74a832558e..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201703021146527_ProductAttributeOptionsSet.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class ProductAttributeOptionsSet : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(ProductAttributeOptionsSet)); - - string IMigrationMetadata.Id - { - get { return "201703021146527_ProductAttributeOptionsSet"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201703021146527_ProductAttributeOptionsSet.cs b/src/Libraries/SmartStore.Data/Migrations/201703021146527_ProductAttributeOptionsSet.cs deleted file mode 100644 index bca51f2063..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201703021146527_ProductAttributeOptionsSet.cs +++ /dev/null @@ -1,66 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class ProductAttributeOptionsSet : DbMigration - { - public override void Up() - { - CreateTable( - "dbo.ProductAttributeOptionsSet", - c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(maxLength: 400), - ProductAttributeId = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.ProductAttribute", t => t.ProductAttributeId, cascadeDelete: true) - .Index(t => t.ProductAttributeId); - - CreateTable( - "dbo.ProductAttributeOption", - c => new - { - Id = c.Int(nullable: false, identity: true), - ProductAttributeOptionsSetId = c.Int(nullable: false), - Alias = c.String(maxLength: 100), - Name = c.String(maxLength: 4000), - PictureId = c.Int(nullable: false), - ColorSquaresRgb = c.String(maxLength: 100), - PriceAdjustment = c.Decimal(nullable: false, precision: 18, scale: 4), - WeightAdjustment = c.Decimal(nullable: false, precision: 18, scale: 4), - IsPreSelected = c.Boolean(nullable: false), - DisplayOrder = c.Int(nullable: false), - ValueTypeId = c.Int(nullable: false), - LinkedProductId = c.Int(nullable: false), - Quantity = c.Int(nullable: false), - }) - .PrimaryKey(t => t.Id) - .ForeignKey("dbo.ProductAttributeOptionsSet", t => t.ProductAttributeOptionsSetId, cascadeDelete: true) - .Index(t => t.ProductAttributeOptionsSetId); - - AddColumn("dbo.SpecificationAttributeOption", "RangeFilterId", c => c.Int(nullable: false)); - CreateIndex("dbo.Customer", "LastIpAddress", name: "IX_Customer_LastIpAddress"); - CreateIndex("dbo.Customer", "CreatedOnUtc", name: "IX_Customer_CreatedOn"); - CreateIndex("dbo.Customer", "LastActivityDateUtc", name: "IX_Customer_LastActivity"); - CreateIndex("dbo.GenericAttribute", "Key", name: "IX_GenericAttribute_Key"); - } - - public override void Down() - { - DropForeignKey("dbo.ProductAttributeOption", "ProductAttributeOptionsSetId", "dbo.ProductAttributeOptionsSet"); - DropForeignKey("dbo.ProductAttributeOptionsSet", "ProductAttributeId", "dbo.ProductAttribute"); - DropIndex("dbo.GenericAttribute", "IX_GenericAttribute_Key"); - DropIndex("dbo.ProductAttributeOption", new[] { "ProductAttributeOptionsSetId" }); - DropIndex("dbo.ProductAttributeOptionsSet", new[] { "ProductAttributeId" }); - DropIndex("dbo.Customer", "IX_Customer_LastActivity"); - DropIndex("dbo.Customer", "IX_Customer_CreatedOn"); - DropIndex("dbo.Customer", "IX_Customer_LastIpAddress"); - DropColumn("dbo.SpecificationAttributeOption", "RangeFilterId"); - DropTable("dbo.ProductAttributeOption"); - DropTable("dbo.ProductAttributeOptionsSet"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201703021146527_ProductAttributeOptionsSet.resx b/src/Libraries/SmartStore.Data/Migrations/201703021146527_ProductAttributeOptionsSet.resx deleted file mode 100644 index df9f3e37a9..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201703021146527_ProductAttributeOptionsSet.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201703161013248_FacetTemplateHint.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201703161013248_FacetTemplateHint.Designer.cs deleted file mode 100644 index 7999911b93..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201703161013248_FacetTemplateHint.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class FacetTemplateHint : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(FacetTemplateHint)); - - string IMigrationMetadata.Id - { - get { return "201703161013248_FacetTemplateHint"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201703161013248_FacetTemplateHint.cs b/src/Libraries/SmartStore.Data/Migrations/201703161013248_FacetTemplateHint.cs deleted file mode 100644 index c1d805592a..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201703161013248_FacetTemplateHint.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class FacetTemplateHint : DbMigration - { - public override void Up() - { - AddColumn("dbo.SpecificationAttributeOption", "NumberValue", c => c.Decimal(nullable: false, precision: 18, scale: 4)); - AddColumn("dbo.SpecificationAttribute", "FacetTemplateHint", c => c.Int(nullable: false)); - AddColumn("dbo.ProductAttribute", "FacetTemplateHint", c => c.Int(nullable: false)); - DropColumn("dbo.SpecificationAttributeOption", "RangeFilterId"); - } - - public override void Down() - { - AddColumn("dbo.SpecificationAttributeOption", "RangeFilterId", c => c.Int(nullable: false)); - DropColumn("dbo.ProductAttribute", "FacetTemplateHint"); - DropColumn("dbo.SpecificationAttribute", "FacetTemplateHint"); - DropColumn("dbo.SpecificationAttributeOption", "NumberValue"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201703161013248_FacetTemplateHint.resx b/src/Libraries/SmartStore.Data/Migrations/201703161013248_FacetTemplateHint.resx deleted file mode 100644 index 780c20cfb2..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201703161013248_FacetTemplateHint.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201704031946479_SpecificationAttributeAllowFiltering.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201704031946479_SpecificationAttributeAllowFiltering.Designer.cs deleted file mode 100644 index bcc661f051..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201704031946479_SpecificationAttributeAllowFiltering.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class SpecificationAttributeAllowFiltering : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(SpecificationAttributeAllowFiltering)); - - string IMigrationMetadata.Id - { - get { return "201704031946479_SpecificationAttributeAllowFiltering"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201704031946479_SpecificationAttributeAllowFiltering.cs b/src/Libraries/SmartStore.Data/Migrations/201704031946479_SpecificationAttributeAllowFiltering.cs deleted file mode 100644 index a9cda504f6..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201704031946479_SpecificationAttributeAllowFiltering.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class SpecificationAttributeAllowFiltering : DbMigration - { - public override void Up() - { - AddColumn("dbo.SpecificationAttribute", "ShowOnProductPage", c => c.Boolean(nullable: false)); - AddColumn("dbo.SpecificationAttribute", "AllowFiltering", c => c.Boolean(nullable: false)); - AlterColumn("dbo.Product_SpecificationAttribute_Mapping", "AllowFiltering", c => c.Boolean()); - AlterColumn("dbo.Product_SpecificationAttribute_Mapping", "ShowOnProductPage", c => c.Boolean()); - } - - public override void Down() - { - AlterColumn("dbo.Product_SpecificationAttribute_Mapping", "ShowOnProductPage", c => c.Boolean(nullable: false)); - AlterColumn("dbo.Product_SpecificationAttribute_Mapping", "AllowFiltering", c => c.Boolean(nullable: false)); - DropColumn("dbo.SpecificationAttribute", "AllowFiltering"); - DropColumn("dbo.SpecificationAttribute", "ShowOnProductPage"); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201704031946479_SpecificationAttributeAllowFiltering.resx b/src/Libraries/SmartStore.Data/Migrations/201704031946479_SpecificationAttributeAllowFiltering.resx deleted file mode 100644 index dc3959442c..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201704031946479_SpecificationAttributeAllowFiltering.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201705032238367_RenameColorSquaresRgb.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201705032238367_RenameColorSquaresRgb.Designer.cs deleted file mode 100644 index 3ab42f2a66..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201705032238367_RenameColorSquaresRgb.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class RenameColorSquaresRgb : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(RenameColorSquaresRgb)); - - string IMigrationMetadata.Id - { - get { return "201705032238367_RenameColorSquaresRgb"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201705032238367_RenameColorSquaresRgb.cs b/src/Libraries/SmartStore.Data/Migrations/201705032238367_RenameColorSquaresRgb.cs deleted file mode 100644 index 0a953a1a0b..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201705032238367_RenameColorSquaresRgb.cs +++ /dev/null @@ -1,63 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System.Data.Entity.Migrations; - using System.Web.Hosting; - using Core.Data; - using Setup; - - public partial class RenameColorSquaresRgb : DbMigration, IDataSeeder - { - private bool IsSqlServer - { - get - { - return HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer; - } - } - - public override void Up() - { - if (IsSqlServer) - { - RenameColumn("dbo.ProductAttributeOption", "ColorSquaresRgb", "Color"); - RenameColumn("dbo.ProductVariantAttributeValue", "ColorSquaresRgb", "Color"); - } - else - { - AddColumn("dbo.ProductAttributeOption", "Color", c => c.String(maxLength: 100)); - AddColumn("dbo.ProductVariantAttributeValue", "Color", c => c.String(maxLength: 100)); - } - } - - public override void Down() - { - if (IsSqlServer) - { - RenameColumn("dbo.ProductVariantAttributeValue", "Color", "ColorSquaresRgb"); - RenameColumn("dbo.ProductAttributeOption", "Color", "ColorSquaresRgb"); - } - else - { - DropColumn("dbo.ProductVariantAttributeValue", "Color"); - DropColumn("dbo.ProductAttributeOption", "Color"); - } - } - - public void Seed(SmartObjectContext context) - { - if (!IsSqlServer) - { - context.Execute("Update [dbo].[ProductAttributeOption] Set [Color] = [ColorSquaresRgb]"); - context.Execute("Update [dbo].[ProductVariantAttributeValue] Set [Color] = [ColorSquaresRgb]"); - - context.Execute("Alter Table [dbo].[ProductAttributeOption] Drop Column [ColorSquaresRgb]"); - context.Execute("Alter Table [dbo].[ProductVariantAttributeValue] Drop Column [ColorSquaresRgb]"); - } - } - - public bool RollbackOnFailure - { - get { return false; } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201705032238367_RenameColorSquaresRgb.resx b/src/Libraries/SmartStore.Data/Migrations/201705032238367_RenameColorSquaresRgb.resx deleted file mode 100644 index 1394eaee45..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201705032238367_RenameColorSquaresRgb.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201705102339006_V3Final.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201705102339006_V3Final.Designer.cs deleted file mode 100644 index e155f7909b..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201705102339006_V3Final.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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.1.3-40302")] - public sealed partial class V3Final : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(V3Final)); - - string IMigrationMetadata.Id - { - get { return "201705102339006_V3Final"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201705102339006_V3Final.cs b/src/Libraries/SmartStore.Data/Migrations/201705102339006_V3Final.cs deleted file mode 100644 index 79b6bd0ffa..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201705102339006_V3Final.cs +++ /dev/null @@ -1,1026 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity; - using System.Data.Entity.Migrations; - using System.Linq; - using Core.Caching; - using Core.Domain.Configuration; - using Core.Infrastructure; - using Setup; - using Core.Domain.Security; - using Core.Domain.Catalog; - - public partial class V3Final : DbMigration, ILocaleResourcesProvider, IDataSeeder - { - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - MigrateSettings(context); - - // Remove permission records - var permissionRecords = context.Set(); - var csRecord = permissionRecords.Where(x => x.SystemName.Equals("ManageContentSlider")).FirstOrDefault(); - var quRecord = permissionRecords.Where(x => x.SystemName.Equals("ManageQuantityUnits")).FirstOrDefault(); - - if (csRecord != null) - permissionRecords.Remove(csRecord); - - if (quRecord != null) - permissionRecords.Remove(quRecord); - - // Add new product template... - context.Set().AddOrUpdate(x => x.ViewPath, new ProductTemplate - { - Name = "Default Product Template", - ViewPath = "Product", - DisplayOrder = 10 - }); - - // ...and set it as default where applicable - var newProductTemplate = context.Set().FirstOrDefault(x => x.Name == "Default Product Template") - ?? context.Set().OrderByDescending(x => x.Id).FirstOrDefault(); - - if (newProductTemplate != null) - { - context.ExecuteSqlCommand("Update [Product] Set [ProductTemplateId] = {0}", true, null, newProductTemplate.Id); - } - - // ...finally delete old ones - var oldProductTemples = context.Set().Where(x => x.ViewPath == "ProductTemplate.Simple" || x.ViewPath == "ProductTemplate.Grouped").ToList(); - context.Set().RemoveRange(oldProductTemples); - - context.SaveChanges(); - } - - public void MigrateSettings(SmartObjectContext context) - { - // Change ProductSortingEnum.Position > Relevance - var settings = context.Set().Where(x => x.Name == "CatalogSettings.DefaultSortOrder" && x.Value.StartsWith("Position")).ToList(); - if (settings.Any()) - { - settings.Each(x => x.Value = "Relevance"); - EngineContext.Current.Resolve().Clear(); - } - - context.MigrateSettings(x => { - x.DeleteGroup("ContentSlider"); - }); - - // Change MediaSettings.ProductThumbPictureSize to 250 if smaller - var keys = new string[] { "MediaSettings.ProductThumbPictureSize", "MediaSettings.CategoryThumbPictureSize", "MediaSettings.ManufacturerThumbPictureSize", "MediaSettings.CartThumbPictureSize", "MediaSettings.MiniCartThumbPictureSize" }; - settings = context.Set().Where(x => keys.Contains(x.Name)).ToList(); - if (settings.Any()) - { - settings.Each(x => - { - var size = x.Value.Convert(); - if (size < 250) - { - x.Value = "250"; - } - }); - } - - // Change MediaSettings.ProductDetailsPictureSize to 600 if smaller - var setting = context.Set().FirstOrDefault(x => x.Name == "MediaSettings.ProductDetailsPictureSize" || x.Name == "MediaSettings.AssociatedProductPictureSize"); - if (setting != null && setting.Value.Convert() < 600) - { - setting.Value = "600"; - } - - // Change MediaSettings.VariantValueThumbPictureSize to 70 if smaller - setting = context.Set().FirstOrDefault(x => x.Name == "MediaSettings.VariantValueThumbPictureSize"); - if (setting != null && setting.Value.Convert() < 70) - { - setting.Value = "70"; - } - - // Change CatalogSettings.PageShareCode (16px > 32px) - setting = context.Set().FirstOrDefault(x => x.Name == "CatalogSettings.PageShareCode"); - if (setting != null) - { - setting.Value = "
"; - } - - // Change ThemeSettings.DefaultTheme to 'Flex' - setting = context.Set().FirstOrDefault(x => x.Name == "ThemeSettings.DefaultTheme"); - if (setting != null) - { - setting.Value = "Flex"; - } - - // Change CatalogSettings.ShowProductsFromSubcategories (to true) - setting = context.Set().FirstOrDefault(x => x.Name == "CatalogSettings.ShowProductsFromSubcategories"); - if (setting != null) - { - setting.Value = "True"; - } - - // Change CatalogSettings.RecentlyAddedProductsNumber (to 100) - setting = context.Set().FirstOrDefault(x => x.Name == "CatalogSettings.RecentlyAddedProductsNumber"); - if (setting != null && setting.Value.Convert() < 100) - { - setting.Value = "100"; - } - - // Change shoppingcartsettings.crosssellsnumber (to 24) - setting = context.Set().FirstOrDefault(x => x.Name == "shoppingcartsettings.crosssellsnumber"); - if (setting != null && setting.Value.Convert() < 24) - { - setting.Value = "24"; - } - - // Change CatalogSettings.ShowProductsFromSubcategories (to true) - setting = context.Set().FirstOrDefault(x => x.Name == "CatalogSettings.ShowProductsFromSubcategories"); - if (setting != null) - { - setting.Value = "True"; - } - - // Change CatalogSettings.ShowManufacturerPictures to true - setting = context.Set().FirstOrDefault(x => x.Name == "CatalogSettings.ShowManufacturerPictures"); - if (setting != null) - { - setting.Value = "True"; - } - - context.SaveChanges(); - - context.MigrateSettings(x => - { - x.Add("MediaSettings.DefaultThumbnailAspectRatio", 1); - x.Delete("ShoppingCartSettings.MiniShoppingCartProductNumber"); - }); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Catalog.Categories.Fields.BadgeText", - "Badge text", - "Badge-Text", - "Gets or sets the text of the badge which will be displayed next to the category link within menus.", - "Legt den Text der Badge fest, die innerhalb von Menus neben den Menueinträgen dargestellt wird."); - - builder.AddOrUpdate("Admin.Catalog.Categories.Fields.BadgeStyle", - "Badge style", - "Badge-Style", - "Gets or sets the type of the badge which will be displayed next to the category link within menus.", - "Legt den Stil der Badge fest, die innerhalb von Menus neben den Menueinträgen dargestellt wird."); - - builder.AddOrUpdate("Admin.Header.ClearDbCache", - "Clear database cache", - "Datenbank Cache löschen"); - - builder.AddOrUpdate("Admin.System.Warnings.TaskScheduler.OK", - "The task scheduler can poll and execute tasks.", - "Der Task-Scheduler kann Hintergrund-Aufgaben planen und ausführen."); - - builder.AddOrUpdate("Admin.System.Warnings.TaskScheduler.Fail", - "The task scheduler cannot poll and execute tasks. Base URL: {0}, Status: {1}. Please specify a working base url in web.config, setting 'sm:TaskSchedulerBaseUrl'.", - "Der Task-Scheduler kann keine Hintergrund-Aufgaben planen und ausführen. Basis-URL: {0}, Status: {1}. Bitte legen Sie eine vom Webserver erreichbare Basis-URL in der web.config Datei fest, Einstellung: 'sm:TaskSchedulerBaseUrl'."); - - builder.AddOrUpdate("Products.NotFound", - "The product with ID {0} was not found.", - "Das Produkt mit der ID {0} wurde nicht gefunden."); - - builder.AddOrUpdate("Products.Deleted", - "The product with ID {0} has been deleted.", - "Das Produkt mit der ID {0} wurde gelöscht."); - - builder.AddOrUpdate("Common.ShowLess", "Show less", "Weniger anzeigen"); - builder.AddOrUpdate("Menu.ServiceMenu", "Help & Services", "Hilfe & Service"); - - // Search - builder.Delete("PageTitle.Search"); - builder.Delete("Search.ResultFor"); - - builder.AddOrUpdate("Search", "Search", "Suchen"); - builder.AddOrUpdate("Search.Title", "Search", "Suche"); - builder.AddOrUpdate("Search.PageTitle", "Search result for {0}", "Suchergebnis für {0}"); - builder.AddOrUpdate("Search.PagingInfo", "{0} of {1}", "{0} von {1}"); - builder.AddOrUpdate("Search.DidYouMean", "Did you mean?", "Meinten Sie?"); - builder.AddOrUpdate("Search.Hits", "Hits", "Treffer"); - builder.AddOrUpdate("Search.NoResultsText", "Your search did not match any products.", "Ihre Suche ergab leider keine Produkttreffer."); - builder.AddOrUpdate("Search.FilterNoResultsText", "Your filter did not match any products.", "Ihr Filter ergab leider keine Produkttreffer."); - builder.AddOrUpdate("Search.NumHits", "{0} Hits", "{0} Treffer"); - builder.AddOrUpdate("Search.InstantSearch", "Instant Search", "Instantsuche"); - builder.AddOrUpdate("Search.ResultFiltering", "Result filtering", "Ergebnisfilterung"); - builder.AddOrUpdate("Search.FilterBy", "Filter by", "Filtern nach"); - builder.AddOrUpdate("Search.TermInCategory", "in {0}", "in {0}"); - builder.AddOrUpdate("Search.TermFromBrand", "from {0}", "von {0}"); - builder.AddOrUpdate("Search.SearchBox.Tooltip", "What are you looking for?", "Wonach suchen Sie?"); - builder.AddOrUpdate("Search.SearchTermMinimumLengthIsNCharacters", - "The minimum length for the search term is {0} characters.", - "Die Mindestlänge für den Suchbegriff beträgt {0} Zeichen."); - builder.AddOrUpdate("Search.TermCorrectedHint", - "Displaying results for {0}. Your search for {1} did not match any results.", - "Ergebnisse für {0} werden angezeigt. Ihre Suche nach {1} ergab leider keine Treffer."); - - builder.Delete("Enums.SmartStore.Core.Domain.Catalog.ProductSortingEnum.Position"); - - builder.Delete( - "Admin.Configuration.Settings.Catalog.ProductSearchAutoCompleteEnabled", - "Admin.Configuration.Settings.Catalog.ProductSearchAutoCompleteEnabled.Hint", - "Admin.Configuration.Settings.Catalog.ProductSearchAutoCompleteNumberOfProducts", - "Admin.Configuration.Settings.Catalog.ProductSearchAutoCompleteNumberOfProducts.Hint", - "Admin.Configuration.Settings.Catalog.SearchPageProductsPerPage", - "Admin.Configuration.Settings.Catalog.SearchPageProductsPerPage.Hint", - "Admin.Configuration.Settings.Catalog.ProductSearchAllowCustomersToSelectPageSize", - "Admin.Configuration.Settings.Catalog.ProductSearchAllowCustomersToSelectPageSize.Hint", - "Admin.Configuration.Settings.Catalog.ProductSearchPageSizeOptions", - "Admin.Configuration.Settings.Catalog.ProductSearchPageSizeOptions.Hint", - "Admin.Configuration.Settings.Catalog.ShowProductImagesInSearchAutoComplete", - "Admin.Configuration.Settings.Catalog.ShowProductImagesInSearchAutoComplete.Hint", - "Admin.Configuration.Settings.Catalog.SuppressSkuSearch", - "Admin.Configuration.Settings.Catalog.SuppressSkuSearch.Hint", - "Admin.Configuration.Settings.Catalog.SearchDescriptions", - "Admin.Configuration.Settings.Catalog.SearchDescriptions.Hint", - "Admin.Configuration.Settings.Catalog.ProductSearchSettings", - "Admin.Configuration.Settings.Catalog.ProductsByTagPageSize", - "Admin.Configuration.Settings.Catalog.ProductsByTagPageSize.Hint" - ); - - builder.AddOrUpdate("Admin.Configuration.Settings.Search.InstantSearchEnabled", - "Enable Instant Search", - "Instant-Suche aktivieren", - "Activates Instant Search (Search-As-You-Type). Search hits and suggestions are already displayed before user finishes typing the search term.", - "Aktiviert die Instant-Suche (Search-As-You-Type). Suchtreffer und -Vorschläge werden schon während der Eingabe des Suchbegriffs angezeigt."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Search.ShowProductImagesInInstantSearch", - "Show product images", - "Produktbilder anzeigen", - "Specifies whether to display product images in instant search.", - "Legt fest, ob Produktbilder in der Instantsuche angezeigt werden sollen."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Search.InstantSearchNumberOfProducts", - "Number of products", - "Produktanzahl", - "Specifies the number of product hits displayed in instant search.", - "Legt die Anzahl der angezeigten Produkt-Treffer in der Instantsuche fest."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Search.InstantSearchTermMinLength", - "Minimum search term length", - "Minimale Suchbegrifflänge", - "Specifies the minimum length of a search term from which to show the result of instant search.", - "Legt die minimale Länge eines Suchbegriffs fest, ab dem das Ergebnis der Instantsuche angezeigt wird."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Search.SearchFields", - "Search fields", - "Suchfelder", - "Specifies additional search fields. The product name is always searched.", - "Legt zusätzlich zu durchsuchende Felder fest. Der Produktname wird grundsätzlich immer durchsucht."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.DefaultProductListPageSize", - "Number of products displayed per page", - "Anzahl pro Seite angezeigter Produkte", - "Specifies the number of products displayed per page in a product list.", - "Legt die Anzahl der pro Seite angezeigten Produkte in einer Produktliste fest."); - - builder.AddOrUpdate("Admin.Validation.ValueRange", - "The value must be between {0} and {1}.", - "Der Wert muss zwischen {0} und {1} liegen."); - - builder.AddOrUpdate("Admin.Validation.ValueGreaterThan", - "The value must be greater than {0}.", - "Der Wert muss größer als {0} sein."); - - builder.AddOrUpdate("Admin.Validation.InvalidPath", - "The path \"{0}\" is invalid. Please enter a valid path.", - "Der Pfad \"{0}\" ist ungültig. Bitte geben Sie einen gültigen Pfad ein."); - - builder.AddOrUpdate("Common.AdditionalShippingSurcharge", - "zzgl. {0} zusätzlicher Versandgebühr", - "Plus {0} shipping surcharge"); - - builder.DeleteFor("Admin.Configuration.ContentSlider"); - builder.DeleteFor("Admin.ContentManagement.ContentSlider"); - builder.DeleteFor("Admin.ContentSlider.Slide"); - builder.Delete("Admin.Themes.ContentSlider"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowShortDescriptionInGridStyleLists", - "Show short description in product lists", - "Zeige Kurzbeschreibung in Produktlisten", - "Specifies whether the product short description should be displayed in product lists", - "Legt fest, ob die Produkt-Kurzbeschreibung auch in Produktlisten angezeigt werden sollen"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowManufacturerInGridStyleLists", - "Show brand in product lists", - "Zeige Hersteller/Marke in Produktlisten", - "Specifies whether the brand name should be displayed in grid style product lists", - "Legt fest, ob der Markenname auch in Rasterstil Produktlisten angezeigt werden sollen"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowManufacturerLogoInLists", - "Show brand logo instead of name", - "Zeige Marken-Logo statt -Name", - "Specifies whether the brand logo should be displayed in line style product lists. Falls back to textual name if no logo has been uploaded.", - "Legt fest, ob das Marken-Logo in Produktlisten dargestellt werden soll (nicht anwendbar in Rasteransicht). Wenn kein Logo hochgeladen wurde, wird grundsätzlich der Name angezeigt."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowProductOptionsInLists", - "Show variant names in product lists", - "Zeige Variantnamen in Produktlisten", - "Specifies whether variant names should be displayed in product lists", - "Legt fest, ob Variantnamen in Produktlisten angezeigt werden sollen"); - - builder.AddOrUpdate("Products.PlusOption", "Further option", "Weitere Option"); - builder.AddOrUpdate("Products.PlusOptions", "More options", "Weitere Optionen"); - - builder.AddOrUpdate("Products.Longdesc.More", "Show more", "Mehr anzeigen"); - builder.AddOrUpdate("Products.Longdesc.Less", "Show less", "Weniger anzeigen"); - - builder.AddOrUpdate("Products.Tags.ProductsTaggedWith", "Products tagged with {0}", "Produkte markiert mit {0}"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.DefaultPageSizeOptions", - "Page size options", - "Auswahlmöglichkeiten für Seitengröße", - "Comma-separated page size options that a customer can select in product lists.", - "Kommagetrennte Liste mit Optionen für Seitengröße, die ein Kunde in Produktlisten wählen kann."); - - builder.AddOrUpdate("Common.ListIsEmpty", "The list is empty.", "Die Liste ist leer."); - - builder.AddOrUpdate("Products.SortByX", "Sort by {0}", "Sortiere nach {0}"); - builder.AddOrUpdate("Products.SwitchToGrid", "Show", "Zur Rasteransicht wechseln"); - builder.AddOrUpdate("Products.SwitchToList", "Show", "Zur Listenansicht wechseln"); - builder.AddOrUpdate("Products.ToFilter", "Filter", "Filtern"); - builder.AddOrUpdate("Products.ToSort", "Sort", "Sortieren"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.ProductSortingEnum.CreatedOn", "Newest Arrivals", "Neu eingetroffen"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.ProductSortingEnum.Initial", "Position", "Position"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.ProductSortingEnum.PriceAsc", "Price: Low to High", "Preis: aufsteigend"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.ProductSortingEnum.PriceDesc", "Price: High to Low", "Preis: absteigend"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.ProductSortingEnum.Relevance", "Relevance", "Beste Ergebnisse"); - - builder.AddOrUpdate("Pager.PageX", "Page {0}", "Seite {0}"); - builder.AddOrUpdate("Pager.XPerPage", "{0} per Page", "{0} pro Seite"); - builder.AddOrUpdate("Pager.PageXOfY", "Page {0} of {1}", "Seite {0} von {1}"); - builder.AddOrUpdate("Pager.PageXOfYShort", "{0} of {1}", "{0} von {1}"); - - builder.AddOrUpdate("Products.Price.OldPrice", "Regular", "Regulär"); - builder.AddOrUpdate("Products.Sku", "SKU", "Art.-Nr."); - builder.AddOrUpdate("Products.ChooseColorX", "Choose {0}", "{0} auswählen"); - - - builder.AddOrUpdate("Tax.LegalInfoShort", "Prices {0}, plus shipping", "Preise {0}, zzgl. Versandkosten"); - builder.AddOrUpdate("Tax.LegalInfoShort2", "Prices {0}, plus shipping", "Preise {0}, zzgl. Versandkosten"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Search.SearchMode.ExactMatch", "Is equal to (term)", "Ist gleich (Term)"); - builder.AddOrUpdate("Enums.SmartStore.Core.Search.SearchMode.StartsWith", "Starts with (prefix)", "Beginnt mit (Prefix)"); - builder.AddOrUpdate("Enums.SmartStore.Core.Search.SearchMode.Contains", "Contains (wildcard)", "Beinhaltet (Wildcard)"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Search.WildcardSearchNote", - "The wildcard mode can slow down the search for a large number of products.", - "Der Wildcard-Modus kann bei einer großen Anzahl an Produkten die Suche verlangsamen."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Search.SearchFieldsNote", - "The standard search supports the search fields Name, SKU and Short Description. For more fields, a search plugin like MegaSearch Plugin is required.", - "In der Standardsuche können die Felder Name, SKU und Kurzbeschreibung durchsucht werden. Für weitere Felder ist ein Such-Plugin wie bspw. dem MegaSearch Plugin notwendig."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Search.SearchMode", - "Search mode", - "Suchmodus", - "Specifies the search mode. Please keep in mind that the search mode can - depending on catalog size - strongly affect search performance. 'Is equal to' is the fastest, 'Contains' the slowest.", - "Legt den Suchmodus fest. Bitte beachten Sie, dass der Suchmodus die Geschwindigkeit der Suche (abhängig von der Produktanzahl) beeinflusst. 'Ist gleich' ist am schnellsten, 'Beinhaltet' am langsamsten."); - - builder.AddOrUpdate("Admin.Configuration.DeliveryTimes.CannotDeleteAssignedProducts", - "The delivery time cannot be deleted. It has associated products or product variants.", - "Die Lieferzeit kann nicht gelöscht werden. Ihr sind Produkte oder Produktvarianten zugeordnet."); - - builder.AddOrUpdate("Media.Manufacturer.ImageLinkTitleFormat", "All products from {0}", "Alle Produkte von {0}"); - builder.AddOrUpdate("Manufacturers.List", "All Brands", "Alle Marken"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.GridStyleListColumnSpan", - "Products per row in grid style list", - "Anzahl Produkte pro Zeile in Rasteransicht", - "Sets the responsive behavior of the grid style product list. The wider the screen, the more products are arranged in a row.", - "Legt das responsive Verhalten der Produktliste in der Rasteransicht fest. Je breiter der Bildschirm, desto mehr Produkte werden in einer Zeile angeordnet."); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.GridColumnSpan.Max2Cols", "Always 2 (mobile & desktop) -not recommended", "Immer 2 (Mobil & Desktop) - nicht empfohlen"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.GridColumnSpan.Max3Cols", "2 (mobile) to 3 (desktop)", "2 (Mobil) bis 3 (Desktop)"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.GridColumnSpan.Max4Cols", "2 (mobile) to 4 (desktop) - recommended", "2 (Mobil) bis 4 (Desktop) - empfohlen"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.GridColumnSpan.Max5Cols", "2 (mobile) to 5 (desktop)", "2 (Mobil) bis 5 (Desktop)"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.GridColumnSpan.Max6Cols", "2 (mobile) to 6 (desktop)", "2 (Mobil) bis 6 (Desktop)"); - - builder.AddOrUpdate("Catalog.Manufacturerall.Numbers", "#", "#"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.SortManufacturersAlphabetically", - "Sort manufacturers alphabetically", - "Hersteller alphabetisch sortieren", - "Specifies whether manufacturers on the manufacturer overview page will be displayed sorted alphabetically.", - "Legt fest ob Hersteller auf der Herstellerübersichtsseite alphabetisch sortiert dargestellt werden."); - - builder.AddOrUpdate("Common.NoImageAvail", "No image available", "Bild wird nachgereicht"); - - builder.AddOrUpdate("Products.Bundle.PriceWithoutDiscount.Note", "Instead of", "Statt"); - builder.AddOrUpdate("Products.Bundle.PriceWithDiscount.Note", "As bundle only", "Im Set nur"); - builder.AddOrUpdate("Products.Price", "Price", "Preis"); - builder.AddOrUpdate("Products.TierPrices", "Block pricing", "Staffelpreise"); - builder.AddOrUpdate("Products.ManufacturerPartNumber", "MPN", "MPN"); - builder.AddOrUpdate("Products.Details", "Description", "Beschreibung"); - builder.AddOrUpdate("Products.Specs", "Features", "Merkmale"); - builder.AddOrUpdate("Products.Availability.InStockWithQuantity", "{0} in stock", "{0} am Lager"); - builder.AddOrUpdate("Products.Availability.InStock", "In stock", "Vorrätig"); - builder.AddOrUpdate("Products.Availability.OutOfStock", "Out of stock", "Vergriffen"); - builder.AddOrUpdate("Products.NewProducts", "What's New", "Neu eingetroffen"); - - builder.AddOrUpdate("Reviews.Overview.First", "Be the first to review this item", "Geben Sie die erste Bewertung ab"); - builder.AddOrUpdate("Reviews.Overview.AddNew", "Write a review", "Bewertung schreiben"); - builder.AddOrUpdate("Reviews.Overview.ReadAll", "Read all reviews", "Alle Bewertungen lesen"); - builder.AddOrUpdate("Reviews.Empty", "There are no reviews yet", "Es liegen keine Bewertungen vor"); - builder.AddOrUpdate("Reviews.Fields.Rating", "Your rating?", "Ihre Bewertung?"); - builder.AddOrUpdate("Reviews.Fields.Title", "Headline for your review", "Titel Ihrer Bewertung"); - builder.AddOrUpdate("Reviews.Fields.ReviewText", "Your opinion on the product", "Ihre Meinung zum Produkt"); - builder.AddOrUpdate("Reviews.SubmitButton", "Submit review", "Bewertung absenden"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Search.FilterMinHitCount", - "Minimum hit count for filters", - "Minimale Trefferanzahl für Filter", - "Specifies the minimum number of search hits from which to show a filter.", - "Legt die minimale Anzahl an Suchtreffern fest, ab dem ein Filter angezeigt wird."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Search.FilterMaxChoicesCount", - "Maximum number of filters", - "Maximale Anzahl an Filtern", - "Specifies the maximum number of filters per group.", - "Legt die maximale Anzahl an Filtern pro Gruppe fest."); - - builder.AddOrUpdate("Enums.SmartStore.Core.Search.Facets.FacetSorting.HitsDesc", - "Hit count: highest first", - "Trefferanzahl: Höchste zuerst"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Search.Facets.FacetSorting.ValueAsc", - "Name: A to Z", - "Name: A bis Z"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Search.Facets.FacetSorting.DisplayOrder", - "According to display order", - "Gemäß Reihenfolge"); - - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Values.ViewLink", - "Edit Options (Total: {0})", - "Optionen bearbeiten (Anzahl: {0})"); - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Values.EditAttributeDetails", - "Options for attribute '{0}'. Product: {1}", - "Optionen für Attribut '{0}'. Produkt: {1}"); - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Values", "Options", "Optionen"); - builder.AddOrUpdate("Admin.Catalog.Attributes.CheckoutAttributes.Values", "Options", "Optionen"); - - - builder.AddOrUpdate("Common.CopyToClipboard.Failed", "Failed to copy.", "Kopieren ist fehlgeschlagen."); - - builder.AddOrUpdate("PDFPackagingSlip.Gtin", "EAN", "Gtin"); - - builder.AddOrUpdate("Common.Error.AliasAlreadyExists", - "An alias \"{0}\" already exists.", - "Ein Alias \"{0}\" existiert bereits."); - - builder.AddOrUpdate("Admin.Configuration.DeliveryTimes.Fields.IsDefault", - "Is default", - "Ist Standard", - "Specifies the default delivery time.", - "Bestimmt die Standard-Lieferzeit"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowDefaultDeliveryTime", - "Show default delivery time", - "Zeige Standard-Lieferzeit", - "Specifies whether to show the default delivery time if there is none assigned to a product.", - "Bestimmt ob die Standard-Lieferzeit für ein Produkt angezeigt wird, wenn dem Produkt keine Lieferzeit zugewiesen wurde."); - - builder.AddOrUpdate("Admin.Catalog.Products.Fields.QuantityStep", - "Quantity step", - "Schrittweite", - "Specifies the incremental respectively decremental step on usage of +/-. Orderable quantities are limited to a multiple of this value.", - "Bestimmt den Wert, um den die Bestellmenge erhöht bzw. vermindert wird, wenn ein Kunde die +/- Steuerelemente benutzt. Die Bestellmenge ist auf ein Vielfaches dieses Wertes beschränkt."); - - builder.AddOrUpdate("Admin.Catalog.Products.Fields.QuantiyControlType", - "Control type", - "Steuerelement", - "Specifies the control type to enter the quantity.", - "Bestimmt das Steuerelement für die Angabe der Bestellmenge."); - - builder.AddOrUpdate("Admin.Catalog.Products.Fields.HideQuantityControl", - "Hide quantity control on product pages", - "Angabe der Bestellmenge auf Produktseiten nicht anbieten", - "Specifies whether to hide the quantity control on product pages.", - "Bestimmt ob eine Element zur Angabe der Bestellmenge auf Produktdetailseiten dargestellt wird."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowManufacturerInProductDetail", - "Display manufacturer", - "Hersteller anzeigen.", - "Specifies whether the product manufacturer will be displayed on product detail pages.", - "Bestimmt ob der Hersteller eines Produktes auf Produktdetailseiten angezeigt wird."); - builder.AddOrUpdate("Admin.Catalog.Products.Fields.CustomsTariffNumber", - "Customs Tariff Number", - "Zolltarifnummer", - "Specifies the customs tariff number of the product.", - "Legt die Zolltarifnummer des Produktes fest."); - - builder.AddOrUpdate("Admin.Catalog.Products.Fields.CountryOfOriginId", - "Country of Origin", - "Herkunftsland", - "Specifies the country of origin of the product.", - "Legt das Herkunftsland des Produktes fest."); - - builder.Delete( - "Products.ProductHasBeenAddedToTheWishlist.Link", - "Products.ProductHasBeenAddedToTheCart.Link"); - - builder.AddOrUpdate("ShoppingCart.AddToWishlist", "Add to wishlist", "Auf die Wunschliste"); - builder.AddOrUpdate("ShoppingCart.Mini.AddedItemToCart", "The product {0} has been successfully added to your cart", "Das Produkt {0} wurde erfolgreich in den Warenkorb gelegt"); - builder.AddOrUpdate("ShoppingCart.Mini.AddedItemToWishlist", "The product {0} has been added to your wishlist", "Das Produkt {0} wurde erfolgreich auf ihrer Wunschliste vermerkt"); - builder.AddOrUpdate("ShoppingCart.Mini.AddedItemToCompare", "The product {0} has been successfully added to your compare list", "Das Produkt {0} wurde der Vergleichsliste erfolgreich hinzugefügt"); - builder.AddOrUpdate("ShoppingCart.Mini.EmptyCart.Title", "Shopping cart empty", "Warenkorb ist leer"); - builder.AddOrUpdate("ShoppingCart.Mini.EmptyWishlist.Title", "Wishlist empty", "Wunschliste ist leer"); - builder.AddOrUpdate("ShoppingCart.Mini.EmptyCompare.Title", "Compare list empty", "Vergleichsliste ist leer"); - builder.AddOrUpdate("ShoppingCart.Mini.EmptyCart.Info", - "You have not added any product to your cart yet. Use the icon to add a product to your cart.", - "Sie haben noch keine Produkte in ihren Warenkorb gelegt.
Benutzen Sie das Symbol, um ein Produkt in den Warenkorb zu legen."); - builder.AddOrUpdate("ShoppingCart.Mini.EmptyWishlist.Info", - "You have not added any product to your wishlist yet. Use the icon to add a product to your wishlist.", - "Sie haben noch keine Produkte auf ihrer Wunschliste vermerkt.
Benutzen Sie das Symbol, um ein Produkt in ihrer Wunschliste zu vermerken."); - builder.AddOrUpdate("ShoppingCart.Mini.EmptyCompare.Info", - "You have not added any product to your compare list yet. Use the icon to add a product to your compare list.", - "Sie haben noch keine Produkte in ihrer Vergleichsliste.
Benutzen Sie das Symbol, um ein Produkt in die Vergleichsliste aufzunehmen."); - - builder.AddOrUpdate("ShoppingCart.UnitPrice", "Price", "Preis"); - - builder.AddOrUpdate("ShoppingCart.DiscountCouponCode", "I have a discount code", "Ich habe einen Rabattcode"); - builder.AddOrUpdate("ShoppingCart.GiftCardCouponCode", "I have a gift card", "Ich habe einen Gutschein"); - builder.AddOrUpdate("ShoppingCart.EstimateShipping", "Estimate shipping", "Versandkosten schätzen"); - - builder.AddOrUpdate("PageTitle.Blog.Month", "Blog entries in {0}", "Blog Einträge des Monats {0}"); - builder.AddOrUpdate("PageTitle.Blog.Tag", "Blog entries for the tag {0}", "Blog-Einträge für das Stichwort {0}"); - builder.AddOrUpdate("Metadesc.Blog.Month", "Blog entries in {0}", "Blog Einträge des Monats {0}"); - builder.AddOrUpdate("Metadesc.Blog.Tag", "Blog entries for the tag {0}", "Blog-Einträge für das Stichwort {0}"); - - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Values.Fields.Picture", - "Picture", - "Bild", - "Choose a picture which will be displayed as the selector for the attribute.", - "Wählen Sie ein Bild, welches als Auswahlelement für das Attribut angezeigt werden soll."); - - builder.Delete( - "Admin.Configuration.Settings.ShoppingCart.MiniShoppingCartProductNumber", - "Admin.Configuration.Settings.ShoppingCart.MiniShoppingCartProductNumber.Hint"); - - builder.AddOrUpdate("ShoppingCart.MoveToWishlist", "Move to wishlist", "In die Wunschliste verschieben"); - builder.AddOrUpdate("Products.Compare.CompareNow", "Compare now", "Jetzt vergleichen"); - builder.AddOrUpdate("Common.Or", "Or", "Oder"); - - builder.AddOrUpdate("Common.Error.OptionAlreadyExists", - "The option \"{0}\" already exists.", - "Die Option \"{0}\" existiert bereits."); - - builder.AddOrUpdate("Common.Error.ChooseDifferentValue", - "Please choose a different value.", - "Bitte wählen Sie einen anderen Wert."); - - builder.AddOrUpdate("Common.Menu", "Menu", "Menü"); - - builder.Delete( - "Admin.Configuration.Settings.GeneralCommon.FullTextSettings", - "Admin.Configuration.Settings.GeneralCommon.FullTextSettings.Supported", - "Admin.Configuration.Settings.GeneralCommon.FullTextSettings.NotSupported", - "Admin.Configuration.Settings.GeneralCommon.FullTextSettings.CurrenlyEnabled", - "Admin.Configuration.Settings.GeneralCommon.FullTextSettings.CurrenlyDisabled", - "Admin.Configuration.Settings.GeneralCommon.FullTextSettings.Disable", - "Admin.Configuration.Settings.GeneralCommon.FullTextSettings.Disabled", - "Admin.Configuration.Settings.GeneralCommon.FullTextSettings.Enable", - "Admin.Configuration.Settings.GeneralCommon.FullTextSettings.Enabled", - "Admin.Configuration.Settings.GeneralCommon.FullTextSettings.SearchMode", - "Admin.Configuration.Settings.GeneralCommon.FullTextSettings.SearchMode.Hint", - "Enums.SmartStore.Core.Domain.Common.FulltextSearchMode.ExactMatch", - "Enums.SmartStore.Core.Domain.Common.FulltextSearchMode.And", - "Enums.SmartStore.Core.Domain.Common.FulltextSearchMode.Or" - ); - - builder.AddOrUpdate("Common.Options.Count", "Number options", "Anzahl Optionen"); - builder.AddOrUpdate("Common.Options.Add", "Add option", "Option hinzufügen"); - builder.AddOrUpdate("Common.Options.Edit", "Edit option", "Option bearbeiten"); - - builder.AddOrUpdate("Admin.Validation.RequiredField", "Please enter \"{0}\".", "Bitte \"{0}\" angeben."); - - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Values.NumberOfCopiedOptions", - "{0} option(s) have been copied.", - "Es wurden {0} Option(en) kopiert."); - - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Values.CopyOptions", - "Copy set options", - "Set Optionen übernehmen"); - - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Values.CopyOptionsHint", - "Would you like to copy all options from set \"{0}\"?", - "Möchten Sie alle Optionen von Set \"{0}\" übernehmen?"); - - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Values.AskExistingValues", - "What should be done with already existing options?", - "Was soll mit den bereits vorhandenen Optionen geschehen?"); - - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Values.MergeExistingValues", - "Merge all options", - "Alle Optionen zusammenführen"); - - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Values.DeleteExistingValues", - "Delete existing options", - "Vorhandene Optionen löschen"); - - builder.AddOrUpdate("Offcanvas.Menu.Categories", "Categories", "Sortiment"); - builder.AddOrUpdate("Offcanvas.Menu.Brands", "Brands", "Marken"); - builder.AddOrUpdate("Offcanvas.Menu.Service", "Service", "Service"); - builder.AddOrUpdate("Offcanvas.Menu.ShowCurrentCat", "Show {0}", "{0} anzeigen"); - - var aliasHintEn = "Seo-compliant URL alias for search filters (optional)."; - var aliasHintDe = "SEO-konformer URL-Alias für Suchfilter (optional)."; - - builder.AddOrUpdate("Admin.Catalog.Attributes.SpecificationAttributes.Fields.Alias", "Alias", "Alias", aliasHintEn, aliasHintDe); - builder.AddOrUpdate("Admin.Catalog.Attributes.SpecificationAttributes.Options.Fields.Alias", "Alias", "Alias", aliasHintEn, aliasHintDe); - - builder.AddOrUpdate("Admin.Catalog.Attributes.ProductAttributes.Fields.Alias.Hint", aliasHintEn, aliasHintDe); - builder.AddOrUpdate("Admin.Catalog.Attributes.SpecificationAttributes.Fields.Alias.Hint", aliasHintEn, aliasHintDe); - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Values.Fields.Alias.Hint", aliasHintEn, aliasHintDe); - - builder.AddOrUpdate("Admin.Configuration.Settings.Search.CommonFacet.Alias", "Alias", "Alias", aliasHintEn, aliasHintDe); - - builder.AddOrUpdate("Admin.Catalog.Attributes.OptionsSets", "Option sets", "Options-Sets"); - - builder.Delete( - "Products.Filter.Remove", - "Products.Filter.ShowAll", - "Products.Filter.SelectMultiple", - "Products.Filter.NoneFound", - "Products.Filter.Contains", - "Products.Filter.StartsWith", - "Products.Filter.EndsWith", - "Filtering.FilterResults", - "Admin.Configuration.Settings.Catalog.FilterEnabled", - "Admin.Configuration.Settings.Catalog.FilterEnabled.Hint", - "Admin.Configuration.Settings.Catalog.MaxFilterItemsToDisplay", - "Admin.Configuration.Settings.Catalog.MaxFilterItemsToDisplay.Hint", - "Admin.Configuration.Settings.Catalog.ExpandAllFilterCriteria", - "Admin.Configuration.Settings.Catalog.ExpandAllFilterCriteria.Hint", - "Admin.Configuration.Settings.Catalog.SortFilterResultsByMatches", - "Admin.Configuration.Settings.Catalog.SortFilterResultsByMatches.Hint" - ); - - builder.AddOrUpdate("Search.Facet.Category", "Category", "Kategorie"); - builder.AddOrUpdate("Search.Facet.Manufacturer", "Brand", "Marke"); - builder.AddOrUpdate("Search.Facet.Price", "Price", "Preis"); - builder.AddOrUpdate("Search.Facet.Rating", "Rating", "Bewertung"); - builder.AddOrUpdate("Search.Facet.DeliveryTime", "Delivery Time", "Lieferzeit"); - builder.AddOrUpdate("Search.Facet.Availability", "Availability", "Verfügbarkeit"); - builder.AddOrUpdate("Search.Facet.NewArrivals", "New Arrivals", "Neuheiten"); - - builder.AddOrUpdate("Search.Facet.RangeMin", "from {0}", "ab {0}"); - builder.AddOrUpdate("Search.Facet.RangeMax", "up to {0}", "bis {0}"); - builder.AddOrUpdate("Search.Facet.RangeBetween", "{0} - {1}", "{0} - {1}"); - - builder.AddOrUpdate("Search.Facet.FindPlaceholder", "Find {0}...", "{0} suchen..."); - builder.AddOrUpdate("Search.Facet.SelectedCount", "{0} selected", "{0} ausgewählt"); - builder.AddOrUpdate("Search.Facet.RemoveAllFilters", "Remove all filters", "Alle Filter aufheben"); - builder.AddOrUpdate("Search.Facet.RemoveFilter", "Remove filter: {0} > {1}", "Filter aufheben: {0} > {1}"); - builder.AddOrUpdate("Search.Facet.RemoveGroupFilters", "Remove filters", "Filter aufheben"); - builder.AddOrUpdate("Search.Facet.1StarAndMore", "1 star & more", "1 Stern & mehr"); - builder.AddOrUpdate("Search.Facet.XStarsAndMore", "{0} stars & more", "{0} Sterne & mehr"); - builder.AddOrUpdate("Search.Facet.StarsAndMore", "& more", "& mehr"); - builder.AddOrUpdate("Search.Facet.LastDays", "Last {0} days", "Letzten {0} Tage"); - builder.AddOrUpdate("Search.Facet.IncludeOutOfStock", "Include Out of Stock", "Nicht verfügbare Artikel einschließen"); - - builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.ForceSslForAllPages", - "Always use SSL", - "Immer SSL verwenden", - "Specifies whether to SSL secure all request.", - "Legt fest, dass alle Anfragen SSL gesichert werden sollen."); - - builder.AddOrUpdate("Enums.SmartStore.Core.Search.Facets.FacetTemplateHint.Checkboxes", - "Checkboxes", - "Kontrollkästchen"); - builder.AddOrUpdate("Enums.SmartStore.Core.Search.Facets.FacetTemplateHint.Custom", - "Boxes (color & image)", - "Kästchen (Farbe & Bild)"); - builder.AddOrUpdate("Enums.SmartStore.Core.Search.Facets.FacetTemplateHint.NumericRange", - "Numeric range", - "Numerischer Bereich"); - - var megaSearchPlusHintEn = "This setting is only effective by using the 'MegaSearchPlus' plugin. Changes will take effect after next update of the search index."; - var megaSearchPlusHintDe = "Diese Einstellung ist nur unter Verwendung des 'MegaSearchPlus' Plugins wirksam. Änderungen werden nach der nächsten Aktualisierung des Suchindex wirksam."; - - builder.AddOrUpdate("Admin.Catalog.Attributes.SpecificationAttributes.Fields.FacetSorting", - "Sorting of search filters", - "Sortierung der Suchfilter", - "Specifies the sorting of the search filters. " + megaSearchPlusHintEn, - "Legt die Sortierung der Suchfilter fest. " + megaSearchPlusHintDe); - - builder.AddOrUpdate("Admin.Catalog.Attributes.SpecificationAttributes.Fields.FacetTemplateHint", - "Search filter presentation", - "Darstellung der Suchfilter", - "Specifies the presentation of search filters. " + megaSearchPlusHintEn, - "Legt die Darstellung der Suchfilter fest. " + megaSearchPlusHintDe); - - builder.AddOrUpdate("Admin.Catalog.Attributes.SpecificationAttributes.Fields.AllowFiltering", - "Allow filtering", - "Filtern ermöglichen", - "Specifies whether search results can be filtered by this attribute. " + megaSearchPlusHintEn, - "Legt fest, ob Suchergebnisse nach diesem Attribut gefiltert werden können. " + megaSearchPlusHintDe); - - builder.AddOrUpdate("Admin.Catalog.Products.SpecificationAttributes.Fields.AllowFiltering", - "Allow filtering", - "Filtern zulassen", - "Specifies whether search results can be filtered by this attribute. " + megaSearchPlusHintEn, - "Legt fest, ob Suchergebnisse nach diesem Attribut gefiltert werden können. " + megaSearchPlusHintDe); - - builder.AddOrUpdate("Admin.Catalog.Attributes.ProductAttributes.Fields.AllowFiltering", - "Allow filtering", - "Filtern zulassen", - "Specifies whether search results can be filtered by this attribute. " + megaSearchPlusHintEn, - "Legt fest, ob Suchergebnisse nach diesem Attribut gefiltert werden können. " + megaSearchPlusHintDe); - - builder.AddOrUpdate("Admin.Catalog.Attributes.ProductAttributes.Fields.FacetTemplateHint", - "Search filter UI type", - "Suchfilter Darstellung", - "Specifies the search filter UI type. " + megaSearchPlusHintEn, - "Legt die Darstellung der Suchfilter fest. " + megaSearchPlusHintDe); - - builder.AddOrUpdate("Admin.Catalog.Attributes.SpecificationAttributes.Options.Fields.NumberValue", - "Numeric value", - "Numerischer Wert", - "Specifies a numeric value to enbale range filtering (e.g. light red to dark red). \"Numeric range\" must be specified as search filter presentation for the attribute. " + megaSearchPlusHintEn, - "Legt einen numerischen Wert fest, um eine Bereichsfilterung zu ermöglichen (z.B. hellrot bis dunkelrot). Für das Attribut muss \"Numerischer Bereich\" als Suchfilterdarstellung festgelegt sein. " + megaSearchPlusHintDe); - - builder.AddOrUpdate("Admin.Catalog.Attributes.SpecificationAttributes.Fields.ShowOnProductPage", - "Show on product page", - "Auf der Produktseite anzeigen", - "Check the box to display the attribute on the product detail page.", - "Legt fest, ob das Attribut auf der Produktdetailseite angezeigt werden soll."); - - - - builder.AddOrUpdate("Account.Administration", "Admin", "Admin"); - builder.AddOrUpdate("Account.PasswordRecovery", "Reset password", "Passwort zurücksetzen"); - - - builder.AddOrUpdate("Common.Shopbar.BasketPartOne", "Shopping", "Waren"); - builder.AddOrUpdate("Common.Shopbar.BasketPartTwo", "Basket", "Korb"); - - builder.AddOrUpdate("Common.From", "From", "Von"); - builder.AddOrUpdate("Common.To", "To", "Bis"); - builder.AddOrUpdate("Common.Any", "Any", "Beliebig"); - - builder.Delete( - "Admin.Catalog.Attributes.SpecificationAttributes.Bundled.Description", - "Admin.Catalog.Attributes.SpecificationAttributes.Bundled.AllowFiltering", - "Admin.Catalog.Attributes.SpecificationAttributes.Bundled.DisallowFiltering", - "Admin.Catalog.Attributes.SpecificationAttributes.Bundled.ShowOnProductPage", - "Admin.Catalog.Attributes.SpecificationAttributes.Bundled.ShowNotOnProductPage"); - - builder.AddOrUpdate("Footer.Info", "Informations", "Informationen"); - - builder.AddOrUpdate("Admin.ContentManagement.Polls.Fields.SystemKeyword", - "System keyword", - "System-Schlüsselwort", - "The system keyword specifies the place in your shop where the poll will be displayed. Available system keywords are: MyAccountMenu, Blog", - "Das System-Schlüsselwort bestimmt den Platz im Shop, an welchem die Umfrage dargestellt wird. Verfügbare System-Schlüsselwörter sind: MyAccountMenu, Blog"); - - builder.Delete("Admin.Configuration.Settings.Catalog.ManufacturersBlockItemsToDisplay"); - - builder.AddOrUpdate("Homepage.TopBrands", "Top Brands", "Top-Marken"); - builder.AddOrUpdate("Homepage.Brands.ShowAll", "Show all", "Alle anzeigen"); - - builder.AddOrUpdate("Account.Fields.ZipPostalCode", "Zip code", "PLZ"); - builder.AddOrUpdate("Account.CustomerReturnRequests.Reason", "Return reason", "Rücksendegrund"); - builder.AddOrUpdate("Account.CustomerReturnRequests.Action", "Return action", "Rücksendeaktion"); - builder.AddOrUpdate("Account.CustomerReturnRequests.Date", "Date Requested", "Anfragedatum"); - builder.AddOrUpdate("Account.CustomerReturnRequests.Item", "Item", "Artikel"); - - builder.AddOrUpdate("Account.CreateAccount", "Create account", "Konto erstellen"); - builder.AddOrUpdate("Account.Register.Button", "Register", "Registrieren"); - - builder.AddOrUpdate("Account.Login.Welcome", "Sign In", "Anmeldung"); - builder.AddOrUpdate("Account.Login.ReturningCustomer", "I am already registered", "Ich bin bereits registriert"); - - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowManufacturersOnHomepage", - "Display manufacturers on homepage", - "Zeige Hersteller auf Startseite", - "Specifies whether manufacturers will be displayed on the homepage.", - "Legt fest, ob Hersteller auf der Startseite angezeigt werden."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowManufacturersInOffCanvas", - "Display manufacturers in offcanvas menu", - "Zeige Hersteller in OffCanvas-Menu", - "Specifies whether manufacturers will be displayed in offcanvas menu.", - "Legt fest, ob Hersteller im OffCanvas-Menu angezeigt werden."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ManufacturerItemsToDisplayOnHomepage", - "Amount of manufacturers to display on homepage", - "Anzahl der anzuzeigenden Hersteller auf der Homepage", - "Specifies the amount of manufacturers to display on homepage.", - "Bestimmt die Anzahl der anzuzeigenden Hersteller auf der Homepage."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowManufacturerPictures", - "Display manufacturer pictures in lists", - "Herstellerbilder in Listen anzeigen", - "Specifies whether manufacturer pictures will be displayed in lists. This setting effects all partial manufacturer lists in the shop (e.g. homepage, offcanvas menu).", - "Bestimmt ob Herstellerbilder in Listen angezeigt werden sollen. Diese Einstellung betrifft alle partiellen Herstellerlisten im Shop (z.B. Homepage, OffCanvas-Menu)."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ManufacturerItemsToDisplayInOffCanvasMenu", - "Amount of manufacturers to display in offcanvas menu", - "Anzahl der anzuzeigenden Hersteller im OffCanvas-Menu", - "Specifies the amount of manufacturers to display in offcanvas menu.", - "Bestimmt die Anzahl der anzuzeigenden Hersteller im OffCanvas-Menu."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.PageShareCode", - "Share button widget code", - "Share-Button Widget-Code", - @"Specifies the code to render the share button widget. By going to addthis.com you can create your own widget code and paste it here. - This way you can configure the display type of the widget as well as get statistic insight.", - @"Legt den Code des Share-Button Widgets fest. Gehen Sie zu addthis.com um Ihren eigenen Widget-Code zu erhalten und fügen Sie diesen hier ein. - Auf diese Weise können Sie die Darstellung des Widgets selbst bestimmen, sowie Statistiken einsehen."); - - builder.AddOrUpdate("Order.CannotCancel") - .Value("de", "Die Bestellung kann nicht storniert werden."); - builder.AddOrUpdate("Order.CannotCapture") - .Value("de", "Die Bestellung nicht gebucht werden."); - builder.AddOrUpdate("Order.CannotMarkCompleted") - .Value("de", "Die Bestellung kann nicht abgeschlossen werden."); - builder.AddOrUpdate("Order.CannotMarkPaid") - .Value("de", "Die Bestellung kann nicht als bezahlt markiert werden."); - builder.AddOrUpdate("Order.CannotPartialRefund") - .Value("de", "Eine Teilrückerstattung ist für diese Bestellung nicht möglich."); - builder.AddOrUpdate("Order.CannotRefund") - .Value("de", "Eine Rückerstattung ist für diese Bestellung nicht möglich."); - builder.AddOrUpdate("Order.CannotVoid") - .Value("de", "Eine Stornierung dieser Bestellung ist nicht möglich."); - builder.AddOrUpdate("Order.CompletePayment.Hint") - .Value("de", "Die Bestellung wurde noch nicht bezahlt. Um die Zahlung nun vorzunehmen, klicken Sie die Schaltfläche 'Zahlung veranlassen'"); - builder.AddOrUpdate("Order.getpdfinvoice") - .Value("de", "Bestellung als PDF"); - builder.AddOrUpdate("Order.NotFound") - .Value("de", "Die Bestellung {0} konnte nicht gefunden werden."); - builder.AddOrUpdate("Order.Order#") - .Value("de", "Bestellnr."); - builder.AddOrUpdate("Order.OrderDate") - .Value("de", "Bestellt am"); - builder.AddOrUpdate("Order.OrderDetails") - .Value("de", "Bestelldetails"); - builder.AddOrUpdate("Order.OrderStatus") - .Value("de", "Bestellstatus"); - builder.AddOrUpdate("Order.Shipments.Order#") - .Value("de", "Bestellnr."); - builder.AddOrUpdate("PDFInvoice.Order#") - .Value("de", "Bestellnr."); - builder.AddOrUpdate("PDFPackagingSlip.Order") - .Value("de", "Bestellung"); - builder.AddOrUpdate("Account.CustomerOrders") - .Value("de", "Bestellungen"); - builder.AddOrUpdate("Account.CustomerOrders.NoOrders") - .Value("de", "Keine Bestellungen"); - builder.AddOrUpdate("Account.CustomerOrders.NotYourOrder") - .Value("de", "Diese Bestellung konnte Ihnen nicht zugeordnet werden."); - builder.AddOrUpdate("Account.CustomerOrders.RecurringOrders.InitialOrder") - .Value("de", "Ursprüngliche Bestellung"); - builder.AddOrUpdate("Account.CustomerOrders.RecurringOrders.ViewInitialOrder") - .Value("de", "Bestellungsansicht (ID - {0})"); - - builder.AddOrUpdate("Order.Product(s).Item", "Item", "Artikel"); - builder.AddOrUpdate("Order.Product(s).Total", "Total", "Gesamt"); - builder.AddOrUpdate("Order.Product(s).SKU", "SKU", "Art.-Nr."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowPopularProductTagsOnHomepage", - "Show popular product tags on homepage", - "Zeige Produkt-Tags auf der Startseite", - "Specifies whether to show popular product tags on the homepage.", - "Bestimmt ob beliebte Produkt-Tags auf der Startseite angezeigt werden."); - - builder.AddOrUpdate("Search.IndexingRequiredNotification", - "This is the default search. For advanced search, indexing is required. Now start indexing or open configuration.", - "Hierbei handelt es sich um die Standardsuche. Für die erweiterte Suche ist eine Indexierung erforderlich. Indexierung jetzt starten oder Konfiguration aufrufen."); - - builder.Delete("ShoppingCart.UpdateCartItem", "ShoppingCart.UpdateCart"); - builder.AddOrUpdate("ShoppingCart.SKU", "SKU", "Art.-Nr."); - - builder.AddOrUpdate("Products.ProductsHaveBeenAddedToTheCart", - "The selected products have successfully been added to the cart.", - "Die von Ihnen gewählten Produkte wurden in den Warenkorb gelegt."); - - builder.AddOrUpdate("Forum.TopicSubject", - "Topic subject", - "Thema"); - - builder.AddOrUpdate("ShoppingCart.AlternativePaymentButtons", "- OR -", "- ODER -"); - - builder.AddOrUpdate("Admin.Configuration.Themes.Notifications.ConfigureError", - "SASS CSS Parser Error: Your changes were not saved because your configuration would lead to an error in the shop. For details see report.", - "SASS CSS Parser Fehler: Ihre Änderungen wurden nicht gespeichert, da Ihre Konfiguration zu einem Fehler im Shop führen würde. Details siehe Fehlerbericht."); - builder.AddOrUpdate("Admin.Configuration.Themes.Validation.ErrorReportTitle", - "SASS parser error report", - "SASS Parser Fehlerbericht"); - - builder.Delete("Enums.SmartStore.Core.Domain.Catalog.AttributeControlType.ColorSquares"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.AttributeControlType.Boxes", - "Boxes (color & image)", - "Kästchen (Farbe & Bild)"); - - builder.Delete( - "Admin.Themes.Grid", - "Admin.Themes.Shopbar", - "Admin.Themes.ContentSlider", - "Admin.Themes.Tables" - // "Admin.Themes.Footer" ??? - ); - - builder.AddOrUpdate("Admin.Themes.Colors", "Colors", "Farben"); - builder.AddOrUpdate("Admin.Themes.States", "Messages", "Meldungen"); - builder.AddOrUpdate("Admin.Themes.Components", "Components", "Komponenten"); - - builder.AddOrUpdate("Order.CompletePayment.AdminNote", - "The payment is pending. The buyer can make the payment by clicking on the Complete payment button on the order details page.", - "Die Zahlung ist ausstehend. Der Käufer kann die Zahlung durchführen, indem er auf der Bestelldetailseite den Button Zahlung veranlassen klickt."); - - builder.AddOrUpdate("Admin.ThemeVar.CostepPogressColor", - "Specifies the color of the checkout progress bar.", - "Legt die Farbe der Fortschrittsanzeige des Checkoutprozesses fest."); - - builder.AddOrUpdate("Admin.Theme.GoogleFonts.Hint", - @"

To embed Google Fonts please not the following advices.

-
    -
  • - Go to https://fonts.google.com/ and choose the font-families you want to use in your store. -
  • -
  • - The Html code that will be provided to you to embed the choosen fonts into your website looks like this: -
    <link href=""https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700|Ubuntu"" rel=""stylesheet"">
    -
  • -
  • - Copy the bold marked part of the link and enter it into one of the threee fields within the theme configurator that's intended for this purpose. -
  • -
  • - Now you can use the fonts by entering the name of a font-family (e.g. Roboto) into one of the input fields which are - provided to configure font-families (e.g. $font-family-sans-serif). -
  • -
", - @"

Um Google Fonts einzubinden, beachten Sie bitte die nachfolgende Anweisung.

-
    -
  • - Gehen Sie zu https://fonts.google.com/ und wählen Sie die Schriftarten, die Sie in Ihrem Shop verwenden möchten. -
  • -
  • - Als Html-Code für Ihre Webseite, wird Ihnen ein Link in folgender Form angeboten: -
    <link href=""https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700|Ubuntu"" rel=""stylesheet"">
    -
  • -
  • - Fügen Sie den fett markierten Teil des Links in eins der drei Felder des Theme-Konfigurators ein, die für Google Fonts vorgesehen sind. -
  • -
  • - Nun können Sie die Schriftart verwenden, indem Sie den Namen der Schriftart (z.B. Roboto) in den Eingabefeldern angeben, - die für Schriftarten vorgesehen sind (z.B. $font-family-sans-serif). -
  • -
"); - - builder.AddOrUpdate("PrivateMessages.NoInboxItems.Title", - "Your inbox is empty", - "Ihr Posteingang ist leer"); - builder.AddOrUpdate("PrivateMessages.NoInboxItems.Info", - "There are no items to be displayed in your email inbox.", - "Es befinden sich keine anzuzeigenden Nachrichten in Ihrem Posteingang."); - builder.AddOrUpdate("PrivateMessages.NoSentItems.Title", - "No sent items", - "Keine versendeten Nachrichten"); - builder.AddOrUpdate("PrivateMessages.NoSentItems.Info", - "There are no sent items to be displayed.", - "Sie haben noch keine Nachrichten versendet."); - - builder.AddOrUpdate("Admin.ContentManagement.Homepage", "Homepage", "Startseite"); - builder.AddOrUpdate("Products.SavingBadgeLabel", "- {0} %", "- {0} %"); - builder.AddOrUpdate("Admin.Themes.Sections", "Sections", "Sektionen"); - - builder.AddOrUpdate("Admin.ThemeVar.Boxed", - "Specifies whether the site will strech over the complete avaliable space.", - "Legt fest, ob sich die Seite über den kompletten verfügabren Platz streckt."); - builder.AddOrUpdate("Admin.ThemeVar.ArtActiveBgColor", - "Specifies the background color for product boxes when hovering over them.", - "Legt die Hintergrundfarbe von Produktboxen für den Hover-Effekt fest."); - builder.AddOrUpdate("Admin.ThemeVar.ArtActiveBorderColor", - "Specifies the border color for product boxes when hovering over them.", - "Legt die Rahmenfarbe von Produktboxen für den Hover-Effekt fest."); - builder.AddOrUpdate("Content.CopyrightNotice", - "Copyright © {0} {1}. All rights reserved.", - "Copyright © {0} {1}. Alle Rechte vorbehalten."); - - builder.AddOrUpdate("Common.CannotCopyFolderIntoItself", - "Cannot copy a folder into itself.", - "Ein Ordner kann nicht in sich selbst kopiert werden."); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201705102339006_V3Final.resx b/src/Libraries/SmartStore.Data/Migrations/201705102339006_V3Final.resx deleted file mode 100644 index 1394eaee45..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201705102339006_V3Final.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201705281903241_MoreIndexes.cs b/src/Libraries/SmartStore.Data/Migrations/201705281903241_MoreIndexes.cs index 1cd8f0c3dd..5f2db2e10f 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201705281903241_MoreIndexes.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201705281903241_MoreIndexes.cs @@ -1,37 +1,2512 @@ namespace SmartStore.Data.Migrations { - using System.Data.Entity.Migrations; - using System.Web.Hosting; - using Core.Data; - - public partial class MoreIndexes : DbMigration - { - public override void Up() - { - if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) - { - // 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.ProductAttribute", "AllowFiltering"); - CreateIndex("dbo.ProductVariantAttributeValue", "Name"); - CreateIndex("dbo.ProductVariantAttributeValue", "ValueTypeId"); - } - - 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.SpecificationAttribute", new[] { "AllowFiltering" }); - DropIndex("dbo.Product_Manufacturer_Mapping", new[] { "IsFeaturedProduct" }); - DropIndex("dbo.Product_Category_Mapping", new[] { "IsFeaturedProduct" }); - } - } + using System.Data.Entity.Migrations; + using System.Web.Hosting; + using Core.Data; + using SmartStore.Data.Setup; + + public partial class MoreIndexes : DbMigration + { + public override void Up() + { + if (!DbMigrationContext.Current.SuppressInitialCreate()) + { + UpInitial(); + } + + if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) + { + // 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.ProductAttribute", "AllowFiltering"); + CreateIndex("dbo.ProductVariantAttributeValue", "ValueTypeId"); + + // Useless index. The name field with 4000 characters is too long for an index. + if (DataSettings.Current.IsSqlServer) + { + CreateIndex("dbo.ProductVariantAttributeValue", "Name"); + } + } + + public override void Down() + { + if (DataSettings.Current.IsSqlServer) + { + DropIndex("dbo.ProductVariantAttributeValue", new[] { "Name" }); + } + + DropIndex("dbo.ProductVariantAttributeValue", new[] { "ValueTypeId" }); + DropIndex("dbo.ProductAttribute", new[] { "AllowFiltering" }); + DropIndex("dbo.Product_ProductAttribute_Mapping", new[] { "AttributeControlTypeId" }); + DropIndex("dbo.SpecificationAttribute", new[] { "AllowFiltering" }); + DropIndex("dbo.Product_Manufacturer_Mapping", new[] { "IsFeaturedProduct" }); + DropIndex("dbo.Product_Category_Mapping", new[] { "IsFeaturedProduct" }); + + DownInitial(); + } + + private void UpInitial() + { + #region auto-generated + + CreateTable( + "dbo.Topic", + c => new + { + Id = c.Int(nullable: false, identity: true), + SystemName = c.String(maxLength: 4000), + IncludeInSitemap = c.Boolean(nullable: false), + IsPasswordProtected = c.Boolean(nullable: false), + Password = c.String(maxLength: 4000), + Title = c.String(maxLength: 4000), + Body = c.String(), + MetaKeywords = c.String(maxLength: 4000), + MetaDescription = c.String(maxLength: 4000), + MetaTitle = c.String(maxLength: 4000), + LimitedToStores = c.Boolean(nullable: false), + RenderAsWidget = c.Boolean(nullable: false), + WidgetZone = c.String(maxLength: 4000), + WidgetWrapContent = c.Boolean(), + WidgetShowTitle = c.Boolean(nullable: false), + WidgetBordered = c.Boolean(nullable: false), + Priority = c.Int(nullable: false), + TitleTag = c.String(), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.TaxCategory", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 400), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.Setting", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 200), + Value = c.String(nullable: false), + StoreId = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.LocalizedProperty", + c => new + { + Id = c.Int(nullable: false, identity: true), + EntityId = c.Int(nullable: false), + LanguageId = c.Int(nullable: false), + LocaleKeyGroup = c.String(nullable: false, maxLength: 400), + LocaleKey = c.String(nullable: false, maxLength: 400), + LocaleValue = c.String(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Language", t => t.LanguageId, cascadeDelete: true) + .Index(t => new { t.EntityId, t.LocaleKey, t.LocaleKeyGroup, t.LanguageId }, name: "IX_LocalizedProperty_Compound"); + + CreateTable( + "dbo.Language", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 100), + LanguageCulture = c.String(nullable: false, maxLength: 20), + UniqueSeoCode = c.String(maxLength: 2), + FlagImageFileName = c.String(maxLength: 50), + Rtl = c.Boolean(nullable: false), + LimitedToStores = c.Boolean(nullable: false), + Published = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.LocaleStringResource", + c => new + { + Id = c.Int(nullable: false, identity: true), + LanguageId = c.Int(nullable: false), + ResourceName = c.String(nullable: false, maxLength: 200), + ResourceValue = c.String(nullable: false), + IsFromPlugin = c.Boolean(), + IsTouched = c.Boolean(), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Language", t => t.LanguageId, cascadeDelete: true) + .Index(t => t.LanguageId); + + CreateTable( + "dbo.PermissionRecord", + c => new + { + Id = c.Int(nullable: false, identity: true), + //Name = c.String(nullable: false), + SystemName = c.String(nullable: false, maxLength: 255), + //Category = c.String(nullable: false, maxLength: 255), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.CustomerRole", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 255), + FreeShipping = c.Boolean(nullable: false), + TaxExempt = c.Boolean(nullable: false), + TaxDisplayType = c.Int(), + Active = c.Boolean(nullable: false), + IsSystemRole = c.Boolean(nullable: false), + SystemName = c.String(maxLength: 255), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.Poll", + c => new + { + Id = c.Int(nullable: false, identity: true), + LanguageId = c.Int(nullable: false), + Name = c.String(nullable: false, maxLength: 4000), + SystemKeyword = c.String(maxLength: 4000), + Published = c.Boolean(nullable: false), + ShowOnHomePage = c.Boolean(nullable: false), + AllowGuestsToVote = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + StartDateUtc = c.DateTime(), + EndDateUtc = c.DateTime(), + LimitedToStores = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Language", t => t.LanguageId, cascadeDelete: true) + .Index(t => t.LanguageId); + + CreateTable( + "dbo.PollAnswer", + c => new + { + Id = c.Int(nullable: false, identity: true), + PollId = c.Int(nullable: false), + Name = c.String(nullable: false, maxLength: 4000), + NumberOfVotes = c.Int(nullable: false), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Poll", t => t.PollId, cascadeDelete: true) + .Index(t => t.PollId); + + CreateTable( + "dbo.CustomerContent", + c => new + { + Id = c.Int(nullable: false, identity: true), + CustomerId = c.Int(nullable: false), + IpAddress = c.String(maxLength: 200), + IsApproved = c.Boolean(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) + .Index(t => t.CustomerId); + + CreateTable( + "dbo.Customer", + c => new + { + Id = c.Int(nullable: false, identity: true), + CustomerGuid = c.Guid(nullable: false), + Username = c.String(maxLength: 500), + Email = c.String(maxLength: 500), + Password = c.String(maxLength: 500), + PasswordFormatId = c.Int(nullable: false), + PasswordSalt = c.String(maxLength: 500), + AdminComment = c.String(maxLength: 4000), + 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: 500), + LastIpAddress = c.String(maxLength: 100), + CreatedOnUtc = c.DateTime(nullable: false), + LastLoginDateUtc = c.DateTime(), + LastActivityDateUtc = c.DateTime(nullable: false), + BillingAddress_Id = c.Int(), + ShippingAddress_Id = c.Int(), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Address", t => t.BillingAddress_Id) + .ForeignKey("dbo.Address", t => t.ShippingAddress_Id) + .Index(t => t.Deleted) + .Index(t => t.SystemName) + .Index(t => t.LastIpAddress, name: "IX_Customer_LastIpAddress") + .Index(t => t.CreatedOnUtc, name: "IX_Customer_CreatedOn") + .Index(t => t.LastActivityDateUtc, name: "IX_Customer_LastActivity") + .Index(t => t.BillingAddress_Id) + .Index(t => t.ShippingAddress_Id); + + CreateTable( + "dbo.Address", + c => new + { + Id = c.Int(nullable: false, identity: true), + Salutation = c.String(), + Title = c.String(), + FirstName = c.String(maxLength: 4000), + LastName = c.String(maxLength: 4000), + Email = c.String(maxLength: 4000), + Company = c.String(maxLength: 4000), + 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), + CreatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Country", t => t.CountryId) + .ForeignKey("dbo.StateProvince", t => t.StateProvinceId) + .Index(t => t.CountryId) + .Index(t => t.StateProvinceId); + + CreateTable( + "dbo.Country", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 100), + AllowsBilling = c.Boolean(nullable: false), + AllowsShipping = c.Boolean(nullable: false), + TwoLetterIsoCode = c.String(maxLength: 2), + ThreeLetterIsoCode = c.String(maxLength: 3), + NumericIsoCode = c.Int(nullable: false), + SubjectToVat = c.Boolean(nullable: false), + Published = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + LimitedToStores = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.ShippingMethod", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 400), + Description = c.String(maxLength: 4000), + DisplayOrder = c.Int(nullable: false), + IgnoreCharges = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.StateProvince", + c => new + { + Id = c.Int(nullable: false, identity: true), + CountryId = c.Int(nullable: false), + Name = c.String(nullable: false, maxLength: 100), + Abbreviation = c.String(maxLength: 100), + Published = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Country", t => t.CountryId, cascadeDelete: true) + .Index(t => t.CountryId); + + CreateTable( + "dbo.BlogPost", + c => new + { + Id = c.Int(nullable: false, identity: true), + LanguageId = c.Int(nullable: false), + Title = c.String(nullable: false, maxLength: 4000), + Body = c.String(nullable: false), + AllowComments = c.Boolean(nullable: false), + ApprovedCommentCount = c.Int(nullable: false), + NotApprovedCommentCount = c.Int(nullable: false), + Tags = c.String(maxLength: 4000), + StartDateUtc = c.DateTime(), + EndDateUtc = c.DateTime(), + MetaKeywords = c.String(maxLength: 400), + MetaDescription = c.String(maxLength: 4000), + MetaTitle = c.String(maxLength: 400), + LimitedToStores = c.Boolean(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Language", t => t.LanguageId, cascadeDelete: true) + .Index(t => t.LanguageId); + + CreateTable( + "dbo.Product", + c => new + { + Id = c.Int(nullable: false, identity: true), + ProductTypeId = c.Int(nullable: false), + ParentGroupedProductId = c.Int(nullable: false), + VisibleIndividually = c.Boolean(nullable: false), + Name = c.String(nullable: false, maxLength: 400), + ShortDescription = c.String(maxLength: 4000), + FullDescription = c.String(), + AdminComment = c.String(maxLength: 4000), + ProductTemplateId = c.Int(nullable: false), + ShowOnHomePage = c.Boolean(nullable: false), + HomePageDisplayOrder = c.Int(nullable: false), + MetaKeywords = c.String(maxLength: 400), + MetaDescription = c.String(maxLength: 4000), + MetaTitle = c.String(maxLength: 400), + AllowCustomerReviews = c.Boolean(nullable: false), + ApprovedRatingSum = c.Int(nullable: false), + NotApprovedRatingSum = c.Int(nullable: false), + ApprovedTotalReviews = c.Int(nullable: false), + NotApprovedTotalReviews = c.Int(nullable: false), + SubjectToAcl = c.Boolean(nullable: false), + LimitedToStores = c.Boolean(nullable: false), + Sku = c.String(maxLength: 400), + ManufacturerPartNumber = c.String(maxLength: 400), + Gtin = c.String(maxLength: 400), + IsGiftCard = c.Boolean(nullable: false), + GiftCardTypeId = c.Int(nullable: false), + RequireOtherProducts = c.Boolean(nullable: false), + RequiredProductIds = c.String(maxLength: 1000), + AutomaticallyAddRequiredProducts = c.Boolean(nullable: false), + IsDownload = c.Boolean(nullable: false), + DownloadId = c.Int(nullable: false), + UnlimitedDownloads = c.Boolean(nullable: false), + MaxNumberOfDownloads = c.Int(nullable: false), + DownloadExpirationDays = c.Int(), + DownloadActivationTypeId = c.Int(nullable: false), + HasSampleDownload = c.Boolean(nullable: false), + SampleDownloadId = c.Int(), + HasUserAgreement = c.Boolean(nullable: false), + UserAgreementText = c.String(maxLength: 4000), + IsRecurring = c.Boolean(nullable: false), + RecurringCycleLength = c.Int(nullable: false), + RecurringCyclePeriodId = c.Int(nullable: false), + RecurringTotalCycles = c.Int(nullable: false), + IsShipEnabled = c.Boolean(nullable: false), + IsFreeShipping = c.Boolean(nullable: false), + AdditionalShippingCharge = c.Decimal(nullable: false, precision: 18, scale: 4), + IsTaxExempt = c.Boolean(nullable: false), + IsEsd = c.Boolean(nullable: false), + TaxCategoryId = c.Int(nullable: false), + ManageInventoryMethodId = c.Int(nullable: false), + StockQuantity = c.Int(nullable: false), + DisplayStockAvailability = c.Boolean(nullable: false), + DisplayStockQuantity = c.Boolean(nullable: false), + MinStockQuantity = c.Int(nullable: false), + LowStockActivityId = c.Int(nullable: false), + NotifyAdminForQuantityBelow = c.Int(nullable: false), + BackorderModeId = c.Int(nullable: false), + AllowBackInStockSubscriptions = c.Boolean(nullable: false), + OrderMinimumQuantity = c.Int(nullable: false), + OrderMaximumQuantity = c.Int(nullable: false), + QuantityStep = c.Int(nullable: false), + QuantiyControlType = c.Int(nullable: false), + HideQuantityControl = c.Boolean(nullable: false), + AllowedQuantities = c.String(maxLength: 1000), + DisableBuyButton = c.Boolean(nullable: false), + DisableWishlistButton = c.Boolean(nullable: false), + AvailableForPreOrder = c.Boolean(nullable: false), + CallForPrice = c.Boolean(nullable: false), + Price = c.Decimal(nullable: false, precision: 18, scale: 4), + OldPrice = c.Decimal(nullable: false, precision: 18, scale: 4), + ProductCost = c.Decimal(nullable: false, precision: 18, scale: 4), + SpecialPrice = c.Decimal(precision: 18, scale: 4), + SpecialPriceStartDateTimeUtc = c.DateTime(), + SpecialPriceEndDateTimeUtc = c.DateTime(), + CustomerEntersPrice = c.Boolean(nullable: false), + MinimumCustomerEnteredPrice = c.Decimal(nullable: false, precision: 18, scale: 4), + MaximumCustomerEnteredPrice = c.Decimal(nullable: false, precision: 18, scale: 4), + HasTierPrices = c.Boolean(nullable: false), + LowestAttributeCombinationPrice = c.Decimal(precision: 18, scale: 4), + HasDiscountsApplied = c.Boolean(nullable: false), + Weight = c.Decimal(nullable: false, precision: 18, scale: 4), + Length = c.Decimal(nullable: false, precision: 18, scale: 4), + Width = c.Decimal(nullable: false, precision: 18, scale: 4), + Height = c.Decimal(nullable: false, precision: 18, scale: 4), + AvailableStartDateTimeUtc = c.DateTime(), + AvailableEndDateTimeUtc = c.DateTime(), + DisplayOrder = c.Int(nullable: false), + Published = c.Boolean(nullable: false), + Deleted = c.Boolean(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + DeliveryTimeId = c.Int(), + QuantityUnitId = c.Int(), + CustomsTariffNumber = c.String(maxLength: 30), + CountryOfOriginId = c.Int(), + BasePriceEnabled = c.Boolean(nullable: false), + BasePriceMeasureUnit = c.String(maxLength: 50), + BasePriceAmount = c.Decimal(precision: 18, scale: 4), + BasePriceBaseAmount = c.Int(), + BundleTitleText = c.String(maxLength: 400), + BundlePerItemShipping = c.Boolean(nullable: false), + BundlePerItemPricing = c.Boolean(nullable: false), + BundlePerItemShoppingCart = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Country", t => t.CountryOfOriginId) + .ForeignKey("dbo.DeliveryTime", t => t.DeliveryTimeId) + .ForeignKey("dbo.QuantityUnit", t => t.QuantityUnitId) + .ForeignKey("dbo.Download", t => t.SampleDownloadId) + .Index(t => t.ManufacturerPartNumber) + .Index(t => t.Gtin) + .Index(t => t.SampleDownloadId) + .Index(t => t.Deleted) + .Index(t => t.DeliveryTimeId) + .Index(t => t.QuantityUnitId) + .Index(t => t.CountryOfOriginId); + + CreateTable( + "dbo.Discount", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 200), + DiscountTypeId = c.Int(nullable: false), + UsePercentage = c.Boolean(nullable: false), + DiscountPercentage = c.Decimal(nullable: false, precision: 18, scale: 4), + DiscountAmount = c.Decimal(nullable: false, precision: 18, scale: 4), + StartDateUtc = c.DateTime(), + EndDateUtc = c.DateTime(), + RequiresCouponCode = c.Boolean(nullable: false), + CouponCode = c.String(maxLength: 100), + DiscountLimitationId = c.Int(nullable: false), + LimitationTimes = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.Category", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 400), + FullName = c.String(maxLength: 400), + Description = c.String(), + BottomDescription = c.String(), + BadgeText = c.String(maxLength: 400), + BadgeStyle = c.Int(nullable: false), + Alias = c.String(maxLength: 100), + CategoryTemplateId = c.Int(nullable: false), + MetaKeywords = c.String(maxLength: 400), + MetaDescription = c.String(maxLength: 4000), + MetaTitle = c.String(maxLength: 400), + ParentCategoryId = c.Int(nullable: false), + PictureId = c.Int(), + PageSize = c.Int(), + AllowCustomersToSelectPageSize = c.Boolean(), + PageSizeOptions = c.String(maxLength: 200), + PriceRanges = c.String(maxLength: 400), + ShowOnHomePage = c.Boolean(nullable: false), + HasDiscountsApplied = c.Boolean(nullable: false), + SubjectToAcl = c.Boolean(nullable: false), + LimitedToStores = c.Boolean(nullable: false), + Published = c.Boolean(nullable: false), + Deleted = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + DefaultViewMode = c.String(), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Picture", t => t.PictureId) + .Index(t => t.PictureId) + .Index(t => t.Deleted); + + CreateTable( + "dbo.Picture", + c => new + { + Id = c.Int(nullable: false, identity: true), + PictureBinary = c.Binary(), + MimeType = c.String(nullable: false, maxLength: 40), + Width = c.Int(), + Height = c.Int(), + SeoFilename = c.String(maxLength: 300), + IsNew = c.Boolean(nullable: false), + IsTransient = c.Boolean(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + MediaStorageId = c.Int(), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.MediaStorage", t => t.MediaStorageId) + .Index(t => new { t.UpdatedOnUtc, t.IsTransient }, name: "IX_UpdatedOn_IsTransient") + .Index(t => t.MediaStorageId); + + CreateTable( + "dbo.MediaStorage", + c => new + { + Id = c.Int(nullable: false, identity: true), + Data = c.Binary(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.Product_Picture_Mapping", + c => new + { + Id = c.Int(nullable: false, identity: true), + ProductId = c.Int(nullable: false), + PictureId = c.Int(nullable: false), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Picture", t => t.PictureId, cascadeDelete: true) + .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) + .Index(t => t.ProductId) + .Index(t => t.PictureId); + + CreateTable( + "dbo.Manufacturer", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 400), + Description = c.String(), + ManufacturerTemplateId = c.Int(nullable: false), + MetaKeywords = c.String(maxLength: 400), + MetaDescription = c.String(maxLength: 4000), + MetaTitle = c.String(maxLength: 400), + PictureId = c.Int(), + PageSize = c.Int(), + AllowCustomersToSelectPageSize = c.Boolean(), + PageSizeOptions = c.String(maxLength: 200), + PriceRanges = c.String(maxLength: 400), + LimitedToStores = c.Boolean(nullable: false), + Published = c.Boolean(nullable: false), + Deleted = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + HasDiscountsApplied = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Picture", t => t.PictureId) + .Index(t => t.PictureId) + .Index(t => t.Deleted); + + // Obsolete. Is now implemented by rule builder. + //CreateTable( + // "dbo.DiscountRequirement", + // c => new + // { + // Id = c.Int(nullable: false, identity: true), + // DiscountId = c.Int(nullable: false), + // DiscountRequirementRuleSystemName = c.String(), + // SpentAmount = c.Decimal(nullable: false, precision: 18, scale: 4), + // BillingCountryId = c.Int(nullable: false), + // ShippingCountryId = c.Int(nullable: false), + // RestrictedToCustomerRoleId = c.Int(), + // RestrictedProductIds = c.String(), + // RestrictedPaymentMethods = c.String(), + // RestrictedShippingOptions = c.String(), + // RestrictedToStoreId = c.Int(), + // ExtraData = c.String(), + // }) + // .PrimaryKey(t => t.Id) + // .ForeignKey("dbo.Discount", t => t.DiscountId, cascadeDelete: true) + // .Index(t => t.DiscountId); + + CreateTable( + "dbo.DeliveryTime", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 50), + ColorHexValue = c.String(nullable: false, maxLength: 50), + DisplayLocale = c.String(maxLength: 50), + DisplayOrder = c.Int(nullable: false), + IsDefault = c.Boolean(), + }) + .PrimaryKey(t => t.Id); + + 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), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Product", t => t.BundleProductId, cascadeDelete: true) + .ForeignKey("dbo.Product", t => t.ProductId) + .Index(t => t.ProductId) + .Index(t => t.BundleProductId); + + CreateTable( + "dbo.ProductBundleItemAttributeFilter", + c => new + { + Id = c.Int(nullable: false, identity: true), + BundleItemId = c.Int(nullable: false), + AttributeId = c.Int(nullable: false), + AttributeValueId = c.Int(nullable: false), + IsPreSelected = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.ProductBundleItem", t => t.BundleItemId, cascadeDelete: true) + .Index(t => t.BundleItemId); + + CreateTable( + "dbo.Product_Category_Mapping", + c => new + { + Id = c.Int(nullable: false, identity: true), + ProductId = c.Int(nullable: false), + CategoryId = c.Int(nullable: false), + IsFeaturedProduct = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Category", t => t.CategoryId, cascadeDelete: true) + .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) + .Index(t => t.ProductId) + .Index(t => t.CategoryId); + + CreateTable( + "dbo.Product_Manufacturer_Mapping", + c => new + { + Id = c.Int(nullable: false, identity: true), + ProductId = c.Int(nullable: false), + ManufacturerId = c.Int(nullable: false), + IsFeaturedProduct = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Manufacturer", t => t.ManufacturerId, cascadeDelete: true) + .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) + .Index(t => t.ProductId) + .Index(t => t.ManufacturerId); + + CreateTable( + "dbo.Product_SpecificationAttribute_Mapping", + c => new + { + Id = c.Int(nullable: false, identity: true), + ProductId = c.Int(nullable: false), + SpecificationAttributeOptionId = c.Int(nullable: false), + AllowFiltering = c.Boolean(), + ShowOnProductPage = c.Boolean(), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) + .ForeignKey("dbo.SpecificationAttributeOption", t => t.SpecificationAttributeOptionId, cascadeDelete: true) + .Index(t => t.ProductId) + .Index(t => t.SpecificationAttributeOptionId); + + CreateTable( + "dbo.SpecificationAttributeOption", + c => new + { + Id = c.Int(nullable: false, identity: true), + SpecificationAttributeId = c.Int(nullable: false), + Name = c.String(nullable: false, maxLength: 4000), + Alias = c.String(maxLength: 30), + DisplayOrder = c.Int(nullable: false), + NumberValue = c.Decimal(nullable: false, precision: 18, scale: 4), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.SpecificationAttribute", t => t.SpecificationAttributeId, cascadeDelete: true) + .Index(t => t.SpecificationAttributeId); + + CreateTable( + "dbo.SpecificationAttribute", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 4000), + Alias = c.String(maxLength: 30), + DisplayOrder = c.Int(nullable: false), + ShowOnProductPage = c.Boolean(nullable: false), + AllowFiltering = c.Boolean(nullable: false), + FacetSorting = c.Int(nullable: false), + FacetTemplateHint = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.ProductTag", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 400), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.ProductVariantAttributeCombination", + c => new + { + Id = c.Int(nullable: false, identity: true), + Sku = c.String(maxLength: 400), + Gtin = c.String(maxLength: 400), + ManufacturerPartNumber = c.String(maxLength: 400), + Price = c.Decimal(precision: 18, scale: 4), + Length = c.Decimal(precision: 18, scale: 4), + Width = c.Decimal(precision: 18, scale: 4), + Height = c.Decimal(precision: 18, scale: 4), + BasePriceAmount = c.Decimal(precision: 18, scale: 4), + BasePriceBaseAmount = c.Int(), + AssignedPictureIds = c.String(maxLength: 1000), + DeliveryTimeId = c.Int(), + QuantityUnitId = c.Int(), + IsActive = c.Boolean(nullable: false), + ProductId = c.Int(nullable: false), + AttributesXml = c.String(), + StockQuantity = c.Int(nullable: false), + AllowOutOfStockOrders = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.DeliveryTime", t => t.DeliveryTimeId) + .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) + .ForeignKey("dbo.QuantityUnit", t => t.QuantityUnitId) + .Index(t => t.DeliveryTimeId) + .Index(t => t.QuantityUnitId) + .Index(t => t.ProductId); + + CreateTable( + "dbo.QuantityUnit", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 50), + Description = c.String(maxLength: 50), + DisplayLocale = c.String(maxLength: 50), + DisplayOrder = c.Int(nullable: false), + IsDefault = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.Product_ProductAttribute_Mapping", + c => new + { + Id = c.Int(nullable: false, identity: true), + ProductId = c.Int(nullable: false), + ProductAttributeId = c.Int(nullable: false), + TextPrompt = c.String(maxLength: 4000), + IsRequired = c.Boolean(nullable: false), + AttributeControlTypeId = c.Int(nullable: false), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) + .ForeignKey("dbo.ProductAttribute", t => t.ProductAttributeId, cascadeDelete: true) + .Index(t => new { t.ProductId, t.DisplayOrder }, name: "IX_Product_ProductAttribute_Mapping_ProductId_DisplayOrder") + .Index(t => t.ProductAttributeId); + + CreateTable( + "dbo.ProductAttribute", + c => new + { + Id = c.Int(nullable: false, identity: true), + Alias = c.String(maxLength: 100), + Name = c.String(nullable: false, maxLength: 4000), + Description = c.String(maxLength: 4000), + AllowFiltering = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + FacetTemplateHint = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.ProductAttributeOptionsSet", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(maxLength: 400), + ProductAttributeId = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.ProductAttribute", t => t.ProductAttributeId, cascadeDelete: true) + .Index(t => t.ProductAttributeId); + + CreateTable( + "dbo.ProductAttributeOption", + c => new + { + Id = c.Int(nullable: false, identity: true), + ProductAttributeOptionsSetId = c.Int(nullable: false), + Alias = c.String(maxLength: 100), + Name = c.String(maxLength: 4000), + PictureId = c.Int(nullable: false), + Color = c.String(maxLength: 100), + PriceAdjustment = c.Decimal(nullable: false, precision: 18, scale: 4), + WeightAdjustment = c.Decimal(nullable: false, precision: 18, scale: 4), + IsPreSelected = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + ValueTypeId = c.Int(nullable: false), + LinkedProductId = c.Int(nullable: false), + Quantity = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.ProductAttributeOptionsSet", t => t.ProductAttributeOptionsSetId, cascadeDelete: true) + .Index(t => t.ProductAttributeOptionsSetId); + + CreateTable( + "dbo.ProductVariantAttributeValue", + c => new + { + Id = c.Int(nullable: false, identity: true), + ProductVariantAttributeId = c.Int(nullable: false), + Alias = c.String(maxLength: 100), + Name = c.String(maxLength: 4000), + PictureId = c.Int(nullable: false), + Color = c.String(maxLength: 100), + PriceAdjustment = c.Decimal(nullable: false, precision: 18, scale: 4), + WeightAdjustment = c.Decimal(nullable: false, precision: 18, scale: 4), + IsPreSelected = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + ValueTypeId = c.Int(nullable: false), + LinkedProductId = c.Int(nullable: false), + Quantity = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Product_ProductAttribute_Mapping", t => t.ProductVariantAttributeId, cascadeDelete: true) + .Index(t => new { t.ProductVariantAttributeId, t.DisplayOrder }, name: "IX_ProductVariantAttributeValue_ProductVariantAttributeId_DisplayOrder"); + + CreateTable( + "dbo.Download", + c => new + { + Id = c.Int(nullable: false, identity: true), + DownloadGuid = c.Guid(nullable: false), + UseDownloadUrl = c.Boolean(nullable: false), + DownloadUrl = c.String(maxLength: 4000), + DownloadBinary = c.Binary(), + ContentType = c.String(maxLength: 4000), + Filename = c.String(maxLength: 4000), + Extension = c.String(maxLength: 4000), + IsNew = c.Boolean(nullable: false), + IsTransient = c.Boolean(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + MediaStorageId = c.Int(), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.MediaStorage", t => t.MediaStorageId) + .Index(t => t.DownloadGuid) + .Index(t => new { t.UpdatedOnUtc, t.IsTransient }, name: "IX_UpdatedOn_IsTransient") + .Index(t => t.MediaStorageId); + + CreateTable( + "dbo.TierPrice", + c => new + { + Id = c.Int(nullable: false, identity: true), + ProductId = c.Int(nullable: false), + StoreId = c.Int(nullable: false), + CustomerRoleId = c.Int(), + Quantity = c.Int(nullable: false), + Price = c.Decimal(nullable: false, precision: 18, scale: 4), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.CustomerRole", t => t.CustomerRoleId, cascadeDelete: true) + .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) + .Index(t => t.ProductId) + .Index(t => t.CustomerRoleId); + + CreateTable( + "dbo.News", + c => new + { + Id = c.Int(nullable: false, identity: true), + LanguageId = c.Int(nullable: false), + Title = c.String(nullable: false, maxLength: 4000), + Short = c.String(nullable: false, maxLength: 4000), + Full = c.String(nullable: false), + Published = c.Boolean(nullable: false), + StartDateUtc = c.DateTime(), + EndDateUtc = c.DateTime(), + AllowComments = c.Boolean(nullable: false), + ApprovedCommentCount = c.Int(nullable: false), + NotApprovedCommentCount = c.Int(nullable: false), + LimitedToStores = c.Boolean(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + MetaKeywords = c.String(maxLength: 400), + MetaDescription = c.String(maxLength: 4000), + MetaTitle = c.String(maxLength: 400), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Language", t => t.LanguageId, cascadeDelete: true) + .Index(t => t.LanguageId); + + CreateTable( + "dbo.ExternalAuthenticationRecord", + c => new + { + Id = c.Int(nullable: false, identity: true), + CustomerId = c.Int(nullable: false), + Email = c.String(maxLength: 4000), + ExternalIdentifier = c.String(maxLength: 4000), + ExternalDisplayIdentifier = c.String(maxLength: 4000), + OAuthToken = c.String(maxLength: 4000), + OAuthAccessToken = c.String(maxLength: 4000), + ProviderSystemName = c.String(maxLength: 4000), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) + .Index(t => t.CustomerId); + + CreateTable( + "dbo.Forums_Post", + c => new + { + Id = c.Int(nullable: false, identity: true), + TopicId = c.Int(nullable: false), + CustomerId = c.Int(nullable: false), + Text = c.String(nullable: false), + IPAddress = c.String(maxLength: 100), + CreatedOnUtc = c.DateTime(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Customer", t => t.CustomerId) + .ForeignKey("dbo.Forums_Topic", t => t.TopicId, cascadeDelete: true) + .Index(t => t.TopicId) + .Index(t => t.CustomerId); + + CreateTable( + "dbo.Forums_Topic", + c => new + { + Id = c.Int(nullable: false, identity: true), + ForumId = c.Int(nullable: false), + CustomerId = c.Int(nullable: false), + TopicTypeId = c.Int(nullable: false), + Subject = c.String(nullable: false, maxLength: 450), + NumPosts = c.Int(nullable: false), + Views = c.Int(nullable: false), + LastPostId = c.Int(nullable: false), + LastPostCustomerId = c.Int(nullable: false), + LastPostTime = c.DateTime(), + CreatedOnUtc = c.DateTime(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Customer", t => t.CustomerId) + .ForeignKey("dbo.Forums_Forum", t => t.ForumId, cascadeDelete: true) + .Index(t => t.ForumId) + .Index(t => t.CustomerId); + + CreateTable( + "dbo.Forums_Forum", + c => new + { + Id = c.Int(nullable: false, identity: true), + ForumGroupId = c.Int(nullable: false), + Name = c.String(nullable: false, maxLength: 200), + Description = c.String(), + NumTopics = c.Int(nullable: false), + NumPosts = c.Int(nullable: false), + LastTopicId = c.Int(nullable: false), + LastPostId = c.Int(nullable: false), + LastPostCustomerId = c.Int(nullable: false), + LastPostTime = c.DateTime(), + DisplayOrder = c.Int(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Forums_Group", t => t.ForumGroupId, cascadeDelete: true) + .Index(t => t.ForumGroupId); + + CreateTable( + "dbo.Forums_Group", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 200), + Description = c.String(), + DisplayOrder = c.Int(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + LimitedToStores = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "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), + OrderShippingTaxRate = 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), + PaymentMethodAdditionalFeeTaxRate = 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(), + CheckoutAttributesXml = c.String(), + 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), + CustomerOrderComment = c.String(), + 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), + UpdatedOnUtc = c.DateTime(nullable: false), + RewardPointsRemaining = c.Int(), + HasNewPaymentNotification = c.Boolean(nullable: false), + AcceptThirdPartyEmailHandOver = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Address", t => t.BillingAddressId) + .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) + .ForeignKey("dbo.Address", t => t.ShippingAddressId) + .Index(t => t.CustomerId) + .Index(t => t.BillingAddressId) + .Index(t => t.ShippingAddressId) + .Index(t => t.Deleted); + + CreateTable( + "dbo.DiscountUsageHistory", + c => new + { + Id = c.Int(nullable: false, identity: true), + DiscountId = c.Int(nullable: false), + OrderId = c.Int(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Discount", t => t.DiscountId, cascadeDelete: true) + .ForeignKey("dbo.Order", t => t.OrderId, cascadeDelete: true) + .Index(t => t.DiscountId) + .Index(t => t.OrderId); + + CreateTable( + "dbo.GiftCardUsageHistory", + c => new + { + Id = c.Int(nullable: false, identity: true), + GiftCardId = c.Int(nullable: false), + UsedWithOrderId = c.Int(nullable: false), + UsedValue = c.Decimal(nullable: false, precision: 18, scale: 4), + CreatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.GiftCard", t => t.GiftCardId, cascadeDelete: true) + .ForeignKey("dbo.Order", t => t.UsedWithOrderId, cascadeDelete: true) + .Index(t => t.GiftCardId) + .Index(t => t.UsedWithOrderId); + + CreateTable( + "dbo.GiftCard", + c => new + { + Id = c.Int(nullable: false, identity: true), + PurchasedWithOrderItemId = c.Int(), + 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), + IsRecipientNotified = c.Boolean(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.OrderItem", t => t.PurchasedWithOrderItemId) + .Index(t => t.PurchasedWithOrderItemId); + + CreateTable( + "dbo.OrderItem", + c => new + { + Id = c.Int(nullable: false, identity: true), + OrderItemGuid = c.Guid(nullable: false), + OrderId = c.Int(nullable: false), + ProductId = c.Int(nullable: false), + Quantity = c.Int(nullable: false), + UnitPriceInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + UnitPriceExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + PriceInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + PriceExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + TaxRate = c.Decimal(nullable: false, precision: 18, scale: 4), + DiscountAmountInclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + DiscountAmountExclTax = c.Decimal(nullable: false, precision: 18, scale: 4), + AttributeDescription = c.String(), + AttributesXml = c.String(), + DownloadCount = c.Int(nullable: false), + IsDownloadActivated = c.Boolean(nullable: false), + LicenseDownloadId = c.Int(), + ItemWeight = c.Decimal(precision: 18, scale: 4), + BundleData = c.String(), + ProductCost = c.Decimal(nullable: false, precision: 18, scale: 4), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Order", t => t.OrderId, cascadeDelete: true) + .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) + .Index(t => t.OrderId) + .Index(t => t.ProductId); + + CreateTable( + "dbo.OrderNote", + c => new + { + Id = c.Int(nullable: false, identity: true), + OrderId = c.Int(nullable: false), + Note = c.String(nullable: false), + DisplayToCustomer = c.Boolean(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Order", t => t.OrderId, cascadeDelete: true) + .Index(t => t.OrderId); + + CreateTable( + "dbo.RewardPointsHistory", + c => new + { + Id = c.Int(nullable: false, identity: true), + CustomerId = c.Int(nullable: false), + Points = c.Int(nullable: false), + PointsBalance = c.Int(nullable: false), + UsedAmount = c.Decimal(nullable: false, precision: 18, scale: 4), + Message = c.String(maxLength: 4000), + CreatedOnUtc = c.DateTime(nullable: false), + UsedWithOrder_Id = c.Int(), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) + .ForeignKey("dbo.Order", t => t.UsedWithOrder_Id) + .Index(t => t.CustomerId) + .Index(t => t.UsedWithOrder_Id); + + CreateTable( + "dbo.Shipment", + c => new + { + Id = c.Int(nullable: false, identity: true), + OrderId = c.Int(nullable: false), + TrackingNumber = c.String(maxLength: 4000), + TotalWeight = c.Decimal(precision: 18, scale: 4), + ShippedDateUtc = c.DateTime(), + DeliveryDateUtc = c.DateTime(), + CreatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Order", t => t.OrderId, cascadeDelete: true) + .Index(t => t.OrderId); + + CreateTable( + "dbo.ShipmentItem", + c => new + { + Id = c.Int(nullable: false, identity: true), + ShipmentId = c.Int(nullable: false), + OrderItemId = c.Int(nullable: false), + Quantity = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Shipment", t => t.ShipmentId, cascadeDelete: true) + .Index(t => t.ShipmentId); + + CreateTable( + "dbo.ReturnRequest", + c => new + { + Id = c.Int(nullable: false, identity: true), + StoreId = c.Int(nullable: false), + OrderItemId = c.Int(nullable: false), + CustomerId = c.Int(nullable: false), + Quantity = c.Int(nullable: false), + ReasonForReturn = c.String(nullable: false, maxLength: 4000), + RequestedAction = c.String(nullable: false, maxLength: 4000), + RequestedActionUpdatedOnUtc = c.DateTime(), + CustomerComments = c.String(maxLength: 4000), + StaffNotes = c.String(maxLength: 4000), + AdminComment = c.String(maxLength: 4000), + ReturnRequestStatusId = c.Int(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) + .Index(t => t.CustomerId); + + CreateTable( + "dbo.ShoppingCartItem", + c => new + { + Id = c.Int(nullable: false, identity: true), + StoreId = c.Int(nullable: false), + ParentItemId = c.Int(), + BundleItemId = c.Int(), + ShoppingCartTypeId = c.Int(nullable: false), + CustomerId = c.Int(nullable: false), + ProductId = c.Int(nullable: false), + AttributesXml = c.String(), + CustomerEnteredPrice = c.Decimal(nullable: false, precision: 18, scale: 4), + Quantity = c.Int(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.ProductBundleItem", t => t.BundleItemId) + .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) + .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) + .Index(t => t.BundleItemId) + .Index(t => t.CustomerId) + .Index(t => t.ProductId); + + CreateTable( + "dbo.CheckoutAttribute", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 400), + TextPrompt = c.String(maxLength: 4000), + IsRequired = c.Boolean(nullable: false), + ShippableProductRequired = c.Boolean(nullable: false), + IsTaxExempt = c.Boolean(nullable: false), + TaxCategoryId = c.Int(nullable: false), + AttributeControlTypeId = c.Int(nullable: false), + DisplayOrder = c.Int(nullable: false), + IsActive = c.Boolean(nullable: false), + LimitedToStores = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.CheckoutAttributeValue", + c => new + { + Id = c.Int(nullable: false, identity: true), + CheckoutAttributeId = c.Int(nullable: false), + Name = c.String(nullable: false, maxLength: 400), + PriceAdjustment = c.Decimal(nullable: false, precision: 18, scale: 4), + WeightAdjustment = c.Decimal(nullable: false, precision: 18, scale: 4), + IsPreSelected = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.CheckoutAttribute", t => t.CheckoutAttributeId, cascadeDelete: true) + .Index(t => t.CheckoutAttributeId); + + CreateTable( + "dbo.RecurringPaymentHistory", + c => new + { + Id = c.Int(nullable: false, identity: true), + RecurringPaymentId = c.Int(nullable: false), + OrderId = c.Int(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.RecurringPayment", t => t.RecurringPaymentId, cascadeDelete: true) + .Index(t => t.RecurringPaymentId); + + CreateTable( + "dbo.RecurringPayment", + c => new + { + Id = c.Int(nullable: false, identity: true), + CycleLength = c.Int(nullable: false), + CyclePeriodId = c.Int(nullable: false), + TotalCycles = c.Int(nullable: false), + StartDateUtc = c.DateTime(nullable: false), + IsActive = c.Boolean(nullable: false), + Deleted = c.Boolean(nullable: false), + InitialOrderId = c.Int(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Order", t => t.InitialOrderId) + .Index(t => t.InitialOrderId); + + CreateTable( + "dbo.ActivityLog", + c => new + { + Id = c.Int(nullable: false, identity: true), + ActivityLogTypeId = c.Int(nullable: false), + CustomerId = c.Int(nullable: false), + Comment = c.String(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.ActivityLogType", t => t.ActivityLogTypeId, cascadeDelete: true) + .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) + .Index(t => t.ActivityLogTypeId) + .Index(t => t.CustomerId); + + CreateTable( + "dbo.ActivityLogType", + c => new + { + Id = c.Int(nullable: false, identity: true), + SystemKeyword = c.String(nullable: false, maxLength: 100), + Name = c.String(nullable: false, maxLength: 200), + Enabled = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.Log", + c => new + { + Id = c.Int(nullable: false, identity: true), + LogLevelId = c.Int(nullable: false), + ShortMessage = c.String(nullable: false, maxLength: 4000), + FullMessage = c.String(), + IpAddress = c.String(maxLength: 200), + CustomerId = c.Int(), + PageUrl = c.String(maxLength: 1500), + ReferrerUrl = c.String(maxLength: 1500), + CreatedOnUtc = c.DateTime(nullable: false), + Logger = c.String(nullable: false, maxLength: 400), + HttpMethod = c.String(maxLength: 10), + UserName = c.String(maxLength: 100), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) + .Index(t => t.LogLevelId, name: "IX_Log_Level") + .Index(t => t.CustomerId) + .Index(t => t.Logger, name: "IX_Log_Logger"); + + CreateTable( + "dbo.Forums_Subscription", + c => new + { + Id = c.Int(nullable: false, identity: true), + SubscriptionGuid = c.Guid(nullable: false), + CustomerId = c.Int(nullable: false), + ForumId = c.Int(nullable: false), + TopicId = c.Int(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Customer", t => t.CustomerId) + .Index(t => t.CustomerId); + + CreateTable( + "dbo.Forums_PrivateMessage", + c => new + { + Id = c.Int(nullable: false, identity: true), + StoreId = c.Int(nullable: false), + FromCustomerId = c.Int(nullable: false), + ToCustomerId = c.Int(nullable: false), + Subject = c.String(nullable: false, maxLength: 450), + Text = c.String(nullable: false), + IsRead = c.Boolean(nullable: false), + IsDeletedByAuthor = c.Boolean(nullable: false), + IsDeletedByRecipient = c.Boolean(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Customer", t => t.FromCustomerId) + .ForeignKey("dbo.Customer", t => t.ToCustomerId) + .Index(t => t.FromCustomerId) + .Index(t => t.ToCustomerId); + + CreateTable( + "dbo.Affiliate", + c => new + { + Id = c.Int(nullable: false, identity: true), + AddressId = c.Int(nullable: false), + Deleted = c.Boolean(nullable: false), + Active = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Address", t => t.AddressId) + .Index(t => t.AddressId); + + CreateTable( + "dbo.ScheduleTask", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 500), + Alias = c.String(maxLength: 500), + CronExpression = c.String(maxLength: 1000), + Type = c.String(nullable: false, maxLength: 800), + Enabled = c.Boolean(nullable: false), + StopOnError = c.Boolean(nullable: false), + NextRunUtc = c.DateTime(), + LastStartUtc = c.DateTime(), + LastEndUtc = c.DateTime(), + LastSuccessUtc = c.DateTime(), + LastError = c.String(maxLength: 1000), + IsHidden = c.Boolean(nullable: false), + ProgressPercent = c.Int(), + ProgressMessage = c.String(maxLength: 1000), + RowVersion = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"), + }) + .PrimaryKey(t => t.Id) + .Index(t => t.Type) + .Index(t => new { t.NextRunUtc, t.Enabled }, name: "IX_NextRun_Enabled") + .Index(t => new { t.LastStartUtc, t.LastEndUtc }, name: "IX_LastStart_LastEnd"); + + CreateTable( + "dbo.AclRecord", + c => new + { + Id = c.Int(nullable: false, identity: true), + EntityId = c.Int(nullable: false), + EntityName = c.String(nullable: false, maxLength: 400), + CustomerRoleId = c.Int(nullable: false), + IsIdle = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.CustomerRole", t => t.CustomerRoleId, cascadeDelete: true) + .Index(t => t.CustomerRoleId); + + CreateTable( + "dbo.UrlRecord", + c => new + { + Id = c.Int(nullable: false, identity: true), + EntityId = c.Int(nullable: false), + EntityName = c.String(nullable: false, maxLength: 400), + Slug = c.String(nullable: false, maxLength: 400), + IsActive = c.Boolean(nullable: false), + LanguageId = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.GenericAttribute", + c => new + { + Id = c.Int(nullable: false, identity: true), + EntityId = c.Int(nullable: false), + KeyGroup = c.String(nullable: false, maxLength: 400), + Key = c.String(nullable: false, maxLength: 400), + Value = c.String(nullable: false), + StoreId = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .Index(t => t.Key, name: "IX_GenericAttribute_Key"); + + CreateTable( + "dbo.ThemeVariable", + c => new + { + Id = c.Int(nullable: false, identity: true), + Theme = c.String(maxLength: 400), + Name = c.String(maxLength: 400), + Value = c.String(maxLength: 2000), + StoreId = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.Store", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 400), + Url = c.String(nullable: false, maxLength: 400), + SslEnabled = c.Boolean(nullable: false), + SecureUrl = c.String(maxLength: 400), + Hosts = c.String(maxLength: 1000), + LogoPictureId = c.Int(nullable: false), + DisplayOrder = c.Int(nullable: false), + HtmlBodyId = c.String(maxLength: 4000), + ContentDeliveryNetwork = c.String(maxLength: 400), + PrimaryStoreCurrencyId = c.Int(nullable: false, defaultValue: 1), + PrimaryExchangeRateCurrencyId = c.Int(nullable: false, defaultValue: 1), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Currency", t => t.PrimaryExchangeRateCurrencyId) + .ForeignKey("dbo.Currency", t => t.PrimaryStoreCurrencyId) + .Index(t => t.PrimaryStoreCurrencyId) + .Index(t => t.PrimaryExchangeRateCurrencyId); + + CreateTable( + "dbo.Currency", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 50), + CurrencyCode = c.String(nullable: false, maxLength: 5), + Rate = c.Decimal(nullable: false, precision: 18, scale: 8), + DisplayLocale = c.String(maxLength: 50), + CustomFormatting = c.String(maxLength: 50), + LimitedToStores = c.Boolean(nullable: false), + Published = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + DomainEndings = c.String(maxLength: 1000), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.StoreMapping", + c => new + { + Id = c.Int(nullable: false, identity: true), + EntityId = c.Int(nullable: false), + EntityName = c.String(nullable: false, maxLength: 400), + StoreId = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.PaymentMethod", + c => new + { + Id = c.Int(nullable: false, identity: true), + PaymentMethodSystemName = c.String(nullable: false, maxLength: 4000), + FullDescription = c.String(maxLength: 4000), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.QueuedEmailAttachment", + c => new + { + Id = c.Int(nullable: false, identity: true), + QueuedEmailId = c.Int(nullable: false), + StorageLocation = c.Int(nullable: false), + Path = c.String(maxLength: 1000), + FileId = c.Int(), + Data = c.Binary(), + Name = c.String(nullable: false, maxLength: 200), + MimeType = c.String(nullable: false, maxLength: 200), + MediaStorageId = c.Int(), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Download", t => t.FileId, cascadeDelete: true) + .ForeignKey("dbo.MediaStorage", t => t.MediaStorageId) + .ForeignKey("dbo.QueuedEmail", t => t.QueuedEmailId, cascadeDelete: true) + .Index(t => t.QueuedEmailId) + .Index(t => t.FileId) + .Index(t => t.MediaStorageId); + + CreateTable( + "dbo.QueuedEmail", + c => new + { + Id = c.Int(nullable: false, identity: true), + Priority = c.Int(nullable: false), + From = c.String(nullable: false, maxLength: 500), + FromName = c.String(maxLength: 500), + To = c.String(nullable: false, maxLength: 500), + ToName = c.String(maxLength: 500), + ReplyTo = c.String(maxLength: 500), + ReplyToName = c.String(maxLength: 500), + CC = c.String(maxLength: 500), + Bcc = c.String(maxLength: 500), + Subject = c.String(maxLength: 1000), + Body = c.String(), + CreatedOnUtc = c.DateTime(nullable: false), + SentTries = c.Int(nullable: false), + SentOnUtc = c.DateTime(), + EmailAccountId = c.Int(nullable: false), + SendManually = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.EmailAccount", t => t.EmailAccountId, cascadeDelete: true) + .Index(t => t.EmailAccountId); + + CreateTable( + "dbo.EmailAccount", + c => new + { + Id = c.Int(nullable: false, identity: true), + Email = c.String(nullable: false, maxLength: 255), + DisplayName = c.String(maxLength: 255), + Host = c.String(nullable: false, maxLength: 255), + Port = c.Int(nullable: false), + Username = c.String(nullable: false, maxLength: 255), + Password = c.String(nullable: false, maxLength: 255), + EnableSsl = c.Boolean(nullable: false), + UseDefaultCredentials = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.Campaign", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 4000), + Subject = c.String(nullable: false, maxLength: 4000), + Body = c.String(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + LimitedToStores = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.MessageTemplate", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 200), + BccEmailAddresses = c.String(maxLength: 200), + Subject = c.String(maxLength: 1000), + Body = c.String(), + IsActive = c.Boolean(nullable: false), + EmailAccountId = c.Int(nullable: false), + LimitedToStores = c.Boolean(nullable: false), + SendManually = c.Boolean(nullable: false), + Attachment1FileId = c.Int(), + Attachment2FileId = c.Int(), + Attachment3FileId = c.Int(), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.NewsLetterSubscription", + c => new + { + Id = c.Int(nullable: false, identity: true), + NewsLetterSubscriptionGuid = c.Guid(nullable: false), + Email = c.String(nullable: false, maxLength: 255), + Active = c.Boolean(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + StoreId = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .Index(t => new { t.Email, t.StoreId }, name: "IX_NewsletterSubscription_Email_StoreId"); + + CreateTable( + "dbo.MeasureWeight", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 100), + SystemKeyword = c.String(nullable: false, maxLength: 100), + Ratio = c.Decimal(nullable: false, precision: 18, scale: 8), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.MeasureDimension", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 100), + SystemKeyword = c.String(nullable: false, maxLength: 100), + Ratio = c.Decimal(nullable: false, precision: 18, scale: 8), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.ImportProfile", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 100), + FolderName = c.String(nullable: false, maxLength: 100), + FileTypeId = c.Int(nullable: false), + EntityTypeId = c.Int(nullable: false), + Enabled = c.Boolean(nullable: false), + Skip = c.Int(nullable: false), + Take = c.Int(nullable: false), + UpdateOnly = c.Boolean(nullable: false), + KeyFieldNames = c.String(maxLength: 1000), + FileTypeConfiguration = c.String(), + ExtraData = c.String(), + ColumnMapping = c.String(), + ResultInfo = c.String(), + SchedulingTaskId = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.ScheduleTask", t => t.SchedulingTaskId) + .Index(t => t.SchedulingTaskId); + + CreateTable( + "dbo.SyncMapping", + c => new + { + Id = c.Int(nullable: false, identity: true), + EntityId = c.Int(nullable: false), + SourceKey = c.String(nullable: false, maxLength: 150), + EntityName = c.String(nullable: false, maxLength: 100), + ContextName = c.String(nullable: false, maxLength: 100), + SourceHash = c.String(maxLength: 40), + CustomInt = c.Int(), + CustomString = c.String(), + CustomBool = c.Boolean(), + SyncedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .Index(t => new { t.EntityId, t.EntityName, t.ContextName }, unique: true, name: "IX_SyncMapping_ByEntity") + .Index(t => new { t.SourceKey, t.EntityName, t.ContextName }, unique: true, name: "IX_SyncMapping_BySource"); + + CreateTable( + "dbo.ExportDeployment", + c => new + { + Id = c.Int(nullable: false, identity: true), + ProfileId = c.Int(nullable: false), + Name = c.String(nullable: false, maxLength: 100), + Enabled = c.Boolean(nullable: false), + ResultInfo = c.String(), + DeploymentTypeId = c.Int(nullable: false), + Username = c.String(maxLength: 400), + Password = c.String(maxLength: 400), + Url = c.String(maxLength: 4000), + HttpTransmissionTypeId = c.Int(nullable: false), + HttpTransmissionType = c.Int(nullable: false), + FileSystemPath = c.String(maxLength: 400), + SubFolder = c.String(maxLength: 400), + EmailAddresses = c.String(maxLength: 4000), + EmailSubject = c.String(maxLength: 400), + EmailAccountId = c.Int(nullable: false), + PassiveMode = c.Boolean(nullable: false), + UseSsl = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.ExportProfile", t => t.ProfileId, cascadeDelete: true) + .Index(t => t.ProfileId); + + CreateTable( + "dbo.ExportProfile", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 100), + FolderName = c.String(nullable: false, maxLength: 400), + FileNamePattern = c.String(maxLength: 400), + SystemName = c.String(maxLength: 400), + ProviderSystemName = c.String(nullable: false, maxLength: 4000), + IsSystemProfile = c.Boolean(nullable: false), + Enabled = c.Boolean(nullable: false), + SchedulingTaskId = c.Int(nullable: false), + Filtering = c.String(), + Projection = c.String(), + ProviderConfigData = c.String(), + ResultInfo = c.String(), + Offset = c.Int(nullable: false), + Limit = c.Int(nullable: false), + BatchSize = c.Int(nullable: false), + PerStore = c.Boolean(nullable: false), + EmailAccountId = c.Int(nullable: false), + CompletedEmailAddresses = c.String(maxLength: 400), + CreateZipArchive = c.Boolean(nullable: false), + Cleanup = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.ScheduleTask", t => t.SchedulingTaskId) + .Index(t => t.SchedulingTaskId); + + CreateTable( + "dbo.BackInStockSubscription", + c => new + { + Id = c.Int(nullable: false, identity: true), + StoreId = c.Int(nullable: false), + ProductId = c.Int(nullable: false), + CustomerId = c.Int(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) + .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) + .Index(t => t.ProductId) + .Index(t => t.CustomerId); + + CreateTable( + "dbo.ManufacturerTemplate", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 400), + ViewPath = c.String(nullable: false, maxLength: 400), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.CategoryTemplate", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 400), + ViewPath = c.String(nullable: false, maxLength: 400), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.ProductTemplate", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 400), + ViewPath = c.String(nullable: false, maxLength: 400), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.RelatedProduct", + c => new + { + Id = c.Int(nullable: false, identity: true), + ProductId1 = c.Int(nullable: false), + ProductId2 = c.Int(nullable: false), + DisplayOrder = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + CreateTable( + "dbo.CrossSellProduct", + c => new + { + Id = c.Int(nullable: false, identity: true), + ProductId1 = c.Int(nullable: false), + ProductId2 = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id); + + //CreateTable( + // "dbo.PermissionRecord_Role_Mapping", + // c => new + // { + // PermissionRecord_Id = c.Int(nullable: false), + // CustomerRole_Id = c.Int(nullable: false), + // }) + // .PrimaryKey(t => new { t.PermissionRecord_Id, t.CustomerRole_Id }) + // .ForeignKey("dbo.PermissionRecord", t => t.PermissionRecord_Id, cascadeDelete: true) + // .ForeignKey("dbo.CustomerRole", t => t.CustomerRole_Id, cascadeDelete: true) + // .Index(t => t.PermissionRecord_Id) + // .Index(t => t.CustomerRole_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 }) + .ForeignKey("dbo.ShippingMethod", t => t.ShippingMethod_Id, cascadeDelete: true) + .ForeignKey("dbo.Country", t => t.Country_Id, cascadeDelete: true) + .Index(t => t.ShippingMethod_Id) + .Index(t => t.Country_Id); + + CreateTable( + "dbo.CustomerAddresses", + c => new + { + Customer_Id = c.Int(nullable: false), + Address_Id = c.Int(nullable: false), + }) + .PrimaryKey(t => new { t.Customer_Id, t.Address_Id }) + .ForeignKey("dbo.Customer", t => t.Customer_Id, cascadeDelete: true) + .ForeignKey("dbo.Address", t => t.Address_Id, cascadeDelete: true) + .Index(t => t.Customer_Id) + .Index(t => t.Address_Id); + + CreateTable( + "dbo.Discount_AppliedToCategories", + c => new + { + Discount_Id = c.Int(nullable: false), + Category_Id = c.Int(nullable: false), + }) + .PrimaryKey(t => new { t.Discount_Id, t.Category_Id }) + .ForeignKey("dbo.Discount", t => t.Discount_Id, cascadeDelete: true) + .ForeignKey("dbo.Category", t => t.Category_Id, cascadeDelete: true) + .Index(t => t.Discount_Id) + .Index(t => t.Category_Id); + + CreateTable( + "dbo.Discount_AppliedToManufacturers", + c => new + { + Discount_Id = c.Int(nullable: false), + Manufacturer_Id = c.Int(nullable: false), + }) + .PrimaryKey(t => new { t.Discount_Id, t.Manufacturer_Id }) + .ForeignKey("dbo.Discount", t => t.Discount_Id, cascadeDelete: true) + .ForeignKey("dbo.Manufacturer", t => t.Manufacturer_Id, cascadeDelete: true) + .Index(t => t.Discount_Id) + .Index(t => t.Manufacturer_Id); + + CreateTable( + "dbo.Discount_AppliedToProducts", + c => new + { + Discount_Id = c.Int(nullable: false), + Product_Id = c.Int(nullable: false), + }) + .PrimaryKey(t => new { t.Discount_Id, t.Product_Id }) + .ForeignKey("dbo.Discount", t => t.Discount_Id, cascadeDelete: true) + .ForeignKey("dbo.Product", t => t.Product_Id, cascadeDelete: true) + .Index(t => t.Discount_Id) + .Index(t => t.Product_Id); + + CreateTable( + "dbo.Product_ProductTag_Mapping", + c => new + { + Product_Id = c.Int(nullable: false), + ProductTag_Id = c.Int(nullable: false), + }) + .PrimaryKey(t => new { t.Product_Id, t.ProductTag_Id }) + .ForeignKey("dbo.Product", t => t.Product_Id, cascadeDelete: true) + .ForeignKey("dbo.ProductTag", t => t.ProductTag_Id, cascadeDelete: true) + .Index(t => t.Product_Id) + .Index(t => t.ProductTag_Id); + + // Obsolete. Is now implemented by granular permissions. + //CreateTable( + // "dbo.Customer_CustomerRole_Mapping", + // c => new + // { + // Customer_Id = c.Int(nullable: false), + // CustomerRole_Id = c.Int(nullable: false), + // }) + // .PrimaryKey(t => new { t.Customer_Id, t.CustomerRole_Id }) + // .ForeignKey("dbo.Customer", t => t.Customer_Id, cascadeDelete: true) + // .ForeignKey("dbo.CustomerRole", t => t.CustomerRole_Id, cascadeDelete: true) + // .Index(t => t.Customer_Id) + // .Index(t => t.CustomerRole_Id); + + CreateTable( + "dbo.PollVotingRecord", + c => new + { + Id = c.Int(nullable: false), + PollAnswerId = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.CustomerContent", t => t.Id) + .ForeignKey("dbo.PollAnswer", t => t.PollAnswerId, cascadeDelete: true) + .Index(t => t.Id) + .Index(t => t.PollAnswerId); + + CreateTable( + "dbo.NewsComment", + c => new + { + Id = c.Int(nullable: false), + CommentTitle = c.String(maxLength: 4000), + CommentText = c.String(), + NewsItemId = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.CustomerContent", t => t.Id) + .ForeignKey("dbo.News", t => t.NewsItemId, cascadeDelete: true) + .Index(t => t.Id) + .Index(t => t.NewsItemId); + + CreateTable( + "dbo.BlogComment", + c => new + { + Id = c.Int(nullable: false), + CommentText = c.String(), + BlogPostId = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.CustomerContent", t => t.Id) + .ForeignKey("dbo.BlogPost", t => t.BlogPostId, cascadeDelete: true) + .Index(t => t.Id) + .Index(t => t.BlogPostId); + + CreateTable( + "dbo.ProductReviewHelpfulness", + c => new + { + Id = c.Int(nullable: false), + ProductReviewId = c.Int(nullable: false), + WasHelpful = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.CustomerContent", t => t.Id) + .ForeignKey("dbo.ProductReview", t => t.ProductReviewId) + .Index(t => t.Id) + .Index(t => t.ProductReviewId); + + CreateTable( + "dbo.ProductReview", + c => new + { + Id = c.Int(nullable: false), + ProductId = c.Int(nullable: false), + Title = c.String(maxLength: 4000), + ReviewText = c.String(), + Rating = c.Int(nullable: false), + HelpfulYesTotal = c.Int(nullable: false), + HelpfulNoTotal = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.CustomerContent", t => t.Id) + .ForeignKey("dbo.Product", t => t.ProductId, cascadeDelete: true) + .Index(t => t.Id) + .Index(t => t.ProductId); + + #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"); + } + + #endregion + } + + private void DownInitial() + { + DropForeignKey("dbo.ProductReview", "ProductId", "dbo.Product"); + DropForeignKey("dbo.ProductReview", "Id", "dbo.CustomerContent"); + DropForeignKey("dbo.ProductReviewHelpfulness", "ProductReviewId", "dbo.ProductReview"); + DropForeignKey("dbo.ProductReviewHelpfulness", "Id", "dbo.CustomerContent"); + DropForeignKey("dbo.BlogComment", "BlogPostId", "dbo.BlogPost"); + DropForeignKey("dbo.BlogComment", "Id", "dbo.CustomerContent"); + DropForeignKey("dbo.NewsComment", "NewsItemId", "dbo.News"); + DropForeignKey("dbo.NewsComment", "Id", "dbo.CustomerContent"); + DropForeignKey("dbo.PollVotingRecord", "PollAnswerId", "dbo.PollAnswer"); + DropForeignKey("dbo.PollVotingRecord", "Id", "dbo.CustomerContent"); + DropForeignKey("dbo.BackInStockSubscription", "ProductId", "dbo.Product"); + DropForeignKey("dbo.BackInStockSubscription", "CustomerId", "dbo.Customer"); + DropForeignKey("dbo.ExportDeployment", "ProfileId", "dbo.ExportProfile"); + DropForeignKey("dbo.ExportProfile", "SchedulingTaskId", "dbo.ScheduleTask"); + DropForeignKey("dbo.ImportProfile", "SchedulingTaskId", "dbo.ScheduleTask"); + DropForeignKey("dbo.QueuedEmailAttachment", "QueuedEmailId", "dbo.QueuedEmail"); + DropForeignKey("dbo.QueuedEmail", "EmailAccountId", "dbo.EmailAccount"); + DropForeignKey("dbo.QueuedEmailAttachment", "MediaStorageId", "dbo.MediaStorage"); + DropForeignKey("dbo.QueuedEmailAttachment", "FileId", "dbo.Download"); + DropForeignKey("dbo.Store", "PrimaryStoreCurrencyId", "dbo.Currency"); + DropForeignKey("dbo.Store", "PrimaryExchangeRateCurrencyId", "dbo.Currency"); + DropForeignKey("dbo.AclRecord", "CustomerRoleId", "dbo.CustomerRole"); + DropForeignKey("dbo.Affiliate", "AddressId", "dbo.Address"); + DropForeignKey("dbo.Forums_PrivateMessage", "ToCustomerId", "dbo.Customer"); + DropForeignKey("dbo.Forums_PrivateMessage", "FromCustomerId", "dbo.Customer"); + DropForeignKey("dbo.Forums_Subscription", "CustomerId", "dbo.Customer"); + DropForeignKey("dbo.Log", "CustomerId", "dbo.Customer"); + DropForeignKey("dbo.ActivityLog", "CustomerId", "dbo.Customer"); + DropForeignKey("dbo.ActivityLog", "ActivityLogTypeId", "dbo.ActivityLogType"); + DropForeignKey("dbo.RecurringPaymentHistory", "RecurringPaymentId", "dbo.RecurringPayment"); + DropForeignKey("dbo.RecurringPayment", "InitialOrderId", "dbo.Order"); + DropForeignKey("dbo.CheckoutAttributeValue", "CheckoutAttributeId", "dbo.CheckoutAttribute"); + DropForeignKey("dbo.ShoppingCartItem", "ProductId", "dbo.Product"); + DropForeignKey("dbo.ShoppingCartItem", "CustomerId", "dbo.Customer"); + DropForeignKey("dbo.ShoppingCartItem", "BundleItemId", "dbo.ProductBundleItem"); + DropForeignKey("dbo.Customer", "ShippingAddress_Id", "dbo.Address"); + DropForeignKey("dbo.ReturnRequest", "CustomerId", "dbo.Customer"); + DropForeignKey("dbo.Order", "ShippingAddressId", "dbo.Address"); + DropForeignKey("dbo.ShipmentItem", "ShipmentId", "dbo.Shipment"); + DropForeignKey("dbo.Shipment", "OrderId", "dbo.Order"); + DropForeignKey("dbo.RewardPointsHistory", "UsedWithOrder_Id", "dbo.Order"); + DropForeignKey("dbo.RewardPointsHistory", "CustomerId", "dbo.Customer"); + DropForeignKey("dbo.OrderNote", "OrderId", "dbo.Order"); + DropForeignKey("dbo.GiftCardUsageHistory", "UsedWithOrderId", "dbo.Order"); + DropForeignKey("dbo.GiftCardUsageHistory", "GiftCardId", "dbo.GiftCard"); + DropForeignKey("dbo.GiftCard", "PurchasedWithOrderItemId", "dbo.OrderItem"); + DropForeignKey("dbo.OrderItem", "ProductId", "dbo.Product"); + DropForeignKey("dbo.OrderItem", "OrderId", "dbo.Order"); + DropForeignKey("dbo.DiscountUsageHistory", "OrderId", "dbo.Order"); + DropForeignKey("dbo.DiscountUsageHistory", "DiscountId", "dbo.Discount"); + DropForeignKey("dbo.Order", "CustomerId", "dbo.Customer"); + DropForeignKey("dbo.Order", "BillingAddressId", "dbo.Address"); + DropForeignKey("dbo.Forums_Post", "TopicId", "dbo.Forums_Topic"); + DropForeignKey("dbo.Forums_Topic", "ForumId", "dbo.Forums_Forum"); + DropForeignKey("dbo.Forums_Forum", "ForumGroupId", "dbo.Forums_Group"); + DropForeignKey("dbo.Forums_Topic", "CustomerId", "dbo.Customer"); + DropForeignKey("dbo.Forums_Post", "CustomerId", "dbo.Customer"); + DropForeignKey("dbo.ExternalAuthenticationRecord", "CustomerId", "dbo.Customer"); + //DropForeignKey("dbo.Customer_CustomerRole_Mapping", "CustomerRole_Id", "dbo.CustomerRole"); + //DropForeignKey("dbo.Customer_CustomerRole_Mapping", "Customer_Id", "dbo.Customer"); + DropForeignKey("dbo.News", "LanguageId", "dbo.Language"); + DropForeignKey("dbo.TierPrice", "ProductId", "dbo.Product"); + DropForeignKey("dbo.TierPrice", "CustomerRoleId", "dbo.CustomerRole"); + DropForeignKey("dbo.Product", "SampleDownloadId", "dbo.Download"); + DropForeignKey("dbo.Download", "MediaStorageId", "dbo.MediaStorage"); + DropForeignKey("dbo.Product", "QuantityUnitId", "dbo.QuantityUnit"); + DropForeignKey("dbo.ProductVariantAttributeValue", "ProductVariantAttributeId", "dbo.Product_ProductAttribute_Mapping"); + DropForeignKey("dbo.Product_ProductAttribute_Mapping", "ProductAttributeId", "dbo.ProductAttribute"); + DropForeignKey("dbo.ProductAttributeOption", "ProductAttributeOptionsSetId", "dbo.ProductAttributeOptionsSet"); + DropForeignKey("dbo.ProductAttributeOptionsSet", "ProductAttributeId", "dbo.ProductAttribute"); + DropForeignKey("dbo.Product_ProductAttribute_Mapping", "ProductId", "dbo.Product"); + DropForeignKey("dbo.ProductVariantAttributeCombination", "QuantityUnitId", "dbo.QuantityUnit"); + DropForeignKey("dbo.ProductVariantAttributeCombination", "ProductId", "dbo.Product"); + DropForeignKey("dbo.ProductVariantAttributeCombination", "DeliveryTimeId", "dbo.DeliveryTime"); + DropForeignKey("dbo.Product_ProductTag_Mapping", "ProductTag_Id", "dbo.ProductTag"); + DropForeignKey("dbo.Product_ProductTag_Mapping", "Product_Id", "dbo.Product"); + DropForeignKey("dbo.Product_SpecificationAttribute_Mapping", "SpecificationAttributeOptionId", "dbo.SpecificationAttributeOption"); + DropForeignKey("dbo.SpecificationAttributeOption", "SpecificationAttributeId", "dbo.SpecificationAttribute"); + DropForeignKey("dbo.Product_SpecificationAttribute_Mapping", "ProductId", "dbo.Product"); + DropForeignKey("dbo.Product_Manufacturer_Mapping", "ProductId", "dbo.Product"); + DropForeignKey("dbo.Product_Manufacturer_Mapping", "ManufacturerId", "dbo.Manufacturer"); + DropForeignKey("dbo.Product_Category_Mapping", "ProductId", "dbo.Product"); + DropForeignKey("dbo.Product_Category_Mapping", "CategoryId", "dbo.Category"); + DropForeignKey("dbo.ProductBundleItem", "ProductId", "dbo.Product"); + DropForeignKey("dbo.ProductBundleItem", "BundleProductId", "dbo.Product"); + DropForeignKey("dbo.ProductBundleItemAttributeFilter", "BundleItemId", "dbo.ProductBundleItem"); + DropForeignKey("dbo.Product", "DeliveryTimeId", "dbo.DeliveryTime"); + DropForeignKey("dbo.Product", "CountryOfOriginId", "dbo.Country"); + //DropForeignKey("dbo.DiscountRequirement", "DiscountId", "dbo.Discount"); + DropForeignKey("dbo.Discount_AppliedToProducts", "Product_Id", "dbo.Product"); + DropForeignKey("dbo.Discount_AppliedToProducts", "Discount_Id", "dbo.Discount"); + DropForeignKey("dbo.Discount_AppliedToManufacturers", "Manufacturer_Id", "dbo.Manufacturer"); + DropForeignKey("dbo.Discount_AppliedToManufacturers", "Discount_Id", "dbo.Discount"); + DropForeignKey("dbo.Manufacturer", "PictureId", "dbo.Picture"); + DropForeignKey("dbo.Discount_AppliedToCategories", "Category_Id", "dbo.Category"); + DropForeignKey("dbo.Discount_AppliedToCategories", "Discount_Id", "dbo.Discount"); + DropForeignKey("dbo.Category", "PictureId", "dbo.Picture"); + DropForeignKey("dbo.Product_Picture_Mapping", "ProductId", "dbo.Product"); + DropForeignKey("dbo.Product_Picture_Mapping", "PictureId", "dbo.Picture"); + DropForeignKey("dbo.Picture", "MediaStorageId", "dbo.MediaStorage"); + DropForeignKey("dbo.BlogPost", "LanguageId", "dbo.Language"); + DropForeignKey("dbo.CustomerContent", "CustomerId", "dbo.Customer"); + DropForeignKey("dbo.Customer", "BillingAddress_Id", "dbo.Address"); + DropForeignKey("dbo.CustomerAddresses", "Address_Id", "dbo.Address"); + DropForeignKey("dbo.CustomerAddresses", "Customer_Id", "dbo.Customer"); + DropForeignKey("dbo.Address", "StateProvinceId", "dbo.StateProvince"); + DropForeignKey("dbo.Address", "CountryId", "dbo.Country"); + DropForeignKey("dbo.StateProvince", "CountryId", "dbo.Country"); + DropForeignKey("dbo.ShippingMethodRestrictions", "Country_Id", "dbo.Country"); + DropForeignKey("dbo.ShippingMethodRestrictions", "ShippingMethod_Id", "dbo.ShippingMethod"); + DropForeignKey("dbo.PollAnswer", "PollId", "dbo.Poll"); + DropForeignKey("dbo.Poll", "LanguageId", "dbo.Language"); + DropForeignKey("dbo.PermissionRecord_Role_Mapping", "CustomerRole_Id", "dbo.CustomerRole"); + DropForeignKey("dbo.PermissionRecord_Role_Mapping", "PermissionRecord_Id", "dbo.PermissionRecord"); + DropForeignKey("dbo.LocalizedProperty", "LanguageId", "dbo.Language"); + DropForeignKey("dbo.LocaleStringResource", "LanguageId", "dbo.Language"); + DropIndex("dbo.ProductReview", new[] { "ProductId" }); + DropIndex("dbo.ProductReview", new[] { "Id" }); + DropIndex("dbo.ProductReviewHelpfulness", new[] { "ProductReviewId" }); + DropIndex("dbo.ProductReviewHelpfulness", new[] { "Id" }); + DropIndex("dbo.BlogComment", new[] { "BlogPostId" }); + DropIndex("dbo.BlogComment", new[] { "Id" }); + DropIndex("dbo.NewsComment", new[] { "NewsItemId" }); + DropIndex("dbo.NewsComment", new[] { "Id" }); + DropIndex("dbo.PollVotingRecord", new[] { "PollAnswerId" }); + DropIndex("dbo.PollVotingRecord", new[] { "Id" }); + //DropIndex("dbo.Customer_CustomerRole_Mapping", new[] { "CustomerRole_Id" }); + //DropIndex("dbo.Customer_CustomerRole_Mapping", new[] { "Customer_Id" }); + DropIndex("dbo.Product_ProductTag_Mapping", new[] { "ProductTag_Id" }); + DropIndex("dbo.Product_ProductTag_Mapping", new[] { "Product_Id" }); + DropIndex("dbo.Discount_AppliedToProducts", new[] { "Product_Id" }); + DropIndex("dbo.Discount_AppliedToProducts", new[] { "Discount_Id" }); + DropIndex("dbo.Discount_AppliedToManufacturers", new[] { "Manufacturer_Id" }); + DropIndex("dbo.Discount_AppliedToManufacturers", new[] { "Discount_Id" }); + DropIndex("dbo.Discount_AppliedToCategories", new[] { "Category_Id" }); + DropIndex("dbo.Discount_AppliedToCategories", new[] { "Discount_Id" }); + DropIndex("dbo.CustomerAddresses", new[] { "Address_Id" }); + DropIndex("dbo.CustomerAddresses", new[] { "Customer_Id" }); + DropIndex("dbo.ShippingMethodRestrictions", new[] { "Country_Id" }); + DropIndex("dbo.ShippingMethodRestrictions", new[] { "ShippingMethod_Id" }); + DropIndex("dbo.PermissionRecord_Role_Mapping", new[] { "CustomerRole_Id" }); + DropIndex("dbo.PermissionRecord_Role_Mapping", new[] { "PermissionRecord_Id" }); + DropIndex("dbo.BackInStockSubscription", new[] { "CustomerId" }); + DropIndex("dbo.BackInStockSubscription", new[] { "ProductId" }); + DropIndex("dbo.ExportProfile", new[] { "SchedulingTaskId" }); + DropIndex("dbo.ExportDeployment", new[] { "ProfileId" }); + DropIndex("dbo.SyncMapping", "IX_SyncMapping_BySource"); + DropIndex("dbo.SyncMapping", "IX_SyncMapping_ByEntity"); + DropIndex("dbo.ImportProfile", new[] { "SchedulingTaskId" }); + DropIndex("dbo.NewsLetterSubscription", "IX_NewsletterSubscription_Email_StoreId"); + DropIndex("dbo.QueuedEmail", new[] { "EmailAccountId" }); + DropIndex("dbo.QueuedEmailAttachment", new[] { "MediaStorageId" }); + DropIndex("dbo.QueuedEmailAttachment", new[] { "FileId" }); + DropIndex("dbo.QueuedEmailAttachment", new[] { "QueuedEmailId" }); + DropIndex("dbo.Store", new[] { "PrimaryExchangeRateCurrencyId" }); + DropIndex("dbo.Store", new[] { "PrimaryStoreCurrencyId" }); + DropIndex("dbo.GenericAttribute", "IX_GenericAttribute_Key"); + DropIndex("dbo.AclRecord", new[] { "CustomerRoleId" }); + DropIndex("dbo.ScheduleTask", "IX_LastStart_LastEnd"); + DropIndex("dbo.ScheduleTask", "IX_NextRun_Enabled"); + DropIndex("dbo.ScheduleTask", new[] { "Type" }); + DropIndex("dbo.Affiliate", new[] { "AddressId" }); + DropIndex("dbo.Forums_PrivateMessage", new[] { "ToCustomerId" }); + DropIndex("dbo.Forums_PrivateMessage", new[] { "FromCustomerId" }); + DropIndex("dbo.Forums_Subscription", new[] { "CustomerId" }); + DropIndex("dbo.Log", "IX_Log_Logger"); + DropIndex("dbo.Log", new[] { "CustomerId" }); + DropIndex("dbo.Log", "IX_Log_Level"); + DropIndex("dbo.ActivityLog", new[] { "CustomerId" }); + DropIndex("dbo.ActivityLog", new[] { "ActivityLogTypeId" }); + DropIndex("dbo.RecurringPayment", new[] { "InitialOrderId" }); + DropIndex("dbo.RecurringPaymentHistory", new[] { "RecurringPaymentId" }); + DropIndex("dbo.CheckoutAttributeValue", new[] { "CheckoutAttributeId" }); + DropIndex("dbo.ShoppingCartItem", new[] { "ProductId" }); + DropIndex("dbo.ShoppingCartItem", new[] { "CustomerId" }); + DropIndex("dbo.ShoppingCartItem", new[] { "BundleItemId" }); + DropIndex("dbo.ReturnRequest", new[] { "CustomerId" }); + DropIndex("dbo.ShipmentItem", new[] { "ShipmentId" }); + DropIndex("dbo.Shipment", new[] { "OrderId" }); + DropIndex("dbo.RewardPointsHistory", new[] { "UsedWithOrder_Id" }); + DropIndex("dbo.RewardPointsHistory", new[] { "CustomerId" }); + DropIndex("dbo.OrderNote", new[] { "OrderId" }); + DropIndex("dbo.OrderItem", new[] { "ProductId" }); + DropIndex("dbo.OrderItem", new[] { "OrderId" }); + DropIndex("dbo.GiftCard", new[] { "PurchasedWithOrderItemId" }); + DropIndex("dbo.GiftCardUsageHistory", new[] { "UsedWithOrderId" }); + DropIndex("dbo.GiftCardUsageHistory", new[] { "GiftCardId" }); + DropIndex("dbo.DiscountUsageHistory", new[] { "OrderId" }); + DropIndex("dbo.DiscountUsageHistory", new[] { "DiscountId" }); + DropIndex("dbo.Order", new[] { "Deleted" }); + DropIndex("dbo.Order", new[] { "ShippingAddressId" }); + DropIndex("dbo.Order", new[] { "BillingAddressId" }); + DropIndex("dbo.Order", new[] { "CustomerId" }); + DropIndex("dbo.Forums_Forum", new[] { "ForumGroupId" }); + DropIndex("dbo.Forums_Topic", new[] { "CustomerId" }); + DropIndex("dbo.Forums_Topic", new[] { "ForumId" }); + DropIndex("dbo.Forums_Post", new[] { "CustomerId" }); + DropIndex("dbo.Forums_Post", new[] { "TopicId" }); + DropIndex("dbo.ExternalAuthenticationRecord", new[] { "CustomerId" }); + DropIndex("dbo.News", new[] { "LanguageId" }); + DropIndex("dbo.TierPrice", new[] { "CustomerRoleId" }); + DropIndex("dbo.TierPrice", new[] { "ProductId" }); + DropIndex("dbo.Download", new[] { "MediaStorageId" }); + DropIndex("dbo.Download", "IX_UpdatedOn_IsTransient"); + DropIndex("dbo.Download", new[] { "DownloadGuid" }); + DropIndex("dbo.ProductVariantAttributeValue", "IX_ProductVariantAttributeValue_ProductVariantAttributeId_DisplayOrder"); + DropIndex("dbo.ProductAttributeOption", new[] { "ProductAttributeOptionsSetId" }); + DropIndex("dbo.ProductAttributeOptionsSet", new[] { "ProductAttributeId" }); + DropIndex("dbo.Product_ProductAttribute_Mapping", new[] { "ProductAttributeId" }); + DropIndex("dbo.Product_ProductAttribute_Mapping", "IX_Product_ProductAttribute_Mapping_ProductId_DisplayOrder"); + DropIndex("dbo.ProductVariantAttributeCombination", new[] { "ProductId" }); + DropIndex("dbo.ProductVariantAttributeCombination", new[] { "QuantityUnitId" }); + DropIndex("dbo.ProductVariantAttributeCombination", new[] { "DeliveryTimeId" }); + DropIndex("dbo.SpecificationAttributeOption", new[] { "SpecificationAttributeId" }); + DropIndex("dbo.Product_SpecificationAttribute_Mapping", new[] { "SpecificationAttributeOptionId" }); + DropIndex("dbo.Product_SpecificationAttribute_Mapping", new[] { "ProductId" }); + DropIndex("dbo.Product_Manufacturer_Mapping", new[] { "ManufacturerId" }); + DropIndex("dbo.Product_Manufacturer_Mapping", new[] { "ProductId" }); + DropIndex("dbo.Product_Category_Mapping", new[] { "CategoryId" }); + DropIndex("dbo.Product_Category_Mapping", new[] { "ProductId" }); + DropIndex("dbo.ProductBundleItemAttributeFilter", new[] { "BundleItemId" }); + DropIndex("dbo.ProductBundleItem", new[] { "BundleProductId" }); + DropIndex("dbo.ProductBundleItem", new[] { "ProductId" }); + //DropIndex("dbo.DiscountRequirement", new[] { "DiscountId" }); + DropIndex("dbo.Manufacturer", new[] { "Deleted" }); + DropIndex("dbo.Manufacturer", new[] { "PictureId" }); + DropIndex("dbo.Product_Picture_Mapping", new[] { "PictureId" }); + DropIndex("dbo.Product_Picture_Mapping", new[] { "ProductId" }); + DropIndex("dbo.Picture", new[] { "MediaStorageId" }); + DropIndex("dbo.Picture", "IX_UpdatedOn_IsTransient"); + DropIndex("dbo.Category", new[] { "Deleted" }); + DropIndex("dbo.Category", new[] { "PictureId" }); + DropIndex("dbo.Product", new[] { "CountryOfOriginId" }); + DropIndex("dbo.Product", new[] { "QuantityUnitId" }); + DropIndex("dbo.Product", new[] { "DeliveryTimeId" }); + DropIndex("dbo.Product", new[] { "Deleted" }); + DropIndex("dbo.Product", new[] { "SampleDownloadId" }); + DropIndex("dbo.Product", new[] { "Gtin" }); + DropIndex("dbo.Product", new[] { "ManufacturerPartNumber" }); + DropIndex("dbo.BlogPost", new[] { "LanguageId" }); + DropIndex("dbo.StateProvince", new[] { "CountryId" }); + DropIndex("dbo.Address", new[] { "StateProvinceId" }); + DropIndex("dbo.Address", new[] { "CountryId" }); + DropIndex("dbo.Customer", new[] { "ShippingAddress_Id" }); + DropIndex("dbo.Customer", new[] { "BillingAddress_Id" }); + DropIndex("dbo.Customer", "IX_Customer_LastActivity"); + DropIndex("dbo.Customer", "IX_Customer_CreatedOn"); + DropIndex("dbo.Customer", "IX_Customer_LastIpAddress"); + DropIndex("dbo.Customer", new[] { "SystemName" }); + DropIndex("dbo.Customer", new[] { "Deleted" }); + DropIndex("dbo.CustomerContent", new[] { "CustomerId" }); + DropIndex("dbo.PollAnswer", new[] { "PollId" }); + DropIndex("dbo.Poll", new[] { "LanguageId" }); + DropIndex("dbo.LocaleStringResource", new[] { "LanguageId" }); + DropIndex("dbo.LocalizedProperty", "IX_LocalizedProperty_Compound"); + DropTable("dbo.ProductReview"); + DropTable("dbo.ProductReviewHelpfulness"); + DropTable("dbo.BlogComment"); + DropTable("dbo.NewsComment"); + DropTable("dbo.PollVotingRecord"); + //DropTable("dbo.Customer_CustomerRole_Mapping"); + DropTable("dbo.Product_ProductTag_Mapping"); + DropTable("dbo.Discount_AppliedToProducts"); + DropTable("dbo.Discount_AppliedToManufacturers"); + DropTable("dbo.Discount_AppliedToCategories"); + DropTable("dbo.CustomerAddresses"); + DropTable("dbo.ShippingMethodRestrictions"); + DropTable("dbo.PermissionRecord_Role_Mapping"); + DropTable("dbo.CrossSellProduct"); + DropTable("dbo.RelatedProduct"); + DropTable("dbo.ProductTemplate"); + DropTable("dbo.CategoryTemplate"); + DropTable("dbo.ManufacturerTemplate"); + DropTable("dbo.BackInStockSubscription"); + DropTable("dbo.ExportProfile"); + DropTable("dbo.ExportDeployment"); + DropTable("dbo.SyncMapping"); + DropTable("dbo.ImportProfile"); + DropTable("dbo.MeasureDimension"); + DropTable("dbo.MeasureWeight"); + DropTable("dbo.NewsLetterSubscription"); + DropTable("dbo.MessageTemplate"); + DropTable("dbo.Campaign"); + DropTable("dbo.EmailAccount"); + DropTable("dbo.QueuedEmail"); + DropTable("dbo.QueuedEmailAttachment"); + DropTable("dbo.PaymentMethod"); + DropTable("dbo.StoreMapping"); + DropTable("dbo.Currency"); + DropTable("dbo.Store"); + DropTable("dbo.ThemeVariable"); + DropTable("dbo.GenericAttribute"); + DropTable("dbo.UrlRecord"); + DropTable("dbo.AclRecord"); + DropTable("dbo.ScheduleTask"); + DropTable("dbo.Affiliate"); + DropTable("dbo.Forums_PrivateMessage"); + DropTable("dbo.Forums_Subscription"); + DropTable("dbo.Log"); + DropTable("dbo.ActivityLogType"); + DropTable("dbo.ActivityLog"); + DropTable("dbo.RecurringPayment"); + DropTable("dbo.RecurringPaymentHistory"); + DropTable("dbo.CheckoutAttributeValue"); + DropTable("dbo.CheckoutAttribute"); + DropTable("dbo.ShoppingCartItem"); + DropTable("dbo.ReturnRequest"); + DropTable("dbo.ShipmentItem"); + DropTable("dbo.Shipment"); + DropTable("dbo.RewardPointsHistory"); + DropTable("dbo.OrderNote"); + DropTable("dbo.OrderItem"); + DropTable("dbo.GiftCard"); + DropTable("dbo.GiftCardUsageHistory"); + DropTable("dbo.DiscountUsageHistory"); + DropTable("dbo.Order"); + DropTable("dbo.Forums_Group"); + DropTable("dbo.Forums_Forum"); + DropTable("dbo.Forums_Topic"); + DropTable("dbo.Forums_Post"); + DropTable("dbo.ExternalAuthenticationRecord"); + DropTable("dbo.News"); + DropTable("dbo.TierPrice"); + DropTable("dbo.Download"); + DropTable("dbo.ProductVariantAttributeValue"); + DropTable("dbo.ProductAttributeOption"); + DropTable("dbo.ProductAttributeOptionsSet"); + DropTable("dbo.ProductAttribute"); + DropTable("dbo.Product_ProductAttribute_Mapping"); + DropTable("dbo.QuantityUnit"); + DropTable("dbo.ProductVariantAttributeCombination"); + DropTable("dbo.ProductTag"); + DropTable("dbo.SpecificationAttribute"); + DropTable("dbo.SpecificationAttributeOption"); + DropTable("dbo.Product_SpecificationAttribute_Mapping"); + DropTable("dbo.Product_Manufacturer_Mapping"); + DropTable("dbo.Product_Category_Mapping"); + DropTable("dbo.ProductBundleItemAttributeFilter"); + DropTable("dbo.ProductBundleItem"); + DropTable("dbo.DeliveryTime"); + //DropTable("dbo.DiscountRequirement"); + DropTable("dbo.Manufacturer"); + DropTable("dbo.Product_Picture_Mapping"); + DropTable("dbo.MediaStorage"); + DropTable("dbo.Picture"); + DropTable("dbo.Category"); + DropTable("dbo.Discount"); + DropTable("dbo.Product"); + DropTable("dbo.BlogPost"); + DropTable("dbo.StateProvince"); + DropTable("dbo.ShippingMethod"); + DropTable("dbo.Country"); + DropTable("dbo.Address"); + DropTable("dbo.Customer"); + DropTable("dbo.CustomerContent"); + DropTable("dbo.PollAnswer"); + DropTable("dbo.Poll"); + DropTable("dbo.CustomerRole"); + DropTable("dbo.PermissionRecord"); + DropTable("dbo.LocaleStringResource"); + DropTable("dbo.Language"); + DropTable("dbo.LocalizedProperty"); + DropTable("dbo.Setting"); + DropTable("dbo.TaxCategory"); + DropTable("dbo.Topic"); + } + } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201706020759565_UpdateMediaPath.cs b/src/Libraries/SmartStore.Data/Migrations/201706020759565_UpdateMediaPath.cs index b4f74fd90f..0cbb97ef49 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201706020759565_UpdateMediaPath.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201706020759565_UpdateMediaPath.cs @@ -1,37 +1,37 @@ namespace SmartStore.Data.Migrations { - using System.Data.Entity.Migrations; - using System.Web.Hosting; - using Core.Data; - using Setup; + using System.Data.Entity.Migrations; + using System.Web.Hosting; + using Core.Data; + using Setup; - public partial class UpdateMediaPath : DbMigration, ILocaleResourcesProvider, IDataSeeder - { + public partial class UpdateMediaPath : DbMigration, ILocaleResourcesProvider, IDataSeeder + { public override void Up() { - if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) - { - var tenantName = DataSettings.Current.TenantName.NullEmpty() ?? "Default"; - var uploadedPath = $"/Media/{tenantName}/Uploaded/"; - var thumbsPath = $"/Media/{tenantName}/Thumbs/"; - - Sql($"UPDATE [dbo].[Category] SET [Description] = REPLACE([Description],'/Media/Uploaded/','{uploadedPath}')"); - Sql($"UPDATE [dbo].[Category] SET [BottomDescription] = REPLACE([BottomDescription],'/Media/Uploaded/','{uploadedPath}')"); - Sql($"UPDATE [dbo].[Manufacturer] SET [Description] = REPLACE([Description],'/Media/Uploaded/','{uploadedPath}')"); - Sql($"UPDATE [dbo].[ShippingMethod] SET [Description] = REPLACE([Description],'/Media/Uploaded/','{uploadedPath}')"); - Sql($"UPDATE [dbo].[PaymentMethod] SET [FullDescription] = REPLACE([FullDescription],'/Media/Uploaded/','{uploadedPath}')"); - Sql($"UPDATE [dbo].[BlogPost] SET [Body] = REPLACE([Body],'/Media/Uploaded/','{uploadedPath}')"); - Sql($"UPDATE [dbo].[News] SET [Full] = REPLACE([Full],'/Media/Uploaded/','{uploadedPath}')"); - Sql($"UPDATE [dbo].[Campaign] SET [Body] = REPLACE([Body],'/Media/Uploaded/','{uploadedPath}')"); - Sql($"UPDATE [dbo].[QueuedEmail] SET [Body] = REPLACE([Body],'/Media/Uploaded/','{uploadedPath}')"); - Sql($"UPDATE [dbo].[QueuedEmail] SET [Body] = REPLACE([Body],'/Media/Thumbs/','{thumbsPath}')"); - Sql($"UPDATE [dbo].[Topic] SET [Body] = REPLACE([Body],'/Media/Uploaded/','{uploadedPath}')"); - Sql($"UPDATE [dbo].[MessageTemplate] SET [Body] = REPLACE([Body],'/Media/Uploaded/','{uploadedPath}')"); - Sql($"UPDATE [dbo].[Product] SET [FullDescription] = REPLACE([FullDescription],'/Media/Uploaded/','{uploadedPath}')"); - - // LocalizedProperty - Sql($"UPDATE [dbo].[LocalizedProperty] SET [LocaleValue] = REPLACE([LocaleValue],'/Media/Thumbs/','{thumbsPath}') WHERE [LocaleKey] = 'Body' AND [LocaleKeyGroup] = 'QueuedEmail'"); - Sql($@" + if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) + { + var tenantName = DataSettings.Current.TenantName.NullEmpty() ?? "Default"; + var uploadedPath = $"/Media/{tenantName}/Uploaded/"; + var thumbsPath = $"/Media/{tenantName}/Thumbs/"; + + Sql($"UPDATE [dbo].[Category] SET [Description] = REPLACE([Description],'/Media/Uploaded/','{uploadedPath}')"); + Sql($"UPDATE [dbo].[Category] SET [BottomDescription] = REPLACE([BottomDescription],'/Media/Uploaded/','{uploadedPath}')"); + Sql($"UPDATE [dbo].[Manufacturer] SET [Description] = REPLACE([Description],'/Media/Uploaded/','{uploadedPath}')"); + Sql($"UPDATE [dbo].[ShippingMethod] SET [Description] = REPLACE([Description],'/Media/Uploaded/','{uploadedPath}')"); + Sql($"UPDATE [dbo].[PaymentMethod] SET [FullDescription] = REPLACE([FullDescription],'/Media/Uploaded/','{uploadedPath}')"); + Sql($"UPDATE [dbo].[BlogPost] SET [Body] = REPLACE([Body],'/Media/Uploaded/','{uploadedPath}')"); + Sql($"UPDATE [dbo].[News] SET [Full] = REPLACE([Full],'/Media/Uploaded/','{uploadedPath}')"); + Sql($"UPDATE [dbo].[Campaign] SET [Body] = REPLACE([Body],'/Media/Uploaded/','{uploadedPath}')"); + Sql($"UPDATE [dbo].[QueuedEmail] SET [Body] = REPLACE([Body],'/Media/Uploaded/','{uploadedPath}')"); + Sql($"UPDATE [dbo].[QueuedEmail] SET [Body] = REPLACE([Body],'/Media/Thumbs/','{thumbsPath}')"); + Sql($"UPDATE [dbo].[Topic] SET [Body] = REPLACE([Body],'/Media/Uploaded/','{uploadedPath}')"); + Sql($"UPDATE [dbo].[MessageTemplate] SET [Body] = REPLACE([Body],'/Media/Uploaded/','{uploadedPath}')"); + Sql($"UPDATE [dbo].[Product] SET [FullDescription] = REPLACE([FullDescription],'/Media/Uploaded/','{uploadedPath}')"); + + // LocalizedProperty + Sql($"UPDATE [dbo].[LocalizedProperty] SET [LocaleValue] = REPLACE([LocaleValue],'/Media/Thumbs/','{thumbsPath}') WHERE [LocaleKey] = 'Body' AND [LocaleKeyGroup] = 'QueuedEmail'"); + Sql($@" UPDATE [dbo].[LocalizedProperty] SET [LocaleValue] = REPLACE([LocaleValue],'/Media/Uploaded/','{uploadedPath}') WHERE ([LocaleKey] = 'BottomDescription' AND [LocaleKeyGroup] = 'Category') OR @@ -39,33 +39,30 @@ public override void Up() ([LocaleKey] = 'Description' AND [LocaleKeyGroup] IN ('Category', 'Manufacturer', 'ShippingMethod')) OR ([LocaleKey] = 'FullDescription' AND [LocaleKeyGroup] IN ('PaymentMethod', 'Product')) OR ([LocaleKey] = 'Body' AND [LocaleKeyGroup] IN ('BlogPost', 'Campaign', 'QueuedEmail', 'MessageTemplate', 'Topic'))"); - } - } - + } + } + public override void Down() { } - public bool RollbackOnFailure - { - get { return false; } - } + public bool RollbackOnFailure => false; - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); - context.SaveChanges(); - } + context.SaveChanges(); + } - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Common.For", "For: {0}", "Für: {0}"); - builder.AddOrUpdate("Products.Sorting.Featured", "Featured", "Empfehlung"); + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("Common.For", "For: {0}", "Für: {0}"); + builder.AddOrUpdate("Products.Sorting.Featured", "Featured", "Empfehlung"); - builder.AddOrUpdate("Common.AdditionalShippingSurcharge", - "Plus {0} shipping surcharge", - "zzgl. {0} zusätzlicher Versandgebühr"); + builder.AddOrUpdate("Common.AdditionalShippingSurcharge", + "Plus {0} shipping surcharge", + "zzgl. {0} zusätzlicher Versandgebühr"); builder.AddOrUpdate("Address.Fields.Salutation", "Salutation", "Anrede"); builder.AddOrUpdate("Address.Fields.Title", "Title", "Titel"); @@ -107,8 +104,8 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Specifies whether to only export individually visible associated products.", "Legt fest, ob nur individuell sichtbare, verknüpfte Produkte exportiert werden sollen."); - builder.AddOrUpdate("Product.ThumbTitle", "{0}, Picture {1} large", "{0}, Bild {1} groß"); - builder.AddOrUpdate("Product.ThumbAlternateText", "{0}, Picture {1}", "{0}, Bild {1}"); - } - } + builder.AddOrUpdate("Product.ThumbTitle", "{0}, Picture {1} large", "{0}, Bild {1} groß"); + builder.AddOrUpdate("Product.ThumbAlternateText", "{0}, Picture {1}", "{0}, Bild {1}"); + } + } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201707190940318_V302Resources.cs b/src/Libraries/SmartStore.Data/Migrations/201707190940318_V302Resources.cs index d2f4b57b0b..c858380a1f 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201707190940318_V302Resources.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201707190940318_V302Resources.cs @@ -1,23 +1,19 @@ namespace SmartStore.Data.Migrations { - using Setup; - using System; using System.Data.Entity.Migrations; + using Setup; public partial class V302Resources : DbMigration, ILocaleResourcesProvider, IDataSeeder { public override void Up() { } - + public override void Down() { } - public bool RollbackOnFailure - { - get { return false; } - } + public bool RollbackOnFailure => false; public void Seed(SmartObjectContext context) { diff --git a/src/Libraries/SmartStore.Data/Migrations/201707281452589_TierPriceCalcMethod.cs b/src/Libraries/SmartStore.Data/Migrations/201707281452589_TierPriceCalcMethod.cs index 3662816352..c4c696c686 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201707281452589_TierPriceCalcMethod.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201707281452589_TierPriceCalcMethod.cs @@ -1,8 +1,7 @@ namespace SmartStore.Data.Migrations { - using Setup; - using System; using System.Data.Entity.Migrations; + using Setup; public partial class TierPriceCalcMethod : DbMigration, ILocaleResourcesProvider, IDataSeeder { @@ -10,17 +9,14 @@ public override void Up() { AddColumn("dbo.TierPrice", "CalculationMethod", c => c.Int(nullable: false)); } - + public override void Down() { DropColumn("dbo.TierPrice", "CalculationMethod"); } - public bool RollbackOnFailure - { - get { return false; } - } - + public bool RollbackOnFailure => false; + public void Seed(SmartObjectContext context) { context.MigrateLocaleResources(MigrateLocaleResources); @@ -41,7 +37,7 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Specifies whether to apply tierprice percentage to attribute price adjustments", "Bestimmt ob prozentuale Ermigungen von Staffelpreisen auf Auf- & Abpreise von Attributen angewendet werden sollen"); - builder.AddOrUpdate("Admin.Header.Account", "Account", "Account"); + builder.AddOrUpdate("Admin.Header.Account", "Account", "Account"); } } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201708251628482_SystemTopics.cs b/src/Libraries/SmartStore.Data/Migrations/201708251628482_SystemTopics.cs index 70488ff6a7..fae5395bdf 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201708251628482_SystemTopics.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201708251628482_SystemTopics.cs @@ -1,10 +1,9 @@ namespace SmartStore.Data.Migrations { + using System.Data.Entity.Migrations; + using System.Linq; using Core.Domain.Topics; using Setup; - using System; - using System.Linq; - using System.Data.Entity.Migrations; public partial class SystemTopics : DbMigration, ILocaleResourcesProvider, IDataSeeder { @@ -12,16 +11,13 @@ public override void Up() { AddColumn("dbo.Topic", "IsSystemTopic", c => c.Boolean(nullable: false, defaultValue: false)); } - + public override void Down() { DropColumn("dbo.Topic", "IsSystemTopic"); } - public bool RollbackOnFailure - { - get { return false; } - } + public bool RollbackOnFailure => false; public void Seed(SmartObjectContext context) { @@ -43,15 +39,15 @@ public void Seed(SmartObjectContext context) var topics = context.Set().Where(x => systemTopics.Contains(x.SystemName)).ToList(); topics.Each(x => x.IsSystemTopic = true); - context.MigrateLocaleResources(MigrateLocaleResources); + context.MigrateLocaleResources(MigrateLocaleResources); - context.SaveChanges(); + context.SaveChanges(); } public void MigrateLocaleResources(LocaleResourcesBuilder builder) { - builder.AddOrUpdate("Admin.ContentManagement.Topics.CannotBeDeleted", - "This topic is required by your Shop and can therefore not be deleted.", + builder.AddOrUpdate("Admin.ContentManagement.Topics.CannotBeDeleted", + "This topic is required by your Shop and can therefore not be deleted.", "Diese Seite wird von Ihrem Shop bentigt und kann daher nicht gelscht werden."); } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201709141000226_V303Resources.cs b/src/Libraries/SmartStore.Data/Migrations/201709141000226_V303Resources.cs index 12b8d62ab1..2142dca719 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201709141000226_V303Resources.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201709141000226_V303Resources.cs @@ -1,23 +1,19 @@ namespace SmartStore.Data.Migrations { - using Setup; - using System; using System.Data.Entity.Migrations; + using Setup; public partial class V303Resources : DbMigration, ILocaleResourcesProvider, IDataSeeder { public override void Up() { } - + public override void Down() { } - public bool RollbackOnFailure - { - get { return false; } - } + public bool RollbackOnFailure => false; public void Seed(SmartObjectContext context) { diff --git a/src/Libraries/SmartStore.Data/Migrations/201709251538312_UpdateTrustedShopsTask.cs b/src/Libraries/SmartStore.Data/Migrations/201709251538312_UpdateTrustedShopsTask.cs index ac54451906..b7984d1b2b 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201709251538312_UpdateTrustedShopsTask.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201709251538312_UpdateTrustedShopsTask.cs @@ -1,25 +1,21 @@ namespace SmartStore.Data.Migrations { - using Core.Domain.Tasks; - using Setup; - using System; using System.Data.Entity.Migrations; using System.Linq; + using Core.Domain.Tasks; + using Setup; public partial class UpdateTrustedShopsTask : DbMigration, IDataSeeder { public override void Up() { } - + public override void Down() { } - public bool RollbackOnFailure - { - get { return false; } - } + public bool RollbackOnFailure => false; public void Seed(SmartObjectContext context) { @@ -30,7 +26,7 @@ public void Seed(SmartObjectContext context) { // remove old task table.Remove(taskToDelete); - + // if task to remnove is not null we assume the plugin is currently installed and so we add the new task without any further request var taskToAdd = new ScheduleTask { diff --git a/src/Libraries/SmartStore.Data/Migrations/201710102038287_CurrencyRounding.cs b/src/Libraries/SmartStore.Data/Migrations/201710102038287_CurrencyRounding.cs index 7cfdcbb63b..cf6805e1cf 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201710102038287_CurrencyRounding.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201710102038287_CurrencyRounding.cs @@ -15,7 +15,7 @@ public override void Up() AddColumn("dbo.Currency", "RoundOrderTotalRule", c => c.Int(nullable: false)); AddColumn("dbo.PaymentMethod", "RoundOrderTotalEnabled", c => c.Boolean(nullable: false)); } - + public override void Down() { DropColumn("dbo.PaymentMethod", "RoundOrderTotalEnabled"); @@ -27,10 +27,7 @@ public override void Down() DropColumn("dbo.Order", "OrderTotalRounding"); } - public bool RollbackOnFailure - { - get { return false; } - } + public bool RollbackOnFailure => false; public void Seed(SmartObjectContext context) { @@ -105,11 +102,11 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "The number of decimal digits must be between 0 and 8.", "Die Anzahl der Dezimalstellen muss zwischen 0 und 8 liegen."); - builder.Delete( - "Admin.Configuration.Settings.ShoppingCart.RoundPricesDuringCalculation", - "Admin.Configuration.Settings.ShoppingCart.RoundPricesDuringCalculation.Hint"); + builder.Delete( + "Admin.Configuration.Settings.ShoppingCart.RoundPricesDuringCalculation", + "Admin.Configuration.Settings.ShoppingCart.RoundPricesDuringCalculation.Hint"); - builder.AddOrUpdate("Admin.Orders.Fields.OrderTotalRounding", + builder.AddOrUpdate("Admin.Orders.Fields.OrderTotalRounding", "Rounding", "Rundung", "The amount by which the order total was rounded up or down.", diff --git a/src/Libraries/SmartStore.Data/Migrations/201710252016556_IndexOptionNames.cs b/src/Libraries/SmartStore.Data/Migrations/201710252016556_IndexOptionNames.cs index d7f9caa6a4..5020d69b3d 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201710252016556_IndexOptionNames.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201710252016556_IndexOptionNames.cs @@ -10,17 +10,14 @@ public override void Up() AddColumn("dbo.SpecificationAttribute", "IndexOptionNames", c => c.Boolean(nullable: false)); AddColumn("dbo.ProductAttribute", "IndexOptionNames", c => c.Boolean(nullable: false)); } - + public override void Down() { DropColumn("dbo.ProductAttribute", "IndexOptionNames"); DropColumn("dbo.SpecificationAttribute", "IndexOptionNames"); } - public bool RollbackOnFailure - { - get { return false; } - } + public bool RollbackOnFailure => false; public void Seed(SmartObjectContext context) { diff --git a/src/Libraries/SmartStore.Data/Migrations/201711112331162_ProductMainPictureId.cs b/src/Libraries/SmartStore.Data/Migrations/201711112331162_ProductMainPictureId.cs index a3073c4e43..8b6f013735 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201711112331162_ProductMainPictureId.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201711112331162_ProductMainPictureId.cs @@ -1,30 +1,26 @@ namespace SmartStore.Data.Migrations { - using System; - using System.Data.Entity.Migrations; - using Setup; - using Utilities; + using System.Data.Entity.Migrations; + using Setup; + using Utilities; - public partial class ProductMainPictureId : DbMigration, IDataSeeder - { - public override void Up() + public partial class ProductMainPictureId : DbMigration, IDataSeeder + { + public override void Up() { AddColumn("dbo.Product", "MainPictureId", c => c.Int()); } - + public override void Down() { DropColumn("dbo.Product", "MainPictureId"); } - public bool RollbackOnFailure - { - get { return true; } - } + public bool RollbackOnFailure => true; - public void Seed(SmartObjectContext context) - { - DataMigrator.FixProductMainPictureIds(context, true); - } - } + public void Seed(SmartObjectContext context) + { + DataMigrator.FixProductMainPictureIds(context, true); + } + } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201711222311112_MoveFsMedia.cs b/src/Libraries/SmartStore.Data/Migrations/201711222311112_MoveFsMedia.cs index 547f68afe8..cd4527c919 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201711222311112_MoveFsMedia.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201711222311112_MoveFsMedia.cs @@ -1,64 +1,60 @@ namespace SmartStore.Data.Migrations { - using System; - using System.Data.Entity.Migrations; - using System.Web.Hosting; - using SmartStore.Data.Setup; - using SmartStore.Utilities; - using System.IO; - using Core.Data; - using SmartStore.Data.Utilities; - - public partial class MoveFsMedia : DbMigration, IDataSeeder - { - const int DirMaxLength = 4; - - public override void Up() + using System.Data.Entity.Migrations; + using System.IO; + using System.Web.Hosting; + using Core.Data; + using SmartStore.Data.Setup; + using SmartStore.Data.Utilities; + using SmartStore.Utilities; + + public partial class MoveFsMedia : DbMigration, IDataSeeder + { + const int DirMaxLength = 4; + + public override void Up() { } - + public override void Down() { } - public bool RollbackOnFailure - { - get { return true; } - } + public bool RollbackOnFailure => true; - public void Seed(SmartObjectContext context) - { - if (!HostingEnvironment.IsHosted) - return; + public void Seed(SmartObjectContext context) + { + if (!HostingEnvironment.IsHosted) + return; - // Move the whole media folder to new location at first - MoveMediaFolder(); + // Move the whole media folder to new location at first + MoveMediaFolder(); - // Reorganize files (root > Storage/{subfolder}) - DataMigrator.MoveFsMedia(context); - } + // Reorganize files (root > Storage/{subfolder}) + DataMigrator.MoveFsMedia(context); + } - private void MoveMediaFolder() - { - // Moves "~/Media/{Tenant}" to "~/App_Data/Tenants/{Tenant}/Media" - // This is relevant for local file system only. For cloud storages (like Azure) - // we don't need to move the main folder. + private void MoveMediaFolder() + { + // Moves "~/Media/{Tenant}" to "~/App_Data/Tenants/{Tenant}/Media" + // This is relevant for local file system only. For cloud storages (like Azure) + // we don't need to move the main folder. - var sourceDir = new DirectoryInfo(CommonHelper.MapPath("~/Media/" + DataSettings.Current.TenantName, false)); - var destinationDir = new DirectoryInfo(CommonHelper.MapPath("~/App_Data/Tenants/" + DataSettings.Current.TenantName + "/Media", false)); + var sourceDir = new DirectoryInfo(CommonHelper.MapPath("~/Media/" + DataSettings.Current.TenantName, false)); + var destinationDir = new DirectoryInfo(CommonHelper.MapPath("~/App_Data/Tenants/" + DataSettings.Current.TenantName + "/Media", false)); - if (!sourceDir.Exists) - { - // Source (legacy media folder) does not exist, for whatever reasons. Nothing to move here. - return; - } + if (!sourceDir.Exists) + { + // Source (legacy media folder) does not exist, for whatever reasons. Nothing to move here. + return; + } - //if (!destinationDir.Exists) - //{ - // destinationDir.Create(); - //} + //if (!destinationDir.Exists) + //{ + // destinationDir.Create(); + //} - sourceDir.MoveTo(destinationDir.FullName); - } - } + sourceDir.MoveTo(destinationDir.FullName); + } + } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201711291017168_SyncStringResources.cs b/src/Libraries/SmartStore.Data/Migrations/201711291017168_SyncStringResources.cs index a2184757a0..7613251157 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201711291017168_SyncStringResources.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201711291017168_SyncStringResources.cs @@ -1,166 +1,171 @@ namespace SmartStore.Data.Migrations { - using System; - using System.Collections.Generic; - using System.Data.Entity.Migrations; - using System.Linq; - using SmartStore.Collections; - using SmartStore.Core.Domain.Localization; - using SmartStore.Data.Setup; - - public partial class SyncStringResources : DbMigration, IDataSeeder - { - private static HashSet _unusedNames = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "Enums.SmartStore.Core.Domain.Catalog.AttributeControlType.ColorSquares", - "Plugins.Payments.PayPal.IpnLogInfo", - "Plugins.Widgets.TrustedShopsCustomerProtection.Prepared", - "Plugins.Shipping.ByWeight.SmallQuantityThresholdNotReached", - "Products.ProductNotAddedToTheCart.Link" - }; - - private static Dictionary _missingEnglishResources = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "Enums.SmartStore.Plugin.Shipping.Fedex.DropoffType.BusinessServiceCenter", "Business service center" }, - { "Enums.SmartStore.Plugin.Shipping.Fedex.DropoffType.DropBox", "Drop box" }, - { "Enums.SmartStore.Plugin.Shipping.Fedex.DropoffType.RegularPickup", "Regular pickup" }, - { "Enums.SmartStore.Plugin.Shipping.Fedex.DropoffType.RequestCourier", "Request courier" }, - { "Enums.SmartStore.Plugin.Shipping.Fedex.DropoffType.Station", "Station" }, - { "Enums.SmartStore.Plugin.Shipping.Fedex.PackingType.PackByDimensions", "Pack by dimensions" }, - { "Enums.SmartStore.Plugin.Shipping.Fedex.PackingType.PackByOneItemPerPackage", "Pack by one item per package" }, - { "Enums.SmartStore.Plugin.Shipping.Fedex.PackingType.PackByVolume", "Pack by volume" }, - { "Order.OrderDetails", "Order Details" }, - { "Plugins.SmartStore.AccardaKar.Amount", "Amount" }, - { "Plugins.SmartStore.Exports.BMEcatProductXml.ExportProfileInfo", "Please click here to view existing BMEcat export profiles or to create a new one." }, - { "Plugins.Description.SmartStore.DiscountRules", "Provides standard discount rules, e.g. \"Country is\", \"Customer group is\", \"Has amount x spent\" etc." }, - { "Plugins.FriendlyName.DiscountRequirement.BillingCountryIs", "Country of invoice address is" }, - { "Plugins.FriendlyName.DiscountRequirement.HadSpentAmount", "Customer has spent amount x" }, - { "Plugins.FriendlyName.DiscountRequirement.HasAllProducts", "Customer has the following products in his shopping cart" }, - { "Plugins.FriendlyName.DiscountRequirement.HasOneProduct", "Customer has one of the following products in his shopping cart" }, - { "Plugins.FriendlyName.DiscountRequirement.MustBeAssignedToCustomerRole", "The customer must be assigned to a customer group" }, - { "Plugins.FriendlyName.DiscountRequirement.ShippingCountryIs", "Country of delivery address is" }, - { "Plugins.FriendlyName.DiscountRequirement.Store", "Limited to specific shop" }, - { "Plugins.FriendlyName.SmartStore.DiscountRules", "Standard discount rules" }, - { "Plugins.FriendlyName.DiscountRequirement.HasPaymentMethod", "Customer selected specific payment method" }, - { "Plugins.FriendlyName.SmartStore.DiscountRules.HasPaymentMethod", "\"Customer has selected certain payment method\" discount rule" }, - { "Plugins.FriendlyName.DiscountRequirement.HasShippingOption", "Customer selected specific shipping method" }, - { "Plugins.FriendlyName.SmartStore.DiscountRules.HasShippingOption", "\"Customer has selected certain shipping method\" discount rule" }, - { "Plugins.FriendlyName.DiscountRequirement.PurchasedAllProducts", "Customer has already purchased the following products" }, - { "Plugins.FriendlyName.DiscountRequirement.PurchasedOneProduct", "The customer has already purchased one of the following products" }, - { "Plugins.FriendlyName.SmartStore.DiscountRules.PurchasedProducts", "\"Customer has already purchased the following products\" discount rule" }, - { "Plugins.SmartStore.GoogleRemarketing.ConversionId.Hint", "You'll find your conversion id in the admin area of google adwords" }, - { "Plugins.Description.SmartStore.OfflinePayment", "Provides offline payment methods, e.g. direct debit, credit card, invoice, prepayment, cash on delivery etc." }, - { "Plugins.FriendlyName.SmartStore.OfflinePayment", "Offline payment methods" }, - { "Plugins.FriendlyName.SmartStore.OutputCache", "Output Cache" }, - { "Plugins.Description.SmartStore.OutputCache", "Allows the temporary storage of entire shop pages and thus contributes to a considerable increase in performance." }, - { "Plugins.FriendlyName.OutputCacheProvider.Memory", "Local memory" }, - { "Plugins.FriendlyName.OutputCacheProvider.Database", "Database" }, - { "Plugins.FriendlyName.SmartStore.PostFinanceECommerce", "PostFinance" }, - { "Plugins.Description.SmartStore.PostFinanceECommerce", "Enables payment with the swiss PostFinance." }, - { "Plugins.SmartStore.ShopConnector.FinalResult", "{0}... {1} of {2} processed. {3} success, {4} failed, {5} skipped. {6} added, {7} updated." }, - { "Plugins.Widgets.TrustedShopsCustomerProtection.Password", "Trusted Shops password" }, - { "Plugins.Widgets.TrustedShopsCustomerProtection.Password.Hint", "Please enter the password provided by Trusted Shops here." }, - { "Plugins.Widgets.TrustedShopsCustomerProtection.UserName", "Trusted Shops user name" }, - { "Plugins.Widgets.TrustedShopsCustomerProtection.UserName.Hint", "Please enter the user name provided by Trusted Shops here." }, - }; - - public override void Up() - { - } - - public override void Down() - { - } - - public bool RollbackOnFailure - { - get { return true; } - } - - public void Seed(SmartObjectContext context) - { - var resourceSet = context.Set(); - var allLanguages = context.Set().ToList(); - - // Accidents. - var accidents = resourceSet.Where(x => x.ResourceName == "Admin.Configuration.ActivityLog.ActivityLogType").ToList(); - if (accidents.Any()) - { - accidents.Each(x => x.ResourceName = "Admin.Configuration.ActivityLog.ActivityLogType"); - context.SaveChanges(); - } - - // Remove unused resources that could be included in the German set. - var unusedResources = resourceSet.Where(x => _unusedNames.Contains(x.ResourceName)).ToList(); - if (unusedResources.Any()) - { - resourceSet.RemoveRange(unusedResources); - context.SaveChanges(); - unusedResources.Clear(); - } - - // Remove duplicate resources. - foreach (var language in allLanguages) - { - var resources = resourceSet.Where(x => x.LanguageId == language.Id).ToList(); - var deleteResources = new List(); - var resourcesMap = new Multimap(StringComparer.OrdinalIgnoreCase); - resources.Each(x => resourcesMap.Add(x.ResourceName, x)); - - foreach (var item in resourcesMap) - { - if (item.Value.Count > 1) - { - // First is ok, rest is bad. - foreach (var resource in item.Value.OrderByDescending(x => x.IsTouched).Skip(1)) - { - deleteResources.Add(resource); - } - } - } - - if (deleteResources.Any()) - { - resourceSet.RemoveRange(deleteResources); - context.SaveChanges(); - deleteResources.Clear(); - } - } - - // Remove resources that are not included in the German set. - // Unfortunately we cannot do that. We have no information about the origin of a resource. We would delete resources of other developers. - - // Add resources included in the German set but missing in the English set. - var deLanguage = allLanguages.FirstOrDefault(x => x.LanguageCulture.IsCaseInsensitiveEqual("de-DE")); - var enLanguage = allLanguages.FirstOrDefault(x => x.LanguageCulture.IsCaseInsensitiveEqual("en-US")); - if (deLanguage != null && enLanguage != null) - { - var deResources = resourceSet.AsNoTracking().Where(x => x.LanguageId == deLanguage.Id).ToList(); - - var enNames = resourceSet - .Where(x => x.LanguageId == enLanguage.Id) - .Select(x => x.ResourceName) - .Distinct() - .ToList(); - var enNamesSet = new HashSet(enNames, StringComparer.OrdinalIgnoreCase); - - foreach (var resource in deResources) - { - if (!enNames.Contains(resource.ResourceName) && _missingEnglishResources.TryGetValue(resource.ResourceName, out string value)) - { - resourceSet.Add(new LocaleStringResource - { - LanguageId = enLanguage.Id, - ResourceName = resource.ResourceName, - ResourceValue = value, - IsFromPlugin = resource.IsFromPlugin - }); - } - } - - context.SaveChanges(); - deResources.Clear(); - } - } - } + using System; + using System.Collections.Generic; + using System.Data.Entity.Migrations; + using System.Linq; + using SmartStore.Collections; + using SmartStore.Core.Data; + using SmartStore.Core.Domain.Localization; + using SmartStore.Data.Setup; + + public partial class SyncStringResources : DbMigration, IDataSeeder + { + private static HashSet _unusedNames = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "Enums.SmartStore.Core.Domain.Catalog.AttributeControlType.ColorSquares", + "Plugins.Payments.PayPal.IpnLogInfo", + "Plugins.Widgets.TrustedShopsCustomerProtection.Prepared", + "Plugins.Shipping.ByWeight.SmallQuantityThresholdNotReached", + "Products.ProductNotAddedToTheCart.Link" + }; + + private static Dictionary _missingEnglishResources = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "Enums.SmartStore.Plugin.Shipping.Fedex.DropoffType.BusinessServiceCenter", "Business service center" }, + { "Enums.SmartStore.Plugin.Shipping.Fedex.DropoffType.DropBox", "Drop box" }, + { "Enums.SmartStore.Plugin.Shipping.Fedex.DropoffType.RegularPickup", "Regular pickup" }, + { "Enums.SmartStore.Plugin.Shipping.Fedex.DropoffType.RequestCourier", "Request courier" }, + { "Enums.SmartStore.Plugin.Shipping.Fedex.DropoffType.Station", "Station" }, + { "Enums.SmartStore.Plugin.Shipping.Fedex.PackingType.PackByDimensions", "Pack by dimensions" }, + { "Enums.SmartStore.Plugin.Shipping.Fedex.PackingType.PackByOneItemPerPackage", "Pack by one item per package" }, + { "Enums.SmartStore.Plugin.Shipping.Fedex.PackingType.PackByVolume", "Pack by volume" }, + { "Order.OrderDetails", "Order Details" }, + { "Plugins.SmartStore.AccardaKar.Amount", "Amount" }, + { "Plugins.SmartStore.Exports.BMEcatProductXml.ExportProfileInfo", "Please click here to view existing BMEcat export profiles or to create a new one." }, + { "Plugins.Description.SmartStore.DiscountRules", "Provides standard discount rules, e.g. \"Country is\", \"Customer group is\", \"Has amount x spent\" etc." }, + { "Plugins.FriendlyName.DiscountRequirement.BillingCountryIs", "Country of invoice address is" }, + { "Plugins.FriendlyName.DiscountRequirement.HadSpentAmount", "Customer has spent amount x" }, + { "Plugins.FriendlyName.DiscountRequirement.HasAllProducts", "Customer has the following products in his shopping cart" }, + { "Plugins.FriendlyName.DiscountRequirement.HasOneProduct", "Customer has one of the following products in his shopping cart" }, + { "Plugins.FriendlyName.DiscountRequirement.MustBeAssignedToCustomerRole", "The customer must be assigned to a customer group" }, + { "Plugins.FriendlyName.DiscountRequirement.ShippingCountryIs", "Country of delivery address is" }, + { "Plugins.FriendlyName.DiscountRequirement.Store", "Limited to specific shop" }, + { "Plugins.FriendlyName.SmartStore.DiscountRules", "Standard discount rules" }, + { "Plugins.FriendlyName.DiscountRequirement.HasPaymentMethod", "Customer selected specific payment method" }, + { "Plugins.FriendlyName.SmartStore.DiscountRules.HasPaymentMethod", "\"Customer has selected certain payment method\" discount rule" }, + { "Plugins.FriendlyName.DiscountRequirement.HasShippingOption", "Customer selected specific shipping method" }, + { "Plugins.FriendlyName.SmartStore.DiscountRules.HasShippingOption", "\"Customer has selected certain shipping method\" discount rule" }, + { "Plugins.FriendlyName.DiscountRequirement.PurchasedAllProducts", "Customer has already purchased the following products" }, + { "Plugins.FriendlyName.DiscountRequirement.PurchasedOneProduct", "The customer has already purchased one of the following products" }, + { "Plugins.FriendlyName.SmartStore.DiscountRules.PurchasedProducts", "\"Customer has already purchased the following products\" discount rule" }, + { "Plugins.SmartStore.GoogleRemarketing.ConversionId.Hint", "You'll find your conversion id in the admin area of google adwords" }, + { "Plugins.Description.SmartStore.OfflinePayment", "Provides offline payment methods, e.g. direct debit, credit card, invoice, prepayment, cash on delivery etc." }, + { "Plugins.FriendlyName.SmartStore.OfflinePayment", "Offline payment methods" }, + { "Plugins.FriendlyName.SmartStore.OutputCache", "Output Cache" }, + { "Plugins.Description.SmartStore.OutputCache", "Allows the temporary storage of entire shop pages and thus contributes to a considerable increase in performance." }, + { "Plugins.FriendlyName.OutputCacheProvider.Memory", "Local memory" }, + { "Plugins.FriendlyName.OutputCacheProvider.Database", "Database" }, + { "Plugins.FriendlyName.SmartStore.PostFinanceECommerce", "PostFinance" }, + { "Plugins.Description.SmartStore.PostFinanceECommerce", "Enables payment with the swiss PostFinance." }, + { "Plugins.SmartStore.ShopConnector.FinalResult", "{0}... {1} of {2} processed. {3} success, {4} failed, {5} skipped. {6} added, {7} updated." }, + { "Plugins.Widgets.TrustedShopsCustomerProtection.Password", "Trusted Shops password" }, + { "Plugins.Widgets.TrustedShopsCustomerProtection.Password.Hint", "Please enter the password provided by Trusted Shops here." }, + { "Plugins.Widgets.TrustedShopsCustomerProtection.UserName", "Trusted Shops user name" }, + { "Plugins.Widgets.TrustedShopsCustomerProtection.UserName.Hint", "Please enter the user name provided by Trusted Shops here." }, + }; + + public override void Up() + { + } + + public override void Down() + { + } + + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + using (var scope = new DbContextScope(context, autoDetectChanges: false, validateOnSave: false)) + { + var resourceSet = context.Set(); + var allLanguages = context.Set().AsNoTracking().ToList(); + + // Accidents. + var accidents = resourceSet.Where(x => x.ResourceName == "Admin.Configuration.ActivityLog.ActivityLogType").ToList(); + if (accidents.Any()) + { + accidents.Each(x => x.ResourceName = "Admin.Configuration.ActivityLog.ActivityLogType"); + context.SaveChanges(); + } + + // Remove unused resources that could be included in the German set. + var unusedResources = resourceSet.Where(x => _unusedNames.Contains(x.ResourceName)).ToList(); + if (unusedResources.Any()) + { + resourceSet.RemoveRange(unusedResources); + context.SaveChanges(); + unusedResources.Clear(); + } + + context.DetachEntities(); + + // Remove duplicate resources. + foreach (var language in allLanguages) + { + var resources = resourceSet.Where(x => x.LanguageId == language.Id).ToList(); + var deleteResources = new List(); + var resourcesMap = new Multimap(StringComparer.OrdinalIgnoreCase); + resources.Each(x => resourcesMap.Add(x.ResourceName, x)); + + foreach (var item in resourcesMap) + { + if (item.Value.Count > 1) + { + // First is ok, rest is bad. + foreach (var resource in item.Value.OrderByDescending(x => x.IsTouched).Skip(1)) + { + deleteResources.Add(resource); + } + } + } + + if (deleteResources.Any()) + { + resourceSet.RemoveRange(deleteResources); + context.SaveChanges(); + deleteResources.Clear(); + context.DetachEntities(); + } + } + + // Remove resources that are not included in the German set. + // Unfortunately we cannot do that. We have no information about the origin of a resource. We would delete resources of other developers. + + // Add resources included in the German set but missing in the English set. + var deLanguage = allLanguages.FirstOrDefault(x => x.LanguageCulture.IsCaseInsensitiveEqual("de-DE")); + var enLanguage = allLanguages.FirstOrDefault(x => x.LanguageCulture.IsCaseInsensitiveEqual("en-US")); + if (deLanguage != null && enLanguage != null) + { + var deResources = resourceSet.AsNoTracking().Where(x => x.LanguageId == deLanguage.Id).ToList(); + + var enNames = resourceSet + .Where(x => x.LanguageId == enLanguage.Id) + .Select(x => x.ResourceName) + .Distinct() + .ToList(); + var enNamesSet = new HashSet(enNames, StringComparer.OrdinalIgnoreCase); + + foreach (var resource in deResources) + { + if (!enNames.Contains(resource.ResourceName) && _missingEnglishResources.TryGetValue(resource.ResourceName, out string value)) + { + resourceSet.Add(new LocaleStringResource + { + LanguageId = enLanguage.Id, + ResourceName = resource.ResourceName, + ResourceValue = value, + IsFromPlugin = resource.IsFromPlugin + }); + } + } + + context.SaveChanges(); + deResources.Clear(); + context.DetachEntities(); + } + } + } + } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201712081631552_Liquid.cs b/src/Libraries/SmartStore.Data/Migrations/201712081631552_Liquid.cs index 450133d11a..cb8df2f1fb 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201712081631552_Liquid.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201712081631552_Liquid.cs @@ -1,16 +1,15 @@ namespace SmartStore.Data.Migrations { - using System; - using System.Data.Entity.Migrations; - using System.Web.Hosting; - using System.Linq; - using SmartStore.Core.Data; - using SmartStore.Core.Domain.Localization; - using SmartStore.Data.Setup; - using SmartStore.Data.Utilities; - - public partial class Liquid : DbMigration, ILocaleResourcesProvider, IDataSeeder - { + using System.Data.Entity.Migrations; + using System.Linq; + using System.Web.Hosting; + using SmartStore.Core.Data; + using SmartStore.Core.Domain.Localization; + using SmartStore.Data.Setup; + using SmartStore.Data.Utilities; + + public partial class Liquid : DbMigration, ILocaleResourcesProvider, IDataSeeder + { public override void Up() { AddColumn("dbo.MessageTemplate", "To", c => c.String(nullable: false, maxLength: 500, defaultValue: " ")); @@ -21,7 +20,7 @@ public override void Up() DropColumn("dbo.QueuedEmail", "ToName"); DropColumn("dbo.QueuedEmail", "ReplyToName"); } - + public override void Down() { AddColumn("dbo.QueuedEmail", "ReplyToName", c => c.String(maxLength: 500)); @@ -33,83 +32,83 @@ public override void Down() DropColumn("dbo.MessageTemplate", "To"); } - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - context.SaveChanges(); - - if (HostingEnvironment.IsHosted && DataSettings.DatabaseIsInstalled()) - { - // Import all xml templates on disk - var converter = new MessageTemplateConverter(context); - var language = ResolveMasterLanguage(context); - converter.ImportAll(language); - - DropDefaultValueConstraint(context); - } - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.Delete( - "Admin.System.QueuedEmails.Fields.FromName", - "Admin.System.QueuedEmails.Fields.FromName.Hint", - "Admin.System.QueuedEmails.Fields.ToName", - "Admin.System.QueuedEmails.Fields.ToName.Hint"); - - builder.AddOrUpdate("Admin.System.QueuedEmails.Fields.ReplyTo", - "Reply to", - "Antwort an", - "Reply-To address of the email.", - "Antwortadresse der E-Mail."); - - builder.AddOrUpdate("Common.Error.NoMessageTemplate", - "The message template '{0}' does not exist.", - "Die Nachrichtenvorlage '{0}' existiert nicht."); - - builder.AddOrUpdate("Admin.ContentManagement.MessageTemplates.NoModelTree", - "Variables are unknown until at least one message of the current type has either been sent or previewed.", - "Variablen sind erst bekannt, wenn mind. eine Nachricht vom aktuellen Typ entweder gesendet oder getestet wurde."); - - builder.AddOrUpdate("Admin.Promotions.Campaigns.Fields.AllowedTokens", - "Allowed template variables", - "Erlaubte Template Variablen", - "Inserts the selected variable in the HTML document.", - "Fgt die gewhlte Variable in das HTML-Dokument ein."); - } - - private Language ResolveMasterLanguage(SmartObjectContext context) - { - var query = context.Set().OrderBy(x => x.DisplayOrder); - - var language = query - .Where(x => (x.UniqueSeoCode == "de" || x.UniqueSeoCode == "en") && x.Published) - .FirstOrDefault(); - - if (language == null) - { - language = query.Where(x => x.Published).FirstOrDefault(); - } - - return language; - } - - private void DropDefaultValueConstraint(SmartObjectContext context) - { - // During migration we created a new NON-Nullable column ("To") - // with a default value contraint of ' ', otherwise column creation - // would have failed. Now we need to get rid of this constraint. - - if (DataSettings.Current.IsSqlServer) - { - string sql = @"DECLARE @name nvarchar(100) + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + context.SaveChanges(); + + if (HostingEnvironment.IsHosted && DataSettings.DatabaseIsInstalled()) + { + // Import all xml templates on disk + var converter = new MessageTemplateConverter(context); + var language = ResolveMasterLanguage(context); + converter.ImportAll(language); + + DropDefaultValueConstraint(context); + } + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.Delete( + "Admin.System.QueuedEmails.Fields.FromName", + "Admin.System.QueuedEmails.Fields.FromName.Hint", + "Admin.System.QueuedEmails.Fields.ToName", + "Admin.System.QueuedEmails.Fields.ToName.Hint"); + + builder.AddOrUpdate("Admin.System.QueuedEmails.Fields.ReplyTo", + "Reply to", + "Antwort an", + "Reply-To address of the email.", + "Antwortadresse der E-Mail."); + + builder.AddOrUpdate("Common.Error.NoMessageTemplate", + "The message template '{0}' does not exist.", + "Die Nachrichtenvorlage '{0}' existiert nicht."); + + builder.AddOrUpdate("Admin.ContentManagement.MessageTemplates.NoModelTree", + "Variables are unknown until at least one message of the current type has either been sent or previewed.", + "Variablen sind erst bekannt, wenn mind. eine Nachricht vom aktuellen Typ entweder gesendet oder getestet wurde."); + + builder.AddOrUpdate("Admin.Promotions.Campaigns.Fields.AllowedTokens", + "Allowed template variables", + "Erlaubte Template Variablen", + "Inserts the selected variable in the HTML document.", + "Fgt die gewhlte Variable in das HTML-Dokument ein."); + } + + private Language ResolveMasterLanguage(SmartObjectContext context) + { + var query = context.Set().OrderBy(x => x.DisplayOrder); + + var language = query + .Where(x => (x.UniqueSeoCode == "de" || x.UniqueSeoCode == "en") && x.Published) + .FirstOrDefault(); + + if (language == null) + { + language = query.Where(x => x.Published).FirstOrDefault(); + } + + return language; + } + + private void DropDefaultValueConstraint(SmartObjectContext context) + { + // During migration we created a new NON-Nullable column ("To") + // with a default value contraint of ' ', otherwise column creation + // would have failed. Now we need to get rid of this constraint. + + if (DataSettings.Current.IsSqlServer) + { + string sql = @"DECLARE @name nvarchar(100) SELECT @name = [name] from sys.objects WHERE type = 'D' and parent_object_id = object_id('MessageTemplate') IF (@name is not null) BEGIN EXEC ('ALTER TABLE [MessageTemplate] Drop Constraint [' + @name +']') END"; - context.ExecuteSqlCommand(sql); - } - } + context.ExecuteSqlCommand(sql); + } + } - public bool RollbackOnFailure => true; - } + public bool RollbackOnFailure => true; + } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201712290151517_AddressFormat.cs b/src/Libraries/SmartStore.Data/Migrations/201712290151517_AddressFormat.cs index 038f51ae45..8dd8239740 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201712290151517_AddressFormat.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201712290151517_AddressFormat.cs @@ -1,39 +1,38 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - using SmartStore.Data.Utilities; + using SmartStore.Data.Setup; + using SmartStore.Data.Utilities; - public partial class AddressFormat : DbMigration, ILocaleResourcesProvider, IDataSeeder - { + public partial class AddressFormat : DbMigration, ILocaleResourcesProvider, IDataSeeder + { public override void Up() { AddColumn("dbo.Country", "AddressFormat", c => c.String()); } - + public override void Down() { DropColumn("dbo.Country", "AddressFormat"); } - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - DataMigrator.ImportAddressFormats(context); + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + DataMigrator.ImportAddressFormats(context); - context.SaveChanges(); - } + context.SaveChanges(); + } - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Configuration.Countries.Fields.AddressFormat", - "Address format", - "Adressenformat", - "The address format according to the countries mailing address format rules.", - "Das Addressenformat gem der Landes-Richtlinien."); - } + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("Admin.Configuration.Countries.Fields.AddressFormat", + "Address format", + "Adressenformat", + "The address format according to the countries mailing address format rules.", + "Das Addressenformat gem der Landes-Richtlinien."); + } - public bool RollbackOnFailure => false; - } + public bool RollbackOnFailure => false; + } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201802081830029_ShippingMethodMultistore.cs b/src/Libraries/SmartStore.Data/Migrations/201802081830029_ShippingMethodMultistore.cs index 92e74e27ac..fa8929783e 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201802081830029_ShippingMethodMultistore.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201802081830029_ShippingMethodMultistore.cs @@ -1,8 +1,7 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class ShippingMethodMultistore : DbMigration { public override void Up() @@ -15,18 +14,18 @@ public override void Up() AddColumn("dbo.PaymentMethod", "LimitedToStores", c => c.Boolean(nullable: false)); DropTable("dbo.ShippingMethodRestrictions"); } - + public override void Down() { CreateTable( "dbo.ShippingMethodRestrictions", c => new - { - ShippingMethod_Id = c.Int(nullable: false), - Country_Id = c.Int(nullable: false), - }) + { + 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"); CreateIndex("dbo.ShippingMethodRestrictions", "Country_Id"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201802270844034_ExportAttributeMappings.cs b/src/Libraries/SmartStore.Data/Migrations/201802270844034_ExportAttributeMappings.cs index d99106df13..38f873aac7 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201802270844034_ExportAttributeMappings.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201802270844034_ExportAttributeMappings.cs @@ -1,15 +1,14 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class ExportAttributeMappings : DbMigration { public override void Up() { AddColumn("dbo.ProductAttribute", "ExportMappings", c => c.String()); } - + public override void Down() { DropColumn("dbo.ProductAttribute", "ExportMappings"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201804060721031_Wallet.cs b/src/Libraries/SmartStore.Data/Migrations/201804060721031_Wallet.cs index 634e0f249f..5b15b5ed48 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201804060721031_Wallet.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201804060721031_Wallet.cs @@ -1,8 +1,6 @@ namespace SmartStore.Data.Migrations { using System.Data.Entity.Migrations; - using System.Web.Hosting; - using Core.Data; using Setup; public partial class Wallet : DbMigration, ILocaleResourcesProvider, IDataSeeder @@ -12,33 +10,33 @@ public override void Up() CreateTable( "dbo.WalletHistory", c => new - { - Id = c.Int(nullable: false, identity: true), - StoreId = c.Int(nullable: false), - CustomerId = c.Int(nullable: false), - OrderId = c.Int(), - Amount = c.Decimal(nullable: false, precision: 18, scale: 4), - AmountBalance = c.Decimal(nullable: false, precision: 18, scale: 4), - AmountBalancePerStore = c.Decimal(nullable: false, precision: 18, scale: 4), - CreatedOnUtc = c.DateTime(nullable: false), - Reason = c.Int(), - Message = c.String(maxLength: 1000), - AdminComment = c.String(maxLength: 4000), - }) + { + Id = c.Int(nullable: false, identity: true), + StoreId = c.Int(nullable: false), + CustomerId = c.Int(nullable: false), + OrderId = c.Int(), + Amount = c.Decimal(nullable: false, precision: 18, scale: 4), + AmountBalance = c.Decimal(nullable: false, precision: 18, scale: 4), + AmountBalancePerStore = c.Decimal(nullable: false, precision: 18, scale: 4), + CreatedOnUtc = c.DateTime(nullable: false), + Reason = c.Int(), + Message = c.String(maxLength: 1000), + AdminComment = c.String(maxLength: 4000), + }) .PrimaryKey(t => t.Id) .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) .ForeignKey("dbo.Order", t => t.OrderId) .Index(t => new { t.StoreId, t.CreatedOnUtc }, name: "IX_StoreId_CreatedOn") .Index(t => t.CustomerId) .Index(t => t.OrderId); - + AddColumn("dbo.Product", "IsSystemProduct", c => c.Boolean(nullable: false)); AddColumn("dbo.Product", "SystemName", c => c.String(maxLength: 500)); AddColumn("dbo.Order", "CreditBalance", c => c.Decimal(nullable: false, precision: 18, scale: 4)); AddColumn("dbo.Order", "RefundedCreditBalance", c => c.Decimal(nullable: false, precision: 18, scale: 4)); CreateIndex("dbo.Product", new[] { "SystemName", "IsSystemProduct" }, name: "Product_SystemName_IsSystemProduct"); } - + public override void Down() { DropForeignKey("dbo.WalletHistory", "OrderId", "dbo.Order"); @@ -54,10 +52,7 @@ public override void Down() DropTable("dbo.WalletHistory"); } - public bool RollbackOnFailure - { - get { return false; } - } + public bool RollbackOnFailure => false; public void Seed(SmartObjectContext context) { diff --git a/src/Libraries/SmartStore.Data/Migrations/201804090744324_ForceSslForAllPages.cs b/src/Libraries/SmartStore.Data/Migrations/201804090744324_ForceSslForAllPages.cs index d0835dc7e8..9d98c90284 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201804090744324_ForceSslForAllPages.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201804090744324_ForceSslForAllPages.cs @@ -16,7 +16,7 @@ public override void Up() CreateIndex("dbo.Product", new[] { "SystemName", "IsSystemProduct" }, name: "Product_SystemName_IsSystemProduct"); DropColumn("dbo.Order", "RefundedCreditBalance"); } - + public override void Down() { AddColumn("dbo.Order", "RefundedCreditBalance", c => c.Decimal(nullable: false, precision: 18, scale: 4)); @@ -26,10 +26,7 @@ public override void Down() CreateIndex("dbo.Product", new[] { "SystemName", "IsSystemProduct" }, name: "Product_SystemName_IsSystemProduct"); } - public bool RollbackOnFailure - { - get { return false; } - } + public bool RollbackOnFailure => false; public void Seed(SmartObjectContext context) { diff --git a/src/Libraries/SmartStore.Data/Migrations/201804200835273_V310Resources.cs b/src/Libraries/SmartStore.Data/Migrations/201804200835273_V310Resources.cs index ff3a2d3b7b..346a3c8e6c 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201804200835273_V310Resources.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201804200835273_V310Resources.cs @@ -1,394 +1,390 @@ namespace SmartStore.Data.Migrations { - using SmartStore.Core.Domain.Configuration; - using SmartStore.Core.Domain.Customers; - using SmartStore.Core.Domain.Media; - using SmartStore.Data.Setup; - using SmartStore.Utilities; - using System; using System.Data.Entity.Migrations; - using System.Linq; - - public partial class V310Resources : DbMigration, ILocaleResourcesProvider, IDataSeeder - { + using System.Linq; + using SmartStore.Core.Domain.Configuration; + using SmartStore.Core.Domain.Customers; + using SmartStore.Core.Domain.Media; + using SmartStore.Data.Setup; + using SmartStore.Utilities; + + public partial class V310Resources : DbMigration, ILocaleResourcesProvider, IDataSeeder + { public override void Up() { } - + public override void Down() { } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - - MigrateSettings(context); - - context.SaveChanges(); - } - - public void MigrateSettings(SmartObjectContext context) - { - // Change MediaSettings.MaximumImageSize to 2048 - var name = TypeHelper.NameOf(y => y.MaximumImageSize, true); - var setting = context.Set().FirstOrDefault(x => x.Name == name); - if (setting != null && setting.Value.Convert() < 2048) - { - setting.Value = "2048"; - } - - // Change MediaSettings.AvatarPictureSize to 250 - name = TypeHelper.NameOf(y => y.AvatarPictureSize, true); - setting = context.Set().FirstOrDefault(x => x.Name == name); - if (setting != null && setting.Value.Convert() < 250) - { - setting.Value = "250"; - } - - // Change MediaSettings.AvatarMaximumSizeBytes to 512000 (500 KB) - name = TypeHelper.NameOf(y => y.AvatarMaximumSizeBytes, true); - setting = context.Set().FirstOrDefault(x => x.Name == name); - if (setting != null && setting.Value.Convert() < 512000) - { - setting.Value = "512000"; - } - - // Delete MessageTemplatesSettings - var settings = context.Set(); - var caseInvariantReplacementSetting = settings.FirstOrDefault(x => x.Name == "MessageTemplatesSettings.CaseInvariantReplacement"); - var color1Setting = settings.FirstOrDefault(x => x.Name == "MessageTemplatesSettings.Color1"); - var color2Setting = settings.FirstOrDefault(x => x.Name == "MessageTemplatesSettings.Color2"); - var color3Setting = settings.FirstOrDefault(x => x.Name == "MessageTemplatesSettings.Color3"); - - if (caseInvariantReplacementSetting != null) settings.Remove(caseInvariantReplacementSetting); - if (color1Setting != null) settings.Remove(color1Setting); - if (color2Setting != null) settings.Remove(color2Setting); - if (color3Setting != null) settings.Remove(color3Setting); - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Orders.Shipment", "Shipment", "Lieferung"); - builder.AddOrUpdate("Admin.Order", "Order", "Auftrag"); - - builder.AddOrUpdate("Admin.Order.ViaShippingMethod", "via {0}", "via {0}"); - builder.AddOrUpdate("Admin.Order.WithPaymentMethod", "with {0}", "per {0}"); - builder.AddOrUpdate("Admin.Order.FromStore", "from {0}", "von {0}"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.MaxItemsToDisplayInCatalogMenu", - "Max items to display in catalog menu", - "Maximale Anzahl von Elementen im Katalogmen", - "Defines the maximum number of top level items to be displayed in the main catalog menu. All menu items which are exceeding this limit will be placed in a new dropdown menu item.", - "Legt die maximale Anzahl von Menu-Eintrgen der obersten Hierarchie fest, die im Katalogmen angezeigt werden. Alle weiteren Menu-Eintrge werden innerhalb eines neuen Dropdownmenus ausgegeben."); - - builder.AddOrUpdate("CatalogMenu.MoreLink", "More", "Mehr"); - - builder.AddOrUpdate("Admin.CatalogSettings.Homepage", "Homepage", "Homepage"); - builder.AddOrUpdate("Admin.CatalogSettings.ProductDisplay", "Product display", "Produktdarstellung"); - builder.AddOrUpdate("Admin.CatalogSettings.Prices", "Prices", "Preise"); - builder.AddOrUpdate("Admin.CatalogSettings.CompareProducts", "Compare products", "Produktvergleich"); - - builder.AddOrUpdate("Footer.Service.Mobile", "Service", "Service, Versand & Zahlung"); - builder.AddOrUpdate("Footer.Company.Mobile", "Company", "Firma, Impressum & Datenschutz"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Search.Facets.FacetSorting.LabelAsc", - "Displayed Name: A to Z", - "Angezeigter Name: A bis Z"); - - builder.AddOrUpdate("Admin.Catalog.Products.Copy.NumberOfCopies", - "Number of copies", - "Anzahl an Kopien", - "Defines the number of copies to be created.", - "Legt die Anzahl der anzulegenden Kopien fest."); - - builder.AddOrUpdate("Admin.Configuration.Languages.OfType", - "of type \"{0}\"", - "vom Typ \"{0}\""); - - builder.AddOrUpdate("Admin.Configuration.Languages.CheckAvailableLanguagesFailed", - "An error occurred while checking for other available languages.", - "Bei der Suche nach weiteren verfgbaren Sprachen trat ein Fehler auf."); - - builder.AddOrUpdate("Admin.Configuration.Languages.NoAvailableLanguagesFound", - "There were no other available languages found for version {0}. On translate.smartstore.com you will find more details about available resources.", - "Es wurden keine weiteren verfgbaren Sprachen fr Version {0} gefunden. Auf translate.smartstore.com finden Sie weitere Details zu verfgbaren Ressourcen."); - - builder.AddOrUpdate("Admin.Configuration.Languages.InstalledLanguages", - "Installed Languages", - "Installierte Sprachen"); - builder.AddOrUpdate("Admin.Configuration.Languages.AvailableLanguages", - "Available Languages", - "Verfgbare Sprachen"); - - 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 verfgbaren Ressourcen."); - - builder.AddOrUpdate("Common.Translated", - "Translated", - "bersetzt"); - builder.AddOrUpdate("Admin.Configuration.Languages.TranslatedPercentage", - "{0}% translated", - "{0}% bersetzt"); - builder.AddOrUpdate("Admin.Configuration.Languages.TranslatedPercentageAtLastImport", - "{0}% at the last import", - "{0}% beim letzten Import"); - - builder.AddOrUpdate("Admin.Configuration.Languages.NumberOfTranslatedResources", - "{0} of {1}", - "{0} von {1}"); - - builder.AddOrUpdate("Admin.Configuration.Languages.DownloadingResources", - "Loading ressources", - "Lade Ressourcen"); - builder.AddOrUpdate("Admin.Configuration.Languages.ImportResources", - "Import resources", - "Importiere Ressourcen"); - - builder.AddOrUpdate("Admin.Configuration.Languages.OnePublishedLanguageRequired", - "At least one published language is required.", - "Mindestens eine verffentlichte Sprache ist erforderlich."); - - builder.AddOrUpdate("Admin.Configuration.Languages.Fields.AvailableLanguageSetId", - "Available Languages", - "Verfgbare Sprachen", - "Specifies the available language whose localized resources are to be imported.", - "Legt die verfgbare Sprache fest, deren lokalisierte Ressourcen importiert werden sollen."); - - builder.AddOrUpdate("Admin.Configuration.Languages.UploadFileOrSelectLanguage", - "Please upload an import file or select an available language whose resources are to be imported.", - "Bitte laden Sie eine Importdatei hoch oder whlen Sie eine verfgbare Sprache, deren Ressourcen importiert werden sollen."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Shipping.ChargeOnlyHighestProductShippingSurcharge", - "Charge the highest shipping surcharge only", - "Nur den hchsten Transportzuschlag berechnen", - "Specifies whether to charge only the highest additional shipping surcharge of products.", - "Bestimmt ob bei der Berechnung der Versandkosten nur der hchste Transportzuschlag von Produkten bercksichtigt wird."); - - builder.AddOrUpdate("Order.OrderDetails") - .Value("en", "Order Details"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Media.AutoGenerateAbsoluteUrls", - "Generate absolute URLs", - "Absolute URLs erzeugen", - "Generates absolute URLs for media files by prepending the current host name (e.g. http://myshop.com/media/image/1.jpg instead of /media/image/1.jpg). Has no effect if a CDN URL has been applied to the store.", - "Erzeugt absolute URLs fr Mediendateien, indem der aktuelle Hostname vorangestellt wird (z.B. http://meinshop.de/media/image/1.jpg statt /media/image/1.jpg). Hat keine Auswirkung, wenn fr den Store eine CDN-URL eingerichtet wurde."); - - builder.AddOrUpdate("Admin.Configuration.Settings.Search.SearchFieldsNote", - "The Name, SKU and Short Description fields can be searched in the standard search. Other fields require a search plugin such as the MegaSearch plugin from Premium Edition.", - "In der Standardsuche knnen die Felder Name, SKU und Kurzbeschreibung durchsucht werden. Fr weitere Felder ist ein Such-Plugin wie etwa das MegaSearch-Plugin aus der Premium Edition notwendig."); - - builder.AddOrUpdate("Admin.DataExchange.Import.FolderName", "Folder path", "Ordnerpfad"); - - builder.AddOrUpdate("Admin.MessageTemplate.Preview.From", "From", "Von"); - builder.AddOrUpdate("Admin.MessageTemplate.Preview.To", "To", "An"); - builder.AddOrUpdate("Admin.MessageTemplate.Preview.ReplyTo", "Reply To", "Antwort an"); - builder.AddOrUpdate("Admin.MessageTemplate.Preview.SendTestMail", "Test-E-mail to...", "Test E-Mail an..."); - builder.AddOrUpdate("Admin.MessageTemplate.Preview.TestMailSent", "E-mail has been sent.", "E-Mail gesendet."); - builder.AddOrUpdate("Admin.MessageTemplate.Preview.NoBody", - "The generated preview file seems to have expired. Please reload the page.", - "Die generierte Vorschaudatei scheint abgelaufen zu sein. Laden Sie die Seite bitte neu."); - - builder.AddOrUpdate("Admin.ContentManagement.MessageTemplates.Preview.SuccessfullySent", - "The email has been sent successfully.", - "Die E-Mail wurde erfolgreich versendet."); - - builder.AddOrUpdate("Admin.ContentManagement.MessageTemplates.SuccessfullyCopied", - "The message template has been copied successfully.", - "Die Nachrichtenvorlage wurde erfolgreich kopiert."); - - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportEntityType.ShoppingCartItem", "Shopping Cart", "Warenkorb"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Orders.ShoppingCartType.ShoppingCart", "Shopping Cart", "Warenkorb"); - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Orders.ShoppingCartType.Wishlist", "Wishlist", "Wunschliste"); - - builder.AddOrUpdate("Admin.DataExchange.Export.Projection.NoBundleProducts", - "Do not export bundled products", - "Keine Produkt-Bundle exportieren", - "Specifies whether to export bundled products. If this option is activated, then the associated bundle items will be exported.", - "Legt fest, ob Produkt-Bundle exportiert werden sollen. Ist diese Option aktiviert, so werden die zum Bundle gehrenden Produkte (Bundle-Bestandteile) exportiert."); - - builder.AddOrUpdate("Admin.DataExchange.Export.Filter.ShoppingCartTypeId", - "Shopping cart type", - "Warenkorbtyp", - "Filter by shopping cart type.", - "Nach Warenkorbtyp filtern."); - - builder.AddOrUpdate("Common.CustomerId", "Customer ID", "Kunden ID"); - - builder.AddOrUpdate("Account.AccountActivation.InvalidEmailOrToken", - "Unknown email or token. Please register again.", - "Unbekannte E-Mail oder Token. Bitte fhren Sie die Registrierung erneut durch."); - - builder.AddOrUpdate("Account.PasswordRecoveryConfirm.InvalidEmailOrToken", - "Unknown email or token. Please click \"Forgot password\" again, if you want to renew your password.", - "Unbekannte E-Mail oder Token. Klicken Sie bitte erneut \"Passwort vergessen\", falls Sie Ihr Passwort erneuern mchten."); - - builder.Delete("Account.PasswordRecoveryConfirm.InvalidEmail"); - builder.Delete("Account.PasswordRecoveryConfirm.InvalidToken"); - - builder.AddOrUpdate("Admin.Common.Acl.SubjectTo", - "Restrict access", - "Zugriff einschrnken", - "Determines whether this entity is subject to access restrictions (no = no restriction, yes = accessible only for selected customer groups)", - "Legt fest, ob dieser Datensatz Zugriffsbeschrnkungen unterliegt (Nein = keine Beschrnkung, Ja = zugnglich nur fr gewhlte Kundengruppen)"); - - builder.AddOrUpdate("Admin.Common.Acl.AvailableFor", - "Customer roles", - "Kundengruppen", - "Select customer roles who can access the entity. For all inactive roles, this record is hidden.", - "Whlen Sie Kundengruppen, die auf den Datensatz zugreifen knnen. Bei allen nicht aktivierten Gruppen wird dieser Datensatz ausgeblendet."); - - builder.Delete( - "Admin.Catalog.Categories.Fields.SubjectToAcl", - "Admin.Catalog.Categories.Fields.AclCustomerRoles", - "Admin.Catalog.Products.Fields.SubjectToAcl", - "Admin.Catalog.Products.Fields.AclCustomerRoles", - "Common.Options.Count"); - - builder.AddOrUpdate("Admin.Common.ApplyFilter", "Apply filter", "Filter anwenden"); - builder.AddOrUpdate("Time.Milliseconds", "Milliseconds", "Millisekunden"); - builder.AddOrUpdate("Common.Pixel", "Pixel", "Pixel"); - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.ShowPlaceholder", "Show placeholder", "Zeige Platzhalter"); - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.HidePlaceholder", "Hide placeholder", "Verberge Platzhalter"); - builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.UpdateExampleFileName", "Update example", "Aktualisiere Beispiel"); - - builder.AddOrUpdate("Admin.Configuration.Themes.AvailableDesktopThemes", "Installed themes", "Installierte Themes"); - - builder.AddOrUpdate("Admin.Catalog.Products.List.GoDirectlyToSku", "Find by SKU", "Nach SKU suchen"); - builder.AddOrUpdate("Admin.Orders.List.GoDirectlyToNumber", "Find by order id", "Nach Auftragsnummer suchen"); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.StoreLastIpAddress", - "Store IP address", - "IP-Adresse speichern", - "Specifies whether to store the IP address in the customer data set.", - "Legt fest, ob die IP-Adresse im Kundendatensatz gespeichert werden soll."); - - builder.AddOrUpdate("Admin.Orders.Info", "General", "Allgemein"); - builder.AddOrUpdate("Admin.Orders.BillingAndShipment", "Billing & Shipping", "Rechnung & Versand"); - builder.AddOrUpdate("Admin.Orders.Fields.ShippingAddress.ViewOnGoogleMaps", "View on Google Maps", "Auf Google Maps ansehen"); - - builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.SocialSettings.InstagramLink", - "Instagram Link", - "Instagram Link", - "Leave this field empty if the Instagram link should not be shown", - "Lassen Sie dieses Feld leer, wenn der Instagram Link nicht angezeigt werden soll"); - - builder.AddOrUpdate("Common.License", "License", "Lizenz"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Payments.CapturePaymentReason.OrderShipped", - "The order has been marked as shipped", - "Der Auftrag wurde als versendet markiert"); - - builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Payments.CapturePaymentReason.OrderDelivered", - "The order has been marked as delivered", - "Der Auftrag wurde als ausgeliefert markiert"); - - builder.AddOrUpdate("Admin.Configuration.Settings.Payment.CapturePaymentReason", - "Capture payment amount when", - "Zahlungsbetrag einziehen, wenn", - "Specifies the event when the payment amount is automatically captured. The selected payment method must support capturing for this.", - "Legt das Ereignis fest, zu dem der Zahlunsgbetrag automatisch eingezogen wird. Die gewhlte Zahlart muss hierfr Buchungen untersttzen."); - - #region taken from V22Final, because they were never added yet - - builder.AddOrUpdate("Common.Next", - "Next", - "Weiter"); - builder.AddOrUpdate("Admin.Common.BackToConfiguration", - "Back to configuration", - "Zurck zur Konfiguration"); - builder.AddOrUpdate("Admin.Common.UploadFileSucceeded", - "The file has been successfully uploaded.", - "Die Datei wurde erfolgreich hochgeladen."); - builder.AddOrUpdate("Admin.Common.UploadFileFailed", - "The upload has failed.", - "Der Upload ist leider fehlgeschlagen."); - builder.AddOrUpdate("Admin.Common.ImportAll", - "Import all", - "Alle importieren"); - builder.AddOrUpdate("Admin.Common.ImportSelected", - "Import selected", - "Ausgewhlte importieren"); - builder.AddOrUpdate("Admin.Common.UnknownError", - "An unknown error has occurred.", - "Es ist ein unbekannter Fehler aufgetreten."); - builder.AddOrUpdate("Plugins.Feed.FreeShippingThreshold", - "Free shipping threshold", - "Kostenloser Versand ab", - "Amount as from shipping is free.", - "Betrag, ab dem keine Versandkosten anfallen."); - - #endregion - - builder.AddOrUpdate("Admin.Product.Picture.Added", - "The picture has successfully been added", - "Das Bild wurde erfolgreich zugefgt"); - - builder.AddOrUpdate("HtmlEditor.ClickToEdit", "Click to edit HTML...", "Hier klicken, um HTML zu editieren..."); - - builder.AddOrUpdate("Admin.Catalog.Attributes.ProductAttributes.Fields.ExportMappings.Note", - "Define mappings of attribute values to export fields according to the pattern <Format prefix>:<Export field name>. Example: gmc:color exports the attribute values for colors to the field color during the Google Merchant Center Export. The mappings are only effective when exporting attribute combinations.", - "Legen Sie Zuordnungen von Attributwerten zu Exportfeldern nach dem Muster <Formatprfix>:<Export-Feldname> fest. Beispiel: gmc:color exportiert beim Google Merchant Center Export die Attributwerte fr Farben in das Feld color. Die Zuordnungen sind nur beim Export von Attributkombinationen wirksam."); - - builder.AddOrUpdate("Admin.Catalog.Attributes.ProductAttributes.Fields.ExportMappings", - "Mappings to export fields", - "Zuordnungen zu Exportfeldern", - "Allows to map attribute values to export fields. Each entry has to be entered in a new line.", - "Ermglicht die Zuordnung von Attributwerten zu Exportfeldern. Jeder Eintrag muss in einer neuen Zeile erfolgen."); - - builder.AddOrUpdate("Admin.Configuration.Payment.Methods.AdditionalFee", - "Additional fee", - "Zustzliche Gebhr", - "Specifies an additional fee to be charged to the customer for using the payment method.", - "Legt eine zustzliche Gebhr fest, die dem Kunden fr die Inanspruchnahme der Zahlart berechnet wird."); - - builder.AddOrUpdate("Admin.Configuration.Payment.Methods.AdditionalFeePercentage", - "Additional fee percentage", - "Zustzliche Gebhr prozentual", - "Specifies whether the additional fee should be calculated as a percentage. A fixed value is used if this option is disabled.", - "Legt fest, ob die zustzliche Gebhr prozentual berechnet werden soll. Es wird ein fester Wert verwendet, falls diese Option deaktiviert ist."); - - builder.Delete("Common.Buttons.Default"); - builder.AddOrUpdate("Common.Buttons.Secondary", "Secondary", "Secondary"); - builder.AddOrUpdate("Common.Buttons.Light", "Light", "Light"); - builder.AddOrUpdate("Common.Buttons.Dark", "Dark", "Dark"); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.AddressFormFields.CountryRequired", - "'Country' required", - "Die Eingabe eines Landes ist erforderlich", - "Check the box if 'Country' is required.", - "Legt fest, ob die Eingabe eines Landes erforderlich ist."); - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.AddressFormFields.StateProvinceRequired", - "'State/province' required", - "Die Eingabe eines Bundeslandes ist erforderlich", - "Check the box if 'State/province' is required.", - "Legt fest, ob die Eingabe eines Bundeslandes erforderlich ist."); - - builder.AddOrUpdate("Address.Fields.StateProvince.Required", "State is required.", "Bundesland wird bentigt"); - - builder.AddOrUpdate("Common.Columns", "Columns", "Spalten"); - builder.AddOrUpdate("Common.Mru", "Recently", "Zuletzt"); - - builder.AddOrUpdate("Admin.WidgetZones.UserDefined", "User-defined", "Benutzerdefiniert"); - - builder.AddOrUpdate("Admin.Configuration.ManageLanguages", "Manage languages", "Sprachen verwalten"); - - builder.AddOrUpdate("Admin.Customers.Customers.Info", "General", "Allgemein"); - builder.AddOrUpdate("Admin.Customers.Customers.Impersonate", "Impersonate", "Imitieren"); - builder.AddOrUpdate("Admin.Customers.Customers.CurrentCart", "Current cart", "Aktueller Warenkorb"); - - builder.AddOrUpdate("Admin.ContentManagement.Topics.CannotBeDeleted", - "This topic is needed by your Shop and can therefore not be deleted.", - "Diese Seite wird von Ihrem Shop bentigt und kann daher nicht gelscht werden."); - } - } + + public bool RollbackOnFailure => false; + + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + + MigrateSettings(context); + + context.SaveChanges(); + } + + public void MigrateSettings(SmartObjectContext context) + { + // Change MediaSettings.MaximumImageSize to 2048 + var name = TypeHelper.NameOf(y => y.MaximumImageSize, true); + var setting = context.Set().FirstOrDefault(x => x.Name == name); + if (setting != null && setting.Value.Convert() < 2048) + { + setting.Value = "2048"; + } + + // Change MediaSettings.AvatarPictureSize to 250 + name = TypeHelper.NameOf(y => y.AvatarPictureSize, true); + setting = context.Set().FirstOrDefault(x => x.Name == name); + if (setting != null && setting.Value.Convert() < 250) + { + setting.Value = "250"; + } + + // Change MediaSettings.AvatarMaximumSizeBytes to 512000 (500 KB) + name = TypeHelper.NameOf(y => y.AvatarMaximumSizeBytes, true); + setting = context.Set().FirstOrDefault(x => x.Name == name); + if (setting != null && setting.Value.Convert() < 512000) + { + setting.Value = "512000"; + } + + // Delete MessageTemplatesSettings + var settings = context.Set(); + var caseInvariantReplacementSetting = settings.FirstOrDefault(x => x.Name == "MessageTemplatesSettings.CaseInvariantReplacement"); + var color1Setting = settings.FirstOrDefault(x => x.Name == "MessageTemplatesSettings.Color1"); + var color2Setting = settings.FirstOrDefault(x => x.Name == "MessageTemplatesSettings.Color2"); + var color3Setting = settings.FirstOrDefault(x => x.Name == "MessageTemplatesSettings.Color3"); + + if (caseInvariantReplacementSetting != null) settings.Remove(caseInvariantReplacementSetting); + if (color1Setting != null) settings.Remove(color1Setting); + if (color2Setting != null) settings.Remove(color2Setting); + if (color3Setting != null) settings.Remove(color3Setting); + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("Admin.Orders.Shipment", "Shipment", "Lieferung"); + builder.AddOrUpdate("Admin.Order", "Order", "Auftrag"); + + builder.AddOrUpdate("Admin.Order.ViaShippingMethod", "via {0}", "via {0}"); + builder.AddOrUpdate("Admin.Order.WithPaymentMethod", "with {0}", "per {0}"); + builder.AddOrUpdate("Admin.Order.FromStore", "from {0}", "von {0}"); + + builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.MaxItemsToDisplayInCatalogMenu", + "Max items to display in catalog menu", + "Maximale Anzahl von Elementen im Katalogmen", + "Defines the maximum number of top level items to be displayed in the main catalog menu. All menu items which are exceeding this limit will be placed in a new dropdown menu item.", + "Legt die maximale Anzahl von Menu-Eintrgen der obersten Hierarchie fest, die im Katalogmen angezeigt werden. Alle weiteren Menu-Eintrge werden innerhalb eines neuen Dropdownmenus ausgegeben."); + + builder.AddOrUpdate("CatalogMenu.MoreLink", "More", "Mehr"); + + builder.AddOrUpdate("Admin.CatalogSettings.Homepage", "Homepage", "Homepage"); + builder.AddOrUpdate("Admin.CatalogSettings.ProductDisplay", "Product display", "Produktdarstellung"); + builder.AddOrUpdate("Admin.CatalogSettings.Prices", "Prices", "Preise"); + builder.AddOrUpdate("Admin.CatalogSettings.CompareProducts", "Compare products", "Produktvergleich"); + + builder.AddOrUpdate("Footer.Service.Mobile", "Service", "Service, Versand & Zahlung"); + builder.AddOrUpdate("Footer.Company.Mobile", "Company", "Firma, Impressum & Datenschutz"); + + builder.AddOrUpdate("Enums.SmartStore.Core.Search.Facets.FacetSorting.LabelAsc", + "Displayed Name: A to Z", + "Angezeigter Name: A bis Z"); + + builder.AddOrUpdate("Admin.Catalog.Products.Copy.NumberOfCopies", + "Number of copies", + "Anzahl an Kopien", + "Defines the number of copies to be created.", + "Legt die Anzahl der anzulegenden Kopien fest."); + + builder.AddOrUpdate("Admin.Configuration.Languages.OfType", + "of type \"{0}\"", + "vom Typ \"{0}\""); + + builder.AddOrUpdate("Admin.Configuration.Languages.CheckAvailableLanguagesFailed", + "An error occurred while checking for other available languages.", + "Bei der Suche nach weiteren verfgbaren Sprachen trat ein Fehler auf."); + + builder.AddOrUpdate("Admin.Configuration.Languages.NoAvailableLanguagesFound", + "There were no other available languages found for version {0}. On translate.smartstore.com you will find more details about available resources.", + "Es wurden keine weiteren verfgbaren Sprachen fr Version {0} gefunden. Auf translate.smartstore.com finden Sie weitere Details zu verfgbaren Ressourcen."); + + builder.AddOrUpdate("Admin.Configuration.Languages.InstalledLanguages", + "Installed Languages", + "Installierte Sprachen"); + builder.AddOrUpdate("Admin.Configuration.Languages.AvailableLanguages", + "Available Languages", + "Verfgbare Sprachen"); + + 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 verfgbaren Ressourcen."); + + builder.AddOrUpdate("Common.Translated", + "Translated", + "bersetzt"); + builder.AddOrUpdate("Admin.Configuration.Languages.TranslatedPercentage", + "{0}% translated", + "{0}% bersetzt"); + builder.AddOrUpdate("Admin.Configuration.Languages.TranslatedPercentageAtLastImport", + "{0}% at the last import", + "{0}% beim letzten Import"); + + builder.AddOrUpdate("Admin.Configuration.Languages.NumberOfTranslatedResources", + "{0} of {1}", + "{0} von {1}"); + + builder.AddOrUpdate("Admin.Configuration.Languages.DownloadingResources", + "Loading ressources", + "Lade Ressourcen"); + builder.AddOrUpdate("Admin.Configuration.Languages.ImportResources", + "Import resources", + "Importiere Ressourcen"); + + builder.AddOrUpdate("Admin.Configuration.Languages.OnePublishedLanguageRequired", + "At least one published language is required.", + "Mindestens eine verffentlichte Sprache ist erforderlich."); + + builder.AddOrUpdate("Admin.Configuration.Languages.Fields.AvailableLanguageSetId", + "Available Languages", + "Verfgbare Sprachen", + "Specifies the available language whose localized resources are to be imported.", + "Legt die verfgbare Sprache fest, deren lokalisierte Ressourcen importiert werden sollen."); + + builder.AddOrUpdate("Admin.Configuration.Languages.UploadFileOrSelectLanguage", + "Please upload an import file or select an available language whose resources are to be imported.", + "Bitte laden Sie eine Importdatei hoch oder whlen Sie eine verfgbare Sprache, deren Ressourcen importiert werden sollen."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Shipping.ChargeOnlyHighestProductShippingSurcharge", + "Charge the highest shipping surcharge only", + "Nur den hchsten Transportzuschlag berechnen", + "Specifies whether to charge only the highest additional shipping surcharge of products.", + "Bestimmt ob bei der Berechnung der Versandkosten nur der hchste Transportzuschlag von Produkten bercksichtigt wird."); + + builder.AddOrUpdate("Order.OrderDetails") + .Value("en", "Order Details"); + + builder.AddOrUpdate("Admin.Configuration.Settings.Media.AutoGenerateAbsoluteUrls", + "Generate absolute URLs", + "Absolute URLs erzeugen", + "Generates absolute URLs for media files by prepending the current host name (e.g. http://myshop.com/media/image/1.jpg instead of /media/image/1.jpg). Has no effect if a CDN URL has been applied to the store.", + "Erzeugt absolute URLs fr Mediendateien, indem der aktuelle Hostname vorangestellt wird (z.B. http://meinshop.de/media/image/1.jpg statt /media/image/1.jpg). Hat keine Auswirkung, wenn fr den Store eine CDN-URL eingerichtet wurde."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Search.SearchFieldsNote", + "The Name, SKU and Short Description fields can be searched in the standard search. Other fields require a search plugin such as the MegaSearch plugin from Premium Edition.", + "In der Standardsuche knnen die Felder Name, SKU und Kurzbeschreibung durchsucht werden. Fr weitere Felder ist ein Such-Plugin wie etwa das MegaSearch-Plugin aus der Premium Edition notwendig."); + + builder.AddOrUpdate("Admin.DataExchange.Import.FolderName", "Folder path", "Ordnerpfad"); + + builder.AddOrUpdate("Admin.MessageTemplate.Preview.From", "From", "Von"); + builder.AddOrUpdate("Admin.MessageTemplate.Preview.To", "To", "An"); + builder.AddOrUpdate("Admin.MessageTemplate.Preview.ReplyTo", "Reply To", "Antwort an"); + builder.AddOrUpdate("Admin.MessageTemplate.Preview.SendTestMail", "Test-E-mail to...", "Test E-Mail an..."); + builder.AddOrUpdate("Admin.MessageTemplate.Preview.TestMailSent", "E-mail has been sent.", "E-Mail gesendet."); + builder.AddOrUpdate("Admin.MessageTemplate.Preview.NoBody", + "The generated preview file seems to have expired. Please reload the page.", + "Die generierte Vorschaudatei scheint abgelaufen zu sein. Laden Sie die Seite bitte neu."); + + builder.AddOrUpdate("Admin.ContentManagement.MessageTemplates.Preview.SuccessfullySent", + "The email has been sent successfully.", + "Die E-Mail wurde erfolgreich versendet."); + + builder.AddOrUpdate("Admin.ContentManagement.MessageTemplates.SuccessfullyCopied", + "The message template has been copied successfully.", + "Die Nachrichtenvorlage wurde erfolgreich kopiert."); + + + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.DataExchange.ExportEntityType.ShoppingCartItem", "Shopping Cart", "Warenkorb"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Orders.ShoppingCartType.ShoppingCart", "Shopping Cart", "Warenkorb"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Orders.ShoppingCartType.Wishlist", "Wishlist", "Wunschliste"); + + builder.AddOrUpdate("Admin.DataExchange.Export.Projection.NoBundleProducts", + "Do not export bundled products", + "Keine Produkt-Bundle exportieren", + "Specifies whether to export bundled products. If this option is activated, then the associated bundle items will be exported.", + "Legt fest, ob Produkt-Bundle exportiert werden sollen. Ist diese Option aktiviert, so werden die zum Bundle gehrenden Produkte (Bundle-Bestandteile) exportiert."); + + builder.AddOrUpdate("Admin.DataExchange.Export.Filter.ShoppingCartTypeId", + "Shopping cart type", + "Warenkorbtyp", + "Filter by shopping cart type.", + "Nach Warenkorbtyp filtern."); + + builder.AddOrUpdate("Common.CustomerId", "Customer ID", "Kunden ID"); + + builder.AddOrUpdate("Account.AccountActivation.InvalidEmailOrToken", + "Unknown email or token. Please register again.", + "Unbekannte E-Mail oder Token. Bitte fhren Sie die Registrierung erneut durch."); + + builder.AddOrUpdate("Account.PasswordRecoveryConfirm.InvalidEmailOrToken", + "Unknown email or token. Please click \"Forgot password\" again, if you want to renew your password.", + "Unbekannte E-Mail oder Token. Klicken Sie bitte erneut \"Passwort vergessen\", falls Sie Ihr Passwort erneuern mchten."); + + builder.Delete("Account.PasswordRecoveryConfirm.InvalidEmail"); + builder.Delete("Account.PasswordRecoveryConfirm.InvalidToken"); + + builder.AddOrUpdate("Admin.Common.Acl.SubjectTo", + "Restrict access", + "Zugriff einschrnken", + "Determines whether this entity is subject to access restrictions (no = no restriction, yes = accessible only for selected customer groups)", + "Legt fest, ob dieser Datensatz Zugriffsbeschrnkungen unterliegt (Nein = keine Beschrnkung, Ja = zugnglich nur fr gewhlte Kundengruppen)"); + + builder.AddOrUpdate("Admin.Common.Acl.AvailableFor", + "Customer roles", + "Kundengruppen", + "Select customer roles who can access the entity. For all inactive roles, this record is hidden.", + "Whlen Sie Kundengruppen, die auf den Datensatz zugreifen knnen. Bei allen nicht aktivierten Gruppen wird dieser Datensatz ausgeblendet."); + + builder.Delete( + "Admin.Catalog.Categories.Fields.SubjectToAcl", + "Admin.Catalog.Categories.Fields.AclCustomerRoles", + "Admin.Catalog.Products.Fields.SubjectToAcl", + "Admin.Catalog.Products.Fields.AclCustomerRoles", + "Common.Options.Count"); + + builder.AddOrUpdate("Admin.Common.ApplyFilter", "Apply filter", "Filter anwenden"); + builder.AddOrUpdate("Time.Milliseconds", "Milliseconds", "Millisekunden"); + builder.AddOrUpdate("Common.Pixel", "Pixel", "Pixel"); + builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.ShowPlaceholder", "Show placeholder", "Zeige Platzhalter"); + builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.HidePlaceholder", "Hide placeholder", "Verberge Platzhalter"); + builder.AddOrUpdate("Admin.DataExchange.Export.Deployment.UpdateExampleFileName", "Update example", "Aktualisiere Beispiel"); + + builder.AddOrUpdate("Admin.Configuration.Themes.AvailableDesktopThemes", "Installed themes", "Installierte Themes"); + + builder.AddOrUpdate("Admin.Catalog.Products.List.GoDirectlyToSku", "Find by SKU", "Nach SKU suchen"); + builder.AddOrUpdate("Admin.Orders.List.GoDirectlyToNumber", "Find by order id", "Nach Auftragsnummer suchen"); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.StoreLastIpAddress", + "Store IP address", + "IP-Adresse speichern", + "Specifies whether to store the IP address in the customer data set.", + "Legt fest, ob die IP-Adresse im Kundendatensatz gespeichert werden soll."); + + builder.AddOrUpdate("Admin.Orders.Info", "General", "Allgemein"); + builder.AddOrUpdate("Admin.Orders.BillingAndShipment", "Billing & Shipping", "Rechnung & Versand"); + builder.AddOrUpdate("Admin.Orders.Fields.ShippingAddress.ViewOnGoogleMaps", "View on Google Maps", "Auf Google Maps ansehen"); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.SocialSettings.InstagramLink", + "Instagram Link", + "Instagram Link", + "Leave this field empty if the Instagram link should not be shown", + "Lassen Sie dieses Feld leer, wenn der Instagram Link nicht angezeigt werden soll"); + + builder.AddOrUpdate("Common.License", "License", "Lizenz"); + + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Payments.CapturePaymentReason.OrderShipped", + "The order has been marked as shipped", + "Der Auftrag wurde als versendet markiert"); + + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Payments.CapturePaymentReason.OrderDelivered", + "The order has been marked as delivered", + "Der Auftrag wurde als ausgeliefert markiert"); + + builder.AddOrUpdate("Admin.Configuration.Settings.Payment.CapturePaymentReason", + "Capture payment amount when", + "Zahlungsbetrag einziehen, wenn", + "Specifies the event when the payment amount is automatically captured. The selected payment method must support capturing for this.", + "Legt das Ereignis fest, zu dem der Zahlunsgbetrag automatisch eingezogen wird. Die gewhlte Zahlart muss hierfr Buchungen untersttzen."); + + #region taken from V22Final, because they were never added yet + + builder.AddOrUpdate("Common.Next", + "Next", + "Weiter"); + builder.AddOrUpdate("Admin.Common.BackToConfiguration", + "Back to configuration", + "Zurck zur Konfiguration"); + builder.AddOrUpdate("Admin.Common.UploadFileSucceeded", + "The file has been successfully uploaded.", + "Die Datei wurde erfolgreich hochgeladen."); + builder.AddOrUpdate("Admin.Common.UploadFileFailed", + "The upload has failed.", + "Der Upload ist leider fehlgeschlagen."); + builder.AddOrUpdate("Admin.Common.ImportAll", + "Import all", + "Alle importieren"); + builder.AddOrUpdate("Admin.Common.ImportSelected", + "Import selected", + "Ausgewhlte importieren"); + builder.AddOrUpdate("Admin.Common.UnknownError", + "An unknown error has occurred.", + "Es ist ein unbekannter Fehler aufgetreten."); + builder.AddOrUpdate("Plugins.Feed.FreeShippingThreshold", + "Free shipping threshold", + "Kostenloser Versand ab", + "Amount as from shipping is free.", + "Betrag, ab dem keine Versandkosten anfallen."); + + #endregion + + builder.AddOrUpdate("Admin.Product.Picture.Added", + "The picture has successfully been added", + "Das Bild wurde erfolgreich zugefgt"); + + builder.AddOrUpdate("HtmlEditor.ClickToEdit", "Click to edit HTML...", "Hier klicken, um HTML zu editieren..."); + + builder.AddOrUpdate("Admin.Catalog.Attributes.ProductAttributes.Fields.ExportMappings.Note", + "Define mappings of attribute values to export fields according to the pattern <Format prefix>:<Export field name>. Example: gmc:color exports the attribute values for colors to the field color during the Google Merchant Center Export. The mappings are only effective when exporting attribute combinations.", + "Legen Sie Zuordnungen von Attributwerten zu Exportfeldern nach dem Muster <Formatprfix>:<Export-Feldname> fest. Beispiel: gmc:color exportiert beim Google Merchant Center Export die Attributwerte fr Farben in das Feld color. Die Zuordnungen sind nur beim Export von Attributkombinationen wirksam."); + + builder.AddOrUpdate("Admin.Catalog.Attributes.ProductAttributes.Fields.ExportMappings", + "Mappings to export fields", + "Zuordnungen zu Exportfeldern", + "Allows to map attribute values to export fields. Each entry has to be entered in a new line.", + "Ermglicht die Zuordnung von Attributwerten zu Exportfeldern. Jeder Eintrag muss in einer neuen Zeile erfolgen."); + + builder.AddOrUpdate("Admin.Configuration.Payment.Methods.AdditionalFee", + "Additional fee", + "Zustzliche Gebhr", + "Specifies an additional fee to be charged to the customer for using the payment method.", + "Legt eine zustzliche Gebhr fest, die dem Kunden fr die Inanspruchnahme der Zahlart berechnet wird."); + + builder.AddOrUpdate("Admin.Configuration.Payment.Methods.AdditionalFeePercentage", + "Additional fee percentage", + "Zustzliche Gebhr prozentual", + "Specifies whether the additional fee should be calculated as a percentage. A fixed value is used if this option is disabled.", + "Legt fest, ob die zustzliche Gebhr prozentual berechnet werden soll. Es wird ein fester Wert verwendet, falls diese Option deaktiviert ist."); + + builder.Delete("Common.Buttons.Default"); + builder.AddOrUpdate("Common.Buttons.Secondary", "Secondary", "Secondary"); + builder.AddOrUpdate("Common.Buttons.Light", "Light", "Light"); + builder.AddOrUpdate("Common.Buttons.Dark", "Dark", "Dark"); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.AddressFormFields.CountryRequired", + "'Country' required", + "Die Eingabe eines Landes ist erforderlich", + "Check the box if 'Country' is required.", + "Legt fest, ob die Eingabe eines Landes erforderlich ist."); + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.AddressFormFields.StateProvinceRequired", + "'State/province' required", + "Die Eingabe eines Bundeslandes ist erforderlich", + "Check the box if 'State/province' is required.", + "Legt fest, ob die Eingabe eines Bundeslandes erforderlich ist."); + + builder.AddOrUpdate("Address.Fields.StateProvince.Required", "State is required.", "Bundesland wird bentigt"); + + builder.AddOrUpdate("Common.Columns", "Columns", "Spalten"); + builder.AddOrUpdate("Common.Mru", "Recently", "Zuletzt"); + + builder.AddOrUpdate("Admin.WidgetZones.UserDefined", "User-defined", "Benutzerdefiniert"); + + builder.AddOrUpdate("Admin.Configuration.ManageLanguages", "Manage languages", "Sprachen verwalten"); + + builder.AddOrUpdate("Admin.Customers.Customers.Info", "General", "Allgemein"); + builder.AddOrUpdate("Admin.Customers.Customers.Impersonate", "Impersonate", "Imitieren"); + builder.AddOrUpdate("Admin.Customers.Customers.CurrentCart", "Current cart", "Aktueller Warenkorb"); + + builder.AddOrUpdate("Admin.ContentManagement.Topics.CannotBeDeleted", + "This topic is needed by your Shop and can therefore not be deleted.", + "Diese Seite wird von Ihrem Shop bentigt und kann daher nicht gelscht werden."); + } + } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201804252356096_TopicSlugs.cs b/src/Libraries/SmartStore.Data/Migrations/201804252356096_TopicSlugs.cs index 3eefe926f1..dfb772e099 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201804252356096_TopicSlugs.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201804252356096_TopicSlugs.cs @@ -1,70 +1,66 @@ namespace SmartStore.Data.Migrations { - using System; - using System.Linq; - using System.Data.Entity.Migrations; - using SmartStore.Core.Domain.Topics; - using SmartStore.Data.Setup; - using SmartStore.Core.Domain.Seo; - using SmartStore.Utilities; + using System.Data.Entity.Migrations; + using System.Linq; + using SmartStore.Core.Domain.Seo; + using SmartStore.Core.Domain.Topics; + using SmartStore.Data.Setup; + using SmartStore.Utilities; - public partial class TopicSlugs : DbMigration, IDataSeeder - { + public partial class TopicSlugs : DbMigration, IDataSeeder + { public override void Up() { } - + public override void Down() { } - public bool RollbackOnFailure - { - get { return false; } - } + public bool RollbackOnFailure => false; - public void Seed(SmartObjectContext context) - { - var allTopics = context.Set() - .AsNoTracking() - .Select(x => new { x.Id, x.SystemName, x.Title }) - .ToList(); + public void Seed(SmartObjectContext context) + { + var allTopics = context.Set() + .AsNoTracking() + .Select(x => new { x.Id, x.SystemName, x.Title }) + .ToList(); - var urlRecords = context.Set(); + var urlRecords = context.Set(); - foreach (var topic in allTopics) - { - var slug = SeoHelper.GetSeName(topic.SystemName, true, false).Truncate(400); - int i = 2; - var tempSlug = slug; + foreach (var topic in allTopics) + { + var slug = SeoHelper.GetSeName(topic.SystemName, true, false).Truncate(400); + int i = 2; + var tempSlug = slug; - while (urlRecords.Any(x => x.Slug == tempSlug)) - { - tempSlug = string.Format("{0}-{1}", slug, i); - i++; - } + while (urlRecords.Any(x => x.Slug == tempSlug)) + { + tempSlug = string.Format("{0}-{1}", slug, i); + i++; + } - slug = tempSlug; + slug = tempSlug; - var ur = urlRecords.FirstOrDefault(x => x.LanguageId == 0 && x.EntityName == "Topic" && x.EntityId == topic.Id); - if (ur != null) - { - ur.Slug = slug; - } - else - { - urlRecords.Add(new UrlRecord - { - EntityId = topic.Id, - EntityName = "Topic", - IsActive = true, - LanguageId = 0, - Slug = slug - }); - } + var ur = urlRecords.FirstOrDefault(x => x.LanguageId == 0 && x.EntityName == "Topic" && x.EntityId == topic.Id); + if (ur != null) + { + ur.Slug = slug; + } + else + { + urlRecords.Add(new UrlRecord + { + EntityId = topic.Id, + EntityName = "Topic", + IsActive = true, + LanguageId = 0, + Slug = slug + }); + } - context.SaveChanges(); - } - } - } + context.SaveChanges(); + } + } + } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201805250724399_V315Resources.cs b/src/Libraries/SmartStore.Data/Migrations/201805250724399_V315Resources.cs index e1a02d62ce..f42a572047 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201805250724399_V315Resources.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201805250724399_V315Resources.cs @@ -1,195 +1,192 @@ namespace SmartStore.Data.Migrations { - using SmartStore.Core.Domain.Catalog; - using SmartStore.Core.Domain.Configuration; - using SmartStore.Core.Domain.Seo; - using SmartStore.Data.Setup; - using SmartStore.Utilities; - using System.Data.Entity.Migrations; - using System.Linq; - - public partial class V315Resources : DbMigration, ILocaleResourcesProvider, IDataSeeder - { + using System.Data.Entity.Migrations; + using System.Linq; + using SmartStore.Core.Domain.Catalog; + using SmartStore.Core.Domain.Configuration; + using SmartStore.Core.Domain.Seo; + using SmartStore.Data.Setup; + using SmartStore.Utilities; + + public partial class V315Resources : DbMigration, ILocaleResourcesProvider, IDataSeeder + { public override void Up() { - } - + } + public override void Down() { } - - public bool RollbackOnFailure - { - get { return false; } - } - - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - context.SaveChanges(); - - MigrateSettings(context); - context.SaveChanges(); - } - - public void MigrateSettings(SmartObjectContext context) - { - // SeoSettings.RedirectLegacyTopicUrls should be true when migrating (it is false by default after fresh install) - var name = TypeHelper.NameOf(y => y.RedirectLegacyTopicUrls, true); - context.MigrateSettings(x => x.Add(name, true)); - - // remove setting which were moved from customer settings to privacy settings and have new default values which should be applied immediately - var settings = context.Set(); - var storeLastIpAddressSetting = settings.FirstOrDefault(x => x.Name == "CustomerSettings.StoreLastIpAddress"); - if (storeLastIpAddressSetting != null) settings.Remove(storeLastIpAddressSetting); - - var displayPrivacyAgreementOnContactUs = settings.FirstOrDefault(x => x.Name == "CustomerSettings.DisplayPrivacyAgreementOnContactUs"); - if (displayPrivacyAgreementOnContactUs != null) settings.Remove(displayPrivacyAgreementOnContactUs); - - - var showShareButtonName = TypeHelper.NameOf(y => y.ShowShareButton, true); - var showShareButtonSetting = context.Set().FirstOrDefault(x => x.Name == showShareButtonName); - if (showShareButtonSetting != null) - { - showShareButtonSetting.Value = "False"; - } - - var allowAnonymousUsersToEmailAFriendSetting = context.Set().FirstOrDefault(x => x.Name == "CatalogSettings.AllowAnonymousUsersToEmailAFriend"); - if (allowAnonymousUsersToEmailAFriendSetting != null) - { - allowAnonymousUsersToEmailAFriendSetting.Value = "False"; - } - - var allowAnonymousUsersToReviewProductSetting = context.Set().FirstOrDefault(x => x.Name == "CatalogSettings.AllowAnonymousUsersToReviewProduct"); - if (allowAnonymousUsersToReviewProductSetting != null) - { - allowAnonymousUsersToReviewProductSetting.Value = "False"; - } - - var allowAnonymousUsersToEmailWishlistSetting = context.Set().FirstOrDefault(x => x.Name == "ShoppingCartSettings.AllowAnonymousUsersToEmailWishlist"); - if (allowAnonymousUsersToEmailWishlistSetting != null) - { - allowAnonymousUsersToEmailWishlistSetting.Value = "False"; - } - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.ThirdPartyEmailHandOver.Hint", - "Specifies whether customers can agree to a transferring of their email address to third parties when ordering, and whether the checkbox is enabled by default during checkout. Please note that the 'Show activated' option isn't legally compliant in line with the GDPR.", - "Legt fest, ob Kunden bei einer Bestellung der Weitergabe ihrer E-Mail Adresse an Dritte zustimmen knnen und ob die Checkbox dafr standardmig aktiviert ist. Bitte beachten Sie, dass die Option 'Aktiviert anzeigen' im Rahmen der DSVGO nicht rechtskonform ist."); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy", "Privacy", "Datenschutz"); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.EnableCookieConsent", - "Enable cookie consent", - "Cookie-Hinweis aktivieren", - "Specifies whether the cookie consent box will be displayed in the frontend.", - "Legt fest, ob ein Element fr die Zustimmung zur Nutzung von Cookies im Frontend angezeigt wird."); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.CookieConsentBadgetext", - "Cookie consent display text", - "Cookie-Hinweistext", - "Specifies the text, that will be displayed to your customers if they havn't agreed to the usage of cookis yet.", - "Bestimmt den Text, der Ihren Kunden beim Besuch der Seite angezeigt wird, sofern Sie ihre Zustimmung zur Nutzung von Cookies noch nicht gegeben haben."); - - builder.AddOrUpdate("CookieConsent.BadgeText", - "{0} is using cookies, to guarantee the best shopping experience. Partially cookies will be set by third parties. Privacy Info", - "{0} benutzt Cookies, um Ihnen das beste Einkaufserlebnis zu ermglichen. Zum Teil werden Cookies auch von Drittanbietern gesetzt. Datenschutzerklrung"); - - builder.AddOrUpdate("CookieConsent.Button", "Okay, got it", "Ok, verstanden"); - - builder.Delete("ContactUs.PrivacyAgreement"); - - builder.Delete("Admin.Configuration.Settings.CustomerUser.StoreLastIpAddress"); - builder.Delete("Admin.Configuration.Settings.CustomerUser.StoreLastIpAddress.Hint"); - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.StoreLastIpAddress", - "Store IP address", - "IP-Adresse speichern", - "Specifies whether to store the IP address in the customer data set.", - "Legt fest, ob die IP-Adresse im Kundendatensatz gespeichert werden soll."); - - builder.Delete("Admin.Configuration.Settings.CustomerUser.DisplayPrivacyAgreementOnContactUs"); - builder.Delete("Admin.Configuration.Settings.CustomerUser.DisplayPrivacyAgreementOnContactUs.Hint"); - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.DisplayGdprConsentOnForms", - "Get privacy consent for form submissions", - "Einwilligungserklrung in Formularen fordern", - "Specifies whether a checkbox is displayed in forms that prompts the user to agree to the processing of his data.", - "Bestimmt ob in Formularen eine Checkbox angezeigt wird, die den Benutzer auffordert der Verarbeitung seiner Daten zuzustimmen."); - - builder.AddOrUpdate("Gdpr.Consent.ValidationMessage", - "Please agree to the processing of your data.", - "Bitte stimmen Sie der Verarbeitung Ihrer Daten zu."); - - builder.Delete("ContactUs.PrivacyAgreement.MustBeAccepted"); - builder.Delete("ContactUs.PrivacyAgreement.DetailText"); - builder.AddOrUpdate("Gdpr.Consent.DetailText", - "Yes I've read the privacy terms and agree that my data given by me can be stored electronically. My data will thereby only be used to process my inquiry.", - "Ja, ich habe die Datenschutzerklrung zur Kenntnis genommen und bin damit einverstanden, dass die von mir angegebenen Daten elektronisch erhoben und gespeichert werden. Meine Daten werden dabei nur zur Bearbeitung meiner Anfrage genutzt."); - - builder.AddOrUpdate("Gdpr.Anonymous", "Anonymous", "Anonym"); - builder.AddOrUpdate("Gdpr.Anonymize", "Anonymize", "Anonymisieren"); - builder.AddOrUpdate("Gdpr.DeletedText", "Deleted", "Gelscht"); - builder.AddOrUpdate("Gdpr.DeletedLongText", - "This content was deleted by the author.", - "Dieser Inhalt wurde vom Autor gelscht."); - builder.AddOrUpdate("Gdpr.Anonymize.Success", - "The customer record '{0}' has been anonymized.", - "Der Kundendatensatz '{0}' wurde anonymisiert."); - - builder.AddOrUpdate("Admin.Configuration.Languages.Fields.LastResourcesImportOn", - "Last import", - "Letzter Import", - "The date on which resources were last downloaded and imported.", - "Das Datum, an dem zuletzt Ressourcen heruntergeladen und importiert worden sind."); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CustomerFormFields", "Registration", "Registrierung"); - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.AddressFormFields", "Addresses", "Adressen"); - - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.FirstNameRequired", - "First name required", - "Vorname ist erforderlich", - "Check the box if 'First name' is required.", - "Legt fest, ob die Angabe des Vornamens erforderlich ist."); - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.LastNameRequired", - "Last name required", - "Nachname ist erforderlich", - "Check the box if 'Last name' is required.", - "Legt fest, ob die Angabe des Nachnamens erforderlich ist."); - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.FullNameOnContactUsRequired", - "Name in the contact form is required", - "Name im Kontaktformular ist erforderlich", - "Specifies whether the name is required in the contact form.", - "Legt fest, ob die Angabe des Namens im Kontaktformular erforderlich ist."); - builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.FullNameOnProductRequestRequired", - "Name in the product request is required", - "Name im Produktanfrage-Formular ist erforderlich", - "Specifies whether the name is required in the product request form.", - "Legt fest, ob die Angabe des Namens im Produktanfrage-Formular erforderlich ist."); - - builder.AddOrUpdate("Checkout.TermsOfService.IAccept", - "I agree with the {0}terms of service{1} and I adhere to them unconditionally. I've read the {3}privacy terms{1} and agree that my data given by me can be stored electronically.", - "Ich habe {0}die AGB{1} und {2}das Widerrufsrecht{1} gelesen und bin mit der Geltung einverstanden. Ich habe die {3}Datenschutzerklrung{1} zur Kenntnis genommen und bin damit einverstanden, dass die von mir angegebenen Daten elektronisch erhoben und gespeichert werden."); - - builder.AddOrUpdate("Admin.Customers.Customers.List.SearchDeletedOnly", "Only deactivated customers", "Nur deaktivierte Kunden"); - - builder.AddOrUpdate("Admin.Common.Global", "Global", "Global"); - builder.AddOrUpdate("Admin.Common.News", "News", "News"); - builder.AddOrUpdate("Admin.Common.Navigation", "Navigation", "Navigation"); - builder.AddOrUpdate("Admin.Common.PDF", "PDF", "PDF"); - builder.AddOrUpdate("Admin.Common.Footer", "Footer", "Footer"); - - builder.AddOrUpdate("Gdpr.Consent.DetailText.Small", "I agree to the Privacy policy.", "Mit den Bestimmungen zum Datenschutz bin ich einverstanden"); - - builder.AddOrUpdate("Account.Fields.Newsletter", - "I would like to subscribe to the newsletter. I agree to the . Unsubscription is possible at any time.", - "Ich mchte den Newsletter abonnieren. Mit den Bestimmungen zum Datenschutz bin ich einverstanden. Eine Abmeldung ist jederzeit mglich."); - - builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.EnableHoneypotProtection", - "Enable Honeypot protection", - "Honeypot aktivieren", - "Honeypot is a simple but reliable bot detection method that does not require any captcha. If active, registration and contact forms are protected against bots and attackers.", - "Honeypot ist eine simple aber zuverlssige Bot-Erkennungsmethode, die ganz ohne Captcha auskommt. Wenn aktiv, werden Registrierungs- und Kontaktformular vor Bots und Angreifern geschtzt."); - } - } + + public bool RollbackOnFailure => false; + + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + context.SaveChanges(); + + MigrateSettings(context); + context.SaveChanges(); + } + + public void MigrateSettings(SmartObjectContext context) + { + // SeoSettings.RedirectLegacyTopicUrls should be true when migrating (it is false by default after fresh install) + var name = TypeHelper.NameOf(y => y.RedirectLegacyTopicUrls, true); + context.MigrateSettings(x => x.Add(name, true)); + + // remove setting which were moved from customer settings to privacy settings and have new default values which should be applied immediately + var settings = context.Set(); + var storeLastIpAddressSetting = settings.FirstOrDefault(x => x.Name == "CustomerSettings.StoreLastIpAddress"); + if (storeLastIpAddressSetting != null) settings.Remove(storeLastIpAddressSetting); + + var displayPrivacyAgreementOnContactUs = settings.FirstOrDefault(x => x.Name == "CustomerSettings.DisplayPrivacyAgreementOnContactUs"); + if (displayPrivacyAgreementOnContactUs != null) settings.Remove(displayPrivacyAgreementOnContactUs); + + + var showShareButtonName = TypeHelper.NameOf(y => y.ShowShareButton, true); + var showShareButtonSetting = context.Set().FirstOrDefault(x => x.Name == showShareButtonName); + if (showShareButtonSetting != null) + { + showShareButtonSetting.Value = "False"; + } + + var allowAnonymousUsersToEmailAFriendSetting = context.Set().FirstOrDefault(x => x.Name == "CatalogSettings.AllowAnonymousUsersToEmailAFriend"); + if (allowAnonymousUsersToEmailAFriendSetting != null) + { + allowAnonymousUsersToEmailAFriendSetting.Value = "False"; + } + + var allowAnonymousUsersToReviewProductSetting = context.Set().FirstOrDefault(x => x.Name == "CatalogSettings.AllowAnonymousUsersToReviewProduct"); + if (allowAnonymousUsersToReviewProductSetting != null) + { + allowAnonymousUsersToReviewProductSetting.Value = "False"; + } + + var allowAnonymousUsersToEmailWishlistSetting = context.Set().FirstOrDefault(x => x.Name == "ShoppingCartSettings.AllowAnonymousUsersToEmailWishlist"); + if (allowAnonymousUsersToEmailWishlistSetting != null) + { + allowAnonymousUsersToEmailWishlistSetting.Value = "False"; + } + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.ThirdPartyEmailHandOver.Hint", + "Specifies whether customers can agree to a transferring of their email address to third parties when ordering, and whether the checkbox is enabled by default during checkout. Please note that the 'Show activated' option isn't legally compliant in line with the GDPR.", + "Legt fest, ob Kunden bei einer Bestellung der Weitergabe ihrer E-Mail Adresse an Dritte zustimmen knnen und ob die Checkbox dafr standardmig aktiviert ist. Bitte beachten Sie, dass die Option 'Aktiviert anzeigen' im Rahmen der DSVGO nicht rechtskonform ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy", "Privacy", "Datenschutz"); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.EnableCookieConsent", + "Enable cookie consent", + "Cookie-Hinweis aktivieren", + "Specifies whether the cookie consent box will be displayed in the frontend.", + "Legt fest, ob ein Element fr die Zustimmung zur Nutzung von Cookies im Frontend angezeigt wird."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.CookieConsentBadgetext", + "Cookie consent display text", + "Cookie-Hinweistext", + "Specifies the text, that will be displayed to your customers if they havn't agreed to the usage of cookis yet.", + "Bestimmt den Text, der Ihren Kunden beim Besuch der Seite angezeigt wird, sofern Sie ihre Zustimmung zur Nutzung von Cookies noch nicht gegeben haben."); + + builder.AddOrUpdate("CookieConsent.BadgeText", + "{0} is using cookies, to guarantee the best shopping experience. Partially cookies will be set by third parties. Privacy Info", + "{0} benutzt Cookies, um Ihnen das beste Einkaufserlebnis zu ermglichen. Zum Teil werden Cookies auch von Drittanbietern gesetzt. Datenschutzerklrung"); + + builder.AddOrUpdate("CookieConsent.Button", "Okay, got it", "Ok, verstanden"); + + builder.Delete("ContactUs.PrivacyAgreement"); + + builder.Delete("Admin.Configuration.Settings.CustomerUser.StoreLastIpAddress"); + builder.Delete("Admin.Configuration.Settings.CustomerUser.StoreLastIpAddress.Hint"); + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.StoreLastIpAddress", + "Store IP address", + "IP-Adresse speichern", + "Specifies whether to store the IP address in the customer data set.", + "Legt fest, ob die IP-Adresse im Kundendatensatz gespeichert werden soll."); + + builder.Delete("Admin.Configuration.Settings.CustomerUser.DisplayPrivacyAgreementOnContactUs"); + builder.Delete("Admin.Configuration.Settings.CustomerUser.DisplayPrivacyAgreementOnContactUs.Hint"); + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.DisplayGdprConsentOnForms", + "Get privacy consent for form submissions", + "Einwilligungserklrung in Formularen fordern", + "Specifies whether a checkbox is displayed in forms that prompts the user to agree to the processing of his data.", + "Bestimmt ob in Formularen eine Checkbox angezeigt wird, die den Benutzer auffordert der Verarbeitung seiner Daten zuzustimmen."); + + builder.AddOrUpdate("Gdpr.Consent.ValidationMessage", + "Please agree to the processing of your data.", + "Bitte stimmen Sie der Verarbeitung Ihrer Daten zu."); + + builder.Delete("ContactUs.PrivacyAgreement.MustBeAccepted"); + builder.Delete("ContactUs.PrivacyAgreement.DetailText"); + builder.AddOrUpdate("Gdpr.Consent.DetailText", + "Yes I've read the privacy terms and agree that my data given by me can be stored electronically. My data will thereby only be used to process my inquiry.", + "Ja, ich habe die Datenschutzerklrung zur Kenntnis genommen und bin damit einverstanden, dass die von mir angegebenen Daten elektronisch erhoben und gespeichert werden. Meine Daten werden dabei nur zur Bearbeitung meiner Anfrage genutzt."); + + builder.AddOrUpdate("Gdpr.Anonymous", "Anonymous", "Anonym"); + builder.AddOrUpdate("Gdpr.Anonymize", "Anonymize", "Anonymisieren"); + builder.AddOrUpdate("Gdpr.DeletedText", "Deleted", "Gelscht"); + builder.AddOrUpdate("Gdpr.DeletedLongText", + "This content was deleted by the author.", + "Dieser Inhalt wurde vom Autor gelscht."); + builder.AddOrUpdate("Gdpr.Anonymize.Success", + "The customer record '{0}' has been anonymized.", + "Der Kundendatensatz '{0}' wurde anonymisiert."); + + builder.AddOrUpdate("Admin.Configuration.Languages.Fields.LastResourcesImportOn", + "Last import", + "Letzter Import", + "The date on which resources were last downloaded and imported.", + "Das Datum, an dem zuletzt Ressourcen heruntergeladen und importiert worden sind."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CustomerFormFields", "Registration", "Registrierung"); + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.AddressFormFields", "Addresses", "Adressen"); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.FirstNameRequired", + "First name required", + "Vorname ist erforderlich", + "Check the box if 'First name' is required.", + "Legt fest, ob die Angabe des Vornamens erforderlich ist."); + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.LastNameRequired", + "Last name required", + "Nachname ist erforderlich", + "Check the box if 'Last name' is required.", + "Legt fest, ob die Angabe des Nachnamens erforderlich ist."); + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.FullNameOnContactUsRequired", + "Name in the contact form is required", + "Name im Kontaktformular ist erforderlich", + "Specifies whether the name is required in the contact form.", + "Legt fest, ob die Angabe des Namens im Kontaktformular erforderlich ist."); + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.FullNameOnProductRequestRequired", + "Name in the product request is required", + "Name im Produktanfrage-Formular ist erforderlich", + "Specifies whether the name is required in the product request form.", + "Legt fest, ob die Angabe des Namens im Produktanfrage-Formular erforderlich ist."); + + builder.AddOrUpdate("Checkout.TermsOfService.IAccept", + "I agree with the {0}terms of service{1} and I adhere to them unconditionally. I've read the {3}privacy terms{1} and agree that my data given by me can be stored electronically.", + "Ich habe {0}die AGB{1} und {2}das Widerrufsrecht{1} gelesen und bin mit der Geltung einverstanden. Ich habe die {3}Datenschutzerklrung{1} zur Kenntnis genommen und bin damit einverstanden, dass die von mir angegebenen Daten elektronisch erhoben und gespeichert werden."); + + builder.AddOrUpdate("Admin.Customers.Customers.List.SearchDeletedOnly", "Only deactivated customers", "Nur deaktivierte Kunden"); + + builder.AddOrUpdate("Admin.Common.Global", "Global", "Global"); + builder.AddOrUpdate("Admin.Common.News", "News", "News"); + builder.AddOrUpdate("Admin.Common.Navigation", "Navigation", "Navigation"); + builder.AddOrUpdate("Admin.Common.PDF", "PDF", "PDF"); + builder.AddOrUpdate("Admin.Common.Footer", "Footer", "Footer"); + + builder.AddOrUpdate("Gdpr.Consent.DetailText.Small", "I agree to the Privacy policy.", "Mit den Bestimmungen zum Datenschutz bin ich einverstanden"); + + builder.AddOrUpdate("Account.Fields.Newsletter", + "I would like to subscribe to the newsletter. I agree to the . Unsubscription is possible at any time.", + "Ich mchte den Newsletter abonnieren. Mit den Bestimmungen zum Datenschutz bin ich einverstanden. Eine Abmeldung ist jederzeit mglich."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.EnableHoneypotProtection", + "Enable Honeypot protection", + "Honeypot aktivieren", + "Honeypot is a simple but reliable bot detection method that does not require any captcha. If active, registration and contact forms are protected against bots and attackers.", + "Honeypot ist eine simple aber zuverlssige Bot-Erkennungsmethode, die ganz ohne Captcha auskommt. Wenn aktiv, werden Registrierungs- und Kontaktformular vor Bots und Angreifern geschtzt."); + } + } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201806051221399_RefundReturnRequests.cs b/src/Libraries/SmartStore.Data/Migrations/201806051221399_RefundReturnRequests.cs index c887156aea..e74f179a68 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201806051221399_RefundReturnRequests.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201806051221399_RefundReturnRequests.cs @@ -1,15 +1,14 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class RefundReturnRequests : DbMigration { public override void Up() { AddColumn("dbo.ReturnRequest", "RefundToWallet", c => c.Boolean()); } - + public override void Down() { DropColumn("dbo.ReturnRequest", "RefundToWallet"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201807051830375_MoveCustomerFields.cs b/src/Libraries/SmartStore.Data/Migrations/201807051830375_MoveCustomerFields.cs index a780ddb08a..0b211b6e2c 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201807051830375_MoveCustomerFields.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201807051830375_MoveCustomerFields.cs @@ -1,12 +1,14 @@ namespace SmartStore.Data.Migrations { using System; - using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - using SmartStore.Data.Utilities; + using System.Collections.Generic; + using System.Data.Entity.Migrations; + using System.Linq; + using SmartStore.Data.Setup; + using SmartStore.Data.Utilities; - public partial class MoveCustomerFields : DbMigration, ILocaleResourcesProvider, IDataSeeder - { + public partial class MoveCustomerFields : DbMigration, ILocaleResourcesProvider, IDataSeeder + { public override void Up() { AddColumn("dbo.Customer", "Salutation", c => c.String(maxLength: 50)); @@ -22,7 +24,7 @@ public override void Up() CreateIndex("dbo.Customer", "CustomerNumber", name: "IX_Customer_CustomerNumber", unique: false); CreateIndex("dbo.Customer", "BirthDate", name: "IX_Customer_BirthDate"); } - + public override void Down() { DropIndex("dbo.Customer", "IX_Customer_BirthDate"); @@ -39,31 +41,64 @@ public override void Down() DropColumn("dbo.Customer", "Salutation"); } - public bool RollbackOnFailure => true; + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + + // Perf + var numDeletedAttrs = DataMigrator.DeleteGuestCustomerGenericAttributes(context, TimeSpan.FromDays(30)); + var numDeletedCustomers = DataMigrator.DeleteGuestCustomers(context, TimeSpan.FromDays(30)); + + var candidates = new[] { "Title", "FirstName", "LastName", "Company", "CustomerNumber", "DateOfBirth" }; + var numUpdatedCustomers = DataMigrator.MoveCustomerFields(context, UpdateCustomer, candidates); + } - public void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - context.SaveChanges(); + private static void UpdateCustomer(IDictionary columns, string key, string value) + { + switch (key) + { + case "Title": + columns[key] = value?.Truncate(50); + break; + case "FirstName": + columns[key] = value?.Truncate(199); + break; + case "LastName": + columns[key] = value?.Truncate(199); + break; + case "Company": + columns[key] = value?.Truncate(255); + break; + case "CustomerNumber": + columns[key] = value?.Truncate(100); + break; + case "DateOfBirth": + columns["BirthDate"] = value?.Convert(); + break; + } - DataMigrator.MoveCustomerFields(context); - } + // Update FullName + var parts = new string[] { (string)columns.Get("Title"), (string)columns.Get("FirstName"), (string)columns.Get("LastName") }; + columns["FullName"] = string.Join(" ", parts.Where(x => x.HasValue())).NullEmpty(); + } - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.Delete( - "Admin.Customers.Customers.List.SearchFirstName", - "Admin.Customers.Customers.List.SearchFirstName.Hint", - "Admin.Customers.Customers.List.SearchLastName", - "Admin.Customers.Customers.List.SearchLastName.Hint", - "Admin.Customers.Customers.List.SearchCompany", - "Admin.Customers.Customers.List.SearchCompany.Hint"); + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.Delete( + "Admin.Customers.Customers.List.SearchFirstName", + "Admin.Customers.Customers.List.SearchFirstName.Hint", + "Admin.Customers.Customers.List.SearchLastName", + "Admin.Customers.Customers.List.SearchLastName.Hint", + "Admin.Customers.Customers.List.SearchCompany", + "Admin.Customers.Customers.List.SearchCompany.Hint"); - builder.AddOrUpdate("Admin.Customers.Customers.List.SearchTerm", - "Search term", - "Suchbegriff", - "Name or company", - "Name oder Firma"); - } - } + builder.AddOrUpdate("Admin.Customers.Customers.List.SearchTerm", + "Search term", + "Suchbegriff", + "Name or company", + "Name oder Firma"); + } + } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201807122120062_TopicAcl.cs b/src/Libraries/SmartStore.Data/Migrations/201807122120062_TopicAcl.cs index 639c810790..a525ea429a 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201807122120062_TopicAcl.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201807122120062_TopicAcl.cs @@ -1,8 +1,7 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class TopicAcl : DbMigration { public override void Up() @@ -14,7 +13,7 @@ public override void Up() AddColumn("dbo.Topic", "IsPublished", c => c.Boolean(nullable: false, defaultValue: true)); CreateIndex("dbo.Customer", "CustomerNumber", name: "IX_Customer_CustomerNumber"); } - + public override void Down() { DropIndex("dbo.Customer", "IX_Customer_CustomerNumber"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201807191020207_OrderItemDeliveryTime.cs b/src/Libraries/SmartStore.Data/Migrations/201807191020207_OrderItemDeliveryTime.cs index 39805ce878..341a2322b7 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201807191020207_OrderItemDeliveryTime.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201807191020207_OrderItemDeliveryTime.cs @@ -1,8 +1,7 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class OrderItemDeliveryTime : DbMigration { public override void Up() @@ -10,7 +9,7 @@ public override void Up() AddColumn("dbo.OrderItem", "DeliveryTimeId", c => c.Int()); AddColumn("dbo.OrderItem", "DisplayDeliveryTime", c => c.Boolean(nullable: false)); } - + public override void Down() { DropColumn("dbo.OrderItem", "DisplayDeliveryTime"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201807201157391_DownloadVersions.cs b/src/Libraries/SmartStore.Data/Migrations/201807201157391_DownloadVersions.cs index 6210c475fc..caadf9da72 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201807201157391_DownloadVersions.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201807201157391_DownloadVersions.cs @@ -1,13 +1,12 @@ namespace SmartStore.Data.Migrations { + using System.Data.Entity.Migrations; + using System.Linq; + using System.Web.Hosting; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; using SmartStore.Data.Setup; using SmartStore.Data.Utilities; - using System; - using System.Data.Entity.Migrations; - using System.Linq; - using System.Web.Hosting; public partial class DownloadVersions : DbMigration, IDataSeeder { @@ -37,10 +36,7 @@ public override void Down() DropColumn("dbo.Download", "EntityId"); } - public bool RollbackOnFailure - { - get { return true; } - } + public bool RollbackOnFailure => true; public void Seed(SmartObjectContext context) { diff --git a/src/Libraries/SmartStore.Data/Migrations/201807311708428_ProductPreviewPicture.cs b/src/Libraries/SmartStore.Data/Migrations/201807311708428_ProductPreviewPicture.cs index 91c06bfb44..9b7aa16571 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201807311708428_ProductPreviewPicture.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201807311708428_ProductPreviewPicture.cs @@ -1,15 +1,14 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class ProductPreviewPicture : DbMigration { public override void Up() { AddColumn("dbo.Product", "HasPreviewPicture", c => c.Boolean(nullable: false)); } - + public override void Down() { DropColumn("dbo.Product", "HasPreviewPicture"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201808051119578_Merge4.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201808051119578_Merge4.Designer.cs deleted file mode 100644 index 9002b8b1aa..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201808051119578_Merge4.Designer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -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 Merge4 : IMigrationMetadata - { - private readonly ResourceManager Resources = new ResourceManager(typeof(Merge4)); - - string IMigrationMetadata.Id - { - get { return "201808051119578_Merge4"; } - } - - string IMigrationMetadata.Source - { - get { return null; } - } - - string IMigrationMetadata.Target - { - get { return Resources.GetString("Target"); } - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201808051119578_Merge4.cs b/src/Libraries/SmartStore.Data/Migrations/201808051119578_Merge4.cs deleted file mode 100644 index 0777552123..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201808051119578_Merge4.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace SmartStore.Data.Migrations -{ - using System; - using System.Data.Entity.Migrations; - - public partial class Merge4 : DbMigration - { - public override void Up() - { - } - - public override void Down() - { - } - } -} diff --git a/src/Libraries/SmartStore.Data/Migrations/201808051119578_Merge4.resx b/src/Libraries/SmartStore.Data/Migrations/201808051119578_Merge4.resx deleted file mode 100644 index 198ea117b6..0000000000 --- a/src/Libraries/SmartStore.Data/Migrations/201808051119578_Merge4.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/201808051818238_Merge4.cs b/src/Libraries/SmartStore.Data/Migrations/201808051818238_Merge4.cs index 0777552123..cf6adcbfff 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201808051818238_Merge4.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201808051818238_Merge4.cs @@ -1,14 +1,13 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class Merge4 : DbMigration { public override void Up() { } - + public override void Down() { } diff --git a/src/Libraries/SmartStore.Data/Migrations/201809171309522_NewsletterSubscriptionLanguage.cs b/src/Libraries/SmartStore.Data/Migrations/201809171309522_NewsletterSubscriptionLanguage.cs index f1eccaf9e5..3304f86e1a 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201809171309522_NewsletterSubscriptionLanguage.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201809171309522_NewsletterSubscriptionLanguage.cs @@ -1,15 +1,14 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class NewsletterSubscriptionLanguage : DbMigration { public override void Up() { AddColumn("dbo.NewsLetterSubscription", "WorkingLanguageId", c => c.Int(nullable: false)); } - + public override void Down() { DropColumn("dbo.NewsLetterSubscription", "WorkingLanguageId"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201809261026134_ForumGroupAcl.cs b/src/Libraries/SmartStore.Data/Migrations/201809261026134_ForumGroupAcl.cs index 4abd203c4a..0c7e1af33d 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201809261026134_ForumGroupAcl.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201809261026134_ForumGroupAcl.cs @@ -1,8 +1,7 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class ForumGroupAcl : DbMigration { public override void Up() @@ -24,7 +23,7 @@ public override void Up() CreateIndex("dbo.Forums_Group", "LimitedToStores"); CreateIndex("dbo.Forums_Group", "SubjectToAcl"); } - + public override void Down() { DropIndex("dbo.Forums_Group", new[] { "SubjectToAcl" }); diff --git a/src/Libraries/SmartStore.Data/Migrations/201810011954195_ForumPostVote.cs b/src/Libraries/SmartStore.Data/Migrations/201810011954195_ForumPostVote.cs index 843ad1b291..37d44c2b2e 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201810011954195_ForumPostVote.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201810011954195_ForumPostVote.cs @@ -1,8 +1,7 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class ForumPostVote : DbMigration { public override void Up() @@ -10,19 +9,19 @@ public override void Up() CreateTable( "dbo.ForumPostVote", c => new - { - Id = c.Int(nullable: false), - ForumPostId = c.Int(nullable: false), - Vote = c.Boolean(nullable: false), - }) + { + Id = c.Int(nullable: false), + ForumPostId = c.Int(nullable: false), + Vote = c.Boolean(nullable: false), + }) .PrimaryKey(t => t.Id) .ForeignKey("dbo.CustomerContent", t => t.Id) .ForeignKey("dbo.Forums_Post", t => t.ForumPostId, cascadeDelete: true) .Index(t => t.Id) .Index(t => t.ForumPostId); - + } - + public override void Down() { DropForeignKey("dbo.ForumPostVote", "ForumPostId", "dbo.Forums_Post"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201810231214068_DataExchangeEnhancements.cs b/src/Libraries/SmartStore.Data/Migrations/201810231214068_DataExchangeEnhancements.cs index 1bdcda90d3..910446ec89 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201810231214068_DataExchangeEnhancements.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201810231214068_DataExchangeEnhancements.cs @@ -1,6 +1,5 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; using SmartStore.Data.Setup; @@ -14,7 +13,7 @@ public override void Up() AlterColumn("dbo.ScheduleTaskHistory", "MachineName", c => c.String(nullable: false, maxLength: 400)); CreateIndex("dbo.ScheduleTaskHistory", new[] { "MachineName", "IsRunning" }); } - + public override void Down() { DropIndex("dbo.ScheduleTaskHistory", new[] { "MachineName", "IsRunning" }); diff --git a/src/Libraries/SmartStore.Data/Migrations/201811061745204_IsSystemProductIndex.cs b/src/Libraries/SmartStore.Data/Migrations/201811061745204_IsSystemProductIndex.cs index a51d10dc8f..964d170410 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201811061745204_IsSystemProductIndex.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201811061745204_IsSystemProductIndex.cs @@ -1,6 +1,5 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; using SmartStore.Core.Data; @@ -15,7 +14,7 @@ public override void Up() } CreateIndex("dbo.Product", new[] { "Published", "Deleted", "IsSystemProduct" }, name: "IX_Product_Published_Deleted_IsSystemProduct"); } - + public override void Down() { DropIndex("dbo.Product", "IX_Product_Published_Deleted_IsSystemProduct"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201811082148279_LocalizedPropertyKeyGroupIndex.cs b/src/Libraries/SmartStore.Data/Migrations/201811082148279_LocalizedPropertyKeyGroupIndex.cs index b5aef74649..847bea3807 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201811082148279_LocalizedPropertyKeyGroupIndex.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201811082148279_LocalizedPropertyKeyGroupIndex.cs @@ -1,15 +1,14 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class LocalizedPropertyKeyGroupIndex : DbMigration { public override void Up() { CreateIndex("dbo.LocalizedProperty", "LocaleKeyGroup", name: "IX_LocalizedProperty_LocaleKeyGroup"); } - + public override void Down() { DropIndex("dbo.LocalizedProperty", "IX_LocalizedProperty_LocaleKeyGroup"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201811161142587_TaskHistoryErrorLength.cs b/src/Libraries/SmartStore.Data/Migrations/201811161142587_TaskHistoryErrorLength.cs index 6a1180c68d..6c6ba2e7a5 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201811161142587_TaskHistoryErrorLength.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201811161142587_TaskHistoryErrorLength.cs @@ -1,15 +1,14 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class TaskHistoryErrorLength : DbMigration { public override void Up() { AlterColumn("dbo.ScheduleTaskHistory", "Error", c => c.String()); } - + public override void Down() { AlterColumn("dbo.ScheduleTaskHistory", "Error", c => c.String(maxLength: 1000)); diff --git a/src/Libraries/SmartStore.Data/Migrations/201811202204501_ProductIndexSeekExport.cs b/src/Libraries/SmartStore.Data/Migrations/201811202204501_ProductIndexSeekExport.cs index e78d62dc3f..90640d3991 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201811202204501_ProductIndexSeekExport.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201811202204501_ProductIndexSeekExport.cs @@ -1,16 +1,15 @@ namespace SmartStore.Data.Migrations { - using SmartStore.Core.Data; - using System; using System.Data.Entity.Migrations; - + using SmartStore.Core.Data; + public partial class ProductIndexSeekExport : DbMigration { public override void Up() { - if (DataSettings.Current.IsSqlServer) - { - Sql(@"CREATE NONCLUSTERED INDEX [IX_SeekExport1] ON [dbo].[Product] + if (DataSettings.Current.IsSqlServer) + { + Sql(@"CREATE NONCLUSTERED INDEX [IX_SeekExport1] ON [dbo].[Product] ( [Published] ASC, [Id] ASC, @@ -21,15 +20,15 @@ public override void Up() [AvailableEndDateTimeUtc] ASC ) INCLUDE ([UpdatedOnUtc]) WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]"); - } + } } - public override void Down() - { - if (DataSettings.Current.IsSqlServer) - { - DropIndex("dbo.Product", "IX_SeekExport1"); - } - } + public override void Down() + { + if (DataSettings.Current.IsSqlServer) + { + DropIndex("dbo.Product", "IX_SeekExport1"); + } + } } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201902211855242_TopicHtmlIdAndBodyCss.cs b/src/Libraries/SmartStore.Data/Migrations/201902211855242_TopicHtmlIdAndBodyCss.cs index 83f8e64ce5..c0a44dcdc9 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201902211855242_TopicHtmlIdAndBodyCss.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201902211855242_TopicHtmlIdAndBodyCss.cs @@ -1,8 +1,7 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class TopicHtmlIdAndBodyCss : DbMigration { public override void Up() @@ -10,7 +9,7 @@ public override void Up() AddColumn("dbo.Topic", "HtmlId", c => c.String(maxLength: 128)); AddColumn("dbo.Topic", "BodyCssClass", c => c.String(maxLength: 512)); } - + public override void Down() { DropColumn("dbo.Topic", "BodyCssClass"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201904110735029_Menus.cs b/src/Libraries/SmartStore.Data/Migrations/201904110735029_Menus.cs index 645fdd9314..ae8714e7d7 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201904110735029_Menus.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201904110735029_Menus.cs @@ -12,56 +12,56 @@ public override void Up() CreateTable( "dbo.MenuRecord", c => new - { - Id = c.Int(nullable: false, identity: true), - SystemName = c.String(nullable: false, maxLength: 400), - IsSystemMenu = c.Boolean(nullable: false), - Template = c.String(maxLength: 400), - WidgetZone = c.String(maxLength: 4000), - Title = c.String(maxLength: 400), - Published = c.Boolean(nullable: false), - DisplayOrder = c.Int(nullable: false), - LimitedToStores = c.Boolean(nullable: false), - SubjectToAcl = c.Boolean(nullable: false), - }) + { + Id = c.Int(nullable: false, identity: true), + SystemName = c.String(nullable: false, maxLength: 400), + IsSystemMenu = c.Boolean(nullable: false), + Template = c.String(maxLength: 400), + WidgetZone = c.String(maxLength: 4000), + Title = c.String(maxLength: 400), + Published = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + LimitedToStores = c.Boolean(nullable: false), + SubjectToAcl = c.Boolean(nullable: false), + }) .PrimaryKey(t => t.Id) .Index(t => new { t.SystemName, t.IsSystemMenu }, name: "IX_Menu_SystemName_IsSystemMenu") .Index(t => t.Published, name: "IX_Menu_Published") .Index(t => t.LimitedToStores, name: "IX_Menu_LimitedToStores") .Index(t => t.SubjectToAcl, name: "IX_Menu_SubjectToAcl"); - + CreateTable( "dbo.MenuItemRecord", c => new - { - Id = c.Int(nullable: false, identity: true), - MenuId = c.Int(nullable: false), - ParentItemId = c.Int(nullable: false), - ProviderName = c.String(maxLength: 100), - Model = c.String(), - Title = c.String(maxLength: 400), - ShortDescription = c.String(maxLength: 400), - PermissionNames = c.String(), - Published = c.Boolean(nullable: false), - DisplayOrder = c.Int(nullable: false), - BeginGroup = c.Boolean(nullable: false), - ShowExpanded = c.Boolean(nullable: false), - NoFollow = c.Boolean(nullable: false), - NewWindow = c.Boolean(nullable: false), - Icon = c.String(maxLength: 100), - Style = c.String(maxLength: 10), - HtmlId = c.String(maxLength: 100), - CssClass = c.String(maxLength: 100), - }) + { + Id = c.Int(nullable: false, identity: true), + MenuId = c.Int(nullable: false), + ParentItemId = c.Int(nullable: false), + ProviderName = c.String(maxLength: 100), + Model = c.String(), + Title = c.String(maxLength: 400), + ShortDescription = c.String(maxLength: 400), + PermissionNames = c.String(), + Published = c.Boolean(nullable: false), + DisplayOrder = c.Int(nullable: false), + BeginGroup = c.Boolean(nullable: false), + ShowExpanded = c.Boolean(nullable: false), + NoFollow = c.Boolean(nullable: false), + NewWindow = c.Boolean(nullable: false), + Icon = c.String(maxLength: 100), + Style = c.String(maxLength: 10), + HtmlId = c.String(maxLength: 100), + CssClass = c.String(maxLength: 100), + }) .PrimaryKey(t => t.Id) .ForeignKey("dbo.MenuRecord", t => t.MenuId, cascadeDelete: true) .Index(t => t.MenuId) .Index(t => t.ParentItemId, name: "IX_MenuItem_ParentItemId") .Index(t => t.Published, name: "IX_MenuItem_Published") .Index(t => t.DisplayOrder, name: "IX_MenuItem_DisplayOrder"); - + } - + public override void Down() { DropForeignKey("dbo.MenuItemRecord", "MenuId", "dbo.MenuRecord"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201905020948354_WidgetTopics.cs b/src/Libraries/SmartStore.Data/Migrations/201905020948354_WidgetTopics.cs index 304479485c..d26335d830 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201905020948354_WidgetTopics.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201905020948354_WidgetTopics.cs @@ -1,6 +1,5 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; using System.Linq; using SmartStore.Core.Data; @@ -12,21 +11,18 @@ public partial class WidgetTopics : DbMigration, IDataSeeder public override void Up() { } - + public override void Down() { } - public bool RollbackOnFailure - { - get { return false; } - } + public bool RollbackOnFailure => false; public void Seed(SmartObjectContext context) { - using (var scope = new DbContextScope(ctx: context, validateOnSave: false, hooksEnabled: false)) - { - var widgetTopics = new[] + using (var scope = new DbContextScope(ctx: context, validateOnSave: false, hooksEnabled: false)) + { + var widgetTopics = new[] { "CheckoutAsGuestOrRegister", "ContactUs", @@ -36,14 +32,14 @@ public void Seed(SmartObjectContext context) }; var topics = context.Set().Where(x => widgetTopics.Contains(x.SystemName)).ToList(); - topics.Each(x => - { + topics.Each(x => + { x.RenderAsWidget = true; x.WidgetWrapContent = false; }); context.SaveChanges(); - } - } + } + } } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201905101159134_V320Resources.cs b/src/Libraries/SmartStore.Data/Migrations/201905101159134_V320Resources.cs index 231a748e84..88c8d19d1b 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201905101159134_V320Resources.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201905101159134_V320Resources.cs @@ -2,24 +2,21 @@ namespace SmartStore.Data.Migrations { using System.Data.Entity.Migrations; using SmartStore.Core.Domain.Catalog; + using SmartStore.Core.Domain.Common; using SmartStore.Data.Setup; using SmartStore.Utilities; - using SmartStore.Core.Domain.Common; public partial class V320Resources : DbMigration, ILocaleResourcesProvider, IDataSeeder { public override void Up() { } - + public override void Down() { } - public bool RollbackOnFailure - { - get { return false; } - } + public bool RollbackOnFailure => false; public void Seed(SmartObjectContext context) { @@ -190,7 +187,6 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Admin.Customers.Customers.Fields.Fax.Required", "Admin.Validation.Name", "Admin.Validation.EmailAddress", - "Admin.Validation.Url", "Admin.Validation.UsernamePassword", "Admin.DataExchange.Export.FileNamePattern.Validate", "Admin.DataExchange.Export.Partition.Validate", diff --git a/src/Libraries/SmartStore.Data/Migrations/201905271110370_V321Resources.cs b/src/Libraries/SmartStore.Data/Migrations/201905271110370_V321Resources.cs index 89ade79f68..4bd176f7f9 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201905271110370_V321Resources.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201905271110370_V321Resources.cs @@ -1,6 +1,5 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; using SmartStore.Data.Setup; @@ -9,15 +8,12 @@ public partial class V321Resources : DbMigration, ILocaleResourcesProvider, IDat public override void Up() { } - + public override void Down() { } - public bool RollbackOnFailure - { - get { return false; } - } + public bool RollbackOnFailure => false; public void Seed(SmartObjectContext context) { diff --git a/src/Libraries/SmartStore.Data/Migrations/201906252008551_QuantityUnitNamePlural.cs b/src/Libraries/SmartStore.Data/Migrations/201906252008551_QuantityUnitNamePlural.cs index 313530a024..5f5ee9c386 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201906252008551_QuantityUnitNamePlural.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201906252008551_QuantityUnitNamePlural.cs @@ -8,11 +8,11 @@ namespace SmartStore.Data.Migrations using SmartStore.Core.Domain.Localization; using SmartStore.Data.Setup; - public partial class QuantityUnitNamePlural : DbMigration, IDataSeeder + public partial class QuantityUnitNamePlural : DbMigration, ILocaleResourcesProvider, IDataSeeder { public override void Up() { - AddColumn("dbo.QuantityUnit", "NamePlural", c => c.String(nullable: false, maxLength: 50, defaultValue: "" )); + AddColumn("dbo.QuantityUnit", "NamePlural", c => c.String(nullable: false, maxLength: 50, defaultValue: "")); } public override void Down() diff --git a/src/Libraries/SmartStore.Data/Migrations/201907032251575_CategoryExternalLink.cs b/src/Libraries/SmartStore.Data/Migrations/201907032251575_CategoryExternalLink.cs index 708ca38b83..f856a343e3 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201907032251575_CategoryExternalLink.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201907032251575_CategoryExternalLink.cs @@ -1,15 +1,14 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class CategoryExternalLink : DbMigration { public override void Up() { AddColumn("dbo.Category", "ExternalLink", c => c.String(maxLength: 255)); } - + public override void Down() { DropColumn("dbo.Category", "ExternalLink"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201907221803421_GranularPermissions.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201907221803421_GranularPermissions.Designer.cs new file mode 100644 index 0000000000..e0cda0f6b7 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201907221803421_GranularPermissions.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 GranularPermissions : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(GranularPermissions)); + + string IMigrationMetadata.Id + { + get { return "201907221803421_GranularPermissions"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201907221803421_GranularPermissions.cs b/src/Libraries/SmartStore.Data/Migrations/201907221803421_GranularPermissions.cs new file mode 100644 index 0000000000..689a322ec2 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201907221803421_GranularPermissions.cs @@ -0,0 +1,48 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using SmartStore.Core.Data; + using SmartStore.Data.Setup; + using SmartStore.Data.Utilities; + + public partial class GranularPermissions : DbMigration, IDataSeeder + { + public override void Up() + { + CreateTable( + "dbo.PermissionRoleMapping", + c => new + { + Id = c.Int(nullable: false, identity: true), + Allow = c.Boolean(nullable: false), + PermissionRecordId = c.Int(nullable: false), + CustomerRoleId = c.Int(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.CustomerRole", t => t.CustomerRoleId, cascadeDelete: true) + .ForeignKey("dbo.PermissionRecord", t => t.PermissionRecordId, cascadeDelete: true) + .Index(t => t.PermissionRecordId) + .Index(t => t.CustomerRoleId); + + } + + public override void Down() + { + DropForeignKey("dbo.PermissionRoleMapping", "PermissionRecordId", "dbo.PermissionRecord"); + DropForeignKey("dbo.PermissionRoleMapping", "CustomerRoleId", "dbo.CustomerRole"); + DropIndex("dbo.PermissionRoleMapping", new[] { "CustomerRoleId" }); + DropIndex("dbo.PermissionRoleMapping", new[] { "PermissionRecordId" }); + DropTable("dbo.PermissionRoleMapping"); + } + + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + if (DataSettings.DatabaseIsInstalled()) + { + DataMigrator.AddGranularPermissions(context); + } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201907221803421_GranularPermissions.resx b/src/Libraries/SmartStore.Data/Migrations/201907221803421_GranularPermissions.resx new file mode 100644 index 0000000000..3810b41e63 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201907221803421_GranularPermissions.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/201907250103367_RuleSystem.cs b/src/Libraries/SmartStore.Data/Migrations/201907250103367_RuleSystem.cs index c1489ce12e..e51e96adb5 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201907250103367_RuleSystem.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201907250103367_RuleSystem.cs @@ -1,8 +1,7 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class RuleSystem : DbMigration { public override void Up() @@ -10,40 +9,40 @@ public override void Up() CreateTable( "dbo.RuleSet", c => new - { - Id = c.Int(nullable: false, identity: true), - Name = c.String(maxLength: 200), - Description = c.String(maxLength: 400), - IsActive = c.Boolean(nullable: false), - Scope = c.Int(nullable: false), - IsSubGroup = c.Boolean(nullable: false), - LogicalOperator = c.Int(nullable: false), - CreatedOnUtc = c.DateTime(nullable: false), - UpdatedOnUtc = c.DateTime(nullable: false), - LastProcessedOnUtc = c.DateTime(), - }) + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(maxLength: 200), + Description = c.String(maxLength: 400), + IsActive = c.Boolean(nullable: false), + Scope = c.Int(nullable: false), + IsSubGroup = c.Boolean(nullable: false), + LogicalOperator = c.Int(nullable: false), + CreatedOnUtc = c.DateTime(nullable: false), + UpdatedOnUtc = c.DateTime(nullable: false), + LastProcessedOnUtc = c.DateTime(), + }) .PrimaryKey(t => t.Id) .Index(t => new { t.IsActive, t.Scope }, name: "IX_RuleSetEntity_Scope"); - + CreateTable( "dbo.Rule", c => new - { - Id = c.Int(nullable: false, identity: true), - RuleSetId = c.Int(nullable: false), - RuleType = c.String(nullable: false, maxLength: 100), - Operator = c.String(nullable: false, maxLength: 10), - Value = c.String(maxLength: 400), - DisplayOrder = c.Int(nullable: false), - }) + { + Id = c.Int(nullable: false, identity: true), + RuleSetId = c.Int(nullable: false), + RuleType = c.String(nullable: false, maxLength: 100), + Operator = c.String(nullable: false, maxLength: 10), + Value = c.String(), + DisplayOrder = c.Int(nullable: false), + }) .PrimaryKey(t => t.Id) .ForeignKey("dbo.RuleSet", t => t.RuleSetId, cascadeDelete: true) .Index(t => t.RuleSetId) .Index(t => t.RuleType, name: "IX_PageBuilder_RuleType") .Index(t => t.DisplayOrder, name: "IX_PageBuilder_DisplayOrder"); - + } - + public override void Down() { DropForeignKey("dbo.Rule", "RuleSetId", "dbo.RuleSet"); diff --git a/src/Libraries/SmartStore.Data/Migrations/201908050758298_MoveFurtherCustomerFields.cs b/src/Libraries/SmartStore.Data/Migrations/201908050758298_MoveFurtherCustomerFields.cs index b95b01129b..020c785b60 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201908050758298_MoveFurtherCustomerFields.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201908050758298_MoveFurtherCustomerFields.cs @@ -1,10 +1,11 @@ namespace SmartStore.Data.Migrations { - using SmartStore.Data.Setup; - using SmartStore.Data.Utilities; using System; + using System.Collections.Generic; using System.Data.Entity.Migrations; - + using SmartStore.Data.Setup; + using SmartStore.Data.Utilities; + public partial class MoveFurtherCustomerFields : DbMigration, IDataSeeder { public override void Up() @@ -21,7 +22,7 @@ public override void Up() AddColumn("dbo.Customer", "LastUserAgent", c => c.String()); AddColumn("dbo.Customer", "LastUserDeviceType", c => c.String()); } - + public override void Down() { DropColumn("dbo.Customer", "LastUserDeviceType"); @@ -41,7 +42,38 @@ public override void Down() public void Seed(SmartObjectContext context) { - DataMigrator.MoveFurtherCustomerFields(context); + // Perf + var numDeletedAttrs = DataMigrator.DeleteGuestCustomerGenericAttributes(context, TimeSpan.FromDays(30)); + var numDeletedCustomers = DataMigrator.DeleteGuestCustomers(context, TimeSpan.FromDays(30)); + + var candidates = new[] { "Gender", "VatNumberStatusId", "TimeZoneId", "TaxDisplayTypeId", "LastForumVisit", "LastUserAgent", "LastUserDeviceType" }; + var numUpdatedCustomers = DataMigrator.MoveCustomerFields(context, UpdateCustomer, candidates); + } + + private static void UpdateCustomer(IDictionary columns, string key, string value) + { + switch (key) + { + case "Gender": + columns[key] = value?.Truncate(100); + break; + case "VatNumberStatusId": + columns[key] = value.Convert(); + break; + case "TimeZoneId": + columns[key] = value?.Truncate(255); + break; + case "TaxDisplayTypeId": + columns[key] = value.Convert(); + break; + case "LastForumVisit": + columns[key] = value.Convert(); + break; + case "LastUserAgent": + case "LastUserDeviceType": + columns[key] = value; + break; + } } } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201908150749388_V322Resources.cs b/src/Libraries/SmartStore.Data/Migrations/201908150749388_V322Resources.cs index 12a38b031e..81056149ca 100644 --- a/src/Libraries/SmartStore.Data/Migrations/201908150749388_V322Resources.cs +++ b/src/Libraries/SmartStore.Data/Migrations/201908150749388_V322Resources.cs @@ -1,6 +1,5 @@ namespace SmartStore.Data.Migrations { - using System; using System.Data.Entity.Migrations; using SmartStore.Data.Setup; @@ -14,10 +13,7 @@ public override void Down() { } - public bool RollbackOnFailure - { - get { return false; } - } + public bool RollbackOnFailure => false; public void Seed(SmartObjectContext context) { @@ -62,8 +58,8 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Bitte berprfen Sie die Link-Daten. Auf Basis Ihrer Eingabe kann kein Link erzeugt werden."); builder.AddOrUpdate("Admin.Packaging.IsIncompatible", - "The package is not compatible the current app version {0}. Please update Smartstore.NET or install another version of this package.", - "Das Paket ist nicht kompatibel mit der aktuellen Programmversion {0}. Bitte aktualisieren Sie Smartstore.NET oder nutzen Sie eine andere, kompatible Paket-Version."); + "The package is not compatible the current app version {0}. Please update Smartstore or install another version of this package.", + "Das Paket ist nicht kompatibel mit der aktuellen Programmversion {0}. Bitte aktualisieren Sie Smartstore oder nutzen Sie eine andere, kompatible Paket-Version."); } } } diff --git a/src/Libraries/SmartStore.Data/Migrations/201908211821559_Merge5.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201908211821559_Merge5.Designer.cs new file mode 100644 index 0000000000..2689514cb0 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201908211821559_Merge5.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 Merge5 : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(Merge5)); + + string IMigrationMetadata.Id + { + get { return "201908211821559_Merge5"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201908211821559_Merge5.cs b/src/Libraries/SmartStore.Data/Migrations/201908211821559_Merge5.cs new file mode 100644 index 0000000000..ee1db0c32e --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201908211821559_Merge5.cs @@ -0,0 +1,15 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class Merge5 : DbMigration + { + public override void Up() + { + } + + public override void Down() + { + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201908211821559_Merge5.resx b/src/Libraries/SmartStore.Data/Migrations/201908211821559_Merge5.resx new file mode 100644 index 0000000000..5d6b6292c0 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201908211821559_Merge5.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/201908211825244_ProductTagPublished.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201908211825244_ProductTagPublished.Designer.cs new file mode 100644 index 0000000000..8d61f6d810 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201908211825244_ProductTagPublished.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 ProductTagPublished : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(ProductTagPublished)); + + string IMigrationMetadata.Id + { + get { return "201908211825244_ProductTagPublished"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201908211825244_ProductTagPublished.cs b/src/Libraries/SmartStore.Data/Migrations/201908211825244_ProductTagPublished.cs new file mode 100644 index 0000000000..b3d720ca95 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201908211825244_ProductTagPublished.cs @@ -0,0 +1,111 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using System.Web.Hosting; + using SmartStore.Core.Data; + using SmartStore.Data.Setup; + + public partial class ProductTagPublished : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + AddColumn("dbo.ProductTag", "Published", c => c.Boolean(nullable: false, defaultValue: true)); + CreateIndex("dbo.ProductTag", "Published", name: "IX_ProductTag_Published"); + + if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) + { + Sql(GetAlterTagCountProcedureSql(true)); + } + } + + public override void Down() + { + DropIndex("dbo.ProductTag", "IX_ProductTag_Published"); + DropColumn("dbo.ProductTag", "Published"); + + if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) + { + Sql(GetAlterTagCountProcedureSql(false)); + } + } + + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + context.SaveChanges(); + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate( + "Admin.Catalog.ProductTags.Published", + "Published", + "Verffentlicht", + "Tags that have not been published are not visible in the shop, but are taken into account in the product search.", + "Nicht verffentlichte Tags sind im Shop nicht sichtbar, werden aber bei der Produktsuche bercksichtigt."); + } + + private string GetAlterTagCountProcedureSql(bool newVersion) + { + string result = null; + + if (newVersion) + { + result = + "ALTER PROCEDURE [dbo].[ProductTagCountLoadAll]\r\n" + + "(\r\n" + + " @StoreId int,\r\n" + + " @IncludeHidden bit = 0\r\n" + + ")\r\n" + + "AS\r\n" + + "BEGIN\r\n" + + " SET NOCOUNT ON\r\n" + + " SELECT pt.Id as [ProductTagId], COUNT(p.Id) as [ProductCount]\r\n" + + " FROM ProductTag pt with (NOLOCK)\r\n" + + " LEFT JOIN Product_ProductTag_Mapping pptm with (NOLOCK) ON pt.[Id] = pptm.[ProductTag_Id]\r\n" + + " LEFT JOIN Product p with (NOLOCK) ON pptm.[Product_Id] = p.[Id]\r\n" + + " WHERE\r\n" + + " p.[Deleted] = 0\r\n" + + " AND p.Published = 1\r\n" + + " AND p.VisibleIndividually = 1\r\n" + + " AND (@IncludeHidden = 1 Or pt.Published = 1)\r\n" + + " AND (@StoreId = 0 or (p.LimitedToStores = 0 OR EXISTS (\r\n" + + " SELECT 1 FROM [StoreMapping] sm\r\n" + + " WHERE [sm].EntityId = p.Id AND [sm].EntityName = 'Product' and [sm].StoreId=@StoreId\r\n" + + " )))\r\n" + + " GROUP BY pt.Id\r\n" + + " ORDER BY pt.Id\r\n" + + "END\r\n"; + } + else + { + result = + "ALTER PROCEDURE [dbo].[ProductTagCountLoadAll]\r\n" + + "(\r\n" + + " @StoreId int\r\n" + + ")\r\n" + + "AS\r\n" + + "BEGIN\r\n" + + " SET NOCOUNT ON\r\n" + + " SELECT pt.Id as [ProductTagId], COUNT(p.Id) as [ProductCount]\r\n" + + " FROM ProductTag pt with (NOLOCK)\r\n" + + " LEFT JOIN Product_ProductTag_Mapping pptm with (NOLOCK) ON pt.[Id] = pptm.[ProductTag_Id]\r\n" + + " LEFT JOIN Product p with (NOLOCK) ON pptm.[Product_Id] = p.[Id]\r\n" + + " WHERE\r\n" + + " p.[Deleted] = 0\r\n" + + " AND p.Published = 1\r\n" + + " AND (@StoreId = 0 or (p.LimitedToStores = 0 OR EXISTS (\r\n" + + " SELECT 1 FROM [StoreMapping] sm\r\n" + + " WHERE [sm].EntityId = p.Id AND [sm].EntityName = 'Product' and [sm].StoreId=@StoreId\r\n" + + " )))\r\n" + + " GROUP BY pt.Id\r\n" + + " ORDER BY pt.Id\r\n" + + "END\r\n"; + } + + return result; + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201908211825244_ProductTagPublished.resx b/src/Libraries/SmartStore.Data/Migrations/201908211825244_ProductTagPublished.resx new file mode 100644 index 0000000000..3fa645579d --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201908211825244_ProductTagPublished.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/201908261226350_MenuItemMultistore.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201908261226350_MenuItemMultistore.Designer.cs new file mode 100644 index 0000000000..8d24538a98 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201908261226350_MenuItemMultistore.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 MenuItemMultistore : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(MenuItemMultistore)); + + string IMigrationMetadata.Id + { + get { return "201908261226350_MenuItemMultistore"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201908261226350_MenuItemMultistore.cs b/src/Libraries/SmartStore.Data/Migrations/201908261226350_MenuItemMultistore.cs new file mode 100644 index 0000000000..c5948be73d --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201908261226350_MenuItemMultistore.cs @@ -0,0 +1,46 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using SmartStore.Data.Setup; + + public partial class MenuItemMultistore : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + AddColumn("dbo.MenuItemRecord", "IconColor", c => c.String(maxLength: 100)); + AddColumn("dbo.MenuItemRecord", "LimitedToStores", c => c.Boolean(nullable: false)); + AddColumn("dbo.MenuItemRecord", "SubjectToAcl", c => c.Boolean(nullable: false)); + CreateIndex("dbo.MenuItemRecord", "LimitedToStores", name: "IX_MenuItem_LimitedToStores"); + CreateIndex("dbo.MenuItemRecord", "SubjectToAcl", name: "IX_MenuItem_SubjectToAcl"); + } + + public override void Down() + { + DropIndex("dbo.MenuItemRecord", "IX_MenuItem_SubjectToAcl"); + DropIndex("dbo.MenuItemRecord", "IX_MenuItem_LimitedToStores"); + DropColumn("dbo.MenuItemRecord", "SubjectToAcl"); + DropColumn("dbo.MenuItemRecord", "LimitedToStores"); + DropColumn("dbo.MenuItemRecord", "IconColor"); + } + + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + context.SaveChanges(); + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate( + "Admin.ContentManagement.Menus.Item.IconColor", + "Icon color", + "Icon Farbe", + "Specifies the color of the icon.", + "Legt die Farbe des Icons fest."); + + builder.AddOrUpdate("Common.White", "White", "Wei"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201908261226350_MenuItemMultistore.resx b/src/Libraries/SmartStore.Data/Migrations/201908261226350_MenuItemMultistore.resx new file mode 100644 index 0000000000..b284d36789 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201908261226350_MenuItemMultistore.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/201909291043284_BlogAndNewsItemPictures.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201909291043284_BlogAndNewsItemPictures.Designer.cs new file mode 100644 index 0000000000..01d2f697a9 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201909291043284_BlogAndNewsItemPictures.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 BlogAndNewsItemPictures : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(BlogAndNewsItemPictures)); + + string IMigrationMetadata.Id + { + get { return "201909291043284_BlogAndNewsItemPictures"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201909291043284_BlogAndNewsItemPictures.cs b/src/Libraries/SmartStore.Data/Migrations/201909291043284_BlogAndNewsItemPictures.cs new file mode 100644 index 0000000000..8dc2ea6c66 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201909291043284_BlogAndNewsItemPictures.cs @@ -0,0 +1,118 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using SmartStore.Data.Setup; + + public partial class BlogAndNewsItemPictures : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + AddColumn("dbo.BlogPost", "PictureId", c => c.Int()); + AddColumn("dbo.BlogPost", "PreviewPictureId", c => c.Int()); + AddColumn("dbo.BlogPost", "SectionBg", c => c.String(maxLength: 100)); + AddColumn("dbo.BlogPost", "Intro", c => c.String()); + AddColumn("dbo.BlogPost", "DisplayTagsInPreview", c => c.Boolean(nullable: false)); + AddColumn("dbo.BlogPost", "IsPublished", c => c.Boolean(nullable: false, defaultValue: true)); + AddColumn("dbo.BlogPost", "PreviewDisplayType", c => c.Int(nullable: false)); + AddColumn("dbo.News", "PictureId", c => c.Int()); + AddColumn("dbo.News", "PreviewPictureId", c => c.Int()); + } + + public override void Down() + { + DropColumn("dbo.News", "PreviewPictureId"); + DropColumn("dbo.News", "PictureId"); + DropColumn("dbo.BlogPost", "PreviewDisplayType"); + DropColumn("dbo.BlogPost", "IsPublished"); + DropColumn("dbo.BlogPost", "DisplayTagsInPreview"); + DropColumn("dbo.BlogPost", "Intro"); + DropColumn("dbo.BlogPost", "SectionBg"); + DropColumn("dbo.BlogPost", "PreviewPictureId"); + DropColumn("dbo.BlogPost", "PictureId"); + } + + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + context.SaveChanges(); + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("Admin.ContentManagement.Blog.BlogPosts.Fields.Intro", + "Intro", + "Intro", + "Specifies the intro of the blog post.", + "Legt das Intro des Blog-Posts fest."); + + builder.AddOrUpdate("Admin.ContentManagement.Blog.BlogPosts.Fields.Picture", + "Picture", + "Bild", + "Specifies the picture of the blog post.", + "Legt das Bild des Blog-Posts fest."); + + builder.AddOrUpdate("Admin.ContentManagement.Blog.BlogPosts.Fields.PreviewPicture", + "Preview picture", + "Vorschaubild", + "Specifies the preview picture of the blog post.", + "Legt das Vorschaubild des Blog-Posts fest."); + + builder.AddOrUpdate("Admin.ContentManagement.Blog.BlogPosts.Fields.SectionBg", + "Background color", + "Hintergrundfarbe", + "Specifies the background color of the blog post.", + "Legt die Hintergrundfarbe des Blog-Posts fest."); + + builder.AddOrUpdate("Admin.ContentManagement.News.NewsItems.Fields.Picture", + "Picture", + "Bild", + "Specifies the picture of the news item.", + "Legt das Bild des News-Eintrags fest."); + + builder.AddOrUpdate("Admin.ContentManagement.News.NewsItems.Fields.PreviewPicture", + "Preview picture", + "Vorschaubild", + "Specifies the preview picture of the news item.", + "Legt das Vorschaubild des News-Eintrags fest."); + + builder.AddOrUpdate("Admin.ContentManagement.News.Blog.BlogPosts.Fields.PreviewDisplayType", + "Preview display type", + "Vorschau-Darstellung", + "Specifies display type of the preview for a blog item.", + "Legt die Darstellung der Vorschau fr einen Blog-Eintrag fest."); + + builder.AddOrUpdate("Admin.ContentManagement.News.Blog.BlogPosts.Fields.DisplayTagsInPreview", + "Display tags on preview", + "Tags in Vorschau anzeigen", + "Specifies whether tags are display in the preview for blog item.", + "Bestimmt, ob Tags in der Vorschau eines Blog-Eintrags angezeigt werden."); + + builder.AddOrUpdate("Admin.ContentManagement.News.Blog.BlogPosts.Fields.IsPublished", + "Is published", + "Verffentlicht", + "Specifies whether the blog post is published and thus will be displayed in the frontend", + "Bestimmt, ob der Blog-Eintrag verffentlicht ist und somit im Frontend dargestellt wird."); + + builder.AddOrUpdate("Common.Cms.EditBlogPost", + "Edit blog post", + "Blog-Post bearbeiten"); + + builder.AddOrUpdate("Common.Cms.EditNewsItem", + "Edit news item", + "News-Eintrag bearbeiten"); + + builder.AddOrUpdate("Common.Cms.ReadMore", + "Read more", + "Mehr lesen"); + + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Blogs.PreviewDisplayType.Bare", "Minimal (no image, no background)", "Minimal (kein Bild, kein Hintergrund)"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Blogs.PreviewDisplayType.Default", "Picture over text", "Bild ber Text"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Blogs.PreviewDisplayType.Preview", "Preview picture over text", "Galleriebild ber Text"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Blogs.PreviewDisplayType.DefaultSectionBg", "Picture behind text", "Bild hinter Text"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Blogs.PreviewDisplayType.PreviewSectionBg", "Preview picture behind text", "Galleriebild hinter Text"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Blogs.PreviewDisplayType.SectionBg", "Background color", "Hintergrundfarbe"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201909291043284_BlogAndNewsItemPictures.resx b/src/Libraries/SmartStore.Data/Migrations/201909291043284_BlogAndNewsItemPictures.resx new file mode 100644 index 0000000000..9c76f69c69 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201909291043284_BlogAndNewsItemPictures.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/201910021805242_RemoveOldPermissions.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201910021805242_RemoveOldPermissions.Designer.cs new file mode 100644 index 0000000000..ea13123073 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201910021805242_RemoveOldPermissions.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 RemoveOldPermissions : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(RemoveOldPermissions)); + + string IMigrationMetadata.Id + { + get { return "201910021805242_RemoveOldPermissions"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201910021805242_RemoveOldPermissions.cs b/src/Libraries/SmartStore.Data/Migrations/201910021805242_RemoveOldPermissions.cs new file mode 100644 index 0000000000..c9b18aac65 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201910021805242_RemoveOldPermissions.cs @@ -0,0 +1,91 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using System.Web.Hosting; + using SmartStore.Core.Data; + using SmartStore.Core.Domain.Tasks; + using SmartStore.Data.Setup; + + public partial class RemoveOldPermissions : DbMigration, IDataSeeder + { + public override void Up() + { + //if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) + //{ + // Sql("DELETE FROM [dbo].[PermissionRecord] WHERE [Name] <> '' AND [Name] IS NOT NULL"); + //} + + //DropForeignKey("dbo.PermissionRecord_Role_Mapping", "PermissionRecord_Id", "dbo.PermissionRecord"); + //DropForeignKey("dbo.PermissionRecord_Role_Mapping", "CustomerRole_Id", "dbo.CustomerRole"); + //DropIndex("dbo.PermissionRecord_Role_Mapping", new[] { "PermissionRecord_Id" }); + //DropIndex("dbo.PermissionRecord_Role_Mapping", new[] { "CustomerRole_Id" }); + //DropColumn("dbo.PermissionRecord", "Name"); + //DropColumn("dbo.PermissionRecord", "Category"); + //DropTable("dbo.PermissionRecord_Role_Mapping"); + } + + public override void Down() + { + //CreateTable( + // "dbo.PermissionRecord_Role_Mapping", + // c => new + // { + // PermissionRecord_Id = c.Int(nullable: false), + // CustomerRole_Id = c.Int(nullable: false), + // }) + // .PrimaryKey(t => new { t.PermissionRecord_Id, t.CustomerRole_Id }); + + //AddColumn("dbo.PermissionRecord", "Category", c => c.String(nullable: false, maxLength: 255)); + //AddColumn("dbo.PermissionRecord", "Name", c => c.String(nullable: false)); + //CreateIndex("dbo.PermissionRecord_Role_Mapping", "CustomerRole_Id"); + //CreateIndex("dbo.PermissionRecord_Role_Mapping", "PermissionRecord_Id"); + //AddForeignKey("dbo.PermissionRecord_Role_Mapping", "CustomerRole_Id", "dbo.CustomerRole", "Id", cascadeDelete: true); + //AddForeignKey("dbo.PermissionRecord_Role_Mapping", "PermissionRecord_Id", "dbo.PermissionRecord", "Id", cascadeDelete: true); + } + + public bool RollbackOnFailure => false; + + public void Seed(SmartObjectContext context) + { + try + { + // Note, core scheduled tasks must always be added to the installation as well! + context.Set().AddOrUpdate(x => x.Type, + new ScheduleTask + { + Name = "Rebuild XML Sitemap", + CronExpression = "45 3 * * *", + Type = "SmartStore.Services.Seo.RebuildXmlSitemapTask, SmartStore.Services", + Enabled = true, + StopOnError = false + } + ); + context.SaveChanges(); + } + catch { } + + if (!HostingEnvironment.IsHosted || !DataSettings.Current.IsSqlServer) + { + return; + } + + Execute(context, "DELETE FROM [dbo].[PermissionRecord] WHERE [Name] <> '' AND [Name] IS NOT NULL"); + Execute(context, "Alter Table [dbo].[PermissionRecord_Role_Mapping] Drop Constraint [FK_dbo.PermissionRecord_Role_Mapping_dbo.PermissionRecord_PermissionRecord_Id]"); + Execute(context, "Alter Table [dbo].[PermissionRecord_Role_Mapping] Drop Constraint [FK_dbo.PermissionRecord_Role_Mapping_dbo.CustomerRole_CustomerRole_Id]"); + Execute(context, "Drop Index [IX_PermissionRecord_Id] ON [dbo].[PermissionRecord_Role_Mapping]"); + Execute(context, "Drop Index [IX_CustomerRole_Id] ON [dbo].[PermissionRecord_Role_Mapping]"); + Execute(context, "Alter Table [dbo].[PermissionRecord] Drop Column [Name]"); + Execute(context, "Alter Table [dbo].[PermissionRecord] Drop Column [Category]"); + Execute(context, "Drop Table [dbo].[PermissionRecord_Role_Mapping]"); + } + + private void Execute(SmartObjectContext context, string sql) + { + try + { + context.ExecuteSqlCommand(sql); + } + catch { } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201910021805242_RemoveOldPermissions.resx b/src/Libraries/SmartStore.Data/Migrations/201910021805242_RemoveOldPermissions.resx new file mode 100644 index 0000000000..740098858f --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201910021805242_RemoveOldPermissions.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 + + + H4sIAAAAAAAEAOy9224dObIo+D7A/IPhp3MG+9hlVzewd6NqDiRZLgvbF7Uk22f3i5DORUls58pcnRdZ6sF82TzMJ80vDJlXXoJ3Zq6Ue6EA11IyGCSDwWAwGIz4//6f//e3//mwzZ7do7LCRf7781cvfnn+DOVpscH57e/Pm/rmf/z78//5f/7v/9tvp5vtw7MvA9yvFI7UzKvfn9/V9e4vL19W6R3aJtWLLU7Loipu6hdpsX2ZbIqXr3/55T9evnr1EhEUzwmuZ89+u2jyGm9R+wf586TIU7SrmyT7UGxQVvXfSclli/XZx2SLql2Sot+fX26Tsr6sixK9eJPUyfNnRxlOSDcuUXbz/FmS50Wd1KSTf/lcocu6LPLbyx35kGRXjztE4G6SrEJ95/8ygduO45fXdBwvp4oDqrSp6mLriPDVrz1hXorVvcj7fCQcId0pIXH9SEfdku/351fFDqfPn4kt/eUkKykUR9qTlr4EDOcv2npV979/eyYA/dvIFL++eE3+++Xfnp00Wd2U6PccNXWZZP/27Lz5luH0P9HjVfEd5b/nTZaxPSV9JWXcB/LpvCx2qKwfL9BN3/+zzfNnL/l6L8WKYzWmTje4s7z+9fXzZx9J48m3DI2MwBCiHdUfKEdlUqPNeVLXqMwpDtSSUmpdaOvysarRlv4e2iT8R9bR82cfkof3KL+t735/Tn4+f/YWP6DN8KXvx+cck2VHKtVlg0xNnVVdY/2Udq0dF0WGkhwYox7Zu3qbTXSarc/HxebxpKpOsqSq5idQnmbNBp3ll5iQKdmF0uisOifd/lGUG1JQo5TwRyjKAeHstLjCdTY/S17eFWVtaurPv0SZWyLWNY28/vOfI7Hr7ET7gOqEiDDKBvOvCdrYG1SlJd51O8wC7S3De+/xlizzzVXRSvAqdGVeoHyDyqPqK97cojoUW4flb0U+Px26pr6WyY5oVDXZtaS+29QnK/kHN29hIz8mzI3KCPKyxEXZbsP6Dd1CGF4lt/PLw+bb38k+cVUcpVmE3YeqUNWdKxV/ezlpgXrdMHk4IUrPbVE++miIycMLBsNBSVS3ZVAP//SL3Q7pyEBvcLXLksdPdCW6rB9r/rlEdd2OxZl3iKS6wbdN2UK/6PEcOMibg17Pw0FfkqyJsYE5NtvSykxdL559X6RJhv+JNkObHtzb4+iYV0J4YGN1W910uE0toPkl+W2T3DqyCICHTh0i9PmjLJrd8gJ6bH9fTS+1vj8m9/i2ZSDFTD5/doGyFqC6w7vOrievrOsJ/G1ZbC+KDFrQI9T1ZdGUKR1fYQS9SspW6/eTKWO3AkVJj+cgQdRtGTbCVzMtl35meqprd+I52ie1/9GgS1ScEBy61iOcYd5mye3Zlgz2Lc6QgdxRTDsXdfBZKbItwPPgFU8F18rMVnR3k3GBqlbGVWoBKkCqZagKcJSNnBhVQg9C1187E1BHUdAEnAcJa5Z1odrVQOv9HF2G1vd0hDmr6Oo6z5pbnEtCxFT1qmhSSPjE06rChQKoWxlFiJdQOEflFld0cV6gtL07cRYIlyhtqBnxhYjrIAjUbVldbdreu3hvd8yMEUb7kOx2pAvwfgeCXsvsM/G4XQ1pB7SsFrQVgm1EYv0J4YH/1W0dZVnxI1iVFJgidFc9aecflXQOnXEpFxmLFVxaLMC1aklO68oCXFpUNnWgFWUpPPq1v6TYEPdIR2njJTb4mXQ3zPfVK27GD1JC3dZC+6N4SC8RuiQs3G0KYSLqKnk4fUDbXfClN0HUH3gpHmEKDLI2rfF98N3z4CHVMX8YrqjazwzKzgwSWZRXLlLcT8cpiExwF1K0WtX+exBM6rZineMj+TZ6Lb/eTWp2v41oBj/qRvMpf0dWzXl7nA7D1mqgfzSoqqur4ktRByMMsEdKd7Rk3b0hjPm5Hn1R6Z9XeGuse5pvPGuG2nn9TCZU0oAmEq5AVve4UkhQarcCUvsor34QcabsVFd+3YlRvltMkXx8FcrDzqkjsiBJ3qE4yHONjCJUepqy/GOz/YbKTzdUglVhA5jjQqVbPmFLDFr70BJ06RMhV2tMTTtHYUUHWahrdjHynVWAgbJBBRssJ1jEQdKCRRRNZjw7TirUd4DVsAe3WuPq7MgUzyrDzuY8sw+xrYlTgswSUUwSh11C3dZAoz8aPLba/XZ1OahIu6bb/xjX/6dklrPZW7F4eBO3obdFuU3q0A17wHaZZPXsXT/abHF+Umy3zCOCGV+zRbM7Hd3c4AyT5RJK7ThWqDcoQxGeig3GrKM0LRrgVccc9qw4fPQ+qeqz3dFmQ45ouhdOts5aBolXIiopP+XgedLZ0auq3xe3OPc9oJL6LRcRUa1E4TpzSdYM74Pn9cQyPdyKM2FvcVnVJsPq6xjP+ehsLNLQWzKjpkceUWaISOddkuschuM8hBzUiO7oNjtLHOOyvqMLxHW9/dG+2pt9s/qS1B0lLslCbKrQfYaOjL4NXOABNn8nE+55X9VEsWm2X3CFax/hSNXKo9slFIyhtTfoHqeIvZGK16Ty6NZvfgrHzGF1XTNg8lXNVKq8mWFAXC0MxzjLCBXGXVrXTREW6CsPou6wAOfaa/FMrut2D3M9nT3lfoswSq8ECdDVFYG9TDOQWwAFqM1BqInNg7nS+vSBHj2T7Kip7+jhM22BdPYoXQ1wGqwqSHNiV8t1glq5dl5UNTy2sRgciFwq9RoA8epiF5lF3ce2XN1JvhjupQDj2s3WOgv3sC0CO8eXSP0Sil27dIHqpiQs8o/2Mg3sGgcCdhGGkLqqAHPv8o+k3JwXOK+rd5ggoa+0wX5LcIreq+GAMWiAXUcyOKpY7TUSMCD+BBi1ABQBXUXg5V3R1j9JyvqMHJ/hvotQIPmVQBLt1ZCuhP+aZBmqdczDQYD9hiGkTivAgq4MRhbweF++3Rb5ix7BwV6sbsvKuPCUQhLZWBfiHTAWachkIY/Titm0EKudJq9L+RG40d+kRuTbPc5T+RhraJEJIDPbsHpZ82qphl7P3tDf8I6qq0lmeHQayQXrrsiR0dwUSUYkDwu1FGacVh8muzUEbumDrjPCTHu5UCTpTWK5s7rELlJt5wRIuYscgLKjPFTYDfVALmdl4w0uUUrVnhc9joO+oW7LsGHO9H6/daSsettTFK/MKpr3/Y/iPaJEPKuWeN1/dVciZNvgrxEaJIIWlTgVGvN0Oh3Cq31Jgu9D1/DGP7J3K6wodI4IC9q+OaGsODFzchvYqUAA+aQMQgWdOYWNKWAz4DAdtgR1W6oDQVzf1rl2lm/fSnSPTSfoONeka5BZXjqp53oX1T69VAhyTBwvlCL4J/a4Dmtes+Z7UoUuehs3I9sIIyZ3rKPdjrBe+OKL6rD0ebeZ5YA5WoJjX7CqbPXKm1ivZX2cFbejJ6Pzkqa1qxcMjnX4mPeduUIP83tP0MFT6088b/YBI8hSDK2vJ8CJnaByiZVAoGA26roSwEMUwWEvULcV66FqrPsF14MkTum0Odqkz6nWiH74Vb4kRwxC+OP5A5q/S6rjLhpgsDuzIYFDvHwjy7NAaxHqJU/4sb1XM3p8J6zzt+fJqKjjI71KbufPXrGfV8aWCTpsQ/OaG1tbgo44I4tszprpKoVRGmBblZdqIlqqtPqLbVe1j9RHrNBDdakQ1J14iLDIHmWxadL6ot3kfA7VSZ2QHr3g8KxDC++7tBZtRd9KR7hFTgwXSc1ch/gR5R3KdjdN9l+ouiKckkVB9rHwwaV+lNtNP/wil+XW6xGSeYsLAcivcEEo5yfkLJaeFjmqKrJWS6ywy6vqXAvrWTEiZQ35mbldtbAX5wP9Q6XP4fBmlIYxHpecJyVps83F0IbojyFl6TsVAn2Wb/A93jRJlj2G6iGmd17z3DO0mfaW1BPpk7Yl21v08fHAtWi7yyK8G44b9WnAE+9i+XCgifM+vj3mD29q2h0q2mm/054um220o34kjAO6VokSBh3cx3hIY2bei3xwvfzezL/okry5SVpbYkn20dro3hin2T9qrFvgcRo5q/7AN/VJUgbfvA14Ymgr9H0PLtGn+o5QvNtOIuQgbXFOyo8hekIUodZQ3Zg+oiO60dFmI/QheExn1ZviR54VSbjTQo8ndOY+51m3wAeEwWP8MPgTf7qRcHrGOevRnD7scJcl8U3yKOK0Q9GGo2hRxGD7d0l1mRCtCcWaVR6b40UI6U33oLxEiFUcfTvDIVvEanJWXdAA/WUET9IR0cljmqGuU6EyjsV4jkpcBK++EWe797eIA9fKWeuLe5rTKhGi7sQMrU3kKaYrL8kGjCd31BIy2q9RirfUNnVekl+tReH5q39//uySJlMh+6dH96NFVTqrTqtgcjIJgkMZh6g49JI4vydLk6Ajav9dODcSDS/9/tcm6W0dQSK7O661GI/uE0zq4ozBGuirB/bUe8PCecSRvy9+dKPuAx8Fe3IWNb55bA0Cb4ty6OMxYtJx+CE+TtLvbWLyD0Q+BwcLo6dBivGsoyU5gYyH3mCNoj30k1nC22YbZ5I6jMlDPIwDlssa7WJgeqTXL2WRUUzjvkut0kNDXLmr3oI3SMAT5R0I2vRYMVpAWSeygHbwuHk8bup6Mq4EyBYK/RVXdxmu6jhIe+GXIbJ4yb7G2a+8L3/J6aRFh9Ng+xqHJPoO/CnbzNtAfzA7aa+hZ2rjckfwJJnjQOxxjn4d9PLew0ODxdX7eXhiGsx5p3mNyioKf/Vim8OMZmaKXrAv2iY5fF1h1K3J4A2P6BCoqo9qIjq/NTU6KbbfcN5fa0ZkQtJnIvPagJrUnzvD4SeGrwjf3s23FPlzXHT0X/FmRuzv5qXNuNOEypMRUZgwiXdjE++lT9xgtOOdeqDRxyYYbRzr8YpeW8hTg+9R+UgrO9q9Bk2WUEK+IrfZ8KqrpMQ3N8Zrgl/jBC1tH2p9uvlU4lucO3aY+mr1G30UC8+I7wNKqqZElIYaCkQJ2zq2ebRlPW9Dd7MRLf3Bo7YjbZNvMtRenhqMnXHWYtfeOSppWKtYNjYOKaVGbJxsPK7wmwKc+/n8E82FfzPg3BV1sNJOERoVI9A1bCi97qGviumGaHIHU0NJLmAaUOfYmryA0bm2XUuwki+bCKLyz5PgXD302A1A22ceUO4wW67sLQfk6UzYrQt1JDwJ7LpfSUpXSBWoymNQCe+cGrY/RnfWaYND5GDD1vh0iiCqAUhwnh1nb/y1fWcBNf2HwFRjAGE9x9FLM+0QehhN7wUIVcdFMM8+jx4r8XyAtQ6yof1tzSU3fZzc8Yyt7T9cRTMefQXV+Ay1PMfbvU3SCFQOTpanTLFSnLIwntL0C9HBiR4PGT203dfU00yQRS3VLNlU9ZwqEbXTwB1G6zpE73GxRzMtD/KAMhOy5Uou5ICcw7UJjhyazoqgcnd5CGWHBTDXLrMGR6C7YzHEGVKhxAsyRNADhEHD9Qoj1Ovi46/DIwR1WwZDkm0AEPfLs3ZmYrhVfa7oaTMlw43gQD50TMYY3QI7NOVq13B209jHO+DeFbIi57xdkbPh67yvMSVMs8WKGmamdWZuV1lw8pcREyWeq9OWyfpwVRiOYrK9gK2gM0FMcNK2pAV23ZxGHOazmdyuUEc3Hg7UYkg8vPeoJnvPHOYh8zAkS5K1naVH1a9n9VPvsU24BjAOCFA9FBA6LHxZb00IeOQ4oDgoGOq2It1UOQpcm6xycfaKBZ9rHRc14dMlWxzSBb3H+Xedlhglcd5xsrk1XaZEIiRt6bJ+nB6++fox4mSBLJ29lIn15PLwpDFStmL65jqWi7ZnKCxyQLnE/xTZ2ML5cgy9eVWQ/RultYhq1NPtevCJd96dLZZka2O4SPJbrc9onBmO/DY5vv/Uil9urs0LJ55/0Zq9Ym4SomV+wejHh1nyjsS+Bvc4hZqvwoEjq7VhfXAPgCK3jpePAxATsVUokyO1igBhEaKGHjgfXD6gDU5e9PUPpxaN+OpIdIzzpJyeDPV/2Swkk5/z1pRW90+znIs4n1U75xnOD9UyxmbxFmco15+/fo30wv0j+hG6OZxVV2WSVzjGM9iYAr1drpSToRCvtkKNRQJfVPUyiQdkrqmAcvmSCgLyvNx18muQpTEMYfRrEES3l2TmSegpnlkkBxmtbossq8RHNGsXoWuINP+9WIiUdtiUbQOmzXXUXu5E4aWAegs+hT+BSj46Cuu4zmemvmoCOtoLaeZOJ2DdsmgOq1bd1n4uABa1OTKccDDSrstIezCrrs6serA0PnlLYwxbdmxjorcXiNmkCHuNxFDq+OcAskoHlUtKEggUpCIB3hZRPDEZfAeVSStCWnJFOKKIZL9oMmT1hDhSFpIdmt3Dsc9NHCkX5hhtLA66C1QR2qbtfjfGvyWr1lEjmdCMh+D5U8gwjSaPlHm6wGFLNjzMhlkPit1yr544ztPpAxFUrGFqgcu3yVd+Pr885R6p9eLz23u4F5sBWYRZRIfdRt2WYSOwfMXv7M6dFeU79PAlyZrlW+919PcF3XPmjmAQ70BwVvVX/NrTpKsJe3oXHG7FnnAdVpy6rUiGbO4VdyiyaJE805hhOtTvcfacg8ToObZwyhGcERZkn4IGOqvhDbq6a7bf8gQHu5b1mWTWY+j5GS00anPKwBQdj1hGnxBqXbObhCYShbqaOSqFpq6ryUUInTFvuA3FnZUxPEfEizamLasB2Hc9yo2baapj6h0C6oMaom5rIlpwQOeB6NEQteeCUGxnNO5Tdw8T00DNysL9yFLjurWQwyErOcLbOQHTYZ2q24qk4cd6qnJWvSVaTzMlutmjPqaOazZyqEVoqglYHZtq/KFYezLgDPu8TzAtU39j7vCRXGsAbAf5MLt8YMn9LyEjeG61DAPHV9LHguP+UKxDdYUZ5EdIQDub/seUJXB8s3CpAuM9yJfZ5QtM+O5mL0pGmU7XhWLY2rzz7EcJPvXcgyeweTHHjjOoWOCW0QltB6bjAo/R6tHZkkCHxZEuWlRBclE/VG+xqEN7kIqu4iw4gVccr5zIgSyixLKPZ3bvYu9zV8fhXkXLRYJdQGa5xYy1En9hAh4kk75dBZ6JPh7VJWHug2MGKR5dfh8k90HGBvpBmpTiuCq6a4CvJEX1ZVHWDK5WpvAFPliHJzzv8OTHMKHmS13NIvkGPXTChX5wv6b3UqvhPWoPwlfclUIEeIhp4Sq5DbcjECQHIestZGd69ufvI2JS/6JFxVeoZmDk/BAm10SeD2d+DfLDolC3dfm9md337I8a6/zNIr0YZay+52S2jdnQIj3njJnQ0jU9pB6bYzZIg/udY/JHg5/HOlOoHVUVvs3JKhre6C6RBXoviQPPqja/e7gHZBxD/GS8+F/bLMJByCDzaIb3ON7F7RHiU1N/ummRtqeciDq0bVIxXU4XQ74x26oqo7N1/RkuD+fLneMxWN/rB9scM7q2DelnbKv6DNuYtMZaS+QHEfDMikV00P3Ube3lmRX99zxrykQn52d6Y2X1/CHmC6uf5zlX1BBlojyJf/o7LHt1W5F0th5NtHtGGmeffNru5o+2f1b1j4ODH+4wW2Jel0VGsa0yjJu7MuWTj89Sf/DWlUSe8xkPaDu2reM6Qv0VncVQRcTtFbNTXsW2xrVS8hppoK1vm4FRjySGcTHiZnLYRSx2kWUyjeznYnTJd7JxLyHjqYKrv3gU8J0+7Iqy/pC0wVlmCMtivSf1N5yXSH9BA8Db7FA21VRC2apuVFk8NRRRKk9ID/LZW3LGuv0IPANE0fTmXEoKfc9pGQZKEoehS31gF6CJAJrKlhJFh2EGuRJdphzkif1Sn6Y2/KXECjTJP8W5zYsUyb2NRTU7Sbob2M3fySLaohnDIX5t744XaCjsdf1sqnR7+I1hGaJJL5l4i2HIPC9APfTgNWxgdvu4zRYY0/DdPxiIbf1u0R42M53kA+n2U+xkccwSh40MaOewkf0LbWSyqXwtNnfLOwg7w73Xdvam+JFnRbLxzig2IDhsUpp129PojwaPrXa/XcP4VWjA9bkMjp0IoJptFxramivrJb21JZNB0c4+FosElHEaOn0gY6qWuLs4pLpUzEC7vkP3tQ6LQduLo0BR3uzl7tzvAU/uaE4fcnhZ8IrGmHB0kDPKjKMggLQRw1BBO+0VRmX/xsD7lDjiOOy26rYiKaNwigbXU5N/Wo44bu2Or1qcw/5ladOtwS5/B3e1PHIrAOYffI+hKCgBxlavedBJBMAQkgxQgMX0fp+aGKGgbp4rnLJkiBimrQt0j9GPdyjb3TRZjqoq3KwloYwmv57RB0IMzw1T1euDz20kRde70KX+Nan6AUZ/H9p1UHdslQh8LVSVDqqGGqqjqalaEAO+Lcpme15U9ZfCy/urrV+94NCsg9PGLoVyWUeZWPw1dgvkLY6O1wzsxEwKEIl7VHBx2CUCqxwUKnVbV8UOp7E0oRjO5cv7EZ6dH202ZbsXznyAW1HaB3Ezih//YGAKvfi5nsAAyTOWqoXOBOKqvHFyC/bfCZCSoi+OSZo69bpdtQbCsoAQaadyDXEZoHBx3ncmSJ63OA4CXd1WS6XVCHQ6WzGuoi6bb39HqW5z+NNMDyG7hVAFanZEfQ5E8T6p6hhK5oAn1hQP+LptR9yGDvuhw37YyVnlhsgXw0JbgPHaEk0d7IHA3rU/dV3rAML3ksBt5LCDqNtqCfRHWTS7mYPSvp4nlteS74I+9nwdKNzj7DJUFEc5z/2Me82/VvK+aQ2rpfk1CyRIc6YMluYsQLg07zsRJNJbHAe5rhEyP700/inXuOSUt8VTmvngB5n9weqqOEoj3m106zJc9IBGFEg2eYmej+RQdlJsOx9RZ9lDa79gUKzjMqLvzBWutRFt4qy3obE4ZluD9CKU9ko5qWTSASPIpszEXk+AE6tC5dJOCQIF7ZZTV/z59ZBl3aRl5rcN5E7mavyKtAbdA7CXe7hFeduKrqVbjZbbW/WSwuSB096M+1W+rMkKpHoAqBaYvA43njUvyW5PFsnxDJ59Qkvvkur49mwbKwVAL0iDNZ6j3a4s7tGmx3cCxJ91Pb0XdXykkfW8qArtB1QnRGL/KEpt7N1IIaNJY0seImh7JtHtOTKlKjJsOUpVpNUcJiheD+EKQSWEh3C1CjNqDKzTeylLom6v1ai8lCXqYl/mSXbU1Hd06+9SQlyglPCtj99br4dXL3SID5qVRghFsgOekvmY/1nLMMvd6G6wNk593CZ7Q8aCLX+ivNxy3zJNHaUpqqplGiR/3uMNKi8fKyJTDGaw2K8atNd+OkECXgRaVZC2ALtaQQfT3ubmLFS7WOzd/w6iU7NoKIGM2TIiLVDaVvB7xqhvLELxHOMsI8TqHf2CvVHICt5p0FmQlxz+6iZaR+JgO08eqfYVFVn3JmROyavgmJOmLFGePp6Qmgs02jV2kUwe3IYHOf/uvRaukodeP4jhWfUlMSfhiShWLptvNRGJ2VmeZlcUrRWx3F8vcY2dPizT2BVtjMxNSo//S42Qa3SZkfZSZ5kR9o0tOjLSkMNSdm+ME45kF8FURUiytwjNTVN1y3MTWN3y3NTu8c8QbhbiodmZdFjr8738LBGZneMkS/IZ35d2xKLC64KMZsOEVZ6xqdmaICcJMgi0cU2V5tzMj6TcnBc4r6uvqERkHYXffZzcofR70UxhZ5Y0t0qNL5JcbNClYt31Hd3c4Awn4cHJxgPPbnYatLcq9JRG0HdL/oTwFq8MerMUwUQxzD+RtMuLnCsk2sx3A5FU39FGNSWzjvDk/v71Ig2dPuxw2UUNKPIpAeZCbf4XSuanJ7u+urRnb9A3HBw+h0F1lLaKwLsi2yzAH3LDCzEm0/Bxkn9f5EQvtLmIiGHbPDtZsrn2vnoK3bVEk2ffkgWUi343bRXA0dVv7nXfkBNOif/ZSpo2albSulxMqsHiTS+yZFSNX6CKyVU3o4Tf0ZuBZQkuN7rQaC+bb6OOvuyQz5syvUsqtOSNxHmCvf2depOOEMNotnnpm6MGByJwdk3NhEZa0Az+BmUoQlTa9frBsyfhC0TvEhkLgtU9zLuEBkbszVIfC3rR3l2MBnuYpSna1Vd3mPQvIZ9bd4V3Sb75dO9xslLeLPN3WuD9crtGr0XA6ToZKpduj0EgVx8i7TV41wJ0382XKLrm/dx1sKR9rpJb9A5XNC8xHHkQALzur7yZ8INKKMnZSQPqGhDiD3zTnhKNg4AArz9XaPMV13fSYMzQ0qAsqrgOrq1FPcE0/N06ikn9F4qkzorlXj37qIwcMhYrejYVwT1jyl17doE2CG3RhpWQp516D3SUhTIyhRFYGoy5huvw6A6rdkUcSmWy8yVSR4Vin17trKSxBClKOwFAIfREKFfZ9zXJiIagkxcchExPoFgiKgQT5NIJS2yPLPQdmgqU7Ac/JHVb4+12oJ27E7+hxvIg7dSoFtirAlMNgzYwfJSDEmuhXRd3v1jn0GSsOs5LA6+VDqs1vv6FELbDMle3NdArdIVyW30MZH0upvluvucQKAM17dXyqYZBIx8+SutSD+0qUASNcInThdWAYOUzSOCEC5mDYFG3NVgPGakAPeq2k04x/B9ndtg4q4bOHqU1vk8i2OQGhCdFs1vItH9BiLGjuT8WsV2OrS3zzucS5fTAvcTIuqaWGdYHcipk3t7OmF5m4o7WiLpvq/NMljL3LdnKPiZt4LajgSWpdjzXqjryiBSgyl1ZBR/+qMgz3AX7sOgQ8cLmME6IFPzoJ4qef77XjIOi5pvjus3DMbdn9tjQ7I7YS4xmkZHM7Tg+2DY6HXFukvGtzU27vTj7LuvkOyT6ihCN46wakEVT49+TBZJPOR8dj0BUWneJbu05xPBEssk3GSJqVjK/O0cn4E/aUPpzrSaU4XtUPlKd0ZG2/es6FkO8W/ujqipS6im+GVQn+LIoos6m0kNNOl64tdnhfha85QLub63VY01qrgn3uZyaSyrUdO08Rmqu8b43UMv9GDMj0k+r5YaGtGZyNC0X5mx471tMPjKrPGEbJIGdPwS43AB/Ca/FBvgjBIUCAvAd1qCG7yLFc+gIHgOH8MzR/2ZqZkPyUua8mSSD1v8PchKCvAF1cJLY0ALHv/+K7EdlM5qIl1+D95OHLBwckUbXq4P8U7cVRQe5KpP0O6H4Qs737cPpuGfNlmeQr0v/cDDzrL647mPtkygueoXLoqunpNp/loW4nmSA3EEOQOk7yUMFOfqxKGMIpcMVgHlFtnSK4tfnE6c9jvXeuBhirgPVeoVXi9c6ENx0A04qHKbDUtAshTUFroMVhr26tHTo5w4Pw7VyTgPmkVlZqeOf5L+SVMxlyuRLT3NX0beBfXnoge/VL1ECbB9ttjg3P47+k2drfkdC/uUAdBiEISSZrACL51Ht8UxC38coVi4iufML9I8GeWVQ783KHJrDlqFuK8qWEU11irX3xHGg6KTd26LsuGl543nPv6i9uo1yyR3WAf0zaLupFZNhzOexWCc3N/0zx7mdA+bdh6RJYWRbnJC3XQy4q6IT59IVzYIayExZJQ1GXIagCvMtBAGYOkGwQPtm0doDTsgOFuZQKGI6bIs64RFhWzxPyv5I7XgM6hxaPCqyUxzjiUG0S684fpH7ibRIliQqCQ9Rv8DZjnFxNJanIIon5lYYtHgpdc3Cs2YtJRhg3FLDRo0GIjUE7SVKIHO/vc+AOpciqRXAs0gFY+5yFD8jKdKq/yYooTrsguq2DC+LbHOJuV4SooeafNru5o9FRl8A/aPBZbhPbHttQ+F7ho+F96y6Sh5OHxBDDV9UBNEJYYPbonyMthHTzL5lkcXQNeKlwD6rWldnd9dXyc85LJGhehMRhVD7Nhy+6IRhrwGROIlr2zrSdah1xaAbUriViDK9xXcQ7Oq2JIoFO5fuZadoVfKjzd8J37Cml+jKeedCskBDZxXBRJY9SiM80wgQqPaSa3mZJaqczsLO814ibUrK133UxeC4LgqEB6mlbksk2c8cyUkcq8JwCfLQtVyZNWXa1QGMm5YVo66zeAvssLI0TPyYZqjbmgNXA0V0jkpcBAdgap03W3yB3vL6bPV7OltEinJ8luMaJ9maJRnbRSspds3XUIsuDtAor3hoVwuacv9fWizLYUod5bmXWG6Znnx6X9x6SGRS65a6tDJYDtJY3RZDpjVd4sTLQrEOwSSQGVzKDMy1BD+tXg2YJJd0sFFvI9iGoIsIqFzb2zgX2hIZY4gTCn8QKeq2uvwNpEc/ilKXSuLVPIYag3no9TytnuYU2FHHsubjsK3wsAXqTe/F7Xt0j7LwNONFWZv9ka09sxybf0vAF4tmtxvjsgcvNE+FwuQOc4s+lzq/jVd/juQgd4PKEpWLNBbV5YJKB+27yJkM6e/qemdMM/QqBrk+V8aAlbZ7UBQlSaUcaZWiiMrQkH3VYysZ61bTz8Oeom6rF4/B99RxLDeeZiT1aUaTvmNkj2sgdYdUKJ8BJIjgF/tsXrOwJ7ITpgPvq9vaz/3skgH6IrqP3OaE1id3lMfdnT5i+pA4nKmzC5QWXiHgL6mhkKB7MSI5LCR1Wx3pQ/eQDst+FuWguFDZHjqOM7KbZhE3MbZvCptWz6PXPChr1YIgALsWCBa0sZHzRsAqLF6M9Q8L8GdegJdZc7t8q9GcIpP8tiEnebcZsM9lQhkDpyGe3vSeoshfiJgOi2ruRUVG9EdZNLvlmZu0vHyjXEKn5W7DPJ6mWa++qzu0RV+SElNUHkuvrV+94NAc1p26rZZQETh3kcNf2Gp4Hefx85zc3wdqcdfd2qNd978Dt8/Oh67PGLW3AHPpeFXmdfsIYKLHc8O9SZwV/LYgJyTScfL/oyyjtzXB1o93RaWN9BApDtD74rY4xyldVOt5hfSu3mbHxYbRquZ7qVzkNVmCQ3TJj6j+UZTfZ2eY8xITWffYSoWTpixRngbrkD3O04f0jhw0EE2S4o1a8xxW2Qj8RJaO8Fpbi3krawKWH80aa7i/95VnxjwyAVwxJA5KPxYeNOwJ8Ngtj5TZJUqpT+SLAclhk1a3Zdik/zyTTbKbGENKyD/PEuPIPg/Tv/tuJ+8LiiCcrDaWXbJ9b8mkty3M217o+1hRaNEFVt3t9eHbnM4dYfE0xDG2su0035CZXUDDuiiafDOGeasi6bYt1o/Ntl92ge88pj62T0di9nHC+gblxRbnCeH42V6BCk1eNIzoaJ9X9tKyhaNROVuAiGfgD0l7rR54FO6xHDZbdVs/wWXGnPaYlGwGhLevkuq7/3tbWptwpIzrwJiaaWXIFX5JfNHkOaOP+IriD0l6h3O0L0YnvBJr936L81bT8YqkedmkKUIbz9qnZTltXXPmSbylnmLniJwOpRSWdnXnCyOtDrXPsD18XpbFyDVfiTk1G2Dls7OpQpjbHdfNCGL0ID/VbRlP0bPIqKMMJ7rDQCyP9yI/fdjRJap37It09qD4Na38+4oeD4H60e5TzgldX1Qf0UNNdlIPmX9WvcMbwrnBZ6EmJwK934bjeX2Bet7cwlfKBGQrrb2Eb/8I29vlua9fveAQHSSwui2OUN3zR7PiONsjMDtP6FgBwmcxhCzjyfzXBjVoc0qYPjuqayJpPIPA9Mpj9QJEeFg46rYYggW/viQ9IZNArd8c51NhLsyIBOpqPE6m0DXzqSJvMeAzbTDUMmnpj3Ge0L3N5rgVpF/O9KD6A9E1DNrYXC2jDU56HjFNgFLPoLMHKhagjLjuwCeVQg0lneQ0oK5XuezIHTrPVzMNgoW2HAxXxXVQDEqHMXG1TENivlqOiK0RdNLm+hll5zrsVxrJX+KiDI6cT9lp+QP7VbF8mxdolz1GadhgJjiZvYnjNJ29jcvm299Rqot6FEm3oL5i83uKxbzQviQL9KrEwfH5CBo/U3YrwNOUplsMVlVRvvmQ5E2SZY/xLBvT7gI/zI68z4k2Dfud0XZALMlNI7rmgcGBcDC6fZoHDNqe+W75788snsMGbVimuiPDn2fx6+q9gEwnJcvGzX7Oy4/wvCjFmzVXl6SKzGQcAjmf16vKEItsppY7c9hllTmLeZl4b9BNQlY12VXbtcC4MEU2i50k212Cb/MQeTXgOMgqdVsGaTHXkz6jjjlTw5F0zn16VS5jlu4X0RXakj3F61XyuAwFVIfV6L0aZ7I1/syH8g8Ess3WNL+bwPukqrvmShRDphpNAZ0+3kWo0o4vThTEn8swECs8RMwzeeT3BUFHfOXB/pXHndBU+3VQ7V+Nta03uI/oR/UeUflM+Jq5q/bf52CMh+1O3RZMsT8aPPah++2zJJc/Z8WRJ3ENlu7O6jKSr0X5nZBw5jg49E3HJapPe8axWoa0TjWsQw7BYdVpVl0cJdNgjLL1/1nRVn6ZFsxVf5vYhPJU99X5DQARaVxwIG83pOIWp0n2aUenm/Fk74P50jLazan8p3nyRxVq8iml6q2l7786nU2TKfKN0pJOaFz3IoRLUyOVyglpZJAg18kJn68YPMhAs2NvO1GhWyNFY/APminBhCQOvMOXRw4CFkekz5HKc1zdMYSAlHxLLSe8hMDbomy2gSeTFkf1QkJ1EAzqtqIeRGIlompnMDy94A6nK02Zp81YIPEvmL9ADSWtVQ1o0Jo9L/E9ocX4jM93wfJ4DqtVs1pjnG8pD8VaqVdFLExma+uf5glNc4Ue9nAxd1ZdoCQ8O2jVJ6s4fjxq6rvwJ2gMwguU4h1m3vbux8KjdvZm2BiUo7xYuebhJ0GqAZMkqQ7W1UV6Wjo2vWehlX2fgEw9ZyCDtoAPKKmIIP6K8O2dj6/VFLqLw3TYA9RtGcxZM52/9pxf8IKSbhQeMwXxcj59uS6TN0Ti5ZXf4UZaKSOyw2I5LJafabGcbXdFSQ2hN9grRjlX/7A41rY43hbZJlqKQOewPJ39Mk5ArjiYorzd7ni+VSPRhnuR6ns39B3vAo+GyXcUhqG7XPmUh3tzkFX3FqNsQ/9aIJjiwGYnRX6Db5syMVwLxvHwOX0g0oud+xnDUWfNNh+DCM7c2gWqiIA+y2903nNxmurjgRDkXnHR/MNOcXuWMuaJGko672lAw4JMPeapf/RIyp1D/OsXDKrDLq1Z1lFCSHb8oU+j82oeA5tV9MqZNvw2cP5DvadjQEvzd0mlC1zxp3iBoc8c4/B1tYY+zb1ntI3RDVzazE2HqTz1sx7aPxh8oMLyDdplxaNnOBoRxUGiqdvqd6VQkbafRR0r6vRySs3ElDEOLBYvCiMlHzE/IIzTkDH1ToxG3tX17qpM8mqL26iKMaYCwsm5y3VCCQbzODl3Zi1DIKY4c3LZfOvsBLO3ZP2oJBIjtO1ZXHZGHFycxxp0PeJ79IHJ4BHwsNbnea4mG05vKwROVuLGfD0CT+cqFYx0qlIChsUreAgzeHL1D4qHuq0VGzxnCmpOxTb91ZNqfrltGYczTgTye0wIu9fQn2dVvykOizdMMEZSLjuBENMaHGYXk5mSMOMSh07yJ91nl7C+DuzY2XwXMcMueIb4dHNTocBYJO1rzzAUx0md3l3ifwZeLJwTqdGlgg1cZxHVq5Niu2s9r1wU0mgBw/6Gd0dlehfjPR+t5fEKSKncTdoW/J4lSMET37YYNcFoFn9OaVNa/NVQCt00vsX/OEm/n+VkvaTfA/3zT4hQzIrbFwqMB81V3VYUx1/y56ZJwyVVJI/fffjWK1gP9LA3wUor0FjBPctrO2FOIxnrmAfSg1qPY4AP8xglu8NN0qZyLgOi7QyyBEJ3ECTqtvZzDP2C0Y9IdsO1easRRkS3RfkYgZdFVAc+PvDxYnzcC/cIbCxgOnDxgYsX4+Le5jUqQd5MzCM68LC6rfFU8SrS6eR1GJ75d/yyqCqig2fhXCaiOvDZWvnM4alP3lygtHVccOeLLQ0fOiA4cIO6ragXTZ6XPnSmQs2lk741s533K96QQ/vfitzQVJSEnrjO5h9Ry+k0yXLoHAQEX4GuOCIGtOzcRK6KozSit8QZYV3YlE4ZmpZ24ue64+/JjgQUSwZ0CCYoOhSPMECmTkgOclXdVkupYE+hkjRG6R3BYt1eqkZy3DAMncYynv0mdRnZeHnXXmMtF5PwHJW9eyH9W3dtGem6fX3C/xjd4jxKCEQyez9OH3ZJvgkf38fibZFlxY9gPOjHV5xvwhGdpaac4jH4v37UrjLLWHHmkZwUmSEoXRzv4S2Tq3W2Zk6q6iRLKsMLyRih45+CntSpP3HUJPGKTadK2alJzZZRkv7aJC0Yfe5UFlnn4X1Wvc2S22oct7eVAsAeTYUifEMEb/ZIXcsY2ctP0Qe0/YbKYbJ3OM+prG4jM/7+/BdpRjnwN2RSidTKR/hXMoU7Wmro+zZJUX1JttR2OfgT9hIlZXr3okVXvWCx7pGg73BdUVXBlqIt1BED/0oP/z75hjIWXn6Yxs8YtyP3dX71nbXhgP0O03ddUaeORb3H+Tu5Q+n3b8UDlaN2M9g5JNjO38dmi0qcXtBHu6o5tJqPK4zKc4IJnSRZ2nRi9QOq74pNRGGlbmSPU9RumLazQ1TplJyfaMyWvsKf9RWONn8n1OoeLg5T+ovH/HxNsgzV50VFBdIFSiqqqIVPTO/9Ur0A8O9xTo42W5xbzwnZvu+SCtmuGXLgx1lmK/EIdJNvVLJO6gshGE4yodKfDEIVfcO1iqGsuOOkKcmxPn28KEizdAKbLIaSMcW1ghrYI3+03fiAN7uCiPc3rAZh4BWu4uedLcscZT+Sx6qtzLVm4B2mGtOWz3YppJG97FK/vy/SPnxJ8FQLiVVVLe1xzo+z4pvtNNOnNWQNIsqz1nKhu3YP2EOZDA1e89HFzh+x7FNtScraltatLkjPS7Z0nvw57VbReJUbsH7A5BQBkwTg2+d2mVsrMJ/KoMOW7tVy+HbDRqBRt7TP8y2mD0HO24TDdvT+QDqCd6S7b4tySwfIVbaagqOqKlLcknDkZhogsLMAXaCqtV9cDzmRhP6f5ptnnV1DW2uygjyOT9ShCs+fdSMixMT14+/P/w9p+LYNjrdTTIPDEIRGXvFjIo18yrtYyM+O2hdkVGJVabKRLUiEohv+S78vUDXtpMgrwh5EFZAvh3CeknnLXIYiILG8Y6KdHJsTS96gHcrp9ZDLHNr0g82gJfdnbFYgpol2v71kmNWCh/E/Wzeutm+WDAxWUXIvC+3MunBTT49vteNYimm18/YkOHbQW+igrqc7NfpnHyqtUrKuRV2Ih9lqszKXW0+BJQBCmlecF1M7EHMB7nYgmE1vWHR7Y3VwGOzgOocPFQ/ZVYcY3pONnJvWc3DvzrI2Ye42tgVY322ebTok1t3fCiiyzKyRcFAgPxf0nOHAvhzCJ6VqgF1fggmhOXgSCgXt+VFe/UDldcsnOqZg4FR81oG4chuLGJKKAAOvg9eAji/EbcBcWMk2Ar83Xruskxq1Lns5OTGe0KgY5aOS40BoiO84QBfWg1sAGHDo6up4UDuCBThRO0dWmmZXZW8s2cdRMTKjAAexYQ/iwoAiVnvW++XFC9nC7cVCij4swDwKmj4ltuFFj2ma+dUSl4V43AAjaaVkfHYC+7MgU4G0tmmfq7h3O8/1FOjJZCyZQHVWHBfeAjADjGXHtD5jP8YZDSk3NGDsJg8fnQoCentSyKvLgxptpPq8niLRmPorVtDRo4f1IYvUDLSJKci+fwXKNIoFbXeq+XIx2O1NXh1nxS29VTSbKyRIiC8HIBeGlBE/KdOFsvsLsKByTp6ECYP2/qTYtuH4RsbRcYkIrOLAHs6VCSX0AB+qGHwdfKgawUKsqJofm+aHOvszqOE22Nj1B7TBSe+5praqAcCgaa2Dc7KrQagBTuT6OduxQNebJSxmGjrbNM/W2x9ndT5ow1gGnlAyAAgOchcH6cRkcBuQDRdGvn95px/CErypnScro25XZTWM2ftK2jKNGGVyDsYUIlPKbayfMfkh7IEx+XmyYswpoOx+rCh9wESjrBQBwbNyD+N0SBbx2kvGeHuvqhNLnG0VdH0KUu0NrtrA7ddHOzIp9DFyPxqsMcbpKkFMNcC7MJW2Dcj6Yse4DqRhg+oa1xYEDJGChXMhB4h/H+tM15EF1pqOzk9zvbEjcllyXL35Vh3fDHS0sufoIDr126wLiYYq81FnbMFe54pAk+HHBfpHg0vUZYUwdhqq5UKZmdxg9f0D6ArAzeT+akW6BYSeFYls+jHU3/chargN/3TzqcS3ODedokR4zTHK4/wkYd+Hh4KhL8udhFS0tumBUHXvbEbEE75H5WObTMPEBSxwZAbjUEMije3n7CwG9WZB/oLobCW8mHr75qzjJt9kiL7FParrEn9ratTlc7ueSkwMZ4NDw4fK6h4MatUVtYrDjHmtBiaXES63FlxYwOpeaKy1ngXSD8XSXqqqZ7UQgjhfaO8JGlFNY9kHX8OzaM/L+zauygNyZ+TlWNiHeWOqFuqu7IX3nqAxv29/NCmPZk0DE0gVNNzmY+NXNuNgil2NoFSOYjkuVc6X1TGrr7MaLrWUiSL8zDz6hLdy1Rj2wKBPV4hydwWcmdrAQcqKGob1vdgxNulocl8NCxtHtBwvG+fTpitsvVVxtqXwheosxM9PWBDrxrEnBn66Avlyh1J8g7sAh6PFw5aB9bU1rAxX9GBqQw+eIHvbjWg5Rreb46fA8vBIPnU5pBUcqWI/D1zgE3INGqcX5R7dgd5qWi3L/S+VgOEusHACeMOmdzCGlW4kWgb3k+168u5ty9F2y3qtwUt//ysufOx737Fs+MZ//XV49r0Kh235KtHE4wNgI1+us5jVKhgpjueJ1eP8kpQ4yetxWk6K7Tect4BOrge2eDSE06DwoKl1h/bty+Da0eXkguuc2vRsTR4QuvFZHugsUKyC45/w+c5hWOtYGk/wpGcxqiF32OccB60KFs8qlgbXIWB9cAPf52YAdXQdHA/NqU3P2Hpr433fHcBD7Edg6J9JwK9Hqj9hUS6cs6pLNJ4yzMY6BxwaNgeqe3C6VSfUXL9io5zHAJdbCi5z77AsVmN+E+0ZGp51Y1ANJvu1Er5OdN2wWC3qJbvWdWMx4L2tHgue8FlDE5Z9rybVxmm91RgR7Eed+jl2GOvR7V/T+in2FnFwbf6uaxW3OrKmFpnDKmnxRFgq+v6ol41p1a529VgNeH8ryYo/HFaViGLfi8vJEGVrbfK5s1mV3WjPxqGnbQGiWXqzItnYhQIEocEgBD2gU3gGEPneogFqu7MAe2lpbdP+muIBXl8mNCHoyBYmAcODR5ZeAnLoElTBvvFlF9yXBaUXTGmbDvA198ZhVxiVZNQ0tQebBFDFBDA4xGEjpAuPKdBr4o23vYX4bN86oH4oC7CofqpsOrCKHIHTMEzXKhLkDGz5FC9LlL1flAmf7oXIBbrH6IftrR4Prdl7O0CPHVho4SmxonYEy23b8Bw9OZZ8h7LdTZPlNFUJz1RWHKSsbmRapqY3/6pbVzM0vGRWxtbGgS3N58Z5dmD8ruLe2P9tUTbbNl+AMUeODAqx9QjlwscAaqeMOFGOQOpOLMBfauK66JX7ZaOrYodTSz7iYZWM1II5c5KAfE+sBPdiKV6CCfx0mOm6/fePsmh2ek5iAJVs5MxBLFKAfZi+rW7rVPV/KcYD5sOm6anWGoRYxzUWQqYb8hziq8OsYr6V8h3Q9WUFHjcf1ny3AvWL4RezlsQMOL4KxiBXcR/I1ythQcUYFtXh5Pmxab6tsH9W/FLU6HriISPD8PBahqSgXkwptKHiy3VmhzOMYknOhOfKWk7uNUncR/SjauPwGdNlSpAQUw5ALvwoI35S6TKV3V+ABZVzYtP23tNl0t4PGRZHxtFxiQis4kCPdJkgeoAPVQy+Dj5UjWAhVlTNj03zQ539Jzpn7xMtcn1z4NFTffPYHW+Y/R/Pnz7UqMyT7Kip7yh5u9gGFygtyo3ZGGVVGyKVrqIL+ew68KTygzsNaYH17jTHT8JC9qnc0KDuOMtwfnu02ZT03kjFYRAwxNEtnAvrgogBTh06OI+tVdeLBZhLR12b5vmae+Yoo7jkwSJy0dOUdHDfF+O6Jym7hqRRnyui0L/DpDfl45iJSu3pq6ulSzvGVvBJzgY3qMkjtj4utRrKAkxrNYc2/dh73jFwJJ3kc+Knbhkvxb1dawDrgjJ7pXzLDWJfTMvNm00n2gr73dzbU66eRwU45fbuajYT8T4dFlT0fKkdXp6Lp8RsJv9OCXIGhnuKPp3K3i/Kdk/Ql/MPfFOfJORQf066fJdUaPMV13cTB6nYxVAPYsuhigtXmppRiUWI++O9/LHs1QK8ZzkNVpwIYtg7Y3JKxMhCJn4Ba+mY0ldr1DcIsKdqFexfhloNZUGe1s6hTT+GOuvi4c/sEnNjZK7qYtzMt/p0FFH7weyLqcH5tOkMV3G/autH6h5hcUaa4JQq60dHVxcR79NhTUXPl1JW5blY/xnpAv0gy+e8IAiqYf0Ybe+6ShAbAvAuDKlt7klZ6W1GsgC32szfk7DgQwOxUwSMNa3FHnj+cWnIb8HIzbrk8LnDu9bpRE8kHgzMvNNDOGXZ4bE+ne0F7vgC6xWeh/VvLkO/uzPzwComvuCgdUznaouDG4CSyii4ej0sCA5hQU4E58im/RHBft0LaDd21h4rAnREZwMRs73PSjyzm7YnS+nOKhrb8tRuBZ4rX5OMyAJrLRoGh5iLg3RhMkUTT0pn1o9hAf7Uz9OT0JP5IeiVPgB2Rp6MoGtH4KqlFToNjdev1V2guinzC/SPBtkEa4DB4VMPA+lmIACbeFJiTj+GRYwCunl6EmJu6rSleqeqEP1FQkw9z+kAVrTNniRlp7IfN/kmQ9oraE0d+DDGg7sdyNRNqd0kmDHMtj1Y9GyR05ZxKmx6MdXaoyVAGIlx21DWmJ0Jn+b+YRzGPvj1Se4i0ihM3mOqCrNz6lN0KTMNYh9s+gQdzM6LLPtS1GQU/esx+uEor35oRKqmDhgWUAB3CgeoaQri1qnzq2NYi6EswLMWc2fFtmOt/Snpdyj9XjRibgrps1ppt0QAKvFgXSeV3rZ1SHuQxrg6bncd3gKs7zrfVkqGWHmP1pS0KQkpbs+Tx/Yy5SzHFK/p+lpTC7at8BXczCu6xuzvb6OczKw6s4i5xGIGrPrB1FsNFw6mSYltbHlEhcCGN71chCybB7jVtDT2L5RdR7cH9jfNt02XxLp7Ww10Wu/J3L8vbq+Z35RnlAtAUwfieQbEhc91rUA2RaHzq+Nsi/EswMwWc2fTC6HqKtjXaGeDgGdi2KdpWNONYGHefJLmNCsuNHGfI9f5ctsqsgvtidGeLIMd3dzgjHxB16brPgkSFHQDkJOYkzAvHn1I2YUlhJSKsFbb5p7dto7STIjA1YaFU+9oEDi8ZWbullMFescgdvuXYvpxLLJx6ubJRbLRevu7jSLHKpphC2+T8vH0Ib1L8lt0QZbaCTkroTx9VF9LmWqC91O0ktOllLEVkHX7vs8jCq37tAAbWs+ClflejWYdDNr+4caZXJX4LMmj3zMvgp1ZmglBgjtwH1d/f2yX3qFNk6GrpPo+GJ/Yb2rmM1QEWVCu48SQpiahNyjsWFa3u9uOaAnetpxPm66w9fbG2X9tUIM2p9sEZ0d1naR3rR31LdbopOoqEDeD0C78rGnONWX0vhnZPJQFWNg8fTadoPArY1ouH70bN0G53RdgYiilPdssN6LZ3E/t+7Y37oTmx6YzbL01cOt1N7BUH/RVVcHAmZ78yDcBcCHX59UpB6aRLMuz4HzZdIGttwZOZRYfy2Ju8o0lzHJSlW0VYGfNilkVN+tHtDdRDMypTV+YavvzOyHK9inBVD9e05+XIhlZFw8JFPQlGaGc3Edk3JCjSFcEo98/o6oHsQBnqmfHpvG+yn4zzl0236q0xDv63TJ3MFhFmXeOhXbOPQc3taeEwtrOLMBtZuK72Pf358Rf4vukRh9QRcPXXb8ti62R7zR1QCd+DtzJhV/T0PJsZ9GbBfjOgvhWJ3Wm3lqY76pwZb2pxqyMxzSzd7aT+7I808lkt+nDVGtvDHe23RVlTbp20xq2bGz16ioQy3HQLhynacbZMh+F7cwdWoDvzMR3MKnj/HavRvXTB2fmU1eBs/B5Mp+mmf0wn7lDCzCfmfhPjvlIQ1nROcUPbKLnCbmCmvEmWHfeA9qBbI46Bt//6dc0lMV4Vj1rNl3oq+yNVY+T9PtZflkX6Xe387CpIsS6ijouHGxs9kn5mtuOZgFmtp3PJ3HQVg3GFNnBUG9hnn6K4R4sx7JHhn6CwR8+oLyhgSt6/1b6p5KFAViIbXkwF26FGgAvzvMGRr5/JtUMYQHG1MyQTett9WU4sTPwkzo1qYHKQcneJmX96dvfUVrTIvRAJj5tJX6S50XdYvnL5wqdZCXlh+r353XZyLovRX2J6tFescNp9fxZ953ho7YAYEehevJwktTotigxArGM5Y9GXOQXjbsBoemLjCjeF2mS4X+iTT99cKdEKHPX3if5bZPcwtj6MrvOocu6bIOLVC3nKbsnwBmRn6Nyi6sKD1nOIcQijAtSsoI+JG00HwPmCdCInvX7h7DyDy9MfS2yDOwa+W5VuQvXokIxRM2xHJJuOEYk/UMdkCbj2yZTR6g3hWJRdmUWC5LIFETWyD0RsCAiDsCaNq3wymsdiXoQI8rjrLg9LyoQ11BmnvxOywBnflD3DCiGlLEQjilLtIk+OkFqLUXPcVo3JYijLzKiYF3HIDy8b54ddXXd4iDMvUvy5iZpYcFlxpZbTxwNx4pLtFXwJQBmRo0yfI/Kxyu8BYfNlttScYowqSEkG7fTFe0Yp+ctzmqFNDTUsW1Uy+48jAXXd/Am3gDAbFFf7lCKb3DaqlnjkDWNwBXMQhes9qk9Q4EyWAPv2Zh9M7bEu0pgtWEstUX0JSlxkk/hpE6K7TecJyrimGsZG/5rk7RfPucYFA1sue8oHLpu24QNbn+kPTsSABv0E7RvQ9aN+M5AG+rMYRr6cHamLaB/kQGK//G1humIhVFJzriwBjYWGtG0rjwqXWkstEOjPDROpTaIEhUGu178URbNTtmLttSI6CP6Uam206HMiOT0gWx7eZIdNfUdPe53QlJ9DtPBGxtr45dBWPtgcJYKD5PIE96BAUDzNgykCYWxw5leLbHrENrRTzXlTPJtGzQ0EaUSTZcO1IBGTkAH0wtMVGfa3PsUUOB2PmbeskSiohifI8yAjE3HAY9TyHZipB6TSgEmG5fpwjhWPl4xPF4xrrTpVCdGwARPd3LAUle0yh1MFYrVSFso8JyKOxWBBR3bsMFttp5MgZxAAwobX8seVWtR1aPrIr8ZjYBgr6x6MwR/AfsxRdOxWNGUgz+g+q4AZSkPYUGlTL3XMbFZDGg+lxo0Y6F5k0A5IgqRdrmJMGYd7I6c+Vsd8BtsquQALAxrBWwK6UMwGA1pbUwAhaFoDLNg0wmNTZctNyOTHofDvQMf+DvgNuE06/+dFFEzPwdgcToEHgrBx0TwwZc9egNSs5LKvLIDlVLuqaPRVLndJfgWPJgNZRZmxtbR9gptd5lCqgkgVur8e1QT9Zq9elYp9zKkecdiniYp9kL+8ZIFPgMyS0zSUw3lychpwLxTNHxC5h3QjbOeVE2JviJ8ewcyIgdgi+4NJuupUoxahDEi5TxyIYyCB7RJgD3mqU7QTsUWR03e9Q0+XoqOilZINcMVXBJNlzKwAwh4R6Ny4nG4A9DKDwDO9h7mUYdYhLE2hGpwCiAW2jMF22iur3gI88DLoqpIxUyDUoSxWKCDPwq8NCdvFQtEk/uGChnrWyMgZJwt9Jfy19OVPlNHczs/VRB9Q9hQqZp6o6fQOGilu4DkfmLbxOArxDYxuTWIDj88sWwJyTpVmKkIQxvGB1ZS0k/0BTFRD8Y+M+m4uIkqnwuZhjbV1MO1qA1RVfDZ0NDTpgGAsKrxB1MZRHwt+8TIhLasqSaFHQKI3HbU8GlMT/pBxoZTvcgyrSTgATTDYuFAUnUOQDrKcChmXtRM3qjOBQkeOgui7zkDqRr+6MJkIAKLCuIDiJIeJODcg65HryOZEDCgegwgPEQU0YNJQxcYJ0AdxrMqmEJDFGcNbUQQ9QgESIgejGeZhhQiooWIIHiTqUnBA5rHwU9tMFl4dABx9FwXoCBcMx1V6wMMlHl3noB1u73dTs/gAqiiJHIIQY5xljH5p3VUEUAthsPXiEAfAeFCROqdKaenZRoqSbDmUYlVdHSaXD8tyCUhhuSQagI86DU4jGrVFRlIPRAJFiIN48KqoYmMamb1hTZ4Umzb55WTJy1MDwlOPw4RPJhhQKQAfZSk9lHvOm9VPvImpONBcBrtDAAHtb3RnVan6kHIALoI/r7htOEceq9Hx16AOjCkZkhgBZBCot+xjlAwVkg1VqCLQKXBnmamEvSYUjse4RVlJCoJryRlrFE2sd7QquMiCUaztwigoOxhHJN125SIal52Gfyxro92uwyjzVXB9lMmihZePSpdNYhYzMsHDa20WKFtXTkFHpRjLwF0bATCqccEgUMUEnzSNVQCMS7NVUJ3bRiLr+LCBVzNmOzFI4b2PN2sRCHkJARtaDhCu4xyqBSTciPOeeX62DD8AkdDMrCCxQihehEIB6IFaAePM5YWMdhqPt18KvEtzjVqhARq3PHFGhpFwkqDkPDNbGAamuVfXKkJxMGZR8OCB5OGQwaxEf+qLBZtlK+5rtnXZEqSWVU3Dt4Gi4bAuldsZspbNa6WidzLvPiz0nfCfEhQVnEgAF/TiuKOJBZamHerkVt3IqMPAeck3aJEGw8500tMJc1kWOOwpCoailkey5SY5z5miA2bmUwCtR+VmcE8ybUod3EHHv5RrpJo6jrGQSqrashof3ozNrLAKQTqg5kPQXC3gZr5MYyQi7Il/NbZgpCGisYh6+triKt8x20ms6HNeQmue7x+rXpyDvgPeKDReAK4YwN9D/QP+XWuCB4dgK6h7Zgi+jrRRyRwXTxabL7crafwgstM2xHrOVUwVcBRefBFTmDXQwjMfLZloIPPySwutYzq+h+LKpowEra2BWsURgrYYtJQWh9MwzwJ1l1Yzo6h65J5/7apHUQV804+55wsqkTpOsLHTPGaDg5FEFVYTHuaGK4LwOwIMWjmmiKPFeK/LDzWghedl+V6IObNtVikIa9VdSMJbLBoyA5H+TFT3qpZ9SzMoaGKGpW6p/aTokPiSiMNLocJ8pgcXcMWU6ThjPjiyGbhmOt6Cw6bJRNVUu1rsYAxrq4VhQ5zocfjTCYtOoc5GgKmuE+UvgfqSTNyScCBzVKlctSbbJUjuzPb0mrOEGLN6KQJA2ou/yF40JdgCgCn8yUA0S3nqHl9mWx3GZoi0qnZR4A0zzlfIZiFBHTQwVJFcg/6jNH1rk+4UNQyfRSQ6gHBFSD6sPH/NBRSINT4zINPOIOopDk/yEA2Q9GcEZzJsug54ALdY/TD4kAlABpXAA8f7DoPY12QRO9Qtrtpspy+h+EKjDRT17QcrhJBXKqqm9GsTVUzHuQeY3tqHwABUOrxycAQydiIoxpiAchmfugzRSk1k0QAMwyDh1YSZYifaqKKgG4JslyzwVMVNGFhDCNgQBXUSMxkYJEANOCCwcZkjj4WrZYzOhireezGEocnOlwwMSSKBgkNNqSvTmwwcDZrnRlLDNHBoFNxCEzgENp8KchxjwmdrCGPAGoxJL5G8JakwquiVqx3ckMQZe2jShlIPRAJFiINE9ZZQxMZ1cyPKmmDw0PFKbo0TA8JTj8OETyYYUCkAH2UpA551i0cUYAnXzCkejhwhQhvunl8Cxy3dNHDtfqMXUX1kK3qQxQ1xEfXUNmuyZn1ojaat0WkARBOPTYIHKLeEPBdQyYQ1czRBbo2dfwmQJi6r+MgaxosxRNQdPzrKdOT+vkSXEFjG9TV0z1fEkPwW7xlgtvQvGWajZJ9LgNLMnbQjuPr+GVGAnYNANSDmdl3Cbb7spJeIohh/UyQylVoVqtETMuQQGNqk4FsOq8xsTkTYhnD2pAD4/qc9PguqdDmK67vmJwWMmlMVdSDM9SEyMbk79BQzYRYxU6xlFAoNcn1lF5ETUO4gnmgYD0d/Rwkk74NgJTKOYpFyc/shFqTk6/lOF6u8pyE5RuaXe7RDDMG0c+AGOTUBKmUeH26G5PEYzDNSQIgJY5WHdXCq4ekqwZRSpHRR0M0bQsz67FQ28YVaq7kNljPZeOI13eufJ619EmR1ItTgFCPiwcE35lMaZ50b0p4PHMuTDYl1PWUaEpNBB7QPAYOXkcSs4oGo4QeXqio7H10HrLsGC0KIqDpDCzAB52nRVwzGxW49F9aaa6AVI8HrgARR8pSpqGSAuvMUptvVSlkIDDboSjFjT91llAJmHRvBmUAhNTtL1AFeFPhk9JpNy0Q6czcM3XeLIGUsBaWcQs55GhrX1gaibn+DLFQdOC6HUhZC97apByF2u1NjXypWCZSH3TrUg3sMEjd6gyk31JrVGpYY95SwjqMS2PsCqTYQj5lRZZ9Keo28Ul7OTSFuVdF3leBq8emqRXuMqbBrQjRr4j277MjgClCr4GspMAOYVtXI98tUYBEVuVB1e0ntu1BixxI5xpBb+FznF6f5bjGSaY5gesq6BQOTT1YmZEytGr1GR36eTU/MAPttZw91kxMZV37gatQ2JDY1mhk2SJAdeOk+iRxmPLgXks5cWWa68DVg9bUAtM6cJmANcTU4YV0TCktcFTy6fQjEM5uYDqtyItSS+lCJppY0sJEA+PYlxzzmM75WnM4k4E0EyfCgmzAZJjWMYGEa+aD2JhE2vgORwGpY2ioArxGMhuXJQXCBRzD6MMw+swFb5Py8fQhvUvyW3RBpmlKCQ0cHoyVNLq+qS6ckqkwBZo34wWpOSXFjkvK9g9rGvLQloPkKsWgGo9wdnLJWb2v+azcANFMdTQjNVQFCQgnKNfR09QKdEvApyoPJiyYNfz6LYaFnwZaPUx1JYiMqnznGjpqGpj5+SbcsukxsEUt18GanglHo+rSD4iZXlzzCeS1dOVhrQbJVTHQ0JpyPFKAXmz5fBzJfLVnSLaSK5uwJJiTHdl2wPf+6inzsQEQwdthv6Y/L+GgLgCU5pQtAYMn+AHKeGiX0UHH865IhdH3xRKbYN387hGGVg9NXUn5vktIDm96vAQjn/kcRtSpe6J4fiAnGSpS35bFVkc6HbjG9quuBQdMYMFNL5HVqJcl3VXhQDgG2HpsU53IRGMQz0yys+2uKOvzsrhp1S+DBq2BVo9NXQmiGgetJ5oG8QKK8umDC+U00OoBqivBb56sKadBvBjl3qBdVnQG474jaroBsKbByVXUNJtgbcgGYIb0OO1s+KTHTNLvZznRpNPv1vuqsY56rKaqYGJRuI6eqMaG5s7Bqmhfc/1tquI+WM1leFSiLnM1/gHlDb2J762S9E8w+x0Aph4SAA3mvuPA9FSCUIJH2LxRoNMQ57eXHQp6F5/gHJVj2W8vqUTdJv2H314SkBTt6ibJPhQblFVDwYekdWyoppr9l2eXuySl18r/4/L5s4dtlle/P7+r691fXr6sWtTViy1Oy6IqbuoXabF9mWyKl69/+eU/Xr569XLb4XiZcsb734Teji11R3ehlGbL2qC3uKzqN0mdfEsqMgsnm60EdrlNyvrTt7+jtG6dEh6E1fHbSOehwT7CUxc6QZ47Ck2vwAZw+rs3CdKmWivkC9qnF2BkiImGb8mwqBBvR4iY6VbUIzUv0yRLSrJQdqisHwcNakNGXmTNNp/+FnlOXfvysSIMSH/zWNjv9tjOqq5eHzyD6xZfZI/zXb3NxDEO3+yxHBebx5OqOskSerHE4uJLHMaap1mzQUTKYTKsZCcMVyp1oeI56csPst5JQU04F4lTDAHY4x8q80inr/aYrnCdCYzTf3LgwDuipwCI2O8us1KXhTgV7Sc3XpF5xAXDB1Qn/4kef3Qim8XEl7hhfIPGjVRGyhW64QWIz3y2x/Uebwmzb66K4eqGxSgV2uO9QPkGlUfVV7xpt0YWrVhmj7Wr8bciF4bOfnfF9rVMdr3vG4SUK3bFTdbDD2CmpEJXvMcF9UgSRYxY5iBdSlyUZC8TpMv41VG6XCW3gIBpvzrImKbVAK6KozQTpAxX4iSjm28Zru4A2TwVyPh+eyls/6KG8VJSMQSFT1RY7NSZ5EGbC9tBqxkxSVe9NrqNrvY8Go6s27hqNW9wtcuSx941kcXEl6xmtskH6lYbNtE9Eo9JVtZc6wS3zrA8iv6Tg4ihRBAHMn5cDWu8L0jn8T/Rpu9+qDgQ8fkIBQsc83BO1wcRx/TVQfXpQ6aJuNjvDtgoQRBRE/uglBxGocwDqwKhOy5g3XAF6+H6MaJdEK8rQvXZsLiy6lpl4tDjkyaj+Shhth4L7fF+zvE/GnSJCmrA4bEKRfY432bJ7dmW9Ie62MhDB4odDh+1oDG2H/Z/KFKon1rl86dRcDopc1mX7eOhqrXCRtjHBIy+W5kRzTxrPu4eNPReXk58iTtGYNcQilyOYfR+/zxrbnEunsPYEheMV0WTAse68fNqVsE5Kre4qvAUWDJkBYjYPLjfjGJNRuz9z1qRDYijTd2EMmz+dHjmmcSjLCt+8Aj6Tw6bosCAYo+gcnvs7OsFEbNYthp2M0ULtucy3fMSC+bSV1+rGvy2RGiIwSBolVyJg1UzeTh9QNudYCFmPjvh6jW07tWhgJArc1iJ9MWbgG345n4X2L1bgq4Cu5KfVcwXWRYo1QkGHyEOVnsKKmf4au04ob9fg5hkLNrPQYte03zK3xExeN4+xRDuO7kyx53zjzb2zlVBkx0AuyhfvMTRUG0oJWxOGBx9rgUXAb7ExYy3AfGx35c7sO9R3gzhMUKlDhwWxFL2qCrPI4Foi5KW139bUvJ8bLbfUPnp5ksXMJRDxRf9xGYZKfxLKCOy4WE82VGPYj6m7JYBxJpTSfyJM9D4rKK/P938N5Vu38/cfw/Q7wcHh4VoPTSrOpC5YTvbjcEHuC5Nn10U4KPdrizuZVPS9N1hnCUie9nmUy5tc3yJgyV+t1Fg5EsW51KROY+z4rbPy+PBl9raM/Fk19wV9Tblp4otcHBII0Og2anEXrHf9z5L57qElDbCWl9/JkndNSqJ6enzss6Q3ehlxmG/O2BLaslsMXxzcMDtsnn+FyLHhzoRbsOkQme8Hws12rFsXdzN5DcNZXQtqll5vmtfwflToYM7YVL1oxFcCZnve59HLimgx+QZ6s8zY2OjIhquwMHfSbIRqMwCC88Ok4HPY260tefd6WXRz5fsT3cYkhWK42S/r+4AGeeiIuAQs/Tp5Y8GK84vXYmDVl/R9IGiOWP66mBW66JzcBa17tM+npoMdYjI2yaywiaVumO+TLIaxtqVOBhkN1ucD5KIt8VyJU5eCfC1EVfg0MMhnp1ISK5g6SujNyhD0mOn8aP71dMYmge6fRoL9/VY7n1C9m3Y3iAU7dNKQLvyvrjFOWhil0vdMA/BNZXIJQCH2Uqypk7k11Ls92VPd+17VZl9mM9u1JNRTV8detVkGdCp8auT7rJL8kdJb+k+uu+KneUc3heHMgcrCi7rO8pHghFl+myP64/22RmPaPjm8u6g7oZxSfixqURZDBS78OsW0UdsIlL2u6+jgYRTKnXj4/YY8wVXuJa5mS1zw0p1nqNbaQsWitxxviFn4xTJDhdQ+Wo06yk2bYhmrQi6a6FYK2vOo1cfZL8dphg6fjSpT9WxUnqOw3x2cjKoEfl2j/MUeJUlFDr0UXrYeuL4qLVfCK/Ec8Hw1RnTaxDTaxdMf8M7akRKMvlNhlDkcKq6I5sMtIFzBQ7rJ3mAsDGfl9CS92UXaddA6Fu9fiX5mEVUNeeR3rJsc5Vrrc9TdYyzTLoAEYpcccLOoGKZw97yo3iP6hqVZxXwJEoudcB8VyKkww2UOzm8oBKnIGaxzEFuD8/xie4rCG2u5Km9k1Lu5DO9Dut3gc4cBm4QQ9FqZBy3Kwc+XWdR+Txg19ef6yohnvoTQYB++1aScwygQvMlT20h7om5B0+NML4esHh6wcBV5+HmuJ7hMQ5g5zilb5Wlm4Pps8sVens/rkIplTpsgGSPIwvrWNAvmM8ODhZJddw9fBacK5jvDkb1VUTWahWt/gKlAnSwqcgBZ++O19c9ke8MYAgHeVzU5kaUQC5muluBKN2Xn8c7/xBZLaaa+OQOwudDkNiQbfQcDpNrsYsqa86zifbNQSZ3ocjltrskI2uDx7QBd0D3RwWMw80GrvC3DJ3lG3yPN02SZYLcBwGWVHDb8JLKdS+Xut2nKRFLhfv0KhiYCG2Jziz7AgDFTvSd4anbUEd9AIAhDhuMgbda3Wl4od2qr6B2JUK4K1md//Fls4U1LKbYS71SoIch3HvfuiPD9AEhvMagbkQJ5GFhixYKcy7l5PJ7I3SQfnBYH0ne3CTtGawkO1oNXR+oYBw8AWox8kz3xcVb6Q98U58kopcc+92hP30dSGkQy1zeJfyjwSX6VN+hctTBhBcKEIRzC5O6AePnyh3Wb0PkFln4KVU0jjYbAZu4lo3QLrM75PcTZ3f67mD46uuIM8t+d/AVzbNueTIpCDmvUaDcZf09DE9fFfhhCHdqnD7scNkaJN8kjxVMGRHGvZXWFa3FAK0tNZSTweYyIcoWglkGKHbxiGBrSlfiUqlTrzt/mhIhWTeVS918mceKskc8UOyyLsec8OLCZApc5Fdf6eQxzdB7lN/Wd6IEgyB8WzhHJS6keVTBeLTSKhgtGkkSQxBO3rl3eHeaJ+T8JwlFrsgFpzpQj1jmdIuG6UpOsqH2yR1NzyJdqCmg9ufffVadVhJt209OPn9DrG7A4Y8tctLJqPE/vycrllQmZ5U7mYmVQC5mzCL9/tcmaU03oh2TK3K+dGrrH90nOEu+4UxCr4byawkeBAzhMA8412CXSx1OA8WPbuy947Z0AQSUO52S8M1ja+94W5RD/46RFDpOC+hwYZGk39scCDQxkvT8Wix0PG+rM2tJB2/7JFzqNltTCJlavG228LzDEK4tJA+mFkQI+xaGOpc1EuJi8yWuGNuUUWWRyV69ULmDboQ3aOhZj0JQjyAARz5Cmx4DFrdqoNhJCtF9+Lh5PG7qWjRcyaXOmL/i6i7DVa1BL4I4UKaTvRkiy/+cnEtlSyEM4XB5Qk6HbVWcim8/uRIXe6yEyhnHp2wDoJm+OluHT6jXAGQX7goc9uQdSnGSAb3jS/wwjteT9IGDfHmphfRrsb/ANLYnwjlwWG9uPc1rVFYQo0EATloAFcQcFgSxjxbQySJg2Z4O0OlkeoVRtw4FySgUOek3qKqP6rrE35oanRTbbzhvz/vAOIzATmMhMrF9skjjB2VYPDuBAPb4vyJ8eydmreq/OVAHOPe6n3S/4o2IpP/kQC9gPO+cxzPuEXrxogHzaEsnWJRA+/RijeraF/kN8HBlL9oZ+EKHHSDqG+A1RfXSzAi+R+UjZTXJ6imUuWvyn3Ms+R+IZa47ZnWVlPjmRv18VABwdtL9dPOpxLc4VzjrssUuZ80K9QoDYBqTSz0wf0BJ1ZSI0lWBnYPwaOFoKzu2SYUeeOkPLW4WwAF/k28y1F6Xy/ZlqdAV7zkqaYgV2CypAPFsg9JA38QI4T2KojNukh1OOxIWzEU3xLnCn1YoctKZeF9cSWMSi1fj6DaqckGebgMWD1c3ddV5fN3kHdV1Lx16DN7OCWVOd1CEs1NCIsmNSShy76kKMVTujh0Sl2LZz+MY3N/RV2QL3hW5/CYNKnfa90GsftiGWWidY9rjoIpfeQhHn5zuFppoZpBPDlu4GqkXKUdtQILap5edNl74FqWToqeD4nFRE11biRUodhAeDzWNaJa9x/l3QXxwJS6q5uYWUgKnz464LutH0TeT/e5i5MeJZNhvP7kYqzu+Vvn6QuUHp1k9rs5NXnVHLpc6YI75Vo2oD5f4n+K9wvjV00m4uiouUYbSGsZvgnXv/yfoclQqdLxsuUjyW3F75AocFKR5HN5nNvs+HXfgNRoZ45tSn4ZB8CZpsvoLOTh/kDRiqXA1ymUvPQPfjvWWAnfNUllzHsWyb+4Y50n5CG4jQ5HLfeEWiHs2fX2KNzyXqKBpoXNJg+YKXO4dPiLBF6n/5OQNWCZ5hSVnWq5gnyLgA9rghHI48MRfLFuNAGA7FiYFWEweokBffR55QPstSOr2y2pmp7+LiyOlOVz+D32XltkRs5HEPEI8uXAr7LOuwJXOYPJZ6drqazUpxTYEsVRQGSBUMAcjhB7XwVTw9EwFhyPyz35EjmW22fPtc39p1kVUinERzSAMuJPWYplJfe6bVl3U+WFjBnLRZEjlW2YB7uTCDN8NcwUOdx1dJFVF+EK51MWi2j+ng1EDxS7XxVVNhHcraMeoFkUm7aM6OJ/WzpXP2yEIrxaSR8og3as5dSsClE9LwwSA260GzG+WWmGgm54RwOkWsUzkkzHzeT0imfG0DJTFDCYfIaytvtYjDalblO/Qw5ckayQfDq7IWbV5X5BCWWKzRftUl86q3iQvmhLHz6vh8V70df6E1JkwihVoQudvCNLhWL8tqHfQhDFKhe4+3LD3to9CBOs96/GtCxdEM8adw1mNyvGxkbAfy6UOpxm8QVd3zfZbLmULEYrscfYBAHls48c9nXl/6rPqWoT6yIMdS0aW8QL2GCLfiHKeHWDqACyy1dlblVa5YSBS1ke2wANfqzopkY6lLkrLeYk6Q6AcI4YrWhufR3Jh5bH5eLIaMaxfb1G51vk51Z1Vb4nIbaY4biJfScU/8Q1ZP8Z4F2UAQn+m3ce1WUzGZfuvu+g6MHAoA7eBFm5w2j5nYLTbCKwMo/Znalt862dveCSdfU+y0hpgHe8UOxUMzr/FlLm67Q7+HgrPXa74J15UuskKTMmkweyTockJ3TwrCu6DHf8vnw8v/AlJ/NNx9/4eMMlyBStfHXOsi2gr4ukY6NfJoXPuDvPsZ2+TFNWXRVlLOPkSR4yDS9Y7LBppgWIHlTbfoIdObNMPAgPIpauRBf2ct+mVIuiaBI+/YglWXus697HX7neOvyQlTnIwileUudfg9+cJJ6Qz6UeB+RvCMysskwEiRtTGny1u2lONB3RUVfg2R5vRfVZUSYByJ5/KJxNJ66xqIzQLjD193Y/lYdK6/9dWuIkUihzk1Ayxwlv97VNTf7ppUbR6JxSbVwZZze7Hsk7YPsdi8tjR9NXXqufQf8+zpkwyGc/wfX8vMg6uS8voirMoiPG0wqdofO4rKQ1uULk9dhrphXyS8nCw310YeEgdJXLw9N1j82OC0Ssv3QWYn9hoLU54lDUXYbEtvspimNDCN77YW9U8ZrL4m9W6zWQqzKcPu6IcFpSAVyxb7YrvL6gIXNy1P+GNIAV0yNaqxMbZa9fFJTNwSDTuWFgfA3jTNMM85FPbm2I+lm6fjQAvSZxNh0ebvzdVLWdOlAodDIGtwU6FWC5dxjFzub24vTmG1GOuwME8i/PvzMs8wU4rFs75gGJdx9uWnLOccbu7/2gHXQW6WaWr2AeFaIXADnL1IFcPcvVfQK5Oaa9DZOiYD9pdXqqrziMbh/b+aLB4AcaVODyIqsa81Z9Lwdgulrn3U0IZiA+KxyiWucjKvEZdVgNRYjIFTk8IgUiIPmEQaRTuvJJsQMxnF9l4CKloCqmonId2qYv4pq+umGRdgf3uxmlfUClzCFfgsBLuaBSmrBAMhMzn1ch8Ju9giNAf0XhIfU3d9V8AgYFAPIJ/6MK++Id6ifcmPoZf0UmSpU3Wun11oV7Eh3RS8WqWyduibLbnRRVo0x3ReCwTTd15lslVscOpiGL86M7aKrZ2vxOVb0Oddt/zo82mRJV4cTF9dhjbip7UK5fu03FybXm8ZbEI66zF47vQFJXnWWltiyKK8ePeVholAXS+5goctsouh4OwVQ4fnR7qdJJYfKXTf3WwIGD0Q0DTf3KwGiRVTRuWDAbMd3dsqpmEyt2xt9GyQLxdyUH+7U/+JeGCz1fmLSru/iiLZgfKvLHkKTuKfBz3MVFKDZ/3IfDoMgfVOq7gIPwseeYQoWkmFbAVARFUwBaPrzhUVJ5HJq5Pgv3c3K2+BJonGrp/arM9rcOPRA8Oj3o5YPFYg+qq86zA90l+2wDmbva7w+FJzqhw5ZpNoY2/KDBL98lB12oygd+6L/Oqz0tczfPZ0mVjrljqYtNdd3bpSyIuiDA/Fq4XmM/2uN4l1fHt2VYKJsB+d3SPPim2XYx+2Tt6KnLAuduVxT3a9HVP5GeWMITD3lvU5kaUQPvfW+Lvqz939po97ahDLuijpr4jjfbRUS5Q2tIyZJfVYfbYed3QzbMbxzVlnm6lWMH9JzcnAkqVsw2lyQ0WtWKo3B17r1ubGgHA7Nv6RCf2qviOhGXIfnfEdpSmqKpUOLlSp3vae0zOGKpMJFD5alZ7/4Q6ZFl3Byz39auoN89CbRuDYllwBY74ZIcs5vP+LufjCKM+C01/2yjilEtdDgxdchMFaqDYcV6I/ls3El6hyL2/MFq51EF0dBllYMRSoTPezj9BKZdUQO4cd9KUJcrTxxMptTUM4dJCV++CKIgiZrbEvc9XyUO/PUGXh2ooF89fMHoO89mVr5tvdVGTvTtPM9IxiL1FCM8WTh9MLYwQ7i1c0fpjEjDdWGDIwBa1Y4MhXVvsRYJmbCKEZwuasYgQni2QuvLagyE85ROR85hq7kn2FiGQZBbgMdoGiWkBHqNtkMwW4A6mvq6KcFCevjryB8x1PpwG57ERipzMDIRQx6QkF10BhSLXEVPRcEG6s5He1EPlPthVWF2wXaAb0gW0gSJ9iWUuWH8k5ea8wHldfUUlItwomloVIC7+yCj9XjTTsy6lJUYPGdCiHCJLAeKub6gM91C5g9nx5gZnGMgNzRV4nCF2ijPEztnQSk8yZEV0i++EsAikFOkhXZx3yw3wtGP86oZJVpqnr46YgDH7jfBDUn1HGz01VTBufT65v38t97j76obp9GGHy85lusjFcIsggC/+/0IJQGWx3I+D3+ASpfUb9A2Lln0VkMs19ljtKG33vHdFBlxpq6BCWoI4SA3l1dJxkn+XD4cggDd+ebGCAH74z07UqGmZF9Y+C68S81juhf3sWyK6WIiF7vtCq5P0N0vwDsFDOKy0hmi5Jf5nu0zb92VJCiXH0MGFtyYzqR4yvMULVEnR+kywLtJxR2+WNfSEIUJagEakhnJyThm1PM2ANGAu7gRlepdUSGk3BgFcToIYvmvnCtxtlNCLJbHMHSs9IpIlvWtq5t2Tyq5oXcnFaSxD0hv/8ePP5drFHqMu0DbBuXTcVIA4uVN8RD9688LHoh6TYEjeFSowB7mXpmhXX91h0uOEfG5vNN8l+ebTvXQI0IOu5tJsMEt8rsh57R2u6vDchQBKnwSGdmjmuWIb7bTiOmW+OxpFwCscx+3Je8nvibn+wDftmS0icwEofZjLDs08zDW0LWJhvztI7QptvuL6DmQyqdANL5Caifn8L8C4cXg1gD8Xe6Dfq4AMtwCJh9VQ7twP3VSKZQ47M2AhdrcMn1VDD9o0BwkQiwkAcB87OQzvoPMZVO6ibaV4RwOZyHqsUOSBE/DnEstc3GlzetKQ1W3muys2oINcgYuDY1VJ3rnjRxdumsjeKpxQGHAJ4CeWqqPEiOCk5fnEQVN3Rmct2qDCwWoqWl6fPI8YlSVe3BOa56MNdALen8ulHpjB23G51IWSqv769lXdT98+gtfyHpfvwxmo217BQStAfNsAyaAAcVAZjFezoVeyc2QrGoLFAe8lhCKXjWqoqlR7AACXVxgpyqdQfHKgR6nYoe9Efn4Fcpax3x0cRZt8kyG6ywguosx3Z/l6QiMIQRK2K3AyHc6QP6z3CWQRSCtNBliXUkHUp0C7wojGV6mA686pVIQrAG2fOSTdF2fmuSqGKzOQddjin1i/ZW3ZcaxdMkYfY5cVlnk4Na4ffzcEQZb231yxgF51QpGbtQyyQbDflz+BPrkVRO/buueqIatmwOKTRl1Zdd2S/KpM0u9kYND1rljmgJU6bELaFVfgeAeL4MtiscxdLQLRSoX/Aqsn3MTCYgpYRUsaWsY2AY+B4buH2QZem85W7ycTkf5rkmWojqO9sLh89BZD/Zn4aJUvGGPtE3HuSboaoAYlFHniPKevGwnJNbgnkH36zFygpBLNRsO35fW9o80W56BHI1+yGmlzgeqmzGkyVBQa6JlD5XVI0tZft7CJvV3FFV7x7ga6pfW2KLvZgtYdU+iCt5121No35fUsFHrjVXvYaQHd5w0OCCSXunBqcnPTmdkEZp2+LyGo1JRmli/8CF0B4tIGfXJ2VXR6iYicL3uajpx7O7gU7Z8nRKTHOLzw2LwOMCYU694PzpOyP37JkRjYEtebEQgjX+JyYJtoDDkDQeV7MzxGvCmf41ZwGBVhYVTSpGlS6hAYYh97+EHeAQ9xgwSehM5D4lngmEfk0X+FO6jEzf+LZiMhn7Y7IEvJ8N3Na+sfDS4hZ63hu6PFM/mWoV5UwLjVUC79vkoeTh+QRAauwMld5ISsnduilNKHCUUeoo/mpyuLDBL7KhjnO8mIYZTPqtbvAYmEHb66uEKEBaRci8CKkPkXxhlDdC2a7VdqXdI4IIAl5eMhG26YVNibbSxtSvoMv3+0FsujAMLq51VgiWmedSc2L5/25fLDky1nrovLbRHYbDG5/phm6D3Kb6UYH2yBI75zVOJCcoAUihzv59vaYigstsDJyBc5DHs8zSnWu+mzHNc4ycAFLpb9xOu8nQBS8L64DVviDCKP1a2tPc/CZpoEzx9y8b6sTnBsEnfb+FNmToo5GoO2cbOCmBTGMJMxuI0v0Yf+F8QxX7TkaeI0p4YKoT/jx9WwULBc85NnC8ox0tR7dI8y6TkF893JGl/WoB8CX2KPkWa9ARFyBQ4b9w5OIrzzSSIc93aAjORzKdjyx49O94uoLFEp4eIK9mlpJ7x1Kx6fh2/2WN7V9Q4K7sN+d3K9Bp4ST19XI5LGMJaB+9mAxmcnU9edSdmC4+J7xcOPdQKATiTq88gefXmnQFfh3rxM0Cyf63A9grXeDK0/QePZbU5IfXKXlLfiGV4o+unvGI7SLEZmphGNl6KvrDsPi3dtizimr66Y5CXDfnfXjy6KTBnkeShzMQadbcS0YcO31bAh0bFisOGIxoMNNXX/tdjwMmvEjIvtl73c3HolK91X/CqUoxKnkfxLRGw+8ayMKNbO2f+JHrv01Bym6asTJgmJS30gKptzRDZXR8M98fHVHdqiL0mJqXkpjIk5VB4cbKg/D/u2jQpXO92nJdXofyGG67XioNNW+yrJ45AF11vr2UoyUjkapy6rDDQls98dsNFbYtkIx3x2MFwWZYpIN8j/j7KM2vGEMxQI4GD+KirxZUb/yckQVyjycAtF+zzdvqu32XGxkfZf9rvLdVhek4UzvOj+iOofRfldvB2DYZy8pciCfmxX45CXT/b7hmGcWzl9SO+I/oja+OL6xlSgqxGdfadCHZSGsfk4AiqrrlWI6pJN+iWZlEO8ecR3o4v9fUEK4UhQQ5GrPYHIy21S11iMAS+XLmfsUi7Q5luGqztxd2I+71OwrukNhXLUBY3mf9qmkBPmRShy4G6akm4KIwoqECoYx1Y+Nts3KCWiN6sA/FypT/9bDy1D/3kY71beoLzY4jwha0DbEgfn3dpFI0oNEGA121YrGvpPERT/HsxX/1dWX7t5JLLh74mcFlOyExB2vkqq73Hcs2WMPoqQFZZ5OIptWppAoczpUVaTy8ljmM/2uD4k6R3OkcyqXIGj0yy4c/IlDgdQnLc6BoBSKHLoZZOmCG3gfgplDqu+LMVNpf/kchQqbqkzwDkip2r5eYpQ6I4X9EGSClcpU+LJkkAh8nROVEcZTgSFrf/kol0X+enDjvKHnK5XKHOw5UqpXF3TuLq6Xmp2192nHFi7XIHDrKGHmghiSa6w311k/Tu82aBcFPXDVwfNtMmJ3OjFuqCT8kWrWf1cmvSw5c+h8lj/hvrzCACuUVVWPiWQm3Oq0jtIKlzbAe+Jevj8tUEN2rT5WI7qmqy98AdmIEoPZrfEMw/TM42LiIQitxMUUWyoqU5mcKnQZYGK7+G6Ly7arexTNHxzsC5JMfpdo/OH6xof8BbJu/r01QET2uCknxWRNmLZGpdztEUctnQX26VKTI76YjCc6avDYiiLrbAU2i8OamUhKJWF25ODXfYoohg/OqjMQk7xE6c84sepoDm2H1wOt9/+jlLh6Dh+dOhHsRHmtPuyT8P8JeH5q9auJNxsj5/dcEHn/+mzw2Gk3StTME2qWObUw82HJG+SLHuUOsmUrEYIskMNk4IsJg8xqK8+k/lYzrnnnG2vvwKTd2OuwM27QnaucJLuRSlaodovbk+ScmlA01cXdauq5Oef01dX48FlJc7X9NlpfG/QTdJkNZFq5FBOH81X0mAhkNWs25Nku0vwbR7orNBj8XFWUFZdq2ntZ95ln+iJujdfX6EtEZWhbt4CMg+eNmJYK2uvQ4n+UGxQ1gU84E+AzHeXBwtV3dUskUAeochJUe/0jO79pthRoPgpipd4j0bmUZLn8nnyV76VtzGjLe0VZPYBin1wv9bjfh2C+1c97l/VuPe0JXxEP6r3qK5RSdbOaMMO2xlgnB4bhC2imfYJsHU5MbMObtnDkdtT9QWNEpHilH8tSpqZTPW2DihezTqjbmzkY1saHJSRweUVilFff61aV+zn+fH27cu0EM36/SeX3hDpATwZZL87PfjA5Psn8k125JQKfy5PYqqukk8pVSkhvFD5quRENCERJCGWFQ+9TJLCsk6f3XDJF23TV3tM8PrxWTgxHkU+uZjEb4uy2cbTKyV0HpxtgWMeBtfrkCGaY9zoZC2BpAv34aOLjWSHUxHP+HGJHWdfPlklvif96o1bgU5ZHC4frywDgpl4PZK+Ty+3VdwtlrlwpgonX7K8LYqmIBF61H5x8nxHiTh3/TcXLH2ctOPHo6a+E/c/oNgL9wVK8Q5LTtwwxE8sMT6gpGpK1KVACDXKM6i8TPLa+ms9Gs4R+/aCOrsJKmT36SfW2Pr5f4NJk1WwwiZi82dIDYoDT/7kPHm23RUlPbHf4NCoPhwqD2401F8rK74tsg0U9Zb97uaRC4XCZ7+7OJoM0we/K/XBGOfdSzfZF4jeTW9kB2Kg2EE4fMeCna/74qArJt/F90HtF1fL26dcvL1jv9tjI9LsLUbZhv4l3DEKRe6cdlLkN/i2KQHfdAWIA7c81GUizy7z2UHzbBEMj7J51ZMvcvEWqJqsPstvJIeB6buLpbp9pUf6oHlxy5SuZhO4fMzTOM/uJ0Q+rxx1tecR/6fRHt1fFk2ZIimeIPPZtVfylsJ+d1k4eU1OujI6rsB1pO+S6g4aavfd1cB2JqVWmT674rqsS0VYm6HEFeNxUWQQvu67i9aap/Crb7ZgNWLh9IFuwm/QLisi5B8Tsfn4VRtRzCMlep1UjgY2fl5S4YylgsXd/KZZgdRNufRpe3CHRlyk6TWuyiSvtrh9vA7RTAUT1oq5DVclsjtwy48RxTInk293epKMvsNnV7c/2EfS30GyrQlapvmSfbsnUt7G9+iDFNOOK3Bai9KzheHbyvatKDYNDpX3jnWwabTkJWKgJkJblhJcoasRUO6lXxgC8uc9JqNTRjoAyp08kDpR2LOC6IbEFy6vDHSMqrTHAMVLnos1nEWYRtL2mc9O809ltmQEYb+7c1NnOpEJCpXvS3X7dHNTIWEbG745esID/u9O7wWSOr27xP8U1gfz2WEGyFJtw3jzdB+/7ntrPim2u/YmVqehKIFcb2z/hndHZXoneWTKpQ6YM5Tkokvl+HE16sBxkn4/y8msp9/juUspkHqoCNaY1u1OQv7cNKm0PpjP+3K6enouC2QB3SRt1Pgy0nNCAKPPfbEVmrUqtV8w+iGfUqevP/Et7wmZqNuifIzDTSI2rxfXJhQHLlodF/WyPA4TCci83C8NGA4stDoW6k+Q/dQFPsLgcPk8xDAgmO0yodOIXik0pVde2F4rsL3+qfnppCyq6hJlWRSOErH5bGxGFD8vV+3NrTJvYqQJnfB4uVKqK890cItq/RxMkHQcsHGyK3Hxt+/3ZcHnfvhqj+kr3tyi+m+FGDiY/e7QL1yL9tf+k8P6WHVGmtliUnS3WlfFUSrmduNKViUZaOKZWNJhwuUpIXQI5pESbatSFNPum8s1YkmGRHsv2Xe4Enf7uCy/+BKHkdIgOsJAu0/LyobLu9ZHRfGcXi51smj31/T0b2FdS4U/izQ7Rrc4B97us9+dZufH6cMuyTdSskuuxOE8WLwtsqz4IZwJx68OmNCPrzjfSKimzw77eSpyXvfFxTD8KK6F/pNbLwgC6Y3b9NnFmWUrheQevjkYg6vqJEsq8Zpl/HrYZdW77FFVFSluncCBjJOdaf6auaPSJZaUoeUMkh1MDwJs3hvFtcGE9brzSQV2a6utdkQI8Ril1tgDr85dJSXRX3071+Nx7NtvL8FZ9JjoY5zRS/themxmW6yimnKXueZxBtJUQBZz2gXUYYzp3LGTIt9gOp/PzqqPTZb9/vwmySrxmt00+mDmITpAe1N+fbTbZZjKx/4iAOvlhb6eyEYD9HDJYMFOugYC52pEHYGbtN0MY/2BWEvLE3lI7C2jI1cIVVWMwYJ5MQfXzqr5g+9pGIuwuPbPJqPF14lDploq5lAYbq2oPWBfNUuMnQzjhh7N0owwNDvc/iW3Wg4AwRU3iROMxeQDiANn3Y+g1p2LMtsE094UzuHHRZFZni6EGip1kwVzUT059Gs8avAdDNQPGFxLs8AF+pGUm/MC53XV59C9/lyhzVdc3/X2JN01sbGyfDEsVbHgC2NDgTPA44rAJ+YOr/GUYiJDPIFzeYfb7y5HXKlOjDOugDSQj0RsMSWOiHuNDGQev5mFBlMZfUKd4JymL+VBRltc/2X8uxo+XHZp5No7imqqR59DbJOWINUuSSlxCcRbXFY15bRvSYU6kOfPhpuS4c63f4P/j+wk68J6DQBEccc3qKqviu8o//35619evX7+rE3FS8PJZzfPnz1ss7z6S9pOY5LnRd0O/ffnd3W9+8vLl1XbYvVii9OyqIqb+kVabF8mm+IlwfXry1evXqLN9qVYvUdrheWX/xiwVNWGSx7H3NINtzQ00OHzZ2JzfznLN+jh9+f/17P/m2e43/4TSZwycNAFunmmYrbfXooVfwMYlnbs9+eY0rtd63+gnEYwRZv+RROFQl2412eUJ+nTnJEvX2rRs1f5XTP5fVKmd0n537bJw39n8dWlHO5U6m1/k9/Tr8P4jb7QcOzXYHaP0CeaD2KyvscYZJ5mzQad5ZeYDDXZhYzzrBreBpOCGqU12oSgmx4aRxhnfy8ZAVN7JQmie/7sQ/Lw/v9v71t748aRRf/KYD5eHOxMsjvAYjF7AbttTwwksdftJOfsl4bSTbd5opZ69fBjgPvfL0m9SImk+NSj5S8zcYssVhWLxWKxWAWiffb4z59/+1V/KrIklsJ8/9tvJgLjhOxPIAvKLGFuJA8DZG52HcF0N9OdWylzWb4DSNkmZ2kRg2MDiY7icUBjAe5bEhxJepNIhJs6LHwxzMyBOZXnMTZaLbVIXdqU2nk0YRBysJ/Dif5griWttG0TcaAKhr7TlBsNwUt9H7AA04FnNDCq92+//qoNlA3hUBU/5SlClnZGHu2+TQ+y0fWnp8x537Ok1SzP6gGg80nGhc5D+Ceoz3BLmO4moRg1iJhs3OkfP13/96bDrA1+EBzn0e6/fiKr8B8/vUNM0kWHLuLjHKG/mSCEweIsaWU8V3thuMHsrz/9P2kHFgtMhqW+rAH6oui9MySNdYf6wi+FbgnrvVe9vzOZqJKBqzzEV7A924c2+C8R/E8O1iBekZxAMuC6FuJVGOyvDwj1KtmL44PmXWZleDo8FhlYsIObWcWCL3Ih3oG08NAuYFFa7Hp1z1rz/mqwy1XM9mL8VcAdGoHXKa4DchvmexgJ5FnN7Xgf51vxmujCUBbl2zrovXxVsQAxFjuFZa41G4VBcTkOq8jhE+HmWRGkb6HxWyKor126EKy0DH1Rr48M21sDEWVpYqItFrBeHaxUjkWVAFDdY9pI733wcvkCDkcrDy4CUposRfpQrsWislNUxUFtfIiFeiyEyxyOmZK12cniMFzCahjdCFO6TdWQkbrQjANPupPTA76uuIk+xDil5t5qEZC98Y8cpBmy4L7GmRUws0MNzzMZJDgSApCcXQUcXFIjg3he9fh9Ge0cQTI+QmopiLMofQZS59GpqAlMrYElRXpNRD18zg/fQXLzgBdOaiPxnt0BdRRsdV16+tJF5xE0M48tpez6yMQUqrkB1M7aZ8djEj/ZbSFsXkSxZlTzKzKVxrWAacvwkoS3qGRcDJQT3y0kIB8g5oPuJNXVFKSOWW15JGlrXQMVxVG5gnsVJ4cg07n9FMNaB2HmGs+z3QFGq/hwoKJbLAMDnZwDzx4eYAiRlNuxzv4UWJa4ZUDIFIPpKbPM++zroClF2U6EPgZpJtmENHbCDQOqhdk7fczke48OYjUkoxvyNPsY72Hk6nyA4BG5RhpfCFKX6xVAA/qQWsqrIG2nV3H9caQGMkGi3/udMe+1g0kxF33AvUKtddd0d44rKO1Lfv1ZwYEDQWQWhtCspwJICxuDEN4KYnFCskSKgWWvfs5hkj1eBI2vw2x51mDYtamCwR8kstbJvv41yAq+rBHGeWq3Ld8jJuAoXUeB/6zT2A41vIyR0ZYfvsIUZi50NbaHz/au7KsK4gV4gltAO8nt3k5083XonWY7AESHWrVnBN1nVXrodCGo46N8VKxtntM/KYo3+ZFfnAj3c+PV5QwY98BqBEmw5xrCQseMxCCAtO5otbIReHCL3xJGW4OLm1Z3G0xW1LsHK4aWWuCdS2DvnQD7NzzexmkWhLwwPLNbpUe0a/NtLbPVG7w4hGbha1R3DxarYAk6v/c0YxJ5Si4C09JasL5RTJ0ELjzHHwFm1nXqIWD1/jEBQB3+X3Xho/UDErhtwTa6Dq0eXyFbf6aBqw5vU4W6uXDpGigs9cdL9C63BE3jzyYZUdF9/56gM1q/Y8zAszCH0PDzMN5j62MJ8jt6JJLaeUpNe0OSlU/oRVGSTyz54NkFqHVRL/XczfviD0F6XjwfsYoM5CUjME5Z4WbaiDlUXuTZ7XLlpX8Ja0XfSplswJ/jzDXIJlGf7XPzSQaAybJJyJ7oTS6xhC2yDm23IY6Gt2W2zwVseCWp9u72ouYHeSRbV0+zg4j99qg1Yjl8grs8CMNXG8HptQ1N3sl2K3e48KWg1q5hOg8PqQSnLN1kN9XuwpMrGK5OnW8qnDFLqmc6xCp0Yp3cBTi9xzo/ODJNnMCrgN0jvR+2iLXEzxVIVwl2HG7N6x+58yUSNNnF0Q6T6UcHSHMhKF29Z5Bz7Pc44HX6B3zIVkFi5RSoYNjv7HfgPzlMwE32CJJbJr+6aZYwAq8xEuSK9d2vBsoqx5OTwS02Gs52u9aQVuhfpxfxcxTGgZ3PpoRhNzVforBYvhU4K8o+VXc4Nw8deEZvFEogly9HmJBVchG8iiCqTGsFkMS4EYD20v0hSNcBsmGAi1llIRncirb621yLIsKKgJUEANrqM6GLAXQPXlxFGN+BbZ4klrc+NZDV6zYEhdqw03c0vFuQwNhymdYQyeZPwFotrGtyW3YZ4S5W8oqTObh5M4yULMmxHIQVtNUjTkhdH9HBFh6CEKcfRv9KSR7hd39H51mcdgRtkwaoOwlPv04vUysWUikE7YQEmTrYfRw9oSWGgCFD/tFW7pBJt/3xrzwoXQMWmrw4TRF4Z08BRH1hSMG0uLXg4mi0e8HIGb0f4+eC1jKY2m4akPUPH17JCfwqTir8zgGVZMIE7Hmw/UFSh+Js4ZavK/DhDsO7LniIDhf1sdTKpCDbF5oZeMgPLiamgBe8uIJXwVhn4GgPh2R+T+IQw7GySeAOVJiVIK0DLMCuhAiBe3sbrWXc+Dx/Pc+zLBYlBlLVC7j1N5g+hjDN7AGWCisEaPGh3YdxCRl5oNGhgoCCWytXFQPA+f54E+78DlCepVbkZtjTGOsjghOExoQoXR1RY9TXSDiu3cEFEA27vFZyBLnyxF1GGUhSa1ksVTQDFXgWoFKNDzomOhTdQ1CsXauNDVkIIM3OsiyB3/MMrOLDdxiRY51XYUX4V7Xb0rJ2mw0V3wDcP/pbvuxZzDn4b3DnEfoHv7ypdyXXOqcG7FbhuLpQ4Uc8qeTxqMrXVQA25YveTfXytmxhlQVZ85WwFX7vLV4Z11fUvpn4Vy6RzQtlp9SZPXw2we6dvZ9+KgkxuEIMn0Dyipe+vhOS7W3jgqzOMF8i2I4FUMCD7W31QoQMlt4HCXx4EF0h0aHbBm9pSfjqzcNNAvcwMo5/bQDY0HsepKC0Oa1dhDWsTyBI8wTg2ZAyT/8hcj3E2YEOKHNtO9XD4H+wQxncRZzn0S4ERc0UjkPcVrkU4G9Bco2UlwsHLQMQs8ElvPVjXPh8kSljdwMFIxexpshMZiNX1ZFSDhKr7PAlRIn1xk6ZJKeuGGh/h/clxYK4RcRZRg/V5b470Jzb/9VQ5jpP8QpgioGx5X18ina9YxzRz52MHHIdKK6eZ1STRIJkyNq2zElQw8HGlcYdoPqrxrdyVnYRneIsLXYbuutAv/M4Q9PrGurlC56TIPwIox9yhW9QpDHY7bm2khn9GNo6e20iHc0uvmDQex2ifRgo16Cb2Ni3CNTKc0QizN1ctvONTBUXQ9XR5nyGA5TX8E+R5GqH46b38RqEYJu1ARsU5qhA3LCXv65ykZID2F0Q7XtuIQ3kw2EQuVsv+wRDdd28O/WcUdGVy3nKjrqHIA+zr+ig+skss4f6m6bqIHz6dmFJ6jmMgqSODkEs/U5+MNvU0OzSUR0CnWV3hWXi6GAuqYxe5ca4/lrUZ/H+1SSO/TOwqqJznd4nQZTCdvCqwjZdr9INBcTqYki+7q1RMnkr/gnsYIAXj9GDdba3h6Rq9ABLUDsXQRb0axsH71KXpMq5D0nVrwItszH4OyZMIbEH/bhqCcLkxV/k/PhMTcqbA8GtA+Ht0N+D2/wO/W9n4lM7E1s5X7Rvb8ubqMNCihxVZBtEBdU9bU2aNt/v8hCIA7/M0gEdgfeb1TLzJT/jna6HsHoz5wLYHUgzpHCJVpTUfdW9sa2A3gqfbRtNFQU4eMXiUDx8cw284rBgh7OEXm4/Vhy+fMmSgD5D+vAC0rF9S9B3vSb/b/oW/yoO4+QDeOEWVbcFXu7vH2OslhzH2bmyHa7T0m+tbHHqejiK4DIcWbYEIR3ZyVFG8pki0epuhYqjt8pbn2Gk4hixIbKSKV6EekhKBkMk5vWDJ6uTFn7fev+YH75HVF0FE0BlirjxT30ndioz19e1gBTysgT13RBvqjyLvlaas+a7ZSKACgwxbuxgXeMY7MJF5eUIX8rekiIeR7YUBLFXKs9b6p52JZLTK6Qg8yaT18T8bLqyu7QbmJHll2a3wYU50/tNjqkJJXkOHuCWoFvvIW8S7V2i+YwvnFwG6eek0OzsE3x1VdiE4odn6lGeVSSE+PTlzyGiXv1Fws4lrA0+/a7E0kOpGNOCDX1vCPTfE7s6UBaPnRlPpeVFiKX0L0HulyFaqorYdntwaQ1dBVuQreMkowYxoZ3AqaJzPkC7IiiEnmJXwD9oeNZ0zbT7YL/U9Wcd52Wbtwbxvsm60iu6ujP7NUhgEHHzUS1hxj1knuenfR8mm71lmJvPBGTmKb1UoBtn9NJ/K+Etu8ickpicpSncR2gFVgGOHlKDvuUiarl+SFJhuzurcZ0OzSXgfx/clEV3lriZ2JE3eXbzQEASIn0YVLQ8LGGD9RFHg/97G+ZJ0JEh6yAa8d23mxCaWYfneDA3l7AEbJVu9f+aaZtPAQlJbLTyhhYBq9eK7bGMsXblbsN5P9BPVLUG69IhRUkfK69Dc1yq87Yrc8rhXcwAQtSfjVNXByxq8XvJE+POT+i8WuQw7jhXO95E3HFdWJcvxzjJyiVqEnRuvCjLOPc1eDNPjd0oo26hljO/hFkXy7z9XNGwLC++B9o52vLu6HmsyeMI1+QWfrXd/6JppCvZuS8oQDyEAwxkGBrpbwclN8Qc81cTDE4b6Kj6t743yPYQW96SL0Zvtuk3VppcRoo+ujzlDm2VK9q+b8p2Ycp24KViUllDpOBdHufmof2bOr+nr+krWv/IYT1QHsH/5AASkA+wL+5c+9FRWpcv/pJYve3igLHyaVTwXKYFxH5DUGSwd/XIjpuKzziXc5S6cgm9ZfM7yWx+HLEhqkYfg6pf+Q/8uxU/GjBdC8cSnXbxJwMrCa/UryDhLS/bUkKPOFFSGO8Nlq3yJlgXQVzCLjhyuIQ8NYiaoS9O46Ly+I7p7SKIxoYWvwU+V0G4zUPCiSKRjAcL8ipO8sMtqXt7+ovnPj7Crb7Mld3snpuWcmsu8Q4urd1c0F3fnu12CUjd15qQZgxwatm7TPKmG03t7LaErF0inEtYvIRa/eVTdmvi1We+jPF0a/k+WDVWdN18DNIMa30ci2pnEBT1IHTM2laxUpNou2LLShU5YOBhguBZo6IVx31U8tfyCqKEIpA6Q2hFvjSDc2KvAL3Tt8IWovMV1FH/0V9vY1jMnvBHEudHw42h7Mv3Tbt6imtfXdJxSNTn0mywUnFcJWygknhG+clrW0fR1E7E3MTnOLvkXg0blqAXZ6GFDKR6voLKu2EUZ3t3yQFhxbYBTqifkRm9lMynH4NonxvddzQ97Y6GvDoTZi4eknXTDShcg9YNJCfZLV1UZGfLsVuBmmZJ6zXSF0hUz02ubbrQPgTp+f764CKrxio+4PAaqzj2s+MxiZ/AroS1kjw7Vttr48w1SIeFQJzufwutvqO83VUVp8/y7BHvIkW+oDuwRQxbwhY4unf08kAlYXZSPPyaii1yCLa0fR1Dv8GCdx//AG6WDwF3tt2CNHUHFP35BNEEW1VJUV6R5eHi9JceIZSfg8Vs7jE8eajdeKEDo+mXsk5Oef9pkCK71d/u6qUsA2OMTQeATTgF6YnM2SxvY2JIlgtYZRkeh6CKQAzHJZ4q6VzlSQKi7evKrH42D3AB8C7IVMNU/m68Ku+Dl3Jb49xPaod4CxJKmSuzdf49Q2shvI62IULVW9QOM9jlyzCD3ePB6rJmA1HIDDoMpaVuGIbCcrBBKUMDaSxW/cEYNYYUP8R7QxBeAeCbp+KRfTNYPLJvbpfw3ZRoI3LiXRBtSg6peiTQDJwHYRB5jJ0smIUV1B2iZkdlfvA4lLch0NEDEQF2nstA3oHnINndxmjHTr+BBKC1YudtXT2C7Y84b56BufbwdAZwlsatsmoEbn1dp+PDAwyhdfnr+hhzdEIj8anicxku10mW5QrNP2t4GU07goJ7u5kIjJIzS7tDn2WB8/QH2IlYZ43p6unpvTNgly9HmBTB5HHUZCZ1CPd/QOCGdlouLyBSbtkFIHJoLpIUmLMt2eA+xOHO0Vx1gTsUBAr4eRD9cHY2bMF1tsRouNcr1yDLqsKuwV5/DxxtSKWGJkZBeR/kZk3kyHpN4J9kpZFXesGWrYziBbwzcRMNcAdSKr2jpTY64gtZ98zpAnaINTpI1zaRe9Rvc9w3Ba79w7cBdHbbXB592TdOdjwtQeLDF1qExzyjXlE5dt5dgBC0kzEsI4KJPi7cgUMAI3EpCsVAgc/guTytf46zuuKK1XX/dguO2f0jRJgG6GdyRfghiHY3TzpGrnpKhvL8/CVFh4YPEMnBMso51s5H/bzodU+r6xDSVX/0spvdqyDzJaosV3/AB3LEWJpcVXTrz2zT02pyv6Rg9w1mj4by1epujYrL+lfjSvISpLcyvygRMKqtLIJjc2NbzYP9bZ1nt+R1WqFKyk0ElqmvKmDoKHl0eMC5Q8QeceYUZ5ZlDdFdWNMaRPgc4ArDApw79D6BNKXiRK3TqlczQmxJS1f2AJqxXthLUI01sU6jjEa0Am/HTc3iIp8JLgFDcpr4vgyuB/J+9zsENYNQ4vuuujqGFZu5b5axo/nmnbf7R/f3jlVqP+tHBNdpBcqJ1fQRiXjUJES0evWCtf43vxXs8mgXgosgC1wFLWMNvSJ5knytCHFxOd1rDRKER8Pz4OcqHMrxMkqmjGhVFCzukWFVlUuCM+Pqjmrq5jDtX16Qx2v0KPeC41bB0gRCK8bLyCBNvQc9uTxzur2rod09G0vP40bHV6S8QvE9W3HDffrLcsQ94D4Jtj9gtHd4f0viFf3aYOQSFri6Ja7MGUfghti/qtWxFI9OTa/RS6Sip4MrwK6D3bsnRVkkvgVhCLIFGTO8V38qAlF029TLdN5ZG/3sHUoOCr/GUwHedzA/M8otfmaIpGOkO1Ar4TVJtXUHgrTxVhn4AwQGbjtbr3bGhrPdAUaCEEfNAm4aR8IsTyJcLxcsI2W1g1fTjnbF0ZWoiwuOYjFdxUkhSG4cK6U4AuJoVXAsmwBVDdzTem7cyqZjdxWcBQ8P2FnlBpxD5cLjLaVFXDwOL95n3ceFeSfwqw3vQBggr+L6MSaRtqsgWc45x14n3wZJeTC0cvUX1xxmIUV0XxsLk5YA+1Ci0XeZkW/x/T1kREsXJLiknc+SJS726NnpwM4j1CUoQV4MmSytmpqjE7xk6KfD0c3TGhwC9p8cJnY338SPiduXK9wFzOv0Pni5fAEUpSZgEJAVms59nLSLnJmqHVwFMIlDe0XuqprzdUrCFzSusHmxC4b5FM1VwGIKMHcoN9i5uyD85uA3UU1vpYTdrnQNl882T/BL8PJh2IKc523S9ZdWF4JzP/YkA0dYqpcgK6vXbQgKBWc1PRjMLUhgLA7xUzNN8AUzgWYVSaKaIHswW4L33FcXjQhmMAgNL4XY3pN/t0g4jn76iIvAnv4ypMjl2NAK09sBMO+bT9UUGFOUV/zrEmS2SIhQJnbvuyZ0b42b1KK5jHBjDS2sPP8L0VOIzI/gCYQG1Uri/YZ0/a+frtMv5L3WP366wmMa5TCOk0zlgrp9zaMEHdcecfqk8KhS+fa9QeVbj2paLZvLHnxJOv5mdu3/ZnLR9gCSBCQ+YDt1EyOh3ncjK9XXA+nOXRCWbocPWXbkJ8ZpKWZd9n1J+e+A9Yo4q2+rVQLEJShX44zsbhLDOzii6J6TtGJi6WRPpy8MXryRg9Xi0z3a7iPEvRVCY29Xt8i/y/5sGy6nMk9BvZ1Hp4DhRZwrK+YuDk3uD5jeVqrzGune0IfeQxbQm7RNRNrWYd6pbGcN1Mk9pUGuafW0S3h+4XZRQQouBBGRUNYNdiwxCLDRgaM9kRsEyMFJg0ksZlk+VDdATlmI7x/BAXwNEohBLUGCCcE6oqdU/11Tq6rA5IpP2y+iDdWfJJVvSE5fgrxsoX0OHaNtOQ213bocKPjut9eZZSDgV3GyBQhH9P+zMMQeM6sDzoc47UbeW7/J+Rjv4766xIOGb33IDuF5vKP2YLtg1jjK0DqoHgN/BtlznPxwPdW3CTwEyStZiVUJNZOAYB4Uy+BkAvLyBVEZ7QFJb22LHx+YBprqQXIl9Deti8TyN5NTsrg8IAtbP9LIZ4HAUpd8jDEADaao35wgnXxAU0alH3cE3mElbifl612p5anE2HMojHEi+UtSssv9/kiKgdUPIlMH9gaB+Dk/XBSrxirYqcGORE+5wq6BeAGi+ACjAAmzt7jS1pB3ebPmXZvvnwJyl7CE/WTqLjV/57QtUplIhu6D9MeCQn9psg3SZzC9LT3id3lEF/boKXBS4/Ap2D7CCOC/NzUQq0wIFEQjx5kco18d+NBItKpd6ggCYHMFI2IrWE1dBcQDOu/0Y0zW+XYLwM7RG/bLJIk78RqmuT33+NL9FqDzvTDlqg4kNwk3jDTkElSjwtFKf+WehTCQm5tGIUpxdPlyxDLBuai3NmaL+FQDRUiEh9V2f3cUhKkw+mfwkiHluym7W20JSLaONxGjDEzM5BIlUzUposggEu86/QB3aGVYGf15hJRZudt5uMtm6mkvQeMwBIvLx7kJWJWE9tgnPnF+wPQfp/OvHORgR2qMnGUZEuqlvOiiCNc/ATCdraxITBayZrD3jpZJs6wn3brD1rvgFTSJGSp62WhqOrk8ouU7jILk1cgC7dUlJi8kPqHNi28g2AIGOxiUQqHPd7a3h5TElOAvQUncJjBOLBOuXCXxwblBfR87B3kHjuGrHlwlK71TpNoW4vl26xrkOv/+v2ArT4Vmoj3x3aybm1mXNwprtBruE2j5dBgBceNrILbH1rC8KdvbbisG0e5TEOVBGL56sLRoTJegO7l169j98Tf9q9Tyiq5/U2dhKxVIpurPOMP3Nk5ETi+1278UzYImsYrWYpr2vkg1AVwce9ZpaHPwQWRfgIcgDzOk+YgEUpd/LjMLBYdjAPfREhYkb80YRqDyt0szYEp75PDX7v6P3qUj+x4ckEZbRsS4l7PYfGzhT/EOkORvzn3hH4M0K6AnoH+RK1rZhcVSPJrsQdngVfbkbW4Xb04kxu1Q+siVocu51akdhu84biI9Zjew3juE9VdNWMq6+zN4Tj8CrA6RGFMe5QWocC7lTqsMC04OSldFz2nYQa44JFaFI9gbMUtj115DuD3Zm9WD0WLbe4Oj9bc4wRW2vL7Aw6Fwa5AVv5/KYtO0l9RKfSnfgDnZMVXyKNJTt1lvEVA7Tw6GoLsIZEiYXJ5fp2gxMW8bjW4A4z3cBuHNEYtUcw1/WlHJ2HpFP22xnWkSwKSlI05KQZRCa5C6tOpotdAwFOM4GfzI6zyHIa6fWQFqbckmGdHaS0Uho4/5C15bjSl8caDJPxqOuydFV3GSH5Zm4Hoza0fPFkmm0zbv7BFup5+i+DaBTwhkHSq6AKm1r5qDL6rNZZTtbVeQNzZHg+5rZ0MquKf+ZvDMEBfhcOPuxvU3Ass6GWUys/PXszx7tIvypIDdgS08Qirce+CDsYYLPkjzBFSVmk9fTfQeKE0sLr8Zbu/wVCi+pzN+PeurvEIpYBdIZqN0IQbUm4wNK2PXh2Oc4PPzA1xG1h4vAnYV44OUH9CwONy6eF/qAo51WHohcXcAX1vv6Ehdo9uoH/BodSQJfgCb/oUn6yayuwtDS+IKgnCH/3L/sL6SoFUcPcB9ngQ8763RJeflS5YE9BRaJpAJ80NUPxx3APEOpHmYXUcPnct4szq6xWs+hJ3V09+6v49ELuvXaLv0t/cqU9GwaXP+WkBppoPOFn6PBMPkBBjnyRaY5hFk0StgydFjM7Yb8pG/gdmy810XX1VSOV2tN1SSp+ol80Pre3NaOV3tDV8yxIcglb/t+Zthbp9rqzfZBYx1lrhS9wXA8zgWRY8qKXk0R54dB5cv2AK6AMcwXkwNtPKEYVS7+UH6IGw8q99FEiC3FkojU/amvlLwuEkGP5XYcQO4CnkltWHiEh/3SRClB0jyBNhzlQfRyu+O1kbh3uh9wWnA03X+vTjaugasER1qMm8EvJIT3hR1F1GZeCXAJ/CJyhlo+NxB68GE5j715hsaxDdkktMIL378r5Jy56tfOZ2C9h6QxE8QccVnwobrtNSLlfyarzAHG32xllx5vcZzRHCFEAmfKxse/YmVtisPVSVohe/LmavKsdl28/CQAquXfiTA3wbAeZBtH9fwTytj5Bat6CJ3u8VCcba9ruLDkdwp6xkb2qdOctX8b3g8Q+Bs47lDEEQ6MZ/Ke/l5sP1xHaHZ2f5YXEyWfXQL+nOXbw0iJeuOMy8VPEQ4BZL8h4Dkx0/enjXaVoyB4Fn3QDiF6+gV4uk+Tl7fBGChAlAqzLf5X+j8lyekUgyWMP21ifDOiYXy3gaKb+2exGm6BmH4Nr0DTK9GnGGUlxUwT4PnMreSUiK8KN80MDaVJwn/7jQnOQ2YOfO5QtLk6WGz9bo9J3+Duz3I/h1HvZD1sy3DrKd6jYmPklsURnVm6t4mRcIdVZKRZX1QpaMFw/wtxH18tg3NhJwC4C5QCoPGhWZOSvERogwSf+JedtXPggShjPlpODzquaGBGFVgK3y9urc4ysl3nPh6veiq9SMJKPH2Nv8WJOV9Nf6740I1c83b6NdCXnzoWB0EtB7OcnzuYA8j60f+aO6fL1+OAULT6obqc3wVh2H8bAUDPH+D0c4OyPW2vyCD9gLJXntW3TttmBjPVRz2PhY3iDo5UGnFXUFdpekqDNLe+HJduA6sDLKYxrc0CBp+rI3KD1/eQpmYGxWITdfu4LYvh+I07zdTmLHMbhw2lhYFjb4eBk1PHwH2SOeTu8nKU20ylRUM1amsxjKaS2YwPU5SXe2uj2j8NcWp6epzNunLpyFmlB5vrrPaoUHTimK7+5zdyt04wMSWQ811Tmn0jW66Pc1kCb26HQr2JnMpnhtZczSY0WwOzEohAhX+RjgUnf1UHC/shOr/dzEy0Acwi+jx5mobdWgwQ6Ps7mW9oqPk1xhX/R7Vv6bADNtFhgg9i9Jnk+Acuq+PScCZOVfxgTwxOlX+l/RxvWqGr5ALgCqZdpQqSKFJMPOLNj19CMd5GO+XIhyu5hLz7DZODeIAm54+5pLkZsPgkco12kRnMZs1lQb5xZquVigQ/roPzi2NrTvwBMHzBxAeH/IwMnQTzWIqGYKNjdOquxUq34K05LjviT312RwrOtrd3l/Mk7PtAieGap7FGL0WLSTzf0BKisI6APU51oQkkvWzNI23kMxsdREQ4xxXxXv+O5CS1AObKmt7S/gvo91P+PiB0zeXDUqM1iB8+Evz46c8zOAxhFuEwj9/fvdze8ncREXiwJ/OyKMh7GtMt8Guyw5Exk6IAwdzFh9uAxa3/9MZEi1jkBTlp1ZxlGZJgNjdXfMw2sJjELb50WqoqB4wpTXI9pcLcAQRji2Q0a0yLp2Lvzt+PUxrBvr48fsvlFApyBr8kwSKEtxmJGg02l0pY7+ehogxNM1CvhgXSRP9gP8sE7i0jbNmkhmPFj2/7IdBBI6LOoOUoIUXwRP7+vwIH582lYFpTEcTQi76tDTyXGvcuS8aCqa9/LhAeZSzcmoy2cZ2PLmMw3Ae+y3GlJUu8sPsd1VCxiw20sbHvekirTtTHqWkwLGDQfWzH/WjOosOpKUkREnPoOajycs6CzJAInkjdHpc4TvtTowNZWqV3xkrq/ptENlh8GXwaH3xY1Dx+ONHiFhylGyoArnRRKmKA3MoRL/+5S/vOjPXQKqi+2hI9W9zFwBu6OLEp14itPZreHLCoL9EBxQJBrnRz/WbOh3MZs1xe3FO89yT/ECbTDt6mIeKZz1TETyAVEljpQVDyqOARpKs+yDZg/btupGCOFm50pnjEcRKHEM/tFSdwxDna+O/HzCSqZ7tS0vvzVcalMZimT++NJAU5lG24RMx0c2rRJqLSv3tZDaviiKdzWs0uaqik+bh1KuwZXBofpy9c68mZR4OPkiecGw+gR0M8NM9mfgwjejpYz9obVQlAqzrrvrNizCISfUjEBU1KkPRuI0nE+VrjVI0uPibzaAvDzCDMItI65MfT7DGDNsKE0uPyohl28mIE/c1V2cyebO4FHHicWg64tSEC45jRVdPSh3qpb7DVPVYmDF+6x/nrlP4b6FF0z+yNqmfSp4dEctxwoESfdjvDKz6MvPY/DiIcuk8QOfh4lm2apIHEC75g3vBmPJnu6OLWY9nUEtdnLCYaU35GGImScwwkJgxr+iH29CYfAnMQY75MPeNTZwWQjDe9DY3moRZ7W9CEeM3OJl9TlvmprjXsVLXs90ZKZMFSJ+2JIwlgT3ZbEaTwvKkOSu1x/NedL6djLLT8VRMUc/VEtaj4sb3SY0vXwN6pUzE61aWHWhQ6ar+cQf+k8ME4BQG4hv/KekuCmEuOsz3k9FhNFU6emxsf3oVGHvzcJPAPYwGCJDVUIPzC5DVUTYt1o8uCkgJwCeQvN7jgl7CZU43YtY382HiEiEmdXyxoHEbWybO82gXApyA6CzLEvg9z0BR6nPTfOmzd6iWnAmmvw55LyekTI5kp7FPK0nEY68yKqZVBYem93REt5TV2VwZmy6Y+ZnoZnLOTOeExMypgPXulm9SIh90IvJR3w4JcoVP68KvhTRPsk7ouq9NkZLRXjaejFzNZlsbUaiGV1Z60TDTUFWMB1+SDH+alzYcIniSdoJXNjzKVIalO0xK9maj0iYgbMOrNv14iGmot/URbOED3JJP9dl2PsLGx5+Hl6jliQiggLw5iCIf9RtSr2+jQhedsaBfHtQEwVdWFAmtClhWDf1kSTAXIdsUKjJiVcbnA5ioclWn1rfgLFI3Wwvb6ApbRsHYMk/Vx+mNLpqMFdHgzH2SRH09EWuBIknDQhgvAoQnXGqBRYI55UzmEkRMdcLHlDJ+WbFhBe1rkMAgymrduooP32FEGo4eESDBjSda0uanFEcgI1QFiymFGMjkbzZn88kL6vD7rq2Mjn1UVxDPf+UBKYvxJYJiGWUa0bLAfjhJ9Shm0KRFj0Z7avI3X52oIpInq/3mpPJax+x0DerTSL9XstOQM+tDeyLFdEmxo5v5FMtB/Y8SIjVEdDJ+x7YPXZU8n9IxqBQr4DiQ9GpKkhc5NpLhBvGxpVm02c9a7U7PDhhD4doYBJPRtm0ivgZhXgupnEI/cjGs5BJyVdAsG/qUYSN58iPKBbUa8twGMLZYj36QH/Dl00hH8vmcuy/i5yiMg914yUwrDFhnev3jSaQzrclRGWtK+Uw36+BwDAEff9NJnJyW0JqeATUEy/zRZOEeggTRh4tTCatZ2hYDJTLhxbqpsWdwoX49ibqfDT0qg02i1mcjVtN38o4jRAM6cvXkZ2zX7VWc5AeSvt91PQjxtlSPyUCifp19IYeGFh0dMq4Q3MdHuB1aCsigXTEofz4NOSiImY8gbMh//0ji/CiUAqpJZ/LKnwfZTciAXRQ8iY6IMR6FR2mgBq8pqBAO3voz5lNeRlA66nM5rMYhzSdgfIjQtpk6nyI0uPWiOa+D2i8ErdGE6DN4TkkajVnUn6qwZXBofpx9/amaFJWxRq8/VVdIpI/xs6rq2usikrmKZmhRc8nSsa3HL8DYEra+cg4mPsAFCd1QbkFrwcMdxhO+y5cMJFEQnuXZI4ZYPIy7A9s42c2jFKiMAgYvecPZa0ApebPwMtwkOy9FiWXeJjImA6T8Ze61iAsyVAZiGT7y7M9C5QwrNEMqEXWpGV1bVBmvv6TopPABImyS1w0/VfdEM5vTmHPxYRucTG5zhiyVcUdPbs6VNc5a0dcRCxAxdZ0ylnwRDMfd+IinasoSVWPZxcCji2ow2WkImZPATD+sYxyxGTCsQ09wxg7r+AM+ZKsAHe1v82T7GKRg9w1mjwIazKexJ/6wwoIB1vzoT5MMlfq+pkVJJrhTMbqIMLYOnyDTKfWka3iYc/EZwOjRkgBH0qZt91QdpyVrX+ilMFFraEqiNpiNZCxnzIyOazZ9jjMwfTsbY9nFoPh13jLUEDJ9O/sOPCNpv40RgLRSTrPwT3IQZ9Dhfp+975JH1Sw8mTw5c7oJ9pjj0xGXwdSQqaww0zLere36ER5xachJ72QVkmyK3frHeQtQTcf0t7EKVeIx4uNtOmueJafjcGA/+ElxrDOxjoRI2S1Rdxj3khajcXR7R9+zRb1d0//cYvpoIvAtCNECn5U9zKDMINL6MnsbmKVnFtYvK09D2LvjisNgJoq+LIx93M7yJMKFz4GHZ6eeDtoUyq0zE/Nl9oqFpWcWiqWOqx7eXNGSzfkZLVqC1+L+mIfmmCCyCpKMKuPss+R4j5i0MWqdddofT6c0eIc2lTEnUAq8I0Kz2KWmIGZD7lVG0jX6dtWRrekH90xBsAYM9TGSq7EjflaPYPsjzts5Njs/izVYpyWjyrpfh3lyxiVLjprPLJo9/PQjkAIKldRdu+uIx75tniCK97fBK7nTuI4ghuvoakN27cUO3Dq/tT/O2xnQoUdpTGomJiMfladITpGrefbmOOASJUXNr1vKTEDcCqWOr6rddzT5xGLwhGbiY7zfUP/GEyn2NLTaMR6H9rdBBJIaVYSNL7+FjGd+5I4mSmW4FoqTELVZnDzHk6ohz5u64jT6UdO9/PhLLtsWndMQmdmIytnDAwzRL2Az0IP8ekAWUPPr3J3nDSlKW8/It/1n27CVDKSb4GSiWWlq1FtbTzhIyg/lPDDWm09okOMD9x3P04mMfJySGh6C5PXyZfsYRHtwh1bECln0INq+SsSrbMCKVvWjupYhKLDuyuIXTzLBo8uPPBR0qHkmhRMwDdEgf7zJxAgywXB+PGHYPoJdHoL7IP1ReX3o38QxrHQjZkKZD8PckXSJEKLk18MjZp0nqeNQpjIs3W802ftXDnKwuzwEMDzLsmD7SFzTV1Bi++hXaPEicFzMW/WluC1mX/iFT5fKwHheJyZqo1WLmpb8DF1HylyGJlFUikJ/UxCxlefDYhox2RKZD4NslRTyInnzJGViVnmXMqXhaPymIFuUnhKSYjexi9gcdcVgNN1G9RzvHhiZhJeknOMG/1NWgbv8XrRm71TZL8Pc9daId1Dh4+HqRpehdQD5oihSGa3Eb9wqE+v8e7pNYFHsfeAiR/TY3aIR7NfZ33h0adLxV45YpRM+BRn4BFKc9GRzlcSH4aSEHbwV6Ml+mr18tAhSOrVRkzEVAbmP38RjIuLRTMVownF9OMZJhlB7gCHw6rsUSwmDAwOx9eUknI4sTRruRhjtR3U4Xr6MLyoMDq3aCacnKixNsxMVNFAYFxHTXBrs59VbwQ4WfQ5C9Ec/Dh/tuXcibBRdKmOW+I0maOfB9sd1tM7i7Q+v5yQvYiZAnkFJ2Gb29pKIslkcukRyN/0XcdMTugHfx9nI3NjP5D6BKMdv+spgN/yn5NYtyjmRZfTPgwgbi3MHF/qTp7u5muABpKtFksqIpMtojoE4DL/GGTIWS5nC09MAJa6keidFv2fYkruPN+1+vZts2ZdfcK/6puFVaI/PaNPOR4W7ZavNsqJgCB+DlPNaY44hX/iHsyh9lthlVJP2rFY/D6K5rGTM1cYoYNeEZKtBcdQqyqv4QI6ZigqM6jK07qKHbhdSrn8/IY0lZLXWcAOLEb8UtGktbI/1uDUlyZFe0qqUPbD8VLiNdzIM472mOqK6DK2O6KGZEyD9+wmpIyGrtYYbWIzwv2/jdlK71iySBu0pLH4cxq+gL0mO1BGfPdOQnwq3cUNIMAZfcVkRRYXEdBpaJbGDd4JOmi8npJYkDNcacHCBqv8SSknTgjuVA2knQ6FypKEEXJqKLNU9RgxRIb7VO/AEwfMHEB4f8jDCqThVPVKC/oN7pkR4cPz9nEYnpNHUZkRr7DHlkPnQd7tTthLO+WgS5fbChseMCQoT03kakmWk1kbVZerCfDpaa0aqagb3zobiNL87Zn0ZGvZm+ZK8f8CLCvUASRVREe/AFUzS7CLIgu9B2j324V5rkNWhq0e4/fmn4mdqMsvfcRzaIfjnz7vvMZrr4HvYdOkonBbg4GUVZGBPXpl3wdNfuYPQDXqGQv/C1x2cYeovvCHqjz3gP8bbIIR/gl0145yBOG14Q3Ka9Q0eRPucBFZ3x6w/cYeqv6qQB9ZZQm6M0jhPttzRuM2ERHZa9mBxC5IDTFMk49VVXAeDbhPe6N1W6iOjdf8pIImX5cPT7XpwoJv2IMImLuqMz37mDcu26CM7DkMeleRnLlHkiwLU6n6XC7v6KBqh+q7Iq9oeErKrbiHjWN1IcVjJePKBekeo85h1Bqi/8ODXH/sIwO96uRq5/sJFv/rYp4kzpLORbntCeyxPhlvfuVqZbdIzYOMc74zVfOIN03ztk+jKuuqKc/WFK8vVxx7wFzAtH7534DefeAM0X/umXLwLy7dg5f33Fm6zPOHNd/2Fy6LqYw94NsNEZwz2M28gtoXafEtoajWQzP6mbLRR3QI+BVH+EJA+PB3DfuaSyrRQlD1cawkm4MBXpNxWMolkGvahAEL4BJLXe3jg8Zr9zB2UaaE2t3QVHdH00m0kM0w30x28Toh/BcOMv2H2dlFCrdNLDVOJ4ui0kC2CqpXyKig79iwGbisZHnRLXVzWR7CFD3BLTmFUBQoRVqL2Mvz4fZQx5Xe/KSO+u1uxtDl3Z5b2MMJOGS8djFTn9D7gmvnUR8lske9q43wNEhhETf2LVXz4DqNAMC8qnSR4Sfv14PuvPCC/fIkgbyNgP/NwYFuYcUedJT1bb/F//XXU7ihGSA0TbblsLa0U5x/px4FurIIN3d4IL2WcdPAxlZqy4I6q6JTNNdZR2aPPnKlz8HVNmfoT14ypv/a51SBIbhPIPV1R37guteZzzyDUrX5nEOobbxDyOd0oHa1IW5EDkv4oGUjNGUkaiwaRwi9bqMD/I4nzo2iQ8qNkpLJFz0hNcGpnnOYTbxT8tRf65QuyD6MgPMuzR+yTLnZWoUdO3pyHhbxHD3Zl3aUOGuXvvPHKT4oHoS8440OdGlV4EmKbyY5CbMseLP6AD8iiTnY9WPCb8bDgt1TEQjKyfDS1aRTIMPVNOJ1Kpy3S8nPM3b6pb8JBis89g9yBZ0TwbQyjLBVPGLcVb2Buwz67+hEeBQf35hPXdq6/Ko4gmDH2s2wkpXlr1UXvDNf6zhuv1aR3Dpny2ZzZY77z541p0svPdrVKDk/bTfh8bbfq8wR2Kyh2XYLdNlzfYLeZ7uAik03UUAkNNTNNWO+MM/uClnw5EDTWREcBDzUE+m8b6OpI3RsH+iv31oFuoD4UHkI+XNGiZ8iiUe/tJo8yIUVKlDQVYro0NN+42DefFbQuXtyfQPYY8zbjdgOR5qXb9E5RKDT1qG/8aQkVjbgviXgQ6htvEOpznxEDIoBOOjJF123CNWo6rfrOZwgGIIfH79xb3NZ37jmNbdJ75RZzrynK3/lXbLHCtUtT2KKroutP/LvO6qsK6uILd/azkBBl/yiv/EF3UF4r7ti8hhoo9IzdP2i/q6TYBIQapPWd6wxhm/T6D7kJjTmORG47vkeR21QdEfnwvYP2n5iZ5OXdEzLzmXsiZlr03uUejgHc83xvzSf+XW71tfeylaQ9vAeHY8jf4jot+FeurUYKbo2PIMtAwiaq4To5eA1FLg9e2z5zjM3c3LXF2O9cQ6xMG60ylHScvkHUPFM9POW0kfiptHjZzg7KcdCyDWQ+xVbTXkkO0jwB3wDcP/KWZus7X4qZJmoDXkCko1I+p7tNJMNSrXpGbqXX7Azb+s4bs9WkbyN7jbaSjZv+yt3G6Aa9Lsl2mj6OG7LdhO96bLdSGlnM1dZ38ZiqXBWm6+oMLWzJDXUSNdYIRpHtB9xmfcEpyjtDFT0gwaDbRBbOpDxyddMrHrjTQnZhrDrsHcDNduK4s3YDvieAbdPH5CROUwQ8FI/abcJlcqdVr9JsUplx1GXzka8om+8K49Bpyrhj0Q1E49FtFKNFxTGdnRay2NGyEei/xqmuHyQRPN0msnuMzdnxGEKwu4/L9lADi544Hn4zNWzoPuoIiYW800INjbJ5PwZV7KBCAIpiHMqmaacc4VBHPasFnKsHnjOVe9UDLjpZuLo86TQRxY+zrRROHXVOC+5Ro/4qOl/UDRQClcVDMV9F4cqqQ7VewvPN/ea70OJumqhJNefZqki4OU0lMs5prYNRHxoKY3cGpN5kyV/SbJp3OFSf2g0t69B+Qta8wWs/CkIoiB/8sD0lj30KKCqPd35hyVdlDf3wqZ8v/NbemMJ74NVwRPpmS58djJ7kvhZKeXxR6SYmk6fuCYWyd0MMBOlTKQJK7eWTPsO4cDfdt15dnin2VCKa2Z7a9HK3nKmwD+2M0hXHNvCxyOj3ZgWtvPdkZqQVj8Y2BUQ+cXQTSyQ7PdgHb3U/wVM2fRKZJ1Gb+i1Wl1B+Q4lGYF+DFcqA/9SL6cd95kV6y19v6ZNennlkRLebuCe3dX4j/UQP7sxJbLFOTCjb0PksDUh6fW6oj7abdWlxiDe/buP+LY+73UkJF5zdGQBeGXEfJHt8g6DEiLKxk3mcAhvOYYhr39SQJUxoNfXHAiUZsiC6yhfTgBZT3Wnrcwm03lgzAEQPp/XZUD3IlZop3UY+TJX2o2LSU/hg2MBkqV6FMu9SOXYLr50Ybd47WYK67P0ra8ywj14LS0bwSteAbObh7KYGzCGc39IJ4mw/7mPfonvrk3PyKxdjP/n8nDddMnj4T4b8+hWoZN47bdzPeNslXmgz0Ut3fTJlHnOJcaPSTUxUO4kAIUqUIIDbk8uW7ke/7BGbPCrdHM35qOxhnihLFgq3nfvFwru5KXYUWZoBF1LBXOxorRtuT59LR8gkfgPvzNJZRdyejuVhokyr7uy0hKvdyadc8Tb1zjefjNERpHYn97bKSGzhJDXhnkflHXzKCSd7CwNElo7F2IqtvG03DzcJ3MNIYsZ2mrp30GnIlAXJbKoZMb1MO8n8cVLfFBMnSWkzPNnCNDIbOveNkBtK3XtJ7GbsoYkVJ+GRwxIk4OGD7kui44DBJUv6T4bCLt6OiKYT4IQtWgw5QVbUx+LmpCHkRLetj3ORIEMUzQiHp6I2af3y0GnqTRqGZUErhRVtIgt5Ie7j09DnDM5jkWMzn0dyv7Rwm3uTmHFYIkgu1s+cno7e2CRPGEZDU8wCZnDvLkl4JsjWxr2VNwAjudXt54saQxSgsomeJLAFyZtcS6t8cF0RlkIbmFUTXQHdIGOZv0TS2t/dRSeUmrm+oL56YYbYRyJp3UuUgBoOGdNgiSTpoOqpWRnEAAdqhYSNNEd1Ui56YXH/Jq7S29sSnRs72SyWRjxlQIgZxEu4SVghS6Q5VxYbiOnwsqnCLXcs4iTj7OQtlbBLqXsvC6S0q1mPYnykMCUpSF2xtMMPekxVxsqADMgUBegKkP0vcBXR7e87oNyOqwS4mWY3wmGVWSqHMwhTVOAyaeBkwPmp3czNeMVtfvC93NN1TpVhuDfqkN9Q4iu0DjtsZ00ujHZRRmTzGV8Hh2MIGsDiOW+1dIT6gLNd53resI+7uiQLWkpuDayfkXVyWJPu4vTUNuRLjM5uI/dmpn9S66e60sBxTqv+GdYPGe8kDiddqV/dkEuSfvfT22rmjWAmhXlDMT81uSHJGzqLuIBeuk0Pykyu8gZlbg7ybu9uR7czW4KUTmvRxhrRcWaTrEQarGzFUu0coj7wuq2S00ufd3QbiVE2f97RTqFPejY/unvFRG+ISi/buB18qC1ZBhAGirSkpDPWKLx143bwaaSMxSJZhQTplqfW0Yc0qVSNINC0qj/os44k7ld4O8htJybP9OEgU66C9OJXozAlVCYMrRY+Zt0febzqGZsmJlYcfMvvIDm9WQff8mp1MFCktTccsabkuyJfitbW8zoJdtT1QMQ8aDdxS3inlknTz5E10eAvOcp2G7k/yvontaogs7lFhs9jkILdN5g9UiN0Ce/r4owcpm+7Rg7pKqx/Y84IZt028MVs4HdwRAi3p3DFKxUfcsSaL/S8K/OH7eVWL4zFnrp2UY9CpJp4UIh03aWmH7ekkj6JnCpJUlNI2t6HYSSpCkWAqJR5csOW3nXR38mtdIzDmqoAlXhNtFq4Jbpdoot0E5bfMieP7P8NXDGVbENHiHN7dvZXaS0w04NQVeam98jXbjinMx9T1Uyq8AQtfag6brU20l1ehM2WfOFC5jVzu5qHI5kpK9ezxXFb+tncOOXySt0tq4Jn4StUWN7Ctu5XuO90UO2qfj3PLGXNe8+ATp5ViuoZlvq+p0ahAwbJVoa4sY/FMTorJC4CYVv3noKh2cCvArnh1KjkaA7VvhJ5EZXVLASnr1KmHFY3PEqx5qXJfsNWj9xcRzCDQSg5Pcg6uD458GtklltPT91Le2ZUJkV3qH6+CPt6IlcKiX/sUitcapDctCnXuemU7uxyTtZcspXzS4sWW7q8XqgIjgiGY5bIti5uOx+71jBk95Hrncw2eY5ms6roupGYp91G7u3STlXaoqew4KyJ4Iat22RRTKGgpc/r+k7t2lKERRVjTbJwxyQZIzwEyevly/YxiPbgDrG2qXfKsbp6O8mYwhZgLRnCL67KGmB0TdjC6uKWfLVkAvlDmXq29czI7hZ73bC1WDnE9/WREMKpE1vQI6n/KoTA2/FVqtfqs4lbrXVzBflaQtJactNsHHAtrU67LiL1VYrNumJLXxy+Qi8xsfZB+ZNg14atjitlEttWTBivYi+hR1aJV8QaEUO8SQ0zhqrQ0J2ckjURiWkq5W7qyrucY1m3leTYxCsCXByW2C+yo1enum8NgN/bNJyYLuvZHxbPb+3D5BYWH17X4cXSiqQmT33oGsGbqyQ+yPgha+6DIfxyyKVjTVrd2JoV97EGI6jGc2cDU9i412iTtPZprnELOBMQ8rrMJuHFOuyQtPbJDm7l5WKPlhZUNmVHUxR6U4MWMYPT1jkhHBjdAtgUGElRa4NCHPxy0dJ9pbePDy3SUy6bAFKtfe2OTZIrmL4u7m9ixmURWy55g//kJrPnNJOdbNoFo8tzjagUdKd3twx0DUFS3NmsMh1djnaDHdCrOEqzJID4pIusjXqFVBV97uNNt4wtZ792Bbt/YRqU/2FlV1DetxDivpK9DthO1+JT4CTVXE6UVtm/UVlCFSpWFRSmtnGXa5YQ/Usdp7TzunrnJyqkbMfY5gWhnF11Oznyyi8URyCbqkatOvtMAWvO1mgH0b88cep3F7unpDC3HWObgmNydtXt5MgrFzQbgWym3Ljq/LdqlAucHXZQ/csVt1h74x4R1mC3ZTL1truPc01TBTLUHpCPxILSZO7Uk1e2ngT9Jalk3I81gMUmJLN7/OA08jdNzActprM9FUkX0zsf9hmJWy9rzaAOLLnq02fN5P7Ulq2G7o/67kn//ZcCCGY8mmWQ1N9+/wW78g5B+QP6s7hQ/BTvQJiSX3//5S5HvQ+g+OsCpHDfgPgdwYzAFo/ZAK3aXEcPuJ7UESSEABqjqkn1uXY0ZMEuyIKzJIM4dT76vEVrCR2hfv6JREfiy73vYHcd3eTZMc8QyeDwPWTuX37/RT7+7790cP69zNXoggSEJkQkgJvoPIfhrsb7KgjT1tYnArFC3P8DoN+LuURLMwP71xrS5zhSBFSy7wIcQbRDS+4eHI4hApbeROvgCZjg9iUFH8E+2L7e4qrjJNhTBKR/Ili2/34Bg30SHNISRtMf/YlkeHd4+b//HxxYvePnHwoA + + + dbo + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201910162004581_Merge6.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201910162004581_Merge6.Designer.cs new file mode 100644 index 0000000000..c9efb16413 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201910162004581_Merge6.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 Merge6 : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(Merge6)); + + string IMigrationMetadata.Id + { + get { return "201910162004581_Merge6"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201910162004581_Merge6.cs b/src/Libraries/SmartStore.Data/Migrations/201910162004581_Merge6.cs new file mode 100644 index 0000000000..f33fea2e5e --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201910162004581_Merge6.cs @@ -0,0 +1,15 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class Merge6 : DbMigration + { + public override void Up() + { + } + + public override void Down() + { + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201910162004581_Merge6.resx b/src/Libraries/SmartStore.Data/Migrations/201910162004581_Merge6.resx new file mode 100644 index 0000000000..9286e1a89b --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201910162004581_Merge6.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/201911090805330_ProductVisibility.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201911090805330_ProductVisibility.Designer.cs new file mode 100644 index 0000000000..ff3538cadb --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201911090805330_ProductVisibility.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 ProductVisibility : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(ProductVisibility)); + + string IMigrationMetadata.Id + { + get { return "201911090805330_ProductVisibility"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201911090805330_ProductVisibility.cs b/src/Libraries/SmartStore.Data/Migrations/201911090805330_ProductVisibility.cs new file mode 100644 index 0000000000..dd167ee0d9 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201911090805330_ProductVisibility.cs @@ -0,0 +1,90 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using System.Web.Hosting; + using SmartStore.Core.Data; + + public partial class ProductVisibility : DbMigration + { + public override void Up() + { + AddColumn("dbo.Product", "Visibility", c => c.Int(nullable: false)); + CreateIndex("dbo.Product", "Visibility"); + CreateIndex("dbo.Product", "IsSystemProduct"); + + if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) + { + var hidden = (int)Core.Domain.Catalog.ProductVisibility.Hidden; + Sql($"Update [dbo].[Product] Set [Visibility] = {hidden} Where [VisibleIndividually] = 0"); + } + + if (DataSettings.Current.IsSqlServer) + { + var fullStr = ((int)Core.Domain.Catalog.ProductVisibility.Full).ToString(); + + Sql("IF EXISTS (SELECT * FROM sys.indexes WHERE name='IX_SeekExport1' AND object_id = OBJECT_ID('[dbo].[Product]')) DROP INDEX [IX_SeekExport1] ON [dbo].[Product];"); + + Sql(@"CREATE NONCLUSTERED INDEX [IX_SeekExport1] ON [dbo].[Product] + ( + [Published] ASC, + [Id] ASC, + [Visibility] ASC, + [Deleted] ASC, + [IsSystemProduct] ASC, + [AvailableStartDateTimeUtc] ASC, + [AvailableEndDateTimeUtc] ASC + ) + INCLUDE ([UpdatedOnUtc]) WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]"); + + Sql("ALTER PROCEDURE [dbo].[ProductTagCountLoadAll]\r\n" + + "(\r\n" + + " @StoreId int,\r\n" + + " @IncludeHidden bit = 0\r\n" + + ")\r\n" + + "AS\r\n" + + "BEGIN\r\n" + + " SET NOCOUNT ON\r\n" + + " SELECT pt.Id as [ProductTagId], COUNT(p.Id) as [ProductCount]\r\n" + + " FROM ProductTag pt with (NOLOCK)\r\n" + + " LEFT JOIN Product_ProductTag_Mapping pptm with (NOLOCK) ON pt.[Id] = pptm.[ProductTag_Id]\r\n" + + " LEFT JOIN Product p with (NOLOCK) ON pptm.[Product_Id] = p.[Id]\r\n" + + " WHERE\r\n" + + " p.[Deleted] = 0\r\n" + + " AND p.Published = 1\r\n" + + " AND p.Visibility = " + fullStr + "\r\n" + + " AND (@IncludeHidden = 1 Or pt.Published = 1)\r\n" + + " AND (@StoreId = 0 or (p.LimitedToStores = 0 OR EXISTS (\r\n" + + " SELECT 1 FROM [StoreMapping] sm\r\n" + + " WHERE [sm].EntityId = p.Id AND [sm].EntityName = 'Product' and [sm].StoreId=@StoreId\r\n" + + " )))\r\n" + + " GROUP BY pt.Id\r\n" + + " ORDER BY pt.Id\r\n" + + "END\r\n"); + } + } + + public override void Down() + { + if (DataSettings.Current.IsSqlServer) + { + Sql("IF EXISTS (SELECT * FROM sys.indexes WHERE name='IX_SeekExport1' AND object_id = OBJECT_ID('[dbo].[Product]')) DROP INDEX [IX_SeekExport1] ON [dbo].[Product];"); + + Sql(@"CREATE NONCLUSTERED INDEX [IX_SeekExport1] ON [dbo].[Product] + ( + [Published] ASC, + [Id] ASC, + [VisibleIndividually] ASC, + [Deleted] ASC, + [IsSystemProduct] ASC, + [AvailableStartDateTimeUtc] ASC, + [AvailableEndDateTimeUtc] ASC + ) + INCLUDE ([UpdatedOnUtc]) WITH (SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]"); + } + + DropIndex("dbo.Product", new[] { "IsSystemProduct" }); + DropIndex("dbo.Product", new[] { "Visibility" }); + DropColumn("dbo.Product", "Visibility"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201911090805330_ProductVisibility.resx b/src/Libraries/SmartStore.Data/Migrations/201911090805330_ProductVisibility.resx new file mode 100644 index 0000000000..465cba8606 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201911090805330_ProductVisibility.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/201911120909434_Merge7.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201911120909434_Merge7.Designer.cs new file mode 100644 index 0000000000..f084fe871c --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201911120909434_Merge7.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 Merge7 : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(Merge7)); + + string IMigrationMetadata.Id + { + get { return "201911120909434_Merge7"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201911120909434_Merge7.cs b/src/Libraries/SmartStore.Data/Migrations/201911120909434_Merge7.cs new file mode 100644 index 0000000000..514a30a8f3 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201911120909434_Merge7.cs @@ -0,0 +1,15 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class Merge7 : DbMigration + { + public override void Up() + { + } + + public override void Down() + { + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201911120909434_Merge7.resx b/src/Libraries/SmartStore.Data/Migrations/201911120909434_Merge7.resx new file mode 100644 index 0000000000..1eae577130 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201911120909434_Merge7.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 + + + H4sIAAAAAAAEAOy9224dObIo+D7A/IPhp5nBPnbZ1Q3s3aiaA0mWy8L2RS3J9tn9IqRzURLbuTJX50WWejBfNg/zSecXDplXXoJ3Zq6Ue6EA11IyGCSDwWAwGIz4n//f///bf3/YZs/uUVnhIv/9+asXvzx/hvK02OD89vfnTX3z3/79+X//v//3/+2308324dmXAe5XCkdq5tXvz+/qeveXly+r9A5tk+rFFqdlURU39Yu02L5MNsXL17/88h8vX716iQiK5wTXs2e/XTR5jbeo/YP8eVLkKdrVTZJ9KDYoq/rvpOSyxfrsY7JF1S5J0e/PL7dJWV/WRYlevEnq5PmzowwnpBuXKLt5/izJ86JOatLJv3yu0GVdFvnt5Y58SLKrxx0icDdJVqG+83+ZwG3H8ctrOo6XU8UBVdpUdbF1RPjq154wL8XqXuR9PhKOkO6UkLh+pKNuyff786tih9Pnz8SW/nKSlRSKI+1JS18ChvMXbb2q+9+/PROA/m1kil9fvCb//fJvz06arG5K9HuOmrpMsn97dt58y3D6n+jxqviO8t/zJsvYnpK+kjLuA/l0XhY7VNaPF+im7//Z5vmzl3y9l2LFsRpTpxvcWV7/+vr5s4+k8eRbhkZGYAjRjuoPlKMyqdHmPKlrVOYUB2pJKbUutHX5WNVoS38PbRL+I+vo+bMPycN7lN/Wd78/Jz+fP3uLH9Bm+NL343OOybIjleqyQaamzqqusX5Ku9aOiyJDSQ6MUY/sXb3NJjrN1ufjYvN4UlUnWVJV8xMoT7Nmg87yS0zIlOxCaXRWnZNu/yjKDSmoUUr4IxTlgHB2WlzhOpufJS/virI2NfXnX6LMLRHrmkZe//nPkdh1dqJ9QHVCRBhlg/nXBG3sDarSEu+6HWaB9pbhvfd4S5b55qpoJXgVujIvUL5B5VH1FW9uUR2KrcPytyKfnw5dU1/LZEc0qprsWlLfbeqTlfyDm7ewkR8T5kZlBHlZ4qJst2H9hm4hDK+S2/nlYfPt72SfuCqO0izC7kNVqOrOlYq/vZy0QL1umDycEKXntigffTTE5OEFg+GgJKrbMqiHf/rFbod0ZKA3uNplyeMnuhJd1o81/1yium7H4sw7RFLd4NumbKFf9HgOHOTNQa/n4aAvSdbE2MAcm21pZaauF8++L9Ikw/9Em6FND+7tcXTMKyE8sLG6rW463KYW0PyS/LZJbh1ZBMBDpw4R+vxRFs1ueQE9tr+vppda3x+Te3zbMpBiJp8/u0BZC1Dd4V1n15NX1vUE/rYsthdFBi3oEer6smjKlI6vMIJeJWWr9fvJlLFbgaKkx3OQIOq2DBvhq5mWSz8zPdW1O/Ec7ZPa/2jQJSpOCA5d6xHOMG+z5PZsSwb7FmfIQO4opp2LOvisFNkW4HnwiqeCa2VmK7q7ybhAVSvjKrUAFSDVMlQFOMpGTowqoQeh66+dCaijKGgCzoOENcu6UO1qoPV+ji5D63s6wpxVdHWdZ80tziUhYqp6VTQpJHziaVXhQgHUrYwixEsonKNyiyu6OC9Q2t6dOAuES5Q21Iz4QsR1EATqtqyuNm3vXby3O2bGCKN9SHY70gV4vwNBr2X2mXjcroa0A1pWC9oKwTYisf6E8MD/6raOsqz4EaxKCkwRuquetPOPSjqHzriUi4zFCi4tFuBatSSndWUBLi0qmzrQirIUHv3aX1JsiHuko7TxEhv8TLob5vvqFTfjBymhbmuh/VE8pJcIXRIW7jaFMBF1lTycPqDtLvjSmyDqD7wUjzAFBlmb1vg++O558JDqmD8MV1TtZwZlZwaJLMorFynup+MURCa4CylarWr/PQgmdVuxzvGRfBu9ll/vJjW730Y0gx91o/mUvyOr5rw9TodhazXQPxpU1dVV8aWogxEG2COlO1qy7t4Qxvxcj76o9M8rvDXWPc03njVD7bx+JhMqaUATCVcgq3tcKSQotVsBqX2UVz+IOFN2qiu/7sQo3y2mSD6+CuVh59QRWZAk71Ac5LlGRhEqPU1Z/rHZfkPlpxsqwaqwAcxxodItn7AlBq19aAm69ImQqzWmpp2jsKKDLNQ1uxj5zirAQNmggg2WEyziIGnBIoomM54dJxXqO8Bq2INbrXF1dmSKZ5VhZ3Oe2YfY1sQpQWaJKCaJwy6hbmug0R8NHlvtfru6HFSkXdPtf4zr/1Myy9nsrVg8vInb0Nui3CZ16IY9YLtMsnr2rh9ttjg/KbZb5hHBjK/Zotmdjm5ucIbJcgmldhwr1BuUoQhPxQZj1lGaFg3wqmMOe1YcPnqfVPXZ7mizIUc03QsnW2ctg8QrEZWUn3LwPOns6FXV74tbnPseUEn9louIqFaicJ25JGuG98HzemKZHm7FmbC3uKxqk2H1dYznfHQ2FmnoLZlR0yOPKDNEpPMuyXUOw3EeQg5qRHd0m50ljnFZ39EF4rre/mhf7c2+WX1J6o4Sl2QhNlXoPkNHRt8GLvAAm7+TCfe8r2qi2DTbL7jCtY9wpGrl0e0SCsbQ2ht0j1PE3kjFa1J5dOs3P4Vj5rC6rhkw+apmKlXezDAgrhaGY5xlhArjLq3rpggL9JUHUXdYgHPttXgm13W7h7mezp5yv0UYpVeCBOjqisBephnILYAC1OYg1MTmwVxpffpAj55JdtTUd/TwmbZAOnuUrgY4DVYVpDmxq+U6Qa1cOy+qGh7bWAwORC6Veg2AeHWxi8yi7mNbru4kXwz3UoBx7WZrnYV72BaBneNLpH4Jxa5dukB1UxIW+Ud7mQZ2jQMBuwhDSF1VgLl3+UdSbs4LnNfVO0yQ0FfaYL8lOEXv1XDAGDTAriMZHFWs9hoJGBB/AoxaAIqAriLw8q5o658kZX1Gjs9w30UokPxKIIn2akhXwn9NsgzVOubhIMB+wxBSpxVgQVcGIwt4vC/fbov8RY/gYC9Wt2VlXHhKIYlsrAvxDhiLNGSykMdpxWxaiNVOk9el/Ajc6G9SI/LtHuepfIw1tMgEkJltWL2sebVUQ69nb+hveEfV1SQzPDqN5IJ1V+TIaG6KJCOSh4VaCjNOqw+T3RoCt/RB1xlhpr1cKJL0JrHcWV1iF6m2cwKk3EUOQNlRHirshnogl7Oy8QaXKKVqz4sex0HfULdl2DBner/fOlJWve0pildmFc37/kfxHlEinlVLvO6/uisRsm3w1wgNEkGLSpwKjXk6nQ7h1b4kwfeha3jjH9m7FVYUOkeEBW3fnFBWnJg5uQ3sVCCAfFIGoYLOnMLGFLAZcJgOW4K6LdWBIK5v61w7y7dvJbrHphN0nGvSNcgsL53Uc72Lap9eKgQ5Jo4XShH8E3tchzWvWfM9qUIXvY2bkW2EEZM71tFuR1gvfPFFdVj6vNvMcsAcLcGxL1hVtnrlTazXsj7OitvRk9F5SdPa1QsGxzp8zPvOXKGH+b0n6OCp9SeeN/uAEWQphtbXE+DETlC5xEogUDAbdV0J4CGK4LAX6CRrNL0m1pvXWFcVztHOiSqJfgCP6LsHH0Cxaws4pTzmaEDvG/arfEnOQ4RLjuePvj4QJrmtzvK+z8Fe2Ia8E/HSpCzPbq0hqxeY4daGXjvq8Z2wPuueB7qijo+UMsfs07mfx9GWeUVsIwqbG1tbXpE4I4tshZvpBojRdWATm5dGJRrYtGqXbVe1b+tHrND7eqkQVPl4iLCAJGWxadL6ot863G0BSZ2QHr3g8Kzj8NB3aS2akb6VjnCLHHQukpq5xfEjyjuU7W6a7L9QdUU4JYuC7GPhg0v9lribfvghMcut1yMk84QYApAfD4NQzi/fWSw9LXJUVWStllhxnaCqcy2sZ8WIlDXk1/F21cIeyg/0D5U+hzOnURrGeBNznpSkzTaFRJtZIIaUpc9rCPRZvsH3eNMkWfYYqoe0KHHGukIxS4ctjXvvMlMCizbz4JIKKH3it2R7iz7GHpYD2pIDdPg76rhRsAY88S7aDyelOPECWvvB8Mao3fqimRE6teyy2UazIUTCOKBrtTNh0MF9jIc0ZibCyCfiy+/N/IsuyZubpDVXlmSDro3unnGa/aPGugUep5Gz6g98U58kZbC5fMATQw2i751wiT7Vd4Ti3XYSISdri3PSqgzRJKIItYYq3fRRIVG6jjYboQ/BYzqr3hQ/8qxIwp04ejyhM/c5z7oFPiAMHuOHwb/6042E0zPuW4/m9GGHu6yRb5JHEacdijY8R4siBtu/S6rLhGhNKNas8tgc71pIb7oH9iVCrOLo2xkO2SLmmLPqgiYsKCN41o6ITh7TDHWdCpVxLMZzVOIiePWNONu9v0UcuFbOWt/k05xWiRCFKGaocSJPMV15STZgPLmjJpbRMI5SvKVGr/OS/GpNFc9f/fvzZ5c0uQzZPz26Hy3K1Fl1WgWTk0mYHMo4RMWhN935PVmaBB1R++/CuZFoeOn3vzZJb0QJEtndca3FeHSfYFKXMz8E+i6CPfXesHAeceTvix/dqPtAUMGerUWNbx5bg8Dbohz6eIyY9CR+iI+T9HubqP0Dkc/BwdPoaZBiPOtoSU4g46E3WKNoD/1klvC22caZpA5j8hAP44Dlska7GJge6b1OWWSSH8jQEFfuqrfgDRLwRHkXgzY9VowWUNaJLKAdPG4ej5u6nowrAbKFQn/F1V2GqzoO0l74ZYgsXrKvcfYr71tlcjpp0eE02L7GIYm+A3/KNvM20B/MTtr77ZnauNwRPEnmOBB7nKPDCPUK8HD9YHH1DiSemAZz3mleo7KKwl+92OYwo5mZohfsi7ZJDl9XGHVrMnjDIzoEquqjmojOb02NTortN5z396URmZD0mci8NsAo9W/PcPiJ4SvCt3fzLUX+HBcd/Ve8mRH7u3lpM+40ofJkRBQmTOLd2MR7+RQ3OO94WR9o9LEJzhvHeryi1yfy1OB7VD7Syo52r0GTJZSQ795tNrzqKinxzY3xmuDXOEFc24drn24+lfgW544dpk5g/UYfxcIz4vuAkqopEaWhhgJRwtiObR5tWZfe0N1sREt/8KjtSNvkmwy1l6cGY2ectdi1d45KGuYrlo2NQ0qpERsnG58s/KYA537PCojmwj9LcO6KOnhrpwiNihHoczaUXvfQV8V0QzT5mamhJN8yDahzrFFewOh85q4lWMlJTgRROf5JcK6uf+wGoO0zDyh3mC1X9pYD8vRS7NaFOjKgBHbdrySlj6UKVOWKqIR3TpXbH6M767TB03KwYWucRUUQ1QAkOM+Oszf+2r6zgJr+Q2CqMYCwnuPopZl2CD2MpvcChKrjIphnn0ePlXjOxVrP29D+tuaSmz5u8HjG1vYfrqIZj76CanyGWp7j7R49aQQqByfLU6ZYKU5ZGE9p+oXo4ESPh4we2u5r6mkmyKKWapZsqnpOlYjaaeAOo3Udove42KOZlgd5QJkJ2XIlF3JAzuHrBEcOTWdFULm7PISywwKYa5dZgyPQ3bEY4gypUOIFGSLoZcOg4XqFVep18fHX4XWDui2DIck2IIr75Vk7MzHcqj5X9LSZkuFGcCAfOiZjjG6BHZpytWs4u2ns44Fx7wpZkXPersjZcH7e15gSptliZw0z0zozt6ssOBnOiIkSz9Vpy2R9uCoMRzHZXsBW0JkgJjhpW9ICu25OIw7z2UxuV6ijGw8HajEkHt57VJO9Zw7zkHkYkiXJ2s7So+rXs/oN+dgmXAMYBwSoHgoIHRbOrbcmBLyeHFAcFAx1W5FuqhwFrk2WvTh7xYLPtY6LmvDpki0O6ZPe4/y7TkuMkkjwONncmi5TIhGStnRZP04P33z9GHGyQNbSXsrEenJ5eNIYKXszfcwdy0XbM9oWOaBc4n+KbGzhfDmGIr0qyP6N0lpENerpdj34xDvvzhZbs7UxXCT5rdZnNM4MR36bHN9/asUvN9fmhRPPv2jNXjE3CdEyv2D048MseVhiX4N7nELNV+HAkdXasD64B0CRbMfLxwGIiWArlMmRa0WAsNBTQw+cDy4f0AYnL/r6h1OLRnx1JDrGeVJOT4b6v2wWksnPeWtKM/ynWc5FnM+qnfMM54dqGcazeIszlOvPX79GeuH+MULkzuqqTPIKx3gGG1Ogt8uVcjIUp9ZWqLFI4IuqXibxgMw1FVAuX1JBQJ6Xu05+DbI0hiGMfg2C6PaSzDwJPcUzi+Qgo9VtkWWV+Ihm7SJ0jb3mvxcLIdgOm7JtJLa5jtrLnSi8FFBvwafwJ1DJR0dhHdf5zNRXTaRIeyHN3OkErFsWzWHVqtvazwXAojZHhhMORtp1GWkPZtXVmVUPlsYnb2mMYcuObUz09gIxmxRhr5EYSh3/HEBW6aBySUkCgYJUJMDbIoonJoPvoDJpRUhLrghHFJHsF02GrJ4QR0pvskOzezj2uZoj5QYdo43FQXeBKkLbtN3vxvi3ZNU6aiQTmvEQPH9uGqbR5JEyTxc4bMmGh9kw60GxW+7VE8d5On0ggoo1TC1w+Tb5ys/nl6fcI7VefH57D/diMyCrMovosNuo2zJsBJav+J3dubOifIceviRZs3zrvY7+vqB7ztwRDOIdCM6q/opfe5p0NWFP74LDrdgTrsOKU7cVyZDNveIORRYtkmcaM0yH+j2O7/F8Ea/g5VOO4IywIPsUNNBZDW/Q1V2z/ZYnONi1rE9Rsx5Dz89ooVGbUwam6HjEMvqEUOua3SQ0kSjU1cxRKTR1XU0uQuiMecNtKO6sjOE5Il60MW1ZDcC+61Fu3ExTHVPvEFAf1BB1WxPRggM6D0SPhqg9F4RiO6Nxn7p7mJgGalYW7keWGtethRwOWckR3s4JmA7rVN1WJA0/1lOVs+ot0XqaKdHNHvUxdVyzkUMtQlNNwOrYVOMPxdqTAWfY532CaZn6G3OHj+RaA2A7yIfZ5QNL7n8JGcFzq2UYOL6SPhYc94diHaorzCA/QgLa2fQ/piyB45uFSxUY70G+zC5fYMJ3N3tRMsp0ui4Uw9bmnWc/SvCp5x48gc2LOXacQcUCt4xOaDswHRd4jFaPzpYEOiyOdNGiCpKL+qF6i0Ud2oNUdBVnwQm84njlRA5kESWWfTyzexd7n7s6DvcqWi4S7AIyyy1mrJX4CxPwIJn07SrwTPTxqC4Jcx8cM0jx6PL7ILkPMjbQD9KkFMdV0V0DfCUpqi+LsmZwtTKFL/DBOjzheYcnP4YJNV/qahbJN+ihEy70g/s1vZdaDe9RexC+4q4UIsBDTAtXyW24HYEgOQhZbyE707M/fx8Rk/oXLSq+QjUDI+eHMLkm8nw482uQHxaFuq3L783svmd/1FjnbxbpxShj9T0ns23MhhbpOWfMhJau6SH12ByzQRrc7xyTPxr8PNaZQu2oqvBtTlbR8EZ3iSzQe0kceFa1+d3DPSDjGOIn48X/2GYRDkIGmUczvMfxLm6PEJ+a+tNNi7Q95UTUoW2TiulyuhjyjdlWVRmdrevPcHk4X+4cj8H6Xj/Y5pjRtW1IP2Nb1WfYxqQ11loiP4iAZ1YsooPup25rL8+s6L/nWVMmOjk/0xsrq+cPMV9Y/TzPuaKGKBPlSfzT32HZq9uKpLP1aKLdM9I4++TTdjd/tP2zqn8cHPxwh9kS87osMoptlWHc3JUpn3x8lvqDt64k8pzPeEDbsW0d1xHqr+gshioibq+YnfIqtjWulZLXSANtfdsMjHokMYyLETeTwy5isYssk2lkPxejS76TjXsJGU8VXP3Fo4Dv9GFXlPWHpA3OMkNYFus9qb/hvET6CxoA3maHsqmmEspWdaPK4qmhiFJ5QnqQz96SM9btR+AZIIqmN+dSUuh7TsswUJI4DF3qA7sATQTQVLaUKDoMM8iV6DLlIE/sl/o0teEvJVagSf4pzm1epEjubSyq2UnS3cBu/k4W0RbNGA7xa3t3vEBDYa/rZ1Ol28NvDMsQTXrJxFsMQ+Z5AeqhB69hA7Pbx222wJiG7/7BQGzrd4v2sJnpJB9It59iJ4tjljhsZEA7h43sX2gjk03la7G5W95B2BnuvbazN8WPPCuSjXdGsQHBYZPSrNueRn80eGy1++0axq9CA67PZXDsRADVbLvQ0NZcWS/prS2ZDIp29rFYJKCM09DpAxlTtcTdxSHVpWIG2vUduq91WAzaXhwFivJmL3fnfg94ckdz+pDDy4JXNMaEo4OcUWYcBQGkjRiGCtpprzAq+zcG3qfEEcdht1W3FUkZhVM0uJ6a/NNyxHFrd3zV4hz2L0ubbg12+Tu4q+WRWwEw/+B7DEVBCTC2es2DTiIAhpBkgAIspvf71MQIBXXzXOGUJUPEMG1doHuMfrxD2e6myXJUVeFmLQllNPn1jD4QYnhumKpeH3xuIym63oUu9a9J1Q8w+vvQroO6Y6tE4GuhqnRQNdRQHU1N1YIY8G1RNtvzoqq/FF7eX2396gWHZh2cNnYplMs6ysTir7FbIG9xdLxmYCdmUoBI3KOCi8MuEVjloFCp27oqdjiNpQnFcC5f3o/w7PxosynbvXDmA9yK0j6Im1H8+AcDU+jFz/UEBkiesVQtdCYQV+WNk1uw/06AlBR9cUzS1KnX7ao1EJYFhEg7lWuIywCFi/O+M0HyvMVxEOjqtloqrUag09mKcRV12Xz7O0p1m8OfZnoI2S2EKlCzI+pzIIr3SVXHUDIHPLGmeMDXbTviNnTYDx32w07OKjdEvhgW2gKM15Zo6mAPBPau/anrWgcQvpcEbiOHHUTdVkugP8qi2c0clPb1PLG8lnwX9LHn60DhHmeXoaI4ynnuZ9xr/rWS901rWC3Nr1kgQZozZbA0ZwHCpXnfiSCR3uI4yHWNkPnppfFPucYlp7wtntLMBz/I7A9WV8VRGvFuo1uX4aIHNKJAsslL9Hwkh7KTYtv5iDrLHlr7BYNiHZcRfWeucK2NaBNnvQ2NxTHbGqQXobRXykklkw4YQTZlJvZ6ApxYFSqXdkoQKGi3nLriz6+HLOsmLTO/bSB3MlfjV6Q16B6AvdzDLcrbVnQt3Wq03N6qlxQmD5z2Ztyv8mVNViDVA0C1wOR1uPGs2Uay6IVSsPZwtNuVxT3a9PhOgFiurifhoo6PNLLOFFU5/IDqhEi/H0WpjWMbKfwyaWxJhZy2ZxKDniNTbuuD+FZu6+0uPEHxezpXCG7oPISrhZVRCWD92EvxEPVkrXbipXhQd/UyT7Kjpr6j22iXXuECpYRvfXzIep22eqFDfNBSNEIokk3tlMzH/E9EhlnuRneDtTHf4zbZGwUWbPkT5eWW+5Zp6ihNUVUt0yD58x5vUHn5WBGZYjApxX4hoL1C0wkS8FLNqoK0BdjVCjrk9fYrZ6HaxTXv/ncQnZpFQwlkzDwRaYHStoLfBkZ9rxCK5xhnGSFW7zQX7NlBVvBOg86CvOQgVTfROhIH23nySLWvqMi69xVzSl4Fx5w0ZYny9PGE1Fyg0a6xi2TyhjY8bvl377VwlTz0+kEML6UviTmhTUSxctl8q4lIzM7yNLuiaGd6CcQ1dvqwTGNXtDEyNyk9/i81Qq7RZUbaS51lRtg3tujISEMOS9m9MU44kl0EUxUhyd4iNDdN1S3PTWB1y3NTu8c/Q+hWiIdmZ9Jhrc/3irJEZHaOkyzJZ3yr2RGLCq8LMpoNE6J4xqZma4KcJMgg0MY17ZhzMz+ScnNe4LyuvqISkXUUfo9wcofS70UzhXBZ0twqNb5Ioq5Bl4p1b3Z0c4MznIQH+hoPPLvZadDeqtBTGkHfLfkTwlu8MujNUgQTxTD/RNIuL3KukGgz3w1EUn1HG9WUzDrCk/v714s0dPqww2X3Ar/Ip2SSC7X5XyiZn57s+upSiL1B33BwKBoG1VHaKgLvimyzAH/IDS/EmEzDx0n+fZETvdDmIiKGbfPsZMnm2vvqKQzWEk2efUsWUC763bRVAEe3ubnXfUNOOCX+Zytp2ghUSUp/TqrB4k0vsmRUjV+gisn7NqOE39GbgWUJLje60Ggvm2+jjr7skM+bMr1LKrTkjcR5gn19hwaTjhAPaLZ56ZujBgcicHZNzYQZWtAM/gZlKEKE1/X6lLMn4QtE7xIZC4LVPcy7hAYZ7M1SHwt60d5djAZ7mKUp2tVXd5j0LyGfW3eFd0m++XTvcbJS3izzd1rg/XK7Rq9FwOk6GSqXbo9BIFcfIu01eNcCdN/Nlyi65v10dLCkfa6SW/QOVzTHLxzFDwC87q+8mVB+SijJ2UkD6hpc4Q98054SjYOAAK8/V2jzFdd30mDM0NKgLKq4Dq6tRT3BNPzdOopJ/ReKpM6K5V49+6iMwjEWK3o2FcE9Y8pde3aBNght0YaVkKedeg90lIUyMoURWBqMuYbr8OgOq3ZFHEplsvMlUkeFYp9e7ayksQQpSjsBQCH0RChX2fc1yYiGoJMXHIRMT6BYIioEE+TSCUtsj4zuHZoKlOwHPyR1W+PtdqCduxO/ocbyIO3UqBbYqwJTDYM2MHyUA/xqoV0Xd79Y59BkrDrOSwOvlQ6rNb7+hRC2wzJXtzXQK3SFclt9DGR9XqP5br7nECgDNe3V8qmGQSMfPkrrUg/tKlAEjXCJ04XVgGDlM0jghAuZg2BRtzVYDxmpAD2QtpNOMfwfZ3bYOKuGzh6lNb5PItjkBoQnRbNbyLR/QYixo3k0FrFdjq0t887nEuX0wL3EyLqmlhnWB3IqbB8LztzOWTVxR2tE3bfVeSZLmfuWbGUfkzZw29HAklQ7nmtVHXlEClDlrqyCD39U5Bk6gn1YdIgeYXMYJ0QKfvQTRc8/32v2PlHzzXHd5rSY2zN7bGh2R+wlRrPISOZ2HB9sG52OODfJ+Nbmpt1enH2XdfIdkmZFiMZxVg3Ioqnx78kCyaf8iY5HICqtu6Sx9hxieCLZ5JsMETUrmd+doxPwJ21Y+rlWE8rwPSofqc7oSNv+dR2LId6t/VFVFSn1FN8MqhN8WRRRZ1PpoSYdL9za7HA/C95yAfe31uqxJs3VhPtcTnMlFWq6dh4jzdV43xuo5X6MmV3op9VyQ8NDM/mOlgsZNrz3LSYfmVWesA2SwM4fAlxugL+E12ID/BGCQgEB+A5rUMN3keI5dASPgUN45uh/MzWzIXkpc95MkkHr/wc5CUHegDo4SWxogePff0X2o7IZTcTLr8H7yUMWDo5Io+vVQf6p24qig1yVSfqdUHwh5/v24XTcs2bLM8jXpX84mHlWX1z3sfZJFBe9wmXR1VNS7T/LQlxPMkDuIAeg9J3koYIc/ViUMYTS4QrAvCJbOkXx6/OJeR7Hem9cDDHXgWq9wqvFax0IbroBJxUO02EpaJbCmgLXwQrDXl1aOvRzh4fhWjmnAfPIrKzU8U/yX0kq5jJl8qWneaDo28C+PPTA9+qXKAG2jzZbnJsfR//JszW/IyH/cgA6DMIQkkxWgMXzqPZ4JqHvYxQrF5Hc+QX6R4O8spH3ZmUOzWHLULcVZcuIpjrF2nviOFB00u5tUXbctLzxvOdf1F7dRrnkDuuA/hm03dSKyTDm81isk5ub/pnj3M4B8+5D0qQwsi1OyNsuBtxV0Ylz6YpmQQ1kpgyNBiMuQ1CF+RaCAEydIFigfbNo7QEnZAcLcygUMR22RZ3wiLAtnidlf6R2PAZ1Di0eFdkpjvHEINqlVxy/yP1EWiRLEpWEh6hf4GzHuDgay1MQxRNzKwxavJS6ZuFZs5YSDDBuqWGjRgORGoL2EiWQud/eZ0CdS5HUCuBZpIIxdzmKn5EUadV/E5RQHXZBdVuGl0W2ucRcLwnRQ00+bXfzxyKjL4D+0eAy3Ce2vbah8D3Dx8J7Vl0lD6cPiKGGLyqC6ISwwW1RPkbbiGmW3LLIYuga8dJJn1Wtq7O766vk5xyWyFC9iYhCqH0bDl90wrDXgEicxLVtHek61Lpi0A0p3EpEmd7iOwh2dVsSxYKdS/eyU7Qq+dHm74RvWNNLdOW8cyFZoKGzimAiyx6lEZ5pBAhUe8m1vMwSVU5nYed5L5E2JeXrPupicFwXBcKD1FK3JZLsZ47kJI5VYbgEeeharsyaMu3qAMZNy4pR11m8BXZYWRomfkwz1G3NgauBIjpHJS6CAzC1zpstvkBveX3m9z2dLSJFOT7LcY2TbM2SjO2ilRS75muoRRcHaJRXPLSrBU25/y8tluUwpY7y3Esst0xPPr0vbj0kMql1S11aGSwHaaxuiyHTmi5x4mWhWIdgEsgMLmUG5lqCn1avBkySSzrYqLcRbEPQRQRUru1tnAttiYwxxAmFP4gUdVtd/gbSox9FqUsl8WoeQ43BPPR6nlZPcwrsqGNZ83HYVnjYAvWm9+L2PbpHWXia8aKszf7I1p5Zjs2/JeCLRbPbjXHZgxeap0Jhcoe5RZ9Lnd/Gqz9HcpC7QWWJykUai+pyQaWD9l3kTIb0d3W9M6YZehWDXJ8rY8BK2z0oipKkUo60SlFEZWjIvuqxlYx1q+nnYU9Rt9WLx+B76jiWG08zkvo0o0nfMbLHNZC6QyqUzwASRPCLfTavWdgT2QnTgffVbe3nfnbJAH0R3Uduc0LrkzvK4+5OHzF9SBzO1NkFSguvEPCX1FBI0L0YkRwWkrqtjvShe0iHZT+LclBcqGwPHccZ2U2ziJsY2zeFTavn0WselLVqQRCAXQsEC9rYyHkjYBUWL8b6hwX4My/Ay6y5Xb7VaE6RSX7bkJO82wzY5zKhjIHTEE9vek9R5C9ETIdFNfeiIiP6oyya3fLMTVpevlEuodNyt2EeT9OsV9/VHdqiL0mJKSqPpdfWr15waA7rTt1WS6gInLvI4S9sNbyO8/h5Tu7vA7W4627t0a7734HbZ+dD12eM2luAuXS8KvO6fQQw0eO54d4kzgp+W5ATEuk4+f9RltHbmmDrx7ui0kZ6iBQH6H1xW5zjlC6q9bxCeldvs+Niw2hV871ULvKaLMEhuuRHVP8oyu+zM8x5iYmse2ylwklTlihPg3XIHufpQ3pHDhqIJknxRq15DqtsBH4iS0d4ra3FvJU1AcuPZo013N/7yjNjHpkArhgSB6UfCw8a9gR47JZHyuwSpdQn8sWA5LBJq9sybNJ/nskm2U2MISXkn2eJcWSfh+nffbeT9wVFEE5WG8su2b63ZNLbFuZtL/R9rCi06AKr7vb68G1O546weBriGFvZdppvyMwuoGFdFE2+GcO8VZF02xbrx2bbL7vAdx5TH9unIzH7OGF9g/Jii/OEcPxsr0CFJi8aRnS0zyt7adnC0aicLUDEM/CHpL1WDzwK91gOm626rZ/gMmNOe0xKNgPC21dJ9d3/vS2tTThSxnVgTM20MuQKvyS+aPKc0Ud8RfGHJL3DOdoXoxNeibV7v8V5q+l4RdK8bNIUoY1n7dOynLauOfMk3lJPsXNETodSCku7uvOFkVaH2mfYHj4vy2Lkmq/EnJoNsPLZ2VQhzO2O62YEMXqQn+q2jKfoWWTUUYYT3WEglsd7kZ8+7OgS1Tv2RTp7UPyaVv59RY+HQP1o9ynnhK4vqo/ooSY7qYfMP6ve4Q3h3OCzUJMTgd5vw/G8vkA9b27hK2UCspXWXsK3f4Tt7fLc169ecIgOEljdFkeo7vmjWXGc7RGYnSd0rADhsxhClvFk/muDGrQ5JUyfHdU1kTSeQWB65bF6ASI8LBx1WwzBgl9fkp6QSaDWb47zqTAXZkQCdTUeJ1PomvlUkbcY8Jk2GGqZtPTHOE/o3mZz3ArSL2d6UP2B6BoGbWyultEGJz2PmCZAqWfQ2QMVC1BGXHfgk0qhhpJOchpQ16tcduQOneermQbBQlsOhqviOigGpcOYuFqmITFfLUfE1gg6aXP9jLJzHfYrjeQvcVEGR86n7LT8gf2qWL7NC7TLHqM0bDATnMzexHGazt7GZfPt7yjVRT2KpFtQX7H5PcViXmhfkgV6VeLg+HwEjZ8puxXgaUrTLQarqijffEjyJsmyx3iWjWl3gR9mR97nRJuG/c5oOyCW5KYRXfPA4EA4GN0+zQMGbc98t/z3ZxbPYYM2LFPdkeHPs/h19V5AppOSZeNmP+flR3helOLNmqtLUkVmMg6BnM/rVWWIRTZTy5057LLKnMW8TLw36CYhq5rsqu1aYFyYIpvFTpLtLsG3eYi8GnAcZJW6LYO0mOtJn1HHnKnhSDrnPr0qlzFL94voCm3JnuL1KnlchgKqw2r0Xo0z2Rp/5kP5BwLZZmua303gfVLVXXMliiFTjaaATh/vIlRpxxcnCuLPZRiIFR4i5pk88vuCoCO+8mD/yuNOaKr9Oqj2r8ba1hvcR/Sjeo+ofCZ8zdxV++9zMMbDdqduC6bYHw0e+9D99lmSy5+z4siTuAZLd2d1GcnXovxOSDhzHBz6puMS1ac941gtQ1qnGtYhh+Cw6jSrLo6SaTBG2fr/rGgrv0wL5qq/TWxCear76vwGgIg0LjiQtxtScYvTJPu0o9PNeLL3wXxpGe3mVP7TPPmjCjX5lFL11tL3X53OpskU+UZpSSc0rnsRwqWpkUrlhDQySJDr5ITPVwweZKDZsbedqNCtkaIx+AfNlGBCEgfe4csjBwGLI9LnSOU5ru4YQkBKvqWWE15C4G1RNtvAk0mLo3ohoToIBnVbUQ8isRJRtTMYnl5wh9OVpszTZiyQ+BfMX6CGktaqBjRozZ6X+J7QYnzG57tgeTyH1apZrTHOt5SHYq3UqyIWJrO19U/zhKa5Qg97uJg7qy5QEp4dtOqTVRw/HjX1XfgTNAbhBUrxDjNve/dj4VE7ezNsDMpRXqxc8/CTINWASZJUB+vqIj0tHZves9DKvk9App4zkEFbwAeUVEQQf0X49s7H12oK3cVhOuwB6rYM5qyZzl97zi94QUk3Co+Zgng5n75cl8kbIvHyyu9wI62UEdlhsRwWy8+0WM62u6KkhtAb7BWjnKt/WBxrWxxvi2wTLUWgc1iezn4ZJyBXHExR3m53PN+qkWjDvUj1vRv6jneBR8PkOwrD0F2ufMrDvTnIqnuLUbahfy0QTHFgs5Miv8G3TZkYrgXjePicPhDpxc79jOGos2abj0EEZ27tAlVEQJ/lNzrvuThN9fFACHKvuGj+Yae4PUsZ80QNJZ33NKBhQaYe89Q/eiTlziH+9QsG1WGX1izrKCEkO/7Qp9F5NY+BzSp65Uwbfhs4/6He0zGgpfm7pNIFrvhTvMDQZ45x+LpaQ5/m3jPaxugGLm3mpsNUnvpZD+0fDD5QYfkG7bLi0TMcjYjiINHUbfW7UqhI28+ijhV1ejmlZmLKGAcWixeFkZKPmB8QxmnImHonRiPv6np3VSZ5tcVtVMUYUwHh5NzlOqEEg3mcnDuzliEQU5w5uWy+dXaC2VuyflQSiRHa9iwuOyMOLs5jDboe8T36wGTwCHhY6/M8V5MNp7cVAicrcWO+HoGnc5UKRjpVKQHD4hU8hBk8ufoHxUPd1ooNnjMFNadim/7qSTW/3LaMwxknAvk9JoTda+jPs6rfFIfFGyYYIymXnUCIaQ0Os4vJTEmYcYlDJ/mT7rNLWF8HduxsvouYYRc8Q3y6ualQYCyS9rVnGIrjpE7vLvE/Ay8WzonU6FLBBq6ziOrVSbHdtZ5XLgpptIBhf8O7ozK9i/Gej9byeAWkVO4mbQt+zxKk4IlvW4yaYDSLP6e0KS3+aiiFbhrf4n+cpN/PcrJe0u+B/vknRChmxe0LBcaD5qpuK4rjL/lz06ThkiqSx+8+fOsVrAd62JtgpRVorOCe5bWdMKeRjHXMA+lBrccxwId5jJLd4SZpUzmXAdF2BlkCoTsIEnVb+zmGfsHoRyS74dq81QgjotuifIzAyyKqAx8f+HgxPu6FewQ2FjAduPjAxYtxcW/zGpUgbybmER14WN3WeKp4Fel08joMz/w7fllUFdHBs3AuE1Ed+GytfObw1CdvLlDaOi6488WWhg8dEBy4Qd1W1Ismz0sfOlOh5tJJ35rZzvsVb8ih/W9FbmgqSkJPXGfzj6jldJpkOXQOAoKvQFccEQNadm4iV8VRGtFb4oywLmxKpwxNSzvxc93x92RHAoolAzoEExQdikcYIFMnJAe5qm6rpVSwp1BJGqP0jmCxbi9VIzluGIZOYxnPfpO6jGy8vGuvsZaLSXiOyt69kP6tu7aMdN2+PuF/jG5xHiUEIpm9H6cPuyTfhI/vY/G2yLLiRzAe9OMrzjfhiM5SU07xGPxfP2pXmWWsOPNITorMEJQujvfwlsnVOlszJ1V1kiWV4YVkjNDxT0FP6tSfOGqSeMWmU6Xs1KRmyxlv0T1GP3pZ1jl4n1Vvs+S2GoftrDgdZ8UtDc4loo6mPhGeIUI3e6RuZYzc5afnA9p+Q+Xoi0CddtqQjL8//0WaSg62T90zgr8ywPcDHeFf2+G/7PzLjm/Hir/aNSRX/JOholzjz7/I7NIxhpZZWgPIF1zhbzhrdcJgXhFs/xPuPTLL27aaHbNcoqRM7zqHusqBZdrRnrfR7yzZ5h3eEFVcySxWM/jXJmmlAn3dWBZZpPU+zCGAfY+zeLnDeU5VM8tVT2Q4UVImAr/yoO/bJEX1JdGg293Pn7AdV71o0VUvWKx7JOg7XFf0ZGBL0RbqiIF/pYd/n3xDGQsvv0PlZ4xTwIdl4Ttrgz3tHabPOKNOHYt6j/N3cofS79+KB1TZzmDnf2Q7fx+bLSpxekHf6Kvm0Go+rjAqzwkmdJJkadNpUR9QfVdsIgordSP73Hmofmw7O+TknKK8Tqa96s/6CkebvxNqde+UFRuV1fx8TbIM1edFRQXSBUoqei4Ln5je2a16AeDf45wcbbY4t54Toq3fJRWyXTMX6AYzyoZB4hHoJt+oZJ3UF0IwnGRCpT+ZlNNvuFYxlBV3nDRlifL08aIgzdIJbLIYSsYUxg5qYI/80XbjA97sCiLe37AahIFXuIqfd7Ysc5T9SB6rtjLXmoF3mGpMWz7bpZA1mtKc6LHvi7SPVhQ81UIeZVVL+zxOZsU322mmL+nIGkSUZ63lQudlE7CHMglZvOajS5UxYtmn2pKUtS2tW12Qmkds6Ty5b9utotFzI2D9gLloAiYJwLfP7TK3VmA+lUGHLV2QgvDthg04pW5pn+dbTN99nbf5xe3o/YF0BO9Id98W5ZYOkKtsNQVHVVWkuCXhyM00Hmhn8L1AVWuuvB5SoAn9P803zzozprbWZPR8HCNSQBWeP+tGRIiJ68ffn/9f0vBtGxwvo5kGhyEIjbzix0Qa+ZR3oc+fHbVWNiqxqjTZyAZjQtEN/6XfF6iadlLkFWEPogrId8E4T8m8ZS5DEZBYXinTTo7NiSVv0A7l9DbYZQ5t+sEmzJP7MzYrENNEu99eMsxqwcP4n63XZts3SwYGqyi5l4V2Zl24qafHt9pxLMW02nl7Ehw76C10UNfTFTr9s4+MWClZ16IuxMNstVmZy62nwBIAIc0rzoupHYi5AHc7EMymNyy6vbE6OAx2cJ1/l4qH7KpDDO/JRs5N6zm4915bmzB3G9sCrO82zzYdEuvubwUUWWbWSDgokJ8Les5wYF8O4ZNSNcCuL8GE0Bw8CYWC9vwor36g8rrlEx1TMHAqPutAXLmNRQxJRYCB18FrQMcX4jZgLqxkG4HfG69d1kmNWg/dnJwYT2gQnPJRyXEgNMR3HKAL68EtAAw4dHV1PKgdwQKcqJ0jK02zq7I3luzDJhmZUYCD2LAHcWFAEas96/3y4oVs4fZiIUUfFmAeBU2fEtvwosc0zfxqictCPG6AkbRSMj47gf1ZkKlAWtu0z1Xcu53neorrZjKWTKA6K44LbwGYAcayY1qfsR/jjEaQHBowdpOHj04FAb09KeTV5UGNNjFFXk+Bp0z9FSvo6NHD+pBFagbaxBRk378CZRrFgrY71Xy5GOz2Jq+o6z+9VTSbKyRIiC8HIBeGlBE/KdOFsvsLsKByTp6ECYP2/qTYttE3R8bRcYkIrOLAHs6VCSX0AB+qGHwdfKgawUKsqJofm+aHOvszqOE2tuD1B7TBSe+5praqAcCgaa2Dc7KrQagBTuT6OduxQNebJSxmGjrbNM/W2x9n9Y+H+rEMPKFkABAc5C4O0onJ4DYgGy6MfP/yTj+EJXhTO09WRt2uymoYs/eVtGUaMajsHIwpBKKV21g/Y/JD2ANj8vNkxZhT/Oj9WFH6+KhGWSkCgmflHsbpkCzitZeM8fZeVSeWONsq6PoUpNobXLV5Gq6PdmRSaOyBfjRYY4zTVYKYaoB3YSptG5D1xY5xHUjDxtA2ri0IGCIFC+dCDhD/PtaZriMLrDUdnZ/memNH5LLkuHrzrTq+GehoZc/RQXTqt1kXEg1V5qPO2IK9zhWBJsOPC/SPBpeoSwJj7DRUy4UyM7nB6vsH0BWAm8n91Yp0Cwg9KxLZ9GOov+9D1HAb/unmU4lvcW46RYnwmmOUx/lJwr4PDwVDX5Y7CalobdMDoere2YyIJ3yPysc2d46JC1jgyAzGoYZEGtvP2VkM6s2C/AXR2Up4MfX2zVnHTb7JEH2Le1TXJf7W1KhL33g9lZgYzgaHhg+V1T0Y1KorahWHGfNaDUwuI1xuLbiwgNW90FhrPQukH4qlvVRVz2ohBHG+0N4TNKKaxrIPvoZn0Z6X921clQfkzsjLsbAP88ZULdRd2QvvPUFjft/+aFIezZoGJpAqaLjNx8avbMbBFLsaQakcxXJcqpwvq2NWX2c1XGopE0X4mXn0CW/lqjHsgUGfrhDl7go4M7WBg5QVNQzre7FjbNLR5L4aFjaOaDleNs6nTVfYeqvibEvhC9VZiJ+fsCDWjWNPDPx0BfLlDqX4BncBDkeLhy0D62trWBmu6MHUhh48Qfa2G9FyjG43x0+B5eGRfOpSxis4UsV+HrjAJ+QaNE4vyj26A73VtFqW+18qAcNdYOEE8IZN72AMK91ItAzuJ9v15N3blqPtlvVag5f+/ldc+Nj3vmPZ8I3/+uvw7HsVDtvyVaKJxwfARr5cZzGrVTBSHM8Tq8f5JSlxktfjtJwU2284bwGdXA9s8WgIp0HhQVPrDu3bl8G1o8vJBdc5tenZmjwgdOOzPNBZoFgFxz/h853DsNaxNJ7gSc9iVEPusM85DloVLJ5VLA2uQ8D64Aa+z80A6ug6OB6aU5uesfXWxvu+O4CH2I/A0D+TgF+PVH/Colw4Z1WXaDxlmI11Djg0bA5U9+B0q06ouX7FRjmPAS63FFzm3mFZrMb8JtozNDzrxqAaTPZrJXyd6LphsVrUS3at68ZiwHtbPRY84bOGJiz7Xk2qjdN6qzEi2I869XPsMNaj27+m9VPsLeLg2vxd1ypudWRNLTKHVdLiibBU9P1RLxvTql3t6rEa8P5WkhV/OKwqEcW+F5eTIcrW2uRzZ7Mqu9GejUNP2wJEs/RmRbKxCwUIQoNBCHpAp/AMIPK9RQPUdmcB9tLS2qb9NcUDvL5MaELQkS1MAoYHjyy9BOTQJaiCfePLLrgvC0ovmNI2HeBr7o3DrjAqyahpag82CaCKCWBwiMNGSBceU6DXxBtvewvx2b51QP1QFmBR/VTZdGAVOQKnYZiuVSTIGdjyKV6WKHu/KBM+3QuRC3SP0Q/bWz0eWrP3doAeO7DQwlNiRe0Iltu24Tl6ciz5DmW7mybLaaoSnqmsOEhZ3ci0TE1v/lW3rmZoeMmsjK2NA1uaz43z7MD4XcW9sf/bomy2bb4AY44cGRRi6xHKhY8B1E4ZcaIcgdSdWIC/1MR10Sv3y0ZXxQ6nlnzEwyoZqQVz5iQB+Z5YCe7FUrwEE/jpMNN1++8fZdHs9JzEACrZyJmDWKQA+zB9W93Wqer/UowHzIdN01OtNQixjmsshEw35DnEV4dZxXwr5Tug68sKPG4+rPluBeoXwy9mLYkZcHwVjEGu4j6Qr1fCgooxLKrDyfNj03xbYf+s+KWo0fXEQ0aG4eG1DElBvZhSaEPFl+vMDmcYxZKcCc+VtZzca5K4j+hH1cbhM6bLlCAhphyAXPhRRvyk0mUqu78ACyrnxKbtvafLpL0fMiyOjKPjEhFYxYEe6TJB9AAfqhh8HXyoGsFCrKiaH5vmhzr7T3TO3ida5PrmwKOn+uaxO94w+z+eP32oUZkn2VFT31HydrENLlBalBuzMcqqNkQqXUUX8tl14EnlB3ca0gLr3WmOn4SF7FO5oUHdcZbh/PZosynpvZGKwyBgiKNbOBfWBREDnDp0cB5bq64XCzCXjro2zfM198xRRnHJg0Xkoqcp6eC+L8Z1T1J2DUmjPldEoX+HSW/KxzETldrTV1dLl3aMreCTnA1uUJNHbH1cajWUBZjWag5t+rH3vGPgSDrJ58RP3TJeinu71gDWBWX2SvmWG8S+mJabN5tOtBX2u7m3p1w9jwpwyu3d1Wwm4n06LKjo+VI7vDwXT4nZTP6dEuQMDPcUfTqVvV+U7Z6gL+cf+KY+Scih/px0+S6p0OYrru8mDlKxi6EexJZDFReuNDWjEosQ98d7+WPZqwV4z3IarDgRxLB3xuSUiJGFTPwC1tIxpa/WqG8QYE/VKti/DLUayoI8rZ1Dm34MddbFw5/ZJebGyFzVxbiZb/XpKKL2g9kXU4PzadMZruJ+1daP1D3C4ow0wSlV1o+Ori4i3qfDmoqeL6WsynOx/jPSBfpBls95QRBUw/ox2t51lSA2BOBdGFLb3JOy0tuMZAFutZm/J2HBhwZipwgYa1qLPfD849KQ34KRm3XJ4XOHd63TiZ5IPBiYeaeHcMqyw2N9OtsL3PEF1is8D+vfXIZ+d2fmgVVMfMFB65jO1RYHNwAllVFw9XpYEBzCgpwIzpFN+yOC/boX0G7srD1WBOiIzgYiZnuflXhmN21PltKdVTS25andCjxXviYZkQXWWjQMDjEXB+nCZIomnpTOrB/DAvypn6cnoSfzQ9ArfQDsjDwZQdeOwFVLK3QaGq9fq7tAdVPmF+gfDbIJ1gCDw6ceBtLNQAA28aTEnH4MixgFdPP0JMTc1GlL9U5VIfqLhJh6ntMBrGibPUnKTmU/bvJNhrRX0Jo68GGMB3c7kKmbUrtJMGOYbXuw6Nkipy3jVNj0Yqq1R0uAMBLjtqGsMTsTPs39wziMffDrk9xFpFGYvMdUFWbn1KfoUmYaxD7Y9Ak6mJ0XWfalqMko+tdj9MNRXv3QiFRNHTAsoADuFA5Q0xTErVPnV8ewFkNZgGct5s6Kbcda+1PS71D6vWjE3BTSZ7XSbokAVOLBuk4qvW3rkPYgjXF13O46vAVY33W+rZQMsfIerSlpUxJS3J4nj+1lylmOKV7T9bWmFmxb4Su4mVd0jdnf30Y5mVl1ZhFzicUMWPWDqbcaLhxMkxLb2PKICoENb3q5CFk2D3CraWnsXyi7jm4P7G+ab5suiXX3throtN6TuX9f3F4zvynPKBeApg7E8wyIC5/rWoFsikLnV8fZFuNZgJkt5s6mF0LVVbCv0c4GAc/EsE/TsKYbwcK8+STNaVZcaOI+R67z5bZVZBfaE6M9WQY7urnBGfmCrk3XfRIkKOgGICcxJ2FePPqQsgtLCCkVYa22zT27bR2lmRCBqw0Lp97RIHB4y8zcLacK9I5B7PYvxfTjWGTj1M2Ti2Sj9fZ3G0WOVTTDFt4m5ePpQ3qX5Lfogiy1E3JWQnn6qL6WMtUE76doJadLKWMrIOv2fZ9HFFr3aQE2tJ4FK/O9Gs06GLT9w40zuSrxWZJHv2deBDuzNBOCBHfgPq7+/tguvUObJkNXSfV9MD6x39TMZ6gIsqBcx4khTU1Cb1DYsaxud7cd0RK8bTmfNl1h6+2Ns//aoAZtTrcJzo7qOknvWjvqW6zRSdVVIG4GoV34WdOca8rofTOyeSgLsLB5+mw6QeFXxrRcPno3boJyuy/AxFBKe7ZZbkSzuZ/a921v3AnNj01n2Hpr4NbrbmCpPuirqoKBMz35kW8C4EKuz6tTDkwjWZZnwfmy6QJbbw2cyiw+lsXc5BtLmOWkKtsqwM6aFbMqbtaPaG+iGJhTm74w1fbnd0KU7VOCqX68pj8vRTKyLh4SKOhLMkI5uY/IuCFHka4IRr9/RlUPYgHOVM+OTeN9lf1mnLtsvlVpiXf0u2XuYLCKMu8cC+2cew5uak8JhbWdWYDbzMR3se/vz4m/xPdJjT6gioavu35bFlsj32nqgE78HLiTC7+moeXZzqI3C/CdBfGtTupMvbUw31XhynpTjVkZj2lm72wn92V5ppPJbtOHqdbeGO5suyvKmnTtpjVs2djq1VUgluOgXThO04yzZT4K25k7tADfmYnvYFLH+e1ejeqnD87Mp64CZ+HzZD5NM/thPnOHFmA+M/GfHPORhrKic4of2ETPE3IFNeNNsO68B7QD2Rx1DL7/069pKIvxrHrWbLrQV9kbqx4n6fez/LIu0u9u52FTRYh1FXVcONjY7JPyNbcdzQLMbDufT+KgrRqMKbKDod7CPP0Uwz1YjmWPDP0Egz98QHlDA1f0/q30TyULA7AQ2/JgLtwKNQBenOcNjHz/TKoZwgKMqZkhm9bb6stwYmfgJ3VqUgOVg5K9Tcr607e/o7SmReiBTHzaSvwkz4u6xfKXzxU6yUrKD9Xvz+uykXVfivoS1aO9YofT6vmz7jvDR20BwI5C9eThJKnRbVFiBGIZyx+NuMgvGncDQtMXGVG8L9Ikw/9Em3764E6JUOauvU/y2ya5hbH1ZXadQ5d12QYXqVrOU3ZPgDMiP0flFlcVHrKcQ4hFGBekZAV9SNpoPgbME6ARPev3D2HlH16Y+lpkGdg18t2qcheuRYViiJpjOSTdcIxI+oc6IE3Gt02mjlBvCsWi7MosFiSRKYiskXsiYEFEHIA1bVrhldc6EvUgRpTHWXF7XlQgrqHMPPmdlgHO/KDuGVAMKWMhHFOWaBN9dILUWoqe47RuShBHX2REwbqOQXh43zw76uq6xUGYe5fkzU3SwoLLjC23njgajhWXaKvgSwDMjBpl+B6Vj1d4Cw6bLbel4hRhUkNINm6nK9oxTs9bnNUKaWioY9uolt15GAuu7+BNvAGA2aK+3KEU3+C0VbPGIWsagSuYhS5Y7VN7hgJlsAbeszH7ZmyJd5XAasNYaovoS1LiJJ/CSZ0U2284T1TEMdcyNvzXJmm/fM4xKBrYct9ROHTdtgkb3P5Ie3YkADboJ2jfhqwb8Z2BNtSZwzT04exMW0D/IgMU/+NrDdMRC6OSnHFhDWwsNKJpXXlUutJYaIdGeWicSm0QJSoMdr34oyyanbIXbakR0Uf0o1Jtp0OZEcnpA9n28iQ7auo7etzvhKT6HKaDNzbWxi+DsPbB4CwVHiaRJ7wDA4DmbRhIEwpjhzO9WmLXIbSjn2rKmeTbNmhoIkolmi4dqAGNnIAOpheYqM60ufcpoMDtfMy8ZYlERTE+R5gBGZuOAx6nkO3ESD0mlQJMNi7ThXGsfLxieLxiXGnTqU6MgAme7uSApa5olTuYKhSrkbZQ4DkVdyoCCzq2YYPbbD2ZAjmBBhQ2vpY9qtaiqkfXRX4zGgHBXln1Zgj+AvZjiqZjsaIpB39A9V0BylIewoJKmXqvY2KzGNB8LjVoxkLzJoFyRBQi7XITYcw62B0587c64DfYVMkBWBjWCtgU0odgMBrS2pgACkPRGGbBphMamy5bbkYmPQ6Hewc+8HfAbcJp1v87KaJmfg7A4nQIPBSCj4nggy979AakZiWVeWUHKqXcU0ejqXK7S/AteDAbyizMjK2j7RXa7jKFVBNArNT596gm6jV79axS7mVI847FPE1S7IX84yULfAZklpikpxrKk5HTgHmnaPiEzDugG2c9qZoSfUX49g5kRA7AFt0bTNZTpRi1CGNEynnkQhgFD2iTAHvMU52gnYotjpq86xt8vBQdFa2QaoYruCSaLmVgBxDwjkblxONwB6CVHwCc7T3Mow6xCGNtCNXgFEAstGcKttFcX/EQ5oGXRVWRipkGpQhjsUAHfxR4aU7eKhaIJvcNFTLWt0ZAyDhb6C/lr6crfaaO5nZ+qiD6hrChUjX1Rk+hcdBKdwHJ/cS2icFXiG1icmsQHX54YtkSknWqMFMRhjaMD6ykpJ/oC2KiHox9ZtJxcRNVPhcyDW2qqYdrURuiquCzoaGnTQMAYVXjD6YyiPha9omRCW1ZU00KOwQQue2o4dOYnvSDjA2nepFlWknAA2iGxcKBpOocgHSU4VDMvKiZvFGdCxI8dBZE33MGUjX80YXJQAQWFcQHECU9SMC5B12PXkcyIWBA9RhAeIgoogeThi4wToA6jGdVMIWGKM4a2ogg6hEIkBA9GM8yDSlERAsRQfAmU5OCBzSPg5/aYLLw6ADi6LkuQEG4Zjqq1gcYKPPuPAHrdnu7nZ7BBVBFSeQQghzjLGPyT+uoIoBaDIevEYE+AsKFiNQ7U05PyzRUkmDNoxKr6Og0uX5akEtCDMkh1QR40GtwGNWqKzKQeiASLEQaxoVVQxMZ1czqC23wpNi2zysnT1qYHhKcfhwieDDDgEgB+ihJ7aPedd6qfORNSMeD4DTaGQAOanujO61O1YOQAXQR/H3DacM59F6Pjr0AdWBIzZDACiCFRL9jHaFgrJBqrEAXgUqDPc1MJegxpXY8wivKSFQSXknKWKNsYr2hVcdFEoxmbxFAQdnDOCbrtikR1bzsMvhjXR/tdhlGm6uC7adMFC28elS6ahCxmJcPGlppsULbunIKPCjHXgLo2AiEU48JAocoJPika6gEYlyaq4Tu2jAWX8WFC7iaMdmLRwztebpZiULISQja0HCEdhnlUCkm5Uac88r1sWH4BY6GZGAFixFC9SIQDkQL0A4eZywtYrDVfLr5VOJbnGvUCAnUuOOLNTSKhJUGIeGb2cA0NMu/uFITiIMzj4YFDyYNhwxiI/5VWSzaKF9zXbOvyZQks6puHLwNFg2Bda/YzJS3alwtE7mXefFnpe+E+ZCgrOJAAL6mFcUdSSy0MO9WI7fuREYfAs5JukWJNh5yppeYSprJsMZhSVU0FLM8likxz33MEBs2M5kEaj8qM4N5kmtR7uIOPPyjXCXR1HWMg1RW1ZDR/vRmbGSBUwjUBzMfguBuAzXzYxghF2VL+K2zBSENFY1D1tfXEFf5jttMZkOb8xJc93j9WvXkHPAf8ECj8QRwxwb6Hugf8utcETw6AF1D2zFF9HWij0jguni02Hy5W0/hBZeZtiPWc6pgqoCj8uCLnMCuhxCY+WzLQAefk1lcahnV9T8WVTRhJGxtC9YojBSwxaShtD6YhnkSrLuwnB1D1yXz/m1TO4gq5p18zjlZVInSdYSPmeI1HRyKIKqwmPY0MVwXgNkRYtDMNUUeK8R/WXisBS86L8v1QMyba7FIQ16r6kYS2GDRkB2O8mOmvFWz6lmYQ0MVNSp1T+0nRYfElUYaXA4T5DE5uoYtpkjDGfHFkc3CMdf1Fhw2SyaqpNrXYgFjXF0rCh3mQo/HmUxadA5zNARMcZ8ofQ/Uk2bkkoADm6VK5ag32SpHdme2pdWcIcSa0UkTBtRc/kPwoC/BFABO50sAolvOUfP6MtnuMjRFpFOzjwBpnnO+QjALCeigg6WK5B70GaPrXZ9woahl+igg1QOCK0D0YeP/aSikQKjxmQefcAZRSXN+kIFshqI5IziTZdFzwAW6x+iHxYFKADSuAB4+2HUexrogid6hbHfTZDl9D8MVGGmmrmk5XCWCuFRVN6NZm6pmPMg9xvbUPgACoNTjk4EhkrERRzXEApDN/NBnilJqJokAZhgGD60kyhA/1UQVAd0SZLlmg6cqaMLCGEbAgCqokZjJwCIBaMAFg43JHH0sWi1ndDBW89iNJQ5PdLhgYkgUDRIabEhfndhg4GzWOjOWGKKDQafiEJjAIbT5UpDjHhM6WUMeAdRiSHyN4C1JhVdFrVjv5IYgytpHlTKQeiASLEQaJqyzhiYyqpkfVdIGh4eKU3RpmB4SnH4cIngww4BIAfooSR3yrFs4ogBPvmBI9XDgChHedPP4Fjhu6aKHa/UZu4rqIVvVhyhqiI+uobJdkzPrRW00b4tIAyCcemwQOES9IeC7hkwgqpmjC3Rt6vhNgDB1X8dB1jRYiieg6PjXU6Yn9fMluILGNqirp3u+JIbgt3jLBLehecs0GyX7XAaWZOygHcfX8cuMBOwaAKgHM7PvEmz3ZSW9RBDD+pkglavQrFaJmJYhgcbUJgPZdF5jYnMmxDKGtSEHxvU56fFdUqHNV1zfMTktZNKYqqgHZ6gJkY3J36Ghmgmxip1iKaFQapLrKb2ImoZwBfNAwXo6+jlIJn0bACmVcxSLkp/ZCbUmJ1/Lcbxc5TkJyzc0u9yjGWYMop8BMcipCVIp8fp0NyaJx2CakwRAShytOqqFVw9JVw2ilCKjj4Zo2hZm1mOhto0r1FzJbbCey8YRr+9c+Txr6ZMiqRenAKEeFw8IvjOZ0jzp3pTweOZcmGxKqOsp0ZSaCDygeQwcvI4kZhUNRgk9vFBR2fvoPGTZMVoUREDTGViADzpPi7hmNipw6b+00lwBqR4PXAEijpSlTEMlBdaZpTbfqlLIQGC2Q1GKG3/qLKESMOneDMoACKnbX6AK8KbCJ6XTblog0pm5Z+q8WQIpYS0s4xZyyNHWvrA0EnP9GWKh6MB1O5CyFry1STkKtdubGvlSsUykPujWpRrYYZC61RlIv6XWqNSwxrylhHUYl8bYFUixhXzKiiz7UtRt4pP2cmgKc6+KvK8CV49NUyvcZUyDWxGiXxHt32dHAFOEXgNZSYEdwrauRr5bogCJrMqDqttPbNuDFjmQzjWC3sLnOL0+y3GNk0xzAtdV0CkcmnqwMiNlaNXqMzr082p+YAbaazl7rJmYyrr2A1ehsCGxrdHIskWA6sZJ9UniMOXBvZZy4so014GrB62pBaZ14DIBa4ipwwvpmFJa4Kjk0+lHIJzdwHRakRelltKFTDSxpIWJBsaxLznmMZ3zteZwJgNpJk6EBdmAyTCtYwIJ18wHsTGJtPEdjgJSx9BQBXiNZDYuSwqECziG0Ydh9JkL3ibl4+lDepfkt+iCTNOUEho4PBgraXR9U104JVNhCjRvxgtSc0qKHZeU7R/WNOShLQfJVYpBNR7h7OSSs3pf81m5AaKZ6mhGaqgKEhBOUK6jp6kV6JaAT1UeTFgwa/j1WwwLPw20epjqShAZVfnONXTUNDDz8024ZdNjYItaroM1PROORtWlHxAzvbjmE8hr6crDWg2Sq2KgoTXleKQAvdjy+TiS+WrPkGwlVzZhSTAnO7LtgO/91VPmYwMggrfDfk1/XsJBXQAozSlbAgZP8AOU8dAuo4OO512RCqPviyU2wbr53SMMrR6aupLyfZeQHN70eAlGPvM5jKhT90Tx/EBOMlSkvi2LrY50OnCN7VddCw6YwIKbXiKrUS9LuqvCgXAMsPXYpjqRicYgnplkZ9tdUdbnZXHTql8GDVoDrR6buhJENQ5aTzQN4gUU5dMHF8ppoNUDVFeC3zxZU06DeDHKvUG7rOgMxn1H1HQDYE2Dk6uoaTbB2pANwAzpcdrZ8EmPmaTfz3KiSaffrfdVYx31WE1VwcSicB09UY0NzZ2DVdG+5vrbVMV9sJrL8KhEXeZq/APKG3oT31sl6Z9g9jsATD0kABrMfceB6akEoQSPsHmjQKchzm8vOxT0Lj7BOSrHst9eUom6TfoPv70kICna1U2SfSg2KKuGgg9J69hQTTX7L88ud0lKr5X/2+XzZw/bLK9+f35X17u/vHxZtairF1uclkVV3NQv0mL7MtkUL1//8st/vHz16uW2w/Ey5Yz3vwm9HVvqju5CKc2WtUFvcVnVb5I6+ZZUZBZONlsJ7HKblPWnb39Had06JTwIq+O3kc5Dg32Epy50gjx3FJpegQ3g9HdvEqRNtVbIF7RPL8DIEBMN35JhUSHejhAx062oR2pepkmWlGSh7FBZPw4a1IaMvMiabT79LfKcuvblY0UYkP7msbDf7bGdVV29PngG1y2+yB7nu3qbiWMcvtljOS42jydVdZIl9GKJxcWXOIw1T7Nmg4iUw2RYyU4YrlTqQsVz0pcfZL2TgppwLhKnGAKwxz9U5pFOX+0xXeE6Exin/+TAgXdETwEQsd9dZqUuC3Eq2k9uvCLziAuGD6hO/hM9/uhENouJL3HD+AaNG6mMlCt0wwsQn/lsj+s93hJm31wVw9UNi1EqtMd7gfINKo+qr3jTbo0sWrHMHmtX429FLgyd/e6K7WuZ7HrfNwgpV+yKm6yHH8BMSYWueI8L6pEkihixzEG6lLgoyV4mSJfxq6N0uUpuAQHTfnWQMU2rAVwVR2kmSBmuxElGN98yXN0BsnkqkPH99lLY/kUN46WkYggKn6iw2KkzyYM2F7aDVjNikq56bXQbXe15NBxZt3HVat7gapclj71rIouJL1nNbJMP1K02bKJ7JB6TrKy51glunWF5FP0nBxFDiSAOZPy4GtZ4X5DO43+iTd/9UHEg4vMRChY45uGcrg8ijumrg+rTh0wTcbHfHbBRgiCiJvZBKTmMQpkHVgVCd1zAuuEK1sP1Y0S7IF5XhOqzYXFl1bXKxKHHJ01G81HCbD0W2uP9nON/NOgSFdSAw2MViuxxvs2S27Mt6Q91sZGHDhQ7HD5qQWNsP+z/UKRQP7XK50+j4HRS5rIu28dDVWuFjbCPCRh9tzIjmnnWfNw9aOi9vJz4EneMwK4hFLkcw+j9/nnW3OJcPIexJS4Yr4omBY514+fVrIJzVG5xVeEpsGTIChCxeXC/GcWajNj7n7UiGxBHm7oJZdj86fDMM4lHWVb84BH0nxw2RYEBxR5B5fbY2dcLImaxbDXsZooWbM9luuclFsylr75WNfhtidAQg0HQKrkSB6tm8nD6gLY7wULMfHbC1Wto3atDASFX5rAS6Ys3Advwzf0usHu3BF0FdiU/q5gvsixQqhMMPkIcrPYUVM7w1dpxQn+/BjHJWLSfgxa9pvmUvyNi8Lx9iiHcd3JljjvnH23snauCJjsAdlG+eImjodpQSticMDj6XAsuAnyJixlvA+Jjvy93YN+jvBnCY4RKHTgsiKXsUVWeRwLRFiUtr/+2pOT52Gy/ofLTzZcuYCiHii/6ic0yUviXUEZkw8N4sqMexXxM2S0DiDWnkvgTZ6DxWUV/f7r5P1S6fT9z/2eAfj84OCxE66FZ1YHMDdvZbgw+wHVp+uyiAB/tdmVxL5uSpu8O4ywR2cs2n3Jpm+NLHCzxu40CI1+yOJeKzHmcFbd9Xh4PvtTWnoknu+auqLcpP1VsgYNDGhkCzU4l9or9vvdZOtclpLQR1vr6M0nqrlFJTE+fl3WG7EYvMw773QFbUktmi+GbgwNul83zvxA5PtSJcBsmFTrj/Vio0Y5l6+JuJr9pKKNrUc3K8137Cs6fCh3cCZOqH43gSsh83/s8ckkBPSbPUH+eGRsbFdFwBQ7+TpKNQGUWWHh2mAx8HnOjrT3vTi+Lfr5kf7rDkKxQHCf7fXUHyDgXFQGHmKVPL380WHF+6UoctPqKpg8UzRnTVwezWhedg7OodZ/28dRkqENE3jaRFTap1B3zZZLVMNauxMEgu9nifJBEvC2WK3HySoCvjbgChx4O8exEQnIFS18ZvUEZkh47jR/dr57G0DzQ7dNYuK/Hcu8Tsm/D9gahaJ9WAtqV98UtzkETu1zqhnkIrqlELgE4zFaSNXUiv5Zivy97umvfq8rsw3x2o56Mavrq0Ksmy4BOjV+ddJddkj9Kekv30X1X7Czn8L44lDlYUXBZ31E+Eowo02d7XH+0z854RMM3l3cHdTeMS8KPTSXKYqDYhV+3iD5iE5Gy330dDSScUqkbH7fHmC+4wrXMzWyZG1aq8xzdSluwUOSO8w05G6dIdriAylejWU+xaUM0a0XQXQvFWllzHr36IPvtMMXQ8aNJfaqOldJzHOazk5NBjci3e5ynwKssodChj9LD1hPHR639QnglnguGr86YXoOYXrtg+hveUSNSkslvMoQih1PVHdlkoA2cK3BYP8kDhI35vISWvC+7SLsGQt/q9SvJxyyiqjmP9JZlm6tca32eqmOcZdIFiFDkihN2BhXLHPaWH8V7VNeoPKuAJ1FyqQPmuxIhHW6g3MnhBZU4BTGLZQ5ye3iOT3RfQWhzJU/tnZRyJ5/pdVi/C3TmMHCDGIpWI+O4XTnw6TqLyucBu77+XFcJ8dSfCAL027eSnGMAFZoveWoLcU/MPXhqhPH1gMXTCwauOg83+8ZHUR95YnqaxzjQnZftHbny/QVU7oAdp/RltXTPMX127qkKpVTqsF2THZmIgWNBG2I+O6/pq+S2Osv7ToFrW4Bw4MlVxAZrVcX+CqgCtMipyAFn71DY1z2Rbz1gCIcdpajNjSiBXAyNtwJRui8/z/uCQ2y4mIrukzvKnw9hbkMUgXM40K+FHqCsOY8a0DcHXRoIRS739SUZWRv+pg0ZBDpwKmAc7mZwhb9l6Czf4Hu8aZIsE+Q+COCIH2eSPZH9vqTC34bbVEoRudTtflGJWCrcp5fFwJJoS/QM2TcCKHai7wxP/4Y66gMRDHHYrgy81Wpiw4v1VtMEdTURwl1l6/yxL5strK8xxV7KmgI9DOHe+9Y9G6YPCOE1BnUjSiAPi2O00KBzqTqX3xuhg/SDw/pI8uYmaU95Jdkfa+g6RQXj4BlRi5F4ui8upoM/8E19koheg+x3h/70dSAVRCyzx3qB/tHgEn2q71A5anQcbhjCuYVJeYHxc+UO67chcoss/JSqLUebjYBNXMtGaJfZHfIdirM7fXcwGvR1xJllvzv4zuZZtzyZlIycFy1Q7rL+HoanwAr8MIQ7NU4fdrhsDbRvkscKpowI495K65rXYoDWlhrKQbtJqsuEKFsIZhmg2MVDhK0puQhIpU697vyLSoRk3VQudfPtHivKLwSAYpd1eYHSpiylq02uwEV+9ZVOHtMMvUf5bX0nSjAIwreFc1TiQppHFYxHK62C0aKRJDEE4eStfId3p3lCTpOSUOSKXHCqAxeJZU63ipiu5CQbap/c0XQ10gWjAmp//u5n1Wkl0bb95OQDOcQuBxwg2SInnYxeXuT3ZMWSyuSsciczsRLIxShapN//2iStIUi0inJFzgb7tv7RfYKzBLJnqKH8WoIHAUM4zAPONdjlUofTQPGjG3vvyC5dYAHlTqckfPPY2jveFuXQv2MkhdLTAjpcfyTp9zYnBE0UJT1HFwsdz9vqTGPSwds+KZm6zdYUQqYWb5stPO8whGsLyYOpBRHCvoWhzmWNhDjhfIkrxjaFVllk8rUmVO6gG+ENGnrWoxDUIwjAkY/QpseAxa0aKHaSQnQfPm4ej5u6Fg1Xcqkz5q+4ustwVWvQiyAOlOlkb4bI8j8n51LZUghDOFzFkNNhWxWn4ltYrsTFHiuhcsbxKdsAaKavztbhE+pFAdmFuwKHPXmHUpxkQO/4Ej+M42UnffAhX4VqIf1a7K9Dje2JcA4c1ptbT/MalRXEaBCAkxZABTGHBUHsowV0sghYtqcDdDqZXmHUrUNBMgpFTvoNquqjui7xt6ZGJ8X2G87b8z4wDiOw01iITGyfcNJ4ShkWz04ggD3+rwjf3olZvPpvDtQBzr3uJ92veCMi6T850AsYzzvn8Yx7hF68aMA82tIJFiXQPr16o7o6Rn4TPTgAiHYGvtBhB4j6JnpNUc40M4LvUflIWU2yegpl7pr85xxL3gximeuOWV0lJb65UT+nFQCcnZY/3Xwq8S3OFc7LbLHLWbNCvcIAmMbkUg/MH1BSNSWidFVg5yA8Wjjaym5yUqEHXvpDi5sFcMDf5JsMtdflsn1ZKnTFe45KGnIGNksqQDzboDTQNzFCeI+i6IybZIfTjoQFc9ENca7w2BWKnHQm3ttX0pjE4tW4zY2qXJDf3IDFw3FOXXUezzl5R3XdS4ceg7dzQpnTHRTh7JSQSHJjEorce6pCDJW7Y4fEpVj287gZ93f0FdmCd0Uuv9GDyp32fRCrH7ZhFlrnmPY4qOJXHsLRJ6e7hSaaGeSTwxauRupFytkbkLD36WXrjRfORumk6OmgeFzURNdWYgWKHYTHQ00jvGXvcf5dEB9ciYuqubmFlMDpsyOuy/pR9M1kv7sY+XEiGfbbTy7G6o6vVb6+UPnBaVaPq3O6V92Ry6UOmGO+hiPqwyX+p3ivMH71dBKuropLlKG0hvGbYN37/wm6HJUKHS9bLpL8VtweuQIHBWkeh/eZzb5Pxx14jUbG+KbUp2EQvEmarP5CDs4fJI1YKlyNctlLz8CXaL2lwF2zVNacR7HsmzvGeVI+gtvIUORyX7gF4sBNX5/iDc8lKmia7FzSoLkCl3uHj+I77v6TkzdgmeQVlpxpuYJ9ioAPaIMTyuFAiAKxbDUCgO1YmBRgMXmIAn31eeQB7bcgqdsvq5md/i4ujpTmcPk/G15aZkfMzhLzCPHkws+wz7oCVzqDyWela6uv1aQU2xDEUkFlgFDBHIwQelwHU8HTMxUcjsg/+xE5ltlmz7fP/aVZF58pxkU0gzDgTlqLZSb1uW9adVHnh40ZyEWTIZVvmQW4kwszfDfMFTjcdXSRZRXhHOVSF4tq/5wORg0Uu1wXVzUR3q2gHaNaFJm0j+rgfFo7Vz5vhyC8WkgeKYN0r+bUrQhQPi0NEwButxowv1lqhYFuekYAp1vEMpFPxszn9YhkxtMyUBYzmHyEsLb6Wo80pG5RvkMPX5KskXw4uCJn1eZ9QQplic0W7VNdOqt6k7xoShw/r4bHe9HX+RNSZ8IoVqAJnb8hSIdj/bag3kETxigVuvtww97bPgoRrPesx7cuXBDNGHcOZzUqx8dGwn4slzqcZvAGXd0122+5lD1FKLLH2YcT5LGNH/d05v2pz6prEeojD3YsGVnGC9hjiHwjynl2gKkDsMhWZ7NVWuWGgUhZMNkCD3yt6qREOpa6KC3nJeoMgUBAdbZobXweyYWVx+bjyWrEsH69ReVa5+dUd1a9JSK3meK4iXwlFf/EN2T9GONdlAEI/Zl2H9dmMRmX7b/uouvAwKEM3AZauMFp+5yB0W4jsDKM2p+pbfGtn73hkXT2PclKa4B1vFPsVDA4HxlT5uq2O/h7KDx3ueKfeFHpJiswRZUGs0/GKid086wouA92/L98fsDwJyTxT8fd+3vAJMsVrHx1zLEuoq2Ip2OgXyeHzrk7zLOfvU1SVF8WZS3h5EscMQ4uWe+waKQFih1U2nyDHjqxTT8IDCCXrkYW9HPeJmuKoGsSPP6KJVh5revcx1673zn+kpQ4ycEoXlHmXoPfnyeckM6kHwXmbwjPrLBMBogYURt/trhpTzUe0FFV4dscbUb3WVElAcqdfCqfTCSts6qN0Cww9vR1P5aHSev+H1vhJlIocpBTM8QKb/W3T0396aZF0eqdUGxeGWQ1ux/LOmH7HIvJY0fTV1+rnkP/Pc+aMslkPMP3/b3IOLguLaMrzqIgxtMKn6Lxua+kNLhB5fbYaaQX8knKw8F+d2HgIXWUyMHTd4/NjwlGr7x0F2B+YqO1OOFR1lyExbb4KothQgvf+GJvVfOYyeJvVus2k6kwnz7sinJYUAJesWy1K76/oCJwcdf+hDeCFNAhW6sSG2evXReXzMAh0bhjYX0M4E3TDPOQT21vivlYun02ArwkcTYdHm3+3lS1nDlRKnQwBLYGOxViuXQZx8zl9uL25hhSj7kCB/Mszr8zL/MEO61YOOcDinUdb1tyznLG7e7+ox10Fehmla5iHxSiFQI7yNWDXD3I1X8BuTqlvQ6RoWM+aHd5qa46j2wc2vujweIFGFfi8CCqGvNWfy4FY7tY5t5PCWUgPigeo1jmIivzGnVZDUSJyRQ4PSEEIiH6hEGkUbjzSrIBMZ9dZOMhpKIppKJyHtqlLuKbvrpiknUF9rsbp31BpcwhXIHDSrijUZiyQjAQMp9XI/OZvIMhQn9E4yH1NXXXfwEEBgLxCP6hC/viH+ol3pv4GH5FJ0mWNlnr9tWFehEf0knFq1kmb4uy2Z4XVaBNd0TjsUw0dedZJlfFDqciivGjO2ur2Nr9TlS+DXXafc+PNpsSVeLFxfTZYWwrelKvXLpPx8m15fGWxSKssxaP70JTVJ5npbUtiijGj3tbaZQE0PmaK3DYKrscDsJWOXx0eqjTSWLxlU7/1cGCgNEPAU3/ycFqkFQ1bVgyGDDf3bGpZhIqd8feRssC8XYlB/m3P/mXhAs+X5m3qLj7oyyaHSjzxpKn7CjycdzHRCk1fN6HwKPLHFTruIKD8LPkmUOEpplUwFYERFABWzy+4lBReR6ZuD4J9nNzt/oSaJ5o6P6pzfa0Dj8SPTg86uWAxWMNqqvOswLfJ/ltA5i72e8Ohyc5o8KVazaFNv6iwCzdJwddq8kEfuu+zKs+L3E1z2dLl425YqmLTXfd2aW7HBXFtouEL/sgT0UOOHe7srhHm77uifyYEYZw2OGK2tyIEmj/Ejz+7vVz54jZ0741ZFw+auo70mgfg+QCpS0tQ/YyHWaP/c0N3Tx7XlyD4elWisjbf3K7qqdUOdtQmtxgUfeEyt2x9xqsqREAzL6tT3Rir4rvSFiG7HdHbEdpiqpKhZMrdboNvcdEk1fl+4DKV7Pa+4fKIcu6O8a4r19FvXkWatsYFDGCK3DEJ7s9MZ/3dwUeRxj1uV76Oz0Rp1zqopZ3KUQUqIFix3khWmbdSHiFIvf+wmjlUgfR0eVtgRFLhc54Oy8ApVxSAblz3ElTlihPH0+kBNIwhEsLXb0LoiCKmNkS9z5fJQ/99gRd0amhXPxrwRg1zGdXvm6+1UVN9u48zUjHIPYWITxbOH0wtTBCuLdwReuPqbZ0Y4EhA1vUjg2GdG2xFwmasYkQni1oxiJCeLZA6sprD4bwlE9EzmOquSfZW4RAklmAx2gbJKYFeIy2QTJbgDsY1LoqwkF5+urIHzDX+XAanC1GKHIyMxBCHZOSXHS4E4pcR0xFwwXpzkZ6uQ6V+2BXYXXBdoFuSBfQBoqnJZa5YP2RlJvzAud19RWViHCjaNBUgLh4/aL0e9FMj6eUlhg9ZECLciAqBYi7vqEyj0PlDmbHmxucYSADM1fgcYbYKc4QO2dDKz3JkBXRLb4TwiKQUqSHdHGRLTfAA4rxqxsmWWmevjpiAsbsN8IPSfUdbfTUVMG49fnk/v613OPuqxum04cdLjvH5CIXgxqCAL74/wslAJXFcj8OfoNLlNZv0DcsWvZVQC6XxWO1o7Td894VGXBxrIIKaQniIDWUV0vHSf5dPhyCAN745cUKAvjhPztRo6ZlXlj7XLdKzGO5F/azb4noyCAWuu8LrU7S3yzBOwQP4bDSGqLllvif7TJtX3ElKZSCQgcX3prMpHrI8BYvUCXFxDPBukjHHb2/1dAThghpARqRGsrJBWTU8jQD0oC5XNqX6V1SIaXdGARwOQli+EabK3C3UULvgsQyd6z0iEiW9K6pmddFKruidSUX16wMSS/px48/lwMVe4y6QNsE59JxUwFi38a7hL647c0LH4t6TDXBt6MBc5B7aYp29dUdJj1OyOf2RvNdkm8+3UuHAD3oai7NBrPE54qc197hqg7PEAig9EkTaIdmniu20U4rrlPmu6NRBLzCcdyevJf8npjrD3zTntkiMheA0oe57NDMw1xD2yIW9ruD1K7Q5iuu70Amkwrd8AIJkJjP/wKMG4dXA/hzsWfwvQrIcAuQ3lcN5c790E2lWOawMwMWYnfL8Fk19KBNJpAAEY8AAPexk8PwDjqfQeUu2laKdzRciKzHCkUeOAF/LrHMQRdHOT1pyOo2890VG9BBrsDFwbGqpIRa40cXbprI3iqcULBtCeAnlqqjxIjgpOX5kEBTd0ZnLdqgwsFqKlpenzyPGPskXnQRmk2jDScC3p/LpR6YwdtxudSFkqr++vZV3U/fPoLX8h6X78MZqNtewUErQHzbAMmgAHFQGYxXs6FXsnPkBBpCsgHvJYQil41qqKpUewAAl1cYKcqngHdyOEWp2KHvRH5+BTKDsd8dHEWbfJMhussILqLMd2f5ekLj9EAStitwMh3OkKWr9wlkEUgrTQZYl1JB1KdAu8KIxlepgOvOqVSEKwBtnzkk3Rdn5rkqhiszkHXY4p9Yv2Vt2XGsXTJGH2OXFZZ5ODWuH383BEGW9t9csYBedUKRm7UMskGw35c/gT65FUTv27rnqiGrZsDik6xcWXXdkvyqTNLvZGDQ9a5Y5oCVOmxC2hVX4HgHi+DLYrHMXS0C0UqF/wKrJ9zEwmIKWEVLGlrGNgGPgeG7h9kGXpvOVu8nE/f9a5JlqI6jvbC4fPQWQ/2Z+GiVLxhj7RNx7km6GqAGJRR54jynrxsJyTW4J5B9+sxcoKQSzUbDt+X1vaPNFuegRyNfshppc4HqpsxpylEUGk6ZQ+V1SNLWX7ewib1dxRVe8e4GuqX1tii72YLWHVPogreddtTaN+X1LBR641V72GkB3ecNDggkl7pwanJz05nZBGadvi8hqNSUZpYv/AhdAeLSBn1ydlV0eomInC97mo6cezu4FO2fJ0Skxzi88Ni8DjAmFOveD86Tsj9+yZEY2BLXmxEII1/icmCbaAw5A0HlezM8Rrwpn+NWcBgVYWFU0tRkUoIOGGIfe/hB3gEPcYMEnoTOQ+JZ4JhH5NF/hTuoxM3/i+b8IJ+2OyAXyPDdzWvrHw0uIWet4bujxTP5lqFeVMC41VAu/b5KHk4fkEQGrsDJXeSErJ3bopSSdAlFHqKPZoEriwwS+yoY5zvJiMGKz6rW7wGJhB2+urhChAWkXIvAipBfF8YZQ3QtmlNXal3SOCCAJeXjIedsmFTYm20sbUr6DL9/tBbLowDC6udVYIlpnnUnNi+f9uXyw5MtZ66Ly20R2Gwxuf6YZug9ym+lGB9sgSO+c1TiQnKAFIoc7+fb2mIoLLbAycgXOdh5PM0p1rvpsxzXOMnABS6W/cTrvJ0AUvC+uA1b4gwij9WtrT3PwmaaBM8fcvG+rE5wbBJ32/hTZk6KORqDtnGzgpgUxjCTMbiNL9GH/hfEMV+05GniNKeGCqE/48fVsFCwXPOTZwvKMdLUe3SPMuk5BfPdyRpf1qAfAl9ij5HmlgERcgUOG/cOTtW780nVG/d2gIzkcynY8sePTveLqCxRKeHiCvZpaSe8dSsen4dv9lje1fUOCu7DfndyvQaeEk9fVyOSxjCWgfvZgMZnJ1PXnUnZguPie8XDj3UCgE4k6vPIHn15p0BX4d68TNAsn+twPYK13gytPw3i2W1OSH1yl5S34hleKPrp7xiO0ixGZqYRjZeir6w7D4t3bYs4pq+umOQlw353148uikwZ5HkoczEGnW3EtGHDt9WwIdGxYrDhiMaDDTV1/7XY8DJrhHCB3Ze93Nx6pQTdV/wqlKMSp5H8S0RsPvGsjCjWztn/iR67JNAcpumrEyYJiUt9ICqbc0Q2V0fDPfHx1R3aoi9Jial5KYyJOVQeHGyoPw/7to0KVzvdpyXV6H8hhuu14qDTVvsqyeOQBddb69lKMlI5Gqcuqww0JbPfHbDRW2LZCMd8djBcFmWKSDfI/4+yjNrxhDMUCOBg/ioq8WVG/8nJEFcosl0LRfs83b6rt9lxsZH2X/a7y3VYXpOFM7zo/ojqH0X5Xbwdg2GcvKXIgn5sV+OQl0/2+4ZhnFs5fUjviP6I2vji+sZUoKsRnX2nQh2UhrH5OAIqq65ViOqSTfolmZRDvHnEd6OL/X1BCuFIUEORqz2ByMttUtdYjAEvly5n7FIu0OZbhqs7cXdiPu9TsK7pDYVy1AWN5n/appAT5kUocuBumpJuCiMKKhAqGMdWPjbbNyglojerAPxcqU//Ww8tQ/95GO9W3qC82OI8IWtA2xIH593aRSNKDRBgNdtWKxr6TxEU/x7MV/9XVl+7eSSy4e+JnBZTshMQdr5Kqu9x3LNljD6KkBWWeTiKbVqaQKHM6VFWk8vJY5jP9rg+JOkdzpHMqlyBo9MsuHPyJQ4HUJy3OgaAUihy6GWTpght4H4KZQ6rvizFTaX/5HIUKm6pM8A5Iqdq+XmKUOiOF/RBkgpXKVPiyZJAIfJ0TlRHGU4Eha3/5KJdF/npw47yh5yuVyhzsOVKqVxd07i6ul5qdtfdpxxYu1yBw6yhh5oIYkmusN9dZP07vNmgXBT1w1cHzbTJidzoxbqgk/JFq1n9XJr0sOXPofJY/4b68wgArlFVVj4lkJtzqtI7SCpc2wHviXr4/LVBDdq0+ViO6pqsvfAHZiBKD2a3xDMP0zONi4iEIrcTFFFsqKlOZnCp0GWBiu/hui8u2q3sUzR8c7AuSTH6XaPzh+saH/AWybv69NUBE9rgpJ8VkTZi2RqXc7RFHLZ0F9ulSkyO+mIwnOmrw2Ioi62wFNovDmplISiVhduTg132KKIYPzqozEJO8ROnPOLHqaA5th9cDrff/o5S4eg4fnToR7ER5rT7sk/D/CXh+avWriTcbI+f3XBB5//ps8NhpN0rUzBNqljm1MPNhyRvkix7lDrJlKxGCLJDDZOCLCYPMaivPpP5WM6555xtr78Ck3djrsDNu0J2rnCS7kUpWqHaL25PknJpQNNXF3WrquTnn9NXV+PBZSXO1/TZaXxv0E3SZDWRauRQTh/NV9JgIZDVrNuTZLtL8G0e6KzQY/FxVlBWXatp7WfeZZ/oibo3X1+hLRGVoW7eAjIPnjZiWCtrr0OJ/lBsUNYFPOBPgMx3lwcLVd3VLJFAHqHISVHv9Izu/abYUaD4KYqXeI9G5lGS5/J58le+lbcxoy3tFWT2AYp9cL/W434dgvtXPe5f1bj3tCV8RD+q96iuUUnWzmjDDtsZYJweG4Qtopn2CbB1OTGzDm7Zw5HbU/UFjRKR4pR/LUqamUz1tg4oXs06o25s5GNbGhyUkcHlFYpRX3+tWlfs5/nx9u3LtBDN+v0nl94Q6QE8GWS/Oz34wOT7J/JNduSUCn8uT2KqrpJPKVUpIbxQ+arkRDQhESQhlhUPvUySwrJOn91wyRdt01d7TPD68Vk4MR5FPrmYxG+LstnG0ysldB6cbYFjHgbX65AhmmPc6GQtgaQL9+Gji41kh1MRz/hxiR1nXz5ZJb4n/eqNW4FOWRwuH68sA4KZeD2Svk8vt1XcLZa5cKYKJ1+yvC2KpiARetR+cfJ8R4k4d/03Fyx9nLTjx6OmvhP3P6DYC/cFSvEOS07cMMRPLDE+oKRqStSlQAg1yjOovEzy2vprPRrOEfv2gjq7CSpk9+kn1tj6+X+DSZNVsMImYvNnSA2KA0/+5Dx5tt0VJT2x3+DQqD4cKg9uNNRfKyu+LbINFPWW/e7mkQuFwme/uziaDNMHvyv1wRjn3Us32ReI3k1vZAdioNhBOHzHgp2v++KgKybfxfdB7RdXy9unXLy9Y7/bYyPS7C1G2Yb+JdwxCkXunHZS5Df4tikB33QFiAO3PNRlIs8u89lB82wRDI+yedWTL3LxFqiarD7LbySHgem7i6W6faVH+qB5ccuUrmYTuHzM0zjP7idEPq8cdbXnEf+n0R7dXxZNmSIpniDz2bVX8pbCfndZOHlNTroyOq7AdaTvkuoOGmr33dXAdialVpk+u+K6rEtFWJuhxBXjcVFkEL7uu4vWmqfwq2+2YDVi4fSBbsJv0C4rIuQfE7H5+FUbUcwjJXqdVI4GNn5eUuGMpYLF3fymWYHUTbn0aXtwh0ZcpOk1rsokr7a4fbwO0UwFE9aKuQ1XJbI7cMuPEcUyJ5Nvd3qSjL7DZ1e3P9hH0t9Bsq0JWqb5kn27J1LexvfogxTTjitwWovSs4Xh28r2rSg2DQ6V9451sGm05CVioCZCW5YSXKGrEVDupV8YAvLnPSajU0Y6AMqdPJA6UdizguiGxBcurwx0jKq0xwDFS56LNZxFmEbS9pnPTvNPZbZkBGG/u3NTZzqRCQqV70t1+3RzUyFhGxu+OXrCA/7vTu8Fkjq9u8T/FNYH89lhBshSbcN483Qfv+57az4ptrv2JlanoSiBXG9s/4Z3R2V6J3lkyqUOmDOU5KJL5fhxNerAcZJ+P8vJrKff47lLKZB6qAjWmNbtTkL+3DSptD6Yz/tyunp6LgtkAd0kbdT4MtJzQgCjz32xFZq1KrVfMPohn1Knrz/xLe8JmajbonyMw00iNq8X1yYUBy5aHRf1sjwOEwnIvNwvDRgOLLQ6FupPkP3UBT7C4HD5PMQwIJjtMqHTiF4pNKVXXtheK7C9/qn56aQsquoSZVkUjhKx+WxsRhQ/L1ftza0yb2KkCZ3weLlSqivPdHCLav0cTJB0HLBxsitx8bfv92XB5374ao/pK97covpvhRg4mP3u0C9ci/bX/pPD+lh1RprZYlJ0t1pXxVEq5nbjSlYlGWjimVjSYcLlKSF0COaREm2rUhTT7pvLNWJJhkR7L9l3uBJ3+7gsv/gSh5HSIDrCQLtPy8qGy7vWR0XxnF4udbJo99f09G9hXUuFP4s0O0a3OAfe7rPfnWbnx+nDLsk3UrJLrsThPFi8LbKs+CGcCcevDpjQj68430iops8O+3kqcl73xcUw/Ciuhf6TWy8IAumN2/TZxZllK4XkHr45GIOr6iRLKvGaZfx62GXVu+xRVRUpbp3AgYyTnWn+mrmj0iWWlKHlDJIdTA8CbN4bxbXBhPW680kFdmurrXZECPEYpdbYA6/OXSUl0V99O9fjcezbby/BWfSY6GOc0Uv7YXpsZlusoppyl7nmcQbSVEAWc9oF1GGM6dyxkyLfYDqfz86qj02W/f78Jskq8ZrdNPpg5iE6QHtTfn2022WYysf+IgDr5YW+nshGA/RwyWDBTroGAudqRB2Bm7TdDGP9gVhLyxN5SOwtoyNXCFVVjMGCeTEH186q+YPvaRiLsLj2zyajxdeJQ6ZaKuZQGG6tqD1gXzVLjJ0M44YezdKMMDQ73P4lt1oOAMEVN4kTjMXkA4gDZ92PoNadizLbBNPeFM7hx0WRWZ4uhBoqdZMFc1E9OfRrPGrwHQzUDxhcS7PABfqRlJvzAud11efQvf5coc1XXN/19iTdNbGxsnwxLFWx4AtjQ4EzwOOKwCfmDq/xlGIiQzyBc3mH2+8uR1ypTowzroA0kI9EbDEljoh7jQxkHr+ZhQZTGX1CneCcpi/lQUZbXP9l/LsaPlx2aeTaO4pqqkefQ2yTliDVLkkpcQnEW1xWNeW0b0mFOpDnz4abkuHOt3+D/4/sJOvCeg0ARHHHN6iqr4rvKP/9+etfXr1+/qxNxUvDyWc3z589bLO8+kvaTmOS50XdDv3353d1vfvLy5dV22L1YovTsqiKm/pFWmxfJpviJcH168tXr16izfalWL1Ha4Xll/8YsFTVhksex9zSDbc0NNDh82dic385yzfo4ffn/8+z/5dnuN/+E0mcMnDQBbp5pmK2316KFX8DGJZ27PfnmNK7Xet/oJxGMEWb/kUThUJduNdnlCfp05yRL19q0bNX+V0z+X1S/q/2vrU3bhxZ9K8E8+nei4Mzk+wOsFjMXsDPxEASe91Ocs5+achqus0TtdSrhx8D3P9+SepFSiTFp6Ru+ctM3CKLVcVisVgsVoWPQfq/dsHL/6bh5Wk/3WkP2+omv+JfCfEev9DQxKt2uzvACdeDaL3vLoiMw6jYgKt4BRGpwd6GzqusfhuMPuQgzMHGBlz70NgBndW9pANI5EqSC+6Xd1+Cl88g3uaP//jl99/0pyJPEynMD7//biIwTsj+AvKgyhLmRvIwQOZm1xFMdzPdu5Uyl+VbgJRtepKVMTg2kOgoHgc0luB+pMGepDeJRbipw8IXw8wcmFN5mmCj1VKLNKVNqZ1HEwYhB/s5nOgP5lrSStu2EQeqYOg7TbnRELw09wELMB14RgOjev/622/aQNkQDlXxU54iZGnn5NHu2/QgG11/eqqc9wNLWs3yrB8AOp9kXOg8gn+C5gy3hOluE4pRg4jJxp3+/u7qv9Y9Zq3xg+CkiDf/8Y6swr+/e4+YpIsOXcTHOUJ/NUEIg8VZ0qp4ru7CcIPZX979P2kHFgtMhqW+bAD6ouiDMySNdYf6wq+EbgnrfVC9vzeZqIqBZ0WEr2AHtg9t8N9i+O8CrEByRnICyYDrWoiXUbC92iHU62Qvjg+at7mV4enwWGRgwY5uZpULvsyFeAuy0kO7gEVpses1PRvN+5vBLlcz24vxVwN3aAReZbgOyE1UbGEskGc1t+NdUoTiNdGHoSzKN03Qe/WqYgFiLHYKy1xrNgqD4nIS1ZHDR8LNkzJI30Ljd0RQX7v0IVhpGfqiXh8ZtrcGIsrSxERbLGC9OlipHIsqBaC+x7SR3rvg5eIF7PZWHlwEpDJZyvShXItFZaeoi4Pa+BBL9VgKlzkcMyVrs5MlUbSE1TC5EaZ0m6ohI02hGQeedCenB3xdcR1/SnBKza3VIiB748cCZDmy4L4nuRUws0MNzzMZpDgSApCcXSUcXFIjh3he9fh9EW8cQTI+QmopiJM4ewZS59GxqAlMrYElRXrNRD18LXb3IL1+wAsns5F4z+6AJgq2vi49fumi8wiamceWUna1Z2IK1dwAamftk/0+TZ7sthA2L6JYM6r5FZlK41rAtGV4ScJbVjIuByqI7xYSkA8Q80F3kppqClLHrLY8krS1roGK4qhcwb1M0l2Q69x+imGtgih3jefJZgfjs2S3o6JbLAMDnZwDTx4eYASRlNuxzv4UWJW4ZUDIFIPpKbPK++zroClF2U6EPgdZLtmENHbCNQOqg9l7fczke48OYg0koxvyLP+cbGHs6nyA4BG5RhpfCFKX6zVAA/qQWirqIG2nV3HDcaQGMkGi34edMR+0g0kxF33AvUStddd0f45rKN1Lfv1ZwYEDQWwWhtCupxJIBxuDEN4aYnlCskSKgWWvfk5hmj+eB62vw2x5NmDYtamCwUcSWetkX/8e5CVfVgjjIrPblu8QE3CUrqPAf9ZpbIcaXsbIaCt232EGcxe6GtvDJ1tX9lUN8Rw8wRDQTnK7txP9fB16p9keANGhVu0ZQf9ZlR46fQjq+CgfFRub5/hPiuJNfuIXJ8L93Hh1OQPGPbAaQRLsuYaw0DEjNQggbTparWwEHtzgt4RxaHBx0+lug8kZ9e7BiqGVFnjvEtgHJ8D+Bfc3SZYHES8Mz+xW6RHt2nxby2z1Bi8OoVn4GtXdg+UqWILOHzzNmESekovArLIWrG8UMyeBC8/JZ4CZdZV5CFi9e0wBUIf/F134aP2AFIYd2EbXofXjK2TrH2jgqsPbVKFuLl26BgpL/fESvcstQdP4s0kmVHT39yk6ow07xgw8C4cQGn4aJVtsfSxBfo2em84woEntWKb4tBgJP3geDuZTgwZJqkCha0dp0ZQYuQC1Kou4nrp59FzzKNhmV3GFpVXgIi9XgnFGDTfiQKy16p7RbhOuYhIqWGf0pZmJZH1Nctcg2zyCtq/hZxmfJkt2IXtBOLu8F7bIOjQtxzi53lTJSBewH1ek2t8GlCVJyBvepribHUR8rYBaI5bDJ7gpgih6tREcAg5G3RweLiMUBq1jk5fC/dolLrxJqLVrmM4DZGrZrIpX2UmTuwDtGoarc/fbLsFYPvVDJWLcOTGAbgOc4GRV7BxZP07g1cDukN6JOsRa4ucKpKsUQw53/9XPwvkSCdr86mgTy/XjI6TZIJSCD3LIcXx4HPAq+wgf8rMgtTqE1zDsjYdb8O8CpuA6fwTpDZNh3jRPGoHX2iFyxfr+NwNlVeDJyWGI7ZKTzaYzpBX6V9l58hxHSWDntapg2E3Ntzgql28NzoqyL/Ut1vVDD57RK40KyMXLHqZklZwHryKISh6GCiCJ8iMA7aX7U5CtAmTDABezykIyuBfu9Le5GEaElSE7KQC01WdCFwPoDry4irG+BWGRppb3Xg2Qs9cwAqXasNN3NLwbkMLEcpk2EMnmT8BaLawrcl94EeMuVvKK01m4eTWNlCzJMh1ENbSzR5ySu/ECgBDugggnYEb/ykgm5fd/Q0dmnHgFbZMGqDsJ0L/KLjIrFlJJFO2EBJk62PMdP6ElhoAhQ/7RVu6QSRf+/GcRVN4HC01enqYIvJOnAKK+zGHd4t6Gi6PR7gVjZ/R+Tp5LWqtwcrtpQNY/fHglJ/DLJK3xOwVUmg0TsKdB+JMkT8X50i3fl+DDHYZ3VfIQHS6aY6mVSUG2LzQzcFfsXExMCS94cQWvhrHKwd4eDsl9nyaR7TXRJ7gBNWYVSOsQE7CpIELg3t5Gaxk3Pi1eT4s8T0SpkVT1Am79A2aPEcxye4CVwooAWnxo92FcQkZObnSoIKBgaOWqYgA43x+vo43fAaqz1Bm5G/c0xmqP4ASRMSFKt1PUGM1NFY7sd3DHRMOubq4cQa49cRdxDtLMWhYrFc1ABZ4FqFLjo46JDkV3EJRr12pjQxYCyPKTPE/hfZGDs2R3D2NyrPMqrAj/unpdVlWvs6HiB4DbR3/Llz2LOQf/A248Qv/klzfNruRa5zSA3SocVxcq/AgjlUwmdQG/GsC6etO8rt8eVy2s8kBrvpO2wu+DxTvr5hZ8BDz/wu3fPtN2SqDZ628T7N7bu+rnkhWEK8fwCaSvePXr+yHZ3jZeyPoY8y2G3YgDBTzY3lbPZMhg2V2QwocH0S0SHb9u8KCYxPBeP1yncAtj4yDgFoANvadBBiqz09pL2MD6AoKsSAGeDSnz9F9jN0Oc7OiwNdfmUzMM/gc7lMF1xGkRbyJQFo7h+MRtlUsJ/gakV0h5ufDRMgAxG1zCWz0mpdsXWTN2l1AwdhHbiixlNlJWHSnlULTaFF9CLNpg+JRJhu6agfbXeN8yLIghIs4ygKiped6D5vwIUA9lrvMUbwHmGH5bXclnaNfbJzH95svIJ9eD4uqNSj1JJE6GrG3LxAwNHGxcaVwDqj/tfKvpZRfUKU5VY7ehu471O01yNL2uoV684DkJos8w/ilX+AaVKoPNlmsrmdGPoa3y1zbY0ezuCwaDNyLah4FqDboJj30LQq2dRySO3c19O9/IVHEx1B1tzmc4RnkF/xRJrnZEbnaXrEAEwrwL2KA6SQ3imr3/dZWQlRzAboN4O3ARaSAfDuPI3TraZxit6+bxree0kq68znN21D0ERZR/RwfVL2bpTdRfTtUH4eO3CytST2EcpE2ACGLpPfnBbFNDs0sHdgh0lt0tlomjg7mnMnoFnOAidPGQxfsXk1D2r5ZvgLO7NIgz2I1fVdimm1W6poBY3Q3J1701SiYv3b+ADQzw4jF6bs/29pBZjh5gCWrnPMiDYW3j4PXrklQ597mq+lWgZS4Jf8eEOWQ3od9XLUGYvPiLnB+fqUl5cyC4dSC8HfoHcDu8Q//bmfjYzsRWzhft29vqJmq3kEpPNdkGUUFNT1uTpsv32yIC4sAvs6RDe+D9ZrVK/8lP+6frIayfzbkAdguyHClcohUlxW91b2xroDfCl9tGU0UBDl6xOJRv31wDrzks2OEsoVfbjxWHL17yNKDPkD68gHRs3xL03aDJ/7u+xX+WREn6CbxwK8vbAq/2988JVkuO4+xc2Q5XWeW3VrY4dT0cZXAZjixbgpBO7OSoIvlMkeh0t0LF0XPl0GcYqThGzOS84CPUxE9eMhghMW/ePFmdtPAT17vHYncfU8UljDPH2VU2n1Wi77mcysz1dSMgpbwsQX23xJsqz7KvleZs+G6ZC6AGQ4wbO1hXOAa7dFF5OcJXsrekiMeJLQVB7JXK85amp12d6OwSKciiTeY1Mz+bruwu7QZmYvml2W1wYc70fpNjakJJqoMHGBJ0mz3kTaK9SzSf8aWTyyADnRSanX2Cr65Km1D88Ew9yrOOhBCfvvw5RNRL4EjYuYS1wafflVh6qJdjWhZi6A2B/ntiVwfK8rEz46m0vAixlP4lyP0yREtVEdtuDy6tocsgBPkqSXNqEBPaCZw6OucTtCu1QugpdwX8g4ZnTddMuwu2S11/1nFetqlrEO/brCuDoqs7s9+DFAYxNyXVEmbcQ/J5fub3cRLaW4a5+cxBZp7VSwW6cVIv/bcS3rKLHFISk5Msg9sYrcA6wNFDdtC3XEQd1w/JK2x3ZzWt06G9BPyvnZva8M5yNxM78rrIrx8ISEKkD4OKloclbLA+4mjwf2+iIg16MmQdRCO++3YTQnPQ4TkezM0lLAFbpVv/v2Ha+ktAQhJbrbymRcDqtWJ3LGOsXbnbcN4P9BNVsMG6ekhZ1cfK69Ael5rU7cqccngXM4IQDWfj1NUBi1r8XvLEuPMTOi8YOY47ztWONxN3XB/Wxcs+SfNqiZoEnRsvyirOfQXezFNjN8qkW6jlzC9h1sUybz9XNCzLi++Rdo6uvDt6HmvyOMI1uaVfbfM/aBrpYnbuawoQD+EIAxmGRvrbQckNMcf81QSD0wY6qjGu7w2yPcRWt+SL0Ztd+o2VJpeRoo8uT7ljW+WKtu+bsl2Ysh15qZhU1hApeJfHucPQ/m2p3+PX9DWtHwvYDFTE8N8FgATkAxyKO9d+dJQ1FYy/pVZvuzhgrHwaNTyXaQGx3xCUGexdPbLjpuIzzuUcZ65cQm/Z/I4ymx9HbIiq0ceg7lf9A/9uxY8WTN/CsUSnW/zJwErCK/U7SHnLy7aU0CNOlBQlW4Nlq7wJNnUQl7ALThwuIU8Nomboi9O4qDy+Y3q7CKKxocVvjc+zIAqLiHCiTCTjwYK8TNJid0NK3x7/4rlL9jDUl7mqm91z00puzSXewaW1mwu6q5uTzSYFmftaE9KMAU4te5dJ3nSjqZ3dlpC1S4RzCYuXUKu/fKpubbz6gS9jPN1avg9WjZVd15+DLMdaH8ei2hkEZT0IHbO2U6zUJNqu3LIyRQ4YeJggeNaoaMVxH1X8tbyCqKAIpM4QWpkvzeCcOChA7/WtsIXofAV1NHz019sYFrMnfEyTYm+4MVR9+b5pV09x7atLOg6J+lqZDVYqjquEDVQSzyg/em3rKJraiZib+BwPLrlXy4Yl6MWD0EIGUn24gsq7YRRne3fJAWHFthFOqF+RGb2UzKefg3hbGN13tD3tjoa8OhNmLh6SddMNKFyD1g0kJ9ktXVRkZ8uxW4GaZ0nrsjZHssOhLFYx4yf7fZo8gU0F60zyxFdtX0ty1yAdFt1wutcstNKN8tZSV3c+KfJHrLHL3Dy3IEQMW8J2M7kn8mJHJTx2Uqj7iorjcQi2sjMdQ7/GgneX/ARulg8BdxKGIMvcAUV/PkE0wVYVSZRXZGXIH//SI4Ty852YzT2GJw9rm+6afjL9UtWkqe4aDdJRd/rbXXNUJVeMsekBsAldID2R6ZgXXUwMyXIBqyp54xBUGfTguJxSLZ1nRZqCOHw9M6tVzQNcArwNctWQkL8Zr8q74KXa1jh3gdrh1ILkTebKbFXc52gtRFdxGCFUvUXIMINdvIwz2B0erCkhNhKFzKDjUFrphnEorAYblTI0kMZi1R+MUWNI8UO8NwTRJQC+eSoe2TeDxSP75nYF3005NCIn3gXRpryPqkcCzcBpEAWxxzjFkllYQd0iajZUlgWPQ3kbAh09EBFg47nk4i14DtLNTYJ27OwHSAFaK3aezbNHEP5MivbJlWsPT28AZynTaqtG4ELXdTo+PMAIWpeabo4xeyc0Ep8qPpfh0phkWZ6h+WcNL6NpR1BwbzcTgVFyZmn36LMsJp79BBsR66wxPXt6+uAM2MXLHqZl4HYSt1lAHcL9bxC4oZ2Wy3OIlFt+DogcmoskBeYkJBvcpyTaOJqrPnCHgkABPw3in87Ohh24zpYYDffqzDXIqoKva7BX94GjDanS0MQoqO6D3KyJAlmvKfyTrDTyIi4I2SokXsA7EzfRALcgo1IpWmqjPb78dM+cPmCHWKODdGMTuUf9psB9M+DaP3wTQFc3u/XRl31PZMfTCiQ+fKFFuC9y6sWSY+fdOYhAN/HBMqKF6OPCLdgFMBaXfVBKIB7gN9DVaf1rkjfVTayu+8MQ7PO7R4gwDdDP5IrwUxBvrp90jFz19AfV+flbhg4NnyCSg2WUTmycj/o5yJueVtchpKv+6FU3uxc45ktUWa4+wgdyxFiaXNV0689s29Nqcr9lYPMD5o+G8tXpbo2Ky1pT00ryEqS3Nr8oETCqYyyCY3NjW8+D/W2dZ7fkVVajSko7BJZppmpg6Ci5d3jAuUXE7nGWEmeWZQPRXVjTCsT4HOAKwxKcO/S+gCyjKp1ZpzCvZ4TYkpau7BE0Y7Owl6AaG2KdRhlNaAXeTJsGxUXuEFxuheQP8X0Z3Azk/e53DGpGocT3XXV9DCs3c98sY0fzzTtv94/u7x3rNHrWjwiushqUE6vpMxLxuE0+aPXCBGv9H36rxRXxJgLnQR64ClrGGvqM5CTytSLEhdx0rzVIEB4Nz4Ofq3QoJ8soTzKhVVGyeECGVVUuCc5M6juquZvDtH95QR6vyaPcS45bBUsTCJ0YLyODNPMe9OTyzOn2roZ296wtPY9rHV+Reg36R7gvb7iPf1lOuAfcpUH4E8Zbh/e3JF7Rrw1GLmGBq1vi2pxxBG6M/ateHUvx6DT0Gr1EKns6uALsO9i9e1KUReJHEEUgX5Axw3v1pyIQZbd1s0wPO0Oin71DyUHh13gqwfsO5mdGucHPDJF0THQHaiW8JmmtbkGQtd4qA3+AwMC1rjx/stnBWBDiqFksTeNImBdpjGvTgmWkh3bwatrRrji5EnVxwVEupsskLQXJjWOlEkdAHK0KjmUToKqBe1rPjTvZdOyugvPg4QE7q9yAc6hceLyltIiLx+Hl+6y7pDTvBH618R0II+QwXD0mJNL2LEiXc86x18k3QVodDK1c/eU1h1lIEd3XxsKkJcA+lGjyXWbiW3x/DxnR0gUpLh/nszyIiz364HRg7xHqEpQgL4ZMllZNzdEJXnL0027v5mkNDgH7dwFTu5tv4sfE7asV7gLmVXYXvFy8AIpSEzAIyBmazm2SdguKmaodXHEvTSJ7Re6qcvJVRsIXNK6webELhvkUzVXAYood9yg32Ln7IPzmuzdRTW9le92udA2XT1ik+CV49TBsQc7zLun6S6sPwbkfe5aBIyzVS5CVs9cwAqWCs5oeDOYGpDARh/ipmSb4gplAs4okUU1GPZotwXvuq4tGDHMYRIaXQmzv2b9bJBxHP33GBVePfxlS5HJsaIXp7QE47JtP1RQYc5RX/OsSZLZMiFAldh+6JnRvjZvUfbmIcWMNLaw8/wvRU4jMz+AJRAaVQZLtmnT9j3dX2TfyXuvv7y7xmEY5jJM0V7mg7l7zKEHHdT6cPincq1SZ/WBQZdajmlbL5rIF39Kev5ld+7+bXLQ9gDQFqQ/YTt3ESKi3/chK9fVAunMXhKXb4VOe7/mJcTqKWZd93zL+O2C9gsnq22qdAHEJytU4I7ubxPAOjii65yStmFg62dPxC4MXb+Rode90j7bbGHHvDKGxtatb5N9lfxJGy6nMU1Jv59EpYXgR59qKuU0ik/sDpreV6rxCujfyofeQBfQmbTORtlVUbJ0DdXJPaZBrWj3tEp5fGC4qSMGFICISqhq9jiUGATY6cHQnco0AOThpMInFLEt16gbIKQvx3SPYge9BCjGoJUgwIVhH9JRqrWtqVRWYXPHp+kW0ofqTpOoNyfFLkJctdMihY7QtZ5G2W5cDBd/9DjqzDAT8MklDgHBE/z+JIuwxszrgfEqyfuS99Zucz8k2GaoBPGr41qd8F50mG2oPtgtmTeIcrYP6MfBXkD8n6U/XU32Twl2QvpKVWJdQMwkI5kGxDE4mIC9eEJXxFpD01rb48YFpoKkeJFdBf9O6SCx/Nzkli8sDsrD1I418FgisdMnnBAPQYIr6zQnSyTs0ZVT6cUfgHVbidlIq3pVankuMPYfCBCeSvyAlu9zvj6QYWPMgMnNgbxCIX4vdeblqrIKdWuxI9JQr7FqI5yBOdjAOkDB7iyvtDHlbtGvetfn+JSB3CUvYT+buUvN3TguRykQydBdkPxcU+kuTbZA+g+lt6RG/LWK6sMdAgZMGhy9B+AhjgP9eN0CsMiFQEI0cZ3KMfnPgQyPRqnapIwiA9SWMia1gNXU1EA/ovNePMVkVYQjAxtEb9os0TXrxGqa5Pbf40v0GoPO9MOWqDiQ3CTeMNOQSVKPC0Up/5Z5EMJCbm0YhSkl88bLHMsG5qLc2Zsv4VANFSISH1XZ/cxSEqTD6V/CSI+W7rrpbbQlItvbXMaMMTMzkCiVTNSmiyCAS7yr7BDdoZVgZ/UWMlFm123m4y2bqaS9B4zAEi8vHuQlYlYT22Cc+cX7A9B+n888CFGBDaoyc5DkS6qW86KII1z8BMJ2trEhMFrJmsPeOlkmzrCf9usPWu+AlNIkZKnvZaGo6uTyi5R7GQfpqZIEO6hKTFxJf0ObFNxBsAYMNDCqh0Oc729tDSmJK8JegJG5SmKSWCVcu02Tn3KC+S5yDvAX76FUPrpKV3itSbQvxNAxdg1wV9/8DQnkqNBPtie9m3dzMurxRWKHVcJdCy6fDCIgbXwOxPULD8qZsb7utGMSbL0FcBFH06sHSojFdgu7k1q1j98ff9a9Sqyu64U2dha1UIJmqP+MM35skFTm91G7/MjQLmsQqWotZNvgi1QRweexZZZHNwQeRfQ4egiLKkeYjEkhd/rnMLBTs9gHcxktYkLw1YxiByt8uzYAp7ZHjX7v7P3pXjuw7sEMabRkR417OYodjC39JNoAkf3PuC/8cZHkJPQXDi1zRyi4tlvLR5ADKBq+yZ29zu3hzIjFux9JHrgxdzq1O4zB8z3ET6TG7hfXBIay/aMJS1t1fwXP2GWB1iMSY8igvQIVzKXdaZVhwclC6KnrOoh5y5SGxLhzB3ohZGrv2GsLtyd6sHowW2z4YHK1/JCmusOX1BR4OhVuBvPz9WBabpr2kVupL+QbMyY6pkkeRnrr1KkRA7Tw5GILuIpAhYXJ5fpWhxcS8bTS6AUy2MAyi6z0WqfYa/riikrH1in4KsZ1pEsCkpSOOSkFUQmuQurTuaLXQMBTjOBn8yOu0gBGun1kD6mzJJhnRuktFIaOP+QteW40pfHGgyT8ajrsnRZdJWuyWZuB6M2snzxZJptM27+wehvNPUXyTwicEsgkVXYDU2lfNwRfV5jLK9rYryJuYo0H3tbMhFdxTfzV4ZoiLcLhxd+P6G4FlnYwqmdnp60mRP9pFeVLAbkEI95AK9x75YKzhgg+yIgV1pebjVxODB0oTi8tvhttbPBWK7+mMX8/6Kq9QCdg5ktk4W4gB9SZj48rY1W6fpPj8/ACXkbXHi4BdJvgg5Qc0LA+3Lt6XuoBjHZZeStwtwNfWGzpS1+g26ifcWx1Jgp/Apn/pybqO7e7C0JK4hCDa4L/cP6yvJegsiR/gtkgDnvfW6JLz4iVPA3oKLRPIRMUubh6OO4B4C7Iiyq/ih95lvFkd3fI1H8LO6ulv099HIpfVaxwu/e29ylS0bFqfvpZQ2umgs4XfIcEwOQEmRRoC0zyCLHolLDl6bMZ2Qz7yNzBbdr7v46tKKqer9YZK8lS95H5o/WBOK6erveFLhvgUZPK3PX81zO1zZfUmu4SxylNX6r4EeJokouhRJSWP5siz4+DiBVtA52AfJYupgVadMIxqNz9IH4RNZ/W7SALk1kJpZcre1FcKHjfJ4KcSO24AVyGvpDZMXOLjLg3ibAdJngB7rvIgWvnd0doo3RuDLzgNeLoq7sujrWvAGtGhJvNGwCs54U1RdxGViVcCfAJfqJyBhs8dtB5MaO5Tb76hUXxDJjmN8OLH/6ood776ldMpaO8BafIEEVd8Jmy4yiq9WMuv+QpzsNGXa8mV12s6RwRXCJHwubLh0Z9YabvyUNWCVvq+nLmqHJtt1w8PGbB66UcC/G0AnAZ5+LiCf1oZIzdoRZe52y0WirPt9SzZ7cmdsp6xoX3qJFfN/4L7EwTONp47AkGsE/OpvJefBuHPqxjNTvhzcTFZ9tEt6M9NERpESjYdD7xU8BjhFEjyHwKSHz99e9ZoWzEGgmfdA+EcrqPPEE+3Sfr6JgALFYBKYb7N/0LnvzohVWKwhOlvTIT3TiyUDzZQfGv3NMmyFYiit+kdYXo14gzjoqqAeRw8l7mVlBLhxcW6hbGuPUn4d6c5yWnAzJnPFZImTw/brdftOfkH3GxB/q8kHoSsn20Z5gPVa0x8lNyiMKoz0/Q2KRLuqJKMLOuDKh0dGOZvIe6SkzAyE3IKgLtAKQwaF5o5KsVHiDJI/Il72VU/C1KEMuan4fCo55oGYlSBrfT16t7iKCffceLr9aKrVo8koMTb2/wbkFb31fjvngvVzDVvo19LefGhY3UQ0Ho4y/G5gy2MrR/5o7l/vnjZBwhNqxuqr8llEkXJsxUM8PwDxhs7IFfhcEEG7QWSvw6suvfaMDGeZ0k0+FjcIOpkR6UVdwX1LMvOoiAbjC/XhevAyiCLaXpLg6Dhx9qo/fDVLZSJuVGDWPftDm77aihO82EzhRnL7MZhbWlR0OjrYdD29BFgj3Q+uZusPdUmU1nDUJ3KeiyjuWQG0+Mk1dXu+ojGX1Oc2q4+Z5O+fBpjRunxDnVWezRoWlFsd5+zW7sbR5jYaqhDnVMafaObbk8zWUGvb4eCrclciudG1hwNZjSbI7NSiECNvxEOZWc/FcdLO6H+/22CDPQRzCJ6vEO1jXo0mKFRdfeyXtFR8nuCq35P6l9TYIbtIkOEnsTZs0lwDt3XxyTgzJxnyY48MTpW/lf0cb1qhq+QS4AqmXaUKkihSTDzi7Y9fQjHaZRslyIcruYS8+wmyQziANuePuaS5GbD4JHKNdpED2I2GyoN8ou1Xa1QIPx1H5xbGVu34AmC508g2j8UUWzoJjqIqWQINjZO6+5WqPwIsorjvif22Gdzquhod3t/OU/OtgucGKp9FmP0WrSUzP8GGSkK6wDU10QTkkjWT7IsCSGZ2foiIME5rsr3/LcgI6kH1nXW9o7wX8Sbd/j4gdM3Vw0qjFYgevjP9scvRZTDfQRDhMI/fnn/S3fJXMdl4sB3J+TREPY1ZmGw6bMDkbER4sDBnMWH24DF7f/0hkTLGKRl+amzJM7yNEDs7q95GIdwH0RdfnQaKqoHTGkDsvvlHOxBjGMLZHSrjEvn4u+P3wzTmYEhfvzxKyVUCrIG/ySBogS3AxI0Gu2+lLFfj0PEGJoOQr4YF0kb/YD/rBK4dI2zdpIZjxY9v+yHUQSOizqDlKCFF8ET+/r8CB+fNpWBaUwnE0Iu+rQ08lxr3LkvGwqmvfq4QHmUs3JuMtnFdjq5TKLoMPZbjCkrXeSHg99VCRkHsZG2Pu51H2ndmfIoJSWOPQzqn/2oH9VZdCAtFSFKegY1n0xeVnmQAxLJG6PT4xm+0+7F2FCmVvWdsbLq30aRHQZfBo/OFz8GFY8/foSIJUfJhiqRm0yU6jgwh0L023/+5/vezLWQ6ug+GlLz26ELADd0ceZTLxFa+zU8O2HQX6IjigSD3OTn+nWTDma94ri9OKd57kl+pE2mGz3MQ8WznqkJHkGqpLHSgiHlUUATSdZdkG5B93bdSEEcrVzpzPEEYiWOoR9bqk5hhPO18d8PGMnUwPalpfcOVxqUxmKZP700kBTmcb7mEzHTzatCmotK8+1oNq+aIp3NazK5qqOTDsOpV2PL4ND+ePDOvYaUw3DwQfKEY/0FbGCAn+7JxIdpRE8f+0Fro6oQYF139W9ehEFMqh+BqKlRGYrGbTqZqF5rVKLBxd9sBn15gBmEWUQ6n/x4gjVm2FaYWHpURqzazkacuK+5epPJm8WliBOPQ/MRpzZccBorun5S6lAvDR2m6sfCjPHb/HjoOoX/Flo0/RNrk+ap5MkesRwnHKjQh8POwLovM4/tj6Mol94DdB4unmWrIXkE4ZI/uBeMKX+2O7mYDXgGtdTFEYuZ1pRPIWaSxAwjiRnzin68DY3Jl8Ac5JgPh76xidNCCMab3+ZGk3BQ+5tQxPgNjmaf05a5Oe51rNQNbHdGymQB0qctCVNJ4EA2m8mksDppHpTa43kvet+ORtnpeCrmqOcaCRtQcdP7pKaXrxG9UibidSPLDjSqdNX/uAX/LmAKcAoD8Y3/nHQXhTAXHeb70egwmiodPTa1P70OjL1+uE7hFsYjBMhqqMHDC5DVUTYd1k8uCkgJwCeQvt7hgl7CZU43YtY382HmEiEmdXqxoHGbWiZOi3gTAZyA6CTPU3hf5KAs9bluvwzZO1RLzgTTX8e8lxNSJkey19inlSTisVcZFdOqgkPbez6iW8nqwVwZmy6YwzPRzeScmc4ZiZlTARvcLd+kRD7oTOSjuR0S5Aqf14VfB2meZB3RdV+XIiWjvWo8G7k6mG1tQqEaX1npRcPMQ1UxHnxJMvx5XtpwiOBJ2hFe2fAoUxmW7jAr2TsYlTYDYRtftenHQ8xDva32IIQPMCSfmrPt4QgbH38eXqKWRyKAAvIOQRT5qF+Ten1rFbrojAXD8qAmCL6yokhoVcCybugnS4K5CNmmUJERqzI+H8BMlas6tb4FZ5G62VrYJlfYMgqmlnmqPs5gdNFsrIgWZ+6TJOrrkVgLFEkaFsJ0ESA84VILLBLMKWcylyBiqhM+pZTxy4qNK2jfgxQGcd7o1rNkdw9j0nDyiAAJbjzRkjY/pjgCGaEqWMwpxEAmfwdzNp+9oI6/79rK6NRHdQXx/GcRkLIY32IollGmES0L7IejVI9iBs1a9Gi05yZ/h6sTVUTyaLXfIam8zjE7W4HmNDLslew15Mz62J5IMV1S7OhmPsVyVP+jhEgNEZ2N37HrQ1clz6d0jCrFCjiOJL2akuRFjo1kuEV8amkWbfYHrXbnZwdMoXBtDILZaNsuEd+DqGiEVE6hH7kYV3IJuSpoVg19yrCRPPkR5ZJaDXnuApharCc/yI/48mmiI/nhnLvPk+c4SoLNdMlMawxYZ3rz41GkM23IURlrTvlM16tgt48AH3/TSZydltCanhE1BMv8yWThDoIU0YeLUwmrWdoWAyUy4cW6abBncKF+PYq6ny09KoPNotZnK1bzd/JOI0QjOnL15Gdq1+1lkhY7kr7fdT0I8bbUjMlAon49+EIOLS06OmRaIbhL9jAcWwrIoH0xqH4+DjkoiTkcQViT/35Mk2IvlAKqSW/yqp9H2U3IgH0UPImOiDEehUdpoBavOagQDt76M+ZTXiZQOupzOa7GIc1nYHyI0LaZOp8iNLr1ojmvo9ovBK3JhOgreM5IGo2DqD9VY8vg0P548PWnGlJUxpq8/lRTIZE+xh9UVddBF5HMVXSAFjWXLB3bevoCjB1hGyrnYOIDXJDQjeUWtBY83GE64bt4yUEaB9FJkT9iiOXDuFsQJunmMEqByihg8JI3PHgNKCXvILwM1+nGS1FimbeJjMkAqX459FrEJRkqA7EMn3j2D0LljCs0YyoRdamZXFvUGa+/Zeik8AkibNLXNT9V90wzm9OYc/FhGxxNbnOGLJVxJ09uzpU1zlrR1xELEDF1nTKVfBEMp934iKdqzhLVYNnHwKOLajTZaQk5JIGZf1jHNGIzYliHnuBMHdbxET7kZwE62t8UafgYZGDzA+aPAhrMp3Eg/rDGggHW/uhPk4yV+r6hRUkmuFMxuYgwtg6fINMp9aRreJhz8RnB6NGSAEfSpm331B3nJWvf6KUwU2toTqI2mo1kLGfMjE5rNn1NcjB/Oxtj2ceg/PWwZaglZP529i14RtJ+kyAAWa2cDsI/yUGcQYf7/eB9lzyqDsKTyZMzp5vggDk+H3EZTQ2ZygozLdPd2q4e4R6Xhpz1TlYjyabYbX48bAFq6Jj/NlajSjxGfLxNZ82z5PQcDuwHPymOdSbWkRApuyWaDtNe0mI09m7v6Ae2qLdr+l86TJ9MBH4EEVrgB2UPMygziHS+HLwNzNJzENYvK09j2LvTisNoJoq+LEx93M6LNMaFz4GHZ6eeDtoUyp0zE/Pl4BULS89BKJYmrnp8c0VLNg/PaNESvA73pzw0JwSRsyDNqTLOPkuOD4hJF6POWaf78XhKg/doUxlzBqXAeyJ0ELvUHMRszL3KSLom3656sjX/4J45CNaIoT5GcjV1xM/ZIwh/JkU3x2bvZ7EG67VkVFn/6zhPzrhkyVHzmUVzgJ9+BFJAoZK663ad8NgXFimieHsTvJI7jasYYriOrjZk117swJ3zW/fjYTsDevQojUnNxGzko/YUySlyNc/eHAdcoqSo+XVLmQmIW6HU8VV1+04mn1gMntBMfE62a+rfeCLFnoZOO8bj0P02ikBSo4qw8eW3kPHMj9zRRKkM10FxFqJ2ECfP6aRqzPOmrjhNftR0Lz/+kst2Rec4ROZgROXk4QFG6BewHulBfjMgC6j99dCd5y0pSlvPxLf9J2HUSQbST3Ay06w0DeqdrScaJeWHch4Y680nMsjxgftO5+lERj5OSQ13Qfp68RI+BvEW3KIVcYYsehCHrxLxqhqwolX/qK5lCAqsu7L8xZNM8OjyIw8lHWqeSeEEzEM0yB9vMjGBTDCcn04YwkewKSJwF2Q/a68P/Zs4hpVuxEwo82GcO5I+EUKU/Hp4xKzzJHUcylSGpftNJnv/LEABNhe7AEYneR6Ej8Q1fQklto9+hRYvAsfFvFNfitvi4Au/8OlSGRjP68xEbbJqUfOSn7HrSJnL0CyKSlHor0siQnk+LKYRky2R+TDKVkkhL5I3T1ImZpV3KVMajsZvDrJF6SkhKXYTu4jNUVcMJtNtVM/p7oGRSXhByjmu8T9lFbir72Vr9k6V/TLOXW+DeA8VPh6ubnQZWkeQL4oildEq/KatMrEq7rMwhWWx95GLHNFj94tGsF8P/sajT5OOv3LCKp3wKcjBF5DhpCfryzTZjScl7OCdQE/208HLR4cgpVMbNRlzEZC75E08ZiIe7VRMJhxXu32S5gi1BxgBr75LsZQwODAQO1+OwunI0qThboTxdlKH48XL9KLC4NCpnXB8osLSdHCiggaKkjJimkuD/bx6K9jBos9BiP7ox+GjPfdOhI2iS2XMCr/JBO00CH9exas8CX96PSd5ETMB8gxKwjYHby+JKDuIQ5dI7ub/Im5+Qjfi+zgbmZv6mdwXEBf4TV8V7Ib/lNy6xQUnsoz+eRRhY3Hu4UJ/8nQ31xA8gnR1SFIZkXSZzDGQRNH3JEfGYiVTeHpaoMSV1Oyk6PccW3J3ybrbb3CTrfryC+7V3zS8Ct3xGW3a+6hwt2y1WdYUjOFjkHJea8wp5Av/cBJnzxK7jGrSndX651E0l5WMudoYBeyakWy1KE5aRfks2ZFjpqICo7qMrbvoobuFlJvfj0hjCVmtNdzIYsQvBW1aC9tjPW5NSXKkl7QqZY8sPzVu050Mo2SrqY6oLmOrI3po5gRI/35E6kjIaq3hRhYj/O+bpJvUrjOLpEF3Cssfx/Er6EuSI3XEZ8885KfGbdoQEozBd1xWRFEhMZ3GVkns4L2gk/bLEaklCcO1BhxdoJq/hFLStuBO5UjayVCoHGkoAZfmIktNjwlDVIhv9RY8QfD8CUT7hyKKcSpOVY+UoP/onikRHhx/P6fREWk0tRnRGntKOWQ+DN3uVK2Ecz6ZRLm9sOExY4bCxHSeh2QZqbVJdZm6MB+P1jogVXUA986G4nR4d8z6MjTuzfIFef+AFxXqAdI6oiLZgEuYZvl5kAf3QdY/9uFeK5A3oat7GP7yrvyZmszqdxyHtgv+8cvmPkFzHdxHbZeewukADl7OghxsySvzPnj6K3cQusHAUOhf+LqDM0zzhTdE83EA/OckDCL4J9jUM84ZiNOGNySn2dDgQbwtSGB1f8zmE3eo5qsKeWCVp+TGKEuKNOSOxm0mJLLXcgCLG5DuYJYhGa+v4noY9JvwRu+3Uh8ZrfsvAUm8LB+ebjeAA910ABE2cVFvfPYzb1i2xRDZSRTxqCQ/c4kiXxSg1ve7XNj1R9EI9XdFXjX2kJBdTQsZx5pGisNKxpMPNDhCk8esN0DzhQe/+ThEAH7Xy9XIzRcu+vXHIU2cI52NdNsT2mN5Mtz5ztXKbJOBAVvneG+s9hNvmPbrkETX1lVfnOsvXFmuPw6AP4dZ9fC9B7/9xBug/To05eJdWL4FK++/NzDMi5Q3380XLovqjwPg2QwTvTHYz7yB2BZq8y2hqdNAMvvrqtFadQv4EsTFQ0D68HQM+5lLKtNCUfZwrSWYgh1fkXJbySSSaTiEAojgE0hf7+COx2v2M3dQpoXa3NJVdETTS7eRzDDdTHfwJiH+JYxy/oY52EUJtV4vNUwliqPXQrYI6lbKq6DqOLAYuK1keNAtdXFZ7UEIH2BITmFUBQoRVqL2Mvz4fZQx5Xe/riK++1uxtDl3Z5b2MMJOGS8djFTn9C7gmvnUR8lske9q43wPUhjEbf2Ls2R3D+NAMC8qnSR4SfsN4PvPIiC/fIshbyNgP/NwYFuYcUedJQNbb/l//XXU7ShGSA0TbbnsLK0M5x8ZxoFurIIN3d4IL2WcdPAxlZqq4I6q6FTNNdZR1WPInGly8PVNmeYT14xpvg651SBIb1LIPV1R37gutfbzwCDUrX5vEOobbxDyOVsrHa1IW5EDkv4oGUjNGUkaiwaRwq9aqMD/mCbFXjRI9VEyUtViYKQ2OLU3TvuJNwr+Ogj94gXZh3EQnRT5I/ZJlzur0CMnb87DQt5jALuq7lIPjep33njVJ8WD0Dec8aFJjSo8CbHNZEchtuUAFh/hA7Ko080AFvxmPCz4LRWxkIwsH01tGgUyTH0TTqfSaYu0/Jpwt2/qm3CQ8vPAILfgGRF8k8A4z8QTxm3FG5jbcMiufoR7wcG9/cS1nZuviiMIZoz9LBtJad46ddF7w3W+88brNBmcQ6Z8Nmf2mO/8eWOaDPKzW62Sw9NuEz5fu62GPIH9Cop9l2C/Ddc32G+mO7jIZBM1VEJDzUwT1jvjzL6gJV8OBI010VHAQw2B4dsGujpS/8aB/sq9daAbqA+Fh5APV7YYGLJsNHi7yaNMSJESJW2FmD4N7Tcu9u1nBa2LF/cXkD8mvM2420Ckeek2g1MUCU096ht/WiJFI+5bKh6E+sYbhPo8ZMSAGKCTjkzR9ZtwjZpeq6HzGYIByOHxnnuL2/nOPaexTQav3BLuNUX1O/+KLVG4dmkLW/RVdPOJf9dZf1VBXXzhzn4WEqLsH+WVP+gPymvFHZvXUAOFgbGHBx12lZSbgFCDdL5znSFsk0H/ITehMceRyG3H9yhym6ojIh9+cNDhEzOTvLx/QmY+c0/ETIvBu9zdPoBbnu+t/cS/y62/Dl62krSHd2C3j/hbXK8F/8q100jBrfEZ5DlI2UQ1XCcHr6HI5cFrO2SOsZmb+7YY+51riFVpo1WGko4zNIiaZ2qAp5w2Ej+VFi+72UE5Dlq2gcyn2Gk6KMlBVqTgB4DbR97S7HznSzHTRG3Ac4h0VMbndL+JZFiq1cDInfSavWE733ljdpoMbWSvcSjZuOmv3G2MbjDokuym6eO4IbtN+K7HbiulkcVc7XwXj6nKVWG6rt7QwpbcUCdRY41gFNl+wG02FJyivDPU0QMSDPpNZOFMyiPXN73igXstZBfGqsPeAtxsI4476zbgewLYNkNMTpMsQ8Aj8aj9Jlwm91oNKs02lRlHXbYf+Yqy/a4wDp2mjDsW3UA0Ht1GMVpUHNPZayGLHa0ageFrnPr6QRLB028iu8dYn+z3EQSbu6RqDzWwGIjj4TdTw4buo46QWMh7LdTQqJoPY1DHDioEoCjGoazbdsoRDk3Us1rAuXrgOVO5Vz3gopeFq8+TXhNR/DjbSuHU0eS04B41mq+i80XTQCFQWTwU81UUrqw6VOclPN/cb78LLe62iZpUc56tioSb01Qi45zWOhgNoaEwdm9A6k2W/CXNun2HQ/Vp3NCyDt0nZO0bvO6jIISC+MEP21Py2KeEovJ451eWfFXW0A+fhvnCb+2NKbwHXi1HpG+29NnB6Enua6GMxxeVbmIyeeqeUCh7N8RAkD6VIqDUXj7pM4wLd91/69XnmWJPJaKZ7alLL3fLmQv70M4oXXFsAx+LjH5vVtLKe09mRlr5aGxdQuQTRzexRLLXg33w1vQTPGXTJ5F5ErVu3mL1CeU3lGgE9jVYqQz4T72YftxnXqS3/PWWPunVmUdGdLeJe3I75zfST/TgzpzEDuvEhLINnc/SiKQ354bmaLteVRaHePPrNx7e8rjbnZRwwdmdAeCVEXdBusU3CEqMqBo7mcc5sOEURrj2TQNZwoROU38sUJIhC6LrfDEtaDHVvbY+l0DnjTUDQPRwWp8N9YNcqZnSb+TDVOk+KiY9hQ+GDUyW+lUo8y6VY7fw2onR5r2TJajL3r+yxgz76LW0ZASvdA3IZh7OrhvAHML5LZ0gzvbjPvYtu3c+OSe/djEOk8/PedMng4f/bMhvXoFK5r3Xxv2Md13ipTYTvXTXJ1PmMZcYNyrdxER1kwgQokQJArg9uWzpf/TLHrHJo9LN0ZxPyh7mibJkoXDbuV8svJubckeRpRlwIRXMxY7WuuH29Ll0hEziN/DOLJ1VxO3pWB5myrT6zk5LuLqdfMoVb1PvffPJGB1B6nZyb6tMxBZOUhPueVTewaeccLK3MEBk6ViMrdja23b9cJ3CLYwlZmyvqXsHnYZMWZDMppoR08u0k8wfJ/VNOXGSlDbjky1MI7Omc98IuaHUfZDEfsYemlhxEh45LEECHj7ooSQ6DhhcsWT4ZCjs4u2IaDoBTtiixZAjZEVzLG5PGkJO9Nv6OBcJMkTRjHB4KuqSNiwPvabepGFcFnRSWNEmspAX4j4+DX3O4DwWOTbzeSQPSwu3uTeJmYYlguRiw8wZ6OiNTfKEYTQ0xSxgBvfukoRngmxt3Ft5AzCSW91hvqgxRAEqm+hJAluQvMm1tMoH1xVhKbSRWTXTFdAPMpb5SySt/d1d9EKpmesL6qsXZoh9JJLWg0QJqOGQMQ+WSJIOqp6alUGMcKBWSNhIc1Qn5aIXFg9v4iq9vS3RQ2Mnm8XSiKcMCDGDeAk3CStkiTQPlcUGYjq+bKpwyx2LOMk4e3lLJexS6j7IAintatajGB8pTEkKUlcs7fGDHlOVsTIgIzJFAboCZP8LXEV0h/uOKLfTKgFuptm1cFhllsrhjMIUFbhMGjgZcH5qN3MzXnGbH30v93SdU2cYHow65DeU+Aqtww67WZNLo12UEdl8xlfBbh+BFrB4zjstHaE+4mw3uZ7X7OOuPsmClpJbA+tnZL0c1qS7OD21DfkSo7PfyL2Z6Z/U5qmuNHCc02p4hvVDxnuJw0lX6lc35JKk38P0dpp5I5hJYd5SzE9Nbkjyms4iLqCXbjOAMpOrvEWZm4O837vf0e3MViCl01q2sUZ0mtkkK5EGK1uxVDuHqI+8buvk9NLnHf1GYpTNn3d0U+iTnu2P7l4x0Rui0ss2bgcfakuWAYSBIi0p6Yw1Cm/duB18GilTsUhWIUG65al19CFNKlUjCDSt6g/6rCOJ+xXeDnLbickzfTjIlKsgvfjVKEwJlQlDp4WPWfdHHq96xrqNiRUH3/I7SE5v1sG3vFodDBRp7Q1HrKn4rsiXsrX1vM6CHU09EDEPuk3cEt6rZdL2c2RNtPhLjrL9Ru6Psv5JrSvIrG+Q4fMYZGDzA+aP1Ah9woe6OCOH6dutkUO6CuvfmDOCWbctfDEb+B0cEcLtKVzxSsWHHLHmGz3vyvxhe7nVC1Oxp6ldNKAQqSYeFCJdd6ntxy2ppE8ip0qS1BSStvdhGEmqQhEgKmWe3LBlcF0Md3IrHdOwpi5AJV4TnRZuie6W6CLdhOW3zMkj+38LV0wl29AR4tyevf1VWgvM9CBUl7kZPPJ1Gx7SmY+paiZVeIKWPlQdt1ob6S4vwmZLvnAh85q5Xc3jkcyUlRvY4rgt/WxunHJ5le6WVcGz8BUqLG9hW/cr3Hc6qG5Vv4FnlrLmg2dAJ88qRfUMK30/UKPQAYNkK0Pc2MfimJwVEheBsK17T8HYbOBXgVxzalRyNIdqX4m8iMpqloIzVClTDqsfHqVY89Jkv2GrR66vYpjDIJKcHmQdXJ8c+DUyq61noO6lPTNqk6I/1DBfhH09kSuFxD92qRUuNUhu2pbrXPdKd/Y5J2su2cr5pUXLLV1eL1QERwTDMUtkWxe3nY9daxyyh8j1TmaXPEezWVd0XUvM034j93Zprypt2VNYcNZEcKPObbIoplDQ0ud1fa92bSXCooqxJlm4E5KMEe6C9PXiJXwM4i24Raxt651yrK7BTjKmsAVYK4bwi6uyBhhdE7a0urglXy2ZQP5Qpp5tfWBk94u9rtlarBzih/pICOHUiS3pkdR/FULg7fgq1Wv12cSt1rq+hHwtIWktuWk2DriWVqddlZH6KsVmXbFlKA5foZeYWPug/Fmwa81Wx5UyiW0rJoxXsZfQI6vEK2KNiCHepIYZQ1Vo6E5OyZqJxLSVctdN5V3OsazfSnJs4hUBLg9L7BfZ0atX3bcBwO9tGk5Ml/UcDovnt/ZhcguLD6+a8GJpRVKTpz50jeD1ZZrsZPyQNffBEH455MqxJq1ubM2Ku0SDEVTjQ2cDU9h40GiTtPZprnELOBMQ8rrMJuHFOuyQtPbJDm7l5XKPlhZUNmVHWxR63YAWMYPT1jkhHBj9AtgUGElRa4NCHPxy0dJ9ZbCPDy0yUC6bAFKtfe2OTZIrmKEu7m9ipmURWy55jf/kJrPnNJOdbLoFo6tzjagUdK93vwx0A0FS3NmsMh1djnaNHdBnSZzlaQDxSRdZG80KqSv63CXrfhlbzn7tCvbwwjQo/8PKrqC8bynEQyV7HbCdrsWnwEmquZworbJ/k7KEKlSsKihMbeM+1ywh+pc6TmnnVf3OT1RI2Y6x7QtCObuadnLklV8oTkA2VY1adfaZAtacrdEOon954tTvLndPSWFuO8a2Bcfk7GrayZFXLmg2AdlMuXHV+e/UKBc4O+yg+pcrbrH21j0irMFuy2TqbfcQ59qmCmSoPSCfiAWVydyrJ69sPQn6S1LJuB9rBItNSGb/+MFp5G+amA9aTGd7KpIupvdw2GckboOsNYM6suSqT581k4dTW3Yauj/quyf9j19LIJjxaJZB2nz741fsytsF1Q/oz/JC8UuyAVFGfv3j19sC9d6B8q9zkMFtC+IPBDMGIR6zBVq3uYofcD2pPUgJATRGdZP6c+NoyINNkAcnaQ5x6nz0OURrCR2hfnlHoiPx5d492FzF10W+L3JEMtjdR8z9yx+/ysf/49cezn9UuRpdkIDQhIgEcB2fFjDaNHhfBlHW2fpEIM4Q9z8C9Hs5l2hp5mD72kD6msSKgCr2nYM9iDdoyd2B3T5CwLLreBU8ARPcvmXgM9gG4esNrjpOgj1FQIYngmX7H+cw2KbBLqtgtP3Rn0iGN7uX//v/AcZT0HoZJQoA + + + dbo + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/201911141820264_ScheduleTaskPriority.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201911141820264_ScheduleTaskPriority.Designer.cs new file mode 100644 index 0000000000..44b6784d80 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201911141820264_ScheduleTaskPriority.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 ScheduleTaskPriority : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(ScheduleTaskPriority)); + + string IMigrationMetadata.Id + { + get { return "201911141820264_ScheduleTaskPriority"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201911141820264_ScheduleTaskPriority.cs b/src/Libraries/SmartStore.Data/Migrations/201911141820264_ScheduleTaskPriority.cs new file mode 100644 index 0000000000..96ddfb67fa --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201911141820264_ScheduleTaskPriority.cs @@ -0,0 +1,56 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using System.Linq; + using SmartStore.Core.Domain.Tasks; + using SmartStore.Data.Setup; + + public partial class ScheduleTaskPriority : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + AddColumn("dbo.ScheduleTask", "Priority", c => c.Int(nullable: false)); + } + + public override void Down() + { + DropColumn("dbo.ScheduleTask", "Priority"); + } + + public bool RollbackOnFailure => false; + + public void Seed(SmartObjectContext context) + { + var highPriorityTaskTypes = new[] + { + "SmartStore.Services.Messages.QueuedMessagesSendTask, SmartStore.Services", + "SmartStore.Services.Directory.UpdateExchangeRateTask, SmartStore.Services", + //"SmartStore.MegaSearch.IndexingTask, SmartStore.MegaSearch", + }; + + context.MigrateLocaleResources(MigrateLocaleResources); + + var tasks = context.Set().Where(x => highPriorityTaskTypes.Contains(x.Type)).ToList(); + foreach (var task in tasks) + { + task.Priority = TaskPriority.High; + } + + context.SaveChanges(); + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate( + "Admin.System.ScheduleTasks.Priority", + "Priority", + "Wichtigkeit", + "Tasks with higher priority run first when multiple tasks are pending.", + "Aufgaben mit hherer Wichtigkeit werden zuerst ausgefhrt, wenn mehrere Aufgaben zur Ausfhrung anstehen."); + + builder.AddOrUpdate("Admin.System.ScheduleTasks.Priority.Low", "Low", "Niedrig"); + builder.AddOrUpdate("Admin.System.ScheduleTasks.Priority.Normal", "Normal", "Normal"); + builder.AddOrUpdate("Admin.System.ScheduleTasks.Priority.High", "High", "Hoch"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201911141820264_ScheduleTaskPriority.resx b/src/Libraries/SmartStore.Data/Migrations/201911141820264_ScheduleTaskPriority.resx new file mode 100644 index 0000000000..457086c5b5 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201911141820264_ScheduleTaskPriority.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/201912051209105_MessageTemplateEmailAddress.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201912051209105_MessageTemplateEmailAddress.Designer.cs new file mode 100644 index 0000000000..8fd955896f --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201912051209105_MessageTemplateEmailAddress.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 MessageTemplateEmailAddress : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(MessageTemplateEmailAddress)); + + string IMigrationMetadata.Id + { + get { return "201912051209105_MessageTemplateEmailAddress"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201912051209105_MessageTemplateEmailAddress.cs b/src/Libraries/SmartStore.Data/Migrations/201912051209105_MessageTemplateEmailAddress.cs new file mode 100644 index 0000000000..78d3aa6849 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201912051209105_MessageTemplateEmailAddress.cs @@ -0,0 +1,35 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using System.Linq; + using SmartStore.Core.Data; + using SmartStore.Core.Domain.Messages; + using SmartStore.Data.Setup; + + public partial class MessageTemplateEmailAddress : DbMigration, IDataSeeder + { + public override void Up() + { + } + + public override void Down() + { + } + + public bool RollbackOnFailure => false; + + public void Seed(SmartObjectContext context) + { + using (var scope = new DbContextScope(ctx: context, validateOnSave: false, hooksEnabled: false)) + { + var gdprAdminEmail = context.Set().Where(x => x.Name.Equals("Admin.AnonymizeRequest")).FirstOrDefault(); + if (gdprAdminEmail != null && gdprAdminEmail.To.Equals("{{ Customer.FullName }} <{{ Customer.Email }}>")) + { + gdprAdminEmail.To = "{{ Email.DisplayName }} <{{ Email.Email }}>"; + } + + context.SaveChanges(); + } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201912051209105_MessageTemplateEmailAddress.resx b/src/Libraries/SmartStore.Data/Migrations/201912051209105_MessageTemplateEmailAddress.resx new file mode 100644 index 0000000000..457086c5b5 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201912051209105_MessageTemplateEmailAddress.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/201912111821362_AddNewPermissions.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/201912111821362_AddNewPermissions.Designer.cs new file mode 100644 index 0000000000..325eb24496 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201912111821362_AddNewPermissions.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 AddNewPermissions : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(AddNewPermissions)); + + string IMigrationMetadata.Id + { + get { return "201912111821362_AddNewPermissions"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201912111821362_AddNewPermissions.cs b/src/Libraries/SmartStore.Data/Migrations/201912111821362_AddNewPermissions.cs new file mode 100644 index 0000000000..27839d3937 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201912111821362_AddNewPermissions.cs @@ -0,0 +1,23 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using SmartStore.Data.Setup; + + public partial class AddNewPermissions : DbMigration, IDataSeeder + { + public override void Up() + { + } + + public override void Down() + { + } + + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + /// This migration is obsolete and has been removed. Permissions are added via . + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/201912111821362_AddNewPermissions.resx b/src/Libraries/SmartStore.Data/Migrations/201912111821362_AddNewPermissions.resx new file mode 100644 index 0000000000..457086c5b5 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/201912111821362_AddNewPermissions.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/202001141118171_SpecificationAttributeColorAndPicture.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202001141118171_SpecificationAttributeColorAndPicture.Designer.cs new file mode 100644 index 0000000000..ed934504ae --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202001141118171_SpecificationAttributeColorAndPicture.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 SpecificationAttributeColorAndPicture : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(SpecificationAttributeColorAndPicture)); + + string IMigrationMetadata.Id + { + get { return "202001141118171_SpecificationAttributeColorAndPicture"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202001141118171_SpecificationAttributeColorAndPicture.cs b/src/Libraries/SmartStore.Data/Migrations/202001141118171_SpecificationAttributeColorAndPicture.cs new file mode 100644 index 0000000000..0f8d65fdb3 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202001141118171_SpecificationAttributeColorAndPicture.cs @@ -0,0 +1,21 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class SpecificationAttributeColorAndPicture : DbMigration + { + public override void Up() + { + AddColumn("dbo.SpecificationAttributeOption", "PictureId", c => c.Int(nullable: false)); + AddColumn("dbo.SpecificationAttributeOption", "Color", c => c.String(maxLength: 100)); + AlterColumn("dbo.Rule", "Operator", c => c.String(nullable: false, maxLength: 20)); + } + + public override void Down() + { + AlterColumn("dbo.Rule", "Operator", c => c.String(nullable: false, maxLength: 10)); + DropColumn("dbo.SpecificationAttributeOption", "Color"); + DropColumn("dbo.SpecificationAttributeOption", "PictureId"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202001141118171_SpecificationAttributeColorAndPicture.resx b/src/Libraries/SmartStore.Data/Migrations/202001141118171_SpecificationAttributeColorAndPicture.resx new file mode 100644 index 0000000000..5721fd2693 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202001141118171_SpecificationAttributeColorAndPicture.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 + + + H4sIAAAAAAAEAOy9224dObIo+D7A/IPhp5nB3nbZ1Q3s3aiaA0mWysK2LbWWbJ/dL0I6FyVlO1fm6rzIUg/my+ZhPml+Yci88hK8My9yLxTgWkoGg2QwGAwGgxH/3//z//72Px536YsHVJRJnv3+8s2rX16+QFmcb5Ps7veXdXX77//x8n/8n//r//Lb6Xb3+OJLD/crgcM1s/L3l/dVtf/L69dlfI92Uflql8RFXua31as4372Otvnrt7/88p+v37x5jTCKlxjXixe/XdVZlexQ8wf+8yTPYrSv6ij9mG9RWnbfccmmwfriU7RD5T6K0e8vN7uoqDZVXqBX76IqevniKE0i3I0NSm9fvoiyLK+iCnfyL59LtKmKPLvb7PGHKL1+2iMMdxulJeo6/5cR3HQcv7wl43g9VuxRxXVZ5TtLhG9+7Qjzmq/uRN6XA+Ew6U4xiasnMuqGfL+/vM73SfzyBd/SX07SgkAxpD1p6IvBkuxVU69s//dvLzigfxuY4tdXb/F/v/zbi5M6reoC/Z6huiqi9N9eXNbf0iT+L/R0nX9H2e9ZnaZ0T3FfcRnzAX+6LPI9KqqnK3Tb9f98+/LFa7bea77iUI2q0w7uPKt+ffvyxSfcePQtRQMjUIRoRvUHylARVWh7GVUVKjKCAzWkFFrn2to8lRXakd99m5j/8Dp6+eJj9PgBZXfV/e8v8c+XL86SR7Ttv3T9+JwleNnhSlVRI11T52XbWDelbWvHeZ6iKAPGqEb2vtqlI50m6/Nxvn06KcuTNCrL6QmUxWm9RefZJsFkiva+NDovL3G3f+TFFhdUKMb84YuyRzg5La6TKp2eJTf3eVHpmvrzL0HmFot1RSNv//znQOw6OdE+oirCIoywwfRrgjT2DpVxkezbHWaG9ubhvQ/JDi/z7XXeSPDSd2VeoWyLiqPya7K9Q5UvthbL3/Jsejq0TX0toj3WqCq8awl9N6mPV/IPZt78Rn6MmRsVAeRlkeRFsw2rN3QDYXgd3U0vD+tvf8f7xHV+FKcBdh+iQpX3tlT87fWoBap1w+jxBCs9d3nx5KIhRo+vKAwHJVHelkY9/NMvZjukJQO9S8p9Gj1dkJVos36M+WeDqqoZizXvYEl1m9zVRQP9qsNz4CBnDno7DQd9idI6xAZm2WxDKz11nXj2Qx5HafJPtO3bdODeDkfLvALCAxvL22qnw25qAc0vyu7q6M6SRQA8ZOoQps8fRV7v5xfQQ/tLNT3X+v4UPSR3DQNJZvLliyuUNgDlfbJv7XriyroZwc+KfHeVp9CCHqBuNnldxGR8uRb0Oioard9Npgzd8hQlHZ6DBJG3pdkI30y0XLqZ6aiu3ImnaB/X/keNNig/wThUrQc4w5yl0d35Dg/2LEmRhtxBTDtXlfdZKbAtwPHgFU4FV8rMRnS3k3GFykbGlXIBykHKZagMcJCNjBiVQvdC110741AHUdA4nAcJq5d1vtpVT+tlji596wsdYc5Lsrou0/ouyQQhoqt6ndcxJHzCaVX+QgHUrbQixEkoXKJil5RkcV6huLk7sRYIGxTXxIz4isd1EATytoyuNk3vXZy3O2rGMKN9jPZ73AV4vwNBb0T2GXncrIawAxpW89oKwTYCsf6I8MD/8raO0jT/4a1Kckzhu6ueNPOPCjKH1riki4zGCi4tGuBGtiTHdWUALiwqkzrQijIUHt3an1Ns8HukpbRxEhvsTNob5rvqJTPjBykhb2um/ZE/pBcIbTALt5uCn4i6jh5PH9Fu733pjRF1B16Ch5sCjayNq+TB++6595Bqmd8PV1DtZwJlZwKJzMsrGynupuPkWCbYCylSrWz+PQgmeVuhzvGBfBudll/nJjW530Ywgx9xo7nI3uNVc9kcp/2wNRroHzUqq/I6/5JX3gg97JHCHS1ed+8wY36uBl9U8ud1stPWPc22jjV97bxuJhMiaUATCVMgqntMKSQolVsBrn2UlT+wOJN2qi2/acUo2y2qSDy+cuV+59QBmZckb1Ec5LlCRmEqPU9Z/qnefUPFxS2RYKXfAKa4UGmXj98Sg9Y+tARt+oTJ1RhT49ZRWNJBGuqGXoxsZyVgoGyQwXrLCRqxl7SgEQWTGS+OoxJ1HaA17N6tVrs6WzKFs8rQsznN7ENsq+MUL7NEEJPEYZeQt9XT6I86GVptf9u6HJS4Xd3tf4jr/1M8y+nkrRg8vAnb0Fle7KLKd8PusW2itJq860fbXZKd5Lsd9YhgwtdswexOR7e3SZrg5eJL7TBWqHcoRQGeivXGrKM4zmvgVccU9qwwfPQhKqvz/dF2i49oqhdOps5aGolXICIpLzLwPGnt6FVWH/K7JHM9oOL6DRdhUS1FYTtzUVr374On9cTSPdwKM2FnSVFWOsPq2xDP+chszNLQGZ5R3SOPIDOEpfM+ylQOw2EeQvZqRHt0m5wljpOiuicLxHa9/dG82pt8s/oSVS0lNngh1qXvPkNGRt4GzvAAm72T8fe8Lyus2NS7L0mZVC7CkaiVR3dzKBh9a+/QQxIj+kYqXJPSo1u3+UkcM/vVdUOBiVc1Y6n0ZoYCsbUwHCdpiqkw7NKqbvKwQF9ZEHmHOTjbXvNnclW3O5ib8ewp9puHkXolCIC2rgj0ZZqG3BwoQG0GQk5sFsyW1qeP5OgZpUd1dU8On3EDpLJHqWqA02BUQZgTs1q2E9TItcu8rOCxDcXgQMRSodcAiFMX28gs8j425fJOssVwLzkY22421lm4h00R2Dm2ROgXV2zbpStU1QVmkX80l2lg1xgQsIswhNBVCZh9l39ExfYyT7KqfJ9gJOSVNthvAU7SezkcMAYFsO1IekcVo71GAAbEHwcjF4A8oK0I3NznTf2TqKjO8fEZ7jsPBZJfCiTQXg5pS/ivUZqiSsU8DATYbxhC6LQEzOvKYGABh/flu12eveoQHOzF8raMjAvPKSSRiXUh3AFjloZ0FvIwrehNC6HaqbOqEB+Ba/1NKoS/PSRZLB5jNS1SAWQmG1Yna97M1dDbyRv6W7In6mqUah6dBnLBus8zpDU3BZIR0eNMLfkZp+WHyXYNgVt6r+sMMONezhUJehNfbq0u0YtU2TkOUuwiAyDtKAvld0Pdk8ta2XiXFCgmas+rDsdB35C3pdkwJ3q/3zhSlp3tKYhXZhnM+/5H/gERIp6Xc7zuv74vEDJt8NcADWJBi4ok5hpzdDrtw6t9ibzvQ9fwxj+wdyusKLSOCDPavhmhLDkxM3Ib2KlAAPGkDEJ5nTm5jcljM2AwHbYEeVuyA0FY39apdpZv3wr0kOhO0GGuSdcgs5x0Usf1zqt9aqng5Zg4XCgF8E/scB3WvGLNd6TyXfQmbkamEUZ07lhH+z1mPf/FF9Rh6fN+O8kBc7AEh75gldnqpTexTsv6OM3vBk9G6yVNapevKBzr8DHvOnONHqf3niCDJ9afcN7sPUaQpSha34yAIztB5QIrgUDebNR2xYOHCILDXqCSrMH0mlBvXkNdVVhHO8eqJPoBPKJvH3wAxbYtJDHhMUsDetewW+UNPg9hLjmePvp6T5jorjzPuj57e2Fr8k6ES5MyP7s1hqxOYPpbGzrtqMN3QvusOx7o8io8UsIck0/nMo+jDfOKmEYU1je2trwiYUYW2Ao30Q0QpevAJjYnjYo3sCnVLtOuKt/WD1ih9/VCIajysRB+AUmKfFvH1VW3ddjbAqIqwj16xeBZx+Gh69JaNCN1Ky3hZjnoXEUVdYvjRpT3KN3f1ul/o/Iac0oaBNmn3AWX/C1xO/3wQ2KaW28GSOoJMQQgPh4GoaxfvtNYOlpkqCzxWi0SyXWCrM4Nt54lI5LWEF/Hm1Xzeyjf099X+hzOnFppGOJNzGVU4DabFBJNZoEQUpY8r8HQ59k2eUi2dZSmT756SIMySWlXKGrp0KVh710mSmDRZB6cUwElT/zmbG/Wx9j9ckA7fID2f0cdNgpWjyfcRfvhpBQmXkBjP+jfGDVbXzAzQquWbepdMBtCIIw9ukY74wbt3cdwSENmIgx8It58r6dfdFFW30aNubLAG3SldfcM0+wfVaJa4GEaOS//SG6rk6jwNpf3eEKoQeS9U1Kgi+oeU7zdTgLkZG1wjlqVJppEEKFWE6WbPCrEStfRdsv1wXtM5+W7/EeW5pG/E0eHx3fmPmdpu8B7hN5j/Nj7V1/cCjgd4751aE4f90mbNfJd9MTjNEPRhOdoUIRg+/dRuYmw1oRCzSqLzfKuBfemfWBfIEQrjq6dYZDNYo45L69IwoIigGftgOjkKU5R2ylfGUdjvERFknuvvgFns/c3iD3Xynnjm3yakSoBohCFDDWO5WlCVl6U9hhP7omJZTCMozjZEaPXZYF/NaaKl2/+4+WLDUkug/dPh+4HizJ1Xp6W3uSkEib7Mg5WcchNd/aAlyZGh9X+e39uxBpe/P2vddQZUbxEdntcazAePUQJrsuYHzx9F8GeOm9YSRZw5B/yH+2ou0BQ3p6teZXcPjUGgbO86Pt4jKj0JG6Ij6P4e5Oo/SOWz97B08hpkGA8b2mJTyDDoddbo2gO/XiWkl29CzNJLcboMRzGHsumQvsQmJ7IvU6Rp4IfSN8QU26rtyRbxOEJ8i4GbTusCZpBWceygHTwuH46rqtqNK54yBYC/TUp79OkrMIg7YRfivDixfsaY79yvlXGp5MGXRJ729cYJMF34It0O20D3cHspLnfnqiNzR7jiVLLgZjjHBxGiFeAg+sHjatzIHHE1JvzTrMKFWUQ/urENoMZTcwUnWCftU18+LpOULsmvTc8rEOgsjqqsOj8VlfoJN99S7LuvjQgE+I+Y5nXBBgl/u1p4n9i+IqSu/vpliJ7jguO/muynRD7+2lpM+w0vvJkQOQnTMLd2IR7+RQ2OO9wWe9p9DEJzhvGeryi1yfi1CQPqHgilS3tXr0miykh3r2bbHjldVQkt7faa4JfwwRxbR6uXdxeFMldkll2mDiBdRt9EAvPgO8jisq6QISGCgoECWM7tHm0o116fXezAS35waI2I22dbVPUXJ5qjJ1h1mLb3iUqSJivUDY2BimhRmicdHwy/5uCJHN7VoA1F/ZZgnVX5MFbW0VoUIxAn7O+9KaDvs7HG6LRz0wOJfiWKUCtY42yAkblM3cjwApOcjyIzPFPgLN1/aM3AGWfWUCxw3S5tLcMkKOXYrsu5JEBBbCbbiVJfSxloDJXRCm8darc7hjdWqc1npa9DVvhLMqDyAYgwDl2nL7xV/adBlT0HwKTjQGEdRxHJ82UQ+hgFL3nIGQd58Ec+zx4rIRzLlZ63vr2tzGX3HZxg4cztrL/cBXFeNQVZOPT1HIcb/voSSFQGThRnlLFUnFKwzhK0y9YB8d6PGT0UHZfUU8xQQa1ZLNkUtVxqnjUVgO3GK3tEJ3HRR/NlDzIAopMSJdLuZABsg5fxzlyKDrLg4rdZSGkHebAbLtMGxyB7g7FEGcIhQIviBBeLxt6DdcprFKniw+/Dq8b5G1pDEmmAVHsL8+amQnhVvW5JKfNGA83gAN53zERY3ALbN+UrV3D2k1jiQfGnStkic95+zyjw/k5X2MKmCaLndXPTOPM3Kwy72Q4AyZCPFunLZ314TrXHMVEewFdQWWCGOGEbUkJbLs5DTj0ZzOxXa6OajwMqMGQWHjnUY32ninMQ/phCJYkYztLh6pbz/I35EObcA1gHBCgfCggtF84t86a4PF6skdxUDDkbQW6qbIUuCZZ9sLsFTM+1zrOK8ync7bYp0/6kGTfVVpikESCx9H2TneZEoiQpKVN9TQ+fHP1Y0yiGbKWdlIm1JPLw5PGQNmbyWPuUC7ajtG28AFlk/yTZ2MD58shFOl1jvdvFFc8qkFPN+vBBeu8O1lszcbGcBVld0qf0TAzHPhtcnj/qRW/3FybF044/6I1e8XcRljL/JKgHx8nycMS+hrc4RSqvwoHjqzGhvXePQCKZDtcPvZAVARbrkyMXMsD+IWe6ntgfXD5iLZJ9Kqrfzi1KMRXS6LjJIuK8clQ95fJQtL5Oe90aYb/NMm5iPFZNXOeYfxQDcN45mdJijL1+evXQC/cPwWI3FleF1FWJiGewYYU6M1yJZwMxak1FWo0EviiqpNJLCB1TQWUi5dUEJDj5a6VX4MojWEIrV8DJ7qdJDNLQkfxTCM5yGh5W3hZRS6iWbkIbWOvue/FXAi2w6ZsGoltqqP2fCcKJwXUWfBJ/Alk8tFSWId1PtP1VREp0lxIU3c6HuuWRnNYtfK2lrkAmNXmSHHCwUi7LiPtway6OrPqwdL47C2NIWzZoY2Jzl4gepMi7DUSQqljnwOIKh1ULihJIJCXigR4WwTxxKTwHVQmpQhpyBXgiMKT/apOkdET4kDpTfZocg/HLldzoNygQ7SxMOiuUIlpGzf73RD/Fq9aS41kRDMcgqfPTUM1Gj0R5mkDh83ZcD8bej0odMudemI5T6ePWFDRhqkZLt9GX/np/PKke6TSi89t72FebHpkVaYRHXYbeVuajcDwFb+1O3eaF+/R45coredvvdPRP+Rkz5k6gkG4A8F52V3xK0+Ttibs8V2wvxV7xHVYcfK2AhmymVfcvsiCRfKMQ4bpkL/HcT2ez+IVPH/KkSTFLEg/BfV0Vku26Pq+3n3LosTbtaxLUbMeQ8/PaKGRm1N6pmh5xDD6BFfrht4kFJEo5NX0USkUdW1NLlzojGnDbUjurLThOQJetFFtGQ3AvOtBbtx0Ux1S7+BQH9QQeVsj0bwDOvdED4aoORf4YjsncZ/ae5iQBmpaFi4jS7Xr1kAO+6zkAG/nOEyHdSpvK5CGH+qpynl5hrWeekx0s6A+Jo9rNnCoQWiqEVgem2r4IVl7IuAE+7xLMC1df0Pu8IFcawBsB/kwuXygyf0vISNYbjUMA8dWUseCY/6QrEN5hQnkh09AO5P+h5QlcHwzf6kC4z3Il8nlC0z49mYvSEaZVteFYtiavPPsRgk+9VzAE1i/mEPHGZQscMPohKYDU3GBw2jV6ExJoMJiSRclKi+5qB6qs1hUoT1IRVtx5p3AK4xXTuBAFkFi2Yczu7ex95mr4/C5gsK8EmnuuRWUdQwRMl/Y2hkErF2AWyNZ7bcbgWRStyvBM9LHobqw87jgmGDLCb7ZHLaZw4bg6bSp0+DDnidso5FFMao2eVFRuBqZwha4YO3fG71PRqeLETVbamvDybbosRUu5IO9T4HTGQDeoxYQvvyu5CPAfewg19Gdv9EDIzkIWWchO9EbRXeHFp36FyyEv0Q1A8P8+zC5Iky+P/MrkB8Whbytzfd6cke5P6pE5RwX6HkrZaK+xLOtTd0W6O1pyOybtrks1dgsU1dqfAUtM1VqnFLWme/tqCyTuwyvot4sMEfK6kWyHJ6XTTJ6f3fNMLcGo/Hif+7SAAchjcwj6ejDuEI3R4iLurq4bZA2p5yAOrRpBjRVAhpNcjTTqjILuXH9CW46p0v04zBY17sS04Q4qrY1uXJMq7oMW5thx1hLZAfh8SaMRnTQ/eRtLfImjPx7mdZFpJLzEz0IM3qrEfI52M/z9ixoPDVenoQ//R2WvbytQDpbhybYpShJCoA/7fbTpwY4L7uXzN6vjKgtMauKPCXYVhlzzl6ZckkeaKg/OOtKPM+5jAe0HZvWsR2h+orOYKg84uY+3CoJZFPjRip5tTRQ1jdNF6lGEsK4GHAzOewiBrvIPGlRlrkYnfNRb9hLyHCq4OovHjl8p4/7vKg+Rk0kmQliyBjvSd0N5wapL2gAeJMdyqSaTCgb1Q0qi8eGAkrlEelBPjtLzlC3H55ngCCa3pRLSaLvWS1DT0liMXShD/QC1BFAUdlQoqgwTCBXgsuUgzwxX+rj1Po/61iBJvmnMLd563YoFqaU3MBu/44X0Q5NGLvxa3N3PENDfqEAJlOlm8NvCMsQydBJBYf0Q+Z4AeqgB69hAzPbx022wJCG7+51Q2jrd4P2sJmpJB9It59iJwtjljhsZEA7h43sX2gjE03la7G5G95BmBnunbazd/mPLM2jrXP6sx7BYZNSrNuORn/UydBq+9s25mCJelyfC+9AjwCqyXahvq2pUnSSW1s8GQTt5GMxyJYZpqHTRzymco67i0NeTskMNOvbd19rsWi0vTAKFOHNTu5O/R7w5J4kIMKHlxmvaLTZUXs5I02PCgIIGzEM5bXTXieo6N4YOJ8SBxyH3VbeViBlFM4nYXtqcs8hEsat3fJVi3WMwjSu2zXYJhthrpYHbgXA3CMFUhQFJcDQ6g0LOooAGEKQARKwkN7vYxMDFNTNS4lTlggRwrR1hR4S9OM9Sve3dZqhsvQ3awkog8mvF+SBEMVz/VR1+uBLE0nR9s53qX+Nym6Awd+Hth1UHVsFAt9wVYWDqqaG7Giqq+bFgGd5Ue8u87L6kjt5fzX1y1cMmnVw2tAlXy5rKROKv4ZugbzF0PGGgh2ZSQIicI8MLgy7BGCVg0Ilb+s63ydxKE0ohHP5/H6E55dH223R7IUTH+BWlKOC34zCxz/omUItfm5GMEDyDKVyoTOC2CpvjNyC/Xc8pCTvi6OTpla9blathrA0IETasVxBXArIX5x3nfGS5w2Og0CXt9VQaTUCncxWiKuoTf3t7yhWbQ5/mughZLsQSk/NDqvPnig+RGUVQsns8YSa4h5fu+3w29BhP7TYD1s5K90Q2WJYaHMwTluiroMdENi75qeqay2A/17iuY0cdhB5Ww2B/ijyej9xBN2308TymvNd0KeOrz2Fe5hdhojiIOe5n3Gv+dfKNDiuYbk0v6GBOGlOlcHSnAbwl+ZdJ7xEeoPjINcVQuanl8Y/5RoXnPJ2CUZ2nTfc4P0gsztYXedHccC7jXZd+ose0IgCySYn0fMJH8pO8l3rI2ote0jtVxSKdVxGdJ25TiplRJsw661vLIzZViO9MKWd8mNKmbTHCLIpNbE3I+DIqlC5sFOCQF675dgVd349pITXaZnZXQ25k9kavwKtQfsA7MUCtyhnjeiau9VgichlLyl0HjjNzbhb5U2FVyDRA0C1QOd1uHWs2USy6ISSt/ZwtN8X+QPadvhOgFiutifhvAqPNLDOFFQ5/IiqCEu/H3mhjGMbKPwybmxOhZy0pxODjiOTbuu9+JZu680uPEKxezpTCG7oLISthZVSCWD92Enx4PVkpXbipHgQd/Uii9Kjuron22ibXuEKxZhvXXzIOp22fKVCfNBSFEIokE3tFM/H9E9E+lluR3ebKGO+h22yMwrM2PIF4eWG++Zp6iiOUVnO0yD+8yHZomLzVGKZojEphX4hoLxCUwkS8FLNqIKwBZjV8jrkdfYra6HaxjVv/3cQnYpFQwikzTwRaIGStrzfBgZ9r+CL5zhJU0yszmnO27MDr+C9Ap0BefFBqqqDdSQMtsvoiWhfQZG17yumlLwSjjmpiwJl8dMJrjlDo21jV9HoDa153PIfzmvhOnrs9IMQXkpfIn1Cm4BiZVN/q7BITM+zOL0maCd6CcQ0dvo4T2PXpDE8NzE5/s81QqbReUbaSZ15Rtg1NuvIcEMWS9khOzAtHPEukhAVIUrPEJqapvKWpyawvOWpqd3hnyB0K8RDkzNpv9ane0VZIDw7x1EaZRO+1WyJRYTXFR7NlgpRPGFTkzWBTxJ4EGhrm3bMupkfUbG9zJOsKr+iAuF15H+PcHKP4u95PYZwmdPcKjQ+S6KuXpcKdW92dHubpEnkH+hrOPDsJ6dBc6tCTmkYfbvkTzBvscqgM0thTATD9BNJujzLuUKgzXQ3EFH5HW1lUzLpCE8eHt7O0tDp4z4p2hf4eTYmk5ypzf9G0fT0pNdXm0LsHfqWeIeioVAdxY0i8D5PtzPwh9jwTIxJNXwcZd9nOdFzbc4iYug2z0/mbK65rx7DYM3R5Pm3aAblottNGwVwcJubet3X+IRTJP9sJE0TgSqKyc9RNZi96VmWjKzxK1RSed8mlPB7cjMwL8HFRmca7ab+Nujo8w75si7i+6hEc95IXEaJq+9Qb9Lh4gFNNi9dc8TggAXOvq6oMEMzmsHfoRQFiPC6Xp9y+iR8hchdImVBMLqHeR+RIIOdWepTTi7a24tRbw+zOEb76vo+wf2L8OfGXeF9lG0vHhxOVtKbZfZOC7xfbtboDQ84XidD5cLtMQhk60OkvAZvW4Duu9kSSdecn472lrTPZXSH3iclyfELR/EDAG+6K28qlJ8USnB2UoDaBlf4I7ltTonaQUCAN59LtP2aVPfCYPTQwqAMqtgOrqlFPMEU/N04ign954qEzvLlTj37JI3CMRRLejYWwT2jym17doW2CO3QlpaQp616D3SUhtIyhRZYGIy+hu3wyA4rd0XsS0WysyVCR7lil17tjaSxAMlLOw5AIvR4KFvZ9zVKsYagkhcMhEhPoFggKgTj5dIJS2yHjO4tmhKU7Ac/JHlbw+22p527Fb++xnIv7VSrFpirAmMNjTbQfxQD/CqhbRd3t1in0GSMOs5KA6eVDqs1rv6FELbDMpe31dPLd4UyW30IZF1eo+luvqcQKD01zdXysYZGI+8/CutSDW0rUDiNcI7ThdGAYOXTS+D4C5mDYJG31VsPKakAPZA2k04h/B8ndtg4L/vOHsVV8hAFsMn1CE/yej+Taf8KE2NP8mjMYrscWpvnnc8GZeTAPcfI2qbmGdZHfCpsHgtO3M55OXJHY0Rd2uo8kaXMfks2so8JG7jpaGBJqhzPjayOOCIJqHRXlsH7PypyDB1BPyw6RI8wOYxjInk/+gmi518umr2P13yzpGpyWkztmT00NLkj9hyjmWUkUzuO97aNVkecmmRsa1PTbhFn33mdfPukWQGicZyXPbJgavwHvECyMX+i5RGISOs2aaw5h2ieSNbZNkVYzYqmd+doBfxJE5Z+qtWE0uQBFU9EZ7Skbfe6jsYQ7tb+qCzzmHiKb3vVCb4sCqizyfRQnY7nb222uJ8Fb7mA+1tj9ViR5mrEfSmmuRIKFV27DJHmarjv9dRyP4XMLvTTarm+4aGpfEfzhQzr3/vmo4/MKk/YGklg5g8BLjfAX8JpsQH+CF6hgAB8hzWo4LtA8RxagofAwT1zdL+ZmtiQPJc5byLJoPT/g5yEIG9AFZwgNpTA4e+/AvtRmYwm4OVX7/3kIAt7R6TB9eog/+RtBdFBroso/o4pPpPzffNwOuxZs+EZ5OrS3x/MHKvPrvsY+yTyi17ismjrKSn3n6UhbkYZIHaQAZD6TrJQXo5+NMoQQulwBaBfkQ2dgvj1ucQ8D2O91y6GkOtAtl7h1eK0Djg3XY+TCoPpsBQUS2FNgetghWFRl5YW/dThYZhWLknAPDwrK3X8E/xXopK6TBl96UkeKPI2sCv3PfC9+SVIgO2j7S7J9I+j/+TYmtuRkH05AB0GYQhBJkvAwnlUOzyTUPcxiJULS+7sCv2jRk7ZyDuzMoPmsGXI2wqyZQRTnULtPWEcKFppd5YXLTfNbzzv+Bc1V7dBLrn9OqB+Bm02tXwyjOk8Fqvo9rZ75ji1c8C0+5AwKZRsCxPyto0Bd5234ly4oplRA5koQ6PGiEsRVGK+hSAAUycI5mnfzBt7wAnewfwcCnlMh21RJTwCbIuXUdEdqS2PQa1Di0NFeopDPDEIdukVxi9ymUiLeEmiAvMQ8Quc7BgXRmN5DqJ4ZG6JQYuVUjc0PG3WkoIBxi05bNBoIEJD0F4iBdL32/kMqHIpEloBPItkMPouB/EzEiKtum+CAqrDLihvS/OyyDSXmO0lIXqs8KfdfvpYZOQF0D/qpPD3iW2ubQh8x/Ch8J6X19Hj6SOiqOGKCiM6wWxwlxdPwTZikiW3yNMQuka4dNLnZePqbO/6Kvg5+yUylG8ivBBq3obDF50w7A0gEkdxbVpHuA41ruh1Qwq3ElCmN/gOgl3elkAxb+fSRXaKRiU/2v4d8w1tegmunLcuJDM0dF5iTHjZozjAMw0PgWouueaXWbzKaS3sHO8l4rogfN1FXfSO6yJBeJBa8rZ4kv3MkZz4sUoMlyAP3YiVaVOmWR3AuGlYMeg6C7fADitLwcRPcYrardlzNRBEl6hIcu8ATI3zZoPP01tenfl9obNFoCjH51lSJVG6ZklGd9FIit2wNeSiiwHUyisW2taCJt3/5xbLYphSS3nuJJYbpsefPuR3DhIZ17ojLq0UloM0lrdFkWlNlzjhslCsQzBxZAaXMgVzI8CPq1cBJsglFWzQ2wi6IegiAipX9jbMhbZAxhDihMAfRIq8rTZ/A+7Rj7xQpZJ4M42hRmMeejtNq6cZAbbUsYz52G8rPGyBatN7fvcBPaDUP814XlR6f2RjzyzL5s8w+GzR7PZDXHbvheaoUOjcYe7Q50Llt/Hmz4Ec5G5RUaBilsaCulwQ6aB8FzmRIf19Ve21aYbehCDX51IbsNJ0DwqiJMmUI6VSFFAZ6rOvOmwlQ91y/HnYU+RtdeLR+546jOXG0YwkP80o0ncM7HEDpO4QCsUzgADh/WKfzmvm90R2xHTgfXlby9zPzhmgL6D7yF2GaX1yT3jc3ukjpA+JxZk6vUJx7hQCfkMMhRjdqwHJYSHJ22pJ77uHtFiWWZS94kJku+84zvFumgbcxOi+SWxaHY/esKC0VQuCAOxaIJjXxobPGx6rMH811D8swJ95AW7S+m7+VoM5RUbZXY1P8nYzYJ7LhDBGEvt4epN7ijx7xWM6LKqpFxUe0R9FXu/nZ27c8vyNMgmd5rsNc3iaZrz6ru/RDn2JioSgclh6Tf3yFYPmsO7kbTWECsC5sxz+/FbD2zCPn6fk/i5Qi73u1hzt2v8duH1yPrR9xqi8BZhKxytTp9tHABM5nmvuTcKs4LMcn5Bwx/H/j9KU3NZ4Wz/e56Uy0kOgOEAf8rv8MonJolrPK6T31S49zreUVjXdS+U8q/AS7KNLfkLVj7z4PjnDXBYJlnVPjVQ4qYsCZbG3DtnhPH2M7/FBA5EkKc6oFc9hpY3AT2TJCG+Utai3sjpg8dGstob9e19xZvQj48AlQ2Kg1GNhQf2eAA/dckiZXaCY+ES+6pEcNml5W5pN+s8T2STbidGkhPzzJDGOzPMw/YfrdvIhJwj8yWpi2cXb9w5PetPCtO35vo/lhRZZYOX9og/fpnTu8IunwY+xkW2n2RbP7Awa1lVeZ9shzFsZSLdtsH6qd92y83znMfaxeToSso8j1ncoy3dJFmGOn+wVKNfkVU2JjuZ5ZSctGzgSlbMBCHgG/hg11+qeR+EOy2Gzlbf1E1xmTGmPifFmgHn7Oiq/u7+3JbUxR4q4DoypmFaKXP6XxFd1llH6iKso/hjF90mGlmJ0zCuhdu+zJGs0HadImps6jhHaOtY+LYpx65oyT+Id8RS7RPh0KKSwNKs7XRhpeah9iu3h87IoRm7YStSpWQMrnp11Ffzc7phuBhCjB/kpb0t7ip5ERh2lSaQ6DITyeM+z08c9WaJqx75AZw+CX9HKf6zo8RBoz8wLKq5jI0XI6hkL7DWu/UXGiHHXzn1CjxXemx12kfPyfbLFa8H7dFVneIvoNvZwfmSg5ji1OBdyC5nKfydx3j3rdnai7uqXrxhEB5muWMo0odoHlXpVdLJnZWa+1aFCjk9iWpnHN/qvNarR9hQzfXpUVVjSOIaV6dTR8hWI8LBw5G1RBPN+z4l7gieB2NMZzifCnJsRAdR2547GYDjTKTdnCeCFrTH9Uonuj5MsInubyQHOS2Od6In2R6xraPS7qVpG2yTqeEQ3AVI9g8weqFiAMuKmBR9VCjmUcDZUgNpeDtMjt+g8W003CBracDBMFdtBUSgtxsTU0g2J+mo4IrqG19md6WeQneuwXykkP3dmc9uqCDvNbwK4zudv8wrt06cgDWsMDyeTN3Ecx5O3sam//R3FqjhKgXQL4n02ve9ZyCvyDV6g10XiHfEPo3EzjjcCPI5JAkdvVRVl249RVkdp+hTOsjHuLvBT78D7HG/TMN8ZTQdEk1w3ohsWGBwIA6Pap1lAr+2Z7Zb7/kzjOWzQmmWqOjL8eRJPsc6vSHdSMmxc7zk9/wgv84K/q7N1cirxTIYhkPV5vSw10c0mark1h23K1FrMi8R7h24jvKrxrtqsBcopKrBZ7CTa7aPkLvORVz2Og6ySt6WRFlM9EtTqmBM1HEjnXNJPcx6zdLeIrtEO7ylO75yHZcihOqxG59U4ka3xZz6Uf8SQTf6n6R0PPkRl1TZXoBAyVWsKaPXxNuaVcnxh4ir+XIaBUAEnQp7JA79Y8DriSw/2bxzuhMbab71q/6qtbbzBfUI/yg+IyGfM19Rdtfs+B2M8bHfytmCK/VEnQx/a3y5Lcv5zVhh5EtZgae/+LiL5mhffMQknjqxDXolsUHXaMY7RMiR1yn4dMggOq06x6sIomRpjlKn/z4q28k2cU1f9TaoUwlPtV+tXBVikMeGGnN2Q8rskjtKLPZluyje+Cw9Mykg3x/Kf5hEhUajxp5iot4avCeQJcupUksGUlLRC46YTIUziG6FUTHEjgni5To74XMXgQQbqHXubifLdGgkajX/QRCkrBHEACfIlwoqFEelTJAcdVncIISCk85LLCSchcJYX9c7zZNLgKF8JqA6CQd5W0INIqNRWzQz6JyzcJ/FKk/ApcyAI/AtmRJBDCWtVAeq1Zi+L5AHTYngY6LpgWTyH1apYrSHOt4SHQq3U6zwUJr219U/TBLu5Ro8LXMydl1co8s83WnbpL46fjurq3v8JGoXwCsXJPqFeCy9j4ZE7e1NsDMpRVqzcsPCjIFWACZJUBWvrIj0uHZPe09DSvo9Aup5TkF5bwEcUlVgQf0XJ3b2Lr9UYDIzBdNgD5G1pzFkTnb8Wzlh4RUg3CI+JwoJZn75sl8k7LPGy0u1wI6yUAdlhsRwWy8+0WM53+7wghtDbxCnqOVP/sDjWtjjO8nQbLOmgdaCf1n4ZJsRXGExB3m63PN+okWjLvEh1vRv6nuw9j4bRd+SHob1cucj8vTnwqjtLULolf80QnrFns5M8u03u6iLSXAuG8fA5fcTSi577CQNcp/UuG8ISTtzaFSqxgD7PblXec2Ga6uKBYOROkdbcA1kxe5Y05okcSjjvKUD9wlY9ZbF7PErCnX1E7VcUqsMurVjWQYJStvyhTszzZhoDm1E8zIk2/CYU/2O10DGgofn7qFQFrvhTuFDT55aR/dpafZ+m3jOaxsgGLmzmusNUFrtZD80fDD4SYfkO7dP8yTEcDY/iINHkbXW7kq9IW2ZRh4pjPZ9SMzJliAOLwYvCQOlM9A8IwzSkTeYTopH3VbW/LqKs3CVNnMYQUwHhZNzlWqEEgzmcnFuzliYQU5g52dTfWjvB5C0ZPyoJxAhNewaXnQEHF+axBlmPyQP6SOUE8XhY6/I8V5Ffp7MVAicrfmO+GYDHc5UMRjhVSQH94hU8+hk8mfoHxUPe1ooNnhOFSSdim/zqSDW93DaMwxkmpvlDggm7aOjP87LbFPvF6ycYAymXrUAIaQ32s4uJTImZcY5DJ/6T7LNzWF97dmxtvrOYYWc8Q1zc3pbIMxZJ89rTD8VxVMX3m+SfnhcLl1hqtMllPddZQPXqJN/tG88rG4U0WMCwvyX7oyK+D/Gej9RyeAUkVe5GbQt+z+Kl4PFvW7SaYDCLP6O0SS3+ciiJbhre4n8cxd/PM7xe4u+e/vknWCim+d0rCcaD5ipvK4jjL/5zW8f+kiqQx+8SvvUS1gM97HWwwgrUVrDPG9tMmNVIhjr6gXSgxuPo4f08RvHucBs1yaELj2g7vSyB0B0EibytZY6hXxL0I5DdcG3eapgR0V1ePAXgZR7VgY8PfDwbH3fCPQAbc5gOXHzg4tm4uLN5DUqQMxOziA48LG9rOFW8CXQ6eeuHZ/odv8jLEuvgqT+X8agOfLZWPrN46pPVVyhuHBfs+WJHwof2CA7cIG8r6EWT46UPmSlfc+mob01s5/2abPGh/W95pmkqSIrQpEqnH1HD6SRts+8ceARfga44Aga0bN1ErvOjOKC3xDlmXdiUThialLbi56bl79GOBBQLBnQIxis6FIvQQ6aOSA5yVd5WQylvT6ECN0boHcBi3VyqBnLc0AydxDKe/CZ1Htm4uW+useaLSXiJis69kPyturYMdN2+PuF/jO6SLEgIRDx7P04f91G29R/fp/wsT9P8hzce9ONrkm39EZ3HuizlIfi/elKusjdB4nDikZzkqTIoXZjhvK92VK7WyZo5KcuTNCo1LyRDhI5/DnpSq/6EUZP4KzaVKmWmJtU7xniLHhL0o5NlrYP3eXmWRnflMGxrxek4ze9IcC4edTD1CfMMFrrpE3Ero+QuOz0f0e4bKgZfBOK004Rk/P3lL8JUMrBd6p4B/I0GvhvoAP/WDP+m9S87vhsq/mrWkFjxT5qKYo0//yKyS8sYSmZpDCBfkjL5lqSNTujNK5ztf8S9ILOcNdXMmGWDoiK+bx3qSguWaUZ72US/M2Sb98kWq+JSZjGawb/WUSMVyOvGIk8Drfd+DgHsC87iZp9kGVHNDFc9luFYSRkJ/MaBvmdRjKoN1qCb3c+dsC1XvWrQla9orAsS9H1SleRkYErRBuqIgn+jhv8QfUMpDS++Q2VnjFHA+2XhOmu9Pe19Qp5xBp06GvWC83dyj+Lv3/JHVJrOYOt/ZDp/n+odKpL4irzRl82h0XxcJ6i4xJjQSZTGdatFfUTVfb4NKKzkjSy58xD92HR28Mk5RlkVjXvVn9UVjrZ/x9Rq3ylLNiqj+fkapSmqLvOSCKQrFJXkXOY/MZ2zW/kKwL/gnBxtd0lmPCdYW7+PSmS6Zq7QbUIpGxqJh6HrbCuTdUJfMMGSKOUq/UmnnH5LKhlDGXHHSV0UKIufrnLcLJnAOg2hZIxh7KAGFuSPphsfk+0+x+L9Ha1BaHiFqfh5b8oyR+mP6KlsKjOtaXiHqka15bJdEs9wLDnzIswJgKArX9FIF5zMT3mxi4yV//fJ3b2xdpOPJ8R/d9EtuWTdhDr4+PAhj7sgUd4TwaWvlrW05Ck+zb+Zzg15wIhFHyKiwlgct85NHqoLlQfHaT7aDCUDliW1xaioTGndqODEKmVK59Fr3kx4DQ4zHmILTAHkMUkAviW1lMxYb7wovM64qtgQ/rs8HedL3tKSZoWEPLe7bNK6m9H7I+5IssfdPcObCxkgU9loCo7KMo+ThoQDN5MwrK2d/QqVjZX4ps88x/X/NNu+aK3HylqjrflpCAQCVXj5oh0RJiberH9/+X8IwzdtcPABoBrsh8A18oYdE27kImsjzr84aoybRGKVcbQV7fSYolv2S7cvEO34JM9KzB5YAxOv4JMsxvOW2gyFQ2J4k086OTTHl7xDe5SRS3ibOTTpB52nUOzP0CxHTB3tfntNMasBDyf/bJxlm74ZMjBYRcq9NLQ168JNPT++VY5jLqZVztuz4NhebyGDuhk9F8ifXUDKUsq6BnUhHqarTcpcdj0FlgAIqV9xTkxtQcwZuNuCYCa9odEtxurgMOjBtW51Mh4yqw4xvCMbWTet5uDOaXBtwtxubDOwvt08m3SIr7vcCsjTVK+RMFAgP+fknGHBvgzCZ6VqgF2fgwmhOXgWCgXp+VFW/kDFTcMnKqag4GR81oLYchuNGJKKAAOvg9eAjs/EbcBcGMk2DL8Yr22qqEKNY3SGT4wnJPZQ8STlOBAa4jsG0Ib14BYABuy7ujoeVI5gBk5UzpGRptlWWYwlu2hVWmbk4CA27EBsGJDHas56v7x6JVq4nVhI0ocZmEdC0+fENqzo0U0zu1rCshCLG2AkpZQMz05gf2ZkKpDWJu0zFRe389yM4fR0xpIRVGXFseEtADPAWGZM6zL24yQlgTv7BrTdZOGDU4FDb04KcXU5UKPJB5JVY7wvXX/5Cip6dLAuZBGagTYxCdmXV6B0o5jRdiebLxuD3WLyiry4ILeKenOFAAnxZQ9kw5Ai4mdlupB2fwYWlM7JszBhkN6f5Lsm6OnAOCou4YFlHNjB2TKhgB7gQxmDr4MPZSOYiRVl82PSfF9nOYNa0oR0vPmItknUea7JrWoAMGhaa+Gs7GoQaoATmX5OdixQ9WYOi5mCzibN0/WW46zuzVY3lp4npAwAgoPcxUBaMRncBmTDhZEvL+/UQ5iDN5XzZGTUbaushjE7X0lTpuFj+U7BmFz8X7GN9TMmO4QFGJOdJyPGHMN2L2NF6cLSamUlDwielTsYq0Myj9dcMobbe2WdmONsK6Hrc5Bq75KySY9xc7THk0JCPnSjSRTGOFUliKl6eBumUrYBWV/MGNeCNHTocu3agoAhUtBwNuQA8S+xzlQdmWGtqej8PNcbPSKbJcfUm27Vsc1ARytzjvaiU7fN2pCorzIddYYWzHWuADTpf1yhf9RJgdrcO9pOQ7VsKDORG6y6fwBdAbiJ3F+NSDeD0DMikUk/+vpLH6L62/CL24siuUsy3SmKh1ccoxzOTwL2JTwUNH2Z7yQko7VJD7iqi7MZFk/JAyqempRFOi6ggQMzGIMaEml0PydnMag3M/IXRGcj4UXVW5qzjutsmyLyFveoqorkW12hNmvmzViiYzgTHAo+lFZ3YFCjrshVHGrMazUw2YxwvrVgwwJG90JDrfUskG4ohvZSWT2jheDF+Vx7z9CIqhvLEnwNz6I5Ly9tXBUHZM/I87GwC/OGVC3kXVmE956hMb9rfzApD2ZNDRMIFRTc5mLjlzZjYYpdjaCUjmI+LpXOl9Exq6uzGi41lIk8/MQ8+oy3ctkYFmDQ5ytEmbsCxkyt4SBpRQXDul7saJu0NLmvhoW1I5qPl7XzadIVut6qONtQ+EJ1ZuLnZyyIVeNYiIGfr0De7FGc3CZtgMPB4mHKwOraClaGKzowtaYHz5C9zUY0H6ObzfFzYHl4JBdNtqcbCUfK2M8BF/iEXIHG6kW5Q3egt5pGy3L5peIx3BkWjgdvmPQOxrDSjUTJ4G6yXU3exbYcZbeM1xq89Jdfcf5jX3zHMuEb9/XX4ll6Ffbb8nWkiMcHwAa+XKcxy1UwXBzOE6vD+SUqkiirhmk5yXffkqwBtHI9MMWjIJwChQNNjTu0tC+DbUfnkwu2c2rSszV5QKjGZ3igM0CxCo5/xuc7i2GtY2k8w5Oewaj6lG2fs8RrVdB4VrE0mA4B64MZ+JKbAdTRdXA8NKcmPaPrrY33XXcAB7EfgKF/JgG/Hqn+jEU5d84qN2g4ZeiNdRY4FGwOVHfgdKNOyLl+xUY5hwHOtxRs5t5iWazG/MbbMxQ8a8egCkzma8V/nai6YbBa5Et2revGYMCLrR4DnnBZQyOWpVeTbOM03mq0CJZRp36OHcZ4dMtrWj/F3sIPrsnfdSPjVkvWVCKzWCUNngBLRd0f+bLRrdrVrh6jAS+3koz4w2JV8SiWXlxWhihTa5PLnc2q7EYLG4eetwWIJEdO82hrFgoQhAaDEHSAVuEZQOSLRQNUdmcG9lLS2qT9NcUDvNlEJCHowBY6AcOCB5ZeHHLoElTCvuFlF9yXGaUXTGmTDrA1F+Ow6wQVeNQktQedBFDGBDA4xGEDpA2PSdAr4o03vYX4bGkdUD2UGVhUPVUmHVhFjsBxGLprFQFyArZ8jpcl0t7PyoTP90LkCj0k6IfprR4Lrdh7W0CHHZhr4TmxonIE823b8Bw9O5Z8j9L9bZ1mJFUJy1RGHCStrmVaqqYz/8pblzM0vGRWxtbagc3N59p5tmD8tuJi7H+WF/WuyRegzZEjgkJsPUDZ8DGA2iojTpAjkLwTM/CXnLg2euWybHSd75PYkI9YWCkjNWDWnMQhX4iV4F7MxUswgZ8PM900//5R5PVezUkUoJSNrDmIRgqwD9W31W2dsv7PxXjAfJg0PdZagxBrucZAyLRDnkJ8tZhlzLdSvgO6Pq/AY+bDmO9WoH5R/KLXkqgBh1fBKOQy7gP5eiUsKBnDrDqcOD8mzTcVlmfFL3mFbkYe0jIMC69kSALqxJRcGzK+XGd2OM0o5uRMeK6M5eSiSeI+oR9lE4dPmy5TgISYsgey4UcR8bNKlynt/gwsKJ0Tk7YXT5dJet9nWBwYR8UlPLCMAx3SZYLoAT6UMfg6+FA2gplYUTY/Js33dZZPdE7fJxrk+mbAg6f6ZrFb3jC7P54/faxQkUXpUV3dE/K2sQ2uUJwXW70xyqg2RCpVRRvymXXgWeUHtxrSDOvdao6fhYXsotiSoO5JmibZ3dF2W5B7IxmHQcAQRzdwNqwLIgY4te/gNLZWVS9mYC4VdU2aZ2suzFFaccmCBeSi5ynp4L7PxnXPUnb1SaM+l1ihf5/g3hRPQyYquaevqpYq7RhdwSU5G9ygIo/Y+rjUaCgzMK3RHJr0Y/G8Y+BIWslnxU/tMp6Le9vWANYFZfZK+ZYZxFJMy8ybSSeaCstu7s0pV82jHJx0e7c1m/F4nw8LSno+1w4vzsVzYjadf6cAOQHDPUefTmnvZ2W7Z+jL+UdyW51E+FB/ibt8H5Vo+zWp7kcOkrGLph7Eln0VG67UNSMTixD3h3v5Y9irGXjPcBqMOBHEsDhjMkrEwEI6fgFrqZjSVWtUNwiwp2wVLC9DjYYyI08r59CkH32ddfHwZ3qJ2TEyU3U2bmZbfT6KqPlglmJqcD5NOsNUXFZt/UTcIwzOSCOcVGX9ZOnqwuN9Pqwp6flcyqo4F+s/I12hH3j5XOYYQdmvH63tXVUJYkMA3oYhlc09Kyu9yUhm4FaT+XsWFnxoIGaKgLamsdgDzz82DbktGLFZmxw+98m+cTpRE4kFAzPvdBBWWXZYrM9ne4E7PsN6hedh/ZtL3+/2zNyzio4vGGgV09na4uAGoKQyEq5eDwuCQ5iRE8E5Mml/QLCsewHpxt7YY4WDDuhswGM291kJZ3ZT9mQu3VlGY1Oe2q/Ac+VrlGJZYKxFw+AQczGQNkwmaeJZ6czqMczAn+p5ehZ6MjsEtdIHwE7IkwF07QBcNbdCp6Dx+rW6K1TVRXaF/lEjk2ANMDh86qEg7QwEYBPPSsypxzCLUUA1T89CzI2dNlTvZBWCv0gIqedZHcDyptmTqGhV9uM626ZIeQWtqAMfxlhwuwOZvCm5mwQ1hsm2B4OezXLa0k6FSS/GWgtaAriRaLcNaY3JmfB57h/aYSzBr89yFxFGofMek1WYnFOfo0uZbhBLsOkzdDC7zNP0S17hUXSvx8iHo6z8oRCpijpgWEAO3CocoKIpiFvHzq+OYQ2GMgPPGsydEdsOtZZT0u9R/D2v+dwUwme50m6IAFTiwbpWKr1p65D2IIxxddxuO7wZWN92vo2UDL7ygtaUuC4wKe4uo6fmMuU8Swhe3fW1ohZsW2Er2JlXVI2Z398GOZkZdWYWc4nBDBj1g6q3Gi7sTZMC25jyiAyBCW86uQgZNg9wq25pLC+UbUe3APvr5tukS3zdxVYDmdYHPPcf8rsb6jfhGekCUNSBeJ4CseFzVSuQTZHr/Oo422A8MzCzwdyZ9IKrugr21drZIOCJGPZ5GtZUI5iZN5+lOc2IC3XcZ8l1rty2iuxCCzHas2Wwo9vbJMVf0I3uuk+ABAVdD2Ql5gTMs0cfknZhDiElI6zRtrmw29ZRnHIRuJqwcPIdDQKHt8zU3nIqQW8ZxG55KaYexywbp2qebCQbqbfcbRQ+VpEMW8kuKp5OH+P7KLtDV3ipneCzEsriJ/m1lK4meD9FKlldSmlbAVm36/s0otC4TzOwofEsGJnv5WjWwaDNH3acyVQJz5Is+oV5EezM3EwIEtyC+5j6y7FdfI+2dYquo/J7b3yiv8mZT1MRZEGxjhVD6pqE3qDQY1nd7m46ojl423A+TbpC11uMs/9aoxptT3dRkh5VVRTfN3bUs0Shk8qrQNwMQtvws6I525TRSzOyfigzsLB++kw6QeBXxrRMPno7boJyu8/AxFBKe7pZZkSTuZ+a920x7oTmx6QzdL01cOtNO7BYHfRVVkHDmY78yDYBcCHT59UpB7qRzMuz4HyZdIGutwZOpRYfzWJ28o0mzHxSlW4VYGfFilkVN6tHtJgoBubUpC9UteX8TrCyfYoxVU835OeGJyPt4iGAgr4kA5SV+4iIG3IUaYtg9MszqnwQM3CmfHZMGu+qLJtxblN/K+Mi2ZPvhrmDwSrSvHM0tHXuObiphRIKKzszA7fpiW9j31/Oib9IHqIKfUQlCV93c1bkOy3fKeqATvwMuJULv6Kh+dnOoDcz8J0B8Y1O6lS9tTDfdW7LemONSRmPamZxthP7Mj/TiWQ36cNYazGGO9/t86LCXbttDFsmtnp5FYjlGGgbjlM0Y22ZD8J2+g7NwHd64luY1JPsblGj+umjNfPJq8BZ+ByZT9HMMsyn79AMzKcn/rNjPtxQmrdO8T2bqHlCrCBnvBHWnveAdiCbo4rBlz/96oYyG8/KZ82kC12VxVj1OIq/n2ebKo+/252HdRUh1pXUseFgbbPPytfcdDQzMLPpfD6Lg7ZsMLrIDpp6M/P0cwz3YDiWBRn6GQZ/+IiymgSu6PxbyZ9SFgZgIbZlwWy4FWoAvDjPahj58kyqGMIMjKmYIZPWm+rzcGJr4Md1KlwDFb2SvYuK6uLb31FckSL0iCc+biR+lGV51WD5y+cSnaQF4Yfy95dVUYu6L0G9QdVgr9gncfnyRfud4qOmAGBHrnr0eBJV6C4vEgRiGcqftLjwLxJ3A0LTFWlRfMjjKE3+ibbd9MGd4qH0XfsQZXd1dAdj68rMOoc2VdEEFykbzpN2j4PTIr9ExS4py6TPcg4h5mFskOIV9DFqovloMI+AWvS03z+ElX14oetrnqZg1/B3o8ptuBYZij5qjuGQVMPRIuke6oA0Gd426TpCvCkki7ItM1iQWKYgvEYesIAFETEAxrRphFdWqUjUgWhRHqf53WVegrj6Mv3kt1oGOPO9uqdB0aeMhXCMWaJ19FEJUmMpepnEVV2AOLoiLQradQzCw/rmmVFX1S0GQt+7KKtvowYWXGZ0ufHEkXCsSYF2Er4EwPSoUZo8oOLpOtmBw6bLTak4RphUEJKO22mLdojTc5aklUQaauqYNqpkdxbGgOtbeB1vAGCmqDd7FCe3SdyoWcOQFY3AFfRCF6x20ZyhQBmsgHdszLwZU+JdR7DaMJSaIvoSFUmUjeGkTvLdtySLZMTR19I2/Nc6ar58zhJQNNDlrqOw6LppEya43ZF27IgBTNCP0K4NGTfiOgNNqDOLaejC2em2gO5FBij+h9cauiNWggp8xoU1sKFQi6Zx5ZHpSkOhGRrpoXEsNUEUyTCY9eKPIq/30l40pVpEn9CPUrad9mVaJKePeNvLovSoru7Jcb8VkvJzmApe21gTvwzC2gWDM1R4qESe8A4MAOq3YSBNKIwdzvRqiF2F0Ix+simnkm+boCGJKKVo2nSgGjRiAjqYXmCiOt3m3qWAArfzIfOWIRIZxdgcYRpkdDoOeJxcthMt9ahUCjDZmEwX2rGy8Yrh8fJxpXWnOj4CJni6EwOW2qKV7mCyUKxa2kKB52TcKQksaNmGCW699WQM5AQaUOj4WuaoGouqGl0b+U1rBAR7ZdSbPvgL2I8xmo7BiiYc/BFV9zkoS1kIAyql8r2Ois2iQfO5UKAZCvWbBMoQVoiUy42H0etg9/jM3+iA32BTJQNgYFjLYVNIF4JBa0hrYgJIDEVDmAWTTihsunS5HpnwOBzuHfjA3wK3Dqde/2+liJz5GQCD0yHwUAg+JoIPvszRa5DqlVTqlR2olDJPHbWmyt0+Su7Ag1lfZmBmbBxtr9Fun0qkGgdipM5/QBVWr+mrZ5lyL0LqdyzqaZJkL2QfLxng0yAzxCQ81ZCejKwGzDpFwydk1gFdO+tRWRfoK0ru7kFGZABM0b1L8HoqJaPmYbRIGY9cCCPnAa0TYE9ZrBK0Y7HBUZN1fYOPl7yjohFSxXA5l0TdpQzsAALe0ciceCzuAJTyA4AzvYd5UiHmYYwNoQqcHIiB9kzAtorrKxZCP/AiL0tcMVWg5GEMFmjvjwIvzdFbxQDR6L4hQ0b71nAIKWcL9aX8zXilT9VR3M6PFXjfEDpUqqLe4Ck0DFrqLiC4n5g20fsK0U2Mbg28ww9LLFNC0k4VeirC0JrxgZWk9ON9QXTUg7FPTDombqLM50KkoUk1+XANakNU5Xw2FPQ0aQAgrGz83lQGEd+IPjEioQ1ryklhhgAitxk1XBpTk76Xsf5Uz9NUKQlYAMWwaDiQVK0DkIoyDIqJFzWVN6p1QYKHToOoe05ByoY/uDBpiECjgvgAoqQDCRj3oJvB60gkBAwoHwMIDxGF92BS0AXGCVCH8qzyplAfxVlBGx5EPgIOEqIH5VmmIAWPaCYicN5kclKwgPpxsFPrTRYWHUAcNdd5KAg3VEfl+gAFpd+dR2DVbm+201O4AKpIiexDkOMkTan80yqqcKAGw2FrBKAPh3AmInXOlOPTMgWVBFj9qPgqKjqNrp8G5BIQQ3JINgEO9OodRpXqiggkH4gAC5GGcmFV0ERENbH6Qho8yXfN88rRkxamhwCnHgcP7s0wIFKAPlJSu6h3rbcqG3kT0vEgOIV2BoCD2t7gTqtS9SBkAF04f19/2jAOvTeDYy9AHRhSMSSwAkgh3u9YRSgYK6QaS9AFoFJvT9NTCXpMqRwP94oyEJW4V5Ii1iCbWGdoVXGRAKPYWzhQUPZQjsmqbYpHNS279P5YN0f7fZqg7XVO91MkihJePipVNYhY1MsHBa2UWKFtXToFDpSjLwFUbATCyccEgUMU4nzSFVQCMc7NVVx3TRiLrWLDBUzNkOzFIob2PNWsBCHkKARNaDhA24yyrxSScgPOaeX60DD8AkdBMrCCwQihegEIB6IFaAePM5QW0dtqLm4viuQuyRRqhACq3fH5GgpFwkiDEPBNbGDqm2VfXMkJxMDpR0ODe5OGQQaxEfuqLBRtpK+5bujXZFKSGVXXDt4Ei4LAqldsesobNS6XiczLvPCz0nVCf0iQVrEgAFvTiOKWJOZamHarEVu3IqMLAack3axEGw4540tMKc1EWO2whCoKihkey6SYpz5m8A3rmUwANR+VnsEcyTUrdzEHHvZRrpRo8jraQUqrKshofnrTNjLDKQTqg54PQXC7ger50Y+Qs7Il/NbZgJCaitohq+sriCt9x60ns6bNaQmuerx+I3tyDvgPOKBReALYYwN9D9QP+VWuCA4dgK6hzZgi+DpRRySwXTxKbK7crabwjMtM2RHjOZUwlcdRufdFjmDXQwhMf7aloL3PyTQuuYxq+x+KKoowEqa2BWMUWgqYYlJQWh1MQz8Jxl2Yz46h6pJ+/zap7UUV/U4+5ZzMqkSpOsLGTHGaDgaFF1VoTAtNDNMFYHa4GDRTTZHDCnFfFg5rwYnO83I9EPPmhi9SkNeoupYEJlgUZIej/Ogpb9SsfBam0FB5jUreU/NJUSGxpZECl8UEOUyOqmGDKVJwRnhxZLJw9HWdBYfJkgkqqZZaLGCMqxtJocVcqPFYk0mJzmKO+oAp9hOl7oF80rRc4nFgM1SpLPUmU+XI7Mw2t5rTh1jTOmnCgIrLfwge9CUYA8CpfAlAdPM5at5sot0+RWNEOjn7cJD6OWcreLMQhw46WMpI7kCfIbrezQkTilqkjwRSPiC4AkQfOv6fgkIShAqfefAJpxeVFOcHEchkKIozgjVZZj0HXKGHBP0wOFBxgNoVwMJ7u87DWGck0XuU7m/rNCPvYZgCLc3kNQ2HK0UQlqryZhRrU9aMA7mH2J7KB0AAlHx8IjBEMjriqIJYALKJH/qMUUr1JOHANMNgoaVE6eOn6qjCoZuDLDd08FQJTWgYzQgoUAk1Ij0ZaCQADZhgsCGZo4tFq+SMFsZoHtuxhOGJFhdMDIGiXkKDDumrEhsUnMlap8YSQnRQ6GQcAhPYhzZfcnzco0InK8jDgRoMia3hvSXJ8MqoFeqdXB9EWfmoUgSSD0SAhUhDhXVW0ERENfGjStJg/1BxjC4N00OAU4+DB/dmGBApQB8pqX2edXNHFODJFwwpHw5cIcCbbhbfDMctVfRwpT5jVlE+ZKP6EEU18dEVVDZrcmK9qInmbRBpAISTjw0Ch6jXB3xXkAlENXF0gbZNFb9xELruqzjImAZz8QQUHf9mzPQkf74EV1DYBlX1VM+X+BD8Bm+Z4DYUb5kmo2SXy8CQjC205fhafpmQgG0DAPVgZnZdgs2+LKUXD6JZPyOkdBXq1Soe0zwkUJjaRCCTzitMbNaEmMew1ufAuLnEPb6PSrT9mlT3VE4LkTS6KvLBaWpCZKPydyiopkMsY6dQSiiUmuRmTC8ipyFcQT9QsJ6KfhaSSd0GQErpHIWi5Gd6Qo3JydayHC9TeUrCsg1NLvdIhhmN6KdANHJqhJRKvC7djU7iUZimJAGQEkepjirh5UNSVYMoJcnooyCasoWJ9Viobe0K1VeyG6zjsrHE6zpXLs9auqRI8sXJQcjHxQKC70zGNE+qNyUsnikXJp0S6mZMNCUnAguoHwMDryKJXkWDUUIPL2RUdj4691l2tBYFHlB3Bubgvc7TPK6JjQpM+i+lNJdAyscDV4CII2QpU1BJgnViqc22KhUyEJjpUKTixp06c6gEVLo3jTIAQqr2F6gCvKmwSemUmxaIdGLuGTuvl0BSWAPLuIEcsrS1zyyN+Fx/mlgoKnDVDiStBW9tQo5C5fYmRz5XLBOhD6p1KQe2GKRqdXrSb641KjSsMG9JYS3GpTB2eVJsJp+yPE2/5FWT+KS5HBrD3Msi78vA5WNT1PJ3GVPgloTol0T7d9kRwBShN0BWUmCHMK2rkO+GKEAiy/KgqvYT0/agRQ6kcw2gt7A5Tm/Os6RKolRxAldVUCkcinqwMiNkaFXqMyr002p+YAbaGzF7rJ6Y0rrmA5ehMCGxqdHIsEWA6tpJdUniMObBvRFy4oo0V4HLB62oBaZ1YDIBK4ipwgvpmEJa4KDkU+lHIJzZwFRakROl5tKFdDQxpIWOBtqxzznmIZ3zjeJwJgIpJo6HBdmAyjCtYgIB18QHsSGJtPYdjgRSxdBQBXiNpCYuSxKEMziGkYdh5JlLsouKp9PH+D7K7tAVnqYxJTRweNBWUuj6urpwSqZcF2hejxek5pgUOywpmz+MachCGw6SqRSCaizCycklZvW+YbNyA0TT1VGMVFMVJCCcoFxFT10r0C0Bm6rcm7Bg1vCbswQWfgpo+TDllSAyyvKdK+ioaGDi55twy7rHwAa1bAereyYcjKpzPyCmenHDJpBX0pWFNRokU0VDQ2PKsUgBetHl03Ek9dWcIelKtmxCk2BKdqTbAd/7y6fMxQaABW+L/Yb83MBBXQAoxSlbAAZP8D2U9tAuooOO522RDKPriyU6wbr+3SMMLR+avJL0fReXHF73eAlGPvE5DKtTD1jx/IhPMkSknhX5TkU6FbjC9iuvBQdMoMF1L5HlqOcl3XVuQTgK2HhsY53ARKMQT0yy890+L6rLIr9t1C+NBq2Alo9NXgmiGgOtJpoC8QyK8umjDeUU0PIByivBb56MKadAPBvl3qF9mrcG464jcroBsLrBiVXkNBthTcgGYIb0OOVsuKTHjOLv5xnWpOPvxvuqto58rLqqYGJRuI6aqNqGps7BKmlfcf2tq2I/WMVleFCiznM1/hFlNbmJ76yS5E8w+x0AJh8SAA3mvmPA1FSCUIJH2KyWoFMQ57fXLQpyFx8lGSqGst9eE4m6i7oPv73GIDHaV3WUfsy3KC37go9R49hQjjW7Ly82+ygm18r/vnn54nGXZuXvL++rav+X16/LBnX5apfERV7mt9WrON+9jrb567e//PKfr9+8eb1rcbyOGeP9b1xvh5baoztXSrJlbdFZUpTVu6iKvkUlnoWT7U4A2+yiorr49ncUV41TwiO3On4b6Nw32EV4akMniHNHoMkVWA9OfncmQdJUY4V8Rfr0CowMMdLwDA+LCPFmhIiabkk9XHMTR2lU4IWyR0X11GtQWzzyPK132fg3z3Py2punEjMg+c1iob+bYzsv23pd8AymW2yROc731S7lx9h/M8dynG+fTsryJI3IxRKNiy2xGGsWp/UWYSmX4GFFe264QqkNFS9xX37g9Y4LKsy5iJ9iCMAcf1+ZRTp+Ncd0nVQpxzjdJwsOvMd6CoCI/m4zK1WR81PRfLLjFZFHbDB8RFX0X+jpRyuyaUxsiR3Gd2jYSEWkTKEdXoD41GdzXB+SHWb27XXeX93QGIVCc7xXKNui4qj8mmybrZFGy5eZY21r/C3PuKHT322xfS2ifef7BiFlim1x4/XwA5gpodAW73FOPJJ4EcOXWUiXIskLvJdx0mX4aildrqM7QMA0Xy1kTN1oANf5UZxyUoYpsZLR9bc0Ke8B2TwWiPh+e81t/7yG8VpQMTiFj1dYzNSZ6FGZC9tCqxkwCVe9JrqNqvY0Go6o29hqNe+Scp9GT51rIo2JLVnNbOMPxK3Wb6I7JA6TLK251glunGFZFN0nCxFDiMAPZPi4Gtb4kOPOJ/9E2677vuKAx+ciFAxwTMM5bR94HONXC9WnC5nG46K/W2AjBEFYTeyCUjIYuTIHrBKE9riAdcMUrIfrh4h2XrwuCdVnwuLSqmuViX2PT+qU5KOE2XooNMf7OUv+UaMNyokBh8XKFZnjPEuju/Md7g9xsRGHDhRbHD4qTmNsPix/KJKon0rl86dRcFops6mK5vFQ2VhhA+xjHEbXrUyLZpo1H3YP6nsvLie2xB4jsGtwRTbHMHK/f5nWd0nGn8PoEhuM13kdA8e64fNqVsElKnZJWSZjYEmfFcBjc+B+PYo1GbGXn7U87REHm7oRpd/8qfBMM4lHaZr/YBF0nyw2RY4B+R5B5ebY6dcLPGa+bDXsposWbM5lquclBsylrr5WNfisQKiPwcBplUyJhVUzejx9RLs9ZyGmPlvh6jS09tUhh5Aps1iJ5MUbh63/Zn8X2L5bgq4C25KfVcznaeop1TEGFyEOVnsOKqf/am05obtfg5hkKFrmoEWuaS6y91gMXjZPMbj7TqbMcuf8o4m9c52TZAfALsoWz3E0lBtKMZtjBkefK85FgC2xMeNtQXz09/kO7AvKmz48hq/UgcOCGMoeWeVpJBBpUdDyum9zSp5P9e4bKi5uv7QBQxlUbNFPbJYRwr/4MiIdHsaRHdUopmPKdhlArDmWhJ84DY3PS/L74vZ/k+n23cz97x76fe/gMBOt+2ZlBzI7bOf7IfgA06Xxs40CfLTfF/mDaEoav1uMs0B4L9teZMI2x5ZYWOL3WwlGtmR2LuWZ8zjN77q8PA58qaw9EU+2zV0Tb1N2qugCC4c0PASSnYrvFf198Vm6VCWkNBHW6voTSeq2UUFMj5/ndYZsRy8yDv3dAltUCWaL/puFA26bzfO/ET4+VBF3GyYUWuP9lMvRDmXr4m4qv6kvoytRTcrzbfsSzh8LLdwJo7IbDedKSH1ffB6ZpIAOk6epP82MDY3yaJgCC38nwUYgMwvMPDtUBj6HuVHWnnanF0U/W7Kc7tAnK+THSX9f3QEyzEWFxyFm7tPLH3UiOb+0JRZafUnSB/LmjPGrhVmtjc7BWNTaT0s8NenrYJG3i0SFTSi1x7yJ0grG2pZYGGS3uyTrJRFri2VKrLwS4GsjpsCih308O56QTMHcV0bvUIqEx07DR/urpyE0D3T7NBQu9VjuQ4T3bdjewBUtaSUgXfmQ3yUZaGIXS+0w98E1pcgFAIvZitK6isTXUvT3eU93zXtVkX2oz3bUE1GNXy16Vacp0Knhq5Xuso+yJ0FvaT/a74qt5RzeF/syCytKUlT3hI84I8r42RzXH82zMxZR/83m3UHVDmOD+bEueVkMFNvw6w6RR2w8Uvq7q6OBgFMotePj5hjzJSmTSuRmuswOK9F5ju6ELZgrssf5Dp+NYyQ6XEDlq9Gsx9i0Ppq1JOiugWItrTmNXn2Q/WaYQuj4waQ+UccK4TkO9dnKyaBC+NtDksXAqyyu0KKPwsPWE8tHrd1CeMOfC/qv1pjegpje2mD6W7InRqQoFd9kcEUWp6p7vMlAGzhTYLF+okcIG/V5Di15KbtIswZ83+p1K8nFLCKrOY30FmWbrVxrfJ7K4yRNhQsQrsgWJ+wMypdZ7C0/8g+oqlBxXgJPosRSC8z3BUIq3EC5lcMLKpIYxMyXWcjt/jk+1n05oc2UPLd3UtKdfKLXYd0u0JrDwA2iL1qNjGN2Zc+n6zQqlwfs6vpTXSWEU38CCNBv3wp8jgFUaLbkuS3EhZi799Tw4+sei6MXDFx1Gm52jY8iP/KE9DQPcaC7LJo7cun7C6jcAnsSk5fVwj3H+Nm6pzKUQqnFdo13ZCwGjjltiPpsvaavo7vyPOs6Ba5tDsKCJ1cRG6xRFbsroBLQIsciC5ydQ2FX90S89YAhLHaUvNI3IgWyMTTecURpv/w87wsOseFCKrrP7ih/2Ye59VEELuFAvwZ6gLTmNGpA1xx0acAV2dzXF3hkTfibJmQQ6MApgbG4m0nK5FuKzrNt8pBs6yhNObkPAljiT1LBnkh/n1Phb8JtSqWIWGp3vyhFLBQu6WXRsyTaYT1D9I0Aiq3oO8HTv76O/EAEQxy2Kw1vNZpY/2K90TRBXY2HsFfZWn/sTb2D9TWq2ElZk6CHIex737hnw/QBIZzGIG9ECuRgcQwWGnQqVWfzveY6SD5YrI8oq2+j5pRX4P2xgq5TZDAWnhEVH4mn/WJjOvgjua1OIt5rkP5u0Z+uDqSC8GXmWK/QP+qkQBfVPSoGjY7BDUNYtzAqLzB+ptxi/dZYbuGFHxO15Wi75bDxa1kLbTO7fb5DfnbH7xZGg64OP7P0dwvf2SxtlyeVkpHxogXKbdbfY/8UWIIfhrCnxunjPikaA+276KmEKcPD2LfSuOY1GKC1JYey0G6ichNhZQvBLAMU23iI0DUFFwGh1KrXrX9RgZCom4qldr7dQ0XxhQBQbLMur1BcF4VwtckU2MivrtLJU5yiDyi7q+55CQZBuLZwiYokF+ZRBuPQSqNgNGgESQxBWHkr3yf70yzCp0lBKDJFNjjlgYv4MqtbxYSs5Cjta5/ck3Q1wgWjBGo5f/fz8rQUaNt8svKB7GOXAw6QdJGVTkYuL7IHvGJxZXxWuReZWApkYxTN4+9/raPGEMRbRZkia4N9U//oIUrSCLJnyKHcWoIHAUNYzEOSKbCLpRangfxHO/bOkV24wALKrU5Jye1TY+84y4u+f8dICKWnBLS4/oji701OCJIoSniOzhdanrflmcaEg7d5UjJ5m40pBE9tsqt38LzDELYtRI+6FngI8xb6OpsKcXHC2RJbjE0KrSJPxWtNqNxCN0q2qO9Zh4JTjyAASz5C2w5Dwm/VQLGVFCL78HH9dFxXFW+4EkutMX9Nyvs0KSsFeh7EgjKt7E0RXv6X+FwqWgphCIurGHw6bKomMf8WlimxsccKqKxxXKRbAM341do6fEK8KCC7cFtgsSfvUZxEKdA7tsQN43DZSR58iFehSki3FrvrUG17PJwFh3Xm1tOsQkUJMRoEYKUFEEHMYEEQ+ygBrSwChu2pAK1OptcJatchJxm5Iiv9BpXVUVUVybe6Qif57luSNed9YBxaYKuxYJnYPOEk8ZTShD87gQDm+L+i5O6ez+LVfbOgDnDutT/pfk22PJLukwW9gPG8tx7PsEeoxYsCzKEtlWCRAi3p1RvU1THwm+jeAYC3M7CFFjtA0DfRa4pyppiR5AEVT4TVBKsnV2avyX/OEsGbgS+z3THL66hIbm/lz2k5AGun5YvbiyK5SzKJ8zJdbHPWLFGnMACmMbHUAfNHFJV1gQhdJdgZCIcWjnaim5xQ6ICX/FDipgEs8NfZNkXNdbloXxYKbfFeooKEnIHNkhIQxzYIDdRNDBDOo8hb4ybe4ZQjocFsdMMkk3jsckVWOhPr7StoTHzxatzmBlXOy2+ux+LgOCevOo3nnLij2u6lfY/B2zmuzOoOCnN2jEkkuDFxRfY9lSGGyu2xQ+KSL/t53Iy7O/oSb8H7PBPf6EHlVvs+iNUNWz8LjXNMcxyU8SsLYemT095CY80M8smhC1cj9QLl7PVI2Pv8svWGC2cjdVJ0dFA8ziusa0uxAsUWwuOxIhHe0g9J9p0TH0yJjaq5vYOUwPGzJa5N9cT7ZtLfbYz8SSQY9ptPNsbqlq9lvr5Q+cFpVo2rdbqX3ZGLpRaYQ76Gw+rDJvknf68wfHV0Ei6v8w1KUVzB+HWw9v2/gC5HhULLy5arKLvjt0emwEJBmsbhfWKz7/NxB16jkTG8KfV5GARvozqtvuCD80dBIxYKV6NcdtLT8yVaZymw1yylNadRLLvmjpMsKp7AbaQvsrkv3AFx4Mavz/GGZ4NykiY7EzRopsDm3uET/467+2TlDVhEWZkIzrRMwZIi4CPaJhHhcCBEAV+2GgFAd8xPCtCYHESBuvo08oD0m5PUzZfVzE53FxdGSjO43J8Nzy2zA2ZnCXmEeHbhZ+hnXZ4rncLkstKV1ddqUgptCKKpIDNAyGAORgg1roOp4PmZCg5H5J/9iBzKbLPw7XN3adbGZwpxEU0h9LiTVmKZSH3umpZd1LlhowZyVadI5ltmAG7lwgzfDTMFFncdbWRZSThHsdTGoto9p4NRA8U218VlhYV3I2iHqBZ5KuyjKjiX1i6lz9shCKcWoifCIO2rOXkrHJRLS/0EgNutAsxtlhphoJqeAcDqFrGIxJMx9Xk9IpnytPSUxRQmFyGsrL7WIw2umxfv0eOXKK0FHw6myFq1+ZDjQlFi00VLqkvnZWeS502Jw+fV8Hgn+lp/QuJMGMQKNKJzNwSpcKzfFtQ5aMIYhUJ7H27Ye9tFIYL1nvX41vkLognjziVphYrhsRG3H4ulFqeZZIuu7+vdt0zInsIVmePswgmy2IaPC515f+qz6lqE+sCDLUsGlvEc9hAiX4tymh1g7AAssuXZbKVWuX4gQhZMusABX6M6SZEOpTZKy2WBWkMgEFCdLlobnwdyYWWxuXiyajGsX2+Ruda5OdWdl2dY5NZjHDeer4Tin/iGrBtjuIsyAKE70y5xbRaScen+qy66Dgzsy8BNoIXbJG6eM1DabQBWhlG7M7UpvvWzNzyS1r4nWGk1sJZ3iq0KBucjo8ps3XZ7fw+J5y5T/BMvKtVkeaaoUmB2yVhlhW6aFQX3wYz/588P6P+EJPzpuH1/D5hkmYJlfCEaqzBgKF79ap1inQZboc/nwmCdK2bK3Wqa/fUsilG1yYtKwMmWWGLsXcTeJ7zRGCi2ULGzLXpstxHygWMAsXQ1sqCb8yZ5VADdF+NxV3TBymtd5y7242Xn+EtUJFEGRhULMvcK/O48YYV0In3NM5+Ef6aHeTJShIgi+bPFcXuu8YmOyjK5y9B2UGF5lQQot/LxfDaRvc7LJmI0x9jj12UsIaPW/T933M0oV2QhpyaIXd7obxd1dXHboGj0TihWsAiymt2PZh2/fY7G5LCjqauvVc8h/16mdRGlIp7++3IvRA6uVPPoipMoiOG0wudoDO8qSQ2AULk5dhJ5Bn8S8oLQ320YuE9lxXPw+N1h86OC40udADiYn9iIzk94kDUXYLHNvspCmND8N77QW9U0ZrLwm9W6zWQyzKeP+7zoFxSHly9b7YrvLswwXNi1P+INIAVUyNaqxIbZa9fFJRNwSDDumFkfA3hTN8Ms5HPbm5a6sFSaDo+2f6/LSszkKBRaGAIbg50MsVg6j6PofHtxc5MNqcdMgYV5Nsm+Uy8FOTstXzjlg451HW8bck5yxm19EYIddCXoJpWufB8kohUCO8jVg1w9yNV/Abk6puH2kaFDfmp7eSmvOo1s7Nv7o074CzCmxOKBVjnk0f5ccMZ2vsy+nwJKT3xQfEi+zEZWZhVqsyzwEpMqsHrSCERmdAnLSKKCZ6VgA6I+28jGQ4hHXYhH6Tw0S53HN361xSTqCvR3O077ggqRQ5gCi5VwT6JCpTlnIKQ+r0bmU3kQfYT+gMZB6ivqrv8CCAxM4hCMRBWGxj30TLg3+iH8ik6iNK7Txu2rDT3DP+wTilezTM7yot5d5qWnTXdA47BMFHWnWSbX+T6JeRTDR3vWlrG1/Z2oeBtqtfteHm23BSr5i4vxs8XYVvTEX7p0n4+Ta8PjDYsFWGcNHteFJqk8zUprWuRRDB8XW2mEBND5mimw2CrbnBLcVtl/tHo41Epi/tVQ99XCgpCgHxya7pOF1SAqK9KwYDCgvttjk80kVG6PvYneBeJtSw7ybzn5F/kLPleZN6u4+6PI6z0o84aS5+wo8mnYx3gp1X9eQuCRZQ6qdUzBQfgZ8swhYtREKmAjAgKogA0eV3EoqTyNTFyfBPu5uVt+CTRNdHb3VGsLrcNPWA/2j8LZY3FYg/Kq06zAD1F2VwPmbvq7xeFJzPBwbZvdoYkHyTFL+8lC16pTjt/aL9Oqz3NczbPZ20VjLl9qY9Ndd7brNmdGvmsj84s+yGORBc79vsgf0LareyI+ZoQhLHa4vNI3IgVaXoKH371+7pw1C+1bfQboo7q6x412MUiuUNzQ0mcvU2F22N/s0E2z54U1GJ7uhAjB3Se7q3pClfMtocltwuueULk99k6D1TUCgJm3dUEm9jr/jrhlSH+3xHYUx6gsZTiZUqvb0IcEa/Ky/CNQ+WpWe/dQ2WdZt8cY+/UrqTfNQm0agyJGMAWW+ES3J+rzclfgYYRRl3umu9PjcYqlNmp5m9JEghootpwXrGVWtYCXK7LvL4xWLLUQHW0eGRixUGiNt/UCkMolGZA9x53URYGy+OlESGgNQ9i00Na7wgoij5kuse/zdfTYbU/QFZ0cysa/FoxRQ3225ev6W5VXeO/O4hR3DGJvHsKxhdNHXQsDhH0L16T+kPpLNRYY0rNF5dhgSNsWO5GgGBsP4diCYiw8hGMLuK649mAIR/mE5XxCNPcoPUMIJJkBeIi2QWIagIdoGySzAbiFQa2twh2Ux6+W/AFznQunwdlruCIrMwMm1DEuyXiHO67IdsRENFzh7myFl+tQuQt2GVYbbFfoFncBbaF4WnyZDdYfUbG9zJOsKr+iAmFu5A2aEhAbr18Uf8/r8fGU1BKjhvRoUQxEJQGx1zdk5nGo3MLseHubpAmQEZopcDhD7CVniL21oZWcZPCKaBffCWYRSClSQ9q4yBZb4AHF8NUOk6g0j18tMQFjdhvhx6j8jrZqaspg7Pp88vDwVuxx+9UO0+njPilax+Q844MaggCu+P8bRQCV+XI3Dn6XFCiu3qFvCW/ZlwHZXBYP1Y7iZs97n6fAxbEMyqcliIPkUE4tHUfZd/FwCAI44xcXKwjghv/8RI6alDlh7XLvSjEP5U7Yz79FvCMDX2i/LzQ6SXezBO8QLITFSquxllsk/2yWafOKK4qhlBgqOP/WRCZVQ/q3eIVKISaeDtZGOu7J/a2CnjCETwvQiORQVi4gg5anGJACzObSvojvoxJJ7cYggM1JMIFvtJkCexsl9C6IL7PHSo6IeEnv64p6XSSzKxpXsnHNSpHwkn74+HM5UNHHqCu0i5JMOG5KQMzbeB+RF7edeeFTXg2pJth2FGAWci+O0b66vk9wjyP8ubnRfB9l24sH4RCgBl3NpVlvlvhc4vPa+6Ss/DMWAihd0haaoZnmim2w0/LrlPpuaRQBr3AstyfnJb8Qc/2R3DZntoDMBaB0YS4zNNMwV982j4X+biG1S7T9mlT3IJMJhXZ4gYRM1Od/AcYNw6se/DnbM/hOBaS4BUg3LIey537oppIvs9iZAQuxvWX4vOx70CQTiICIRwCA/djxYXgPnc+gchttK072JFyIqMdyRQ44AX8uvsxCF0cZOWmI6jb13RYb0EGmwMbBsSyFhFrDRxtuGsneKJxQsG0B4CeWqoPECOCk5fiQQFF3Qmct0qDEwWosml+fvAwY+yRcdBGSTaMJJwLen4ulDpjB23Gx1IaSsv669lXeT9c+gtfyDpfv/Rmo3V7BQUtAXNsAySABsVAZtFezvleyU+QE6kOyAe8luCKbjaqvKlV7AACbVxgxysaAd2I4RaHYou9Yfn4FMoPR3y0cRetsmyKyy3AuotR3a/l6QuL0QBK2LbAyHU6QpavzCaQRCCtNBFiXUoHVJ0+7woDGVamA606pVPgrAE2fGSTtF2vmuc77KzOQdejin1i/pW3ZYaxdIkYXY5cRlmk4NawffzsETpZ232yxgF51XJGdtQyyQdDf5z+BPrsVRO7b2ueqPqumx+KSrFxadd2S/LqI4u94YND1Ll9mgZU4bELaFVNgeQeL4MtivsxeLQLRCoX/AqvH38RCY/JYRXMaWoY2AY+B/ruD2QZem9ZW72cT9/1rlKaoCqO90Lhc9BZN/Yn4aJUvGEPtE2HuSdoaoAbFFTnivCSvGzHJFbhHkCV9Zq5QVPJmo/7b/Pre0XaXZKBHI1uyGmlzhaq6yEjKUeQbTplB5XRIUtZft7AJvV2FFV7h7gbapXWWF+1sQeuOKrTB20w7auyb4nrmCp3xyj3slID28wYHBBJLbTg1ur1tzWwcs47f5xBUckpTyxd+hC4BsWmDPDm7zlu9hEfOlj1PR87FDi558+cJFukhDi8sNqcDjA7FuveDy6jojl9iJAa6xPZmBMLIltgc2EYaQ85AUPlihseAN+VT3Ar2o8IsjAqSmkxI0AFDLLGHH+Qd8BDXS+AJ6BwkngGOaUQe+Ze7g4rs/L9Izg/8abcHcoH03+28tv5RJwXkrNV/t7R4Rt9S1IkKGLccyqbf19Hj6SMSyMAUWLmLnOC1c5cXQpIurshB9JEscEWeQmJfBmN9JxkwWPF52fg9IJ6w/VcbVwi/gJRrEVgB8uvCOEOIrllz6gqtCxoHBDCnfDzknPWTCovZxuK6IM/wu0droTwKIKxuXgWGmKZZd3zz4mlfLD882bLmurDcFoDNZpPrT3GKPqDsTojxQRdY4rtERZILDpBckeX9fFObD4VFF1gZ+QIHOw+nOYV6N32eJVUSpeAC58t+4nXeTAAu+JDf+S1xCpHD6lbWnmZhU02C5w+xeCmrExybxN42/pyZk2AOxqBN3CwvJoUxTGQMbuJLdKH/OXHMFs15mjjNiKGC68/wcTUs5C3X3OTZjHIMN/UBPaBUeE5BfbeyxhcV6IfAlphjJLllQIRMgcXGvYdT9e5dUvWGvR3AI/lccLb84aPV/SIqClQIuJiCJS3tmLfu+ONz/80cy/uq2kPBfejvVq7XwFPi8etqRNIQxtJzP+vRuOxk8roTKVtwXHynePihTgDQiUR+HlnQl3cMdOXvzUsFzXK5DlcjWOvN0PrTIJ7fZZjUJ/dRccef4bmin/6O4ShOQ2RmGtA4KfrSutOweNs2j2P8aotJXDL0d3v96CpPpUGe+zIbY9D5lk8b1n9bDRtiHSsEGw5oHNhQUfdfiw03ac2FC2y/LHJz65QSdKn4VShDRRIH8i/hsbnEs9KiWDtn/xd6apNAM5jGr1aYBCQ29YGobNYR2WwdDRfi4+t7tENfoiIh5iU/JmZQOXCwpv407Ns0yl3ttJ/mVKP/hRiu04q9TlvNqySHQxZcb61nK8FIZWmc2pQpaEqmv1tgI7fEohGO+mxhuMyLGOFu4P8fpSmx43FnKBDAwvyVl/zLjO6TlSEul2S75oqWPN2+r3bpcb4V9l/6u811WFbhhdO/6P6Eqh958Z2/HYNhrLyl8IJ+alZjn5dP9PuGYaxbOX2M77H+iJr44urGZKCrEZ1dp3wdlPqxuTgCSquuVYiqkk26JZkUQ7w5xHcji/1DjgvhSFB9ka09AcvLXVRVCR8DXiydz9glXaD1tzQp7/ndifq8pGBd0xsK6ahzEs3/tEkhx80LV2TB3SQl3RhGFFQgZDCWrXyqd+9QjEVvWgL4mVKX/jceWpr+szDOrbxDWb5LsgivAWVLDJxza1c1LzVAgNVsW41o6D4FUPw7MFf9X1p97eaRwIa/Z3JajPFOgNn5Oiq/h3HPFjG6KEJGWKbhKLppYQK5MqtHWXUmJo+hPpvj+hjF90mGRFZlCiydZsGdky2xOIAmWaNjACi5Iote1nGM0BbuJ1dmseqLgt9Uuk82R6H8jjgDXCJ8qhafp3CF9nhBHyShcJUyJZws8RQiz+dEdZQmEaewdZ9stOs8O33cE/4Q0/VyZRa2XCGVq20aV1vXS4XxAe9U/Fvu8avVPr2/yAApwBRYzD96rLBIFyQU/d1m13ifbLco4zeN/quFjltnWAJ1GwSn3bJFq5EjTMJ1P0HCoHKQJJr604gSplFZfj8pkJ2bq9TPSChc21HxmfoK/bVGNdo2mV2OqgqvPf+naiBKB2Y3xDMN01ON84i4IruzGFaRiNFPZHCh0GaB8i/r2i82erLondR/s7BTCdH+beP8+2stH5MdEvWD8asFJrRNom5WeNrwZWtczsEWsd/SnW2XCqaKnRX5jlsKzRcLBTXn1NPc7vHCPn3iUQwfLZRvLjv5iVVG8uOY0xybDzbH5G9/RzF3CB0+WvQj33Jz2n5Z0sS/wTx/3ViouDvy4bMdLsiSMH62ONY0e2UMJlzly6x6uP0YZXWUpk9CJ6mS1QhBeqh+UpDG5CAG1dUnMkSL2fus8/Z1l2nibswU2PlpiG4aVtI9L3h7VvPF7nFTJgxo/GqjbpWl+JB0/GprhtiU/HyNn63G9w7dRnVaYamGD+Xk+X0pDBYCWc26PYl2+yi5yzzdHjosLm4P0qprNdL9zLvsMz1Rd4bwa7TDotLXYZxD5sDTWgxrZe11KNEf8y1K29AJ7AmQ+m7z9KGs2poF4sjDFVkp6q2e0b4E5TsKFD9H8RLu+ck0SvJU3lPuyrf0Xmewpb2BzD5AsQvut2rcb31w/6rG/asc90Jbwif0o/yAqgoVeO0MNmy/nQHG6bBBmCKaaJ8AWxdTPKvg5j0c2T16n9EoESji+de8IDnOZK/0gOLVrDPiEIc/NqXe4R0pXE5BHdX116p1hX7oH27f3sQ5b9bvPtn0BksP4PEh/d3q6UiCv1/gb6JLqFD4c/kkE3UVf4qJSgnhhcpXJSeCCQkvCTGveOhkkhDgdfxsh0u8aBu/mmOC14/LwgnxvPLZRTc+y4t6F06vFNA5cLYBjmkYXK1D+miOYeOcNQQSLtz7jzY2kn0S83iGj3PsOEv5ZBXJA+5XZ9zydMpicLl4ZWkQTMTrgfR9crkt426+zIYzZTjZkvltUSSZCdej5ouVDz2K+Lnrvtlg6SKuHT8d1dU9v/8BxU64r1Cc7BPBHRyG+IklxkcUlXWB2mQKvkZ5CpWTSV5Zf61Hwymi6F4RZzdOhWw//cQaWzf/7xLcZOmtsPHY3BlSgeLAkz85T57v9nlBTuy3iW98IAaVAzdq6q+VFc/ydAvFz6W/23nkQkH16e82jib99MEvVF0whnlB0072FSJ301vRgRgothAO3xPOztd+sdAVo+/8S6Pmi63l7SLjb+/o7+bYsDQ7S1C6JX9xd4xckT2nneTZbXJXF4BvugTEglseqyISZ5f6bKF5Ngj6592s6skW2XgLlHVanWe3gsPA+N3GUt2898N9ULzdpUpXswlsnrI4zAP+EZHLe0lV7WnE/2mw5/ubvC5iJEQmpD7b9krcUujvNgsnq/BJV0THFNiO9H1U3kNDbb/bGtjOhSQt42dbXJuqkATI6UtsMR7neQrha7/baK1ZDL8fpwtWIxZOH8km/A7t0zxAJjMem4tftRbFNFKi00nFuGLD5zkVzlAqWNjNb5wVSN0US5+3B7dv7EaSqOO6iLJylzTP4CGayWD8WtG3YatEtgdu8TEiX2Zl8m1PT4LRt/9s6/YH+0i6O0g2NUHLNFuytHsi4e3kAX0UouMxBVZrUXi20H9b2b4VxKbBoHLesQ42jYa8WAxUWGiLUoIptDUCir10C0OA/3xI8OikkQ6AcisPpFYUdqzAuyGxhfMrAy2jSu0xQPGc52IFZ2GmEbR96rPV/BOZLRhB6O/23NSaTkSCQuVLqW4Xt7cl4rax/pulJzzg/271XiCq4vtN8k9ufVCfLWYAL9UmIDhL9+Hr0lvzSb7bNzexKg1FCmR7Y/u3ZH9UxPeCR6ZYaoE5RVHGu1QOH1ejDhxH8ffzDM96/D2cu5QEqYOKYIxp3e4k+M9tHQvrg/q8lNPV83NZwAvoNmrizxeBnhMCGF3ui43QrFWp/ZKgH+Ipdfz6E9/ynuCJusuLpzDcxGNzenGtQ3HgotVxUSfLwzARh8zJ/VKD4cBCq2Oh7gTZTZ3nIwwGl8tDDA2CyS4TWo3ojURTeuOE7a0E29ufmp9OirwsNyhNg3AUj81lY9Oi+Hm5ajG3yqwOkXB0xOPkSimvPNHBLaj1szdBknHAxsm2xMbfvtuXOZ/7/qs5pq/J9g5Vf8v5wMH0d4t+JRVvf+0+WayPVee2mSwmRXurdZ0fxXyWOKZkVZKBpLAJJR1GXI4SQoVgGinRtCpEMW2/2VwjFnhIpPeCfYcpsbePi/KLLbEYKQmiww20/TSvbNjcNz4qkuf0YqmVRbu7pid/c+taKPxZpNkxuksy4O0+/d1qdn6cPu6jbCukzWRKLM6D+VmepvkP7kw4fLXAhH58TbKtgGr8bLGfxzzntV9sDMNP/FroPtn1AiMQ3riNn22cWXZCSO7+m4UxuCxP0qjkr1mGr4ddVr7LHpVlHieNEziQu7I1zd9Qd1SqFJUitJiLsoXpQIDNeyu5Nhix3rQ+qcBubbTVDgghHiPUGnrg1LnrqMD6q2vnOjyWffvtNTiLDhN9nKTk0r6fHpPZ5qvIptxmrlmcnjTlkIWcdg61H2Nad+wkz7YJmc8X5+WnOk1/f3kbpSV/za4bvTfzYB2guSm/Odrv04TIx+4iIFHLC3U9no166P6SwYCdVA14ztWAOgA3Kbvpx/o9seaWJ+KQ6FtGS67gqsoYgwZzYg6mnVXzB9tTPxahcS3PJoPF14pDxloy5pAYbo2o3WNfNUsMnfTjhg7N3IzQN9vf/kV3Sg4AwSU3iSOMweQDiD1n3Y2gxp0LMtsY02IKZ//jKk8NTxdcDZm6SYPZqJ4M+jUeNdgOeuoHFK65WeAK/YiK7WWeZFXZZeO9+Vyi7dekuu/sSaprYm1l8WJYqGLAF9qGPGeAxRWAT/QdXuMpRUeGcAJnc580322OuEKdEGdcDqknH/HYQkocHvcaGUg/fj0L9aYy8oQ6SjKSvpQFGWxx3Zfh77L/sGnTyDV3FOVYjzyH2EUNQcp9FBPiYoizpCgrwmnfohK1IC9f9Dcl/Z1v9wb/H+lJ2ob16gGw4p7corK6zr+j7PeXb3958/bliyapLwknn96+fPG4S7PyL3EzjVGW5VUz9N9f3lfV/i+vX5dNi+WrXRIXeZnfVq/ifPc62uavMa5fX7958xptd6/56h1aIyy//GePpSy3TPI46pauv6UhgQ5fvuCb+8t5tkWPv7/8v1783yzD/fZfSOCUnoOu0P/f3rf2xo0ji/6VYD7de3FwZjK7AywW2Qv4mRhIYq/bSc7ZLw1ZTbd5opZ69fBjgPvfL0m9SImk+JTULX+ZiVtksapYLBaLxaqHdyJh+/Brt+MHjsBixP7xC8T8Jmv9I4hxBlOwqV404VagTPf6DsskfprTyOWvUvD0VX45TPwUpOFjkP6vXfDyv2l4edpPd9rDtrrJr/hXQrzHLzQ08ard7g5wwvUgWu+7CyLjMCo24CpeQURqsLeh8yqr3wajDzkIc7CxAdc+NHZAZ3Uv6QASuZLkgvvl3Zfg5TOIt/njP3754zf9qcjTRArz9z/+MBEYJ2R/AXlQZQlzI3kYIHOz6wimu5nu3UqZy/ItQMo2PcnKGBwbSHQUjwMaS3A/0mBP0pvEItzUYeGLYWYOzKk8TbDRaqlFmtKm1M6jCYOQg/0cTvQHcy1ppW3biANVMPSdptxoCF6a+4AFmA48o4FRvX/97TdtoGwIh6r4KU8RsrRz8mj3bXqQja4/PVXO+4ElrWZ51g8AnU8yLnQewT9Bc4ZbwnS3CcWoQcRk405/f3f1X+ses9b4QXBSxJv/eEdW4d/fvUdM0kWHLuLjHKG/miCEweIsaVU8V3dhuMHsL+/+n7QDiwUmw1JfNgB9UfS7MySNdYf6wq+EbgnrfVC9vzeZqIqBZ0WEr2AHtg9t8N9i+O8CrEByRnICyYDrWoiXUbC92iHU62Qvjg+at7mV4enwWGRgwY5uZpULvsyFeAuy0kO7gEVpses1PRvN+5vBLlcz24vxVwN3aAReZbgOyE1UbGEskGc1t+NdUoTiNdGHoSzKN03Qe/WqYgFiLHYKy1xrNgqD4nIS1ZHDR8LNkzJI30Ljd0RQX7v0IVhpGfqiXh8ZtrcGIsrSxERbLGC9OlipHIsqBaC+x7SR3rvg5eIF7PZWHlwEpDJZyvShXItFZaeoi4Pa+BBL9VgKlzkcMyVrs5MlUbSE1TC5EaZ0m6ohI02hGQeedCenB3xdcR1/SnBKza3VIiB748cCZDmy4L4nuRUws0MNzzMZpDgSApCcXSUcXFIjh3he9fh9EW8cQTI+QmopiJM4ewZS59GxqAlMrYElRXrNRD18LXb3IL1+wAsns5F4z+6AJgq2vi49fumi8wiamceWUna1Z2IK1dwAamftk/0+TZ7sthA2L6JYM6r5FZlK41rAtGV4ScJbVjIuByqI7xYSkA8Q80F3kppqClLHrLY8krS1roGK4qhcwb1M0l2Q69x+imGtgih3jefJZgfjs2S3o6JbLAMDnZwDTx4eYASRlNuxzv4UWJW4ZUDIFIPpKbPK++zroClF2U6EPgdZLtmENHbCNQOqg9l7fczke48OYg0koxvyLP+cbGHs6nyA4BG5RhpfCFKX6zVAA/qQWirqIG2nV3HDcaQGMkGi34edMb9rB5NiLvqAe4la667p/hzXULqX/PqzggMHgtgsDKFdTyWQDjYGIbw1xPKEZIkUA8te/ZzCNH88D1pfh9nybMCwa1MFg48kstbJvv49yEu+rBDGRWa3Ld8hJuAoXUeB/6zT2A41vIyR0VbsvsMM5i50NbaHT7au7Ksa4jl4giGgneR2byf6+Tr0TrM9AKJDrdozgv6zKj10+hDU8VE+KjY2z/GfFMWb/MQvToT7ufHqcgaMe2A1giTYcw1hoWNGahBA2nS0WtkIPLjBbwnj0ODiptPdBpMz6t2DFUMrLfDeJbDfnQD7F9zfJFkeRLwwPLNbpUe0a/NtLbPVG7w4hGbha1R3D5arYAk6f/A0YxJ5Si4Cs8pasL5RzJwELjwnnwFm1lXmIWD17jEFQB3+X3Tho/UDUhh2YBtdh9aPr5Ctf6CBqw5vU4W6uXTpGigs9cdL9C63BE3jzyaZUNHd36fojDbsGDPwLBxCaPhplGyx9bEE+TV6bjrDgCa1Y5ni02Ik/OB5OJhPDRokqQKFrh2lRVNi5ALUqizieurm0XPNo2CbXcUVllaBi7xcCcYZNdyIA7HWqntGu024ikmoYJ3Rl2YmkvU1yV2DbPMI2r6Gn2V8mizZhewF4ezyXtgi69C0HOPkelMlI13AflyRan8bUJYkIW94m+JudhDxtQJqjVgOn+CmCKLo1UZwCDgYdXN4uIxQGLSOTV4K92uXuPAmodauYToPkKllsypeZSdN7gK0axiuzt1vuwRj+dQPlYhx58QAug1wgpNVsXNk/TiBVwO7Q3on6hBriZ8rkK5SDDnc/Vc/C+dLJGjzq6NNLNePj5Bmg1AKPsghx/HhccCr7CN8yM+C1OoQXsOwNx5uwb8LmILr/BGkN0yGedM8aQRea4fIFev73wyUVYEnJ4chtktONpvOkFboX2XnyXMcJYGd16qCYTc13+KoXL41OCvKvtS3WNcPPXhGrzQqIBcve5iSVXIevIogKnkYKoAkyo8AtJfuT0G2CpANA1zMKgvJ4F6409/mYhgRVobspADQVp8JXQygO/DiKsb6FoRFmlreezVAzl7DCJRqw07f0fBuQAoTy2XaQCSbPwFrtbCuyH3hRYy7WMkrTmfh5tU0UrIky3QQ1dDOHnFK7sYLAEK4CyKcgBn9KyOZlN//DR2ZceIVtE0aoO4kQP8qu8isWEglUbQTEmTqYM93/ISWGAKGDPlHW7lDJl34859FUHkfLDR5eZoi8E6eAoj6Mod1i3sbLo5GuxeMndH7OXkuaa3Cye2mAVn/8OGVnMAvk7TG7xRQaTZMwJ4G4U+SPBXnS7d8X4IPdxjeVclDdLhojqVWJgXZvtDMwF2xczExJbzgxRW8GsYqB3t7OCT3fZpEttdEn+AG1JhVIK1DTMCmggiBe3sbrWXc+LR4PS3yPBGlRlLVC7j1D5g9RjDL7QFWCisCaPGh3YdxCRk5udGhgoCCoZWrigHgfH+8jjZ+B6jOUmfkbtzTGKs9ghNExoQo3U5RYzQ3VTiy38EdEw27urlyBLn2xF3EOUgza1msVDQDFXgWoEqNjzomOhTdQVCuXauNDVkIIMtP8jyF90UOzpLdPYzJsc6rsCL86+p1WVW9zoaKHwBuH/0tX/Ys5hz8D7jxCP2TX940u5JrndMAdqtwXF2o8COMVDKZ1AX8agDr6k3zun57XLWwygOt+U7aCr/fLd5ZN7fgI+D5F27/9pm2UwLNXn+bYPfe3lU/l6wgXDmGTyB9xatf3w/J9rbxQtbHmG8x7EYcKODB9rZ6JkMGy+6CFD48iG6R6Ph1gwfFJIb3+uE6hVsYGwcBtwBs6D0NMlCZndZewgbWFxBkRQrwbEiZp/8auxniZEeHrbk2n5ph8D/YoQyuI06LeBOBsnAMxyduq1xK8DcgvULKy4WPlgGI2eAS3uoxKd2+yJqxu4SCsYvYVmQps5Gy6kgph6LVpvgSYtEGw6dMMnTXDLS/xvuWYUEMEXGWAURNzfMeNOdHgHooc52neAswx/Db6ko+Q7vePonpN19GPrkeFFdvVOpJInEyZG1bJmZo4GDjSuMaUP1p51tNL7ugTnGqGrsN3XWs32mSo+l1DfXiBc9JEH2G8U+5wjeoVBlstlxbyYx+DG2Vv7bBjmZ3XzAYvBHRPgxUa9BNeOxbEGrtPCJx7G7u2/lGpoqLoe5ocz7DMcor+KdIcrUjcrO7ZAUiEOZdwAbVSWoQ1+z9r6uErOQAdhvE24GLSAP5cBhH7tbRPsNoXTePbz2nlXTldZ6zo+4hKKL8OzqofjFLb6L+cqo+CB+/XViRegrjIG0CRBBL78kPZpsaml06sEOgs+xusUwcHcw9ldEr4AQXoYuHLN6/mISyf7V8A5zdpUGcwW78qsI23azSNQXE6m5Ivu6tUTJ56f4FbGCAF4/Rc3u2t4fMcvQAS1A750EeDGsbB69fl6TKuc9V1a8CLXNJ+DsmzCG7Cf2+agnC5MVf5Pz4TE3KmwPBrQPh7dA/gNvhHfrfzsTHdia2cr5o395WN1G7hVR6qsk2iApqetqaNF2+3xYREAd+mSUd2gPvN6tV+k9+2j9dD2H9bM4FsFuQ5UjhEq0oKX6re2NbA70Rvtw2mioKcPCKxaF8++YaeM1hwQ5nCb3afqw4fPGSpwF9hvThBaRj+5ag7wZN/j/0Lf6zJErST+CFW1neFni1v39OsFpyHGfnyna4yiq/tbLFqevhKIPLcGTZEoR0YidHFclnikSnuxUqjp4rhz7DSMUxYibnBR+hJn7yksEIiXnz5snqpIWfuN49Frv7mCouYZw5zq6y+awSfc/lVGaurxsBKeVlCeq7Jd5UeZZ9rTRnw3fLXAA1GGLc2MG6wjHYpYvKyxG+kr0lRTxObCkIYq9Unrc0Pe3qRGeXSEEWbTKvmfnZdGV3aTcwE8svzW6DC3Om95scUxNKUh08wJCg2+whbxLtXaL5jC+dXAYZ6KTQ7OwTfHVV2oTih2fqUZ51JIT49OXPIaJeAkfCziWsDT79rsTSQ70c07IQQ28I9N8TuzpQlo+dGU+l+1xAAw821Z2qdg8xLNflElbkMoRedYuw3bhc2mmXQQjyVZLm1CAmtBM4ddzQJ2hXBIbQU+5X+AcNn5+uAXkXbJe6/qwj0GyT6iDet/lgBkVXd2a/BykMYm6yrCXMuIe0+Pyc9OOk2rcMwPOZHc0835gKdON0Y/qvOLzlPTmk9ConWQa3MVqBtXHpIW/pW5akjlOKZDy2u02b1h3SXk/+185N1XpnWaWJHXld5NcPBCQh0odBRcvDEjZYHxE++L83UZEGPRmyDu8R38q7Ce456MAhD+bmEpaArdKt/98wbf0lIMGSrVZe0yJg9Y6yO5Yx1q4cgTgjCfqJKiVhXdekrDdk5XVoj0tNUnllTjm8JRpBiIbzhOrqgEUtfi8ZbNz5CZ2XshzHHedqx5uJO64P6+Jln6R5tURNwuGNF2UVgb8Cb+apsRtl0i3UcuaXMOtimbefKxqW5ZX8SDtHV94dPdwd/4ZR4NE82fwPmka6zJ77agfEQzjCQIZBm/52UHJ3zTF/NcHghIaOqp/re4NsD7HV/f1i9GaXfmOlyWWk6KPLU+7YVrmi7fumbBembEdeKiY1P0QK3uVx7jC0f1uE+Pg1fU3rxwI2AxUx/HcBIAH5AIci4rWfQ2VNbeVvqdWrMw4YK59GDc9lwkLsNwRlbn1Xz/+4SQKNs0zHmSuX0FuewaPMM8gRG6Jq9DGo+1X/wL9b8aMF07dwLNHplqUysJLwSv0OUt7ysi1y9IhTOEXJ1mDZKm+CTYXGJeyCE4dLyJOWqBn64gQzKs8Cmd4ugmhsaPFbffQsiMIiIpwoU9x4sCAvk7TY3ZCivMe/eO6SPQz1Za7qZvcQtpJbc4l3cGnt5oLu6uZks0lB5r4KhjSXgVPL3mX6Od1oame3JWTtEuFcwuIl1Oovn6pbG69+4MsYT7eW74NVY2XX9ecgy7HWx7GodgZBWalCx6ztlFE1ibYrt6xMkQMGHiYInjVqbXHcRxV/La8gKigCqTOEVmZyMzgnDgrQe30rbCE6X0EdDR/99TaGxewJH9Ok2BtuDFVfvm/a1SNh+7qXjkOivlZmg5WK4yphA5XEM8qPXts6iqZ2IuYmPseDSzvWsmEJevEgtJCBVB+uoPJuGMV56F1yQFhLboQT6ldkRi8lJ+vnIN4WRvcdbU+7oyGvAoaZi4fkA3UDClfHdQPJSd5NF7Xi2ULxVqDmWWy7rBqS7HAoi1XM+Ml+nyZPYFPBOpM88VXb15LcNUiH5UCc7jULrcGjvLXUdadPivwRa+wyN88tCBHDlrDdTO6JvNhRqZidlBC/ouJ4HIKt7EzH0K+x4N0lP4Gb5UPAnYQhyDJ3QNGfTxBNsFWtFOUVWRnyx7/0CKH8fCdmc4/hycPaprumn0y/VNVyqrtGg0TZnf521xxVMRhjbHoAbEIXSE9kOuZFFxNDslzAqorxOARVBj04LvRUS+dZkaYgDl/PzKpo8wCXAG+DXDUk5G/Gq/IueKm2Nc5doHY4tSB5k7kyWxX3OVoL0VUcRghVbxEyzGAXL+MMdocHa4qbjUQhM+g4lFa6YRwKq8FGpQwNpLFY9Qdj1BhS/BDvDUF0CYBvnopH9s1g8ci+uV3Bd1OojciJd0G0KTyk6pFAM3AaREHsMU6xZBZWULeImg2VZcHjUN6GQEcPRATYeC4GeQueg3Rzk6AdO/sBUoDWip1n8+wRhD+Ton1y5drD0xvAWcq02qoRuNB1nY4PDzCC1kWwm2PM3gmNxKeKz2W4aCdZlmdo/lnDy2jaERTc281EYJScWdo9+izLnGc/wUbEOmtMz56efncG7OJlD9MycDuJ2yygDuH+Nwjc0E7L5TlEyi0/B0QOzUWSAnMSkg3uUxJtHM1VH7hDQaCAnwbxT2dnww5cZ0uMhnt15hpkVVvYNdir+8DRhlRpaGIUVPdBbtZEgazXFP5JVhp5EReEbH0UL+CdiZtogFuQUakULbXRHl9+umdOH7BDrNFBurGJ3KN+U+C+GXDtH74JoKub3froy74nsuNpBRIfvtAi3Bc59WLJsfPuHESgm/hgGdFC9HHhFuwCGIvLPiglEA/wG+jqtP41yZvqJlbX/WEI9vndI0SYBuhnckX4KYg31086Rq56+oPq/PwtQ4eGTxDJwTKKOjbOR/0c5E1Pq+sQ0lV/9Kqb3Qsc8yWqLFcf4QM5YixNrmq69We27Wk1ud8ysPkB80dD+ep0t0bFbxWsMSV5CdJbm1+UCBhVWBbBsbmxrefB/rbOs1vyKqtRJaUdAss0UzUwdJTcOzzg3CJi9zhLiTPLsoHoLqxpBWJ8DnCFYQnOHXpfQJZRlc6sU5jXM0JsSUtX9giasVnYS1CNDbFOo4wmtAJvpk2D4iJ3CC63QvKH+L4Mbgbyfvc7BjWjUOL7rro+hpWbuW+WsaP55p23+0f39451Gj3rRwRXWQ3KidX0GYl43CYftHphgrX+D7/V4op4E4HzIA9cBS1jDX1GchL5WhHiQm661xokCI+G58HPVTqUk2WUJ5nQqihZPCDDqiqXBGcm9R3V3M1h2r+8II/X5FHuJcetgqUJhE6Ml5FBmnkPenJ55nR7V0O7e9aWnse1jq9IvQb9I9yXN9zHvywn3APu0iD8CeOtw/tbEq/o1wYjl7DA1S1xbc44AjfG/lWvjqV4dBp6jV4ilT0dXAH2HezePSnKIvEjiCKQL8iY4b36UxGIstu6WaaHnSHRz96h5KDwazyV4H0H8zOj3OBnhkg6JroDtRJek7RWtyDIWm+VgT9AYOBaV54/2exgLAhx1CyWpnEkzIs0xrVpwTLSQzt4Ne1oV5xcibq44CgX02WSloLkxrFSiSMgjlYFx7IJUNXAPa3nxp1sOnZXwXnw8ICdVW7AOVQuPN5SWsTF4/DyfdZdUpp3Ar/a+A6EEXIYrh4TEml7FqTLOefY6+SbIK0Ohlau/vKawyykiO5rY2HSEmAfSjT5LjPxLb6/h4xo6YIUl4/zWR7ExR59cDqw9wh1CUqQF0MmS6um5ugELzn6abd387QGh4D9u4Cp3c038WPi9tUKdwHzKrsLXi5eAEWpCRgE5AxN5zZJuwXFTNUOrriXJpG9IndVOfkqI+ELGlfYvNgFw3yK5ipgMcWOe5Qb7Nx9EH7z3ZuopreyvW5XuobLJyxS/BK8ehi2IOd5l3T9pdWH4NyPPcvAEZbqJcjK2WsYgVLBWU0PBnMDUpiIQ/zUTBN8wUygWUWSqCajHs2W4D331UUjhjkMIsNLIbb37N8tEo6jnz7jgqvHvwwpcjk2tML09gAc9s2nagqMOcor/nUJMlsmRKgSuw9dE7q3xk3qvlzEuLGGFlae/4XoKUTmZ/AEIoPKIMl2Tbr+x7ur7Bt5r/X3d5d4TKMcxkmaq1xQd695lKDjOh9OnxTuVarM/m5QZdajmlbL5rIF39Kev5ld+3+YXLQ9gDQFqQ/YTt3ESKi3/chK9fVAunMXhKXb4VOe7/mJcTqKWZd93zL+O2C9gsnq22qdAHEJytU4I7ubxPAOjii65yStmFg62dPxC4MXb+Rode90j7bbGHHvDKGxtatb5N9lfxJGy6nMU1Jv59EpYXgR59qKuU0ik/sDpreV6rxCujfyofeQBfQmbTORtlVUbJ0DdXJPaZBrWj3tEp5fGC4qSMGFICISqhq9jiUGATY6cHQnco0AOThpMInFLEt16gbIKQvx3SPYge9BCjGoJUgwIVhH9JRqrWtqVRWYXPHp+kW0ofqTpOoNyfFLkJctdMihY7QtZ5G2W5cDBd/9DjqzDAT8MklDgHBE/z+JIuwxszrgfEqyfuS99Zucz8k2GaoBPGr41qd8F50mG2oPtgtmTeIcrYP6MfBXkD8n6U/XU32Twl2QvpKVWJdQMwkI5kGxDE4mIC9eEJXxFpD01rb48YFpoKkeJFdBf9O6SCz/MDkli8sDsrD1I418FgisdMnnBAPQYIr6zQnSyTs0ZVT6cUfgHVbidlIq3pVankuMPYfCBCeSvyAlu9zvj6QYWPMgMnNgbxCIX4vdeblqrIKdWuxI9JQr7FqI5yBOdjAOkDB7iyvtDHlbtGvetfn+JSB3CUvYT+buUvN3TguRykQydBdkPxcU+kuTbZA+g+lt6RG/LWK6sMdAgZMGhy9B+AhjgP9eN0CsMiFQEI0cZ3KMfnPgQyPRqnapIwiA9SWMia1gNXU1EA/ovNePMVkVYQjAxtEb9os0TXrxGqa5Pbf40v0GoPO9MOWqDiQ3CTeMNOQSVKPC0Up/5Z5EMJCbm0YhSkl88bLHMsG5qLc2Zsv4VANFSISH1XZ/cxSEqTD6V/CSI+W7rrpbbQk3KUxSy/fGSD731zGjUExM7YosU1Ur4opBNN9V9glu0OqyOjgUMVKI1Y7p4T6cqcm9BK3FECwuQecm6FUSHmSfPMX5IdV/rM8/C1CADalTcpLnSKiX8iqMIlz/FMF0trJEMVnIIsIeQFomzTKn9GsXW++kl9Ak7qjsZaOp6QT1iJZ7GAfpq5EVO6hLTF5ZfEGbF9/IsAUMNjCohEKf72xvD2mNKcFfgpJwYURdpsnOuVF+lzgHeQv20aseXCVLv1fo2hbiaRi6Brkq7v8HhPJ0aibaE9/vurnddXkrsUKr4S6Fls+PERA3/gpie4SGJVLZ3nZbMYg3X4K4CKLo1YOlRWO6BN3JrX3H7o9/6F/HVtd8w5s6C1upyDJVw8YZvjdJKnKcqd0gZmgWNIlVtBazbPBVqwng8tizyiKbgw8i+xw8BEWUI81HJJC6QHSZnSjY7QO4jZewIHlrxjCKlb9dmgFT2iPHv7r3f/SunOF3YIc02jKizr2cxQ7HFv6SbABJIOfcn/45yPISegqGF7milV1aLOXDywGUDV52z97mdvFuRWLcjqWPXBm6nJuhxmH4nuMm0mN2C+t3h7D+oglLWXd/Bc/ZZ4DVIRJjyqO8ABXOpdxppWLByUHpqug5i3rIlYfEuvgEe6tmaezaawi3J3uzmjJabPvd4Gj9I0lxlS6vr/hwON0K5OXvx7LYNO0ltXJhyjdgTnZMlVyM9NStVyECaufJwRB0F4EMCZML+KsMLSbmfaTRDWCyhWEQXe+xSLXX8McV2YytV/RTiO1MkyAoLR1xVAqiElqD9Kd1R6uFhqEYx9rgh2KnBYxwDc4aUGdLNsmq1l0qAr2pDXf4GaeBxhS+WtDkHw3H3bOkyyQtdkszcL2ZtZNnnCTTaZu7dg/D+ac5vknhEwLZhJsuQGrtK+/gi2pzGWV72xX1TczRoPva2ZAK7qm/GjxVxIU83Li7cQ2PwLLWRpUQ7fT1pMgf7aI8KWC3IIR7SIWMj3ww1nDBB1mRgrra8/GricEDpYnF5TdL7i2eCsU3ecYvcH2VaKgE7BzJbJwtxIB6k7FxZexqt09SfH5+gMvI/ONFwC4TfJDyAxqWh1sXb1RdwLEOSy8l7hbga+sNHalrdBv1E+6tjiTBT2DTv/RkXcd2d2FoSVxCEG3wX+4f59cSdJbED3BbpAHPe2t0yXnxkqcBPYWWSWiiYhc3j88dQLwFWRHlV/FD7zLerBZv+SIQYWf1fLjp7yMZzOo1Dpf+fl9lKlo2rU9fSyjtdNAZx++QYJicAJMiDYFpLkIWvRKWHD0267shH/kbmC073/fxVSWV09V6QyW5rl5yP7T+bk4rp6u94UuG+BRk8rc9fzXMD3Rl9a67hLHKU1fqvgR4miSi6FElJY/myLPj4OIFW0DnYB8li6mjVp0wjOo/P0gfhE1n9btIJOTWQmllyt7UVwoeN8kCqBI7bgBXITelNkxcJuQuDeJsB0muAXuu8iBa+d3R2ijdG4MvOA14uiruy6Ota8Aa0aEm80bAKznhTVF3EZWJVwJ8Al+ovIOGzx20Hkxo7lNvvqFRfEMmeZHw4sf/qih3vvqV0ylo7wFp8gQRV3wmbLjKKr1Yy6/5CnOw0ZdryZXXazpHBFcIkfC5suHRn1hpu/JQ1YJW+r6cuaocm23XDw8ZsHrpRwL8bQCcBnn4uIJ/WhkjN2hFl/nfLRaKs+31LNntyZ2ynrGhfeokV83/gvsTBM42njsCQawT86m8l58G4c+rGM1O+HNxMVn20S3oz00RGkRKNh0PvNzwGOEUSPIfApJjP3171mhbdQaCZ90D4Ryuo88QT7dJ+vomAAsVgEphvs3/Que/OiFVYrCE6W9MhPdOLJTfbaD41u5pkmUrEEVv0zvC9GrEGcZFVUXzOHgucyspJcKLi3ULY117kvDvTvOa04CZM58rJE2eHrZbr9tz8g+42YL8X0k8CFk/YzPMByrgmPgouYVlVGem6W1SaNxRNRpZ1gdVOjowzN9C3CUnYWQm5BQAd4FSGDQuVnNUio8QZZD4E/eyq6AWpAhlzE/D4VHPNQ3EKGl56evVvcVRTr7jxNfrRVetHklAibe3+Tcgre6r8d89F6qZa95Gv5by4kPH6iCg9XCW43MHWxhbP/JHc/988bIPEJpWN1Rfk8skipJnKxjg+QeMN3ZArsLhog7aCyR/HVh17/UTViA8z5Jo4LG4AbK4uGe/sKct1LMsO4uCbDC+XBeuAyuDLKbpLQ2Chh9ro/bDV7dQJuZGDWLdtzu47auhOM2HzRRmLLMbh7WlRUGjr4dB29NHgD3S+eRusvZUm0xlDUN1KuuxjOaSGUyPk1RXu+sjGn9NcWq7+pxN+vJpjBmlxzvUWe3RoGlFsd19zm7tbhxhYquhDnVOafSNbro9zWQFvb4dCrYmcymeG1lzNJjRbI7MSiECNf5GOJSd/VQtL+2E+v+3CTLQRzCL6PEO1Tbq0WCGRtXdy3pFR8nvCa4cPql/TYEZtosMEXoSZ88mwTl0Xx+TgDNzniU78sToWPlf0cf1qhm+Qi4BqmTaUaoghSbBzC/a9vQhHKdRsl2KcLiaS8yzmyQziANse/qYS5KbDYNHKtdoEz2I2WyoNMgv1na1QoHw131wbmVs3YInCJ4/gWj/UESxoZvoIKaSIdjYOK27W6HyI8gqjvue2GOfzamio93t/eU8OdsucGKo9lmM0WvRUjL/G2SkKKwDUF8TTUgiWT/JsiSEZGbri4AE57gq3/PfgoykHljXWds7wn8Rb97h4wdO31w1qDBagejhP9sfvxRRDvcRDBEK//jl/S/dJXMdl4kD352QR0PY15iFwabPDkTGRogDB3MWH24DFrf/0xsSLWOQluWnzpI4y9MAsbu/5mEcwn0QdfnRaaioHjClDcjul3OwBzGOLZDRrTIunYu/P34zTGcGhvjx4VdKqBRkDf5JAkUJbgckaDTafSljvx6HiDE0HYR8MS6SNvoB/1klcOkaZ+0kMx4ten7ZD6MIHBd1BilBCy+CJ/b1+RE+Pm0qA9OYTiaEXPRpaeS51rhzXzYUTHv1cYHyKGfl3GSyi+10cplE0WHstxhTVrrIDwe/qxIyDmIjbX3c6z7SujPlUUpKHHsY1D/7UT+qs+hAWipClPQMaj6ZvKzyIAckkjdGp8czfKfdi7GhTK3qO2Nl1b+NIjsMvgwenS9+DCoef/wIEUuOkg1VIjeZKNVxYA6F6Lf//M/3vZlrIdXRfTSk5rdDFwBu6OLMp14itPZreHbCoL9ERxQJBrnJz/XrJh3MesVxe3FO89yT/EibTDd6mIeKZz1TEzyCVEljpQVDyqOAJpKsuyDdgu7tupGCOFq50pnjCcRKHEM/tlSdwgjna+O/HzCSqYHtS0vvHa40KI3FMn96aSApzON8zSdipptXhTQXlebb0WxeNUU6m9dkclVHJx2GU6/GlsGh/fHgnXsNKYfh4IPkCcf6C9jAAD/dk4kP04iePvaD1kZVIcC67urfvAiDmFQ/AlFTozIUjdt0MlG91qhEg4u/2Qz68gAzCLOIdD758QRrzLCtMLH0qIxYtZ2NOHFfc/UmkzeLSxEnHofmI05tuOA0VnT9pNShXho6TNWPhRnjt/nx0HUK/y20aPon1ibNU8mTPWI5TjhQoQ+HnYF1X2Ye2x9HUS69B+g8XDzLVkPyCMIlf3AvGFP+bHdyMRvwDGqpiyMWM60pn0LMJIkZRhIz5hX9eBsaky+BOcgxHw59YxOnhRCMN7/NjSbhoPY3oYjxGxzNPqctc3Pc61ipG9jujJTJAqRPWxKmksCBbDaTSWF10jwotcfzXvS+HY2y0/FUzFHPNRI2oOKm90lNL18jeqVMxOtGlh1oVOmq/3EL/l3AFOAUBuIb/znpLgphLjrM96PRYTRVOnpsan96HRh7/XCdwi2MRwiQ1VCDhxcgq6NsOqyfXBSQEoBPIH29wwW9hMucbsSsb+bDzCVCTOr0YkHjNrVMnBbxJgI4AdFJnqfwvshBWepz3X4ZsneolpwJpr+OeS8npEyOZK+xTytJxGOvMiqmVQWHtvd8RLeS1YO5MjZdMIdnopvJOTOdMxIzpwI2uFu+SYl80JnIR3M7JMgVPq8Lvw7SPMk6ouu+LkVKRnvVeDZydTDb2oRCNb6y0ouGmYeqYjz4kmT487y04RDBk7QjvLLhUaYyLN1hVrJ3MCptBsI2vmrTj4eYh3pb7UEIH2BIPjVn28MRNj7+PLxELY9EAAXkHYIo8lG/JvX61ip00RkLhuVBTRB8ZUWR0KqAZd3QT5YEcxGyTaEiI1ZlfD6AmSpXdWp9C84idbO1sE2usGUUTC3zVH2cweii2VgRLc7cJ0nU1yOxFiiSNCyE6SJAeMKlFlgkmFPOZC5BxFQnfEop45cVG1fQvgcpDOK80a1nye4exqTh5BEBEtx4oiVtfkxxBDJCVbCYU4iBTP4O5mw+e0Edf9+1ldGpj+oK4vnPIiBlMb7FUCyjTCNaFtgPR6kexQyatejRaM9N/g5XJ6qI5NFqv0NSeZ1jdrYCzWlk2CvZa8iZ9bE9kWK6pNjRzXyK5aj+RwmRGiI6G79j14euSp5P6RhVihVwHEl6NSXJixwbyXCL+NTSLNrsD1rtzs8OmELh2hgEs9G2XSK+B1HRCKmcQj9yMa7kEnJV0Kwa+pRhI3nyI8oltRry3AUwtVhPfpAf8eXTREfywzl3nyfPcZQEm+mSmdYYsM705sejSGfakKMy1pzyma5XwW4fAT7+ppM4Oy2hNT0jagiW+ZPJwh0EKaIPF6cSVrO0LQZKZMKLddNgz+BC/XoUdT9belQGm0Wtz1as5u/knUaIRnTk6snP1K7byyQtdiR9v+t6EOJtqRmTgUT9evCFHFpadHTItEJwl+xhOLYUkEH7YlD9fBxyUBJzOIKwJv/9mCbFXigFVJPe5FU/j7KbkAH7KHgSHRFjPAqP0kAtXnNQIRy89WfMp7xMoHTU53JcjUOaz8D4EKFtM3U+RWh060VzXke1XwhakwnRV/CckTQaB1F/qsaWwaH98eDrTzWkqIw1ef2ppkIifYw/qKqugy4imavoAC1qLlk6tvX0BRg7wjZUzsHEB7ggoRvLLWgteLjDdMJ38ZKDNA6ikyJ/xBDLh3G3IEzSzWGUApVRwOAlb3jwGlBK3kF4Ga7TjZeixDJvExmTAVL9cui1iEsyVAZiGT7x7B+EyhlXaMZUIupSM7m2qDNef8vQSeETRNikr2t+qu6ZZjanMefiwzY4mtzmDFkq406e3Jwra5y1oq8jFiBi6jplKvkiGE678RFP1ZwlqsGyj4FHF9VostMSckgCM/+wjmnEZsSwDj3BmTqs4yN8yM8CdLS/KdLwMcjA5gfMHwU0mE/jQPxhjQUDrP3RnyYZK/V9Q4uSTHCnYnIRYWwdPkGmU+pJ1/Aw5+IzgtGjJQGOpE3b7qk7zkvWvtFLYabW0JxEbTQbyVjOmBmd1mz6muRg/nY2xrKPQfnrYctQS8j87exb8Iyk/SZBALJaOR2Ef5KDOIMO9/vB+y55VB2EJ5MnZ043wQFzfD7iMpoaMpUVZlqmu7VdPcI9Lg05652sRpJNsdv8eNgC1NAx/22sRpV4jPh4m86aZ8npORzYD35SHOtMrCMhUnZLNB2mvaTFaOzd3tEPbFFv1/S/dJg+mQj8CCK0wA/KHmZQZhDpfDl4G5il5yCsX1aexrB3pxWH0UwUfVmY+ridF2mMC58DD89OPR20KZQ7Zybmy8ErFpaeg1AsTVz1+OaKlmwentGiJXgd7k95aE4IImdBmlNlnH2WHB8Qky5GnbNO9+PxlAbv0aYy5gxKgfdE6CB2qTmI2Zh7lZF0Tb5d9WRr/sE9cxCsEUN9jORq6oifs0cQ/kyKbo7N3s9iDdZryaiy/tdxnpxxyZKj5jOL5gA//QikgEIlddftOuGxLyxSRPH2JngldxpXMcRwHV1tyK692IE757fux8N2BvToURqTmonZyEftKZJT5GqevTkOuERJUfPrljITELdCqeOr6vadTD6xGDyhmficbNfUv/FEij0NnXaMx6H7bRSBpEYVYePLbyHjmR+5o4lSGa6D4ixE7SBOntNJ1ZjnTV1xmvyo6V5+/CWX7YrOcYjMwYjKycMDjNAvYD3Sg/xmQBZQ++uhO89bUpS2nolv+0/CqJMMpJ/gZKZZaRrUO1tPNErKD+U8MNabT2SQ4wP3nc7TiYx8nJIa7oL09eIlfAziLbhFK+IMWfQgDl8l4lU1YEWr/lFdyxAUWHdl+YsnmeDR5UceSjrUPJPCCZiHaJA/3mRiAplgOD+dMISPYFNE4C7IftZeH/o3cQwr3YiZUObDOHckfSKEKPn18IhZ50nqOJSpDEv3m0z2/lmAAmwudgGMTvI8CB+Ja/oSSmwf/QotXgSOi3mnvhS3xcEXfuHTpTIwnteZidpk1aLmJT9j15Eyl6FZFJWi0F+XRITyfFhMIyZbIvNhlK2SQl4kb56kTMwq71KmNByN3xxki9JTQlLsJnYRm6OuGEym26ie090DI5PwgpRzXON/yipwV9/L1uydKvtlnLveBvEeKnw8XN3oMrSOIF8URSqjVfhNW2ViVdxnYQrLYu8jFzmix+4XjWC/HvyNR58mHX/lhFU64VOQgy8gw0lP1pdpshtPStjBO4Ge7KeDl48OQUqnNmoy5iIgd8mbeMxEPNqpmEw4rnb7JM0Rag8wAl59l2IpYXBgIHa+HIXTkaVJw90I4+2kDseLl+lFhcGhUzvh+ESFpengRAUNFCVlxDSXBvt59Vawg0WfgxD90Y/DR3vunQgbRZfKmBV+kwnaaRD+vIpXeRL+9HpO8iJmAuQZlIRtDt5eElF2EIcukdzN/0Xc/IRuxPdxNjI39TO5LyAu8Ju+KtgN/ym5dYsLTmQZ/fMowsbi3MOF/uTpbq4heATp6pCkMiLpMpljIImi70mOjMVKpvD0tECJK6nZSdHvObbk7pJ1t9/gJlv15Rfcq79peBW64zPatPdR4W7ZarOsKRjDxyDlvNaYU8gX/uEkzp4ldhnVpDur9c+jaC4rGXO1MQrYNSPZalGctIryWbIjx0xFBUZ1GVt30UN3Cyk3vx+RxhKyWmu4kcWIXwratBa2x3rcmpLkSC9pVcoeWX5q3KY7GUbJVlMdUV3GVkf00MwJkP79iNSRkNVaw40sRvjfN0k3qV1nFkmD7hSWP47jV9CXJEfqiM+eechPjdu0ISQYg++4rIiiQmI6ja2S2MF7QSftlyNSSxKGaw04ukA1fwmlpG3BncqRtJOhUDnSUAIuzUWWmh4ThqgQ3+oteILg+ROI9g9FFONUnKoeKUH/0T1TIjw4/n5OoyPSaGozojX2lHLIfBi63alaCed8Molye2HDY8YMhYnpPA/JMlJrk+oydWE+Hq11QKrqAO6dDcXp8O6Y9WVo3JvlC/L+AS8q1AOkdURFsgGXMM3y8yAP7oOsf+zDvVYgb0JX9zD85V35MzWZ1e84Dm0X/OOXzX2C5jq4j9ouPYXTARy8nAU52JJX5n3w9FfuIHSDgaHQv/B1B2eY5gtviObjAPjPSRhE8E+wqWecMxCnDW9ITrOhwYN4W5DA6v6YzSfuUM1XFfLAKk/JjVGWFGnIHY3bTEhkr+UAFjcg3cEsQzJeX8X1MOg34Y3eb6U+Mlr3XwKSeFk+PN1uAAe66QAibOKi3vjsZ96wbIshspMo4lFJfuYSRb4oQK3vd7mw64+iEervirxq7CEhu5oWMo41jRSHlYwnH2hwhCaPWW+A5gsPfvNxiAD8rperkZsvXPTrj0OaOEc6G+m2J7TH8mS4852rldkmAwO2zvHeWO0n3jDt1yGJrq2rvjjXX7iyXH8cAH8Os+rhew9++4k3QPt1aMrFu7B8C1bef29gmBcpb76bL1wW1R8HwLMZJnpjsJ95A7Et1OZbQlOngWT211WjteoW8CWIi4eA9OHpGPYzl1SmhaLs4VpLMAU7viLltpJJJNNwCAUQwSeQvt7BHY/X7GfuoEwLtbmlq+iIppduI5lhupnu4E1C/EsY5fwNc7CLEmq9XmqYShRHr4VsEdStlFdB1XFgMXBbyfCgW+ristqDED7AkJzCqAoUIqxE7WX48fsoY8rvfl1FfPe3Ymlz7s4s7WGEnTJeOhipzuldwDXzqY+S2SLf1cb5HqQwiNv6F2fJ7h7GgWBeVDpJ8JL2G8D3n0VAfvkWQ95GwH7m4cC2MOOOOksGtt7y//rrqNtRjJAaJtpy2VlaGc4/MowD3VgFG7q9EV7KOOngYyo1VcEdVdGpmmuso6rHkDnT5ODrmzLNJ64Z03wdcqtBkN6kkHu6or5xXWrt54FBqFv93iDUN94g5HO2VjpakbYiByT9UTKQmjOSNBYNIoVftVCB/zFNir1okOqjZKSqxcBIbXBqb5z2E28U/HUQ+sULsg/jIDop8kfsky53VqFHTt6ch4W8xwB2Vd2lHhrV77zxqk+KB6FvOONDkxpVeBJim8mOQmzLASw+wgdkUaebASz4zXhY8FsqYiEZWT6a2jQKZJj6JpxOpdMWafk14W7f1DfhIOXngUFuwTMi+CaBcZ6JJ4zbijcwt+GQXf0I94KDe/uJazs3XxVHEMwY+1k2ktK8deqi94brfOeN12kyOIdM+WzO7DHf+fPGNBnkZ7daJYen3SZ8vnZbDXkC+xUU+y7Bfhuub7DfTHdwkckmaqiEhpqZJqx3xpl9QUu+HAgaa6KjgIcaAsO3DXR1pP6NA/2Ve+tAN1AfCg8hH65sMTBk2WjwdpNHmZAiJUraCjF9GtpvXOzbzwpaFy/uLyB/THibcbeBSPPSbQanKBKaetQ3/rREikbct1Q8CPWNNwj1eciIATFAJx2Zous34Ro1vVZD5zMEA5DD4z33FrfznXtOY5sMXrkl3GuK6nf+FVuicO3SFrboq+jmE/+us/6qgrr4wp39LCRE2T/KK3/QH5TXijs2r6EGCgNjDw867CopNwGhBul85zpD2CaD/kNuQmOOI5Hbju9R5DZVR0Q+/OCgwydmJnl5/4TMfOaeiJkWg3e5u30AtzzfW/uJf5dbfx28bCVpD+/Abh/xt7heC/6Va6eRglvjM8hzkLKJarhODl5DkcuD13bIHGMzN/dtMfY71xCr0karDCUdZ2gQNc/UAE85bSR+Ki1edrODchy0bAOZT7HTdFCSg6xIwQ8At4+8pdn5zpdiponagOcQ6aiMz+l+E8mwVKuBkTvpNXvDdr7zxuw0GdrIXuNQsnHTX7nbGN1g0CXZTdPHcUN2m/Bdj91WSiOLudr5Lh5TlavCdF29oYUtuaFOosYawSiy/YDbbCg4RXlnqKMHJBj0m8jCmZRHrm96xQP3WsgujFWHvQW42UYcd9ZtwPcEsG2GmJwmWYaAR+JR+024TO61GlSabSozjrpsP/IVZftdYRw6TRl3LLqBaDy6jWK0qDims9dCFjtaNQLD1zj19YMkgqffRHaPsT7Z7yMINndJ1R5qYDEQx8NvpoYN3UcdIbGQ91qooVE1H8agjh1UCEBRjENZt+2UIxyaqGe1gHP1wHOmcq96wEUvC1efJ70movhxtpXCqaPJacE9ajRfReeLpoFCoLJ4KOarKFxZdajOS3i+ud9+F1rcbRM1qeY8WxUJN6epRMY5rXUwGkJDYezegNSbLPlLmnX7Dofq07ihZR26T8jaN3jdR0EIBfGDH7an5LFPCUXl8c6vLPmqrKEfPg3zhd/aG1N4D7xajkjfbOmzg9GT3NdCGY8vKt3EZPLUPaFQ9m6IgSB9KkVAqb180mcYF+66/9arzzPFnkpEM9tTl17uljMX9qGdUbri2AY+Fhn93qyklfeezIy08tHYuoTIJ45uYolkrwf74K3pJ3jKpk8i8yRq3bzF6hPKbyjRCOxrsFIZ8J96Mf24z7xIb/nrLX3SqzOPjOhuE/fkds5vpJ/owZ05iR3WiQllGzqfpRFJb84NzdF2vaosDvHm1288vOVxtzsp4YKzOwPAKyPugnSLbxCUGFE1djKPc2DDKYxw7ZsGsoQJnab+WKAkQxZE1/liWtBiqnttfS6BzhtrBoDo4bQ+G+oHuVIzpd/Ih6nSfVRMegofDBuYLPWrUOZdKsdu4bUTo817J0tQl71/ZY0Z9tFrackIXukakM08nF03gDmE81s6QZztx33sW3bvfHJOfu1iHCafn/OmTwYP/9mQ37wClcx7r437Ge+6xEttJnrprk+mzGMuMW5UuomJ6iYRIESJEgRwe3LZ0v/olz1ik0elm6M5n5Q9zBNlyULhtnO/WHg3N+WOIksz4EIqmIsdrXXD7elz6QiZxG/gnVk6q4jb07E8zJRp9Z2dlnB1O/mUK96m3vvmkzE6gtTt5N5WmYgtnKQm3POovINPOeFkb2GAyNKxGFuxtbft+uE6hVsYS8zYXlP3DjoNmbIgmU01I6aXaSeZP07qm3LiJCltxidbmEZmTee+EXJDqfsgif2MPTSx4iQ8cliCBDx80ENJdBwwuGLJ8MlQ2MXbEdF0ApywRYshR8iK5ljcnjSEnOi39XEuEmSIohnh8FTUJW1YHnpNvUnDuCzopLCiTWQhL8R9fBr6nMF5LHJs5vNIHpYWbnNvEjMNSwTJxYaZM9DRG5vkCcNoaIpZwAzu3SUJzwTZ2ri38gZgJLe6w3xRY4gCVDbRkwS2IHmTa2mVD64rwlJoI7NqpiugH2Qs85dIWvu7u+iFUjPXF9RXL8wQ+0gkrQeJElDDIWMeLJEkHVQ9NSuDGOFArZCwkeaoTspFLywe3sRVentboofGTjaLpRFPGRBiBvESbhJWyBJpHiqLDcR0fNlU4ZY7FnGScfbylkrYpdR9kAVS2tWsRzE+UpiSFKSuWNrjBz2mKmNlQEZkigJ0Bcj+F7iK6A73HVFup1UC3Eyza+GwyiyVwxmFKSpwmTRwMuD81G7mZrziNj/6Xu7pOqfOMDwYdchvKPEVWocddrMml0a7KCOy+Yyvgt0+Ai1g8Zx3WjpCfcTZbnI9r9nHXX2SBS0ltwbWz8h6OaxJd3F6ahvyJUZnv5F7M9M/qc1TXWngOKfV8Azrh4z3EoeTrtSvbsglSb+H6e0080Ywk8K8pZifmtyQ5DWdRVxAL91mAGUmV3mLMjcHeb93v6Pbma1ASqe1bGON6DSzSVYiDVa2Yql2DlEfed3Wyemlzzv6jcQomz/v6KbQJz3bH929YqI3RKWXbdwOPtSWLAMIA0VaUtIZaxTeunE7+DRSpmKRrEKCdMtT6+hDmlSqRhBoWtUf9FlHEvcrvB3kthOTZ/pwkClXQXrxq1GYEioThk4LH7Pujzxe9Yx1GxMrDr7ld5Cc3qyDb3m1Ohgo0tobjlhT8V2RL2Vr63mdBTuaeiBiHnSbuCW8V8uk7efImmjxlxxl+43cH2X9k1pXkFnfIMPnMcjA5gfMH6kR+oQPdXFGDtO3WyOHdBXWvzFnBLNuW/hiNvA7OCKE21O44pWKDzlizTd63pX5w/ZyqxemYk9Tu2hAIVJNPChEuu5S249bUkmfRE6VJKkpJG3vwzCSVIUiQFTKPLlhy+C6GO7kVjqmYU1dgEq8Jjot3BLdLdFFugnLb5mTR/b/Fq6YSrahI8S5PXv7q7QWmOlBqC5zM3jk6zY8pDMfU9VMqvAELX2oOm61NtJdXoTNlnzhQuY1c7uaxyOZKSs3sMVxW/rZ3Djl8irdLauCZ+ErVFjewrbuV7jvdFDdqn4DzyxlzQfPgE6eVYrqGVb6fqBGoQMGyVaGuLGPxTE5KyQuAmFb956CsdnArwK55tSo5GgO1b4SeRGV1SwFZ6hSphxWPzxKsealyX7DVo9cX8Uwh0EkOT3IOrg+OfBrZFZbz0DdS3tm1CZFf6hhvgj7eiJXCol/7FIrXGqQ3LQt17nule7sc07WXLKV80uLllu6vF6oCI4IhmOWyLYubjsfu9Y4ZA+R653MLnmOZrOu6LqWmKf9Ru7t0l5V2rKnsOCsieBGndtkUUyhoKXP6/pe7dpKhEUVY02ycCckGSPcBenrxUv4GMRbcItY29Y75Vhdg51kTGELsFYM4RdXZQ0wuiZsaXVxS75aMoH8oUw92/rAyO4Xe12ztVg5xA/1kRDCqRNb0iOp/yqEwNvxVarX6rOJW611fQn5WkLSWnLTbBxwLa1Ouyoj9VWKzbpiy1AcvkIvMbH2QfmzYNearY4rZRLbVkwYr2IvoUdWiVfEGhFDvEkNM4aq0NCdnJI1E4lpK+Wum8q7nGNZv5Xk2MQrAlweltgvsqNXr7pvA4Df2zScmC7rORwWz2/tw+QWFh9eNeHF0oqkJk996BrB68s02cn4IWvugyH8csiVY01a3diaFXeJBiOoxofOBqaw8aDRJmnt01zjFnAmIOR1mU3Ci3XYIWntkx3cysvlHi0tqGzKjrYo9LoBLWIGp61zQjgw+gWwKTCSotYGhTj45aKl+8pgHx9aZKBcNgGkWvvaHZskVzBDXdzfxEzLIrZc8hr/yU1mz2kmO9l0C0ZX5xpRKehe734Z6AaCpLizWWU6uhztGjugz5I4y9MA4pMusjaaFVJX9LlL1v0ytpz92hXs4YVpUP6HlV1Bed9SiIdK9jpgO12LT4GTVHM5UVpl/yZlCVWoWFVQmNrGfa5ZQvQvdZzSzqv6nZ+okLIdY9sXhHJ2Ne3kyCu/UJyAbKoatersMwWsOVujHUT/8sSp313unpLC3HaMbQuOydnVtJMjr1zQbAKymXLjqvPfqVEucHbYQfUvV9xi7a17RFiD3ZbJ1NvuIc61TRXIUHtAPhELKpO5V09e2XoS9JekknE/1ggWm5DM/vGD08jfNDEftJjO9lQkXUzv4bDPSNwGWWsGdWTJVZ8+ayYPp7bsNHR/1HdP+odfSyCY8WiWQdp8+/ArduXtguoH9Gd5ofgl2YAoI79++PW2QL13oPzrHGRw24L4gGDGIMRjtkDrNlfxA64ntQcpIYDGqG5Sf24cDXmwCfLgJM0hTp2PPodoLaEj1C/vSHQkvty7B5ur+LrI90WOSAa7+4i5f/nwq3z8D7/2cP5Q5Wp0QQJCEyISwHV8WsBo0+B9GURZZ+sTgThD3P8I0O/lXKKlmYPtawPpaxIrAqrYdw72IN6gJXcHdvsIAcuu41XwBExw+5aBz2AbhK83uOo4CfYUARmeCJbtH85hsE2DXVbBaPujP5EMb3Yv//f/AyC9Zc1HKQoA + + + dbo + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/202001221054109_ManufacturerBottomDescription.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202001221054109_ManufacturerBottomDescription.Designer.cs new file mode 100644 index 0000000000..362b02beb3 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202001221054109_ManufacturerBottomDescription.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 ManufacturerBottomDescription : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(ManufacturerBottomDescription)); + + string IMigrationMetadata.Id + { + get { return "202001221054109_ManufacturerBottomDescription"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202001221054109_ManufacturerBottomDescription.cs b/src/Libraries/SmartStore.Data/Migrations/202001221054109_ManufacturerBottomDescription.cs new file mode 100644 index 0000000000..a3603f70c5 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202001221054109_ManufacturerBottomDescription.cs @@ -0,0 +1,19 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class ManufacturerBottomDescription : DbMigration + { + public override void Up() + { + AddColumn("dbo.Manufacturer", "BottomDescription", c => c.String()); + CreateIndex("dbo.PermissionRecord", "SystemName"); + } + + public override void Down() + { + DropIndex("dbo.PermissionRecord", new[] { "SystemName" }); + DropColumn("dbo.Manufacturer", "BottomDescription"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202001221054109_ManufacturerBottomDescription.resx b/src/Libraries/SmartStore.Data/Migrations/202001221054109_ManufacturerBottomDescription.resx new file mode 100644 index 0000000000..00b21c4d78 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202001221054109_ManufacturerBottomDescription.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/202001301039020_DiscountRuleSets.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202001301039020_DiscountRuleSets.Designer.cs new file mode 100644 index 0000000000..198f5378e0 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202001301039020_DiscountRuleSets.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 DiscountRuleSets : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(DiscountRuleSets)); + + string IMigrationMetadata.Id + { + get { return "202001301039020_DiscountRuleSets"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202001301039020_DiscountRuleSets.cs b/src/Libraries/SmartStore.Data/Migrations/202001301039020_DiscountRuleSets.cs new file mode 100644 index 0000000000..d0391353e4 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202001301039020_DiscountRuleSets.cs @@ -0,0 +1,84 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using SmartStore.Core.Data; + using SmartStore.Data.Setup; + using SmartStore.Data.Utilities; + + public partial class DiscountRuleSets : DbMigration, IDataSeeder + { + public override void Up() + { + CreateTable( + "dbo.RuleSet_PaymentMethod_Mapping", + c => new + { + PaymentMethod_Id = c.Int(nullable: false), + RuleSetEntity_Id = c.Int(nullable: false), + }) + .PrimaryKey(t => new { t.PaymentMethod_Id, t.RuleSetEntity_Id }) + .ForeignKey("dbo.PaymentMethod", t => t.PaymentMethod_Id, cascadeDelete: true) + .ForeignKey("dbo.RuleSet", t => t.RuleSetEntity_Id, cascadeDelete: true) + .Index(t => t.PaymentMethod_Id) + .Index(t => t.RuleSetEntity_Id); + + CreateTable( + "dbo.RuleSet_ShippingMethod_Mapping", + c => new + { + ShippingMethod_Id = c.Int(nullable: false), + RuleSetEntity_Id = c.Int(nullable: false), + }) + .PrimaryKey(t => new { t.ShippingMethod_Id, t.RuleSetEntity_Id }) + .ForeignKey("dbo.ShippingMethod", t => t.ShippingMethod_Id, cascadeDelete: true) + .ForeignKey("dbo.RuleSet", t => t.RuleSetEntity_Id, cascadeDelete: true) + .Index(t => t.ShippingMethod_Id) + .Index(t => t.RuleSetEntity_Id); + + CreateTable( + "dbo.RuleSet_Discount_Mapping", + c => new + { + Discount_Id = c.Int(nullable: false), + RuleSetEntity_Id = c.Int(nullable: false), + }) + .PrimaryKey(t => new { t.Discount_Id, t.RuleSetEntity_Id }) + .ForeignKey("dbo.Discount", t => t.Discount_Id, cascadeDelete: true) + .ForeignKey("dbo.RuleSet", t => t.RuleSetEntity_Id, cascadeDelete: true) + .Index(t => t.Discount_Id) + .Index(t => t.RuleSetEntity_Id); + + CreateIndex("dbo.RuleSet", "IsSubGroup"); + } + + public override void Down() + { + DropForeignKey("dbo.RuleSet_Discount_Mapping", "RuleSetEntity_Id", "dbo.RuleSet"); + DropForeignKey("dbo.RuleSet_Discount_Mapping", "Discount_Id", "dbo.Discount"); + DropForeignKey("dbo.RuleSet_ShippingMethod_Mapping", "RuleSetEntity_Id", "dbo.RuleSet"); + DropForeignKey("dbo.RuleSet_ShippingMethod_Mapping", "ShippingMethod_Id", "dbo.ShippingMethod"); + DropForeignKey("dbo.RuleSet_PaymentMethod_Mapping", "RuleSetEntity_Id", "dbo.RuleSet"); + DropForeignKey("dbo.RuleSet_PaymentMethod_Mapping", "PaymentMethod_Id", "dbo.PaymentMethod"); + DropIndex("dbo.RuleSet_Discount_Mapping", new[] { "RuleSetEntity_Id" }); + DropIndex("dbo.RuleSet_Discount_Mapping", new[] { "Discount_Id" }); + DropIndex("dbo.RuleSet_ShippingMethod_Mapping", new[] { "RuleSetEntity_Id" }); + DropIndex("dbo.RuleSet_ShippingMethod_Mapping", new[] { "ShippingMethod_Id" }); + DropIndex("dbo.RuleSet_PaymentMethod_Mapping", new[] { "RuleSetEntity_Id" }); + DropIndex("dbo.RuleSet_PaymentMethod_Mapping", new[] { "PaymentMethod_Id" }); + DropIndex("dbo.RuleSet", new[] { "IsSubGroup" }); + DropTable("dbo.RuleSet_Discount_Mapping"); + DropTable("dbo.RuleSet_ShippingMethod_Mapping"); + DropTable("dbo.RuleSet_PaymentMethod_Mapping"); + } + + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + if (DataSettings.DatabaseIsInstalled()) + { + DataMigrator.AddRuleSets(context); + } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202001301039020_DiscountRuleSets.resx b/src/Libraries/SmartStore.Data/Migrations/202001301039020_DiscountRuleSets.resx new file mode 100644 index 0000000000..77687f2ab1 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202001301039020_DiscountRuleSets.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 + + + H4sIAAAAAAAEAOy9224dObIo+D7A/IPhp5nBPna5uhvYp1E1B5IslYXji1qS7bP7RUjnoqRs58pcnRdL6l+bh/mk+YUhmTdegndmrpR7oQDXUjIYJIPBYDAYjPj//p//97f/8bjNX/xAVZ2Vxe8v37z65eULVKTlJivufn/ZNrf/7T9f/o//+3//33473WwfX3wZ4P5E4HDNov795X3T7P76+nWd3qNtUr/aZmlV1uVt8yott6+TTfn6119++e+v37x5jTCKlxjXixe/XbZFk20R/QP/eVIWKdo1bZJ/KDcor/vvuOSKYn3xMdmiepek6PeXV9ukaq6askKv3iZN8vLFUZ4luBtXKL99+SIpirJJGtzJv36u0VVTlcXd1Q5/SPLrpx3CcLdJXqO+83+dwG3H8cuvZByvp4oDqrStm3LriPDNn3rCvBare5H35Ug4TLpTTOLmiYyaku/3l9flLktfvhBb+utJXhEojrQnlL4YLCte0Xp197//eCEA/cfIFH969Sv+75f/eHHS5k1bod8L1DZVkv/Hi4v2W56l/xM9XZffUfF70eY521PcV1zGfcCfLqpyh6rm6RLd9v0/37x88Zqv91qsOFZj6nSDOy+aP/368sVH3HjyLUcjIzCEoKP6AxWoShq0uUiaBlUFwYEoKaXWhbaunuoGbcnvoU3Mf3gdvXzxIXl8j4q75v73l/jnyxdn2SPaDF/6fnwuMrzscKWmapGpqfO6a6yf0q6147LMUVIAY9Qje9ds84lOs/X5uNw8ndT1SZ7U9fwEKtK83aDz4irDZEp2oTQ6ry9wtx/KaoMLGpRi/ghFOSCcnRbXWZPPz5JX92XVmJr6yy9R5haLdU0jv/7lL5HYdXaifUBNgkUYYYP51wRp7C2q0yrbdTvMAu0tw3vvsy1e5pvrkkrwOnRlXqJig6qj+mu2uUNNKLYOy9/LYn46dE19rZId1qgavGtJfbepj1fyAzdvYSM/xsyNqgjyssrKim7D+g3dQhheJ3fzy8P22z/wPnFdHqV5hN2HqFD1vSsVf3s9aYF63TB5PMFKz11ZPfloiMnjKwbDQUlUt2VQD//8i90O6chAb7N6lydPn8hKdFk/1vxzhZqGjsWZd7Ckus3u2opCv+rxHDjIm4N+nYeDviR5G2MDc2yW0spMXS+efV+mSZ79C22GNj24t8fRMa+E8MDG6ra66XCbWkDzS4q7NrlzZBEAD5k6hOnzR1W2u+UF9Nj+vppean1/TH5kd5SBFDP58sUlyilAfZ/tOruevLJuJvCzqtxeljm0oEeom6uyrVIyvtIIep1UVOv3kyljtwJFSY/nIEHUbRk2wjczLZd+Znqqa3fiOdrHtf/ZoitUnmAcutYjnGHO8uTufIsHe5blyEDuKKadyyb4rBTZFuB58IqngmtlJhXd3WRcoprKuFotQAVItQxVAY6ykROjSuhB6PprZwLqKAqagPMgYc2yLlS7Gmi9n6PL0PqejjDnNVldF3l7lxWSEDFVvS7bFBI+8bSqcKEA6lZGEeIlFC5Qtc1qsjgvUUrvTpwFwhVKW2JGfCXiOggCdVtWV5u29y7e2x0zY5jRPiS7He4CvN+BoDcy+0w8bldD2gEtqwVthWAbkVh/Qnjgf3VbR3lePgSrkgJThO6qJ3T+UUXm0BmXcpGxWMGlxQLcqJbktK4swKVFZVMHWlGWwqNf+0uKDXGPdJQ2XmKDn0l3w3xfveZm/CAl1G0ttD+Kh/QKoSvMwt2mECairpPH00e03QVfemNE/YGX4BGmwCBr0yb7EXz3PHhIdcwfhiuq9jODsjODRBbllYsU99NxSiwT3IUUqVbTfw+CSd1WrHN8JN9Gr+XXu0nN7rcRzeBH3Gg+Fe/wqrmgx+kwbFQD/aNFdVNfl1/KJhhhgD1SuqPF6+4tZszPzeiLSv68zrbGuqfFxrNmqJ3Xz2RCJA1oIuEKZHWPK4UEpXYrwLWPivoBizNlp7rym06M8t1iiuTjq1Aedk4dkQVJ8g7FQZ5rZBSm0vOU5R/b7TdUfbolEqwOG8AcFyrd8glbYtDah5agS58wuagxNe0chRUdZKFu2MXId1YBBsoGFWywnGARB0kLFlE0mfHiOKlR3wFWwx7cao2rsyNTPKsMO5vzzD7EtiZOCTJLRDFJHHYJdVsDjf5os7HV7rery0GN2zXd/se4/j/Fs5zP3orFw5u4DZ2V1TZpQjfsAdtVkjezd/1os82Kk3K7ZR4RzPiaLZrd6ej2NsszvFxCqR3HCvUW5SjCU7HBmHWUpmULvOqYw54Vh4/eJ3VzvjvabPARTffCydZZyyDxKkQk5acCPE86O3rVzfvyLit8D6i4PuUiLKqVKFxnLsnb4X3wvJ5YpodbcSbsLKvqxmRY/TXGcz4yG4s0dIZn1PTII8oMYem8Swqdw3Cch5CDGtEd3WZnieOsau7JAnFdb3/QV3uzb1ZfkqajxBVeiG0dus+QkZG3gQs8wObvZMI97+sGKzbt9ktWZ42PcCRq5dHdEgrG0Npb9CNLEXsjFa9J5dGt3/wUjpnD6rphwOSrmqlUeTPDgLhaGI6zPMdUGHdpXTdFWKCvPIi6wwKca6/FM7mu2z3MzXT2lPstwii9EiRAV1cE9jLNQG4BFKA2B6EmNg/mSuvTR3L0TPKjtrknh8+UAunsUboa4DRYVZDmxK6W6wRRuXZR1g08trEYHIhcKvUaAPHqYheZRd1HWq7uJF8M91KAce0mtc7CPaRFYOf4EqlfQrFrly5R01aYRf5JL9PArnEgYBdhCKmrCjD3Lj8k1eaizIqmfpdhJOSVNthvCU7RezUcMAYNsOtIBkcVq71GAgbEnwCjFoAioKsIvLovaf2TpGrO8fEZ7rsIBZJfCSTRXg3pSvivSZ6jRsc8HATYbxhC6rQCLOjKYGQBj/fl221ZvOoRHOzF6rasjAvPKSSRjXUh3gFjkYZMFvI4rZhNC7HaaYumkh+BG/1NGoS//ciKVD7GGlpkAsjMNqxe1rxZqqFfZ2/o79mOqKtJbnh0GskF674skNHcFElGJI8LtRRmnFYfJrs1BG7pg64zwkx7uVAk6U1iubO6xC5SbecESLmLHICyozxU2A31QC5nZeNtVqGUqD2vehwHfUPdlmHDnOn9PnWkrHvbUxSvzDqa9/1D+R4RIp7XS7zuv76vELJt8E8RGsSCFlVZKjTm6XQ6hFf7kgTfh67hjX9k71ZYUegcERa0fXNCWXFi5uQ2sFOBAPJJGYQKOnMKG1PAZsBhOmwJ6rZUB4K4vq1z7SzfvlXoR2Y6Qce5Jl2DzPLSST3Xu6j26aVCkGPieKEUwT+xx3VY85o135MqdNHbuBnZRhgxuWMd7XaY9cIXX1SHpc+7zSwHzNESHPuCVWWrV97Eei3r47y8Gz0ZnZc0qV2/YnCsw8e878w1epzfe4IMnlh/4nmzDxhBlmJofTMBTuwElUusBAIFs1HXlQAeIggOe4FOskbTa2K9eY11VeEc7RyrkugBeETfPfgAil1byFLCY44G9L5hv8pX+DyEueR4/ujrA2GSu/q86Psc7IVtyDsRL03K8uxGDVm9wAy3NvTaUY/vhPVZ9zzQlU18pIQ5Zp/O/TyOtswrYhtR2NzY2vKKxBlZZCvcTDdAjK4Dm9i8NCrRwKZVu2y7qn1bP2KF3tdLhaDKx0OEBSSpyk2bNpf91uFuC0iaBPfoFYdnHYeHvktr0Yz0rXSEW+Sgc5k0zC2OH1HeoXx32+b/heprzCl5FGQfSx9c6rfE3fTDD4lZbr0ZIZknxBCA/HgYhHJ++c5i6WlRoLrGa7XKFNcJqjo3wnpWjEhZQ34db1ct7KH8QP9Q6XM4cxqlYYw3MRdJhdukKSRoZoEYUpY8r8HQ58Um+5Ft2iTPn0L1EIoyy1lXKGbpsKVx711mSmBBMw8uqYCSJ35LtrfoY+xhOaAtPkCHv6OOGwVrwBPvov1wUooTL4DaD4Y3RnTri2ZG6NSyq3YbzYYQCeOAjmpnwqCD+xgPacxMhJFPxFff2/kXXVK0twk1V1Z4g26M7p5xmv2jyXQLPE4j5/Uf2W1zklTB5vIBTww1iLx3yir0qbnHFO+2kwg5WSnOSasyRJOIItRaonSTR4VY6TrabIQ+BI/pvH5bPhR5mYQ7cfR4Qmfuc5F3C3xAGDzGD4N/9adbCadn3LcezenjLuuyRr5NnkScdihoeA6KIgbbv0vqqwRrTSjWrPLYHO9acG+6B/YVQqzi6NsZDtki5pjz+pIkLKgieNaOiE6e0hx1nQqVcSzGC1RlZfDqG3HSvZ8iDlwr59Q3+bQgVSJEIYoZahzL04ysvCQfMJ7cExPLaBhHabYlRq+LCv+ipoqXb/7z5YsrklwG758e3Y8WZeq8Pq2DyckkTA5lHKzikJvu4gdemhgdVvvvw7kRa3jp97+1SW9ECRLZ3XGNYjz6kWS4Lmd+CPRdBHvqvWFlRcSRvy8fulH3gaCCPVvLJrt9ogaBs7Ia+niMmPQkfoiPk/Q7TdT+Acvn4OBp5DRIMJ53tMQnkPHQG6xR0EM/nqVs227jTFKHMXmMh3HActWgXQxMT+RepypzyQ9kaIgrd9Vbsg0S8ER5F4M2PdYMLaCsY1lAOnjcPh23TTMZVwJkC4H+mtX3eVY3cZD2wi9HePHifY2zX3nfKuPTCUWXpcH2NQ5J9B34U76Zt4H+YHZC77dnauNqh/EkueNA7HGODiPEK8DD9YPF1TuQeGIazHmnRYOqOgp/9WKbw4xmZopesC/aJj58XWeoW5PBGx7WIVDdHDVYdH5rG3RSbr9lRX9fGpEJcZ+xzKMBRol/e56Fnxi+ouzufr6lyJ/joqP/mm1mxP5uXtqMO02oPBkRhQmTeDc28V4+xQ3OO17WBxp9bILzxrEer+j1iTw12Q9UPZHKjnavQZPFlJDv3m02vPo6qbLbW+M1wZ/iBHGlD9c+3X6qsruscOwwcQLrN/ooFp4R3weU1G2FCA01FIgSxnZs82jLuvSG7mYjWvKDR21H2rbY5IhenhqMnXHWYtfeBapImK9YNjYOKaFGbJxsfLLwm4Ks8HtWgDUX/lmCc1fUwVs7RWhUjECfs6H0poe+LqcbosnPTA0l+ZZpQJ1jjfICRuczdyPBSk5yIojK8U+Cc3X9YzcAbZ95QLnDbLmytxyQp5dity7UkQElsJt+JSl9LFWgKldEJbxzqtz+GN1Zpw2eloMNW+MsKoKoBiDBeXacvfHX9p0F1PQfAlONAYT1HEcvzbRD6GE0vRcgVB0XwTz7PHqsxHMu1nrehvaXmktu+7jB4xlb23+4imY8+gqq8RlqeY63e/SkEagcnCxPmWKlOGVhPKXpF6yDYz0eMnpou6+pp5kgi1qqWbKp6jlVImqngTuM1nWI3uNij2ZaHuQBZSZky5VcyAE5h68THDk0nRVB5e7yEMoOC2CuXWYNjkB3x2KIM6RCiRdkiKCXDYOG6xVWqdfFx1+H1w3qtgyGJNuAKO6XZ3RmYrhVfa7JaTPFw43gQD50TMYY3QI7NOVq13B209jHA+PeFbLG57xdWbDh/LyvMSVMs8XOGmaGOjPTVRacDGfERIjn6rRlsj5cl4ajmGwvYCvoTBATnLQtaYFdN6cRh/lsJrcr1NGNhwO1GBIP7z2qyd4zh3nIPAzJkmRtZ+lR9etZ/YZ8bBOuAYwDAlQPBYR2Hc1lm6MrZBrBBAX0eihU93SECAs219s6At52DigO6o+6rUj3aI7bgU0OwDg72YKPyY7LBvPpki0OyZ3eZ8V3nQ4bJc3hcbK5M131RCIkaemqeZqe5fl6WWbJAjlVeykT60Ho4cFlpNzS5Kl5LAdyz1hg+Ph0lf1LZGML19AxUOp1ifdmlDYiqvEUYdeDT7xr8WyRP6kF5DIp7rQerXFmOPLL6fjeXSt+V7o2H6F43k9r9tm5TbCW+SVDDx9myRIT+5Le44xsvqgHDtTWZv/BeQGKsztejQ5ATHxdoUyOqysChAXGGnrgfHD5gDZZ8qqvfzi1aMRXR6LjrEiq6UFT/5fNQjJ5YW9NSZD/PMu5iPOotXPt4bxkLYOMlmdZjgr9+etPkd7ff4wQV7S+rpKizmI80o0p0OlyJZwMRdG1FWosEvgarZdJPCBziQaUy1doEJDn1bOT14UsjWEIo9eFILq9JDNPQk/xzCI5yGh1W3hZJT6iWbsIXSPD+e/FQoC4w6ZsGydurqP2cicKLwXUW/ApvB1U8tFRWMd1jTP1VRPH0l5IMzdOAeuWRXNYteq29nMB8HPb5VneO5iF12UWPhhyV2fIPdg2n71tM4b1PLb50tsrxmzEhL1oYqiR/PMIWYmEyiW1DAQKUsoA75MonqkMvoOSphUhlFwRDkUi2am3js2T6kjpXnZodo/PPnd1pFypY/S1OOguUY1pm9L9bowHjFeto0YyoRmP3fPn6mEaTZ4I83SB1JZseJgNsx4Uu+VePXGcp9NHLKhYU9gC133T24H5/BSVe6TWq9Fr7+n9CU97SWm165A69bDtcAgO24y6rUhvIWIc/WOFf6ZxBYOdQ67SUogrR3mq++oeDqX9RlNgBPuElHck9PKnHZnusuL615eRbk7lP81x431SN/hTiupaidJVWkZxilaKRclt2vqwIOy0kDmXBQE7CkPId24wmGuXqfAFe0pKOjE8YGd7KZdKPQRAXHs3aBA6ivIwIEkVIFKPVXBheY/YifI4iPX1a551Dlujui2OULYhqGbznbfeQ2NsopdYgG2o9YqG3Y4USinU6Of3xsVbVEo3X3qJ6q3ohmm5BxXXyMzdTAWf4jEag4eY7esC1xixorYH6elzNPwlydv572fmuLYfVY0YGokoCDRKi5cQ4PUFj719QCAoMQehoG7r578Dj3cHdX5XYFp3qSjCI/fuRQkI0O7F1W86BfhdtnAh2zxuWSqUkjQXr1hEh/Xvvf4tw3g6x3PIy+odejTtq/O03guE9yW5ZAlvfTHpU/evaLTuE65eolNgwHBH0QnXYcWp24rkK8qFcQxFFi2VTxozTq86II/vphtJ2zE+zlw453CWYxZkY8GFUYkkfbm+b7ffiiQLfr3Z56hej2fTz+iSpPYfGpii4xHL8LNCrRt2k9CEolVXM4el1dR1tXILsXPnjbercAs3xueN6MvOtGU1APuuR3FqN011TL1DQH1QQ9RtTUQLzug2ED0aInouCMV2TgK/d47HMT0yWVm4H1lqXLcWcjhkJUcITyVgOqxTdVuRNPxY0WDO6zOs9bRTpus96mPqxAYjh1rEpp+A1cHpxx+KtScDzrDP+0TTN/U35g4f6fUagO0gH2aXDyy5/y1kBM+tlnkg+Er6ZBDcH4p1qK4wg/wIyWhh0/+YsgROcBAuVWC8B/kyu3yBCd+5skdJKd3pulASK6O17uFT0Y8SjKa2vGyyWMyxE40oFrhlehJrB0QNF3iMVo/OlgQ6LI500aIK843QDtVbLOrQHqSiqzgLFWSRnqFFjhUbJZllPLN7l3yTuzqOnyw8TiAWes8dwU/Ocd+Il7dqAQHrluHKSlaH7Uaw34y2XQUexqfGvbrsb+OBY4YtJ/pmc9hmDhtC4CtlkwYf9zzh+mghSVFzVVYNg4vKFL7AB+sQYOddNjldTKj5UlcbTrFBj51wIR8i+ifq5JjCaXF54Ss9ZQoQ4CF2kOvkLtzogZEchKy3kJ3JBdrfocWk/kXL4alQzcA8nyFMrsmTGc78GuSHRaFu6+p7O7uj3B9NNv87eNZEfYFnuzs+zt4sDSIWyxGy60gsbFyk7VBkfBDuUGzHSY0o5VzD5FiiJT941Hbh7Oo6uyvwKhrMAobUOnFiRUwPGBwDr7DZbR2rxooaEenWYDJe/K9tHuEgZJB5TZl+j+MKTY8Qn9rm0y1FSk85EXVo/pmMX8prHodT3mu2qspCbl1/hpvO+TJ9ewzW967ENiO2rm1Dsmzbqj7DNqbYttYS+UEEvAljER10P3Vbe3kTRv69yNsq0cn5mR6EWb3ViPkc7Od5exY1ZYEoT+Kf/g7LXt1WJJ2tRxPtUpTk3cSftrv5s2+e133ovuBXRsyWWDRVmRNsq0zr4K5MOWhQrmqTt64k8pzPeEDbsW0d1xHqr+gshioipvfhWssfWONGKXmNNNDWV9kO3ZDEMC5G3EwOu4jFLrJM5uH9XIwu+ag37iVkPFVw9RePAr7Tx11ZNR8SGqtkhqDJ1ntSf8Opjs+mhLfZoWyqqYSyVd2osnhqKKJUnpAe5LO35Ix1+xF4Boii6c25lBT6ntMyDJQkDkOX+sAuQBMBNJUtJYoOwwxyJbpMOcgT+6U+TW34s44VaJKRAsmu26FYmlJyA7v5B15EWzRjspKv9O54gYbCQgHMpkrTw28My9D7rPjOZEMJQ+Z5AeqhB69hA7Pbx222wJiG7/51Q2zrN0V72Mx0kg+k20+xk8UxSxw2MqCdw0b2b7SRyabytdjcLe8g7Az3XtvZ2/KhyMvEJ3b3B7TJklcDgsMmpVm3PY3+aLOx1e63a8zBGg24PlfBgR4BVPPF9O7bOs6KpBoX+PCXTXOm7alo8GQQtLOP5SzLUbHE1n36iMdUL3F3cV5/RA+h/HReX1cJ7i+zn/qiipqAiwoqsoSTO+eMiHR9h+5rHRaDthdHgSK82cvdud8DntyTjNv48LLgFQ07lXAKtF7O3PCQTBo0CEDaiGGooJ32OkNV/8bA+5Q44jjstuq2IimjcAJV11OTf9LcOG7tjq9anGMU5mnbrcEhBQxztTxyKwDmHymQoSgoAcZWb3jQSQTAEJIMUIDF9H6fmhihoG5eKJyyZIgYpq1L9CNDD+9Qvrtt8wLVdbhZS0IZTX69IA+EGJ4bpqrXB1/aSIqud6FL/WtS9wOM/j6066Du2CoR+EaoKh1UDTVUR1NTtSAGPCurdntR1s2X0sv7i9avX3Fo1sFpY5dCuayjTCz+GrsF8hZHxxsGdmImBYjEPSq4OOwSgVUOCpW6retyl6WxNKEYzuXL+xGeXxxtNhXdC2c+wK0oR4W4GcWPfzAwhV783ExggOQZS9VCZwJxVd44uQX77wRISdEXxyRNnXpNV62BsCwgRNqpXENcBihcnPedCZLnFMdBoKvbolRajUAnsxXjKuqq/fYPlOo2hz/P9BCyWwh1oGaH1edAFO+TuomhZA54Yk3xgK/bdsRt6LAfOuyHnZxVboh8MSy0BRivLdHUwR4I7B39qetaBxC+lwRuI4cdRN0WJdAfVdnuZo6g++vzT2f8sefrQOEeZ5chojjKee5n3Gv+vTINTmtYLc1vWCBBmjNlsDRnAcKled+JIJFOcRzkukbI/PTS+Kdc45JTXlimevhgdV0epRHvNrp1GS56QCMKJJu8RM9HfCg7Kbedj6iz7CG1XzEo1nEZ0XfmOmu0EW3irLehsThmW4P0wpT2yo+pZNIBI8imzMTeTIATq0Ll0k4JAgXtllNX/Pn1kBLepGUWdy3kTuZq/Iq0Bt0DsFd7uEU5o6Jr6VajJSJXvaQweeDQm3G/ylcNXoFEDwDVApPX4cazJo1k0QulYO3haLeryh9o0+M7AWK5up6EyyY+0sg6U1Tl8ANqEiz9HspKG8c2Uvhl3NiSCjlpzyQGPUem3NYH8a3c1ukuPEHxezpXCG7oPISrhZVRCWD92EvxEPVkrXbipXgQd/WqSPKjtrkn22iXXuESpZhvfXzIep22fqVDfNBSNEIokk3tFM/H/E9EhlnuRnebaWO+x22yNwos2PInwsuU+5Zp6ihNUV0v0yD+80e2QdXVU41lisGkFPuFgPYKTSdIwEs1qwrSFmBXK+iQ19uvnIVqF9e8+99BdGoWDSGQMfNEpAVK2gp+Gxj1vUIonuMszzGxeqe5YM8OvIJ3GnQW5MUHqaaN1pE42C6SJ6J9RUXWva+YU/IqOOakrSpUpE8nuOYCjXaNXSaTN7Thcct/eq+F6+Sx1w9ieCl9ScwJbSKKlav2W4NFYn5epPk1QTvTSyCusdPHZRq7Jo3huUnJ8X+pEXKNLjPSXuosM8K+sUVHhhtyWMoe2YFZ4Yh3kYyoCEl+htDcNFW3PDeB1S3PTe0e/wyhWyEemp1Jh7U+3yvKCuHZOU7ypJjxrWZHLCK8LvFoNkyI4hmbmq0JfJLAg0Ab17Rjzs08JNXmosyKpv6KKoTXUfg9wsk9Sr+X7RTCZUlzq9T4Iom6Bl0q1r3Z0e1tlmdJeKCv8cCzm50G9FaFnNIw+m7Jn2De4pVBb5bCmAiG+SeSdHmRc4VEm/luIJL6O9qopmTWEZ78+PHrIg2dPu6yqnuBXxZTMsmF2vwvlMxPT3Z9dSnE3qJvWXAoGgbVUUoVgXdlvlmAP+SGF2JMpuHjpPi+yIleaHMREcO2eX6yZHP0vnoKg7VEk+ffkgWUi343pQrg6DY397pv8Qmnyv5FJQ2NQJWk5OekGize9CJLRtX4JaqZvG8zSvgduRlYluByowuN9qr9Nuroyw75oq3S+6RGS95IXCSZr+/QYNIR4gHNNi99c8TggAXOrm2YMEMLmsHfohxFiPC6Xp9y9iR8ichdImNBsLqHeZeQIIO9WepjSS7au4vRYA+zNEW75vo+w/1L8GfqrvAuKTaffnicrJQ3y/ydFni/TNfojQg4XSdD5dLtMQjk6kOkvQbvWoDuu/kSRde8n44OlrTPdXKH3mU1yfELR/EDAG/6K28mlJ8SSnJ20oC6Blf4I7ulp0TjICDAm8812nzNmntpMGZoaVAWVVwHR2sRTzANf1NHMan/QpHUWbHcq2cflVE4xmJFz6YiuGdMuWvPLtEGoS3asBLytFPvgY6yUEamMAJLgzHXcB0e2WHVrohDqUx2vkTqqFDs06udlTSWIEVpJwAohJ4I5Sr7viY51hB08oKDkOkJFEtEhWCCXDphie2R0b1DU4OS/eCHpG5rvN0OtHN34jfUWB6knRrVAntVYKph0AaGj3KAXy206+LuF+scmoxVx3lp4LXSYbXG178QwnZY5uq2BnqFrlBuq4+BrM9rNN/N9xwCZaCmvVo+1TBo5MNHaV3qoV0FiqARLnG6sBoQrHwGCZxwIXMQLOq2BushIxWgB9J20imG/+PMDhvn9dDZo7TJfiQRbHIDwpOy3S1k2r/ExNiRPBqL2C7H1pZ553OFCnLgXmJkXVPLDOsDPhXSx4Izt3NeT9xBjaj7tjrPZClz35Kt7GPSBm47GliSasdzo6ojj0gBqtyVVfDhj4o8Q0ewD4sO0SNsDuOYSMGPfqLo+Rd7zd4nar5F1tCcFnN7Zo8Nze6IvcRoFhnJ3I7jg22j0xHnJhnf2ty024uz77JOvkPSrAjROM7rAVk0Nf49XiDFlD/R8QhEpHWXNNaeQwxPJNtikyOsZiXzu3N0Av6EhqWfazWhPPuBqieiMzrStn9dx2KId2t/VNdlSjzFN4PqBF8WRdTZVHqoSccLtzY73M+Ct1zA/a21eqxJczXhvpDTXEmFmq5dxEhzNd73Bmq5H2NmF/pptdzQ8NBMvqPlQoYN733LyUdmlSdsgySw84cAlxvgL+G12AB/hKBQQAC+wxrU8F2keA4dwWPgEJ45+t9MzWxIXsqcN5Nk0Pr/QU5CkDegDk4SG1rg+Pdfkf2obEYT8fJr8H7ykIWDI9LoenWQf+q2ougg11WSfscUX8j5nj6cjnvWpDyDfF36h4OZZ/XFdR9rn0Rx0StcFl09JdX+syzEzSQD5A5yAErfSR4qyNGPRRlDKB2uAMwrktIpil+fT8zzONZ742KIuQ5U6xVeLV7rQHDTDTipcJgOS0GzFNYUuA5WGPbq0tKhnzs8DNfKBQmYh2dlpY5/kv9KUjOXKZMvPckDRd4G9uWhB743v0QJsH202WaF+XH0nz1b8zsS8i8HoMMgDCHJZAVYPI9qj2cS+j5GsXJhyV1con+2yCsbeW9W5tActgx1W1G2jGiqU6y9J44DRSftzsqq46bljec9/yJ6dRvlkjusA/pn0HZTKybDmM9jsUlub/tnjnM7B8y7D0mTwsi2OCFvuxhw12UnzqUrmgU1kJkyNBqMuAxBFeZbCAIwdYJggfbNktoDTvAOFuZQKGI6bIs64RFhW7xIqv5I7XgM6hxaPCqyUxzjiUG0S684fpH7ibSIlySqMA8Rv8DZjnFxNJbnIIon5lYYtHgpdcPCs2YtJRhg3FLDRo0GIjUE7SVKIHO/vc+AOpciqRXAs0gFY+5yFD8jKdKq/yYooTrsguq2DC+LbHOJuV4SoscGf9ru5o9FRl4A/bPNqnCfWHptQ+B7ho+F97y+Th5PHxFDDV9UGNEJZoO7snqKthGTLLlVmcfQNeKlkz6vqauzu+ur5OcclshQvYmIQoi+DYcvOmHYG0AkTuLato50HWpdMeiGFG4lokyn+A6CXd2WRLFg59K97BRUJT/a/APzDWt6ia6cdy4kCzR0XmNMeNmjNMIzjQCBai+5lpdZosrpLOw87yXStiJ83UddDI7rokB4kFrqtkSS/cyRnMSxKgyXIA/dyJVZU6ZdHcC4aVkx6jqLt8AOK0vDxE9pjrqtOXA1EEQXqMrK4ABM1HmT4gv0ltdnft/T2SJSlOPzImuyJF+zJGO7aCXFbvgaatHFARrlFQ/takFT7v9Li2U5TKmjPPcSy5Tp8af35Z2HRMa17ohLK4PlII3VbTFkWtMlTrwsFOsQTAKZwaXMwNxI8NPq1YBJckkHG/U2gm0IuoiAyrW9jXOhLZExhjgh8AeRom6ry9+Ae/RQVrpUEm/mMdQYzEO/ztPqaUGAHXUsaz4O2woPW6De9F7evUc/UB6eZrysGrM/srVnlmPzZxh8sWh2uzEue/BC81QoTO4wd+hzpfPbePOXSA5yt6iqULVIY1FdLoh00L6LnMmQ/q5pdsY0Q29ikOtzbQxYabsHRVGSVMqRVimKqAwN2Vc9tpKxbj39POwp6rZ68Rh8Tx3HcuNpRlKfZjTpO0b2uAFSd0iF8hlAggg8AOSXKC294lVfEasGRvdqRHLgeHVbHelDGb7Dsp8b3kHKEkYMHcc5Xvp5xBXH9k1xAO959IYHZY/gEARwCAfBglYhVo4CVmH5aqx/WIA/8wK8ytu75VuN5sGVFHctPna4zYB94gXCGFka4pZKjKpl8UrEdFhUcy8qPKI/qrLdLc/cuOXlG+Wyzyxnuvd4R2O9+q7v0RZ9SaqMoPJYerR+/YpDc1h36rYooSJw7iKehGGr4dc4LzXn5P4+qoS77kZ9mbv/Hbh9dj50fXOlNVnOpePVuddVCYCJHM8NRt44K/isxCck3HH8/6M8J6Zld/980Qha1tpn6ZGClrwv78qLLCWLaj1PJt412/y43DBa1XzPKsuiwUtwCIX3ETUPZfV9doa5qDIs656oVDhpqwoVabAO2eM8fUzv8UEDkYwO3qg1b/eUjcDv+cgIb7S1mId9JmD5hZ+xhvvjRHlmzCMTwBVD4qD0Y+FBw94rjt3yyO9boZQ4cL0akBw2aXVbhk36LzPZJLuJMeSv+8ssAVnsk8b8p+928r4kCMLJamPZxdv3Fk86bWHe9kIf84lCiyyw+n6vr3TmvIkOe/wvjpHKttNig2d2AQ3rsmyLzRiTqo6k21KsH9ttv+wCndKnPlI/95h9nLC+RUW5zYoEc/xsT9aEJi9bRnTQt2C9tKRwJIQgBYh4Bv6Q0IgDgUfhHsths1W39RNcZsxpj0nxZoB5+zqpv/s/DiS1MUfKuA6MqZlWhlzhl8SXbVEw+oivKP6QpPdZgfbF6JhXYu3eZ1lBNR2vsH9XbZoitPGsfVpVpc4dLlpStzvi1nKB8OlQyrdnV3e+mLfquOAM28PnZVmM3PCVmFOzAVY+O5sqhEW947oZQYwe5Ke6LeMpehYZdZRnie4wEMs9tyxOH3dkieojqEY6e3RPWpSt/OeKXjqA9syyYoLQUSlCVs9U4K5x7T4VnBj37dxH9NjgvdljFzmv32UbvBaCT1dtgbeIfmOP50cGao5zi3MpEYqt/PcS539rUYs2p1g250dNgwno+bS/32XrVyDCg4xXt8UQLPhNDe4JngRiJuSiUhMeFWZEAnUVSMkUkGA+mX2WAc6lBosWk2z4OCsSsmRt9NI1PpP7gEWoYduaq2W0yZKeR0wToBSfZPZAeQnKiJsOfJKUaihJ5dWAut55sSN36DxfzTQIFtpyMFwV10ExKB3GxNUyDYn5ajkitkbQkYTrZ5Sd67BfaSS/oIr6bVWEnZY/2VyXy7d5iXb5U5SGDeepk9mbOE7T2du4ar/9A6W6WBaRdAviVDO/S03Mm78rvECvqyw46hJG42fzowI8TUkSrWBVFRWbD0nRJnn+FO/ANu0u8HO7yPuceFSz3xltB8SS3DSiGx4YHAgHo9unecCg7Znvlv/+zOI5bNCGZao7MvxlFgeY3l3CdFKybNzsELr8CC/KSryCcPXdqPFMxiGQ83m9rg0RZmZqubOAXtW5s5iXifcW3SZ4VeNdla4FxtcjcgiZk2S7S7K7IkReDTgOskrdlkFazPX2yahjztRwJJ1zn+5nQd561uuvX0TXaIv3FK/nm+MyFFAdVqP3apzJ1vgzH8o/YEiag2P++9T3Sd10zVUohkw1mgI6fbyLO6IdX5zYVj+XYSDWO/qYZ/LIjthBR3zlwf6Nx53QVPvXoNp/Mta23uA+oof6PSLyGfN1nVbZrruG89/nYIyH7U7dFkyxP9ps7EP322dJLn/OiiNP4hosY+Tq/FpW3zEJZw4YclZW7TZwKVIc9SsJ1WENalgk5sqLFU+bzmB4loRdlq408r828KLEv2AYRjWUZFbWgAYZli+q7Aemxejg67tgeTyH1apZrTEEOuGhWCv1uoyFyXy8+PM8j1ZJUs3lbUIkv2YSIQdmH3Pz+Omobe7DXUkZhJcozXYZ4/W/H5VG7d3EsDEoR3mxcsPDT4JUAyZJUh2sq0/QtHRses9CK/s+AZl6zkAGbQEfUFJjQdylAQx61M9hOuwB6rYMRsKZ8hTsOU3CJSGd5Utd7+f9zi/NXZfJWyzxitrvcCOtlBHZYbEcFsvPtFjOt7uyInnObzOv6IVc/cPiWNviOCvzTbRMB84PdnPq0B/nqX4cTFHep3U8T9VItOGeYPheHnzPdoFHw+Q7CsPQRVr5VIRfX+BVd5ahfEP+WiDMysBmJ2Vxm921Ff8aaK4rrdNHLL3YuZ8xUF3ebosxvMjMrV2iGgvo8+JWd10cp6n+XR9G7hUxwf9BOrdnKd8uqqGk854GNOz5+VOR+seVIdw5RMZ7xaA67NKaZR0luEzHH/oA22/mMbBZxbWZacOnITUfmz0dAyjN3yW17qXmn+OFjDt3jNDR1Rr6NPeeQRsjG7i0mZsOU0XqZz2095B/JMLyLdrlpW9qdRHFQaKp2+p3pVCRtp9FHSse3XJKzcSUMQ4sFi70kcISmz3m4zRkDModoxGSR/C6Sop6m9F4KzGmAsI5joTGHKBCCQbzODl3Zi1D5IE4c3LVfuvsBLO3ZO1FGYkRaHsWl50RBxfHO5Gsx+wH+sDE9g14SeLzHkUTJ7u3FQInK3FjvhmBp3OVCkY6VSkBwx7oPYYZPLn6B8VD3daKDZ4zhTskYpv86kk1v9ymW0SksRq1yB8ZJqxtkzOlY+s3xWHxBnqTx1EuO4EQ0xocZheTmRIz4xKHTvwn2WeXsL4O7NjZfBcxwy54hvh0e1ujwMe39HlDGIrjpEnvr7J/BV4sXGCp0SWJClxnEdWrk3K7o55XLgpptAgZf892R1V6H8OBndSakgeGK3eTtgXHlAhS8MQIEkZNMJrFn1PalBZ/NZRCN41v8T9O0u/nBV4v6fdA//wTLBTz8u6VAuNBc1W3FcXxF/+5adNwSRXJ43cfvvUK1gM97E2w0go0VnDP/0QnzGkkYx3zQHpQ63EM8GEeo3h3uE1okrcq4Hn5IEsgdAdBom5rP8fQLxl6iGQ3XJu3GmZEdFdWTxF4WUR14OMDHy/Gx71wj8DGAqYDFx+4eDEu7m1eoxLkzcQ8ogMPq9saTxVvIp1Ofg3DM/+OX5V1jXXwPJzLRFQHPlsrnzk89SnaS5RSxwV3vtiSeFkDggM3qNuKetHkeelDZirUXDrpWzPbeb9mG3xo/3tZGJqKkuona7RpciNdAK4v0WzsCE6dm8h1eZRG9JagqV9BmxJhaFLaiZ+bjr8nOxJQLBnQIZigBDk8wgCZOiE5yFV1W5RSwZ5CFW6M0DuCxZpeqkZy3DAMnQTvm/0mdRnZeHVPr7GY25q5hTGqevdC8rfu2jLSdfv6hP8xusuKP6rS4/JTnr2H08ddUmzCx/exPCvzvHwIxoMevmbFJhzReWrKNhiD/5sn7Sp7E6MRMpKTMtfmYY0znHfNlklONlszJ3V9kie14YVkjFipz0FP6tSfOGqSeMWmU6Xs1KR2yxlv0Y8MPfSyrHPwPq/P8uSuHoftrDgd5+UdCc4loo6mPmGewUI3fyJuZYzc5afnA9p+Q9Xoi0Ccdr4keYv/+EWaSg62j1U/gr8xwPcDHeF/tcN/1fmXHd+NFf9k15Bc8c+GinKNv/wis0vHGFpmoQaQL1mdfctyqhMG84pg+59w75FZzmg1O2a5QkmV3ncOdbUDy9DRXtDod5ZsMyRcVTCL1Qz+rU2oVCCvG6syj7TehzkEsO9xFq92WVEQ1cxy1WMZjpWUicBvPOh72eboKi29qUrq169GLHuk3klSNbakoyow2ZRUtJOQj04zA+9bLZeJ9z2m5n15l6VJTmj7aUfOuET7CpgkAN8ep+uo2NjPVhCLnyUpaq7wIZEqeP6yoxOcryi6+hWLdY9kfJc1NTn82tKSQh0x8AbGf598QzkLb2B8/owZwP2UvoPJ+F1GXipHnToW9T6l1j1Kv38rH1FtO4OdKLKdv48tllpZeknCUKjm0Go+rjNUXWBM6CTJ07Y7KHxAzX25ibgfqxvZp3JFjoC2s3OB8OGnaJJJHfuLvsLR5h+YWt1TfIUuZjU/X5M8R81FWROBdImSmpgewiem3/fqVwD+fe4em21WWM8JPpDeJzWyXTOX6DZj9GmDxMPQLbOX/cnQF0ywDG/CfKU/m85f37JGxVBW3HHSVhUq0qfLEjdLJrDNY+jRU6RGqIE98gftxodssyuxeH/LKskGXuEqft7ZssxR/pA81bQy15qBd5hqTFs+2yV5/DClSw6eWIKufsUi3eNkfiyrbWJ9vn2X3d1bazflZAT5bz66pZCA9apLmv6+TPs4aMETIaQkVbW0T0NVXn6znRvyRheLPkREhbU47vz3AlQXXWyIcBHIxvlSt7RPs0JGnttd0DymdtP0AXck2+HunuGVRwbIVbZaJ0d1XaYZJeG41kgY1s7OfolqaiW+GVKtCP0/LTYvOuuxttZka34aA4FAFV6+6EaEiYkl2e8v/y9p+LYNjj4ATIPDEIRG3vBjwo18KrqI8y+OqHGTmCzqNNnIdnpM0Q3/pV80RHU4KYsaswfenuQr+KxI8bzlLkMRkFje5JNOjs2JJW/RDhXkEt5lDm36wSbmkfszNisQ00S7314zzGrBw9m/qLMs7ZslA4NVlNzLQjuzLtzU8+Nb7TiWYlrtvD0Ljh0OcGRQN5PnAvmzD0hZK1nXoi7Ew2y1WZnLrafAEgAhzSvOi6kdiLkAdzsQzKY3LLq9sTo4DHZwnVudiofsqkMM78lGzk3rObh3GlybMHcb2wKs7zbPNh0S6+5vBZR5btZIOCiQn0tyznBgXw7hs1I1wK4vwYTQHDwLhYL0/KioH1B1Q/lExxQMnIrPOhBXbmMRQ1IRYOB18BrQ8YW4DZgLK9mG4ffGa1dN0iDqGF3gE+MJiT1UPSk5DoSG+I4DdGE9uAWAAYeuro4HtSNYgBO1c2SlaXZV9saSfbQqIzMKcBAb9iAuDChitWe9X169ks2sXiyk6MMCzKOg6XNiG170mKaZXy1xWYjHDTCSVkrGZyewPwsyFUhrm/a5inu389xM4fRMxpIJVGfFceEtADPAWHZM6zP24ywngTuHBozd5OGjU0FAb08KeXV5UIPmAymaKd6Xqb9iBR09elgfskjNQJuYguz7V6BMo1jQdqeaLxeD3d7kFXlxQW4VzeYKCRLiywHIhSFlxM/KdKHs/gIsqJyTZ2HCIL0/Kbc06OnIODouEYFVHNjDuTKhhB7gQxWDr4MPVSNYiBVV82PT/FBnfwa1jIZ0vPmANlnSu/WorWoAMGha6+Cc7GoQaoATuX7OdizQ9WYJi5mGzjbNs/X2x1n9m61+LANPKBkABAe5i4N0YjK4DciGCyPfv7zTD2EJ3tTOk5VRt6uyGsbsH0vZMo0Yy3cOxhTi/8ptrJ8x+SHsgTH5ebJizCls936sKH1YWqOsFAHBs3IP43RIFvHaS8Z4e6+qE0ucbRV0fQ5S7W1W0/QYN0c7PCkk5EM/mkxjjNNVgphqgHdhKm0bkPXFjnEdSMOGLjeuLQgYIgUL50IOEP8+1pmuIwusNR2dn+d6Y0fksuS4evOtOr4Z6Ghlz9FBdOq3WRcSDVXmo87Ygr3OFYEmw49L9M82q1CXe8fYaaiWC2VmcoPV9w+gKwA3k/urFekWEHpWJLLpx1B/f4eohKZt6p5H39AIHEjDuzA4eIhiIZ3OUHATAN/1Zad9nMZYq5qg7XAOrSvJIYNCpJigXOgA4Halwf5Pk+pBLLBK1bNj03hfZX9ua/cZdSq2XZoKeNB1jQN18l1TNLLc6hxFr5EgEmR0rWMPwx/MIb0r0qfbT1V2lxUmq5cIrzF7edi7JOz78Cgz9GU5y5WK1jY9EKru27Z6g3eL7AeqnmiKORMXsMCRGYxDDamgbD9nZzGoNwvyF0RnK2WTqbdvzjpui02OSPC0o6apsm9tg7osxzdTiYnhbHBo+FBZ3YNBrbqiPpIyY16dCucxwuXWggsL2PRqqrWeBdIPxfJ+S1XPaiEEcb7Q3jO89DKNZR98Dc+iPS/v+zJMHpA7Iy/Hwj7MG1O1UHdlL7z3DC9f+/bHK8DxGsrABFIFDbf53Mkqm3G4OluNoFSOYjkuVc6X1TGrr7MaLrWUiSL8zDz6jLdy1Rj2wKDPV4hyd7vctaKBg5QVNQzrexFvbNLxinQ1LGwc0XK8bJxPm66w9VbF2ZbCF6qzED8/Y0GsG8eeGPj5CuSrHUqz26yL1jlaPGwZWF9bw8pwRQ+mNvTgGbK33YiWY3S7OX4OLA+P5BPNznej4EgV+3ngAu9NNWicblE9ugO9rbdalvtfKgHDXWDhBPCGTe9gDCvdSLQM7ifb9eTd25aj7Zb1WoOX/v5XXPjY975j2fCN//rr8Ox7FQ7b8nWiiZ8KwEa+XGcxq1UwXBzdjeVLUmVJ0YzTclJuv2UFBXRyPbDFoyGcBoUHTa07tG9fBteOLicXXOfUpmdr8oDQjc/yQGeBYhUc/4zPdw7DWsfSeIYnPYtRDSk2PxdZ0Kpg8axiaXAdAtYHN/B9bgZQR9fB8dCc2vSMrbc23vfdATzEfgSG/pkE/Hqk+jMW5cI5qyZPL8QiE2vb4NCwOVDdg9OtOqHm+hUb5TwGuNxScJl7h2WxGvObaM/Q8Kwbg2ow2a+V8HWi64bFalEv2bWuG4sB7231WPCEzxqasOx7Nak2TuutxohgP+rUz7HDWI9u/5rWT7G3iIOj+RZvVNzqyJpaZA6rhOKJsFT0/VEvG9OqXe3qsRrw/laSFX84rCoRxb4Xl5Mhytba5HNnsyq70Z6NQ8/bAkQyfedlsrEL3QpCgw/be0Cnh+0g8r1Fb9V2ZwH20tLapv01xW+9uUpIAueRLUwChgePLL0E5NAlqIJ948suuC8LSi+Y0jYd4GvujcOuM1ThUZNUTGzSVhUTwOAQh42QLjymQK/JD0F7C/HZvnVA/VAWYFH9VNl0YBU5XadhmK5VJMgZ2PI5XpYoe78oEz7fC5FL9CNDD7a3ejy0Zu/tAD12YKGF58SK2hEst23Dc/TsWPIdyne3bV6Q1FI8U1lxkLK6kWmZmt78q25dzdDwklkZWxsHtjSfG+fZgfG7intj/7Oyarc0v4sxp5kMCrH1COXCxwBqpwxmUY5A6k4swF9q4rrolftlo+tyl6WWfMTDKhmJgjlzkoB8T6wE92IpXoIJ/HyY6Yb++0dVtjs9JzGASjZy5iAWKcA+TN9Wt3Wq+r8U4wHzYdP0VGsNQqzjGgsh0w15DvHVYVYx30r5Duj6sgKPmw9rvluB+sXwi1lLYgYcXwVjkKu4D+TrlbCgYgyL6nDy/Ng0TyvsnxW/lA26mXjIyDA8vJYhCagXUwptqPhyndk8DaNYkjPhubKWk3tN6vkRPdQ0Dp8xvbEECTHlAOTCjzLiZ5XeWNn9BVhQOSc2be89vTHp/ZARd2QcHZeIwCoO9EhvDKIH+FDF4OvgQ9UIFmJF1fzYND/U2V9KxeFUz94nqt/Mw+BgekWFWUSbXhHG7njD7P94/vSxQVWR5Edtc0/I28U2uERpWW3Mxiir2hCpdBVdyGfXAScT1r6Xt9OQFljvTnP8LCxkn6oNCeqe5XlW3B1tNhW5N1JxGAQMcTSFc2FdEDHAqUMH57G16nqxAHPpqGvTPF9zzxxlFJc8WEQuep6SDu77Ylz3LGXXkC/qc40V+ncZ7k31NGYOVHv66mrpUlmxFXzSWsENavI+ro9LrYayANNazaFNP/aeJxIcSSf5nPipW8ZLcW/XGsC6oMxeKd9yg9gX03LzZtMJWmG/mzs95ep5VIBTbu+uZjMR7/NhQUXPl9rh5bl4Tsxm8u+UIGdguOfo06ns/aJs9wx9Of/IbpuTBB/qL3CX75Mabb5mzf3EQSp2MdSD2HKo4sKVpmZUYhHi/ngvfyx7tQDvWU6DFSeCGPbOmJwSMbKQiV/AWjqm9NUa9Q0C7KlaBfuXoVZDWZCntXNo04+hzrp4+DO7xNwYmau6GDfzrT4fRdR+MPtianA+bTrDVdyv2vqRuEdYnJEmOKXK+tHR1UXE+3xYU9HzpZRVeS7Wf0a6RA94+VyUGEE9rB+j7V1XCWJDAN6FIbXNPSsrvc1IFuBWm/l7FhZ8aCB2ioCxprXYA88/Lg35LRi5WZccPvfZjjqd6InEg4GZd3oIpyw7PNbns73AHV9gvcLzsP7NZeh3d2YeWMXEFxy0julcbXFwA1BSGQVXr4cFwSEsyIngHNm0PyLYr3sB6cbO2mNFgI7obCBitvdZiWd20/ZkKd1ZRWNbntqtwHPla5JjWWCtRcPgEHNxkC5MpmjiWenM+jEswJ/6eXoWejI/BL3SB8DOyJMRdO0IXLW0Qqeh8fq1ukvUtFVxif7ZIptgDTA4fOphIN0MBGATz0rM6cewiFFAN0/PQsxNnbZU71QVor9IiKnnOR3AStrsSVJ1KvtxW2xypL2C1tSBD2M8uNuBTN2U2k2CGcNs24NFzxY5bRmnwqYXU609WgKEkRi3DWWN2Znwee4fxmHsg1+f5S4ijcLkPaaqMDunPkeXMtMg9sGmz9DB7KLM8y9lg0fRvx4jH46K+kEjUjV1wLCAArhTOEBNUxC3Tp1fHcNaDGUBnrWYOyu2HWvtT0m/R+n3shVzU0if1Uq7JQJQiQfrOqn0tq1D2oM0xtVxu+vwFmB91/m2UjLEynu0pqRthUlxd5E80cuU8yIjeE3X15pasG2Fr+BmXtE1Zn9/G+VkZtWZRcwlFjNg1Q+m3mq4cDBNSmxjyyMqBDa86eUiZNk8wK2mpbF/oew6uj2wv2m+bbok1t3baiDT+gPP/fvy7ob5TXhGuQA0dSCeZ0Bc+FzXCmRTFDq/Os62GM8CzGwxdza9EKqugn2NdjYIeCaGfZ6GNd0IFubNZ2lOs+JCE/c5cp0vt60iu9CeGO3ZMtjR7W2W4y/oxnTdJ0GCgm4AchJzEubFow8pu7CEkFIR1mrb3LPb1lGaCxG4aFg49Y4GgcNbZu5uOVWgdwxit38pph/HIhunbp5cJBupt7/bKHysIhm2sm1SPZ0+pvdJcYcu8VI7wWclVKRP6mspU03wfopUcrqUMrYCsm7f93lEoXWfFmBD61mwMt+r0ayDQekfbpzJVYnPkjz6PfMi2JmlmRAkuAP3cfX3x3bpPdq0ObpO6u+D8Yn9pmY+Q0WQBeU6TgxpahJ6g8KOZXW7u+2IluBty/m06Qpbb2+c/bcWtWhzuk2y/KhpkvSe2lHPMo1Oqq4CcTMI7cLPmuZcU0bvm5HNQ1mAhc3TZ9MJAr8ypuXy0btxE5TbfQEmhlLas81yI5rN/dS+b3vjTmh+bDrD1lsDt950A0v1QV9VFQyc6cmPfBMAF3J9Xp1yYBrJsjwLzpdNF9h6a+BUZvGxLOYm31jCLCdV2VYBdtasmFVxs35EexPFwJza9IWptt9UX1fttzqtsh35bpm0FayiTPjFQjsn/YKb2lMmV21nFmBAM/FdDKt7TLWe/Uga9AHVJG7YzVlVbo18p6kDJ1Vnwd1SqasbWp7tLHqzAN9ZEN/qiMTUWwvzXZeurDfVmJXxmGb2znZyX5ZnOpnsNn2Yau2N4c63u7JqcNduqUXBxkiqrgKxHAftwnGaZpxNolHYztyhBfjOTHwHW2ZW3O3Vmnn66Mx86ipw+jNP5tM0sx/mM3doAeYzE//ZMR9uKC87b+SBTfQ8IVdQM94E6857QDuQsUfH4Ps/H5uGshjPqmfNpgt9lb2x6nGSfj8vrpoy/e52HjZVhFhXUceFg43NPisnX9vRLMDMtvP5LA7aqsGYntQb6i3M08/xnb3lWPbI0M/w1f0HVLQkYkDvWEj+VLIwAAuxLQ/mwq1QA+CNZdHCyPfPpJohLMCYmhmyaZ1WX4YTT3Gd5gnXaXANVA1K9japmk/f/oHShhShRzzxKZX4SVGUDcXy1881Oskrwg/17y+bqpV1X4L6CjWjvWKXpfXLF913ho9oAcCOQvXk8SRp0F1ZZQjEMpY/GXHhXyTgAYSmLzKieF+mSZ79C2366YM7JUKZu/Y+Ke7a5A7G1pfZdQ5dNRWN6lBTzlN2T4AzIr9A1Tar62xILw0hFmFckOIV9CGhYVQMmCdAI3o+ibyMlfd4N/W1zHOwa/i7VeUuToYKxRCuxHJIuuEYkfQvJECajI9KTB0h19iKRdmVWSxILFMQXiM/sIAFEXEA1rShwqtodCTqQYwoj/Py7qKsQVxDmXnyOy0DnPlB3TOgGHJ1Qjim9Lwm+ugEqbUUvcjSpq1AHH2REQXrswPh4Z2i7Kir6xYHYe5dUrS3CYUFlxlbbj1xJA5mVqGtgi8BMCPqyxYLcdTQz/CcshAWE9u9kv+AmvsSFu8sgFX3DH2z7NgQ7FLdMx7CPCkoz36g6uk624J9Y8tt+W8KiqhhQTbUpCvaMbTMWZY3in3EUMe2Ua2g4GEs2KqDN60qAMwW9dUOpdltllIFdRyyphG4gpkPwWqf6OkT5EoNvGdj9s3YEu86gRWusdQW0ZekypJiioB0Um6/ZUWiIo65lrHhv7UJ/fK5yEChypb7jsKh67ZN2OD2R9qzIwawQT9B+zZk3YjvDNDoXA7T0EdgM20B/SMCUPyPDwxMh9MMVRdVBuuuY6ERDXWCUmmZY6EdGuVxeyq1QZSoMNj14o+qbHfKXtBSI6KP6KFWbadDmRHJ6SPe9ookP2qbe2Io6YSk+gSrgzc2RkNuQVj7+GWWqiKTexLegQFA8zYMZLaEscPJSS2x6xDa0U815Uy+aBs0JHeiEk2XwdKkv0o50xSKLJRbzUKjVR0FpmRRlkhUFOPTWhmQsRkk4HEKCTqM1GOi/8Nk45IzGMfKh9iFxyuGQjadh8WgjeC5WI6x6YpWuYOpoocaaQvFSlNxpyIWnmMbNrjNdqcp9hBoemJDQtmjorZoPbouWJnRfAr2yqo3Q7wSsB9TABjjoHL11sRE/zCg+Vxp0IyFZpmOCoT1F+3qEGHMKtM92iKqsn2DbbIcgIUFsYRtPv0jf6PFkL46V1jExof8Np3QGK/ZcjMy6fkx3DvwCbkDbhNOi9MX8HYEPoaBb4Ds0RuQmpVA5uEVqPRxr9+MRtTtLsnuwIPPUGZhAKUuwNdou8sVUkMAsVKX36MGq6/spbhKeZYh7RR7E27geYzx1Mc6RcPnPN4B3UjbpG4r9BVld/fgdHMAtujeZphra8WoRRgjUs4jF8IoeECb1vVTkerkz1RscWDiXd/gQ5LoqGiFVDNcwSXRdCkDO4CAdzQqJx6HOwDtKgXgbO9hnnSIRRhrc54GpwBioQMSsI3m+oqHMA+8KusaV8w1KEUYiwU6+KPAS3PyVrFANLlvqJCxvjUCQsbZQn8pfzNd6TN1NLfzUwXRN4SNUampN3oKjYNWugtI7ie2TQy+QmwTk1uD6PDDE8uWkKxThZmKMLRhfGAlJf1EXxAT9WDsM5OOC1in8rmQaWhTTT1ci9oQVQWfDQ09bRoACKsafzCVQcQ3sk+MTGjLmmpS2CGAyG1HDZ/G9KQfZGw41cs810oCHkAzLBYOJFXnAKSjDIdi5kXNJOzpXJDgobMg+p4zkKrhjy5MBiKwqCA+gCjpQQLOPehm9DqSCQEDqscAwkNEET2YNHSBcQLUYTyrgik0hM/V0EYEUY9AgITowXiWaUghIlqICII3mZoUPKB5HPzUBpOFRwcQR891AQrCDdNRtT7AQJl35wlYt9vb7fQMLoAqSiKHEOQ4y3Mm8a+OKgKoxXD4GhHoIyBciEi9M+X0tExDJQnWPCqxio5Ok+unBbkkxJAcUk2AB70Gh1GtuiIDqQciwUKkYVxYNTSRUc2svpAGT8otfV45edLC9JDg9OMQwYMZBkQK0EdJah/1rvNW5UMeQjoeBKfRzgBwUNsb3Wl1qh6EDKCL4O8bThvOofdmdOwFqANDaoYEVgApJPod6wgFY4VUYwW6CFQa7GlmKkGPKbXjEV5RRqKS8EpSxhplE+sNrToukmA0e4sACsoexr1Wt02JqOZll8Gr6OZot8sztLku2X7KRNHCq0elqwYRi3n5oKGVFiu0rSunwINy7CWAjo1AOPWYIHCIQoJntYZKIMaluUrorg1j8VVcuICrGZO9eMTQnqeblSiEnISgDQ1HaJdRDpViUm7EOa9cHxuGX+BoSAZWsBghVC8C4UC0AO3gcYZrEeyjn5v+RRFIQAWkZr8HK4BahPA0SadEwEgBgknvp4JpNb1pGhqG6ARAqYcjA0P04d9aaagDoFuEMvwDLS0bqUA1llW4BmivFZ+S6Qy2CrSLEGxc/jpSyUAW8kRHHkfRtCxJhvNAbzX+dPupyu6yQnOgkUCNZw+xhuZIY3WWkfDNbOoemuVfMKoJxMGZR8OCB5OGQwZtaPwrzVi0Ub6OvGFfZypJZlXdOHgbLBoC616Fmilv1bhaO+Neusaflb4TZnOFsooDAfiaVhR3JLHQwrxKr9y6Exl9CDgn6RYl2mhumV42K2kmwxqHJVXRUMzSQKTEPLfBQ2zYzGQSqP2ozAzmSa5FuYszvfCP3JVEU9cxDlJZVUNGezuSsZEF7CFQH8x8CIK7DdTMj2GEXJQt4dgBFoQ0VDQOWV9fQ1xlXAQzmQ1tzktwXTCIG1UIB+B07IFGc8R1xwaeqvWBMXRnbI8OQA4xdkwRfZ3oI3y4Lh4tNl/u1lN4wWWm7Yj1nCqYKuCoPLyKSGAnaAjMfLZloIPPySwutYzq+h+LKpqwLLa2BWsURgrYYtJQWh+cxjwJ1l1Yzo6h65J5/7apHUQV804+55wsqkTpOsLHIPKaDg5FEFVYTHuaGK4LwOwIMZ3mmiKPFeK/LDzWghedl+V6IIbUjVikIa9VdSMJbLBoyA5HzTJT3qpZ9SzMoaGKGpW6p/aTokPiSiMNLocJ8pgcXcMWU6ThjPjiyGbhmOt6Cw6bJRNVUu1rsYAx424UhQ5zocfjTCYtOoc5GgIQuU+UvgfqSTNyScCBzVKlctSbbJUjuzPb0mrOELLQ6C4OA2ru+iF40HVgCqiocx0A0S3nMn5zlWx3OZoiPKrZR4A0zzlfIZiFBHTQwVJFcg/6jNEqb064oPgyfRSQ6gHBFSD6sPE0NRRSINS83gEfkwdRSXN+kIFshqI5IziTZdFzwCX6kaEHiwOVAGhcATx88CMeGOuCJHqH8t1tmxfkZR5XYKSZuqblcJUI4lJV3Yxmbaqa8SD3GCtX+xQRgFKPTwaGSMZG8NUQC0A285PDKeqvmSQCmGEYPLSSKEM8YhNVBHRLkOWGDUasoAkLYxgBA6qgRmImA4sEoAEXXDkmc/SxnbWc0cFYzWM3ljg80eGCiSFRNEhosCGydWKDgbNZ68xYYogOBp2KQ2ACh9DmS4mPe0wocg15BFCLIfE1grckFV4VtWK92B2Ckmufd8tA6oFIsBBpmDDpGprIqGZ+3k0aHJ5MT9HaYXpIcPpxiODBDAMiBeijJHVIgAnhiCJTSAGpHg5cIUJ0CR7fAsctXTR+rT5jV1E9ZKv6EEUN+QY0VLZrcma9iEbHt4h5AsKpxwaBQ9QbEihoyASimjnOSdemjt8ECFP3dRxkTYOleALKNjE+StS9SoIraGyDunq610piSguLp0twG5pXlbNRss8NYknGDtpxfB2/zEjArgGAejAz+y5Bui8r6SWCGNbPBKlchWa1SsS0DAk0pjYZyKbzGhObMyGWMawNOWVuLnCP75Mabb5mzT2TI0YmjamKenCGmhDZmHw4GqqZEKvYKZYSCqX6uZnS9ahpCFcwDxSsp6Ofg2TStwGQUjlHsSj5mZ1Qa3LytRzHy1Wek7B8Q7PLPZKxySD6GRCDnJoglRKvTx9lkngMpjlJAKSY0qqjWnj1kHTVwKgDcIYsXfgBXQsz67FQ28YVaq7kNljPZeOI13euPAM7UMOJkoQChHpcPKAqegMY10SHZ86FyaZYu5kSt6mJwAOax8DB60hiVtFglNDDCxWVvY/OQyQNo0VBBDSdgQX4oPO0iGtmowKXTk8rzRWQ6vHAFSDiSFn/NFRSYJ1ZavOtKoUMBGY7FKW48afOEioBkz7RoAyAkLr9BaoAbyp8kkftpgUinZl7ps6bJZAS1sIybiGHHG3tC0sjMXemIRaKDly3AylrwVublPNTu72pkS8Vy0Tqg25dqoEdBqlbnYH0W2qNSg1rzFtKWIdxaYxdgRRbyKeszPMvZUNTMNHLoSnhhioHiApcPTZNrXCXMQ1uRbIQRd4Rnx0BTLl7A2T5BXYI27oa+W6JAiSyKq+wbj+xbQ9a5EB65Ah6C58z+Oa8yJosyTUncF0FncKhqQcrM1LGY60+o0M/r+YHZnS+kbMxm4mprGs/cBUKGxLbGo0sW4SiEJom1SedzJRX+kbKMS3TXAeuHrSmFphghsusrSGmDi+kY0pptqOST6cfgXB2A9NpRV6UWkoXMtHEkhYmGhjHvuSYx/ToN5rDmQykmTgRFmQDJmO7jgkkXDMfxMYs78Z3OApIHUNDFeA1ktu4LCkQLuAYRh6GkWcu2Tapnk4f0/ukuEOXeJqmnO3A4cFYSaPrm+rCyeFKU8oLM16QmlPW+rikpH9Y05CHthwkVykG1XiEs5MrvUebNkfXSf19UETYbyDRTHU0IzVUBQko1TFpWMZWoFsCpjwGYf/WohZtTrdJluMDR5LeU5XuLIOFnwZaPUx1JYiMILSejpoGZn6+CbdsegxsUct1sKZnwtGouvQDYqYXN11fUqUHpBLWapBcFQMNrSnHIwXoxZbPx5HMV3uGZCu5sglLgjnZkW0HfO+vnjLfdzlX7bc6rbIuyInxdR8MrR6mupLyFRMLbfFEB0Y+82kDKw0/sHr1AevrRHCcVeVWRzoduMbCqa4FhwVgwU3vbdWolyXddelAOAbYemxTnchEYxDPTLLz7a6smouqvKVKhkFP1ECrx6auBFGNg9YTTYN4AXXw9NGFchpo9QDVleCXPdaU0yBejHJv0S4vO7No3xE13QBY0+DkKmqaTbA2ZAMwQ9qKdjZ80tEm6ffzAuuL6XfrfdVYRz1WU1UwkS9cR09UY0Nz5zxWtK+55DVVcR+s5so3KlGXuQD+gIqW3Df3tjfyJ5htEgBTDwmABnNNcmB6KkEowYNa0SrQaYjz2+sOBblxTrICVWPZb6+JRN0m/YffXmOQFO2aNsk/lBuU10PBh4Re39dTzf7Li6tdkpLL0/929fLF4zYv6t9f3jfN7q+vX9cUdf1qm6VVWZe3zau03L5ONuXrX3/55b+/fvPm9bbD8TrlTNS/Cb0dW+oOqEIpyQm1QWdZVTdvkyb5ltR4Fk42WwnsaptUzadv/0BpQ6/eH4XV8dtI56HBPo5RFyBAnjsCTS56BnDyuzd8kaaore0V6dMrMP7BRMMzPCwixOkIETPdinq45lWa5EmFF8oOVc3ToEFt8MjLvN0W098iz6lrXz3VmAHJbx4L+90e23nd1etDRHDd4ovscb5rtrk4xuGbPZbjcvN0UtcneUKuT1hcfInDWIs0bzcIS7kMDyvZCcOVSl2oeIH78oDXOy5oMOcicYohAHv8Q2Ue6fTVHtN11uQC4/SfHDjwHuspACL2u8usNFUpTgX95MYrMo+4YPiAmuR/oqeHTmSzmPgSN4xv0biRyki5Qje8APGZz/a43mdbzOyb63K4oGAxSoX2eC9RsUHVUf0129CtkUUrltlj7Wr8vSyEobPfXbF9rZJd7+EFIeWKXXHj9fAAzJRU6Ir3uCR+N6KIEcscpEuVlRXeywTpMn51lC7XyR0gYOhXBxnTUg3gujxKc0HKcCVOMrr9lmf1PSCbpwIZ32+vhe1f1DBeSyqGoPCJCoudOpM8anPPO2g1IybpQtNGt9HVnkfDkXUbV63mbVbv8uSpd8BjMfElq5lt/IE4j4ZNdI/EY5KVNdc6wdTlk0fRf3IQMYQI4kDGj6thjfcl7nz2L7Tpux8qDkR8PkLBAsc8nNP1QcQxfXVQffrAYCIu9rsDNkIQhNXEPvQih1Eo88CqQOiOC1g3XMF6uH6M2xbE64qAdDYsrqy6Vpk49PikzUnWRZitx0J7vJ+L7J8tukIlMeDwWIUie5xneXJ3vsX9IY4k8tCBYofDRyNojPTD/g9FCvVTq3z+NApOJ2Wumoo+kampFTbCPiZg9N3KjGjmWfNx96Ch9/Jy4kvcMQK7hlDkcgwj9/sXeXuXFeI5jC1xwXhdtilwrBs/r2YVXKBqm9V1NoVPDFkBIjYP7jejWJMRe/+zVuYD4mhTN6EMmz8dnnkm8SjPywceQf/JYVMUGFDsEVRuj5310Rcxi2WrYTdTTFx7LtM9orBgLn31tarBZxVCQ6QBQavkShysmsnj6SPa7gQLMfPZCVevoXVv6wSEXJnDSiTvugRswzf3u8DudQ50FdiV/KxivszzQKmOMfgIcbDac1A5w1drxwn9/RrEJGPRfg5a5JrmU/EOi8EL+uBAuO/kyhx3zj9ohJnrkoT0B3ZRvniJo6HaUIrZHDM4+twILgJ8iYsZbwPiY78vd2Dfo7wZgkCESh04+IWl7FFVnkcCkRYlLa//tqTk+dhuv6Hq0+2XLiwmh4ov+onNMlKQk1BGZIOgeLKjHsV8TNktA4g1p5L4E2eg8XlNfn+6/T9Uun0/c/9ngH4/ODgsROuhWdWBzA3b+W58Ys91afrsogAf7XZV+UM2JU3fHcZZIbyXbT4V0jbHlzhY4ncbBUa+ZHEuFZnzOC/v+uwzHnyprT0TT3bNXRNvU36q2AIHhzQ8BJKDSewV+33vs3ShS7toI6z19WeS1F2jkpiePi/rDNmNXmYc9rsDtqSRzBbDNwcH3C5n5X8hfHxoEuE2TCp0xvuxVKMdy9bF3UwWz1BG16Kalee79hWcPxU6uBMmdT8awZWQ+b73eeRS33lMnqH+PDM2Niqi4Qoc/J0kG4HKLLDw7DB55jzmRlt73p1eFv18yf50hyElnzhO9vvqDpBxLioCDjFLn17+aDPF+aUrcdDqa5IkTzRnTF8dzGpdDArOotZ92sdTk6EOFnnbRFbYpFJ3zFdJ3sBYuxIHg+xmmxWDJOJtsVyJk1cCfG3EFTj0cIjaJhKSK1j6yugtypH02Gn86H71NAaggW6fxsJ9PZZ7n+B9G7Y3CEX7tBKQrrwv77ICNLHLpW6YhxCSSuQSgMNsJXnbJPJrKfb7sqc7+l5VZh/msxv1ZFTTV4detXkOdGr86qS77JLiSdJbuo/uu2JnOYf3xaHMwYqSVc094SPBiDJ9tsf1B312xiMavrm8O2i6YVxhfmxrURYDxS78ukXkEZuIlP3u62gg4ZRK3fiYHmO+ZHXWyNzMlrlhJTrP0Z20BQtF7jjf4rNximSHC6h8NZr1FIE1RLNWhJa1UKyVNefRqw+y3w5TDB0/mtQn6lglPcdhPjs5GTQIf/uRFSnwKksodOij9LD1xPFRa78Q3ojnguGrM6ZfQUy/umD6e7YjRqQkl99kCEUOp6p7vMlAGzhX4LB+kkcIG/N5CS15X3YRugZC3+r1K8nHLKKqOY/0lmWbq1yjPk/1cZbn0gWIUOSKE3YGFcsc9paH8j1qGlSd18CTKLnUAfN9hZAON1Du5PCCqiwFMYtlDnJ7eI6PdV9BaHMlz+2dlHInn+l1WL8LdOYwcIMYilYj47hdOfDpOovK5wG7vv5cVwnx1J8IAvTbtwqfYwAVmi95bgtxT8w9eGqE8fWAxdMLBq46Dzf7xkdRH3lieprHONBdVPSOXPn+Aip3wJ6l5GW1dM8xfXbuqQqlVOqwXeMdGYuBY0EbYj47r+nr5K4+L/pOgWtbgHDgyVXEBqOqYn8FVANa5FTkgLN3KOzrnsi3HjCEw45SNuZGlEAuhsY7gSjdl5/nfcEhNlxMRffZHeUvhjC3IYrABRzo10IPUNacRw3om4MuDYQil/v6Co+Mhr+hIYNAB04FjMPdTFZn33J0XmyyH9mmTfJckPsggCP+LJfsiez3JRV+Gm5TKUXkUrf7RSViqXCfXhYDS6It1jNk3wig2Im+Mzz9G+qoD0QwxGG7MvAW1cSGF+tU0wR1NRHCXWXr/LGv2i2srzHFXsqaAj0M4d576p4N0weE8BqDuhElkIfFMVpo0LlUnavvrdBB8sFhfSRFe5vQU16F98cGuk5RwTh4RjRiJJ7ui4vp4I/stjlJRK9B9rtDf/o6kAoiltljvUT/bLMKfWruUTVqdBxuGMK5hUl5gfFz5Q7rt8VyCy/8lKgtR5uNgE1cy0Zol9kdsvqJszt9dzAa9HXEmWW/O/jOFnm3PJnEg5wXLVDusv4eh6fACvwwhDs1Th93WUUNtG+TpxqmjAjj3gp1zaMYoLWlhnLQbpL6KsHKFoJZBih28RBha0ouAlKpU687/6IKIVk3lUvdfLvHivILAaDYZV2OmeDFhckUuMivvtLJU5qj96i4a+5FCQZB+LZwgaqslOZRBePRClUwKBpJEkMQTt7K99nutEjwaVISilyRC0514CKxzOlWMSMrOcmH2if3JF2NdMGogNqfv/t5fVpLtKWfnHwgh9jlgAMkW+Skk5HLi+IHXrG4Mj6r3MtMrARyMYqW6fe/tQk1BIlWUa7I2WBP6x/9SLI8gewZaii/luBBwBAO85AVGuxyqcNpoHzoxt47sksXWEC50ykpu32i9o6zshr6d4ykUHpaQIfrjyT9TnNCkERR0nN0sdDxvK3ONCYdvO2TkqnbpKYQPLXZtt3C8w5DuLaQPJpaECHsWxjqXDVIiBPOl7hipCm0qjKXrzWhcgfdKNugoWc9CkE9ggAc+QhtegyZuFUDxU5SiOzDx+3Tcds0ouFKLnXG/DWr7/OsbjToRRAHynSyN0d4+V/gc6lsKYQhHK5i8OmQVs1S8S0sV+Jij5VQOeP4lG8ANNNXZ+vwCfGigOzCXYHDnrxDaZbkQO/4Ej+M42UnefAhX4VqIf1a7K9Dje2JcA4c1ptbT4sGVTXEaBCAkxZABDGHBUHsowV0sghYtqcDdDqZXmeoW4eCZBSKnPQbVDdHTVNl39oGnZTbb1lBz/vAOIzATmPBMpE+4STxlPJMPDuBAPb4v6Ls7l7M4tV/c6AOcO51P+l+zTYikv6TA72A8bxzHs+4R+jFiwbMoy2dYFEC7dOrN6qrY+Q30YMDgGhn4AsddoCob6LXFOVMMyPZD1Q9EVaTrJ5Cmbsm/7nIJG8Gscx1x6yvkyq7vVU/pxUAnJ2WP91+qrK7rFA4L7PFLmfNGvUKA2Aak0s9MH9ASd1WiNBVgZ2D8GjhaCu7yUmFHnjJDy1uFsABf1tsckSvy2X7slToivcCVSTkDGyWVIB4tkFooG9ihPAeRdkZN/EOpx0JC+aiG2aFwmNXKHLSmXhvX0ljEotX4zY3qnJBfnMDFg/HOXXVeTzn5B3VdS8degzezgllTndQmLNTTCLJjUkocu+pCjFU7o4dEpdi2c/jZtzf0dd4C96VhfxGDyp32vdBrH7YhlmgzjH0OKjiVx7C0Senu4XGmhnkk8MWrkbqRcrZG5Cw9/ll640XzkbppOjpoHhcNljXVmIFih2Ex2NDIrzl77PiuyA+uBIXVXNzBymB02dHXFfNk+ibyX53MfJniWTYp59cjNUdX6t8faHyg9OsHlfndK+6I5dLHTDHfA2H1Yer7F/ivcL41dNJuL4ur1CO0gbGb4J17/8n6HJUKnS8bLlMijtxe+QKHBSkeRzeZzb7Ph934DUaGeObUp+HQfA2afPmCz44f5A0YqlwNcplLz0DX6L1lgJ3zVJZcx7Fsm/uOCuS6gncRoYil/vCLRAHbvr6HG94rlBJ0mQXkgbNFbjcO3wU33H3n5y8AaukqDPJmZYr2KcI+IA2WUI4HAhRIJatRgCwHQuTAiwmD1Ggrz6PPCD9FiQ1/bKa2env4uJIaQ6X/7PhpWV2xOwsMY8Qzy78DPusK3ClM5h8Vrq2+lpNSs/JEMRSWGXcUMEcDBx6XAczxPMzQxyO3z/78TuWSWjPN9v9hVwX+ynGJTeDMOC+W4tlJtW8b1p1CeiHjRnIZZsjld+aBbiTezR878wVOCgOXdRaRahIudTFWts/1YNRA8UuV9F1g4U3FbRjxIwyl/ZRHZxPaxfKp/MQhFcLyRNhkO5FnroVAcqnpWECwO1WA+Y3S1QY6KZnBHC6oawS+dTNfF6NSKYLHjWnwyOhEGnM4vK5gDfU/3c5Mp3XUIqp6auDpEtL0WLbf3Jypm6/0ehikh/1+N3lCcUdCa3xCX9L8OyLTyaEwp9LeSNJK/CnlOQmgfBC5auRE4JgD7LQsah8DHT6+jPZ59hGVQqVEsjNpWeOCHKXWJvZ0DMRDd0A+pmrYJY7ge5xE4y2AwZtf8vuff2GK2k/02c3XPIF4fTVHhO8OfjsCl+SvBWz/3affmID+KAXxxDVPC6f9AIGBP8uOl18c9X5XYFJ3YWbEQStUPTTi2/2JVqgPYnB5GNI0lZfK6/jumX1Dj0C0lIocub39yUulK1ObNFe11DduyyJR5vx82p4vDffdO+tyGOrKLfkEzr/i3IdjvXflfcP2GCMUqE9XjgGjmfsKSC18/R1LW+PwgXRjHG5s7xB1RiMQdjZ5FKHG5lsg67v2+23QsouKRQ5qK1duHVBcR0+7une7qe+b1uLUB95sGPJyDJewB5D5BtRzrMDTB2ARfZQ4uBZMAxERMgVeOCjqpMS6VjqorRcVKhzZgASTrFFa+PzSE/8eGw+lhYjhvXrLaqnR36Pjs7rMyxy2ynOtchXUvFPbEDpxxjPkRBA6M+0+3ArjMm4bP91znoHBg5lYBqI7jZL6XNvRruNwMowan+mtsW3fvaGR9L5KEieJgZYR7/ITgWTIsGIZa7PGnsyqF42csU/8aLSTVagkV2D2cfk7oRunhUF98GO/5c2f8Z4Yh//dNzFJwNMslzBfvy5qVUYMBSvfrXOsU6jrdDnc2GwzhUz5241z/56lqSouSqrRsLJlzhiHJ65vMtEozFQ7KBiFxv02G0j5IN45yiVrkYW9HNOk+tG0H0xHn9FF6y81nXuYz/e7xx/SaosKcCoy1HmXoPfnyeckM6krwXm2wvPhLdMxr4YUfZ/tjjXzzV+61FdZ3cF2owqrKiSAOVO79SeTeTjeA7jMS0hk9b9v7bCzahQ5CCnZsjtRPW3T23z6ZaioHonlEtFBlnN7seyTtg+x2Ly2NH01deq55B/L/K2SnIZz/B9766DB1eqmXXFWRTEeFrhczSG95WUBkCo3B47icyJP0l5E9nvLgw8pPoVOXj67rH5McnDlE4AAsxPbEQXJzzKmouw2BZfZTFMaOEbX+ytah4zWfzNat1mMhXm08ddWQ0LSsArlq12xfcXZhgu7tqf8EaQAjpka1Vi4+y16+KSGTgkGncsrI8BvGmaYR7yue1N+7qw1JoOjzb/aOtGznQvFToYAqnBToVYLl3GUXS5vZjeZEPqMVfg8oat+M5EOxHfsAmFcz7oWNfxlpJzljNu54sQ7aCrQDerdBX7oBCtENhBrh7k6kGu/hvI1bflQ5GXSeCL+gGLz+tiZdV5ZOPQ3h9tJl6AcSUOD7RqNFT9XAnGdrHMvZ8SykB8UPx8scxFVhYN6rLQiRKTKXB60ghErvcJW0+yJhW1ZANiPrvIxkMIfFMIfOU80KUu4pu+umKSdQX2uxunfUGVzCFcgcNKuCeRbfNSMBAyn1cj85k88SFCf0TjIfU1ddd/AQQGV/QIqKgLpekfPjPeG/0YfkUnSZ62OXX76qPlCA/7pOLVLJOzsmq3F2UdaNMd0XgsE03deZbJdbnLUhHF+NGdtVVs7X4nKt+GOu2+F0ebTYVq8eJi+uwwthU98Vcu3efj5Ep5nLJYhHVG8fguNEXleVYabVFEMX7c20ojJIDO11yBw1bZ5dwTtsrho9PDoU4Si6+G+q8OFoQMPQho+k8OVgMS0xQ3LBkMmO/u2FQzCZW7Y6fRu0C8XclB/u1P/iXhgs9X5i0q7mhgZVDmjSXP2VHk47iPiVJq+LwPgUeWOajWcQUH4WfJM4eIUTOpgFQERFABu+jtnuJQUXkembg+CfZzc7f6EmieDFP+qaj3tA4/Yj04PArngMVjDaqrzrMC3yfFXQuYu9nvDocnOUvdtWuGOhoPUmCW7pODrtXmAr91X+ZVn5e4mr+o0A98WFOhlEpdbLqYiTH7Ikmq8CUu1xUbEB/73dEJ+aTcdtnFZB/kqcgB525XlT/Qpq97Ij9mhCEcdriyMTeiBNq/BI+/e/3ceTf3tG+RS92qSPKjtrnHjfYxSC5RSmkZspfpMHvsb27o5tnz4hoMT7dShOD+k9tVPaHK+YbQ5DYTdU+o3B17r8GaGgHA7Nv6RCb2uvyOhGXIfnfEdpSS1EkqnFyp023ojwxr8sqUP0D5alZ7/1A5ZFl3xxj39auoN89CpY1BESO4Akd8stsT83l/V+BxhFGfP7O/0xNxyqUuanmX8UWBGih2nBesZTathFcocu8vjFYudRAdXTYwGLFU6Ix39lRkA2edtFWFivTppNyIrmsghEsLXb1LrCCKmNkS9z5fJ4/99gRd0amhXPxrwRg1zGdXvm6/NSTt2nmR5rhjEHuLEJ4tnD6aWhgh3FugqePG9MW6scCQgS1qxwZDurbYiwTN2EQIzxY0YxEhPFvAdeW1B0N4yics5zOiuSf5GUIgySzAY7QNEtMCPEbbIJktwB0Mal0V4aA8fXXkD5jrfDgNzl4jFDmZGTChjnFJITrcCUWuIyaigSbAlF6uQ+U+2FVYXbBdolvcBbSB4mmJZS5YH5Jqc1FmRVN/RRXC3CjlBoVBXLx+Ufq9bKfHU0pLjB4yoEU5EJUCxF3fUJnHoXIHs+PtbZZnCZCUhC3wOEPs4F6eO+VyptZUcpLBK6JbfCeYRSClSA/p4iJbbYAHFONXN0yy0jx9dcQEjNlvhB+S+jva6KmpgnHr88mPH7/KPe6+umE6fdxlVeeYXBZiUEMQwBf/f6EEoLJY7sfBb7MKpc1b9C0TLfsqIJfL4rHaUUr3vHdlDlwcq6BCWoI4SA3l1dJxUnyXD4cggDd+ebGCAH74z0/UqEmZF1Z6NSO9HwPKvbCff0tERwax0H1foDpJf7ME7xA8hMNKa7GWW2X/osuUvuJKUiglhg4uvDWZSfWQ4S1eolqKiWeCdZGOO3J/q6EnDBHSAjQiNZSTC8io5WkGpAFzubSv0vukRkq7MQjgchLM4BttrsDdRgm9CxLL3LGSIyJe0ru2YV4XqeyK1pVcXLNyJL2kHz/+XA5U7DHqEm2TrJCOmwoQ+zbeJeTFbW9e+Fg2Y6oJvh0NmIPcS1O0a67vM9zjBH+mN5rvkmLz6Yd0CNCDrubSbDBLfK7xee1dVjfhGQsBlD5pC+3QzHPFNtppxXXKfHc0ioBXOI7bk/eS3xNz/ZHd0jNbROYCUPowlx2aeZhraFvEwn53kNo12nzNmnuQyaRCN7xAQibm878B48bh1QD+XOwZfK8CMtwCpBtWQ7lzP3RTKZY57MyAhdjdMnxeDz2gyQQSIOIRAOA+dnwY3kHnM6jcRdtKsx0JFyLrsUKRB07An0ssc9DFUUFOGrK6zXx3xQZ0kCtwcXCsaymh1vjRhZsmslOFEwq2LQH8xFJ1lBgRnLQ8HxJo6s7orEUaVDhYTUXL65MXEWOfxIsuQrJp0HAi4P25XOqBGbwdl0tdKKnqr29f1f307SN4Le9x+T6cgbrtFRy0AsS3DZAMChAHlcF4NRt6JTtHTqAhJBvwXkIoctmohqpKtQcAcHmFkaJiCngnh1OUih36juXnVyAzGPvdwVG0LTY5IruM4CLKfHeWryckTg8kYbsCJ9PhDFm6ep9AFoG00mSAdSkVWH0KtCuMaHyVCrjunEpFuAJA+8wh6b44M891OVyZgazDFv/E+i1ry45j7ZIx+hi7rLDMw6lx/fi7IQiytP/migX0qhOK3KxlkA2C/b78CfTZrSBy39Y9Vw1ZNQMWn2TlyqrrluTXVZJ+xwODrnfFMgesxGET0q64Asc7WARfFotl7moRiFYq/DdYPeEmFhZTwCpa0tAytgl4DAzfPcw28Np0tno/m7jvX5M8R00c7YXF5aO3GOrPxEerfMEYa5+Ic0/S1QA1KKHIE+cFed2ISa7BPYHs02fmEiW1aDYavi2v7x1ttlkBejTyJauRNpeoaauCpBxFoeGUOVRehyRt/XULm9jbVVzhFe9uoFtaZ2XVzRa07phCF7x02hG1b8rrWSj0xqv2sNMCus8bHBBILnXh1OT2tjOzCcw6fV9CUKkpzSxf+BG6AsSlDfLk7Lrs9BIROV/2PB0593ZwKemfJ1ikxzi88Ni8DjAmFOveDy6Sqj9+yZEY2BLXmxEII1/icmCbaAw5A0HlezM8Rrwpn+NWcBgVZmFUkdRkUoIOGGIfe/hB3gEPcYMEnoTOQ+JZ4JhH5JF/hTuoxM3/i+T8wJ+2OyAXyPDdzWvrn21WQc5aw3dHi2fyLUe9qIBxq6Fc+n2dPJ4+IokMXIGTu8gJXjt3ZSUl6RKKPEQfyQJXlTkk9lUwzneSEYMVn9fU7wGJhB2+urhChAWkXIvAipBfF8YZQ3QtmlNXal3SOCCAJeXjIedsmFTYm20sbSvyDL9/tBbLowDC6udVYIlpnnUnNi+f9uXyw5MtZ66Ly20R2Gwxuf6U5ug9Ku6kGB9sgSO+C1RlpeQAKRQ53s/T2mIoLLbAycgXOdh5PM0p1rvp8yJrsiQHF7hY9hOvczoBuOB9eRe2xBlEHqtbW3uehc00CZ4/5OJ9WZ3g2CTutvHnzJwEczQGpXGzgpgUxjCTMZjGl+hD/wvimC9a8jRxWhBDhdCf8eNqWChYrvnJswXlGG7qPfqBcuk5BfPdyRpfNaAfAl9ij5HklgERcgUOG/cOTtW780nVG/d2AI/kcyXY8sePTveLqKpQJeHiCvZpace8dScen4dv9ljeNc0OCu7DfndyvQaeEk9fVyOSxjCWgfvZgMZnJ1PXnUnZguPie8XDj3UCgE4k6vPI3lSgPEaemhGNl9qjrDsPs3Rtizimr66YZKnAfnffLS7LXBnydihzORqfb8QkSsO31bAh3nFisOGIxoMNNXX/vdjwKm+F4Gndl73cY3klSNxXNB9UoCpLI922i9h8ovsYUayds/HRs0uJy2GavjphkpC41AdiVDnHp3J1u9oTH1/foy36klQZOWyHMTGHyoODDfXnYV/aqGDo7j4taQD5N2K43g8hhNG6NxruDKaoNw9jhTOFdGR3PKpf1TloWGO/O2Ajd2aySYL57GDGKasU4W7g/x/lObFqCAYYEMDBGFDWop96/8nJLFEqcv8KRfv0InrXbPPjciPtv+x3l8uBosELZ3jf+hE1D2X1XbwrgGGcfEfwgn6iq3HIUiZ7wcIwzq2cPqb3WH9ENNqyvjEV6GpEZ9+pUHeNYWw+blHKqmsVorrUe34p9+SAVx7Rrshif1/iQjguzlDkak/A8nKbNE0mRsSWS5dzL1Qu0Ih53+ML1jV5lCtHXZLY5qc0oZYwL0KRA3eTBF1TUEVQgVDBOLbysd2+RSkWvXkN4OdKffpP/VUM/edhvFt5i4pymxUJXgPaljg479YuW1FqgACr2baoaOg/RVD8ezBf/V9Zfe3mkciGv2dyWkzxToDZ+Tqpv8dxVpUx+ihCVljm4Si2aWkChTKnJyptIafSYD7b4/qQpPdZgWRW5QocXQjBnZMvcTiAZgXVMQCUQpFDL9s0RWgD91Moc1j1VSVuKv0np5eGd+Rq9ALhU7XsrC8UuuMFPTKkwlXKlHiyJFCIPJ8T1VGeJYLC1n9y0a7L4vRxR/hDTl4qlDnYcqXElq5JLV0d0TTGB7xTiS9bp69O+/TuUwFIAa7AYf7RY4NFuiSh2O8uu8a7bLNBhbhpDF8ddNy2wBKo3yAE7ZYvWo0c+VuLWrShqQCOmgZ3L/xtA4jSQ7JY4plHxDCNi4iEIjd1Fe8ixC4iB1ORCl3c7MSnGN0XF1VCduAYvjkc5aXw0K6BocMF+4dsi2QROn11wIQ2WdLPikgbsWyNyznaIg5buosl6om2W51V5VZYCvSLy/Mj8dWRm7frLn8SUYwfHfQTIZ3tiVMK2+NU2FzpB5eTxLd/oLQRjxD9R4d+lBthTrsv+7SCXmGev6aHeOEacfzshgs6bE2fHTQ/ulemYIY+scyph5sPSdEmef4kdZIpWY0QZIcaJgVZTB5iUF99JludnO7JOdFTf98g78ZcgdtVtnyT7STdy0o88tMvbt7whTSg6auLulXX8suj6avrSe2qFudr+uw0vrfoNmnzBks1fG4h7zVrabAQyGrW7Umy3SXZXRF4M9xj8bkZVlZdqx3jZ95ln2lImN5WeI22WFSG+tQKyDx42ohhray9DiX6Q7lBeffWlj8BMt9dvMPrpqtZIYE8QpGTot7pGd3TIbGjQPFzFC/xPPTnUZLncjDxV76Vpu/RlvYGMvsAxT64f9Xj/jUE95/0uP+kxr2nLeEjeqjfo6ZBFV47Y8a8sJ0BxumxQdgimmmfAFuXc4Lq4JY9HLm9klzQKBEpRO7XsiJJcVQPmYDi1ayzs7Jqt/GWmITOY3VZ4JhnYemXU8giihsjgBJIunsYPrqoi7sslQJyDh+XWJh7YvqLiiYe7fX8MI7ncXmwuwnBTLweSfQRO7+Ku8UyF85U4eRLllfLSSBgoUf0i5PHFUrEueu/uWDpoxUcPx21zb3oJQAUe+Eek8or0TMQP7HE+ICSuq1QF4g01D7BoPKyTmjrr9U2MUcEqkty7y+9ycicrBXPLpZrP/9vM9xkHaywidj8GVKD4sCTPzlPnm93ZUUC5N9moa/JOVQe3Giov1ZWPCvzDRR7iv3u5pwEBaRkv7u+QIDw8SXL+1t2k32JiJl+I/tSAcUOwuF7JoSk6L446IrJd9EvlX5xfTv2qRANmex3e2xYmp1lKN+QvwRzq1DkzmknZXGb3bUV4KanAHHglsemSuTZZT47aJ4UwfAYiFc9+SKXi5O6zZvz4la6O5m+O7/zwH3QvPRgSlezCVw9FWmc514TIh/vel3tecR/vMdeV2VbpUiKY8N8du2VvKX4PRyj7/8fGxkdV+A60ndJfQ8NtfvuamA7lwIcT59dcV01leI59VDiivG4LHMIX/fdRWstUvi1EVuwGrFw+kg24bdol5cRsgCI2HxczIwo5pESvU4qR6EYPy+pcMZSweJuftOsQOqmXPq8ndlCI/2QILfXVVLU24w+moJopoIJa8XchqsS2R245XcZYpmTybc7PUlG3+GzqwcE7C7i7ytCa4KWab5k354ahLezH+iDFEuFK3Bai5IH5/BtZftWFJsGh8p7xzrYNCh5sRhokJivWyp0NQLKvWS/O23yPzI8OhVWqNzlZqYXhT0rCJcyQuHyykDHqEp7DFC85LlYw1mYaSRtn/nsNP9EZktGEPa7Ozd1phOZoFD5vlS3T7e3tZjJfPjm6BQIuAI6uU4mTXp/lf1LWB/MZ4cZwEuVho/k6T5+3ffWfFJud/QmVqehKIFcb2z/nu2OqvRecuaSSx0w5ygpxOi/48fVqAPHSfr9vMCznn6P5y6lQOqhIlhjWrc7yUXE5OiRE2g9O5cFvIBuExqttIr0sgLA6HNfbIVmrUrtlww9yKfU6etPfMs7JOqOw00iNq/HZyYUBy5aHRf1sjwOEwnIvNwvDRgOLLQ6FupPkP3UhaYVZnF5JRXWI5jtMqHTiN4oNKU3Xth+VWD79afmp5OqrOsrlOdROErE5rOxGVH8vFy1N7fKoo2RnmrC4+VKqa4808EtqvVzMEGSccDGya7Exd++35cFn/vhq8MjrWxzh5q/l2KYOfa7Q7+yRrS/9p8c1seqI6HP9jy3u9W6Lo9SMacIV7IqyUACnseSDhMuTwmhQzCPlKCtSgHdum8u14gVHhLpvWTf4Urc7eOy/OJLHEZK4gkIA+0+LSsbaKbkt2gy5PErRSp1smj31/Tkb2FdS4U/izQ7RndZAaSZY787zc7D6eMuKTZSkiWuxOE8WJ6VeV4+CGfC8asDJvTwNSs2Eqrps8N+noqc131xMQw/iWuh/+TWC4xAeuM2fXZxZtlK0UmHbw7G4Lo+yRMxffj09bDLqnfZo7ou04w6gQOZjjrT/A1zR6VLaCRDy5mLOpghsbu8eW8U1wYT1pvOJxXYra222hEhxGOEWmMPvDp3nVRYf/XtXI/HsW+/vQZn0WOij7OcXNoP02Mz22IV1ZS7zDWPM5CmArKY0y6gDmNM546dlMUmI/P54rz+2Ob57y9vk7wWr9lNow9mHqwD0Jvym6PdLs+IfOwvAjK9vNDXE9logB4uGSzYSddA4FyNqCNwk7abYaw/EGtpeSIPib1ldOQKoaqKMVgwL+bg2lk1f/A9DWMRFtf+2WS0+DpxyFRLxRwKw60VtQfsq2aJsZNh3NCjWZoRLhL6jOADau7LzQ1JnIZBtEygqiHdJ7JwPVhXbMELcCOBfMAjjcAMil6GMQJHqqXZ4eo+o3868IOyivQekQN05QhFM4EsIWCNwBOqfj5jphhlng07AMCqrcGVBSTUa9wXfoYJH/aiwSUkudPvCBC4wr1kgrHZBWTEoVuA1y5r3bkoKgDGtDcrxPDjsswtTU5CDZUNggVzsUdw6Ndof+I7GHhoZHD9/+19aXPcOLLgX3H40+7Gi3HbMxPxtqN7I3S2FWtZGkm235svFVQVVMIzi6zmoWM23n9fALwAEvdBsqr0pdsqAolMIJFIJPIYmwVuwHOUra5TmBR5XdBz8S0Hqx+weKwfGWS+Q8rOQ2+hQRcNvlAO5LgCLCwPfKJGeI6mK9U0+BM4jbpkYvcc9PFh+OwBdeSjPjSfEqcPe44MpKZfzULN+wnOqxHBBFdAZJu0DzT1L+3fefPDbVVmjTxc510/HCO3iciE5NtoiScXtTiHWV5gTruPclA1ef+ueT5vHIHqO8+f8Ulc5XpsGlxGCXwAeXGX/gTJ7+8//fLx0/t3pC4oTrceP7x/97KJk/zXJVnGKEnSgpD++/vHotj++uFDTkbM/7KByyzN04fiL8t08yFapR8QrL9++PjxA1htPvS712C1oPzyvxsoeb5iiqtRrhvN0z3Ofvv+XX+4Xy+SFXj5/f3/e/ffLMP99n/BgFMaDroBD+9EzPbbh37H3zgMixH7/T3E8032+h8AsQNxNq3CXHErUF0j3mGexPGaLV9+kIKn/buqYZKnKFs+Rtn/2EQv/5OGV2SlElzj3lXPXwXxHoftGeLVvMV6wAnXS+ieZH0QmSzjcgUukluISI22LnRe5E3CCPShAMsCrFzAddknPNBZO6t4gET8VLjg3r+7jF6+gGRdPP7+/u+/mC9FkaVSmJ/+/ncbhvFC9iUoojp1pB/OwwAZdx9PMP2t9MBVwZ6XbwASttlRXjlmukCiXTs90FiB+5FFW5LzKhHhpg8Lewsxa2BP5XGKlVZHKdKW/qROHkMYhBxs5/AiPxhfFSdp27mh6YKhHV3kSkP00j4SH4DqwFMaGNH7t19+MQbK+vXpsp/2EiFNuyCZHN6WB+no5svzPYpLtRDV0zybqHDvi4wLgcfwX6C9wx3CcndZJqlBxGTjTr++u/iPxWCyFjhLRFomq397R3bhr+8+okkyRYcucuMdob/ZIITB4tSZtZNvf2P4weyv7/5b2oHFApPhKC9bgKEo+uQNSWvZob/xa6Y7hP2uFO8fbRaqnsCTMsZ+OYrjwxj8twT+WYJbkJ6QRHEy4KYa4nkcrS82CPUmA5jni+ZN4aR4erwWWWiwo6tZ1YavEuTegLyy0B7ApnQ49dqereT9xeKUayY7iPLXAPeoBF7kuDjUdVyuYSLgZz2z411aLsV7YghDm5Wv20ioOtTuANhYbBSWUy+xu7lIE2oJ0riJNdmTqT6qwrocjoMef5qLniEEJxFEv+KbI8P2NkBEm5sYV4wD2Mzqo0C5UznqVgZA88jpwr130cvZC9hsncy7CEitz1QJp7nqjM4x0tSidTEwVrKzYi57ODIJbPK4oS9j0zg+hN0wuYam9dRqwCNtaTIPZnYvVwv8lnGVfE5xEua10yYgZ+MfJcgLpN59TwsnYHY3Hp7ZMsqwmwQgWR4rOLgIUwHxuprN91my8gTJ+n5pJCCOkvwZKHTA/RATmFoLTYr0mol4+Fpu7kF29YA3Tu7C8YFtBa2LbPOWuv/cRWeetVOPHbnsYss4HOrZCPQu4kfbbZY+uR0hbCZdsWTUMzqSGnlWwIx5+JCY948StgOVxLALCcgHiOfBdJHa+jtSq60xP5JE576BipysfME9T7NNVJg8jYph3UZx4RvPo9UGJifpZkO5vjh6DXq5Bx49PMAYIi53mzr3W2BdFJ0BIbWbWd4y60oBoS6auqY+Cxb6EuWF5BAyOAkXDKgeZh/NMZOfPSaItZCsns/z4ku6homv+wGCR/gaSXwhSNNZbwBa0IfEUtl4cHt9p1M7mVrwBHGNVxtjPhl7muJZDAH3HLU23dPDNW6g9D0AzFcFexVEiZ2PQrefKiDydwYtbGqI1Q3JESkGlrv4OYZZ8XgadbYOu+3ZgmH3pg4GfxC3Wy/n+veoqOblFmFc5m7H8h2aBOzC6ykqgDUau6GGtzFS2srNd5jDwoesxvrw0dqXftVAPAVPcAloI7lbYMUww5PZbXYAQHSp1YsxGMZcmaEzhKCPj/ZVsdV59v+mKD7kJw5HEZ7n1rvLGzDuhdUKkuDMtYSFrhmZhXdp29FpZyPw4BoHGiZLi4ebXncXTE6ooAinCa2lwEefwD55AfZPuL1O8yKKeT56dq9Kj+jU5utadrs3evEIzcHWqG8erHbBIch85W3Gxi2VPATmtbbg/KKYe3FceE6/ADxZF3kAb9a7xwwAffh/NYWP9g/I4LIH2+o5tInMQrr+jnq1enxNFcrmyqRrIbD0I5voU+4QJE04nWRCQXd/n6E7mtowZmFZ2AW/8eM4XWPt4xD41yoWdYYOTXrXMs24Y8T84FntzKcHDZLkskLTjtamqTDyAeq2Kvt97CciupmjaJ1fJDWWTo6LvEQK1uk2/LAD0dbqd0a3Q7j2SahhndCPZjac9TUtfIPskgy6hsrP0j9NlglDFl44u6QYrsh6VC3HuLle1+mrD+A8rkl1fw2oiliRAN+2HKgbRPysgFqjKYdPcFVGcfzqwjgEHIz7CT58eigotWObMOJhtSsf1iTU2jdM7w4yDW/W5Q7duMmfg3YDw9e9++2UYDSfJlCJKHdeFKCbCGc/uS03nrQfL/AaYHdI7sQ9Yh3x8wXSV/4hj6f/7c/S+xaJuooc6BArzP0jpKkitJwPCsgxfAQc8CL/Az4UJ1HmdAlvYLgrDzfgzxJm4Kp4BNk1U5PENokagdfpIXLB+vEXC2FV4sUp4BLrJUerVW9IJ/Qv8tP0OYnTyM1qVcNwW5pvSVxt3wacE2WXzSvW1cMAnlWURg3k7GULM7JLTqNXEUQtC0MNkHj5EYDu3P05ym8jpMMAH6vKQrJ4F+71d3kYRoRVLjsZALTWZ0MXA+gOvPjysb4ByzLLHN+9WiAnr8sYVGLDTd7R8K5BBlPHbdpCJIc/Aeu0sS7Ie+FZgrs48SvOdeEnahoJWZKCOoobaCePOF93awUAS7iJYpydGf0rJ2mWP/47ujLjrCzomLRA3YuD/kV+ljtNIZVh0Y1JkKqDLd/JE9piCFhVrcbxWlWky5//KKPa+uAgyavbFIF39BRB1Je5rDu823BxtDq9YOKN3i/pc0Vr7U7utgxI+4cPr+QGfp5mDX7HgEqzYQP2OFr+JJlVcTJ1x/gSfLnD8C6qOUSXi/Za6qRSkOMLrQzclBsfC1PBi158wWtg3BZg6w6HJMbP0tj1megzXIEGsxqks4sJWNUQIfCvb6O9jBsfl6/HZVGkorxJunIBt/4B88cY5oU7wFpgxQBtPnT6MCYhKyM3ulQQUHDpZKpiAHg/H6/iVdgB6rvUCXkbDzTG7RbBiWJrQrRep6gx2pcq7Nnv4Y2Jhl2/XHmC3FjizpICZLkzL9YimoEKAjNQLcZHHRNdiu4gqPau08GGNASQF0dFkcH7sgAn6eYeJuRaF5RZEf5NXbu8rnfqQsUPANeP4bYvexfzDv4HXAWE/jns3LSnkm+Z0wL2K3B8PajwPYx0Mpk01f0aAIs6pnnRxB7XLZySRBvGSTvh98khzrp9BR8Bz79y+3dh2l4JtIv+tsHuo7upfi5ZQbh8DJ9A9op3v7kdku3tYoVsrjHfEtj3ONDAg+3tFCZDBsvvogw+PIhekWj/dYuAYuLDe/VwlcE1TKydgDsALvQeRzmo1U5nK2EL6xJEeZkBvBrSyTOPxm6HONrQbmu+1ad2GPwPdiiL54jjMlnFoKoqw7GJuwqXCvw1yC6Q8PJho2UA4mnwCe/2Ma3MvkibcXuEgokP31akKbOesvpIabuiNar4IfiiKd2nbNJ3NxPo/oz3LceMuETEOToQNSgNoXm/AjRD2cs8zVeAObrf1k/yOTr1tmlCx3xZ2eQGUHzFqDSLRPxkyN52TMzQwsHKlcEzoH5o51vBLzenTnGqGrcD3bev33FaoOX1DfXsBa9JFH+ByU+5wLcoYxmt1lxdyY5+DO22eO2cHe3evmCkfBExvgzUe9CPe+ybE2pjPCJ+7H7e2/lKpo6Joenocj/DPsq38F8izjX2yM3v0lsQg2XRB2xRuqQBccW+//pKyEouYDdRslY8RFrwh0c/cr+G9hl66/oJvg2cVtKX1XnOhrqHqIyL7+iiemmX3kQ/cqq5CO+/XliTegyTKGsdRNCU3pMf7A41tLq0Y4dAZrm9YtkYOph3Kqso4BRXqEtUGu9fbVzZvzrGAOd3WZTksO+/qnFMt7t0QQFxehuS73tnlGwi3S/BCkZ481iF27O9A2SWowc4BLFzGhWRWtp4iH49JFHODVfVfwp0zCUR7powh+wmdHzVITBTEHvRbth16KV+M0v4NUu8mRIUuO2eKeHtpr1vN20nk47xm3D9vrU5kPpRDdkWvkZtT1dFqT/vN2UMxO5kdqmMtiD4e22dVJSfTNDU7tgE4/kAdgPyAglcIhUlJXVN34EboNfCeHCrpaIAR6+YHaqIOt/AmxkWnHCO0Ovjx2mGz16KLKJvpiFsi2SvgaL6fU9kmqnLi+MbsHveCE6ZKA0BzCzd4naJgDpJYgLB9BSQIWFjq7vIb8t7kvAqmMaEiyEhGX+1xfyW7qnShCsJoJ+WIM81QLo8TtBC+hA0JoZgzeLaf/vF0p3FQOwYy50bpFusyFYl6Q08OByHLx2Mxc1enVW1/DRXv9uOTjIfQ+E/julYQaI1OC5hjEZfNIDkZav0ImB7gllwhBvD/R7FpX/fMNH123D+aDjKddTP5F4ruYcjnXfCUOzLZnOxTtDsVQlb3KJigwtuOjLpjQ8HsTaaztJxmn0GL2pBZgG85skvKTZ/eI4S8sbvee11o23ZNn2frUJjcFzMITDpxE+0dRySLRK97k6oeEq2tAwZBCeOcLER+aYHpaYbZ4CsyjBGbN5mbHA653CCnrvHcnOfUKXxrPNex04LMKsyRXMxZNjL65ZBKn45BPHdEW8rPKu+TpKznXfHTGYNGKLcuMG6wBGk1VN4kKfCmvcOKV5rYk1BEDmiE5zf9nRC4CI/RwKy7FIRz+w935R3D81/bGL+pafbwt2X6f3Gx9SCkkRtD3BJ0G3PkDeODs7R/ImvHtMt8mdLobnpJ9hFrtIJxWkz9GPU6tmThKmFM4jom30l03kIe4NPvy+2DFDt07aonSoC2jwbkq8LZZWqibFU+s9kqkg3o29UlU6iMozccV8ewo48DKbXPSJcDy6fetp5tATFbZoV1CA2tBM4TXzCZ+hWwpLQU51X+IcQD1L1Kt1F60Pdf87Poq4pQdHcd9ks/T1619C/RxmMEm6q30NY8QBFvfgVtcYpFOYY6BMyt7N9tmQd6NbJks1j0INlbdyl5JBHeQ7XCdqBjXIZoOrCW45XtQP0bplDuufJ/9jESkVXr/i2p5o4RI+8KourBwKSEBlCoaL54RAO2BAePvi/13GZRQMecnbv0XQktnbu2WnHoQDq5iFsAVeh2/y/nbTFZUT8VTupzDjGOkWW9MeyxtqXIRDnU0Q/UYXwnKsyVtVSnawO3XWpLYmlPVMeX4lGYCJ1lQNTGXBQmz9I/k1/dkLfbmgjmeN8nXgzMccNYZ29bNOsqLeoTdit9aasI31vwZt6am1GmfQIdVz5Q1h1Mc+7rxUNy/FJfqSTwzlEci4vjAKL5tHqv9Ay0kXC/ddqIxbCEQaydNoMd4KSt2uO+mscWpX8pHJkuIAytwa5XmLr9/uDkZt9+q2FJnciRR993nLH1so1dd83YXtgwnbkrWJTsVAk4L3mGtkJ6X+aPidxGh1EgHhD6x8lbAcqE/hnCSAB+QBVHvHG4VA5aAb9ljlFnXHAuMWh1/B8plvHdkNQVQbzFf7HTXFuXSMnyX2ZhN6ypO9llnQO2xBRY45B06/+B/7daT46MEMNxxGdflFdCy0J79TvIONtL9cSrY84VWycri22rfYh2NaXP4RTcGJ3CXlyRD1FX5zIUicskOntw4nGhRZr9zTN4Ml4WcZkJpo8QN41yPM0KzfXaX4QpvW7dAuX5jxXd3MLhK351p7jPTxa+3mgu7g+Wq0ykPuv4SfNZeBVs/eZsdHUm9rbawnZu4Q5D2HzEmrNt0/drfNX3/FtjJfbyPbBirGq64JkGUVSH/uiuikEVZ09E7WW1Vf/ZuNtVx1ZueYMWFiYIHg2qBTMMR/V8+v4BFFDEXCdJbQqk5vFPVHJQB/NtbADkfka4kh99Tc7GA7mTCBJpy0Phrov3zbtK0hYlsJ8Epeor7Xa4CTiuELYQiTxlPK9l7aevKm9sLmNzXHn0o5103AIcnEnpJAFV+8uo/JeGMWpe33OgLAS9gg31K9IjT6UnKxfomRdWr13dD3droa8Snt2Jh6SD9QPKFwMwQ8kL3k3Vb4qel4l4AldEH2Aui2irDhFDGhc54P3yLTyBKmqTphusCuLk8/40XabpU9gVcM6kYT46p1raeEbpMeyg17PmgOt9al9tGCPgCyJ4qOyeMQSu8rNcwOWaMIO4biZ3BJ5tqFSMTs7d+ClvKD8eDyCrfVMz9CvMOPdpT+Bn+1DwB0tcdknf0DRn08QLbBTTUbtHVkr8vu/9Qih/HwndmuP4cnd2qZ7pp9MvtRVOeu3RotE2b3+bs8cdT0ea2wGAFxcF0hPpDoWZR8TS7J8wKrLq3kEparUZufmWHPnSZllIFm+nqQrX4ArgDdRoesS8u/Wu/IueqmPNc5boLE7tSB5k70wuy3vC1ya7iJZxgjVYB4yzGBnL+MMRorutUWUR6KQGXQcSmvZMA6F9WCjUoYGMtis5oMxYgwJfojPhig+ByD0nIpHDj3B4pFDz3YN309B6Kq6ZmhGdCk8pGuRQCtwHMVREtBPsStFSiqTUlkWAg4VbAh09UBEgFXgovM34DnKVtcpOrHzHyADaK+4WTZPHsHyZ1p2IVe+LTyDAbylTGu0GoEJ3dTo+PAAYxi5FrZprzFbLzQSmyq+l6HtUW3LE7T+rOJltewICu7tZyEwSt407QF9bgbGKP8JVqKpc8b05OnpkzdgZy9bmFWO22nSZQH1CPc/QeSHdpovTyESbsUpIHxoz5IUmKMlOeA+p/HK01oNgXtkBAr4cZT89HY37MH1tsVouBcnvkGSt5UuvNAX2Iv7yNOBVEtoohTU70F+9kSJtNcM/ovsNBIRFy3Z+ihBwHtjN9EANyCnUik6SqMtfvz0PzlDwB6xRhfpVifyj/p1ifvmwLd9+DqCvl52+3XFfcxpDRJfvtAm3JYFFbHk2Xh3CmLQT3xwGN5C9HXhBmwimIjLPmglEI9wDHR9W/+aFm11E6fn/uUSbIu7R4gwjdDP5Inwc5Ssrp5MlFz99Af1/flbji4NnyHig8Mo6tgaH81zkLc9nZ5DSFfz0etubhE49ltUm6/+gA/kinFofNXQbb6yXU+nxf2Wg9UPWDxa8levuzMqYatgjcnJh8C9jfpFsYBVhWURHJcX22Yd3F/rApslL/IGVVLaIXJMM9UAQ1fJrccLzg0idouzlHjTLFuI/tyabkGC7wG+MKzA+UPvEuQ5VenMOYV5syJEl3Q0ZY8gGduNfQiisSXWq5fRhFrg9bRpUHzkDsHlVkj+kNCPwe1Awd9+x6BmFEpCv1U317DqMA89Zexooecu2Puj/3fHJo2ecxDBRd6A8qI1fUEsnnTJB50iTLDU/xG2WlyZrGJwGhWRL6dlLKFPSE6iUDtCXMjN9FmDOOHR8ALYuSqDcnoY5Ukm1CqqKVbwsK7IJc6ZafNGNXd1mLYvH5DFa3Iv92rGnZylCYSej5eVQpoHd3ryeef0+1ZDm3sWjpbHhYmtSL8G/SPcVi/c+78tJzwD7rJo+RMma4/vt8RfMawORh5hga9X4kad8QRujPOr2R2HYtFp6bWKRKp6engCHBrYg1tStFniRxTHoDggZYYX9afDEFW3RbtNdztDYpizQ8tAEVZ5qsCHduZnRrnGYYaIOyZ6A3ViXpu0VjcgyjtrlYU9QKDgOleeP1ptYCJwcTQslmZwJSzKLMG1acFhpIf2EDXt6VScXIj6eOCoNtN5mlWM5MewUrMjIIZWDcOyDVBdxz2jcONeNh23p+AienjAxio/4DwKF97cUlLER3B4FZ91l1bqncCuNr4BYYQchrePKfG0PYmyw7nnuMvk6yirL4ZOpv7qmcPOpYju66Jh0hzg7ko0+Skz8St+uEBGtHVBhsvHhSwP4uOM3jkZOAhCPQQhyPMhk6VV0zN0gpcC/bTZ+gmtwS5gf5Ywc3v5JnZM3L7e4T5gXuR30cvZC6AotQGDgJyg5VynWb+gmK3YwRX3sjR2F+S+Kidf5MR9weAJm+e7YJlP0V4EHEyx4wHlFif3EETYfPc2oumtbK/fnW5g8lmWGY4ErwPDDsh43ifdfGsNIXi3Y8/ScYSl+hB45eR1GYNKwDktDwZzDTKYil389FQT/MBMoDl5kugmox5Nl+CF+5qikcACRrHloxDbe/Zxi2TG0U9fcMHV/d+GFLkcHVpjeQcAdvvlUzcFxhz5Ff96CDxbJUSoE7urngn9a+M2dV/OEtzYQAprr/+ByClE5hfwBGKLyiDpekG6/tu7i/wbidf69d05HtMqh3GaFToP1P1nHi3ouM6H15DCrU6V2U8WVWYDimm9bC5r8C0b2JvZvf93m4e2B5BlIAsB26uZGDH1euhZqb8fSHfuhnA0O3wuii0/MU5PMJtO37ecHwdsVjBZ/1htEiAegnC1zsjuJzG8hyuK6T3JQL2KD6ceSkW92z26ghHEpNmcHTdpbGO1ZXo7MewF4vg4BLehc+eN22bCbbdxufYO1MvrkEWGX/1kN3h94fKgnoZ9MCIioa6M6pljEGArNa+/kAsEyIN+x6RzciyQaOqWpM3Ed49gA75HGcSgDoGDCcEmrKdV4dpQqurA5LJP/zZqDDUcJ9We+/vPQUGOUNU12upYzmNjYxoHCn5xU5oQLBj8PM2WAOGI/n8Ux9hO4VQO8zNdHd5bJAS6/qeqyqujOs18LjbxcbqizmA3F8I0KdA+aEIwv4LiOc1++l7q6wxuouyV7MSmcJWNGyYPiqNLKAF59oKoTNaAJBV2xY8PzABNfdekGvqb1EVs+XebW7K4KBsL29y/I2RZtlqWfEkxAINJ0bdXI5m8QUtGJX32BN5j/WMvBbp9ieW5eDZzKExx+u4zUijJ//lISjC1YWi5B32DQPxabk6rXePkYtJhR3xWfGHXQTwFSbqBSYSYOZg3X2/Im7Lb877V98uIxHscwnkyd5NauHvaEolMxEN3Uf7zgBwuabItkhYwvR0t4jdlQpdTUJSVaHG4jJaPMAH470ULxCn+nIJoZTiTY/SLBxsa8RF0C9gnABbnMCG6gtPSNUACoPPR/GX/tlwuAVh5ihw+y7J08Epum1FxjZ86rwG63wsTXZpA8pPmwEpCHoJo1Lhame/coxhGcnXTyjEkTc5etpgnONH4zsps5RVoIQgJ87DS7t89ub5pjP4VvBRI+C7q7k5HwnUG08wxyhPx5/YqYQSKjapdk2UrakWzYuFDdZF/hiu0u5wuDmWCBGJ9YgZ4D/9HCUqwIvn6j4oCDXMo0REU4eZ6HdPZSTfAZKEzCttk6EwhdhkEhjU8nWXbObTxBKl6uewdOlEzouUeXZazVyu9QnlM2XgbXyJxwhf7roDBCkY1U5jPO9s7QHpPivEPQUj4ONbOs3TjXU26S72DvAHb+NUMrpbuNSj46grxeLn0DfK2vP8vsJSnFbKRnvjFzc97m0878S3aDXcZdAzDQ0D83CCJ7rG0LBXI9nY7ikGyuoySMorj1wCaFo3pIchObg0o9nz8u/kDWf3woj7UWdhaxUapWg7e8L1OM5EpQ+9NJ0erYEispraY58roLhvA1cXpNo9drj2I7FPwEJVxgSQf4UDqScdnlo5os43gOjmEDcnbM5Z+hfzj0g6Y1hk5/mNq+AwxtXnyDmyQRDsMP+Agd7Hd0YUv0xUgiZS8Wzi/RHlRQc+AepNratmVxlIFIClQtohwnL3O7SOSQKLcjiWPfCm6HFt9azD8yDETmU12B+uTR1h/NYSlLbu/guf8C8DiELExVUDuAEQ4l3KvFTsFNwct4/1zHg+Qqy6JTRJ29p3DUdl1lxB+b/Z2tRWMpu2TxdX6R5rhajVB46rO06zcHNpuDLYHJ08TQ5bTNeHUFi7nn5vsOiO1OFtvhQPgWvd02diqbs+jbG+3SlypPRp0XzfTpYYu/TcLT3ecfdfP3Rwn3o0cE+TWWQyOX4/K4tHNSYAC1taEn+gUN7AXRHmZgaZE2/6LCaW1wCb5VNjUVjd4KUIHcITKq1oz2Cni2SQ/EAXqjcfG5bGLzTbNcEb3B3gYgeNBGOw8jVfmyZo0nZVj4tniI8TBBxzn0JuK424AtrGvaLciK9PZT7h1upJEP4FL/yrE6ypxM9yhLXEOQbzCf/mP7Wo46CRNHuC6zBjHNieL7NlLkUX0EjrGMMflJmljlzxAvAF5GRcXycPg5cCugFblUI6wc4o+afuHiCW+fU2Whx7+pbMU3TQtjl8rKN1y0GkC7xBj2NwA0zJbAttUNix6FSw5emyqRst55B9grtP5cYivLqmcrs4HKkmV8FKEofWTPa2cru6KLxnic5TLHZH/ZhlefuEUFlTBuC0yX+K+AnicpiJXFy0hj9YosOHg7AVrQKdgG6cHU/ygvmFYFW17kHqvT6f1+4hD96uhdDzlruprebrZJJHRcXSzgKuR2sgYJs7te5dFSb6BJFTNfVZ5EJ3s7mhvVOYNZbiJxZzelvfV1dY3YANXFpt1I+C1jPC2qPtwIcE7AT6BSyptjaVvppF3p+E59WYbGsU2ZBNWjzc//ldNuffdT+SKKeaaEeFPEM2K/gg2iWdrudjwr4PXmPtBX+0lX1av6QwRXCZEzOdLh0d/YqHty0LVMFpl+/JmqvKstl09PORAdJXS90Z0AXAcFcvHW/gvJ2XkGu3oKn3oHDw0T9LNlrwpmykbxrdO8tT8T7g9QuBcnc9iECVdSmWPZ/lxtPx5kaDVWf48OJ8sD8Xgpy05Prnz1yjuFIjzHyKSojV7i8FwTVoOwbPphXAOz9FNrew3BjhQBqgF5tv6H+j61zekmg0OYflbFeGjFw3lkwuU0NI9S/P8FsTx2/KOsLwGfoZJWRdh2o85l5mVtLL2JOWig7FoLEn4d69pMWnAzJ3PF5I2qeO6o9fvPfkHXK1B8c80UUI2T/gHC0UCdRsbJTcvue7KtL1tqgN6SmYuC1HVpaMHwz4W4i49WsZ2TE4B8OcohUHjXOd7JfgIURZZynAvtwIcUYZQxvNpOTzquaCBWOW8rGy9pq842pkCvNh6g8gqUqT5FFBmPc+yEGT1ezX+e2BCtTPNu8jXil9CyFgTBGggFjgcgzVMmBp6Vo9Bj+nz2cs2Qmg6vVB9Tc/TOE6fnWCA5x8wWbkBuViqcwIbb5DiVbHrzOszYzxP0niYbtsVWVwbalgXyhXqSZ6fxJGiMroFXA9aBtlM02saBI0w2kZjh2+q01uoGw2IxVDv4Lavh+I0V6spzFh2Lw4LR42CRt8Mg65nCAd7JPPJ22RjqbZZygaG7lI2Y1mtJTOY2UxSXd2ej2j8Ddmp6xpyNenHpzFWlB5vV1d1QIOhFsV2D7m6jblxhIWth9rVNaXRt3rpDrSS1xHxar4ExWO6wqWzbkFRNbJZVAaa7soyo1qt73BYwynu9Xda6SE5Zsj0+wcJZnuEJFjG07Kz4MZbd864hv56fQB7v/KNPHNec1P57b7aM5Diu7DC9XnRvPdHaytBLjxtZc3RYHbye9zDUYhAg78VDlXnMGWMq5tf8/+bNAZjXHTp8Xb1tjugwQ6NunuQ/ZrG8fcUlxKe9MVEYzJcNxki9CjJn23cLem+IRYBJ4Y8STckaHRf57+mj/tOYplXogKokztNq4ARWgS7l66uZwjmOI7T9aEwh6+1xHN2neYWnt1dzxBrSbJtYvBI5Fodojuxmi2VFhkju65OKJD59R9uUStbN+AJgufPIN4+lHFiafjfiaVkCLZWTpvuTqj8iPJ6xkMv7L6v5lTxLv7O/mqdvB0XONVfF+hoFf9fceZ/gvwOzWLsAdTX1BCSiNeP8jxdQrKyzdNuirMWVhlabkBOksksmqThPeY/S1bv8PUD176oG9QY3YL44S/dj5dlXMBtDJcIhd/ff3zf3zJXSZUK9t0RCQPFr0f5MloNpwORsRLiwMGcxYfbgMXtfw2GRNsYZFX1o5M0yYssQtM93PMwWcJtFPfno9dQUzxgSluQ/S+nYAsS7C0mo1tnXDoV/HD8dpjeCqjm47cPFFNp8Br8F3H9J7jtEKPRaA+5jP26HyzG0LQT/MWYSDp/NvxnnZKrr5x1i8xYtOj1ZT+MwnBc1BmkBC2CMJ7Y1heG+fi06QxMYzoZE3LRp7mRZ1rjrn3VULDs9ccD5Ef5VM6NJ/vYTseXaRzvxnmLMWW5i/yw86cqIWMnDtLOxr0YIm26UgG5pMJxgEHzcxjxo7uKHrilJkRLzqDmk/HLbREVgMRmJOj2eILftwdek5SqVX9ntKzmt1F4h8GXwaP3JYxCxZufMEzEkqOlQ1XITcZKjWevRyb65S9/+ThYuQ5S469NQ2p/23UG4Dqjz3zpJUzrvodnxwzmW3RElmCQm/xev2gTfC1uOWYvzm2ee5Mf6ZDpx4PwUAksZxqCR+AqafSLYEi5F9BEnHUXZWvQf123EhB7y1cmazwBW4mjosbmqmMY4wyc/IgwK55SHF9Gcm93uUFrLHbyp+cGUpQiKRZ8ImZ6eNVIc1Fpv+3N4dVQZHJ4TcZXjXfSbhj1GmwZHLofd96415KyGwY+SILyFpdgBSMcjC1jH6YRvXzsB6ODqkaANd01vwVhBjGpYRiioUZnKBq36XiijtaoWYOLv90KhrIAMwiziPQ+hbEEG6ywKzOx9OiMWLedDTtx43MHi8lbxUNhJ94MzYedOnfBabToJkmAR7mkukw16R8Y5bf9cddlCj+7hWj5J5Ymbdjk0RZNOU4hU6MP1cbApi+zjt2PowiXQUoRHi6BeasleQTmkqdQEYwpD+SdnM0UlkEjcbHHbGa05FOwmSTVzkhsxuRFGe9AYzLgMBc55sOuH2ziRD+C8eZ3uNEk7NT5JmQxfoO9OeeMeW6OZx3LdYrjzkqYHAD3GXPCVByoyE82GRfWN82dEns868Xg294IOxNLxRzlXMthChE3vU1qev4a0Splw17XsuxAo3JXm84K/FnCDOAUBuIX/znJLgphLjrM972RYTRVJnJsOns6kxmwTt+lPCSZXqwgY7+MY2IXp3gUotZrFkbIMXMxAhfq5roUjK6R3HJCllScquJ1ly71gbGkOVNMy5KqpIojsSRGo8ahxmhH+LBDfIDKfvEZRZEBX00X2sTmZdU9d9lubHhC79M4EU+SNLti5MYQdL3pGIEDtVMOC4bXyTE8JWvu1Pk7X8YcXTK68uVMjuD2RqwrK2d1GRYyoKDF/lyIjfltRqa93ZR982K60eWdPefNRNI1xsc6cPbq4SqDa5iMELNsYJnevZhlE/tvb+onZwUkFeATyF7v4EZy4NGNmO3OfJg5R4hJnZ4taNym5onjMlnFAOeEPiqKDN6XBTiHcYHDA9svqicoqiVngemvY7pKCymTIzloHPLhSjTHQXlUTKsODl3v+bBuzas748Vvu2F279XUjs+Z5ZwRm3llMOVp+cYl8kFnwh+tw66gIOe8fLB7SPM4a488sPsUaSntdePZ8NXOHGsTMtX4wsosQGkeoopxqpRUnJ2nHy2HCB6n7aEXLY8ynWHpDrPivZ0RaTNgtvFFm3mIyjzE2+0WLOEDXJJP7d12d5iNjz8PL1HLPWFAAXm7wIp81K+2+M+FDl3067OaH/QYIdTruIRWDSybhmEcN+xZyPWhXEaszvh8ADMVrvrUhmacg5TNzsw2ucCWUTA1z1Mli9W+7HPRIjqcuVliqK97oi1QJBloCNM/zNPMpRfrJVhTzmIeAovpLviUXMav9D4uo32PMhglRStbT9LNPUxIw8k9AiS48VhL2nyf/AhkhOpgMScXAxn/7czdfPaMOv6568qjU1/VNdjzH2VEvPi+JVDMo0wjmhfYD3spHsUTNGvWo9GeG//trkzUYcm9lX67JPJ61+z8FrS3EbVVctCQs+pjWyLFdEmxo5uFZMtR7Y8SIg1YdDZ2x74NXZe8kNwxKhdr4DgS9xpyUhA+tuLhDvGpuVl02O+02J2fHjCFwHVRCGYjbftEfI/ismVSOYVh+GJcziXk6qBZNwzJw1b8FIaVK2oN+LkPYGq2nvwiP2Lk00RX8t25d5+mz0mcRqvp6ss0GLDG9PbHvagw05KjM9acSswsbqPNNgZ8/G0XcXZSwmh5RpQQ7ORPxgt3EGSIPlwvvK6khkkRR3bQjXjV6KoPHJ4Iot202DO4UL8GrYk3nKkwbNXRozMYjd0M2Gr+Rt5pmGhEQ64Z/0xtuj1Ps3JDKir6LtEpPpbaMRlI1K87X1uzo8VEhkzLBHfpFi7H5gIy6JAN6p/3gw8qYnaHERbkv39kabkVcgHVZLB49c+jnCZkwCEKgVhHNDEBmUdroA6vOYgQDt7mKxaSXyYQOvprOa7EIc1noHyI0HZZupAsNLr2Yriuo+ovBK3JmOgreM5JGo2dKAneYMvg0P248yXBW1J0xpq8JHijcDHmGGVMhqEKHISPmuGUJiKZqWgHNWouWSa69XR+8wJmU1XYtLEBHhDTjWUWdGY83GE65jt7KUCWRPFRWTxiiFVg3A1YptnK+6U/CPPJKGDwkjfceQkoJW8nrAxX2QongoRxDJP10WqVgVxcNar5Tq9w+5u+tYmMyQCpfwnCD1yiwrBDRYbOQOyET7z6OyFyxmWaMYWIPtdMLi2ajNnfcnRT+AwRNtnrgp8sfqb59WnMufiwDfYmuz5Dls64k9eb4/IaZ6+Yy4gDYDF9mTIVfxEMpz34iKVqzhzVYjnEIKCJajTe6QjZJYaZv1vHNGwzoluHGeNM7dbxB3woTiJ0tb8us+VjlIPVD1g8CmiwX0aF/2GDBQOs+zGcJBkr9X1LixZPcJdichZhdB0+QbZLGkjW8DDn4jOC0mPEAZ64zVjvaTrOi9e+0VthptrQnFhtNB3Jms+YFZ1WbfqaFmD+ejbGcohB9etu81BHyPz17BvwjLj9OkUA8kY47YR9koM4W9+P933nbZc8qnbCksnjM6+HoEIdnw+7jCaGbHmFWZZpqx3jAvWzPskaJAeli6sfd5uBWjrmf4w1qBKLER9v21ULzDkDgwP7IVgJdu2F9cRE2maJtsO0j7RNcW6Pb/SKI+rtmf59b9InY4EfUYw2+E7pwwzKDCK9LzuvA7P07IT2y/LTGPrutOwwmopizgtTX7eLMktuwJ8lCBB2GuiiTaHcuzMxX3ZesLD07IRgaf2qx1dXjHhz95QWI8brzf6Ul+aUIHISZQVVxjlkyXEFm/Qx6t11+h/3pzT4gDadMWdQCnzAQjtxSs2BzcY8q6y4a/LjasBb83fumQNjjejqY8VXU3v8nDyC5c+07OfYHPwslmCDlowoG34dJ+SMS5YctZBZNBXzGYYhBRRqibt+1wmvfcsyQxSvr6NX8qZxkUAM19PThuzZix24d3/rf9xtY8CAHq0xqZWYDX80liI5Rb7WOZjhgEuUFLWwZik7BvHLlCa2qn7fyfgTs8ETWokv6XpB/RsvpNjS0GvHWBz630ZhSGpUETah7BayOQvDdzRROsP1UJwFq+3EzXM6rhrzvmnKTpNfNf3zT7jksn3W2Q+W2RlWOXp4gDH6BSxGCshvB2QBdb/uuvG8I0Xr6Jn4tf9oGfeSgQwTnMw0K02Leu/oiUdJ+aGdB8b58IktcnzgvtNZOpGSj1NSw02UvZ69LB+jZA1u0I44QRo9SJavEvaqG7Cs1fyoL2UICqy5svolEE/w6ArDDxUdepZJ4QLMgzXIH288MQFPMDM/HTMsH8GqjMFdlP9srD70b2IfVroRs6DMh3HeSIZECFEKa+ERT10gruNQpjMs3W8y3vtHCUqwOttEMD4qimj5SEzT51Ci+5hXaAnCcFzMe/WluC12vvALny6dgfG6zozVJqsWNS/+GbuOlD0PzaKoFIX+oiJiKc+HxTRisiUyH0Y5KinkRfwWiMvEUxWcy7SGo/GbA29RckpIitvCHsThaMoGk8k2que0RQFuy/t8mcGqNvfINWnosYc5/tmvO2+gHtJkYl6asKgifIoKcAlynKNicZ6lm/G4hB2855fHftp5/ugRpKVkU4sxFwa5S9/YYybs0S3FZMxxsdmmWYFQe0D3waCmJjGXMDgwEHtf9sJGxNJkYB2CyXpS+9DZy/SswuDQS3W/f6zC0rRzrIIGitPKwZVLg/u6BquvwKLPQYj+GOZ+brz2XpiNoktnzBq/yRjtOFr+vEhui3T5M+g9KQibCZBnUBK22Xl9SUTZTly6RHw3/wCm+THdiOFMLjw3dVTTJUhKHIJV+ybhPyWPJEnJcQSifx6F2VicB7jQnwI9pbQEj8BdPZJ0RiRdJjMMpHH8PS2QsljzFF6eDigxJbUnKfq9wJrcXbro91MesnVffn205puBVaE/PiNNBx81ngKdDsuGgjFsDNKZNxpzCv7CPxwl+bNEL6Oa9Fe1+XkUyeXEY74ORsF0zYi3OhQnLXp7km7INVNTgFFdxpZd9ND9urft73sksYRTbTTcyGzEr9xrW7o4YPlkQ07yJJeMChuPzD8NbtPdDON0bSiOqC5jiyN6aOYGSP++R+JIONVGw43MRvjfwwr0vVUkDfpLWP04jl3BnJM8iSP+9MyDfxrcpnUhwRh8x1UgNAUS02lskcQOPnA66b7skViSTLjRgKMzVPuXkEu6FtylHEk6WTKVJwklmKW58FLbY0IXFWJbvQFPEDx/BvH2oYwTnDlR1yIl6D+6ZUqEB8fez2m0RxJNb0WMxp6SD5kPqtedupVwzSfjKL8PNrzJmCEzMZ3nwVlWYm1SWabPzPsjtXZIVO3Au7MlO+3eG7M5D437snyG+hSveFOhHiBrPCrSFTiHWV6cRkV0H+XDax/udQuK1nV1C5fv31U/U4tZ/4790DbR7+9X9yla6+g+7roMBE4PcPRyEhVgTYKCh+Dpr9xB6AaKodC/8HMHZ5j2C2+I9qMC/Jd0GcXwX2DVrDhnIE4b3pCcZqrBo2RdEsfq4ZjtJ+5Q7Vcd8sBtkZEXozwtsyV3NG4zIZGDlgosrkG2gXmOeLx5ihtgMGzCG33YSn9ktO8vI5InVz483U6BA91UgQibZ2YwPvuZNyzbQkV2Gsc8KsnPXKLIFw2ozfsuF3bzUTRC811zrlp9SDhdbQvZjLWNNIeVjCcfSDlCm3ZqMED7hQe//agiAIdhciVy+4WLfvNRJYkLJLORbHtCZyyPh3vfuVKZbaIYsDOOD8bqPvGG6b6qOLrRrobs3Hzh8nLzUQH+FOZ1nPIAfveJN0D3VbXk4lNYfgRrn7/XcFmUGW+92y/cKWo+KsCzCQEGY7CfeQOxLfTWW0JTr4Fk9Rd1o4XuEXAZJeVDRPrwZAz7mUsq00KT93BpHJiBDV+QclvJOJJpqEDhpkSaAiiqnzmD977zhq2bqBe2SrZ7CYrHlKtesN+5y8o20aBNSpiKKrW4revTCGnqN+AK3F4bFdOAGD6B7PUObni7g/3MZROmhd5upMvUiDYk3UayJ+lmpoO3GefPYVzwVRxlFy3UBr30MJWI+kELmdhqWmnLrbqjQnxxW8nwoFua4nK7BUv4AJfk3kyVeBBhJWovw4/fRxtTfver2kd/uJmlzblbW9rDCjttvEww0l3Tu4h7MaM+SlaLfNcb53uUwSjpCkycpJt7mESCddHpJMFL2k+B7z/KiPzyLYG8o5v9zMOBbWE3O/pTolCWqv+b76N+RzFCepgY82Vva+VEHVHiQDfWwYZub4WXNk4m+NhyTV3RRpd16uYG+6juoVJn2iR3Q1Wm/cRVY9qvKkMoBNl1Brn3Yeob1wjafVYMQvlhDAahvvEGIZ/zhdZlmLQVmYzpj5KB9MzHpLFoECn8uoUO/D+ytNyKBqk/SkaqWyhG6tyJB+N0n3ij4K9K6GcvSD9MovioLB7xK0J1sgptqPLmPCzkPRTY1YWNBmjUv/PGqz9pXl2/4Rwdbe5R4d2VbSa7vLItFVj8AR+QRp2tFFjwm/Gw4LfUxEIysnw0vWUU8DD1TbicWrct0vJryj2+qW/CQarPqjs5eEYEX6cwKXLxgnFbcW/pvIYal3aBqaX7JLqoaxlVmoaCFWM/y0bSWrde4fHBcL3vvPF6TZRryNSn5qwe852/bkwT5Xz2y0Fy5rTfhD+v/VYq2+2wROHQiDtsw7XmDpuZDi5S2UQNtdDQU9OEBcU4qy9oyecDQWNDdDTw0ENA/T5Elx8aDMp85b4T0Q30h8JDyIerWiiGrBop36N5lAkp0qKkK8EypKH7xsW++6ycrliodlHf+FMUaypU3zLxINQ33iDUZ5VCARKAbh0yoTNswlUwBq1UdyUEA5CL3D33Dbz3nXtnYpsoHyxT7iNP/Tv/gTLVeLTqqjgMxWX7if9S3HzVQV3srsB+FhKibavk5fofDsprxR2b19AABcXY6kE1TGvcZLocGxu3Hd/Yxm2qj4h8eOWg6sskkzh7eHlkPnMvi0wL5cP0ZhvBNc8s1X3iP0w3X5UvxySH4x3YbGO+9B+04L8f9xpp3Pi/gALdm9msO9z7P6+hyBrAa6tj51CgwWkjsXoYDd/PDsox97ENZBaqXlPl4kd5mYEfAK4fedzc+85feKaJ3oCnEG3rnD/TwyaSYalWipF76TUHw/a+88bsNVGJ4tdkKTl66K9cQUw3UBq4+mn6OEatfhO+IavfSmtk8az2vovH1J1VYbquwdDCllxXJ1FjA2cUmQjlNlM5p2gL0+YtWoLBsInMnUl75ObdUDzwoIXs+VF32BuAm63Efmf9Bvx7JdtGNclZmucIeCweddiEO8mDVkqh2aUy44jL7iNfUHbfNcah05Rxx6IbiMaj22h6i4p9OgctZL6jdSOgfhRojNkSf5BhE5lVfHG03cYQrO7Suj00wELhFcJvpocN3UcfITGTD1rooVE3V2PAOJCpfN9kjSWOcAumn74zCuMGpsJN2lqGHNtRG7vWt1CBl6CdDKN2OQ3dDXR8UjRdUxZdO20sWtd1vagB/egBplqu/qQMUqkN52TQRBQEwLbSuG21iUm4V6z2q+he1TbQ8DYXD8V8Ffmc6w7VS2fAv7N134XXpq6JHldzYo9FzM1pKuFxTmsTjFRoaIw9GJAKrKPNz8PopUUXTEX1aS3Tsg79OMAukLIf2YVQEEdtsT0lEVsVFJ0IrA8s+bpTQ0evqeeF3zrYpPCi9LoZkQbemU8HIye5IV85b150uonJ5Il7QqEs+IuBII13I6D0wtfMJ4wLdzEM2BvOmWZPLaKZ46lPL/fImcv0oZNRuuPYBiE2GR00WNHKCwq0I62K/FtUEPnE0U0ckRz0YKMW236CeERzEpm4tkUbUDcklN9QIhHYkL5KGPDj9Zh+3Fg90lsegmdOen1xlRHdb+Kf3N4lnPQTRU3ak9ibOjGhbEPvqzQi6e29obVPLG5rjUN8+A0bq4887nEnJVxggGEABJ2IuyhbY3dtrYmoG3tZxzlMwzGMcQGjFrJkEnpNw02BFg85EN0k/elAi6ketA25BXqB8gwAUfS7+TQ0UdVSNWXYKISq0o8MJz2FUd8WKksT2ssEF3P0Fl47Mdq8YGeCuiyImVVm2MjlSpMRhFpbkM1EPy9awBzC+S29IM7240ZsV917n7yT39iJ1eTzExcNyeDhPxvy28BQyboP2vhf8f67RiXNROkKzMmUPXtIlBudbmKi+pkgCFGiLA/cntxpGX4MOz1ilUenm6c1n3R6mKhlyUbhtvO/WXjPb9WJIssV4YMrmNc5o33D7Rly6wgnid8g+GSZ7CJuT8/8MNNJax5ejZir3ykkX/EO9cG3kBNjwkj9Tv51lYmmhZOZhnsflXcIySecFDwMEFlOHQstlvEQqB/CZZtI3kHCJ7ycOxW3yFLpiGFw3/+HAHvNQk2YeHPJO4iJFdMnJWl2E9alN1q0+ZmGs8RpFXZqhimZWgDeSO95uGjsLkUPiXGYm/+psg5LszpJoIinWdou2LSJ95iiR1hOmsektQeWBpcJ2wY914QTI2gRYEo0VKBxeWe6yWkTfFUPZlcPVxlcw0Rivxo09f8yZ6BMOpDMpp0T08u0k6whJw1etXSS9Hbjky1MKbeg8+AJZ0Oru5LEYfY+mlhxQj45LEEyPj5oVUI9DxNcT4naJCzsEsw2bLsAXqbFaEL2cCpae3hnYhTOxLBtCIOoIFskPREezaF90tT8MGgajBvGnYJeOkvaNiacC3GfkBY+zuC8KfJs3+ORrOYWbvNgHDPNlAgSjaonR9Ex2DTJk4fS0DQzglpcKCXJTwWZW7m3TAswkmujel70JkQDKpv0UQJbkMjRN7fKBzdlYSm0kadqpjtgGF0ktfGKW4dzWhjEUNEQ6K9BJkNivxW3VhIloIZDxjymRJKAWPfWrA1ihAu1RvJmekZN0i8HmWL1Ia7TO9gW3bXpZDNaW80pA0I8Qbzk22QqZEm1d3WKLdh0fN7UmS1/U8RJzD3IYS6ZLq3uyimQ0q6nPYrxkcKUpCP3NaWD+aDH1J1YGZARJ0UDugbk8Btch3XVfUfk22mFADfr/EI4rPaUyuGMMik6cJmUsDLg/DSv9mq85jE/+lke6DmnqTagDDfgN5TYCp3jDfoVFCqlXVQdwX7Fb6PNNgYdYPGa91p6Qn3E1W7rPizYqO4hyYKWklcD5/jxQT0L0l1cqsKFfInSOWzkX80MT2qbo0MaMcZppV5h81ixQRER0pX61Q+5pACImt5es2AEM+VMOor5ZUosSV7QFUUE9NJtFCgzdUs6lLn1SIa9hx39rmwNUrqsVRtnRKdZTbITabCyHUu184j6yPu2KVQjjescNhKjbB/X2S+nQ3p2P/oLX6YPRK2Qdm6HEGJLlvqLgSItCO5tajSC3LkdQiopU02RrFqS9MjT6xiCm3QqSBFoRpWgzKeOFPHRSBrAbScmzzZjAFO6ivTiV6ayJVTGDL0WIVY9HHm8SlqLzmlY7IXL7yC5vTl7J/PqdjFQpHW4PE1NPe+a81K1dl7XWUxHWxtMPAf9Jn4JH9Q16/p50iY6/CVX2WEj/1fZ8KQ21eQW10jxeYxysPoBi0dqhCHhqi7eyGH69uvlka7CWnj2E8Hs2w6+eBr4HTwRwu0p3PFahQg9Tc03et2154ft5VcuTDU9bR1DhUCkmgQQiHQNxq4ft7yiRWDgsGKiVBWStg+hGEkqRBIgOiUf/UyLcl+oO/nljmmmpilGKd4TvRZ+ie6X67xtovo8xWLTtTYXHVwxlWxDT4hzew7OV2ldUNuLUBMhqbzy9Rvu0p2PqXAqFXiCliFEHbdyK+kuL8jqSr5wI/Oa+d3N45HMlJhVHHHclmEON07p3Fp2yyriOtgKNba3sK3/HR46D2S/wq8izFLWXHkH9BJWKaptXMt7Rb1iDxMk2xnixiE2x+RTITERCNv6txSMPQ38itALTr1qjuTQ7SvhF1GJ7YpxVFWz5bCG7lGa9a9tzhu2kvTiIoEFjGLJ7UHWwffNgV8vuz56FDWw3SejUSmGQ6nnRdg3ELlSSPxrl14Rc4us5l3p7sWgjPdw5mTNJUc5v8x4daTLa4eL4IhgeJ4S2dHFbRfi1BqHbBW5wcnsk+dpNZvq7guJejps5F8vHVSor3oKi8/bMG7ce00W+RQKWoZ8rm+H7LGwqGK9TfmNlGRhhpsoez17WT5GyRrcoKnt6q1ztC5lJ9mksAXg6wnhF3dnFTC6Jn2ldXFLzjtOAvlDm3q29Y6RPSw2v2BrwXOIV/WREMKpU1/RI6k/L4TAO/G5352niVuefnEO+VJC0lry0mztcM0druepz20RaFpUfvgavcTEujvlz2K6FhX0pdDzQ9hWTBgLkvZOYj7oTY1oQoJxDTOGLtPQnbySNROOIY6qdGVytW84v3UIvXMw3NDHVlpU3SbeBT4hFeMSqYxoly/Os3Qjmw9Z8xATwo7Xsy6xn3xPxV1qMBFU412fhovNNs1w6gd0GwBKzUXSOqTOwgzLgOh98eBjazIdktYhp4MZtudGG2I6TsE2Tis7XgtaNBmctt4J4cDohuWAoT86T8lxtPx5kSA9aflT+1xR9gkhRQSDMoCEbYJNk+QdQtXF/3PEtFN0CZISv3PUBhH8J7eUC6eZTL1PSo65g/5ZMiHsWAMI9Cf3A7hXjH2BrbAnaZIXWQTxdQ9pG+0OaerZ3aWLYRF3znntC7Z6Y1oUv2N5V1DcvmJiVcF6D9NOV6LVmEmquZwoo6K3k04JjvA6STfk0NJkFKqLKFzNAWJ4rmPwp2Awv3ud2C6MTj5dbTs58tphehOQjStBGq4+1UVU1tIBYnh+YvCnT0/6d68T25XblE9X206OvHY5zwnIbqNtv2PfbM31ZzpJw4jtoYbnqx4VffNI98XzJFMBzqqZ65pqkKEXRT3RFNQq8w14guD5M4i3D2Wc4ILiutqToL8kn4r/sUbQ2IRkDq8fnEbhlon5YDTpbE9N0sX07s70WbGbcmrtoI7MufrL5zzJ6vyOvYb+r/r+Sf/tQwUETzxaZZC13377gE15m6j+Af1ZvapdpisQ5+TX3z7clKj3BlR/nYIcrjsQvyGYCVjiMTugTZuL5AFXU9yCjBBAY9Q0aT63hoYiWkVFdJQVEOePR5+XaC+hK9T7d8RFEL9w3YPVRXJVFtuyQCSDzX3MvPv+9kE+/m8fBjj/Vics9EECQhMiEsBVclzCeNXifR7Fee/oE4E4QbP/B0C/V2uJtmYB1q8tpK9pogmonr5TsAXJCm25O7DZxghYfpXcRk/ABrdvOfgC1tHyFf3+BInHowiIeiHYaf/tFEbrLNrkNYyuP/oT8fBq8/J//j/DaEttLlkKAA== + + + dbo + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/202002172101120_PictureMediaRename.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202002172101120_PictureMediaRename.Designer.cs new file mode 100644 index 0000000000..287c106f5c --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002172101120_PictureMediaRename.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 PictureMediaRename : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(PictureMediaRename)); + + string IMigrationMetadata.Id + { + get { return "202002172101120_PictureMediaRename"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002172101120_PictureMediaRename.cs b/src/Libraries/SmartStore.Data/Migrations/202002172101120_PictureMediaRename.cs new file mode 100644 index 0000000000..e0d19a569e --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002172101120_PictureMediaRename.cs @@ -0,0 +1,171 @@ +namespace SmartStore.Data.Migrations +{ + using System; + using System.Data.Entity.Migrations; + using System.Data.Entity.Migrations.Builders; + using System.Data.Entity.Migrations.Model; + using SmartStore.Core.Data; + + public partial class PictureMediaRename : DbMigration + { + private void RenameTableEx(string name, string newName) + { + RenameTable(name: name, newName: newName); + } + + private void RenameColumnEx(string table, string name, string newName) + { + RenameColumn(table, name, newName); + } + + private void RenameIndexEx(string table, string name, string newName) + { + RenameIndex(table, name, newName); + } + + private void RenameColumnCe( + string table, + string name, + string newName, + Func columnAction, + string[] foreignKey = null, + string[] newForeignKey = null, + bool dropIndex = false, + bool createIndex = false) + { + var nativeTableName = table.StartsWith("dbo.", StringComparison.OrdinalIgnoreCase) + ? table.Substring(4) + : table; + + AddColumn(table, newName, columnAction); + + Sql($"UPDATE [{nativeTableName}] SET [{newName}] = [{name}]"); + + if (foreignKey != null) + { + // Attention, different methods. 'The argument 'principalTable' cannot be null, empty or contain only white space.' + if (foreignKey.Length == 1) + { + // Delete by key name. + DropForeignKey(table, foreignKey[0]); + } + else + { + // Delete by EF generated key name. + DropForeignKey(table, foreignKey[0], foreignKey[1]); + } + } + + // Add new foreign key after DropForeignKey to allow using old key name. + if (newForeignKey != null) + { + AddForeignKey(table, newName, newForeignKey[0], newForeignKey[1], newForeignKey[2].ToBool(), newForeignKey[3]); + } + + if (dropIndex) + { + DropIndex(table, new[] { name }); + } + + if (createIndex) + { + CreateIndex(table, newName); + } + + DropColumn(table, name); + } + + public override void Up() + { + RenameTableEx(name: "dbo.Picture", newName: "MediaFile"); + RenameTableEx(name: "dbo.Product_Picture_Mapping", newName: "Product_MediaFile_Mapping"); + + if (DataSettings.Current.IsSqlServer) + { + RenameColumnEx(table: "dbo.BlogPost", name: "PictureId", newName: "MediaFileId"); + RenameColumnEx(table: "dbo.BlogPost", name: "PreviewPictureId", newName: "PreviewMediaFileId"); + RenameColumnEx(table: "dbo.Category", name: "PictureId", newName: "MediaFileId"); + RenameColumnEx(table: "dbo.MediaFile", name: "SeoFilename", newName: "Name"); + RenameColumnEx(table: "dbo.Product_MediaFile_Mapping", name: "PictureId", newName: "MediaFileId"); + RenameColumnEx(table: "dbo.Manufacturer", name: "PictureId", newName: "MediaFileId"); + RenameColumnEx(table: "dbo.SpecificationAttributeOption", name: "PictureId", newName: "MediaFileId"); + RenameColumnEx(table: "dbo.ProductVariantAttributeCombination", name: "AssignedPictureIds", newName: "AssignedMediaFileIds"); + RenameColumnEx(table: "dbo.ProductAttributeOption", name: "PictureId", newName: "MediaFileId"); + RenameColumnEx(table: "dbo.ProductVariantAttributeValue", name: "PictureId", newName: "MediaFileId"); + RenameColumnEx(table: "dbo.News", name: "PictureId", newName: "MediaFileId"); + RenameColumnEx(table: "dbo.News", name: "PreviewPictureId", newName: "PreviewMediaFileId"); + RenameColumnEx(table: "dbo.Store", name: "LogoPictureId", newName: "LogoMediaFileId"); + + RenameIndexEx(table: "dbo.Category", name: "IX_PictureId", newName: "IX_MediaFileId"); + //RenameIndexEx(table: "dbo.Product_MediaFile_Mapping", name: "IX_PictureId", newName: "IX_MediaFileId"); + RenameIndexEx(table: "dbo.Manufacturer", name: "IX_PictureId", newName: "IX_MediaFileId"); + } + else + { + // SQL Server CE does not support direct renaming of columns. + // The goal is an identical replica of the SQL-Server installation. + RenameColumnCe("dbo.BlogPost", "PictureId", "MediaFileId", c => c.Int()); + RenameColumnCe("dbo.BlogPost", "PreviewPictureId", "PreviewMediaFileId", c => c.Int()); + + RenameColumnCe("dbo.Category", "PictureId", "MediaFileId", c => c.Int(), + new[] { "PictureId", "dbo.Picture" }, + new[] { "dbo.MediaFile", "Id", "false", "FK_dbo.Category_dbo.Picture_PictureId" }, + true, + true); + + RenameColumnCe("dbo.MediaFile", "SeoFilename", "Name", c => c.String(maxLength: 300)); + + RenameColumnCe("dbo.Product_MediaFile_Mapping", "PictureId", "MediaFileId", c => c.Int(nullable: false), + new[] { "FK_dbo.Product_Picture_Mapping_dbo.Picture_PictureId" }, + new[] { "dbo.MediaFile", "Id", "true", "FK_dbo.Product_Picture_Mapping_dbo.Picture_PictureId" }, + true, + true); + + RenameColumnCe("dbo.Manufacturer", "PictureId", "MediaFileId", c => c.Int(), + new[] { "PictureId", "dbo.Picture" }, + new[] { "dbo.MediaFile", "Id", "false", "FK_dbo.Manufacturer_dbo.Picture_PictureId" }, + true, + true); + + RenameColumnCe("dbo.SpecificationAttributeOption", "PictureId", "MediaFileId", c => c.Int(nullable: false)); + RenameColumnCe("dbo.ProductVariantAttributeCombination", "AssignedPictureIds", "AssignedMediaFileIds", c => c.String(maxLength: 1000)); + RenameColumnCe("dbo.ProductAttributeOption", "PictureId", "MediaFileId", c => c.Int(nullable: false)); + RenameColumnCe("dbo.ProductVariantAttributeValue", "PictureId", "MediaFileId", c => c.Int(nullable: false)); + RenameColumnCe("dbo.News", "PictureId", "MediaFileId", c => c.Int()); + RenameColumnCe("dbo.News", "PreviewPictureId", "PreviewMediaFileId", c => c.Int()); + RenameColumnCe("dbo.Store", "LogoPictureId", "LogoMediaFileId", c => c.Int(nullable: false)); + } + } + + public override void Down() + { + if (DataSettings.Current.IsSqlServer) + { + RenameIndexEx(table: "dbo.Manufacturer", name: "IX_MediaFileId", newName: "IX_PictureId"); + RenameIndexEx(table: "dbo.Product_MediaFile_Mapping", name: "IX_MediaFileId", newName: "IX_PictureId"); + RenameIndexEx(table: "dbo.Category", name: "IX_MediaFileId", newName: "IX_PictureId"); + + RenameColumnEx(table: "dbo.Store", name: "LogoMediaFileId", newName: "LogoPictureId"); + RenameColumnEx(table: "dbo.News", name: "PreviewMediaFileId", newName: "PreviewPictureId"); + RenameColumnEx(table: "dbo.News", name: "MediaFileId", newName: "PictureId"); + RenameColumnEx(table: "dbo.ProductVariantAttributeValue", name: "MediaFileId", newName: "PictureId"); + RenameColumnEx(table: "dbo.ProductAttributeOption", name: "MediaFileId", newName: "PictureId"); + RenameColumnEx(table: "dbo.ProductVariantAttributeCombination", name: "AssignedMediaFileIds", newName: "AssignedPictureIds"); + RenameColumnEx(table: "dbo.SpecificationAttributeOption", name: "MediaFileId", newName: "PictureId"); + RenameColumnEx(table: "dbo.Manufacturer", name: "MediaFileId", newName: "PictureId"); + RenameColumnEx(table: "dbo.Product_MediaFile_Mapping", name: "MediaFileId", newName: "PictureId"); + RenameColumnEx(table: "dbo.MediaFile", name: "Name", newName: "SeoFilename"); + RenameColumnEx(table: "dbo.Category", name: "MediaFileId", newName: "PictureId"); + RenameColumnEx(table: "dbo.BlogPost", name: "PreviewMediaFileId", newName: "PreviewPictureId"); + RenameColumnEx(table: "dbo.BlogPost", name: "MediaFileId", newName: "PictureId"); + } + else + { + // Come on, give me a break. + } + + //RenameTableEx(name: "dbo.Product_MediaFile_Mapping", newName: "Product_Picture_Mapping"); + RenameTableEx(name: "dbo.MediaFile", newName: "Picture"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002172101120_PictureMediaRename.resx b/src/Libraries/SmartStore.Data/Migrations/202002172101120_PictureMediaRename.resx new file mode 100644 index 0000000000..84b2630cc0 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002172101120_PictureMediaRename.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/202002180228163_PictureMediaRename1.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202002180228163_PictureMediaRename1.Designer.cs new file mode 100644 index 0000000000..e5997cafc4 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002180228163_PictureMediaRename1.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 PictureMediaRename1 : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(PictureMediaRename1)); + + string IMigrationMetadata.Id + { + get { return "202002180228163_PictureMediaRename1"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002180228163_PictureMediaRename1.cs b/src/Libraries/SmartStore.Data/Migrations/202002180228163_PictureMediaRename1.cs new file mode 100644 index 0000000000..fb3fc9139d --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002180228163_PictureMediaRename1.cs @@ -0,0 +1,16 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class PictureMediaRename1 : DbMigration + { + public override void Up() + { + // A cumbersome but necessary step to prevent EF from dropping renamed tables. + } + + public override void Down() + { + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002180228163_PictureMediaRename1.resx b/src/Libraries/SmartStore.Data/Migrations/202002180228163_PictureMediaRename1.resx new file mode 100644 index 0000000000..03222476be --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002180228163_PictureMediaRename1.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/202002191252074_RemoveDiscountRequirements.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202002191252074_RemoveDiscountRequirements.Designer.cs new file mode 100644 index 0000000000..7984e35545 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002191252074_RemoveDiscountRequirements.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 RemoveDiscountRequirements : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(RemoveDiscountRequirements)); + + string IMigrationMetadata.Id + { + get { return "202002191252074_RemoveDiscountRequirements"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002191252074_RemoveDiscountRequirements.cs b/src/Libraries/SmartStore.Data/Migrations/202002191252074_RemoveDiscountRequirements.cs new file mode 100644 index 0000000000..72b39d7843 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002191252074_RemoveDiscountRequirements.cs @@ -0,0 +1,122 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using System.Linq; + using System.Web.Hosting; + using SmartStore.Core.Data; + using SmartStore.Core.Domain.DataExchange; + using SmartStore.Core.Domain.Localization; + using SmartStore.Data.Setup; + + public partial class RemoveDiscountRequirements : DbMigration, IDataSeeder + { + public override void Up() + { + //DropForeignKey("dbo.DiscountRequirement", "DiscountId", "dbo.Discount"); + //DropIndex("dbo.DiscountRequirement", new[] { "DiscountId" }); + //DropTable("dbo.DiscountRequirement"); + } + + public override void Down() + { + //CreateTable( + // "dbo.DiscountRequirement", + // c => new + // { + // Id = c.Int(nullable: false, identity: true), + // DiscountId = c.Int(nullable: false), + // DiscountRequirementRuleSystemName = c.String(), + // SpentAmount = c.Decimal(nullable: false, precision: 18, scale: 4), + // BillingCountryId = c.Int(nullable: false), + // ShippingCountryId = c.Int(nullable: false), + // RestrictedToCustomerRoleId = c.Int(), + // RestrictedProductIds = c.String(), + // RestrictedPaymentMethods = c.String(), + // RestrictedShippingOptions = c.String(), + // RestrictedToStoreId = c.Int(), + // ExtraData = c.String(), + // }) + // .PrimaryKey(t => t.Id); + + //CreateIndex("dbo.DiscountRequirement", "DiscountId"); + //AddForeignKey("dbo.DiscountRequirement", "DiscountId", "dbo.Discount", "Id", cascadeDelete: true); + } + + public bool RollbackOnFailure => false; + + public void Seed(SmartObjectContext context) + { + if (!HostingEnvironment.IsHosted || !DataSettings.Current.IsSqlServer) + { + return; + } + + // Remove discount requirement. + // Data has been migrated through 202001301039020_DiscountRuleSets.cs. + context.ExecuteSqlCommandSafe("ALTER TABLE [dbo].[DiscountRequirement] DROP CONSTRAINT [FK_dbo.DiscountRequirement_dbo.Discount_DiscountId]"); + context.ExecuteSqlCommandSafe("DROP INDEX [IX_DiscountId] ON [dbo].[DiscountRequirement]"); + context.ExecuteSqlCommandSafe("DROP TABLE [dbo].[DiscountRequirement]"); + + // Remove data of obsolete filter plugins. + var syncMappingsSet = context.Set(); + var obsoleteSyncMappings = syncMappingsSet.Where(x => x.ContextName == "SmartStore.ShippingFilter" || x.ContextName == "SmartStore.PaymentFilter").ToList(); + if (obsoleteSyncMappings.Any()) + { + syncMappingsSet.RemoveRange(obsoleteSyncMappings); + context.SaveChanges(); + } + + // Remove obsolete string resources. + var resourceRanges = new string[] + { + "Plugins.Payment.PaymentFilter.", + "Plugins.Shipping.ShippingFilter.", + "Plugins.DiscountRequirement.MustBeAssignedToCustomerRole", + "Plugins.DiscountRules.", + "Plugins.SmartStore.DiscountRules.", + "Plugins.FriendlyName.DiscountRequirement.", + "Plugins.FriendlyName.SmartStore.DiscountRules.", + "Admin.Promotions.Discounts.Requirements." + }; + + var resourceNames = new string[] + { + "Plugins.FriendlyName.SmartStore.PaymentFilter", + "Plugins.FriendlyName.SmartStore.ShippingFilter", + "Plugins.FriendlyName.SmartStore.DiscountRules", + "Plugins.Description.SmartStore.DiscountRules", + "Plugins.Description.SmartStore.PaymentFilter", + "Plugins.Description.SmartStore.ShippingFilter", + "Enums.SmartStore.PaymentFilter.PaymentFilterCountryContext.BillingAddress", + "Enums.SmartStore.PaymentFilter.PaymentFilterCountryContext.ShippingAddress", + "Enums.SmartStore.PaymentFilter.PaymentFilterAmountContext.SubtotalAmount", + "Enums.SmartStore.PaymentFilter.PaymentFilterAmountContext.TotalAmount", + "Enums.SmartStore.ShippingFilter.ShippingFilterCountryContext.BillingAddress", + "Enums.SmartStore.ShippingFilter.ShippingFilterCountryContext.ShippingAddress", + "Admin.Common.Restrictions", + "Admin.Configuration.Restriction.SaveBeforeEdit", + "Admin.Configuration.Shipping.Methods.RestrictionNote", + "Admin.Configuration.Payment.Methods.RestrictionNote" + }; + + var resourceSet = context.Set(); + + foreach (var range in resourceRanges) + { + var resources = resourceSet.Where(x => x.ResourceName.StartsWith(range)).ToList(); + if (resources.Any()) + { + resourceSet.RemoveRange(resources); + context.SaveChanges(); + } + } + + var moreResources = resourceSet.Where(x => resourceNames.Contains(x.ResourceName)).ToList(); + if (moreResources.Any()) + { + resourceSet.RemoveRange(moreResources); + context.SaveChanges(); + } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002191252074_RemoveDiscountRequirements.resx b/src/Libraries/SmartStore.Data/Migrations/202002191252074_RemoveDiscountRequirements.resx new file mode 100644 index 0000000000..8143dabe8a --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002191252074_RemoveDiscountRequirements.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/202002211011108_GenericMessageTemplate.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202002211011108_GenericMessageTemplate.Designer.cs new file mode 100644 index 0000000000..3e65742e14 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002211011108_GenericMessageTemplate.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 GenericMessageTemplate : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(GenericMessageTemplate)); + + string IMigrationMetadata.Id + { + get { return "202002211011108_GenericMessageTemplate"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002211011108_GenericMessageTemplate.cs b/src/Libraries/SmartStore.Data/Migrations/202002211011108_GenericMessageTemplate.cs new file mode 100644 index 0000000000..b3e70a5953 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002211011108_GenericMessageTemplate.cs @@ -0,0 +1,60 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using System.Linq; + using SmartStore.Core.Data; + using SmartStore.Core.Domain.Messages; + using SmartStore.Data.Setup; + + public partial class GenericMessageTemplate : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + } + + public override void Down() + { + } + + public bool RollbackOnFailure => false; + + public void Seed(SmartObjectContext context) + { + if (!DataSettings.DatabaseIsInstalled()) + { + return; + } + + context.MigrateLocaleResources(MigrateLocaleResources); + + var defaultEmailAccount = context.Set().FirstOrDefault(x => x.Email != null); + var table = context.Set(); + + table.Add(new MessageTemplate + { + IsActive = true, + Name = "System.Generic", + To = "{{ Generic.Email }}", + ReplyTo = "{{ Generic.ReplyTo }}", + Subject = "{{ Generic.Subject }}", + EmailAccountId = (defaultEmailAccount?.Id).GetValueOrDefault(), + Body = @"{% extends 'master' %} + {% block 'body' %} + {{ Generic.Body }} + {% endblock %}" + }); + + context.SaveChanges(); + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("Admin.Customers.Customers.SendEmail.EmailNotValid", + "No valid e-mail address is stored for the customer.", + "Fr den Kunden ist keine gltige E-Mail-Adresse hinterlegt."); + + builder.Delete("Admin.Customers.Customers.SendPM.Message.Hint"); + builder.Delete("Admin.Customers.Customers.SendPM.Subject.Hint"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002211011108_GenericMessageTemplate.resx b/src/Libraries/SmartStore.Data/Migrations/202002211011108_GenericMessageTemplate.resx new file mode 100644 index 0000000000..8143dabe8a --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002211011108_GenericMessageTemplate.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/202002241354543_ProductCondition.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202002241354543_ProductCondition.Designer.cs new file mode 100644 index 0000000000..db117a9ec5 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002241354543_ProductCondition.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 ProductCondition : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(ProductCondition)); + + string IMigrationMetadata.Id + { + get { return "202002241354543_ProductCondition"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002241354543_ProductCondition.cs b/src/Libraries/SmartStore.Data/Migrations/202002241354543_ProductCondition.cs new file mode 100644 index 0000000000..e5ee0c0891 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002241354543_ProductCondition.cs @@ -0,0 +1,19 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class ProductCondition : DbMigration + { + public override void Up() + { + AddColumn("dbo.Product", "Condition", c => c.Int(nullable: false)); + CreateIndex("dbo.ProductAttribute", "DisplayOrder"); + } + + public override void Down() + { + DropIndex("dbo.ProductAttribute", new[] { "DisplayOrder" }); + DropColumn("dbo.Product", "Condition"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002241354543_ProductCondition.resx b/src/Libraries/SmartStore.Data/Migrations/202002241354543_ProductCondition.resx new file mode 100644 index 0000000000..dc59d0c6b7 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002241354543_ProductCondition.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 + + + H4sIAAAAAAAEAOy9224dObIo+D7A/IPhp5nBPnbZ3Q3s06iaA0mWbOHYllqS7bP7RUjloqRs58pcnRdL6l+bh/mk+YUh88pL8M68SL1QgEsrGQySwWAwGAxG/H//z//7+/943KavfqGiTPLsj9fv3vz2+hXK4nyTZHd/vK6r2//2n6//x//9v/9vvx9vto+vvvdwfyJwuGZW/vH6vqp2f337tozv0TYq32yTuMjL/LZ6E+fbt9Emf/v+t9/++9t3794ijOI1xvXq1e8XdVYlW9T8wD+P8ixGu6qO0i/5BqVl9x2XXDZYX32NtqjcRTH64/XlNiqqyyov0JsPURW9fnWQJhHuxiVKb1+/irIsr6IKd/Kv30p0WRV5dne5wx+i9OpphzDcbZSWqOv8X0dw03H89p6M4+1YsUcV12WVby0RvvtTR5i3fHUn8r4eCIdJd4xJXD2RUTfk++P1Vb5L4tev+Jb+epQWBIoh7VFDXwyWZG+aemX7v/94xQH9x8AUf3rzHv/323+8OqrTqi7QHxmqqyJK/+PVeX2TJvH/RE9X+U+U/ZHVaUr3FPcVlzEf8KfzIt+honq6QLdd/083r1+9Zeu95SsO1ag67eBOs+pP71+/+oobj25SNDACRYhmVB9RhoqoQpvzqKpQkREcqCGl0DrX1uVTWaEt+btvE/MfXkevX32JHj+j7K66/+M1/vP1q5PkEW36L10/vmUJXna4UlXUSNfUadk21k1p29phnqcoyoAxqpF9qrbpSKfJ+nyYb56OyvIojcpyegJlcVpv0Gl2mWAyRTtfGp2W57jbD3mxwQUVijF/+KLsEU5Oi6ukSqdnycv7vKh0Tf3ltyBzi8W6opH3f/lLIHadnGhfUBVhEUbYYPo1QRr7gMq4SHbtDjNDe/Pw3udki5f55ipvJHjpuzIvULZBxUH5I9ncocoXW4vl73k2PR3apn4U0Q5rVBXetYS+m9THK/mBmTe/kR9i5kZFAHlZJHnRbMPqDd1AGF5Fd9PLw/rmH3ifuMoP4jTA7kNUqPLeloq/vx21QLVuGD0eYaXnLi+eXDTE6PENhWGvJMrb0qiHf/7NbIe0ZKAPSblLo6czshJt1o8x/1yiqmrGYs07WFLdJnd10UC/6fDsOciZg95Pw0Hfo7QOsYFZNtvQSk9dJ579nMdRmvwLbfo2Hbi3w9Eyr4Bwz8byttrpsJtaQPOLsrs6urNkEQAPmTqE6fOxyOvd/AJ6aH+ppuda31+jX8ldw0CSmXz96gKlDUB5n+xau564sq5H8JMi317kKbSgB6jry7wuYjK+XAt6FRWN1u8mU4ZueYqSDs9egsjb0myE7yZaLt3MdFRX7sRTtI9r/7NGlyg/wjhUrQc4w5yk0d3pFg/2JEmRhtxBTDsXlfdZKbAtwPHgFU4FV8rMRnS3k3GBykbGlXIBykHKZagMcJCNjBiVQvdC110741AHUdA4nHsJq5d1vtpVT+tlji596wsdYU5LsrrO0/ouyQQhoqt6ldcxJHzCaVX+QgHUrbQixEkonKNim5RkcV6guLk7sRYIlyiuiRnxDY9rLwjkbRldbZreuzhvd9SMYUb7Eu12uAvwfgeCXovsM/K4WQ1hBzSs5rUVgm0EYv0R4Z7/5W0dpGn+4K1Kckzhu6seNfOPCjKH1riki4zGCi4tGuBatiTHdWUALiwqkzrQijIUHt3an1Ns8HukpbRxEhvsTNob5rvqJTPjeykhb2um/ZE/pBcIXWIWbjcFPxF1FT0eP6LtzvvSGyPqDrwEDzcFGlkbV8kv77vn3kOqZX4/XEG1nwmUnQkkMi+vbKS4m46TY5lgL6RItbL5dy+Y5G2FOscH8m10Wn6dm9TkfhvBDH7EjeYs+4RXzXlznPbD1migH2tUVuVV/j2vvBF62COFO1q87j5gxvxWDb6o5OdVstXWPc42jjV97bxuJhMiaUATCVMgqntMKSQolVsBrn2QlQ9YnEk71ZZft2KU7RZVJB5fuXK/c+qAzEuStyj28lwhozCVnqcs/1pvb1BxdkskWOk3gCkuVNrl47fEoLUPLUGbPmFyNcbUuHUUlnSQhrqmFyPbWQkYKBtksN5ygkbsJS1oRMFkxqvDqERdB2gNu3er1a7OlkzhrDL0bE4z+xDb6jjFyywRxCSx3yXkbfU0+lgnQ6vt37YuByVuV3f7H+L6/xjPcjp5KwYPb8I2dJIX26jy3bB7bJdRWk3e9YPNNsmO8u2WekQw4Wu2YHang9vbJE3wcvGldhgr1AeUogBPxXpj1kEc5zXwqmMKe1YYPvocldXp7mCzwUc01QsnU2ctjcQrEJGUZxl4nrR29Cqrz/ldkrkeUHH9houwqJaisJ25KK3798HTemLpHm6FmbCTpCgrnWH1fYjnfGQ2ZmnoBM+o7pFHkBnC0nkXZSqH4TAPIXs1oj26Tc4Sh0lR3ZMFYrvePjav9ibfrL5HVUuJS7wQ69J3nyEjI28DZ3iAzd7J+HvelxVWbOrt96RMKhfhSNTKg7s5FIy+tQ/oVxIj+kYqXJPSo1u3+UkcM/vVdU2BiVc1Y6n0ZoYCsbUwHCZpiqkw7NKqbvKwQF9ZEHmHOTjbXvNnclW3O5jr8ewp9puHkXolCIC2rgj0ZZqG3BwoQG0GQk5sFsyW1seP5OgZpQd1dU8On3EDpLJHqWqA02BUQZgTs1q2E9TItfO8rOCxDcXgQMRSodcAiFMX28gs8j425fJOssVwLzkY22421lm4h00R2Dm2ROgXV2zbpQtU1QVmkX82l2lg1xgQsIswhNBVCZh9lx+iYnOeJ1lVfkowEvJKG+y3ACfpvRwOGIMC2HYkvaOK0V4jAAPij4ORC0Ae0FYEXt7nTf2jqKhO8fEZ7jsPBZJfCiTQXg5pS/gfUZqiSsU8DATYbxhC6LQEzOvKYGABh/fl222evekQ7O3F8raMjAvPKSSRiXUh3AFjloZ0FvIwrehNC6HaqbOqEB+Ba/1NKoS//UqyWDzGalqkAshMNqxO1rybq6H3kzf092RH1NUo1Tw6DeSCdZ9nSGtuCiQjoseZWvIzTssPk+0aArf0XtcZYMa9nCsS9Ca+3FpdohepsnMcpNhFBkDaURbK74a6J5e1svEhKVBM1J43HY69viFvS7NhTvR+v3GkLDvbUxCvzDKY9/1D/hkRIp6Wc7zuv7ovEDJt8E8BGsSCFhVJzDXm6HTah1f7Hnnfh67hjX9g71ZYUWgdEWa0fTNCWXJiZuQ2sFOBAOJJGYTyOnNyG5PHZsBg2m8J8rZkB4Kwvq1T7Sw3NwX6lehO0GGuSdcgs5x0Usf1zqt9aqng5Zg4XCgF8E/scO3XvGLNd6TyXfQmbkamEUZ07lgHux1mPf/FF9Rh6dtuM8kBc7AEh75gldnqpTexTsv6MM3vBk9G6yVNapdvKBzr8DHvOnOFHqf3niCDJ9afcN7sPUaQpShaX4+AIztB5QIrgUDebNR2xYOHCIL9XqCSrMH0mlBvXkNdVVhHO8eqJHoAHtG3Dz6AYtsWkpjwmKUBvWvYrfIlPg9hLjmcPvp6T5jorjzNuj57e2Fr8k6ES5MyP7s1hqxOYPpbGzrtqMN3RPusOx7o8io8UsIck0/nMo+jDfOKmEYU1je2trwiYUYW2Ao30Q0QpevAJjYnjYo3sCnVLtOuKt/WD1ih9/VCIajysRB+AUmKfFPH1UW3ddjbAqIqwj16w+BZx+Gh69JaNCN1Ky3hZjnoXEQVdYvjRpRPKN3d1ul/ofIKc0oaBNnX3AWX/C1xO/3wQ2KaW68HSOoJMQQgPh4GoaxfvtNYOlpkqCzxWi0SyXWCrM41t54lI5LWEF/Hm1Xzeyjf099X+uzPnFppGOJNzHlU4DabFBJNZoEQUpY8r8HQp9km+ZVs6ihNn3z1kAZlktKuUNTSoUttFZwc95LRASm8VGHY65yJ8mI0CQ3n1GvJy8E525v1jXe/ytAWn8v9n2eHDa7V4wl3f78/gIUJQ9CYJfqnS82OGsw60Wp7l/U2mGkiEMYeXaP0cYP27mM4pCETHAY+aF/+rKdfdFFW30aNFbTA+36l9SIN0+zHKlEt8DCNnJYfk9vqKCq8rfA9nhDaFXlGlRTorLrHFG+3kwCpXhuco7KmCVIRRKjVRJcnbxWxLnew2XB98B7Tafkhf8jSPPL3Denw+M7ctyxtF3iP0HuMX3q37bNbAadjOLkOzfHjLmmTUX6InnicZiiaqB8NihBs/ykqLyOsNaFQs8pis7zCwb1p3+0XCNGKo2tnGGSzWHlOywuSB6EI4LA7IDp6ilPUdspXxtEYz1GR5N6rb8DZ7P0NYs+1ctq4PB9npEqA4EYhI5hjedocMqO0x3h0Tyw3g70dxcmW2NLOC/xXYwF5/e4/X7+6JDlr8P7p0P1gwatOy+PSm5xUHmZfxsEqDrlAz37hpYnRYbX/3p8bsYYX//xbHXW2GS+R3R7XGowHv6IE12WsGp4ukWBPnTesJAs48s/5QzvqLr6Ut8NsXiW3T41B4CQv+j4eIirriRviwyj+2eR//4Lls3dMNnIaJBhPW1riE8hw6PXWKJpDP56lZFtvw0xSizF6DIexx3JZoV0ITE/kuqjIU8G9pG+IKbfVW5IN4vAEeW6DNh3WBM2grGNZQDp4WD8d1lU1Glc8ZAuB/pGU92lSVmGQdsIvRXjx4n2NsV85X1bj00mDLom97WsMkuA78Fm6mbaBwYBdVpO1cbnDeKLUciDmOAc/FOJs4OBRQuPq/FIcMfXmvOOsQkUZhL86sc1gRhMzRSfYZ20TH76uEtSuSe8ND+sQqKwOKiw6b+oKHeXbmyTrrmEDMiHuM5Z5TdxS4jafJv4nhh8oubufbimy57jg6H8kmwmxf5qWNsNO4ytPBkR+wiTcjU24B1VhY/4OPgCeRh+TmL9hrMcretQiTk3yCxVPpLKl3avXZDElxCt9kw2vvIqK5PZWe03wpzCxYZv3cGe3Z0Vyl2SWHSa+Zd1GH8TCM+D7gqKyLhChoYICQaLjDm0ebGlPYd/dbEBL/mBRm5G2zjYpai5PNcbOMGuxbe8cFSR6WCgbG4OUUCM0Tjrsmf9NQZK5vVbAmgv72sG6K/KYsK0iNChGoCtbX3rdQV/l4w3R6L4mhxJc1hSg1iFMWQGjcsW7FmAF3zseROZPKMDZehTSG4Cyzyyg2GG6XNpbBsjR+bFdF/KAgwLYdbeSpK6bMlCZh6MU3joDb3eMbq3TGgfO3oat8EHlQWQDEOAcO07f+Cv7TgMq+g+BycYAwjqOo5NmyiF0MIrecxCyjvNgjn0ePFbC+SwrHXp9+9uYS267cMTDGVvZf7iKYjzqCrLxaWo5jrd9S6UQqAycKE+pYqk4pWEcpel3rINjPR4yeii7r6inmCCDWrJZMqnqOFU8aquBW4zWdojO46KPZkoeZAFFJqTLpVzIAFlHxeMcORSd5UHF7rIQ0g5zYLZdpg2OQHeHYogzhEKBF0QIrwcTvYbrFK2p08WHv/aPJuRtaQxJpnFW7C/PmpkJ4Vb1rSSnzRgPN4ADed8xEWNwC2zflK1dw9pNY4l3y50rZInPebs8o6MEOl9jCpgmC8nVz0zjzNysMu8cOwMmQjxbpy2d9eEq1xzFRHsBXUFlghjhhG1JCWy7OQ049GczsV2ujmo8DKjBkFh451GN9p4pzEP6YQiWJNMRXNQpukS6jo9QQH/7Qnk3Bwi/qG+ddcDjkWWPYq8wyNsKdPNkKUBNkvGFkf0zPr86zCvMp3O22GdZ+pxkP1VaX5B8g4fR5k53ORKIkKSly+ppfMjm6peYRDMkN+2kTKgnlPsnioGSPJM336Fcrh2DcuEDx2XyL56NDZwph4ilVznem1Fc8agGvdusB2esM+5kITgbm8FFlN0pfUDDzHDgt8bh/aFW/BJzbV414fyF1uzlchthLfN7gh6+TJKuJfS1tsOpUn+1DRxBjQ3l/XU/FPB2uEzsgahAt1yZGOCWB/CLUNX3wPrg8gVtkuhNV39/alGIr5ZEh0kWFeMToO6XyULS+S1vddmI/zzJuYjxQTVzhmH8Sg2jfeYnSYoy9fnrT4FerH8NEOCzvCqirExCPGsNKdCb5Uo4GQpnayrUaCTwxVMnk1hA6toJKBcvnSAgx8taKz8FURrDEFo/BU50O0lmloSO4plGspfR8rbwsopcRLNyEdqGaHPfi7lIbftN2TRg21RH7flOFE4KqLPgk/gHyOSjpbAO60ym66sioKS5kKbuaDzWLY1mv2rlbS1zAfCy7fI07+3NwusyC+8Nuasz5O5tm8/ethnCeh7afOnsR6I3YsJ+JyHUSPZBgahEQuWCWgYCeSllnU/HcadSGGllpE7Zq2UMgr0+Jm8rkAdnCPUrVNDKJhqS9wXdZZxz0XAanmq/2j/irm+aeODe93L5HQkYebYj050XTP+6MtLNsfzFiPzPUVnhTzEqSylK4/d/ZqLdzDFNKr4F1zVjgR09kTCIbaA1id2TBgE7CkOIdk8YzLbLjfAFe0pKWjHcY6d7KZYKPQRAbHvXv3hWUZSFAUkqARGTUEvg/JJA0BPlYKjo6pcs6+y3RnlbDKFMA2dM5r9ovIeG2EQvsADbNCeIJlhooAAQvgcvNz9jZ1EpWB/VEtVZ0fXTcvcqrpaZ25nyjp+L0Whu6SfKKC9oe5CePkXD36O0nt5GNsXVyaBqhNBIeEGgUFqchACrLzjs7T0CTonZCwV5Wy//HiKcHfD0LsO0bgNo+8cbXEQJ8NDu+dWvOwU4iQA20IzDq+UCxSQ49xsa0X79O69/w+Bj1q9Q07z4hB51++o0rXcC4XNOHgP7tz6b9Ck7T2blFZatp84YzsjfWWfEtV9x8rYC+eswwad8kQVLQBCHjC4oDyPguukG0na0D2RmzpSYpJgF6Qg2nm9ykg26uq+3N1mUeL+g6RJ2rud2+SVeC8vvcHumaHnEMGgeV+ua3iQUAfTk1fTB9BR1ba3cXMS/aaMESlzztFEFA/oTUm0ZDcC860EcC3VTHVLv4FDv1RB5WyPRvPPQ9EQPhqg5F/hiOyXhalvnr5BeMbQsXEaWatetgRz2WckBQoRwmPbrVN5WIA0/1Iv80/IEaz31mJ9zQX1MHo554FCDiLojsDyk7vCHZO2JgBPs8y4xgHX9DbnDB3pBAGDby4fJ5QNN7n8LGcFyq2H0araSOoQ180OyDuUVJpAfPnG4TfofUpbAYZn9pQqMdy9fJpcvMOHbZxVBEmG2ui6UekNrrXs4y7pRghFtFnjwqF/MocOjSxa4YVB1YwdEBRc4jFaNzpQEKiyWdFGi8vONUA7VWSyq0O6loq048847rL6eML0tCByvL0gKrnBm9zZlGHN1HD7FaZjH8M09dwA/Oct9I1y2jRkErF1eDiNZ7bcbwX4zynYleCifGvvqor+NA44Jtpzgm81+m9lvCH5HC60GH/Y8YftoIYpRdZkXFYWrkSlsgQvWPsjBp2R0uhhRs6W2Npxsgx5b4UI+BPRPVMkxidPi/MJXeMrkIcB97CBX0Z2/0QMj2QtZZyE7kQu0u0OLTv0LlnlMopqB2cl8mFyR3cuf+RXI94tC3tblz3pyR7mPVTL9O3jaRH2OZ1ubcTpQwBsSyCWUI2TbkVDYmGinvsjYQKjB8kmvK031QVkmdxleRb1ZQJPeIEysiCWSs4eKGhHo1mA0XvyvbRrgIKSReVUe/wzjCt0cIc7q6uy2QdqccgLq0KaJm1V5MzU5nU2ryizkxvUnuOmcLj+pw2Bd70pM83iq2tak+DSt6jJsbWJQYy2RHYTHmzAa0V73k7e1yJsw8u95WheRSs5P9CDM6K1GyOdgL+ftWdCw0bw8CX/62y97eVuBdLYOTbBLUZL7DH/a7qbPgHZadhlXvV8ZUVtiVhV5SrCtMrS2vTLlkvPcUH9w1pV4nnMZD2g7Nq1jO0L1FZ3BUHnEzX24Ve76psa1VPJqaaCsb5rlXo0khHEx4Gay30UMdpF5sj8uczE656PesJeQ4VTB1V88cviOH3d5UX2JmlglKsYMnf2NFxrdDac8PpsU3mSHMqkmE8pGdYPK4rGhgFJ5RLqXz86SM9Tth+cZIIimN+VSkuh7VsvQU5JYDF3oA70AdQRQVDaUKCoME8iV4DJlL0/Ml/o4tf7POlagSQYKJLtuh2JhSskN7OYfeBFtkc0trOVofjR3xzM05BcKYDJVujn8hrAMfU6yn8Njy4ViQTnowWvYwMz2cZMtMKThu3vdENr63aDdb2YqyQfS7UXsZGHMEvuNDGhnv5H9G21koql8LTZ3wzsIM8O903b2IX/I0jxyid3dZnnuEew3KcW67Wj0sU6GVtu/bWMOlqjH9a3wDvQIoJoupnfXVp/guuu5Rbpr3faUVXgyCNrJx3KSpCibY+s+fsRjKue4uzgtv6IHX346La+KCPeX2k9dUQVNwEUnorf0rm3lpe++1mLRaHthFCjCm53cnfo94NE9yXqKDy8zXtHQUwmnQOvkzDULSaVBgwCEjRiG8tpprxJUdG8MnE+JA479bitvK5Ay2vTC+9TUZTUmXOX4JsDXt8vqVYt1jMI0rts12KeAoa6WB24FwNwjBVIUBSXA0Oo1CzqKABhCkAESsJDe72MTAxTUzXOJU5YIEcK0dYF+JejhE0p3t3WaobL0N2sJKIPJr1fkgRDFc/1UdfrgaxNJ0fbOd6n/iMpugMHfh7YdVB1bBQJfc1WFg6qmhuxoqqvmxYAneVFvz/Oy+p47eX819cs3DJp1cNrQJV8uaykTir+GboG8xdDxmoIdmUkCInCPDC4MuwRglb1CJW/rKt8lcShNKIRz+fx+hKfnB5tN0eyFEx/gVpSjgt+Mwsc/6JlCLX6uRzBA8gylcqEzgtgqb4zcgv13PKQk74ujk6ZWvW5WrYawNCBE2rFcQVwKyF+cd53xkucNjr1Al7fVUGk1Ap3MVoirqMv65h8oVm0Of57oIWS7EEpPzQ6rz54oPkdlFULJ7PGEmuIeX7vt8NvQfj+02A9bOSvdENliWGhzME5boq6DHRDYu+ZPVddaAP+9xHMb2e8g8rYaAn0s8no3cQTd988/nfHXjq89hXuYXYaI4iDnuZe41/x7ZRoc17Bcml/TQJw0p8pgaU4D+EvzrhNeIr3BsZfrCiHz4qXxi1zjglOeX6Z6+GB1lR/EAe822nXpL3pAIwokm5xEz1d8KDvKt62PqLXsIbXfUCjWcRnRdeYqqZQRbcKst76xMGZbjfTClHbKjyll0h4jyKbUxF6PgCOrQuXCTgkCee2WY1fc+XWfEl6nZWZ3NeROZmv8CrQG7QOwFwvcopw0omvuVoMlIpe9pNB54DQ3426VLyu8AokeAKoFOq/DjWPNJpJFJ5S8tYeD3a7If6FNh+8IiOVqexLOq/BIA+tMQZXDL6iKsPR7yAtlHNtA4ZdxY3Mq5KQ9nRh0HJl0W+/Ft3Rbb3bhEYrd05lCcENnIWwtrJRKAOvHTooHrycrtRMnxYO4qxdZlB7U1T3ZRtv0Chcoxnzr4kPW6bTlGxXivZaiEEKBbGrHeD6mfyLSz3I7uttEGfM9bJOdUWDGls8ILzfcN09TB3GMynKeBvHPX8kGFZdPJZYpGpNS6BcCyis0lSABL9WMKghbgFktr0NeZ7+yFqptXPP2f3vRqVg0hEDazBOBFihpy/ttYND3Cr54DpM0xcTqnOa8PTvwCt4p0BmQFx+kqjpYR8JgO4+eiPYVFFn7vmJKySvhmKO6KFAWPx3hmjM02jZ2EY3e0JrHLf/pvBauosdOPwjhpfQ90ie0CShWLuubCovE9DSL0yuCdqKXQExjx4/zNHZFGsNzE5Pj/1wjZBqdZ6Sd1JlnhF1js44MN2SxlB2yA9PCEe8iCVERovQEoalpKm95agLLW56a2h3+CUK3Qjw0OZP2a326V5QFwrNzGKVRNuFbzZZYRHhd4NFsqBDFEzY1WRP4JIEHgTa2acesm3mIis15nmRV+QMVCK8j/3uEo3sU/8zrMYTLnOZWofFZEnX1ulSoe7OD29skTSL/QF/DgWc3OQ2aWxVySsPo2yV/hHmLVQadWQpjIhimn0jS5VnOFQJtpruBiMqfaCObkklHePTr1/tZGjp+3CVF+wI/z8ZkkjO1+V8omp6e9PpqU4h9QDeJdygaCtVB3CgCn/J0MwN/iA3PxJhUw4dR9nOWEz3X5iwihm7z9GjO5pr76jEM1hxNnt5EMygX3W7aKICD29zU677GJ5wi+VcjaZoIVFFM/hxVg9mbnmXJyBq/QCWV921CCb8jNwPzElxsdKbRXtY3g44+75DP6yK+j0o0543EeZS4+g71Jh0uHtBk89I1RwwOWODs6ooKMzSjGfwDSlGACK/r9SmnT8IXiNwlUhYEo3uYTxEJMtiZpb7m5KK9vRj19jCLY7Srru4T3L8If27cFT5F2ebsl8PJSnqzzN5pgffLzRq95gHH62SoXLg9BoFsfYiU1+BtC9B9N1si6Zrz09HekvatjO7Qp6QkOX7hKH4A4HV35U2F8pNCCc5OClDb4Aofk9vmlKgdBAR4/a1Emx9JdS8MRg8tDMqgiu3gmlrEE0zB342jmNB/rkjoLF/u1LOv0igcQ7GkZ2MR3DOq3LZnF2iD0BZtaAl53Kr3QEdpKC1TaIGFwehr2A6P7LByV8S+VCQ7WyJ0lCt26dXOSBoLkLy04wAkQo+HspV9P6IUawgqecFAiPQEigWiQjBeLp2wxHbI6N6iKUHJvvdDkrc13G572rlb8etrLPfSTrVqgbkqMNbQaAP9RzHArxLadnF3i3UKTcao46w0cFrpsFrj6l8IYdsvc3lbPb18Vyiz1YdA1uU1mu7mewqB0lPTXC0fa2g08v6jsC7V0LYChdMI5zhdGA0IVj69BI6/kNkLFnlbvfWQkgrQA2kz6RTC/3Fih43Tsu/sQVwlv6IANrke4VFe72Yy7V9gYuxIHo1ZbJdDa/O887lEGTlwzzGytql5hvUFnwqbx4ITt3NajtzRGFGXtjpPZCmz35KN7GPCBm46GliSKsdzLasjjkgCKt2VZfD+j4ocQ0fQD4v20SNMDuOYSN6PfoLo+eeLZu/jNd8sqZqcFlN7Zg8NTe6IPcdoZhnJ1I7jvW2j1RGnJhnb2tS0W8TZd14n3z5pVoBoHKdljyyYGv8ZL5BszJ9oeQQi0rpNGmvOIZonknW2SRFWs6Lp3TlaAX/UhKWfajWhNPmFiieiM1rStntdR2MId2t/UJZ5TDzFN73qBF8WBdTZZHqoTsfztzZb3M+Ct1zA/a2xeqxIczXiPhfTXAmFiq6dh0hzNdz3emq5X0NmF3qxWq5veGgq39F8IcP697756COzyhO2RhKY+UOAyw3wl3BabIA/glcoIADffg0q+C5QPIeW4CFwcM8c3W+mJjYkz2XOm0gyKP3/ICchyBtQBSeIDSVw+PuvwH5UJqMJePnVez85yMLeEWlwvdrLP3lbQXSQqyKKf2KKz+R83zycDnvWbHgGubr09wczx+qz6z7GPon8ope4LNp6Ssr9Z2mI61EGiB1kAKS+kyyUl6MfjTKEUNpfAehXZEOnIH59LjHPw1jvtYsh5DqQrVd4tTitA85N1+OkwmDaLwXFUlhT4DpYYVjUpaVFP3V4GKaVcxIwD8/KSh3/BP+VqKQuU0ZfepIHirwN7Mp9D3zvfgsSYPtgs00y/ePoPzu25nYkZF8OQIdBGEKQyRKwcB7VDs8k1H0MYuXCkju7QP+skVM28s6szKDZbxnytoJsGcFUp1B7TxgHilbaneRFy03zG887/kXN1W2QS26/DqifQZtNLZ8MYzqPxSq6ve2eOU7tHDDtPiRMCiXbwoS8bWPAXeWtOBeuaGbUQCbK0Kgx4lIElZhvIQjA1AmCedo388YecIR3MD+HQh7TfltUCY8A2+J5VHRHastjUOvQ4lCRnuIQTwyCXXqF8YtcJtIiXpKowDxE/AInO8aF0ViegygemVti0GKl1DUNT5u1pGCAcUsOGzQaiNAQtJdIgfT9dj4DqlyKhFYAzyIZjL7LQfyMhEir7puggGq/C8rb0rwsMs0lZntJiB4r/Gm7mz4WGXkB9M86Kfx9YptrGwLfMXwovKflVfR4/IgoariiwoiOMBvc5cVTsI2YZMkt8jSErhEunfRp2bg627u+Cn7OfokM5ZsIL4Sat+HwRScMew2IxFFcm9YRrkONK3rdkMKtBJTpDb69YJe3JVDM27l0kZ2iUckPNv/AfEObXoIr560LyQwNnZYYE172KA7wTMNDoJpLrvllFq9yWgs7x3uJuC4IX3dRF73jukgQ7qWWvC2eZC85khM/VonhEuSha7Eybco0qwMYNw0rBl1n4RbYfmUpmPgpTlG7NXuuBoLoHBVJ7h2AqXHebPB5esurM78vdLYIFOX4NEuqJErXLMnoLhpJsWu2hlx0MYBaecVC21rQpPv/3GJZDFNqKc+dxHLD9PjT5/zOQSLjWnfEpZXCspfG8rYoMq3pEidcFop1CCaOzOBSpmCuBfhx9SrABLmkgg16G0E3BF1EQOXK3oa50BbIGEKcEPi9SJG31eZvwD16yAtVKol30xhqNOah99O0epwRYEsdy5iP/bbC/RaoNr3nd5/RL5T6pxnPi0rvj2zsmWXZ/AkGny2a3W6Iy+690BwVCp07zB36Vqj8Nt79JZCD3C0qClTM0lhQlwsiHZTvIicypH+qqp02zdC7EOT6VmoDVpruQUGUJJlypFSKAipDffZVh61kqFuOf+73FHlbnXj0vqcOY7lxNCPJTzOK9B0De1wDqTuEQvEMIEB4HgDSCxTnTvGqL4lVA6N7MyDZc7y8rZb0vgzfYlnmhreXsoQRfcdxipd+GnDF0X2THMA7Hr1mQekjOAQBHMJBMK9ViJUjj1WYvxnq7xfgS16Al2l9N3+rwTy4ouyuxscOuxkwT7xAGCOJfdxSiVE1z97wmPaLaupFhUf0scjr3fzMjVuev1Em+8x8pnuHdzTGq+/qHm3R96hICCqHpdfUL98waPbrTt5WQ6gAnDuLJ6Hfangf5qXmlNzfRZWw190aX+b2f3tun5wPbd9cKU2WU+l4Zep0VQJgIsdzjZE3zAo+yfEJCXcc//8gTYlp2d4/nzeC5qXyWXqgoCWf87v8PInJolrPk4lP1TY9zDeUVjXds8o8q/AS7EPhfUXVQ178nJxhzosEy7qnRioc1UWBsthbh+xwHj/G9/iggUhGB2fUird70kbg93xkhNfKWtTDPh2w+MJPW8P+caI4M/qRceCSITFQ6rGwoH7vFYduOeT3LVBMHLje9Ej2m7S8Lc0m/ZeJbJLtxGjy1/1lkoAs5klj/tN1O/mcEwT+ZDWx7OLte4snvWlh2vZ8H/PxQosssPJ+0Vc6U95E+z3+58fYyLbjbINndgYN6yKvs80Qk6oMpNs2WL/W227ZeTqlj31s/NxD9nHE+gFl+TbJIszxkz1Z45q8qCnR0bwF66RlA0dCCDYAAc/AX6Im4oDnUbjDst9s5W29gMuMKe0xMd4MMG9fReVP98eBpDbmSBHXnjEV00qRy/+S+KLOMkofcRXFX6L4PsnQUoyOeSXU7n2SZI2m4xT277KOY4Q2jrWPiyJXucMFS+p2R9xazhE+HQr59szqThfzVh4XnGJ7+LwsipFrthJ1atbAimdnXQW/qHdMNwOI0b38lLelPUVPIqMO0iRSHQZCuefm2fHjjixRdQTVQGeP9kmLtJX/XNFLB9CemRdUELpGipDVMxbYa1y7s4wR466d+4oeK7w3O+wip+WnZIPXgvfpqs7wFtFt7OH8yEDNcWpxLiRCMZX/TuL8bzWq0eYYy+b0oKowAR2f9ne7bPkGRLiX8fK2KIJ5v6nBPcGTQMyETFRqwqPcjAigtgIpGgMSTCezTxLAuVRj0aKSDR8mWUSWrIleusZncl+wCNVsW1O1jDZJ1PGIbgKk4pPMHigvQRlx3YKPklIOJai8ClDbOy965BadZ6vpBkFDGw6GqWI7KAqlxZiYWrohUV8NR0TX8DqSMP0MsnPt9yuF5OdUUbetirDT/Cebq3z+Ni/QLn0K0rDmPHU0eROHcTx5G5f1zT9QrIplEUi3IE4107vUhLz5u8QL9KpIvKMuYTRuNr9GgMcxSaLlraqibPMlyuooTZ/CHdjG3QV+bhd4n+OPauY7o+mAaJLrRnTNAoMDYWBU+zQL6LU9s91y359pPPsNWrNMVUeGv0ziANO5S+hOSoaN6x1C5x/heV7wVxC2vhslnskwBLI+r5elJsLMRC23FtDLMrUW8yLxPqDbCK9qvKs2a4Hy9QgcQuYo2u6i5C7zkVc9jr2skrelkRZTvX3S6pgTNRxI51zS/czLW894/XWL6Apt8Z7i9HxzWIYcqv1qdF6NE9kaX/Kh/AuGbHJwTH+f+jkqq7a5AoWQqVpTQKuPt3FHlOMLE9vqZRkGQr2jD3kmD+yI7XXElx7s3zncCY2133vV/pO2tvEG9xU9lJ8Rkc+Yr8u4SHbtNZz7Pgdj3G938rZgin2sk6EP7d8uS3L+c1YYeRLWYBkiV+ePvPiJSThxwJCTvKi3nkuxwVG+EVDt16CCRUKuvFDxtJsZ9M+SsEvilUb+VwZeFPgXDMMohxLMygpQL8PyeZH8wrQYHHxdFyyLZ79aFas1hEAnPBRqpV7loTDpjxd/nubRKkmqOb9NiOTXjALkwOxibh4+HdTVvb8rKYXwAsXJLqG8/pdRaeTeTRQbg3KUFSvXLPwoSBVggiRVwdr6BI1Lx6T3NLS07yOQrucUpNcW8AVFJRbEbRpAr0f9DKb9HiBvS2MknChPwcJpEi4I6Qxf6jo/77d+aW67TD5giZeVbocbYaUMyPaLZb9YXtJiOd3u8oLkOb9NnKIXMvX3i2Nti+MkTzfBMh1YP9hNG4f+ME/1w2AK8j6t5flGjUQb5gmG6+XBz2TneTSMfiI/DG2klbPM//oCr7qTBKUb8muGMCs9mx3l2W1yVxfsa6CprrSOH7H0oud+wkB1ab3NhvAiE7d2gUosoE+zW9V1cZimund9GLlTxAT3B+nMniV9uyiHEs57ClC/5+dPWeweV4ZwZx8Z7w2Far9LK5Z1kOAyLX+oA2y/m8bAZhTXZqINvwmp+VgtdAxoaP4pKlUvNf8cLmTcqWWEjrZW36ep94ymMbKBC5u57jCVxW7WQ3MP+UciLD+gXZq7plbnUewlmrytblfyFWnLLOpQ8ejmU2pGpgxxYDFwoQ8UlljvMR+mIW1Q7hCNkDyCV0WUldukibcSYiognMNImpgDjVCCwRxOzq1ZSxN5IMycXNY3rZ1g8paMvSgDMULTnsFlZ8DBhfFOJOsx+YW+ULF9PV6SuLxHUcTJ7myFwMmK35ivB+DxXCWDEU5VUkC/B3qPfgZPpv5e8ZC3tWKD50ThDonYJn91pJpebjdbRKCxarXIXwkmrGmTE6Vj6zbFfvF6epOHUS5bgRDSGuxnFxOZEjPjHIdO/JPss3NYX3t2bG2+s5hhZzxDnN3elsjz8W3zvMEPxWFUxfeXyb88LxbOsdRok0R5rrOA6tVRvt01nlc2CmmwCBl/T3YHRXwfwoGd1BqTB/ord6O2BceU8FLw+AgSWk0wmMWfUdqkFn85lEQ3DW/xP4zin6cZXi/xT0///CMsFNP87o0E415zlbcVxPEX/9zUsb+kCuTxu4RvvYT1QA97HaywArUV7PM/NRNmNZKhjn4gHajxOHp4P49RvDvcRk2St8LjeXkvSyB0e0Eib2uZY+j3BD0EshuuzVsNMyK6y4unALzMo9rz8Z6PZ+PjTrgHYGMO056L91w8Gxd3Nq9BCXJmYhbRnoflbQ2nineBTifv/fBMv+MXeVliHTz15zIe1Z7P1spnFk99svoCxY3jgj1fbEm8rB7BnhvkbQW9aHK89CEz5WsuHfWtie28P5INPrT/Pc80TQVJ9ZNUyjS5gS4A15doNnQEp9ZN5Co/iAN6SzSpX0GbEmFoUtqKn+uWv0c7ElAsGNAhGK8EOSxCD5k6ItnLVXlbDaW8PYUK3BihdwCLdXOpGshxQzN0Erxv8pvUeWTj5X1zjUXd1kwtjFHRuReS36pry0DX7esT/ofoLsk+FrnD5ac4ew/Hj7so2/iP72t+kqdp/uCNBz38SLKNP6LTWJdtMAT/V0/KVfYuRCNkJEd5qszDGmY4n6otlZxssmaOyvIojUrNC8kQsVKfg57Uqj9h1CT+ik2lSpmpSfWWMd6iXwl66GRZ6+B9Wp6k0V05DNtacTpM8zsSnItHHUx9wjyDhW76RNzKKLnLTs8XtL1BxeCLQJx2vkdpjX/8JkwlA9vFqh/A32ngu4EO8O/N8F+2/mWHd0PFP5k1JFb8s6aiWOMvv4ns0jKGklkaA8j3pExukrTRCb15hbP9j7gXZJaTppoZs1yiqIjvW4e60oJlmtGeN9HvDNmmT7gqYRabGTzKs03SanehJ3BAveD8faUWo4ao+HxVFzedPmg4ed9KCli72KMtnuSN37T9rY4aYU4epRZ5GkhM9zMHYF9w8i53SZYRjdpQWOOtF+uW47p450DfizpFl3HuTFVSv3wzYFmQekdRUZmSrjm5EF1CRjsB+eDr1DO/kZQbed9haj7nd0kcpYS2ZztimiBKs8ckAfgWnK6DbGM+W14sfhLFqLrEZ/tGL3eXHe1+96ZBV76hsS5Ixk9JVRKbhSktG6gDCl7D+J+jG5TS8BrGZ00DHtzf0Le39H9KyAPzoFNHo15Sat2j+OdN/ohK0xlsRZHp/H2tsdRK4gsSPUQ2h0bzcZWg4hxjQkdRGtft+e4Lqu7zTcD9WN7IkjoxObmbzs45wmfWrIpGLfov6goHm39garURFCRamNH8/IjSFFXneUkE0gWKyjAqbrfvlW8A/EvuHpttkhnPSY0Xf1Qi0zWDNeOEOgZpJB7Ro6m97E+avmCCJXgTZiv9WXdsvkkqGUMZccdRXRQoi58uctwsmcA6DaFHjwE2oQYW5I+mG1+SzS7H4v0DrSTrTkV0xW87U5Y5SB+ip7KpzLSm4R2qGtWWy3ZJ3qyMWa69J5agK9/QSJc81ObFNjI2S3xK7u6NtZt8PC7/Nxfdksube9nmuv+cx1EgGwOXSVbW0pL2xTS/MZ0b8rQaiz5ERIWxOG7dLj1UF1VID38RSIdnk7e0pFkhIa8kz5v0s2bT9AV3JNnh7p7glUcGyFQ2WicHZZnHSUPCYa2R6Lnt9cgFKhvj/nWfIYfr/3G2edUa/ZW1xiuCpyF+C1Th9at2RJiYWJL98fr/EoZv2uDgukE12A+Ba+QdOybcyFnWJgp4ddDYpInJooyjjXi9gim6Yb90i4aoDkd5VmL2wNuT6DmRZDGet9RmKBwSQwcM0smhOb7kA9qhjPhO2MyhST/ofEpif4ZmOWLqaPf7W4pZDXg4+Vfj49z0zZCBwSpS7qWhrVkXbur58a1yHHMxrXLengXH9gc4Mqjr0eGE/OziiJZS1jWoC/EwXW1S5rLrKbAEQEj9inNiagtizsDdFgQz6Q2NbjFWB4dBD671hpTxkFl1iOEd2ci6aTUHd76eaxPmdmObgfXt5tmkQ3zd5VZAnqZ6jYSBAvk5J+cMC/ZlED4rVQPs+hxMCM3Bs1AoSM8PsvIBFdcNn6iYgoKT8VkLYsttNGJIKgIMvA5eAzo+E7cBc2Ek2zD8Yrx2WUUVavzZM3xiPCIho4onKceB0BDfMYA2rAe3ADBg39XV8aByBDNwonKOjDTNtspiLNkFGdMyIwcHsWEHYsOAPFZz1vvtzRvRzOrEQpI+zMA8Epo+J7ZhRY9umtnVEpaFWNwAIymlZHh2AvszI1OBtDZpn6m4uJ3neoyCqDOWjKAqK44NbwGYAcYyY1qXsR8mKYm32jeg7SYLH5wKHHpzUoiry4EaTRqXrBrDtOn6y1dQ0aODdSGL0Ay0iUnIvrwCpRvFjLY72XzZGOwWk1fkoQy5VdSbKwRIiC97IBuGFBE/K9OFtPszsKB0Tp6FCYP0/ijfNrFqB8ZRcQkPLOPADs6WCQX0AB/KGHwdfCgbwUysKJsfk+b7OssZ1JImEuf1F7RJos6tR25VA4BB01oLZ2VXg1ADnMj0c7Jjgao3c1jMFHQ2aZ6utxxndU/turH0PCFlABAc5C4G0orJ4DYgGy6MfHl5px7CHLypnCcjo25bZTWM2T2WMmUaPgTzFIzJhW0W21g/Y7JDWIAx2XkyYswx2voyVpQumrBWVvKA4Fm5g7E6JPN4zSVjuL1X1ok5zrYSuj4HqfYhKZusJtcHOzwpJFJHN5pEYYxTVYKYqoe3YSplG5D1xYxxLUhDR5zXri0IGCIFDWdDDhD/EutM1ZEZ1pqKzs9zvdEjsllyTL3pVh3bDHS0MudoLzp126wNifoq01FnaMFc53KnyXnUpCpq35ZeN+ELkIIeMDiogdKQVgoo3ARAjK7suItNGIokBG2Ls29dSg4RFCLFCGVDBwC3LQ2WV8Xlg5hBrstnx6TxrspyPj/3SeORabo0JfCg3w8DauX4I2lkvtU5yEstQQTI4CJ7geH3Z8nOj+Ps9qxI7pJMZzLg4RU2AwdjgYB9CXccTV/mO/bLaG3SA67q0oapa7xbJL9Q8dSkVdNxAQ0cmMEY1AB3Mf2cnMWg3szIXxCdTZqn6y3NWYd1tkkRiTx1UFVFclNXqM3sez2W6BjOBIeCD6XVHRjUqCtyfZ4a8+pUOIcRzrcWbFjApFdjrfUskG4ohpcDsnpGC8GL87n2nuGNgW4sS/A1PIvmvLz0TYI4IHtGno+FXZg3pGoh78oivPcMb6669of7k8GGr2ECoYKC21wutKTNWNw7rEZQSkcxH5dK58vomNXVWQ2XGspEHn5iHn3GW7lsDAsw6PMVoszFGHMno+EgaUUFw7reYmqbtLxfWg0La0c0Hy9r59OkK3S9VXG2ofCF6szEz89YEKvGsRADP1+BfLlDcXKbtKEOB4uHKQOraytYGa7owNSaHjxD9jYb0XyMbjbHz4Hl4ZGcNRnpriUcKWM/B1zgvakCjdUtqkN3oIfJRsty+aXiMdwZFo4Hb5j0Dsaw0o1EyeBusl1N3sW2HGW3jNcavPSXX3H+Y198xzLhG/f11+JZehX22/JVpAg+CcAGvlynMctVMFwc3I3le1QkUVYN03KUb2+SrAG0cj0wxaMgnAKFA02NO7S0L4NtR+eTC7ZzatKzNXlAqMZneKAzQLEKjn/G5zuLYa1jaTzDk57BqPr8hN+yxGtV0HhWsTSYDgHrgxn4kpsB1NF1cDw0pyY9o+utjfdddwAHsR+AoV+SgF+PVH/Gopw7Z5Xk6QVfpGNtExwKNgeqO3C6USfkXL9io5zDAOdbCjZzb7EsVmN+4+0ZCp61Y1AFJvO14r9OVN0wWC3yJbvWdWMw4MVWjwFPuKyhEcvSq0m2cRpvNVoEy6hTL2OHMR7d8prWi9hb+ME1yequZdxqyZpKZBarpMETYKmo+yNfNrpVu9rVYzTg5VaSEX9YrCoexdKLy8oQZWptcrmzWZXdaGHj0PO2AJE0yWkebcziXoLQ4MP2DtDqYTuIfLHQl8ruzMBeSlqbtL+m4JfXlxHJfjuwhU7AsOCBpReHHLoElbBveNkF92VG6QVT2qQDbM3FOOwqQQUeNcljQ2e8lDEBDA5x2ABpw2MS9Irg+k1vIT5bWgdUD2UGFlVPlUkHVpEQcxyG7lpFgJyALZ/jZYm097My4fO9ELlAvxL0YHqrx0Ir9t4W0GEH5lp4TqyoHMF82zY8R8+OJT+hdHdbpxnJy8MylREHSatrmZaq6cy/8tblDA0vmZWxtXZgc/O5dp4tGL+tuBj7n+RFvW2SY2gTQomgEFsPUDZ8DKC2Sv8U5Agk78QM/CUnro1euSwbXeW7JDbkIxZWykgNmDUnccgXYiW4F3PxEkzg58NM182/H4u83qk5iQKUspE1B9FIAfah+ra6rVPW/7kYD5gPk6bHWmsQYi3XGAiZdshTiK8Ws4z5Vsp3QNfnFXjMfBjz3QrUL4pf9FoSNeDwKhiFXMZ9IF+vhAUlY5hVhxPnx6T5psLyrPg9r9D1yENahmHhlQxJQJ2YkmtDxpfrTIWoGcWcnAnPlbGcXDQj4lf0UDZx+LS5YQVIiCl7IBt+FBE/q9yw0u7PwILSOTFpe/HcsKT3fTrRgXFUXMIDyzjQITcsiB7gQxmDr4MPZSOYiRVl82PSfF9nuXx0/amevk80SGzPgAfPa89it7xhdn88f/xYoSKL0oO6uifkbWMbXKA4LzZ6Y5RRbYhUqoo25DPrgJUJa+nlbTWkGda71Rw/CwvZWbEhQd2TNE2yu4PNpiD3RjIOg4Ahjm7gbFgXRAxwat/BaWytql7MwFwq6po0z9ZcmKO04pIFC8hFz1PSwX2fjeuepezq80V9K7FC/ynBvSmerofEUzIuUdZSpbKiK7iktYIbhHw0JbmzludSo6HMwLRGc2jSj77Ouni4lXxW/NQu47m4t20NYF1QZq+Ub5lBLMW0zLyZdKKpsOzm3pxy1TzKwUm3d1uzGY/3+bCgpOdz7fDiXDwnZtP5dwqQEzDcc/TplPZ+VrZ7hr6cH5Pb6ijCh/pz3OX7qESbH0l1P3KQjF009SC27KvYcKWuGZlYhLg/3Msfw17NwHuG02DEiSCGxRmTUSIGFtLxC1hLxZSuWqO6QYA9ZatgeRlqNJQZeVo5hyb96Ousi4e/0UvMjpGZqrNxM9vq81FEzQezFFOD82nSGabismrrV+IeYXBGGuGkKutXS1cXHu/zYU1Jz+dSVsW5WP8Z6QI94OVznmMEZb9+tLZ3VSWIDQF4G4ZUNvesrPQmI5mBW03m71lY8KGBmCkC2prGYg88/9g05LZgxGZtcvjcJ7vG6URNJBYMzLzTQVhl2WGxPp/tBe74DOsVnof1by59v9szc88qOr5goFVMZ2uLgxuAkspIuHo9LAgOYUZOBOfIpP0BwbLuBaQbO2OPFQ46oLMBj9ncZyWc2U3Zk7l0ZxmNTXlqtwLPlR9RimWBsRYNg0PMxUDaMJmkiWelM6vHMAN/qufpWejJ7BDUSh8AOyFPBtC1A3DV3Aqdgsbr1+ouUFUX2QX6Z41MgjXA4PCph4K0MxCATTwrMacewyxGAdU8PQsxN3baUL2TVQj+IiGknmd1AMubZo+iolXZD+tskyLlFbSiDnwYY8HtDmTypuRuEtQYJtseDHo2y2lLOxUmvRhrLWgJ4Eai3TakNSZnwue5f2iHsQS/PstdRBiFzntMVmFyTn2OLmW6QSzBps/Qwew8T9PveYVH0b0eIx8OsvJBIVIVdcCwgBy4VThARVMQt46dXx3DGgxlBp41mDsjth1qLaek36P4Z17zuSmEz3Kl3RABqMSDda1UetPWIe1BGOPquN12eDOwvu18GykZfOUFrSlxXWBS3J1HT81lymmWELy662tFLdi2wlawM6+oGjO/vw1yMjPqzCzmEoMZMOoHVW81XNibJgW2MeURGQIT3nRyETJsHuBW3dJYXijbjm4B9tfNt0mX+LqLrQYyrb/w3H/O766pvwnPSBeAog7E8xSIDZ+rWoFsilznV8fZBuOZgZkN5s6kF1zVVbCv1s4GAU/EsM/TsKYawcy8+SzNaUZcqOM+S65z5bZVZBdaiNGeLYMd3N4mKf6CrnXXfQIkKOh6ICsxJ2CePfqQtAtzCCkZYY22zYXdtg7ilIvA1YSFk+9oEDi8Zab2llMJessgdstLMfU4Ztk4VfNkI9lIveVuo/CximTYSrZR8XT8GN9H2R26wEvtCJ+VUBY/ya+ldDXB+ylSyepSStsKyLpd36cRhcZ9moENjWfByHwvR7MOBm1+2HEmUyU8S7LoF+ZFsDNzMyFIcAvuY+ovx3bxPdrUKbqKyp+98Yn+Jmc+TUWQBcU6VgypaxJ6g0KPZXW7u+mI5uBtw/k06QpdbzHO/luNarQ53kZJelBVUXzf2FFPEoVOKq8CcTMIbcPPiuZsU0Yvzcj6oczAwvrpM+kEgV8Z0zL56O24CcrtPgMTQynt6WaZEU3mfmret8W4E5ofk87Q9dbArdftwGJ10FdZBQ1nOvIj2wTAhUyfV6cc6EYyL8+C82XSBbreGjiVWnw0i9nJN5ow80lVulWAnRUrZlXcrB7RYqIYmFOTvlDVlk31dVnflHGR7Mh3w6StYBVpwi8a2jrpF9zUQplclZ2ZgQH1xLcxrC6Yaj35FVXoCypJ3LDrkyLfavlOUQdOqk6D26VSlzc0P9sZ9GYGvjMgvtERiaq3Fua7ym1Zb6wxKeNRzSzOdmJf5mc6kewmfRhrLcZwp9tdXlS4a7eNRcHESCqvArEcA23DcYpmrE2iQdhO36EZ+E5PfAtbZpLdLWrNPH60Zj55FTj9mSPzKZpZhvn0HZqB+fTEf3bMhxtK89YbuWcTNU+IFeSMN8La8x7QDmTsUTH48udj3VBm41n5rJl0oauyGKseRvHP0+yyyuOfdudhXUWIdSV1bDhY2+yzcvI1Hc0MzGw6n8/ioC0bjO5JvabezDz9HN/ZG45lQYZ+hq/uv6CsJhEDOsdC8lPKwgAsxLYsmA23Qg2AN5ZZDSNfnkkVQ5iBMRUzZNJ6U30eTjzGdaonXKfCNVDRK9nbqKjObv6B4ooUoUc88XEj8aMsy6sGy1+/legoLQg/lH+8ropa1H0J6ktUDfaKXRKXr1+13yk+agoAduSqR49HUYXu8iJBIJah/EmLC/9FAh5AaLoiLYrPeRylyb/Qpps+uFM8lL5rn6Psro7uYGxdmVnn0GVVNFEdyobzpN3j4LTIz1GxTcoy6dNLQ4h5GBukeAV9iZowKhrMI6AWPZtEXsTKerzr+pqnKdg1/N2ochsnQ4aiD1diOCTVcLRIuhcSIE2GRyW6jpBrbMmibMsMFiSWKQivkV9YwIKIGABj2jTCK6tUJOpAtCgP0/zuPC9BXH2ZfvJbLQOc+V7d06Doc3VCOMb0vDr6qASpsRQ9T+KqLkAcXZEWBe2zA+FhnaLMqKvqFgOh712U1bdRAwsuM7pci+yixpIWVc1nmPA0hAH126fsX1B1n8MymAYw6p6mb4Yd6yNSynvGQuhZHqXJL1Q8XSVbsG90uSmTjJELFXxCx4O0RTvEfzlJ0koi7DV1TBtVrmYWxoCtWngd6wNgpqgvdyhObpO40SKHISsagSvo+RCsdtYcEUGuVMA7NmbejCnxriJYKxpKTRF9j4okysYwRUf59ibJIhlx9LW0Df+tjpov37IE3L3octdRWHTdtAkT3O5IO3bEACboR2jXhowbcZ2BJoSWxTR0YdJ0W0Dn6Q+K/+EVgO4EmaACH+FhBXMo1KJpPJVkquBQaIZGeiYeS00QRTIMZr34WOT1TtqLplSL6Ct6KGXbaV+mRXL8iLe9LEoP6uqeWDNaISk/ZqrgtY01cbEgrF2QMUNFnEoQCe/AAKB+GwbST8LY4QyihthVCM3oJ5tyKqmzCRqS4FCKpk0zqdNfhcRmEkUWSoBmoNFuJefIMaOTIRIZxdjcUxpkdJoHeJxcFg0t9agQ/TDZmAwK2rGycXDh8fLxinWHVj6yInh4FQNh2qKV7mCyEJ9a2kIBzWTcKQlYZ9mGCW69cWgMEATah+i4TeaoGoOxGl0bUUxr4wR7ZdSbPqgI2I8xSot2UKl8a6JCdGjQfCsUaIZCvUxHGcL6i3J18DB6lekebVGjst3AhlMGwMDMl8OGme4lvtas1zwNl5ithtf2Jp1QWJjpcj0y4Y0w3DvwnbcFbh1Og9MX8MADPoaBD3XM0WuQ6pVA6nUUqPQxT9S0ls7tLkruwINPX2ZgpWz8dK/QdpdKpAYHYqQuf0YVVl/pm2uZ8ixCmin2OtzAGxbtqY/2XIbPeayXuJa2UVkX6AdK7u7B6WYATNF9SDDXlpJR8zBapIzbLISRc1PWreunLFbJn7HY4MDE+qfBhyTem9AIqWK4nN+g7uYE9tIAL1JknjYWhnrlKgXgTC9LnlSIeRhjc54CJwdioAMSsI3ijomF0A+8yMsSV0wVKHkYgwXaO43AS3N0KTFANPpYyJDRDjAcQsojQn1zfj3eu1N1FFfoYwXegYMOJKmoN7jzDIOW3ukLPiKmTfQOPXQTo+8B75XDEsuUkLTng56KMLRmfGAlKf14hw0d9WDsE5OOiSonc4wQaWhSTT5cg9oQVTnHCgU9TRoACCsbvzeVQcTXouOKSGjDmnJSmCGAyG1GDZfG1KTvZaw/1fM0VUoCFkAxLBoOJFXrpaOiDINi4kVNZdVp/YTgodMg6p5TkLLhD35GGiLQqCA+gCjpQALGh+d6cA0SCQEDyscAwkNE4d2MFHSBcQLUodyfvCnUx7hV0IYHkY+Ag4ToQbl/KUjBI5qJCJzLl5wULKB+HOzUepOFRQcQR811HgrCNdVRuT5AQel35xFYtdub7fQULoAqUiL7EOQwSVMqO6+KKhyowXDYGgHowyGciUidx+P4/ktBJQFWPyq+iopOo3+mAbkExJAckk2AA716r06luiICyQciwEKkofxMFTQRUU2svpAGj/Jt8wZydHeF6SHAqcfBg3szDIgUoI+U1C7qXetSysYlhHQ8CE6hnQHgoLY3+LyqVD0IGUAXzinXnzaM1+314H0LUAeGVAwJrABSiHcOVhEKxgqpxhJ0AajU29P0VIJePCrHwz11DEQl7imjiDXIJtYZWlVcJMAo9hYOFJQ9lHutapviUU3LLr1X0fXBbpcmaHOV0/0UiaKEl49KVQ0iFvU8QUErJVZoW5dOgQPl6EsAFRuBcPIxQeAQhTjPagWVQIxzcxXXXRPGYqvYcAFTMyR7sYihPU81K0EIOQpBExoO0Daj7CuFpNyAc1q5zjxWue5ewoC0kkAq9imwArj7cU9qVJsfjBQgkvDux5tW41ucvmGITgCUfDgiMEQf9o2QgjoAulkowz4sUrKRDFRhEYRrgHZG/gmUytAoQTsLwYb1riKVCGQgQFTksZRF85Kk12M7a+fZ7VmR3CWZQhEXQLU6M19DoYob6eACvolNtH2z7Ms7OYEYOP1oaHBv0jDIALpwrwtD0Ub6qu+aflUoJZlRde3gTbAoCKx6zainvFHjcq2CeaEZfla6TuiP2dIqFgRgaxpR3JLEXAsTK2tC61ZkdCHglKSblWiDmWB8kSulmQirHZZQRUExQ8OGFPPUB3W+YT2TCaDmo9IzmCO5ZuUuxmTAPs6WEk1eRztIaVUFGc3tH9pGZjjHQ33Q8yEIbjdQPT/6EXJWtoTfvBsQUlNRO2R1fQVxpe/59WTWtDktwVVBDK5loQeA07EDGsUR1x4beKpWB3RQnbEdOgA5cpgxRfB1oo5MYbt4lNhcuVtN4RmXmbIjxnMqYSqPo3LvzR/BzrsQmP5sS0F7n5NpXHIZ1fY/FFUU4URMbQvGKLQUMMWkoLQ6qIp+Eoy7MJ8dQ9Ul/f5tUtuLKvqdfMo5mVWJUnWEjZ3jNB0MCi+q0JgWmhimC8DscLGIppoihxXiviwc1oITnefleiD20TVfpCCvUXUtCUywKMgOR3vSU96oWfksTKGh8hqVvKfmk6JCYksjBS6LCXKYHFXDBlOk4Izw4shk4ejrOgsOkyUTVFIttVjAWGfXkkKLuVDjsSaTEp3FHPWBc+wnSt0D+aRpucTjwGaoUlnqTabKkdmZbW41pw+1p3VzhgEVd/0QPOg6MAYCVLkOgOjmc3W+voy2uxSNkQnl7MNB6uecreDNQhw66GApI7kDfYYoi9dHTMR1kT4SSPmA4AoQfeg4kAoKSRAqXp2Aj6C9qKQ4P4hAJkNRnBGsyTLrOeAC/UrQg8GBigPUrgAW3vvxCYx1RhJ9Qunutk4z8qKMKdDSTF7TcLhSBGGpKm9GsTZlzTiQe4jxqnxCB0DJxycCQySjI88qiAUgm/ip3BitVk8SDkwzDBZaSpQ+jq6OKhy6OchyTQfRldCEhtGMgAKVUCPSk4FGAtCACQockjm6mMRKzmhhjOaxHUsYnmhxwcQQKOolNOjQziqxQcGZrHVqLCFEB4VOxiEwgX1o8z3Hxz0qhLaCPByowZDYGt5bkgyvjFqhXpr2wbSVz5JFIPlABFiINFR4bwVNRFQTP0smDfZPfcco4zA9BDj1OHhwb4YBkQL0kZLaJzACd0QRKSSBlA8HrhAgKgKLb4bjliqKvFKfMasoH7JRfYiimjj5CiqbNTmxXtREdTeI1QHCyccGgUPU6wP/K8gEopo4PkfbporfOAhd91UcZEyDuXgCypJwPSY0k79KgisobIOqeqrXSnwqBoOnS3AbkI1N9iIqFCW7nBaGZGyhLcfX8suEBGwbAKgHM7PrEmz2ZSm9eBDN+hkhpatQr1bxmOYhgcLUJgKZdF5hYrMmxDyGtT4XyvU57vF9VKLNj6S6p3KbiKTRVZEPTlMTIhuVx0VBNR1iGTuFUkKhFDXXY5oZOQ3hCvqBgvVU9LOQTOo2AFJK5ygUJb/RE2pMTraW5XiZylMSlm1ocrlHMg1pRD8FopFTI6RU4nVpj3QSj8I0JQmA1EhKdVQJLx+SqhoYdQDO7KQKP6BqYWI9Fmpbu0L1lewG67hsLPG6zpVjYIfGcCIlIQchHxcLKIvesNWaczg8Uy5MOjXY9ZhwTE4EFlA/BgZeRRK9igajhB5eyKjsfHTuI2loLQo8oO4MzMF7nad5XBMbFZg0cEppLoGUjweuABFHyFanoJIE68RSm21VKmQgMNOhSMWNO3XmUAmotH8aZQCEVO0vUAV4U2GTEyo3LRDpxNwzdl4vgaSwBpZxAzlkaWufWRrxOR81sVBU4KodSFoL3tqEXJXK7U2OfK5YJkIfVOtSDmwxSNXq9KTfXGtUaFhh3pLCWoxLYezypNhMPmV5mn7PqyZ1UHM5NCaKkOWukIHLx6ao5e8ypsAtSXIhyZfhsiOAqWKvgey0wA5hWlch3w1RgESW5cNV7Sem7UGLHEjrG0BvYXPdXp9mSZVEqeIErqqgUjgU9WBlRsjUq9RnVOin1fzATMTXYhZhPTGldc0HLkNhQmJTo5Fhi1AUQt2kuqRBGfMhXwu5kUWaq8Dlg1bUAhOjMBmhFcRU4YV0TCE9dFDyqfQjEM5sYCqtyIlSc+lCOpoY0kJHA+3Y5xzzkNb7WnE4E4EUE8fDgmxAZRpXMYGAa+KD2JCdXPsORwKpYmioArxGUhOXJQnCGRzDyMMw8swl2UbF0/FjfB9ld+gCT9OYaxw4PGgrKXR9XV04qVmuS9WgxwtSc8y2HpaUzQ9jGrLQhoNkKoWgGotwcnKJ6eKv2XTvANF0dRQj1VQFCSjU0WlY2lagWwKqPARhwez21ycJLPwU0PJhyitBZASh1XRUNDDx8024Zd1jYINatoPVPRMORtW5HxBTvbhu+xJLPSClsEaDZKpoaGhMORYpQC+6fDqOpL6aMyRdyZZNaBJMyY50O+B7f/mUub7LuaxvyrhI2iAn2td9MLR8mPJK0ldMNLTBEx0Y+cSnDaw0/MLq1ResrxPBcVLkWxXpVOAKC6e8FhwWgAbXvbeVo56XdFe5BeEoYOOxjXUCE41CPDHJTre7vKjOi/y2UTI0eqICWj42eSWIagy0mmgKxDOog8ePNpRTQMsHKK8Ev+wxppwC8WyU+4B2ad6aRbuOyOkGwOoGJ1aR02yENSEbgBnSVpSz4ZJGNYp/nmZYX4x/Gu+r2jryseqqgglo4TpqomobmjpXr6R9xSWvror9YBVXvkGJOs8F8BeU1eS+ubO9kZ9glkQATD4kABrMkciAqakEoQQPalktQacgzu9vWxTkxjlKMlQMZb+/JRJ1G3Uffn+LQWK0q+oo/ZJvUFr2BV+i5vq+HGt2X15d7qKYXJ7+t8vXrx63aVb+8fq+qnZ/ffu2bFCXb7ZJXORlflu9ifPt22iTv33/22///e27d2+3LY63MWOi/p3r7dBSe0DlSklOqA06SYqy+hBV0U1U4lk42mwFsMttVFRnN/9AcdVcvT9yq+P3gc59g10cozZAgDh3BJpc9PTg5O/O8EWaamxtb0if3oDxD0YanuBhESHejBBR0y2ph2texlEaFXih7FBRPfUa1AaPPE/rbTb+5nlOXvvyqcQMSP5msdDfzbGdlm29LkQE0y22yBznp2qb8mPsv5ljOcw3T0dleZRG5PqExsWWWIw1i9N6g7CUS/Cwoh03XKHUhornuC8PeL3jggpzLuKnGAIwx99XZpGOX80xXSVVyjFO98mCA++xngIgor/bzEpV5PxUNJ/seEXkERsMX1AV/U/09NCKbBoTW2KH8QMaNlIRKVNohxcgPvXZHNfnZIuZfXOV9xcUNEah0BzvBco2qDgofySbZmuk0fJl5ljbGn/PM27o9HdbbD+KaNd5eEFImWJb3Hg9PAAzJRTa4j3Mid8NL2L4MgvpUiR5gfcyTroMXy2ly1V0BwiY5quFjKkbDeAqP4hTTsowJVYyur5Jk/IekM1jgYjv97fc9s9rGG8FFYNT+HiFxUydiR6VOdMttJoBk3ChaaLbqGpPo+GIuo2tVvMhKXdp9NQ54NGY2JLVzDb+QJxH/Sa6Q+IwydKaa53gxuWTRdF9shAxhAj8QIaPq2GNzznufPIvtOm67ysOeHwuQsEAxzSc0/aBxzF+tVB9usBgPC76uwU2QhCE1cQu9CKDkStzwCpBaI8LWDdMwXq4fojb5sXrkoB0JiwurbpWmdj3+KhOSdZFmK2HQnO837LknzW6RDkx4LBYuSJznCdpdHe6xf0hjiTi0IFii8NHxWmMzYflD0US9VOpfL4YBaeVMpdV0TyRKRsrbIB9jMPoupVp0Uyz5sPuQX3vxeXElthjBHYNrsjmGEbu98/T+i7J+HMYXWKD8SqvY+BYN3xezSo4R8U2KctkDJ/oswJ4bA7cr0exJiP28rOWpz3iYFM3ovSbPxWeaSbxIE3zBxZB98liU+QYkO8RVG6OnfbR5zHzZathN11MXHMuUz2iMGAudfW1qsEnBUJ9pAFOq2RKLKya0ePxI9ruOAsx9dkKV6ehtW/rOIRMmcVKJO+6OGz9N/u7wPZ1DnQV2Ja8VDGfp6mnVMcYXIQ4WO05qJz+q7XlhO5+DWKSoWiZgxa5pjnLPmExeN48OODuO5kyy53zYxNh5ionIf2BXZQtnuNoKDeUYjbHDI6+VZyLAFtiY8bbgPjo7/Md2BeUN30QCF+pAwe/MJQ9ssrTSCDSoqDldd/mlDxf6+0NKs5uv7dhMRlUbNELNssIQU58GZEOguLIjmoU0zFluwwg1hxLwk+chsanJfn77Pb/kOn23cz9nx76fe/gMBOt+2ZlBzI7bKe74Yk906Xxs40CfLDbFfkv0ZQ0frcYZ4HwXrY5y4Rtji2xsMTvNhKMbMnsXMoz52Ga33XZZxz4Ull7Ip5sm7si3qbsVNEFFg5peAgkBxPfK/r74rN0rkq7aCKs1fUnktRto4KYHj/P6wzZjl5kHPq7BbaoEswW/TcLB9w2Z+V/IXx8qCLuNkwotMb7NZejHcrWxd1UFk9fRleimpTn2/YlnD8WWrgTRmU3Gs6VkPq++Dwyqe8cJk9Tf5oZGxrl0TAFFv5Ogo1AZhaYeXaoPHMOc6OsPe1OL4p+tmQ53aFPycePk/6+ugNkmIsKj0PM3KeXj3UiOb+0JRZafUmS5PHmjPGrhVmtjUHBWNTaT0s8NenrYJG3jUSFTSi1x3wZpRWMtS2xMMhutknWSyLWFsuUWHklwNdGTIFFD/uobTwhmYK5r4w+oBQJj52Gj/ZXT0MAGuj2aShc6rHc5wjv27C9gSta0kpAuvI5v0sy0MQultph7kNISpELABazFaV1FYmvpejv857umveqIvtQn+2oJ6Iav1r0qk5ToFPDVyvdZRdlT4Le0n603xVbyzm8L/ZlFlaUpKjuCR9xRpTxszmuj82zMxZR/83m3UHVDuMS82Nd8rIYKLbh1y0ij9h4pPR3V0cDAadQasfHzTHme1ImlcjNdJkdVqLzHNwJWzBXZI/zAz4bx0h0uIDKV6NZjxFYfTRrSWhZA8VaWnMavXov+80whdDxg0l9oo4VwnMc6rOVk0GF8LdfSRYDr7K4Qos+Cg9bjywftXYL4R1/Lui/WmN6D2J6b4Pp78mOGJGiVHyTwRVZnKru8SYDbeBMgcX6iR4hbNTnObTkpewizRrwfavXrSQXs4is5jTSW5RttnKt8XkqD5M0FS5AuCJbnLAzKF9msbc85J9RVaHitASeRImlFpjvC4RUuIFyK4cXVCQxiJkvs5Db/XN8rPtyQpspeW7vpKQ7+USvw7pdoDWHgRtEX7QaGcfsyp5P12lULg/Y1fWnukoIp/4EEKA3NwU+xwAqNFvy3BbiQszde2r48XWPxdELBq46DTe7xkeRH3lCepqHONCdF80dufT9BVRugT2Jyctq4Z5j/GzdUxlKodRiu8Y7MhYDh5w2RH22XtNX0V15mnWdAtc2B2HBk6uIDdaoit0VUAlokWORBc7OobCreyTeesAQFjtKXukbkQLZGBrvOKK0X17O+4J9bLiQiu6zO8qf92FufRSBczjQr4EeIK05jRrQNQddGnBFNvf1BR5ZE/6mCRkEOnBKYCzuZpIyuUnRabZJfiWbOkpTTu6DAJb4k1SwJ9LfbQ4PuBvi4qc+z3l4aEJ3SiWSWGp3VylFLBQu6bHRszfaYp1F9LMAiq3oO8Ezwr6O/HAFQ+y3Pg1vNVpd//q90VpBvY+HsFf/Wt/uy3oL635UsZPiJ0EPQ9j3vnH1hukDQjiNQd6IFMjBehkszOhUatPlz5rrIPlgsT6irL6NmhNjgffaCrqakcFYeFlUfFSf9ouNGeJjclsdRbwHIv3doj9dHUid4cvMsV6gf9ZJgc6qe1QM2iGDG4awbmFUhGD8TLnF+q2x3MILPyYq0MFmw2Hj17IW2mZ2+wyB/OyO3y0MEF0dfmbp7xZ+uFnaLk8qiSHjkQuU26y/x/5ZsQQ/DGFPjePHXVI0xt4P0VMJU4aHsW+lcfNrMEBrSw5lod1E5WWElS0EswxQbONtQtcU3A2EUqtet75KBUKibiqW2vmJDxXF1wZAsc26HLLK8wuTKrCRX12lo6c4RZ9Rdlfd8xIMgnBt4RwVSS7MowzGoZVGwWjQCJIYgrDyfL5PdsdZhE+mglBkimxwyoMg8WVWN5TNyTRK+9pH9yT1jXBZKYGy6X9Y3/nT8rgUaNt8svKn7OOgA86UdJGVTkYuQrJfeMXiyvisci8ysRTIxsCaxz//VkeNUYm3sDJF1sb/pv7BryhJI8g2IodyawkeBAxhMQ9JpsAullqcBvKHduydU7xwGQaUW52Sktunxt5xkhd9/w6REJZPCWhxlRLFP5v8EiTplPC0nS+0PG/Ls5YJB2/zBGfyNhtTCJ7aZFtv4XmHIWxbiB51LfAQ5i30dS4rxMUcZ0tsMTbpuIo8Fa9IoXIL3SjZoL5nHQpOPYIALPkIbToMCb9VA8VWUojsw4f102FdVbzhSiy1xvwjKe/TpKwU6HkQC8q0sjdFePmf43OpaCmEISzs2fh02FRNYv5dLVNiY48VUFnjOEs3AJrxq7V1+Ih4ZEB24bbAYk/eoTiJUqB3bIkbxuHilDweEa9VlZBuLXZXq9r2eDgLDuvMrcdZhYoSYjQIwEoLIIKYwYIg9lECWlkEDNtTAVqdTK8S1K5DTjJyRVb6DSqrg6oqkpu6Qkf59ibJmvM+MA4tsNVYsExsnoOS2Expwp+dQABz/D9QcnfPZwTrvllQBzj32p90fyQbHkn3yYJewHg+WY9n2CPU4kUB5tCWSrBIgZb0EA7qNhn4fXXvTMDbGdhCix0g6PvqNUVMU8xI8gsVT4TVBKsnV2avyX/LEsEzgi+z3THLq6hIbm/lT3M5AGsH6LPbsyK5SzKJIzRdbHPWLFGnMACmMbHUAfMXFJV1gQhdJdgZCIcWDraiy51Q6ICX/KHETQNY4K+zTYqa63LRviwU2uI9RwUJXwObJSUgjm0QGqibGCCcR5G3xk28wylHQoPZ6IZJJvH+5YqsdCbWc1jQmPji1bjgDaqclw9ej8XBCU9edRovPHFHtd1L+x6Dt3NcmdUdFObsGJNIcGPiiux7KkMMldtjh8QlX/ZyXJa7O/oSb8G7PBPf+0HlVvs+iNUNWz8LjXNMcxyU8SsLYemT095CY80M8smhC1cj9QLl//VI/vv8Mv+GC40jdVJ0dFA8zCusa0uxAsUWwuOxItHi0s9J9pMTH0yJjaq5uYOUwPGzJa7L6on3zaS/2xj5k0gw7DefbIzVLV/LfH2h8r3TrBpX68AvuyMXSy0wh3xZh9WHy+Rf/L3C8NXRSbi8yi9RiuIKxq+Dte//GXQ5KhRaXrZcRNkdvz0yBRYK0jQO7xObfZ+PO/AajYzhTanPwyB4G9Vp9R0fnL8IGrFQuBrlspOenq/aOkuBvWYprTmNYtk1d5hkUfEEbiN9kc194RaIKTd+fY43PJcoJym3M0GDZgps7h2+8m/Cu09W3oBFlJWJ4EzLFCwpAr6gTRIRDgfCHfBlqxEAdMf8pACNyUEUqKtPIw9IvzlJ3XxZzex0d3FhpDSDy/0J8twyO2Cml5BHiGcXyoZ+1uW50ilMLitdWX2tJqXnZAiiKSwzbshg9gYONa69GeL5mSH2x++XfvwOZRJaaG++qFOEPx73Huk+uzONy+W2R1P/32V/Pi2h3CjjV4vTdJzz5oHuk5XnXn3ThMURnPaG7zb+unfkHfcZ/hbh2ef9c7nClyUpSLR1/CkmQfUhvFD5auTEefREGmtf/3keB2lULqdBdf2JDoN0ozKvUymQ3f3xFOGKLvD+s2k24OadMOjUKIOZT91ZcBMMtgN6bX/z7n3dhis8Wx8/2+ESrdHjV3NM8Obgsit8j9KaT1vZfnrB1pbeeTWEqGZxucTF1iD4d9Hpwp+NTu8yTOo2tgGfe54tevHim3724OmhS2Fy8dJVVl8rr+O6efEJPQLSkiuy5vfPOS4U3WnpokXXUNndj/NHm+Hzani8u3VonfuJZ3+QK5kRnfutjArH+i9mutcSMEah0P5BFfyUysU9GXZJXo+ju78gmjAIbJJWqBhe/nI7m1hqYf5LNujqvt7eZEJaNK7IQm1t4wRzimv/cSEj8Ys27q5FqA882LJkYBnPYQ8h8rUop9kBxg7AIluepl56jdUPREhvTRc44GtUJynSodRGaTkvUHtzBmRKoYvWxueB3pOw2FwsLVoM69dbZH7ubh7up+UJFrn1GFSV5yuh+AUbULoxhvNaARC6M+0SPiwhGZfuv8ozZM/AvgzcRD26TeLmbSGl3QZgZRi1O1Ob4ls/e8MjaT1RhDC/GlhLJ5xWBRPCDvBltm9oeudLyTMapvgFLyrVZHka2RWYXUzuVuimWVFwH8z4f/7Ev/7vOcOfjttgOIBJlilYxnmwsQoDhuLVr9Yp1mmwFfp8LgzWuWKm3K2m2V9PohhVl3lRCTjZEkuMvU/1p4Q3GgPFFip2tkGP7TZCPvB3jkLpamRBN+dNVsgAui/G467ogpXXus5d7MfLzvH3qEiiDAzxGWTuFfjdecIK6UT6mmdyJ/+0S/OkhwoR0vmlBVV9rsECD8oyucvQZlBheZUEKLd6FPFswmyGcxgPaQkZte7/teVuRrkiCzk1QSKRRn87q6uz2wZFo3dCgftFkNXsfjTr+O1zNCaHHU1dfa16Dvn3PK2LKBXx9N8Xdx3cu1JNrCtOoiCG0wqfozG8qyQ1AELl5thJGDj8SUjSRX+3YeA+ryTPweN3h82PylQjdQLgYF6wEZ2f8CBrLsBim32VhTCh+W98obeqacxk4TerdZvJZJiPH3d50S8oDi9fttoV312YYbiwa3/EG0AKqJCtVYkNs9eui0sm4JBg3DGzPgbwpm6GWcjntjctdWGpNB0ebP5Rl5WYVlkotDAENgY7GWKxdB5H0fn24uYmG1KPmQKbN2zZTyovPf+GjSuc8kHHuo63DTknOeO2vgjBDroSdJNKV74PEtEKge3l6l6u7uXqv4Fc/ZA/ZGkeeb6o77G4vC6WVp1GNvbtfawT/gKMKbF4oFWivuq3gjO282X2/RRQeuKDgjXzZTayMqtQm/KIl5hUgdWTRiBMskuMZJKiIysFGxD12UY27uMt6+ItS+ehWeo8vvGrLSZRV6C/23Had1SIHMIUWKyEexJGMc05AyH1eTUyn0pK7CP0BzQOUl9Rd/0XQM1gBLfv/qMFy3QxQy/yVMyVw5VNqXoolVhPv6KjKI3rtHH76qLlcA/7hOLVLJOTvKi353npadMd0DgsE0XdaZbJVb5LYh7F8NGetWVsbX8nKt6GWu2+5webTYFK/uJi/GwxthU98Zcu3efj5NrweMNiAdZZg8d1oUkqT7PSmhZ5FMPHxVYaIQF0vmYKLLbKNsETt1X2H60eDrWSmH811H21sCAk6IFD032ysBqQmKa4YcFgQH23xyabSajcHnsTvQvE25bs5d9y8i/yF3yuMm9WcdcEVgZl3lDynB1Fvg77GC+l+s9LCDyyzEG1jinYCz9DntlHjJpIBWxEQAAVsI3e7igOJZWnkYnrk2Avm7vll0DTpDNxz3u60Dr8ivVg/yicPRaHNSivOs0K/BxldzVg7qa/WxyexJRIV7bpkJp4kByztJ8sdK065fit/TKt+jzH1fx5gX7hw5oMpVBqY9PFTIzZFwlShS2xua7YgPjo75ZOyEf5liwb6B3XWGSBc7cr8l9o09U9Eh8zwhAWO1xe6RuRAi0vwcPvXi87ydtC+xa51C2yKD2oq3vcaBeD5ALFDS199jIVZof9zQ7dNHteWIPh8VaIENx9sruqJ1Q53RCa3Ca87gmV22PvNFhdIwCYeVtnZGKv8p+IW4b0d0tsBzFJnSTDyZRa3Yb+SrAmL035A5SvZrV3D5V9lnV7jLFfv5J60yzUpjEoYgRTYIlPdHuiPi93BR5GGB0maYqnr7vT43GKpTZqeZvxRYIaKLacF6xlVrWAlyuy7y+MViy1EB1tNjAYsVBojXfyVGQ9Zx3VRYGy+Oko3/CuayCETQttvQusIPKY6RL7Pl9Fj932BF3RyaFs/GvBGDXUZ1u+rm8qknbtNItT3DGIvXkIxxaOH3UtDBD2LTSp4/pMEsqxwJCeLSrHBkPattiJBMXYeAjHFhRj4SEcW8B1xbUHQzjKJyznE6K5R+kJQiDJDMBDtA0S0wA8RNsgmQ3ALQxqbRXuoDx+teQPmOtcOA3OXsMVWZkZMKEOcUnGO9xxRbYjJqKhSYApvFyHyl2wy7DaYLtAt7gLaAPF0+LLbLA+RMXmPE+yqvyBCoS5UcgNCoPYeP2i+Gdej4+npJYYNaRHi2IgKgmIvb4hM49D5RZmx9vbJE0iICkJXeBwhtjBvTy1yuXcWFPJSQaviHbxHWEWgZQiNaSNi2yxAR5QDF/tMIlK8/jVEhMwZrcRfonKn2ijpqYMxq7PR79+vRd73H61w3T8uEuK1jE5z/ighiCAK/7/QhFAZb7cjYM/JAWKqw/oJuEt+zIgm8viodpB3Ox5n/IUuDiWQfm0BHGQHMqppcMo+ykeDkEAZ/ziYgUB3PCfHslRkzInrM3VjPB+DCh3wn56E/GODHyh/b7Q6CTdzRK8Q7AQFiutxlpukfyrWabNK64ohlJiqOD8WxOZVA3p3+IFKoWYeDpYG+m4I/e3CnrCED4tQCOSQ1m5gAxanmJACjCbS/sivo9KJLUbgwA2J8EEvtFmCuxtlNC7IL7MHis5IuIlvasr6nWRzK5oXMnGNStFwkv64ePLcqCij1EXaBslmXDclICYt/EpIi9uO/PC17waUk2w7SjALOReHKNddXWf4B5H+HNzo/kpyjZnv4RDgBp0NZdmvVniW4nPa5+SsvLPWAigdElbaIZmmiu2wU7Lr1Pqu6VRBLzCsdyenJf8Qsz1MbltzmwBmQtA6cJcZmimYa6+bR4L/d1Capdo8yOp7kEmEwrt8AIJmajP/waMG4ZXPfhztmfwnQpIcQuQblgOZc/90E0lX2axMwMWYnvL8GnZ96BJJhABEY8AAPux48PwDjqfQeU22lac7Ei4EFGP5YoccAL+XHyZhS6OMnLSENVt6rstNqCDTIGNg2NZCgm1ho823DSSvVE4oWDbAsALlqqDxAjgpOX4kEBRd0JnLdKgxMFqLJpfnzwPGPskXHQRkk2jCScC3p+LpQ6YwdtxsdSGkrL+uvZV3k/XPoLX8g6X7/0ZqN1ewUFLQFzbAMkgAbFQGbRXs75XslPkBOpDsgHvJbgim42qrypVewAAm1cYMcrGgHdiOEWh2KLvWH7+ADKD0d8tHEXrbJMisstwLqLUd2v5ekTi9EASti2wMh1OkKWr8wmkEQgrTQRYl1KB1SdPu8KAxlWpgOtOqVT4KwBNnxkk7Rdr5rnK+yszkHXo4hes39K27DDWLhGji7HLCMs0nBrWj78dAidLu2+2WECvOq7IzloG2SDo7/OfQJ/dCiL3be1zVZ9V02NxSVYurbpuSX5VRPFPPDDoepcvs8BKHDYh7YopsLyDRfBlMV9mrxaBaIXCf4PV429ioTF5rKI5DS1Dm4DHQP/dwWwDr01rq/ezifv+I0pTVIXRXmhcLnqLpv5EfLTKF4yh9okw9yRtDVCD4ooccZ6T142Y5ArcI8iSPjMXKCp5s1H/bX5972CzTTLQo5EtWY20uUBVXWQk5SjyDafMoHI6JCnrr1vYhN6uwgqvcHcD7dI6yYt2tqB1RxXa4G2mHTX2TXE9c4XOeOUedkpA+3mDAwKJpTacGt3etmY2jlnH73MIKjmlqeULP0KXgNi0QZ6cXeWtXsIjZ8uepyPnYgeXvPl5hEV6iMMLi83pAKNDse794DwquuOXGImBLrG9GYEwsiU2B7aRxpAzEFS+mOEx4E35FLeC/agwC6OCpCYTEnTAEEvs4Xt5BzzE9RJ4AjoHiWeAYxqRR/7l7qAiO/8vkvMDf9rugFwg/Xc7r61/1kkBOWv13y0tntFNijpRAeOWQ9n0+yp6PH5EAhmYAit3kSO8du7yQkjSxRU5iD6SBa7IU0jsy2Cs7yQDBis+LRu/B8QTtv9q4wrhF5ByLQIrQH5dGGcI0TVrTl2hdUHjgADmlI/7nLN+UmEx21hcF+QZfvdoLZRHAYTVzavAENM0645vXjzti+X7J1vWXBeW2wKw2Wxy/SlO0WeU3QkxPugCS3znqEhywQGSK7K8n29q86Gw6AIrI1/gYOfhNKdQ76ZPs6RKohRc4HzZC17nzQTggs/5nd8SpxA5rG5l7WkWNtUkeP4Qi5eyOsGxSext48+ZOQnmYAzaxM3yYlIYw0TG4Ca+RBf6nxPHbNGcp4njjBgquP4MH1fDQt5yzU2ezSjHcFOf0S+UCs8pqO9W1viiAv0Q2BJzjCS3DIiQKbDYuHdwqt6dS6resLcDeCTfCs6WP3y0ul9ERYEKARdTsKSlHfPWHX987r+ZY/lUVTsouA/93cr1GnhKPH5djUgawlh67mc9GpedTF53ImULjovvFA8/1AkAOpHIzyOLqUBpiDw1AxontUdadxpmadvmcYxfbTGJUoH+br9bXOSpNORtX2ZzND7d8EmU+m+rYUO844RgwwGNAxsq6v57seFlWnPB09ovi9xjOSVIXCqaD8pQkcSBbtt5bC7RfbQo1s7Z+OjZpsRlMI1frTAJSGzqAzGqrONT2bpdLcTHV/doi75HRUIO235MzKBy4GBN/WnYt2mUM3S3n+Y0gPwbMVznh+DDaO0bDXsGk9SbhrH8mUI4slse1S/LFDSs0d8tsJE7M9EkQX22MOPkRYxwN/D/D9KUWDU4AwwIYGEMyEveT737ZGWWyCW5f7miJb2IPlXb9DDfCPsv/d3mciCr8MLp37d+RdVDXvzk7wpgGCvfEbygn5rV2GcpE71gYRjrVo4f43usP6Im2rK6MRnoakRn1ylfd41+bC5uUdKqaxWiqtR7bin3xIBXDtGuyGL/nONCOC5OX2RrT8DychtVVcJHxBZL53MvlC7QgHnfwwvWNXmUS0edk9jmx01CLW5euCIL7iYJusagiqACIYOxbOVrvf2AYix60xLAz5S69L/xV9H0n4VxbuUDyvJtkkV4DShbYuCcW7uoeakBAqxm22pEQ/cpgOLfgbnq/9LqazePBDb8PZPTYox3AszOV1H5M4yzqojRRREywjINR9FNCxPIlVk9UakzMZUG9dkc15covk8yJLIqU2DpQgjunGyJxQE0yRodA0DJFVn0so5jhDZwP7kyi1VfFPym0n2yeml4R65GzxE+VYvO+lyhPV7QI0MoXKVMCSdLPIXI8zlRHaRJxCls3Scb7TrPjh93hD/E5KVcmYUtV0hsaZvU0tYRTWF8wDsV/7J1/Gq1T+/OMkAKMAUW848eKyzSBQlFf7fZNT4lmw3K+E2j/2qh49YZlkDdBsFpt2zRauTI32pUo02TCuCgqnD3/N82gCgdJIshnmlEDNU4j4grslNX8S5C7CJiMBWh0MbNjn+K0X6xUSVEB47+m8VRXggPbRsY2l+wf0m2SBSh41cLTGiTRN2s8LThy9a4nIMtYr+lO1uinmC71UmRb7ml0HyxeX7Evzqy83bdpU88iuGjhX7CpbM9skphexhzm2vzweYkcfMPFFf8EaL7aNGPfMPNaftlSSvoJeb5q+YQz10jDp/tcEGHrfGzhebX7JUxmKGPL7Pq4eZLlNVRmj4JnaRKViME6aH6SUEak4MYVFefyFYnpnuyTvTU3TeIuzFTYHeVLd5kW0n3vOCP/M0XO2/4TBjQ+NVG3SpL8eXR+NX2pHZZ8vM1frYa3wd0G9VphaUaPreQ95qlMFgIZDXr9ija7qLkLvO8Ge6wuNwMS6uu1Y7xknfZZxoSprMVXqEtFpW+PrUcMgee1mJYK2uvQ4n+km9Q2r61ZU+A1Hcb7/CyamsWiCMPV2SlqLd6Rvt0iO8oUPwcxUs4D/1plOSpHEzclW+p6Xuwpb2DzD5AsQvu92rc731w/0mN+09y3AttCV/RQ/kZVRUq8NoZMub57QwwTocNwhTRRPsE2LqYE1QFN+/hyO6V5IxGiUAhcn/kBUmKI3vIBBSvZp2d5EW9DbfEBHQOq8sAxzQLS72cfBZR2BgBDYGEu4f+o426uEtiISBn/3GOhbkQ058XTeLRTs/343gWlwO76xBMxOuBRB+x88u4my+z4UwZTrZkfrWcBALmetR8sfK4QhE/d903GyxdtILDp4O6uue9BIBiJ9xDUnkpegriBUuMLygq6wK1gUh97RMUKifrhLL+Wm0TU0SguiD3/sKbjMTKWvHsYrl28/8hwU2W3gobj82dIRUo9jz5wnnydLvLCxIg/zbxfU3OoHLgRk39tbLiSZ5uoNhT9Hc75yQoICX93fYFAoSPLZnf37Kd7AtEzPQb0ZcKKLYQDj8TLiRF+8VCV4x+8n6pzRfbt2NnGW/IpL+bY8PS7CRB6Yb84sytXJE9px3l2W1yVxeAm54ExIJbHqsiEmeX+myheTYI+sdArOrJFtlcnJR1Wp1mt8Ldyfjd+p0H7oPipQdVuppN4PIpi8M89xoRuXjXq2pPI/7DPfa6zOsiRkIcG+qzba/ELcXt4Vjz/v+xEtExBbYj/RSV99BQ2++2BrZTIcDx+NkW12VVSJ5T9yW2GA/zPIXwtd9ttNYshl8b0QWrEQvHj2QT/oB2aR4gCwCPzcXFTItiGinR6aRiFIrh85wKZygVLOzmN84KpG6Kpc/bmc030g8JcntVRFm5TZpHUxDNZDB+rejbsFUi2wO3+C6DL7My+banJ8Ho23+29YCA3UXcfUWamqBlmi1Z2lOD8HbyC30RYqkwBVZrUfDg7L+tbN8KYtNgUDnvWHubRkNeLAYqxOfrFgptjYBiL+nvVpv8rwSPToYVKre5melEYccK3KUMVzi/MtAyqtQeAxTPeS5WcBZmGkHbpz5bzT+R2YIRhP5uz02t6UQkKFS+lOp2dntb8pnM+2+WToGAK6CV62RUxfeXyb+49UF9tpgBvFSb8JEs3YevS2/NR/l219zEqjQUKZDtje3fk91BEd8LzlxiqQXmFEUZH/13+LgadeAwin+eZnjW45/h3KUkSB1UBGNM63YnOQ+YHD1wAq1n57KAF9Bt1EQrLQK9rAAwutwXG6FZq1L7PUEP4il1/PqCb3n7RN1huInH5vT4TIdiz0Wr46JOlodhIg6Zk/ulBsOehVbHQt0Jsps637TCNC6npMJqBJNdJrQa0TuJpvTOCdt7Cbb3L5qfjoq8LC9RmgbhKB6by8amRfFyuWoxt8qsDpGeasTj5EoprzzRwS2o9bM3QZJxwMbJtsTG377blzmf+/6rxSOtZHOHqr/nfJg5+rtFv5KKt792nyzWx6ojoU/2PLe91brKD2I+pwhTsirJQAKeh5IOIy5HCaFCMI2UaFoVArq132yuEQs8JNJ7wb7DlNjbx0X5xZZYjJTEE+AG2n6aVzY0mZI/oNGQx64UodTKot1d05Pf3LoWCl+KNDtEd0kGpJmjv1vNzsPx4y7KNkKSJabE4jyYn+Rpmj9wZ8LhqwUm9PAjyTYCqvGzxX4e85zXfrExDD/xa6H7ZNcLjEB44zZ+tnFm2QrRSftvFsbgsjxKIz59+Ph1v8vKd9mDsszjpHECBzIdtab5a+qOSpXQSIQWMxe1MH1id3Hz3kiuDUas161PKrBbG221A0KIxwi1hh44de4qKrD+6tq5Do9l335/C86iw0QfJim5tO+nx2S2+SqyKbeZaxanJ005ZCGnnUPtx5jWHTvKs01C5vPVafm1TtM/Xt9Gaclfs+tG7808WAdobsqvD3a7NCHysbsISNTyQl2PZ6Meur9kMGAnVQOeczWgDsBNym76sX5PrLnliTgk+pbRkiu4qjLGoMGcmINpZ9X8wfbUj0VoXMuzyWDxteKQsZaMOSSGWyNq99hXzRJDJ/24oUMzNyOcR80zgi+ous831yRxGgZRMoGshnCfSMN1YG2xAS/AjXjyAYs0ADNIeunHCAyp5maHy/uk+WnBD9IqwntEBtCWIyTNeLIEhzUAT8j6+YyZYpB5JuwAAMu2BlsWEFCvcV94CRPe70W9S0h0p94RIHCJe8kIY7ILiIh9twCnXda4c0FUAIxpMStE/8dFnhqanLgaMhsEDWZjj2DQr9H+xHbQ89BI4ZqbBS7QQ1RszvMkq8ouoef1txJtfiTVfXfJoPId0lYWvYWEKgZ8oW3IcwZYXAH4RN/hNZqudGQIJ3B6dcnG7inUCWH45JB68hGPLaTE4XGvkYH049ezUH9/QuJqRElGMiCyIMMFTfdl+F32Hy7bNGvNxXU51iNv5LZRQ5ByF8WEuBjiJCnKinDaTVSiFuT1q/76vHcE6s48/0yP0jbWYw/wJcqSW1RWV/lPlP3x+v1v796/ftXkBSXh1tPb168et2lW/jVupjHKsrxqhv7H6/uq2v317duyabF8s03i4v9v71ub48aRBP+Koz/dXWyM293TEXsTnovQ01acZWlUsr07XyqoKqiENYus4UOPvtj/fgD4Aki8HyRLpS/dVhFIZCYSiUQikZnm6X3xl1W6fR+t0/cI1u/vP3x4D9bb9/3uNVgtKL/+7wZKnq+Z4mpU6EZzdY+z3/7yrj/c3y6SNXj++y//791/swL38f+CgaQ0EnQD7t+JhO3j+37HjxyBxYj9/ReI+U3W+ieAxIEEm1bPXHErUB0j3mGZxO81W7l8LwVPx3dVwySPUbZ6iLL/sY2e/ycNr8hKJbgmvKvmXwXxDj/bM8SruYv1gBOul9BdyfogMlnF5RpcJAuISI12LnRe5E3CCPShAKsCrF3AddknPNBZB6t4gETiVLjgfnl3GT1/AcmmePj7L3/8aj4VRZZKYf72xx82AuOF7EtQRHXqSD+ShwEy4T6eYPqb6UGogr0s3wCkbLOjvArMdIFEh3Z6oLEC9yOLdiTnVSLCTR8WjhZi5sCeyuMUG62OWqQt/UntPIYwCDnYz+FFfzCxKk7atgtD0wVDB7rIjYboub0kPgDTgWc0MKr3r7/+agyUjevTFT/tKUKWdkEyObxND7LRzafnexSXaiWqZ3k2r8K9TzIuBB7DP0F7hjuE6e6yTFKDiMnGnf727uI/lgNmLXGWiLRM1v/2jqzCv737gJhkig5d5MY7Qn+1QQiDxakz6yDf/sLwg9nv7/5b2oHFApPhqC9bgKEo+s0bkta6Q3/h10J3COtdqd4/2ExUzcCTMsZxOYrtwxj8twT+qwQLkJ6QRHEy4KYW4nkcbS62CPUmA5jng+ZN4WR4ejwWWViwo5tZ1YKvEuTegLzy0B7AonTY9dqereb91WKXa5gdxPhrgHs0Ai9yXBzqOi43MBHIs57b8TYtV+I1MYShLcrX7Uuo+qndAYix2Cksp17id3PRJtQUpHHz1uSVsPqoetblsB305NNc9QwhOKkg+hbfHBm2twEi2tLEhGIcwGJWbwXKlcoxtzIAmktOF+m9jZ7PnsF25+TeRUBqe6ZKOM01Z3S2kaYWrYuDsdKdlXDZw5FpYJPLDX0dm8bxIayGyS00ratWAxlpS5N5cLN7OVrgu4yr5HOKkzBvnBYB2Rs/lSAvkHn3PS2cgNmdeHhuyyjDYRKAZHms4OAiTAXE82rG77Nk7QmS9fnSSEEcJfkTUNiAr0NNYGotLCnSaybq4Wu5vQPZ1T1eOLmLxAf2FbQhss1d6uuXLjrzrJ157ChlFzsm4FDPR6B3ED/a7bL00W0LYTPpijWjntOR1MizAmYsw4ckvJ9K2A5UEscuJCDvIeaD6SS19XekXltjeSSJzn0DFQVZ+YJ7nmbbqDC5GhXDWkRx4RvPo/UWJifpdkuFvjhGDXo5Bx7d38MYIil3Y537KbAuis6AkPrNLE+ZdaWAUAdNXVefhQh9ifJCsgkZ7IRLBlQPsw/mmMn3HhPEWkhW1+d58SXdwMTX+QDBI3KNNL4QpCnXG4AW9CG1VDYR3F7v6dRBphYyQULj1c6Y34wjTTEXQ8A9R61N1/Rwjhso/QgA81nBUQVRYhej0K2nCoj8nkELmxpidUJyRIqB5a5+jmFWPJxGna/Dbnm2YNi1qYPBJxJ262Vf/x4VFV8WCOMyd9uWbxETcAivp1cBrNPYDTW8jJHRVm6/wxwWPnQ1toePNr7sqwbiKXiEK0A7yd0eVgwzPJmdZgcARIdavTcGwzdXZugMIejjo31UbG2e139SFG/yEz9HEe7n1qvLGzDugdUKkmDPtYSFjhmZRXRp29FpZSPw4Bo/NExWFhc3ve4umJxQjyKcGFprgQ8+gf3mBdg/4e46zYso5sXo2d0qPaBdm29r2a3e6NkjNAdfo757sFoFh6DzlacZm7BUchGY19aC841i7iVw4Sn9AjCzLvIA0ay3DxkA+vB/N4WP1g/I4KoH2+o6tHmZhWz9PY1q9XibKtTNlUvXQmHpv2yid7lD0DThbJIJFd3dXYbOaGrHmIVnYR/ixo/jdIOtj0OQX6u3qDMMaNI7lmm+O0bCD57UwXx60CBJLit07WgtmgojH6AWVdnvYz8vohseRZv8IqmxdApc5CVSsE634UcciLVW3zO6bcJ1TEIN64S+NLORrK9p4Rtkl2TQ9an8LOPTZJkwZM8LZ5cUwxVZj6blGCfX6zp99QHsxzWp7rcBVREr8sC3LQfqBhFfK6DWiOXwEa7LKI5fXASHgINxP8GHzwiFNkuak5pVmdg2b5GHJbN8uKRQa98wvUfZNAJe10x0E0l/Ud4NDF+H97ethjGfmtdOxEL0YkXdRDiFyqLcejKhvMBrgN0i5RX3iHXEzxdIX0mMPJoQi5+l9yUSdWU90E5YmAdZSPNNaEUwFJDjPQk44EX+Cd4XJ1HmdJJvYLhbIDfgXyXMwFXxALJrprCJbSY2Aq8zZuSK9cOvFsqqxJNTwBU2bo7W696QTuhf5KfpUxKnkZvrq4bhNjXfkrhavg04J8oum6uwq/sBPKunHjWQs+cdzMgqOY1eRBC13BQ1QBIqSAC6S/fnKF9EyIYBPmaVhWRxudzr73K7jAir4n4yAGirz4YuBtAtePYVqH0DVmWWOV6etUBOXlYxqNSGm76j4V2DDKaOy7SFSDZ/AtZpYV2QS8ezBHdxklecMMPP02ukZMkJLYobaCcPOOl360oAK7iNYpziGf0rJ7maP/w7Onfj1C5om7RA3UuU/0V+ljuxkErT6CYkyNTB7vPkES0xBKwqeeN4rCrS1c9/lFHtwnDQ5NVpisA7eowg6suc+B0uf7g4Wu1eMPFG75f0qaK1jkl3mwZk/cP7F3ICP0+zBr9jQOXqsAF7HK1+kvSsOCO74yMVfLjD8C4qHqLDRXssdTIpyPaFZgZuy62PiangRc++4DUwFgXYucMh2fWzNHa9a/oM16DBrAbpHKcC1jVECPzb22gt48bH5ctxWRSpKPmSrl7ArX/A/CGGeeEOsFZYMUCLD+0+jEvIylOODhUEFFw5uaoYAN73x6t4HXaA+ix1Qi7YA42x2CE4UWxNiNYVFzVGe92Fnwd4uKiiYdfXX54gN564s6QAWe4si7WKZqCCwAJUq/FRx0SHolsIqrXrtLEhCwHkxVFRZPCuLMBJur2DCTnWBRVWhH9THC+vi6a6UPEDwM1DuOXLnsW8g/8B1wGhfw7Lm3ZX8q1zWsB+FY6vCxV+mJJOOpSmRGADYFk/jF42D5jrFk6Zpg0fWzvh95vDY+32Kn0EPH/n9u/eensl0O4JuQ12H9xd9XNJLcKVY/gIshe8+s39kGxvFy9kc4z5lsB+2IIGHmxvp7c2ZLD8Nsrg/b3oFokOgrd4lUwCga/urzK4gYl1JHEHwIXe4ygHtdnp7CVsYV2CKC8zgGdDyjzzJ93tEEdbOvbNt/nUDoP/wQ5lcR1xXCbrGFSlaTg+cVflUoG/BtkFUl4+fLQMQMwGn/AWD2nl9kXWjNslFEx8BMgiS5kNt9VHSjuerTHFDyGgTRk+ZZMDvGGg+zXetxwL4goR5xhA1KA0hOb9CNAMZa/zNG8B5hjDW1/J52jX26UJ/XDMyic3gOLroUszSSROhqxtx+wOLRxsXBlcA+q/D32rGuYW1CnOd+O2ofuO9TtOCzS9vqGePeM5ieIvMPkpV/gWtTCj9YZrK9nRj6Etipcu2NHu7gtGyhsR48NAvQb9hMe+BaE2ziMSDO/nvp1vZOq4GJqOLuczHKO8gH+KJNc4Ije/TRcgBquiD9ii/kkD4oq9//WV1ZUcwG6iZKO4iLSQD49x5H4d7TOM1vXzgjdwbkpfXuc5O+ruozIuvqOD6qVdjhT951fNQfj124U1qccwibI2QASx9I78YLepodmlAzsEOsvtFsvG0cHcU1k9JU5xmbtEZfH+bhPK/tXxIXF+m0VJDvvxqxrbdLtKlxQQp7sh+bp3RsnmufwlWMMILx6rN/ts7wDp6egBDkHtnEZFpNY2Hp7QHpIq57551b8KdExIEe6YMIcUKfT7qkMQpiD+ov3w69BT/eaW8OuWeHMlKHDbP1fC20n7tZ20nVw62jvqTRmDBSiq3w9l17RYuhLl7v6AmlN0RUMbM1O3XKwQUCfbjUAw3RJkSNgcWi/yRXlH0scEUx24tMgqiq92WN7SV6o9cF5u9NMK5LkGSBcvXfSCX9lW7/8OwSZnCNYsVfvXXy3vdQ3UjrHeuUFby5osVfLO10PkXfhCnFjdvKq9qtaf5rZ429FJ52MofC+xznEAWcvHJYzR6MsGkLwIjN5TsJ5iFmzhxnC/R3HpP0hCZIca8o+G46/cexOGeTjaeS88Jr4OLxebBHGvylzg9jwsuOKmQ/Tf5HAQdK4ZNRin2WfwrFZkFsBrmfyS4rhNz+Hy3uQ9r6+ftV08phcVVYw4DhA/BCGd+K6iDsi3RaLX3QkVT1lHViFfg4hDvW1UvulGqRnPFCC9KIyRmLdPl532OZyp4vah3N4lVKEp6yyysdMEzKrox1wcGfb6uhWQSl4OQX13xNsqz6qvk+Zs+e6Y0qcBQ4wbN1gX+ClVdScUxGdey94hPVyY2FIQhFDrvFJtezohcJGfIwVZdjk5Z3axZSq7hxZIMbH80uy2iHtjer/JMTWhJGPRPVwRdNs95E2ig0s0n/FV3IRFIlkpNDf7BMeKVDah+P24/mONJqBRfPoK5xDRd/tK2HkIa4NPvy+xDFA7z7ZElOopoHlaEF8HyipnCeOp9J/ST5F3Qd+pKmWi8j2l47o8hBV5GEKvu0W4blw+7bTzaAWKRZoV1CA2tBM4TaDuZ+hWEI7QU+1X+IcQF1L1LN1Gm0Ndf87Xoq658RDvu7Ru/i69a+jfowxGCTfn5SHMeIDqNvzSMuNUzHGMeA+Z5NQ+bagOdOusoeaPMYOlL9unLGlHeQ43CVqBjXEZIP34W7JDdQD0frlDuuvJ/9jGSkNXr5Stp+IQxI68KourewKSEBnCoKLl4RA22BARPvi/13GZRQMZcg7v0Qwktg7u2evAoQDm5iEsAVel2/y/ZdryMiLxqp1WZgJjnV6W9MeyxtqXIxAnFkM/URWhnMuTVWUDnbwO3XGprQ2jzSmPt0QjCJE63bepDjioxR8kEZ0/P6H3itTjuOMsFoSlz296X90Q1tnzLs2Kev0OhMtrcqneiq1ffC/Am+1q7WOZdH91nPlDmHWxzLvPFQ3L8b5+pG3F+f3kXK4fBe7Oo/V/oWmkS+n6r2hE3IcjDGQZ0RnuQEkutjm2sfG7q+QnVVncBZS5q8j1hFtf7h+M3uzTb600uYwUffR5BB7bZNe0Wd+U7YEp25GXik1dL5GC95qIZC+0/2n6lMRpdBCvxxtaP5WwHahM4L9KAAnIe6gKlzd+K5WDZtBvmdOTNA4Yt0fqNTyfSYmxUxFU9XN8vQ3kJgK2riSR5L78RW+5hF9lLmGO2BBVY45B06/+B/7diR8dmKGF44hOv/SkhZWEV+p3kPGWl2shwwecUDFONxbLVnsTbKswH8IuOHEsBSHH0dCvc4PepLGFbmB7+4iwcaElbIXxkyhelTHhRJMkyLsFeZ5m5fY6zQ/CtX6b7uDKXObqbm6vZGu5tZd4Dzfafm7vLq6P1usM5P4rXUkTHXi17H2mczQNtfZ2W0LWLhHOQ1i8hFrz5VN364LZ93wZ4+k28n2waqzquiQpSJHWx4GqbgZBVY3KxKztlUq3CcWrtqxckwMWHiYIngzqaXLcRzV/Ha8gaigCqbOEVqV5szgnKgXog7kVdiA6X0MdqY/+ZhvDwewJJCO15cZQ9+X7pn29IHavbe05XuprbTY4qTiuErZQSTyj/NVrW0+h1l7E3MbnuHc5yTo2HIJe3AstNEo45VwElXfDKM7r65MDwnqxI5xQvyIz+lAStn6Jkk1pdd/R9XQ7GvLqUdm5eEiyUD+gcKUEP5C8JOVUxaroRZWAR3RA9AFqUURZcYoE0LgICO+Sae0JUlXDK93iUBanmPGj3S5LH8G6hnUief+rt6+lhW+QHotzed1rDrQinvbWgiMCsiSKj8riAWvsKnHPDVghhh3CdjO5J/JsS+Vpdg7uwFN5QcXxeARb25meoV9hwbtNfwI/y4eAO1rhmlD+gKI/HyGaYHElJJ/38LUh//qXHiGUnwzFbu4xPHlY23TX9JPpl2MYxzDZ1HeNFlm0e/3drjnqYj3W2AwAuIQukJ7IdCzKPiaWZPmAVdde8whKVcbNLsyxls6TMstAsno5Sde+AFcAb6JCNyTk361X5W30XG9rnLtA43BqQWYne2W2KO8KXLfuIlnFCNVgETLMYGfP4wxGKvI1BT/GopAZdBxKa90wDoX1YKNShgYyWKzmgzFqDCl+iPeGKD4HIDRPxSOHZrB45NDcruHbvEwXyElwQXSpSqTrkUAzcBzFURIwTrGrU0rKllIpGAIOFWwIdPRARIC1fWI8zWGeomx9naIdO/8BMoDWiptn8+QBrH6mZffkyreHZzCAt3xqjVUjcKGbOh3v72EMI9eqN+0xZueFRuJTxecytDyqZXmC5p81vKymHUHBvf1MBEbJm6U9oM/NwRjlP8FaxDpnTE8eH3/zBuzseQezKnA7TboUoR7h/ieI/NBOy+UpRMqtOAVEDu1FkgJztCIb3Oc0XnuaqyFwj4JAAT+Okp/ezoY9uN6WGA334sQ3SHK30j0v9AX24i7ytCHVGpoYBfV9kJ81USLrNYN/kpVGXsRFK7Z4ShDw3sRNNMANyKk8i47aaIcvP/0zZwjYI9boIN3aRP5Rvy5x3xz49g9fR9DXzW6/6LgPntYg8eELLcJdWVAvljw7705BDPqJDw4jWog+LtyAbQQTcU0IreziEX4DXZ/Wv6ZFW/rE6bp/tQK74vYBIkwj9DO5IvwcJeurRxMjVz/9QX1+/pajQ8NniOTgMCo+ts5H8wTlbU+n6xDS1Xz0upvbCxz7JaotV5/gPTliHJpcNXSbz2zX02lyv+Vg/QMWD5by1evujErYElljSvIhSG9jflEiYFV+WQTH5ca2mQf327rAbsmLvEGV1H2IHNNMNcDQUXLn8YBzg4jd4Swl3izLFqK/sKYFSPA5wBeGFTh/6F2CPKfKoDnnN29mhNiSjq7sETRju7APQTW2xHqNMprQCryeNg2Kj9whuBYLyR8S+jK4HSj43e8Y1IxCSei76uYYVm3moVnGjhaad8HuH/3fOzZp9JwfEVzkDSgvVtMXJOJJl3zQ6YUJ1vo/wpaSK5N1DE6jIvIVtIw19AnJSRRqRYirvJlea5AgPBpeAD9X5VBOD6N2yYRWRcVihQzrqlwSnJk2d1RzN4dp//IBebwmj3KvOO4ULE0g9GK8rAzSPHjQk88zp9+7Gtrds3T0PC5NfEX6Beof4K664X79y3LCPeA2i1Y/YbLxeH9L4hXD2mDkEhb4uiVuzBlP4MbYv5rVcSgenZZeq5dIVU8PV4BDB3twT4q2SPyI4hgUB2TM8F796QhE1W3ZLtP9zpAYZu/QclCENZ4q8KGD+ZlRrvEzQyQdE92BOgmvTVqrGxDlnbfKwh8gMHCdy9IfrbcwEYQ4GhZLMzgSFmWW4MK14DDSQ3t4Ne1pV5xcifq44KgW03maVYLkx7FSiyMgjlYNx7INUN3APaPnxr1sOm5XwUV0f4+dVX7AeVQuPN5SWsTH4/DqfdZtWpl3Ar/a+A6EEXIYLh5SEml7EmWHc85x18nXUVYfDJ1c/dU1h11IEd3XxcKkJcA9lGjyXWbiW/xwDxnR0gUZLh8XsjyIjz1673Tg4BHqIShBXgyZLK2anqMTPBfop+3Oz9MaHAL2rxJmbjffxI+J29cr3AfMi/w2ej57BhSlNmAQkBM0nZs06xcUs1U7uOJelsbuitxX5eSLnIQvGFxh82IXLPMp2quAgyl2PKDcYuceggib795GNb2V7fW70g1cPqsywy/B64dhB+Q875NuvrSGELz7sWcZOMJSfQiycvKyikGl4JymB4O5BhlMxSF+eqYJvmAm0JwiSXSTUY9mS/Ce+5qikcACRrHlpRDbe/bvFgnH0U9fcMHV178MKXI5NrTG9A4A7PfNp24KjDnKK/71EGS2SohQJ3ZXXRP6t8Zt6r6cJbixgRbWnv8D0VOIzC/gEcQWlUHSzZJ0/bd3F/k38l7rb+/O8ZhWOYzTrNC5oO5f82hBx3U+vD4p3OlUmf3NospsQDWtl81lA75lA38zu/b/sLlouwdZBrIQsL26iZFQb4aRlfrrgXTnLghHt8PnotjxE+P0FLMp+77l/HfAZgWT9bfVJgHiIShX64zsfhLDeziimJ6TDMyr+HDqoVTUu52jKxhBXJrN3nGTxjZeW6a3k8BeIImPQ0gb2nfepG0m0raIy413oF5uhywy/Oonu8HzC1cHdTXsQxARCXVlVM8SgwBbmXn9iVwiQB7sOyadk2OBRNOwJG0hvn0AW/A9yiAGdQgSTAg2ET2tCteGWlUHJld8+qdRY6jhJKmO3H/9EhRkC1Udo6225Tw2dqZxoOAbN6ULwULAz9NsBRCO6P9HcYz9FE7lMD/T1eG9vYRAx/9UVXl11KCZz8U2Pk7X1B7sFkKYJgVaB80TzK+geEqzn76n+jqD2yh7ISuxKVxlE4bJg+IYEkpAnj0jKpMNIEmFXfHjAzNAUz80qYb+pnWRWP5hc0oWF2VjYZvHd4Qsy1brki8pBmDAFH1/NdLJWzRlVNJnT+A91j/2UqDbl1qeS2Qzh8IUp+8+I4WS/O+PpART+wwt92BvEIhfy+1ptWqcQkw67EjMii/sOoinIEm3MImQMAeL5usNeVN2a963+X4Zkfceh7CfzN2lFu6ctkIqE8nQbZT/PKCAS5psi6QFTG9Hj/hNmdDlFBRlJVocLqPVA0wA/nvZAnF6f05BtHKcyTH61YMPjcQIuj3YJwCW5zAhtoLT1DVAAqDzwfxmf1GuVgCsPb0cPsuydHBLbptRcYOvOq8BOt8LE12aQPKT5sBKQx6CatQ4Wpmv3KMYRnJz0yowJE3OnndYJjiv8Z2N2Soq0EIREuFhtd2/ewp90xj9K3gukPJd1t2dtoTrDKaZ4ytPJJ+7q4RRKDamdk2WraoVccUihuoi/wzXaHU5HRzKBCnEescMcB/+jxKUYE3y9R8VBRrmUF5HUISb23VMZyfbAJOF9ijsk6EzhdhlEBjW8HTWbefQJhKk6uWyduhEzYiWO3RYzl6s7ArlNmUTbXyJ1Alf7bsCBmsY1UJhzne2d4D0npTgH4KS8LGtnWfp1ruZdJt6B3kDdvGLGVwt22tQ8NUV4vFq5Rvkorz7L7CSpxWy0Z74xs3PfZtPP/ECrYbbDDo+w0NA/Jwgie2xsiwVyPZ224pBsr6MkjKK45cAlhaN6SHoTm4NKHZ//MP8gqy+eFFv6ixsrWKjVC0Hb/hep5nIlaF3p5OjWTAkVtNazHPl6y4bwNXBaZHHLsceRPYpuI/KuECaj0ggdaXjM0tHtN1FcJMcwoLkrRnLuEL+dmkHTGuPHP8yNXyGmNo9eQu2SKMdRhxwkLPY/tjCl+kakERK3j2cX6K8qKBnQL3INa3symKpHiApULZ44Th7m9vHSwKJcTuWPvJl6HJ89a3D8APHTWTG7A7Wbx5h/W4IS1t3fwVP+ReA1SESY6qA3AGocC7lXit2Ck4OWs77pzweIFcdEpsk7Ow9h6Ox664h/J7s7WorGLHtN4uj9Y80w9Vqgr6rOk+zcntoqzHYGpw8TQyZTteEUzu4mn9usuuM1OJsoxUOQGrd02Vjr7q9jLK93SpxpfZo0H3dXJcatvRfLSLdcfZdP2dznHg3ckyQW2cxOH45KosHtyABClhbE36iXdzAXxDlZQaaEm2vX00ovQU2yafCpra6wVMR+gFHqLyqtYCdIplN8gMxoN5kbFwZu9ju0gxndL+Hh/FwPIiAnafx2jxZk2awckwiW3w8cfABx/npTSVxNwD72Nd0WJGV6+wn3DkdSaKfwKV/9cTrKnFz3KElcQ5BvMZ/+X/b1UjQSZrcw02ZMYFtTh7Zs+cii+gpdHzDHJfbpH275AHiDcjLuLhI7gc3B3YFtKqAcoSd0+uTtn+It8SLl2R16M+/dKaiY9Py+KWC0k0HnSbwFgmGzQkwLbMVsE1lw6JXwZKjx6ZqtOQjfwNzZeeHIb66pHK6Om+oJFXCcxGG1t/saeV0dTd8yRCfo1weiPxXy+flF07PgioYiyLzpe4rgMdpKgp10VLyaI4COw7OnrEFdAp2cXowxQ/qE4ZV0bZ7afT6dFa/j3fofi2UTqbcTX2tSDebJDI6gW4WcDVSGxnDxLl9b7MoybeQPFVz5yoPopPfHa2Nyr2hfG5iwdNFeVcdbX0DNghlsZk3Al7LCW+Luo8QErwS4CO4pNLWWMZmGkV3Gu5Tb76hUXxDNs/q8eLH/6op9776iV4xxVzzRfgjRFzRH8Em8WytFxv5dYgac9/oq7Xky+s1nSOCK4RI+HzZ8OhPrLR9eagaQat8X95cVZ7Ntqv7+xyIjlL60YguAI6jYvWwgH86GSPXaEVX6UPnEKF5km535E7ZzNgwPnWSq+Z/wt0RAucafBaDKOlSKnvcy4+j1c+LBM3O6ufBxWR5KAY/bcnxyYO/RgmnQJJ/H5EUrdnbGwzXpOUQPJkeCOdwHd3Uyn4TgAMVgFphvs3/gc5/fUKqxeAQpr81ET54sVB+c4ESWrtnaZ4vQBy/Te8I02sQZ5iUdRGm18FzmVtJK2tPUi47GMvGk4R/95oWkwbMnPl8IWmTOq7bev2ek3/A9QYU/0wTJWTzhH+wUCRQt/FRcvOS685M29umOqCnZOayJ6q6dPRg2L+FuE2PVrGdkFMA/AVKYdA41/mrUnyEKIssZbiXWwGOKEMoY35aDo96LmkgVjkvK1+v6S2OdqYAL77eILqKFGk+BZRbz7MuBFl9X43/HrhQ7VzzLvq1kpcQOtYEARqIBQ7HYAMTpoae1WXQQ/p09ryLEJpON1Rf0/M0jtMnJxjg6QdM1m5ALlbqnMDGC6R4Uaw68/rMGM+TNB6m23ZFFteGGtaFcoV6kucncaSojG4B14OVQRbT9JYGQSOMtdH44Zvq9BbmRgNiObQ7uO3roTjN1WYKM5bdjcPS0aKg0TfDoOsZIsAe6XxyN9l4qm2msoGhO5XNWFZzyQxmxkmqq9v1EY2/oTh1XUPOJn35NMaM0uPt66wOaDC0otjuIWe3cTeOMLH1UPs6pzT6VjfdgWbyOiJRzZegeEjXuHTWAhRVI5tJZaDpziwzqtX8Doc1ZHGvv9NMD8kxQ6bfP8hjtgdIHst4mnYW3HjzzhnXMF6vD+DVz3yjz5zn3FR/u8/2DLT4PsxwvV809/3RxkqRC3dbWXM0mJ3+HndzFCLQ4G+FQ9U5TBnj6uTX/P8mjcEYB116vH097Q5osEOj7h5kvaZx/D3FpYQnvTHRYIbrIkOEHiX5k024Jd03xCTgxJAn6ZY8Gn2t/K/p496TWOaVqADq5E7TKmCEJsHupqvrGUI4juN0cyjC4WsuMc+u09wisrvrGWIuSbZNDB6pXKtNdC9ms6XSImNk19UJBcJf/88tamPrBjxC8PQZxLv7Mk4sHf97MZUMwdbGadPdCZUfUV5zPPTEvvbZnOq9i7+9v5onb9sFTvXXPXS0ev9fSeZ/gvwWcTH2AOpraghJJOtHeZ6uIJnZ5mo3xVkLqwwtNyAnyWSWTdLwnvCfJet3+PiBa1/UDWqMFiC+/0v342UZF3AXwxVC4e+/fPilv2SukioV7Lsj8gwU3x7lq2g9ZAciYy3EgYM5iw+3AYvb/xoMiZYxyKrqRydpkhdZhNg9XPMwWcFdFPf50WuoqR4wpS3I/pdTsAMJjhaT0a0zLp0Kfjh+O0xvBlT8+PieEioNWYN/ktB/gtseCRqN9lDK2K+vQ8QYmvZCvhgXSRfPhv+sU3L1jbNukhmPFj2/7IdRBI6LOoOUoEUQwRP7+sIIH582nYFpTCcTQi76tDTyXGvcua8aCqa9/niA8ihn5dxkso/tdHKZxvF+7LcYU1a6yA97v6sSMvZiI+183Msh0qYzFVBKKhwHGDQ/h1E/urPoQVpqQrT0DGo+mbwsiqgA5G1Ggk6PJ/h+exA1SZla9XfGymp+G0V2GHwZPHpfwhhUPP6EESKWHC0bqkJuMlFqIns9CtGvf/nLh8HMdZCaeG0aUvvbvgsANxh95lMvEVr3NTw7YTBfoiOKBIPc5Of6ZZvga7nguL04p3nuSX6kTab/HoSHSmA90xA8glRJX78IhpRHAU0kWbdRtgH923UrBfFq5cpkjicQK/GrqLGl6hjGOAMn/0WYlUwpti8jvbe/0qA1Fsv86aWBFKVIiiWfiJluXjXSXFTab69m82ooMtm8JpOrJjppP5x6DbYMDt2Pe+/ca0nZDwcfJI/ylpdgDSP8GFsmPkwjevrYD0YbVY0A67prfgsiDGJSwwhEQ43OUDRu08lE/VqjFg0u/nYzGMoDzCDMItL7FMYTbDDDrsLE0qMzYt12NuLEfZ87mEzeLB6KOPE4NB9x6sIFp7GimyQBHvWS6jDVpH9gjN/2x33XKfzsFqLpn1ibtM8mj3aI5TiFTI0+VDsDm77MPHY/jqJcBilFeLgElq2W5BGES55CRTCm/CHv5GKm8AwaqYtXLGZGUz6FmElS7YwkZkxelPE2NCYDDnOQYz7s+8YmTvQjGG9+mxtNwl7tb0IR4zd4NfucsczNca9jpU6x3VkpkwOQPmNJmEoCFfnJJpPC+qS5V2qP570YfHs1ys7EUzFHPddKmELFTe+Tml6+RvRK2YjXtSw70ChuTiZhW51VSam7mF6sfLFfxvF8ijPvCVHrNQsjewwvxvCLaqYgFAmjOufghCKpUHbieZdO9YGJpLlQTCuSqlx3I4kkRqPGocZoT+SwQ3yAyuuSM4oiA7ma7sUJmy5Td99lu7FR471P4zxEkWQ/FSM3hqLrsWMECdTOBCsYXif165SiuVf773wFc3TN6CqXM9mC22Owrq6ck39FLICCFq/G12IubzPyuOyn7puX0I2u7+wlbyaarvEJ1e8Zr+6vMriByQhPSQ0chvv3lNTELddj/eSigLQCfATZyy3cSjY8uhGz3JkPM5cIManTiwWN29QycVwm6xjgVL1HRZHBu7IA5zAu8Kut9ovqZoBqyZlg+uuYEaxCyuRIDhqHvE8Q8TiojIpp1cGh6z0f0a1ldW+Cq20XzP5dZtnJOTOdMxIzrwKm3C3fpEQ+6Ezko42jFNRJnFdobA9pnmS9osDYPkVaRnvdeDZytTfb2oRCNb6yMns3Mg9VxcS6SQqBzjO8kUMET9JeYXAjjzKdYekOs5K9vVFpMxC28VWb+cuBeai3xQ6s4D1ckU/t2XZ/hI2PPw8vUctXIoAC8vZBFPmoX+3wn0sduujbZ7U86AlCqNtxCa0aWDYNwwRu2IuQ60W5jFid8fkAZqpc9akNLTgHqZudhW1yhS2jYGqZpyrJqmPZ52JFdDhzk3dQX1+JtUCRZGAhTH8xTwuX3hMcwZxyJvMQREx3wqeUMn4B7nEF7XuUwSgpWt16km7vYEIaTh4RIMGNJ1rS5q8pjkBGqA4WcwoxkMnf3pzNZy+o4++7rjI69VFdQzz/UUYkiu9bAsUyyjSiZYH98CrVo5hBsxY9Gu25yd/+6kQdkXy12m+fVF7vmJ0vQHsaUXslBw05sz62J1JMlxQ7ullIsRzV/ygh0kBEZ+N37PvQdckLKR2jSrEGjiNJr6EkBZFjKxnuEJ9amkWb/V6r3fnZAVMoXBeDYDbatk/E9yguWyGVUxhGLsaVXEKuDpp1w5AybCVPYUS5otZAnvsAphbryQ/yI758muhIvj/n7tP0KYnTaD1d2Y8GA9aZ3v74Kgp/tOTojDWnyh/LRbTdxYCPv+0kzk5LGE3PiBqCZf5ksnALQYbow2Wc6wJXmBTxyw66Ea9IWPWBIxNBrJsWewYX6tegpcqGnAojVh09OoPR2M1ArObv5J1GiEZ05JrJz9Su2/M0K7ek0J3vyonibakdk4FE/br3JQ87Wkx0yLRCcJvu4GpsKSCDDsWg/vl1yEFFzP4IwpL891OWljuhFFBNBpNX/zzKbkIGHKIQSHREjAkoPFoDdXjNQYVw8DafsZDyMoHS0Z/LcTUOaT4D40OEtsvUhRSh0a0Xw3kd1X4haE0mRF/BU07SaOxFpeYGWwaH7se9r9TckqIz1uSVmhuDi3HHKN9kzKqEvMpFJHMV7aFFzSXLxLaeLm5eIGyqwoc2PsADErqx3ILOgoc7TCd8Z88FyJIoPiqLBwyxehh3A1ZptvZ+6A8ifDIKGLzkDfdeA0rJ2wsvw1W2xokgYRzDZHO0Xmcgz4Wi13ynZ7j9Td/bRMZkgNS/BJEHLlFhxKEiQ2cgluETz/5eqJxxhWZMJaIvNZNriyZj9rccnRQ+Q4RN9rLkJ4ufaX59GnMuPmyDV5NdnyFLZ9ym47xkjbNWzHXEAYiYvk6ZSr4IhtNufMRTNWeJarEcYhDQRTWa7HSE7JPAzD+sYxqxGTGsw0xwpg7r+ATvi5MIHe2vy2z1EOVg/QMWDwIa7KdREX/YYMEA634Mp0nGSn3f0qIlE9ypmFxEGFuHT5DtlAbSNTzMufiMYPQYSYAnaTO2e5qO85K1b/RSmKk1NCdRG81GspYzZkanNZu+pgWYv52NsRxiUP263zLUETJ/O/sGPCFpv04RgLxRTnvhn+Qgztb3433fe98lj6q98GTy5MzrJqgwx+cjLqOpIVtZYaZl2mrHuED9rHeyBslB6eLqx/0WoJaO+W9jDarEY8TH23bWAkvOwOHAfghWgl17Yj0JkbZbou0w7SVtU5zb4x29Yot6u6b/pcf0yUTgRxSjBb5X9jCDMoNI78ve28AsPXth/bLyNIa9O604jGaimMvC1MftosySG/CvEgR4dhrooE2h3DszMV/2XrGw9OyFYmnjqsc3V4xkc/+MFiPB63F/ykNzShA5ibKCKuMcsuS4Qkz6GPXOOv2Pr6c0+IA2nTFnUAp8IEJ7sUvNQczG3KuspGvy7WogW/MP7pmDYI0Y6mMlV1NH/Jw8gNXPtOzn2Bz8LNZgg5aMKht+HefJGZcsOWohs2gq+BlGIAUUaqm7ftcJj32rMkMUb66jF3KncZFADNfT1Ybs2osduHd+63/cb2fAgB6tMamZmI18NJ4iOUW+5jmY44BLlBS1sG4pOwHxK5Qmvqp+38nkE4vBI5qJL+lmSf0bT6TY09Brx3gc+t9GEUhqVBE2ofwWMp6FkTuaKJ3heijOQtT24uQ5nVSNed40FafJj5r+5Sdcctm+6LwOkdkbUTm6v4cx+gUsR3qQ3w7IAup+3XfneUeK1tYz8W3/0SruJQMZJjiZaVaaFvXe1hOPkvJDOw+M8+YTW+T4wH2n83QiIx+npIbbKHs5e149RMkG3KAVcYIsepCsXiTiVTdgRav5UV/LEBRYd2X1SyCZ4NEVRh4qOvQ8k8IJmIdokD/eZGICmWA4P50wrB7AuozBbZT/bLw+9G/iGFa6ETOhzIdx7kiGRAhRCuvhEbMukNRxKNMZlu43mez9owQlWJ9tIxgfFUW0eiCu6XMosX3MK7QEETgu5r36UtwWe1/4hU+XzsB4XmcmapNVi5qX/IxdR8pehmZRVIpCf1kRsZLnw2IaMdkSmQ+jbJUU8iJ5CyRlYlYFlzKt4Wj85iBblJ4SkuI2sQexOZqKwWS6jeo5bVGARXmXrzJY1eYeuSYNPfYwxz/7de8d1EOaTNxLExZVhI9RAS5BjnNULM+zdDuelLCD9+Ly2E97Lx89grSMbGoy5iIgt+mbeMxEPLqpmEw4Lra7NCsQavfoPBjU1SSWEgYHBmLvy6vwEbE0GXiHYLKZ1D909jy9qDA49FLdvz5RYWnaO1FBA8VpFeDKpcF9XoPVV2DR5yBEfwxzPjeeey/CRtGlM2aN32SCdhytfl4kiyJd/Qx6TgoiZgLkGZSEbfbeXhJRtheHLpHczf8B0/yEbsTnTC4yN/WrpkuQlPgJVh2bhP+UXJIkJScQiP55FGFjcR7gQn8KdJXSEjyCdPVI0hmRdJnMMZDG8fe0QMZiLVN4ejqgxJXU7qTo9wJbcrfpst9PucnWffn10ZpvBl6F/viMNh181LgKdNosGwrG8DFIOW805hTyhX84SvIniV1GNenPavPzKJrLScZ8bYwCds1ItjoUJy16e5JuyTFTU4FRXcbWXfTQ/bq37e+vSGMJWW003MhixK/ca1u6OGD5ZENJ8qSXjAobjyw/DW7TnQzjdGOojqguY6sjemjmBEj//orUkZDVRsONLEb438MK9L1ZJA36U1j9OI5fwVySPKkjPnvmIT8NbtOGkGAMvuMqEJoKiek0tkpiBx8EnXRfXpFakjDcaMDRBar9SyglXQvuVI6knSyFypOGEnBpLrLU9pgwRIX4Vm/AIwRPn0G8uy/jBGdO1PVICfqP7pkS4cHx93MavSKNpjcjRmNPKYfMB9XtTt1KOOeTSZTfCxseM2YoTEzneUiWlVqbVJfpC/Pr0Vp7pKr24N7ZUpz2747ZXIbGvVk+Q32KF7yoUA+QNREV6RqcwywvTqMiuovy4bEP91qAog1d3cHVL++qn6nJrH/HcWjb6O+/rO9SNNfRXdx1GSicHuDo+SQqwIY8Ch6Cp79yB6EbKIZC/8LXHZxh2i+8IdqPCvBf0lUUwz/BuplxzkCcNrwhOc1Ug0fJpiSB1cMx20/codqvOuSBRZGRG6M8LbMVdzRuMyGRg5YKLK5BtoV5jmS8uYobYDBswht92Ep/ZLTuLyOSJ1c+PN1OgQPdVIEIm2dmMD77mTcs20JFdhrHPCrJz1yiyBcNqM39Lhd281E0QvNdk1etPSRkV9tCxrG2keawkvHkAylHaNNODQZov/Dgtx9VBOBnmFyN3H7hot98VGniAulspNse0R7Lk+Hed65WZpsoBuyc44Oxuk+8YbqvKolurKuhODdfuLLcfFSAP4V5/U55AL/7xBug+6qacvEuLN+Ctfffa7gqyow33+0XLouajwrwbEKAwRjsZ95AbAu9+ZbQ1Gsgmf1l3WipuwVcRkl5H5E+PB3DfuaSyrRQjHZTom0aFNXPnOF633nj1U3UXK0y3V6C4iHl7u3sdy5P2SYatEkJU1Gl1nV1cRghTf0GXG3Xa6PSFiCGjyB7uYVbnmiyn7lag2mhtxToGjGi1UC3kSwIupnp4G2693MYF3z7QtlFC7VBLz1MJXp20EKmM5pW2kqj7qjQHdxWMjzolqa4LHZgBe/hihxaqfoKIqxE7WX48ftoY8rvflUHyA8Xs7Q5d2lLe1hhp42XCUa6c3obcU9F1EfJbJHveuN8jzIYJV11h5N0eweTSDAvOp0keEn7KfD9RxmRX74lkGe+sZ95OLAt7LijzxKFpVL933wd9TuKEdLDxFgue0srJ+aIEge6sQ42dHsrvLRxMsHHVmrqcjK6olM3N1hHdQ+VOdNmmBuaMu0nrhnTflV5ISHIrjPIPYxS37geyO6zYhAqCGIwCPWNNwj5nC+1TqKkrchfS3+UDKTnuyWNRYNI4dctdOB/ytJyJxqk/igZqW6hGKmL5R2M033ijYK/KqGfPSP7MInio7J4wC78amcVOjDlzXlYyHsosKurCg3QqH/njVd/0vRZfMMJMtrEn0L/BdtM5stgWyqw+ATvkUWdrRVY8JvxsOC31MRCMrJ8NL1pFMgw9U04nVqnLdLya8rdvqlvwkGqz6ozOXhCBF+nMCly8YRxW3FP6byGGof2Ld9h3H0SHdS3Oi7ipqFgxtjPspG05q1X9XswXO87b7xeE+UcMsWhObPHfOfPG9NEyc9+LUYOT/tN+Hztt1I5Tof1AYce1GEbrit12Mx0cJHJJmqohYaemSas5sWZfUFLvhwIGhuio4GHHgLqyxm69s9gUOYr95KGbqA/FB5CPlzVQjFk1Uh5GcyjTEiRFiVd/ZMhDd03LvbdZyW7YqHZRX3jsyjWNKi+ZeJBqG+8QajPKoMCJACdOmRKZ9iEa2AMWqnOSggGIAe5O+4FdO8798zENlHeFqbcG5b6d/7tYKpxY9SVUBiqy/YT/5q2+aqDujhWgP0sJETbV8lLtD8clNeKOzavoQEKirHVg2q41riZbDk+Nm47vrON21QfEfnwykHVh0kma/Xw8Mh85h4WmRbKW+HtLoIbnluq+8S/FW6+Kq9tSQLFW7DdxXztP2jBv7ztNdI48X8BBTo3sylvuOd/XkORN4DXVsfPoUCD00bi9TAavp+ak+PuYxvIPFS9psrJj/IyAz8A3DzwpLn3nT/xTBO9AU8hWtY5n9PDJpJhqVaKkXu5LQfD9r7zxuw1Uanil2Ql2Xror1xFTDdQOrj6OfI4Tq1+E74jq99Ka2QxV3vfxWPqclWYK2swtLAlN85I1NggEkSmQrnNVJEh2sq0uYuWYDBsIosl0h65uTcUDzxoIbt+1B32BuBma3HQV78B/1zJtlExOUvzHAGPxaMOm3CZPGilVJpdHjGOuuw+8hVl911jHDpHGHcsuoFoPLqNZqimOKBy0EIWuFk3AupLgcaZLYkHGTaRecWXR7tdDMH6Nq3bQwMsFFEh/GZ62NB99BESC/mghR4adXM1BkwAmSr2TdZYEgi3ZPrpB6MwYWAq3KStZcixHbWxa3iuwkvQToZRO52G4QY6MSmaoSnLrp02Fm3cuF7Ivn7oPlOqVp8pgzxmQ54Mmogi8NlWGqetNisI94jVfhWdq9oGGqHe4qGYr6KAb92herkE+Ge27rvw2NQ10ZNqzsNfkXBzmkpknNPaBCMVGhpjDwakXrXJ3yItu5dMVJ/WMy3r0H+E171i7D+rQiiIn0yxPSXPpSooOs+f3rPk67KGfjqm5gu/dTCm8J7IdRyRvnozZwejJ7nvrXIeX3S6icnkqXtCoezlFQNB+tiMgNJ7O2bOMC7c5fC13JBnmj21iGa2pz693C1nLuxDO6N0xbENQiwy+sVeRSvvRZ4dadWzu2UFkU8c3cQRyUEP9slg20/wGNCcROZR2bJ9zTYklN9QohHY93SVMuA/lmP6cR/Kkd7y92/mpNcHVxnR/Sb+ye0dwkk/0ZNFexJ7rBMTyjb0Pksjkt6eG1r/xHJRWxzizW/YWL3lcbc7KeECBwwDICgjbqNsg8O1tRhRN/Yyj3NgwzGMcfWgFrKECb2m4VigJUMORDcZdzrQYqoHbUMugd4rdQaA6Om5ORuaJ81SM2XYKISp0n+WTXoKn1xbmCzNu1rmZS/HbuG1E6PNe2lMUJe9IGaNGfbZcGXJCN45W5DNPD1etoA5hPNbekGc7cd9Ll11733yTn7jJ1aTz88aNCSDh/9syG8fhkrmfdDG/4z37zUqbSbKFWBOpuzaQ2Lc6HQTE9VPw0CIEqVY4PbksmX4MSx7xCaPTjdPcz4pe5hXy5KFwm3nf7Hwrt+qHUWWqMGHVDC3c0brhtsz5NIRMonfIDizTFYRt6dneZgp05qLVyPh6ncKKVe8TX3wLSRjTASp38m/rTIuW9iL8Pq+VyYr8g4SdvBSy1RMkWWMEcPgXnMPAfaahWKYWIbkHcTEiumTkjQ7hnVZfJZtGqIhlzitwrJmmHmoBeCN9F4gh8bqUvSQ+EC5aY4qJ6g0eZEEipjN0nbB2CZeY4oeYSVpHkxrtyoNKRO2DbnNixkjaBGAJRo7/biyMx1z2jxW1b3Q1f1VBjcwkbhpBk39X0AZ2EwOJLPZ1cT0Mu0kc8jJ9lZNnSSL2/hkCzOnLel0b0JuaHVXkjhMUkcTK847J4clyDnHB63KG+eBwTVL1J5PYZdgLlDbCfDCFiOGvEJWtG7fzpMm5MSwbQi/nyApIs0Ij16/PmlqeRg0DSYN47Kgl7WRdgEJeSHuE9KRxRmcxyLPbiweyWpp4TYPJjHTsESQT1PNHEXHYGyS58ikoWkmvrQ4UEpyfAoSlHJPmRZgJMdGNV/0GKIBlc1tKIEtyFfoW1rlg5uKsBTayKya6QoYPqKR+njFrcPdzQ+eCtEQ6K9BmCHx34pbK4kSUMMhYx4skeTZ1T01a4MY4UCtkaOY5qhJluEgLFZv4jq9gy3RfWMnm7jZiqcMCDGDeDmmCStkuaP3lcUWYjq+bOpwyx+LOPmnB6m6JezS6q5kgZR2PetRjI8UpiTrti+WDvhBj6nLWBmQEZmiAV0DcvgFriO66r4jyu20SoCbXH0pHFabpXI4ozBFBy6T+VQGnJ/N1N6M19zmR9/LA13nNEn1lVH1/IYSX6FzWH2/UEBltIuKANjP+CLa7mLQARbPea+lJ9RHnO22vMGSfbw8JFnQUnJr4PxMelC2gXQXV2RwIV9idA4b+Tczw5PapqKQPozitFLPsPmTqEGtDNKV+tUPuaTOhZreXrNgBDNVOzqK+dU4LEle0oUzBPTSbRQoM+U5OpS5ZTeGvYcd/c5sDVI6rVUbZ0SnmU2yEmmwshVLtfOI+sjrtqnHIn2+OGwkRtn++WK/agzp2f3o75UuvSFqvdzmdgihtmQZrhgo0qLT3lij8Zab2yGkkTIVi2RFgaRbnl7HENKkUyiJQDMqeGTOOlKrRuNtPLedmDzbh/FMhSbSi1+AyZZQmTD0WoSY9XDk8QpGLbugYXEULr+D5PTmHJ3MK0/FQJGWm/LEmprvmnypWjvP6yzY0ZbAEvOg38Qv4YPyXV0/T9ZEh7/kKDts5P8oG57Upmja8hoZPg9RDtY/YPFAjTAkXNXFGzlM335ZONJVWPLNnhHMuu3gi9nA7+CJEG5P4YrXqrfniTXf6HnX5g/by69emIo9bbk+hUKkmgRQiHSpwa4ft4qgxcPAYWFAqSkkbR/CMJIUQiRAdCob+mGLcl2oO/mVjmlY09RcFK+JXgu/RPerUi6aV33cbMX25JH9v4MrppJt6Alxbs/B/iotf2l7EGpeSCqPfP2G+3TmYwp5ShWeoGUIVcctUEq6y+uOupIvXMi8Zn5X83gkM5VUFVsct2WYzY1TIbbW3bLCrw6+Qo3lLWzrf4WHTnfYL2SreGYpa648A3p5Vikq4Vvre0VZXg8Mkq0MceMQi2NyVkhcBMK2/j0FY7OBX/h4ySnLzNEcun0l8iKqJF0Jjqo4tBzWMDxKs8yzzX7DFkxeXiSwgFEsOT3IOvg+OfDLQtdbj6LUszszGpNiOJSaL8K+gciVQuIfu/RqdVsk7+4qVC8H1aqHnJM1l2zl/Gra1ZYuL5EtgiOC4Zklsq2L2y7ErjUO2Spyg5PZJ8/TbDZFzJcS83TYyL9dOijEXvUU1li3Edy4d5ssiikUtAx5XT8oEV+LsKgwu02ViZQkG4bbKHs5e149RMkG3CDWdmXFOVaXspOMKWyd85oh/BrmrAFGl16vrC5uZXVHJpA/tKlnW+8Z2cOa6ku25DmHeFUfCSGccuwVPZIy60IIvB1fp0i8OZu4VdiX55CvJSStJTfN1gHX0qrziypSX6eIvC+2qOLwNXqJiXUPyp8Fu5Zs1Xspk9i2YsJYkHR0EvNBjzUihgSTGmYMXaGhO3klayYSQwJV6QLc6thwfusQdudguGGMrbR2uM17F/iITIxLZDKiVb48z9KtjB+y5iEYwo7X8y6xn3yz4jY1YATVeN/ZcLHdpRlO/YBOA0BpuUhah7RZmGEZEL0vHmJsTdghaR2SHcywvTDaEOw4Bbs4rfx4LWgRMzhtvRPCgdENywFDf3RmyXG0+nmRIDtp9VN7X1H2CaFFBIMygIRtgrFJcg+h6uL/OmJaFl2CpMT3HLVDBP/JrVjCaSYz75OS4+6gf5YwhB1rAIH+5L4B92qOL7EX9iRN8iKLID7uIWujXSFN2bbbdDmsVc7Zr33BVi9MixpvrOwKarhXQqyqy+6B7XTBVQ1OUs3lRBnVdp2UJVQ1el1BYQrYD7nmCDG81DH4UzCY370ytntGJ2dX206OvPYzvQnIxgUPDWef6iKq3ugAMbw8MfjTuyf9u1fGdlUl5exq28mR165aOQHZ7Wvb7zg2W3P+mU7SZ8T2UMPLVY+Kvnuk++KZydQDZxXnuqYaZOi9op6IBbXJfAMeIXj6DOLdfRknuG62rvUk6C/Jp+J/rBEsNiGZw+MHp1G4aWI+GDGd7alJupje/WGflbgpWWsHdWTJ1Z8+Zyar8zv2Gvo/6vsn/eP7CghmPJplkLXfPr7HrrxtVP+A/qxu1S7TNYhz8uvH9zcl6r0F1V+nIIebDsRHBDMBKzxmB7Rpc5Hc46KBO5ARAmiMmibN59bRUETrqIiOsgLi/PHo8wqtJXSE+uUdCRHEN1x3YH2RXJXFriwQyWB7FzP3vh/fy8f/+H6A88c6YaEPEhCaEJEArpLjEsbrFu/zKM57W58IxAni/ieAfq/mEi3NAmxeWkhf00QTUM2+U7ADyRotuVuw3cUIWH6VLKJHYIPbtxx8AZto9YJ+f4Qk4lEERD0RLNs/nsJok0XbvIbR9Ud/Ihleb5//z/8HALWcd6tBCgA= + + + dbo + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/202002251510114_ManufacturerSubjectToAcl.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202002251510114_ManufacturerSubjectToAcl.Designer.cs new file mode 100644 index 0000000000..30eaed8497 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002251510114_ManufacturerSubjectToAcl.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 ManufacturerSubjectToAcl : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(ManufacturerSubjectToAcl)); + + string IMigrationMetadata.Id + { + get { return "202002251510114_ManufacturerSubjectToAcl"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002251510114_ManufacturerSubjectToAcl.cs b/src/Libraries/SmartStore.Data/Migrations/202002251510114_ManufacturerSubjectToAcl.cs new file mode 100644 index 0000000000..e7a616ff0a --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002251510114_ManufacturerSubjectToAcl.cs @@ -0,0 +1,19 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class ManufacturerSubjectToAcl : DbMigration + { + public override void Up() + { + AddColumn("dbo.Manufacturer", "SubjectToAcl", c => c.Boolean(nullable: false)); + CreateIndex("dbo.Manufacturer", "SubjectToAcl"); + } + + public override void Down() + { + DropIndex("dbo.Manufacturer", new[] { "SubjectToAcl" }); + DropColumn("dbo.Manufacturer", "SubjectToAcl"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002251510114_ManufacturerSubjectToAcl.resx b/src/Libraries/SmartStore.Data/Migrations/202002251510114_ManufacturerSubjectToAcl.resx new file mode 100644 index 0000000000..b0daf591e5 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002251510114_ManufacturerSubjectToAcl.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/202002271206204_ShipmentTrackingUrl.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202002271206204_ShipmentTrackingUrl.Designer.cs new file mode 100644 index 0000000000..e56eab80b2 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002271206204_ShipmentTrackingUrl.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 ShipmentTrackingUrl : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(ShipmentTrackingUrl)); + + string IMigrationMetadata.Id + { + get { return "202002271206204_ShipmentTrackingUrl"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002271206204_ShipmentTrackingUrl.cs b/src/Libraries/SmartStore.Data/Migrations/202002271206204_ShipmentTrackingUrl.cs new file mode 100644 index 0000000000..7d752fe47f --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002271206204_ShipmentTrackingUrl.cs @@ -0,0 +1,17 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class ShipmentTrackingUrl : DbMigration + { + public override void Up() + { + AddColumn("dbo.Shipment", "TrackingUrl", c => c.String(maxLength: 2000)); + } + + public override void Down() + { + DropColumn("dbo.Shipment", "TrackingUrl"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002271206204_ShipmentTrackingUrl.resx b/src/Libraries/SmartStore.Data/Migrations/202002271206204_ShipmentTrackingUrl.resx new file mode 100644 index 0000000000..0653e0d4c9 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002271206204_ShipmentTrackingUrl.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/202002280005340_MediaManager.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202002280005340_MediaManager.Designer.cs new file mode 100644 index 0000000000..d829eb7571 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002280005340_MediaManager.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 MediaManager : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(MediaManager)); + + string IMigrationMetadata.Id + { + get { return "202002280005340_MediaManager"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002280005340_MediaManager.cs b/src/Libraries/SmartStore.Data/Migrations/202002280005340_MediaManager.cs new file mode 100644 index 0000000000..b210c5ef59 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002280005340_MediaManager.cs @@ -0,0 +1,169 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using SmartStore.Data.Setup; + + public partial class MediaManager : DbMigration, IDataSeeder + { + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + // Empty, but we need the events in SmartStore.Services + } + + public override void Up() + { + DropIndex("dbo.MediaFile", "IX_UpdatedOn_IsTransient"); + CreateTable( + "dbo.MediaFolder", + c => new + { + Id = c.Int(nullable: false, identity: true), + ParentId = c.Int(), + Name = c.String(nullable: false, maxLength: 255), + Slug = c.String(maxLength: 255), + CanDetectTracks = c.Boolean(nullable: false), + Metadata = c.String(), + FilesCount = c.Int(nullable: false), + ResKey = c.String(maxLength: 255), + IncludePath = c.Boolean(), + Order = c.Int(), + Discriminator = c.String(nullable: false, maxLength: 128), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.MediaFolder", t => t.ParentId) + .Index(t => new { t.ParentId, t.Name }, unique: true, name: "IX_NameParentId"); + + CreateTable( + "dbo.MediaTag", + c => new + { + Id = c.Int(nullable: false, identity: true), + Name = c.String(nullable: false, maxLength: 100), + }) + .PrimaryKey(t => t.Id) + .Index(t => t.Name, name: "IX_MediaTag_Name"); + + CreateTable( + "dbo.MediaTrack", + c => new + { + Id = c.Int(nullable: false, identity: true), + MediaFileId = c.Int(nullable: false), + Album = c.String(nullable: false, maxLength: 50), + EntityId = c.Int(nullable: false), + EntityName = c.String(nullable: false, maxLength: 255), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.MediaFile", t => t.MediaFileId, cascadeDelete: true) + .Index(t => new { t.MediaFileId, t.EntityId, t.EntityName }, unique: true, name: "IX_MediaTrack_Composite") + .Index(t => t.Album); + + CreateTable( + "dbo.MediaFile_Tag_Mapping", + c => new + { + MediaFile_Id = c.Int(nullable: false), + MediaTag_Id = c.Int(nullable: false), + }) + .PrimaryKey(t => new { t.MediaFile_Id, t.MediaTag_Id }) + .ForeignKey("dbo.MediaFile", t => t.MediaFile_Id, cascadeDelete: true) + .ForeignKey("dbo.MediaTag", t => t.MediaTag_Id, cascadeDelete: true) + .Index(t => t.MediaFile_Id) + .Index(t => t.MediaTag_Id); + + AddColumn("dbo.MediaFile", "FolderId", c => c.Int()); + AddColumn("dbo.MediaFile", "Alt", c => c.String(maxLength: 400)); + AddColumn("dbo.MediaFile", "Title", c => c.String(maxLength: 400)); + AddColumn("dbo.MediaFile", "Extension", c => c.String(maxLength: 50)); + AddColumn("dbo.MediaFile", "MediaType", c => c.String(nullable: false, maxLength: 20)); + AddColumn("dbo.MediaFile", "Size", c => c.Int(nullable: false)); + AddColumn("dbo.MediaFile", "PixelSize", c => c.Int()); + AddColumn("dbo.MediaFile", "Metadata", c => c.String()); + AddColumn("dbo.MediaFile", "CreatedOnUtc", c => c.DateTime(nullable: false)); + AddColumn("dbo.MediaFile", "Deleted", c => c.Boolean(nullable: false)); + AddColumn("dbo.MediaFile", "Hidden", c => c.Boolean(nullable: false)); + AddColumn("dbo.MediaFile", "Version", c => c.Int(nullable: false)); + AddColumn("dbo.Download", "MediaFileId", c => c.Int()); + AlterColumn("dbo.MediaFile", "MimeType", c => c.String(nullable: false, maxLength: 100)); + CreateIndex("dbo.BlogPost", "MediaFileId"); + CreateIndex("dbo.BlogPost", "PreviewMediaFileId"); + CreateIndex("dbo.MediaFile", "FolderId"); + CreateIndex("dbo.MediaFile", "Name"); + CreateIndex("dbo.MediaFile", "Alt"); + CreateIndex("dbo.MediaFile", "Extension"); + CreateIndex("dbo.MediaFile", "MimeType"); + CreateIndex("dbo.MediaFile", "MediaType"); + CreateIndex("dbo.MediaFile", "Size"); + CreateIndex("dbo.MediaFile", "PixelSize"); + CreateIndex("dbo.MediaFile", "IsNew"); + CreateIndex("dbo.MediaFile", new[] { "CreatedOnUtc", "IsTransient" }, name: "IX_CreatedOn_IsTransient"); + CreateIndex("dbo.MediaFile", "Deleted"); + CreateIndex("dbo.Download", "MediaFileId"); + CreateIndex("dbo.News", "MediaFileId"); + CreateIndex("dbo.News", "PreviewMediaFileId"); + AddForeignKey("dbo.MediaFile", "FolderId", "dbo.MediaFolder", "Id"); + AddForeignKey("dbo.Download", "MediaFileId", "dbo.MediaFile", "Id"); + AddForeignKey("dbo.BlogPost", "MediaFileId", "dbo.MediaFile", "Id"); + AddForeignKey("dbo.BlogPost", "PreviewMediaFileId", "dbo.MediaFile", "Id"); + AddForeignKey("dbo.News", "MediaFileId", "dbo.MediaFile", "Id"); + AddForeignKey("dbo.News", "PreviewMediaFileId", "dbo.MediaFile", "Id"); + } + + public override void Down() + { + DropForeignKey("dbo.News", "PreviewMediaFileId", "dbo.MediaFile"); + DropForeignKey("dbo.News", "MediaFileId", "dbo.MediaFile"); + DropForeignKey("dbo.BlogPost", "PreviewMediaFileId", "dbo.MediaFile"); + DropForeignKey("dbo.BlogPost", "MediaFileId", "dbo.MediaFile"); + DropForeignKey("dbo.MediaTrack", "MediaFileId", "dbo.MediaFile"); + DropForeignKey("dbo.MediaFile_Tag_Mapping", "MediaTag_Id", "dbo.MediaTag"); + DropForeignKey("dbo.MediaFile_Tag_Mapping", "MediaFile_Id", "dbo.MediaFile"); + DropForeignKey("dbo.Download", "MediaFileId", "dbo.MediaFile"); + DropForeignKey("dbo.MediaFile", "FolderId", "dbo.MediaFolder"); + DropForeignKey("dbo.MediaFolder", "ParentId", "dbo.MediaFolder"); + DropIndex("dbo.MediaFile_Tag_Mapping", new[] { "MediaTag_Id" }); + DropIndex("dbo.MediaFile_Tag_Mapping", new[] { "MediaFile_Id" }); + DropIndex("dbo.News", new[] { "PreviewMediaFileId" }); + DropIndex("dbo.News", new[] { "MediaFileId" }); + DropIndex("dbo.MediaTrack", new[] { "Album" }); + DropIndex("dbo.MediaTrack", "IX_MediaTrack_Composite"); + DropIndex("dbo.MediaTag", "IX_MediaTag_Name"); + DropIndex("dbo.Download", new[] { "MediaFileId" }); + DropIndex("dbo.MediaFolder", "IX_NameParentId"); + DropIndex("dbo.MediaFile", new[] { "Deleted" }); + DropIndex("dbo.MediaFile", "IX_CreatedOn_IsTransient"); + DropIndex("dbo.MediaFile", new[] { "IsNew" }); + DropIndex("dbo.MediaFile", new[] { "PixelSize" }); + DropIndex("dbo.MediaFile", new[] { "Size" }); + DropIndex("dbo.MediaFile", new[] { "MediaType" }); + DropIndex("dbo.MediaFile", new[] { "MimeType" }); + DropIndex("dbo.MediaFile", new[] { "Extension" }); + DropIndex("dbo.MediaFile", new[] { "Alt" }); + DropIndex("dbo.MediaFile", new[] { "Name" }); + DropIndex("dbo.MediaFile", new[] { "FolderId" }); + DropIndex("dbo.BlogPost", new[] { "PreviewMediaFileId" }); + DropIndex("dbo.BlogPost", new[] { "MediaFileId" }); + AlterColumn("dbo.MediaFile", "MimeType", c => c.String(nullable: false, maxLength: 40)); + DropColumn("dbo.Download", "MediaFileId"); + DropColumn("dbo.MediaFile", "Version"); + DropColumn("dbo.MediaFile", "Hidden"); + DropColumn("dbo.MediaFile", "Deleted"); + DropColumn("dbo.MediaFile", "CreatedOnUtc"); + DropColumn("dbo.MediaFile", "Metadata"); + DropColumn("dbo.MediaFile", "PixelSize"); + DropColumn("dbo.MediaFile", "Size"); + DropColumn("dbo.MediaFile", "MediaType"); + DropColumn("dbo.MediaFile", "Extension"); + DropColumn("dbo.MediaFile", "Title"); + DropColumn("dbo.MediaFile", "Alt"); + DropColumn("dbo.MediaFile", "FolderId"); + DropTable("dbo.MediaFile_Tag_Mapping"); + DropTable("dbo.MediaTrack"); + DropTable("dbo.MediaTag"); + DropTable("dbo.MediaFolder"); + CreateIndex("dbo.MediaFile", new[] { "UpdatedOnUtc", "IsTransient" }, name: "IX_UpdatedOn_IsTransient"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202002280005340_MediaManager.resx b/src/Libraries/SmartStore.Data/Migrations/202002280005340_MediaManager.resx new file mode 100644 index 0000000000..09e4b931b3 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202002280005340_MediaManager.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/202003022018038_Merge8.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202003022018038_Merge8.Designer.cs new file mode 100644 index 0000000000..171f291d76 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003022018038_Merge8.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 Merge8 : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(Merge8)); + + string IMigrationMetadata.Id + { + get { return "202003022018038_Merge8"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202003022018038_Merge8.cs b/src/Libraries/SmartStore.Data/Migrations/202003022018038_Merge8.cs new file mode 100644 index 0000000000..d1d37ebb47 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003022018038_Merge8.cs @@ -0,0 +1,15 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class Merge8 : DbMigration + { + public override void Up() + { + } + + public override void Down() + { + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202003022018038_Merge8.resx b/src/Libraries/SmartStore.Data/Migrations/202003022018038_Merge8.resx new file mode 100644 index 0000000000..17f1a266b7 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003022018038_Merge8.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/202003052100521_CustomerRoleMappings.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202003052100521_CustomerRoleMappings.Designer.cs new file mode 100644 index 0000000000..081c95329a --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003052100521_CustomerRoleMappings.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 CustomerRoleMappings : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(CustomerRoleMappings)); + + string IMigrationMetadata.Id + { + get { return "202003052100521_CustomerRoleMappings"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202003052100521_CustomerRoleMappings.cs b/src/Libraries/SmartStore.Data/Migrations/202003052100521_CustomerRoleMappings.cs new file mode 100644 index 0000000000..24bd074a26 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003052100521_CustomerRoleMappings.cs @@ -0,0 +1,92 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using System.Linq; + using System.Web.Hosting; + using SmartStore.Core.Data; + using SmartStore.Core.Domain.Localization; + using SmartStore.Core.Domain.Tasks; + using SmartStore.Data.Setup; + + public partial class CustomerRoleMappings : DbMigration, IDataSeeder + { + public override void Up() + { + CreateTable( + "dbo.CustomerRoleMapping", + c => new + { + Id = c.Int(nullable: false, identity: true), + CustomerId = c.Int(nullable: false), + CustomerRoleId = c.Int(nullable: false), + IsSystemMapping = c.Boolean(nullable: false), + }) + .PrimaryKey(t => t.Id) + .ForeignKey("dbo.Customer", t => t.CustomerId, cascadeDelete: true) + .ForeignKey("dbo.CustomerRole", t => t.CustomerRoleId, cascadeDelete: true) + .Index(t => t.CustomerId) + .Index(t => t.CustomerRoleId) + .Index(t => t.IsSystemMapping); + + CreateTable( + "dbo.RuleSet_CustomerRole_Mapping", + c => new + { + CustomerRole_Id = c.Int(nullable: false), + RuleSetEntity_Id = c.Int(nullable: false), + }) + .PrimaryKey(t => new { t.CustomerRole_Id, t.RuleSetEntity_Id }) + .ForeignKey("dbo.CustomerRole", t => t.CustomerRole_Id, cascadeDelete: true) + .ForeignKey("dbo.RuleSet", t => t.RuleSetEntity_Id, cascadeDelete: true) + .Index(t => t.CustomerRole_Id) + .Index(t => t.RuleSetEntity_Id); + + if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) + { + // Copy customer role mappings. + Sql("IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Customer_CustomerRole_Mapping')) BEGIN Insert Into [dbo].[CustomerRoleMapping] (CustomerId, CustomerRoleId, IsSystemMapping) Select Customer_Id As Customer_Id, CustomerRole_Id As CustomerRole_Id, 0 As IsSystemMapping From [dbo].[Customer_CustomerRole_Mapping] END"); + } + } + + public override void Down() + { + DropForeignKey("dbo.RuleSet_CustomerRole_Mapping", "RuleSetEntity_Id", "dbo.RuleSet"); + DropForeignKey("dbo.RuleSet_CustomerRole_Mapping", "CustomerRole_Id", "dbo.CustomerRole"); + DropForeignKey("dbo.CustomerRoleMapping", "CustomerRoleId", "dbo.CustomerRole"); + DropForeignKey("dbo.CustomerRoleMapping", "CustomerId", "dbo.Customer"); + DropIndex("dbo.RuleSet_CustomerRole_Mapping", new[] { "RuleSetEntity_Id" }); + DropIndex("dbo.RuleSet_CustomerRole_Mapping", new[] { "CustomerRole_Id" }); + DropIndex("dbo.CustomerRoleMapping", new[] { "IsSystemMapping" }); + DropIndex("dbo.CustomerRoleMapping", new[] { "CustomerRoleId" }); + DropIndex("dbo.CustomerRoleMapping", new[] { "CustomerId" }); + DropTable("dbo.RuleSet_CustomerRole_Mapping"); + DropTable("dbo.CustomerRoleMapping"); + } + + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + if (!DataSettings.DatabaseIsInstalled()) + { + return; + } + + var defaultLang = context.Set().AsNoTracking().OrderBy(x => x.DisplayOrder).First(); + var isGerman = defaultLang.UniqueSeoCode.IsCaseInsensitiveEqual("de"); + + // Note, core scheduled tasks must always be added to the installation as well! + context.Set().AddOrUpdate(x => x.Type, + new ScheduleTask + { + Name = isGerman ? "Zuordnungen von Kunden zu Kundengruppen aktualisieren" : "Update assignments of customers to customer roles", + CronExpression = "15 2 * * *", // At 02:15 + Type = "SmartStore.Services.Customers.TargetGroupEvaluatorTask, SmartStore.Services", + Enabled = true, + StopOnError = false + } + ); + context.SaveChanges(); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202003052100521_CustomerRoleMappings.resx b/src/Libraries/SmartStore.Data/Migrations/202003052100521_CustomerRoleMappings.resx new file mode 100644 index 0000000000..4f8646a1ab --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003052100521_CustomerRoleMappings.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/202003112359492_MediaManager2.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202003112359492_MediaManager2.Designer.cs new file mode 100644 index 0000000000..78b5637134 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003112359492_MediaManager2.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 MediaManager2 : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(MediaManager2)); + + string IMigrationMetadata.Id + { + get { return "202003112359492_MediaManager2"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202003112359492_MediaManager2.cs b/src/Libraries/SmartStore.Data/Migrations/202003112359492_MediaManager2.cs new file mode 100644 index 0000000000..bcf8c00d5f --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003112359492_MediaManager2.cs @@ -0,0 +1,89 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using SmartStore.Data.Setup; + + public partial class MediaManager2 : DbMigration, IDataSeeder + { + public override void Up() + { + #region Moved from "202003052100521_CustomerRoleMappings" to here + + DropIndex("dbo.MediaFile", new[] { "IsNew" }); + AddColumn("dbo.QueuedEmailAttachment", "MediaFileId", c => c.Int()); + CreateIndex("dbo.QueuedEmailAttachment", "MediaFileId"); + AddForeignKey("dbo.QueuedEmailAttachment", "MediaFileId", "dbo.MediaFile", "Id"); + DropForeignKey("dbo.QueuedEmailAttachment", "MediaStorageId", "dbo.MediaStorage"); + AddForeignKey("dbo.QueuedEmailAttachment", "MediaStorageId", "dbo.MediaStorage", "Id", cascadeDelete: true); + DropColumn("dbo.MediaFile", "IsNew"); + DropColumn("dbo.Download", "DownloadBinary"); + DropColumn("dbo.Download", "IsNew"); + + #endregion + + DropIndex("dbo.MediaTrack", "IX_MediaTrack_Composite"); + AddColumn("dbo.MediaTrack", "Property", c => c.String(maxLength: 255)); + AlterColumn("dbo.Setting", "Name", c => c.String(nullable: false, maxLength: 400)); + CreateIndex("dbo.Setting", "Name", name: "IX_Setting_Name"); + CreateIndex("dbo.Setting", "StoreId", name: "IX_Setting_StoreId"); + CreateIndex("dbo.MediaTrack", new[] { "MediaFileId", "EntityId", "EntityName", "Property" }, unique: true, name: "IX_MediaTrack_Composite"); + DropColumn("dbo.MediaFile", "PictureBinary"); + } + + public override void Down() + { + #region Moved from "202003052100521_CustomerRoleMappings" to here + + AddColumn("dbo.QueuedEmailAttachment", "Data", c => c.Binary()); + AddColumn("dbo.QueuedEmailAttachment", "FileId", c => c.Int()); + AddColumn("dbo.Download", "MediaStorageId", c => c.Int()); + AddColumn("dbo.Download", "IsNew", c => c.Boolean(nullable: false)); + AddColumn("dbo.Download", "Extension", c => c.String()); + AddColumn("dbo.Download", "Filename", c => c.String()); + AddColumn("dbo.Download", "ContentType", c => c.String()); + AddColumn("dbo.Download", "DownloadBinary", c => c.Binary()); + AddColumn("dbo.MediaFile", "IsNew", c => c.Boolean(nullable: false)); + DropForeignKey("dbo.QueuedEmailAttachment", "MediaStorageId", "dbo.MediaStorage"); + DropForeignKey("dbo.QueuedEmailAttachment", "MediaFileId", "dbo.MediaFile"); + DropIndex("dbo.QueuedEmailAttachment", new[] { "MediaFileId" }); + DropColumn("dbo.QueuedEmailAttachment", "MediaFileId"); + CreateIndex("dbo.QueuedEmailAttachment", "FileId"); + CreateIndex("dbo.Download", "MediaStorageId"); + CreateIndex("dbo.MediaFile", "IsNew"); + AddForeignKey("dbo.QueuedEmailAttachment", "MediaStorageId", "dbo.MediaStorage", "Id"); + AddForeignKey("dbo.QueuedEmailAttachment", "FileId", "dbo.Download", "Id", cascadeDelete: true); + AddForeignKey("dbo.Download", "MediaStorageId", "dbo.MediaStorage", "Id"); + + #endregion + + AddColumn("dbo.MediaFile", "PictureBinary", c => c.Binary()); + DropIndex("dbo.MediaTrack", "IX_MediaTrack_Composite"); + DropIndex("dbo.Setting", "IX_Setting_StoreId"); + DropIndex("dbo.Setting", "IX_Setting_Name"); + AlterColumn("dbo.Setting", "Name", c => c.String(nullable: false, maxLength: 200)); + DropColumn("dbo.MediaTrack", "Property"); + CreateIndex("dbo.MediaTrack", new[] { "MediaFileId", "EntityId", "EntityName" }, unique: true, name: "IX_MediaTrack_Composite"); + } + + public void Seed(SmartObjectContext context) + { + // We cannot tear down obsolete DB structure during regular migration, but only + // AFTER a successfull data seed, 'cause during the seed we need the data. + var sql = @" +ALTER TABLE [dbo].[Download] DROP CONSTRAINT [FK_dbo.Download_dbo.MediaStorage_MediaStorageId] +ALTER TABLE [dbo].[QueuedEmailAttachment] DROP CONSTRAINT [FK_dbo.QueuedEmailAttachment_dbo.Download_FileId] +DROP INDEX [IX_MediaStorageId] ON [dbo].[Download] +DROP INDEX [IX_FileId] ON [dbo].[QueuedEmailAttachment] +ALTER TABLE [dbo].[Download] DROP COLUMN [ContentType] +ALTER TABLE [dbo].[Download] DROP COLUMN [Filename] +ALTER TABLE [dbo].[Download] DROP COLUMN [Extension] +ALTER TABLE [dbo].[Download] DROP COLUMN [MediaStorageId] +ALTER TABLE [dbo].[QueuedEmailAttachment] DROP COLUMN [FileId] +ALTER TABLE [dbo].[QueuedEmailAttachment] DROP COLUMN [Data] +"; + context.ExecuteSqlCommand(sql); + } + + public bool RollbackOnFailure => false; + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202003112359492_MediaManager2.resx b/src/Libraries/SmartStore.Data/Migrations/202003112359492_MediaManager2.resx new file mode 100644 index 0000000000..ce11e8be30 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003112359492_MediaManager2.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/202003171812584_CookieManager.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202003171812584_CookieManager.Designer.cs new file mode 100644 index 0000000000..aaa30bb291 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003171812584_CookieManager.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 CookieManager : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(CookieManager)); + + string IMigrationMetadata.Id + { + get { return "202003171812584_CookieManager"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202003171812584_CookieManager.cs b/src/Libraries/SmartStore.Data/Migrations/202003171812584_CookieManager.cs new file mode 100644 index 0000000000..799ce565aa --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003171812584_CookieManager.cs @@ -0,0 +1,90 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using System.Linq; + using SmartStore.Core.Data; + using SmartStore.Core.Domain.Cms; + using SmartStore.Data.Setup; + + public partial class CookieManager : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + } + + public override void Down() + { + } + + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + if (!DataSettings.DatabaseIsInstalled()) + return; + + // Add resources. + context.MigrateLocaleResources(MigrateLocaleResources); + + // Add menu item to footer. + var menuSet = context.Set(); + var menuItemSet = context.Set(); + var footerService = menuSet.Where(x => x.SystemName.Equals("FooterService")).FirstOrDefault(); + + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerService.Id, + ProviderName = "route", + Model = "{{\"routename\":\"{0}\"}}".FormatInvariant("CookieManager"), + Title = "Cookie Manager", + DisplayOrder = 100, + Published = true, + CssClass = "cookie-manager" + }); + + context.SaveChanges(); + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("CookieManager.MyAccount.Button", "Cookie Manager", "Cookie Manager"); + builder.AddOrUpdate("CookieManager.Dialog.Title", "Cookie settings", "Cookie-Einstellungen"); + builder.AddOrUpdate("CookieManager.Dialog.Heading", "Yes, we use cookies", "Ja, wir verwenden Cookies"); + builder.AddOrUpdate("CookieManager.Dialog.Intro", + "You decide which cookies you allow or reject. You can change your decision at any time in your My Account area. Further information can also be found in our privacy policy.", + "Sie entscheiden, welche Cookies Sie zulassen oder ablehnen. Ihre Entscheidung knnen Sie jederzeit in Ihrem My-Account-Bereich ndern. Weitere Infos auch in unserer Datenschutzerklrung."); + + builder.AddOrUpdate("CookieManager.Dialog.Required.Heading", + "Required cookies", + "Notwendige Cookies"); + builder.AddOrUpdate("CookieManager.Dialog.Required.Intro", + "Technically required cookies help us to make the operation of the website possible. They provide basic functions such as the display of products or login and are therefore a prerequisite for using the site.", + "Technisch erforderliche Cookies helfen uns dabei, die Bedienung der Webseite zu ermglichen. Sie stellen Grundfunktionen, wie die Darstellung von Produkten oder den Login sicher und sind daher eine Voraussetzung zur Nutzung der Seite."); + + builder.AddOrUpdate("CookieManager.Dialog.Analytics.Heading", + "Analytical cookies", + "Analytische Cookies"); + builder.AddOrUpdate("CookieManager.Dialog.Analytics.Intro", + "These cookies help us to improve our website by anonymously understanding the performance and use of our site.", + "Diese Cookies helfen uns, unsere Website zu verbessern, indem wir anonym die Leistung und die Verwendung unserer Seite verstehen."); + + builder.AddOrUpdate("CookieManager.Dialog.ThirdParty.Heading", + "Third party cookies", + "Cookies von Drittanbietern"); + builder.AddOrUpdate("CookieManager.Dialog.ThirdParty.Intro", + "These cookies help us to use comfort functions of other service providers and integrate them into our shop.", + "Diese Cookies helfen uns, Komfort-Funktionen von anderen Dienstanbietern zu nutzen und in unseren Shop einzubinden."); + + builder.AddOrUpdate("CookieManager.Dialog.Show", "Show", "Anzeigen"); + builder.AddOrUpdate("CookieManager.Dialog.ReadMore", "Read more", "Weiterlesen"); + builder.AddOrUpdate("CookieManager.Dialog.Button.AcceptAll", "Accept all", "Alle auswhlen"); + builder.AddOrUpdate("CookieManager.Dialog.Button.AcceptSelected", "Accept selected", "Auswahl besttigen"); + + // Remove deleted resources from consent badge. + builder.Delete("CookieConsent.Button"); + builder.Delete("CookieConsent.BadgeText"); + builder.Delete("Admin.Configuration.Settings.CustomerUser.Privacy.CookieConsentBadgetext"); + builder.Delete("Admin.Configuration.Settings.CustomerUser.Privacy.CookieConsentBadgetext.Hint"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202003171812584_CookieManager.resx b/src/Libraries/SmartStore.Data/Migrations/202003171812584_CookieManager.resx new file mode 100644 index 0000000000..ce11e8be30 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003171812584_CookieManager.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/202003251118391_CategoryRuleSets.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202003251118391_CategoryRuleSets.Designer.cs new file mode 100644 index 0000000000..ce453cbe79 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003251118391_CategoryRuleSets.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 CategoryRuleSets : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(CategoryRuleSets)); + + string IMigrationMetadata.Id + { + get { return "202003251118391_CategoryRuleSets"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202003251118391_CategoryRuleSets.cs b/src/Libraries/SmartStore.Data/Migrations/202003251118391_CategoryRuleSets.cs new file mode 100644 index 0000000000..5069c892c2 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003251118391_CategoryRuleSets.cs @@ -0,0 +1,62 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using System.Linq; + using SmartStore.Core.Domain.Localization; + using SmartStore.Core.Domain.Tasks; + using SmartStore.Data.Setup; + + public partial class CategoryRuleSets : DbMigration, IDataSeeder + { + public override void Up() + { + CreateTable( + "dbo.RuleSet_Category_Mapping", + c => new + { + Category_Id = c.Int(nullable: false), + RuleSetEntity_Id = c.Int(nullable: false), + }) + .PrimaryKey(t => new { t.Category_Id, t.RuleSetEntity_Id }) + .ForeignKey("dbo.Category", t => t.Category_Id, cascadeDelete: true) + .ForeignKey("dbo.RuleSet", t => t.RuleSetEntity_Id, cascadeDelete: true) + .Index(t => t.Category_Id) + .Index(t => t.RuleSetEntity_Id); + + AddColumn("dbo.Product_Category_Mapping", "IsSystemMapping", c => c.Boolean(nullable: false)); + CreateIndex("dbo.Product_Category_Mapping", "IsSystemMapping"); + } + + public override void Down() + { + DropForeignKey("dbo.RuleSet_Category_Mapping", "RuleSetEntity_Id", "dbo.RuleSet"); + DropForeignKey("dbo.RuleSet_Category_Mapping", "Category_Id", "dbo.Category"); + DropIndex("dbo.RuleSet_Category_Mapping", new[] { "RuleSetEntity_Id" }); + DropIndex("dbo.RuleSet_Category_Mapping", new[] { "Category_Id" }); + DropIndex("dbo.Product_Category_Mapping", new[] { "IsSystemMapping" }); + DropColumn("dbo.Product_Category_Mapping", "IsSystemMapping"); + DropTable("dbo.RuleSet_Category_Mapping"); + } + + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + var defaultLang = context.Set().AsNoTracking().OrderBy(x => x.DisplayOrder).First(); + var isGerman = defaultLang.UniqueSeoCode.IsCaseInsensitiveEqual("de"); + + // Note, core scheduled tasks must always be added to the installation as well! + context.Set().AddOrUpdate(x => x.Type, + new ScheduleTask + { + Name = isGerman ? "Zuordnungen von Produkten zu Warengruppen aktualisieren" : "Update assignments of products to categories", + CronExpression = "20 2 * * *", // At 02:20 + Type = "SmartStore.Services.Catalog.ProductRuleEvaluatorTask, SmartStore.Services", + Enabled = true, + StopOnError = false + } + ); + context.SaveChanges(); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202003251118391_CategoryRuleSets.resx b/src/Libraries/SmartStore.Data/Migrations/202003251118391_CategoryRuleSets.resx new file mode 100644 index 0000000000..3cad72abe9 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003251118391_CategoryRuleSets.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 + + + H4sIAAAAAAAEAOy9224cO7Io+D7A/IPhp5nB3va6dAN9Gt1zIMuSLWzbUkuyfXa/CKksSspWVmZ1Xiypf20e5pPmF4bMKy/BO/NS7sIClkvJYJAMBoPBYETw//t//t+//M/nbfrqByrKJM/++vrXN7+8foWyON8k2f1fX9fV3X/+6fX//L//9//tLyeb7fOrbz3c7wQO18zKv75+qKrdn9++LeMHtI3KN9skLvIyv6vexPn2bbTJ3/72yy//4+2vv75FGMVrjOvVq79c1lmVbFHzB/7zOM9itKvqKP2cb1Badt9xyVWD9dWXaIvKXRSjv76+2kZFdVXlBXrzPqqi16+O0iTC3bhC6d3rV1GW5VVU4U7++WuJrqoiz+6vdvhDlF6/7BCGu4vSEnWd//MIbjqOX34j43g7VuxRxXVZ5VtLhL/+3hHmLV/dibyvB8Jh0p1gElcvZNQN+f76+jrfJfHrV3xLfz5OCwLFkPa4oS8GS7I3Tb2y/ec/XnFA/zEwxe9vfsP//fIfr47rtKoL9NcM1VURpf/x6qK+TZP4v9DLdf6Isr9mdZrSPcV9xWXMB/zposh3qKheLtFd1/+zzetXb9l6b/mKQzWqTju4s6z6/bfXr77gxqPbFA2MQBGiGdUHlKEiqtDmIqoqVGQEB2pIKbTOtXX1UlZoS373bWL+w+vo9avP0fMnlN1XD399jX++fnWaPKNN/6Xrx9cswcsOV6qKGumaOivbxropbVt7l+cpijJgjGpkH6ttOtJpsj6/yzcvx2V5nEZlOT2BsjitN+gsu0owmaKdL43Oygvc7ae82OCCCsWYP3xR9ggnp8V1UqXTs+TVQ15Uuqb++EuQucViXdHIb3/8YyB2nZxon1EVYRFG2GD6NUEae4/KuEh27Q4zQ3vz8N6nZIuX+eY6byR46bsyL1G2QcVR+T3Z3KPKF1uL5e95Nj0d2qa+F9EOa1QV3rWEvpvUxyv5iZk3v5G/w8yNigDyskjyotmG1Ru6gTC8ju6nl4f17T/wPnGdH8VpgN2HqFDlgy0V//J21ALVumH0fIyVnvu8eHHREKPnNxSGg5Iob0ujHv7hF7Md0pKB3iflLo1ezslKtFk/xvxzhaqqGYs172BJdZfc10UD/abDc+CgtXHQtyitQ2xgls02tNJT14lnP+VxlCb/Qpu+TQfu7XC0zCsgPLCxvK12OuymFtD8ouy+ju4tWQTAQ6YOYfp8KPJ6N//yGtpfqum51veX6Edy3zCQZCZfv7pEaQNQPiS71q4nrqybEfy0yLeXeQot6AHq5iqvi5iML9eCXkdFo/W7yZShW56ipMNzkCDytjQb4a8TLZduZjqqq+wQk7SPa/+zRlcoP8Y4VK0HOMOcptH92RYP9jRJkYbcQUw7l5X3WSmwLcDx4BVOBVfKzEZ0t5NxicpGxpVyAcpBymWoDHCQjYwYlUL3QtddO+NQB1HQOJwHCauXdb7aVU9rjQj5bRqJ3be+0BHmrCSr6yKt75NMECK6qtd5HUPCJ5xW5S8UQN1KK0KchMIFKrZJSRbnJYqbuxNrgXCF4pqYEd/wuA6CQN6W0dWm6b2L83ZHzRhmtM/Rboe7AO93IOiNyD4jj5vVEHZAw2peWyHYRiDWHxEe+F/e1lGa5k/eqiTHFL676nEz/6ggc2iNS7rIaKzg0qIBbmRLclxXBuDCojKpA60oQ+HRrf05xQa/R1pKGyexwc6kvWG+q14yM36QEvK2Ztof+UN6gdAVZuF2U/ATUdfR88kz2u68L70xou7AS/BwU6CRtXGV/PC+e+49pFrm98MVVPuZQNmZQCLz8spGipuO8LLGpwRUGYxphJSMogdQ93uA8hKrHZaTTiQYyVVSp+wFK4PgIE/d5amhuUBjlzPyiDK9e9EJpTDC7SrOR6HaLJeGp9qv9oKyvmVuvZwtr/l9gk/+5zsy3XnB9K8rI90cy2313QIRNjrPvlaDz+t7/OU62TqY0XebcMg+RWWFP8WoLKUojfXv1okmkRhyex8bWCbyhaJmLUDY6tG0RA0qupWHAOfuYi0kzutMssv0pWA3hUKhiyKE9akketliYfgZVQ/5RmLKoEHAjsIQoqkCBrPtcrOXgT0lJe2u1mOneymWCj0EQGx712vCKoqyMCBJJSBCj2VwXvYeD088XDVK8/s3B1e8tTpSnWLwQC2HUGxCxXZUmE/nbPHkmUx2lH5KssfpYwGizT26Rs/V9IQkLV1VL+Oh1c022ATFBXCO0GhknZS5Rlt81K+87wkNwyHCrI81hkOEGdlFVGAJ2E+O/6Rskog4gAiIdL3AnJz8y9L4Qwztg+XxOsf7NYorHpXh9Wlf7byZYBVHhTlKXhRJjC6j7B5Nz70kSOM8+4jJdNFc1nqGAEbloCgf7XZp4u/vEjL+YbU+PZg7A0QCergG7c0R+T26i7Ce+S1BT5/VjmuO0lZ6Iuj42fAg2EFf5/Q5HDgTAnDy4yEEbHuwGcSw2ixAgQF2gaFUNJSKIGHtuXZ2C2n3wthw+5lxOGENfDT8Opyx5G0t4/HVzwxp11f9+VqiC4R5MasC7LN9x0SM71GcbCO8U14U+FeXjeNPr19dEV8qrDQ4N3W0bRl9omauKsz6ZFcwMXoKISAbx5qX6J91gjWB47ze5RntCe06MyKmyU5N/cw0Wk2zyryjVwZMhHilJTLdzsnuhVNtnrzEN9pprUfyOcrquygmsrgwHQxXRzUeBtRgSCy886jw900dG6s2I7hqLD2UwTAG0KAqg6VxXtrNMCoDPVUehlkazUFxkLe1jHH257aZ0rx3MNmtymR3MLKt0cgW2O4U0iR2sGHNb8MKYSUNbaZyVk71xipYmQ1jr6JxwzYrGELQ8iRgfqre2B9rPa+p+2bAcFDx5G2d5ikWFpYbnkYv/D2I3D9KVTe9YfaWefQKck2elWpdKUjk9mcsUGlX7/kC8pu1pml6mlB8QPGy3cVxn1Jr/Y2opZsm9+3E+u/3ZFM9WHXtI0ruHyqrKuvd78/K6yLCqwdKpLeMlvUx2WDh6oul24ECuAWQHQCKDzdVEtotANYQhr28B6J0A65M1Ap4AFvDFD06TfdYUKiTNISiqwyYbYc7E9yATeJLykHBqpccSvQplYPaKozXkSS8hW6djWRhSxSkbYptSYoXf/yo6FJTLlFegXKBdiCQl+Noi6ZbLl6qa4PjoLzK22p9rMIqrxMFCF6ltSrdaRgXyeMoe49IYux+1fhtUbOpOI2sPKavSL0DqB+SdFOQXVoqy9o9qWUhUZ7RpbDQYEFsBa18e7Dac+GeAZuyeSxES48QZIO3ApCy7pL2KL2tt36CtkERTM6+eheViIq6ovcDzX16qc49GEZIdFn5sbB98BUQkKHRfSYH9c1nLjskh11T3tZ7SqS/S7KIxLiYiPQwCV95ZdXjGpdHdZh0eVsdrabzQ19BJj61tdv72CWkD9Gf0CwPjmb9HoAVve4+6PvcA/plBOt75LuUDytYu4JDuFO2ylcTb94k3w0hGb4lZYKhz7JN8iPZ1FGavnibyAjKJKWem6AXBF1q7WeIe8k4L1B4qUJLtMu4yzRv/szpkEGCJ+ds72izTbLjfLulTMCTNdavskCeOYEjhDo84e75D55Dge4qKWeeS/QjQU/e1pej3a7If6DNZUQe47gih1yfmf6SV4Ex9uiu8QaXcoP27mM4pCuOgbt6rKdfdJRvBt73qy/19naUGpM1+6FKVAs8VCqfD8lddRwV3nd6PZ4Q2lUXF3FePWCKD17efv3rcI7KmiauPIhQq4kuX5GUQenL0WbD9cF7TGfl+/wpS/PI3++tw+MdZpSl7QLvEfqb0KPndsGd3wk4HQ/OHZqT513Svtf0PnrhcZqhaPJgtRErAdj+Y1ReRVhrQqFmlcVmecuDe/O1RMXRfYHQNoDvAINsliwUZ+UlSRVcBMgoOSA6folT1HbKV8bRGC9QkeTeq2/A2ez9DWLPtXJWktRAJxmp4s2OJIV8uCSfWJ42h8wo7TEePxATzGQxgmdlsLyiZ+VJ6U1O6qlCb6NklBH/l+wHXpoYXZsGyvvkVuXx49/qqLPNBLB1NhiPfkQJrstYNTzdvcGeOm9YSRZw5J/yp3bUZKcJ8NIYPiAkdy+NQeA0L/o+vkNUYnA3xO+i+LF5IpXkRfDtZXMaJBjPWlriE8hw6PXWKJpDP56lZFtvw0xSizF6Doexx3JVoV0ITC/kJd0iTxkHU2Ky6xtiyu3d+RCHx1u4EwZAmw5rogySCaSsY1lAOviufnlXV9VoXPGQLQT6e1I+pElZhUHaCb8U4cWL9zXGfuUcM45PJw26JPa2rzFIgu/A5+lm2gYGA3Y5YaqBHcYTpZYDMcc5pDIgzsIOSQloXF1qA0dMvTnvJKtQUQbhr05sM5jRxEzRCfZZ28SHr+sEtWvSe8PDOgQqq6MKi87bukLH+fY2ybpL1IBMOEH2re+MH35wKrPnuODombiD8CwyLW2GncZXngyI/IRJuBubtcWc9m88DD4AnkYfk2cewliP1xvzgqcm+YGKF1LZ0u7Va7KYEraO0e0WUV5HRXJ3p70m+D3IFBBxW7yc350XyX0ipuDRHdpK1G30QSw8A77PKCrrAhEaKigQJGRvaNM2RZQhWvKDRW1G2jrbpKi5PNUYO8Osxba9C1Sc4dUfysbGICXUCI3z6iFvTXd4j/G/KUiyi6S5p7M3dWMuITelXX3rroSOh7+wy22kj4IfQK0fSmAFjMrB7kaAFfzreBCZe50AZ+sPSG8Ayj6zgGKH6XJpbxkgR9fFdl2QRaEMehvBbrqVJHVmlIHKwt+k8NYhEN0xWp1vjYV6UXhl8iCyAQhwjh3Xp1cDAFVepQCYNAYRgnUcRyfNDIMoFf2XOrtqvWIdez74rcg73oIoes0CyLrMQTn2tzGa3CVxAzKctJX9h6soxqOuIBufppbjeKVRrj1eBk6UqlSxVKjSMI4y9RvWxLE2D5k+lN1X1FNMkEEt2SyZVHWcKh611cAtRms7ROdx0Qc0JQ+ygCIT0uVSLmSAbNmQd+dQdJYHFbvLQkg7zIFZh4tTZkegu0MxxBlCocALIoTfE0OtluiU/xofC8ld95sOxyFwQt6Wxpg0UUac5g6wfJekaQhvkQZZsAdmn/JPiBDxrMw1yZZ/C5Fk6aFAyLTB3wM0+KXeIrxKucY8vYe/Rd5H/HCm22DW5MAOzUebDcZSnubFNlIZjUI/L3GFJQU57/1IMpnoZ0BuBtFLvTYHAYhvzYFQXtsAg9JrM2AwHbYEeVvdvHm7Iy2zs9zeEnNfpImhCZMnfw0yS2fdC7neeb1ULRWc1jtrs/NY7jSiw2p3XqaG9zjWMaZpXnxEz9+itJ6/9W61fcrJfdHUd1jh1JGzsnuiSpA2TitNsAz7R6iPuA4rTt7WRZiYcsaO74ssmC93HPKiVv4YketGO4vvxPxB50mKWZA2A/pRiXj9Xj/U29ssSrz9fbvcBz/h+XBFPjLyS+qeKVoeMbx/5Grd0JuE4i5SXk1/L6moa2tF5i5Pp71wldhqtRe0lqZ+w2EYDcC866pOuysa3FSH1Ds41Ac1RN7WSDTvkJ6e6MEQNecCX2xnxPOnfTkm5DMYtCxcRpZq162BHPZZyb0vhv/C7TEd1qm8rUAafqh40rPyFGs99ZjqYD36WO9t/TlyuwmSm9IGhjfwdRqB5c5Oww/JUhYBJ1AbXLyzdP0NqTAEes8QwHYQN5OLG5rcP5PIkefXZLjV0K+QraR2LmT+kKWulFaYIummh4ekSf9DypLWFdBfirR4JkuH3QciklBrkpdljpWqSwIXxkTVEm6W3DBtBjc/onxE6e6uTv8blU2+lSDIvuQuuHxWqK0frWRVSrxtnfx+O1pkqCzxWtU5kAt1brj1LBmRtIbaVVhezc+VrVvVLuKnfwN1+HXQXeRt9TT6UCdDq+1vh4RWme5iNMjrd1vKwj5ZKxdRWZIsprM11Po4+WdibrFdRcrXDsN0fdZEugHzTR3d3SVpEiAbb5OGyPuaJnDg9FHMXChOGjgdho8+RWV1tut8/Sb3QAp6BUW6/im/TzKCwSGOn9Tvk1lJUVjnOUzrSufNFcRRQ6cHh5mw06QoKw0j/vZbiKdsyGzM0hBJea67Vw8yQ1g676Js+keAejVCG+YfhiXeJUX1QBaI7XrDitJG2b8wm9W3qMuLTNz96tL/vLlFf88zar+arOd4k+1MPaQh346TBYUVm3pLPBoqF+HY5kmdQ8HoW3uPjy+x7tnj0A7n3eYnOdr1q+uGAhuPcWKpcCoFQGyPpF3kybBLq7rJwwJ9ZUHkHebgbHvNW4dU3e5gbsazp9hvHkY4HEsBrTMN9Nn/MdrudkZNdQpQOQQITjoMENhnKBrO4UABxmEg5HzDgtmyDXlhHZ9m06O6eiDn6DZe+BLF7bsWwAhUNcDpMKogzItZLevHI4mIvshLSUKOoRgciFgq9BoAceridb5LYkUfm3J5J9liuJccjG03m/sRuIdNEdg5tkToF1ds26VLVNVFRpLtI9kEMyBgF2EIoasSMPsuP0XF5iJPsqr8mJSV7B4ZgJP0Xg4HjEEBbDuSPsTSaNsUgAHxx8HIBSAPaB2sTeUikqdh4aFA8kuBxGg4KaQt4b9HaYoqFfMwEGC/YQih0xIwL/v3wAL25u98u82zNx2Cg+lb3paRnSTQqWqWC0MTQ0m4s9IsDemM/WFa0VtJQrUDh4lq+JQOGbTNwUg/djjdg3qNrPl1roZ+m7yhvyc7oq5GqSa7QKA3Ah/yDGktZ4FkRP+A0PTM7mVnd4rR7XUdIDqXKxL0Jr7cWl1ig+4VneMgxS6yIcKyjrJQXg4/gpEkwJ17h+ugfygWSEcqb18/g6uz3wK9j9c/p+h7u7gPcWDjkSC00VB2aJNaF52W9bs0vx9u562XNKldvqFwrMODr+vMLF5xZPBEDbBeoHIjeocRZCmK1jcj4MhOULnASiCQNxu1XfHgIYLgsBeoJGuwYN1PUXZfk/fLVuLkav3oTOM+SF1CDn1ovRzFYls/+z4tq+VZqmvatfoVigmnvLufXG71xInuy7Os67W3dxF5yGp6iZtvQhxXXbLhdUIz2DPbHb5jIFW+/SN14ZG2qWgnns7hsRQHz6jufRSHmoc36MN45YVN5TeROYDSd+AbCietijfxK1Uv0672G7O0mw3WEYrtI1MIqn0shK0BY9jW1N2jwID+jRnPpR0cQeyDItj9V91RERroLw8k77YAGS7EigqhCBVtRaFcx7GN6Z2vZvo9KrsBhotX5oJk5oyvUYYQ6cNynBhw8Mj4lhMnSmuua+qXbxg06+C0oUu+XNZSJhR/Dd1Se9iQRm8oWMDNhgURuEcGF4ZdArDKwQwgb6vxPvJOWxHIrhzIxmZ70LzYy0CQoO/1uduC3IzZ1g5+UqGjNl0bCUgitzR+iPZSUuqRKJGm9m6JGsLSgBBpKedDOXEpIH9x3nXGS543OA4CXd5WQ6XVCHQyWyHCOrpHDAJEL9mawgY3ZS/Nrn1MyzewJYSS2eMJNcU9vnbbsbObHfZDD2dyUGhLHM6tNhddBzsgsHfNT1XXWgD/vcRzGznsIPK2GgJ9KPJ6N/HrEqYuItbR7fMZx7+MISJehAqyyxBRHOQ89zPuNf9eGaXHNSyX5jc0ECfNqTJYmtMA/tK864SXSG9wHOS6Qsj89NL4p1zj016SDq/DHcUB7zbadekvekAjCiSbnETPF3woc3dXJLXfUCjWcRnReyvOEv4zp2skobRTHnQpk/YYQTalJvZmBBxZFSoXdkoQyGu3HLvizq+Hp39+anfG5mGb+Zs9bUTX7L6boXxYl3LRXMRf7d/R/XBVzmUH/8EgI3NzfBt2YsjxTSgEN3UWIqzj24AfdCQTS+UddHd8oxQXWIt3Uo94bV6pQwX10Ruwq3z0pEByCgf20cvT9FtO8mK36XUclDyConzDI1rH0YT06igrnxzsgPLZH3DCHnQcIW5oeMptTg4m+sopYL1nv++ax7y3KA76vZoPJ77ZmEjbbePoz+86hxWfAUzxugYhrHQZdkukhWGXHlUELje63NqFm1uukkz2/nJCyFlvIFOc5YSXhDjIBnlboc7+y8iHNoN2d5KYXLEPdva+esifzrOPWJG4CPB6cHO4/dCknrvOnVyqJ7tVWOa073vydTtlNdIaOmExBaC8dz9ZjTJWLucNNyJInkMblZMMB5KdBsmBQuE7SHl5W6H8EWiy+7+fNtW7iCZJRbxS+cqSiyjz/tp2nyCzHkJbyWwY5JvVUJoKXkc+VW5dL3mgQnwQDNMLhnmSCPaz3I7uLpkhxVrfZKcTzdjyOeHlhvvmaeoojlFZztPgBUmqhrVLoxdwQj8EoNwfJkoyzotZu9TkTrK2U9+thWqbX7v95yA6FYuGEGimRI9NW97vxzUDXkukDPvqhHe4DJuK2/JSuiFvmHdk+o6EwXYRvZDLoqDIPqPqId9MKXklHHNcFwXK4pdZEr72jV3SLxihONmSp04vCvyrkWKvf/3T61dXcUSI+CfntRD2RZ/hZaN5xMpVfVuRF2DPsji9JmiNiPUH62ExjZ08z9NY87Ytnpvmvb65Rsg0Os9IO6kzzwi7xmYdGW7IYinbN8YIR7yLJERFiNJThKamqbzlqQksb3lqanf4p0+P1vDQ5Ezar/XJWjkuEJ6dd1EaNQmtpxxLI7wu8Wg2lFFuwqYmawKfJPAg0OZoO+nU0G/ZfEcFwusoQO7mBxQ/5nV1VOGFcVtXaE7/NaHx8n9tZ3ggotOlQl1IBnuBeDjw7CanQXOTR05p5H2eZskfY95ilUFnlsKYCIbpJ5J0eZZzhUCb6Vw6o/IRbWRTMukIj3/8mP4xDtLQyfMuKRrLyOc8qx5mbvO/UTQ9Pen19T7BG0D1Ht2Oj6a635cPqLqXwT/m6Rxv4IoNz8SYVMPvouxxlhM91+YsIoZu8+x4zuaG91Xma/LsNppBueh200YBHGIRp173NT7hFMm/GklzXURZGTWJwmd47FnW9CxLRtb4JSrrdHqyH0c7cjMwL8HFRmca7VV9O+jo8w75oi7ih6hEc95IXESJq3tWb9Jp7Q7Tz0vXHDE4kFfougcJZzeDv0cpqn6qZ30UJ+FLRO4SKQuC0T3Mx6j8gp46s9SXnFy0x8zrkc4uknGMdtX1Q4L7F+HPjbvCxyjbnP9wOFn5PODevjQsf70dKhduj0EgVz8jv5eU4a45Oz/1lrSvZXSPVE/LQoA33ZX32Fk5lODuqAC1jdD6kNw1p0TtICDAm68l2nxPqgdhMHpoYVAGVZye35a/VjwUi5PBFcGPcFPlTj37Ik1tOhRLejYWwT2jyu0f294gtEUbWkKeSJ9YhF7HljKFFtjo8W0/liA7rDxysi8Vyc6WAC9VM8VTvAveYZY/Cg4CSISe73Pgtu9pC/QEijUvacspa+xmBEtsa6+jHk0JSvaDH5K8reF229PO3YpfX2P5NE/QjBdspqrAWEOjDfQfhUWthrZd3N1inUKTMeo4Kw2cVjqs1rj6F0LYDstc3lZPL98Vymz1IZB9i9J6ujvpiQRKT01ztXysodHI+4/CulRD2woUTiOc43RhNCBY+fQSOP5C5iBY5G311kNKKkBZ58ykUwj/x4kdNs7KvrNHcZX8iALY5HqEx3m9m8m0f4mJsUuImWwO2+XQ2jxxPlcoIwfuOUbWNjXPsD7jUyEV7z1ZO2flyB2NEXVpq/NEljL7LdnIPiZs4KajgSWpcjw3sjriiCSg0l1ZBu8fVOSYj5MOLDqk5DQ5jGMieQf9BNHzL9pnEX3R/K2OutF7HTqypLooknhyz+yhockdsecYzSwjmdpxvLdttDri1CRjW5uados4+87r5Ps+f8rSPNoESG96VvbIgqnxn/ACyUrU47U8AhFp/R0l9w8WRxdNiGSdbVKE1axoeneOVsAfN2/9TbWaUJr8QMUL0RktadtF19EYwt3aH5VlHhNP8U2vOsGXRQF1NpkeqtPx/K3NFvez4C0XcH9rrB63PKbp1gAFdawrVHSth/DXcr+4PdtMa7lfQj7Z/NNqub6Z56iMZ/NlnuvjffPRR2aVJ2yNJDDzhwCXG+Av4bTYAH8Er1RAAL7DGlTwXaB8Di3BQ+Dgwhzdb6YmNiTPZc6bSDIo/f8gJyHIG1AFJ4gNJXD4+6/AflQmowl4+dV7PznIwt4RaXC9Osg/eVtBdJDrIoofMcVncr7vm/taqI7sv/0S5PmMJko77MG2YVDkGj/QnwIdq8+uaBk7QPISRuIfaeuWKXfWpSFuRoEjdpABkDpqslBeXoU0yhAS8HDfoF+RDZ2COBG6vFoX5qpAuxhCrgPZeoVXi9M64HyCPY5FDKbDUlAshTVlyYO1k0X9Z1r0U+eiYVq5INn58Kys1MtQcJaJSurmZnTcJy95N89jtOW+p8tfw+h3R5ttkukjsf/g2Jrb+ZMNU4BOnjCEIJMlYOHctx1iMtR9DGJSw5I7u0T/JI9CuNuwGTSHLUPeVpAtI5jqFGrvCeOt0Uq707xouWl+S33Hv6i5Jw5yo+7XAXXMtdnU8k+ZTuceWUV3d1/yOVL1TbwPCZNCybYw+XXbhHPXeSvOhfugGTUQv7B+V4sxRVCJrRiCAOyqIJinMTVv7AHHeAfz817kMR22RZXwCLAtXkRFd6S2PAa13jMOFekpDhHPEOyGLYwT5jJpHfGSRAXmIeKEONkxLozGsg+ieGRuiUGLlVI3NDxt1pKCAcYtOWzQ1CNCQ9BeIgXS99v5DKjyXxJaAdyYZDD6LgdxauqQXO3wWuvz6gyywMW0GFVRmt+/UeI9bI+Ty3OY8Oc7NvmY415BEmaeJikeGPREm3YffTrPulGCD10auVi5PEPptIaVjAwtaKMK4oOPRrWsL/sUXOAwWjU6UxKosFjSRYnK72ygHKqzWFShPUhFW3G2n88TH6VJpDKc/B7CkhHutd7We2XafA6f0SaJ8I7if6rK01zlafNr4OsKpXiSPL67tIgVnvT1l9Z++xGsOyvbleCh1Gr76qLG7YBjgk0n+HZz2GgOW4Lf4UKrw4c9UVhiO41iVF3lRUXhamQKW+CC9RptMQ0r9DGhriMG1GypbehitkHPrXAhHwI+UK+SYxIXwPmFr+A+6CHAfSwh15HTG/Ss2QMjOQhZZyH7B0MlzdZSTohbPjhEBOvUP6WidzPyRAlocHSxTDVjYEIw+beoSKJsfMvpON/eJlnkebjVIz8sCnlbV491gDWhbuMD3vkmb+RzlNV3UUwmsSB51bXhD2GatbxEUiNrOxIK2/dkEw7Zx7DZBKISNZSz9Q81REt+sKjNvFXLMrnP0IYyDKj040BOjx65CPpLRpIdxjZFRNnkqvBWovfyJhgLzfgxzA1tc4g4r6vzuwZp65oRbq9n00zI93vF9nfD4hB0AdOqMiu5cf2Ql5wGbctvSQxquQzW9b6EXsWug2VxWI2YruoybKa+lyWKHYRD3nbyflhevLyhER20P3lbmiPRHyc5EZH/X6R1Eank/DRNm2WzMmzbyO71KSe6y0yNBTCynZXv0V1EvVFmtoX5HgXDn/8Oy17eViCdrUMT7GL0Gj1X+NN2N/0DeSQt7T9rvF94J2qjtsSsKvI0hJfmQt4m/BKy0KBs1SZnXYnnOZfxgNZj0zq2I1Rf0hkMlUfc3IkrbX9gjRup5NXSQFlfZj20QxLCvBhwMznsIga7iO760NHrYRVXo7OmPQ16DRlOFVz91SOH7+R5lxfV56jxV54gRsx4T+ruOK+Q+ooGgDfZoUyqyYSyUd2gsnhsKKBUHpEe5LOz5Ax1/+F5Bgii6U25lCT6ntUy9JQkFkMX+kAvQB0BFJUNJYoKwwRyJbhMOcgT86U+Tq1/aMcKNMlAwdxrdyoWJpXcwm7+gZcRHewe3Ne6Tdo3Q0NnJcaEBSSKA2ThD6dMN8ffELahT0n2SGJXl3wExUETXsMWZraTm2yCIU3fXYxDaPt3g/awnakkH0i3n2IvC/VG3GErA9o5bGX/RluZaC5fi93d8B7CzHjvtKH1jxM5bF6NaHnTIzhsU4p129HI+/G9r+NrUlSubmdxIqKa8LL2uoiyMqFkrWu3/VKgGO+PGkN1M/G+Aq/FolEEwuysZIzdgpw6XOz4IcruEdZrZ7TfD/MICvee028osFF0i6WCYAZAvMTudYKKzu3c+dAw4DiIXnlbgTSToEmcCUs5Oon7OvtMmS3rOErjul16n1H1kA8jbJbgwK0AmHcGQUJRcOEPrd6woOPihyEEASABC+kOPTYxQEHdvJB46YgQXhKqEXRuIY2tYtgjOEgneVuBdn7n9TPsZvD1FL3ZsSGIbIlwscQVe90atXxEXqjxZEWC4sCM8raCmYqO0tt6O7tf+Iwa+W9//OMkYcbdnwFaDqM2j+sGVpyhcmFfAoH8jPDRC7HZ9dqDtVDo6pdvGEQH0aDgTJpQVy9lhbahbmJtHdowuJljX6jE3nmdbRqLa/Nw2UlGOhvgafBtgifiOm8mJmCs4WWdIrnTGj2NNyMoZcUEIUQ7JQzm98gExnLScaPRkiYVyn5Nj7UP61jBzO1MeWemx2hI/RlUV67l8x0ZufIi6rdJGmay2E3miTdF0Eg353CS/WHV3AxgVIJ9oVRMri+CeL9SSlx/nfd25qW+EdNBKMjbCuRjumLv/IChlfcZpvXxA+Fxb2f3ZZQAdmGAWoAEBHygEILzEgHHDyh+zGuvUKDucQ0B1UEMrE0M7GW8aMP3BL6ztIbCe1ZeR88nz4iihisqjOgYs8F9XnibRFYWHSvQLEwOnMmEsSCEFMGfMOwNIBJHYW1aRzDSGlf0Mt/CrQSU6QcnSe11PEexiZOfT5WE8eAaOPPJERA7c8ssXuO0FnZupigU1wXh687O5f5e9vDyKYjwILXkbfEkm+bla1sXB6+nvOTnNW6skgcRQR66ESvTTySa1QEeTTSsGHSdhVtgh5WlYOKXOEVsmlLH1UAQXaAiyTfeaYPI3UqDr/RDdFXhaSdLMoRzaKizxXu8yQfY48+ypEqidM2SjO6ikRS7YWvIRRcDqJVXLLStl5Z0/59bLPNHNmt57iSWG6bHnz7lLt5fuNY9McBTWA7SWN4WRaYQhpVQj8PqH86eKGvPRIKJIzO4lCmYGwF+XL0KMEEuqWCDvnJKNwQ9cAqVK3sb5qFsgYwhxAmBP4gUeVutgw7u0VNebBQreKI7ep3z3DStOjnmGPOx31Z42ALVpvf8/hP6gVLvII0HkjsMlSX1rtTMrmn61gPdZe2ONpsCNxZgoTkqFDqPwXukjiv89Y9h/PPQHSoKVMzSWNCn3Il0CPK6iWW7H6tqxwXoQHtDAHJ9LVERKMQhiJIkU46USlFAZejuLkmTyMmjYahbjj8Pe4q8rU48et9Th7HcOJqR5KeZXvhDJ4GePW4GKOoYwBeKZwABwvMAkF6iuNFD7X35iFUDo3szIDlwvLytGeNfJtqYlPGx1qbbs00acMVpI00HHpVGmsIQwCEcBPNahVg58liF+Zuh/mEB/swL8CqtVTkbJmo1mAdXlN3X+NhhNwPGa6hhjCT2ylCfb7d59obHdFhUUy8qPKIPRV7v5mdu3PL8jeqCVSYy3TskyTBPmPKAtqhJfXWbuiy9pn75hkFzWHfythpCTR7vFGib81sNv4UJFJ2S+xvcLrpb48vc/nPg9sn50DaRmdJkOZWOV4aKYW6O5xojb5gVfJrjExLuOP73KE2Jadk79upjXlYzvAv8Kb/Pg+XVCBc08bHapu/yDaVXTXZTQYJG8CLsH5H9gqqnvHicnGUuigRLu5dGLhzXRYGy2FuL7HCePMdNgrtLLGqcUSsyREkbgcP5yAhvlLWowD4dsBjip61hnwJLnBn9yDhwyZAYKPVYWFC/iMWhWx7P3fZIDtu0vC3NNj1NXqF+Yo4xClXjU7R92VwWGcWf/Ml1O5nnddvWlok38C2edOqJtKna8w3n44UWWWDlw5pSeAe9iw6aU7eVbSfZRvOGWyAda8zQc1ahbRlIu22wfqm33bLzdEufJosQh/U9yvItedF9zI8SPGiNa5KE/A8z3ESDddKygcMT3gIEPAV3bwN6HoY7LIfNVt7WT3CdMaVFJsabAUmDFJWP7uGBpDbmSBHXgTEV00qRy/+a+LLOsgBPtn6O4ockQ0sxOuaVULv3aZI1mo4Em6YndRwjtHGsfVIUytReYSwS+M974thygfDpcAxxMHNr7Orq/TxdlRvpuZlme/i8LIqRG7YSdWrWwIpnZ10Fv7RfTDcDiNGD/JS3pT1FTyKjdG9qhXLQzbOT5x1ZourEYoHOHm1Qi7SVP60o1gG0Z+YF9ahAm4Uer56xwF7j2p1njBh37dwX9FzhvdlhFzkrPyYbvBa8T1d1hreIbmMP50kGao5Ti3M+kNRY/juJ87/VqEabEyyb06OqwgR0DO7vdtnyDYjwIOPlbVEEC/H0CZ4EYiZkkjUSHuVmRAC1FUjRmJJgOpnt+uSTZt+cKK7tM5Z4ml1mqpYJnbop1ZHKMcE8uK7hXPMaUEFh1cHbXlvR1LAdy1DNaDgdtM2I+iq2g6JQWoyJqaUbEvXVcER0Da9TBdPPIJvPYctRCG9Om3TbbQg7zX84uc7nb/MS7dKXIA1rjkTHkzfxLo4nb+Oqvv0HilUJKQKpB8QvZnqvmJCXd1d4gV4XiXfqJIzGzWzXCPA4zmv/NG24E5vPUVZHafoS7sw17i5wzFzgfY4/bZnvjKYDokmuG9ENCwwOhIFR7dMsoNf2zHbLfX+m8Rw2aM0yVR0jpnnVqvN40J2e3B62Arw65x/hRV7wtwi27hclnskwBLI+cpelJk3MRC23Rsyr0vsZa/IoNrqL8KrGu2qzFih3jcB5YI6j7S5K7jMfedXjOMgqeVsaaTFVAJNWx5yo4UA655IeZF4OdxaPgTaL6Bpt8Z7iFIM5LEMO1WE1Oq/GieyPP/Oh/DOGbB7SmP5K9FNUVm1zBQohU7WmgFYfb5OHKMcXJkHVz2UYCBUMH/JMHtiX2uuILz3Y/+pwozPW/s2r9u/a2sYb3Bf0VH5CRD5jvqZeSHPf52CMh+1O3hZMsQ91MvSh/e2yJOc/Z4WRJ2ENlvaOuSKS73nxiEk4cdaP07yot55LscFRvhFQHdaggkVCrrxQSbGbGfR/6mCXxCtN36/MnijwL5hLUQ4lmJUVoH7vwxfJD0yLwUfXdcGyeA6rVbFaQwh0wkOhVup1HgqT/njxh2niTsnLmPPbhMgjmVGAhyy7xJnvXo7q6sHfG5RCeIniZJdQjvvLqDRSGUqzMShHWbFyw8KPglQBJkhSFaytT9C4dEx6T0NL+z4C6XpOQXptAZ9RVGJB3L7l5xWXz2A67AHytjRGwokeG1j4rYNLQrqpI/Stg8Vtl8l7LPGy0u1wI6yUAdlhsRwWy8+0WM62u7wgj5XfJU4pCJn6h8WxtsVxmqebYM8VWMfcpo2Tf5ho+zCYgoSYtTzfqJFog5XryPvy4DHZeR4No0fkh6FNlnKe+V9f4FV3mqB0Q/6aIVNKz2bHeXaX3NcFG9Az1ZXWyTOWXvTcT5hrLq232ZAhZOLWLlGJBfRZdqe6Lg7TVBeah5E7JT1wjyln9ixp+KEcSjjvKUD9Ishfstg9NQzhzj653RsK1WGXVizrIPlhWv5QZ8n+dRoDm1Fqmok2/CYr5nO10DGgofnHqFQFW/4hXNa3M8skG22tvk9T7xlNY2QDFzZz3WEqi92sh+Ye8s9EWL5HuzR3fR+dR3GQaPK2ul3JV6Qts6hDpZSbT6kZmTLEgcXAhT5QZmG9x3yYhrSZtUM0Qh4DvC6irNwmTcqUEFMB4RxG0qQNaIQSDOZwcm7NWprkAWHm5Kq+be0Ek7dk7EUZiBGa9gwuOwMOLox3IlmPyQ/0mUrP6xFJ4hKPokh13dkKgZMVvzHfDMDjuUoGI5yqpIB+AXrPfgZPpv5B8ZC3tWKD50QZC4nYJr86Uk0vt5stItBYtVrkjwQT1rTJid5U6zbFfvF6epOHUS5bgRDSGuxnFxOZEjPjHIdO/CfZZ+ewvvbs2Np8ZzHDzniGOL+7K5Fn8G0T3uCH4l1UxQ9Xyb88LxYusNRoX3ryXGcB1avjfLtrPK9sFNJgGTL+nuyOivghhAM7qTW+AOiv3I3aFpxTwkvB4zNIaDXBYBZ/RmmTWvzlUBLdNLzF/10UP55leL3Ej57++cdYKKb5/RsJxoPmKm8riOMv/nNTx/6SKpDH7xK+9RLWAz3sdbDCCtRWsH/CqZkwq5EMdfQD6UCNx9HD+3mM4t3hLorJKi48wst7WQKhOwgSeVvLHEO/JegpkN1wbd5qmBHRfV68BOBlHtWBjw98PBsfd8I9ABtzmA5cfODi2bi4s3kNSpAzE7OIDjwsb2s4Vfwa6HTymx+e6Xf8Ii9LrIOn/lzGozrw2Vr5zCLUJ6svUdw4LtjzxZbky+oRHLhB3lbQiybHSx8yU77m0lHfmtjO+z3Z4EP73/NM01SQ13qSSvnSbaALwPW9FRs6g1PrJnKdH8UBvSWa11tBmxJhaFLaip+blr9HOxJQLBjQIRivN25YhB4ydURykKvythpKeXsKFbgxQu8AFuvmUjWQ44Zm6CR53+Q3qfPIxquH5hqLuq2ZWhijonMvJH+rri0DXbevT/i/Q/dJ9qHIHS4/xdl7OnneRdnGf3xf8tM8TfMnbzzo6XuSbfwRncW6BwND8H/1olxlv4ZohIzkOE+VT6mGGc7Haku9LzZZM8dleZxGpSZCMkSu1H3Qk1r1J4yaxF+xqVQpMzWp3tJmrzpFV3FO/LrPytM0ui+H0RrpS6R++WbAEkxBwlyBxWr6QhzHKMnKTsBntL1FxXCdQnL8f4vSGv/xizBZDGwjswkVhwq/qiuMt7wdvBgixcAPBp4O/HdxctppUEzNp/w+iaOU0PZ8R5QyIi48JgnAt+B0HWUb89mSTZMRHbu5+JaUyW2SNpqsExUVNxYj7gUpetpUMyPpFYqK+KF1AyxH6mpqdaO9aHL29QtBU6d/6bVfCL+4z+Bxnm2SVicNPYED6gXnD+tJptOHT4V1cdtpsYaT97WkgHWz9j7a4kne+E3b3+qo2YJIKG2RN8nUA84cgH3BybvaJVlG7Q86+mKFAWvEmadgQz8S9NSdQwKR9x2mLUmsyaNekLbvogIZE7Z9Z8ZCpDUDNV8YLf6r1jf83b10hUgaEiv+QSuq+Rp/dFmM36M0RdVFXlaY8JcoKsOI0U4vKt8A+JfULjbbJDPlmQusYT9EJTJVBrH0TaitVqMKEllN6Tq/a/qCCZZgJY2t9AcdU94mI8v/0YE7TqMYVVd5UTWnNne2aPWKNw268g2NdUFm+JhUJbFomfJDA3VEwWv44VN0i1IaXsMRrOHI44TQ0Le/B/qYkPQDQaeORr3kye4BxY+3+TMqTWewFUum8/elxhIsiS9JbhnZHBrNx3WCiguMCR1HaVy3p//PqHrINwH1HnkjS549iF3HWN6iIkZZFaUykSVK839garX5NST7utH8HNdFgbL45TLHwpVsU3UaQmUaE2xCDSw4K003PiebXY4X8Hta3dSdL+iKX3emC+kofYpeyqYy05pGHlLVqLZcBCKJWRlfufaeWIKufEMjXfJ4mBfbyPiA/zG5fzDev/JR//1Pl4MI927uVfvW/ac8jgKd1rmXZGUtLXlGSfNb07khodVYwUNEVBgrna3bpcfmpErp4S8C6fRs8paWPKAnJEryonl+1myaPuOOJDvc3VO88sgAmcpG6+SoLPM4aUg4rDWSPbe9HrlEZWPcv+lfyOH6f5JtXrVGf2Wt8YrgZcjfAlV4/aodESYmlmR/ff1/CcM3bXBw3aAa7IfANfIrOybcyHnWPhTw6qg51xLDfRlHG/F6BVN0w37pFg05IB3nWYnZA29PoudEksV43lKboXBIDB0wSCeH5viS92iHMuI7YTOHJv2g31MS+zM0yxFTR7u/vKWY1YCHk381Ps5N3wwZGKwi5V4a2pp14ab2j2+V45iLaZXzthcc25upyKBuRocT8meXR7SUsq5BXYiH6WqTMpddT4ElAELqV5wTU1sQcwbutiCYSW9odIux+vukbLJK3Bzt8OQRT4kuODBBch5XVYKYu4e3kcvKNgC27GMaNW1YkOYz2iRRm1PppvU0lBJEBIXIQEHZUAJADoxfgfyXN2/E04LTepR3ZYblJyeySeOdr+hS66ztPEmY0c2RerZHQDkjJeJOoWcjCvGyTCR2ZDYWEolr0nRbYwUM1PzqTBkGbESDT8BMDHoZS/W9nYGnoP7MylkQvU06QNdbjMs6/xZuNA13yFhBXgXiNh7ahukULUllGdDE8mc1/UBm4Fj9vBlzbf9a+iIse1Vhza+J38hidHNMdEasBcp4CISGGJUBtOFSuAVIY+26ujr2VI5gBs5UzpHRyaqtsrQU7bt+fndeJPdJphOhPLxCfjqITQG7OUuG26c1fZlP7MlobcFefdXF2QxLh+QHKl6afGY6LqCBAzMYgxrgLqafk7MY1JsZ+Quis0nzdL2lOetdnW3wzl6h7VFVFcltXaE2pe7NWKJjOBMcCj6UVndgUKOuQBZPvvr6tmyHEc63FmxYwKRXY631LJBuKL30NOZEtp7RQvDifK49ObfvAY/DY1mCr+FZNOflIVXMatjZnpHnY2EX5g2pWsi7sgjvOXDdSvitv7O5GS5vNEwgVFBwm+GFkFkzFhdOqxGU0lHMx6XS+TI6ZnV1VsOlhjKRh5+YR/d4K5eNYQEG3V8hSiemvqH/0FrsZRVVdnsav4PpXtokZMBXtLUaFtaOaEZLvm4+jez5VL1Vcbah8IXqzMTPeyyIVeNYiIH3UCB3b+lo76A4OIg9OxAbluSxLmHel/RhBhaS0NTCnL8427A3jrppZi/JwrIQixtgJOXlaHh2AvszI1OBtDZpn6m4uDvzzfjYl4wDRFCVs7INbwGYAcYyY1qXsb9LUvKsYN+AtpssfHAqcOjNSSGuLgdqkCQoxH9yGIKuv3wFFT06WBeyCM1Am5iE7MsrU7pRzCCxdPNltB9Sz24tIq9IThkSPKePExIgIb7sgWwYUkS8V8FA0u7PwILSOTFpe/HQn6H3esdHETQ497k5OIZTwOQdmZOR9tQxceh/l8vJgqP4GsEZS2hgYf6S9WdONpMR3cz0wNZdlOmO823zpOzAISp24IFlrNbB2XKbgB5gNBknr2MflY1gJtaUzY9J832dpQ25l83i+IjS3V2dZuS8wxTobLqa6grzrlDTwcara11u7u1GtzquthvYfPZfw3m2MAW3FRdj/9O8qLfNotUetEVQiK0HKBs+BlBbHauD7PTyTszAX3Li7sV5uOn+db5LYkM+YmGljNSAWXMSh3whVoJ7MRcvwQTeH2a6af7fPiGinGwKUMpG1hxEIwXYh+rb6rZOWf/nYjxgPkyaHmutQYi1XGMgZNohTyG+Wswy5lsp3wFdn1fgMfNhzHcrUL8oftFrSdSAw6tgFHIZ94F8vRIWlIxhVh1OnB+T5psKy7Pit7xCNyMPaRmGhVcyJAF1YkquDRlfrtNEoxnFnJwJz5WxnFzUUvMFPZVNNIH2zk2AhJiyB7LhRxHxXt25Sbs/AwtK58Sk7cXv3Ibe629IRNDg3Lf0nYi8I3My0p7euQ39N75zk9YIzlgruXPT9mdONvsp7tzIaPprmoFDVOzAA8tYzeHODUQPMJqMk9exj8pGMBNryubHpPm+znJ3bnma6rU4Bgq8QctJ3nSL2zIG4V5pbmDXZ2A1cA72QmMjPT/KyicSp5EL79OzTEHByfisBbHlNhoxdAkLMPA6eA3o+EzcBsyF0U6L4RflNXyybtLgN28YUxyjYg9JHRkP0uC2nChrSsKVMLuvgzc1Q5mJTzVzZ8qzba3F4y7IuLoE4eYe91AlXeJ442Tshs3tpfe9aiQzsK/J/Jl0Y/GLYtVAyDcnDibfZubipkkFJzdj2Stupke0MEfT82nD1aTe4pzNjMIgJIwBDx4RxmK3ZFj3ALmT5woVWZQe1dUDIW/7NFq39Wp3K6PaEKlUFW3IZ9aBvdrIrIY0w/q3muO92Nqap3RNI0IhYIiju/d5zVkXRGweCBrIHKzqxQzMpaKuSfNszYU5SisuWbCAXLSfkg7u+2xct5eyq38X6St54fRjUpKHhG+GB5ZkXKKspXqyia7g8nwT3CCUHFryRtTyXGo0lBmY1mgOTfrR11kXD7eSz4qf2mU8F/e2rQGsC8rslfItM4ilmJaZN5NONBWW3dybe0A1j3Jw0u3d1oOAx7s/LCjp+Vw7vDgX+8Rsuox3AuQEDLePOe2kvZ+V7fYwf92H5K46jsiNC+7yQ1Sizfekehg5SMYumnoQW/ZVbLhS14xMLELcH86dyrBXM/Ce4TQYcSKIYXHGZJSIgYV0/ALWUjGlq9aobhBgT9kqWF6GGg1lRp5WzqFJP/o66+Lhr/QSs2Nkpups3My2uj+KqPlglmJqcD5NOsNUXFZt/UICYAzOSCOcVGX9YhnMxOPdH9aU9HwuZVWci/WfkS7RE14+FzlGUPbrR2t7V1WC2BCAt2FIZXN7ZaU3GckM3Goyf3thwYcGYqYIaGsaiz3w/GPTkNuC8Upee/WQ7Bq3fDWRWDDwad8OwupVXxbr/mwvcMdnWK/wPKx/c+n73Z6Ze1bR8QUDrWI6W1sc3ACUk13C1ethQXAIM3IiOEcm7Q8IlnUvIN3YGXuscNABnQ14zD7Jy720ZllP5tKdZTQ25andCjxXvkcplgXGWjQMDjEXA2nDZJIm9kpnVo9hBv5Uz9Ne6MnsENRKHwA7IU8G0LUDcNXcCp2CxuvX6i5RVRfZJfpnjUzSccLg8KmHgrQzEIBN7JWYU49hFqOAap72QsyNnTZU72QVgkckhNTzrA5gedPscVRU1OPayitoRR34MMaC2x3I5E3J3STkb1+H2x4MejbLaUs7FSa9GGstaAngRqLdNqQ1JmfC/dw/tMNYgl/3chcRRqHzHpNVmJxT99GlTDeIJdh0Dx3M2HT+Og4FobWPPTi/8LCXjKkcwQxcqZyjPWLJqx2Kk7sutPGoqorktq6QKYuqaytYFq7owMKaHuwhS5uNaD4WN5vjfWB5eCTnO/LnjYQjpbu7PS5QwVCgsVI2HLoDXaUZLcvll4rHcOfQWNx5w+gCBcSw0o1EyeBusl1N3sW2HGW3jNcavPSXX3H+Y198xzLhG/f11+JZehX22/J1dC+3owKwinXjsEIYzHIVDBeHS+fS4fwWFUmUVcO0HOfb2yRrAG/wikl+oOLlOtkq8oPa4VEQToHCgabGHYJCzOmBT2aFdezofHLBdk5NekbXW3rxq8ZneKAzQLEKjt/j853FsNaxNPbwpGcwqr/VuARzxtcs8VoVNJ5VLA2mQ8D6YAa+5GYAdXQdHA/NqUnP6Hpr433XHcBB7Adg6J9JwK9Hqu+xKOfOWeUVGk4ZemOdBQ4FmwPVHTjdqBNyrl+xUc5hgPMtBZu5t1gWqzG/8fYMBc/aMagCk/la8V8nqm4YrBb5kl3rujEY8GKrx4AnXNbQiGXp1STbOI23Gi2CZdSpn2OHMR7d8prWT7G38IP7FqX1MEKBWy1ZU4nMYpU0eAIsFXV/5MtGt2pXu3qMBrzcSjLiD4tVxaNYenFZGaJMrU0udzarshstbBzabwvQ+/wpS/NoY/CsrAgKZpXtoKwyyYqYl3n2U96RGfhJTl+Txpd/4XMwGEXbXYoGPtDJEhY8sKDikEP3nRJ+DS+m4L7MKKhgSpt0gK25GIddJ6jAo46R2RtLMDjEYQOkDY9J0Fs+StPw2dLqnnooM7CoeqpMOrCKR5PGYehuUATICdhyH+9FpL2flQn39+5j1B0M7/CECoodWKqBGWzFYjv7xJa6Qcy3i0vnax+YlFarFU6HLBjEkE6cyKGVHTGCuhq2GIsofjQ4YUHA0tE3cNbD57HbnLOWX4aqIcywBFXzsx9ntM9RVt9FcVUXqDBhSBAcZEkK0oop4RaWOf6rOzMHgynpvR8sNjy2dLTDU4Q21zk9KrnQ19RTvWDk8mqRrBmI88x524tO3e5sQ6K+ynTUGVowV9cC0OSyTtEVMiFFDxmcAgNiYOBd2Uljvw03/OOoQvckb5ReMougYGabDsoqs42IeRlpLO/IDJJYTt/9kMJD/7UrSYAMzkgLrKSL6IXkIv2Mqod8o6cBDA6ehGlIq2Mw3MR8JCFoW5x96/LEbgIomNRtgLLK6CbitqXB8ucQ+SBmEE3y2TFpvKuyaOZokqLFdGlK4GXZo0dQ2/zRUCMzbv202VsvsyFoVWo76ObBJL0d28CM8hsV26Qs8QfSg89RMzs31NfmYXW5PDeqDsp3qKaVnDdrGtKm+dGtTuzZjW0Om6jVPBtZSLm6yylwDyh+zGveuUn4LJcRhghAsQHWtRIgpq1D16XCGFe3EmyHN8eJxXK+TbokVF4wNXFcF5gU950efXOWJQSv7i0YRS04UTFbwS5Xsaox88dQghyejTozh7JqMgNG/aDqrYYL+zzfAtuY8ogMgQlvOr23Zdg8pOlplsbyQtl2dAuwv26+jU5wXN3FVgOZ1h947j/l9zfUb8Iz0gWgqAPxPAViw+eqVqAE3VznV8fZBuOZgZkN5s6kF1zVVbCvNmk1BDwRw+5nlmrVCGbmzb3MTW3EhTrus+Q6V25bhc/qQoy2twx2dHeXpPgLutG9nSFAgoKuB7IScwJm8+cyAh2MpF2YQ0jJCGu0bS78BtpRnHbmNCPnexgc3jJT2OCp3jBB9JbO98tLMfU4Ztk4VfNkI9kW9by/wscq4hKbbKPi5eQ5foiye3SJl9oxPiuhLH6RXzDpaoJXTaSS1Q2TthWQdbu+TyMKjfs0Axsaz4KREV+OZh0M2vxhx5lMlfAsyaJfmBfBzszNhCDBLbiPqb8c28UPaFOn6DoqH3vjE/1NznyaiiALinWsGFLXJJQZmx7L6nZ30xHNwduG82nSFbreYpz9txrVaHOyjZL0qKqi+KGxo+o9JzX1IL4Gq9hwtq7NZRwsDXs1A3MazolJT5Z3vVQMhuwJ0b0Tb3ZV52bPvlkZh/YjWqOFyHxYy7I4N7XGXN7VWwOj37QDi1t3ewM+YypomNqRldkmAAZm+rw63UE3knl5Fpwvky7Q9dbAqdTio1nMTjTShJlPINOtgkmXpCtmVdysHtFiohiYU5O+UNUWY+/TvKi3V/VtGRdJm3RTe5MkrwIxtABtw8yKpqxuOIPovPrOzMCAeuLb2F0XTDmR/Igq9BmVJVFfTot8q+U7RR048QQNbpd2Qt7Q/Gxn0JsZ+M6A+Ca9oOuthfmuc1vWG2tMynhUM4uzndiX+ZlOJLtJH8ZaizHc2XaXFxXu2l1jkTCxocqrQCzHQNtwnKIZa4tpELbTd2gGvtMT38LUmWT3ixo7T56tmU9eBWI+BtqG+RTNLMN8+g7NwHx64u8d8+GG0rx1Vu7ZRM0TYgU5442w9rwHtAMZe1QMvvz5WDeU2XhWPmsmXeiqLMaq76L48Sy7qvL40e48rKsIsa6kjg0Ha5vdKx9g09HMwMym87kXB23ZYHQpHjX1Zubpfcz7aDiWBRl6L7NAZvVZhbad3yH5U5ESUYCFMyLSYHZZEcUGwMvOrF5rWL5iCDMwpmKGTFpvqs/DiSdNkgpcp8I1UNEr2duoqM5v/4HiihShZzzxcSPxoyzLqwbLn7+W6DgtCD+Uf31dFbWo+xLUV6ga7BW7JC5fv2q/0ymdSQHAjlz16LnLnpQgEMtQ/qLFhX9V+AQBoemKtCg+5XGUJv9Cm2764E7xUPqufYqy+zq6h7F1ZWadQ1cViYi8RGXDedLucXBa5Hw6CAixmLfDHOmYtUKDmU5GokFP+2NDWFmHeA0yOr8LPO1cBhhd51RsbczTfZI+CMmY9E+DhE1LKSJi0z7qkPVuRyCm0ZfLCE2ebiRdGovNUHUuIlJcg/uOjmW55NMgtwpJws2QKnDp2YnMtIybmjIDAYllPMIN/sAbHoiIAdDzJkqTH6h4uU62IDa63JRE7+psg0/VeJdVEGsEskc7JPjAE1fBvKerY9qoUgKwMPq563lOs5QBMGMxqhKhWiRd0BY41iHOzbAjjXYCyzwORIvyXZrfX+QliKsv0yJpbrFlWIZCMzRSfWksNUEUyTCY9eJDkdc7aS+aUi2iL+iplC3UvkzP1nmaHmXlk4Sbh1IjRDIUVoqEQksBwLSoT56xxMii9KiuHogqHzdqtlzHUsFrG2tyxkBYuwQ8hsrGV3J/2brOw8ILANRLsA/JHRZ3xUaLHQA0x65CaEY/GU8PhWZovuQVOLqhUK+Roifc6YscH/pKFb1EOANV4CHZbSUyti8zRiKjGF2uRfY9SlNUqcZJQ+hHeInw7ocXzj9rBAtuBsBgrHmz4DEfKcbLwphu6lc7FCd33VIflA3F9g5X0I8BrNY9MA2NRwHv2Jh5M6bEa95XkVKqeeLEDBH/Bupxvr1NskhGHH0tbcP0u5hQE+zjom6jsOi6aRMmuN2Rju+dm6Cnn7B3a8i4EdcZaLITWkxDl4FSt1d3TxSC2/Pw0qPO+ta/Awaa3sY32UwO4JJ1OD40ZISEPDkjR9M+BKSbBjr/OEh3NrO5gV1IYxQytAixuZdlGyad11mntvJJK0GlVcwxaotWysGy7KnarRnKFSdTbiS5AC3bMMGtP+SOuZfAcy6dEsscVWNsV6Nrk7Vp7cNgr4x60+drAfsxJsDRDiqVn2yo7CcaNF8LBZqhUH8kQBnC8ku5OngYvch8QFvUiOxb2DjIABiY5PJCYoprkhxoD65N1L3ELDgkMjDphOLcS5frkQnh13DvwBB6C9w6nAbaFxAcA6thYJCTOXoNUr0NgYosA20GTHif9l5iu4uSe1Dx6csMNuvGx/kabXepRGpwIEbmpE+oqlBB3/rLjEsipJnhS4cbiP/Ran201zes57Ee9lraRmVdoO8ouX8Ap5sBMEX3PsFcW0pGzcNokTIuxxBGzsVbt65fslglf8ZiA3sb69sH29h4T0wjpIrhcj6XOqM07OEC2qhlXkoWV37KVQrAmV5tvqgQ8zDGx3kFTg7EQAckYBvF7RsLoR94kZf4rJmmCpQ8jMEC7R1u4KU5uuMYIBr9U2TIaOchDiHlTaL2OrgZfRaoOgr3g7EC7/xC5+hU1BtcoYZBS/0hBP8a0yZ6Zyi6idFvg/doYollSkjaa0RPRRhaMz6wkpR+vLOLjnow9olJxyTskzmViDQ0qSYfrkFtiKqcU4qCniYNAISVjd+byuKTmPQNtkheJbx82KpqEEEpFxgFMZVYASrSQ/OmHOWtcnMRFY1KIdILgJKPRwSGaMN60SjIA6ADiKJC50wWEljRe/LIiELB6MYwgsoJkuiWnoBqTmKw/kgqkshTKUlGAyZQ8iEPmBqJRzj6XnlTiXetotKcAZRSQMsHJ68EUQzwB1MQToFbymEQUgfKMb5bN4NLmEg0GFA+JhAeIhXvXqagE4wTEtOj21so3uqbO787L5L7JFMwlgCqnXm+hoKljDhJwDcTjVjfPTmBGDj9aGhwb9IwyAC6cP6JoWgj9Qu8of0SpSQzqq4dvAkWBYFV/pB6yhs1DmmugI9n+FnpOjEcvw1mgqtiQQC2phHFLUnMtSAn6zTEtCKjCwGnJN2sRBteAR99eqU0E2G1wxKqKCgmP9yYYZ76vMQ3rGcyAdR8VHoGcyTXrNxFG2pvWPduuV4sraPXYGVVVToy65tuoCZLG4GUZRX2QHTV8yEIbjdQPT/6EXIetuzfdVEcNHgQ+TA4SIg0VHyBgh48oom15eFxGzZuRk4KFlA/Dvac5E0WFh1AHPURzsNye0N1VG6opaD0ZtMRWGWGNTPBUrgAqkiJ7EOQd0lK8t0MsTEKqnCgBsNhawSgD4dwJiJ1UT5jUhMFlQRY/aj4Kio6jTFJBuQSEENySDYBDvTqI5mUN0oikHwgAixEGiq2SkETEdXEl0VDg0rTIQBlMAalqdCSHvOZBocmLwr0I0FPZoQRgA3GxNcJQCYB5RzUOs63TWKoMUQQJpQApx4QD+4tcECkAIGkNHdXnC+bafmI0t1dnWZkU2AKFDq0rqZW3dUg8KaqYTMKoS5rxoHcQ3ypchcEoOTjE4EhktFRrwpiAcgm3u3GSFk9STgwzTBYaClR+hheHVU4dHOQ5YYO4JXQhIbRjIAClVAj0pOBRgLQgAlIDskcXTy0kjNaGKN5bMcShidaXDAxBIp6CQ06rFwlNig4k7VOjSWE6KDQyTgEJrAPbb7lFbqhwvcV5OFADYbE1vDekmR4ZdQKtdn3gfzKk4UIJB+IAAuRhkotoKCJiGrik8XQoFKBBqAMxqBUmS3pMZ+uPDRpcrKQAxuMyeRkYUmm2U8WpOVeWx8zZMCEEuDUA+LBvQUOiBQgkJTmLieLPE2VgoYFUCjwNBxoaG+zhajOAAyKiQXLmOfkps1XAg+dBlH3nIKUDX/IuqIhAo0KulKAKOlIAry3NY7ZTXpFOvcLTA4ZuHo8klr+h0YFbgndJFPg6SDdeQQbGU1BeL19E6qm84WWOSqbNzDx+UnVdptB0I6OTR23oTYe4VPTs2lEQVPQgd3nxoNDrLjwYCENrieYCgGuO1h8MxBJlfZJuZLNKsqHbFQfoqgmsZWCymZNTrzamzRMBpdwIJx8bBA4RL0+U5eCTCCqiS/e2jZV/MZB6Lqv4iBjGszFE1Bas5sxy6pIDHUF+bCU9VQxMHzuNIOAGLgNyF1WFmcTipJdEjpDMrbQluNr+WVCArYNANSDmdl1CTanLSm9eBDN+hkhpatQf2rmMc1DAoVDlghk0nmF85U1IeZxtOqTF95c4B4/RCXafE+qByoZoUgaXRX54DQ1IbJRiRcVVNMhlrFTKNMClFPyZswLKachXEE/ULCein4WkkndBkBK6RyFouRXekKNycnWshwvU3lKwrINTS73SGpQjeinQDRyaoSUSrwuT6lO4lGYpiQBkMtUqY4q4eVDUlWDKCVJxaogmrKFifVYqG3tCtVXshus47KxxOs6Vy4Bll02W/ni5CDk42IBwVjKMT+vKoySxTPlwqRz+d6MGYLlRGAB9WNg4FUk0atoMErI3VlGZeejc5/BUWtR4AF1Z2AO3us8zeOa2KjA5G1WSnMJpHw8cAWIOEJ6aQWVJFgnltpsq1IhA4GZDkUqbtypM4dKQOXp1igDIKRqf4EqwJsKm01cuWmBSGe6vTCRQFJYA8u4gRyytLXPLI34JO2aqG0VuGoHktaCtzYhubxye5MjnyvqWuiDal3KgS0GqVqdnvSba40KDSvMW1JYi3EpjF2eFJs19LVzt9YHZ3KAirt6CD6s2/i8JIKfUDAgmaaidrDq+ooYV+nzEHoCa9qcluCqNzFuZC9ZAMvbAY1ifdpjA4WC+n0QlYBw6AB0HjNjiuDrRP3Qie3iUWJz5W41hWdcZsqOGM+phKk8MhZ1/7bPUEinjAHTkoCG9k5XROOSy6i2/6GoonidxjTFkzEKLQVMMSkorX6jRz8Jxl2YL52Uqkv6/dukthdV9Dv5lHMyqxKl6gj7FJPTdDAovKhCY1poYpguALPDPW011RQ5rBD3ZeGwFpzoPC/XA09p3fBFCvIaVdeSwASLguzw42F6yhs1K5+FKTRUXqOS99R8UlRIbGmkwGUxQQ6To2rYYIoUnBFeHJksHH1dZ8FhsmSCSqqlFgv4dN6NpNBiLtR4rMmkRGcxR/07bPYTpe6BfNK0XOJxYDNUqSz1JlPlyOzMNrea07/cqI4wBaDkQxGBQf/Y8TlJlUusiGvqmMlB54q2uxSNL1vK+YWD1E8yW8GbZzh00ElSRmwH+gyvdGpjlSSQ8gHBFSD60O+IKigkQThDeM3YsuLAIAKZDEVxKLAmy6yK/7iG9YcoEVa7DoQqE2TXn4detLCDjYschHwELGCQFxpk9kTqUd4wFGhe31VvTCCcZhAcuJQm/dPAOqLw+KbeoNgktkrqwJCK8YAVQAoZZ4yV4JyaSuLzPFyX5VFBsioKNUVdM+QjQyziGdIWi10YJZ0JDQdom1H2lUJSbsA5rfAeGiYPdbenfwWZBiCDkfSwAYgyoAJo0ZU1X8I4Mg8JzJXSCoBSOHMJwKA3hVmedQDZ1OJpaFLFJSKQwRhUXGJLj3nZpHsUvX2LXkkYCaRCkQMrgAoiDanRDmGks9CKYGwb6BsG/VNFKPlwRGDQL7WH0sajiOhmoUzv3WnARjJQlV8aWEMWPTCC6uMHILTzSGf6aKwURyCgQoxA8AFfsJxZOkGvYNKvaHZP3ALSyqymQtAYIQClmdHTnS6Nqd8JhZNyuDDnA4of85o3LAufQX41ravgN0MUIFeDdTX8bdoeZMLigcLEQsR1QV4v7ja7m7MML6goVUT1qSootgtVPThAgq2gi5FQoZ82moRtuY9j4b+bEFNa13zgMhQmJDYNRDVsERLbukl1eTMlrpIfuIFP+f0N9Zu8Ug6+nKIAlw9aUQt8RWUEURNThReKW2FBQpNPFXMBwpkNTBVp4USpueIrdDQxpIWOBtqxzznmo7u7JMUf0Y0i4EsEUkwcDwuyQQ+kYQIB18TBXUdxyuURk92GSSBVDA1VgNdIapIGTYJwhtuwK7wBkMuTZBsVLyfP8UOU3aNLPE3HWNqjLJY8G6yrpDhQ6erCzwnjSpqDmhYvSM2mLNDxlu5B84cxDVlow0EylUJQjUU4ObniB7TBZ8HrqHzsFRH6G0g0XR3FSDVVQQIKdXQalrYVKCqCKg9B2L/VqEabk22UpPjAEcUPjUqnNPDqqsgHrKkJURWsoiarrpWpbcKK9q/aB+8tqdrXchpyV3l62vYNycjblQem8E3bl1iaalEKazRIpoqGhsaUY5EC9KLLp+NI6qs5Q9KVbNmEJsGU7Ei3A/reyafM9VmQq/q2jIukdTjWPi4EQ8uHKa8kfUSFhlbTUIF84iMI1iR+YJ3rM1biieA4LfKtinQqcJXvj7QW7FhEg+vciuSo5yXddW5BOArYeGxjncBEoxBPTLKz7S4vqosiv2tUDY3yqICWj01eCaIaA60mmgLxDDriybMN5RTQ8gHKK8EpxI0pp0A8G+Xeo12at7bSriNyugGwusGJVeQ0G2FNyAZghrQV5Wy4PKQZxY9nGdYX40fjfVVbRz5WXVXwLVK4jpqo2oamfu1X0r7CjVdXxX6wCqfeoESdy8U3q0lim84gR/6E/VtFMPmQAGjYu5UG03m4iijBg1pW21/6/uVti4Iku4mSDBVD2V/eEom6jboPf3mLQWK0q+oo/ZxvUFr2Bf2V9liz+/LqahfF5Eb1P69ev3repln519cPVbX789u3ZYO6fLNN4iIv87vqTZxv30ab/O1vv/zyP97++uvbbYvjbczYrf/C9XZoqT2gcqW4adzT06Qoq/dRFd1GJZ6F481WALvaRkV1fvsPFFdN1p9nbnX8ZaBz32AXYtC+TyjOHYEmtz89OPndWcNIU40B7g3p0xvw+cWRhqd4WESINyNE1HRL6uGaV3GURgVeKDtUVC+9BrXBI8/TepuNf/M8J6999VJiBiS/WSz0d3NsZ2Vbr3uhkukWW2SO82O1Tfkx9t/MsbzLNy/HZXmcRuROhcbFlliMNYvTeoOwlEvwsKIdN1yh1IaKF7gvT3i944IKcy7ipxgCMMffV2aRjl/NMV0nVcoxTvfJggMfsJ4CIKK/28xKVeT8VDSf7HhF5BEbDJ9RFf0XenlqRTaNiS2xw/geDRupiJQptMMLEJ/6bI7rU7LFzL65zvtbCxqjUGiO9xJlG1Qcld+TTbM10mj5MnOsbY2/5xk3dPq7LbbvRbTrkstBSJliW9x4PTwBMyUU2uJ9lxNnHF7E8GUW0qVI8gLvZZx0Gb5aSpfr6B4QMM1XCxlTNxrAdX4Up5yUYUqsZHR9myblAyCbxwIR31/ects/r2G8FVQMTuHjFRYzdSZ6ptzTvbSaAZNwy2mi26hqT6PhiLqNrVbzPil3afTSeeXRmNiS1cw2/lA1rqg+E90hcZhkac21TnDjB8qi6D5ZiBhCBH4gw8fVsManHHc++RfadN33FQc8PhehYIBjGs5p+8DjGL9aqD7d88E8Lvq7BTZCEITVxA9FXnMHC77MAasEoT0uYN0wBevh+uF1Zy9elzxVbcLi0qprlYl9j4/rlES7wmw9FJrj/Zol/6zRFcqJAYfFyhWZ4zxNo/uzLe4PcRYRhw4UWxw+Kk5jbD4sfyiSqJ9K5fOnUXBaKXNVFc373GVjhQ2wj3EYXbcyLZpp1nzYPajvvbic2BJ7jMCuwRXZHMPI/f5FWt8nGX8Oo0tsMF7ndQwc64bPq1kFYkiYzwrQhcwZcL8exZqM2MvPGh01GGjqRpR+86fCM80kHqVp/sQi6D5ZbIocA/I9gsrNsdOO+zxmvmw17MZGG/hwmSqywoC51NXXqgafFgj1AeScVsmUWFg1o+eTZ7TdcRZi6rMVrk5DawPuOIRMmcVKJMFeHLb+m/1dYBuyA10FtiU/qZjnY+R9Vh6Ny8XAoqm/1rUnve9yvOs6KyHOHr9acGKc88ut+2S1PupbwMBDf7cxyNwn+Ps5/hbh2eeNMlyhxZ5XoAifUM+zrxV3oc+WWBgAdhsJRrbE5uBTEke3mIQdQnih8tXIiUDXMh53Mvt3IXNapylg5hm+Lidi3uUVVnKkWIFiC2v1c4WKLEo/JdkjZ7FmSix6G23u0TXxiWJ7OX62xHVVvfCbPf3d5jCQRCV/GGg+WYiujq+vsVaV4t+Cyg6UH1w81LguogKLkJ5ywvFKKLXpZRfsxyNlCmx6itku+Rfie9h/tTyYDh6/1/kVSlFcwfh1sPb9H55YgIYhfX9BgZekJr4kkdU8TrrAyn3r6Tz7iMd70cRQci5cTJmF019UDkkXuxyTnAcgBLCkQ8he2PkxNwrOhMPHOe4L9lnNfI/uojqtviXo6bNwdyUUrkbBHJOX+uiXPRYH/VJeda36Zd9jQg6+L3yZBUeW6AIVMSaRICi5IvueyhBD5fbYj7ZtHDeEuS+z8ZTBrIWZCgkrky2x8ePYgPjo7zb3VP+sEyylj/ERPM/Ea2qo3ELSSbC6YetnodlgmiAQGb+yEJb7WlOveylR2NfowtVIPV1ycXPJR2NykH7q6quVgHt0LqYpLDvryWAO5z0drsOpbB9PZVOdRsKfmw7nm7Wcb0KduZfa8cdsUV7bvSxjlsleL687zUZ/mqeYEXkc49c5VYajtOKlrt1ddoBYQmIIz0phM6U+W2x9WKMVb9XHr5abKIBq/GwhgIXNzXoLS55RCuzB42c7VQMLm0jUNNqv5pi+J5vqgUXTfbIQYCi5f+CYsP/2c4nqs/K6iDBHC3GGTMH8G+fHZLNB3OLrv1mE42AlTVjDw0fLddflDwD1V6psZdtYI8HtNjI89fj3+d3/Ae9oDcb/03lTa6rPta2190jw7dLc29pVWnOOX+0Xm3vI7D0iUfr9m3fsJSRXuITwbbSmY9HGR38Pv0BcFLOj9Lbe7gcTX6JSiLXqv1lI+jaPxEXE745MgTk+4EyjOswc59kmIafZV41rfkfIdhYEM2OBz5WZzMFoCW6RTfjC7AITtWtuXqr6bFJD6ljveZKk0jWdKGn1aRb2e0HmvpfI26UiDIRneH1miMfmElegRTGRHtG2KygS4+flLKF7F3N3IX9q1JqZ3HloZtaBboC5Ilu1tnHtbWLdQd6UwFicnpIyucXciPeYH8mmjtKUU0JAAEv8SSpkeKG/21yHdnshfxs6fJ5V2X9o8mtK7mTEUjuXVSliodDC6rbB2sFxvt0KpgC2xJ7zJTdpQPHizmJdHblIhSEOl38a3qKvzS7RjwQ9CT65EIRFC7tdkf9Am8uIJIy54g8VQLGFNMgrDXoYwr7313kVpTB9QAinMcgbkQL9jO6QV48110Hywc1ZAO+11Zd6e8tLCxmMeSsfKj4cvf1iY+H9kNxVxxGfpZH+btGfrg6kzvBl1o5S59UDKgbtEHKV4iCsWxgVIRg/U26xfmsst/DCj4kKdLTZcNj4tayFtpnd9/lTluaRMLvjd4ujRFdHcP2ivlvcVmRpuzz76hwdoHKb9ffcLqfzOwl+GMKeGifPu6RofNPeRy8lTBkexr6VJnaw9YCDnEWlUFbX8lcRVrYQzDJAsYU0ZWoK2cuEUqtefy1RcXRfICTqpmKplRvtWFEMXgKKbdbl8EYqvzCpAhv51VU6folT9All97wZF4ZwbeECFUkuzKMMxqGVRsFo0AiSGIKwoTyJpT/JInwyFYQiU2SDUx69z5fZnLjak2mU9rWPH0jOdv70JYOyuuWFcwUwBTb4TkqBts0nCy+NMYEnz2ZckZVORm5hsx94xeLK+KzyIDKxFMjG8TyPH/9WR20YPOd5zhRZm/Ga+kc/oiSNINuIHMqtJXgQMISNx0umwC6WWpwG8qd27N0zwkKKKqDc6pSU3L009o7TvOj79w4J+WSUgBbOzFH82CRGJpE+/FCEQsvztvy5DeHgbf4yh7zNxhSCpzbZ1lt43mEI2xaiZ10LPIR5C32dqwpxuRTYEluMzTsSRZ6KrltQuZWXDOp71qEQXGZEAEs+QpsOQ8Jv1UCxlRQi+/C7+uVdXVVCdIBQao35e1I+pElZKdDzIBaUaWVvivDyv8DnUtFSCEPYOHukaVM1ifkAH6bE0jUccAq3WoPpBkAzfrW2Dh/nZcX3iSqw2JN3KE6iFOgdW+KGcQgoI+FJYriZEtKtxS7kTNseD2fBYZ259SSrUFFCjAYBWGkBRBAzWBDEPkpAK4uAYXsqQKuT6XWC2nXISUauyEq/QWV1VFVFcltX6Djf3hKfDbwZA+PQAs/vsi/D/x3wqP1u7VELnXvtT7rr8RAe9gi1eFGAObSlEixSoDncEaQ7xQoDe/rMc70zAW9nYAstdoCgr2jtgwc6Jn7yAxUvhNUEqydXZq/Jf80SwTOCL7PdMcvrqEju7qALHxDAxocBi9fi5fzuvEjuEyECGyi2OWuWqFMYANOYWOqA+TOKyrpAhK4S7AyEQwtQ/gCh0AEv+aHETQNY4K+zTYrad4bE5Fh8oS3eC1SQNx9hs6QExLENQgN1EwOE8yjy1riJdzjlSGgwG90wyS6S5iZWtAcyRVY6Ez7YkSvyrrqgMfHFq3HBayWJd6rAVh65ZAqU1ZzGB0/cT2130sbUUb5L0lRYBlyRLU54+fJlFnb1p/wTqvCJ5qwEXsYQSy0wPxQIqXAD5RZzVOOjWBKDmPkyB3+UbxEnVtiShbTR4LryVN4yR5sNrlGe5sWWJyRXtBoZhw9LFd688x9J5vumB4PK5R0zdf1phF4nYyVK5OwC9PaWbIaR6KPIluzbQlwqFxt1MvLMx0ZhcsnJpqy+1u0c182Lj+gZeEGGK7LmofbhHpCJ+qIlN4izskswKPiL9Z9Xw+Od+aTV/4nyHyRiZETnHjuiwjENy18EDEDqDlQwRqHQ3uYCW1ss2T6WJwp0wXQxSVpDf0E0YZxIkmJFfLgc4FRBsdTu1vn6od7eZlEC3DdTReY4u1AiFtvwcd+Ugn2wx65FqA882LJkYBnPYQ8h8rUop9kBxg7AIrsvsVDJ+4HwCJkCB3yN6iRFOpTaKC0XBWpz7QGvpNNFa+PzQI9ysNhcLG5aDOvXW2Tuom6+omflKRa59Rh3Ibj18sXLqu3tDWD/lB543zgUrm0VhMuhCyD0SCOwQEbdkAuC7r8qT+3PsDAWfvSQOE8i39T3HcHasEp3tpXVXz/DhsjM2I5evE6lv1tga8J1OUzdNxtvoHR3V6f/jcomaIV3C+IKrfF+yeVoh7LwS0bD3dI0cdyacUkVx6GYzWTeNSt7k9VSkO66qxGuS+NnG6HcR2bz0nj8/u95DrWUwe/S/L7PZWHPl8raU13jbOH4RKbA4hyJh3CRl6Lhj/q++CwxO10n6TKyZDw3TSWqSffPtn3JLjoWWnixRmU3Gs6Vlfq++Dye5kW9JVz1La9cbpc09aeZsaFRMR02VWBh2yR9Zw2bzZfFZ+cLeirdZaGy9rSyUFQj2ZLlpCshCmSjo78vPu8XeZpiFsRA7eP1LlJVi2IiaYrbPcrKJ1FHY0tWd4D09LTrsHio0nPr0B/qRKJFtyUWumVJ3qTlb9rGr+aYTrbCjVX3yYIDo7IkSbo47hu+2mNqPafE/H18qT3mq4i/52dLbPy/QmeHC52T4OjuLkkTIM8cU2CBD3jh3f5999DxJkcxcDEuFC4Vb0LeR5ecermiJc+qpCvkIfsMfOpPLLXD3GcekCIXACxmK0rrCnCho7/Pa407TYqyEtmH+mxHPRHV+NWiV8GedceCbRdlL4Ju2H603xXlQUNjmcVZPimqB8JH3FF+/GyO6wPKBKN8/83irBN1Se6Iy2td8rIYKLbh1y36e54JAp7+boEteu7uHKB8W2KpHR83R0Xiv1KJ3EyX2WFtU1IJWzBXZI/zPfqRxMAbRVD5ajTrbi/xvFnvdyR7xVpacxq9+iD7zTCF0PGDSf2ALvhMDIGQYI8vtOij4LF5bOmt2S2EX8GwkF8dMP0GYvrNBtPfkx0x1EWpGEzEFVmcqh7wJgNt4EyBxfrp81Ny62f8PIeWvJD07q8b/KR3j8XxKgeuOo38PislvqlMgY3EzO5r4D0y+vu8+0EX7UrpTfxlh1hu4ewS+gHhtjdStFC5hbRGMdmS3/Evfo2frZ1mrqP78izrugU6z3AQFrxJMoNxXNl+sjiL5BtuH2m/WEj/Jgd8a0kC08MPRRY4u9vxri7wNBkMYWHxH5OmyxuRAtmcV+45orRfrDSINnuMmE+KKbHQtdosMQI++rvNCv93fBthqojdvdMIhttVP5VgQONz3TyXUnCd75JYsD30H5dyXBJvQ63TX1/Ark8XqzAAh09i5BJ2tOQya1gswDpr8LguNEnlCZ06QIeORVcaIQFof6QLLLb4NqkFt7v3Hy2Uml4Ss1rM8NXCICw+M/PN9lEZYgaC/HHo7/bYZDMJldtjb+LhQbxtyUH+LSf/In/B5yrzZhV3zeN/oMwbSixEgmCdtbXMSjVxRy38y7CP8VKq/7yEwCPLHFTrmIKD8DPkmUMM9kQqYCMCAqiADR5XcSipPI1MXJ8E+7m5e26Lh/vbgwutw94p2G8V9lgcXbjnzGKzvjuLJsMKxyztJztvI9HTaGr1+Se5J1m5TfpwJ6FqZ32263/Pu4WlsjsMIReecfEDHsdIFFnlaXYx0iIUfTK3Nto/bkqiycSDK100h0a6IA/6c58j3+2n3uTPeW10QCc1ocCBoWgZHQircE/n2cd8iy6EjH98maUu8KFGZVVe52JQJ1C85Elw7ZqVr+6wcDzfZZ72aMOE9lEIPaL8lFimEU5hb8Togcgw9mU2znd7mT7r5LkiQY7pUV094EaTuPHzbiNu/bhOhdmB/ezQ7QMfhnAj76lytiE0uUt40Q6V22PvNghdIwCYeVvnZGKv80fEnXPo75bYjuIYlaUMJ1NqY6zIfyR4o5SFNULlq1ntzRbvt6xbLcF+/UrqTbNQm8Ygz3emwBKfGG5OfbbRmnLxhaDh41LCqHtcpnOeEjL3CKU2mnL7wowENVBsOS9wHCBXZN9fGK1YaiE6oheyNGDEQqE13vaZealckgHZc9xxXRQoi1/EwBcYwqaFtt6lEHDKltj3WR2LKYeycEPqA085V6Txsy1f17cVSbx3lsUp7hjE3jyEYwsnz7oWBgj7FprkgX0SfOVYYEjPFpVjgyFtW+xEgmJsPIRjC4qx8BCOLeC64tqDIRzlE5bzCdHco/QUIZBkBuAh2gaJaQAeom2QzAbgNrEcTRUhnqP/askfMNe5cBr88AZXZHWPgwn1DpdkwtvibJHtiIlouMTd2QineajcBbsMqw22S3SHu4A20NOifJkN1qeo2FzkSVaV31GBMDfyVlMJiMXMPaD4Ma/Hx8WlV11qSI8Wy/+1TTVNtSD2+obMng6VW9iHA2cgGs4KO8kZYmdtvSYnGbwi2sV3jFkEUorUkBb9x7XEKNDxqx0mUWkev1pignKwOI3wc1Q+oo2amjIYuz4f//jxm9jj9qsdppPnXVI05rnPeUYeveeRCgCu+P8bRQCV+XI3Dn6fFCiu3qNbPsGKFMjmLmao1mXU+pinwL2MDMqnJYiD5FBOLb2LskfxcAgCOOMXFysI4Ib/7FiOmpQ5Ye1fJJZhHsqdsJ/dRrzHKF9ovy80OgmYkw+GsFhpNdZyi+RfzTK9LqKsjJq4dWFjU8D5tyYyqRrSv8VLVAovI+pgbaTjjryboaAnDOHTAjQiOZSVr+2g5SkGpACz8Qwo4oeoRFK7MQhgcxJM4IttpsDeRtmeFWELZV9mj5UcEUkqoi7pk9quaFzJxgc+TEbLffBUp49Rl2gbJZlw3JSAmLfxMSq/oKfOvPAlJ3dzMZDmSwFmIffiGO2q64cE9zjCn5sbzY9Rtjn/IRwC1KCruTTrzRJfS3xe+5iUlf9jawBKlxfXzNBMc8U22Gn5dUp9tzSKgFc4ltvTvqWk+JDcNWe2gMwFoHRhLjM00zBX3zaPhf5uIbVLtPmeVA8gkwmFdniB572pz/8GjBuGVz34czaf7E4FpLgFeIVBDmXP/dBNJV9msTMDFmJ7y/BZ2fegSTIdAa+lAgD2Y8eH4R10PoPKbbStONklRL0R9FiuyAEn4M/Fl1no4k1eZEDdpr7bYgM6yBTYRJCUpeD2PHy04aaR7I3CKXITAPATS9VBYgRw0nKM2FTUndBZizQocbAai+bXJy8CPk35tzpqqMyiGr9aKB5ZUl0USQzfn4ulDpjB23Gx1IaSsv669lXeT9c+gtfyDpfv/Rmo3V7BQUtAXNsAySABsVAZtFezvleyiqtY5yvY9/lTlubRBghI5YpsNqq+qlTtAQBsQlVilJWoxyFEYYnFFn3H8vM7Su4f+DddqO8WjqJ1tkkR2WU4F1Hqu7V8PSYJESEJ2xZYmQ6TH6h4IUlgBMsEV2a11ohPII1AWGkiwLqUii9NnKa3UvHF7ZVFRd0plQp/BeCLEIT3xS3u7jofnkmDWIcu/on1W9qWHcbaJWJ0MXYZYZmGU8P68bdD4GRp980WC+hVxxXZWcsgGwT9ff4T6N6tIHLf1uYD8Vk1PRaHpSKvum5Jfl1E8SMeGHS9y5fZY/1apDDKpsACH3EAhbQ1psDyThfBl898mb2aBaIVCv8NVqO/yYbG5LEq5zTcDG0CHgj9dwczELzWra3o9uaWhXjoe5SmqAqjDdG4XPQgTf2J+GiVEZGh9p0w9y5tDVAj44occV6QaElMcgXuEWRJH5xLFJW8Gar/Nr/+6P5q8WInsKouskv0zyaNjOfRi0LldOhS1l+3sAm9XYUVXuHuGtqldZoX7WxB644qtMHbTDtq7KXieuYKnfHKPfaUgPbzBmdwFEttODW6u/siZl6jv88hqOSUppYvHNQuAbFpg4SwXeetXsIjZ8v20zF0sYNL3vx5jEV6iMMLi83pAKNDse794CIquuOXmNmBLrG9aYEwsiU2B7aRxpBzEVS+mCEz4M37FLeM/agwC6MCbZorZ3jcLMQSe/hB3vV8c7VD8eDXPs69X3ZPFWqXtJ92+KYRiyGXHzyS8x0YzqOBtVj0JEzzNEnx4hNCKvgyKxH6dJ51ZJCl/mSKrW8K9yczrmqyPBUKBWYX5cIK3USKBtgHM/63vLOOfLPvHqVJJOSibz7Nwc3ScTW3M4DHPVNgYwMK+44BRpHz4ejtp5Wv2CnWarBVuj9v96xz1Uy5Y02zx55GMaqu8qIScLIllhiv0RYTsEIfE94CAhRbuNdlG/TcbiXkA/8erVC6GlnQzXnzxncA/RfjcVd2wcprXed79BRlR95vUZHgY+MgU4/z7W2SRf56mh6/O09YIZ1IZ3usOWlJPlhEM2FBxcUvNV8sdJQoq++imGRTKEiUNJz/B4axdKUHfOitXJhRds8n++m/mWP5nmx4JN0ncxwfAfeZj/aOzlGJGiJAF7hCoQNe8kOJmwaw2JPLMrnP0IZSYnmlBIRY2sm6N2yRIBMeK19m47jf+ONzzD1+/XkMkliKxo+wzZArstTvzuvq/K5B0aUPF9U8EWQ1OyDNOn57HY3JYVdTV1+rrkP+f5HWBZ+tkv5uIzkmed30U44L4ZCMvmjJE9hZ+R7dRUKSJOrzalaLRPWaREkMpxnuo1G8qyQ1BELlFm7E6LnCn7Y73ouY+m7DwOQKPymg6Oz+u8Pmd5xnVZGn0E2kDOYnNqbzEx5kzQVYbLOvshBmNP+NL/RWNY2pLPxmtW5TmQzzyfMuL/oFxeHly1a74ruLMwwXdu2PeANIARWytSqxYfbadXHJBBwSjDtm1scA3tTNMAu5b3vTkheXShPi0eYfdVmJLqZCoYVBsDHcyRCLpTZa7UWBrlCKYiCbA1O05H7c3GpDKjJTYGGmTbJH4owGnmSEwp8w3EpyhmzIOck5t/VLCHbYlaCbVMLyfZCIVwjsIFsPsvUgW/9NZGufoMgzO3KHxSUjsrTqNPKxb09MXceWWHhWj2mehJwCfJl9PwWUjvjOyibXfiIIDaZgDn/yueR4y2Q8svGrLSZxp6K/W9hr8Ei+oaIU7FRMgcV+9RBl9yjNORMV9Xk10uY6QUWz+/mJmwGNg7xR1F3/FUTouPvLXFxtfNkS4S8hvFuOozSuU+ppC26YYvFqlkkj8Ly9/HosDotEXnVpg+GiM0ISBIWYE4LHeVbgytPMS+gt+Si9rbf8gbH5tP/bev+nIOa7r6vhZebVWk/7CY3KxWCirj/RhjzT4+undZpK7ySFQnO8zRO249uzJ1l0mwpvvUpgbE7G2wRr8dd5M2clfzLmClfD3Jd12jKYd5bKHpFTckpF5WmYmrQI3KJQn+1wiW+0jl/NMZ3jP6KKt6aNXy0NP4DJ56d2MGEfhvPPVjficsxXp0KwtGYonfVpPBhDehveZ5jU+Lxe3AseFmzRTy++xde8vZheQOfA9wY41sr6a/YpbMQJ0Uo6QwiMWw5lZfmMnk+ekUAGpsCCqtHzcVSh+7wQTiFc0c/jYSmnbahgjZ9FYAW4lYZxhhBds95EC60LlkYIYF4vs8Mt7R6qxZcorgvid9sd0kM9SwBhdXuawBDTREdBrnkxxZ9YbnGk+3d995UnW1huC8Bms8n1lzhFUOwwU2CJ7wIVSS68osQVWehoxOrV1OY0CabA5uINTx+ckp8pWUJzCvX4+lmWVEmUggucL/uJ13kzAbjgU+55/0YhcljdytrTLGyqSfD8IRbb3ziHSTUJZsV1SIi7z8xJMAdj0Maw68WkMIZpGLW9j/kv9PKUF7zBgC2a8zQBXsQobl4WYiFvueYmz2aUY7ipT+gHSgV/Tuq7jaGKxKJBjw+wJXYXgyBCpsBi494dbTYFrscRbfy8lJwm2ckEB8bho82R5g4VBSoEXEzBHHJfwXP3/PG5/2aO5WNV7SBnJfq7lWMq8B75+HU1Iuno7i5Jk8jX2j+gcdnJ5HUnUrbatSkoWePn+U8A0IlEfh5ZTAVK8ckX7+6+yk+HxkntkdadhlnW6vM0jR/pGWZ/PjlM/201bIh3nBBsOKBxYENF3X8vNrxKa875vf2yyD1WlN3XWMMRlE/q+2qY+APKUJHEgW7beWwOLK1HsXbOxkfPD0Ve71hM41crTAKSuV2mbAMOlooreUBb1MSy3qa+sSU0Kpf4EnX9adi3aZQzdLef5jSA/BsxXOeH4MNo7cOM9gwmqTcNY/kzhRgSaXdUvyphD2f6uwU2cmcmmiSozxZmnLyIEe4G/vcoTYlVgzPAgAAWxoC85B+n6z5ZmSVyaRCHULikJ9HHapu+yzfCHkx/t7kgyCq8ePrctl9Q9ZQXj/x9AQxj5T+CF/VLsyKP66JAWSz0XwZj3crJc9xEdF5Gla4xGehqxGfXKV+XjX5sLq5R0qprFaR9j4/zjfBsGV1iYVUl5i7WnNp8sRYDQbO5tnYDLDO3USW+JyGWzudiKF2gDq8LzCdY1/SUnHTU+TZKspNskwi5ALkilzCp5rVMTZgUC2PZypd6+x7FWPSmJYCfKV1bmBeH4T3K8i15soGP2VHBObdGQoqUzbQAq9m2GtHQfQqg/HdgrmcAafW1m0gCG//25MQY452AhNBF5WMYh1URo4siZIRlGo6imxYmkCuzClOps0xQHKjP5rg+R/FDkiGRVZkCSzdCcOdkSywOoUnW6BgASq7Iopd1HCO0gfvJlVms+qLgN5Xuk81RKL8n16MXCJ+sRYd9rtAeL+iVIRSuUqaEkyWeQmR/TlQh0hceF3l28rwj/CEEmPJlFvZcIfraNvLa1hlNYXzAO5WY1aL/arVP784zQAowBRbzj54rLNIFCUV/t9k1PiabDcr4TaP/aqHj1hmWQN0GwWm3bNFq5MjfalSjzQk+aKVHVYW75x/fAKJ0eoPHCM80IoZqnEfEFdmpq3gXIXYRMSRdKLRxtePDMdovFspO4GQ//gL6c7IFElGMXy3H1lEXHB5VtsZlGWwx+i3B+XLmhdp1ToucSzjVfrEJJeIjiOw8V3fpC49i+GihZxxzusWxTe13MbdJNh9sTgS3/0BxxR8Fuo8W/cg33Jy2X5a0Zl5hnr9uDuPcleDw2Q4XdGgaP1tocM2eF8fkNUnB6sKVWfVwQ14fjdKUf3KQKVmNEKSH6icFaUwOYlBdfSKbWyN0xal3uFEQd2OmwO5aWryVtpLuecEf3Zsvdp7tmTCg8auN2lSWYhTR+NX2xHVV8vM1frYaX/fSIJZq+PxBYi9LYbAQyGrW7XG03UXJveezNz0WlxteadW12iN+5l12T9O7dDa//vk03+SvDDKnDLAaDGtl7XUo0Z/zDUrbuFn2BEh9t/H0Lqu2ZoE48nBFVop6q2e0YUB8R4HifRQv4bztp1GSp3IUcVe+pSbswSb2K2S5AYpdcP+mxv2bD+7f1bh/l+NeaEv4gp7KT6iqUIHXzpBB0m9ngHE6bBCmiCbaJ8DWxZdWVHDzHo7sIh5nNEoEeujhe148Yn6RBSUBxatZZ6d5UW/DLTEBncPqMsAxzcJSLyefRRQ23r8hEI9q+GijLu6SWEiu2X+cY2Eu9tRh8gP3q9PzfR83pHE5PWeoRjARrwcSfcTOL+NuvsyGM2U42ZL51XKS1JfrUfPFynMKRfzcdd9ssHSZB969HNXVA3/bDxQ74b5EcbKDXjODIH5iifEZRWVdoDapqK99gkLlZJ1Q1l+rbWKKbFKX5P5eiK1IrKwVe5eXtZv/9wlusvRW2Hhs7gypQHHgyZ+cJ8+2u7wgye7vEt/IcAaVAzdq6q+VFU/zdAPlkaK/2/grp+D7vfR320gCCB9bMr/fZDvZl4iY6TeEfbhJFIsthMNjwqWXaL9Y6IrRI+9f2nyxjQE7z3hDJv3dHBuWZqcJSjfkL87cyhXZc9pxnt0l93UBuNtJQCy45bkqInF2qc8WmmeDoA/qYVVPtsjm4qSs0+osuxPuTsbv1vEauA+KiA2qdDWbwNVLFocJ2xoRuXjJq2pPI/7DBW1d5XURIyEnDfV5qQCwJo7/uRLRMQW2I/0YlQ/QUNvvtga2MyFZ8fjZFtdVVUjCovsSW4zv8jyF8LXfbbTWLIajhuiC1YiFk+f/v70vW45bRxb8FYefZiZutI99uiN6Os6ZCEmWjxVjW2qVbN/bLxUUCyrxikVWc9HSE/3vA4AbQCzEyqVKL7aKABKZQCKRSCQy0Sb8Eezj1EFE/z40ExezQRB+pEStk7LRJNrPYyqcrlQwt5tfNys8dZMtXbYzm23UHhSw9iYLknwX4cdPvDET1bHrZbgPXSWyOnCz7yv6ZVom3+r0xBh9m8+6HhB8dxFzXxHckmuZpkum9tRAvB09gq9MTBSqQGstMh6czbeZ7VtObBoUKOMd69WmgYcXioECCm1WSlCFukZAFkuzdNTw52MEqRNmvOaU69zM1KKwZoXepUyvcHxloGJUoT2GUzzmuVjCWZBpGG2f+Kw1/0hmM0YQ8rs+N1WmE3ZAeeVTqW6Xd3c56G1jzTdNp0COK6CW62RQhPer6F+99UF81pgBuFRxKEh63NuvU2/NZ+luj29iZRqKsJLuje0/ov1JFt4zzlxsqQbkGARJP5Jv+3E26sBpED5cJHDWwwd37lICoAYqgjKkebuTXFUJiDln5ObzVE5Xy3NZgAvoLgiLMgOZo5cVHIgm98VKYOaq1P6IwBN7Su2+HvAtb5N02w039aEZPT4bAvHKRbPjolqWu2GiHjAj98sBCK8sNDsWqk+Q9dTZpggmYRklCJYD8HaZUGlE7wWa0nsjaB8E0D4cND+dZWmer0AcO+GoPjSTjW0QxOFy1WRulUnpItVUB8fIlVLc2NPBzan1szFBIjr4xsmqRMffvt6Xez73zVeNR1rRZguKf6T9cHHkdw28oqJvf60/aayPWUc09/Y8t7rVuklPwn5+EKpkVpIBBS53JR06WIYSQgbAj5TAvTIB3apvOteIGSQJYc/Yd6gSffs4K7/oEg1KUTyBHqHVp3FlA856/BF0hjx6pTClWhbt+poe/e6ta6bwUKTZKdhGCSdlHPlda3aezp/3QbJhEiZRJRrnwfRTGsfpU+9M2H7VgASefkbJhgHVfdbYz8M+51VfdAzDL/21UH/SwwICYN64dZ91nFl2TJTR5puGMTjPz+Kgnwq8+/q6y4p32ZM8T8MIO4EzWy1c3fgObH2y38cRorw28eEIgeJtVd6uv4c2tRvzIWdH37ACSdjBunJZ5WzmSjtxC5rHgmgwW1ws0bwJMqjomqLZDJYmmr+94863Oks0tzVr4tpSlquKrc0mparq1FUUZp+FajnnLUAHc85Bzm6maziTTfRpFCM/jmZ6VGa730Q05TpzTcO0HNMeMJfT3gNtx5jaiJ2lySZC8/nmIv9WxvHvb++COO97XgxR7455yHzoarzTayFiHbKaDhtR4OcoNmgELTcJAtbY8uMaPAXZ5iqNkiKvU+ysv+dg8zMq7uvjguwWYLAxa/dnmijwxWBHljNAw3LAJ8MIz1HiDA2DO4Gzuo/wd53timnjYr/qAbXkoz40lxKnD3uODDRMvzUL1dcg6+YGOthKuYdbXXCb3dVR4CAOYMspaSA6YBoecnac3UEae4dqM3ysh+a6X5O1Atfl+A+1aaZhWs5wB8zBHPcws5veZkTGnlz26E36uWlaL3pNRQYMspqREYPqZ9Z2DBpTSxYhYE3PJq3PgRaHdK1EzCFwHVAa7Qb6rFmiRdLJfjAZI6Dsu7BUbf67yqJpr2tUJTqT34Ce45y3uNlNNTU2o9u3GgOqyoRzKoucX3UnnAFta5QwMwwr4rbgCb8K8FP1r6C4TzdKsy5qwWj5ZD3d+ed3YqvxU0Bd6P18LBfMDs2JUoMfhE2YmDdURV2OEHRjyRI9qA54QoTngpmCMpgqbQz8BiIDEqqmvUHwunBkuTYwDGvgOGdGaK7FUbikIEpQglq6SnvvXn9pf+fNh1WVPRP7I+VdO/T0eRdgovJ9EKLRgTU+RVleIJa5DXJQVXn7pvGKavw7azHzz/gsrkL4NhXgESm6A3lxkz6A5Pe3H355/+HtG5y2GWXRiO/evnnexUn+txDPQ5AkaYFJ//3tfVHs//buXY57zP+0i8IszdO74k9hunsXbNJ3ENav796/fwc2u3f95jVYJSi//O8GSp5vqJyZhEde45GFgpq/fdPv7m8XyQY8//72/735N800v/1fwDBhM+/X4I5goL5NsN/wNw7TIcR+fxuh8cbr9g8A2QG/IaiiF6BaoFqtb5CBEj3Db42U76TgSbfdqpvkMcjC+yD7H7vg+X+S8IqsHATXeO3W41dBvEWvsTXxalxsHOCE0uB0njYuiEzCuNyAi2QVQVKDvQ2dF3kTBwgWFCAswMYGXBdUyAGdtQ+iA0jY/ZAL7u2br8HzF5Bsi/vf3/7lF/2pKLJUCvPDX/5iwjBOyIZ6R1BHBHbDeQgg5cXpCKa7mWY80Mx5+RpAYZud5JW/vQ0k0mPfAY0VuJ9ZsMehDBMRbuqwkBMoNQfmVJ6m6AbTUoq0GZ2JnUcTBiYHXTQ4kR+UC6KVtO28i1XBkP6LcqUheG49BI9AdeApDZTo/fMvv2gDpd21VdlPeYqgpl3gAD3HOj1iulH9v725+M91PUZr9AGOhu2M/gjicljuqimrTXwQYtg0CKqbI5rccNOXFB4To3+B9oh3DHzVRSnWmwVmsNYoylBaJpv/eIOX+9/evB+cGo7CQSRJc47Qn00QQmBR6OX6kYjBClTA7Nc3/5Y2oLFwsIxbgL4o+uAMSWOJo77wa6Y7hvU+uM2/N5moegDPyhjdqstPcPrgvyfRP0uwAukZDjQqA66rin6Kg+3FDqLeRJB0fKK9Lqw0XIfnLwNVeXR9rlrwVYD1a5BXJucjWJQWu17bspW8vxjscs1gD66ADwbSoQHuUHW8yFFywau43EaJgJ/V7Js3aRmK1wQLQ5mVr9qXtPVT7SNgY7H1WU69xMBnI02IKUjj5q3igQz1SfUs2GI76PGnvuhhIViJIPJKTx8ZurUGIsrcRD0AOoLFPLwVDK5UjrqVAdBc49tw703wfP4MdnsrOzIEUuszVcICrjqjso00ucxtLJmV7KyYyxyOTALr3KIorwqJc8EBc/4v2ucAyTWP7JispsRw+E9BZNL+DqsQArUS4BiCrtyWIWFiP4ILqbylTDUDw2FiEdpG8JhyuUf8lmocgzh7HhU6uIKDMvsVEeJA3VMyTgroCNiXIEce5CF6UD4M0kKAvF6xWBrJPsHquoAtJZah30YBp9c11PNnNCdB/CVKHpw7DgSbLajSu7ugH0GrY9+YSwzsg6Vjv1NBrR9HubcMNFGUuUnYMuWoHhO2yFZRzZrBtR3U+o2c/tGIaCra39WogeyLs2eY68no0Nqco/KbdAViEBZ9wAZ2nAbEZZ2TwbHydpVFIbgOki1wztDIVeQy+Zyi1EVbK9X/c5A372Xy+o2U1UnCkY/G7CzYAGdC8aYdmlnIl6QafgR3QRkXKNL4V859jIIMVlYOG4Z+VQ4NTe/NACKwdhvQ9xxcgSyExFnKqfZ9KgNtA8JoF8TICx3+lWN38vd/hSONLoWgdDXu6mRXMZGnblZFkCFXe6B9SuK5Y2wcQboG/ywjKHHP4FkYBUrcWM0aC8WV8tlMEt4o8Nq2Y9QOzk206zYch7eU1LP7V8lk6Bm4iOMlL1fR6+nIEbKvJ5ph3JZ3onGo8AvPIC4V9tdTxSxOFVbHV/Wtu5Ecx7BvV3mW9cVr085Gtuo6gbSdoz96LiG/6guhk5gx2ir1DtvJ/VaNH9rZAkWG7iTnXh8q0NW27lGn79D4FS5u8rpcC42mcQ8LE5fTKs6XMSJN6773kT4ijAah8oQBtjHZqqJnEJv01zbUX8hI/9vgxNIOlMmf0QalpjNWtz6DaHtfWACQ738qfkYNgPVFfpMFcE1B4Wx1be50FyWQ0nUHkFNmchevpxS1iNTNDDr8HG3gXmmjx/2AWjwhY41vaupQDYZHm7a1+g6sqQHhHf4YdKA6s5D2PKA/mrbdLPzHm4u8eozwtzc3cA5G1osYhN7zELJ00VvFJfPK1/Ym/SxIPgIUDgEKlvDB6kzodD9CJwFkIE1Ee4qqfzv3IZXdmNVhKapkrsZ2BNmJUdU6m8ETfUK6OvFNvx/+6vSUVgvBYxBSHwl+hkN7C0c7e3H6wu2qCnJ5VMffqybNqLa23DS00uv82jfn8FqrHqcj4iX7a9RqC8cOqm3OaDuIP6I8grXhkEeP0aYM4vjFSgNG4KK4Hy7EpTGzDcBvQ7aXSyk2r6ILJQPWdg3zZAO35LN0tyMOelYAGwZ3csfl0KmphuHK/v16+8a7uLoGjxF4slLLT/b7LH0Em+sAxeZYlTurtZ0WTuE1wG6g8Ip7xFri5wrkDN3tVg+l8yVCXKfDnbD4Vu5uAedYofpY1ACFP4pIy4Ru3eFF/kd0V5wFmdUFYwPDXgOp/YIui3uQXVG5B0zjumF4nTIz6J6uL6xKNDkFenUTv5xsNr0urdC/yD+mT0mcBnb3vzUMSx+7JK6WbwPOzlISPFfr6/KOgWeCXQPk/HkfZXiVfAxeRBCVbAs1QPx8rfLYsubuz0G+CqAOA1zMKg3JIFJVr73NhSok7HsOspNtBsCub97X9+XsADl74HKRX4OwzDLLp70tkLOXMAaV2LCTdyS8K5BFqeUybSHizR+DtVpYFzl6En2eoCZW/IqiYrh5Xw2FLD6hBXED7eweBdf25kN7kTt50n2Rn+dWQ0gEfbT08wsSdIWTPMIlBoFVMestj1VFGj78vQxqE4aFJK9OUxjeyWMQwbbUid8iMhAXR6PdK0qc0fslfapoRRsNG+1OX/uP7l7wCfxTmjX4nQIiIIcJ2NMgfMDBXtGjCjsM8eEOwbuoxhAeLtpjqZVKgbcvODPRrty5mJgKXvDsCl4DY1WAvT0cHKs/S2PSy8RIJ4k2oMGsBmklm9Hsgk0NMRrw4DTRt+FaRpVPy5fTsihSqxv1GtbPKL+Po7ywB1gLrBjAxQd3H8okZPSyAR4qMKgotDJVUQCc74+X8cZvB/VZ6izNPT6U2UM4QWxMiNKDSqKP9mEOegni4EkNCbt+qOMIcmOJO08KkOXWvFiLaAoq8MxAtRgftU94KLqJQLV2rTY2qCGAvDgpiiy6LQtwlu7QJSzaL70yq+M3uz8p9zjno02fxZyDp5wD3bOK37FpdyXXMqcF7FbguLpQ4b+eUL9kX7cAGr+/dRMLq64xogviv63w+2AWroiENQqev3Lbd5HDnBJoFhLSBLv39qb6+b6OgXMaPYLsBa1+fTsk3drGCtkcY74nkYFjDd3aBo9Ky8hvgiy6uxPdIlFPSLR7QJty9nJ5d5lF26j/JlnFn7oPwIbe0yAHtdppbSVsYX0FQV5mAM2GdPD0H4m0XZi/vNfqBv1Bd2VwHXFaJpsYVIluODZxW+FSgb8C2QUUXi5stBRANAwu4a3u08rsC7UZu0uoKLmK8I2v0KalqClDpkHX7DU0daTUQ9FVK/YY/NkGvadMnmRhw1R+GsWx7e0DBuQkTuxT+gWgwbrIPSQPuLnPAFCH/6sufLixAbiue7Ct3Et+BFbLecIkAl59XU42Gwgh/5Rmu4CR/C4jGsHjYQH3rPQxSo4jtUEtVI31J0sXaD+C7vYW7UXBYDRfg3A4S0jTQZ4njoGHB5noL/o8dJbGafYZPHOTYtgCr+e/SqfiWLd3Jb0v8jqQnIDRbXJvVKaBSqFF2uwxMOnVtM9d6tODKRK95laoOHKRCH0eXcVR8Ezk/aCAMotM6uEtRBRDNm/vWax0NnStfnNf7m6TILK6UK9f0ByMXjwXM6a5vG4ZpOKXYxDfHfGmwrNqayU523G39D9qwGDlxg7WBbL7VGHVvESEavwejigNwcSagsC/UuVI2La0QuAi/wQFZNk9IFC9dptMojd3Xk0OMzf4ar8eD44r7unE64QcboM35FTrg14v2inckBMmOI6o4g3J5hn1bJlnX9tW5aZ3o7RX9RtMK+/PpenKzbQcE/P+UUZtRyWOdhRhkHcRGgfdScpRwqBBg55+gMYdcQx1BfQqyPOntHs66hpudeFhG9KigrUKeEE37fB0HufA0Tujk7u7KI6sAyXYJ470HXi40TxPQsoc5jo3per7awMWQpnsJJuQxk64pkDJY5iOE4yyQayFZJLOEBKFUhomjvJOIHjNQyshSN1RbwCaeCAGcVkMX9XpX4QMx+Uw4IlPUZYXg+bcDx+0w8mhUfQBV5x+UGeOGyh9H079WYGbxT5IONH4tNZTBUQeTVHnXYd+oAsOUhQse/FzGmXFPVqhdsuzBaPv1QhVxQ07Kkb7+o+gDiaCHCzK3G5bRvfa/0gTYnO3wo3OM22Z3QYuY6i0lTt0aVG4kNVVMABX+lUD8SN4jEJuoHCz7C6VU1m99a4NTOR9ADbeuI1nmjk6LAQPoX5bnefwT4riTd5sxfK2dsMLT8F+bry6nAHjHliNIAn2XENYvvzFlFY26Z5nsKzp5lYvHAgHBstQgVgKvHcJ7IMTYP+I9ldpjkKomKWR5Jgi7uGuzde1zFZvE0LJDWub2xqVZf5pnG6v8Bvuwxf6F7kTt4svQbItjYL1dy2tTORqu43ilRV+I0GomzbK5gxStFX0WCDCQrDaH0CI+jllIuQbCYRmmoJtfpHUiNpw8gWKLeJG3043w7uP+luO2oLrJMJoDcs6aj8RxNMVSDSRToZ/nhllX4PnNjuGu4cnY+gE2F5wLErBTbqPQv1tom5m59g09V27UihHtZ3kSuXWfsQ7j4nzaumGuXDmeoXXLmbOY1i8mFqThJW4WRcDY+HLGE03x1SsKsaqpvjeDEl9ZM22wqd+PGp+a/tngydM36otK1ccAX2qftiGbG/G196gj6AIuM4QWvUyz+BaZ5CB3huYso5D5iuIo+EQRnobw9HsCThhjuHGULddk86lft8UfzB4U+z68POtVhvsEl3whLCBSOIp5QcvbY1sQazrsxM2N4mdtjjX2G4YjkEuLkIKGXD1chlVz1DkcgSE2YNGOKF+g2r0sbyxP6SrIvy+2w0o5L3n6P7KxYXe67XVMu4TjulqaB5XBq93Og1Y5aevaRyfJPnTcTy4QtQayCrcyvmR2mwHaVKe/UgLu6xMvpMnp2jPPHyWmlxlcsda1QOmWlq68Vdzoe64y7aLN+Q/SpAX+U2KFpANMFexD+apuxjv59oPfa/TGDRxHw5DJEx+wUYOrTkSVWvLyAqTxvU4f0YzGcQnZXGP5jLEHV2DEMu3w9+bJmdEdy74zVReEA/jHYKthblj6JeI8W7SB+DmbIDBnYQhyHN3QK+QNz+cYPHrZZfhgevd8vCXHibUoW89huc0TgSm2O5+aHL5Qr96s301Z4lM782b9aM5O5MYbunisWiDlQtYV8ELsjY5BFXlnbUSXmK+PiuzDCThi7P3Ow3Aa/JVsjzy6l+NV6XLZ7nt82N3wmxV3hYov/RFEsYQVcXh+LOZJG46O38epzOcObuJgTsWhVSn41Bay4ZxKKw7G5Uy2JHGYtXvjBJjXVbyTwD4HlNxz74HWNyz79Gu4bt5xIL5xDsj2gTqVr1ugTNwGsRB4jO/Lx4sJKCuITUbwhjgsStvXcCjByQCbMzTfyl28xRkm6sU7tj5T5ABuFYsowHeg/AhLbtEsK6vr5gO8v/cOXqDX2s1Anu7riXYTXyz9hizd0Ijtk+jcxlKTIaX5Rmcf1rxMksBnm1QazcTgVBypmkz9Nndngb5A9iIhs4a07PHRzeRARCw8+d9lOGj4Nc06RIDO4T7XyBwQzvJlx8jKNyKj+C2i85jdnvSgqlD731OY1fBkljgDhmBAH4aJA/OzoY9uM6WGAn34sw1yDanoFuwF7eBow2pltBYKXAZaBPZYuGC+BdeaTdZkOQBfjrvKKaWCLwzdhN1cA3yko1zaiiN9iiqtvvBYQE7xBoepFudyD3qVyVqmwPX9uGrIHJ19dscfavzmZsxrUGiwxcKplTHsvJivPMdMXa+3tjkceEa7IIoIY5ZZjlpv4Gn+rT+LUX3DCEVg8zIByMMwb64uY8gpgH8jK8IPwfJ5vJRR8lVz+VXn5+/5/DQ8DmCfHAcSVBa46N+NvW2pdV1CG6q33vdzM7/YYSgE39Ed/iIcWx81dCtP7NdS6vJ/Z6Dzc+ouDfkr15za1So/JY+jHOjcfIxcG+jfhEsYJSRTATH5sa2mQf72zrPZsmLvEEVhy0PtBKXiemGR8m9wwPONSR2HyHNxZVm2UJ059a0whGinWFYgXOH3leQ54TPrWVmim5GsC45WWIbPW+lY3n71xLr1MtoQi3watr0Zi6y9H5PouIqi0Lvl8FtR97vfsegZhRKfN9VN8ewajP3PWR0b77Hztv9o/t7x4/pUxKnwcb6heRF3oByojV9gSye5KCBKVQYlbZmKPV/gmh7b6QzKsVbxRl8PwZF4MppGSe1xTEffa0IEEePIHtBwU+sRrd2wiPhebBzVQZl/KbpWHSVSbSKb8SzMbvndo1zZtqlPJy3Okzal4/I4jW5l3s14lbO0hhCz8fLSCHNvTs9uTxzur2rIc09Bjlx+u09ZMRB92y7I8n8O+EecJMF4UOUbB3e3zYgv2fyHKsfftGPU4F9If3qd/iCF7i6gW5UJUfgxtgbm5V3LNaill6jV05VSwfXi6zx3ruVRpklfgZxDIojUpR4LwqVkkrhZl2S12VHt/azLykZP/wqZhV43w8FqF6u0BNGyB0T3a9aMa9JSNJrEOSdJczA1iBQnvtZDZzmKe9HpnIVmuoaFGWWXIN/ooAtxyo/p9kVJxeiLi5PqsX0Kc0qRnJjtKnZEWAjroLR2gSoqlOg1lPmXhhC2/RNd3ffUlcP6RwKF97YElLExcPz6u3XTVqpdwKb3fjGiRHiT6/uU+zFexZkx3POsZfJV0FWHwytrhGqKxQzdyWyrV1i6I4D7N2UJt9lJvYQ8PdIEi5dkEH2R/fg3nRnF3v04mRgPfOrPRzDxh2+ncdjEIgTrxn+wF/u6Sc6yqYHKTS7xY3eTH6KYjjc/ch32jL36TKpR08SAdMqXYEjBUEynMewNvj0u2LL2cSJPYmjQB5M+1dtNd1VNNXqSsavU74o7L3u3pfGKXN5pJd/0nJlHsOaPA62V90kbLculw8pPwUhKFZpVohfKarDuQE7OJAF+BxZeukheqodC33wEfi4nqWbYHus60+Wy0DxGY9h6sJu7Lvshe7iDdfQfwRZBI9GrZg9S3e3URIcjRb0UOpMuNLTKCgjXMP8GiTlXRCiQAIZeonMd+aw7cX4CK4UpB3j5Qv6z2jjD/hnv+7GQQ7wyJtfiWp1g/6guzIw8J3kebRNwIZQLwfTk+srHGKvZpWn4VRrG0tiYzxCD0/08aBb2+BxkWNvfCt96eDMiHBHCB9cmPewJnlZFpd3GCQm0odKRfLDMWyxg0qVSUJy+O9VXGaB3AHRALLkkY8EtMbx6UuKBLgf2NZHs4v8I7gLiOhE7o8SfYXzGJaArdBt/m8HbV0nKemksrtUw/2+jLF2ZQy8Ac8F/LTbMxfuxm+7/1lGmd2TNuLAlBRZGnNuGF3aH2wyUtsy0Qfnh86jWvzDprhB06lPS6HzV63jGORGSWY9E2sdC+v8eZ9mRb1+TRybjFdsRUC+Aq+6q7GVZdL91XLmj2HWxTxvP1ckLMs7+5G2FWuvxvlcQQpMnieb/4YTSXp0Or+GrV60jdDRRQ4hrUAMQsuYDa6OlPh6297/7kuUPCAnMd5Baj7PqARn3PqK/2gkZ59+Y7HJHUhRoctD8NhKu6LW+ipuj07cjrxYhg/76iLe5VFsGfK/iXB0DLK+oVUegs8lD3zvQkgRoQeMVhsLxtKmiKPvR4SoUfXyaB3B1wQQq71L7lpujZLJYUW0JykgQzS1uTutlqp+9027+g/03S7LdAuG3Vst0cG8Yrc/o3H+AbJ86PZL33nw7D5ItiBOtwbrTVn83kCRUzuxHL78nfgm38Fjs+YF0HVqIhjo1i78O6weznl9vnQWxGEZE3lIPOguWNAetaOn6m6EPDLRF7m8tZ8LFGbpGGbDVjvAA7VGuXrSPCqI/fk/3lzk37FK+rc3N1AQGBkCbsudEavglj0WMXBIMVVcpCPz3sHIWGoyUvw+8PAjI4v95S8m+yX+6R7bX/WwtbmNI7MRH4NooAgWJ9+S3lGoXfXC6qq+X0ahHVAm4S7b73mCmloGVd5BJtzcpHjMfXgqXpcxqD4eCDchgoxu8dqGViozgoL6NZJA6KHUaRmhvKPrBpC2AsLidLlHgztg6f2gD5d61ejq6tzKSEqMHwnH3YV5Pxfh4UtnXfeISRySnPnGbhM4emcQja2d845/wc1kNn9lRkNmnKXvKRY0qH5tyXEB8yK/CZ7PnwFBqQkYCOQMTuc2zfpHFvf+tRM5NLh4hTOBCDgaHwSGcgPTIgvCeTgL+3fFr3fpTle6RqzPsMyQc3N9OD2iqMl90g1OUgwE5wGMdeXFKNlIaKqPgVfOXsIY0K++jaYHgbkCWZSK80apqSbI7oKhWaUnWRVBVigE+x9Nl+DlkNd+mhAVURAbRgOnW88+GTYecfjpS3oUV10EuVouWt09Rh/AskPeC2L3mj2eGplfK+Ph4fNsZeiHSDyl2UaqOJuYPAe18Q8GQLUN+srzfyRyCpL5BTyCWF8wwJZr3JS8hvuE+jRxa7lHD+sUMhMY3zI5zVO9P9lsMghQh591gvP6ENNKjuJwhIbSS73/i0mE9TuQZSDzAdtpfGDI1FtehCfV9YCbcxeEpdnhc1HsaR8kgWDWHb7vOT+5vKcokyd3d1EcBcdhG6+lhIHy1zS00rkcHFF0z0ka6lUMT+dYzzh8PuA6Ezn0+7GULWM4hCqeyy82sQ9ug/vOK7fNhNtWccl4o1sDdXI7FCTbEipAOiOnzIF4fqPwqK6GXTAiJOGPLC33zjkGAjZS8/oTuYaAHOh3XMcdM+uI9hMB9Yce92AH8JPG2/goOBgT7NqdSleqqsAc9vsyyQrsj5NWVcrGw+cgL1vo0DHaaFvOXXjHrtCN26AJwYDBP6VZCCCO8P+TOEZ2CitnsM9pzia6s46cC4//qaM4AK7cZj4Xu/g03RC7sF32qDQp4Epogvx+A8VTmj24nuyrLNoF2Qtei2dlloEkNHiKwYdimQ0Mgzx/DvG7ymsoI6zx4wPzEVqqgf4qd83eBDXjd5ZuBmDre3hgi5iSh89fTWWJlxC41ekfSuUdnDIi8qAj8MYee5y1y00IMZFYnktSOw6FUC5EyXmy4YU4tN4hu/c4KNNk7kDjwBC/lbuP1aqxcjJx/1qoB/EjSNIdyvPRvfxw7s/X6xI9WvGlwNeRMI9hP5m7Uc3fSS2EIhM9fAryhyNyuSTJNsjOR7W2tIlfl0miHtO3xeFrEN5HCUC/1y0Qqwg3BEQj05kco18cWNGwl6BNDJ4awPpTlGBdwWrqGiAe0Hmvf7e/KsMQgI2jpPHnWca+XjQ6UsKfW3TZeQXgCd8qO04DScWPZFh5MZKQxyAaFY5W+it3OPKjkWtImpw/7xFPDD0uN1FmK79AA0GImYeWdn915Pym0Ps38FxA4buum1tmrYjSzDKGD+TP/WVCCRQTVbsmy1TUikbFKGHU52gDV5fVwaFMoECsd0wPN+J/L0EJNufwrBefFAXs5ljeRxCEmyQVIxpbx/GCexSyyZBPro1iYAXdUw9nsm360IGD24yJv/BXKA74YtsWMCK6nlTDIWtbq4+ayXI/hkXuYlv6lKWcCFSWas5N6hzkNdjHL3pwlXSnM9cQT8PQNchVefvfIGRelVhLP3Rj5ua+zKWddwVXw00WWT6kg0DcnACx7hCGKKWpQYBXqrXdVgqSDcrUG8TxiwdNicT0GGRnvUnI9keDMHD1xcnwpj4UtI1/l+8c36s0E5ki1O5kcjgLmsQqant5Pvg+ywRwdfBZ5VYRvlGs8CqVJZR8mAOJKxmXcTaC3T6ItkeRF4m3Zgw9A/nbpRkwpT1y/MtQ/zFeavNik6DuWFnQ+si0HF34a7oBOBSScwvllyAvKugZGF7kilp2pbFUT4gGUDZ4ozh7ndvFWwCJcjuWPHKl6HJs7a3B773UUVEp72gL64NDWL9qwlKW3d/AU/4FIHEI2ZgIeHgEIpxLuTyLjJuTg5Lx/SmPGeSqQ+K69m0QJtwwUXbtJYTbkz3HfcP1sJmkmPqZZg9RsvX6MupTmpW7Y1uN3tbg5IFe8HTahozaR+H8o4tdZdEjBNl6GxwB19onn0FWdXMepVtb8elNao4G2dbOdKmgS//ZwFMdxc91czZHoXMDyxC3dRyC05eTsri3u+QngF2DMNozqd9mKCe+giAvM1AF9DwGMTFoLTAJH+U3ONU1mgrfDzB8RUatGewj5NkkPxIF6pXHxuWxi90+zVBM9rvoOJ5+e2GwTylKhOEHdBQ7yPfezbstHOunMxXHXQNkY998DIrAynT2EO2tjiTBA7BpXz3RukzsDHdwSXyKQLxBv9y/zWo46CxN7qJtmVGOaVYW2fPnIgvIKbR8gxyXu6R9e+QA4jXIy7i4SO6YmwMjcLVDOMTO6vVI297HW+DVSxIe+/Mtlanohml9+lJBcZrocJWWWQhMg9HQ6FWw5OjRwRadPmGzHU5OdkRVUrmJFS03VBzq4LnwQysn06IqrUNJGo0UX9zF5yCXOxL/2fB5+IXVs54KxqrIXIn7CuBpmopcXZSEPJwjz4aD82ekAX0E+zg9mvQF9QnDKL32ndTnfDqt38U7crcaSsdT9qq+kqebSRAYFUc3A7gKwYm0YaLovDdZkOS7CD81sx9VHkQruztcG5V5Y/C5iMGYrsrb6mjrGrCGK4vJvGHwSkZ4U9RduJCglRA9gq9E2BlD30wt707NferVNjSKbcjkWTxa/OivmnLnq185QbT2HpCljxEcFZ8pqC/yWi42/GvhNWa/0VdryZXVazpDBJcJIfO50uHhTyS0XVmoGkarbF/OTFWO1bbLu7scWD1LwN6INgBOgyK8X0X/slJGruCKrgKAzsFD8yzd7fGdsp6yoX3qxFfN/4j2JxCcrfNZDIKkC4rscC8/DcKHiwTOTvhwdD5Z9t4tV1VmYKPTa9Vw4Vm+xnCngJx/F4RFmYHs9Q2GbdjxCDzpHgjncB3dZLt+ZYAjZYBaYL7O/5HOf31CqtngGKa/VRHeO9FQPthA8S3dszTPVyCOX6d3hOnV8DNMyjqN0mGMucyspBS1JynXHYx1Y0lC352GtSQBU2c+V0iahH7rtl635+Sf0WYLin+kySBk/YB9UTEQAN3ERsmNK646M21rk/x+joKRy56oqtLRg2H+FuImPQljMyYnALhzlEKgUazygxJ8mCiDKGWolV0CjSCDKKPxNOwetlyTQIxiVla2Xt1bHOVIAU5svV5kFU6z/BEQZj3HshBk9X01+s2YUM1M8zbyteIXHzJWBwESiAEOp2AbJVQWPKPLoPv06fx5H0A0rW6ovqWf0jhOn6xggKefUbKxA3IRDsf01V4gxcvAqtPPsIzwPEtjNly2LbIotxOb18kW6lmen8XBQG5zA7gOtAy8mKbXNDAafrQNKCnwjVZj3zTRNxoYa1bx4NZv+uLUH1ZUqM70xCPR1O7SgcRf89qja+on4VZ1q1LfKZpMZgNCdTLrrozmkurL7P7Idi5J9PUw6Fr6nEky2fUY00n2t9Q5ZWgwQ6Nu7mN2a6tZc6cQbE2mtm6tOrNdZ0bzSvZmdNVsO6s9/I1wqBr7mNE2Bjn+w3BGWyCqc9p0ZjSjdG+6J42urdWsUhQY4OBvRhttgbz+H0M7IvtbqobE0KA5s3Rzn7PbXPiMMLFieb2IOR15A9CeSZR3cAWKqnyM+aQ6XOqsskTo4dFv70UXrg9N1jMsOXh6muHRj4rLnOGrAL8N+wqK+3RjPc0UtPHmmu1WU0z22h/8rK/uI/zk2NG00+DGm3dOv5qvHvoADn7mySO2vVSXmyx8SfapjAzLnPGrNI5/pChh+6T32gqjYasmQ0JPkvzJxCmebOtjElD43rN0h5/2H+r41/Rxb7MNo/9UAFUiXCqlmYOTYOaP0LX0wRyncbo9FuZwNZdozK7S3OD9TdfSx1zimMgIPBS5RtcGi5jNlkqDuL5dUysU8Pi6fxRXW0yuwWMEnj6DeH9Xxonhhd4ippIi2NjC1DS3QuVnkNcj7ntiD302p3qV6G7vr+bJ2XaBArJ2z9GNorRUnPlfIL+Boxg7APUt1YQk4vWTPE/DCM9s3QPKNQxPFjiO1jXIccivdZPaocf858nmDToLoQxFdYUaoxWI7/7UffxaxkW0j6MQovD72/dv+0vmMqkCdr85wY/1kQkuD4MNOxyQjI0QBw7mND7cCjRu/4vpEi5jgDxJoyA+S5O8yAI43Oyaj5Iw2gdxfzx6FRXFA6K0Bdkv+Qj2IEE+vTK6VfolE3aw/bfd9GZgaDx+e0cwlQKvRf/CD7QwbgtiNBJtlsvo0sNgMYqmRfAXZa/pvI7RzzpwYl856yaZ8uEh55cuGIXhuKhTSAlqeGE8sXeTH+bj06bSMYnpZEzYXvqd7OFMIJfU+pIoAvl6xZHWxMQ3bam57j6OwnyMsykPl67QC8u1JI/AbnLnWkGf8mvoydnsJsi2oG8hImQdb2pFU3rAbKY15VOwmfwufAw2q3zIcBC6+oWVkKuIqtRkUt/pefzlT396z0wlFyKCpArVEXeQPYzAIH1iVbqs37xNyxzIwZA3TCOwRtTT1Yivh8MSkaLaVeE1A2bAf6F3OLKDHVWJmcO2YClMwSV3DlxBYjcZb9Qmwx6LsDSYz6Wvk1gPcfoQxhR6FDqq8217+OrTpMxjTXLmSRhsVUBN6Qo94U5CsD5DihbzTI7Qf+tySv1tvo3CVxS+FB69Ej96L298/PATTY6SzlshN7Wsapjo8u4yi7ZR4oKZBravxs2dI2OWzwhcH345CzRDPzkrwAUfPYLs5QYFrBQeaMlK1GGWKpg5R4hJnZ4tSNym5onTMtnA/a4Au5OiyKLbsgBVKOt1VyJkFQYGb4LJ0jF1HSFlciSZyl7Ykx2b8XhUTKsKDl3r+bBuzav851VGUmgs1tRbMG6ZbxKW0+AxwrFjJmzmlMEGd8tXLpF3OhP+aG3bgqgm87qu6CHN46wDuqzoU6SktNeVZ8NXi9nWJmSq8YWVFk/NRFRRT90loQYI6yVZiTJgUgWj2jBFKHHL/VgyhUPnleW0uyUbzIr3FiPSZsBs44s2bT6bWrw10bEc2ssHDFpNzDMSUvtt6SZObkA3QVdTW7mbqZfY5+2vK2bHDPq3ESOyBIXc5D606zbl2ZDTYtOC6zU70rbTj6nIQ8WznGkIHoGrpBEkBV3KYwxOxFkDfooaAuJg+UpnjidgK3Fk0bG56jSKUU5SflRVI54a2L605N5yuUHN8kwN/vTcAOEUEOKaT8RMN68aaS4qbdnBbF4NRTqb12R81UQCWMaDtQZbCofu4+Kfp7WkqPQ1+au0lnecOz0ObFBTsMGozopafDC9k2LLCFcZfrL+yg9T8kN/EiZjizbMiXNtRcwKbZ8UJOLr4tWMjpZFKBgY3Zt0H4VjcwHulGWD+vNh8EFFzHIYoQp9VCV3kk4elf+pm7z68yjKJu6QRcET64gGxiPzKD6+avCagwjh4K0/Yz75ZQKhoz6X40ocXH0GyocIbZup88lCo2svmvM6qv6C0ZqMiZqYl8uwjjTYUjh0HxdvHWlJWYR1pOWdsU/DU7DBqKdhLT6Y/hjcMsJE1pFXfpildQSF217GvoIwpX3d8IfF7yeYjEXsJV1o9jWLtO5MeeSSCkcGg+azH3dI1Vl0wC01IUpyBlaf/LYYYV7HmVvWjTGBuDBm4TjBAce8OSapWoRNT8Zn6Nsgr+FKEwelnB/PsUM3T75D7SbnPYrhFuXTObgIZIthwWJOm81n5OJJM9tQOMoZSrk5Md3Ycs6Y8VCD6Zjv/LkAWRLEJ2VxjyCGuLDKZLUMtU5GAYWXvOLiJaCUvEVofDgxhgeXZJkBC/dJAam/LN0TuSJDpSN6wCee/UWInHGZZkwhos41k0uLJjb09zzYgs8RxCZ7WfNjoM80WDyJORcfusLBBI2nyFLpt2k4L17jrBV9GXEELKYuU6biL4zhtBsfvsSbM0e1WLIYeLzrG413OkKWxDDzD20xDduMGMZCj3GmDl7xR3RXnAXwaH9VomR/Odj8jIp7AQ3m0zjgNdBgQQHrPvqTJGMFv2xpUeIJ7lRMziKUrsMnyHRKPckaHuZcfEZQerQ4wBG3aes9TcN58dp3cinMVBuaE6uNpiMZ8xk1o9OqTd/SAsxfz/5GJN/uMKi+LpuHOkLmr2dfgyfI7VcpBJA3wmkR9kkO4hQ63PLF2y55VC3CksnjM6eb4IA6Ph92GU0MmfIKNS3T3dqu7qP9DoKa9U7WIEmHvms/LpuBWjrmv401qGKLER9v01nzzDmMwYEu8BPtUGdiHTGRslmibTDtJS1CYz9q2LDXa/q3vUGfjAV+BjFc4IvShymUKUR6JYvXgWl6FqH90vw0hr47LTuMpqLo88LUx+2izJJr8M8SeAjr4+mgTaDcOzNRJYsXLDQ9ixAsrV/1+OrKa5RTod4y5aE5xYicBVlBJHLzmXRwgE36GPXOOv3Cw0kOyNCm0ucMkgEyLLSIXWoObDbmXmXEXZNvVwxvzd+5Zw6MNaKrjxFfTe3xU/e/2oMwuqsfhbSpWBfAY1L8eXiJai6d++TkLYEV+ahf7tHPtQpdpOgZ5gc1RvAlGiW0KmDZVPRj4jZnIVsJKiNW6TDBBTBT4apOrW/GOUrZbM1skwtsGQVT83yjOtwE28HACLPRIjqcebiQpQeiLRAkaWgI09loeMw1EAhBPqecyTwGFlOd8Cm5DFafnNF+BFkUJEUrW8/S3W2U4IpryBPRI8hebqKdWLRRlagHdlSBlllwGDcea0mr+3k7Khwgr9wmI1QFCxLtOfPfYs7ms2fU8fddWx6d+qiuwJ5/L2EJnKXvSSTmUaoSyQt0wUGKR/EAzZr1SLTnxn/LlYkqLHmw0m9JIq93zM5XoD2NDFslmYqcWR/bEimmS4odWc0nW45qf5QQqcGis7E79m3oquT55I5RuVgBx5G4V5OTvPCxEQ93iE/NzaLNftFid356wBQC10YhmI207RPxI4jLlknlFPrhi3E5F5OrgmZd0ScPG/GTH1auqNXg5z6Aqdl68oO8ymFt2Ufy5Zy7P6ZPSZwGm9ETSzUd0zb09uPyEwm1tKj0NYMMQrVwWAW7fQz4yJtO3+zEgtbcjCgS6MGfjBduIpBB+kLgMf8G5gkv6kyLPYUL8fUgwtB39Kh0NoskGx1bzd+qOw0TjWi51eOfmdhq221yARzUR5mHilB5Wh4/MRQtga06flLxatNUgD3xVdsf/qPvdsQpXb4qzRKlpVNP53TU468BxzbxhI7n1DYj7hrJpc2CuSZ1aKswyILwwf3Z3SdzYZQ5LF59PhBhVVGzjJP/1yAp74KwKDOQjW4GIjunAVIFB8AWJD3LYIwmvcL6ZA+HHmxuUpKIQXVpTplXhGzGr3AwmVe0ua6d89ly3ZASZSJQjoD7tDlhKg6ktqMZcWF9Vl2U2ONZQZiygxF2OtaOOcq5lsPUHkBNaNuanr9GtGyZsFdzmzY9d12XMViBZYmtGudzfD3ORahX42BEGE3XwgRZy2oD8ks8vdJpPQqm02eAyTiPajFhNvWgAFsUSXRs00XTMX3l3X5cvsmipWUZ5oqWEVS3PK0J9CR9mu7E0kdQw48Tg86MO+IufcHTTvSMWG1RW968mG70Lc+c82ay5V0FLygpwFdQ3KcbZXFHtaIPj3TJOO4RZJ9iRpRV83OwpMZiBG6UUKh00qR4YW4suSixOFeWHF1AWrLkTKQkQqPGocZoIXzYIc6gclh8RlCkwVeT5lVC4WA19126GZPeiCwaLdlS16mY9aX1vKViIoZjBA6U0ajSfY8lZseai9p/58uYo0tGW76cyRZMPjtQN8uYPCfxdVImupSclsW1DuKtiZg+JXMNyQQzY8VFycd5MuP45hsrbpyJZLwC2S7Kc/gBkfE1wNJ+TXwFYZqJn18yFakDKlM4zvGZR5IIMbKGHzuOdCj9cCafPiUrTg/b6cTkPQgf0rIf9IL5LN69mZqUcGJLxxGdXLLkqPkMazEwnn7YU0Ch0h7ebzph0sCwzCDF29pItb5IIgTXUWJs8XVvv+Ne9r9+4bJTSTL0KPVJzMRs+KPJMyqnyNU8e0s7ySVKiprfpKZmDOKWKXUynfbbTsafiA0e4Ux8Sbdr4m80kUKW7Nej8lX2y0ZhSKJXETa+sl7KxswP35FEqXTXQ3EWrLaIvIXTcdWY2Qp12WnyRIXu+cdf8Jc+6xwGyyyGVU7u7qIYfgFrh7mXZVp52yENqPu69NTLHSlKW0+F2IRbTlwZMDxGjvK2+cQcWxrx9SBM+R09OvIEtZ3ubhMq+SjgT7QLspfz5/A+SLbgGq6IM6jRgyR8kbBXXYFmreajupTBKNB3kNUXTzzBo8sPP1R0KJkoxRMwD9bAP155YgKeoEZ+OmYI78GmjMFNkD80Vh/ym9hBh6xETShVMI4PBEuEECW/Fh7x0HniOg5lKt2S7Sbjvb+XoASb810QxSdFEYT32DQ99oskLha94M3cGst/q8QnTKXj6R8uSZgHSdZgO8A/TSWGhdqCsU7fM2RA7gjOkgdrTOfAhuuKiJDz/pmYbaoSOcl0wSj7JoG8iN88cZl4qLxzmVJ3JH5z4C1CxAlJsZvYoxBuumwwmWwjWk7Gfp/SrNytyts8zKIqc5br+xCxUsb0TUHklC7eWs3SpGNrmjDGdPQYFOAryHO4Da8/ZeluPC6hO++F9qGLFs8fPYJUeiQnYy4McpO+ssdM2KObismY42K3TzMUI+oOhXv2aXcScwmFAwWxV3IQBiOaJg1TUZRsJzUWnT9PzyoUDvTh7fnwWIWmaXGsAjuK08rblUuD/bx6OjL10ecgRBb6OZ9rz70TZiPoUumzxm8yRjsNwoeLZFWk4YPXc5IXNhMgT6EkrLN4fUlE2SIOXSK+m39+n/kx3YgxUW14bvqkP0l5UYBd7aiEfkruV5KS4xVEfh4pcwaJM4MLWeTpKqUleATu6pGk0iNuMplhII3jH2kBlcWap9D0dECxKandSeH3AmlyN+m6325wk63bcvfatkzDqtDvn5KmTKHCXbTVZtlQMIaNQTryWn1OwV/ow0mSP0n0MqJKf1abz+O8CrbhMVcbo2C4ZsRbHYqTcdk38JSfpTt8zFQUYESTsWUX2TUJkvp+QBJLONRa3Y3MRuhvtJELmaOt0J/C6uMoEsqAkxzJJf7wzIN/GtymOxnG6VZTHBFNxhZHZNfUCZD8fkDiSDjUWt2NzEbo76s0FzNHW6E/hdXHcewK+pzkSBzxh2ce/NPgNq0LCcIAqnZAVSBRjcYWSXTnjNNJV3JAYkky4Fodjs5Q7S8hl3Q1uFM5knQyZCpHEkowSnPhpbbFhC4q2LZ6DR4j8PQZxPu7Mk5AnitbpATtR7dMifDg2Ps5lQ5IoqnNiFbfU/IhVTB0u1PXEs75ZBzl9sKGNxgzZCaq8Tw4y0isTSrL1Jn5cKTWgkTVAu6dDdlpeXfM+jw07s3yOQ4hixYVbAGyxqMi3YBPUZYXH4MiuA1y9tiHWq1A0bqu7qPw7ZvqMzGZ9Xfkh7YLfn+7uU3hXAe3cdeEETg9wMFzl8uMBU+WcjshKwx0Bf8qcCRXppu2hNdFWzgA/ksaBnH0L7BpZpzTEacOr0tOtaHOg2RbYsdqts+2iNtVW6pCHlgVGb4xyqvg6AIKmWpCIpmaA1iwQYMZDNgqvN7ZWuo9p0RUYFn3ZL0BHMiqA4jQQWeY/uliXrd0jYHeerG5me565bz+mrQ2g4SJ5YBcCChLgC5RMNNDV8TroSsd6IHKI8/2QhfzeqJrDPXWvcBnu+rKuP10xUqdpPGGTxFZKu6orqDSVfscnN9XWyzsrK0xtKKrfVg2iGwV7jqu02i31daqa7nVyUQ9SzscXlGIZ/kLqinhrqemcGhDLeDCg7g8QlWJN3q9cu7mSlcZWr8gjh5B9nIT7Xj90cXcdUzVUJuc0zLZxKC66RVNE1lHMmFkNd3O25jWkMEK7lIcbqKEGtNKDVOJ9GZqyBZRlyhZbw0NSF5uLeliJmoq48Kcv4Xbc1tDtkO3lRS7lfQn72iwhzbmIdNBW8KD3xYOgO8uLxn4XRGvg650oAfiBoLpgijj9YGL87V6N6LDElko6Ujt4IQrizqRwq9rqMD/I0vLvaiTulDSU11joKfOkYbppyvi9YJKh8UD4UrISgWikCsMiHKFfgQ9iGFrKfviMwe31pDqryrTzp/hLpAE8UlZ3CMDRIhNDMLjl7w6Dyl5iwHs6gQJDBr1d15/dZHiWeE7et7bxjATnhvoarIzBF1zAIs/oju4b2abASz41XhY8GsqYiHpWd6b2jQKhABRJpxOJZ0K1/yG7335nVRlwk6q4qEDM3iCBF+lUVLk4gnj1uIennkVh/Ty+2i/46sfXRFXG29LFXsQzBhdLOtJad5+BnEMCvFg9sp5/fWqDM4hVPugBPpnCbgKQ6+cP29UlcHxTLEwhmtFPKb9Kvxx7ddS055XexBGd7XoJdIeifRoUX2ZRs1vo6xb85tf1k/V2PGSVueOnbSFEXbKeOlgpDqnNwHXPkkUSmYLl6v18yPIoiDpki6dpbvbKAkE86LSSIKXtN0Avn8vA/zlexLxVjVdzMOBrmE2OupDIl9N9f/666jfUIyQGibafNlbWjm2DQ/iQFZWwYasb4SXMk46+JhyTZ3lTZV16uoa66huMaQUp09JnAY8RbAr4iq/benQfWAEsqss4toTiTLuXWBXrGJn5ovHrkhoX1YRjVXFLAgfhH1UheJeqvIhdqnyRDWZ2Vn+oMu5DEFXUbgPOpddBjWFopsgJd2SzDbP1S7JCiL9kqwzdMxmEz6yh2y2DveIzVbT7Vy02EUVldBQW+DC9GwcHVhQk68NCyproqOAhxoCwwZPMpkTa/QkS7mGT7KCeleoC3l3VY2BLqtKgxf6PMqEFClR0iW0YWnoyrjYd8WDwxULjU9EGX+IYkWz0vdM3AlRxuuEKB4yq4AEwP1KJnTYKlwzC1NraJeFMABWAW659569cu5uS1cZvCrESSdYWV59518N4qJBA2mTE4NjFW2K+KbQplQFdbHtlS4WEqJ8yuVlTmA75dXi9s2rqIHCQN/DnSocyrjRiDmnM249/jGNW1UdEXn3g50Om9SpyOOsCZ0q5prMqRqDfjW7fRBteQearojvV9OUDmq7OAjmDdjtY770Z2rw9d5eJYWLoy+gKEBGhy1iehdVFF0q8eqqXJcNoMGpI7k80+q+H16Vc1CkK8guOntVByc/yMsM/ATR9p7Hzb1y/sRTVdQ6/BjBZZ3zR5qtIumWqDXQcy8+KdNtr5zXZ6/KkCh+SULJ1kOWcgUxWWHwmq8f55CVS0wV/nVev5ZSz+JR7ZWL+1QdVWG8M6ZrYU2uL4KosobPoEyEcqsN+RAqC9PG70aCAVtF5o2p3HNjcRZ3zNSQGa5Vu70GqNpG7ILXr8A/V9J1hgY5S/McAo/FvbJVuIPM1BoUml0sOI647Ar5grIrV+iHjPPG7YusIOqPrKN4VS9xQWOryK7o1yf7fRyBzU1a14/AsKNJ41shdpViashcNOpKGh0rOoarO4hT2VF17xdULqEU76LWXT1lLFpf3CFbL11H6iq91kGg4aUBv0R+NTXmJNsMs0nTXix6mBpqaNTV1TEYelMgqCd5W7BusVL21KwFwRAugnoyXPQ9WEkL/BBCssoyrKh26pfulB19CDdpbRlydENtd1sknwYnUlxXOplGIpAJGMhOI1NF5CNI11I4Erfhd7jn4LZUdPhtKyj47Iq7okpFnruqXfWCdvAP1l258GzbVVHbwzgv7EVbGaeqZEfj1NbBaAgNhb6ZDonno+QdAftGb909GSTatNcHsgZUj2/I58L994sQBfHbRLql5F1iBUXlneE7mnzVoSHfaA6PC7+2t0HhvUXtRkT6vFR/OCg5yX3YmPPGRaWZmEyexosplD1xpCBIX3ViUGqPNPUHTHbWWK9qLmUHTKWZmNz+A0hMoehxI7dl/4xFQRA+xHQ8PDdBtkV+PZrDUzeT8BOPOBFRkw4P8aoSanYZ3kTZ0eDUEpPAeeeJsZc84BS1R52rwjAmHh3IGpAi0ok6vgmPeiJI/MDWhmD6gauMbFla4T7yvSe3Hf6Cp7Qjk99/hUtkLOcMgaS2MyLoPUTwjrjaPphC6+Gg3syu28e67EjwK0rEH/1cuJJ+/LfAVDvuO2DcWv6815gTGlou7y6zaBslEjZgqronv2dQIafdIcn002UxvVQ9yYbFeUpdbVaSJ9Ljky18lrwm31ILR0Op+SCJ7Atwkljxo245LMGDbj7ooUfZDga4HpJ2ChUGtdfECa+4nAAnw6I1IAc4FK05s1NfhSPB1vWhbAsiDpAD4VDV7pM2zA9MVW/cMO4Q9EIikBcWYjVM2EaijHGuTCp9TBIch6uSiQBxy70M0zC3cKt745iRh6S+xZRpqf0q7hW03o0sbieKTGFOYk/XFRNKV3SuVo9IentR3F5WSyxX4srDBj6ucU9KuOA2ngLgdSDENipxZSfzOIdhOI1ilJS4hSwZhF5Vf0OgxEMWRDeBfDvQYqqZuj6XQC8YEQVAFGFIfxiaSDzSaxC2ko/bj340IdxSGCnIglSpHYpTy4/9aTRyrzJ8dahGNVN5mcS398bSdc2p5WNFM0GscFPiqxtycQCqYXp71bwRTIXT6ijmh8kyJHlNRrQS0EvWGUCZipvVocyNh8W2Zhu6ndkapHRaqzrWiE4zm3glkmBlK5ao5xD1kddtEyhNuvuylXzsvv1wbrhl99EdqdJ9iFPLzwY0Grkqu6+48jKJR/5vUpamK/hgZzKuX2Uo4cXtMyOtCi24riDyiSOrWCLJtKDDIrbtBAEP7XyCascZpdMRt77PE5LI90clXKLbYakclvSGBrdRI9LMSWraYaLoVDIwcRt4PWEPDbE05L6zoVEwOXEbjME7Yw+RLKioVAipNfTBTSqBVjE0rYCp+kOHY10qmPC49dzb76gIr7gVP4CrKaEyZujV8DHr/sjjBZxdd86eYudJfgMxCfZepbzwthQUabhaR0NTj7viuFS1red1FsPRhtAVj0G/ilvCmfC/XTtHJ4kOf8mNLFvJ/T2sf1KboMvrK6j43Ac52PyMinuiB5bwoSbOyKHa9sNK46bCkNHmA0Gt2w6+eBj4DRwRwm0pXPFK8bodDc13ct6Vx4du5VYuTDU8bbjvAYFIVPEgEMlQ5V07bhRyfRI5gcWlqpC0vg/FSBJIHQNRiYzuZlgG18VwI7fcMc3QNDHbxWuiV8Mt0f2o9riZMGK9OXl4/+/giqmkKzpCnNuS2V+l4fNND0LNI+zBI1+/4pLOfFQiAKnAE9T0Ieq4CQ5wc3neAlvyhQuZV83tah6PZCoTw8AWx63pZ3PjZJioZbcscYSFrVBheQvrul/hvr2y+okwBt6LyKoPngGdvA8RpQCp5f1AWg8HAyRbGeLKPhbH5EMhMREI67q3FIw9DFey1C4KjuwDDd0PkLRfHjTFfCoGHCRJHSPIe8NlLgMwEv4ZHhe1AVGASqfMkMAWpMFwza3yznVZWApt5KGa6QpgQ7XJrmoltb2JCTYgHQmBLPUyGOLLWUntQaIE1HDImMeQSNI3qb5wVgYhuWdx9fhZIfUVOaI6yau8DPHwJq7S2tsSXdpw0vnAjMaUAiEeIF7qMjwUspRkSx1iAzYdnzdVRsvdEHHSmjEZ4CTDpdR8cAiktKtpj2J8pDAlydxcDSkzHmSfqgMrAzLioChAV4Dsf4GrsO5w2xH5dlohwM3ZtxZ2qzykcjijDIoKXCotmgw4P9WZuRqvuM2PvperbGUmzlF1rkb5AwtOLTHCNm8M+jknK0VdlE/SfJZXwW4fgw6weJ57NR2hPuIMt5kyB/3OBTWHrb7mHsNMBlDcXJzc04Z8iaLJVnKvWvontYnq0QbDU4hHwtT1plOPFjKPCuEvM0zxK/oRbMIUBTQMYc5Z62EQm6T4FQdI4VIwZImaaBBwdl35Bset55ETqGzBxABy8wAbkE0FYpISzq/piXSTWE9O4glT2TO0Ii5zW0q0AGv3eOEg8St4Hyyd+Mvclo75YaaDVm9keszVb+STr3jbN1Pmc2B0GKnfyL1WMtGw1KlGlNikX9cnd3AzqlBgejU8DIkCg/TrigkTEyQlZCaD06UVkm3enFp+Nm6t8J025CosD2FdR+hzW4o5QFDDw5BIXvyK6vpdHtMNDp3fSoFp5A0kuwvZjt5j6BLZ+Xcw4xcLcKQBk/gpSBv45as5DBiCVwFriOc6EbO1/A5N1yEDwBnpvfxsCqtroIWYHLoh8+qBLBp4OTGQt44D0jXHiAZBvMYGWvjlpHkMGpUSSmXzl9X3aagezD7IgBtnqIbDgHDre1YMJh8sbiIvMudYHT+Ds/GptZTsXkwn5J7FFMr2wcnylZ3dg/AhLfu3xsxnLt+ptpXwD9sPyTdsqYwXuejIAbq6U4ZzXGYoFWGtqqwvkqiIgljytlTWQEyk2btSuq/ew6R+ofPBaB6csV0Nj4uwrSdypZD4j3IFdayH8SQsokfYzZd0uyb+RtlBuXHoJdXFJDKACdKYMslQEXVFMBwPiexhE7fe8C6m/6ZpHLKHyPVOZp88R7N5dxfF8CNYSx4vspUks2H4arHtg27ZfXXAuHEv1pjINUNQ06fG23bZY+HYWeyxFRSJyPEg2gXZy/lzeB8kW3ANh/YMyk6QhII8d0ONZIPSwKUGpPkoOy2hXulzUfXF7SDgH8rU07UXRnZ4DzZQ6b8J8odmNye/cYkfaiMhhAJN0kMVyIaC7VwIyN1u//cSlGBzvguiGGqnQXiPtULppcBQEz83BNxeew6P3Bo+h2glzp2q0GpgoFrg/bFqC+Y+XOsKeiiMESisKyaMBknQQxeoDY1oQLxxDdWHKtOQjZySNROOwaHrV+VtHmZR9fBhMFsEv7YPHZTpjgLBKXXgZRk9QnXjK1Qf4Spff8rSnWw8ZNV9DAjdX88tgi5yPRQ3qcZAEJWXPgwXu32aIWeRO+REOaTFSGr71F+obikQvRLr4Th/1hkOSW2fw0F1S29Uzz6G4yPYx2ll02tBiwaDU9c5IRwYXbccMGSh9ZCcBuHDRQL1pPBBeV8ZbONDigg6pQAJ63gbJolb/1ATmcA086Obdoi+gqREEXFq4wj6yXfvZqvJ1Puk5Jg+yM9S/26yLwYCWWS/Aadx/CMtomRbU4YssmdpkhdZEEHxgbSNdoU0eQhv0nW/nSghiRPYwwvTIGkhzbsMOSQTM4Xuh53MpKIwkkR1OVFaSVsmHRKUB+gs3eFNS5FRiCai9EYWEP1zHYU/AYP67nRgu2RL8uFq68mRV07mNAHZKKuj5uwTTUTJKi0g+ucnCn9y9yS/Ox3YLnWmfLjaenLklVNzTkB2m3/vB4rirTj/VCNpYkFzqP75qkdF3zzSlTgeZCLl4dDIdVUVyOCTMJMhqFXma5yR7zOI93dlnKBE8Krak6C95A2v+75G0NiEZLLHD04lf9NEFWgNOt1SkXQxvcsZPiN2GxxaM6gjc6769FkP8vBb/l5F90d996T/9q4CggYezjLI2rLf3iFT3i6oP8Cf1a3a13QD4hx//e3ddQlb70D16yPIo20H4jcIMwEh6rMD2tS5SO7QE8M9yDABJEZNlaa4NTQUwSYogpOsiNA7UlgcwrWE3S2xuyC64boFm4vksiz2ZQFJBrvbmLoD/u2dvP/f3jE4/1bHfXJBAkQzgiSAy+S0jOJNi/enIM57W58IxBkc/T8A/F7NJVyaBdi+tJC+pYkioHr4PoI9SDZwyd2A3T6GwPLLZBU8AhPcvufgC9gG4Qv8/hhh70cRkOGJoIf9t49RsM2CXV7D6NrDn5CHN7vn//P/AYhmF/+B6QoA + + + dbo + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/202003311314082_CookieManagerCountries.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202003311314082_CookieManagerCountries.Designer.cs new file mode 100644 index 0000000000..d87fcba817 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003311314082_CookieManagerCountries.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 CookieManagerCountries : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(CookieManagerCountries)); + + string IMigrationMetadata.Id + { + get { return "202003311314082_CookieManagerCountries"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202003311314082_CookieManagerCountries.cs b/src/Libraries/SmartStore.Data/Migrations/202003311314082_CookieManagerCountries.cs new file mode 100644 index 0000000000..bfcb56642b --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003311314082_CookieManagerCountries.cs @@ -0,0 +1,41 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using SmartStore.Core.Data; + using SmartStore.Data.Setup; + + public partial class CookieManagerCountries : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + AddColumn("dbo.Country", "DisplayCookieManager", c => c.Boolean(nullable: false, defaultValue: true)); + } + + public override void Down() + { + DropColumn("dbo.Country", "DisplayCookieManager"); + } + + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + if (!DataSettings.DatabaseIsInstalled()) + return; + + // Add resources. + context.MigrateLocaleResources(MigrateLocaleResources); + + context.SaveChanges(); + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("Admin.Configuration.Countries.Fields.DisplayCookieManager", + "Display Cookie Manager", + "Cookie-Manager anzeigen", + "Specifies whether the Cookie Manager will be displayed to shop visitors from this country.", + "Bestimmt, ob der Cookie-Manager Shop-Besuchern aus diesem Land angezeigt wird."); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202003311314082_CookieManagerCountries.resx b/src/Libraries/SmartStore.Data/Migrations/202003311314082_CookieManagerCountries.resx new file mode 100644 index 0000000000..70fda553ac --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202003311314082_CookieManagerCountries.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/202004301922188_RemoveCustomerCustomerRoles.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202004301922188_RemoveCustomerCustomerRoles.Designer.cs new file mode 100644 index 0000000000..50ab827064 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202004301922188_RemoveCustomerCustomerRoles.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 RemoveCustomerCustomerRoles : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(RemoveCustomerCustomerRoles)); + + string IMigrationMetadata.Id + { + get { return "202004301922188_RemoveCustomerCustomerRoles"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202004301922188_RemoveCustomerCustomerRoles.cs b/src/Libraries/SmartStore.Data/Migrations/202004301922188_RemoveCustomerCustomerRoles.cs new file mode 100644 index 0000000000..99ac87c6de --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202004301922188_RemoveCustomerCustomerRoles.cs @@ -0,0 +1,65 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using System.Web.Hosting; + using SmartStore.Core.Data; + using SmartStore.Data.Setup; + + public partial class RemoveCustomerCustomerRoles : DbMigration, IDataSeeder + { + public override void Up() + { + if (DataSettings.Current.IsSqlServer) + { + AlterColumn("dbo.Rule", "Value", c => c.String()); + } + else + { + // SQL Server CE cannot alter column of type NTEXT or IMAGE. + } + + //DropForeignKey("dbo.Customer_CustomerRole_Mapping", "Customer_Id", "dbo.Customer"); + //DropForeignKey("dbo.Customer_CustomerRole_Mapping", "CustomerRole_Id", "dbo.CustomerRole"); + //DropIndex("dbo.Customer_CustomerRole_Mapping", new[] { "Customer_Id" }); + //DropIndex("dbo.Customer_CustomerRole_Mapping", new[] { "CustomerRole_Id" }); + //DropTable("dbo.Customer_CustomerRole_Mapping"); + } + + public override void Down() + { + AlterColumn("dbo.Rule", "Value", c => c.String(maxLength: 400)); + + //CreateTable( + // "dbo.Customer_CustomerRole_Mapping", + // c => new + // { + // Customer_Id = c.Int(nullable: false), + // CustomerRole_Id = c.Int(nullable: false), + // }) + // .PrimaryKey(t => new { t.Customer_Id, t.CustomerRole_Id }); + + //CreateIndex("dbo.Customer_CustomerRole_Mapping", "CustomerRole_Id"); + //CreateIndex("dbo.Customer_CustomerRole_Mapping", "Customer_Id"); + //AddForeignKey("dbo.Customer_CustomerRole_Mapping", "CustomerRole_Id", "dbo.CustomerRole", "Id", cascadeDelete: true); + //AddForeignKey("dbo.Customer_CustomerRole_Mapping", "Customer_Id", "dbo.Customer", "Id", cascadeDelete: true); + } + + public bool RollbackOnFailure => false; + + public void Seed(SmartObjectContext context) + { + if (!HostingEnvironment.IsHosted || !DataSettings.Current.IsSqlServer) + { + return; + } + + // Remove Customer_CustomerRole_Mapping. + // Has been migrated through 202003052100521_CustomerRoleMappings. + context.ExecuteSqlCommandSafe("ALTER TABLE [dbo].[Customer_CustomerRole_Mapping] DROP CONSTRAINT [FK_dbo.Customer_CustomerRole_Mapping_dbo.Customer_Customer_Id]"); + context.ExecuteSqlCommandSafe("ALTER TABLE [dbo].[Customer_CustomerRole_Mapping] DROP CONSTRAINT [FK_dbo.Customer_CustomerRole_Mapping_dbo.CustomerRole_CustomerRole_Id]"); + context.ExecuteSqlCommandSafe("DROP INDEX [IX_Customer_Id] ON [dbo].[Customer_CustomerRole_Mapping]"); + context.ExecuteSqlCommandSafe("DROP INDEX [IX_CustomerRole_Id] ON [dbo].[Customer_CustomerRole_Mapping]"); + context.ExecuteSqlCommandSafe("DROP TABLE [dbo].[Customer_CustomerRole_Mapping]"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202004301922188_RemoveCustomerCustomerRoles.resx b/src/Libraries/SmartStore.Data/Migrations/202004301922188_RemoveCustomerCustomerRoles.resx new file mode 100644 index 0000000000..f54ddd7294 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202004301922188_RemoveCustomerCustomerRoles.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/202005111006305_CampaignSubjectToAcl.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202005111006305_CampaignSubjectToAcl.Designer.cs new file mode 100644 index 0000000000..e4153785ef --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202005111006305_CampaignSubjectToAcl.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 CampaignSubjectToAcl : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(CampaignSubjectToAcl)); + + string IMigrationMetadata.Id + { + get { return "202005111006305_CampaignSubjectToAcl"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202005111006305_CampaignSubjectToAcl.cs b/src/Libraries/SmartStore.Data/Migrations/202005111006305_CampaignSubjectToAcl.cs new file mode 100644 index 0000000000..e6d328c13a --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202005111006305_CampaignSubjectToAcl.cs @@ -0,0 +1,19 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class CampaignSubjectToAcl : DbMigration + { + public override void Up() + { + AddColumn("dbo.Campaign", "SubjectToAcl", c => c.Boolean(nullable: false)); + CreateIndex("dbo.NewsLetterSubscription", "Active"); + } + + public override void Down() + { + DropIndex("dbo.NewsLetterSubscription", new[] { "Active" }); + DropColumn("dbo.Campaign", "SubjectToAcl"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202005111006305_CampaignSubjectToAcl.resx b/src/Libraries/SmartStore.Data/Migrations/202005111006305_CampaignSubjectToAcl.resx new file mode 100644 index 0000000000..8bb9fa2f2d --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202005111006305_CampaignSubjectToAcl.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/202005201826512_MediaManager3.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202005201826512_MediaManager3.Designer.cs new file mode 100644 index 0000000000..dd34b95167 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202005201826512_MediaManager3.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 MediaManager3 : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(MediaManager3)); + + string IMigrationMetadata.Id + { + get { return "202005201826512_MediaManager3"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202005201826512_MediaManager3.cs b/src/Libraries/SmartStore.Data/Migrations/202005201826512_MediaManager3.cs new file mode 100644 index 0000000000..d80781eac3 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202005201826512_MediaManager3.cs @@ -0,0 +1,23 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using SmartStore.Data.Setup; + + public partial class MediaManager3 : DbMigration, IDataSeeder + { + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + // Empty, but we need the events in SmartStore.Services + } + + public override void Up() + { + } + + public override void Down() + { + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202005201826512_MediaManager3.resx b/src/Libraries/SmartStore.Data/Migrations/202005201826512_MediaManager3.resx new file mode 100644 index 0000000000..8bb9fa2f2d --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202005201826512_MediaManager3.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 + + + H4sIAAAAAAAEAOy9224cO7Io+D7A/IPhp5nB3va6dAN9Gt1zIMuSLWzbUkuyfXa/CKksSspWVmZ1Xiypf20e5pPmF4bMKy/BO/NS7sIClkvJYJAMRgSDwWDw//t//t+//M/nbfrqByrKJM/++vrXN7+8foWyON8k2f1fX9fV3X/+6fX//L//9//tLyeb7fOrbz3c7wQO18zKv75+qKrdn9++LeMHtI3KN9skLvIyv6vexPn2bbTJ3/72yy//4+2vv75FGMVrjOvVq79c1lmVbFHzB/7zOM9itKvqKP2cb1Badt9xyVWD9dWXaIvKXRSjv76+2kZFdVXlBXrzPqqi16+O0iTC3bhC6d3rV1GW5VVU4U7++WuJrqoiz+6vdvhDlF6/7BCGu4vSEnWd//MIbjqOX34j43g7VuxRxXVZ5VtLhL/+3hHmLV/dibyvB8Jh0p1gElcvZNQN+f76+jrfJfHrV3xLfz5OCwLFkPa4oS8GS7I3Tb2y/ec/XnFA/zEwxe9vfsP//fIfr47rtKoL9NcM1VURpf/x6qK+TZP4v9DLdf6Isr9mdZrSPcV9xWXMB/zposh3qKheLtFd1/+zzetXb9l6b/mKQzWqTju4s6z6/bfXr77gxqPbFA2MQBGiGdUHlKEiqtDmIqoqVGQEB2pIKbTOtXX1UlZoS373bWL+w3L0+tXn6PkTyu6rh7++xj9fvzpNntGm/9L142uWYLHDlaqiRrqmzsq2sW5K29be5XmKogwYoxrZx2qbjnSarM/v8s3LcVkep1FZTk+gLE7rDTrLrhJMpmjnS6Oz8gJ3+ykvNrigQjHmD1+UPcLJaXGdVOn0LHn1kBeVrqk//hJkbrFaVzTy2x//GIhdJyfaZ1RFWIURNpheJkhj71EZF8muXWFmaG8e3vuUbLGYb67zRoOXvpJ5ibINKo7K78nmHlW+2Fosf8+z6enQNvW9iHbYoqrwqiX03aQ+luQnZt78Rv4OMzcqAujLIsmLZhlWL+gGyvA6up9eH9a3/8DrxHV+FKcBVh9iQpUPtlT8y9vRClTbhtHzMTZ67vPixcVCjJ7fUBgORqK8LY15+IdfzFZISwZ6n5S7NHo5J5JoIz/G/HOFqqoZizXvYE11l9zXRQP9psNz4KC1cdC3KK1DLGCWzTa00lPXiWc/5XGUJv9Cm75NB+7tcLTMKyA8sLG8rXY67KYWsPyi7L6O7i1ZBMBDpg5h+nwo8no3v3gN7S/V9Fzy/SX6kdw3DCSZydevLlHaAJQPya7164mSdTOCnxb59jJPIYEeoG6u8rqIyfhyLeh1VDRWv5tOGbrlqUo6PAcNIm9LsxD+OpG4dDPTUV3lh5ikfVz7nzW6QvkxxqFqPcAe5jSN7s+2eLCnSYo05A7i2rmsvPdKgX0BjhuvcCa4Umc2qrudjEtUNjqulCtQDlKuQ2WAg25k1KgUule67tYZhzqIgcbhPGhYva7zta56WmtUyG/TaOy+9YW2MGclka6LtL5PMkGJ6Kpe53UMKZ9wVpW/UgBtK60KcVIKF6jYJiURzksUN2cn1grhCsU1cSO+4XEdFIG8LaOjTdNzF+fljpoxzGifo90OdwFe70DQG5F9Rh43qyGsgIbVvJZCsI1ArD8iPPC/vK2jNM2fvE1Jjil8V9XjZv5RQebQGpdUyGisoGjRADcykRzlygBcECqTOpBEGSqPTvbnVBv8GmmpbZzUBjuT9o75rnrJzPhBS8jbmml95DfpBUJXmIXbRcFPRV1HzyfPaLvzPvTGiLoNL8HDTYFG18ZV8sP77LmPkGqZ3w9XUOtnAmNnAo3M6ysbLW46wssa7xJQZTCmEVIyih5A3e8BykutdlhOOpVgpFdJnbJXrAyCgz5116eG7gKNX84oIsr07EWnlMIot6s4H5VqIy4NT7Vf7RVlfcucejl7XvP7BO/8z3dkuvOC6V9XRro5ltvauwUibHSefa2GmNf3+Mt1snVwo+824ZB9isoKf4pRWUpRGtvfbRBNInHk9jE2sE7kC0XLWoCwtaNpjRpUdSs3Ac7dxVZInNeZZJXpS8FuCoVCF0UI611J9LLFyvAzqh7yjcSVQYOAHYUhRFcFDGbb5WYtA3tKStpVrcdO91IsFXoIgNj2rreEVRRlYUCSSkCEHsvgvPw9HpF4uGqU5vdvDqF4aw2kOsXggVoOYdiEuttRYT6ds8WTZzLZUfopyR6nvwsQbe7RNXqupickaemqehk3rW6+weZSXIDgCI1F1mmZa7TFW/3K+5zQ8DpEGPlY43WIMCO7iAqsAfvJ8Z+UTRKRABABka4XmJOTf1k6f4ijffA8Xud4vUZxxaMyPD7tq503E6ziqDBbyYsiidFllN2j6bmXXNI4zz5iMl00h7WeVwCjcjCUj3a7NPGPdwl5/2G1MT2YOwPcBPQIDdqbLfJ7dBdhO/Nbgp4+qwPXHLWtdEfQ8bPhRrCDvs7pfTiwJwTg5NtDCNh2YzOoYbVbgAID/AJDqegoFUHC+nPt/BbS7oXx4fYz47DDGvho+HXYY8nbWibiq58Z0q6v+fO1RBcI82JWBVhn+46JGN+jONlGeKW8KPCvLhvHn16/uiKxVNhocG7qaNsy+kTNXFWY9cmqYOL0FK6AbBxrXqJ/1gm2BI7zepdndCS068yImCbbNfUz01g1jZR5314ZMBHilZbIdCsnuxZOtXjyGt9opbUeyecoq++imOjiwnQwXB3VeBhQgyGx8M6jwt83dWxs2ozgqrH0UAbDGECDmgyWznlpN8OYDPRUeThmaTQHw0He1jLO2Z/bZ0rz3sFltyqX3cHJtkYnW2C/U0iX2MGHNb8PK4SXNLSbytk41TurYGM2jL+Kxg37rGAIwcqTgPmZemN/rO28pu6bAcPBxJO3dZqnWFlYLngau/D3IHr/KFWd9IZZW+axK8gxeVaqbaUgN7c/Y4VKh3rPdyG/kTVN09NcxQcML9tVHPcptbbfiFm6aXLfTmz/fk821YNV1z6i5P6hsqqy3vX+rLwuIiw9UCK9Zaysj8kGK1dfLN0KFCAsgKwA0P1wUyOhXQJgC2FYy3sgyjbgykSrgAewdUzRo9N0jwWFOklDKLrKgNl2uHPBDdgksaQcFGx6yaHEmFI5qK3BeB1JrrfQrbM3WdgSBWmbYluSYuGPHxVdasolxitQLtAOBPIKHG3RdOLiZbo2OA7Gq7ytNsYqrPE60QXBq7RWpTsNEyJ5HGXvEUmM3UuN3xI1m4nT6Mpj+ojU+wL1Q5JuCrJKS3VZuya1LCTqM7oUVhosiK2ilS8PVmsu3DNgUTa/C9HSIwTZ4KUApKy7pj1Kb+utn6JtUATTs6/eRSWibl3R64HmPL1U5x4MoyS6rPxY2T74KgjI0eg+k4P55jOXHZLDqilv6z2l0t8lWUTuuJio9DAJX3lj1eMYl0d1mHR5Wx2tpotDX0EmPrW323vbJaQP0e/QLDeOZv0egBW97j7o+9wD+mUE63vkK8oHCdZKcIhwytb4au6bN8l3Q2iGb0mZYOizbJP8SDZ1lKYv3i4ygjJJqecmaIGgS63jDHEvmeAFCi9VaIl2mXCZ5s2fOQMyyOXJOds72myT7DjfbikX8GSN9VIWKDIn8A2hDk+4c/5D5FCgs0oqmOcS/UjQk7f35Wi3K/IfaHMZkcc4rsgm12emv+RVYIw9umu8wKXcoL37GA7piu/AXT3W0wsdFZuB1/3qS729HbXGZM1+qBKVgIdK5fMhuauOo8L7TK/HE8K66u5FnFcPmOJDlLdf/zqco7GmuVceRKnVxJavSMqg9OVos+H64D2ms/J9/pSleeQf99bh8b5mlKWtgPcI/V3o0XMrcOd3Ak7HjXOH5uR5l7TvNb2PXnicZiiaPFjtjZUAbP8xKq8ibDWhULPKYrM85cG9+Vqi4ui+QGgbIHaAQTZLFoqz8pKkCi4CZJQcEB2/xClqO+Wr42iMF6hIcm/pG3A2a3+D2FNWzkqSGugkI1W82ZGkkA+X5BPr02aTGaU9xuMH4oKZ7I7gWRksr+hZeVJ6k5N6qtDbKRllJP4l+4FFE6Nr00B579yqPH78Wx11vpkAvs4G49GPKMF1Ga+GZ7g32FPnBSvJAo78U/7UjpqsNAFeGsMbhOTupXEInOZF38d3iEoM7ob4XRQ/Nk+kkrwIvr1sdoME41lLS7wDGTa93hZFs+nHs5Rs622YSWoxRs/hMPZYriq0C4HphbykW+QpE2BKXHZ9Q0y5fTgf4vB4K3fCAGjTYU2Ul2QCGetYF5AOvqtf3tVVNTpXPHQLgf6elA9pUlZhkHbKL0VYePG6xvivnO+M491Jgy6Jvf1rDJLgK/B5upm2gcGBXU6YamCH8USp5UDMcQ6pDEiwsENSAhpXl9rAEVPvzjvJKlSUQfirU9sMZjQxU3SKfdY28ebrOkGtTHoveNiGQGV1VGHVeVtX6Djf3iZZd4gakAknyL71nYnDD05ldh8XHD1z7yA8i0xLm2Gl8dUnAyI/ZRLuxGZtd077Nx6GGABPp4/JMw9hvMfrvfOCpyb5gYoXUtnS79VbspgStoHR7RJRXkdFcnenPSb4PcgUEHVbvJzfnRfJfSKm4NFt2krULfRBPDwDvs8oKusCERoqKBDkyt7Qpm2KKEO05AeL2oy0dbZJUXN4qnF2hpHFtr0LVJxh6Q/lY2OQEmqExnn1kLeuO7zG+J8UJNlF0pzT2bu6MZeQk9KuvnVXQt+Hv7DLbaS/BT+AWj+UwCoYVYDdjQArxNfxILLwOgHONh6QXgCUfWYBxQ7T5dLeMkCOoYutXBChUF56G8FuOkmSBjPKQGXX36Tw1lcgum20Ot8aC/WiiMrkQWQDEOAcO65PrwYAqqJKATDpHUQI1nEcnTYzvESp6L802FUbFevY8yFuRd7xFkTRaxZA1mUOyrG/jdPkLokbkGGnrew/XEUxHnUF2fg0tRzHK73l2uNl4EStShVLlSoN46hTv2FLHFvzkOtD2X1FPcUEGdSSzZJJVcep4lFbDdxitLZDdB4XvUFT8iALKDIhXS7lQgbIlg35cA5FZ3lQsbsshLTDHJj1dXHK7Qh0dyiGOEMoFHhBhPB7Yqi1Ep3yX+NtITnrftPhOFyckLelcSZNlBGnOQMs3yVpGiJapEEW7IHZp/wTIkQ8K3NNsuXfQiRZeigQMm3w9wANfqm3CEsp15hn9PC3yHuLH851G8yb3GE6zvPHBLVxNN4nsoGDpI82G4ylPM2LbaRyRIV+suIKax+yh/yRZLLlhAG5GdQ59YIdBCC+XwdCeS0tDEqvBYbBdFhm5G118+Yd4rTManV7S1yIkeZeTpjc+2vQgzqPYUh5521dtVZwknfWD+gh7jSig7Q7i6nh2ZD1vdU0Lz6i529RWs/feidtn3JyBjX1uVg4E+es7J69ErSNk6QJ3mb/W+8jroPEydu6CHNPnTkb8EUWLD48Dnn4K3/gyHWhnSUeY/6L7EmKWZB2LfpRiUQSXz/U29ssSrxjiLt8Cj/hnnNFcTfyg++eKVoeMTzT5Grd0IuE4nxTXk1/1qmoa+uZ5g5kpz3Elfh/tYe+lscHhsMwGoB511Wddjc0uKkOaXdwqA9miLytkWje14R6ogdD1OwLfLGdkWii9jWakE9r0LpwGV2qlVsDPewjyX18h7/g9pgOcipvK5CFH+qO6ll5iq2eekyfsB57rI/g/hy5nS7JXWkDwxvET43A8gCq4YdElEXACcwGl4gvXX9DGgyB3kgEsB3UzeTqhib3z6Ry5Dk7GW41jFVkK6kDFpk/ZOkwpRWmSOTpEXVp0v+QuqQNL/TXIi2eyVJs95cbyfVtkutlDknVJZYL46JqCTdLvpk2K5wfUT6idHdXp/+NyiaHSxBkX3IXXD4SahubK5FKSQSvUyxxR4sMlSWWVV1QulDnhpNnyYikNdThx/JqfuFxnVS7qJ/+XdXh18F2kbfV0+hDnQyttr8dkmRluoPRIC/qbSkP+2StXERlSTKjztZQG+Pkn925xXYVKV9QDNP1WZPzBsxhdXR3l6RJgAy/TWoj72OawJexj2LmQHHSy9hh+OhTVFZnuy7Wb/IIpKBHUKTrn/L7JCMYHHIDkPp9giwpCuvciWld6aK5ggRq6OzgMBN2mhRlpWHE334L8TwOmY1ZGiJp1HXn6kFmCGvnXZRN/7BQb0ZoUweEYYl3SVE9EAGxlTdsKG2U/QuzWH2LulzLJNyvLv33m1v09zyj1qvJeo4X2c7VQxry7TgRKGzY1FsS0VC5KMc29+ocBkbf2nu8fYl1TymHDjjvFj/J1q6XrhsKbNzGiaXCrhQAsd2SdrdZhlVa1U0eFugrCyLvMAdn22veO6TqdgdzM+49xX7zMMLmWAponb2gf1EAo+1OZ9RUpwCVQ4DgpMMAgW2HQt5Ax3vD9KiuHsiutL3Re4ni9uUJYEyqGuDgjCoIozSrZf28I1F4F3kpSZkxFIMDEUuFXgMgTl28zndJrOhjUy7vJFsM95KDse1mc9oA97ApAjvHlgj94optu3SJqrrISDp8JJtgBgTsIgwhdFUCZt/lp6jYXORJVpUfk7KSncoCcJLey+GAMSiAbUfSX4I0WoQEYGAV4mDkyxAPaH2dmsoWJE+UwkOB5JcCiXfLpJC2hP8epSmqVMzDQID9hiGETkvAvLzJAwvYO5Pz7TbP3nQIDo5keVtGXodAe5RZjt9M3A7hdh6zNKRznYdpRe9zCNUOfOlSw6f0BTzbLIn0c4TTPXnX6Jpf52rot8kb+nuyI+ZqlGru/wd6xe8hz5DWDxVIR/RP/EzP7F5ea6cbr72tA9x15YoEu4kvtzaX2Cvsis5xkGIX2Qu3so6yUF7hM4LLIcAJdofrYH8oBKQjlXfknMFB1G+BXrDrHzz0Pavbh1tV45YgtAtOtmmT+uqcxPpdmt8PZ93WIk1ql28oHOuIh+s6M0uMGRk8MQOsBVTuku4wgixF0fpmBBzZCSoXWAkE8majtisePEQQHNYClWYNdvX1U5Td1+SFsZWEjFo/C9ME41FHekMf2phBsdg2ar1PnGq5l+qadq1+hWLCKe/uJ9dbPXGi+/Is63rtHatDnpqaXuPmmxDbVZd8dZ3SDPYQdofvGEhmb/+MXHikbbLYiadzeM7EIc6oe8HEoebhlfgwMW5hE+NN5A6g7B34hMLJquJd/ErTy7Sr/cIs7WaDdYRi+8gUgmYfC2HrwBiWNXX3KDCgf2NOcmkHRxD7Kwbs+qvuqAgN9JcHkndbgAx3YYm6kBDq7hKFch3bNqZ3vpbp96jsBhju9i935WTO2yrKCzn6Sy5ODDhEZHzLSUiiNdc19cs3DJp1cNrQJV8uaykTir+GbqkjbEijNxQsEGbDggjcI4MLwy4BWOXgBpC31UQfeSeBCORXDuRjs91oXuzltYqgL+q5+4LcnNnWAX5SpaN2XRspSKK3NHGI9lpSGpEo0ab2YYkawtKAEGmp4EM5cSkgf3XedcZLnzc4Dgpd3lZDpdUodDJbIS5JdM8MBLgLZOsKG8KUvSy79rkr32siIYzMHk+oKe7xtcuOnd/ssB56BJODSlsScG61uOg62AGBvWt+qrrWAvivJZ7LyGEFkbfVEOhDkde7id9qMA0Rsb4rPp9z/Mt4RcSLUEFWGaKKg+znfsa15t8rP/Mow3JtfkMDcdqcKoO1OQ3gr827Tnip9AbHQa8rlMxPr41/Shmf9pB0eL/tKA54ttHKpb/qAZ0okG5yUj1f8KbMPVyR1H5DoVjHYUQfrTjL9Z85QyMJpZ2yikuZtMcIsik1sTcj4MiqULmwUoJAXqvl2BV3fj08pPNThzM2z8TM3+xpo7pmj90MFcO6VIjmIvFq/47hh6sKLjvEDwYZmVvg27ASQ4FvQiG4qLMQYQPfBvxgIJlYKu+ge+AbZbjAVryTecRb80obKmiM3oBdFaMnBZJTOHCMXp6m33KSZbpNr+Ng5BEU5Rse0Tq2JqRXR1n55OAHlM/+gBOOoOMIcUPDU2FzcjAxVk4B6z37fdc85r1FcbDv1Xw48cnGRNZue4/+/K4LWPEZwBRvVRDCSsWwE5EWhhU9qggUN7rcOoSbE1dJXnh/PSFkgDfQKc56wktDHHSDvK1Qe/9l9EObj7rbSUxu2Afbe1895E/n2UdsSFwEeIu32dx+aFLPXedOIdWTnSoss9v33fm67bIabQ3tsJgCUN+776xGHSvX84YLEaTPoYXKSYcDqUOD5ECh8B20vLytUPEINNn9XyOb6pVBk6QiXolxZclFlFl0bbtPkFkPoa1kNgzyzWooTQWvLZ8qt66XPlAhPiiG6RXDPEkE+1luR3eXzJBirW+ys4lmbPmc8HLDffM0dRTHqCznafCCJFXD1qXRezKh0+or14eJkozzatYuNbmTru3Md2ul2ubXbv85qE6F0BACzZTosWnL+zW2ZsBruSnDvuHgfV2GTcVteSjdkDfMqyx9R8Jgu4heyGFRUGSfUfWQb6bUvBKOOa6LAmXxyywJX/vGLun3gFCcbMnDoRcF/tVosde//un1q6s4IkT8k7MshH0fZ3gnaB61clXfVuQ91bMsTq8JWiNi/cF6WExjJ8/zNNa8FIvnpnn9bq4RMo3OM9JO68wzwq6xWUeGG7IQZfvGGOWIV5GEmAhReorQ1DSVtzw1geUtT03tDv/06dEaHpqcSXtZn6yV4wLh2XkXpVGT0HrKsTTK6xKPZkM55SZsarIm8E4CDwJtjraTTg39ls13VCAsRwFyNz+g+DGvq6MKC8ZtXaE549eExsv/tZ3hgYjOlgp1IBnsPd9hw7ObnAbNSR7ZpZH3eRqRP8a8xRqDziyFMREM008k6fIs+wqBNtOFdEblI9rIpmTSER7/+DH9YxykoZPnXVI0npHPeVY9zNzmf6NoenrS8vU+wQtA9R7djk+Qup+XD6i6d7Y/5ukcL8qKDc/EmFTD76LscZYdPdfmLCqGbvPseM7mhvdV5mvy7DaawbjoVtPGABzuIk4t9zXe4RTJvxpNc11EWRk1icJneDpZ1vQsIiNr/BKVdTo92Y+jHTkZmJfgYqMzjfaqvh1s9HmHfFEX8UNUojlPJC6ixDU8q3fptH6H6eela444HMgrdN2DhLO7wd+jFFU/1bM+ip3wJSJniZQHwegc5mNUfkFPnVvqS04O2mPm9UjnEMk4Rrvq+iHB/Yvw5yZc4WOUbc5/OOysfJ5Db18alr+FDpULp8cgkGuckd9LynDXnIOfek/a1zK6R6qnZSHAm+7Ie+ysHEoId1SA2t7Q+pDcNbtE7SAgwJuvJdp8T6oHYTB6aGFQBlWcnt+Wv1Y8FIuTwRXBj3BT5U49+yJNbToUS3o2FsE9o8rtH9veILRFG1pDnkifWIRex5YyhRbY6PFtP5YgK6z85mRfKpKdLQFeqmaKp3gXvMMsfxQcBJAoPd/nwG3f0xboCRRrXtKWU9Y4zAjW2NZRRz2aEtTshzgkeVvD6bann7tVv77O8mmeoBkP2ExNgbGGxhroPwpCrYa2Fe5OWKewZIw6zmoDJ0mHzRrX+EII20HM5W319PKVUGapD4HsW5TW051JT6RQemqam+VjDY1F3n8U5FINbatQOItwjt2F0YBg49NL4fgrmYNikbfVew8prQBlnTPTTiHiHycO2Dgr+84exVXyIwrgk+sRHuf1bibX/iUmxi4hbrI5fJdDa/Pc87lCGdlwzzGytql5hvUZ7wqp+96TtXNWjtzROFGX9jpP5CmzX5KN/GPCAm46GliTKsdzI6sjjkgCKl2VZfD+l4oc83HSF4sOKTlNNuOYSN6XfoLY+Rfts4i+aP5WR93ovTYdWVJdFEk8eWT20NDkgdhzjGaWkUwdON77NlobcWqSsa1NTbtFgn3nDfJ9nz9laR5tAqQ3PSt7ZMHM+E9YQLIS9Xgtt0BEW39Hyf2DxdZFc0WyzjYpwmZWNH04R6vgj5u3/qaSJpQmP1DxQmxGS9p2t+toDOFO7Y/KMo9JpPimN53gw6KANpvMDtXZeP7eZovzWfCUCzi/NTaPWx7TdGuAgjrWFSq61kP4W7lf3J5tpq3cLyGfbP5prVzfzHNUxrP5Ms/1933zMUZmlTtsjSYwi4cAxQ2Il3ASNiAewSsVEIDvIIMKvguUz6EleAgc3DVH95OpiR3Jc7nzJtIMyvg/KEgIigZUwQlqQwkc/vwrcByVyWgCHn710U8OurAPRBpCrw76T95WEBvkuojiR0zxmYLv++a+Fqot+2+/BHk+o7mlHXZj2zAocr0/0O8CHavPbmgZB0DyGkYSH2kblikP1qUhbkaFI3aQAZAGarJQXlGFNMoQGvBw3qCXyIZOQYIIXV6tC3NUoBWGkHIgk1dYWpzkgIsJ9tgWMZgOoqAQhTVlyYOtk0XjZ1r0U+eiYVq5INn58KysNMpQCJaJSurkZgzcJy95N89jtOW+u8tfw9h3R5ttkulvYv/BsTW3/Sd7TQHaecIQgk6WgIUL33a4k6HuYxCXGtbc2SX6J3kUwt2HzaA5LBnytoIsGcFMp1BrT5hojVbbneZFy03ze+o7/kXNOXGQE3W/DqjvXJtNLf+U6XThkVV0d/clnyNV38TrkDAplG4Lk1+3TTh3nbfqXDgPmtEC8bvW7+oxpggq8RVDEIBfFQTzdKbmjT/gGK9gftGLPKbDsqhSHgGWxYuo6LbUltugNnrGoSI9xSHuMwQ7YQsThLlMWkcskqjAPESCECfbxoWxWPZBFY/MLXFosVrqhoan3VpSMMC5JYcNmnpEaAhaS6RA+n477wFV8UtCK0AYkwxG3+UgQU0dkqsdlrU+r86gC1xci1EVpfn9GyXew/I4uT6HCX++Y5OPOa4VJGHmaZLigUFPtGnX0afzrBsl+NClUYiVyzOUTjKsZGRIoI0qiA8+GtWyPuxTcIHDaNXoTEmgwmJJFyUqv72BcqjOalGF9qAVbdXZfj5PfJQmkcpx8nsIT0a413rb6JVp8zl8RpskwiuK/64qT3NVpM2vgY8rlOpJ8vju0ipWeNLXX1v7rUew7axsV4KHMqvtq4sWtwOOCRad4MvNYaE5LAl+mwutDR92R2GJ7TSKUXWVFxWFq9EpbIEL1mu0xTSs0MeEOo4YULOltlcXsw16bpUL+RDwgXqVHpOEAM6vfIXwQQ8F7uMJuY6c3qBn3R4YyUHJOivZPxgaabaeckLc8sHhRrDO/FMaejcjT5SABUcXy0wzBiYEk3+LiiTKxrecjvPtbZJFnptbPfKDUMjbunqsA8iEuo0PeOWbvJHPUVbfRTGZxILkVddefwjTrOUhkhpZ25FQ2L4nm3DIPobNJhCVqKGcbXyoIVryg0VtFq1alsl9hjaUY0BlHwcKevTIRdAfMpLsMLYpIsomV4W3Eb2XJ8FYacaPYU5om03EeV2d3zVI29CMcGs9m2ZCvt4rlr8bFodgC5hWlXnJjeuHPOQ0aFt+SmJQy2WwrucltBS7DpbFYTViuqrLsJn6Xp4odhAOedvJ+2F58fKGRnSw/uRtabZEf5xkR0T+f5HWRaTS89M0bZbNyrBtI7/Xp5zYLjM1FsDJdla+R3cR9UaZ2RLmuxUMv/87iL28rUA2W4cm2MHoNXqu8KftbvoH8kha2n/WeL3wTtRGLYlZVeRpiCjNhaJNeBGysKBszSZnW4nnOZfxgN5j0zq2I1Qf0hkMlUfcnIkrfX9gjRup5tXSQFlf5j20QxLCvRhwMTmsIgariO740DHqYRVHo7OmPQ16DBnOFFz90SOH7+R5lxfV56iJV57gjpjxmtSdcV4h9RENAG+yQplUkyllo7pBdfHYUECtPCI96GdnzRnq/MNzDxDE0ptSlCT2npUYemoSi6ELfaAFUEcARWVDjaLCMIFeCa5TDvrEXNTHqfW/2rECSzLQZe61BxULk0pOYTf/wGJEX3YPHmvdJu2boaGzEmPCChLFAbLwhzOmm+1vCN/QpyR7JHdXl3wExcESXsMSZraSmyyCIV3f3R2H0P7vBu1hOVNpPpBuP8VaFuqNuMNSBrRzWMr+jZYy0V2+Fr+74TmEmfPeaUHrHydyWLwa1fKmR3BYphRy29HI+/G9r+NrUlSubmd1IqKa8LD2uoiyMqF0rWu3/VKgGK+PGkd1M/G+Cq/FojEEwqysZIydQE59Xez4IcruEbZrZ/TfD/MIKvee028osFF1i6WCYgZAvNTudYKKLuzcedMw4DioXnlbgSyToEmcCUs5Bon7BvtMmS3rOErjuhW9z6h6yIcRNiI4cCsA5p1BkFAUFPyh1RsWdBR+GEJQABKwkOHQYxMDFNTNC0mUjgjhpaEaRed2pbE1DHsEB+0kbyvQyu8sP8NqBh9P0YsdewWRLREOlrhir1Ojlo/ICzWerEhQHJhR3lYwV9FReltvZ48Ln9Ei/+2Pf5zkmnH3Z4CWw5jNo9zAhjNULqxLIJCfEz56IT673nqwVgpd/fINg+igGhScSRPq6qWs0DbUSaxtQBsGNwvsC5XYO6+zTeNxbR4uO8lIZwM8Db5N8ERc583EBLxreFmnSB60Rk/jzQhKeTFBCNFPCYP5PTKBsZx03Ggk0qRC2cv0WPsgxwpmbmfKOzM9RkPqz2C6ci2f78jIlQdRv03SMJPFbjIv6RSXRro5h5PsD1JzM4BRCfaFUjG5vgji/UopCf11XtuZl/pGTAelIG8rUIzpiqPzA16tvM8wrY8fCI97B7svYwSwggFaARIQ8IFCCM5LBRw/oPgxr72uAnWPawioDmpgbWpgL++LNnxP4DtPayi8Z+V19HzyjChquKLCiI4xG9znhbdLZGW3YwWahcmBM5kyFpSQ4vInDHsDqMRRWZvWEZy0xhW93LdwKwF1+iFIUnscz1Fs4uTnUyVhPIQGzrxzBNTO3DqLtzitlZ2bKwrFdUH4uvNzub+XPbx8CiI8aC15WzzJpnn52jbEwespL/l+jRur5EFEkIduxMr0E4lmdYBHEw0rBpWzcAJ2kCwFE7/EKWLTlDpKA0F0gYok33inDSJnKw2+0g/RVYWnnYhkiODQUHuL93iRD7DGn2VJlUTpmjUZ3UUjLXbD1pCrLgZQq69YaNsoLen6P7da5rds1vrcSS03TI8/fcpdor9wrXvigKewHLSxvC2KTCEcK6Eeh9U/nD1R1p6JFBNHZlCUKZgbAX6UXgWYoJdUsEFfOaUbgh44hcqVvQ3zULZAxhDqhMAfVIq8rTZAB/foKS82Cgme6IxeFzw3TatOgTnGfOy3FB6WQLXrPb//hH6g1PuSxgPJHYbKknpXaubQNH3rgc6ydkebTYEbCyBojgaFLmLwHqnvFf76xzDxeegOFQUqZmks6FPuRDsEed3Est2PVbXjLuhAa0MAcn0tURHoikMQI0lmHCmNooDG0N1dkiaRU0TDULccfx7WFHlbnXr0PqcO47lxdCPJdzO98od2Aj173AxQ1DaALxT3AAKE5wYgvURxY4fax/IRrwZG92ZAcuB4eVsz3n+ZaGFS3o+1dt2ebdKAEqe9aTrwqPSmKQwBbMJBMC8pxMaRhxTmb4b6BwH8mQXwKq1VORsmajVYBFeU3dd422E3A8Yy1DBGEntlqM+32zx7w2M6CNXUQoVH9KHI6938zI1bnr/RUJdVpk+SYZ4w5QFtUZP66jZ1Eb2mfvmGQXOQO3lbDaECcO4skYR+0vBbmIuiU3J/g9vFdmtimdt/Dtw+OR/aJjJTuiynsvHKUHeYm+25xskbRoJPc7xDwh3H/x6lKXEte9+9+piX1QzvAn/K7/NgeTXCXZr4WG3Td/mGsqsmO6kgl0awEPaPyH5B1VNePE7OMhdFgrXdS6MXjuuiQFnsbUV2OE+e4ybB3SVWNc6oFRmipI3A1/nICG+UtaiLfTpg8YqftoZ9CixxZvQj48AlQ2Kg1GNhQf1uLA7d8njutkdyWKblbWmW6WnyCvUTc4xRqBqfou3L5rDI6P7Jn1yXk3let219mXgB3+JJp55Im6o93+t8vNIiAlY+rCmFd9Cz6KA5dVvddpJtNG+4BbKxxgw9ZxXaloGs2wbrl3rbiZ1nWPo0WYQ4rO9Rlm/Ji+5jfpTgl9a4JsmV/2GGm9tgnbZs4PCEtwABd8Hd24Cem+EOy2Gxlbf1ExxnTOmRifFiQNIgReWj+/VAUhtzpIjrwJiKaaXI5X9MfFlnWYAnWz9H8UOSoaUYHfNKqNX7NMkaS0eCTdOTOo4R2jjWPikKZWqvMB4J/Oc9CWy5QHh3OF5xMAtr7Orq4zxdjRvpvplme3i/LKqRG7YStWvWwIp7Z10Fv7RfTDcDqNGD/pS3pd1FT6KjdG9qhQrQzbOT5x0RUXVisUB7j/ZSi7SVP63orgPoz8wL6lGBNgs9lp6xwN7i2p1njBp37dwX9FzhtdlhFTkrPyYbLAveu6s6w0tEt7CHiyQDLcep1Tl/kdRY/zup87/VqEabE6yb06OqwgR0vNzfrbLlGxDhQcfL26IIFuLpEzwJxE3IJGskPMrNiABqq5CiMSXBdDrb9cknzbo50b22z1jjaVaZqVomdOqmVEcqxwTzoFzDueY1oILBqoO3PbaiqWE7lqGa0XA6aJsR9VVsB0WhtBgTU0s3JOqr4YjoGl67CqafQRafw5KjUN6cNem22hB2mn9zcp3P3+Yl2qUvQRrWbImOJ2/iXRxP3sZVffsPFKsSUgQyD0hczPRRMSEP766wgF4XiXfqJIzGzW3XKPA4zmv/NG24E5vPUVZHafoSbs81ri7wnbnA6xy/2zJfGU0HRJNcN6IbFhgcCAOjWqdZQK/lme2W+/pM4zks0BoxVW0jpnnVqot40O2e3B62AqI65x/hRV7wpwi24RclnskwBLLecpelJk3MRC23Tsyr0vsZa/IoNrqLsFTjVbWRBSpcI3AemONou4uS+8xHX/U4DrpK3pZGW0x1gUlrY07UcCCbc8kIssABd91UXOdHsaWCsHhYtBHIa7TF65PTfc5BpDlUB8l2luyJfJk/8wb/M4ZsHuWY/nj1U1RWbXMFCqGftW6F1rZvE5Eoxxcm2dXP5WQIdbE+5P4+9DLh4y6QOgl+dTgdGmv/5lX7d21t4wXuC3oqPyGinzFfU6+tua9zMMbDcidvC6bYhzoZ+tD+dhHJ+fdsYfRJWOenfZCviOR7XjxiEk6cQeQ0L+qtpyg2OMo3AqqDDCpYJKTkhUqw3cyg/7MJuyRe6VMAykyMAv+CeRnlUIKLWgHq99Z8kfzAtBjifV0FlsVzkFaFtIZQ6ISHQknqdR4Kk3578Ydp7rCSVzbn9y+RBzejAI9idkk4370c1dWDf2QphfASxckuoS4BLGPSSHUozcagHmXVyg0LPypSBZigSVWwtvFFo+iY9J6GlvZ9BNL1nIL0WgI+o6jEirh9F9Drjj+D6bAGyNvSOAknerhg4XcTLgnpDC/uOt/2t754bism77HGy0q3zY0gKQOyg7AchOVnEpaz7S4vyMPnd4lTOkOm/kE41iYcp3m6Cfb0gfX93bS5MBDm5n4YTEGuq7U835iRaION68j78OAx2XluDaNH5IehTbxynvkfX2CpO01QuiF/zZB1pWez4zy7S+7rgr0cNNWR1skz1l703E+Yty6tt9mQbWTi1i5RiRX0WXanOi4O01R3zQ8jd0qg4H4/nVmzpFcZ5VDCfk8B6ncb/SWL3dPMEO7sE+W9oVAdVmmFWAfJNdPyhzrj9q/TONiM0txMtOA3GTafq4W2AQ3NP0al6uLmH8JlkDuzTNjR1ur7NPWa0TRGFnBhMddtprLYzXtoHm3/TJTle7RLc9e31nkUB40mb6tblXxV2jJCHSo93XxGzciUITYsBuH4gbIU66PvwzSkzdIdohHysOB1EWXlNmnSr4SYCgjnMJImBUGjlGAwh51z69bSJCIIMydX9W3rJ5i8JeMoykCM0LRncNgZcHBhohOJPCY/0Gcq1a/HrRSXuy2KtNmdrxDYWfEL880APO6rZDDCrkoK6HfZ79nP4cnUPxge8rZW7PCcKPshUdvkV0eq6fV2s0QEGqvWivyRYMKaNjnR+2zdotgLr2c0eRjjslUIIb3Bfn4xkSkxM86x6cR/knV2Du9rz46tz3cWN+yMe4jzu7sSeV7kba43+KF4F1Xxw1XyL8+DhQusNdpXozzlLKB5dZxvd03klY1BGizbxt+T3VERP4QIYCe1xtcE/Y270dqC81N4GXh8NgqtJRjM488YbVKPvxxKYpuG9/i/i+LHswzLS/zoGZ9/jJVimt+/kWA8WK7ytoIE/uI/N3Xsr6kCRfwuEVsvYT0wwl4HK0igtoL9c1DNhFmNZKijH0gHajyOHt4vYhSvDndRTKS48Lhe3usSCN1BkcjbWmYb+i1BT4H8hmuLVsOMiO7z4iUAL/OoDnx84OPZ+LhT7gHYmMN04OIDF8/GxZ3PazCCnJmYRXTgYXlbw67i10C7k9/88Ey/4hd5WWIbPPXnMh7Vgc/WymcWV32y+hLFTeCCPV9sSb6sHsGBG+RtBT1ocjz0ITPl6y4d7a2J/bzfkw3etP89zzRNBXn5J6mUr+YGOgBc37uzq0n0p/IqNS/Bgj4lwtCktFU/Ny1/j34koFhwoEMwXu/lsAg9dOqI5KBX5W01lPKOFCpwY4TeATzWzaFqoMANzdBJ8r7JT1Ln0Y1XD80xFnVaM7UyRkUXXkj+Vh1bBjpuX5/yf4fuk+xDkTscfoqz93TyvIuyjf/4vuSneZrmT9540NP3JNv4IzqLdY8PhuD/6kUpZb+GaISM5DhPlc+yhhnOx2pLvVU2WTPHZXmcRqXmhmSIXKn7YCe15k8YM4k/YlOZUmZmUr2l3V51iq7inMR1n5WnaXRfDqM1spdI/fLNgCWYgYS5AqvV9IUEjlGalZ2Az2h7i4rhOIW8F/AtSmv8xy/CZLGww6FtBy/eeGLgB39NB/67SOuWqgpKf8rvkzhKCanOd8TGItLvQXMA34LUP8o2psQ/H8n+qwMdu7n4lpTJbZI2hqkTFRUHECPuBSl62lQzI+kVior4oY3qK0fqamp1o71oUvD1gqCp0z8C2wvCL+4zeJxnm6Q1MUNP4IB6wfnDZo/p9OFNXl3cdkap4eR9LSlg3ay9j7Z4kjd+0/a3OmpWFHIztsib3OgBZw7AvuDkXe2SLKPWBx198fqPDdzMU7GhHwl66rYVgcj7DtOW5MnkUS9I23dRgYwJ2z5BY6HSmoGaC0aL/6oN9X53L5UQSUNixT9oVTVf448uwvg9SlNUXeRlhQl/iaIyjBrt7KLyDYB/Setis00yU565wAbzQ1QimTAC2jehllqNKUh0NWXr/K7pCyZYgo00ttIfdEx5m4ws/0cH7jiNYlRd5UXVbMLc2aK1K9406Mo3NNYFmeFjUpXEQWXKDw3UEQWv4YdP0S1KaXgNR7B+II8dQkPf/ljnY0KyCQSdOhr1khu1BxQ/3ubPqLTbrpnO35caa7AkviSpYmRzaDQf1wkqLjAmdBylcd1u5j+j6iHfBLR75I0sufcgbhpjfYuKGGVVlMpUlqjN/4Gp1abLkKzrRvNzXBcFyuKXyxwrV7JM1WkIk2nMlwk1sOCsNN34nGx2ORbg97S5qdtf0BW/7kwF6Sh9il7KpjLTmkYfUtWotlwUIrmCMj6A7T2xBF35hka65PYwL7aR8Qb/Y3L/YLx+5aP9+58uGxHuSV1CHbx7/JTHUaDdOvfIrKylJfcoaX5rOjfkpjQ28BBRFcZGZxtF6bE4qTJ0+KtAOtuavKUlN+gJufR40bxMazZNn3FHkh3u7imWPDJAprKRnByVZR4nDQkHWSPJcNvTjktUNr76m/7BG67/J9nmVevDV9YaPf4vQzoWqMLrV+2IMDGxJvvr6/9LGL5pg0MkBtVgPwSukV/ZMeFGzrM27/+ro2ZfS/zwZRxtxNMSTNEN+6UTGrJBOs6zErMHXp7EQIgki/G8pTZD4ZAYxlOQTg7N8SXv0Q5lJBTCZg5N+kE/jyT2Z2iWI6aOdn95SzGrAQ8n/2pClpu+GTIwWEXKvTS0NevCTe0f3yrHMRfTKudtLzi2d1ORQd2M8SPkzy4taCllXYO6EA/T1SZlLrueAiIAQuolzompLYg5A3dbEMykNzS6xVj9fVI2SSJujnZ48kjgQ3fXL0FyHldVgpi7h7fRy8o2ALbsryhq2rAgzWe0SaI2RdJNGzgoJYgICpGBgrKhBIAcGL8C+S9v3oi7BSd5lHdlBvGTE9mk8S70cyk5aztP8l90c6Se7RFQzkiJuFLo2YhCvCwTiR2ZjYVE4po03dZYAQM1vzpXhgEb0eATMBODXsZSfW9n4CmoP7NyFkRvkw7Q9Rbjsi6+hRtNwx0yVpBXgbiNh7ZhOkVLUl0GNLH8Xk0/kBk4Vj9vxlzbP36+CMteVdjya65jZDG6OSY2I7YCZTwEQkOMygDacCncAmSxdl1dHXsqRzADZyrnyGhn1VZZWov2XT+/Oy+S+yTTqVAeXqE/HdSmgN2cJcOt05q+zKf2ZLS2YK++6uJshrVD8gMVL016Mh0X0MCBGYxBDXAX08/JWQzqzYz8BdHZpHm63tKc9a7ONnhlr9D2qKqK5LauUJsh92Ys0TGcCQ4FH0qrOzCoUVcgjydffX1LtsMI55MFGxYw6dVYaz0C0g2l157GnMjWMxIEL87n2pNz+x7wODyWJfgankVzXh4yv6yGne0ZeT4WdmHekKaFvCuL8J4D162E3/ozm5vh8EbDBEIFBbcZHgiZNWNx4LQaRSkdxXxcKp0vo21WV2c1XGqoE3n4iXl0j5dy2RgWYND9VaJ0nukb+g+tx15WUeW3p/E7uO6lTUIOfEVbq2Fh7Yhm9OTr5tPIn0/VWxVnGypfqM5M/LzHilg1joUYeA8Vcvc0jvYMioOD2LMDsWFJHusS7n1JH2ZgIQlNLdz5i7MNe+Kom2b2kCwsC7G4AUZSHo6GZyewPzMyFUhrk/aZiouHM9+Mb3fJOEAEVQUr2/AWgBlgLDOmdRn7uyQlrwT2DWi7ycIHpwKH3pwUonQ5UIMkQSHxk8MQdP3lK6jo0cG6kEVoBlrEJGRf3pjSjWIGjaWbL6P1kHpFaxF9RXLKkMtz+ntCAiTElz2QDUOKiPfqMpC0+zOwoHROTNpe/OrP0Ht94KMIGpz73AIcwxlg8o7MyUh7Gpg49L/L5WTBUXyN4IwlNLAwf8n6MyebyYhu5npg6y7KdMf5tnkhduAQFTvwwDJW6+BsuU1ADzCajJPXsY7KRjATa8rmx6T5vs7SjtzLRjg+onR3V6cZ2e8wBTqfrqa6wr0r1HTw8epal7t7u9GtjqvtBjaf/9dwni1cwW3Fxdj/NC/qbSO02o22CAqx9QBlw8cAaqttdZCVXt6JGfhLTty92A833b/Od0lsyEcsrJSRGjBrTuKQL8RKcC/m4iWYwPvDTDfN/9sXQZSTTQFK2ciag2ikAPtQfVvd0inr/1yMB8yHSdNjrTUosZZrDJRMO+Qp1FeLWcZ8K+U7oOvzKjxmPoz5bgXmF8UveiuJGnB4E4xCLuM+kK9XwoKSMcxqw4nzY9J8U2F5VvyWV+hm5CEtw7DwSoYkoE5MybUh48t1umg0o5iTM+G5MtaTi3pqvqCnsrlNoD1zEyAhpuyBbPhRRLxXZ27S7s/AgtI5MWl78TO3off6ExIRNDj3LX0mIu/InIy0p2duQ/+Nz9ykNYIz1krO3LT9mZPNfoozNzKa/phm4BAVO/DAMlZzOHMD0QOMJuPkdayjshHMxJqy+TFpvq+z3JlbnqZ6K46BAk/QcpI33eK0jEG4V5Yb2PUZWA2cg72w2EjPj7LyidzTyIXn5lmmoOBkfNaC2HIbjRg6hAUYeB28BnR8Jm4D5sJopcXwi/Ia3lk3afCbJ4kpjlGxh6SOjAdpcFtOlDUl4UqY3dfBm5qhzMSnmrkz5dm21uL3Lsi4ugTh5hH3UCVd4njjZOyGze1l9L1qJDOwr8n8mXRj8YNi1UDINycOJt9m5uKmSQUnN2PZK26mR7QwR9PzacPVpN5inH3yXKEii9KjunogGNvXwLrVRqugjWpDPK6qaMPsZh3YK91tNaQZWN5qjvdCmzevx5pegoSAIY7unqQ1Z10Qsfndx0AeUFUvZmAuFXVNmmdrLsxRWnXJggXkov3UdHDfZ+O6vdRd/VNAX8mjnh+TkrydezO8KSTjEmUt1StFdAWXF4vgBqF8yJJnkZbnUqOhzMC0RnNo0o++zrp4uNV8VvzUivFc3Nu2BrAuqLNXyrfMIJZiWmbeTDrRVFh2cW+OvtQ8ysFJl3fbQ3Me7/6woKTnc63w4lzsE7PpkrwJkBMw3D6mcZP2fla228OUbR+Su+o4IocMuMsPUYk235PqYeQgGbto6kFs2Vex4UpdMzK1CHF/uAgiw17NwHuG02DEiSCGxRmTMSIGFtLxC1hLxZSuVqO6QYA9ZVKwvA41GsqMPK2cQ5N+9HXWxcNfaRGzY2Sm6mzczLa6P4ao+WCWYmpwPk06w1Rc1mz9Qu58GOyRRjipyfrF8v4Oj3d/WFPS87mMVXEu1r9HukRPWHwucoyg7OVH63tXVYLYEIC3YUhlc3vlpTcZyQzcajJ/e+HBhwZiZghoaxqrPXD/Y9OQm8B45Wu9ekh2TSS6mkgsGPiabQdh9ZAti3V/lhe44zPIKzwP619c+n63e+aeVXR8wUCrmM7WFwc3AKUhl3D1elgQHMKMnAjOkUn7A4JlwwtIN3bGESscdMBgAx6zT75uL6tZ1pO5bGcZjU15areCyJXvUYp1gbEVDYNDzMVA2jCZpIm9spnVY5iBP9XztBd2MjsEtdEHwE7IkwFs7QBcNbdBp6Dx+q26S1TVRXaJ/lkjkwyUMDi866Eg7RwEYBN7pebUY5jFKaCap71Qc2OnDc07WYXgz7KEtPOsNmB50+xxVFTUe9LKI2hFHXgzxoLbbcjkTcnDJOTPPYdbHgx6NstuSzsVJr1Ywbv2wki0y4a0xuRMuJ/rh3YYS/DrXq4iwih00WOyCpNz6j6GlOkGsQSb7mGAGZvBXsehILT2fQPnRw32kjGVI5iBK5VztEcsebVDcXLXXW08qqoiua0rZMqi6toKloUrOrCwpgd7yNJmI5qPxc3meB9YHh7J+Y78eSPhSOnqbo8LNDAUaKyMDYfuQEdpRmK5vKh4DHcOi8WdN4wOUEAMK11IlAzuptvV5F1syVF2y1jWYNFfXuL8x774imXCN+7y1+JZWgr7Zfk6upf7UQFYhdw4SAiDWW6C4eJwT3x3OL9FRRJl1TAtx/n2NskawBssMckPVLxcJ1tFSkw7PArCKVA40NS4Q9AVc3rgk3lhHTs6n16wnVOTntH1lhZ+1fgMN3QGKFbB8Xu8v7MY1jpEYw93egaj+luNSzBnfM0SL6mg8axCNJgOAfLBDHzJxQDq6Do4HppTk57R9dbG+64rgIPaD8DQP5OCX49W32NVzu2zyis07DL0zjoLHAo2B6o7cLpRJ+Rcv2KnnMMA5xMFm7m3EIvVuN94f4aCZ+0YVIHJXFb85UTVDQNpkYvsWuXGYMCLSY8BT7jI0IhlaWmSLZzGS40WwTLm1M+xwhiPbnlL66dYW/jBfYvSehihwK2WrKlEZiElDZ4AoqLuj1xsdFK7WukxGvBykmTEHxZSxaNYWrisHFGm3iaXM5tV+Y0Wdg7ttwfoff6UpXm0MXhJVQQFs8p2UFaZZEXMy7x0Ke/IDPwkp69J48s/ajk4jKLtLkUDH+h0CQseWFFxyKHzTgm/hldTcF9mVFQwpU06wNZcjMOuE1TgUcfI7FkhGBzisAHShsck6C0fDmr4bGlzTz2UGVhUPVUmHVjFO0HjMHQnKALkBGy5j+ci0t7PyoT7e/Yx2g6GZ3hCBcUKLLXADJZisZ19YkvdIOZbxaXztQ9MSpvViqBDFgxiSCdO5NDKthhBQw1bjEUUPxrssCBg6egbOOvh89ht9lnLi6FqCDOIoGp+9mOP9jnK6rsoruoCFSYMCYKDLElBWjEl3MIy2391Z+ZgMCW994PFhseWjnZ4itDmOqdHJVf6mnqqF4xcXi2SNQNxnjlve9GpW51tSNRXmY46Qwvm5loAmlzWKbpCJqToIYNTYEAMDLwrO2n8t+GGfxxV6J7kjdJrZhEUzGzTQVllthExL6ON5R2ZQRPL6bsfWnjov1aSBMjgjLSAJF1ELyQX6WdUPeQbPQ1gcHAnTENabYPhJuYjCUHb4uxblyd2E0DBpG4DlFVGNxG3LQ2W34fIBzGDapLPjknjXZVFM0eTFC2moimBl2WPHkFt80dDjcy49NNub73OhqBVqe2gkweT9HZsAzPqb1Rsk7LEH0gPPkfN7NxQX5uH1eX63Kg6qN+hmlZ63qxpyJrmR7c6tWc3tjl8olbzbOQh5eouZ8A9oPgxr/ngJuGzXEcYIgDVBljXSoGYtg4dlwpjXJ0k2A5vjh2L5XybdEmovGBq4rguMCnuOzv65ixLCF7dWzCKWnCiYraCXa5iVWPmj6EE2TwbdWYOY9VkBoz6QdVbDRf2eb4FtjHlERkCE950em/LsHnI0tOIxvJK2XZ0C7C/br6NdnBc3cWkgUzrDzz3n/L7G+o34RmpACjqQDxPgdjwuaoVKEE31/nVcbbBeGZgZoO5M+kFV3UV7KtNWg0BT8Sw+5mlWjWCmXlzL3NTG3Ghjvssuc6V21YRs7oQo+0tgx3d3SUp/oJudG9nCJCgouuBrNScgNn8uYxAGyNpF+ZQUjLCGi2bC7+BdhSnnTvNKPgeBoeXzBR2eKoXTBC9ZfD98lpMPY5ZFk7VPNlotkUj76/wtoqExCbbqHg5eY4fouweXWJRO8Z7JZTFL/IDJl1N8KiJVLI6YdK2ArJu1/dpVKFxn2ZgQ+NZMHLiy9Gsg0GbP+w4k6kSniVZ9AvzItiZuZkQJLgF9zH1l2O7+AFt6hRdR+Vj73yiv8mZT1MRZEGxjhVD6pqEMmPTY1nd6m46ojl423A+TbpC11uMs/9WoxptTrZRkh5VVRQ/NH5UfeSkph7E12AVG87WtblMgKVhr2ZgTsM5MenJ8qGXisGQNSG6d+LNrurc7Nk3K+PQfkRr9BCZD2tZFuem1pjLu3prYPSbdmBxG25vwGdMBQ1TO7Iy2wTAwEyfV2c76EYyL8+C82XSBbreGjiVEj6axexUI02Y+RQy3SqYdEkqMaviZvWIFlPFwJya9IWqthh7n+ZFvb2qb8u4SNqkm9qTJHkViKEFaBtmVjRldcIZxObVd2YGBtQT38bvumDKieRHVKHPqCyJ+XJa5Fst3ynqwIknaHC7tBPyhuZnO4PezMB3BsQ36QVdby3Md53bst5YY1LGo5pZnO3EvszPdCLZTfow1lqM4c62u7yocNfuGo+EiQ9VXgViOQbahuMUzVh7TIOwnb5DM/CdnvgWrs4ku1/U2XnybM188ioQ8zHQNsynaGYZ5tN3aAbm0xN/75gPN5TmbbByzyZqnhAryBlvhLXnPaAdyNmjYvDl98e6oczGs/JZM+lCV2UxVn0XxY9n2VWVx492+2FdRYh1JXVsOFjb7F7FAJuOZgZmNp3PvdhoywajS/GoqTczT+9j3kfDsSzI0HuZBTKrzyq07eIOyZ+KlIgCLJwRkQazy4ooNgAedmb1Wq/lK4YwA2MqZsik9ab6PJx40iSpwHUqXAMVvZG9jYrq/PYfKK5IEXrGEx83Gj/KsrxqsPz5a4mO04LwQ/nX11VRi7YvQX2FqsFfsUvi8vWr9jud0pkUAOzIVY+eu+xJCQKxDOUvWlz4V4V3EBCarkiL4lMeR2nyL7Tppg/uFA+l79qnKLuvo3sYW1dm1jl0VZEbkZeobDhP2j0OToucTwcBIRbzdpgjHbNWaDDTyUg06Ol4bAgrGxCvQUbnd4GnncsAo+uciq2NebpP0gchGZP+aZCwaSlFRGzaRx2yPuwIxDTGchmhydONpEtjsRmqLkREimsI39GxLJd8GuRWIUm4GVIFLj07kZmWcVNTZqAgsY5HuMEfeMEDETEAet5EafIDFS/XyRbERpebkuhdnW3wrhqvsgpijUD2aIcEH3jiKpj3dHVMG1VqABZGP3c9z2lEGQAzVqMqFapF0l3aAsc63HMz7EhjncA6jwPRonyX5vcXeQni6su0SJpTbBmWodAMjdReGktNEEUyDGa9+FDk9U7ai6ZUi+gLeiplgtqX6dk6T9OjrHyScPNQaoRIhsLKkFBYKQCYFvXJM9YYWZQe1dUDMeXjxsyW21gqeG1jTc4YCGuXgMfQ2PhKzi/b0HlYeQGAeg32IbnD6q7YaLEDgObYVQjN6Cfj6aHQDM2XvAJHNxTqLVL0hDt9keNNX6milwhnYAo8JLutRMf2ZcZIZBSjy7XIvkdpiirVOGkI/QgvEV79sOD8s0aw4mYADMaaNwKP+UgxXhbGdFG/2qE4uetEfTA2FMs7XEE/BrBa98A0NB4FvGNj5s2YEq95X0VKqeaJEzNE/Buox/n2NskiGXH0tbQN0+9iQk2wj4u6jcKi66ZNmOB2Rzq+d26Cnn7C3q0h40ZcZ6DJTmgxDV0GSt1a3T1RCC7Pw0uPOu9b/w4Y6Hob32Qz2YBL5HB8aMgICXlyRo6mfQhINw10/nGQ7mxmcwO/kMYpZOgRYnMvyxZMOq+zzmzlk1aCRquYY9QWrZSDZdlTtUszlCtOZtxIcgFatmGCW7/JHXMvgftcOiWWOarG2a5G1yZr0/qHwV4Z9abP1wL2Y0yAox1UKt/ZUNlPNGi+Fgo0Q6F+S4AyhPWXUjp4GL3KfEBb1KjsW9g5yAAYuOTyQuKKa5IcaDeuza17iVtwSGRg0gnFvpcu1yMTrl/DvQOv0Fvg1uE0sL6AyzGwGQZecjJHr0Gq9yFQN8tAnwFzvU97LrHdRck9aPj0ZQaLdRPjfI22u1SiNTgQI3fSJ1RVqKBP/WXOJRHSzPGlww3c/9FafXTUN2znsRH2WtpGZV2g7yi5fwCnmwEwRfc+wVxbSkbNw2iRMiHHEEYuxFsn1y9ZrNI/Y7GBv42N7YN9bHwkphFSxXC5mEudUxqOcAF91LIoJYsjP6WUAnCmR5svKsQ8jPF2XoGTAzGwAQnYRnH6xkLoB17kJd5rpqkCJQ9jIKB9wA0smmM4jgGiMT5FhowOHuIQUtEk6qiDmzFmgaqjCD8YK/DBL3SOTkW9IRRqGLQ0HkKIrzFtog+GopsY4zb4iCaWWKaEpKNG9FSEoTXjAytJ6ccHu+ioB2OfmHRMwj5ZUIlIQ5Nq8uEa1IaoygWlKOhp0gBAWNn4vaksPolJn2CL5FXCy4etqgYRlAqBURBTiRWgIj00b8pR0So3F1HRmBQivQAo+XhEYIg2bBSNgjwAOoAoKnTOZCEXK/pIHhlRKBjdGEZQOUESnegJqOYkBhuPpCKJPJWSZDRgAiUf8oCpkXiEY+yVN5X40CoqzRlAKQW0fHDyShDFgHgwBeEUuKUcBiF1oBwTu3UzhISJRIMB5WMC4SFS8eFlCjrBOCE1PYa9heKtvrnzu/MiuU8yBWMJoNqZ52soWMqIkwR8M9GIjd2TE4iB04+GBvcmDYMMoAsXnxiKNtK4wBs6LlFKMqPq2sGbYFEQWBUPqae8UeOQ5QrEeIafla4Tw/bbYCa4KhYEYGsaUdySxFwLcrJOQ0wrMroQcErSzUq04RXwMaZXSjMRVjssoYqCYvLNjRnmqfdLfMN6JhNAzUelZzBHcs3KXbSj9oYN75bbxdI6egtWVlVlI7Ox6QZmsrQRyFhWYQ9EVz0fguB2A9Xzox8h52HL/l0XxUaDB5EPg4OESEPdL1DQg0c0sbU8PG7D3puRk4IF1I+D3Sd5k4VFBxBHvYXz8NzeUB2VO2opKL3bdARWuWHNXLAULoAqUiL7EORdkpJ8N8PdGAVVOFCD4bA1AtCHQzgTkbpbPmNSEwWVBFj9qPgqKjqNd5IMyCUghvSQbAIc6NXfZFKeKIlA8oEIsBBpqLtVCpqIqCY+LBoaVLoOASiDMShdhZb0mM81ODR5UaAfCXoyI4wAbDAmvk4AMgko56DWcb5tEkONVwRhQglw6gHx4N4KB0QKEEhKc3fD+bKZlo8o3d3VaUYWBaZAYUPramrNXQ0Cb6oaNqNQ6rJmHMg93C9VroIAlHx8IjBEMvrWq4JYALKJV7vxpqyeJByYZhgstJQo/R1eHVU4dHOQ5Ya+wCuhCQ2jGQEFKqFGpCcDjQSgAXMhOSRzdPehlZzRwhjNYzuWMDzR4oKJIVDUS2nQ18pVaoOCM5F1aiwhVAeFTsYhMIF9aPMtr9ANdX1fQR4O1GBIbA3vJUmGV0atUIt9f5FfubMQgeQDEWAh0lCpBRQ0EVFNvLMYGlQa0ACUwRiUJrMlPeazlYcmTXYWcmCDMZnsLCzJNPvOgrTcW+tjhgyYUAKcekA8uLfCAZECBJLS3GVnkaepUtGwAAoDnoYDHe1tthDVHoBBMbFiGfOc3LT5SuCh0yDqnlOQsuEPWVc0RKBRQUcKECUdSYDXtiYwu0mvSOd+gckhA1ePR1LLf9OowC2hm2QKPAOku4hgI6cpCK/3b0LVdLHQskBl8wYm3j+p2m4zCNrRsanjNtQmInxqejaNKGgKBrA70FWV0UjJpGYV5YM3qg+RWZOzSUFvsyYnZuQmw5DB+RIIJx8bBA5Rr09CpSATiGriM6W2TRW/cRC67qs4yJgGc/EElLHrZkwgKhJDXUE+LGU91fUOPi2YwV0PuA0oElR2hSQUJbv8aoZkbKEtx9fyy4QEbBsAqAczs6sINhsJKb14EI38jJBSKdRvCHlM85BAEWskApl0XhFXZE2IeWKI+rx8Nxe4xw9RiTbfk+qByrMnkkZXRT44TU2IbFROQQXVdIhl7BRq1wylS7wZUx7KaQhX0A8UrKein4VmUrcBkFI6R6Eo+ZWeUGNysrUsx8tUnpKwbEOT6z2S9VKj+ikQjZ4aIaUar0vBqdN4FKYpSQCk6VSao0p4+ZBU1SBKSbKMKoimbGFiOxZqWyuh+kp2g3UUG0u8rnPlcnewS9QqF04OQj4uFhC8JjimnlXdEGTxTCmYdJramzH5rZwILKB+DAy8iiR6Ew1GCUXyyqjsvHXukxNqPQo8oG4PzMF77ad5XBM7FZiUxEptLoGUjweuABFHyJysoJIE68Ram21VqmQgMNOhSNWNO3XmMAmoFNQaYwCEVK0vUAV4UWETZSsXLRDpTI55Ew0khdX7yE30kGXU/MzaiM8/rrmQrAJXrUDSWvDSJuRNVy5vcuRzXSgW+qCSSzmwxSBV0ulJv7lkVGhY4d6SwlqMS+Hs8qTYrLc6u0hi/b1DDlBxDA3Bh42InpdE8OsABiTTVNQOVl1fcX1T+vKBnsCaNqcluOq5hxvZIw2AeDugUcinPTZQKaifvlApCIcOQPsxM6YILifqNzxshUeJzZW71RSeUcyUHTGeUwlTeSTj6f5tX1iQThkDpiUBDe2diYfGJddRbf9DUUXx8Ipp9iJjFFoKmGJSUFr9/Ix+Eoy7MF+mJFWX9Ou3SW0vquhX8innZFYjStUR9pUhp+lgUHhRhca00MQwXQBmh3u1aaopcpAQd7FwkAUnOs/L9cArUTd8kYK8RtW1JDDBoiA7/C6WnvJGzcpnYQoLlbeo5D01nxQVElsaKXBZTJDD5KgaNpgiBWeEV0cmgqOv66w4TEQmqKZaSljAV+FuJIUWc6HGY00mJTqLOeqfGLOfKHUP5JOm5RKPDZuhSWVpN5kaR2Z7trnNnP5RQvXlSQBKPhQRGIyPHV9KVIXEirimvg442FzRdpei8dFGOb9wkPpJZit48wyHDtpJyojtQJ/hAUrtNRwJpHxAcAWIPvQTmQoKSRDOcL1mbFmxYRCBTIai2BRYk2VWw3+UYf0mSoTVyoFQZYLE8fPQi1Z2sHORg5CPgAUM8viAzJ9IvTcbhgLNw7LqhQmE0wyCA5fSpH/1VkcUHt/UCxSbn1VJHRhSMR6wAkgh42SoEpxTU0l8eYbrsvxWkKyKwkxR1wz5fg6LeIaMvGIXRk1nQsMB2maUfaWQlBtwTqu8h4bJG9Tt7l9BpgHIYCQ9bACiDKgAWnRlzZcwgcxDbm6ltgKgFMFcAjAYTWGWQhxANrV6GppUcYkIZDAGFZfY0mNeNmFegFcSRgKpMOTACqCByL1Tr7IOYaSz0Gp84L5vGIxPFaHkwxGBwbjUHkp7H0VENwtl+uhOAzaSgari0sAastsDI6j+/gCEdh7tTG+NleoIBFSoEQg+4OOMM2sn6IFH+oHI7vVWQFuZ1VQoGiMEoDYzepXSpTH1E5hwUg4X5nxA8WNe845l4TPIr6Z1FfxmiALkarCuhr9N24NcWDxQmLsQcV2Qh3m7xe7mLMMCFaWKW32qCorlQlUPviDBVtDdkVChn/Y2Cdtyf4+F/25CTGld84HLUJiQ2PQiqmGLkNrWTarLcyBxlfzADXzK72+o3+QBbvBREAW4fNCKWuADISOImpgqvNC9FRYkNPlUdy5AOLOBqW5aOFFqrvsVOpoY0kJHA+3Y5xzz0d1dkuKP6EZx4UsEUkwcDwuyQQ+kYQIB18SXu47ilMsjJjsNk0CqGBqqAMtIapIGTYJwhtOwK7wAkMOTZBsVLyfP8UOU3aNLPE3HWNujLJa8iKurpNhQ6erCL+XiSpqNmhYvSM2mLND2lu5B84cxDVlow0EylUJQjUU4ObniB7TBe8HrqHzsDRH6G0g0XR3FSDVVQQIKdXQWlrYV6FYEVR6CsH+rUY02J9soSfGGI4ofGpNO6eDVVZEPWFMToipYRU1WXStT+4QV7V/J36U3qOU05Cv5e/Vhads3NMM79lQvbtq+xNJUi1JYo0EyVTQ0NKYcixSgF10+HUdSX80Zkq5kyyY0CaZkR7odMPZOPmWuL15c1bdlXCRtwLH23RwYWj5MeSXp+yA0tJqGCuQTb0GwJfED21yfsRFPFMdpkW9VpFOBq2J/pLXgwCIaXBdWJEc9L+mucwvCUcDGYxvrBCYahXhikp1td3lRXRT5XWNqaIxHBbR8bPJKENUYaDXRFIhnsBFPnm0op4CWD1BeCU4hbkw5BeLZKPce7dK89ZV2HZHTDYDVDU6sIqfZCGtCNgAzZK0oZ8PljcgofjzLsL0YPxqvq9o68rHqqoLPbMJ11ETVNjT1Q7aS9hVhvLoq9oNVBPUGJepcIb5ZTRLbdA458icc3yqCyYcEQMPRrTSYLsJVRAlu1LLa/tD3L29bFCTZTZRkqBjK/vKWaNRt1H34y1sMEqNdVUfp53yD0rIv6I+0x5rdl1dXuygmJ6r/efX61fM2zcq/vn6oqt2f374tG9Tlm20SF3mZ31Vv4nz7Ntrkb3/75Zf/8fbXX99uWxxvY8Zv/Reut0NL7QaVKyVv2m/QaVKU1fuoim6jEs/C8WYrgF1to6I6v/0Hiqsm688zJx1/GejcN9hdMWif3hPnjkCT058enPzuvGGkqcYB94b06Q34suBIw1M8LKLEmxEiarol9XDNqzhKowILyg4V1UtvQW3wyPO03mbj3zzPyWtfvZSYAclvFgv93RzbWdnW6x5fZLrFFpnj/FhtU36M/TdzLO/yzctxWR6nETlToXGxJRZjzeK03iCs5RI8rGjHDVcotaHiBe7LE5Z3XFBhzkX8FEMA5vj7yizS8as5puukSjnG6T5ZcOADtlMARPR3m1mpipyfiuaTHa+IPGKD4TOqov9CL0+tyqYxsSV2GN+jYSEVkTKFdngB4lOfzXF9SraY2TfXeX9qQWMUCs3xXqJsg4qj8nuyaZZGGi1fZo61rfH3POOGTn+3xfa9iHZdcjkIKVNsixvLwxMwU0KhLd53OQnG4VUMX2ahXYokL/BaxmmX4auldrmO7gEF03y10DF1YwFc50dxymkZpsRKR9e3aVI+ALp5LBDx/eUtt/zzFsZbwcTgDD7eYDEzZ6JnKjzdy6oZMAmnnCa2jar2NBaOaNvYWjXvk3KXRi9dVB6NiS1ZzWzjD1UTiuoz0R0Sh0mW1lzrBDdxoCyK7pOFiiFE4AcyfFwNa3zKceeTf6FN131fdcDjc1EKBjim4Zy2DzyO8auF6dO9jMvjor9bYCMEQdhM/FDkNbex4MscsEoQ2uMC5IYpWA/XDw8Xe/G65BVmExaXVl2rTux7fFyn5LYrzNZDoTner1nyzxpdoZw4cFisXJE5ztM0uj/b4v6QYBFx6ECxxeaj4izG5sPymyKJ+ak0Pn8aA6fVMldV0Tw9XTZe2ADrGIfRdSnToplG5sOuQX3vRXFiS+wxAqsGV2SzDSPn+xdpfZ9k/D6MLrHBeJ3XMbCtGz6vRgrEK2E+EqC7MmfA/XoUa3JiLz9r9K3BQFM3ovSbPxWeaSbxKE3zJxZB98liUeQYkO8RVG6OnQ7c5zHzZathN/a2gQ+XqW5WGDCXuvpazeDTAqH+AjlnVTIlFl7N6PnkGW13nIeY+myFq7PQ2gt3HEKmzEISyWUvDlv/zf4ssL2yAx0FtiU/qZrn78j7SB6Ny8XBoqm/VtmTnnc5nnWdlRBnj18tODHOeXHrPlnJR30LOHjo7zYOmfsEfz/H3yI8+7xThiu0WPMKFOEd6nn2teIO9NkSCwfAbiPByJbYbHxKEugWk2uHEF6ofDV6ItCxjMeZzP4dyJzWaQq4eYavy6mYd3mFjRwpVqDYwlv9XKEii9JPSfbIeayZEoveRpt7dE1iothejp8tcV1VL/xiT3+32QwkUclvBppPFqqr4+trbFWl+LdgsgPlhxAPNa6LqMAqpKecsL0SSm162V3245EyBTY9xWyX/AvxPey/Wm5Mh4jf6/wKpSiuYPw6WPv+D08sQMOQvr+gwEtSE1+Sm9U8TrrAKnzr6Tz7iMd70dyh5EK4mDKLoL+oHJIudjkmuQhACGDJgJC98PNjbhSCCYePc5wX7LOZ+R7dRXVafUvQ02fh7EooXI2BOSYv9bEveywO9qW86lrty77HhBx8X/gyC44s0QUqYkwiQVFyRfY9lSGGyu2xH23be9wQ5r7MJlIGsxZmKiRIJltiE8exAfHR323Oqf5ZJ1hLH+MteJ6Jx9RQuYWmk2B1w9bPQrPANJdAZPzKQliua0297qVEYV2jC1ej9XTJxc01H43JQfupq69WA+7RvpimsGyvJ4M57Pd0uA67sn3clU21Gwm/bzrsb9ayvwm1515qxR+zRXkt97KMWSZrvbzuNAv9aZ5iRuRxjF/nNBmO0orXunZn2QHuEhJHeFYKiyn12WLpwxateKo+frVcRAFU42cLBSwsbtZLWPKMUmANHj/bmRpY2USipdF+Ncf0PdlUDyya7pOFAkPJ/QPHhP23n0tVn5XXRYQ5WrhnyBTMv3B+TDYbxAlf/83iOg420gQZHj5ayl2XPwC0X6mylS1jjQa3W8jw1OPf53f/B7yiNRj/T+dFrak+17LWniPBp0tzL2tXac0FfrVfbM4hs/eI3NLv37xjDyG5wiWUb2M1HYs+Pvp7eAFxMcyO0tt6ux9MfIlK4a5V/81C07d5JC4ifnVkCszxAXsa1WbmOM82CdnNvmpC8ztCtrMguBkLvK/MZAFGS3CLbMIXZheYqF1z81LVZ5EaUsd6z5Mkla7pREmrTyPY7wWd+16ib5e6YSA8w+szQzw2l3sFWhQT2RFtu4IhMX5ezhO6d3fuLuRPjVozkzsPzcw60AkwV2Rr1jahvc1dd5A3JTAWu6ekTG4xN+I15keyqaM05YwQEMASf5IKGV7o7zbHod1ayJ+GDp9nNfYfmvyakjMZsdQuZFWKWCi08LptsHVwnG+3giuALbHnfMlJGlC8eLBYV0euUmGIw+GfhrfoY7NL9CNBT0JMLgRh0cJuV+Q/0OYyIgljrvhNBVBsoQ3ySoMehrDv/XVeRSlMHxDCaQzyRqRAP2M45NVjzXWQfHALFsBrbfWl3t7y2kIGY97Kh4q/jt5+sfHwfkjuquOIz9JIf7foT1cHMmf4MutAqfPqARWDdQiFSnEQ1i2MhhCMnym3kN8a6y0s+DExgY42Gw4bL8taaJvZfZ8/ZWkeCbM7frfYSnR1hNAv6rvFaUWWtuLZV+foAJXbyN9zK07ndxL8MIQ9NU6ed0nRxKa9j15KmDI8jH0rzd3BNgIOChaVQlkdy19F2NhCMMsAxRbalKkpZC8TSq16/bVExdF9gZBom4qlVmG0Y0Xx8hJQbCOXwxupvGBSBTb6q6t0/BKn6BPK7nk3Lgzh2sIFKpJcmEcZjEMrjYHRoBE0MQRhQ3lyl/4ki/DOVFCKTJENTvntfb7MZsfV7kyjtK99/EBytvO7LxmU1SkvnCuAKbDBd1IKtG0+WURpjAk8eTbjiqxsMnIKm/3AEosr473Kg8jEUiCbwPM8fvxbHbXX4LnIc6bI2o3X1D/6ESVpBPlG5FBuLcGDgCFsIl4yBXax1GI3kD+1Y++eERZSVAHlVruk5O6l8Xec5kXfv3dIyCejBLQIZo7ixyYxMrnpww9FKLTcb8uf2xA23uYvc8jbbFwheGqTbb2F5x2GsG0heta1wEOYt9DXuaoQl0uBLbHF2LwjUeSpGLoFlVtFyaC+Zx0KIWRGBLDkI7TpMCT8Ug0UW2khsg6/q1/e1VUl3A4QSq0xf0/KhzQpKwV6HsSCMq3uTREW/wu8LxU9hTCETbBHmjZVk5i/4MOUWIaGA0HhVjKYbgA041dr7/BxXlZ8n6gCizV5h+IkSoHesSVuGIcLZeR6knjdTAnp1mJ35UzbHg9nwWGdu/Ukq1BRQowGAVhZAUQRM1gQxD5KQCuPgGF7KkCrnel1glo55DQjV2Rl36CyOqqqIrmtK3Scb29JzAZejIFxaIHnD9mX4f8ORNR+t46ohfa99jvd9UQID2uEWr0owBzaUikWKdAc4QjSlWKFF3v6zHN9MAHvZ2ALLVaAoK9o7UMEOiZ+8gMVL4TVBK8nV2ZvyX/NEiEygi+zXTHL66hI7u6gAx8QwCaGAavX4uX87rxI7hPhBjZQbLPXLFFnMACuMbHUAfNnFJV1gQhdJdgZCIcWoPwBQqEDXvJDiZsGsMBfZ5sUte8Micmx+EJbvBeoIG8+wm5JCYhjG4QG6iYGCOdR5K1zE69wypHQYDa2YZJdJM1JrOgPZIqsbCa8sSNH5F11wWLii1cTgtdqEu9Uga0+cskUKKs5TQyeuJ7arqSNq6N8l6SpIAZckS1OWHz5Mgu/+lP+CVV4R3NWAi9jiKUWmB8KhFS4gXKLOarxViyJQcx8mUM8yreIUytsyULWaHBbuat3nOePCWpPOmDMHMTy8ThHmw2uUZ7mxZafKq5oNVoUb8cqbB7kP5LM99UQBpXLS2nq+tOo1U6LS8zU2VX07S1ZbiMxCpIt2TdRXyrbG7X38sz4RmFyyfqmrL5WgwHXzYuP6Bl4o4Yrsuah9mkgkIn6oiWXoLOyS2EoRKT1n1fD452Dpt1hkO1FkDspIzr32ykqHNOw/EXAK07dlg3GKBTae3Vgf44l28fyVIQumC4mSZzor4gmvImSpNjUH44fOFNQLLU7175+qLe3WZQAJ9pUkTnO7rISi234uG9GwT54fNei1AcebFkysI7nsIdQ+VqU06wAYwdgld2XWJjk/UB4hEyBA77GdJIiHUptjJaLArXZ/IB32OmitfF5oGc/WGwuPj0thvXbLbKAVLdo1LPyFKvcerzZIQQO88XLmu3tGWP/WB94ojkUrk0KwmXpBRB6JCpYIGdvSIGg+6/KhPszCMbCzyqS8Ezkm1y/I1h7cdOdbWX118+wIXI/tqMXD2zp7xbYmgvBHKbum028Ubq7q9P/RmVzLYYPPOIKrfF+yeVoh7LwIqPhbmkiOk5mXJLRcShmc5l3zcpefbVUpLvuaITr0vjZRin3d795bTx+//fch1rq4Hdpft9ny7DnS2XtqY5xtvANSKbAYh+Jh3CRl6Ljj/q++CwxK12n6TIiMp6LphLVpOtn275kFR0LLeJko7IbDRcsS31ffB5P86LeEq76llcup0ua+tPM2NComHCbKrDwbZK+s47N5svis/MFPZXuulBZe1pdKJqRbMly2pUQBfLR0d8Xn/eLPE0xC2KgSxTnxcZFq2pRTKRNcbtHWfkk2mhsyeo2kJ6xfB0WD1N6bhv6Q51IrOi2xMK2LMmrt/xJ2/jVHNPJVjix6j5ZcGBUliQNGMd9w1d7TG3klJghkC+1x3wV8ef8bIlN/Ffo/HOhsx4c3d0laQJksmMKLPABb8jbvyAf+kbLUQwcjAuFS91oIS+wS3a9XNGSe1XSlU/5fZKBjwmKpXaY+9wGUuQCgMVsRWldASF09Pd5vXGnSVFWIvtQn+2oJ6Iav1r0KtjD8Vix7aLsRbAN24/2q6L8WtJYZrGXT4rqgfARt5UfP5vj+oAywSnff7PY60RdGj0S8lqXvC4Gim34dYv+nmeCgqe/W2CLnrszByijl1hqx8fNVpHEr1QiN9NldljbpFfCEswV2eN8j34kMfAKElS+Gsu6W0s8T9b7FcnesJbWnMauPuh+M0whbPxgWj9gCD5zh0BI4ccXWvRRiNg8tozW7AThV/BayK8OmH4DMf1mg+nvyY446qJUvK7EFVnsqh7wIgMt4EyBhfz0GTA5+Rk/z2ElL6S9++MGP+3dY3E8yoGrTqO/z0pJbCpTYKMxs/saePGM/j7vetDdp6XsJv6wQyy3CHYJ/URx2xspWqjcQlujmCzJ7/g3xcbP1kEz19F9eZZ13QKDZzgIC94kucc4rmw/WexF8g23jrRfLLR/k2W+9SSBCeiHIguc3el4Vxd4/AyGsPD4j2nZ5Y1IgWz2K/ccUdovVhZEm59GzFjFlFjYWm0eGgEf/d1Gwv8dX1+Y6sbu3lkEw+mqn0kwoPE5bp7LKLjOd0ks+B76j0sFLomnodYJti/g0KeLVTiAw6dJcrl2tKSYNSwWQM4aPK6CJqk8YVAHGNCxqKQREoD+R7rAYolv02Zwq3v/0cKo6TUxa8UMXy0cwuJDNt9sn60hbiAoHof+bo9NNpNQuT325j48iLctOei/5fRf5K/4XHXerOqueV4Q1HlDiYVKELyztp5ZqSXuaIV/GdYxXkv1n5dQeETMQbOOKTgoP0OeOdzBnsgEbFRAABOwweOqDiWVp9GJ69NgPzd3z+3xcH/dcCE57IOC/aSwx+IYwj1nFpv1nVk0GVY4Zmk/2UUbiZFGU5vPP8k5ycp90oczCVU76/Nd/3ueLSyV3WG4cuF5L37A43gTRVZ5mlWMtAjdPpnbGu2fTyW3ycSNK100h0W6IA/6c58j3+2n3eTPee3tgE5rQhcHhqJlbCBswj2dZx/zLboQMv7xZZa2wIcalVV5nYuXOoHiJXeCa7esfG2Hhe/zXeZpjzbM1T4KocctPyWWaZRT2BMxeiAyjH2ZTfDdXqbPOnmuyCXH9KiuHnCjSdzEebc3bv24ToXZgf3s0O0DH4YII++pcrYhNLlLeNUOldtj7xYIXSMAmHlb52Rir/NHxO1z6O+W2I7iGJWlDCdTauOsyH8keKGUXWuEylcj7c0S7yfWrZVgL7+SetMIatMYFPnOFFjiE6+bU59trKZcfINo+LiUMuqer+mCp4TMPUKpjaXcvmEjQQ0UW84LfA+QK7LvL4xWLLVQHdELEQ0YsVBojbd9yF6ql2RA9hx3XBcFyuIX8eILDGHTQlvvUrhwypbY91l9F1MOZRGG1F885UKRxs+2fF3fViTx3lkWp7hjEHvzEI4tnDzrWhgg7Ftokgf2SfCVY4EhPVtUjg2GtG2xUwmKsfEQji0oxsJDOLaA64qyB0M46ies5xNiuUfpKUIgyQzAQ7QNEtMAPETbIJkNwG3ucjRVhPsc/VdL/oC5zoXT4Ic3uCKrcxxMqHe4JBNeL2eLbEdMVMMl7s5G2M1D5S7YZVhtsF2iO9wFtIEeL+XLbLA+RcXmIk+yqvyOCoS5kfeaSkAsZu4BxY95PT5fLj3qUkN6tFj+r22qaaoFsbc3ZP50qNzCPxw4A9GwV9hJ9hA7a+812clgiWiF7xizCGQUqSEt+o9ribdAx692mESjefxqiQnKweI0ws9R+Yg2amrKYOz6fPzjx29ij9uvdphOnndJ0bjnPudZ9SAiFQBc8f83igAq8+VuHPw+KVBcvUe3fIIVKZDNWcxQrcuo9TFPgXMZGZRPSxAHyaGcWnoXZY/i5hAEcMYvCisI4Ib/7FiOmpQ5Ye3fPJZhHsqdsJ/dRnzEKF9ovy40NgmYkw+GsJC0Glu5RfKvRkyviygro+beurCwKeD8WxOZVA3p3+IlKoWXEXWwNtpxR97NUNAThvBpARqRHMoq1naw8hQDUoDZRAYU8UNUIqnfGASw2Qkm8ME2U2Dvo2z3irCHsi+zx0q2iCQVUZf0Se1XNK5kEwMfJqPlPkSq09uoS7SNkkzYbkpAzNv4GJVf0FPnXviSk7O5GEjzpQCz0HtxjHbV9UOCexzhz82J5sco25z/EDYBatDVHJr1bomvJd6vfUzKyv+xNQCly4trZmimOWIb/LS8nFLfLZ0i4BGO5fK0bykpPiR3zZ4tIHMBKF2YywzNNMzVt81job9baO0Sbb4n1QPIZEKhHV7geW/q878B44bhVQ/+nC0muzMBKW4BXmGQQ9lzP3RSyZdZrMyAh9jeM3xW9j1okkxHwGupAID92PFmeAftz6ByG2srTnYJMW8EO5YrcsAJxHPxZRa2eJMXGTC3qe+22IAOMgU2N0jKUgh7Hj7acNNI9sbgFLkJAPiJteqgMQIEaTne2FTUnTBYizQoCbAai+a3Jy8CPk35tzpqqMyiGr9aGB5ZUl0USQyfn4ulDpjB03Gx1IaSsv669lXeT9c+gsfyDofv/R6oXV7BQUtAXNsAySABsTAZtEezvkeyiqNY5yPY9/lTlubRBriQyhXZLFR9VanZAwDYXFWJUVaiHodwC0sstug71p/fUXL/wL/pQn23CBSts02KyCrDhYhS36316zFJiAhp2LbAynWY/EDFC0kCI3gmuDIrWSMxgTQCQdJEgHUZFV+ae5reRsUXt1cWFXWnNCr8DYAvwiW8L2737q7z4Zk0iHXo4p/YvqV92WG8XSJGF2eXEZZpODVsHH87BE6Xdt9ssYBRdVyRnbcM8kHQ3+ffge6dBJHztjYfiI/U9FgcREVedd2a/LqI4kc8MOh4ly+zx/q1SGGUTYEFPhIACllrTIHlmS6CD5/5MnszC0QrFP4bSKO/y4bG5CGVczpuhjaBCIT+u4MbCJZ1ay+6vbtlIR76HqUpqsJYQzQuFztIU38iPlrljchQ606Yc5e2BmiRcUWOOC/IbUlMcgXuEWTJGJxLFJW8G6r/Nr/96P5q8WI7sKouskv0zyaNjOfWi0LltOlS1l+3sgm9XIVVXuHOGlrROs2LdrYguaMKbfA2044af6koz1yhM155xJ4S0H7e4AyOYqkNp0Z3d1/EzGv09zkUlZzSlPjCl9olIDZtkCts13lrl/DI2bL9DAxdbOOSN38eY5UeYvPCYnPawOhQrHs9uIiKbvslZnagS2xPWiCMbInNhm2kMRRcBJUv5sgMePI+xSljPyrMwqhAm+bIGR43C7HEGn7Qdz3fXO1QPMS1j3Pvl91Thdol7acdvmnUYkjxg0dyvgOv82hgLYSeXNM8TVIsfMKVCr7MSoU+nWcdGWSpP5li65PC/cmMq5osT4NCgdnFuLBCN5GhAfbBjP8tz6wj3+y7R2kSCbnom09zcLN0XM3pDBBxzxTY+IDCvmOAUeT8dfT208oldgpZDSal+/N2zzqlZsoVa5o19jSKUXWVF5WAky2xxHiNtpiAFfqY8B4QoNgivC7boOd2KSEf+PdohdLV6IJuzps3vgPYvxiPu7ELVl6rnO/RU5Qdeb9FRYK3jYNOPc63t0kW+dtpevzuPGGFdCKb7bHmtCX5YHGbCSsq7v5S88XCRomy+i6KSTaFgtyShvP/wDCWofRADL1VCDPK7vlkP/03cyzfkw2PpPtkjuMjED7z0T7QOSpRQwToAFcodMBLfihx0wAWa3JZJvcZ2lBGLG+UgBBLB1n3ji1yyYTHypfZBO438fgcc49ffx6HJNai8SPsM+SKLO2787o6v2tQdOnDRTNPBFnNCkizjt9aR2NyWNXU1ddq65D/X6R1wWerpL/baI5JXjf9lONC+EpGX7TkDuysfI/uIiFJEvV5NdIiMb0mMRLDWYb76BTvKkkdgVC5RRgxeq7wp+2OjyKmvtswMDnCTwrodnb/3WHxO86zqshT6CRSBvMTO9P5CQ8icwGEbXYpC+FG81/4Qi9V07jKwi9W63aVyTCfPO/yohcoDi9ftlqJ7w7OMFxY2R/xBtACKmRrNWLDrLXr4pIJOCQYd8xsjwG8qZthFnLf1qYlDy6VLsSjzT/qshJDTIVCC4dg47iTIRZLbazaiwJdoRTFQDYHpmjJ9bg51YZMZKbAwk2bZI8kGA3cyQiFP+F1K8kesiHnJPvcNi4h2GZXgm5SDcv3QaJeIbCDbj3o1oNu/TfRrX2CIs/syB0Wl4zI0qrT6Me+PTF1HVtiEVk9pnkScgrwZfb9FFA64jsrm1z7iaA0mII54snn0uMtk/HIxq+2mMSViv5u4a/BI/mGilLwUzEFFuvVQ5TdozTnXFTU59Vom+sEFc3q56duBjQO+kZRd/1HEKHv3V/morTxZUtcfwkR3XIcpXGdUk9bcMMUi1cjJo3C847y67E4CIm86tIOw0VnhCQICjEnBI/zrMCVp5mX0EvyUXpbb/kNY/Np/5f1/k9BzXdfV8PLzKu1nv4TGpWLw0Rdf6IFeabH10/rNJWeSQqF5nibJ2zHt2dPsug2Fd56lcDY7Iy3Cbbir/Nmzkp+Z8wVroa5L+u0ZTDvLJU9IqfklIrK0zA1aRE4RaE+2+ES32gdv5pjOsd/RBXvTRu/Wjp+AJfPTx1gwj4M55+tbsTlmK9OhWBpy1A669NEMIaMNrzPMKnxfr24FyIs2KKfXn2Lr3l7Mb2AzoHvDXCslfXXHFPYqBNilXSOEBi3HMrK8xk9nzwjgQxMgQVVo+fjqEL3eSHsQriinyfCUk7bUJc1fhaFFeBUGsYZQnXNehIttC54GiGAeaPMDqe0e2gWX6K4LkjcbbdJD/UsAYTV7WkCQ0wTbQW55sUUf2K5xZbu3/XdV55sYbktAJvNptdf4hRBd4eZAkt8F6hIcuEVJa7IwkYjXq+mNmdJMAU2B294+uCU/EzJEpZTqMfXz7KkSqIUFHC+7CeW82YCcMGn3PP8jULkIN3K2tMINtUkuP8Qi+1PnMOkmgSz4jokxN1n5iSYgzFo49j1YlIYwzSM2p7H/Bd6ecoL3mHAFs25mwAPYhQnLwuxkLdec9NnM+ox3NQn9AOlQjwn9d3GUUXuokGPD7AldgeDIEKmwGLh3h1tNgWuxxFt/LyUnibZyYQAxuGjzZbmDhUFKgRcTMEcel/Bc/f89rn/Zo7lY1XtoGAl+rtVYCrwHvn4dTUq6ejuLkmTyNfbP6BxWcnkdScytlrZFIys8fP8OwBoRyLfjyxmAqV454tXd1/jp0PjZPZI607DLGuNeZomjvQMsz+fHKb/tho2xCtOCDYc0DiwoaLuvxcbXqU1F/zeflnkHCvK7mts4QjGJ/V9NUz8AWWoSOJAp+08NgeW1qNYO2fjreeHIq93LKbxqxUmAcncIVO2Fw6WulfygLaouct6m/reLaFRudwvUdefhn2bRjlHd/tpTgfIvxHDdXEIPozWPsxoz2CSetMwlj9TiFci7bbqVyUc4Ux/t8BGzsxElwT12cKNkxcxwt3A/x6lKfFqcA4YEMDCGZCX/ON03Scrt0QuvcQhFC4ZSfSx2qbv8o2wBtPfbQ4IsgoLT5/b9guqnvLikT8vgGGs4kewUL80EnlcFwXKYqH/MhjrVk6e4+ZG52VU6RqTga5GfXad8g3Z6MfmEholrbpWRdr3+DjfCM+W0SUWXlXi7mLdqc0XazUQNJtr6zfAOnMbVeJ7EmLpfCGGUgF1eF1gPsW6pqfkpKPOt1GSnWSbRMgFyBW5XJNqXsvUXJNiYSxb+VJv36MYq960BPAzpWu75sVheI+yfEuebODv7KjgnFsjV4qUzbQAq1m2GtXQfQpg/HdgrnsAafW1u0gCO//2ZMcY45WAXKGLyscwAasiRhdDyAjLNBxFNy1MIFdmdU2lzjLBcKA+m+P6HMUPSYZEVmUKLMMIwZWTLbHYhCZZY2MAKLkii17WcYzQBu4nV2Yh9UXBLyrdJ5utUH5PjkcvEN5ZiwH7XKE9XjAqQyhcpU4Jp0s8lcj+7KhCpC88LvLs5HlH+EO4YMqXWfhzhdvXtjevbYPRFM4HvFKJWS36r1br9O48A7QAU2Ax/+i5wipd0FD0d5tV42Oy2aCMXzT6rxY2bp1hDdQtEJx1yxatRo/8rUY12pzgjVZ6VFW4e/73G0CUTm/wGOGZRsVQjfOIuCI7cxWvIsQvIl5JFwptQu346xjtFwtjJ3CyH38F/TnZAokoxq+WY+uoCw6PKlujWAYTRj8RnC9nXqhV57TIuYRT7Rebq0T8DSK7yNVd+sKjGD5a2BnHnG1xbFP7Xcwtks0Hmx3B7T9QXPFbge6jRT/yDTen7ZclvZlXmOevm804dyQ4fLbDBW2axs8WFlyz5sUxeU1S8LpwZVY93JDXR6M05Z8cZEpWowTpofppQRqTgxpUV5/I59YoXXHqHU4UxNWYKbA7lhZPpa20e17wW/fmi11keyYMaPxqYzaVpXiLaPxqu+O6Kvn5Gj9bja97aRBrNbz/IHcvS2GwEMhq5PY42u6i5N7z2Zsei8sJr7TqWv0RP/MqO9XZa0ee6/wo5mN6mJLVyEXnRewfZPNNJ8sgc8opq8GwVmFZh1n+Od+gtL2Jy+4pqe82seNl1dYsEEcersjK9G8tl/ZiEd9RoHgfFVa4+P1pzO7J1J+zOS91ig9etl8hXxBQ7IL7NzXu33xw/67G/bsc90JLwhf0VH5CVYUKLDtDTkq/lQHG6bBAmCKaaJ0AWxffblHBzbvdsrtDOaObI9DTEd/z4hHzi+yaE1C8Gjk7zYt6G07EBHQO0mWAYxrBUouTjxCFzSDQEIhHNXy0MRd3SSyk6+w/ziGYiz2emPzA/ersfN/nEmlcTg8kqhFMxOuBVB85OZBxN19mw5kynGzJ/GY5SRPM9aj5YhWLhSJ+7rpvNli6XAbvXo7q6oGPHwCKnXBfojjZQe+jQRA/scb4jKKyLlCbptTXP0GhcvJOKOuv1TcxRX6qSxIRINzWSKy8FXuX6bWb//cJbrL0Nth4bO4MqUBx4MmfnCfPtru8IOnz7xLfu+YMKgdu1NRfKyue5ukGykxFf7eJgE7BF4Hp77Z3EyB8bMn8kZjtZF8i4qbfEPbhJlEstlAOjwmXsKL9YmErRo98xGrzxfZW2XnGOzLp7+bYsDY7TVC6IX9x7lauyJ7TjvPsLrmvCyCATwJiwS3PVRGJs0t9trA8GwT9NSHW9GSLbA5OyjqtzrI74exk/G59AwT3QXEHhCpdzSJw9ZLFYS6CjYhc4u5VtadR/+GugV3ldREjIcsN9dm2V+KS4nalrMkM8FyJ6JgC25F+jMoHaKjtd1sH25mQ/nj8bIvrqiokF637EluM7/I8/f/b+7LluHVkwV9x+Glm4kb72Kc7oqfjnImQZPlYMbalVsn2vf1SQbGgEq9YZDUXLT3R/z4AuAHEQqxcqvRiqwggkQkkEolEIpMHr/quo7UmIf8dElkwG7Fw/ow24Y9gH6cOcgT0oZk4rQ2C8CMlap2UjU/Rfh5T4XSlgrnd/LpZ4ambbOmy3eNs4wChELg3WZDkuwg/p+KNmaiOXS/DfegqkdWBm32x0S/TMvlWpyfG6Nt81vWA4LuLmPuK4JZcyzRdMrWnBuLt6BF8ZaKsUAVaa5HxCW2+zWzfcmLToEAZ71ivNg08vFAMFFBos1KCKtQ1ArJYmiW4hj8fI0idMIc2p1znZqYWhTUr9C5leoXjKwMVowrtMZziMc/FEs6CTMNo+8RnrflHMpsxgpDf9bmpMp2wA8orn0p1u7y7y0FvG2u+aToFclwBtVwngyK8X0X/6q0P4rPGDMClioNL0uPefp16az5Ld3t8EyvTUISVdG9s/xHtT7LwnnHmYks1IMcgSPqxgduPs1EHToPw4SKBsx4+uHOXEgA1UBGUIc3bneSqSmnMOSM3n6dyulqeywJcQHdBWJQZyBy9rOBANLkvVgIzV6X2RwSe2FNq9/WAb3mbNN5uuKkPzeg52xCIVy6aHRfVstwNE/WAGblfDkB4ZaHZsVB9gqynzjbpMAnLKOWwHIC3y4RKI3ov0JTeG0H7IID24aD56SxL83wF4tgJR/WhmWxsgyAOl6smc6tMShfJqzo4Rq6U4saeDm5OrZ+NCRLRwTdOViU6/vb1vtzzuW++ajzSijZbUPwj7QegI79r4BUVfftr/Uljfcw6RvprdAJCMqBQ6K6kQwfLUELIAPiRErhXJkRc9U3nGjGDJCHsGfsOVaJvH2flF12iQSmKJ9AjtPo0rmzAeZQ/gs6QR68UplTLol1f06PfvXXNFB6KNDsF2yjhJKEjv2vNztP58z5INkwKJqpE4zyYfkrjOH3qnQnbrxqQwNPPKNkwoLrPGvt52Oe86ouOYfilvxbqT3pYQADMG7fus44zy46JW9p80zAG5/lZHPSTi3dfX3dZ8S57kudpGGEncGarhasb34GtT/b7OEKU1yY+HHNQvK3K2/X30KZ2Yz7k7OgbViAJO1hXLquczVxpJ25B81gQDWaLiyWaN0EGFV1TNJvB0kTzt3fc+VZniea2Zk1cW8qyX7G12TRXVZ26isLss1At57wF6GDOOcjZzXQNZ7KJPo1i5MfRTI/KbPebiKZcZ65pmJZj2gPmctp7oO0YUxuxszTZRGg+31zk38o4/v3tXRDnfc+LIeqtmecaPAXZ5iqNkiKvM7asv+dg8zMq7mtdUWYCHmzMGn2ZJgq8NdiRJZfRsBww2TDCc2S3oWFwJ6pW9xH+riOrmDYuhFUPqCUf9aG5FFd92HNkoGH6rVmotoGvm+vHYCvlHm51wVVmV0eBgziALaekgeiAaXjI2XF2B2ls9aZNGLEemut+TdYEWJfjP9SmmYZpOcMdMAdz3MPMbnqbERl7ctlzF+nkpHl07TUVnV7JakYnWKqfWR9iaUwtWYSANT2btBfOWhzStRIxh+DeWGm0G+izZokWSSf7wWSMgJK5wlK1+e8qi6a9rlGV6Ex+A3qOc97iZjfV1NiMbtxorGcqE86pLPJ81J1wBrStIcvMKqiI24In/CrA75S/guI+3SjNuqgFo+WT9XTnn9+JrcZPAXWh9/OxXDA7NCdKDX4QNmECnlAVdTlC0I0lS/SgOuAJEZ4LZorGJnKdwtOP0sbAbyAyIKFq2hsErwtHtx0YpkNbEo3jnBmhuRNFsXKCKEH5Tukq7aVr/aX9nTcfVlUyRuyMknft0LvXXYCJyvdBiEYH1vgUZXmBWOY2yEFV5e2bxiWmce6rxcw/47O4it/aVIBHpOgO5MVN+gCS399++OX9h7dvcBZglEIhvnv75nkXJ/nfQjwPQZKkBSb997f3RbH/27t3Oe4x/9MuCrM0T++KP4Xp7l2wSd9BWL++e//+Hdjs3vWb12CVoPzyvxsoeb6hUjAS7liNOw6KaP32Tb+7v10kG/D8+9v/9+bfNNP89n8Bw4TNvF+DO4KB+jbBfsPfOEyHEPv9bYTGG6/bPwBkB+xAXj1dR7VAtVrfIAMleoPdGinfScGTPptVN8ljkIX3QfY/dsHz/yThFVk5CK5x2azHr4J4i57iauLV+Fc4wAnlQOncLFwQmYRxuQEXySqCpAZ7Gzov8iYIDCwoQFiAjQ24LqKMAzprBzQHkLDvGRfc2zdfg+cvINkW97+//csv+lNRZKkU5oe//MWEYZyQDfWOoA4H64bzEEDKhc8RTHczzbgfmfPyNYDCNjvJK2drG0iku7YDGitwP7Ngj+PYJSLc1GEhD0BqDsypPE3RDaalFGkTBBM7jyYMTA66aHAiPyj/Mytp27mWqoIhndfkSkPw3LqHHYHqwFMaKNH7519+0QZK++qqsp/yFEFNu8DRWY51esR0o/p/e3Pxn+t6jNboAxwN2xn9EcTlsNxVU1ab4BDEsGkQVDdHNLnhpi8pPCZG/wLtEe8Y+KoLUas3C8xgrVGImbRMNv/xBi/3v715Pzg1HIWDyJDlHKE/myCEwKK4u/ULAYMVqIDZr2/+LW1AY+FgGbcAfVH0wRmSxhJHfeHXTHcM631wm39vMlH1AJ6VMbpVl5/g9MF/T6J/lmAF0jMcZVIGXFcV/RQH24sdRL0JH+j4RHtdWGm4Ds9fBqry6PpcteCr6NrXIK9MzkewKC12vbZlK3l/MdjlmsEeXAEfDKRDA9yh6niRo8xyV3G5jRIBP6vZN2/SMhSvCRaGMitftc8o63e6R8DGYuuznHqJgc9GmhBTkMbNQ7UDGeqT6k2oxXbQ40990cNCsBJB5JWePjJ0aw1ElLmJ7OEYFvPwVjC4UjnqVgZAc41vw703wfP5M9jtrezIEEitz1TR6rnqjMo20iSytrFkVrKzYi5zODIJrHOLorwqJM4FB8z5v2ifAyTXPLJjspoSw+E/BZFJ+zusQgjUSoBjCLpyW4aEif0ILqTyljLVDAyHiUVoG8FjyuUe8VuqcQzi7HlU3NgKDkrrVkSIA3VPyTgjnCNgX4IceZCH6DXxMEgLAfJ6xWJpJPsEq+sCtpRYhn4bBZxe11DPn9GcBPGXKHlw7jgQbLagyu3tgn4ErQ58Yi4xsA+Wjv1OBbV+EN3eMtBEUeYmYcuUo3pM2CJbhbRqBtd2UOs3cvpHI6KpaH9XowayL06dYK4no0Nrc47Kb9IViEFY9AEb2HEaEJd1QH7HyttVFoXgOki2wDlDI1eRy+RzivLWbK1U/89B3ryXyes3UlYnCUc+GrOzYAOcBsObdmhmIV+SavgR3AVlXKAw01859zEKMlhZOWwY+lU5NDS9NwOIwNptQN9zcAWyEBJnKafa96kMtA0Io10QIy90+FeO3cnf/xWONLoUgtLVuKuTXcVEnrpZFUGGXO2B9imJ546xcQTpGvyzjKDEPYNnYRQlb2M1aywUV8pnM0l4o8Br245ROzg30a7bcBzeUlLP7l8lk6Fn4CKOl7xENa+nI0fIvp5ohnFb3onGocIvPIO4VNhfTxWzOFVYHV/Vt+5GchzDvl0l2dUXr007G9mq6wTSdo7+6LmE/KovhE5ixmir1DtsJ/dbNX5oZwsUGbqTnHt9qEBX27pHnb5D41e4uMnrci00msY9LExcTqs4X8aINK373kf6iDAahMoTBtjGZKuKnkFs0l/bUH8hI/1vg7MKO1Amf0YblJfMWN36DKLtfWEBQL7/qfgZNQDWF/lNFsA1BYWz1bW5012UQErXHUBOmcldvJ5S1CJSNzPo8HO0gXuljR73A2rxhIw1vqmpQzUYHm3a1uo7sKYGhHf4Y9CB6rQy2vOA/mjadrPwH28u8uoxwt/e3MA5GFkvYhB6z0PI0kVvFZfMK1/bm/SzIPkIUDgEKFjCB6szodP9CJ0EkIE0Ee0pqv7t3IdUdmNWh6WoMnka2xFkJ0ZV62wGT/QJ6erEN/1++KvTU1otBI9BSH0k+BkO7S0c7ezF6Qu3qyrI5VEdf6+6jPGa2nLT0Eqv82vfnMNrrXqcjoiX7K9Rqy0cO6i2CYPtIP6I8gjWhkMePUabMojjFysNGIGL4n64EJfGzDYAvw3ZXi6l2KR6LpQMWNs1zJMN3JLP0t2OOOhZAWwY3Mkdl0OnphqGK/v36+0b7+LqGjxG4MlKLT/Z77P0EWyuAxSbY1XurNZ2WjiF1wC7gcIr7hFriZ8rkDN0t1s9lM6XCHGdDnfC4lu5uwWcY4XqY1EDFP4oIi0TunWHF/kf0V1xFmRWF4wNDHsNpPYLuizuQXZF5R4wjeuG4XXKzKB7ur6wKtHkFOjVTfxystn0urRC/yL/mD4lcRrY3f/WMCx97JK4Wr4NODtLSfBcra/LOwaeCXYNkPPnfZThVfIxeBFBVLIt1ADx87XKY8uauz8H+SqAOgxwMas0JINIVb32NheqkLDvOchOthkAu755X9+XswPk7IHLRX4NwjLLLJ/2tkDOXsIYVGLDTt6R8K5AFqWWy7SFiDd/DNZqYV3k6En0eYKaWPEriorh5n01FLL4hBbEDbSzexRc25sP7UXu5En3RX6eWw0hEfTR0s8vSNAVTvIIlxgEVsWstzxWFWn48PcyqE0YFpK8Ok1heCePQQTbUid+i8hAXByNdq8ocUbvl/SpohVtNGy0O33tP7p7wSfwT2nW4HcKiIAcJmBPg/ABB3tFjyrsMMSHOwTvohpDeLhoj6VWKgXevuDMRLty52JiKnjBsyt4DYxVAfb2cHCs/iyNSS8TI50k2oAGsxqklWxGsws2NcRowIPTRN+GaxlVPi1fTsuiSK1u1GtYP6P8Po7ywh5gLbBiABcf3H0ok5DRywZ4qMCgotDKVEUBcL4/XsYbvx3UZ6mzNPf4UGYP4QSxMSFKDyqJPtqHOegliIMnNSTs+qGOI8iNJe48KUCWW/NiLaIpqMAzA9VifNQ+4aHoJgLV2rXa2KCGAPLipCiy6LYswFm6Q5ewaL/0yqyO3+z+pNzjnI82fRZzDp5yDnTPKn7Hpt2VXMucFrBbgePqQoX/ekL9kn3dAmj8/tZNLKy6xoguiP+2wu+DWbgiEtYoeP7Kbd9FDnNKoFlISBPs3tub6uf7OgbOafQIshe0+vXtkHRrGytkc4z5nkQGjjV0axs8Ki0jvwmy6O5OdItEPSHR7gFtytnL5d1lFm2j/ptkFX/qPgAbek+DHNRqp7WVsIX1FQR5mQE0G9LB038k0nZh/vJeqxv0B92VwXXEaZlsYlAluuHYxG2FSwX+CmQXUHi5sNFSANEwuIS3uk8rsy/UZuwuoaLkKsI3vkKblqKmDJkGXbPX0NSRUg9FV63YY/BnG/SeMnmShQ1T+WkUx7a3DxiQkzixT+kXgAbrIveQPODmPgNAHf6vuvDhxgbguu7BtnIv+RFYLecJkwgI4Zyl6UMEqvsWK0ugQ9+Zk80GQsg/pdkuYHYSlxGS4HGzgHtg+hglx5EqoRbSxvqYpUu1H8F5e4v2tmAwOrBBeJ0lpP0gzyfHwMODTPQXfR46S+M0+wyeuUk2bIHX81+lZ3F8VnC1G1zkdWA6AaPb5PKoTA2Vgoy042Ng0qtpn8/UpxFTJHrNrVBx5HIR+jwKi6Pqmcj7QQFlFunUw9uKKIZs3t7bWOls6Jr+5r7c3SZBZHVBX7/IORg9ey5mUXN53TJIxS/HIL474k2FZ9XWSnK2427pz9SAwcqNHawLZEeqwrR5iTDV+FEcUVqDiTUFgb+mypGwbWmFwEX+CQrIsnuQoHqNN5lEb+7QmpxobvDVfo0eHFcc1YnXCTncBm/SqdYHvV60U8Ihp05wHFHKG5LNM/TZMs++tq3KTflGabTqN51W3qRL05WbaTkm5v2jjNqOShw9KcIg7yI0DrqTlKMERIMGPf2AjzviGOoK6FWQ509p9xTVNdzqwsM2REYFaxXwgnja4ek8boKjd0snd3dRHFkHXrBPROk7kHGjeZ6ElDnMda5L1ffcBiyEMuNJNiGNnXBNgZLHRB0nuGWDWAvJJD0iJAqlSEwc5bFA8JqHW0KQuqPeADTxaAzishi+qtO/CBmO82HAE5+iLC8GzbkfPmiHp0Oj6AOuOJ2hzhw3UPo+ofqzAjeLfZBwovtpracKiDw6o847Ef3AGRykKFj24uc0yop7tELtlmcLRt9LEqqKG3ZUjPb1H0EdnAQ5WJS53baM7rX/kSbE5m6FG5232jJbDlzGUGkrd+jSonAhq6vgAq70qwbiR/AYhdzA42bZYiontXrrXRuYyPsAbLx7G083c3RYCB5CB7c6z+GfFMWbvNmK5W3thheegv3ceHU5A8Y9sBpBEuy5hrB8+YsprWzSPc9gWdPNrV5MEA4MlqEHsRR47xLYByfA/hHtr9IchWQxS0vJMUXcw12br2uZrd4mJJMb1ja3NSrL/NM43V7hN+GHL/QvciduF1+CZFsaBf/vWlqZyNV2G8UrK/zmglA3bZTNGaR8q+ixQISFYLU/gBD1c8pE3DcSCM00Bdv8IqkRteHkCxSrxI2+nW6Gdx/1tyG1BddJxNIalnUWACIoqCuQaCKdDP88M9S+BuNtdgx3D0/G0AmwveBYlIKbdB+F+ttE3czOsWnqu3al0JBqO8mVyq39iHceE+fp0g2b4cz1Cq9dzJzHsHgxtSYJMHGzLqbGwpcxmm6OqVhVjFVN8b0ZkvrImm2FT/0Y1fzW9s8GT5i+VVtWrjgC+lT9sA0B34yvvUEfQRFwnSG06mWewbXOIAO9NzBlHYfMVxBHwyGR9DaGo9kTcAIew42hbrsmnUv9vin+YPCm2PXh51utNtglzuAJYQORxFPKD17aGtmCWNdnJ2xuEottca6x3TAcg1xchBQy4OrlMqqeocjlCAizEY1wQv0G1ehjeWN/SFdF+H23G1DIe8/R/ZWLC73Xa6tl3Ccc09XQPK4MXu90GrDKT1/TOD5J8qfjeHCFqDWQVbiV8yO12Q7SpFD7kRZ2WZ58J2NO0Z55+Cw1ucrkjrWqB0y1tHTjr+ZC3XGXvRdvyH+UIC/ymxQtIBtgrmIfzFN3Md7PtR/6XqcxaOI+HIZImPyCjRxacySq1paRFSaN63H+jGYyiE/K4h7NZYg7ugYhlm+HvzdNzojuXPCbqbwgHsY7BFsLc8fQLxHj3aQPwM3ZAIM7CUOQ5+6AXiFvfjjB4tfLLsMD17vl4S89TKhD33oMz2mcCEyx3f3Q5PKFfvVm+2rOEpnemzfrR3N2JjHc0sVj0QYrF7CughdkbXIIqspjayW8xHx9VmYZSMIXZ+93GoDX5KtkeeTVvxqvSpfPctvnx+6E2aq8LVC+6oskjCGqisPxZzNJ3HR2/jxOZzgTdxMDdywKqU7HobSWDeNQWHc2KmWwI43Fqt8ZJca6LOefAPA9puKefQ+wuGffo13Dd/OIBfOJd0a0CdStet0CZ+A0iIPEZ75gPFhIQF1DajaEMcBjV966gEcPSATYmKcTU+zmKcg2VyncsfOfIANwrVhGA7wH4UNadollXV9fMR3k/7lz9Aa/1moE9nZdS7Cb+GbtMWbvhEZsn0bnMpToDC/LMzj/tOJlllI826DWbiYCoeRM02bos7s9DfIHsBENnTWmZ4+PbiIDIGDnz/sow0fBr2nSJRp2CPe/QOCGdpIvP0ZQuBUfwW0Xncfs9qQFU4fe+5zGroIlscAdMgIB/DRIHpydDXtwnS0xEu7FmWuQbY5Ct2AvbgNHG1ItobFS4DLQJrLFwgXxL7zSbrIgyQP8dN5RTC0ReGfsJurgGuQlG+fUUBrtUVRt94PDAnaINTxItzqRe9SvStQ2B67tw1dB5Orqtzn6VuczN2Nag0SHLxRMqY5l5cV45zti7Hy9scnjwjXYBVFCHLPMctx+A0/1af1biu4ZQioGmZEPRhiCfXFzH0FMA/gZXxF+DpLN5aOOkquey68+P3/P4aHhcwT54DiSoLTGR/3s7G1Lq+sQ3FS/97qZnf/DCEEn/oju8BHj2PiqoVt/ZruWVpP7PQebn1Fxb8hfvebWqFD5LX0Y50bj5GPg3kb9IljAKCOZCI7NjW0zD/a3dZ7Nkhd5gyoOWx5oJS4T0w2PknuHB5xrSOw+QpqLK82yhejOrWmFI0Q7w7AC5w69ryDPCZ9by8wU3YxgXXKyxDZ63krH8vavJdapl9GEWuDVtOnNXGTp/Z5ExVUWhd4vg9uOvN/9jkHNKJT4vqtujmHVZu57yOjefI+dt/tH9/eOH9OnJE6DjfULyYu8AeVEa/oCWTzJQQNTqDAqbc1Q6v8E0fbeSGdUireKM/h+DIrAldMyTmqLYz76WhEgjh5B9oKCn1iNbu2ER8LzYOeqDMr4TdOx6CqTaBXfiGdjds/tGufMtEt5OG91mLQvH5HFa3Iv92rErZylMYSej5eRQpp7d3pyeeZ0e1dDmnsMcuL023vIiIPu2XZHkvl3wj3gJgvChyjZOry/bUB+z+Q5Vj/8oh+nAvtC+tXv8AUvcHUD3ahKjsCNsTc2K+9YrEUtvUavnKqWDq4XWeO9dyuNMkv8DOIYFEekKPFeFCollcLNuiSvy45u7WdfUjJ++FXMKvC+HwpQvVyhJ4yQOya6X7ViXpOQpNcgyDtLmIGtQaA897MaOM1T3o9M5So01TUoyiy5Bv9EAVuOVX5OsytOLkRdXJ5Ui+lTmlWM5MZoU7MjwEZcBaO1CVBVp0Ctp8y9MIS26Zvu7r6lrh7SORQuvLElpIiLh+fV26+btFLvBDa78Y0TI8SfXt2n2Iv3LMiO55xjL5Ovgqw+GFpdI1RXKGbuSmRbu8TQHQfYuylNvstM7CHg75EkXLogg+yP7sG96c4u9ujFycB65ld7OIaNO3w7j8cgECdeM/yBv9zTT3SUTQ9SaHaLG72Z/BTFcLj7ke+0Ze7TZVKPniQCplW6AkcKgmQ4j2Ft8Ol3xZaziRN7EkeBPJj2r9pquqtoqtWVjF+nfFHYe929L41T5vJIL/+k5co8hjV5HGyvuknYbl0uH1J+CkJQrNKsEL9SVIdzA3ZwIAvwObL00kP0VDsW+uAj8HE9SzfB9ljXnyyXgeIzHsPUhd3Yd9kL3cUbrqH/CLIIHo1aMXuW7m6jJDgaLeih1JlwpadRUEa4hvk1SMq7IESBBDL0EpnvzGHbi/ERXClIO8bLF/Sf0cYf8M9+3Y2DHOCRN78S1eoG/UF3ZWDgO8nzaJuADaFeDqYn11c4xF7NKk/DqdY2lsTGeIQenujjQbe2weMix974VvrSwZkR4Y4QPrgw72FN8rIsLu8wSEykD5WK5Idj2GIHlSqThOTw36u4zAK5A6IBZMkjHwlojePTlxQJcD+wrY9mF/lHcBcQ0YncHyX6CucxLAFbodv83w7auk5S0klld6mG+30ZY+3KGHgDngv4abdnLtyN33b/s4wyuydtxIEpKbI05twwurQ/2GSktmWiD84PnUe1+IdNcYOmU5+WQuevWscxyI2SzHom1joW1vnzPs2Kev2aODYZr9iKgHwFXnVXYyvLpPur5cwfw6yLed5+rkhYlnf2I20r1l6N87mCFJg8Tzb/DSeS9Oh0fg1bvWgboaOLHEJagRiEljEbXB0p8fW2vf/dlyh5QE5ivIPUfJ5RCc649RX/0UjOPv3GYpM7kKJCl4fgsZV2Ra31VdwenbgdebEMH/bVRbzLo9gy5H8T4egYZH1DqzwEn0se+N6FkCJCDxitNhaMpU0RR9+PCFGj6uXROoKvCSBWe5fctdwaJZPDimhPUkCGaGpzd1otVf3um3b1H+i7XZbpFgy7t1qig3nFbn9G4/wDZPnQ7Ze+8+DZfZBsQZxuDdabsvi9gSKndmI5fPk78U2+g8dmzQug69REMNCtXfh3WD2c8/p86SyIwzIm8pB40F2woD1qR0/V3Qh5ZKIvcnlrPxcozNIxzIatdoAHao1y9aR5VBD783+8uci/Y5X0b29uoCAwMgTcljsjVsEteyxi4JBiqrhIR+a9g5Gx1GSk+H3g4UdGFvvLX0z2S/zTPba/6mFrcxtHZiM+BtFAESxOviW9o1C76oXVVX2/jEI7oEzCXbbf8wQ1tQyqvINMuLlJ8Zj78FS8LmNQfTwQbkIEGd3itQ2tVGYEBfVrJIHQQ6nTMkJ5R9cNIG0FhMXpco8Gd8DS+0EfLvWq0TLTpoVllBg0Eo67W/J+AsLDF8m6PhGTeCE5c4jdJnD0ziAaWzuPHf/Smkln/sqMhsw4S4dTLGhQ/dp84wLmRX4TPJ8/A4JSEzAQyBmczm2a9c8p7p1qJ/JicPH0ZgIRcDSOBwzlBvZEFoTzGBb2j4lfL9CdrnSNAJ9hmSGP5vpEekShkvukGxyfGAjOoxbryotRUpDQVB8Dr5y9hDGgn3obTQ8CcwWyKBUni1JTTZCxBUOzykmyKoKsUIjwP5ouwUscr/0eISqiIDYMAU63nn0GbDzi8NOX9CjutwhytfyyusuLPoBlx7kXBOw1ezE1Mr9WFsPD59nKug+ReEqzjVRxNrFzDmrjHwyAalvxlef/SOQUJPMLeASxvmCALde4KXn39gn1aeLLco9e0ymkIzC+WnKanHp/stlkEKAOP+tE5PUhppW8w+EIDeWUev8Xk7DqdyDLQOYDttOgwJCpt7ywTqrrATfnLghLs8PnotjTjkcCwaw7fN9zfkZ5T6ElT+7uojgKjsM2XksJA+WvaWilczk4ouiekzTUqxiezrGecfh8wPUgcujsYylbxvACVTyXX2xiH9wG951XbpsJt63iknFBtwbq5HYoSLYlVIB0Rk6ZA/H8RuFRXQ27YERIwh9ZWu6dcwwEbKTm9SdyDQE50O/UvHX8vAtQf91xD3YAv2O8jY+CgzHBOqznKsSDLkwu+1inAvbHSasqT+Phc5CXLXToGG20LecuXGJX6MZt0IRgwOCf0iwEEEf4/0kcIzuFlTPY5zRns9tZh8uFx//U0eN/V24zn4tdfJpuiF3YLmVUmhRwJTSRfb+B4inNHlxP9lUW7YLsBa/FszLLQBIavL/gQ7FMAYZBnj+H+DHlNZQR1vjxgfmIJ9VAf5W7Zg+BmvE7SzcDsPU9PLBFTMnD56+mssRL3Nvq9A+l8g5OGRFu0BF4Y489ztrlZoGYSCzPJZMdh0IoF6LkPNnw4hpa75DdIxyUXjJ3oHFgiN/K3cdq1Vg5mbh/ItSD+BEk6Q4l9+ieezj35+t1iV6q+FLg6/CXx7CfzN2o5u+kFkKRiV47BfnDEblckmQbpOSjWlvaxK/LJFEP5Nvi8DUI76MEoN/rFohVWBsCopHpTI7RLw6saNhL0CbwTg1g/SlKsK5gNXUNEA/ovNe/21+VYQjAxlGm+PMsY58sGh0p4c8tuuy8AvCEb5USp4Gk4kcyrLwYSchjEI0KRyv9lTsc7tHINSRNzp/3iCeGXpSbKLOVX6CBIMTMQ0u7vzpyflPo/Rt4LqDwXdfNLVNVRGlmGbgH8uf+MqEEiomqXZNlKmpFo2KUJepztIGry+rgUCZQINY7pocb8b+XoASbc3jWi0+KAnZzLO8jCMJNMokRja2Dd8E9CtlkyCfXRoGvgu6phzPZNn28wMFtxsRf+CsUB3yxbQsYEV1PquGQta3VR81kuR/DInexLX3KUk7YKUs15yZ1DvIa7OMXPbhKutOZa4inYega5Kq8/W8QMq9KrKUfujFzc1/m0s67gqvhJossH9JBIG5OgFh3CEOUx9QgqivV2m4rBckGpecN4vjFg6ZEYnoMsrPeJGT7o0Hst/riZHhTH4rUxr/Ld47vVZqJTBFqdzI5nAVNYhW1vTwffJ9lArg6+Kxyq7DeKEB4lb8SSj7MgcSVjMs4G8FuH0Tbo0iGxFszhp6B/O3SDJjSHjn+ZajDG+N6uG7Sk1BjUWiEmMVmyia73bGysvXRazk69dd0A3BIJeeWzi9BXlTQMzAsLBS19UrzqZ4iDaBs8NZx9rq7izcFEiV5QrlmpDBzbPat4fC91OFRKWlpC+uDQ1i/asJSlt3fwFP+BSBxCNmYCJx4BCKcS7k8BY2bE4iSEf8pjxnkqsPmuvaREGbrMFGaORLCZb4dt+YDjo+I6zE1SV71M80eomTr9fnVpzQrd8e2VL0t0MmjyeDptI1LtY/C+Ycwu8qiRwiydWk4Aq61T2uDTPfmPEq3tuLTm9QcDbKtnX1UQdH+s4E7PArS68YAgOLzBpZxdOtgB6cvJ2Vxb+dJQAC7BmG0Z5LKzVBOfAVBXmagihp6DGJi0JRgEqPKbwSsazQVvl95+Aq/WjPYR8izSX4kCtQrj43LYxe7fZqhwO930XG8L/fCYJ9SlG3DD+godpBJvpt3WzjW73MqjrsGyAC/+RgUgZVd7SHaWx1Jggdg0756B3aZ2Fn14JL4FIF4g365fwDWcNBZmtxF2zKjvN+szLXnz0UWkFNo+dA5LndJ+8DJAcRrkJdxcZHcMdcKRuBqr3OIndUTlba9jwfHq5ckPPY3YipT0Q3T+vSlguI0heIqLbMQmEa8odGrYMnRoyM6Go4jfwOzHU5O3kVVUrkpGy03VBxP4bnwQysnh6MqrUPpH40UX9zF5yCXeyv/2fAN+oXV26EKxqrIXIn7CuBpmopcB5SEPJwjz4aD82ekAX0E+zg9mhwJ9QnDKHH3ndSxfTqt38VjdbcaSsdT9qq+kjudSaQZFW86A7gKEZC0YaIQwDdZkOS7CL9nsx9VHkQruztcG5V5Y/BNisGYrsrb6mjrGrCGn4vJvGHwSkZ4U9Rd+JeglRA9gq9EbBtDB1AtF1LNferVNjSKbcjk7T1a/OivmnLnq1859bT2HpCljxEcFZ/JrS/yWi42/GvhUma/0VdryZXVazpDBJcJIfO50uHhTyS0XVmoGkarbF/OTFWO1bbLu7scWL19wK6KNgBOgyK8X0X/slJGruCKrqKMzsF98yzd7fGdsp6yoX3qxFfN/4j2JxCcpe/qWQyCpIu87HAvPw3Ch4sEzk74cHQ+WfbeLVdV+mGj02vVcOGpxMZwp4CcfxeERZmB7PWBhm1s8wg86R4I53Ad3aTUfmWAI2WAWmC+zv+Rzn99QqrZ4Bimv1UR3jvRUD7YQPEt3bM0z1cgjl+nd4Tp1fAzTMo6V9NhjLnMrKQUGigp1x2MdWNJQt+dxs4kASs+aNJD0iS+XLf1uj0n/4w2W1D8I00GIetHBYyKgSjrJjZKbvBy1ZlpW5skEXQU8Vz2flWVjh4M87cQ7JN+ZSYnALhzlEKgUUD0gxJ8mCiDUGiolV2WjiCDKKPxNOwetlyTQIwCY1a2Xt1bHOUwAk5svV5kFc7l/BEQZj3HshBk9X01+s2YUM1M8zbyteIXHzJWBwESiAEOp2AbJVSqPaPLoPv06fx5H0A0rW6ovqWf0jhOn6xggKefUbKxA3IRDgcO1l4gxcvAqtNP44zwPEtjNia3LbIogRSbPMoW6lmen8XBQAJ1A7gOtAy8mKbXNDAafrQNKCnwjVZj3zTRNxoYa1bx4NZv+uLUH1ZUqM70xCPR1O7SgcRf89qja+onq1d1q1LfKZpMZgNCdTLrrozmkurL7P7Idi5J9PUw6Fr6mMnartJYnYOtyVzWrVWnsuvMaDbJ3owuI23nsoe/EQ5VYx8z2obCxn8YzmgLRHVOm86MZpTuTVcX7dpazSpFgQEO/ma02U/IC+Ix9k+yv6XuoQwNmjNLN/c5u82VwAgTK5bXi5jTkTcA7ZlE6e9WoKjKx5hPqsOlzipLhB4e/fZe9N5arbaeYcnRxNMMj36YWOYMXwX49dBXUNynG+tppqCNN9dst5pistf+4Gd9dR/hR6mOpp0GN968c/rV9IvvAzj4mW/O99dpDOylOgFsRMne79XMwlE3P/gZv0rj+EeK8oZPevOpMBq2ajIk9CTJn0zcpsm2PiYBBXg9S3f48fehjn9NH/e+0zA+TAVQJQaiUrYzOAlmN9ZdSx/McRqn22NhDldzicbsKs0NXmh0LX3MJY6ai8BDkQsOdjZbKg0iv3ZNrVDA4+v+2VRtMbkGjxF4+gzi/V0ZJ4ZXPouYSopgYwtT09wKlZ9BXo+474k99Nmc6t2au72/midn2wUK2dk9WDaK41Fx5n+B/AaOYuwA1LdUE5KI10/yPA0jPLONi0aKoo9WkZauQY6DQq2b4P895j9PNm/QWQgluKkr1BitQHz3p+7j1zIuon0chRCF39++f9tfMpdJFdL5zQl+zo1McHkYbNjhgGRshDhwMKfx4VagcftfTJdwGQPkaxgF8Vma5EUWwOFm13yUhNE+iPvj0auoKB4QpS3IfslHsAcJ8vqU0a3SL5nSge2/7aY3A0Pj8ds7gqkUeC36F37Cg3FbEKORaLNcRpceBotRNC2Cvyh7TeeXin7WofX6ylk3yWRban7pglEYjos6hZSghhfGowZgBObj06bSMYnpZEzYXvqd7OFMIKfF+pIoAvl6xZHWxMQ3bam57j6OwnyMOyIPl67QC8u1JI/AbnL3S0Gf8mvoydnsJsi2oG8hImQdb2pFU3rAbKY15VOwmfwufAw2q3zIcJiy+g2OkKuIqtRkUt/pefzlT396z0wlFyKCpArVEXeQPYzAIH1iVbqsX0VNyxzIwZA3TCOwRtTT1Yivh8MSkaLaVeE1A2bAf6GXGrKDHVWJmcO2YClMwSV3DlxBYjcZb9Qmwx6LsDSYz6Wvk1gPcfoQxhR6FDqq8217+OrTpMxjTW7fSRhsVUBN6Qo98k1CsD5DihbzkIrQf+tySv1tvo3CVxS+FB69Ej96L298/PATTY6SzlshN7Wsapjo8u4yi7ZR4oKZBravxs2dI2OWzwhcH345CzRDPzkrwAUfPYLs5QaFNBQeaMlK1GGWKpg5R4hJnZ4tSNym5onTMtnA/a4Au5OiyKLbsgBVsON1VyJkFQYGb4LJ0jF1HSFlciSZyl7Ykx2b8XhUTKsKDl3r+bBuzav851VGUmgs1tRbMG6ZbxKW0+AxwrFjJmzmlMEGd8tXLpF3OhP+aG3bgrgX87qu6CHN46wDuqzoU6SktNeVZ8NXi9nWJmSq8YWVFk/NRFRRT90loQYI6yVZiTJgUgWj2jBFKHHL/VgyhUPnleW0uyUbzIr3FiPSZsBs44s2bT6bWrw18ZMc2ssHDFpNVCwSUvtt6SZObsgvQVdTW7mbqZfY5+2vK2bHDPq3ESOyBIXc5D606zYp1pDTYtOC6zU70rbTj7rHQ8WznGkIHoGrpDEGBV3KgwpOxFkDfooaAuJg+UpnjidgK3HsybG56jSKUdZKftxNI54a2L605N5yuUHN8kwN/vTcAOEUEOKaT8RMN68aaS4qbdnBbF4NRTqb12R81UQCWMaDtQZbCofu4+Kfp7WkqPQ1+au0lnecOz0ObFBTsMGozopafDC9k2LLCFcZfrL+yg9T8kN/EiZjizbMiXNtRcwKbZ8UJOLr4tWMjpZFKBgY3Zt0H4VjcwHulGWD+vNh8EFFzHIYoQp9VKX/kU4elSGom7z68yjKJu6QRcET64gGxiPzKD6+avCagwjh4K0/Yz75ZQKhoz6X40ocXH0GyocIbZup88lCo2svmvM6qv6C0ZqMiZqYl8uwjjTYUjh0HxdvHWlJWYR1pOWdsU/DU7DBqKdhLT6Y/hjcMsJE1pFXfpildQSF217GvoIwpX3d8IfF7yeYjEXsJV1o9jWLtO5MeeSSCkcGg+azH3dI1Vl0wC01IUpyBlaf/LYYYV7HmVvWjTGBuDBm4TjBAce8OSapWoRNT8Zn6Nsgr+FKEwelnB/PsUM3T75D7SbjvfPnAmRJEJ+UxT2CGOLCKr/LMoSdjAIKL3nFxYs/KXmLkIM4XLwHRz3ZsQ73SQGpvyzdP68iQ6UjesAnnv1FiJxxmWZMIaLONZNLiyZi6vccnik/RxCb7GXNjww80xDKJOZcfOgKBxNKmSJLpd+m4bx4jbNW9GXEEbCYukyZir8whtNufNi0PWeOarFkMfBoAR+NdzpClsQw83/wPQ3bjPi4W49xpn7S/Ud0V5wF8Gh/VaIUWDnY/IyKewEN5tM4cJfWYEEB6z76kyRjhYRraVHiCe5UTM4ilK7DJ8h0Sj3JGh7mXHxGUHq0OMARt2nrPU3DefHad3IpzFQbmhOrjaYjGfMZNaPTqk3f0gLMX8/+RqSk7TCovi6bhzpC5q9nX4MnyO1XKQSQN8JpEfZJDuIUOtzyxdsueVQtwpLJ4zOnm+CAOj4fdhlNDJnyCjUt08UxWN1H+x0KYDDnnaxBkg4I1X5cNgO1dMx/G2tQxRYjPt6ms+aZcxiDA13gJwaYzsQ6YiJls0TbYNpLWoTGftRgOq/X9G97gz4ZC/wMYrjAF6UPUyhTiPRKFq8D0/QsQvul+WkMfXdadhhNRdHnhamP20WZJdfgnyXwEOzC00GbQLl3ZqJKFi9YaHoWIVja0H/jqyuvsf+EesuUh+YUI3IWZAWR3shnKq4BNulj1Dvr9AsPJ2UWQ5tKnzNIkcWw0CJ2qTmw2Zh7lRF3Tb5dMbw1f+eeOTDWiK4+Rnw1tcdP3f9qD8Lorn4U0iYoXACPSfHn4SWquXTuk5O3BFbko365Rz/XKnSRomeYH9QYwZdolNCqgGVT0Y+J25yFbCWojFilwwQXwEyFqzq1vhnnKGWzNbNNLrBlFEzN843qcBNsB1PAzEaL6HDm4UKWHoi2QJCkoSFMZ6PhMddAFhj5nHIm8xhYTHXCp+QyWH1yRvsRZFGQFK1sPUt3t1GCK64hT0SPIHu5iXZi0UZVoh7YUQVaZsFh3HisJa3u5+2ocIC8cpuMUBUsSLTnzH+LOZvPnlHH33dteXTqo7oCe/69hCVwlr4nkZhHqUokL9AFBykexQM0a9Yj0Z4b/y1XJqqw5MFKvyWJvN4xO1+B9jQybJVkKnJmfWxLpJguKXZkNZ9sOar9UUKkBovOxu7Yt6GrkueTO0blYgUcR+JeTU7ywsdGPNwhPjU3izb7RYvd+ekBUwhcG4VgNtK2T8SPIC5bJpVT6IcvxuVcTK4KmnVFnzxsxE9+WLmiVoOf+wCmZuvJD/Iqh7VlH8mXc+7+mD4lcRpsRk+30nRM29Dbj8tPr9HSotLXDPJq1MJhFez2MeAjbzp9sxMLWnMzokigB38yXriJQAbpC4HHqPSYJ7yoMy32FC7E14MIQt/Ro9LZLELPd2w1f6vuNEw0ouVWj39mYqttt8kFcFAfZR4qQuVpefzEULQEtur4ScWrTVMB9sRXbX/4j77bEad0+ao0S5SWTj2d01GPvwYc28QTOp5T24y4aySXNgvmmtShrcIgC8IH92d3n8yFUeawePX5QIRVRc0yTv5fg6S8C8KizEA2uhmI7JwGSBUcAFuQ9CyDMZr0CuuTPRx6sLlJSSIG1aU5ZV4Rshm/wsFkXtHmunbOZ8t1Q0qUiUA5Au7T5oSpOJDajmbEhfVZdVFij2cFYcoORtjpWDvmKOdaDlN7ADWhbWt6/hrRsmXCXs1t2vTcdV3GYAWWJbZqnM/x9TgXoV6NgxFhNF0LE2Qtqw3IL/H0Sqf1KJhOnwEm4zyqxXTsdxYUYIsiiY5tumg6pq+824/LN1m0tCzDXNEyguqWpzWBnqRP051Y+ghq+HFi0JlxR9ylL3jaiZ4Rqy1qy5sX042+5Zlz3ky2vKvgBSUF+AqK+3SjLO6oVvThkS4Zxz2C7FPMiLJqfg6W1FiMwI0SCpVOmhQvzI0lFyUW58qSowtIS5aciZREaNQ41BgthA87xBlUDovPCIo0+GrSvEooHKzmvks3Y9IbkUWjJVvqOhWzvrSet1RMxHCMwIEyGlW677HE7FhzUfvvfBlzdMloy5cz2YLJZwfqZhmT5yS+TspEl5LTsrjWQbw1EdOnZK4hmWBmrLgo+ThPZhzffGPFjTORjFcg20V5Dj8gMr4GWNqvia8gTDPx80umInVAZQrHOT7zSBIhRtbwY8eRDqUfzuTTp2TF6WE7nZi8B+FDWvaDXjCfxbs3U5MSTmzpOKKTS5YcNZ9hLQbG0w97CihU2sP7TSdMGhiWGaR4Wxup1hdJhOA6Sowtvu7td9zL/tcvXHYqSYYepT6JmZgNfzR5RuUUuZpnb2knuURJUfOb1NSMQdwypU6m037byfgTscEjnIkv6XZN/I0mUsiS/XpUvsp+2SgMSfQqwsZX1kvZmPnhO5Iole56KM6C1RaRt3A6rhozW6EuO02eqNA9//gL/tJnncNgmcWwysndXRTDL2DtMPeyTCtvO6QBdV+Xnnq5I0Vp66kQm3DLiSsDhsfIUd42n5hjSyO+HoQpv6NHR56gttPdbUIlHwX8iXZB9nL+HN4HyRZcwxVxBjV6kIQvEvaqK9Cs1XxUlzIYBfoOsvriiSd4dPnhh4oOJROleALmwRr4xytPTMAT1MhPxwzhPdiUMbgJ8ofG6kN+EzvokJWoCaUKxvGBYIkQouTXwiMeOk9cx6FMpVuy3WS89/cSlGBzvgui+KQogvAem6bHfpHExaIXvJlbY/lvlfiEqXQ8/cMlCfMgyRpsB/inqcSwUFsw1ul7hgzIHcFZ8mCN6RzYcF0REXLePxOzTVUiJ5kuGGXfJJAX8ZsnLhMPlXcuU+qOxG8OvEWIOCEpdhN7FMJNlw0mk21Ey8nY71OalbtVeZuHWVRlznJ9HyJWypi+KYic0sVbq1madGxNE8aYjh6DAnwFeQ634fWnLN2NxyV0573QPnTR4vmjR5BKj+RkzIVBbtJX9pgJe3RTMRlzXOz2aYZiRN2hcM8+7U5iLqFwoCD2Sg7CYETTpGEqipLtpMai8+fpWYXCgT68PR8eq9A0LY5VYEdxWnm7cmmwn1dPR6Y++hyEyEI/53PtuXfCbARdKn3W+E3GaKdB+HCRrIo0fPB6TvLCZgLkKZSEdRavL4koW8ShS8R388/vMz+mGzEmqg3PTZ/0JykvCrCrHZXQT8n9SlJyvILIzyNlziBxZnAhizxdpbQEj8BdPZJUesRNJjMMpHH8Iy2gsljzFJqeDig2JbU7KfxeIE3uJl332w1usnVb7l7blmlYFfr9U9KUKVS4i7baLBsKxrAxSEdeq88p+At9OEnyJ4leRlTpz2rzeZxXwTY85mpjFAzXjHirQ3EyLvsGnvKzdIePmYoCjGgytuwiuyZBUt8PSGIJh1qru5HZCP2NNnIhc7QV+lNYfRxFQhlwkiO5xB+eefBPg9t0J8M43WqKI6LJ2OKI7Jo6AZLfD0gcCYdaq7uR2Qj9fZXmYuZoK/SnsPo4jl1Bn5MciSP+8MyDfxrcpnUhQRhA1Q6oCiSq0dgiie6ccTrpSg5ILEkGXKvD0Rmq/SXkkq4GdypHkk6GTOVIQglGaS681LaY0EUF21avwWMEnj6DeH9XxgnIc2WLlKD96JYpER4cez+n0gFJNLUZ0ep7Sj6kCoZud+pawjmfjKPcXtjwBmOGzEQ1ngdnGYm1SWWZOjMfjtRakKhawL2zITst745Zn4fGvVk+xyFk0aKCLUDWeFSkG/ApyvLiY1AEt0HOHvtQqxUoWtfVfRS+fVN9Jiaz/o780HbB7283tymc6+A27powAqcHOHjucpmx4MlSbidkhYGu4F8FjuTKdNOW8LpoCwfAf0nDII7+BTbNjHM64tThdcmpNtR5kGxL7FjN9tkWcbtqS1XIA6siwzdGeRUcXUAhU01IJFNzAAs2aDCDAVuF1ztbS73nlIgKLOuerDeAA1l1ABE66AzTP13M65auMdBbLzY3012vnNdfk9ZmkDCxHJALAWUJ0CUKZnroing9dKUDPVB55Nle6GJeT3SNod66F/hsV10Zt5+uWKmTNN7wKSJLxR3VFVS6ap+D8/tqi4WdtTWGVnS1D8sGka3CXcd1Gu222lp1Lbc6mahnaYfDKwrxLH9BNSXc9dQUDm2oBVx4EJdHqCrxRq9Xzt1c6SpD6xfE0SPIXm6iHa8/upi7jqkaapNzWiabGFQ3vaJpIutIJoysptt5G9MaMljBXYrDTZRQY1qpYSqR3kwN2SLqEiXrraEBycutJV3MRE1lXJjzt3B7bmvIdui2kmK3kv7kHQ320MY8ZDpoS3jw28IB8N3lJQO/K+J10JUO9EDcQDBdEGW8PnBxvlbvRnRYIgslHakdnHBlUSdS+HUNFfh/ZGm5F3VSF0p6qmsM9NQ50jD9dEW8XlDpsHggXAlZqUAUcoUBUa7Qj6AHMWwtZV985uDWGlL9VWXa+TPcBZIgPimLe2SACLGJQXj8klfnISVvMYBdnSCBQaP+zuuvLlI8K3xHz3vbGGbCcwNdTXaGoGsOYPFHdAf3zWwzgAW/Gg8Lfk1FLCQ9y3tTm0aBECDKhNOppFPhmt/wvS+/k6pM2ElVPHRgBk+Q4Ks0SopcPGHcWtzDM6/ikF5+H+13fPWjK+Jq422pYg+CGaOLZT0pzdvPII5BIR7MXjmvv16VwTmEah+UQP8sAVdh6JXz542qMjieKRbGcK2Ix7RfhT+u/Vpq2vNqD8Lorha9RNojkR4tqi/TqPltlHVrfvPL+qkaO17S6tyxk7Ywwk4ZLx2MVOf0JuDaJ4lCyWzhcrV+fgRZFCRd0qWzdHcbJYFgXlQaSfCSthvA9+9lgL98TyLeqqaLeTjQNcxGR31I5Kup/l9/HfUbihFSw0SbL3tLK8e24UEcyMoq2JD1jfBSxkkHH1OuqbO8qbJOXV1jHdUthpTi9CmJ04CnCHZFXOW3LR26D4xAdpVFXHsiUca9C+yKVezMfPHYFQntyyqisaqYBeGDsI+qUNxLVT7ELlWeqCYzO8sfdDmXIegqCvdB57LLoKZQdBOkpFuS2ea52iVZQaRfknWGjtlswkf2kM3W4R6x2Wq6nYsWu6iiEhpqC1yYno2jAwtq8rVhQWVNdBTwUENg2OBJJnNijZ5kKdfwSVZQ7wp1Ie+uqjHQZVVp8EKfR5mQIiVKuoQ2LA1dGRf7rnhwuGKh8Yko4w9RrGhW+p6JOyHKeJ0QxUNmFZAAuF/JhA5bhWtmYWoN7bIQBsAqwC333rNXzt1t6SqDV4U46QQry6vv/KtBXDRoIG1yYnCsok0R3xTalKqgLra90sVCQpRPubzMCWynvFrcvnkVNVAY6Hu4U4VDGTcaMed0xq3HP6Zxq6ojIu9+sNNhkzoVeZw1oVPFXJM5VWPQr2a3D6It70DTFfH9aprSQW0XB8G8Abt9zJf+TA2+3turpHBx9AUUBcjosEVM76KKokslXl2V67IBNDh1JJdnWt33w6tyDop0BdlFZ6/q4OQHeZmBnyDa3vO4uVfOn3iqilqHHyO4rHP+SLNVJN0StQZ67sUnZbrtlfP67FUZEsUvSSjZeshSriAmKwxe8/XjHLJyianCv87r11LqWTyqvXJxn6qjKox3xnQtrMn1RRBV1vAZlIlQbrUhH0JlYdr43UgwYKvIvDGVe24szuKOmRoyw7Vqt9cAVduIXfD6FfjnSrrO0CBnaZ5D4LG4V7YKd5CZWoNCs4sFxxGXXSFfUHblCv2Qcd64fZEVRP2RdRSv6iUuaGwV2RX9+mS/jyOwuUnr+hEYdjRpfCvErlJMDZmLRl1JoeOaBVRufRQvf9ZdPeVrhdb5dci4SteR+iavdRBoJm/AEZBfTY0byDbD89K0F691poYaGnV1dQyGnPgF9STO/OsWK2XXyHrlDeEiqCfDRd9llDR5DyEkqyzDimqnfstNGa6HcJPWliFHN9T2b0X+Y4MTKa4rnUyimfqEMhH62Glkqoic8uhaCmfQNt4N9+DZlopOm20FBSdZcVdUqchVVrWrXpQM/km2KxceJrsqansY50m7aCvjVJXsaJzaOhgNoaHQN9Mh8V6TNMqzj+LW3Rs9ok1rr5c1oHp8Q77P7T8YhCiIHwPSLSUPASsoKg/73tHkqw4N+ShyeFz4tb0NCu/xZzci0vec+sNByUnuS8KcNy4qzcRk8t4eYgplbwopCNJnlBiU2qtI/QGTKffrVc2l7ICpNBOT239xiCkUvSbktuwfaigIwpePjofnJsi2yJFGc3jqZhJ+4hEnImrS4SGeMULNLsObKDsanFpiEjgPKzH2kheTovaoc1UYxsSjA1kDUkQ6Ucc34VFPBIlftNoQTL8olZEty+PbR773xrXDX/B2dWTy+89eiRThnCGQ1HZGBL2HCB7uVtsHU2g9HNQj1XX7OpYdCX5Fifij3+dW0o//+JZqx314i1vL39Mac0JDy+XdZRZto0TCBkxV9+T3DCrktDskmX4rLKaXqifZsDhvl6vNSvImeXyyhe+A1+TjZeFoKDUfJJF9ck0SK35FLYcleEHNBz30CtrBANdD0k6hwqD2mjjhFZcT4GRYtAbkAIeiNWd26qtwJNi6PpRtwRN/ciAcqtp90ob5ganqjRvGHYJeDALywkKshgnbSJQxzpVJpY9JotFwVTIRIG65l2Ea5hZudW8cM/KQ1NeGMi21X8W9gta7AsXtRKEgzEns6bpiQumKztXqEUlvzGzr9nZYYrkSVx428HGNe1LCBdffFACvAyG2UYkrO5nHOQzDaRSjLMAtZMkg9Kr6GwIlHrIguomc24EWU83U9bkEetF/KACikD76w9CEvpFeg7CVfNx+9MP34JbC0DwWpErtUJxafuxPo5F7leGrQzWqmcrLJL69N5aua04tHyuaiRqFmxJf3ZCLIz4N09ur5o1gKn5VRzE/LpUhyWsyhJSAXrLOAMpUoKoOZW4AKrY129DtzNYgpdNa1bFGdJrZxCuRBCtbsUQ9h6iPvG6byGTS3Zet5GP37cdPwy27j+5Ile5DnFp+NqDRyFXZfcWVl0k88n+TsjRdwQc7k4H0KkMJL1CeGWlVLL91BZFPHFnFEkmmBR2HsG0niDBo5xNUO84onY649X2ekES+PyrxCd0OS+WwpDc0uI0akWZOUtMMkywOo5SN1Br64CeV2JQYmlaMSf2hw+EBFYww3HruLTBUUEzcih/z0pRQGTP0aviYdX/k8WJ0rjt3PbH7G7+BxBvA2i+QFxGUgiKN8OloaOpxVxyXqrb1vM5iONqoo+Ix6FdxSzgTMbVr50gX7PCX3KmxldzfpPkntYlTu74qs/A+yMHmZ1TcEz2whA81cUYO1bYfiRc3FUbZNR8Iat128MXDwG/giBBuS+GKVwpx7GhovpPzrjw+dCu3cmGq4WkjJA8IRKKKB4FIRnfu2nEDN+uTyInFLFWFpPV9KEaS2NMYiEowaTfDMrguhhu55Y5phqYJcy1eE70abonuBwLHzYRBvs3Jw/t/B1dMJV3REeLclsz+Ko04bnoQap7RDh75+hWXdOajYqdLBZ6gpg9Rx40Jj5vLQ73bki9cyLxqblfzeCRTwesHtjhuTT+bGycofy27ZbH2LbxqFJa3sK77Fe7br6afO2DA419WffAM6MTDX5Q1oZb3A5kQHAyQbGWIK/tYHJMPhcREIKzr3lIw9jBcybJhKLgiDzR0P0DSfnnQFFNQGHCQJNuGIFUIl7kMwEj4Z3hc1AZEASqdZUACW5A5wDW3yjvXZWEptJGHaqYrgA22JfPmltT2JibYkGIkBLLUy2CIPboltQeJElDDIWMeQyLJeKP6RlUZhOSexdXzVYVsQeSI6uT78TLEw5u4SmtvS3Rpw0mnUDIaUwqEeIB42Z7wUMiyOC11iA3YdHzeVBktd0PEyQTFJM2SDJdS88EhkNKupj2K8ZHClOS/cjWkzHiQfaoOrAzIiIOiAF0Bsv8FrsK6w21H5NtphQA3zdla2K3ykMrhjDIoKnCpTFIy4PzsUOZqvOI2P/perrKVmThH1ent5C7ynFpihG28xPtp+ipFXZSCz3yWV8FuH4MOsHieezUdoT7iDLfJBQc9hwU1h62+5v7CTNJE3FycD9GGfImiyVZyr1r6J7WJy9CGM1OIKMHU9aZTjxb0jArCLjNM8Sv6EWzCIPM0DGGaTuthEJuk+BUHSOFSMGSJmmgQcEJS+QbHreeRE6gEq8QAclOnGpBNhdKREs6v6Yl0k2g9TiLCUvkPtGLmcltKtABr93jhIPEreB8snQi63JaO+WGmg1ZvZHrM1W/kk6942zdT5nNgdBip38i9VjLRsNTJIpTYpF/XJ3dwc2JQYHo1PAyJAoP064oJExMkJWQmg9MlhpFt3pxafjZurQCMNuQqLA9hXUfoc1uKOUBQw8OQSMKEier6XR7TDQ6doUiBaeQNJLsL2Y7eY+gS2fl3MGcTC3CkAZP4KUgb+OWrOQwYglcBa4jnOhGztfwOTdchA8AZ6b0MWwqra6CFmBy6IfPqgSwaeDkxkHmMA9I1x4gGQbzGBlr45aR5DBqV1Edl85fV92moHswfx4AbZ6iGY4dy63tWDCYfLG4qJjJrVB0/g7PxqbWU7F5MJ+SexRTK9sHJMk6d3YPwIS37t8bMZy7fqbaV8A/bD8k3bKmMF7noyAG6ulOGc1xmKJlcraqsL5KoiIJY8rZU1kBMpNm7Urqv3sOkfqHzwWgenLFdDY+LsK0ncqWQ+I9yBXWsh/EkLKJH2M2XdLsm/kb5HbmRxCXVxSQygAnSmDLJUBF1RTAcD4nsYRO33vAupv+maRyyh8j1TmafPEezeXcXxfAjWEseL7KVJLNh+Gqx7YNu2X11wLhxL9aYyDVDUNOnxtt22WPh2FnssRUUicjxINoF2cv5c3gfJFtwDYf2DMpOkISCTGVDjWSD0sClBqT5KDstoV7pc1H1xe0g4B/K1NO1F0Z2eA82UOm/CfKHZjcnv3GJH2ojIYQCTdJDFciGgu1cCMjdbv/3EpRgc74Lohhqp0F4j7VC6aXAUBM/NwTcXnsOj9waPodoJc5+qdBqYKBa4P2xagvmPlzrCnoojBEorCsmjAZJ0EMXqA2NaEC8cQ3VhyrTkI2ckjUTjsHBx1flbR5mUfXwYTDeP7+2Dx2U6Y4CwSl14GUZPUJ14ytUH+EqX3/K0p1sPGTVfQwI3V/PLYIucj0UN6nGQBCVlz4MF7t9miFnkTvkRDmkxUhq+9RfqG4pEL0S6+E4f9YZDkltn8NBdUtvVM8+huMj2MdpZdNrQYsGg1PXOSEcGF23HDBkofWQnAbhw0UC9aTwQXlfGWzjQ4oIOqUACet4GyaJW/9QE5nANPOjm3aIvoKkRBFxauMI+sl372arydT7pOSYPsjPUv9usi8GAllkvwGncfwjLaJkW1OGLLJnaZIXWRBB8YG0jXaFNJnkbtJ1v50opYQT2MML0yDtHM27DDkkEzOF7oedzIWhMJJEdTlRWmk3Jh0SlMnlLN3hTUuRUYgmogQ1FhD9cx2FPwGD+u50YLt0OfLhauvJkVdOxzMB2Sgvn+bsE01E6QYtIPrnJwp/cvckvzsd2C75oXy42npy5JWTK05AdptB7QeK4q04/1QjaWo4c6j++apHRd880pU4HmQiad3QyHVVFcjgkzCTIahV5mucU+0ziPd3ZZygVN6q2pOgveQNr/u+RtDYhGSyxw9OJX/TRBVoDTrdUpF0Mb3LGT4jdhscWjOoI3Ou+vRZD/LwW/5eRfdHffek//auAoIGHs4yyNqy394hU94uqD/An9Wt2td0A+Icf/3t3XUJW+9A9esjyKNtB+I3CDMBIeqzA9rUuUju0BPDPcgwASRGTZWmuDU0FMEmKIKTrIjQO1JYHMK1hN0tsbsguuG6BZuL5LIs9mUBSQa725i6A/7tnbz/394xOP9Wx31yQQJEM4IkgMvktIziTYv3pyDOe1ufCMQZHP0/APxezSVcmgXYvrSQvqWJIqB6+D6CPUg2cMndgN0+hsDyy2QVPAIT3L7n4AvYBuEL/P4YYe9HEZDhiaCH/bePUbDNgl1ew+jaw5+Qhze75//z/wHT6TpY0twKAA== + + + dbo + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/202006250801086_V400Resources.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202006250801086_V400Resources.Designer.cs new file mode 100644 index 0000000000..395c46d11f --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202006250801086_V400Resources.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 V400Resources : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(V400Resources)); + + string IMigrationMetadata.Id + { + get { return "202006250801086_V400Resources"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202006250801086_V400Resources.cs b/src/Libraries/SmartStore.Data/Migrations/202006250801086_V400Resources.cs new file mode 100644 index 0000000000..6ec22fa5c1 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202006250801086_V400Resources.cs @@ -0,0 +1,901 @@ +namespace SmartStore.Data.Migrations +{ + using System; + using System.Data.Entity.Migrations; + using System.Linq; + using System.Web.Hosting; + using SmartStore.Core.Data; + using SmartStore.Core.Domain.Configuration; + using SmartStore.Core.Domain.Media; + using SmartStore.Data.Setup; + + public partial class V400Resources : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer) + { + // MaxUploadFileSize setting is stored with an empty string in some databases, which leads to type conversion problems. + Sql("UPDATE [dbo].[Setting] SET [Value] = '102400' WHERE [Name] = 'MediaSettings.MaxUploadFileSize' And [Value] = ''"); + } + } + + public override void Down() + { + } + + public bool RollbackOnFailure => false; + + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + MigrateSettings(context); + context.SaveChanges(); + } + + public void MigrateSettings(SmartObjectContext context) + { + var prefix = nameof(MediaSettings) + "."; + + ChangeMediaSetting(nameof(MediaSettings.AvatarPictureSize), "256", x => x == 250); + ChangeMediaSetting(nameof(MediaSettings.ProductThumbPictureSize), "256", x => x == 250); + ChangeMediaSetting(nameof(MediaSettings.CategoryThumbPictureSize), "256", x => x == 250); + ChangeMediaSetting(nameof(MediaSettings.ManufacturerThumbPictureSize), "256", x => x == 250); + ChangeMediaSetting(nameof(MediaSettings.CartThumbPictureSize), "256", x => x == 250); + ChangeMediaSetting(nameof(MediaSettings.MiniCartThumbPictureSize), "256", x => x == 250); + ChangeMediaSetting(nameof(MediaSettings.ProductThumbPictureSizeOnProductDetailsPage), "72", x => x == 70); + ChangeMediaSetting(nameof(MediaSettings.MessageProductThumbPictureSize), "72", x => x == 70); + ChangeMediaSetting(nameof(MediaSettings.BundledProductPictureSize), "72", x => x == 70); + ChangeMediaSetting(nameof(MediaSettings.VariantValueThumbPictureSize), "72", x => x == 70); + ChangeMediaSetting(nameof(MediaSettings.AttributeOptionThumbPictureSize), "72", x => x == 70); + + void ChangeMediaSetting(string propName, string newVal, Func predicate) + { + var name = prefix + propName; + var settings = context.Set().Where(x => x.Name == name).ToList(); + foreach (var setting in settings) + { + if (predicate(setting.Value.Convert())) + { + setting.Value = newVal; + } + } + } + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + // FluentValidation splits messages by dot for client validation. Don't use abbrevs. + builder.AddOrUpdate("Validation.MinimumLengthValidator") + .Value("de", "'{PropertyName}' muss mindestens {MinLength} Zeichen lang sein. Sie haben {TotalLength} Zeichen eingegeben."); + builder.AddOrUpdate("Validation.MaximumLengthValidator") + .Value("de", "'{PropertyName}' darf maximal {MaxLength} Zeichen lang sein. Sie haben {TotalLength} Zeichen eingegeben."); + + builder.AddOrUpdate("Admin.Configuration.Measures.Weights.AddWeight", "Add weight", "Gewicht hinzufgen"); + builder.AddOrUpdate("Admin.Configuration.Measures.Weights.EditWeight", "Edit weight", "Gewicht bearbeiten"); + + builder.AddOrUpdate("Admin.Configuration.Measures.Dimensions.AddDimension", "Add dimension", "Abmessung hinzufgen"); + builder.AddOrUpdate("Admin.Configuration.Measures.Dimensions.EditDimension", "Edit dimension", "Abmessung bearbeiten"); + + builder.AddOrUpdate("Admin.Configuration.QuantityUnit.AddQuantityUnit", "Add quantity unit", "Verpackungseinheit hinzufgen"); + builder.AddOrUpdate("Admin.Configuration.QuantityUnit.EditQuantityUnit", "Edit quantity unit", "Verpackungseinheit bearbeiten"); + + builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ApplyPercentageDiscountOnTierPrice", + "Apply percentage discounts on tier prices", + "Prozentuale Rabatte auf Staffelpreise anwenden", + "Specifies whether to apply percentage discounts also on tier prices.", + "Legt fest, ob prozentuale Rabatte auch auf Staffelpreise angewendet werden sollen."); + + builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.ShowProductBundleImagesOnShoppingCart", + "Show product images of bundle items", + "Produktbilder von Bundle-Bestandteilen anzeigen", + "Specifies whether to show product images of bundle items.", + "Legt fest, ob Produktbilder von Bundle-Bestandteilen angezeigt werden sollen."); + + builder.AddOrUpdate("Admin.Common.CustomerRole.LimitedTo", + "Limited to customer roles", + "Auf Kundengruppen begrenzt", + "Specifies whether the object is only available to certain customer groups.", + "Legt fest, ob das Objekt nur fr bestimmte Kundengruppen verfgbar ist."); + + builder.AddOrUpdate("Admin.Permissions.AllowInherited", "Allow (inherited)", "Erlaubt (geerbt)"); + builder.AddOrUpdate("Admin.Permissions.DenyInherited", "Deny (inherited)", "Verweigert (geerbt)"); + + builder.AddOrUpdate("Admin.AccessDenied.DetailedDescription", + "
You do not have permission to perform the selected operation.
Access right: {0}
System name: {1}
", + "
Sie haben keine Berechtigung, diesen Vorgang durchzufhren.
Zugriffsrecht: {0}
Systemname: {1}
"); + + builder.Delete( + "Admin.Configuration.Measures.Weights.Fields.MarkAsPrimaryWeight", + "Admin.Configuration.Measures.Dimensions.Fields.MarkAsPrimaryDimension", + "Admin.Customers.Customers.Addresses.AddButton", + "Admin.Address", + "Admin.Catalog.Products.Acl", + "Admin.Configuration.ACL.Updated", + "Admin.Configuration.Stores.NoStoresDefined", + "Admin.Configuration.Acl.NoRolesDefined", + "Admin.Common.Acl.AvailableFor", + "Admin.Catalog.Attributes.SpecificationAttributes.OptionsCount"); + + builder.AddOrUpdate("Admin.ContentManagement.Blog.BlogPosts.Fields.Tags.Hint", + "Tags are keywords that this blog post can also be identified by. Enter a comma separated list of the tags to be associated with this blog post.", + "Dieser Blog-Eintrag kann durch die Verwendung von Tags (Stichwrter) gekennzeichnet und kategorisiert werden. Mehrere Tags knnen als kommagetrennte Liste eingegeben werden."); + + // Granular permission. + builder.AddOrUpdate("Common.Read", "Read", "Lesen"); + builder.AddOrUpdate("Common.Create", "Create", "Erstellen"); + builder.AddOrUpdate("Common.Notify", "Notify", "Benachrichtigen"); + builder.AddOrUpdate("Common.Approve", "Approve", "Genehmigen"); + builder.AddOrUpdate("Common.Rules", "Rules", "Regeln"); + builder.AddOrUpdate("Common.Allow", "Allow", "Erlaubt"); + builder.AddOrUpdate("Common.Deny", "Deny", "Verweigert"); + builder.AddOrUpdate("Common.ExpandCollapseAll", @"Expand\collapse all", @"Alle auf-\zuklappen"); + builder.AddOrUpdate("Common.Trash", "Trash", "Papierkorb"); + builder.AddOrUpdate("Common.Cut", "Cut", "Ausschneiden"); + builder.AddOrUpdate("Common.Copy", "Copy", "Kopieren"); + builder.AddOrUpdate("Common.Paste", "Paste", "Einfgen"); + builder.AddOrUpdate("Common.SelectAll", "Select all", "Alles auswhlen"); + builder.AddOrUpdate("Common.Rename", "Rename", "Umbenennen"); + + builder.AddOrUpdate("Common.CtrlKey", "Ctrl", "Strg"); + builder.AddOrUpdate("Common.ShiftKey", "Shift", "Umschalt"); + builder.AddOrUpdate("Common.AltKey", "Alt", "Alt"); + builder.AddOrUpdate("Common.DelKey", "Del", "Entf"); + builder.AddOrUpdate("Common.EnterKey", "Enter", "Eingabe"); + builder.AddOrUpdate("Common.EscKey", "Esc", "Esc"); + + builder.AddOrUpdate("Admin.Customers.PermissionViewNote", + "The view shows the permissions that apply to this customer based on the customer roles assigned to him. To change permissions, switch to the relevant customer role.", + "Die Ansicht zeigt die Rechte, die fr diesen Kunden auf Basis der ihm zugeordneten Kundengruppen gelten. Um Rechte zu ndern, wechseln Sie bitte zur betreffenden Kundengruppe."); + + builder.AddOrUpdate("Admin.Permissions.AddedPermissions", + "Added permissions: {0}", + "Hinzugefgte Zugriffsrechte: {0}"); + + builder.AddOrUpdate("Admin.Permissions.RemovedPermissions", + "Permissions have been removed: {0}", + "Zugriffsrechte wurden entfernt: {0}"); + + builder.AddOrUpdate("Permissions.DisplayName.DisplayPrice", "Display prices", "Preise anzeigen"); + builder.AddOrUpdate("Permissions.DisplayName.AccessShop", "Access shop", "Zugang zum Shop"); + builder.AddOrUpdate("Permissions.DisplayName.AccessShoppingCart", "Access shoppping cart", "Auf Warenkorb zugreifen"); + builder.AddOrUpdate("Permissions.DisplayName.AccessWishlist", "Access wishlist", "Auf Wunschliste zugreifen"); + builder.AddOrUpdate("Permissions.DisplayName.AccessBackend", "Access backend", "Auf Backend zugreifen"); + builder.AddOrUpdate("Permissions.DisplayName.EditOrderItem", "Edit order items", "Auftragspositionen bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditShipment", "Edit shipment", "Sendungen bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditAnswer", "Edit answers", "Antworten bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditOptionSet", "Edit options sets", "Options-Sets bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditCategory", "Edit categories", "Warengruppen bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditManufacturer", "Edit manufacturers", "Hersteller bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditAssociatedProduct", "Edit associated products", "Verknpfte Produkte bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditBundle", "Edit bundles", "Bundles bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditPromotion", "Edit promotion", "Promotion bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditPicture", "Edit pictures", "Bilder bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditTag", "Edit tags", "Tags bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditAttribute", "Edit attributes", "Attribute bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditVariant", "Edit variants", "Varianten bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditTierPrice", "Edit tier prices", "Staffelpreise bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditRecurringPayment", "Edit recurring payment", "Wiederkehrende Zahlungen bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditOption", "Edit options", "Optionen bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditAddress", "Edit addresses", "Adressen bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditCustomerRole", "Edit customer roles", "Kundengruppen bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.SendPM", "Send private messages", "Private Nachrichten senden"); + builder.AddOrUpdate("Permissions.DisplayName.EditProduct", "Edit products", "Produkte bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditComment", "Edit comments", "Kommentare bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.EditResource", "Edit resources", "Ressourcen bearbeiten"); + builder.AddOrUpdate("Permissions.DisplayName.ReadStats", "Display dashboard", "bersicht anzeigen"); + + builder.AddOrUpdate("Admin.ContentManagement.Blog.Heading.Publish", "Publishing", "Verffentlichung"); + builder.AddOrUpdate("Admin.ContentManagement.Blog.Heading.Display", "Display", "Darstellung"); + builder.AddOrUpdate("Admin.ContentManagement.News.Heading.Publish", "Publishing", "Verffentlichung"); + + builder.AddOrUpdate("Admin.Validation.Url", "Please enter a valid URL.", "Bitte geben Sie eine gltige URL ein."); + + builder.AddOrUpdate("Common.WrongInvisibleCaptcha", + "The reCAPTCHA failed. Please try it again.", + "reCAPTCHA ist fehlgeschlagen. Bitte versuchen Sie es erneut."); + + builder.AddOrUpdate("Common.CannotDisplayView", + "The view \"{0}\" could not be displayed.", + "Die Ansicht \"{0}\" konnte nicht angezeigt werden."); + + builder.AddOrUpdate("Admin.Catalog.Products.Fields.Visibility", + "Visibility", + "Sichtbarkeit", + "Limits the visibility of the product. In the case of \"Not visible\", the product only appears as an associated product on the parent product detail page, but without a link to an individual page.", + "Schrnkt die Sichtbarkeit des Produktes ein. Bei \"Nicht sichtbar\" erscheint das Produkt nur noch als verknpftes Produkt auf der bergeordneten Produktdetailseite, jedoch ohne Verlinkung auf eine eigenstndige Seite."); + + builder.AddOrUpdate("Admin.DataExchange.Export.Filter.Visibility", + "Visibility", + "Sichtbarkeit", + "Filter by visibility. \"In search results\" includes fully visible products.", + "Nach Sichtbarkeit filter. \"In Suchergebnissen\" schliet berall sichtbare Produkte mit ein."); + + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.ProductVisibility.Full", "Fully visible", "berall sichtbar"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.ProductVisibility.SearchResults", "In search results", "In Suchergebnissen"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.ProductVisibility.ProductPage", "On product detail pages", "Auf Produktdetailseiten"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.ProductVisibility.Hidden", "Not visible", "Nicht sichtbar"); + + builder.Delete( + "Admin.Catalog.Products.Fields.VisibleIndividually", + "Admin.Catalog.Products.Fields.VisibleIndividually.Hint", + "Admin.Promotions.Discounts.NoDiscountsAvailable", + "Admin.Orders.Shipments.TrackingNumber.Button", + "Admin.Catalog.Products.Copy.CopyImages"); + + // Rule + builder.AddOrUpdate("Admin.Rules.SystemName", "System name", "Systemname"); + builder.AddOrUpdate("Admin.Rules.Title", "Title", "Titel"); + builder.AddOrUpdate("Admin.Rules.TestConditions", "{0} Test {1} Rules", "Bedingungen {0} Testen {1}"); + builder.AddOrUpdate("Admin.Rules.AddGroup", "Add group", "Gruppe hinzufgen"); + builder.AddOrUpdate("Admin.Rules.DeleteGroup", "Delete group", "Gruppe lschen"); + builder.AddOrUpdate("Admin.Rules.AddCondition", "Add condition", "Bedingung hinzufgen"); + builder.AddOrUpdate("Admin.Rules.SaveConditions", "Save all conditions", "Alle Bedingungen speichern"); + builder.AddOrUpdate("Admin.Rules.OpenRule", "Open rule", "Regel ffnen"); + + builder.AddOrUpdate("Admin.Rules.EditRule", "Edit rule", "Regel bearbeiten"); + builder.AddOrUpdate("Admin.Rules.AddRule", "Add rule", "Regel hinzufgen"); + builder.AddOrUpdate("Admin.Rules.RuleSet.Added", "The rule has been successfully added.", "Die Regel wurde erfolgreich hinzugefgt."); + builder.AddOrUpdate("Admin.Rules.RuleSet.Deleted", "The rule has been successfully deleted.", "Die Regel wurde erfolgreich gelscht."); + builder.AddOrUpdate("Admin.Rules.Operator.All", "ALL", "ALLE"); + builder.AddOrUpdate("Admin.Rules.Operator.One", "ONE", "EINE"); + + builder.AddOrUpdate("Admin.Rules.OneCondition", + "If at least {0} of the following conditions is true.", + "Wenn mindestens {0} der folgenden Bedingungen zutrifft."); + + builder.AddOrUpdate("Admin.Rules.AllConditions", + "If {0} of the following conditions are true.", + "Wenn {0} der folgenden Bedingungen erfllt sind."); + + builder.AddOrUpdate("Admin.Rules.NotFound", "The rule with ID {0} was not found.", "Die Regel mit der ID {0} wurde nicht gefunden."); + builder.AddOrUpdate("Admin.Rules.GroupNotFound", "The group with ID {0} was not found.", "Die Gruppe mit der ID {0} wurde nicht gefunden."); + builder.AddOrUpdate("Admin.Rules.NumberOfRules", "Number of rules", "Anzahl an Regeln"); + builder.AddOrUpdate("Admin.Rules.OperatorNotSupported", "The rule scope does not support this operator.", "Die Regelart untersttzt diesen Operator nicht."); + + builder.AddOrUpdate("Admin.Rules.InvalidDescriptor", + "Invalid rule. This rule is no longer supported and should be deleted.", + "Ungltige Regel. Diese Regel wird nicht mehr untersttzt und sollte gelscht werden."); + + builder.AddOrUpdate("Admin.Rules.SaveToCreateConditions", + "Conditions can only be created after saving the rule.", + "Bedingungen knnen erst nach Speichern der Regel festgelegt werden."); + + builder.AddOrUpdate("Admin.Rules.Execute.MatchCustomers", + "{0} customers match the rule conditions.", + "{0} Kunden entsprechen den Regelbedingungen."); + builder.AddOrUpdate("Admin.Rules.Execute.MatchProducts", + "{0} products match the rule conditions.", + "{0} Produkte entsprechen den Regelbedingungen."); + builder.AddOrUpdate("Admin.Rules.Execute.MatchCart", + "The rule conditions are true for the current customer {0}.", + "Die Regelbedingungen sind fr den aktuellen Kunden {0} wahr."); + builder.AddOrUpdate("Admin.Rules.Execute.DoesNotMatchCart", + "The rule conditions are false for the current customer {0}.", + "Die Regelbedingungen sind fr den aktuellen Kunden {0} falsch."); + + // Rule fields + builder.AddOrUpdate("Admin.Rules.RuleSet.Fields.Name", "Name", "Name"); + builder.AddOrUpdate("Admin.Rules.RuleSet.Fields.Description", "Description", "Beschreibung"); + builder.AddOrUpdate("Admin.Rules.RuleSet.Fields.IsActive", "Is active", "Ist aktiv"); + builder.AddOrUpdate("Admin.Rules.RuleSet.Fields.Scope", "Scope", "Art"); + builder.AddOrUpdate("Admin.Rules.RuleSet.Fields.IsSubGroup", "Is sub group", "Ist Untergruppe"); + builder.AddOrUpdate("Admin.Rules.RuleSet.Fields.LogicalOperator", "Logical operator", "Logischer Operator"); + + // Rule descriptors + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.TaxExempt", "Tax exempt", "Steuerbefreit"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.BillingCountry", "Billing country", "Rechnungsland"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.ShippingCountry", "Shipping country", "Lieferland"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.LastActivityDays", "Days since last activity", "Tage seit letztem Besuch"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.CompletedOrderCount", "Completed order count", "Anzahl abgeschlossener Bestellungen"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.CancelledOrderCount", "Cancelled order count", "Anzahl stornierter Bestellungen"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.NewOrderCount", "New order count", "Anzahl neuer Bestellungen"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.OrderInStore", "Orders in store", "Bestellungen im Shop"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.PurchasedProduct", "Purchased product", "Gekauftes Produkt"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.PurchasedFromManufacturer", "Purchased from manufacturer", "Gekauft von Hersteller"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.HasPurchasedProduct", "Has purchased product", "Hat eines der folgenden Produkte gekauft"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.HasPurchasedAllProducts", "Has purchased all products", "Hat alle folgenden Produkte gekauft"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.RuleSet", "Other rule set", "Anderer Regelsatz"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Active", "Is active", "Ist aktiv"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.LastLoginDays", "Days since last login", "Tage seit letztem Login"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.CreatedDays", "Days since registration", "Tage seit Registrierung"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Salutation", "Salutation", "Anrede"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Title", "Title", "Titel"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Company", "Company", "Firma"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.CustomerNumber", "Customer number", "Kundennummer"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.BirthDate", "Days since date of birth", "Tage seit dem Geburtsdatum"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Gender", "Gender", "Geschlecht"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.ZipPostalCode", "Zip postal code", "PLZ"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.VatNumberStatus", "Vat number status", "Steuernummerstatus"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.TimeZone", "Time zone", "Zeitzone"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.TaxDisplayType", "Tax display type", "Steueranzeigetyp"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.IPCountry", "IP associated with country", "IP gehrt zu Land"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Currency", "Currency", "Whrung"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.MobileDevice", "Mobile device", "Mobiles Endgert"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.DeviceFamily", "Device family", "Endgertefamilie"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.OperatingSystem", "Operating system", "Betriebssystem"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.BrowserName", "Browser name", "Browser Name"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.BrowserMajorVersion", "Browser major version", "Browser Hauptversionsnummer"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.BrowserMinorVersion", "Browser minor version", "Browser Nebenversionsnummer"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Language", "Language", "Sprache"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.LastForumVisit", "Days since last forum visit", "Tage seit letztem Forenbesuch"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.LastUserAgent", "Last user agent", "Zuletzt genutzter User-Agent"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.IsInCustomerRole", "In customer role", "In Kundengruppe"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Store", "Store", "Shop"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.LastOrderDateDays", "Days since last order", "Tage seit letzter Bestellung"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.AcceptThirdPartyEmailHandOver", "Accept third party email handover", "Akzeptiert Weitergabe der E-Mail Adresse an Dritte"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.CartTotal", "Total amount of cart", "Gesamtbetrag des Warenkorbes"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.CartSubtotal", "Subtotal amount of cart", "Zwischensumme des Warenkorbes"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.ProductInCart", "Product in cart", "Produkt im Warenkorb"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.ProductFromCategoryInCart", "Product from category in cart", "Produkt aus Kategorie im Warenkorb"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.ProductFromManufacturerInCart", "Product from manufacturer in cart", "Produkt von Hersteller im Warenkorb"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.ProductOnWishlist", "Product on wishlist", "Produkt auf der Wunschliste"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.ProductReviewCount", "Number of product reviews", "Anzahl der Produkt Rezensionen"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.RewardPointsBalance", "Number of reward points", "Anzahl der Bonuspunkte"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.OrderTotal", "Order total", "Gesamtbetrag der Bestellung"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.OrderSubtotalInclTax", "Order subtotal incl. tax", "Gesamtbetrag der Bestellung (Brutto)"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.OrderSubtotalExclTax", "Order subtotal excl tax", "Gesamtbetrag der Bestellung (Netto)"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.OrderCount", "Number of orders", "Anzahl der Auftrge"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.SpentAmount", "Amount spent", "Ausgegebener Betrag"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.PaymentMethod", "Selected payment method", "Gewhlte Zahlart"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.PaymentStatus", "Payment status", "Zahlungsstatus"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.PaidBy", "Paid by", "Bezahlt mit"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.ShippingRateComputationMethod", "Shipping rate computation method", "Berechnungsmethode fr Versandkosten"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.ShippingMethod", "Shipping method", "Versandart"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.ShippingStatus", "Shipping status", "Lieferstatus"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.ReturnRequestCount", "Number of return requests", "Anzahl Rcksendeauftrge"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.AvailableByDate", "Available by date", "Nach Datum verfgbar"); + + // Rule operators + builder.AddOrUpdate("Admin.Rules.RuleOperator.ContainsOperator", "Contains", "Enthlt"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.EndsWithOperator", "Ends with", "Endet auf"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.GreaterThanOperator", "Greater than", "Grer als"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.GreaterThanOrEqualOperator", "Greater than or equal to", "Grer oder gleich"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.IsEmptyOperator", "Is empty", "Ist leer"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.EqualOperator", "Is equal to", "Gleich"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.IsNotEmptyOperator", "Is not empty", "Ist nicht leer"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.NotEqualOperator", "Is not equal to", "Ungleich"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.IsNotNullOperator", "Is not null", "Ist nicht NULL"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.IsNullOperator", "Is null", "Ist NULL"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.LessThanOperator", "Less than", "Kleiner als"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.LessThanOrEqualOperator", "Less than or equal to", "Kleiner oder gleich"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.NotContainsOperator", "Not contains", "Enthlt nicht"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.StartsWithOperator", "Starts with", "Beginnt mit"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.InOperator", "In", "Ist eine von"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.NotInOperator", "Not in", "Ist KEINE von"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.AllInOperator", "All in", "Sind alle von"); + builder.AddOrUpdate("Admin.Rules.RuleOperator.NotAllInOperator", "Not all in", "Sind nicht alle von"); + + builder.AddOrUpdate("Admin.Rules.RuleOperator.Sequence.InOperator", + "At least one value left is included right", + "Mind. ein Wert links ist rechts enthalten", + "True for left {1,2,3} and right {5,4,3}. False for left {1,2,3} and right {6,5,4}.", + "Wahr fr links {1,2,3} und rechts {5,4,3}. Falsch fr links {1,2,3} und rechts {6,5,4}."); + + builder.AddOrUpdate("Admin.Rules.RuleOperator.Sequence.NotInOperator", + "At least one value left is missing right", + "Mind. ein Wert links fehlt rechts", + "True for left {1,2,3} and right {3,4,5,6}. False for left {1,2,3} and right {5,4,3,2,1}.", + "Wahr fr links {1,2,3} und rechts {3,4,5,6}. Falsch fr links {1,2,3} und rechts {5,4,3,2,1}."); + + builder.AddOrUpdate("Admin.Rules.RuleOperator.Sequence.AllInOperator", + "Right contains ALL values of left", + "Rechts enthlt ALLE Werte von links", + "True for left {3,2,1} and right {0,1,2,3}. False for left {1,2,9} and right {9,8,2}.", + "Wahr fr links {3,2,1} und rechts {0,1,2,3}. Falsch fr links {1,2,9} und rechts {9,8,2}."); + + builder.AddOrUpdate("Admin.Rules.RuleOperator.Sequence.NotAllInOperator", + "Right contains NO value of left", + "Rechts enthlt KEINEN Wert von links", + "True for left {1,2,3} and right {4,5}. False for left {1,2,3} and right {3,4,5}.", + "Wahr fr links {1,2,3} und rechts {4,5}. Falsch fr links {1,2,3} und rechts {3,4,5}."); + + builder.AddOrUpdate("Admin.Rules.RuleOperator.Sequence.ContainsOperator", + "Left contains ALL values of right", + "Links enthlt ALLE Werte von rechts", + "True for left {3,2,1,0} and right {2,3}. False for left {3,2,1} and right {0,1,2,3}.", + "Wahr fr links {3,2,1,0} und rechts {2,3}. Falsch fr links {3,2,1} und rechts {0,1,2,3}."); + + builder.AddOrUpdate("Admin.Rules.RuleOperator.Sequence.NotContainsOperator", + "Left contains NO value of right", + "Links enthlt KEINEN Wert von rechts", + "True for left {1,2,3} and right {9,8}. False for left {1,2,3} and right {9,8,2}.", + "Wahr fr links {1,2,3} und rechts {9,8}. Falsch fr links {1,2,3} und rechts {9,8,2}."); + + builder.AddOrUpdate("Admin.Rules.RuleOperator.Sequence.EqualOperator", + "Left and right contain the same values", + "Links und rechts enthalten dieselben Werte", + "True for left {1,2,3} and right {3,1,2}. False for left {1,2,3} and right {1,2,3,4}.", + "Wahr fr links {1,2,3} und rechts {3,1,2}. Falsch fr links {1,2,3} und rechts {1,2,3,4}."); + + builder.AddOrUpdate("Admin.Rules.RuleOperator.Sequence.NotEqualOperator", + "Left and right differ in at least one value", + "Links und rechts unterscheiden sich in mind. einem Wert", + "True for left {1,2,3} and right {1,2,3,4}. False for left {1,2,3} and right {3,1,2}.", + "Wahr fr links {1,2,3} und rechts {1,2,3,4}. Falsch fr links {1,2,3} und rechts {3,1,2}."); + + builder.AddOrUpdate("Enums.SmartStore.Rules.RuleScope.Cart", "Cart", "Warenkorb"); + builder.AddOrUpdate("Enums.SmartStore.Rules.RuleScope.OrderItem", "Order item", "Bestellposition"); + builder.AddOrUpdate("Enums.SmartStore.Rules.RuleScope.Customer", "Customer", "Kunde"); + builder.AddOrUpdate("Enums.SmartStore.Rules.RuleScope.Product", "Product", "Produkt"); + + builder.AddOrUpdate("Enums.SmartStore.Rules.RuleScope.Cart.Hint", + "Rule to grant discounts to the customer or offer shipping and payment methods.", + "Regel, um dem Kunden Rabatte zu gewhren oder Versand- und Zahlarten anzubieten."); + + builder.AddOrUpdate("Enums.SmartStore.Rules.RuleScope.Customer.Hint", + "Rule to automatically assign customers to customer roles per scheduled task.", + "Regel, um Kunden automatisch per geplanter Aufgabe Kundengruppen zuzuordnen."); + + builder.AddOrUpdate("Enums.SmartStore.Rules.RuleScope.Product.Hint", + "Rule to automatically assign products to categories per scheduled task.", + "Regel, um Produkte automatisch per geplanter Aufgabe Warengruppen zuzuordnen."); + + builder.AddOrUpdate("SmartStore.Blog.Button", "Visit our blog", "Zum Blog"); + builder.AddOrUpdate("SmartStore.Blog.Button.Hint", "Click here to be forwarded to our blog", "Klicken Sie hier um zu unserem Blog weitergeleitet zu werden"); + + builder.AddOrUpdate("Admin.Catalog.Attributes.SpecificationAttributes.Options.Fields.ColorSquaresRgb", + "RGB color", + "RGB-Farbe", + "Specifies a color for the color squares control.", + "Legt eine Farbe fr das Farbquadrat-Steuerelement fest."); + + builder.AddOrUpdate("Admin.Catalog.Attributes.SpecificationAttributes.Options.Fields.Picture", + "Picture", + "Bild", + "Specifies an image as the selector element.", + "Legt ein Bild als Auswahlelement fest."); + + builder.AddOrUpdate("Admin.Catalog.Manufacturers.Fields.BottomDescription", + "Bottom description", + "Untere Beschreibung", + "Optional second description displayed below products on the category page.", + "Optionale zweite Beschreibung, die auf der Herstellerseite unterhalb der Produkte angezeigt wird."); + + builder.AddOrUpdate("Admin.Promotions.Discounts.AppliedDiscounts", + "Discounts", + "Rabatte", + "Specifies discounts to be applied to the object.", + "Legt auf das Objekt anzuwendende Rabatte fest."); + + builder.AddOrUpdate("Admin.Promotions.Discounts.RuleSetRequirements", + "Requirements", + "Voraussetzungen", + "Specifies requirements for the applying of the discount. The discount is applied when one of the selected rules is fulfilled.", + "Legt Voraussetzungen fr die Anwendung des Rabatts fest. Der Rabatt wird gewhrt, wenn eine der ausgewhlten Regeln erfllt ist."); + + builder.AddOrUpdate("Admin.Rules.RuleSet.AssignedObjects", + "Assigned {0}", + "Zugeordnete {0}", + "A list of objects to which the rule is assigned. The assignment can be made on the details page of the object.", + "Eine Liste von Objekten, denen die Regel zugeordnet ist. Die Zuordnung kann auf der Detailseite des Objektes vorgenommen werden."); + + builder.AddOrUpdate("Admin.Configuration.Shipping.Methods.Fields.Requirements", + "Requirements", + "Voraussetzungen", + "Specifies requirements for the availability of the shipping method. The shipping method is offered if one of the selected rules is fulfilled.", + "Legt Voraussetzungen fr die Verfgbarkeit der Versandart fest. Die Versandart wird angeboten, wenn eine der ausgewhlten Regeln erfllt ist."); + + builder.AddOrUpdate("Admin.Configuration.Payment.Methods.Requirements", + "Requirements", + "Voraussetzungen", + "Specifies requirements for the availability of the payment method. The payment method is offered if one of the selected rules is fulfilled.", + "Legt Voraussetzungen fr die Verfgbarkeit der Zahlart fest. Die Zahlart wird angeboten, wenn eine der ausgewhlten Regeln erfllt ist."); + + builder.AddOrUpdate("Admin.Common.IsPublished", "Published", "Verffentlicht"); + + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Discounts.DiscountType.AssignedToOrderTotal") + .Value("de", "Bezogen auf Gesamtsumme"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Discounts.DiscountType.AssignedToSkus") + .Value("de", "Bezogen auf Produkte"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Discounts.DiscountType.AssignedToCategories") + .Value("de", "Bezogen auf Warengruppen"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Discounts.DiscountType.AssignedToManufacturers") + .Value("de", "Bezogen auf Hersteller"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Discounts.DiscountType.AssignedToShipping") + .Value("de", "Bezogen auf Versandkosten"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Discounts.DiscountType.AssignedToOrderSubTotal") + .Value("de", "Bezogen auf Zwischensumme"); + + builder.AddOrUpdate("Admin.Configuration.Settings.Blog.NotifyAboutNewBlogComments.Hint") + .Value("de", "Der Administrator erhlt eine Benachrichtigungen bei neuen Blogkommentaren."); + + builder.AddOrUpdate("ActivityLog.EditSettings", + "The setting {0} has been changed. The new value is {1}.", + "Die Einstellung {0} wurde gendert. Der neue Wert ist {1}."); + + builder.AddOrUpdate("Admin.Catalog.Products.Fields.Condition", + "Product condition", + "Artikelzustand", + "Specifies the product condition.", + "Legt den Artikelzustand fest."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowProductCondition", + "Show product condition", + "Artikelzustand anzeigen", + "Specifies whether to show the product condition.", + "Legt fest, ob der Artikelzustand im Shop angezeigt werden soll."); + + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.ProductCondition.New", "New", "Neu"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.ProductCondition.Refurbished", "Refurbished", "Generalberholt"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.ProductCondition.Used", "Used", "Gebraucht"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.ProductCondition.Damaged", "Damaged", "Defekt"); + + builder.AddOrUpdate("Products.Condition", "Product condition", "Artikelzustand"); + builder.AddOrUpdate("Common.OpenUrl", "Open URL", "URL ffnen"); + builder.AddOrUpdate("Common.NoPreview", "A preview is not available.", "Eine Vorschau ist nicht verfgbar."); + + builder.AddOrUpdate("Admin.Orders.Shipments.TrackingNumber", + "Tracking number", + "Tracking-Nummer", + "Specifies the tracking number for tracking the shipment.", + "Legt die Tracking-Nummer zur Sendungsverfolgung fest."); + + builder.AddOrUpdate("Admin.Orders.Shipments.TrackingUrl", + "Tracking URL", + "Tracking-URL", + "Specifies the URL for tracking the shipment.", + "Legt die URL zur Sendungsverfolgung fest."); + + builder.AddOrUpdate("Admin.Media.Album.Message", "Messages", "Nachrichten"); + builder.AddOrUpdate("Admin.Media.Album.File", "Files", "Dateien"); + builder.AddOrUpdate("Admin.Media.Album.Content", "Content", "Inhalte"); + + builder.AddOrUpdate("Admin.SalesReport.LatestOrders", "Latest orders", "Neueste Bestellungen"); + builder.AddOrUpdate("Admin.SalesReport.TopCustomers", "Top customers", "Top Kunden"); + + builder.AddOrUpdate("Admin.Customers.CustomerRoles.AutomatedAssignmentRules", + "Rules for automated assignment", + "Regeln fr automatische Zuordnung", + "Customers are automatically assigned to this customer group by scheduled task if they fulfill one of the selected rules.", + "Kunden werden automatisch per geplanter Aufgabe dieser Kundengruppe zugeordnet, wenn sie eine der gewhlten Regeln erfllen."); + + builder.AddOrUpdate("Admin.Catalog.Categories.AutomatedAssignmentRules", + "Rules for automated assignment", + "Regeln fr automatische Zuordnung", + "Products are automatically assigned to this category by scheduled task if they fulfill one of the selected rules.", + "Produkte werden automatisch per geplanter Aufgabe dieser Warengruppe zugeordnet, wenn sie eine der gewhlten Regeln erfllen."); + + builder.AddOrUpdate("Admin.Rules.AddedByRule", "Added by rule", "Durch Regel hinzugefgt"); + builder.AddOrUpdate("Admin.Rules.ReapplyRules", "Reapply rules", "Regeln neu anwenden"); + + builder.AddOrUpdate("Admin.CustomerRoleMapping.RoleMappingListDescription", + "The list shows customers who are assigned to this customer role. Customers are automatically assigned by scheduled task as long as the group is active and active rules are specified for it. You can make a manual assignment using the customer role selection at the respective customer.", + "Die Liste zeigt Kunden, die dieser Kundengruppe zugeordnet sind. Kunden werden automatisch per geplanter Aufgabe zugeordnet, sofern die Gruppe aktiv ist und fr sie aktive Regeln festgelegt sind. Eine manuelle Zuordnung knnen Sie ber die Kundengruppenauswahl beim jeweiligen Kunden vornehmen."); + + builder.AddOrUpdate("Admin.Catalog.Categories.ProductListDescription", + "The list shows products that are assigned to this category. Products are automatically assigned by scheduled task as long as the category is published and active rules are specified for it.", + "Die Liste zeigt Produkte, die dieser Warengruppe zugeordnet sind. Produkte werden automatisch per geplanter Aufgabe zugeordnet, sofern die Warengruppe verffentlicht ist und fr sie aktive Regeln festgelegt sind."); + + builder.AddOrUpdate("Admin.System.ScheduleTasks.TaskNotFound", + "The scheduled task \"{0}\" was not found.", + "Die geplante Aufgabe \"{0}\" wurde nicht gefunden."); + + builder.AddOrUpdate("Admin.SalesReport.ByQuantity", "By quantity", "Nach Anzahl"); + builder.AddOrUpdate("Admin.SalesReport.ByAmount", "By amount", "Nach Betrag"); + builder.AddOrUpdate("Admin.SalesReport.Attribute", "Attributes", "Attribute"); + builder.AddOrUpdate("Admin.SalesReport.Value", "Values", "Werte"); + builder.AddOrUpdate("Admin.SalesReport.NewOrders", "Total new orders", "Neue Auftrge"); + builder.AddOrUpdate("Admin.SalesReport.NoIncompleteOrders", "No incomplete orders", "Keine unvollstndigen Auftrge"); + builder.AddOrUpdate("Admin.Report.StoreStatistics", "Statistics", "Statistiken"); + builder.AddOrUpdate("Admin.Report.CustomerRegistrations", "Customer registrations", "Kundenregistrierungen"); + builder.AddOrUpdate("Admin.Report.Today", "Today", "Heute"); + builder.AddOrUpdate("Admin.Report.Yesterday", "Yesterday", "Gestern"); + builder.AddOrUpdate("Admin.Report.LastWeek", "Last 7 days", "Letzte 7 Tage"); + builder.AddOrUpdate("Admin.Report.LastMonth", "Last 28 days", "Letzte 28 Tage"); + builder.AddOrUpdate("Admin.Report.ThisYear", "This year", "Dieses Jahr"); + builder.AddOrUpdate("Admin.Report.Overall", "Overall", "Insgesamt"); + builder.AddOrUpdate("Admin.Promotions.NewsLetterSubscriptions.Short", "Newsletter abos", "Newsletter Abos"); + builder.AddOrUpdate("Admin.Promotions.SalesReport.Sales", "Sales", "Verkufe"); + builder.AddOrUpdate("Common.Amount", "Amount", "Betrag"); + builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.AttributeCombinations.Short", "Combinations", "Kombinationen"); + builder.AddOrUpdate("Admin.CurrentWishlists.Short", "Whishlists", "Wunschlisten"); + builder.AddOrUpdate("Admin.CurrentCarts.Short", "Shopping carts", "Warenkrbe"); + builder.AddOrUpdate("Admin.SalesReport.Sales", "Sales", "Umsatz"); + builder.AddOrUpdate("Admin.SalesReport.Sales.Hint", "Total value of all orders", "Gesamtwert aller Auftrge"); + builder.AddOrUpdate("Admin.Report.OnlineCustomers", "Customers online within last 15 minutes", "Kunden in den letzten 15 Minuten online"); + builder.AddOrUpdate("Admin.Orders.Overall", "Orders overall", "Auftrge insgesamt"); + builder.AddOrUpdate("Admin.Report.Registrations", "Registrations", "Registrierungen"); + + builder.AddOrUpdate("Common.FileUploader.Upload", "To upload files drop them here or click.", "Zum Hochladen Dateien hier ablegen oder klicken."); + builder.AddOrUpdate("FileUploader.Dropzone.Message", "To upload files drop them here or click", "Zum Hochladen Dateien hier ablegen oder klicken"); + builder.AddOrUpdate("FileUploader.MultiFiles.MainMediaFile", "Main media file", "Hauptbild"); + builder.AddOrUpdate("FileUploader.Preview.SetMainMedia.Title", "Set as main picture", "Zum Hauptbild machen"); + builder.AddOrUpdate("FileUploader.Preview.DeleteEntityMedia.Title", "Remove assignment", "Zuordnung entfernen"); + + builder.AddOrUpdate("FileUploader.StatusWindow.Uploading.File", "file is uploading", "Datei wird hochgeladen"); + builder.AddOrUpdate("FileUploader.StatusWindow.Uploading.Files", "files are uploading", "Dateien werden hochgeladen"); + builder.AddOrUpdate("FileUploader.StatusWindow.Complete.File", "upload complete", "Upload abgeschlossen"); + builder.AddOrUpdate("FileUploader.StatusWindow.Complete.Files", "uploads complete", "Uploads abgeschlossen"); + builder.AddOrUpdate("FileUploader.StatusWindow.Canceled.File", "upload canceled", "Upload abgebrochen"); + builder.AddOrUpdate("FileUploader.StatusWindow.Canceled.Files", "uploads canceled", "Uploads abgebrochen"); + builder.AddOrUpdate("FileUploader.StatusWindow.Collapse.Title", "Minimize", "Minimieren"); + + builder.AddOrUpdate("FileUploader.DuplicateDialog.Title", "Replace or skip", "Ersetzen oder berspringen"); + builder.AddOrUpdate("FileUploader.DuplicateDialog.Intro", "A file with the name already exists in the target.", "Im Ziel ist bereits eine Datei mit dem Namen vorhanden."); + builder.AddOrUpdate("FileUploader.DuplicateDialog.DupeFile.Title", "Source file", "Quelldatei"); + builder.AddOrUpdate("FileUploader.DuplicateDialog.ExistingFile.Title", "Destination file", "Zieldatei"); + builder.AddOrUpdate("FileUploader.DuplicateDialog.Option.Skip", "Skip this file", "Diese Datei berspringen"); + builder.AddOrUpdate("FileUploader.DuplicateDialog.Option.Replace", "Replace file in target", "Datei im Ziel ersetzen"); + builder.AddOrUpdate("FileUploader.DuplicateDialog.Option.Rename", "Rename file", "Datei umbenennen"); + builder.AddOrUpdate("FileUploader.DuplicateDialog.Option.SaveSelection", "Remember selection and apply to remaing conflicts", "Auswahl merken und auf verbleibende Konflikte anwenden"); + + builder.AddOrUpdate("FileUploader.Dropzone.DictDefaultMessage", "Drop files here to upload", "Dateien zum Hochladen hier ablegen"); + builder.AddOrUpdate("FileUploader.Dropzone.DictFallbackMessage", "Your browser does not support drag'n'drop file uploads.", "Ihr Browser untersttzt keine Datei-Uploads per Drag'n'Drop."); + builder.AddOrUpdate("FileUploader.Dropzone.DictFallbackText", "Please use the fallback form below to upload your files like in the olden days.", "Bitte benutzen Sie das untenstehende Formular, um Ihre Dateien wie in lngst vergangenen Zeiten hochzuladen."); + builder.AddOrUpdate("FileUploader.Dropzone.DictFileTooBig", "File is too big ({{filesize}}MiB). Max filesize: {{maxFilesize}}MiB.", "Die Datei ist zu gro ({{filesize}}MB). Maximale Dateigre: {{maxFilesize}}MB."); + builder.AddOrUpdate("FileUploader.Dropzone.DictInvalidFileType", "You can't upload files of this type.", "Dateien dieses Typs knnen nicht hochgeladen werden."); + builder.AddOrUpdate("FileUploader.Dropzone.DictResponseError", "Server responded with {{statusCode}} code.", "Der Server gab die Antwort {{statusCode}} zurck."); + builder.AddOrUpdate("FileUploader.Dropzone.DictCancelUpload", "Cancel upload", "Upload abbrechen"); + builder.AddOrUpdate("FileUploader.Dropzone.DictUploadCanceled", "Upload canceled.", "Upload abgebrochen."); + builder.AddOrUpdate("FileUploader.Dropzone.DictCancelUploadConfirmation", "Are you sure you want to cancel this upload?", "Sind Sie sicher, dass Sie den Upload abbrechen wollen?"); + builder.AddOrUpdate("FileUploader.Dropzone.DictRemoveFile", "Remove file", "Datei entfernen"); + builder.AddOrUpdate("FileUploader.Dropzone.DictMaxFilesExceeded", "You can not upload any more files.", "Sie knnen keine weiteren Dateien hochladen."); + + builder.AddOrUpdate("Admin.Catalog.Products.ProductPictures.Delete.Success", "The assignment was successfully removed.", "Die Zuordnung wurde erfolgreich entfernt."); + + builder.AddOrUpdate("Common.Entity.Customer", "Customer", "Kunde"); + builder.AddOrUpdate("Common.Entity.ProductAttributeOption", "Product attribute options set", "Produktattribut-Options-Set"); + builder.AddOrUpdate("Common.Entity.ProductVariantAttributeValue", "Product attribute option", "Produktattribut-Option"); + builder.AddOrUpdate("Common.Entity.SpecificationAttributeOption", "Specification attribute option", "Spezifikationsattribut-Option"); + builder.AddOrUpdate("Common.Entity.BlogPost", "Blog post", "Blog-Eintrag"); + builder.AddOrUpdate("Common.Entity.NewsItem", "News item", "News"); + builder.AddOrUpdate("Common.Entity.Download", "Download", "Download"); + builder.AddOrUpdate("Common.Entity.MessageTemplate", "Message template", "Nachrichtenvorlage"); + + builder.AddOrUpdate("Common.Download.Version.Hint", "Enter the version number in the correct format (e.g.: 1.0.0.0, 2.0 or 3.1.5).", "Geben Sie die Versionsnummer in korrektem Format an (z.B.: 1.0.0.0, 2.0 oder 3.1.5)."); + builder.AddOrUpdate("Common.Download.Version.Placeholder", "e.g.: 1.0.0.0, 2.0 or 3.1.5", "z.B.: 1.0.0.0, 2.0 oder 3.1.5"); + + builder.AddOrUpdate("Admin.Configuration.Settings.Media.MaxUploadFileSize", + "Maximum file size", + "Maximale Dateigre", + "Specifies the maximum file size of an upload (in KB). The default is 102,400 (100 MB).", + "Legt die maximale Dateigre eines Uploads in KB fest. Der Standardwert ist 102.400 (100 MB)."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.XmlSitemapEnabled", + "Enable XML Sitemap", + "XML-Sitemap aktivieren", + "The XML sitemap contains URLs to store pages which can be automatically read and indexed by search engines like Google or Bing.", + "Die XML-Sitemap enthlt URLs zu Shop-Seiten, welche von Suchmaschinen wie Google oder Bing automatisch gelesen und indiziert werden knnen."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.XmlSitemapIncludesBlog", + "Include blog posts", + "Blog-Eintrge einbeziehen", + "Adds blog pages to sitemap.", + "Fgt Blog-Seiten zur Sitemap hinzu."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.XmlSitemapIncludesCategories", + "Include categories", + "Warengruppen einbeziehen", + "Adds category pages to sitemap.", + "Fgt Warengruppen-Seiten zur Sitemap hinzu."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.XmlSitemapIncludesForum", + "Include forums", + "Foren einbeziehen", + "Adds forum pages to sitemap.", + "Fgt Forum-Seiten zur Sitemap hinzu."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.XmlSitemapIncludesManufacturers", + "Include brands", + "Hersteller einbeziehen", + "Adds brand pages to sitemap.", + "Fgt Hersteller-Seiten zur Sitemap hinzu."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.XmlSitemapIncludesNews", + "Include news", + "News einbeziehen", + "Adds news pages to sitemap.", + "Fgt News-Seiten zur Sitemap hinzu."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.XmlSitemapIncludesProducts", + "Include products", + "Produkte einbeziehen", + "Adds product pages to sitemap.", + "Fgt Produkt-Seiten zur Sitemap hinzu."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.XmlSitemapIncludesTopics", + "Include topics", + "Seiten einbeziehen", + "Adds topic pages to sitemap.", + "Fgt Inhalts-Seiten zur Sitemap hinzu."); + + builder.AddOrUpdate("Admin.System.XMLSitemap", "XML Sitemap", "XML-Sitemap"); + + builder.AddOrUpdate("Admin.Configuration.Settings.Media.MakeFilesTransientWhenOrphaned", + "Automatically delete orphaned files", + "Verwaiste Dateien automatisch lschen", + "Specifies whether orphaned media files should be automatically deleted during the next cleanup operation.", + "Legt fest, ob verwaiste Mediendateien beim nchsten Aufrumvorgang automatisch gelscht werden sollen."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Media.MediaTypes", "Media types", "Medientypen"); + + builder.AddOrUpdate("Admin.Configuration.Settings.Media.MediaTypesNotes", + "Media types control which files can be uploaded. All other media types are generally rejected. Please enter types separated by spaces and without dots.", + "Medientypen steuern, welche Dateien hochgeladen werden knnen. Alle anderen Medientypen werden grundstzlich abgelehnt. Die Typen bitte Leerzeichen getrennt und ohne Punkt angeben."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Media.Type.Image", "Image", "Bild"); + builder.AddOrUpdate("Admin.Configuration.Settings.Media.Type.Video", "Video", "Video"); + builder.AddOrUpdate("Admin.Configuration.Settings.Media.Type.Audio", "Audio", "Audio"); + builder.AddOrUpdate("Admin.Configuration.Settings.Media.Type.Document", "Document", "Dokument"); + builder.AddOrUpdate("Admin.Configuration.Settings.Media.Type.Text", "Text", "Text"); + builder.AddOrUpdate("Admin.Configuration.Settings.Media.Type.Bin", "Other", "Sonstige"); + + builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowDiscountSign", + "Show discount sign", + "Specifies whether a discount sign should be displayed on product pictures when discounts were applied", + "Rabattzeichen anzeigen", + "Legt fest, ob ein Rabattzeichen auf dem Produktbild angezeigt werden soll, wenn Rabatte angewendet wurden."); + + builder.AddOrUpdate("Admin.Common.IsPublished", "Published", "Verffentlicht"); + + builder.AddOrUpdate("Admin.CheckUpdate.AutoUpdatePossibleInfo", + "<p>This update can be installed automatically. For this Smartstore downloads an installation package to your webserver, executes it and restarts the application. Before the installation your shop directory is backed up, except the folders <i>App_Data</i> and <i>Media</i>, as well as the SQL Server database file. </p><p>Click the <b>Update now</b> button to download and install the package. As an alternative to this, you can download the package to your local PC further below and perform the installation at a later time manually.</p>", + "<p>Dieses Update kann automatisch installiert werden. Hierfr ldt Smartstore ein Installationspaket auf Ihren Webserver herunter, fhrt die Installation durch und startet die Anwendung neu. Vor der Installation wird der Verzeichnisinhalt Ihres Shops gesichert, mit Ausnahme der Ordner <i>App_Data</i> und <i>Media</i> sowie der SQL Server Datenbank. </p><p>Klicken Sie die Schaltflche <b>Jetzt aktualisieren</b>, um das Paket downzuloaden und zu installieren. Alternativ hierzu knnen Sie weiter unten das Paket auf Ihren lokalen PC downloaden und die Installation zu einem spteren Zeitpunkt manuell durchfhren.</p>"); + + builder.AddOrUpdate("Admin.AppNews", + "Smartstore News", + "Smartstore News"); + + builder.AddOrUpdate("Admin.CheckUpdate.IsUpToDate", + "Smartstore is up to date", + "Smartstore ist auf dem neuesten Stand"); + + builder.AddOrUpdate("Admin.Common.About", + "About Smartstore", + "ber Smartstore"); + + builder.AddOrUpdate("Admin.Help.OtherWorkNote", + "Smartstore includes works distributed under the licenses listed below. Please refer to the specific resources for more detailed information about the authors, copyright notices and licenses.", + "Smartstore beinhaltet Werke, die unter den unten aufgefhrten Lizenzen vertrieben werden. Bitte beachten Sie die betreffenden Ressourcen fr ausfhrlichere Informationen ber Autoren, Copyright-Vermerke und Lizenzen."); + + builder.AddOrUpdate("Admin.Marketplace.ComingSoon", + "In the Smartstore Marketplace we offer modules, themes & language packages, which will make your shop better and more successful. Once we are ready to go, you'll be informed about the latest extensions here. Stay tuned...", + "Im Smartstore Marketplace werden Module, Themes & Sprachpakete angeboten, die Ihren Onlineshop besser, flexibler und erfolgreicher machen sollen. Sobald wir die Arbeiten am Marketplace abgeschlossen haben, werden Sie hier ber die neuesten Erweiterungen informiert."); + + builder.AddOrUpdate("Admin.Packaging.IsIncompatible", + "The package is not compatible the current app version {0}. Please update Smartstore or install another version of this package.", + "Das Paket ist nicht kompatibel mit der aktuellen Programmversion {0}. Bitte aktualisieren Sie Smartstore oder nutzen Sie eine andere, kompatible Paket-Version."); + + builder.AddOrUpdate("Admin.PageTitle", + "Smartstore administration", + "Smartstore Administration"); + + builder.AddOrUpdate("Admin.System.SystemInfo.AppDate.Hint", + "The creation date of this Smartstore version.", + "Das Erstellungsdatum dieser Smartstore Version."); + + builder.AddOrUpdate("Admin.System.SystemInfo.Appversion", + "Smartstore version", + "Smartstore Version"); + + builder.AddOrUpdate("Admin.System.SystemInfo.AppVersion.Hint", + "Smartstore version", + "Smartstore Version"); + + builder.AddOrUpdate("Admin.System.Warnings.IncompatiblePlugin", + "'{0}' plugin is incompatible with your Smartstore version. Delete it or update to the latest version.", + "'{0}' Plugin ist nicht kompatibel mit Ihrer Smartstore-Version. Lschen Sie es oder installieren Sie die richtige Version."); + + + builder.AddOrUpdate("Admin.Media.Exception.FileNotFound", + "Media file with Id '{0}' does not exist.", + "Die Mediendatei mit der Id '{0}' existiert nicht."); + + builder.AddOrUpdate("Admin.Media.Exception.FolderNotFound", + "Media folder '{0}' does not exist.", + "Der Medienordner '{0}' existiert nicht."); + + builder.AddOrUpdate("Admin.Media.Exception.DuplicateFile", + "File '{0}' already exists.", + "Die Datei '{0}' existiert bereits."); + + builder.AddOrUpdate("Admin.Media.Exception.DuplicateFolder", + "Folder '{0}' already exists.", + "Der Ordner '{0}' existiert bereits."); + + builder.AddOrUpdate("Admin.Media.Exception.NotSameAlbum", + "The file operation requires that the destination path belongs to the source album. Source: {0}, Destination: {1}.", + "Die Dateioperation erfordert, dass der Zielpfad zum Ursprungsalbum gehrt. Quelle: {0}, Ziel: {1}."); + + builder.AddOrUpdate("Admin.Media.Exception.DeniedMediaType", + "The media type of '{0}' is not allowed. If you want the media type '{1}' supported, enter the file name extension to the media configuration under 'Configuration > Settings > Media > Media types'.", + "Der Medientyp von '{0}' ist unzulssig. Wenn Sie wollen, dass der Medientyp '{1}' untersttzt wird, tragen Sie die Dateiendung in die Medienkonfiguration unter 'Konfiguration > Einstellungen > Medien > Medientypen' ein."); + + builder.AddOrUpdate("Admin.Media.Exception.DeniedMediaType.Hint", + " Accepted: {0}, current: {1}.", + " Akzeptiert: {0}, ausgewhlt: {1}."); + + builder.AddOrUpdate("Admin.Media.Exception.ExtractThumbnail", + "Thumbnail extraction for file '{0}' failed. Reason: {1}.", + "Thumbnail-Erstellung fr die Datei '{0}' ist fehlgeschlagen. Grund: {1}."); + + builder.AddOrUpdate("Admin.Media.Exception.MaxFileSizeExceeded", + "The file '{0}' with a size of {1} exceeds the maximum allowed file size of {2}.", + "Die Datei '{0}' mit einer Gre von {1} berschreitet die maximal zulssige Dateigre von {2}."); + + builder.AddOrUpdate("Admin.Media.Exception.TopLevelAlbum", + "Creating top-level (album) folders is not supported. Folder: {0}.", + "Das Erstellen von Ordnern auf oberster Ebene (Album) wird nicht untersttzt. Ordner: {0}."); + + builder.AddOrUpdate("Admin.Media.Exception.AlterRootAlbum", + "Moving or renaming root album folders is not supported. Folder: {0}.", + "Das Verschieben oder Umbenennen von Album-Stammordnern wird nicht untersttzt. Ordner: {0}."); + + builder.AddOrUpdate("Admin.Media.Exception.DescendantFolder", + "Destination folder '{0}' is not allowed to be a descendant of source folder '{1}'.", + "Der Zielordner '{0}' darf kein Unterordner von '{1}' sein."); + + builder.AddOrUpdate("Admin.Media.Exception.CopyRootAlbum", + "Copying root album folders is not supported. Folder: {0}.", + "Das Kopieren von Album-Stammordnern wird nicht untersttzt. Ordner: {0}."); + + builder.AddOrUpdate("Admin.Media.Exception.InUse", + "Cannot delete file '{0}' because it is being used by another process.", + "Datei '{0}' kann nicht gelscht werden, da sie von einem anderen Prozess verwendet wird."); + + builder.AddOrUpdate("Admin.Media.Exception.PathSpecification", + "Invalid path specification '{0}' for '{1}' operation.", + "Ungltige Pfadangabe '{0}' fr den Befehl '{1}'."); + + builder.AddOrUpdate("Admin.Media.Exception.InvalidPath", + "Invalid path '{0}'.", + "Ungltiger Pfad '{0}'."); + + builder.AddOrUpdate("Admin.Media.Exception.InvalidPathExample", + "Invalid path '{0}'. Valid path expression is: {{albumName}}[/subfolders]/{{fileName}}.{{extension}}", + "Ungltiger Pfad '{0}'. Ein gltiger Pfadausdruck lautet: {{albumName}}[/subfolders]/{{fileName}}.{{extension}}"); + + builder.AddOrUpdate("Admin.Media.Exception.FileExtension", + "Cannot process files without file extension. Path: {0}", + "Dateien ohne Dateiendung knnen nicht verarbeitet werden. Pfad: {0}"); + + builder.AddOrUpdate("Admin.Media.Exception.Overwrite", + "Overwrite operation is not possible if source and destination folders are identical.", + "Ein berschreibvorgang ist nicht mglich, wenn Quell- und Zielordner identisch sind."); + + builder.AddOrUpdate("Admin.Media.Exception.FolderAssignment", + "Cannot operate on files without folder assignment.", + "Kann nicht mit Dateien ohne Ordnerzuweisung arbeiten."); + + builder.AddOrUpdate("Admin.Media.Exception.MimeType", + "The file operation '{0}' does not allow MIME type switching. Source MIME: {1}, target MIME: {2}.", + "Die Dateioperation '{0}' erlaubt keine nderung des MIME-Typs. Quell-MIME: {1}, Ziel-MIME: {2}."); + + builder.AddOrUpdate("Admin.Media.Exception.TrackUnassignedFile", + "Cannot track a media file that is not assigned to any album.", + "Es kann keine Mediendatei verfolgt werden, die keinem Album zugeordnet ist."); + + builder.AddOrUpdate("Admin.Media.Exception.AlbumNonexistent", + "The album '{0}' does not exist.", + "Das Album '{0}' existiert nicht."); + + builder.AddOrUpdate("Admin.Media.Exception.AlbumNoTrack", + "The album '{0}' does not support track detection.", + "Das Album '{0}' untersttzt die Erkennung von Verweisen nicht."); + + builder.AddOrUpdate("Admin.Media.Exception.NullInputStream", + "Input stream was null", + "Eingabe-Stream war null"); + + builder.AddOrUpdate("Admin.Packaging.Dialog.Upload", "Upload & Install package file", "Paket-Datei hochladen & installieren"); + + builder.AddOrUpdate("Admin.Packaging.InstallSuccess.Theme", "Theme was uploaded and installed successfully. Please reload the list.", "Theme wurde hochgeladen und erfolgreich installiert. Bitte laden Sie die Liste neu."); + + builder.AddOrUpdate("Admin.Media.Exception.DeleteReferenzedFile", + "The file '{0}' is referenced by at least one entity. Permanently deleting referenced media files is not supported", + "Die Datei '{0}' wird von mind. einer Entitt referenziert. Endgltiges Lschen referenzierter Mediendateien wird nicht untersttzt."); + + builder.AddOrUpdate("Common.Resume", "Resume", "Fortsetzen"); + + builder.AddOrUpdate("Admin.Media.Exception.FileNamesIdentical", + "The source and destination file names are identical. Path: {0}", + "Der Quell- und Zieldateiname sind identisch. Pfad: {0}"); + + builder.Delete("ShoppingCart.MaximumUploadedFileSize"); + builder.Delete("Admin.Help.NopCommerceNote"); + + builder.AddOrUpdate("Common.FileUploader.UploadAvatar", "To upload an avatar image drop files here or click.", "Zum Hochladen eines Avatarbildes Dateien hier platzieren oder klicken."); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202006250801086_V400Resources.resx b/src/Libraries/SmartStore.Data/Migrations/202006250801086_V400Resources.resx new file mode 100644 index 0000000000..521bd8d863 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202006250801086_V400Resources.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/202007160058551_MediaFileIndexReorg.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202007160058551_MediaFileIndexReorg.Designer.cs new file mode 100644 index 0000000000..9387aac6ac --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202007160058551_MediaFileIndexReorg.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 MediaFileIndexReorg : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(MediaFileIndexReorg)); + + string IMigrationMetadata.Id + { + get { return "202007160058551_MediaFileIndexReorg"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202007160058551_MediaFileIndexReorg.cs b/src/Libraries/SmartStore.Data/Migrations/202007160058551_MediaFileIndexReorg.cs new file mode 100644 index 0000000000..66ff8fec6f --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202007160058551_MediaFileIndexReorg.cs @@ -0,0 +1,49 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class MediaFileIndexReorg : DbMigration + { + public override void Up() + { + DropIndex("dbo.MediaFile", new[] { "FolderId" }); + DropIndex("dbo.MediaFile", new[] { "Name" }); + DropIndex("dbo.MediaFile", new[] { "Alt" }); + DropIndex("dbo.MediaFile", new[] { "Extension" }); + DropIndex("dbo.MediaFile", new[] { "MimeType" }); + DropIndex("dbo.MediaFile", new[] { "MediaType" }); + DropIndex("dbo.MediaFile", new[] { "Size" }); + DropIndex("dbo.MediaFile", new[] { "PixelSize" }); + DropIndex("dbo.MediaFile", "IX_CreatedOn_IsTransient"); + DropIndex("dbo.MediaFile", new[] { "Deleted" }); + CreateIndex("dbo.MediaFile", new[] { "FolderId", "Extension", "PixelSize", "Deleted" }, name: "IX_Media_Extension"); + CreateIndex("dbo.MediaFile", new[] { "FolderId", "Deleted" }, name: "IX_Media_FolderId"); + CreateIndex("dbo.MediaFile", new[] { "FolderId", "MediaType", "Extension", "PixelSize", "Deleted" }, name: "IX_Media_MediaType"); + CreateIndex("dbo.MediaFile", new[] { "FolderId", "Name", "Deleted" }, name: "IX_Media_Name"); + CreateIndex("dbo.MediaFile", new[] { "FolderId", "PixelSize", "Deleted" }, name: "IX_Media_PixelSize"); + CreateIndex("dbo.MediaFile", new[] { "FolderId", "Size", "Deleted" }, name: "IX_Media_Size"); + CreateIndex("dbo.MediaFile", new[] { "FolderId", "Deleted" }, name: "IX_Media_UpdatedOnUtc"); + } + + public override void Down() + { + DropIndex("dbo.MediaFile", "IX_Media_UpdatedOnUtc"); + DropIndex("dbo.MediaFile", "IX_Media_Size"); + DropIndex("dbo.MediaFile", "IX_Media_PixelSize"); + DropIndex("dbo.MediaFile", "IX_Media_Name"); + DropIndex("dbo.MediaFile", "IX_Media_MediaType"); + DropIndex("dbo.MediaFile", "IX_Media_FolderId"); + DropIndex("dbo.MediaFile", "IX_Media_Extension"); + CreateIndex("dbo.MediaFile", "Deleted"); + CreateIndex("dbo.MediaFile", new[] { "CreatedOnUtc", "IsTransient" }, name: "IX_CreatedOn_IsTransient"); + CreateIndex("dbo.MediaFile", "PixelSize"); + CreateIndex("dbo.MediaFile", "Size"); + CreateIndex("dbo.MediaFile", "MediaType"); + CreateIndex("dbo.MediaFile", "MimeType"); + CreateIndex("dbo.MediaFile", "Extension"); + CreateIndex("dbo.MediaFile", "Alt"); + CreateIndex("dbo.MediaFile", "Name"); + CreateIndex("dbo.MediaFile", "FolderId"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202007160058551_MediaFileIndexReorg.resx b/src/Libraries/SmartStore.Data/Migrations/202007160058551_MediaFileIndexReorg.resx new file mode 100644 index 0000000000..4145116cbc --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202007160058551_MediaFileIndexReorg.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/202007241131557_V401Resources.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202007241131557_V401Resources.Designer.cs new file mode 100644 index 0000000000..94d37bbc96 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202007241131557_V401Resources.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 V401Resources : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(V401Resources)); + + string IMigrationMetadata.Id + { + get { return "202007241131557_V401Resources"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202007241131557_V401Resources.cs b/src/Libraries/SmartStore.Data/Migrations/202007241131557_V401Resources.cs new file mode 100644 index 0000000000..049b0f2f23 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202007241131557_V401Resources.cs @@ -0,0 +1,59 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using SmartStore.Data.Setup; + + public partial class V401Resources : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + } + + public override void Down() + { + } + + public bool RollbackOnFailure => false; + + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + context.SaveChanges(); + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.CartProductCount", "Number of products", "Anzahl der Produkte"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Weekday", "Weekday", "Wochentag"); + + builder.AddOrUpdate("ShoppingCart.QuantityExceedsStock") + .Value("de", "Die Bestellmenge bersteigt den Lagerbestand. Es knnen maximal {0} bestellt werden."); + + builder.AddOrUpdate("Account.CustomerOrders.RecurringOrders.ViewInitialOrder", + "Order Details (ID - {0})", + "Bestelldetails (ID - {0})"); + + builder.Delete( + "Account.CustomerOrders.RecurringOrders.InitialOrder", + "Admin.System.Warnings.NoCustomerRolesDefined", + "Admin.Configuration.ACL.Permission"); + + builder.AddOrUpdate("Enums.SmartStore.Core.Search.IndexingStatus.Unavailable", + "Unavailable", + "Nicht vorhanden"); + builder.AddOrUpdate("Enums.SmartStore.Core.Search.IndexingStatus.Idle", + "Idle", + "Bereit"); + builder.AddOrUpdate("Enums.SmartStore.Core.Search.IndexingStatus.Rebuilding", + "Rebuilding", + "Reindexierend"); + builder.AddOrUpdate("Enums.SmartStore.Core.Search.IndexingStatus.Updating", + "Updating", + "Aktualisierend"); + + builder.AddOrUpdate("Admin.Packaging.InstallSuccess", + "Package was uploaded and unzipped successfully. Please reload the list.", + "Paket wurde hochgeladen und erfolgreich entpackt. Bitte laden Sie die Liste neu."); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202007241131557_V401Resources.resx b/src/Libraries/SmartStore.Data/Migrations/202007241131557_V401Resources.resx new file mode 100644 index 0000000000..f321dbeec5 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202007241131557_V401Resources.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/202007291847004_NewPropertiesAndIndexes.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202007291847004_NewPropertiesAndIndexes.Designer.cs new file mode 100644 index 0000000000..5d7b94d21e --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202007291847004_NewPropertiesAndIndexes.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 NewPropertiesAndIndexes : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(NewPropertiesAndIndexes)); + + string IMigrationMetadata.Id + { + get { return "202007291847004_NewPropertiesAndIndexes"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202007291847004_NewPropertiesAndIndexes.cs b/src/Libraries/SmartStore.Data/Migrations/202007291847004_NewPropertiesAndIndexes.cs new file mode 100644 index 0000000000..31eef90291 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202007291847004_NewPropertiesAndIndexes.cs @@ -0,0 +1,47 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class NewPropertiesAndIndexes : DbMigration + { + public override void Up() + { + AddColumn("dbo.Country", "DefaultCurrencyId", c => c.Int()); + AddColumn("dbo.CheckoutAttributeValue", "MediaFileId", c => c.Int()); + AddColumn("dbo.CheckoutAttributeValue", "Color", c => c.String(maxLength: 100)); + CreateIndex("dbo.CustomerRole", "Active"); + CreateIndex("dbo.CustomerRole", "IsSystemRole"); + CreateIndex("dbo.CustomerRole", new[] { "SystemName", "IsSystemRole" }, name: "IX_CustomerRole_SystemName_IsSystemRole"); + CreateIndex("dbo.CustomerRole", "SystemName"); + CreateIndex("dbo.Country", "DefaultCurrencyId"); + CreateIndex("dbo.Customer", new[] { "Deleted", "IsSystemAccount" }, name: "IX_Customer_Deleted_IsSystemAccount"); + CreateIndex("dbo.Customer", "IsSystemAccount"); + CreateIndex("dbo.ProductVariantAttributeCombination", "Gtin"); + CreateIndex("dbo.ProductVariantAttributeCombination", "ManufacturerPartNumber"); + CreateIndex("dbo.ProductVariantAttributeCombination", "IsActive"); + CreateIndex("dbo.CheckoutAttributeValue", "MediaFileId"); + AddForeignKey("dbo.Country", "DefaultCurrencyId", "dbo.Currency", "Id"); + AddForeignKey("dbo.CheckoutAttributeValue", "MediaFileId", "dbo.MediaFile", "Id"); + } + + public override void Down() + { + DropForeignKey("dbo.CheckoutAttributeValue", "MediaFileId", "dbo.MediaFile"); + DropForeignKey("dbo.Country", "DefaultCurrencyId", "dbo.Currency"); + DropIndex("dbo.CheckoutAttributeValue", new[] { "MediaFileId" }); + DropIndex("dbo.ProductVariantAttributeCombination", new[] { "IsActive" }); + DropIndex("dbo.ProductVariantAttributeCombination", new[] { "ManufacturerPartNumber" }); + DropIndex("dbo.ProductVariantAttributeCombination", new[] { "Gtin" }); + DropIndex("dbo.Customer", new[] { "IsSystemAccount" }); + DropIndex("dbo.Customer", "IX_Customer_Deleted_IsSystemAccount"); + DropIndex("dbo.Country", new[] { "DefaultCurrencyId" }); + DropIndex("dbo.CustomerRole", new[] { "SystemName" }); + DropIndex("dbo.CustomerRole", "IX_CustomerRole_SystemName_IsSystemRole"); + DropIndex("dbo.CustomerRole", new[] { "IsSystemRole" }); + DropIndex("dbo.CustomerRole", new[] { "Active" }); + DropColumn("dbo.CheckoutAttributeValue", "Color"); + DropColumn("dbo.CheckoutAttributeValue", "MediaFileId"); + DropColumn("dbo.Country", "DefaultCurrencyId"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202007291847004_NewPropertiesAndIndexes.resx b/src/Libraries/SmartStore.Data/Migrations/202007291847004_NewPropertiesAndIndexes.resx new file mode 100644 index 0000000000..37017bdeb4 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202007291847004_NewPropertiesAndIndexes.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/202007301117363_AddCustomerRoleOrderAmount.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202007301117363_AddCustomerRoleOrderAmount.Designer.cs new file mode 100644 index 0000000000..e9d53d3e43 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202007301117363_AddCustomerRoleOrderAmount.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 AddCustomerRoleOrderAmount : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(AddCustomerRoleOrderAmount)); + + string IMigrationMetadata.Id + { + get { return "202007301117363_AddCustomerRoleOrderAmount"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202007301117363_AddCustomerRoleOrderAmount.cs b/src/Libraries/SmartStore.Data/Migrations/202007301117363_AddCustomerRoleOrderAmount.cs new file mode 100644 index 0000000000..2b3fdefbff --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202007301117363_AddCustomerRoleOrderAmount.cs @@ -0,0 +1,19 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class AddCustomerRoleOrderAmount : DbMigration + { + public override void Up() + { + AddColumn("dbo.CustomerRole", "MinOrderAmount", c => c.Decimal(nullable: false, precision: 18, scale: 2)); + AddColumn("dbo.CustomerRole", "MaxOrderAmount", c => c.Decimal(nullable: false, precision: 18, scale: 2)); + } + + public override void Down() + { + DropColumn("dbo.CustomerRole", "MinOrderAmount"); + DropColumn("dbo.CustomerRole", "MaxOrderAmount"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202007301117363_AddCustomerRoleOrderAmount.resx b/src/Libraries/SmartStore.Data/Migrations/202007301117363_AddCustomerRoleOrderAmount.resx new file mode 100644 index 0000000000..9cda56f950 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202007301117363_AddCustomerRoleOrderAmount.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/202008181949580_RenamedCustomerRoleOrderTotal.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202008181949580_RenamedCustomerRoleOrderTotal.Designer.cs new file mode 100644 index 0000000000..ec697d5c0b --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202008181949580_RenamedCustomerRoleOrderTotal.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.4.4")] + public sealed partial class RenamedCustomerRoleOrderTotal : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(RenamedCustomerRoleOrderTotal)); + + string IMigrationMetadata.Id + { + get { return "202008181949580_RenamedCustomerRoleOrderTotal"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202008181949580_RenamedCustomerRoleOrderTotal.cs b/src/Libraries/SmartStore.Data/Migrations/202008181949580_RenamedCustomerRoleOrderTotal.cs new file mode 100644 index 0000000000..910fea9429 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202008181949580_RenamedCustomerRoleOrderTotal.cs @@ -0,0 +1,23 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class RenamedCustomerRoleOrderTotal : DbMigration + { + public override void Up() + { + AddColumn("dbo.CustomerRole", "OrderTotalMinimum", c => c.Decimal(nullable: true, precision: 18, scale: 2)); + AddColumn("dbo.CustomerRole", "OrderTotalMaximum", c => c.Decimal(nullable: true, precision: 18, scale: 2)); + DropColumn("dbo.CustomerRole", "MinOrderAmount"); + DropColumn("dbo.CustomerRole", "MaxOrderAmount"); + } + + public override void Down() + { + AddColumn("dbo.CustomerRole", "MinOrderAmount", c => c.Decimal(nullable: true, precision: 18, scale: 2)); + AddColumn("dbo.CustomerRole", "MaxOrderAmount", c => c.Decimal(nullable: true, precision: 18, scale: 2)); + DropColumn("dbo.CustomerRole", "OrderTotalMaximum"); + DropColumn("dbo.CustomerRole", "OrderTotalMinimum"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202008181949580_RenamedCustomerRoleOrderTotal.resx b/src/Libraries/SmartStore.Data/Migrations/202008181949580_RenamedCustomerRoleOrderTotal.resx new file mode 100644 index 0000000000..10b085d3d3 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202008181949580_RenamedCustomerRoleOrderTotal.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/202009021705132_Merge9.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202009021705132_Merge9.Designer.cs new file mode 100644 index 0000000000..664b26aadd --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202009021705132_Merge9.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.4.4")] + public sealed partial class Merge9 : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(Merge9)); + + string IMigrationMetadata.Id + { + get { return "202009021705132_Merge9"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202009021705132_Merge9.cs b/src/Libraries/SmartStore.Data/Migrations/202009021705132_Merge9.cs new file mode 100644 index 0000000000..4aaa198af3 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202009021705132_Merge9.cs @@ -0,0 +1,15 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + + public partial class Merge9 : DbMigration + { + public override void Up() + { + } + + public override void Down() + { + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202009021705132_Merge9.resx b/src/Libraries/SmartStore.Data/Migrations/202009021705132_Merge9.resx new file mode 100644 index 0000000000..0848e0a3bd --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202009021705132_Merge9.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/202009041122208_ShopIcons.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202009041122208_ShopIcons.Designer.cs new file mode 100644 index 0000000000..9a78bf96fb --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202009041122208_ShopIcons.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.4.4")] + public sealed partial class ShopIcons : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(ShopIcons)); + + string IMigrationMetadata.Id + { + get { return "202009041122208_ShopIcons"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202009041122208_ShopIcons.cs b/src/Libraries/SmartStore.Data/Migrations/202009041122208_ShopIcons.cs new file mode 100644 index 0000000000..227a055aa8 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202009041122208_ShopIcons.cs @@ -0,0 +1,73 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using SmartStore.Core.Data; + using SmartStore.Data.Setup; + + public partial class ShopIcons : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + AddColumn("dbo.Store", "FavIconMediaFileId", c => c.Int()); + AddColumn("dbo.Store", "PngIconMediaFileId", c => c.Int()); + AddColumn("dbo.Store", "AppleTouchIconMediaFileId", c => c.Int()); + AddColumn("dbo.Store", "MsTileImageMediaFileId", c => c.Int()); + AddColumn("dbo.Store", "MsTileColor", c => c.String()); + } + + public override void Down() + { + DropColumn("dbo.Store", "MsTileColor"); + DropColumn("dbo.Store", "MsTileImageMediaFileId"); + DropColumn("dbo.Store", "AppleTouchIconMediaFileId"); + DropColumn("dbo.Store", "PngIconMediaFileId"); + DropColumn("dbo.Store", "FavIconMediaFileId"); + } + + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + if (!DataSettings.DatabaseIsInstalled()) + return; + + // Add resources. + context.MigrateLocaleResources(MigrateLocaleResources); + + context.SaveChanges(); + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("Admin.Configuration.Stores.Fields.FavIconMediaFileId", + "Favicon", + "Favicon", + "Defines the favicon for the store. This icon should contain graphics in the dimensions 16×16, 32×32 and 48×48.", + "Legt das Favicon für den Shop fest. Dieses Icon sollte Grafiken in den Abmessungen 16×16, 32×32 und 48×48 beinhalten."); + + builder.AddOrUpdate("Admin.Configuration.Stores.Fields.PngIconMediaFileId", + "PNG icon", + "PNG-Icon", + "Defines the PNG icon for the store. This icon is recognized as favicon by up-to-date devices and used when shortcuts to your store are created. Optimal dimensions for this icon are 196x196.", + "Legt das PNG-Icon für den Shop fest. Dieses Icon wird von aktuellen Geräten als Favicon erkannt und verwendet, wenn Shortcuts zu Ihrem Shop erstellt werden. Optimale Abmessungen für das Icon sind 196x196."); + + builder.AddOrUpdate("Admin.Configuration.Stores.Fields.AppleTouchIconMediaFileId", + "Apple Touch icon", + "Apple Touch Icon", + "Defines the icon used by Apple devices as shortcut icon. Optimal dimensions for this icon are 180x180.", + "Legt das Icon fest, das von Apple-Geräten als Shortcut-Icon verwendet wird. Optimale Abmessungen für das Icon sind 180x180."); + + builder.AddOrUpdate("Admin.Configuration.Stores.Fields.MsTileImageMediaFileId", + "Microsoft tile picture", + "Microsoft Kachelgrafik", + "Defines the icon that is used by Microsoft devices as a tile picture when shortcuts to your store are created. Optimal dimensions for the picture are 310x310.", + "Legt das Icon fest, das von Microsoft-Geräten als Kachelgrafik verwendet wird, wenn Shortcuts zu Ihrem Shop erstellt werden. Optimale Abmessungen für das Bild sind 310x310."); + + builder.AddOrUpdate("Admin.Configuration.Stores.Fields.MsTileColor", + "Microsoft tile color", + "Microsoft Kachelfarbe", + "Defines the color used by Microsoft devices as tile color when shortcuts to your store are created.", + "Legt die Farbe fest, die von Microsoft-Geräten als Kachelfarbe verwendet wird, wenn Shortcuts zu Ihrem Shop erstellt werden."); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202009041122208_ShopIcons.resx b/src/Libraries/SmartStore.Data/Migrations/202009041122208_ShopIcons.resx new file mode 100644 index 0000000000..eab00470d5 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202009041122208_ShopIcons.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/202009090817220_DeliveryTimeMinMaxDays.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202009090817220_DeliveryTimeMinMaxDays.Designer.cs new file mode 100644 index 0000000000..6a218b39fb --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202009090817220_DeliveryTimeMinMaxDays.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.4.4")] + public sealed partial class DeliveryTimeMinMaxDays : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(DeliveryTimeMinMaxDays)); + + string IMigrationMetadata.Id + { + get { return "202009090817220_DeliveryTimeMinMaxDays"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202009090817220_DeliveryTimeMinMaxDays.cs b/src/Libraries/SmartStore.Data/Migrations/202009090817220_DeliveryTimeMinMaxDays.cs new file mode 100644 index 0000000000..4cf6cab153 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202009090817220_DeliveryTimeMinMaxDays.cs @@ -0,0 +1,78 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using System.Linq; + using SmartStore.Core.Data; + using SmartStore.Core.Domain.Catalog; + using SmartStore.Core.Domain.Configuration; + using SmartStore.Core.Domain.Directory; + using SmartStore.Core.Domain.Orders; + using SmartStore.Data.Setup; + + public partial class DeliveryTimeMinMaxDays : DbMigration, IDataSeeder + { + public override void Up() + { + AddColumn("dbo.DeliveryTime", "MinDays", c => c.Int()); + AddColumn("dbo.DeliveryTime", "MaxDays", c => c.Int()); + } + + public override void Down() + { + DropColumn("dbo.DeliveryTime", "MaxDays"); + DropColumn("dbo.DeliveryTime", "MinDays"); + } + + public bool RollbackOnFailure => false; + + public void Seed(SmartObjectContext context) + { + if (!DataSettings.DatabaseIsInstalled()) + { + return; + } + + // Migrate old to new delivery times catalog settings. + var settings = context.Set(); + var showInLists = settings.FirstOrDefault(x => x.Name == "CatalogSettings.ShowDeliveryTimesInProductLists"); + var showInDetail = settings.FirstOrDefault(x => x.Name == "CatalogSettings.ShowDeliveryTimesInProductDetail"); + var showInShoppingCart = settings.FirstOrDefault(x => x.Name == "ShoppingCartSettings.ShowDeliveryTimes"); + + context.MigrateSettings(x => + { + if (showInLists != null && showInLists.Value.IsCaseInsensitiveEqual("False")) + { + var key = string.Concat(nameof(CatalogSettings), ".", nameof(CatalogSettings.DeliveryTimesInLists)); + x.Add(key, DeliveryTimesPresentation.None.ToString()); + } + if (showInDetail != null && showInDetail.Value.IsCaseInsensitiveEqual("False")) + { + var key = string.Concat(nameof(CatalogSettings), ".", nameof(CatalogSettings.DeliveryTimesInProductDetail)); + x.Add(key, DeliveryTimesPresentation.None.ToString()); + } + if (showInShoppingCart != null && showInShoppingCart.Value.IsCaseInsensitiveEqual("False")) + { + var key = string.Concat(nameof(ShoppingCartSettings), ".", nameof(ShoppingCartSettings.DeliveryTimesInShoppingCart)); + x.Add(key, DeliveryTimesPresentation.None.ToString()); + } + }); + + context.SaveChanges(); + + if (showInLists != null) + { + settings.Remove(showInLists); + } + if (showInDetail != null) + { + settings.Remove(showInDetail); + } + if (showInShoppingCart != null) + { + settings.Remove(showInShoppingCart); + } + + context.SaveChanges(); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202009090817220_DeliveryTimeMinMaxDays.resx b/src/Libraries/SmartStore.Data/Migrations/202009090817220_DeliveryTimeMinMaxDays.resx new file mode 100644 index 0000000000..5021045a95 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202009090817220_DeliveryTimeMinMaxDays.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 + + + H4sIAAAAAAAEAOy923LcOrIo+D4R8w8OP81M7G2v5dUd0adj9ZyQdbEV27bUkmyf3S8KigWp2GKR1bxYUv/aPMwnzS8MwCsuiTtIVnnVi10iEgkgkZlIJBKJ/+//+X9//5/Pm/TVD1SUSZ797fWvb355/Qplcb5Ksoe/va6r+//8y+v/+X//7//b76erzfOrbz3cbwQO18zKv71eV9X2r2/flvEabaLyzSaJi7zM76s3cb55G63yt+9++eV/vP3117cIo3iNcb169ftVnVXJBjV/4D+P8yxG26qO0s/5CqVl9x2XXDdYX32JNqjcRjH62+vrTVRU11VeoDcnURW9fnWUJhHuxjVK71+/irIsr6IKd/KvX0t0XRV59nC9xR+i9OZlizDcfZSWqOv8X0dw03H88o6M4+1YsUcV12WVbywR/vpbR5i3fHUn8r4eCIdJd4pJXL2QUTfk+9vrm3ybxK9f8S399TgtCBRD2uOGvhgsyd409cr2v/94xQH9x8AUf3rzyxvMP//x6rhOq7pAf8tQXRVR+h+vLuu7NIn/C73c5I8o+1tWpyndU9xXXMZ8wJ8ui3yLiurlCt13/T9fvX71lq33lq84VKPqtIM7z6rf3r1+9QU3Ht2laGAEihDNqD6gDBVRhVaXUVWhIiM4UENKoXWureuXskIb8rtvE/MflqPXrz5Hz59Q9lCt//Ya/3z96ix5Rqv+S9ePr1mCxQ5Xqooa6Zo6L9vGuiltW3uf5ymKMmCMamQfq0060mmyPr/PVy/HZXmcRmU5PYGyOK1X6Dy7TjCZoq0vjc7LS9ztp7xY4YIKxZg/fFH2CCenxU1SpdOz5PU6LypdU3/+JcjcYrWuaOTdn/8ciF0nJ9pnVEVYhRE2mF4mSGMnqIyLZNuuMDO0Nw/vfUo2WMxXN3mjwUtfybxC2QoVR+X3ZPWAKl9sLZZ/5Nn0dGib+l5EW2xRVXjVEvpuUh9L8hMzb34jf4+ZGxUB9GWR5EWzDKsXdANleBM9TK8P67t/4nXiJj+K0wCrDzGhyrUtFX9/O1qBatswej7GRs9DXry4WIjR8xsKw8FIlLelMQ//9IvZCmnJQCdJuU2jlwsiiTbyY8w/16iqmrFY8w7WVPfJQ1000G86PAcO2jUO+haldYgFzLLZhlZ66jrx7Kc8jtLk32jVt+nAvR2OlnkFhAc2lrfVTofd1AKWX5Q91NGDJYsAeMjUIUyfD0Veb+cXr6H9pZqeS76/RD+Sh4aBJDP5+tUVShuAcp1sW7+eKFm3I/hZkW+u8hQS6AHq9jqvi5iML9eC3kRFY/W76ZShW56qpMNz0CDytjQL4a8TiUs3Mx3VVX6ISdrHtf9Vo2uUH2McqtYD7GHO0ujhfIMHe5akSEPuIK6dq8p7rxTYF+C48Qpngit1ZqO628m4QmWj40q5AuUg5TpUBjjoRkaNSqF7petunXGogxhoHM6DhtXrOl/rqqe1RoW8m0Zj960vtIU5L4l0Xab1Q5IJSkRX9SavY0j5hLOq/JUCaFtpVYiTUrhExSYpiXBeobg5O7FWCNcorokb8Q2P66AI5G0ZHW2anrs4L3fUjGFG+xxtt7gL8HoHgt6K7DPyuFkNYQU0rOa1FIJtBGL9EeGB/+VtHaVp/uRtSnJM4buqHjfzjwoyh9a4pEJGYwVFiwa4lYnkKFcG4IJQmdSBJMpQeXSyP6fa4NdIS23jpDbYmbR3zHfVS2bGD1pC3tZM6yO/SS8QusYs3C4KfirqJno+fUabrfehN0bUbXgJHm4KNLo2rpIf3mfPfYRUy/x+uIJaP+qmGvfADebH9HOSJZt607d4guJkE6WvX10W+FcXrfmX16+uia3deHuMEUfPXohDmGcTrCG8hrVZd0xHeFXjfQ2qDMY0QkpG0QOo+z1AeS0EHZbTTokZrQSkTtkvBQyCwwogbyuQg0PjSTSK4TI9LdKp0TDq+DrOx2WgEZeGp9qv9qq9vmPO6Zx9xflDgtXcxZZMd14w/evKSDfHclsLvUCEjS6yr9UQpXuCv9wkGwfH/3YVDtmnqKzwpxiVpRSl8Y6hDftJJK7nPioI1ol8obgXECBsLX9aowZV3cpti3N3sd0U53UmWWX6UrCbQqHQRRHCeh8VvWywMvyMqnW+kjhfaBCwozCE6FyBwWy73KxlYE9JSbuq9djpXoqlQg8BENve9ba7iqIsDEhSCYjQYxmcl4fKI3YQV43S/OHNIXhwV0O/zjB4oJZDGDahbqNUmE/nbPH0mUx2lH5Kssfpby9Eqwd0g56r6QlJWrquXsZttps3s7nGFyCcQ2ORdVrmBm22WMt6n2waXuAIIx+7eIEjzMguowJrwH5y/CdllUQkZEVApOsF5uTk35buKnI0MPhKb3K8XqO44lEZHvj21S6aCVZxVJit5GWRxOgqyh7Q9NxLrpVcZB8xmS6b42XPS4tRORjKR9ttmvhH6IS8sbGzUUiYOwPcXfQIZtqbLfIJuo+wnfktQU+f1aF2jtpWuiPo+NlwI9hB3+T0PhzYEwJw8u0hBGy7sRnUsNotQIEBfoGhVHSUiiBh/bl2fgtp98L4cPuZcdhhDXw0/DrsseRtLROj1s8MadfX/PlaokuEeTGrAqyzfcdEjJqDoz85N3W0aRl9omauK8z6ZFUwcXoKl1ZWjjWv0L/qBFsCx3m9zTM6dtt1ZkRMk+2a+plprJpGyrzv2wyYCPFKS2S6lZNdC6daPHmNb7TSWo/kc5TV91FMdHFhOhiujmo8DKjBkFh451Hh76s6NjZtRnDVWHoog2EMoEFNBkvnvLSbYUwGeqo8HLM0moPhIG9rGefsz+0zpXnv4LLbKZfdwcm2i062wH6nkC6xgw9rfh9WCC9paDeVs3Gqd1bBxmwYfxWNG/ZZwRCClScB8zP1xv5Y23lN3TcDhoOJJ2/rLE+xsrBc8DR24W9B9P5RqjrpDbO2zGNXkGPyrFTbSkHumn/GCpUOTp8vhUAja5qmp0keABhetqs47lNqbb8Rs3TVZOud2P79nqyqtVXXPqLkYV1ZVdnd9f68vCkiLD1Q6r9lrKyPyQorV18s3QoUICyArADQjXZTI6FdAmALYVjLeyDKNuDKRKuAB7B1TNGj03SPBYU6SUMousqA2Xa4c8EN2CSxpBwUbHrJocSYUjmorcF4E0mut9CtszdZ2BIFaZtiW5Ji4Y8fFV1qyiXGK1Au0A4E8gocbdF04uJlujY4DsarvK02xiqs8TrRlcbrtFYlaA0TInkcZSeIpPLupcZviZrNxGl05TF9ROp95XudpKuCrNJSXdauSS0LifqMLoWVBgtiq2jly4PVmgv3DFiUze9CtPQIQTZ4KQAp665pj9I7cvPTR9E2KILp2VfvoxJRt67o9UBznl6qsyWGURLdOwJY2a59FQTkaHSfycF885nLDslh1ZS3dUKp9PdJFpE7LiYqPUyKWt5Y9TjG5VEdJl3eVker6eLQdyB3oNrb7b3tEhKe6HdolhtHs34PwIpedx/0fe4B/XKY9T3yFeWDBGslOEQ4ZWt8NffNm3TBITTDt6RMMPR5tkp+JKs6StMXbxcZQZmk1AMZtEDQpdZxhriXTPAChZcqtES7TLhM80rRnAEZ5PLknO0drTZJdpxvNpQLeLLGeikLFJkT+IZQhyfcOf8hcijQWSUVzHOFfiToydv7crTdFvkPtLqKyPMh12N6I7eZ/pJXgTH26JoUTNygvfsYDukO34G7fqynFzoqNgOv+9WXenM3ao3Jmv1QJSoBD5XK50NyXx1HhfeZXo8nhHXV3Yu4qNaY4kOUt1//Opyjsaa5Vx5EqdXElq9IyqD05Wi14vrgPabz8iR/ytI88o976/B4XzPK0lbAe4T+LvTouRW4i3sBp+PGuUNz+rxN2hemTqIXHqcZiiYPVntjJQDbf4zK6whbTSjUrLLYLE95cG++lqg4eigQ2gSIHWCQzZKF4ry8IsmNiwA5MAdExy9xitpO+eo4GuMlKpLcW/oGnM3a3yD2lJXzkqQGOs1IFW92JEnvw6Ulxfq02WRGaY/xeE1cMJPdETwvg2VCPS9PS29yUo8rejslo4zEv2Q/sGhidG0aKO+dW5XHj3+vo843E8DX2WA8+hEluC7j1fAM9wZ76rxgJVnAkX/Kn9pRk5UmwNtoeIOQ3L80DoGzvOj7+B5RqczdEL+P4sfmUVeSF8G3l81ukGA8b2mJdyDDptfbomg2/V0m3TCT1GJsU+iGwdhjua7QNgSmF/L2b5GnTIApcdn1DTHl9uF8iMPjrdwJA6BVhzVRXpIJZKxjXUA6+L5+eV9X1ehc8dAtBPp7Uq7TpKzCIO2UX4qw8OJ1jfFfOd8Zx7uTBl0Se/vXGCTBV+CLdDVtA4MDu5ww1cAW44lSy4GY4xxSGZBgYYekBDSuLrWBI6benXeaVagog/BXp7YZzGhipugU+6xt4s3XTYJamfRe8LANgcrqqMKq866u0HG+uUuy7hA1IBNOkH3rOxOHH5zK7D4uOHrm3kF4FpmWNsNK46tPBkR+yiTcic2u3TntX6UYYgA8nT4mD1OE8R7v7p0XPDXJD1S8kMqWfq/eksWUsA2MbpeI8iYqkvt77THBb0GmgKjb4uXi/qJIHhIxBY9u01aibqEP4uEZ8H1GUVkXiNBQQYEgV/aGNm1TRBmiJT9Y1GakrbNViprDU42zM4wstu1douIcS38oHxuDlFAjNM7rdd667vAa439SkGSXSXNOZ+/qxlxCTkq7+tZdCX0f/tIut5H+FvwAav1QAqtgVAF2twKsEF/Hg8jC6wQ423hAegFQ9pkFFDtMl0t7ywA5hi62ckGEQnnpbQS77SRJGswoA5Vdf5PCW1+B6LbR6nxrLNSLIiqTB5ENQIBz7Lg+vRoAqIoqBcCkdxAhWMdxdNrM8BKlov/SYFdtVKxjz4e4FXnHWxBFr1kAWZc5KMf+Nk6T+yRuQIadtrL/cBXFeNQVZOPT1HIcr/SWa4+XgRO1KlUsVao0jKNO/YYtcWzNQ64PZfcV9RQTZFBLNksmVR2nikdtNXCL0doO0Xlc9AZNyYMsoMiEdLmUCxkgWzbkwzkUneVBxe6yENIOc2DW18UptyPQ3aEY4gyhUOAFEcLviaHWSnTKf423heSs+02H43BxQt6Wxpk0UUac5gywfJ+kaYhokQZZsCdxn/JPiBDxvMw1yZbfhUiytC4QMm3wtwANfqk3CEsp15hn9PC3yHuLH851G8yb3GE6zvPHBLVxNN4nsoGDpI9WK4ylPMuLTaRyRIWJuuueyDiuiwJlsRiwYr6DZ/DAzzG0uvtWgKUeZYBBxKcZJHDWSz5WuWTj/CPJZGsoA3I7rGHUs30QgPhoHwjlt54OBPRZUDskhxVVoWDVK6qhY9w20KObGM0CMkn2litMJkPX/F9cdfCnnCCY+ryhPeRplWlF2RJTtbezr0b9AVLltrrtNFu1j89PHYJ2hXX4qiFo4wYOdCjXYMUWXSd2nhHhYx+bIPOQfRyxnqAs3xD/x/j+dvAAB65J8rwCExnZa8sGDk94CxAmhQezdHsttgymw4qrUDKtfeQdP73MVvjujpxPRppLv2Ee9tmFFUJ3HBnSruZ3Amrr20ne2UNGD3GnER2k3VlMJ7Kv8zQvPqLnb1Faz9/6rEZwOOPvvOx23IK20UYCW1/bxFTQ1LFN2DMemfun7hlxHSRb3tZlmGQ7TICDL7Jgl9zikBFs8lcaXRf0WYJK58/Gk6SYBenzUT8qketQN+t6c5dFifdFqC4p1GHXP+WuXx691zNFyyOGgVlcrVt6kVAEacmr6QO2FHVtj9e5qLJpI9Ekh9jayDXLGAjDYRgNwLzrqk67GxrcVIe0OzjUBzNE3tZINO+7zj3RgyFq9h++2M5JSHT7pF7I98FoXbiMLtXKrYEe9pHkPkjVX3B7TAc5lbcVyMIPlWjjvDzDVk895oDaHXusv4b2OXILkZG77AaGNwgCH4HlUeDDD4koi4ATmA0uYeu6/oY0GAI99AxgO6ibydUNTe6fSeXIE48z3Gp44YKtpL51wfwhy+ktrTBFNnKPqyMm/Q+pS9o7Ev5apMUz2TshfYYGkoOGJKybQ1J12XHDuKhaws2SNK9NbetHlI8o3d7X6X+jsjlaD4LsS+6Cy0dCbS8YSaRScg3J6UJUR4sMlSWWVd3NOqHOLSfPkhFJa6jvUMmrecYktlLton76x+GHXwfbRd5WT6MPdTK02v52yPSZ6Q5ggzwLvKE87JO1chmVJUnvPltDbWyh/xMVLbbrSPkMdJiuz/rCQMBEnEf390maBHimoMnP6H1MEzijzFHMHChOmlEmDB99isrqfNtdWJg80inoERTp+qf8gYQlVC4Jjkj9PsunFIXtzEVpXemixoIEhOjs4DATdpYUZaVhxHfvQrzxR2ZjlobIWzC6c/Uwcev5Zhtl07+O2JsR2vxHYVjifVJU6xP6doGhvGFDaaXsX5jF6lvUPRhBwgrr0n+/uUH/yDNqvZqs53iR7Vw9pCHfjhOBwoZNvSERDZWLcmwTyM9hYPStneDtS9x4GMI3KY85aBc/ydaul65bCoy67CaUivfcRBDbLWl3JXdYpVXd5GGBvrIg8g5zcLa95r1Dqm53MLfj3lPsNw8jbI6lgNYpmPpnkTDa7nRGTXUKUDkECE46DBDYdiinz2RPGqVHdbUmu9I2LckVitvns4AxqWqAgzOqIIzSrJb1G9VE4V3mpSTv11AMDkQsFXoNgDh18SbfJrGij025vJNsMdxLDsa2m81pA9zDpgjsHFsi9Isrtu3SFarqIiNv+iDZBDMgYBdhCKGrEjD7Lj9FxeoyT7Kq/JiUlexUFoCT9F4OB4xBAWw7kj6Tg9EiJAADqxAHI1+GeEDrC+JUykN5tjceCiS/FEi8Ky6FtCX89yhNUaViHgYC7DcMIXRaAublTR5YwN6ZnG82efamQ3BwJMvbMvI6BNqjzHL8ZuJ2CLfzmKUhnes8TCt6n0OoduDLnRo+pS/62aZ6pt9Unu7d3kbX/DpXQ+8mb+gfyZaYq1GqyUER6CnidZ4hrR8qkI7o3ymcntm9vNZON2t7Wwe4U8sVCXYTX+6VT0fZOQ5S7CJ7sVfWURbKK3xGcDkEOMHucB3sD4WAdKTyjpwzOIh6F+gZ3v7VZt+zun24VTVuCUK74GSbNqmvzkms36f5w3DWbS3SpHb5hsKxG/FwXWdmiTEjgydmgLWAyl3SHUaQpSha346AIztB5QIrgUDebNR2xYOHCILDWqDSrMGuvn6KsoeaPJO6IyGj1m/bNcF41JHe0Ic2ZlAsto1a77O/W+6luqZdq1+jmHDKe1UmtkCpLTviRA/ledb12jtWh7yXOb3GzVchtqsuSXc7pemfxbSzkDp8x8CLPLbpDPIqPNI24/3E0zm8yeYQZ9Q9w+ZQ8zOqIqzbn9qzOnm4ShCTmDQ2ZxYI0p5OM4cZWeCEjhO5Ayh7Bz6hcLKqeBe/0vQy7Wq/MEu72WAdodg+MoWg2cdC2DowhmVN3T0KDOjf+LCKtIMjiP0VA3b9VXdUhAb6ywPJuy1AhruwRF1ICHV3iUK5G9s2pne+lun3qOwGGO72L3flZM7bKsoLOfpLLk4MOERkfMtJSKI11zX1yzcMmt3gtKFLvlzWUiYUfw3dUkfYkEZvKVggzIYFEbhHBheGXQKwysENIG+riT7yTgIRyK8cyMdmu9G83MtrFUHzebv7gtyc2dYBflKlo3ZdGylIorc0cYj2WlIakSjRpvZhiRrC0oAQaangQzlxKSB/dd51xkufNzgOCl3eVkOlnVHoZLZCXJLo3koKcBfI1hU2hCl7WXbtm52+10RCGJk9nlBT3ONrlx07v9lhPfQIJgeVtiTg3Gpx0XWwAwJ71/xUda0F8F9LPJeRwwoib6sh0Icir7cTvwlhGiJifVd8Puf4l/GKiBehgqwyRBUH2c/9jGvNHys/8yjDcm1+SwNx2pwqg7U5DeCvzbtOeKn0BsdBryuUzE+vjX9KGZ/2kHR4hPYoDni20cqlv+oBnSiQbnJSPV/wpsw9XJHUfkOh2I3DiD5acZbrP3OGRhJKO2UVlzJpjxFkU2pib0fAkVWhcmGlBIG8VsuxK+78enhI56cOZ2yeiZm/2bNGdc0euxkqhnWpEM1F4tX+iOGHOxVcdogfDDIyt8C3YSWGAt+EQnBRZyHCBr4N+MFAMrFU3kH3wDfKcIGteCfziLfmlTZU0Bi9AbsqRk8KJKdw4Bi9PE2/5STLdJtex8HIIyjKNzyi3diakF4dZeWTgx9QPvsDTjiCjiPELQ1Phc3JwcRYOQWs9+z3XfOY9xbFwb5X8+HEJxsTWbvtPfqL+y5gxWcAU7xVQQgrFcNORFoYVvSoIlDc6HLrEG5OXCV54f31hJAB3kCnOOsJLw1x0A3ytkLt/ZfRD20+6m4nMblhH2zvfb3Ony6yj9iQuAzwFm+zuf3QpJ67yZ1Cqic7VVhmt++783XbZTXaGtphMQWgvnffWY06Vq7nDRciSJ9DC5WTDgdShwbJgULhO2h5eVuh4hFosvu/RjbVK4MmSUW8EuPKkosos+jadp8gsx5CW8lsGOSb1VCaCl5bPlVuXS99oEJ8UAzTK4Z5kgj2s9yO7j6ZIcVa32RnE83Y8gXh5Yb75mnqKI5RWc7T4CVJqoatS6P3ZEKn1VeuDxMlGefVrF1qcidd25nv1kq1za/d/ndQnQqhIQSaKdFj05b3a2zNgHflpgz7hoP3dRk2FbfloXRD3jCvsvQdCYPtMnohh0VBkX1G1TpfTal5JRxzXBcFyuKXWRK+9o1d0e8BoTjZkIdDLwv8q9Fir3/9y+tX13FEiPgXZ1kI+z7O8E7QPGrlur6ryHuq51mc3hC0RsT6k/WwmMZOn+dprHkpFs9N8/rdXCNkGp1npJ3WmWeEXWOzjgw3ZCHK9o0xyhGvIgkxEaL0DKGpaSpveWoCy1uemtod/unTozU8NDmT9rI+WSvHBcKz8z5Koyah9ZRjaZTXFR7NinLKTdjUZE3gnQQeBFodbSadGvotm++oQFiOAuRuXqP4Ma+rowoLxl1doTnj14TGy/+1meGBiM6WCnUgGew932HDs52cBs1JHtmlkfd5GpE/xrzFGoPOLIUxEQzTTyTp8iz7CoE204V0RuUjWsmmZNIRHv/4Mf1jHKSh0+dtUjSekc95Vq1nbvO/UTQ9PWn5OknwAlCdoLvxCVL38/IBVffO9sc8neNFWbHhmRiTavh9lD3OsqPn2pxFxdBtnh/P2dzwvsp8TZ7fRTMYF91q2hiAw13EqeW+xjucIvl3o2luiigroyZR+AxPJ8uankVkZI1fobJOpyf7cbQlJwPzElxsdKbRXtd3g40+75Av6yJeRyWa80TiMkpcw7N6l07rd5h+XrrmiMOBvELXPUg4uxv8BKWo+qme9VHshK8QOUukPAhG5zAfo/ILeurcUl9yctAeM69HOodIxjHaVjfrBPcvwp+bcIWPUba6+OGws/J5Dr19aVj+FjpULpweg0CucUZ+LynDXXMOfuo9aV/L6AGpnpaFAG+7I++xs3IoIdxRAWp7Q+tDct/sErWDgABvv5Zo9T2p1sJg9NDCoAyqOD2/LX+teCgWJ4Mrgh/hpsqdevZFmtp0KJb0bCyCe0aV2z+2vUJog1a0hjyVPrEIvY4tZQotsNHj234sQVZY+c3JvlQkO1sCvFTNFE/xLniHWf4oOAggUXq+z4Hbvqct0BMo1rykLaescZgRrLGto456NCWo2Q9xSPK2htNtTz93q359neXTPEEzHrCZmgJjDY010H8UhFoNbSvcnbBOYckYdZzVBk6SDps1rvGFELaDmMvb6unlK6HMUh8C2bcorac7k55IofTUNDfLxxoai7z/KMilGtpWoXAW4Ry7C6MBwcanl8LxVzIHxSJvq/ceUloByjpnpp1CxD9OHLBxXvadPYqr5EcUwCfXIzzO6+1Mrv0rTIxtQtxkc/guh9bmuedzjTKy4Z5jZG1T8wzrM94VUve9J2vnvBy5o3GiLu11nshTZr8kG/nHhAXcdDSwJlWO51ZWRxyRBFS6Ksvg/S8VOebjpC8WHVJymmzGMZG8L/0EsfMv22cRfdH8vY660XttOrKkuiySePLI7KGhyQOx5xjNLCOZOnC89220NuLUJGNbm5p2iwT7zhvke5I/ZWkerQKkNz0ve2TBzPhPWECyEvV4LbdARFt/R8nD2mLrorkiWWerFGEzK5o+nKNV8MfNW39TSRNKkx+oeCE2oyVtu9t1NIZwp/ZHZZnHJFJ81ZtO8GFRQJtNZofqbDx/b7PF+Sx4ygWc3xqbxy2Pabo1QEEd6woVXesh/K3cL27PNtNW7peQTzb/tFaub+Y5KuPZfJnn+vu++Rgjs5M7bI0mMIuHAMUNiJdwEjYgHsErFRCA7yCDCr4LlM+hJXgIHNw1R/eTqYkdyXO58ybSDMr4PyhICIoGVMEJakMJHP78K3AclcloAh5+9dFPDrqwD0QaQq8O+k/eVhAb5KaI4kdM8ZmC7/vmvhaqLfu7X4I8n9Hc0g67sW0YFLneH+h3gY7VZze0jAMgeQ0jiY+0DcuUB+vSELejwhE7yABIAzVZKK+oQhplCA14OG/QS2RDpyBBhC6v1oU5KtAKQ0g5kMkrLC1OcsDFBHtsixhMB1FQiMIuZcmDrZNF42da9FPnomFauSTZ+fCs7GiUoRAsE5XUyc0YuE9e8m6ex2jLfXeXv4ax745WmyTT38T+k2NrbvtP9poCtPOEIQSdLAELF77tcCdD3ccgLjWsubMr9C/yKIS7D5tBc1gy5G0FWTKCmU6h1p4w0RqttjvLi5ab5vfUd/yLmnPiICfqfh1Q37k2m1r+KdPpwiOr6P7+Sz5Hqr6J1yFhUijdFia/bptw7iZv1blwHjSjBeJ3rd/VY0wRVOIrhiAAvyoI5ulMzRt/wDFewfyiF3lMh2VRpTwCLIuXUdFtqS23QW30jENFeopD3GcIdsIWJghzmbSOWCRRgXmIBCFOto0LY7HsgyoemVvi0GK11C0NT7u1pGCAc0sOGzT1iNAQtJZIgfT9dt4DquKXhFaAMCYZjL7LQYKaOiTXWyxrfV6dQRe4uBajKkrzhzdKvIflcXJ9DhP+YssmH3NcK0jCzLMkxQODnmjTrqNPF1k3SvChS6MQK5dnKJ1kWMnIkEAbVRAffDSqZX3Yp+ACh9Gq0ZmSQIXFki5KVH57A+VQndWiCu1BK9qqs/18nvgoTSKV4+S3EJ6McK/1ttEr0+Zz+IxWSYRXFP9dVZ7mqkibXwMfVyjVk+Tx3aVVrPCkr7+29luPYNtZ2a4ED2VW21cXLW4HHBMsOsGXm8NCc1gS/DYXWhs+7I7CEttZFKPqOi8qClejU9gCF6w3aINpWKGPCXUcMaBmS22vLmYr9NwqF/Ih4AP1Kj0mCQGcX/kK4YMeCtzHE3ITOb1Bz7o9MJKDknVWsn8yNNJsPeWEuOXa4UawzvxTGnq3I0+UgAVHF8tMMwYmBJN/i4okysa3nI7zzV2SRZ6bWz3yg1DI27p+rAPIhLqND3jlm7yRz1FW30cxmcSC5FXXXn8I06zlIZIaWduRUNi+J6twyD6GzSYQlaihnG18qCFa8oNFbRatWpbJQ4ZWlGNAZR8HCnr0yEXQHzKS7DC2KSLKJleFtxG9lyfBWGnGj2FOaJtNxEVdXdw3SNvQjHBrPZtmQr7eK5a/WxaHYAuYVpV5yY3rhzzkNGhbfkpiUMtlsK7nJbQUuw6WxWE1Yrqqy7CZ+l6eKHYQDnnbyfthefHyhkZ0sP7kbWm2RH+eZEdE/r1M6yJS6flpmjbLZmXYtpHf61NObJeZGgvgZDsvT9B9RL1RZraE+W4Fw+//DmIvbyuQzdahCXYweoOeK/xps53+gTySlvZfNV4vvBO1UUtiVhV5GiJKc6FoE16ELCwoW7PJ2Vbiec5lPKD32LSO7QjVh3QGQ+URN2fiSt8fWONWqnm1NFDWl3kP7ZCEcC8GXEwOq4jBKqI7PnSMetiJo9FZ054GPYYMZwru/NEjh+/0eZsX1eeoiVee4I6Y8ZrUnXFeI/URDQBvskKZVJMpZaO6QXXx2FBArTwiPehnZ80Z6vzDcw8QxNKbUpQk9p6VGHpqEouhC32gBVBHAEVlQ42iwjCBXgmuUw76xFzUx6n1v9qxA5ZkoMvcux5ULEwqOYVd/ROLEX3ZPXisdZu0b4aGzkuMCStIFAfIwh/OmG62vyF8Q5+S7JHcXV3yERQHS3gXljCzldxkEQzp+u7uOIT2fzdoD8uZSvOBdPsp1rJQb8QdljKgncNS9gdaykR3+a743Q3PIcyc904LWv84kcPi1aiWNz2CwzKlkNuORt6P730dX5OicnU7qxMR1YSHtTdFlJUJpWtdu+2XAsV4fdQ4qpuJ91V4LRaNIRBmZSVj7ARy6utix+soe0DYrp3Rfz/MI6jce06/pcBG1S2WCooZAPFSuzcJKrqwc+dNw4DjoHrlbQWyTIImcSYs5Rgk7hvsM2W2rOMojetW9D6jap0PI2xEcOBWAMw7gyChKCj4Q6u3LOgo/DCEoAAkYCHDoccmBiiom5eSKB0RwktDNYrO7Upjaxj2CA7aSd5WoJXfWX6G1Qw+nqIXO/YKIlsiHCxxxV6nRi0fkRdqPFmRoDgwo7ytYK6io/Su3sweFz6jRf7uz3+e5Jpx92eAlsOYzaPcwIYzVC6sSyCQnxM+eiE+u956sFYKXf3yDYPooBoUnEkT6vqlrNAm1EmsbUAbBjcL7AuV2Duvs1XjcW0eLjvNSGcDPA2+SfBE3OTNxAS8a3hVp0getEZP4+0ISnkxQQjRTwmD+T0ygbGcdtxoJNKkQtnL9Fj7IMcKZm5nyjszPUZD6s9gunItX2zJyJUHUe8maZjJYjeZl3SKSyPdnMNJ9gepuR3AqAT7QqmYXF8E8X6llIT+Oq/tzEt9I6aDUpC3FSjGdIej8wNerXzIMK2P14THvYPdlzECWMEArQAJCPhAIQTnpQKO1yh+zGuvq0Dd4xoCqoMa2DU1sJf3RRu+J/CdpzUU3vPyJno+fUYUNVxRYUTHmA0e8sLbJbJjt2MFmoXJgTOZMhaUkOLyJwx7C6jEUVmb1hGctMYVvdy3cCsBdfohSFJ7HM9RbOLk51MlYTyEBs6iUF0jYuZOoA4oxbk1Km8PW6viMG56SbOgy14HazqmQK78KxTXBWGVzqPo/jL58MYsiPCwPsjb4kk2zRvjtsEkXo+myXfG3FglT0+CPHQrVqYfozSrAzxPaVgxqJyFE7CDZCmY+CVOEZsQ1lEaCKJLVCT5yjtBEznFavCVfoiuKzztRCRDhOGG2sWdYHMqgDV1niVVEqW7rMnoLhppsVu2hlx1MYBafcVC2xo20vV/brXMb46t9bmTWm6YHn/6lLvE2eFaD+Sog8Jy0MbytigyhXBhhXqGV/9E+UT5kSZSTByZQVGmYG4F+FF6FWCCXlLBBn1Plm4IekoWKlf2NsyT5AIZQ6gTAn9QKfK22lAo3KOnvFgF8H2EdcS9m6ZVpxAoYz72WwoPS6D6kCN/+IR+oNT7OsyaZGlDZUm94DVzEKC+9UCnhtuj1arAjQUQNEeDQheb+YDUNzh//XOYSEh0j4oCFbM05meciGz/EOQdGct2P1bVlrsKBa0NAcj1tURFoMskQYwkmXGkNIoCGkP390maRE6xI0Pdcvx5WFPkbXXq0TsiIIznxtGNJN/N9Mof2gn07HE7QFHbAL5Q3AMIEJ4bgPQKxY0dah81SbwaGN2bAcmB4+VtzXjTaKKFSXkT2dp1e75KA0qc9k7vwKPSO70wBLAJB8G8pBAbRx5SmL8Z6h8E8GcWwOu0VmXHmKjVYLFyUfZQ422H3QwYy1DDGEns9RZAvtnk2Rse00GophYqPKIPRV5v52du3PL8jYa6FjR9OhLz1DRrtEFNkrG71EX0mvrlGwbNQe7kbTWECsC5s8Rs+knDuzBXcqfk/ga3i+3WRI23/x24fXI+tE0Zp3RZTmXjlaFuizfbc42TN4wEn+V4h4Q7jv8/SlPiWva+5fYxL6sZXmD+lD/kwTKYnEU/zmOSnyoQvsvsISi+o+02RTd5Ha+Dov1c3hAsGzztgXHqgsN37Z7nx2qTvs9XlDk8WafJrSqsO/tXlr+g6ikvHieX9MsiwYvUS6POj+uiQFnsbfx3OE+f4yYD5BVeIZxRK1KoSRuB77uSEd4qa1E3X3XA4h1YbQ37HHHizOhHxoFLhsRAqcfCgvrd6ieouke9PG2rDsvBxJK39RN4x6Y08OM1WpH8JVH56H7bhNTGHCniOjCmYlopcvmfOlzVWRbgrcXPUbxOMrQUo2NeCRXlcJZkSbmWYtP0pI5jhFaOtU+LYgbzDv/5QM5JLxFetcaIWbMoma6uPmzIdT8iXc9ptofXcVGN3LKVqNVcAyuu6boKfis7080AavSgP+VtaXSUaeCVdTZP9WM4oeK98uz0eUtEVJ0RKJC7oI2Rlrbylx0KnQX3WXlBZQNv00dj6RkL7C2u7UXGqHHXzn1BzxVemx1WkfPyY7LCsuDbA9w6XiK6hT1cYAJoOU6tzvl7Scb630md/71GNVqdYt2cHlUVJqDjXdFulS3fgAgPOl7eFkWwEG8W4En4lMcRk2WN8Cg3IwKorUKKxhuu0+ls18wUy1yT+Iw1nmaVmaplQqduSnWkckw5Aco1nHFCAyoYrDp4p9wZHTVsxzJUMxpOB20zor6K7aAolBZjYmrphkR9NRwRXcNrV8H0M8jic1hyFMqbsyYdj8wwO82/ObnJ52/zCm3TlyANa7ZEx5M38T6OJ2/jur77J4pV95sDmQfkvG7607qQ95KusYDeFIl3Jg6Mxs1t1yjwOM5r/6w/uBOrz1FWR2n6Em7PNa4u8BWMwOscv9syXxlNB0STXDeiWxYYHAgDo1qnWUCv5Zntlvv6TOM5LNAaMVVtI6Z5jqaLptDtntxepAGChOYf4WVe8KcItoFlJZ7JMASy3nKXpSbrwEQtt07M69L7/Vnymi26j7BU41W1kQVca6K0AsfRZhslD5mPvupxHHSVvC2NtpgqHl5rY07UcCCbc9bMPXzgpGfia3gqbvKj2FJBWLwI2AjkDdrg9cnpetAg0hyqg2Q7S/ZEvsyfeYP/GUM22fSnP179FJVV21yBQuhnrVuhte3be+3K8YXJnfJzORlC3dMMub8PvUz4uAukToJfHU6HxtrvvGr/pq1tvMB9QU/lJ0T0M+Zr6pkk93UOxnhY7uRtwRT7UCdDH9rfLiI5/54tjD4J6/y0D/IVkXzPi0dMwokvpJ/lRb3xFMUGR/lGQHWQQQWLhJS8UPlamxn0z8K9TeIdzSytTOwl8C+Y5ksOJbioFaB+j0QXyQ9MiyHe11VgWTwHaVVIawiFTngolKTe5KEw6bcXf5rmUXvyPN78/iXyUl4U4DW7Lqfb+5ejulr7R5ZSCK9QnGwT6hLAMiaNVIfSbAw/xc2olVsWnnqPWw4mPsqtgLWNLxpFx6T3NLS07yOQrucUpNcS8BlFJVbE7YNeDivASVJgoc+LlzcMpsMaIG9L4yScKA/2wmm4rwjpBuWhfibuL67HsNaX2m3F5ARrvKx029wIkjIgOwjLQVh+JmE532zzgrxYfJ84Zcdi6h+EY9eE4yxPV8EyaVvf302bCwNhbu6HwRTkulrL840ZiVbYuI68Dw8ek63n1jB6RH4Yvm5XeDgXmf/xBZa6swSlK/LXDImSejY7zrP75KEu2MtBUx1pnT5j7UXP/YT5dNJ6kw3ZRiZu7QqVWEGfZ/eq4+IwTXXX/DBypwQK7vfTmTVLepVRDiXs9xSgfrfRX7LYPc0M4c4+gc8bCtVhlVaIdZBcMy1/qBO4/jqNg80ozc1EC36T+eu5Wmgb0ND8Y1SqLm7+Kdy7NueWCTvaWn2fpl4zmsbIAi4s5rrNVBa7eQ/No+2fibI8Qds0d326l0dx0GjytrpVyVelLSPUgQz2GY2akSlDbFgMwvEDZU/UR9+HaUib9DVEI+SdqpsiyspN0qRfCTEVEM5hJE0KgkYpwWAOO+fWraVJRBBmTq7ru9ZPMHlLxlGUgRihac/gsDPg4MJEJxJ5TH4gEuIa4FaKy90WRTrPzlcI7Kz4hfl2AB73VTIYYVclBfS77Pfs5/Bk6h8MD3lbO+zwnCj7IVHb5FdHqun1drNEBBqr1or8kWDCmjY50XM/3aLYC69nNHkY47JVCCG9wX5+MZEpMTPOsenEf5J1dg7va8+Orc93FjfsjHuIi/v7Enle5G2uN/iheB9V8fo6+bfnwcIl1hrtIySechbQvDrON9sm8srGIA2WbeMfyfaoiNchAthJrfFxKn/jbrS24PwUXgYen41CawkG8/gzRpvU4y+Hktim4T3+76P48TzD8hI/esbnH2OlmOYPbyQYD5arvK0ggb/4z1Ud+2uqQBG/S8TWS1gPjLDXwQoSqK1g/0xFM2FWIxnq6AfSgRqPo4f3ixjFq8N9FBMpLjyul/e6BEJ3UCTytpbZhn5L0FMgv+GuRathRkQPefESgJd5VAc+PvDxbHzcKfcAbMxhOnDxgYtn4+LO5zUYQc5MzCI68LC8rWFX8Wug3ck7PzzTr/hFXpbYBk/9uYxHdeCzXeUzi6s+WX2F4iZwwZ4vNiRfVo/gwA3ytoIeNDke+pCZ8nWXjvbWxH7e78kKb9r/kWeapoK8/JNU6fQjajidvMbmOwfh3rTdmUR/Kq/SOWZd2JVOGJqUturntuXv0Y8EFAsOdAjG670cFqGHTh2RHPSqvK2GUt6RQgVujNA7gMe6OVQNFLihGTpJ3jf5Seo8uvF63RxjUac1UytjVHThheRv1bFloOP23VP+79FDkn0ocofDT3H2nk6ft1G28h/fl/wsT9P8yRsPevqeZCt/ROex7vHBEPxfvSil7NcQjZCRHOep8lnWMMP5WG2ot8oma+a4LI/TqNTckAyRK3Uf7KTW/AljJvFHbCpTysxMqje026tO0XWck7ju8/IsjR7KYbRG9hKpX74ZsAQzkDBXYLWavpDAMUqzshPwGW3uUDEcp5D3Ar5FaY3/+EWYLBZ2OLTt4MUbTwz84K/pwH8Tad1SVUHpT/lDEkcpIdXFlthYRPo9aA7gW5D6R9nKlPgXI9l/daBjNxffkjK5S9LGMHWiouIAYsS9IEXPmmpmJL1GURGv26i+cqSuplY32ssmBV8vCJo6/SOwvSD84j6Dx3m2SloTM/QEDqgXnD9s9phOH97k1cVdZ5QaTt7XkgLWzdpJtMGTvPKbtr/XUbOikJuxRd7kRg84cwD2BSfveptkGbU+6OiL139s4GZeiu24LvDuN365ymvMvdkD0ewBCDymf4IaWJDGTTc+J6ttnmTVCU09nbjQFb9uZUQXlqj0KXopm8pMa5q1n6pGteViAVwW6EeCnrp9YyD5eY+FhyRC5VEvOLHvowIZS077xpDFmtUM1Fzztfiv21j+9w9SFShpSKz4J+1azNf4s4u2/R6lKaou87IikoqiMsw62Rm+5RsA/5Lm42qTGMv/Jd4RraMSmQo+Xl4TypbSyDtZjClj9jdNXzDBEmyFs5X+pGPKu2Rk+T87cMdZFKPqOi+qZpftzhat4fimQVe+obEuyAwfk6okHkhTfmigjih4DT98iu5QSsNrOIJ19HksAA19+3O7jwlJFxF06mjUS+7E1yh+vMufUWk6g61aMp2/LzXWYEl8RXIByebQaD5uElRcYkzoOErjuvXWfEbVOl8FNGzljSy5uSR+OGN9i4oYZVWUylSWqM3/ianV5kORrOtm8xOVj+MD2N4zQtCVb2ikS24P82ITGW/wPyYPa2P1lo/m0X+6bES4J3UJdfDu8VMeR4F269wjs7KWljRh0/zOdG7ITWm8/iOytzK2SdooSg/dpcrQ4b9npLOtyVtacoOekEuPl83LtGbT9Bl3JNni7p5hySMDZCobyclRWeZx0pBwkDWSDLc97bhCZeOrv+0fvOH6f5qtXrU+fGWt0eP/MqRjgSq8ftWOCBMTa7K/vf6/hOGbNjhEYlAN9kPgGvmVHRNu5CJr8/6/Omq2PcQPX8bRSjwtwRRdsV86oSH283GelZg9sN0iBkIkWYznLbUZCofEMJ6CdHJoji85QVuUkVAImzk06Qf9PJLYn6FZjpg62v3+lmJWAx5O/t2ELDd9M2RgsIqUe2loa9aFm9o/vlWOYy6mVc7bXnBs78Ugg7od40fIn11a0FLKugZ1IR6mq03KXHY9BUQAhNRLnBNTWxBzBu62IJhJb2h0i7H6SVI2SSJuj7Z48kjgQ3fXL0FyHldVgpi7h7fRy8o2ALbsryhq2rAgzWe0SqI2RdJtGzgoJYgICpGBgrKhBIAcGL8C+S9v3oi7BSd5lHdlBvGTE9mk8S70cyk5aztP8l90c6Se7RFQzkiJuFLo2YhCvCwTiR2ZjYVE4po03dbYAQZqfnWuDAM2osEnYCYGvYyl+t7OwFNQf2blLIjeJh2g6y3GZV18CzeahjtkrCCvAnEbD23DdIqWpLoMaGL5vZp+IDNwrH7ejLm2f/x8mc0asRKLl9vuJL4PQpFv0GB4cFPWgtrwqAw7ZLH2PZ1MK2o6M8eWSU1skx5wVRfjs+sK7zCaaz9ZjG571pDxAQgN8RgDaMNpcAsQn8FcvLwaVI5gBuZUzpHRDr6tsvRq3Xf94v6iSB6STLdU8/CKddpheRawm7NkOM2n6ct8y6uM1hbs1VddnM2wdkh+oOKlSYOn4wIaODCDMagB7mL6OTmLQb2Zkb8gOpstq2O9pTnrfZ2tsAVZoc1RVRXJXV2hNhPz7ViiYzgTHAo+lFZ3YFCjrkCedb767i3ZDiOcTxZsWMCkV2Ot3RGQbii99jTmRLaekSB4cT7Xnpzb94DH4bEswdfwLJrz8pBhaGfY2Z6R52NhF+YNaVrIu7II7zlw3Y7wW382eDscEmqYQKig4DbDg0ezZiwONndGUUpHMR+XSufLaJvV1dkZLjXUiTz8xDy6x0u5bAwLMOj+KlE6n/kt/Yf2ZEhWUXU+RON3OCKSNgkdFCna2hkW1o5oPl7WzqdJV+h6O8XZhsoXqjMTP++xIlaNYyEG3kOF3D3BpD2D4uAg9uxAbFiSx7qEe1/ShxlYSEJTC3f+4mzDnjjqppk9JAvLQixugJGUh6Ph2Qnsz4xMBdLapH2m4uJh87fjG3EyDhBBVUHxVgEYImaAscyY1mXs75OUvEbZN6DtJgsfnAocenNSiNLlQA2SbIfE6Q5D0PWXr6CiRwfrQhahGTA8Byb78saUbhQzaCzdfBmth9RrbYvoK5LahlzS1N9HEyAhvuyBbBhSRLxXl86k3Z+BBaVzYtL24lfMht7rA2xF0ODc5xZIG84Ak3dkTkba0wDYof9dSikLjuJrBGcsoYGF+UvWnznZTEZ0M9cDW3dRpjvON81LxAOHqNiBB5axWgdny20CeoDRZJy8G+uobAQzsaZsfkya7+ss7ci9aoTjI0q393Wakf0OU6Dz6WqqK9y7Qk0HH6+udbm7txvdznG13cDm8/8azrOFK7ituBj7n+VFvWmEVrvRFkEhth6gbPgYQG21rQ6y0ss7MQN/yYm7F/vhpvs3+TaJDfmIhZUyUgNmzUkc8oVYCe7FXLwEE3h/mOm2+bd9eUY52RSglI2sOYhGCrAP1bedWzpl/Z+L8YD5MGl6rLULSqzlGgMl0w55CvXVYpYx347yHdD1eRUeMx/GfLcD5hfFL3oriRpweBOMQi7jPpCvd4QFJWOY1YYT58ek+abC8qz4La/Q7chDWoZh4ZUMSUCdmJJrQ8aXu+mi0YxiTs6E58pYTy7qqfmCnsrmNoH2zE2AhJiyB7LhRxHxXp25Sbs/AwtK58Sk7cXP3Ibe609IRNDg3Lf0mYi8I3My0p6euQ39Nz5zk9YIzlg7cuam7c+cbPZTnLmR0fTHNAOHqNiBB5axmsOZG4geYDQZJ+/GOiobwUysKZsfk+b7OsudueVpqrfiGCjwBC0n+fktTssYhHtluYFdn4HVwDnYC4uN9PwoK5/IPQ3CJyqmoOBkfNaC2HIbjRg6hAUYeDd4Dej4TNwGzIXRSovhF+U1vLNunltonr6mOEbFHpI6Mh6kwW05UdaUhCthdt8N3tQMZSY+1cydKc+2tRa/d0HG1SWiN4+4hyrpHigwTvpv2NxeRt+rRjID+5rMn0k3Fj8oVg2EfHPiYPJtZi5umlRwcjOWveJmekQLczQ9nzZcTeotxtmnzxUqsig9qqs1wdi+OtetNloFbVQb4nFVRRtmN+vAXuluqyHNwPJWc7wX2rx5xNb0EiQEDHF09zKuOeuCiM3vPgbygKp6MQNzqahr0jxbc2GO0qpLFiwgF+2npoP7PhvX7aXu6p+c+koej/2Y4N6QzPX921UyLlHWUr2GRVdweRkLbhDKhyx5fmt5LjUaygxMazSHJv3o6+wWD7eaz4qfWjGei3vb1gDWBXX2jvItM4ilmJaZN5NONBWWXdyboy81j3Jw0uXd9tCcx7s/LCjp+VwrvDgX+8RsuiRvAuQEDLePadykvZ+V7fYwZduH5L46jsghA+7yOirR6ntSrUcOkrGLph7Eln0VG67UNSNTixD3h4sgMuzVDLxnOA1GnAhiWJwxGSNiYCEdv4C1VEzpajWqGwTYUyYFy+tQo6HMyNPKOTTpR19nt3j4Ky1idozMVJ2Nm9lW98cQNR/MUkwNzqdJZ5iKy5qtX8idD4M90ggnNVm/WN7f4fHuD2tKej6XsSrOxe7vka7QExafyxwjKHv50freVZUgNgTgbRhS2dxeeelNRjIDt5rM31548KGBmBkC2prGag/c/9g05CYwXvlar9fJtolEVxOJBQNfs+0grB6yZbHuz/ICd3wGeYXnYfcXl77f7Z65ZxUdXzDQKqaz9cXBDUBpyCVcvTssCA5hRk4E58ik/QHBsuEFpBtb44gVDjpgsAGP2Sdft5fVLOvJXLazjMamPLXdgciV71GKdYGxFQ2DQ8zFQNowmaSJvbKZ1WOYgT/V87QXdjI7BLXRB8BOyJMBbO0AXDW3Qaeg8e5bdVeoqovsCv2rRiYZKGFweNdDQdo5CMAm9krNqccwi1NANU97oebGThuad7IKwZ9lCWnnWW3A8qbZ46ioqPeklUfQijrwZowFt9uQyZuSh0nIn3sOtzwY9GyW3ZZ2Kkx6sQPv2gsj0S4b0hqTM+F+rh/aYSzBr3u5igij0EWPySpMzqn7GFKmG8QSbLqHAWZsBnsdh4LQ2vcNnB812EvGVI5gBq5UztEeseT1FsXJfXe18aiqiuSurpApi6prK1gWrujAwpoe7CFLm41oPhY3m+N9YHl4JBdb8uethCOlq7s9LtDAUKCxMjYcugMdpRmJ5fKi4jHcOSwWd94wOkABMezoQqJkcDfdribvYkuOslvGsgaL/vIS5z/2xVcsE75xl78Wz9JS2C/LN9GD3I8KwCrkxkFCGMxyEwwXh3viu8P5LSqSKKuGaTnON3dJ1gDeYolJfqDi5SbZKFJi2uFREE6BwoGmxh2CrpjTA5/MC+vY0fn0gu2cmvSMrre08KvGZ7ihM0CxExy/x/s7i2Hthmjs4U7PYFR/r3EJ5oyvWeIlFTSenRANpkOAfDADX3IxgDq6GxwPzalJz+h6u8b7riuAg9oPwNA/k4LfHa2+x6qc22eV12jYZeiddRY4FGwOVHfgdKNOyLl+h51yDgOcTxRs5t5CLHbG/cb7MxQ8a8egCkzmsuIvJ6puGEiLXGR3VW4MBryY9BjwhIsMjViWlibZwmm81GgRLGNO/RwrjPHolre0foq1hR/ctyithxEK3GrJmkpkFlLS4AkgKur+yMVGJ7U7Kz1GA15Okoz4w0KqeBRLC5eVI8rU2+RyZrNTfqOFnUP77QE6yZ+yNI9WBi+piqBgVtkOyiqTrIh5mZcu5R2ZgZ/k9DVpfPlHLQeHUbTZpmjgA50uYcEDKyoOOXTeKeHX8GoK7suMigqmtEkH2JqLcdhNggo86hiZPSsEg0McNkDa8JgEveXDQQ2fLW3uqYcyA4uqp8qkAzvxTtA4DN0JigA5AVvu47mItPezMuH+nn2MtoPhGZ5QQbECSy0wg6VYbGef2FI3iPlWcel87QOT0ma1IuiQBYMY0okTObSyLUbQUMMWYxHFjwY7LAhYOvoGznr4PHabfdbyYqgawgwiqJqf/dijfY6y+j6Kq7pAhQlDguAgS1KQVkwJt7DM9l/dmTkYTEnv/WCx4bGloy2eIrS6yelRyZW+pp7qBSOXV4tkzUCcZ87bXnTqVmcbEvVVpqPO0IK5uRaAJld1iq6RCSl6yOAUGBADA+/KThv/bbjhH0cVeiB5o/SaWQQFM9t0UFaZbUTMy2hjeUdm0MRy+u6HFh76r5UkATI4Iy0gSZfRC8lF+hlV63ylpwEMDu6EaUirbTDcxHwkIWhbnH3r8sRuAiiY1G2AssroJuK2pcHy+xD5IGZQTfLZMWm8q7Jo5miSosVUNCXwsuzRI6ht/miokRmXftrtrdfZELQqtR108mCS3o5tYEb9jYpNUpb4A+nB56iZnVvqa/OwulyfG1UH9TtU00rPmzUNWdP86HZO7dmNbQ6fqNU8G3lIubrLGXBrFD/mNR/cJHyW6whDBKDaAOtaKRDT1qHjUmGMOycJtsObY8diOd8mXRIq75o4GGyUNRVnZf/Ft9SG3VqOXfd0u40Xi7rAQ3/otnm351lC8OqeKlLUgvNosxXsUmmrGjN/qycIIxp1Zo69lMkMGPWDqrczXNinoRfYxpRHZAhMeNPpOTjD5qGNiEY0lrcZbEe3APvr5tvIwcDVXUwayLT+wHP/KX+4pX4TnpEKgKIOxPMUiA2fq1qB8sdznd85zjYYzwzMbDB3Jr3gqu4E+2pzqkPAEzHsfiZRV41gZt7cy9TpRlyo4z5LrnPltp0IqV6I0faWwY7u75MUf0G3uqddBEhQ0fVAVmpOwGz+mkugjZG0C3MoKRlhjZbNhZ/oO4rTzttrdDcEBoeXzBT2x6sXTBC95d2Q5bWYehyzLJyqebLRbIteDLnG2yoSsZ1souLl9DleR9kDusKidoz3SiiLX+Tnn7qa4EkoqWR1AKptBWTdru/TqELjPs3AhsazYNIXBZrdYNDmDzvOZKqEZ0kW/cK8CHZmbiYECW7BfUz95dguXqNVnaKbqHzsnU/0NznzaSqCLCjWsWJIXZNQ4nZ6LDu3upuOaA7eNpxPk67Q9Rbj7L/XqEar002UpEdVFcXrxo+qP6/U1IP4Gqxiw9m6Npc5rDTs1QzMaTgnJj1Z/qhSMRiyJkQPTrzZVZ2bPftmZRzaj2gXPUTmw1qWxbmpNebyrt4uMPptO7C4vQ1iwGdMBQ1TO7Iy2wTAwEyfd8520I1kXp4F58ukC3S9XeBUSvhoFrNTjTRh5lPIdKtgTjCpxOwUN6tHtJgqBubUpC9UtcXY+ywv6s11fVfGRdLmhNWeJMmrQAwtQNsws6IpqxPOIDavvjMzMKCe+DZ+1wUzoiQ/ogp9RmVJzJezIt9o+U5RB86LQoPbZUWRNzQ/2xn0Zga+MyC+SS/oervCfDe5LeuNNSZlPKqZxdlO7Mv8TCeS3aQPY63FGO58s82LCnftvvFImPhQ5VUglmOgbThO0Yy1xzQI2+k7NAPf6Ylv4epMsodFnZ2nz9bMJ68CMR8DbcN8imaWYT59h2ZgPj3x9475cENp3gYr92yi5gmxgpzxRlh73gPagZw9KgZffn+sG8psPCufNZMudFUWY9X3Ufx4nl1Xefxotx/WVYRYV1LHhoO1ze5VDLDpaGZgZtP53IuNtmwwugykmnoz8/Q+piU1HMuCDL2XSUqz+rxCmy7ukPypyNgpwMIJO2kwu6SdYgPgYWdW72rWCMUQZmBMxQyZtN5Un4cTT5scKrhOhWugojeyN1FRXdz9E8UVKULPeOLjRuNHWZZXDZa/fi3RcVoQfij/9roqatH2JaivUTX4K7ZJXL5+1X6nM46TAoAduerRc5fcK0EglqH8RYsL/6rwDgJC0xVpUXzK4yhN/o1W3fTBneKh9F37FGUPdfQAY+vKzDqHrityI/IKlQ3nSbvHwWmR89lKIMRiWhlzpGNSFQ1mOleOBj0djw1hZQPiNcjo9EPwtHMJinSdU7G1MU/3OSQhJGNOSg0SNmuqiIjNSqpD1ocdgZjGWC4jNHm6knRpLDZD1YWISHEN4Ts6luVyo4PcKuSwN0OqwKVnJzLTMm5qygxYso3RlSAZwp51ahavFAh3+wdeNkFMDICew1Ga/EDFy02yAbHR5aaEfl9nK7w3x2u1guQjkD3aIdUInv4K5mBdHdNGlXqEhdHPXc+5GoUAgBkrY5Ui1iLprn6BYx1uyxl2pLFxYM3JgWhRvk/zh8u8BHH1ZVokzVm4DMtQaIZGanWNpSaIIhkGs158KPJ6K+1FU6pF9AU9lTJB7cv0bJ2n6VFWPkm4eSg1QiRDYWWOKGwdAEyL+vQZa4wsSo/qak02BHFjrMstNRW8trEm8wyEtUvjY2iyfCWnoG0APqy8AEC9BvuQ3GN1V6y02AFAc+wqhGb0k/H0UGiG5ktegaMbCvV2LXrCnb7M8daxVNFLhDMwBdbJdiPRsX2ZMRIZxehyLbLvUZqiSjVOGkI/wiuEVz8sOP+qEay4GQCDseaNwGM+UoyXhTFd1K+3KE7uO1EfjA3F8g5X0I8BrNa9og6NRwHv2Jh5M6bEax4RklKqecfHDBH/0O9xvrlLskhGHH0tbcP0469QE+wLum6jsOi6aRMmuN2RduzYpD7Wox+hXRsybsR1BpqchhbT0OWZ1K3V3Tuc4PI8PGeq8+H1j92BDrzx4UGTbbxEDsfXtIyQkHeV5Gja165000An2QfpzqbvN/AuaVxLhn4lNsG4bMGkk5frzFY+fSZotIqJdG3RSjlYliNVuzRDGedkxo0ko6BlGya49ZvcMYMTuM+lE2uZo2pc9mp0bco3rZcZ7JVRb/qsL2A/xjQ62kGl8p0NlUNFg+ZroUAzFOq3BChDWH8ppYOH0avMNdqgRmXfwS5GBsDAJZcXEldckyrBpLpix0qX65EJ169hkQSv0Fvg1uE0sJuAyzGwAQVecjJHr0Gq3/1TN8vA3T5zvU97LrHZRskDaLL0ZQbLbBPjfIM221Qi7xyIkSPoE6oqVNCn/jK3kAhp5rLS4Qbu/2jtNTrqG7bQ2Ah7LW2jsi7Qd5Q8rMHpZgBM0Z0kmGtLyah5GC1SJuQYwsiFeOvk+iWLVfpnLDbwlLGxfbB3jI/ENEKqGC4Xc6lzJ8MRLqB3WRalZHHkp5RSAM70aPNFhZiHMd6IK3ByIAbWGwFbKU7fWAj9wIu8xLvENFWg5GEMBLQPuIFFcwzHMUA0xqfIkNHBQxxCKppEHXVwO8YsUHUU4QdjBT74hc7Rqag3hEINg5bGQwjxNaZN9MFQdBNj3AYf0cQSy5SQdNSInoowtGZ8YCUp/fhgFx31YOwTk45J2CcLKhFpaFJNPlyD2hBVuaAUBT1NGgAIKxu/N5XFF1vps2eRvEp4+bBV1SCCUiEwCmIqsQJUpIfmTTkqWuX2Mioak0KkFwAlH48IDNGGjaJRkAdABxBFhc6ZLORiRR/JIyMKBaMbwwgqJ0iiEz0B1ZzEYOORVCSRp1KSjAZMoORDHjA1Eo9wjL3yphIfWkWlOQMopYCWD05eCaIYEA+mIJwCt5TDIKQua2QbBHZ7gu6jOq3GcC5gXZSAKpYquAa4/o2Baqq1T4IQ0tRU1Jo3lZjYtNshcE6kEQwoHxAID9GHD59TEAnGCZFIRnN3Ceybu7i/KJKHJFOInwCqlQ++hkLwjORNwDcTjdjYRDmBGDj9aGhwb9IwyAC6cPGXoWgjjXu8peMupSQzqq4dvAkWBYFV8Z56yhs1Dtn3QAxr+FnpOjE4KQxmgqtiQQC2phHFLUnMtSAn6zTEtCKjCwGnJN2sROv9grdjzLKUZiKsdlhCFQXF5FtAM8xT7yr5hvVMJoCaj0rPYI7kmpW7aHf2LRu+Lt89SOvo7XxZVdVOgo29N9hMSBuBthQq7IHoqudDENxuoHp+9CPkPGzZv36j2GjwIPJhcJAQaaj7Ewp68IgmtpaHJ4DYe0FyUrCA+nGw+yRvsrDoAOKot3Ae/u1bqqNydzYFpXcuj8AqZ7WZo5rCBVBFSmQfgrxPUpIVaLj7o6AKB2owHLZGAPpwCGciUneLaUz9oqCSAKsfFV9FRafxzpUBuQTEoPNHMgEO9OpvainP3UQg+UAEWIg01N0xBU1EVBMfqQ0NKh2sAJTBGJQOVUt6zOdAHZq8LNCPBD2ZEUYANhgTXycAmQSUc1DrON806bPGK5AwoQQ49YB4cG+FAyIFCCSlubvhfNVMy0eUbu/rNCOLAlOgsKF1NbXmrgaBN1UNm1EodVkzDuQe7s8qV0EASj4+ERgiGX2rV0EsANnEq914E1hPEg5MMwwWWkqU/o6yjiocujnIcktfUJbQhIbRjIAClVAj0pOBRgLQgLlwHZI5uvveSs5oYYzmsR1LGJ5occHEECjqpTToa/MqtUHBmcg6NZYQqoNCJ+MQmMA+tPmWV+iWSk+gIA8HajAktob3kiTDK6NWqMW+T1Sg3FmIQPKBCLAQaajUCQqaiKgm3lkMDSoNaADKYAxKk9mSHvPZykOTJjsLObDBmEx2FpZkmn1nQVrurfUxAwhMKAFOPSAe3FvhgEgBAklp7rKzyNNUqWhYAIUBT8OBjvY2G4pqD8CgmFixjHlcbtt8LPDQaRB1zylI2fCHrDIaItCooCMFiJKOJMBrWxO+3iShpHPbwOSQgavHI6nlv2lU4JbQTTIFnmHkXdy0kdMUhNf7N6FquohxWTi3eQMT759Ubbd5Fu3o2NRxG2oTNz81PZtGFDQFw/wd6KrK2KRkUrOK8sEb1YfIrMlJpaC3WZMTM3KTQcngfAmEk48NAoeo1yfZUpAJRDXxmVLbporfOAhd91UcZEyDuXgCykh2O6ZZFYmhriAflrKe6hIMn/bM4EYM3AYUCSq7aBOKkl3+OEMyttCW42v5ZUICtg0A1IOZ2VUEm42ElF48iEZ+RkipFOo3hDymeUigiDUSgUw6r4grsibEPDFEfd7B20vc43VUotX3pFpTeQRF0uiqyAenqQmRjcqZqKCaDrGMnULtmqF0kLdjSkc5DeEK+oGC9VT0s9BM6jYAUkrnKBQlv9ITakxOtpbleJnKUxKWbWhyvUeyempUPwWi0VMjpFTjdSlGdRqPwjQlCYA0pEpzVAkvH5KqGkQpSRZVBdGULUxsx0JtayVUX8lusI5iY4nXda5c7g52iWjlwslByMfFAoLXBMfUuqobgiyeKQWTTsN7Oyb3lROBBdSPgYFXkURvosEooUheGZWdt8598kWtR4EH1O2BOXiv/TSPa2KnApNyWanNJZDy8cAVIOIImaEVVJJgnVhrs61KlQwEZjoUqbpxp84cJgGVYltjDICQqvUFqgAvKmwicOWiBSKdyTFvooGksHofuYkesoyan1kb8fnVNReSVeCqFUhaC17ahLzwyuVNjnyuC8VCH1RyKQe2GKRKOj3pN5eMCg0r3FtSWItxKZxdnhSb9VZnF0msv3fIASqOoSH4sBHR85IIfv3AgGSaitrBqusrrm9KX3bQE1jT5rQEVz1ncSt7hAIQbwc0Cvm0xwYqBfXTHioF4dABaD9mxhTB5UT9Romt8CixuXK3msIzipmyI8ZzKmEqj2Q83f/tCxLSKWPAtCSgob0z8dC45Dqq7X8oqigeljHNXmSMQksBU0wKSquf19FPgnEX5suUpOqSfv02qe1FFf1KPuWczGpEqTrCvqLkNB0MCi+q0JgWmhimC8DscK9STTVFDhLiLhYOsuBE53m5HngF65YvUpDXqLqWBCZYFGSH3/3SU96oWfksTGGh8haVvKfmk6JCYksjBS6LCXKYHFXDBlOk4Izw6shEcPR1nRWHicgE1VRLCQv46t2tpNBiLtR4rMmkRGcxR/0TavYTpe6BfNK0XOKxYTM0qSztJlPjyGzPNreZ0z+6qL48CUDJhyICg/Gx40uQqpBYEdfU1wEHmyvabFM0Pkop5xcOUj/JbAVvnuHQQTtJGbEd6DM8sKm9hiOBlA8IrgDRh34CVEEhCcIZrteMLSs2DCKQyVAUmwJrssxq+I8yrN9EibBaORCqTJBefx560coOdi5yEPIRsIBBnmiQ+ROp93TDUKB5OFe9MIFwmkFw4FKa9K/66ojC45t6gWLzsyqpA0MqxgNWAClknAxVgnNqKonv83Bdlt8KklVRmCnqmiFfGWIRz5CRV+zCqOlMaDhA24yyrxSScgPOaZX30DB5Y7vd/SvINAAZjKSHDUCUARVAi66s+RImkHnIza3UVgCUIphLAAajKcxSiAPIJn89p29SxSUikMEYVFxiS4952YR54V5JGAmkwpADK4AGIg2psQ5hpLPQimBsG+gbBuNTRSj5cERgMC61h9LeRxHRzUKZPrrTgI1koKq4NLCG7PbACKq/PwChnUc701tjpToCARVqBIIP+ITlzNoJegaTfkaze+MW0FZmNRWKxggBqM2M3u50aUz9UCiclMOFOdcofsxr3rEsfAb51bSugt8MUYBcDdbV8Ldpe5ALiweakP5qQ05Xx3r8aiMvHJ3ns/+whNQFeRa6MyJuzzOsqKJUcVtSVUGxDKvqwRdP2Aq6uycq9NPe0mFb7u8H8d9NiCmtaz5wGQoTEpte8DVsEVoOdZPq8sxKXCU/cAOf8odb6jd5/h18bEUBLh+0ohb48MoIoiamCi90H4gFCU0+1V0WEM5sYKobLE6Umuveio4mhrTQ0UA79jnHfHR/n6T4I7pVXKQTgRQTx8OCbNADaZhAwDXxpbmjOOXys8lOGSWQKoaGKsAykpqkl5MgnOGU8RovAORQKtlExcvpc7yOsgd0hadJ9RqzvpJio6qrC79AjCtpNsBavNM/1Ez1oPnDmIYstOEgmUohqMYinJxc8Rqt8B77Jiofe0OE/gYSTVdHMVJNVZCAQh2dhaVtBbptQpWHIOzfa1Sj1ekmSlK8OYnidWPSKfdbuiryAWtqQlQFq6jJqmtl6r2Won0iNJKs1ga1nIbcVZ6etn1DMvJ25YEpfNv2JZamsJTCGg2SqaKhoTHlWKQAvejy6TiS+mrOkHQlWzahSTAlO9LtgDGN8ilzfUnkur4r4yJpA7m17xHB0PJhyitJ312hodU0VCCfeAuCLYkf2Ob6jI14ojjOinyjIp0KXBVTJa0FB2zR4LpwLTnqeUl3k1sQjgI2HttYJzDRKMQTk+x8s82L6rLI7xtTQ2M8KqDlY5NXgqjGQKuJpkA8g414+mxDOQW0fIDySnBqdmPKKRDPRrkTtE3z1lfadURONwBWNzixipxmI6wJ2QDMkLWinA2Xtzej+PE8w/Zi/Gi8rmrryMeqqwo+XwrXURNV29DUDwRL2leER+uq2A9WESwdlKhzhU5nNUkY1DnkyJ9w3LAIJh8SAA1HDdNgushhESW4Uctq+8P039+2KEgSoSjJUDGU/f6WaNRN1H34/S0GidG2qqP0c75CadkX9KECY83uy6vrbRSTE9T/vH796nmTZuXfXq+ravvXt2/LBnX5ZpPERV7m99WbON+8jVb523e//PI/3v7669tNi+NtzPitf+d6O7TUblC5Utw07ulZUpTVSVRFd1GJZ+F4tRHArjdRUV3c/RPFVZNN6ZmTjt8HOvcNdlc32icNxbkj0OT0pwcnvztvGGmqccC9IX16A77YONLwDA+LKPFmhIiabkk9XPM6jtKowIKyRUX10ltQKzzyPK032fg3z3Py2tcvJWZA8pvFQn83x3ZetvW6Ry2ZbrFF5jg/VpuUH2P/zRzL+3z1clyWx2lEzlRoXGyJxVizOK1XCGu5BA8r2nLDFUptqHiJ+/KE5R0XVJhzET/FEIA5/r4yi3T8ao7pJqlSjnG6TxYcuMZ2CoCI/m4zK1WR81PRfLLjFZFHbDB8RlX0X+jlqVXZNCa2xA7jCRoWUhEpU2iHFyA+9dkc16dkg5l9dZP3pxY0RqHQHO8VylaoOCq/J6tmaaTR8mXmWNsa/8gzbuj0d1ts34to2yXtg5Ayxba4sTw8ATMlFNrifZ+TYBxexfBlFtqlSPICr2Wcdhm+WmqXm+gBUDDNVwsdUzcWwE1+FKeclmFKrHR0fZcm5RrQzWOBiO/3t9zyz1sYbwUTgzP4eIPFzJyJnqmwfy+rZsAknHKa2Daq2tNYOKJtY2vVnCTlNo1euqg8GhNbsjOzjT9UTYivz0R3SBwmWVpzVye4iftkUXSfLFQMIQI/kOHjzrDGpxx3Pvk3WnXd91UHPD4XpWCAYxrOafvA4xi/Wpg+3YvDPC76uwU2QhCEzcQPRV5zGwu+zAGrBKE9LkBumILd4frhQWgvXpe8bm3C4tKqu6oT+x4f1ym5RQyz9VBojvdrlvyrRtcoJw4cFitXZI7zLI0ezje4PyRYRBw6UGyx+ag4i7H5sPymSGJ+Ko3Pn8bAabXMdVU0T3qXjRc2wDrGYXRdyrRoppH5sGtQ33tRnNgSe4zAqsEV2WzDyPn+ZVo/JBm/D6NLbDDe5HUMbOuGzzsjBeJVOx8J0F1FNOB+PYpdcmIvP2v0bcxAUzei9Js/FZ5pJvEoTfMnFkH3yWJR5BiQ7xFUbo6dDtznMfNlO8Nu7G0DHy5T3awwYC519V01g88KhPqL+ZxVyZRYeDWj59NntNlyHmLqsxWuzkJrL9xxCJkyC0kkl704bP03+7PA9soOdBTYlix1VtnYtDd5FaWfkyzZ1BsWKVDshDt6VuLui3dGX/A5E3w0Bo3LxTGkqb+rOkN6Tud4RndeQhI5frWQoDjn1UT3yUqu6zvAMUV/t3EkPST4+wX+FuHZ551JXKHFWl2gCO+sL7KvFReIwJZYOC62KwlGtsRmw1aSAL2YXJeE8ELlO6MnAh0neZwl7d9B0lmdpoB7avi6nIp5n1fYOJNiBYotvOzPFSqyKP2UZI+cp50psehttHpANySWi+3l+NkS13X1whsp9HebTUwSlfwmpvlkobo6vr7B1mCKfwtbDaD8EJqixnUZFViF9JQTtoVCqU0vu0uKPFKmwKanmO2SfyO+h/1Xyw31EKl8k1+jFMUVjF8Ha9//4ckNaBjS9zgUeEmq6ityI5zHSRdYhZ09XWQf8Xgvm7ufXOgZU2YRrBiVQxLOLucoF7kIASwZyLIX5xOYG4UgyOHjHOcc+2xmnqD7qE6rbwl6+iycuQmFO2NgjslsfezLHouDfSmvuqv2Zd9jQg6+L3yZBUeW6BIVMSaRoCi5IvueyhBD5fbYjzbt/XMIc19mE+GDWQszFRIkky2xiT9Zgfjo7zbna/+qE6ylj/EWPM/E43Wo3ELTSbC6YetnoVlgmssrMn5lISzXtaZe93KmsK7RhTuj9XTJ5s01H43JQfupq++sBtyjfTFNYdleTwZz2O/pcB12Zfu4K5tqNxJ+33TY3+zK/ibUnnupFX/McuW13MsyfZms9fK60yz0Z3mKGZHHMX6d02Q4Site69qdwQe4A0kc4VkpLKbUZ4ulD1u0YjTA+NVyEQVQjZ8tFLCwuFkvYckzSoE1ePxsZ2pgZROJlkb71RzT92RVrVk03ScLBYaShzXHhP23n0tVn5c3RYQ5WrgfyRTMv3B+TFYrxAlf/80cyzdspAkyPHy0lLsu7wFov1JlO7aMNRrcbiHDU49/X9z/H/CK1mD8P50Xtab6XMtae44Eny7NvaxdpzUXsNZ+sTmHzE4QyS7Qv4HIHkJyhUso38ZqOhZ9fPT38ALiYpgdpXf1Zj+Y+AqVwh2x/puFpm/zX1xG/OrIFFhG1AFRdBIcx3m2Sshu9lVzpaAjZDsLgpuxwPvKTBZgtAS3yCZ8YXaBido1Ny9VfRapIeWt9zxJUgCbTpS0+jSCfSLo3BOJvl3qZoTwLLPPDPHYXO5DaFFMZEe07QqGxPh5OU/o3t0VvJQ/PWvNTO48NDPrQCfAXJGtWduE9jZ39EHelMBY7J6SMrnD3IjXmB/Jqo7SlDNCQABL/EkqZKahv9sch3ZrIX8aOnye1dhfN3lBJWcyYqldyKoUsVBo4XVbYevgON9sBFcAW2LP+ZKTNKB48WCxro5cpcIQh8M/DW/Rx2ZX6EeCnoSYXAjCooXttsh/oNVVRBLdXPObCqDYQhvklQY9DGHf++YGEkwfEMJpDPJGpEA/Yzjk9WPNdZB8cAsWwGtt9aXe3PHaQgZj3sqHir9G336x8fB+SO6r44jPLkl/t+hPVwcyZ/gy60Cpi2qNisE6hEKlOAjrFkZDCMbPlFvIb431Fhb8mJhAR6sVh42XZS20zeye5E9ZmkfC7I7fLbYSXR0h9Iv6bnFakaWtePbVOTpA5Tby99yK08W9BD8MYU+N0+dtUjSxaSfRSwlThoexb6W5O9hGwEHBolIoq2P56wgbWwhmGaDYQpsyNYWsa0KpVa+/lqg4eigQEm1TsdQqjHasKF5eAopt5HJ425UXTKrARn91lY5f4hR9QtkD78aFIVxbuERFkgvzKINxaKUxMBo0giaGIGwoT3IAnGYR3pkKSpEpssEpzzrAl9nsuNqdaZT2tY/XJNc8v/uSQVmd8sI5DpgCG3ynpUDb5pNFlMaYeJRnM67IyiYjp7DZDyyxuDLeq6xFJpYC2QSe5/Hj3+uovQbPRZ4zRdZuvKb+0Y8oSSPINyKHcmsJHgQMYRPxkimwi6UWu4H8qR179/yxkFoLKLfaJSX3L42/4ywv+v69R0IeHCWgRTBzFD82CZ3JTR9+KEKh5X5b/kyIsPE2f1FE3mbjCukyc8DzDkPYttDm51C1wEOYt9DXua4Ql0uBLbHF2Lx/UeSpGLoFlVtFyaC+Zx0KIWRGBLDkI7TqMCT8Ug0UW2khsg6/r1/e11Ul3A4QSq0xf0/KdZqUlQI9D2JBmVb3pgiL/yXel4qeQhjCJtgjTZuqScxf8GFKLEPDgaBwKxlMVwCa8au1d/g4Lyu+T1SBxZq8RXESpUDv2BI3jMOFMnI9SbxupoR0a7G7cqZtj4ez4LDO3XqaVagoIUaDAKysAKKIGSwIYh8loJVHwLA9FaDVzvQmQa0ccpqRK7Kyb1BZHVVVkdzVFTrON3ckZgMvxsA4tMDzh+zL8H8HImq/W0fUQvte+53u7kQID2uEWr0owBzaUikWKdAc4QjSlWIHL/b0GfP6YALez8AWWqwAQTPq7UMEOiZ+8gMVL4TVBK8nV2ZvyX/NEiEygi+zXTHLm6hI7u+hAx8QwCaGAavX4uXi/qJIHhLhBjZQbLPXLFFnMACuMbHUAfNnFJV1gQhdJdgZCIcWoPwBQqEDXvJDiZsGsMBfZ6sUte8jicmx+EJbvJeoIG9Vwm5JCYhjG4QG6iYGCOdR5K1zE69wypHQYDa2YZJdJs1JrOgPZIqsbCa8sSNH5F11wWLii3cmBK/VJN6pAlt95JIpUFZzmhg8cT21XUkbV0f5PklTQQy4IlucsPjyZRZ+9af8E6rwjua8BF70EEstMK8LhFS4gXKLOarxViyJQcx8mUM8yreIUytsyULWaHBbuat3nOePCWpPOmDMHMTy8ThHqxWuUZ7lxYafKq7IxrZscmkd10WBslg4LACKd0dDt53yVtHd2JzSw8uq7qqS7nsM5ERiSiyOq/GWhjuebr5YC2T7DA0oiX2R7TakFYdKWDrE0uWFe7eV5l5sk/MNtlNPs1XzNgkXG8QUWXA3tsJWDbWIaV2CG0MZjGUreAU/QXGyiVI+3EModel/Eyui6T8L49zKCcry4R6doiUGzrk1knBf2UwLsDPL1nWFuRsP6UeS+T4AxqByefRUXX+aRazb2Eg8N7PvWu7uyA40Ei8GsCX7psiXSoBKuSM9k6BSmFwSoSqr76x5lqd58RE9A8/NcUXLmlfhDYzzsttqCEHa/WerU1UxDHn4aHVaCuDpP+6MzHVnKK0TsDFBQlwbHdG5XyBV4ZhGBC8D3kLuvKowRqHQ/uAFPnKxFMNYni3YBdPlJLmN/RXjhJdFk7RCxRAhwEm7WGoXenazrjd3WZQAQWdUkTnO7j4xi234uG9Gyj7sNndFqQ882LJkYB3PYQ+h8rUop1kBxg7AKrsvsdgi9APhETIFDvgaU06KdCi1MaIuC9Qm3BUvdjBFu8bngV7mYrG5uHS1GHbfbpHdGXG7MHJenmGVW4+XL3m+EoqX3Ua0YUD9O8Bg0NFQuGtSEC6RPoDQI5fQAmn1QwoE3X9VsvqfQTAWfrGZ3KBAvu/fdARrcyu4s62s/u4zbIj0zO3oxZgq+rvVKZ+gTvtvNiHB6fa+Tv8blY1jno8N5gqt8X7J5WiHsvAio+Fuaa5YTmZc8sVyKGZz4XfNyh6Ut1Sk2y56gevS+NlGKffpWXhtPH7/Y+5DLXXw+zR/6BNa2fOlsvZUx0obOEkBU2Cxj8RDuMxL0fFHfV98lpiVrtN0GREZz0VTiWrS9bNtX7KKjoUWV1mishsNd5+F+r74PJ7lRb0hXPUtr1xOuzT1p5mxoVHxTQyqwMK3mfORRO2XxWfnC3oq3XWhsva0ulA0I9mS5bQrIQrko6O/Lz7vl3maYhbEQFcozouVi1bVophIm+J2j7LySbTR2JKd20D6xnK2WDxM6blt6A91IrGi2xIL27IkD9PzJ23jV3NMpxvhxKr7ZMGBUVmSTJ0c9w1f7TG18ZpiEl++1B7zdcTHHbAlNiHaoVPEhk5MdHR/n6QJkGyWKbDAR9LI8AmZum82YehhL50excDBuFBocf4c9NLppwjbRvCulytacq9KuvIpfyBxM8B7v2KpHeY+/ZAUuQBgMVtRWldASB/9fV5v3FlSlJXIPtRnO+qJqMavFr2q0xTo1PDVyj7cRtmLYBu2H+1XRfnN4bHMYi+fFNX6RLioQH02x/UBZYJTvv9msdeJuky3JAS3LnldDBTb8OsG/SPPBAVPf7fAFj13Zw5Q0k2x1I6Pm60iiV+pRG6my+ywtnkphSWYK7LHeYJ+JDHwUCFUvjOWdbeWeJ6s9yuSvWEtrTmNXX3Q/WaYQtj4wbR+wCsBzJ0GIcsuX2jRRyFi89gyWrMThF/5fUH/1RrTOxDTOxtM/0i2xFEXpeKFQK7IYle1xosMtIAzBRby0yep5uRn/DyHlbyQ9u6PG/y0d4/F8SgHrjqN/j4vJbGpTIGNxsweauBRUvr7vOtBl/KCspv4ww6x3CLYJfDbWV1vpGihcgttjWKyJL/nn/0cP1sHzdxED+V51nULDJ7hICx4k6QH5biy/WSxF8lX3DrSfrHQ/s1DMK0nCXwjZiiywNmdjnd1gfdJYQgLj//4coq8ESmQzX6Fv/zbfrGyINoUcmJSSabEwtZqU8UJ+OjvNhL+R3wgaap793tnEQynq34mwYDG57h5LqPgJt8mseB76D8uFbgknoZav4FxCYc+Xe6EAzh8igaXa0dLilnDYgHkrMHjKmiSyhMGdYABHYtKGiEB6H+kCyyW+DazFbe69x8tjJpeE7NWzPDVwiEsvjX3zfZlOeIGguJx6O/22GQzCZXbY2/u54N425KD/ltO/0X+is9V582q7poXgEGdN5RYqATBO2vrmZVa4o5W+JdhHeO1VP95CYVHxBw065iCg/Iz5JnDHeyJTMBGBQQwARs8rupQUnkanbh7Guzn5u65PR7uDxAvJId9ULCfFPZYHEO458xis3tnFk2GFY5Z2k920UZipNHU5vNPck6y4z7pw5mEqp3d813/Mc8WlsruMFy58LwXP+BxvIkiqzzNKkZahG6fzG2N9i+ck9tk4saVLprDIl2QB/25z5Hv9tNu8ue89nZApzWhiwND0TI2EDbhni6yj/kGXQoZ//gyS1vgQ43KqrzJxUudQPGSO8Fdt6x8bYeF7/Nd5WmPNszVPgqhxy0/JZZplFPYEzF6IDKMfZlN8N1eps86fa7IJcf0qK7WuNEkbuK82xu3flynwuzAfnbo9oEPQ4SR91Q5XxGa3Ce8aofK7bF3C4SuEQDMvK0LMrE3+SPi9jn0d0tsR3GMylKGkym1cVbkPxK8UMquNULlOyPtzRLvJ9atlWAvv5J60whq0xgU+c4UWOITr5tTn22splx8JnD4uJQy6l6Y64KnhMw9QqmNpdw+MydBDRRbzgt8D5Arsu8vjFYstVAd0QsRDRixUGiN9zOq1vlKqpdkQPYcp3oJC4KwaaGtJ76MxZbY91l9F1MOZRGG1F885UKRxs+2fF3fVSTx3nkWp7hjEHvzEI4tnD7rWhgg7Fto3yvqkuArxwJDeraoHBsMadtipxIUY+MhHFtQjIWHcGwB1xVlD4Zw1E9YzyfEco/SM4RAkhmAh2gbJKYBeIi2QTIbgNvc5WiqCPc5+q+W/AFznQunwQ9vcEVW5ziYUO9xCXmHiz/IoYtsR9y8f0aeQxN281C5C3YZVhtsV+gedwGtoPfF+TIbrE9RsbrMk6wqv6MCYW4Unr6DQSxmbo3ix7yuhqz90qMuNaRHi+X/2qSaploQe3tD5k+Hyi38w4EzEA17ha1kD7G19l6TnQyWiFb4jjGLQEaRGtKi/7iWeAt0/GqHSTSax6+WmKAcLE4j/ByVj2ilpqYMxq7Pxz9+vBN73H61w3T6vE2Kxj33Oc+qtYhUAHDF/98oAqjMl7tx8ElSoLg6QXd8ghUpkM1ZzFCty6j1MU+BcxkZlE9LEAfJoZxaeh9lj+LmEARwxi8KKwjghv/8WI6alDlh7XJySDEP5U7Yz+8iPmKUL7RfFxqbBMzJB0NYSFqNrdwi+XcjpjdFlJVRc29dWNgUcP6tiUyqhvRv8QqVwkuNOlgb7bgl72Yo6AlD+LQAjUgOZRVrO1h5igEpwGwiA4p4HZVI6jcGAWx2ggl8sM0U2Pso270i7KHsy+yxki0iSUXUJX1S+xWNK9nEwIfJaLkPker0NuoKkafWhe2mBMS8jY9R+QU9de6FLzk5m4uBNF8KMAu9F8doW92sE9zjCH9uTjQ/Rtnq4oewCVCD7syhWe+W+Fri/drHpKz8H1sDULq8uGaGZpojtsFPy8sp9d3SKQIe4VguT/uWkuJDct/s2QIyF4DShbnM0EzDXH3bPBb6u4XWLtHqe1KtQSYTCu3wAs+NU5//AIwbhlc9+HO2mOzOBKS4BXiFQQ5lz/3QSSVfZrEyAx5ie8/wedn3oEkyHQGvpQIA9mPHm+EttD+Dym2srTjZJsS8EexYrsgBJxDPxZdZ2OJNXmTA3Ka+22IDOsgU2NwgKUsh7Hn4aMNNI9kbg1PkJgDgJ9aqg8YIEKTleGNTUXfCYC3SoCTAaiya3568DPg05d/rqKEyi2r8amF4ZEl1WSQxfH4uljpgBk/HxVIbSsr669pXeT9d+wgeyzscvvd7oHZ5BQctAXFtAySDBMTCZNAezfoeySqOYp2PYE/ypyzNoxVwIZUrslmo+qpSswcAsLmqEqOsRD0O4RaWWGzRd6w/v6PkYc2/6UJ9twgUrbNVisgqw4WIUt+t9esxSYgIadi2wMp1mPxAxQtJAiN4JrgyK1kjMYE0AkHSRIDdMiq+NPc0vY2KL26vLCrqTmlU+BsAX4RLeF/c7t3d5MMzaRDr0MU/sX1L+7LDeLtEjC7OLiMs03Bq2Dj+dgicLu2+2WIBo+q4IjtvGeSDoL/PvwPdOwki521tPhAfqemxOIiKvOpua/KbIoof8cCg412+zB7r1yKFUTYFFvhIAChkrTEFlme6CD585svszSwQrVD4B5BGf5cNjclDKud03AxtAhEI/XcHNxAs69ZedHt3y0I89D1KU1SFsYZoXC52kKb+RHy0kzciQ607Yc5d2hqgRcYVOeK8JLclMckVuEeQJWNwrlBU8m6o/tv89qP7q8WL7cCqusiu0L+aNDKeWy8KldOmS1l/t5VN6OUqrPIKd9bQitZZXrSzBckdVWiDt5l21PhLRXnmCp3xyiP2lID28wZncBRLbTg1ur//ImZeo7/PoajklKbEF77ULgGxaYNcYbvJW7uER86W7Wdg6GIbl7z58xir9BCbFxab0wZGh2K314PLqOi2X2JmB7rE9qQFwsiW2GzYRhpDwUVQ+WKOzIAn71OcMvajwiyMCrRqjpzhcbMQS6zhB33X8831FsVDXPs4937ZPVWoXdJ+2uGbRi2GFD94JBdb8DqPBtZC6Mk1zbMkxcInXKngy6xU6NNF1pFBlvqTKbY+KdyfzLiqyfI0KBSYXYwLK3QTGRpgH8z43/LMOvLNvnuUJpGQi775NAc3S8fVnM4AEfdMgY0PKOw7BhhFzl9Hbz/tuMROIavBpHR/3u7ZTamZcsWaZo09i2JUXedFJeBkSywx3qANJmCFPia8BwQotgivy1bouV1KyAf+PVqhdGd0QTfnzRvfAexfjMfd2AUr76qc79FTlB15v0VFgreNg049zjd3SRb522l6/O48YYV0Ipvtsea0JflgcZsJKyru/lLzxcJGibL6PopJNoWC3JKG8//AMJah9EAMvVUIM8oe+GQ//TdzLN+TFY+k+2SO4yMQPvPRPtA5KlFDBOgAVyh0wEt+KHHTABZrclkmDxlaUUYsb5SAEEsHWfeOLXLJhMfKl9kE7jfx+Bxzj19/Hock1qLxI+wz5Ios7buLurq4b1B06cNFM08E2ZkVkGYdv7WOxuSwqqmr76qtQ/69TOuCz1ZJf7fRHJO8bvopx4XwlYy+aMkd2Hl5gu4jIUkS9XlnpEViek1iJIazDPfRKd5VkjoCoXKLMGL0XOFPmy0fRUx9t2FgcoSfFNDt7P67w+J3nGdVkafQSaQM5id2pvMTHkTmAgjb7FIWwo3mv/CFXqqmcZWFX6x221Umw3z6vM2LXqA4vHzZzkp8d3CG4cLK/og3gBZQIdtVIzbMWrtbXDIBhwTjjpntMYA3dTPMQu7b2rTkwaXShXi0+mddVmKIqVBo4RBsHHcyxGKpjVV7WaBrlKIYyObAFC25Hjen2pCJzBRYuGmT7JEEo4E7GaHwJ7xuJdlDNuScZJ/bxiUE2+xK0E2qYfk+SNQrBHbQrQfdetCtfxDd2ico8syO3GFxyYgsrTqNfuzbE1PXsSUWkdVjmichpwBfZt9PAaUjvvOyybWfCEqDKZgjnnwuPd4yGY9s/GqLSVyp6O8W/ho8km+oKAU/FVNgsV6to+wBpTnnoqI+74y2uUlQ0ax+fupmQOOgbxR1d/8IIvS9+6tclDa+bInrLyGiW46jNK5T6mkLbphi8c6ISaPwvKP8eiwOQiKvurTDcNEZIQmCQswJweM8K3DlaeYl9JJ8lN7VG37D2Hza/2W9/1NQ893XneFl5tVaT/8JjcrFYaKuP9GCPNPj62d1mkrPJIVCc7zNE7bj27OnWXSXCm+9SmBsdsabBFvxN3kzZyW/M+YKd4a5r+q0ZTDvLJU9IqfklIrK0zA1aRE4RaE+2+ES32gdv5pjusB/RBXvTRu/Wjp+AJfPTx1gwj4M55+tbsTlmK9OhWBpy1A669NEMIaMNnzIMKnxfr14ECIs2KKfXn2Lr3l7Mb2AzoHvDXDsKuvvckxho06IVdI5QmDccigrz2f0fPqMBDIwBRZUjZ6Powo95IWwC+GKfp4ISzltQ13W+FkUVoBTaRhnCNU160m00LrgaYQA5o0yO5zSTqEV/oBpGq5QXBckFrhzHIR6KgHC6vZcgiGmibanXPNi2kGx3GKb+Ud9i5YnW1huC8Bms601L3GKoPvMTIElvktUJLnwshNXZGE3Ek9cU5uzbpgCm8NAPH3wMwFMyRLWXKgH4c+zpEqiFBRwvuwnlvNmAnDBp9zzTJBC5CDdytrTCDbVJLgnEovtT8HDpL8EM/U6JOndZ+YkmIMxaONs9mJSGMM0jNqeEf0XennKC96JwRbNucMBD4cUp0ELsZC3XnPTZzPqMdzUJ/QDpUKMKfXdxnlG7sdBDyKwJXaHlSBCpsBi4d4erVYFrscRbfy8lJ4mGdOEoMrho82W5h4VBSoEXEzBHHpfwXMP/Ja+/2aO5WNVbaEAKvq7VbAs8Eb6+HVnVNLR/X2SJpHvCcSAxmUlk9edyNhqZVMwssbP8+8AoB2JfD+ymAmU4p0vXt19jZ8OjZPZI607DbPsahzWNLGt55j9+YQ1/bedYUO84oRgwwGNAxsq6v6x2PA6rbmA/PbLImdrUfZQYwtHMD6p7zvDxB9QhookDhQBwGNzYGk9il3nbLz1/FDk9ZbFNH61wiQgmTuMy/YSxFJ3XdZog5r7tXep730XGpXLnRd1/WnYt2mUc3S3n+Z0gPyBGK6LjfBhtPaxSHsGk9SbhrH8mUK8pmm3Vb8u4ahr+rsFNnJmJrokqM8Wbpy8iBHuBv7/KE2JV4NzwIAAFs6AvOQfzOs+WbklcmmEgFBoMfbox3lMbnNJUEPlFm6j7EGJHSq32OVutym6yet4rWxEAWYRnVHekDobPPfyQA0JjG0rQOQGU7BklMrHapO+z1eChUV/tzn+ySqsGvtsyl9Q9ZQXj/xpEAxjFbGEVfZLo2+P66JAWSz0XwZj3crpc9zcIb6KKl1jMtDdWhy7TwHWyA7MdamUVt/1nUTgPfKeGFbxGq3I7ZeofAwT1yVidInpMsIy0Wkn1bQwgVyZVYR5nWUJn2yT+myx/kTxOsmQyKpMgWW0DXg8w5ZY2CtJ1jw+AqDkiix6WccxQiu4n1yZhdQXBb+Kd5+srsc+kFOES4QNUDHWliu0xwseXgqFO6lTwukSTyWyPzu4EJnHjos8O33eEv4Q7obxZRZuD+HipO2lSduYDYUVh1cq8UJ6/9Vqnd5eZIAWYAos5h89V1ilCxqK/m6zanxMViuU8YtG/9XiSL/OsAbqFgjuUJ8t2hk98vca1Wh1uomS9KiqcPf8w4BBlE7PZxjhmUbFUI3ziLgiO3MVryLkgQrxNqlQaBORwkctt1+Wu3Dhr6A/JxvgDvn41XJsHXXB4VFluyiWwYTRTwTnS3cVatU5K3IuV0z7xSbing+0twvw2qYvPIrho4WdcczZFsc2td/H3CLZfLDZEdz9E8UVvxXoPlr0I19xc9p+WTLA7Rrz/E2zGec858NnO1zQpmn8bGHBNWteHJOH4ASvC1dm1cMVeTgwSlP+tTCmZGeUID1UPy1IY3JQg+rqE/ncGqUrTr2Dz1tcjZkCu9Mb8fDGSrvnBb91b77YBYBmwoDGrzZmU1mKwfbjV9sd13XJz9f42Wp83SNhWKvh/Qe5olQKg4VAdkZuj6PNNkoePF+s6LG43FuXVt1Vf8TPvMr6ZmbQ0OwmP4r5o2+mZGfkovMi9m8p+WaCZJA5pYPUYNhVYdkNs/xzvkJpe2GN3VNS321CLMuqrVkgjjxckZXp31oubfw931GgeB8VVrgw12nM7snUn7M5L3WKD162X8HgEbHYBfc7Ne53Prh/U+P+TY57oSXhC3oqP6GqQgWWnSGdnN/KAON0WCBMEU20ToCti88uqODm3W7ZXTWa0c0RKOv797x4xPwiuw0AFO+MnJ3lRb0JJ2ICOgfpMsAxjWCpxclHiMJetG0IJARh9h9tzMVtEvN4ho9zCOZi754lP/7/9r60OW4cWfCvOPxpd2Ni3HZ3R8x2dG+ELrcr1rY0Ktl+b75UUCyoxCcWWc1Dx2y8/74AeAHEQZwkq0pfbBUBJDKBRCKRSGRCvGo93zbTGQnLKLeZHIAnXnck+tDNgYi7+2U6nCmCSZeMr5ajCJ89jPAXLV8sEPTnrv6mA6V+8nv6clIW933/AU6xEexrEEY7XmojXo0DlhhfQJCXGagiDNraJwhQRtYJafu52iZ8hHG5Rh4BPXtF9WkM9/dpWfE8gl3m1gpbH5o5Q0pAvPLkgfPkYovyykMq7iLbJ5kUKANuHGg/V1b8mMZrXgAX8ruOB3TMTeZJftd9m8CDR5eM74lZTfY1QGb6NWKf3iSyxRrC4SHqveuuvmjoisFD32MVf9G488QZHy+TviGT/K4ODUqzjxGI1+hXz9zaK9LntLM0uYs2ZcZx4BNU0eCW5yIL2NklPuu8JkMAmmdCvUdkVJHOxUlexsUiuWPuTrrv2i9AIA6SNyBE6Ww2geVLErp5CNYBMvG7l7X2I/7dPQNbpmUWAiYYBPFZFyt2SzF7UoafWD4XLDiqQJfST0F+zyO1+q5rYFswUUK7z7qwlkXGSgiqRBfiaZrGPHjVdx2tNQn575DIgtmIhYtntAmfg12cOgil3Ydm4rQ2CMKPlKh1Uvahb/t5TIXTlQrmdvPrZoWnbrKl++0eZxsuA0WKxDnGtxF+TsUbM1Edu16G+9BVIqsDN/tio1+mZfKtTk+M0bf5rOsBwXcXMfcVwS25lmm6ZGpPDcTb0SNAvjYs07cFWmuR8Qltvs1s33Ji06BAGe9YrzYNPLxQDBRQaLNSgirUNQKyWJrlpoU/HyNInTD9Ladc52amFoU1K/QuZXqF4ysDFaMK7TGc4jHPxRLOgkzDaPvEZ635RzKbMYKQ3/W5qTKdsAPKK59Kdbu8u8tBbxtrvmk6BXJcAbVcJ4MivF9G/+6tD+KzxgzApYpjsNHj3n6dems+S7c7fBMr01CElXRvbP8V7U6y8J5x5mJLNSDHIEj6ITTbj7NRB06D8GGRwFkPH9y5SwmAGqgIypDm7U5yVWUj5ZyRm89TOV3tn8sCXEB3QViUGcgcvazgQDS5L1YCM1el9nsEnthTavf1gG95mwy8bripD83oOdsQiFcumh0X1bLcDRP1gBm5Xw5AeGWh2bFQfYKsp842NycJyygzpxyAt8uESiN6L9CU3htB+yCA9uGg+eksS/N8CeLYCUf1oZlsbIMgDperJnOrTEoXOV46OEaulOLGng5uTq2fjQkS0cE3TlYlOv729b7c87lvvmo80orWG1D8K+0HoCO/a+AVFX37a/1JY32UtzGORtpbHt3nMWSu1BL3Gp2gWpQLyLqupEMHy1BCyAD4kRK4VyZEXPVN5xoxgyQh7Bn7DlWibx9n5RddokEpiifQI7T6NK5swOlGz0FnyKNXClOqZdGur+nR7966ZgoPRZqdgk2UcHI1kd+1Zufp4nkXJGsmUwlVonEeTD+mcZw+9c6E7VcNSODpR5SsGVDdZ439POxzXvVFxzD80l8L9Sc9LM7YfBfEZx1nli0Tt7T5pmEMzvOzOOjn4O2+vu6y4l32JM/TMMJO4MxWC1c3vgNbnex2cYQor018OOageFuVt+vvoU3txnzI2dHXrEASdrCqXFY5m7nSTtyC5rEgGswWF0s0b4IMKrqmaDaDpYnm7++4863OEs1tzYq4tpScvTm1mcN1XadJmD08rCxUyzlvATqYcw5ydjNdw5lsok+jGPlxtPnMFWa730Q05TpzTcO0HNMeMJfT3gNtx5jaiJ2lyTpC8/lmkX8t4/iPt3dBnPc9L4aot2aea/AUZOurNEqKvM7YsvqWg/WPqLivdUWZCXiwMWv0ZZoo8NZgR5ZcRsNywGTDCM+R3YaGwZ2oWt5H+LuOrGLauBBWPaCWfNSH5lJc9WHPkYGG6bdmodoGvmquH4ONlHu41QVXmV0dBQ7iALackgaiA6bhIWfH2R2ksdWbNmHEamiu+zVZE2Bdjv9Qm2YapuUMd8AczHEPM7vpbUZk7Mllz12kk5Pm0bXXVHR6JasZnWCpfmZ9iKUxtWQRAtb0bNJeOGtxSNdKxByCe2Ol0W6gz5olWiSd7AeTMcJ1GQNYqjb/XWXRtNc1qhKdyW9Az3HOW9zsppoam9GNG431TGXCOZVFno+6E86AtjVkmVkFFXHb4wm/CvA75S+guE/XSrMuasFo+WQ93fnnd2Kr8VNAXej9fCz3mB2aE6UGPwibMAFPqIq6HCHoxpIlelAd8IQIzz1misYmcp3C04/SxsBvIDIgoWraGwSvC0e3HRimQ1sSjeOcGaG5E0WxcoIoQflO6SrtpWv9pf2dNx+WVTJG7IySd+3Qu9dtgInKd0GIRgfW+BhleYFY5jbIQVXl7ZvGJaZx7qvFzF/xWVzFb20qwCNSdAfy4iZ9AMkfbz/89P7D2zc4CzBKoRDfvX3zvI2T/LcQz0OQJGmBSf/j7X1R7H579y7HPeZ/30ZhlubpXfH3MN2+C9bpOwjr53fv378D6+27fvMarBKUn/53AyXP11QKRsIdq3HHQRGt377pd/fbIlmD5z/e/r83/00zze//FzBM2Mz7NbgjGKhvE+w3/J3DdAixP95GaLzxuv0TQHbADuTV03VUC1Sr9Q0yUKI32K2R8p0UPOmzWXWTPAZZeB9k/2MbPP9PEl6RlYPgGpfNevwqiLfoKa4mXo1/hQOcUA6Uzs3CBZFJGJdrsEiWESQ12NnQucibIDCwoABhAdY24LqIMg7orB3QHEDCvmdccG/ffAmeP4NkU9z/8fbXn/SnoshSKcwPv/5qwjBOyIZ6R1CHg3XDeQgg5cLnCKa7mWbcj8x5+RpAYZud5JWztQ0k0l3bAY0VuB9ZsMNx7BIRbuqwkAcgNQfmVJ6m6AbTUoq0CYKJnUcTBiYHXTQ4kR+U/5mVtO1cS1XBkM5rcqUheG7dw45AdeApDZTo/eWnn7SB0r66quynPEVQ0y5wdJZjnR4x3aj+b28W/7Gqx2iFPsDRsJ3R70FcDstdNWW1CQ5BDJsGQXVzRJMbbvqcwmNi9G/QHvGOga+6ELV6s8AM1gqFmEnLZP23N3i5//bm/eDUcBQOIkOWc4R+MUEIgUVxd+sXAgYrUAGzn9/8t7QBjYWDZdwC9EXRB2dIGksc9YVfM90xrPfBbf69yUTVA3hWxuhWXX6C0wf/LYn+KsESpGc4yqQMuK4q+jEONostRL0JH+j4RHtdWGm4Ds9fBqry6PpcteCr6NrXIK9MzkewKC12vbZlK3l/MtjlmsEeXAEfDKRDA9yh6rjIUWa5q7jcRImAn9XsmzdpGYrXBAtDmZWv2meU9TvdI2BjsfVZTr3EwGcjTYgpSOPmodqBDPVJ9SbUYjvo8ae+6GEhWIkg8kpPHxm6tQYiytxE9nAMi3l4KxhcqRx1KwOguca34d6b4PniGWx3VnZkCKTWZ6po9Vx1RmUbaRJZE6hIxZ32ZlcJ1orz1DoRLI1VJ6JXJFj62DaO2DdF8b18x1CZMAzpBqIUf4mSaFtuG8TXIIy2QYwuwuFfOb7Rfv8PuLiQXmpwvCA6Cp7ddaQstCS+HwcsmH7SPqZJbuFkVgw1HVNLPLRrgnZHWYYQqNX+iiHobqsyJEzMe3BBl7eUJc25tPycbiK4iC53iN9SjVMqRyWhwvrWCxd+KSLEgbpGDJywzxGwz0GOHPxD9Nh7GKSFAHm9AbO0YX6E1XUBW0osQ7eaAk6va6gXz2hOgvhzlDw49+sI1htQpV53QT+CVselMZcY2EVOx7yqglo/xnFvGWiiKPNisWXKUR1abJGtIo41g2s7qPUTRv2TK9FUtL+rUQPZF2e2MD/GIJtCo4rnN+kSxCAs+oANzGwNiMs6X4Jj5e0qi0JwHSQb4JyhkSfPZfIpRWmFNlbuPJ+CvHnOlNdP2GzAuXKhmd0FA8BZSrxph2YXGPukGp6Du6CMCxQF/AvnukxBBisrhw1DvyqHhjcjzQAisHYb0LccXIEshMRZyqn2+TADbcBk8YtxVyfbiok8dbMsggy9hADapySet8zaEaRr8FcZQYl7Bs/CKIjh2mrWWCiulM9mkvBGgde2HaN2cG6ibbfhOLxEpqIivEomQ8fNvThe8vIIvZ6OHCH7eqIZxm3/TjQOFX7hGcSlwv56qpjFqcLq+Kq+dTeS4xj27SoHsqF4XSHLaoK0Y1LEMrWaPqSVqpBZL/T9ElML/ZRWuIqeQYyEorTWYAWSbW22D937aBm5/Svnn/Xl7knM2KltRbmXfb/lK4uxY3nzvRLb9X3G9d1tv0DZRjpzOPNwbpG1GBaWYMaVQR8xRkNSRodaiSaXue2CdyXBPihxyc9Ksue9vsRAuvQaJ9B2oJj/iNYoBZ+x6voJRJv7wgLAfHWJRX6TBXDmxU9tPSh1g8zHYytm+1STY78MbZ88RmdZmFdrsAJ3+zTx6voUraFmYzNB3+GZi9hIjO/V6rgnhppS21pdmdDUVzGTHIPGWudo0p4H9EfTtpuFv71Z5NXLnt/e3MA5GFHF4yL0noeQpb/rMi6ZJ/O2fg9nQXIOUGwRKEXDB6sTvNMdD53bkDk7Ee1aqo9FuK8S7casjvFSpcU1tvrIzveqtvQs2kYJ6ZjGV1E//MPpmboWgscgpM4JfoZDewtHO3tx+lz0qooYe1TGiqsmYav2K5GmoZXzql9r9ByePtbjdES8ZH/pXW3h2J24zb5tB/F7lEewNhzy6DFal0Ecv1hpwAhcFPdj77g0PbfZLGzI9nKFyGaodKFkwNquYZ6s4ZZ8lm63xIHUCmDD4E5uJB26oNUwXN1WvN6V8q4Zr8FjBJ6s1PKT3S5LH8H6OkCBbpbdqyGjtZ0WTuE1wPCrph6xlvi5AjlD58jlQ+l8iRDOD3AnLL6W21vAOVaovrw2QOHPItK6J7DucJH/Gd0VZ0FmdR3cwLDXQGovrsviHmRXVCIP0yCJGF6nzAw+JtAXViWanAK9kYpfTtbrXpdW6C/y8/QpidPA7ra+hmHpEZnE1fJtwNlZSoLnan1d3jHwTLBrgFw876IMr5Lz4EUEUcm2UAPEjw0r/zpr7v4U5MsA6jDAxazSkAzCvvXa29wNQ8K+5SA72WQAbC2vIShAzp4jLfJrEJZZZvlOvgVy9hLGoBIbdvKOhHcFsii1XKYtRLz5Y7BWC2uRo/gCFwlqYsWvKMSMm2AFUMjiE1oQN9DO7lGkem8ez4vcSXyERX6RWw0hEUHV0iszSNAVTvIIlxgEViWAsDxWFWn48M8yqE0YFpK8Ok1heCePQQTbUid+izBbXByNdq8ocUbv5/SpohVtNGzoSH3tP7p7wSfwj2nW4HcKiOg2JmBPg/ABR05GT2DsMMSHOwRvUY0hPFy0x1IrlQJvX3X8CRcTU8Grwky4gNfAWBZgZw8HJ77I0pj0oTHSSaI1aDCrQVrJZjS7YF1DjAb8bU30bbiWUeXT8uW0LIrU6ka9hvUjyu/jKC/sAdYCKwZw8cHdhzIJGb1DgYcKDCoKrUxVFADn++NlvPbbQX2WOktzj8+adhBOEBsTovT8leijfUaF3u04eABFwq6fVTmC3FjiLpICZLk1L9YimoIKPDNQLcZH7RMeim4iUK1dq40NagggL06KIotuywKcpVt0CYv2S6/M6viF9Q/KAc/5aNNnMefgKfdD96zid2zaXcm1zGkBuxU4ri5U+G9d1C/ZVy2AVe0c2UZQq2tY+ftqvqKxws/EY7EHaxQ8f+a254Swc0GgWaA9E+z6jukGpvr5+h/DOY0eQfaCVr++HZJubWOFbI4x35LIwLGGbm2DR6Vl5DdBFt3diW6RqNcv2j2gTTl7uby7zKJN1H9BrhLetQ/Aht7TIAe12mltJWxhfQFBXmYAzYZ08PTftLRdmMdJ0OoG/UF3ZXAdcVom6xhUWaM4NnFb4VKBvwLZAgovFzZaCiAaBpfwlvdpZfaF2ozdJVSUXEX4xldo01LUlCHToGv2Gpo6UuqBA6sVewz+bIPeUyYPzrBhKj+N4tj29gEDchJ0+Sn9DNBgLXIPmThu7jMA1OH/rAsfbmwArusebCv3ku+B1XKeMCOHEM5Zmj5EoLpvsbIEOvSdOVmvIYT8Y5ptA2YnMbpdrWNknZVZBpLQIPsVA8DD46IG+KsIZdQmNVt0PX6DokT/Wc81pFpRDxt6xyFciVUuHMe6ZKXqVyupILYDR+BnF03w4ONynEO5ECUXyRolind+d3WNUrrh0UNqbO7gvIQhwq34vFo1Vq4eHXbYd8QVdh3Ec5Ckvddazm2dvS5RwHX1MVFPlAo3Dni6Sx+j5DgyatXHD2NLg+VjIT9HgttbdGoLBrMUGIT524fscKTl7Rh42ItSlMZp9gk8c3Ox2QL3qbm42soXea27CxhdLcqMrbcuHAotCLrPCCvzE9q0j2GhXE37OLW29Zki0WtuhYojh8bQp6FZHGHYZM8ZFJJmUd89vFyMYsjmrVeE1eEIOcHd3Jfb2ySIrNzf6veurwc1HjCLg5q5vG4ZpOKXYxDfHfGmwrNqayU523G39BZuwGAFyw7WAt3SVCFrvUTbbLwUjyjF08SaguA1hMqxtG1phcAi/wgFZNk991N1kplMojceKk36Xjf4asd6CY4rpvzE64QcboOIL1Trg14v2tmL0ZMJcBwZWxqSzZNJ2zLPrr65lF+UG6UUrSMmWL3V2DdduZmWY2LeP8uo7ajEsQkjDPIuQuOgO0k5SsY4aFTUjxm9JY6hroBeBXmOQsv4gltdgtoGoKpgLYOB6N4GeDqPSuToVfDJ3V0UR9ZhjThJkUdzR28WF+PdfRJiy5RtimMSlk/0RkzWbsvNKGGxZD/U2JRXFKgeZibZVaXboA5iLSSTrNWQKJS5OnGUXgzBa15oC0HqjnoD0ITtgrgshm8u9e+FhgN6GfDExyjLi0HL8ocP2nFo0Sj6gCvOMq0zxw2U/uMPAz+jdLsLEk4YX631VAGRh2HWeRCqHyGLgxQFy178nEZZcX9O+pMZLc8WjP5zCKi1rtlRMVIxvgd1FDLkb1LmdhoCuub/V5oQeoYVblD5qc/YCJhlEkO4jKH+WG7R/UnhQlZXUYRcqXoNxHPwGIXchChmSfwqb/R6610ZWOv7AGye8TQu7ebosBA8uPG2Os/hH1rFm7zZiuVt7YZ3r4L93Hh1OQPGPTsbQRLsuYawfLnPKa1s0lvRYFnTza2eRhK+FJYxhrEUeO8S2AcnwP4V7a7SHMVeM8sWzrGK3MNdm69rma3eJvaiG9Y2N3sqy/zTON1c4eAvhy/0F7kTD5DPQbIpjbL8dC2trPVqu43i7Rl+XEmomzbK5gwy8Vb0WCDCQrDaH0CI+jllnq+YPQarpynY5IukRtSGkxcoKJkbfTtdD+8+6o9Aa2Oyk9DkNSzrdD9E9G9XINFEOhn+NvqMA3tcHXDGAaTXqPvNjuHusdkYOgG2FxyLUnCT7qJQf5uom9n5WE197a8UA1ptJ7lScSAY8c5DfzCcPnbUjY/lzAsMr13MnMeweDG1+sunbtYFz9rzZYymm2MqVhVjVVN8b4akPrJmW+FTR50wv7X9xeBF19dqy8oVR0Cfqu+2uV6a8bU36CMoAq4zhFY9VDS41hlkIIPs1kci8xXE0bBXh97GcDR7As60Z7gx1G1XpJ+r3yfWHwyeWLs+/Hyt1Qa7DFk8IWwgknhK+cFLWyNbEOuF7YTNTdzJ9s5LtxuGY5CLeyGFDLh6fxlVz1DkcgSEaQdHOKF+hWr0sTz3P6SrIvzU3A0o5L3n6P7KxYXe67XVftwnHNPV0DyuDF7vdBqwyq9w0zg+SfKn43j7hag1kFW4lfMjtdkO0uRK/Z4WdukcPT+LRYN2DCw1ucrkjrWqB0y1tHTjr+ZC3YGK3NNl8indgivLiEZ4Q/6zBHmR36RoAdkAcxWGYZ66i/F+rv3m+DqNQROC4jBEwuQXbOTQmiNRtbYM8jBpiJGLZzSTQXxSFvdoLkPc0TUIsXw7/L1pckZ054LfTOWCeKPvEGwtzB1Dv0SMd5M+ADdnAwzuJAxBnrsDeoW8+eEEi18vK4BVXpH1bnn4Sw8T6tC3HsNzGrICU2x3PzS5fKFfvdm+mrNEpvfmzfrRnJ1JDLd08Vi0wcoFrKvgBVmbHIKqEtZbCS8xX8vyXBgCrgB6zXLRYO/yWW77/NidMFuWtwWKy79Iwhiiqjgcv5hJ4qazi+dxOquSHNTheMeikOp0HEpr2TAOhXVno1IGO9JYrPqdUWIMCv4I7Q1B/BEA32Mq7tn3AIt79j3aNXw3j1iq1CK+GdEmZrjqdQucgdMgDhKPKdCJPCwoLQthDPDYlbcu4NEDEgHW5nlDFbt5CrL1VQp37PwHyABcK5aBCe9B+JCWXQZ519dXTAf5f2wdvcGvtRqBvV3XEuwm1Fp7jNk5oRHbp9G5DGU0xcvyDM4/rXgZTTuEglq7mQiEkjNNm6HP7vY0yB/AWjR01piePT66iQyAgF0876IMHwW/pElx7wHuf4LADe0kX55HULgV5+C2i85jdnvSgqlj731KY1fBkljgDhmBAH4aJA/OzoY9uM6WGAl3ceYaZJuM2C3YxW3gaEOqJTRWClzG/ES2WLgg/o1X2k0WJHmAn847iqklAu+M3UQdXIO8ZEOuGkqjHQrw7X5wWMAOsYYH6VYnco/6VYna5sC1ffgqiFxd/TZH3+p85mZMa5Do8IWCKdWxrLwY7zSD1x6QNzZ5XLgGKHspccwyS2b/FTzVp/WvKbpnCKkYZEY+GGEIdsXNfQQxDeBnfEX4KUjWl486Sq56asP6/Pwth4eGTxHkg+PIx9IaH/UzcLctra5DquS22r3Xzez8H0YIOvFndIePGMfGVw3d+jPbtbSa3G85WP+IintD/uo1t0aFSvfpwzg3GicfA/c26hfBAkbJ0URwbG5sm3mwv63zbJZc5A2qOGx5oJVDTUw3PEruHB5wriGxuwhpLq40yxaiO7emJY4Q7QzDCpw79L6APCd8bi2TZHQzgnXJyXLs6HkrHcvbv5ZYp15GE2qBV9NmWnORMPhbEhVXWRR6vwxuO/J+9zsGNaNQ4vuuujmGVZu57yGje/M9dt7uH93fO56nT0mcBmvrF5KLvAHlRGv6DFk8yUEDU6gwKm3NUOr/ANHm3khnVIq3ipMJnwdF4MppGefXxTEffa0IEEePIHtBwU+sRrd2wiPhebBzVQZl/KbpWHSVSbSKr8SzMbvndo1zZtplX5y3Okzal4/I4jW5l3s14lbO0hhCz8fLSCHNvTs9uTxzur2rIc09Bjlx+u09ZMRB92zbI0lCPOEecJMF4UOUbBze3zYgv2XydK8fftKPU4F9If3qd/iCF7i6gW5UJUfgxtgbm5V3LNaill6jV05VSwfXi6zx3ruVRpklfgRxDIojUpR4LwqVkkrhZl2S1/2Obu1nX1IyfvhVzCrwvh8KUL1coSeMkDsmul+1Yl6TkKTXIMg7S5iBrUGgPPezGjhNmd6PTOUqNNU1KMosuQZ/oYAtxyo/p9kVJxeiLi5PqsX0Mc0qRnJjtKnZEWAjroLR2gSoqlOg1lPmXhhC2/RNd3dfU1cP6RwKF97YElLExcPz6u3XTVqpdwKb3fjGiRHiTy/vU+zFexZkx3POsZfJV0FWHwytrhGqKxQzdyWyrV1i6I4D7N2UJt9lJvYQ8PdIEi5dkEH2R/fg3nRnF3v03snAeuaXOziGjTt8O4/HIBAnXjP8gb/c0U90lE0PUmh2ixu9mfwYxXC4+5HvtGXu02VSj54kAqZVugJHCoJkOI9hbfDpd8WWs4kTexJHgTyY9s/aarqraKrVlYxfp3xR2HvdvS+NU+bySC//pOXKPIY1eRxsr7pJ2G5dLh9SfgxCUCzTrBC/UlSHcwO2cCAL8Cmy9NJD9FQ7FvrgI/BxPUs3weZY158sl4HiMx7D1IXd2HfZC93FG66hfw+yCB6NWjF7lm5voyQ4Gi3oodSZcKWnUVBGsDCV07MapPYIkvIuCFGUgQw9U+Z7enhFwfjwrhTeHePlC/qPaO0P+Ce/jspBDvDIm1+manWD/qC7MjANnuR5tEnAmlBMBxOb66sqYn9olUflVGsbG2RjdkJPVvTxoFvb4LHIsR8/8KYfHZx1Em404YMLqyFWUC/L4vIOg8RE+tDUSGY5hp17UFczyXMO/72KyyyQ+zUaQJa8HZKA1jiVfU6RdPcD2/rEt8jPwV1ABD1yf0Lp67HHsARshW7zfztoqzr3SSeV3WUw7vdljLUrG+MNeC7gp+2Oucc3fjL+Vxlldi/liHNYUmRpzLm4dLlt2yS6tmWiD87Pske1+IctfIMWWZ8GSOePZcex842SI3smRkAW1sXzLs2Kev2a+EsZr9iKgHwJXnVXYxPMpPur5cwfw6yLed5+rkhYlq4AI20r1s6S87nZFNhDT9b/BSeSdBR1frtbPZQboaNFDiEtQQxCy1AQro6U+Nbc3q3vc5Q8IN8z3kFqPq+zBGfc2nPgaCRnn35jsckdSFGhy0Pw2Eq7otb6Km6PTtyOvFiGD/vqIt7lUWw/5H8TOOkYZH1Dqzyyn0se+NZFpiIiGhitNhaMpU0RB/WPCFGj6jzS+pevCCBWe5fcY90aJZPDimhPUkCGaGpzsVotVf3um3b1H+i7XfLqFoyO34cSOu977iEG+zMa5+8gy4duv/R9Es/ug2QD4nRjsN6Uxe8NFDm1h8vhy9+Jb/IdvGFrHhZdpyaCgW7twvnD6j2e11dRZ0EcljGR3sSD7oIF7VH7j6ruRsjRE32Ry1v7uUDRm45hNmy1AzxQK5QCKM2jgtif//ZmkX/DKulvb26gIDAyBNyWWyNWwS17LGLgkGKquEhH5r2DkbHUZKT4feDhRwYs+/VXk/0S/3SP7c962NrcxpFJjo9BNFAEi3N6Se8o1K56YXVV3y+jiBEoQXGXRPgiQU0tYzVvIROub1I85j48Fa/LGFQfD4SbEEFGt3htQyuVGUFB/RpJIPT+6rSMUDrTVQNIWwFhcbrcocEdsPR+0IdLPZa0TOBpYRklBo2E4+6WvJ/X8PBFsq5PxCReSM4cYjcJHL0ziMbGzmPHv7RmsqS/MqMhM87S4RQLGlS/Nt+4gLnIb4Lni2dAUGoCBgI5g9O5SbP+OcW9U+1EXgzcdznzFwFH43jAUG5gT2RBOA+NYf9G+fUC3fNKn/6qaswQHtcgLDPkqF0ftI8osHSfdINTIQPBeYxnXTE4SsIWmupj4JWzlzAG9PN2o+lBYK5AFqXi1FpqGheyIWFoVhlclkWQFQr5EEZTkc7hRmC5DyySqIiC2DBgOt169vnC8YjDT5/To7i2I8jVcjfr7mT6APY7K4AgvLHZQ7CR+bUyhB4+z1aXFhCJpzRb62h1bg4ZHwyAal9OKM//kcgpSOZn8AhifcEAW65wU/JK8SPq08RF5x49ElRI3mB8Y+Y0lffuZL3OIEAdftaJX+xDTCs5vcMRGsrA9f5XkyD0dyDLQOYDttMQypCpN3pxruj1gJtzF4SlNeVTUexofyqBYNYdvm85yAZFs7tT/MndXRRHwXGY/GspYaD8NQ2tdC4HRxTdc5KGehXD0znWMw6fD7iOUQ59mCxlyxjOrYrn8sU69sFtcN955baZcNsyLhnPemugTi69gmRTQgVIZ+SUORDPbxQe1Y23C0aEJPyZpeXOOcdAwEZqXn8iVxCQA/1OzQnJz3MH9Ucr92AL8PPM2/goOBgTrMN6riJX6MLkso914mR/nLSssloePgd52UKHjtFG23LuwtN3iW7cBk0IBgz+Mc1CAHGE/5/EMbJTWPm4fUpzNhegdYhgePxPHcU0+Bg8LkL0hMoJtKtk4xDayW4Xg5u0DO8dAv2S3yAYWzizTiFyXRQmddT8VGzj03RNKEZ2Oc/SpIDCqQkw/RUUT2n24Hr9XWXRNshesHg8K7MMJKHBSx8+FMscdhjkxXOIn+1eQ7FtjR8fmI/IZXgk6uB4x7Adzv1s6k/hCe/BGr2FCPKHI/JcIsk2yANGtbY0LV2XSaIe5rPF4UsQ3kcJQL9XLRCroBcERLMHflKMfnJwGMXONnZZ4TGA1ccowSlmrKauAeIBnff6V2TLMgwBWDtKT32RZY60E/hzg+4MrgBUlK2yaTSQVK5jh7VkIwl5DKJxcAP71WDlDgeDM7phTZOL5x3iiaH3pianpsq9xkAQYuahpd0/HPmQKPT+FTwXUPiu6uaWgeyjNLMM6wH5c3eZUALF5GRck2UqakWjYpRg5lO0hqvLhhqIChSI9Y7p4WLpnyUowfpiG0TxSVHAbo7FzZgg3CQJEdHYOrQP3KNQphLyQaZZmvrOY9qZbJv+icbgNmPidvcFigO+2LYFjIiuJ9VwyNrW6qNmstyPYZG72JY+ZiknKI2lmnOTOgd5DXbxix5cJd3pzDXE0zB0DXJZ3v4XCBnnbGvph6ycbmycLn38lnA13GSR5XsUCMTNCRDrDmGIUiAaxHykWtttpSBZo7SfQRy/eNCUSEyPQXbWm4RsfzSIDFXfQQxv6kNxnPhXYs7xvUozkSlC7aYzh7OgSayitpfng88cTABXB59lbhX0F4UPrrLbQcmHORC28rAoz4LtLog2R5EqhbdmDB1s+NulGTClPdL3qyjOVbZpBAjhcN2kJ6HGotAIQInNlE3uq2NlZeuj1/7o1F/SNcABV5xbOj8HeVFBz8CwsFDU1ivNp/LoH0DZ4MnQ7HV3F665EiV5QrlmpDBzbPat4fC91OlFKaVhC+uDQ1g/a8JSlt1fwVP+GSBxCNmYCKt2BCKcS7k8QYWbE4iSEf8pjxnkqsPmqvaREMbyN1GaPWeSd2s+4PiIuB5Tk9Q2P9LsIUo2Xl8xfEyzcntsS9XbAp08KAOeTtvwLrsonH8koKsseoQgW5eGI+Ba+6QXyHRvzqN0a7u06qk5GmRbO/uogqL9i0HUfBTC040BAEXvDCyjbNZvhk9fTsri3s6TgAB2DcJox6ScmqGc+AKCvMxAFVPwGMTEoCnBJNSL30Ay12gqWgaQR3n8h6mFXdPLX5fBziHPJvmRKFCvPDYujy22uzRDYaHvouN4pumFwT6mKBa/H9BR7CDPdDfvtnBY/0fdrR5z3DVABvj1eVAEVna1h2hndSQJHoBN+yqn5GViZ9WDS+JjBOI1+uX+pWHDQWdpchdtyozyfrMy1148F1lATqHl47S43CbtAycHEK9BXsbFIrljrhWMwNVe5xA7qycqbXsvj8RekvDY34ipTEU3TKvTlwqK0wRry7TMQmAaOIJGr4IlR48OjGY4jvwNzHY4OVnZVEnlJnSz3FDxG9jnwg+tnAxvqrQOJYczUnxxF5+CXO6t/Ith8MGF1duhCsayyFyJ+wrgaZqKXAeUhDycI8+Gg4tnpAGdg12cHk2o8fqEYZTW907q2D6d1u9AGXasoXQ8Za/qK7nTmUQHUPGmM4CrEEhEGyaKpIlTxW8j/J7NflR5EK3s7nBtVOaNwTcpBmO6LG+ro61rwBp+LibzhsErGeFNUXfhX4JWQvQIkBORpQOolgup5j71ahsaxTZk8vYeLX70V02589WvnJhWew/I0scIjorP1LeLvJaLDf9auJTZb/TVWnJl9ZrOEMFlQsh8rnR4+BMJbVcWqobRKtuXM1OVY7Xt8u4uB1ZvH7Crog2A06AI75fRv62UkSu4oqtgfXNw30QZzfGdsp6yoX3qxFfN/4p2JxCcpe/qWQyCpAtg6nAvPw3Ch0UCZyd8ODqfLHvvlqsqOanR6bVquOcZecZwp4CcfxeERZmB7PWBhm2I4Ag86R4I53Ad3STcfWWAI2WAWmC+zv+Rzn99QqrZ4Bimv1UR3jvRUD7YQPEt3bM0z5cgjl+nd4Tp1fAzTMo65clhjLnMrKQUGigpVx2MVWNJQt+dxs4kASs+aNJD0iS+XLf1uj0n/4jWG1D8K00GIetHBYyK2Dm+V+VtjCOAGs1M29okF5ej4OGy96uqdPRgmL+FYJ/0KzM5AcCdoxQCvYDr5aAEHybKIBQaamUXWT3IIMpoPA27hy1XJBCjwJiVrVf3Fkc5jIATW68XWYVTop4DwqznWBaCrL6vRr8ZE6qZad5Gvlb84kPG6iBAAjHA4RRsooTKWGV0GXSfPl087wKIptUN1df0YxrH6ZMVDPD0I0rWdkBQohDX63dZvAysOv1sqAhPbsYQW2RR0g824Yct1LM8P4uDgTzEBnAdaBl4MU2vaWA0/GgbUFLgG63GvmmibzQwVqziwa3f9MWpP6yoUJ3piUeiqd2lA4m/5rVH19SHk31zq9Jk9jaYzAaE6mTWXRnNJdWX2f2R7VyS6Oth0LX0MZO1XaWxOgcbk7msW6tOZdeZ0WySvRldRtrOZQ9/Ixyqxj5mtA2Fjf8wnNEWiOqcNp0ZzSjdm64u2rW1mlWKAgMc/M1os5+QF8Rj7J9kf/u6hzI0aM4s3dzn7DZXAiNMrFhe78WcjrwBaM/kdRmDJSiq8jHmk+pwX2eVJUIPj357L3pvrVZbz7DkaOJphkc/TOznDF8F+PXQF1Dcp2vraaagjTfXbLeaYrLX/uBnfXkf4UepjqadBjfevHP61fSL7wM4+JlvzvfXaQzspToBbETJ3u/VzMJRNz/4Gb9K4/h7WkAun/TmU2E0bNVkSOhJkj+ZuE2TbX1MAgrwepZu8ePvQx3/mj7ufadhfJgKoEoMRKVsZ3ASzG6su5Y+mOM0TjfHwhyu5hKN2VWaG7zQ6Fr6mEscNReBhyIXHOxstlQaRH7tmlqhgMfX/bOp2mJyDR4j8PQJxLu7Mk4Mr3z2Yiopgo0tTE1zK1R+BHk94r4n9tBnc6p3a+72/mqenG0XKGRn92DZKI5HxZn/CfIbOIqxA1BfU01IIl4/yfM0jPDMNi4aKYo+WkVaugY5Dgq1aoL/95j/Ilm/QWchlOCmrlBjtATx3d+7j1/KuIh2cRRCFP54+/5tf8lcJlVI5zcn+Dk3MsHlYbBmhwOSsRbiwMGcxodbgcbtfzFdwmUMkK9hFMRnaZIXWQCHm13zURJGuyDuj0evoqJ4QJS2IPsl52AHEuT1KaNbpV8ypQPbf9tNbwaGxuP3dwRTKfBa9G/8hAfjtkeMRqLNchldehgsRtG0F/xF2Ws6v1T0sw6t11fOukkm21LzSxeMwnBc1CmkBDW8MB41ACMwH582lY5JTCdjwvbS72QHZwI5LdaXRBHIV0uOtCYmvmlLzXX3cRTmY9wRebh0hV5YriV5BHaTu18K+pRfQ0/OZjdBtgF9CxEh63hTK5rSA2YzrSmfgs3kd+FjsFnlQ4bDlNVvcIRcRVSlJpP6Ts/jT3//+3tmKrkQESRVqI64g+xhBAbpE6vSZf0qalrmQA6GvGEagTWinq5GfD0clogU1a4KrxkwA/4LvdSQHeyoSswctgX7whRccufAFSR2k/FGbTLssQhLg/lc+jqJ9RCnD2FMoUehozrftoevPk3KPNbk9p3m5I9UK6gnnYO7AM7BWZlBwGHfU4U87dcV6JN+81FL6tR906Cab55O5Dzy/DBEQ4nSqYge/cm4YVlAvfkKPflOQrDiUmA2f56kDIUvhUevxA83acywJTPR5CidgCrkpt65Gia6vLvMok2UuGCmAbHSPHrg7Dj7zwjcFx1yFmiGfnJWgAs+egTZyw0KcCk0b5CVKNMGVTBzjhCTOj1bkLhNzROnZbKG2k8BtidFkUW3ZQGq0NerrkTIKgwM3gSTpWNqvkLK5Egylb2wJzs24/GomFYVHLrW82Hdmlf5j+2MpNBYrKm3YNwy3yQsp8FjhJvPTNjMKYMN7pavXCLvdCb80d50CKKgzOvyqoc0j7MO6OqqT5GS0l5Xng1f7c22NiFTjS+stHhqJqKKCnwgCTxB2LLJSpQ5myoY1aItQolb7seuLRw6ryyn3S3ZYFa8tzcibQbMNr5o0+azqcVbE03Lob18wKDVxEgjIbXf9t3EyQ0AJ+hqait3M/US+7z9dcXsmEH/NmJElqCQm9yjetWmSBtyYW1acH2oR9p2+jEYeah4ljMNwSNwlTTipKBLeYjJiThrwGtVQ0AcLF/pzPEEbCWORDo2V51GMcphyo/CasRTQ14gOnJvf7lBzfJMDf703ADhFBDiik/ETDevGmkuKm3ZwWxeDUU6m9dkfNXEhdiP54sNthQO3ce9f6zYkqLS1+RvFFvece4CO7BBTcEGo7quavHB9C6rLSNcZTiAwSs/TMkP/UmYjC3aoDfOtRUxK7R9UpCIr3uvZnS07IWCgdG9SXdRODYX4E5ZNqg/HwYfVMTsDyNUgbCqZFDSyaPyRXWTV38eRdnEHbIoeGId0cB4ZB7Fp3gNXnMQIRy89WfMJ79MIHTU53JciYOrz0D5EKFtM3U+WWh07UVzXkfVXzBakzFREwF1P6wjDbYUDt3HvbeOtKTshXWk5Z2xT8NTsMGop2EtPpj+GNwywkTWkVd+mKV1BAVf3499BWFK+7rhD3u/n2Ay9mIv6QL1r1ikdWfKI5dUODIYNJ/9uEOqzqIDbqkJUZIzsPrkt8UI8zrq4H7dGBOICyNYjhMqcsybY5KqvbDpyfgMfRvkNVxp4hCl8+M5dujmyXeo3WS8d/FcgCwJ4pOyuEcQQ1xYZfvZD2Eno4DCS15x78WflLy9kIM4eYAHRz3ZsQ73SQGpv+y7f15FhkpH9IBPPPt7IXLGZZoxhYg610wuLZr4ud9yeKb8FEFsULg3bpzomQbUJjHn4kNXOJjA2hRZKv02DefFa5y1oi8jjoDF1GXKVPyFMZx248Om7TlzVIsli4FHC/hovNMRsk8MM/8H39OwzYiPu/UYZ+on3X9Gd8VZAI/2VyVKiJaD9Y+ouBfQYD6NA3dpDRYUsO6jP0kyVki4lhYlnuBOxeQsQuk6fIJMp9STrOFhzsVnBKVHiwMccZu23tM0nBevfSOXwky1oTmx2mg6kjGfUTM6rdr0NS3A/PXsr0SC4g6D6ut+81BHyPz17GvwBLn9KoUA8kY47YV9koM4hQ63fO9tlzyq9sKSyeMzp5vggDo+H3YZTQyZ8go1LdPFMVjeR7stCmAw552sQZIOCNV+3G8GaumY/zbWoIotRny8TWfNM+cwBge6wE8MMJ2JdcREymaJtsG0l7QIjd2owXRer+nf9gZ9Mhb4EcRwge+VPkyhTCHSK9l7HZimZy+0X5qfxtB3p2WH0VQUfV6Y+rhdlFlyDf4qgYdgF54O2gTKvTMTVbL3goWmZy8ESxv6b3x15TX2n1BvmfLQnGJEzoKsINIb+UzFNcAmfYx6Z51+4eGkzGJoU+lzBimyGBbai11qDmw25l5lxF2Tb1cMb83fuWcOjDWiq48RX03t8VP3v9yBMLqrH4W0CQr3gMek+PPwEtXcd+6Tk7cPrMhH/XKHfq5U6CJFzzA/qDGCL9EooVUBy6aiHxO3OQvZSlAZsUqHCS6AmQpXdWp9M85RymZrZptcYMsomJrnG9XhJtgMpoCZjRbR4czDhSw9EG2BIElDQ5jORsNjroEsMPI55UzmMbCY6oRPyWWw+uSM9j3IoiApWtl6lm5vowRXXEGeiB5B9nITbcWijapEPbCjCrTMgsO48VhLWt3P21HhAHnlNhmhKliQaM+Z//bmbD57Rh1/37Xl0amP6grs+c8SlsBZ+pZEYh6lKpG8QBccpHgUD9CsWY9Ee278t78yUYUlD1b67ZPI6x2z8yVoTyPDVkmmImfWx7ZEiumSYkdW88mWo9ofJURqsOhs7I59G7oqeT65Y1QuVsBxJO7V5CQvfGzEwx3iU3OzaLPfa7E7Pz1gCoFroxDMRtr2ifgexGXLpHIK/fDFuJyLyVVBs67ok4eN+MkPK1fUavBzH8DUbD35QV7lsLbfR/L9OXefp09JnAbr0dOtNB3TNvT24/6n12hpUelrBnk1auGwDLa7GPCRN52+2YkFrbkZUSTQgz8ZL9xEIIP0hcBjVHrME17UmRZ7Chfi60EEoe/oUelsFqHnO7aav1V3GiYa0XKrxz8zsdW22+QecFAfZR4qQuVp//iJoWgf2KrjJxWvNk0F2BNftf3hP/puR5zS/VelWaK0dOrpnI56/DXg2Cae0PGc2mbEXSO5tFkw16QObRUGWRA+uD+7+2QujDKHxavPByKsKmr24+T/JUjKuyAsygxko5uByM5pgFTBAbAFSc9+MEaTXmF1soNDD9Y3KUnEoLo0p8wrQjbjVziYzCvaXNfO+Wy5bkiJMhEoR8B92pwwFQdS29GMuLA+q+6V2ONZQZiygxF2OtaOOcq5lsPUHkBNaNuanr9GtGyZsFdzmzY9d12XMViC/RJbNc4X+Hqci1CvxsGIMJquPRNkLasNyC/x9Eqn9SiYTp8BJuM8qsV07HcWFGCDIomObbpoOqavvNuP+2+yaGnZD3NFywiqW57WBHqSPk13YukjqOHHiUFnxh1xl77gaSd6Rqy2V1vevJhu9C3PnPNmsuVdBS8oKcAXUNyna2VxR7WiD490yTjuEWSfYkaUVfNzsKTGYgRulFCodNKkeGFuLLlXYnGuLDm6gLRkyZlISYRGjUON0Z7wYYc4g8ph8RlBkQZfTZpXCYWD1dx36WZMeiOyaLRkS12nYtaX1vOWiokYjhE4UEajSvc9lpgda+7V/jtfxhxdMtry5Uy2YPLZgbpZxuQ5ia+TMtGl5LQsrnUQb03E9CmZa0gmmBkr7pV8nCczjm++seLGmUjGK5BtozyHHxAZXwIs7VfEVxCmmfj5JVOROqAyheMcn3kkiRAja/ix40iH0g9n8ulTsuL0sJ1OTN6D8CEt+0EvmM/i3ZupSQkntnQc0cklS46az7AWA+Pphz0FFCrt4f2mc2PQ0S9/Z8ZQ414MmzPS9NfEUMCWGaR0U1s5V4skQnAdZVYXs0y/4176yH7hfuciZehR6pOYidnwR5OoVk6Rq3n2lreUS5QUNb9Zcc0YxC1T6qTK7bedjD8RGzzCmficblbE32gihSzZr0clPO2XjcKQRK8ibHylTZWNmR++I4lS6a6H4ixYbS8SX07HVWOmu9Rlp8kzXbrnH3/Rg/qscxgsszescnJ3F8XwC1g5TN4t08rbDmlA3dd9z93dkaK09VSITbjlxJUFzGPoMW+bT8wxxhJfD+IuqKNHR56gttNdjkMlH0WMirZB9nLxHN4HyQZcwxVxBjV6kIQvEvaqK9Cs1XxUlzIYBfoSu/riiSd4dPnhh4oOlY4kEzAP1sA/XnliAp6gRn46ZgjvwbqMwU2QPzRWH/Kb2MOLrERNKFUwjhMNS4QQJb8WHvHQeeI6DmUq3ZLtJuO9f5agBOuLbRDFJ0URhPfYND32rQYXi170b26N/b/T4BOm0vH0VxoS5kGSNdgM8E9TiWGhtmCs0/cMGZA7grPkwRrTObDhqiIi5DygJ2abqkROMl0wyr5JIC/iN09cJh4q71ym1B2J3xx4ixBxQlLsJvYohJsuG0wm24iWk7HfxzQrt8vyNg+zqEq95vo+RKyUMX1TEDmle2+tZmnSsTVNGKQ8egwK8AXkOdyGVx+zdDsel9Cd92JD0UV7zx89glR6JCdjLgxyk76yx0zYo5uKyZhjsd2lGQoydofihfu0O4m5hMKBgtgrOQiDEU2ThqkoSjaTGosunqdnFQoH+vD2fHisQtO0d6wCO4rTytuVS4P9vHo6MvXR5yBEFvo5n2vPvRNmI+hS6bPGbzJGOw3Ch0WyLNLwwes5yQubCZCnUBLW2Xt9SUTZXhy6RHw3/wRR82O6EYPq2vDc9FmjknJRgG3tqIR+Su5XkpLjFUR+Hin1CokzgwtZ5OkqpSV4BO7qkaTSI24ymWEgjePvaQGVxZqn0PR0QLEpqd1J4fcCaXI36arfbnCTrdty99q2TMOq0O+fkqZMocJdtNVm2VAwho1BOvJafU7BX+jDSZI/SfQyokp/VpvP4zwrt+ExVxujYLhmxFsdipNx2VfwlJ+lW3zMVBRgRJOxZRfZNQmS+n5AEks41FrdjcxG6G+0kQuZo63Qn8Lq4ygSyoCTHMkl/vDMg38a3KY7GcbpRlMcEU3GFkdk19QJkPx+QOJIONRa3Y3MRujvqzQXM0dboT+F1cdx7Ar6nORIHPGHZx780+A2rQsJwgCqdkBVIFGNxhZJdOeM00lXckBiSTLgWh2OzlDtLyGXdDW4UzmSdDJkKkcSSjBKc+GltsWELirYtnoNHiPw9AnEu7syTkCeK1ukBO1Ht0yJ8ODY+zmVDkiiqc2IVt9T8iFVMHS7U9cSzvlkHOX2woY3GDNkJqrxPDjLSKxNKsvUmflwpNYeiao9uHc2ZKf9u2PW56Fxb5YvcAxitKhgC5A1HhXpGnyMsrw4D4rgNsjZYx9qtQRF67q6i8K3b6rPxGTW35Ef2jb44+36NoVzHdzGXRNG4PQAB89dMjwWPFnK7YSsMNAV/KvAoYCZbtoSXhdt4QD4z2kYxNG/wbqZcU5HnDq8LjnVhjoPkk2JHavZPtsibldtqQp5YFlk+MYor6LrCyhkqgmJZGoOYMFGnWYwYKvwemdrqfecEmGlZd2T9QZwIKsOIEIHnWH6p4t53dI1BnrrBXdnuuuV8/pr8iINEiaWA3IhoCwBukzTTA9dEa+HrnSghy9BUt4FYVFm6Oqa6YUu5vVE1xjqrXuBz3bVlXH76YqVOknjNZ8islTcUV1Bpav2OTi/r7ZY2FlbY2hFV/uwbBDZKtx1XOdhb6utVNdyq5OJepZ2OLyiEM/yF1RTwl1PTeGgJGri0XCkUFPEl0BN6dCWXcClDal9hMoYb3565dztm64yJCFAHD2C7OUm2vL6o4u5koKqoTb9p2WyjkF1lyxiBLKOhCXIarqdt4G2IQsX3MU+3EQJNaaVGqaS/YGpIVumXS5vvVU6INu5taTigqipjAtzwhcqAG0NmQ7QVlLsVtKfvKPBHtqoikwHbQkPfls4AL67HmXgd0W8DrrSgR6IOw6mC6KM1wcuzlfq3YiOY2ShpCO1oxmuLOpECr+uoQL/zywtd6JO6kJJT3WNgZ46Vx2mn66I1wsqHRYPhLMiKxWIQq4wIMoV+hH0IIatdZwQn2q4tYYOF6oy7eIZ7gJJEJ+UxT0ycYTYiCE84Mmr85CStxjArk7BwKBRf+f1Vxcpnka+oQfEbZQ04cmEriY7pdA1B7D4M7qD+2a2HsCCX42HBb+mIhaSnuW9qU2jQAgQZcLpVNKpcM2v+GaZ30lVJuykKh46koMnSPBVGiVFLp4wbi3u8ZxXcUgvv492W7760RVxtfG2VLEHwYzRxbKelObtRxDHoBAPZq+c11+vyuAcQrUPSqC/SsBVGHrl/HmjqgyOZ4qFMVwr4jHtV+GPa7+Wmva83IEwuqtFL5GZS6RHi+rLNGp+G2Xdmt/8sn4Mx46XtDp37KQtjLBTxksHI9U5vQm4FlCiUDJbuFytn+9BFgVJlwnqLN3eRkkgmBeVRhK8pO0G8P1nGeAv35KIt6rpYh4OdA2z0VEfEvlqqv/XX0f9hmKE1DDR5sve0sqx9XkQB7KyCjZkfSO8lHHSwceUa+q8caqsU1fXWEd1iyGlOH1K4jTgKYJdEVf5bUuHbhwjkF1lEdeeSJRxbxu7YhVLNl88dkVCC7aKaKwqZkH4IOyjKhT3UpUPsUuViarKEc7jD7qcyxB0FYUbpwvZdVNTKLprUtItu7znAu2SrCDSL8k6Q8dsNicpe8hm63CP2Gw13c5Fi11UUQkNtQUuTADH0YEFNfnasKCyJjoKeKghMGzwJNNFsUZPspRr+CQrqHeFupB3V9UY6LKqNOgywKNMSJESJV3KHJaGroyLfVc8OFyx0PhElPGHKFY0K33LxJ0QZbxOiOIhswpIANyvZEKHrcI1szC1hnZZCANgFeCWe7PaK+futnSVwatCnNaCleXVd/7VIC5SASy2jNLFwm6Uz6C8zAlsp7xa3L55FTVQGOh7uFOFIxM3GjHn7MStxz9EcauqIyLvfrDTYYM3FXmcNXBTxVyDNlVj0K9muwuiDe+40RXx/Wqa0kFdFAfBvAHbXcyXzUwNvlbaq6RwrfMZFAXI6LBFTO+iiqIrH15dlcusATQ4dSRXW1rd98Orco5xdAXZNWSv6uDkB3mZgR8g2tzzuLlXzp94qopah+cRXNY5f6TZKpJuiVoDPffikzLd9sp5ffaqDInilySUbD1kKVcQkxUGL+H6cQ5ZucRU4V+29Wsp9Swe1V65uE/VURXGO2O6FtbkegqIKmv4DMpEKLfakA+hsjBtvGIkGLBVZN6Yyj039mBxx0wNmVlZtdtrgKqtxS54/Qr8Ux9dZ2iQszTPIfBY3CtbhTvITK1BodnFguOIy66QLyi7coV+yDhv3L7ICqL+yDqKF+kSBzG2iuwCfXWy28URWN+kdf0IDLuBNJ4PYkcmpobMgaKupNBxzQIqdzKKVzOrrp6y0b91fh0yfdJ1pL7JKx0EmskbcNPjV1PjBrLN8Lw07cVrnamhhkZdXR2DISd+QT2JM/+qxUrZcbFeeUO4COrJcNF36CQN0kMIySrLsKLaqd9BU2blIdyktWXI0Q21vU+Rd9fgRIrrSieTaKY+oUyEPnYamSoilzm6lsIZtI13wz14tqWi02ZbQcGFVdwVVSpyZFXtqhclg3+S7cqFh8muitoexnnSLtrKOFUlOxqntg5GQ2go9M10SLzXJE3m7KO4VfdGj2jTWtNlDage35Dvc/sPBiEK4seAdEvJQ8AKisrDvnc0+apDQz6KHB4Xfm1vg8J7/NmNiPQ9p/5wUHKS+5Iw542LSjMxmby3h5hC2ZtCCoL0GSUGpfYqUn/AZMr9allzKTtgKs3E5PZfHGIKRa8JuS37hxoKgvDlo+PhuQmyDXJz0RyeupmEn3jEiYiadHiIZ4xQs8vwJsqOBqeWmATOw0qMveTFpKg96lwVhjHx6EDWgBSRTtTxTXjUE0HiF602BNMvSmVky/L49pHvvXHt8Be8XR2Z/P6zVyJFOGcIJLWdEUHvIYKHu9X2wRTab7XVe9jVObgLyrjoXq9ytldBVdmWSj+irbdT/gNZuiX9urdqKHi6q0809W521cJlSeZXdII21Y77Fhi3lj/xNWb/hpbLu8ss2kSJhPeZqu7J71mRSF53SDL9fFlML1VPsktznlNXO7TkmfT4ZAufJq/I99TC0VBqPkgi+wqcJFb8sFsOS/Comw966GG2gwGuh6SdQoVB7TVxwisuJ8DJsGgNyAEORWvD7XR24UiwdX2cMARRB8iBcHi+6JM2zA9MVW/cMO4Q9MIikLc0Yt1T2EaigXLuiSolVBKCh6uHigBxy70M0zC3cKt745iRh6S+K5Vpqf0q7hW03r0vbieKTmFOYk/XFRNKV3SuVo9IemNbXLVX4hJznbjysFWTa9GUH8H4d/4UAK8DITbMiSs7mcc5DMNpFKPUxy1kySD0qvobAiUesiC6CRfcgRZTzdT1uQR6AYkoAKIoQ/rD0ETjkd79sJV8XPn0IwrhlsJoQRakSo1vnFp+jG6jkXuV4ftSNaqZyvtJfHtZLl3XnFo+VjQTyAo3Jb66IRcHoRqmt1fNG8FUSK2OYn6oLEOSV2RUKwG9ZJ0BlKnYWR3K3JhYbGu2oduZrUFKp7WqY43oNLOJVyIJVrZiiXoOUR953TbB0qS7L1vJx+7bD+mGW3Yf3ZEq3Yc4tfxsQKORq7L7iivvJ/HI6U/K0nQFH+xMxvarDCW82H1mpFXhBVcVRD5xZBVLJJkWdGjEtp0g6KGdI1TtLaR0OuLW93lCEjk8qYRMdDsslZeW3tDgNmpEmnmGTTNMstCQUjZSa+iDn1TCZWJoWmEv9YcORyxUMMJw67m3wFBxOnErfhhOU0JlzNCr4WPW/ZHHCxu66nwUxT5//AYSbwBrZ0hekFIKijToqKOhqcddcVyq2tbzOovhaAOhisegX8Ut4UwQ166dI12ww19yp8ZWcn+T5p/UJnTu6qrMwvsgB+sfUXFP9MASPtTEGTlU235wYNxUGPjXfCCoddvBFw8Dv4EjQrgthSteKeqyo6H5Rs678vjQrdzKhamGpw3aPCAQiSoeBCIZcLprx40lrU8iJzy0VBWS1vehGEnCYWMgKvGt3QzL4LoYbuSWO6YZmibytnhN9Gq4Jbofmxw3E8YdNycP7/8dXDGVdEVHiHNbMvurNAi66UGoeTs8eOTrV9ynMx8Vzl0q8AQ1fYg6bph63Fwefd6WfOFC5lVzu5rHI5mKpz+wxXFr+tncOHkCatktC/9v4VWjsLyFdd2vcN9+Nf10BgMe/7Lqg2dAJx7+okQOtbwfSM7gYIBkK0Nc2cfimHwoJCYCYV33loKxh+FKlqBDwRV5oKH7AZL2y4OmmBXDgIMkCUAE2Uu4zGUARsI/w+OiNiAKUOnEBxLYgmQGrrlV3rkuC0uhjTxUM10BbIQxmTe3pLY3McHGUSMhkKVeBkPs0S2pPUiUgBoOGfMYEkkSHtU3qsogJPcsrp6vKiQwIkdUJwWRlyEe3sRVWntbovs2nHRWJ6MxpUCIB4iXgAoPhSyx1L4OsQGbjs+bKqPlbog4yamYPF6S4VJqPjgEUtrVtEcxPlKYkpRcroaUGQ+yT9WBlQEZcVAUoCtA9r/AVVh3uO2IfDutEOBmXlsJu1UeUjmcUQZFBS6V3EoGnJ+wylyNV9zmR9/LVbYyE+eoOuOe3EWeU0uMsI2XeD9zYKWoi7ICms/yMtjuYtABFs9zr6Yj1Eec4Tbf4aDnsKDmsNXX3F+YyeOIm4tTNNqQL1E02UruVUv/pDZxGdoYbgoRJZi63nTq0SK9UZHnZYYpfkU/gk0YWZ+GIcwcaj0MYpMUv+IAKVwKhixREw0CzpEq3+C49TxyApXzlRhAbjZXA7KpUDpSwvk1PZFuEq3HSRhcKumDVqBgbkuJFmDtHi8cJH4F74OlEzaY29IxP8x00OqNTI+5+o188hVv+2bKfA6MDiP1G7nXSiYaljpDhhKb9Ov65A5uIhAKTK+GhyFRYJB+XTFhYoKkhMxkcLpsOLLNm1PLz8atFYDRhlyF5SGs6wh9bksxBwhqeBgSSZgwUV2/y2O6waHTMikwjbyBZHch29F7DF0iO/8OJqpiAY40YBI/BWkDv3w1hwFD8CpgDfFcJ2K2lt+h6TpkADgjvZdWTGF1DbQQk0M3ZF49kEUDLycG0q1xQLrmGNEgiNfYQAu/nDSPQaMyGals/rL6Pg3Vg0nzGHDjDNVw7FBufc+KweSDxc0/RabKquNncDY+tZaS3YvphNyzmELZPjhZmq2zexA+pGX/1pj5zOU71bYS/mH7IfmGLZXxIhcdOUBXd8qCoZCf6YbaeDrhTThMcCmUGUo0WGt0q0USFVEQS57gyhqIiTR7fkv31Xu/1S90PhjNuzy2q+FxEbb1RK4UEv/tsqCO9TCehEX0CLv5nG5WxN8o9yc34LqkuphEBjBBGlMmGSqirgiG4yGRvf/i1hve7PWffo1D9hC53snsk+doNu/uohh+BCvJG0+2kmQ2DB93tn3QLbuvDhg37oVkE3mwCGr6PBi0XfZYOHYWom0JRSLyz4i2QfZy8RzeB8kGXMOhleWwG24kGxTTbHa4V/r4WH1xOwj4hzL1dO09Izu8B2t4NroJ8odmNye/cYkfaiMhhAJN0kMVyIaC7VwIyN1u/88SlGB9sQ2iGGqnQXiPtUKpnj3UxI+aze215xfKreFziJbizKgKrQYGqgXeH6u2YO7Dtaqgh8JQisK6YsJokAQ9dIHa0IgGxBvXUH2oMg3ZyClZM+EYHKN9Wd7mYRZV70MG0yLwa/vQQZnuKBCcUgfOqNEjVDe+QPURrvLVxyzdysZDVt3HgND99bxH6CLXQ3GTagwEUXnfh2Gx3aUZ8qm5Q76mQ1qMpLZP/YXqlgLRK7EejotnneGQ1PY5HFS39Eb17GM4zsEuTiubXgtaNBicus4J4cDouuWAIQuth+Q0CB8WCdSTwgflfWWwjQ8pIuiUAiSs422YJK8fhprIBKaZu+G0Q/QFJCUKHFQbR9BPvhc8W02m3iclx/RBfpa6wZN9MRDIIvsNOI3j72kRJZuaMmSRPUuTvMiCCIoPpG20K6RJuHeTrvrtRJk3nMAeXpgG2flo3mXIIZmYKXQ/7GTKEIWRJKrLidLKTjLpkKCEN2fpFm9aioxCNBHl8bGA6J/rKPwJGNR3pwPbZRWSD1dbT468ctaiCchG6Qs1Z59oIsrKaAHRPz9R+JO7J/nd6cB2OSLlw9XWkyOvnINyArLbRHPfUbBzxfmnGkkz6JlD9c9XPSr65pGuxPEgE7n9hkauq6pABp+EmQxBrTJf49Rzn0C8uyvjBGU8V9WeBO0lT53d9zWCxiYkkz1+cCr5myaqQGvQ6ZaKpIvp3Z/hM2K3waE1gzoy56pPn/UgD4c86FV0f9R3T/rv7yogaODhLIOsLfv9HTLlbYP6A/xZ3ap9SdcgzvHX399dl7D1FlS/zkEebToQv0OYCQhRnx3Qps4iuUMvMXcgwwSQGDVVmuLW0FAE66AITrIiQs9tYXEI1xL2SsXuguiG6xasF8llWezKApIMtrcxdQf8+zt5/7+/Y3D+vQ6P5YIEiGYESQCXyWkZxesW749BnPe2PhGIMzj6fwL4vZpLuDQLsHlpIX1NE0VA9fCdgx1I1nDJ3YDtLobA8stkGTwCE9y+5eAz2AThC/z+GGHvRxGQ4Ymgh/338yjYZME2r2F07eFPyMPr7fP/+f+pG2XMs/sKAA== + + + dbo + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Migrations/202010011005387_TopicCookieType.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202010011005387_TopicCookieType.Designer.cs new file mode 100644 index 0000000000..b4b28db84f --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202010011005387_TopicCookieType.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.4.4")] + public sealed partial class TopicCookieType : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(TopicCookieType)); + + string IMigrationMetadata.Id + { + get { return "202010011005387_TopicCookieType"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202010011005387_TopicCookieType.cs b/src/Libraries/SmartStore.Data/Migrations/202010011005387_TopicCookieType.cs new file mode 100644 index 0000000000..818986dceb --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202010011005387_TopicCookieType.cs @@ -0,0 +1,43 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using SmartStore.Core.Data; + using SmartStore.Data.Setup; + + public partial class TopicCookieType : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + AddColumn("dbo.Topic", "CookieType", c => c.Int()); + } + + public override void Down() + { + DropColumn("dbo.Topic", "CookieType"); + } + + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + if (!DataSettings.DatabaseIsInstalled()) + return; + + // Add resources. + context.MigrateLocaleResources(MigrateLocaleResources); + + context.SaveChanges(); + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("Admin.ContentManagement.Topics.Fields.CookieType", + "Cookie type", + "Art des Cookies", + "Specifies whether this widget is displayed according to the customer's settings in the cookie manager. " + + " This option should be used if you add a third-party script that sets cookies.", + "Bestimmt, ob dieses Widget in Abhängigkeit zur Kundeneinstellung im Cookie-Manager ausgegeben wird. " + + "Diese Option sollte verwendet werden, wenn Sie ein Script für einen Drittanbieter zufügen, das Cookies setzt."); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202010011005387_TopicCookieType.resx b/src/Libraries/SmartStore.Data/Migrations/202010011005387_TopicCookieType.resx new file mode 100644 index 0000000000..e45a3f8553 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202010011005387_TopicCookieType.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/202010030939136_AttributeChoiceBehaviour.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202010030939136_AttributeChoiceBehaviour.Designer.cs new file mode 100644 index 0000000000..1051bb9ea1 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202010030939136_AttributeChoiceBehaviour.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.4.4")] + public sealed partial class AttributeChoiceBehaviour : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(AttributeChoiceBehaviour)); + + string IMigrationMetadata.Id + { + get { return "202010030939136_AttributeChoiceBehaviour"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202010030939136_AttributeChoiceBehaviour.cs b/src/Libraries/SmartStore.Data/Migrations/202010030939136_AttributeChoiceBehaviour.cs new file mode 100644 index 0000000000..01d2fb1e83 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202010030939136_AttributeChoiceBehaviour.cs @@ -0,0 +1,22 @@ +namespace SmartStore.Data.Migrations +{ + using System; + using System.Data.Entity.Migrations; + + public partial class AttributeChoiceBehaviour : DbMigration + { + public override void Up() + { + AddColumn("dbo.Product", "AttributeChoiceBehaviour", c => c.Int(nullable: false)); + AddColumn("dbo.Product_ProductAttribute_Mapping", "CustomData", c => c.String()); + CreateIndex("dbo.ProductVariantAttributeCombination", new[] { "StockQuantity", "AllowOutOfStockOrders" }); + } + + public override void Down() + { + DropIndex("dbo.ProductVariantAttributeCombination", new[] { "StockQuantity", "AllowOutOfStockOrders" }); + DropColumn("dbo.Product_ProductAttribute_Mapping", "CustomData"); + DropColumn("dbo.Product", "AttributeChoiceBehaviour"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202010030939136_AttributeChoiceBehaviour.resx b/src/Libraries/SmartStore.Data/Migrations/202010030939136_AttributeChoiceBehaviour.resx new file mode 100644 index 0000000000..103fee40a2 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202010030939136_AttributeChoiceBehaviour.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/202010121359446_RemoveBlogAndNewsLanguage.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202010121359446_RemoveBlogAndNewsLanguage.Designer.cs new file mode 100644 index 0000000000..e662cced0e --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202010121359446_RemoveBlogAndNewsLanguage.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.4.4")] + public sealed partial class RemoveBlogAndNewsLanguage : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(RemoveBlogAndNewsLanguage)); + + string IMigrationMetadata.Id + { + get { return "202010121359446_RemoveBlogAndNewsLanguage"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202010121359446_RemoveBlogAndNewsLanguage.cs b/src/Libraries/SmartStore.Data/Migrations/202010121359446_RemoveBlogAndNewsLanguage.cs new file mode 100644 index 0000000000..53fcbb0cdc --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202010121359446_RemoveBlogAndNewsLanguage.cs @@ -0,0 +1,103 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using System.Linq; + using SmartStore.Core.Data; + using SmartStore.Core.Domain.Blogs; + using SmartStore.Core.Domain.Localization; + using SmartStore.Core.Domain.News; + using SmartStore.Core.Domain.Seo; + using SmartStore.Data.Setup; + + public partial class RemoveBlogAndNewsLanguage : DbMigration, IDataSeeder + { + public override void Up() + { + DropForeignKey("dbo.BlogPost", "LanguageId", "dbo.Language"); + DropForeignKey("dbo.News", "LanguageId", "dbo.Language"); + + if (DataSettings.Current.IsSqlServer) + { + Sql("IF (OBJECT_ID('BlogPost_Language', 'F') IS NOT NULL) ALTER TABLE [dbo].[BlogPost] DROP CONSTRAINT [BlogPost_Language];"); + Sql("IF (OBJECT_ID('NewsItem_Language', 'F') IS NOT NULL) ALTER TABLE [dbo].[News] DROP CONSTRAINT [NewsItem_Language];"); + + // See Indexes.sql. + Sql("IF EXISTS (SELECT * FROM sys.indexes WHERE name='IX_BlogPost_LanguageId' AND object_id = OBJECT_ID('[dbo].[BlogPost]')) DROP INDEX [IX_BlogPost_LanguageId] ON [dbo].[BlogPost];"); + Sql("IF EXISTS (SELECT * FROM sys.indexes WHERE name='IX_News_LanguageId' AND object_id = OBJECT_ID('[dbo].[News]')) DROP INDEX [IX_News_LanguageId] ON [dbo].[News];"); + } + else + { + DropIndex("dbo.BlogPost", "IX_BlogPost_LanguageId"); + DropIndex("dbo.News", "IX_News_LanguageId"); + } + + DropIndex("dbo.BlogPost", new[] { "LanguageId" }); + DropIndex("dbo.News", new[] { "LanguageId" }); + DropColumn("dbo.BlogPost", "LanguageId"); + DropColumn("dbo.News", "LanguageId"); + } + + public override void Down() + { + var defaultLanguageId = 1; + + if (DataSettings.DatabaseIsInstalled()) + { + defaultLanguageId = new SmartObjectContext().Set().Select(x => x.Id).FirstOrDefault(); + } + + AddColumn("dbo.News", "LanguageId", c => c.Int(nullable: false, defaultValue: defaultLanguageId)); + AddColumn("dbo.BlogPost", "LanguageId", c => c.Int(nullable: false, defaultValue: defaultLanguageId)); + CreateIndex("dbo.News", "LanguageId"); + CreateIndex("dbo.BlogPost", "LanguageId"); + AddForeignKey("dbo.News", "LanguageId", "dbo.Language", "Id", cascadeDelete: true); + AddForeignKey("dbo.BlogPost", "LanguageId", "dbo.Language", "Id", cascadeDelete: true); + } + + public bool RollbackOnFailure => true; + + public void Seed(SmartObjectContext context) + { + if (!DataSettings.DatabaseIsInstalled()) + { + return; + } + + // URL records of BlogPost and NewsItem must now have 0 for "Standard" as language ID otherwise no link is created. + using (var scope = new DbContextScope(ctx: context, validateOnSave: false, hooksEnabled: false, autoCommit: false)) + { + var urlRecords = context.Set(); + var entityNames = new[] { nameof(BlogPost), nameof(NewsItem) }; + + foreach (var entityName in entityNames) + { + var allEntityIds = urlRecords + .Where(x => x.EntityName == entityName) + .Select(x => x.EntityId) + .Distinct() + .ToList(); + + foreach (var chunk in allEntityIds.Slice(200)) + { + var entities = urlRecords + .Where(x => x.EntityName == entityName && chunk.Contains(x.EntityId)) + .ToList(); + var entitiesMap = entities.ToMultimap(x => x.EntityId, x => x); + + foreach (var kvp in entitiesMap) + { + if (!kvp.Value.Any(x => x.LanguageId == 0)) + { + // Migrate active and inactive slugs. + kvp.Value.Where(x => x.LanguageId != 0).Each(x => x.LanguageId = 0); + } + } + + scope.Commit(); + context.DetachEntities(); + } + } + } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202010121359446_RemoveBlogAndNewsLanguage.resx b/src/Libraries/SmartStore.Data/Migrations/202010121359446_RemoveBlogAndNewsLanguage.resx new file mode 100644 index 0000000000..66f0c45a54 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202010121359446_RemoveBlogAndNewsLanguage.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/202011091154314_V410Resources.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202011091154314_V410Resources.Designer.cs new file mode 100644 index 0000000000..316f66a305 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202011091154314_V410Resources.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.4.4")] + public sealed partial class V410Resources : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(V410Resources)); + + string IMigrationMetadata.Id + { + get { return "202011091154314_V410Resources"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202011091154314_V410Resources.cs b/src/Libraries/SmartStore.Data/Migrations/202011091154314_V410Resources.cs new file mode 100644 index 0000000000..34543f8e68 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202011091154314_V410Resources.cs @@ -0,0 +1,857 @@ +namespace SmartStore.Data.Migrations +{ + using System; + using System.Data.Entity.Migrations; + using System.Linq; + using SmartStore.Core.Domain.Configuration; + using SmartStore.Core.Domain.Media; + using SmartStore.Core.Domain.Seo; + using SmartStore.Data.Setup; + using SmartStore.Utilities; + + public partial class V410Resources : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + } + + public override void Down() + { + } + + public bool RollbackOnFailure => false; + + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + MigrateSettings(context); + context.SaveChanges(); + } + + public void MigrateSettings(SmartObjectContext context) + { + var settings = context.Set(); + + // Add .ico extension to MediaSettings.ImageTypes + var name = TypeHelper.NameOf(y => y.ImageTypes, true); + var setting = settings.FirstOrDefault(x => x.Name == name); + if (setting != null) + { + var arr = setting.Value.EmptyNull() + .Replace(Environment.NewLine, " ") + .ToLower() + .Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries); + + if (!arr.Contains("ico")) + { + setting.Value += " ico"; + } + } + + // Migrate old 'defaultSeoSettings' into new SeoSettings + // Old settings. + var defaultTitle = settings.FirstOrDefault(x => x.Name == "SeoSettings.DefaultTitle"); + var defaultMetaKeywords = settings.FirstOrDefault(x => x.Name == "SeoSettings.DefaultMetaKeywords"); + var defaultMetaDescription = settings.FirstOrDefault(x => x.Name == "SeoSettings.DefaultMetaDescription"); + + // New settings. + context.MigrateSettings(x => + { + x.Add(TypeHelper.NameOf(y => y.MetaTitle, true), defaultTitle != null && defaultTitle.Value.HasValue() ? defaultTitle.Value : ""); + x.Add(TypeHelper.NameOf(y => y.MetaDescription, true), defaultMetaDescription != null && defaultMetaDescription.Value.HasValue() ? defaultMetaDescription.Value : ""); + x.Add(TypeHelper.NameOf(y => y.MetaKeywords, true), defaultMetaKeywords != null && defaultMetaKeywords.Value.HasValue() ? defaultMetaKeywords.Value : ""); + }); + + if (defaultTitle != null) settings.Remove(defaultTitle); + if (defaultMetaDescription != null) settings.Remove(defaultMetaDescription); + if (defaultMetaKeywords != null) settings.Remove(defaultMetaKeywords); + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.Delete( + "Admin.SalesReport.Bestsellers.RunReport", + "Admin.SalesReport.NeverSold.RunReport", + "Admin.Customers.Reports.RunReport", + "Admin.Customers.Reports.BestBy.BestByNumberOfOrders", + "Admin.Customers.Reports.BestBy.BestByNumberOfOrders"); + + builder.AddOrUpdate("Admin.Customers.Reports.BestCustomers", "Top customers", "Top Kunden"); + + builder.AddOrUpdate("Admin.Configuration.Settings.Search.SearchProductByIdentificationNumber", + "Open product directly at SKU, MPN or GTIN", + "Produkt bei SKU, MPN oder GTIN direkt öffnen", + "Specifies whether the product page should be opened directly if the search term matches a SKU, MPN or GTIN.", + "Legt fest, ob bei einer Übereinstimmung des Suchbegriffes mit einer SKU, MPN oder GTIN die Produktseite direkt geöffnet werden soll."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.PasswordMinLength", + "Minimum password length", + "Mindestlänge eines Passworts", + "Specifies the minimum length of a password.", + "Legt die minimale Länge eines Passworts fest."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.MinDigitsInPassword", + "Minimum digits in password", + "Mindestanzahl von Ziffern im Passwort", + "Specifies the minimum number of digits for a password.", + "Legt die Mindestanzahl von Ziffern eines Passworts fest."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.MinSpecialCharsInPassword", + "Minimum special characters in password", + "Mindestanzahl von Sonderzeichen im Passwort", + "Specifies the minimum number of special characters for a password.", + "Legt die Mindestanzahl von Sonderzeichen eines Passworts fest."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.MinUppercaseCharsInPassword", + "Minimum uppercase letters in password", + "Mindestanzahl von Großbuchstaben im Passwort", + "Specifies the minimum number of uppercase letters for a password.", + "Legt die Mindestanzahl von Großbuchstaben eines Passworts fest."); + + builder.AddOrUpdate("Account.Fields.Password.MustContainChars", + "The password must contain at least {0}.", + "Das Passwort muss mindestens {0} enthalten."); + builder.AddOrUpdate("Account.Fields.Password.Digits", "{0} digits", "{0} Ziffern"); + builder.AddOrUpdate("Account.Fields.Password.SpecialChars", "{0} special characters", "{0} Sonderzeichen"); + builder.AddOrUpdate("Account.Fields.Password.UppercaseChars", "{0} uppercase letters", "{0} Großbuchstaben"); + + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.PaymentMethod", "Payment method", "Zahlart"); + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Group.BrowserUserAgent", "Browser User Agent", "Browser User-Agent"); + + builder.AddOrUpdate("Admin.Orders.NoOrdersSelected", + "No orders are selected. Please select the desired orders.", + "Es sind keine Aufträge ausgewählt. Bitte wählen Sie die gewünschten Aufträge aus."); + + builder.AddOrUpdate("Admin.Orders.ProcessSelectedOrders", + "There are {0} orders selected. Would you like to proceed?", + "Es sind {0} Aufträge ausgewählt. Möchten Sie fortfahren?"); + + builder.AddOrUpdate("Admin.Orders.ProcessingResult", + "
{0} of {1} orders were successfully processed.
{2} orders cannot be changed as desired due to their current status and were skipped.", + "
Es wurden {0} von {1} Aufträgen erfolgreich verarbeitet.
{2} Aufträge können aufgrund ihres aktuellen Status nicht wie gewünscht geändert werden und wurden übersprungen."); + + builder.AddOrUpdate("Admin.Configuration.Countries.Fields.DefaultCurrency", + "Default currency", + "Standardwährung", + "Specifies the default currency. Preselects the default currency in the shop according to the country to which the current IP address belongs.", + "Legt die Standardwährung fest. Bewirkt im Shop eine Vorauswahl der Standardwährung anhand des Landes, zu dem die aktuelle IP-Adresse gehört."); + + builder.AddOrUpdate("Admin.Catalog.Attributes.CheckoutAttributes.Values.Fields.MediaFile", + "Picture", + "Bild", + "Specifies an image to be displayed as the selection element for the attribute.", + "Legt ein Bild fest, welches als Auswahlelement für das Attribut angezeigt werden soll."); + + builder.AddOrUpdate("Admin.Catalog.Attributes.CheckoutAttributes.Values.Fields.Color", + "RGB color", + "RGB-Farbe", + "Specifies a color for the color squares control.", + "Legt eine Farbe für das Farbflächen-Steuerelement fest."); + + builder.AddOrUpdate("Common.Entity.CheckoutAttributeValue", "Checkout attribute option", "Checkout-Attribut-Option"); + + builder.AddOrUpdate("Checkout.MaxOrderSubtotalAmount", + "The maximum order value for the subtotal is {0}.", + "Der Höchstbestellwert für die Zwischensumme ist {0}."); + + builder.AddOrUpdate("Checkout.MaxOrderTotalAmount", + "The maximum order value for the total is {0}.", + "Der Höchstbestellwert der Gesamtsumme ist {0}."); + + builder.AddOrUpdate("Admin.Customers.CustomerRoles.Fields.MinOrderTotal", + "Minimum order total", + "Mindestbestellwert", + "Defines the minimum order total for customers in this group.", + "Legt den Mindestbestellwert für Kunden in dieser Gruppe fest."); + + builder.AddOrUpdate("Admin.Customers.CustomerRoles.Fields.MaxOrderTotal", + "Maximum order total", + "Höchstbestellwert", + "Defines the maximum order total for customers in this group.", + "Legt den Höchstbestellwert für Kunden in dieser Gruppe fest."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Order.MinOrderTotal", + "Minimum order total", + "Mindestbestellwert", + "Defines the default minimum order total for the shop. Overridden by customer group restrictions.", + "Legt den standardmäßigen Mindestbestellwert für den Shop fest. Wird von Beschränkungen in Kundengruppen überschrieben."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Order.MaxOrderTotal", + "Maximum order total", + "Höchstbestellwert", + "Defines the default maximum order total for the shop. Overridden by customer group restrictions.", + "Legt den standardmäßigen Höchstbestellwert für den Shop fest. Wird von Beschränkungen in Kundengruppen überschrieben."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Order.ApplyToSubtotal", + "Order total restriction in relation to subtotal", + "Beschränkung bezogen auf Zwischensumme", + "Determines whether the min/max order value refers to the order subtotal, otherwise it refers to the total amount.", + "Bestimmt, ob sich der Mindest-/Höchstbetrag auf die Auftragszwischensumme bezieht, andernfalls bezieht er sich auf den Gesamtbetrag."); + + builder.Delete("Admin.Configuration.Settings.Order.MaxOrderSubtotalAmount", + "Admin.Configuration.Settings.Order.MaxOrderSubtotalAmount.Hint", + "Admin.Configuration.Settings.Order.MaxOrderTotalAmount", + "Admin.Configuration.Settings.Order.MaxOrderTotalAmount.Hint", + "Admin.Configuration.Settings.Order.MinOrderSubtotalAmount", + "Admin.Configuration.Settings.Order.MinOrderSubtotalAmount.Hint", + "Admin.Configuration.Settings.Order.MinOrderTotalAmount", + "Admin.Configuration.Settings.Order.MinOrderTotalAmount.Hint"); + + builder.AddOrUpdate("Admin.Configuration.Settings.Order.OrderTotalRestrictionType", + "Restrictions in customer groups are cumulative", + "Beschränkungen in Kundengruppen sind kumulativ", + "Determines whether multiple order total restrictions are cumulated by customer group assignments.", + "Bestimmt, ob mehrfache Bestellwertbeschränkungen durch Kundengruppenzuordnungen kumuliert werden."); + + builder.Delete("Products.AskQuestion.Question.Text"); + + builder.AddOrUpdate("Products.AskQuestion.Sent", + "Thank you. Your inquiry has been sent successfully.", + "Vielen Dank. Ihre Anfrage wurde erfolgreich gesendet."); + + builder.AddOrUpdate("Products.AskQuestion.Question.GeneralInquiry", + "I have following questions concerning the product {0}:", + "Ich habe folgende Fragen zum Artikel {0}:"); + + builder.AddOrUpdate("Products.AskQuestion.Question.QuoteRequest", + "I would like to request price information on the following article: {0}", + "Ich bitte um Preisinformationen zu folgendem Artikel: {0}"); + + builder.AddOrUpdate("Products.AskQuestion.CallHotline", "or call directly", "oder rufen Sie direkt an"); + + builder.AddOrUpdate("Products.AskQuestion.TitleQuoteRequest", "Request for quotation", "Angebotsanfrage"); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CustomerFormFields.Description", + "Manage form fields that are displayed during registration.
" + + "In order to ensure the address transfer from the registration form, " + + "it is necessary that the following fields are activated and filled in by the customer: " + + "
  • First name
  • Last name
  • E-mail
  • and all fields that are selected as required in the tab 'Addresses'
", + "Verwalten Sie Formularfelder, die während der Registrierung angezeigt werden.
" + + "Um die Adressübergabe aus dem Registrierungsformular zu gewährleisten, ist es notwending, " + + "dass folgende Felder aktiviert und vom Kunden ausgefüllt sind:" + + "
  • Vorname
  • Nachname
  • E-Mail
  • und alle Felder die im Tab \"Adressen\" als erforderlich ausgewählt sind
"); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.Cookies.AddCookieInfo.Title", + "Add new cookie information", + "Neue Cookie-Informationen hinzufügen"); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.Cookies.EditCookieInfo.Title", + "Edit new cookie information", + "Cookie-Informationen bearbeiten"); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.CookieInfo.Name", + "Name", + "Name", + "Specifies the display name of the cookie information.", + "Bestimmt den Anzeigenamen der Cookie-Information."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.CookieInfo.Description", + "Description", + "Beschreibung", + "Specifies the description of the cookie information.", + "Bestimmt die Beschreibung der Cookie-Information."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.CookieInfo.CookieType", + "Cookie type", + "Art des Cookies", + "Specifies the usage type of the cookie (Required, Analytics, ThirdParty).", + "Bestimmt die Verwendungsart des Cookies (Required, Analytics, ThirdParty)."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.SameSiteMode", + "'SameSite' mode", + "'SameSite' Modus"); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.SameSiteMode.AdminInstruction", + @"
SameSite Mode
+

+ The SameSite setting for cookies specifies the level of security for cookies when they are requested by other sites (e.g. in a cross-site request or in an iframe). + The recommended setting is Lax, as cookies are sent with most secure actions. +
+ If the store should also be embedded in an iframe, it is necessary to select the setting None. Additionally SSL must be set up in the store. +

+
    +
  • Unspecified: No SameSite header is transmitted to requests.
  • +
  • None: No mode specified. This setting must be used if the store should also be embedded in an iframe (SSL must be set up).
  • +
  • Lax: Recommended setting. Cookies are sent at the highest level for requests on the same website and for cross-site requests.
  • +
  • Strict: Cookies are only sent to requests from the same website.
  • +
", + @"
SameSite Modus
+

+ Die SameSite-Einstellung für Cookies bestimmt, das Sicherheitslevel für Cookies, wenn diese von anderen Seiten (z.B. in einem Cross-Site-Request oder in einem IFrame) aufgerufen werden. + Die empfohlene Einstellung ist Lax, da hier Cookies bei den meisten gesicherten Aktionen mitgesendet werden. +
+ Soll der Shop auch in ein IFrame eingebettet werden, ist es notwendig die Einstellung None zu wählen. Zusätzlich muss SSL im Shop eingerichtet sein. +

+
    +
  • Unspecified: Es wird kein SameSite-Header an Requests übermittelt.
  • +
  • None: Kein Modus angegeben. Diese Einstellung muss verwendet werden, wenn der Shop auch in ein IFrame eingebettet werden soll (SSL muss eingerichtet sein).
  • +
  • Lax: Empfohlene Einstellung. Cookies werden bei Anfragen auf derselben Website und bei Cross-Site-Requests auf höchster Ebene gesendet.
  • +
  • Strict: Cookies werden nur an Anfragen derselben Website gesendet.
  • +
"); + + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.ModalCookieConsent", + "Modal Cookie Manager", + "Cookie-Manager modal anzeigen", + "Specifies whether the Cookie Manager is displayed in a modal dialog in the frontend. The modal mode requires a customer interaction with the dialog before the customer can navigate in the store.", + "Bestimmt, ob der Cookie-Manager im Frontend in einem modalen Dialog angezeigt wird. Der modale Modus macht eine Kundeninteraktion mit dem Dialog erfoderlich, bevor der Kunde im Shop navigieren kann."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.EnableCookieConsent", + "Enable Cookie Manager", + "Cookie-Manager aktivieren", + "Specifies whether the Cookie Manager will be displayed in the frontend.", + "Legt fest, ob ein Dialog für die Zustimmung zur Nutzung von Cookies im Frontend angezeigt wird."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.CookieInfo.IsPluginInfo", + "System cookie", + "System-Cookie"); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.Cookies.CookieInfoNotFound", + "Cookie information wasn't found.", + "Cookie-Information wurde nicht gefunden."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.Cookies.RegisterCookieInfo.Title", + "Register your own cookie information", + "Eigene Cookie-Informationen registrieren"); + + builder.AddOrUpdate("CookieManager.Dialog.Hide", + "Hide", + "Ausblenden"); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Privacy.Cookies.AdminInstruction", + @"

+ If you have included third-party scripts in your shop that use cookies and are not integrated into Smartstore via a plugin, + you can provide your own information about these cookies for the Cookie Manager in the following table. + The added information is displayed in the Cookie Manager of the front end, corresponding to the type. + Cookie information provided to the Cookie Manager by active plug-ins is displayed in this table, + but cannot be edited. The texts of this information can be edited using the language resources + Admin > Configuration > Regional Settings > Languages. +

", + @"

+ Wenn Sie Skripte von Drittanbietern in Ihren Shop eingebunden haben, die Cookies nutzen und die nicht über ein Plugin in Smartstore integriert sind, + können Sie in folgender Tabelle eigene Informationen über diese Cookies für den Cookie-Manager bereitstellen. + Die zugefügten Informationen werden, dem Typ entsprechend, im Cookie-Manager des Frontends angezeigt. + Cookie-Informationen, die dem Cookie-Manager von aktiven Plugins zur Verfügung gestellt werden, werden in dieser Tabelle angezeigt, + können aber nicht bearbeitet werden. Die Texte dieser Informationen können über die Sprachresourcen bearbeitet werden + Admin > Konfiguration > Regionale Einstellungen > Sprachen. +

"); + + builder.AddOrUpdate("Admin.Catalog.Products.StockQuantityNotChanged", + "The stock quantity has not been updated because the value has changed since the page was loaded (e.g. due to a placed order). Current value is {0}.", + "Der Lagerbestand wurde nicht aktualisiert, weil sich der Wert seit Laden der Seite geändert hat (z.B. durch einen Auftragseingang). Aktueller Wert ist {0}."); + + builder.AddOrUpdate("Admin.Configuration.SeoTab.Title", + "SEO settings", + "SEO-Einstellungen"); + + builder.AddOrUpdate("Admin.Configuration.Seo.MetaTitle", + "Title tag", + "Title-Tag", + "Defines the title of the page, which is displayed in search engine results. If possible, enter a unique, concise title here.", + "Bestimmt den Titel der Seite, der unter anderem in den Ergebnissen von Suchmaschinen angezeigt wird. Geben Sie hier nach Möglichkeit einen einzigartigen, prägnanten Titel an."); + + builder.AddOrUpdate("Admin.Configuration.Seo.MetaDescription", + "Meta description", + "Meta Description", + "Defines the meta description of the site. An extract of this description is displayed in search engine results below the title.", + "Bestimmt die Meta Desciption der Seite. Ein Auszug dieser Beschreibung wird in den Ergebnissen von Suchmaschinen unterhalb des Titels dargestellt."); + + builder.AddOrUpdate("Admin.Configuration.Seo.MetaKeywords", + "Meta keywords", + "Meta Keywords", + "Defines the meta keywords of the site. Enter the keywords as a comma separated list.", + "Bestimmt die Meta Keywords der Seite. Geben Sie die Keywords als kommagetrennte Liste an."); + + builder.AddOrUpdate("Admin.System.MetaInfos", + "Meta tags", + "Meta-Elemente"); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.HomepageTitle", + "Title tag (Homepage)", + "Title-Tag (Startseite)", + "Defines the title tag for the homepage, which is displayed in search engine results.", + "Legt das Title-Tag für die Startseite fest, das unter anderem in den Ergebnissen von Suchmaschinen angezeigt wird."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.HomepageMetaDescription", + "Meta description (Homepage)", + "Meta Description (Startseite)", + "Defines the meta description for the homepage. An extract of this description is displayed in search engine results below the title.", + "Legt die Meta Description für die Startseite fest. Ein Auszug dieser Beschreibung wird in den Ergebnissen von Suchmaschinen unterhalb des Titels dargestellt."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.HomepageMetaKeywords", + "Meta keywords (Homepage)", + "Meta Keywords (Startseite)", + "Defines the meta keywords for the homepage. Enter the keywords as a comma separated list.", + "Legt die Meta Keywords für die Startseite fest. Geben Sie die Keywords als kommagetrennte Liste an."); + + builder.AddOrUpdate("Admin.Configuration.DeliveryTimes.Fields.MinDays", + "Delivery not before (in days)", + "Lieferung frühestens (in Tagen)", + "Specifies the minimum number of days for the delivery.", + "Legt den frühesten Zeitpunkt der Lieferung in Tagen fest."); + + builder.AddOrUpdate("Admin.Configuration.DeliveryTimes.Fields.MaxDays", + "Delivery not later than (in days)", + "Lieferung spätestens (in Tagen)", + "Specifies the maximum number of days for the delivery.", + "Legt den spätesten Zeitpunkt der Lieferung in Tagen fest."); + + builder.AddOrUpdate("Products.DeliveryDate", "Arrives:", "Lieferung:"); + + builder.AddOrUpdate("DeliveryTimes.Dates.OnTomorrow", "Tomorrow", "Morgen"); // exactly tomorrow + builder.AddOrUpdate("DeliveryTimes.Dates.Tomorrow", "tomorrow", "Morgen"); // tomorrow + + builder.AddOrUpdate("DeliveryTimes.Dates.DeliveryOn", "{0}", "{0}"); // allow "on {0}" etc. + builder.AddOrUpdate("DeliveryTimes.Dates.NotBefore", "not before {0}", "frühestens {0}"); + builder.AddOrUpdate("DeliveryTimes.Dates.Until", "until {0}", "bis {0}"); + builder.AddOrUpdate("DeliveryTimes.Dates.Between", "{0} - {1}", "{0} - {1}"); // allow "{0} till {1}" etc. + + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Directory.DeliveryTimesPresentation.None", "Do not display", "Nicht anzeigen"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Directory.DeliveryTimesPresentation.DateOnly", "Date only (if possible)", "Nur Datum (sofern möglich)"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Directory.DeliveryTimesPresentation.LabelOnly", "Label only", "Nur Label"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Directory.DeliveryTimesPresentation.LabelAndDate", "Label and date", "Label und Datum"); + + builder.Delete( + "Admin.Configuration.Settings.Catalog.ShowDeliveryTimesInProductLists", + "Admin.Configuration.Settings.Catalog.ShowDeliveryTimesInProductLists.Hint", + "Admin.Configuration.Settings.Catalog.ShowDeliveryTimesInProductDetail", + "Admin.Configuration.Settings.Catalog.ShowDeliveryTimesInProductDetail.Hint", + "Admin.Configuration.Settings.ShoppingCart.ShowDeliveryTimes", + "Admin.Configuration.Settings.ShoppingCart.ShowDeliveryTimes.Hint"); + + builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.DeliveryTimesInProductDetail", + "Presentation of delivery times", + "Darstellung von Lieferzeiten", + "Specifies the way delivery times are displayed on product detail pages.", + "Legt die Darstellungsart von Lieferzeiten auf Produktdetailseiten fest."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.DeliveryTimesInLists", + "Presentation of delivery times", + "Darstellung von Lieferzeiten", + "Specifies the way delivery times are displayed in product lists. Due to lack of space, the grid view does not show a date for the delivery time.", + "Legt die Darstellungsart von Lieferzeiten in Produktlisten fest. Aus Platzgründen wird in der Rasteransicht kein Datum zur Lieferzeit angezeigt."); + + builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.DeliveryTimesInShoppingCart", + "Presentation of delivery times", + "Darstellung von Lieferzeiten", + "Specifies the way delivery times are displayed in shopping cart.", + "Legt die Darstellungsart von Lieferzeiten im Warenkorb fest."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Shipping.TodayShipmentHour", + "Order before x o'clock for same day shipment", + "Bis x Uhr bestellt, heute verschickt", + "Specifies the hour by which the order will be shipped the same day.", + "Legt die Stunde fest, bis zu der die Bestellung noch am selben Tag verschickt wird."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Shipping.DeliveryOnWorkweekDaysOnly", + "Delivery on workweek days only", + "Lieferungen nur an Werktagen", + "Specifies whether delivery takes place only on workweek days.", + "Legt fest, ob Lieferungen nur an Werktagen stattfinden."); + + builder.AddOrUpdate("Admin.Configuration.Seo.SeName", + "URL alias", + "URL-Alias", + "Set a search engine friendly page name e.g. 'my-landing-page' to make the page URL 'http://www.yourStore.com/my-landing-page'. Leave empty to generate it automatically based on the name of the entity.", + "Legen Sie einen suchmaschinenfreundlichen Seitennamen fest, z.B. 'meine-landing-page', um die URL zu 'http://www.yourStore.com/meine-landing-page' zu machen. Lassen Sie das Feld leer, um den Pfad automatisch auf Basis des Namens der Entität zu generieren."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.PageTitleSeparator", + "Page title separator", + "Titel-Trennzeichen", + "Specify page title tag separator.", + "Legt das Trennzeichen für das Titel-Tag fest."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.PageTitleSeoAdjustment", + "Title tag order", + "Titel-Tag Reihenfolge", + "Select the page title order. The generated title tag can be (PAGE NAME | YOURSTORE.COM) or (YOURSTORE.COM | PAGE NAME).", + "Legen Sie hier die Seitentitel-Reihenfolge fest. Das erzeugte Title-Tag könnte z.B. (MEINSHOP.DE | SEITE NAME) oder (SEITE NAME | MEINSHOP.DE) lauten."); + + builder.Delete("Admin.Catalog.Categories.Fields.SeName", + "Admin.Catalog.Categories.Fields.SeName.Hint", + "Admin.Catalog.Manufacturers.Fields.SeName", + "Admin.Catalog.Manufacturers.Fields.SeName.Hint", + "Admin.Catalog.Products.Fields.SeName", + "Admin.Catalog.Products.Fields.SeName.Hint", + "Admin.ContentManagement.Blog.BlogPosts.Fields.SeName", + "Admin.ContentManagement.Blog.BlogPosts.Fields.SeName.Hint", + "Admin.ContentManagement.Forums.Forum.Fields.SeName", + "Admin.ContentManagement.Forums.Forum.Fields.SeName.Hint", + "Admin.ContentManagement.Forums.ForumGroup.Fields.SeName", + "Admin.ContentManagement.Forums.ForumGroup.Fields.SeName.Hint", + "Admin.ContentManagement.News.NewsItems.Fields.SeName", + "Admin.ContentManagement.News.NewsItems.Fields.SeName.Hint", + "Admin.ContentManagement.Topics.Fields.SeName", + "Admin.ContentManagement.Topics.Fields.SeName.Hint"); + + builder.Delete("Admin.Catalog.Categories.Fields.MetaTitle", + "Admin.Catalog.Categories.Fields.MetaTitle.Hint", + "Admin.Catalog.Manufacturers.Fields.MetaTitle", + "Admin.Catalog.Manufacturers.Fields.MetaTitle.Hint", + "Admin.Catalog.Products.Fields.MetaTitle", + "Admin.Catalog.Products.Fields.MetaTitle.Hint", + "Admin.ContentManagement.Blog.BlogPosts.Fields.MetaTitle", + "Admin.ContentManagement.Blog.BlogPosts.Fields.MetaTitle.Hint", + "Admin.ContentManagement.News.NewsItems.Fields.MetaTitle", + "Admin.ContentManagement.News.NewsItems.Fields.MetaTitle.Hint", + "Admin.ContentManagement.Topics.Fields.MetaTitle", + "Admin.ContentManagement.Topics.Fields.MetaTitle.Hint"); + + builder.Delete("Admin.Catalog.Categories.Fields.MetaDescription", + "Admin.Catalog.Categories.Fields.MetaDescription.Hint", + "Admin.Catalog.Manufacturers.Fields.MetaDescription", + "Admin.Catalog.Manufacturers.Fields.MetaDescription.Hint", + "Admin.Catalog.Products.Fields.MetaDescription", + "Admin.Catalog.Products.Fields.MetaDescription.Hint", + "Admin.ContentManagement.Blog.BlogPosts.Fields.MetaDescription", + "Admin.ContentManagement.Blog.BlogPosts.Fields.MetaDescription.Hint", + "Admin.ContentManagement.News.NewsItems.Fields.MetaDescription", + "Admin.ContentManagement.News.NewsItems.Fields.MetaDescription.Hint", + "Admin.ContentManagement.Topics.Fields.MetaDescription", + "Admin.ContentManagement.Topics.Fields.MetaDescription.Hint"); + + builder.Delete("Admin.Catalog.Categories.Fields.MetaKeywords", + "Admin.Catalog.Categories.Fields.MetaKeywords.Hint", + "Admin.Catalog.Manufacturers.Fields.MetaKeywords", + "Admin.Catalog.Manufacturers.Fields.MetaKeywords.Hint", + "Admin.Catalog.Products.Fields.MetaKeywords", + "Admin.Catalog.Products.Fields.MetaKeywords.Hint", + "Admin.ContentManagement.Blog.BlogPosts.Fields.MetaKeywords", + "Admin.ContentManagement.Blog.BlogPosts.Fields.MetaKeywords.Hint", + "Admin.ContentManagement.News.NewsItems.Fields.MetaKeywords", + "Admin.ContentManagement.News.NewsItems.Fields.MetaKeywords.Hint", + "Admin.ContentManagement.Topics.Fields.MetaKeywords", + "Admin.ContentManagement.Topics.Fields.MetaKeywords.Hint", + "Admin.ContentManagement.Blog.BlogPosts.Fields.Language", + "Admin.ContentManagement.News.NewsItems.Fields.Language"); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.DefaultTitle", + "Default title tag", + "Standard-Titel-Tag", + "Defines the default title for pages in your store. You can override this for individual categories, products, manufacturer and topic pages.", + "Legt das Standard-Titel-Tag für Seiten im Shop fest. Es kann für Warengruppen, Produkte, Hersteller und Seiten individuell angegeben werden."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.DefaultMetaDescription", + "Default meta description", + "Standard Meta Beschreibung", + "Defines the default meta description for pages in your store. You can override this for individual categories, products, manufacturer and topic pages.", + "Legt die Meta Beschreibung für Seiten im Shop fest. Diese kann für Warengruppen, Produkte, Hersteller und Seiten individuell angegeben werden."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.DefaultMetaKeywords", + "Default meta keywords", + "Standard Meta Keywords", + "Defines the default meta keywords for pages in your store. You can override these for individual categories, products, manufacturer and topic pages.", + "Legt die Meta Keywords für Seiten im Shop fest. Für Warengruppen, Produkte, Hersteller und Seiten können diese individuell angegeben werden."); + + builder.AddOrUpdate("Admin.Configuration.Settings.Catalog.ShowDiscountSign", + "Show discount sign", + "Rabattzeichen anzeigen", + "Specifies whether a discount sign should be displayed on product pictures when discounts were applied.", + "Legt fest, ob ein Rabattzeichen auf dem Produktbild angezeigt werden soll, wenn Rabatte angewendet wurden."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.StateProvinceRequired", + "'State/province' required", + "'Bundesland' erferderlich", + "Check the box if 'State/province' is required.", + "Legt fest, ob Angaben im Eingabefeld 'Adresszusatz' erforderlich sind."); + + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.AttributeChoiceBehaviour.GrayOutUnavailable", + "Gray out unavailable attributes", + "Nicht verfügbare Attribute ausgrauen"); + builder.AddOrUpdate("Enums.SmartStore.Core.Domain.Catalog.AttributeChoiceBehaviour.None", + "None", + "Keines"); + + builder.AddOrUpdate("Admin.Catalog.Products.Fields.AttributeChoiceBehaviour", + "Attribute choice behaviour", + "Attribut-Auswahlverhalten", + "Specifies the behaviour when selecting attributes.", + "Legt das Verhalten bei der Auswahl von Attributen fest."); + + builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Fields.CustomData", + "Custom data", + "Benutzerdefinierte Daten", + "Specifies user-defined data. For free usage, e.g. for individual shop extensions.", + "Legt benutzerdefinierte Daten fest. Zur freien Verwendung, z.B. bei individuellen Shop-Erweiterungen."); + + #region Media + + var mediaRoot = "Admin.Configuration.Settings.Media."; + + builder.AddOrUpdate(mediaRoot + "ImageSizes", "Image/thumbnail sizes", "Bild-/Thumbnailgrößen"); + + builder.AddOrUpdate(mediaRoot + "DefaultPictureZoomEnabled", + "Enable image zoom", + "Bildzoom aktivieren", + "Adds 'zoom on hover' functionality to product detail images.", + "Aktiviert 'Zoom bei Hover'-Funktion für Produktdetail-Bilder."); + + builder.AddOrUpdate(mediaRoot + "AssociatedProductPictureSize", "Associated (grouped) product", "Verknüpftes (Gruppen)-Produkt"); + builder.AddOrUpdate(mediaRoot + "AvatarPictureSize", "Customer avatar", "Kundenavatar"); + builder.AddOrUpdate(mediaRoot + "BundledProductPictureSize", "Bundle items", "Bundle Stückliste"); + builder.AddOrUpdate(mediaRoot + "CartThumbBundleItemPictureSize", "Bundle items in cart", "Bundle Stückliste im Warenkorb"); + builder.AddOrUpdate(mediaRoot + "CartThumbPictureSize", "Cart", "Warenkorb"); + builder.AddOrUpdate(mediaRoot + "CategoryThumbPictureSize", "Categories", "Warengruppen"); + builder.AddOrUpdate(mediaRoot + "ManufacturerThumbPictureSize", "Brands", "Marken"); + builder.AddOrUpdate(mediaRoot + "MessageProductThumbPictureSize", "Products in e-mails", "Produkte in E-Mails"); + builder.AddOrUpdate(mediaRoot + "MiniCartThumbPictureSize", "Mini cart", "Mini-Warenkorb"); + builder.AddOrUpdate(mediaRoot + "ProductDetailsPictureSize", "Product detail", "Produktdetail"); + builder.AddOrUpdate(mediaRoot + "ProductThumbPictureSize", "Product list", "Produktliste"); + builder.AddOrUpdate(mediaRoot + "ProductThumbPictureSizeOnProductDetailsPage", "Gallery thumbnail", "Gallerie-Thumbnail"); + + builder.Delete( + mediaRoot + "AssociatedProductPictureSize.Hint", + mediaRoot + "AvatarPictureSize.Hint", + mediaRoot + "BundledProductPictureSize.Hint", + mediaRoot + "CartThumbBundleItemPictureSize.Hint", + mediaRoot + "CartThumbPictureSize.Hint", + mediaRoot + "CategoryThumbPictureSize.Hint", + mediaRoot + "ManufacturerThumbPictureSize.Hint", + mediaRoot + "MessageProductThumbPictureSize.Hint", + mediaRoot + "MiniCartThumbPictureSize.Hint", + mediaRoot + "ProductDetailsPictureSize.Hint", + mediaRoot + "ProductThumbPictureSize.Hint", + mediaRoot + "ProductThumbPictureSizeOnProductDetailsPage.Hint"); + + #endregion + + #region Customer settings + + builder.Delete("Admin.Configuration.Settings.CustomerUser.AddressFormFields.TitleEnabled", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.TitleEnabled.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.CompanyEnabled", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.CompanyEnabled.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.CompanyRequired", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.CompanyRequired.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.StreetAddressEnabled", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.StreetAddressEnabled.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.StreetAddressRequired", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.StreetAddressRequired.Hint", + "Admin.Configuration.Settings.CustomerUser.StreetAddress2Enabled", + "Admin.Configuration.Settings.CustomerUser.StreetAddress2Enabled.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.StreetAddress2Enabled", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.StreetAddress2Enabled.Hint", + "Admin.Configuration.Settings.CustomerUser.StreetAddress2Required", + "Admin.Configuration.Settings.CustomerUser.StreetAddress2Required.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.StreetAddress2Required", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.StreetAddress2Required.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.ZipPostalCodeEnabled", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.ZipPostalCodeEnabled.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.ZipPostalCodeRequired", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.ZipPostalCodeRequired.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.CityEnabled", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.CityEnabled.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.CityRequired", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.CityRequired.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.CountryEnabled", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.CountryEnabled.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.CountryRequired", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.CountryRequired.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.StateProvinceEnabled", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.StateProvinceEnabled.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.StateProvinceRequired", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.StateProvinceRequired.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.PhoneEnabled", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.PhoneEnabled.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.PhoneRequired", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.PhoneRequired.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.FaxEnabled", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.FaxEnabled.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.FaxRequired", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.FaxRequired.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.SalutationEnabled", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.SalutationEnabled.Hint", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.Salutations", + "Admin.Configuration.Settings.CustomerUser.AddressFormFields.Salutations.Hint"); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.GenderEnabled", + "'Gender' enabled", + "\"Geschlecht\" aktiv", + "Defines whether the input of 'Gender' is enabled.", + "Legt fest, ob das Eingabefeld \"Geschlecht\" aktiviert ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.TitleEnabled", + "'Title' enabled", + "\"Titel\" aktiv", + "Defines whether the input of 'Title' is enabled.", + "Legt fest, ob das Eingabefeld \"Titel\" aktiviert ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CompanyEnabled", + "'Company' enabled", + "\"Firma\" aktiv", + "Defines whether the input of 'Company' is enabled.", + "Legt fest, ob das Eingabefeld \"Firma\" aktiviert ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CompanyRequired", + "'Company' required", + "\"Firma\" ist erforderlich", + "Defines whether the input of 'Company' is required.", + "Legt fest, ob die Eingabe von \"Firma\" erforderlich ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.StreetAddressEnabled", + "'Street address' enabled", + "\"Straße\" aktiv", + "Defines whether the input of 'Street adress' is enabled.", + "Legt fest, ob das Eingabefeld \"Straße\" aktiviert ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.StreetAddressRequired", + "'Street address' required", + "\"Straße\" ist erforderlich", + "Defines whether the input of 'Street adress' is required.", + "Legt fest, ob die Eingabe von \"Straße\" erforderlich ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.StreetAddress2Enabled", + "'Street address addition' enabled", + "\"Adressszusatz\" aktiv", + "Defines whether the input of 'street address addition' is enabled.", + "Legt fest, ob das Eingabefeld \"Adressszusatz\" aktiviert ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.StreetAddress2Required", + "'Street address addition' required", + "\"Adressszusatz\" ist erforderlich", + "Defines whether the input of 'street address addition' is required.", + "Legt fest, ob die Eingabe von \"Adressszusatz\" erforderlich ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.ZipPostalCodeEnabled", + "'Zip / postal code' enabled", + "\"Postleitzahl\" aktiv", + "Defines whether the input of 'Zip / postal code' is enabled.", + "Legt fest, ob das Eingabefeld \"Postleitzahl\" aktiviert ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.ZipPostalCodeRequired", + "'Zip / postal code' required", + "\"Postleitzahl\" ist erforderlich", + "Defines whether the input of 'Zip / postal code' is required.", + "Legt fest, ob die Eingabe von \"Postleitzahl\" erforderlich ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CityEnabled", + "'City' enabled", + "\"Stadt\" aktiv", + "Defines whether the input of 'City' is enabled.", + "Legt fest, ob das Eingabefeld \"Stadt\" aktiviert ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CityRequired", + "'City' required", + "\"Stadt\" ist erforderlich", + "Defines whether the input of 'City' is required.", + "Legt fest, ob die Eingabe von \"Stadt\" erforderlich ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CountryEnabled", + "'Country' enabled", + "\"Land\" aktiv", + "Defines whether the input of 'Country' is enabled.", + "Legt fest, ob das Eingabefeld \"Land\" aktiviert ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.CountryRequired", + "'Country' required", + "\"Land\" ist erforderlich", + "Defines whether the input of 'Country' is required.", + "Legt fest, ob die Eingabe von \"Land\" erforderlich ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.StateProvinceEnabled", + "'State / province' enabled", + "\"Bundesland\" aktiv", + "Defines whether the input of 'State / province' is enabled.", + "Legt fest, ob das Eingabefeld \"Bundesland\" aktiviert ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.StateProvinceRequired", + "'State / province' required", + "\"Bundesland\" ist erforderlich", + "Defines whether the input of 'State / province' is required.", + "Legt fest, ob die Eingabe von \"Bundesland\" erforderlich ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.PhoneEnabled", + "'Phone number' enabled", + "\"Telefon\" aktiv", + "Defines whether the input of 'Phone number' is enabled.", + "Legt fest, ob das Eingabefeld \"Telefon\" aktiviert ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.PhoneRequired", + "'Phone number' required", + "\"Telefon\" ist erforderlich", + "Defines whether the input of 'Phone number' is required.", + "Legt fest, ob die Eingabe von \"Telefon\" erforderlich ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.FaxEnabled", + "'Fax number' enabled", + "\"Fax\" aktiv", + "Defines whether the input of 'Fax number' is enabled.", + "Legt fest, ob das Eingabefeld \"Fax\" aktiviert ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.FaxRequired", + "'Fax number' required", + "\"Fax\" ist erforderlich", + "Defines whether the input of 'Fax number' is required.", + "Legt fest, ob die Eingabe von \"Fax\" erforderlich ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.NewsletterEnabled", + "'Newsletter' enabled", + "\"Newsletter\" aktiv", + "Displays the newsletter registration form.", + "Zeigt das Newsletter Anmeldeformular an."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.SalutationEnabled", + "'Salutation' enabled", + "\"Anrede\" aktiv", + "Defines whether the input of 'Salutation' is enabled.", + "Legt fest, ob das Eingabefeld \"Anrede\" aktiviert ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.Salutations", + "Salutations", + "Anreden", + "Comma separated list of salutations (e.g. Mr., Mrs). Define the entries which will populate the dropdown list for salutation when entering addresses.", + "Komma getrennte Liste (z.B. Herr, Frau). Bestimmen Sie die Einträge für die Auswahl der Anrede, bei der Adresserfassung."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.DateOfBirthEnabled", + "'Date of birth' enabled", + "\"Geburtsdatum\" aktiv", + "Defines whether the input of 'Date of birth' is enabled.", + "Legt fest, ob das Eingabefeld \"Geburtsdatum\" aktiviert ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.LastNameRequired", + "'Last name' required", + "\"Nachname\" ist erforderlich", + "Defines whether the input of 'Last name' is required.", + "Legt fest, ob die Eingabe von \"Nachname\" erforderlich ist."); + + builder.AddOrUpdate("Admin.Configuration.Settings.CustomerUser.FirstNameRequired", + "'First name' required", + "\"Vorname\" ist erforderlich", + "Defines whether the input of 'First name' is required.", + "Legt fest, ob die Eingabe von \"Vorname\" erforderlich ist."); + + #endregion Customer settings + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.SocialSettings.FacebookAppId", + "Facebook App ID", + "Facebook App ID", + "A Facebook App ID is a unique number that identifies your application and connects it to your Facebook account. It is used when sharing products, category and manufacturer pages on Facebook.", + "Eine Facebook App ID ist eine eindeutige Nummer, die Ihre Anwendung identifiziert und mit Ihrem Facebook-Account verknüpft. Wird beim Teilen von Produkten, Warengruppen- und Herstellerseiten auf Facebook genutzt."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.SocialSettings.TwitterSite", + "Twitter Username", + "Twitter-Benutzername", + "Twitter Username that gets displayed on Twitter cards when a product, category and manufacturer page is shared on Twitter. Starts with a '@'.", + "Twitter-Benutzername, der auf Twitter-Karten angezeigt wird, wenn ein Produkt, eine Kategorie oder eine Herstellerseite auf Twitter geteilt wird. Beginnt mit einem '@'."); + + builder.AddOrUpdate("Admin.ContentManagement.Topics.Validation.NoEmptyPassword", + "Password must have a value.", + "Passwort muss einen Wert haben."); + + builder.AddOrUpdate("Admin.ContentManagement.Topics.Validation.NoPasswordAllowed", + "HTML widgets cannot have a password protection.", + "HTML Widgets können keinen Passwortschutz haben."); + + builder.AddOrUpdate("Order.CompletePayment.Hint", + "Click 'Complete payment' if you have not yet paid and wish to start the payment process again.", + "Klicken Sie 'Zahlung veranlassen', falls Sie noch nicht gezahlt haben und den Zahlungsvorgang erneut starten möchten."); + + builder.AddOrUpdate("Admin.ContentManagement.Forums.ForumGroup.Updated") + .Value("de", "Die Forengruppe wurde erfolgreich aktualisiert."); + + builder.AddOrUpdate("Account.Login.NotRegisteredYet", + "Not registered yet?", + "Noch nicht registriert?"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202011091154314_V410Resources.resx b/src/Libraries/SmartStore.Data/Migrations/202011091154314_V410Resources.resx new file mode 100644 index 0000000000..aa24204390 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202011091154314_V410Resources.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/202012051645539_AddBlogAndNewsLanguage.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202012051645539_AddBlogAndNewsLanguage.Designer.cs new file mode 100644 index 0000000000..05f0cd1fa7 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202012051645539_AddBlogAndNewsLanguage.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.4.4")] + public sealed partial class AddBlogAndNewsLanguage : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(AddBlogAndNewsLanguage)); + + string IMigrationMetadata.Id + { + get { return "202012051645539_AddBlogAndNewsLanguage"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202012051645539_AddBlogAndNewsLanguage.cs b/src/Libraries/SmartStore.Data/Migrations/202012051645539_AddBlogAndNewsLanguage.cs new file mode 100644 index 0000000000..0312293126 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202012051645539_AddBlogAndNewsLanguage.cs @@ -0,0 +1,28 @@ +namespace SmartStore.Data.Migrations +{ + using System; + using System.Data.Entity.Migrations; + + public partial class AddBlogAndNewsLanguage : DbMigration + { + public override void Up() + { + AddColumn("dbo.BlogPost", "LanguageId", c => c.Int()); + AddColumn("dbo.News", "LanguageId", c => c.Int()); + CreateIndex("dbo.BlogPost", "LanguageId"); + CreateIndex("dbo.News", "LanguageId"); + AddForeignKey("dbo.BlogPost", "LanguageId", "dbo.Language", "Id"); + AddForeignKey("dbo.News", "LanguageId", "dbo.Language", "Id"); + } + + public override void Down() + { + DropForeignKey("dbo.News", "LanguageId", "dbo.Language"); + DropForeignKey("dbo.BlogPost", "LanguageId", "dbo.Language"); + DropIndex("dbo.News", new[] { "LanguageId" }); + DropIndex("dbo.BlogPost", new[] { "LanguageId" }); + DropColumn("dbo.News", "LanguageId"); + DropColumn("dbo.BlogPost", "LanguageId"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202012051645539_AddBlogAndNewsLanguage.resx b/src/Libraries/SmartStore.Data/Migrations/202012051645539_AddBlogAndNewsLanguage.resx new file mode 100644 index 0000000000..9e6a6c4fa3 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202012051645539_AddBlogAndNewsLanguage.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/202101251149352_V411Resources.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202101251149352_V411Resources.Designer.cs new file mode 100644 index 0000000000..7c33223814 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202101251149352_V411Resources.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.4.4")] + public sealed partial class V411Resources : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(V411Resources)); + + string IMigrationMetadata.Id + { + get { return "202101251149352_V411Resources"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202101251149352_V411Resources.cs b/src/Libraries/SmartStore.Data/Migrations/202101251149352_V411Resources.cs new file mode 100644 index 0000000000..fd19393cf6 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202101251149352_V411Resources.cs @@ -0,0 +1,92 @@ +namespace SmartStore.Data.Migrations +{ + using System.Data.Entity.Migrations; + using SmartStore.Data.Setup; + + public partial class V411Resources : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + } + + public override void Down() + { + } + + public bool RollbackOnFailure => false; + + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + context.SaveChanges(); + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("Admin.Rules.FilterDescriptor.CartItemQuantity", + "Product quantity is in range", + "Produktmenge liegt in folgendem Bereich"); + + builder.AddOrUpdate("Newsletter.SubscriptionFailed", + "The subscription or unsubscription has failed.", + "Die Abonnierung bzw. Abbestellung ist fehlgeschlagen."); + + builder.AddOrUpdate("Common.UnsupportedBrowser", + "You are using an unsupported browser! Please consider switching to a modern browser such as Google Chrome, Firefox or Opera to fully enjoy your shopping experience.", + "Sie verwenden einen nicht unterstützten Browser! Bitte ziehen Sie in Betracht, zu einem modernen Browser wie Google Chrome, Firefox oder Opera zu wechseln, um Ihr Einkaufserlebnis in vollen Zügen genießen zu können."); + + builder.Delete("Admin.Configuration.Settings.Order.ApplyToSubtotal"); + builder.Delete("Checkout.MaxOrderTotalAmount"); + builder.Delete("Checkout.MinOrderTotalAmount"); + + builder.AddOrUpdate("Checkout.MaxOrderSubtotalAmount", + "Your maximum order total allowed is {0}.", + "Ihr zulässiger Höchstbestellwert beträgt {0}."); + + builder.AddOrUpdate("Checkout.MinOrderSubtotalAmount", + "Your minimum order total allowed is {0}.", + "Ihr zulässiger Mindestbestellwert beträgt {0}."); + + builder.Delete("Admin.Configuration.Settings.Order.OrderTotalRestrictionType"); + + builder.AddOrUpdate("Admin.Configuration.Settings.Order.MultipleOrderTotalRestrictionsExpandRange", + "Customer groups extend the value range", + "Kundengruppen erweitern den Wertebereich", + "Specifies whether multiple order total restrictions through customer group assignments extend the allowed order value range.", + "Legt fest, ob mehrfache Bestellwertbeschränkungen durch Kundengruppenzuordnungen den erlaubten Bestellwertbereich erweitern."); + + builder.AddOrUpdate("ActivityLog.EditOrder", + "Edited order {0}", + "Auftrag {0} bearbeitet"); + + builder.AddOrUpdate("Admin.ContentManagement.Blog.BlogPosts.Fields.Language", + "Regional relevance", + "Regionale Relevanz", + "Specifies the language for which the post is displayed. If limited to one language, blog contents need only be edited in that language (no multilingualism).", + "Legt fest, für welche Sprache der Beitrag angezeigt wird. Bei einer Begrenzung auf eine Sprache brauchen Blog-Inhalte nur in dieser Sprache eingegeben zu werden (keine Mehrsprachigkeit)."); + + builder.AddOrUpdate("Admin.ContentManagement.News.NewsItems.Fields.Language", + "Regional relevance", + "Regionale Relevanz", + "Specifies the language for which the news is displayed. If limited to one language, news contents need only be edited in that language (no multilingualism).", + "Legt fest, für welche Sprache die News angezeigt wird. Bei einer Begrenzung auf eine Sprache brauchen News-Inhalte nur in dieser Sprache eingegeben zu werden (keine Mehrsprachigkeit)."); + + builder.AddOrUpdate("Common.International", "International", "International"); + + builder.AddOrUpdate("Admin.Plugins.KnownGroup.B2B", "B2B", "B2B"); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.ReCaptchaTypeChangeWarning", + "When changing the reCAPTCHA type, the public and private key must also be changed.", + "Beim Ändern des reCAPTCHA Typs müssen auch die beiden Zugangsschlüssel (public\\private key) geändert werden."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.ExtraRobotsAllows", + "Extra Allows for robots.txt", + "Extra Allows für robots.txt", + "Enter additional paths that should be included as Allow entries in your robots.txt. Each entry has to be entered in a new line.", + "Geben Sie hier zusätzliche Pfade an, die als Allow-Einträge zur robots.txt hinzugefügt werden sollen. Jeder Eintrag muss in einer neuen Zeile erfolgen."); + + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.DisplayAllows", "Show items for 'Allow'", "Einträge für 'Allow' anzeigen"); + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.DisplayDisallows", "Show items for 'Disallow'", "Einträge für 'Disallow' anzeigen"); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202101251149352_V411Resources.resx b/src/Libraries/SmartStore.Data/Migrations/202101251149352_V411Resources.resx new file mode 100644 index 0000000000..45481ac4d2 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202101251149352_V411Resources.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/202112171231491_V420Resources.Designer.cs b/src/Libraries/SmartStore.Data/Migrations/202112171231491_V420Resources.Designer.cs new file mode 100644 index 0000000000..db9463e650 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202112171231491_V420Resources.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.4.4")] + public sealed partial class V420Resources : IMigrationMetadata + { + private readonly ResourceManager Resources = new ResourceManager(typeof(V420Resources)); + + string IMigrationMetadata.Id + { + get { return "202112171231491_V420Resources"; } + } + + string IMigrationMetadata.Source + { + get { return null; } + } + + string IMigrationMetadata.Target + { + get { return Resources.GetString("Target"); } + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202112171231491_V420Resources.cs b/src/Libraries/SmartStore.Data/Migrations/202112171231491_V420Resources.cs new file mode 100644 index 0000000000..9dae67f2b9 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202112171231491_V420Resources.cs @@ -0,0 +1,46 @@ +namespace SmartStore.Data.Migrations +{ + using System; + using System.Data.Entity.Migrations; + using SmartStore.Core.Data; + using SmartStore.Data.Setup; + + public partial class V420Resources : DbMigration, ILocaleResourcesProvider, IDataSeeder + { + public override void Up() + { + } + + public override void Down() + { + } + + + public bool RollbackOnFailure => false; + + public void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + context.SaveChanges(); + + if (DataSettings.DatabaseIsInstalled()) + { + var logTypeMigrator = new ActivityLogTypeMigrator(context); + logTypeMigrator.AddActivityLogType("EditOrder", "Edit an order", "Auftrag bearbeitet"); + } + } + + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.SocialSettings.TwitterSite.Error", + "The Twitter username must begin with an '@'.", + "Der Twitter-Benutzername muss mit einem '@' beginnen."); + + builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.AddProductsToBasketInSinglePositions", + "Add products to cart in single positions", + "Produkte in einzelnen Positionen in den Warenkorb legen", + "Enable this option if you want products with different quantities to be added to the shopping cart in single position.", + "Aktivieren Sie diese Option, wenn Produkte mit verschiedenen Mengenangaben als Einzelpositionen in den Warenkorb gelegt werden sollen."); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Migrations/202112171231491_V420Resources.resx b/src/Libraries/SmartStore.Data/Migrations/202112171231491_V420Resources.resx new file mode 100644 index 0000000000..a16bcc4ba7 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Migrations/202112171231491_V420Resources.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/MigrationsConfiguration.cs b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs index a78fb4b2b7..9afb1372cc 100644 --- a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs +++ b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs @@ -1,20 +1,19 @@ namespace SmartStore.Data.Migrations { - using System; - using System.Data.Entity.Migrations; - using Setup; + using System; + using System.Data.Entity.Migrations; + using System.Linq; + using Setup; using SmartStore.Core.Data; - using SmartStore.Core.Domain.Catalog; - using SmartStore.Core.Domain.Common; - using SmartStore.Utilities; + using SmartStore.Utilities; - public sealed class MigrationsConfiguration : DbMigrationsConfiguration - { - public MigrationsConfiguration() - { - AutomaticMigrationsEnabled = false; - AutomaticMigrationDataLossAllowed = true; - ContextKey = "SmartStore.Core"; + public sealed class MigrationsConfiguration : DbMigrationsConfiguration + { + public MigrationsConfiguration() + { + AutomaticMigrationsEnabled = false; + AutomaticMigrationDataLossAllowed = true; + ContextKey = "SmartStore.Core"; if (DataSettings.Current.IsSqlServer) { @@ -26,118 +25,35 @@ public MigrationsConfiguration() CommandTimeout = 9999999; } - } - - public void SeedDatabase(SmartObjectContext context) - { - using (var scope = new DbContextScope(context, hooksEnabled: false)) - { - Seed(context); - scope.Commit(); - } - } - - protected override void Seed(SmartObjectContext context) - { - context.MigrateLocaleResources(MigrateLocaleResources); - MigrateSettings(context); } - public void MigrateSettings(SmartObjectContext context) - { - - } - - public void MigrateLocaleResources(LocaleResourcesBuilder builder) - { - builder.AddOrUpdate("Checkout.OrderCompletes", - "Your order will be completed.", - "Ihre Bestellung wird abgeschlossen."); - - builder.AddOrUpdate("Admin.Catalog.Attributes.CheckoutAttributes.Fields.TextPrompt", - "Text prompt", - "Text Eingabeaufforderung", - "Specifies the prompt text.", - "Legt den Text zur Eingabeaufforderung fest."); - - builder.AddOrUpdate("Admin.Catalog.Products.ProductVariantAttributes.Attributes.Fields.TextPrompt", - "Text prompt", - "Text Eingabeaufforderung"); - - builder.AddOrUpdate("Admin.Catalog.Categories.Fields.ExternalLink", - "External link", - "Externer Link", - "Alternative external link for this category in the main menu and in category listings. For example, to a landing page that contains a back link to the category.", - "Abweichender, externer Verweis für diese Warengruppe im Hauptmenü und in Warengruppen-Listings. Z.B. auf eine Landingpage, die einen Rückverweis auf die Warengruppe enthält."); + public void SeedDatabase(SmartObjectContext context) + { + using (var scope = new DbContextScope(context, hooksEnabled: false)) + { + Seed(context); + scope.Commit(); + } + } - // Rule - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.TaxExempt", "Tax exempt", "Steuerbefreit"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.BillingCountry", "Billing country", "Rechnungsland"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.ShippingCountry", "Shipping country", "Lieferland"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.LastActivityDays", "Last activity days", "Tage seit letztem Besuch"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.CompletedOrderCount", "Completed order count", "Anzahl abgeschlossener Bestellungen"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.CancelledOrderCount", "Cancelled order count", "Anzahl stornierter Bestellungen"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.NewOrderCount", "New order count", "Anzahl neuer Bestellungen"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.HasPurchasedProduct", "Has purchased product", "Hat eines der folgenden Produkt gekauft"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.HasPurchasedAllProducts", "Has purchased all products", "Hat alle folgenden Produkte gekauft"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.RuleSet", "Other rule set", "Anderer Regelsatz"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Active", "Is active", "Ist aktiv"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.LastLoginDays", "Last login days", "Tage seit letztem Login"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.CreatedDays", "Created days", "Tage seit Registrierung"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Salutation", "Salutation", "Anrede"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Title", "Title", "Titel"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Company", "Company", "Firma"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.CustomerNumber", "Customer number", "Kundennummer"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.BirthDate", "Birthdate", "Geburtstag"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.Gender", "Gender", "Gender"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.ZipPostalCode", "Zip postal code", "Plz"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.VatNumberStatusId", "Vat number status", "USt-IdNr."); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.TimeZoneId", "Time zone", "Zeitzone"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.TaxDisplayTypeId", "Tax display type", "Steueranzeigetyp"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.CountryId", "Country", "Land"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.CurrencyId", "Currency", "Währung"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.LanguageId", "Language", "Sprache"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.LastForumVisit", "Last forum visit days", "Tage seit letztem Forenbesuch"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.LastUserAgent", "Last user agent", "Zuletzt genutzter User-Agent"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.IsInCustomerRole", "In customer role", "In Kundengruppe"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.StoreId", "Store", "Store"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.LastOrderDateDays", "Last order date days", "Tage seit letzter Bestellung"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.AcceptThirdPartyEmailHandOver", "Accept third party email handover", "Akzeptiert Weitergabe der Emailadresse an Dritte"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.OrderTotal", "Order total", "Gesamtbetrag der Bestellung"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.OrderSubtotalInclTax", "Order subtotal incl. tax", "Gesamtbetrag der Bestellung (Brutto)"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.OrderSubtotalExclTax", "Order subtotal excl tax", "Gesamtbetrag der Bestellung (Netto)"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.PaymentMethodSystemName", "Payment method systemname", "Systemname der Zahlart"); - builder.AddOrUpdate("Admin.Rules.FilterDescriptor.ShippingRateComputationMethodSystemName", "Shipping rate computation method Systemname", "Systemname der Versandart"); + protected override void Seed(SmartObjectContext context) + { + context.MigrateLocaleResources(MigrateLocaleResources); + MigrateSettings(context); + } - builder.AddOrUpdate("Admin.Rules.SystemName", "System name", "Systemname"); - builder.AddOrUpdate("Admin.Rules.Title", "Title", "Titel"); - builder.AddOrUpdate("Admin.Rules.Execute", "{0} Execute {1} Rules", "Bedingungen {0} Ausführen {1}"); - builder.AddOrUpdate("Admin.Configuration.RuleSets", "Rule sets", "Regelsätze"); + public void MigrateSettings(SmartObjectContext context) + { - builder.AddOrUpdate("Admin.Rules.RuleSet.Fields.Name", "Name", "Name"); - builder.AddOrUpdate("Admin.Rules.RuleSet.Fields.Description", "Description", "Beschreibung"); - builder.AddOrUpdate("Admin.Rules.RuleSet.Fields.IsActive", "Is active", "Ist aktiv"); - builder.AddOrUpdate("Admin.Rules.RuleSet.Fields.Scope", "Scope", "Art"); - builder.AddOrUpdate("Admin.Rules.RuleSet.Fields.IsSubGroup", "Is sub group", "Titel"); - builder.AddOrUpdate("Admin.Rules.RuleSet.Fields.LogicalOperator", "Logical operator", "Logischer Operator"); + } - // RuleOperators - builder.AddOrUpdate("Admin.Rules.RuleOperator.Contains", "Contains", "Enthält"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.EndsWith", "Ends with", "Endet auf"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.GreaterThan", "Greater than", "Größer als"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.GreaterThanOrEqualTo", "Greater than or equal to", "Größer oder gleich"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.In", "In", "Ist eine von"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.IsEmpty", "Is empty", "Ist leer"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.IsEqualTo", "Is equal to", "Gleich"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.IsNotEmpty", "Is not empty", "Ist nicht leer"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.IsNotEqualTo", "Is not equal to", "Ungleich"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.IsNotNull", "Is not null", "Ist nicht NULL"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.IsNull", "Is null", "Ist NULL"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.LessThan", "Less than", "Kleiner als"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.LessThanOrEqualTo", "Less than or equal to", "Kleiner oder gleich"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.NotContains", "Not contains", "Enthält nicht"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.NotIn", "Not in", "Ist KEINE von"); - builder.AddOrUpdate("Admin.Rules.RuleOperator.StartsWith", "Starts with", "Beginnt mit"); + public void MigrateLocaleResources(LocaleResourcesBuilder builder) + { + builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.DisplayRegionInLanguageSelector", + "Display region in language selector", + "Region in der Sprachauswahl anzeigen", + "Whether to display region/country name in language selector (e.g. 'Deutsch (Deutschland)' instead of 'Deutsch')", + "Zeigt den Namen der Region/des Landes in der Sprachauswahl an (z. B. 'Deutsch (Deutschland)' statt 'Deutsch')"); } } -} +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/ObjectContextBase.SaveChanges.cs b/src/Libraries/SmartStore.Data/ObjectContextBase.SaveChanges.cs index 3faed3ad16..8503451f1f 100644 --- a/src/Libraries/SmartStore.Data/ObjectContextBase.SaveChanges.cs +++ b/src/Libraries/SmartStore.Data/ObjectContextBase.SaveChanges.cs @@ -10,346 +10,331 @@ using SmartStore.Core; using SmartStore.Core.Data.Hooks; using SmartStore.Utilities; +using SmartStore.Utilities.ObjectPools; using EfState = System.Data.Entity.EntityState; namespace SmartStore.Data { - public abstract partial class ObjectContextBase - { - private SaveChangesOperation _currentSaveOperation; - - private readonly static ConcurrentDictionary _hookableEntities = new ConcurrentDictionary(); - - private enum SaveStage - { - PreSave, - PostSave - } - - private IEnumerable GetChangedEntries() - { - return ChangeTracker.Entries().Where(x => x.State > EfState.Unchanged); - } - - public IDbHookHandler DbHookHandler - { - get; - set; - } - - public override int SaveChanges() - { - var op = _currentSaveOperation; - - if (op != null) - { - if (op.Stage == SaveStage.PreSave) - { - // This was called from within a PRE action hook. We must get out:... - // 1.) to prevent cyclic calls - // 2.) we want new entities in the state tracker (added by pre hooks) to be committed atomically in the core SaveChanges() call later on. - return 0; - } - else if (op.Stage == SaveStage.PostSave) - { - // This was called from within a POST action hook. Core SaveChanges() has already been called, - // but new entities could have been added to the state tracker by hooks. - // Therefore we need to commit them and get outta here, otherwise: cyclic nightmare! - // DetectChanges() here is important, 'cause we turned it off for the save process. - base.ChangeTracker.DetectChanges(); - return SaveChangesCore(); - } - } - - _currentSaveOperation = new SaveChangesOperation(this, this.DbHookHandler); - - Action endExecute = () => - { - _currentSaveOperation?.Dispose(); - _currentSaveOperation = null; - }; - - using (new ActionDisposable(endExecute)) - { - return _currentSaveOperation.Execute(); - } - } - - public override Task SaveChangesAsync(CancellationToken cancellationToken) - { - var op = _currentSaveOperation; - - if (op != null) - { - if (op.Stage == SaveStage.PreSave) - { - // This was called from within a PRE action hook. We must get out:... - // 1.) to prevent cyclic calls - // 2.) we want new entities in the state tracker (added by pre hooks) to be committed atomically in the core SaveChanges() call later on. - return Task.FromResult(0); - } - else if (op.Stage == SaveStage.PostSave) - { - // This was called from within a POST action hook. Core SaveChanges() has already been called, - // but new entities could have been added to the state tracker by hooks. - // Therefore we need to commit them and get outta here, otherwise: cyclic nightmare! - // DetectChanges() here is important, 'cause we turned it off for the save process. - base.ChangeTracker.DetectChanges(); - return SaveChangesCoreAsync(cancellationToken); - } - } - - _currentSaveOperation = new SaveChangesOperation(this, this.DbHookHandler); - - var result = _currentSaveOperation.ExecuteAsync(cancellationToken); - - result.ContinueWith(t => - { - _currentSaveOperation?.Dispose(); - _currentSaveOperation = null; - }); - - return result; - } - - /// - /// Just calls DbContext.SaveChanges() without any sugar - /// - /// The number of affected records - protected internal int SaveChangesCore() - { - return base.SaveChanges(); - } - - /// - /// Just calls DbContext.SaveChangesAsync() without any sugar - /// - /// The number of affected records - protected internal Task SaveChangesCoreAsync(CancellationToken cancellationToken) - { - return base.SaveChangesAsync(cancellationToken); - } - - private void IgnoreMergedData(IEnumerable entries, bool ignore) - { - foreach (var entry in entries) - { - entry.MergedDataIgnore = ignore; - } - } - - internal bool IsInSaveOperation - { - get { return _currentSaveOperation != null; } - } - - private bool IsHookableEntry(IHookedEntity entry) - { - var entity = entry.Entity; - if (entity == null) - { - return false; - } - - return IsHookableEntityType(entry.EntityType); - } - - private static bool IsHookableEntityType(Type entityType) - { - var isHookable = _hookableEntities.GetOrAdd(entityType, t => - { - var attr = t.GetAttribute(true); - if (attr != null) - { - return attr.IsHookable; - } - - // Entities are hookable by default - return true; - }); - - return isHookable; - } - - class SaveChangesOperation : IDisposable - { - private SaveStage _stage; - private IEnumerable _changedEntries; - private ObjectContextBase _ctx; - private IDbHookHandler _hookHandler; - - public SaveChangesOperation(ObjectContextBase ctx, IDbHookHandler hookHandler) - { - _ctx = ctx; - _hookHandler = hookHandler; - } - - public IEnumerable ChangedEntries - { - get { return _changedEntries; } - } - - public SaveStage Stage - { - get { return _stage; } - } - - private IDisposable ExecuteCore() - { - var autoDetectChanges = _ctx.Configuration.AutoDetectChangesEnabled; - IEnumerable mergeableEntities = null; - - // Suppress implicit DetectChanges() calls by EF, - // e.g. called by SaveChanges(), ChangeTracker.Entries() etc. - _ctx.Configuration.AutoDetectChangesEnabled = false; - - // Get all attached entries implementing IMergedData, - // we need to ignore merge on them. Otherwise - // EF's change detection may think that properties has changed - // where they actually didn't. - mergeableEntities = _ctx.GetMergeableEntitiesFromChangeTracker().ToArray(); - - // Now ignore merged data, otherwise merged data will be saved to database - _ctx.IgnoreMergedData(mergeableEntities, true); - - // We must detect changes earlier in the process - // before hooks are executed. Therefore we suppressed the - // implicit DetectChanges() call by EF and call it here explicitly. - _ctx.ChangeTracker.DetectChanges(); - - // Now get changed entries - _changedEntries = _ctx.GetChangedEntries(); - - // pre - PreExecute(out IEnumerable changedHookEntries); - - return new ActionDisposable(endExecute); - - void endExecute() - { - try - { - // post - PostExecute(changedHookEntries); - } - finally - { - _ctx.Configuration.AutoDetectChangesEnabled = autoDetectChanges; - _ctx.IgnoreMergedData(mergeableEntities, false); - } - } - } - - public int Execute() - { - using (ExecuteCore()) - { - return _ctx.SaveChangesCore(); - } - } - - public async Task ExecuteAsync(CancellationToken cancellationToken) - { - using (ExecuteCore()) - { - return await _ctx.SaveChangesCoreAsync(cancellationToken); - } - } - - private IEnumerable PreExecute(out IEnumerable changedHookEntries) - { - bool enableHooks = false; - bool importantHooksOnly = false; - bool anyStateChanged = false; - IEnumerable processedHooks = null; - - changedHookEntries = null; - - enableHooks = _changedEntries.Any(); // hooking is meaningless without hookable entries - if (enableHooks) - { - // despite the fact that hooking can be disabled, we MUST determine if any "important" pre hook exists. - // If yes, but hooking is disabled, we'll trigger only the important ones. - importantHooksOnly = !_ctx.HooksEnabled && _hookHandler.HasImportantSaveHooks(); - - // we'll enable hooking for this unit of work only when it's generally enabled, - // OR we have "important" hooks in the pipeline. - enableHooks = importantHooksOnly || _ctx.HooksEnabled; - } - - if (enableHooks) - { - changedHookEntries = _changedEntries - .Select(x => new HookedEntity(x)) - .Where(x => IsHookableEntityType(x.EntityType)) - .ToArray(); - - // Regardless of validation (possible fixing validation errors too) - processedHooks = _hookHandler.TriggerPreSaveHooks(changedHookEntries, importantHooksOnly, out anyStateChanged); - - if (processedHooks.Any() && changedHookEntries.Any(x => x.State == SmartStore.Core.Data.EntityState.Modified)) - { - // Because at least one pre action hook has been processed, - // we must assume that entity properties has been changed. - // We need to call DetectChanges() again. - _ctx.ChangeTracker.DetectChanges(); - } - } - - if (anyStateChanged) - { - // because the state of at least one entity has been changed during pre hooking - // we have to further reduce the set of hookable entities (for the POST hooks) - changedHookEntries = changedHookEntries.Where(x => x.InitialState > SmartStore.Core.Data.EntityState.Unchanged); - } - - return processedHooks ?? Enumerable.Empty(); - } - - private IEnumerable PostExecute(IEnumerable changedHookEntries) - { - if (changedHookEntries == null || !changedHookEntries.Any()) - return Enumerable.Empty(); - - // the existence of hook entries actually implies that hooking is enabled. - - _stage = SaveStage.PostSave; - - var importantHooksOnly = !_ctx.HooksEnabled && _hookHandler.HasImportantSaveHooks(); - - return _hookHandler.TriggerPostSaveHooks(changedHookEntries, importantHooksOnly); - } - - private string FormatValidationExceptionMessage(IEnumerable results) - { - var sb = new StringBuilder(); - sb.Append("Entity validation failed" + Environment.NewLine); - - foreach (var res in results) - { - var baseEntity = res.Entry.Entity as BaseEntity; - sb.AppendFormat("Entity Name: {0} - Id: {0} - State: {1}", - res.Entry.Entity.GetType().Name, - baseEntity != null ? baseEntity.Id.ToString() : "N/A", - res.Entry.State.ToString()); - sb.AppendLine(); - - foreach (var validationError in res.ValidationErrors) - { - sb.AppendFormat("\tProperty: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage); - sb.AppendLine(); - } - } - - return sb.ToString(); - } - - public void Dispose() - { - _ctx = null; - _hookHandler = null; - _changedEntries = null; - } - } - } + public abstract partial class ObjectContextBase + { + private SaveChangesOperation _currentSaveOperation; + + private readonly static ConcurrentDictionary _hookableEntities = new ConcurrentDictionary(); + + private enum SaveStage + { + PreSave, + PostSave + } + + private IEnumerable GetChangedEntries() + { + return ChangeTracker.Entries().Where(x => x.State > EfState.Unchanged); + } + + public IDbHookHandler DbHookHandler + { + get; + set; + } + + public override int SaveChanges() + { + var op = _currentSaveOperation; + + if (op != null) + { + if (op.Stage == SaveStage.PreSave) + { + // This was called from within a PRE action hook. We must get out:... + // 1.) to prevent cyclic calls + // 2.) we want new entities in the state tracker (added by pre hooks) to be committed atomically in the core SaveChanges() call later on. + return 0; + } + else if (op.Stage == SaveStage.PostSave) + { + // This was called from within a POST action hook. Core SaveChanges() has already been called, + // but new entities could have been added to the state tracker by hooks. + // Therefore we need to commit them and get outta here, otherwise: cyclic nightmare! + // DetectChanges() here is important, 'cause we turned it off for the save process. + base.ChangeTracker.DetectChanges(); + return SaveChangesCore(); + } + } + + _currentSaveOperation = new SaveChangesOperation(this, this.DbHookHandler); + + Action endExecute = () => + { + _currentSaveOperation?.Dispose(); + _currentSaveOperation = null; + }; + + using (new ActionDisposable(endExecute)) + { + return _currentSaveOperation.Execute(); + } + } + + public override Task SaveChangesAsync(CancellationToken cancellationToken) + { + var op = _currentSaveOperation; + + if (op != null) + { + if (op.Stage == SaveStage.PreSave) + { + // This was called from within a PRE action hook. We must get out:... + // 1.) to prevent cyclic calls + // 2.) we want new entities in the state tracker (added by pre hooks) to be committed atomically in the core SaveChanges() call later on. + return Task.FromResult(0); + } + else if (op.Stage == SaveStage.PostSave) + { + // This was called from within a POST action hook. Core SaveChanges() has already been called, + // but new entities could have been added to the state tracker by hooks. + // Therefore we need to commit them and get outta here, otherwise: cyclic nightmare! + // DetectChanges() here is important, 'cause we turned it off for the save process. + base.ChangeTracker.DetectChanges(); + return SaveChangesCoreAsync(cancellationToken); + } + } + + _currentSaveOperation = new SaveChangesOperation(this, this.DbHookHandler); + + var result = _currentSaveOperation.ExecuteAsync(cancellationToken); + + result.ContinueWith(t => + { + _currentSaveOperation?.Dispose(); + _currentSaveOperation = null; + }); + + return result; + } + + /// + /// Just calls DbContext.SaveChanges() without any sugar + /// + /// The number of affected records + protected internal int SaveChangesCore() + { + return base.SaveChanges(); + } + + /// + /// Just calls DbContext.SaveChangesAsync() without any sugar + /// + /// The number of affected records + protected internal Task SaveChangesCoreAsync(CancellationToken cancellationToken) + { + return base.SaveChangesAsync(cancellationToken); + } + + private void IgnoreMergedData(IEnumerable entries, bool ignore) + { + foreach (var entry in entries) + { + entry.MergedDataIgnore = ignore; + } + } + + internal bool IsInSaveOperation => _currentSaveOperation != null; + + private static bool IsHookableEntityType(Type entityType) + { + var isHookable = _hookableEntities.GetOrAdd(entityType, t => + { + var attr = t.GetAttribute(true); + if (attr != null) + { + return attr.IsHookable; + } + + // Entities are hookable by default + return true; + }); + + return isHookable; + } + + class SaveChangesOperation : IDisposable + { + private SaveStage _stage; + private IEnumerable _changedEntries; + private ObjectContextBase _ctx; + private IDbHookHandler _hookHandler; + + public SaveChangesOperation(ObjectContextBase ctx, IDbHookHandler hookHandler) + { + _ctx = ctx; + _hookHandler = hookHandler; + } + + public IEnumerable ChangedEntries => _changedEntries; + + public SaveStage Stage => _stage; + + private IDisposable ExecuteCore() + { + var autoDetectChanges = _ctx.Configuration.AutoDetectChangesEnabled; + IEnumerable mergeableEntities = null; + + // Suppress implicit DetectChanges() calls by EF, + // e.g. called by SaveChanges(), ChangeTracker.Entries() etc. + _ctx.Configuration.AutoDetectChangesEnabled = false; + + // Get all attached entries implementing IMergedData, + // we need to ignore merge on them. Otherwise + // EF's change detection may think that properties has changed + // where they actually didn't. + mergeableEntities = _ctx.GetMergeableEntitiesFromChangeTracker().ToArray(); + + // Now ignore merged data, otherwise merged data will be saved to database + _ctx.IgnoreMergedData(mergeableEntities, true); + + // We must detect changes earlier in the process + // before hooks are executed. Therefore we suppressed the + // implicit DetectChanges() call by EF and call it here explicitly. + _ctx.ChangeTracker.DetectChanges(); + + // Now get changed entries + _changedEntries = _ctx.GetChangedEntries(); + + // pre + PreExecute(out IEnumerable changedHookEntries); + + return new ActionDisposable(endExecute); + + void endExecute() + { + try + { + // post + PostExecute(changedHookEntries); + } + finally + { + _ctx.Configuration.AutoDetectChangesEnabled = autoDetectChanges; + _ctx.IgnoreMergedData(mergeableEntities, false); + } + } + } + + public int Execute() + { + using (ExecuteCore()) + { + return _ctx.SaveChangesCore(); + } + } + + public async Task ExecuteAsync(CancellationToken cancellationToken) + { + using (ExecuteCore()) + { + return await _ctx.SaveChangesCoreAsync(cancellationToken); + } + } + + private IEnumerable PreExecute(out IEnumerable changedHookEntries) + { + bool enableHooks = false; + bool importantHooksOnly = false; + bool anyStateChanged = false; + IEnumerable processedHooks = null; + + changedHookEntries = null; + + enableHooks = _changedEntries.Any(); // hooking is meaningless without hookable entries + if (enableHooks) + { + // despite the fact that hooking can be disabled, we MUST determine if any "important" pre hook exists. + // If yes, but hooking is disabled, we'll trigger only the important ones. + importantHooksOnly = !_ctx.HooksEnabled && _hookHandler.HasImportantSaveHooks(); + + // we'll enable hooking for this unit of work only when it's generally enabled, + // OR we have "important" hooks in the pipeline. + enableHooks = importantHooksOnly || _ctx.HooksEnabled; + } + + if (enableHooks) + { + var contextType = _ctx.GetType(); + + changedHookEntries = _changedEntries + .Select(x => new HookedEntity(contextType, x)) + .Where(x => IsHookableEntityType(x.EntityType)) + .ToArray(); + + // Regardless of validation (possible fixing validation errors too) + processedHooks = _hookHandler.TriggerPreSaveHooks(changedHookEntries, importantHooksOnly, out anyStateChanged); + + if (processedHooks.Any() && changedHookEntries.Any(x => x.State == SmartStore.Core.Data.EntityState.Modified)) + { + // Because at least one pre action hook has been processed, + // we must assume that entity properties has been changed. + // We need to call DetectChanges() again. + _ctx.ChangeTracker.DetectChanges(); + } + } + + if (anyStateChanged) + { + // because the state of at least one entity has been changed during pre hooking + // we have to further reduce the set of hookable entities (for the POST hooks) + changedHookEntries = changedHookEntries.Where(x => x.InitialState > SmartStore.Core.Data.EntityState.Unchanged); + } + + return processedHooks ?? Enumerable.Empty(); + } + + private IEnumerable PostExecute(IEnumerable changedHookEntries) + { + if (changedHookEntries == null || !changedHookEntries.Any()) + return Enumerable.Empty(); + + // the existence of hook entries actually implies that hooking is enabled. + + _stage = SaveStage.PostSave; + + var importantHooksOnly = !_ctx.HooksEnabled && _hookHandler.HasImportantSaveHooks(); + + return _hookHandler.TriggerPostSaveHooks(changedHookEntries, importantHooksOnly); + } + + private string FormatValidationExceptionMessage(IEnumerable results) + { + var psb = PooledStringBuilder.Rent(); + var sb = (StringBuilder)psb; + + sb.Append("Entity validation failed" + Environment.NewLine); + + foreach (var res in results) + { + var baseEntity = res.Entry.Entity as BaseEntity; + sb.AppendFormat("Entity Name: {0} - Id: {0} - State: {1}", + res.Entry.Entity.GetType().Name, + baseEntity != null ? baseEntity.Id.ToString() : "N/A", + res.Entry.State.ToString()); + sb.AppendLine(); + + foreach (var validationError in res.ValidationErrors) + { + sb.AppendFormat("\tProperty: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage); + sb.AppendLine(); + } + } + + return psb.ToStringAndReturn(); + } + + public void Dispose() + { + _ctx = null; + _hookHandler = null; + _changedEntries = null; + } + } + } } diff --git a/src/Libraries/SmartStore.Data/ObjectContextBase.cs b/src/Libraries/SmartStore.Data/ObjectContextBase.cs index ea5675eabb..26d5a65685 100644 --- a/src/Libraries/SmartStore.Data/ObjectContextBase.cs +++ b/src/Libraries/SmartStore.Data/ObjectContextBase.cs @@ -3,155 +3,154 @@ using System.Data; using System.Data.Common; using System.Data.Entity; -using System.Data.Entity.Core.Objects; using System.Data.Entity.Infrastructure; using System.Linq; +using System.Runtime.CompilerServices; using SmartStore.ComponentModel; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Data.Hooks; -using SmartStore.Data.Setup; using SmartStore.Utilities; using EfState = System.Data.Entity.EntityState; namespace SmartStore.Data { - [DbConfigurationType(typeof(SmartDbConfiguration))] + [DbConfigurationType(typeof(SmartDbConfiguration))] public abstract partial class ObjectContextBase : DbContext, IDbContext { - private static bool? _isSqlServer2012OrHigher = null; - private static int? _commandTimeoutInSeconds = CommonHelper.GetAppSetting("sm:EfCommandTimeout"); - - // Instance of the internal ObjectStateManager.TransactionManager - // required for detecting if EF performs change detection - private object _transactionManager; - - /// - /// Parameterless constructor for tooling support, e.g. EF Migrations. - /// - protected ObjectContextBase() - : this(GetConnectionString(), null) - { - } - - protected ObjectContextBase(string nameOrConnectionString, string alias = null) + private static bool? _isSqlServer2012OrHigher = null; + private static int? _commandTimeoutInSeconds = CommonHelper.GetAppSetting("sm:EfCommandTimeout"); + + // Instance of the internal ObjectStateManager.TransactionManager + // required for detecting if EF performs change detection + private object _transactionManager; + + /// + /// Parameterless constructor for tooling support, e.g. EF Migrations. + /// + protected ObjectContextBase() + : this(GetConnectionString(), null) + { + } + + protected ObjectContextBase(string nameOrConnectionString, string alias = null) : base(nameOrConnectionString) { - this.HooksEnabled = true; - this.AutoCommitEnabled = true; + this.HooksEnabled = true; + this.AutoCommitEnabled = true; this.Alias = null; - this.DbHookHandler = NullDbHookHandler.Instance; + this.DbHookHandler = NullDbHookHandler.Instance; - if (_commandTimeoutInSeconds >= 0 && DataSettings.Current.IsSqlServer) - { - Database.CommandTimeout = _commandTimeoutInSeconds; - } - } + if (_commandTimeoutInSeconds >= 0 && DataSettings.Current.IsSqlServer) + { + Database.CommandTimeout = _commandTimeoutInSeconds; + } + } - public bool HooksEnabled - { - get; - set; - } + public bool HooksEnabled + { + get; + set; + } - #region IDbContext members + #region IDbContext members - public virtual string CreateDatabaseScript() + public virtual string CreateDatabaseScript() { return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript(); } - public new DbSet Set() where TEntity : BaseEntity - { - return base.Set(); - } - - private IEnumerable ToParameters(params object[] parameters) - { - if (parameters == null || parameters.Length == 0) - return Enumerable.Empty(); - - return parameters.Cast(); - } - - public virtual IList ExecuteStoredProcedureList(string commandText, params object[] parameters) where TEntity : BaseEntity, new() - { - // Add parameters to command - var commandText2 = commandText; - var dbParams = ToParameters(parameters); - bool firstParam = true; - bool hasOutputParams = false; - foreach (var p in dbParams) - { - commandText += firstParam ? " " : ", "; - firstParam = false; - - commandText += "@" + p.ParameterName; - if (p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Output) - { - // output parameter - hasOutputParams = true; - commandText += " output"; - } - } - - - var isLegacyDb = !this.IsSqlServer2012OrHigher(); - if (isLegacyDb && hasOutputParams) - { - // SQL Server 2008 or lower is not capable of handling - // stored procedures with output parameters - return ExecuteStoredProcedureListLegacy(commandText2, dbParams); - } - - var result = this.Database.SqlQuery(commandText, parameters).ToList(); - if (!ForceNoTracking) - { - using (var scope = new DbContextScope(this, autoDetectChanges: false)) - { - for (int i = 0; i < result.Count; i++) - { - result[i] = Attach(result[i]); - } - } - } - return result; - } - - private IList ExecuteStoredProcedureListLegacy(string commandText, IEnumerable parameters) where TEntity : BaseEntity, new() - { - var connection = this.Database.Connection; - // Don't close the connection after command execution - - // open the connection for use - if (connection.State == ConnectionState.Closed) - connection.Open(); - - // create a command object - using (var cmd = connection.CreateCommand()) - { - // command to execute - cmd.CommandText = commandText; - cmd.CommandType = CommandType.StoredProcedure; - - // move parameters to command object - cmd.Parameters.AddRange(parameters.ToArray()); - - // database call - var reader = cmd.ExecuteReader(); - var result = ((IObjectContextAdapter)(this)).ObjectContext.Translate(reader).ToList(); - if (!ForceNoTracking) - { - for (int i = 0; i < result.Count; i++) - { - result[i] = Attach(result[i]); - } - } - // close up the reader, we're done saving results - reader.Close(); - return result; - } - } + public new DbSet Set() where TEntity : BaseEntity + { + return base.Set(); + } + + private IEnumerable ToParameters(params object[] parameters) + { + if (parameters == null || parameters.Length == 0) + return Enumerable.Empty(); + + return parameters.Cast(); + } + + public virtual IList ExecuteStoredProcedureList(string commandText, params object[] parameters) where TEntity : BaseEntity, new() + { + // Add parameters to command + var commandText2 = commandText; + var dbParams = ToParameters(parameters); + bool firstParam = true; + bool hasOutputParams = false; + foreach (var p in dbParams) + { + commandText += firstParam ? " " : ", "; + firstParam = false; + + commandText += "@" + p.ParameterName; + if (p.Direction == ParameterDirection.InputOutput || p.Direction == ParameterDirection.Output) + { + // output parameter + hasOutputParams = true; + commandText += " output"; + } + } + + + var isLegacyDb = !this.IsSqlServer2012OrHigher(); + if (isLegacyDb && hasOutputParams) + { + // SQL Server 2008 or lower is not capable of handling + // stored procedures with output parameters + return ExecuteStoredProcedureListLegacy(commandText2, dbParams); + } + + var result = this.Database.SqlQuery(commandText, parameters).ToList(); + if (!ForceNoTracking) + { + using (var scope = new DbContextScope(this, autoDetectChanges: false)) + { + for (int i = 0; i < result.Count; i++) + { + result[i] = Attach(result[i]); + } + } + } + return result; + } + + private IList ExecuteStoredProcedureListLegacy(string commandText, IEnumerable parameters) where TEntity : BaseEntity, new() + { + var connection = this.Database.Connection; + // Don't close the connection after command execution + + // open the connection for use + if (connection.State == ConnectionState.Closed) + connection.Open(); + + // create a command object + using (var cmd = connection.CreateCommand()) + { + // command to execute + cmd.CommandText = commandText; + cmd.CommandType = CommandType.StoredProcedure; + + // move parameters to command object + cmd.Parameters.AddRange(parameters.ToArray()); + + // database call + var reader = cmd.ExecuteReader(); + var result = ((IObjectContextAdapter)(this)).ObjectContext.Translate(reader).ToList(); + if (!ForceNoTracking) + { + for (int i = 0; i < result.Count; i++) + { + result[i] = Attach(result[i]); + } + } + // close up the reader, we're done saving results + reader.Close(); + return result; + } + } /// /// Creates a raw SQL query that will return elements of the given generic type. @@ -194,74 +193,80 @@ public int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, in return result; } - /// - /// Checks whether the underlying ORM mapper is currently in the process of detecting changes. - /// - /// - public virtual bool IsDetectingChanges() - { - if (_transactionManager == null && DataSettings.DatabaseIsInstalled()) - { - var stateManager = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager; - if (stateManager != null) - { - // Get the internal TransactionManager property instance from ObjectStateManager - _transactionManager = FastProperty.GetProperty(stateManager.GetType(), "TransactionManager")?.GetValue(stateManager); - } - } - - if (_transactionManager != null) - { - // Get the "IsDetectChanges" property of the internal TransactionManager - var prop = FastProperty.GetProperty(_transactionManager.GetType(), "IsDetectChanges"); - if (prop != null) - { - return (bool)prop.GetValue(_transactionManager); - } - } - - return false; - } - - public void DetectChanges() - { - base.ChangeTracker.DetectChanges(); - } - - public bool HasChanges - { - get + public int? ExecuteSqlCommandSafe(string sql, bool doNotEnsureTransaction = false, int? timeout = null, params object[] parameters) + { + try { - return GetChangedEntries().Any(); + return ExecuteSqlCommand(sql, doNotEnsureTransaction, timeout, parameters); } + catch + { + return null; + } + } + + /// + /// Checks whether the underlying ORM mapper is currently in the process of detecting changes. + /// + /// + public virtual bool IsDetectingChanges() + { + if (_transactionManager == null && DataSettings.DatabaseIsInstalled()) + { + var stateManager = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager; + if (stateManager != null) + { + // Get the internal TransactionManager property instance from ObjectStateManager + _transactionManager = FastProperty.GetProperty(stateManager.GetType(), "TransactionManager")?.GetValue(stateManager); + } + } + + if (_transactionManager != null) + { + // Get the "IsDetectChanges" property of the internal TransactionManager + var prop = FastProperty.GetProperty(_transactionManager.GetType(), "IsDetectChanges"); + if (prop != null) + { + return (bool)prop.GetValue(_transactionManager); + } + } + + return false; } - public virtual bool IsModified(BaseEntity entity) - { - Guard.NotNull(entity, nameof(entity)); + public void DetectChanges() + { + base.ChangeTracker.DetectChanges(); + } - var entry = this.Entry((object)entity); - return entry.HasChanges(this); - } + public bool HasChanges => GetChangedEntries().Any(); - public bool TryGetModifiedProperty(BaseEntity entity, string propertyName, out object originalValue) - { - Guard.NotNull(entity, nameof(entity)); + public virtual bool IsModified(BaseEntity entity) + { + Guard.NotNull(entity, nameof(entity)); - if (entity.IsTransientRecord()) - { - originalValue = null; - return false; - } + var entry = this.Entry((object)entity); + return entry.HasChanges(this); + } - var entry = this.Entry((object)entity); - return entry.TryGetModifiedProperty(this, propertyName, out originalValue); - } + public bool TryGetModifiedProperty(BaseEntity entity, string propertyName, out object originalValue) + { + Guard.NotNull(entity, nameof(entity)); - public IDictionary GetModifiedProperties(BaseEntity entity) - { - return this.Entry((object)entity).GetModifiedProperties(this); - } + if (entity.IsTransientRecord()) + { + originalValue = null; + return false; + } + + var entry = this.Entry((object)entity); + return entry.TryGetModifiedProperty(this, propertyName, out originalValue); + } + + public IDictionary GetModifiedProperties(BaseEntity entity) + { + return this.Entry((object)entity).GetModifiedProperties(this); + } // required for UoW implementation public string Alias { get; internal set; } @@ -269,222 +274,261 @@ public IDictionary GetModifiedProperties(BaseEntity entity) // performance on bulk inserts public bool AutoDetectChangesEnabled { - get + get => this.Configuration.AutoDetectChangesEnabled; + set => this.Configuration.AutoDetectChangesEnabled = value; + } + + // performance on bulk inserts + public bool ValidateOnSaveEnabled + { + get => this.Configuration.ValidateOnSaveEnabled; + set => this.Configuration.ValidateOnSaveEnabled = value; + } + + public bool ProxyCreationEnabled + { + get => this.Configuration.ProxyCreationEnabled; + set => this.Configuration.ProxyCreationEnabled = value; + } + + public bool LazyLoadingEnabled + { + get => this.Configuration.LazyLoadingEnabled; + set => this.Configuration.LazyLoadingEnabled = value; + } + + public bool ForceNoTracking { get; set; } + + public bool AutoCommitEnabled { get; set; } + + public ITransaction BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified) + { + var dbContextTransaction = this.Database.BeginTransaction(isolationLevel); + return new DbContextTransactionWrapper(dbContextTransaction); + } + + public void UseTransaction(DbTransaction transaction) + { + this.Database.UseTransaction(transaction); + } + + private IEnumerable GetMergeableEntitiesFromChangeTracker() + { + return base.ChangeTracker.Entries() + .Where(x => x.State > EfState.Detached) + .Select(x => x.Entity) + .OfType(); + } + + #endregion + + #region Utils + + /// + /// Resolves the connection string from the Settings.txt file + /// + /// The connection string + /// This helper is called from parameterless DbContext constructors which are required for EF tooling support or during installation. + public static string GetConnectionString() + { + if (DataSettings.Current.IsValid()) { - return this.Configuration.AutoDetectChangesEnabled; + return DataSettings.Current.DataConnectionString; } - set + + throw Error.Application("A connection string could not be resolved for the parameterless constructor of the derived DbContext. Either the database is not installed, or the file 'Settings.txt' does not exist or contains invalid content."); + } + + protected internal bool IsSqlServer2012OrHigher() + { + if (!_isSqlServer2012OrHigher.HasValue) { - this.Configuration.AutoDetectChangesEnabled = value; + try + { + // TODO: actually we should cache this value by connection (string). + // But fact is: it's quite unlikely that multiple DB versions are used within a single application scope. + var info = this.GetSqlServerInfo(); + string productVersion = info.ProductVersion; + int version = productVersion.Split(new char[] { '.' })[0].ToInt(); + _isSqlServer2012OrHigher = version >= 11; + } + catch + { + _isSqlServer2012OrHigher = false; + } } + + return _isSqlServer2012OrHigher.Value; } - // performance on bulk inserts - public bool ValidateOnSaveEnabled + public TEntity Attach(TEntity entity) where TEntity : BaseEntity { - get + var dbSet = Set(); + var alreadyAttached = dbSet.Local.FirstOrDefault(x => x.Id == entity.Id); + + if (alreadyAttached == null) { - return this.Configuration.ValidateOnSaveEnabled; + dbSet.Attach(entity); + return entity; } - set + + return alreadyAttached; + } + + public bool IsAttached(TEntity entity) where TEntity : BaseEntity + { + if (entity != null) { - this.Configuration.ValidateOnSaveEnabled = value; + return Set().Local.Any(x => x.Id == entity.Id); } + + return false; } - public bool ProxyCreationEnabled + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int DetachEntity(TEntity entity, bool deep = false) where TEntity : BaseEntity + { + return DetachInternal(entity, deep ? new HashSet() : null, deep); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int DetachEntities(bool unchangedEntitiesOnly = true, bool deep = false) where TEntity : BaseEntity + { + return DetachEntities(o => o is TEntity, unchangedEntitiesOnly, deep); + } + + public int DetachEntities(Func predicate, bool unchangedEntitiesOnly = true, bool deep = false) { - get + Guard.NotNull(predicate, nameof(predicate)); + + var numDetached = 0; + + using (new DbContextScope(this, autoDetectChanges: false, lazyLoading: false)) { - return this.Configuration.ProxyCreationEnabled; + var entries = this.ChangeTracker.Entries().Where(Match).ToList(); + + HashSet objSet = deep ? new HashSet() : null; + + foreach (var entry in entries) + { + numDetached += DetachInternal(entry, objSet, deep); + } + + return numDetached; + } + + bool Match(DbEntityEntry entry) + { + if (entry.State > EfState.Detached && predicate(entry.Entity)) + { + return unchangedEntitiesOnly + ? entry.State == EfState.Unchanged + : true; + } + + return false; } - set + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int DetachInternal(BaseEntity obj, ISet objSet, bool deep) + { + if (obj == null) + return 0; + + return DetachInternal(this.Entry(obj), objSet, deep); + } + + internal int DetachInternal(DbEntityEntry entry, ISet objSet, bool deep) + { + var obj = entry.Entity; + int numDetached = 0; + + if (deep) { - this.Configuration.ProxyCreationEnabled = value; + // This is to prevent an infinite recursion when the child object has a navigation property + // that points back to the parent + if (objSet != null && !objSet.Add(obj)) + return 0; + + // Recursively detach all navigation properties + foreach (var prop in FastProperty.GetProperties(obj.GetUnproxiedType()).Values) + { + if (typeof(BaseEntity).IsAssignableFrom(prop.Property.PropertyType)) + { + numDetached += DetachInternal(prop.GetValue(obj) as BaseEntity, objSet, deep); + } + else if (typeof(IEnumerable).IsAssignableFrom(prop.Property.PropertyType)) + { + var val = prop.GetValue(obj); + if (val is IEnumerable list) + { + foreach (var item in list.ToList()) + { + numDetached += DetachInternal(item, objSet, deep); + } + } + } + } } + + entry.State = EfState.Detached; + numDetached++; + + return numDetached; } - public bool LazyLoadingEnabled - { - get - { - return this.Configuration.LazyLoadingEnabled; - } - set - { - this.Configuration.LazyLoadingEnabled = value; - } - } - - public bool ForceNoTracking { get; set; } - - public bool AutoCommitEnabled { get; set; } - - public ITransaction BeginTransaction(IsolationLevel isolationLevel = IsolationLevel.Unspecified) - { - var dbContextTransaction = this.Database.BeginTransaction(isolationLevel); - return new DbContextTransactionWrapper(dbContextTransaction); - } - - public void UseTransaction(DbTransaction transaction) - { - this.Database.UseTransaction(transaction); - } - - private IEnumerable GetMergeableEntitiesFromChangeTracker() - { - return base.ChangeTracker.Entries() - .Where(x => x.State > EfState.Detached) - .Select(x => x.Entity) - .OfType(); - } + public void ChangeState(TEntity entity, EfState requestedState) where TEntity : BaseEntity + { + //Console.WriteLine("ChangeState ORIGINAL"); + var entry = this.Entry(entity); + + if (entry.State != requestedState) + { + // Only change state when requested state differs, + // because EF internally sets all properties to modified + // if necessary, even when requested state equals current state. + entry.State = requestedState; + } + } + + public void ReloadEntity(TEntity entity) where TEntity : BaseEntity + { + this.Entry((object)entity).ReloadEntity(); + } #endregion - #region Utils + #region Nested classes + + private class DbContextTransactionWrapper : ITransaction + { + private readonly DbContextTransaction _tx; - /// - /// Resolves the connection string from the Settings.txt file - /// - /// The connection string - /// This helper is called from parameterless DbContext constructors which are required for EF tooling support or during installation. - public static string GetConnectionString() - { - if (DataSettings.Current.IsValid()) - { - return DataSettings.Current.DataConnectionString; - } - - throw Error.Application("A connection string could not be resolved for the parameterless constructor of the derived DbContext. Either the database is not installed, or the file 'Settings.txt' does not exist or contains invalid content."); - } - - protected internal bool IsSqlServer2012OrHigher() - { - if (!_isSqlServer2012OrHigher.HasValue) - { - try - { - // TODO: actually we should cache this value by connection (string). - // But fact is: it's quite unlikely that multiple DB versions are used within a single application scope. - var info = this.GetSqlServerInfo(); - string productVersion = info.ProductVersion; - int version = productVersion.Split(new char[] { '.' })[0].ToInt(); - _isSqlServer2012OrHigher = version >= 11; - } - catch - { - _isSqlServer2012OrHigher = false; - } - } - - return _isSqlServer2012OrHigher.Value; - } - - public TEntity Attach(TEntity entity) where TEntity : BaseEntity - { - var dbSet = Set(); - var alreadyAttached = dbSet.Local.FirstOrDefault(x => x.Id == entity.Id); - - if (alreadyAttached == null) - { - dbSet.Attach(entity); - return entity; - } - - return alreadyAttached; - } - - public bool IsAttached(TEntity entity) where TEntity : BaseEntity - { - if (entity != null) - { - return Set().Local.Any(x => x.Id == entity.Id); - } - - return false; - } - - public void DetachEntity(TEntity entity) where TEntity : BaseEntity - { - this.Entry(entity).State = EfState.Detached; - } - - public int DetachEntities(bool unchangedEntitiesOnly = true) where TEntity : class - { - return DetachEntities(o => o is TEntity, unchangedEntitiesOnly); - } - - public int DetachEntities(Func predicate, bool unchangedEntitiesOnly = true) - { - Guard.NotNull(predicate, nameof(predicate)); - - Func predicate2 = x => - { - if (x.State > EfState.Detached && predicate(x.Entity)) - { - return unchangedEntitiesOnly - ? x.State == EfState.Unchanged - : true; - } - - return false; - }; - - var attachedEntities = this.ChangeTracker.Entries().Where(predicate2).ToList(); - attachedEntities.Each(entry => entry.State = EfState.Detached); - return attachedEntities.Count; - } - - public void ChangeState(TEntity entity, EfState requestedState) where TEntity : BaseEntity - { - //Console.WriteLine("ChangeState ORIGINAL"); - var entry = this.Entry(entity); - - if (entry.State != requestedState) - { - // Only change state when requested state differs, - // because EF internally sets all properties to modified - // if necessary, even when requested state equals current state. - entry.State = requestedState; - } - } - - public void ReloadEntity(TEntity entity) where TEntity : BaseEntity - { - this.Entry((object)entity).ReloadEntity(); - } - - #endregion - - #region Nested classes - - private class DbContextTransactionWrapper : ITransaction - { - private readonly DbContextTransaction _tx; - - public DbContextTransactionWrapper(DbContextTransaction tx) - { - Guard.NotNull(tx, nameof(tx)); - - _tx = tx; - } - - public void Commit() - { - _tx.Commit(); - } - - public void Rollback() - { - _tx.Rollback(); - } + public DbContextTransactionWrapper(DbContextTransaction tx) + { + Guard.NotNull(tx, nameof(tx)); - public void Dispose() - { - _tx.Dispose(); - } - } + _tx = tx; + } - #endregion - } + public void Commit() + { + _tx.Commit(); + } + + public void Rollback() + { + if (_tx.UnderlyingTransaction.Connection != null) + _tx.Rollback(); + } + + public void Dispose() + { + _tx.Dispose(); + } + } + + #endregion + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Setup/Builder/ActivityLogTypeMigrator.cs b/src/Libraries/SmartStore.Data/Setup/Builder/ActivityLogTypeMigrator.cs index 103513101e..c6419d7f3b 100644 --- a/src/Libraries/SmartStore.Data/Setup/Builder/ActivityLogTypeMigrator.cs +++ b/src/Libraries/SmartStore.Data/Setup/Builder/ActivityLogTypeMigrator.cs @@ -6,63 +6,63 @@ namespace SmartStore.Data.Setup { - internal class ActivityLogTypeMigrator - { - private readonly SmartObjectContext _ctx; - private readonly DbSet _activityLogTypeRecords; + internal class ActivityLogTypeMigrator + { + private readonly SmartObjectContext _ctx; + private readonly DbSet _activityLogTypeRecords; - public ActivityLogTypeMigrator(SmartObjectContext ctx) - { - Guard.NotNull(ctx, nameof(ctx)); + public ActivityLogTypeMigrator(SmartObjectContext ctx) + { + Guard.NotNull(ctx, nameof(ctx)); - _ctx = ctx; - _activityLogTypeRecords = _ctx.Set(); - } + _ctx = ctx; + _activityLogTypeRecords = _ctx.Set(); + } - private Language GetDefaultAdminLanguage(DbSet settingRecords, DbSet languageRecords) - { - const string settingKey = "LocalizationSettings.DefaultAdminLanguageId"; + private Language GetDefaultAdminLanguage(DbSet settingRecords, DbSet languageRecords) + { + const string settingKey = "LocalizationSettings.DefaultAdminLanguageId"; - var defaultAdminLanguageSetting = settingRecords.FirstOrDefault(x => x.Name == settingKey && x.StoreId == 0); + var defaultAdminLanguageSetting = settingRecords.FirstOrDefault(x => x.Name == settingKey && x.StoreId == 0); - if (defaultAdminLanguageSetting == null) - defaultAdminLanguageSetting = settingRecords.FirstOrDefault(x => x.Name == settingKey); + if (defaultAdminLanguageSetting == null) + defaultAdminLanguageSetting = settingRecords.FirstOrDefault(x => x.Name == settingKey); - if (defaultAdminLanguageSetting != null) - { - var defaultAdminLanguageId = defaultAdminLanguageSetting.Value.ToInt(); - if (defaultAdminLanguageId != 0) - { - var language = languageRecords.FirstOrDefault(x => x.Id == defaultAdminLanguageId); - if (language != null) - return language; - } - } + if (defaultAdminLanguageSetting != null) + { + var defaultAdminLanguageId = defaultAdminLanguageSetting.Value.ToInt(); + if (defaultAdminLanguageId != 0) + { + var language = languageRecords.FirstOrDefault(x => x.Id == defaultAdminLanguageId); + if (language != null) + return language; + } + } - return languageRecords.First(); - } + return languageRecords.First(); + } - public void AddActivityLogType(string systemKeyword, string enName, string deName) - { - Guard.NotEmpty(systemKeyword, nameof(systemKeyword)); - Guard.NotEmpty(enName, nameof(enName)); - Guard.NotEmpty(deName, nameof(deName)); + public void AddActivityLogType(string systemKeyword, string enName, string deName) + { + Guard.NotEmpty(systemKeyword, nameof(systemKeyword)); + Guard.NotEmpty(enName, nameof(enName)); + Guard.NotEmpty(deName, nameof(deName)); - var record = _activityLogTypeRecords.FirstOrDefault(x => x.SystemKeyword == systemKeyword); + var record = _activityLogTypeRecords.FirstOrDefault(x => x.SystemKeyword == systemKeyword); - if (record == null) - { - var language = GetDefaultAdminLanguage(_ctx.Set(), _ctx.Set()); + if (record == null) + { + var language = GetDefaultAdminLanguage(_ctx.Set(), _ctx.Set()); - _activityLogTypeRecords.Add(new ActivityLogType - { - Enabled = true, - SystemKeyword = systemKeyword, - Name = (language.UniqueSeoCode.IsCaseInsensitiveEqual("de") ? deName : enName) - }); + _activityLogTypeRecords.Add(new ActivityLogType + { + Enabled = true, + SystemKeyword = systemKeyword, + Name = (language.UniqueSeoCode.IsCaseInsensitiveEqual("de") ? deName : enName) + }); - _ctx.SaveChanges(); - } - } - } + _ctx.SaveChanges(); + } + } + } } diff --git a/src/Libraries/SmartStore.Data/Setup/Builder/BuilderDbContextExtensions.cs b/src/Libraries/SmartStore.Data/Setup/Builder/BuilderDbContextExtensions.cs index 91ad3c1149..fff96484f1 100644 --- a/src/Libraries/SmartStore.Data/Setup/Builder/BuilderDbContextExtensions.cs +++ b/src/Libraries/SmartStore.Data/Setup/Builder/BuilderDbContextExtensions.cs @@ -2,42 +2,40 @@ namespace SmartStore.Data.Setup { + public static class BuilderDbContextExtensions + { + #region Resource building - public static class BuilderDbContextExtensions - { - #region Resource building + public static void MigrateLocaleResources(this SmartObjectContext ctx, Action fn, bool updateTouchedResources = false) + { + Guard.NotNull(ctx, nameof(ctx)); + Guard.NotNull(fn, nameof(fn)); - public static void MigrateLocaleResources(this SmartObjectContext ctx, Action fn, bool updateTouchedResources = false) - { - Guard.NotNull(ctx, nameof(ctx)); - Guard.NotNull(fn, nameof(fn)); + var builder = new LocaleResourcesBuilder(); + fn(builder); + var entries = builder.Build(); - var builder = new LocaleResourcesBuilder(); - fn(builder); - var entries = builder.Build(); - - var migrator = new LocaleResourcesMigrator(ctx); - migrator.Migrate(entries, updateTouchedResources); - } + var migrator = new LocaleResourcesMigrator(ctx); + migrator.Migrate(entries, updateTouchedResources); + } - #endregion + #endregion - #region Settings building + #region Settings building - public static void MigrateSettings(this SmartObjectContext ctx, Action fn) - { - Guard.NotNull(ctx, nameof(ctx)); - Guard.NotNull(fn, nameof(fn)); + public static void MigrateSettings(this SmartObjectContext ctx, Action fn) + { + Guard.NotNull(ctx, nameof(ctx)); + Guard.NotNull(fn, nameof(fn)); - var builder = new SettingsBuilder(); - fn(builder); - var entries = builder.Build(); + var builder = new SettingsBuilder(); + fn(builder); + var entries = builder.Build(); - var migrator = new SettingsMigrator(ctx); - migrator.Migrate(entries); - } - - #endregion - } + var migrator = new SettingsMigrator(ctx); + migrator.Migrate(entries); + } + #endregion + } } diff --git a/src/Libraries/SmartStore.Data/Setup/Builder/LocaleResourcesBuilder.cs b/src/Libraries/SmartStore.Data/Setup/Builder/LocaleResourcesBuilder.cs index 2f90e53a5b..6459b02090 100644 --- a/src/Libraries/SmartStore.Data/Setup/Builder/LocaleResourcesBuilder.cs +++ b/src/Libraries/SmartStore.Data/Setup/Builder/LocaleResourcesBuilder.cs @@ -1,178 +1,177 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; namespace SmartStore.Data.Setup { - internal class LocaleResourceEntry - { - public string Key { get; set; } - public string Value { get; set; } - public string Lang { get; set; } - - public bool UpdateOnly { get; set; } - public bool Important { get; set; } - } - - public interface IResourceAddBuilder : IHideObjectMembers - { - IResourceAddBuilder Value(string value); - IResourceAddBuilder Value(string lang, string value); - IResourceAddBuilder Hint(string value); - IResourceAddBuilder Hint(string lang, string value); - } - - public class LocaleResourcesBuilder : IHideObjectMembers - { - private readonly List _entries = new List(); - - /// - /// Deletes one or many locale resources in any language from the database - /// - /// The key(s) of the resources to delete - public void Delete(params string[] keys) - { - keys.Each(x => _entries.Add(new LocaleResourceEntry { Key = x, Important = true })); - } - - /// - /// Deletes one or many locale resources in the specified language from the database - /// - /// The language identifier - /// The key(s) of the resources to delete - public void DeleteFor(string lang, params string[] keys) - { - Guard.NotEmpty(lang, nameof(lang)); - lang = lang.NullEmpty(); - - keys.Each(x => _entries.Add(new LocaleResourceEntry { Key = x, Lang = lang, Important = true })); - } - - /// - /// Updates existing locale resources - /// - /// The key of the resource - /// IResourceAddBuilder - public IResourceAddBuilder Update(string key) - { - Guard.NotEmpty(key, nameof(key)); - - Action fn = (string v, string l, bool isHint) => - { - string k = key; - if (isHint) - { - k += ".Hint"; - } - - _entries.Add(new LocaleResourceEntry { Key = k, Value = v, Lang = l, UpdateOnly = true }); - }; - - return new ResourceAddBuilder(key, fn); - } - - /// - /// Adds or updates locale resources - /// - /// The key of the resource - /// IResourceAddBuilder - public IResourceAddBuilder AddOrUpdate(string key) - { - Guard.NotEmpty(key, nameof(key)); - - Action fn = (string v, string l, bool isHint) => - { - string k = key; - if (isHint) - { - k += ".Hint"; - } - - _entries.Add(new LocaleResourceEntry { Key = k, Value = v, Lang = l }); - }; - - return new ResourceAddBuilder(key, fn); - } - - /// - /// Adds or updates locale resources - /// - /// The key of the resource - /// Primary English (untranslated) value of the resource - /// German value of the resource - public void AddOrUpdate(string key, string value, string deValue) - { - Guard.NotEmpty(key, nameof(key)); - - _entries.Add(new LocaleResourceEntry { Key = key, Value = value }); - _entries.Add(new LocaleResourceEntry { Key = key, Value = deValue, Lang = "de" }); - } - - /// - /// Adds or updates locale resources - /// - /// The key of the resource - /// Primary English (untranslated) value of the resource - /// German value of the resource - /// Primary English (untranslated) hint resource - /// German hint resource - public void AddOrUpdate(string key, string value, string deValue, string hint, string deHint) - { - Guard.NotEmpty(key, nameof(key)); - - AddOrUpdate(key, value, deValue); - AddOrUpdate(key + ".Hint", hint, deHint); - } - - internal void Reset() - { - _entries.Clear(); - } - - internal IEnumerable Build() - { - return _entries.OrderByDescending(x => x.Important).ThenBy(x => x.Lang).ToList(); - } - - #region Nested builder for AddOrUpdate - - internal class ResourceAddBuilder : IResourceAddBuilder - { - private readonly Action _fn; - - public ResourceAddBuilder(string key, Action fn) - { - _fn = fn; - } - - public IResourceAddBuilder Value(string value) - { - return Value(null, value); - } - - public IResourceAddBuilder Value(string lang, string value) - { - Guard.NotEmpty(value, nameof(value)); - _fn(value, lang.NullEmpty(), false); - return this; - } - - - public IResourceAddBuilder Hint(string value) - { - return Hint(null, value); - } - - public IResourceAddBuilder Hint(string lang, string value) - { - Guard.NotEmpty(value, nameof(value)); - _fn(value, lang.NullEmpty(), true); - return this; - } - } - - #endregion - - } + internal class LocaleResourceEntry + { + public string Key { get; set; } + public string Value { get; set; } + public string Lang { get; set; } + + public bool UpdateOnly { get; set; } + public bool Important { get; set; } + } + + public interface IResourceAddBuilder : IHideObjectMembers + { + IResourceAddBuilder Value(string value); + IResourceAddBuilder Value(string lang, string value); + IResourceAddBuilder Hint(string value); + IResourceAddBuilder Hint(string lang, string value); + } + + public class LocaleResourcesBuilder : IHideObjectMembers + { + private readonly List _entries = new List(); + + /// + /// Deletes one or many locale resources in any language from the database + /// + /// The key(s) of the resources to delete + public void Delete(params string[] keys) + { + keys.Each(x => _entries.Add(new LocaleResourceEntry { Key = x, Important = true })); + } + + /// + /// Deletes one or many locale resources in the specified language from the database + /// + /// The language identifier + /// The key(s) of the resources to delete + public void DeleteFor(string lang, params string[] keys) + { + Guard.NotEmpty(lang, nameof(lang)); + lang = lang.NullEmpty(); + + keys.Each(x => _entries.Add(new LocaleResourceEntry { Key = x, Lang = lang, Important = true })); + } + + /// + /// Updates existing locale resources + /// + /// The key of the resource + /// IResourceAddBuilder + public IResourceAddBuilder Update(string key) + { + Guard.NotEmpty(key, nameof(key)); + + Action fn = (string v, string l, bool isHint) => + { + string k = key; + if (isHint) + { + k += ".Hint"; + } + + _entries.Add(new LocaleResourceEntry { Key = k, Value = v, Lang = l, UpdateOnly = true }); + }; + + return new ResourceAddBuilder(key, fn); + } + + /// + /// Adds or updates locale resources + /// + /// The key of the resource + /// IResourceAddBuilder + public IResourceAddBuilder AddOrUpdate(string key) + { + Guard.NotEmpty(key, nameof(key)); + + Action fn = (string v, string l, bool isHint) => + { + string k = key; + if (isHint) + { + k += ".Hint"; + } + + _entries.Add(new LocaleResourceEntry { Key = k, Value = v, Lang = l }); + }; + + return new ResourceAddBuilder(key, fn); + } + + /// + /// Adds or updates locale resources + /// + /// The key of the resource + /// Primary English (untranslated) value of the resource + /// German value of the resource + public void AddOrUpdate(string key, string value, string deValue) + { + Guard.NotEmpty(key, nameof(key)); + + _entries.Add(new LocaleResourceEntry { Key = key, Value = value }); + _entries.Add(new LocaleResourceEntry { Key = key, Value = deValue, Lang = "de" }); + } + + /// + /// Adds or updates locale resources + /// + /// The key of the resource + /// Primary English (untranslated) value of the resource + /// German value of the resource + /// Primary English (untranslated) hint resource + /// German hint resource + public void AddOrUpdate(string key, string value, string deValue, string hint, string deHint) + { + Guard.NotEmpty(key, nameof(key)); + + AddOrUpdate(key, value, deValue); + AddOrUpdate(key + ".Hint", hint, deHint); + } + + internal void Reset() + { + _entries.Clear(); + } + + internal IEnumerable Build() + { + return _entries.OrderByDescending(x => x.Important).ThenBy(x => x.Lang).ToList(); + } + + #region Nested builder for AddOrUpdate + + internal class ResourceAddBuilder : IResourceAddBuilder + { + private readonly Action _fn; + + public ResourceAddBuilder(string key, Action fn) + { + _fn = fn; + } + + public IResourceAddBuilder Value(string value) + { + return Value(null, value); + } + + public IResourceAddBuilder Value(string lang, string value) + { + Guard.NotEmpty(value, nameof(value)); + _fn(value, lang.NullEmpty(), false); + return this; + } + + + public IResourceAddBuilder Hint(string value) + { + return Hint(null, value); + } + + public IResourceAddBuilder Hint(string lang, string value) + { + Guard.NotEmpty(value, nameof(value)); + _fn(value, lang.NullEmpty(), true); + return this; + } + } + + #endregion + + } } diff --git a/src/Libraries/SmartStore.Data/Setup/Builder/LocaleResourcesMigrator.cs b/src/Libraries/SmartStore.Data/Setup/Builder/LocaleResourcesMigrator.cs index 85718543d3..eadb6db49d 100644 --- a/src/Libraries/SmartStore.Data/Setup/Builder/LocaleResourcesMigrator.cs +++ b/src/Libraries/SmartStore.Data/Setup/Builder/LocaleResourcesMigrator.cs @@ -2,120 +2,147 @@ using System.Collections.Generic; using System.Data.Entity; using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Threading.Tasks; using SmartStore.Core.Data; using SmartStore.Core.Domain.Localization; namespace SmartStore.Data.Setup { - internal class LocaleResourcesMigrator - { - private readonly SmartObjectContext _ctx; - private readonly DbSet _languages; - private readonly DbSet _resources; - - public LocaleResourcesMigrator(SmartObjectContext ctx) - { - Guard.NotNull(ctx, nameof(ctx)); - - _ctx = ctx; - _languages = _ctx.Set(); - _resources = _ctx.Set(); - } - - public void Migrate(IEnumerable entries, bool updateTouchedResources = false) - { - Guard.NotNull(entries, nameof(entries)); - - if (!entries.Any() || !_languages.Any()) - return; - - using (var scope = new DbContextScope(_ctx, autoDetectChanges: false, hooksEnabled: false)) - { - var langMap = _languages.ToDictionarySafe(x => x.UniqueSeoCode.EmptyNull().ToLower()); - - var toDelete = new List(); - var toUpdate = new List(); - var toAdd = new List(); - - // remove all entries with invalid lang identifier - var invalidEntries = entries.Where(x => x.Lang != null && !langMap.ContainsKey(x.Lang.ToLower())); - if (invalidEntries.Any()) - { - entries = entries.Except(invalidEntries); - } - - foreach (var lang in langMap) - { - var validEntries = entries.Where(x => x.Lang == null || langMap[x.Lang.ToLower()].Id == lang.Value.Id); - foreach (var entry in validEntries) - { - var db = GetResource(entry.Key, lang.Value.Id, toAdd, out bool isLocal); - - if (db == null && entry.Value.HasValue() && !entry.UpdateOnly) - { - // ADD action - toAdd.Add(new LocaleStringResource { LanguageId = lang.Value.Id, ResourceName = entry.Key, ResourceValue = entry.Value }); - } - - if (db == null) - continue; - - if (entry.Value == null) - { - // DELETE action - if (isLocal) - toAdd.Remove(db); - else - toDelete.Add(db); - } - else - { - if (isLocal) - { - db.ResourceValue = entry.Value; - continue; - } - - // UPDATE action - if (updateTouchedResources || !db.IsTouched.GetValueOrDefault()) - { - db.ResourceValue = entry.Value; - toUpdate.Add(db); - if (toDelete.Contains(db)) - toDelete.Remove(db); - } - } - } - } - - // add new resources to context - _resources.AddRange(toAdd); - - // remove deleted resources - _resources.RemoveRange(toDelete); - - // save now - int affectedRows = _ctx.SaveChanges(); - } - } - - private LocaleStringResource GetResource(string key, int langId, IList local, out bool isLocal) - { - var res = local.FirstOrDefault(x => x.ResourceName.Equals(key, StringComparison.InvariantCultureIgnoreCase) && x.LanguageId == langId); - isLocal = res != null; - - if (res == null) - { - var query = _resources.Where(x => x.ResourceName.Equals(key, StringComparison.InvariantCultureIgnoreCase) && x.LanguageId == langId); - res = query.FirstOrDefault(); - } - - return res; - } - - } + internal class LocaleResourcesMigrator + { + private readonly SmartObjectContext _ctx; + private readonly DbSet _languages; + private readonly DbSet _resources; + + public LocaleResourcesMigrator(SmartObjectContext ctx) + { + Guard.NotNull(ctx, nameof(ctx)); + + _ctx = ctx; + _languages = _ctx.Set(); + _resources = _ctx.Set(); + } + + public void Migrate(IEnumerable entries, bool updateTouchedResources = false) + { + Guard.NotNull(entries, nameof(entries)); + + if (!entries.Any() || !_languages.Any()) + return; + + using (var scope = new DbContextScope(_ctx, autoDetectChanges: false, hooksEnabled: false)) + { + var langMap = _languages.ToDictionarySafe(x => x.LanguageCulture.EmptyNull().ToLower()); + + var toDelete = new List(); + var toUpdate = new List(); + var toAdd = new List(); + + bool IsEntryValid(LocaleResourceEntry entry, Language targetLang) + { + if (entry.Lang == null) + return true; + + var sourceLangCode = entry.Lang.ToLower(); + + if (targetLang != null) + { + var culture = targetLang.LanguageCulture; + if (culture == sourceLangCode || culture.StartsWith(sourceLangCode + "-")) + { + return true; + } + } + else + { + if (langMap.ContainsKey(sourceLangCode)) + return true; + + if (langMap.Keys.Any(k => k.StartsWith(sourceLangCode + "-", StringComparison.OrdinalIgnoreCase))) + return true; + } + + return false; + } + + // Remove all entries with invalid lang identifier + var invalidEntries = entries.Where(x => !IsEntryValid(x, null)); + if (invalidEntries.Any()) + { + entries = entries.Except(invalidEntries).ToArray(); + } + + foreach (var lang in langMap) + { + var validEntries = entries.Where(x => IsEntryValid(x, lang.Value)).ToArray(); + foreach (var entry in validEntries) + { + var db = GetResource(entry.Key, lang.Value.Id, toAdd, out bool isLocal); + + if (db == null && entry.Value.HasValue() && !entry.UpdateOnly) + { + // ADD action + toAdd.Add(new LocaleStringResource { LanguageId = lang.Value.Id, ResourceName = entry.Key, ResourceValue = entry.Value }); + } + + if (db == null) + continue; + + if (entry.Value == null) + { + // DELETE action + if (isLocal) + toAdd.Remove(db); + else + toDelete.Add(db); + } + else + { + if (isLocal) + { + db.ResourceValue = entry.Value; + continue; + } + + // UPDATE action + if (updateTouchedResources || !db.IsTouched.GetValueOrDefault()) + { + db.ResourceValue = entry.Value; + toUpdate.Add(db); + if (toDelete.Contains(db)) + toDelete.Remove(db); + } + } + } + } + + // add new resources to context + _resources.AddRange(toAdd); + + // remove deleted resources + _resources.RemoveRange(toDelete); + + // save now + int affectedRows = _ctx.SaveChanges(); + + _ctx.DetachEntities(); + _ctx.DetachEntities(); + } + } + + private LocaleStringResource GetResource(string key, int langId, IList local, out bool isLocal) + { + var res = local.FirstOrDefault(x => x.ResourceName.Equals(key, StringComparison.InvariantCultureIgnoreCase) && x.LanguageId == langId); + isLocal = res != null; + + if (res == null) + { + var query = _resources.Where(x => x.ResourceName.Equals(key, StringComparison.InvariantCultureIgnoreCase) && x.LanguageId == langId); + res = query.FirstOrDefault(); + } + + return res; + } + + } } diff --git a/src/Libraries/SmartStore.Data/Setup/Builder/PermissionMigrator.cs b/src/Libraries/SmartStore.Data/Setup/Builder/PermissionMigrator.cs deleted file mode 100644 index 8825ed79f8..0000000000 --- a/src/Libraries/SmartStore.Data/Setup/Builder/PermissionMigrator.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Data.Entity; -using System.Linq; -using SmartStore.Core.Data; -using SmartStore.Core.Domain.Customers; -using SmartStore.Core.Domain.Security; - -namespace SmartStore.Data.Setup -{ - internal class PermissionMigrator - { - private readonly SmartObjectContext _ctx; - private readonly DbSet _permissionRecords; - private readonly IQueryable _customerRoles; - - public PermissionMigrator(SmartObjectContext ctx) - { - Guard.NotNull(ctx, nameof(ctx)); - - _ctx = ctx; - _permissionRecords = _ctx.Set(); - _customerRoles = _ctx.Set().Expand(x => x.PermissionRecords); - } - - public void AddPermission(PermissionRecord permission, string[] rolesToMap) - { - Guard.NotNull(permission, nameof(permission)); - Guard.NotNull(rolesToMap, nameof(rolesToMap)); - - if (permission.SystemName.IsEmpty()) - return; - - var permissionRecord = _permissionRecords.FirstOrDefault(x => x.SystemName == permission.SystemName); - - if (permissionRecord == null) - { - _permissionRecords.Add(permission); - - _ctx.SaveChanges(); - } - - permissionRecord = _permissionRecords.FirstOrDefault(x => x.SystemName == permission.SystemName); - - foreach (var roleName in rolesToMap) - { - var role = _customerRoles.FirstOrDefault(x => x.SystemName == roleName); - if (role != null && !role.PermissionRecords.Any(x => x.SystemName == permission.SystemName)) - { - role.PermissionRecords.Add(permissionRecord); - } - } - - _ctx.SaveChanges(); - } - } -} diff --git a/src/Libraries/SmartStore.Data/Setup/Builder/SettingsBuilder.cs b/src/Libraries/SmartStore.Data/Setup/Builder/SettingsBuilder.cs index 2a2d087e89..6496fc4d4e 100644 --- a/src/Libraries/SmartStore.Data/Setup/Builder/SettingsBuilder.cs +++ b/src/Libraries/SmartStore.Data/Setup/Builder/SettingsBuilder.cs @@ -1,59 +1,57 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; namespace SmartStore.Data.Setup { - internal class SettingEntry - { - public string Key { get; set; } - public string Value { get; set; } - public bool KeyIsGroup { get; set; } - } - - public class SettingsBuilder : IHideObjectMembers - { - private readonly List _entries = new List(); - - /// - /// Deletes one or many setting records from the database - /// - /// The key(s) of the settings to delete - public void Delete(params string[] keys) - { - keys.Each(x => _entries.Add(new SettingEntry { Key = x.TrimSafe() })); - } - - /// - /// Deletes all settings records prefixed with the specified group name from the database - /// - /// The group/prefix (actually the settings class name) - public void DeleteGroup(string group) - { - Guard.NotEmpty(group, nameof(group)); - _entries.Add(new SettingEntry { Key = group.Trim(), KeyIsGroup = true }); - } - - /// - /// Adds a setting if it doesn't exist yet. - /// - public void Add(string key, TValue value) - { - Guard.NotEmpty(key, nameof(key)); - - var valueStr = value.Convert(); - _entries.Add(new SettingEntry { Key = key.Trim(), Value = valueStr }); - } - - internal void Reset() - { - _entries.Clear(); - } - - internal IEnumerable Build() - { - return _entries.Where(x => x.Key.HasValue()); - } - } + internal class SettingEntry + { + public string Key { get; set; } + public string Value { get; set; } + public bool KeyIsGroup { get; set; } + } + + public class SettingsBuilder : IHideObjectMembers + { + private readonly List _entries = new List(); + + /// + /// Deletes one or many setting records from the database + /// + /// The key(s) of the settings to delete + public void Delete(params string[] keys) + { + keys.Each(x => _entries.Add(new SettingEntry { Key = x.TrimSafe() })); + } + + /// + /// Deletes all settings records prefixed with the specified group name from the database + /// + /// The group/prefix (actually the settings class name) + public void DeleteGroup(string group) + { + Guard.NotEmpty(group, nameof(group)); + _entries.Add(new SettingEntry { Key = group.Trim(), KeyIsGroup = true }); + } + + /// + /// Adds a setting if it doesn't exist yet. + /// + public void Add(string key, TValue value) + { + Guard.NotEmpty(key, nameof(key)); + + var valueStr = value.Convert(); + _entries.Add(new SettingEntry { Key = key.Trim(), Value = valueStr }); + } + + internal void Reset() + { + _entries.Clear(); + } + + internal IEnumerable Build() + { + return _entries.Where(x => x.Key.HasValue()); + } + } } diff --git a/src/Libraries/SmartStore.Data/Setup/Builder/SettingsMigrator.cs b/src/Libraries/SmartStore.Data/Setup/Builder/SettingsMigrator.cs index b3d707666e..16eccfdfd4 100644 --- a/src/Libraries/SmartStore.Data/Setup/Builder/SettingsMigrator.cs +++ b/src/Libraries/SmartStore.Data/Setup/Builder/SettingsMigrator.cs @@ -2,103 +2,101 @@ using System.Collections.Generic; using System.Data.Entity; using System.Linq; -using System.Linq.Expressions; -using System.Text; using SmartStore.Core.Data; using SmartStore.Core.Domain.Configuration; namespace SmartStore.Data.Setup { - - internal class SettingsMigrator - { - private readonly SmartObjectContext _ctx; - private readonly DbSet _settings; - - public SettingsMigrator(SmartObjectContext ctx) - { - Guard.NotNull(ctx, nameof(ctx)); - - _ctx = ctx; - _settings = _ctx.Set(); - } - - public void Migrate(IEnumerable entries) - { - Guard.NotNull(entries, nameof(entries)); - - if (!entries.Any()) - return; - - using (var scope = new DbContextScope(_ctx, autoDetectChanges: false)) - { - var toDelete = new List(); - var toAdd = new List(); - - // First perform DELETE actions - foreach (var entry in entries.Where(x => x.Value == null)) - { - bool isPattern = entry.KeyIsGroup; - if (!HasSettings(entry.Key, isPattern)) - continue; // nothing to delete - - var db = GetSettings(entry.Key, isPattern); - _settings.RemoveRange(db); - } - - _ctx.SaveChanges(); - - // Then perform ADD actions - foreach (var entry in entries.Where(x => x.Value.HasValue())) - { - var existing = toAdd.FirstOrDefault(x => x.Name.Equals(entry.Key, StringComparison.InvariantCultureIgnoreCase)); - if (existing != null) - { - existing.Value = entry.Value; - continue; - } - - if (HasSettings(entry.Key, false)) - continue; // skip existing (we don't perform updates) - - _settings.Add(new Setting - { - Name = entry.Key, - Value = entry.Value, - StoreId = 0 - }); - } - - _ctx.SaveChanges(); - } - } - - private bool HasSettings(string key, bool isPattern = false) - { - var query = BuildQuery(key, isPattern); - return query.Any(); - } - - private IList GetSettings(string key, bool isPattern = false) - { - var query = BuildQuery(key, isPattern); - return query.ToList(); - } - - private IQueryable BuildQuery(string key, bool isPattern = false) - { - var query = _settings.AsQueryable(); - if (isPattern) - { - query = query.Where(x => x.Name.StartsWith(key)); - } - else - { - query = query.Where(x => x.Name.Equals(key)); - } - - return query; - } - } + + internal class SettingsMigrator + { + private readonly SmartObjectContext _ctx; + private readonly DbSet _settings; + + public SettingsMigrator(SmartObjectContext ctx) + { + Guard.NotNull(ctx, nameof(ctx)); + + _ctx = ctx; + _settings = _ctx.Set(); + } + + public void Migrate(IEnumerable entries) + { + Guard.NotNull(entries, nameof(entries)); + + if (!entries.Any()) + return; + + using (var scope = new DbContextScope(_ctx, autoDetectChanges: false)) + { + var toDelete = new List(); + var toAdd = new List(); + + // First perform DELETE actions + foreach (var entry in entries.Where(x => x.Value == null)) + { + bool isPattern = entry.KeyIsGroup; + if (!HasSettings(entry.Key, isPattern)) + continue; // nothing to delete + + var db = GetSettings(entry.Key, isPattern); + _settings.RemoveRange(db); + } + + _ctx.SaveChanges(); + + // Then perform ADD actions + foreach (var entry in entries.Where(x => x.Value.HasValue())) + { + var existing = toAdd.FirstOrDefault(x => x.Name.Equals(entry.Key, StringComparison.InvariantCultureIgnoreCase)); + if (existing != null) + { + existing.Value = entry.Value; + continue; + } + + if (HasSettings(entry.Key, false)) + continue; // skip existing (we don't perform updates) + + _settings.Add(new Setting + { + Name = entry.Key, + Value = entry.Value, + StoreId = 0 + }); + } + + _ctx.SaveChanges(); + } + } + + private bool HasSettings(string key, bool isPattern = false) + { + var query = BuildQuery(key, isPattern); + return query.Any(); + } + + private IList GetSettings(string key, bool isPattern = false) + { + var query = BuildQuery(key, isPattern); + return query.ToList(); + } + + private IQueryable BuildQuery(string key, bool isPattern = false) + { + var query = _settings.AsQueryable(); + if (isPattern) + { + query = query.Where(x => x.Name.StartsWith(key)); + } + else + { + query = query.Where(x => x.Name.Equals(key)); + } + + return query; + } + } } diff --git a/src/Libraries/SmartStore.Data/Setup/DbMigrationContext.cs b/src/Libraries/SmartStore.Data/Setup/DbMigrationContext.cs index 02159f16b1..61c6c92d0d 100644 --- a/src/Libraries/SmartStore.Data/Setup/DbMigrationContext.cs +++ b/src/Libraries/SmartStore.Data/Setup/DbMigrationContext.cs @@ -3,41 +3,83 @@ using System.Collections.Generic; using System.Data.Entity; using System.Linq; -using System.Text; +using SmartStore.Collections; namespace SmartStore.Data.Setup { - public class DbMigrationContext - { - private static DbMigrationContext s_dbMigrationContext; - private readonly ConcurrentDictionary _map = new ConcurrentDictionary(); - - public static DbMigrationContext Current - { - get - { - if (s_dbMigrationContext == null) - { - s_dbMigrationContext = new DbMigrationContext(); - } - return s_dbMigrationContext; - } - } - - internal void SetSuppressInitialCreate(bool suppress) where TContext : DbContext - { - _map[typeof(TContext)] = suppress; - } - - public bool SuppressInitialCreate() where TContext : DbContext - { - bool value; - if (_map.TryGetValue(typeof(TContext), out value)) - { - return value; - } - - return false; - } - } + public class DbMigrationContext + { + private static DbMigrationContext _instance; + private readonly ConcurrentDictionary _map = new ConcurrentDictionary(); + private Multimap _appliedMigrations; + private readonly object _lock = new object(); + + private DbMigrationContext() + { + } + + public static DbMigrationContext Current + { + get + { + if (_instance == null) + { + _instance = new DbMigrationContext(); + } + + return _instance; + } + } + + internal void SetSuppressInitialCreate(bool suppress) where TContext : DbContext + { + _map[typeof(TContext)] = suppress; + } + + public bool SuppressInitialCreate() where TContext : DbContext + { + if (_map.TryGetValue(typeof(TContext), out var value)) + { + return value; + } + + return false; + } + + internal void AddAppliedMigration(Type contextType, string migrationName) + { + if (_appliedMigrations == null) + { + lock (_lock) + { + if (_appliedMigrations == null) + { + _appliedMigrations = new Multimap(); + } + } + } + + _appliedMigrations.Add(contextType, migrationName); + } + + public IEnumerable GetAppliedMigrations() + { + if (_appliedMigrations != null) + { + return _appliedMigrations.SelectMany(x => x.Value); + } + + return Enumerable.Empty(); + } + + public IEnumerable GetAppliedMigrations() where TContext : DbContext + { + if (_appliedMigrations != null && _appliedMigrations.ContainsKey(typeof(TContext))) + { + return _appliedMigrations[typeof(TContext)].AsReadOnly(); + } + + return Enumerable.Empty(); + } + } } diff --git a/src/Libraries/SmartStore.Data/Setup/DbMigrationException.cs b/src/Libraries/SmartStore.Data/Setup/DbMigrationException.cs index 64caf77d27..8552957fe7 100644 --- a/src/Libraries/SmartStore.Data/Setup/DbMigrationException.cs +++ b/src/Libraries/SmartStore.Data/Setup/DbMigrationException.cs @@ -1,27 +1,23 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SmartStore.Data.Setup { - - public class DbMigrationException : ApplicationException - { - private const string MSG_DDL = "Migration '{0}' could not be applied due to following error: '{1}'."; - private const string MSG_SEED = "Migration '{0}' could not be seeded due to following error: '{1}'."; - private const string MSG_SUFFIX = " All changes to the database were reversed to '{2}'. Please downgrade your application to a version which is known to be compatible with '{2}'."; - - public DbMigrationException(string initialMigration, string targetMigration, Exception inner, bool isSeed) - : base(((isSeed ? MSG_SEED : MSG_DDL) + MSG_SUFFIX).FormatCurrent(targetMigration, inner != null ? inner.Message : "", initialMigration), inner) - { - this.InitialMigration = initialMigration; - this.TargetMigration = targetMigration; - } - public string InitialMigration { get; private set; } - public string TargetMigration { get; private set; } - } + public class DbMigrationException : ApplicationException + { + private const string MSG_DDL = "Migration '{0}' could not be applied due to following error: '{1}'."; + private const string MSG_SEED = "Migration '{0}' could not be seeded due to following error: '{1}'."; + private const string MSG_SUFFIX = " All changes to the database were reversed to '{2}'. Please downgrade your application to a version which is known to be compatible with '{2}'."; + + public DbMigrationException(string initialMigration, string targetMigration, Exception inner, bool isSeed) + : base(((isSeed ? MSG_SEED : MSG_DDL) + MSG_SUFFIX).FormatCurrent(targetMigration, inner != null ? inner.Message : "", initialMigration), inner) + { + this.InitialMigration = initialMigration; + this.TargetMigration = targetMigration; + } + + public string InitialMigration { get; private set; } + public string TargetMigration { get; private set; } + } } diff --git a/src/Libraries/SmartStore.Data/Setup/DbSeedingMigrator.cs b/src/Libraries/SmartStore.Data/Setup/DbSeedingMigrator.cs index ab071e861c..633c5ff6b1 100644 --- a/src/Libraries/SmartStore.Data/Setup/DbSeedingMigrator.cs +++ b/src/Libraries/SmartStore.Data/Setup/DbSeedingMigrator.cs @@ -4,219 +4,241 @@ using System.Data.Entity.Migrations; using System.Data.Entity.Migrations.Infrastructure; using System.Linq; +using SmartStore.Core.Events; using SmartStore.Core.Infrastructure; using SmartStore.Core.Logging; using SmartStore.Data.Migrations; namespace SmartStore.Data.Setup { - /// - /// Provides advanced migrations by providing a seeding platform for each migration. - /// This allows for initial seed data after each new database version (for example when - /// deploying new features and you want to include initial data). Seeders will be executing - /// in the correct order after all migrations have been completed. - /// - public class DbSeedingMigrator : DbMigrator where TContext : DbContext - { - private ILogger _logger; - private static bool _isMigrating; - private static Exception _lastSeedException; - - /// - /// Initializes a new instance of the DbMigrator class with the default (core db) configuration. - /// - public DbSeedingMigrator() - : this(new MigrationsConfiguration()) - { - } - - /// - /// Initializes a new instance of the DbMigrator class. - /// - /// Configuration to be used for the migration process. - public DbSeedingMigrator(DbMigrationsConfiguration configuration) - : base(configuration) - { - } - - public ILogger Logger - { - get - { - if (_logger == null) - { - try - { - _logger = EngineContext.Current.Resolve().GetLogger(this.GetType()); - } - catch - { - _logger = NullLogger.Instance; - } - } - - return _logger; - } - } - - public static bool IsMigrating - { - get - { - return _isMigrating; - } - } - - /// - /// Migrates the database to the latest version - /// - /// The number of applied migrations - public int RunPendingMigrations(TContext context) - { - if (_lastSeedException != null) - { - // This can happen when a previous migration attempt failed with a rollback. - throw _lastSeedException; - } - - var pendingMigrations = GetPendingMigrations().ToList(); - if (!pendingMigrations.Any()) - return 0; - - var coreSeeders = new List(); - var externalSeeders = new List(); - var isCoreMigration = context is SmartObjectContext; - var databaseMigrations = this.GetDatabaseMigrations().ToArray(); - var initialMigration = databaseMigrations.LastOrDefault() ?? "[Initial]"; - var lastSuccessfulMigration = databaseMigrations.FirstOrDefault(); - - IDataSeeder coreSeeder = null; - IDataSeeder externalSeeder = null; - - int result = 0; - _isMigrating = true; - - // Apply migrations - foreach (var migrationId in pendingMigrations) - { - if (MigratorUtils.IsAutomaticMigration(migrationId)) - continue; - - if (!MigratorUtils.IsValidMigrationId(migrationId)) - continue; - - // Resolve and instantiate the DbMigration instance from the assembly - var migration = MigratorUtils.CreateMigrationInstanceByMigrationId(migrationId, Configuration); - - // Seeders for the core DbContext must be run in any case - // (e.g. for Resource or Setting updates even from external plugins) - coreSeeder = migration as IDataSeeder; - externalSeeder = null; - - if (!isCoreMigration) - { - // Context specific seeders should only be resolved - // when origin is external (e.g. a Plugin) - externalSeeder = migration as IDataSeeder; - } - - try - { - // Call the actual Update() to execute this migration - Update(migrationId); - result++; - } - catch (AutomaticMigrationsDisabledException) - { - if (context is SmartObjectContext) - { - _isMigrating = false; - throw; - } - - // DbContexts in plugin assemblies tend to produce - // this error, but obviously without any negative side-effect. - // Therefore catch and forget! - // TODO: (MC) investigate this and implement a cleaner solution - } - catch (Exception ex) - { - result = 0; - _isMigrating = false; - throw new DbMigrationException(lastSuccessfulMigration, migrationId, ex.InnerException ?? ex, false); - } - - if (coreSeeder != null) - coreSeeders.Add(new SeederEntry { - DataSeeder = coreSeeder, - MigrationId = migrationId, - PreviousMigrationId = lastSuccessfulMigration, - }); - - if (externalSeeder != null) - externalSeeders.Add(new SeederEntry { - DataSeeder = externalSeeder, - MigrationId = migrationId, - PreviousMigrationId = lastSuccessfulMigration, - }); - - lastSuccessfulMigration = migrationId; - } - - // Apply core data seeders first - SmartObjectContext coreContext = null; - if (coreSeeders.Any()) - { - coreContext = isCoreMigration ? context as SmartObjectContext : new SmartObjectContext(); - RunSeeders(coreSeeders, coreContext); - } - - // Apply external data seeders - RunSeeders(externalSeeders, context); - - _isMigrating = false; - - Logger.Info("Database migration successful: {0} >> {1}".FormatInvariant(initialMigration, lastSuccessfulMigration)); - - return result; - } - - private void RunSeeders(IEnumerable seederEntries, T ctx) where T : DbContext - { - foreach (var seederEntry in seederEntries) - { - var seeder = (IDataSeeder)seederEntry.DataSeeder; - - try - { - seeder.Seed(ctx); - } - catch (Exception ex) - { - if (seeder.RollbackOnFailure) - { - Update(seederEntry.PreviousMigrationId); - _isMigrating = false; - _lastSeedException = new DbMigrationException(seederEntry.PreviousMigrationId, seederEntry.MigrationId, ex.InnerException ?? ex, true); - throw _lastSeedException; - } - - Logger.WarnFormat(ex, "Seed error in migration '{0}'. The error was ignored because no rollback was requested.", seederEntry.MigrationId); - } - } - } - - private void LogError(string initialMigration, string targetMigration, Exception exception) - { - Logger.ErrorFormat(exception, "Database migration error: {0} >> {1}", initialMigration, targetMigration); - } - - private class SeederEntry - { - public string PreviousMigrationId { get; set; } - public string MigrationId { get; set; } - public object DataSeeder { get; set; } - } - } + /// + /// Provides advanced migrations by providing a seeding platform for each migration. + /// This allows for initial seed data after each new database version (for example when + /// deploying new features and you want to include initial data). Seeders will be executing + /// in the correct order after all migrations have been completed. + /// + public class DbSeedingMigrator : DbMigrator where TContext : DbContext + { + private ILogger _logger; + private static bool _isMigrating; + private static Exception _lastSeedException; + + /// + /// Initializes a new instance of the DbMigrator class with the default (core db) configuration. + /// + public DbSeedingMigrator() + : this(new MigrationsConfiguration()) + { + } + + /// + /// Initializes a new instance of the DbMigrator class. + /// + /// Configuration to be used for the migration process. + public DbSeedingMigrator(DbMigrationsConfiguration configuration) + : base(configuration) + { + } + + public ILogger Logger + { + get + { + if (_logger == null) + { + try + { + _logger = EngineContext.Current.Resolve().GetLogger(this.GetType()); + } + catch + { + _logger = NullLogger.Instance; + } + } + + return _logger; + } + } + + public static bool IsMigrating => _isMigrating; + + /// + /// Migrates the database to the latest version + /// + /// The number of applied migrations + public int RunPendingMigrations(TContext context) + { + if (_lastSeedException != null) + { + // This can happen when a previous migration attempt failed with a rollback. + //return 0; + throw _lastSeedException; + } + + var pendingMigrations = GetPendingMigrations().ToList(); + if (!pendingMigrations.Any()) + return 0; + + // There are pending migrations and the app version is 3.0.0 at least. + // Earlier versions are totally blocked by AppUpdater. + // We have to ensure that the initial migration - which is '201705281903241_MoreIndexes' + // after squashing old 2.x and 1.x migrations - does not run if current head is '201705102339006_V3Final'. + DbMigrationContext.Current.SetSuppressInitialCreate(true); + + var coreSeeders = new List(); + var externalSeeders = new List(); + var isCoreMigration = context is SmartObjectContext; + var databaseMigrations = this.GetDatabaseMigrations().ToArray(); + var initialMigration = databaseMigrations.LastOrDefault() ?? "[Initial]"; + var lastSuccessfulMigration = databaseMigrations.FirstOrDefault(); + int result = 0; + + _isMigrating = true; + + // Apply migrations + foreach (var migrationId in pendingMigrations) + { + if (MigratorUtils.IsAutomaticMigration(migrationId)) + continue; + + if (!MigratorUtils.IsValidMigrationId(migrationId)) + continue; + + // Resolve and instantiate the DbMigration instance from the assembly + var migration = MigratorUtils.CreateMigrationInstanceByMigrationId(migrationId, Configuration); + + // Seeders for the core DbContext must be run in any case + // (e.g. for Resource or Setting updates even from external plugins) + var coreSeeder = migration as IDataSeeder; + IDataSeeder externalSeeder = null; + + if (!isCoreMigration) + { + // Context specific seeders should only be resolved + // when origin is external (e.g. a Plugin) + externalSeeder = migration as IDataSeeder; + } + + try + { + // Call the actual Update() to execute this migration + Update(migrationId); + result++; + } + catch (AutomaticMigrationsDisabledException) + { + if (context is SmartObjectContext) + { + _isMigrating = false; + throw; + } + + // DbContexts in plugin assemblies tend to produce + // this error, but obviously without any negative side-effect. + // Therefore catch and forget! + // TODO: (MC) investigate this and implement a cleaner solution + } + catch (Exception ex) + { + result = 0; + _isMigrating = false; + throw new DbMigrationException(lastSuccessfulMigration, migrationId, ex.InnerException ?? ex, false); + } + + var migrationName = MigratorUtils.GetMigrationClassName(migrationId); + + if (coreSeeder != null) + coreSeeders.Add(new SeederEntry + { + DataSeeder = coreSeeder, + MigrationId = migrationId, + MigrationName = migrationName, + PreviousMigrationId = lastSuccessfulMigration, + }); + + if (externalSeeder != null) + externalSeeders.Add(new SeederEntry + { + DataSeeder = externalSeeder, + MigrationId = migrationId, + MigrationName = migrationName, + PreviousMigrationId = lastSuccessfulMigration, + }); + + lastSuccessfulMigration = migrationId; + DbMigrationContext.Current.AddAppliedMigration(typeof(TContext), migrationId); + } + + if (coreSeeders.Any()) + { + // Apply core data seeders first + var coreContext = isCoreMigration ? context as SmartObjectContext : new SmartObjectContext(); + RunSeeders(coreSeeders, coreContext); + } + + // Apply external data seeders + RunSeeders(externalSeeders, context); + + _isMigrating = false; + + Logger.Info("Database migration successful: {0} >> {1}".FormatInvariant(initialMigration, lastSuccessfulMigration)); + + return result; + } + + private void RunSeeders(IEnumerable seederEntries, T ctx) where T : DbContext + { + var eventPublisher = EngineContext.Current.Resolve(); + + foreach (var seederEntry in seederEntries) + { + var seeder = (IDataSeeder)seederEntry.DataSeeder; + + try + { + // Pre seed event + eventPublisher.Publish(new SeedingDbMigrationEvent { MigrationName = seederEntry.MigrationName, DbContext = ctx }); + + // Seed + seeder.Seed(ctx); + + // Post seed event + eventPublisher.Publish(new SeededDbMigrationEvent { MigrationName = seederEntry.MigrationName, DbContext = ctx }); + } + catch (Exception ex) + { + if (seeder.RollbackOnFailure) + { + _lastSeedException = new DbMigrationException(seederEntry.PreviousMigrationId, seederEntry.MigrationId, ex.InnerException ?? ex, true); + + try + { + Update(seederEntry.PreviousMigrationId); + } + catch { } + + _isMigrating = false; + + throw _lastSeedException; + } + + Logger.WarnFormat(ex, "Seed error in migration '{0}'. The error was ignored because no rollback was requested.", seederEntry.MigrationId); + } + } + } + + private void LogError(string initialMigration, string targetMigration, Exception exception) + { + Logger.ErrorFormat(exception, "Database migration error: {0} >> {1}", initialMigration, targetMigration); + } + + private class SeederEntry + { + public string PreviousMigrationId { get; set; } + public string MigrationId { get; set; } + public string MigrationName { get; set; } + public object DataSeeder { get; set; } + } + } } diff --git a/src/Libraries/SmartStore.Data/Setup/IDataSeeder.cs b/src/Libraries/SmartStore.Data/Setup/IDataSeeder.cs index bb30dd802e..72f5a511a0 100644 --- a/src/Libraries/SmartStore.Data/Setup/IDataSeeder.cs +++ b/src/Libraries/SmartStore.Data/Setup/IDataSeeder.cs @@ -1,21 +1,18 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Linq; +using System.Data.Entity; namespace SmartStore.Data.Setup { - public interface IDataSeeder where TContext : DbContext - { - /// - /// Seeds data - /// - void Seed(TContext context); + public interface IDataSeeder where TContext : DbContext + { + /// + /// Seeds data + /// + void Seed(TContext context); - /// - /// Gets a value indicating whether migration should be completely rolled back - /// when an error occurs during migration seeding. - /// - bool RollbackOnFailure { get; } - } + /// + /// Gets a value indicating whether migration should be completely rolled back + /// when an error occurs during migration seeding. + /// + bool RollbackOnFailure { get; } + } } diff --git a/src/Libraries/SmartStore.Data/Setup/IDbMigrationExtensions.cs b/src/Libraries/SmartStore.Data/Setup/IDbMigrationExtensions.cs index c0c842e39c..8513c8352e 100644 --- a/src/Libraries/SmartStore.Data/Setup/IDbMigrationExtensions.cs +++ b/src/Libraries/SmartStore.Data/Setup/IDbMigrationExtensions.cs @@ -1,44 +1,33 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity.Migrations.Infrastructure; +using System.Data.Entity.Migrations.Infrastructure; using System.Data.Entity.Migrations.Model; -using System.Diagnostics; -using System.IO; -using System.Linq; using System.Reflection; -using System.Text; using SmartStore.Core.Data; -using SmartStore.Utilities; namespace SmartStore.Data.Setup { - - public static class IDbMigrationExtensions - { + public static class IDbMigrationExtensions + { + public static void SqlFileOrResource(this IDbMigration migration, string fileName, Assembly assembly = null, string location = null) + { + Guard.NotEmpty(fileName, nameof(fileName)); - public static void SqlFileOrResource(this IDbMigration migration, string fileName, Assembly assembly = null, string location = null) - { - Guard.NotEmpty(fileName, nameof(fileName)); - - var tokenizer = new SqlFileTokenizer(fileName, assembly ?? Assembly.GetExecutingAssembly(), location); - foreach (var cmd in tokenizer.Tokenize()) - { - if (cmd.HasValue()) - { - migration.AddSqlOperation(cmd); - } - } - } - - private static void AddSqlOperation(this IDbMigration migration, string sql, bool suppressTransaction = false, object anonymousArguments = null) - { - var operation = new SqlOperation(sql, anonymousArguments) - { - SuppressTransaction = suppressTransaction - }; - migration.AddOperation(operation); - } - - } + var tokenizer = new SqlFileTokenizer(fileName, assembly ?? Assembly.GetExecutingAssembly(), location); + foreach (var cmd in tokenizer.Tokenize()) + { + if (cmd.HasValue()) + { + migration.AddSqlOperation(cmd); + } + } + } + private static void AddSqlOperation(this IDbMigration migration, string sql, bool suppressTransaction = false, object anonymousArguments = null) + { + var operation = new SqlOperation(sql, anonymousArguments) + { + SuppressTransaction = suppressTransaction + }; + migration.AddOperation(operation); + } + } } diff --git a/src/Libraries/SmartStore.Data/Setup/ILocaleResourcesProvider.cs b/src/Libraries/SmartStore.Data/Setup/ILocaleResourcesProvider.cs index 95eeb90779..0ad4a5c81c 100644 --- a/src/Libraries/SmartStore.Data/Setup/ILocaleResourcesProvider.cs +++ b/src/Libraries/SmartStore.Data/Setup/ILocaleResourcesProvider.cs @@ -1,11 +1,9 @@ -using System; - -namespace SmartStore.Data.Setup +namespace SmartStore.Data.Setup { - - public interface ILocaleResourcesProvider - { - void MigrateLocaleResources(LocaleResourcesBuilder builder); - } + + public interface ILocaleResourcesProvider + { + void MigrateLocaleResources(LocaleResourcesBuilder builder); + } } diff --git a/src/Libraries/SmartStore.Data/Setup/InstallDatabaseInitializer.cs b/src/Libraries/SmartStore.Data/Setup/InstallDatabaseInitializer.cs index 3a8d7f115a..1e48290630 100644 --- a/src/Libraries/SmartStore.Data/Setup/InstallDatabaseInitializer.cs +++ b/src/Libraries/SmartStore.Data/Setup/InstallDatabaseInitializer.cs @@ -1,44 +1,42 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; +using System.Data.Entity; using System.Data.Entity.Migrations; using SmartStore.Data.Migrations; namespace SmartStore.Data.Setup { - /// - /// An implementation of that will use Code First Migrations - /// to setup and seed the database. - /// - public class InstallDatabaseInitializer : MigrateDatabaseInitializer - { - public InstallDatabaseInitializer() - : base() - { - } + /// + /// An implementation of that will use Code First Migrations + /// to setup and seed the database. + /// + public class InstallDatabaseInitializer : MigrateDatabaseInitializer + { + public InstallDatabaseInitializer() + : base() + { + } - public InstallDatabaseInitializer(string connectionString) - : base(connectionString) - { - } + public InstallDatabaseInitializer(string connectionString) + : base(connectionString) + { + } - /// - /// Initializes the database. - /// - /// The context. - /// - public override void InitializeDatabase(SmartObjectContext context) - { - // we don't use DbSeedingMigrator here because we don't care - // about Migration seeds during installation. - // The installation seeder contains ALL required seed data already. - var migrator = new DbMigrator(base.CreateConfiguration()); + /// + /// Initializes the database. + /// + /// The context. + /// + public override void InitializeDatabase(SmartObjectContext context) + { + // we don't use DbSeedingMigrator here because we don't care + // about Migration seeds during installation. + // 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); - } - } + // 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 7042551b5c..a4d10d520a 100644 --- a/src/Libraries/SmartStore.Data/Setup/MigrateDatabaseInitializer.cs +++ b/src/Libraries/SmartStore.Data/Setup/MigrateDatabaseInitializer.cs @@ -4,6 +4,7 @@ using System.Data.Entity.Infrastructure; using System.Data.Entity.Migrations; using System.Linq; +using SmartStore.Collections; using SmartStore.Core.Data; using SmartStore.Data.Caching; using SmartStore.Data.Migrations; @@ -11,164 +12,147 @@ namespace SmartStore.Data.Setup { - /// - /// An implementation of that will use Code First Migrations - /// to update the database to the latest version. - /// - public class MigrateDatabaseInitializer : IDatabaseInitializer - where TContext : DbContext, new() - where TConfig : DbMigrationsConfiguration, new() - { - #region Ctor - - public MigrateDatabaseInitializer() - { - } - - public MigrateDatabaseInitializer(string connectionString) - { - this.ConnectionString = connectionString; - } - - #endregion - - #region Properties - - public IEnumerable> DataSeeders - { - get; - set; - } - - public IEnumerable TablesToCheck - { - get; - set; - } - - public string ConnectionString - { - get; - private set; - } - - #endregion - - #region Interface members - - /// - /// Initializes the database. - /// - /// The context. - public virtual void InitializeDatabase(TContext context) - { - if (!context.Database.Exists()) - { - throw Error.InvalidOperation("Database migration failed because the target database does not exist. Ensure the database was initialized and seeded with the 'InstallDatabaseInitializer'."); - } - - var config = CreateConfiguration(); - - var migrator = new DbSeedingMigrator(config); - var tablesExist = CheckTables(context); - - if (tablesExist) - { - // Tables specific to the model DO exist in the database... - var hasHistoryEntry = migrator.GetDatabaseMigrations().Any(); - if (!hasHistoryEntry) - { - // ...but there is no entry in the __MigrationHistory table (or __MigrationHistory doesn't exist at all) - // We MUST assume that the database was created with a previous SmartStore version - // prior integrating EF Migrations. - // Running the Migrator with initial DDL would crash in this case as - // the db objects exist already. Therefore we set a suppression flag - // which we read in the corresponding InitialMigration to exit early. - DbMigrationContext.Current.SetSuppressInitialCreate(true); - } - } - - using (new DbContextScope(context as IDbContext, hooksEnabled: false)) - { - // run all pending migrations - var appliedCount = migrator.RunPendingMigrations(context); - - if (appliedCount > 0) - { - Seed(context); - } - else - { - // DB is up-to-date and no migration ran. - EfMappingViewCacheFactory.SetContext(context); - - if (config is MigrationsConfiguration coreConfig && context is SmartObjectContext ctx) - { - // Call the main Seed method anyway (on every startup), - // we could have locale resources or settings to add/update. - coreConfig.SeedDatabase(ctx); - } - } - - // not needed anymore - this.DataSeeders = null; - } - } - - #endregion - - #region Methods - - /// - /// Seeds the specified context. - /// - /// The context. - protected virtual void Seed(TContext context) - { - if (this.DataSeeders == null) - return; - - this.DataSeeders.Each((x) => x.Seed(context)); - } - - protected virtual TConfig CreateConfiguration() - { - var config = new TConfig(); - if (this.ConnectionString.HasValue()) - { - var dbContextInfo = new DbContextInfo(typeof(TContext)); - config.TargetDatabase = new DbConnectionInfo(this.ConnectionString, dbContextInfo.ConnectionProviderName); - } - - if (config.CommandTimeout == null && DataSettings.Current.IsSqlServer) - { - var commandTimeout = CommonHelper.GetAppSetting("sm:EfMigrationsCommandTimeout"); - if (commandTimeout.HasValue) - { - config.CommandTimeout = commandTimeout.Value; - } - } - - return config; - } - - /// - /// Checks tables existence - /// - /// - /// Returns true if the tables to check exist in the database or the check list is empty. - /// - protected bool CheckTables(TContext context) - { - if (this.TablesToCheck == null || !this.TablesToCheck.Any()) - return true; - - var existingTableNames = new List(context.Database.SqlQuery("SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type = 'BASE TABLE'")); - var result = existingTableNames.Intersect(this.TablesToCheck, StringComparer.InvariantCultureIgnoreCase).Count() == 0; - return !result; - } - - #endregion - } + /// + /// An implementation of that will use Code First Migrations + /// to update the database to the latest version. + /// + public class MigrateDatabaseInitializer : IDatabaseInitializer + where TContext : DbContext, new() + where TConfig : DbMigrationsConfiguration, new() + { + private static readonly SyncedCollection _initializedContextTypes = new List().AsSynchronized(); + + public MigrateDatabaseInitializer() + { + } + + public MigrateDatabaseInitializer(string connectionString) + { + this.ConnectionString = connectionString; + } + + public IEnumerable> DataSeeders + { + get; + set; + } + + public IEnumerable TablesToCheck + { + get; + set; + } + + public string ConnectionString + { + get; + private set; + } + + #region Interface members + + /// + /// Initializes the database. + /// + /// The context. + public virtual void InitializeDatabase(TContext context) + { + if (_initializedContextTypes.Contains(context.GetType())) + { + return; + } + + if (!context.Database.Exists()) + { + throw Error.InvalidOperation("Database migration failed because the target database does not exist. Ensure the database was initialized and seeded with the 'InstallDatabaseInitializer'."); + } + + var config = CreateConfiguration(); + var migrator = new DbSeedingMigrator(config); + + using (new DbContextScope(context as IDbContext, hooksEnabled: false)) + { + // run all pending migrations + var appliedCount = migrator.RunPendingMigrations(context); + + if (appliedCount > 0) + { + Seed(context); + } + else + { + // DB is up-to-date and no migration ran. + EfMappingViewCacheFactory.SetContext(context); + + if (config is MigrationsConfiguration coreConfig && context is SmartObjectContext ctx) + { + // Call the main Seed method anyway (on every startup), + // we could have locale resources or settings to add/update. + coreConfig.SeedDatabase(ctx); + } + } + + // not needed anymore + this.DataSeeders = null; + + _initializedContextTypes.Add(context.GetType()); + } + } + + #endregion + + #region Methods + + /// + /// Seeds the specified context. + /// + /// The context. + protected virtual void Seed(TContext context) + { + if (this.DataSeeders == null) + return; + + this.DataSeeders.Each((x) => x.Seed(context)); + } + + protected virtual TConfig CreateConfiguration() + { + var config = new TConfig(); + if (this.ConnectionString.HasValue()) + { + var dbContextInfo = new DbContextInfo(typeof(TContext)); + config.TargetDatabase = new DbConnectionInfo(this.ConnectionString, dbContextInfo.ConnectionProviderName); + } + + if (config.CommandTimeout == null && DataSettings.Current.IsSqlServer) + { + var commandTimeout = CommonHelper.GetAppSetting("sm:EfMigrationsCommandTimeout"); + if (commandTimeout.HasValue) + { + config.CommandTimeout = commandTimeout.Value; + } + } + + return config; + } + + /// + /// Checks tables existence + /// + /// + /// Returns true if the tables to check exist in the database or the check list is empty. + /// + protected bool CheckTables(TContext context) + { + if (this.TablesToCheck == null || !this.TablesToCheck.Any()) + return true; + + var existingTableNames = new List(context.Database.SqlQuery("SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_type = 'BASE TABLE'")); + var result = existingTableNames.Intersect(this.TablesToCheck, StringComparer.InvariantCultureIgnoreCase).Count() == 0; + return !result; + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Data/Setup/MigratorUtils.cs b/src/Libraries/SmartStore.Data/Setup/MigratorUtils.cs index a89c9fcb13..4ed6df0496 100644 --- a/src/Libraries/SmartStore.Data/Setup/MigratorUtils.cs +++ b/src/Libraries/SmartStore.Data/Setup/MigratorUtils.cs @@ -3,172 +3,170 @@ using System.Data.Entity.Migrations; using System.IO; using System.Linq; -using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; using SmartStore.Data.Migrations; namespace SmartStore.Data.Setup { - internal static class MigratorUtils - { - private static readonly Regex _migrationIdPattern = new Regex(@"\d{15}_.+"); - private const string _migrationTypeFormat = "{0}.{1}, {2}"; - private const string _automaticMigration = "AutomaticMigration"; - - /// - /// Creates a full type instance for the migration id by using the current migrations namespace - /// ie: SmartStore.Data.Migrations.34589734533_Initial - /// - /// The migrator context - /// The migration id from the migrations list of the migrator - /// The full DbMigration instance - public static DbMigration CreateMigrationInstanceByMigrationId(string migrationId, DbMigrationsConfiguration config) - { - string migrationTypeName = - string.Format(_migrationTypeFormat, - config.MigrationsNamespace, - GetMigrationClassName(migrationId), - config.MigrationsAssembly.FullName); - - return CreateTypeInstance(migrationTypeName); - } - - /// - /// Checks if the migration id is valid - /// - /// The migration id from the migrations list of the migrator - /// true if valid, otherwise false - /// - /// This snippet has been copied from the EntityFramework source (http://entityframework.codeplex.com/) - /// - public static bool IsValidMigrationId(string migrationId) - { - if (string.IsNullOrWhiteSpace(migrationId)) - return false; - - return _migrationIdPattern.IsMatch(migrationId) || migrationId == DbMigrator.InitialDatabase; - } - - /// - /// Checks if the the migration id belongs to an automatic migration - /// - /// The migration id from the migrations list of the migrator - /// true if automatic, otherwise false - /// - /// This snippet has been copied from the EntityFramework source (http://entityframework.codeplex.com/) - /// - public static bool IsAutomaticMigration(string migrationId) - { - if (string.IsNullOrWhiteSpace(migrationId)) - return false; - - return migrationId.EndsWith(_automaticMigration, StringComparison.Ordinal); - } - - /// - /// Gets the ClassName from a migration id - /// - /// The migration id from the migrations list of the migrator - /// The class name for this migration id - /// - /// This snippet has been copied from the EntityFramework source (http://entityframework.codeplex.com/) - /// - public static string GetMigrationClassName(string migrationId) - { - if (string.IsNullOrWhiteSpace(migrationId)) - return string.Empty; - - return migrationId.Substring(16); - } - - /// - /// Creates a new instance of a typename - /// - /// The type of the return instance - /// The full name (including assembly and namespaces) of the type to create - /// - /// A new instance of the type if it is (or boxable to) , - /// otherwise the default of - /// - private static TType CreateTypeInstance(string typeName) where TType : class - { - Type classType = Type.GetType(typeName, false); - - if (classType == null) - return default(TType); - - object newType = Activator.CreateInstance(classType); - - return newType as TType; - } - - - public static void ExecutePendingResourceMigrations(string resPath, SmartObjectContext dbContext) - { - Guard.NotNull(dbContext, nameof(dbContext)); - - string headPath = Path.Combine(resPath, "head.txt"); - if (!File.Exists(headPath)) - return; - - string resHead = File.ReadAllText(headPath).Trim(); - if (!MigratorUtils.IsValidMigrationId(resHead)) - return; - - var migrator = new DbMigrator(new MigrationsConfiguration()); - var migrations = GetPendingResourceMigrations(migrator, resHead); - - foreach (var id in migrations) - { - if (IsAutomaticMigration(id)) - continue; - - if (!IsValidMigrationId(id)) - continue; - - // Resolve and instantiate the DbMigration instance from the assembly - var migration = CreateMigrationInstanceByMigrationId(id, migrator.Configuration); - - var provider = migration as ILocaleResourcesProvider; - if (provider == null) - continue; - - var builder = new LocaleResourcesBuilder(); - provider.MigrateLocaleResources(builder); - - var resEntries = builder.Build(); - var resMigrator = new LocaleResourcesMigrator(dbContext); - resMigrator.Migrate(resEntries); - } - } - - private static IEnumerable GetPendingResourceMigrations(DbMigrator migrator, string resHead) - { - var local = migrator.GetLocalMigrations(); - var atHead = false; - - if (local.Last().IsCaseInsensitiveEqual(resHead)) - yield break; - - foreach (var id in local) - { - if (!atHead) - { - if (!id.IsCaseInsensitiveEqual(resHead)) - { - continue; - } - else - { - atHead = true; - continue; - } - } - - yield return id; - } - } - - } + internal static class MigratorUtils + { + private static readonly Regex _migrationIdPattern = new Regex(@"\d{15}_.+"); + private const string _migrationTypeFormat = "{0}.{1}, {2}"; + private const string _automaticMigration = "AutomaticMigration"; + + /// + /// Creates a full type instance for the migration id by using the current migrations namespace + /// ie: SmartStore.Data.Migrations.34589734533_Initial + /// + /// The migrator context + /// The migration id from the migrations list of the migrator + /// The full DbMigration instance + public static DbMigration CreateMigrationInstanceByMigrationId(string migrationId, DbMigrationsConfiguration config) + { + string migrationTypeName = + string.Format(_migrationTypeFormat, + config.MigrationsNamespace, + GetMigrationClassName(migrationId), + config.MigrationsAssembly.FullName); + + return CreateTypeInstance(migrationTypeName); + } + + /// + /// Checks if the migration id is valid + /// + /// The migration id from the migrations list of the migrator + /// true if valid, otherwise false + /// + /// This snippet has been copied from the EntityFramework source (http://entityframework.codeplex.com/) + /// + public static bool IsValidMigrationId(string migrationId) + { + if (string.IsNullOrWhiteSpace(migrationId)) + return false; + + return _migrationIdPattern.IsMatch(migrationId) || migrationId == DbMigrator.InitialDatabase; + } + + /// + /// Checks if the the migration id belongs to an automatic migration + /// + /// The migration id from the migrations list of the migrator + /// true if automatic, otherwise false + /// + /// This snippet has been copied from the EntityFramework source (http://entityframework.codeplex.com/) + /// + public static bool IsAutomaticMigration(string migrationId) + { + if (string.IsNullOrWhiteSpace(migrationId)) + return false; + + return migrationId.EndsWith(_automaticMigration, StringComparison.Ordinal); + } + + /// + /// Gets the ClassName from a migration id + /// + /// The migration id from the migrations list of the migrator + /// The class name for this migration id + /// + /// This snippet has been copied from the EntityFramework source (http://entityframework.codeplex.com/) + /// + public static string GetMigrationClassName(string migrationId) + { + if (string.IsNullOrWhiteSpace(migrationId)) + return string.Empty; + + return migrationId.Substring(16); + } + + /// + /// Creates a new instance of a typename + /// + /// The type of the return instance + /// The full name (including assembly and namespaces) of the type to create + /// + /// A new instance of the type if it is (or boxable to) , + /// otherwise the default of + /// + private static TType CreateTypeInstance(string typeName) where TType : class + { + Type classType = Type.GetType(typeName, false); + + if (classType == null) + return default(TType); + + object newType = Activator.CreateInstance(classType); + + return newType as TType; + } + + + public static void ExecutePendingResourceMigrations(string resPath, SmartObjectContext dbContext) + { + Guard.NotNull(dbContext, nameof(dbContext)); + + string headPath = Path.Combine(resPath, "head.txt"); + if (!File.Exists(headPath)) + return; + + string resHead = File.ReadAllText(headPath).Trim(); + if (!MigratorUtils.IsValidMigrationId(resHead)) + return; + + var migrator = new DbMigrator(new MigrationsConfiguration()); + var migrations = GetPendingResourceMigrations(migrator, resHead); + + foreach (var id in migrations) + { + if (IsAutomaticMigration(id)) + continue; + + if (!IsValidMigrationId(id)) + continue; + + // Resolve and instantiate the DbMigration instance from the assembly + var migration = CreateMigrationInstanceByMigrationId(id, migrator.Configuration); + + var provider = migration as ILocaleResourcesProvider; + if (provider == null) + continue; + + var builder = new LocaleResourcesBuilder(); + provider.MigrateLocaleResources(builder); + + var resEntries = builder.Build(); + var resMigrator = new LocaleResourcesMigrator(dbContext); + resMigrator.Migrate(resEntries); + } + } + + private static IEnumerable GetPendingResourceMigrations(DbMigrator migrator, string resHead) + { + var local = migrator.GetLocalMigrations(); + var atHead = false; + + if (local.Last().IsCaseInsensitiveEqual(resHead)) + yield break; + + foreach (var id in local) + { + if (!atHead) + { + if (!id.IsCaseInsensitiveEqual(resHead)) + { + continue; + } + else + { + atHead = true; + continue; + } + } + + yield return id; + } + } + + } } diff --git a/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.ActivityLogTypes.cs b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.ActivityLogTypes.cs new file mode 100644 index 0000000000..44ae838d99 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.ActivityLogTypes.cs @@ -0,0 +1,417 @@ +using System.Collections.Generic; +using SmartStore.Core.Domain.Logging; + +namespace SmartStore.Data.Setup +{ + public abstract partial class InvariantSeedData + { + public IList ActivityLogTypes() + { + var entities = new List() + { + //admin area activities + new ActivityLogType + { + SystemKeyword = "AddNewCategory", + Enabled = true, + Name = "Add a new category" + }, + new ActivityLogType + { + SystemKeyword = "AddNewCheckoutAttribute", + Enabled = true, + Name = "Add a new checkout attribute" + }, + new ActivityLogType + { + SystemKeyword = "AddNewCustomer", + Enabled = true, + Name = "Add a new customer" + }, + new ActivityLogType + { + SystemKeyword = "AddNewCustomerRole", + Enabled = true, + Name = "Add a new customer role" + }, + new ActivityLogType + { + SystemKeyword = "AddNewDiscount", + Enabled = true, + Name = "Add a new discount" + }, + new ActivityLogType + { + SystemKeyword = "AddNewGiftCard", + Enabled = true, + Name = "Add a new gift card" + }, + new ActivityLogType + { + SystemKeyword = "AddNewManufacturer", + Enabled = true, + Name = "Add a new manufacturer" + }, + new ActivityLogType + { + SystemKeyword = "AddNewProduct", + Enabled = true, + Name = "Add a new product" + }, + new ActivityLogType + { + SystemKeyword = "AddNewProductAttribute", + Enabled = true, + Name = "Add a new product attribute" + }, + new ActivityLogType + { + SystemKeyword = "AddNewSetting", + Enabled = true, + Name = "Add a new setting" + }, + new ActivityLogType + { + SystemKeyword = "AddNewSpecAttribute", + Enabled = true, + Name = "Add a new specification attribute" + }, + new ActivityLogType + { + SystemKeyword = "AddNewWidget", + Enabled = true, + Name = "Add a new widget" + }, + new ActivityLogType + { + SystemKeyword = "DeleteCategory", + Enabled = true, + Name = "Delete category" + }, + new ActivityLogType + { + SystemKeyword = "DeleteCheckoutAttribute", + Enabled = true, + Name = "Delete a checkout attribute" + }, + new ActivityLogType + { + SystemKeyword = "DeleteCustomer", + Enabled = true, + Name = "Delete a customer" + }, + new ActivityLogType + { + SystemKeyword = "DeleteCustomerRole", + Enabled = true, + Name = "Delete a customer role" + }, + new ActivityLogType + { + SystemKeyword = "DeleteDiscount", + Enabled = true, + Name = "Delete a discount" + }, + new ActivityLogType + { + SystemKeyword = "DeleteGiftCard", + Enabled = true, + Name = "Delete a gift card" + }, + new ActivityLogType + { + SystemKeyword = "DeleteManufacturer", + Enabled = true, + Name = "Delete a manufacturer" + }, + new ActivityLogType + { + SystemKeyword = "DeleteProduct", + Enabled = true, + Name = "Delete a product" + }, + new ActivityLogType + { + SystemKeyword = "DeleteProductAttribute", + Enabled = true, + Name = "Delete a product attribute" + }, + new ActivityLogType + { + SystemKeyword = "DeleteReturnRequest", + Enabled = true, + Name = "Delete a return request" + }, + new ActivityLogType + { + SystemKeyword = "DeleteSetting", + Enabled = true, + Name = "Delete a setting" + }, + new ActivityLogType + { + SystemKeyword = "DeleteSpecAttribute", + Enabled = true, + Name = "Delete a specification attribute" + }, + new ActivityLogType + { + SystemKeyword = "DeleteWidget", + Enabled = true, + Name = "Delete a widget" + }, + new ActivityLogType + { + SystemKeyword = "EditCategory", + Enabled = true, + Name = "Edit category" + }, + new ActivityLogType + { + SystemKeyword = "EditCheckoutAttribute", + Enabled = true, + Name = "Edit a checkout attribute" + }, + new ActivityLogType + { + SystemKeyword = "EditCustomer", + Enabled = true, + Name = "Edit a customer" + }, + new ActivityLogType + { + SystemKeyword = "EditCustomerRole", + Enabled = true, + Name = "Edit a customer role" + }, + new ActivityLogType + { + SystemKeyword = "EditDiscount", + Enabled = true, + Name = "Edit a discount" + }, + new ActivityLogType + { + SystemKeyword = "EditGiftCard", + Enabled = true, + Name = "Edit a gift card" + }, + new ActivityLogType + { + SystemKeyword = "EditManufacturer", + Enabled = true, + Name = "Edit a manufacturer" + }, + new ActivityLogType + { + SystemKeyword = "EditProduct", + Enabled = true, + Name = "Edit a product" + }, + new ActivityLogType + { + SystemKeyword = "EditProductAttribute", + Enabled = true, + Name = "Edit a product attribute" + }, + new ActivityLogType + { + SystemKeyword = "EditPromotionProviders", + Enabled = true, + Name = "Edit promotion providers" + }, + new ActivityLogType + { + SystemKeyword = "EditReturnRequest", + Enabled = true, + Name = "Edit a return request" + }, + new ActivityLogType + { + SystemKeyword = "EditSettings", + Enabled = true, + Name = "Edit setting(s)" + }, + new ActivityLogType + { + SystemKeyword = "EditSpecAttribute", + Enabled = true, + Name = "Edit a specification attribute" + }, + new ActivityLogType + { + SystemKeyword = "EditWidget", + Enabled = true, + Name = "Edit a widget" + }, + new ActivityLogType + { + SystemKeyword = "EditThemeVars", + Enabled = true, + Name = "Edit theme variables" + }, + new ActivityLogType + { + SystemKeyword = "EditOrder", + Enabled = true, + Name = "Edit an order" + }, + new ActivityLogType + { + SystemKeyword = "ResetThemeVars", + Enabled = true, + Name = "Reset theme variables to defaults" + }, + new ActivityLogType + { + SystemKeyword = "ImportThemeVars", + Enabled = true, + Name = "Import theme variables" + }, + new ActivityLogType + { + SystemKeyword = "ExportThemeVars", + Enabled = true, + Name = "Export theme variables" + }, + + //public store activities + new ActivityLogType + { + SystemKeyword = "PublicStore.ViewCategory", + Enabled = false, + Name = "Public store. View a category" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.ViewManufacturer", + Enabled = false, + Name = "Public store. View a manufacturer" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.ViewProduct", + Enabled = false, + Name = "Public store. View a product" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.PlaceOrder", + Enabled = false, + Name = "Public store. Place an order" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.SendPM", + Enabled = false, + Name = "Public store. Send PM" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.ContactUs", + Enabled = false, + Name = "Public store. Use contact us form" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.AddToCompareList", + Enabled = false, + Name = "Public store. Add to compare list" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.AddToShoppingCart", + Enabled = false, + Name = "Public store. Add to shopping cart" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.AddToWishlist", + Enabled = false, + Name = "Public store. Add to wishlist" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.Login", + Enabled = false, + Name = "Public store. Login" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.Logout", + Enabled = false, + Name = "Public store. Logout" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.AddProductReview", + Enabled = false, + Name = "Public store. Add product review" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.AddNewsComment", + Enabled = false, + Name = "Public store. Add news comment" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.AddBlogComment", + Enabled = false, + Name = "Public store. Add blog comment" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.AddForumTopic", + Enabled = false, + Name = "Public store. Add forum topic" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.EditForumTopic", + Enabled = false, + Name = "Public store. Edit forum topic" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.DeleteForumTopic", + Enabled = false, + Name = "Public store. Delete forum topic" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.AddForumPost", + Enabled = false, + Name = "Public store. Add forum post" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.EditForumPost", + Enabled = false, + Name = "Public store. Edit forum post" + }, + new ActivityLogType + { + SystemKeyword = "PublicStore.DeleteForumPost", + Enabled = false, + Name = "Public store. Delete forum post" + }, + //new ActivityLogType + // { + // SystemKeyword = "EditThemeVars", + // Enabled = false, + // Name = "Edit theme variable" + // }, + //new ActivityLogType + // { + // SystemKeyword = "ResetThemeVars", + // Enabled = false, + // Name = "Reset theme variable" + // }, + }; + + this.Alter(entities); + return entities; + } + } +} diff --git a/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Categories.cs b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Categories.cs new file mode 100644 index 0000000000..1b27139014 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Categories.cs @@ -0,0 +1,426 @@ +using System.Collections.Generic; +using System.Linq; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Discounts; +using SmartStore.Rules; +using SmartStore.Rules.Domain; + +namespace SmartStore.Data.Setup +{ + public abstract partial class InvariantSeedData + { + public IList CategoriesFirstLevel() + { + var imagesPath = _sampleImagesPath; + var gridOrLinesTemplate = CategoryTemplates().Where(pt => pt.ViewPath == "CategoryTemplate.ProductsInGridOrLines").FirstOrDefault(); + + var categoryFurniture = new Category + { + Name = "Furniture", + Alias = "Furniture", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/furniture.jpg"), + Published = true, + DisplayOrder = 1, + MetaTitle = "Furniture", + ShowOnHomePage = true + }; + + var categoryApple = new Category + { + Name = "Apple", + Alias = "Apple", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/apple.png"), + Published = true, + DisplayOrder = 1, + MetaTitle = "Apple", + ShowOnHomePage = true + }; + + var categorySports = new Category + { + Name = "Sports", + Alias = "Sports", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/sports.jpg"), + Published = true, + DisplayOrder = 1, + MetaTitle = "Sports", + ShowOnHomePage = true + }; + + var categoryBooks = new Category + { + Name = "Books", + Alias = "Books", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/emblem_library.png", GetSeName("Books")), + Published = true, + DisplayOrder = 1, + MetaTitle = "Books" + }; + + var categoryFashion = new Category + { + Name = "Fashion", + Alias = "Fashion", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/fashion.jpg"), + Published = true, + DisplayOrder = 2, + MetaTitle = "Fashion", + ShowOnHomePage = true, + BadgeText = "NEW", + BadgeStyle = 4 + }; + + var categoryGaming = new Category + { + Name = "Gaming", + Alias = "Gaming", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("product/ps4_bundle_minecraft.jpg", GetSeName("Gaming")), + Published = true, + DisplayOrder = 3, + ShowOnHomePage = true, + MetaTitle = "Gaming" + }; + + var categoryDigitalDownloads = new Category + { + Name = "Digital Products", + Alias = "Digital Products", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/digitalproducts.jpg"), + Published = true, + DisplayOrder = 6, + MetaTitle = "Digital Products", + ShowOnHomePage = true + }; + + var categoryGiftCards = new Category + { + Name = "Gift Cards", + Alias = "Gift Cards", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/gift-cards.png"), + Published = true, + DisplayOrder = 12, + MetaTitle = "Gift cards", + ShowOnHomePage = true, + }; + + var categoryWatches = new Category + { + Name = "Watches", + Alias = "Watches", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/watches.png"), + Published = true, + DisplayOrder = 10, + MetaTitle = "Watches", + ShowOnHomePage = true + }; + + var categorySale = new Category + { + Name = "Sale", + Alias = "Sale", + MetaTitle = "Sale", + CategoryTemplateId = gridOrLinesTemplate.Id, + Published = true, + DisplayOrder = 99, + ShowOnHomePage = false, + BadgeText = "%", + BadgeStyle = 5 + }; + + var saleRuleSet = _ctx.Set().FirstOrDefault(x => x.Scope == RuleScope.Product && x.Name == "Sale"); + if (saleRuleSet != null) + { + categorySale.RuleSets.Add(saleRuleSet); + } + + //var categoryComputers = new Category + //{ + // Name = "Computers", + // Alias = "Computers", + // CategoryTemplateId = categoryTemplateInGridAndLines.Id, + // Picture = CreatePicture("category/computers.png"), + // Published = true, + // DisplayOrder = 2, + // MetaTitle = "Computers" + //}; + + //var categoryCellPhones = new Category + //{ + // Name = "Cell phones", + // Alias = "Cell phones", + // CategoryTemplateId = categoryTemplateInGridAndLines.Id, + // //ParentCategoryId = categoryElectronics.Id, + // Picture = CreatePicture("category/cellphone.png"), + // Published = true, + // DisplayOrder = 4, + // MetaTitle = "Cell phones" + //}; + + var entities = new List + { + categoryApple, categorySports, categoryBooks, categoryFurniture, categoryDigitalDownloads, + categoryGaming, categoryGiftCards, categoryFashion, categoryWatches, categorySale + }; + + Alter(entities); + return entities; + } + + public IList CategoriesSecondLevel() + { + var imagesPath = _sampleImagesPath; + var gridOrLinesTemplate = CategoryTemplates().Where(pt => pt.ViewPath == "CategoryTemplate.ProductsInGridOrLines").FirstOrDefault(); + var categories = _ctx.Set().ToList().ToDictionarySafe(x => x.Alias, x => x); + var discounts = _ctx.Set().Where(x => x.DiscountTypeId == (int)DiscountType.AssignedToCategories).ToList(); + + var categoryFashionJackets = new Category + { + Name = "Jackets", + Alias = "Jackets", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/jackets.jpg"), + Published = true, + ParentCategoryId = categories["Fashion"].Id, + DisplayOrder = 1, + MetaTitle = "Jackets", + ShowOnHomePage = true + }; + + var categoryFashionShoes = new Category + { + Name = "Shoes", + Alias = "Shoes", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/shoes.png"), + Published = true, + ParentCategoryId = categories["Fashion"].Id, + DisplayOrder = 1, + MetaTitle = "Shoes", + ShowOnHomePage = true + }; + + var categoryFashionSunglasses = new Category + { + Name = "Sunglasses", + Alias = "Sunglasses", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/glasses.png"), + Published = true, + ParentCategoryId = categories["Fashion"].Id, + DisplayOrder = 1, + MetaTitle = "Sunglasses", + ShowOnHomePage = true + }; + + var categoryFashionTrousers = new Category + { + Name = "Trousers", + Alias = "Trousers", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/trousers.png"), + Published = true, + ParentCategoryId = categories["Fashion"].Id, + DisplayOrder = 1, + MetaTitle = "Trousers", + ShowOnHomePage = true + }; + if (discounts.Any()) + { + categoryFashionTrousers.HasDiscountsApplied = true; + categoryFashionTrousers.AppliedDiscounts.Add(discounts.First()); + } + + var categorySportsGolf = new Category + { + Name = "Golf", + Alias = "Golf", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/golf.jpg"), + Published = true, + ParentCategoryId = categories["Sports"].Id, + DisplayOrder = 1, + MetaTitle = "Golf", + ShowOnHomePage = true + }; + + var categorySportsSoccer = new Category + { + Name = "Soccer", + Alias = "Soccer", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/soccer.png"), + Published = true, + ParentCategoryId = categories["Sports"].Id, + DisplayOrder = 1, + MetaTitle = "Soccer", + ShowOnHomePage = true + }; + + var categorySportsBasketball = new Category + { + Name = "Basketball", + Alias = "Basketball", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/basketball.png"), + Published = true, + ParentCategoryId = categories["Sports"].Id, + DisplayOrder = 1, + MetaTitle = "Basketball", + ShowOnHomePage = true + }; + + var categoryBooksSpiegel = new Category + { + Name = "SPIEGEL-Bestseller", + Alias = "SPIEGEL-Bestseller", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/0000930_spiegel-bestseller.png", GetSeName("SPIEGEL-Bestseller")), + Published = true, + ParentCategoryId = categories["Books"].Id, + DisplayOrder = 1, + MetaTitle = "SPIEGEL-Bestseller" + }; + + var categoryBooksCookAndEnjoy = new Category + { + Name = "Cook and enjoy", + Alias = "Cook and enjoy", + CategoryTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("category/0000936_kochen-geniesen.jpeg", GetSeName("Cook and enjoy")), + Published = true, + ParentCategoryId = categories["Books"].Id, + DisplayOrder = 2, + MetaTitle = "Cook and enjoy" + }; + + var categoryGamingAccessories = new Category + { + Name = "Gaming Accessories", + Alias = "Gaming Accessories", + CategoryTemplateId = gridOrLinesTemplate.Id, + ParentCategoryId = categories["Gaming"].Id, + MediaFile = CreatePicture("category/gaming_accessories.png"), + Published = true, + DisplayOrder = 2, + MetaTitle = "Gaming Accessories" + }; + + var categoryGamingGames = new Category + { + Name = "Games", + Alias = "Games", + CategoryTemplateId = gridOrLinesTemplate.Id, + ParentCategoryId = categories["Gaming"].Id, + MediaFile = CreatePicture("category/games.jpg"), + Published = true, + DisplayOrder = 3, + MetaTitle = "Games" + }; + + var categoryFurnitureSofas = new Category + { + Name = "Sofas", + Alias = "Sofas", + CategoryTemplateId = gridOrLinesTemplate.Id, + ParentCategoryId = categories["Furniture"].Id, + MediaFile = CreatePicture("category/sofas.jpg"), + Published = true, + DisplayOrder = 3, + MetaTitle = "Sofas" + }; + + var categoryFurnitureChairs = new Category + { + Name = "Chairs", + Alias = "Chairs", + CategoryTemplateId = gridOrLinesTemplate.Id, + ParentCategoryId = categories["Furniture"].Id, + MediaFile = CreatePicture("category/furniture.jpg"), + Published = true, + DisplayOrder = 3, + MetaTitle = "Chairs" + }; + + var categoryFurnitureTables = new Category + { + Name = "Tables", + Alias = "Tables", + CategoryTemplateId = gridOrLinesTemplate.Id, + ParentCategoryId = categories["Furniture"].Id, + MediaFile = CreatePicture("category/tables.jpg"), + Published = true, + DisplayOrder = 3, + MetaTitle = "Tables" + }; + + //var categoryFashionLeatherJackets = new Category + //{ + // Name = "Leather jackets", + // Alias = "Leather jackets", + // CategoryTemplateId = categoryTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("category/leather_jackets.jpg"), + // Published = true, + // ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Fashion").First().Id, + // DisplayOrder = 1, + // MetaTitle = "Leather jackets", + // ShowOnHomePage = true + //}; + + //var categoryFashionTracksuits = new Category + //{ + // Name = "Tracksuits", + // Alias = "Tracksuits", + // CategoryTemplateId = categoryTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("category/tracksuit.png"), + // Published = true, + // ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Fashion").First().Id, + // DisplayOrder = 1, + // MetaTitle = "Tracksuits", + // ShowOnHomePage = true + //}; + + //var categoryDesktops = new Category + //{ + // Name = "Desktops", + // Alias = "Desktops", + // CategoryTemplateId = categoryTemplateInGridAndLines.Id, + // ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Computers").First().Id, + // Picture = CreatePicture("category/desktops.png"), + // Published = true, + // DisplayOrder = 1, + // MetaTitle = "Desktops" + //}; + + //var categoryNotebooks = new Category + //{ + // Name = "Notebooks", + // Alias = "Notebooks", + // CategoryTemplateId = categoryTemplateInGridAndLines.Id, + // ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Computers").First().Id, + // Picture = CreatePicture("category/notebooks.png"), + // Published = true, + // DisplayOrder = 2, + // MetaTitle = "Notebooks" + //}; + + var entities = new List + { + categorySportsSoccer, categorySportsBasketball, categorySportsGolf, categoryBooksSpiegel, + categoryBooksCookAndEnjoy, categoryGamingAccessories, categoryGamingGames, categoryFashionJackets, categoryFashionShoes, + categoryFashionTrousers, categoryFashionSunglasses, categoryFurnitureChairs, categoryFurnitureSofas, categoryFurnitureTables + }; + + Alter(entities); + return entities; + } + } +} diff --git a/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Countries.cs b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Countries.cs new file mode 100644 index 0000000000..6d977a5751 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Countries.cs @@ -0,0 +1,3402 @@ +using System.Collections.Generic; +using SmartStore.Core.Domain.Directory; + +namespace SmartStore.Data.Setup +{ + public abstract partial class InvariantSeedData + { + public IList Countries() + { + var cUsa = new Country + { + Name = "United States", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "US", + ThreeLetterIsoCode = "USA", + NumericIsoCode = 840, + SubjectToVat = false, + DisplayOrder = 1, + Published = true, + }; + + #region US Regions + + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "AA (Armed Forces Americas)", + Abbreviation = "AA", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "AE (Armed Forces Europe)", + Abbreviation = "AE", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Alabama", + Abbreviation = "AL", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Alaska", + Abbreviation = "AK", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "American Samoa", + Abbreviation = "AS", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "AP (Armed Forces Pacific)", + Abbreviation = "AP", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Arizona", + Abbreviation = "AZ", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Arkansas", + Abbreviation = "AR", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "California", + Abbreviation = "CA", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Colorado", + Abbreviation = "CO", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Connecticut", + Abbreviation = "CT", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Delaware", + Abbreviation = "DE", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "District of Columbia", + Abbreviation = "DC", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Federated States of Micronesia", + Abbreviation = "FM", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Florida", + Abbreviation = "FL", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Georgia", + Abbreviation = "GA", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Guam", + Abbreviation = "GU", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Hawaii", + Abbreviation = "HI", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Idaho", + Abbreviation = "ID", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Illinois", + Abbreviation = "IL", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Indiana", + Abbreviation = "IN", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Iowa", + Abbreviation = "IA", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Kansas", + Abbreviation = "KS", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Kentucky", + Abbreviation = "KY", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Louisiana", + Abbreviation = "LA", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Maine", + Abbreviation = "ME", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Marshall Islands", + Abbreviation = "MH", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Maryland", + Abbreviation = "MD", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Massachusetts", + Abbreviation = "MA", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Michigan", + Abbreviation = "MI", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Minnesota", + Abbreviation = "MN", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Mississippi", + Abbreviation = "MS", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Missouri", + Abbreviation = "MO", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Montana", + Abbreviation = "MT", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Nebraska", + Abbreviation = "NE", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Nevada", + Abbreviation = "NV", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "New Hampshire", + Abbreviation = "NH", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "New Jersey", + Abbreviation = "NJ", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "New Mexico", + Abbreviation = "NM", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "New York", + Abbreviation = "NY", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "North Carolina", + Abbreviation = "NC", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "North Dakota", + Abbreviation = "ND", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Northern Mariana Islands", + Abbreviation = "MP", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Ohio", + Abbreviation = "OH", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Oklahoma", + Abbreviation = "OK", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Oregon", + Abbreviation = "OR", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Palau", + Abbreviation = "PW", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Pennsylvania", + Abbreviation = "PA", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Puerto Rico", + Abbreviation = "PR", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Rhode Island", + Abbreviation = "RI", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "South Carolina", + Abbreviation = "SC", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "South Dakota", + Abbreviation = "SD", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Tennessee", + Abbreviation = "TN", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Texas", + Abbreviation = "TX", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Utah", + Abbreviation = "UT", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Vermont", + Abbreviation = "VT", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Virgin Islands", + Abbreviation = "VI", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Virginia", + Abbreviation = "VA", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Washington", + Abbreviation = "WA", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "West Virginia", + Abbreviation = "WV", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Wisconsin", + Abbreviation = "WI", + Published = true, + DisplayOrder = 1, + }); + cUsa.StateProvinces.Add(new StateProvince() + { + Name = "Wyoming", + Abbreviation = "WY", + Published = true, + DisplayOrder = 1, + }); + + #endregion + + var cCanada = new Country + { + Name = "Canada", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CA", + ThreeLetterIsoCode = "CAN", + NumericIsoCode = 124, + SubjectToVat = false, + DisplayOrder = 2, + Published = true, + }; + + #region CA Regions + + cCanada.StateProvinces.Add(new StateProvince() + { + Name = "Alberta", + Abbreviation = "AB", + Published = true, + DisplayOrder = 1, + }); + cCanada.StateProvinces.Add(new StateProvince() + { + Name = "British Columbia", + Abbreviation = "BC", + Published = true, + DisplayOrder = 1, + }); + cCanada.StateProvinces.Add(new StateProvince() + { + Name = "Manitoba", + Abbreviation = "MB", + Published = true, + DisplayOrder = 1, + }); + cCanada.StateProvinces.Add(new StateProvince() + { + Name = "New Brunswick", + Abbreviation = "NB", + Published = true, + DisplayOrder = 1, + }); + cCanada.StateProvinces.Add(new StateProvince() + { + Name = "Newfoundland and Labrador", + Abbreviation = "NL", + Published = true, + DisplayOrder = 1, + }); + cCanada.StateProvinces.Add(new StateProvince() + { + Name = "Northwest Territories", + Abbreviation = "NT", + Published = true, + DisplayOrder = 1, + }); + cCanada.StateProvinces.Add(new StateProvince() + { + Name = "Nova Scotia", + Abbreviation = "NS", + Published = true, + DisplayOrder = 1, + }); + cCanada.StateProvinces.Add(new StateProvince() + { + Name = "Nunavut", + Abbreviation = "NU", + Published = true, + DisplayOrder = 1, + }); + cCanada.StateProvinces.Add(new StateProvince() + { + Name = "Ontario", + Abbreviation = "ON", + Published = true, + DisplayOrder = 1, + }); + cCanada.StateProvinces.Add(new StateProvince() + { + Name = "Prince Edward Island", + Abbreviation = "PE", + Published = true, + DisplayOrder = 1, + }); + cCanada.StateProvinces.Add(new StateProvince() + { + Name = "Quebec", + Abbreviation = "QC", + Published = true, + DisplayOrder = 1, + }); + cCanada.StateProvinces.Add(new StateProvince() + { + Name = "Saskatchewan", + Abbreviation = "SK", + Published = true, + DisplayOrder = 1, + }); + cCanada.StateProvinces.Add(new StateProvince() + { + Name = "Yukon Territory", + Abbreviation = "YU", + Published = true, + DisplayOrder = 1, + }); + #endregion + + var entities = new List() + { + new Country() + { + Name = "Germany", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "DE", + ThreeLetterIsoCode = "DEU", + NumericIsoCode = 276, + SubjectToVat = true, + DisplayOrder = -10, + Published = true + }, + + new Country + { + Name = "Austria", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AT", + ThreeLetterIsoCode = "AUT", + NumericIsoCode = 40, + SubjectToVat = true, + DisplayOrder = -5, + Published = true + }, + new Country + { + Name = "Switzerland", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CH", + ThreeLetterIsoCode = "CHE", + NumericIsoCode = 756, + SubjectToVat = false, + DisplayOrder = -1, + Published = true + }, + cUsa, + cCanada, + + //other countries + new Country + { + Name = "Argentina", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AR", + ThreeLetterIsoCode = "ARG", + NumericIsoCode = 32, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Armenia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AM", + ThreeLetterIsoCode = "ARM", + NumericIsoCode = 51, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Aruba", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AW", + ThreeLetterIsoCode = "ABW", + NumericIsoCode = 533, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Australia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AU", + ThreeLetterIsoCode = "AUS", + NumericIsoCode = 36, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Azerbaijan", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AZ", + ThreeLetterIsoCode = "AZE", + NumericIsoCode = 31, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Bahamas", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BS", + ThreeLetterIsoCode = "BHS", + NumericIsoCode = 44, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Bangladesh", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BD", + ThreeLetterIsoCode = "BGD", + NumericIsoCode = 50, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Belarus", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BY", + ThreeLetterIsoCode = "BLR", + NumericIsoCode = 112, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Belgium", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BE", + ThreeLetterIsoCode = "BEL", + NumericIsoCode = 56, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Belize", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BZ", + ThreeLetterIsoCode = "BLZ", + NumericIsoCode = 84, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Bermuda", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BM", + ThreeLetterIsoCode = "BMU", + NumericIsoCode = 60, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Bolivia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BO", + ThreeLetterIsoCode = "BOL", + NumericIsoCode = 68, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Bosnia and Herzegowina", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BA", + ThreeLetterIsoCode = "BIH", + NumericIsoCode = 70, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Brazil", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BR", + ThreeLetterIsoCode = "BRA", + NumericIsoCode = 76, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Bulgaria", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BG", + ThreeLetterIsoCode = "BGR", + NumericIsoCode = 100, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Cayman Islands", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "KY", + ThreeLetterIsoCode = "CYM", + NumericIsoCode = 136, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Chile", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CL", + ThreeLetterIsoCode = "CHL", + NumericIsoCode = 152, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "China", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CN", + ThreeLetterIsoCode = "CHN", + NumericIsoCode = 156, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Colombia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CO", + ThreeLetterIsoCode = "COL", + NumericIsoCode = 170, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Costa Rica", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CR", + ThreeLetterIsoCode = "CRI", + NumericIsoCode = 188, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Croatia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "HR", + ThreeLetterIsoCode = "HRV", + NumericIsoCode = 191, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Cuba", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CU", + ThreeLetterIsoCode = "CUB", + NumericIsoCode = 192, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Cyprus", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CY", + ThreeLetterIsoCode = "CYP", + NumericIsoCode = 196, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Czech Republic", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CZ", + ThreeLetterIsoCode = "CZE", + NumericIsoCode = 203, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Denmark", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "DK", + ThreeLetterIsoCode = "DNK", + NumericIsoCode = 208, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Dominican Republic", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "DO", + ThreeLetterIsoCode = "DOM", + NumericIsoCode = 214, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Ecuador", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "EC", + ThreeLetterIsoCode = "ECU", + NumericIsoCode = 218, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Egypt", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "EG", + ThreeLetterIsoCode = "EGY", + NumericIsoCode = 818, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Finland", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "FI", + ThreeLetterIsoCode = "FIN", + NumericIsoCode = 246, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "France", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "FR", + ThreeLetterIsoCode = "FRA", + NumericIsoCode = 250, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Georgia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GE", + ThreeLetterIsoCode = "GEO", + NumericIsoCode = 268, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Gibraltar", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GI", + ThreeLetterIsoCode = "GIB", + NumericIsoCode = 292, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Greece", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GR", + ThreeLetterIsoCode = "GRC", + NumericIsoCode = 300, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Guatemala", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GT", + ThreeLetterIsoCode = "GTM", + NumericIsoCode = 320, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Hong Kong", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "HK", + ThreeLetterIsoCode = "HKG", + NumericIsoCode = 344, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Hungary", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "HU", + ThreeLetterIsoCode = "HUN", + NumericIsoCode = 348, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "India", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "IN", + ThreeLetterIsoCode = "IND", + NumericIsoCode = 356, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Indonesia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "ID", + ThreeLetterIsoCode = "IDN", + NumericIsoCode = 360, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Ireland", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "IE", + ThreeLetterIsoCode = "IRL", + NumericIsoCode = 372, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Israel", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "IL", + ThreeLetterIsoCode = "ISR", + NumericIsoCode = 376, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Italy", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "IT", + ThreeLetterIsoCode = "ITA", + NumericIsoCode = 380, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Jamaica", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "JM", + ThreeLetterIsoCode = "JAM", + NumericIsoCode = 388, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Japan", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "JP", + ThreeLetterIsoCode = "JPN", + NumericIsoCode = 392, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Jordan", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "JO", + ThreeLetterIsoCode = "JOR", + NumericIsoCode = 400, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Kazakhstan", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "KZ", + ThreeLetterIsoCode = "KAZ", + NumericIsoCode = 398, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Korea, Democratic People's Republic of", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "KP", + ThreeLetterIsoCode = "PRK", + NumericIsoCode = 408, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Kuwait", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "KW", + ThreeLetterIsoCode = "KWT", + NumericIsoCode = 414, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Malaysia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MY", + ThreeLetterIsoCode = "MYS", + NumericIsoCode = 458, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Mexico", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MX", + ThreeLetterIsoCode = "MEX", + NumericIsoCode = 484, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Netherlands", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "NL", + ThreeLetterIsoCode = "NLD", + NumericIsoCode = 528, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "New Zealand", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "NZ", + ThreeLetterIsoCode = "NZL", + NumericIsoCode = 554, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Norway", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "NO", + ThreeLetterIsoCode = "NOR", + NumericIsoCode = 578, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Pakistan", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "PK", + ThreeLetterIsoCode = "PAK", + NumericIsoCode = 586, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Paraguay", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "PY", + ThreeLetterIsoCode = "PRY", + NumericIsoCode = 600, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Peru", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "PE", + ThreeLetterIsoCode = "PER", + NumericIsoCode = 604, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Philippines", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "PH", + ThreeLetterIsoCode = "PHL", + NumericIsoCode = 608, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Poland", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "PL", + ThreeLetterIsoCode = "POL", + NumericIsoCode = 616, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Portugal", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "PT", + ThreeLetterIsoCode = "PRT", + NumericIsoCode = 620, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Puerto Rico", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "PR", + ThreeLetterIsoCode = "PRI", + NumericIsoCode = 630, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Qatar", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "QA", + ThreeLetterIsoCode = "QAT", + NumericIsoCode = 634, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Romania", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "RO", + ThreeLetterIsoCode = "ROM", + NumericIsoCode = 642, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Russia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "RU", + ThreeLetterIsoCode = "RUS", + NumericIsoCode = 643, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Saudi Arabia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SA", + ThreeLetterIsoCode = "SAU", + NumericIsoCode = 682, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Singapore", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SG", + ThreeLetterIsoCode = "SGP", + NumericIsoCode = 702, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Slovakia (Slovak Republic)", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SK", + ThreeLetterIsoCode = "SVK", + NumericIsoCode = 703, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Slovenia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SI", + ThreeLetterIsoCode = "SVN", + NumericIsoCode = 705, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "South Africa", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "ZA", + ThreeLetterIsoCode = "ZAF", + NumericIsoCode = 710, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Spain", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "ES", + ThreeLetterIsoCode = "ESP", + NumericIsoCode = 724, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Sweden", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SE", + ThreeLetterIsoCode = "SWE", + NumericIsoCode = 752, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Taiwan", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TW", + ThreeLetterIsoCode = "TWN", + NumericIsoCode = 158, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Thailand", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TH", + ThreeLetterIsoCode = "THA", + NumericIsoCode = 764, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Turkey", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TR", + ThreeLetterIsoCode = "TUR", + NumericIsoCode = 792, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Ukraine", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "UA", + ThreeLetterIsoCode = "UKR", + NumericIsoCode = 804, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "United Arab Emirates", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AE", + ThreeLetterIsoCode = "ARE", + NumericIsoCode = 784, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "United Kingdom", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GB", + ThreeLetterIsoCode = "GBR", + NumericIsoCode = 826, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "United States minor outlying islands", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "UM", + ThreeLetterIsoCode = "UMI", + NumericIsoCode = 581, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Uruguay", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "UY", + ThreeLetterIsoCode = "URY", + NumericIsoCode = 858, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Uzbekistan", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "UZ", + ThreeLetterIsoCode = "UZB", + NumericIsoCode = 860, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Venezuela", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "VE", + ThreeLetterIsoCode = "VEN", + NumericIsoCode = 862, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Serbia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "RS", + ThreeLetterIsoCode = "SRB", + NumericIsoCode = 688, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Afghanistan", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AF", + ThreeLetterIsoCode = "AFG", + NumericIsoCode = 4, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Albania", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AL", + ThreeLetterIsoCode = "ALB", + NumericIsoCode = 8, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Algeria", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "DZ", + ThreeLetterIsoCode = "DZA", + NumericIsoCode = 12, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "American Samoa", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AS", + ThreeLetterIsoCode = "ASM", + NumericIsoCode = 16, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Andorra", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AD", + ThreeLetterIsoCode = "AND", + NumericIsoCode = 20, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Angola", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AO", + ThreeLetterIsoCode = "AGO", + NumericIsoCode = 24, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Anguilla", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AI", + ThreeLetterIsoCode = "AIA", + NumericIsoCode = 660, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Antarctica", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AQ", + ThreeLetterIsoCode = "ATA", + NumericIsoCode = 10, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Antigua and Barbuda", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AG", + ThreeLetterIsoCode = "ATG", + NumericIsoCode = 28, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Bahrain", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BH", + ThreeLetterIsoCode = "BHR", + NumericIsoCode = 48, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Barbados", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BB", + ThreeLetterIsoCode = "BRB", + NumericIsoCode = 52, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Benin", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BJ", + ThreeLetterIsoCode = "BEN", + NumericIsoCode = 204, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Bhutan", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BT", + ThreeLetterIsoCode = "BTN", + NumericIsoCode = 64, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Botswana", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BW", + ThreeLetterIsoCode = "BWA", + NumericIsoCode = 72, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Bouvet Island", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BV", + ThreeLetterIsoCode = "BVT", + NumericIsoCode = 74, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "British Indian Ocean Territory", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "IO", + ThreeLetterIsoCode = "IOT", + NumericIsoCode = 86, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Brunei Darussalam", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BN", + ThreeLetterIsoCode = "BRN", + NumericIsoCode = 96, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Burkina Faso", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BF", + ThreeLetterIsoCode = "BFA", + NumericIsoCode = 854, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Burundi", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "BI", + ThreeLetterIsoCode = "BDI", + NumericIsoCode = 108, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Cambodia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "KH", + ThreeLetterIsoCode = "KHM", + NumericIsoCode = 116, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Cameroon", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CM", + ThreeLetterIsoCode = "CMR", + NumericIsoCode = 120, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Cape Verde", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CV", + ThreeLetterIsoCode = "CPV", + NumericIsoCode = 132, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Central African Republic", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CF", + ThreeLetterIsoCode = "CAF", + NumericIsoCode = 140, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Chad", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TD", + ThreeLetterIsoCode = "TCD", + NumericIsoCode = 148, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Christmas Island", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CX", + ThreeLetterIsoCode = "CXR", + NumericIsoCode = 162, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Cocos (Keeling) Islands", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CC", + ThreeLetterIsoCode = "CCK", + NumericIsoCode = 166, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Comoros", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "KM", + ThreeLetterIsoCode = "COM", + NumericIsoCode = 174, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Congo", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CG", + ThreeLetterIsoCode = "COG", + NumericIsoCode = 178, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Cook Islands", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CK", + ThreeLetterIsoCode = "COK", + NumericIsoCode = 184, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Cote D'Ivoire", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "CI", + ThreeLetterIsoCode = "CIV", + NumericIsoCode = 384, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Djibouti", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "DJ", + ThreeLetterIsoCode = "DJI", + NumericIsoCode = 262, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Dominica", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "DM", + ThreeLetterIsoCode = "DMA", + NumericIsoCode = 212, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "El Salvador", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SV", + ThreeLetterIsoCode = "SLV", + NumericIsoCode = 222, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Equatorial Guinea", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GQ", + ThreeLetterIsoCode = "GNQ", + NumericIsoCode = 226, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Eritrea", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "ER", + ThreeLetterIsoCode = "ERI", + NumericIsoCode = 232, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Estonia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "EE", + ThreeLetterIsoCode = "EST", + NumericIsoCode = 233, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Ethiopia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "ET", + ThreeLetterIsoCode = "ETH", + NumericIsoCode = 231, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Falkland Islands (Malvinas)", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "FK", + ThreeLetterIsoCode = "FLK", + NumericIsoCode = 238, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Faroe Islands", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "FO", + ThreeLetterIsoCode = "FRO", + NumericIsoCode = 234, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Fiji", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "FJ", + ThreeLetterIsoCode = "FJI", + NumericIsoCode = 242, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "French Guiana", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GF", + ThreeLetterIsoCode = "GUF", + NumericIsoCode = 254, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "French Polynesia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "PF", + ThreeLetterIsoCode = "PYF", + NumericIsoCode = 258, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "French Southern Territories", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TF", + ThreeLetterIsoCode = "ATF", + NumericIsoCode = 260, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Gabon", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GA", + ThreeLetterIsoCode = "GAB", + NumericIsoCode = 266, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Gambia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GM", + ThreeLetterIsoCode = "GMB", + NumericIsoCode = 270, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Ghana", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GH", + ThreeLetterIsoCode = "GHA", + NumericIsoCode = 288, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Greenland", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GL", + ThreeLetterIsoCode = "GRL", + NumericIsoCode = 304, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Grenada", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GD", + ThreeLetterIsoCode = "GRD", + NumericIsoCode = 308, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Guadeloupe", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GP", + ThreeLetterIsoCode = "GLP", + NumericIsoCode = 312, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Guam", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GU", + ThreeLetterIsoCode = "GUM", + NumericIsoCode = 316, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Guinea", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GN", + ThreeLetterIsoCode = "GIN", + NumericIsoCode = 324, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Guinea-bissau", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GW", + ThreeLetterIsoCode = "GNB", + NumericIsoCode = 624, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Guyana", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GY", + ThreeLetterIsoCode = "GUY", + NumericIsoCode = 328, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Haiti", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "HT", + ThreeLetterIsoCode = "HTI", + NumericIsoCode = 332, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Heard and Mc Donald Islands", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "HM", + ThreeLetterIsoCode = "HMD", + NumericIsoCode = 334, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Honduras", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "HN", + ThreeLetterIsoCode = "HND", + NumericIsoCode = 340, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Iceland", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "IS", + ThreeLetterIsoCode = "ISL", + NumericIsoCode = 352, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Iran (Islamic Republic of)", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "IR", + ThreeLetterIsoCode = "IRN", + NumericIsoCode = 364, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Iraq", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "IQ", + ThreeLetterIsoCode = "IRQ", + NumericIsoCode = 368, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Kenya", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "KE", + ThreeLetterIsoCode = "KEN", + NumericIsoCode = 404, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Kiribati", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "KI", + ThreeLetterIsoCode = "KIR", + NumericIsoCode = 296, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Korea", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "KR", + ThreeLetterIsoCode = "KOR", + NumericIsoCode = 410, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Kyrgyzstan", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "KG", + ThreeLetterIsoCode = "KGZ", + NumericIsoCode = 417, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Lao People's Democratic Republic", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "LA", + ThreeLetterIsoCode = "LAO", + NumericIsoCode = 418, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Latvia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "LV", + ThreeLetterIsoCode = "LVA", + NumericIsoCode = 428, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Lebanon", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "LB", + ThreeLetterIsoCode = "LBN", + NumericIsoCode = 422, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Lesotho", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "LS", + ThreeLetterIsoCode = "LSO", + NumericIsoCode = 426, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Liberia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "LR", + ThreeLetterIsoCode = "LBR", + NumericIsoCode = 430, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Libyan Arab Jamahiriya", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "LY", + ThreeLetterIsoCode = "LBY", + NumericIsoCode = 434, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Liechtenstein", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "LI", + ThreeLetterIsoCode = "LIE", + NumericIsoCode = 438, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Lithuania", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "LT", + ThreeLetterIsoCode = "LTU", + NumericIsoCode = 440, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Luxembourg", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "LU", + ThreeLetterIsoCode = "LUX", + NumericIsoCode = 442, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Macau", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MO", + ThreeLetterIsoCode = "MAC", + NumericIsoCode = 446, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Macedonia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MK", + ThreeLetterIsoCode = "MKD", + NumericIsoCode = 807, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Madagascar", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MG", + ThreeLetterIsoCode = "MDG", + NumericIsoCode = 450, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Malawi", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MW", + ThreeLetterIsoCode = "MWI", + NumericIsoCode = 454, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Maldives", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MV", + ThreeLetterIsoCode = "MDV", + NumericIsoCode = 462, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Mali", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "ML", + ThreeLetterIsoCode = "MLI", + NumericIsoCode = 466, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Malta", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MT", + ThreeLetterIsoCode = "MLT", + NumericIsoCode = 470, + SubjectToVat = true, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Marshall Islands", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MH", + ThreeLetterIsoCode = "MHL", + NumericIsoCode = 584, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Martinique", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MQ", + ThreeLetterIsoCode = "MTQ", + NumericIsoCode = 474, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Mauritania", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MR", + ThreeLetterIsoCode = "MRT", + NumericIsoCode = 478, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Mauritius", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MU", + ThreeLetterIsoCode = "MUS", + NumericIsoCode = 480, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Mayotte", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "YT", + ThreeLetterIsoCode = "MYT", + NumericIsoCode = 175, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Micronesia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "FM", + ThreeLetterIsoCode = "FSM", + NumericIsoCode = 583, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Moldova", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MD", + ThreeLetterIsoCode = "MDA", + NumericIsoCode = 498, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Monaco", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MC", + ThreeLetterIsoCode = "MCO", + NumericIsoCode = 492, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Mongolia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MN", + ThreeLetterIsoCode = "MNG", + NumericIsoCode = 496, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Montenegro", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "ME", + ThreeLetterIsoCode = "MNE", + NumericIsoCode = 499, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Montserrat", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MS", + ThreeLetterIsoCode = "MSR", + NumericIsoCode = 500, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Morocco", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MA", + ThreeLetterIsoCode = "MAR", + NumericIsoCode = 504, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Mozambique", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MZ", + ThreeLetterIsoCode = "MOZ", + NumericIsoCode = 508, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Myanmar", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MM", + ThreeLetterIsoCode = "MMR", + NumericIsoCode = 104, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Namibia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "NA", + ThreeLetterIsoCode = "NAM", + NumericIsoCode = 516, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Nauru", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "NR", + ThreeLetterIsoCode = "NRU", + NumericIsoCode = 520, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Nepal", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "NP", + ThreeLetterIsoCode = "NPL", + NumericIsoCode = 524, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Netherlands Antilles", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "AN", + ThreeLetterIsoCode = "ANT", + NumericIsoCode = 530, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "New Caledonia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "NC", + ThreeLetterIsoCode = "NCL", + NumericIsoCode = 540, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Nicaragua", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "NI", + ThreeLetterIsoCode = "NIC", + NumericIsoCode = 558, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Niger", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "NE", + ThreeLetterIsoCode = "NER", + NumericIsoCode = 562, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Nigeria", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "NG", + ThreeLetterIsoCode = "NGA", + NumericIsoCode = 566, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Niue", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "NU", + ThreeLetterIsoCode = "NIU", + NumericIsoCode = 570, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Norfolk Island", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "NF", + ThreeLetterIsoCode = "NFK", + NumericIsoCode = 574, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Northern Mariana Islands", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "MP", + ThreeLetterIsoCode = "MNP", + NumericIsoCode = 580, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Oman", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "OM", + ThreeLetterIsoCode = "OMN", + NumericIsoCode = 512, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Palau", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "PW", + ThreeLetterIsoCode = "PLW", + NumericIsoCode = 585, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Panama", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "PA", + ThreeLetterIsoCode = "PAN", + NumericIsoCode = 591, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Papua New Guinea", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "PG", + ThreeLetterIsoCode = "PNG", + NumericIsoCode = 598, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Pitcairn", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "PN", + ThreeLetterIsoCode = "PCN", + NumericIsoCode = 612, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Reunion", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "RE", + ThreeLetterIsoCode = "REU", + NumericIsoCode = 638, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Rwanda", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "RW", + ThreeLetterIsoCode = "RWA", + NumericIsoCode = 646, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Saint Kitts and Nevis", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "KN", + ThreeLetterIsoCode = "KNA", + NumericIsoCode = 659, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Saint Lucia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "LC", + ThreeLetterIsoCode = "LCA", + NumericIsoCode = 662, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Saint Vincent and the Grenadines", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "VC", + ThreeLetterIsoCode = "VCT", + NumericIsoCode = 670, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Samoa", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "WS", + ThreeLetterIsoCode = "WSM", + NumericIsoCode = 882, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "San Marino", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SM", + ThreeLetterIsoCode = "SMR", + NumericIsoCode = 674, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Sao Tome and Principe", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "ST", + ThreeLetterIsoCode = "STP", + NumericIsoCode = 678, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Senegal", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SN", + ThreeLetterIsoCode = "SEN", + NumericIsoCode = 686, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Seychelles", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SC", + ThreeLetterIsoCode = "SYC", + NumericIsoCode = 690, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Sierra Leone", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SL", + ThreeLetterIsoCode = "SLE", + NumericIsoCode = 694, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Solomon Islands", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SB", + ThreeLetterIsoCode = "SLB", + NumericIsoCode = 90, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Somalia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SO", + ThreeLetterIsoCode = "SOM", + NumericIsoCode = 706, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "South Georgia & South Sandwich Islands", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "GS", + ThreeLetterIsoCode = "SGS", + NumericIsoCode = 239, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Sri Lanka", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "LK", + ThreeLetterIsoCode = "LKA", + NumericIsoCode = 144, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "St. Helena", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SH", + ThreeLetterIsoCode = "SHN", + NumericIsoCode = 654, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "St. Pierre and Miquelon", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "PM", + ThreeLetterIsoCode = "SPM", + NumericIsoCode = 666, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Sudan", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SD", + ThreeLetterIsoCode = "SDN", + NumericIsoCode = 736, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Suriname", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SR", + ThreeLetterIsoCode = "SUR", + NumericIsoCode = 740, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Svalbard and Jan Mayen Islands", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SJ", + ThreeLetterIsoCode = "SJM", + NumericIsoCode = 744, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Swaziland", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SZ", + ThreeLetterIsoCode = "SWZ", + NumericIsoCode = 748, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Syrian Arab Republic", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "SY", + ThreeLetterIsoCode = "SYR", + NumericIsoCode = 760, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Tajikistan", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TJ", + ThreeLetterIsoCode = "TJK", + NumericIsoCode = 762, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Tanzania", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TZ", + ThreeLetterIsoCode = "TZA", + NumericIsoCode = 834, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Togo", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TG", + ThreeLetterIsoCode = "TGO", + NumericIsoCode = 768, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Tokelau", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TK", + ThreeLetterIsoCode = "TKL", + NumericIsoCode = 772, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Tonga", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TO", + ThreeLetterIsoCode = "TON", + NumericIsoCode = 776, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Trinidad and Tobago", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TT", + ThreeLetterIsoCode = "TTO", + NumericIsoCode = 780, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Tunisia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TN", + ThreeLetterIsoCode = "TUN", + NumericIsoCode = 788, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Turkmenistan", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TM", + ThreeLetterIsoCode = "TKM", + NumericIsoCode = 795, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Turks and Caicos Islands", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TC", + ThreeLetterIsoCode = "TCA", + NumericIsoCode = 796, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Tuvalu", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "TV", + ThreeLetterIsoCode = "TUV", + NumericIsoCode = 798, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Uganda", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "UG", + ThreeLetterIsoCode = "UGA", + NumericIsoCode = 800, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Vanuatu", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "VU", + ThreeLetterIsoCode = "VUT", + NumericIsoCode = 548, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Vatican City State (Holy See)", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "VA", + ThreeLetterIsoCode = "VAT", + NumericIsoCode = 336, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Viet Nam", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "VN", + ThreeLetterIsoCode = "VNM", + NumericIsoCode = 704, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Virgin Islands (British)", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "VG", + ThreeLetterIsoCode = "VGB", + NumericIsoCode = 92, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Virgin Islands (U.S.)", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "VI", + ThreeLetterIsoCode = "VIR", + NumericIsoCode = 850, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Wallis and Futuna Islands", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "WF", + ThreeLetterIsoCode = "WLF", + NumericIsoCode = 876, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Western Sahara", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "EH", + ThreeLetterIsoCode = "ESH", + NumericIsoCode = 732, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Yemen", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "YE", + ThreeLetterIsoCode = "YEM", + NumericIsoCode = 887, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Zambia", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "ZM", + ThreeLetterIsoCode = "ZMB", + NumericIsoCode = 894, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + new Country + { + Name = "Zimbabwe", + AllowsBilling = true, + AllowsShipping = true, + TwoLetterIsoCode = "ZW", + ThreeLetterIsoCode = "ZWE", + NumericIsoCode = 716, + SubjectToVat = false, + DisplayOrder = 100, + Published = true + }, + }; + this.Alter(entities); + return entities; + } + } +} diff --git a/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Manufacturers.cs b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Manufacturers.cs new file mode 100644 index 0000000000..51afc48fcd --- /dev/null +++ b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Manufacturers.cs @@ -0,0 +1,343 @@ +using System.Collections.Generic; +using System.Linq; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Discounts; + +namespace SmartStore.Data.Setup +{ + public abstract partial class InvariantSeedData + { + public IList Manufacturers() + { + var imagesPath = _sampleImagesPath; + var gridOrLinesTemplate = ManufacturerTemplates().Where(x => x.ViewPath == "ManufacturerTemplate.ProductsInGridOrLines").FirstOrDefault(); + var discounts = _ctx.Set().Where(x => x.DiscountTypeId == (int)DiscountType.AssignedToManufacturers).ToList(); + + var manufacturerJackWolfskin = new Manufacturer + { + Name = "Jack-Wolfskin", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/Jack_Wolfskin.png"), + Published = true, + DisplayOrder = 1 + }; + + var manufacturerEASports = new Manufacturer + { + Name = "EA Sports", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/EA_Sports.png"), + Published = true, + DisplayOrder = 1 + }; + + var manufacturerWarnerHome = new Manufacturer + { + Name = "Warner Home Video Games", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/wb.png", GetSeName("Warner Home Video Games")), + Published = true, + DisplayOrder = 1 + }; + + var manufacturerBreitling = new Manufacturer + { + Name = "Breitling", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/breitling.png"), + Published = true, + DisplayOrder = 1 + }; + if (discounts.Any()) + { + manufacturerBreitling.HasDiscountsApplied = true; + manufacturerBreitling.AppliedDiscounts.Add(discounts.First()); + } + + var manufacturerTissot = new Manufacturer + { + Name = "Tissot", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/Tissot.png"), + Published = true, + DisplayOrder = 1 + }; + + var manufacturerSeiko = new Manufacturer + { + Name = "Seiko", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/seiko.png"), + Published = true, + DisplayOrder = 1 + }; + if (discounts.Any()) + { + manufacturerSeiko.HasDiscountsApplied = true; + manufacturerSeiko.AppliedDiscounts.Add(discounts.First()); + } + + var manufacturerTitleist = new Manufacturer + { + Name = "Titleist", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/titleist.png"), + Published = true, + DisplayOrder = 1 + }; + + var manufacturerPuma = new Manufacturer + { + Name = "Puma", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/puma.jpg"), + Published = true, + DisplayOrder = 1 + }; + + var manufacturerNike = new Manufacturer + { + Name = "Nike", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/nike.png"), + Published = true, + DisplayOrder = 1 + }; + + var manufacturerAdidas = new Manufacturer + { + Name = "Adidas", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/adidas.png"), + Published = true, + DisplayOrder = 1 + }; + + var manufacturerRayban = new Manufacturer + { + Name = "Ray-Ban", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/ray-ban.jpg"), + Published = true, + DisplayOrder = 1 + }; + + var manufacturerOakley = new Manufacturer + { + Name = "Oakley", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/oakley.png"), + Published = true, + DisplayOrder = 1 + }; + + var manufacturerApple = new Manufacturer + { + Name = "Apple", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/apple.png"), + Published = true, + DisplayOrder = 1 + }; + + var manufacturerMicrosoft = new Manufacturer + { + Name = "Microsoft", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/microsoft-icon.png", GetSeName("Microsoft")), + Published = true, + DisplayOrder = 6 + }; + + var manufacturerFestina = new Manufacturer + { + Name = "Festina", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/festina.png"), + Published = true, + DisplayOrder = 17 + }; + + var manufacturerCertina = new Manufacturer + { + Name = "Certina", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/certina.png"), + Published = true, + DisplayOrder = 18 + }; + + var manufacturerSony = new Manufacturer + { + Name = "Sony", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/sony.png"), + Published = true, + DisplayOrder = 19 + }; + + var manufacturerUbisoft = new Manufacturer + { + Name = "Ubisoft", + ManufacturerTemplateId = gridOrLinesTemplate.Id, + MediaFile = CreatePicture("brand/ubisoft.png"), + Published = true, + DisplayOrder = 20 + }; + + //var manufacturerMeyAndEdlich = new Manufacturer + //{ + // Name = "Mey-And-Edlich", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/MeyAndEdlich.jpg", GetSeName("Mey Edlich")), + // Published = true, + // DisplayOrder = 1 + //}; + + //var manufacturerWilson = new Manufacturer + //{ + // Name = "Wilson", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/wilson.png"), + // Published = true, + // DisplayOrder = 1 + //}; + //var manufacturerAndroid = new Manufacturer + //{ + // Name = "Android", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/android.png"), + // Published = true, + // DisplayOrder = 2 + //}; + + //var manufacturerLG = new Manufacturer + //{ + // Name = "LG", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/lg.png"), + // Published = true, + // DisplayOrder = 3 + //}; + + //var manufacturerDell = new Manufacturer + //{ + // Name = "Dell", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/dell.png"), + // Published = true, + // DisplayOrder = 4 + //}; + + //var manufacturerHP = new Manufacturer + //{ + // Name = "HP", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/hp.png"), + // Published = true, + // DisplayOrder = 5 + //}; + + //var manufacturerSamsung = new Manufacturer + //{ + // Name = "Samsung", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/samsung.png"), + // Published = true, + // DisplayOrder = 7 + //}; + + //var manufacturerAcer = new Manufacturer + //{ + // Name = "Acer", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/acer.jpg"), + // Published = true, + // DisplayOrder = 8 + //}; + + //var manufacturerTrekStor = new Manufacturer + //{ + // Name = "TrekStor", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/trekstor.png"), + // Published = true, + // DisplayOrder = 9 + //}; + + //var manufacturerWesternDigital = new Manufacturer + //{ + // Name = "Western Digital", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/westerndigital.png"), + // Published = true, + // DisplayOrder = 10 + //}; + + //var manufacturerMSI = new Manufacturer + //{ + // Name = "MSI", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/msi.png"), + // Published = true, + // DisplayOrder = 11 + //}; + + //var manufacturerCanon = new Manufacturer + //{ + // Name = "Canon", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/canon.png"), + // Published = true, + // DisplayOrder = 12 + //}; + + //var manufacturerCasio = new Manufacturer + //{ + // Name = "Casio", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/casio.png"), + // Published = true, + // DisplayOrder = 13 + //}; + + //var manufacturerPanasonic = new Manufacturer + //{ + // Name = "Panasonic", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/panasonic.png"), + // Published = true, + // DisplayOrder = 14 + //}; + + //var manufacturerBlackBerry = new Manufacturer + //{ + // Name = "BlackBerry", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/blackberry.png"), + // Published = true, + // DisplayOrder = 15 + //}; + + //var manufacturerHTC = new Manufacturer + //{ + // Name = "HTC", + // ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, + // MediaFile = CreatePicture("brand/htc.png"), + // Published = true, + // DisplayOrder = 16 + //}; + + var entities = new List + { + manufacturerEASports, manufacturerWarnerHome, manufacturerBreitling, manufacturerTissot, manufacturerSeiko, + manufacturerTitleist, manufacturerApple, manufacturerFestina, manufacturerCertina, + manufacturerSony, manufacturerUbisoft, manufacturerOakley, manufacturerRayban, manufacturerAdidas, + manufacturerPuma, manufacturerNike, manufacturerJackWolfskin, manufacturerMicrosoft, + }; + + Alter(entities); + return entities; + } + } +} diff --git a/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Products.cs b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Products.cs new file mode 100644 index 0000000000..1886ae9f64 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Products.cs @@ -0,0 +1,3878 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Directory; +using SmartStore.Core.Domain.Discounts; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.Tax; + +namespace SmartStore.Data.Setup +{ + public abstract partial class InvariantSeedData + { + public IList ProductTags() + { + #region tag apple + var productTagApple = new ProductTag + { + Name = "apple" + }; + + _ctx.Set().Where(pt => pt.MetaTitle == "iPhone Plus").First().ProductTags.Add(productTagApple); + + #endregion tag apple + + #region tag gift + var productTagGift = new ProductTag + { + Name = "gift" + }; + + _ctx.Set().Where(pt => pt.MetaTitle == "$10 Virtual Gift Card").First().ProductTags.Add(productTagGift); + _ctx.Set().Where(pt => pt.MetaTitle == "$25 Virtual Gift Card").First().ProductTags.Add(productTagGift); + _ctx.Set().Where(pt => pt.MetaTitle == "$50 Virtual Gift Card").First().ProductTags.Add(productTagGift); + _ctx.Set().Where(pt => pt.MetaTitle == "$100 Virtual Gift Card").First().ProductTags.Add(productTagGift); + + #endregion tag gift + + #region tag book + var productTagBook = new ProductTag + { + Name = "book" + }; + + _ctx.Set().Where(pt => pt.MetaTitle == "Überman: The novel").First().ProductTags.Add(productTagBook); + _ctx.Set().Where(pt => pt.MetaTitle == "Best Grilling Recipes").First().ProductTags.Add(productTagBook); + _ctx.Set().Where(pt => pt.MetaTitle == "Car of superlatives").First().ProductTags.Add(productTagBook); + _ctx.Set().Where(pt => pt.MetaTitle == "Picture Atlas Motorcycles").First().ProductTags.Add(productTagBook); + _ctx.Set().Where(pt => pt.MetaTitle == "The Car Book").First().ProductTags.Add(productTagBook); + _ctx.Set().Where(pt => pt.MetaTitle == "Fast Cars").First().ProductTags.Add(productTagBook); + _ctx.Set().Where(pt => pt.MetaTitle == "Motorcycle Adventures").First().ProductTags.Add(productTagBook); + + #endregion tag book + + #region tag cooking + var productTagCooking = new ProductTag + { + Name = "cooking" + }; + + _ctx.Set().Where(pt => pt.MetaTitle == "Überman: The novel").FirstOrDefault().ProductTags.Add(productTagCooking); + _ctx.Set().Where(pt => pt.MetaTitle == "Best Grilling Recipes").FirstOrDefault().ProductTags.Add(productTagCooking); + + #endregion tag cooking + + #region tag cars + var productTagCars = new ProductTag + { + Name = "cars" + }; + + _ctx.Set().Where(pt => pt.MetaTitle == "The Car Book").FirstOrDefault().ProductTags.Add(productTagCars); + _ctx.Set().Where(pt => pt.MetaTitle == "Fast Cars").FirstOrDefault().ProductTags.Add(productTagCars); + + #endregion tag cars + + #region tag motorbikes + var productTagMotorbikes = new ProductTag + { + Name = "motorbikes" + }; + + _ctx.Set().Where(pt => pt.MetaTitle == "Fast Cars").FirstOrDefault().ProductTags.Add(productTagMotorbikes); + _ctx.Set().Where(pt => pt.MetaTitle == "Motorcycle Adventures").FirstOrDefault().ProductTags.Add(productTagMotorbikes); + + #endregion tag motorbikes + + #region tag mp3 + var productTagMP3 = new ProductTag + { + Name = "mp3" + }; + + _ctx.Set().Where(pt => pt.MetaTitle == "Antonio Vivaldi: spring").FirstOrDefault().ProductTags.Add(productTagMP3); + _ctx.Set().Where(pt => pt.MetaTitle == "Ludwig van Beethoven: Für Elise").FirstOrDefault().ProductTags.Add(productTagMP3); + + #endregion tag mp3 + + #region tag download + var productTagDownload = new ProductTag + { + Name = "download" + }; + + _ctx.Set().Where(pt => pt.MetaTitle == "Antonio Vivaldi: spring").FirstOrDefault().ProductTags.Add(productTagDownload); + _ctx.Set().Where(pt => pt.MetaTitle == "Ludwig van Beethoven: Für Elise").FirstOrDefault().ProductTags.Add(productTagDownload); + + #endregion tag download + + #region Tag Watches + + var productTagWatches = new ProductTag + { + Name = "watches" + }; + + _ctx.Set().Where(pt => pt.MetaTitle == "Certina DS Podium Big Size").FirstOrDefault().ProductTags.Add(productTagWatches); + + #endregion Tag Watches + + #region Tag Furniture + + var productTagFurniture = new ProductTag + { + Name = "Furniture" + }; + + _ctx.Set().Where(x => x.ProductCategories.Any(x => x.Category.ParentCategoryId == 4)).Each(y => y.ProductTags.Add(productTagFurniture)); + + #endregion Tag Furniture + + var entities = new List + { + productTagGift, productTagBook, productTagCooking, productTagCars, productTagMotorbikes, + productTagMP3, productTagDownload, productTagFurniture + }; + + this.Alter(entities); + return entities; + } + + private List GetFashionProducts( + Dictionary categories, + Dictionary manufacturers, + Dictionary specAttributes) + { + var specialPriceEndDate = DateTime.UtcNow.AddMonths(1); + + var productTemplate = _ctx.Set().First(x => x.ViewPath == "Product"); + var firstDeliveryTime = _ctx.Set().First(x => x.DisplayOrder == 0); + var thirdDeliveryTime = _ctx.Set().First(x => x.DisplayOrder == 2); + + var specOptionCotton = _ctx.Set().First(x => x.DisplayOrder == 8).SpecificationAttributeOptions.First(x => x.DisplayOrder == 9); + var taxCategoryIdApparel = _ctx.Set().First(x => x.Name.Equals(TaxNameApparel)).Id; + + #region Category Shoes + + #region Jack Wolfskin COOGEE LOW + + var jackWolfskinCooGeeLow = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "COOGEE LOW M", + MetaTitle = "COOGEE LOW M", + ShortDescription = "MEN'S LEISURE SHOES", + FullDescription = "

You are always on the go: to the cinema, to the newly opened bar or to the next city festival. The stylish COOGEE XT LOW is THE shoe for your life in the city. Because it combines function with style.

", + Sku = "Wolfskin-4032541", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + Price = 69.90M, + OldPrice = 99.95M, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = firstDeliveryTime, + DisplayOrder = 5, + TaxCategoryId = taxCategoryIdApparel + }; + + AddProductPicture(jackWolfskinCooGeeLow, "product/wolfskin_shoes_coogee_1.png", "wolfskin-shoes-coogee-1"); + + jackWolfskinCooGeeLow.ProductCategories.Add(new ProductCategory { Category = categories["Shoes"], DisplayOrder = 1 }); + jackWolfskinCooGeeLow.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Jack-Wolfskin"], DisplayOrder = 1, }); + + #endregion Jack Wolfskin COOGEE LOW + + #region Adidas SUPERSTAR SCHUH + + var adidasSuperstarSchuh = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "SUPERSTAR SHOE", + MetaTitle = "SUPERSTAR SHOE", + ShortDescription = "THE STREETWEAR CLASSIC WITH THE SHELL TOE.", + FullDescription = "

The Adidas Superstar was first released in 1969 and soon lived up to its name. Today he is considered a street style legend. In this version, the shoe comes with a comfortable full grain leather upper. The look is perfected by the classic rubber shell toe for added durability.

", + Sku = "Adidas-C77124", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + Price = 99.95M, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = firstDeliveryTime, + DisplayOrder = 5, + TaxCategoryId = taxCategoryIdApparel + }; + + AddProductPicture(adidasSuperstarSchuh, "product/adidas_superstar_schuh_1.png", "adidas-superstar-schuh-1"); + + jackWolfskinCooGeeLow.ProductCategories.Add(new ProductCategory { Category = categories["Shoes"], DisplayOrder = 1 }); + jackWolfskinCooGeeLow.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Adidas"], DisplayOrder = 1, }); + + #endregion Adidas SUPERSTAR SCHUH + + #region Converse All Star + + var converseAllStar = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Converse All Star", + MetaTitle = "Converse All Star", + ShortDescription = "The classical sneaker!", + FullDescription = "

Since 1912 and to this day unrivalled: the converse All Star sneaker. A shoe for every occasion.

", + Sku = "Fashion-112355", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + ShowOnHomePage = true, + Price = 79.90M, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = firstDeliveryTime, + DisplayOrder = 1, + TaxCategoryId = taxCategoryIdApparel + }; + + AddProductPicture(converseAllStar, "product/allstar_converse.jpg", "allstar-converse"); + AddProductPicture(converseAllStar, "product/allstar_hi_charcoal.jpg", "allstar-converse-charcoal"); + AddProductPicture(converseAllStar, "product/allstar_hi_maroon.jpg", "allstar-converse-maroon"); + AddProductPicture(converseAllStar, "product/allstar_hi_navy.jpg", "allstar-converse-navy"); + AddProductPicture(converseAllStar, "product/allstar_hi_purple.jpg", "allstar-converse-purple"); + AddProductPicture(converseAllStar, "product/allstar_hi_white.jpg", "allstar-converse-white"); + + converseAllStar.ProductCategories.Add(new ProductCategory { Category = categories["Shoes"], DisplayOrder = 1 }); + + converseAllStar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) + }); + + #endregion Converse All Star + + #endregion Category Shoes + + #region Category Jackets + + #region Ladies Jacket + + var ladiesJacket = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Ladies Sports Jacket", + MetaTitle = "Ladies Sports Jacket", + FullDescription = "

Lightweight wind and water repellent fabric, lining of soft single jersey knit cuffs on arm and waistband. 2 side pockets with zipper, hood in slightly waisted cut.

    Material: 100% polyamideLining: 65% polyester, 35% cottonLining 2: 100% polyester.
", + Sku = "Fashion-JN1107", + ManufacturerPartNumber = "JN1107", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + Price = 55.00M, + OldPrice = 60.00M, + ProductCost = 20.00M, + SpecialPrice = 52.99M, + SpecialPriceStartDateTimeUtc = new DateTime(2017, 5, 1, 0, 0, 0), + SpecialPriceEndDateTimeUtc = specialPriceEndDate, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = firstDeliveryTime, + DisplayOrder = 2, + TaxCategoryId = taxCategoryIdApparel + }; + + AddProductPicture(ladiesJacket, "product/ladies_jacket_silver.jpg", "ladies-jacket-silver"); + AddProductPicture(ladiesJacket, "product/ladies_jacket_black.jpg", "ladies-jacket-black"); + AddProductPicture(ladiesJacket, "product/ladies_jacket_red.jpg", "ladies-jacket-red"); + AddProductPicture(ladiesJacket, "product/ladies_jacket_orange.jpg", "ladies-jacket-orange"); + AddProductPicture(ladiesJacket, "product/ladies_jacket_green.jpg", "ladies-jacket-green"); + AddProductPicture(ladiesJacket, "product/ladies_jacket_blue.jpg", "ladies-jacket-blue"); + AddProductPicture(ladiesJacket, "product/ladies_jacket_navy.jpg", "ladies-jacket-navy"); + + ladiesJacket.ProductCategories.Add(new ProductCategory { Category = categories["Jackets"], DisplayOrder = 1 }); + + ladiesJacket.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 11) + }); + + #endregion Ladies Jacket + + #region Jack Wolfskin KANUKA POINT + + var jackWolfskinKanukaPoint = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "KANUKA POINT JACKET M", + MetaTitle = "KANUKA POINT JACKET M", + ShortDescription = "SOFTSHELL JACKET MEN", + FullDescription = "

Sporty design for sporty tours: The KANUKA POINT likes to be on the move as much as you do. The softshell jacket is made of super-elastic and very breathable material that adapts to your every move on the road. With the KANUKA POINT, you can take every pass with ease, and you don't have to worry about your jacket even when you're scrambling to the top, because its material is very durable, even in windy conditions and light showers.

", + Sku = "jack-1305851", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + Price = 83.90M, + OldPrice = 119.95M, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = firstDeliveryTime, + DisplayOrder = 5, + TaxCategoryId = taxCategoryIdApparel + }; + + AddProductPicture(jackWolfskinKanukaPoint, "product/jack_wolfskin_kanuka_point_1.png"); + + jackWolfskinKanukaPoint.ProductCategories.Add(new ProductCategory { Category = categories["Jackets"], DisplayOrder = 1 }); + jackWolfskinKanukaPoint.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Jack-Wolfskin"], DisplayOrder = 1, }); + + #endregion Jack Wolfskin KANUKA POINT + + #endregion Category Jackets + + #region Category Sunglasses + + #region RayBan TopBar + + var rayBanTopBar = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Ray-Ban Top Bar RB 3183", + IsEsd = false, + ShortDescription = "The Ray-Ban Original Wayfarer is the most famous style in the history of sunglasses. With the original design from 1952 the Wayfarer is popular with celebrities, musicians, artists and fashion experts.", + FullDescription = "

The Ray-Ban ® RB3183 sunglasses give me their aerodynamic shape a reminiscence of speed.

A rectangular shape and the classic Ray-Ban logo imprinted on the straps characterize this light Halbrand model.

", + Sku = "P-3004", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Ray-Ban Top Bar RB 3183", + Price = 139M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategoryIdApparel + }; + + AddProductPicture(rayBanTopBar, "product/RayBanTopBar_1.jpg", "rayban-top-bar-1"); + AddProductPicture(rayBanTopBar, "product/RayBanTopBar_2.jpg", "rayban-top-bar-2"); + AddProductPicture(rayBanTopBar, "product/RayBanTopBar_3.jpg", "rayban-top-bar-3"); + + rayBanTopBar.ProductCategories.Add(new ProductCategory { Category = categories["Sunglasses"], DisplayOrder = 1 }); + rayBanTopBar.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Ray-Ban"], DisplayOrder = 1 }); + + #endregion RayBan TopBar + + #region ORIGINAL WAYFARER AT COLLECTION + + var originalWayfarer = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "ORIGINAL WAYFARER AT COLLECTION", + IsEsd = false, + ShortDescription = "The Ray-Ban Original Wayfarer is the most famous style in the history of sunglasses. With the original design from 1952 the Wayfarer is popular with celebrities, musicians, artists and fashion experts.", + FullDescription = "

Radar® EV Path™ PRIZM™ Road

A new milestone in the heritage of performance, Radar® EV takes breakthroughs of a revolutionary design even further with a taller lens that extends the upper field of view. From the comfort and protection of the O Matter® frame to the grip of its Unobtanium® components, this premium design builds on the legacy of Radar innovation and style.

Features

  • PRIZM™ is a revolutionary lens technology that fine-tunes vision for specific sports and environments. See what you’ve been missing. Click here to learn more about Prizm Lens Technology.
  • Path lenses enhance performance if traditional lenses touch your cheeks and help extend the upper field of view
  • Engineered for maximized airflow for optimal ventilation to keep you cool
  • Unobtanium® earsocks and nosepads keep glasses in place, increasing grip despite perspiration
  • Interchangeable Lenses let you change lenses in seconds to optimize vision in any sport environment
", + Sku = "P-3003", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "ORIGINAL WAYFARER AT COLLECTION", + Price = 149M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategoryIdApparel + }; + + AddProductPicture(originalWayfarer, "product/OriginalWayfarer_1.jpg", "wayfarer-blue-gray-classic-black-1"); + AddProductPicture(originalWayfarer, "product/OriginalWayfarer_2.jpg", "wayfarer-blue-gray-classic-black-2"); + AddProductPicture(originalWayfarer, "product/OriginalWayfarer_3.jpg", "wayfarer-gray-course-black"); + AddProductPicture(originalWayfarer, "product/OriginalWayfarer_4.jpg", "wayfarer-brown-course-havana"); + AddProductPicture(originalWayfarer, "product/OriginalWayfarer_5.jpg", "wayfarer-green-classic-havana-black"); + AddProductPicture(originalWayfarer, "product/OriginalWayfarer_6.jpg", "wayfarer-blue-gray-classic-black-3"); + + originalWayfarer.ProductCategories.Add(new ProductCategory { Category = categories["Sunglasses"], DisplayOrder = 1 }); + originalWayfarer.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Ray-Ban"], DisplayOrder = 1 }); + + #endregion ORIGINAL WAYFARER AT COLLECTION + + #region Radar EV Prizm Sports Sunglasses + + var radarSportsSunglasses = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Radar EV Prizm Sports Sunglasses", + IsEsd = false, + ShortDescription = "", + FullDescription = "

Radar® EV Path™ PRIZM™ Road

A new milestone in the heritage of performance, Radar® EV takes breakthroughs of a revolutionary design even further with a taller lens that extends the upper field of view. From the comfort and protection of the O Matter® frame to the grip of its Unobtanium® components, this premium design builds on the legacy of Radar innovation and style.

Features

  • PRIZM™ is a revolutionary lens technology that fine-tunes vision for specific sports and environments. See what you’ve been missing. Click here to learn more about Prizm Lens Technology.
  • Path lenses enhance performance if traditional lenses touch your cheeks and help extend the upper field of view
  • Engineered for maximized airflow for optimal ventilation to keep you cool
  • Unobtanium® earsocks and nosepads keep glasses in place, increasing grip despite perspiration
  • Interchangeable Lenses let you change lenses in seconds to optimize vision in any sport environment
", + Sku = "P-3001", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Radar EV Prizm Sports Sunglasses", + Price = 149M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategoryIdApparel + }; + + AddProductPicture(radarSportsSunglasses, "product/radar_ev_prizm.jpg"); + + radarSportsSunglasses.ProductCategories.Add(new ProductCategory { Category = categories["Sunglasses"], DisplayOrder = 1 }); + radarSportsSunglasses.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Oakley"], DisplayOrder = 1 }); + + #endregion Radar EV Prizm Sports Sunglasses + + #region Custom Flak Sunglasses + + var customFlakSunglasses = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Custom Flak Sunglasses", + IsEsd = false, + ShortDescription = "", + FullDescription = "

Radar® EV Path™ PRIZM™ Road

A new milestone in the heritage of performance, Radar® EV takes breakthroughs of a revolutionary design even further with a taller lens that extends the upper field of view. From the comfort and protection of the O Matter® frame to the grip of its Unobtanium® components, this premium design builds on the legacy of Radar innovation and style.

Features

  • PRIZM™ is a revolutionary lens technology that fine-tunes vision for specific sports and environments. See what you’ve been missing. Click here to learn more about Prizm Lens Technology.
  • Path lenses enhance performance if traditional lenses touch your cheeks and help extend the upper field of view
  • Engineered for maximized airflow for optimal ventilation to keep you cool
  • Unobtanium® earsocks and nosepads keep glasses in place, increasing grip despite perspiration
  • Interchangeable Lenses let you change lenses in seconds to optimize vision in any sport environment
", + Sku = "P-3002", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Custom Flak Sunglasses", + Price = 179M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategoryIdApparel + }; + + AddProductPicture(customFlakSunglasses, "product/CustomFlakSunglasses.jpg", "customflak"); + AddProductPicture(customFlakSunglasses, "product/CustomFlakSunglasses_black_white.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_matteblack_gray.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_matteblack_clear.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_matteblack_jadeiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_matteblack_positiverediridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_matteblack_rubyiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_matteblack_sapphireiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_matteblack_violetiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_matteblack_24kiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_matteblack_fireiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_orangeflare_24kiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_orangeflare_clear.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_orangeflare_fireiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_orangeflare_gray.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_orangeflare_jadeiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_orangeflare_positiverediridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_orangeflare_rubyiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_orangeflare_sapphireiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_orangeflare_violetiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_polishedwhite_24kiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_polishedwhite_clear.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_polishedwhite_fireiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_polishedwhite_gray.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_polishedwhite_jadeiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_polishedwhite_rubyiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_polishedwhite_sapphireiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_polishedwhite_violetiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_polishedwhite_positiverediridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_redline_24kiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_redline_clear.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_redline_fireiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_redline_gray.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_redline_jadeiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_redline_positiverediridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_redline_rubyiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_redline_sapphireiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_redline_violetiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_skyblue_24kiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_skyblue_clear.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_skyblue_fireiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_skyblue_gray.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_skyblue_jadeiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_skyblue_positiverediridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_skyblue_rubyiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_skyblue_sapphireiridium.jpg"); + AddProductPicture(customFlakSunglasses, "product/CustomFlak_skyblue_violetiridium.jpg"); + + customFlakSunglasses.ProductCategories.Add(new ProductCategory { Category = categories["Sunglasses"], DisplayOrder = 1 }); + customFlakSunglasses.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Oakley"], DisplayOrder = 1 }); + + #endregion Custom Flak Sunglasses + + #endregion Category Sunglasses + + #region Shirt Meccanica + + var shirtMeccanica = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Sleeveless shirt Meccanica", + MetaTitle = "Sleeveless shirt Meccanica", + ShortDescription = "Woman shirt with trendy imprint", + FullDescription = "

Also in summer, the Ducati goes with fashion style! With the sleeveless shirt Meccanica, every woman can express her passion for Ducati with a comfortable and versatile piece of clothing. The shirt is available in black and vintage red. It carries on the front the traditional lettering in plastisol print, which makes it even clearer and more radiant, while on the back in the neck area is the famous logo with the typical \"wings\" of the fifties.

", + Sku = "Fashion-987693502", + ManufacturerPartNumber = "987693502", + Gtin = "987693502", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + Price = 38.00M, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = firstDeliveryTime, + DisplayOrder = 4, + TaxCategoryId = taxCategoryIdApparel + }; + + AddProductPicture(shirtMeccanica, "product/shirt_meccanica_black_1.jpg", "shirt-meccanica-black-1"); + AddProductPicture(shirtMeccanica, "product/shirt_meccanica_black_2.jpg", "shirt-meccanica-black-2"); + AddProductPicture(shirtMeccanica, "product/shirt_meccanica_black_3.jpg", "shirt-meccanica-black-3"); + AddProductPicture(shirtMeccanica, "product/shirt_meccanica_red_1.jpg", "shirt-meccanica-red-1"); + AddProductPicture(shirtMeccanica, "product/shirt_meccanica_red_2.jpg", "shirt-meccanica-red-2"); + AddProductPicture(shirtMeccanica, "product/shirt_meccanica_red_3.jpg", "shirt-meccanica-red-3"); + AddProductPicture(shirtMeccanica, "product/shirt_meccanica_red_4.jpg", "shirt-meccanica-red-4"); + + shirtMeccanica.ProductCategories.Add(new ProductCategory { Category = categories["Fashion"], DisplayOrder = 1 }); + + shirtMeccanica.TierPrices.Add(new TierPrice + { + Quantity = 10, + Price = 36.00M + }); + + shirtMeccanica.TierPrices.Add(new TierPrice + { + Quantity = 50, + Price = 29.00M + }); + + shirtMeccanica.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) + }); + + #endregion Shirt Meccanica + + #region Clark Premium Blue Jeans + + var clarkPremiumBlueJeans = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Clark Premium Blue Jeans", + MetaTitle = "Clark Premium Blue Jeans", + ShortDescription = "Modern Jeans in Easy Comfort Fit", + FullDescription = "

Real five-pocket jeans by Joker with additional, set-up pocket. Thanks to easy comfort fit with normal rise and comfortable leg width suitable for any character.

  • Material: softer, lighter premium denim made of 100% cotton.
  • Waist (inch): 29-46
  • leg (inch): 30 to 38
", + Sku = "Fashion-65986524", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + Price = 109.90M, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = firstDeliveryTime, + DisplayOrder = 5, + TaxCategoryId = taxCategoryIdApparel + }; + + AddProductPicture(clarkPremiumBlueJeans, "product/clark_premium_jeans.jpg", "clark-premium-jeans"); + + clarkPremiumBlueJeans.ProductCategories.Add(new ProductCategory { Category = categories["Trousers"], DisplayOrder = 1 }); + + clarkPremiumBlueJeans.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) + }); + + #endregion Clark Premium Blue Jeans + + return new List + { + jackWolfskinCooGeeLow, adidasSuperstarSchuh, converseAllStar, ladiesJacket, jackWolfskinKanukaPoint, + rayBanTopBar, originalWayfarer, radarSportsSunglasses, customFlakSunglasses, shirtMeccanica, + clarkPremiumBlueJeans + }; + } + + private List GetFurnitureProducts(Dictionary categories, Dictionary specAttributes) + { + var productTemplateSimple = _ctx.Set().First(x => x.ViewPath == "Product"); + var thirdDeliveryTime = _ctx.Set().First(x => x.DisplayOrder == 2); + var taxCategoryIdElectronics = _ctx.Set().First(x => x.Name.Equals(TaxNameElectronics)).Id; + var discounts = _ctx.Set().Where(x => x.DiscountTypeId == (int)DiscountType.AssignedToSkus && !x.RequiresCouponCode).ToList(); + + #region Category Sofas + + #region Le Corbusier LC2 Sofa + + var corbusierSofa = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Le Corbusier LC2 sofa, 3-seater (1929)", + MetaTitle = "Le Corbusier LC2 sofa, 3-seater (1929)", + ShortDescription = "Sofa 3-seater LC 2, Designer: Le Corbusier, tubular steel frame (chrome), cushions of polyurethane foam and dacron wadding, seat upholstery with down, cover: leather", + FullDescription = "

The 3-seater sofa LC2, together with the two-seater of the same name, is the perfect complement to the well-known Corbusier armchairs. " + + "Together they form a perfectly shaped seating group for lobbies, lofts or salons with high design standards. Even though the Corbusier sofas bear the abbreviation of the designer (LC), they were not designed by him. They merely lean closely on the LC2 and LC3 seating furniture designed by Le Corbusier. " + + "However, this circumstance does not harm the appearance and comfort. The frame of this sofa consists of an elaborately curved tubular steel frame in chrome. The leather cushions are filled with polyurethane foam and dacron wadding, and the seat of the sofa is additionally upholstered with down feathers for optimum seating comfort.

" + + "

Dimensions of the sofa: W x D x H: 180 x 70 x 67 cm, seat height: approx. 45 cm

", + Sku = "LC2 DS/23-1", + ProductTemplateId = productTemplateSimple.Id, + AllowCustomerReviews = true, + Published = true, + Price = 1999.00M, + HasTierPrices = true, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = thirdDeliveryTime, + TaxCategoryId = taxCategoryIdElectronics + }; + + AddProductPicture(corbusierSofa, "product/le_corbusier_lc2_sofa_1.jpg"); + + corbusierSofa.ProductCategories.Add(new ProductCategory { Category = categories["Sofas"], DisplayOrder = 1 }); + + corbusierSofa.TierPrices.Add(new TierPrice + { + Quantity = 2, + Price = 1799.10M + }); + corbusierSofa.TierPrices.Add(new TierPrice + { + Quantity = 4, + Price = 1699.15M + }); + + corbusierSofa.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + corbusierSofa.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 14) + }); + + if (discounts.Any()) + { + corbusierSofa.HasDiscountsApplied = true; + corbusierSofa.AppliedDiscounts.Add(discounts.First()); + + // Add the mapping manually. We do not want to wait for the schedule task to do this automatically. + corbusierSofa.ProductCategories.Add(new ProductCategory { Category = categories["Sale"], IsSystemMapping = true }); + } + + #endregion Le Corbusier LC2 Sofa + + #region Josef Hoffmann Sofa + + var josefHoffmannSofa = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Josef Hoffmann sofa 2-seater Cubus (1910)", + MetaTitle = "Josef Hoffmann sofa 2-seater Cubus (1910)", + ShortDescription = "Sofa Kubus, 2-seater, Designer: Josef Hoffmann, width 166 cm, depth 72 cm, height 77 cm, base frame: solid beech wood, upholstery: solid polyurethane foam (dimensionally stable), cover: leather", + FullDescription = "

The two-seater from Josef Hoffmann's Kubus series is a stylish eye-catcher in residential and business premises. Together with the Kubus armchair and the Kubus three-seater, it creates a stylish seating group for reception halls and large living rooms." + + "Josef Hoffmann's sofa is upholstered with a shape-retaining upholstery, covered with leather and, using a special sewing technique, shows numerous squares that form an overall picture. " + + "The purely geometric form of this Hoffman design was also a pioneer of Cubism, which reached its peak at the beginning of the 20th century.

" + + "

Dimensions: width 166 cm, depth 72 cm, height 77 cm, cbm: 1,50

", + Sku = "JH DS/82-1", + ProductTemplateId = productTemplateSimple.Id, + AllowCustomerReviews = true, + Published = true, + Price = 3099.00M, + HasTierPrices = true, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = thirdDeliveryTime, + TaxCategoryId = taxCategoryIdElectronics + }; + + AddProductPicture(josefHoffmannSofa, "product/josef_hoffmann_sofa_cubus_1.jpg"); + + josefHoffmannSofa.ProductCategories.Add(new ProductCategory { Category = categories["Sofas"], DisplayOrder = 1 }); + + josefHoffmannSofa.TierPrices.Add(new TierPrice + { + Quantity = 2, + Price = 2659.05M + }); + josefHoffmannSofa.TierPrices.Add(new TierPrice + { + Quantity = 4, + Price = 2519.10M + }); + josefHoffmannSofa.TierPrices.Add(new TierPrice + { + Quantity = 6, + Price = 2379.15M + }); + josefHoffmannSofa.TierPrices.Add(new TierPrice + { + Quantity = 10, + Price = 2239.20M + }); + + josefHoffmannSofa.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + josefHoffmannSofa.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 14) + }); + + #endregion Josef Hoffmann Sofa + + #region Mies van der Rohe Barcelona Sofa + + var miesBarcelonaSofa = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Mies van der Rohe Barcelona - Loveseat sofa (1929)", + MetaTitle = "Mies van der Rohe Barcelona - Loveseat sofa (1929)", + ShortDescription = "Armchair Barcelona long, Designer: Mies van der Rohe, L x D x H: 147 x 75 x 75 cm, chrome-plated frame of special spring steel, covering: core leather strips, upholstery with polyurethane foam core, cover: leather", + FullDescription = "

The Loveseat Sofa Barcelona is one of the most famous pieces of furniture of the Bauhaus era. It was designed by Mies van der Rohe and presented at the World Exhibition in Barcelona in 1929. Mies van der Rohe dedicated it to the Spanish royal couple. The Barcelona sofa in the long version is ideal as a seating furniture for exhibitions or business premises. " + + "Together with the slim design and a table by Mies van der Rohe, a stylish sitting area for living rooms is also created. " + + "The Loveseat Barcelona sofa has a frame made of special high quality spring steel, covered with leather strips. " + + "On top of this lies the upholstery with leather covering.individual squares create a symmetrical, stylish picture.

" + + "

Dimensions: length 147 cm, depth 75 cm, height 75 cm, cbm: 0.96

", + Sku = "LR 556", + ProductTemplateId = productTemplateSimple.Id, + AllowCustomerReviews = true, + Published = true, + Price = 2799.00M, + HasTierPrices = true, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = thirdDeliveryTime, + TaxCategoryId = taxCategoryIdElectronics + }; + + AddProductPicture(miesBarcelonaSofa, "product/mies_van_der_rohe_sofa_barcelona_1.jpg"); + + miesBarcelonaSofa.ProductCategories.Add(new ProductCategory { Category = categories["Sofas"], DisplayOrder = 1 }); + + miesBarcelonaSofa.TierPrices.Add(new TierPrice + { + Quantity = 4, + Price = 2659.05M + }); + miesBarcelonaSofa.TierPrices.Add(new TierPrice + { + Quantity = 6, + Price = 2519.10M + }); + miesBarcelonaSofa.TierPrices.Add(new TierPrice + { + Quantity = 10, + Price = 2379.15M + }); + + miesBarcelonaSofa.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + miesBarcelonaSofa.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 14) + }); + + #endregion Mies van der Rohe Barcelona + + #endregion Category Sofas + + #region Category Tables + + #region Le Corbusier LC 6 tTble + + var corbusierTable = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Le Corbusier LC 6 dining table (1929)", + MetaTitle = "Le Corbusier LC 6 dining table (1929)", + ShortDescription = "Dining table LC 6, Designer: Le Corbusier, W x H x D: 225 x 69/74 (adjustable) x 85 cm, substructure: steel pipe, glass plate: Clear or sandblasted, 15 or 19 mm, height-adjustable.", + FullDescription = "

Four small plates carry a glass plate. The structure of the steel pipe is covered in clear structures. The LC6 is a true classic of Bauhaus art and is used in combination with the swivel chairs LC7 as a form-beautiful Le Corbusier dining area. In addition, the table is also increasingly found in offices or in halls. It is height-adjustable and can thus be perfectly adapted to the respective purpose.

Le Corbusier's beautifully shaped table is available with a clear or sandblasted glass plate. The substructure consists of oval steel tubes.

", + Sku = "Furniture-lc6", + ProductTemplateId = productTemplateSimple.Id, + AllowCustomerReviews = true, + Published = true, + Price = 749.00M, + HasTierPrices = true, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = thirdDeliveryTime, + TaxCategoryId = taxCategoryIdElectronics + }; + + AddProductPicture(corbusierTable, "product/corbusier_lc6_table_1.jpg", "corbusier-lc6-table-1"); + AddProductPicture(corbusierTable, "product/corbusier_lc6_table_2.jpg", "corbusier-lc6-table-2"); + AddProductPicture(corbusierTable, "product/corbusier_lc6_table_3.jpg", "corbusier-lc6-table-3"); + AddProductPicture(corbusierTable, "product/corbusier_lc6_table_4.jpg", "corbusier-lc6-table-4"); + + corbusierTable.ProductCategories.Add(new ProductCategory { Category = categories["Tables"], DisplayOrder = 1 }); + + corbusierTable.TierPrices.Add(new TierPrice + { + Quantity = 2, + Price = 647.10M + }); + corbusierTable.TierPrices.Add(new TierPrice + { + Quantity = 4, + Price = 636.65M + }); + + corbusierTable.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + corbusierTable.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 14) + }); + + #endregion Le Corbusier LC 6 Table + + #region Isamu Noguchi Coffee Table + + var noguchiTable = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Isamu Noguchi couch table, Coffee Table (1945)", + MetaTitle = "Isamu Noguchi couch table, Coffee Table (1945)", + ShortDescription = "Coffee table, Designer: Isamu Noguchi, W x H x D: 128 x 40 x 92.5 cm, base: wood, table top: crystal glass, 15 or 19 mm", + FullDescription = "

The coffee table of Isamu Noguchi once impressed even the president of the New York Museum of Modern Art. " + + "Because it was originally designed for him. The curved ash base is an elegant eye-catcher. " + + "It appears unobtrusive and is perfectly shown off to advantage through the transparent, three-sided glass plate. " + + "A Bauhaus - furniture, which today is used in many rooms with upscale furnishings as a side table. " + + "It fits perfectly in the lounge, living room and reception areas.

" + + "

Dimensions: width 128 cm, height 40 cm, depth 92,5 cm

", + Sku = "IN 200", + ProductTemplateId = productTemplateSimple.Id, + AllowCustomerReviews = true, + Published = true, + Price = 499.00M, + HasTierPrices = true, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = thirdDeliveryTime, + TaxCategoryId = taxCategoryIdElectronics + }; + + AddProductPicture(noguchiTable, "product/isamu_noguchi_coffeetable_1.jpg"); + AddProductPicture(noguchiTable, "product/isamu_noguchi_coffeetable_2.jpg"); + + noguchiTable.ProductCategories.Add(new ProductCategory { Category = categories["Tables"], DisplayOrder = 1 }); + + noguchiTable.TierPrices.Add(new TierPrice + { + Quantity = 2, + Price = 449.10M + }); + noguchiTable.TierPrices.Add(new TierPrice + { + Quantity = 4, + Price = 424.15M + }); + + noguchiTable.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + noguchiTable.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 14) + }); + + #endregion Isamu Noguchi Coffee Table + + #region Mies van der Rohe Barcelona Table + + var miesBarcelonaTable = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Ludwig Mies van der Rohe table Barcelona (1930)", + MetaTitle = "Ludwig Mies van der Rohe table Barcelona (1930)", + ShortDescription = "Table Barcelona, Designer: Mies van der Rohe, width 90 cm, height 46 cm, depth 90 cm, base: chromed flat steel, table top: glass (12 mm)", + FullDescription = "

This table by Mies van der Rohe matches the famous Barcelona series of armchair and stool, which was designed for the King of Spain and presented at the World Fair in 1929. " + + "Although the coffee table was not made until some time later by Mies van der Rohe for the house 'Tugendhat', it forms an attractive sitting area for offices and living rooms with the furniture of the Barcelona series. " + + "The table by Mies van der Rohe consists of a flat steel frame and a 12 mm thick glass plate, under the transparent plate a chromed 'X' appears through the construction.

" + + "

dimensions: width 90 cm, height 46 cm, depth 90 cm, glass plate thickness: 12 mm

", + Sku = "LM T/98", + ProductTemplateId = productTemplateSimple.Id, + AllowCustomerReviews = true, + Published = true, + Price = 629.00M, + HasTierPrices = true, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = thirdDeliveryTime, + TaxCategoryId = taxCategoryIdElectronics + }; + + AddProductPicture(miesBarcelonaTable, "product/mies_van_der_rohe_tisch_barcelona_1.jpg"); + + miesBarcelonaTable.ProductCategories.Add(new ProductCategory { Category = categories["Tables"], DisplayOrder = 1 }); + + miesBarcelonaTable.TierPrices.Add(new TierPrice + { + Quantity = 2, + Price = 597.55M + }); + miesBarcelonaTable.TierPrices.Add(new TierPrice + { + Quantity = 4, + Price = 566.10M + }); + miesBarcelonaTable.TierPrices.Add(new TierPrice + { + Quantity = 6, + Price = 534.65M + }); + + miesBarcelonaTable.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + miesBarcelonaTable.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 14) + }); + + #endregion Mies van der Rohe Barcelona Table + + #endregion Category Tables + + #region Category Armchairs + + #region Ball Chair + + var ballChair = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Eero Aarnio Ball Chair (1966)", + MetaTitle = "Eero Aarnio Ball Chair (1966)", + FullDescription = "

The ball chair, or also called the globe chair, is a real masterpiece of the legendary designer Eero Aarnio. The ball chair from the Sixties has written designer history. The egg designed armchair rests on a trumpet foot and is not lastly appreciated due to its shape and the quiet atmosphere inside this furniture. The design of the furniture body allows noise and disturbing outer world elements in the Hintergurnd us. A place as created for resting and relaxing. With its wide range of colours, the eyeball chair fits in every living and working environment. A chair that stands out for its timeless design and always has the modern look. The ball chair is 360° to rotate to change the view of the surroundings. The outer shell in fiberglass white or black. The upholstery is mixed in leather or linen.

Dimension: Width 102 cm, depth 87 cm, height 124 cm, seat height: 44 cm.

", + Sku = "Furniture-ball-chair", + ProductTemplateId = productTemplateSimple.Id, + AllowCustomerReviews = true, + Published = true, + Price = 2199.00M, + HasTierPrices = true, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = thirdDeliveryTime, + TaxCategoryId = taxCategoryIdElectronics + }; + + AddProductPicture(ballChair, "product/ball_chair_white.jpg", "ball-chair-white"); + AddProductPicture(ballChair, "product/ball_chair_black.jpg", "ball-chair-black"); + + ballChair.ProductCategories.Add(new ProductCategory { Category = categories["Chairs"], DisplayOrder = 1 }); + + ballChair.TierPrices.Add(new TierPrice + { + Quantity = 2, + Price = 1979.10M + }); + ballChair.TierPrices.Add(new TierPrice + { + Quantity = 4, + Price = 1869.15M + }); + + ballChair.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + }); + ballChair.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) + }); + + #endregion Ball Chair + + #region Charles Eames Lounge Chair + + var loungeChair = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Charles Eames Lounge Chair (1956)", + MetaTitle = "Charles Eames Lounge Chair (1956)", + ShortDescription = "Club lounge chair, Designer: Charles Eames, width 80 cm, depth 80 cm, height 60 cm, seat shell: plywood, foot (rotatable): Aluminium casting, cushion (upholstered) with leather cover.", + FullDescription = "

That's how you sit in a baseball glove. In any case, this was one of the ideas Charles Eames had in mind when designing this club chair. The lounge chair should be a comfort armchair, in which one can sink luxuriously. Through the construction of three interconnected, movable seat shells and a comfortable upholstery Charles Eames succeeded in the implementation. In fact, the club armchair with a swiveling foot is a contrast to the Bauhaus characteristics that emphasized minimalism and functionality. Nevertheless, he became a classic of Bauhaus history and still provides in many living rooms and clubs for absolute comfort with style.

Dimensions: Width 80 cm, depth 60 cm, height total 80 cm (height backrest: 60 cm). CBM: 0.70.

Lounge chair with seat shell of laminated curved plywood with rosewood veneer, walnut nature or in black. Rotatable base made of aluminium cast black with polished edges or optionally fully chromed. Elaborate upholstery of pillows in leather.

All upholstery units are removable at the Eames Lounge chair (seat, armrest, backrest, headrest).

", + Sku = "Furniture-lounge-chair", + ProductTemplateId = productTemplateSimple.Id, + AllowCustomerReviews = true, + Published = true, + ShowOnHomePage = true, + Price = 1799.00M, + HasTierPrices = true, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = thirdDeliveryTime, + TaxCategoryId = taxCategoryIdElectronics + }; + + AddProductPicture(loungeChair, "product/charles_eames_lounge_chair_white.jpg", "charles-eames-lounge-chair-white"); + AddProductPicture(loungeChair, "product/charles_eames_lounge_chair_black.jpg", "charles-eames-lounge-chair-black"); + + loungeChair.ProductCategories.Add(new ProductCategory { Category = categories["Chairs"], DisplayOrder = 1 }); + + loungeChair.TierPrices.Add(new TierPrice + { + Quantity = 2, + Price = 1709.05M + }); + loungeChair.TierPrices.Add(new TierPrice + { + Quantity = 4, + Price = 1664.08M + }); + loungeChair.TierPrices.Add(new TierPrice + { + Quantity = 6, + Price = 1619.10M + }); + + loungeChair.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 13) + }); + loungeChair.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) + }); + loungeChair.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 4) + }); + + #endregion Charles Eames Lounge Chair + + #region Josef Hoffmann Cube Chair + + var cubeChair = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Josef Hoffmann cube chair (1910)", + MetaTitle = "Josef Hoffmann cube chair (1910)", + ShortDescription = "Armchair Cube, Designer: Josef Hoffmann, width 93 cm, depth 72 cm, height 77 cm, basic frame: solid beech wood, upholstery: solid polyurethane foam (shape resistant), Upholstery: leather", + FullDescription = "

The cube chair by Josef Hoffmann holds what the name promises and that is the same in two respects. It consists of many squares, both in terms of construction and in relation to the design of the surface. In addition, the cube, with its purely geometric form, was a kind of harbinger of cubism. The chair by Josef Hoffmann was designed in 1910 and still stands today as a replica in numerous business and residential areas.

Originally, the cube was a club chair. Together with the two-and three-seater sofa of the series, a cosy sitting area with a sophisticated charisma is created. The basic frame of the armchair is made of wood. The form-resistant upholstery is covered with leather and has been shaped visually to squares with a special sewing.

Dimensions: Width 93 cm, depth 72 cm, height 77 cm. CBM: 0.70.

", + Sku = "Furniture-cube-chair", + ProductTemplateId = productTemplateSimple.Id, + AllowCustomerReviews = true, + Published = true, + ShowOnHomePage = true, + Price = 2299.00M, + HasTierPrices = true, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + IsShipEnabled = true, + DeliveryTime = thirdDeliveryTime, + TaxCategoryId = taxCategoryIdElectronics + }; + + AddProductPicture(cubeChair, "product/hoffmann_cube_chair_black.jpg", "hoffmann-cube-chair-black"); + + cubeChair.ProductCategories.Add(new ProductCategory { Category = categories["Chairs"], DisplayOrder = 1 }); + + cubeChair.TierPrices.Add(new TierPrice + { + Quantity = 4, + Price = 1899.05M + }); + cubeChair.TierPrices.Add(new TierPrice + { + Quantity = 6, + Price = 1799.10M + }); + + cubeChair.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) + }); + + if (discounts.Any()) + { + cubeChair.HasDiscountsApplied = true; + cubeChair.AppliedDiscounts.Add(discounts.First()); + + // Add the mapping manually. We do not want to wait for the schedule task to do this automatically. + cubeChair.ProductCategories.Add(new ProductCategory { Category = categories["Sale"], IsSystemMapping = true }); + } + + #endregion Josef Hoffmann Cube Chair + + #endregion Category Armchairs + + return new List + { + miesBarcelonaTable, noguchiTable, corbusierTable, ballChair, loungeChair, cubeChair, + miesBarcelonaSofa, corbusierSofa, josefHoffmannSofa + }; + } + + public IList Products() + { + var specialPriceEndDate = DateTime.UtcNow.AddMonths(1); + + var productTemplate = _ctx.Set().First(x => x.ViewPath == "Product"); + var firstDeliveryTime = _ctx.Set().First(x => x.DisplayOrder == 0); + var secondDeliveryTime = _ctx.Set().First(x => x.DisplayOrder == 1); + var thirdDeliveryTime = _ctx.Set().First(x => x.DisplayOrder == 2); + + var manufacturers = _ctx.Set().ToList().ToDictionarySafe(x => x.Name, x => x); + var categories = _ctx.Set().ToList().ToDictionarySafe(x => x.Alias, x => x); + var specAttributes = _ctx.Set().ToList().ToDictionarySafe(x => x.DisplayOrder, x => x); + var taxCategories = _ctx.Set().ToList().ToDictionarySafe(x => x.Name, x => x); + + #region Category Sports + + #region Category Golf + + #region Titleist SM6 Tour Chrome + + var titleistSM6TourChrome = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Titleist SM6 Tour Chrome", + IsEsd = false, + ShortDescription = "For golfers who want maximum impact control and feedback.", + FullDescription = "​​

Inspired by the best iron players in the world

The new 'Spin Milled 6' wages establish a new performance class in three key areas of the Wedge game: precise length steps, bounce and maximum spin.


For each loft the center of gravity of the wedge is determined individually. Therefore, the SM6 offers a particularly precise length and flight curve control combined with great impact.
Bob Vokey's tourer-puffed sole cleat allows all golfers more bounce, adapted to their personal swing profile and the respective ground conditions.


A new, parallel face texture was developed for the absolutely exact and with 100% quality control machined grooves. The result is a consistently higher edge sharpness for more spin.

  • Precise lengths and flight curve control thanks to progressively placed center of gravity.
  • Improved bounce due to Bob Vokey's proven soles.
  • TX4 grooves produce more spin through a new surface and edge sharpness.
  • Multiple personalization options.
", + Sku = "P-7004", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Titleist SM6 Tour Chrome", + Price = 164.95M, + OldPrice = 199.95M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameApparel].Id + }; + + AddProductPicture(titleistSM6TourChrome, "product/titleist_sm6_tour_chrome.jpg"); + + titleistSM6TourChrome.ProductCategories.Add(new ProductCategory { Category = categories["Golf"], DisplayOrder = 1 }); + titleistSM6TourChrome.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Titleist"], DisplayOrder = 1 }); + + #endregion Titleist SM6 Tour Chrome + + #region Titleist Pro V1x + + var titleistProV1x = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Titleist Pro V1x", + IsEsd = false, + ShortDescription = "Golf ball with high ball flight", + FullDescription = "​​The top players rely on the new Titleist Pro V1x. High ball flight, soft feel and more spin in the short game are the advantages of the V1x version. Perfect performance from the leading manufacturer. The new Titleist Pro V1 golf ball is exactly defined and promises penetrating ball flight with very soft hit feeling.", + Sku = "P-7001", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Titleist Pro V1x", + Price = 2.1M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameApparel].Id + }; + + AddProductPicture(titleistProV1x, "product/titleist-pro-v1x.jpg"); + + titleistProV1x.ProductCategories.Add(new ProductCategory { Category = categories["Golf"], DisplayOrder = 1 }); + titleistProV1x.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Titleist"], DisplayOrder = 1 }); + + #endregion Titleist Pro V1x + + #region Supreme Golfball + + var supremeGolfball = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Supreme Golfball", + IsEsd = false, + ShortDescription = "Training balls with perfect flying characteristics", + FullDescription = "​Perfect golf exercise ball with the characteristics like the 'original', but in a glass-fracture-proof execution. Massive core, an ideal training ball for yard and garden. Colors: white, yellow, orange.", + Sku = "P-7002", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Supreme Golfball", + Price = 1.9M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameApparel].Id + }; + + AddProductPicture(supremeGolfball, "product/supremeGolfball_1.jpg"); + AddProductPicture(supremeGolfball, "product/supremeGolfball_2.jpg"); + + supremeGolfball.ProductCategories.Add(new ProductCategory { Category = categories["Golf"], DisplayOrder = 1 }); + supremeGolfball.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Titleist"], DisplayOrder = 1 }); + + #endregion Supreme Golfball + + #region GBB Epic Sub Zero Driver + + var gbbEpicSubZeroDriver = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "GBB Epic Sub Zero Driver", + IsEsd = false, + ShortDescription = "Low spin for good golfing!", + FullDescription = "Your game wins with the GBB Epic Sub Zero Driver. A golf club with an extremely low spin and the phenomenal high-speed characteristic.", + Sku = "P-7003", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "GBB Epic Sub Zero Driver", + Price = 489M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameApparel].Id + }; + + AddProductPicture(gbbEpicSubZeroDriver, "product/gbb-epic-sub-zero-driver.jpg"); + + gbbEpicSubZeroDriver.ProductCategories.Add(new ProductCategory { Category = categories["Golf"], DisplayOrder = 1 }); + gbbEpicSubZeroDriver.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Titleist"], DisplayOrder = 1 }); + + #endregion GBB Epic Sub Zero Driver + + #endregion Category Golf + + #region Category Soccer + + #region Nike Strike Football + + var nikeStrikeFootball = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Nike Strike Football", + IsEsd = false, + ShortDescription = "GREAT TOUCH. HIGH VISIBILITY.", + FullDescription = "

Enhance play everyday, with the Nike Strike Football.

Reinforced rubber retains its shape for confident and consistent control. A stand out Visual Power graphic in black, green and orange is best for ball tracking, despite dark or inclement conditions.

  • Visual Power graphic helps give a true read on flight trajectory.
  • Textured casing offers superior touch.
  • Reinforced rubber bladder supports air and shape retention.
  • 66% rubber/ 15% polyurethane/ 13% polyester/ 7% EVA.
", + Sku = "P-5004", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Nike Strike Football", + Price = 59.90M, + OldPrice = 69.90M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + HasTierPrices = true, + TaxCategoryId = taxCategories[TaxNameApparel].Id + }; + + AddProductPicture(nikeStrikeFootball, "product/nike-strike-football.jpg"); + + nikeStrikeFootball.ProductCategories.Add(new ProductCategory { Category = categories["Soccer"], DisplayOrder = 1 }); + nikeStrikeFootball.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Nike"], DisplayOrder = 1 }); + + nikeStrikeFootball.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Manufacturer -> Nike + SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 20) + }); + nikeStrikeFootball.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Material -> rubber + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 12) + }); + + nikeStrikeFootball.TierPrices.Add(new TierPrice { Quantity = 6, Price = 26.90M }); + nikeStrikeFootball.TierPrices.Add(new TierPrice { Quantity = 12, Price = 24.90M }); + nikeStrikeFootball.TierPrices.Add(new TierPrice { Quantity = 24, Price = 22.90M }); + + #endregion Nike Strike Football + + #region Evopower 5.3 Trainer HS Ball + + var nikeEvoPowerBall = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Evopower 5.3 Trainer HS Ball", + IsEsd = false, + ShortDescription = "Entry level training ball.", + FullDescription = "

Entry level training ball.< p > Constructed from 32 panels with equal surface areas for reduced seam-stress and a perfectly round shape.Handstitched panels with multilayered woven backing for enhanced stability and aerodynamics.", + Sku = "P-5003", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Evopower 5.3 Trainer HS Ball", + Price = 59.90M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameApparel].Id + }; + + AddProductPicture(nikeEvoPowerBall, "product/nike-vopower-53-trainer-hs-ball.jpg"); + + nikeEvoPowerBall.ProductCategories.Add(new ProductCategory { Category = categories["Soccer"], DisplayOrder = 1 }); + nikeEvoPowerBall.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Nike"], DisplayOrder = 1 }); + + nikeEvoPowerBall.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Manufacturer -> Nike + SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 20) + }); + nikeEvoPowerBall.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Material -> leather + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) + }); + + #endregion Evopower 5.3 Trainer HS Ball + + #region Torfabrik official game ball + + var torfabrikOfficialGameBall = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Torfabrik official game ball", + IsEsd = false, + ShortDescription = "Available in different colors", + FullDescription = "", + Sku = "P-5002", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Torfabrik official game ball", + Price = 59.90M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameApparel].Id + }; + + AddProductPicture(torfabrikOfficialGameBall, "product/torfabrik-offizieller-spielball_white.png", "official-game-ball-white"); + AddProductPicture(torfabrikOfficialGameBall, "product/torfabrik-offizieller-spielball_red.png", "official-game-ball-red"); + AddProductPicture(torfabrikOfficialGameBall, "product/torfabrik-offizieller-spielball_yellow.png", "official-game-ball-yellow"); + AddProductPicture(torfabrikOfficialGameBall, "product/torfabrik-offizieller-spielball_blue.png", "official-game-ball-blue"); + AddProductPicture(torfabrikOfficialGameBall, "product/torfabrik-offizieller-spielball_green.png", "official-game-ball-green"); + + torfabrikOfficialGameBall.ProductCategories.Add(new ProductCategory { Category = categories["Soccer"], DisplayOrder = 1 }); + torfabrikOfficialGameBall.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Adidas"], DisplayOrder = 1 }); + + torfabrikOfficialGameBall.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Manufacturer -> Adidas + SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 19) + }); + torfabrikOfficialGameBall.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Material -> leather + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) + }); + + #endregion Torfabrik official game ball + + #region Adidas TANGO SALA BALL + + var adidasTangoSalaBall = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Adidas TANGO SALA BALL", + IsEsd = false, + ShortDescription = "In different colors", + FullDescription = "

TANGO SALA BALL
A SALA BALL TO MATCH YOUR INDOOR PLAYMAKING.

Take the game indoors. With a design nod to the original Tango ball that set the performance standard, this indoor soccer is designed for low rebound and enhanced control for futsal. Machine-stitched for a soft touch and high durability.

  • Machine-stitched for soft touch and high durability
  • Low rebound for enhanced ball control
  • Butyl bladder for best air retention
  • Requires inflation
  • 100% natural rubber
  • Imported

", + Sku = "P-5001", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Adidas TANGO SALA BALL", + Price = 59.90M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameApparel].Id + }; + + AddProductPicture(adidasTangoSalaBall, "product/adidas-tango-pasadena-ball-white.png"); + AddProductPicture(adidasTangoSalaBall, "product/adidas-tango-pasadena-ball-yellow.jpg"); + AddProductPicture(adidasTangoSalaBall, "product/adidas-tango-pasadena-ball-red.jpg"); + AddProductPicture(adidasTangoSalaBall, "product/adidas-tango-pasadena-ball-green.jpg"); + AddProductPicture(adidasTangoSalaBall, "product/adidas-tango-pasadena-ball-gray.jpg"); + AddProductPicture(adidasTangoSalaBall, "product/adidas-tango-pasadena-ball-brown.jpg"); + AddProductPicture(adidasTangoSalaBall, "product/adidas-tango-pasadena-ball-blue.jpg"); + + adidasTangoSalaBall.ProductCategories.Add(new ProductCategory { Category = categories["Soccer"], DisplayOrder = 1 }); + adidasTangoSalaBall.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Adidas"], DisplayOrder = 1 }); + + adidasTangoSalaBall.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Manufacturer -> Adidas + SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 19) + }); + adidasTangoSalaBall.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Material -> leather + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) + }); + + #endregion Adidas TANGO SALA BALL + + #endregion Category Soccer + + #region Category Basketball + + #region Wilson Evolution High School Game Basketball + + var evolutionBasketball = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Evolution High School Game Basketball", + IsEsd = false, + ShortDescription = "For all positions on all levels, match day and every day", + FullDescription = "

The Wilson Evolution High School Game Basketball has exclusive microfiber composite leather construction with deep embossed pebbles to give you the ultimate in feel and control.

Its patented Cushion Core Technology enhances durability for longer play. This microfiber composite Evolution high school basketball is pebbled with composite channels for better grip, helping players raise their game to the next level.

For all positions at all levels of play, game day and every day, Wilson delivers the skill-building performance that players demand.

This regulation-size 29.5' Wilson basketball is an ideal basketball for high school players, and is designed for either recreational use or for league games. It is NCAA and NFHS approved, so you know it's a high-quality basketball that will help you hone your shooting, passing and ball-handling skills.

Take your team all the way to the championship with the Wilson Evolution High School Game Basketball.

", + Sku = "P-4001", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Evolution High School Game Basketball", + Price = 25.90M, + OldPrice = 29.90M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + HasTierPrices = true, + TaxCategoryId = taxCategories[TaxNameApparel].Id + }; + + AddProductPicture(evolutionBasketball, "product/evolution-high-school-game-basketball.jpg"); + + evolutionBasketball.ProductCategories.Add(new ProductCategory { Category = categories["Basketball"], DisplayOrder = 1 }); + evolutionBasketball.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Adidas"], DisplayOrder = 1 }); + + evolutionBasketball.TierPrices.Add(new TierPrice { Quantity = 6, Price = 24.90M }); + evolutionBasketball.TierPrices.Add(new TierPrice { Quantity = 12, Price = 22.90M }); + evolutionBasketball.TierPrices.Add(new TierPrice { Quantity = 24, Price = 20.90M }); + + #endregion Wilson Evolution High School Game Basketball + + #region All Court Basketball + + var allCourtBasketball = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "All-Court Basketball", + IsEsd = false, + ShortDescription = "A durable Basketball for all surfaces", + FullDescription = "

All-Court Prep Ball

A durable basketball for all surfaces.

Whether on parquet or on asphalt - the adidas All-Court Prep Ball hat has only one goal: the basket. This basketball is made of durable artificial leather, was also predestined for indoor games also for outdoor games.

  • Composite cover made of artificial leather
  • suitable for indoors and outdoors
  • Delivered unpumped
", + Sku = "P-4002", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "All-Court Basketball", + Price = 25.90M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameApparel].Id + }; + + AddProductPicture(allCourtBasketball, "product/all-court-basketball.png"); + + allCourtBasketball.ProductCategories.Add(new ProductCategory { Category = categories["Basketball"], DisplayOrder = 1 }); + allCourtBasketball.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Adidas"], DisplayOrder = 1 }); + + #endregion All Court Basketball + + #endregion Category Basketball + + #endregion Category Sports + + #region Category Gift Cards + + #region GiftCard10 + + var giftCard10 = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "$10 Virtual Gift Card", + IsEsd = true, + ShortDescription = "$10 Gift Card. Gift Cards must be redeemed through our site Web site toward the purchase of eligible products.", + FullDescription = "

Gift Cards must be redeemed through our site Web site toward the purchase of eligible products. Purchases are deducted from the GiftCard balance. Any unused balance will be placed in the recipient's GiftCard account when redeemed. If an order exceeds the amount of the GiftCard, the balance must be paid with a credit card or other available payment method.

", + Sku = "P-1000", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "$10 Virtual Gift Card", + Price = 10M, + IsGiftCard = true, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + DisplayOrder = 1, + TaxCategoryId = taxCategories[TaxNameTaxFree].Id + }; + + AddProductPicture(giftCard10, "product/gift_card_10.png"); + giftCard10.ProductCategories.Add(new ProductCategory { Category = categories["Gift Cards"], DisplayOrder = 1 }); + + #endregion GiftCard10 + + #region GiftCard25 + + var giftCard25 = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "$25 Virtual Gift Card", + IsEsd = true, + ShortDescription = "$25 Gift Card. Gift Cards must be redeemed through our site Web site toward the purchase of eligible products.", + FullDescription = "

Gift Cards must be redeemed through our site Web site toward the purchase of eligible products. Purchases are deducted from the GiftCard balance. Any unused balance will be placed in the recipient's GiftCard account when redeemed. If an order exceeds the amount of the GiftCard, the balance must be paid with a credit card or other available payment method.

", + Sku = "P-1001", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "$25 Virtual Gift Card", + Price = 25M, + IsGiftCard = true, + GiftCardType = GiftCardType.Virtual, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + DisplayOrder = 2, + TaxCategoryId = taxCategories[TaxNameTaxFree].Id + }; + + AddProductPicture(giftCard25, "product/gift_card_25.png"); + giftCard25.ProductCategories.Add(new ProductCategory { Category = categories["Gift Cards"], DisplayOrder = 1 }); + + #endregion GiftCard25 + + #region GiftCard50 + + var giftCard50 = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "$50 Virtual Gift Card", + IsEsd = true, + ShortDescription = "$50 Gift Card. Gift Cards must be redeemed through our site Web site toward the purchase of eligible products.", + FullDescription = "

Gift Cards must be redeemed through our site Web site toward the purchase of eligible products. Purchases are deducted from the GiftCard balance. Any unused balance will be placed in the recipient's GiftCard account when redeemed. If an order exceeds the amount of the GiftCard, the balance must be paid with a credit card or other available payment method.

", + Sku = "P-1002", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "$50 Virtual Gift Card", + Price = 50M, + IsGiftCard = true, + GiftCardType = GiftCardType.Virtual, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + DisplayOrder = 3, + TaxCategoryId = taxCategories[TaxNameTaxFree].Id + }; + + AddProductPicture(giftCard50, "product/gift_card_50.png"); + giftCard50.ProductCategories.Add(new ProductCategory { Category = categories["Gift Cards"], DisplayOrder = 1 }); + + #endregion GiftCard50 + + #region GiftCard100 + + var giftCard100 = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "$100 Virtual Gift Card", + IsEsd = true, + ShortDescription = "$100 Gift Card. Gift Cards must be redeemed through our site Web site toward the purchase of eligible products.", + FullDescription = "

Gift Cards must be redeemed through our site Web site toward the purchase of eligible products. Purchases are deducted from the GiftCard balance. Any unused balance will be placed in the recipient's GiftCard account when redeemed. If an order exceeds the amount of the GiftCard, the balance must be paid with a credit card or other available payment method.

", + Sku = "P-10033", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "$100 Virtual Gift Card", + Price = 100M, + IsGiftCard = true, + GiftCardType = GiftCardType.Virtual, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + DisplayOrder = 4, + TaxCategoryId = taxCategories[TaxNameTaxFree].Id + }; + + AddProductPicture(giftCard100, "product/gift_card_100.png"); + giftCard100.ProductCategories.Add(new ProductCategory { Category = categories["Gift Cards"], DisplayOrder = 1 }); + + #endregion GiftCard100 + + #endregion Category Gift Cards + + #region Category Apple + + #region IPhone Plus + + var iPhonePlus = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "iPhone Plus", + IsEsd = false, + ShortDescription = "iPhone 7 dramatically improves the most important aspects of the iPhone experience. It introduces advanced new camera systems. The best performance and battery life ever in an iPhone. Immersive stereo speakers. The brightest, most colorful iPhone display. Splash and water resistance.1 And it looks every bit as powerful as it is. This is iPhone 7.", + FullDescription = "", + Sku = "P-2001", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "iPhone Plus", + Price = 878M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 9, + StockQuantity = 10000, + DisplayStockAvailability = true, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + IsFreeShipping = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(iPhonePlus, "product/iphone-plus_all_colors.jpg"); + AddProductPicture(iPhonePlus, "product/iphoneplus_1.jpg", "iphone-plus-default"); + AddProductPicture(iPhonePlus, "product/iphone-plus_red.jpg"); + AddProductPicture(iPhonePlus, "product/iphone-plus_silver.jpg"); + AddProductPicture(iPhonePlus, "product/iphone-plus_black.jpg"); + AddProductPicture(iPhonePlus, "product/iphone-plus_rose.jpg"); + AddProductPicture(iPhonePlus, "product/iphone-plus_gold.jpg"); + + iPhonePlus.ProductCategories.Add(new ProductCategory { Category = categories["Apple"], DisplayOrder = 1 }); + iPhonePlus.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Apple"], DisplayOrder = 1 }); + + iPhonePlus.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // offer type -> Permanent low price + SpecificationAttributeOption = specAttributes[22].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) + }); + + iPhonePlus.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // storage capacity -> 64gb + SpecificationAttributeOption = specAttributes[27].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) + }); + iPhonePlus.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // storage capacity -> 128gb + SpecificationAttributeOption = specAttributes[27].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + }); + iPhonePlus.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // operating system -> ios + SpecificationAttributeOption = specAttributes[5].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) + }); + + #endregion IPhone Plus + + #region Watch Series 2 + + var watchSeries2 = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Watch Series 2", + IsEsd = false, + ShortDescription = "Live a better day. Built-in GPS. Water resistance to 50 meters.1 A lightning-fast dual‑core processor. And a display that’s two times brighter than before. Full of features that help you stay active, motivated, and connected, Apple Watch Series 2 is the perfect partner for a healthy life.", + FullDescription = "", + Sku = "P-2002", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Watch Series 2", + Price = 299M, + OldPrice = 399M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(watchSeries2, "product/watchseries2_1.jpg"); + AddProductPicture(watchSeries2, "product/watchseries2_2.jpg"); + + watchSeries2.ProductCategories.Add(new ProductCategory { Category = categories["Apple"], DisplayOrder = 1 }); + watchSeries2.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Apple"], DisplayOrder = 1 }); + + watchSeries2.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // offer type -> offer of the day + SpecificationAttributeOption = specAttributes[22].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 6) + }); + + watchSeries2.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // storage capacity -> 32gb + SpecificationAttributeOption = specAttributes[27].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + + watchSeries2.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // operating system -> ios + SpecificationAttributeOption = specAttributes[5].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) + }); + + #endregion Watch Series 2 + + #region Airpods + + var airPods = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "AirPods", + IsEsd = false, + ShortDescription = "Wireless. Effortless. Magical. Just take them out and they’re ready to use with all your devices. Put them in your ears and they connect instantly. Speak into them and your voice sounds clear. Introducing AirPods. Simplicity and technology, together like never before. The result is completely magical.", + FullDescription = "", + Sku = "P-2003", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "AirPods", + Price = 999M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(airPods, "product/airpods_white.jpg", "airpods-white"); + AddProductPicture(airPods, "product/airpods_turquoise.jpg", "airpods-turquoise"); + AddProductPicture(airPods, "product/airpods_lightblue.jpg", "airpods-lightblue"); + AddProductPicture(airPods, "product/airpods_rose.jpg", "airpods-rose"); + AddProductPicture(airPods, "product/airpods_gold.jpg", "airpods-gold"); + AddProductPicture(airPods, "product/airpods_mint.jpg", "airpods-mint"); + + airPods.ProductCategories.Add(new ProductCategory { Category = categories["Apple"], DisplayOrder = 1 }); + airPods.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Apple"], DisplayOrder = 7 }); + + #endregion Airpods + + #region Ultimate Apple Pro Hipster Bundle + + var appleProHipsterBundle = new Product + { + ProductType = ProductType.BundledProduct, + Name = "Ultimate Apple Pro Hipster Bundle", + IsEsd = false, + ShortDescription = "Save with this set 5%!", + FullDescription = "As an Apple fan and hipster, it is your basic need to always have the latest Apple products. So you do not have to spend four times a year in front of the Apple Store, simply subscribe to the Ultimate Apple Pro Hipster Set in the year subscription!", + Sku = "P-2005-Bundle", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + ShowOnHomePage = true, + MetaTitle = "Ultimate Apple Pro Hipster Bundle", + Price = 2371M, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + BundleTitleText = "Bundle includes", + BundlePerItemPricing = true, + BundlePerItemShoppingCart = true, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(appleProHipsterBundle, "product/ultimate-apple-pro-hipster-bundle.jpg"); + AddProductPicture(appleProHipsterBundle, "product/airpods_white.jpg", "bundle-airpods-white"); + AddProductPicture(appleProHipsterBundle, "product/watchseries2_2.jpg", "bundle-watchseries"); + AddProductPicture(appleProHipsterBundle, "product/iphoneplus_2.jpg", "bundle-iphoneplus"); + AddProductPicture(appleProHipsterBundle, "category/apple.png", "bundle-apple"); + + appleProHipsterBundle.ProductCategories.Add(new ProductCategory { Category = categories["Apple"], DisplayOrder = 1 }); + appleProHipsterBundle.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Apple"], DisplayOrder = 1 }); + + #endregion Ultimate Apple Pro Hipster Bundle + + #region 9,7 IPad + + var iPad = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "9,7' iPad", + IsEsd = false, + ShortDescription = "Flat-out fun. Learn, play, surf, create. iPad gives you the incredible display, performance, and apps to do what you love to do. Anywhere. Easily. Magically.", + FullDescription = "
  • 9,7' Retina Display mit True Tone und
  • A9X Chip der dritten Generation mit 64-Bit Desktoparchitektur
  • Touch ID Fingerabdrucksensor
  • 12 Megapixel iSight Kamera mit 4K Video
  • 5 Megapixel FaceTime HD Kamera
  • 802.11ac WLAN mit MIMO
  • Bis zu 10 Stunden Batterielaufzeit***
  • 4-Lautsprecher-Audio
", + Sku = "P-2004", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + ShowOnHomePage = true, + MetaTitle = "9,7' iPad", + Price = 319.00M, + SpecialPrice = 299.00M, + SpecialPriceStartDateTimeUtc = new DateTime(2017, 5, 1, 0, 0, 0), + SpecialPriceEndDateTimeUtc = specialPriceEndDate, + IsGiftCard = false, + ManageInventoryMethod = ManageInventoryMethod.ManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(iPad, "product/ipad_1.jpg"); + AddProductPicture(iPad, "product/ipad_2.jpg"); + AddProductPicture(iPad, "product/97-ipad-yellow.jpg"); + AddProductPicture(iPad, "product/97-ipad-turquoise.jpg"); + AddProductPicture(iPad, "product/97-ipad-lightblue.jpg"); + AddProductPicture(iPad, "product/97-ipad-purple.jpg"); + AddProductPicture(iPad, "product/97-ipad-mint.jpg"); + AddProductPicture(iPad, "product/97-ipad-rose.jpg"); + AddProductPicture(iPad, "product/97-ipad-spacegray.jpg"); + AddProductPicture(iPad, "product/97-ipad-gold.jpg"); + AddProductPicture(iPad, "product/97-ipad-silver.jpg"); + + iPad.ProductCategories.Add(new ProductCategory { Category = categories["Apple"], DisplayOrder = 1 }); + iPad.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Apple"], DisplayOrder = 1 }); + + iPad.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // offer type -> promotion + SpecificationAttributeOption = specAttributes[22].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + }); + + iPad.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // storage capacity -> 64gb + SpecificationAttributeOption = specAttributes[27].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) + }); + iPad.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // storage capacity -> 128gb + SpecificationAttributeOption = specAttributes[27].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + }); + iPad.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // operating system -> ios + SpecificationAttributeOption = specAttributes[5].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) + }); + + #endregion 9,7 IPad + + #endregion Category Apple + + #region Category Books + + #region Überman + + var booksUberMan = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Überman: The novel", + ShortDescription = "(Hardcover)", + FullDescription = "

From idiots to riches - and back ... Ever since it with my Greek financial advisors were no more delicious cookies to meetings, I should have known something. Was the last cookie it when I bought a Romanian forest funds and leveraged discount certificates on lean hogs - which is sort of a more stringent bet that the price of lean hogs will remain stable, and that's nothing special because it is also available for cattle and cotton and fat pig. Again and again and I joked Kosmas Nikiforos Sarantakos. About all the part-time seer who tremblingly put for fear the euro crisis gold coins under the salami slices of their frozen pizzas And then came the day that revealed to me in almost Sarantakos fraudulent casualness that my plan had not worked out really. 'Why all of a sudden> my plan', 'I heard myself asking yet, but it was in the garage I realized what that really meant minus 211.2 percent in my portfolio report: personal bankruptcy, gutter and Drug Addiction with subsequent loss of the incisors . Not even the study of my friend, I would still be able to finance. The only way out was to me as quickly as secretly again to draw from this unspeakable Greek shit - I had to be Überman! By far the bekloppteste story about 'idiot' Simon Peter! »Tommy Jaud – Deutschlands witzigste Seite.« Alex Dengler, Bild am Sonntag

", + Sku = "P-1003", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Überman: The novel", + Price = 16.99M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + TaxCategoryId = taxCategories[TaxNameBooks].Id + }; + + AddProductPicture(booksUberMan, "product/0000932_uberman-der-roman.jpeg"); + booksUberMan.ProductCategories.Add(new ProductCategory { Category = categories["SPIEGEL-Bestseller"], DisplayOrder = 1 }); + + booksUberMan.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + booksUberMan.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 7) + }); + booksUberMan.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + + #endregion Überman + + #region Gefangene des Himmels + + var booksGefangeneDesHimmels = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "The Prisoner of Heaven: A Novel", + ShortDescription = "(Hardcover)", + FullDescription = "

By Shadow of the Wind and The Angel's Game, the new large-Barcelona novel by Carlos Ruiz Zafón. - Barcelona, Christmas 1957th The bookseller Daniel Sempere and his friend Fermín be drawn again into a great adventure. In the continuation of his international success with Carlos Ruiz Zafón takes the reader on a fascinating journey into his Barcelona. Creepy and fascinating, with incredible suction power and humor, the novel, the story of Fermin, who 'rose from the dead, and the key to the future is.' Fermin's life story linking the threads of The Shadow of the Wind with those from The Angel's Game. A masterful puzzle that keeps the reader around the world in thrall.

Product Hardcover: 416 pages Publisher: S. Fischer Verlag; 1 edition (October 25, 2012) Language: German ISBN-10: 3,100,954,025 ISBN-13: 978-3100954022 Original title: El prisionero del cielo Size and / or weight: 21.4 x 13.6 cm x 4.4

", + ProductTemplateId = productTemplate.Id, + Sku = "P-1004", + AllowCustomerReviews = true, + Published = true, + MetaTitle = "The Prisoner of Heaven: A Novel", + Price = 22.99M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameBooks].Id + }; + + AddProductPicture(booksGefangeneDesHimmels, "product/0000935_der-gefangene-des-himmels-roman_300.jpeg"); + booksGefangeneDesHimmels.ProductCategories.Add(new ProductCategory { Category = categories["SPIEGEL-Bestseller"], DisplayOrder = 1 }); + + booksGefangeneDesHimmels.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Edition -> bound + SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + + booksGefangeneDesHimmels.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Category -> bound + SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 7) + }); + booksGefangeneDesHimmels.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Language -> German + SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + + #endregion Gefangene des Himmels + + #region Best grilling recipes + + var booksBestGrillingRecipes = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Best Grilling Recipes", + ShortDescription = "More Than 100 Regional Favorites Tested and Perfected for the Outdoor Cook (Hardcover)", + FullDescription = "

Take a winding cross-country trip and you'll discover barbecue shacks with offerings like tender-smoky Baltimore pit beef and saucy St. Louis pork steaks. To bring you the best of these hidden gems, along with all the classics, the editors of Cook's Country magazine scoured the country, then tested and perfected their favorites. HEre traditions large and small are brought into the backyard, from Hawaii's rotisserie favorite, the golden-hued Huli Huli Chicken, to fall-off-the-bone Chicago Barbecued Ribs. In Kansas City, they're all about the sauce, and for our saucy Kansas City Sticky Ribs, we found a surprise ingredient-root beer. We also tackle all the best sides.

Not sure where or how to start? This cookbook kicks off with an easy-to-follow primer that will get newcomers all fired up. Whether you want to entertain a crowd or just want to learn to make perfect burgers, Best Grilling Recipes shows you the way.

", + ProductTemplateId = productTemplate.Id, + Sku = "P-1005", + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Best Grilling Recipes", + Price = 27.00M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameBooks].Id + }; + + AddProductPicture(booksBestGrillingRecipes, "product/bestgrillingrecipes.jpg"); + booksBestGrillingRecipes.ProductCategories.Add(new ProductCategory { Category = categories["Cook and enjoy"], DisplayOrder = 1 }); + + booksBestGrillingRecipes.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Edition -> bound + SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + booksBestGrillingRecipes.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Category -> cook & bake + SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 8) + }); + booksBestGrillingRecipes.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Language -> German + SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) + }); + + #endregion Best grilling recipes + + #region Cooking for two + + var booksCookingForTwo = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Cooking for Two", + ShortDescription = "More Than 200 Foolproof Recipes for Weeknights and Special Occasions (Hardcover)", + FullDescription = "

In Cooking for Two, the test kitchen's goal was to take traditional recipes and cut them down to size to serve just twowith tailored cooking techniques and smart shopping tips that will cut down on wasted food and wasted money. Great lasagna starts to lose its luster when you're eating the leftovers for the fourth day in a row. While it may seem obvious that a recipe for four can simply be halved to work, our testing has proved that this is not always the case; cooking with smaller amounts of ingredients often requires different preparation techniques, cooking time, temperature, and the proportion of ingredients. This was especially true as we worked on scaled-down desserts; baking is an unforgiving science in which any changes in recipe amounts often called for changes in baking times and temperatures.

Hardcover: 352 pages
Publisher: America's Test Kitchen (May 2009)
Language: English
ISBN-10: 1933615435
ISBN-13: 978-1933615431

", + ProductTemplateId = productTemplate.Id, + Sku = "P-1006", + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Cooking for Two", + Price = 27.00M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = secondDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameBooks].Id + }; + + AddProductPicture(booksCookingForTwo, "product/cookingfortwo.jpg"); + booksCookingForTwo.ProductCategories.Add(new ProductCategory { Category = categories["Cook and enjoy"], DisplayOrder = 1 }); + + booksCookingForTwo.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Edition -> bound + SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + booksCookingForTwo.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Category -> cook & bake + SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 8) + }); + booksCookingForTwo.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Language -> German + SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) + }); + + #endregion Cooking for two + + #region Autos der Superlative + + var booksAutosDerSuperlative = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Cars of superlatives: The strongest, the first, the most beautiful, the fastest", + ShortDescription = "Hardcover", + FullDescription = "

For some, the car is only a useful means of transportation. For everyone else, there are 'cars - The Ultimate Guide' of art-connoisseur Michael Doerflinger. With authentic images, all important data and a lot of information can be presented to the fastest, most innovative, the strongest, the most unusual and the most successful examples of automotive history. A comprehensive manual for the specific reference and extensive browsing.

", + Sku = "P-1007", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Car of superlatives", + Price = 14.95M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameBooks].Id + }; + + AddProductPicture(booksAutosDerSuperlative, "product/0000944_autos-der-superlative-die-starksten-die-ersten-die-schonsten-die-schnellsten.jpeg"); + booksAutosDerSuperlative.ProductCategories.Add(new ProductCategory { Category = categories["Books"], DisplayOrder = 1 }); + + booksAutosDerSuperlative.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Edition -> bound + SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + booksAutosDerSuperlative.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Category -> cars + SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 6) + }); + booksAutosDerSuperlative.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Language -> German + SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + + #endregion Autos der Superlative + + #region Bildatlas Motorraeder + + var booksBildatlasMotorraeder = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Picture Atlas Motorcycles: With more than 350 brilliant images", + ShortDescription = "Hardcover", + FullDescription = "

Motorcycles are like no other means of transportation for the great dream of freedom and adventure. This richly illustrated atlas image portrayed in brilliant color photographs and informative text, the most famous bikes of the world's motorcycle history. From the primitive steam engine under the saddle of the late 19th Century up to the hugely powerful, equipped with the latest electronics and computer technology superbikes of today he is an impressive picture of the development and fabrication of noble and fast-paced motorcycles. The myth of the motorcycle is just as much investigated as a motorcycle as a modern lifestyle product of our time. Country-specific, company-historical background information and interesting stories and History about the people who preceded drove one of the seminal inventions of recent centuries and evolved, make this comprehensive illustrated book an incomparable reference for any motorcycle enthusiast and technology enthusiasts.

• Extensive history of the legendary models of all major motorcycle manufacturers worldwide
• With more than 350 brilliant color photographs and fascinating background information relating
• With informative drawings, stunning detail shots and explanatory info-boxes

content • 1817 1913: The beginning of a success story
• 1914 1945: mass mobility
• 1946 1990: Battle for the World Market
• In 1991: The modern motorcycle
• motorcycle cult object: From Transportation to Lifestyle

", + Sku = "P-1008", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Picture Atlas Motorcycles", + Price = 14.99M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameBooks].Id + }; + + AddProductPicture(booksBildatlasMotorraeder, "product/0000942_bildatlas-motorrader-mit-mehr-als-350-brillanten-abbildungen.jpeg"); + booksBildatlasMotorraeder.ProductCategories.Add(new ProductCategory { Category = categories["Books"], DisplayOrder = 1 }); + + booksBildatlasMotorraeder.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Edition -> bound + SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + booksBildatlasMotorraeder.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Category -> non-fiction + SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) + }); + booksBildatlasMotorraeder.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Language -> German + SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + + #endregion Bildatlas Motorraeder + + #region Auto Buch + + var booksAutoBuch = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "The Car Book. The great history with over 1200 models", + ShortDescription = "Hardcover", + FullDescription = "

Makes, models, milestones
The car - for some, a utensil for other expression of lifestyle, cult object and passion. Few inventions have changed their lives as well as the good of the automobile 125 years ago - one more reason for this extensive chronicle. The car-book brings the history of the automobile to life. It presents more than 1200 important models - Karl Benz 'Motorwagen about legendary cult car to advanced hybrid vehicles. It explains the milestones in engine technology and portrays the big brands and their designers. Characteristics from small cars to limousines and send racing each era invite you to browse and discover. The most comprehensive and bestbebildert illustrated book on the market - it would be any car lover!

Hardcover: 360 pages
Publisher: Dorling Kindersley Publishing (September 27, 2012)
Language: German
ISBN-10: 3,831,022,062
ISBN-13: 978-3831022069
Size and / or weight: 30.6 x 25.8 x 2.8 cm

", + Sku = "P-1009", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "The Car Book", + Price = 29.95M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameBooks].Id + }; + + AddProductPicture(booksAutoBuch, "product/0000947_das-auto-buch-die-grose-chronik-mit-uber-1200-modellen_300.jpeg"); + booksAutoBuch.ProductCategories.Add(new ProductCategory { Category = categories["Books"], DisplayOrder = 1 }); + + booksAutoBuch.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Edition -> bound + SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + booksAutoBuch.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Category -> non-fiction + SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) + }); + booksAutoBuch.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Language -> German + SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + + #endregion Auto Buch + + #region Fast cars + + var booksFastCars = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Fast Cars, Image Calendar 2013", + ShortDescription = "spiral bound", + FullDescription = "

Large Size: 48.5 x 34 cm.
This impressive picture calendar with silver ring binding thrilled with impressive photographs of exclusive sports cars. Who understands cars not only as a pure commercial vehicles, will find the most sought-after status symbols at all: fast cars are effectively set to the razor sharp and vivid photos in scene and convey freedom, speed, strength and the highest technical perfection. Starting with the 450-horsepower Maserati GranTurismo MC Stradale on the stylish, luxurious Aston Martin Virage Volante accompany up to the produced only in small numbers Mosler Photon MT900S the fast racer with style and elegance through the months.

Besides the calendar draws another picture to look at interesting details. There are the essential information on any sports car in the English language. After this year, the high-quality photos are framed an eye-catcher on the wall of every lover of fast cars. Even as a gift this beautiful years companion is wonderfully suited. 12 calendar pages, neutral and discreet held calendar. Printed on paper from sustainable forests. For lovers of luxury vintage cars also available in ALPHA EDITION: the large format image Classic Cars Calendar 2013: ISBN 9,783,840,733,376th

Spiral-bound: 14 pages
Publisher: Alpha Edition (June 1, 2012)
Language: German
ISBN-10: 3,840,733,383
ISBN-13: 978-3840733383
Size and / or weight: 48.8 x 34.2 x 0.6 cm

", + Sku = "P-1010", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Fast Cars", + Price = 16.95M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameBooks].Id + }; + + AddProductPicture(booksFastCars, "product/0000946_fast-cars-bildkalender-2013_300.jpeg"); + booksFastCars.ProductCategories.Add(new ProductCategory { Category = categories["Books"], DisplayOrder = 1 }); + + booksFastCars.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Edition -> bound + SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + booksFastCars.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Category -> cars + SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 6) + }); + booksFastCars.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Language -> German + SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + + #endregion Fast cars + + #region Motorrad Abenteuer + + var booksMotorradAbenteuer = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Motorcycle Adventures: Riding for travel enduros", + ShortDescription = "Hardcover", + FullDescription = "

Modern travel enduro bikes are ideal for adventure travel. Their technique is complex, their weight considerably. The driving behavior changes depending on the load and distance.

Before the tour starts, you should definitely attend a training course. This superbly illustrated book presents practical means of many informative series photos the right off-road driving in mud and sand, gravel and rock with and without luggage. In addition to the driving course full of information and tips on choosing the right motorcycle for travel planning and practical issues may be on the way.

", + Sku = "P-1011", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Motorcycle Adventures", + Price = 24.90M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = secondDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameBooks].Id + }; + + AddProductPicture(booksMotorradAbenteuer, "product/0000943_motorrad-abenteuer-fahrtechnik-fur-reise-enduros.jpeg"); + booksMotorradAbenteuer.ProductCategories.Add(new ProductCategory { Category = categories["Books"], DisplayOrder = 1 }); + + booksMotorradAbenteuer.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Edition -> bound + SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + booksMotorradAbenteuer.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Category -> cars + SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) + }); + booksMotorradAbenteuer.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Language -> German + SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + + #endregion Motorrad Abenteuer + + #endregion Category Books + + #region Category Digital Products & Instant Downloads + + #region Stone of the Wise + + var booksStoneOfTheWise = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Ebook 'Stone of the Wise' in 'Lorem ipsum'", + IsEsd = true, + ShortDescription = "E-Book, 465 pages", + FullDescription = "", + Sku = "P-6001", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Ebook 'Stone of the Wise' in 'Lorem ipsum'", + Price = 9.90M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsDownload = true, + HasSampleDownload = true, + TaxCategoryId = taxCategories[TaxNameDigitalGoods].Id + }; + + AddProductPicture(booksStoneOfTheWise, "product/stone_of_wisdom.jpg"); + booksStoneOfTheWise.ProductCategories.Add(new ProductCategory { Category = categories["Digital Products"], DisplayOrder = 1 }); + + booksStoneOfTheWise.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Edition -> bound + SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + booksStoneOfTheWise.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Category -> cars + SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 6) + }); + booksStoneOfTheWise.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 3, + // Language -> German + SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + + #endregion Stone of the Wise + + #region Antonio Vivaldi: Spring + + var instantDownloadVivaldi = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Antonio Vivaldi: spring", + IsEsd = true, + ShortDescription = "MP3, 320 kbit/s", + FullDescription = "

Antonio Vivaldi: Spring

Antonio Lucio Vivaldi (March 4, 1678 in Venice, † 28 July 1741 in Vienna) was a Venetian composer and violinist in the Baroque.

The Four Seasons (Le quattro stagioni Italian) is perhaps the most famous works of Antonio Vivaldi. It's four violin concertos with extra-musical programs, each portraying a concert season. This is the individual concerts one - probably written by Vivaldi himself - Sonnet preceded by consecutive letters in front of the lines and in the appropriate places in the score arrange the verbal description of the music.

Vivaldi had previously always been experimenting with non-musical programs, which often reflected in his tracks, the exact interpretation of the individual points score is unusual for him. His experience as a virtuoso violinist allowed him access to particularly effective playing techniques, as an opera composer, he had developed a strong sense of effects, both of which benefitted from him.

As the title suggests, especially to imitate natural phenomena - gentle winds, severe storms and thunderstorms are elements that are common to all four concerts. There are also various birds and even a dog, further human activities such as hunting, a barn dance, ice skating, including stumbling and falling to the heavy sleep of a drunkard.

The work dates from 1725 and is available in two print editions, which appeared more or less simultaneously published in Amsterdam and Paris.

", + Sku = "P-1016", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Antonio Vivaldi: spring", + Price = 1.99M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsDownload = true, + HasSampleDownload = true, + TaxCategoryId = taxCategories[TaxNameDigitalGoods].Id + }; + + AddProductPicture(instantDownloadVivaldi, "product/vivaldi.jpg"); + instantDownloadVivaldi.ProductCategories.Add(new ProductCategory { Category = categories["Digital Products"], DisplayOrder = 1 }); + + instantDownloadVivaldi.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // mp3 quality > 320 kbit/S + SpecificationAttributeOption = specAttributes[18].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + }); + instantDownloadVivaldi.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2, + // genre > classic + SpecificationAttributeOption = specAttributes[19].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 6) + }); + + #endregion Antonio Vivaldi: Spring + + #region Beethoven für Elise + + var instantDownloadBeethoven = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Ludwig van Beethoven: For Elise", + IsEsd = true, + ShortDescription = "Ludwig van Beethoven's most popular compositions", + FullDescription = "

The score was not published until 1867, 40 years after the composer's death in 1827. The discoverer of the piece, Ludwig Nohl, affirmed that the original autographed manuscript, now lost, was dated 27 April 1810.[4] The version of \"Für Elise\" we hear today is an earlier version that was transcribed by Ludwig Nohl. There is a later version, with drastic changes to the accompaniment which was transcribed from a later manuscript by Barry Cooper. The most notable difference is in the first theme, the left-hand arpeggios are delayed by a 16th note beat. There are a few extra bars in the transitional section into the B section; and finally, the rising A minor arpeggio figure is moved later into the piece. The tempo marking Poco Moto is believed to have been on the manuscript that Ludwig Nohl transcribed (now lost). The later version includes the marking Molto Grazioso. It is believed that Beethoven intended to add the piece to a cycle of bagatelles.[citation needed]

Therese Malfatti, widely believed to be the dedicatee of \"Für Elise\" The pianist and musicologist Luca Chiantore (es) argued in his thesis and his 2010 book Beethoven al piano that Beethoven might not have been the person who gave the piece the form that we know today. Chiantore suggested that the original signed manuscript, upon which Ludwig Nohl claimed to base his transcription, may never have existed.[5] On the other hand, the musicologist Barry Cooper stated, in a 1984 essay in The Musical Times, that one of two surviving sketches closely resembles the published version.[6]

", + Sku = "P-1017", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Ludwig van Beethoven: Für Elise", + ShowOnHomePage = true, + Price = 1.89M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsDownload = true, + HasSampleDownload = true, + TaxCategoryId = taxCategories[TaxNameDigitalGoods].Id + }; + + AddProductPicture(instantDownloadBeethoven, "product/Beethoven.jpg"); + instantDownloadBeethoven.ProductCategories.Add(new ProductCategory { Category = categories["Digital Products"], DisplayOrder = 1 }); + + instantDownloadBeethoven.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // mp3 quality > 320 kbit/S + SpecificationAttributeOption = specAttributes[18].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + }); + instantDownloadBeethoven.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2, + // genre > classic + SpecificationAttributeOption = specAttributes[19].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 6) + }); + + #endregion Beethoven für Elise + + #endregion Category Digital Products & Instant Downloads + + #region Category Watches + + #region Transocean Chronograph + + var transoceanChronograph = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "TRANSOCEAN CHRONOGRAPH", + ShortDescription = "The Transocean Chronograph interprets the factual aesthetics of classic chronographs of the 1950s and 1960s in a decidedly contemporary style.", + FullDescription = "

The Transocean Chronograph interprets the factual aesthetics of classic chronographs of the 1950s and 1960s in a decidedly contemporary style. The high-performance caliber 01, designed and manufactured entirely in the Breitling studios, works in its form, which is reduced to the essentials.

Caliber Breitling 01 (Manufactory caliber)
Movement Mechanically, Automatic
Power reserve Min. 70 hour
Chronograph 1/4-Seconds, 30 Minutes, 12 Hours
Half vibrations 28 800 a/h
Rubies 47 Rubies
Calendar Window
", + Sku = "P-9001", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "TRANSOCEAN CHRONOGRAPH", + ShowOnHomePage = true, + Price = 24110.00M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameJewelry].Id + }; + + AddProductPicture(transoceanChronograph, "product/transocean-chronograph.jpg"); + transoceanChronograph.ProductCategories.Add(new ProductCategory { Category = categories["Watches"], DisplayOrder = 1 }); + transoceanChronograph.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Breitling"], DisplayOrder = 1 }); + + transoceanChronograph.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // offer > promotion + SpecificationAttributeOption = specAttributes[22].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + }); + transoceanChronograph.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2, + // manufacturer > Breitling + SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 18) + }); + transoceanChronograph.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // housing > steel + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + transoceanChronograph.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // material -> leather + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) + }); + transoceanChronograph.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // Gender -> gentlemen + SpecificationAttributeOption = specAttributes[7].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + transoceanChronograph.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // movement -> mechanical, self winding + SpecificationAttributeOption = specAttributes[9].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + transoceanChronograph.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // diameter -> 44mm + SpecificationAttributeOption = specAttributes[24].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + }); + transoceanChronograph.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // closure -> folding clasp + SpecificationAttributeOption = specAttributes[25].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) + }); + + #endregion Transocean Chronograph + + #region TissotT Touch Expert Solar + + var tissotTTouchExpertSolar = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Tissot T-Touch Expert Solar", + ShortDescription = "The beam of the Tissot T-Touch Expert Solar on the dial ensures that the Super-LumiNova®-coated indexes and hands illuminate in the dark, and on the other hand, charges the battery of the watch. This model is a force package in every respect.", + FullDescription = "

The T-Touch Expert Solar is an important new model in the Tissot range.

Tissot’s pioneering spirit is what led to the creation of tactile watches in 1999.

Today, it is the first to present a touch-screen watch powered by solar energy, confirming its position as leader in tactile technology in watchmaking.

Extremely well designed, it showcases clean lines in both sports and timeless pieces.

Powered by solar energy with 25 features including weather forecasting, altimeter, second time zone and a compass it is the perfect travel companion.

", + Sku = "P-9002", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Tissot T-Touch Expert Solar", + ShowOnHomePage = true, + Price = 969.00M, + OldPrice = 990.00M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameJewelry].Id + }; + + AddProductPicture(tissotTTouchExpertSolar, "product/tissot-t-touch-expert-solar.jpg"); + AddProductPicture(tissotTTouchExpertSolar, "product/tissot-t-touch-expert-solar-t091_2.jpg"); + + tissotTTouchExpertSolar.ProductCategories.Add(new ProductCategory { Category = categories["Watches"], DisplayOrder = 1 }); + tissotTTouchExpertSolar.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Tissot"], DisplayOrder = 1 }); + + tissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // offer > best price + SpecificationAttributeOption = specAttributes[22].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 8) + }); + tissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2, + // manufacturer > Tissot + SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 17) + }); + tissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // housing > steel + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + tissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // material -> silicone + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 7) + }); + tissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // Gender -> gentlemen + SpecificationAttributeOption = specAttributes[7].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + tissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // movement -> Automatic, self-winding + SpecificationAttributeOption = specAttributes[9].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + tissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // diameter -> 44mm + SpecificationAttributeOption = specAttributes[24].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + }); + tissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // closure -> thorn close + SpecificationAttributeOption = specAttributes[25].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + }); + + #endregion TissotT Touch Expert Solar + + #region Seiko SRPA49K1 + + var seikoSRPA49K1 = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Seiko Mechanical Automatic SRPA49K1", + ShortDescription = "Seiko Mechanical Automatic SRPA49K1", + FullDescription = "

Seiko 5 Sports Automatic Watch SRPA49K1 SRPA49

  • Unidirectional Rotating Bezel
  • Day And Date Display
  • See Through Case Back
  • 100M Water Resistance
  • Stainless Steel Case
  • Automatic Movement
  • 24 Jewels
  • Caliber: 4R36
", + Sku = "P-9003", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Seiko Mechanical Automatic SRPA49K1", + ShowOnHomePage = true, + Price = 269.00M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameJewelry].Id + }; + + AddProductPicture(seikoSRPA49K1, "product/SeikoSRPA49K1.jpg"); + seikoSRPA49K1.ProductCategories.Add(new ProductCategory { Category = categories["Watches"], DisplayOrder = 1 }); + seikoSRPA49K1.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Seiko"], DisplayOrder = 1 }); + + seikoSRPA49K1.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // housing > steel + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + seikoSRPA49K1.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // material -> stainless steel + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + seikoSRPA49K1.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2, + // manufacturer > Seiko + SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 16) + }); + seikoSRPA49K1.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // Gender -> gentlemen + SpecificationAttributeOption = specAttributes[7].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + seikoSRPA49K1.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // movement -> quarz + SpecificationAttributeOption = specAttributes[9].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + seikoSRPA49K1.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // closure -> folding clasp + SpecificationAttributeOption = specAttributes[25].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) + }); + seikoSRPA49K1.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // diameter -> 44mm + SpecificationAttributeOption = specAttributes[24].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + }); + + #endregion Seiko SRPA49K1 + + #region Certina DS Podium Big Size + + var certinaDSPodiumBigSize = new Product + { + ProductType = ProductType.SimpleProduct, + Name = "Certina DS Podium Big Size", + ShortDescription = "C001.617.26.037.00", + FullDescription = "

Since 1888, Certina has maintained an enviable reputation for its excellent watches and reliable movements. From the time of its integration into the SMH (today's Swatch Group) in the early 1980s, every Certina has been equipped with a high-quality ETA movement.

In a quartz watch movement, high-frequency oscillations are generated in a tiny synthetic crystal, then divided down electronically to provide the extreme accuracy of the Certina internal clock. A battery supplies the necessary energy.

The quartz movement is sometimes equipped with an end-of-life (EOL) indicator. When the seconds hand begins moving in four-second increments, the battery should be replaced within two weeks.

An automatic watch movement is driven by a rotor. Arm and wrist movements spin the rotor, which in turn winds the main spring. Energy is continuously produced, eliminating the need for a battery. The rate precision therefore depends on a rigorous manufacturing process and the original calibration, as well as the lifestyle of the user.

Most automatic movements are driven by an offset rotor. To earn the title of chronometer, a watch must be equipped with a movement that has obtained an official rate certificate from the COSC (Contrôle Officiel Suisse des Chronomètres). To obtain this, precision tests in different positions and at different temperatures must be carried out. These tests take place over a 15-day period. Thermocompensated means that the effective temperature inside the watch is measured and taken into account when improving precision. This allows fluctuations in the rate precision of a normal quartz watch due to temperature variations to be reduced by several seconds a week. The precision is 20 times more accurate than on a normal quartz watch, i.e. +/- 10 seconds per year (0.07 seconds/day).

", + Sku = "P-9004", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Certina DS Podium Big Size", + ShowOnHomePage = true, + Price = 479.00M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = thirdDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameJewelry].Id + }; + + AddProductPicture(certinaDSPodiumBigSize, "product/certina_ds_podium_big.png"); + certinaDSPodiumBigSize.ProductCategories.Add(new ProductCategory { Category = categories["Watches"], DisplayOrder = 1 }); + certinaDSPodiumBigSize.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Certina"], DisplayOrder = 1 }); + + certinaDSPodiumBigSize.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 1, + // housing > steel + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + certinaDSPodiumBigSize.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // material -> leather + SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) + }); + certinaDSPodiumBigSize.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 2, + // manufacturer > Certina + SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 14) + }); + certinaDSPodiumBigSize.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // Gender -> gentlemen + SpecificationAttributeOption = specAttributes[7].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) + }); + certinaDSPodiumBigSize.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // movement -> quarz + SpecificationAttributeOption = specAttributes[9].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + }); + certinaDSPodiumBigSize.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // closure -> folding clasp + SpecificationAttributeOption = specAttributes[25].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) + }); + certinaDSPodiumBigSize.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + AllowFiltering = true, + ShowOnProductPage = true, + DisplayOrder = 5, + // diameter -> 40mm + SpecificationAttributeOption = specAttributes[24].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) + }); + + #endregion Certina DS Podium Big Size + + #endregion Category Watches + + #region Category Gaming + + #region PS3 + + var playstation3 = new Product + { + ProductType = ProductType.SimpleProduct, + Sku = "Sony-PS399000", + Name = "Playstation 4 Pro", + ShortDescription = "The Sony PlayStation 4 Pro is the multi media console for next-generation digital home entertainment. It offers the Blu-ray technology, which enables you to enjoy movies in high definition.", + FullDescription = "
  • PowerPC-base Core @5.2GHz
  • 1 VMX vector unit per core
  • 512KB L2 cache
  • 7 x SPE @5.2GHz
  • 7 x 128b 128 SIMD GPRs
  • 7 x 256MB SRAM for SPE
  • * 1 of 8 SPEs reserved for redundancy total floating point performance: 218 GFLOPS
  • 1.8 TFLOPS floating point performance
  • Full HD (up to 1080p) x 2 channels
  • Multi-way programmable parallel floating point shader pipelines
  • GPU: RSX @550MHz
  • 256MB XDR Main RAM @3.2GHz
  • 256MB GDDR3 VRAM @700MHz
  • Sound: Dolby 5.1ch, DTS, LPCM, etc. (Cell-base processing)
  • Wi-Fi: IEEE 802.11 b/g
  • USB: Front x 4, Rear x 2 (USB2.0)
  • Memory Stick: standard/Duo, PRO x 1
", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + //MetaTitle = "Playstation 4 Super Slim", + MetaTitle = "Playstation 4 Pro", + Price = 189.00M, + OldPrice = 199.99M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(playstation3, "product/ps4_w_controller.jpg", "ps4-w-controller"); + AddProductPicture(playstation3, "product/ps4_wo_controller.jpg", "ps4-wo-single"); + + playstation3.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); + playstation3.ProductCategories.Add(new ProductCategory { Category = categories["Gaming"], DisplayOrder = 4 }); + + #endregion PS3 + + #region Dualshock 4 Controller + + var dualshock4Controller = new Product + { + ProductType = ProductType.SimpleProduct, + Sku = "Sony-PS399004", + Name = "DUALSHOCK 4 Wireless Controller", + ShortDescription = "Revolutionary. Intuitive. Precise. A revolutionary controller for a new era of gaming, the DualShock 4 Wireless Controller features familiar PlayStation controls and innovative new additions, such as a touch pad, light bar, and more.", + FullDescription = "
  • Precision Controller for PlayStation 4: The feel, shape, and sensitivity of the DualShock 4’s analog sticks and trigger buttons have been enhanced to offer players absolute control for all games
  • Sharing at your Fingertips: The addition of the Share button makes sharing your greatest gaming moments as easy as a push of a button. Upload gameplay videos and screenshots directly from your system or live-stream your gameplay, all without disturbing the game in progress.
  • New ways to Play: Revolutionary features like the touch pad, integrated light bar, and built-in speaker offer exciting new ways to experience and interact with your games and its 3.5mm audio jack offers a practical personal audio solution for gamers who want to listen to their games in private.
  • Charge Efficiently: The DualShock 4 Wireless Controller can easily be recharged by plugging it into your PlayStation 4 system, even when on standby, or with any standard charger with a micro-USB port.
", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "DUALSHOCK 4 Wireless Controller", + Price = 54.90M, + OldPrice = 59.90M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(dualshock4Controller, "product/dualshock4.jpg"); + dualshock4Controller.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); + dualshock4Controller.ProductCategories.Add(new ProductCategory { Category = categories["Gaming Accessories"], DisplayOrder = 1 }); + + #endregion Dualshock 4 Controller + + #region Minecraft + + var gamingMinecraft = new Product + { + ProductType = ProductType.SimpleProduct, + //Sku = "Ubi-acreed3", + Sku = "PD-Minecraft4ps4", + Name = "Minecraft - Playstation 4 Edition", + ShortDescription = "Third-person action-adventure title set.", + FullDescription = "

Build! Craft! Explore!

The critically acclaimed Minecraft comes to PlayStation 4, offering bigger worlds and greater draw distance than the PS3 and PS Vita editions.

Create your own world, then, build, explore and conquer. When night falls the monsters appear, so be sure to build a shelter before they arrive.

The world is only limited by your imagination! Bigger worlds and greater draw distance than PS3 and PS Vita Editions Includes all features from the PS3 version Import your PS3 and PS Vita worlds to the PS4 Editition.

", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + //MetaTitle = "Assassin's Creed III", + MetaTitle = "Minecraft - Playstation 4 Edition", + + Price = 49.90M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(gamingMinecraft, "product/minecraft.jpg"); + gamingMinecraft.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); + gamingMinecraft.ProductCategories.Add(new ProductCategory { Category = categories["Games"], DisplayOrder = 4 }); + + #endregion Minecraft + + #region Bundle PS3, Controller & Minecraft + + var bundleMinecraftPs3 = new Product + { + ProductType = ProductType.BundledProduct, + Sku = "Sony-PS399105", + Name = "PlayStation 4 Minecraft Bundle", + ShortDescription = "100GB PlayStation®4 system, 2 × DUALSHOCK®4 wireless controller and Minecraft for PS4 Edition.", + FullDescription = + "
  • Processor

    • Processor Technology : Cell Broadband Engine™
  • General

    • Communication : Ethernet (10BASE-T, 100BASE-TX, 1000BASE-T IEEE 802.11 b/g Wi-Fi
      Bluetooth 2.0 (EDR)
    • Inputs and Outputs : USB 2.0 X 2
  • Graphics

    • Graphics Processor : RSX
  • Memory

    • Internal Memory : 256MB XDR Main RAM
      256MB GDDR3 VRAM
  • Power

    • Power Consumption (in Operation) : Approximately 250 watts
  • Storage

    • Storage Capacity : 2.5' Serial ATA (500GB)
  • Video

    • Resolution : 480i, 480p, 720p, 1080i, 1080p (24p/60p)
  • Weights and Measurements

    • Dimensions (Approx.) : Approximately 11.42\" (W) x 2.56\" (H) x 11.42\" (D) (290mm x 65mm x 290mm)
    • Weight (Approx.) : Approximately 7.055 lbs (3.2 kg)
", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "PlayStation 4 Minecraft Bundle", + Price = 269.97M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + ShowOnHomePage = true, + BundleTitleText = "Bundle includes", + BundlePerItemPricing = true, + BundlePerItemShoppingCart = true, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(bundleMinecraftPs3, "product/ps4_bundle_minecraft.jpg"); + bundleMinecraftPs3.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); + bundleMinecraftPs3.ProductCategories.Add(new ProductCategory { Category = categories["Gaming"], DisplayOrder = 1 }); + + #endregion Bundle PS3, Controller & Minecraft + + #region PS4 + + var playstation4 = new Product + { + ProductType = ProductType.SimpleProduct, + Sku = "Sony-PS410034", + Name = "PlayStation 4", + ShortDescription = "The best place to play. Working with some of the most creative minds in the industry, PlayStation®4 delivers breathtaking and unique gaming experiences.", + FullDescription = "

The power to perform.

PlayStation®4 was designed from the ground up to ensure that game creators can unleash their imaginations to develop the very best games and deliver new play experiences never before possible. With ultra-fast customized processors and 8GB of high-performance unified system memory, PS4™ is the home to games with rich, high-fidelity graphics and deeply immersive experiences that shatter expectations.

  • Processor

    • Processor Technology : Low power x86-64 AMD 'Jaguar', 8 cores
  • Software

    • Processor : Single-chip custom processor
  • Display

    • Display Technology : HDMI
      Digital Output (optical)
  • General

    • Ethernet ports x speed : Ethernet (10BASE-T, 100BASE-TX, 1000BASE-T); IEEE 802.11 b/g/n; Bluetooth® 2.1 (EDR)
    • Hard disk : Built-in
  • General Specifications

    • Video : BD 6xCAV
      DVD 8xCAV
  • Graphics

    • Graphics Processor : 1.84 TFLOPS, AMD Radeon™ Graphics Core Next engine
  • Interface

    • I/O Port : Super-Speed USB (USB 3.0), AUX
  • Memory

    • Internal Memory : GDDR5 8GB

", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "PlayStation 4", + Price = 399.99M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 3, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(playstation4, "product/sony_ps4.png", "sony-ps4"); + AddProductPicture(playstation4, "product/sony_dualshock4_wirelesscontroller.png"); + + playstation4.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); + playstation4.ProductCategories.Add(new ProductCategory { Category = categories["Gaming"], DisplayOrder = 5 }); + + #endregion PS4 + + #region PS4 Camera + + var playstation4Camera = new Product + { + ProductType = ProductType.SimpleProduct, + Sku = "Sony-PS410040", + Name = "PlayStation 4 Camera", + ShortDescription = "Play, challenge and share your epic gaming moments with PlayStation®Camera and your PS4™. Multiplayer is enhanced through immediate, crystal clear audio and picture-in-picture video sharing.", + FullDescription = "
  • When combined with the DualShock 4 Wireless Controller's light bar, the evolutionary 3D depth-sensing technology in the PlayStation Camera allows it to precisely track a player's position in the room.
  • From navigational voice commands to facial recognition, the PlayStation Camera adds incredible innovation to your gaming.
  • Automatically integrate a picture-in-picture video of yourself during gameplay broadcasts, and challenge your friends during play.
  • Never leave a friend hanging or miss a chance to taunt your opponents with voice chat that keeps the conversation going, whether it's between rounds, between games, or just while kicking back.
", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "PlayStation 4 Camera", + Price = 59.99M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(playstation4Camera, "product/sony_ps4_camera.png"); + playstation4Camera.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); + playstation4Camera.ProductCategories.Add(new ProductCategory { Category = categories["Gaming Accessories"], DisplayOrder = 3 }); + + #endregion PS4 Camera + + #region Bundle PS4, Controller & Camera + + var bundlePs4 = new Product + { + ProductType = ProductType.BundledProduct, + Sku = "Sony-PS410099", + Name = "PlayStation 4 Bundle", + ShortDescription = "PlayStation®4 system, DUALSHOCK®4 wireless controller and PS4 camera.", + FullDescription = + "

The best place to play

PlayStation 4 is the best place to play with dynamic, connected gaming, powerful graphics and speed, intelligent personalization, deeply integrated social capabilities, and innovative second-screen features. Combining unparalleled content, immersive gaming experiences, all of your favorite digital entertainment apps, and PlayStation exclusives, PS4 centers on gamers, enabling them to play when, where and how they want. PS4 enables the greatest game developers in the world to unlock their creativity and push the boundaries of play through a system that is tuned specifically to their needs.

Gamer focused, developer inspired

The PS4 system focuses on the gamer, ensuring that the very best games and the most immersive experiences are possible on the platform. The PS4 system enables the greatest game developers in the world to unlock their creativity and push the boundaries of play through a system that is tuned specifically to their needs. The PS4 system is centered around a powerful custom chip that contains eight x86-64 cores and a state of the art 1.84 TFLOPS graphics processor with 8 GB of ultra-fast GDDR5 unified system memory, easing game creation and increasing the richness of content achievable on the platform. The end result is new games with rich, high-fidelity graphics and deeply immersive experiences.

Personalized, curated content

The PS4 system has the ability to learn about your preferences. It will learn your likes and dislikes, allowing you to discover content pre-loaded and ready to go on your console in your favorite game genres or by your favorite creators. Players also can look over game-related information shared by friends, view friends’ gameplay with ease, or obtain information about recommended content, including games, TV shows and movies.

New DUALSHOCK controller

DUALSHOCK 4 features new innovations to deliver more immersive gaming experiences, including a highly sensitive six-axis sensor as well as a touch pad located on the top of the controller, which offers gamers completely new ways to play and interact with games.

Shared game experiences

Engage in endless personal challenges with your community and share your epic triumphs with the press of a button. Simply hit the SHARE button on the controller, scan through the last few minutes of gameplay, tag it and return to the game—the video uploads as you play. The PS4 system also enhances social spectating by enabling you to broadcast your gameplay in real-time.

Remote play

Remote Play on the PS4 system fully unlocks the PlayStation Vita system’s potential, making it the ultimate companion device. With the PS Vita system, gamers will be able to seamlessly play a range of PS4 titles on the beautiful 5-inch display over Wi-Fi access points in a local area network.

PlayStation app

The PlayStation App will enable iPhone, iPad, and Android-based smartphones and tablets to become second screens for the PS4 system. Once installed on these devices, players can view in-game items, purchase PS4 games and download them directly to the console at home, or remotely watch the gameplay of other gamers playing on their devices.

PlayStation Plus

Built to bring games and gamers together and fuel the next generation of gaming, PlayStation Plus helps you discover a world of exceptional gaming experiences. PlayStation Plus is a membership service that takes your gaming experience to the next level. Each month members receive an Instant Game Collection of top rated blockbuster and innovative Indie games, which they can download direct to their console.

", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "PlayStation 4 Bundle", + Price = 429.99M, + OldPrice = 449.99M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + BundleTitleText = "Bundle includes", + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(bundlePs4, "product/sony_ps4_bundle.png"); + bundlePs4.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); + bundlePs4.ProductCategories.Add(new ProductCategory { Category = categories["Gaming"], DisplayOrder = 2 }); + + #endregion Bundle PS4, Controller & Camera + + #region Accessories + + var groupAccessories = new Product + { + ProductType = ProductType.GroupedProduct, + Sku = "Sony-GroupAccessories", + Name = "Accessories for unlimited gaming experience", + ShortDescription = "The future of gaming is now with dynamic, connected gaming, powerful graphics and speed, intelligent personalization, deeply integrated social capabilities, and innovative second-screen features. The brilliant culmination of the most creative minds in the industry, PlayStation®4 delivers a unique gaming environment that will take your breath away.", + FullDescription = "
  • Immerse yourself in a new world of gameplay with powerful graphics and speed.
  • Eliminate lengthy load times of saved games with Suspend mode.
  • Immediately play digital titles without waiting for them to finish downloading thanks to background downloading and updating capability.
  • Instantly share images and videos of your favorite gameplay moments on Facebook with the SHARE button on the DUALSHOCK®4 controller.
  • Broadcast while you play in real-time through Ustream.
", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Accessories for unlimited gaming experience", + Price = 0.0M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 3, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + ShowOnHomePage = true, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(groupAccessories, "category/gaming_accessories.png"); + groupAccessories.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); + groupAccessories.ProductCategories.Add(new ProductCategory { Category = categories["Gaming"], DisplayOrder = 3 }); + + #endregion Accessories + + #region PS3 & Prince of Persia + + var gamingPrinceOfPersia = new Product + { + ProductType = ProductType.SimpleProduct, + Sku = "Ubi-princepersia", + Name = "Prince of Persia \"The Forgotten Sands\"", + ShortDescription = "Play the epic story of the heroic Prince as he fights and outwits his enemies in order to save his kingdom.", + FullDescription = "

This game marks the return to the Prince of Persia® Sands of Time storyline. Prince of Persia: The Forgotten Sands™ will feature many of the fan-favorite elements from the original series as well as new gameplay innovations that gamers have come to expect from Prince of Persia.

Experience the story, setting, and gameplay in this return to the Sands of Time universe as we follow the original Prince of Persia through a new untold chapter.

Created by Ubisoft Montreal, the team that brought you various Prince of Persia® and Assassin’s Creed® games, Prince of Persia The Forgotten Sands™ has been over 2 years in the making.

", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Prince of Persia", + Price = 39.90M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(gamingPrinceOfPersia, "product/princeofpersia.jpg"); + gamingPrinceOfPersia.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Ubisoft"], DisplayOrder = 1 }); + gamingPrinceOfPersia.ProductCategories.Add(new ProductCategory { Category = categories["Games"], DisplayOrder = 2 }); + + #endregion PS3 & Prince of Persia + + #region Horizon Zero Down + + var gamingHorizonZeroDown = new Product + { + ProductType = ProductType.SimpleProduct, + //Sku = "Ubi-princepersia", + Sku = "PD-ZeroDown4PS4", + Name = "Horizon Zero Dawn - PlayStation 4", + ShortDescription = "Experience A Vibrant, Lush World Inhabited By Mysterious Mechanized Creatures", + FullDescription = "
  • A Lush Post-Apocalyptic World – How have machines dominated this world, and what is their purpose? What happened to the civilization here before? Scour every corner of a realm filled with ancient relics and mysterious buildings in order to uncover your past and unearth the many secrets of a forgotten land.
  • Nature and Machines Collide – Horizon Zero Dawn juxtaposes two contrasting elements, taking a vibrant world rich with beautiful nature and filling it with awe-inspiring highly advanced technology. This marriage creates a dynamic combination for both exploration and gameplay.
  • Defy Overwhelming Odds – The foundation of combat in Horizon Zero Dawn is built upon the speed and cunning of Aloy versus the raw strength and size of the machines. In order to overcome a much larger and technologically superior enemy, Aloy must use every ounce of her knowledge, intelligence, and agility to survive each encounter.
  • Cutting Edge Open World Tech – Stunningly detailed forests, imposing mountains, and atmospheric ruins of a bygone civilization meld together in a landscape that is alive with changing weather systems and a full day/night cycle.
", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Horizon Zero Dawn - PlayStation 4", + Price = 69.90M, + OldPrice = 79.90M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(gamingHorizonZeroDown, "product/horizon.jpg"); + gamingHorizonZeroDown.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); + gamingHorizonZeroDown.ProductCategories.Add(new ProductCategory { Category = categories["Games"], DisplayOrder = 2 }); + + #endregion Horizon Zero Down + + #region FIFA 17 + + var gamingFifa17 = new Product + { + ProductType = ProductType.SimpleProduct, + //Sku = "Ubi-princepersia", + Sku = "PD-Fifa17", + Name = "FIFA 17 - PlayStation 4", + ShortDescription = "Powered by Frostbite", + FullDescription = "
  • Powered by Frostbite: One of the industry’s leading game engines, Frostbite delivers authentic, true-to-life action, takes players to new football worlds, and introduces fans to characters full of depth and emotion in FIFA 17.
  • The Journey: For the first time ever in FIFA, live your story on and off the pitch as the Premier League’s next rising star, Alex Hunter. Play on any club in the premier league, for authentic managers and alongside some of the best players on the planet. Experience brand new worlds in FIFA 17, all while navigating your way through the emotional highs and lows of The Journey.
  • Own Every Moment: Complete innovation in the way players think and move, physically interact with opponents, and execute in attack puts you in complete control of every moment on the pitch.
", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "FIFA 17 - PlayStation 4", + Price = 79.90M, + OldPrice = 89.90M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(gamingFifa17, "product/fifa17.jpg"); + gamingFifa17.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["EA Sports"], DisplayOrder = 1 }); + gamingFifa17.ProductCategories.Add(new ProductCategory { Category = categories["Games"], DisplayOrder = 2 }); + + #endregion FIFA 17 + + #region Lego Worlds + + var gamingLegoWorlds = new Product + { + ProductType = ProductType.SimpleProduct, + Sku = "Gaming-Lego-001", + Name = "LEGO Worlds - PlayStation 4", + ShortDescription = "Experience a galaxy of Worlds made entirely from LEGO bricks.", + FullDescription = "
  • Experience a galaxy of Worlds made entirely from LEGO bricks.
  • LEGO Worlds is an open environment of procedurally-generated Worlds made entirely of LEGO bricks which you can freely manipulate and dynamically populate with LEGO models.
  • Create anything you can imagine one brick at a time, or use large-scale landscaping tools to create vast mountain ranges and dot your world with tropical islands.
  • Explore using helicopters, dragons, motorbikes or even gorillas and unlock treasures that enhance your gameplay.
  • Watch your creations come to life through characters and creatures that interact with you and each other in unexpected ways.

", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "LEGO Worlds - PlayStation 4", + Price = 29.90M, + OldPrice = 34.90M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(gamingLegoWorlds, "product/legoworlds.jpg"); + gamingLegoWorlds.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Warner Home Video Games"], DisplayOrder = 1 }); + gamingLegoWorlds.ProductCategories.Add(new ProductCategory { Category = categories["Games"], DisplayOrder = 3 }); + + #endregion Lego Worlds + + #region XBox One S + + var xBoxOneS = new Product + { + ProductType = ProductType.SimpleProduct, + Sku = "Microsoft-xbox1s", + Name = "Xbox One S 500 GB Konsole", + ShortDescription = "Genieße über 100 Spiele, die es nur für die Konsole gibt, sowie eine ständig größer werdende Bibliothek an Xbox 360-Spielen auf der Xbox One S im neuen Design – der einzigen Konsole mit 4K Ultra HD Blu-ray, 4K-Videostreaming und HDR. Streame deine Lieblingsfilme und -sendungen in atemberaubendem 4K Ultra HD. Spiele Blockbuster wie Gears of War 4 und Battlefield 1 mit Freunden auf Xbox Live, dem schnellsten und zuverlässigsten Gaming-Netzwerk.", + FullDescription = "
  • Die ultimativen Spiele und 4K-Entertainment-System.
  • 40 % kompaktere Konsole
    Lasse dich nicht von der Größe täuschen. Mit integriertem Netzteil und bis zu 2 TB Speicherplatz ist die Xbox One S die fortschrittlichste Xbox überhaupt.
  • Der beste Controller - jetzt noch besser
    Der neue Xbox Wireless Controller bietet ein schlankes, optimiertes Design, texturierte Grip - Fläche und Bluetooth zum Spielen auf Windows 10 Geräten. Genieße individuelle Tastenbelegung und verbesserte drahtlose Reichweite und stecke jeden kompatiblen Kopfhörer mit der 3, 5 mm Stereo - Headset - Buchse ein.
", + ProductTemplateId = productTemplate.Id, + AllowCustomerReviews = true, + Published = true, + MetaTitle = "Xbox One S", + Price = 279.99M, + OldPrice = 279.99M, + ManageInventoryMethod = ManageInventoryMethod.DontManageStock, + OrderMinimumQuantity = 1, + OrderMaximumQuantity = 10000, + StockQuantity = 10000, + NotifyAdminForQuantityBelow = 1, + AllowBackInStockSubscriptions = false, + IsShipEnabled = true, + DeliveryTimeId = firstDeliveryTime.Id, + TaxCategoryId = taxCategories[TaxNameElectronics].Id + }; + + AddProductPicture(xBoxOneS, "product/xbox_one_s_1.png", "microsoft-xbox-one-s-1"); + xBoxOneS.ProductCategories.Add(new ProductCategory { Category = categories["Gaming"], DisplayOrder = 1 }); + xBoxOneS.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Microsoft"], DisplayOrder = 1 }); + + #endregion XBox One S + + #endregion Category Gaming + + var entities = new List + { + transoceanChronograph, tissotTTouchExpertSolar, seikoSRPA49K1, certinaDSPodiumBigSize, titleistSM6TourChrome, + titleistProV1x, gbbEpicSubZeroDriver, supremeGolfball, nikeStrikeFootball, nikeEvoPowerBall, + torfabrikOfficialGameBall, adidasTangoSalaBall, allCourtBasketball, evolutionBasketball, appleProHipsterBundle, + iPad, airPods, iPhonePlus, watchSeries2, giftCard10, giftCard25, giftCard50, giftCard100, + booksUberMan, booksGefangeneDesHimmels, booksBestGrillingRecipes, booksCookingForTwo, booksStoneOfTheWise, + booksAutosDerSuperlative, booksBildatlasMotorraeder, booksAutoBuch, booksFastCars, booksMotorradAbenteuer, + instantDownloadVivaldi, instantDownloadBeethoven, playstation3, bundleMinecraftPs3, playstation4, + dualshock4Controller, playstation4Camera, bundlePs4, gamingMinecraft, groupAccessories, gamingPrinceOfPersia, + gamingLegoWorlds, gamingHorizonZeroDown, gamingFifa17, xBoxOneS + }; + + entities.AddRange(GetFashionProducts(categories, manufacturers, specAttributes)); + entities.AddRange(GetFurnitureProducts(categories, specAttributes)); + + this.Alter(entities); + return entities; + } + + public IList ProductBundleItems() + { + var products = _ctx.Set().ToList().ToDictionarySafe(x => x.Sku, x => x); + + #region Bundles Apple + + var BundleAppleProHipster = _ctx.Set().First(x => x.Sku == "P-2005-Bundle"); + + var bundleItemIphonePlus = new ProductBundleItem() + { + BundleProduct = BundleAppleProHipster, + Product = products["P-2001"], + Quantity = 1, + Discount = 40.0M, + Visible = true, + Published = true, + DisplayOrder = 1 + }; + + var bundleItemWatchSeries2 = new ProductBundleItem() + { + BundleProduct = BundleAppleProHipster, + Product = products["P-2002"], + Quantity = 2, + Discount = 30.0M, + Visible = true, + Published = true, + DisplayOrder = 2 + }; + + var bundleItemAirPods = new ProductBundleItem() + { + BundleProduct = BundleAppleProHipster, + Product = products["P-2003"], + Quantity = 1, + Discount = 30.0M, + Visible = true, + Published = true, + DisplayOrder = 3 + }; + + var bundleItemIpad = new ProductBundleItem() + { + BundleProduct = BundleAppleProHipster, + Product = products["P-2004"], + Quantity = 1, + Discount = 30.0M, + Visible = true, + Published = true, + DisplayOrder = 3 + }; + + #endregion Bundles Apple + + #region Bundles Gaming + + var bundlePs4Minecraft = products["Sony-PS399105"]; + + var bundleItemPs4Minecraft1 = new ProductBundleItem() + { + BundleProduct = bundlePs4Minecraft, + Product = products["Sony-PS399000"], + Quantity = 1, + Discount = 20.0M, + Visible = true, + Published = true, + DisplayOrder = 1 + }; + + var bundleItemPs4Minecraft2 = new ProductBundleItem() + { + BundleProduct = bundlePs4Minecraft, + Product = products["Sony-PS399004"], + Quantity = 2, + Discount = 30.0M, + Visible = true, + Published = true, + DisplayOrder = 2 + }; + + var bundleItemPs4Minecraft3 = new ProductBundleItem() + { + BundleProduct = bundlePs4Minecraft, + Product = products["PD-Minecraft4ps4"], + Quantity = 1, + Discount = 20.0M, + Visible = true, + Published = true, + DisplayOrder = 3 + }; + + + var bundlePs4 = products["Sony-PS410099"]; + + var bundleItemPs41 = new ProductBundleItem + { + BundleProduct = bundlePs4, + Product = products["Sony-PS410034"], + Quantity = 1, + Visible = true, + Published = true, + DisplayOrder = 1 + }; + + var bundleItemPs42 = new ProductBundleItem + { + BundleProduct = bundlePs4, + Product = products["Sony-PS399004"], + Quantity = 1, + Visible = true, + Published = true, + DisplayOrder = 2 + }; + + var bundleItemPs43 = new ProductBundleItem + { + BundleProduct = bundlePs4, + Product = products["Sony-PS410040"], + Quantity = 1, + Visible = true, + Published = true, + DisplayOrder = 3 + }; + + #endregion Bundles Gaming + + var entities = new List + { + bundleItemPs4Minecraft1, bundleItemPs4Minecraft2, bundleItemPs4Minecraft3, bundleItemPs41, bundleItemPs42, + bundleItemPs43, bundleItemIphonePlus, bundleItemWatchSeries2, bundleItemAirPods, bundleItemIpad + }; + + this.Alter(entities); + return entities; + } + + public void AddDownloads(IList savedProducts) + { + // Sample downloads. + var sampleDownloadSkus = new List { "P-1017", "P-1016", "P-6001" }; + var sampleDownloadProducts = savedProducts + .Where(x => sampleDownloadSkus.Contains(x.Sku)) + .ToDictionary(x => x.Sku); + + var now = DateTime.UtcNow; + + foreach (var product in sampleDownloadProducts.Values) + { + if (product.Sku.IsCaseInsensitiveEqual("P-1017")) + { + var buffer = File.ReadAllBytes(_sampleImagesPath + "download/beethoven-fur-elise.mp3"); + product.SampleDownload = new Download + { + EntityId = product.Id, + EntityName = nameof(Product), + DownloadGuid = Guid.NewGuid(), + MediaFile = new MediaFile + { + Name = "beethoven-fur-elise.mp3", + MediaType = "audio", + MimeType = "audio/mp3", + Extension = "mp3", + Size = buffer.Length, + UpdatedOnUtc = now, + CreatedOnUtc = now, + MediaStorage = new MediaStorage { Data = buffer } + }, + UpdatedOnUtc = now + }; + } + else if (product.Sku.IsCaseInsensitiveEqual("P-1016")) + { + var buffer = File.ReadAllBytes(_sampleImagesPath + "download/vivaldi-four-seasons-spring.mp3"); + product.SampleDownload = new Download + { + EntityId = product.Id, + EntityName = nameof(Product), + DownloadGuid = Guid.NewGuid(), + MediaFile = new MediaFile + { + Name = "vivaldi-four-seasons-spring.mp3", + MediaType = "audio", + MimeType = "audio/mp3", + Extension = "mp3", + Size = buffer.Length, + UpdatedOnUtc = now, + CreatedOnUtc = now, + MediaStorage = new MediaStorage { Data = buffer } + }, + UpdatedOnUtc = DateTime.UtcNow + }; + } + else if (product.Sku.IsCaseInsensitiveEqual("P-6001")) + { + var buffer = File.ReadAllBytes(_sampleImagesPath + "download/Stone_of_the_wise_preview.pdf"); + product.SampleDownload = new Download + { + EntityId = product.Id, + EntityName = nameof(Product), + DownloadGuid = Guid.NewGuid(), + MediaFile = new MediaFile + { + Name = "Stone_of_the_wise_preview.pdf", + MediaType = "document", + MimeType = "application/pdf", + Extension = "pdf", + Size = buffer.Length, + UpdatedOnUtc = now, + CreatedOnUtc = now, + MediaStorage = new MediaStorage { Data = buffer } + }, + UpdatedOnUtc = DateTime.UtcNow + }; + } + } + + _ctx.SaveChanges(); + } + + public void AssignGroupedProducts(IList savedProducts) + { + int productGamingAccessoriesId = savedProducts.First(x => x.Sku == "Sony-GroupAccessories").Id; + var gamingAccessoriesSkus = new List() { "Sony-PS399004", "PD-Minecraft4ps4", "Sony-PS410037", "Sony-PS410040" }; + + savedProducts + .Where(x => gamingAccessoriesSkus.Contains(x.Sku)) + .ToList() + .Each(x => { x.ParentGroupedProductId = productGamingAccessoriesId; }); + + _ctx.SaveChanges(); + } + } +} diff --git a/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.SpecificationAttributes.cs b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.SpecificationAttributes.cs new file mode 100644 index 0000000000..e8deecb501 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.SpecificationAttributes.cs @@ -0,0 +1,1089 @@ +using System.Collections.Generic; +using SmartStore.Core.Domain.Catalog; + +namespace SmartStore.Data.Setup +{ + public abstract partial class InvariantSeedData + { + public IList SpecificationAttributes() + { + #region sa1 CPU-Manufacturer + + var sa1 = new SpecificationAttribute + { + Name = "CPU-Manufacturer", + DisplayOrder = 1, + }; + sa1.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "AMD", + DisplayOrder = 1, + }); + sa1.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Intel", + DisplayOrder = 2, + }); + sa1.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "ARM", + DisplayOrder = 3, + }); + sa1.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Samsung", + DisplayOrder = 4, + }); + sa1.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Apple", + DisplayOrder = 5, + }); + + #endregion sa1 CPU-Manufacturer + + #region sa2 color + + var sa2 = new SpecificationAttribute + { + Name = "Color", + DisplayOrder = 2, + }; + sa2.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "white", + DisplayOrder = 1, + }); + sa2.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "black", + DisplayOrder = 2, + }); + sa2.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "beige", + DisplayOrder = 3, + }); + sa2.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "red", + DisplayOrder = 4, + }); + sa2.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "blue", + DisplayOrder = 5, + }); + sa2.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "green", + DisplayOrder = 6, + }); + sa2.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "yellow", + DisplayOrder = 7, + }); + + #endregion sa2 color + + #region sa3 harddisk capacity + + var sa3 = new SpecificationAttribute + { + Name = "Harddisk capacity", + DisplayOrder = 3, + }; + sa3.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "250 GB", + DisplayOrder = 1, + }); + sa3.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "500 GB", + DisplayOrder = 2, + }); + sa3.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "750 GB", + DisplayOrder = 3, + }); + sa3.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "1000 GB", + DisplayOrder = 4, + }); + sa3.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "1500 GB", + DisplayOrder = 5, + }); + + #endregion sa3 harddisk capacity + + #region sa4 ram + + var sa4 = new SpecificationAttribute + { + Name = "RAM", + DisplayOrder = 4, + }; + sa4.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "4 GB", + DisplayOrder = 1, + }); + sa4.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "8 GB", + DisplayOrder = 2, + }); + sa4.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "13 GB", + DisplayOrder = 3, + }); + sa4.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "2 GB", + DisplayOrder = 4, + }); + sa4.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "1500 GB", + DisplayOrder = 5, + }); + + #endregion sa4 ram + + #region sa5 Operating System + + var sa5 = new SpecificationAttribute + { + Name = "Operating System", + DisplayOrder = 5, + }; + sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Windows 7 32 Bit", + DisplayOrder = 1, + }); + sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Windows 7 64 Bit", + DisplayOrder = 2, + }); + sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Windows 8 32 Bit", + DisplayOrder = 3, + }); + sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Windows 8 64 Bit", + DisplayOrder = 4, + }); + sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Linux", + DisplayOrder = 5, + }); + sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Mac OS", + DisplayOrder = 6, + }); + sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Android 2", + DisplayOrder = 7, + }); + sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Android 4", + DisplayOrder = 8, + }); + sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "iOS", + DisplayOrder = 9, + }); + + #endregion sa5 Operating System + + #region sa6 ports + + var sa6 = new SpecificationAttribute + { + Name = "Ports", + DisplayOrder = 6, + }; + sa6.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "USB 2.0", + DisplayOrder = 1, + }); + sa6.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "USB 3.0", + DisplayOrder = 2, + }); + sa6.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Firewire", + DisplayOrder = 3, + }); + sa6.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "HDMI", + DisplayOrder = 4, + }); + sa6.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "DVI", + DisplayOrder = 5, + }); + sa6.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "VGA", + DisplayOrder = 6, + }); + sa6.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Ethernet/RJ45", + DisplayOrder = 7, + }); + + #endregion sa6 ports + + #region sa7 Gender + + var sa7 = new SpecificationAttribute + { + Name = "Gender", + DisplayOrder = 7, + }; + sa7.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "gentlemen", + DisplayOrder = 1, + }); + sa7.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "ladies", + DisplayOrder = 2, + }); + sa7.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "unisex", + DisplayOrder = 3, + }); + + #endregion sa7 Gender + + #region sa8 material + + var sa8 = new SpecificationAttribute + { + Name = "Material", + DisplayOrder = 8, + }; + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "stainless steel", + DisplayOrder = 1, + }); + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "titanium", + DisplayOrder = 2, + }); + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "plastic", + DisplayOrder = 3, + }); + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "aluminium", + DisplayOrder = 4, + }); + + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "leather", + DisplayOrder = 5, + }); + + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "nylon", + DisplayOrder = 6, + }); + + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "silicone", + DisplayOrder = 7, + }); + + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "ceramic", + DisplayOrder = 8, + }); + + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption + { + Name = "cotton", + DisplayOrder = 9, + }); + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption + { + Name = "100% organic cotton", + DisplayOrder = 10, + }); + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption + { + Name = "polyamide", + DisplayOrder = 11, + }); + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption + { + Name = "rubber", + DisplayOrder = 12, + }); + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption + { + Name = "wood", + DisplayOrder = 13, + }); + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption + { + Name = "glass", + DisplayOrder = 14, + }); + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption + { + Name = "spandex", + DisplayOrder = 15, + }); + sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption + { + Name = "polyester", + DisplayOrder = 16, + }); + + #endregion sa8 material + + #region sa9 movement + + var sa9 = new SpecificationAttribute + { + Name = "Movement", + DisplayOrder = 9, + }; + sa9.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "mechanical, self winding", + DisplayOrder = 1, + }); + sa9.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "mechanical", + DisplayOrder = 2, + }); + sa9.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "quarz, battery operated", + DisplayOrder = 3, + }); + + #endregion sa9 movement + + #region sa10 clasp + + var sa10 = new SpecificationAttribute + { + Name = "Clasp", + DisplayOrder = 10, + }; + sa10.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "glidelock", + DisplayOrder = 1, + }); + sa10.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "folding clasp", + DisplayOrder = 2, + }); + sa10.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "thorn close", + DisplayOrder = 3, + }); + + #endregion sa10 clasp + + #region sa11 window material + + var sa11 = new SpecificationAttribute + { + Name = "Window material", + DisplayOrder = 11, + }; + sa11.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "mineral", + DisplayOrder = 1, + }); + sa11.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "sapphire", + DisplayOrder = 2, + }); + + #endregion sa11 window material + + #region sa12 language + + var sa12 = new SpecificationAttribute + { + Name = "Language", + DisplayOrder = 12, + }; + sa12.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "german", + DisplayOrder = 1, + }); + sa12.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "english", + DisplayOrder = 2, + }); + sa12.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "french", + DisplayOrder = 3, + }); + sa12.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "italian", + DisplayOrder = 4, + }); + + #endregion sa12 language + + #region sa13 edition + + var sa13 = new SpecificationAttribute + { + Name = "Edition", + DisplayOrder = 13, + }; + sa13.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "bound", + DisplayOrder = 1, + }); + sa13.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "paperback", + DisplayOrder = 2, + }); + + #endregion sa13 edition + + #region sa14 category + + var sa14 = new SpecificationAttribute + { + Name = "Category", + DisplayOrder = 14, + }; + sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "adventure", + DisplayOrder = 1, + }); + sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "fantasy & science fiction", + DisplayOrder = 2, + }); + sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "history", + DisplayOrder = 3, + }); + sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "internet & computer", + DisplayOrder = 4, + }); + sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "thriller", + DisplayOrder = 5, + }); + sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "cars", + DisplayOrder = 6, + }); + sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "novel", + DisplayOrder = 7, + }); + sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "cook and bake", + DisplayOrder = 8, + }); + sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "non-fiction", + DisplayOrder = 9, + }); + + #endregion sa14 category + + #region sa15 Computer-type + + var sa15 = new SpecificationAttribute + { + Name = "Computer-type", + DisplayOrder = 15, + }; + sa15.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "desktop", + DisplayOrder = 1, + }); + sa15.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "all-in-one", + DisplayOrder = 2, + }); + sa15.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "laptop", + DisplayOrder = 3, + }); + sa15.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "tablet", + DisplayOrder = 4, + }); + + #endregion sa15 Computer-type + + #region sa16 type of mass-storage + + var sa16 = new SpecificationAttribute + { + Name = "Type of mass-storage", + DisplayOrder = 16, + }; + sa16.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "HDD", + DisplayOrder = 1, + }); + sa16.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "SSD", + DisplayOrder = 2, + }); + sa16.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Flash", + DisplayOrder = 3, + }); + + #endregion sa16 type of mass-storage + + #region sa17 Size (ext. HDD) + + var sa17 = new SpecificationAttribute + { + Name = "Size (ext. HDD)", + DisplayOrder = 17, + }; + sa17.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "3.5", + DisplayOrder = 1, + }); + sa17.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "2.5", + DisplayOrder = 2, + }); + + #endregion sa17 Size (ext. HDD) + + #region sa18 MP3 quality + + var sa18 = new SpecificationAttribute + { + Name = "MP3 quality", + DisplayOrder = 18, + }; + sa18.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "128 kbit/s", + DisplayOrder = 1, + }); + sa18.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "256 kbit/s", + DisplayOrder = 2, + }); + sa18.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "320 kbit/s", + DisplayOrder = 3, + }); + + #endregion sa18 MP3 quality + + #region sa19 music genre + + var sa19 = new SpecificationAttribute + { + Name = "Music genre", + DisplayOrder = 19, + }; + sa19.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "blues", + DisplayOrder = 1, + }); + sa19.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "jazz", + DisplayOrder = 2, + }); + sa19.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "disco", + DisplayOrder = 3, + }); + sa19.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "pop", + DisplayOrder = 4, + }); + sa19.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "funk", + DisplayOrder = 5, + }); + sa19.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "classical", + DisplayOrder = 6, + }); + sa19.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "R&B", + DisplayOrder = 7, + }); + + #endregion sa19 music genre + + #region sa20 manufacturer + + var sa20 = new SpecificationAttribute + { + Name = "Manufacturer", + DisplayOrder = 20, + }; + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Apple", + DisplayOrder = 1, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Samsung", + DisplayOrder = 2, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "HTC", + DisplayOrder = 3, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "LG", + DisplayOrder = 4, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Motorola", + DisplayOrder = 5, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Nokia", + DisplayOrder = 6, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Sony", + DisplayOrder = 7, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Blackberry", + DisplayOrder = 8, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Microsoft", + DisplayOrder = 9, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "HP", + DisplayOrder = 10, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Canon", + DisplayOrder = 11, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Casio", + DisplayOrder = 12, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Panasonic", + DisplayOrder = 13, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Certina", + DisplayOrder = 14, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Festina", + DisplayOrder = 15, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Seiko", + DisplayOrder = 16, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Tissot", + DisplayOrder = 17, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Breitling", + DisplayOrder = 18, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Adidas", + DisplayOrder = 19, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Nike", + DisplayOrder = 20, + }); + sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Puma", + DisplayOrder = 21, + }); + + #endregion sa20 manufacturer + + #region sa21 Watches for whom + + var sa21 = new SpecificationAttribute + { + Name = "For whom", + DisplayOrder = 21, + }; + sa21.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "For him", + DisplayOrder = 1, + }); + sa21.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "For her", + DisplayOrder = 2, + }); + + #endregion sa11 Watches for whom + + #region sa22 Offer + + var sa22 = new SpecificationAttribute + { + Name = "Offer", + DisplayOrder = 22, + }; + + sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Clearance", + DisplayOrder = 1, + }); + + sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Permanent low price", + DisplayOrder = 2, + }); + + sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Promotion", + DisplayOrder = 3, + }); + + sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Reduced price", + DisplayOrder = 4, + }); + + sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Special Buy", + DisplayOrder = 5, + }); + + sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Offer of the day", + DisplayOrder = 6, + }); + + sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Weekly offer", + DisplayOrder = 7, + }); + + sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Best Price", + DisplayOrder = 8, + }); + + #endregion sa22 Offer + + #region sa23 Size + + var sa23 = new SpecificationAttribute + { + Name = "Size", + DisplayOrder = 23, + }; + + sa23.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "XS", + DisplayOrder = 1, + }); + + sa23.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "S", + DisplayOrder = 2, + }); + + sa23.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "M", + DisplayOrder = 3, + }); + + sa23.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "L", + DisplayOrder = 4, + }); + + sa23.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "XL", + DisplayOrder = 5, + }); + + sa23.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "XXL", + DisplayOrder = 6, + }); + + + #endregion sa23 Size + + #region sa24 diameter + + var sa24 = new SpecificationAttribute + { + Name = "Diameter", + DisplayOrder = 24, + }; + + sa24.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "38mm", + DisplayOrder = 1, + }); + + sa24.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "40mm", + DisplayOrder = 2, + }); + + sa24.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "44mm", + DisplayOrder = 3, + }); + + #endregion sa24 diameter + + #region sa25 closure + + var sa25 = new SpecificationAttribute + { + Name = "Closure", + DisplayOrder = 25, + }; + + sa25.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "snap closure", + DisplayOrder = 1, + }); + + sa25.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "folding clasp", + DisplayOrder = 2, + }); + + sa25.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "thorn close", + DisplayOrder = 3, + }); + + #endregion sa25 closure + + #region sa26 facial shape + + var sa26 = new SpecificationAttribute + { + Name = "Facial shape", + DisplayOrder = 26, + }; + + sa26.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "oval", + DisplayOrder = 1, + }); + + sa26.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "round", + DisplayOrder = 2, + }); + + sa26.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "heart shaped", + DisplayOrder = 3, + }); + + sa26.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "angular", + DisplayOrder = 4, + }); + + #endregion sa26 facial shape + + #region sa27 storage capacity + + var sa27 = new SpecificationAttribute + { + Name = "Storage capacity", + DisplayOrder = 27, + }; + + sa27.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "32 GB", + DisplayOrder = 1, + }); + + sa27.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "64 GB", + DisplayOrder = 2, + }); + + sa27.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "128 GB", + DisplayOrder = 3, + }); + + #endregion sa27 facial shape + + #region sa28 Dial window material type + + var sa28 = new SpecificationAttribute + { + Name = "Dial window material type", + DisplayOrder = 28, + }; + + sa28.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Mineral", + DisplayOrder = 1, + }); + + sa28.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + { + Name = "Sapphire", + DisplayOrder = 2, + }); + + #endregion sa28 Dial window material type + + var entities = new List + { + sa1,sa2,sa3,sa4,sa5,sa6,sa7,sa8,sa9,sa10,sa11,sa12,sa13,sa14,sa15,sa16,sa17,sa18,sa19,sa20,sa21,sa22,sa23,sa24,sa25,sa26,sa27,sa28 + }; + + this.Alter(entities); + return entities; + } + } +} diff --git a/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Variants.cs b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Variants.cs new file mode 100644 index 0000000000..840dd8d046 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.Variants.cs @@ -0,0 +1,3006 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Data.Setup +{ + public abstract partial class InvariantSeedData + { + public IList ProductAttributes() + { + var entities = new List + { + new ProductAttribute + { + Name = "Color", + Alias = "color" + }, + new ProductAttribute + { + Name = "Custom Text", + Alias = "custom-text" + }, + new ProductAttribute + { + Name = "HDD", + Alias = "hdd" + }, + new ProductAttribute + { + Name = "OS", + Alias = "os" + }, + new ProductAttribute + { + Name = "Processor", + Alias = "processor" + }, + new ProductAttribute + { + Name = "RAM", + Alias = "ram", + }, + new ProductAttribute + { + Name = "Size", + Alias = "size" + }, + new ProductAttribute + { + Name = "Software", + Alias = "software" + }, + new ProductAttribute + { + Name = "Game", + Alias = "game" + }, + new ProductAttribute + { + Name = "Color", + Alias = "iphone-color" + }, + new ProductAttribute + { + Name = "Color", + Alias = "ipad-color" + }, + new ProductAttribute + { + Name = "Memory capacity", + Alias = "memory-capacity" + }, + new ProductAttribute + { + Name = "Width", + Alias = "width" + }, + new ProductAttribute + { + Name = "Length", + Alias = "length" + }, + new ProductAttribute + { + Name = "Plate", + Alias = "plate" + }, + new ProductAttribute + { + Name = "Plate Thickness", + Alias = "plate-thickness" + }, + new ProductAttribute + { + Name = "Ballsize", + Alias = "ballsize" + }, + new ProductAttribute + { + Name = "Leather color", + Alias = "leather-color" + }, + new ProductAttribute + { + Name = "Seat Shell", + Alias = "seat-shell" + }, + new ProductAttribute + { + Name = "Base", + Alias = "base" + }, + new ProductAttribute + { + Name = "Material", + Alias = "material" + }, + new ProductAttribute + { + Name = "Style", + Alias = "style" + }, + new ProductAttribute + { + Name = "Controller", + Alias = "controller" + }, + new ProductAttribute + { + Name = "Framecolor", + Alias = "framecolor" + }, + new ProductAttribute + { + Name = "Lenscolor", + Alias = "lenscolor" + }, + new ProductAttribute + { + Name = "Lenstype", + Alias = "lenstype" + }, + new ProductAttribute + { + Name = "Lenscolor", + Alias = "wayfarerlenscolor" + }, + new ProductAttribute + { + Name = "Framecolor", + Alias = "wayfarerframecolor" + } + }; + + this.Alter(entities); + return entities; + } + + public IList ProductAttributeOptionsSets() + { + var entities = new List(); + var colorAttribute = _ctx.Set().First(x => x.Alias == "color"); + + entities.Add(new ProductAttributeOptionsSet + { + Name = "General colors", + ProductAttributeId = colorAttribute.Id + }); + + this.Alter(entities); + return entities; + } + + public IList ProductAttributeOptions() + { + var entities = new List(); + var colorAttribute = _ctx.Set().First(x => x.Alias == "color"); + var sets = _ctx.Set().ToList(); + + var generalColors = new[] + { + new { Color = "Red", Code = "#ff0000" }, + new { Color = "Green", Code = "#008000" }, + new { Color = "Blue", Code = "#0000ff" }, + new { Color = "Yellow", Code = "#ffff00" }, + new { Color = "Black", Code = "#000000" }, + new { Color = "White", Code = "#ffffff" }, + new { Color = "Gray", Code = "#808080" }, + new { Color = "Silver", Code = "#dddfde" }, + new { Color = "Brown", Code = "#a52a2a" }, + }; + + for (var i = 0; i < generalColors.Length; ++i) + { + entities.Add(new ProductAttributeOption + { + ProductAttributeOptionsSetId = sets[0].Id, + Alias = generalColors[i].Color.ToLower(), + Name = generalColors[i].Color, + Quantity = 1, + DisplayOrder = i + 1, + Color = generalColors[i].Code + }); + } + + this.Alter(entities); + return entities; + } + + public IList ProductVariantAttributes() + { + var entities = new List(); + var attrColor = _ctx.Set().First(x => x.Alias == "color"); + var attrController = _ctx.Set().First(x => x.Alias == "controller"); + var attrSize = _ctx.Set().First(x => x.Alias == "size"); + var attrGames = _ctx.Set().First(x => x.Alias == "game"); + var attrBallsize = _ctx.Set().First(x => x.Alias == "ballsize"); + var attrMemoryCapacity = _ctx.Set().First(x => x.Alias == "memory-capacity"); + var attrLensType = _ctx.Set().First(x => x.Alias == "lenstype"); + var attrFramecolor = _ctx.Set().First(x => x.Alias == "framecolor"); + var attrLenscolor = _ctx.Set().First(x => x.Alias == "lenscolor"); + var attrIphoneColor = _ctx.Set().First(x => x.Alias == "iphone-color"); + var attr97iPadColor = _ctx.Set().First(x => x.Alias == "ipad-color"); + var attrWidth = _ctx.Set().First(x => x.Alias == "width"); + var attrLength = _ctx.Set().First(x => x.Alias == "length"); + var attrPlate = _ctx.Set().First(x => x.Alias == "plate"); + var attrPlateThickness = _ctx.Set().First(x => x.Alias == "plate-thickness"); + var attrLeatherColor = _ctx.Set().First(x => x.Alias == "leather-color"); + var attrSeatShell = _ctx.Set().First(x => x.Alias == "seat-shell"); + var attrBase = _ctx.Set().First(x => x.Alias == "base"); + var attrMaterial = _ctx.Set().First(x => x.Alias == "material"); + var attrWayfarerLenscolor = _ctx.Set().First(x => x.Alias == "wayfarerlenscolor"); + var attrWayfarerFramecolor = _ctx.Set().First(x => x.Alias == "wayfarerframecolor"); + + var generalColors = new[] + { + new { Name = "Black", Color = "#000000" }, + new { Name = "White", Color = "#ffffff" }, + new { Name = "Anthracite", Color = "#32312f" }, + new { Name = "Fuliginous", Color = "#5F5B5C" }, + new { Name = "Light grey", Color = "#e3e3e5" }, + new { Name = "Natural", Color = "#BBB98B" }, + new { Name = "Biscuit", Color = "#e0ccab" }, + new { Name = "Beige", Color = "#d1bc8a" }, + new { Name = "Hazelnut", Color = "#94703e" }, + new { Name = "Brown", Color = "#755232" }, + new { Name = "Dark brown", Color = "#27160F" }, + new { Name = "Dark green", Color = "#0a3210" }, + new { Name = "Blue", Color = "#0000ff" }, + new { Name = "Cognac", Color = "#e9aa1b" }, + new { Name = "Yellow", Color = "#e6e60c" }, + new { Name = "Orange", Color = "#ff6501" }, + new { Name = "Tomato red", Color = "#b10101" }, + new { Name = "Red", Color = "#fe0000" }, + new { Name = "Dark red", Color = "#5e0000" } + }; + + var products = _ctx.Set().ToList().ToDictionarySafe(x => x.Sku, x => x); + + #region Oakley custom flak + + var productCustomFlak = products["P-3002"]; + var attributeLensType = new ProductVariantAttribute() + { + Product = productCustomFlak, + ProductAttribute = attrLensType, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.RadioList + }; + + attributeLensType.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Standard", + Alias = "standard", + IsPreSelected = true, + DisplayOrder = 1, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 100.0M + }); + + attributeLensType.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Polarized", + Alias = "polarized", + DisplayOrder = 2, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 200.0M + }); + attributeLensType.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Prizm", + Alias = "prizm", + DisplayOrder = 3, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 200.0M + }); + + entities.Add(attributeLensType); + + + var attributeFramecolor = new ProductVariantAttribute() + { + Product = productCustomFlak, + ProductAttribute = attrFramecolor, + IsRequired = true, + DisplayOrder = 2, + AttributeControlType = AttributeControlType.Boxes + }; + + attributeFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Matte Black", + Alias = "matteblack", + IsPreSelected = true, + DisplayOrder = 1, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#2d2d2d" + }); + + attributeFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Polishedwhite", + Alias = "polishedwhite", + DisplayOrder = 2, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#f5f5f5" + }); + + attributeFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Sky Blue", + Alias = "skyblue", + DisplayOrder = 3, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#4187f6" + }); + + attributeFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Orange Flare", + Alias = "orangeflare", + DisplayOrder = 4, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#f55700" + }); + + attributeFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Redline", + Alias = "redline", + DisplayOrder = 5, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#cf0a02" + }); + + entities.Add(attributeFramecolor); + + var attributeLenscolor = new ProductVariantAttribute() + { + Product = productCustomFlak, + ProductAttribute = attrLenscolor, + IsRequired = true, + DisplayOrder = 3, + AttributeControlType = AttributeControlType.Boxes + }; + + attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Gray", + Alias = "gray", + IsPreSelected = true, + DisplayOrder = 1, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#7A798B" + }); + + attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Sapphire Iridium", + Alias = "sapphireiridium", + DisplayOrder = 2, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#4460BB" + }); + + attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Violet Iridium", + Alias = "violetiridium", + DisplayOrder = 3, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#5C5A89" + }); + + attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Jade Iridium", + Alias = "jadeiridium", + DisplayOrder = 4, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#376559" + }); + + attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Ruby Iridium", + Alias = "rubyiridium", + DisplayOrder = 5, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#CCAD12" + }); + + attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "24K Iridium", + Alias = "24kiridium", + DisplayOrder = 6, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#CE9D12" + }); + + attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Positive Red Iridium", + Alias = "positiverediridium", + DisplayOrder = 7, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#764CDC" + }); + + attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Clear", + Alias = "clear", + DisplayOrder = 7, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#e2e2e3" + }); + attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Fire Iridium", + Alias = "fireiridium", + DisplayOrder = 7, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#E2C724" + }); + + entities.Add(attributeLenscolor); + + #endregion Oakley custom flak + + #region Wayfarer + + var productWayfarer = products["P-3003"]; + var wayfarerFramePictures = _ctx.Set().Where(x => x.Name.StartsWith("wayfarer-")).ToList(); + + var attributeWayfarerLenscolor = new ProductVariantAttribute() + { + Product = productWayfarer, + ProductAttribute = attrWayfarerLenscolor, + IsRequired = true, + DisplayOrder = 3, + AttributeControlType = AttributeControlType.Boxes + }; + + attributeWayfarerLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Blue-Gray classic", + Alias = "blue-gray-classic", + IsPreSelected = true, + DisplayOrder = 1, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#3e4659" + }); + + attributeWayfarerLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Brown course", + Alias = "brown-course", + DisplayOrder = 2, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#3e4659" + }); + + attributeWayfarerLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Gray course", + Alias = "gray-course", + DisplayOrder = 3, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#727377" + }); + + attributeWayfarerLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Green classic", + Alias = "green-classic", + DisplayOrder = 4, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#3c432e" + }); + + entities.Add(attributeWayfarerLenscolor); + + var attributeWayfarerFramecolor = new ProductVariantAttribute() + { + Product = productWayfarer, + ProductAttribute = attrWayfarerFramecolor, + IsRequired = true, + DisplayOrder = 3, + AttributeControlType = AttributeControlType.Boxes + }; + + var wayfarerFramePicture = wayfarerFramePictures.First(x => x.Name.Contains("-rayban-black")); + + attributeWayfarerFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Black", + Alias = "rayban-black", + IsPreSelected = true, + DisplayOrder = 1, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + //Color = "#3e4659" + MediaFileId = wayfarerFramePicture.Id + }); + + wayfarerFramePicture = wayfarerFramePictures.First(x => x.Name.Contains("-havana-black")); + attributeWayfarerFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Havana; Black", + Alias = "havana-black", + DisplayOrder = 2, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + //Color = "#3e4659" + MediaFileId = wayfarerFramePicture.Id + }); + + wayfarerFramePicture = wayfarerFramePictures.First(x => x.Name.Contains("-havana")); + attributeWayfarerFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Havana", + Alias = "havana", + DisplayOrder = 3, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + //Color = "#727377", + MediaFileId = wayfarerFramePicture.Id + }); + + entities.Add(attributeWayfarerFramecolor); + + #endregion wayfarer + + #region 9,7 iPad + + var product97iPad = products["P-2004"]; + var attribute97iPadMemoryCapacity = new ProductVariantAttribute() + { + Product = product97iPad, + ProductAttribute = attrMemoryCapacity, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.RadioList + }; + + attribute97iPadMemoryCapacity.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "64 GB", + Alias = "64gb", + IsPreSelected = true, + DisplayOrder = 2, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 100.0M + }); + + attribute97iPadMemoryCapacity.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "128 GB", + Alias = "128gb", + DisplayOrder = 3, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 200.0M + }); + + entities.Add(attribute97iPadMemoryCapacity); + + var attribute97iPadColor = new ProductVariantAttribute() + { + Product = product97iPad, + ProductAttribute = attr97iPadColor, + IsRequired = true, + DisplayOrder = 2, + AttributeControlType = AttributeControlType.Boxes + }; + + attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Silver", + Alias = "silver", + IsPreSelected = true, + DisplayOrder = 1, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#dddfde" + }); + + attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Gold", + Alias = "gold", + DisplayOrder = 2, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#e3d0ba" + }); + + attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Spacegray", + Alias = "spacegray", + DisplayOrder = 3, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#abaeb1" + }); + + attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Rose", + Alias = "rose", + DisplayOrder = 4, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#d9a6ad" + }); + + attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Mint", + Alias = "mint", + DisplayOrder = 5, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#a6dbb1" + }); + + attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Purple", + Alias = "purple", + DisplayOrder = 6, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#dba5d7" + }); + + attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Lightblue", + Alias = "lightblue", + DisplayOrder = 7, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#a6b9df" + }); + + attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Turquoise", + Alias = "turquoise", + DisplayOrder = 8, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#a4dbde" + }); + + attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Yellow", + Alias = "yellow", + DisplayOrder = 7, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#dfddb6" + }); + + entities.Add(attribute97iPadColor); + + #endregion 9,7 iPad + + #region iPhone 7 plus + + var productIphone7Plus = products["P-2001"]; + var attributeIphone7PlusMemoryCapacity = new ProductVariantAttribute() + { + Product = productIphone7Plus, + ProductAttribute = attrMemoryCapacity, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.RadioList + }; + + attributeIphone7PlusMemoryCapacity.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "64 GB", + Alias = "64gb", + IsPreSelected = true, + DisplayOrder = 2, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 100.0M + }); + + attributeIphone7PlusMemoryCapacity.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "128 GB", + Alias = "128gb", + DisplayOrder = 3, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 200.0M + }); + + entities.Add(attributeIphone7PlusMemoryCapacity); + + + var attributeIphone7PlusColor = new ProductVariantAttribute() + { + Product = productIphone7Plus, + ProductAttribute = attrIphoneColor, + IsRequired = true, + DisplayOrder = 2, + AttributeControlType = AttributeControlType.Boxes + }; + + attributeIphone7PlusColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Silver", + Alias = "silver", + IsPreSelected = true, + DisplayOrder = 1, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#dddfde" + }); + + attributeIphone7PlusColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Gold", + Alias = "gold", + DisplayOrder = 2, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#e3d0ba" + }); + + attributeIphone7PlusColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Red", + Alias = "red", + DisplayOrder = 3, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#af1e2d" + }); + + attributeIphone7PlusColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Rose", + Alias = "rose", + DisplayOrder = 4, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#d9a6ad" + }); + + attributeIphone7PlusColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Black", + Alias = "black", + DisplayOrder = 5, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + Color = "#000000" + }); + + entities.Add(attributeIphone7PlusColor); + + #endregion iPhone 7 plus + + #region Dualshock3ControllerColor + + var productPs3 = products["Sony-PS399000"]; + var attributeDualshock3ControllerColor = new ProductVariantAttribute() + { + Product = productPs3, + ProductAttribute = attrController, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.RadioList + }; + + attributeDualshock3ControllerColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "without controller", + Alias = "without_controller", + IsPreSelected = true, + DisplayOrder = 1, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple + }); + + attributeDualshock3ControllerColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "with controller", + Alias = "with_controller", + PriceAdjustment = 60.0M, + DisplayOrder = 2, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple + }); + + entities.Add(attributeDualshock3ControllerColor); + + #endregion Dualshock3ControllerColor + + #region Apple Airpod + + var productAirpod = products["P-2003"]; + var attributeAirpod = new ProductVariantAttribute() + { + Product = productAirpod, + ProductAttribute = attrColor, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.Boxes + }; + + attributeAirpod.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Gold", + Alias = "gold", + DisplayOrder = 1, + Quantity = 1, + Color = "#e3d0ba", + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 5.00M + //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-acreed3").Id + }); + + attributeAirpod.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Rose", + Alias = "rose", + DisplayOrder = 2, + Quantity = 1, + Color = "#d9a6ad", + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 10.00M, + //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-watchdogs").Id + }); + + attributeAirpod.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Mint", + Alias = "mint", + DisplayOrder = 3, + Quantity = 1, + Color = "#a6dbb1", + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 15.00M + //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-princepersia").Id + }); + + attributeAirpod.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Lightblue", + Alias = "lightblue", + DisplayOrder = 3, + Quantity = 1, + Color = "#a6b9df", + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 15.00M + //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-princepersia").Id + }); + + attributeAirpod.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "Turquoise", + Alias = "turquoise", + DisplayOrder = 3, + Quantity = 1, + Color = "#a4dbde", + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 15.00M + //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-princepersia").Id + }); + + attributeAirpod.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "White", + Alias = "white", + DisplayOrder = 3, + Quantity = 1, + Color = "#ffffff", + IsPreSelected = true, + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 15.00M + //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-princepersia").Id + }); + + entities.Add(attributeAirpod); + + #endregion Apple Airpod + + #region Evopower 5.3 Trainer HS Ball + + var productEvopower = products["P-5003"]; + var attributeEvopower = new ProductVariantAttribute() + { + Product = productEvopower, + ProductAttribute = attrBallsize, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.RadioList + }; + + attributeEvopower.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "3", + Alias = "ballsize-3", + DisplayOrder = 1, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 5.00M + //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-acreed3").Id + }); + + attributeEvopower.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "4", + Alias = "ballsize-4", + DisplayOrder = 2, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 10.00M, + IsPreSelected = true + //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-watchdogs").Id + }); + + attributeEvopower.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + { + Name = "5", + Alias = "ballsize-5", + DisplayOrder = 3, + Quantity = 1, + ValueType = ProductVariantAttributeValueType.Simple, + PriceAdjustment = 15.00M + //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-princepersia").Id + }); + + entities.Add(attributeEvopower); + + #endregion Evopower 5.3 Trainer HS Ball + + #region Ps3OneGameFree + + //var productPs3OneGameFree = _ctx.Set().First(x => x.Sku == "Sony-PS310111"); + + //var attributePs3OneGameFree = new ProductVariantAttribute() + //{ + // Product = productPs3OneGameFree, + // ProductAttribute = attrGames, + // IsRequired = true, + // DisplayOrder = 1, + // AttributeControlType = AttributeControlType.DropdownList + //}; + + //attributePs3OneGameFree.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + //{ + // Name = "Minecraft - Playstation 4 Edition", + // Alias = "minecraft-playstation4edition", + // DisplayOrder = 1, + // Quantity = 1, + // ValueType = ProductVariantAttributeValueType.ProductLinkage, + // LinkedProductId = _ctx.Set().First(x => x.Sku == "PD-Minecraft4ps4").Id + //}); + + //attributePs3OneGameFree.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + //{ + // Name = "Watch Dogs", + // Alias = "watch-dogs", + // DisplayOrder = 2, + // Quantity = 1, + // ValueType = ProductVariantAttributeValueType.ProductLinkage, + // LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-watchdogs").Id + //}); + + //attributePs3OneGameFree.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + //{ + // Name = "Horizon Zero Dawn - PlayStation 4", + // Alias = "horizon-zero-dawn-playStation-4", + // DisplayOrder = 3, + // Quantity = 1, + // ValueType = ProductVariantAttributeValueType.ProductLinkage, + // LinkedProductId = _ctx.Set().First(x => x.Sku == "PD-ZeroDown4PS4").Id + //}); + + //attributePs3OneGameFree.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() + //{ + // Name = "LEGO Worlds - PlayStation 4", + // Alias = "lego-worlds-playstation_4", + // DisplayOrder = 4, + // Quantity = 1, + // ValueType = ProductVariantAttributeValueType.ProductLinkage, + // LinkedProductId = _ctx.Set().First(x => x.Sku == "Gaming-Lego-001").Id + //}); + + //entities.Add(attributePs3OneGameFree); + + #endregion Ps3OneGameFree + + #region Fashion - Converse All Star + + var productAllStar = products["Fashion-112355"]; + var allStarColors = new string[] { "Charcoal", "Maroon", "Navy", "Purple", "White" }; + var allStarPictures = _ctx.Set().Where(x => x.Name.StartsWith("allstar-")).ToList(); + + var attrAllStarColor = new ProductVariantAttribute + { + Product = productAllStar, + ProductAttribute = attrColor, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.Boxes + }; + + for (var i = 0; i < allStarColors.Length; ++i) + { + var allStarPicture = allStarPictures.First(x => x.Name.Contains(allStarColors[i].ToLower())); + attrAllStarColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = allStarColors[i], + Alias = allStarColors[i].ToLower(), + DisplayOrder = i + 1, + Quantity = 1, + MediaFileId = allStarPicture.Id + }); + } + entities.Add(attrAllStarColor); + + var attrAllStarSize = new ProductVariantAttribute + { + Product = productAllStar, + ProductAttribute = attrSize, + IsRequired = true, + DisplayOrder = 2, + AttributeControlType = AttributeControlType.Boxes + }; + attrAllStarSize.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "42", + Alias = "42", + DisplayOrder = 1, + Quantity = 1, + IsPreSelected = true + }); + attrAllStarSize.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "43", + Alias = "43", + DisplayOrder = 2, + Quantity = 1 + }); + attrAllStarSize.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "44", + Alias = "44", + DisplayOrder = 3, + Quantity = 1 + }); + entities.Add(attrAllStarSize); + + #endregion + + #region Fashion - Shirt Meccanica + + var productShirtMeccanica = products["Fashion-987693502"]; + var shirtMeccanicaSizes = new string[] { "XS", "S", "M", "L", "XL" }; + var shirtMeccanicaColors = new[] + { + new { Color = "Red", Code = "#fe0000" }, + new { Color = "Black", Code = "#000000" } + }; + + var attrShirtMeccanicaColor = new ProductVariantAttribute + { + Product = productShirtMeccanica, + ProductAttribute = attrColor, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.Boxes + }; + + for (var i = 0; i < shirtMeccanicaColors.Length; ++i) + { + attrShirtMeccanicaColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = shirtMeccanicaColors[i].Color, + Alias = shirtMeccanicaColors[i].Color.ToLower(), + DisplayOrder = i + 1, + Quantity = 1, + Color = shirtMeccanicaColors[i].Code, + IsPreSelected = shirtMeccanicaColors[i].Color == "Red" + }); + } + entities.Add(attrShirtMeccanicaColor); + + var attrShirtMeccanicaSize = new ProductVariantAttribute + { + Product = productShirtMeccanica, + ProductAttribute = attrSize, + IsRequired = true, + DisplayOrder = 2, + AttributeControlType = AttributeControlType.Boxes + }; + + for (var i = 0; i < shirtMeccanicaSizes.Length; ++i) + { + attrShirtMeccanicaSize.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = shirtMeccanicaSizes[i], + Alias = shirtMeccanicaSizes[i].ToLower(), + DisplayOrder = i + 1, + Quantity = 1, + IsPreSelected = shirtMeccanicaSizes[i] == "XS" + }); + } + entities.Add(attrShirtMeccanicaSize); + + #endregion + + #region Fashion - Ladies Jacket + + var productLadiesJacket = products["Fashion-JN1107"]; + var ladiesJacketSizes = new string[] { "XS", "S", "M", "L", "XL" }; + var ladiesJacketColors = new[] + { + new { Color = "Red", Code = "#CE1F1C" }, + new { Color = "Orange", Code = "#EB7F01" }, + new { Color = "Green", Code = "#24B87E" }, + new { Color = "Blue", Code = "#0F8CCE" }, + new { Color = "Navy", Code = "#525671" }, + new { Color = "Silver", Code = "#ABB0B3" }, + new { Color = "Black", Code = "#404040" } + }; + + var attrLadiesJacketColor = new ProductVariantAttribute + { + Product = productLadiesJacket, + ProductAttribute = attrColor, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.Boxes + }; + + for (var i = 0; i < ladiesJacketColors.Length; ++i) + { + attrLadiesJacketColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = ladiesJacketColors[i].Color, + Alias = ladiesJacketColors[i].Color.ToLower(), + DisplayOrder = i + 1, + Quantity = 1, + Color = ladiesJacketColors[i].Code, + IsPreSelected = ladiesJacketColors[i].Color == "Red" + }); + } + entities.Add(attrLadiesJacketColor); + + var attrLadiesJacketSize = new ProductVariantAttribute + { + Product = productLadiesJacket, + ProductAttribute = attrSize, + IsRequired = true, + DisplayOrder = 2, + AttributeControlType = AttributeControlType.RadioList + }; + + for (var i = 0; i < ladiesJacketSizes.Length; ++i) + { + attrLadiesJacketSize.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = ladiesJacketSizes[i], + Alias = ladiesJacketSizes[i].ToLower(), + DisplayOrder = i + 1, + Quantity = 1, + IsPreSelected = ladiesJacketSizes[i] == "XS" + }); + } + entities.Add(attrLadiesJacketSize); + + #endregion + + #region Fashion - Clark Jeans + + var productClarkJeans = products["Fashion-65986524"]; + var clarkJeansWidth = new string[] { "31", "32", "33", "34", "35", "36", "38", "40", "42", "44", "46" }; + var clarkJeansLength = new string[] { "30", "32", "34" }; + + var attrClarkJeansWidth = new ProductVariantAttribute + { + Product = productClarkJeans, + ProductAttribute = attrWidth, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.Boxes + }; + + for (var i = 0; i < clarkJeansWidth.Length; ++i) + { + attrClarkJeansWidth.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = clarkJeansWidth[i], + Alias = clarkJeansWidth[i], + DisplayOrder = i + 1, + Quantity = 1, + IsPreSelected = clarkJeansWidth[i] == "31" + }); + } + entities.Add(attrClarkJeansWidth); + + var attrClarkJeansLength = new ProductVariantAttribute + { + Product = productClarkJeans, + ProductAttribute = attrLength, + IsRequired = true, + DisplayOrder = 2, + AttributeControlType = AttributeControlType.Boxes + }; + + for (var i = 0; i < clarkJeansLength.Length; ++i) + { + attrClarkJeansLength.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = clarkJeansLength[i], + Alias = clarkJeansLength[i], + DisplayOrder = i + 1, + Quantity = 1, + IsPreSelected = clarkJeansLength[i] == "30" + }); + } + entities.Add(attrClarkJeansLength); + + #endregion Fashion - Clark Jeans + + #region Furniture - Le Corbusier LC 6 table + + var productCorbusierTable = products["Furniture-lc6"]; + var attrCorbusierTablePlate = new ProductVariantAttribute + { + Product = productCorbusierTable, + ProductAttribute = attrPlate, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.Boxes + }; + attrCorbusierTablePlate.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "Clear glass", + Alias = "clear-glass", + DisplayOrder = 1, + Quantity = 1, + IsPreSelected = true + }); + attrCorbusierTablePlate.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "Sandblasted glass", + Alias = "sandblasted-glass", + DisplayOrder = 2, + Quantity = 1 + }); + entities.Add(attrCorbusierTablePlate); + + var attrCorbusierTableThickness = new ProductVariantAttribute + { + Product = productCorbusierTable, + ProductAttribute = attrPlateThickness, + IsRequired = true, + DisplayOrder = 2, + AttributeControlType = AttributeControlType.Boxes + }; + attrCorbusierTableThickness.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "15 mm", + Alias = "15mm", + DisplayOrder = 1, + Quantity = 1, + IsPreSelected = true + }); + attrCorbusierTableThickness.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "19 mm", + Alias = "19mm", + DisplayOrder = 2, + Quantity = 1 + }); + entities.Add(attrCorbusierTableThickness); + + #endregion + + #region Soccer Adidas TANGO SALA BALL + + var productAdidasTANGOSALABALL = products["P-5001"]; + var productAdidasTANGOSALABALLSizes = new string[] { "3", "4", "5" }; + var productAdidasTANGOSALABALLColors = new[] + { + new { Color = "Red", Code = "#ff0000" }, + new { Color = "Yellow", Code = " #ffff00" }, + new { Color = "Green", Code = "#008000" }, + new { Color = "Blue", Code = "#0000ff" }, + new { Color = "Gray", Code = "#808080" }, + new { Color = "White", Code = "#ffffff" }, + new { Color = "Brown", Code = "#a52a2a" } + }; + + var attrAdidasTANGOSALABALLColor = new ProductVariantAttribute + { + Product = productAdidasTANGOSALABALL, + ProductAttribute = attrColor, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.Boxes + }; + + for (var i = 0; i < productAdidasTANGOSALABALLColors.Length; ++i) + { + attrAdidasTANGOSALABALLColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = productAdidasTANGOSALABALLColors[i].Color, + Alias = productAdidasTANGOSALABALLColors[i].Color.ToLower(), + DisplayOrder = i + 1, + Quantity = 1, + Color = productAdidasTANGOSALABALLColors[i].Code, + IsPreSelected = productAdidasTANGOSALABALLColors[i].Color == "White" + }); + } + entities.Add(attrAdidasTANGOSALABALLColor); + + var attrAdidasTANGOSALABALLSize = new ProductVariantAttribute + { + Product = productAdidasTANGOSALABALL, + ProductAttribute = attrSize, + IsRequired = true, + DisplayOrder = 2, + AttributeControlType = AttributeControlType.RadioList + }; + + for (var i = 0; i < productAdidasTANGOSALABALLSizes.Length; ++i) + { + attrAdidasTANGOSALABALLSize.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = productAdidasTANGOSALABALLSizes[i], + Alias = productAdidasTANGOSALABALLSizes[i].ToLower(), + DisplayOrder = i + 1, + Quantity = 1, + IsPreSelected = productAdidasTANGOSALABALLSizes[i] == "5" + }); + } + entities.Add(attrAdidasTANGOSALABALLSize); + + #endregion Soccer Adidas TANGO SALA BALL + + #region Torfabrik official game ball + + var productTorfabrikBall = products["P-5002"]; + var productTorfabrikBallSizes = new string[] { "3", "4", "5" }; + var productTorfabrikBallColors = new[] + { + new { Color = "Red", Code = "#ff0000" }, + new { Color = "Yellow", Code = " #ffff00" }, + new { Color = "Green", Code = "#008000" }, + new { Color = "Blue", Code = "#0000ff" }, + new { Color = "White", Code = "#ffffff" }, + }; + + var attrTorfabrikBallColor = new ProductVariantAttribute + { + Product = productTorfabrikBall, + ProductAttribute = attrColor, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.Boxes + }; + + for (var i = 0; i < productTorfabrikBallColors.Length; ++i) + { + attrTorfabrikBallColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = productTorfabrikBallColors[i].Color, + Alias = productTorfabrikBallColors[i].Color.ToLower(), + DisplayOrder = i + 1, + Quantity = 1, + Color = productTorfabrikBallColors[i].Code, + IsPreSelected = productTorfabrikBallColors[i].Color == "White" + }); + } + entities.Add(attrTorfabrikBallColor); + + var attrTorfabrikSize = new ProductVariantAttribute + { + Product = productTorfabrikBall, + ProductAttribute = attrSize, + IsRequired = true, + DisplayOrder = 2, + AttributeControlType = AttributeControlType.RadioList + }; + + for (var i = 0; i < productTorfabrikBallSizes.Length; ++i) + { + attrTorfabrikSize.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = productTorfabrikBallSizes[i], + Alias = productTorfabrikBallSizes[i].ToLower(), + DisplayOrder = i + 1, + Quantity = 1, + IsPreSelected = productTorfabrikBallSizes[i] == "5" + }); + } + entities.Add(attrTorfabrikSize); + + #endregion Soccer Torfabrik official game ball + + #region Furniture - Ball chair + + var productBallChair = products["Furniture-ball-chair"]; + var attrBallChairMaterial = new ProductVariantAttribute + { + Product = productBallChair, + ProductAttribute = attrMaterial, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.DropdownList + }; + attrBallChairMaterial.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "Leather Special", + Alias = "leather-special", + DisplayOrder = 1, + Quantity = 1, + IsPreSelected = true + }); + attrBallChairMaterial.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "Leather Aniline", + Alias = "leather-aniline", + DisplayOrder = 2, + Quantity = 1 + }); + attrBallChairMaterial.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "Mixed Linen", + Alias = "mixed-linen", + DisplayOrder = 3, + Quantity = 1 + }); + entities.Add(attrBallChairMaterial); + + var attrBallChairColor = new ProductVariantAttribute + { + Product = productBallChair, + ProductAttribute = attrColor, + IsRequired = true, + DisplayOrder = 2, + AttributeControlType = AttributeControlType.Boxes + }; + attrBallChairColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "White", + Alias = "white", + Color = "#ffffff", + DisplayOrder = 1, + Quantity = 1, + IsPreSelected = true + }); + attrBallChairColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "Black", + Alias = "black", + Color = "#000000", + DisplayOrder = 2, + Quantity = 1 + }); + entities.Add(attrBallChairColor); + + var attrBallChairLeatherColor = new ProductVariantAttribute + { + Product = productBallChair, + ProductAttribute = attrLeatherColor, + IsRequired = true, + DisplayOrder = 3, + AttributeControlType = AttributeControlType.Boxes + }; + + for (var i = 0; i < generalColors.Length; ++i) + { + attrBallChairLeatherColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = generalColors[i].Name, + Alias = generalColors[i].Name.Replace(" ", "-").ToLower(), + DisplayOrder = i + 1, + Quantity = 1, + Color = generalColors[i].Color, + IsPreSelected = (generalColors[i].Name == "Tomato red") + }); + } + entities.Add(attrBallChairLeatherColor); + + #endregion + + #region Furniture - Lounge chair + + var productLoungeChair = products["Furniture-lounge-chair"]; + var attrLoungeChairMaterial = new ProductVariantAttribute + { + Product = productLoungeChair, + ProductAttribute = attrMaterial, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.DropdownList + }; + attrLoungeChairMaterial.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "Leather Special", + Alias = "leather-special", + DisplayOrder = 1, + Quantity = 1, + IsPreSelected = true + }); + attrLoungeChairMaterial.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "Leather Aniline", + Alias = "leather-aniline", + DisplayOrder = 2, + Quantity = 1 + }); + entities.Add(attrLoungeChairMaterial); + + var loungeChairSeatShells = new string[] { "Palisander", "Cherry", "Walnut", "Wooden black lacquered" }; + var attrLoungeChairSeatShell = new ProductVariantAttribute + { + Product = productLoungeChair, + ProductAttribute = attrSeatShell, + IsRequired = true, + DisplayOrder = 2, + AttributeControlType = AttributeControlType.DropdownList + }; + + for (var i = 0; i < loungeChairSeatShells.Length; ++i) + { + attrLoungeChairSeatShell.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = loungeChairSeatShells[i], + Alias = loungeChairSeatShells[i].Replace(" ", "-").ToLower(), + DisplayOrder = i + 1, + Quantity = 1, + IsPreSelected = (i == 0), + PriceAdjustment = (loungeChairSeatShells[i] == "Wooden black lacquered" ? 100.00M : decimal.Zero) + }); + } + entities.Add(attrLoungeChairSeatShell); + + var attrLoungeChairBase = new ProductVariantAttribute + { + Product = productLoungeChair, + ProductAttribute = attrBase, + IsRequired = true, + DisplayOrder = 3, + AttributeControlType = AttributeControlType.DropdownList + }; + attrLoungeChairBase.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "Top edge polished", + Alias = "top-edge-polished", + DisplayOrder = 1, + Quantity = 1, + IsPreSelected = true + }); + attrLoungeChairBase.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "Completely polished", + Alias = "completely-polished", + DisplayOrder = 2, + Quantity = 1, + PriceAdjustment = 150.00M + }); + entities.Add(attrLoungeChairBase); + + var attrLoungeChairLeatherColor = new ProductVariantAttribute + { + Product = productLoungeChair, + ProductAttribute = attrLeatherColor, + IsRequired = true, + DisplayOrder = 4, + AttributeControlType = AttributeControlType.Boxes + }; + + for (var i = 0; i < generalColors.Length; ++i) + { + attrLoungeChairLeatherColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = generalColors[i].Name, + Alias = generalColors[i].Name.Replace(" ", "-").ToLower(), + DisplayOrder = i + 1, + Quantity = 1, + Color = generalColors[i].Color, + IsPreSelected = (generalColors[i].Name == "White") + }); + } + entities.Add(attrLoungeChairLeatherColor); + + #endregion + + #region Furniture - Cube chair + + var productCubeChair = products["Furniture-cube-chair"]; + var attrCubeChairMaterial = new ProductVariantAttribute + { + Product = productCubeChair, + ProductAttribute = attrMaterial, + IsRequired = true, + DisplayOrder = 1, + AttributeControlType = AttributeControlType.DropdownList + }; + attrCubeChairMaterial.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "Leather Special", + Alias = "leather-special", + DisplayOrder = 1, + Quantity = 1, + IsPreSelected = true + }); + attrCubeChairMaterial.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = "Leather Aniline", + Alias = "leather-aniline", + DisplayOrder = 2, + Quantity = 1, + PriceAdjustment = 400.00M + }); + entities.Add(attrCubeChairMaterial); + + var attrCubeChairLeatherColor = new ProductVariantAttribute + { + Product = productCubeChair, + ProductAttribute = attrLeatherColor, + IsRequired = true, + DisplayOrder = 2, + AttributeControlType = AttributeControlType.Boxes + }; + + for (var i = 0; i < generalColors.Length; ++i) + { + attrCubeChairLeatherColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + { + Name = generalColors[i].Name, + Alias = generalColors[i].Name.Replace(" ", "-").ToLower(), + DisplayOrder = i + 1, + Quantity = 1, + Color = generalColors[i].Color, + IsPreSelected = (generalColors[i].Name == "Black") + }); + } + entities.Add(attrCubeChairLeatherColor); + + #endregion + + this.Alter(entities); + return entities; + } + + public IList ProductVariantAttributeCombinations() + { + var entities = new List(); + var attrColor = _ctx.Set().First(x => x.Alias == "color"); + var attrController = _ctx.Set().First(x => x.Alias == "controller"); + var attrSize = _ctx.Set().First(x => x.Alias == "size"); + var attrMemoryCapacity = _ctx.Set().First(x => x.Alias == "memory-capacity"); + var attrColorIphoneColors = _ctx.Set().First(x => x.Alias == "iphone-color"); + var attr97iPadColors = _ctx.Set().First(x => x.Alias == "ipad-color"); + var attrPlate = _ctx.Set().First(x => x.Alias == "plate"); + var attrPlateThickness = _ctx.Set().First(x => x.Alias == "plate-thickness"); + var attrMaterial = _ctx.Set().First(x => x.Alias == "material"); + var attrLeatherColor = _ctx.Set().First(x => x.Alias == "leather-color"); + var attrSeatShell = _ctx.Set().First(x => x.Alias == "seat-shell"); + var attrBase = _ctx.Set().First(x => x.Alias == "base"); + var attrFlakLenstype = _ctx.Set().First(x => x.Alias == "lenstype"); + var attrFlakFramecolor = _ctx.Set().First(x => x.Alias == "framecolor"); + var attrFlakLenscolor = _ctx.Set().First(x => x.Alias == "lenscolor"); + var attrWayfarerLenscolor = _ctx.Set().First(x => x.Alias == "wayfarerlenscolor"); + var attrWayfarerFramecolor = _ctx.Set().First(x => x.Alias == "wayfarerframecolor"); + + #region ORIGINAL WAYFARER AT COLLECTION + + var productWayfarer = _ctx.Set().First(x => x.Sku == "P-3003"); + var wayfarerPictureIds = productWayfarer.ProductPictures.Select(pp => pp.MediaFileId).ToList(); + var picturesWayfarer = _ctx.Set().Where(x => wayfarerPictureIds.Contains(x.Id)).ToList(); + + var wayfarerLenscolor = _ctx.Set().First(x => x.ProductId == productWayfarer.Id && x.ProductAttributeId == attrWayfarerLenscolor.Id); + var wayfarerLenscolorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == wayfarerLenscolor.Id).ToList(); + + var wayfarerFramecolor = _ctx.Set().First(x => x.ProductId == productWayfarer.Id && x.ProductAttributeId == attrWayfarerFramecolor.Id); + var wayfarerFramecolorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == wayfarerFramecolor.Id).ToList(); + + #region blue-gray-classic-black + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productWayfarer, + Sku = productWayfarer.Sku + "_blue-gray-classic-black", + + AttributesXml = FormatAttributeXml( + wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "blue-gray-classic").Id, + wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "rayban-black").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + //Price = 299M, + AssignedMediaFileIds = picturesWayfarer.First(x => x.Name.StartsWith("wayfarer-blue-gray-classic-black-1")).Id.ToString() + }); + + #endregion blue-gray-classic-black + + #region gray-course-black + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productWayfarer, + Sku = productWayfarer.Sku + "_gray-course-black", + + AttributesXml = FormatAttributeXml( + wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "gray-course").Id, + wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "rayban-black").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + //Price = 299M, + AssignedMediaFileIds = picturesWayfarer.First(x => x.Name.StartsWith("wayfarer-gray-course-black")).Id.ToString() + }); + + #endregion gray-course-black + + #region brown-course-havana + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productWayfarer, + Sku = productWayfarer.Sku + "_brown-course-havana", + + AttributesXml = FormatAttributeXml( + wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "brown-course").Id, + wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + //Price = 299M, + AssignedMediaFileIds = picturesWayfarer.First(x => x.Name.StartsWith("wayfarer-brown-course-havana")).Id.ToString() + }); + + #endregion brown-course-havana + + #region green-classic-havana-black + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productWayfarer, + Sku = productWayfarer.Sku + "_green-classic-havana-black", + + AttributesXml = FormatAttributeXml( + wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "green-classic").Id, + wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana-black").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + //Price = 299M, + AssignedMediaFileIds = picturesWayfarer.First(x => x.Name.StartsWith("wayfarer-green-classic-havana-black")).Id.ToString() + }); + + #endregion green-classic-havana-black + + #region blue-gray-classic-havana-black + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productWayfarer, + Sku = productWayfarer.Sku + "_blue-gray-classic-havana-black", + + AttributesXml = FormatAttributeXml( + wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "blue-gray-classic").Id, + wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana-black").Id), + + StockQuantity = 0, + AllowOutOfStockOrders = true, + IsActive = false, + //Price = 299M, + AssignedMediaFileIds = picturesWayfarer.First(x => x.Name.StartsWith("wayfarer-blue-gray-classic-black-1")).Id.ToString() + }); + + #endregion green-classic-havana-black + + #region blue-gray-classic-havana + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productWayfarer, + Sku = productWayfarer.Sku + "_blue-gray-classic-havana", + + AttributesXml = FormatAttributeXml( + wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "blue-gray-classic").Id, + wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana").Id), + + StockQuantity = 0, + AllowOutOfStockOrders = true, + IsActive = false, + //Price = 299M, + AssignedMediaFileIds = picturesWayfarer.First(x => x.Name.StartsWith("wayfarer-blue-gray-classic-black-1")).Id.ToString() + }); + + #endregion green-classic-rayban-black + + #region gray-course-havana-black + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productWayfarer, + Sku = productWayfarer.Sku + "_gray-course-havana-black", + + AttributesXml = FormatAttributeXml( + wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "gray-course").Id, + wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana-black").Id), + + StockQuantity = 0, + AllowOutOfStockOrders = true, + IsActive = true, + //Price = 299M, + AssignedMediaFileIds = picturesWayfarer.First(x => x.Name.StartsWith("wayfarer-gray-course-black")).Id.ToString() + }); + + #endregion gray-course-havana-black + + #region gray-course-havana + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productWayfarer, + Sku = productWayfarer.Sku + "_gray-course-havana", + + AttributesXml = FormatAttributeXml( + wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "gray-course").Id, + wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana").Id), + + StockQuantity = 0, + AllowOutOfStockOrders = true, + IsActive = false, + //Price = 299M, + AssignedMediaFileIds = picturesWayfarer.First(x => x.Name.StartsWith("wayfarer-gray-course-black")).Id.ToString() + }); + + #endregion gray-course-rayban-black + + #region green-classic-rayban-black + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productWayfarer, + Sku = productWayfarer.Sku + "_green-classic-rayban-black", + + AttributesXml = FormatAttributeXml( + wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "green-classic").Id, + wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "rayban-black").Id), + + StockQuantity = 0, + AllowOutOfStockOrders = true, + IsActive = false, + //Price = 299M, + AssignedMediaFileIds = picturesWayfarer.First(x => x.Name.StartsWith("wayfarer-green-classic-havana-black")).Id.ToString() + }); + + #endregion green-classic-rayban-black + + #region green-classic-havana + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productWayfarer, + Sku = productWayfarer.Sku + "_green-classic-havana", + + AttributesXml = FormatAttributeXml( + wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "green-classic").Id, + wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana").Id), + + StockQuantity = 0, + AllowOutOfStockOrders = true, + IsActive = false, + //Price = 299M, + AssignedMediaFileIds = picturesWayfarer.First(x => x.Name.StartsWith("wayfarer-green-classic-havana-black")).Id.ToString() + }); + + #endregion gray-course-rayban-black + + #region brown-course-havana-black + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productWayfarer, + Sku = productWayfarer.Sku + "_brown-course-havana-black", + + AttributesXml = FormatAttributeXml( + wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "brown-course").Id, + wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana-black").Id), + + StockQuantity = 0, + AllowOutOfStockOrders = true, + IsActive = false, + //Price = 299M, + AssignedMediaFileIds = picturesWayfarer.First(x => x.Name.StartsWith("wayfarer-brown-course-havana")).Id.ToString() + }); + + #endregion brown-course-havana-black + + #region brown-course-rayban-black + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productWayfarer, + Sku = productWayfarer.Sku + "_brown-course-rayban-black", + + AttributesXml = FormatAttributeXml( + wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "brown-course").Id, + wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "rayban-black").Id), + + StockQuantity = 0, + AllowOutOfStockOrders = true, + IsActive = false, + //Price = 299M, + AssignedMediaFileIds = picturesWayfarer.First(x => x.Name.StartsWith("wayfarer-brown-course-havana")).Id.ToString() + }); + + #endregion brown-course-rayban-black + + #endregion ORIGINAL WAYFARER AT COLLECTION + + #region Custom Flak + + var productFlak = _ctx.Set().First(x => x.Sku == "P-3002"); + var flakPictureIds = productFlak.ProductPictures.Select(pp => pp.MediaFileId).ToList(); + var picturesFlak = _ctx.Set().Where(x => flakPictureIds.Contains(x.Id)).ToList(); + + //var attributeColorIphone7Plus = _ctx.Set().First(x => x.ProductId == productIphone7Plus.Id && x.ProductAttributeId == attrColor.Id); + + var flakLenscolor = _ctx.Set().First(x => x.ProductId == productFlak.Id && x.ProductAttributeId == attrFlakLenscolor.Id); + var flakLenscolorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == flakLenscolor.Id).ToList(); + + var flakLenstype = _ctx.Set().First(x => x.ProductId == productFlak.Id && x.ProductAttributeId == attrFlakLenstype.Id); + var flakLenstypeValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == flakLenstype.Id).ToList(); + + var flakFramecolor = _ctx.Set().First(x => x.ProductId == productFlak.Id && x.ProductAttributeId == attrFlakFramecolor.Id); + var flakFramecolorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == flakFramecolor.Id).ToList(); + + //#region matteblack-gray-standard + + foreach (var lenscolorValue in flakLenscolorValues) + { + foreach (var framecolorValue in flakFramecolorValues) + { + + foreach (var lenstypeValue in flakLenstypeValues) + { + try + { + entities.Add(new ProductVariantAttributeCombination + { + Product = productFlak, + Sku = productFlak.Sku + string.Concat("-", framecolorValue.Alias, "-", lenscolorValue.Alias, "-", lenstypeValue.Alias), + AttributesXml = FormatAttributeXml(flakLenscolor.Id, lenscolorValue.Id, flakLenstype.Id, lenstypeValue.Id, flakFramecolor.Id, framecolorValue.Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + + AssignedMediaFileIds = picturesFlak.First(x => x.Name.Contains(framecolorValue.Alias + "_" + lenscolorValue.Alias)).Id.ToString(), + + //Price = ballChairPrice + }); + } + catch + { + Console.WriteLine("An error occurred: '{0}'", framecolorValue.Alias + "_" + lenscolorValue.Alias); + } + } + } + } + + #endregion Custom Flak + + #region ps3 + + var productPs3 = _ctx.Set().First(x => x.Sku == "Sony-PS399000"); + var ps3PictureIds = productPs3.ProductPictures.Select(pp => pp.MediaFileId).ToList(); + var picturesPs3 = _ctx.Set().Where(x => ps3PictureIds.Contains(x.Id)).ToList(); + + var productAttributeColor = _ctx.Set().First(x => x.ProductId == productPs3.Id && x.ProductAttributeId == attrController.Id); + var attributeColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == productAttributeColor.Id).ToList(); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productPs3, + Sku = productPs3.Sku + "-B", + AttributesXml = FormatAttributeXml(productAttributeColor.Id, attributeColorValues.First(x => x.Alias == "with_controller").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesPs3.First(x => x.Name.Contains("-controller")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productPs3, + Sku = productPs3.Sku + "-W", + AttributesXml = FormatAttributeXml(productAttributeColor.Id, attributeColorValues.First(x => x.Alias == "without_controller").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesPs3.First(x => x.Name.Contains("-single")).Id.ToString() + }); + + #endregion ps3 + + #region Apple Airpod + + var productAirpod = _ctx.Set().First(x => x.Sku == "P-2003"); + var airpodPictureIds = productAirpod.ProductPictures.Select(pp => pp.MediaFileId).ToList(); + var picturesAirpod = _ctx.Set().Where(x => airpodPictureIds.Contains(x.Id)).ToList(); + + var airpodAttributeColor = _ctx.Set().First(x => x.ProductId == productAirpod.Id && x.ProductAttributeId == attrColor.Id); + var airpodAttributeColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == airpodAttributeColor.Id).ToList(); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productAirpod, + Sku = productAirpod.Sku + "-gold", + AttributesXml = FormatAttributeXml(airpodAttributeColor.Id, airpodAttributeColorValues.First(x => x.Alias == "gold").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesAirpod.First(x => x.Name.Contains("-gold")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productAirpod, + Sku = productAirpod.Sku + "-rose", + AttributesXml = FormatAttributeXml(airpodAttributeColor.Id, airpodAttributeColorValues.First(x => x.Alias == "rose").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesAirpod.First(x => x.Name.Contains("-rose")).Id.ToString() + }); + entities.Add(new ProductVariantAttributeCombination() + { + Product = productAirpod, + Sku = productAirpod.Sku + "-mint", + AttributesXml = FormatAttributeXml(airpodAttributeColor.Id, airpodAttributeColorValues.First(x => x.Alias == "mint").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesAirpod.First(x => x.Name.Contains("-mint")).Id.ToString() + }); + entities.Add(new ProductVariantAttributeCombination() + { + Product = productAirpod, + Sku = productAirpod.Sku + "-lightblue", + AttributesXml = FormatAttributeXml(airpodAttributeColor.Id, airpodAttributeColorValues.First(x => x.Alias == "lightblue").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesAirpod.First(x => x.Name.Contains("-lightblue")).Id.ToString() + }); + entities.Add(new ProductVariantAttributeCombination() + { + Product = productAirpod, + Sku = productAirpod.Sku + "-turquoise", + AttributesXml = FormatAttributeXml(airpodAttributeColor.Id, airpodAttributeColorValues.First(x => x.Alias == "turquoise").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesAirpod.First(x => x.Name.Contains("-turquoise")).Id.ToString() + }); + entities.Add(new ProductVariantAttributeCombination() + { + Product = productAirpod, + Sku = productAirpod.Sku + "-white", + AttributesXml = FormatAttributeXml(airpodAttributeColor.Id, airpodAttributeColorValues.First(x => x.Alias == "white").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesAirpod.First(x => x.Name.Contains("-white")).Id.ToString() + }); + + #endregion Apple Airpod + + #region 9,7 Ipad + + var productiPad97 = _ctx.Set().First(x => x.Sku == "P-2004"); + var iPad97PictureIds = productiPad97.ProductPictures.Select(pp => pp.MediaFileId).ToList(); + var picturesiPad97 = _ctx.Set().Where(x => iPad97PictureIds.Contains(x.Id)).ToList(); + + //var attributeColorIphone7Plus = _ctx.Set().First(x => x.ProductId == productIphone7Plus.Id && x.ProductAttributeId == attrColor.Id); + + var iPad97Color = _ctx.Set().First(x => x.ProductId == productiPad97.Id && x.ProductAttributeId == attr97iPadColors.Id); + var iPad97ColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == iPad97Color.Id).ToList(); + + var ipad97Capacity = _ctx.Set().First(x => x.ProductId == productiPad97.Id && x.ProductAttributeId == attrMemoryCapacity.Id); + var iPad97CapacityValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == ipad97Capacity.Id).ToList(); + + #region silver + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "-silver-64gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "silver").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + Price = 299M, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-silver")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "silver-128gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "silver").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-silver")).Id.ToString() + }); + + #endregion silver + + #region gold + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "-gold-64gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "gold").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), + Price = 279M, + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-gold")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "gold-128gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "gold").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-gold")).Id.ToString() + }); + #endregion gold + + #region spacegray + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "-spacegray-64gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "spacegray").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-spacegray")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "spacegray-128gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "spacegray").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-spacegray")).Id.ToString() + }); + #endregion spacegray + + #region rose + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "-rose-64gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "rose").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-rose")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "rose-128gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "rose").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-rose")).Id.ToString() + }); + #endregion rose + + #region mint + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "-mint-64gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "mint").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-mint")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "mint-128gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "mint").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-mint")).Id.ToString() + }); + #endregion mint + + #region purple + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "-purple-64gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "purple").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-purple")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "purple-128gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "purple").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-purple")).Id.ToString() + }); + #endregion purple + + #region lightblue + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "-lightblue-64gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "lightblue").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-lightblue")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "lightblue-128gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "lightblue").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-lightblue")).Id.ToString() + }); + #endregion lightblue + + #region yellow + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "-yellow-64gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "yellow").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-yellow")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "yellow-128gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "yellow").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-yellow")).Id.ToString() + }); + #endregion yellow + + #region turquoise + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "-turquoise-64gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "turquoise").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-turquoise")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productiPad97, + Sku = productiPad97.Sku + "turquoise-128gb", + + AttributesXml = FormatAttributeXml( + iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "turquoise").Id, + ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesiPad97.First(x => x.Name.Contains("-turquoise")).Id.ToString() + }); + #endregion turquoise + + #endregion 9,7 Ipad + + #region Iphone 7 plus + + var productIphone7Plus = _ctx.Set().First(x => x.Sku == "P-2001"); + var Iphone7PlusPictureIds = productIphone7Plus.ProductPictures.Select(pp => pp.MediaFileId).ToList(); + var picturesIphone7Plus = _ctx.Set().Where(x => Iphone7PlusPictureIds.Contains(x.Id)).ToList(); + + //var attributeColorIphone7Plus = _ctx.Set().First(x => x.ProductId == productIphone7Plus.Id && x.ProductAttributeId == attrColor.Id); + + var Iphone7PlusColor = _ctx.Set().First(x => x.ProductId == productIphone7Plus.Id && x.ProductAttributeId == attrColorIphoneColors.Id); + var Iphone7PlusColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == Iphone7PlusColor.Id).ToList(); + + var Iphone7PlusCapacity = _ctx.Set().First(x => x.ProductId == productIphone7Plus.Id && x.ProductAttributeId == attrMemoryCapacity.Id); + var Iphone7PlusCapacityValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == Iphone7PlusCapacity.Id).ToList(); + + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productIphone7Plus, + Sku = productIphone7Plus.Sku + "-black-64gb", + + AttributesXml = FormatAttributeXml( + Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "black").Id, + Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "64gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesIphone7Plus.First(x => x.Name.Contains("-black")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productIphone7Plus, + Sku = productIphone7Plus.Sku + "-black-128gb", + + AttributesXml = FormatAttributeXml( + Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "black").Id, + Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "128gb").Id), + + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesIphone7Plus.First(x => x.Name.Contains("-black")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productIphone7Plus, + Sku = productIphone7Plus.Sku + "-red-64", + AttributesXml = FormatAttributeXml( + Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "red").Id, + Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "64gb").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesIphone7Plus.First(x => x.Name.Contains("-red")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productIphone7Plus, + Sku = productIphone7Plus.Sku + "-red-128", + AttributesXml = FormatAttributeXml( + Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "red").Id, + Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "128gb").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesIphone7Plus.First(x => x.Name.Contains("-red")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productIphone7Plus, + Sku = productIphone7Plus.Sku + "-silver-64", + AttributesXml = FormatAttributeXml( + Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "silver").Id, + Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "64gb").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesIphone7Plus.First(x => x.Name.Contains("-silver")).Id.ToString() + }); + + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productIphone7Plus, + Sku = productIphone7Plus.Sku + "-silver-128", + AttributesXml = FormatAttributeXml( + Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "silver").Id, + Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "128gb").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesIphone7Plus.First(x => x.Name.Contains("-silver")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productIphone7Plus, + Sku = productIphone7Plus.Sku + "-rose-64", + AttributesXml = FormatAttributeXml( + Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "rose").Id, + Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "64gb").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesIphone7Plus.First(x => x.Name.Contains("-rose")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productIphone7Plus, + Sku = productIphone7Plus.Sku + "-rose-128", + AttributesXml = FormatAttributeXml( + Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "rose").Id, + Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "128gb").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesIphone7Plus.First(x => x.Name.Contains("-rose")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productIphone7Plus, + Sku = productIphone7Plus.Sku + "-gold-64", + AttributesXml = FormatAttributeXml( + Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "gold").Id, + Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "64gb").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesIphone7Plus.First(x => x.Name.Contains("-gold")).Id.ToString() + }); + + entities.Add(new ProductVariantAttributeCombination() + { + Product = productIphone7Plus, + Sku = productIphone7Plus.Sku + "-gold-128", + AttributesXml = FormatAttributeXml( + Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "gold").Id, + Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "128gb").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = picturesIphone7Plus.First(x => x.Name.Contains("-gold")).Id.ToString() + }); + + #endregion Iphone 7 plus + + #region Fashion - Converse All Star + + var productAllStar = _ctx.Set().First(x => x.Sku == "Fashion-112355"); + var allStarPictureIds = productAllStar.ProductPictures.Select(x => x.MediaFileId).ToList(); + var allStarPictures = _ctx.Set().Where(x => allStarPictureIds.Contains(x.Id)).ToList(); + + var allStarColor = _ctx.Set().First(x => x.ProductId == productAllStar.Id && x.ProductAttributeId == attrColor.Id); + var allStarColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == allStarColor.Id).ToList(); + + var allStarSize = _ctx.Set().First(x => x.ProductId == productAllStar.Id && x.ProductAttributeId == attrSize.Id); + var allStarSizeValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == allStarSize.Id).ToList(); + + var allStarCombinations = new[] + { + new { Color = "Charcoal", Size = "42" }, + new { Color = "Charcoal", Size = "43" }, + new { Color = "Charcoal", Size = "44" }, + new { Color = "Maroon", Size = "42" }, + new { Color = "Maroon", Size = "43" }, + new { Color = "Maroon", Size = "44" }, + new { Color = "Navy", Size = "42" }, + new { Color = "Navy", Size = "43" }, + new { Color = "Navy", Size = "44" }, + new { Color = "Purple", Size = "42" }, + new { Color = "Purple", Size = "43" }, + new { Color = "Purple", Size = "44" }, + new { Color = "White", Size = "42" }, + new { Color = "White", Size = "43" }, + new { Color = "White", Size = "44" }, + }; + + foreach (var comb in allStarCombinations) + { + var lowerColor = comb.Color.ToLower(); + entities.Add(new ProductVariantAttributeCombination + { + Product = productAllStar, + Sku = productAllStar.Sku + string.Concat("-", lowerColor, "-", comb.Size), + AttributesXml = FormatAttributeXml( + allStarColor.Id, allStarColorValues.First(x => x.Alias == lowerColor).Id, + allStarSize.Id, allStarSizeValues.First(x => x.Alias == comb.Size).Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = allStarPictures.First(x => x.Name.Contains(lowerColor)).Id.ToString() + }); + } + + #endregion + + #region Fashion - Shirt Meccanica + + var productShirtMeccanica = _ctx.Set().First(x => x.Sku == "Fashion-987693502"); + var shirtMeccanicaPictureIds = productShirtMeccanica.ProductPictures.Select(x => x.MediaFileId).ToList(); + var shirtMeccanicaPictures = _ctx.Set().Where(x => shirtMeccanicaPictureIds.Contains(x.Id)).ToList(); + + var shirtMeccanicaColor = _ctx.Set().First(x => x.ProductId == productShirtMeccanica.Id && x.ProductAttributeId == attrColor.Id); + var shirtMeccanicaColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == shirtMeccanicaColor.Id).ToList(); + + var shirtMeccanicaSize = _ctx.Set().First(x => x.ProductId == productShirtMeccanica.Id && x.ProductAttributeId == attrSize.Id); + var shirtMeccanicaSizeValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == shirtMeccanicaSize.Id).ToList(); + + var shirtMeccanicaCombinations = new[] + { + new { Color = "Red", Size = "XS" }, + new { Color = "Red", Size = "S" }, + new { Color = "Red", Size = "M" }, + new { Color = "Red", Size = "L" }, + new { Color = "Red", Size = "XL" }, + new { Color = "Black", Size = "XS" }, + new { Color = "Black", Size = "S" }, + new { Color = "Black", Size = "M" }, + new { Color = "Black", Size = "L" }, + new { Color = "Black", Size = "XL" } + }; + + foreach (var comb in shirtMeccanicaCombinations) + { + var lowerColor = comb.Color.ToLower(); + var lowerSize = comb.Size.ToLower(); + var pictureIds = shirtMeccanicaPictures.Where(x => x.Name.Contains($"_{lowerColor}_")).Select(x => x.Id); + + entities.Add(new ProductVariantAttributeCombination + { + Product = productShirtMeccanica, + Sku = productShirtMeccanica.Sku + string.Concat("-", lowerColor, "-", lowerSize), + AttributesXml = FormatAttributeXml( + shirtMeccanicaColor.Id, shirtMeccanicaColorValues.First(x => x.Alias == lowerColor).Id, + shirtMeccanicaSize.Id, shirtMeccanicaSizeValues.First(x => x.Alias == lowerSize).Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = string.Join(",", pictureIds) + }); + } + + #endregion + + #region Fashion - Ladies Jacket + + var productLadiesJacket = _ctx.Set().First(x => x.Sku == "Fashion-JN1107"); + var ladiesJacketPictureIds = productLadiesJacket.ProductPictures.Select(x => x.MediaFileId).ToList(); + var ladiesJacketPictures = _ctx.Set().Where(x => ladiesJacketPictureIds.Contains(x.Id)).ToList(); + + var ladiesJacketColor = _ctx.Set().First(x => x.ProductId == productLadiesJacket.Id && x.ProductAttributeId == attrColor.Id); + var ladiesJacketColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == ladiesJacketColor.Id).ToList(); + + var ladiesJacketSize = _ctx.Set().First(x => x.ProductId == productLadiesJacket.Id && x.ProductAttributeId == attrSize.Id); + var ladiesJacketSizeValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == ladiesJacketSize.Id).ToList(); + + var ladiesJacketCombinations = new[] + { + new { Color = "Red", Size = "XS" }, + new { Color = "Red", Size = "S" }, + new { Color = "Red", Size = "M" }, + new { Color = "Red", Size = "L" }, + new { Color = "Red", Size = "XL" }, + new { Color = "Orange", Size = "XS" }, + new { Color = "Orange", Size = "S" }, + new { Color = "Orange", Size = "M" }, + new { Color = "Orange", Size = "L" }, + new { Color = "Orange", Size = "XL" }, + new { Color = "Green", Size = "XS" }, + new { Color = "Green", Size = "S" }, + new { Color = "Green", Size = "M" }, + new { Color = "Green", Size = "L" }, + new { Color = "Green", Size = "XL" }, + new { Color = "Blue", Size = "XS" }, + new { Color = "Blue", Size = "S" }, + new { Color = "Blue", Size = "M" }, + new { Color = "Blue", Size = "L" }, + new { Color = "Blue", Size = "XL" }, + new { Color = "Navy", Size = "XS" }, + new { Color = "Navy", Size = "S" }, + new { Color = "Navy", Size = "M" }, + new { Color = "Navy", Size = "L" }, + new { Color = "Navy", Size = "XL" }, + new { Color = "Silver", Size = "XS" }, + new { Color = "Silver", Size = "S" }, + new { Color = "Silver", Size = "M" }, + new { Color = "Silver", Size = "L" }, + new { Color = "Silver", Size = "XL" }, + new { Color = "Black", Size = "XS" }, + new { Color = "Black", Size = "S" }, + new { Color = "Black", Size = "M" }, + new { Color = "Black", Size = "L" }, + new { Color = "Black", Size = "XL" } + }; + + foreach (var comb in ladiesJacketCombinations) + { + var lowerColor = comb.Color.ToLower(); + var lowerSize = comb.Size.ToLower(); + + entities.Add(new ProductVariantAttributeCombination + { + Product = productLadiesJacket, + Sku = productLadiesJacket.Sku + string.Concat("-", lowerColor, "-", lowerSize), + AttributesXml = FormatAttributeXml( + ladiesJacketColor.Id, ladiesJacketColorValues.First(x => x.Alias == lowerColor).Id, + ladiesJacketSize.Id, ladiesJacketSizeValues.First(x => x.Alias == lowerSize).Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = ladiesJacketPictures.First(x => x.Name.Contains(lowerColor)).Id.ToString() + }); + } + + #endregion + + #region Furniture - Le Corbusier LC 6 table + + var productCorbusierTable = _ctx.Set().First(x => x.Sku == "Furniture-lc6"); + + var corbusierTablePlate = _ctx.Set().First(x => x.ProductId == productCorbusierTable.Id && x.ProductAttributeId == attrPlate.Id); + var corbusierTablePlateValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == corbusierTablePlate.Id).ToList(); + + var corbusierTablePlateThickness = _ctx.Set().First(x => x.ProductId == productCorbusierTable.Id && x.ProductAttributeId == attrPlateThickness.Id); + var corbusierTablePlateThicknessValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == corbusierTablePlateThickness.Id).ToList(); + + entities.Add(new ProductVariantAttributeCombination + { + Product = productCorbusierTable, + Sku = productCorbusierTable.Sku + "-clear-15", + AttributesXml = FormatAttributeXml( + corbusierTablePlate.Id, corbusierTablePlateValues.First(x => x.Alias == "clear-glass").Id, + corbusierTablePlateThickness.Id, corbusierTablePlateThicknessValues.First(x => x.Alias == "15mm").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + Price = 749.00M + }); + entities.Add(new ProductVariantAttributeCombination + { + Product = productCorbusierTable, + Sku = productCorbusierTable.Sku + "-clear-19", + AttributesXml = FormatAttributeXml( + corbusierTablePlate.Id, corbusierTablePlateValues.First(x => x.Alias == "clear-glass").Id, + corbusierTablePlateThickness.Id, corbusierTablePlateThicknessValues.First(x => x.Alias == "19mm").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + Price = 899.00M + }); + entities.Add(new ProductVariantAttributeCombination + { + Product = productCorbusierTable, + Sku = productCorbusierTable.Sku + "-sandblasted-15", + AttributesXml = FormatAttributeXml( + corbusierTablePlate.Id, corbusierTablePlateValues.First(x => x.Alias == "sandblasted-glass").Id, + corbusierTablePlateThickness.Id, corbusierTablePlateThicknessValues.First(x => x.Alias == "15mm").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + Price = 849.00M + }); + entities.Add(new ProductVariantAttributeCombination + { + Product = productCorbusierTable, + Sku = productCorbusierTable.Sku + "-sandblasted-19", + AttributesXml = FormatAttributeXml( + corbusierTablePlate.Id, corbusierTablePlateValues.First(x => x.Alias == "sandblasted-glass").Id, + corbusierTablePlateThickness.Id, corbusierTablePlateThicknessValues.First(x => x.Alias == "19mm").Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + Price = 999.00M + }); + + #endregion + + #region Soccer Adidas TANGO SALA BALL + + var productAdidasTANGOSALABALL = _ctx.Set().First(x => x.Sku == "P-5001"); + var adidasTANGOSALABALLPictureIds = productAdidasTANGOSALABALL.ProductPictures.Select(x => x.MediaFileId).ToList(); + var adidasTANGOSALABALLJacketPictures = _ctx.Set().Where(x => adidasTANGOSALABALLPictureIds.Contains(x.Id)).ToList(); + + var adidasTANGOSALABALLColor = _ctx.Set().First(x => x.ProductId == productAdidasTANGOSALABALL.Id && x.ProductAttributeId == attrColor.Id); + var adidasTANGOSALABALLColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == adidasTANGOSALABALLColor.Id).ToList(); + + var adidasTANGOSALABALLSize = _ctx.Set().First(x => x.ProductId == productAdidasTANGOSALABALL.Id && x.ProductAttributeId == attrSize.Id); + var adidasTANGOSALABALLSizeValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == adidasTANGOSALABALLSize.Id).ToList(); + + var adidasTANGOSALABALLCombinations = new[] + { + new { Color = "Red", Size = "3" }, + new { Color = "Red", Size = "4" }, + new { Color = "Red", Size = "5" }, + + new { Color = "Yellow", Size = "3" }, + new { Color = "Yellow", Size = "4" }, + new { Color = "Yellow", Size = "5" }, + + new { Color = "Green", Size = "3" }, + new { Color = "Green", Size = "4" }, + new { Color = "Green", Size = "5" }, + + new { Color = "Blue", Size = "3" }, + new { Color = "Blue", Size = "4" }, + new { Color = "Blue", Size = "5" }, + + new { Color = "Gray", Size = "3" }, + new { Color = "Gray", Size = "4" }, + new { Color = "Gray", Size = "5" }, + + new { Color = "White", Size = "3" }, + new { Color = "White", Size = "4" }, + new { Color = "White", Size = "5" }, + + new { Color = "Brown", Size = "3" }, + new { Color = "Brown", Size = "4" }, + new { Color = "Brown", Size = "5" }, + + }; + + foreach (var comb in adidasTANGOSALABALLCombinations) + { + var lowerColor = comb.Color.ToLower(); + var lowerSize = comb.Size.ToLower(); + + entities.Add(new ProductVariantAttributeCombination + { + Product = productAdidasTANGOSALABALL, + Sku = productAdidasTANGOSALABALL.Sku + string.Concat("-", lowerColor, "-", lowerSize), + AttributesXml = FormatAttributeXml( + adidasTANGOSALABALLColor.Id, adidasTANGOSALABALLColorValues.First(x => x.Alias == lowerColor).Id, + adidasTANGOSALABALLSize.Id, adidasTANGOSALABALLSizeValues.First(x => x.Alias == lowerSize).Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = adidasTANGOSALABALLJacketPictures.First(x => x.Name.Contains(lowerColor)).Id.ToString() + }); + } + + #endregion Soccer Adidas TANGO SALA BALL + + #region Soccer Torfabrik official game ball + + var productTorfabrikBall = _ctx.Set().First(x => x.Sku == "P-5002"); + var torfabrikBallPictureIds = productTorfabrikBall.ProductPictures.Select(x => x.MediaFileId).ToList(); + var torfabrikBallPictures = _ctx.Set().Where(x => torfabrikBallPictureIds.Contains(x.Id)).ToList(); + + var torfabrikBallColor = _ctx.Set().First(x => x.ProductId == productTorfabrikBall.Id && x.ProductAttributeId == attrColor.Id); + var torfabrikBallColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == torfabrikBallColor.Id).ToList(); + + var torfabrikBallSize = _ctx.Set().First(x => x.ProductId == productTorfabrikBall.Id && x.ProductAttributeId == attrSize.Id); + var torfabrikBallSizeValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == torfabrikBallSize.Id).ToList(); + + var torfabrikBallSizeCombinations = new[] + { + new { Color = "Red", Size = "3" }, + new { Color = "Red", Size = "4" }, + new { Color = "Red", Size = "5" }, + + new { Color = "Yellow", Size = "3" }, + new { Color = "Yellow", Size = "4" }, + new { Color = "Yellow", Size = "5" }, + + new { Color = "Green", Size = "3" }, + new { Color = "Green", Size = "4" }, + new { Color = "Green", Size = "5" }, + + new { Color = "Blue", Size = "3" }, + new { Color = "Blue", Size = "4" }, + new { Color = "Blue", Size = "5" }, + + new { Color = "White", Size = "3" }, + new { Color = "White", Size = "4" }, + new { Color = "White", Size = "5" }, + + }; + + foreach (var comb in torfabrikBallSizeCombinations) + { + var lowerColor = comb.Color.ToLower(); + var lowerSize = comb.Size.ToLower(); + + entities.Add(new ProductVariantAttributeCombination + { + Product = productTorfabrikBall, + Sku = productTorfabrikBall.Sku + string.Concat("-", lowerColor, "-", lowerSize), + AttributesXml = FormatAttributeXml( + torfabrikBallColor.Id, torfabrikBallColorValues.First(x => x.Alias == lowerColor).Id, + torfabrikBallSize.Id, torfabrikBallSizeValues.First(x => x.Alias == lowerSize).Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = torfabrikBallPictures.First(x => x.Name.Contains(lowerColor)).Id.ToString() + }); + } + + #endregion Soccer Torfabrik official game ball + + #region Furniture - Ball chair + + var productBallChair = _ctx.Set().First(x => x.Sku == "Furniture-ball-chair"); + var ballChairPictureIds = productBallChair.ProductPictures.Select(x => x.MediaFileId).ToList(); + var ballChairPictures = _ctx.Set().Where(x => ballChairPictureIds.Contains(x.Id)).ToList(); + + var ballChairMaterial = _ctx.Set().First(x => x.ProductId == productBallChair.Id && x.ProductAttributeId == attrMaterial.Id); + var ballChairMaterialValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == ballChairMaterial.Id).ToList(); + + var ballChairColor = _ctx.Set().First(x => x.ProductId == productBallChair.Id && x.ProductAttributeId == attrColor.Id); + var ballChairColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == ballChairColor.Id).ToList(); + + var ballChairLeatherColor = _ctx.Set().First(x => x.ProductId == productBallChair.Id && x.ProductAttributeId == attrLeatherColor.Id); + var ballChairLeatherColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == ballChairLeatherColor.Id).ToList(); + + foreach (var materialValue in ballChairMaterialValues) + { + foreach (var colorValue in ballChairColorValues) + { + decimal ballChairPrice = 2199.00M; + + if (materialValue.Alias.StartsWith("leather-special")) + { + ballChairPrice = 2599.00M; + } + else if (materialValue.Alias.StartsWith("leather-aniline")) + { + ballChairPrice = 2999.00M; + } + + foreach (var leatherColorValue in ballChairLeatherColorValues) + { + entities.Add(new ProductVariantAttributeCombination + { + Product = productBallChair, + Sku = productBallChair.Sku + string.Concat("-", colorValue.Alias, "-", materialValue.Alias), + AttributesXml = FormatAttributeXml(ballChairMaterial.Id, materialValue.Id, ballChairColor.Id, colorValue.Id, ballChairLeatherColor.Id, leatherColorValue.Id), + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true, + AssignedMediaFileIds = ballChairPictures.First(x => x.Name.Contains(colorValue.Alias)).Id.ToString(), + Price = ballChairPrice + }); + } + } + } + + #endregion + + return entities; + } + } +} diff --git a/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.cs b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.cs index 25ec8fc33c..8f1ac6d219 100644 --- a/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.cs +++ b/src/Libraries/SmartStore.Data/Setup/SeedData/InvariantSeedData.cs @@ -3,7 +3,6 @@ using System.Globalization; using System.IO; using System.Linq; -using System.Text; using SmartStore.Core; using SmartStore.Core.Configuration; using SmartStore.Core.Domain; @@ -30,13812 +29,1577 @@ using SmartStore.Core.Domain.Tax; using SmartStore.Core.Domain.Themes; using SmartStore.Core.Domain.Topics; +using SmartStore.Core.IO; +using SmartStore.Rules; +using SmartStore.Rules.Domain; using SmartStore.Utilities; namespace SmartStore.Data.Setup { - public abstract class InvariantSeedData - { - private SmartObjectContext _ctx; - private string _sampleImagesPath; - private string _sampleDownloadsPath; - - protected InvariantSeedData() - { - } - - public void Initialize(SmartObjectContext context) - { - _ctx = context; - - _sampleImagesPath = CommonHelper.MapPath("~/App_Data/Samples/"); - _sampleDownloadsPath = CommonHelper.MapPath("~/App_Data/Samples/"); - } - - #region Mandatory data creators - - public IList Pictures() - { - var entities = new List - { - CreatePicture(File.ReadAllBytes(_sampleImagesPath + "company_logo.png"), "image/png", GetSeName("company-logo")), - - CreatePicture(File.ReadAllBytes(_sampleImagesPath + "product_allstar_charcoal.jpg"), "image/jpeg", "all-star-charcoal"), - CreatePicture(File.ReadAllBytes(_sampleImagesPath + "product_allstar_maroon.jpg"), "image/jpeg", "all-star-maroon"), - CreatePicture(File.ReadAllBytes(_sampleImagesPath + "product_allstar_navy.jpg"), "image/jpeg", "all-star-navy"), - CreatePicture(File.ReadAllBytes(_sampleImagesPath + "product_allstar_purple.jpg"), "image/jpeg", "all-star-purple"), - CreatePicture(File.ReadAllBytes(_sampleImagesPath + "product_allstar_white.jpg"), "image/jpeg", "all-star-white"), - - CreatePicture(File.ReadAllBytes(_sampleImagesPath + "wayfarer_havana.png"), "image/png", "wayfarer_havana"), - CreatePicture(File.ReadAllBytes(_sampleImagesPath + "wayfarer_havana_black.png"), "image/png", "wayfarer_havana_black"), - CreatePicture(File.ReadAllBytes(_sampleImagesPath + "wayfarer_rayban-black.png"), "image/png", "wayfarer_rayban_black") - }; - - this.Alter(entities); - return entities; - } - - public IList Stores() - { - var seName = GetSeName("company-logo"); - var imgCompanyLogo = _ctx.Set().Where(x => x.SeoFilename == seName).FirstOrDefault(); - - var currency = _ctx.Set().FirstOrDefault(x => x.CurrencyCode == "EUR"); - if (currency == null) - currency = _ctx.Set().First(); - - var entities = new List() - { - new Store() - { - Name = "Your store name", - Url = "http://www.yourStore.com/", - Hosts = "yourstore.com,www.yourstore.com", - SslEnabled = false, - DisplayOrder = 1, - LogoPictureId = imgCompanyLogo.Id, - PrimaryStoreCurrencyId = currency.Id, - PrimaryExchangeRateCurrencyId = currency.Id - } - }; - this.Alter(entities); - return entities; - } - - public IList MeasureDimensions() - { - var entities = new List() - { - new MeasureDimension() - { - Name = "millimetre", - SystemKeyword = "mm", - Ratio = 25.4M, - DisplayOrder = 1, - }, - new MeasureDimension() - { - Name = "centimetre", - SystemKeyword = "cm", - Ratio = 0.254M, - DisplayOrder = 2, - }, - new MeasureDimension() - { - Name = "meter", - SystemKeyword = "m", - Ratio = 0.0254M, - DisplayOrder = 3, - }, - new MeasureDimension() - { - Name = "in", - SystemKeyword = "inch", - Ratio = 1M, - DisplayOrder = 4, - }, - new MeasureDimension() - { - Name = "feet", - SystemKeyword = "ft", - Ratio = 0.08333333M, - DisplayOrder = 5, - } - }; - - this.Alter(entities); - return entities; - } - - public IList MeasureWeights() - { - var entities = new List() - { - new MeasureWeight() - { - Name = "ounce", // Ounce, Unze - SystemKeyword = "oz", - Ratio = 16M, - DisplayOrder = 5, - }, - new MeasureWeight() - { - Name = "lb", // Pound - SystemKeyword = "lb", - Ratio = 1M, - DisplayOrder = 6, - }, - - new MeasureWeight() - { - Name = "kg", - SystemKeyword = "kg", - Ratio = 0.45359237M, - DisplayOrder = 1, - }, - new MeasureWeight() - { - Name = "gram", - SystemKeyword = "g", - Ratio = 453.59237M, - DisplayOrder = 2, - }, - new MeasureWeight() - { - Name = "liter", - SystemKeyword = "l", - Ratio = 0.45359237M, - DisplayOrder = 3, - }, - new MeasureWeight() - { - Name = "milliliter", - SystemKeyword = "ml", - Ratio = 0.45359237M, - DisplayOrder = 4, - } - }; - - this.Alter(entities); - return entities; - } - - protected virtual string TaxNameBooks - { - get => "Books"; - } - - protected virtual string TaxNameDigitalGoods - { - get => "Downloadable Products"; - } - - protected virtual string TaxNameJewelry - { - get => "Jewelry"; - } - - protected virtual string TaxNameApparel - { - get => "Apparel & Shoes"; - } - - protected virtual string TaxNameFood - { - get => "Food"; - } - - protected virtual string TaxNameElectronics - { - get => "Electronics & Software"; - } - - protected virtual string TaxNameTaxFree - { - get => "Tax free"; - } - - public virtual decimal[] FixedTaxRates - { - get => new decimal[] { 0, 0, 0, 0, 0 }; - } - - public IList TaxCategories() - { - var entities = new List - { - new TaxCategory - { - Name = this.TaxNameBooks, - DisplayOrder = 1, - }, - new TaxCategory - { - Name = this.TaxNameElectronics, - DisplayOrder = 5, - }, - new TaxCategory - { - Name = this.TaxNameDigitalGoods, - DisplayOrder = 10, - }, - new TaxCategory - { - Name = this.TaxNameJewelry, - DisplayOrder = 15, - }, - new TaxCategory - { - Name = this.TaxNameApparel, - DisplayOrder = 20, - }, - }; - - this.Alter(entities); - return entities; - } - - public IList Currencies() - { - var entities = new List() - { - CreateCurrency("en-US", published: true, rate: 1M, order: 0), - CreateCurrency("en-GB", published: true, rate: 0.61M, order: 5), - CreateCurrency("en-AU", published: true, rate: 0.94M, order: 10), - CreateCurrency("en-CA", published: true, rate: 0.98M, order: 15), - CreateCurrency("de-DE", rate: 0.79M, order: 20/*, formatting: string.Format("0.00 {0}", "\u20ac")*/), - CreateCurrency("de-CH", rate: 0.93M, order: 25, formatting: "CHF #,##0.00"), - CreateCurrency("zh-CN", rate: 6.48M, order: 30), - CreateCurrency("zh-HK", rate: 7.75M, order: 35), - CreateCurrency("ja-JP", rate: 80.07M, order: 40), - CreateCurrency("ru-RU", rate: 27.7M, order: 45), - CreateCurrency("tr-TR", rate: 1.78M, order: 50), - CreateCurrency("sv-SE", rate: 6.19M, order: 55) - }; - - this.Alter(entities); - return entities; - } - - public IList Countries() - { - var cUsa = new Country - { - Name = "United States", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "US", - ThreeLetterIsoCode = "USA", - NumericIsoCode = 840, - SubjectToVat = false, - DisplayOrder = 1, - Published = true, - }; - - #region US Regions - - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "AA (Armed Forces Americas)", - Abbreviation = "AA", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "AE (Armed Forces Europe)", - Abbreviation = "AE", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Alabama", - Abbreviation = "AL", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Alaska", - Abbreviation = "AK", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "American Samoa", - Abbreviation = "AS", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "AP (Armed Forces Pacific)", - Abbreviation = "AP", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Arizona", - Abbreviation = "AZ", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Arkansas", - Abbreviation = "AR", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "California", - Abbreviation = "CA", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Colorado", - Abbreviation = "CO", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Connecticut", - Abbreviation = "CT", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Delaware", - Abbreviation = "DE", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "District of Columbia", - Abbreviation = "DC", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Federated States of Micronesia", - Abbreviation = "FM", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Florida", - Abbreviation = "FL", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Georgia", - Abbreviation = "GA", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Guam", - Abbreviation = "GU", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Hawaii", - Abbreviation = "HI", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Idaho", - Abbreviation = "ID", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Illinois", - Abbreviation = "IL", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Indiana", - Abbreviation = "IN", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Iowa", - Abbreviation = "IA", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Kansas", - Abbreviation = "KS", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Kentucky", - Abbreviation = "KY", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Louisiana", - Abbreviation = "LA", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Maine", - Abbreviation = "ME", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Marshall Islands", - Abbreviation = "MH", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Maryland", - Abbreviation = "MD", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Massachusetts", - Abbreviation = "MA", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Michigan", - Abbreviation = "MI", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Minnesota", - Abbreviation = "MN", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Mississippi", - Abbreviation = "MS", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Missouri", - Abbreviation = "MO", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Montana", - Abbreviation = "MT", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Nebraska", - Abbreviation = "NE", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Nevada", - Abbreviation = "NV", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "New Hampshire", - Abbreviation = "NH", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "New Jersey", - Abbreviation = "NJ", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "New Mexico", - Abbreviation = "NM", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "New York", - Abbreviation = "NY", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "North Carolina", - Abbreviation = "NC", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "North Dakota", - Abbreviation = "ND", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Northern Mariana Islands", - Abbreviation = "MP", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Ohio", - Abbreviation = "OH", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Oklahoma", - Abbreviation = "OK", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Oregon", - Abbreviation = "OR", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Palau", - Abbreviation = "PW", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Pennsylvania", - Abbreviation = "PA", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Puerto Rico", - Abbreviation = "PR", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Rhode Island", - Abbreviation = "RI", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "South Carolina", - Abbreviation = "SC", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "South Dakota", - Abbreviation = "SD", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Tennessee", - Abbreviation = "TN", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Texas", - Abbreviation = "TX", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Utah", - Abbreviation = "UT", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Vermont", - Abbreviation = "VT", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Virgin Islands", - Abbreviation = "VI", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Virginia", - Abbreviation = "VA", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Washington", - Abbreviation = "WA", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "West Virginia", - Abbreviation = "WV", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Wisconsin", - Abbreviation = "WI", - Published = true, - DisplayOrder = 1, - }); - cUsa.StateProvinces.Add(new StateProvince() - { - Name = "Wyoming", - Abbreviation = "WY", - Published = true, - DisplayOrder = 1, - }); - - #endregion - - var cCanada = new Country - { - Name = "Canada", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CA", - ThreeLetterIsoCode = "CAN", - NumericIsoCode = 124, - SubjectToVat = false, - DisplayOrder = 2, - Published = true, - }; - - #region CA Regions - - cCanada.StateProvinces.Add(new StateProvince() - { - Name = "Alberta", - Abbreviation = "AB", - Published = true, - DisplayOrder = 1, - }); - cCanada.StateProvinces.Add(new StateProvince() - { - Name = "British Columbia", - Abbreviation = "BC", - Published = true, - DisplayOrder = 1, - }); - cCanada.StateProvinces.Add(new StateProvince() - { - Name = "Manitoba", - Abbreviation = "MB", - Published = true, - DisplayOrder = 1, - }); - cCanada.StateProvinces.Add(new StateProvince() - { - Name = "New Brunswick", - Abbreviation = "NB", - Published = true, - DisplayOrder = 1, - }); - cCanada.StateProvinces.Add(new StateProvince() - { - Name = "Newfoundland and Labrador", - Abbreviation = "NL", - Published = true, - DisplayOrder = 1, - }); - cCanada.StateProvinces.Add(new StateProvince() - { - Name = "Northwest Territories", - Abbreviation = "NT", - Published = true, - DisplayOrder = 1, - }); - cCanada.StateProvinces.Add(new StateProvince() - { - Name = "Nova Scotia", - Abbreviation = "NS", - Published = true, - DisplayOrder = 1, - }); - cCanada.StateProvinces.Add(new StateProvince() - { - Name = "Nunavut", - Abbreviation = "NU", - Published = true, - DisplayOrder = 1, - }); - cCanada.StateProvinces.Add(new StateProvince() - { - Name = "Ontario", - Abbreviation = "ON", - Published = true, - DisplayOrder = 1, - }); - cCanada.StateProvinces.Add(new StateProvince() - { - Name = "Prince Edward Island", - Abbreviation = "PE", - Published = true, - DisplayOrder = 1, - }); - cCanada.StateProvinces.Add(new StateProvince() - { - Name = "Quebec", - Abbreviation = "QC", - Published = true, - DisplayOrder = 1, - }); - cCanada.StateProvinces.Add(new StateProvince() - { - Name = "Saskatchewan", - Abbreviation = "SK", - Published = true, - DisplayOrder = 1, - }); - cCanada.StateProvinces.Add(new StateProvince() - { - Name = "Yukon Territory", - Abbreviation = "YU", - Published = true, - DisplayOrder = 1, - }); - #endregion - - var entities = new List() - { - new Country() - { - Name = "Germany", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "DE", - ThreeLetterIsoCode = "DEU", - NumericIsoCode = 276, - SubjectToVat = true, - DisplayOrder = -10, - Published = true - }, - - new Country - { - Name = "Austria", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AT", - ThreeLetterIsoCode = "AUT", - NumericIsoCode = 40, - SubjectToVat = true, - DisplayOrder = -5, - Published = true - }, - new Country - { - Name = "Switzerland", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CH", - ThreeLetterIsoCode = "CHE", - NumericIsoCode = 756, - SubjectToVat = false, - DisplayOrder = -1, - Published = true - }, - cUsa, - cCanada, - - //other countries - new Country - { - Name = "Argentina", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AR", - ThreeLetterIsoCode = "ARG", - NumericIsoCode = 32, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Armenia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AM", - ThreeLetterIsoCode = "ARM", - NumericIsoCode = 51, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Aruba", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AW", - ThreeLetterIsoCode = "ABW", - NumericIsoCode = 533, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Australia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AU", - ThreeLetterIsoCode = "AUS", - NumericIsoCode = 36, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Azerbaijan", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AZ", - ThreeLetterIsoCode = "AZE", - NumericIsoCode = 31, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Bahamas", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BS", - ThreeLetterIsoCode = "BHS", - NumericIsoCode = 44, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Bangladesh", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BD", - ThreeLetterIsoCode = "BGD", - NumericIsoCode = 50, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Belarus", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BY", - ThreeLetterIsoCode = "BLR", - NumericIsoCode = 112, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Belgium", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BE", - ThreeLetterIsoCode = "BEL", - NumericIsoCode = 56, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Belize", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BZ", - ThreeLetterIsoCode = "BLZ", - NumericIsoCode = 84, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Bermuda", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BM", - ThreeLetterIsoCode = "BMU", - NumericIsoCode = 60, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Bolivia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BO", - ThreeLetterIsoCode = "BOL", - NumericIsoCode = 68, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Bosnia and Herzegowina", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BA", - ThreeLetterIsoCode = "BIH", - NumericIsoCode = 70, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Brazil", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BR", - ThreeLetterIsoCode = "BRA", - NumericIsoCode = 76, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Bulgaria", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BG", - ThreeLetterIsoCode = "BGR", - NumericIsoCode = 100, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Cayman Islands", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "KY", - ThreeLetterIsoCode = "CYM", - NumericIsoCode = 136, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Chile", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CL", - ThreeLetterIsoCode = "CHL", - NumericIsoCode = 152, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "China", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CN", - ThreeLetterIsoCode = "CHN", - NumericIsoCode = 156, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Colombia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CO", - ThreeLetterIsoCode = "COL", - NumericIsoCode = 170, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Costa Rica", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CR", - ThreeLetterIsoCode = "CRI", - NumericIsoCode = 188, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Croatia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "HR", - ThreeLetterIsoCode = "HRV", - NumericIsoCode = 191, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Cuba", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CU", - ThreeLetterIsoCode = "CUB", - NumericIsoCode = 192, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Cyprus", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CY", - ThreeLetterIsoCode = "CYP", - NumericIsoCode = 196, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Czech Republic", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CZ", - ThreeLetterIsoCode = "CZE", - NumericIsoCode = 203, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Denmark", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "DK", - ThreeLetterIsoCode = "DNK", - NumericIsoCode = 208, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Dominican Republic", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "DO", - ThreeLetterIsoCode = "DOM", - NumericIsoCode = 214, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Ecuador", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "EC", - ThreeLetterIsoCode = "ECU", - NumericIsoCode = 218, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Egypt", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "EG", - ThreeLetterIsoCode = "EGY", - NumericIsoCode = 818, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Finland", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "FI", - ThreeLetterIsoCode = "FIN", - NumericIsoCode = 246, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "France", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "FR", - ThreeLetterIsoCode = "FRA", - NumericIsoCode = 250, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Georgia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GE", - ThreeLetterIsoCode = "GEO", - NumericIsoCode = 268, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Gibraltar", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GI", - ThreeLetterIsoCode = "GIB", - NumericIsoCode = 292, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Greece", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GR", - ThreeLetterIsoCode = "GRC", - NumericIsoCode = 300, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Guatemala", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GT", - ThreeLetterIsoCode = "GTM", - NumericIsoCode = 320, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Hong Kong", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "HK", - ThreeLetterIsoCode = "HKG", - NumericIsoCode = 344, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Hungary", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "HU", - ThreeLetterIsoCode = "HUN", - NumericIsoCode = 348, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "India", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "IN", - ThreeLetterIsoCode = "IND", - NumericIsoCode = 356, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Indonesia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "ID", - ThreeLetterIsoCode = "IDN", - NumericIsoCode = 360, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Ireland", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "IE", - ThreeLetterIsoCode = "IRL", - NumericIsoCode = 372, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Israel", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "IL", - ThreeLetterIsoCode = "ISR", - NumericIsoCode = 376, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Italy", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "IT", - ThreeLetterIsoCode = "ITA", - NumericIsoCode = 380, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Jamaica", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "JM", - ThreeLetterIsoCode = "JAM", - NumericIsoCode = 388, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Japan", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "JP", - ThreeLetterIsoCode = "JPN", - NumericIsoCode = 392, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Jordan", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "JO", - ThreeLetterIsoCode = "JOR", - NumericIsoCode = 400, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Kazakhstan", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "KZ", - ThreeLetterIsoCode = "KAZ", - NumericIsoCode = 398, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Korea, Democratic People's Republic of", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "KP", - ThreeLetterIsoCode = "PRK", - NumericIsoCode = 408, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Kuwait", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "KW", - ThreeLetterIsoCode = "KWT", - NumericIsoCode = 414, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Malaysia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MY", - ThreeLetterIsoCode = "MYS", - NumericIsoCode = 458, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Mexico", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MX", - ThreeLetterIsoCode = "MEX", - NumericIsoCode = 484, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Netherlands", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "NL", - ThreeLetterIsoCode = "NLD", - NumericIsoCode = 528, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "New Zealand", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "NZ", - ThreeLetterIsoCode = "NZL", - NumericIsoCode = 554, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Norway", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "NO", - ThreeLetterIsoCode = "NOR", - NumericIsoCode = 578, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Pakistan", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "PK", - ThreeLetterIsoCode = "PAK", - NumericIsoCode = 586, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Paraguay", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "PY", - ThreeLetterIsoCode = "PRY", - NumericIsoCode = 600, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Peru", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "PE", - ThreeLetterIsoCode = "PER", - NumericIsoCode = 604, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Philippines", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "PH", - ThreeLetterIsoCode = "PHL", - NumericIsoCode = 608, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Poland", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "PL", - ThreeLetterIsoCode = "POL", - NumericIsoCode = 616, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Portugal", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "PT", - ThreeLetterIsoCode = "PRT", - NumericIsoCode = 620, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Puerto Rico", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "PR", - ThreeLetterIsoCode = "PRI", - NumericIsoCode = 630, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Qatar", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "QA", - ThreeLetterIsoCode = "QAT", - NumericIsoCode = 634, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Romania", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "RO", - ThreeLetterIsoCode = "ROM", - NumericIsoCode = 642, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Russia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "RU", - ThreeLetterIsoCode = "RUS", - NumericIsoCode = 643, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Saudi Arabia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SA", - ThreeLetterIsoCode = "SAU", - NumericIsoCode = 682, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Singapore", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SG", - ThreeLetterIsoCode = "SGP", - NumericIsoCode = 702, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Slovakia (Slovak Republic)", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SK", - ThreeLetterIsoCode = "SVK", - NumericIsoCode = 703, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Slovenia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SI", - ThreeLetterIsoCode = "SVN", - NumericIsoCode = 705, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "South Africa", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "ZA", - ThreeLetterIsoCode = "ZAF", - NumericIsoCode = 710, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Spain", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "ES", - ThreeLetterIsoCode = "ESP", - NumericIsoCode = 724, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Sweden", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SE", - ThreeLetterIsoCode = "SWE", - NumericIsoCode = 752, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Taiwan", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TW", - ThreeLetterIsoCode = "TWN", - NumericIsoCode = 158, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Thailand", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TH", - ThreeLetterIsoCode = "THA", - NumericIsoCode = 764, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Turkey", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TR", - ThreeLetterIsoCode = "TUR", - NumericIsoCode = 792, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Ukraine", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "UA", - ThreeLetterIsoCode = "UKR", - NumericIsoCode = 804, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "United Arab Emirates", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AE", - ThreeLetterIsoCode = "ARE", - NumericIsoCode = 784, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "United Kingdom", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GB", - ThreeLetterIsoCode = "GBR", - NumericIsoCode = 826, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "United States minor outlying islands", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "UM", - ThreeLetterIsoCode = "UMI", - NumericIsoCode = 581, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Uruguay", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "UY", - ThreeLetterIsoCode = "URY", - NumericIsoCode = 858, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Uzbekistan", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "UZ", - ThreeLetterIsoCode = "UZB", - NumericIsoCode = 860, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Venezuela", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "VE", - ThreeLetterIsoCode = "VEN", - NumericIsoCode = 862, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Serbia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "RS", - ThreeLetterIsoCode = "SRB", - NumericIsoCode = 688, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Afghanistan", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AF", - ThreeLetterIsoCode = "AFG", - NumericIsoCode = 4, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Albania", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AL", - ThreeLetterIsoCode = "ALB", - NumericIsoCode = 8, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Algeria", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "DZ", - ThreeLetterIsoCode = "DZA", - NumericIsoCode = 12, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "American Samoa", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AS", - ThreeLetterIsoCode = "ASM", - NumericIsoCode = 16, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Andorra", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AD", - ThreeLetterIsoCode = "AND", - NumericIsoCode = 20, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Angola", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AO", - ThreeLetterIsoCode = "AGO", - NumericIsoCode = 24, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Anguilla", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AI", - ThreeLetterIsoCode = "AIA", - NumericIsoCode = 660, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Antarctica", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AQ", - ThreeLetterIsoCode = "ATA", - NumericIsoCode = 10, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Antigua and Barbuda", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AG", - ThreeLetterIsoCode = "ATG", - NumericIsoCode = 28, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Bahrain", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BH", - ThreeLetterIsoCode = "BHR", - NumericIsoCode = 48, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Barbados", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BB", - ThreeLetterIsoCode = "BRB", - NumericIsoCode = 52, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Benin", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BJ", - ThreeLetterIsoCode = "BEN", - NumericIsoCode = 204, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Bhutan", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BT", - ThreeLetterIsoCode = "BTN", - NumericIsoCode = 64, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Botswana", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BW", - ThreeLetterIsoCode = "BWA", - NumericIsoCode = 72, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Bouvet Island", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BV", - ThreeLetterIsoCode = "BVT", - NumericIsoCode = 74, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "British Indian Ocean Territory", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "IO", - ThreeLetterIsoCode = "IOT", - NumericIsoCode = 86, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Brunei Darussalam", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BN", - ThreeLetterIsoCode = "BRN", - NumericIsoCode = 96, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Burkina Faso", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BF", - ThreeLetterIsoCode = "BFA", - NumericIsoCode = 854, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Burundi", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "BI", - ThreeLetterIsoCode = "BDI", - NumericIsoCode = 108, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Cambodia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "KH", - ThreeLetterIsoCode = "KHM", - NumericIsoCode = 116, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Cameroon", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CM", - ThreeLetterIsoCode = "CMR", - NumericIsoCode = 120, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Cape Verde", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CV", - ThreeLetterIsoCode = "CPV", - NumericIsoCode = 132, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Central African Republic", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CF", - ThreeLetterIsoCode = "CAF", - NumericIsoCode = 140, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Chad", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TD", - ThreeLetterIsoCode = "TCD", - NumericIsoCode = 148, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Christmas Island", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CX", - ThreeLetterIsoCode = "CXR", - NumericIsoCode = 162, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Cocos (Keeling) Islands", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CC", - ThreeLetterIsoCode = "CCK", - NumericIsoCode = 166, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Comoros", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "KM", - ThreeLetterIsoCode = "COM", - NumericIsoCode = 174, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Congo", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CG", - ThreeLetterIsoCode = "COG", - NumericIsoCode = 178, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Cook Islands", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CK", - ThreeLetterIsoCode = "COK", - NumericIsoCode = 184, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Cote D'Ivoire", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "CI", - ThreeLetterIsoCode = "CIV", - NumericIsoCode = 384, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Djibouti", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "DJ", - ThreeLetterIsoCode = "DJI", - NumericIsoCode = 262, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Dominica", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "DM", - ThreeLetterIsoCode = "DMA", - NumericIsoCode = 212, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "El Salvador", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SV", - ThreeLetterIsoCode = "SLV", - NumericIsoCode = 222, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Equatorial Guinea", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GQ", - ThreeLetterIsoCode = "GNQ", - NumericIsoCode = 226, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Eritrea", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "ER", - ThreeLetterIsoCode = "ERI", - NumericIsoCode = 232, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Estonia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "EE", - ThreeLetterIsoCode = "EST", - NumericIsoCode = 233, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Ethiopia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "ET", - ThreeLetterIsoCode = "ETH", - NumericIsoCode = 231, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Falkland Islands (Malvinas)", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "FK", - ThreeLetterIsoCode = "FLK", - NumericIsoCode = 238, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Faroe Islands", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "FO", - ThreeLetterIsoCode = "FRO", - NumericIsoCode = 234, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Fiji", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "FJ", - ThreeLetterIsoCode = "FJI", - NumericIsoCode = 242, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "French Guiana", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GF", - ThreeLetterIsoCode = "GUF", - NumericIsoCode = 254, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "French Polynesia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "PF", - ThreeLetterIsoCode = "PYF", - NumericIsoCode = 258, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "French Southern Territories", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TF", - ThreeLetterIsoCode = "ATF", - NumericIsoCode = 260, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Gabon", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GA", - ThreeLetterIsoCode = "GAB", - NumericIsoCode = 266, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Gambia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GM", - ThreeLetterIsoCode = "GMB", - NumericIsoCode = 270, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Ghana", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GH", - ThreeLetterIsoCode = "GHA", - NumericIsoCode = 288, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Greenland", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GL", - ThreeLetterIsoCode = "GRL", - NumericIsoCode = 304, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Grenada", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GD", - ThreeLetterIsoCode = "GRD", - NumericIsoCode = 308, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Guadeloupe", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GP", - ThreeLetterIsoCode = "GLP", - NumericIsoCode = 312, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Guam", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GU", - ThreeLetterIsoCode = "GUM", - NumericIsoCode = 316, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Guinea", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GN", - ThreeLetterIsoCode = "GIN", - NumericIsoCode = 324, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Guinea-bissau", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GW", - ThreeLetterIsoCode = "GNB", - NumericIsoCode = 624, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Guyana", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GY", - ThreeLetterIsoCode = "GUY", - NumericIsoCode = 328, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Haiti", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "HT", - ThreeLetterIsoCode = "HTI", - NumericIsoCode = 332, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Heard and Mc Donald Islands", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "HM", - ThreeLetterIsoCode = "HMD", - NumericIsoCode = 334, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Honduras", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "HN", - ThreeLetterIsoCode = "HND", - NumericIsoCode = 340, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Iceland", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "IS", - ThreeLetterIsoCode = "ISL", - NumericIsoCode = 352, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Iran (Islamic Republic of)", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "IR", - ThreeLetterIsoCode = "IRN", - NumericIsoCode = 364, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Iraq", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "IQ", - ThreeLetterIsoCode = "IRQ", - NumericIsoCode = 368, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Kenya", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "KE", - ThreeLetterIsoCode = "KEN", - NumericIsoCode = 404, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Kiribati", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "KI", - ThreeLetterIsoCode = "KIR", - NumericIsoCode = 296, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Korea", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "KR", - ThreeLetterIsoCode = "KOR", - NumericIsoCode = 410, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Kyrgyzstan", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "KG", - ThreeLetterIsoCode = "KGZ", - NumericIsoCode = 417, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Lao People's Democratic Republic", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "LA", - ThreeLetterIsoCode = "LAO", - NumericIsoCode = 418, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Latvia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "LV", - ThreeLetterIsoCode = "LVA", - NumericIsoCode = 428, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Lebanon", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "LB", - ThreeLetterIsoCode = "LBN", - NumericIsoCode = 422, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Lesotho", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "LS", - ThreeLetterIsoCode = "LSO", - NumericIsoCode = 426, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Liberia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "LR", - ThreeLetterIsoCode = "LBR", - NumericIsoCode = 430, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Libyan Arab Jamahiriya", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "LY", - ThreeLetterIsoCode = "LBY", - NumericIsoCode = 434, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Liechtenstein", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "LI", - ThreeLetterIsoCode = "LIE", - NumericIsoCode = 438, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Lithuania", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "LT", - ThreeLetterIsoCode = "LTU", - NumericIsoCode = 440, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Luxembourg", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "LU", - ThreeLetterIsoCode = "LUX", - NumericIsoCode = 442, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Macau", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MO", - ThreeLetterIsoCode = "MAC", - NumericIsoCode = 446, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Macedonia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MK", - ThreeLetterIsoCode = "MKD", - NumericIsoCode = 807, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Madagascar", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MG", - ThreeLetterIsoCode = "MDG", - NumericIsoCode = 450, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Malawi", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MW", - ThreeLetterIsoCode = "MWI", - NumericIsoCode = 454, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Maldives", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MV", - ThreeLetterIsoCode = "MDV", - NumericIsoCode = 462, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Mali", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "ML", - ThreeLetterIsoCode = "MLI", - NumericIsoCode = 466, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Malta", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MT", - ThreeLetterIsoCode = "MLT", - NumericIsoCode = 470, - SubjectToVat = true, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Marshall Islands", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MH", - ThreeLetterIsoCode = "MHL", - NumericIsoCode = 584, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Martinique", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MQ", - ThreeLetterIsoCode = "MTQ", - NumericIsoCode = 474, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Mauritania", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MR", - ThreeLetterIsoCode = "MRT", - NumericIsoCode = 478, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Mauritius", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MU", - ThreeLetterIsoCode = "MUS", - NumericIsoCode = 480, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Mayotte", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "YT", - ThreeLetterIsoCode = "MYT", - NumericIsoCode = 175, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Micronesia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "FM", - ThreeLetterIsoCode = "FSM", - NumericIsoCode = 583, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Moldova", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MD", - ThreeLetterIsoCode = "MDA", - NumericIsoCode = 498, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Monaco", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MC", - ThreeLetterIsoCode = "MCO", - NumericIsoCode = 492, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Mongolia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MN", - ThreeLetterIsoCode = "MNG", - NumericIsoCode = 496, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Montenegro", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "ME", - ThreeLetterIsoCode = "MNE", - NumericIsoCode = 499, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Montserrat", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MS", - ThreeLetterIsoCode = "MSR", - NumericIsoCode = 500, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Morocco", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MA", - ThreeLetterIsoCode = "MAR", - NumericIsoCode = 504, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Mozambique", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MZ", - ThreeLetterIsoCode = "MOZ", - NumericIsoCode = 508, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Myanmar", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MM", - ThreeLetterIsoCode = "MMR", - NumericIsoCode = 104, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Namibia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "NA", - ThreeLetterIsoCode = "NAM", - NumericIsoCode = 516, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Nauru", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "NR", - ThreeLetterIsoCode = "NRU", - NumericIsoCode = 520, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Nepal", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "NP", - ThreeLetterIsoCode = "NPL", - NumericIsoCode = 524, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Netherlands Antilles", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "AN", - ThreeLetterIsoCode = "ANT", - NumericIsoCode = 530, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "New Caledonia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "NC", - ThreeLetterIsoCode = "NCL", - NumericIsoCode = 540, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Nicaragua", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "NI", - ThreeLetterIsoCode = "NIC", - NumericIsoCode = 558, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Niger", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "NE", - ThreeLetterIsoCode = "NER", - NumericIsoCode = 562, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Nigeria", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "NG", - ThreeLetterIsoCode = "NGA", - NumericIsoCode = 566, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Niue", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "NU", - ThreeLetterIsoCode = "NIU", - NumericIsoCode = 570, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Norfolk Island", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "NF", - ThreeLetterIsoCode = "NFK", - NumericIsoCode = 574, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Northern Mariana Islands", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "MP", - ThreeLetterIsoCode = "MNP", - NumericIsoCode = 580, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Oman", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "OM", - ThreeLetterIsoCode = "OMN", - NumericIsoCode = 512, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Palau", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "PW", - ThreeLetterIsoCode = "PLW", - NumericIsoCode = 585, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Panama", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "PA", - ThreeLetterIsoCode = "PAN", - NumericIsoCode = 591, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Papua New Guinea", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "PG", - ThreeLetterIsoCode = "PNG", - NumericIsoCode = 598, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Pitcairn", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "PN", - ThreeLetterIsoCode = "PCN", - NumericIsoCode = 612, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Reunion", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "RE", - ThreeLetterIsoCode = "REU", - NumericIsoCode = 638, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Rwanda", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "RW", - ThreeLetterIsoCode = "RWA", - NumericIsoCode = 646, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Saint Kitts and Nevis", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "KN", - ThreeLetterIsoCode = "KNA", - NumericIsoCode = 659, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Saint Lucia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "LC", - ThreeLetterIsoCode = "LCA", - NumericIsoCode = 662, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Saint Vincent and the Grenadines", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "VC", - ThreeLetterIsoCode = "VCT", - NumericIsoCode = 670, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Samoa", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "WS", - ThreeLetterIsoCode = "WSM", - NumericIsoCode = 882, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "San Marino", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SM", - ThreeLetterIsoCode = "SMR", - NumericIsoCode = 674, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Sao Tome and Principe", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "ST", - ThreeLetterIsoCode = "STP", - NumericIsoCode = 678, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Senegal", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SN", - ThreeLetterIsoCode = "SEN", - NumericIsoCode = 686, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Seychelles", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SC", - ThreeLetterIsoCode = "SYC", - NumericIsoCode = 690, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Sierra Leone", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SL", - ThreeLetterIsoCode = "SLE", - NumericIsoCode = 694, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Solomon Islands", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SB", - ThreeLetterIsoCode = "SLB", - NumericIsoCode = 90, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Somalia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SO", - ThreeLetterIsoCode = "SOM", - NumericIsoCode = 706, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "South Georgia & South Sandwich Islands", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "GS", - ThreeLetterIsoCode = "SGS", - NumericIsoCode = 239, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Sri Lanka", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "LK", - ThreeLetterIsoCode = "LKA", - NumericIsoCode = 144, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "St. Helena", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SH", - ThreeLetterIsoCode = "SHN", - NumericIsoCode = 654, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "St. Pierre and Miquelon", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "PM", - ThreeLetterIsoCode = "SPM", - NumericIsoCode = 666, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Sudan", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SD", - ThreeLetterIsoCode = "SDN", - NumericIsoCode = 736, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Suriname", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SR", - ThreeLetterIsoCode = "SUR", - NumericIsoCode = 740, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Svalbard and Jan Mayen Islands", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SJ", - ThreeLetterIsoCode = "SJM", - NumericIsoCode = 744, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Swaziland", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SZ", - ThreeLetterIsoCode = "SWZ", - NumericIsoCode = 748, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Syrian Arab Republic", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "SY", - ThreeLetterIsoCode = "SYR", - NumericIsoCode = 760, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Tajikistan", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TJ", - ThreeLetterIsoCode = "TJK", - NumericIsoCode = 762, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Tanzania", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TZ", - ThreeLetterIsoCode = "TZA", - NumericIsoCode = 834, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Togo", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TG", - ThreeLetterIsoCode = "TGO", - NumericIsoCode = 768, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Tokelau", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TK", - ThreeLetterIsoCode = "TKL", - NumericIsoCode = 772, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Tonga", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TO", - ThreeLetterIsoCode = "TON", - NumericIsoCode = 776, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Trinidad and Tobago", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TT", - ThreeLetterIsoCode = "TTO", - NumericIsoCode = 780, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Tunisia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TN", - ThreeLetterIsoCode = "TUN", - NumericIsoCode = 788, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Turkmenistan", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TM", - ThreeLetterIsoCode = "TKM", - NumericIsoCode = 795, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Turks and Caicos Islands", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TC", - ThreeLetterIsoCode = "TCA", - NumericIsoCode = 796, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Tuvalu", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "TV", - ThreeLetterIsoCode = "TUV", - NumericIsoCode = 798, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Uganda", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "UG", - ThreeLetterIsoCode = "UGA", - NumericIsoCode = 800, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Vanuatu", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "VU", - ThreeLetterIsoCode = "VUT", - NumericIsoCode = 548, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Vatican City State (Holy See)", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "VA", - ThreeLetterIsoCode = "VAT", - NumericIsoCode = 336, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Viet Nam", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "VN", - ThreeLetterIsoCode = "VNM", - NumericIsoCode = 704, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Virgin Islands (British)", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "VG", - ThreeLetterIsoCode = "VGB", - NumericIsoCode = 92, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Virgin Islands (U.S.)", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "VI", - ThreeLetterIsoCode = "VIR", - NumericIsoCode = 850, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Wallis and Futuna Islands", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "WF", - ThreeLetterIsoCode = "WLF", - NumericIsoCode = 876, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Western Sahara", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "EH", - ThreeLetterIsoCode = "ESH", - NumericIsoCode = 732, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Yemen", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "YE", - ThreeLetterIsoCode = "YEM", - NumericIsoCode = 887, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Zambia", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "ZM", - ThreeLetterIsoCode = "ZMB", - NumericIsoCode = 894, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - new Country - { - Name = "Zimbabwe", - AllowsBilling = true, - AllowsShipping = true, - TwoLetterIsoCode = "ZW", - ThreeLetterIsoCode = "ZWE", - NumericIsoCode = 716, - SubjectToVat = false, - DisplayOrder = 100, - Published = true - }, - }; - this.Alter(entities); - return entities; - } - - public IList ShippingMethods() - { - var entities = new List() - { - new ShippingMethod - { - Name = "In-Store Pickup", - Description ="Pick up your items at the store", - DisplayOrder = 0 - }, - new ShippingMethod - { - Name = "By Ground", - Description ="Compared to other shipping methods, like by flight or over seas, ground shipping is carried out closer to the earth", - DisplayOrder = 1 - }, - }; - this.Alter(entities); - return entities; - } - - public IList CustomerRoles() - { - var crAdministrators = new CustomerRole - { - Name = "Administrators", - Active = true, - IsSystemRole = true, - SystemName = SystemCustomerRoleNames.Administrators, - }; - var crForumModerators = new CustomerRole - { - Name = "Forum Moderators", - Active = true, - IsSystemRole = true, - SystemName = SystemCustomerRoleNames.ForumModerators, - }; - var crRegistered = new CustomerRole - { - Name = "Registered", - Active = true, - IsSystemRole = true, - SystemName = SystemCustomerRoleNames.Registered, - }; - var crGuests = new CustomerRole - { - Name = "Guests", - Active = true, - IsSystemRole = true, - SystemName = SystemCustomerRoleNames.Guests, - }; - var entities = new List - { - crAdministrators, - crForumModerators, - crRegistered, - crGuests - }; - this.Alter(entities); - return entities; - } - - public Address AdminAddress() - { - var cCountry = _ctx.Set() - .Where(x => x.ThreeLetterIsoCode == "USA") - .FirstOrDefault(); - - var entity = new Address() - { - FirstName = "John", - LastName = "Smith", - PhoneNumber = "12345678", - Email = "admin@myshop.com", - FaxNumber = "", - Company = "John Smith LLC", - Address1 = "1234 Main Road", - Address2 = "", - City = "New York", - StateProvince = cCountry.StateProvinces.FirstOrDefault(), - Country = cCountry, - ZipPostalCode = "12212", - CreatedOnUtc = DateTime.UtcNow, - }; - this.Alter(entity); - return entity; - } - - public Customer SearchEngineUser() - { - var entity = new Customer - { - Email = "builtin@search-engine-record.com", - CustomerGuid = Guid.NewGuid(), - PasswordFormat = PasswordFormat.Clear, - AdminComment = "Built-in system guest record used for requests from search engines.", - Active = true, - IsSystemAccount = true, - SystemName = SystemCustomerNames.SearchEngine, - CreatedOnUtc = DateTime.UtcNow, - LastActivityDateUtc = DateTime.UtcNow, - }; - - this.Alter(entity); - return entity; - } - - public Customer BackgroundTaskUser() - { - var entity = new Customer - { - Email = "builtin@background-task-record.com", - CustomerGuid = Guid.NewGuid(), - PasswordFormat = PasswordFormat.Clear, - AdminComment = "Built-in system record used for background tasks.", - Active = true, - IsSystemAccount = true, - SystemName = SystemCustomerNames.BackgroundTask, - CreatedOnUtc = DateTime.UtcNow, - LastActivityDateUtc = DateTime.UtcNow, - }; - - this.Alter(entity); - return entity; - } - - public Customer PdfConverterUser() - { - var entity = new Customer - { - Email = "builtin@pdf-converter-record.com", - CustomerGuid = Guid.NewGuid(), - PasswordFormat = PasswordFormat.Clear, - AdminComment = "Built-in system record used for the PDF converter.", - Active = true, - IsSystemAccount = true, - SystemName = SystemCustomerNames.PdfConverter, - CreatedOnUtc = DateTime.UtcNow, - LastActivityDateUtc = DateTime.UtcNow, - }; - - this.Alter(entity); - return entity; - } - - public IList EmailAccounts() - { - var entities = new List() - { - new EmailAccount - { - Email = "test@mail.com", - DisplayName = "Store name", - Host = "smtp.mail.com", - Port = 25, - Username = "123", - Password = "123", - EnableSsl = false, - UseDefaultCredentials = false - } - }; - this.Alter(entities); - return entities; - } - - public IList Topics() - { - var entities = new List() - { - new Topic - { - SystemName = "AboutUs", - IncludeInSitemap = false, - IsPasswordProtected = false, - Title = "About Us", - Body = "

Put your "About Us" information here. You can edit this in the admin site.

" - }, - new Topic - { - SystemName = "CheckoutAsGuestOrRegister", - IncludeInSitemap = false, - IsPasswordProtected = false, - RenderAsWidget = true, - WidgetWrapContent = false, - Title = "", - Body = "

Register and save time!
Register with us for future convenience:

  • Fast and easy check out
  • Easy access to your order history and status
" - }, - new Topic - { - SystemName = "ConditionsOfUse", - IncludeInSitemap = false, - IsPasswordProtected = false, - Title = "Conditions of use", - Body = "

Put your conditions of use information here. You can edit this in the admin site.

" - }, - new Topic - { - SystemName = "ContactUs", - IncludeInSitemap = false, - IsPasswordProtected = false, - RenderAsWidget = true, - WidgetWrapContent = false, - Title = "Contact us", - Body = "

Put your contact information here. You can edit this in the admin site.

" - }, - new Topic - { - SystemName = "ForumWelcomeMessage", - IncludeInSitemap = false, - IsPasswordProtected = false, - RenderAsWidget = true, - WidgetWrapContent = false, - Title = "Forums", - Body = "

Put your welcome message here. You can edit this in the admin site.

" - }, - new Topic - { - SystemName = "HomePageText", - IncludeInSitemap = false, - IsPasswordProtected = false, - RenderAsWidget = true, - WidgetWrapContent = false, - Title = "Welcome to our store", - Body = "

Online shopping is the process consumers go through to purchase products or services over the Internet. You can edit this in the admin site.

" - }, - new Topic - { - SystemName = "LoginRegistrationInfo", - IncludeInSitemap = false, - IsPasswordProtected = false, - RenderAsWidget = true, - WidgetWrapContent = false, - Title = "About login / registration", - Body = "

Not registered yet?

Create your own account now and experience our diversity. With an account you can place orders faster and will always have a perfect overview of your current and previous orders.

" - }, - new Topic - { - SystemName = "PrivacyInfo", - IncludeInSitemap = false, - IsPasswordProtected = false, - ShortTitle = "Privacy", - Title = "Privacy policy", - Body = "

" - }, - new Topic - { - SystemName = "ShippingInfo", - IncludeInSitemap = false, - IsPasswordProtected = false, - Title = "Shipping & Returns", - Body = "

Put your shipping & returns information here. You can edit this in the admin site.

" - }, - - new Topic - { - SystemName = "Imprint", - IncludeInSitemap = false, - IsPasswordProtected = false, - Title = "Imprint", - Body = @"

Put your imprint information here. YOu can edit this in the admin site.

" - }, - new Topic - { - SystemName = "Disclaimer", - IncludeInSitemap = false, - IsPasswordProtected = false, - Title = "Disclaimer", - Body = "

Put your disclaimer information here. You can edit this in the admin site.

" - }, - new Topic - { - SystemName = "PaymentInfo", - IncludeInSitemap = false, - IsPasswordProtected = false, - Title = "Payment info", - Body = "

Put your payment information here. You can edit this in the admin site.

" - }, - }; - this.Alter(entities); - return entities; - } - - public IList Settings() - { - var defaultDimensionId = _ctx.Set().FirstOrDefault(x => x.SystemKeyword == "inch")?.Id ?? 0; - var defaultWeightId = _ctx.Set().FirstOrDefault(x => x.SystemKeyword == "lb")?.Id ?? 0; - var defaultLanguageId = _ctx.Set().FirstOrDefault()?.Id ?? 0; - var defaultEmailAccountId = _ctx.Set().FirstOrDefault()?.Id ?? 0; + public abstract partial class InvariantSeedData + { + private SmartObjectContext _ctx; + private string _sampleImagesPath; - var entities = new List - { - new PdfSettings - { - }, - new CommonSettings - { - }, - new SeoSettings - { - }, - new SocialSettings - { - }, - new AdminAreaSettings - { - }, - new CatalogSettings - { - }, - new LocalizationSettings - { - DefaultAdminLanguageId = defaultLanguageId - }, - new CustomerSettings - { - }, - new AddressSettings - { - }, - new MediaSettings - { - }, - new StoreInformationSettings - { - }, - new RewardPointsSettings - { - }, - new CurrencySettings - { - }, - new MeasureSettings - { - BaseDimensionId = defaultDimensionId, - BaseWeightId = defaultWeightId, - }, - new ShoppingCartSettings - { - }, - new OrderSettings - { - }, - new SecuritySettings - { - }, - new ShippingSettings - { - }, - new PaymentSettings - { - ActivePaymentMethodSystemNames = new List - { - "Payments.CashOnDelivery", - "Payments.Manual", - "Payments.PayInStore", - "Payments.Prepayment" - } - }, - new TaxSettings - { - }, - new BlogSettings - { - }, - new NewsSettings - { - }, - new ForumSettings - { - }, - new EmailAccountSettings - { - DefaultEmailAccountId = defaultEmailAccountId - }, - new ThemeSettings - { - } - }; - - this.Alter(entities); - return entities; - } - - public IList ActivityLogTypes() - { - var entities = new List() - { - //admin area activities - new ActivityLogType - { - SystemKeyword = "AddNewCategory", - Enabled = true, - Name = "Add a new category" - }, - new ActivityLogType - { - SystemKeyword = "AddNewCheckoutAttribute", - Enabled = true, - Name = "Add a new checkout attribute" - }, - new ActivityLogType - { - SystemKeyword = "AddNewCustomer", - Enabled = true, - Name = "Add a new customer" - }, - new ActivityLogType - { - SystemKeyword = "AddNewCustomerRole", - Enabled = true, - Name = "Add a new customer role" - }, - new ActivityLogType - { - SystemKeyword = "AddNewDiscount", - Enabled = true, - Name = "Add a new discount" - }, - new ActivityLogType - { - SystemKeyword = "AddNewGiftCard", - Enabled = true, - Name = "Add a new gift card" - }, - new ActivityLogType - { - SystemKeyword = "AddNewManufacturer", - Enabled = true, - Name = "Add a new manufacturer" - }, - new ActivityLogType - { - SystemKeyword = "AddNewProduct", - Enabled = true, - Name = "Add a new product" - }, - new ActivityLogType - { - SystemKeyword = "AddNewProductAttribute", - Enabled = true, - Name = "Add a new product attribute" - }, - new ActivityLogType - { - SystemKeyword = "AddNewSetting", - Enabled = true, - Name = "Add a new setting" - }, - new ActivityLogType - { - SystemKeyword = "AddNewSpecAttribute", - Enabled = true, - Name = "Add a new specification attribute" - }, - new ActivityLogType - { - SystemKeyword = "AddNewWidget", - Enabled = true, - Name = "Add a new widget" - }, - new ActivityLogType - { - SystemKeyword = "DeleteCategory", - Enabled = true, - Name = "Delete category" - }, - new ActivityLogType - { - SystemKeyword = "DeleteCheckoutAttribute", - Enabled = true, - Name = "Delete a checkout attribute" - }, - new ActivityLogType - { - SystemKeyword = "DeleteCustomer", - Enabled = true, - Name = "Delete a customer" - }, - new ActivityLogType - { - SystemKeyword = "DeleteCustomerRole", - Enabled = true, - Name = "Delete a customer role" - }, - new ActivityLogType - { - SystemKeyword = "DeleteDiscount", - Enabled = true, - Name = "Delete a discount" - }, - new ActivityLogType - { - SystemKeyword = "DeleteGiftCard", - Enabled = true, - Name = "Delete a gift card" - }, - new ActivityLogType - { - SystemKeyword = "DeleteManufacturer", - Enabled = true, - Name = "Delete a manufacturer" - }, - new ActivityLogType - { - SystemKeyword = "DeleteProduct", - Enabled = true, - Name = "Delete a product" - }, - new ActivityLogType - { - SystemKeyword = "DeleteProductAttribute", - Enabled = true, - Name = "Delete a product attribute" - }, - new ActivityLogType - { - SystemKeyword = "DeleteReturnRequest", - Enabled = true, - Name = "Delete a return request" - }, - new ActivityLogType - { - SystemKeyword = "DeleteSetting", - Enabled = true, - Name = "Delete a setting" - }, - new ActivityLogType - { - SystemKeyword = "DeleteSpecAttribute", - Enabled = true, - Name = "Delete a specification attribute" - }, - new ActivityLogType - { - SystemKeyword = "DeleteWidget", - Enabled = true, - Name = "Delete a widget" - }, - new ActivityLogType - { - SystemKeyword = "EditCategory", - Enabled = true, - Name = "Edit category" - }, - new ActivityLogType - { - SystemKeyword = "EditCheckoutAttribute", - Enabled = true, - Name = "Edit a checkout attribute" - }, - new ActivityLogType - { - SystemKeyword = "EditCustomer", - Enabled = true, - Name = "Edit a customer" - }, - new ActivityLogType - { - SystemKeyword = "EditCustomerRole", - Enabled = true, - Name = "Edit a customer role" - }, - new ActivityLogType - { - SystemKeyword = "EditDiscount", - Enabled = true, - Name = "Edit a discount" - }, - new ActivityLogType - { - SystemKeyword = "EditGiftCard", - Enabled = true, - Name = "Edit a gift card" - }, - new ActivityLogType - { - SystemKeyword = "EditManufacturer", - Enabled = true, - Name = "Edit a manufacturer" - }, - new ActivityLogType - { - SystemKeyword = "EditProduct", - Enabled = true, - Name = "Edit a product" - }, - new ActivityLogType - { - SystemKeyword = "EditProductAttribute", - Enabled = true, - Name = "Edit a product attribute" - }, - new ActivityLogType - { - SystemKeyword = "EditPromotionProviders", - Enabled = true, - Name = "Edit promotion providers" - }, - new ActivityLogType - { - SystemKeyword = "EditReturnRequest", - Enabled = true, - Name = "Edit a return request" - }, - new ActivityLogType - { - SystemKeyword = "EditSettings", - Enabled = true, - Name = "Edit setting(s)" - }, - new ActivityLogType - { - SystemKeyword = "EditSpecAttribute", - Enabled = true, - Name = "Edit a specification attribute" - }, - new ActivityLogType - { - SystemKeyword = "EditWidget", - Enabled = true, - Name = "Edit a widget" - }, - new ActivityLogType - { - SystemKeyword = "EditThemeVars", - Enabled = true, - Name = "Edit theme variables" - }, - new ActivityLogType - { - SystemKeyword = "ResetThemeVars", - Enabled = true, - Name = "Reset theme variables to defaults" - }, - new ActivityLogType - { - SystemKeyword = "ImportThemeVars", - Enabled = true, - Name = "Import theme variables" - }, - new ActivityLogType - { - SystemKeyword = "ExportThemeVars", - Enabled = true, - Name = "Export theme variables" - }, - - //public store activities - new ActivityLogType - { - SystemKeyword = "PublicStore.ViewCategory", - Enabled = false, - Name = "Public store. View a category" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.ViewManufacturer", - Enabled = false, - Name = "Public store. View a manufacturer" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.ViewProduct", - Enabled = false, - Name = "Public store. View a product" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.PlaceOrder", - Enabled = false, - Name = "Public store. Place an order" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.SendPM", - Enabled = false, - Name = "Public store. Send PM" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.ContactUs", - Enabled = false, - Name = "Public store. Use contact us form" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.AddToCompareList", - Enabled = false, - Name = "Public store. Add to compare list" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.AddToShoppingCart", - Enabled = false, - Name = "Public store. Add to shopping cart" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.AddToWishlist", - Enabled = false, - Name = "Public store. Add to wishlist" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.Login", - Enabled = false, - Name = "Public store. Login" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.Logout", - Enabled = false, - Name = "Public store. Logout" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.AddProductReview", - Enabled = false, - Name = "Public store. Add product review" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.AddNewsComment", - Enabled = false, - Name = "Public store. Add news comment" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.AddBlogComment", - Enabled = false, - Name = "Public store. Add blog comment" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.AddForumTopic", - Enabled = false, - Name = "Public store. Add forum topic" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.EditForumTopic", - Enabled = false, - Name = "Public store. Edit forum topic" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.DeleteForumTopic", - Enabled = false, - Name = "Public store. Delete forum topic" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.AddForumPost", - Enabled = false, - Name = "Public store. Add forum post" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.EditForumPost", - Enabled = false, - Name = "Public store. Edit forum post" - }, - new ActivityLogType - { - SystemKeyword = "PublicStore.DeleteForumPost", - Enabled = false, - Name = "Public store. Delete forum post" - }, - //new ActivityLogType - // { - // SystemKeyword = "EditThemeVars", - // Enabled = false, - // Name = "Edit theme variable" - // }, - //new ActivityLogType - // { - // SystemKeyword = "ResetThemeVars", - // Enabled = false, - // Name = "Reset theme variable" - // }, - }; - - this.Alter(entities); - return entities; - } - - public IList ProductTemplates() - { - var entities = new List - { - new ProductTemplate - { - Name = "Default Product Template", - ViewPath = "Product", - DisplayOrder = 10 - } - }; - this.Alter(entities); - return entities; - } - - public IList CategoryTemplates() - { - var entities = new List - { - new CategoryTemplate - { - Name = "Products in Grid or Lines", - ViewPath = "CategoryTemplate.ProductsInGridOrLines", - DisplayOrder = 1 - }, - }; - this.Alter(entities); - return entities; - } - - public IList ManufacturerTemplates() - { - var entities = new List - { - new ManufacturerTemplate - { - Name = "Products in Grid or Lines", - ViewPath = "ManufacturerTemplate.ProductsInGridOrLines", - DisplayOrder = 1 - }, - }; - this.Alter(entities); - return entities; - } - - public IList ScheduleTasks() - { - var entities = new List - { - new ScheduleTask - { - Name = "Send emails", - CronExpression = "* * * * *", // every Minute - Type = "SmartStore.Services.Messages.QueuedMessagesSendTask, SmartStore.Services", - Enabled = true, - StopOnError = false, - }, - new ScheduleTask - { - Name = "Delete guests", - CronExpression = "*/10 * * * *", // Every 10 minutes - Type = "SmartStore.Services.Customers.DeleteGuestsTask, SmartStore.Services", - Enabled = true, - StopOnError = false, - }, - new ScheduleTask - { - Name = "Delete logs", - CronExpression = "0 1 * * *", // At 01:00 - Type = "SmartStore.Services.Logging.DeleteLogsTask, SmartStore.Services", - Enabled = true, - StopOnError = false, - }, - new ScheduleTask - { - Name = "Clear cache", - CronExpression = "0 */12 * * *", // Every 12 hours - Type = "SmartStore.Services.Caching.ClearCacheTask, SmartStore.Services", - Enabled = false, - StopOnError = false, - }, - new ScheduleTask - { - Name = "Update currency exchange rates", - CronExpression = "0 */6 * * *", // Every 6 hours - Type = "SmartStore.Services.Directory.UpdateExchangeRateTask, SmartStore.Services", - Enabled = false, - StopOnError = false, - }, - new ScheduleTask - { - Name = "Clear transient uploads", - CronExpression = "30 1,13 * * *", // At 01:30 and 13:30 - Type = "SmartStore.Services.Media.TransientMediaClearTask, SmartStore.Services", - Enabled = true, - StopOnError = false, - }, - new ScheduleTask - { - Name = "Clear email queue", - CronExpression = "0 2 * * *", // At 02:00 - Type = "SmartStore.Services.Messages.QueuedMessagesClearTask, SmartStore.Services", - Enabled = true, - StopOnError = false, - }, - new ScheduleTask - { - Name = "Cleanup temporary files", - CronExpression = "30 3 * * *", // At 03:30 - Type = "SmartStore.Services.Common.TempFileCleanupTask, SmartStore.Services", - Enabled = true, - StopOnError = false - } - }; - this.Alter(entities); - return entities; - } - - #endregion - - #region Sample data creators - - public IList SpecificationAttributes() - { - #region sa1 CPU-Manufacturer - - var sa1 = new SpecificationAttribute - { - Name = "CPU-Manufacturer", - DisplayOrder = 1, - }; - sa1.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "AMD", - DisplayOrder = 1, - }); - sa1.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Intel", - DisplayOrder = 2, - }); - sa1.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "ARM", - DisplayOrder = 3, - }); - sa1.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Samsung", - DisplayOrder = 4, - }); - sa1.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Apple", - DisplayOrder = 5, - }); - - #endregion sa1 CPU-Manufacturer - - #region sa2 color - - var sa2 = new SpecificationAttribute - { - Name = "Color", - DisplayOrder = 2, - }; - sa2.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "white", - DisplayOrder = 1, - }); - sa2.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "black", - DisplayOrder = 2, - }); - sa2.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "beige", - DisplayOrder = 3, - }); - sa2.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "red", - DisplayOrder = 4, - }); - sa2.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "blue", - DisplayOrder = 5, - }); - sa2.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "green", - DisplayOrder = 6, - }); - sa2.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "yellow", - DisplayOrder = 7, - }); - - #endregion sa2 color - - #region sa3 harddisk capacity - - var sa3 = new SpecificationAttribute - { - Name = "Harddisk capacity", - DisplayOrder = 3, - }; - sa3.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "250 GB", - DisplayOrder = 1, - }); - sa3.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "500 GB", - DisplayOrder = 2, - }); - sa3.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "750 GB", - DisplayOrder = 3, - }); - sa3.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "1000 GB", - DisplayOrder = 4, - }); - sa3.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "1500 GB", - DisplayOrder = 5, - }); - - #endregion sa3 harddisk capacity - - #region sa4 ram - - var sa4 = new SpecificationAttribute - { - Name = "RAM", - DisplayOrder = 4, - }; - sa4.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "4 GB", - DisplayOrder = 1, - }); - sa4.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "8 GB", - DisplayOrder = 2, - }); - sa4.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "13 GB", - DisplayOrder = 3, - }); - sa4.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "2 GB", - DisplayOrder = 4, - }); - sa4.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "1500 GB", - DisplayOrder = 5, - }); - - #endregion sa4 ram - - #region sa5 Operating System - - var sa5 = new SpecificationAttribute - { - Name = "Operating System", - DisplayOrder = 5, - }; - sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Windows 7 32 Bit", - DisplayOrder = 1, - }); - sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Windows 7 64 Bit", - DisplayOrder = 2, - }); - sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Windows 8 32 Bit", - DisplayOrder = 3, - }); - sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Windows 8 64 Bit", - DisplayOrder = 4, - }); - sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Linux", - DisplayOrder = 5, - }); - sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Mac OS", - DisplayOrder = 6, - }); - sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Android 2", - DisplayOrder = 7, - }); - sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Android 4", - DisplayOrder = 8, - }); - sa5.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "iOS", - DisplayOrder = 9, - }); - - #endregion sa5 Operating System - - #region sa6 ports - - var sa6 = new SpecificationAttribute - { - Name = "Ports", - DisplayOrder = 6, - }; - sa6.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "USB 2.0", - DisplayOrder = 1, - }); - sa6.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "USB 3.0", - DisplayOrder = 2, - }); - sa6.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Firewire", - DisplayOrder = 3, - }); - sa6.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "HDMI", - DisplayOrder = 4, - }); - sa6.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "DVI", - DisplayOrder = 5, - }); - sa6.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "VGA", - DisplayOrder = 6, - }); - sa6.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Ethernet/RJ45", - DisplayOrder = 7, - }); - - #endregion sa6 ports - - #region sa7 Gender - - var sa7 = new SpecificationAttribute - { - Name = "Gender", - DisplayOrder = 7, - }; - sa7.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "gentlemen", - DisplayOrder = 1, - }); - sa7.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "ladies", - DisplayOrder = 2, - }); - sa7.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "unisex", - DisplayOrder = 3, - }); - - #endregion sa7 Gender - - #region sa8 material - - var sa8 = new SpecificationAttribute - { - Name = "Material", - DisplayOrder = 8, - }; - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "stainless steel", - DisplayOrder = 1, - }); - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "titanium", - DisplayOrder = 2, - }); - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "plastic", - DisplayOrder = 3, - }); - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "aluminium", - DisplayOrder = 4, - }); - - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "leather", - DisplayOrder = 5, - }); + protected InvariantSeedData() + { + } - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "nylon", - DisplayOrder = 6, - }); + public void Initialize(SmartObjectContext context) + { + _ctx = context; + _sampleImagesPath = CommonHelper.MapPath("~/App_Data/Samples/"); + } - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "silicone", - DisplayOrder = 7, - }); + #region Mandatory data creators - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + public IList Pictures() + { + var entities = new List { - Name = "ceramic", - DisplayOrder = 8, - }); + CreatePicture("company-logo.png"), + CreatePicture("product/allstar_charcoal.jpg"), + CreatePicture("product/allstar_maroon.jpg"), + CreatePicture("product/allstar_navy.jpg"), + CreatePicture("product/allstar_purple.jpg"), + CreatePicture("product/allstar_white.jpg"), + CreatePicture("product/wayfarer_havana.png"), + CreatePicture("product/wayfarer_havana_black.png"), + CreatePicture("product/wayfarer_rayban-black.png") + }; - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption - { - Name = "cotton", - DisplayOrder = 9, - }); - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption - { - Name = "100% organic cotton", - DisplayOrder = 10, - }); - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption - { - Name = "polyamide", - DisplayOrder = 11, - }); - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption - { - Name = "rubber", - DisplayOrder = 12, - }); - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption - { - Name = "wood", - DisplayOrder = 13, - }); - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption - { - Name = "glass", - DisplayOrder = 14, - }); - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption - { - Name = "spandex", - DisplayOrder = 15, - }); - sa8.SpecificationAttributeOptions.Add(new SpecificationAttributeOption - { - Name = "polyester", - DisplayOrder = 16, - }); - - #endregion sa8 material - - #region sa9 movement - - var sa9 = new SpecificationAttribute - { - Name = "Movement", - DisplayOrder = 9, - }; - sa9.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "mechanical, self winding", - DisplayOrder = 1, - }); - sa9.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "mechanical", - DisplayOrder = 2, - }); - sa9.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "quarz, battery operated", - DisplayOrder = 3, - }); - - #endregion sa9 movement - - #region sa10 clasp - - var sa10 = new SpecificationAttribute - { - Name = "Clasp", - DisplayOrder = 10, - }; - sa10.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "glidelock", - DisplayOrder = 1, - }); - sa10.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "folding clasp", - DisplayOrder = 2, - }); - sa10.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "thorn close", - DisplayOrder = 3, - }); - - #endregion sa10 clasp - - #region sa11 window material - - var sa11 = new SpecificationAttribute - { - Name = "Window material", - DisplayOrder = 11, - }; - sa11.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "mineral", - DisplayOrder = 1, - }); - sa11.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "sapphire", - DisplayOrder = 2, - }); - - #endregion sa11 window material - - #region sa12 language - - var sa12 = new SpecificationAttribute - { - Name = "Language", - DisplayOrder = 12, - }; - sa12.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "german", - DisplayOrder = 1, - }); - sa12.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "english", - DisplayOrder = 2, - }); - sa12.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "french", - DisplayOrder = 3, - }); - sa12.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "italian", - DisplayOrder = 4, - }); - - #endregion sa12 language - - #region sa13 edition - - var sa13 = new SpecificationAttribute - { - Name = "Edition", - DisplayOrder = 13, - }; - sa13.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "bound", - DisplayOrder = 1, - }); - sa13.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "paperback", - DisplayOrder = 2, - }); - - #endregion sa13 edition - - #region sa14 category - - var sa14 = new SpecificationAttribute - { - Name = "Category", - DisplayOrder = 14, - }; - sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "adventure", - DisplayOrder = 1, - }); - sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "fantasy & science fiction", - DisplayOrder = 2, - }); - sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "history", - DisplayOrder = 3, - }); - sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "internet & computer", - DisplayOrder = 4, - }); - sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "thriller", - DisplayOrder = 5, - }); - sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "cars", - DisplayOrder = 6, - }); - sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "novel", - DisplayOrder = 7, - }); - sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "cook and bake", - DisplayOrder = 8, - }); - sa14.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "non-fiction", - DisplayOrder = 9, - }); - - #endregion sa14 category - - #region sa15 Computer-type - - var sa15 = new SpecificationAttribute - { - Name = "Computer-type", - DisplayOrder = 15, - }; - sa15.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "desktop", - DisplayOrder = 1, - }); - sa15.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "all-in-one", - DisplayOrder = 2, - }); - sa15.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "laptop", - DisplayOrder = 3, - }); - sa15.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "tablet", - DisplayOrder = 4, - }); - - #endregion sa15 Computer-type - - #region sa16 type of mass-storage - - var sa16 = new SpecificationAttribute - { - Name = "Type of mass-storage", - DisplayOrder = 16, - }; - sa16.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "HDD", - DisplayOrder = 1, - }); - sa16.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "SSD", - DisplayOrder = 2, - }); - sa16.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Flash", - DisplayOrder = 3, - }); - - #endregion sa16 type of mass-storage - - #region sa17 Size (ext. HDD) - - var sa17 = new SpecificationAttribute - { - Name = "Size (ext. HDD)", - DisplayOrder = 17, - }; - sa17.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "3.5", - DisplayOrder = 1, - }); - sa17.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "2.5", - DisplayOrder = 2, - }); - - #endregion sa17 Size (ext. HDD) - - #region sa18 MP3 quality - - var sa18 = new SpecificationAttribute - { - Name = "MP3 quality", - DisplayOrder = 18, - }; - sa18.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "128 kbit/s", - DisplayOrder = 1, - }); - sa18.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "256 kbit/s", - DisplayOrder = 2, - }); - sa18.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "320 kbit/s", - DisplayOrder = 3, - }); - - #endregion sa18 MP3 quality - - #region sa19 music genre - - var sa19 = new SpecificationAttribute - { - Name = "Music genre", - DisplayOrder = 19, - }; - sa19.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "blues", - DisplayOrder = 1, - }); - sa19.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "jazz", - DisplayOrder = 2, - }); - sa19.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "disco", - DisplayOrder = 3, - }); - sa19.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "pop", - DisplayOrder = 4, - }); - sa19.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "funk", - DisplayOrder = 5, - }); - sa19.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "classical", - DisplayOrder = 6, - }); - sa19.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "R&B", - DisplayOrder = 7, - }); - - #endregion sa19 music genre - - #region sa20 manufacturer - - var sa20 = new SpecificationAttribute - { - Name = "Manufacturer", - DisplayOrder = 20, - }; - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Apple", - DisplayOrder = 1, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Samsung", - DisplayOrder = 2, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "HTC", - DisplayOrder = 3, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "LG", - DisplayOrder = 4, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Motorola", - DisplayOrder = 5, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Nokia", - DisplayOrder = 6, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Sony", - DisplayOrder = 7, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Blackberry", - DisplayOrder = 8, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Microsoft", - DisplayOrder = 9, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "HP", - DisplayOrder = 10, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Canon", - DisplayOrder = 11, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Casio", - DisplayOrder = 12, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Panasonic", - DisplayOrder = 13, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Certina", - DisplayOrder = 14, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Festina", - DisplayOrder = 15, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Seiko", - DisplayOrder = 16, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Tissot", - DisplayOrder = 17, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Breitling", - DisplayOrder = 18, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Adidas", - DisplayOrder = 19, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Nike", - DisplayOrder = 20, - }); - sa20.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Puma", - DisplayOrder = 21, - }); + this.Alter(entities); + return entities; + } - #endregion sa20 manufacturer + public IList Stores() + { + var imgCompanyLogo = _ctx.Set().Where(x => x.Name == "company-logo.png").FirstOrDefault(); - #region sa21 Watches for whom + var currency = _ctx.Set().FirstOrDefault(x => x.CurrencyCode == "EUR"); + if (currency == null) + currency = _ctx.Set().First(); - var sa21 = new SpecificationAttribute + var entities = new List() { - Name = "For whom", - DisplayOrder = 21, + new Store() + { + Name = "Your store name", + Url = "http://www.yourStore.com/", + Hosts = "yourstore.com,www.yourstore.com", + SslEnabled = false, + DisplayOrder = 1, + LogoMediaFileId = imgCompanyLogo.Id, + PrimaryStoreCurrencyId = currency.Id, + PrimaryExchangeRateCurrencyId = currency.Id + } }; - sa21.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "For him", - DisplayOrder = 1, - }); - sa21.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "For her", - DisplayOrder = 2, - }); - - #endregion sa11 Watches for whom - - #region sa22 Offer + this.Alter(entities); + return entities; + } - var sa22 = new SpecificationAttribute + public IList MeasureDimensions() + { + var entities = new List() { - Name = "Offer", - DisplayOrder = 22, + new MeasureDimension() + { + Name = "millimetre", + SystemKeyword = "mm", + Ratio = 25.4M, + DisplayOrder = 1, + }, + new MeasureDimension() + { + Name = "centimetre", + SystemKeyword = "cm", + Ratio = 0.254M, + DisplayOrder = 2, + }, + new MeasureDimension() + { + Name = "meter", + SystemKeyword = "m", + Ratio = 0.0254M, + DisplayOrder = 3, + }, + new MeasureDimension() + { + Name = "in", + SystemKeyword = "inch", + Ratio = 1M, + DisplayOrder = 4, + }, + new MeasureDimension() + { + Name = "feet", + SystemKeyword = "ft", + Ratio = 0.08333333M, + DisplayOrder = 5, + } }; - sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Clearance", - DisplayOrder = 1, - }); - - sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Permanent low price", - DisplayOrder = 2, - }); - - sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Promotion", - DisplayOrder = 3, - }); - - sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Reduced price", - DisplayOrder = 4, - }); - - sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Special Buy", - DisplayOrder = 5, - }); + this.Alter(entities); + return entities; + } - sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + public IList MeasureWeights() + { + var entities = new List() { - Name = "Offer of the day", - DisplayOrder = 6, - }); + new MeasureWeight() + { + Name = "ounce", // Ounce, Unze + SystemKeyword = "oz", + Ratio = 16M, + DisplayOrder = 5, + }, + new MeasureWeight() + { + Name = "lb", // Pound + SystemKeyword = "lb", + Ratio = 1M, + DisplayOrder = 6, + }, - sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Weekly offer", - DisplayOrder = 7, - }); + new MeasureWeight() + { + Name = "kg", + SystemKeyword = "kg", + Ratio = 0.45359237M, + DisplayOrder = 1, + }, + new MeasureWeight() + { + Name = "gram", + SystemKeyword = "g", + Ratio = 453.59237M, + DisplayOrder = 2, + }, + new MeasureWeight() + { + Name = "liter", + SystemKeyword = "l", + Ratio = 0.45359237M, + DisplayOrder = 3, + }, + new MeasureWeight() + { + Name = "milliliter", + SystemKeyword = "ml", + Ratio = 0.45359237M, + DisplayOrder = 4, + } + }; - sa22.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Best Price", - DisplayOrder = 8, - }); + this.Alter(entities); + return entities; + } - #endregion sa22 Offer + protected virtual string TaxNameBooks => "Books"; - #region sa23 Size + protected virtual string TaxNameDigitalGoods => "Downloadable Products"; - var sa23 = new SpecificationAttribute - { - Name = "Size", - DisplayOrder = 23, - }; + protected virtual string TaxNameJewelry => "Jewelry"; - sa23.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "XS", - DisplayOrder = 1, - }); + protected virtual string TaxNameApparel => "Apparel & Shoes"; - sa23.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "S", - DisplayOrder = 2, - }); + protected virtual string TaxNameFood => "Food"; - sa23.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "M", - DisplayOrder = 3, - }); + protected virtual string TaxNameElectronics => "Electronics & Software"; - sa23.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "L", - DisplayOrder = 4, - }); + protected virtual string TaxNameTaxFree => "Tax free"; - sa23.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "XL", - DisplayOrder = 5, - }); + public virtual decimal[] FixedTaxRates => new decimal[] { 0, 0, 0, 0, 0 }; - sa23.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + public IList TaxCategories() + { + var entities = new List { - Name = "XXL", - DisplayOrder = 6, - }); - - - #endregion sa23 Size + new TaxCategory + { + Name = this.TaxNameTaxFree, + DisplayOrder = 0, + }, + new TaxCategory + { + Name = this.TaxNameBooks, + DisplayOrder = 1, + }, + new TaxCategory + { + Name = this.TaxNameElectronics, + DisplayOrder = 5, + }, + new TaxCategory + { + Name = this.TaxNameDigitalGoods, + DisplayOrder = 10, + }, + new TaxCategory + { + Name = this.TaxNameJewelry, + DisplayOrder = 15, + }, + new TaxCategory + { + Name = this.TaxNameApparel, + DisplayOrder = 20, + } + }; - #region sa24 diameter + this.Alter(entities); + return entities; + } - var sa24 = new SpecificationAttribute - { - Name = "Diameter", - DisplayOrder = 24, + public IList Currencies() + { + var entities = new List() + { + CreateCurrency("en-US", published: true, rate: 1M, order: 0), + CreateCurrency("en-GB", published: true, rate: 0.61M, order: 5), + CreateCurrency("en-AU", published: true, rate: 0.94M, order: 10), + CreateCurrency("en-CA", published: true, rate: 0.98M, order: 15), + CreateCurrency("de-DE", rate: 0.79M, order: 20/*, formatting: string.Format("0.00 {0}", "\u20ac")*/), + CreateCurrency("de-CH", rate: 0.93M, order: 25, formatting: "CHF #,##0.00"), + CreateCurrency("zh-CN", rate: 6.48M, order: 30), + CreateCurrency("zh-HK", rate: 7.75M, order: 35), + CreateCurrency("ja-JP", rate: 80.07M, order: 40), + CreateCurrency("ru-RU", rate: 27.7M, order: 45), + CreateCurrency("tr-TR", rate: 1.78M, order: 50), + CreateCurrency("sv-SE", rate: 6.19M, order: 55) }; - sa24.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "38mm", - DisplayOrder = 1, - }); + this.Alter(entities); + return entities; + } - sa24.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + public IList ShippingMethods(bool includeSamples) + { + var entities = new List { - Name = "40mm", - DisplayOrder = 2, - }); + new ShippingMethod + { + Name = "In-Store Pickup", + Description ="Pick up your items at the store", + DisplayOrder = 0 + }, + new ShippingMethod + { + Name = "By Ground", + Description ="Compared to other shipping methods, like by flight or over seas, ground shipping is carried out closer to the earth", + DisplayOrder = 1 + }, + }; - sa24.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + if (includeSamples) { - Name = "44mm", - DisplayOrder = 3, - }); - - #endregion sa24 diameter + entities.Add(new ShippingMethod + { + Name = "Free shipping", + DisplayOrder = 2, + IgnoreCharges = true + }); + } - #region sa25 closure + Alter(entities); + return entities; + } - var sa25 = new SpecificationAttribute + public IList CustomerRoles(bool includeSamples) + { + var entities = new List { - Name = "Closure", - DisplayOrder = 25, + new CustomerRole + { + Name = "Administrators", + Active = true, + IsSystemRole = true, + SystemName = SystemCustomerRoleNames.Administrators, + }, + new CustomerRole + { + Name = "Forum Moderators", + Active = true, + IsSystemRole = true, + SystemName = SystemCustomerRoleNames.ForumModerators, + }, + new CustomerRole + { + Name = "Registered", + Active = true, + IsSystemRole = true, + SystemName = SystemCustomerRoleNames.Registered, + }, + new CustomerRole + { + Name = "Guests", + Active = true, + IsSystemRole = true, + SystemName = SystemCustomerRoleNames.Guests, + } }; - sa25.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "snap closure", - DisplayOrder = 1, - }); - - sa25.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + if (includeSamples) { - Name = "folding clasp", - DisplayOrder = 2, - }); + entities.Add(new CustomerRole + { + Name = "Inactive new customers", + Active = true, + IsSystemRole = false, + // SystemName is not required. It's only used here to assign a rule set later. + SystemName = "InactiveNewCustomers" + }); + } - sa25.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "thorn close", - DisplayOrder = 3, - }); + Alter(entities); + return entities; + } - #endregion sa25 closure + public Address AdminAddress() + { + var cCountry = _ctx.Set() + .Where(x => x.ThreeLetterIsoCode == "USA") + .FirstOrDefault(); - #region sa26 facial shape + var entity = new Address() + { + FirstName = "John", + LastName = "Smith", + PhoneNumber = "12345678", + Email = "admin@myshop.com", + FaxNumber = "", + Company = "John Smith LLC", + Address1 = "1234 Main Road", + Address2 = "", + City = "New York", + StateProvince = cCountry.StateProvinces.FirstOrDefault(), + Country = cCountry, + ZipPostalCode = "12212", + CreatedOnUtc = DateTime.UtcNow, + }; + this.Alter(entity); + return entity; + } - var sa26 = new SpecificationAttribute + public Customer SearchEngineUser() + { + var entity = new Customer { - Name = "Facial shape", - DisplayOrder = 26, + Email = "builtin@search-engine-record.com", + CustomerGuid = Guid.NewGuid(), + PasswordFormat = PasswordFormat.Clear, + AdminComment = "Built-in system guest record used for requests from search engines.", + Active = true, + IsSystemAccount = true, + SystemName = SystemCustomerNames.SearchEngine, + CreatedOnUtc = DateTime.UtcNow, + LastActivityDateUtc = DateTime.UtcNow, }; - sa26.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "oval", - DisplayOrder = 1, - }); + this.Alter(entity); + return entity; + } - sa26.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + public Customer BackgroundTaskUser() + { + var entity = new Customer { - Name = "round", - DisplayOrder = 2, - }); + Email = "builtin@background-task-record.com", + CustomerGuid = Guid.NewGuid(), + PasswordFormat = PasswordFormat.Clear, + AdminComment = "Built-in system record used for background tasks.", + Active = true, + IsSystemAccount = true, + SystemName = SystemCustomerNames.BackgroundTask, + CreatedOnUtc = DateTime.UtcNow, + LastActivityDateUtc = DateTime.UtcNow, + }; - sa26.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "heart shaped", - DisplayOrder = 3, - }); + this.Alter(entity); + return entity; + } - sa26.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + public Customer PdfConverterUser() + { + var entity = new Customer { - Name = "angular", - DisplayOrder = 4, - }); - - #endregion sa26 facial shape + Email = "builtin@pdf-converter-record.com", + CustomerGuid = Guid.NewGuid(), + PasswordFormat = PasswordFormat.Clear, + AdminComment = "Built-in system record used for the PDF converter.", + Active = true, + IsSystemAccount = true, + SystemName = SystemCustomerNames.PdfConverter, + CreatedOnUtc = DateTime.UtcNow, + LastActivityDateUtc = DateTime.UtcNow, + }; - #region sa27 storage capacity + this.Alter(entity); + return entity; + } - var sa27 = new SpecificationAttribute + public IList EmailAccounts() + { + var entities = new List() { - Name = "Storage capacity", - DisplayOrder = 27, + new EmailAccount + { + Email = "test@mail.com", + DisplayName = "Store name", + Host = "smtp.mail.com", + Port = 25, + Username = "123", + Password = "123", + EnableSsl = false, + UseDefaultCredentials = false + } }; + this.Alter(entities); + return entities; + } - sa27.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + public IList Topics() + { + var entities = new List() { - Name = "32 GB", - DisplayOrder = 1, - }); - - sa27.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "64 GB", - DisplayOrder = 2, - }); - - sa27.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "128 GB", - DisplayOrder = 3, - }); - - #endregion sa27 facial shape - - #region sa28 Dial window material type - - var sa28 = new SpecificationAttribute - { - Name = "Dial window material type", - DisplayOrder = 28, + new Topic + { + SystemName = "AboutUs", + IncludeInSitemap = false, + IsPasswordProtected = false, + Title = "About Us", + Body = "

Put your "About Us" information here. You can edit this in the admin site.

" + }, + new Topic + { + SystemName = "CheckoutAsGuestOrRegister", + IncludeInSitemap = false, + IsPasswordProtected = false, + RenderAsWidget = true, + WidgetWrapContent = false, + Title = "", + Body = "

Register and save time!
Register with us for future convenience:

  • Fast and easy check out
  • Easy access to your order history and status
" + }, + new Topic + { + SystemName = "ConditionsOfUse", + IncludeInSitemap = false, + IsPasswordProtected = false, + Title = "Conditions of use", + Body = "

Put your conditions of use information here. You can edit this in the admin site.

" + }, + new Topic + { + SystemName = "ContactUs", + IncludeInSitemap = false, + IsPasswordProtected = false, + RenderAsWidget = true, + WidgetWrapContent = false, + Title = "Contact us", + Body = "

Put your contact information here. You can edit this in the admin site.

" + }, + new Topic + { + SystemName = "ForumWelcomeMessage", + IncludeInSitemap = false, + IsPasswordProtected = false, + RenderAsWidget = true, + WidgetWrapContent = false, + Title = "Forums", + Body = "

Put your welcome message here. You can edit this in the admin site.

" + }, + new Topic + { + SystemName = "HomePageText", + IncludeInSitemap = false, + IsPasswordProtected = false, + RenderAsWidget = true, + WidgetWrapContent = false, + Title = "Welcome to our store", + Body = "

Online shopping is the process consumers go through to purchase products or services over the Internet. You can edit this in the admin site.

" + }, + new Topic + { + SystemName = "LoginRegistrationInfo", + IncludeInSitemap = false, + IsPasswordProtected = false, + RenderAsWidget = true, + WidgetWrapContent = false, + Title = "About login / registration", + Body = "

Not registered yet?

Create your own account now and experience our diversity. With an account you can place orders faster and will always have a perfect overview of your current and previous orders.

" + }, + new Topic + { + SystemName = "PrivacyInfo", + IncludeInSitemap = false, + IsPasswordProtected = false, + ShortTitle = "Privacy", + Title = "Privacy policy", + Body = "

" + }, + new Topic + { + SystemName = "ShippingInfo", + IncludeInSitemap = false, + IsPasswordProtected = false, + Title = "Shipping & Returns", + Body = "

Put your shipping & returns information here. You can edit this in the admin site.

" + }, + + new Topic + { + SystemName = "Imprint", + IncludeInSitemap = false, + IsPasswordProtected = false, + Title = "Imprint", + Body = "

Put your imprint information here. You can edit this in the admin site.

" + }, + new Topic + { + SystemName = "Disclaimer", + IncludeInSitemap = false, + IsPasswordProtected = false, + Title = "Disclaimer", + Body = "

Put your disclaimer information here. You can edit this in the admin site.

" + }, + new Topic + { + SystemName = "PaymentInfo", + IncludeInSitemap = false, + IsPasswordProtected = false, + Title = "Payment info", + Body = "

Put your payment information here. You can edit this in the admin site.

" + }, }; + this.Alter(entities); + return entities; + } - sa28.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() - { - Name = "Mineral", - DisplayOrder = 1, - }); + public IList Settings() + { + var defaultDimensionId = _ctx.Set().FirstOrDefault(x => x.SystemKeyword == "inch")?.Id ?? 0; + var defaultWeightId = _ctx.Set().FirstOrDefault(x => x.SystemKeyword == "lb")?.Id ?? 0; + var defaultLanguageId = _ctx.Set().FirstOrDefault()?.Id ?? 0; + var defaultEmailAccountId = _ctx.Set().FirstOrDefault()?.Id ?? 0; - sa28.SpecificationAttributeOptions.Add(new SpecificationAttributeOption() + var entities = new List { - Name = "Sapphire", - DisplayOrder = 2, - }); - - #endregion sa28 Dial window material type - - var entities = new List - { - sa1,sa2,sa3,sa4,sa5,sa6,sa7,sa8,sa9,sa10,sa11,sa12,sa13,sa14,sa15,sa16,sa17,sa18,sa19,sa20,sa21,sa22,sa23,sa24,sa25,sa26,sa27,sa28 - }; - - this.Alter(entities); - return entities; - } - - public IList ProductAttributes() - { - var entities = new List - { - new ProductAttribute - { - Name = "Color", - Alias = "color" - }, - new ProductAttribute - { - Name = "Custom Text", - Alias = "custom-text" - }, - new ProductAttribute - { - Name = "HDD", - Alias = "hdd" - }, - new ProductAttribute - { - Name = "OS", - Alias = "os" - }, - new ProductAttribute - { - Name = "Processor", - Alias = "processor" - }, - new ProductAttribute - { - Name = "RAM", - Alias = "ram", - }, - new ProductAttribute - { - Name = "Size", - Alias = "size" - }, - new ProductAttribute - { - Name = "Software", - Alias = "software" - }, - new ProductAttribute - { - Name = "Game", - Alias = "game" - }, - new ProductAttribute - { - Name = "Color", - Alias = "iphone-color" - }, - new ProductAttribute + new PdfSettings + { + }, + new CommonSettings + { + }, + new SeoSettings + { + }, + new SocialSettings + { + }, + new AdminAreaSettings + { + }, + new CatalogSettings + { + }, + new LocalizationSettings + { + DefaultAdminLanguageId = defaultLanguageId + }, + new CustomerSettings + { + }, + new AddressSettings + { + }, + new MediaSettings + { + }, + new StoreInformationSettings + { + }, + new RewardPointsSettings + { + }, + new CurrencySettings + { + }, + new MeasureSettings + { + BaseDimensionId = defaultDimensionId, + BaseWeightId = defaultWeightId, + }, + new ShoppingCartSettings + { + }, + new OrderSettings + { + }, + new SecuritySettings + { + }, + new ShippingSettings + { + }, + new PaymentSettings { - Name = "Color", - Alias = "ipad-color" + ActivePaymentMethodSystemNames = new List + { + "Payments.CashOnDelivery", + "Payments.Manual", + "Payments.PayInStore", + "Payments.Prepayment" + } }, - new ProductAttribute - { - Name = "Memory capacity", - Alias = "memory-capacity" - }, - new ProductAttribute - { - Name = "Width", - Alias = "width" - }, - new ProductAttribute - { - Name = "Length", - Alias = "length" - }, - new ProductAttribute - { - Name = "Plate", - Alias = "plate" - }, - new ProductAttribute - { - Name = "Plate Thickness", - Alias = "plate-thickness" - }, - new ProductAttribute + new TaxSettings { - Name = "Ballsize", - Alias = "ballsize" - }, - new ProductAttribute - { - Name = "Leather color", - Alias = "leather-color" - }, - new ProductAttribute - { - Name = "Seat Shell", - Alias = "seat-shell" - }, - new ProductAttribute - { - Name = "Base", - Alias = "base" - }, - new ProductAttribute - { - Name = "Material", - Alias = "material" - }, - new ProductAttribute - { - Name = "Style", - Alias = "style" - }, - new ProductAttribute + }, + new BlogSettings { - Name = "Controller", - Alias = "controller" }, - new ProductAttribute + new NewsSettings { - Name = "Framecolor", - Alias = "framecolor" }, - new ProductAttribute + new ForumSettings { - Name = "Lenscolor", - Alias = "lenscolor" }, - new ProductAttribute + new EmailAccountSettings { - Name = "Lenstype", - Alias = "lenstype" + DefaultEmailAccountId = defaultEmailAccountId }, - new ProductAttribute + new ThemeSettings { - Name = "Lenscolor", - Alias = "wayfarerlenscolor" }, - new ProductAttribute + new HomePageSettings { - Name = "Framecolor", - Alias = "wayfarerframecolor" } }; - this.Alter(entities); - return entities; - } + this.Alter(entities); + return entities; + } - public IList ProductAttributeOptionsSets() + public IList ProductTemplates() { - var entities = new List(); - var colorAttribute = _ctx.Set().First(x => x.Alias == "color"); - - entities.Add(new ProductAttributeOptionsSet - { - Name = "General colors", - ProductAttributeId = colorAttribute.Id - }); - + var entities = new List + { + new ProductTemplate + { + Name = "Default Product Template", + ViewPath = "Product", + DisplayOrder = 10 + } + }; this.Alter(entities); return entities; } - public IList ProductAttributeOptions() + public IList CategoryTemplates() { - var entities = new List(); - var colorAttribute = _ctx.Set().First(x => x.Alias == "color"); - var sets = _ctx.Set().ToList(); - - var generalColors = new[] - { - new { Color = "Red", Code = "#ff0000" }, - new { Color = "Green", Code = "#008000" }, - new { Color = "Blue", Code = "#0000ff" }, - new { Color = "Yellow", Code = "#ffff00" }, - new { Color = "Black", Code = "#000000" }, - new { Color = "White", Code = "#ffffff" }, - new { Color = "Gray", Code = "#808080" }, - new { Color = "Silver", Code = "#dddfde" }, - new { Color = "Brown", Code = "#a52a2a" }, - }; - - for (var i = 0; i < generalColors.Length; ++i) - { - entities.Add(new ProductAttributeOption - { - ProductAttributeOptionsSetId = sets[0].Id, - Alias = generalColors[i].Color.ToLower(), - Name = generalColors[i].Color, - Quantity = 1, - DisplayOrder = i + 1, - Color = generalColors[i].Code - }); - } - + var entities = new List + { + new CategoryTemplate + { + Name = "Products in Grid or Lines", + ViewPath = "CategoryTemplate.ProductsInGridOrLines", + DisplayOrder = 1 + }, + }; this.Alter(entities); return entities; } - public IList ProductVariantAttributes() - { - var entities = new List(); - var attrColor = _ctx.Set().First(x => x.Alias == "color"); - var attrController = _ctx.Set().First(x => x.Alias == "controller"); - var attrSize = _ctx.Set().First(x => x.Alias == "size"); - var attrGames = _ctx.Set().First(x => x.Alias == "game"); - var attrBallsize = _ctx.Set().First(x => x.Alias == "ballsize"); - var attrMemoryCapacity = _ctx.Set().First(x => x.Alias == "memory-capacity"); - var attrLensType = _ctx.Set().First(x => x.Alias == "lenstype"); - var attrFramecolor = _ctx.Set().First(x => x.Alias =="framecolor"); - var attrLenscolor = _ctx.Set().First(x => x.Alias == "lenscolor"); - var attrIphoneColor = _ctx.Set().First(x => x.Alias == "iphone-color"); - var attr97iPadColor = _ctx.Set().First(x => x.Alias == "ipad-color"); - var attrWidth = _ctx.Set().First(x => x.Alias == "width"); - var attrLength = _ctx.Set().First(x => x.Alias == "length"); - var attrPlate = _ctx.Set().First(x => x.Alias == "plate"); - var attrPlateThickness = _ctx.Set().First(x => x.Alias == "plate-thickness"); - var attrLeatherColor = _ctx.Set().First(x => x.Alias == "leather-color"); - var attrSeatShell = _ctx.Set().First(x => x.Alias == "seat-shell"); - var attrBase = _ctx.Set().First(x => x.Alias == "base"); - var attrMaterial = _ctx.Set().First(x => x.Alias == "material"); - var attrWayfarerLenscolor = _ctx.Set().First(x => x.Alias == "wayfarerlenscolor"); - var attrWayfarerFramecolor = _ctx.Set().First(x => x.Alias == "wayfarerframecolor"); - - var generalColors = new[] - { - new { Name = "Black", Color = "#000000" }, - new { Name = "White", Color = "#ffffff" }, - new { Name = "Anthracite", Color = "#32312f" }, - new { Name = "Fuliginous", Color = "#5F5B5C" }, - new { Name = "Light grey", Color = "#e3e3e5" }, - new { Name = "Natural", Color = "#BBB98B" }, - new { Name = "Biscuit", Color = "#e0ccab" }, - new { Name = "Beige", Color = "#d1bc8a" }, - new { Name = "Hazelnut", Color = "#94703e" }, - new { Name = "Brown", Color = "#755232" }, - new { Name = "Dark brown", Color = "#27160F" }, - new { Name = "Dark green", Color = "#0a3210" }, - new { Name = "Blue", Color = "#0000ff" }, - new { Name = "Cognac", Color = "#e9aa1b" }, - new { Name = "Yellow", Color = "#e6e60c" }, - new { Name = "Orange", Color = "#ff6501" }, - new { Name = "Tomato red", Color = "#b10101" }, - new { Name = "Red", Color = "#fe0000" }, - new { Name = "Dark red", Color = "#5e0000" } - }; - - - #region Oakley custom flak - - var productCustomFlak = _ctx.Set().First(x => x.Sku == "P-3002"); - - var attributeLensType = new ProductVariantAttribute() - { - Product = productCustomFlak, - ProductAttribute = attrLensType, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.RadioList - }; - - attributeLensType.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Standard", - Alias = "standard", - IsPreSelected = true, - DisplayOrder = 1, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 100.0M - }); - - attributeLensType.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Polarized", - Alias = "polarized", - DisplayOrder = 2, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 200.0M - }); - attributeLensType.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Prizm", - Alias = "prizm", - DisplayOrder = 3, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 200.0M - }); - - entities.Add(attributeLensType); - - - var attributeFramecolor = new ProductVariantAttribute() - { - Product = productCustomFlak, - ProductAttribute = attrFramecolor, - IsRequired = true, - DisplayOrder = 2, - AttributeControlType = AttributeControlType.Boxes - }; - - attributeFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Matte Black", - Alias = "matteblack", - IsPreSelected = true, - DisplayOrder = 1, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#2d2d2d" - }); - - attributeFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Polishedwhite", - Alias = "polishedwhite", - DisplayOrder = 2, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#f5f5f5" - }); - - attributeFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Sky Blue", - Alias = "skyblue", - DisplayOrder = 3, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#4187f6" - }); - - attributeFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Orange Flare", - Alias = "orangeflare", - DisplayOrder = 4, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#f55700" - }); - - attributeFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Redline", - Alias = "redline", - DisplayOrder = 5, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#cf0a02" - }); - - entities.Add(attributeFramecolor); - - var attributeLenscolor = new ProductVariantAttribute() - { - Product = productCustomFlak, - ProductAttribute = attrLenscolor, - IsRequired = true, - DisplayOrder = 3, - AttributeControlType = AttributeControlType.Boxes - }; - - attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Gray", - Alias = "gray", - IsPreSelected = true, - DisplayOrder = 1, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#7A798B" - }); - - attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Sapphire Iridium", - Alias = "sapphireiridium", - DisplayOrder = 2, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#4460BB" - }); - - attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Violet Iridium", - Alias = "violetiridium", - DisplayOrder = 3, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#5C5A89" - }); - - attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Jade Iridium", - Alias = "jadeiridium", - DisplayOrder = 4, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#376559" - }); - - attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Ruby Iridium", - Alias = "rubyiridium", - DisplayOrder = 5, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#CCAD12" - }); - - attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "24K Iridium", - Alias = "24kiridium", - DisplayOrder = 6, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#CE9D12" - }); - - attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Positive Red Iridium", - Alias = "positiverediridium", - DisplayOrder = 7, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#764CDC" - }); - - attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Clear", - Alias = "clear", - DisplayOrder = 7, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#e2e2e3" - }); - attributeLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Fire Iridium", - Alias = "fireiridium", - DisplayOrder = 7, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#E2C724" - }); - - entities.Add(attributeLenscolor); - - #endregion Oakley custom flak - - - #region wayfarer - - var productWayfarer = _ctx.Set().First(x => x.Sku == "P-3003"); - var wayfarerFramePictures = _ctx.Set().Where(x => x.SeoFilename.StartsWith("wayfarer_")).ToList(); - - var attributeWayfarerLenscolor = new ProductVariantAttribute() - { - Product = productWayfarer, - ProductAttribute = attrWayfarerLenscolor, - IsRequired = true, - DisplayOrder = 3, - AttributeControlType = AttributeControlType.Boxes - }; - - attributeWayfarerLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Blue-Gray classic", - Alias = "blue-gray-classic", - IsPreSelected = true, - DisplayOrder = 1, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#3e4659" - }); - - attributeWayfarerLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Brown course", - Alias = "brown-course", - DisplayOrder = 2, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#3e4659" - }); - - attributeWayfarerLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Gray course", - Alias = "gray-course", - DisplayOrder = 3, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#727377" - }); - - attributeWayfarerLenscolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Green classic", - Alias = "green-classic", - DisplayOrder = 4, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#3c432e" - }); - - entities.Add(attributeWayfarerLenscolor); - - var attributeWayfarerFramecolor = new ProductVariantAttribute() - { - Product = productWayfarer, - ProductAttribute = attrWayfarerFramecolor, - IsRequired = true, - DisplayOrder = 3, - AttributeControlType = AttributeControlType.Boxes - }; - - var wayfarerFramePicture = wayfarerFramePictures.First(x => x.SeoFilename.EndsWith("_rayban_black")); - - attributeWayfarerFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Black", - Alias = "rayban-black", - IsPreSelected = true, - DisplayOrder = 1, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - //Color = "#3e4659" - PictureId = wayfarerFramePicture.Id - }); - - wayfarerFramePicture = wayfarerFramePictures.First(x => x.SeoFilename.EndsWith("_havana_black")); - attributeWayfarerFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Havana; Black", - Alias = "havana-black", - DisplayOrder = 2, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - //Color = "#3e4659" - PictureId = wayfarerFramePicture.Id - }); - - wayfarerFramePicture = wayfarerFramePictures.First(x => x.SeoFilename.EndsWith("_havana")); - attributeWayfarerFramecolor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Havana", - Alias = "havana", - DisplayOrder = 3, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - //Color = "#727377", - PictureId = wayfarerFramePicture.Id - }); - - - entities.Add(attributeWayfarerFramecolor); - - #endregion wayfarer - - #region 9,7 iPad - - var product97iPad = _ctx.Set().First(x => x.Sku == "P-2004"); - - var attribute97iPadMemoryCapacity = new ProductVariantAttribute() - { - Product = product97iPad, - ProductAttribute = attrMemoryCapacity, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.RadioList - }; - - attribute97iPadMemoryCapacity.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "64 GB", - Alias = "64gb", - IsPreSelected = true, - DisplayOrder = 2, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 100.0M - }); - - attribute97iPadMemoryCapacity.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "128 GB", - Alias = "128gb", - DisplayOrder = 3, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 200.0M - }); - - entities.Add(attribute97iPadMemoryCapacity); - + public IList ManufacturerTemplates() + { + var entities = new List + { + new ManufacturerTemplate + { + Name = "Products in Grid or Lines", + ViewPath = "ManufacturerTemplate.ProductsInGridOrLines", + DisplayOrder = 1 + }, + }; + this.Alter(entities); + return entities; + } - var attribute97iPadColor = new ProductVariantAttribute() + public IList ScheduleTasks() + { + var entities = new List { - Product = product97iPad, - ProductAttribute = attr97iPadColor, - IsRequired = true, - DisplayOrder = 2, - AttributeControlType = AttributeControlType.Boxes - }; - - attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Silver", - Alias = "silver", - IsPreSelected = true, - DisplayOrder = 1, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#dddfde" - }); - - attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Gold", - Alias = "gold", - DisplayOrder = 2, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#e3d0ba" - }); - - attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Spacegray", - Alias = "spacegray", - DisplayOrder = 3, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#abaeb1" - }); - - attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Rose", - Alias = "rose", - DisplayOrder = 4, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#d9a6ad" - }); - - attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Mint", - Alias = "mint", - DisplayOrder = 5, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#a6dbb1" - }); - - attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Purple", - Alias = "purple", - DisplayOrder = 6, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#dba5d7" - }); - - attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Lightblue", - Alias = "lightblue", - DisplayOrder = 7, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#a6b9df" - }); - - attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Turquoise", - Alias = "turquoise", - DisplayOrder = 8, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#a4dbde" - }); - - attribute97iPadColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Yellow", - Alias = "yellow", - DisplayOrder = 7, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#dfddb6" - }); - - - entities.Add(attribute97iPadColor); - - #endregion 9,7 iPad - - #region iPhone 7 plus - - var productIphone7Plus = _ctx.Set().First(x => x.Sku == "P-2001"); - - var attributeIphone7PlusMemoryCapacity = new ProductVariantAttribute() - { - Product = productIphone7Plus, - ProductAttribute = attrMemoryCapacity, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.RadioList - }; - - attributeIphone7PlusMemoryCapacity.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "64 GB", - Alias = "64gb", - IsPreSelected = true, - DisplayOrder = 2, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 100.0M - }); - - attributeIphone7PlusMemoryCapacity.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "128 GB", - Alias = "128gb", - DisplayOrder = 3, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 200.0M - }); - - entities.Add(attributeIphone7PlusMemoryCapacity); - - - var attributeIphone7PlusColor = new ProductVariantAttribute() - { - Product = productIphone7Plus, - ProductAttribute = attrIphoneColor, - IsRequired = true, - DisplayOrder = 2, - AttributeControlType = AttributeControlType.Boxes - }; - - attributeIphone7PlusColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Silver", - Alias = "silver", - IsPreSelected = true, - DisplayOrder = 1, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#dddfde" - }); - - attributeIphone7PlusColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Gold", - Alias = "gold", - DisplayOrder = 2, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#e3d0ba" - }); - - attributeIphone7PlusColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Red", - Alias = "red", - DisplayOrder = 3, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#af1e2d" - }); - - attributeIphone7PlusColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Rose", - Alias = "rose", - DisplayOrder = 4, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#d9a6ad" - }); - - attributeIphone7PlusColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Black", - Alias = "black", - DisplayOrder = 5, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - Color = "#000000" - }); - - entities.Add(attributeIphone7PlusColor); - - #endregion iPhone 7 plus - - #region iPhone - - // var productIphone = _ctx.Set().First(x => x.Sku == "Apple-1001"); - - //var attributeIphoneMemoryCapacity = new ProductVariantAttribute() - //{ - // Product = productIphone, - // ProductAttribute = attrMemoryCapacity, - // IsRequired = true, - // DisplayOrder = 1, - // AttributeControlType = AttributeControlType.DropdownList - //}; - - //attributeIphoneMemoryCapacity.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - //{ - // Name = "16 GB", - // Alias = "16gb", - // IsPreSelected = true, - // DisplayOrder = 1, - // Quantity = 1, - // ValueType = ProductVariantAttributeValueType.Simple - //}); - - //attributeIphoneMemoryCapacity.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - //{ - // Name = "64 GB", - // Alias = "64gb", - // DisplayOrder = 2, - // Quantity = 1, - // ValueType = ProductVariantAttributeValueType.Simple, - // PriceAdjustment = 100.0M - //}); - - //attributeIphoneMemoryCapacity.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - //{ - // Name = "128 GB", - // Alias = "128gb", - // DisplayOrder = 3, - // Quantity = 1, - // ValueType = ProductVariantAttributeValueType.Simple, - // PriceAdjustment = 200.0M - //}); - - //entities.Add(attributeIphoneMemoryCapacity); - - - //var attributeIphoneColor = new ProductVariantAttribute() - //{ - // Product = productIphone, - // ProductAttribute = attrIphoneColor, - // IsRequired = true, - // DisplayOrder = 2, - // AttributeControlType = AttributeControlType.DropdownList - //}; - - //attributeIphoneColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - //{ - // Name = "Silver", - // Alias = "silver", - // IsPreSelected = true, - // DisplayOrder = 1, - // Quantity = 1, - // ValueType = ProductVariantAttributeValueType.Simple - //}); - - //attributeIphoneColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - //{ - // Name = "Gold", - // Alias = "gold", - // DisplayOrder = 2, - // Quantity = 1, - // ValueType = ProductVariantAttributeValueType.Simple - //}); - - //attributeIphoneColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - //{ - // Name = "Space gray", - // Alias = "spacegray", - // DisplayOrder = 3, - // Quantity = 1, - // ValueType = ProductVariantAttributeValueType.Simple - //}); - - //entities.Add(attributeIphoneColor); - - #endregion iPhone - - #region attributeDualshock3ControllerColor - - var productPs3 = _ctx.Set().First(x => x.Sku == "Sony-PS399000"); - - var attributeDualshock3ControllerColor = new ProductVariantAttribute() - { - Product = productPs3, - ProductAttribute = attrController, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.RadioList - }; - - attributeDualshock3ControllerColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "without controller", - Alias = "without_controller", - IsPreSelected = true, - DisplayOrder = 1, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple - }); - - attributeDualshock3ControllerColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "with controller", - Alias = "with_controller", - PriceAdjustment = 60.0M, - DisplayOrder = 2, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple - }); - - entities.Add(attributeDualshock3ControllerColor); - - #endregion attributeDualshock3ControllerColor - - #region attribute Apple Airpod - - var productAirpod = _ctx.Set().First(x => x.Sku == "P-2003"); - - var attributeAirpod = new ProductVariantAttribute() - { - Product = productAirpod, - ProductAttribute = attrColor, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.Boxes - }; - - attributeAirpod.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Gold", - Alias = "gold", - DisplayOrder = 1, - Quantity = 1, - Color = "#e3d0ba", - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 5.00M - //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-acreed3").Id - }); - - attributeAirpod.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Rose", - Alias = "rose", - DisplayOrder = 2, - Quantity = 1, - Color = "#d9a6ad", - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 10.00M, - //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-watchdogs").Id - }); - - attributeAirpod.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Mint", - Alias = "mint", - DisplayOrder = 3, - Quantity = 1, - Color= "#a6dbb1", - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 15.00M - //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-princepersia").Id - }); - - attributeAirpod.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Lightblue", - Alias = "lightblue", - DisplayOrder = 3, - Quantity = 1, - Color = "#a6b9df", - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 15.00M - //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-princepersia").Id - }); - - attributeAirpod.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "Turquoise", - Alias = "turquoise", - DisplayOrder = 3, - Quantity = 1, - Color = "#a4dbde", - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 15.00M - //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-princepersia").Id - }); - - attributeAirpod.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "White", - Alias = "white", - DisplayOrder = 3, - Quantity = 1, - Color = "#ffffff", - IsPreSelected = true, - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 15.00M - //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-princepersia").Id - }); - - entities.Add(attributeAirpod); - - #endregion attribute Apple Airpod - - #region attribute Evopower 5.3 Trainer HS Ball - - var productEvopower = _ctx.Set().First(x => x.Sku == "P-5003"); - - var attributeEvopower = new ProductVariantAttribute() - { - Product = productEvopower, - ProductAttribute = attrBallsize, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.RadioList - }; - - attributeEvopower.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "3", - Alias = "ballsize-3", - DisplayOrder = 1, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 5.00M - //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-acreed3").Id - }); - - attributeEvopower.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "4", - Alias = "ballsize-4", - DisplayOrder = 2, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 10.00M, - IsPreSelected = true - //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-watchdogs").Id - }); - - attributeEvopower.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - { - Name = "5", - Alias = "ballsize-5", - DisplayOrder = 3, - Quantity = 1, - ValueType = ProductVariantAttributeValueType.Simple, - PriceAdjustment = 15.00M - //LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-princepersia").Id - }); - - - entities.Add(attributeEvopower); - - #endregion attribute Evopower 5.3 Trainer HS Ball - - #region attributePs3OneGameFree - - // var productPs3OneGameFree = _ctx.Set().First(x => x.Sku == "Sony-PS310111"); - - //var attributePs3OneGameFree = new ProductVariantAttribute() - //{ - // Product = productPs3OneGameFree, - // ProductAttribute = attrGames, - // IsRequired = true, - // DisplayOrder = 1, - // AttributeControlType = AttributeControlType.DropdownList - //}; - - //attributePs3OneGameFree.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - //{ - // Name = "Minecraft - Playstation 4 Edition", - // Alias = "minecraft-playstation4edition", - // DisplayOrder = 1, - // Quantity = 1, - // ValueType = ProductVariantAttributeValueType.ProductLinkage, - // LinkedProductId = _ctx.Set().First(x => x.Sku == "PD-Minecraft4ps4").Id - //}); - - //attributePs3OneGameFree.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - //{ - // Name = "Watch Dogs", - // Alias = "watch-dogs", - // DisplayOrder = 2, - // Quantity = 1, - // ValueType = ProductVariantAttributeValueType.ProductLinkage, - // LinkedProductId = _ctx.Set().First(x => x.Sku == "Ubi-watchdogs").Id - //}); - - //attributePs3OneGameFree.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - //{ - // Name = "Horizon Zero Dawn - PlayStation 4", - // Alias = "horizon-zero-dawn-playStation-4", - // DisplayOrder = 3, - // Quantity = 1, - // ValueType = ProductVariantAttributeValueType.ProductLinkage, - // LinkedProductId = _ctx.Set().First(x => x.Sku == "PD-ZeroDown4PS4").Id - //}); - - //attributePs3OneGameFree.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue() - //{ - // Name = "LEGO Worlds - PlayStation 4", - // Alias = "lego-worlds-playstation_4", - // DisplayOrder = 4, - // Quantity = 1, - // ValueType = ProductVariantAttributeValueType.ProductLinkage, - // LinkedProductId = _ctx.Set().First(x => x.Sku == "Gaming-Lego-001").Id - //}); - - //entities.Add(attributePs3OneGameFree); - - #endregion attributePs3OneGameFree - - #region Fashion - Converse All Star - - var productAllStar = _ctx.Set().First(x => x.Sku == "Fashion-112355"); - var allStarColors = new string[] { "Charcoal", "Maroon", "Navy", "Purple", "White" }; - var allStarPictures = _ctx.Set().Where(x => x.SeoFilename.StartsWith("all-star-")).ToList(); - - var attrAllStarColor = new ProductVariantAttribute - { - Product = productAllStar, - ProductAttribute = attrColor, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.Boxes - }; - - for (var i = 0; i < allStarColors.Length; ++i) - { - var allStarPicture = allStarPictures.First(x => x.SeoFilename.EndsWith(allStarColors[i].ToLower())); - attrAllStarColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = allStarColors[i], - Alias = allStarColors[i].ToLower(), - DisplayOrder = i + 1, - Quantity = 1, - PictureId = allStarPicture.Id - }); - } - entities.Add(attrAllStarColor); - - var attrAllStarSize = new ProductVariantAttribute - { - Product = productAllStar, - ProductAttribute = attrSize, - IsRequired = true, - DisplayOrder = 2, - AttributeControlType = AttributeControlType.Boxes - }; - attrAllStarSize.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "42", - Alias = "42", - DisplayOrder = 1, - Quantity = 1, - IsPreSelected = true - }); - attrAllStarSize.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "43", - Alias = "43", - DisplayOrder = 2, - Quantity = 1 - }); - attrAllStarSize.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "44", - Alias = "44", - DisplayOrder = 3, - Quantity = 1 - }); - entities.Add(attrAllStarSize); - - #endregion - - #region Fashion - Shirt Meccanica - - var productShirtMeccanica = _ctx.Set().First(x => x.Sku == "Fashion-987693502"); - var shirtMeccanicaSizes = new string[] { "XS", "S", "M", "L", "XL" }; - var shirtMeccanicaColors = new[] - { - new { Color = "Red", Code = "#fe0000" }, - new { Color = "Black", Code = "#000000" } - }; - - var attrShirtMeccanicaColor = new ProductVariantAttribute - { - Product = productShirtMeccanica, - ProductAttribute = attrColor, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.Boxes - }; - - for (var i = 0; i < shirtMeccanicaColors.Length; ++i) - { - attrShirtMeccanicaColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = shirtMeccanicaColors[i].Color, - Alias = shirtMeccanicaColors[i].Color.ToLower(), - DisplayOrder = i + 1, - Quantity = 1, - Color = shirtMeccanicaColors[i].Code, - IsPreSelected = shirtMeccanicaColors[i].Color == "Red" - }); - } - entities.Add(attrShirtMeccanicaColor); - - var attrShirtMeccanicaSize = new ProductVariantAttribute - { - Product = productShirtMeccanica, - ProductAttribute = attrSize, - IsRequired = true, - DisplayOrder = 2, - AttributeControlType = AttributeControlType.Boxes - }; - - for (var i = 0; i < shirtMeccanicaSizes.Length; ++i) - { - attrShirtMeccanicaSize.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = shirtMeccanicaSizes[i], - Alias = shirtMeccanicaSizes[i].ToLower(), - DisplayOrder = i + 1, - Quantity = 1, - IsPreSelected = shirtMeccanicaSizes[i] == "XS" - }); - } - entities.Add(attrShirtMeccanicaSize); - - #endregion - - #region Fashion - Ladies Jacket - - var productLadiesJacket = _ctx.Set().First(x => x.Sku == "Fashion-JN1107"); - var ladiesJacketSizes = new string[] { "XS", "S", "M", "L", "XL" }; - var ladiesJacketColors = new[] - { - new { Color = "Red", Code = "#CE1F1C" }, - new { Color = "Orange", Code = "#EB7F01" }, - new { Color = "Green", Code = "#24B87E" }, - new { Color = "Blue", Code = "#0F8CCE" }, - new { Color = "Navy", Code = "#525671" }, - new { Color = "Silver", Code = "#ABB0B3" }, - new { Color = "Black", Code = "#404040" } - }; - - var attrLadiesJacketColor = new ProductVariantAttribute - { - Product = productLadiesJacket, - ProductAttribute = attrColor, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.Boxes - }; - - for (var i = 0; i < ladiesJacketColors.Length; ++i) - { - attrLadiesJacketColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = ladiesJacketColors[i].Color, - Alias = ladiesJacketColors[i].Color.ToLower(), - DisplayOrder = i + 1, - Quantity = 1, - Color = ladiesJacketColors[i].Code, - IsPreSelected = ladiesJacketColors[i].Color == "Red" - }); - } - entities.Add(attrLadiesJacketColor); - - var attrLadiesJacketSize = new ProductVariantAttribute - { - Product = productLadiesJacket, - ProductAttribute = attrSize, - IsRequired = true, - DisplayOrder = 2, - AttributeControlType = AttributeControlType.RadioList - }; - - for (var i = 0; i < ladiesJacketSizes.Length; ++i) - { - attrLadiesJacketSize.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = ladiesJacketSizes[i], - Alias = ladiesJacketSizes[i].ToLower(), - DisplayOrder = i + 1, - Quantity = 1, - IsPreSelected = ladiesJacketSizes[i] == "XS" - }); - } - entities.Add(attrLadiesJacketSize); - - #endregion - - #region Fashion - Clark Jeans - - var productClarkJeans = _ctx.Set().First(x => x.Sku == "Fashion-65986524"); - var clarkJeansWidth = new string[] { "31", "32", "33", "34", "35", "36", "38", "40", "42", "44", "46" }; - var clarkJeansLength = new string[] { "30", "32", "34" }; - - var attrClarkJeansWidth = new ProductVariantAttribute - { - Product = productClarkJeans, - ProductAttribute = attrWidth, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.Boxes - }; - - for (var i = 0; i < clarkJeansWidth.Length; ++i) - { - attrClarkJeansWidth.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = clarkJeansWidth[i], - Alias = clarkJeansWidth[i], - DisplayOrder = i + 1, - Quantity = 1, - IsPreSelected = clarkJeansWidth[i] == "31" - }); - } - entities.Add(attrClarkJeansWidth); - - var attrClarkJeansLength = new ProductVariantAttribute - { - Product = productClarkJeans, - ProductAttribute = attrLength, - IsRequired = true, - DisplayOrder = 2, - AttributeControlType = AttributeControlType.Boxes - }; - - for (var i = 0; i < clarkJeansLength.Length; ++i) - { - attrClarkJeansLength.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = clarkJeansLength[i], - Alias = clarkJeansLength[i], - DisplayOrder = i + 1, - Quantity = 1, - IsPreSelected = clarkJeansLength[i] == "30" - }); - } - entities.Add(attrClarkJeansLength); - - #endregion Fashion - Clark Jeans - - #region Furniture - Le Corbusier LC 6 table - - var productCorbusierTable = _ctx.Set().First(x => x.Sku == "Furniture-lc6"); - - var attrCorbusierTablePlate = new ProductVariantAttribute - { - Product = productCorbusierTable, - ProductAttribute = attrPlate, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.Boxes - }; - attrCorbusierTablePlate.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "Clear glass", - Alias = "clear-glass", - DisplayOrder = 1, - Quantity = 1, - IsPreSelected = true - }); - attrCorbusierTablePlate.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "Sandblasted glass", - Alias = "sandblasted-glass", - DisplayOrder = 2, - Quantity = 1 - }); - entities.Add(attrCorbusierTablePlate); - - var attrCorbusierTableThickness = new ProductVariantAttribute - { - Product = productCorbusierTable, - ProductAttribute = attrPlateThickness, - IsRequired = true, - DisplayOrder = 2, - AttributeControlType = AttributeControlType.Boxes - }; - attrCorbusierTableThickness.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "15 mm", - Alias = "15mm", - DisplayOrder = 1, - Quantity = 1, - IsPreSelected = true - }); - attrCorbusierTableThickness.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "19 mm", - Alias = "19mm", - DisplayOrder = 2, - Quantity = 1 - }); - entities.Add(attrCorbusierTableThickness); - - #endregion - - #region Soccer Adidas TANGO SALA BALL - - var productAdidasTANGOSALABALL = _ctx.Set().First(x => x.Sku == "P-5001"); - var productAdidasTANGOSALABALLSizes = new string[] { "3", "4", "5" }; - var productAdidasTANGOSALABALLColors = new[] - { - new { Color = "Red", Code = "#ff0000" }, - new { Color = "Yellow", Code = " #ffff00" }, - new { Color = "Green", Code = "#008000" }, - new { Color = "Blue", Code = "#0000ff" }, - new { Color = "Gray", Code = "#808080" }, - new { Color = "White", Code = "#ffffff" }, - new { Color = "Brown", Code = "#a52a2a" } - }; - - var attrAdidasTANGOSALABALLColor = new ProductVariantAttribute - { - Product = productAdidasTANGOSALABALL, - ProductAttribute = attrColor, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.Boxes - }; - - for (var i = 0; i < productAdidasTANGOSALABALLColors.Length; ++i) - { - attrAdidasTANGOSALABALLColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + new ScheduleTask { - Name = productAdidasTANGOSALABALLColors[i].Color, - Alias = productAdidasTANGOSALABALLColors[i].Color.ToLower(), - DisplayOrder = i + 1, - Quantity = 1, - Color = productAdidasTANGOSALABALLColors[i].Code, - IsPreSelected = productAdidasTANGOSALABALLColors[i].Color == "White" - }); - } - entities.Add(attrAdidasTANGOSALABALLColor); - - var attrAdidasTANGOSALABALLSize = new ProductVariantAttribute - { - Product = productAdidasTANGOSALABALL, - ProductAttribute = attrSize, - IsRequired = true, - DisplayOrder = 2, - AttributeControlType = AttributeControlType.RadioList - }; - - for (var i = 0; i < productAdidasTANGOSALABALLSizes.Length; ++i) - { - attrAdidasTANGOSALABALLSize.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + Name = "Send emails", + CronExpression = "* * * * *", // every Minute + Type = "SmartStore.Services.Messages.QueuedMessagesSendTask, SmartStore.Services", + Enabled = true, + StopOnError = false, + Priority = TaskPriority.High + }, + new ScheduleTask { - Name = productAdidasTANGOSALABALLSizes[i], - Alias = productAdidasTANGOSALABALLSizes[i].ToLower(), - DisplayOrder = i + 1, - Quantity = 1, - IsPreSelected = productAdidasTANGOSALABALLSizes[i] == "5" - }); - } - entities.Add(attrAdidasTANGOSALABALLSize); - - #endregion Soccer Adidas TANGO SALA BALL - - #region Torfabrik official game ball - - var productTorfabrikBall = _ctx.Set().First(x => x.Sku == "P-5002"); - var productTorfabrikBallSizes = new string[] { "3", "4", "5" }; - var productTorfabrikBallColors = new[] - { - new { Color = "Red", Code = "#ff0000" }, - new { Color = "Yellow", Code = " #ffff00" }, - new { Color = "Green", Code = "#008000" }, - new { Color = "Blue", Code = "#0000ff" }, - new { Color = "White", Code = "#ffffff" }, - }; - - var attrTorfabrikBallColor = new ProductVariantAttribute - { - Product = productTorfabrikBall, - ProductAttribute = attrColor, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.Boxes - }; - - for (var i = 0; i < productTorfabrikBallColors.Length; ++i) - { - attrTorfabrikBallColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + Name = "Delete guests", + CronExpression = "*/10 * * * *", // Every 10 minutes + Type = "SmartStore.Services.Customers.DeleteGuestsTask, SmartStore.Services", + Enabled = true, + StopOnError = false, + }, + new ScheduleTask { - Name = productTorfabrikBallColors[i].Color, - Alias = productTorfabrikBallColors[i].Color.ToLower(), - DisplayOrder = i + 1, - Quantity = 1, - Color = productTorfabrikBallColors[i].Code, - IsPreSelected = productTorfabrikBallColors[i].Color == "White" - }); - } - entities.Add(attrTorfabrikBallColor); - - var attrTorfabrikSize = new ProductVariantAttribute - { - Product = productTorfabrikBall, - ProductAttribute = attrSize, - IsRequired = true, - DisplayOrder = 2, - AttributeControlType = AttributeControlType.RadioList - }; - - for (var i = 0; i < productTorfabrikBallSizes.Length; ++i) - { - attrTorfabrikSize.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue + Name = "Delete logs", + CronExpression = "0 1 * * *", // At 01:00 + Type = "SmartStore.Services.Logging.DeleteLogsTask, SmartStore.Services", + Enabled = true, + StopOnError = false, + }, + new ScheduleTask { - Name = productTorfabrikBallSizes[i], - Alias = productTorfabrikBallSizes[i].ToLower(), - DisplayOrder = i + 1, - Quantity = 1, - IsPreSelected = productTorfabrikBallSizes[i] == "5" - }); - } - entities.Add(attrTorfabrikSize); - - #endregion Soccer Torfabrik official game ball - - #region Furniture - Ball chair - - var productBallChair = _ctx.Set().First(x => x.Sku == "Furniture-ball-chair"); - - var attrBallChairMaterial = new ProductVariantAttribute - { - Product = productBallChair, - ProductAttribute = attrMaterial, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.DropdownList - }; - attrBallChairMaterial.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "Leather Special", - Alias = "leather-special", - DisplayOrder = 1, - Quantity = 1, - IsPreSelected = true - }); - attrBallChairMaterial.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "Leather Aniline", - Alias = "leather-aniline", - DisplayOrder = 2, - Quantity = 1 - }); - attrBallChairMaterial.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "Mixed Linen", - Alias = "mixed-linen", - DisplayOrder = 3, - Quantity = 1 - }); - entities.Add(attrBallChairMaterial); - - var attrBallChairColor = new ProductVariantAttribute - { - Product = productBallChair, - ProductAttribute = attrColor, - IsRequired = true, - DisplayOrder = 2, - AttributeControlType = AttributeControlType.Boxes - }; - attrBallChairColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "White", - Alias = "white", - Color = "#ffffff", - DisplayOrder = 1, - Quantity = 1, - IsPreSelected = true - }); - attrBallChairColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "Black", - Alias = "black", - Color = "#000000", - DisplayOrder = 2, - Quantity = 1 - }); - entities.Add(attrBallChairColor); - - var attrBallChairLeatherColor = new ProductVariantAttribute - { - Product = productBallChair, - ProductAttribute = attrLeatherColor, - IsRequired = true, - DisplayOrder = 3, - AttributeControlType = AttributeControlType.Boxes - }; - - for (var i = 0; i < generalColors.Length; ++i) - { - attrBallChairLeatherColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = generalColors[i].Name, - Alias = generalColors[i].Name.Replace(" ", "-").ToLower(), - DisplayOrder = i + 1, - Quantity = 1, - Color = generalColors[i].Color, - IsPreSelected = (generalColors[i].Name == "Tomato red") - }); - } - entities.Add(attrBallChairLeatherColor); - - #endregion - - #region Furniture - Lounge chair - - var productLoungeChair = _ctx.Set().First(x => x.Sku == "Furniture-lounge-chair"); - - var attrLoungeChairMaterial = new ProductVariantAttribute - { - Product = productLoungeChair, - ProductAttribute = attrMaterial, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.DropdownList - }; - attrLoungeChairMaterial.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "Leather Special", - Alias = "leather-special", - DisplayOrder = 1, - Quantity = 1, - IsPreSelected = true - }); - attrLoungeChairMaterial.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "Leather Aniline", - Alias = "leather-aniline", - DisplayOrder = 2, - Quantity = 1 - }); - entities.Add(attrLoungeChairMaterial); - - var loungeChairSeatShells = new string[] { "Palisander", "Cherry", "Walnut", "Wooden black lacquered" }; - var attrLoungeChairSeatShell = new ProductVariantAttribute - { - Product = productLoungeChair, - ProductAttribute = attrSeatShell, - IsRequired = true, - DisplayOrder = 2, - AttributeControlType = AttributeControlType.DropdownList - }; - - for (var i = 0; i < loungeChairSeatShells.Length; ++i) - { - attrLoungeChairSeatShell.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = loungeChairSeatShells[i], - Alias = loungeChairSeatShells[i].Replace(" ", "-").ToLower(), - DisplayOrder = i + 1, - Quantity = 1, - IsPreSelected = (i == 0), - PriceAdjustment = (loungeChairSeatShells[i] == "Wooden black lacquered" ? 100.00M : decimal.Zero) - }); - } - entities.Add(attrLoungeChairSeatShell); - - var attrLoungeChairBase = new ProductVariantAttribute - { - Product = productLoungeChair, - ProductAttribute = attrBase, - IsRequired = true, - DisplayOrder = 3, - AttributeControlType = AttributeControlType.DropdownList - }; - attrLoungeChairBase.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "Top edge polished", - Alias = "top-edge-polished", - DisplayOrder = 1, - Quantity = 1, - IsPreSelected = true - }); - attrLoungeChairBase.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "Completely polished", - Alias = "completely-polished", - DisplayOrder = 2, - Quantity = 1, - PriceAdjustment = 150.00M - }); - entities.Add(attrLoungeChairBase); - - var attrLoungeChairLeatherColor = new ProductVariantAttribute - { - Product = productLoungeChair, - ProductAttribute = attrLeatherColor, - IsRequired = true, - DisplayOrder = 4, - AttributeControlType = AttributeControlType.Boxes - }; - - for (var i = 0; i < generalColors.Length; ++i) - { - attrLoungeChairLeatherColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = generalColors[i].Name, - Alias = generalColors[i].Name.Replace(" ", "-").ToLower(), - DisplayOrder = i + 1, - Quantity = 1, - Color = generalColors[i].Color, - IsPreSelected = (generalColors[i].Name == "White") - }); - } - entities.Add(attrLoungeChairLeatherColor); - - #endregion - - #region Furniture - Cube chair - - var productCubeChair = _ctx.Set().First(x => x.Sku == "Furniture-cube-chair"); - - var attrCubeChairMaterial = new ProductVariantAttribute - { - Product = productCubeChair, - ProductAttribute = attrMaterial, - IsRequired = true, - DisplayOrder = 1, - AttributeControlType = AttributeControlType.DropdownList - }; - attrCubeChairMaterial.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "Leather Special", - Alias = "leather-special", - DisplayOrder = 1, - Quantity = 1, - IsPreSelected = true - }); - attrCubeChairMaterial.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = "Leather Aniline", - Alias = "leather-aniline", - DisplayOrder = 2, - Quantity = 1, - PriceAdjustment = 400.00M - }); - entities.Add(attrCubeChairMaterial); - - var attrCubeChairLeatherColor = new ProductVariantAttribute - { - Product = productCubeChair, - ProductAttribute = attrLeatherColor, - IsRequired = true, - DisplayOrder = 2, - AttributeControlType = AttributeControlType.Boxes - }; - - for (var i = 0; i < generalColors.Length; ++i) - { - attrCubeChairLeatherColor.ProductVariantAttributeValues.Add(new ProductVariantAttributeValue - { - Name = generalColors[i].Name, - Alias = generalColors[i].Name.Replace(" ", "-").ToLower(), - DisplayOrder = i + 1, - Quantity = 1, - Color = generalColors[i].Color, - IsPreSelected = (generalColors[i].Name == "Black") - }); - } - entities.Add(attrCubeChairLeatherColor); - - #endregion - - this.Alter(entities); - return entities; - } - - public IList ProductVariantAttributeCombinations() - { - var sb = new StringBuilder(); - var entities = new List(); - var attrColor = _ctx.Set().First(x => x.Alias == "color"); - var attrController = _ctx.Set().First(x => x.Alias == "controller"); - var attrSize = _ctx.Set().First(x => x.Alias == "size"); - var attrMemoryCapacity = _ctx.Set().First(x => x.Alias == "memory-capacity"); - var attrColorIphoneColors = _ctx.Set().First(x => x.Alias == "iphone-color"); - var attr97iPadColors = _ctx.Set().First(x => x.Alias == "ipad-color"); - var attrPlate = _ctx.Set().First(x => x.Alias == "plate"); - var attrPlateThickness = _ctx.Set().First(x => x.Alias == "plate-thickness"); - var attrMaterial = _ctx.Set().First(x => x.Alias == "material"); - var attrLeatherColor = _ctx.Set().First(x => x.Alias == "leather-color"); - var attrSeatShell = _ctx.Set().First(x => x.Alias == "seat-shell"); - var attrBase = _ctx.Set().First(x => x.Alias == "base"); - var attrFlakLenstype = _ctx.Set().First(x => x.Alias == "lenstype"); - var attrFlakFramecolor = _ctx.Set().First(x => x.Alias == "framecolor"); - var attrFlakLenscolor = _ctx.Set().First(x => x.Alias == "lenscolor"); - var attrWayfarerLenscolor = _ctx.Set().First(x => x.Alias == "wayfarerlenscolor"); - var attrWayfarerFramecolor = _ctx.Set().First(x => x.Alias == "wayfarerframecolor"); - - - #region ORIGINAL WAYFARER AT COLLECTION - - var productWayfarer = _ctx.Set().First(x => x.Sku == "P-3003"); - var wayfarerPictureIds = productWayfarer.ProductPictures.Select(pp => pp.PictureId).ToList(); - var picturesWayfarer = _ctx.Set().Where(x => wayfarerPictureIds.Contains(x.Id)).ToList(); - - //var attributeColorIphone7Plus = _ctx.Set().First(x => x.ProductId == productIphone7Plus.Id && x.ProductAttributeId == attrColor.Id); - - var wayfarerLenscolor = _ctx.Set().First(x => x.ProductId == productWayfarer.Id && x.ProductAttributeId == attrWayfarerLenscolor.Id); - var wayfarerLenscolorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == wayfarerLenscolor.Id).ToList(); - - var wayfarerFramecolor = _ctx.Set().First(x => x.ProductId == productWayfarer.Id && x.ProductAttributeId == attrWayfarerFramecolor.Id); - var wayfarerFramecolorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == wayfarerFramecolor.Id).ToList(); - - #region blue-gray-classic-black - entities.Add(new ProductVariantAttributeCombination() - { - Product = productWayfarer, - Sku = productWayfarer.Sku + "_blue-gray-classic-black", - - AttributesXml = FormatAttributeXml( - wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "blue-gray-classic").Id, - wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "rayban-black").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - //Price = 299M, - AssignedPictureIds = picturesWayfarer.First(x => x.SeoFilename == "wayfarer-blue-gray-classic-black-1").Id.ToString() - }); - - #endregion blue-gray-classic-black - - #region gray-course-black - entities.Add(new ProductVariantAttributeCombination() - { - Product = productWayfarer, - Sku = productWayfarer.Sku + "_gray-course-black", - - AttributesXml = FormatAttributeXml( - wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "gray-course").Id, - wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "rayban-black").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - //Price = 299M, - AssignedPictureIds = picturesWayfarer.First(x => x.SeoFilename == "wayfarer-gray-course-black").Id.ToString() - }); - - #endregion gray-course-black - - #region brown-course-havana - entities.Add(new ProductVariantAttributeCombination() - { - Product = productWayfarer, - Sku = productWayfarer.Sku + "_brown-course-havana", - - AttributesXml = FormatAttributeXml( - wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "brown-course").Id, - wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - //Price = 299M, - AssignedPictureIds = picturesWayfarer.First(x => x.SeoFilename == "wayfarer-brown-course-havana").Id.ToString() - }); - - #endregion brown-course-havana - - #region green-classic-havana-black - entities.Add(new ProductVariantAttributeCombination() - { - Product = productWayfarer, - Sku = productWayfarer.Sku + "_green-classic-havana-black", - - AttributesXml = FormatAttributeXml( - wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "green-classic").Id, - wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana-black").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - //Price = 299M, - AssignedPictureIds = picturesWayfarer.First(x => x.SeoFilename == "wayfarer-green-classic-havana-black").Id.ToString() - }); - - #endregion green-classic-havana-black - - // not available products not available products not available products not available products not available products - - #region blue-gray-classic-havana-black - entities.Add(new ProductVariantAttributeCombination() - { - Product = productWayfarer, - Sku = productWayfarer.Sku + "_blue-gray-classic-havana-black", - - AttributesXml = FormatAttributeXml( - wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "blue-gray-classic").Id, - wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana-black").Id), - - StockQuantity = 0, - AllowOutOfStockOrders = true, - IsActive = false, - //Price = 299M, - AssignedPictureIds = picturesWayfarer.First(x => x.SeoFilename == "wayfarer-blue-gray-classic-black-1").Id.ToString() - }); - - #endregion green-classic-havana-black - - #region blue-gray-classic-havana - entities.Add(new ProductVariantAttributeCombination() - { - Product = productWayfarer, - Sku = productWayfarer.Sku + "_blue-gray-classic-havana", - - AttributesXml = FormatAttributeXml( - wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "blue-gray-classic").Id, - wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana").Id), - - StockQuantity = 0, - AllowOutOfStockOrders = true, - IsActive = false, - //Price = 299M, - AssignedPictureIds = picturesWayfarer.First(x => x.SeoFilename == "wayfarer-blue-gray-classic-black-1").Id.ToString() - }); - - #endregion green-classic-rayban-black - - // gray-course - #region gray-course-havana-black - entities.Add(new ProductVariantAttributeCombination() - { - Product = productWayfarer, - Sku = productWayfarer.Sku + "_gray-course-havana-black", - - AttributesXml = FormatAttributeXml( - wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "gray-course").Id, - wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana-black").Id), - - StockQuantity = 0, - AllowOutOfStockOrders = true, - IsActive = true, - //Price = 299M, - AssignedPictureIds = picturesWayfarer.First(x => x.SeoFilename == "wayfarer-gray-course-black").Id.ToString() - }); - - #endregion gray-course-havana-black - - #region gray-course-havana - entities.Add(new ProductVariantAttributeCombination() - { - Product = productWayfarer, - Sku = productWayfarer.Sku + "_gray-course-havana", - - AttributesXml = FormatAttributeXml( - wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "gray-course").Id, - wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana").Id), - - StockQuantity = 0, - AllowOutOfStockOrders = true, - IsActive = false, - //Price = 299M, - AssignedPictureIds = picturesWayfarer.First(x => x.SeoFilename == "wayfarer-gray-course-black").Id.ToString() - }); - - #endregion gray-course-rayban-black - - #region green-classic-rayban-black - entities.Add(new ProductVariantAttributeCombination() - { - Product = productWayfarer, - Sku = productWayfarer.Sku + "_green-classic-rayban-black", - - AttributesXml = FormatAttributeXml( - wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "green-classic").Id, - wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "rayban-black").Id), - - StockQuantity = 0, - AllowOutOfStockOrders = true, - IsActive = false, - //Price = 299M, - AssignedPictureIds = picturesWayfarer.First(x => x.SeoFilename == "wayfarer-green-classic-havana-black").Id.ToString() - }); - - #endregion green-classic-rayban-black - - #region green-classic-havana - entities.Add(new ProductVariantAttributeCombination() - { - Product = productWayfarer, - Sku = productWayfarer.Sku + "_green-classic-havana", - - AttributesXml = FormatAttributeXml( - wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "green-classic").Id, - wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana").Id), - - StockQuantity = 0, - AllowOutOfStockOrders = true, - IsActive = false, - //Price = 299M, - AssignedPictureIds = picturesWayfarer.First(x => x.SeoFilename == "wayfarer-green-classic-havana-black").Id.ToString() - }); - - #endregion gray-course-rayban-black - - // brown-course - #region brown-course-havana-black - entities.Add(new ProductVariantAttributeCombination() - { - Product = productWayfarer, - Sku = productWayfarer.Sku + "_brown-course-havana-black", - - AttributesXml = FormatAttributeXml( - wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "brown-course").Id, - wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "havana-black").Id), - - StockQuantity = 0, - AllowOutOfStockOrders = true, - IsActive = false, - //Price = 299M, - AssignedPictureIds = picturesWayfarer.First(x => x.SeoFilename == "wayfarer-brown-course-havana").Id.ToString() - }); - - #endregion brown-course-havana-black - - #region brown-course-rayban-black - entities.Add(new ProductVariantAttributeCombination() - { - Product = productWayfarer, - Sku = productWayfarer.Sku + "_brown-course-rayban-black", - - AttributesXml = FormatAttributeXml( - wayfarerLenscolor.Id, wayfarerLenscolorValues.First(x => x.Alias == "brown-course").Id, - wayfarerFramecolor.Id, wayfarerFramecolorValues.First(x => x.Alias == "rayban-black").Id), - - StockQuantity = 0, - AllowOutOfStockOrders = true, - IsActive = false, - //Price = 299M, - AssignedPictureIds = picturesWayfarer.First(x => x.SeoFilename == "wayfarer-brown-course-havana").Id.ToString() - }); - - #endregion brown-course-rayban-black - - #endregion ORIGINAL WAYFARER AT COLLECTION - - #region Custom Flak - - var productFlak = _ctx.Set().First(x => x.Sku == "P-3002"); - var flakPictureIds = productFlak.ProductPictures.Select(pp => pp.PictureId).ToList(); - var picturesFlak = _ctx.Set().Where(x => flakPictureIds.Contains(x.Id)).ToList(); - - //var attributeColorIphone7Plus = _ctx.Set().First(x => x.ProductId == productIphone7Plus.Id && x.ProductAttributeId == attrColor.Id); - - var flakLenscolor = _ctx.Set().First(x => x.ProductId == productFlak.Id && x.ProductAttributeId == attrFlakLenscolor.Id); - var flakLenscolorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == flakLenscolor.Id).ToList(); - - var flakLenstype = _ctx.Set().First(x => x.ProductId == productFlak.Id && x.ProductAttributeId == attrFlakLenstype.Id); - var flakLenstypeValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == flakLenstype.Id).ToList(); - - var flakFramecolor = _ctx.Set().First(x => x.ProductId == productFlak.Id && x.ProductAttributeId == attrFlakFramecolor.Id); - var flakFramecolorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == flakFramecolor.Id).ToList(); - - //#region matteblack-gray-standard - - foreach (var lenscolorValue in flakLenscolorValues) - { - foreach (var framecolorValue in flakFramecolorValues) + Name = "Clear cache", + CronExpression = "0 */12 * * *", // Every 12 hours + Type = "SmartStore.Services.Caching.ClearCacheTask, SmartStore.Services", + Enabled = false, + StopOnError = false, + }, + new ScheduleTask { - - foreach (var lenstypeValue in flakLenstypeValues) - { - try { - entities.Add(new ProductVariantAttributeCombination - { - Product = productFlak, - Sku = productFlak.Sku + string.Concat("-", framecolorValue.Alias, "-", lenscolorValue.Alias, "-",lenstypeValue.Alias), - AttributesXml = FormatAttributeXml(flakLenscolor.Id, lenscolorValue.Id, flakLenstype.Id, lenstypeValue.Id, flakFramecolor.Id, framecolorValue.Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - - AssignedPictureIds = picturesFlak.First(x => x.SeoFilename.Contains(framecolorValue.Alias + "_" + lenscolorValue.Alias)).Id.ToString(), - - //Price = ballChairPrice - }); - } - catch - { - Console.WriteLine("An error occurred: '{0}'", framecolorValue.Alias + "_" + lenscolorValue.Alias); - } - } - } - } - - - - - #endregion Custom Flak - - #region ps3 - - var productPs3 = _ctx.Set().First(x => x.Sku == "Sony-PS399000"); - var ps3PictureIds = productPs3.ProductPictures.Select(pp => pp.PictureId).ToList(); - var picturesPs3 = _ctx.Set().Where(x => ps3PictureIds.Contains(x.Id)).ToList(); - - var productAttributeColor = _ctx.Set().First(x => x.ProductId == productPs3.Id && x.ProductAttributeId == attrController.Id); - var attributeColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == productAttributeColor.Id).ToList(); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productPs3, - Sku = productPs3.Sku + "-B", - AttributesXml = FormatAttributeXml(productAttributeColor.Id, attributeColorValues.First(x => x.Alias == "with_controller").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesPs3.First(x => x.SeoFilename.EndsWith("-controller")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productPs3, - Sku = productPs3.Sku + "-W", - AttributesXml = FormatAttributeXml(productAttributeColor.Id, attributeColorValues.First(x => x.Alias == "without_controller").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesPs3.First(x => x.SeoFilename.EndsWith("-single")).Id.ToString() - }); - - #endregion ps3 - - #region Apple Airpod - - var productAirpod = _ctx.Set().First(x => x.Sku == "P-2003"); - var airpodPictureIds = productAirpod.ProductPictures.Select(pp => pp.PictureId).ToList(); - var picturesAirpod = _ctx.Set().Where(x => airpodPictureIds.Contains(x.Id)).ToList(); - - var airpodAttributeColor = _ctx.Set().First(x => x.ProductId == productAirpod.Id && x.ProductAttributeId == attrColor.Id); - var airpodAttributeColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == airpodAttributeColor.Id).ToList(); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productAirpod, - Sku = productAirpod.Sku + "-gold", - AttributesXml = FormatAttributeXml(airpodAttributeColor.Id, airpodAttributeColorValues.First(x => x.Alias == "gold").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesAirpod.First(x => x.SeoFilename.EndsWith("-gold")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productAirpod, - Sku = productAirpod.Sku + "-rose", - AttributesXml = FormatAttributeXml(airpodAttributeColor.Id, airpodAttributeColorValues.First(x => x.Alias == "rose").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesAirpod.First(x => x.SeoFilename.EndsWith("-rose")).Id.ToString() - }); - entities.Add(new ProductVariantAttributeCombination() - { - Product = productAirpod, - Sku = productAirpod.Sku + "-mint", - AttributesXml = FormatAttributeXml(airpodAttributeColor.Id, airpodAttributeColorValues.First(x => x.Alias == "mint").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesAirpod.First(x => x.SeoFilename.EndsWith("-mint")).Id.ToString() - }); - entities.Add(new ProductVariantAttributeCombination() - { - Product = productAirpod, - Sku = productAirpod.Sku + "-lightblue", - AttributesXml = FormatAttributeXml(airpodAttributeColor.Id, airpodAttributeColorValues.First(x => x.Alias == "lightblue").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesAirpod.First(x => x.SeoFilename.EndsWith("-lightblue")).Id.ToString() - }); - entities.Add(new ProductVariantAttributeCombination() - { - Product = productAirpod, - Sku = productAirpod.Sku + "-turquoise", - AttributesXml = FormatAttributeXml(airpodAttributeColor.Id, airpodAttributeColorValues.First(x => x.Alias == "turquoise").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesAirpod.First(x => x.SeoFilename.EndsWith("-turquoise")).Id.ToString() - }); - entities.Add(new ProductVariantAttributeCombination() - { - Product = productAirpod, - Sku = productAirpod.Sku + "-white", - AttributesXml = FormatAttributeXml(airpodAttributeColor.Id, airpodAttributeColorValues.First(x => x.Alias == "white").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesAirpod.First(x => x.SeoFilename.EndsWith("-white")).Id.ToString() - }); - - #endregion Apple Airpod - - #region 9,7 Ipad - - var productiPad97 = _ctx.Set().First(x => x.Sku == "P-2004"); - var iPad97PictureIds = productiPad97.ProductPictures.Select(pp => pp.PictureId).ToList(); - var picturesiPad97 = _ctx.Set().Where(x => iPad97PictureIds.Contains(x.Id)).ToList(); - - //var attributeColorIphone7Plus = _ctx.Set().First(x => x.ProductId == productIphone7Plus.Id && x.ProductAttributeId == attrColor.Id); - - var iPad97Color = _ctx.Set().First(x => x.ProductId == productiPad97.Id && x.ProductAttributeId == attr97iPadColors.Id); - var iPad97ColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == iPad97Color.Id).ToList(); - - var ipad97Capacity = _ctx.Set().First(x => x.ProductId == productiPad97.Id && x.ProductAttributeId == attrMemoryCapacity.Id); - var iPad97CapacityValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == ipad97Capacity.Id).ToList(); - - #region silver - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "-silver-64gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "silver").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - Price = 299M, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-silver")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "silver-128gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "silver").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-silver")).Id.ToString() - }); - - #endregion silver - - #region gold - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "-gold-64gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "gold").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), - Price = 279M, - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-gold")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "gold-128gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "gold").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-gold")).Id.ToString() - }); - #endregion gold - - #region spacegray - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "-spacegray-64gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "spacegray").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-spacegray")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "spacegray-128gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "spacegray").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-spacegray")).Id.ToString() - }); - #endregion spacegray - - #region rose - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "-rose-64gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "rose").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-rose")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "rose-128gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "rose").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-rose")).Id.ToString() - }); - #endregion rose - - #region mint - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "-mint-64gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "mint").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-mint")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "mint-128gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "mint").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-mint")).Id.ToString() - }); - #endregion mint - - #region purple - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "-purple-64gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "purple").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-purple")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "purple-128gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "purple").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-purple")).Id.ToString() - }); - #endregion purple - - #region lightblue - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "-lightblue-64gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "lightblue").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-lightblue")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "lightblue-128gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "lightblue").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-lightblue")).Id.ToString() - }); - #endregion lightblue - - #region yellow - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "-yellow-64gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "yellow").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-yellow")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "yellow-128gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "yellow").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-yellow")).Id.ToString() - }); - #endregion yellow - - #region turquoise - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "-turquoise-64gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "turquoise").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "64gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-turquoise")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productiPad97, - Sku = productiPad97.Sku + "turquoise-128gb", - - AttributesXml = FormatAttributeXml( - iPad97Color.Id, iPad97ColorValues.First(x => x.Alias == "turquoise").Id, - ipad97Capacity.Id, iPad97CapacityValues.First(x => x.Alias == "128gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesiPad97.First(x => x.SeoFilename.EndsWith("-turquoise")).Id.ToString() - }); - #endregion turquoise - - #endregion 9,7 Ipad - - #region Iphone 7 plus - - var productIphone7Plus = _ctx.Set().First(x => x.Sku == "P-2001"); - var Iphone7PlusPictureIds = productIphone7Plus.ProductPictures.Select(pp => pp.PictureId).ToList(); - var picturesIphone7Plus = _ctx.Set().Where(x => Iphone7PlusPictureIds.Contains(x.Id)).ToList(); - - //var attributeColorIphone7Plus = _ctx.Set().First(x => x.ProductId == productIphone7Plus.Id && x.ProductAttributeId == attrColor.Id); - - var Iphone7PlusColor = _ctx.Set().First(x => x.ProductId == productIphone7Plus.Id && x.ProductAttributeId == attrColorIphoneColors.Id); - var Iphone7PlusColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == Iphone7PlusColor.Id).ToList(); - - var Iphone7PlusCapacity = _ctx.Set().First(x => x.ProductId == productIphone7Plus.Id && x.ProductAttributeId == attrMemoryCapacity.Id); - var Iphone7PlusCapacityValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == Iphone7PlusCapacity.Id).ToList(); - - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productIphone7Plus, - Sku = productIphone7Plus.Sku + "-black-64gb", - - AttributesXml = FormatAttributeXml( - Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "black").Id, - Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "64gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesIphone7Plus.First(x => x.SeoFilename.EndsWith("-black")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productIphone7Plus, - Sku = productIphone7Plus.Sku + "-black-128gb", - - AttributesXml = FormatAttributeXml( - Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "black").Id, - Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "128gb").Id), - - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesIphone7Plus.First(x => x.SeoFilename.EndsWith("-black")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productIphone7Plus, - Sku = productIphone7Plus.Sku + "-red-64", - AttributesXml = FormatAttributeXml( - Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "red").Id, - Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "64gb").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesIphone7Plus.First(x => x.SeoFilename.EndsWith("-red")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productIphone7Plus, - Sku = productIphone7Plus.Sku + "-red-128", - AttributesXml = FormatAttributeXml( - Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "red").Id, - Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "128gb").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesIphone7Plus.First(x => x.SeoFilename.EndsWith("-red")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productIphone7Plus, - Sku = productIphone7Plus.Sku + "-silver-64", - AttributesXml = FormatAttributeXml( - Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "silver").Id, - Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "64gb").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesIphone7Plus.First(x => x.SeoFilename.EndsWith("-silver")).Id.ToString() - }); - - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productIphone7Plus, - Sku = productIphone7Plus.Sku + "-silver-128", - AttributesXml = FormatAttributeXml( - Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "silver").Id, - Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "128gb").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesIphone7Plus.First(x => x.SeoFilename.EndsWith("-silver")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productIphone7Plus, - Sku = productIphone7Plus.Sku + "-rose-64", - AttributesXml = FormatAttributeXml( - Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "rose").Id, - Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "64gb").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesIphone7Plus.First(x => x.SeoFilename.EndsWith("-rose")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productIphone7Plus, - Sku = productIphone7Plus.Sku + "-rose-128", - AttributesXml = FormatAttributeXml( - Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "rose").Id, - Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "128gb").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesIphone7Plus.First(x => x.SeoFilename.EndsWith("-rose")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productIphone7Plus, - Sku = productIphone7Plus.Sku + "-gold-64", - AttributesXml = FormatAttributeXml( - Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "gold").Id, - Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "64gb").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesIphone7Plus.First(x => x.SeoFilename.EndsWith("-gold")).Id.ToString() - }); - - entities.Add(new ProductVariantAttributeCombination() - { - Product = productIphone7Plus, - Sku = productIphone7Plus.Sku + "-gold-128", - AttributesXml = FormatAttributeXml( - Iphone7PlusColor.Id, Iphone7PlusColorValues.First(x => x.Alias == "gold").Id, - Iphone7PlusCapacity.Id, Iphone7PlusCapacityValues.First(x => x.Alias == "128gb").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = picturesIphone7Plus.First(x => x.SeoFilename.EndsWith("-gold")).Id.ToString() - }); - - #endregion Iphone 7 plus - - #region Fashion - Converse All Star - - var productAllStar = _ctx.Set().First(x => x.Sku == "Fashion-112355"); - var allStarPictureIds = productAllStar.ProductPictures.Select(x => x.PictureId).ToList(); - var allStarPictures = _ctx.Set().Where(x => allStarPictureIds.Contains(x.Id)).ToList(); - - var allStarColor = _ctx.Set().First(x => x.ProductId == productAllStar.Id && x.ProductAttributeId == attrColor.Id); - var allStarColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == allStarColor.Id).ToList(); - - var allStarSize = _ctx.Set().First(x => x.ProductId == productAllStar.Id && x.ProductAttributeId == attrSize.Id); - var allStarSizeValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == allStarSize.Id).ToList(); - - var allStarCombinations = new[] - { - new { Color = "Charcoal", Size = "42" }, - new { Color = "Charcoal", Size = "43" }, - new { Color = "Charcoal", Size = "44" }, - new { Color = "Maroon", Size = "42" }, - new { Color = "Maroon", Size = "43" }, - new { Color = "Maroon", Size = "44" }, - new { Color = "Navy", Size = "42" }, - new { Color = "Navy", Size = "43" }, - new { Color = "Navy", Size = "44" }, - new { Color = "Purple", Size = "42" }, - new { Color = "Purple", Size = "43" }, - new { Color = "Purple", Size = "44" }, - new { Color = "White", Size = "42" }, - new { Color = "White", Size = "43" }, - new { Color = "White", Size = "44" }, - }; - - foreach (var comb in allStarCombinations) - { - var lowerColor = comb.Color.ToLower(); - entities.Add(new ProductVariantAttributeCombination - { - Product = productAllStar, - Sku = productAllStar.Sku + string.Concat("-", lowerColor, "-", comb.Size), - AttributesXml = FormatAttributeXml( - allStarColor.Id, allStarColorValues.First(x => x.Alias == lowerColor).Id, - allStarSize.Id, allStarSizeValues.First(x => x.Alias == comb.Size).Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = allStarPictures.First(x => x.SeoFilename.EndsWith(lowerColor)).Id.ToString() - }); - } - - #endregion - - #region Fashion - Shirt Meccanica - - var productShirtMeccanica = _ctx.Set().First(x => x.Sku == "Fashion-987693502"); - var shirtMeccanicaPictureIds = productShirtMeccanica.ProductPictures.Select(x => x.PictureId).ToList(); - var shirtMeccanicaPictures = _ctx.Set().Where(x => shirtMeccanicaPictureIds.Contains(x.Id)).ToList(); - - var shirtMeccanicaColor = _ctx.Set().First(x => x.ProductId == productShirtMeccanica.Id && x.ProductAttributeId == attrColor.Id); - var shirtMeccanicaColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == shirtMeccanicaColor.Id).ToList(); - - var shirtMeccanicaSize = _ctx.Set().First(x => x.ProductId == productShirtMeccanica.Id && x.ProductAttributeId == attrSize.Id); - var shirtMeccanicaSizeValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == shirtMeccanicaSize.Id).ToList(); - - var shirtMeccanicaCombinations = new[] - { - new { Color = "Red", Size = "XS" }, - new { Color = "Red", Size = "S" }, - new { Color = "Red", Size = "M" }, - new { Color = "Red", Size = "L" }, - new { Color = "Red", Size = "XL" }, - new { Color = "Black", Size = "XS" }, - new { Color = "Black", Size = "S" }, - new { Color = "Black", Size = "M" }, - new { Color = "Black", Size = "L" }, - new { Color = "Black", Size = "XL" } - }; - - foreach (var comb in shirtMeccanicaCombinations) - { - var lowerColor = comb.Color.ToLower(); - var lowerSize = comb.Size.ToLower(); - var pictureIds = shirtMeccanicaPictures.Where(x => x.SeoFilename.Contains($"_{lowerColor}_")).Select(x => x.Id); - - entities.Add(new ProductVariantAttributeCombination - { - Product = productShirtMeccanica, - Sku = productShirtMeccanica.Sku + string.Concat("-", lowerColor, "-", lowerSize), - AttributesXml = FormatAttributeXml( - shirtMeccanicaColor.Id, shirtMeccanicaColorValues.First(x => x.Alias == lowerColor).Id, - shirtMeccanicaSize.Id, shirtMeccanicaSizeValues.First(x => x.Alias == lowerSize).Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = string.Join(",", pictureIds) - }); - } - - #endregion - - #region Fashion - Ladies Jacket - - var productLadiesJacket = _ctx.Set().First(x => x.Sku == "Fashion-JN1107"); - var ladiesJacketPictureIds = productLadiesJacket.ProductPictures.Select(x => x.PictureId).ToList(); - var ladiesJacketPictures = _ctx.Set().Where(x => ladiesJacketPictureIds.Contains(x.Id)).ToList(); - - var ladiesJacketColor = _ctx.Set().First(x => x.ProductId == productLadiesJacket.Id && x.ProductAttributeId == attrColor.Id); - var ladiesJacketColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == ladiesJacketColor.Id).ToList(); - - var ladiesJacketSize = _ctx.Set().First(x => x.ProductId == productLadiesJacket.Id && x.ProductAttributeId == attrSize.Id); - var ladiesJacketSizeValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == ladiesJacketSize.Id).ToList(); - - var ladiesJacketCombinations = new[] - { - new { Color = "Red", Size = "XS" }, - new { Color = "Red", Size = "S" }, - new { Color = "Red", Size = "M" }, - new { Color = "Red", Size = "L" }, - new { Color = "Red", Size = "XL" }, - new { Color = "Orange", Size = "XS" }, - new { Color = "Orange", Size = "S" }, - new { Color = "Orange", Size = "M" }, - new { Color = "Orange", Size = "L" }, - new { Color = "Orange", Size = "XL" }, - new { Color = "Green", Size = "XS" }, - new { Color = "Green", Size = "S" }, - new { Color = "Green", Size = "M" }, - new { Color = "Green", Size = "L" }, - new { Color = "Green", Size = "XL" }, - new { Color = "Blue", Size = "XS" }, - new { Color = "Blue", Size = "S" }, - new { Color = "Blue", Size = "M" }, - new { Color = "Blue", Size = "L" }, - new { Color = "Blue", Size = "XL" }, - new { Color = "Navy", Size = "XS" }, - new { Color = "Navy", Size = "S" }, - new { Color = "Navy", Size = "M" }, - new { Color = "Navy", Size = "L" }, - new { Color = "Navy", Size = "XL" }, - new { Color = "Silver", Size = "XS" }, - new { Color = "Silver", Size = "S" }, - new { Color = "Silver", Size = "M" }, - new { Color = "Silver", Size = "L" }, - new { Color = "Silver", Size = "XL" }, - new { Color = "Black", Size = "XS" }, - new { Color = "Black", Size = "S" }, - new { Color = "Black", Size = "M" }, - new { Color = "Black", Size = "L" }, - new { Color = "Black", Size = "XL" } - }; - - foreach (var comb in ladiesJacketCombinations) - { - var lowerColor = comb.Color.ToLower(); - var lowerSize = comb.Size.ToLower(); - - entities.Add(new ProductVariantAttributeCombination - { - Product = productLadiesJacket, - Sku = productLadiesJacket.Sku + string.Concat("-", lowerColor, "-", lowerSize), - AttributesXml = FormatAttributeXml( - ladiesJacketColor.Id, ladiesJacketColorValues.First(x => x.Alias == lowerColor).Id, - ladiesJacketSize.Id, ladiesJacketSizeValues.First(x => x.Alias == lowerSize).Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = ladiesJacketPictures.First(x => x.SeoFilename.EndsWith(lowerColor)).Id.ToString() - }); - } - - #endregion - - #region Furniture - Le Corbusier LC 6 table - - var productCorbusierTable = _ctx.Set().First(x => x.Sku == "Furniture-lc6"); - - var corbusierTablePlate = _ctx.Set().First(x => x.ProductId == productCorbusierTable.Id && x.ProductAttributeId == attrPlate.Id); - var corbusierTablePlateValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == corbusierTablePlate.Id).ToList(); - - var corbusierTablePlateThickness = _ctx.Set().First(x => x.ProductId == productCorbusierTable.Id && x.ProductAttributeId == attrPlateThickness.Id); - var corbusierTablePlateThicknessValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == corbusierTablePlateThickness.Id).ToList(); - - entities.Add(new ProductVariantAttributeCombination - { - Product = productCorbusierTable, - Sku = productCorbusierTable.Sku + "-clear-15", - AttributesXml = FormatAttributeXml( - corbusierTablePlate.Id, corbusierTablePlateValues.First(x => x.Alias == "clear-glass").Id, - corbusierTablePlateThickness.Id, corbusierTablePlateThicknessValues.First(x => x.Alias == "15mm").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - Price = 749.00M - }); - entities.Add(new ProductVariantAttributeCombination - { - Product = productCorbusierTable, - Sku = productCorbusierTable.Sku + "-clear-19", - AttributesXml = FormatAttributeXml( - corbusierTablePlate.Id, corbusierTablePlateValues.First(x => x.Alias == "clear-glass").Id, - corbusierTablePlateThickness.Id, corbusierTablePlateThicknessValues.First(x => x.Alias == "19mm").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - Price = 899.00M - }); - entities.Add(new ProductVariantAttributeCombination - { - Product = productCorbusierTable, - Sku = productCorbusierTable.Sku + "-sandblasted-15", - AttributesXml = FormatAttributeXml( - corbusierTablePlate.Id, corbusierTablePlateValues.First(x => x.Alias == "sandblasted-glass").Id, - corbusierTablePlateThickness.Id, corbusierTablePlateThicknessValues.First(x => x.Alias == "15mm").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - Price = 849.00M - }); - entities.Add(new ProductVariantAttributeCombination - { - Product = productCorbusierTable, - Sku = productCorbusierTable.Sku + "-sandblasted-19", - AttributesXml = FormatAttributeXml( - corbusierTablePlate.Id, corbusierTablePlateValues.First(x => x.Alias == "sandblasted-glass").Id, - corbusierTablePlateThickness.Id, corbusierTablePlateThicknessValues.First(x => x.Alias == "19mm").Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - Price = 999.00M - }); - - #endregion - - #region Soccer Adidas TANGO SALA BALL - - var productAdidasTANGOSALABALL = _ctx.Set().First(x => x.Sku == "P-5001"); - var adidasTANGOSALABALLPictureIds = productAdidasTANGOSALABALL.ProductPictures.Select(x => x.PictureId).ToList(); - var adidasTANGOSALABALLJacketPictures = _ctx.Set().Where(x => adidasTANGOSALABALLPictureIds.Contains(x.Id)).ToList(); - - var adidasTANGOSALABALLColor = _ctx.Set().First(x => x.ProductId == productAdidasTANGOSALABALL.Id && x.ProductAttributeId == attrColor.Id); - var adidasTANGOSALABALLColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == adidasTANGOSALABALLColor.Id).ToList(); - - var adidasTANGOSALABALLSize = _ctx.Set().First(x => x.ProductId == productAdidasTANGOSALABALL.Id && x.ProductAttributeId == attrSize.Id); - var adidasTANGOSALABALLSizeValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == adidasTANGOSALABALLSize.Id).ToList(); - - var adidasTANGOSALABALLCombinations = new[] - { - new { Color = "Red", Size = "3" }, - new { Color = "Red", Size = "4" }, - new { Color = "Red", Size = "5" }, - - new { Color = "Yellow", Size = "3" }, - new { Color = "Yellow", Size = "4" }, - new { Color = "Yellow", Size = "5" }, - - new { Color = "Green", Size = "3" }, - new { Color = "Green", Size = "4" }, - new { Color = "Green", Size = "5" }, - - new { Color = "Blue", Size = "3" }, - new { Color = "Blue", Size = "4" }, - new { Color = "Blue", Size = "5" }, - - new { Color = "Gray", Size = "3" }, - new { Color = "Gray", Size = "4" }, - new { Color = "Gray", Size = "5" }, - - new { Color = "White", Size = "3" }, - new { Color = "White", Size = "4" }, - new { Color = "White", Size = "5" }, - - new { Color = "Brown", Size = "3" }, - new { Color = "Brown", Size = "4" }, - new { Color = "Brown", Size = "5" }, - - }; - - foreach (var comb in adidasTANGOSALABALLCombinations) - { - var lowerColor = comb.Color.ToLower(); - var lowerSize = comb.Size.ToLower(); - - entities.Add(new ProductVariantAttributeCombination + Name = "Update currency exchange rates", + CronExpression = "0 */6 * * *", // Every 6 hours + Type = "SmartStore.Services.Directory.UpdateExchangeRateTask, SmartStore.Services", + Enabled = false, + StopOnError = false, + Priority = TaskPriority.High + }, + new ScheduleTask { - Product = productAdidasTANGOSALABALL, - Sku = productAdidasTANGOSALABALL.Sku + string.Concat("-", lowerColor, "-", lowerSize), - AttributesXml = FormatAttributeXml( - adidasTANGOSALABALLColor.Id, adidasTANGOSALABALLColorValues.First(x => x.Alias == lowerColor).Id, - adidasTANGOSALABALLSize.Id, adidasTANGOSALABALLSizeValues.First(x => x.Alias == lowerSize).Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = adidasTANGOSALABALLJacketPictures.First(x => x.SeoFilename.EndsWith(lowerColor)).Id.ToString() - }); - } - - #endregion Soccer Adidas TANGO SALA BALL - - #region Soccer Torfabrik official game ball - - var productTorfabrikBall = _ctx.Set().First(x => x.Sku == "P-5002"); - var torfabrikBallPictureIds = productTorfabrikBall.ProductPictures.Select(x => x.PictureId).ToList(); - var torfabrikBallPictures = _ctx.Set().Where(x => torfabrikBallPictureIds.Contains(x.Id)).ToList(); - - var torfabrikBallColor = _ctx.Set().First(x => x.ProductId == productTorfabrikBall.Id && x.ProductAttributeId == attrColor.Id); - var torfabrikBallColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == torfabrikBallColor.Id).ToList(); - - var torfabrikBallSize = _ctx.Set().First(x => x.ProductId == productTorfabrikBall.Id && x.ProductAttributeId == attrSize.Id); - var torfabrikBallSizeValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == torfabrikBallSize.Id).ToList(); - - var torfabrikBallSizeCombinations = new[] - { - new { Color = "Red", Size = "3" }, - new { Color = "Red", Size = "4" }, - new { Color = "Red", Size = "5" }, - - new { Color = "Yellow", Size = "3" }, - new { Color = "Yellow", Size = "4" }, - new { Color = "Yellow", Size = "5" }, - - new { Color = "Green", Size = "3" }, - new { Color = "Green", Size = "4" }, - new { Color = "Green", Size = "5" }, - - new { Color = "Blue", Size = "3" }, - new { Color = "Blue", Size = "4" }, - new { Color = "Blue", Size = "5" }, - - new { Color = "White", Size = "3" }, - new { Color = "White", Size = "4" }, - new { Color = "White", Size = "5" }, - - }; - - foreach (var comb in torfabrikBallSizeCombinations) - { - var lowerColor = comb.Color.ToLower(); - var lowerSize = comb.Size.ToLower(); - - entities.Add(new ProductVariantAttributeCombination + Name = "Clear transient uploads", + CronExpression = "30 1,13 * * *", // At 01:30 and 13:30 + Type = "SmartStore.Services.Media.TransientMediaClearTask, SmartStore.Services", + Enabled = true, + StopOnError = false, + }, + new ScheduleTask { - Product = productTorfabrikBall, - Sku = productTorfabrikBall.Sku + string.Concat("-", lowerColor, "-", lowerSize), - AttributesXml = FormatAttributeXml( - torfabrikBallColor.Id, torfabrikBallColorValues.First(x => x.Alias == lowerColor).Id, - torfabrikBallSize.Id, torfabrikBallSizeValues.First(x => x.Alias == lowerSize).Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = torfabrikBallPictures.First(x => x.SeoFilename.EndsWith(lowerColor)).Id.ToString() - }); - } - - #endregion Soccer Torfabrik official game ball - - #region Furniture - Ball chair - - var productBallChair = _ctx.Set().First(x => x.Sku == "Furniture-ball-chair"); - var ballChairPictureIds = productBallChair.ProductPictures.Select(x => x.PictureId).ToList(); - var ballChairPictures = _ctx.Set().Where(x => ballChairPictureIds.Contains(x.Id)).ToList(); - - var ballChairMaterial = _ctx.Set().First(x => x.ProductId == productBallChair.Id && x.ProductAttributeId == attrMaterial.Id); - var ballChairMaterialValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == ballChairMaterial.Id).ToList(); - - var ballChairColor = _ctx.Set().First(x => x.ProductId == productBallChair.Id && x.ProductAttributeId == attrColor.Id); - var ballChairColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == ballChairColor.Id).ToList(); - - var ballChairLeatherColor = _ctx.Set().First(x => x.ProductId == productBallChair.Id && x.ProductAttributeId == attrLeatherColor.Id); - var ballChairLeatherColorValues = _ctx.Set().Where(x => x.ProductVariantAttributeId == ballChairLeatherColor.Id).ToList(); - - foreach (var materialValue in ballChairMaterialValues) - { - foreach (var colorValue in ballChairColorValues) - { - decimal ballChairPrice = 2199.00M; - - if (materialValue.Alias.StartsWith("leather-special")) - { - ballChairPrice = 2599.00M; - } - else if (materialValue.Alias.StartsWith("leather-aniline")) - { - ballChairPrice = 2999.00M; - } - - foreach (var leatherColorValue in ballChairLeatherColorValues) - { - entities.Add(new ProductVariantAttributeCombination - { - Product = productBallChair, - Sku = productBallChair.Sku + string.Concat("-", colorValue.Alias, "-", materialValue.Alias), - AttributesXml = FormatAttributeXml(ballChairMaterial.Id, materialValue.Id, ballChairColor.Id, colorValue.Id, ballChairLeatherColor.Id, leatherColorValue.Id), - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true, - AssignedPictureIds = ballChairPictures.First(x => x.SeoFilename.EndsWith(colorValue.Alias)).Id.ToString(), - Price = ballChairPrice - }); - } - } - } - - #endregion - - return entities; - } - - public IList ProductTags() - { - #region tag apple - var productTagApple = new ProductTag - { - Name = "apple" - }; - - _ctx.Set().Where(pt => pt.MetaTitle == "iPhone Plus").First().ProductTags.Add(productTagApple); - - #endregion tag apple - - #region tag gift - var productTagGift = new ProductTag - { - Name = "gift" - }; - - _ctx.Set().Where(pt => pt.MetaTitle == "$10 Virtual Gift Card").First().ProductTags.Add(productTagGift); - _ctx.Set().Where(pt => pt.MetaTitle == "$25 Virtual Gift Card").First().ProductTags.Add(productTagGift); - _ctx.Set().Where(pt => pt.MetaTitle == "$50 Virtual Gift Card").First().ProductTags.Add(productTagGift); - _ctx.Set().Where(pt => pt.MetaTitle == "$100 Virtual Gift Card").First().ProductTags.Add(productTagGift); - - #endregion tag gift - - #region tag book - var productTagBook = new ProductTag - { - Name = "book" - }; - - _ctx.Set().Where(pt => pt.MetaTitle == "Überman: The novel").First().ProductTags.Add(productTagBook); - _ctx.Set().Where(pt => pt.MetaTitle == "Best Grilling Recipes").First().ProductTags.Add(productTagBook); - _ctx.Set().Where(pt => pt.MetaTitle == "Car of superlatives").First().ProductTags.Add(productTagBook); - _ctx.Set().Where(pt => pt.MetaTitle == "Picture Atlas Motorcycles").First().ProductTags.Add(productTagBook); - _ctx.Set().Where(pt => pt.MetaTitle == "The Car Book").First().ProductTags.Add(productTagBook); - _ctx.Set().Where(pt => pt.MetaTitle == "Fast Cars").First().ProductTags.Add(productTagBook); - _ctx.Set().Where(pt => pt.MetaTitle == "Motorcycle Adventures").First().ProductTags.Add(productTagBook); - - #endregion tag book - - #region tag cooking - var productTagCooking = new ProductTag - { - Name = "cooking" - }; - - _ctx.Set().Where(pt => pt.MetaTitle == "Überman: The novel").FirstOrDefault().ProductTags.Add(productTagCooking); - _ctx.Set().Where(pt => pt.MetaTitle == "Best Grilling Recipes").FirstOrDefault().ProductTags.Add(productTagCooking); - - #endregion tag cooking - - #region tag cars - var productTagCars = new ProductTag - { - Name = "cars" - }; - - _ctx.Set().Where(pt => pt.MetaTitle == "The Car Book").FirstOrDefault().ProductTags.Add(productTagCars); - _ctx.Set().Where(pt => pt.MetaTitle == "Fast Cars").FirstOrDefault().ProductTags.Add(productTagCars); - - #endregion tag cars - - #region tag motorbikes - var productTagMotorbikes = new ProductTag - { - Name = "motorbikes" - }; - - _ctx.Set().Where(pt => pt.MetaTitle == "Fast Cars").FirstOrDefault().ProductTags.Add(productTagMotorbikes); - _ctx.Set().Where(pt => pt.MetaTitle == "Motorcycle Adventures").FirstOrDefault().ProductTags.Add(productTagMotorbikes); - - #endregion tag motorbikes - - #region tag mp3 - var productTagMP3 = new ProductTag - { - Name = "mp3" - }; - - _ctx.Set().Where(pt => pt.MetaTitle == "Antonio Vivaldi: spring").FirstOrDefault().ProductTags.Add(productTagMP3); - _ctx.Set().Where(pt => pt.MetaTitle == "Ludwig van Beethoven: Für Elise").FirstOrDefault().ProductTags.Add(productTagMP3); - - #endregion tag mp3 - - #region tag download - var productTagDownload = new ProductTag - { - Name = "download" - }; - - _ctx.Set().Where(pt => pt.MetaTitle == "Antonio Vivaldi: spring").FirstOrDefault().ProductTags.Add(productTagDownload); - _ctx.Set().Where(pt => pt.MetaTitle == "Ludwig van Beethoven: Für Elise").FirstOrDefault().ProductTags.Add(productTagDownload); - - #endregion tag download - - #region tag watches - var productTagWatches = new ProductTag - { - Name = "watches" - }; - - _ctx.Set().Where(pt => pt.MetaTitle == "Certina DS Podium Big Size").FirstOrDefault().ProductTags.Add(productTagWatches); - - #endregion tag download - - var entities = new List - { - productTagGift, productTagBook, productTagCooking, productTagCars, productTagMotorbikes, - productTagMP3, productTagDownload - }; - - this.Alter(entities); - return entities; - } - - public IList CategoriesFirstLevel() - { - var sampleImagesPath = this._sampleImagesPath; - var categoryTemplateInGridAndLines = this.CategoryTemplates().Where(pt => pt.ViewPath == "CategoryTemplate.ProductsInGridOrLines").FirstOrDefault(); - - #region category definitions - - var categoryFurniture = new Category - { - Name = "Furniture", - Alias = "Furniture", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_furniture.jpg"), "image/jpeg", GetSeName("Furniture")), - Published = true, - DisplayOrder = 1, - MetaTitle = "Furniture", - ShowOnHomePage = true - }; - - var categoryApple = new Category - { - Name = "Apple", - Alias = "Apple", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_apple.png"), "image/png", GetSeName("Apple")), - Published = true, - DisplayOrder = 1, - MetaTitle = "Apple", - ShowOnHomePage = true - }; - - var categorySports = new Category - { - Name = "Sports", - Alias = "Sports", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_sports.jpg"), "image/jpeg", GetSeName("Sports")), - Published = true, - DisplayOrder = 1, - MetaTitle = "Sports", - ShowOnHomePage = true - }; - - var categoryBooks = new Category - { - Name = "Books", - Alias = "Books", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "emblem_library.png"), "image/jpeg", GetSeName("Books")), - Published = true, - DisplayOrder = 1, - MetaTitle = "Books" - }; - - //var categoryComputers = new Category - //{ - // Name = "Computers", - // Alias = "Computers", - // CategoryTemplateId = categoryTemplateInGridAndLines.Id, - // Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_computers.png"), "image/png", GetSeName("Computers")), - // Published = true, - // DisplayOrder = 2, - // MetaTitle = "Computers" - //}; - - var categoryFashion = new Category - { - Name = "Fashion", - Alias = "Fashion", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_fashion.jpg"), "image/png", GetSeName("Fashion")), - Published = true, - DisplayOrder = 2, - MetaTitle = "Fashion", - ShowOnHomePage = true, - BadgeText = "SALE", - BadgeStyle = 4 - }; - - var categoryGaming = new Category - { - Name = "Gaming", - Alias = "Gaming", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "ps4_bundle_minecraft.jpg"), "image/png", GetSeName("Gaming")), - Published = true, - DisplayOrder = 3, - ShowOnHomePage = true, - MetaTitle = "Gaming" - }; - - //var categoryCellPhones = new Category - //{ - // Name = "Cell phones", - // Alias = "Cell phones", - // CategoryTemplateId = categoryTemplateInGridAndLines.Id, - // //ParentCategoryId = categoryElectronics.Id, - // Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_cellphone.png"), "image/png", GetSeName("Cell phones")), - // Published = true, - // DisplayOrder = 4, - // MetaTitle = "Cell phones" - //}; - - var categoryDigitalDownloads = new Category - { - Name = "Digital Products", - Alias = "Digital Products", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_digitalproducts.jpg"), "image/jpeg", GetSeName("Digital Products")), - Published = true, - DisplayOrder = 6, - MetaTitle = "Digital Products", - ShowOnHomePage = true - }; - - var categoryGiftCards = new Category - { - Name = "Gift Cards", - Alias = "Gift Cards", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_gift-cards.png"), "image/png", GetSeName("Gift Cards")), - Published = true, - DisplayOrder = 12, - MetaTitle = "Gift cards", - ShowOnHomePage = true, - }; - - var categoryWatches = new Category - { - Name = "Watches", - Alias = "Watches", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_watches.png"), "image/png", GetSeName("Watches")), - Published = true, - DisplayOrder = 10, - MetaTitle = "Watches", - ShowOnHomePage = true, - BadgeText = "%", - BadgeStyle = 5 - }; - - #endregion - - var entities = new List - { - categoryApple, categorySports, categoryBooks, categoryFurniture, categoryDigitalDownloads, categoryGaming, - categoryGiftCards, categoryFashion, categoryWatches - }; - - this.Alter(entities); - return entities; - } - - public IList CategoriesSecondLevel() - { - var sampleImagesPath = this._sampleImagesPath; - var categoryTemplateInGridAndLines = this.CategoryTemplates().Where(pt => pt.ViewPath == "CategoryTemplate.ProductsInGridOrLines").FirstOrDefault(); - - #region category definitions - - #region new - - var categoryFashionJackets = new Category - { - Name = "Jackets", - Alias = "Jackets", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_jackets.jpg"), "image/png", GetSeName("Jackets")), - Published = true, - ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Fashion").First().Id, - DisplayOrder = 1, - MetaTitle = "Jackets", - ShowOnHomePage = true - }; - - var categoryFashionLeatherJackets = new Category - { - Name = "Leather jackets", - Alias = "Leather jackets", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_leather_jackets.jpg"), "image/png", GetSeName("Leather jackets")), - Published = true, - ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Fashion").First().Id, - DisplayOrder = 1, - MetaTitle = "Leather jackets", - ShowOnHomePage = true - }; - - var categoryFashionShoes = new Category - { - Name = "Shoes", - Alias = "Shoes", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_shoes.png"), "image/png", GetSeName("Shoes")), - Published = true, - ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Fashion").First().Id, - DisplayOrder = 1, - MetaTitle = "Shoes", - ShowOnHomePage = true - }; - - var categoryFashionTrousers = new Category - { - Name = "Trousers", - Alias = "Pants", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_trousers.png"), "image/png", GetSeName("Trousers")), - Published = true, - ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Fashion").First().Id, - DisplayOrder = 1, - MetaTitle = "Trousers", - ShowOnHomePage = true - }; - - var categoryFashionTracksuits = new Category - { - Name = "Tracksuits", - Alias = "Tracksuits", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_tracksuit.png"), "image/png", GetSeName("Tracksuits")), - Published = true, - ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Fashion").First().Id, - DisplayOrder = 1, - MetaTitle = "Tracksuits", - ShowOnHomePage = true - }; - - #endregion - - - - var categorySportsGolf = new Category - { - Name = "Golf", - Alias = "Golf", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_golf.jpg"), "image/png", GetSeName("Golf")), - Published = true, - ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Sports").First().Id, - DisplayOrder = 1, - MetaTitle = "Golf", - ShowOnHomePage = true - }; - - var categorySportsSunglasses = new Category - { - Name = "Sunglasses", - Alias = "Sunglasses", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_glasses.png"), "image/png", GetSeName("Sunglasses")), - Published = true, - ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Fashion").First().Id, - DisplayOrder = 1, - MetaTitle = "Sunglasses", - ShowOnHomePage = true - }; - - var categorySportsSoccer = new Category - { - Name = "Soccer", - Alias = "Soccer", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_soccer.png"), "image/png", GetSeName("Soccer")), - Published = true, - ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Sports").First().Id, - DisplayOrder = 1, - MetaTitle = "Soccer", - ShowOnHomePage = true - }; - - var categorySportsBasketball = new Category - { - Name = "Basketball", - Alias = "Basketball", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_basketball.png"), "image/png", GetSeName("Basketball")), - Published = true, - ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Sports").First().Id, - DisplayOrder = 1, - MetaTitle = "Basketball", - ShowOnHomePage = true - }; - - var categoryBooksSpiegel = new Category - { - Name = "SPIEGEL-Bestseller", - Alias = "SPIEGEL-Bestseller", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "0000930_spiegel-bestseller.png"), "image/png", GetSeName("SPIEGEL-Bestseller")), - Published = true, - ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Books").First().Id, - DisplayOrder = 1, - MetaTitle = "SPIEGEL-Bestseller" - }; - - var categoryBooksCookAndEnjoy = new Category - { - Name = "Cook and enjoy", - Alias = "Cook and enjoy", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "0000936_kochen-geniesen.jpeg"), "image/jpeg", GetSeName("Cook and enjoy")), - Published = true, - ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Books").First().Id, - DisplayOrder = 2, - MetaTitle = "Cook and enjoy" - }; - - //var categoryDesktops = new Category - //{ - // Name = "Desktops", - // Alias = "Desktops", - // CategoryTemplateId = categoryTemplateInGridAndLines.Id, - // ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Computers").First().Id, - // Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_desktops.png"), "image/png", GetSeName("Desktops")), - // Published = true, - // DisplayOrder = 1, - // MetaTitle = "Desktops" - //}; - - //var categoryNotebooks = new Category - //{ - // Name = "Notebooks", - // Alias = "Notebooks", - // CategoryTemplateId = categoryTemplateInGridAndLines.Id, - // ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Computers").First().Id, - // Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_notebooks.png"), "image/png", GetSeName("Notebooks")), - // Published = true, - // DisplayOrder = 2, - // MetaTitle = "Notebooks" - //}; - - var categoryGamingAccessories = new Category - { - Name = "Gaming Accessories", - Alias = "Gaming Accessories", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Gaming").First().Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_gaming_accessories.png"), "image/png", GetSeName("Gaming Accessories")), - Published = true, - DisplayOrder = 2, - MetaTitle = "Gaming Accessories" - }; - - var categoryGamingGames = new Category - { - Name = "Games", - Alias = "Games", - CategoryTemplateId = categoryTemplateInGridAndLines.Id, - ParentCategoryId = _ctx.Set().Where(x => x.MetaTitle == "Gaming").First().Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "category_games.jpg"), "image/png", GetSeName("Games")), - Published = true, - DisplayOrder = 3, - MetaTitle = "Games" - }; - - #endregion - - var entities = new List - { - categorySportsSunglasses,categorySportsSoccer, categorySportsBasketball,categorySportsGolf, categoryBooksSpiegel, categoryBooksCookAndEnjoy, - categoryGamingAccessories, categoryGamingGames, categoryFashionJackets, categoryFashionLeatherJackets, categoryFashionShoes, categoryFashionTrousers, - categoryFashionTracksuits - }; - - this.Alter(entities); - return entities; - } - - public IList Manufacturers() - { - //pictures - var sampleImagesPath = this._sampleImagesPath; - - var manufacturerTemplateInGridAndLines = - this.ManufacturerTemplates().Where(pt => pt.ViewPath == "ManufacturerTemplate.ProductsInGridOrLines").FirstOrDefault(); - - //var categoryTemplateInGridAndLines = - // this.CategoryTemplates().Where(pt => pt.Name == "Products in Grid or Lines").FirstOrDefault(); - - // - - #region Jack Wolfskin - - var manufacturerJackWolfskin = new Manufacturer - { - Name = "Jack-Wolfskin", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_Jack_Wolfskin.png"), "image/png", GetSeName("Jack Wolfskin")), - Published = true, - DisplayOrder = 1 - }; - - #endregion - - #region Mey & Edlich - - var manufacturerMeyAndEdlich = new Manufacturer - { - Name = "Mey-And-Edlich", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_MeyAndEdlich.jpg"), "image/png", GetSeName("Mey Edlich")), - Published = true, - DisplayOrder = 1 - }; - - #endregion - - #region EA Sports - - var manufacturerEASports = new Manufacturer - { - Name = "EA Sports", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_EA_Sports.png"), "image/png", GetSeName("EA Sports")), - Published = true, - DisplayOrder = 1 - }; - - #endregion EA Sports - - #region Warner Home Video Games - - var manufacturerWarnerHome = new Manufacturer - { - Name = "Warner Home Video Games", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_wb.png"), "image/png", GetSeName("Warner Home Video Games")), - Published = true, - DisplayOrder = 1 - }; - - #endregion Warner Home Video Games - - #region Breitling - - var manufacturerBreitling = new Manufacturer - { - Name = "Breitling", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_breitling.png"), "image/png", GetSeName("Breitling")), - Published = true, - DisplayOrder = 1 - }; - - #endregion Breitling - - #region Tissot - - var manufacturerTissot = new Manufacturer - { - Name = "Tissot", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_Tissot.png"), "image/png", GetSeName("Tissot")), - Published = true, - DisplayOrder = 1 - }; - - #endregion Tissot - - #region Seiko - - var manufacturerSeiko = new Manufacturer - { - Name = "Seiko", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_seiko.png"), "image/png", GetSeName("Seiko")), - Published = true, - DisplayOrder = 1 - }; - - #endregion Seiko - - #region Titleist - - var manufacturerTitleist = new Manufacturer - { - Name = "Titleist", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_titleist.png"), "image/png", GetSeName("Titleist")), - Published = true, - DisplayOrder = 1 - }; - - #endregion Titleist - - #region Puma - - var manufacturerPuma = new Manufacturer - { - Name = "Puma", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_puma.jpg"), "image/png", GetSeName("Puma")), - Published = true, - DisplayOrder = 1 - }; - - #endregion Puma - - #region Nike - - var manufacturerNike = new Manufacturer - { - Name = "Nike", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_nike.png"), "image/png", GetSeName("Nike")), - Published = true, - DisplayOrder = 1 - }; - - #endregion Nike - - #region Wilson - - var manufacturerWilson = new Manufacturer - { - Name = "Wilson", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_wilson.png"), "image/png", GetSeName("Wilson")), - Published = true, - DisplayOrder = 1 - }; - - #endregion Wilson - - #region Adidas - - var manufacturerAdidas = new Manufacturer - { - Name = "Adidas", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_adidas.png"), "image/png", GetSeName("Adidas")), - Published = true, - DisplayOrder = 1 - }; - - #endregion Adidas - - #region Ray-ban - - var manufacturerRayban = new Manufacturer - { - Name = "Ray-Ban", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_ray-ban.jpg"), "image/png", GetSeName("Ray-Ban")), - Published = true, - DisplayOrder = 1 - }; - - #endregion Ray-ban - - #region Oakley - - var manufacturerOakley = new Manufacturer - { - Name = "Oakley", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_oakley.png"), "image/png", GetSeName("Oakley")), - Published = true, - DisplayOrder = 1 - }; - - #endregion Oakley - - #region Apple - - var manufacturerApple = new Manufacturer - { - Name = "Apple", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_apple.png"), "image/png", GetSeName("Apple")), - Published = true, - DisplayOrder = 1 - }; - - #endregion Apple - - #region Android - - var manufacturerAndroid = new Manufacturer - { - Name = "Android", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-android.png"), "image/png", GetSeName("Android")), - Published = true, - DisplayOrder = 2 - }; - - #endregion Android - - #region LG - - var manufacturerLG = new Manufacturer - { - Name = "LG", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-lg.png"), "image/png", GetSeName("LG")), - Published = true, - DisplayOrder = 3 - }; - - #endregion LG - - #region Dell - - var manufacturerDell = new Manufacturer - { - Name = "Dell", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-dell.png"), "image/png", GetSeName("Dell")), - Published = true, - DisplayOrder = 4 - }; - - #endregion Dell - - #region HP - - var manufacturerHP = new Manufacturer - { - Name = "HP", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-hp.png"), "image/png", GetSeName("HP")), - Published = true, - DisplayOrder = 5 - }; - - #endregion HP - - #region Microsoft - - var manufacturerMicrosoft = new Manufacturer - { - Name = "Microsoft", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-microsoft.png"), "image/png", GetSeName("Microsoft")), - Published = true, - DisplayOrder = 6 - }; - - #endregion Microsoft - - #region Samsung - - var manufacturerSamsung = new Manufacturer - { - Name = "Samsung", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-samsung.png"), "image/png", GetSeName("Samsung")), - Published = true, - DisplayOrder = 7 - }; - - #endregion Samsung - - #region Acer - - var manufacturerAcer = new Manufacturer - { - Name = "Acer", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "acer-logo.jpg"), "image/pjpeg", GetSeName("Acer")), - Published = true, - DisplayOrder = 8 - }; - - #endregion Acer - - #region TrekStor - - var manufacturerTrekStor = new Manufacturer - { - Name = "TrekStor", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-trekstor.png"), "image/png", GetSeName("TrekStor")), - Published = true, - DisplayOrder = 9 - }; - - #endregion TrekStor - - #region Western Digital - - var manufacturerWesternDigital = new Manufacturer - { - Name = "Western Digital", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-westerndigital.png"), "image/png", GetSeName("Western Digital")), - Published = true, - DisplayOrder = 10 - }; - - #endregion Western Digital - - #region MSI - - var manufacturerMSI = new Manufacturer - { - Name = "MSI", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-msi.png"), "image/png", GetSeName("MSI")), - Published = true, - DisplayOrder = 11 - }; - - #endregion MSI - - #region Canon - - var manufacturerCanon = new Manufacturer - { - Name = "Canon", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-canon.png"), "image/png", GetSeName("Canon")), - Published = true, - DisplayOrder = 12 - }; - - #endregion Canon - - #region Casio - - var manufacturerCasio = new Manufacturer - { - Name = "Casio", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-casio.png"), "image/png", GetSeName("Casio")), - Published = true, - DisplayOrder = 13 - }; - - #endregion Casio - - #region Panasonic - - var manufacturerPanasonic = new Manufacturer - { - Name = "Panasonic", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-panasonic.png"), "image/png", GetSeName("Panasonic")), - Published = true, - DisplayOrder = 14 - }; - - #endregion Panasonic - - #region BlackBerry - - var manufacturerBlackBerry = new Manufacturer - { - Name = "BlackBerry", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-blackberry.png"), "image/png", GetSeName("BlackBerry")), - Published = true, - DisplayOrder = 15 - }; - - #endregion BlackBerry - - #region HTC - - var manufacturerHTC = new Manufacturer - { - Name = "HTC", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-htc.png"), "image/png", GetSeName("HTC")), - Published = true, - DisplayOrder = 16 - }; - - #endregion HTC - - #region Festina - - var manufacturerFestina = new Manufacturer - { - Name = "Festina", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_festina.png"), "image/png", GetSeName("Festina")), - Published = true, - DisplayOrder = 17 - }; - - #endregion Festina - - #region Certina - - var manufacturerCertina = new Manufacturer - { - Name = "Certina", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer-certina.png"), "image/png", GetSeName("Certina")), - Published = true, - DisplayOrder = 18 - }; - - #endregion Certina - - #region Sony - - var manufacturerSony = new Manufacturer - { - Name = "Sony", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_sony.png"), "image/png", GetSeName("Sony")), - Published = true, - DisplayOrder = 19 - }; - - #endregion Sony - - #region Ubisoft - - var manufacturerUbisoft = new Manufacturer - { - Name = "Ubisoft", - ManufacturerTemplateId = manufacturerTemplateInGridAndLines.Id, - Picture = CreatePicture(File.ReadAllBytes(sampleImagesPath + "manufacturer_ubisoft.png"), "image/png", GetSeName("Ubisoft")), - Published = true, - DisplayOrder = 20 - }; - - #endregion Ubisoft - - var entities = new List - { - manufacturerEASports,manufacturerWarnerHome,manufacturerBreitling,manufacturerTissot,manufacturerSeiko, manufacturerTitleist, manufacturerApple, - manufacturerSamsung,manufacturerLG,manufacturerTrekStor, manufacturerWesternDigital,manufacturerDell, manufacturerMSI, - manufacturerCanon, manufacturerCasio, manufacturerPanasonic, manufacturerBlackBerry, manufacturerHTC, manufacturerFestina, manufacturerCertina, - manufacturerHP, manufacturerAcer, manufacturerSony, manufacturerUbisoft, manufacturerOakley, manufacturerRayban, manufacturerAdidas, manufacturerWilson, - manufacturerPuma,manufacturerNike, manufacturerMeyAndEdlich, manufacturerJackWolfskin, manufacturerMicrosoft - }; - - this.Alter(entities); - return entities; - } - - private List GetFashionProducts(Dictionary specAttributes) - { - var result = new List(); - var productTemplateSimple = _ctx.Set().First(x => x.ViewPath == "Product"); - var firstDeliveryTime = _ctx.Set().First(x => x.DisplayOrder == 0); - var fashionCategory = _ctx.Set().First(x => x.Alias == "Fashion"); - var specialPriceEndDate = DateTime.UtcNow.AddMonths(1); - var specOptionCotton = _ctx.Set().First(x => x.DisplayOrder == 8).SpecificationAttributeOptions.First(x => x.DisplayOrder == 9); - - // Converse All Star - var converseAllStar = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Converse All Star", - MetaTitle = "Converse All Star", - ShortDescription = "The classical sneaker!", - FullDescription = "

Since 1912 and to this day unrivalled: the converse All Star sneaker. A shoe for every occasion.

", - Sku = "Fashion-112355", - ProductTemplateId = productTemplateSimple.Id, - AllowCustomerReviews = true, - Published = true, - ShowOnHomePage = true, - Price = 79.90M, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - IsShipEnabled = true, - DeliveryTime = firstDeliveryTime, - DisplayOrder = 1 - }; - - converseAllStar.ProductCategories.Add(new ProductCategory - { - Category = fashionCategory, - DisplayOrder = 1 - }); - - var allStarImages = new string[] { "product_allstar_converse.jpg", "product_allstar_hi_charcoal.jpg", "product_allstar_hi_maroon.jpg", "product_allstar_hi_navy.jpg", - "product_allstar_hi_purple.jpg", "product_allstar_hi_white.jpg" }; - - for (var i = 0; i < allStarImages.Length; ++i) - { - converseAllStar.ProductPictures.Add(new ProductPicture - { - Picture = CreatePicture(File.ReadAllBytes(_sampleImagesPath + allStarImages[i]), "image/jpeg", allStarImages[i].Replace("product_", "").Replace(".jpg", "")), - DisplayOrder = i + 1 - }); - } - - converseAllStar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) - }); - - result.Add(converseAllStar); - - // Shirt Meccanica - var shirtMeccanica = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Sleeveless shirt Meccanica", - MetaTitle = "Sleeveless shirt Meccanica", - ShortDescription = "Woman shirt with trendy imprint", - FullDescription = "

Also in summer, the Ducati goes with fashion style! With the sleeveless shirt Meccanica, every woman can express her passion for Ducati with a comfortable and versatile piece of clothing. The shirt is available in black and vintage red. It carries on the front the traditional lettering in plastisol print, which makes it even clearer and more radiant, while on the back in the neck area is the famous logo with the typical \"wings\" of the fifties.

", - Sku = "Fashion-987693502", - ManufacturerPartNumber = "987693502", - Gtin = "987693502", - ProductTemplateId = productTemplateSimple.Id, - AllowCustomerReviews = true, - Published = true, - Price = 38.00M, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - IsShipEnabled = true, - DeliveryTime = firstDeliveryTime, - DisplayOrder = 4 - }; - - shirtMeccanica.ProductCategories.Add(new ProductCategory - { - Category = fashionCategory, - DisplayOrder = 1 - }); - - shirtMeccanica.TierPrices.Add(new TierPrice - { - Quantity = 10, - Price = 36.00M - }); - shirtMeccanica.TierPrices.Add(new TierPrice - { - Quantity = 50, - Price = 29.00M - }); - - var shirtMeccanicaImages = new string[] { "product_shirt_meccanica_black_1.jpg", "product_shirt_meccanica_black_2.jpg", "product_shirt_meccanica_black_3.jpg", - "product_shirt_meccanica_red_1.jpg", "product_shirt_meccanica_red_2.jpg", "product_shirt_meccanica_red_3.jpg", "product_shirt_meccanica_red_4.jpg" }; - - for (var i = 0; i < shirtMeccanicaImages.Length; ++i) - { - shirtMeccanica.ProductPictures.Add(new ProductPicture - { - Picture = CreatePicture(File.ReadAllBytes(_sampleImagesPath + shirtMeccanicaImages[i]), "image/jpeg", shirtMeccanicaImages[i].Replace("product_", "").Replace(".jpg", "")), - DisplayOrder = i + 1 - }); - } - - shirtMeccanica.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) - }); - - result.Add(shirtMeccanica); - - // Ladies jacket - var ladiesJacket = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Ladies Sports Jacket", - MetaTitle = "Ladies Sports Jacket", - FullDescription = "

Lightweight wind and water repellent fabric, lining of soft single jersey knit cuffs on arm and waistband. 2 side pockets with zipper, hood in slightly waisted cut.

    Material: 100% polyamideLining: 65% polyester, 35% cottonLining 2: 100% polyester.
", - Sku = "Fashion-JN1107", - ManufacturerPartNumber = "JN1107", - ProductTemplateId = productTemplateSimple.Id, - AllowCustomerReviews = true, - Published = true, - Price = 55.00M, - OldPrice = 60.00M, - ProductCost = 20.00M, - SpecialPrice = 52.99M, - SpecialPriceStartDateTimeUtc = new DateTime(2017, 5, 1, 0, 0, 0), - SpecialPriceEndDateTimeUtc = specialPriceEndDate, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - IsShipEnabled = true, - DeliveryTime = firstDeliveryTime, - DisplayOrder = 2 - }; - - ladiesJacket.ProductCategories.Add(new ProductCategory - { - Category = fashionCategory, - DisplayOrder = 1 - }); - - var ladiesJacketImages = new string[] { "product_ladies_jacket_silver.jpg", "product_ladies_jacket_black.jpg", "product_ladies_jacket_red.jpg", - "product_ladies_jacket_orange.jpg", "product_ladies_jacket_green.jpg", "product_ladies_jacket_blue.jpg", "product_ladies_jacket_navy.jpg", }; - - for (var i = 0; i < ladiesJacketImages.Length; ++i) - { - ladiesJacket.ProductPictures.Add(new ProductPicture - { - Picture = CreatePicture(File.ReadAllBytes(_sampleImagesPath + ladiesJacketImages[i]), "image/jpeg", ladiesJacketImages[i].Replace("product_", "").Replace(".jpg", "")), - DisplayOrder = i + 1 - }); - } - - ladiesJacket.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 11) - }); - - result.Add(ladiesJacket); - - // Clark Premium Blue Jeans - var clarkJeans = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Clark Premium Blue Jeans", - MetaTitle = "Clark Premium Blue Jeans", - ShortDescription = "Modern Jeans in Easy Comfort Fit", - FullDescription = "

Real five-pocket jeans by Joker with additional, set-up pocket. Thanks to easy comfort fit with normal rise and comfortable leg width suitable for any character.

  • Material: softer, lighter premium denim made of 100% cotton.
  • Waist (inch): 29-46
  • leg (inch): 30 to 38
", - Sku = "Fashion-65986524", - ProductTemplateId = productTemplateSimple.Id, - AllowCustomerReviews = true, - Published = true, - Price = 109.90M, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - IsShipEnabled = true, - DeliveryTime = firstDeliveryTime, - DisplayOrder = 5 - }; - - clarkJeans.ProductCategories.Add(new ProductCategory - { - Category = fashionCategory, - DisplayOrder = 1 - }); - - clarkJeans.ProductPictures.Add(new ProductPicture - { - Picture = CreatePicture(File.ReadAllBytes(_sampleImagesPath + "product_clark_premium_jeans.jpg"), "image/jpeg", "clark_premium_jeans"), - DisplayOrder = 1 - }); - - clarkJeans.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) - }); - - result.Add(clarkJeans); - - return result; - } - - private List GetFurnitureProducts(Dictionary specAttributes) - { - var result = new List(); - var productTemplateSimple = _ctx.Set().First(x => x.ViewPath == "Product"); - var thirdDeliveryTime = _ctx.Set().First(x => x.DisplayOrder == 2); - var furnitureCategory = _ctx.Set().First(x => x.MetaTitle == "Furniture"); - - // Le Corbusier LC 6 table - var corbusierTable = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Le Corbusier LC 6 dining table (1929)", - MetaTitle = "Le Corbusier LC 6 dining table (1929)", - ShortDescription = "Dining table LC 6, designer: Le Corbusier, W x H x D: 225 x 69/74 (adjustable) x 85 cm, substructure: steel pipe, glass plate: Clear or sandblasted, 15 or 19 mm, height-adjustable.", - FullDescription = "

Four small plates carry a glass plate. The structure of the steel pipe is covered in clear structures. The LC6 is a true classic of Bauhaus art and is used in combination with the swivel chairs LC7 as a form-beautiful Le Corbusier dining area. In addition, the table is also increasingly found in offices or in halls. It is height-adjustable and can thus be perfectly adapted to the respective purpose.

Le Corbusier's beautifully shaped table is available with a clear or sandblasted glass plate. The substructure consists of oval steel tubes.

", - Sku = "Furniture-lc6", - ProductTemplateId = productTemplateSimple.Id, - AllowCustomerReviews = true, - Published = true, - Price = 749.00M, - HasTierPrices = true, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - IsShipEnabled = true, - DeliveryTime = thirdDeliveryTime - }; - - corbusierTable.ProductCategories.Add(new ProductCategory - { - Category = furnitureCategory, - DisplayOrder = 1 - }); - - var corbusierTableImages = new string[] { "product_corbusier_lc6_table_1.jpg", "product_corbusier_lc6_table_2.jpg", "product_corbusier_lc6_table_3.jpg", - "product_corbusier_lc6_table_4.jpg" }; - - for (var i = 0; i < corbusierTableImages.Length; ++i) - { - corbusierTable.ProductPictures.Add(new ProductPicture - { - Picture = CreatePicture(File.ReadAllBytes(_sampleImagesPath + corbusierTableImages[i]), "image/jpeg", corbusierTableImages[i].Replace("product_", "").Replace(".jpg", "")), - DisplayOrder = i + 1 - }); - } - - corbusierTable.TierPrices.Add(new TierPrice - { - Quantity = 2, - Price = 647.10M - }); - corbusierTable.TierPrices.Add(new TierPrice - { - Quantity = 4, - Price = 636.65M - }); - - corbusierTable.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - corbusierTable.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 2, - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 14) - }); - - result.Add(corbusierTable); - - // Ball Chair - var ballChair = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Eero Aarnio Ball Chair (1966)", - MetaTitle = "Eero Aarnio Ball Chair (1966)", - FullDescription = "

The ball chair, or also called the globe chair, is a real masterpiece of the legendary designer Eero Aarnio. The ball chair from the Sixties has written designer history. The egg designed armchair rests on a trumpet foot and is not lastly appreciated due to its shape and the quiet atmosphere inside this furniture. The design of the furniture body allows noise and disturbing outer world elements in the Hintergurnd us. A place as created for resting and relaxing. With its wide range of colours, the eyeball chair fits in every living and working environment. A chair that stands out for its timeless design and always has the modern look. The ball chair is 360° to rotate to change the view of the surroundings. The outer shell in fiberglass white or black. The upholstery is mixed in leather or linen.

Dimension: Width 102 cm, depth 87 cm, height 124 cm, seat height: 44 cm.

", - Sku = "Furniture-ball-chair", - ProductTemplateId = productTemplateSimple.Id, - AllowCustomerReviews = true, - Published = true, - Price = 2199.00M, - HasTierPrices = true, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - IsShipEnabled = true, - DeliveryTime = thirdDeliveryTime - }; - - ballChair.ProductCategories.Add(new ProductCategory - { - Category = furnitureCategory, - DisplayOrder = 1 - }); - - ballChair.ProductPictures.Add(new ProductPicture - { - Picture = CreatePicture(File.ReadAllBytes(_sampleImagesPath + "product_ball_chair_white.jpg"), "image/jpeg", "ball_chair_white"), - DisplayOrder = 1 - }); - ballChair.ProductPictures.Add(new ProductPicture - { - Picture = CreatePicture(File.ReadAllBytes(_sampleImagesPath + "product_ball_chair_black.jpg"), "image/jpeg", "ball_chair_black"), - DisplayOrder = 2 - }); - - ballChair.TierPrices.Add(new TierPrice - { - Quantity = 2, - Price = 1979.10M - }); - ballChair.TierPrices.Add(new TierPrice - { - Quantity = 4, - Price = 1869.15M - }); - - ballChair.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) - }); - ballChair.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 2, - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) - }); - - result.Add(ballChair); - - // Lounge chair - var loungeChair = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Charles Eames Lounge Chair (1956)", - MetaTitle = "Charles Eames Lounge Chair (1956)", - ShortDescription = "Club lounge chair, designer: Charles Eames, width 80 cm, depth 80 cm, height 60 cm, seat shell: plywood, foot (rotatable): Aluminium casting, cushion (upholstered) with leather cover.", - FullDescription = "

That's how you sit in a baseball glove. In any case, this was one of the ideas Charles Eames had in mind when designing this club chair. The lounge chair should be a comfort armchair, in which one can sink luxuriously. Through the construction of three interconnected, movable seat shells and a comfortable upholstery Charles Eames succeeded in the implementation. In fact, the club armchair with a swiveling foot is a contrast to the Bauhaus characteristics that emphasized minimalism and functionality. Nevertheless, he became a classic of Bauhaus history and still provides in many living rooms and clubs for absolute comfort with style.

Dimensions: Width 80 cm, depth 60 cm, height total 80 cm (height backrest: 60 cm). CBM: 0.70.

Lounge chair with seat shell of laminated curved plywood with rosewood veneer, walnut nature or in black. Rotatable base made of aluminium cast black with polished edges or optionally fully chromed. Elaborate upholstery of pillows in leather.

All upholstery units are removable at the Eames Lounge chair (seat, armrest, backrest, headrest).

", - Sku = "Furniture-lounge-chair", - ProductTemplateId = productTemplateSimple.Id, - AllowCustomerReviews = true, - Published = true, - ShowOnHomePage = true, - Price = 1799.00M, - OldPrice = 1999.00M, - HasTierPrices = true, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - IsShipEnabled = true, - DeliveryTime = thirdDeliveryTime - }; - - loungeChair.ProductCategories.Add(new ProductCategory - { - Category = furnitureCategory, - DisplayOrder = 1 - }); - - loungeChair.ProductPictures.Add(new ProductPicture - { - Picture = CreatePicture(File.ReadAllBytes(_sampleImagesPath + "product_charles_eames_lounge_chair_white.jpg"), "image/jpeg", "charles_eames_lounge_chair_white"), - DisplayOrder = 1 - }); - loungeChair.ProductPictures.Add(new ProductPicture - { - Picture = CreatePicture(File.ReadAllBytes(_sampleImagesPath + "product_charles_eames_lounge_chair_black.jpg"), "image/jpeg", "charles_eames_lounge_chair_black"), - DisplayOrder = 2 - }); - - loungeChair.TierPrices.Add(new TierPrice - { - Quantity = 2, - Price = 1709.05M - }); - loungeChair.TierPrices.Add(new TierPrice - { - Quantity = 4, - Price = 1664.08M - }); - loungeChair.TierPrices.Add(new TierPrice - { - Quantity = 6, - Price = 1619.10M - }); - - loungeChair.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 13) - }); - loungeChair.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 2, - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) - }); - loungeChair.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 4) - }); - - result.Add(loungeChair); - - // Cube chair - var cubeChair = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Josef Hoffmann cube chair (1910)", - MetaTitle = "Josef Hoffmann cube chair (1910)", - ShortDescription = "Armchair Cube, Designer: Josef Hoffmann, width 93 cm, depth 72 cm, height 77 cm, basic frame: solid beech wood, upholstery: solid polyurethane foam (shape resistant), Upholstery: leather", - FullDescription = "

The cube chair by Josef Hoffmann holds what the name promises and that is the same in two respects. It consists of many squares, both in terms of construction and in relation to the design of the surface. In addition, the cube, with its purely geometric form, was a kind of harbinger of cubism. The chair by Josef Hoffmann was designed in 1910 and still stands today as a replica in numerous business and residential areas.

Originally, the cube was a club chair. Together with the two-and three-seater sofa of the series, a cosy sitting area with a sophisticated charisma is created. The basic frame of the armchair is made of wood. The form-resistant upholstery is covered with leather and has been shaped visually to squares with a special sewing.

Dimensions: Width 93 cm, depth 72 cm, height 77 cm. CBM: 0.70.

", - Sku = "Furniture-cube-chair", - ProductTemplateId = productTemplateSimple.Id, - AllowCustomerReviews = true, - Published = true, - ShowOnHomePage = true, - Price = 2299.00M, - HasTierPrices = true, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - IsShipEnabled = true, - DeliveryTime = thirdDeliveryTime - }; - - cubeChair.ProductCategories.Add(new ProductCategory - { - Category = furnitureCategory, - DisplayOrder = 1 - }); - - cubeChair.ProductPictures.Add(new ProductPicture - { - Picture = CreatePicture(File.ReadAllBytes(_sampleImagesPath + "product_hoffmann_cube_chair_black.jpg"), "image/jpeg", "hoffmann_cube_chair_black"), - DisplayOrder = 1 - }); - - cubeChair.TierPrices.Add(new TierPrice - { - Quantity = 4, - Price = 1899.05M - }); - cubeChair.TierPrices.Add(new TierPrice - { - Quantity = 6, - Price = 1799.10M - }); - - cubeChair.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) - }); - - result.Add(cubeChair); - - return result; - } - - public IList Products() - { - var specialPriceEndDate = DateTime.UtcNow.AddMonths(1); - - var productTemplate = _ctx.Set().First(x => x.ViewPath == "Product"); - var firstDeliveryTime = _ctx.Set().First(x => x.DisplayOrder == 0); - var secondDeliveryTime = _ctx.Set().First(x => x.DisplayOrder == 1); - var thirdDeliveryTime = _ctx.Set().First(x => x.DisplayOrder == 2); - - var manufacturers = _ctx.Set().ToList().ToDictionarySafe(x => x.Name, x => x); - var categories = _ctx.Set().ToList().ToDictionarySafe(x => x.Alias, x => x); - var specAttributes = _ctx.Set().ToList().ToDictionarySafe(x => x.DisplayOrder, x => x); - - - //#region category shoes - - //var categoryShoes = _ctx.Set().First(x => x.Alias == "Shoes"); - - //#region product Jack Wolfskin COOGEE LOW M - - //var productJackWolfskinCooGeeLowM = new Product - //{ - // ProductType = ProductType.SimpleProduct, - // VisibleIndividually = true, - // Name = "COOGEE LOW M", - // MetaTitle = "COOGEE LOW M", - // ShortDescription = "MÄNNER FREIZEITSCHUHE", - // FullDescription = "

Du bist immer auf dem Sprung: zum Kino, zur neueröffneten Bar oder zum nächsten Stadtfest. Der stylische COOGEE LOW ist DER Schuh für dein Leben in der Stadt. Denn er verbindet Funktion mit Style. Die sportliche EVA - Zwischensohle ist nicht nur leicht und flexibel, sondern dämpft deine Ferse mit ihrer Erhöhung im hinteren Bereich auch besonders gut und ist sehr komfortabel.So sind deine Füße auch nach vielen Stunden noch fit – du wirst es merken, wenn nach einem langen Tag z.B.noch eine spontane Party ansteht. Die Passform haben wir nach dem Vorbild einer Socke gestaltet, sie ist locker und komfortabel.Dennoch bleibt der Schuh in seiner schmalen Form. Dank des atmungsaktiven Obermaterials bleibt das Fußklima angenehm, egal, wie warm es draussen ist.Gleichzeitig ist der Schuh sehr pflegeleicht.Worauf wartest du also noch ? Ab in die City!

", - // Sku = "Wolfskin-4032541", - // ProductTemplateId = productTemplate.Id, - // AllowCustomerReviews = true, - // Published = true, - // Price = 69.90M, - // OldPrice = 99.95M, - // ManageInventoryMethod = ManageInventoryMethod.ManageStock, - // OrderMinimumQuantity = 1, - // OrderMaximumQuantity = 10000, - // StockQuantity = 10000, - // NotifyAdminForQuantityBelow = 1, - // IsShipEnabled = true, - // DeliveryTime = firstDeliveryTime, - // DisplayOrder = 5 - //}; - - //AddProductPicture(productJackWolfskinCooGeeLowM, "product_wolfskin_shoes_coogee_1.jpg", "jack-wolfskin-shoes-coogee-low-m-1"); - - //productJackWolfskinCooGeeLowM.ProductCategories.Add(new ProductCategory { Category = categoryShoes, DisplayOrder = 1 }); - - //productJackWolfskinCooGeeLowM.ProductManufacturers.Add(new ProductManufacturer - //{ - // Manufacturer = _ctx.Set().Where(c => c.Name == "Jack-Wolfskin").Single(), - // DisplayOrder = 1, - //}); - - //#endregion product Jack Wolfskin COOGEE LOW M - - //#region product Adidas SUPERSTAR SCHUH - - //var productAdidasSuperstarSchuh = new Product - //{ - // ProductType = ProductType.SimpleProduct, - // VisibleIndividually = true, - // Name = "SUPERSTAR SCHUH", - // MetaTitle = "SUPERSTAR SCHUH", - // ShortDescription = "DER STREETWEAR-KLASSIKER MIT DER SHELL TOE.", - // FullDescription = "

Der adidas Superstar wurde erstmals 1969 herausgebracht und machte seinem Namen schon bald alle Ehre. Heute gilt er als Streetstyle-Legende. In dieser Version kommt der Schuh mit einem bequemen Obermaterial aus Full-Grain-Leder. Perfekt wird der Look durch die klassische Shell Toe aus Gummi für mehr Strapazierfähigkeit.

", - // Sku = "Adidas-C77124", - // ProductTemplateId = productTemplate.Id, - // AllowCustomerReviews = true, - // Published = true, - // Price = 99.95M, - // ManageInventoryMethod = ManageInventoryMethod.ManageStock, - // OrderMinimumQuantity = 1, - // OrderMaximumQuantity = 10000, - // StockQuantity = 10000, - // NotifyAdminForQuantityBelow = 1, - // IsShipEnabled = true, - // DeliveryTime = firstDeliveryTime, - // DisplayOrder = 5 - //}; - - //AddProductPicture(productAdidasSuperstarSchuh, "product_adidas_superstar_schuh_1.jpg", "adidas-superstar-schuh-1"); - - //productJackWolfskinCooGeeLowM.ProductCategories.Add(new ProductCategory { Category = categoryShoes, DisplayOrder = 1 }); - - //productJackWolfskinCooGeeLowM.ProductManufacturers.Add(new ProductManufacturer - //{ - // Manufacturer = _ctx.Set().Where(c => c.Name == "Adidas").Single(), - // DisplayOrder = 1, - //}); - - - //#endregion product Adidas SUPERSTAR SCHUH - //#endregion category shoes - - #region category jackets - - var categoryJackets = _ctx.Set().First(x => x.Alias == "Jackets"); - - #region product Jack Wolfskin KANUKA POINT - - var productJackWolfsKinKanukaPoint = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "KANUKA POINT JACKET M", - MetaTitle = "KANUKA POINT JACKET M", - ShortDescription = "SOFTSHELLJACKE MÄNNER", - FullDescription = "

Sportliches Design für sportliche Touren: Die KANUKA POINT ist so gern in Bewegung wie du. Die Softshelljacke besteht aus superelastischem und sehr atmungsaktivem Material, das sich unterwegs jeder deiner Bewegungen anpasst. Deswegen nimmst du mit der KANUKA POINT jeden Pass mit Leichtigkeit.Und musst dir auch bei Kraxeleien zum Gipfel keine Gedanken um deine Jacke machen, denn ihr Material hält einiges aus.Auch bei Wind und leichten Schauern bleibst du gelassen.

", - Sku = "jack-1305851", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - Price = 83.90M, - OldPrice = 119.95M, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - IsShipEnabled = true, - DeliveryTime = firstDeliveryTime, - DisplayOrder = 5 - }; - - AddProductPicture(productJackWolfsKinKanukaPoint, "product_jack_wolfskin_kanuka_point_1.png", "jack-wolfskin-kanuka-point-1"); - - productJackWolfsKinKanukaPoint.ProductCategories.Add(new ProductCategory { Category = categoryJackets, DisplayOrder = 1 }); - - productJackWolfsKinKanukaPoint.ProductManufacturers.Add(new ProductManufacturer - { - Manufacturer = _ctx.Set().Where(c => c.Name == "Jack-Wolfskin").Single(), - DisplayOrder = 1, - }); - - #endregion product Jack Wolfskin KANUKA POINT - #endregion category jackets - - - #region category golf - - #region product Titleist SM6 Tour Chrome - - var productTitleistSM6TourChrome = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Titleist SM6 Tour Chrome", - IsEsd = false, - ShortDescription = "For golfers who want maximum impact control and feedback.", - FullDescription = "​​

Inspired by the best iron players in the world

The new 'Spin Milled 6' wages establish a new performance class in three key areas of the Wedge game: precise length steps, bounce and maximum spin.


For each loft the center of gravity of the wedge is determined individually. Therefore, the SM6 offers a particularly precise length and flight curve control combined with great impact.
Bob Vokey's tourer-puffed sole cleat allows all golfers more bounce, adapted to their personal swing profile and the respective ground conditions.


A new, parallel face texture was developed for the absolutely exact and with 100% quality control machined grooves. The result is a consistently higher edge sharpness for more spin.

  • Precise lengths and flight curve control thanks to progressively placed center of gravity.
  • Improved bounce due to Bob Vokey's proven soles.
  • TX4 grooves produce more spin through a new surface and edge sharpness.
  • Multiple personalization options.
", - Sku = "P-7004", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Titleist SM6 Tour Chrome", - Price = 164.95M, - OldPrice = 199.95M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productTitleistSM6TourChrome, "product_titleist_sm6_tour_chrome.jpg"); - - productTitleistSM6TourChrome.ProductCategories.Add(new ProductCategory { Category = categories["Golf"], DisplayOrder = 1 }); - - productTitleistSM6TourChrome.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Titleist"], DisplayOrder = 1 }); - - #endregion product Titleist SM6 Tour Chrome - - #region product Titleist Pro V1x - - var productTitleistProV1x = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Titleist Pro V1x", - IsEsd = false, - ShortDescription = "Golf ball with high ball flight", - FullDescription = "​​The top players rely on the new Titleist Pro V1x. High ball flight, soft feel and more spin in the short game are the advantages of the V1x version. Perfect performance from the leading manufacturer. The new Titleist Pro V1 golf ball is exactly defined and promises penetrating ball flight with very soft hit feeling.", - Sku = "P-7001", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Titleist Pro V1x", - Price = 2.1M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productTitleistProV1x, "product_titleist-pro-v1x.jpg"); - - productTitleistProV1x.ProductCategories.Add(new ProductCategory { Category = categories["Golf"], DisplayOrder = 1 }); - - productTitleistProV1x.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Titleist"], DisplayOrder = 1 }); - - #endregion product Titleist Pro V1x - - #region product Supreme Golfball - - var productSupremeGolfball = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Supreme Golfball", - IsEsd = false, - ShortDescription = "Training balls with perfect flying characteristics", - FullDescription = "​Perfect golf exercise ball with the characteristics like the 'original', but in a glass-fracture-proof execution. Massive core, an ideal training ball for yard and garden. Colors: white, yellow, orange.", - Sku = "P-7002", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Supreme Golfball", - Price = 1.9M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productSupremeGolfball, "product_supremeGolfball_1.jpg", "golfball-1"); - AddProductPicture(productSupremeGolfball, "product_supremeGolfball_2.jpg", "golfball-2"); - - productSupremeGolfball.ProductCategories.Add(new ProductCategory { Category = categories["Golf"], DisplayOrder = 1 }); - - productSupremeGolfball.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Titleist"], DisplayOrder = 1 }); - - #endregion product Supreme Golfball - - #region product GBB Epic Sub Zero Driver - - var productGBBEpicSubZeroDriver = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "GBB Epic Sub Zero Driver", - IsEsd = false, - ShortDescription = "Low spin for good golfing!", - FullDescription = "Your game wins with the GBB Epic Sub Zero Driver. A golf club with an extremely low spin and the phenomenal high-speed characteristic.", - Sku = "P-7003", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "GBB Epic Sub Zero Driver", - Price = 489M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productGBBEpicSubZeroDriver, "product_gbb-epic-sub-zero-driver.jpg"); - - productGBBEpicSubZeroDriver.ProductCategories.Add(new ProductCategory { Category = categories["Golf"], DisplayOrder = 1 }); - - productGBBEpicSubZeroDriver.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Titleist"], DisplayOrder = 1 }); - - #endregion product GBB Epic Sub Zero Driver - - #endregion category golf - - #region category Soccer - - #region product Nike Strike Football - - var productNikeStrikeFootball = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Nike Strike Football", - IsEsd = false, - ShortDescription = "GREAT TOUCH. HIGH VISIBILITY.", - FullDescription = "

Enhance play everyday, with the Nike Strike Football.

Reinforced rubber retains its shape for confident and consistent control. A stand out Visual Power graphic in black, green and orange is best for ball tracking, despite dark or inclement conditions.

  • Visual Power graphic helps give a true read on flight trajectory.
  • Textured casing offers superior touch.
  • Reinforced rubber bladder supports air and shape retention.
  • 66% rubber/ 15% polyurethane/ 13% polyester/ 7% EVA.
", - Sku = "P-5004", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Nike Strike Football", - Price = 59.90M, - OldPrice = 69.90M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id, - HasTierPrices = true - }; - - AddProductPicture(productNikeStrikeFootball, "products_nike-strike-football.jpg"); - - productNikeStrikeFootball.ProductCategories.Add(new ProductCategory { Category = categories["Soccer"], DisplayOrder = 1 }); - - productNikeStrikeFootball.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Nike"], DisplayOrder = 1 }); - - productNikeStrikeFootball.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Manufacturer -> Nike - SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 20) - }); - productNikeStrikeFootball.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Material -> rubber - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 12) - }); - - productNikeStrikeFootball.TierPrices.Add(new TierPrice { Quantity = 6, Price = 26.90M }); - productNikeStrikeFootball.TierPrices.Add(new TierPrice { Quantity = 12, Price = 24.90M }); - productNikeStrikeFootball.TierPrices.Add(new TierPrice { Quantity = 24, Price = 22.90M }); - - #endregion product Nike Strike Football - - #region product Evopower 5.3 Trainer HS Ball - - var productNikeEvoPowerBall = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Evopower 5.3 Trainer HS Ball", - IsEsd = false, - ShortDescription = "Entry level training ball.", - FullDescription = "

Entry level training ball.< p > Constructed from 32 panels with equal surface areas for reduced seam-stress and a perfectly round shape.Handstitched panels with multilayered woven backing for enhanced stability and aerodynamics.", - Sku = "P-5003", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Evopower 5.3 Trainer HS Ball", - Price = 59.90M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productNikeEvoPowerBall, "product_nike-vopower-53-trainer-hs-ball.jpg"); - - productNikeEvoPowerBall.ProductCategories.Add(new ProductCategory { Category = categories["Soccer"], DisplayOrder = 1 }); - - productNikeEvoPowerBall.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Nike"], DisplayOrder = 1 }); - - productNikeEvoPowerBall.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Manufacturer -> Nike - SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 20) - }); - productNikeEvoPowerBall.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Material -> leather - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) - }); - - #endregion Evopower 5.3 Trainer HS Ball - - #region product Torfabrik official game ball - - var productTorfabrikOfficialGameBall = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Torfabrik official game ball", - IsEsd = false, - ShortDescription = "Available in different colors", - FullDescription = "", - Sku = "P-5002", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Torfabrik official game ball", - Price = 59.90M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productTorfabrikOfficialGameBall, "product_torfabrik-offizieller-spielball_white.png", "official-game-ball-white"); - AddProductPicture(productTorfabrikOfficialGameBall, "product_torfabrik-offizieller-spielball_red.png", "official-game-ball-red"); - AddProductPicture(productTorfabrikOfficialGameBall, "product_torfabrik-offizieller-spielball_yellow.png", "official-game-ball-yellow"); - AddProductPicture(productTorfabrikOfficialGameBall, "product_torfabrik-offizieller-spielball_blue.png", "official-game-ball-blue"); - AddProductPicture(productTorfabrikOfficialGameBall, "product_torfabrik-offizieller-spielball_green.png", "official-game-ball-green"); - - productTorfabrikOfficialGameBall.ProductCategories.Add(new ProductCategory { Category = categories["Soccer"], DisplayOrder = 1 }); - - productTorfabrikOfficialGameBall.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Adidas"], DisplayOrder = 1 }); - - productTorfabrikOfficialGameBall.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Manufacturer -> Adidas - SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 19) - }); - productTorfabrikOfficialGameBall.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Material -> leather - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) - }); - - #endregion Torfabrik official game ball - - #region product Adidas TANGO SALA BALL - - var productAdidasTangoSalaBall = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Adidas TANGO SALA BALL", - IsEsd = false, - ShortDescription = "In different colors", - FullDescription = "

TANGO SALA BALL
A SALA BALL TO MATCH YOUR INDOOR PLAYMAKING.

Take the game indoors. With a design nod to the original Tango ball that set the performance standard, this indoor soccer is designed for low rebound and enhanced control for futsal. Machine-stitched for a soft touch and high durability.

  • Machine-stitched for soft touch and high durability
  • Low rebound for enhanced ball control
  • Butyl bladder for best air retention
  • Requires inflation
  • 100% natural rubber
  • Imported

", - Sku = "P-5001", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Adidas TANGO SALA BALL", - Price = 59.90M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productAdidasTangoSalaBall, "product_adidas-tango-pasadena-ball-white.png", "adidas-tango-pasadena-ball-white"); - AddProductPicture(productAdidasTangoSalaBall, "product_adidas-tango-pasadena-ball-yellow.jpg", "adidas-tango-pasadena-ball-yellow"); - AddProductPicture(productAdidasTangoSalaBall, "product_adidas-tango-pasadena-ball-red.jpg", "adidas-tango-pasadena-ball-red"); - AddProductPicture(productAdidasTangoSalaBall, "product_adidas-tango-pasadena-ball-green.jpg", "adidas-tango-pasadena-ball-green"); - AddProductPicture(productAdidasTangoSalaBall, "product_adidas-tango-pasadena-ball-gray.jpg", "adidas-tango-pasadena-ball-gray"); - AddProductPicture(productAdidasTangoSalaBall, "product_adidas-tango-pasadena-ball-brown.jpg", "adidas-tango-pasadena-ball-brown"); - AddProductPicture(productAdidasTangoSalaBall, "product_adidas-tango-pasadena-ball-blue.jpg", "adidas-tango-pasadena-ball-blue"); - - productAdidasTangoSalaBall.ProductCategories.Add(new ProductCategory { Category = categories["Soccer"], DisplayOrder = 1 }); - - productAdidasTangoSalaBall.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Adidas"], DisplayOrder = 1 }); - - productAdidasTangoSalaBall.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Manufacturer -> Adidas - SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 19) - }); - productAdidasTangoSalaBall.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Material -> leather - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) - }); - - #endregion Adidas TANGO SALA BALL - - #endregion category Soccer - - #region category Basketball - - #region Wilson Evolution High School Game Basketball - - var productEvolutionHighSchoolGameBasketball = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Evolution High School Game Basketball", - IsEsd = false, - ShortDescription = "For all positions on all levels, match day and every day", - FullDescription = "

The Wilson Evolution High School Game Basketball has exclusive microfiber composite leather construction with deep embossed pebbles to give you the ultimate in feel and control.

Its patented Cushion Core Technology enhances durability for longer play. This microfiber composite Evolution high school basketball is pebbled with composite channels for better grip, helping players raise their game to the next level.

For all positions at all levels of play, game day and every day, Wilson delivers the skill-building performance that players demand.

This regulation-size 29.5' Wilson basketball is an ideal basketball for high school players, and is designed for either recreational use or for league games. It is NCAA and NFHS approved, so you know it's a high-quality basketball that will help you hone your shooting, passing and ball-handling skills.

Take your team all the way to the championship with the Wilson Evolution High School Game Basketball.

", - Sku = "P-4001", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Evolution High School Game Basketball", - Price = 25.90M, - OldPrice = 29.90M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id, - HasTierPrices = true - }; - - AddProductPicture(productEvolutionHighSchoolGameBasketball, "product_evolution-high-school-game-basketball.jpg"); - - productEvolutionHighSchoolGameBasketball.ProductCategories.Add(new ProductCategory { Category = categories["Basketball"], DisplayOrder = 1 }); - - productEvolutionHighSchoolGameBasketball.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Adidas"], DisplayOrder = 1 }); - - productEvolutionHighSchoolGameBasketball.TierPrices.Add(new TierPrice { Quantity = 6, Price = 24.90M }); - productEvolutionHighSchoolGameBasketball.TierPrices.Add(new TierPrice { Quantity = 12, Price = 22.90M }); - productEvolutionHighSchoolGameBasketball.TierPrices.Add(new TierPrice { Quantity = 24, Price = 20.90M }); - - #endregion Wilson Evolution High School Game Basketball - - #region All Court Basketball - - var productAllCourtBasketball = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "All-Court Basketball", - IsEsd = false, - ShortDescription = "A durable Basketball for all surfaces", - FullDescription = "

All-Court Prep Ball

A durable basketball for all surfaces.

Whether on parquet or on asphalt - the adidas All-Court Prep Ball hat has only one goal: the basket. This basketball is made of durable artificial leather, was also predestined for indoor games also for outdoor games.

  • Composite cover made of artificial leather
  • suitable for indoors and outdoors
  • Delivered unpumped
", - Sku = "P-4002", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "All-Court Basketball", - Price = 25.90M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productAllCourtBasketball, "product_all-court-basketball.png"); - - productAllCourtBasketball.ProductCategories.Add(new ProductCategory { Category = categories["Basketball"], DisplayOrder = 1 }); - - productAllCourtBasketball.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Adidas"], DisplayOrder = 1 }); - - #endregion All Court Basketball - - #endregion category Basketball - - #region category sunglasses - - #region product Top bar - - var productRayBanTopBar = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Ray-Ban Top Bar RB 3183", - IsEsd = false, - ShortDescription = "The Ray-Ban Original Wayfarer is the most famous style in the history of sunglasses. With the original design from 1952 the Wayfarer is popular with celebrities, musicians, artists and fashion experts.", - FullDescription = "

The Ray-Ban ® RB3183 sunglasses give me their aerodynamic shape a reminiscence of speed.

A rectangular shape and the classic Ray-Ban logo imprinted on the straps characterize this light Halbrand model.

", - Sku = "P-3004", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Ray-Ban Top Bar RB 3183", - Price = 139M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productRayBanTopBar, "product_RayBanTopBar_1.jpg", "rayban-top-bar-1"); - AddProductPicture(productRayBanTopBar, "product_RayBanTopBar_2.jpg", "rayban-top-bar-2"); - AddProductPicture(productRayBanTopBar, "product_RayBanTopBar_3.jpg", "rayban-top-bar-3"); - - productRayBanTopBar.ProductCategories.Add(new ProductCategory { Category = categories["Sunglasses"], DisplayOrder = 1 }); - - productRayBanTopBar.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Ray-Ban"], DisplayOrder = 1 }); - - #endregion product Top bar - - #region product ORIGINAL WAYFARER AT COLLECTION - - var productOriginalWayfarer = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "ORIGINAL WAYFARER AT COLLECTION", - IsEsd = false, - ShortDescription = "The Ray-Ban Original Wayfarer is the most famous style in the history of sunglasses. With the original design from 1952 the Wayfarer is popular with celebrities, musicians, artists and fashion experts.", - FullDescription = "

Radar® EV Path™ PRIZM™ Road

A new milestone in the heritage of performance, Radar® EV takes breakthroughs of a revolutionary design even further with a taller lens that extends the upper field of view. From the comfort and protection of the O Matter® frame to the grip of its Unobtanium® components, this premium design builds on the legacy of Radar innovation and style.

Features

  • PRIZM™ is a revolutionary lens technology that fine-tunes vision for specific sports and environments. See what you’ve been missing. Click here to learn more about Prizm Lens Technology.
  • Path lenses enhance performance if traditional lenses touch your cheeks and help extend the upper field of view
  • Engineered for maximized airflow for optimal ventilation to keep you cool
  • Unobtanium® earsocks and nosepads keep glasses in place, increasing grip despite perspiration
  • Interchangeable Lenses let you change lenses in seconds to optimize vision in any sport environment
", - Sku = "P-3003", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "ORIGINAL WAYFARER AT COLLECTION", - Price = 149M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productOriginalWayfarer, "product_productOriginalWayfarer_1.jpg", "wayfarer-blue-gray-classic-black-1"); - AddProductPicture(productOriginalWayfarer, "product_productOriginalWayfarer_2.jpg", "wayfarer-blue-gray-classic-black-2"); - AddProductPicture(productOriginalWayfarer, "product_productOriginalWayfarer_3.jpg", "wayfarer-gray-course-black"); - AddProductPicture(productOriginalWayfarer, "product_productOriginalWayfarer_4.jpg", "wayfarer-brown-course-havana"); - AddProductPicture(productOriginalWayfarer, "product_productOriginalWayfarer_5.jpg", "wayfarer-green-classic-havana-black"); - AddProductPicture(productOriginalWayfarer, "product_productOriginalWayfarer_6.jpg", "wayfarer-blue-gray-classic-black-3"); - - productOriginalWayfarer.ProductCategories.Add(new ProductCategory { Category = categories["Sunglasses"], DisplayOrder = 1 }); - - productOriginalWayfarer.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Ray-Ban"], DisplayOrder = 1 }); - - #endregion product ORIGINAL WAYFARER AT COLLECTION - - #region product Radar EV Prizm Sports Sunglasses - - var productRadarEVPrizmSportsSunglasses = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Radar EV Prizm Sports Sunglasses", - IsEsd = false, - ShortDescription = "", - FullDescription = "

Radar® EV Path™ PRIZM™ Road

A new milestone in the heritage of performance, Radar® EV takes breakthroughs of a revolutionary design even further with a taller lens that extends the upper field of view. From the comfort and protection of the O Matter® frame to the grip of its Unobtanium® components, this premium design builds on the legacy of Radar innovation and style.

Features

  • PRIZM™ is a revolutionary lens technology that fine-tunes vision for specific sports and environments. See what you’ve been missing. Click here to learn more about Prizm Lens Technology.
  • Path lenses enhance performance if traditional lenses touch your cheeks and help extend the upper field of view
  • Engineered for maximized airflow for optimal ventilation to keep you cool
  • Unobtanium® earsocks and nosepads keep glasses in place, increasing grip despite perspiration
  • Interchangeable Lenses let you change lenses in seconds to optimize vision in any sport environment
", - Sku = "P-3001", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Radar EV Prizm Sports Sunglasses", - Price = 149M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productRadarEVPrizmSportsSunglasses, "product_radar_ev_prizm.jpg"); - - productRadarEVPrizmSportsSunglasses.ProductCategories.Add(new ProductCategory { Category = categories["Sunglasses"], DisplayOrder = 1 }); - - productRadarEVPrizmSportsSunglasses.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Oakley"], DisplayOrder = 1 }); - - #endregion product Radar EV Prizm Sports Sunglasses - - #region product Custom Flak Sunglasses - - var productCustomFlakSunglasses = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Custom Flak Sunglasses", - IsEsd = false, - ShortDescription = "", - FullDescription = "

Radar® EV Path™ PRIZM™ Road

A new milestone in the heritage of performance, Radar® EV takes breakthroughs of a revolutionary design even further with a taller lens that extends the upper field of view. From the comfort and protection of the O Matter® frame to the grip of its Unobtanium® components, this premium design builds on the legacy of Radar innovation and style.

Features

  • PRIZM™ is a revolutionary lens technology that fine-tunes vision for specific sports and environments. See what you’ve been missing. Click here to learn more about Prizm Lens Technology.
  • Path lenses enhance performance if traditional lenses touch your cheeks and help extend the upper field of view
  • Engineered for maximized airflow for optimal ventilation to keep you cool
  • Unobtanium® earsocks and nosepads keep glasses in place, increasing grip despite perspiration
  • Interchangeable Lenses let you change lenses in seconds to optimize vision in any sport environment
", - Sku = "P-3002", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Custom Flak Sunglasses", - Price = 179M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlakSunglasses.jpg", "custom_flak"); - AddProductPicture(productCustomFlakSunglasses, "productCustomFlakSunglasses_black_white.jpg", "custom_flak_black_white"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_matteblack_gray.jpg", "custom_flak_matteblack_gray"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_matteblack_clear.jpg", "custom_flak_matteblack_clear"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_matteblack_jadeiridium.jpg", "custom_flak_matteblack_jadeiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_matteblack_positiverediridium.jpg", "custom_flak_matteblack_positiverediridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_matteblack_rubyiridium.jpg", "custom_flak_matteblack_rubyiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_matteblack_sapphireiridium.jpg", "custom_flak_matteblack_sapphireiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_matteblack_violetiridium.jpg", "custom_flak_matteblack_violetiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_matteblack_24kiridium.jpg", "custom_flak_matteblack_24kiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_matteblack_fireiridium.jpg", "custom_flak_matteblack_fireiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_orangeflare_24kiridium.jpg", "custom_flak_orangeflare_24kiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_orangeflare_clear.jpg", "custom_flak_orangeflare_clear"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_orangeflare_fireiridium.jpg", "custom_flak_orangeflare_fireiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_orangeflare_gray.jpg", "custom_flak_orangeflare_gray"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_orangeflare_jadeiridium.jpg", "custom_flak_orangeflare_jadeiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_orangeflare_positiverediridium.jpg", "custom_flak_orangeflare_positiverediridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_orangeflare_rubyiridium.jpg", "custom_flak_orangeflare_rubyiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_orangeflare_sapphireiridium.jpg", "custom_flak_orangeflare_sapphireiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_orangeflare_violetiridium.jpg", "custom_flak_orangeflare_violetiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_polishedwhite_24kiridium.jpg", "custom_flak_polishedwhite_24kiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_polishedwhite_clear.jpg", "custom_flak_polishedwhite_clear"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_polishedwhite_fireiridium.jpg", "custom_flak_polishedwhite_fireiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_polishedwhite_gray.jpg", "custom_flak_polishedwhite_gray"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_polishedwhite_jadeiridium.jpg", "custom_flak_polishedwhite_jadeiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_polishedwhite_rubyiridium.jpg", "custom_flak_polishedwhite_rubyiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_polishedwhite_sapphireiridium.jpg", "custom_flak_polishedwhite_sapphireiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_polishedwhite_violetiridium.jpg", "custom_flak_polishedwhite_violetiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_polishedwhite_positiverediridium.jpg", "custom_flak_polishedwhite_positiverediridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_redline_24kiridium.jpg", "custom_flak_redline_24kiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_redline_clear.jpg", "custom_flak_redline_clear"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_redline_fireiridium.jpg", "custom_flak_redline_fireiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_redline_gray.jpg", "custom_flak_redline_gray"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_redline_jadeiridium.jpg", "custom_flak_redline_jadeiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_redline_positiverediridium.jpg", "custom_flak_redline_positiverediridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_redline_rubyiridium.jpg", "custom_flak_redline_rubyiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_redline_sapphireiridium.jpg", "custom_flak_redline_sapphireiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_redline_violetiridium.jpg", "custom_flak_redline_violetiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_skyblue_24kiridium.jpg", "custom_flak_skyblue_24kiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_skyblue_clear.jpg", "custom_flak_skyblue_clear"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_skyblue_fireiridium.jpg", "custom_flak_skyblue_fireiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_skyblue_gray.jpg", "custom_flak_skyblue_gray"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_skyblue_jadeiridium.jpg", "custom_flak_skyblue_jadeiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_skyblue_positiverediridium.jpg", "custom_flak_skyblue_positiverediridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_skyblue_rubyiridium.jpg", "custom_flak_skyblue_rubyiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_skyblue_sapphireiridium.jpg", "custom_flak_skyblue_sapphireiridium"); - AddProductPicture(productCustomFlakSunglasses, "product_CustomFlak_skyblue_violetiridium.jpg", "custom_flak_skyblue_violetiridium"); - - productCustomFlakSunglasses.ProductCategories.Add(new ProductCategory { Category = categories["Sunglasses"], DisplayOrder = 1 }); - - productCustomFlakSunglasses.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Oakley"], DisplayOrder = 1 }); - - #endregion product Custom Flak Sunglasses - - - #endregion category sunglasses - - #region category apple - - #region product iphone plus - - var productIphoneplus = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "iPhone Plus", - IsEsd = false, - ShortDescription = "iPhone 7 dramatically improves the most important aspects of the iPhone experience. It introduces advanced new camera systems. The best performance and battery life ever in an iPhone. Immersive stereo speakers. The brightest, most colorful iPhone display. Splash and water resistance.1 And it looks every bit as powerful as it is. This is iPhone 7.", - FullDescription = "", - Sku = "P-2001", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "iPhone Plus", - Price = 878M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 9, - StockQuantity = 10000, - DisplayStockAvailability = true, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - IsFreeShipping = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productIphoneplus, "product_iphone-plus_all_colors.jpg", "iphone-plus-all-colors"); - AddProductPicture(productIphoneplus, "product_iphoneplus_1.jpg", "iphone-plus-default"); - AddProductPicture(productIphoneplus, "product_iphone-plus_red.jpg", "iphone-plus-red"); - AddProductPicture(productIphoneplus, "product_iphone-plus_silver.jpg", "iphone-plus-silver"); - AddProductPicture(productIphoneplus, "product_iphone-plus_black.jpg", "iphone-plus-black"); - AddProductPicture(productIphoneplus, "product_iphone-plus_rose.jpg", "iphone-plus-rose"); - AddProductPicture(productIphoneplus, "product_iphone-plus_gold.jpg", "iphone-plus-gold"); - - productIphoneplus.ProductCategories.Add(new ProductCategory { Category = categories["Apple"], DisplayOrder = 1 }); - - productIphoneplus.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // offer type -> Permanent low price - SpecificationAttributeOption = specAttributes[22].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) - }); - - productIphoneplus.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // storage capacity -> 64gb - SpecificationAttributeOption = specAttributes[27].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) - }); - productIphoneplus.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // storage capacity -> 128gb - SpecificationAttributeOption = specAttributes[27].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) - }); - productIphoneplus.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // operating system -> ios - SpecificationAttributeOption = specAttributes[5].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) - }); - - #endregion product iphone plus - - #region product Watch Series 2 - - var productWatchSeries2 = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = false, - Name = "Watch Series 2", - IsEsd = false, - ShortDescription = "Live a better day. Built-in GPS. Water resistance to 50 meters.1 A lightning-fast dual‑core processor. And a display that’s two times brighter than before. Full of features that help you stay active, motivated, and connected, Apple Watch Series 2 is the perfect partner for a healthy life.", - FullDescription = "", - Sku = "P-2002", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Watch Series 2", - Price = 299M, - OldPrice = 399M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productWatchSeries2, "product_watchseries2_1.jpg", "watchseries-1"); - AddProductPicture(productWatchSeries2, "product_watchseries2_2.jpg", "watchseries-2"); - - productWatchSeries2.ProductCategories.Add(new ProductCategory { Category = categories["Apple"], DisplayOrder = 1 }); - - productWatchSeries2.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Apple"], DisplayOrder = 1 }); - - productWatchSeries2.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // offer type -> offer of the day - SpecificationAttributeOption = specAttributes[22].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 6) - }); - - productWatchSeries2.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // storage capacity -> 32gb - SpecificationAttributeOption = specAttributes[27].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - - productWatchSeries2.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // operating system -> ios - SpecificationAttributeOption = specAttributes[5].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) - }); - - #endregion product Watch Series 2 - - #region product Airpods - - var productAirpods = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "AirPods", - IsEsd = false, - ShortDescription = "Wireless. Effortless. Magical. Just take them out and they’re ready to use with all your devices. Put them in your ears and they connect instantly. Speak into them and your voice sounds clear. Introducing AirPods. Simplicity and technology, together like never before. The result is completely magical.", - FullDescription = "", - Sku = "P-2003", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "AirPods", - Price = 999M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productAirpods, "products_airpods_white.jpg", "airpods-white"); - AddProductPicture(productAirpods, "products_airpods_turquoise.jpg", "airpods-turquoise"); - AddProductPicture(productAirpods, "products_airpods_lightblue.jpg", "airpods-lightblue"); - AddProductPicture(productAirpods, "products_airpods_rose.jpg", "airpods-rose"); - AddProductPicture(productAirpods, "products_airpods_gold.jpg", "airpods-gold"); - AddProductPicture(productAirpods, "products_airpods_mint.jpg", "airpods-mint"); - - productAirpods.ProductCategories.Add(new ProductCategory { Category = categories["Apple"], DisplayOrder = 1 }); - - productAirpods.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Apple"], DisplayOrder = 7 }); - - #endregion product Airpods - - #region product Ultimate Apple Pro Hipster Bundle - - var productAppleProHipsterBundle = new Product - { - ProductType = ProductType.BundledProduct, - VisibleIndividually = true, - Name = "Ultimate Apple Pro Hipster Bundle", - IsEsd = false, - ShortDescription = "Save with this set 5%!", - FullDescription = "As an Apple fan and hipster, it is your basic need to always have the latest Apple products. So you do not have to spend four times a year in front of the Apple Store, simply subscribe to the Ultimate Apple Pro Hipster Set in the year subscription!", - Sku = "P-2005-Bundle", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - ShowOnHomePage = true, - MetaTitle = "Ultimate Apple Pro Hipster Bundle", - Price = 2371M, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id, - BundleTitleText = "Bundle includes", - BundlePerItemPricing = true, - BundlePerItemShoppingCart = true - }; - - AddProductPicture(productAppleProHipsterBundle, "product_ultimate-apple-pro-hipster-bundle.jpg", "apple-pro-hipster-bundle"); - AddProductPicture(productAppleProHipsterBundle, "products_airpods_white.jpg", "bundle-airpods-white"); - AddProductPicture(productAppleProHipsterBundle, "product_watchseries2_2.jpg", "bundle-watchseries"); - AddProductPicture(productAppleProHipsterBundle, "product_iphoneplus_2.jpg", "bundle-iphoneplus"); - AddProductPicture(productAppleProHipsterBundle, "category_apple.png", "bundle-apple"); - - productAppleProHipsterBundle.ProductCategories.Add(new ProductCategory { Category = categories["Apple"], DisplayOrder = 1 }); - - productAppleProHipsterBundle.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Apple"], DisplayOrder = 1 }); - - #endregion product Ultimate Apple Pro Hipster Bundle - - #region product 9,7 iPad - - var product97ipad = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "9,7' iPad", - IsEsd = false, - ShortDescription = "Flat-out fun. Learn, play, surf, create. iPad gives you the incredible display, performance, and apps to do what you love to do. Anywhere. Easily. Magically.", - FullDescription = "
  • 9,7' Retina Display mit True Tone und
  • A9X Chip der dritten Generation mit 64-Bit Desktoparchitektur
  • Touch ID Fingerabdrucksensor
  • 12 Megapixel iSight Kamera mit 4K Video
  • 5 Megapixel FaceTime HD Kamera
  • 802.11ac WLAN mit MIMO
  • Bis zu 10 Stunden Batterielaufzeit***
  • 4-Lautsprecher-Audio
", - Sku = "P-2004", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - ShowOnHomePage = true, - MetaTitle = "9,7' iPad", - Price = 319.00M, - OldPrice = 349.00M, - SpecialPrice = 299.00M, - SpecialPriceStartDateTimeUtc = new DateTime(2017, 5, 1, 0, 0, 0), - SpecialPriceEndDateTimeUtc = specialPriceEndDate, - IsGiftCard = false, - ManageInventoryMethod = ManageInventoryMethod.ManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(product97ipad, "product_ipad_1.jpg", "ipad-1"); - AddProductPicture(product97ipad, "product_ipad_2.jpg", "ipad-2"); - AddProductPicture(product97ipad, "product_97-ipad-yellow.jpg", "ipad-yellow"); - AddProductPicture(product97ipad, "product_97-ipad-turquoise.jpg", "ipad-turquoise"); - AddProductPicture(product97ipad, "product_97-ipad-lightblue.jpg", "ipad-lightblue"); - AddProductPicture(product97ipad, "product_97-ipad-purple.jpg", "ipad-purple"); - AddProductPicture(product97ipad, "product_97-ipad-mint.jpg", "ipad-mint"); - AddProductPicture(product97ipad, "product_97-ipad-rose.jpg", "ipad-rose"); - AddProductPicture(product97ipad, "product_97-ipad-spacegray.jpg", "ipad-spacegray"); - AddProductPicture(product97ipad, "product_97-ipad-gold.jpg", "ipad-gold"); - AddProductPicture(product97ipad, "product_97-ipad-silver.jpg", "ipad-silver"); - - product97ipad.ProductCategories.Add(new ProductCategory { Category = categories["Apple"], DisplayOrder = 1 }); - - product97ipad.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Apple"], DisplayOrder = 1 }); - - product97ipad.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // offer type -> promotion - SpecificationAttributeOption = specAttributes[22].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) - }); - - product97ipad.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // storage capacity -> 64gb - SpecificationAttributeOption = specAttributes[27].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) - }); - product97ipad.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // storage capacity -> 128gb - SpecificationAttributeOption = specAttributes[27].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) - }); - product97ipad.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // operating system -> ios - SpecificationAttributeOption = specAttributes[5].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) - }); - - #endregion product 9,7 iPad - - #endregion category apple - - #region category Gift Cards - - #region product10GiftCard - - var product10GiftCard = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "$10 Virtual Gift Card", - IsEsd = true, - ShortDescription = "$10 Gift Card. Gift Cards must be redeemed through our site Web site toward the purchase of eligible products.", - FullDescription = "

Gift Cards must be redeemed through our site Web site toward the purchase of eligible products. Purchases are deducted from the GiftCard balance. Any unused balance will be placed in the recipient's GiftCard account when redeemed. If an order exceeds the amount of the GiftCard, the balance must be paid with a credit card or other available payment method.

", - Sku = "P-1000", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "$10 Virtual Gift Card", - Price = 10M, - IsGiftCard = true, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - DisplayOrder = 1 - }; - - AddProductPicture(product10GiftCard, "product_gift_card_10.png"); - product10GiftCard.ProductCategories.Add(new ProductCategory { Category = categories["Gift Cards"], DisplayOrder = 1 }); - - #endregion product10GiftCard - - #region product25GiftCard - - var product25GiftCard = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "$25 Virtual Gift Card", - IsEsd = true, - ShortDescription = "$25 Gift Card. Gift Cards must be redeemed through our site Web site toward the purchase of eligible products.", - FullDescription = "

Gift Cards must be redeemed through our site Web site toward the purchase of eligible products. Purchases are deducted from the GiftCard balance. Any unused balance will be placed in the recipient's GiftCard account when redeemed. If an order exceeds the amount of the GiftCard, the balance must be paid with a credit card or other available payment method.

", - Sku = "P-1001", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "$25 Virtual Gift Card", - Price = 25M, - IsGiftCard = true, - GiftCardType = GiftCardType.Virtual, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - DisplayOrder = 2 - }; - - AddProductPicture(product25GiftCard, "product_gift_card_25.png"); - product25GiftCard.ProductCategories.Add(new ProductCategory { Category = categories["Gift Cards"], DisplayOrder = 1 }); - - #endregion product25GiftCard - - #region product50GiftCard - - var product50GiftCard = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "$50 Virtual Gift Card", - IsEsd = true, - ShortDescription = "$50 Gift Card. Gift Cards must be redeemed through our site Web site toward the purchase of eligible products.", - FullDescription = "

Gift Cards must be redeemed through our site Web site toward the purchase of eligible products. Purchases are deducted from the GiftCard balance. Any unused balance will be placed in the recipient's GiftCard account when redeemed. If an order exceeds the amount of the GiftCard, the balance must be paid with a credit card or other available payment method.

", - Sku = "P-1002", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "$50 Virtual Gift Card", - Price = 50M, - IsGiftCard = true, - GiftCardType = GiftCardType.Virtual, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - DisplayOrder = 3 - }; - - AddProductPicture(product50GiftCard, "product_gift_card_50.png"); - product50GiftCard.ProductCategories.Add(new ProductCategory { Category = categories["Gift Cards"], DisplayOrder = 1 }); - - #endregion product50GiftCard - - #region product100GiftCard - - var product100GiftCard = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "$100 Virtual Gift Card", - IsEsd = true, - ShortDescription = "$100 Gift Card. Gift Cards must be redeemed through our site Web site toward the purchase of eligible products.", - FullDescription = "

Gift Cards must be redeemed through our site Web site toward the purchase of eligible products. Purchases are deducted from the GiftCard balance. Any unused balance will be placed in the recipient's GiftCard account when redeemed. If an order exceeds the amount of the GiftCard, the balance must be paid with a credit card or other available payment method.

", - Sku = "P-10033", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "$100 Virtual Gift Card", - Price = 100M, - IsGiftCard = true, - GiftCardType = GiftCardType.Virtual, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - DisplayOrder = 4, - }; - - AddProductPicture(product100GiftCard, "product_gift_card_100.png"); - product100GiftCard.ProductCategories.Add(new ProductCategory { Category = categories["Gift Cards"], DisplayOrder = 1 }); - - #endregion product100GiftCard - - #endregion category Gift Cards - - #region category books - - #region productBooksUberMan - - var productBooksUberMan = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Überman: The novel", - ShortDescription = "(Hardcover)", - FullDescription = "

From idiots to riches - and back ... Ever since it with my Greek financial advisors were no more delicious cookies to meetings, I should have known something. Was the last cookie it when I bought a Romanian forest funds and leveraged discount certificates on lean hogs - which is sort of a more stringent bet that the price of lean hogs will remain stable, and that's nothing special because it is also available for cattle and cotton and fat pig. Again and again and I joked Kosmas Nikiforos Sarantakos. About all the part-time seer who tremblingly put for fear the euro crisis gold coins under the salami slices of their frozen pizzas And then came the day that revealed to me in almost Sarantakos fraudulent casualness that my plan had not worked out really. 'Why all of a sudden> my plan', 'I heard myself asking yet, but it was in the garage I realized what that really meant minus 211.2 percent in my portfolio report: personal bankruptcy, gutter and Drug Addiction with subsequent loss of the incisors . Not even the study of my friend, I would still be able to finance. The only way out was to me as quickly as secretly again to draw from this unspeakable Greek shit - I had to be Überman! By far the bekloppteste story about 'idiot' Simon Peter! »Tommy Jaud – Deutschlands witzigste Seite.« Alex Dengler, Bild am Sonntag

", - Sku = "P-1003", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Überman: The novel", - Price = 16.99M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true - }; - - AddProductPicture(productBooksUberMan, "0000932_uberman-der-roman.jpeg"); - productBooksUberMan.ProductCategories.Add(new ProductCategory { Category = categories["SPIEGEL-Bestseller"], DisplayOrder = 1 }); - - productBooksUberMan.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productBooksUberMan.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 7) - }); - productBooksUberMan.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - - #endregion productBooksUberMan - - #region productBooksGefangeneDesHimmels - - var productBooksGefangeneDesHimmels = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "The Prisoner of Heaven: A Novel", - ShortDescription = "(Hardcover)", - FullDescription = "

By Shadow of the Wind and The Angel's Game, the new large-Barcelona novel by Carlos Ruiz Zafón. - Barcelona, Christmas 1957th The bookseller Daniel Sempere and his friend Fermín be drawn again into a great adventure. In the continuation of his international success with Carlos Ruiz Zafón takes the reader on a fascinating journey into his Barcelona. Creepy and fascinating, with incredible suction power and humor, the novel, the story of Fermin, who 'rose from the dead, and the key to the future is.' Fermin's life story linking the threads of The Shadow of the Wind with those from The Angel's Game. A masterful puzzle that keeps the reader around the world in thrall.

Product Hardcover: 416 pages Publisher: S. Fischer Verlag; 1 edition (October 25, 2012) Language: German ISBN-10: 3,100,954,025 ISBN-13: 978-3100954022 Original title: El prisionero del cielo Size and / or weight: 21.4 x 13.6 cm x 4.4

", - ProductTemplateId = productTemplate.Id, - Sku = "P-1004", - AllowCustomerReviews = true, - Published = true, - MetaTitle = "The Prisoner of Heaven: A Novel", - Price = 22.99M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id - }; - - AddProductPicture(productBooksGefangeneDesHimmels, "0000935_der-gefangene-des-himmels-roman_300.jpeg"); - productBooksGefangeneDesHimmels.ProductCategories.Add(new ProductCategory { Category = categories["SPIEGEL-Bestseller"], DisplayOrder = 1 }); - - productBooksGefangeneDesHimmels.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Edition -> bound - SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - - productBooksGefangeneDesHimmels.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Category -> bound - SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 7) - }); - productBooksGefangeneDesHimmels.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Language -> German - SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - - #endregion productBooksGefangeneDesHimmels - - #region productBooksBestGrillingRecipes - - var productBooksBestGrillingRecipes = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Best Grilling Recipes", - ShortDescription = "More Than 100 Regional Favorites Tested and Perfected for the Outdoor Cook (Hardcover)", - FullDescription = "

Take a winding cross-country trip and you'll discover barbecue shacks with offerings like tender-smoky Baltimore pit beef and saucy St. Louis pork steaks. To bring you the best of these hidden gems, along with all the classics, the editors of Cook's Country magazine scoured the country, then tested and perfected their favorites. HEre traditions large and small are brought into the backyard, from Hawaii's rotisserie favorite, the golden-hued Huli Huli Chicken, to fall-off-the-bone Chicago Barbecued Ribs. In Kansas City, they're all about the sauce, and for our saucy Kansas City Sticky Ribs, we found a surprise ingredient-root beer. We also tackle all the best sides.

Not sure where or how to start? This cookbook kicks off with an easy-to-follow primer that will get newcomers all fired up. Whether you want to entertain a crowd or just want to learn to make perfect burgers, Best Grilling Recipes shows you the way.

", - ProductTemplateId = productTemplate.Id, - Sku = "P-1005", - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Best Grilling Recipes", - Price = 27.00M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id - }; - - AddProductPicture(productBooksBestGrillingRecipes, "product_bestgrillingrecipes.jpg"); - productBooksBestGrillingRecipes.ProductCategories.Add(new ProductCategory { Category = categories["Cook and enjoy"], DisplayOrder = 1 }); - - productBooksBestGrillingRecipes.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Edition -> bound - SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productBooksBestGrillingRecipes.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Category -> cook & bake - SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 8) - }); - productBooksBestGrillingRecipes.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Language -> German - SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) - }); - - #endregion productBooksBestGrillingRecipes - - #region productBooksCookingForTwo - - var productBooksCookingForTwo = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Cooking for Two", - ShortDescription = "More Than 200 Foolproof Recipes for Weeknights and Special Occasions (Hardcover)", - FullDescription = "

In Cooking for Two, the test kitchen's goal was to take traditional recipes and cut them down to size to serve just twowith tailored cooking techniques and smart shopping tips that will cut down on wasted food and wasted money. Great lasagna starts to lose its luster when you're eating the leftovers for the fourth day in a row. While it may seem obvious that a recipe for four can simply be halved to work, our testing has proved that this is not always the case; cooking with smaller amounts of ingredients often requires different preparation techniques, cooking time, temperature, and the proportion of ingredients. This was especially true as we worked on scaled-down desserts; baking is an unforgiving science in which any changes in recipe amounts often called for changes in baking times and temperatures.

Hardcover: 352 pages
Publisher: America's Test Kitchen (May 2009)
Language: English
ISBN-10: 1933615435
ISBN-13: 978-1933615431

", - ProductTemplateId = productTemplate.Id, - Sku = "P-1006", - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Cooking for Two", - Price = 27.00M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = secondDeliveryTime.Id - }; - - AddProductPicture(productBooksCookingForTwo, "product_cookingfortwo.jpg"); - productBooksCookingForTwo.ProductCategories.Add(new ProductCategory { Category = categories["Cook and enjoy"], DisplayOrder = 1 }); - - productBooksCookingForTwo.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Edition -> bound - SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productBooksCookingForTwo.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Category -> cook & bake - SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 8) - }); - productBooksCookingForTwo.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Language -> German - SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) - }); - - #endregion productBooksCookingForTwo - - #region productBooksAutosDerSuperlative - - var productBooksAutosDerSuperlative = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Car of superlatives: the strongest, the first, the most beautiful, the fastest", - ShortDescription = "Hardcover", - FullDescription = "

For some, the car is only a useful means of transportation. For everyone else, there are 'cars - The Ultimate Guide' of art-connoisseur Michael Doerflinger. With authentic images, all important data and a lot of information can be presented to the fastest, most innovative, the strongest, the most unusual and the most successful examples of automotive history. A comprehensive manual for the specific reference and extensive browsing.

", - Sku = "P-1007", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Car of superlatives", - Price = 14.95M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id - }; - - AddProductPicture(productBooksAutosDerSuperlative, "0000944_autos-der-superlative-die-starksten-die-ersten-die-schonsten-die-schnellsten.jpeg"); - productBooksAutosDerSuperlative.ProductCategories.Add(new ProductCategory { Category = categories["Books"], DisplayOrder = 1 }); - - productBooksAutosDerSuperlative.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Edition -> bound - SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productBooksAutosDerSuperlative.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Category -> cars - SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 6) - }); - productBooksAutosDerSuperlative.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Language -> German - SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - - #endregion productBooksAutosDerSuperlative - - #region productBooksBildatlasMotorraeder - - var productBooksBildatlasMotorraeder = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Picture Atlas Motorcycles: With more than 350 brilliant images", - ShortDescription = "Hardcover", - FullDescription = "

Motorcycles are like no other means of transportation for the great dream of freedom and adventure. This richly illustrated atlas image portrayed in brilliant color photographs and informative text, the most famous bikes of the world's motorcycle history. From the primitive steam engine under the saddle of the late 19th Century up to the hugely powerful, equipped with the latest electronics and computer technology superbikes of today he is an impressive picture of the development and fabrication of noble and fast-paced motorcycles. The myth of the motorcycle is just as much investigated as a motorcycle as a modern lifestyle product of our time. Country-specific, company-historical background information and interesting stories and History about the people who preceded drove one of the seminal inventions of recent centuries and evolved, make this comprehensive illustrated book an incomparable reference for any motorcycle enthusiast and technology enthusiasts.

• Extensive history of the legendary models of all major motorcycle manufacturers worldwide
• With more than 350 brilliant color photographs and fascinating background information relating
• With informative drawings, stunning detail shots and explanatory info-boxes

content • 1817 1913: The beginning of a success story
• 1914 1945: mass mobility
• 1946 1990: Battle for the World Market
• In 1991: The modern motorcycle
• motorcycle cult object: From Transportation to Lifestyle

", - Sku = "P-1008", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Picture Atlas Motorcycles", - Price = 14.99M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id - }; - - AddProductPicture(productBooksBildatlasMotorraeder, "0000942_bildatlas-motorrader-mit-mehr-als-350-brillanten-abbildungen.jpeg"); - productBooksBildatlasMotorraeder.ProductCategories.Add(new ProductCategory { Category = categories["Books"], DisplayOrder = 1 }); - - productBooksBildatlasMotorraeder.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Edition -> bound - SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productBooksBildatlasMotorraeder.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Category -> non-fiction - SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) - }); - productBooksBildatlasMotorraeder.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Language -> German - SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); + Name = "Clear email queue", + CronExpression = "0 2 * * *", // At 02:00 + Type = "SmartStore.Services.Messages.QueuedMessagesClearTask, SmartStore.Services", + Enabled = true, + StopOnError = false, + }, + new ScheduleTask + { + Name = "Cleanup temporary files", + CronExpression = "30 3 * * *", // At 03:30 + Type = "SmartStore.Services.Common.TempFileCleanupTask, SmartStore.Services", + Enabled = true, + StopOnError = false + }, + new ScheduleTask + { + Name = "Rebuild XML Sitemap", + CronExpression = "45 3 * * *", + Type = "SmartStore.Services.Seo.RebuildXmlSitemapTask, SmartStore.Services", + Enabled = true, + StopOnError = false + }, + new ScheduleTask + { + Name = "Update assignments of customers to customer roles", + CronExpression = "15 2 * * *", // At 02:15 + Type = "SmartStore.Services.Customers.TargetGroupEvaluatorTask, SmartStore.Services", + Enabled = true, + StopOnError = false + }, + new ScheduleTask + { + Name = "Update assignments of products to categories", + CronExpression = "20 2 * * *", // At 02:20 + Type = "SmartStore.Services.Catalog.ProductRuleEvaluatorTask, SmartStore.Services", + Enabled = true, + StopOnError = false + } + }; + this.Alter(entities); + return entities; + } - #endregion productBooksBildatlasMotorraeder + #endregion - #region productBooksAutoBuch + #region Sample data creators - var productBooksAutoBuch = new Product + public IList ForumGroups() + { + var forumGroupGeneral = new ForumGroup { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "The Car Book. The great history with over 1200 models", - ShortDescription = "Hardcover", - FullDescription = "

Makes, models, milestones
The car - for some, a utensil for other expression of lifestyle, cult object and passion. Few inventions have changed their lives as well as the good of the automobile 125 years ago - one more reason for this extensive chronicle. The car-book brings the history of the automobile to life. It presents more than 1200 important models - Karl Benz 'Motorwagen about legendary cult car to advanced hybrid vehicles. It explains the milestones in engine technology and portrays the big brands and their designers. Characteristics from small cars to limousines and send racing each era invite you to browse and discover. The most comprehensive and bestbebildert illustrated book on the market - it would be any car lover!

Hardcover: 360 pages
Publisher: Dorling Kindersley Publishing (September 27, 2012)
Language: German
ISBN-10: 3,831,022,062
ISBN-13: 978-3831022069
Size and / or weight: 30.6 x 25.8 x 2.8 cm

", - Sku = "P-1009", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "The Car Book", - Price = 29.95M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id + Name = "General", + Description = "", + DisplayOrder = 1 }; - AddProductPicture(productBooksAutoBuch, "0000947_das-auto-buch-die-grose-chronik-mit-uber-1200-modellen_300.jpeg"); - productBooksAutoBuch.ProductCategories.Add(new ProductCategory { Category = categories["Books"], DisplayOrder = 1 }); - - productBooksAutoBuch.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Edition -> bound - SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productBooksAutoBuch.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + var entities = new List { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Category -> non-fiction - SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) - }); - productBooksAutoBuch.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Language -> German - SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); + forumGroupGeneral + }; - #endregion productBooksAutoBuch + this.Alter(entities); + return entities; + } - #region productBooksFastCars + public IList Forums() + { + var group = _ctx.Set().FirstOrDefault(c => c.DisplayOrder == 1); - var productBooksFastCars = new Product + var newProductsForum = new Forum { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Fast Cars, Image Calendar 2013", - ShortDescription = "spiral bound", - FullDescription = "

Large Size: 48.5 x 34 cm.
This impressive picture calendar with silver ring binding thrilled with impressive photographs of exclusive sports cars. Who understands cars not only as a pure commercial vehicles, will find the most sought-after status symbols at all: fast cars are effectively set to the razor sharp and vivid photos in scene and convey freedom, speed, strength and the highest technical perfection. Starting with the 450-horsepower Maserati GranTurismo MC Stradale on the stylish, luxurious Aston Martin Virage Volante accompany up to the produced only in small numbers Mosler Photon MT900S the fast racer with style and elegance through the months.

Besides the calendar draws another picture to look at interesting details. There are the essential information on any sports car in the English language. After this year, the high-quality photos are framed an eye-catcher on the wall of every lover of fast cars. Even as a gift this beautiful years companion is wonderfully suited. 12 calendar pages, neutral and discreet held calendar. Printed on paper from sustainable forests. For lovers of luxury vintage cars also available in ALPHA EDITION: the large format image Classic Cars Calendar 2013: ISBN 9,783,840,733,376th

Spiral-bound: 14 pages
Publisher: Alpha Edition (June 1, 2012)
Language: German
ISBN-10: 3,840,733,383
ISBN-13: 978-3840733383
Size and / or weight: 48.8 x 34.2 x 0.6 cm

", - Sku = "P-1010", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Fast Cars", - Price = 16.95M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id + ForumGroup = group, + Name = "New Products", + Description = "Discuss new products and industry trends", + NumTopics = 0, + NumPosts = 0, + LastPostCustomerId = 0, + LastPostTime = null, + DisplayOrder = 1 }; - AddProductPicture(productBooksFastCars, "0000946_fast-cars-bildkalender-2013_300.jpeg"); - productBooksFastCars.ProductCategories.Add(new ProductCategory { Category = categories["Books"], DisplayOrder = 1 }); - - productBooksFastCars.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Edition -> bound - SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productBooksFastCars.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Category -> cars - SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 6) - }); - productBooksFastCars.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Language -> German - SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - - #endregion productBooksFastCars - - #region productBooksMotorradAbenteuer - - var productBooksMotorradAbenteuer = new Product + var packagingShippingForum = new Forum { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Motorcycle Adventures: Riding for travel enduros", - ShortDescription = "Hardcover", - FullDescription = "

Modern travel enduro bikes are ideal for adventure travel. Their technique is complex, their weight considerably. The driving behavior changes depending on the load and distance.

Before the tour starts, you should definitely attend a training course. This superbly illustrated book presents practical means of many informative series photos the right off-road driving in mud and sand, gravel and rock with and without luggage. In addition to the driving course full of information and tips on choosing the right motorcycle for travel planning and practical issues may be on the way.

", - Sku = "P-1011", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Motorcycle Adventures", - Price = 24.90M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = secondDeliveryTime.Id + ForumGroup = group, + Name = "Packaging & Shipping", + Description = "Discuss packaging & shipping", + NumTopics = 0, + NumPosts = 0, + LastPostTime = null, + DisplayOrder = 20 }; - AddProductPicture(productBooksMotorradAbenteuer, "0000943_motorrad-abenteuer-fahrtechnik-fur-reise-enduros.jpeg"); - productBooksMotorradAbenteuer.ProductCategories.Add(new ProductCategory { Category = categories["Books"], DisplayOrder = 1 }); - productBooksMotorradAbenteuer.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + var entities = new List { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Edition -> bound - SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productBooksMotorradAbenteuer.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Category -> cars - SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 9) - }); - productBooksMotorradAbenteuer.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Language -> German - SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - - #endregion productBooksMotorradAbenteuer - - #endregion category books + newProductsForum, packagingShippingForum + }; - #region Instant Download Music / Digital Products + this.Alter(entities); + return entities; + } - #region product Books Stone of the Wise + public IList Discounts() + { + var ruleSets = _ctx.Set().Expand(x => x.Rules); - var productBooksStoneOfTheWise = new Product + var couponCodeDiscount = new Discount { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Ebook 'Stone of the Wise' in 'Lorem ipsum'", - IsEsd = true, - ShortDescription = "E-Book, 465 pages", - FullDescription = "", - Sku = "P-6001", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Ebook 'Stone of the Wise' in 'Lorem ipsum'", - Price = 9.90M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsDownload = true, - HasSampleDownload = true + Name = "Sample discount with coupon code", + DiscountType = DiscountType.AssignedToSkus, + DiscountLimitation = DiscountLimitationType.Unlimited, + UsePercentage = false, + DiscountAmount = 10, + RequiresCouponCode = true, + CouponCode = "123" }; - AddProductPicture(productBooksStoneOfTheWise, "stone_of_wisdom.jpg"); - productBooksStoneOfTheWise.ProductCategories.Add(new ProductCategory { Category = categories["Digital Products"], DisplayOrder = 1 }); - - productBooksStoneOfTheWise.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Edition -> bound - SpecificationAttributeOption = specAttributes[13].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productBooksStoneOfTheWise.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + var orderTotalDiscount = new Discount { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Category -> cars - SpecificationAttributeOption = specAttributes[14].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 6) - }); - productBooksStoneOfTheWise.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 3, - // Language -> German - SpecificationAttributeOption = specAttributes[12].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - - #endregion product Books Stone of the Wise - - - #region Antonio Vivaldi: then spring + Name = "20% order total discount", + DiscountType = DiscountType.AssignedToOrderTotal, + DiscountLimitation = DiscountLimitationType.Unlimited, + UsePercentage = true, + DiscountPercentage = 20, + StartDateUtc = new DateTime(2020, 2, 1), + EndDateUtc = new DateTime(2020, 2, 10) + }; + orderTotalDiscount.RuleSets.Add(ruleSets.FirstOrDefault(x => x.Rules.Any(y => y.RuleType == "CartOrderCount"))); - var productInstantDownloadVivaldi = new Product + var weekendDiscount = new Discount { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Antonio Vivaldi: spring", - IsEsd = true, - ShortDescription = "MP3, 320 kbit/s", - FullDescription = "

Antonio Vivaldi: Spring

Antonio Lucio Vivaldi (March 4, 1678 in Venice, † 28 July 1741 in Vienna) was a Venetian composer and violinist in the Baroque.

The Four Seasons (Le quattro stagioni Italian) is perhaps the most famous works of Antonio Vivaldi. It's four violin concertos with extra-musical programs, each portraying a concert season. This is the individual concerts one - probably written by Vivaldi himself - Sonnet preceded by consecutive letters in front of the lines and in the appropriate places in the score arrange the verbal description of the music.

Vivaldi had previously always been experimenting with non-musical programs, which often reflected in his tracks, the exact interpretation of the individual points score is unusual for him. His experience as a virtuoso violinist allowed him access to particularly effective playing techniques, as an opera composer, he had developed a strong sense of effects, both of which benefitted from him.

As the title suggests, especially to imitate natural phenomena - gentle winds, severe storms and thunderstorms are elements that are common to all four concerts. There are also various birds and even a dog, further human activities such as hunting, a barn dance, ice skating, including stumbling and falling to the heavy sleep of a drunkard.

The work dates from 1725 and is available in two print editions, which appeared more or less simultaneously published in Amsterdam and Paris.

", - Sku = "P-1016", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Antonio Vivaldi: spring", - Price = 1.99M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsDownload = true, - HasSampleDownload = true + Name = "5% on weekend orders", + DiscountType = DiscountType.AssignedToOrderSubTotal, + DiscountLimitation = DiscountLimitationType.Unlimited, + UsePercentage = true, + DiscountPercentage = 5, + StartDateUtc = new DateTime(2020, 3, 1), + EndDateUtc = new DateTime(2020, 3, 10) }; + weekendDiscount.RuleSets.Add(ruleSets.FirstOrDefault(x => x.Rules.Any(y => y.RuleType == "Weekday"))); - AddProductPicture(productInstantDownloadVivaldi, "vivaldi.jpg"); - productInstantDownloadVivaldi.ProductCategories.Add(new ProductCategory { Category = categories["Digital Products"], DisplayOrder = 1 }); - - productInstantDownloadVivaldi.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // mp3 quality > 320 kbit/S - SpecificationAttributeOption = specAttributes[18].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) - }); - productInstantDownloadVivaldi.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + var manufacturersDiscount = new Discount { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 2, - // genre > classic - SpecificationAttributeOption = specAttributes[19].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 6) - }); - - #endregion Antonio Vivildi: then spring + Name = "10% for certain manufacturers", + DiscountType = DiscountType.AssignedToManufacturers, + DiscountLimitation = DiscountLimitationType.Unlimited, + UsePercentage = true, + DiscountPercentage = 10, + StartDateUtc = new DateTime(2020, 4, 5), + EndDateUtc = new DateTime(2020, 4, 10) + }; - #region Beethoven für Elise + var categoriesDiscount = new Discount + { + Name = "20% for certain categories", + DiscountType = DiscountType.AssignedToCategories, + DiscountLimitation = DiscountLimitationType.Unlimited, + UsePercentage = true, + DiscountPercentage = 20, + StartDateUtc = new DateTime(2020, 6, 1), + EndDateUtc = new DateTime(2020, 6, 30) + }; - var productInstantDownloadBeethoven = new Product + var productsDiscount = new Discount { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Ludwig van Beethoven: For Elise", - IsEsd = true, - ShortDescription = "Ludwig van Beethoven's most popular compositions", - FullDescription = "

The score was not published until 1867, 40 years after the composer's death in 1827. The discoverer of the piece, Ludwig Nohl, affirmed that the original autographed manuscript, now lost, was dated 27 April 1810.[4] The version of \"Für Elise\" we hear today is an earlier version that was transcribed by Ludwig Nohl. There is a later version, with drastic changes to the accompaniment which was transcribed from a later manuscript by Barry Cooper. The most notable difference is in the first theme, the left-hand arpeggios are delayed by a 16th note beat. There are a few extra bars in the transitional section into the B section; and finally, the rising A minor arpeggio figure is moved later into the piece. The tempo marking Poco Moto is believed to have been on the manuscript that Ludwig Nohl transcribed (now lost). The later version includes the marking Molto Grazioso. It is believed that Beethoven intended to add the piece to a cycle of bagatelles.[citation needed]

Therese Malfatti, widely believed to be the dedicatee of \"Für Elise\" The pianist and musicologist Luca Chiantore (es) argued in his thesis and his 2010 book Beethoven al piano that Beethoven might not have been the person who gave the piece the form that we know today. Chiantore suggested that the original signed manuscript, upon which Ludwig Nohl claimed to base his transcription, may never have existed.[5] On the other hand, the musicologist Barry Cooper stated, in a 1984 essay in The Musical Times, that one of two surviving sketches closely resembles the published version.[6]

", - Sku = "P-1017", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Ludwig van Beethoven: Für Elise", - ShowOnHomePage = true, - Price = 1.89M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsDownload = true, - HasSampleDownload = true + Name = "25% on certain products", + DiscountType = DiscountType.AssignedToSkus, + DiscountLimitation = DiscountLimitationType.Unlimited, + UsePercentage = true, + DiscountPercentage = 25, + StartDateUtc = new DateTime(2020, 5, 10), + EndDateUtc = new DateTime(2020, 5, 15) }; - AddProductPicture(productInstantDownloadBeethoven, "Beethoven.jpg"); - productInstantDownloadBeethoven.ProductCategories.Add(new ProductCategory { Category = categories["Digital Products"], DisplayOrder = 1 }); - productInstantDownloadBeethoven.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // mp3 quality > 320 kbit/S - SpecificationAttributeOption = specAttributes[18].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) - }); - productInstantDownloadBeethoven.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + var entities = new List { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 2, - // genre > classic - SpecificationAttributeOption = specAttributes[19].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 6) - }); - - #endregion Beethoven für Elise + couponCodeDiscount, orderTotalDiscount, weekendDiscount, + manufacturersDiscount, categoriesDiscount, productsDiscount + }; - #endregion Instant Download Music + Alter(entities); + return entities; + } - #region watches + public IList DeliveryTimes() + { + var entities = new List() + { + new DeliveryTime + { + Name = "available and ready to ship", + DisplayOrder = 0, + ColorHexValue = "#008000", + MinDays = 1, + MaxDays = 3 + }, + new DeliveryTime + { + Name = "2-5 woking days", + DisplayOrder = 1, + ColorHexValue = "#FFFF00", + MinDays = 2, + MaxDays = 5 + }, + new DeliveryTime + { + Name = "7 working days", + DisplayOrder = 2, + ColorHexValue = "#FF9900", + MinDays = 7, + MaxDays = 14 + }, + }; + this.Alter(entities); + return entities; + } - #region productTRANSOCEANCHRONOGRAPH + public IList QuantityUnits() + { + var count = 0; + var entities = new List(); - var productTRANSOCEANCHRONOGRAPH = new Product + var quPluralEn = new Dictionary { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "TRANSOCEAN CHRONOGRAPH", - ShortDescription = "The Transocean Chronograph interprets the factual aesthetics of classic chronographs of the 1950s and 1960s in a decidedly contemporary style.", - FullDescription = "

The Transocean Chronograph interprets the factual aesthetics of classic chronographs of the 1950s and 1960s in a decidedly contemporary style. The high-performance caliber 01, designed and manufactured entirely in the Breitling studios, works in its form, which is reduced to the essentials.

Caliber Breitling 01 (Manufactory caliber)
Movement Mechanically, Automatic
Power reserve Min. 70 hour
Chronograph 1/4-Seconds, 30 Minutes, 12 Hours
Half vibrations 28 800 a/h
Rubies 47 Rubies
Calendar Window
", - Sku = "P-9001", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "TRANSOCEAN CHRONOGRAPH", - ShowOnHomePage = true, - Price = 24110.00M, - OldPrice = 26230.00M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id + { "Piece", "Pieces" }, + { "Box", "Boxes" }, + { "Parcel", "Parcels" }, + { "Palette", "Pallets" }, + { "Unit", "Units" }, + { "Sack", "Sacks" }, + { "Bag", "Bags" }, + { "Can", "Cans" }, + { "Packet", "Packets" }, + { "Bar", "Bars" }, + { "Bottle", "Bottles" }, + { "Glass", "Glasses" }, + { "Bunch", "Bunches" }, + { "Roll", "Rolls" }, + { "Cup", "Cups" }, + { "Bundle", "Bundles" }, + { "Barrel", "Barrels" }, + { "Set", "Sets" }, + { "Bucket", "Buckets" } }; - AddProductPicture(productTRANSOCEANCHRONOGRAPH, "product_transocean-chronograph.jpg"); - productTRANSOCEANCHRONOGRAPH.ProductCategories.Add(new ProductCategory { Category = categories["Watches"], DisplayOrder = 1 }); + foreach (var qu in quPluralEn) + { + entities.Add(new QuantityUnit + { + Name = qu.Key, + NamePlural = qu.Value, + Description = qu.Key, + IsDefault = qu.Key == "Piece", + DisplayOrder = count++ + }); + } - productTRANSOCEANCHRONOGRAPH.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Breitling"], DisplayOrder = 1 }); + this.Alter(entities); + return entities; + } - productTRANSOCEANCHRONOGRAPH.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + public IList PollAnswers() + { + var pollAnswer1 = new PollAnswer() { - AllowFiltering = true, - ShowOnProductPage = true, + Name = "Excellent", DisplayOrder = 1, - // offer > promotion - SpecificationAttributeOption = specAttributes[22].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) - }); - productTRANSOCEANCHRONOGRAPH.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + }; + var pollAnswer2 = new PollAnswer() { - AllowFiltering = true, - ShowOnProductPage = true, + Name = "Good", DisplayOrder = 2, - // manufacturer > Breitling - SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 18) - }); - productTRANSOCEANCHRONOGRAPH.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + }; + var pollAnswer3 = new PollAnswer() { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // housing > steel - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productTRANSOCEANCHRONOGRAPH.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + Name = "Poor", + DisplayOrder = 3, + }; + var pollAnswer4 = new PollAnswer() { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // material -> leather - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) - }); - productTRANSOCEANCHRONOGRAPH.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + Name = "Very bad", + DisplayOrder = 4, + }; + var pollAnswer5 = new PollAnswer() { - AllowFiltering = true, - ShowOnProductPage = true, + Name = "Daily", DisplayOrder = 5, - // Gender -> gentlemen - SpecificationAttributeOption = specAttributes[7].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productTRANSOCEANCHRONOGRAPH.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + }; + var pollAnswer6 = new PollAnswer() { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // movement -> mechanical, self winding - SpecificationAttributeOption = specAttributes[9].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productTRANSOCEANCHRONOGRAPH.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + Name = "Once a week", + DisplayOrder = 6, + }; + var pollAnswer7 = new PollAnswer() { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // diameter -> 44mm - SpecificationAttributeOption = specAttributes[24].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) - }); - productTRANSOCEANCHRONOGRAPH.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + Name = "Every two weeks", + DisplayOrder = 7, + }; + var pollAnswer8 = new PollAnswer() { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // closure -> folding clasp - SpecificationAttributeOption = specAttributes[25].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) - }); + Name = "Once a month", + DisplayOrder = 8, + }; - #endregion productTRANSOCEANCHRONOGRAPH + var entities = new List + { + pollAnswer1, pollAnswer2, pollAnswer3, pollAnswer4, pollAnswer5, pollAnswer6, pollAnswer7, pollAnswer8 + }; - #region productTissotT-TouchExpertSolar + this.Alter(entities); + return entities; + } - var productTissotTTouchExpertSolar = new Product + public IList Polls() + { + var defaultLanguage = _ctx.Set().FirstOrDefault(); + var poll1 = new Poll { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Tissot T-Touch Expert Solar", - ShortDescription = "The beam of the Tissot T-Touch Expert Solar on the dial ensures that the Super-LumiNova®-coated indexes and hands illuminate in the dark, and on the other hand, charges the battery of the watch. This model is a force package in every respect.", - FullDescription = "

The T-Touch Expert Solar is an important new model in the Tissot range.

Tissot’s pioneering spirit is what led to the creation of tactile watches in 1999.

Today, it is the first to present a touch-screen watch powered by solar energy, confirming its position as leader in tactile technology in watchmaking.

Extremely well designed, it showcases clean lines in both sports and timeless pieces.

Powered by solar energy with 25 features including weather forecasting, altimeter, second time zone and a compass it is the perfect travel companion.

", - Sku = "P-9002", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, + Language = defaultLanguage, + Name = "How do you like the shop?", + SystemKeyword = "Blog", Published = true, - MetaTitle = "Tissot T-Touch Expert Solar", - ShowOnHomePage = true, - Price = 969.00M, - OldPrice = 990.00M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id + DisplayOrder = 1, }; - AddProductPicture(productTissotTTouchExpertSolar, "product_tissot-t-touch-expert-solar.jpg", "tissot-1"); - AddProductPicture(productTissotTTouchExpertSolar, "product_tissot-t-touch-expert-solar-t091_2.jpg", "tissot-2"); - - productTissotTTouchExpertSolar.ProductCategories.Add(new ProductCategory { Category = categories["Watches"], DisplayOrder = 1 }); - - productTissotTTouchExpertSolar.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Tissot"], DisplayOrder = 1 }); - - productTissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + poll1.PollAnswers.Add(new PollAnswer { - AllowFiltering = true, - ShowOnProductPage = true, + Name = "Excellent", DisplayOrder = 1, - // offer > best price - SpecificationAttributeOption = specAttributes[22].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 8) }); - productTissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + + poll1.PollAnswers.Add(new PollAnswer { - AllowFiltering = true, - ShowOnProductPage = true, + Name = "Good", DisplayOrder = 2, - // manufacturer > Tissot - SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 17) - }); - productTissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // housing > steel - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productTissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // material -> silicone - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 7) - }); - productTissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // Gender -> gentlemen - SpecificationAttributeOption = specAttributes[7].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productTissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // movement -> Automatic, self-winding - SpecificationAttributeOption = specAttributes[9].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) }); - productTissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + + poll1.PollAnswers.Add(new PollAnswer { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // diameter -> 44mm - SpecificationAttributeOption = specAttributes[24].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + Name = "Poor", + DisplayOrder = 3, }); - productTissotTTouchExpertSolar.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + + poll1.PollAnswers.Add(new PollAnswer { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // closure -> thorn close - SpecificationAttributeOption = specAttributes[25].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + Name = "Very bad", + DisplayOrder = 4, }); - #endregion productTissotT-TouchExpertSolar - - #region productSeikoSRPA49K1 - var productSeikoSRPA49K1 = new Product + var poll2 = new Poll { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Seiko Mechanical Automatic SRPA49K1", - ShortDescription = "Seiko Mechanical Automatic SRPA49K1", - FullDescription = "

Seiko 5 Sports Automatic Watch SRPA49K1 SRPA49

  • Unidirectional Rotating Bezel
  • Day And Date Display
  • See Through Case Back
  • 100M Water Resistance
  • Stainless Steel Case
  • Automatic Movement
  • 24 Jewels
  • Caliber: 4R36
", - Sku = "P-9003", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, + Language = defaultLanguage, + Name = "How often do you buy online?", + SystemKeyword = "Blog", Published = true, - MetaTitle = "Seiko Mechanical Automatic SRPA49K1", - ShowOnHomePage = true, - Price = 269.00M, - OldPrice = 329.00M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id + DisplayOrder = 2, }; - AddProductPicture(productSeikoSRPA49K1, "product_SeikoSRPA49K1.jpg"); - productSeikoSRPA49K1.ProductCategories.Add(new ProductCategory { Category = categories["Watches"], DisplayOrder = 1 }); - - productSeikoSRPA49K1.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Seiko"], DisplayOrder = 1 }); - - productSeikoSRPA49K1.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + poll2.PollAnswers.Add(new PollAnswer { - AllowFiltering = true, - ShowOnProductPage = true, + Name = "Daily", DisplayOrder = 1, - // housing > steel - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) }); - productSeikoSRPA49K1.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // material -> stainless steel - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productSeikoSRPA49K1.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + + poll2.PollAnswers.Add(new PollAnswer { - AllowFiltering = true, - ShowOnProductPage = true, + Name = "Once a week", DisplayOrder = 2, - // manufacturer > Seiko - SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 16) - }); - productSeikoSRPA49K1.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // Gender -> gentlemen - SpecificationAttributeOption = specAttributes[7].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productSeikoSRPA49K1.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // movement -> quarz - SpecificationAttributeOption = specAttributes[9].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) }); - productSeikoSRPA49K1.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + + poll2.PollAnswers.Add(new PollAnswer { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // closure -> folding clasp - SpecificationAttributeOption = specAttributes[25].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) + Name = "Every two weeks", + DisplayOrder = 3, }); - productSeikoSRPA49K1.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + + poll2.PollAnswers.Add(new PollAnswer { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // diameter -> 44mm - SpecificationAttributeOption = specAttributes[24].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + Name = "Once a month", + DisplayOrder = 4, }); - #endregion productSeikoSRPA49K1 + var entities = new List + { + poll1, poll2 + }; - #region productWatchesCertinaDSPodiumBigSize + this.Alter(entities); + return entities; + } - var productWatchesCertinaDSPodiumBigSize = new Product + public IList Campaigns() + { + var entities = new List { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Name = "Certina DS Podium Big Size", - ShortDescription = "C001.617.26.037.00", - FullDescription = "

Since 1888, Certina has maintained an enviable reputation for its excellent watches and reliable movements. From the time of its integration into the SMH (today's Swatch Group) in the early 1980s, every Certina has been equipped with a high-quality ETA movement.

In a quartz watch movement, high-frequency oscillations are generated in a tiny synthetic crystal, then divided down electronically to provide the extreme accuracy of the Certina internal clock. A battery supplies the necessary energy.

The quartz movement is sometimes equipped with an end-of-life (EOL) indicator. When the seconds hand begins moving in four-second increments, the battery should be replaced within two weeks.

An automatic watch movement is driven by a rotor. Arm and wrist movements spin the rotor, which in turn winds the main spring. Energy is continuously produced, eliminating the need for a battery. The rate precision therefore depends on a rigorous manufacturing process and the original calibration, as well as the lifestyle of the user.

Most automatic movements are driven by an offset rotor. To earn the title of chronometer, a watch must be equipped with a movement that has obtained an official rate certificate from the COSC (Contrôle Officiel Suisse des Chronomètres). To obtain this, precision tests in different positions and at different temperatures must be carried out. These tests take place over a 15-day period. Thermocompensated means that the effective temperature inside the watch is measured and taken into account when improving precision. This allows fluctuations in the rate precision of a normal quartz watch due to temperature variations to be reduced by several seconds a week. The precision is 20 times more accurate than on a normal quartz watch, i.e. +/- 10 seconds per year (0.07 seconds/day).

", - Sku = "P-9004", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Certina DS Podium Big Size", - ShowOnHomePage = true, - Price = 479.00M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = thirdDeliveryTime.Id + new Campaign + { + Name = "Reminder of inactive new customers", + Subject = "New, exciting products are waiting for you to be discovered.", + Body = "

Efficiently unleash client-centric technologies and go forward information. Conveniently benchmark client-focused resources vis-a-vis interdependent paradigms. Synergistically disseminate interdependent supply chains via equity invested internal or 'organic' sources. Objectively exploit seamless growth strategies without orthogonal methodologies. Intrinsicly disseminate bricks-and-clicks web-readiness and e-business e-services.

Objectively develop performance based e-business and interdependent sources. Objectively evolve flexible markets via leveraged interfaces. Professionally deliver focused 'outside the box' thinking rather than global sources. Energistically redefine leveraged supply chains through customized relationships. Dramatically actualize resource sucking content rather than cross-platform e-business.

Seamlessly synthesize vertical mindshare without flexible sources. Distinctively productize timely infrastructures rather than cross-media niches. Dynamically evisculate pandemic convergence and scalable mindshare. Seamlessly embrace fully tested relationships whereas go forward initiatives. Globally actualize user-centric channels.

", + CreatedOnUtc = DateTime.UtcNow, + SubjectToAcl = true + } }; - AddProductPicture(productWatchesCertinaDSPodiumBigSize, "product_certina_ds_podium_big.png"); - productWatchesCertinaDSPodiumBigSize.ProductCategories.Add(new ProductCategory { Category = categories["Watches"], DisplayOrder = 1 }); - - productWatchesCertinaDSPodiumBigSize.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Certina"], DisplayOrder = 1 }); + Alter(entities); + return entities; + } - productWatchesCertinaDSPodiumBigSize.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 1, - // housing > steel - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productWatchesCertinaDSPodiumBigSize.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // material -> leather - SpecificationAttributeOption = specAttributes[8].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 5) - }); - productWatchesCertinaDSPodiumBigSize.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 2, - // manufacturer > Certina - SpecificationAttributeOption = specAttributes[20].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 14) - }); - productWatchesCertinaDSPodiumBigSize.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + public IList RuleSets() + { + // Cart: weekends. + var weekends = new RuleSetEntity { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // Gender -> gentlemen - SpecificationAttributeOption = specAttributes[7].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 1) - }); - productWatchesCertinaDSPodiumBigSize.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + Scope = RuleScope.Cart, + Name = "Weekends", + IsActive = true + }; + weekends.Rules.Add(new RuleEntity { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // movement -> quarz - SpecificationAttributeOption = specAttributes[9].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 3) + RuleType = "Weekday", + Operator = RuleOperator.In, + Value = $"{(int)DayOfWeek.Sunday},{(int)DayOfWeek.Saturday}" }); - productWatchesCertinaDSPodiumBigSize.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + + // Cart: major customers. + var majorCustomers = new RuleSetEntity { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // closure -> folding clasp - SpecificationAttributeOption = specAttributes[25].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) - }); - productWatchesCertinaDSPodiumBigSize.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + Scope = RuleScope.Cart, + Name = "Major customers", + Description = "3 or more orders and current order value at least 200,- Euro.", + IsActive = true + }; + majorCustomers.Rules.Add(new RuleEntity { - AllowFiltering = true, - ShowOnProductPage = true, - DisplayOrder = 5, - // diameter -> 40mm - SpecificationAttributeOption = specAttributes[24].SpecificationAttributeOptions.FirstOrDefault(x => x.DisplayOrder == 2) + RuleType = "CartSubtotal", + Operator = RuleOperator.GreaterThanOrEqualTo, + Value = "200" }); - - #endregion productWatchesCertinaDSPodiumBigSize - - #endregion watches - - #region gaming - - var manuSony = _ctx.Set().First(c => c.Name == "Sony"); - var manuMicrosoft = _ctx.Set().First(c => c.Name == "Microsoft"); - var manuEASports = _ctx.Set().First(c => c.Name == "EA Sports"); - var manuUbisoft = _ctx.Set().First(c => c.Name == "Ubisoft"); - var categoryGaming = _ctx.Set().First(c => c.Alias == "Gaming"); - var categoryGamingAccessories = _ctx.Set().First(c => c.Alias == "Gaming Accessories"); - var categoryGamingGames = _ctx.Set().First(c => c.Alias == "Games"); - var manuWarnerHomme = _ctx.Set().First(c => c.Name == "Warner Home Video Games"); - - #region bundlePs3AssassinCreed - - var productPs3 = new Product + majorCustomers.Rules.Add(new RuleEntity { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Sku = "Sony-PS399000", - Name = "Playstation 4 Pro", - ShortDescription = "The Sony PlayStation 4 Pro is the multi media console for next-generation digital home entertainment. It offers the Blu-ray technology, which enables you to enjoy movies in high definition.", - FullDescription = "
  • PowerPC-base Core @5.2GHz
  • 1 VMX vector unit per core
  • 512KB L2 cache
  • 7 x SPE @5.2GHz
  • 7 x 128b 128 SIMD GPRs
  • 7 x 256MB SRAM for SPE
  • * 1 of 8 SPEs reserved for redundancy total floating point performance: 218 GFLOPS
  • 1.8 TFLOPS floating point performance
  • Full HD (up to 1080p) x 2 channels
  • Multi-way programmable parallel floating point shader pipelines
  • GPU: RSX @550MHz
  • 256MB XDR Main RAM @3.2GHz
  • 256MB GDDR3 VRAM @700MHz
  • Sound: Dolby 5.1ch, DTS, LPCM, etc. (Cell-base processing)
  • Wi-Fi: IEEE 802.11 b/g
  • USB: Front x 4, Rear x 2 (USB2.0)
  • Memory Stick: standard/Duo, PRO x 1
", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - //MetaTitle = "Playstation 4 Super Slim", - MetaTitle = "Playstation 4 Pro", - Price = 189.00M, - OldPrice = 199.99M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id - }; - - AddProductPicture(productPs3, "product_ps4_w_controller.jpg", "ps4-w-controller"); - AddProductPicture(productPs3, "product_ps4_wo_controller.jpg", "ps4-wo-single"); - - productPs3.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); - productPs3.ProductCategories.Add(new ProductCategory { Category = categories["Gaming"], DisplayOrder = 4 }); + RuleType = "CartOrderCount", + Operator = RuleOperator.GreaterThanOrEqualTo, + Value = "3" + }); - var productDualshock4Controller = new Product + // Product: sale. + var saleProducts = new RuleSetEntity { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Sku = "Sony-PS399004", - Name = "DUALSHOCK 4 Wireless Controller", - ShortDescription = "Revolutionary. Intuitive. Precise. A revolutionary controller for a new era of gaming, the DualShock 4 Wireless Controller features familiar PlayStation controls and innovative new additions, such as a touch pad, light bar, and more.", - FullDescription = "
  • Precision Controller for PlayStation 4: The feel, shape, and sensitivity of the DualShock 4’s analog sticks and trigger buttons have been enhanced to offer players absolute control for all games
  • Sharing at your Fingertips: The addition of the Share button makes sharing your greatest gaming moments as easy as a push of a button. Upload gameplay videos and screenshots directly from your system or live-stream your gameplay, all without disturbing the game in progress.
  • New ways to Play: Revolutionary features like the touch pad, integrated light bar, and built-in speaker offer exciting new ways to experience and interact with your games and its 3.5mm audio jack offers a practical personal audio solution for gamers who want to listen to their games in private.
  • Charge Efficiently: The DualShock 4 Wireless Controller can easily be recharged by plugging it into your PlayStation 4 system, even when on standby, or with any standard charger with a micro-USB port.
", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "DUALSHOCK 4 Wireless Controller", - Price = 54.90M, - OldPrice = 59.90M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id + Scope = RuleScope.Product, + Name = "Sale", + Description = "Products with applied discounts.", + IsActive = true }; + saleProducts.Rules.Add(new RuleEntity + { + RuleType = "Discount", + Operator = RuleOperator.IsEqualTo, + Value = "true" + }); - AddProductPicture(productDualshock4Controller, "product_dualshock4.jpg"); - productDualshock4Controller.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); - productDualshock4Controller.ProductCategories.Add(new ProductCategory { Category = categories["Gaming Accessories"], DisplayOrder = 1 }); - - - var productMinecraft = new Product + // Customer: inactive new customers. + var inactiveNewCustomers = new RuleSetEntity { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - //Sku = "Ubi-acreed3", - Sku = "PD-Minecraft4ps4", - Name = "Minecraft - Playstation 4 Edition", - ShortDescription = "Third-person action-adventure title set.", - FullDescription = "

Build! Craft! Explore!

The critically acclaimed Minecraft comes to PlayStation 4, offering bigger worlds and greater draw distance than the PS3 and PS Vita editions.

Create your own world, then, build, explore and conquer. When night falls the monsters appear, so be sure to build a shelter before they arrive.

The world is only limited by your imagination! Bigger worlds and greater draw distance than PS3 and PS Vita Editions Includes all features from the PS3 version Import your PS3 and PS Vita worlds to the PS4 Editition.

", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - //MetaTitle = "Assassin's Creed III", - MetaTitle = "Minecraft - Playstation 4 Edition", - - Price = 49.90M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id + Scope = RuleScope.Customer, + Name = "Inactive new customers", + Description = "One completed order placed at least 90 days ago.", + IsActive = true }; - - AddProductPicture(productMinecraft, "product_minecraft.jpg"); - productMinecraft.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); - productMinecraft.ProductCategories.Add(new ProductCategory { Category = categories["Games"], DisplayOrder = 4 }); + inactiveNewCustomers.Rules.Add(new RuleEntity + { + RuleType = "CompletedOrderCount", + Operator = RuleOperator.IsEqualTo, + Value = "1" + }); + inactiveNewCustomers.Rules.Add(new RuleEntity + { + RuleType = "LastOrderDateDays", + Operator = RuleOperator.GreaterThanOrEqualTo, + Value = "90" + }); - var productBundlePs3AssassinCreed = new Product + // Offer free shipping method for major customers. + var freeShipping = _ctx.Set().FirstOrDefault(x => x.DisplayOrder == 2); + if (freeShipping != null) { - ProductType = ProductType.BundledProduct, - VisibleIndividually = true, - Sku = "Sony-PS399105", - Name = "PlayStation 4 Minecraft Bundle", - ShortDescription = "100GB PlayStation®4 system, 2 × DUALSHOCK®4 wireless controller and Minecraft for PS4 Edition.", - FullDescription = - "
  • Processor

    • Processor Technology : Cell Broadband Engine™
  • General

    • Communication : Ethernet (10BASE-T, 100BASE-TX, 1000BASE-T IEEE 802.11 b/g Wi-Fi
      Bluetooth 2.0 (EDR)
    • Inputs and Outputs : USB 2.0 X 2
  • Graphics

    • Graphics Processor : RSX
  • Memory

    • Internal Memory : 256MB XDR Main RAM
      256MB GDDR3 VRAM
  • Power

    • Power Consumption (in Operation) : Approximately 250 watts
  • Storage

    • Storage Capacity : 2.5' Serial ATA (500GB)
  • Video

    • Resolution : 480i, 480p, 720p, 1080i, 1080p (24p/60p)
  • Weights and Measurements

    • Dimensions (Approx.) : Approximately 11.42\" (W) x 2.56\" (H) x 11.42\" (D) (290mm x 65mm x 290mm)
    • Weight (Approx.) : Approximately 7.055 lbs (3.2 kg)
", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "PlayStation 4 Minecraft Bundle", - Price = 269.97M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id, - ShowOnHomePage = true, - BundleTitleText = "Bundle includes", - BundlePerItemPricing = true, - BundlePerItemShoppingCart = true - }; - - AddProductPicture(productBundlePs3AssassinCreed, "ps4_bundle_minecraft.jpg"); - productBundlePs3AssassinCreed.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); - productBundlePs3AssassinCreed.ProductCategories.Add(new ProductCategory { Category = categories["Gaming"], DisplayOrder = 1 }); + freeShipping.RuleSets.Add(majorCustomers); + } - #endregion bundlePs3AssassinCreed + // Assign rule conditions for inactive new customers to the related customer role. + // We later bind the reminder campaign to it. + var inactiveNewCustomersRole = _ctx.Set().FirstOrDefault(x => x.SystemName == "InactiveNewCustomers"); + if (inactiveNewCustomersRole != null) + { + inactiveNewCustomersRole.RuleSets.Add(inactiveNewCustomers); + } - #region bundlePs4 - var productPs4 = new Product + var entities = new List { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Sku = "Sony-PS410034", - //Sku = "PS410037", - - Name = "PlayStation 4", - ShortDescription = "The best place to play. Working with some of the most creative minds in the industry, PlayStation®4 delivers breathtaking and unique gaming experiences.", - FullDescription = "

The power to perform.

PlayStation®4 was designed from the ground up to ensure that game creators can unleash their imaginations to develop the very best games and deliver new play experiences never before possible. With ultra-fast customized processors and 8GB of high-performance unified system memory, PS4™ is the home to games with rich, high-fidelity graphics and deeply immersive experiences that shatter expectations.

  • Processor

    • Processor Technology : Low power x86-64 AMD 'Jaguar', 8 cores
  • Software

    • Processor : Single-chip custom processor
  • Display

    • Display Technology : HDMI
      Digital Output (optical)
  • General

    • Ethernet ports x speed : Ethernet (10BASE-T, 100BASE-TX, 1000BASE-T); IEEE 802.11 b/g/n; Bluetooth® 2.1 (EDR)
    • Hard disk : Built-in
  • General Specifications

    • Video : BD 6xCAV
      DVD 8xCAV
  • Graphics

    • Graphics Processor : 1.84 TFLOPS, AMD Radeon™ Graphics Core Next engine
  • Interface

    • I/O Port : Super-Speed USB (USB 3.0), AUX
  • Memory

    • Internal Memory : GDDR5 8GB

", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "PlayStation 4", - Price = 399.99M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 3, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id + weekends, majorCustomers, saleProducts, inactiveNewCustomers }; - AddProductPicture(productPs4, "product_sony_ps4.png", "sony-ps4"); - AddProductPicture(productPs4, "product_sony_dualshock4_wirelesscontroller.png", "sony-ps4-dualshock"); - - productPs4.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); - productPs4.ProductCategories.Add(new ProductCategory { Category = categories["Gaming"], DisplayOrder = 5 }); - + Alter(entities); + return entities; + } - var productPs4Camera = new Product + public void FinalizeSamples() + { + // Bind the reminder campaign to the rule conditions for inactive new customers. + var reminderCampaign = _ctx.Set().FirstOrDefault(x => x.SubjectToAcl); + if (reminderCampaign != null) { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Sku = "Sony-PS410040", - Name = "PlayStation 4 Camera", - ShortDescription = "Play, challenge and share your epic gaming moments with PlayStation®Camera and your PS4™. Multiplayer is enhanced through immediate, crystal clear audio and picture-in-picture video sharing.", - FullDescription = "
  • When combined with the DualShock 4 Wireless Controller's light bar, the evolutionary 3D depth-sensing technology in the PlayStation Camera allows it to precisely track a player's position in the room.
  • From navigational voice commands to facial recognition, the PlayStation Camera adds incredible innovation to your gaming.
  • Automatically integrate a picture-in-picture video of yourself during gameplay broadcasts, and challenge your friends during play.
  • Never leave a friend hanging or miss a chance to taunt your opponents with voice chat that keeps the conversation going, whether it's between rounds, between games, or just while kicking back.
", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "PlayStation 4 Camera", - Price = 59.99M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id - }; + var inactiveNewCustomersRole = _ctx.Set().FirstOrDefault(x => x.SystemName == "InactiveNewCustomers"); + if (inactiveNewCustomersRole != null) + { + _ctx.Set().Add(new AclRecord + { + EntityId = reminderCampaign.Id, + EntityName = nameof(Campaign), + CustomerRoleId = inactiveNewCustomersRole.Id, + IsIdle = false + }); + } + } + } - AddProductPicture(productPs4Camera, "product_sony_ps4_camera.png"); - productPs4Camera.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); - productPs4Camera.ProductCategories.Add(new ProductCategory { Category = categories["Gaming Accessories"], DisplayOrder = 3 }); + #region Alterations + protected virtual void Alter(IList entities) + { + } - var productBundlePs4 = new Product - { - ProductType = ProductType.BundledProduct, - VisibleIndividually = true, - Sku = "Sony-PS410099", - Name = "PlayStation 4 Bundle", - ShortDescription = "PlayStation®4 system, DUALSHOCK®4 wireless controller and PS4 camera.", - FullDescription = - "

The best place to play

PlayStation 4 is the best place to play with dynamic, connected gaming, powerful graphics and speed, intelligent personalization, deeply integrated social capabilities, and innovative second-screen features. Combining unparalleled content, immersive gaming experiences, all of your favorite digital entertainment apps, and PlayStation exclusives, PS4 centers on gamers, enabling them to play when, where and how they want. PS4 enables the greatest game developers in the world to unlock their creativity and push the boundaries of play through a system that is tuned specifically to their needs.

Gamer focused, developer inspired

The PS4 system focuses on the gamer, ensuring that the very best games and the most immersive experiences are possible on the platform. The PS4 system enables the greatest game developers in the world to unlock their creativity and push the boundaries of play through a system that is tuned specifically to their needs. The PS4 system is centered around a powerful custom chip that contains eight x86-64 cores and a state of the art 1.84 TFLOPS graphics processor with 8 GB of ultra-fast GDDR5 unified system memory, easing game creation and increasing the richness of content achievable on the platform. The end result is new games with rich, high-fidelity graphics and deeply immersive experiences.

Personalized, curated content

The PS4 system has the ability to learn about your preferences. It will learn your likes and dislikes, allowing you to discover content pre-loaded and ready to go on your console in your favorite game genres or by your favorite creators. Players also can look over game-related information shared by friends, view friends’ gameplay with ease, or obtain information about recommended content, including games, TV shows and movies.

New DUALSHOCK controller

DUALSHOCK 4 features new innovations to deliver more immersive gaming experiences, including a highly sensitive six-axis sensor as well as a touch pad located on the top of the controller, which offers gamers completely new ways to play and interact with games.

Shared game experiences

Engage in endless personal challenges with your community and share your epic triumphs with the press of a button. Simply hit the SHARE button on the controller, scan through the last few minutes of gameplay, tag it and return to the game—the video uploads as you play. The PS4 system also enhances social spectating by enabling you to broadcast your gameplay in real-time.

Remote play

Remote Play on the PS4 system fully unlocks the PlayStation Vita system’s potential, making it the ultimate companion device. With the PS Vita system, gamers will be able to seamlessly play a range of PS4 titles on the beautiful 5-inch display over Wi-Fi access points in a local area network.

PlayStation app

The PlayStation App will enable iPhone, iPad, and Android-based smartphones and tablets to become second screens for the PS4 system. Once installed on these devices, players can view in-game items, purchase PS4 games and download them directly to the console at home, or remotely watch the gameplay of other gamers playing on their devices.

PlayStation Plus

Built to bring games and gamers together and fuel the next generation of gaming, PlayStation Plus helps you discover a world of exceptional gaming experiences. PlayStation Plus is a membership service that takes your gaming experience to the next level. Each month members receive an Instant Game Collection of top rated blockbuster and innovative Indie games, which they can download direct to their console.

", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "PlayStation 4 Bundle", - Price = 429.99M, - OldPrice = 449.99M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id, - BundleTitleText = "Bundle includes" - }; + protected virtual void Alter(IList entities) + { + } - AddProductPicture(productBundlePs4, "product_sony_ps4_bundle.png"); - productBundlePs4.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); - productBundlePs4.ProductCategories.Add(new ProductCategory { Category = categories["Gaming"], DisplayOrder = 2 }); + protected virtual void Alter(IList entities) + { + } - #endregion bundlePs4 + protected virtual void Alter(IList entities) + { + } - #region groupAccessories + protected virtual void Alter(IList entities) + { + } - var productGroupAccessories = new Product - { - ProductType = ProductType.GroupedProduct, - VisibleIndividually = true, - Sku = "Sony-GroupAccessories", - Name = "Accessories for unlimited gaming experience", - ShortDescription = "The future of gaming is now with dynamic, connected gaming, powerful graphics and speed, intelligent personalization, deeply integrated social capabilities, and innovative second-screen features. The brilliant culmination of the most creative minds in the industry, PlayStation®4 delivers a unique gaming environment that will take your breath away.", - FullDescription = "
  • Immerse yourself in a new world of gameplay with powerful graphics and speed.
  • Eliminate lengthy load times of saved games with Suspend mode.
  • Immediately play digital titles without waiting for them to finish downloading thanks to background downloading and updating capability.
  • Instantly share images and videos of your favorite gameplay moments on Facebook with the SHARE button on the DUALSHOCK®4 controller.
  • Broadcast while you play in real-time through Ustream.
", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Accessories for unlimited gaming experience", - Price = 0.0M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 3, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - ShowOnHomePage = true - }; + protected virtual void Alter(IList entities) + { + } - AddProductPicture(productGroupAccessories, "category_gaming_accessories.png"); - productGroupAccessories.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); - productGroupAccessories.ProductCategories.Add(new ProductCategory { Category = categories["Gaming"], DisplayOrder = 3 }); + protected virtual void Alter(IList entities) + { + } - #endregion groupAccessories + protected virtual void Alter(Address entity) + { + } - #region Ps3PlusOneGame + protected virtual void Alter(Customer entity) + { + } - var productPrinceOfPersia = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Sku = "Ubi-princepersia", - Name = "Prince of Persia \"The Forgotten Sands\"", - ShortDescription = "Play the epic story of the heroic Prince as he fights and outwits his enemies in order to save his kingdom.", - FullDescription = "

This game marks the return to the Prince of Persia® Sands of Time storyline. Prince of Persia: The Forgotten Sands™ will feature many of the fan-favorite elements from the original series as well as new gameplay innovations that gamers have come to expect from Prince of Persia.

Experience the story, setting, and gameplay in this return to the Sands of Time universe as we follow the original Prince of Persia through a new untold chapter.

Created by Ubisoft Montreal, the team that brought you various Prince of Persia® and Assassin’s Creed® games, Prince of Persia The Forgotten Sands™ has been over 2 years in the making.

", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Prince of Persia", - Price = 39.90M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id - }; + protected virtual void Alter(IList entities) + { + } - AddProductPicture(productPrinceOfPersia, "products_princeofpersia.jpg"); - productPrinceOfPersia.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Ubisoft"], DisplayOrder = 1 }); - productPrinceOfPersia.ProductCategories.Add(new ProductCategory { Category = categories["Games"], DisplayOrder = 2 }); + protected virtual void Alter(IList entities) + { + } - #endregion Ps3PlusOneGame + protected virtual void Alter(IList entities) + { + } - #region Horizon Zero Down + protected virtual void Alter(IList entities) + { + } - var productHorizonZeroDown = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - //Sku = "Ubi-princepersia", - Sku = "PD-ZeroDown4PS4", - Name = "Horizon Zero Dawn - PlayStation 4", - ShortDescription = "Experience A Vibrant, Lush World Inhabited By Mysterious Mechanized Creatures", - FullDescription = "
  • A Lush Post-Apocalyptic World – How have machines dominated this world, and what is their purpose? What happened to the civilization here before? Scour every corner of a realm filled with ancient relics and mysterious buildings in order to uncover your past and unearth the many secrets of a forgotten land.
  • Nature and Machines Collide – Horizon Zero Dawn juxtaposes two contrasting elements, taking a vibrant world rich with beautiful nature and filling it with awe-inspiring highly advanced technology. This marriage creates a dynamic combination for both exploration and gameplay.
  • Defy Overwhelming Odds – The foundation of combat in Horizon Zero Dawn is built upon the speed and cunning of Aloy versus the raw strength and size of the machines. In order to overcome a much larger and technologically superior enemy, Aloy must use every ounce of her knowledge, intelligence, and agility to survive each encounter.
  • Cutting Edge Open World Tech – Stunningly detailed forests, imposing mountains, and atmospheric ruins of a bygone civilization meld together in a landscape that is alive with changing weather systems and a full day/night cycle.
", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "Horizon Zero Dawn - PlayStation 4", - Price = 69.90M, - OldPrice = 79.90M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id - }; + protected virtual void Alter(IList entities) + { + } - AddProductPicture(productHorizonZeroDown, "product_horizon.jpg"); - productHorizonZeroDown.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Sony"], DisplayOrder = 1 }); - productHorizonZeroDown.ProductCategories.Add(new ProductCategory { Category = categories["Games"], DisplayOrder = 2 }); + protected virtual void Alter(IList entities) + { + } - #endregion Horizon Zero Down + protected virtual void Alter(IList entities) + { + } - #region Fifa 17 + protected virtual void Alter(IList settings) + { + } - var productFifa17 = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - //Sku = "Ubi-princepersia", - Sku = "PD-Fifa17", - Name = "FIFA 17 - PlayStation 4", - ShortDescription = "Powered by Frostbite", - FullDescription = "
  • Powered by Frostbite: One of the industry’s leading game engines, Frostbite delivers authentic, true-to-life action, takes players to new football worlds, and introduces fans to characters full of depth and emotion in FIFA 17.
  • The Journey: For the first time ever in FIFA, live your story on and off the pitch as the Premier League’s next rising star, Alex Hunter. Play on any club in the premier league, for authentic managers and alongside some of the best players on the planet. Experience brand new worlds in FIFA 17, all while navigating your way through the emotional highs and lows of The Journey.
  • Own Every Moment: Complete innovation in the way players think and move, physically interact with opponents, and execute in attack puts you in complete control of every moment on the pitch.
", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "FIFA 17 - PlayStation 4", - Price = 79.90M, - OldPrice = 89.90M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id - }; + protected virtual void Alter(IList settings) + { + } - AddProductPicture(productFifa17, "product_fifa17.jpg"); - productFifa17.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["EA Sports"], DisplayOrder = 1 }); - productFifa17.ProductCategories.Add(new ProductCategory { Category = categories["Games"], DisplayOrder = 2 }); - - #endregion Fifa 17 - - #region Lego Worlds - - var productLegoWorlds = new Product - { - ProductType = ProductType.SimpleProduct, - VisibleIndividually = true, - Sku = "Gaming-Lego-001", - Name = "LEGO Worlds - PlayStation 4", - ShortDescription = "Experience a galaxy of Worlds made entirely from LEGO bricks.", - FullDescription = "
  • Experience a galaxy of Worlds made entirely from LEGO bricks.
  • LEGO Worlds is an open environment of procedurally-generated Worlds made entirely of LEGO bricks which you can freely manipulate and dynamically populate with LEGO models.
  • Create anything you can imagine one brick at a time, or use large-scale landscaping tools to create vast mountain ranges and dot your world with tropical islands.
  • Explore using helicopters, dragons, motorbikes or even gorillas and unlock treasures that enhance your gameplay.
  • Watch your creations come to life through characters and creatures that interact with you and each other in unexpected ways.

", - ProductTemplateId = productTemplate.Id, - AllowCustomerReviews = true, - Published = true, - MetaTitle = "LEGO Worlds - PlayStation 4", - Price = 29.90M, - OldPrice = 34.90M, - ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - OrderMinimumQuantity = 1, - OrderMaximumQuantity = 10000, - StockQuantity = 10000, - NotifyAdminForQuantityBelow = 1, - AllowBackInStockSubscriptions = false, - IsShipEnabled = true, - DeliveryTimeId = firstDeliveryTime.Id - }; + protected virtual void Alter(IList settings) + { + } - AddProductPicture(productLegoWorlds, "product_legoworlds.jpg"); - productLegoWorlds.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manufacturers["Warner Home Video Games"], DisplayOrder = 1 }); - productLegoWorlds.ProductCategories.Add(new ProductCategory { Category = categories["Games"], DisplayOrder = 3 }); - - #endregion Lego Worlds - - //#region XBox One S - - //var productXBoxOneS = new Product - //{ - // ProductType = ProductType.SimpleProduct, - // VisibleIndividually = true, - // Sku = "Microsoft-xbox1s", - // Name = "Xbox One S 500 GB Konsole", - // ShortDescription = "Genieße über 100 Spiele, die es nur für die Konsole gibt, sowie eine ständig größer werdende Bibliothek an Xbox 360-Spielen auf der Xbox One S im neuen Design – der einzigen Konsole mit 4K Ultra HD Blu-ray, 4K-Videostreaming und HDR. Streame deine Lieblingsfilme und -sendungen in atemberaubendem 4K Ultra HD. Spiele Blockbuster wie Gears of War 4 und Battlefield 1 mit Freunden auf Xbox Live, dem schnellsten und zuverlässigsten Gaming-Netzwerk.", - // FullDescription = "
  • Die ultimativen Spiele und 4K-Entertainment-System.
  • 40 % kompaktere Konsole
    Lasse dich nicht von der Größe täuschen. Mit integriertem Netzteil und bis zu 2 TB Speicherplatz ist die Xbox One S die fortschrittlichste Xbox überhaupt.
  • Der beste Controller - jetzt noch besser
    Der neue Xbox Wireless Controller bietet ein schlankes, optimiertes Design, texturierte Grip - Fläche und Bluetooth zum Spielen auf Windows 10 Geräten. Genieße individuelle Tastenbelegung und verbesserte drahtlose Reichweite und stecke jeden kompatiblen Kopfhörer mit der 3, 5 mm Stereo - Headset - Buchse ein.
", - // ProductTemplateId = productTemplate.Id, - // AllowCustomerReviews = true, - // Published = true, - // MetaTitle = "Xbox One S", - // Price = 279.99M, - // OldPrice = 279.99M, - // ManageInventoryMethod = ManageInventoryMethod.DontManageStock, - // OrderMinimumQuantity = 1, - // OrderMaximumQuantity = 10000, - // StockQuantity = 10000, - // NotifyAdminForQuantityBelow = 1, - // AllowBackInStockSubscriptions = false, - // IsShipEnabled = true, - // DeliveryTimeId = firstDeliveryTime.Id - //}; - - //AddProductPicture(productXBoxOneS, "product_xbox_one_s_1.png", "microsoft-xbox-one-s-1"); - //productXBoxOneS.ProductManufacturers.Add(new ProductManufacturer { Manufacturer = manuMicrosoft, DisplayOrder = 1 }); - //productXBoxOneS.ProductCategories.Add(new ProductCategory { Category = categoryGaming, DisplayOrder = 1 }); - - //#endregion - - #endregion gaming - - var entities = new List - { - productTRANSOCEANCHRONOGRAPH, productTissotTTouchExpertSolar, productSeikoSRPA49K1, - productTitleistSM6TourChrome, productTitleistProV1x, productGBBEpicSubZeroDriver, - productSupremeGolfball, productBooksStoneOfTheWise, productNikeStrikeFootball, productNikeEvoPowerBall, - productTorfabrikOfficialGameBall, productAdidasTangoSalaBall, productAllCourtBasketball, productEvolutionHighSchoolGameBasketball, productRayBanTopBar, - productOriginalWayfarer, productCustomFlakSunglasses, productRadarEVPrizmSportsSunglasses, productAppleProHipsterBundle, product97ipad, productAirpods, - productIphoneplus,productWatchSeries2,product10GiftCard, product25GiftCard, product50GiftCard,product100GiftCard, productBooksUberMan, productBooksGefangeneDesHimmels, - productBooksBestGrillingRecipes, productBooksCookingForTwo, productBooksAutosDerSuperlative, productBooksBildatlasMotorraeder, - productBooksAutoBuch, productBooksFastCars, productBooksMotorradAbenteuer, - productInstantDownloadVivaldi, productInstantDownloadBeethoven, productWatchesCertinaDSPodiumBigSize, - productPs3, productMinecraft, productBundlePs3AssassinCreed, - productPs4, productDualshock4Controller, productPs4Camera, productBundlePs4, - productGroupAccessories, productPrinceOfPersia, productLegoWorlds, productHorizonZeroDown, productFifa17, productJackWolfsKinKanukaPoint - }; + protected virtual void Alter(IList settings) + { + } - entities.AddRange(GetFashionProducts(specAttributes)); - entities.AddRange(GetFurnitureProducts(specAttributes)); + protected virtual void Alter(IList settings) + { + } - this.Alter(entities); - return entities; - } + protected virtual void Alter(IList settings) + { + } - public IList ProductBundleItems() - { - var utcNow = DateTime.UtcNow; + protected virtual void Alter(IList settings) + { + } - #region apple bundles - var bundleAppleProHipster = _ctx.Set().First(x => x.Sku == "P-2005-Bundle"); + protected virtual void Alter(IList settings) + { + } - var bundleItemIproductIphoneplus = new ProductBundleItem() - { - BundleProduct = bundleAppleProHipster, - Product = _ctx.Set().First(x => x.Sku == "P-2001"), - Quantity = 1, - Discount = 40.0M, - Visible = true, - Published = true, - DisplayOrder = 1 - }; + protected virtual void Alter(IList entities) + { + } - var bundleItemProductWatchSeries2 = new ProductBundleItem() - { - BundleProduct = bundleAppleProHipster, - Product = _ctx.Set().First(x => x.Sku == "P-2002"), - Quantity = 2, - Discount = 30.0M, - Visible = true, - Published = true, - DisplayOrder = 2 - }; + protected virtual void Alter(IList entities) + { + } - var bundleItemproductAirpods = new ProductBundleItem() - { - BundleProduct = bundleAppleProHipster, - Product = _ctx.Set().First(x => x.Sku == "P-2003"), - Quantity = 1, - Discount = 30.0M, - Visible = true, - Published = true, - DisplayOrder = 3 - }; + protected virtual void Alter(IList entities) + { + } - var bundleItemproductIpad = new ProductBundleItem() - { - BundleProduct = bundleAppleProHipster, - Product = _ctx.Set().First(x => x.Sku == "P-2004"), - Quantity = 1, - Discount = 30.0M, - Visible = true, - Published = true, - DisplayOrder = 3 - }; + protected virtual void Alter(IList entities) + { + } - #endregion apple bundles - - #region gaming - - //var bundlePs3AssassinCreed = _ctx.Set().First(x => x.Sku == "Sony-PS399105"); - var bundlePs4Minecraft = _ctx.Set().First(x => x.Sku == "Sony-PS399105"); - - var bundleItemPs4Minecraft1 = new ProductBundleItem() - { - BundleProduct = bundlePs4Minecraft, - Product = _ctx.Set().First(x => x.Sku == "Sony-PS399000"), - Quantity = 1, - Discount = 20.0M, - Visible = true, - Published = true, - DisplayOrder = 1 - }; - - var bundleItemPs4Minecraft2 = new ProductBundleItem() - { - BundleProduct = bundlePs4Minecraft, - Product = _ctx.Set().First(x => x.Sku == "Sony-PS399004"), - Quantity = 2, - Discount = 30.0M, - Visible = true, - Published = true, - DisplayOrder = 2 - }; - - var bundleItemPs4Minecraft3 = new ProductBundleItem() - { - BundleProduct = bundlePs4Minecraft, - Product = _ctx.Set().First(x => x.Sku == "PD-Minecraft4ps4"), - Quantity = 1, - Discount = 20.0M, - Visible = true, - Published = true, - DisplayOrder = 3 - }; - - - var bundlePs4 = _ctx.Set().First(x => x.Sku == "Sony-PS410099"); - - var bundleItemPs41 = new ProductBundleItem - { - BundleProduct = bundlePs4, - Product = _ctx.Set().First(x => x.Sku == "Sony-PS410034"), - Quantity = 1, - Visible = true, - Published = true, - DisplayOrder = 1 - }; - - var bundleItemPs42 = new ProductBundleItem - { - BundleProduct = bundlePs4, - //Product = _ctx.Set().First(x => x.Sku == "Sony-PS410037"), - Product = _ctx.Set().First(x => x.Sku == "Sony-PS399004"), - Quantity = 1, - Visible = true, - Published = true, - DisplayOrder = 2 - }; - - var bundleItemPs43 = new ProductBundleItem - { - BundleProduct = bundlePs4, - Product = _ctx.Set().First(x => x.Sku == "Sony-PS410040"), - Quantity = 1, - Visible = true, - Published = true, - DisplayOrder = 3 - }; - - #endregion gaming - - var entities = new List - { - bundleItemPs4Minecraft1, bundleItemPs4Minecraft2, bundleItemPs4Minecraft3, - bundleItemPs41, bundleItemPs42, bundleItemPs43,bundleItemIproductIphoneplus, bundleItemProductWatchSeries2,bundleItemproductAirpods,bundleItemproductIpad - }; + protected virtual void Alter(IList entities) + { + } - this.Alter(entities); - return entities; - } + protected virtual void Alter(IList entities) + { + } - public void AddDownloads(IList savedProducts) + protected virtual void Alter(IList entities) { - // Sample downloads. - var sampleDownloadSkus = new List { "P-1017", "P-1016", "P-6001" }; - var sampleDownloadProducts = savedProducts - .Where(x => sampleDownloadSkus.Contains(x.Sku)) - .ToDictionary(x => x.Sku); + } - foreach (var product in sampleDownloadProducts.Values) - { - if (product.Sku.IsCaseInsensitiveEqual("P-1017")) - { - product.SampleDownload = new Download - { - EntityId = product.Id, - EntityName = nameof(Product), - DownloadGuid = Guid.NewGuid(), - ContentType = "audio/mp3", - MediaStorage = new MediaStorage - { - Data = File.ReadAllBytes(_sampleDownloadsPath + "beethoven-fur-elise.mp3") - }, - Extension = ".mp3", - Filename = "beethoven-fur-elise.mp3", - IsNew = true, - UpdatedOnUtc = DateTime.UtcNow - }; - } - else if (product.Sku.IsCaseInsensitiveEqual("P-1016")) - { - product.SampleDownload = new Download - { - EntityId = product.Id, - EntityName = nameof(Product), - DownloadGuid = Guid.NewGuid(), - ContentType = "audio/mp3", - MediaStorage = new MediaStorage - { - Data = File.ReadAllBytes(_sampleDownloadsPath + "vivaldi-four-seasons-spring.mp3") - }, - Extension = ".mp3", - Filename = "vivaldi-four-seasons-spring", - IsNew = true, - UpdatedOnUtc = DateTime.UtcNow - }; - } - else if (product.Sku.IsCaseInsensitiveEqual("P-6001")) - { - product.SampleDownload = new Download - { - EntityId = product.Id, - EntityName = nameof(Product), - DownloadGuid = Guid.NewGuid(), - ContentType = "application/pdf", - MediaStorage = new MediaStorage - { - Data = File.ReadAllBytes(_sampleDownloadsPath + "Stone_of_the_wise_preview.pdf") - }, - Extension = ".pdf", - Filename = "Stone_of_the_wise_preview", - IsNew = true, - UpdatedOnUtc = DateTime.UtcNow - }; - } - } + protected virtual void Alter(IList entities) + { + } - _ctx.SaveChanges(); + protected virtual void Alter(IList entities) + { } - public void AssignGroupedProducts(IList savedProducts) - { - int productGamingAccessoriesId = savedProducts.First(x => x.Sku == "Sony-GroupAccessories").Id; - var gamingAccessoriesSkus = new List() { "Sony-PS399004", "PD-Minecraft4ps4", "Sony-PS410037", "Sony-PS410040" }; - - savedProducts - .Where(x => gamingAccessoriesSkus.Contains(x.Sku)) - .ToList() - .Each(x => - { - x.ParentGroupedProductId = productGamingAccessoriesId; - - //_ctx.Set().Attach(x); - //_ctx.Entry(x).State = System.Data.Entity.EntityState.Modified; - }); - - _ctx.SaveChanges(); - } - - public IList ForumGroups() - { - var forumGroupGeneral = new ForumGroup - { - Name = "General", - Description = "", - DisplayOrder = 1 - }; - - var entities = new List - { - forumGroupGeneral - }; - - this.Alter(entities); - return entities; - } - - public IList Forums() - { - var group = _ctx.Set().FirstOrDefault(c => c.DisplayOrder == 1); + protected virtual void Alter(IList entities) + { + } - var newProductsForum = new Forum - { - ForumGroup = group, - Name = "New Products", - Description = "Discuss new products and industry trends", - NumTopics = 0, - NumPosts = 0, - LastPostCustomerId = 0, - LastPostTime = null, - DisplayOrder = 1 - }; - - var packagingShippingForum = new Forum - { - ForumGroup = group, - Name = "Packaging & Shipping", - Description = "Discuss packaging & shipping", - NumTopics = 0, - NumPosts = 0, - LastPostTime = null, - DisplayOrder = 20 - }; - - - var entities = new List - { - newProductsForum, packagingShippingForum - }; - - this.Alter(entities); - return entities; - } - - public IList Discounts() - { - var sampleDiscountWithCouponCode = new Discount() - { - Name = "Sample discount with coupon code", - DiscountType = DiscountType.AssignedToSkus, - DiscountLimitation = DiscountLimitationType.Unlimited, - UsePercentage = false, - DiscountAmount = 10, - RequiresCouponCode = true, - CouponCode = "123" - }; - var sampleDiscounTwentyPercentTotal = new Discount() - { - Name = "'20% order total' discount", - DiscountType = DiscountType.AssignedToOrderTotal, - DiscountLimitation = DiscountLimitationType.Unlimited, - UsePercentage = true, - DiscountPercentage = 20, - StartDateUtc = new DateTime(2013, 1, 1), - EndDateUtc = new DateTime(2020, 1, 1), - RequiresCouponCode = true, - CouponCode = "456" - }; - - var entities = new List - { - sampleDiscountWithCouponCode, sampleDiscounTwentyPercentTotal - }; - - this.Alter(entities); - return entities; - } - - public IList DeliveryTimes() - { - var entities = new List() - { - new DeliveryTime - { - Name = "available and ready to ship", - DisplayOrder = 0, - ColorHexValue = "#008000" - }, - new DeliveryTime - { - Name = "2-5 woking days", - DisplayOrder = 1, - ColorHexValue = "#FFFF00" - }, - new DeliveryTime - { - Name = "7 working days", - DisplayOrder = 2, - ColorHexValue = "#FF9900" - }, - }; - this.Alter(entities); - return entities; - } + protected virtual void Alter(IList entities) + { + } - public IList QuantityUnits() + protected virtual void Alter(IList entities) { - var count = 0; - var entities = new List(); + } - var quPluralEn = new Dictionary - { - { "Piece", "Pieces" }, - { "Box", "Boxes" }, - { "Parcel", "Parcels" }, - { "Palette", "Pallets" }, - { "Unit", "Units" }, - { "Sack", "Sacks" }, - { "Bag", "Bags" }, - { "Can", "Cans" }, - { "Packet", "Packets" }, - { "Bar", "Bars" }, - { "Bottle", "Bottles" }, - { "Glass", "Glasses" }, - { "Bunch", "Bunches" }, - { "Roll", "Rolls" }, - { "Cup", "Cups" }, - { "Bundle", "Bundles" }, - { "Barrel", "Barrels" }, - { "Set", "Sets" }, - { "Bucket", "Buckets" } - }; + protected virtual void Alter(IList entities) + { + } - foreach (var qu in quPluralEn) - { - entities.Add(new QuantityUnit - { - Name = qu.Key, - NamePlural = qu.Value, - Description = qu.Key, - IsDefault = qu.Key == "Piece", - DisplayOrder = count++ - }); - } - - this.Alter(entities); - return entities; + protected virtual void Alter(IList entities) + { } - public IList BlogPosts() - { - var defaultLanguage = _ctx.Set().FirstOrDefault(); - var blogPostDiscountCoupon = new BlogPost() - { - AllowComments = true, - Language = defaultLanguage, - Title = "Online Discount Coupons", - Body = "

Online discount coupons enable access to great offers from some of the world’s best sites for Internet shopping. The online coupons are designed to allow compulsive online shoppers to access massive discounts on a variety of products. The regular shopper accesses the coupons in bulk and avails of great festive offers and freebies thrown in from time to time. The coupon code option is most commonly used when using a shopping cart. The coupon code is entered on the order page just before checking out. Every online shopping resource has a discount coupon submission option to confirm the coupon code. The dedicated web sites allow the shopper to check whether or not a discount is still applicable. If it is, the sites also enable the shopper to calculate the total cost after deducting the coupon amount like in the case of grocery coupons. Online discount coupons are very convenient to use. They offer great deals and professionally negotiated rates if bought from special online coupon outlets. With a little research and at times, insider knowledge the online discount coupons are a real steal. They are designed to promote products by offering ‘real value for money’ packages. The coupons are legitimate and help with budgeting, in the case of a compulsive shopper. They are available for special trade show promotions, nightlife, sporting events and dinner shows and just about anything that could be associated with the promotion of a product. The coupons enable the online shopper to optimize net access more effectively. Getting a ‘big deal’ is not more utopian amidst rising prices. The online coupons offer internet access to the best and cheapest products displayed online. Big discounts are only a code away! By Gaynor Borade (buzzle.com)

", - Tags = "e-commerce, money", - CreatedOnUtc = DateTime.UtcNow, - }; - var blogPostCustomerService = new BlogPost() - { - AllowComments = true, - Language = defaultLanguage, - Title = "Customer Service - Client Service", - Body = "

Managing online business requires different skills and abilities than managing a business in the ‘real world.’ Customers can easily detect the size and determine the prestige of a business when they have the ability to walk in and take a look around. Not only do ‘real-world’ furnishings and location tell the customer what level of professionalism to expect, but "real world" personal encounters allow first impressions to be determined by how the business approaches its customer service. When a customer walks into a retail business just about anywhere in the world, that customer expects prompt and personal service, especially with regards to questions that they may have about products they wish to purchase.

Customer service or the client service is the service provided to the customer for his satisfaction during and after the purchase. It is necessary to every business organization to understand the customer needs for value added service. So customer data collection is essential. For this, a good customer service is important. The easiest way to lose a client is because of the poor customer service. The importance of customer service changes by product, industry and customer. Client service is an important part of every business organization. Each organization is different in its attitude towards customer service. Customer service requires a superior quality service through a careful design and execution of a series of activities which include people, technology and processes. Good customer service starts with the design and communication between the company and the staff.

In some ways, the lack of a physical business location allows the online business some leeway that their ‘real world’ counterparts do not enjoy. Location is not important, furnishings are not an issue, and most of the visual first impression is made through the professional design of the business website.

However, one thing still remains true. Customers will make their first impressions on the customer service they encounter. Unfortunately, in online business there is no opportunity for front- line staff to make a good impression. Every interaction the customer has with the website will be their primary means of making their first impression towards the business and its client service. Good customer service in any online business is a direct result of good website design and planning.

By Jayashree Pakhare (buzzle.com)

", - Tags = "e-commerce, Smartstore.NET, asp.net, sample tag, money", - CreatedOnUtc = DateTime.UtcNow.AddSeconds(1), - }; - - var entities = new List - { - blogPostDiscountCoupon, blogPostCustomerService - }; - - this.Alter(entities); - return entities; - } - - public IList NewsItems() - { - var defaultLanguage = _ctx.Set().FirstOrDefault(); - var news1 = new NewsItem() - { - AllowComments = true, - Language = defaultLanguage, - Title = "Smartstore.NET 3.2 now with the new CMS Page Builder", - Short = "Create fascinating content with the new Page Builder", - Full = "

Create fascinating content from products, product groups, images, videos and texts.
Transitions, animations, gradients, hover effects or overlays are easily applied in the WYSIWYG editor.

More information about Smartstore 3.2 and Page Builder can be found at www.smartstore.com

", - Published = true, - MetaTitle = "Smartstore.NET 3.2", - CreatedOnUtc = DateTime.Now - }; - var news2 = new NewsItem() - { - AllowComments = true, - Language = defaultLanguage, - Title = "Smartstore.NET new release!", - Short = "Smartstore.NET includes everything you need to begin your e-commerce online store.", - Full = "

Smartstore.NET includes everything you need to begin your e-commerce online store.
We have thought of everything and it's all included!

From downloads to documentation, www.smartstore.com offers a comprehensive base of information, resources, and support to the Smartstore.NET community.

", - Published = true, - MetaTitle = "Smartstore.NET new release!", - CreatedOnUtc = DateTime.Now - }; + protected virtual void Alter(IList entities) + { + } - var entities = new List - { - news1, news2 - }; - - this.Alter(entities); - return entities; - } - - public IList PollAnswers() - { - var pollAnswer1 = new PollAnswer() - { - Name = "Excellent", - DisplayOrder = 1, - }; - var pollAnswer2 = new PollAnswer() - { - Name = "Good", - DisplayOrder = 2, - }; - var pollAnswer3 = new PollAnswer() - { - Name = "Poor", - DisplayOrder = 3, - }; - var pollAnswer4 = new PollAnswer() - { - Name = "Very bad", - DisplayOrder = 4, - }; - var pollAnswer5 = new PollAnswer() - { - Name = "Daily", - DisplayOrder = 5, - }; - var pollAnswer6 = new PollAnswer() - { - Name = "Once a week", - DisplayOrder = 6, - }; - var pollAnswer7 = new PollAnswer() - { - Name = "Every two weeks", - DisplayOrder = 7, - }; - var pollAnswer8 = new PollAnswer() - { - Name = "Once a month", - DisplayOrder = 8, - }; - - var entities = new List - { - pollAnswer1, pollAnswer2, pollAnswer3, pollAnswer4, pollAnswer5, pollAnswer6, pollAnswer7, pollAnswer8 - }; - - this.Alter(entities); - return entities; - } - - public IList Polls() - { - var defaultLanguage = _ctx.Set().FirstOrDefault(); - var poll1 = new Poll - { - Language = defaultLanguage, - Name = "How do you like the shop?", - SystemKeyword = "Blog", - Published = true, - DisplayOrder = 1, - }; - - poll1.PollAnswers.Add(new PollAnswer - { - Name = "Excellent", - DisplayOrder = 1, - }); - - poll1.PollAnswers.Add(new PollAnswer - { - Name = "Good", - DisplayOrder = 2, - }); - - poll1.PollAnswers.Add(new PollAnswer - { - Name = "Poor", - DisplayOrder = 3, - }); - - poll1.PollAnswers.Add(new PollAnswer - { - Name = "Very bad", - DisplayOrder = 4, - }); - - - var poll2 = new Poll - { - Language = defaultLanguage, - Name = "How often do you buy online?", - SystemKeyword = "Blog", - Published = true, - DisplayOrder = 2, - }; - - poll2.PollAnswers.Add(new PollAnswer - { - Name = "Daily", - DisplayOrder = 1, - }); - - poll2.PollAnswers.Add(new PollAnswer - { - Name = "Once a week", - DisplayOrder = 2, - }); - - poll2.PollAnswers.Add(new PollAnswer - { - Name = "Every two weeks", - DisplayOrder = 3, - }); - - poll2.PollAnswers.Add(new PollAnswer - { - Name = "Once a month", - DisplayOrder = 4, - }); - - - var entities = new List - { - poll1, poll2 - }; - - this.Alter(entities); - return entities; - } - - #region Alterations - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(Address entity) - { - } - - protected virtual void Alter(Customer entity) - { - } - - protected virtual void Alter(IList entities) - { - } + protected virtual void Alter(IList entities) + { + } - protected virtual void Alter(IList entities) + protected virtual void Alter(IList entities) + { + } + + protected virtual void Alter(IList entities) { } - protected virtual void Alter(IList entities) - { - } + protected virtual void Alter(IList entities) + { + } - protected virtual void Alter(IList entities) - { - } + protected virtual void Alter(IList entities) + { + } - protected virtual void Alter(IList entities) - { - } + protected virtual void Alter(IList entities) + { + } - protected virtual void Alter(IList entities) - { - } + protected virtual void Alter(IList entities) + { + } - protected virtual void Alter(IList entities) - { - } + protected virtual void Alter(IList entities) + { + } - protected virtual void Alter(IList settings) - { - } + protected virtual void Alter(IList entities) + { + } - protected virtual void Alter(IList settings) - { - } + protected virtual void Alter(UrlRecord entity) + { + } - protected virtual void Alter(IList settings) - { - } + #endregion Alterations - protected virtual void Alter(IList settings) - { - } + #endregion Sample data creators - protected virtual void Alter(IList settings) - { - } + #region Helpers - protected virtual void Alter(IList settings) - { - } + protected SmartObjectContext DbContext => _ctx; - protected virtual void Alter(IList settings) - { - } + protected string SampleImagesPath => _sampleImagesPath; - protected virtual void Alter(IList settings) - { - } + public virtual UrlRecord CreateUrlRecordFor(T entity) where T : BaseEntity, ISlugSupported, new() + { + var name = ""; + + switch (entity) + { + case Category x: + name = x.Name; + break; + case Manufacturer x: + name = x.Name; + break; + case Product x: + name = x.Name; + break; + case BlogPost x: + name = x.Title; + break; + case NewsItem x: + name = x.Title; + break; + case Topic x: + name = SeoHelper.GetSeName(x.SystemName, true, false, true).Truncate(400); + break; + } - protected virtual void Alter(IList entities) - { - } + if (name.HasValue()) + { + var result = new UrlRecord + { + EntityId = entity.Id, + EntityName = entity.GetUnproxiedType().Name, + LanguageId = 0, + Slug = name, + IsActive = true + }; + + this.Alter(result); + return result; + } - protected virtual void Alter(IList entities) - { - } + return null; + } - protected virtual void Alter(IList entities) - { - } + protected MediaFile CreatePicture(string fileName, string seoFilename = null) + { + try + { + var ext = Path.GetExtension(fileName); + var path = Path.Combine(_sampleImagesPath, fileName).Replace('/', '\\'); + var mimeType = MimeTypes.MapNameToMimeType(ext); + var buffer = File.ReadAllBytes(path); + var now = DateTime.UtcNow; - protected virtual void Alter(IList entities) - { - } + var name = seoFilename.HasValue() + ? seoFilename.Truncate(100) + ext + : Path.GetFileName(fileName).ToLower().Replace('_', '-'); - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(IList entities) - { - } - - protected virtual void Alter(UrlRecord entity) - { - } - - #endregion Alterations - - #endregion Sample data creators - - #region Helpers - - protected SmartObjectContext DbContext - { - get - { - return _ctx; - } - } - - protected string SampleImagesPath - { - get - { - return this._sampleImagesPath; - } - } - - protected string SampleDownloadsPath - { - get - { - return this._sampleDownloadsPath; - } - } - - public virtual UrlRecord CreateUrlRecordFor(T entity) where T : BaseEntity, ISlugSupported, new() - { - var name = ""; - var languageId = 0; - - switch (entity) - { - case Category x: - name = x.Name; - break; - case Manufacturer x: - name = x.Name; - break; - case Product x: - name = x.Name; - break; - case BlogPost x: - name = x.Title; - languageId = x.LanguageId; - break; - case NewsItem x: - name = x.Title; - languageId = x.LanguageId; - break; - case Topic x: - name = SeoHelper.GetSeName(x.SystemName, true, false, true).Truncate(400); - break; - } - - if (name.HasValue()) - { - var result = new UrlRecord - { - EntityId = entity.Id, - EntityName = entity.GetUnproxiedType().Name, - LanguageId = languageId, - Slug = name, - IsActive = true - }; - - this.Alter(result); - return result; - } - - return null; - } - - protected Picture CreatePicture(byte[] pictureBinary, string mimeType, string seoFilename) - { - mimeType = mimeType.EmptyNull().Truncate(20); - seoFilename = seoFilename.Truncate(100); - - var picture = _ctx.Set().Create(); - picture.MimeType = mimeType; - picture.SeoFilename = seoFilename; - picture.UpdatedOnUtc = DateTime.UtcNow; - - picture.MediaStorage = new MediaStorage - { - Data = pictureBinary - }; - - return picture; - } + var file = new MediaFile + { + Name = name, + MediaType = "image", + MimeType = mimeType, + Extension = ext.EmptyNull().TrimStart('.'), + CreatedOnUtc = now, + UpdatedOnUtc = now, + Size = buffer.Length, + MediaStorage = new MediaStorage { Data = buffer }, + Version = 1 // so that FolderId is set later during track detection + }; + + return file; + } + catch (Exception ex) + { + //throw ex; + System.Diagnostics.Debug.WriteLine(ex.Message); + return null; + } + } protected void AddProductPicture( Product product, @@ -13843,86 +1607,81 @@ protected void AddProductPicture( string seName = null, int displayOrder = 1) { - string mimeType = null; - if (imageName.EndsWith(".png")) + if (seName == null) { - mimeType = "image/png"; - } - else if (imageName.EndsWith(".gif")) - { - mimeType = "image/gif"; + seName = GetSeName(Path.GetFileNameWithoutExtension(imageName)); } - product.ProductPictures.Add(new ProductPicture + product.ProductPictures.Add(new ProductMediaFile { - Picture = CreatePicture(File.ReadAllBytes(_sampleImagesPath + imageName), mimeType ?? "image/jpeg", seName ?? GetSeName(product.Name)), + MediaFile = CreatePicture(imageName, seName), DisplayOrder = displayOrder }); } protected string GetSeName(string name) - { - return SeoHelper.GetSeName(name, true, false); - } - - protected Currency CreateCurrency(string locale, decimal rate = 1M, string formatting = "", bool published = false, int order = 1) - { - RegionInfo info = null; - Currency currency = null; - try - { - info = new RegionInfo(locale); - if (info != null) - { - currency = new Currency - { - DisplayLocale = locale, - Name = info.CurrencyNativeName, - CurrencyCode = info.ISOCurrencySymbol, - Rate = rate, - CustomFormatting = formatting, - Published = published, - DisplayOrder = order - }; - } - } - catch - { - return null; - } - - return currency; - } - - protected string FormatAttributeXml(int attributeId, int valueId, bool withRootTag = true) - { - var xml = $"{valueId}"; - - if (withRootTag) - { - return string.Concat("", xml, ""); - } - - return xml; - } - protected string FormatAttributeXml(int attributeId1, int valueId1, int attributeId2, int valueId2) - { - return string.Concat( - "", - FormatAttributeXml(attributeId1, valueId1, false), - FormatAttributeXml(attributeId2, valueId2, false), - ""); - } - protected string FormatAttributeXml(int attributeId1, int valueId1, int attributeId2, int valueId2, int attributeId3, int valueId3) - { - return string.Concat( - "", - FormatAttributeXml(attributeId1, valueId1, false), - FormatAttributeXml(attributeId2, valueId2, false), - FormatAttributeXml(attributeId3, valueId3, false), - ""); - } - - #endregion - } + { + return SeoHelper.GetSeName(name, true, false); + } + + protected Currency CreateCurrency(string locale, decimal rate = 1M, string formatting = "", bool published = false, int order = 1) + { + RegionInfo info = null; + Currency currency = null; + try + { + info = new RegionInfo(locale); + if (info != null) + { + currency = new Currency + { + DisplayLocale = locale, + Name = info.CurrencyNativeName, + CurrencyCode = info.ISOCurrencySymbol, + Rate = rate, + CustomFormatting = formatting, + Published = published, + DisplayOrder = order + }; + } + } + catch + { + return null; + } + + return currency; + } + + protected string FormatAttributeXml(int attributeId, int valueId, bool withRootTag = true) + { + var xml = $"{valueId}"; + + if (withRootTag) + { + return string.Concat("", xml, ""); + } + + return xml; + } + protected string FormatAttributeXml(int attributeId1, int valueId1, int attributeId2, int valueId2) + { + return string.Concat( + "", + FormatAttributeXml(attributeId1, valueId1, false), + FormatAttributeXml(attributeId2, valueId2, false), + ""); + } + protected string FormatAttributeXml(int attributeId1, int valueId1, int attributeId2, int valueId2, int attributeId3, int valueId3) + { + return string.Concat( + "", + FormatAttributeXml(attributeId1, valueId1, false), + FormatAttributeXml(attributeId2, valueId2, false), + FormatAttributeXml(attributeId3, valueId3, false), + ""); + } + + #endregion + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Setup/SeedData/SeedDataConfiguration.cs b/src/Libraries/SmartStore.Data/Setup/SeedData/SeedDataConfiguration.cs index 6bed1ce189..df091e38f3 100644 --- a/src/Libraries/SmartStore.Data/Setup/SeedData/SeedDataConfiguration.cs +++ b/src/Libraries/SmartStore.Data/Setup/SeedData/SeedDataConfiguration.cs @@ -1,28 +1,25 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using SmartStore.Core.Domain.Localization; namespace SmartStore.Data.Setup { - public class SeedDataConfiguration - { - public SeedDataConfiguration() - { - this.SeedSampleData = true; - this.StoreMediaInDB = true; - this.ProgressMessageCallback = (x) => { }; // Noop - } + public class SeedDataConfiguration + { + public SeedDataConfiguration() + { + this.SeedSampleData = true; + this.StoreMediaInDB = true; + this.ProgressMessageCallback = (x) => { }; // Noop + } - public string DefaultUserName { get; set; } - public string DefaultUserPassword { get; set; } - public Language Language { get; set; } - public InvariantSeedData Data { get; set; } - public bool SeedSampleData { get; set; } - public bool StoreMediaInDB { get; set; } - public Action ProgressMessageCallback { get; set; } - } + public string DefaultUserName { get; set; } + public string DefaultUserPassword { get; set; } + public Language Language { get; set; } + public InvariantSeedData Data { get; set; } + public bool SeedSampleData { get; set; } + public bool StoreMediaInDB { get; set; } + public Action ProgressMessageCallback { get; set; } + } } diff --git a/src/Libraries/SmartStore.Data/Setup/SeedData/SeedDataException.cs b/src/Libraries/SmartStore.Data/Setup/SeedData/SeedDataException.cs index 6267ab633b..ab70d99eb4 100644 --- a/src/Libraries/SmartStore.Data/Setup/SeedData/SeedDataException.cs +++ b/src/Libraries/SmartStore.Data/Setup/SeedData/SeedDataException.cs @@ -1,14 +1,11 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace SmartStore.Data.Setup { - + public class SeedDataException : Exception { - public SeedDataException(string stage, Exception inner) + public SeedDataException(string stage, Exception inner) : base("An error occurred during installation. Stage: " + stage, inner) { this.Stage = stage; diff --git a/src/Libraries/SmartStore.Data/Setup/SeedData/SeedEntityExtensions.cs b/src/Libraries/SmartStore.Data/Setup/SeedData/SeedEntityExtensions.cs index 229dd25f9b..f1ae049a4a 100644 --- a/src/Libraries/SmartStore.Data/Setup/SeedData/SeedEntityExtensions.cs +++ b/src/Libraries/SmartStore.Data/Setup/SeedData/SeedEntityExtensions.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Text; using SmartStore.Core; using SmartStore.Core.Configuration; @@ -12,9 +9,9 @@ public static class SeedEntityExtensions { #region fluent extensions - public static SeedEntityAlterer WithKey(this IList list, Expression> expression) where T : BaseEntity + public static SeedEntityAlterer WithKey(this IList list, Func predicate) where T : BaseEntity { - return new SeedEntityAlterer(list, expression); + return new SeedEntityAlterer(list, predicate); } public static SeedSettingsAlterer Alter(this IList list, Action action) where TSettings : class, ISettings, new() @@ -60,26 +57,26 @@ public class SeedEntityAlterer where T : BaseEntity private readonly Dictionary _entityMap; // for faster access! private readonly IList _entities; - public SeedEntityAlterer(IList list, Expression> keyExpression) + public SeedEntityAlterer(IList list, Func keyPredicate) { _entities = list; // fetch all key values from list and build a key/value map for faster access. _entityMap = new Dictionary(list.Count); - var fn = keyExpression.Compile(); + var fn = keyPredicate; foreach (var entity in list) { TKey key = fn.Invoke(entity); _entityMap.Add(key, entity); } - + } public SeedEntityAlterer Alter(TKey key, Action action) { T entity = default(T); - if (_entityMap.TryGetValue(key, out entity)) + if (_entityMap.TryGetValue(key, out entity)) { action(entity); } diff --git a/src/Libraries/SmartStore.Data/Setup/SeedDbMigrationEvent.cs b/src/Libraries/SmartStore.Data/Setup/SeedDbMigrationEvent.cs new file mode 100644 index 0000000000..720dddb90d --- /dev/null +++ b/src/Libraries/SmartStore.Data/Setup/SeedDbMigrationEvent.cs @@ -0,0 +1,18 @@ +using System.Data.Entity; + +namespace SmartStore.Data.Setup +{ + public sealed class SeedingDbMigrationEvent + { + public string MigrationId { get; internal set; } + public string MigrationName { get; internal set; } + public DbContext DbContext { get; internal set; } + } + + public sealed class SeededDbMigrationEvent + { + public string MigrationId { get; internal set; } + public string MigrationName { get; internal set; } + public DbContext DbContext { get; internal set; } + } +} diff --git a/src/Libraries/SmartStore.Data/SmartDbConfiguration.cs b/src/Libraries/SmartStore.Data/SmartDbConfiguration.cs index d18414d6e9..e58a59a192 100644 --- a/src/Libraries/SmartStore.Data/SmartDbConfiguration.cs +++ b/src/Libraries/SmartStore.Data/SmartDbConfiguration.cs @@ -1,22 +1,20 @@ -using System; -using System.Linq; -using System.Data.Entity; +using System.Data.Entity; using System.Data.Entity.Core.Common; +using System.Data.Entity.Infrastructure; +using System.Data.Entity.Infrastructure.DependencyResolution; +using System.Web.Hosting; using SmartStore.Core.Data; using SmartStore.Core.Infrastructure; using SmartStore.Data.Caching; -using System.Web.Hosting; -using System.Data.Entity.Infrastructure; -using System.Data.Entity.Infrastructure.DependencyResolution; using SmartStore.Utilities; namespace SmartStore.Data { - public class SmartDbConfiguration : DbConfiguration - { - public SmartDbConfiguration() - { - // DB model caching + public class SmartDbConfiguration : DbConfiguration + { + public SmartDbConfiguration() + { + // DB model caching if (HostingEnvironment.IsHosted) { var cacheLocation = CommonHelper.MapPath("~/App_Data/EfCache"); @@ -25,41 +23,41 @@ public SmartDbConfiguration() AddDependencyResolver(new SingletonDependencyResolver(modelStore)); } - IEfDataProvider provider = null; - try - { - provider = (new EfDataProviderFactory(DataSettings.Current).LoadDataProvider()) as IEfDataProvider; - } - catch - { - /* SmartStore is not installed yet! */ - } - - if (provider != null) - { - base.SetDefaultConnectionFactory(provider.GetConnectionFactory()); + IEfDataProvider provider = null; + try + { + provider = (new EfDataProviderFactory(DataSettings.Current).LoadDataProvider()) as IEfDataProvider; + } + catch + { + /* SmartStore is not installed yet! */ + } - if (HostingEnvironment.IsHosted && DataSettings.DatabaseIsInstalled()) - { - Loaded += (sender, args) => - { - // prepare EntityFramework 2nd level cache - IDbCache cache = null; - try - { - cache = EngineContext.Current.Resolve(); - } - catch - { - cache = new NullDbCache(); - } + if (provider != null) + { + base.SetDefaultConnectionFactory(provider.GetConnectionFactory()); + + if (HostingEnvironment.IsHosted && DataSettings.DatabaseIsInstalled()) + { + Loaded += (sender, args) => + { + // prepare EntityFramework 2nd level cache + IDbCache cache = null; + try + { + cache = EngineContext.Current.Resolve(); + } + catch + { + cache = new NullDbCache(); + } - var cacheInterceptor = new CacheTransactionInterceptor(cache); - AddInterceptor(cacheInterceptor); - args.ReplaceService((s, o) => new CachingProviderServices(s, cacheInterceptor)); - }; - } - } - } - } + var cacheInterceptor = new CacheTransactionInterceptor(cache); + AddInterceptor(cacheInterceptor); + args.ReplaceService((s, o) => new CachingProviderServices(s, cacheInterceptor)); + }; + } + } + } + } } diff --git a/src/Libraries/SmartStore.Data/SmartObjectContext.cs b/src/Libraries/SmartStore.Data/SmartObjectContext.cs index 586a4f6f3f..d74769d6ed 100644 --- a/src/Libraries/SmartStore.Data/SmartObjectContext.cs +++ b/src/Libraries/SmartStore.Data/SmartObjectContext.cs @@ -13,27 +13,27 @@ namespace SmartStore.Data { public class SmartObjectContext : ObjectContextBase { - static SmartObjectContext() - { - if (!DataSettings.DatabaseIsInstalled()) - return; + static SmartObjectContext() + { + if (!DataSettings.DatabaseIsInstalled()) + return; - var initializer = new MigrateDatabaseInitializer - { - TablesToCheck = new[] { "Customer", "Discount", "Order", "Product", "ShoppingCartItem" } - }; - Database.SetInitializer(initializer); - } + var initializer = new MigrateDatabaseInitializer + { + TablesToCheck = new[] { "Customer", "Discount", "Order", "Product", "ShoppingCartItem", "QueuedEmailAttachment", "ExportProfile" } + }; + Database.SetInitializer(initializer); + } - /// - /// For tooling support, e.g. EF Migrations - /// - public SmartObjectContext() - : base() - { - } + /// + /// For tooling support, e.g. EF Migrations + /// + public SmartObjectContext() + : base() + { + } - public SmartObjectContext(string nameOrConnectionString) + public SmartObjectContext(string nameOrConnectionString) : base(nameOrConnectionString) { } @@ -49,14 +49,14 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder) // && type.BaseType != null // && type.BaseType.IsGenericType // && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)); - + var typesToRegister = from t in Assembly.GetExecutingAssembly().GetTypes() - where t.Namespace.HasValue() && - t.BaseType != null && - t.BaseType.IsGenericType - let genericType = t.BaseType.GetGenericTypeDefinition() - where genericType == typeof(EntityTypeConfiguration<>) || genericType == typeof(ComplexTypeConfiguration<>) - select t; + where t.Namespace.HasValue() && + t.BaseType != null && + t.BaseType.IsGenericType + let genericType = t.BaseType.GetGenericTypeDefinition() + where genericType == typeof(EntityTypeConfiguration<>) || genericType == typeof(ComplexTypeConfiguration<>) + select t; foreach (var type in typesToRegister) { diff --git a/src/Libraries/SmartStore.Data/SmartObjectContextSaveHook.cs b/src/Libraries/SmartStore.Data/SmartObjectContextSaveHook.cs new file mode 100644 index 0000000000..6c024dad8b --- /dev/null +++ b/src/Libraries/SmartStore.Data/SmartObjectContextSaveHook.cs @@ -0,0 +1,9 @@ +using SmartStore.Data; + +namespace SmartStore.Core.Data.Hooks +{ + public abstract class DbSaveHook : DbSaveHook + where TEntity : class + { + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj index 2260ae49e4..fb594ed6a4 100644 --- a/src/Libraries/SmartStore.Data/SmartStore.Data.csproj +++ b/src/Libraries/SmartStore.Data/SmartStore.Data.csproj @@ -1,6 +1,5 @@  - Debug AnyCPU @@ -11,7 +10,7 @@ Properties SmartStore.Data SmartStore.Data - v4.6.1 + v4.7.2 512 @@ -35,6 +34,7 @@ 4 false latest + false pdbonly @@ -67,21 +67,24 @@ latest - - ..\..\packages\Autofac.4.9.0\lib\net45\Autofac.dll + + ..\..\packages\Autofac.5.2.0\lib\net461\Autofac.dll - ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + ..\..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll True - ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + ..\..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.SqlServer.dll True - ..\..\packages\EntityFramework.SqlServerCompact.6.2.0\lib\net45\EntityFramework.SqlServerCompact.dll + ..\..\packages\EntityFramework.SqlServerCompact.6.4.4\lib\net45\EntityFramework.SqlServerCompact.dll True + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + True ..\..\packages\Microsoft.SqlServer.Scripting.11.0.2100.61\lib\Microsoft.SqlServer.ConnectionInfo.dll @@ -94,20 +97,24 @@ True ..\..\packages\Microsoft.SqlServer.Scripting.11.0.2100.61\lib\Microsoft.SqlServer.Smo.dll - - ..\..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + True ..\..\packages\Microsoft.SqlServer.Compact.4.0.8876.1\lib\net40\System.Data.SqlServerCe.dll - - - ..\..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + @@ -150,363 +157,14 @@ + + + - - - 201403112331027_Initial.cs - - - - 201403210038385_AclPerf.cs - - - - 201403211655284_CurrencyMorePrecision.cs - - - - 201403251610279_AboutPage.cs - - - - 201403262015448_ImportExcel.cs - - - - 201404032144400_Packaging.cs - - - - 201404101903014_AttributeCombinationPrice.cs - - - - 201404170814451_BundleYouSaveLabelFix.cs - - - - 201404232319321_LessValidation.cs - - - - 201404292222025_FixMaxLength.cs - - - - 201405080948530_ProductBulkEditInfo.cs - - - - 201405071457441_FixRes1.cs - - - - 201405282243489_TaskUpdate.cs - - - - 201406122329031_ContentDeliveryNetwork.cs - - - - 201406262150229_Providers.cs - - - - 201407191010096_BetterReturnRequest.cs - - - - 201408071158147_MultistorePoll.cs - - - - 201408291450443_CheckoutCommmentBox.cs - - - - 201409150936394_CustomerNameFormat.cs - - - - 201410091620122_NewRes.cs - - - - 201411182201537_EuEsd.cs - - - - 201411182202431_V21Final.cs - - - - 201412081934318_V211.cs - - - - 201501191849483_WidgetWrapContent.cs - - - - 201501221417371_DefaultViewModeForCategories.cs - - - - 201502191643156_QtyUnitsAndOrderItemTaxRate.cs - - - - 201502202248081_NewIndexesV22.cs - - - - 201502280342294_LocalizedPropertyIndex.cs - - - - 201503032227054_V22Res.cs - - - - 201503101851123_PdfConverterUser.cs - - - - 201502120858030_Licensing.cs - - - - 201504131412183_NewCategoryProperties.cs - - - - 201504171629262_V22Final.cs - - - - 201504232202590_AutoUpdateRes.cs - - - - 201504270946381_TempFileCleanupTask.cs - - - - 201504290937512_SendEmailsManually.cs - - - - 201505052021483_PdfConverterRoleFix.cs - - - - 201505061813010_CountryMultistore.cs - - - - 201505272236124_V222.cs - - - - 201506181858349_AclRecordCustomerRole.cs - - - - 201506211043073_PaymentShippingRestrictions.cs - - - - 201506261018157_Merge.cs - - - - 201506261756463_PrimaryStoreCurrencyMultiStore.cs - - - - 201507132241575_QueuedEmailAttachments.cs - - - - 201507072138058_MessageTemplateAttachments.cs - - - - 201507092146153_DownloadGuidIndex.cs - - - - 201507102159496_TransientMedia.cs - - - - 201507141647299_CustomerTablePerf.cs - - - - 201507200832223_SortFilterHomepageProducts.cs - - - - 201507210952098_PaymentMethodDescription.cs - - - - 201507242008201_WebScheduler.cs - - - - 201507250039446_Merge2.cs - - - - 201508042207146_RemoveKeepAlive.cs - - - - 201508121735397_AddSyncMapping.cs - - - - 201508142203054_CronExpressions.cs - - - - 201509031112324_Merge3.cs - - - - 201508091512101_ExportFramework.cs - - - - 201508211346171_Merge1.cs - - - - 201509021536425_ExportFramework1.cs - - - - 201509150931528_ExportFramework2.cs - - - - 201511271019577_ExportFramework3.cs - - - - 201512151526290_ImportFramework.cs - - - - 201601262000441_ImportFramework1.cs - - - - 201603121451066_ThirdPartyEmailHandOver.cs - - - - 201605020640016_GtinMpnIndex.cs - - - - 201605061916117_SwapColumnMappingValues.cs - - - - 201605111140288_ImportExtraData.cs - - - - 201605191216116_CheckoutAttributeMultiStore.cs - - - - 201605201911421_ExportRevision.cs - - - - 201606241315549_AddressEnhancement.cs - - - - 201607111649138_FixExportDeploymentIsPublic.cs - - - - 201608110948010_MediaStorageData.cs - - - - 201609201852449_log4net.cs - - - - 201609281549565_CategoryBadges.cs - - - - 201611261017137_Search.cs - - - - 201612061904083_ListPaging.cs - - - - 201701121659419_PictureSize.cs - - - - 201701251835419_SpecificationAttributeAlias.cs - - - - 201702061557351_DefaultDeliveryTime.cs - - - - 201702081502539_QuantityEnhancements.cs - - - - 201702091426405_ProductSellingAbroad.cs - - - - 201702141336312_VariantAttributeValuePictures.cs - - - - 201702202112266_VariantAttributeAllowFiltering.cs - - - - 201703021146527_ProductAttributeOptionsSet.cs - - - - 201703161013248_FacetTemplateHint.cs - - - - 201704031946479_SpecificationAttributeAllowFiltering.cs - - - - 201705032238367_RenameColorSquaresRgb.cs - - - - 201705102339006_V3Final.cs - 201705281903241_MoreIndexes.cs @@ -683,7 +341,6 @@ 201907032251575_CategoryExternalLink.cs - 201907250103367_RuleSystem.cs @@ -696,11 +353,210 @@ 201908150749388_V322Resources.cs + + + 201907221803421_GranularPermissions.cs + + + + 201908211821559_Merge5.cs + + + + 201908211825244_ProductTagPublished.cs + + + + 201908261226350_MenuItemMultistore.cs + + + + 201909291043284_BlogAndNewsItemPictures.cs + + + + 201910021805242_RemoveOldPermissions.cs + + + + 201910162004581_Merge6.cs + + + + 201911090805330_ProductVisibility.cs + + + + 201911120909434_Merge7.cs + + + + 201911141820264_ScheduleTaskPriority.cs + + + + 201912051209105_MessageTemplateEmailAddress.cs + + + + 201912111821362_AddNewPermissions.cs + + + + 202001141118171_SpecificationAttributeColorAndPicture.cs + + + + 202001221054109_ManufacturerBottomDescription.cs + + + + 202001301039020_DiscountRuleSets.cs + + + + 202002172101120_PictureMediaRename.cs + + + + 202002180228163_PictureMediaRename1.cs + + + + 202002280005340_MediaManager.cs + + + + 202002191252074_RemoveDiscountRequirements.cs + + + + 202002211011108_GenericMessageTemplate.cs + + + + 202002241354543_ProductCondition.cs + + + + 202002251510114_ManufacturerSubjectToAcl.cs + + + + 202002271206204_ShipmentTrackingUrl.cs + + + + 202003022018038_Merge8.cs + + + + 202003052100521_CustomerRoleMappings.cs + + + + 202003112359492_MediaManager2.cs + + + + 202003251118391_CategoryRuleSets.cs + + + + 202003171812584_CookieManager.cs + + + + 202003311314082_CookieManagerCountries.cs + + + + 202004301922188_RemoveCustomerCustomerRoles.cs + + + + 202005111006305_CampaignSubjectToAcl.cs + + + + 202005201826512_MediaManager3.cs + + + + 202006250801086_V400Resources.cs + + + + 202007160058551_MediaFileIndexReorg.cs + + + + 202007241131557_V401Resources.cs + + + + 202007291847004_NewPropertiesAndIndexes.cs + + + + 202007301117363_AddCustomerRoleOrderAmount.cs + + + + 202008181949580_RenamedCustomerRoleOrderTotal.cs + + + + 202009021705132_Merge9.cs + + + + 202009041122208_ShopIcons.cs + + + + 202009090817220_DeliveryTimeMinMaxDays.cs + + + + 202010011005387_TopicCookieType.cs + + + + 202010030939136_AttributeChoiceBehaviour.cs + + + + 202010121359446_RemoveBlogAndNewsLanguage.cs + + + + 202011091154314_V410Resources.cs + + + + 202012051645539_AddBlogAndNewsLanguage.cs + + + + 202101251149352_V411Resources.cs + + + + 202112171231491_V420Resources.cs + - + + + + + + + + @@ -743,6 +599,7 @@ + @@ -761,7 +618,6 @@ - @@ -805,8 +661,8 @@ - - + + @@ -823,8 +679,12 @@ + + + + @@ -837,272 +697,6 @@ - - 201403112331027_Initial.cs - - - - - 201403210038385_AclPerf.cs - - - 201403211655284_CurrencyMorePrecision.cs - - - 201403251610279_AboutPage.cs - - - 201403262015448_ImportExcel.cs - - - 201404032144400_Packaging.cs - - - 201404101903014_AttributeCombinationPrice.cs - - - 201404170814451_BundleYouSaveLabelFix.cs - - - 201404232319321_LessValidation.cs - - - 201404292222025_FixMaxLength.cs - - - 201405080948530_ProductBulkEditInfo.cs - - - 201405071457441_FixRes1.cs - - - 201405282243489_TaskUpdate.cs - - - 201406122329031_ContentDeliveryNetwork.cs - - - 201406262150229_Providers.cs - - - 201407191010096_BetterReturnRequest.cs - - - 201408071158147_MultistorePoll.cs - - - 201408291450443_CheckoutCommmentBox.cs - - - 201409150936394_CustomerNameFormat.cs - - - 201410091620122_NewRes.cs - - - 201411182201537_EuEsd.cs - - - 201411182202431_V21Final.cs - - - 201412081934318_V211.cs - - - 201501191849483_WidgetWrapContent.cs - - - 201501221417371_DefaultViewModeForCategories.cs - - - 201502191643156_QtyUnitsAndOrderItemTaxRate.cs - - - 201502202248081_NewIndexesV22.cs - - - 201502280342294_LocalizedPropertyIndex.cs - - - 201503032227054_V22Res.cs - - - 201503101851123_PdfConverterUser.cs - - - 201502120858030_Licensing.cs - - - 201504131412183_NewCategoryProperties.cs - - - 201504171629262_V22Final.cs - - - 201504232202590_AutoUpdateRes.cs - - - 201504270946381_TempFileCleanupTask.cs - - - 201504290937512_SendEmailsManually.cs - - - 201505052021483_PdfConverterRoleFix.cs - - - 201505061813010_CountryMultistore.cs - - - 201505272236124_V222.cs - - - 201506181858349_AclRecordCustomerRole.cs - - - 201506211043073_PaymentShippingRestrictions.cs - - - 201506261018157_Merge.cs - - - 201506261756463_PrimaryStoreCurrencyMultiStore.cs - - - 201507132241575_QueuedEmailAttachments.cs - - - 201507072138058_MessageTemplateAttachments.cs - - - 201507092146153_DownloadGuidIndex.cs - - - 201507102159496_TransientMedia.cs - - - 201507141647299_CustomerTablePerf.cs - - - 201507200832223_SortFilterHomepageProducts.cs - - - 201507210952098_PaymentMethodDescription.cs - - - 201507242008201_WebScheduler.cs - - - 201507250039446_Merge2.cs - - - 201508042207146_RemoveKeepAlive.cs - - - 201508121735397_AddSyncMapping.cs - - - 201508142203054_CronExpressions.cs - - - 201509031112324_Merge3.cs - - - 201508091512101_ExportFramework.cs - - - 201508211346171_Merge1.cs - - - 201509021536425_ExportFramework1.cs - - - 201509150931528_ExportFramework2.cs - - - 201511271019577_ExportFramework3.cs - - - 201512151526290_ImportFramework.cs - - - 201601262000441_ImportFramework1.cs - - - 201603121451066_ThirdPartyEmailHandOver.cs - - - 201605020640016_GtinMpnIndex.cs - - - 201605061916117_SwapColumnMappingValues.cs - - - 201605111140288_ImportExtraData.cs - - - 201605191216116_CheckoutAttributeMultiStore.cs - - - 201605201911421_ExportRevision.cs - - - 201606241315549_AddressEnhancement.cs - - - 201607111649138_FixExportDeploymentIsPublic.cs - - - 201608110948010_MediaStorageData.cs - - - 201609201852449_log4net.cs - - - 201609281549565_CategoryBadges.cs - - - 201611261017137_Search.cs - - - 201612061904083_ListPaging.cs - - - 201701121659419_PictureSize.cs - - - 201701251835419_SpecificationAttributeAlias.cs - - - 201702061557351_DefaultDeliveryTime.cs - - - 201702081502539_QuantityEnhancements.cs - - - 201702091426405_ProductSellingAbroad.cs - - - 201702141336312_VariantAttributeValuePictures.cs - - - 201702202112266_VariantAttributeAllowFiltering.cs - - - 201703021146527_ProductAttributeOptionsSet.cs - - - 201703161013248_FacetTemplateHint.cs - - - 201704031946479_SpecificationAttributeAllowFiltering.cs - - - 201705032238367_RenameColorSquaresRgb.cs - - - 201705102339006_V3Final.cs - 201705281903241_MoreIndexes.cs @@ -1238,12 +832,156 @@ 201907250103367_RuleSystem.cs + + 201907221803421_GranularPermissions.cs + 201908050758298_MoveFurtherCustomerFields.cs 201908150749388_V322Resources.cs + + 201908211821559_Merge5.cs + + + 201908211825244_ProductTagPublished.cs + + + 201908261226350_MenuItemMultistore.cs + + + 201909291043284_BlogAndNewsItemPictures.cs + + + 201910021805242_RemoveOldPermissions.cs + + + 201910162004581_Merge6.cs + + + 201911090805330_ProductVisibility.cs + + + 201911120909434_Merge7.cs + + + 201911141820264_ScheduleTaskPriority.cs + + + 201912051209105_MessageTemplateEmailAddress.cs + + + 201912111821362_AddNewPermissions.cs + + + 202001141118171_SpecificationAttributeColorAndPicture.cs + + + 202001221054109_ManufacturerBottomDescription.cs + + + 202001301039020_DiscountRuleSets.cs + + + 202002172101120_PictureMediaRename.cs + + + 202002180228163_PictureMediaRename1.cs + + + 202002280005340_MediaManager.cs + + + 202002191252074_RemoveDiscountRequirements.cs + + + 202002211011108_GenericMessageTemplate.cs + + + 202002241354543_ProductCondition.cs + + + 202002251510114_ManufacturerSubjectToAcl.cs + + + 202002271206204_ShipmentTrackingUrl.cs + + + 202003022018038_Merge8.cs + + + 202003052100521_CustomerRoleMappings.cs + + + 202003112359492_MediaManager2.cs + + + 202003251118391_CategoryRuleSets.cs + + + 202003171812584_CookieManager.cs + + + 202003311314082_CookieManagerCountries.cs + + + 202004301922188_RemoveCustomerCustomerRoles.cs + + + 202005111006305_CampaignSubjectToAcl.cs + + + 202005201826512_MediaManager3.cs + + + 202006250801086_V400Resources.cs + + + 202007160058551_MediaFileIndexReorg.cs + + + 202007241131557_V401Resources.cs + + + 202007291847004_NewPropertiesAndIndexes.cs + + + 202007301117363_AddCustomerRoleOrderAmount.cs + + + 202008181949580_RenamedCustomerRoleOrderTotal.cs + + + 202009021705132_Merge9.cs + + + 202009041122208_ShopIcons.cs + + + 202009090817220_DeliveryTimeMinMaxDays.cs + + + 202010011005387_TopicCookieType.cs + + + 202010030939136_AttributeChoiceBehaviour.cs + + + 202010121359446_RemoveBlogAndNewsLanguage.cs + + + 202011091154314_V410Resources.cs + + + 202012051645539_AddBlogAndNewsLanguage.cs + + + 202101251149352_V411Resources.cs + + + 202112171231491_V420Resources.cs + diff --git a/src/Libraries/SmartStore.Data/SqlCeDataProvider.cs b/src/Libraries/SmartStore.Data/SqlCeDataProvider.cs index ba2681d732..676365febc 100644 --- a/src/Libraries/SmartStore.Data/SqlCeDataProvider.cs +++ b/src/Libraries/SmartStore.Data/SqlCeDataProvider.cs @@ -4,26 +4,20 @@ namespace SmartStore.Data { - public class SqlCeDataProvider : IEfDataProvider + public class SqlCeDataProvider : IEfDataProvider { public virtual IDbConnectionFactory GetConnectionFactory() { - return new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0"); - } - - public bool StoredProceduresSupported - { - get { return false; } + return new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0"); } + public bool StoredProceduresSupported => false; + public DbParameter GetParameter() { return new SqlParameter(); } - public string ProviderInvariantName - { - get { return "System.Data.SqlServerCe.4.0"; } - } + public string ProviderInvariantName => "System.Data.SqlServerCe.4.0"; } } diff --git a/src/Libraries/SmartStore.Data/SqlServerDataProvider.cs b/src/Libraries/SmartStore.Data/SqlServerDataProvider.cs index ae7f5630bd..b331488ed7 100644 --- a/src/Libraries/SmartStore.Data/SqlServerDataProvider.cs +++ b/src/Libraries/SmartStore.Data/SqlServerDataProvider.cs @@ -1,19 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Data.Common; -using System.Data.Entity; +using System.Data.Common; using System.Data.Entity.Infrastructure; using System.Data.SqlClient; -using System.IO; -using System.Text; -using System.Web.Hosting; -using SmartStore.Data.Setup; -using SmartStore.Data.Migrations; -using SmartStore.Utilities; namespace SmartStore.Data { - public class SqlServerDataProvider : IEfDataProvider + public class SqlServerDataProvider : IEfDataProvider { /// /// Get connection factory @@ -27,10 +18,7 @@ public virtual IDbConnectionFactory GetConnectionFactory() /// /// A value indicating whether this data provider supports stored procedures /// - public bool StoredProceduresSupported - { - get { return true; } - } + public bool StoredProceduresSupported => true; /// /// Gets a support database parameter object (used by stored procedures) @@ -41,9 +29,6 @@ public DbParameter GetParameter() return new SqlParameter(); } - public string ProviderInvariantName - { - get { return "System.Data.SqlClient"; } - } - } + public string ProviderInvariantName => "System.Data.SqlClient"; + } } diff --git a/src/Libraries/SmartStore.Data/Utilities/BlogPostConverter.cs b/src/Libraries/SmartStore.Data/Utilities/BlogPostConverter.cs new file mode 100644 index 0000000000..235e020564 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Utilities/BlogPostConverter.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Blogs; +using SmartStore.Core.Domain.Localization; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.IO; +using SmartStore.Utilities; + +namespace SmartStore.Data.Utilities +{ + public sealed class BlogPostConverter + { + private readonly SmartObjectContext _ctx; + + public BlogPostConverter(IDbContext context) + { + _ctx = context as SmartObjectContext; + if (_ctx == null) + throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); + } + + /// + /// Loads all blog post from disk (~/App_Data/Samples/blog/) + /// + /// Language + /// The virtual root path of blogs to load, e.g. "~/Plugins/MyPlugins/BlogPosts". Default is "~/App_Data/Samples/blog". + /// List of deserialized blog posts xml + public IEnumerable LoadAll(Language language, string virtualRootPath = null) + { + Guard.NotNull(language, nameof(language)); + + var dir = ResolveBlogDirectory(language, virtualRootPath); + var files = dir.EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly); + + foreach (var file in files) + { + yield return DeserializeBlog(file.FullName); + } + } + + /// + /// Imports all blog xml files to BlogPost table + /// + /// The virtual root path of blogs to import, e.g. "~/Plugins/MyPlugins/BlogPost". Default is "~/App_Data/Samples/blog". + /// List of new imported blog posts + public IList ImportAll(Language language, string virtualRootPath = null) + { + var blogsImported = new List(); + var table = _ctx.Set(); + var sourceBlogs = LoadAll(language); + var dbBlogMap = table.ToList().ToMultimap(x => x.Title, x => x, StringComparer.OrdinalIgnoreCase); + + foreach (var source in sourceBlogs) + { + if (dbBlogMap.ContainsKey(source.Title)) + { + foreach (var target in dbBlogMap[source.Title]) + { + if (source.Title.HasValue()) target.Title = source.Title; + if (source.MetaTitle.HasValue()) target.MetaTitle = source.MetaTitle; + if (source.MetaDescription.HasValue()) target.MetaDescription = source.MetaDescription; + if (source.Intro.HasValue()) target.Intro = source.Intro; + if (source.Body.HasValue()) target.Body = source.Body; + if (source.Tags.HasValue()) target.Tags = source.Tags; + if (source.CreatedOnUtc != null) target.CreatedOnUtc = source.CreatedOnUtc; + if (source.MediaFile != null) target.MediaFile = source.MediaFile; + if (source.PreviewMediaFile != null) target.PreviewMediaFile = source.PreviewMediaFile; + if (source.SectionBg.HasValue()) target.SectionBg = source.SectionBg; + target.DisplayTagsInPreview = source.DisplayTagsInPreview; + target.PreviewDisplayType = source.PreviewDisplayType; + target.AllowComments = source.AllowComments; + } + } + else + { + var blog = new BlogPost + { + Title = source.Title, + MetaTitle = source.MetaTitle, + MetaDescription = source.MetaDescription, + Intro = source.Intro, + Body = source.Body, + Tags = source.Tags, + DisplayTagsInPreview = source.DisplayTagsInPreview, + CreatedOnUtc = source.CreatedOnUtc, + PreviewDisplayType = source.PreviewDisplayType, + MediaFile = source.MediaFile, + PreviewMediaFile = source.PreviewMediaFile, + AllowComments = source.AllowComments, + SectionBg = source.SectionBg, + IsPublished = true + }; + + blogsImported.Add(blog); + table.Add(blog); + } + } + + _ctx.SaveChanges(); + return blogsImported; + } + + private DirectoryInfo ResolveBlogDirectory(Language language, string virtualRootPath = null) + { + var testPaths = new[] + { + language.LanguageCulture, + language.GetTwoLetterISOLanguageName(), + "en" + }; + + var rootPath = CommonHelper.MapPath(virtualRootPath.NullEmpty() ?? "~/App_Data/Samples/blog/"); + foreach (var path in testPaths.Select(x => Path.Combine(rootPath, x))) + { + if (Directory.Exists(path)) + { + return new DirectoryInfo(path); + } + } + + throw new DirectoryNotFoundException($"Could not obtain an blog post path for language {language.LanguageCulture}. Fallback to 'en' failed, because directory does not exist."); + } + + private BlogPost DeserializeBlog(string fullPath) + { + return DeserializeDocument(XDocument.Load(fullPath)); + } + + private BlogPost DeserializeDocument(XDocument doc) + { + var result = new BlogPost(); + var nodes = doc.Root.Nodes().OfType(); + + foreach (var node in nodes) + { + var value = node.Value.Trim(); + + switch (node.Name.LocalName) + { + case "Title": + result.Title = value; + break; + case "MetaTitle": + result.MetaTitle = value; + break; + case "MetaDescription": + result.MetaDescription = value; + break; + case "Intro": + result.Intro = value; + break; + case "Body": + result.Body = value; + break; + case "Tags": + result.Tags = value; + break; + case "DisplayTags": + result.DisplayTagsInPreview = value.ToBool(); + break; + case "CreatedOn": + result.CreatedOnUtc = value.ToDateTime(new DateTime()).Value; + break; + case "DisplayType": + result.PreviewDisplayType = (PreviewDisplayType)int.Parse(value); + break; + case "Image": + var seName = GetSeName(Path.GetFileNameWithoutExtension(value)); + result.MediaFile = CreateImage(value, seName); + break; + case "ImagePreview": + seName = GetSeName(Path.GetFileNameWithoutExtension(value)); + result.PreviewMediaFile = CreateImage(value, seName); + break; + case "Comments": + result.AllowComments = value.ToBool(); + break; + case "SectionBg": + result.SectionBg = value; + break; + } + } + + return result; + } + + private MediaFile CreateImage(string fileName, string seoFilename = null) + { + try + { + var ext = Path.GetExtension(fileName); + var mimeType = MimeTypes.MapNameToMimeType(ext); + var path = Path.Combine(CommonHelper.MapPath("~/App_Data/Samples/blog/"), fileName).Replace('/', '\\'); + var buffer = File.ReadAllBytes(path); + var now = DateTime.UtcNow; + + var name = seoFilename.HasValue() + ? seoFilename.Truncate(100) + ext + : Path.GetFileName(fileName).ToLower().Replace('_', '-'); + + var file = new MediaFile + { + Name = name, + MediaType = "image", + MimeType = mimeType, + Extension = ext.EmptyNull().TrimStart('.'), + CreatedOnUtc = now, + UpdatedOnUtc = now, + Size = buffer.Length, + MediaStorage = new MediaStorage { Data = buffer }, + Version = 1 // so that FolderId is set later during track detection + }; + + return file; + } + catch (Exception ex) + { + // Throw ex; + System.Diagnostics.Debug.WriteLine(ex.Message); + return null; + } + } + + private string GetSeName(string name) + { + return SeoHelper.GetSeName(name, true, false); + } + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs b/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs index 4d2f2747af..796f12c1fa 100644 --- a/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs +++ b/src/Libraries/SmartStore.Data/Utilities/DataMigrator.cs @@ -1,10 +1,15 @@ using System; using System.Collections.Generic; using System.Data.Entity; +using System.Globalization; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using System.Xml.Linq; +using System.Xml.Serialization; +using Newtonsoft.Json; +using SmartStore.Collections; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; @@ -12,20 +17,28 @@ using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Configuration; using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Domain.Directory; +using SmartStore.Core.Domain.Discounts; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.Payments; using SmartStore.Core.Domain.Security; +using SmartStore.Core.Domain.Shipping; using SmartStore.Core.Infrastructure; using SmartStore.Core.IO; -using SmartStore.Data.Setup; +using SmartStore.Core.Logging; +using SmartStore.Core.Security; +using SmartStore.Rules; +using SmartStore.Rules.Domain; using SmartStore.Utilities; +using SmartStore.Utilities.ObjectPools; using EfState = System.Data.Entity.EntityState; namespace SmartStore.Data.Utilities { public static class DataMigrator - { + { #region Download.ProductId /// @@ -39,23 +52,23 @@ public static int SetDownloadProductId(IDbContext context) if (ctx == null) throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); - const string entityName = "Product"; + const string entityName = "Product"; #pragma warning disable 612, 618 // Get all products with a download var productQuery = from p in ctx.Set().AsNoTracking() - where (p.DownloadId != 0) - orderby p.Id - select new { p.Id, p.DownloadId }; + where (p.DownloadId != 0) + orderby p.Id + select new { p.Id, p.DownloadId }; #pragma warning restore 612, 618 var downloads = context.Set().Select(x => x).ToDictionary(x => x.Id); - + int pageIndex = -1; while (true) { var products = PagedList.Create(productQuery, ++pageIndex, 1000); - + foreach (var p in products) { try @@ -89,464 +102,479 @@ orderby p.Id /// When null, Product.ProductPictures gets called. /// Product to fix /// true when value was fixed - public static bool FixProductMainPictureId(IDbContext context, Product product, IEnumerable entities = null) - { - Guard.NotNull(product, nameof(product)); - - // INFO: this method must be able to handle pre-save state also. - - var ctx = context as SmartObjectContext; - if (ctx == null) - throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); - - entities = entities ?? product.ProductPictures; - if (entities == null) - return false; - - var transientEntities = entities.Where(x => x.Id == 0); - - var sortedEntities = entities - // Remove transient entities - .Except(transientEntities) - .OrderBy(x => x.DisplayOrder) - .ThenBy(x => x.Id) - .Select(x => ctx.Entry(x)) - // Remove deleted and detached entities - .Where(x => x.State != EfState.Deleted && x.State != EfState.Detached) - .Select(x => x.Entity) - // Added/transient entities must be appended - .Concat(transientEntities.OrderBy(x => x.DisplayOrder)); - - var newMainPictureId = sortedEntities.FirstOrDefault()?.PictureId; - - if (newMainPictureId != product.MainPictureId) - { - product.MainPictureId = newMainPictureId; - return true; - } - - return false; - } - - /// - /// Traverses all products and fixes 'MainPictureId' property values if it is out of sync. - /// - /// Database context (must be ) - /// Minimum modified or created date of products to process. Pass null to fix all products. - /// The total count of fixed and updated product entities - public static int FixProductMainPictureIds(IDbContext context, DateTime? ifModifiedSinceUtc = null) - { - return FixProductMainPictureIds(context, false, ifModifiedSinceUtc); - } - - /// - /// Called from migration seeder and only processes product entities without MainPictureId value. - /// - /// The total count of fixed and updated product entities - internal static int FixProductMainPictureIds(IDbContext context, bool initial, DateTime? ifModifiedSinceUtc = null) - { - var ctx = context as SmartObjectContext; - if (ctx == null) - throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); - - var query = from p in ctx.Set().AsNoTracking() - where (!initial || p.MainPictureId == null) && (ifModifiedSinceUtc == null || p.UpdatedOnUtc >= ifModifiedSinceUtc.Value) - orderby p.Id - select new { p.Id, p.MainPictureId }; - - // Key = ProductId, Value = MainPictureId - var toUpate = new Dictionary(); - - // 1st pass - int pageIndex = -1; - while (true) - { - var products = PagedList.Create(query, ++pageIndex, 1000); - var 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]; - } - - // Update only if fixed PictureId differs from current - if (fixedPictureId != p.MainPictureId) - { - toUpate.Add(p.Id, fixedPictureId); - } - } - - 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); - } - - context.SaveChanges(); - tx.Commit(); - } - } - - return toUpate.Count; - } - - private static IDictionary GetPoductPictureMap(SmartObjectContext context, IEnumerable productIds) - { - var map = new Dictionary(); - - var query = from pp in context.Set().AsNoTracking() - where productIds.Contains(pp.ProductId) - group pp by pp.ProductId into g - select new - { - ProductId = g.Key, - PictureIds = g.OrderBy(x => x.DisplayOrder).ThenBy(x => x.Id) - .Take(1) - .Select(x => x.PictureId) - }; - - map = query.ToList().ToDictionary(x => x.ProductId, x => x.PictureIds.First()); - - return map; - } - - #endregion - - #region MoveFsMedia (V3.1) - - /// - /// Reorganizes media files in subfolders for V3.1 - /// - /// - /// - public static int MoveFsMedia(IDbContext context) - { - var ctx = context as SmartObjectContext; - if (ctx == null) - throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); - - int dirMaxLength = 4; - - // Check whether FS storage provider is active... - var setting = context.Set().FirstOrDefault(x => x.Name == "Media.Storage.Provider"); - if (setting == null || !setting.Value.IsCaseInsensitiveEqual("MediaStorage.SmartStoreFileSystem")) - { - // DB provider is active: no need to move anything. - return 0; - } - - // What a huge, fucking hack! > IMediaFileSystem is defined in an - // assembly which we don't reference from here. But it also implements - // IFileSystem, which we can cast to. - var fsType = Type.GetType("SmartStore.Services.Media.IMediaFileSystem, SmartStore.Services"); - var fs = EngineContext.Current.Resolve(fsType) as IFileSystem; - - // Pattern for file matching. E.g. matches 0000234-0.png - var rg = new Regex(@"^([0-9]{7})-0[.](.{3,4})$", RegexOptions.Compiled | RegexOptions.Singleline); - var subfolders = new Dictionary(); - int i = 0; - - // Get root files - var files = fs.ListFiles(""); - foreach (var chunk in files.Slice(500)) - { - foreach (var file in chunk) - { - var match = rg.Match(file.Name); - if (match.Success) - { - var name = match.Groups[1].Value; - var ext = match.Groups[2].Value; - // The new file name without trailing -0 - var newName = string.Concat(name, ".", ext); - // The subfolder name, e.g. 0024, when file name is 0024893.png - var dirName = name.Substring(0, dirMaxLength); - - if (!subfolders.TryGetValue(dirName, out string subfolder)) - { - // Create subfolder "Storage/0000" - subfolder = fs.Combine("Storage", dirName); - fs.TryCreateFolder(subfolder); - subfolders[dirName] = subfolder; - } - - // Build destination path - var destinationPath = fs.Combine(subfolder, newName); - - // Move the file now! - fs.RenameFile(file.Path, destinationPath); - i++; - } - } - } - - return i; - } - - #endregion - - #region Address Formats - - public static int ImportAddressFormats(IDbContext context) - { - var ctx = context as SmartObjectContext; - if (ctx == null) - throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); - - var filePath = CommonHelper.MapPath("~/App_Data/AddressFormats.xml"); - - if (!File.Exists(filePath)) - { - return 0; - } - - var countries = ctx.Set() - .Where(x => x.AddressFormat == null) - .ToList() - .ToDictionarySafe(x => x.TwoLetterIsoCode, StringComparer.OrdinalIgnoreCase); - - var doc = XDocument.Load(filePath); - - foreach (var node in doc.Root.Nodes().OfType()) - { - var code = node.Attribute("code")?.Value?.Trim(); - var format = node.Value.Trim(); - - if (code.HasValue() && countries.TryGetValue(code, out var country)) - { - country.AddressFormat = format; - } - } - - return ctx.SaveChanges(); - } - - #endregion - - #region MoveCustomerFields (V3.2) - - /// - /// Moves several customer fields saved as generic attributes to customer entity (Title, FirstName, LastName, BirthDate, Company, CustomerNumber) - /// - /// Database context (must be ) - /// The total count of fixed and updated customer entities - public static int MoveCustomerFields(IDbContext context) - { - var ctx = context as SmartObjectContext; - if (ctx == null) - throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); - - // We delete attrs only if the WHOLE migration succeeded - var attrIdsToDelete = new List(1000); - var gaTable = context.Set(); - var candidates = new[] { "Title", "FirstName", "LastName", "Company", "CustomerNumber", "DateOfBirth" }; - - var query = gaTable - .AsNoTracking() - .Where(x => x.KeyGroup == "Customer" && candidates.Contains(x.Key)) - .OrderBy(x => x.Id); - - int numUpdated = 0; - - using (var scope = new DbContextScope(ctx: context, validateOnSave: false, hooksEnabled: false, autoCommit: false)) - { - for (var pageIndex = 0; pageIndex < 9999999; ++pageIndex) - { - var attrs = new PagedList(query, pageIndex, 250); - - var customerIds = attrs.Select(a => a.EntityId).Distinct().ToArray(); - var customers = context.Set() - .Where(x => customerIds.Contains(x.Id)) - .ToDictionary(x => x.Id); - - // Move attrs one by one to customer - foreach (var attr in attrs) - { - var customer = customers.Get(attr.EntityId); - if (customer == null) - continue; - - switch (attr.Key) - { - case "Title": - customer.Title = attr.Value?.Truncate(100); - break; - case "FirstName": - customer.FirstName = attr.Value?.Truncate(225); - break; - case "LastName": - customer.LastName = attr.Value?.Truncate(225); - break; - case "Company": - customer.Company = attr.Value?.Truncate(255); - break; - case "CustomerNumber": - customer.CustomerNumber = attr.Value?.Truncate(100); - break; - case "DateOfBirth": - customer.BirthDate = attr.Value?.Convert(); - break; - } - - // Update FullName - var parts = new[] { customer.Title, customer.FirstName, customer.LastName }; - customer.FullName = string.Join(" ", parts.Where(x => x.HasValue())).NullEmpty(); - - attrIdsToDelete.Add(attr.Id); - } - - // Save batch - numUpdated += scope.Commit(); - - // Breathe - context.DetachAll(); - - if (!attrs.HasNextPage) - break; - } - - // Everything worked out, now delete all orpahned attributes - if (attrIdsToDelete.Count > 0) - { - try - { - // Don't rollback migration when this fails - var stubs = attrIdsToDelete.Select(x => new GenericAttribute { Id = x }).ToList(); - foreach (var chunk in stubs.Slice(500)) - { - chunk.Each(x => gaTable.Attach(x)); - gaTable.RemoveRange(chunk); - scope.Commit(); - } - } - catch (Exception ex) - { - var msg = ex.Message; - } - } - } - - return numUpdated; - } + public static bool FixProductMainPictureId(IDbContext context, Product product, IEnumerable entities = null) + { + Guard.NotNull(product, nameof(product)); + + // INFO: this method must be able to handle pre-save state also. + + var ctx = context as SmartObjectContext; + if (ctx == null) + throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); + + entities = entities ?? product.ProductPictures; + if (entities == null) + return false; + + var transientEntities = entities.Where(x => x.Id == 0); + + var sortedEntities = entities + // Remove transient entities + .Except(transientEntities) + .OrderBy(x => x.DisplayOrder) + .ThenBy(x => x.Id) + .Select(x => ctx.Entry(x)) + // Remove deleted and detached entities + .Where(x => x.State != EfState.Deleted && x.State != EfState.Detached) + .Select(x => x.Entity) + // Added/transient entities must be appended + .Concat(transientEntities.OrderBy(x => x.DisplayOrder)); + var newMainPictureId = sortedEntities.FirstOrDefault()?.MediaFileId; + + if (newMainPictureId != product.MainPictureId) + { + product.MainPictureId = newMainPictureId; + return true; + } + + return false; + } /// - /// Moves several customer fields saved as generic attributes to customer entity - /// (Gender, ZipPostalCode, VatNumberStatusId, TimeZoneId, TaxDisplayTypeId, CountryId, CurrencyId, LanguageId, LastForumVisit, LastUserAgent) + /// Traverses all products and fixes 'MainPictureId' property values if it is out of sync. /// /// Database context (must be ) - /// The total count of fixed and updated customer entities - public static int MoveFurtherCustomerFields(IDbContext context) + /// Minimum modified or created date of products to process. Pass null to fix all products. + /// The total count of fixed and updated product entities + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int FixProductMainPictureIds(IDbContext context, DateTime? ifModifiedSinceUtc = null) + { + return FixProductMainPictureIds(context, false, ifModifiedSinceUtc); + } + + /// + /// Called from migration seeder and only processes product entities without MainPictureId value. + /// + /// The total count of fixed and updated product entities + internal static int FixProductMainPictureIds(IDbContext context, bool initial, DateTime? ifModifiedSinceUtc = null) { var ctx = context as SmartObjectContext; if (ctx == null) throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); - // We delete attrs only if the WHOLE migration succeeded - var attrIdsToDelete = new List(1000); - var gaTable = context.Set(); - var candidates = new[] { "Gender", "VatNumberStatusId", "TimeZoneId", "TaxDisplayTypeId", "LastForumVisit", "LastUserAgent", "LastUserDeviceType" }; - - var query = gaTable - .AsNoTracking() - .Where(x => x.KeyGroup == "Customer" && candidates.Contains(x.Key)) - .OrderBy(x => x.Id); + var query = from p in ctx.Set().AsNoTracking() + where (!initial || p.MainPictureId == null) && (ifModifiedSinceUtc == null || p.UpdatedOnUtc >= ifModifiedSinceUtc.Value) + orderby p.Id + select new { p.Id, p.MainPictureId }; - int numUpdated = 0; + // Key = ProductId, Value = MainPictureId + var toUpdate = new Dictionary(); - using (var scope = new DbContextScope(ctx: context, validateOnSave: false, hooksEnabled: false, autoCommit: false)) + // 1st pass + int pageIndex = -1; + while (true) { - for (var pageIndex = 0; pageIndex < 9999999; ++pageIndex) + var products = PagedList.Create(query, ++pageIndex, 1000); + var map = GetPoductPictureMap(ctx, products.Select(x => x.Id).ToArray()); + + foreach (var p in products) { - var attrs = new PagedList(query, pageIndex, 250); + int? fixedPictureId = null; + if (map.ContainsKey(p.Id)) + { + // Product has still a pic. + fixedPictureId = map[p.Id]; + } - var customerIds = attrs.Select(a => a.EntityId).Distinct().ToArray(); - var customers = context.Set() - .Where(x => customerIds.Contains(x.Id)) - .ToDictionary(x => x.Id); + // Update only if fixed PictureId differs from current + if (fixedPictureId != p.MainPictureId) + { + toUpdate.Add(p.Id, fixedPictureId); + } + } - // Move attrs one by one to customer - foreach (var attr in attrs) + if (!products.HasNextPage) + break; + } + + // 2nd pass + foreach (var chunk in toUpdate.Slice(1000)) + { + using (var tx = ctx.Database.BeginTransaction()) + { + foreach (var kvp in chunk) { - var customer = customers.Get(attr.EntityId); - if (customer == null) - continue; + context.ExecuteSqlCommand("Update [Product] Set [MainPictureId] = {0} WHERE [Id] = {1}", false, null, kvp.Value, kvp.Key); + } - switch (attr.Key) + context.SaveChanges(); + tx.Commit(); + } + } + + return toUpdate.Count; + } + + private static IDictionary GetPoductPictureMap(SmartObjectContext context, IEnumerable productIds) + { + var map = new Dictionary(); + + var query = from pp in context.Set().AsNoTracking() + where productIds.Contains(pp.ProductId) + group pp by pp.ProductId into g + select new { - case "Gender": - customer.Gender = attr.Value?.Truncate(100); - break; - case "VatNumberStatusId": - customer.VatNumberStatusId = attr.Value.Convert(); - break; - case "TimeZoneId": - customer.TimeZoneId = attr.Value?.Truncate(255); - break; - case "TaxDisplayTypeId": - customer.TaxDisplayTypeId = attr.Value.Convert(); - break; - case "LastForumVisit": - customer.LastForumVisit = attr.Value.Convert(); - break; - case "LastUserAgent": - customer.LastUserAgent = attr.Value.Convert(); - break; - case "LastUserDeviceType": - // TODO: split - customer.LastUserDeviceType = attr.Value.Convert(); - break; + ProductId = g.Key, + PictureIds = g.OrderBy(x => x.DisplayOrder).ThenBy(x => x.Id) + .Take(1) + .Select(x => x.MediaFileId) + }; + + map = query.ToList().ToDictionary(x => x.ProductId, x => x.PictureIds.First()); + + return map; + } + + #endregion + + #region MoveFsMedia (V3.1) + + /// + /// Reorganizes media files in subfolders for V3.1 + /// + /// + /// + public static int MoveFsMedia(IDbContext context) + { + var ctx = context as SmartObjectContext; + if (ctx == null) + throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); + + int dirMaxLength = 4; + + // Check whether FS storage provider is active... + var setting = context.Set().FirstOrDefault(x => x.Name == "Media.Storage.Provider"); + if (setting == null || !setting.Value.IsCaseInsensitiveEqual("MediaStorage.SmartStoreFileSystem")) + { + // DB provider is active: no need to move anything. + return 0; + } + + // What a huge, fucking hack! > IMediaFileSystem is defined in an + // assembly which we don't reference from here. But it also implements + // IFileSystem, which we can cast to. + var fsType = Type.GetType("SmartStore.Services.Media.IMediaFileSystem, SmartStore.Services"); + var fs = EngineContext.Current.Resolve(fsType) as IFileSystem; + + // Pattern for file matching. E.g. matches 0000234-0.png + var rg = new Regex(@"^([0-9]{7})-0[.](.{3,4})$", RegexOptions.Compiled | RegexOptions.Singleline); + var subfolders = new Dictionary(); + int i = 0; + + // Get root files + var files = fs.ListFiles(""); + foreach (var chunk in files.Slice(500)) + { + foreach (var file in chunk) + { + var match = rg.Match(file.Name); + if (match.Success) + { + var name = match.Groups[1].Value; + var ext = match.Groups[2].Value; + // The new file name without trailing -0 + var newName = string.Concat(name, ".", ext); + // The subfolder name, e.g. 0024, when file name is 0024893.png + var dirName = name.Substring(0, dirMaxLength); + + if (!subfolders.TryGetValue(dirName, out string subfolder)) + { + // Create subfolder "Storage/0000" + subfolder = fs.Combine("Storage", dirName); + fs.CreateFolder(subfolder); + subfolders[dirName] = subfolder; } - attrIdsToDelete.Add(attr.Id); + // Build destination path + var destinationPath = fs.Combine(subfolder, newName); + + // Move the file now! + fs.RenameFile(file.Path, destinationPath); + i++; } + } + } + + return i; + } + + #endregion + + #region Address Formats + + public static int ImportAddressFormats(IDbContext context) + { + var ctx = context as SmartObjectContext; + if (ctx == null) + throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); + + var filePath = CommonHelper.MapPath("~/App_Data/AddressFormats.xml"); + + if (!File.Exists(filePath)) + { + return 0; + } + + var countries = ctx.Set() + .Where(x => x.AddressFormat == null) + .ToList() + .ToDictionarySafe(x => x.TwoLetterIsoCode, StringComparer.OrdinalIgnoreCase); + + var doc = XDocument.Load(filePath); + + foreach (var node in doc.Root.Nodes().OfType()) + { + var code = node.Attribute("code")?.Value?.Trim(); + var format = node.Value.Trim(); + + if (code.HasValue() && countries.TryGetValue(code, out var country)) + { + country.AddressFormat = format; + } + } + + return ctx.SaveChanges(); + } - // Save batch - numUpdated += scope.Commit(); + #endregion + + #region MoveCustomerFields (V3.2) + + class CustomerStub + { + public int Id { get; set; } + public IEnumerable Attributes { get; set; } + } + + class AttributeStub + { + public int Id { get; set; } + public string Key { get; set; } + public string Value { get; set; } + } - // Breathe - context.DetachAll(); + /// + /// Moves several customer fields saved as generic attributes to customer entity (Title, FirstName, LastName, BirthDate, Company, CustomerNumber) + /// + /// Database context (must be ) + /// The total count of fixed and updated customer entities + public static int MoveCustomerFields( + IDbContext context, + Action, string, string> updater, + params string[] candidates) + { + Guard.NotNull(updater, nameof(updater)); + + if (!(context is SmartObjectContext ctx)) + throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); - if (!attrs.HasNextPage) + if (candidates.Length == 0) + return 0; + + const int pageSize = 1000; + var gaTable = context.Set().AsNoTracking(); + //var candidates = new[] { "Title", "FirstName", "LastName", "Company", "CustomerNumber", "DateOfBirth" }; + + var query = ( + from a in gaTable + where a.KeyGroup == "Customer" && candidates.Contains(a.Key) + group a by a.EntityId into grps + where grps.Any() + select new CustomerStub + { + Id = grps.Key, + Attributes = grps.Select(a2 => new AttributeStub { Id = a2.Id, Key = a2.Key, Value = a2.Value }) + }) + .OrderBy(x => x.Id); + + int numAffectedRows = 0; + int numUpdated = 0; + + using (var scope = new DbContextScope(ctx: context, validateOnSave: false, hooksEnabled: false, autoCommit: false)) + { + for (var pageIndex = 0; pageIndex < 9999999; pageIndex++) + { + var data = query + .Skip(pageIndex * pageSize) + .Take(pageSize) + .ToList(); + + foreach (var chunk in data.Slice(100)) + { + numAffectedRows += GenerateAndExecuteSql(chunk); + } + + numUpdated += data.Count; + + if (data.Count == 0) break; } + } + + return numUpdated; + + int GenerateAndExecuteSql(IEnumerable customers) + { + var sb = PooledStringBuilder.Rent(); - // Everything worked out, now delete all orpahned attributes - if (attrIdsToDelete.Count > 0) + foreach (var c in customers) { - try + var attrs = c.Attributes.ToArray(); + + if (attrs.Length == 0) + continue; + + var columns = new Dictionary(); + + /// Generate UPDATE Customer statement + foreach (var attr in attrs) + { + updater(columns, attr.Key, attr.Value); + } + + if (columns.Count == 0) + continue; + + sb.Append("UPDATE [Customer] SET"); + + int i = 0; + foreach (var kvp in columns) { - // Don't rollback migration when this fails - var stubs = attrIdsToDelete.Select(x => new GenericAttribute { Id = x }).ToList(); - foreach (var chunk in stubs.Slice(500)) + sb.Append(" "); + if (i > 0) sb.Append(", "); + + sb.Append($"[{kvp.Key}] = "); + + if (kvp.Value is int n) + { + sb.Append($"{n.ToString(CultureInfo.InvariantCulture)}"); + } + else if (kvp.Value is DateTime d) + { + sb.Append($"'{d.ToIso8601String()}'"); + } + else if (kvp.Value == null) + { + sb.Append("NULL"); + } + else { - chunk.Each(x => gaTable.Attach(x)); - gaTable.RemoveRange(chunk); - scope.Commit(); + sb.Append($"N'{kvp.Value}'"); } + + i++; } - catch (Exception ex) + + sb.Append(" WHERE Id = {0}".FormatInvariant(c.Id)); + sb.Append("\n"); + + // Generate all GenericAttribute DELETE statements + for (i = 0; i < attrs.Length; i++) { - var msg = ex.Message; + var a = attrs[i]; + sb.AppendLine($"DELETE FROM [GenericAttribute] WHERE [Id] = {a.Id}"); } } + + var sql = sb.ToStringAndReturn(); + if (sql.HasValue()) + { + return ctx.Execute(sql); + } + + return 0; } + } - return numUpdated; + public static int DeleteGuestCustomerGenericAttributes(IDbContext context, TimeSpan olderThan) + { + if (!(context is SmartObjectContext ctx)) + { + throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); + } + + int numTotalDeleted = 0; + var maxDate = DateTime.UtcNow - olderThan; + + var sql = @" +DELETE TOP(50000) [g] + FROM [dbo].[GenericAttribute] AS [g] + LEFT OUTER JOIN [dbo].[Customer] AS [c] ON c.Id = g.EntityId + LEFT OUTER JOIN [dbo].[Order] AS [o] ON c.Id = o.CustomerId + LEFT OUTER JOIN [dbo].[CustomerContent] AS [cc] ON c.Id = cc.CustomerId + LEFT OUTER JOIN [dbo].[Forums_PrivateMessage] AS [pm] ON c.Id = pm.ToCustomerId + LEFT OUTER JOIN [dbo].[Forums_Post] AS [fp] ON c.Id = fp.CustomerId + LEFT OUTER JOIN [dbo].[Forums_Topic] AS [ft] ON c.Id = ft.CustomerId + WHERE g.KeyGroup = 'Customer' AND c.Username IS Null AND c.Email IS NULL AND c.IsSystemAccount = 0 AND c.LastActivityDateUtc < @p0 + AND (NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[ShoppingCartItem] AS [sci] WHERE c.Id = sci.CustomerId )) + AND (NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[Order] AS [o1] WHERE c.Id = o1.CustomerId )) + AND (NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[CustomerContent] AS [cc1] WHERE c.Id = cc1.CustomerId )) + AND (NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[Forums_PrivateMessage] AS [pm1] WHERE c.Id = pm1.ToCustomerId )) + AND (NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[Forums_Post] AS [fp1] WHERE c.Id = fp1.CustomerId )) + AND (NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[Forums_Topic] AS [ft1] WHERE c.Id = ft1.CustomerId )) +"; + + while (true) + { + var numDeleted = ctx.Execute(sql, maxDate); + if (numDeleted == 0) + break; + + numTotalDeleted += numDeleted; + } + + return numTotalDeleted; } + public static int DeleteGuestCustomers(IDbContext context, TimeSpan olderThan) + { + if (!(context is SmartObjectContext ctx)) + { + throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); + } + + int numTotalDeleted = 0; + var maxDate = DateTime.UtcNow - olderThan; + + var sql = @" +DELETE TOP(20000) [c] + FROM [dbo].[Customer] AS [c] + LEFT OUTER JOIN [dbo].[Order] AS [o] ON c.Id = o.CustomerId + LEFT OUTER JOIN [dbo].[CustomerContent] AS [cc] ON c.Id = cc.CustomerId + LEFT OUTER JOIN [dbo].[Forums_PrivateMessage] AS [pm] ON c.Id = pm.ToCustomerId + LEFT OUTER JOIN [dbo].[Forums_Post] AS [fp] ON c.Id = fp.CustomerId + LEFT OUTER JOIN [dbo].[Forums_Topic] AS [ft] ON c.Id = ft.CustomerId + WHERE c.Username IS Null AND c.Email IS NULL AND c.IsSystemAccount = 0 AND c.LastActivityDateUtc < @p0 + AND (NOT EXISTS (SELECT 1 AS x FROM [dbo].[ShoppingCartItem] AS [sci] WHERE c.Id = sci.CustomerId )) + AND (NOT EXISTS (SELECT 1 AS x FROM [dbo].[Order] AS [o1] WHERE c.Id = o1.CustomerId )) + AND (NOT EXISTS (SELECT 1 AS x FROM [dbo].[CustomerContent] AS [cc1] WHERE c.Id = cc1.CustomerId )) + AND (NOT EXISTS (SELECT 1 AS x FROM [dbo].[Forums_PrivateMessage] AS [pm1] WHERE c.Id = pm1.ToCustomerId )) + AND (NOT EXISTS (SELECT 1 AS x FROM [dbo].[Forums_Post] AS [fp1] WHERE c.Id = fp1.CustomerId )) + AND (NOT EXISTS (SELECT 1 AS x FROM [dbo].[Forums_Topic] AS [ft1] WHERE c.Id = ft1.CustomerId )) +"; + + while (true) + { + var numDeleted = ctx.Execute(sql, maxDate); + if (numDeleted == 0) + break; + + numTotalDeleted += numDeleted; + } + + return numTotalDeleted; + } #endregion @@ -595,14 +623,6 @@ public static void CreateSystemMenus(IDbContext context) using (var scope = new DbContextScope(ctx: context, validateOnSave: false, hooksEnabled: false, autoCommit: false)) { - var permissionMigrator = new PermissionMigrator(ctx); - permissionMigrator.AddPermission(new PermissionRecord - { - Name = "Admin area. Manage Menus", - SystemName = "ManageMenus", - Category = "Content Management" - }, new string[] { SystemCustomerRoleNames.Administrators }); - var menuSet = context.Set(); var menuItemSet = context.Set(); var defaultLang = context.Set().OrderBy(x => x.DisplayOrder).First(); @@ -761,6 +781,15 @@ public static void CreateSystemMenus(IDbContext context) Model = "topic:paymentinfo", DisplayOrder = ++order }); + menuItemSet.Add(new MenuItemRecord + { + MenuId = footerService.Id, + ProviderName = routeProvider, + Model = routeTemplate.FormatInvariant("CookieManager"), + Title = "Cookie Manager", + DisplayOrder = ++order, + CssClass = "cookie-manager" + }); scope.Commit(); order = 0; @@ -930,5 +959,926 @@ string GetResource(string name) } #endregion + + #region GranularPermissions (V4.0) + + public static void AddGranularPermissions(IDbContext context) + { + var ctx = context as SmartObjectContext; + if (ctx == null) + { + throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); + } + + var mappingSet = ctx.Set(); + var oldPermissions = GetOldPermissions(); + + var allRoleIds = ctx.Set() + .AsNoTracking() + .Where(x => !string.IsNullOrEmpty(x.SystemName)) + .Select(x => new { x.Id, x.SystemName }) + .ToList() + .ToDictionarySafe(x => x.SystemName, x => x.Id, StringComparer.OrdinalIgnoreCase); + + allRoleIds.TryGetValue(SystemCustomerRoleNames.Administrators, out var adminRoleId); + allRoleIds.TryGetValue(SystemCustomerRoleNames.ForumModerators, out var forumModRoleId); + allRoleIds.TryGetValue(SystemCustomerRoleNames.Registered, out var registeredRoleId); + allRoleIds.TryGetValue(SystemCustomerRoleNames.Guests, out var guestRoleId); + allRoleIds.Clear(); + + // Mapping has no entity and no navigation property -> use SQL. + var oldMappings = context.SqlQuery("select * from [dbo].[PermissionRecord_Role_Mapping]") + .ToList() + .ToMultimap(x => x.PermissionRecord_Id, x => x.CustomerRole_Id); + + var permissionToRoles = new Multimap(StringComparer.OrdinalIgnoreCase); + var allPermissions = ctx.Set() + .AsNoTracking() + .Select(x => new { x.Id, x.SystemName }) + .ToList(); + + foreach (var permission in allPermissions) + { + var roleIds = oldMappings.ContainsKey(permission.Id) + ? oldMappings[permission.Id] + : Enumerable.Empty(); + + permissionToRoles.AddRange(permission.SystemName, roleIds); + } + allPermissions.Clear(); + + using (var scope = new DbContextScope(ctx: context, validateOnSave: false, hooksEnabled: false, autoCommit: false)) + { + var permissionSystemNames = PermissionHelper.GetPermissions(typeof(Permissions)); + + var newPermissions = InsertPermissions(scope, permissionSystemNames); + + // Migrate mappings of standard permissions (whether the new permission is granted). + foreach (var kvp in oldPermissions) + { + foreach (var name in kvp.Value) + { + Allow(kvp.Key, newPermissions[name]); + } + } + // Commit to avoid duplicate mappings! + scope.Commit(); + + // Add mappings for new permissions. + AllowForRole(adminRoleId, + newPermissions[Permissions.Cart.Read], + newPermissions[Permissions.System.Rule.Self]); + + // Add mappings originally added by old migrations. + // We had to remove these migration statements again because the table does not yet exist at this time. + AllowForRole(adminRoleId, + newPermissions[Permissions.Configuration.Export.Self], + newPermissions[Permissions.Configuration.Import.Self], + newPermissions[Permissions.System.UrlRecord.Self], + newPermissions[Permissions.Cms.Menu.Self]); + + scope.Commit(); + + // Migrate known plugin permissions. + var pluginPermissionNames = new Dictionary + { + { "ManageDebitoor", "SmartStore.Debitoor.Security.DebitoorPermissions, SmartStore.Debitoor" }, + { "AccessImportBiz", "SmartStore.BizImporter.Security.BizImporterPermissions, SmartStore.BizImporter" }, + { "ManageShopConnector", "SmartStore.ShopConnector.Security.ShopConnectorPermissions, SmartStore.ShopConnector" }, + { "ManageNewsImporter", "SmartStore.NewsImporter.Security.NewImporterPermissions, SmartStore.NewsImporter" }, + { "ManageWebApi", "SmartStore.WebApi.Security.WebApiPermissions, SmartStore.WebApi" }, + { "ManageMegaSearch", "SmartStore.MegaSearch.Security.MegaSearchPermissions, SmartStore.MegaSearch" }, + { "ManageErpConnector", "Srt.ErpConnector.Security.ErpConnectorPermissions, Srt.ErpConnector" }, + { "ManagePowerBi", "SmartStore.PowerBi.Security.PowerBiPermissions, SmartStore.PowerBi" }, + { "ManageWallet", "SmartStore.Wallet.Security.WalletPermissions, SmartStore.Wallet" }, + { "ManageStories", "SmartStore.PageBuilder.Services.PageBuilderPermissions, SmartStore.PageBuilder" }, + { "ManageDlm", "SmartStore.Dlm.Security.DlmPermissions, SmartStore.Dlm" } + }; + + // Get new plugin permission names. + var allPluginPermissionNames = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var kvp in pluginPermissionNames) + { + if (permissionToRoles.ContainsKey(kvp.Key)) + { + var assemblyName = Type.GetType(kvp.Value)?.AssemblyQualifiedName; + if (assemblyName.HasValue()) + { + var type = Type.GetType(assemblyName); + allPluginPermissionNames.AddRange(PermissionHelper.GetPermissions(type)); + } + else + { + $"Plugin permission type not found ({kvp.Value}).".Dump(); + } + } + else + { + // Ignore unknown permissions. Will be deleted later by another migration. + } + } + + newPermissions.Clear(); + newPermissions = InsertPermissions(scope, allPluginPermissionNames); + + // Add PermissionRoleMapping for old plugin permissions. + PermissionRecord pr; + if (newPermissions.TryGetValue("debitoor", out pr)) + { + Allow("ManageDebitoor", pr); + } + if (newPermissions.TryGetValue("bizimporter", out pr)) + { + Allow("AccessImportBiz", pr); + } + if (newPermissions.TryGetValue("shopconnector", out pr)) + { + Allow("ManageShopConnector", pr); + } + if (newPermissions.TryGetValue("newsimporter", out pr)) + { + Allow("ManageNewsImporter", pr); + } + if (newPermissions.TryGetValue("webapi", out pr)) + { + Allow("ManageWebApi", pr); + } + if (newPermissions.TryGetValue("megasearch", out pr)) + { + Allow("ManageMegaSearch", pr); + } + if (newPermissions.TryGetValue("erpconnector", out pr)) + { + Allow("ManageErpConnector", pr); + } + if (newPermissions.TryGetValue("powerbi", out pr)) + { + Allow("ManagePowerBi", pr); + } + if (newPermissions.TryGetValue("wallet", out pr)) + { + Allow("ManageWallet", pr); + } + if (newPermissions.TryGetValue("pagebuilder", out pr)) + { + Allow("ManageStories", pr); + } + if (newPermissions.TryGetValue("dlm", out pr)) + { + Allow("ManageDlm", pr); + } + + // Add PermissionRoleMapping for default plugin permissions. + if (newPermissions.TryGetValue("pagebuilder.displaystory", out pr)) + { + AllowForRole(forumModRoleId, pr); + AllowForRole(guestRoleId, pr); + AllowForRole(registeredRoleId, pr); + } + if (newPermissions.TryGetValue("contentslider.displayslider", out pr)) + { + AllowForRole(forumModRoleId, pr); + AllowForRole(guestRoleId, pr); + AllowForRole(registeredRoleId, pr); + } + + scope.Commit(); + + // Migrate permission names of menu items. + var menuItems = ctx.Set().Where(x => !string.IsNullOrEmpty(x.PermissionNames)).ToList(); + if (menuItems.Any()) + { + foreach (var item in menuItems) + { + var newNames = new HashSet(StringComparer.OrdinalIgnoreCase); + var names = item.PermissionNames.SplitSafe(","); + foreach (var name in names) + { + if (oldPermissions.ContainsKey(name)) + { + oldPermissions[name].Each(x => newNames.Add(x)); + } + } + item.PermissionNames = string.Join(",", newNames); + } + + scope.Commit(); + } + } + + void Allow(string oldSystemName, params PermissionRecord[] permissions) + { + var appliedRoleIds = permissionToRoles.ContainsKey(oldSystemName) + ? permissionToRoles[oldSystemName] + : Enumerable.Empty(); + + foreach (var permission in permissions) + { + Guard.NotZero(permission.Id, nameof(permission.Id)); + + foreach (var roleId in appliedRoleIds) + { + mappingSet.Add(new PermissionRoleMapping + { + Allow = true, + PermissionRecordId = permission.Id, + CustomerRoleId = roleId + }); + } + } + } + + void AllowForRole(int roleId, params PermissionRecord[] permissions) + { + foreach (var permission in permissions) + { + if (!mappingSet.Any(x => x.PermissionRecordId == permission.Id && x.CustomerRoleId == roleId)) + { + mappingSet.Add(new PermissionRoleMapping + { + Allow = true, + PermissionRecordId = permission.Id, + CustomerRoleId = roleId + }); + } + } + } + + Multimap GetOldPermissions() + { + var map = new Multimap(StringComparer.OrdinalIgnoreCase); + map.Add("AccessAdminPanel", Permissions.System.AccessBackend); + map.Add("AllowCustomerImpersonation", Permissions.Customer.Impersonate); + map.AddRange("ManageCatalog", new string[] { Permissions.Catalog.Self, Permissions.Cart.CheckoutAttribute.Self }); + map.Add("ManageCustomers", Permissions.Customer.Self); + map.Add("ManageCustomerRoles", Permissions.Customer.Role.Self); + map.Add("ManageOrders", Permissions.Order.Self); + map.Add("ManageGiftCards", Permissions.Order.GiftCard.Self); + map.Add("ManageReturnRequests", Permissions.Order.ReturnRequest.Self); + map.Add("ManageAffiliates", Permissions.Promotion.Affiliate.Self); + map.Add("ManageCampaigns", Permissions.Promotion.Campaign.Self); + map.Add("ManageDiscounts", Permissions.Promotion.Discount.Self); + map.Add("ManageNewsletterSubscribers", Permissions.Promotion.Newsletter.Self); + map.Add("ManagePolls", Permissions.Cms.Poll.Self); + map.Add("ManageNews", Permissions.Cms.News.Self); + map.Add("ManageBlog", Permissions.Cms.Blog.Self); + map.Add("ManageWidgets", Permissions.Cms.Widget.Self); + map.Add("ManageTopics", Permissions.Cms.Topic.Self); + map.Add("ManageMenus", Permissions.Cms.Menu.Self); + map.Add("ManageForums", Permissions.Cms.Forum.Self); + map.Add("ManageMessageTemplates", Permissions.Cms.MessageTemplate.Self); + map.Add("ManageCountries", Permissions.Configuration.Country.Self); + map.Add("ManageLanguages", Permissions.Configuration.Language.Self); + map.Add("ManageSettings", Permissions.Configuration.Setting.Self); + map.Add("ManagePaymentMethods", Permissions.Configuration.PaymentMethod.Self); + map.Add("ManageExternalAuthenticationMethods", Permissions.Configuration.Authentication.Self); + map.Add("ManageTaxSettings", Permissions.Configuration.Tax.Self); + map.Add("ManageShippingSettings", Permissions.Configuration.Shipping.Self); + map.Add("ManageCurrencies", Permissions.Configuration.Currency.Self); + map.Add("ManageDeliveryTimes", Permissions.Configuration.DeliveryTime.Self); + map.Add("ManageThemes", Permissions.Configuration.Theme.Self); + map.Add("ManageMeasures", Permissions.Configuration.Measure.Self); + map.Add("ManageActivityLog", Permissions.Configuration.ActivityLog.Self); + map.Add("ManageACL", Permissions.Configuration.Acl.Self); + map.Add("ManageEmailAccounts", Permissions.Configuration.EmailAccount.Self); + map.Add("ManageStores", Permissions.Configuration.Store.Self); + map.Add("ManagePlugins", Permissions.Configuration.Plugin.Self); + map.Add("ManageSystemLog", Permissions.System.Log.Self); + map.Add("ManageMessageQueue", Permissions.System.Message.Self); + map.Add("ManageMaintenance", Permissions.System.Maintenance.Self); + map.Add("UploadPictures", Permissions.Media.Self); + map.Add("ManageScheduleTasks", Permissions.System.ScheduleTask.Self); + map.Add("ManageExports", Permissions.Configuration.Export.Self); + map.Add("ManageImports", Permissions.Configuration.Import.Self); + map.Add("ManageUrlRecords", Permissions.System.UrlRecord.Self); + map.Add("DisplayPrices", Permissions.Catalog.DisplayPrice); + map.Add("EnableShoppingCart", Permissions.Cart.AccessShoppingCart); + map.Add("EnableWishlist", Permissions.Cart.AccessWishlist); + map.Add("PublicStoreAllowNavigation", Permissions.System.AccessShop); + + return map; + } + + Dictionary InsertPermissions(DbContextScope scope, IEnumerable systemNames) + { + var newPermissions = new Dictionary(); + var newPermissionSql = "Select * From [dbo].[PermissionRecord] Where [Name] = '' And [Category] = ''"; + + // Delete existing granular permissions to ensure correct order in tree. + // Maybe this step is not required anymore but doesn't hurt. + var existingPermissions = ctx.SqlQuery(newPermissionSql); + foreach (var permission in existingPermissions) + { + if (systemNames.Contains(permission.SystemName)) + { + ctx.Database.ExecuteSqlCommand("Delete From [dbo].[PermissionRecord] Where [Id] = {0}", permission.Id); + } + } + + // Insert new granular permissions. + // Name and category property cannot be null, exist in database, but not in the domain model -> use SQL. + foreach (var name in systemNames) + { + ctx.Database.ExecuteSqlCommand("Insert Into PermissionRecord (SystemName, Name, Category) Values({0}, {1}, {2})", + name, string.Empty, string.Empty); + } + + // Now load new permissions. + var permissions = ctx.SqlQuery(newPermissionSql); + foreach (var permission in permissions) + { + if (systemNames.Contains(permission.SystemName)) + { + // It's a new granular permission. + newPermissions[permission.SystemName] = permission; + } + } + + return newPermissions; + } + } + + public class OldPermissionRoleMapping + { + public int PermissionRecord_Id { get; set; } + public int CustomerRole_Id { get; set; } + } + + public static void AddRuleSets(IDbContext context) + { + var ctx = context as SmartObjectContext; + if (ctx == null) + { + throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); + } + + var defaultLang = ctx.Set().AsNoTracking().OrderBy(x => x.DisplayOrder).First(); + var isGerman = defaultLang.UniqueSeoCode.IsCaseInsensitiveEqual("de"); + var discountNameTemplate = isGerman ? "Regel für Rabatt \"{0}\"" : "Rule for discount \"{0}\""; + var shippingNameTemplate = isGerman ? "Regel für Versandart \"{0}\"" : "Rule for shipping method \"{0}\""; + var paymentNameTemplate = isGerman ? "Regel für Zahlart \"{0} ({1})\"" : "Rule for payment method \"{0} ({1})\""; + var utcNow = DateTime.UtcNow; + + var cartRules = ctx.Set() + .Include(x => x.Rules) + .Where(x => x.Scope == RuleScope.Cart && !x.IsSubGroup) + .ToList(); + + var existingRuleSets = cartRules + .Where(x => x.Name.HasValue()) + .ToDictionarySafe(x => x.Name, x => x, StringComparer.OrdinalIgnoreCase); + + using (var scope = new DbContextScope(ctx: context, validateOnSave: false, hooksEnabled: false, autoCommit: false)) + { + AddRuleSetsForDiscountRequirements(); + scope.Commit(); + + AddRuleSetsForShippingFilters(); + scope.Commit(); + + AddRuleSetsForPaymentFilters(); + scope.Commit(); + } + + void AddRuleSetsForDiscountRequirements() + { + var allRequirements = ctx.SqlQuery("select * from [dbo].[DiscountRequirement]") + .ToList() + .ToMultimap(x => x.DiscountId, x => x); + + if (!allRequirements.Any()) + { + return; + } + + var discountIds = allRequirements.Select(x => x.Key).ToList(); + var discounts = ctx.Set() + .Where(x => discountIds.Contains(x.Id)) + .ToList(); + + if (!discounts.Any()) + { + return; + } + + foreach (var discount in discounts) + { + var ruleSetName = discountNameTemplate.FormatInvariant(discount.Name.NullEmpty() ?? discount.Id.ToString()).Truncate(195, "…"); + + // Avoid adding rule sets multiple times. + if (!existingRuleSets.TryGetValue(ruleSetName, out var ruleSet)) + { + // All requirements must be fulfilled for a discount to be applied. + ruleSet = new RuleSetEntity + { + Name = ruleSetName, + IsActive = true, + Scope = RuleScope.Cart, + LogicalOperator = LogicalRuleOperator.And, + CreatedOnUtc = utcNow, + UpdatedOnUtc = utcNow + }; + + // Try to combine requirements to one single rule, if possible. + var requirements = allRequirements[discount.Id] + .ToMultimap(x => x.DiscountRequirementRuleSystemName.EmptyNull().ToLower(), x => x, StringComparer.OrdinalIgnoreCase); + + foreach (var name in requirements.Keys) + { + switch (name) + { + case "discountrequirement.billingcountryis": + foreach (var requirement in requirements[name].Where(x => x.BillingCountryId != 0)) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = "CartBillingCountry", + Operator = RuleOperator.In, + Value = requirement.BillingCountryId.ToString() + }); + } + break; + case "discountrequirement.shippingcountryis": + foreach (var requirement in requirements[name].Where(x => x.ShippingCountryId != 0)) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = "CartShippingCountry", + Operator = RuleOperator.In, + Value = requirement.ShippingCountryId.ToString() + }); + } + break; + case "discountrequirement.mustbeassignedtocustomerrole": + var roleIds = new HashSet(requirements[name] + .Select(x => x.RestrictedToCustomerRoleId ?? 0) + .Where(x => x != 0)); + + if (roleIds.Any()) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = "CustomerRole", + Operator = RuleOperator.Contains, + Value = string.Join(",", roleIds) + }); + } + break; + case "discountrequirement.hadspentamount": + foreach (var requirement in requirements[name].Where(x => x.SpentAmount != decimal.Zero)) + { + var limitToCurrentBasketSubTotal = GetExtraData(requirement, "LimitToCurrentBasketSubTotal"); + + ruleSet.Rules.Add(new RuleEntity + { + RuleType = limitToCurrentBasketSubTotal ? "CartSubtotal" : "CartSpentAmount", + Operator = RuleOperator.GreaterThanOrEqualTo, + Value = requirement.SpentAmount.ToString(CultureInfo.InvariantCulture) + }); + } + break; + case "discountrequirement.hasallproducts": + case "discountrequirement.hasoneproduct": + case "discountrequirement.purchasedallproducts": + case "discountrequirement.purchasedoneproduct": + var productIds = new HashSet(); + var failedRequirements = new List(); + + foreach (var requirement in requirements[name].Where(x => x.RestrictedProductIds.HasValue())) + { + try + { + productIds.AddRange(requirement.RestrictedProductIds.ToIntArray()); + } + catch + { + failedRequirements.Add(requirement); + } + } + + if (!failedRequirements.Any()) + { + if (productIds.Any()) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = name == "discountrequirement.hasallproducts" || name == "discountrequirement.hasoneproduct" ? "ProductInCart" : "CartPurchasedProduct", + Operator = name == "discountrequirement.hasallproducts" || name == "discountrequirement.purchasedallproducts" ? RuleOperator.Contains : RuleOperator.In, + Value = string.Join(",", productIds) + }); + } + } + else + { + var sb = PooledStringBuilder.Rent(); + sb.AppendLine($"Cannnot create a rule for '{name}'."); + sb.AppendLine($"Discount name: {discount.Name.NaIfEmpty()}"); + sb.AppendLine($"Discount ID: {discount.Id}"); + sb.AppendLine(); + sb.AppendLine("Unsupported expressions for restricted product IDs:"); + failedRequirements.Each(x => sb.AppendLine(x.RestrictedProductIds)); + + LogWarning($"Cannot migrate requirements for discount '{discount.Name.NaIfEmpty()}'.", sb.ToStringAndReturn()); + } + break; + case "discountrequirement.store": + foreach (var requirement in requirements[name].Where(x => x.RestrictedToStoreId != 0)) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = "Store", + Operator = RuleOperator.In, + Value = requirement.RestrictedToStoreId.ToString() + }); + } + break; + case "discountrequirement.haspaymentmethod": + var paymentMethods = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var requirement in requirements[name].Where(x => x.RestrictedPaymentMethods.HasValue())) + { + paymentMethods.AddRange(requirement.RestrictedPaymentMethods.SplitSafe(",")); + } + + if (paymentMethods.Any()) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = "CartPaymentMethod", + Operator = RuleOperator.In, + Value = string.Join(",", paymentMethods) + }); + } + break; + case "discountrequirement.hasshippingoption": + var shippingMethodIds = new HashSet(); + foreach (var requirement in requirements[name].Where(x => x.RestrictedShippingOptions.HasValue())) + { + shippingMethodIds.AddRange(requirement.RestrictedShippingOptions.ToIntArray()); + } + + if (shippingMethodIds.Any()) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = "CartShippingMethod", + Operator = RuleOperator.In, + Value = string.Join(",", shippingMethodIds) + }); + } + break; + default: + LogWarning($"Cannot add rule set for unknown discount requirement type ({name}).", null); + break; + } + } + } + + if (ruleSet.Rules.Any()) + { + // Map rule set to discount. + discount.RuleSets.Add(ruleSet); + } + } + } + + void AddRuleSetsForShippingFilters() + { + var syncMappings = ctx.Set() + .AsNoTracking() + .Where(x => x.ContextName == "SmartStore.ShippingFilter" && x.EntityName == "ShippingMethod") + .ToList() + .ToDictionarySafe(x => x.EntityId, x => x); + + if (!syncMappings.Any()) + { + return; + } + + var shippingMethodIds = syncMappings.Select(x => x.Key).ToList(); + var shippingMethods = ctx.Set() + .Where(x => shippingMethodIds.Contains(x.Id)) + .ToList(); + + if (!shippingMethods.Any()) + { + return; + } + + foreach (var shippingMethod in shippingMethods) + { + var ruleSetName = shippingNameTemplate.FormatInvariant(shippingMethod.Name.NullEmpty() ?? shippingMethod.Id.ToString()).Truncate(195, "…"); + + // Avoid adding rule sets multiple times. + if (!existingRuleSets.TryGetValue(ruleSetName, out var ruleSet)) + { + ruleSet = new RuleSetEntity + { + Name = ruleSetName, + IsActive = true, + Scope = RuleScope.Cart, + LogicalOperator = LogicalRuleOperator.And, + CreatedOnUtc = utcNow, + UpdatedOnUtc = utcNow + }; + + var model = GetFilterData(syncMappings, shippingMethod.Id, "FilterOutShippingMethodModel"); + if (model != null) + { + AddRulesForCommonFilters(ctx, ruleSet, model, "SmartStore.ShippingFilter"); + } + } + + if (ruleSet.Rules.Any()) + { + // Map rule set to shipping method. + shippingMethod.RuleSets.Add(ruleSet); + } + } + } + + void AddRuleSetsForPaymentFilters() + { + var syncMappings = ctx.Set() + .AsNoTracking() + .Where(x => x.ContextName == "SmartStore.PaymentFilter" && x.EntityName == "PaymentMethod") + .ToList() + .ToDictionarySafe(x => x.EntityId, x => x); + + if (!syncMappings.Any()) + { + return; + } + + var paymentMethodIds = syncMappings.Select(x => x.Key).ToList(); + var paymentMethods = ctx.Set() + .Where(x => paymentMethodIds.Contains(x.Id)) + .ToList(); + + if (!paymentMethods.Any()) + { + return; + } + + var stringRessourceSet = ctx.Set().AsNoTracking(); + + foreach (var paymentMethod in paymentMethods) + { + var resourceName = $"Plugins.FriendlyName.{paymentMethod.PaymentMethodSystemName}"; + var friendlyName = stringRessourceSet + .Where(x => x.ResourceName == resourceName && x.LanguageId == defaultLang.Id) + .Select(x => x.ResourceValue) + .FirstOrDefault() + .NullEmpty(); + + var ruleSetName = paymentNameTemplate.FormatInvariant(friendlyName ?? "?", paymentMethod.PaymentMethodSystemName ?? paymentMethod.Id.ToString()).Truncate(195, "…"); + + // Avoid adding rule sets multiple times. + if (!existingRuleSets.TryGetValue(ruleSetName, out var ruleSet)) + { + ruleSet = new RuleSetEntity + { + Name = ruleSetName, + IsActive = true, + Scope = RuleScope.Cart, + LogicalOperator = LogicalRuleOperator.And, + CreatedOnUtc = utcNow, + UpdatedOnUtc = utcNow + }; + + var model = GetFilterData(syncMappings, paymentMethod.Id, "FilterOutPaymentMethodModel"); + if (model != null) + { + AddRulesForCommonFilters(ctx, ruleSet, model, "SmartStore.PaymentFilter"); + + // Order number. + if (model.HasLessThanOrders.HasValue) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = "CartOrderCount", + Operator = RuleOperator.GreaterThanOrEqualTo, + Value = model.HasLessThanOrders.Value.ToString() + }); + } + + // Spent amount. + if (model.HasLessThanTotalAmount.HasValue) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = "CartSpentAmount", + Operator = RuleOperator.GreaterThanOrEqualTo, + Value = model.HasLessThanTotalAmount.Value.ToString(CultureInfo.InvariantCulture) + }); + } + + // Shipping method. + if (model.ExcludedShippingMethods?.Any() ?? false) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = "CartShippingMethod", + Operator = RuleOperator.NotIn, + Value = string.Join(",", model.ExcludedShippingMethods) + }); + } + + // Minimum cart amount. + if (model.MinimumOrderAmount.HasValue) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = model.AmountContext.IsCaseInsensitiveEqual("TotalAmount") ? "CartTotal" : "CartSubtotal", + Operator = RuleOperator.GreaterThanOrEqualTo, + Value = model.MinimumOrderAmount.Value.ToString(CultureInfo.InvariantCulture) + }); + } + + // Maximum cart amount. + if (model.MaximumOrderAmount.HasValue) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = model.AmountContext.IsCaseInsensitiveEqual("TotalAmount") ? "CartTotal" : "CartSubtotal", + Operator = RuleOperator.LessThanOrEqualTo, + Value = model.MaximumOrderAmount.Value.ToString(CultureInfo.InvariantCulture) + }); + } + } + } + + if (ruleSet.Rules.Any()) + { + // Map rule set to payment method. + paymentMethod.RuleSets.Add(ruleSet); + } + } + } + + T GetExtraData(OldDiscountRequirement req, string name) + { + try + { + if (req?.ExtraData?.HasValue() ?? false) + { + var extraData = JsonConvert.DeserializeObject>(req.ExtraData); + if (extraData.TryGetValue(name, out var obj)) + { + return obj.Convert(CultureInfo.InvariantCulture); + } + } + } + catch (Exception ex) + { + ex.Dump(); + } + + return default(T); + } + + PluginFilterConfigModel GetFilterData(Dictionary syncMappings, int entityId, string rootNodeName) + { + try + { + if (syncMappings.TryGetValue(entityId, out var syncMapping) && syncMapping.CustomString.HasValue()) + { + using (var reader = new StringReader(syncMapping.CustomString)) + { + var serializer = new XmlSerializer(typeof(PluginFilterConfigModel), new XmlRootAttribute(rootNodeName)); + return serializer.Deserialize(reader) as PluginFilterConfigModel; + } + } + } + catch (Exception ex) + { + ex.Dump(); + } + + return null; + } + + void AddRulesForCommonFilters(SmartObjectContext _ctx, RuleSetEntity ruleSet, PluginFilterConfigModel model, string pluginSystemName) + { + // Store id (if plugin is limited to stores). + var settingName = $"PluginSetting.{pluginSystemName}.LimitedToStores"; + var limitedToStoresValue = _ctx.Set() + .Where(x => x.Name == settingName) + .Select(x => x.Value) + .FirstOrDefault(); + + if (limitedToStoresValue.HasValue()) + { + var storeIds = limitedToStoresValue.ToIntArray(); + if (storeIds.Any()) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = "Store", + Operator = RuleOperator.In, + Value = string.Join(",", storeIds) + }); + } + } + + // Customer role. + if (model?.ExcludedCustomerRoles?.Any() ?? false) + { + var excludedRoleIds = model.ExcludedCustomerRoles.Where(x => x != 0).ToArray(); + if (excludedRoleIds.Any()) + { + if (model.FilterIfRoleIsAssigned) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = "CustomerRole", + Operator = RuleOperator.NotContains, + Value = string.Join(",", excludedRoleIds) + }); + } + else + { + // There's no operator for this filter. We have to turn him around logically. + var allRoleIds = _ctx.Set().Select(x => x.Id).ToList(); + var includedRoleIds = allRoleIds.Except(excludedRoleIds).ToArray(); + + if (includedRoleIds.Any()) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = "CustomerRole", + Operator = RuleOperator.In, + Value = string.Join(",", includedRoleIds) + }); + } + } + } + } + + // Address country. + if (model.ExcludedCountries?.Any() ?? false) + { + var excludedCountryIds = model.ExcludedCountries.Where(x => x != 0).ToArray(); + if (excludedCountryIds.Any()) + { + ruleSet.Rules.Add(new RuleEntity + { + RuleType = model.CountryContext.IsCaseInsensitiveEqual("BillingAddress") ? "CartBillingCountry" : "CartShippingCountry", + Operator = RuleOperator.NotIn, + Value = string.Join(",", excludedCountryIds) + }); + } + } + } + + /*static*/ + static void LogWarning(string message, string fullDescription) + { + try + { + var logger = EngineContext.Current.Resolve().GetLogger(typeof(DataMigrator)); + if (logger != null) + { + logger.Warn(fullDescription.HasValue() ? new SmartException(fullDescription) : null, message); + } + } + catch { } + } + } + + public class OldDiscountRequirement + { + public int Id { get; set; } + public int DiscountId { get; set; } + public string DiscountRequirementRuleSystemName { get; set; } + public decimal SpentAmount { get; set; } + public int BillingCountryId { get; set; } + public int ShippingCountryId { get; set; } + public int? RestrictedToCustomerRoleId { get; set; } + public string RestrictedProductIds { get; set; } + public string RestrictedPaymentMethods { get; set; } + public string RestrictedShippingOptions { get; set; } + public int? RestrictedToStoreId { get; set; } + public string ExtraData { get; set; } + } + + [Serializable] + public class PluginFilterConfigModel + { + // Values for payment and shipping filter. + public int[] ExcludedCustomerRoles { get; set; } + public bool FilterIfRoleIsAssigned { get; set; } + public int[] ExcludedCountries { get; set; } + public string CountryContext { get; set; } + + // Values for payment filter. + public int[] ExcludedShippingMethods { get; set; } + public decimal? MinimumOrderAmount { get; set; } + public decimal? MaximumOrderAmount { get; set; } + public string AmountContext { get; set; } + public int? HasLessThanOrders { get; set; } + public decimal? HasLessThanTotalAmount { get; set; } + } + + #endregion } } diff --git a/src/Libraries/SmartStore.Data/Utilities/FastPager.cs b/src/Libraries/SmartStore.Data/Utilities/FastPager.cs new file mode 100644 index 0000000000..568157d7c4 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Utilities/FastPager.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Data.Entity; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; +using SmartStore.Core; + +namespace SmartStore.Data.Utilities +{ + /// + /// Ensures stable and consistent paging performance over very large datasets. + /// Other than LINQs Skip(x).Take(y) approach the entity set is sorted + /// descending by id and a specified amount of records are returned. + /// The FastPager remembers the last (lowest) returned id and uses + /// it for the next batches' WHERE clause. This way Skip() can be avoided which + /// is known for performing really bad on large tables. + /// + public sealed class FastPager where T : BaseEntity, new() + { + private readonly IQueryable _query; + private readonly int _pageSize; + + private int? _maxId; + private int? _currentPage; + + public FastPager(IQueryable query, int pageSize = 1000) + { + Guard.NotNull(query, nameof(query)); + Guard.IsPositive(pageSize, nameof(pageSize)); + + _query = query; + _pageSize = pageSize; + } + + public void Reset() + { + _maxId = null; + _currentPage = null; + } + + public int? MaxId => _maxId; + + public int? CurrentPage => _currentPage; + + public bool ReadNextPage(out IList page) + { + return ReadNextPage(x => x, x => x.Id, out page); + } + + public bool ReadNextPage( + Expression> shaper, + Func idSelector, + out IList page) + { + Guard.NotNull(shaper, nameof(shaper)); + + page = null; + + if (_maxId == null) + { + _maxId = int.MaxValue; + _currentPage = 0; + } + if (_maxId.Value <= 1) + { + return false; + } + + page = _query + .Where(x => x.Id < _maxId.Value) + .OrderByDescending(x => x.Id) + .Take(() => _pageSize) + .Select(shaper) + .ToList(); + + if (page.Count == 0) + { + _maxId = -1; + page = null; + return false; + } + + _currentPage++; + _maxId = idSelector(page.Last()); + return true; + } + + public async Task> ReadNextPageAsync() + { + return await ReadNextPageAsync(x => x, x => x.Id); + } + + public async Task> ReadNextPageAsync( + Expression> shaper, + Func idSelector) + { + Guard.NotNull(shaper, nameof(shaper)); + + if (_maxId == null) + { + _maxId = int.MaxValue; + _currentPage = 0; + } + if (_maxId.Value <= 1) + { + return null; + } + + var page = await _query + .Where(x => x.Id < _maxId.Value) + .OrderByDescending(x => x.Id) + .Take(() => _pageSize) + .Select(shaper) + .ToListAsync(); + + if (page.Count == 0) + { + _maxId = -1; + page = null; + return null; + } + + _currentPage++; + _maxId = idSelector(page.Last()); + return page; + } + } +} diff --git a/src/Libraries/SmartStore.Data/Utilities/MessageTemplateConverter.cs b/src/Libraries/SmartStore.Data/Utilities/MessageTemplateConverter.cs index 310aa4a220..1ad0d218b5 100644 --- a/src/Libraries/SmartStore.Data/Utilities/MessageTemplateConverter.cs +++ b/src/Libraries/SmartStore.Data/Utilities/MessageTemplateConverter.cs @@ -1,219 +1,219 @@ using System; using System.Collections.Generic; -using System.Linq; using System.IO; +using System.Linq; +using System.Xml; +using System.Xml.Linq; using SmartStore.Core.Data; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Messages; using SmartStore.Utilities; -using System.Xml; -using System.Xml.Linq; namespace SmartStore.Data.Utilities { - public sealed class MessageTemplateConverter - { - private readonly SmartObjectContext _ctx; - private readonly EmailAccount _defaultEmailAccount; - - public MessageTemplateConverter(IDbContext context) - { - _ctx = context as SmartObjectContext; - if (_ctx == null) - throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); - - _defaultEmailAccount = _ctx.Set().FirstOrDefault(x => x.Email != null); - } - - /// - /// Loads a single message template from file (~/App_Data/EmailTemplates/) - /// and deserializes its xml content. - /// - /// Name of template without extension, e.g. 'GiftCard.Notification' - /// Language - /// The virtual root path of templates to load, e.g. "~/Plugins/MyPlugins/EmailTemplates". Default is "~/App_Data/EmailTemplates". - /// Deserialized template xml - public MessageTemplate Load(string templateName, Language language, string virtualRootPath = null) - { - Guard.NotEmpty(templateName, nameof(templateName)); - Guard.NotNull(language, nameof(language)); - - var dir = ResolveTemplateDirectory(language, virtualRootPath); - var fullPath = Path.Combine(dir.FullName, templateName + ".xml"); - - if (!File.Exists(fullPath)) - { - throw new FileNotFoundException($"File '{fullPath}' does not exist."); - } - - return DeserializeTemplate(fullPath); - } - - /// - /// Loads all message templates from disk (~/App_Data/EmailTemplates/) - /// - /// Language - /// The virtual root path of templates to load, e.g. "~/Plugins/MyPlugins/EmailTemplates". Default is "~/App_Data/EmailTemplates". - /// List of deserialized template xml - public IEnumerable LoadAll(Language language, string virtualRootPath = null) - { - Guard.NotNull(language, nameof(language)); - - var dir = ResolveTemplateDirectory(language, virtualRootPath); - var files = dir.EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly); - - foreach (var file in files) - { - var template = DeserializeTemplate(file.FullName); - template.Name = Path.GetFileNameWithoutExtension(file.Name); - yield return template; - } - } - - public MessageTemplate Deserialize(string xml, string templateName) - { - Guard.NotEmpty(xml, nameof(xml)); - Guard.NotEmpty(templateName, nameof(templateName)); - - var template = DeserializeDocument(XDocument.Parse(xml)); - template.Name = templateName; - return template; - } - - public XmlDocument Save(MessageTemplate template, Language language) - { - Guard.NotNull(template, nameof(template)); - Guard.NotNull(language, nameof(language)); - - var doc = new XmlDocument(); - doc.LoadXml(""); - - var root = doc.DocumentElement; - root.AppendChild(doc.CreateElement("To")).InnerText = template.To; - if (template.ReplyTo.HasValue()) - root.AppendChild(doc.CreateElement("ReplyTo")).InnerText = template.ReplyTo; - root.AppendChild(doc.CreateElement("Subject")).InnerText = template.Subject; - root.AppendChild(doc.CreateElement("ModelTypes")).InnerText = template.ModelTypes; - root.AppendChild(doc.CreateElement("Body")).AppendChild(doc.CreateCDataSection(template.Body)); - - var path = Path.Combine(CommonHelper.MapPath("~/App_Data/EmailTemplates"), language.GetTwoLetterISOLanguageName()); - if (!Directory.Exists(path)) - { - Directory.CreateDirectory(path); - } - - // File path - path = Path.Combine(path, template.Name + ".xml"); - - var xml = Prettifier.PrettifyXML(doc.OuterXml); - File.WriteAllText(path, xml); - - return doc; - } - - /// - /// Imports all template xml files to MessageTemplate table - /// - /// The virtual root path of templates to import, e.g. "~/Plugins/MyPlugins/EmailTemplates". Default is "~/App_Data/EmailTemplates". - public void ImportAll(Language language, string virtualRootPath = null) - { - var table = _ctx.Set(); - - var sourceTemplates = LoadAll(language, virtualRootPath); - var dbTemplatesMap = table - .ToList() - .ToMultimap(x => x.Name, x => x, StringComparer.OrdinalIgnoreCase); - - foreach (var source in sourceTemplates) - { - if (dbTemplatesMap.ContainsKey(source.Name)) - { - foreach (var target in dbTemplatesMap[source.Name]) - { - if (source.To.HasValue()) target.To = source.To; - if (source.ReplyTo.HasValue()) target.ReplyTo = source.ReplyTo; - if (source.Subject.HasValue()) target.Subject = source.Subject; - if (source.ModelTypes.HasValue()) target.ModelTypes = source.ModelTypes; - if (source.Body.HasValue()) target.Body = source.Body; - } - } - else - { - var template = new MessageTemplate - { - Name = source.Name, - To = source.To, - ReplyTo = source.ReplyTo, - Subject = source.Subject, - ModelTypes = source.ModelTypes, - Body = source.Body, - IsActive = true, - EmailAccountId = (_defaultEmailAccount?.Id).GetValueOrDefault(), - }; - - table.Add(template); - } - } - - _ctx.SaveChanges(); - } - - private DirectoryInfo ResolveTemplateDirectory(Language language, string virtualRootPath = null) - { - var rootPath = CommonHelper.MapPath(virtualRootPath.NullEmpty() ?? "~/App_Data/EmailTemplates/"); - var testPaths = new[] - { - language.LanguageCulture, - language.GetTwoLetterISOLanguageName(), - "en" - }; - - foreach (var path in testPaths.Select(x => Path.Combine(rootPath, x))) - { - if (Directory.Exists(path)) - { - return new DirectoryInfo(path); - } - } - - throw new DirectoryNotFoundException($"Could not obtain an email templates path for language {language.LanguageCulture}. Fallback to 'en' failed, because directory does not exist."); - } - - private MessageTemplate DeserializeTemplate(string fullPath) - { - return DeserializeDocument(XDocument.Load(fullPath)); - } - - private MessageTemplate DeserializeDocument(XDocument doc) - { - var root = doc.Root; - var result = new MessageTemplate(); - - foreach (var node in root.Nodes().OfType()) - { - var value = node.Value.Trim(); - - switch (node.Name.LocalName) - { - case "To": - result.To = value; - break; - case "ReplyTo": - result.ReplyTo = value; - break; - case "Subject": - result.Subject = value; - break; - case "ModelTypes": - result.ModelTypes = value; - break; - case "Body": - result.Body = value; - break; - } - } - - return result; - } - } + public sealed class MessageTemplateConverter + { + private readonly SmartObjectContext _ctx; + private readonly EmailAccount _defaultEmailAccount; + + public MessageTemplateConverter(IDbContext context) + { + _ctx = context as SmartObjectContext; + if (_ctx == null) + throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); + + _defaultEmailAccount = _ctx.Set().FirstOrDefault(x => x.Email != null); + } + + /// + /// Loads a single message template from file (~/App_Data/EmailTemplates/) + /// and deserializes its xml content. + /// + /// Name of template without extension, e.g. 'GiftCard.Notification' + /// Language + /// The virtual root path of templates to load, e.g. "~/Plugins/MyPlugins/EmailTemplates". Default is "~/App_Data/EmailTemplates". + /// Deserialized template xml + public MessageTemplate Load(string templateName, Language language, string virtualRootPath = null) + { + Guard.NotEmpty(templateName, nameof(templateName)); + Guard.NotNull(language, nameof(language)); + + var dir = ResolveTemplateDirectory(language, virtualRootPath); + var fullPath = Path.Combine(dir.FullName, templateName + ".xml"); + + if (!File.Exists(fullPath)) + { + throw new FileNotFoundException($"File '{fullPath}' does not exist."); + } + + return DeserializeTemplate(fullPath); + } + + /// + /// Loads all message templates from disk (~/App_Data/EmailTemplates/) + /// + /// Language + /// The virtual root path of templates to load, e.g. "~/Plugins/MyPlugins/EmailTemplates". Default is "~/App_Data/EmailTemplates". + /// List of deserialized template xml + public IEnumerable LoadAll(Language language, string virtualRootPath = null) + { + Guard.NotNull(language, nameof(language)); + + var dir = ResolveTemplateDirectory(language, virtualRootPath); + var files = dir.EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly); + + foreach (var file in files) + { + var template = DeserializeTemplate(file.FullName); + template.Name = Path.GetFileNameWithoutExtension(file.Name); + yield return template; + } + } + + public MessageTemplate Deserialize(string xml, string templateName) + { + Guard.NotEmpty(xml, nameof(xml)); + Guard.NotEmpty(templateName, nameof(templateName)); + + var template = DeserializeDocument(XDocument.Parse(xml)); + template.Name = templateName; + return template; + } + + public XmlDocument Save(MessageTemplate template, Language language) + { + Guard.NotNull(template, nameof(template)); + Guard.NotNull(language, nameof(language)); + + var doc = new XmlDocument(); + doc.LoadXml(""); + + var root = doc.DocumentElement; + root.AppendChild(doc.CreateElement("To")).InnerText = template.To; + if (template.ReplyTo.HasValue()) + root.AppendChild(doc.CreateElement("ReplyTo")).InnerText = template.ReplyTo; + root.AppendChild(doc.CreateElement("Subject")).InnerText = template.Subject; + root.AppendChild(doc.CreateElement("ModelTypes")).InnerText = template.ModelTypes; + root.AppendChild(doc.CreateElement("Body")).AppendChild(doc.CreateCDataSection(template.Body)); + + var path = Path.Combine(CommonHelper.MapPath("~/App_Data/EmailTemplates"), language.GetTwoLetterISOLanguageName()); + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + // File path + path = Path.Combine(path, template.Name + ".xml"); + + var xml = Prettifier.PrettifyXML(doc.OuterXml); + File.WriteAllText(path, xml); + + return doc; + } + + /// + /// Imports all template xml files to MessageTemplate table + /// + /// The virtual root path of templates to import, e.g. "~/Plugins/MyPlugins/EmailTemplates". Default is "~/App_Data/EmailTemplates". + public void ImportAll(Language language, string virtualRootPath = null) + { + var table = _ctx.Set(); + + var sourceTemplates = LoadAll(language, virtualRootPath); + var dbTemplatesMap = table + .ToList() + .ToMultimap(x => x.Name, x => x, StringComparer.OrdinalIgnoreCase); + + foreach (var source in sourceTemplates) + { + if (dbTemplatesMap.ContainsKey(source.Name)) + { + foreach (var target in dbTemplatesMap[source.Name]) + { + if (source.To.HasValue()) target.To = source.To; + if (source.ReplyTo.HasValue()) target.ReplyTo = source.ReplyTo; + if (source.Subject.HasValue()) target.Subject = source.Subject; + if (source.ModelTypes.HasValue()) target.ModelTypes = source.ModelTypes; + if (source.Body.HasValue()) target.Body = source.Body; + } + } + else + { + var template = new MessageTemplate + { + Name = source.Name, + To = source.To, + ReplyTo = source.ReplyTo, + Subject = source.Subject, + ModelTypes = source.ModelTypes, + Body = source.Body, + IsActive = true, + EmailAccountId = (_defaultEmailAccount?.Id).GetValueOrDefault(), + }; + + table.Add(template); + } + } + + _ctx.SaveChanges(); + } + + private DirectoryInfo ResolveTemplateDirectory(Language language, string virtualRootPath = null) + { + var rootPath = CommonHelper.MapPath(virtualRootPath.NullEmpty() ?? "~/App_Data/EmailTemplates/"); + var testPaths = new[] + { + language.LanguageCulture, + language.GetTwoLetterISOLanguageName(), + "en" + }; + + foreach (var path in testPaths.Select(x => Path.Combine(rootPath, x))) + { + if (Directory.Exists(path)) + { + return new DirectoryInfo(path); + } + } + + throw new DirectoryNotFoundException($"Could not obtain an email templates path for language {language.LanguageCulture}. Fallback to 'en' failed, because directory does not exist."); + } + + private MessageTemplate DeserializeTemplate(string fullPath) + { + return DeserializeDocument(XDocument.Load(fullPath)); + } + + private MessageTemplate DeserializeDocument(XDocument doc) + { + var root = doc.Root; + var result = new MessageTemplate(); + + foreach (var node in root.Nodes().OfType()) + { + var value = node.Value.Trim(); + + switch (node.Name.LocalName) + { + case "To": + result.To = value; + break; + case "ReplyTo": + result.ReplyTo = value; + break; + case "Subject": + result.Subject = value; + break; + case "ModelTypes": + result.ModelTypes = value; + break; + case "Body": + result.Body = value; + break; + } + } + + return result; + } + } } diff --git a/src/Libraries/SmartStore.Data/Utilities/NewsItemConverter.cs b/src/Libraries/SmartStore.Data/Utilities/NewsItemConverter.cs new file mode 100644 index 0000000000..b9552f88a0 --- /dev/null +++ b/src/Libraries/SmartStore.Data/Utilities/NewsItemConverter.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Localization; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.News; +using SmartStore.Core.IO; +using SmartStore.Utilities; + +namespace SmartStore.Data.Utilities +{ + public sealed class NewsItemConverter + { + private readonly SmartObjectContext _ctx; + + public NewsItemConverter(IDbContext context) + { + _ctx = context as SmartObjectContext; + if (_ctx == null) + throw new ArgumentException("Passed context must be an instance of type '{0}'.".FormatInvariant(typeof(SmartObjectContext)), nameof(context)); + } + + /// + /// Loads all news item from disk (~/App_Data/Samples/news/) + /// + /// Language + /// The virtual root path of news to load, e.g. "~/Plugins/MyPlugins/NewsItem". Default is "~/App_Data/Samples/news". + /// List of deserialized news items xml + public IEnumerable LoadAll(Language language, string virtualRootPath = null) + { + Guard.NotNull(language, nameof(language)); + + var dir = ResolveNewsDirectory(language, virtualRootPath); + var files = dir.EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly); + + foreach (var file in files) + { + yield return DeserializeNews(file.FullName); + } + } + + /// + /// Imports all news xml files to NewsItem table + /// + /// The virtual root path of blogs to import, e.g. "~/Plugins/MyPlugins/NewsItem". Default is "~/App_Data/Samples/news". + /// List of new imported news items + public IList ImportAll(Language language, string virtualRootPath = null) + { + var newsImported = new List(); + var table = _ctx.Set(); + var sourceBlogs = LoadAll(language); + var dbNewsMap = table.ToList().ToMultimap(x => x.Title, x => x, StringComparer.OrdinalIgnoreCase); + + foreach (var source in sourceBlogs) + { + if (dbNewsMap.ContainsKey(source.Title)) + { + foreach (var target in dbNewsMap[source.Title]) + { + if (source.Title.HasValue()) target.Title = source.Title; + if (source.MetaTitle.HasValue()) target.MetaTitle = source.MetaTitle; + if (source.MetaDescription.HasValue()) target.MetaDescription = source.MetaDescription; + if (source.Short.HasValue()) target.Short = source.Short; + if (source.Full.HasValue()) target.Full = source.Full; + if (source.CreatedOnUtc != null) target.CreatedOnUtc = source.CreatedOnUtc; + if (source.MediaFile != null) target.MediaFile = source.MediaFile; + if (source.PreviewMediaFile != null) target.PreviewMediaFile = source.PreviewMediaFile; + target.AllowComments = source.AllowComments; + } + } + else + { + var news = new NewsItem + { + Title = source.Title, + MetaTitle = source.MetaTitle, + MetaDescription = source.MetaDescription, + Short = source.Short, + Full = source.Full, + CreatedOnUtc = source.CreatedOnUtc, + MediaFile = source.MediaFile, + PreviewMediaFile = source.PreviewMediaFile, + AllowComments = source.AllowComments, + Published = true + }; + + newsImported.Add(news); + table.Add(news); + } + } + + _ctx.SaveChanges(); + return newsImported; + } + + private DirectoryInfo ResolveNewsDirectory(Language language, string virtualRootPath = null) + { + var testPaths = new[] + { + language.LanguageCulture, + language.GetTwoLetterISOLanguageName(), + "en" + }; + + var rootPath = CommonHelper.MapPath(virtualRootPath.NullEmpty() ?? "~/App_Data/Samples/news/"); + foreach (var path in testPaths.Select(x => Path.Combine(rootPath, x))) + { + if (Directory.Exists(path)) + { + return new DirectoryInfo(path); + } + } + + throw new DirectoryNotFoundException($"Could not obtain a news item path for language {language.LanguageCulture}. Fallback to 'en' failed, because directory does not exist."); + } + + private NewsItem DeserializeNews(string fullPath) + { + return DeserializeDocument(XDocument.Load(fullPath)); + } + + private NewsItem DeserializeDocument(XDocument doc) + { + var result = new NewsItem(); + var nodes = doc.Root.Nodes().OfType(); + + foreach (var node in nodes) + { + var value = node.Value.Trim(); + + switch (node.Name.LocalName) + { + case "Title": + result.Title = value; + break; + case "MetaTitle": + result.MetaTitle = value; + break; + case "MetaDescription": + result.MetaDescription = value; + break; + case "Short": + result.Short = value; + break; + case "Full": + result.Full = value; + break; + case "CreatedOn": + result.CreatedOnUtc = value.ToDateTime(new DateTime()).Value; + break; + case "Image": + var seName = GetSeName(Path.GetFileNameWithoutExtension(value)); + result.MediaFile = CreateImage(value, seName); + break; + case "ImagePreview": + seName = GetSeName(Path.GetFileNameWithoutExtension(value)); + var previewImage = CreateImage(value, seName); + result.PreviewMediaFile = previewImage.Name == result.MediaFile.Name + ? result.MediaFile + : previewImage; + break; + case "Comments": + result.AllowComments = value.ToBool(); + break; + } + } + + return result; + } + + private MediaFile CreateImage(string fileName, string seoFilename = null) + { + try + { + var ext = Path.GetExtension(fileName); + var mimeType = MimeTypes.MapNameToMimeType(ext); + var path = Path.Combine(CommonHelper.MapPath("~/App_Data/Samples/news/"), fileName).Replace('/', '\\'); + var buffer = File.ReadAllBytes(path); + var now = DateTime.UtcNow; + + var name = seoFilename.HasValue() + ? seoFilename.Truncate(100) + ext + : Path.GetFileName(fileName).ToLower().Replace('_', '-'); + + var file = new MediaFile + { + Name = name, + MediaType = "image", + MimeType = mimeType, + Extension = ext.EmptyNull().TrimStart('.'), + CreatedOnUtc = now, + UpdatedOnUtc = now, + Size = buffer.Length, + MediaStorage = new MediaStorage { Data = buffer }, + Version = 1 // so that FolderId is set later during track detection + }; + + return file; + } + catch (Exception ex) + { + // Throw ex; + System.Diagnostics.Debug.WriteLine(ex.Message); + return null; + } + } + + private string GetSeName(string name) + { + return SeoHelper.GetSeName(name, true, false); + } + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Data/Utilities/SqlBlobStream.cs b/src/Libraries/SmartStore.Data/Utilities/SqlBlobStream.cs new file mode 100644 index 0000000000..9e346c08ab --- /dev/null +++ b/src/Libraries/SmartStore.Data/Utilities/SqlBlobStream.cs @@ -0,0 +1,196 @@ +using System; +using System.Data; +using System.Data.Common; +using System.Data.Entity.Infrastructure; +using System.IO; + +namespace SmartStore.Data.Utilities +{ + public class SqlBlobStream : Stream + { + private readonly IDbConnectionFactory _connectionFactory; + private DbConnection _connection; + private DbDataReader _reader; + + private long? _length; + private long _dataIndex = 0; + + public SqlBlobStream( + IDbConnectionFactory connectionFactory, + string connectionString, + string tableName, + string blobColumnName, + string pkColumnName, + object pkColumnValue) + { + Guard.NotNull(connectionFactory, nameof(connectionFactory)); + Guard.NotEmpty(connectionString, nameof(connectionString)); + Guard.NotEmpty(tableName, nameof(tableName)); + Guard.NotEmpty(blobColumnName, nameof(blobColumnName)); + Guard.NotEmpty(pkColumnName, nameof(pkColumnName)); + Guard.NotNull(pkColumnValue, nameof(pkColumnValue)); + + ConnectionString = connectionString; + TableName = tableName; + BlobColumnName = blobColumnName; + PkColumnName = pkColumnName; + PkColumnValue = pkColumnValue; + + _connectionFactory = connectionFactory; + } + + private void EnsureOpen() + { + if (_reader != null) + return; + + _connection = _connectionFactory.CreateConnection(ConnectionString); + _connection.Open(); + + using (var command = _connection.CreateCommand()) + { + PrimaryKey = command.CreateParameter(); + PrimaryKey.ParameterName = "@" + PkColumnName; + PrimaryKey.Value = PkColumnValue; + + command.Connection = _connection; + command.CommandType = CommandType.Text; + command.CommandText = $"SELECT [{BlobColumnName}] FROM [{TableName}] WHERE [{PrimaryKey.ParameterName.Substring(1)}] = {PrimaryKey.Value}"; + command.Parameters.Add(PrimaryKey); + + _reader = command.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.CloseConnection); + if (!_reader.Read()) + { + throw new DataException($"Blob [{TableName}].[{BlobColumnName}] with id '{PrimaryKey.Value}' does not exist."); + } + + if (_reader.IsDBNull(0)) + { + _length = 0; + } + } + } + + public string ConnectionString { get; private set; } + public string TableName { get; private set; } + public string BlobColumnName { get; private set; } + public string PkColumnName { get; private set; } + public object PkColumnValue { get; private set; } + public DbParameter PrimaryKey { get; private set; } + + #region Stream members + + public override long Seek(long offset, SeekOrigin origin) + { + if (origin != SeekOrigin.Begin) + { + throw new NotSupportedException($"{nameof(SqlBlobStream)} only supports 'SeekOrigin.Begin'."); + } + + if (offset == _dataIndex) + { + return 0; + } + + if (offset < _dataIndex) + { + // This is a forward-only read. Close and start over again. + CloseReader(); + return Seek(offset, origin); + } + + if (offset > _dataIndex) + { + EnsureOpen(); + + var buffer = new byte[1]; + var num = _reader.GetBytes(0, offset - 1, buffer, 0, 1); + _dataIndex = offset; + return num; + } + + return 0; + } + + public override bool CanSeek => true; + public override bool CanRead => true; + public override bool CanWrite => false; + public override long Position + { + get => _dataIndex; + set => Seek(value, SeekOrigin.Begin); + } + public override long Length + { + get + { + EnsureOpen(); + + if (_length == null) + { + _length = _reader.GetBytes(0, 0, null, 0, 0); + } + + return _length.Value; + } + } + + public override int Read(byte[] buffer, int offset, int count) + { + EnsureOpen(); + + var read = _reader.GetBytes(0, _dataIndex + offset, buffer, 0, count); + _dataIndex += read; + + return (int)read; + } + + public override void Flush() + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + CloseReader(); + } + } + + public void CloseReader() + { + if (_reader != null) + { + if (!_reader.IsClosed) + _reader.Close(); + + _reader = null; + } + + if (_connection != null) + { + if (_connection.State == ConnectionState.Open) + _connection.Close(); + + _connection.Dispose(); + _connection = null; + } + + _dataIndex = 0; + PrimaryKey = null; + } + + #endregion + } +} diff --git a/src/Libraries/SmartStore.Data/app.config b/src/Libraries/SmartStore.Data/app.config index 63141836b9..a625161735 100644 --- a/src/Libraries/SmartStore.Data/app.config +++ b/src/Libraries/SmartStore.Data/app.config @@ -4,20 +4,28 @@ - + - - - - + + + + + + + + + + + + - + diff --git a/src/Libraries/SmartStore.Data/packages.config b/src/Libraries/SmartStore.Data/packages.config index a063941a5e..bc852168b3 100644 --- a/src/Libraries/SmartStore.Data/packages.config +++ b/src/Libraries/SmartStore.Data/packages.config @@ -1,10 +1,12 @@  - - - + + + + - - + + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Affiliates/AffiliateService.cs b/src/Libraries/SmartStore.Services/Affiliates/AffiliateService.cs index 68475199cf..25abf782ae 100644 --- a/src/Libraries/SmartStore.Services/Affiliates/AffiliateService.cs +++ b/src/Libraries/SmartStore.Services/Affiliates/AffiliateService.cs @@ -7,46 +7,20 @@ namespace SmartStore.Services.Affiliates { - /// - /// Affiliate service - /// public partial class AffiliateService : IAffiliateService { - #region Fields - private readonly IRepository _affiliateRepository; - private readonly IEventPublisher _eventPublisher; - - #endregion - - #region Ctor - /// - /// Ctor - /// - /// Affiliate repository - /// Event published - public AffiliateService(IRepository affiliateRepository, - IEventPublisher eventPublisher) + public AffiliateService(IRepository affiliateRepository) { _affiliateRepository = affiliateRepository; - _eventPublisher = eventPublisher; } - #endregion - - #region Methods - - /// - /// Gets an affiliate by affiliate identifier - /// - /// Affiliate identifier - /// Affiliate public virtual Affiliate GetAffiliateById(int affiliateId) { if (affiliateId == 0) return null; - + return _affiliateRepository.GetById(affiliateId); } @@ -102,8 +76,5 @@ public virtual void UpdateAffiliate(Affiliate affiliate) _affiliateRepository.Update(affiliate); } - - #endregion - } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Affiliates/IAffiliateService.cs b/src/Libraries/SmartStore.Services/Affiliates/IAffiliateService.cs index c81ca3cd53..ab8dfe25a2 100644 --- a/src/Libraries/SmartStore.Services/Affiliates/IAffiliateService.cs +++ b/src/Libraries/SmartStore.Services/Affiliates/IAffiliateService.cs @@ -39,6 +39,6 @@ public partial interface IAffiliateService /// /// Affiliate void UpdateAffiliate(Affiliate affiliate); - + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Authentication/External/AuthorizationResult.cs b/src/Libraries/SmartStore.Services/Authentication/External/AuthorizationResult.cs index 1458b333e5..f1a4d248cf 100644 --- a/src/Libraries/SmartStore.Services/Authentication/External/AuthorizationResult.cs +++ b/src/Libraries/SmartStore.Services/Authentication/External/AuthorizationResult.cs @@ -17,10 +17,7 @@ public void AddError(string error) this.Errors.Add(error); } - public bool Success - { - get { return this.Errors.Count == 0; } - } + public bool Success => this.Errors.Count == 0; public OpenAuthenticationStatus Status { get; private set; } diff --git a/src/Libraries/SmartStore.Services/Authentication/External/AuthorizeState.cs b/src/Libraries/SmartStore.Services/Authentication/External/AuthorizeState.cs index 35e853bbfc..da90c9f326 100644 --- a/src/Libraries/SmartStore.Services/Authentication/External/AuthorizeState.cs +++ b/src/Libraries/SmartStore.Services/Authentication/External/AuthorizeState.cs @@ -10,7 +10,7 @@ public partial class AuthorizeState public IList Errors { get; set; } private readonly string _returnUrl; - + public AuthorizeState(string returnUrl, OpenAuthenticationStatus openAuthenticationStatus) { Errors = new List(); @@ -29,11 +29,8 @@ public AuthorizeState(string returnUrl, AuthorizationResult authorizationResult) } public OpenAuthenticationStatus AuthenticationStatus { get; private set; } - - public bool Success - { - get { return (this.Errors.Count == 0); } - } + + public bool Success => (this.Errors.Count == 0); public void AddError(string error) { diff --git a/src/Libraries/SmartStore.Services/Authentication/External/ExternalAuthorizer.cs b/src/Libraries/SmartStore.Services/Authentication/External/ExternalAuthorizer.cs index 7eab4cd87a..c46a48d693 100644 --- a/src/Libraries/SmartStore.Services/Authentication/External/ExternalAuthorizer.cs +++ b/src/Libraries/SmartStore.Services/Authentication/External/ExternalAuthorizer.cs @@ -26,25 +26,25 @@ public partial class ExternalAuthorizer : IExternalAuthorizer private readonly CustomerSettings _customerSettings; private readonly ExternalAuthenticationSettings _externalAuthenticationSettings; private readonly IShoppingCartService _shoppingCartService; - private readonly IMessageFactory _messageFactory; - private readonly LocalizationSettings _localizationSettings; + private readonly IMessageFactory _messageFactory; + private readonly LocalizationSettings _localizationSettings; #endregion #region Ctor public ExternalAuthorizer( - IAuthenticationService authenticationService, + IAuthenticationService authenticationService, IOpenAuthenticationService openAuthenticationService, IGenericAttributeService genericAttributeService, ICustomerRegistrationService customerRegistrationService, - ICustomerActivityService customerActivityService, - ILocalizationService localizationService, - IWorkContext workContext, - CustomerSettings customerSettings, + ICustomerActivityService customerActivityService, + ILocalizationService localizationService, + IWorkContext workContext, + CustomerSettings customerSettings, ExternalAuthenticationSettings externalAuthenticationSettings, IShoppingCartService shoppingCartService, - IMessageFactory messageFactory, - LocalizationSettings localizationSettings) + IMessageFactory messageFactory, + LocalizationSettings localizationSettings) { _authenticationService = authenticationService; _openAuthenticationService = openAuthenticationService; @@ -130,13 +130,13 @@ public virtual AuthorizationResult Authorize(OpenAuthenticationParameters parame var registrationResult = _customerRegistrationService.RegisterCustomer(registrationRequest); if (registrationResult.Success) { - // store other parameters (form fields) - if (details.FirstName.HasValue()) - currentCustomer.FirstName = details.FirstName; + // store other parameters (form fields) + if (details.FirstName.HasValue()) + currentCustomer.FirstName = details.FirstName; if (details.LastName.HasValue()) - currentCustomer.LastName = details.LastName; + currentCustomer.LastName = details.LastName; - userFound = currentCustomer; + userFound = currentCustomer; _openAuthenticationService.AssociateExternalAccountWithUser(currentCustomer, parameters); ExternalAuthorizerHelper.RemoveParameters(); @@ -145,7 +145,7 @@ public virtual AuthorizationResult Authorize(OpenAuthenticationParameters parame //authenticate if (isApproved) _authenticationService.SignIn(userFound ?? userLoggedIn, false); - + //notifications if (_customerSettings.NotifyNewCustomerRegistration) _messageFactory.SendCustomerRegisteredNotificationMessage(currentCustomer, _localizationSettings.DefaultAdminLanguageId); @@ -153,24 +153,24 @@ public virtual AuthorizationResult Authorize(OpenAuthenticationParameters parame switch (_customerSettings.UserRegistrationType) { case UserRegistrationType.EmailValidation: - { - // email validation message - _genericAttributeService.SaveAttribute(currentCustomer, SystemCustomerAttributeNames.AccountActivationToken, Guid.NewGuid().ToString()); - _messageFactory.SendCustomerEmailValidationMessage(currentCustomer, _workContext.WorkingLanguage.Id); + { + // email validation message + _genericAttributeService.SaveAttribute(currentCustomer, SystemCustomerAttributeNames.AccountActivationToken, Guid.NewGuid().ToString()); + _messageFactory.SendCustomerEmailValidationMessage(currentCustomer, _workContext.WorkingLanguage.Id); - return new AuthorizationResult(OpenAuthenticationStatus.AutoRegisteredEmailValidation); - } + return new AuthorizationResult(OpenAuthenticationStatus.AutoRegisteredEmailValidation); + } case UserRegistrationType.AdminApproval: - { - return new AuthorizationResult(OpenAuthenticationStatus.AutoRegisteredAdminApproval); - } + { + return new AuthorizationResult(OpenAuthenticationStatus.AutoRegisteredAdminApproval); + } case UserRegistrationType.Standard: - { - // send customer welcome message - _messageFactory.SendCustomerWelcomeMessage(currentCustomer, _workContext.WorkingLanguage.Id); + { + // send customer welcome message + _messageFactory.SendCustomerWelcomeMessage(currentCustomer, _workContext.WorkingLanguage.Id); - return new AuthorizationResult(OpenAuthenticationStatus.AutoRegisteredStandard); - } + return new AuthorizationResult(OpenAuthenticationStatus.AutoRegisteredStandard); + } default: break; } diff --git a/src/Libraries/SmartStore.Services/Authentication/External/IExternalAuthenticationMethod.cs b/src/Libraries/SmartStore.Services/Authentication/External/IExternalAuthenticationMethod.cs index 6e8029ce4a..e7eed71acb 100644 --- a/src/Libraries/SmartStore.Services/Authentication/External/IExternalAuthenticationMethod.cs +++ b/src/Libraries/SmartStore.Services/Authentication/External/IExternalAuthenticationMethod.cs @@ -9,7 +9,7 @@ namespace SmartStore.Services.Authentication.External /// Provides an interface for creating external authentication methods /// public partial interface IExternalAuthenticationMethod : IProvider, IUserEditable - { + { /// /// Gets a route for displaying plugin in public store /// diff --git a/src/Libraries/SmartStore.Services/Authentication/External/IExternalProviderAuthorizer.cs b/src/Libraries/SmartStore.Services/Authentication/External/IExternalProviderAuthorizer.cs index d46a5ba1db..afc3348198 100644 --- a/src/Libraries/SmartStore.Services/Authentication/External/IExternalProviderAuthorizer.cs +++ b/src/Libraries/SmartStore.Services/Authentication/External/IExternalProviderAuthorizer.cs @@ -5,12 +5,12 @@ namespace SmartStore.Services.Authentication.External { public partial interface IExternalProviderAuthorizer { - /// - /// Authorize response - /// - /// Return URL - /// true - Verify response;false - request authentication;null - determine automatically - /// Authorize state - AuthorizeState Authorize(string returnUrl, bool? verifyResponse = null); + /// + /// Authorize response + /// + /// Return URL + /// true - Verify response;false - request authentication;null - determine automatically + /// Authorize state + AuthorizeState Authorize(string returnUrl, bool? verifyResponse = null); } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Authentication/External/OpenAuthenticationParameters.cs b/src/Libraries/SmartStore.Services/Authentication/External/OpenAuthenticationParameters.cs index 1348bcadd3..e8a84d18b3 100644 --- a/src/Libraries/SmartStore.Services/Authentication/External/OpenAuthenticationParameters.cs +++ b/src/Libraries/SmartStore.Services/Authentication/External/OpenAuthenticationParameters.cs @@ -14,9 +14,6 @@ public abstract partial class OpenAuthenticationParameters public string OAuthToken { get; set; } public string OAuthAccessToken { get; set; } - public virtual IList UserClaims - { - get { return new List(0); } - } + public virtual IList UserClaims => new List(0); } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Authentication/External/OpenAuthenticationService.cs b/src/Libraries/SmartStore.Services/Authentication/External/OpenAuthenticationService.cs index aad91d8ff2..1cbca5a9f4 100644 --- a/src/Libraries/SmartStore.Services/Authentication/External/OpenAuthenticationService.cs +++ b/src/Libraries/SmartStore.Services/Authentication/External/OpenAuthenticationService.cs @@ -17,34 +17,34 @@ public partial class OpenAuthenticationService : IOpenAuthenticationService private readonly IPluginFinder _pluginFinder; private readonly ExternalAuthenticationSettings _externalAuthenticationSettings; private readonly IRepository _externalAuthenticationRecordRepository; - private readonly ISettingService _settingService; - private readonly IProviderManager _providerManager; + private readonly ISettingService _settingService; + private readonly IProviderManager _providerManager; public OpenAuthenticationService( - IRepository externalAuthenticationRecordRepository, + IRepository externalAuthenticationRecordRepository, IPluginFinder pluginFinder, ExternalAuthenticationSettings externalAuthenticationSettings, ICustomerService customerService, - ISettingService settingService, - IProviderManager providerManager) + ISettingService settingService, + IProviderManager providerManager) { this._externalAuthenticationRecordRepository = externalAuthenticationRecordRepository; this._pluginFinder = pluginFinder; this._externalAuthenticationSettings = externalAuthenticationSettings; this._customerService = customerService; - this._settingService = settingService; - this._providerManager = providerManager; + this._settingService = settingService; + this._providerManager = providerManager; } - /// - /// Load all external authentication methods - /// - /// Load records allows only in specified store; pass 0 to load all records - /// External authentication methods - public virtual IEnumerable> LoadAllExternalAuthenticationMethods(int storeId = 0) - { - return _providerManager.GetAllProviders(storeId); - } + /// + /// Load all external authentication methods + /// + /// Load records allows only in specified store; pass 0 to load all records + /// External authentication methods + public virtual IEnumerable> LoadAllExternalAuthenticationMethods(int storeId = 0) + { + return _providerManager.GetAllProviders(storeId); + } /// /// Load active external authentication methods @@ -53,11 +53,11 @@ public virtual IEnumerable> LoadAllExter /// Payment methods public virtual IEnumerable> LoadActiveExternalAuthenticationMethods(int storeId = 0) { - var allMethods = LoadAllExternalAuthenticationMethods(storeId); - var activeMethods = allMethods - .Where(p => _externalAuthenticationSettings.ActiveAuthenticationMethodSystemNames.Contains(p.Metadata.SystemName, StringComparer.InvariantCultureIgnoreCase)); + var allMethods = LoadAllExternalAuthenticationMethods(storeId); + var activeMethods = allMethods + .Where(p => _externalAuthenticationSettings.ActiveAuthenticationMethodSystemNames.Contains(p.Metadata.SystemName, StringComparer.InvariantCultureIgnoreCase)); - return activeMethods; + return activeMethods; } /// @@ -67,7 +67,7 @@ public virtual IEnumerable> LoadActiveEx /// Found external authentication method public virtual Provider LoadExternalAuthenticationMethodBySystemName(string systemName, int storeId = 0) { - return _providerManager.GetProvider(systemName, storeId); + return _providerManager.GetProvider(systemName, storeId); } @@ -83,11 +83,11 @@ public virtual void AssociateExternalAccountWithUser(Customer customer, OpenAuth if (parameters.UserClaims != null) foreach (var userClaim in parameters.UserClaims .Where(x => x.Contact != null && !String.IsNullOrEmpty(x.Contact.Email))) - { - //found - email = userClaim.Contact.Email; - break; - } + { + //found + email = userClaim.Contact.Email; + break; + } var externalAuthenticationRecord = new ExternalAuthenticationRecord() { diff --git a/src/Libraries/SmartStore.Services/Authentication/External/OpenAuthentificationExtentions.cs b/src/Libraries/SmartStore.Services/Authentication/External/OpenAuthentificationExtentions.cs index 1db598a637..41295dfd59 100644 --- a/src/Libraries/SmartStore.Services/Authentication/External/OpenAuthentificationExtentions.cs +++ b/src/Libraries/SmartStore.Services/Authentication/External/OpenAuthentificationExtentions.cs @@ -7,7 +7,7 @@ namespace SmartStore.Services.Authentication.External { public static class OpenAuthenticationExtentions { - public static bool IsMethodActive(this Provider method, ExternalAuthenticationSettings settings) + public static bool IsMethodActive(this Provider method, ExternalAuthenticationSettings settings) { if (method == null) throw new ArgumentNullException("method"); @@ -18,7 +18,7 @@ public static bool IsMethodActive(this Provider m if (settings.ActiveAuthenticationMethodSystemNames == null) return false; - return settings.ActiveAuthenticationMethodSystemNames.Contains(method.Metadata.SystemName, StringComparer.OrdinalIgnoreCase); + return settings.ActiveAuthenticationMethodSystemNames.Contains(method.Metadata.SystemName, StringComparer.OrdinalIgnoreCase); } } } diff --git a/src/Libraries/SmartStore.Services/Authentication/External/UserClaims.cs b/src/Libraries/SmartStore.Services/Authentication/External/UserClaims.cs index 5e2ab5f934..c080e859c0 100644 --- a/src/Libraries/SmartStore.Services/Authentication/External/UserClaims.cs +++ b/src/Libraries/SmartStore.Services/Authentication/External/UserClaims.cs @@ -142,6 +142,6 @@ public partial class BirthDateClaims public DateTime? WholeBirthDate { get; set; } public int Year { get; set; } public string Raw { get; set; } - public DateTime GeneratedBirthDate { get { return new DateTime(Year, Month, DayOfMonth); } } + public DateTime GeneratedBirthDate => new DateTime(Year, Month, DayOfMonth); } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Authentication/FormsAuthenticationService.cs b/src/Libraries/SmartStore.Services/Authentication/FormsAuthenticationService.cs index 3e5a33fa85..626d002326 100644 --- a/src/Libraries/SmartStore.Services/Authentication/FormsAuthenticationService.cs +++ b/src/Libraries/SmartStore.Services/Authentication/FormsAuthenticationService.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Web; using System.Web.Security; using SmartStore.Core; @@ -12,43 +14,50 @@ public partial class FormsAuthenticationService : IAuthenticationService private readonly HttpContextBase _httpContext; private readonly ICustomerService _customerService; private readonly CustomerSettings _customerSettings; + private readonly PrivacySettings _privacySettings; private readonly TimeSpan _expirationTimeSpan; private Customer _cachedCustomer; - public FormsAuthenticationService(HttpContextBase httpContext, ICustomerService customerService, CustomerSettings customerSettings) + public FormsAuthenticationService( + HttpContextBase httpContext, + ICustomerService customerService, + CustomerSettings customerSettings, + PrivacySettings privacySettings) { _httpContext = httpContext; _customerService = customerService; _customerSettings = customerSettings; + _privacySettings = privacySettings; _expirationTimeSpan = FormsAuthentication.Timeout; } public virtual void SignIn(Customer customer, bool createPersistentCookie) { var now = DateTime.UtcNow.ToLocalTime(); - var name = _customerSettings.CustomerLoginType != CustomerLoginType.Email ? customer.Username : customer.Email; + var name = _customerSettings.CustomerLoginType != CustomerLoginType.Email ? customer.Username : customer.Email; - var ticket = new FormsAuthenticationTicket( + var ticket = new FormsAuthenticationTicket( 1 /*version*/, - name, + name, now, now.Add(_expirationTimeSpan), createPersistentCookie, - name, + name, FormsAuthentication.FormsCookiePath); var encryptedTicket = FormsAuthentication.Encrypt(ticket); - var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket) - { - HttpOnly = true, - Secure = FormsAuthentication.RequireSSL, - Path = FormsAuthentication.FormsCookiePath - }; + var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket) + { + HttpOnly = true, + Path = FormsAuthentication.FormsCookiePath, + Secure = FormsAuthentication.RequireSSL, + SameSite = FormsAuthentication.RequireSSL ? (SameSiteMode)_privacySettings.SameSiteMode : SameSiteMode.Lax + }; - if (ticket.IsPersistent) + if (ticket.IsPersistent) { cookie.Expires = ticket.Expiration; } @@ -70,57 +79,68 @@ public virtual void SignOut() public virtual Customer GetAuthenticatedCustomer() { - if (_cachedCustomer != null) - return _cachedCustomer; + if (_cachedCustomer != null) + return _cachedCustomer; - if (_httpContext?.Request == null || !_httpContext.Request.IsAuthenticated || _httpContext.User == null) - return null; + if (_httpContext?.Request == null || !_httpContext.Request.IsAuthenticated) + return null; - Customer customer = null; - SmartStoreIdentity ident = null; + Customer customer = null; - if (_httpContext.User.Identity is FormsIdentity formsIdentity) - { - customer = GetAuthenticatedCustomerFromTicket(formsIdentity.Ticket); - } - else if ((ident = _httpContext.User.Identity as SmartStoreIdentity) != null) - { - customer = _customerService.GetCustomerById(ident.CustomerId); - } + if (_httpContext.User.Identity is FormsIdentity formsIdent) + { + customer = GetAuthenticatedCustomerFromTicket(formsIdent.Ticket); + } + else if (_httpContext.User.Identity is SmartStoreIdentity ident) + { + customer = _customerService.GetCustomerById(ident.CustomerId); + } - if (customer != null && customer.Active && !customer.Deleted && customer.IsRegistered()) - { - _cachedCustomer = customer; - } + if (customer != null && customer.Active && !customer.Deleted && customer.IsRegistered()) + { + _cachedCustomer = customer; + } - return _cachedCustomer; - } + return _cachedCustomer; + } public virtual Customer GetAuthenticatedCustomerFromTicket(FormsAuthenticationTicket ticket) { - Guard.NotNull(ticket, nameof(ticket)); + Guard.NotNull(ticket, nameof(ticket)); var usernameOrEmail = ticket.UserData; - if (string.IsNullOrWhiteSpace(usernameOrEmail)) + if (!usernameOrEmail.HasValue()) return null; - Customer customer = null; + List> customerResolvers = new List>(2); if (_customerSettings.CustomerLoginType == CustomerLoginType.Email) { - customer = _customerService.GetCustomerByEmail(usernameOrEmail); + customerResolvers.Add(_customerService.GetCustomerByEmail); } else if (_customerSettings.CustomerLoginType == CustomerLoginType.Username) { - customer = _customerService.GetCustomerByUsername(usernameOrEmail); + customerResolvers.Add(_customerService.GetCustomerByUsername); } else { - customer = _customerService.GetCustomerByEmail(usernameOrEmail) ?? _customerService.GetCustomerByUsername(usernameOrEmail); + customerResolvers.Add(_customerService.GetCustomerByUsername); + var mayBeEmail = usernameOrEmail.IndexOf('@') > -1; + if (mayBeEmail) + { + customerResolvers.Insert(0, _customerService.GetCustomerByEmail); + } + else + { + customerResolvers.Add(_customerService.GetCustomerByEmail); + } } - return customer; + return customerResolvers + .Select(x => x.Invoke(usernameOrEmail)) + .Where(x => x != null) + .FirstOrDefault(); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Authentication/IAuthenticationService.cs b/src/Libraries/SmartStore.Services/Authentication/IAuthenticationService.cs index 16248f3ac8..6ebbb2baf7 100644 --- a/src/Libraries/SmartStore.Services/Authentication/IAuthenticationService.cs +++ b/src/Libraries/SmartStore.Services/Authentication/IAuthenticationService.cs @@ -5,7 +5,7 @@ namespace SmartStore.Services.Authentication /// /// Authentication service interface /// - public partial interface IAuthenticationService + public partial interface IAuthenticationService { void SignIn(Customer customer, bool createPersistentCookie); void SignOut(); diff --git a/src/Libraries/SmartStore.Services/Blogs/BlogExtensions.cs b/src/Libraries/SmartStore.Services/Blogs/BlogExtensions.cs index 172050bd20..1c54dec053 100644 --- a/src/Libraries/SmartStore.Services/Blogs/BlogExtensions.cs +++ b/src/Libraries/SmartStore.Services/Blogs/BlogExtensions.cs @@ -20,7 +20,7 @@ public static class BlogExtensions public static IList GetPostsByDate(this IList source, DateTime dateFrom, DateTime dateTo) { - var list = source.ToList().FindAll(delegate(BlogPost p) + var list = source.ToList().FindAll(delegate (BlogPost p) { return (dateFrom.Date <= p.CreatedOnUtc && p.CreatedOnUtc.Date <= dateTo); }); diff --git a/src/Libraries/SmartStore.Services/Blogs/BlogMessageFactoryExtensions.cs b/src/Libraries/SmartStore.Services/Blogs/BlogMessageFactoryExtensions.cs index 7ef5c96706..32e138fe47 100644 --- a/src/Libraries/SmartStore.Services/Blogs/BlogMessageFactoryExtensions.cs +++ b/src/Libraries/SmartStore.Services/Blogs/BlogMessageFactoryExtensions.cs @@ -5,15 +5,15 @@ namespace SmartStore.Services.Blogs { - public static class BlogMessageFactoryExtensions - { - /// - /// Sends a blog comment notification message to a store owner - /// - public static CreateMessageResult SendBlogCommentNotificationMessage(this IMessageFactory factory, BlogComment blogComment, int languageId = 0) - { - Guard.NotNull(blogComment, nameof(blogComment)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.BlogCommentStoreOwner, languageId, customer: blogComment.Customer), true, blogComment); - } - } + public static class BlogMessageFactoryExtensions + { + /// + /// Sends a blog comment notification message to a store owner + /// + public static CreateMessageResult SendBlogCommentNotificationMessage(this IMessageFactory factory, BlogComment blogComment, int languageId = 0) + { + Guard.NotNull(blogComment, nameof(blogComment)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.BlogCommentStoreOwner, languageId, customer: blogComment.Customer), true, blogComment); + } + } } diff --git a/src/Libraries/SmartStore.Services/Blogs/BlogService.cs b/src/Libraries/SmartStore.Services/Blogs/BlogService.cs index 7cdfafa8b7..1dc0977884 100644 --- a/src/Libraries/SmartStore.Services/Blogs/BlogService.cs +++ b/src/Libraries/SmartStore.Services/Blogs/BlogService.cs @@ -4,96 +4,105 @@ using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Blogs; -using SmartStore.Core.Domain.Stores; -using SmartStore.Core.Events; -using SmartStore.Services.Localization; using SmartStore.Core.Domain.Seo; +using SmartStore.Core.Domain.Stores; +using SmartStore.Services.Seo; using SmartStore.Utilities; namespace SmartStore.Services.Blogs { - /// - /// Blog service - /// - public partial class BlogService : IBlogService + public partial class BlogService : IBlogService, IXmlSitemapPublisher { - #region Fields - private readonly IRepository _blogPostRepository; - private readonly IRepository _storeMappingRepository; - private readonly ICommonServices _services; - private readonly ILanguageService _languageService; + private readonly IRepository _storeMappingRepository; private readonly SeoSettings _seoSettings; - private readonly BlogSettings _blogSettings; - - #endregion - - #region Ctor - public BlogService(IRepository blogPostRepository, - IRepository storeMappingRepository, - ICommonServices services, - ILanguageService languageService, - SeoSettings seoSettings, - BlogSettings blogSettings) + public BlogService( + IRepository blogPostRepository, + IRepository storeMappingRepository, + SeoSettings seoSettings) { _blogPostRepository = blogPostRepository; - _storeMappingRepository = storeMappingRepository; - _services = services; - _languageService = languageService; - _blogSettings = blogSettings; + _storeMappingRepository = storeMappingRepository; _seoSettings = seoSettings; - - this.QuerySettings = DbQuerySettings.Default; } - public DbQuerySettings QuerySettings { get; set; } + public DbQuerySettings QuerySettings { get; set; } = DbQuerySettings.Default; - #endregion + public virtual void InsertBlogPost(BlogPost blogPost) + { + Guard.NotNull(blogPost, nameof(blogPost)); - #region Methods + _blogPostRepository.Insert(blogPost); + } + + public virtual void UpdateBlogPost(BlogPost blogPost) + { + Guard.NotNull(blogPost, nameof(blogPost)); + + _blogPostRepository.Update(blogPost); + } - /// - /// Deletes a blog post - /// - /// Blog post public virtual void DeleteBlogPost(BlogPost blogPost) { - if (blogPost == null) - throw new ArgumentNullException("blogPost"); + if (blogPost != null) + { + _blogPostRepository.Delete(blogPost); + } + } + + public virtual void UpdateCommentTotals(BlogPost blogPost) + { + Guard.NotNull(blogPost, nameof(blogPost)); + + var approvedCommentCount = 0; + var notApprovedCommentCount = 0; + var blogComments = blogPost.BlogComments; + + foreach (var bc in blogComments) + { + if (bc.IsApproved) + { + approvedCommentCount++; + } + else + { + notApprovedCommentCount++; + } + } + + blogPost.ApprovedCommentCount = approvedCommentCount; + blogPost.NotApprovedCommentCount = notApprovedCommentCount; - _blogPostRepository.Delete(blogPost); + UpdateBlogPost(blogPost); } - /// - /// Gets a blog post - /// - /// Blog post identifier - /// Blog post public virtual BlogPost GetBlogPostById(int blogPostId) { if (blogPostId == 0) + { return null; + } - return _blogPostRepository.GetById(blogPostId); + return _blogPostRepository.GetById(blogPostId); } - /// - /// Gets all blog posts - /// - /// The store identifier; pass 0 to load all records - /// Language identifier; 0 if you want to get all records - /// Filter by created date; null if you want to get all records - /// Filter by created date; null if you want to get all records - /// Page index - /// Page size - /// A value indicating whether to show hidden records - /// The maximum age of returned blog posts - /// Blog posts - public virtual IPagedList GetAllBlogPosts(int storeId, int languageId, - DateTime? dateFrom, DateTime? dateTo, int pageIndex, int pageSize, bool showHidden = false, DateTime? maxAge = null) + public virtual IPagedList GetAllBlogPosts( + int storeId, + DateTime? dateFrom, + DateTime? dateTo, + int pageIndex, + int pageSize, + int languageId = 0, + bool showHidden = false, + DateTime? maxAge = null, + string title = "", + string intro = "", + string body = "", + string tag = "", + bool untracked = true) { - var query = _blogPostRepository.Table; + var query = untracked ? _blogPostRepository.TableUntracked : _blogPostRepository.Table; if (dateFrom.HasValue) query = query.Where(b => dateFrom.Value <= b.CreatedOnUtc); @@ -101,66 +110,72 @@ public virtual IPagedList GetAllBlogPosts(int storeId, int languageId, if (dateTo.HasValue) query = query.Where(b => dateTo.Value >= b.CreatedOnUtc); - if (languageId > 0) - query = query.Where(b => languageId == b.LanguageId); + if (maxAge.HasValue) + query = query.Where(b => b.CreatedOnUtc >= maxAge.Value); + + if (title.HasValue()) + query = query.Where(b => b.Title.Contains(title)); + + if (intro.HasValue()) + query = query.Where(b => b.Intro.Contains(intro)); - if (maxAge.HasValue) - query = query.Where(b => b.CreatedOnUtc >= maxAge.Value); + if (body.HasValue()) + query = query.Where(b => b.Body.Contains(body)); + + if (tag.HasValue()) + query = query.Where(b => b.Tags.Contains(tag)); + + if (languageId != 0) + { + query = query.Where(b => !b.LanguageId.HasValue || b.LanguageId == languageId); + } if (!showHidden) { var utcNow = DateTime.UtcNow; query = query.Where(b => !b.StartDateUtc.HasValue || b.StartDateUtc <= utcNow); query = query.Where(b => !b.EndDateUtc.HasValue || b.EndDateUtc >= utcNow); + query = query.Where(b => b.IsPublished); } - if (storeId > 0 && !QuerySettings.IgnoreMultiStore) - { - //Store mapping - query = from bp in query - join sm in _storeMappingRepository.Table - on new { c1 = bp.Id, c2 = "BlogPost" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into bp_sm - from sm in bp_sm.DefaultIfEmpty() - where !bp.LimitedToStores || storeId == sm.StoreId - select bp; - - //only distinct blog posts (group by ID) - query = from bp in query - group bp by bp.Id into bpGroup - orderby bpGroup.Key - select bpGroup.FirstOrDefault(); - } + if (storeId > 0 && !QuerySettings.IgnoreMultiStore) + { + // Store mapping. + query = from bp in query + join sm in _storeMappingRepository.Table + on new { c1 = bp.Id, c2 = "BlogPost" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into bp_sm + from sm in bp_sm.DefaultIfEmpty() + where !bp.LimitedToStores || storeId == sm.StoreId + select bp; + + // Only distinct blog posts (group by ID). + query = from bp in query + group bp by bp.Id into bpGroup + orderby bpGroup.Key + select bpGroup.FirstOrDefault(); + } query = query.OrderByDescending(b => b.CreatedOnUtc); - + var blogPosts = new PagedList(query, pageIndex, pageSize); return blogPosts; } - /// - /// Gets all blog posts - /// - /// The store identifier; pass 0 to load all records - /// Language identifier. 0 if you want to get all news - /// Tag - /// Page index - /// Page size - /// A value indicating whether to show hidden records - /// Blog posts - public virtual IPagedList GetAllBlogPostsByTag( - int storeId, - int languageId, - string tag, - int pageIndex, - int pageSize, - bool showHidden = false) + public virtual IPagedList GetAllBlogPostsByTag( + int storeId, + string tag, + int pageIndex, + int pageSize, + int languageId = 0, + bool showHidden = false, + DateTime? maxAge = null) { tag = tag.Trim(); - //we laod all records and only then filter them by tag - var blogPostsAll = GetAllBlogPosts(storeId, languageId, null, null, 0, int.MaxValue, showHidden); + // We load all records and only then filter them by tag. + var blogPostsAll = GetAllBlogPosts(storeId, null, null, 0, int.MaxValue, languageId, showHidden, maxAge); var taggedBlogPosts = new List(); - + foreach (var blogPost in blogPostsAll) { var tags = blogPost.ParseTags().Select(x => SeoHelper.GetSeName(x, @@ -169,27 +184,19 @@ public virtual IPagedList GetAllBlogPostsByTag( true, _seoSettings.SeoNameCharConversion)); - if (!String.IsNullOrEmpty(tags.FirstOrDefault(t => t.Equals(tag, StringComparison.InvariantCultureIgnoreCase)))) + if (tags.FirstOrDefault(t => t.Equals(tag, StringComparison.InvariantCultureIgnoreCase)).HasValue()) taggedBlogPosts.Add(blogPost); } - //server-side paging var result = new PagedList(taggedBlogPosts, pageIndex, pageSize); return result; } - /// - /// Gets all blog post tags - /// - /// The store identifier; pass 0 to load all records - /// Language identifier. 0 if you want to get all news - /// A value indicating whether to show hidden records - /// Blog post tags - public virtual IList GetAllBlogPostTags(int storeId, int languageId, bool showHidden = false) + public virtual IList GetAllBlogPostTags(int storeId, int languageId = 0, bool showHidden = false) { var blogPostTags = new List(); - var blogPosts = GetAllBlogPosts(storeId, languageId, null, null, 0, int.MaxValue, showHidden); + var blogPosts = GetAllBlogPosts(storeId, null, null, 0, int.MaxValue, languageId, showHidden); foreach (var blogPost in blogPosts) { var tags = blogPost.ParseTags(); @@ -198,7 +205,7 @@ public virtual IList GetAllBlogPostTags(int storeId, int languageId var foundBlogPostTag = blogPostTags.Find(bpt => bpt.Name.Equals(tag, StringComparison.InvariantCultureIgnoreCase)); if (foundBlogPostTag == null) { - foundBlogPostTag = new BlogPostTag() + foundBlogPostTag = new BlogPostTag { Name = tag, BlogPostCount = 1 @@ -206,60 +213,47 @@ public virtual IList GetAllBlogPostTags(int storeId, int languageId blogPostTags.Add(foundBlogPostTag); } else + { foundBlogPostTag.BlogPostCount++; + } } } return blogPostTags; } - /// - /// Inserts an blog post - /// - /// Blog post - public virtual void InsertBlogPost(BlogPost blogPost) + #region XML Sitemap + + public XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context) { - if (blogPost == null) - throw new ArgumentNullException("blogPost"); + if (!context.LoadSetting().XmlSitemapIncludesBlog || !context.LoadSetting().Enabled) + return null; - _blogPostRepository.Insert(blogPost); + var query = GetAllBlogPosts(context.RequestStoreId, null, null, 0, int.MaxValue).SourceQuery; + + return new BlogPostXmlSitemapResult { Query = query }; } - /// - /// Updates the blog post - /// - /// Blog post - public virtual void UpdateBlogPost(BlogPost blogPost) + class BlogPostXmlSitemapResult : XmlSitemapProvider { - if (blogPost == null) - throw new ArgumentNullException("blogPost"); + public IQueryable Query { get; set; } - _blogPostRepository.Update(blogPost); - } - - /// - /// Update blog post comment totals - /// - /// Blog post - public virtual void UpdateCommentTotals(BlogPost blogPost) - { - if (blogPost == null) - throw new ArgumentNullException("blogPost"); + public override int GetTotalCount() + { + return Query.Count(); + } - int approvedCommentCount = 0; - int notApprovedCommentCount = 0; - var blogComments = blogPost.BlogComments; - foreach (var bc in blogComments) + public override IEnumerable Enlist() { - if (bc.IsApproved) - approvedCommentCount++; - else - notApprovedCommentCount++; + var blogPosts = Query.Select(x => new { x.Id, x.CreatedOnUtc, x.LanguageId }).ToList(); + + foreach (var x in blogPosts) + { + yield return new NamedEntity { EntityName = "BlogPost", Id = x.Id, LastMod = x.CreatedOnUtc, LanguageId = x.LanguageId }; + } } - blogPost.ApprovedCommentCount = approvedCommentCount; - blogPost.NotApprovedCommentCount = notApprovedCommentCount; - UpdateBlogPost(blogPost); + public override int Order => 300; } #endregion diff --git a/src/Libraries/SmartStore.Services/Blogs/IBlogService.cs b/src/Libraries/SmartStore.Services/Blogs/IBlogService.cs index 682ddbe191..9e483667d7 100644 --- a/src/Libraries/SmartStore.Services/Blogs/IBlogService.cs +++ b/src/Libraries/SmartStore.Services/Blogs/IBlogService.cs @@ -6,76 +6,99 @@ namespace SmartStore.Services.Blogs { /// - /// Blog service interface + /// Blog service interface. /// public partial interface IBlogService { /// - /// Deletes a blog post + /// Inserts an blog post. /// - /// Blog post - void DeleteBlogPost(BlogPost blogPost); + /// Blog post. + void InsertBlogPost(BlogPost blogPost); /// - /// Gets a blog post + /// Updates the blog post. /// - /// Blog post identifier - /// Blog post - BlogPost GetBlogPostById(int blogPostId); + /// Blog post. + void UpdateBlogPost(BlogPost blogPost); /// - /// Gets all blog posts + /// Deletes a blog post. /// - /// The store identifier; pass 0 to load all records - /// Language identifier; 0 if you want to get all records - /// Filter by created date; null if you want to get all records - /// Filter by created date; null if you want to get all records - /// Page index - /// Page size - /// A value indicating whether to show hidden records - /// The maximum age of returned blog posts - /// Blog posts - IPagedList GetAllBlogPosts(int storeId, int languageId, - DateTime? dateFrom, DateTime? dateTo, int pageIndex, int pageSize, bool showHidden = false, DateTime? maxAge = null); + /// Blog post. + void DeleteBlogPost(BlogPost blogPost); /// - /// Gets all blog posts + /// Update blog post comment totals. /// - /// The store identifier; pass 0 to load all records - /// Language identifier. 0 if you want to get all news - /// Tag - /// Page index - /// Page size - /// A value indicating whether to show hidden records - /// Blog posts - IPagedList GetAllBlogPostsByTag(int storeId, int languageId, string tag, - int pageIndex, int pageSize, bool showHidden = false); + /// Blog post. + void UpdateCommentTotals(BlogPost blogPost); /// - /// Gets all blog post tags + /// Gets a blog post. /// - /// The store identifier; pass 0 to load all records - /// Language identifier. 0 if you want to get all news - /// A value indicating whether to show hidden records - /// Blog post tags - IList GetAllBlogPostTags(int storeId, int languageId, bool showHidden = false); + /// Blog post identifier. + /// Blog post. + BlogPost GetBlogPostById(int blogPostId); /// - /// Inserts an blog post + /// Gets all blog posts. /// - /// Blog post - void InsertBlogPost(BlogPost blogPost); + /// The store identifier; pass 0 to load all records. + /// Filter by created date; null if you want to get all records. + /// Filter by created date; null if you want to get all records. + /// Page index. + /// Page size. + /// Filter by a language identifier. 0 to get all blog posts. + /// A value indicating whether to show hidden records. + /// The maximum age of returned blog posts. + /// Search for a term or phrase in the title. + /// Search for a term or phrase in the intro. + /// Search for a term or phrase in the full description. + /// Search for a term or phrase in the tags field. + /// Indicates whether to load entities tracked or untracked. + /// Blog posts. + IPagedList GetAllBlogPosts( + int storeId, + DateTime? dateFrom, + DateTime? dateTo, + int pageIndex, + int pageSize, + int languageId = 0, + bool showHidden = false, + DateTime? maxAge = null, + string title = "", + string intro = "", + string body = "", + string tag = "", + bool untracked = true); /// - /// Updates the blog post + /// Gets all blog posts. /// - /// Blog post - void UpdateBlogPost(BlogPost blogPost); + /// The store identifier; pass 0 to load all records. + /// Tag. + /// Page index. + /// Page size. + /// Filter by a language identifier. 0 to get all blog posts. + /// A value indicating whether to show hidden records. + /// Blog posts. + IPagedList GetAllBlogPostsByTag( + int storeId, + string tag, + int pageIndex, + int pageSize, + int languageId = 0, + bool showHidden = false, + DateTime? maxAge = null); /// - /// Update blog post comment totals + /// Gets all blog post tags. /// - /// Blog post - void UpdateCommentTotals(BlogPost blogPost); + /// The store identifier; pass 0 to load all records. + /// Filter by a language identifier. 0 to get all blog posts. + /// A value indicating whether to show hidden records. + /// Blog post tags. + IList GetAllBlogPostTags(int storeId, int languageId = 0, bool showHidden = false); } } diff --git a/src/Libraries/SmartStore.Services/Caching/ClearCacheTask.cs b/src/Libraries/SmartStore.Services/Caching/ClearCacheTask.cs index 2ebfdd303e..cbb5717df4 100644 --- a/src/Libraries/SmartStore.Services/Caching/ClearCacheTask.cs +++ b/src/Libraries/SmartStore.Services/Caching/ClearCacheTask.cs @@ -10,19 +10,19 @@ namespace SmartStore.Services.Caching /// public partial class ClearCacheTask : ITask { - private readonly ICacheManager _cacheManager; - - public ClearCacheTask(ICacheManager cacheManager) - { - _cacheManager = cacheManager; - } - - /// + private readonly ICacheManager _cacheManager; + + public ClearCacheTask(ICacheManager cacheManager) + { + _cacheManager = cacheManager; + } + + /// /// Executes a task /// - public void Execute(TaskExecutionContext ctx) + public void Execute(TaskExecutionContext ctx) { - _cacheManager.Clear(); + _cacheManager.Clear(); } } } diff --git a/src/Libraries/SmartStore.Services/Cart/RenderingOrderTotalsEvent.cs b/src/Libraries/SmartStore.Services/Cart/RenderingOrderTotalsEvent.cs new file mode 100644 index 0000000000..907a20c53f --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/RenderingOrderTotalsEvent.cs @@ -0,0 +1,15 @@ +using SmartStore.Core.Domain.Customers; + +namespace SmartStore.Services.Cart +{ + public class RenderingOrderTotalsEvent + { + public RenderingOrderTotalsEvent() + { + } + + public Customer Customer { get; set; } + + public int? StoreId { get; set; } + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/CartRuleContext.cs b/src/Libraries/SmartStore.Services/Cart/Rules/CartRuleContext.cs index a3940fdbaa..529c7e6414 100644 --- a/src/Libraries/SmartStore.Services/Cart/Rules/CartRuleContext.cs +++ b/src/Libraries/SmartStore.Services/Cart/Rules/CartRuleContext.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using SmartStore.Core; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Stores; @@ -11,8 +7,18 @@ namespace SmartStore.Services.Cart.Rules { public class CartRuleContext { + private readonly Func _sessionKeyBuilder; + private object _sessionKey; + + internal CartRuleContext(Func sessionKeyBuilder) + { + _sessionKeyBuilder = sessionKeyBuilder; + } + public Customer Customer { get; set; } public Store Store { get; set; } public IWorkContext WorkContext { get; set; } + + public object SessionKey => _sessionKey ?? (_sessionKey = _sessionKeyBuilder?.Invoke() ?? 0); } } diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/CartRuleProvider.cs b/src/Libraries/SmartStore.Services/Cart/Rules/CartRuleProvider.cs index 1b7b63badf..3fc46b2574 100644 --- a/src/Libraries/SmartStore.Services/Cart/Rules/CartRuleProvider.cs +++ b/src/Libraries/SmartStore.Services/Cart/Rules/CartRuleProvider.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Autofac; +using SmartStore.Core.Localization; using SmartStore.Rules; using SmartStore.Rules.Domain; using SmartStore.Services.Cart.Rules.Impl; @@ -18,6 +16,7 @@ public interface ICartRuleProvider : IRuleProvider bool RuleMatches(RuleExpression expression); bool RuleMatches(int[] ruleSetIds, LogicalRuleOperator logicalOperator); + bool RuleMatches(IRulesContainer entity, LogicalRuleOperator logicalOperator = LogicalRuleOperator.Or); bool RuleMatches(RuleExpression[] expressions, LogicalRuleOperator logicalOperator); } @@ -35,6 +34,8 @@ public CartRuleProvider(IRuleFactory ruleFactory, IComponentContext componentCon _services = services; } + public Localizer T { get; set; } = NullLocalizer.Instance; + public RuleExpressionGroup CreateExpressionGroup(int ruleSetId) { return _ruleFactory.CreateExpressionGroup(ruleSetId, this) as RuleExpressionGroup; @@ -83,6 +84,26 @@ public bool RuleMatches(int[] ruleSetIds, LogicalRuleOperator logicalOperator) var expressions = ruleSetIds .Select(id => _ruleFactory.CreateExpressionGroup(id, this)) + .Where(x => x != null) + .Cast() + .ToArray(); + + return RuleMatches(expressions, logicalOperator); + } + + public bool RuleMatches(IRulesContainer entity, LogicalRuleOperator logicalOperator = LogicalRuleOperator.Or) + { + Guard.NotNull(entity, nameof(entity)); + + var ruleSets = entity.RuleSets.Where(x => x.Scope == RuleScope.Cart).ToArray(); + if (!ruleSets.Any()) + { + return true; + } + + var expressions = ruleSets + .Select(x => _ruleFactory.CreateExpressionGroup(x, this)) + .Where(x => x != null) .Cast() .ToArray(); @@ -98,7 +119,7 @@ public bool RuleMatches(RuleExpression[] expressions, LogicalRuleOperator logica return true; } - RuleExpressionGroup group = null; + RuleExpressionGroup group; if (expressions.Length == 1 && expressions[0] is RuleExpressionGroup group2) { @@ -106,11 +127,11 @@ public bool RuleMatches(RuleExpression[] expressions, LogicalRuleOperator logica } else { - group = new RuleExpressionGroup() { LogicalOperator = logicalOperator }; + group = new RuleExpressionGroup { LogicalOperator = logicalOperator }; group.AddExpressions(expressions); } - var context = new CartRuleContext + var context = new CartRuleContext(() => group.GetHashCode()) { Customer = _services.WorkContext.CurrentCustomer, Store = _services.StoreContext.CurrentStore, @@ -124,15 +145,15 @@ public bool RuleMatches(RuleExpression[] expressions, LogicalRuleOperator logica public IRule GetProcessor(RuleExpression expression) { + var group = expression as RuleExpressionGroup; var descriptor = expression.Descriptor as CartRuleDescriptor; - if (descriptor == null) + + if (group == null && descriptor == null) { - // TODO: ErrHandling - throw new InvalidOperationException(); + throw new InvalidOperationException($"Missing cart rule descriptor for expression {expression.Id} ('{expression.RawValue.EmptyNull()}')."); } IRule instance; - var group = expression as RuleExpressionGroup; if (group == null && descriptor.ProcessorType != typeof(CompositeRule)) { @@ -148,65 +169,303 @@ public IRule GetProcessor(RuleExpression expression) protected override IEnumerable LoadDescriptors() { - return new List + var language = _services.WorkContext.WorkingLanguage; + var currencyCode = _services.StoreContext.CurrentStore.PrimaryStoreCurrency.CurrencyCode; + + var stores = _services.StoreService.GetAllStores() + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.Name }) + .ToArray(); + + var cartItemQuantity = new CartRuleDescriptor + { + Name = "CartItemQuantity", + DisplayName = T("Admin.Rules.FilterDescriptor.CartItemQuantity"), + RuleType = RuleType.String, + ProcessorType = typeof(CartItemQuantityRule), + Operators = new[] { RuleOperator.IsEqualTo } + }; + cartItemQuantity.Metadata["ValueTemplateName"] = "ValueTemplates/CartItemQuantity"; + cartItemQuantity.Metadata["ProductRuleDescriptor"] = new CartRuleDescriptor + { + Name = "CartItemQuantity-Product", + RuleType = RuleType.Int, + ProcessorType = typeof(CartItemQuantityRule), + Operators = new[] { RuleOperator.IsEqualTo }, + SelectList = new RemoteRuleValueSelectList("Product") + }; + + var descriptors = new List { - new CartRuleDescriptor - { - Name = "CartTotal", - RuleType = RuleType.Money, - Constraints = new IRuleConstraint[0], - ProcessorType = typeof(CartTotalRule) - }, new CartRuleDescriptor { Name = "Currency", + DisplayName = T("Admin.Rules.FilterDescriptor.Currency"), RuleType = RuleType.IntArray, ProcessorType = typeof(CurrencyRule), - Constraints = new IRuleConstraint[0], SelectList = new RemoteRuleValueSelectList("Currency") { Multiple = true } }, new CartRuleDescriptor - { - Name = "CustomerRole", - RuleType = RuleType.IntArray, - ProcessorType = typeof(CustomerRoleRule), - Constraints = new IRuleConstraint[0], - SelectList = new RemoteRuleValueSelectList("CustomerRole") { Multiple = true } - }, - new CartRuleDescriptor { Name = "Language", + DisplayName = T("Admin.Rules.FilterDescriptor.Language"), RuleType = RuleType.IntArray, ProcessorType = typeof(LanguageRule), - Constraints = new IRuleConstraint[0], SelectList = new RemoteRuleValueSelectList("Language") { Multiple = true } }, new CartRuleDescriptor { Name = "Store", + DisplayName = T("Admin.Rules.FilterDescriptor.Store"), RuleType = RuleType.IntArray, ProcessorType = typeof(StoreRule), - Constraints = new IRuleConstraint[0], - SelectList = new RemoteRuleValueSelectList("Store") { Multiple = true } + SelectList = new LocalRuleValueSelectList(stores) { Multiple = true } }, new CartRuleDescriptor { Name = "IPCountry", + DisplayName = T("Admin.Rules.FilterDescriptor.IPCountry"), RuleType = RuleType.StringArray, ProcessorType = typeof(IPCountryRule), - Constraints = new IRuleConstraint[0], SelectList = new RemoteRuleValueSelectList("Country") { Multiple = true } }, new CartRuleDescriptor + { + Name = "Weekday", + DisplayName = T("Admin.Rules.FilterDescriptor.Weekday"), + RuleType = RuleType.IntArray, + ProcessorType = typeof(WeekdayRule), + SelectList = new LocalRuleValueSelectList(WeekdayRule.GetDefaultValues(language)) { Multiple = true } + }, + + new CartRuleDescriptor + { + Name = "CustomerRole", + DisplayName = T("Admin.Rules.FilterDescriptor.IsInCustomerRole"), + RuleType = RuleType.IntArray, + ProcessorType = typeof(CustomerRoleRule), + SelectList = new RemoteRuleValueSelectList("CustomerRole") { Multiple = true }, + IsComparingSequences = true + }, + new CartRuleDescriptor + { + Name = "CartBillingCountry", + DisplayName = T("Admin.Rules.FilterDescriptor.BillingCountry"), + RuleType = RuleType.IntArray, + ProcessorType = typeof(BillingCountryRule), + SelectList = new RemoteRuleValueSelectList("Country") { Multiple = true } + }, + new CartRuleDescriptor + { + Name = "CartShippingCountry", + DisplayName = T("Admin.Rules.FilterDescriptor.ShippingCountry"), + RuleType = RuleType.IntArray, + ProcessorType = typeof(ShippingCountryRule), + SelectList = new RemoteRuleValueSelectList("Country") { Multiple = true } + }, + new CartRuleDescriptor + { + Name = "CartShippingMethod", + DisplayName = T("Admin.Rules.FilterDescriptor.ShippingMethod"), + RuleType = RuleType.IntArray, + ProcessorType = typeof(ShippingMethodRule), + SelectList = new RemoteRuleValueSelectList("ShippingMethod") { Multiple = true } + }, + new CartRuleDescriptor + { + Name = "CartPaymentMethod", + DisplayName = T("Admin.Rules.FilterDescriptor.PaymentMethod"), + RuleType = RuleType.StringArray, + ProcessorType = typeof(PaymentMethodRule), + SelectList = new RemoteRuleValueSelectList("PaymentMethod") { Multiple = true } + }, + + new CartRuleDescriptor + { + Name = "CartTotal", + DisplayName = T("Admin.Rules.FilterDescriptor.CartTotal"), + RuleType = RuleType.Money, + ProcessorType = typeof(CartTotalRule) + }, + new CartRuleDescriptor + { + Name = "CartSubtotal", + DisplayName = T("Admin.Rules.FilterDescriptor.CartSubtotal"), + RuleType = RuleType.Money, + ProcessorType = typeof(CartSubtotalRule) + }, + new CartRuleDescriptor + { + Name = "CartProductCount", + DisplayName = T("Admin.Rules.FilterDescriptor.CartProductCount"), + RuleType = RuleType.Int, + ProcessorType = typeof(CartProductCountRule) + }, + cartItemQuantity, + new CartRuleDescriptor + { + Name = "ProductInCart", + DisplayName = T("Admin.Rules.FilterDescriptor.ProductInCart"), + RuleType = RuleType.IntArray, + ProcessorType = typeof(ProductInCartRule), + SelectList = new RemoteRuleValueSelectList("Product") { Multiple = true }, + IsComparingSequences = true + }, + new CartRuleDescriptor + { + Name = "ProductFromCategoryInCart", + DisplayName = T("Admin.Rules.FilterDescriptor.ProductFromCategoryInCart"), + RuleType = RuleType.IntArray, + ProcessorType = typeof(ProductFromCategoryInCartRule), + SelectList = new RemoteRuleValueSelectList("Category") { Multiple = true }, + IsComparingSequences = true + }, + new CartRuleDescriptor + { + Name = "ProductFromManufacturerInCart", + DisplayName = T("Admin.Rules.FilterDescriptor.ProductFromManufacturerInCart"), + RuleType = RuleType.IntArray, + ProcessorType = typeof(ProductFromManufacturerInCartRule), + SelectList = new RemoteRuleValueSelectList("Manufacturer") { Multiple = true }, + IsComparingSequences = true + }, + new CartRuleDescriptor + { + Name = "ProductInWishlist", + DisplayName = T("Admin.Rules.FilterDescriptor.ProductOnWishlist"), + RuleType = RuleType.IntArray, + ProcessorType = typeof(ProductOnWishlistRule), + SelectList = new RemoteRuleValueSelectList("Product") { Multiple = true }, + IsComparingSequences = true + }, + new CartRuleDescriptor + { + Name = "ProductReviewCount", + DisplayName = T("Admin.Rules.FilterDescriptor.ProductReviewCount"), + RuleType = RuleType.Int, + ProcessorType = typeof(ProductReviewCountRule) + }, + new CartRuleDescriptor + { + Name = "RewardPointsBalance", + DisplayName = T("Admin.Rules.FilterDescriptor.RewardPointsBalance"), + RuleType = RuleType.Int, + ProcessorType = typeof(RewardPointsBalanceRule) + }, + new CartRuleDescriptor { Name = "RuleSet", + DisplayName = T("Admin.Rules.FilterDescriptor.RuleSet"), RuleType = RuleType.Int, ProcessorType = typeof(RuleSetRule), Operators = new[] { RuleOperator.IsEqualTo, RuleOperator.IsNotEqualTo }, - Constraints = new IRuleConstraint[0], SelectList = new RemoteRuleValueSelectList("CartRule"), - } + }, + + new CartRuleDescriptor + { + Name = "CartOrderCount", + DisplayName = T("Admin.Rules.FilterDescriptor.OrderCount"), + GroupKey = "Admin.Orders", + RuleType = RuleType.Int, + ProcessorType = typeof(OrderCountRule) + }, + new CartRuleDescriptor + { + Name = "CartSpentAmount", + DisplayName = T("Admin.Rules.FilterDescriptor.SpentAmount"), + GroupKey = "Admin.Orders", + RuleType = RuleType.Money, + ProcessorType = typeof(SpentAmountRule) + }, + new CartRuleDescriptor + { + Name = "CartPaidBy", + DisplayName = T("Admin.Rules.FilterDescriptor.PaidBy"), + GroupKey = "Admin.Orders", + RuleType = RuleType.StringArray, + ProcessorType = typeof(PaidByRule), + SelectList = new RemoteRuleValueSelectList("PaymentMethod") { Multiple = true }, + IsComparingSequences = true + }, + new CartRuleDescriptor + { + Name = "CartPurchasedProduct", + DisplayName = T("Admin.Rules.FilterDescriptor.PurchasedProduct"), + GroupKey = "Admin.Orders", + RuleType = RuleType.IntArray, + ProcessorType = typeof(PurchasedProductRule), + SelectList = new RemoteRuleValueSelectList("Product") { Multiple = true }, + IsComparingSequences = true + }, + new CartRuleDescriptor + { + Name = "CartPurchasedFromManufacturer", + DisplayName = T("Admin.Rules.FilterDescriptor.PurchasedFromManufacturer"), + GroupKey = "Admin.Orders", + RuleType = RuleType.IntArray, + ProcessorType = typeof(PurchasedFromManufacturerRule), + SelectList = new RemoteRuleValueSelectList("Manufacturer") { Multiple = true }, + IsComparingSequences = true + }, + + new CartRuleDescriptor + { + Name = "UserAgent.IsMobile", + DisplayName = T("Admin.Rules.FilterDescriptor.MobileDevice"), + GroupKey = "Admin.Rules.FilterDescriptor.Group.BrowserUserAgent", + RuleType = RuleType.Boolean, + ProcessorType = typeof(IsMobileRule) + }, + new CartRuleDescriptor + { + Name = "UserAgent.Device", + DisplayName = T("Admin.Rules.FilterDescriptor.DeviceFamily"), + GroupKey = "Admin.Rules.FilterDescriptor.Group.BrowserUserAgent", + RuleType = RuleType.StringArray, + ProcessorType = typeof(DeviceRule), + SelectList = new LocalRuleValueSelectList(DeviceRule.GetDefaultValues()) { Multiple = true, Tags = true } + }, + new CartRuleDescriptor + { + Name = "UserAgent.OS", + DisplayName = T("Admin.Rules.FilterDescriptor.OperatingSystem"), + GroupKey = "Admin.Rules.FilterDescriptor.Group.BrowserUserAgent", + RuleType = RuleType.StringArray, + ProcessorType = typeof(OSRule), + SelectList = new LocalRuleValueSelectList(OSRule.GetDefaultValues()) { Multiple = true, Tags = true } + }, + new CartRuleDescriptor + { + Name = "UserAgent.Browser", + DisplayName = T("Admin.Rules.FilterDescriptor.BrowserName"), + GroupKey = "Admin.Rules.FilterDescriptor.Group.BrowserUserAgent", + RuleType = RuleType.StringArray, + ProcessorType = typeof(BrowserRule), + SelectList = new LocalRuleValueSelectList(BrowserRule.GetDefaultValues()) { Multiple = true, Tags = true } + }, + new CartRuleDescriptor + { + Name = "UserAgent.BrowserMajorVersion", + DisplayName = T("Admin.Rules.FilterDescriptor.BrowserMajorVersion"), + GroupKey = "Admin.Rules.FilterDescriptor.Group.BrowserUserAgent", + RuleType = RuleType.Int, + ProcessorType = typeof(BrowserMajorVersionRule) + }, + new CartRuleDescriptor + { + Name = "UserAgent.BrowserMinorVersion", + DisplayName = T("Admin.Rules.FilterDescriptor.BrowserMinorVersion"), + GroupKey = "Admin.Rules.FilterDescriptor.Group.BrowserUserAgent", + RuleType = RuleType.Int, + ProcessorType = typeof(BrowserMinorVersionRule) + }, }; + + descriptors + .Where(x => x.RuleType == RuleType.Money) + .Each(x => x.Metadata["postfix"] = currencyCode); + + return descriptors; } } } diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/BillingCountryRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/BillingCountryRule.cs new file mode 100644 index 0000000000..5313c27059 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/BillingCountryRule.cs @@ -0,0 +1,10 @@ +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class BillingCountryRule : ListRuleBase + { + protected override int GetValue(CartRuleContext context) + { + return context.Customer?.BillingAddress?.CountryId ?? 0; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CartItemQuantityRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CartItemQuantityRule.cs new file mode 100644 index 0000000000..b15f586972 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CartItemQuantityRule.cs @@ -0,0 +1,95 @@ +using System; +using System.Linq; +using Newtonsoft.Json.Linq; +using SmartStore.Core.Domain.Orders; +using SmartStore.Core.Logging; +using SmartStore.Rules; +using SmartStore.Services.Orders; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class CartItemQuantityRule : IRule + { + private readonly IShoppingCartService _shoppingCartService; + + public CartItemQuantityRule(IShoppingCartService shoppingCartService) + { + _shoppingCartService = shoppingCartService; + } + + public ILogger Logger { get; set; } = NullLogger.Instance; + + public bool Match(CartRuleContext context, RuleExpression expression) + { + int productId = 0; + int? minQuantity = null; + int? maxQuantity = null; + + try + { + var rawValue = expression.Value as string; + if (rawValue.HasValue()) + { + dynamic json = JObject.Parse(rawValue); + productId = ((string)json.ProductId).ToInt(); + + var str = (string)json.MinQuantity; + if (str.HasValue()) + { + minQuantity = str.ToInt(); + } + + str = (string)json.MaxQuantity; + if (str.HasValue()) + { + maxQuantity = str.ToInt(); + } + } + } + catch (Exception ex) + { + Logger.Error(ex); + } + + if (productId == 0) + { + return false; + } + + var cart = _shoppingCartService.GetCartItems(context.Customer, ShoppingCartType.ShoppingCart, context.Store.Id); + var items = cart.Where(x => x.Item.ProductId == productId); + if (!items.Any()) + { + return false; + } + + var quantity = items.Sum(x => x.Item.Quantity); + if (quantity == 0) + { + return false; + } + + if (minQuantity.HasValue && maxQuantity.HasValue) + { + if (minQuantity == maxQuantity) + { + return quantity == minQuantity; + } + else + { + return quantity >= minQuantity && quantity <= maxQuantity; + } + } + else if (minQuantity.HasValue) + { + return quantity >= minQuantity; + } + else if (maxQuantity.HasValue) + { + return quantity <= maxQuantity; + } + + return false; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CartProductCountRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CartProductCountRule.cs new file mode 100644 index 0000000000..f2fed96f95 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CartProductCountRule.cs @@ -0,0 +1,24 @@ +using SmartStore.Core.Domain.Orders; +using SmartStore.Rules; +using SmartStore.Services.Orders; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class CartProductCountRule : IRule + { + private readonly IShoppingCartService _shoppingCartService; + + public CartProductCountRule(IShoppingCartService shoppingCartService) + { + _shoppingCartService = shoppingCartService; + } + + public bool Match(CartRuleContext context, RuleExpression expression) + { + var cart = _shoppingCartService.GetCartItems(context.Customer, ShoppingCartType.ShoppingCart, context.Store.Id); + var productCount = cart.GetTotalProducts(); + + return expression.Operator.Match(productCount, expression.Value); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CartSubtotalRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CartSubtotalRule.cs new file mode 100644 index 0000000000..96c0c8a728 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CartSubtotalRule.cs @@ -0,0 +1,47 @@ +using SmartStore.Core.Domain.Orders; +using SmartStore.Rules; +using SmartStore.Services.Orders; +using SmartStore.Utilities.Threading; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class CartSubtotalRule : IRule + { + private readonly IShoppingCartService _shoppingCartService; + private readonly IOrderTotalCalculationService _orderTotalCalculationService; + + public CartSubtotalRule(IShoppingCartService shoppingCartService, IOrderTotalCalculationService orderTotalCalculationService) + { + _shoppingCartService = shoppingCartService; + _orderTotalCalculationService = orderTotalCalculationService; + } + + public bool Match(CartRuleContext context, RuleExpression expression) + { + var sessionKey = context.SessionKey; + var lockKey = "rule:cart:cartsubtotalrule:" + sessionKey.ToString(); + + if (KeyedLock.IsLockHeld(lockKey)) + { + //$"locked expression {expression.Id}: {lockKey}".Dump(); + return false; + } + + // We must prevent the rule from indirectly calling itself. It would cause a stack overflow on cart page. + using (KeyedLock.Lock(lockKey)) + { + var cart = _shoppingCartService.GetCartItems(context.Customer, ShoppingCartType.ShoppingCart, context.Store.Id); + + _orderTotalCalculationService.GetShoppingCartSubTotal(cart, out _, out _, out var cartSubtotal, out _); + + // Currency values must be rounded, otherwise unexpected results may occur. + var money = new Money(cartSubtotal, context.WorkContext.WorkingCurrency); + cartSubtotal = money.RoundedAmount; + + var result = expression.Operator.Match(cartSubtotal, expression.Value); + //$"unlocked expression {expression.Id}: {lockKey}".Dump(); + return result; + } + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CartTotalRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CartTotalRule.cs index 20736334c5..163535831b 100644 --- a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CartTotalRule.cs +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CartTotalRule.cs @@ -1,20 +1,47 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using SmartStore.Core.Domain.Orders; using SmartStore.Rules; +using SmartStore.Services.Orders; +using SmartStore.Utilities.Threading; namespace SmartStore.Services.Cart.Rules.Impl { public class CartTotalRule : IRule { + private readonly IShoppingCartService _shoppingCartService; + private readonly IOrderTotalCalculationService _orderTotalCalculationService; + + public CartTotalRule( + IShoppingCartService shoppingCartService, + IOrderTotalCalculationService orderTotalCalculationService) + { + _shoppingCartService = shoppingCartService; + _orderTotalCalculationService = orderTotalCalculationService; + } + public bool Match(CartRuleContext context, RuleExpression expression) { - // INFO/TODO: get cart total from somewhere - var cartTotal = 1000d; + var sessionKey = context.SessionKey; + var lockKey = "rule:cart:carttotalrule:" + sessionKey.ToString(); + + if (KeyedLock.IsLockHeld(lockKey)) + { + return false; + } + + // We must prevent the rule from indirectly calling itself. It would cause a stack overflow on cart page. + using (KeyedLock.Lock(lockKey)) + { + var cart = _shoppingCartService.GetCartItems(context.Customer, ShoppingCartType.ShoppingCart, context.Store.Id); + + var cartTotal = ((decimal?)_orderTotalCalculationService.GetShoppingCartTotal(cart)) ?? decimal.Zero; + + // Currency values must be rounded, otherwise unexpected results may occur. + var money = new Money(cartTotal, context.WorkContext.WorkingCurrency); + cartTotal = money.RoundedAmount; - return expression.Operator.Match(cartTotal, expression.Value); + var result = expression.Operator.Match(cartTotal, expression.Value); + return result; + } } } } diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CurrencyRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CurrencyRule.cs index ae92cc3b49..98197d0d52 100644 --- a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CurrencyRule.cs +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CurrencyRule.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SmartStore.Services.Cart.Rules.Impl +namespace SmartStore.Services.Cart.Rules.Impl { public class CurrencyRule : ListRuleBase { diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CustomerRoleRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CustomerRoleRule.cs index 873424bc39..7586c5e723 100644 --- a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CustomerRoleRule.cs +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/CustomerRoleRule.cs @@ -1,25 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using SmartStore.Core.Domain.Customers; -using SmartStore.Rules; +using System.Collections.Generic; namespace SmartStore.Services.Cart.Rules.Impl { - public class CustomerRoleRule : IRule + public class CustomerRoleRule : ListRuleBase { - public bool Match(CartRuleContext context, RuleExpression expression) + protected override IEnumerable GetValues(CartRuleContext context) { - var list = expression.Value as List; - if (list == null || list.Count == 0) - { - return true; - } - - var currentRoleIds = context.Customer.CustomerRoles.Select(x => x.Id); - return currentRoleIds.All(x => expression.Operator.Match(x, list)); + var roleIds = context.Customer.GetRoleIds(); + return roleIds; } } } diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/IPCountryRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/IPCountryRule.cs index 711882580b..e87f58c91d 100644 --- a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/IPCountryRule.cs +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/IPCountryRule.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using SmartStore.Core; using SmartStore.Services.Directory; @@ -14,6 +10,7 @@ public class IPCountryRule : ListRuleBase private readonly IWebHelper _webHelper; public IPCountryRule(IGeoCountryLookup countryLookup, IWebHelper webHelper) + : base(StringComparer.InvariantCultureIgnoreCase) { _countryLookup = countryLookup; _webHelper = webHelper; diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/LanguageRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/LanguageRule.cs index 00409545ee..90a78b736a 100644 --- a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/LanguageRule.cs +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/LanguageRule.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SmartStore.Services.Cart.Rules.Impl +namespace SmartStore.Services.Cart.Rules.Impl { public class LanguageRule : ListRuleBase { diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/OrderCountRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/OrderCountRule.cs new file mode 100644 index 0000000000..2b85245af7 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/OrderCountRule.cs @@ -0,0 +1,24 @@ +using System.Linq; +using SmartStore.Rules; +using SmartStore.Services.Orders; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class OrderCountRule : IRule + { + private readonly IOrderService _orderService; + + public OrderCountRule(IOrderService orderService) + { + _orderService = orderService; + } + + public bool Match(CartRuleContext context, RuleExpression expression) + { + var query = _orderService.GetOrders(context.Store.Id, context.Customer.Id, null, null, null, null, null, null, null, null); + var orderCount = query.Count(); + + return expression.Operator.Match(orderCount, expression.Value); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/PaidByRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/PaidByRule.cs new file mode 100644 index 0000000000..7e2d61978f --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/PaidByRule.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Data.Entity; +using System.Linq; +using SmartStore.Core.Domain.Orders; +using SmartStore.Rules; +using SmartStore.Services.Orders; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class PaidByRule : ListRuleBase + { + private readonly IOrderService _orderService; + + public PaidByRule(IOrderService orderService) + : base(StringComparer.InvariantCultureIgnoreCase) + { + _orderService = orderService; + } + + public override bool Match(CartRuleContext context, RuleExpression expression) + { + if (expression.Operator == RuleOperator.In || expression.Operator == RuleOperator.NotIn) + { + // Get result using LINQ to Entities. + var paymentMethods = expression.Value as List; + if (!(paymentMethods?.Any() ?? false)) + { + return true; + } + + var query = GetQuery(context); + + if (expression.Operator == RuleOperator.In) + { + return query.Where(o => paymentMethods.Contains(o.PaymentMethodSystemName)).Any(); + } + + return query.Where(o => !paymentMethods.Contains(o.PaymentMethodSystemName)).Any(); + } + + // Get result using LINQ to Objects. + return base.Match(context, expression); + } + + protected virtual IQueryable GetQuery(CartRuleContext context) + { + var query = _orderService.GetOrders(context.Store.Id, context.Customer.Id, null, null, null, null, null, null, null, null, null); + return query; + } + + protected override IEnumerable GetValues(CartRuleContext context) + { + // Fast batch loading of payment methods. + var paymentMethods = new HashSet(StringComparer.InvariantCultureIgnoreCase); + var take = 4000; + var query = GetQuery(context); + var maxId = query.Max(x => (int?)x.Id) ?? 0; + + for (var lastId = 0; lastId < maxId;) + { + var batchQuery = query + .OrderBy(x => x.Id) + .Select(x => new { x.Id, x.PaymentMethodSystemName }); + + if (lastId > 0) + { + batchQuery = batchQuery.Where(x => x.Id > lastId); + } + + batchQuery = batchQuery.Take(() => take); + var batch = batchQuery.ToList(); + + paymentMethods.AddRange(batch.Select(x => x.PaymentMethodSystemName)); + lastId = batch.Last().Id; + } + + return paymentMethods; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/PaymentMethodRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/PaymentMethodRule.cs new file mode 100644 index 0000000000..786bca68b7 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/PaymentMethodRule.cs @@ -0,0 +1,21 @@ +using System; +using SmartStore.Core.Domain.Customers; +using SmartStore.Services.Common; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class PaymentMethodRule : ListRuleBase + { + public PaymentMethodRule() + : base(StringComparer.InvariantCultureIgnoreCase) + { + } + + protected override string GetValue(CartRuleContext context) + { + var paymentMethod = context.Customer.GetAttribute(SystemCustomerAttributeNames.SelectedPaymentMethod, context.Store.Id); + + return paymentMethod.NullEmpty(); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductFromCategoryInCartRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductFromCategoryInCartRule.cs new file mode 100644 index 0000000000..1c9b82479f --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductFromCategoryInCartRule.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Linq; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Orders; +using SmartStore.Services.Orders; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class ProductFromCategoryInCartRule : ListRuleBase + { + private readonly IRepository _productCategoryRepository; + private readonly IShoppingCartService _shoppingCartService; + + public ProductFromCategoryInCartRule( + IRepository productCategoryRepository, + IShoppingCartService shoppingCartService) + { + _productCategoryRepository = productCategoryRepository; + _shoppingCartService = shoppingCartService; + } + + protected override IEnumerable GetValues(CartRuleContext context) + { + var cart = _shoppingCartService.GetCartItems(context.Customer, ShoppingCartType.ShoppingCart, context.Store.Id); + var productIds = cart.Select(x => x.Item.ProductId).ToArray(); + + if (productIds.Any()) + { + // It's unnecessary to check things like ACL, limited-to-stores, published, deleted etc. here + // because the products are from shopping cart and it cannot contain hidden products. + var categoryIds = _productCategoryRepository.TableUntracked + .Where(x => productIds.Contains(x.ProductId)) + .Select(x => x.CategoryId) + .ToList(); + + return categoryIds.Distinct(); + } + + return Enumerable.Empty(); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductFromManufacturerInCartRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductFromManufacturerInCartRule.cs new file mode 100644 index 0000000000..268d21fa0e --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductFromManufacturerInCartRule.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Linq; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Orders; +using SmartStore.Services.Orders; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class ProductFromManufacturerInCartRule : ListRuleBase + { + private readonly IRepository _productManufacturerRepository; + private readonly IShoppingCartService _shoppingCartService; + + public ProductFromManufacturerInCartRule( + IRepository productManufacturerRepository, + IShoppingCartService shoppingCartService) + { + _productManufacturerRepository = productManufacturerRepository; + _shoppingCartService = shoppingCartService; + } + + protected override IEnumerable GetValues(CartRuleContext context) + { + var cart = _shoppingCartService.GetCartItems(context.Customer, ShoppingCartType.ShoppingCart, context.Store.Id); + var productIds = cart.Select(x => x.Item.ProductId).ToArray(); + + if (productIds.Any()) + { + // It's unnecessary to check things like ACL, limited-to-stores, published, deleted etc. here + // because the products are from shopping cart and it cannot contain hidden products. + var manufacturerIds = _productManufacturerRepository.TableUntracked + .Where(x => productIds.Contains(x.ProductId)) + .Select(x => x.ManufacturerId) + .ToList(); + + return manufacturerIds.Distinct(); + } + + return Enumerable.Empty(); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductInCartRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductInCartRule.cs new file mode 100644 index 0000000000..b6671644bf --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductInCartRule.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Linq; +using SmartStore.Core.Domain.Orders; +using SmartStore.Services.Orders; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class ProductInCartRule : ListRuleBase + { + private readonly IShoppingCartService _shoppingCartService; + + public ProductInCartRule(IShoppingCartService shoppingCartService) + { + _shoppingCartService = shoppingCartService; + } + + protected override IEnumerable GetValues(CartRuleContext context) + { + var cart = _shoppingCartService.GetCartItems(context.Customer, ShoppingCartType.ShoppingCart, context.Store.Id); + + return cart.Select(x => x.Item.ProductId); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductOnWishlistRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductOnWishlistRule.cs new file mode 100644 index 0000000000..c16b844340 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductOnWishlistRule.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.Linq; +using SmartStore.Core.Domain.Orders; +using SmartStore.Services.Orders; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class ProductOnWishlistRule : ListRuleBase + { + private readonly IShoppingCartService _shoppingCartService; + + public ProductOnWishlistRule(IShoppingCartService shoppingCartService) + { + _shoppingCartService = shoppingCartService; + } + + protected override IEnumerable GetValues(CartRuleContext context) + { + var wishlistProductIds = _shoppingCartService.GetCartItems(context.Customer, ShoppingCartType.Wishlist, context.Store.Id) + .Select(x => x.Item.ProductId); + + return wishlistProductIds; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductReviewCountRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductReviewCountRule.cs new file mode 100644 index 0000000000..cdf8c9b12e --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ProductReviewCountRule.cs @@ -0,0 +1,25 @@ +using System.Linq; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Rules; +using SmartStore.Services.Customers; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class ProductReviewCountRule : IRule + { + private readonly ICustomerContentService _customerContentService; + + public ProductReviewCountRule(ICustomerContentService customerContentService) + { + _customerContentService = customerContentService; + } + + public bool Match(CartRuleContext context, RuleExpression expression) + { + var query = _customerContentService.GetAllCustomerContent(context.Customer.Id, true).SourceQuery; + var reviewsCount = query.Count(); + + return expression.Operator.Match(reviewsCount, expression.Value); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/PurchasedFromManufacturerRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/PurchasedFromManufacturerRule.cs new file mode 100644 index 0000000000..a4f14359d7 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/PurchasedFromManufacturerRule.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using System.Data.Entity; +using System.Linq; +using SmartStore.Core.Domain.Orders; +using SmartStore.Rules; +using SmartStore.Services.Orders; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class PurchasedFromManufacturerRule : ListRuleBase + { + private readonly IOrderService _orderService; + + public PurchasedFromManufacturerRule(IOrderService orderService) + { + _orderService = orderService; + } + + public override bool Match(CartRuleContext context, RuleExpression expression) + { + if (expression.Operator == RuleOperator.In || expression.Operator == RuleOperator.NotIn) + { + // Get result using LINQ to Entities. + var manuIds = expression.Value as List; + if (!(manuIds?.Any() ?? false)) + { + return true; + } + + var query = GetQuery(context); + + if (expression.Operator == RuleOperator.In) + { + return query.Where(oi => oi.Product.ProductManufacturers.Any(pm => manuIds.Contains(pm.ManufacturerId))).Any(); + } + + return query.Where(oi => oi.Product.ProductManufacturers.Any(pm => !manuIds.Contains(pm.ManufacturerId))).Any(); + } + + // Get result using LINQ to Objects. + return base.Match(context, expression); + } + + protected virtual IQueryable GetQuery(CartRuleContext context) + { + var query = _orderService.GetOrders(context.Store.Id, context.Customer.Id, null, null, null, null, null, null, null, null, null) + .SelectMany(o => o.OrderItems); + + return query; + } + + protected override IEnumerable GetValues(CartRuleContext context) + { + // Fast batch loading of manufacturer IDs. + var manuIds = new HashSet(); + var take = 4000; + var query = GetQuery(context); + var maxId = query.Max(x => (int?)x.Id) ?? 0; + + for (var lastId = 0; lastId < maxId;) + { + var batchQuery = query + .OrderBy(x => x.Id) + .Select(x => new { x.Id, ManufacturerIds = x.Product.ProductManufacturers.Select(pm => pm.ManufacturerId) }); + + if (lastId > 0) + { + batchQuery = batchQuery.Where(x => x.Id > lastId); + } + + batchQuery = batchQuery.Take(() => take); + var batch = batchQuery.ToList(); + + manuIds.AddRange(batch.SelectMany(x => x.ManufacturerIds)); + lastId = batch.Last().Id; + } + + return manuIds; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/PurchasedProductRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/PurchasedProductRule.cs new file mode 100644 index 0000000000..379fa39e69 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/PurchasedProductRule.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using System.Data.Entity; +using System.Linq; +using SmartStore.Core.Domain.Orders; +using SmartStore.Rules; +using SmartStore.Services.Orders; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class PurchasedProductRule : ListRuleBase + { + protected readonly IOrderService _orderService; + + public PurchasedProductRule(IOrderService orderService) + { + _orderService = orderService; + } + + public override bool Match(CartRuleContext context, RuleExpression expression) + { + if (expression.Operator == RuleOperator.In || expression.Operator == RuleOperator.NotIn) + { + // Get result using LINQ to Entities. + var productIds = expression.Value as List; + if (!(productIds?.Any() ?? false)) + { + return true; + } + + var query = GetQuery(context); + + if (expression.Operator == RuleOperator.In) + { + return query.Where(oi => productIds.Contains(oi.ProductId)).Any(); + } + + return query.Where(oi => !productIds.Contains(oi.ProductId)).Any(); + } + + // Get result using LINQ to Objects. + return base.Match(context, expression); + } + + protected virtual IQueryable GetQuery(CartRuleContext context) + { + var query = _orderService.GetOrders(context.Store.Id, context.Customer.Id, null, null, null, null, null, null, null, null, null) + .SelectMany(o => o.OrderItems); + + return query; + } + + protected override IEnumerable GetValues(CartRuleContext context) + { + // Fast batch loading of product IDs. + var productIds = new HashSet(); + var take = 4000; + var query = GetQuery(context); + var maxId = query.Max(x => (int?)x.Id) ?? 0; + + for (var lastId = 0; lastId < maxId;) + { + var batchQuery = query + .OrderBy(x => x.Id) + .Select(x => new { x.Id, x.ProductId }); + + if (lastId > 0) + { + batchQuery = batchQuery.Where(x => x.Id > lastId); + } + + batchQuery = batchQuery.Take(() => take); + var batch = batchQuery.ToList(); + + productIds.AddRange(batch.Select(x => x.ProductId)); + lastId = batch.Last().Id; + } + + return productIds; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/RewardPointsBalanceRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/RewardPointsBalanceRule.cs new file mode 100644 index 0000000000..023a37c113 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/RewardPointsBalanceRule.cs @@ -0,0 +1,14 @@ +using SmartStore.Rules; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class RewardPointsBalanceRule : IRule + { + public bool Match(CartRuleContext context, RuleExpression expression) + { + var rewardPointsBalance = context.Customer.GetRewardPointsBalance(); + + return expression.Operator.Match(rewardPointsBalance, expression.Value); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/RuleSetRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/RuleSetRule.cs index b859c2f333..1fbcb14f6a 100644 --- a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/RuleSetRule.cs +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/RuleSetRule.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using SmartStore.Rules; +using SmartStore.Rules; namespace SmartStore.Services.Cart.Rules.Impl { @@ -29,10 +24,13 @@ public bool Match(CartRuleContext context, RuleExpression expression) { var otherExpression = GetOtherExpression(expression); if (otherExpression == null) - return false; // TBD: really?! + { + // Skip\ignore expression. + return true; + } var otherRule = _cartRuleProvider.GetProcessor(otherExpression); - var otherMatch = otherRule.Match(context, otherExpression); + //var otherMatch = otherRule.Match(context, otherExpression); //return expression.Operator.Match(otherMatch, true); diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ShippingCountryRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ShippingCountryRule.cs new file mode 100644 index 0000000000..a297d56359 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ShippingCountryRule.cs @@ -0,0 +1,10 @@ +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class ShippingCountryRule : ListRuleBase + { + protected override int GetValue(CartRuleContext context) + { + return context.Customer?.ShippingAddress?.CountryId ?? 0; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ShippingMethodRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ShippingMethodRule.cs new file mode 100644 index 0000000000..52b053b27d --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/ShippingMethodRule.cs @@ -0,0 +1,16 @@ +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.Shipping; +using SmartStore.Services.Common; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class ShippingMethodRule : ListRuleBase + { + protected override int GetValue(CartRuleContext context) + { + var shippingMethod = context.Customer.GetAttribute(SystemCustomerAttributeNames.SelectedShippingOption, context.Store.Id); + + return shippingMethod?.ShippingMethodId ?? 0; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/SpentAmountRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/SpentAmountRule.cs new file mode 100644 index 0000000000..68b56202c5 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/SpentAmountRule.cs @@ -0,0 +1,28 @@ +using System.Linq; +using SmartStore.Core.Domain.Orders; +using SmartStore.Rules; +using SmartStore.Services.Orders; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class SpentAmountRule : IRule + { + private readonly IOrderService _orderService; + + public SpentAmountRule(IOrderService orderService) + { + _orderService = orderService; + } + + public bool Match(CartRuleContext context, RuleExpression expression) + { + var query = _orderService.GetOrders(context.Store.Id, context.Customer.Id, null, null, new int[] { (int)OrderStatus.Complete }, null, null, null, null, null); + var spentAmount = query.Sum(x => (decimal?)x.OrderTotal) ?? decimal.Zero; + + var money = new Money(spentAmount, context.WorkContext.WorkingCurrency); + spentAmount = money.RoundedAmount; + + return expression.Operator.Match(spentAmount, expression.Value); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/StoreRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/StoreRule.cs index e3f817a2a4..bf55aeb3df 100644 --- a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/StoreRule.cs +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/StoreRule.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SmartStore.Services.Cart.Rules.Impl +namespace SmartStore.Services.Cart.Rules.Impl { public class StoreRule : ListRuleBase { diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/BrowserMajorVersionRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/BrowserMajorVersionRule.cs new file mode 100644 index 0000000000..4d5de2e3d4 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/BrowserMajorVersionRule.cs @@ -0,0 +1,25 @@ +using SmartStore.Rules; +using SmartStore.Services.Common; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class BrowserMajorVersionRule : IRule + { + private readonly IUserAgent _userAgent; + + public BrowserMajorVersionRule(IUserAgent userAgent) + { + _userAgent = userAgent; + } + + public bool Match(CartRuleContext context, RuleExpression expression) + { + if (_userAgent.UserAgent.Major.HasValue() && int.TryParse(_userAgent.UserAgent.Major, out var majorVersion)) + { + return expression.Operator.Match(majorVersion, expression.Value); + } + + return false; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/BrowserMinorVersionRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/BrowserMinorVersionRule.cs new file mode 100644 index 0000000000..79cb6d3acd --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/BrowserMinorVersionRule.cs @@ -0,0 +1,25 @@ +using SmartStore.Rules; +using SmartStore.Services.Common; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class BrowserMinorVersionRule : IRule + { + private readonly IUserAgent _userAgent; + + public BrowserMinorVersionRule(IUserAgent userAgent) + { + _userAgent = userAgent; + } + + public bool Match(CartRuleContext context, RuleExpression expression) + { + if (_userAgent.UserAgent.Minor.HasValue() && int.TryParse(_userAgent.UserAgent.Minor, out var minorVersion)) + { + return expression.Operator.Match(minorVersion, expression.Value); + } + + return false; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/BrowserRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/BrowserRule.cs new file mode 100644 index 0000000000..eb29c02821 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/BrowserRule.cs @@ -0,0 +1,43 @@ +using System.Linq; +using SmartStore.Rules; +using SmartStore.Services.Common; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class BrowserRule : ListRuleBase + { + private readonly IUserAgent _userAgent; + + public BrowserRule(IUserAgent userAgent) + { + _userAgent = userAgent; + } + + public static RuleValueSelectListOption[] GetDefaultValues() + { + return new[] + { + "Chrome", + "Chrome Mobile", + "Edge", + "Firefox", + "Firefox Mobile", + "IE", + "IE Mobile", + "Mobile Safari", + "Opera", + "Opera Mobile", + "Opera Mini", + "Safari", + "Samsung Internet" + } + .Select(x => new RuleValueSelectListOption { Value = x, Text = x }) + .ToArray(); + } + + protected override string GetValue(CartRuleContext context) + { + return _userAgent.UserAgent.Family.NullEmpty(); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/DeviceRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/DeviceRule.cs new file mode 100644 index 0000000000..b82f61c452 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/DeviceRule.cs @@ -0,0 +1,46 @@ +using System.Linq; +using SmartStore.Rules; +using SmartStore.Services.Common; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class DeviceRule : ListRuleBase + { + private readonly IUserAgent _userAgent; + + public DeviceRule(IUserAgent userAgent) + { + _userAgent = userAgent; + } + + public static RuleValueSelectListOption[] GetDefaultValues() + { + return new[] + { + "BlackBerry", + "Generic Feature Phone", + "Generic Smartphone", + "Generic Tablet", + "HP TouchPad", + "iPad", + "iPhone", + "iPod", + "Kindle", + "Kindle Fire", + "Lumia", + "Motorola", + "Nokia", + "Palm", + "Spider", + "Other" + } + .Select(x => new RuleValueSelectListOption { Value = x, Text = x }) + .ToArray(); + } + + protected override string GetValue(CartRuleContext context) + { + return _userAgent.Device.Family.NullEmpty(); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/IsMobileRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/IsMobileRule.cs new file mode 100644 index 0000000000..e9c217216b --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/IsMobileRule.cs @@ -0,0 +1,20 @@ +using SmartStore.Rules; +using SmartStore.Services.Common; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class IsMobileRule : IRule + { + private readonly IUserAgent _userAgent; + + public IsMobileRule(IUserAgent userAgent) + { + _userAgent = userAgent; + } + + public bool Match(CartRuleContext context, RuleExpression expression) + { + return expression.Operator.Match(_userAgent.IsMobileDevice, expression.Value); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/OSRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/OSRule.cs new file mode 100644 index 0000000000..5fe72b37ad --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/UserAgent/OSRule.cs @@ -0,0 +1,46 @@ +using System.Linq; +using SmartStore.Rules; +using SmartStore.Services.Common; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class OSRule : ListRuleBase + { + private readonly IUserAgent _userAgent; + + public OSRule(IUserAgent userAgent) + { + _userAgent = userAgent; + } + + public static RuleValueSelectListOption[] GetDefaultValues() + { + return new[] + { + "Android", + "BlackBerry OS", + "BlackBerry Tablet OS", + "Chrome OS", + "Firefox OS", + "iOS", + "Kindle", + "Linux", + "Mac OS X", + "Symbian OS", + "Ubuntu", + "webOS", + "Windows", + "Windows Mobile", + "Windows Phone", + "Windows CE" + } + .Select(x => new RuleValueSelectListOption { Value = x, Text = x }) + .ToArray(); + } + + protected override string GetValue(CartRuleContext context) + { + return _userAgent.OS.Family.NullEmpty(); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/Impl/WeekdayRule.cs b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/WeekdayRule.cs new file mode 100644 index 0000000000..0cce47603d --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/Rules/Impl/WeekdayRule.cs @@ -0,0 +1,36 @@ +using System; +using System.Globalization; +using System.Linq; +using SmartStore.Core.Domain.Localization; +using SmartStore.Rules; + +namespace SmartStore.Services.Cart.Rules.Impl +{ + public class WeekdayRule : ListRuleBase + { + public static RuleValueSelectListOption[] GetDefaultValues(Language language) + { + CultureInfo cultureInfo = null; + + try + { + cultureInfo = CultureInfo.GetCultureInfo(language.LanguageCulture); + } + catch { } + + var dtif = cultureInfo?.DateTimeFormat ?? DateTimeFormatInfo.InvariantInfo; + + var options = Enum.GetValues(typeof(DayOfWeek)) + .Cast() + .Select(x => new RuleValueSelectListOption { Value = ((int)x).ToString(), Text = dtif.GetDayName(x) }) + .ToArray(); + + return options; + } + + protected override int GetValue(CartRuleContext context) + { + return (int)DateTime.Now.DayOfWeek; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Cart/Rules/ListRuleBase.cs b/src/Libraries/SmartStore.Services/Cart/Rules/ListRuleBase.cs index 75de53ef72..93c301ee8c 100644 --- a/src/Libraries/SmartStore.Services/Cart/Rules/ListRuleBase.cs +++ b/src/Libraries/SmartStore.Services/Cart/Rules/ListRuleBase.cs @@ -1,38 +1,92 @@ -using System; -using System.Collections; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using SmartStore.Rules; namespace SmartStore.Services.Cart.Rules { public abstract class ListRuleBase : IRule { - protected abstract T GetValue(CartRuleContext context); + protected readonly IEqualityComparer _comparer; - public bool Match(CartRuleContext context, RuleExpression expression) + protected ListRuleBase(IEqualityComparer comparer = null) { - var list = expression.Value as List; - if (list == null || list.Count == 0) + _comparer = comparer; + } + + protected virtual T GetValue(CartRuleContext context) + { + return default; + } + + protected virtual IEnumerable GetValues(CartRuleContext context) + { + return default; + } + + public virtual bool Match(CartRuleContext context, RuleExpression expression) + { + var right = expression.Value as List; + if (!(right?.Any() ?? false)) { return true; } - var value = GetValue(context); + var left = GetValues(context); - if (object.Equals(value, default(T))) - { - return false; - } - if (expression.Operator == RuleOperator.In) + if (left == null) { - return list.Contains(value); + var value = GetValue(context); + + if (object.Equals(value, default(T))) + { + return false; + } + + if (expression.Operator == RuleOperator.In) + { + return right.Contains(value, _comparer); + } + if (expression.Operator == RuleOperator.NotIn) + { + return !right.Contains(value, _comparer); + } } - if (expression.Operator == RuleOperator.NotIn) + else { - return !list.Contains(value); + if (expression.Operator == RuleOperator.IsEqualTo) + { + return !right.Except(left, _comparer).Any(); + } + else if (expression.Operator == RuleOperator.IsNotEqualTo) + { + return right.Except(left, _comparer).Any(); + } + else if (expression.Operator == RuleOperator.Contains) + { + // FALSE for left { 3,2,1 } and right { 0,1,2,3 }. + return right.All(x => left.Contains(x, _comparer)); + } + else if (expression.Operator == RuleOperator.NotContains) + { + return right.All(x => !left.Contains(x, _comparer)); + } + else if (expression.Operator == RuleOperator.In) + { + return left.Any(x => right.Contains(x, _comparer)); + } + else if (expression.Operator == RuleOperator.NotIn) + { + return left.Any(x => !right.Contains(x, _comparer)); + } + else if (expression.Operator == RuleOperator.AllIn) + { + // TRUE for left { 3,2,1 } and right { 0,1,2,3 }. + return left.All(x => right.Contains(x, _comparer)); + } + else if (expression.Operator == RuleOperator.NotAllIn) + { + return left.All(x => !right.Contains(x, _comparer)); + } } throw new InvalidRuleOperatorException(expression); diff --git a/src/Libraries/SmartStore.Services/Cart/ValidatingCartEvent.cs b/src/Libraries/SmartStore.Services/Cart/ValidatingCartEvent.cs new file mode 100644 index 0000000000..b17f20784b --- /dev/null +++ b/src/Libraries/SmartStore.Services/Cart/ValidatingCartEvent.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Web.Mvc; +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.Orders; + +namespace SmartStore.Services.Cart +{ + public class ValidatingCartEvent + { + public ValidatingCartEvent( + Customer customer, + IList warnings, + IList cart) + { + Customer = customer; + Warnings = warnings; + Cart = cart; + } + + public Customer Customer { get; set; } + + public IList Cart { get; set; } + + public IList Warnings { get; set; } + + public ActionResult Result { get; set; } + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Catalog/AdjustInventoryResult.cs b/src/Libraries/SmartStore.Services/Catalog/AdjustInventoryResult.cs index 91867ebe55..aac4efb6ea 100644 --- a/src/Libraries/SmartStore.Services/Catalog/AdjustInventoryResult.cs +++ b/src/Libraries/SmartStore.Services/Catalog/AdjustInventoryResult.cs @@ -1,50 +1,32 @@  namespace SmartStore.Services.Catalog { - public class AdjustInventoryResult - { - private int? _stockQuantityOld; - private int? _stockQuantityNew; + public class AdjustInventoryResult + { + private int? _stockQuantityOld; + private int? _stockQuantityNew; - /// - /// The stock quantity before adjustment - /// - public int StockQuantityOld - { - get - { - return _stockQuantityOld ?? 0; - } - set - { - _stockQuantityOld = value; - } - } + /// + /// The stock quantity before adjustment + /// + public int StockQuantityOld + { + get => _stockQuantityOld ?? 0; + set => _stockQuantityOld = value; + } - /// - /// The stock quantity after adjustment - /// - public int StockQuantityNew - { - get - { - return _stockQuantityNew ?? 0; - } - set - { - _stockQuantityNew = value; - } - } + /// + /// The stock quantity after adjustment + /// + public int StockQuantityNew + { + get => _stockQuantityNew ?? 0; + set => _stockQuantityNew = value; + } - /// - /// Determines whether the adjustment resulted in a clear, unique stock quantity update. For instance false for bundle products. - /// - public bool HasClearStockQuantityResult - { - get - { - return _stockQuantityOld.HasValue && _stockQuantityNew.HasValue; - } - } - } + /// + /// Determines whether the adjustment resulted in a clear, unique stock quantity update. For instance false for bundle products. + /// + public bool HasClearStockQuantityResult => _stockQuantityOld.HasValue && _stockQuantityNew.HasValue; + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/BackInStockSubscriptionService.cs b/src/Libraries/SmartStore.Services/Catalog/BackInStockSubscriptionService.cs index 38a945b869..53feaaacaa 100644 --- a/src/Libraries/SmartStore.Services/Catalog/BackInStockSubscriptionService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/BackInStockSubscriptionService.cs @@ -19,22 +19,22 @@ public partial class BackInStockSubscriptionService : IBackInStockSubscriptionSe private readonly IRepository _backInStockSubscriptionRepository; private readonly IMessageFactory _messageFactory; - private readonly IWorkContext _workContext; + private readonly IWorkContext _workContext; private readonly IEventPublisher _eventPublisher; #endregion - + #region Ctor public BackInStockSubscriptionService( - IRepository backInStockSubscriptionRepository, - IMessageFactory messageFactory, - IWorkContext workContext, + IRepository backInStockSubscriptionRepository, + IMessageFactory messageFactory, + IWorkContext workContext, IEventPublisher eventPublisher) { _backInStockSubscriptionRepository = backInStockSubscriptionRepository; _messageFactory = messageFactory; - _workContext = workContext; + _workContext = workContext; _eventPublisher = eventPublisher; } @@ -67,9 +67,9 @@ public virtual IPagedList GetAllSubscriptionsByCustomer var query = _backInStockSubscriptionRepository.Table; //customer query = query.Where(biss => biss.CustomerId == customerId); - //store - if (storeId > 0) - query = query.Where(biss => biss.StoreId == storeId); + //store + if (storeId > 0) + query = query.Where(biss => biss.StoreId == storeId); //product query = query.Where(biss => !biss.Product.Deleted); query = query.OrderByDescending(biss => biss.CreatedOnUtc); @@ -86,14 +86,14 @@ public virtual IPagedList GetAllSubscriptionsByCustomer /// Page size /// Subscriptions public virtual IPagedList GetAllSubscriptionsByProductId(int productId, - int storeId, int pageIndex, int pageSize) + int storeId, int pageIndex, int pageSize) { var query = _backInStockSubscriptionRepository.Table; //product query = query.Where(biss => biss.ProductId == productId); - //store - if (storeId > 0) - query = query.Where(biss => biss.StoreId == storeId); + //store + if (storeId > 0) + query = query.Where(biss => biss.StoreId == storeId); //customer query = query.Where(biss => !biss.Customer.Deleted); query = query.OrderByDescending(biss => biss.CreatedOnUtc); @@ -109,11 +109,11 @@ public virtual IPagedList GetAllSubscriptionsByProductI /// Subscriptions public virtual BackInStockSubscription FindSubscription(int customerId, int productId, int storeId) { - var query = - from biss in _backInStockSubscriptionRepository.Table - orderby biss.CreatedOnUtc descending - where biss.CustomerId == customerId && biss.ProductId == productId && biss.StoreId == storeId - select biss; + var query = + from biss in _backInStockSubscriptionRepository.Table + orderby biss.CreatedOnUtc descending + where biss.CustomerId == customerId && biss.ProductId == productId && biss.StoreId == storeId + select biss; var subscription = query.FirstOrDefault(); return subscription; @@ -164,17 +164,17 @@ public virtual void UpdateSubscription(BackInStockSubscription subscription) /// Number of sent email public virtual int SendNotificationsToSubscribers(Product product) { - if (product == null) - throw new ArgumentNullException("product"); + if (product == null) + throw new ArgumentNullException("product"); int result = 0; - var subscriptions = GetAllSubscriptionsByProductId(product.Id, 0, 0, int.MaxValue); + var subscriptions = GetAllSubscriptionsByProductId(product.Id, 0, 0, int.MaxValue); foreach (var subscription in subscriptions) { // Ensure that customer is registered (simple and fast way) - if (subscription.Customer.Email.IsEmail()) + if (subscription.Customer.Email.IsEmail()) { - _messageFactory.SendBackInStockNotification(subscription); + _messageFactory.SendBackInStockNotification(subscription); result++; } } @@ -182,7 +182,7 @@ public virtual int SendNotificationsToSubscribers(Product product) DeleteSubscription(subscriptions[i]); return result; } - + #endregion } } diff --git a/src/Libraries/SmartStore.Services/Catalog/CatalogMessageFactoryExtensions.cs b/src/Libraries/SmartStore.Services/Catalog/CatalogMessageFactoryExtensions.cs index cb0fb7fb1d..9fa9ee35e3 100644 --- a/src/Libraries/SmartStore.Services/Catalog/CatalogMessageFactoryExtensions.cs +++ b/src/Libraries/SmartStore.Services/Catalog/CatalogMessageFactoryExtensions.cs @@ -7,73 +7,77 @@ namespace SmartStore.Services.Catalog { - public static class CatalogMessageFactoryExtensions - { - /// - /// Sends "email a friend" message - /// - public static CreateMessageResult SendShareProductMessage(this IMessageFactory factory, Customer customer, Product product, - string fromEmail, string toEmail, string personalMessage, int languageId = 0) - { - Guard.NotNull(customer, nameof(customer)); - Guard.NotNull(product, nameof(product)); + public static class CatalogMessageFactoryExtensions + { + /// + /// Sends "email a friend" message + /// + public static CreateMessageResult SendShareProductMessage(this IMessageFactory factory, Customer customer, Product product, + string fromEmail, string toEmail, string personalMessage, int languageId = 0) + { + Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(product, nameof(product)); - var model = new NamedModelPart("Message") - { - ["Body"] = personalMessage.NullEmpty(), - ["From"] = fromEmail.NullEmpty(), - ["To"] = toEmail.NullEmpty() - }; + var model = new NamedModelPart("Message") + { + ["Body"] = personalMessage.NullEmpty(), + ["From"] = fromEmail.NullEmpty(), + ["To"] = toEmail.NullEmpty() + }; - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.ShareProduct, languageId, customer: customer), true, product, model); - } + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.ShareProduct, languageId, customer: customer), true, product, model); + } public static CreateMessageResult SendProductQuestionMessage(this IMessageFactory factory, Customer customer, Product product, - string senderEmail, string senderName, string senderPhone, string question, int languageId = 0) + string senderEmail, string senderName, string senderPhone, string question, + string attributes, string productUrl, bool isQuoteRequest, int languageId = 0) { Guard.NotNull(customer, nameof(customer)); Guard.NotNull(product, nameof(product)); var model = new NamedModelPart("Message") { + ["ProductUrl"] = productUrl.NullEmpty(), + ["IsQuoteRequest"] = isQuoteRequest, + ["ProductAttributes"] = attributes.NullEmpty(), ["Message"] = question.NullEmpty(), ["SenderEmail"] = senderEmail.NullEmpty(), ["SenderName"] = senderName.NullEmpty(), ["SenderPhone"] = senderPhone.NullEmpty() }; - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.ProductQuestion, languageId, customer: customer), true, product, model); - } + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.ProductQuestion, languageId, customer: customer), true, product, model); + } - /// - /// Sends a product review notification message to a store owner - /// - public static CreateMessageResult SendProductReviewNotificationMessage(this IMessageFactory factory, ProductReview productReview, int languageId = 0) - { - Guard.NotNull(productReview, nameof(productReview)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.ProductReviewStoreOwner, languageId, customer: productReview.Customer), true, productReview, productReview.Product); - } + /// + /// Sends a product review notification message to a store owner + /// + public static CreateMessageResult SendProductReviewNotificationMessage(this IMessageFactory factory, ProductReview productReview, int languageId = 0) + { + Guard.NotNull(productReview, nameof(productReview)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.ProductReviewStoreOwner, languageId, customer: productReview.Customer), true, productReview, productReview.Product); + } - /// - /// Sends a "quantity below" notification to a store owner - /// - public static CreateMessageResult SendQuantityBelowStoreOwnerNotification(this IMessageFactory factory, Product product, int languageId = 0) - { - Guard.NotNull(product, nameof(product)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.QuantityBelowStoreOwner, languageId), true, product); - } + /// + /// Sends a "quantity below" notification to a store owner + /// + public static CreateMessageResult SendQuantityBelowStoreOwnerNotification(this IMessageFactory factory, Product product, int languageId = 0) + { + Guard.NotNull(product, nameof(product)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.QuantityBelowStoreOwner, languageId), true, product); + } - /// - /// Sends a 'Back in stock' notification message to a customer - /// - public static CreateMessageResult SendBackInStockNotification(this IMessageFactory factory, BackInStockSubscription subscription) - { - Guard.NotNull(subscription, nameof(subscription)); + /// + /// Sends a 'Back in stock' notification message to a customer + /// + public static CreateMessageResult SendBackInStockNotification(this IMessageFactory factory, BackInStockSubscription subscription) + { + Guard.NotNull(subscription, nameof(subscription)); - var customer = subscription.Customer; + var customer = subscription.Customer; var languageId = customer.GetAttribute(SystemCustomerAttributeNames.LanguageId); return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.BackInStockCustomer, languageId, subscription.StoreId, customer), true, subscription.Product); - } - } + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/CategoryExtensions.cs b/src/Libraries/SmartStore.Services/Catalog/CategoryExtensions.cs index f5ce3b7ece..b62bb38a57 100644 --- a/src/Libraries/SmartStore.Services/Catalog/CategoryExtensions.cs +++ b/src/Libraries/SmartStore.Services/Catalog/CategoryExtensions.cs @@ -18,36 +18,109 @@ public static class CategoryExtensions /// /// Source /// Parent category identifier - /// A value indicating whether categories without parent category in provided category list (source) should be ignored + /// A value indicating whether categories without existing parent category in provided category list (source) should be ignored /// Sorted categories - public static IList SortCategoryNodesForTree(this IEnumerable source, int parentId = 0, bool ignoreCategoriesWithoutExistingParent = false) - where T : ICategoryNode + public static IEnumerable SortCategoryNodesForTree(this IEnumerable source, int parentId = 0, bool ignoreDetachedCategories = false) + where T : ICategoryNode { - Guard.NotNull(source, nameof(source)); + Guard.NotNull(source, nameof(source)); - var result = new List(); - var sourceCount = source.Count(); + var sourceCount = source.Count(); + var result = new List(sourceCount); + var lookup = source.ToLookup(x => x.ParentCategoryId); + + result.AddRange(SortCategoryNodesForTreeInternal(parentId, lookup)); + + if (!ignoreDetachedCategories && result.Count != sourceCount) + { + // Find categories without parent in provided category source and insert them into result + var resultLookup = result.ToDictionarySafe(x => x.Id); + foreach (var cat in source) + { + if (!resultLookup.ContainsKey(cat.Id)) + { + result.Add(cat); + } + } + } + + return result; + } + + private static IEnumerable SortCategoryNodesForTreeInternal(int parentId, ILookup lookup) + where T : ICategoryNode + { + if (!lookup.Contains(parentId)) + { + return Enumerable.Empty(); + } - var childNodes = source.Where(c => c.ParentCategoryId == parentId).ToArray(); + var childNodes = lookup[parentId]; + var result = new List(); foreach (var node in childNodes) { result.Add(node); - result.AddRange(SortCategoryNodesForTree(source, node.Id, true)); + result.AddRange(SortCategoryNodesForTreeInternal(node.Id, lookup)); } - if (!ignoreCategoriesWithoutExistingParent && result.Count != sourceCount) + return result; + } + + class CategoryNodeSorter + where T : ICategoryNode + { + private readonly IEnumerable _source; + private readonly int _parentId; + private readonly bool _ignoreDetachedCategories; + + public CategoryNodeSorter(IEnumerable source, int parentId = 0, bool ignoreDetachedCategories = false) { - // Find categories without parent in provided category source and insert them into result - foreach (var cat in source) - { - if (result.Where(x => x.Id == cat.Id).FirstOrDefault() == null) - { - result.Add(cat); - } - } + _source = source; + _parentId = parentId; + _ignoreDetachedCategories = ignoreDetachedCategories; } - return result; + public IEnumerable Sort() + { + var sourceCount = _source.Count(); + var result = new List(sourceCount); + var lookup = _source.ToLookup(x => x.ParentCategoryId); + + result.AddRange(SortInternal(_parentId, lookup)); + + if (!_ignoreDetachedCategories && result.Count != sourceCount) + { + // Find categories without parent in provided category source and insert them into result + var resultLookup = result.ToDictionarySafe(x => x.Id); + foreach (var cat in _source) + { + if (!resultLookup.ContainsKey(cat.Id)) + { + result.Add(cat); + } + } + } + + return result; + } + + private IEnumerable SortInternal(int parentId, ILookup lookup) + { + if (!lookup.Contains(parentId)) + { + return Enumerable.Empty(); + } + + var childNodes = lookup[parentId]; + var result = new List(); + foreach (var node in childNodes) + { + result.Add(node); + result.AddRange(SortInternal(node.Id, lookup)); + } + + return result; + } } /// @@ -59,70 +132,70 @@ public static IList SortCategoryNodesForTree(this IEnumerable source, i /// A ProductCategory that has the specified values; otherwise null public static ProductCategory FindProductCategory(this IList source, int productId, int categoryId) { - foreach (var productCategory in source) - { - if (productCategory.ProductId == productId && productCategory.CategoryId == categoryId) - return productCategory; - } + foreach (var productCategory in source) + { + if (productCategory.ProductId == productId && productCategory.CategoryId == categoryId) + return productCategory; + } return null; } - public static string GetCategoryNameIndented(this TreeNode treeNode, - string indentWith = "--", - int? languageId = null, - bool withAlias = true) - { - Guard.NotNull(treeNode, nameof(treeNode)); - - var sb = new StringBuilder(); - var indentSize = treeNode.Depth - 1; - for (int i = 0; i < indentSize; i++) - { - sb.Append(indentWith); - } - - var cat = treeNode.Value; - - var name = languageId.HasValue - ? cat.GetLocalized(n => n.Name, languageId.Value) - : cat.Name; - - sb.Append(name); - - if (withAlias && cat.Alias.HasValue()) - { - sb.Append(" ("); - sb.Append(cat.Alias); - sb.Append(")"); - } - - return sb.ToString(); - } - - /// - /// Builds a category breadcrumb (path) for a particular category node - /// - /// The category node - /// The id of language. Pass null to skip localization. - /// true appends the category alias - if specified - to the name - /// The separator string - /// Category breadcrumb path - public static string GetCategoryPath(this ICategoryNode categoryNode, - ICategoryService categoryService, - int? languageId = null, - string aliasPattern = null, - string separator = " ") - { - Guard.NotNull(categoryNode, nameof(categoryNode)); - - var treeNode = categoryService.GetCategoryTree(categoryNode.Id, true); - if (treeNode != null) - { - return categoryService.GetCategoryPath(treeNode, languageId, aliasPattern, separator); - } - - return string.Empty; - } - } + public static string GetCategoryNameIndented(this TreeNode treeNode, + string indentWith = "--", + int? languageId = null, + bool withAlias = true) + { + Guard.NotNull(treeNode, nameof(treeNode)); + + var sb = new StringBuilder(); + var indentSize = treeNode.Depth - 1; + for (int i = 0; i < indentSize; i++) + { + sb.Append(indentWith); + } + + var cat = treeNode.Value; + + var name = languageId.HasValue + ? cat.GetLocalized(n => n.Name, languageId.Value) + : cat.Name; + + sb.Append(name); + + if (withAlias && cat.Alias.HasValue()) + { + sb.Append(" ("); + sb.Append(cat.Alias); + sb.Append(")"); + } + + return sb.ToString(); + } + + /// + /// Builds a category breadcrumb (path) for a particular category node + /// + /// The category node + /// The id of language. Pass null to skip localization. + /// true appends the category alias - if specified - to the name + /// The separator string + /// Category breadcrumb path + public static string GetCategoryPath(this ICategoryNode categoryNode, + ICategoryService categoryService, + int? languageId = null, + string aliasPattern = null, + string separator = " ") + { + Guard.NotNull(categoryNode, nameof(categoryNode)); + + var treeNode = categoryService.GetCategoryTree(categoryNode.Id, true); + if (treeNode != null) + { + return categoryService.GetCategoryPath(treeNode, languageId, aliasPattern, separator); + } + + return string.Empty; + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/CategoryService.cs b/src/Libraries/SmartStore.Services/Catalog/CategoryService.cs index f0c104405b..e4f1c20899 100644 --- a/src/Libraries/SmartStore.Services/Catalog/CategoryService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/CategoryService.cs @@ -17,327 +17,365 @@ using SmartStore.Services.Search; using SmartStore.Services.Security; using SmartStore.Services.Stores; +using SmartStore.Utilities.ObjectPools; +using SmartStore.Services.Seo; +using SmartStore.Core.Domain.Seo; +using SmartStore.Data.Utilities; namespace SmartStore.Services.Catalog { - public partial class CategoryService : ICategoryService + public partial class CategoryService : ICategoryService, IXmlSitemapPublisher { - internal static TimeSpan CategoryTreeCacheDuration = TimeSpan.FromHours(6); + internal static TimeSpan CategoryTreeCacheDuration = TimeSpan.FromHours(6); - // {0} = IncludeHidden, {1} = CustomerRoleIds, {2} = StoreId - internal const string CATEGORY_TREE_KEY = "category:tree-{0}-{1}-{2}"; - internal const string CATEGORY_TREE_PATTERN_KEY = "category:tree-*"; + // {0} = IncludeHidden, {1} = CustomerRoleIds, {2} = StoreId + internal const string CATEGORY_TREE_KEY = "category:tree-{0}-{1}-{2}"; + internal const string CATEGORY_TREE_PATTERN_KEY = "category:tree-*"; - private const string CATEGORIES_BY_PARENT_CATEGORY_ID_KEY = "category:byparent-{0}-{1}-{2}-{3}"; - private const string PRODUCTCATEGORIES_ALLBYCATEGORYID_KEY = "productcategory:allbycategoryid-{0}-{1}-{2}-{3}-{4}-{5}"; - private const string PRODUCTCATEGORIES_ALLBYPRODUCTID_KEY = "productcategory:allbyproductid-{0}-{1}-{2}-{3}"; - private const string CATEGORIES_PATTERN_KEY = "category:*"; - private const string PRODUCTCATEGORIES_PATTERN_KEY = "productcategory:*"; + private const string CATEGORIES_BY_PARENT_CATEGORY_ID_KEY = "category:byparent-{0}-{1}-{2}-{3}"; + private const string PRODUCTCATEGORIES_ALLBYCATEGORYID_KEY = "productcategory:allbycategoryid-{0}-{1}-{2}-{3}-{4}-{5}"; + private const string PRODUCTCATEGORIES_ALLBYPRODUCTID_KEY = "productcategory:allbyproductid-{0}-{1}-{2}-{3}"; + private const string CATEGORIES_PATTERN_KEY = "category:*"; + private const string PRODUCTCATEGORIES_PATTERN_KEY = "productcategory:*"; private readonly IRepository _categoryRepository; private readonly IRepository _productCategoryRepository; private readonly IRepository _productRepository; private readonly IRepository _aclRepository; - private readonly IRepository _storeMappingRepository; + private readonly IRepository _storeMappingRepository; private readonly IWorkContext _workContext; - private readonly IStoreContext _storeContext; + private readonly IStoreContext _storeContext; private readonly IEventPublisher _eventPublisher; private readonly IRequestCache _requestCache; - private readonly ICacheManager _cache; - private readonly IStoreMappingService _storeMappingService; - private readonly IAclService _aclService; + private readonly ICacheManager _cache; + private readonly IStoreMappingService _storeMappingService; + private readonly IAclService _aclService; private readonly ICustomerService _customerService; private readonly IStoreService _storeService; - private readonly ICatalogSearchService _catalogSearchService; + private readonly ICatalogSearchService _catalogSearchService; - public CategoryService(IRequestCache requestCache, - ICacheManager cache, - IRepository categoryRepository, + public CategoryService(IRequestCache requestCache, + ICacheManager cache, + IRepository categoryRepository, IRepository productCategoryRepository, IRepository productRepository, IRepository aclRepository, - IRepository storeMappingRepository, + IRepository storeMappingRepository, IWorkContext workContext, - IStoreContext storeContext, + IStoreContext storeContext, IEventPublisher eventPublisher, - IStoreMappingService storeMappingService, - IAclService aclService, + IStoreMappingService storeMappingService, + IAclService aclService, ICustomerService customerService, IStoreService storeService, - ICatalogSearchService catalogSearchService) + ICatalogSearchService catalogSearchService) { _requestCache = requestCache; - _cache = cache; + _cache = cache; _categoryRepository = categoryRepository; _productCategoryRepository = productCategoryRepository; _productRepository = productRepository; _aclRepository = aclRepository; - _storeMappingRepository = storeMappingRepository; + _storeMappingRepository = storeMappingRepository; _workContext = workContext; - _storeContext = storeContext; + _storeContext = storeContext; _eventPublisher = eventPublisher; - _storeMappingService = storeMappingService; - _aclService = aclService; + _storeMappingService = storeMappingService; + _aclService = aclService; _customerService = customerService; _storeService = storeService; - _catalogSearchService = catalogSearchService; + _catalogSearchService = catalogSearchService; - QuerySettings = DbQuerySettings.Default; + QuerySettings = DbQuerySettings.Default; } - public DbQuerySettings QuerySettings { get; set; } - - private void DeleteAllCategories(IList categories, bool delete) - { - foreach (var category in categories) - { - if (delete) - { - category.Deleted = true; - } - else - { - category.ParentCategoryId = 0; - } - - UpdateCategory(category); - - var childCategories = GetAllCategoriesByParentCategoryId(category.Id, true); - DeleteAllCategories(childCategories, delete); + public DbQuerySettings QuerySettings { get; set; } + + private void DeleteAllCategories(IList categories, bool delete) + { + foreach (var category in categories) + { + if (delete) + { + category.Deleted = true; + } + else + { + category.ParentCategoryId = 0; + } + + UpdateCategory(category); + + var childCategories = GetAllCategoriesByParentCategoryId(category.Id, true); + DeleteAllCategories(childCategories, delete); } - } + } public virtual void InheritAclIntoChildren( - int categoryId, + int categoryId, bool touchProductsWithMultipleCategories = false, bool touchExistingAcls = false, bool categoriesOnly = false) { - var category = GetCategoryById(categoryId); - var subcategories = GetAllCategoriesByParentCategoryId(categoryId, true); - var allCustomerRoles = _customerService.GetAllCustomerRoles(true); - var categoryCustomerRoles = _aclService.GetCustomerRoleIdsWithAccessTo(category); + var allCustomerRoles = _customerService.GetAllCustomerRoles(true); + var referenceCategory = GetCategoryById(categoryId); + var referenceRoleIds = _aclService.GetCustomerRoleIdsWithAccessTo(referenceCategory); - var categoryIds = new HashSet(subcategories.Select(x => x.Id)); - categoryIds.Add(categoryId); + using (var scope = new DbContextScope(ctx: _aclRepository.Context, autoDetectChanges: false, proxyCreation: false, validateOnSave: false, autoCommit: false)) + { + ProcessCategory(scope, referenceCategory); + } - var searchQuery = new CatalogSearchQuery() - .WithCategoryIds(null, categoryIds.ToArray()); + _cache.RemoveByPattern(AclService.ACL_SEGMENT_PATTERN); - var query = _catalogSearchService.PrepareQuery(searchQuery); - var products = query.OrderBy(p => p.Id).ToList(); - using (var scope = new DbContextScope(ctx: _aclRepository.Context, autoDetectChanges: false, proxyCreation: false, validateOnSave: false)) + void ProcessCategory(DbContextScope scope, Category c) { - _aclRepository.AutoCommitEnabled = false; + // Process sub-categories. + var subCategories = GetAllCategoriesByParentCategoryId(c.Id, true); - foreach (var subcategory in subcategories) + foreach (var subCategory in subCategories) { - if (subcategory.SubjectToAcl != category.SubjectToAcl) + if (subCategory.SubjectToAcl != referenceCategory.SubjectToAcl) { - subcategory.SubjectToAcl = category.SubjectToAcl; - _categoryRepository.Update(subcategory); + subCategory.SubjectToAcl = referenceCategory.SubjectToAcl; + _categoryRepository.Update(subCategory); } - var existingAclRecords = _aclService.GetAclRecords(subcategory).ToDictionarySafe(x => x.CustomerRoleId); - - foreach (var customerRole in allCustomerRoles) + var existingAclRecords = _aclService.GetAclRecords(subCategory).ToDictionarySafe(x => x.CustomerRoleId); + + foreach (var role in allCustomerRoles) { - if (categoryCustomerRoles.Contains(customerRole.Id)) + if (referenceRoleIds.Contains(role.Id)) { - if (!existingAclRecords.ContainsKey(customerRole.Id)) + if (!existingAclRecords.ContainsKey(role.Id)) { - _aclRepository.Insert(new AclRecord { CustomerRole = customerRole, CustomerRoleId = customerRole.Id, EntityId = subcategory.Id, EntityName = "Category" }); + _aclRepository.Insert(new AclRecord { CustomerRoleId = role.Id, EntityId = subCategory.Id, EntityName = "Category" }); } } - else + else if (existingAclRecords.TryGetValue(role.Id, out var aclRecordToDelete)) { - if (existingAclRecords.TryGetValue(customerRole.Id, out var aclRecordToDelete)) - { - _aclRepository.Delete(aclRecordToDelete); - } + _aclRepository.Delete(aclRecordToDelete); } } } - _aclRepository.Context.SaveChanges(); + scope.Commit(); - foreach (var product in products) + // Process products. + var categoryIds = new HashSet(subCategories.Select(x => x.Id)); + categoryIds.Add(c.Id); + + var searchQuery = new CatalogSearchQuery().WithCategoryIds(null, categoryIds.ToArray()); + var productsQuery = _catalogSearchService.PrepareQuery(searchQuery); + var productsPager = new FastPager(productsQuery, 500); + + while (productsPager.ReadNextPage(out var products)) { - if (product.SubjectToAcl != category.SubjectToAcl) + foreach (var product in products) { - product.SubjectToAcl = category.SubjectToAcl; - _productRepository.Update(product); - } + if (product.SubjectToAcl != referenceCategory.SubjectToAcl) + { + product.SubjectToAcl = referenceCategory.SubjectToAcl; + _productRepository.Update(product); + } - var existingAclRecords = _aclService.GetAclRecords(product).ToDictionarySafe(x => x.CustomerRoleId); + var existingAclRecords = _aclService.GetAclRecords(product).ToDictionarySafe(x => x.CustomerRoleId); - foreach (var customerRole in allCustomerRoles) - { - if (categoryCustomerRoles.Contains(customerRole.Id)) + foreach (var role in allCustomerRoles) { - if (!existingAclRecords.ContainsKey(customerRole.Id)) + if (referenceRoleIds.Contains(role.Id)) + { + if (!existingAclRecords.ContainsKey(role.Id)) + { + _aclRepository.Insert(new AclRecord { CustomerRoleId = role.Id, EntityId = product.Id, EntityName = "Product" }); + } + } + else if (existingAclRecords.TryGetValue(role.Id, out var aclRecordToDelete)) { - _aclRepository.Insert(new AclRecord { CustomerRole = customerRole, CustomerRoleId = customerRole.Id, EntityId = product.Id, EntityName = "Product" }); + _aclRepository.Delete(aclRecordToDelete); } } - else - { - if (existingAclRecords.TryGetValue(customerRole.Id, out var aclRecordToDelete)) - { - _aclRepository.Delete(aclRecordToDelete); - } - } } + + scope.Commit(); } - _aclRepository.Context.SaveChanges(); + scope.Commit(); + + try + { + scope.DbContext.DetachEntities(x => x is Product || x is Category || x is AclRecord, false); + } + catch { } + + foreach (var subCategory in subCategories) + { + ProcessCategory(scope, subCategory); + } } } public virtual void InheritStoresIntoChildren( - int categoryId, + int categoryId, bool touchProductsWithMultipleCategories = false, bool touchExistingAcls = false, bool categoriesOnly = false) { - var category = GetCategoryById(categoryId); - var subcategories = GetAllCategoriesByParentCategoryId(categoryId, true); - var allStores = _storeService.GetAllStores(); - var categoryStoreMappings = _storeMappingService.GetStoresIdsWithAccess(category); - - var categoryIds = new HashSet(subcategories.Select(x => x.Id)); - categoryIds.Add(categoryId); + var allStores = _storeService.GetAllStores(); + var referenceCategory = GetCategoryById(categoryId); + var referenceStoreMappings = _storeMappingService.GetStoresIdsWithAccess(referenceCategory); - var searchQuery = new CatalogSearchQuery() - .WithCategoryIds(null, categoryIds.ToArray()); + using (var scope = new DbContextScope(ctx: _storeMappingRepository.Context, autoDetectChanges: false, proxyCreation: false, validateOnSave: false, autoCommit: false)) + { + ProcessCategory(scope, referenceCategory); + } - var query = _catalogSearchService.PrepareQuery(searchQuery); - var products = query.OrderBy(p => p.Id).ToList(); + _cache.RemoveByPattern(StoreMappingService.STOREMAPPING_SEGMENT_PATTERN); - using (var scope = new DbContextScope(ctx: _storeMappingRepository.Context, autoDetectChanges: false, proxyCreation: false, validateOnSave: false)) + + void ProcessCategory(DbContextScope scope, Category c) { - _storeMappingRepository.AutoCommitEnabled = false; + // Process sub-categories. + var subCategories = GetAllCategoriesByParentCategoryId(c.Id, true); - foreach (var subcategory in subcategories) + foreach (var subCategory in subCategories) { - if (subcategory.LimitedToStores != category.LimitedToStores) + if (subCategory.LimitedToStores != referenceCategory.LimitedToStores) { - subcategory.LimitedToStores = category.LimitedToStores; - _categoryRepository.Update(subcategory); + subCategory.LimitedToStores = referenceCategory.LimitedToStores; + _categoryRepository.Update(subCategory); } - var existingStoreMappingsRecords = _storeMappingService.GetStoreMappings(subcategory).ToDictionary(x => x.StoreId); + var existingStoreMappingsRecords = _storeMappingService.GetStoreMappings(subCategory).ToDictionary(x => x.StoreId); foreach (var store in allStores) { - if (categoryStoreMappings.Contains(store.Id)) + if (referenceStoreMappings.Contains(store.Id)) { if (!existingStoreMappingsRecords.ContainsKey(store.Id)) { - _storeMappingRepository.Insert(new StoreMapping { StoreId = store.Id, EntityId = subcategory.Id, EntityName = "Category" }); + _storeMappingRepository.Insert(new StoreMapping { StoreId = store.Id, EntityId = subCategory.Id, EntityName = "Category" }); } } - else + else if (existingStoreMappingsRecords.TryGetValue(store.Id, out var storeMappingToDelete)) { - if (existingStoreMappingsRecords.TryGetValue(store.Id, out var storeMappingToDelete)) - { - _storeMappingRepository.Delete(storeMappingToDelete); - } - } + _storeMappingRepository.Delete(storeMappingToDelete); + } } } - _storeMappingRepository.Context.SaveChanges(); + scope.Commit(); + + // Process products. + var categoryIds = new HashSet(subCategories.Select(x => x.Id)); + categoryIds.Add(c.Id); - foreach (var product in products) + var searchQuery = new CatalogSearchQuery().WithCategoryIds(null, categoryIds.ToArray()); + var productsQuery = _catalogSearchService.PrepareQuery(searchQuery); + var productsPager = new FastPager(productsQuery, 500); + + while (productsPager.ReadNextPage(out var products)) { - if (product.LimitedToStores != category.LimitedToStores) + foreach (var product in products) { - product.LimitedToStores = category.LimitedToStores; - _productRepository.Update(product); - } + if (product.LimitedToStores != referenceCategory.LimitedToStores) + { + product.LimitedToStores = referenceCategory.LimitedToStores; + _productRepository.Update(product); + } - var existingStoreMappingsRecords = _storeMappingService.GetStoreMappings(product).ToDictionary(x => x.StoreId); + var existingStoreMappingsRecords = _storeMappingService.GetStoreMappings(product).ToDictionary(x => x.StoreId); - foreach (var store in allStores) - { - if (categoryStoreMappings.Contains(store.Id)) + foreach (var store in allStores) { - if (!existingStoreMappingsRecords.ContainsKey(store.Id)) + if (referenceStoreMappings.Contains(store.Id)) + { + if (!existingStoreMappingsRecords.ContainsKey(store.Id)) + { + _storeMappingRepository.Insert(new StoreMapping { StoreId = store.Id, EntityId = product.Id, EntityName = "Product" }); + } + } + else if (existingStoreMappingsRecords.TryGetValue(store.Id, out var storeMappingToDelete)) { - _storeMappingRepository.Insert(new StoreMapping { StoreId = store.Id, EntityId = product.Id, EntityName = "Product" }); + _storeMappingRepository.Delete(storeMappingToDelete); } } - else - { - if (existingStoreMappingsRecords.TryGetValue(store.Id, out var storeMappingToDelete)) - { - _storeMappingRepository.Delete(storeMappingToDelete); - } - } } + + scope.Commit(); } - _storeMappingRepository.Context.SaveChanges(); + scope.Commit(); + + try + { + scope.DbContext.DetachEntities(x => x is Product || x is Category || x is StoreMapping, false); + } + catch { } + + foreach (var subCategory in subCategories) + { + ProcessCategory(scope, subCategory); + } } } - public virtual void DeleteCategory(Category category, bool deleteChilds = false) + public virtual void DeleteCategory(Category category, bool deleteChilds = false) { - Guard.NotNull(category, nameof(category)); + Guard.NotNull(category, nameof(category)); - category.Deleted = true; + category.Deleted = true; UpdateCategory(category); - var childCategories = GetAllCategoriesByParentCategoryId(category.Id, true); - DeleteAllCategories(childCategories, deleteChilds); + var childCategories = GetAllCategoriesByParentCategoryId(category.Id, true); + DeleteAllCategories(childCategories, deleteChilds); } - public virtual IQueryable BuildCategoriesQuery( - string categoryName = "", - bool showHidden = false, - string alias = null, - int storeId = 0) - { - var query = _categoryRepository.Table; + public virtual IQueryable BuildCategoriesQuery( + string categoryName = "", + bool showHidden = false, + string alias = null, + int storeId = 0) + { + var query = _categoryRepository.Table; - if (!showHidden) - query = query.Where(c => c.Published); + query = query.Where(c => !c.Deleted); - if (categoryName.HasValue()) - query = query.Where(c => c.Name.Contains(categoryName) || c.FullName.Contains(categoryName)); + if (!showHidden) + query = query.Where(c => c.Published); - if (alias.HasValue()) - query = query.Where(c => c.Alias.Contains(alias)); + if (categoryName.HasValue()) + query = query.Where(c => c.Name.Contains(categoryName) || c.FullName.Contains(categoryName)); - query = ApplyHiddenCategoriesFilter(query, storeId, showHidden); + if (alias.HasValue()) + query = query.Where(c => c.Alias.Contains(alias)); - query = query.Where(c => !c.Deleted); + query = ApplyHiddenCategoriesFilter(query, storeId, showHidden); - return query; - } + return query; + } public virtual IPagedList GetAllCategories( - string categoryName = "", - int pageIndex = 0, - int pageSize = int.MaxValue, - bool showHidden = false, - string alias = null, - bool ignoreCategoriesWithoutExistingParent = true, - int storeId = 0) + string categoryName = "", + int pageIndex = 0, + int pageSize = int.MaxValue, + bool showHidden = false, + string alias = null, + bool ignoreDetachedCategories = true, + int storeId = 0) { - var query = BuildCategoriesQuery(categoryName, showHidden, alias, storeId); + var query = BuildCategoriesQuery(categoryName, showHidden, alias, storeId); - query = query - .OrderBy(x => x.ParentCategoryId) - .ThenBy(x => x.DisplayOrder) - .ThenBy(x => x.Name); + query = query + .OrderBy(x => x.ParentCategoryId) + .ThenBy(x => x.DisplayOrder) + .ThenBy(x => x.Name); var unsortedCategories = query.ToList(); // Sort categories - var sortedCategories = unsortedCategories.SortCategoryNodesForTree(ignoreCategoriesWithoutExistingParent: ignoreCategoriesWithoutExistingParent); + var sortedCategories = unsortedCategories.SortCategoryNodesForTree(ignoreDetachedCategories: ignoreDetachedCategories); // Paging return new PagedList(sortedCategories, pageIndex, pageSize); @@ -345,8 +383,8 @@ public virtual IPagedList GetAllCategories( public IList GetAllCategoriesByParentCategoryId(int parentCategoryId, bool showHidden = false) { - int storeId = _storeContext.CurrentStore.Id; - string key = string.Format(CATEGORIES_BY_PARENT_CATEGORY_ID_KEY, parentCategoryId, showHidden, _workContext.CurrentCustomer.Id, storeId); + int storeId = _storeContext.CurrentStore.Id; + string key = string.Format(CATEGORIES_BY_PARENT_CATEGORY_ID_KEY, parentCategoryId, showHidden, _workContext.CurrentCustomer.Id, storeId); return _requestCache.Get(key, () => { var query = _categoryRepository.Table; @@ -356,57 +394,57 @@ public IList GetAllCategoriesByParentCategoryId(int parentCategoryId, query = query.Where(c => c.ParentCategoryId == parentCategoryId && !c.Deleted); - query = ApplyHiddenCategoriesFilter(query, storeId, showHidden); + query = ApplyHiddenCategoriesFilter(query, storeId, showHidden); var categories = query.OrderBy(x => x.DisplayOrder).ToList(); return categories; }); } - protected virtual IQueryable ApplyHiddenCategoriesFilter(IQueryable query, int storeId = 0, bool showHidden = false) + protected virtual IQueryable ApplyHiddenCategoriesFilter(IQueryable query, int storeId = 0, bool showHidden = false) { - var entityName = nameof(Category); - var applied = false; - - // Store mapping - if (storeId > 0 && !QuerySettings.IgnoreMultiStore) - { - query = from c in query - join m in _storeMappingRepository.Table - on new { c1 = c.Id, c2 = entityName } equals new { c1 = m.EntityId, c2 = m.EntityName } into cm - from m in cm.DefaultIfEmpty() - where !c.LimitedToStores || storeId == m.StoreId - select c; - - applied = true; - } - - // ACL (access control list) - if (!showHidden && !QuerySettings.IgnoreAcl) - { - var allowedCustomerRolesIds = _workContext.CurrentCustomer.CustomerRoles.Where(x => x.Active).Select(x => x.Id).ToList(); - - query = from c in query - join a in _aclRepository.Table - on new { c1 = c.Id, c2 = entityName } equals new { c1 = a.EntityId, c2 = a.EntityName } into ca - from a in ca.DefaultIfEmpty() - where !c.SubjectToAcl || allowedCustomerRolesIds.Contains(a.CustomerRoleId) - select c; - - applied = true; - } - - if (applied) - { - // Only distinct categories (group by ID) - query = from c in query - group c by c.Id into cGroup - orderby cGroup.Key - select cGroup.FirstOrDefault(); - } - - - return query; + var entityName = nameof(Category); + var grouping = false; + + // Store mapping + if (!showHidden && storeId > 0 && !QuerySettings.IgnoreMultiStore) + { + query = from c in query + join m in _storeMappingRepository.Table + on new { c1 = c.Id, c2 = entityName } equals new { c1 = m.EntityId, c2 = m.EntityName } into cm + from m in cm.DefaultIfEmpty() + where !c.LimitedToStores || storeId == m.StoreId + select c; + + grouping = true; + } + + // ACL (access control list) + if (!showHidden && !QuerySettings.IgnoreAcl) + { + var allowedCustomerRolesIds = _workContext.CurrentCustomer.GetRoleIds(); + + query = from c in query + join a in _aclRepository.Table + on new { c1 = c.Id, c2 = entityName } equals new { c1 = a.EntityId, c2 = a.EntityName } into ca + from a in ca.DefaultIfEmpty() + where !c.SubjectToAcl || allowedCustomerRolesIds.Contains(a.CustomerRoleId) + select c; + + grouping = true; + } + + if (grouping) + { + // Only distinct categories (group by ID). + // SQL Server CE: where-clause beyond this statement produces "SELECT @p__linq__0 AS[p__linq__0]..." which fails in CE. + query = from c in query + group c by c.Id into cGroup + orderby cGroup.Key + select cGroup.FirstOrDefault(); + } + + return query; } public virtual IList GetAllCategoriesDisplayedOnHomePage() @@ -414,7 +452,7 @@ public virtual IList GetAllCategoriesDisplayedOnHomePage() var query = from c in _categoryRepository.Table orderby c.DisplayOrder where c.Published && - !c.Deleted && + !c.Deleted && c.ShowOnHomePage select c; @@ -427,8 +465,8 @@ public virtual Category GetCategoryById(int categoryId) if (categoryId == 0) return null; - return _categoryRepository.GetByIdCached(categoryId, "db.category.id-" + categoryId); - } + return _categoryRepository.GetByIdCached(categoryId, "db.category.id-" + categoryId); + } public virtual IList GetCategoriesByIds(int[] categoryIds) { @@ -449,9 +487,9 @@ where categoryIds.Contains(c.Id) public virtual void InsertCategory(Category category) { - Guard.NotNull(category, nameof(category)); + Guard.NotNull(category, nameof(category)); - _categoryRepository.Insert(category); + _categoryRepository.Insert(category); _requestCache.RemoveByPattern(CATEGORIES_PATTERN_KEY); _requestCache.RemoveByPattern(PRODUCTCATEGORIES_PATTERN_KEY); @@ -459,7 +497,7 @@ public virtual void InsertCategory(Category category) public virtual void UpdateCategory(Category category) { - Guard.NotNull(category, nameof(category)); + Guard.NotNull(category, nameof(category)); //validate category hierarchy var parentCategory = GetCategoryById(category.ParentCategoryId); @@ -481,17 +519,17 @@ public virtual void UpdateCategory(Category category) public virtual void UpdateHasDiscountsApplied(Category category) { - Guard.NotNull(category, nameof(category)); + Guard.NotNull(category, nameof(category)); - category.HasDiscountsApplied = category.AppliedDiscounts.Count > 0; + category.HasDiscountsApplied = category.AppliedDiscounts.Count > 0; UpdateCategory(category); } public virtual void DeleteProductCategory(ProductCategory productCategory) { - Guard.NotNull(productCategory, nameof(productCategory)); + Guard.NotNull(productCategory, nameof(productCategory)); - _productCategoryRepository.Delete(productCategory); + _productCategoryRepository.Delete(productCategory); //cache _requestCache.RemoveByPattern(CATEGORIES_PATTERN_KEY); @@ -503,8 +541,8 @@ public virtual IPagedList GetProductCategoriesByCategoryId(int if (categoryId == 0) return new PagedList(new List(), pageIndex, pageSize); - int storeId = _storeContext.CurrentStore.Id; - string key = string.Format(PRODUCTCATEGORIES_ALLBYCATEGORYID_KEY, showHidden, categoryId, pageIndex, pageSize, _workContext.CurrentCustomer.Id, storeId); + int storeId = _storeContext.CurrentStore.Id; + string key = string.Format(PRODUCTCATEGORIES_ALLBYCATEGORYID_KEY, showHidden, categoryId, pageIndex, pageSize, _workContext.CurrentCustomer.Id, storeId); return _requestCache.Get(key, () => { @@ -518,9 +556,9 @@ join p in _productRepository.Table on pc.ProductId equals p.Id query = ApplyHiddenProductCategoriesFilter(query, storeId); } - query = query - .OrderBy(pc => pc.DisplayOrder) - .ThenBy(pc => pc.Id); // required for paging! + query = query + .OrderBy(pc => pc.DisplayOrder) + .ThenBy(pc => pc.Id); // required for paging! var productCategories = new PagedList(query, pageIndex, pageSize); @@ -533,132 +571,132 @@ public virtual IList GetProductCategoriesByProductId(int produc if (productId == 0) return new List(); - string key = string.Format(PRODUCTCATEGORIES_ALLBYPRODUCTID_KEY, showHidden, productId, _workContext.CurrentCustomer.Id, _storeContext.CurrentStore.Id); + string key = string.Format(PRODUCTCATEGORIES_ALLBYPRODUCTID_KEY, showHidden, productId, _workContext.CurrentCustomer.Id, _storeContext.CurrentStore.Id); return _requestCache.Get(key, () => { - var query = from pc in _productCategoryRepository.Table + var query = from pc in _productCategoryRepository.Table join c in _categoryRepository.Table on pc.CategoryId equals c.Id where pc.ProductId == productId && - !c.Deleted && - (showHidden || c.Published) + !c.Deleted && + (showHidden || c.Published) orderby pc.DisplayOrder select pc; - query = query.Include(x => x.Category); - - var allProductCategories = query.ToList(); - var result = new List(); - if (!showHidden) - { - foreach (var pc in allProductCategories) - { - // ACL (access control list) and store mapping - var category = pc.Category; - if (_aclService.Authorize(category) && _storeMappingService.Authorize(category)) - result.Add(pc); - } - } - else - { - // No filtering - result.AddRange(allProductCategories); - } - return result; + query = query.Include(x => x.Category); + + var allProductCategories = query.ToList(); + var result = new List(); + if (!showHidden) + { + foreach (var pc in allProductCategories) + { + // ACL (access control list) and store mapping + var category = pc.Category; + if (_aclService.Authorize(category) && _storeMappingService.Authorize(category)) + result.Add(pc); + } + } + else + { + // No filtering + result.AddRange(allProductCategories); + } + return result; }); } - public virtual Multimap GetProductCategoriesByProductIds(int[] productIds, bool? hasDiscountsApplied = null, bool showHidden = false) - { - Guard.NotNull(productIds, nameof(productIds)); + public virtual Multimap GetProductCategoriesByProductIds(int[] productIds, bool? hasDiscountsApplied = null, bool showHidden = false) + { + Guard.NotNull(productIds, nameof(productIds)); if (!productIds.Any()) { return new Multimap(); } - var query = - from pc in _productCategoryRepository.TableUntracked - join c in _categoryRepository.Table on pc.CategoryId equals c.Id - where productIds.Contains(pc.ProductId) && !c.Deleted && (showHidden || c.Published) - orderby pc.DisplayOrder - select pc; + var query = + from pc in _productCategoryRepository.TableUntracked + join c in _categoryRepository.Table on pc.CategoryId equals c.Id + where productIds.Contains(pc.ProductId) && !c.Deleted && (showHidden || c.Published) + orderby pc.DisplayOrder + select pc; - query = query.Include(x => x.Category.Picture); + query = query.Include(x => x.Category.MediaFile); - if (hasDiscountsApplied.HasValue) - { - query = query.Where(x => x.Category.HasDiscountsApplied == hasDiscountsApplied); - } + if (hasDiscountsApplied.HasValue) + { + query = query.Where(x => x.Category.HasDiscountsApplied == hasDiscountsApplied); + } - var list = query.ToList(); + var list = query.ToList(); - if (!showHidden) - { - list = list.Where(x => _aclService.Authorize(x.Category) && _storeMappingService.Authorize(x.Category)).ToList(); - } + if (!showHidden) + { + list = list.Where(x => _aclService.Authorize(x.Category) && _storeMappingService.Authorize(x.Category)).ToList(); + } - var map = list.ToMultimap(x => x.ProductId, x => x); + var map = list.ToMultimap(x => x.ProductId, x => x); - return map; - } + return map; + } - public virtual Multimap GetProductCategoriesByCategoryIds(int[] categoryIds) - { - Guard.NotNull(categoryIds, nameof(categoryIds)); + public virtual Multimap GetProductCategoriesByCategoryIds(int[] categoryIds) + { + Guard.NotNull(categoryIds, nameof(categoryIds)); - var query = _productCategoryRepository.TableUntracked - .Where(x => categoryIds.Contains(x.CategoryId)) - .OrderBy(x => x.DisplayOrder); + var query = _productCategoryRepository.TableUntracked + .Where(x => categoryIds.Contains(x.CategoryId)) + .OrderBy(x => x.DisplayOrder); - var map = query - .ToList() - .ToMultimap(x => x.CategoryId, x => x); + var map = query + .ToList() + .ToMultimap(x => x.CategoryId, x => x); - return map; - } + return map; + } - protected virtual IQueryable ApplyHiddenProductCategoriesFilter(IQueryable query, int storeId = 0) + protected virtual IQueryable ApplyHiddenProductCategoriesFilter(IQueryable query, int storeId = 0) { - bool group = false; + bool group = false; //ACL (access control list) - if (!QuerySettings.IgnoreAcl) - { - group = true; - var allowedCustomerRolesIds = _workContext.CurrentCustomer.CustomerRoles.Where(cr => cr.Active).Select(cr => cr.Id).ToList(); - - query = from pc in query - join c in _categoryRepository.Table on pc.CategoryId equals c.Id - join acl in _aclRepository.Table - on new { c1 = c.Id, c2 = "Category" } equals new { c1 = acl.EntityId, c2 = acl.EntityName } into c_acl - from acl in c_acl.DefaultIfEmpty() - where !c.SubjectToAcl || allowedCustomerRolesIds.Contains(acl.CustomerRoleId) - select pc; - } + if (!QuerySettings.IgnoreAcl) + { + group = true; + var allowedCustomerRolesIds = _workContext.CurrentCustomer.GetRoleIds(); + + query = from pc in query + join c in _categoryRepository.Table on pc.CategoryId equals c.Id + join acl in _aclRepository.Table + on new { c1 = c.Id, c2 = "Category" } equals new { c1 = acl.EntityId, c2 = acl.EntityName } into c_acl + from acl in c_acl.DefaultIfEmpty() + where !c.SubjectToAcl || allowedCustomerRolesIds.Contains(acl.CustomerRoleId) + select pc; + } //Store mapping - if (!QuerySettings.IgnoreMultiStore && storeId > 0) - { - group = true; - query = from pc in query - join c in _categoryRepository.Table on pc.CategoryId equals c.Id - join sm in _storeMappingRepository.Table - on new { c1 = c.Id, c2 = "Category" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into c_sm - from sm in c_sm.DefaultIfEmpty() - where !c.LimitedToStores || storeId == sm.StoreId - select pc; - } - - if (group) - { - //only distinct categories (group by ID) - query = from pc in query - group pc by pc.Id into pcGroup - orderby pcGroup.Key - select pcGroup.FirstOrDefault(); - } - - return query; + if (!QuerySettings.IgnoreMultiStore && storeId > 0) + { + group = true; + query = from pc in query + join c in _categoryRepository.Table on pc.CategoryId equals c.Id + join sm in _storeMappingRepository.Table + on new { c1 = c.Id, c2 = "Category" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into c_sm + from sm in c_sm.DefaultIfEmpty() + where !c.LimitedToStores || storeId == sm.StoreId + select pc; + } + + if (group) + { + //only distinct categories (group by ID) + query = from pc in query + group pc by pc.Id into pcGroup + orderby pcGroup.Key + select pcGroup.FirstOrDefault(); + } + + return query; } public virtual ProductCategory GetProductCategoryById(int productCategoryId) @@ -691,164 +729,199 @@ public virtual void UpdateProductCategory(ProductCategory productCategory) _requestCache.RemoveByPattern(PRODUCTCATEGORIES_PATTERN_KEY); } - public virtual IEnumerable GetCategoryTrail(ICategoryNode node) - { - Guard.NotNull(node, nameof(node)); - - var treeNode = GetCategoryTree(node.Id, true); - - if (treeNode == null) - { - return Enumerable.Empty(); - } - - return treeNode.Trail - .Where(x => !x.IsRoot) - //.TakeWhile(x => x.Value.Published) // TBD: (mc) do we need this? - .Select(x => x.Value); - } - - public virtual string GetCategoryPath( - TreeNode treeNode, - int? languageId = null, - string aliasPattern = null, - string separator = " ") - { - Guard.NotNull(treeNode, nameof(treeNode)); - - var lookupKey = "Path.{0}.{1}.{2}".FormatInvariant(separator, languageId ?? 0, aliasPattern.HasValue()); - var cachedPath = treeNode.GetMetadata(lookupKey, false); - - if (cachedPath != null) - { - return cachedPath; - } - - var trail = treeNode.Trail; - var sb = new StringBuilder(string.Empty, (trail.Count()) * 16); - - foreach (var node in trail) - { - if (!node.IsRoot) - { - var cat = node.Value; - - var name = languageId.HasValue - ? cat.GetLocalized(n => n.Name, languageId.Value) - : cat.Name; - - sb.Append(name); - - if (aliasPattern.HasValue() && cat.Alias.HasValue()) - { - sb.Append(" "); - sb.Append(string.Format(aliasPattern, cat.Alias)); - } - - if (node != treeNode) - { - // Is not self (trail end) - sb.Append(separator); - } - } - } - - var path = sb.ToString(); - treeNode.SetThreadMetadata(lookupKey, path); - return path; - } - - public TreeNode GetCategoryTree(int rootCategoryId = 0, bool includeHidden = false, int storeId = 0) - { - var storeToken = QuerySettings.IgnoreMultiStore ? "0" : storeId.ToString(); - var rolesToken = QuerySettings.IgnoreAcl || includeHidden ? "0" : _workContext.CurrentCustomer.GetRolesIdent(); - var cacheKey = CATEGORY_TREE_KEY.FormatInvariant(includeHidden.ToString().ToLower(), rolesToken, storeToken); - - var root = _cache.Get(cacheKey, () => - { - // (Perf) don't fetch every field from db - var query = from x in BuildCategoriesQuery(showHidden: includeHidden, storeId: storeId) - orderby x.ParentCategoryId, x.DisplayOrder, x.Name - select new - { - x.Id, - x.ParentCategoryId, - x.Name, + public virtual IEnumerable GetCategoryTrail(ICategoryNode node) + { + Guard.NotNull(node, nameof(node)); + + var treeNode = GetCategoryTree(node.Id, true); + + if (treeNode == null) + { + return Enumerable.Empty(); + } + + return treeNode.Trail + .Where(x => !x.IsRoot) + //.TakeWhile(x => x.Value.Published) // TBD: (mc) do we need this? + .Select(x => x.Value); + } + + public virtual string GetCategoryPath( + TreeNode treeNode, + int? languageId = null, + string aliasPattern = null, + string separator = " ") + { + Guard.NotNull(treeNode, nameof(treeNode)); + + var lookupKey = "Path.{0}.{1}.{2}".FormatInvariant(separator, languageId ?? 0, aliasPattern.HasValue()); + var cachedPath = treeNode.GetMetadata(lookupKey, false); + + if (cachedPath != null) + { + return cachedPath; + } + + var trail = treeNode.Trail; + var psb = PooledStringBuilder.Rent(); + var sb = (StringBuilder)psb; + + foreach (var node in trail) + { + if (!node.IsRoot) + { + var cat = node.Value; + + var name = languageId.HasValue + ? cat.GetLocalized(n => n.Name, languageId.Value) + : cat.Name; + + sb.Append(name); + + if (aliasPattern.HasValue() && cat.Alias.HasValue()) + { + sb.Append(" "); + sb.Append(string.Format(aliasPattern, cat.Alias)); + } + + if (node != treeNode) + { + // Is not self (trail end) + sb.Append(separator); + } + } + } + + var path = psb.ToStringAndReturn(); + treeNode.SetThreadMetadata(lookupKey, path); + return path; + } + + public TreeNode GetCategoryTree(int rootCategoryId = 0, bool includeHidden = false, int storeId = 0) + { + var storeToken = QuerySettings.IgnoreMultiStore ? "0" : storeId.ToString(); + var rolesToken = QuerySettings.IgnoreAcl || includeHidden ? "0" : _workContext.CurrentCustomer.GetRolesIdent(); + var cacheKey = CATEGORY_TREE_KEY.FormatInvariant(includeHidden.ToString().ToLower(), rolesToken, storeToken); + + var root = _cache.Get(cacheKey, () => + { + // (Perf) don't fetch every field from db + var query = from x in BuildCategoriesQuery(showHidden: includeHidden, storeId: storeId) + orderby x.ParentCategoryId, x.DisplayOrder, x.Name + select new + { + x.Id, + x.ParentCategoryId, + x.Name, x.ExternalLink, - x.Alias, - x.PictureId, - x.Published, - x.DisplayOrder, - x.UpdatedOnUtc, - x.BadgeText, - x.BadgeStyle, - x.LimitedToStores, - x.SubjectToAcl - }; - - var unsortedNodes = query.ToList().Select(x => new CategoryNode - { - Id = x.Id, - ParentCategoryId = x.ParentCategoryId, - Name = x.Name, + x.Alias, + x.MediaFileId, + x.Published, + x.DisplayOrder, + x.UpdatedOnUtc, + x.BadgeText, + x.BadgeStyle, + x.LimitedToStores, + x.SubjectToAcl + }; + + var unsortedNodes = query.ToList().Select(x => new CategoryNode + { + Id = x.Id, + ParentCategoryId = x.ParentCategoryId, + Name = x.Name, ExternalLink = x.ExternalLink, - Alias = x.Alias, - PictureId = x.PictureId, - Published = x.Published, - DisplayOrder = x.DisplayOrder, - UpdatedOnUtc = x.UpdatedOnUtc, - BadgeText = x.BadgeText, - BadgeStyle = x.BadgeStyle, - LimitedToStores = x.LimitedToStores, - SubjectToAcl = x.SubjectToAcl - }); - - var nodes = unsortedNodes.SortCategoryNodesForTree(0, true); - var curParent = new TreeNode(new CategoryNode { Name = "Home" }); - CategoryNode prevNode = null; - - foreach (var node in nodes) - { - // Determine parent - if (prevNode != null) - { - if (node.ParentCategoryId != curParent.Value.Id) - { - if (node.ParentCategoryId == prevNode.Id) - { - // level +1 - curParent = curParent.LastChild; - } - else - { - // level -x - while (!curParent.IsRoot) - { - if (curParent.Value.Id == node.ParentCategoryId) - { - break; - } - curParent = curParent.Parent; - } - } - } - } - - // add to parent - curParent.Append(node, node.Id); - - prevNode = node; - } - - return curParent.Root; - }, CategoryTreeCacheDuration); - - if (rootCategoryId > 0) - { - root = root.SelectNodeById(rootCategoryId); - } - - return root; - } - } + Alias = x.Alias, + MediaFileId = x.MediaFileId, + Published = x.Published, + DisplayOrder = x.DisplayOrder, + UpdatedOnUtc = x.UpdatedOnUtc, + BadgeText = x.BadgeText, + BadgeStyle = x.BadgeStyle, + LimitedToStores = x.LimitedToStores, + SubjectToAcl = x.SubjectToAcl + }); + + var nodeMap = unsortedNodes.ToMultimap(x => x.ParentCategoryId, x => x); + var curParent = new TreeNode(new CategoryNode { Name = "Home" }); + + AddChildTreeNodes(curParent, 0, nodeMap); + + return curParent.Root; + }, CategoryTreeCacheDuration); + + if (rootCategoryId > 0) + { + root = root.SelectNodeById(rootCategoryId); + } + + return root; + } + + private void AddChildTreeNodes(TreeNode parentNode, int parentItemId, Multimap nodeMap) + { + if (parentNode == null) + { + return; + } + + var nodes = nodeMap.ContainsKey(parentItemId) + ? nodeMap[parentItemId].OrderBy(x => x.DisplayOrder) + : Enumerable.Empty(); + + foreach (var node in nodes) + { + var newNode = new TreeNode(node) + { + Id = node.Id + }; + + parentNode.Append(newNode); + + AddChildTreeNodes(newNode, node.Id, nodeMap); + } + } + + public int CountAllCategories() + { + var query = _categoryRepository.Table; + query = query.Where(x => !x.Deleted); + + return query.Count(); + } + + #region XML Sitemap + + public XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context) + { + if (!context.LoadSetting().XmlSitemapIncludesCategories) + return null; + + var query = BuildCategoriesQuery(showHidden: false, storeId: context.RequestStoreId); + return new CategoryXmlSitemapResult { Query = query }; + } + + class CategoryXmlSitemapResult : XmlSitemapProvider + { + public IQueryable Query { get; set; } + + public override int GetTotalCount() + { + return Query.Count(); + } + + public override IEnumerable Enlist() + { + var topics = Query.Select(x => new { x.Id, x.UpdatedOnUtc }).ToList(); + foreach (var x in topics) + { + yield return new NamedEntity { EntityName = "Category", Id = x.Id, LastMod = x.UpdatedOnUtc }; + } + } + + public override int Order => int.MinValue; + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/CategoryTemplateService.cs b/src/Libraries/SmartStore.Services/Catalog/CategoryTemplateService.cs index c455ddc0c6..ff50dcd3b9 100644 --- a/src/Libraries/SmartStore.Services/Catalog/CategoryTemplateService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/CategoryTemplateService.cs @@ -12,7 +12,7 @@ public partial class CategoryTemplateService : ICategoryTemplateService private readonly IRepository _categoryTemplateRepository; private readonly IEventPublisher _eventPublisher; - public CategoryTemplateService(IRepository categoryTemplateRepository, IEventPublisher eventPublisher) + public CategoryTemplateService(IRepository categoryTemplateRepository, IEventPublisher eventPublisher) { _categoryTemplateRepository = categoryTemplateRepository; _eventPublisher = eventPublisher; @@ -35,7 +35,7 @@ orderby pt.DisplayOrder var templates = query.ToList(); return templates; } - + public virtual CategoryTemplate GetCategoryTemplateById(int categoryTemplateId) { if (categoryTemplateId == 0) diff --git a/src/Libraries/SmartStore.Services/Catalog/CategoryTreeChangeHandler.cs b/src/Libraries/SmartStore.Services/Catalog/CategoryTreeChangeHandler.cs index 804584a665..df7cd1a0af 100644 --- a/src/Libraries/SmartStore.Services/Catalog/CategoryTreeChangeHandler.cs +++ b/src/Libraries/SmartStore.Services/Catalog/CategoryTreeChangeHandler.cs @@ -12,281 +12,280 @@ using SmartStore.Core.Domain.Stores; using SmartStore.Core.Events; using SmartStore.Core.Search; +using SmartStore.Data; namespace SmartStore.Services.Catalog { - public enum CategoryTreeChangeReason - { - ElementCounts, - Data, - Localization, - StoreMapping, - Acl, - Hierarchy - } + public enum CategoryTreeChangeReason + { + ElementCounts, + Data, + Localization, + StoreMapping, + Acl, + Hierarchy + } - public class CategoryTreeChangedEvent - { - public CategoryTreeChangedEvent(CategoryTreeChangeReason reason) - { - Reason = reason; - } + public class CategoryTreeChangedEvent + { + public CategoryTreeChangedEvent(CategoryTreeChangeReason reason) + { + Reason = reason; + } - public CategoryTreeChangeReason Reason { get; private set; } - } + public CategoryTreeChangeReason Reason { get; private set; } + } - public class CategoryTreeChangeHook : IDbSaveHook, IConsumer - { - private readonly ICommonServices _services; - private readonly ICategoryService _categoryService; + public class CategoryTreeChangeHook : IDbSaveHook, IConsumer + { + private readonly ICommonServices _services; - private readonly bool[] _handledReasons = new bool[(int)CategoryTreeChangeReason.Hierarchy + 1]; - private bool _invalidated; + private readonly bool[] _handledReasons = new bool[(int)CategoryTreeChangeReason.Hierarchy + 1]; + private bool _invalidated; - // Hierarchy affecting category prop names - private static readonly string[] _h = new string[] { "ParentCategoryId", "Published", "Deleted", "DisplayOrder" }; - // Visibility affecting category prop names - private static readonly string[] _a = new string[] { "LimitedToStores", "SubjectToAcl" }; - // Data affecting category prop names - private static readonly string[] _d = new string[] { "Name", "Alias", "ExternalLink", "PictureId", "BadgeText", "BadgeStyle" }; + // Hierarchy affecting category prop names + private static readonly string[] _h = new string[] { "ParentCategoryId", "Published", "Deleted", "DisplayOrder" }; + // Visibility affecting category prop names + private static readonly string[] _a = new string[] { "LimitedToStores", "SubjectToAcl" }; + // Data affecting category prop names + private static readonly string[] _d = new string[] { "Name", "Alias", "ExternalLink", "MediaFileId", "BadgeText", "BadgeStyle" }; - private static readonly HashSet _candidateTypes = new HashSet - { - typeof(Product), - typeof(Category), - typeof(ProductCategory), - typeof(Setting), - typeof(Language), - typeof(LocalizedProperty), - typeof(StoreMapping), - typeof(AclRecord) - }; + private static readonly HashSet _candidateTypes = new HashSet + { + typeof(Product), + typeof(Category), + typeof(ProductCategory), + typeof(Setting), + typeof(Language), + typeof(LocalizedProperty), + typeof(StoreMapping), + typeof(AclRecord) + }; - public CategoryTreeChangeHook(ICommonServices services, ICategoryService categoryService) - { - _services = services; - _categoryService = categoryService; - } + public CategoryTreeChangeHook(ICommonServices services) + { + _services = services; + } - public void OnBeforeSave(IHookedEntity entry) - { - if (_invalidated) - return; + public void OnBeforeSave(IHookedEntity entry) + { + if (_invalidated) + return; - if (entry.InitialState != EntityState.Modified) - throw new NotSupportedException(); + if (entry.InitialState != EntityState.Modified) + throw new NotSupportedException(); - var cache = _services.Cache; - var entity = entry.Entity; - - if (entity is Product) - { - var modProps = _services.DbContext.GetModifiedProperties(entity); - var toxicPropNames = Product.GetVisibilityAffectingPropertyNames(); - if (modProps.Keys.Any(x => toxicPropNames.Contains(x))) - { - // No eviction, just notification - PublishEvent(CategoryTreeChangeReason.ElementCounts); - } - } - else if (entity is ProductCategory) - { - var modProps = _services.DbContext.GetModifiedProperties(entity); - if (modProps.ContainsKey("CategoryId")) - { - // No eviction, just notification - PublishEvent(CategoryTreeChangeReason.ElementCounts); - } - } - else if (entity is Category category) - { - var modProps = _services.DbContext.GetModifiedProperties(entity); + var cache = _services.Cache; + var entity = entry.Entity; - if (modProps.Keys.Any(x => _h.Contains(x))) - { - // Hierarchy affecting properties has changed. Nuke each tree. - cache.RemoveByPattern(CategoryService.CATEGORY_TREE_PATTERN_KEY); - PublishEvent(CategoryTreeChangeReason.Hierarchy); - _invalidated = true; - } - else if (modProps.Keys.Any(x => _a.Contains(x))) - { - if (modProps.ContainsKey("LimitedToStores")) - { - // Don't nuke store agnostic trees - cache.RemoveByPattern(BuildCacheKeyPattern("*", "*", "[^0]*")); - PublishEvent(CategoryTreeChangeReason.StoreMapping); - } - if (modProps.ContainsKey("SubjectToAcl")) - { - // Don't nuke ACL agnostic trees - cache.RemoveByPattern(BuildCacheKeyPattern("*", "[^0]*", "*")); - PublishEvent(CategoryTreeChangeReason.Acl); - } - } - else if (modProps.Keys.Any(x => _d.Contains(x))) - { - // Only data has changed. Don't nuke trees, update corresponding cache entries instead. - var keys = cache.Keys(CategoryService.CATEGORY_TREE_PATTERN_KEY).ToArray(); - foreach (var key in keys) - { - var tree = cache.Get>(key); - if (tree != null) - { - var node = tree.SelectNodeById(entity.Id); - if (node != null) - { - var value = node.Value as CategoryNode; - if (value == null) - { - // Cannot update. Nuke tree. - cache.Remove(key); - } - else - { - value.Name = category.Name; + if (entity is Product) + { + var modProps = _services.DbContext.GetModifiedProperties(entity); + var toxicPropNames = Product.GetVisibilityAffectingPropertyNames(); + if (modProps.Keys.Any(x => toxicPropNames.Contains(x))) + { + // No eviction, just notification + PublishEvent(CategoryTreeChangeReason.ElementCounts); + } + } + else if (entity is ProductCategory) + { + var modProps = _services.DbContext.GetModifiedProperties(entity); + if (modProps.ContainsKey("CategoryId")) + { + // No eviction, just notification + PublishEvent(CategoryTreeChangeReason.ElementCounts); + } + } + else if (entity is Category category) + { + var modProps = _services.DbContext.GetModifiedProperties(entity); + + if (modProps.Keys.Any(x => _h.Contains(x))) + { + // Hierarchy affecting properties has changed. Nuke each tree. + cache.RemoveByPattern(CategoryService.CATEGORY_TREE_PATTERN_KEY); + PublishEvent(CategoryTreeChangeReason.Hierarchy); + _invalidated = true; + } + else if (modProps.Keys.Any(x => _a.Contains(x))) + { + if (modProps.ContainsKey("LimitedToStores")) + { + // Don't nuke store agnostic trees + cache.RemoveByPattern(BuildCacheKeyPattern("*", "*", "[^0]*")); + PublishEvent(CategoryTreeChangeReason.StoreMapping); + } + if (modProps.ContainsKey("SubjectToAcl")) + { + // Don't nuke ACL agnostic trees + cache.RemoveByPattern(BuildCacheKeyPattern("*", "[^0]*", "*")); + PublishEvent(CategoryTreeChangeReason.Acl); + } + } + else if (modProps.Keys.Any(x => _d.Contains(x))) + { + // Only data has changed. Don't nuke trees, update corresponding cache entries instead. + var keys = cache.Keys(CategoryService.CATEGORY_TREE_PATTERN_KEY).ToArray(); + foreach (var key in keys) + { + var tree = cache.Get>(key); + if (tree != null) + { + var node = tree.SelectNodeById(entity.Id); + if (node != null) + { + var value = node.Value as CategoryNode; + if (value == null) + { + // Cannot update. Nuke tree. + cache.Remove(key); + } + else + { + value.Name = category.Name; value.ExternalLink = category.ExternalLink; - value.Alias = category.Alias; - value.PictureId = category.PictureId; - value.BadgeText = category.BadgeText; - value.BadgeStyle = category.BadgeStyle; + value.Alias = category.Alias; + value.MediaFileId = category.MediaFileId; + value.BadgeText = category.BadgeText; + value.BadgeStyle = category.BadgeStyle; - // Persist to cache store - cache.Put(key, tree, CategoryService.CategoryTreeCacheDuration); - } - } - } - } + // Persist to cache store + cache.Put(key, tree, CategoryService.CategoryTreeCacheDuration); + } + } + } + } - // Publish event only once - PublishEvent(CategoryTreeChangeReason.Data); - } - } - else - { - throw new NotSupportedException(); - } - } + // Publish event only once + PublishEvent(CategoryTreeChangeReason.Data); + } + } + else + { + throw new NotSupportedException(); + } + } - public void OnAfterSave(IHookedEntity entry) - { - if (_invalidated) - return; + public void OnAfterSave(IHookedEntity entry) + { + if (_invalidated) + return; - if (!_candidateTypes.Contains(entry.EntityType)) - throw new NotSupportedException(); + if (!_candidateTypes.Contains(entry.EntityType)) + throw new NotSupportedException(); - // INFO: Acl & StoreMapping affect element counts + // INFO: Acl & StoreMapping affect element counts - var cache = _services.Cache; - var isNewOrDeleted = entry.InitialState == EntityState.Added || entry.InitialState == EntityState.Deleted; - var entity = entry.Entity; + var cache = _services.Cache; + var isNewOrDeleted = entry.InitialState == EntityState.Added || entry.InitialState == EntityState.Deleted; + var entity = entry.Entity; - if (entity is Product) - { - // INFO: 'Modified' case already handled in 'OnBeforeSave()' - if (entry.InitialState == EntityState.Deleted || (entry.InitialState == EntityState.Added && ((Product)entity).Published)) - { - // No eviction, just notification, but for PUBLISHED products only - PublishEvent(CategoryTreeChangeReason.ElementCounts); - } - } - else if (entity is ProductCategory && isNewOrDeleted) - { - // INFO: 'Modified' case already handled in 'OnBeforeSave()' - // New or deleted product category mappings affect counts - PublishEvent(CategoryTreeChangeReason.ElementCounts); - } - else if (entity is Category && isNewOrDeleted) - { - // INFO: 'Modified' case already handled in 'OnBeforeSave()' - // Hierarchy affecting change, nuke all. - cache.RemoveByPattern(CategoryService.CATEGORY_TREE_PATTERN_KEY); - PublishEvent(CategoryTreeChangeReason.Hierarchy); - _invalidated = true; - } - else if (entity is Setting) - { - var name = (entity as Setting).Name.ToLowerInvariant(); - if (name == "catalogsettings.showcategoryproductnumber" || name == "catalogsettings.showcategoryproductnumberincludingsubcategories") - { - PublishEvent(CategoryTreeChangeReason.ElementCounts); - } - } - else if (entity is Language && entry.InitialState == EntityState.Deleted) - { - PublishEvent(CategoryTreeChangeReason.Localization); - } - else if (entity is LocalizedProperty) - { - var lp = entity as LocalizedProperty; - var key = lp.LocaleKey; - if (lp.LocaleKeyGroup == "Category" && (key == "Name" || key == "BadgeText")) - { - PublishEvent(CategoryTreeChangeReason.Localization); - } - } - else if (entity is StoreMapping) - { - var stm = entity as StoreMapping; - if (stm.EntityName == "Product") - { - PublishEvent(CategoryTreeChangeReason.ElementCounts); - } - else if (stm.EntityName == "Category") - { - // Don't nuke store agnostic trees - cache.RemoveByPattern(BuildCacheKeyPattern("*", "*", "[^0]*")); - PublishEvent(CategoryTreeChangeReason.StoreMapping); - } - } - else if (entity is AclRecord) - { - var acl = entity as AclRecord; - if (!acl.IsIdle) - { - if (acl.EntityName == "Product") - { - PublishEvent(CategoryTreeChangeReason.ElementCounts); - } - else if (acl.EntityName == "Category") - { - // Don't nuke ACL agnostic trees - cache.RemoveByPattern(BuildCacheKeyPattern("*", "[^0]*", "*")); - PublishEvent(CategoryTreeChangeReason.Acl); - } - } - } - } + if (entity is Product) + { + // INFO: 'Modified' case already handled in 'OnBeforeSave()' + if (entry.InitialState == EntityState.Deleted || (entry.InitialState == EntityState.Added && ((Product)entity).Published)) + { + // No eviction, just notification, but for PUBLISHED products only + PublishEvent(CategoryTreeChangeReason.ElementCounts); + } + } + else if (entity is ProductCategory && isNewOrDeleted) + { + // INFO: 'Modified' case already handled in 'OnBeforeSave()' + // New or deleted product category mappings affect counts + PublishEvent(CategoryTreeChangeReason.ElementCounts); + } + else if (entity is Category && isNewOrDeleted) + { + // INFO: 'Modified' case already handled in 'OnBeforeSave()' + // Hierarchy affecting change, nuke all. + cache.RemoveByPattern(CategoryService.CATEGORY_TREE_PATTERN_KEY); + PublishEvent(CategoryTreeChangeReason.Hierarchy); + _invalidated = true; + } + else if (entity is Setting) + { + var name = (entity as Setting).Name.ToLowerInvariant(); + if (name == "catalogsettings.showcategoryproductnumber" || name == "catalogsettings.showcategoryproductnumberincludingsubcategories") + { + PublishEvent(CategoryTreeChangeReason.ElementCounts); + } + } + else if (entity is Language && entry.InitialState == EntityState.Deleted) + { + PublishEvent(CategoryTreeChangeReason.Localization); + } + else if (entity is LocalizedProperty) + { + var lp = entity as LocalizedProperty; + var key = lp.LocaleKey; + if (lp.LocaleKeyGroup == "Category" && (key == "Name" || key == "BadgeText")) + { + PublishEvent(CategoryTreeChangeReason.Localization); + } + } + else if (entity is StoreMapping) + { + var stm = entity as StoreMapping; + if (stm.EntityName == "Product") + { + PublishEvent(CategoryTreeChangeReason.ElementCounts); + } + else if (stm.EntityName == "Category") + { + // Don't nuke store agnostic trees + cache.RemoveByPattern(BuildCacheKeyPattern("*", "*", "[^0]*")); + PublishEvent(CategoryTreeChangeReason.StoreMapping); + } + } + else if (entity is AclRecord) + { + var acl = entity as AclRecord; + if (!acl.IsIdle) + { + if (acl.EntityName == "Product") + { + PublishEvent(CategoryTreeChangeReason.ElementCounts); + } + else if (acl.EntityName == "Category") + { + // Don't nuke ACL agnostic trees + cache.RemoveByPattern(BuildCacheKeyPattern("*", "[^0]*", "*")); + PublishEvent(CategoryTreeChangeReason.Acl); + } + } + } + } - public void HandleEvent(IndexingCompletedEvent message) - { - if (message.IndexInfo.IsModified) - { - PublishEvent(CategoryTreeChangeReason.ElementCounts); - } - } + public void HandleEvent(IndexingCompletedEvent message) + { + if (message.IndexInfo.IsModified) + { + PublishEvent(CategoryTreeChangeReason.ElementCounts); + } + } - private void PublishEvent(CategoryTreeChangeReason reason) - { - if (_handledReasons[(int)reason] == false) - { - _services.EventPublisher.Publish(new CategoryTreeChangedEvent(reason)); - _handledReasons[(int)reason] = true; - } - } + private void PublishEvent(CategoryTreeChangeReason reason) + { + if (_handledReasons[(int)reason] == false) + { + _services.EventPublisher.Publish(new CategoryTreeChangedEvent(reason)); + _handledReasons[(int)reason] = true; + } + } - private string BuildCacheKeyPattern(string includeHiddenToken = "*", string rolesToken = "*", string storeToken = "*") - { - return CategoryService.CATEGORY_TREE_KEY.FormatInvariant(includeHiddenToken, rolesToken, storeToken); - } + private string BuildCacheKeyPattern(string includeHiddenToken = "*", string rolesToken = "*", string storeToken = "*") + { + return CategoryService.CATEGORY_TREE_KEY.FormatInvariant(includeHiddenToken, rolesToken, storeToken); + } - public void OnBeforeSaveCompleted() - { - } + public void OnBeforeSaveCompleted() + { + } - public void OnAfterSaveCompleted() - { - } - } + public void OnAfterSaveCompleted() + { + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/CompareProductsService.cs b/src/Libraries/SmartStore.Services/Catalog/CompareProductsService.cs index 6368d7dae5..1d5730b812 100644 --- a/src/Libraries/SmartStore.Services/Catalog/CompareProductsService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/CompareProductsService.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Web; using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Customers; using SmartStore.Services.Search; namespace SmartStore.Services.Catalog @@ -16,16 +17,19 @@ public partial class CompareProductsService : ICompareProductsService private readonly HttpContextBase _httpContext; private readonly IProductService _productService; - private readonly ICatalogSearchService _catalogSearchService; - - public CompareProductsService( - HttpContextBase httpContext, - IProductService productService, - ICatalogSearchService catalogSearchService) + private readonly ICatalogSearchService _catalogSearchService; + private readonly PrivacySettings _privacySettings; + + public CompareProductsService( + HttpContextBase httpContext, + IProductService productService, + ICatalogSearchService catalogSearchService, + PrivacySettings privacySettings) { _httpContext = httpContext; _productService = productService; - _catalogSearchService = catalogSearchService; + _catalogSearchService = catalogSearchService; + _privacySettings = privacySettings; } #region Utilities @@ -66,6 +70,10 @@ public virtual void ClearCompareProducts() { compareCookie.Values.Clear(); compareCookie.Expires = DateTime.Now.AddYears(-1); + compareCookie.HttpOnly = true; + compareCookie.Secure = _httpContext.Request.IsHttps(); + compareCookie.SameSite = compareCookie.Secure ? (SameSiteMode)_privacySettings.SameSiteMode : SameSiteMode.Lax; + _httpContext.Response.Cookies.Set(compareCookie); } } @@ -85,20 +93,19 @@ public virtual IList GetComparedProducts() public virtual int GetComparedProductsCount() { - var productIds = GetComparedProductIds(); - if (productIds.Count == 0) - return 0; + var productIds = GetComparedProductIds(); + if (productIds.Count == 0) + return 0; - var searchQuery = new CatalogSearchQuery() - .VisibleOnly() - .WithProductIds(productIds.ToArray()) - .BuildHits(false); + var searchQuery = new CatalogSearchQuery() + .VisibleOnly() + .WithProductIds(productIds.ToArray()) + .BuildHits(false); - var result = _catalogSearchService.Search(searchQuery); + var result = _catalogSearchService.Search(searchQuery); return result.TotalHitsCount; } - /// /// Removes a product from a "compare products" list /// @@ -112,11 +119,21 @@ public virtual void RemoveProductFromCompareList(int productId) var compareCookie = _httpContext.Request.Cookies.Get(COMPARE_PRODUCTS_COOKIE_NAME); if ((compareCookie == null) || (compareCookie.Values == null)) + { return; + } + compareCookie.Values.Clear(); foreach (int newProductId in newProductIds) + { compareCookie.Values.Add("CompareProductIds", newProductId.ToString()); + } + compareCookie.Expires = DateTime.Now.AddDays(10.0); + compareCookie.HttpOnly = true; + compareCookie.Secure = _httpContext.Request.IsHttps(); + compareCookie.SameSite = compareCookie.Secure ? (SameSiteMode)_privacySettings.SameSiteMode : SameSiteMode.Lax; + _httpContext.Response.Cookies.Set(compareCookie); } @@ -129,21 +146,16 @@ public virtual void AddProductToCompareList(int productId) var oldProductIds = GetComparedProductIds(); var newProductIds = new List(); newProductIds.Add(productId); - - foreach (int oldProductId in oldProductIds) - { - if (oldProductId != productId) - newProductIds.Add(oldProductId); - } - var compareCookie = _httpContext.Request.Cookies.Get(COMPARE_PRODUCTS_COOKIE_NAME); - if (compareCookie == null) - { - compareCookie = new HttpCookie(COMPARE_PRODUCTS_COOKIE_NAME); - compareCookie.HttpOnly = true; - } + foreach (int oldProductId in oldProductIds) + { + if (oldProductId != productId) + newProductIds.Add(oldProductId); + } + var compareCookie = _httpContext.Request.Cookies.Get(COMPARE_PRODUCTS_COOKIE_NAME) ?? new HttpCookie(COMPARE_PRODUCTS_COOKIE_NAME); compareCookie.Values.Clear(); + int maxProducts = 4; int i = 1; foreach (int newProductId in newProductIds) @@ -153,7 +165,12 @@ public virtual void AddProductToCompareList(int productId) break; i++; } + compareCookie.Expires = DateTime.Now.AddDays(10.0); + compareCookie.HttpOnly = true; + compareCookie.Secure = _httpContext.Request.IsHttps(); + compareCookie.SameSite = compareCookie.Secure ? (SameSiteMode)_privacySettings.SameSiteMode : SameSiteMode.Lax; + _httpContext.Response.Cookies.Set(compareCookie); } diff --git a/src/Libraries/SmartStore.Services/Catalog/CopyProductService.cs b/src/Libraries/SmartStore.Services/Catalog/CopyProductService.cs index 15088a1c1f..395b2f0e2e 100644 --- a/src/Libraries/SmartStore.Services/Catalog/CopyProductService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/CopyProductService.cs @@ -1,376 +1,359 @@ using System; using System.Collections.Generic; -using System.Data.Entity; -using System.Globalization; using System.Linq; -using SmartStore.Core.Logging; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.Seo; using SmartStore.Core.Localization; +using SmartStore.Core.Logging; using SmartStore.Services.Localization; using SmartStore.Services.Media; using SmartStore.Services.Search; using SmartStore.Services.Seo; using SmartStore.Services.Stores; -using SmartStore.Core.Domain.Seo; namespace SmartStore.Services.Catalog { public partial class CopyProductService : ICopyProductService { - private readonly IRepository _productRepository; - private readonly IRepository _relatedProductRepository; - private readonly IRepository _crossSellProductRepository; - private readonly ICommonServices _services; - private readonly IProductService _productService; + private readonly IRepository _productRepository; + private readonly IRepository _relatedProductRepository; + private readonly IRepository _crossSellProductRepository; + private readonly ICommonServices _services; + private readonly IProductService _productService; private readonly IProductAttributeService _productAttributeService; private readonly ILanguageService _languageService; private readonly ILocalizedEntityService _localizedEntityService; - private readonly IPictureService _pictureService; private readonly IDownloadService _downloadService; private readonly IProductAttributeParser _productAttributeParser; private readonly IUrlRecordService _urlRecordService; - private readonly IStoreMappingService _storeMappingService; - private readonly ICatalogSearchService _catalogSearchService; - private readonly SeoSettings _seoSettings; - - public CopyProductService( - IRepository productRepository, - IRepository relatedProductRepository, - IRepository crossSellProductRepository, - ICommonServices services, - IProductService productService, + private readonly IStoreMappingService _storeMappingService; + private readonly ICatalogSearchService _catalogSearchService; + private readonly SeoSettings _seoSettings; + + public CopyProductService( + IRepository productRepository, + IRepository relatedProductRepository, + IRepository crossSellProductRepository, + ICommonServices services, + IProductService productService, IProductAttributeService productAttributeService, - ILanguageService languageService, + ILanguageService languageService, ILocalizedEntityService localizedEntityService, - IPictureService pictureService, - IDownloadService downloadService, + IDownloadService downloadService, IProductAttributeParser productAttributeParser, - IUrlRecordService urlRecordService, - IStoreMappingService storeMappingService, - ICatalogSearchService catalogSearchService, - SeoSettings seoSettings) + IUrlRecordService urlRecordService, + IStoreMappingService storeMappingService, + ICatalogSearchService catalogSearchService, + SeoSettings seoSettings) { - _productRepository = productRepository; - _relatedProductRepository = relatedProductRepository; - _crossSellProductRepository = crossSellProductRepository; - _services = services; - _productService = productService; + _productRepository = productRepository; + _relatedProductRepository = relatedProductRepository; + _crossSellProductRepository = crossSellProductRepository; + _services = services; + _productService = productService; _productAttributeService = productAttributeService; _languageService = languageService; _localizedEntityService = localizedEntityService; - _pictureService = pictureService; _downloadService = downloadService; _productAttributeParser = productAttributeParser; - _urlRecordService = urlRecordService; - _storeMappingService = storeMappingService; - _catalogSearchService = catalogSearchService; - _seoSettings = seoSettings; - - T = NullLocalizer.Instance; + _urlRecordService = urlRecordService; + _storeMappingService = storeMappingService; + _catalogSearchService = catalogSearchService; + _seoSettings = seoSettings; } - public Localizer T { get; set; } + public Localizer T { get; set; } = NullLocalizer.Instance; - public virtual Product CopyProduct( - Product product, - string newName, - bool isPublished, - bool copyImages, - bool copyAssociatedProducts = true) + public virtual Product CopyProduct( + Product product, + string newName, + bool isPublished, + bool copyAssociatedProducts = true) { - Guard.NotNull(product, nameof(product)); - Guard.NotEmpty(newName, nameof(newName)); - - using (_services.Chronometer.Step("Copy product " + product.Id)) - { - Product clone = null; - var utcNow = DateTime.UtcNow; - var languages = _languageService.GetAllLanguages(true); - - // Media stuff - int? sampleDownloadId = null; - var clonedPictures = new Dictionary(); // Key = former ID, Value = cloned picture - - using (var scope = new DbContextScope(ctx: _productRepository.Context, - autoCommit: false, - autoDetectChanges: false, - proxyCreation: true, - validateOnSave: false, - forceNoTracking: true, - hooksEnabled: false)) - { - if (product.HasSampleDownload) - { + Guard.NotNull(product, nameof(product)); + Guard.NotEmpty(newName, nameof(newName)); + + using (_services.Chronometer.Step("Copy product " + product.Id)) + { + Product clone = null; + var utcNow = DateTime.UtcNow; + var languages = _languageService.GetAllLanguages(true); + int? sampleDownloadId = null; + + using (var scope = new DbContextScope(ctx: _productRepository.Context, + autoCommit: false, + autoDetectChanges: false, + proxyCreation: true, + validateOnSave: false, + forceNoTracking: true, + hooksEnabled: false)) + { + if (product.HasSampleDownload) + { var sampleDownload = _downloadService.GetDownloadById((int)product.SampleDownloadId); - var sampleDownloadClone = CopyDownload(sampleDownload); + var sampleDownloadClone = CopyDownload(sampleDownload); sampleDownloadId = sampleDownloadClone.Id; } - if (copyImages) - { - clonedPictures = CopyPictures(product, newName); - } - - // Product - clone = new Product - { - ProductTypeId = product.ProductTypeId, - ParentGroupedProductId = product.ParentGroupedProductId, - VisibleIndividually = product.VisibleIndividually, - Name = newName, - ShortDescription = product.ShortDescription, - FullDescription = product.FullDescription, - ProductTemplateId = product.ProductTemplateId, - AdminComment = product.AdminComment, - ShowOnHomePage = product.ShowOnHomePage, - HomePageDisplayOrder = product.HomePageDisplayOrder, - MetaKeywords = product.MetaKeywords, - MetaDescription = product.MetaDescription, - MetaTitle = product.MetaTitle, - AllowCustomerReviews = product.AllowCustomerReviews, - LimitedToStores = product.LimitedToStores, - Sku = product.Sku, - ManufacturerPartNumber = product.ManufacturerPartNumber, - Gtin = product.Gtin, - IsGiftCard = product.IsGiftCard, - GiftCardType = product.GiftCardType, - RequireOtherProducts = product.RequireOtherProducts, - RequiredProductIds = product.RequiredProductIds, - AutomaticallyAddRequiredProducts = product.AutomaticallyAddRequiredProducts, - IsDownload = product.IsDownload, - UnlimitedDownloads = product.UnlimitedDownloads, - MaxNumberOfDownloads = product.MaxNumberOfDownloads, - DownloadExpirationDays = product.DownloadExpirationDays, - DownloadActivationType = product.DownloadActivationType, - HasSampleDownload = product.HasSampleDownload, - SampleDownloadId = sampleDownloadId, - HasUserAgreement = product.HasUserAgreement, - UserAgreementText = product.UserAgreementText, - IsRecurring = product.IsRecurring, - RecurringCycleLength = product.RecurringCycleLength, - RecurringCyclePeriod = product.RecurringCyclePeriod, - RecurringTotalCycles = product.RecurringTotalCycles, - IsShipEnabled = product.IsShipEnabled, - IsFreeShipping = product.IsFreeShipping, - AdditionalShippingCharge = product.AdditionalShippingCharge, - IsEsd = product.IsEsd, - IsTaxExempt = product.IsTaxExempt, - TaxCategoryId = product.TaxCategoryId, - ManageInventoryMethod = product.ManageInventoryMethod, - StockQuantity = product.StockQuantity, - DisplayStockAvailability = product.DisplayStockAvailability, - DisplayStockQuantity = product.DisplayStockQuantity, - MinStockQuantity = product.MinStockQuantity, - LowStockActivityId = product.LowStockActivityId, - NotifyAdminForQuantityBelow = product.NotifyAdminForQuantityBelow, - BackorderMode = product.BackorderMode, - AllowBackInStockSubscriptions = product.AllowBackInStockSubscriptions, - OrderMinimumQuantity = product.OrderMinimumQuantity, - OrderMaximumQuantity = product.OrderMaximumQuantity, - QuantityStep = product.QuantityStep, - QuantiyControlType = product.QuantiyControlType, - HideQuantityControl = product.HideQuantityControl, - AllowedQuantities = product.AllowedQuantities, - DisableBuyButton = product.DisableBuyButton, - DisableWishlistButton = product.DisableWishlistButton, - AvailableForPreOrder = product.AvailableForPreOrder, - CallForPrice = product.CallForPrice, - Price = product.Price, - OldPrice = product.OldPrice, - ProductCost = product.ProductCost, - SpecialPrice = product.SpecialPrice, - SpecialPriceStartDateTimeUtc = product.SpecialPriceStartDateTimeUtc, - SpecialPriceEndDateTimeUtc = product.SpecialPriceEndDateTimeUtc, - CustomerEntersPrice = product.CustomerEntersPrice, - MinimumCustomerEnteredPrice = product.MinimumCustomerEnteredPrice, - MaximumCustomerEnteredPrice = product.MaximumCustomerEnteredPrice, - LowestAttributeCombinationPrice = product.LowestAttributeCombinationPrice, - Weight = product.Weight, - Length = product.Length, - Width = product.Width, - Height = product.Height, - AvailableStartDateTimeUtc = product.AvailableStartDateTimeUtc, - AvailableEndDateTimeUtc = product.AvailableEndDateTimeUtc, - DisplayOrder = product.DisplayOrder, - Published = isPublished, - Deleted = product.Deleted, - IsSystemProduct = product.IsSystemProduct, - DeliveryTimeId = product.DeliveryTimeId, - QuantityUnitId = product.QuantityUnitId, - BasePriceEnabled = product.BasePriceEnabled, - BasePriceMeasureUnit = product.BasePriceMeasureUnit, - BasePriceAmount = product.BasePriceAmount, - BasePriceBaseAmount = product.BasePriceBaseAmount, - BundleTitleText = product.BundleTitleText, - BundlePerItemShipping = product.BundlePerItemShipping, - BundlePerItemPricing = product.BundlePerItemPricing, - BundlePerItemShoppingCart = product.BundlePerItemShoppingCart, - CustomsTariffNumber = product.CustomsTariffNumber, - CountryOfOriginId = product.CountryOfOriginId, - CreatedOnUtc = utcNow, - UpdatedOnUtc = utcNow - }; - - // Category mappings - foreach (var pc in product.ProductCategories) - { - clone.ProductCategories.Add(new ProductCategory - { - CategoryId = pc.CategoryId, - IsFeaturedProduct = pc.IsFeaturedProduct, - DisplayOrder = pc.DisplayOrder - }); - } - - // Manufacturer mappings - foreach (var pm in product.ProductManufacturers) - { - clone.ProductManufacturers.Add(new ProductManufacturer - { - ManufacturerId = pm.ManufacturerId, - IsFeaturedProduct = pm.IsFeaturedProduct, - DisplayOrder = pm.DisplayOrder - }); - } - - // Picture mappings - if (copyImages) - { - foreach (var pp in product.ProductPictures) - { - var pictureClone = clonedPictures.Get(pp.PictureId); - if (pictureClone != null) - { - clone.ProductPictures.Add(new ProductPicture - { - PictureId = pictureClone.Id, - DisplayOrder = pp.DisplayOrder - }); - } - } - } - - // Product specifications - foreach (var psa in product.ProductSpecificationAttributes) - { - clone.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute - { - SpecificationAttributeOptionId = psa.SpecificationAttributeOptionId, - AllowFiltering = psa.AllowFiltering, - ShowOnProductPage = psa.ShowOnProductPage, - DisplayOrder = psa.DisplayOrder - }); - } - - // Tier prices - foreach (var tp in product.TierPrices) - { - clone.TierPrices.Add(new TierPrice - { - StoreId = tp.StoreId, - CustomerRoleId = tp.CustomerRoleId, - Quantity = tp.Quantity, - Price = tp.Price, - CalculationMethod = tp.CalculationMethod - }); - } - - // Discount mapping - foreach (var discount in product.AppliedDiscounts) - { - clone.AppliedDiscounts.Add(discount); - } - - // Tags - foreach (var tag in product.ProductTags) - { - clone.ProductTags.Add(tag); - } - - // >>>>>>> Put clone to db (from here on we need the product clone's ID) - _productRepository.Insert(clone); - Commit(); - - // Related products mapping - foreach (var rp in _productService.GetRelatedProductsByProductId1(product.Id, true)) - { - _relatedProductRepository.Insert(new RelatedProduct - { - ProductId1 = clone.Id, - ProductId2 = rp.ProductId2, - DisplayOrder = rp.DisplayOrder - }); - } - - // CrossSell products mapping - foreach (var csp in _productService.GetCrossSellProductsByProductId1(product.Id, true)) - { - _crossSellProductRepository.Insert(new CrossSellProduct - { - ProductId1 = clone.Id, - ProductId2 = csp.ProductId2 - }); - } - - // Store mapping - var selectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(product); - foreach (var id in selectedStoreIds) - { - _storeMappingService.InsertStoreMapping(clone, id); - } - - // SEO - ProcessSlug(clone); - - // Localization - ProcessLocalization(product, clone, languages); - - // Attr stuff ... - ProcessAttributes(product, clone, newName, copyImages, clonedPictures, languages); - - // update computed properties - clone.HasTierPrices = clone.TierPrices.Count > 0; - clone.HasDiscountsApplied = clone.AppliedDiscounts.Count > 0; - clone.LowestAttributeCombinationPrice = _productAttributeService.GetLowestCombinationPrice(clone.Id); - clone.MainPictureId = clone.ProductPictures.OrderBy(x => x.DisplayOrder).Select(x => x.PictureId).FirstOrDefault(); - - // Associated products - if (copyAssociatedProducts && product.ProductType != ProductType.BundledProduct) - { - ProcessAssociatedProducts(product, clone, isPublished, copyImages); - } - - // Bundle items - ProcessBundleItems(product, clone); + // Product + clone = new Product + { + ProductTypeId = product.ProductTypeId, + ParentGroupedProductId = product.ParentGroupedProductId, + Visibility = product.Visibility, + Condition = product.Condition, + Name = newName, + ShortDescription = product.ShortDescription, + FullDescription = product.FullDescription, + ProductTemplateId = product.ProductTemplateId, + AdminComment = product.AdminComment, + ShowOnHomePage = product.ShowOnHomePage, + HomePageDisplayOrder = product.HomePageDisplayOrder, + MetaKeywords = product.MetaKeywords, + MetaDescription = product.MetaDescription, + MetaTitle = product.MetaTitle, + AllowCustomerReviews = product.AllowCustomerReviews, + LimitedToStores = product.LimitedToStores, + Sku = product.Sku, + ManufacturerPartNumber = product.ManufacturerPartNumber, + Gtin = product.Gtin, + IsGiftCard = product.IsGiftCard, + GiftCardType = product.GiftCardType, + RequireOtherProducts = product.RequireOtherProducts, + RequiredProductIds = product.RequiredProductIds, + AutomaticallyAddRequiredProducts = product.AutomaticallyAddRequiredProducts, + IsDownload = product.IsDownload, + UnlimitedDownloads = product.UnlimitedDownloads, + MaxNumberOfDownloads = product.MaxNumberOfDownloads, + DownloadExpirationDays = product.DownloadExpirationDays, + DownloadActivationType = product.DownloadActivationType, + HasSampleDownload = product.HasSampleDownload, + SampleDownloadId = sampleDownloadId, + HasUserAgreement = product.HasUserAgreement, + UserAgreementText = product.UserAgreementText, + IsRecurring = product.IsRecurring, + RecurringCycleLength = product.RecurringCycleLength, + RecurringCyclePeriod = product.RecurringCyclePeriod, + RecurringTotalCycles = product.RecurringTotalCycles, + IsShipEnabled = product.IsShipEnabled, + IsFreeShipping = product.IsFreeShipping, + AdditionalShippingCharge = product.AdditionalShippingCharge, + IsEsd = product.IsEsd, + IsTaxExempt = product.IsTaxExempt, + TaxCategoryId = product.TaxCategoryId, + ManageInventoryMethod = product.ManageInventoryMethod, + StockQuantity = product.StockQuantity, + DisplayStockAvailability = product.DisplayStockAvailability, + DisplayStockQuantity = product.DisplayStockQuantity, + MinStockQuantity = product.MinStockQuantity, + LowStockActivityId = product.LowStockActivityId, + NotifyAdminForQuantityBelow = product.NotifyAdminForQuantityBelow, + BackorderMode = product.BackorderMode, + AllowBackInStockSubscriptions = product.AllowBackInStockSubscriptions, + OrderMinimumQuantity = product.OrderMinimumQuantity, + OrderMaximumQuantity = product.OrderMaximumQuantity, + QuantityStep = product.QuantityStep, + QuantiyControlType = product.QuantiyControlType, + HideQuantityControl = product.HideQuantityControl, + AllowedQuantities = product.AllowedQuantities, + DisableBuyButton = product.DisableBuyButton, + DisableWishlistButton = product.DisableWishlistButton, + AvailableForPreOrder = product.AvailableForPreOrder, + CallForPrice = product.CallForPrice, + Price = product.Price, + OldPrice = product.OldPrice, + ProductCost = product.ProductCost, + SpecialPrice = product.SpecialPrice, + SpecialPriceStartDateTimeUtc = product.SpecialPriceStartDateTimeUtc, + SpecialPriceEndDateTimeUtc = product.SpecialPriceEndDateTimeUtc, + CustomerEntersPrice = product.CustomerEntersPrice, + MinimumCustomerEnteredPrice = product.MinimumCustomerEnteredPrice, + MaximumCustomerEnteredPrice = product.MaximumCustomerEnteredPrice, + LowestAttributeCombinationPrice = product.LowestAttributeCombinationPrice, + AttributeChoiceBehaviour = product.AttributeChoiceBehaviour, + Weight = product.Weight, + Length = product.Length, + Width = product.Width, + Height = product.Height, + AvailableStartDateTimeUtc = product.AvailableStartDateTimeUtc, + AvailableEndDateTimeUtc = product.AvailableEndDateTimeUtc, + DisplayOrder = product.DisplayOrder, + Published = isPublished, + Deleted = product.Deleted, + IsSystemProduct = product.IsSystemProduct, + DeliveryTimeId = product.DeliveryTimeId, + QuantityUnitId = product.QuantityUnitId, + BasePriceEnabled = product.BasePriceEnabled, + BasePriceMeasureUnit = product.BasePriceMeasureUnit, + BasePriceAmount = product.BasePriceAmount, + BasePriceBaseAmount = product.BasePriceBaseAmount, + BundleTitleText = product.BundleTitleText, + BundlePerItemShipping = product.BundlePerItemShipping, + BundlePerItemPricing = product.BundlePerItemPricing, + BundlePerItemShoppingCart = product.BundlePerItemShoppingCart, + CustomsTariffNumber = product.CustomsTariffNumber, + CountryOfOriginId = product.CountryOfOriginId, + CreatedOnUtc = utcNow, + UpdatedOnUtc = utcNow + }; + + // Category mappings + foreach (var pc in product.ProductCategories) + { + clone.ProductCategories.Add(new ProductCategory + { + CategoryId = pc.CategoryId, + IsFeaturedProduct = pc.IsFeaturedProduct, + DisplayOrder = pc.DisplayOrder + }); + } + + // Manufacturer mappings + foreach (var pm in product.ProductManufacturers) + { + clone.ProductManufacturers.Add(new ProductManufacturer + { + ManufacturerId = pm.ManufacturerId, + IsFeaturedProduct = pm.IsFeaturedProduct, + DisplayOrder = pm.DisplayOrder + }); + } + + // Picture mappings + foreach (var pp in product.ProductPictures) + { + clone.ProductPictures.Add(new ProductMediaFile + { + MediaFileId = pp.MediaFileId, + DisplayOrder = pp.DisplayOrder + }); + + if (!clone.MainPictureId.HasValue) + { + clone.MainPictureId = pp.MediaFileId; + } + } + + // Product specifications + foreach (var psa in product.ProductSpecificationAttributes) + { + clone.ProductSpecificationAttributes.Add(new ProductSpecificationAttribute + { + SpecificationAttributeOptionId = psa.SpecificationAttributeOptionId, + AllowFiltering = psa.AllowFiltering, + ShowOnProductPage = psa.ShowOnProductPage, + DisplayOrder = psa.DisplayOrder + }); + } + + // Tier prices + foreach (var tp in product.TierPrices) + { + clone.TierPrices.Add(new TierPrice + { + StoreId = tp.StoreId, + CustomerRoleId = tp.CustomerRoleId, + Quantity = tp.Quantity, + Price = tp.Price, + CalculationMethod = tp.CalculationMethod + }); + clone.HasTierPrices = true; + } + + // Discount mapping + foreach (var discount in product.AppliedDiscounts) + { + clone.AppliedDiscounts.Add(discount); + clone.HasDiscountsApplied = true; + } + + // Tags + foreach (var tag in product.ProductTags) + { + clone.ProductTags.Add(tag); + } + + // >>>>>>> Put clone to db (from here on we need the product clone's ID) + _productRepository.Insert(clone); + Commit(); + + // Related products mapping + foreach (var rp in _productService.GetRelatedProductsByProductId1(product.Id, true)) + { + _relatedProductRepository.Insert(new RelatedProduct + { + ProductId1 = clone.Id, + ProductId2 = rp.ProductId2, + DisplayOrder = rp.DisplayOrder + }); + } + + // CrossSell products mapping + foreach (var csp in _productService.GetCrossSellProductsByProductId1(product.Id, true)) + { + _crossSellProductRepository.Insert(new CrossSellProduct + { + ProductId1 = clone.Id, + ProductId2 = csp.ProductId2 + }); + } + + // Store mapping + var selectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(product); + foreach (var id in selectedStoreIds) + { + _storeMappingService.InsertStoreMapping(clone, id); + } + + // SEO + ProcessSlug(clone); + + // Localization + ProcessLocalization(product, clone, languages); + + // Attributes and attribute combinations. + ProcessAttributes(product, clone, languages); + + // Update computed properties. + clone.LowestAttributeCombinationPrice = _productAttributeService.GetLowestCombinationPrice(clone.Id); + + // Associated products. + if (copyAssociatedProducts && product.ProductType != ProductType.BundledProduct) + { + ProcessAssociatedProducts(product, clone, isPublished); + } + + // Bundle items + ProcessBundleItems(product, clone); // Downloads CopyDownloads(product.Id, clone.Id, "Product"); - + // >>>>>>> Our final commit Commit(); - } + } - return clone; - } + return clone; + } } - private void Commit() - { - _services.DbContext.SaveChanges(); - } + private void Commit() + { + _services.DbContext.SaveChanges(); + } - private Dictionary CopyDownloads(int productId, int cloneId, string entityName = "") - { - var downloads = _downloadService.GetDownloadsFor(productId, entityName); + private Dictionary CopyDownloads(int productId, int cloneId, string entityName = "") + { + var downloads = _downloadService.GetDownloadsFor(productId, entityName); var clonedDownloads = new Dictionary(); if (!downloads.Any()) - { - return null; - } + { + return null; + } using (var scope = new DbContextScope(ctx: _productRepository.Context, autoCommit: true)) { @@ -380,10 +363,10 @@ private Dictionary CopyDownloads(int productId, int cloneId, stri var clone = CopyDownload(download); clonedDownloads[clone.Id] = clone; } - } - - return clonedDownloads; - } + } + + return clonedDownloads; + } private Download CopyDownload(Download download) { @@ -392,10 +375,7 @@ private Download CopyDownload(Download download) DownloadGuid = Guid.NewGuid(), UseDownloadUrl = download.UseDownloadUrl, DownloadUrl = download.DownloadUrl, - ContentType = download.ContentType, - Filename = download.Filename, - Extension = download.Extension, - IsNew = download.IsNew, + MediaFile = download.MediaFile, UpdatedOnUtc = DateTime.UtcNow, EntityId = download.EntityId, EntityName = download.EntityName, @@ -403,309 +383,245 @@ private Download CopyDownload(Download download) FileVersion = download.FileVersion }; - _downloadService.InsertDownload(clone, download.MediaStorage?.Data); + _services.DbContext.Set().Add(clone); + _services.DbContext.SaveChanges(); return clone; } - private Dictionary CopyPictures(Product product, string newProductName) - { - var clonedPictures = new Dictionary(); - var seoFilename = _pictureService.GetPictureSeName(newProductName); - - foreach (var pp in product.ProductPictures) - { - var clone = CopyPicture(pp.Picture, seoFilename); - clonedPictures[pp.PictureId] = clone; - } - - return clonedPictures; - } - - private Picture CopyPicture(Picture picture, string seoFilename) - { - using (var scope = new DbContextScope(ctx: _productRepository.Context, autoCommit: true)) - { - var buffer = _pictureService.LoadPictureBinary(picture); - - var clone = _pictureService.InsertPicture( - buffer, - picture.MimeType, - seoFilename, - true, - picture.Width ?? 0, - picture.Height ?? 0, - false); - - return clone; - } - } - - private void ProcessSlug(Product clone) - { - using (var scope = new DbContextScope(ctx: _productRepository.Context, autoCommit: true)) - { - var slug = clone.ValidateSeName("", clone.Name, true, _urlRecordService, _seoSettings); - _urlRecordService.SaveSlug(clone, slug, 0); - } - } - - private void ProcessLocalization(Product product, Product clone, IEnumerable languages) - { - using (var scope = new DbContextScope(ctx: _productRepository.Context, autoCommit: true)) - { - foreach (var lang in languages) - { - var name = product.GetLocalized(x => x.Name, lang, false, false); - if (!String.IsNullOrEmpty(name)) - _localizedEntityService.SaveLocalizedValue(clone, x => x.Name, name, lang.Id); - - var shortDescription = product.GetLocalized(x => x.ShortDescription, lang, false, false); - if (!String.IsNullOrEmpty(shortDescription)) - _localizedEntityService.SaveLocalizedValue(clone, x => x.ShortDescription, shortDescription, lang.Id); - - var fullDescription = product.GetLocalized(x => x.FullDescription, lang, false, false); - if (!String.IsNullOrEmpty(fullDescription)) - _localizedEntityService.SaveLocalizedValue(clone, x => x.FullDescription, fullDescription, lang.Id); - - var metaKeywords = product.GetLocalized(x => x.MetaKeywords, lang, false, false); - if (!String.IsNullOrEmpty(metaKeywords)) - _localizedEntityService.SaveLocalizedValue(clone, x => x.MetaKeywords, metaKeywords, lang.Id); - - var metaDescription = product.GetLocalized(x => x.MetaDescription, lang, false, false); - if (!String.IsNullOrEmpty(metaDescription)) - _localizedEntityService.SaveLocalizedValue(clone, x => x.MetaDescription, metaDescription, lang.Id); - - var metaTitle = product.GetLocalized(x => x.MetaTitle, lang, false, false); - if (!String.IsNullOrEmpty(metaTitle)) - _localizedEntityService.SaveLocalizedValue(clone, x => x.MetaTitle, metaTitle, lang.Id); - - var bundleTitleText = product.GetLocalized(x => x.BundleTitleText, lang, false, false); - if (!String.IsNullOrEmpty(bundleTitleText)) - _localizedEntityService.SaveLocalizedValue(clone, x => x.BundleTitleText, bundleTitleText, lang.Id); - - // Search engine name. - var slug = clone.ValidateSeName("", name, false, _urlRecordService, _seoSettings, lang.Id); - _urlRecordService.SaveSlug(clone, slug, lang.Id); - } - } - } - - private void ProcessAssociatedProducts(Product product, Product clone, bool isPublished, bool copyImages) - { - var searchQuery = new CatalogSearchQuery().HasParentGroupedProduct(product.Id); - - var query = _catalogSearchService.PrepareQuery(searchQuery); - var associatedProducts = query.OrderBy(p => p.DisplayOrder).ToList(); - - foreach (var associatedProduct in associatedProducts) - { - var associatedProductCopy = CopyProduct(associatedProduct, T("Admin.Common.CopyOf", associatedProduct.Name), isPublished, copyImages, false); - associatedProductCopy.ParentGroupedProductId = clone.Id; - } - } - - private void ProcessBundleItems(Product product, Product clone) - { - var bundledItems = _productService.GetBundleItems(product.Id, true); - - foreach (var bundleItem in bundledItems) - { - var newBundleItem = bundleItem.Item.Clone(); - newBundleItem.BundleProductId = clone.Id; - - _productService.InsertBundleItem(newBundleItem); - - foreach (var itemFilter in bundleItem.Item.AttributeFilters) - { - var newItemFilter = itemFilter.Clone(); - newItemFilter.BundleItemId = newBundleItem.Id; - - _productAttributeService.InsertProductBundleItemAttributeFilter(newItemFilter); - } - } - } - - private void ProcessAttributes( - Product product, - Product clone, - string newName, - bool copyImages, - Dictionary clonedPictures, - IEnumerable languages) - { - // Former attribute id > clone - var pvaMap = new Dictionary(); - - // Former attribute value id > clone - var pvavMap = new Dictionary(); - - var pictureSeName = _pictureService.GetPictureSeName(newName); - - foreach (var pva in product.ProductVariantAttributes) - { - var pvaClone = new ProductVariantAttribute - { - ProductAttributeId = pva.ProductAttributeId, - TextPrompt = pva.TextPrompt, - IsRequired = pva.IsRequired, - AttributeControlTypeId = pva.AttributeControlTypeId, - DisplayOrder = pva.DisplayOrder - }; - - clone.ProductVariantAttributes.Add(pvaClone); - - // Save associated value (used for combinations copying) - pvaMap[pva.Id] = pvaClone; - - // Product variant attribute values - foreach (var pvav in pva.ProductVariantAttributeValues) - { - var pvavClone = new ProductVariantAttributeValue - { - Name = pvav.Name, - Color = pvav.Color, - PriceAdjustment = pvav.PriceAdjustment, - WeightAdjustment = pvav.WeightAdjustment, - IsPreSelected = pvav.IsPreSelected, - DisplayOrder = pvav.DisplayOrder, - ValueTypeId = pvav.ValueTypeId, - LinkedProductId = pvav.LinkedProductId, - Quantity = pvav.Quantity, - PictureId = copyImages ? pvav.PictureId : 0 // we'll clone this later - }; - - pvaClone.ProductVariantAttributeValues.Add(pvavClone); - - // Save associated value (used for combinations copying) - pvavMap.Add(pvav.Id, pvavClone); - } - } - - // >>>>>> Commit - Commit(); - - // Attribute value localization - foreach (var pvav in product.ProductVariantAttributes.SelectMany(x => x.ProductVariantAttributeValues).ToArray()) - { - foreach (var lang in languages) - { - var name = pvav.GetLocalized(x => x.Name, lang, false, false); - if (!String.IsNullOrEmpty(name)) - { - var pvavClone = pvavMap.Get(pvav.Id); - if (pvavClone != null) - { - _localizedEntityService.SaveLocalizedValue(pvavClone, x => x.Name, name, lang.Id); - } - } - } - } - - // Clone attribute value images - if (copyImages) - { - // Reduce value set to those with assigned pictures - var allValueClonesWithPictures = pvavMap.Values.Where(x => x.PictureId > 0).ToArray(); - // Get those pictures for cloning - var allPictures = _pictureService.GetPicturesByIds(allValueClonesWithPictures.Select(x => x.PictureId).ToArray(), true); - - foreach (var pvavClone in allValueClonesWithPictures) - { - var picture = allPictures.FirstOrDefault(x => x.Id == pvavClone.PictureId); - if (picture != null) - { - var pictureClone = CopyPicture(picture, pictureSeName); - clonedPictures[pvavClone.PictureId] = pictureClone; - pvavClone.PictureId = pictureClone.Id; - } - } - } - - // >>>>>> Commit attributes & values - Commit(); - - // attribute combinations - using (var scope = new DbContextScope(lazyLoading: false, forceNoTracking: false)) - { - scope.LoadCollection(product, (Product p) => p.ProductVariantAttributeCombinations); - } - - foreach (var combination in product.ProductVariantAttributeCombinations) - { - // Generate new AttributesXml according to new value IDs - string newAttributesXml = ""; - var parsedProductVariantAttributes = _productAttributeParser.ParseProductVariantAttributes(combination.AttributesXml); - foreach (var oldPva in parsedProductVariantAttributes) - { - if (!pvaMap.ContainsKey(oldPva.Id)) - continue; - - var newPva = pvaMap.Get(oldPva.Id); - - if (newPva == null) - continue; - - var oldPvaValuesStr = _productAttributeParser.ParseValues(combination.AttributesXml, oldPva.Id); - foreach (var oldPvaValueStr in oldPvaValuesStr) - { - if (newPva.ShouldHaveValues()) - { - // attribute values - int oldPvaValue = oldPvaValueStr.Convert(); - if (pvavMap.ContainsKey(oldPvaValue)) - { - var newPvav = pvavMap.Get(oldPvaValue); - if (newPvav != null) - { - newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml, newPva, newPvav.Id.ToString()); - } - } - } - else - { - // just a text - newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml, newPva, oldPvaValueStr); - } - } - } - - var newAssignedPictureIds = new HashSet(); - foreach (var strPicId in combination.AssignedPictureIds.EmptyNull().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) - { - var newPic = clonedPictures.Get(strPicId.Convert()); - if (newPic != null) - { - newAssignedPictureIds.Add(newPic.Id.ToString(CultureInfo.InvariantCulture)); - } - } - - var combinationClone = new ProductVariantAttributeCombination - { - AttributesXml = newAttributesXml, - StockQuantity = combination.StockQuantity, - AllowOutOfStockOrders = combination.AllowOutOfStockOrders, - Sku = combination.Sku, - Gtin = combination.Gtin, - ManufacturerPartNumber = combination.ManufacturerPartNumber, - Price = combination.Price, - AssignedPictureIds = copyImages ? String.Join(",", newAssignedPictureIds) : null, - Length = combination.Length, - Width = combination.Width, - Height = combination.Height, - BasePriceAmount = combination.BasePriceAmount, - BasePriceBaseAmount = combination.BasePriceBaseAmount, - DeliveryTimeId = combination.DeliveryTimeId, - QuantityUnitId = combination.QuantityUnitId, - IsActive = combination.IsActive - //IsDefaultCombination = combination.IsDefaultCombination - }; - - clone.ProductVariantAttributeCombinations.Add(combinationClone); - } - - // >>>>>> Commit combinations - Commit(); - } - } + private void ProcessSlug(Product clone) + { + using (var scope = new DbContextScope(ctx: _productRepository.Context, autoCommit: true)) + { + var slug = clone.ValidateSeName("", clone.Name, true, _urlRecordService, _seoSettings); + _urlRecordService.SaveSlug(clone, slug, 0); + } + } + + private void ProcessLocalization(Product product, Product clone, IEnumerable languages) + { + using (var scope = new DbContextScope(ctx: _productRepository.Context, autoCommit: true)) + { + foreach (var lang in languages) + { + var name = product.GetLocalized(x => x.Name, lang, false, false); + if (!String.IsNullOrEmpty(name)) + _localizedEntityService.SaveLocalizedValue(clone, x => x.Name, name, lang.Id); + + var shortDescription = product.GetLocalized(x => x.ShortDescription, lang, false, false); + if (!String.IsNullOrEmpty(shortDescription)) + _localizedEntityService.SaveLocalizedValue(clone, x => x.ShortDescription, shortDescription, lang.Id); + + var fullDescription = product.GetLocalized(x => x.FullDescription, lang, false, false); + if (!String.IsNullOrEmpty(fullDescription)) + _localizedEntityService.SaveLocalizedValue(clone, x => x.FullDescription, fullDescription, lang.Id); + + var metaKeywords = product.GetLocalized(x => x.MetaKeywords, lang, false, false); + if (!String.IsNullOrEmpty(metaKeywords)) + _localizedEntityService.SaveLocalizedValue(clone, x => x.MetaKeywords, metaKeywords, lang.Id); + + var metaDescription = product.GetLocalized(x => x.MetaDescription, lang, false, false); + if (!String.IsNullOrEmpty(metaDescription)) + _localizedEntityService.SaveLocalizedValue(clone, x => x.MetaDescription, metaDescription, lang.Id); + + var metaTitle = product.GetLocalized(x => x.MetaTitle, lang, false, false); + if (!String.IsNullOrEmpty(metaTitle)) + _localizedEntityService.SaveLocalizedValue(clone, x => x.MetaTitle, metaTitle, lang.Id); + + var bundleTitleText = product.GetLocalized(x => x.BundleTitleText, lang, false, false); + if (!String.IsNullOrEmpty(bundleTitleText)) + _localizedEntityService.SaveLocalizedValue(clone, x => x.BundleTitleText, bundleTitleText, lang.Id); + + // Search engine name. + var slug = clone.ValidateSeName("", name, false, _urlRecordService, _seoSettings, lang.Id); + _urlRecordService.SaveSlug(clone, slug, lang.Id); + } + } + } + + private void ProcessAssociatedProducts(Product product, Product clone, bool isPublished) + { + var searchQuery = new CatalogSearchQuery().HasParentGroupedProduct(product.Id); + + var query = _catalogSearchService.PrepareQuery(searchQuery); + var associatedProducts = query.OrderBy(p => p.DisplayOrder).ToList(); + + foreach (var associatedProduct in associatedProducts) + { + var associatedProductCopy = CopyProduct(associatedProduct, T("Admin.Common.CopyOf", associatedProduct.Name), isPublished, false); + associatedProductCopy.ParentGroupedProductId = clone.Id; + } + } + + private void ProcessBundleItems(Product product, Product clone) + { + var bundledItems = _productService.GetBundleItems(product.Id, true); + + foreach (var bundleItem in bundledItems) + { + var newBundleItem = bundleItem.Item.Clone(); + newBundleItem.BundleProductId = clone.Id; + + _productService.InsertBundleItem(newBundleItem); + + foreach (var itemFilter in bundleItem.Item.AttributeFilters) + { + var newItemFilter = itemFilter.Clone(); + newItemFilter.BundleItemId = newBundleItem.Id; + + _productAttributeService.InsertProductBundleItemAttributeFilter(newItemFilter); + } + } + } + + private void ProcessAttributes(Product product, Product clone, IEnumerable languages) + { + using (var scope = new DbContextScope(lazyLoading: false, forceNoTracking: false)) + { + scope.LoadCollection(product, (Product p) => p.ProductVariantAttributes); + scope.LoadCollection(product, (Product p) => p.ProductVariantAttributeCombinations); + } + + // Former attribute id > clone. + var pvaMap = new Dictionary(); + // Former attribute value id > clone. + var pvavMap = new Dictionary(); + + // Product attributes. + foreach (var pva in product.ProductVariantAttributes) + { + var pvaClone = new ProductVariantAttribute + { + ProductId = clone.Id, + ProductAttributeId = pva.ProductAttributeId, + TextPrompt = pva.TextPrompt, + IsRequired = pva.IsRequired, + AttributeControlTypeId = pva.AttributeControlTypeId, + DisplayOrder = pva.DisplayOrder + }; + _productAttributeService.InsertProductVariantAttribute(pvaClone); + + // Save associated value (used for combinations copying). + pvaMap[pva.Id] = pvaClone; + } + + // >>>>>> Commit attributes. + Commit(); + + // Product variant attribute values. + foreach (var pva in product.ProductVariantAttributes) + { + var pvaClone = pvaMap[pva.Id]; + foreach (var pvav in pva.ProductVariantAttributeValues) + { + var pvavClone = new ProductVariantAttributeValue + { + ProductVariantAttributeId = pvaClone.Id, + Name = pvav.Name, + Color = pvav.Color, + PriceAdjustment = pvav.PriceAdjustment, + WeightAdjustment = pvav.WeightAdjustment, + IsPreSelected = pvav.IsPreSelected, + DisplayOrder = pvav.DisplayOrder, + ValueTypeId = pvav.ValueTypeId, + LinkedProductId = pvav.LinkedProductId, + Quantity = pvav.Quantity, + MediaFileId = pvav.MediaFileId + }; + + _productAttributeService.InsertProductVariantAttributeValue(pvavClone); + + // Save associated value (used for combinations copying) + pvavMap.Add(pvav.Id, pvavClone); + } + } + + // >>>>>> Commit attribute values. + Commit(); + + // Attribute value localization. + foreach (var pvav in product.ProductVariantAttributes.SelectMany(x => x.ProductVariantAttributeValues).ToArray()) + { + foreach (var lang in languages) + { + var name = pvav.GetLocalized(x => x.Name, lang, false, false); + if (!string.IsNullOrEmpty(name)) + { + var pvavClone = pvavMap.Get(pvav.Id); + if (pvavClone != null) + { + _localizedEntityService.SaveLocalizedValue(pvavClone, x => x.Name, name, lang.Id); + } + } + } + } + + // Attribute combinations. + foreach (var combination in product.ProductVariantAttributeCombinations) + { + // Generate new AttributesXml according to new value IDs. + string newAttributesXml = ""; + var parsedProductVariantAttributes = _productAttributeParser.ParseProductVariantAttributes(combination.AttributesXml); + foreach (var oldPva in parsedProductVariantAttributes) + { + if (!pvaMap.ContainsKey(oldPva.Id)) + continue; + + var newPva = pvaMap.Get(oldPva.Id); + + if (newPva == null) + continue; + + var oldPvaValuesStr = _productAttributeParser.ParseValues(combination.AttributesXml, oldPva.Id); + foreach (var oldPvaValueStr in oldPvaValuesStr) + { + if (newPva.ShouldHaveValues()) + { + var oldPvaValue = oldPvaValueStr.ToInt(); + if (pvavMap.ContainsKey(oldPvaValue)) + { + var newPvav = pvavMap.Get(oldPvaValue); + if (newPvav != null) + { + newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml, newPva, newPvav.Id.ToString()); + } + } + } + else + { + // Simple text value. + newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml, newPva, oldPvaValueStr); + } + } + } + + var combinationClone = new ProductVariantAttributeCombination + { + ProductId = clone.Id, + AttributesXml = newAttributesXml, + StockQuantity = combination.StockQuantity, + AllowOutOfStockOrders = combination.AllowOutOfStockOrders, + Sku = combination.Sku, + Gtin = combination.Gtin, + ManufacturerPartNumber = combination.ManufacturerPartNumber, + Price = combination.Price, + AssignedMediaFileIds = combination.AssignedMediaFileIds, + Length = combination.Length, + Width = combination.Width, + Height = combination.Height, + BasePriceAmount = combination.BasePriceAmount, + BasePriceBaseAmount = combination.BasePriceBaseAmount, + DeliveryTimeId = combination.DeliveryTimeId, + QuantityUnitId = combination.QuantityUnitId, + IsActive = combination.IsActive + //IsDefaultCombination = combination.IsDefaultCombination + }; + + _productAttributeService.InsertProductVariantAttributeCombination(combinationClone); + } + + // >>>>>> Commit combinations. + Commit(); + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/Extensions/ProductUrlHelper.cs b/src/Libraries/SmartStore.Services/Catalog/Extensions/ProductUrlHelper.cs index ce2b90d9d2..7c7f4df528 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Extensions/ProductUrlHelper.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Extensions/ProductUrlHelper.cs @@ -14,7 +14,7 @@ namespace SmartStore.Services.Catalog.Extensions { - public class ProductUrlHelper + public class ProductUrlHelper { private readonly HttpRequestBase _httpRequest; private readonly ICommonServices _services; diff --git a/src/Libraries/SmartStore.Services/Catalog/Extensions/ProductUrlHelperExtensions.cs b/src/Libraries/SmartStore.Services/Catalog/Extensions/ProductUrlHelperExtensions.cs index d4501c5a6d..249d8eec8c 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Extensions/ProductUrlHelperExtensions.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Extensions/ProductUrlHelperExtensions.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Runtime.CompilerServices; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Orders; using SmartStore.Services.Catalog.Modelling; @@ -7,123 +8,124 @@ namespace SmartStore.Services.Catalog.Extensions { - public static class ProductUrlHelperExtensions - { - /// - /// Creates a product URL including variant query string. - /// - /// Product URL helper - /// Product identifier - /// Product SEO name - /// Bundle item identifier. Use 0 if it's not a bundle item. - /// Variant values - /// Product URL - public static string GetProductUrl( - this ProductUrlHelper helper, - int productId, - string productSeName, - int bundleItemId, - params ProductVariantAttributeValue[] variantValues) - { - Guard.NotZero(productId, nameof(productId)); - - var query = new ProductVariantQuery(); - - foreach (var value in variantValues) - { - var attribute = value.ProductVariantAttribute; - - query.AddVariant(new ProductVariantQueryItem(value.Id.ToString()) - { - ProductId = productId, - BundleItemId = bundleItemId, - AttributeId = attribute.ProductAttributeId, - VariantAttributeId = attribute.Id, - Alias = attribute.ProductAttribute.Alias, - ValueAlias = value.Alias - }); - } - - return helper.GetProductUrl(query, productSeName); - } - - /// - /// Creates a product URL including variant query string. - /// - /// Product URL helper - /// Product entity - /// Variant values - /// Product URL - public static string GetProductUrl( - this ProductUrlHelper helper, - Product product, - params ProductVariantAttributeValue[] variantValues) - { - Guard.NotNull(product, nameof(product)); - - return helper.GetProductUrl(product.Id, product.GetSeName(), 0, variantValues); - } - - /// - /// Creates a product URL including variant query string. - /// - /// Product URL helper - /// Product SEO name - /// Organized shopping cart item - /// Product URL - public static string GetProductUrl( - this ProductUrlHelper helper, - string productSeName, - OrganizedShoppingCartItem cartItem) - { - Guard.NotNull(cartItem, nameof(cartItem)); - - var query = new ProductVariantQuery(); - var product = cartItem.Item.Product; - - if (product.ProductType != ProductType.BundledProduct) - { - helper.DeserializeQuery(query, product.Id, cartItem.Item.AttributesXml); - } - else if (cartItem.ChildItems != null && product.BundlePerItemPricing) - { - foreach (var childItem in cartItem.ChildItems.Where(x => x.Item.Id != cartItem.Item.Id)) - { - helper.DeserializeQuery(query, childItem.Item.ProductId, childItem.Item.AttributesXml, childItem.BundleItemData.Item.Id); - } - } - - return helper.GetProductUrl(query, productSeName); - } - - /// - /// Creates a product URL including variant query string. - /// - /// Product URL helper - /// Product SEO name - /// Order item - /// Product URL - public static string GetProductUrl( - this ProductUrlHelper helper, - string productSeName, - OrderItem orderItem) - { - Guard.NotNull(orderItem, nameof(orderItem)); - - var query = new ProductVariantQuery(); - - if (orderItem.Product.ProductType != ProductType.BundledProduct) - { - helper.DeserializeQuery(query, orderItem.ProductId, orderItem.AttributesXml); - } - else if (orderItem.Product.BundlePerItemPricing && orderItem.BundleData.HasValue()) - { - var bundleData = orderItem.GetBundleData(); - - bundleData.ForEach(x => helper.DeserializeQuery(query, x.ProductId, x.AttributesXml, x.BundleItemId)); - } - - return helper.GetProductUrl(query, productSeName); - } - } + public static class ProductUrlHelperExtensions + { + /// + /// Creates a product URL including variant query string. + /// + /// Product URL helper + /// Product identifier + /// Product SEO name + /// Bundle item identifier. Use 0 if it's not a bundle item. + /// Variant values + /// Product URL + public static string GetProductUrl( + this ProductUrlHelper helper, + int productId, + string productSeName, + int bundleItemId, + params ProductVariantAttributeValue[] variantValues) + { + Guard.NotZero(productId, nameof(productId)); + + var query = new ProductVariantQuery(); + + foreach (var value in variantValues) + { + var attribute = value.ProductVariantAttribute; + + query.AddVariant(new ProductVariantQueryItem(value.Id.ToString()) + { + ProductId = productId, + BundleItemId = bundleItemId, + AttributeId = attribute.ProductAttributeId, + VariantAttributeId = attribute.Id, + Alias = attribute.ProductAttribute.Alias, + ValueAlias = value.Alias + }); + } + + return helper.GetProductUrl(query, productSeName); + } + + /// + /// Creates a product URL including variant query string. + /// + /// Product URL helper + /// Product entity + /// Variant values + /// Product URL + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetProductUrl( + this ProductUrlHelper helper, + Product product, + params ProductVariantAttributeValue[] variantValues) + { + Guard.NotNull(product, nameof(product)); + + return helper.GetProductUrl(product.Id, product.GetSeName(), 0, variantValues); + } + + /// + /// Creates a product URL including variant query string. + /// + /// Product URL helper + /// Product SEO name + /// Organized shopping cart item + /// Product URL + public static string GetProductUrl( + this ProductUrlHelper helper, + string productSeName, + OrganizedShoppingCartItem cartItem) + { + Guard.NotNull(cartItem, nameof(cartItem)); + + var query = new ProductVariantQuery(); + var product = cartItem.Item.Product; + + if (product.ProductType != ProductType.BundledProduct) + { + helper.DeserializeQuery(query, product.Id, cartItem.Item.AttributesXml); + } + else if (cartItem.ChildItems != null && product.BundlePerItemPricing) + { + foreach (var childItem in cartItem.ChildItems.Where(x => x.Item.Id != cartItem.Item.Id)) + { + helper.DeserializeQuery(query, childItem.Item.ProductId, childItem.Item.AttributesXml, childItem.BundleItemData.Item.Id); + } + } + + return helper.GetProductUrl(query, productSeName); + } + + /// + /// Creates a product URL including variant query string. + /// + /// Product URL helper + /// Product SEO name + /// Order item + /// Product URL + public static string GetProductUrl( + this ProductUrlHelper helper, + string productSeName, + OrderItem orderItem) + { + Guard.NotNull(orderItem, nameof(orderItem)); + + var query = new ProductVariantQuery(); + + if (orderItem.Product.ProductType != ProductType.BundledProduct) + { + helper.DeserializeQuery(query, orderItem.ProductId, orderItem.AttributesXml); + } + else if (orderItem.Product.BundlePerItemPricing && orderItem.BundleData.HasValue()) + { + var bundleData = orderItem.GetBundleData(); + + bundleData.ForEach(x => helper.DeserializeQuery(query, x.ProductId, x.AttributesXml, x.BundleItemId)); + } + + return helper.GetProductUrl(query, productSeName); + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/Extensions/ProductVariantQueryExtensions.cs b/src/Libraries/SmartStore.Services/Catalog/Extensions/ProductVariantQueryExtensions.cs index 42b07515b7..da76ad1190 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Extensions/ProductVariantQueryExtensions.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Extensions/ProductVariantQueryExtensions.cs @@ -10,129 +10,130 @@ namespace SmartStore.Services.Catalog.Extensions { - public static class ProductVariantQueryExtensions - { - public static string CreateSelectedAttributesXml( - this ProductVariantQuery query, - int productId, - int bundleItemId, - IEnumerable variantAttributes, - IProductAttributeParser productAttributeParser, - ILocalizationService localization, - IDownloadService downloadService, - CatalogSettings catalogSettings, - HttpRequestBase request, - List warnings) - { - var result = string.Empty; + public static class ProductVariantQueryExtensions + { + public static string CreateSelectedAttributesXml( + this ProductVariantQuery query, + int productId, + int bundleItemId, + IEnumerable variantAttributes, + IProductAttributeParser productAttributeParser, + ILocalizationService localization, + IDownloadService downloadService, + CatalogSettings catalogSettings, + HttpRequestBase request, + List warnings) + { + var result = string.Empty; - foreach (var pva in variantAttributes) - { - var selectedItems = query.Variants.Where(x => - x.ProductId == productId && - x.BundleItemId == bundleItemId && - x.AttributeId == pva.ProductAttributeId && - x.VariantAttributeId == pva.Id); + foreach (var pva in variantAttributes) + { + var selectedItems = query.Variants.Where(x => + x.ProductId == productId && + x.BundleItemId == bundleItemId && + x.AttributeId == pva.ProductAttributeId && + x.VariantAttributeId == pva.Id); - switch (pva.AttributeControlType) - { - case AttributeControlType.DropdownList: - case AttributeControlType.RadioList: - case AttributeControlType.Boxes: - { - var firstItemValue = selectedItems.FirstOrDefault()?.Value; - if (firstItemValue.HasValue()) - { - var selectedAttributeId = firstItemValue.SplitSafe(",").SafeGet(0).ToInt(); - if (selectedAttributeId > 0) - { - result = productAttributeParser.AddProductAttribute(result, pva, selectedAttributeId.ToString()); - } - } - } - break; + switch (pva.AttributeControlType) + { + case AttributeControlType.DropdownList: + case AttributeControlType.RadioList: + case AttributeControlType.Boxes: + { + var firstItemValue = selectedItems.FirstOrDefault()?.Value; + if (firstItemValue.HasValue()) + { + var selectedAttributeId = firstItemValue.SplitSafe(",").SafeGet(0).ToInt(); + if (selectedAttributeId > 0) + { + result = productAttributeParser.AddProductAttribute(result, pva, selectedAttributeId.ToString()); + } + } + } + break; - case AttributeControlType.Checkboxes: - foreach (var item in selectedItems) - { - var selectedAttributeId = item.Value.SplitSafe(",").SafeGet(0).ToInt(); - if (selectedAttributeId > 0) - { - result = productAttributeParser.AddProductAttribute(result, pva, selectedAttributeId.ToString()); - } - } - break; + case AttributeControlType.Checkboxes: + foreach (var item in selectedItems) + { + var selectedAttributeId = item.Value.SplitSafe(",").SafeGet(0).ToInt(); + if (selectedAttributeId > 0) + { + result = productAttributeParser.AddProductAttribute(result, pva, selectedAttributeId.ToString()); + } + } + break; - case AttributeControlType.TextBox: - case AttributeControlType.MultilineTextbox: - { - var selectedValue = string.Join(",", selectedItems.Select(x => x.Value)); - if (selectedValue.HasValue()) - { - result = productAttributeParser.AddProductAttribute(result, pva, selectedValue); - } - } - break; + case AttributeControlType.TextBox: + case AttributeControlType.MultilineTextbox: + { + var selectedValue = string.Join(",", selectedItems.Select(x => x.Value)); + if (selectedValue.HasValue()) + { + result = productAttributeParser.AddProductAttribute(result, pva, selectedValue); + } + } + break; - case AttributeControlType.Datepicker: - { - var firstItemDate = selectedItems.FirstOrDefault()?.Date; - if (firstItemDate.HasValue) - { - result = productAttributeParser.AddProductAttribute(result, pva, firstItemDate.Value.ToString("D")); - } - } - break; + case AttributeControlType.Datepicker: + { + var firstItemDate = selectedItems.FirstOrDefault()?.Date; + if (firstItemDate.HasValue) + { + result = productAttributeParser.AddProductAttribute(result, pva, firstItemDate.Value.ToString("D")); + } + } + break; - case AttributeControlType.FileUpload: - if (request == null) - { - var firstItemValue = selectedItems.FirstOrDefault()?.Value; - Guid downloadGuid; - Guid.TryParse(firstItemValue, out downloadGuid); - var download = downloadService.GetDownloadByGuid(downloadGuid); - if (download != null) - { - download.IsTransient = false; - downloadService.UpdateDownload(download); + case AttributeControlType.FileUpload: + if (request == null) + { + var firstItemValue = selectedItems.FirstOrDefault()?.Value; + Guid.TryParse(firstItemValue, out var downloadGuid); - result = productAttributeParser.AddProductAttribute(result, pva, download.DownloadGuid.ToString()); - } - } - else - { - var postedFile = request.Files[ProductVariantQueryItem.CreateKey(productId, bundleItemId, pva.ProductAttributeId, pva.Id)]; - if (postedFile != null && postedFile.FileName.HasValue()) - { - if (postedFile.ContentLength > catalogSettings.FileUploadMaximumSizeBytes) - { - warnings.Add(localization.GetResource("ShoppingCart.MaximumUploadedFileSize").FormatInvariant((int)(catalogSettings.FileUploadMaximumSizeBytes / 1024))); - } - else - { - var download = new Download - { - DownloadGuid = Guid.NewGuid(), - UseDownloadUrl = false, - DownloadUrl = "", - ContentType = postedFile.ContentType, - Filename = System.IO.Path.GetFileNameWithoutExtension(postedFile.FileName), - Extension = System.IO.Path.GetExtension(postedFile.FileName), - IsNew = true, - UpdatedOnUtc = DateTime.UtcNow - }; + var download = downloadService.GetDownloadByGuid(downloadGuid); + if (download != null) + { + if (download.IsTransient) + { + download.IsTransient = false; + downloadService.UpdateDownload(download); + } - downloadService.InsertDownload(download, postedFile.InputStream != null ? postedFile.InputStream.ToByteArray() : null); + result = productAttributeParser.AddProductAttribute(result, pva, download.DownloadGuid.ToString()); + } + } + else + { + var postedFile = request.Files[ProductVariantQueryItem.CreateKey(productId, bundleItemId, pva.ProductAttributeId, pva.Id)]; + if (postedFile != null && postedFile.FileName.HasValue()) + { + if (postedFile.ContentLength > catalogSettings.FileUploadMaximumSizeBytes) + { + warnings.Add(localization.GetResource("ShoppingCart.MaximumUploadedFileSize").FormatInvariant((int)(catalogSettings.FileUploadMaximumSizeBytes / 1024))); + } + else + { + var download = new Download + { + DownloadGuid = Guid.NewGuid(), + UseDownloadUrl = false, + DownloadUrl = "", + UpdatedOnUtc = DateTime.UtcNow, + EntityId = productId, + EntityName = "ProductAttribute" + }; - result = productAttributeParser.AddProductAttribute(result, pva, download.DownloadGuid.ToString()); - } - } - } - break; - } - } + downloadService.InsertDownload(download, postedFile.InputStream, postedFile.FileName); - return result; - } - } + result = productAttributeParser.AddProductAttribute(result, pva, download.DownloadGuid.ToString()); + } + } + } + break; + } + } + + return result; + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/IBackInStockSubscriptionService.cs b/src/Libraries/SmartStore.Services/Catalog/IBackInStockSubscriptionService.cs index eb802ed5cf..5fb2732286 100644 --- a/src/Libraries/SmartStore.Services/Catalog/IBackInStockSubscriptionService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/IBackInStockSubscriptionService.cs @@ -23,7 +23,7 @@ public partial interface IBackInStockSubscriptionService /// Page size /// Subscriptions IPagedList GetAllSubscriptionsByCustomerId(int customerId, - int storeId, int pageIndex, int pageSize); + int storeId, int pageIndex, int pageSize); /// /// Gets all subscriptions @@ -34,7 +34,7 @@ IPagedList GetAllSubscriptionsByCustomerId(int customer /// Page size /// Subscriptions IPagedList GetAllSubscriptionsByProductId(int productId, - int storeId, int pageIndex, int pageSize); + int storeId, int pageIndex, int pageSize); /// /// Gets all subscriptions diff --git a/src/Libraries/SmartStore.Services/Catalog/ICategoryService.cs b/src/Libraries/SmartStore.Services/Catalog/ICategoryService.cs index b52589cb5e..c7659168e7 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ICategoryService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ICategoryService.cs @@ -44,40 +44,40 @@ void InheritStoresIntoChildren(int categoryId, /// Whether to delete child categories or to set them to no parent. void DeleteCategory(Category category, bool deleteChilds = false); - /// - /// Builds LINQ query for categories - /// - /// Category name filter - /// A value indicating whether to show hidden records - /// Alias filter - /// Store identifier; 0 to load all records - /// Category query - IQueryable BuildCategoriesQuery( - string categoryName = "", - bool showHidden = false, - string alias = null, - int storeId = 0); + /// + /// Builds LINQ query for categories + /// + /// Category name filter + /// A value indicating whether to show hidden records + /// Alias filter + /// Store identifier; 0 to load all records + /// Category query + IQueryable BuildCategoriesQuery( + string categoryName = "", + bool showHidden = false, + string alias = null, + int storeId = 0); - /// - /// Gets all categories - /// - /// Category name filter - /// Page index - /// Page size - /// A value indicating whether to show hidden records - /// Alias filter - /// (Obsolete) Whether to apply instances to the actual categories query. Never applied when is true - /// A value indicating whether categories without parent category in provided category list (source) should be ignored - /// Store identifier; 0 to load all records - /// Categories - IPagedList GetAllCategories( - string categoryName = "", - int pageIndex = 0, - int pageSize = int.MaxValue, - bool showHidden = false, - string alias = null, - bool ignoreCategoriesWithoutExistingParent = true, - int storeId = 0); + /// + /// Gets all categories + /// + /// Category name filter + /// Page index + /// Page size + /// A value indicating whether to show hidden records + /// Alias filter + /// (Obsolete) Whether to apply instances to the actual categories query. Never applied when is true + /// A value indicating whether categories without parent category in provided category list (source) should be ignored + /// Store identifier; 0 to load all records + /// Categories + IPagedList GetAllCategories( + string categoryName = "", + int pageIndex = 0, + int pageSize = int.MaxValue, + bool showHidden = false, + string alias = null, + bool ignoreDetachedCategories = true, + int storeId = 0); /// /// Gets all categories filtered by parent category identifier @@ -92,7 +92,7 @@ IPagedList GetAllCategories( /// /// Categories IList GetAllCategoriesDisplayedOnHomePage(); - + /// /// Gets a category /// @@ -140,10 +140,10 @@ IPagedList GetAllCategories( /// A value indicating whether to show hidden records /// Product a category mapping collection IPagedList GetProductCategoriesByCategoryId( - int categoryId, - int pageIndex, - int pageSize, - bool showHidden = false); + int categoryId, + int pageIndex, + int pageSize, + bool showHidden = false); /// /// Gets a product category mapping collection @@ -153,21 +153,21 @@ IPagedList GetProductCategoriesByCategoryId( /// Product category mapping collection IList GetProductCategoriesByProductId(int productId, bool showHidden = false); - /// - /// Gets product category mappings - /// - /// Product identifiers - /// A value indicating whether to filter categories with applied discounts - /// A value indicating whether to show hidden records - /// Map with product category mappings - Multimap GetProductCategoriesByProductIds(int[] productIds, bool? hasDiscountsApplied = null, bool showHidden = false); + /// + /// Gets product category mappings + /// + /// Product identifiers + /// A value indicating whether to filter categories with applied discounts + /// A value indicating whether to show hidden records + /// Map with product category mappings + Multimap GetProductCategoriesByProductIds(int[] productIds, bool? hasDiscountsApplied = null, bool showHidden = false); - /// - /// Gets product category mappings - /// - /// Category identifiers - /// Map with product category mappings - Multimap GetProductCategoriesByCategoryIds(int[] categoryIds); + /// + /// Gets product category mappings + /// + /// Category identifiers + /// Map with product category mappings + Multimap GetProductCategoriesByCategoryIds(int[] categoryIds); /// /// Gets a product category mapping @@ -188,78 +188,84 @@ IPagedList GetProductCategoriesByCategoryId( /// >Product category mapping void UpdateProductCategory(ProductCategory productCategory); - /// - /// Gets the category trail - /// - /// The category node - /// Trail - IEnumerable GetCategoryTrail(ICategoryNode node); + /// + /// Gets the category trail + /// + /// The category node + /// Trail + IEnumerable GetCategoryTrail(ICategoryNode node); - /// - /// Builds a category breadcrumb (path) for a particular category node - /// - /// The category node - /// The id of language. Pass null to skip localization. - /// How the category alias - if specified - should be appended to the category name (e.g. ({0})) - /// The separator string - /// Category breadcrumb path - string GetCategoryPath( - TreeNode treeNode, - int? languageId = null, - string aliasPattern = null, - string separator = " "); + /// + /// Builds a category breadcrumb (path) for a particular category node + /// + /// The category node + /// The id of language. Pass null to skip localization. + /// How the category alias - if specified - should be appended to the category name (e.g. ({0})) + /// The separator string + /// Category breadcrumb path + string GetCategoryPath( + TreeNode treeNode, + int? languageId = null, + string aliasPattern = null, + string separator = " "); - /// - /// Gets the tree representation of categories - /// - /// Specifies which node to return as root - /// false excludes unpublished and ACL-inaccessible categories - /// > 0 = apply store mapping, 0 to bypass store mapping - /// The category tree representation - /// - /// This method puts the tree result into application cache, so subsequent calls are very fast. - /// Localization is up to the caller because the nodes only contain unlocalized data. - /// Subscribe to the CategoryTreeChanged event if you need to evict cache entries which depend - /// on this method's result. - /// - TreeNode GetCategoryTree( - int rootCategoryId = 0, - bool includeHidden = false, - int storeId = 0); - } + /// + /// Gets the tree representation of categories + /// + /// Specifies which node to return as root + /// false excludes unpublished and ACL-inaccessible categories + /// > 0 = apply store mapping, 0 to bypass store mapping + /// The category tree representation + /// + /// This method puts the tree result into application cache, so subsequent calls are very fast. + /// Localization is up to the caller because the nodes only contain unlocalized data. + /// Subscribe to the CategoryTreeChanged event if you need to evict cache entries which depend + /// on this method's result. + /// + TreeNode GetCategoryTree( + int rootCategoryId = 0, + bool includeHidden = false, + int storeId = 0); - public static class ICategoryServiceExtensions - { - /// - /// Builds a category breadcrumb for a particular product - /// - /// The product - /// The id of language. Pass null to skip localization. - /// The id of store. Pass null to skip store filtering. - /// The separator string - /// Category breadcrumb for product - public static string GetCategoryPath(this ICategoryService categoryService, - Product product, - int? languageId = null, - int? storeId = null, - string separator = " ") - { - Guard.NotNull(product, nameof(product)); + /// + /// Get count of all categories + /// + /// Count + int CountAllCategories(); + } + + public static class ICategoryServiceExtensions + { + /// + /// Builds a category breadcrumb for a particular product + /// + /// The product + /// The id of language. Pass null to skip localization. + /// The id of store. Pass null to skip store filtering. + /// The separator string + /// Category breadcrumb for product + public static string GetCategoryPath(this ICategoryService categoryService, + Product product, + int? languageId = null, + int? storeId = null, + string separator = " ") + { + Guard.NotNull(product, nameof(product)); - string result = string.Empty; + string result = string.Empty; - var pc = categoryService.GetProductCategoriesByProductId(product.Id).FirstOrDefault(); - if (pc != null) - { - var node = categoryService.GetCategoryTree(pc.CategoryId, false, storeId ?? 0); - if (node != null) - { - result = categoryService.GetCategoryPath(node, languageId, null, separator); - } - } + var pc = categoryService.GetProductCategoriesByProductId(product.Id).FirstOrDefault(); + if (pc != null) + { + var node = categoryService.GetCategoryTree(pc.CategoryId, false, storeId ?? 0); + if (node != null) + { + result = categoryService.GetCategoryPath(node, languageId, null, separator); + } + } - return result; - } - } + return result; + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/ICopyProductService.cs b/src/Libraries/SmartStore.Services/Catalog/ICopyProductService.cs index 287d69836b..3d02e61910 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ICopyProductService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ICopyProductService.cs @@ -1,27 +1,24 @@ - using SmartStore.Core.Domain.Catalog; namespace SmartStore.Services.Catalog { /// - /// Copy product service + /// Copy product service. /// public partial interface ICopyProductService { - /// - /// Creates a copy of a product with all it's dependencies - /// - /// The product to copy - /// The name of the product duplicate - /// A value indicating whether the product duplicate should be published - /// A value indicating whether the product images should be copied - /// A value indicating whether the copy associated products - /// Product copy - Product CopyProduct( - Product product, - string newName, - bool isPublished, - bool copyImages, - bool copyAssociatedProducts = true); + /// + /// Creates a duplicate of a product with all it's dependencies. + /// + /// The product to copy. + /// The name of the product duplicate. + /// A value indicating whether the product duplicate should be published. + /// A value indicating whether to copy associated products. + /// Product duplicate. + Product CopyProduct( + Product product, + string newName, + bool isPublished, + bool copyAssociatedProducts = true); } } diff --git a/src/Libraries/SmartStore.Services/Catalog/IManufacturerService.cs b/src/Libraries/SmartStore.Services/Catalog/IManufacturerService.cs index f8127a3d2c..480c9196b5 100644 --- a/src/Libraries/SmartStore.Services/Catalog/IManufacturerService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/IManufacturerService.cs @@ -6,10 +6,10 @@ namespace SmartStore.Services.Catalog { - /// - /// Manufacturer service - /// - public partial interface IManufacturerService + /// + /// Manufacturer service + /// + public partial interface IManufacturerService { /// /// Deletes a manufacturer @@ -17,13 +17,13 @@ public partial interface IManufacturerService /// Manufacturer void DeleteManufacturer(Manufacturer manufacturer); - /// - /// Get manufacturer query - /// - /// A value indicating whether to show hidden records - /// Store identifier - /// Manufacturer query - IQueryable GetManufacturers(bool showHidden = false, int storeId = 0); + /// + /// Get manufacturer query + /// + /// A value indicating whether to show hidden records + /// Store identifier + /// Manufacturer query + IQueryable GetManufacturers(bool showHidden = false, int storeId = 0); /// /// Gets all manufacturers @@ -32,25 +32,25 @@ public partial interface IManufacturerService /// Manufacturer collection IList GetAllManufacturers(bool showHidden = false); - /// - /// Gets all manufacturers - /// - /// Manufacturer name - /// Whether to filter result by store identifier - /// A value indicating whether to show hidden records - /// Manufacturer collection - IList GetAllManufacturers(string manufacturerName, int storeId = 0, bool showHidden = false); - - /// - /// Gets all manufacturers - /// - /// Manufacturer name - /// Page index - /// Page size - /// Whether to filter result by store identifier - /// A value indicating whether to show hidden records - /// Manufacturers - IPagedList GetAllManufacturers(string manufacturerName, + /// + /// Gets all manufacturers + /// + /// Manufacturer name + /// Whether to filter result by store identifier + /// A value indicating whether to show hidden records + /// Manufacturer collection + IList GetAllManufacturers(string manufacturerName, int storeId = 0, bool showHidden = false); + + /// + /// Gets all manufacturers + /// + /// Manufacturer name + /// Page index + /// Page size + /// Whether to filter result by store identifier + /// A value indicating whether to show hidden records + /// Manufacturers + IPagedList GetAllManufacturers(string manufacturerName, int pageIndex, int pageSize, int storeId = 0, bool showHidden = false); /// @@ -79,25 +79,25 @@ IPagedList GetAllManufacturers(string manufacturerName, /// Manufacturer void UpdateManufacturer(Manufacturer manufacturer); - /// - /// Update HasDiscountsApplied property (used for performance optimization) - /// - /// Manufacturer - void UpdateHasDiscountsApplied(Manufacturer manufacturer); + /// + /// Update HasDiscountsApplied property (used for performance optimization) + /// + /// Manufacturer + void UpdateHasDiscountsApplied(Manufacturer manufacturer); + + /// + /// Deletes a product manufacturer mapping + /// + /// Product manufacturer mapping + void DeleteProductManufacturer(ProductManufacturer productManufacturer); - /// - /// Deletes a product manufacturer mapping - /// - /// Product manufacturer mapping - void DeleteProductManufacturer(ProductManufacturer productManufacturer); - /// /// Gets product manufacturer collection /// /// Manufacturer identifier /// Page index /// Page size - /// A value indicating whether to show hidden records + /// Indicates whether to load hidden records. /// Product manufacturer collection IPagedList GetProductManufacturersByManufacturerId(int manufacturerId, int pageIndex, int pageSize, bool showHidden = false); @@ -106,23 +106,25 @@ IPagedList GetProductManufacturersByManufacturerId(int manu /// Gets a product manufacturer mapping collection /// /// Product identifier - /// A value indicating whether to show hidden records + /// Indicates whether to load hidden records. /// Product manufacturer mapping collection IList GetProductManufacturersByProductId(int productId, bool showHidden = false); - /// - /// Get product manufacturers by manufacturer identifiers - /// - /// Manufacturer identifiers - /// Product manufacturers - Multimap GetProductManufacturersByManufacturerIds(int[] manufacturerIds); - - /// - /// Get product manufacturers by product identifiers - /// - /// Product identifiers - /// Product manufacturers - Multimap GetProductManufacturersByProductIds(int[] productIds); + /// + /// Get product manufacturers by manufacturer identifiers + /// + /// Manufacturer identifiers + /// Indicates whether to load hidden records. + /// Product manufacturers + Multimap GetProductManufacturersByManufacturerIds(int[] manufacturerIds, bool showHidden = false); + + /// + /// Get product manufacturers by product identifiers + /// + /// Product identifiers + /// Indicates whether to load hidden records. + /// Product manufacturers + Multimap GetProductManufacturersByProductIds(int[] productIds, bool showHidden = false); /// /// Gets a product manufacturer mapping diff --git a/src/Libraries/SmartStore.Services/Catalog/IPriceCalculationService.cs b/src/Libraries/SmartStore.Services/Catalog/IPriceCalculationService.cs index c0a78cad3c..90daa5573a 100644 --- a/src/Libraries/SmartStore.Services/Catalog/IPriceCalculationService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/IPriceCalculationService.cs @@ -7,196 +7,199 @@ namespace SmartStore.Services.Catalog { - /// - /// Price calculation service - /// - public partial interface IPriceCalculationService + /// + /// Price calculation service + /// + public partial interface IPriceCalculationService { - /// - /// Creates a price calculation context - /// - /// Products. null to lazy load data if required. - /// Customer, null to use current customer. - /// Store identifier, null to use current store. - /// Price calculation context - PriceCalculationContext CreatePriceCalculationContext(IEnumerable products = null, Customer customer = null, int? storeId = null); - - /// - /// Get product special price (is valid) - /// - /// Product - /// Product special price - decimal? GetSpecialPrice(Product product); - - /// - /// Gets the final price - /// - /// Product - /// A value indicating whether include discounts or not for final price computation - /// Final price - decimal GetFinalPrice(Product product, bool includeDiscounts); - - /// - /// Gets the final price - /// - /// Product - /// The customer - /// A value indicating whether include discounts or not for final price computation - /// Final price - decimal GetFinalPrice(Product product, - Customer customer, - bool includeDiscounts); - - /// - /// Gets the final price - /// - /// Product - /// The customer - /// Additional charge - /// A value indicating whether include discounts or not for final price computation - /// Final price - decimal GetFinalPrice(Product product, - Customer customer, - decimal additionalCharge, - bool includeDiscounts); - - /// - /// Gets the final price - /// - /// Product - /// The customer - /// Additional charge - /// A value indicating whether include discounts or not for final price computation - /// Shopping cart item quantity - /// A product bundle item - /// Object with cargo data for better performance - /// Final price - decimal GetFinalPrice(Product product, - Customer customer, - decimal additionalCharge, - bool includeDiscounts, - int quantity, - ProductBundleItemData bundleItem = null, - PriceCalculationContext context = null, + /// + /// Creates a price calculation context + /// + /// Products. null to lazy load data if required. + /// Customer, null to use current customer. + /// Store identifier, null to use current store. + /// Indicates whether to load hidden records. + /// Price calculation context + PriceCalculationContext CreatePriceCalculationContext(IEnumerable products = null, Customer customer = null, int? storeId = null, bool includeHidden = true); + + /// + /// Get product special price (is valid) + /// + /// Product + /// Product special price + decimal? GetSpecialPrice(Product product); + + /// + /// Gets the final price + /// + /// Product + /// A value indicating whether include discounts or not for final price computation + /// Final price + decimal GetFinalPrice(Product product, bool includeDiscounts); + + /// + /// Gets the final price + /// + /// Product + /// The customer + /// A value indicating whether include discounts or not for final price computation + /// Final price + decimal GetFinalPrice(Product product, + Customer customer, + bool includeDiscounts); + + /// + /// Gets the final price + /// + /// Product + /// The customer + /// Additional charge + /// A value indicating whether include discounts or not for final price computation + /// Final price + decimal GetFinalPrice(Product product, + Customer customer, + decimal additionalCharge, + bool includeDiscounts); + + /// + /// Gets the final price + /// + /// Product + /// The customer + /// Additional charge + /// A value indicating whether include discounts or not for final price computation + /// Shopping cart item quantity + /// A product bundle item + /// Object with cargo data for better performance + /// Final price + decimal GetFinalPrice(Product product, + Customer customer, + decimal additionalCharge, + bool includeDiscounts, + int quantity, + ProductBundleItemData bundleItem = null, + PriceCalculationContext context = null, bool isTierPrice = false); - /// - /// Gets the final price including bundle per-item pricing - /// - /// Product - /// Bundle items - /// The customer - /// Additional charge - /// A value indicating whether include discounts or not for final price computation - /// Shopping cart item quantity - /// A product bundle item - /// Object with cargo data for better performance - /// Final price - decimal GetFinalPrice(Product product, - IEnumerable bundleItems, - Customer customer, - decimal additionalCharge, - bool includeDiscounts, - int quantity, - ProductBundleItemData bundleItem = null, - PriceCalculationContext context = null); - - /// - /// Get the lowest possible price for a product. - /// - /// Product - /// The customer - /// Object with cargo data for better performance - /// Whether to display the from message. - /// The lowest price. - decimal GetLowestPrice(Product product, Customer customer, PriceCalculationContext context, out bool displayFromMessage); - - /// - /// Get the lowest price of a grouped product. - /// - /// Grouped product - /// The customer - /// Object with cargo data for better performance - /// Products associated to product - /// The associated product with the lowest price - /// The lowest price. - decimal? GetLowestPrice(Product product, Customer customer, PriceCalculationContext context, IEnumerable associatedProducts, out Product lowestPriceProduct); - - /// - /// Get the initial price including preselected attributes - /// - /// Product - /// The customer - /// The currency - /// Object with cargo data for better performance - /// Preselected price - decimal GetPreselectedPrice(Product product, Customer customer, Currency currency, PriceCalculationContext context); - - /// - /// Gets the product cost - /// - /// Product - /// Shopping cart item attributes in XML - /// Product cost - decimal GetProductCost(Product product, string attributesXml); - - /// - /// Gets discount amount - /// - /// Product - /// Discount amount - decimal GetDiscountAmount(Product product); - - /// - /// Gets discount amount - /// - /// Product - /// The customer - /// Discount amount - decimal GetDiscountAmount(Product product, - Customer customer); - - /// - /// Gets discount amount - /// - /// Product - /// The customer - /// Additional charge - /// Discount amount - decimal GetDiscountAmount(Product product, - Customer customer, - decimal additionalCharge); - - /// - /// Gets discount amount - /// - /// Product - /// The customer - /// Additional charge - /// Applied discount - /// Discount amount - decimal GetDiscountAmount(Product product, - Customer customer, - decimal additionalCharge, - out Discount appliedDiscount); - - /// - /// Gets discount amount - /// - /// Product - /// The customer - /// Additional charge - /// Product quantity - /// Applied discount - /// A product bundle item - /// Object with cargo data for better performance - /// Discount amount - decimal GetDiscountAmount(Product product, - Customer customer, - decimal additionalCharge, - int quantity, - out Discount appliedDiscount, - ProductBundleItemData bundleItem = null, - PriceCalculationContext context = null); + /// + /// Gets the final price including bundle per-item pricing + /// + /// Product + /// Bundle items + /// The customer + /// Additional charge + /// A value indicating whether include discounts or not for final price computation + /// Shopping cart item quantity + /// A product bundle item + /// Object with cargo data for better performance + /// Final price + decimal GetFinalPrice(Product product, + IEnumerable bundleItems, + Customer customer, + decimal additionalCharge, + bool includeDiscounts, + int quantity, + ProductBundleItemData bundleItem = null, + PriceCalculationContext context = null); + + /// + /// Get the lowest possible price for a product. + /// + /// Product + /// The customer + /// Object with cargo data for better performance + /// Whether to display the from message. + /// The lowest price. + decimal GetLowestPrice(Product product, Customer customer, PriceCalculationContext context, out bool displayFromMessage); + + /// + /// Get the lowest price of a grouped product. + /// + /// Grouped product + /// The customer + /// Object with cargo data for better performance + /// Products associated to product + /// The associated product with the lowest price + /// The lowest price. + decimal? GetLowestPrice(Product product, Customer customer, PriceCalculationContext context, IEnumerable associatedProducts, out Product lowestPriceProduct); + + /// + /// Get the initial price including preselected attributes + /// + /// Product + /// The customer + /// The currency + /// Object with cargo data for better performance + /// Preselected price + decimal GetPreselectedPrice(Product product, Customer customer, Currency currency, PriceCalculationContext context); + + /// + /// Gets the product cost + /// + /// Product + /// Shopping cart item attributes in XML + /// Product cost + decimal GetProductCost(Product product, string attributesXml); + + /// + /// Gets discount amount + /// + /// Product + /// Discount amount + decimal GetDiscountAmount(Product product); + + /// + /// Gets discount amount + /// + /// Product + /// The customer + /// Discount amount + decimal GetDiscountAmount(Product product, + Customer customer); + + /// + /// Gets discount amount + /// + /// Product + /// The customer + /// Additional charge + /// Discount amount + decimal GetDiscountAmount(Product product, + Customer customer, + decimal additionalCharge); + + /// + /// Gets discount amount + /// + /// Product + /// The customer + /// Additional charge + /// Applied discount + /// Discount amount + decimal GetDiscountAmount(Product product, + Customer customer, + decimal additionalCharge, + out Discount appliedDiscount); + + /// + /// Gets discount amount + /// + /// Product + /// The customer + /// Additional charge + /// Product quantity + /// Applied discount + /// A product bundle item + /// Object with cargo data for better performance + /// Final product price without discount. + /// Discount amount + decimal GetDiscountAmount(Product product, + Customer customer, + decimal additionalCharge, + int quantity, + out Discount appliedDiscount, + ProductBundleItemData bundleItem = null, + PriceCalculationContext context = null, + decimal? finalPrice = null); /// @@ -214,7 +217,7 @@ decimal GetDiscountAmount(Product product, /// A value indicating whether include discounts or not for price computation /// Shopping cart unit price (one item) decimal GetUnitPrice(OrganizedShoppingCartItem shoppingCartItem, bool includeDiscounts); - + @@ -223,7 +226,7 @@ decimal GetDiscountAmount(Product product, /// /// The shopping cart item /// Discount amount - decimal GetDiscountAmount(OrganizedShoppingCartItem shoppingCartItem); + decimal GetDiscountAmount(OrganizedShoppingCartItem shoppingCartItem); /// /// Gets discount amount @@ -234,12 +237,12 @@ decimal GetDiscountAmount(Product product, decimal GetDiscountAmount(OrganizedShoppingCartItem shoppingCartItem, out Discount appliedDiscount); - /// - /// Gets the price adjustment of a variant attribute value - /// - /// Product variant attribute value - /// Price adjustment of a variant attribute value - decimal GetProductVariantAttributeValuePriceAdjustment(ProductVariantAttributeValue attributeValue, + /// + /// Gets the price adjustment of a variant attribute value + /// + /// Product variant attribute value + /// Price adjustment of a variant attribute value + decimal GetProductVariantAttributeValuePriceAdjustment(ProductVariantAttributeValue attributeValue, Product product, Customer customer, PriceCalculationContext context, int productQuantity = 1); } } diff --git a/src/Libraries/SmartStore.Services/Catalog/IPriceFormatter.cs b/src/Libraries/SmartStore.Services/Catalog/IPriceFormatter.cs index 584c3f40e9..fd814f7798 100644 --- a/src/Libraries/SmartStore.Services/Catalog/IPriceFormatter.cs +++ b/src/Libraries/SmartStore.Services/Catalog/IPriceFormatter.cs @@ -66,17 +66,17 @@ public partial interface IPriceFormatter /// Price string FormatPrice(decimal price, bool showCurrency, Currency targetCurrency, Language language, bool priceIncludesTax); - /// - /// Formats the price - /// - /// Price - /// A value indicating whether to show a currency - /// Target currency - /// Language - /// A value indicating whether price includes tax - /// A value indicating whether to show tax suffix - /// Price - string FormatPrice(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax, bool showTax); + /// + /// Formats the price + /// + /// Price + /// A value indicating whether to show a currency + /// Target currency + /// Language + /// A value indicating whether price includes tax + /// A value indicating whether to show tax suffix + /// Price + string FormatPrice(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax, bool showTax); /// /// Formats the price @@ -110,19 +110,19 @@ public partial interface IPriceFormatter /// Price string FormatShippingPrice(decimal price, bool showCurrency, Currency targetCurrency, Language language, bool priceIncludesTax); - /// - /// Formats the shipping price - /// - /// Price - /// A value indicating whether to show a currency - /// Target currency code - /// Language - /// A value indicating whether price includes tax - /// A value indicating whether to show tax suffix - /// Price - string FormatShippingPrice(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax, bool showTax); - - /// + /// + /// Formats the shipping price + /// + /// Price + /// A value indicating whether to show a currency + /// Target currency code + /// Language + /// A value indicating whether price includes tax + /// A value indicating whether to show tax suffix + /// Price + string FormatShippingPrice(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax, bool showTax); + + /// /// Formats the shipping price /// /// Price @@ -133,7 +133,7 @@ public partial interface IPriceFormatter /// A value indicating whether to show tax suffix /// Price string FormatShippingPrice(decimal price, bool showCurrency, Currency targetCurrency, Language language, bool priceIncludesTax, bool showTax); - + /// /// Formats the shipping price /// @@ -166,17 +166,17 @@ public partial interface IPriceFormatter /// Price string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency, Currency targetCurrency, Language language, bool priceIncludesTax); - /// - /// Formats the payment method additional fee - /// - /// Price - /// A value indicating whether to show a currency - /// Target currency code - /// Language - /// A value indicating whether price includes tax - /// A value indicating whether to show tax suffix - /// Price - string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax, bool showTax); + /// + /// Formats the payment method additional fee + /// + /// Price + /// A value indicating whether to show a currency + /// Target currency code + /// Language + /// A value indicating whether price includes tax + /// A value indicating whether to show tax suffix + /// Price + string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax, bool showTax); /// /// Formats the payment method additional fee diff --git a/src/Libraries/SmartStore.Services/Catalog/IProductAttributeFormatter.cs b/src/Libraries/SmartStore.Services/Catalog/IProductAttributeFormatter.cs index e7e9f66afc..20cc18f050 100644 --- a/src/Libraries/SmartStore.Services/Catalog/IProductAttributeFormatter.cs +++ b/src/Libraries/SmartStore.Services/Catalog/IProductAttributeFormatter.cs @@ -9,29 +9,35 @@ namespace SmartStore.Services.Catalog public partial interface IProductAttributeFormatter { /// - /// Formats attributes + /// Formats attributes. /// /// Product /// Attributes - /// Attributes + /// Formatted attributes. string FormatAttributes(Product product, string attributes); /// - /// Formats attributes + /// Formats attributes. /// - /// Product + /// Product /// Attributes /// Customer - /// Serapator + /// Separator /// A value indicating whether to encode (HTML) values /// A value indicating whether to render prices /// A value indicating whether to render product attributes /// A value indicating whether to render gift card attributes - /// A value indicating whether to HTML hyperink tags could be rendered (if required) - /// Attributes - string FormatAttributes(Product product, string attributes, - Customer customer, string serapator = "
", bool htmlEncode = true, bool renderPrices = true, - bool renderProductAttributes = true, bool renderGiftCardAttributes = true, + /// A value indicating whether to render HTML hyperlinks + /// Formatted attributes. + string FormatAttributes( + Product product, + string attributes, + Customer customer, + string separator = "
", + bool htmlEncode = true, + bool renderPrices = true, + bool renderProductAttributes = true, + bool renderGiftCardAttributes = true, bool allowHyperlinks = true); } } diff --git a/src/Libraries/SmartStore.Services/Catalog/IProductAttributeParser.cs b/src/Libraries/SmartStore.Services/Catalog/IProductAttributeParser.cs index 0017183f46..ce76d9bf37 100644 --- a/src/Libraries/SmartStore.Services/Catalog/IProductAttributeParser.cs +++ b/src/Libraries/SmartStore.Services/Catalog/IProductAttributeParser.cs @@ -1,36 +1,37 @@ +using System; using System.Collections.Generic; using SmartStore.Collections; using SmartStore.Core.Domain.Catalog; namespace SmartStore.Services.Catalog { - /// - /// Product attribute parser interface - /// - public partial interface IProductAttributeParser + /// + /// Product attribute parser interface + /// + public partial interface IProductAttributeParser { - #region Product attributes + #region Product attributes - /// - /// Parses, prefetches & caches all passed attribute definitions for the current request - /// - /// All attribute definitions to prefetch + /// + /// Parses, prefetches & caches all passed attribute definitions for the current request + /// + /// All attribute definitions to prefetch /// Number of fetched attributes - int PrefetchProductVariantAttributes(IEnumerable attributesXml); + int PrefetchProductVariantAttributes(IEnumerable attributesXml); - /// - /// Gets selected product variant attributes as a map of integer ids with their corresponding values. - /// - /// XML formatted attributes - /// The deserialized map - Multimap DeserializeProductVariantAttributes(string attributesXml); + /// + /// Gets selected product variant attributes as a map of integer ids with their corresponding values. + /// + /// XML formatted attributes + /// The deserialized map + Multimap DeserializeProductVariantAttributes(string attributesXml); - /// - /// Gets selected product variant attributes - /// - /// XML formatted attributes - /// Selected product variant attributes - IList ParseProductVariantAttributes(string attributesXml); + /// + /// Gets selected product variant attributes + /// + /// XML formatted attributes + /// Selected product variant attributes + IList ParseProductVariantAttributes(string attributesXml); /// /// Get product variant attribute values @@ -52,13 +53,13 @@ public partial interface IProductAttributeParser /// Collection of product variant attribute values ICollection ParseProductVariantAttributeValues(Multimap attributeCombination, IEnumerable attributes); - /// - /// Gets selected product variant attribute value - /// - /// XML formatted attributes - /// Product variant attribute identifier - /// Product variant attribute value - IList ParseValues(string attributesXml, int productVariantAttributeId); + /// + /// Gets selected product variant attribute value + /// + /// XML formatted attributes + /// Product variant attribute identifier + /// Product variant attribute value + IList ParseValues(string attributesXml, int productVariantAttributeId); /// /// Adds an attribute @@ -69,44 +70,59 @@ public partial interface IProductAttributeParser /// Attributes string AddProductAttribute(string attributesXml, ProductVariantAttribute pva, string value); - /// - /// Creates formatted xml for a list of product variant attribute values - /// - /// The attributes map - /// Attributes XML - string CreateAttributesXml(Multimap attributes); - - /// - /// Are attributes equal - /// - /// The attributes of the first product - /// The attributes of the second product - /// Result - bool AreProductAttributesEqual(string attributeXml1, string attributeXml2); - - /// - /// Finds a product variant attribute combination by attributes stored in XML - /// - /// Product identifier - /// XML formatted attributes - /// Found product variant attribute combination - ProductVariantAttributeCombination FindProductVariantAttributeCombination(int productId, string attributesXml); + /// + /// Creates formatted xml for a list of product variant attribute values + /// + /// The attributes map + /// Attributes XML + string CreateAttributesXml(Multimap attributes); - #endregion + /// + /// Are attributes equal + /// + /// The attributes of the first product + /// The attributes of the second product + /// Result + bool AreProductAttributesEqual(string attributeXml1, string attributeXml2); - #region Gift card attributes + /// + /// Finds a product variant attribute combination by attributes stored in XML + /// + /// Product identifier + /// XML formatted attributes + /// Found product variant attribute combination + ProductVariantAttributeCombination FindProductVariantAttributeCombination(int productId, string attributesXml); - /// - /// Add gift card attrbibutes - /// - /// XML formatted attributes - /// Recipient name - /// Recipient email - /// Sender name - /// Sender email - /// Message - /// Attributes - string AddGiftCardAttribute(string attributesXml, string recipientName, + /// + /// Returns informations about the availability of an attribute combination. + /// + /// Product. + /// All product attributes of the specified product. null to test availability of . + /// The attribute values of the currently selected attribute combination. + /// The current attribute value. null to test availability of . + /// Informations about the attribute combination's availability. null if the combination is available. + CombinationAvailabilityInfo IsCombinationAvailable( + Product product, + IEnumerable attributes, + IEnumerable selectedValues, + ProductVariantAttributeValue currentValue); + + #endregion + + #region Gift card attributes + + /// + /// Add gift card attrbibutes + /// + /// XML formatted attributes + /// Recipient name + /// Recipient email + /// Sender name + /// Sender email + /// Message + /// Attributes + /// Attributes + string AddGiftCardAttribute(string attributesXml, string recipientName, string recipientEmail, string senderName, string senderEmail, string giftCardMessage); /// @@ -124,4 +140,12 @@ void GetGiftCardAttribute(string attributesXml, out string recipientName, #endregion } + + + [Serializable] + public class CombinationAvailabilityInfo + { + public bool IsActive { get; set; } + public bool IsOutOfStock { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/IProductAttributeService.cs b/src/Libraries/SmartStore.Services/Catalog/IProductAttributeService.cs index 7c02afc179..9053d4dec3 100644 --- a/src/Libraries/SmartStore.Services/Catalog/IProductAttributeService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/IProductAttributeService.cs @@ -5,10 +5,10 @@ namespace SmartStore.Services.Catalog { - /// - /// Product attribute service interface - /// - public partial interface IProductAttributeService + /// + /// Product attribute service interface + /// + public partial interface IProductAttributeService { #region Product attributes @@ -19,10 +19,13 @@ public partial interface IProductAttributeService void DeleteProductAttribute(ProductAttribute productAttribute); /// - /// Gets all product attributes + /// Gets all product attributes. /// - /// Product attribute collection - IList GetAllProductAttributes(); + /// Page index, + /// Page size. + /// Indicates whether to load entities tracked or untracked. + /// Product attributes. + IPagedList GetAllProductAttributes(int pageIndex, int pageSize, bool untracked = true); /// /// Gets a product attribute @@ -43,101 +46,101 @@ public partial interface IProductAttributeService /// Product attribute void UpdateProductAttribute(ProductAttribute productAttribute); - /// - /// Gets the export mappings for a given field prefix. - /// - /// The export field prefix, e.g. gmc. - /// A multimap with export field names to ProductAttribute.Id mappings. - Multimap GetExportFieldMappings(string fieldPrefix); - - #endregion - - #region Product attribute options - - /// - /// Gets an attribute option by id - /// - /// Product attribute option identifier - /// Product attribute option - ProductAttributeOption GetProductAttributeOptionById(int id); - - /// - /// Gets all attribute options by options set identifier - /// - /// Attribute options set identifier - /// List of attribute options - IList GetProductAttributeOptionsByOptionsSetId(int optionsSetId); - - /// - /// Gets all attribute options by attribute identifier - /// - /// Attribute identifier - /// List of attribute options - IList GetProductAttributeOptionsByAttributeId(int attributeId); - - /// - /// Deletes an attribute option - /// - /// Product attribute option - void DeleteProductAttributeOption(ProductAttributeOption productAttributeOption); - - /// - /// Inserts an attribute option - /// - /// Product attribute option - void InsertProductAttributeOption(ProductAttributeOption productAttributeOption); - - /// - /// Updates an attribute option - /// - /// Product attribute option - void UpdateProductAttributeOption(ProductAttributeOption productAttributeOption); - - #endregion - - #region Product attribute options sets - - /// - /// Gets an attribute options set by id - /// - /// Product attribute options set identifier - /// Product attribute options set - ProductAttributeOptionsSet GetProductAttributeOptionsSetById(int id); - - /// - /// Gets all attribute options sets by attribute identifier - /// - /// - /// - IList GetProductAttributeOptionsSetsByAttributeId(int productAttributeId); - - /// - /// Deletes an attribute options set - /// - /// Product attribute options set - void DeleteProductAttributeOptionsSet(ProductAttributeOptionsSet productAttributeOptionsSet); - - /// - /// Inserts an attribute options set - /// - /// Product attribute options set - void InsertProductAttributeOptionsSet(ProductAttributeOptionsSet productAttributeOptionsSet); - - /// - /// Updates an attribute options set - /// - /// Product attribute options set - void UpdateProductAttributeOptionsSet(ProductAttributeOptionsSet productAttributeOptionsSet); - - #endregion - - #region Product variant attributes mappings (ProductVariantAttribute) - - /// - /// Deletes a product variant attribute mapping - /// - /// Product variant attribute mapping - void DeleteProductVariantAttribute(ProductVariantAttribute productVariantAttribute); + /// + /// Gets the export mappings for a given field prefix. + /// + /// The export field prefix, e.g. gmc. + /// A multimap with export field names to ProductAttribute.Id mappings. + Multimap GetExportFieldMappings(string fieldPrefix); + + #endregion + + #region Product attribute options + + /// + /// Gets an attribute option by id + /// + /// Product attribute option identifier + /// Product attribute option + ProductAttributeOption GetProductAttributeOptionById(int id); + + /// + /// Gets all attribute options by options set identifier + /// + /// Attribute options set identifier + /// List of attribute options + IList GetProductAttributeOptionsByOptionsSetId(int optionsSetId); + + /// + /// Gets all attribute options by attribute identifier + /// + /// Attribute identifier + /// List of attribute options + IList GetProductAttributeOptionsByAttributeId(int attributeId); + + /// + /// Deletes an attribute option + /// + /// Product attribute option + void DeleteProductAttributeOption(ProductAttributeOption productAttributeOption); + + /// + /// Inserts an attribute option + /// + /// Product attribute option + void InsertProductAttributeOption(ProductAttributeOption productAttributeOption); + + /// + /// Updates an attribute option + /// + /// Product attribute option + void UpdateProductAttributeOption(ProductAttributeOption productAttributeOption); + + #endregion + + #region Product attribute options sets + + /// + /// Gets an attribute options set by id + /// + /// Product attribute options set identifier + /// Product attribute options set + ProductAttributeOptionsSet GetProductAttributeOptionsSetById(int id); + + /// + /// Gets all attribute options sets by attribute identifier + /// + /// + /// + IList GetProductAttributeOptionsSetsByAttributeId(int productAttributeId); + + /// + /// Deletes an attribute options set + /// + /// Product attribute options set + void DeleteProductAttributeOptionsSet(ProductAttributeOptionsSet productAttributeOptionsSet); + + /// + /// Inserts an attribute options set + /// + /// Product attribute options set + void InsertProductAttributeOptionsSet(ProductAttributeOptionsSet productAttributeOptionsSet); + + /// + /// Updates an attribute options set + /// + /// Product attribute options set + void UpdateProductAttributeOptionsSet(ProductAttributeOptionsSet productAttributeOptionsSet); + + #endregion + + #region Product variant attributes mappings (ProductVariantAttribute) + + /// + /// Deletes a product variant attribute mapping + /// + /// Product variant attribute mapping + void DeleteProductVariantAttribute(ProductVariantAttribute productVariantAttribute); /// /// Gets product variant attribute mappings by product identifier @@ -146,13 +149,13 @@ public partial interface IProductAttributeService /// Product variant attribute mapping collection IList GetProductVariantAttributesByProductId(int productId); - /// - /// Gets product variant attribute mappings by multiple product identifiers - /// - /// The product identifiers - /// An optional control type filter. null loads all controls regardless of type. - /// A map with product id as key and a collection of variant attributes as value. - Multimap GetProductVariantAttributesByProductIds(int[] productIds, AttributeControlType? controlType); + /// + /// Gets product variant attribute mappings by multiple product identifiers + /// + /// The product identifiers + /// An optional control type filter. null loads all controls regardless of type. + /// A map with product id as key and a collection of variant attributes as value. + Multimap GetProductVariantAttributesByProductIds(int[] productIds, AttributeControlType? controlType); /// /// Gets a product variant attribute mapping @@ -161,13 +164,13 @@ public partial interface IProductAttributeService /// Product variant attribute mapping ProductVariantAttribute GetProductVariantAttributeById(int productVariantAttributeId); - /// - /// Gets product variant attribute mappings - /// - /// Enumerable of product variant attribute mapping identifiers - /// Collection of already loaded product attribute mappings to reduce database round trips - /// - IList GetProductVariantAttributesByIds(IEnumerable productVariantAttributeIds, IEnumerable attributes = null); + /// + /// Gets product variant attribute mappings + /// + /// Enumerable of product variant attribute mapping identifiers + /// Collection of already loaded product attribute mappings to reduce database round trips + /// + IList GetProductVariantAttributesByIds(IEnumerable productVariantAttributeIds, IEnumerable attributes = null); /// /// Inserts a product variant attribute mapping @@ -181,24 +184,24 @@ public partial interface IProductAttributeService /// The product variant attribute mapping void UpdateProductVariantAttribute(ProductVariantAttribute productVariantAttribute); - /// - /// Copies attribute options (if any) to product variant attribute values. Existing values are ignored (identified by name field). - /// - /// The product variant attribute mapping - /// Product attribute options set identifier - /// Indicates whether to delete all existing product variant attribute values - /// Number of inserted product variant attribute values - int CopyAttributeOptions(ProductVariantAttribute productVariantAttribute, int productAttributeOptionsSetId, bool deleteExistingValues); + /// + /// Copies attribute options (if any) to product variant attribute values. Existing values are ignored (identified by name field). + /// + /// The product variant attribute mapping + /// Product attribute options set identifier + /// Indicates whether to delete all existing product variant attribute values + /// Number of inserted product variant attribute values + int CopyAttributeOptions(ProductVariantAttribute productVariantAttribute, int productAttributeOptionsSetId, bool deleteExistingValues); - #endregion + #endregion - #region Product variant attribute values (ProductVariantAttributeValue) + #region Product variant attribute values (ProductVariantAttributeValue) - /// - /// Deletes a product variant attribute value - /// - /// Product variant attribute value - void DeleteProductVariantAttributeValue(ProductVariantAttributeValue productVariantAttributeValue); + /// + /// Deletes a product variant attribute value + /// + /// Product variant attribute value + void DeleteProductVariantAttributeValue(ProductVariantAttributeValue productVariantAttributeValue); /// /// Gets product variant attribute values by product identifier @@ -243,37 +246,37 @@ public partial interface IProductAttributeService /// Product variant attribute combination void DeleteProductVariantAttributeCombination(ProductVariantAttributeCombination combination); - /// - /// Gets all product variant attribute combinations - /// - /// Product identifier - /// Page index - /// Page size - /// Specifies whether loaded entities should be tracked by the state manager - /// Product variant attribute combination collection - IPagedList GetAllProductVariantAttributeCombinations(int productId, int pageIndex, int pageSize, bool untracked = true); - - /// - /// Gets a distinct list of picture identifiers. - /// Only pictures that are explicitly assigned to combinations are taken into account. - /// - /// Product id - /// Picture ids - IList GetAllProductVariantAttributeCombinationPictureIds(int productId); - - /// - /// Gets product variant attribute combinations by multiple product identifiers - /// - /// The product identifiers - /// A map with product id as key and a collection of product variant attribute combinations as value. - Multimap GetProductVariantAttributeCombinations(int[] productIds); - - /// - /// Get the lowest price of all combinations for a product - /// - /// Product identifier - /// Lowest price - decimal? GetLowestCombinationPrice(int productId); + /// + /// Gets all product variant attribute combinations + /// + /// Product identifier + /// Page index + /// Page size + /// Specifies whether loaded entities should be tracked by the state manager + /// Product variant attribute combination collection + IPagedList GetAllProductVariantAttributeCombinations(int productId, int pageIndex, int pageSize, bool untracked = true); + + /// + /// Gets a distinct list of picture identifiers. + /// Only pictures that are explicitly assigned to combinations are taken into account. + /// + /// Product id + /// Picture ids + IList GetAllProductVariantAttributeCombinationPictureIds(int productId); + + /// + /// Gets product variant attribute combinations by multiple product identifiers + /// + /// The product identifiers + /// A map with product id as key and a collection of product variant attribute combinations as value. + Multimap GetProductVariantAttributeCombinations(int[] productIds); + + /// + /// Get the lowest price of all combinations for a product + /// + /// Product identifier + /// Lowest price + decimal? GetLowestCombinationPrice(int productId); /// /// Gets a product variant attribute combination by identifier @@ -282,12 +285,12 @@ public partial interface IProductAttributeService /// Product variant attribute combination ProductVariantAttributeCombination GetProductVariantAttributeCombinationById(int productVariantAttributeCombinationId); - /// - /// Gets a product variant attribute combination by SKU - /// - /// SKU - /// Product variant attribute combination - ProductVariantAttributeCombination GetProductVariantAttributeCombinationBySku(string sku); + /// + /// Gets a product variant attribute combination by SKU + /// + /// SKU + /// Product variant attribute combination + ProductVariantAttributeCombination GetProductVariantAttributeCombinationBySku(string sku); /// /// Gets a product variant attribute combination by GTIN. @@ -315,10 +318,10 @@ public partial interface IProductAttributeService /// Product variant attribute combination void UpdateProductVariantAttributeCombination(ProductVariantAttributeCombination combination); - /// - /// Creates all variant attributes combinations - /// - void CreateAllProductVariantAttributeCombinations(Product product); + /// + /// Creates all variant attributes combinations + /// + void CreateAllProductVariantAttributeCombinations(Product product); /// /// Gets a value indicating the existence of any attribute combination for a product @@ -327,58 +330,58 @@ public partial interface IProductAttributeService #endregion - #region Product bundle item attribute filter - - /// - /// Inserts a product bundle item attribute filter - /// - /// Product bundle item attribute filter - void InsertProductBundleItemAttributeFilter(ProductBundleItemAttributeFilter attributeFilter); - - /// - /// Updates the product bundle item attribute filter - /// - /// Product bundle item attribute filter - void UpdateProductBundleItemAttributeFilter(ProductBundleItemAttributeFilter attributeFilter); - - /// - /// Deletes a product bundle item attribute filter - /// - /// Product bundle item attribute filter - void DeleteProductBundleItemAttributeFilter(ProductBundleItemAttributeFilter attributeFilter); - - /// - /// Deletes all attribute filters of a bundle item - /// - /// Bundle item - void DeleteProductBundleItemAttributeFilter(ProductBundleItem bundleItem); - - /// - /// Deletes product bundle item attribute filters - /// - /// Attribute identifier - /// Attribute value identifier - void DeleteProductBundleItemAttributeFilter(int attributeId, int attributeValueId); - - /// - /// Deletes product bundle item attribute filters - /// - /// Attribute identifier - void DeleteProductBundleItemAttributeFilter(int attributeId); - - #endregion + #region Product bundle item attribute filter + + /// + /// Inserts a product bundle item attribute filter + /// + /// Product bundle item attribute filter + void InsertProductBundleItemAttributeFilter(ProductBundleItemAttributeFilter attributeFilter); + + /// + /// Updates the product bundle item attribute filter + /// + /// Product bundle item attribute filter + void UpdateProductBundleItemAttributeFilter(ProductBundleItemAttributeFilter attributeFilter); + + /// + /// Deletes a product bundle item attribute filter + /// + /// Product bundle item attribute filter + void DeleteProductBundleItemAttributeFilter(ProductBundleItemAttributeFilter attributeFilter); + + /// + /// Deletes all attribute filters of a bundle item + /// + /// Bundle item + void DeleteProductBundleItemAttributeFilter(ProductBundleItem bundleItem); + + /// + /// Deletes product bundle item attribute filters + /// + /// Attribute identifier + /// Attribute value identifier + void DeleteProductBundleItemAttributeFilter(int attributeId, int attributeValueId); + + /// + /// Deletes product bundle item attribute filters + /// + /// Attribute identifier + void DeleteProductBundleItemAttributeFilter(int attributeId); + + #endregion } - public static class IProductAttributeServiceExtensions - { - /// - /// Gets all product variant attribute combinations - /// - /// Product identifier - /// Product variant attribute combination collection - public static IList GetAllProductVariantAttributeCombinations(this IProductAttributeService service, int productId) - { - return service.GetAllProductVariantAttributeCombinations(productId, 0, int.MaxValue, true); - } - } + public static class IProductAttributeServiceExtensions + { + /// + /// Gets all product variant attribute combinations + /// + /// Product identifier + /// Product variant attribute combination collection + public static IList GetAllProductVariantAttributeCombinations(this IProductAttributeService service, int productId) + { + return service.GetAllProductVariantAttributeCombinations(productId, 0, int.MaxValue, true); + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/IProductService.cs b/src/Libraries/SmartStore.Services/Catalog/IProductService.cs index 89cc3363ae..1f8adbf816 100644 --- a/src/Libraries/SmartStore.Services/Catalog/IProductService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/IProductService.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; using SmartStore.Collections; +using SmartStore.Core; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Discounts; using SmartStore.Core.Domain.Orders; +using SmartStore.Services.Media; namespace SmartStore.Services.Catalog { @@ -13,20 +15,31 @@ namespace SmartStore.Services.Catalog /// public partial interface IProductService { - #region Products + #region Products + /// + /// Get count of all products + /// + /// Count + int CountAllProducts(); - /// - /// Delete a product - /// - /// Product - void DeleteProduct(Product product); + /// + /// Get count of all product variant combinations + /// + /// Count + int CountAllProductVariants(); + + /// + /// Delete a product + /// + /// Product + void DeleteProduct(Product product); /// /// Gets all products displayed on the home page /// /// Product collection IList GetAllProductsDisplayedOnHomePage(); - + /// /// Gets product /// @@ -42,18 +55,18 @@ public partial interface IProductService /// Products IList GetProductsByIds(int[] productIds, ProductLoadFlags flags = ProductLoadFlags.None); - /// - /// Get product by system name. - /// - /// System name - /// Product entity. - Product GetProductBySystemName(string systemName); + /// + /// Get product by system name. + /// + /// System name + /// Product entity. + Product GetProductBySystemName(string systemName); - /// - /// Inserts a product - /// - /// Product - void InsertProduct(Product product); + /// + /// Inserts a product + /// + /// Product + void InsertProduct(Product product); /// /// Updates the product @@ -66,7 +79,7 @@ public partial interface IProductService /// /// Product void UpdateProductReviewTotals(Product product); - + /// /// Get low stock products /// @@ -87,36 +100,50 @@ public partial interface IProductService /// Product Product GetProductByGtin(string gtin); - /// - /// Gets a product by manufacturer part number (MPN) - /// - /// Manufacturer part number - /// Product - Product GetProductByManufacturerPartNumber(string manufacturerPartNumber); - - /// - /// Gets a product by name - /// - /// Product name - /// Product - Product GetProductByName(string name); - - /// - /// Adjusts inventory - /// - /// Shopping cart item - /// A value indicating whether to increase or descrease product stock quantity - /// Adjust inventory result - AdjustInventoryResult AdjustInventory(OrganizedShoppingCartItem sci, bool decrease); + /// + /// Gets a product by manufacturer part number (MPN) + /// + /// Manufacturer part number + /// Product + Product GetProductByManufacturerPartNumber(string manufacturerPartNumber); - /// - /// Adjusts inventory - /// - /// Order item - /// A value indicating whether to increase or descrease product stock quantity - /// Quantity - /// Adjust inventory result - AdjustInventoryResult AdjustInventory(OrderItem orderItem, bool decrease, int quantity); + /// + /// Gets a product by SKU, GTIN or MPN. + /// + /// SKU, GTIN or MPN. + /// Returns the attribute combination if one with the identification number exists. + /// Whether to include hidden products. + /// Specifies whether loaded entities should be tracked by the state manager. + /// Product. + Product GetProductByIdentificationNumber( + string identificationNumber, + out ProductVariantAttributeCombination attributeCombination, + bool includeHidden = false, + bool untracked = true); + + /// + /// Gets a product by name + /// + /// Product name + /// Product + Product GetProductByName(string name); + + /// + /// Adjusts inventory + /// + /// Shopping cart item + /// A value indicating whether to increase or descrease product stock quantity + /// Adjust inventory result + AdjustInventoryResult AdjustInventory(OrganizedShoppingCartItem sci, bool decrease); + + /// + /// Adjusts inventory + /// + /// Order item + /// A value indicating whether to increase or descrease product stock quantity + /// Quantity + /// Adjust inventory result + AdjustInventoryResult AdjustInventory(OrderItem orderItem, bool decrease, int quantity); /// /// Adjusts inventory @@ -134,11 +161,11 @@ public partial interface IProductService /// Product void UpdateHasTierPricesProperty(Product product); - /// - /// Update LowestAttributeCombinationPrice property (used for performance optimization) - /// - /// Product - void UpdateLowestAttributeCombinationPriceProperty(Product product); + /// + /// Update LowestAttributeCombinationPrice property (used for performance optimization) + /// + /// Product + void UpdateLowestAttributeCombinationPriceProperty(Product product); /// /// Update HasDiscountsApplied property (used for performance optimization) @@ -146,12 +173,13 @@ public partial interface IProductService /// Product void UpdateHasDiscountsApplied(Product product); - /// - /// Get product tags by product identifiers - /// - /// Product identifiers - /// Map of product tags - Multimap GetProductTagsByProductIds(int[] productIds); + /// + /// Get product tags by product identifiers + /// + /// Product identifiers + /// Whether to include hidden product tags. + /// Map of product tags + Multimap GetProductTagsByProductIds(int[] productIds, bool includeHidden = false); /// /// Gets products that are assigned to group products. @@ -205,12 +233,12 @@ public partial interface IProductService /// Related product void UpdateRelatedProduct(RelatedProduct relatedProduct); - /// - /// Ensure existence of all mutually related products - /// - /// First product identifier - /// Number of inserted related products - int EnsureMutuallyRelatedProducts(int productId1); + /// + /// Ensure existence of all mutually related products + /// + /// First product identifier + /// Number of inserted related products + int EnsureMutuallyRelatedProducts(int productId1); #endregion @@ -230,20 +258,20 @@ public partial interface IProductService /// Cross-sell product collection IList GetCrossSellProductsByProductId1(int productId1, bool showHidden = false); - /// - /// Gets a cross-sell product collection by many product identifiers - /// - /// A sequence of alpha product identifiers - /// A value indicating whether to show hidden records - /// Cross-sell product collection - IList GetCrossSellProductsByProductIds(IEnumerable productIds, bool showHidden = false); + /// + /// Gets a cross-sell product collection by many product identifiers + /// + /// A sequence of alpha product identifiers + /// A value indicating whether to show hidden records + /// Cross-sell product collection + IList GetCrossSellProductsByProductIds(IEnumerable productIds, bool showHidden = false); - /// - /// Gets a cross-sell product - /// - /// Cross-sell product identifier - /// Cross-sell product - CrossSellProduct GetCrossSellProductById(int crossSellProductId); + /// + /// Gets a cross-sell product + /// + /// Cross-sell product identifier + /// Cross-sell product + CrossSellProduct GetCrossSellProductById(int crossSellProductId); /// /// Inserts a cross-sell product @@ -256,24 +284,24 @@ public partial interface IProductService /// /// Cross-sell product void UpdateCrossSellProduct(CrossSellProduct crossSellProduct); - + /// /// Gets a cross-sells /// /// Shopping cart /// Number of products to return /// Cross-sells - IList GetCrosssellProductsByShoppingCart(IList cart, int numberOfProducts); + IList GetCrosssellProductsByShoppingCart(IList cart, int numberOfProducts); - /// - /// Ensure existence of all mutually cross selling products - /// - /// First product identifier - /// Number of inserted cross selling products - int EnsureMutuallyCrossSellProducts(int productId1); + /// + /// Ensure existence of all mutually cross selling products + /// + /// First product identifier + /// Number of inserted cross selling products + int EnsureMutuallyCrossSellProducts(int productId1); #endregion - + #region Tier prices /// @@ -289,14 +317,14 @@ public partial interface IProductService /// Tier price TierPrice GetTierPriceById(int tierPriceId); - /// - /// Gets tier prices by product identifiers - /// - /// Product identifiers - /// Filter tier prices by customer - /// Filter tier prices by store - /// Map of tier prices - Multimap GetTierPricesByProductIds(int[] productIds, Customer customer = null, int storeId = 0); + /// + /// Gets tier prices by product identifiers + /// + /// Product identifiers + /// Filter tier prices by customer + /// Filter tier prices by store + /// Map of tier prices + Multimap GetTierPricesByProductIds(int[] productIds, Customer customer = null, int storeId = 0); /// /// Inserts a tier price @@ -318,86 +346,91 @@ public partial interface IProductService /// Deletes a product picture /// /// Product picture - void DeleteProductPicture(ProductPicture productPicture); + void DeleteProductPicture(ProductMediaFile productPicture); /// /// Gets a product pictures by product identifier /// /// The product identifier + /// Number of pictures to return. By default value of 0, all pictures are returned. /// Product pictures - IList GetProductPicturesByProductId(int productId); + IList GetProductPicturesByProductId(int productId, int numberOfPictures = 0); - /// - /// Get product pictures by product identifiers - /// - /// Product identifiers - /// Whether to only load the first picture for each product - /// Product pictures - Multimap GetProductPicturesByProductIds(int[] productIds, bool onlyFirstPicture = false); + /// + /// Get product pictures by product identifiers. + /// + /// Product identifiers. + /// Maximum number of pictures to load. + /// Eager loading flags. + /// Product pictures. + Multimap GetProductPicturesByProductIds( + int[] productIds, + int? maxPicturesPerProduct = null, + MediaLoadFlags flags = MediaLoadFlags.None); /// /// Gets a product picture /// /// Product picture identifier /// Product picture - ProductPicture GetProductPictureById(int productPictureId); + ProductMediaFile GetProductPictureById(int productPictureId); /// /// Inserts a product picture /// /// Product picture - void InsertProductPicture(ProductPicture productPicture); + void InsertProductPicture(ProductMediaFile productPicture); /// /// Updates a product picture /// /// Product picture - void UpdateProductPicture(ProductPicture productPicture); + void UpdateProductPicture(ProductMediaFile productPicture); #endregion - #region Bundled products - - /// - /// Inserts a product bundle item - /// - /// Product bundle item - void InsertBundleItem(ProductBundleItem bundleItem); - - /// - /// Updates a product bundle item - /// - /// Product bundle item - void UpdateBundleItem(ProductBundleItem bundleItem); - - /// - /// Deletes a product bundle item - /// - /// Product bundle item - void DeleteBundleItem(ProductBundleItem bundleItem); - - /// - /// Get a product bundle item by item identifier - /// - /// Product bundle item identifier - /// Product bundle item - ProductBundleItem GetBundleItemById(int bundleItemId); - - /// - /// Gets a list of bundle items for a particular product identifier - /// - /// Product identifier - /// A value indicating whether to show hidden records - /// List of bundle items - IList GetBundleItems(int bundleProductId, bool showHidden = false); - - /// - /// Get bundle items by product identifiers - /// - /// Product identifiers - /// A value indicating whether to show hidden records - /// Map of bundle items - Multimap GetBundleItemsByProductIds(int[] productIds, bool showHidden = false); + #region Bundled products + + /// + /// Inserts a product bundle item + /// + /// Product bundle item + void InsertBundleItem(ProductBundleItem bundleItem); + + /// + /// Updates a product bundle item + /// + /// Product bundle item + void UpdateBundleItem(ProductBundleItem bundleItem); + + /// + /// Deletes a product bundle item + /// + /// Product bundle item + void DeleteBundleItem(ProductBundleItem bundleItem); + + /// + /// Get a product bundle item by item identifier + /// + /// Product bundle item identifier + /// Product bundle item + ProductBundleItem GetBundleItemById(int bundleItemId); + + /// + /// Gets a list of bundle items for a particular product identifier + /// + /// Product identifier + /// A value indicating whether to show hidden records + /// List of bundle items + IList GetBundleItems(int bundleProductId, bool showHidden = false); + + /// + /// Get bundle items by product identifiers + /// + /// Product identifiers + /// A value indicating whether to show hidden records + /// Map of bundle items + Multimap GetBundleItemsByProductIds(int[] productIds, bool showHidden = false); /// /// Checks whether a product is a bundle item. @@ -409,23 +442,23 @@ public partial interface IProductService #endregion } - [Flags] - public enum ProductLoadFlags - { - None = 0, - WithCategories = 1 << 0, - WithManufacturers = 1 << 1, - WithPictures = 1 << 2, - WithReviews = 1 << 3, - WithSpecificationAttributes = 1 << 4, - WithAttributes = 1 << 5, - WithAttributeValues = 1 << 7, - WithAttributeCombinations = 1 << 8, - WithTags = 1 << 9, - WithTierPrices = 1 << 10, - WithDiscounts = 1 << 11, - WithBundleItems = 1 << 12, - WithDeliveryTime = 1 << 13, - Full = WithCategories | WithManufacturers | WithPictures | WithReviews | WithSpecificationAttributes | WithAttributes | WithAttributeValues | WithAttributeCombinations | WithTags | WithTierPrices | WithDiscounts | WithBundleItems | WithDeliveryTime - } + [Flags] + public enum ProductLoadFlags + { + None = 0, + WithCategories = 1 << 0, + WithManufacturers = 1 << 1, + WithPictures = 1 << 2, + WithReviews = 1 << 3, + WithSpecificationAttributes = 1 << 4, + WithAttributes = 1 << 5, + WithAttributeValues = 1 << 7, + WithAttributeCombinations = 1 << 8, + WithTags = 1 << 9, + WithTierPrices = 1 << 10, + WithDiscounts = 1 << 11, + WithBundleItems = 1 << 12, + WithDeliveryTime = 1 << 13, + Full = WithCategories | WithManufacturers | WithPictures | WithReviews | WithSpecificationAttributes | WithAttributes | WithAttributeValues | WithAttributeCombinations | WithTags | WithTierPrices | WithDiscounts | WithBundleItems | WithDeliveryTime + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/IProductTagService.cs b/src/Libraries/SmartStore.Services/Catalog/IProductTagService.cs index 937d14b1b7..70476562ae 100644 --- a/src/Libraries/SmartStore.Services/Catalog/IProductTagService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/IProductTagService.cs @@ -6,7 +6,7 @@ namespace SmartStore.Services.Catalog /// /// Product tag service interface /// - public partial interface IProductTagService + public partial interface IProductTagService : IScopedService { /// /// Delete a product tag @@ -17,8 +17,9 @@ public partial interface IProductTagService /// /// Gets all product tags /// + /// Whether to include hidden product tags. /// Product tags - IList GetAllProductTags(); + IList GetAllProductTags(bool includeHidden = false); /// /// Gets all product tag names @@ -32,7 +33,7 @@ public partial interface IProductTagService /// Product tag identifier /// Product tag ProductTag GetProductTagById(int productTagId); - + /// /// Gets product tag by name /// @@ -52,12 +53,20 @@ public partial interface IProductTagService /// Product tag void UpdateProductTag(ProductTag productTag); - /// - /// Get number of products - /// - /// Product tag identifier - /// Store identifier - /// Number of products - int GetProductCount(int productTagId, int storeId); + /// + /// Updates the product tags. + /// + /// Product. + /// New tags for the product. + void UpdateProductTags(Product product, string[] tagNames); + + /// + /// Get number of products + /// + /// Product tag identifier + /// Store identifier + /// Whether to include hidden product tags. + /// Number of products + int GetProductCount(int productTagId, int storeId, bool includeHidden = false); } } diff --git a/src/Libraries/SmartStore.Services/Catalog/ISpecificationAttributeService.cs b/src/Libraries/SmartStore.Services/Catalog/ISpecificationAttributeService.cs index d4546d153d..f6a6d13873 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ISpecificationAttributeService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ISpecificationAttributeService.cs @@ -25,12 +25,12 @@ public partial interface ISpecificationAttributeService /// Specification attribute query IQueryable GetSpecificationAttributes(); - /// - /// Gets specification attributes by identifier - /// - /// Identifiers - /// Specification attribute query - IQueryable GetSpecificationAttributesByIds(int[] ids); + /// + /// Gets specification attributes by identifier + /// + /// Identifiers + /// Specification attribute query + IQueryable GetSpecificationAttributesByIds(int[] ids); /// /// Deletes a specification attribute @@ -62,7 +62,7 @@ public partial interface ISpecificationAttributeService SpecificationAttributeOption GetSpecificationAttributeOptionById(int specificationAttributeOption); /// - /// Gets a specification attribute option by specification attribute id + /// Gets specification attribute options by specification attribute id /// /// The specification attribute identifier /// Specification attribute option diff --git a/src/Libraries/SmartStore.Services/Catalog/Importer/CategoryImporter.cs b/src/Libraries/SmartStore.Services/Catalog/Importer/CategoryImporter.cs index 14daae3178..0e7e928198 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Importer/CategoryImporter.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Importer/CategoryImporter.cs @@ -1,13 +1,16 @@ using System; using System.Collections.Generic; +using System.Data.Entity; using System.IO; using System.Linq; using System.Linq.Expressions; using SmartStore.Core.Async; +using SmartStore.Core.Caching; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Domain.Media; +using SmartStore.Core.IO; using SmartStore.Services.DataExchange.Import; using SmartStore.Services.DataExchange.Import.Events; using SmartStore.Services.Localization; @@ -17,181 +20,196 @@ namespace SmartStore.Services.Catalog.Importer { public class CategoryImporter : EntityImporterBase - { - private readonly IRepository _categoryRepository; - private readonly IRepository _pictureRepository; - private readonly ICategoryTemplateService _categoryTemplateService; - private readonly IPictureService _pictureService; - private readonly ILocalizedEntityService _localizedEntityService; - private readonly FileDownloadManager _fileDownloadManager; - - private static readonly Dictionary>> _localizableProperties = new Dictionary>> - { - { "Name", x => x.Name }, - { "FullName", x => x.FullName }, - { "Description", x => x.Description }, - { "BottomDescription", x => x.BottomDescription }, - { "MetaKeywords", x => x.MetaKeywords }, - { "MetaDescription", x => x.MetaDescription }, - { "MetaTitle", x => x.MetaTitle } - }; - - public CategoryImporter( - IRepository categoryRepository, - IRepository pictureRepository, - ICategoryTemplateService categoryTemplateService, - IPictureService pictureService, - ILocalizedEntityService localizedEntityService, - FileDownloadManager fileDownloadManager) - { - _categoryRepository = categoryRepository; - _pictureRepository = pictureRepository; - _categoryTemplateService = categoryTemplateService; - _pictureService = pictureService; - _localizedEntityService = localizedEntityService; - _fileDownloadManager = fileDownloadManager; - } - - protected override void Import(ImportExecuteContext context) - { - var srcToDestId = new Dictionary(); - - var templateViewPaths = _categoryTemplateService.GetAllCategoryTemplates().ToDictionarySafe(x => x.ViewPath, x => x.Id); - - using (var scope = new DbContextScope(ctx: context.Services.DbContext, hooksEnabled: false, autoDetectChanges: false, proxyCreation: false, validateOnSave: false)) - { - var segmenter = context.DataSegmenter; - - Initialize(context); - - while (context.Abort == DataExchangeAbortion.None && segmenter.ReadNextBatch()) - { - var batch = segmenter.GetCurrentBatch(); - - // Perf: detach all entities - _categoryRepository.Context.DetachAll(true); - - context.SetProgress(segmenter.CurrentSegmentFirstRowIndex - 1, segmenter.TotalRows); - - try - { - ProcessCategories(context, batch, templateViewPaths, srcToDestId); - } - catch (Exception ex) - { - context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessCategories"); - } - - // Reduce batch to saved (valid) products. - // No need to perform import operations on errored products. - batch = batch.Where(x => x.Entity != null && !x.IsTransient).ToArray(); - - // Update result object. - context.Result.NewRecords += batch.Count(x => x.IsNew && !x.IsTransient); - context.Result.ModifiedRecords += batch.Count(x => !x.IsNew && !x.IsTransient); - - // Process slugs. - if (segmenter.HasColumn("SeName", true) || batch.Any(x => x.IsNew || x.NameChanged)) - { - try - { - _categoryRepository.Context.AutoDetectChangesEnabled = true; - ProcessSlugs(context, batch, typeof(Category).Name); - } - catch (Exception ex) - { - context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessSlugs"); - } - finally - { - _categoryRepository.Context.AutoDetectChangesEnabled = false; - } - } - - // Process store mappings. - if (segmenter.HasColumn("StoreIds")) - { - try - { - ProcessStoreMappings(context, batch); - } - catch (Exception ex) - { - context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessStoreMappings"); - } - } - - // Localizations. - try - { - ProcessLocalizations(context, batch, _localizableProperties); - } - catch (Exception ex) - { - context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessLocalizedProperties"); - } - - // Process pictures. - if (segmenter.HasColumn("ImageUrl") && !segmenter.IsIgnored("PictureId")) - { - try - { - _categoryRepository.Context.AutoDetectChangesEnabled = true; - ProcessPictures(context, batch); - } - catch (Exception ex) - { - context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessPictures"); - } - finally - { - _categoryRepository.Context.AutoDetectChangesEnabled = false; - } - } + { + private readonly IRepository _categoryRepository; + private readonly ICategoryTemplateService _categoryTemplateService; + private readonly IMediaService _mediaService; + private readonly IFolderService _folderService; + private readonly ILocalizedEntityService _localizedEntityService; + private readonly FileDownloadManager _fileDownloadManager; + private readonly ICacheManager _cache; + + private static readonly Dictionary>> _localizableProperties = new Dictionary>> + { + { "Name", x => x.Name }, + { "FullName", x => x.FullName }, + { "Description", x => x.Description }, + { "BottomDescription", x => x.BottomDescription }, + { "MetaKeywords", x => x.MetaKeywords }, + { "MetaDescription", x => x.MetaDescription }, + { "MetaTitle", x => x.MetaTitle } + }; + + public CategoryImporter( + IRepository categoryRepository, + ICategoryTemplateService categoryTemplateService, + IMediaService mediaService, + IFolderService folderService, + ILocalizedEntityService localizedEntityService, + FileDownloadManager fileDownloadManager, + ICacheManager cache) + { + _categoryRepository = categoryRepository; + _categoryTemplateService = categoryTemplateService; + _mediaService = mediaService; + _folderService = folderService; + _localizedEntityService = localizedEntityService; + _fileDownloadManager = fileDownloadManager; + _cache = cache; + } + + protected override void Import(ImportExecuteContext context) + { + var srcToDestId = new Dictionary(); + + var templateViewPaths = _categoryTemplateService.GetAllCategoryTemplates().ToDictionarySafe(x => x.ViewPath, x => x.Id); + + using (var scope = new DbContextScope(ctx: context.Services.DbContext, hooksEnabled: false, autoDetectChanges: false, proxyCreation: false, validateOnSave: false)) + { + var segmenter = context.DataSegmenter; + + Initialize(context); + + while (context.Abort == DataExchangeAbortion.None && segmenter.ReadNextBatch()) + { + var batch = segmenter.GetCurrentBatch(); + + // Perf: detach all entities + _categoryRepository.Context.DetachAll(true); + + context.SetProgress(segmenter.CurrentSegmentFirstRowIndex - 1, segmenter.TotalRows); + + try + { + ProcessCategories(context, batch, templateViewPaths, srcToDestId); + } + catch (Exception ex) + { + context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessCategories"); + } + + // Reduce batch to saved (valid) products. + // No need to perform import operations on errored products. + batch = batch.Where(x => x.Entity != null && !x.IsTransient).ToArray(); + + // Update result object. + context.Result.NewRecords += batch.Count(x => x.IsNew && !x.IsTransient); + context.Result.ModifiedRecords += batch.Count(x => !x.IsNew && !x.IsTransient); + + // Process slugs. + if (segmenter.HasColumn("SeName", true) || batch.Any(x => x.IsNew || x.NameChanged)) + { + try + { + _categoryRepository.Context.AutoDetectChangesEnabled = true; + ProcessSlugs(context, batch, typeof(Category).Name); + } + catch (Exception ex) + { + context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessSlugs"); + } + finally + { + _categoryRepository.Context.AutoDetectChangesEnabled = false; + } + } + + // Process store mappings. + if (segmenter.HasColumn("StoreIds")) + { + try + { + ProcessStoreMappings(context, batch); + } + catch (Exception ex) + { + context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessStoreMappings"); + } + } + + // Localizations. + try + { + ProcessLocalizations(context, batch, _localizableProperties); + } + catch (Exception ex) + { + context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessLocalizedProperties"); + } + + // Process pictures. + if (segmenter.HasColumn("ImageUrl") && !segmenter.IsIgnored("PictureId")) + { + try + { + _categoryRepository.Context.AutoDetectChangesEnabled = true; + ProcessPictures(context, batch); + } + catch (Exception ex) + { + context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessPictures"); + } + finally + { + _categoryRepository.Context.AutoDetectChangesEnabled = false; + } + } context.Services.EventPublisher.Publish(new ImportBatchExecutedEvent(context, batch)); } - // Map parent id of inserted categories. - if (srcToDestId.Any() && segmenter.HasColumn("Id") && segmenter.HasColumn("ParentCategoryId") && !segmenter.IsIgnored("ParentCategoryId")) - { - segmenter.Reset(); - - while (context.Abort == DataExchangeAbortion.None && segmenter.ReadNextBatch()) - { - var batch = segmenter.GetCurrentBatch(); - _categoryRepository.Context.DetachAll(false); - - try - { - ProcessParentMappings(context, batch, srcToDestId); - } - catch (Exception ex) - { - context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessParentMappings"); - } - } - } - } - } - - protected virtual int ProcessPictures( - ImportExecuteContext context, - IEnumerable> batch) - { - foreach (var row in batch) - { - try - { - var srcId = row.GetDataValue("Id"); - var imageUrl = row.GetDataValue("ImageUrl"); + // Map parent id of inserted categories. + if (srcToDestId.Any() && segmenter.HasColumn("Id") && segmenter.HasColumn("ParentCategoryId") && !segmenter.IsIgnored("ParentCategoryId")) + { + segmenter.Reset(); + + while (context.Abort == DataExchangeAbortion.None && segmenter.ReadNextBatch()) + { + var batch = segmenter.GetCurrentBatch(); + _categoryRepository.Context.DetachAll(unchangedEntitiesOnly: false); + + try + { + ProcessParentMappings(context, batch, srcToDestId); + } + catch (Exception ex) + { + context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessParentMappings"); + } + } + } + } + + // Hooks are disabled but category tree may have changed. + _cache.Clear(); + } + + protected virtual int ProcessPictures(ImportExecuteContext context, IEnumerable> batch) + { + var allFileIds = batch + .Where(row => row.HasDataValue("ImageUrl") && row.Entity.MediaFileId > 0) + .Select(row => row.Entity.MediaFileId.Value) + .Distinct() + .ToArray(); + + var allFiles = _mediaService.GetFilesByIds(allFileIds).ToDictionary(x => x.Id, x => x.File); + var catalogAlbumId = _folderService.GetNodeByPath(SystemAlbumProvider.Catalog).Value.Id; + + foreach (var row in batch) + { + try + { + var imageUrl = row.GetDataValue("ImageUrl"); if (imageUrl.IsEmpty()) { continue; } - var seoName = _pictureService.GetPictureSeName(row.EntityDisplayName); - var image = CreateDownloadImage(context, imageUrl, seoName, 1); + var image = CreateDownloadImage(context, imageUrl, 1); + if (image == null) + { + continue; + } if (image.Url.HasValue() && !image.Success.HasValue) { @@ -201,265 +219,250 @@ protected virtual int ProcessPictures( if ((image.Success ?? false) && File.Exists(image.Path)) { Succeeded(image); - var pictureBinary = File.ReadAllBytes(image.Path); - - if (pictureBinary != null && pictureBinary.Length > 0) + using (var stream = File.OpenRead(image.Path)) { - var currentPictures = new List(); - var pictureId = row.Entity.PictureId ?? 0; - if (pictureId != 0) + if ((stream?.Length ?? 0) > 0) { - var picture = _pictureRepository.TableUntracked.Expand(x => x.MediaStorage).FirstOrDefault(x => x.Id == pictureId); - if (picture != null) + var assignedFile = allFiles.Get(row.Entity.MediaFileId ?? 0); + MediaFile sourceFile = null; + + if (assignedFile != null && _mediaService.FindEqualFile(stream, new[] { assignedFile }, true, out var _)) { - currentPictures.Add(picture); + context.Result.AddInfo($"Found equal image in data store for '{image.FileName}'. Skipping file.", row.GetRowInfo(), "ImageUrl"); + } + else if (_mediaService.FindEqualFile(stream, image.FileName, catalogAlbumId, true, out sourceFile)) + { + context.Result.AddInfo($"Found equal image in catalog album for '{image.FileName}'. Assigning existing file instead.", row.GetRowInfo(), "ImageUrl"); + } + else + { + var path = _mediaService.CombinePaths(SystemAlbumProvider.Catalog, image.FileName); + sourceFile = _mediaService.SaveFile(path, stream, false, DuplicateFileHandling.Rename)?.File; } - } - - pictureBinary = _pictureService.FindEqualPicture(pictureBinary, currentPictures, out var equalPictureId); - if (pictureBinary != null && pictureBinary.Length > 0) - { - var picture = _pictureService.InsertPicture(pictureBinary, image.MimeType, seoName, true, false, false); - if (picture != null) + if (sourceFile?.Id > 0) { - row.Entity.PictureId = picture.Id; + row.Entity.MediaFileId = sourceFile.Id; _categoryRepository.Update(row.Entity); } } - else - { - context.Result.AddInfo("Found equal picture in data store. Skipping field.", row.GetRowInfo(), "ImageUrls"); - } } } else if (image.Url.HasValue()) { context.Result.AddInfo("Download of an image failed.", row.GetRowInfo(), "ImageUrls"); } - } - catch (Exception ex) - { - context.Result.AddWarning(ex.ToAllMessages(), row.GetRowInfo(), "ImageUrls"); - } - } - - var num = _categoryRepository.Context.SaveChanges(); - return num; - } - - protected virtual int ProcessLocalizations( - ImportExecuteContext context, - IEnumerable> batch, - string[] localizedProperties) - { - if (localizedProperties.Length == 0) - { - return 0; - } - - bool shouldSave = false; - - foreach (var row in batch) - { - foreach (var prop in localizedProperties) - { - var lambda = _localizableProperties[prop]; - foreach (var lang in context.Languages) - { - var code = lang.UniqueSeoCode; - string value; - - if (row.TryGetDataValue(prop /* ColumnName */, code, out value)) - { - _localizedEntityService.SaveLocalizedValue(row.Entity, lambda, value, lang.Id); - shouldSave = true; - } - } - } - } - - if (shouldSave) - { - // commit whole batch at once - return context.Services.DbContext.SaveChanges(); - } - - return 0; - } - - protected virtual int ProcessParentMappings( - ImportExecuteContext context, - IEnumerable> batch, - Dictionary srcToDestId) - { - foreach (var row in batch) - { - var id = row.GetDataValue("Id"); - var rawParentId = row.GetDataValue("ParentCategoryId"); - var parentId = rawParentId.ToInt(-1); - - if (id != 0 && parentId != -1 && srcToDestId.ContainsKey(id) && srcToDestId.ContainsKey(parentId)) - { - // only touch hierarchical data if child and parent were inserted - if (srcToDestId[id].Inserted && srcToDestId[parentId].Inserted && srcToDestId[id].DestinationId != 0) - { - var category = _categoryRepository.GetById(srcToDestId[id].DestinationId); - if (category != null) - { - category.ParentCategoryId = srcToDestId[parentId].DestinationId; - - _categoryRepository.Update(category); - } - } - } - } - - var num = _categoryRepository.Context.SaveChanges(); - - return num; - } - - protected virtual int ProcessCategories( - ImportExecuteContext context, - IEnumerable> batch, - Dictionary templateViewPaths, - Dictionary srcToDestId) - { - _categoryRepository.AutoCommitEnabled = true; - - var defaultTemplateId = templateViewPaths["CategoryTemplate.ProductsInGridOrLines"]; + } + catch (Exception ex) + { + context.Result.AddWarning(ex.ToAllMessages(), row.GetRowInfo(), "ImageUrls"); + } + } + + var num = _categoryRepository.Context.SaveChanges(); + return num; + } + + protected virtual int ProcessLocalizations( + ImportExecuteContext context, + IEnumerable> batch, + string[] localizedProperties) + { + if (localizedProperties.Length == 0) + { + return 0; + } + + bool shouldSave = false; + + foreach (var row in batch) + { + foreach (var prop in localizedProperties) + { + var lambda = _localizableProperties[prop]; + foreach (var lang in context.Languages) + { + var code = lang.UniqueSeoCode; + string value; + + if (row.TryGetDataValue(prop /* ColumnName */, code, out value)) + { + _localizedEntityService.SaveLocalizedValue(row.Entity, lambda, value, lang.Id); + shouldSave = true; + } + } + } + } + + if (shouldSave) + { + // commit whole batch at once + return context.Services.DbContext.SaveChanges(); + } + + return 0; + } + + protected virtual int ProcessParentMappings( + ImportExecuteContext context, + IEnumerable> batch, + Dictionary srcToDestId) + { + foreach (var row in batch) + { + var id = row.GetDataValue("Id"); + var rawParentId = row.GetDataValue("ParentCategoryId"); + var parentId = rawParentId.ToInt(-1); + + if (id != 0 && parentId != -1 && srcToDestId.ContainsKey(id) && srcToDestId.ContainsKey(parentId)) + { + // only touch hierarchical data if child and parent were inserted + if (srcToDestId[id].Inserted && srcToDestId[parentId].Inserted && srcToDestId[id].DestinationId != 0) + { + var category = _categoryRepository.GetById(srcToDestId[id].DestinationId); + if (category != null) + { + category.ParentCategoryId = srcToDestId[parentId].DestinationId; + + _categoryRepository.Update(category); + } + } + } + } + + var num = _categoryRepository.Context.SaveChanges(); + + return num; + } + + protected virtual int ProcessCategories( + ImportExecuteContext context, + IEnumerable> batch, + Dictionary templateViewPaths, + Dictionary srcToDestId) + { + _categoryRepository.AutoCommitEnabled = true; + + var defaultTemplateId = templateViewPaths["CategoryTemplate.ProductsInGridOrLines"]; var hasNameColumn = context.DataSegmenter.HasColumn("Name"); foreach (var row in batch) - { - Category category = null; - var id = row.GetDataValue("Id"); - var name = row.GetDataValue("Name"); - - foreach (var keyName in context.KeyFieldNames) - { - switch (keyName) - { - case "Id": - if (id != 0) - category = _categoryRepository.GetById(id); - break; - case "Name": - if (name.HasValue()) - category = _categoryRepository.Table.FirstOrDefault(x => x.Name == name); - break; - } - - if (category != null) - break; - } - - if (category == null) - { - if (context.UpdateOnly) - { - ++context.Result.SkippedRecords; - continue; - } + { + Category category = null; + var id = row.GetDataValue("Id"); + var name = row.GetDataValue("Name"); + + foreach (var keyName in context.KeyFieldNames) + { + switch (keyName) + { + case "Id": + if (id != 0) + category = _categoryRepository.GetById(id); + break; + case "Name": + if (name.HasValue()) + category = _categoryRepository.Table.FirstOrDefault(x => x.Name == name); + break; + } + + if (category != null) + break; + } + + if (category == null) + { + if (context.UpdateOnly) + { + ++context.Result.SkippedRecords; + continue; + } // A name is required for new categories. if (!row.HasDataValue("Name")) { - ++context.Result.SkippedRecords; - context.Result.AddError("The 'Name' field is required for new categories. Skipping row.", row.GetRowInfo(), "Name"); - continue; - } - - category = new Category(); - } - - row.Initialize(category, name ?? category.Name); - - if (!row.IsNew && hasNameColumn && !category.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) - { - // Perf: use this later for SeName updates. - row.NameChanged = true; - } - - row.SetProperty(context.Result, (x) => x.Name); - row.SetProperty(context.Result, (x) => x.FullName); - row.SetProperty(context.Result, (x) => x.Description); - row.SetProperty(context.Result, (x) => x.BottomDescription); - row.SetProperty(context.Result, (x) => x.MetaKeywords); - row.SetProperty(context.Result, (x) => x.MetaDescription); - row.SetProperty(context.Result, (x) => x.MetaTitle); - row.SetProperty(context.Result, (x) => x.PageSize); - row.SetProperty(context.Result, (x) => x.AllowCustomersToSelectPageSize); - row.SetProperty(context.Result, (x) => x.PageSizeOptions); - row.SetProperty(context.Result, (x) => x.ShowOnHomePage); - row.SetProperty(context.Result, (x) => x.HasDiscountsApplied); - row.SetProperty(context.Result, (x) => x.Published, true); - row.SetProperty(context.Result, (x) => x.DisplayOrder); - row.SetProperty(context.Result, (x) => x.Alias); - row.SetProperty(context.Result, (x) => x.DefaultViewMode); - // With new entities, "LimitedToStores" is an implicit field, meaning - // it has to be set to true by code if it's absent but "StoreIds" exists. - row.SetProperty(context.Result, (x) => x.LimitedToStores, !row.GetDataValue>("StoreIds").IsNullOrEmpty()); - - string tvp; - if (row.TryGetDataValue("CategoryTemplateViewPath", out tvp, row.IsTransient)) - { - category.CategoryTemplateId = (tvp.HasValue() && templateViewPaths.ContainsKey(tvp) ? templateViewPaths[tvp] : defaultTemplateId); - } - - if (id != 0 && !srcToDestId.ContainsKey(id)) - { - srcToDestId.Add(id, new ImportCategoryMapping { Inserted = row.IsTransient }); - } - - if (row.IsTransient) - { - _categoryRepository.Insert(category); - } - else - { - _categoryRepository.Update(category); - } - } - - // Commit whole batch at once. - var num = _categoryRepository.Context.SaveChanges(); - - // Get new category ids. - foreach (var row in batch) - { - var id = row.GetDataValue("Id"); + ++context.Result.SkippedRecords; + context.Result.AddError("The 'Name' field is required for new categories. Skipping row.", row.GetRowInfo(), "Name"); + continue; + } + + category = new Category(); + } + + row.Initialize(category, name ?? category.Name); + + if (!row.IsNew && hasNameColumn && !category.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + // Perf: use this later for SeName updates. + row.NameChanged = true; + } + + row.SetProperty(context.Result, (x) => x.Name); + row.SetProperty(context.Result, (x) => x.FullName); + row.SetProperty(context.Result, (x) => x.Description); + row.SetProperty(context.Result, (x) => x.BottomDescription); + row.SetProperty(context.Result, (x) => x.MetaKeywords); + row.SetProperty(context.Result, (x) => x.MetaDescription); + row.SetProperty(context.Result, (x) => x.MetaTitle); + row.SetProperty(context.Result, (x) => x.PageSize); + row.SetProperty(context.Result, (x) => x.AllowCustomersToSelectPageSize); + row.SetProperty(context.Result, (x) => x.PageSizeOptions); + row.SetProperty(context.Result, (x) => x.ShowOnHomePage); + row.SetProperty(context.Result, (x) => x.HasDiscountsApplied); + row.SetProperty(context.Result, (x) => x.Published, true); + row.SetProperty(context.Result, (x) => x.DisplayOrder); + row.SetProperty(context.Result, (x) => x.Alias); + row.SetProperty(context.Result, (x) => x.DefaultViewMode); + // With new entities, "LimitedToStores" is an implicit field, meaning + // it has to be set to true by code if it's absent but "StoreIds" exists. + row.SetProperty(context.Result, (x) => x.LimitedToStores, !row.GetDataValue>("StoreIds").IsNullOrEmpty()); + + string tvp; + if (row.TryGetDataValue("CategoryTemplateViewPath", out tvp, row.IsTransient)) + { + category.CategoryTemplateId = (tvp.HasValue() && templateViewPaths.ContainsKey(tvp) ? templateViewPaths[tvp] : defaultTemplateId); + } + + if (id != 0 && !srcToDestId.ContainsKey(id)) + { + srcToDestId.Add(id, new ImportCategoryMapping { Inserted = row.IsTransient }); + } + + if (row.IsTransient) + { + _categoryRepository.Insert(category); + } + else + { + _categoryRepository.Update(category); + } + } + + // Commit whole batch at once. + var num = _categoryRepository.Context.SaveChanges(); + + // Get new category ids. + foreach (var row in batch) + { + var id = row.GetDataValue("Id"); if (id != 0 && srcToDestId.ContainsKey(id)) { srcToDestId[id].DestinationId = row.Entity.Id; } - } - - return num; - } - - public static string[] SupportedKeyFields - { - get - { - return new string[] { "Id", "Name" }; - } - } - - public static string[] DefaultKeyFields - { - get - { - return new string[] { "Name", "Id" }; - } - } - - public class ImportCategoryMapping - { - public int DestinationId { get; set; } - public bool Inserted { get; set; } - } - } + } + + return num; + } + + public static string[] SupportedKeyFields => new string[] { "Id", "Name" }; + + public static string[] DefaultKeyFields => new string[] { "Name", "Id" }; + + public class ImportCategoryMapping + { + public int DestinationId { get; set; } + public bool Inserted { get; set; } + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/Importer/ProductImporter.cs b/src/Libraries/SmartStore.Services/Catalog/Importer/ProductImporter.cs index ed77e3abc3..7da3f102c3 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Importer/ProductImporter.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Importer/ProductImporter.cs @@ -12,6 +12,7 @@ using SmartStore.Core.Domain.Media; using SmartStore.Core.Domain.Seo; using SmartStore.Core.Domain.Stores; +using SmartStore.Core.IO; using SmartStore.Core.Localization; using SmartStore.Data.Utilities; using SmartStore.Services.DataExchange.Import; @@ -24,7 +25,6 @@ namespace SmartStore.Services.Catalog.Importer { public class ProductImporter : EntityImporterBase { - private readonly IRepository _productPictureRepository; private readonly IRepository _productManufacturerRepository; private readonly IRepository _productCategoryRepository; private readonly IRepository _productTagRepository; @@ -32,8 +32,8 @@ public class ProductImporter : EntityImporterBase private readonly IRepository _tierPriceRepository; private readonly IRepository _attributeValueRepository; private readonly IRepository _attributeCombinationRepository; - private readonly ILocalizedEntityService _localizedEntityService; - private readonly IPictureService _pictureService; + private readonly IMediaService _mediaService; + private readonly IFolderService _folderService; private readonly IManufacturerService _manufacturerService; private readonly ICategoryService _categoryService; private readonly IProductService _productService; @@ -53,7 +53,6 @@ public class ProductImporter : EntityImporterBase }; public ProductImporter( - IRepository productPictureRepository, IRepository productManufacturerRepository, IRepository productCategoryRepository, IRepository productTagRepository, @@ -61,8 +60,8 @@ public ProductImporter( IRepository tierPriceRepository, IRepository attributeValueRepository, IRepository attributeCombinationRepository, - ILocalizedEntityService localizedEntityService, - IPictureService pictureService, + IMediaService mediaService, + IFolderService folderService, IManufacturerService manufacturerService, ICategoryService categoryService, IProductService productService, @@ -70,7 +69,6 @@ public ProductImporter( IProductAttributeService productAttributeService, FileDownloadManager fileDownloadManager) { - _productPictureRepository = productPictureRepository; _productManufacturerRepository = productManufacturerRepository; _productCategoryRepository = productCategoryRepository; _productTagRepository = productTagRepository; @@ -78,19 +76,17 @@ public ProductImporter( _tierPriceRepository = tierPriceRepository; _attributeValueRepository = attributeValueRepository; _attributeCombinationRepository = attributeCombinationRepository; - _localizedEntityService = localizedEntityService; - _pictureService = pictureService; + _mediaService = mediaService; + _folderService = folderService; _manufacturerService = manufacturerService; _categoryService = categoryService; _productService = productService; _productTemplateService = productTemplateService; _productAttributeService = productAttributeService; _fileDownloadManager = fileDownloadManager; - - T = NullLocalizer.Instance; } - public Localizer T { get; set; } + public Localizer T { get; set; } = NullLocalizer.Instance; protected override void Import(ImportExecuteContext context) { @@ -121,7 +117,7 @@ protected override void Import(ImportExecuteContext context) } protected virtual void ImportProducts(ImportExecuteContext context) - { + { var segmenter = context.DataSegmenter; var srcToDestId = new Dictionary(); var templateViewPaths = _productTemplateService.GetAllProductTemplates().ToDictionarySafe(x => x.ViewPath, x => x.Id); @@ -135,7 +131,7 @@ protected virtual void ImportProducts(ImportExecuteContext context) { return x is Product || x is UrlRecord || x is StoreMapping || x is ProductVariantAttribute || x is LocalizedProperty || x is ProductBundleItem || x is ProductCategory || x is ProductManufacturer || x is Category || x is Manufacturer || - x is ProductPicture || x is Picture || x is ProductTag || x is TierPrice; + x is ProductMediaFile || x is MediaFile || x is ProductTag || x is TierPrice; }); //_productRepository.Context.DetachAll(true); @@ -300,7 +296,7 @@ x is ProductBundleItem || x is ProductCategory || x is ProductManufacturer || x // =========================================================================== // 10.) PostProcess: normalization - // =========================================================================== + // =========================================================================== DataMigrator.FixProductMainPictureIds(_productRepository.Context, UtcNow); } @@ -574,7 +570,7 @@ protected virtual void ImportAttributeCombinations(ImportExecuteContext context) row.SetProperty(context.Result, (x) => x.Height); row.SetProperty(context.Result, (x) => x.BasePriceAmount); row.SetProperty(context.Result, (x) => x.BasePriceBaseAmount); - row.SetProperty(context.Result, (x) => x.AssignedPictureIds); + row.SetProperty(context.Result, (x) => x.AssignedMediaFileIds); row.SetProperty(context.Result, (x) => x.IsActive, true); row.SetProperty(context.Result, (x) => x.AllowOutOfStockOrders); row.SetProperty(context.Result, (x) => x.DeliveryTimeId); @@ -605,444 +601,480 @@ protected virtual void ImportAttributeCombinations(ImportExecuteContext context) } protected virtual int ProcessProducts( - ImportExecuteContext context, - IEnumerable> batch, - Dictionary templateViewPaths, - Dictionary srcToDestId) - { - _productRepository.AutoCommitEnabled = false; - - var defaultTemplateId = templateViewPaths["Product"]; + ImportExecuteContext context, + IEnumerable> batch, + Dictionary templateViewPaths, + Dictionary srcToDestId) + { + _productRepository.AutoCommitEnabled = false; + + var defaultTemplateId = templateViewPaths["Product"]; var hasNameColumn = context.DataSegmenter.HasColumn("Name"); - + foreach (var row in batch) - { - Product product = null; - var id = row.GetDataValue("Id"); - - foreach (var keyName in context.KeyFieldNames) - { - var keyValue = row.GetDataValue(keyName); - - if (keyValue.HasValue() || id > 0) - { - switch (keyName) - { - case "Id": - product = _productRepository.GetById(id); // get it uncached - break; - case "Sku": - product = _productService.GetProductBySku(keyValue); - break; - case "Gtin": - product = _productService.GetProductByGtin(keyValue); - break; - case "ManufacturerPartNumber": - product = _productService.GetProductByManufacturerPartNumber(keyValue); - break; - case "Name": - product = _productService.GetProductByName(keyValue); - break; - } - } - - if (product != null) - break; - } - - if (product == null) - { - if (context.UpdateOnly) - { - ++context.Result.SkippedRecords; - continue; - } - - // A name is required for new products. - if (!row.HasDataValue("Name")) - { - ++context.Result.SkippedRecords; - context.Result.AddError("The 'Name' field is required for new products. Skipping row.", row.GetRowInfo(), "Name"); - continue; - } - - product = new Product(); - } - - var name = row.GetDataValue("Name"); - - row.Initialize(product, name ?? product.Name); - - if (!row.IsNew && hasNameColumn) - { - if (!product.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) - { - // Perf: use this later for SeName updates. - row.NameChanged = true; - } - } - - row.SetProperty(context.Result, (x) => x.ProductTypeId, (int)ProductType.SimpleProduct); - row.SetProperty(context.Result, (x) => x.VisibleIndividually, true); - row.SetProperty(context.Result, (x) => x.Name); - row.SetProperty(context.Result, (x) => x.ShortDescription); - row.SetProperty(context.Result, (x) => x.FullDescription); - row.SetProperty(context.Result, (x) => x.AdminComment); - row.SetProperty(context.Result, (x) => x.ShowOnHomePage); - row.SetProperty(context.Result, (x) => x.HomePageDisplayOrder); - row.SetProperty(context.Result, (x) => x.MetaKeywords); - row.SetProperty(context.Result, (x) => x.MetaDescription); - row.SetProperty(context.Result, (x) => x.MetaTitle); - row.SetProperty(context.Result, (x) => x.AllowCustomerReviews, true); - row.SetProperty(context.Result, (x) => x.ApprovedRatingSum); - row.SetProperty(context.Result, (x) => x.NotApprovedRatingSum); - row.SetProperty(context.Result, (x) => x.ApprovedTotalReviews); - row.SetProperty(context.Result, (x) => x.NotApprovedTotalReviews); - row.SetProperty(context.Result, (x) => x.Published, true); - row.SetProperty(context.Result, (x) => x.Sku); - row.SetProperty(context.Result, (x) => x.ManufacturerPartNumber); - row.SetProperty(context.Result, (x) => x.Gtin); - row.SetProperty(context.Result, (x) => x.IsGiftCard); - row.SetProperty(context.Result, (x) => x.GiftCardTypeId); - row.SetProperty(context.Result, (x) => x.RequireOtherProducts); - row.SetProperty(context.Result, (x) => x.RequiredProductIds); // TODO: global scope - row.SetProperty(context.Result, (x) => x.AutomaticallyAddRequiredProducts); - row.SetProperty(context.Result, (x) => x.IsDownload); - //row.SetProperty(context.Result, (x) => x.DownloadId); - //row.SetProperty(context.Result, (x) => x.UnlimitedDownloads, true); - row.SetProperty(context.Result, (x) => x.MaxNumberOfDownloads, 10); - row.SetProperty(context.Result, (x) => x.DownloadExpirationDays); - row.SetProperty(context.Result, (x) => x.DownloadActivationTypeId, 1); - row.SetProperty(context.Result, (x) => x.HasSampleDownload); - row.SetProperty(context.Result, (x) => x.SampleDownloadId, (int?)null, ZeroToNull); // TODO: global scope - row.SetProperty(context.Result, (x) => x.HasUserAgreement); - row.SetProperty(context.Result, (x) => x.UserAgreementText); - row.SetProperty(context.Result, (x) => x.IsRecurring); - row.SetProperty(context.Result, (x) => x.RecurringCycleLength, 100); - row.SetProperty(context.Result, (x) => x.RecurringCyclePeriodId); - row.SetProperty(context.Result, (x) => x.RecurringTotalCycles, 10); - row.SetProperty(context.Result, (x) => x.IsShipEnabled, true); - row.SetProperty(context.Result, (x) => x.IsFreeShipping); - row.SetProperty(context.Result, (x) => x.AdditionalShippingCharge); - row.SetProperty(context.Result, (x) => x.IsEsd); - row.SetProperty(context.Result, (x) => x.IsTaxExempt); - row.SetProperty(context.Result, (x) => x.TaxCategoryId, 1); // TODO: global scope - row.SetProperty(context.Result, (x) => x.ManageInventoryMethodId); - row.SetProperty(context.Result, (x) => x.StockQuantity, 10000); - row.SetProperty(context.Result, (x) => x.DisplayStockAvailability); - row.SetProperty(context.Result, (x) => x.DisplayStockQuantity); - row.SetProperty(context.Result, (x) => x.MinStockQuantity); - row.SetProperty(context.Result, (x) => x.LowStockActivityId); - row.SetProperty(context.Result, (x) => x.NotifyAdminForQuantityBelow, 1); - row.SetProperty(context.Result, (x) => x.BackorderModeId); - row.SetProperty(context.Result, (x) => x.AllowBackInStockSubscriptions); - 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.HideQuantityControl); + { + Product product = null; + var id = row.GetDataValue("Id"); + + foreach (var keyName in context.KeyFieldNames) + { + var keyValue = row.GetDataValue(keyName); + + if (keyValue.HasValue() || id > 0) + { + switch (keyName) + { + case "Id": + product = _productRepository.GetById(id); // get it uncached + break; + case "Sku": + product = _productService.GetProductBySku(keyValue); + break; + case "Gtin": + product = _productService.GetProductByGtin(keyValue); + break; + case "ManufacturerPartNumber": + product = _productService.GetProductByManufacturerPartNumber(keyValue); + break; + case "Name": + product = _productService.GetProductByName(keyValue); + break; + } + } + + if (product != null) + break; + } + + if (product == null) + { + if (context.UpdateOnly) + { + ++context.Result.SkippedRecords; + continue; + } + + // A name is required for new products. + if (!row.HasDataValue("Name")) + { + ++context.Result.SkippedRecords; + context.Result.AddError("The 'Name' field is required for new products. Skipping row.", row.GetRowInfo(), "Name"); + continue; + } + + product = new Product(); + } + + var name = row.GetDataValue("Name"); + + row.Initialize(product, name ?? product.Name); + + if (!row.IsNew && hasNameColumn) + { + if (!product.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + // Perf: use this later for SeName updates. + row.NameChanged = true; + } + } + + row.SetProperty(context.Result, (x) => x.ProductTypeId, (int)ProductType.SimpleProduct); + row.SetProperty(context.Result, (x) => x.Name); + row.SetProperty(context.Result, (x) => x.ShortDescription); + row.SetProperty(context.Result, (x) => x.FullDescription); + row.SetProperty(context.Result, (x) => x.AdminComment); + row.SetProperty(context.Result, (x) => x.ShowOnHomePage); + row.SetProperty(context.Result, (x) => x.HomePageDisplayOrder); + row.SetProperty(context.Result, (x) => x.MetaKeywords); + row.SetProperty(context.Result, (x) => x.MetaDescription); + row.SetProperty(context.Result, (x) => x.MetaTitle); + row.SetProperty(context.Result, (x) => x.AllowCustomerReviews, true); + row.SetProperty(context.Result, (x) => x.ApprovedRatingSum); + row.SetProperty(context.Result, (x) => x.NotApprovedRatingSum); + row.SetProperty(context.Result, (x) => x.ApprovedTotalReviews); + row.SetProperty(context.Result, (x) => x.NotApprovedTotalReviews); + row.SetProperty(context.Result, (x) => x.Published, true); + row.SetProperty(context.Result, (x) => x.Sku); + row.SetProperty(context.Result, (x) => x.ManufacturerPartNumber); + row.SetProperty(context.Result, (x) => x.Gtin); + row.SetProperty(context.Result, (x) => x.IsGiftCard); + row.SetProperty(context.Result, (x) => x.GiftCardTypeId); + row.SetProperty(context.Result, (x) => x.RequireOtherProducts); + row.SetProperty(context.Result, (x) => x.RequiredProductIds); // TODO: global scope + row.SetProperty(context.Result, (x) => x.AutomaticallyAddRequiredProducts); + row.SetProperty(context.Result, (x) => x.IsDownload); + //row.SetProperty(context.Result, (x) => x.DownloadId); + //row.SetProperty(context.Result, (x) => x.UnlimitedDownloads, true); + row.SetProperty(context.Result, (x) => x.MaxNumberOfDownloads, 10); + row.SetProperty(context.Result, (x) => x.DownloadExpirationDays); + row.SetProperty(context.Result, (x) => x.DownloadActivationTypeId, 1); + row.SetProperty(context.Result, (x) => x.HasSampleDownload); + row.SetProperty(context.Result, (x) => x.SampleDownloadId, (int?)null, ZeroToNull); // TODO: global scope + row.SetProperty(context.Result, (x) => x.HasUserAgreement); + row.SetProperty(context.Result, (x) => x.UserAgreementText); + row.SetProperty(context.Result, (x) => x.IsRecurring); + row.SetProperty(context.Result, (x) => x.RecurringCycleLength, 100); + row.SetProperty(context.Result, (x) => x.RecurringCyclePeriodId); + row.SetProperty(context.Result, (x) => x.RecurringTotalCycles, 10); + row.SetProperty(context.Result, (x) => x.IsShipEnabled, true); + row.SetProperty(context.Result, (x) => x.IsFreeShipping); + row.SetProperty(context.Result, (x) => x.AdditionalShippingCharge); + row.SetProperty(context.Result, (x) => x.IsEsd); + row.SetProperty(context.Result, (x) => x.IsTaxExempt); + row.SetProperty(context.Result, (x) => x.TaxCategoryId, 1); // TODO: global scope + row.SetProperty(context.Result, (x) => x.ManageInventoryMethodId); + row.SetProperty(context.Result, (x) => x.StockQuantity, 10000); + row.SetProperty(context.Result, (x) => x.DisplayStockAvailability); + row.SetProperty(context.Result, (x) => x.DisplayStockQuantity); + row.SetProperty(context.Result, (x) => x.MinStockQuantity); + row.SetProperty(context.Result, (x) => x.LowStockActivityId); + row.SetProperty(context.Result, (x) => x.NotifyAdminForQuantityBelow, 1); + row.SetProperty(context.Result, (x) => x.BackorderModeId); + row.SetProperty(context.Result, (x) => x.AllowBackInStockSubscriptions); + 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.HideQuantityControl); row.SetProperty(context.Result, (x) => x.AllowedQuantities); - row.SetProperty(context.Result, (x) => x.DisableBuyButton); - row.SetProperty(context.Result, (x) => x.DisableWishlistButton); - row.SetProperty(context.Result, (x) => x.AvailableForPreOrder); - row.SetProperty(context.Result, (x) => x.CallForPrice); - row.SetProperty(context.Result, (x) => x.Price); - row.SetProperty(context.Result, (x) => x.OldPrice); - row.SetProperty(context.Result, (x) => x.ProductCost); - row.SetProperty(context.Result, (x) => x.SpecialPrice); - row.SetProperty(context.Result, (x) => x.SpecialPriceStartDateTimeUtc); - row.SetProperty(context.Result, (x) => x.SpecialPriceEndDateTimeUtc); - row.SetProperty(context.Result, (x) => x.CustomerEntersPrice); - row.SetProperty(context.Result, (x) => x.MinimumCustomerEnteredPrice); - row.SetProperty(context.Result, (x) => x.MaximumCustomerEnteredPrice, 1000); - // HasTierPrices... ignore as long as no tier prices are imported - // LowestAttributeCombinationPrice... ignore as long as no combinations are imported - row.SetProperty(context.Result, (x) => x.Weight); - row.SetProperty(context.Result, (x) => x.Length); - row.SetProperty(context.Result, (x) => x.Width); - row.SetProperty(context.Result, (x) => x.Height); - row.SetProperty(context.Result, (x) => x.DisplayOrder); - row.SetProperty(context.Result, (x) => x.DeliveryTimeId); // TODO: global scope - row.SetProperty(context.Result, (x) => x.QuantityUnitId); // TODO: global scope - row.SetProperty(context.Result, (x) => x.BasePriceEnabled); - row.SetProperty(context.Result, (x) => x.BasePriceMeasureUnit); - row.SetProperty(context.Result, (x) => x.BasePriceAmount); - row.SetProperty(context.Result, (x) => x.BasePriceBaseAmount); - row.SetProperty(context.Result, (x) => x.BundleTitleText); - row.SetProperty(context.Result, (x) => x.BundlePerItemShipping); - row.SetProperty(context.Result, (x) => x.BundlePerItemPricing); - row.SetProperty(context.Result, (x) => x.BundlePerItemShoppingCart); - row.SetProperty(context.Result, (x) => x.AvailableStartDateTimeUtc); - row.SetProperty(context.Result, (x) => x.AvailableEndDateTimeUtc); - // With new entities, "LimitedToStores" is an implicit field, meaning - // it has to be set to true by code if it's absent but "StoreIds" exists. - row.SetProperty(context.Result, (x) => x.LimitedToStores, !row.GetDataValue>("StoreIds").IsNullOrEmpty()); - row.SetProperty(context.Result, (x) => x.CustomsTariffNumber); - row.SetProperty(context.Result, (x) => x.CountryOfOriginId); + row.SetProperty(context.Result, (x) => x.DisableBuyButton); + row.SetProperty(context.Result, (x) => x.DisableWishlistButton); + row.SetProperty(context.Result, (x) => x.AvailableForPreOrder); + row.SetProperty(context.Result, (x) => x.CallForPrice); + row.SetProperty(context.Result, (x) => x.Price); + row.SetProperty(context.Result, (x) => x.OldPrice); + row.SetProperty(context.Result, (x) => x.ProductCost); + row.SetProperty(context.Result, (x) => x.SpecialPrice); + row.SetProperty(context.Result, (x) => x.SpecialPriceStartDateTimeUtc); + row.SetProperty(context.Result, (x) => x.SpecialPriceEndDateTimeUtc); + row.SetProperty(context.Result, (x) => x.CustomerEntersPrice); + row.SetProperty(context.Result, (x) => x.MinimumCustomerEnteredPrice); + row.SetProperty(context.Result, (x) => x.MaximumCustomerEnteredPrice, 1000); + // HasTierPrices... ignore as long as no tier prices are imported + // LowestAttributeCombinationPrice... ignore as long as no combinations are imported + row.SetProperty(context.Result, (x) => x.Weight); + row.SetProperty(context.Result, (x) => x.Length); + row.SetProperty(context.Result, (x) => x.Width); + row.SetProperty(context.Result, (x) => x.Height); + row.SetProperty(context.Result, (x) => x.DisplayOrder); + row.SetProperty(context.Result, (x) => x.DeliveryTimeId); // TODO: global scope + row.SetProperty(context.Result, (x) => x.QuantityUnitId); // TODO: global scope + row.SetProperty(context.Result, (x) => x.BasePriceEnabled); + row.SetProperty(context.Result, (x) => x.BasePriceMeasureUnit); + row.SetProperty(context.Result, (x) => x.BasePriceAmount); + row.SetProperty(context.Result, (x) => x.BasePriceBaseAmount); + row.SetProperty(context.Result, (x) => x.BundleTitleText); + row.SetProperty(context.Result, (x) => x.BundlePerItemShipping); + row.SetProperty(context.Result, (x) => x.BundlePerItemPricing); + row.SetProperty(context.Result, (x) => x.BundlePerItemShoppingCart); + row.SetProperty(context.Result, (x) => x.AvailableStartDateTimeUtc); + row.SetProperty(context.Result, (x) => x.AvailableEndDateTimeUtc); + // With new entities, "LimitedToStores" is an implicit field, meaning + // it has to be set to true by code if it's absent but "StoreIds" exists. + row.SetProperty(context.Result, (x) => x.LimitedToStores, !row.GetDataValue>("StoreIds").IsNullOrEmpty()); + 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("AttributeChoiceBehaviour", out int attributeChoiceBehaviour)) + { + product.AttributeChoiceBehaviour = (AttributeChoiceBehaviour)attributeChoiceBehaviour; + } + if (row.TryGetDataValue("Visibility", out int visibilityValue)) + { + product.Visibility = (ProductVisibility)visibilityValue; + } + if (row.TryGetDataValue("Condition", out int conditionValue)) + { + product.Condition = (ProductCondition)conditionValue; + } + + if (row.TryGetDataValue("ProductTemplateViewPath", out string tvp, row.IsTransient)) + { + product.ProductTemplateId = tvp.HasValue() && templateViewPaths.ContainsKey(tvp) ? templateViewPaths[tvp] : defaultTemplateId; + } + + if (id != 0 && !srcToDestId.ContainsKey(id)) + { + srcToDestId.Add(id, new ImportProductMapping { Inserted = row.IsTransient }); + } + + if (row.IsTransient) + { + _productRepository.Insert(product); + } + else + { + product.UpdatedOnUtc = DateTime.UtcNow; + //_productRepository.Update(product); // unnecessary: we use DetectChanges() + } + } - if (row.TryGetDataValue("ProductTemplateViewPath", out string tvp, row.IsTransient)) - { - product.ProductTemplateId = tvp.HasValue() && templateViewPaths.ContainsKey(tvp) ? templateViewPaths[tvp] : defaultTemplateId; - } - - if (id != 0 && !srcToDestId.ContainsKey(id)) - { - srcToDestId.Add(id, new ImportProductMapping { Inserted = row.IsTransient }); - } - - if (row.IsTransient) - { - _productRepository.Insert(product); - } - else - { - //_productRepository.Update(product); // unnecessary: we use DetectChanges() - } - } - - // Commit whole batch at once. - var num = _productRepository.Context.SaveChanges(); - - // Get new product ids. - foreach (var row in batch) - { - var id = row.GetDataValue("Id"); + // Commit whole batch at once. + var num = _productRepository.Context.SaveChanges(); + + // Get new product ids. + foreach (var row in batch) + { + var id = row.GetDataValue("Id"); if (id != 0 && srcToDestId.ContainsKey(id)) { srcToDestId[id].DestinationId = row.Entity.Id; } - } - - return num; - } - - protected virtual int ProcessProductMappings( - ImportExecuteContext context, - IEnumerable> batch, - Dictionary srcToDestId) - { - _productRepository.AutoCommitEnabled = false; - - foreach (var row in batch) - { - var id = row.GetDataValue("Id"); - var parentGroupedProductId = row.GetDataValue("ParentGroupedProductId"); - - if (id != 0 && parentGroupedProductId != 0 && srcToDestId.ContainsKey(id) && srcToDestId.ContainsKey(parentGroupedProductId)) - { - // Only touch relationship if child and parent were inserted. - if (srcToDestId[id].Inserted && srcToDestId[parentGroupedProductId].Inserted && srcToDestId[id].DestinationId != 0) - { - var product = _productRepository.GetById(srcToDestId[id].DestinationId); - if (product != null) - { - product.ParentGroupedProductId = srcToDestId[parentGroupedProductId].DestinationId; - //_productRepository.Update(product); // unnecessary: we use DetectChanges() - } - } - } - } - - var num = _productRepository.Context.SaveChanges(); - return num; - } - - protected virtual void ProcessProductPictures(ImportExecuteContext context, IEnumerable> batch) - { - // true, cause pictures must be saved and assigned an id prior adding a mapping. - _productPictureRepository.AutoCommitEnabled = true; - - var equalPictureId = 0; - var numberOfPictures = (context.ExtraData.NumberOfPictures ?? int.MaxValue); - - foreach (var row in batch) - { - var imageUrls = row.GetDataValue>("ImageUrls"); - if (imageUrls.IsNullOrEmpty()) - continue; - - var imageNumber = 0; - var displayOrder = -1; - var seoName = _pictureService.GetPictureSeName(row.EntityDisplayName); - var imageFiles = new List(); - - // Collect required image file infos. - foreach (var urlOrPath in imageUrls) - { - var image = CreateDownloadImage(context, urlOrPath, seoName, ++imageNumber); - - if (image != null) - imageFiles.Add(image); - - if (imageFiles.Count >= numberOfPictures) - break; - } - - // Download images. - if (imageFiles.Any(x => x.Url.HasValue())) - { - // Async downloading in batch processing is inefficient cause only the image processing benefits from async, - // not the record processing itself. a per record processing may speed up the import. - - AsyncRunner.RunSync(() => _fileDownloadManager.DownloadAsync(DownloaderContext, imageFiles.Where(x => x.Url.HasValue() && !x.Success.HasValue))); - } - - // Import images. - foreach (var image in imageFiles.OrderBy(x => x.DisplayOrder)) - { - try - { - if ((image.Success ?? false) && File.Exists(image.Path)) - { - Succeeded(image); - var pictureBinary = File.ReadAllBytes(image.Path); - - if (pictureBinary != null && pictureBinary.Length > 0) - { - var currentProductPictures = _productPictureRepository.TableUntracked - .Expand(x => x.Picture.MediaStorage) - .Where(x => x.ProductId == row.Entity.Id) - .ToList(); - - var currentPictures = currentProductPictures - .Select(x => x.Picture) - .ToList(); - - if (displayOrder == -1) - { - displayOrder = currentProductPictures.Any() ? currentProductPictures.Select(x => x.DisplayOrder).Max() : 0; - } - - pictureBinary = _pictureService.FindEqualPicture(pictureBinary, currentPictures, out equalPictureId); - - if (pictureBinary != null && pictureBinary.Length > 0) - { - var newPicture = _pictureService.InsertPicture(pictureBinary, image.MimeType, seoName, true, false, false); - - if (newPicture != null) - { - var mapping = new ProductPicture - { - ProductId = row.Entity.Id, - PictureId = newPicture.Id, - DisplayOrder = ++displayOrder - }; - - _productPictureRepository.Insert(mapping); - } - } - else - { - context.Result.AddInfo($"Found equal picture in data store for {image.Url}. Skipping field.", row.GetRowInfo(), "ImageUrls" + image.DisplayOrder.ToString()); - } - } - } - else if (image.Url.HasValue()) - { - context.Result.AddInfo($"Download failed for picture {image.Url}.", row.GetRowInfo(), "ImageUrls" + image.DisplayOrder.ToString()); - } - } - catch (Exception ex) - { - context.Result.AddWarning(ex.ToAllMessages(), row.GetRowInfo(), "ImageUrls" + image.DisplayOrder.ToString()); - } - } - } - } - - protected virtual int ProcessProductManufacturers(ImportExecuteContext context, IEnumerable> batch) - { - _productManufacturerRepository.AutoCommitEnabled = false; - - foreach (var row in batch) - { - var manufacturerIds = row.GetDataValue>("ManufacturerIds"); - if (!manufacturerIds.IsNullOrEmpty()) - { - try - { - foreach (var id in manufacturerIds) - { - if (_productManufacturerRepository.TableUntracked.Where(x => x.ProductId == row.Entity.Id && x.ManufacturerId == id).FirstOrDefault() == null) - { - // Ensure that manufacturer exists. - var manufacturer = _manufacturerService.GetManufacturerById(id); - if (manufacturer != null) - { - var productManufacturer = new ProductManufacturer - { - ProductId = row.Entity.Id, - ManufacturerId = manufacturer.Id, - IsFeaturedProduct = false, - DisplayOrder = 1 - }; - _productManufacturerRepository.Insert(productManufacturer); - } - } - } - } - catch (Exception ex) - { - context.Result.AddWarning(ex.Message, row.GetRowInfo(), "ManufacturerIds"); - } - } - } - - // Commit whole batch at once. - var num = _productManufacturerRepository.Context.SaveChanges(); - return num; - } - - protected virtual int ProcessProductCategories(ImportExecuteContext context, IEnumerable> batch) - { - _productCategoryRepository.AutoCommitEnabled = false; - - foreach (var row in batch) - { - var categoryIds = row.GetDataValue>("CategoryIds"); - if (!categoryIds.IsNullOrEmpty()) - { - try - { - foreach (var id in categoryIds) - { - if (_productCategoryRepository.TableUntracked.Where(x => x.ProductId == row.Entity.Id && x.CategoryId == id).FirstOrDefault() == null) - { - // Ensure that category exists. - var category = _categoryService.GetCategoryById(id); - if (category != null) - { - var productCategory = new ProductCategory - { - ProductId = row.Entity.Id, - CategoryId = category.Id, - IsFeaturedProduct = false, - DisplayOrder = 1 - }; - _productCategoryRepository.Insert(productCategory); - } - } - } - } - catch (Exception ex) - { - context.Result.AddWarning(ex.Message, row.GetRowInfo(), "CategoryIds"); - } - } - } - - // Commit whole batch at once, - var num = _productCategoryRepository.Context.SaveChanges(); - return num; - } + } + + return num; + } + + protected virtual int ProcessProductMappings( + ImportExecuteContext context, + IEnumerable> batch, + Dictionary srcToDestId) + { + _productRepository.AutoCommitEnabled = false; + + foreach (var row in batch) + { + var id = row.GetDataValue("Id"); + var parentGroupedProductId = row.GetDataValue("ParentGroupedProductId"); + + if (id != 0 && parentGroupedProductId != 0 && srcToDestId.ContainsKey(id) && srcToDestId.ContainsKey(parentGroupedProductId)) + { + if (srcToDestId[id].DestinationId != 0) + { + var product = _productRepository.GetById(srcToDestId[id].DestinationId); + if (product != null) + { + product.ParentGroupedProductId = srcToDestId[parentGroupedProductId].DestinationId; + //_productRepository.Update(product); // unnecessary: we use DetectChanges() + } + } + } + } + + var num = _productRepository.Context.SaveChanges(); + return num; + } + + protected virtual void ProcessProductPictures(ImportExecuteContext context, IEnumerable> batch) + { + _productRepository.AutoCommitEnabled = false; + + var numberOfPictures = context.ExtraData.NumberOfPictures ?? int.MaxValue; + var productIds = batch.Select(x => x.Entity.Id).ToArray(); + var tmpFileMap = _productService.GetProductPicturesByProductIds(productIds, null, MediaLoadFlags.None); + + foreach (var row in batch) + { + var rawImageUrls = row.GetDataValue("ImageUrls"); + + // Force pipe symbol as separator because file names can contain commas or semicolons. + var imageUrls = rawImageUrls.SplitSafe("|"); + if (imageUrls.Length == 0) + { + continue; + } + + var productId = row.Entity.Id; + var imageNumber = 0; + var displayOrder = -1; + var imageFiles = new List(); + var catalogAlbumId = _folderService.GetNodeByPath(SystemAlbumProvider.Catalog).Value.Id; + + // Collect required image file infos. + foreach (var urlOrPath in imageUrls) + { + var image = CreateDownloadImage(context, urlOrPath, ++imageNumber); + if (image != null) + { + imageFiles.Add(image); + + if (imageFiles.Count >= numberOfPictures) + break; + } + } + + // Download images. + if (imageFiles.Any(x => x.Url.HasValue())) + { + AsyncRunner.RunSync(() => _fileDownloadManager.DownloadAsync(DownloaderContext, imageFiles.Where(x => x.Url.HasValue() && !x.Success.HasValue))); + + var hasDuplicateFileNames = imageFiles + .Where(x => x.Url.HasValue()) + .Select(x => x.FileName) + .GroupBy(x => x) + .Any(x => x.Count() > 1); + + if (hasDuplicateFileNames) + { + context.Result.AddWarning($"Found duplicate names (not supported yet). File names in URLs have to be unique!", row.GetRowInfo(), "ImageUrls"); + } + } + + // Import images. + foreach (var image in imageFiles.OrderBy(x => x.DisplayOrder)) + { + try + { + if ((image.Success ?? false) && File.Exists(image.Path)) + { + Succeeded(image); + using (var stream = File.OpenRead(image.Path)) + { + if ((stream?.Length ?? 0) > 0) + { + MediaFile sourceFile = null; + var currentFiles = tmpFileMap.ContainsKey(productId) + ? tmpFileMap[productId] + : Enumerable.Empty(); + + if (displayOrder == -1) + { + displayOrder = currentFiles.Any() ? currentFiles.Select(x => x.DisplayOrder).Max() : 0; + } + + if (_mediaService.FindEqualFile(stream, currentFiles.Select(x => x.MediaFile), true, out var _)) + { + context.Result.AddInfo($"Found equal file in product data for '{image.FileName}'. Skipping file.", row.GetRowInfo(), "ImageUrls"); + } + else if (_mediaService.FindEqualFile(stream, image.FileName, catalogAlbumId, true, out sourceFile)) + { + context.Result.AddInfo($"Found equal file in catalog album for '{image.FileName}'. Assigning existing file instead.", row.GetRowInfo(), "ImageUrls"); + } + else + { + var path = _mediaService.CombinePaths(SystemAlbumProvider.Catalog, image.FileName); + sourceFile = _mediaService.SaveFile(path, stream, false, DuplicateFileHandling.Rename)?.File; + } + + if (sourceFile?.Id > 0) + { + var productMediaFile = new ProductMediaFile + { + ProductId = productId, + MediaFileId = sourceFile.Id, + DisplayOrder = ++displayOrder + }; + + _productService.InsertProductPicture(productMediaFile); + + productMediaFile.MediaFile = sourceFile; + + tmpFileMap.Add(productId, productMediaFile); + // Update for FixProductMainPictureIds. + row.Entity.UpdatedOnUtc = DateTime.UtcNow; + + //_productRepository.Update(row.Entity); + } + } + } + } + else if (image.Url.HasValue()) + { + context.Result.AddInfo($"Download failed for image {image.Url}.", row.GetRowInfo(), "ImageUrls" + image.DisplayOrder.ToString()); + } + } + catch (Exception ex) + { + context.Result.AddWarning(ex.ToAllMessages(), row.GetRowInfo(), "ImageUrls" + image.DisplayOrder.ToString()); + } + } + } + + _productRepository.Context.SaveChanges(); + } + + protected virtual int ProcessProductManufacturers(ImportExecuteContext context, IEnumerable> batch) + { + _productManufacturerRepository.AutoCommitEnabled = false; + + foreach (var row in batch) + { + var manufacturerIds = row.GetDataValue>("ManufacturerIds"); + if (!manufacturerIds.IsNullOrEmpty()) + { + try + { + foreach (var id in manufacturerIds) + { + if (_productManufacturerRepository.TableUntracked.Where(x => x.ProductId == row.Entity.Id && x.ManufacturerId == id).FirstOrDefault() == null) + { + // Ensure that manufacturer exists. + var manufacturer = _manufacturerService.GetManufacturerById(id); + if (manufacturer != null) + { + var productManufacturer = new ProductManufacturer + { + ProductId = row.Entity.Id, + ManufacturerId = manufacturer.Id, + IsFeaturedProduct = false, + DisplayOrder = 1 + }; + _productManufacturerRepository.Insert(productManufacturer); + } + } + } + } + catch (Exception ex) + { + context.Result.AddWarning(ex.Message, row.GetRowInfo(), "ManufacturerIds"); + } + } + } + + // Commit whole batch at once. + var num = _productManufacturerRepository.Context.SaveChanges(); + return num; + } + + protected virtual int ProcessProductCategories(ImportExecuteContext context, IEnumerable> batch) + { + _productCategoryRepository.AutoCommitEnabled = false; + + foreach (var row in batch) + { + var categoryIds = row.GetDataValue>("CategoryIds"); + if (!categoryIds.IsNullOrEmpty()) + { + try + { + foreach (var id in categoryIds) + { + if (!_productCategoryRepository.TableUntracked.Any(x => x.ProductId == row.Entity.Id && x.CategoryId == id)) + { + // Ensure that category exists. + var category = _categoryService.GetCategoryById(id); + if (category != null) + { + var productCategory = new ProductCategory + { + ProductId = row.Entity.Id, + CategoryId = category.Id, + IsFeaturedProduct = false, + DisplayOrder = 1 + }; + _productCategoryRepository.Insert(productCategory); + } + } + } + } + catch (Exception ex) + { + context.Result.AddWarning(ex.Message, row.GetRowInfo(), "CategoryIds"); + } + } + } + + // Commit whole batch at once, + var num = _productCategoryRepository.Context.SaveChanges(); + return num; + } protected virtual void ProcessProductTags(ImportExecuteContext context, IEnumerable> batch) { - // true, cause product tags must be saved and assigned an id prior adding a mapping. + // True, cause product tags must be saved and assigned an id prior adding a mapping. _productTagRepository.AutoCommitEnabled = true; var productIds = batch.Select(x => x.Entity.Id).ToList(); @@ -1055,15 +1087,24 @@ protected virtual void ProcessProductTags(ImportExecuteContext context, IEnumera { try { - var tagNames = row.GetDataValue>("TagNames"); var product = row.Entity; + var tags = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var str in row.GetDataValue("TagNames").SplitSafe("|")) + { + var arr = str.SplitSafe("~"); + if (arr.Length > 0) + { + tags[arr[0]] = arr.Length > 1 ? arr[1].ToBool(true) : true; + } + } if (!tagsPerBatch.TryGetValue(product.Id, out var existingTags)) { existingTags = new List(); } - if (tagNames.IsNullOrEmpty()) + if (!tags.Any()) { // Remove all tags. if (existingTags.Any()) @@ -1078,7 +1119,7 @@ protected virtual void ProcessProductTags(ImportExecuteContext context, IEnumera var tagsToRemove = new List(); foreach (var existingTag in existingTags) { - if (!tagNames.Any(x => x.IsCaseInsensitiveEqual(existingTag.Name))) + if (!tags.Keys.Any(x => x.IsCaseInsensitiveEqual(existingTag.Name))) { tagsToRemove.Add(existingTag); } @@ -1090,14 +1131,18 @@ protected virtual void ProcessProductTags(ImportExecuteContext context, IEnumera } // Add tags. - foreach (var tagName in tagNames.Distinct()) + foreach (var tag in tags) { - if (!existingTags.Any(x => x.Name.IsCaseInsensitiveEqual(tagName))) + if (!existingTags.Any(x => x.Name.IsCaseInsensitiveEqual(tag.Key))) { - var productTag = _productTagRepository.Table.FirstOrDefault(x => x.Name == tagName); + var productTag = _productTagRepository.Table.FirstOrDefault(x => x.Name == tag.Key); if (productTag == null) { - productTag = new ProductTag { Name = tagName }; + productTag = new ProductTag + { + Name = tag.Key, + Published = tag.Value + }; _productTagRepository.Insert(productTag); } @@ -1117,36 +1162,23 @@ protected virtual void ProcessProductTags(ImportExecuteContext context, IEnumera } private int? ZeroToNull(object value, CultureInfo culture) - { - int result; - if (CommonHelper.TryConvert(value, culture, out result) && result > 0) - { - return result; - } - - return (int?)null; - } - - public static string[] SupportedKeyFields - { - get - { - return new string[] { "Id", "Sku", "Gtin", "ManufacturerPartNumber", "Name" }; - } - } - - public static string[] DefaultKeyFields - { - get - { - return new string[] { "Sku", "Gtin", "ManufacturerPartNumber" }; - } - } - - public class ImportProductMapping - { - public int DestinationId { get; set; } - public bool Inserted { get; set; } - } - } + { + if (CommonHelper.TryConvert(value, culture, out int result) && result > 0) + { + return result; + } + + return (int?)null; + } + + public static string[] SupportedKeyFields => new string[] { "Id", "Sku", "Gtin", "ManufacturerPartNumber", "Name" }; + + public static string[] DefaultKeyFields => new string[] { "Sku", "Gtin", "ManufacturerPartNumber" }; + + public class ImportProductMapping + { + public int DestinationId { get; set; } + public bool Inserted { get; set; } + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/ManufacturerService.cs b/src/Libraries/SmartStore.Services/Catalog/ManufacturerService.cs index ae476bb05d..218387e4cc 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ManufacturerService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ManufacturerService.cs @@ -1,19 +1,21 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Data.Entity; +using System.Linq; using SmartStore.Collections; using SmartStore.Core; using SmartStore.Core.Caching; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Security; +using SmartStore.Core.Domain.Seo; using SmartStore.Core.Domain.Stores; -using SmartStore.Core.Events; using SmartStore.Data.Caching; +using SmartStore.Services.Seo; namespace SmartStore.Services.Catalog { - public partial class ManufacturerService : IManufacturerService + public partial class ManufacturerService : IManufacturerService, IXmlSitemapPublisher { private const string PRODUCTMANUFACTURERS_ALLBYMANUFACTURERID_KEY = "productmanufacturer:allbymanufacturerid-{0}-{1}-{2}-{3}-{4}"; private const string PRODUCTMANUFACTURERS_ALLBYPRODUCTID_KEY = "productmanufacturer:allbyproductid-{0}-{1}-{2}"; @@ -23,69 +25,94 @@ public partial class ManufacturerService : IManufacturerService private readonly IRepository _manufacturerRepository; private readonly IRepository _productManufacturerRepository; private readonly IRepository _productRepository; - private readonly IRepository _storeMappingRepository; - private readonly IWorkContext _workContext; - private readonly IStoreContext _storeContext; - private readonly IEventPublisher _eventPublisher; + private readonly IRepository _storeMappingRepository; + private readonly IRepository _aclRepository; + private readonly IWorkContext _workContext; + private readonly IStoreContext _storeContext; private readonly IRequestCache _requestCache; - public ManufacturerService(IRequestCache requestCache, + public ManufacturerService(IRequestCache requestCache, IRepository manufacturerRepository, IRepository productManufacturerRepository, IRepository productRepository, - IRepository storeMappingRepository, - IWorkContext workContext, - IStoreContext storeContext, - IEventPublisher eventPublisher) + IRepository storeMappingRepository, + IRepository aclRepository, + IWorkContext workContext, + IStoreContext storeContext) { _requestCache = requestCache; _manufacturerRepository = manufacturerRepository; _productManufacturerRepository = productManufacturerRepository; _productRepository = productRepository; - _storeMappingRepository = storeMappingRepository; - _workContext = workContext; - _storeContext = storeContext; - _eventPublisher = eventPublisher; + _storeMappingRepository = storeMappingRepository; + _aclRepository = aclRepository; + _workContext = workContext; + _storeContext = storeContext; - this.QuerySettings = DbQuerySettings.Default; - } + QuerySettings = DbQuerySettings.Default; + } - public DbQuerySettings QuerySettings { get; set; } + public DbQuerySettings QuerySettings { get; set; } public virtual void DeleteManufacturer(Manufacturer manufacturer) { if (manufacturer == null) throw new ArgumentNullException("manufacturer"); - + manufacturer.Deleted = true; UpdateManufacturer(manufacturer); } - public virtual IQueryable GetManufacturers(bool showHidden = false, int storeId = 0) - { - var query = _manufacturerRepository.Table - .Where(m => !m.Deleted); + public virtual IQueryable GetManufacturers(bool showHidden = false, int storeId = 0) + { + var grouping = false; + var entityName = nameof(Manufacturer); + var query = _manufacturerRepository.Table.Where(m => !m.Deleted); - if (!showHidden) - query = query.Where(m => m.Published); + if (!showHidden) + { + query = query.Where(m => m.Published); + } - if (!QuerySettings.IgnoreMultiStore && storeId > 0) - { - query = from m in query - join sm in _storeMappingRepository.Table - on new { c1 = m.Id, c2 = "Manufacturer" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into m_sm - from sm in m_sm.DefaultIfEmpty() - where !m.LimitedToStores || storeId == sm.StoreId - select m; + // Store mapping. + if (!showHidden && storeId > 0 && !QuerySettings.IgnoreMultiStore) + { + query = from m in query + join sm in _storeMappingRepository.Table + on new { m1 = m.Id, m2 = entityName } equals new { m1 = sm.EntityId, m2 = sm.EntityName } into m_sm + from sm in m_sm.DefaultIfEmpty() + where !m.LimitedToStores || storeId == sm.StoreId + select m; - query = from m in query - group m by m.Id into mGroup - orderby mGroup.Key - select mGroup.FirstOrDefault(); - } + grouping = true; + } - return query; - } + // ACL (access control list). + if (!showHidden && !QuerySettings.IgnoreAcl) + { + var allowedCustomerRolesIds = _workContext.CurrentCustomer.GetRoleIds(); + + query = from m in query + join a in _aclRepository.Table + on new { m1 = m.Id, m2 = entityName } equals new { m1 = a.EntityId, m2 = a.EntityName } into ma + from a in ma.DefaultIfEmpty() + where !m.SubjectToAcl || allowedCustomerRolesIds.Contains(a.CustomerRoleId) + select m; + + grouping = true; + } + + if (grouping) + { + query = + from m in query + group m by m.Id into mGroup + orderby mGroup.Key + select mGroup.FirstOrDefault(); + } + + return query; + } public virtual IList GetAllManufacturers(bool showHidden = false) { @@ -94,19 +121,19 @@ public virtual IList GetAllManufacturers(bool showHidden = false) public virtual IList GetAllManufacturers(string manufacturerName, int storeId = 0, bool showHidden = false) { - var query = GetManufacturers(showHidden, storeId); + var query = GetManufacturers(showHidden, storeId); - if (manufacturerName.HasValue()) - query = query.Where(m => m.Name.Contains(manufacturerName)); + if (manufacturerName.HasValue()) + query = query.Where(m => m.Name.Contains(manufacturerName)); - query = query.OrderBy(m => m.DisplayOrder) - .ThenBy(m => m.Name); + query = query.OrderBy(m => m.DisplayOrder) + .ThenBy(m => m.Name); var manufacturers = query.ToList(); return manufacturers; } - public virtual IPagedList GetAllManufacturers(string manufacturerName, + public virtual IPagedList GetAllManufacturers(string manufacturerName, int pageIndex, int pageSize, int storeId = 0, bool showHidden = false) { var manufacturers = GetAllManufacturers(manufacturerName, storeId, showHidden); @@ -118,8 +145,8 @@ public virtual Manufacturer GetManufacturerById(int manufacturerId) if (manufacturerId == 0) return null; - return _manufacturerRepository.GetByIdCached(manufacturerId, "db.manu.id-" + manufacturerId); - } + return _manufacturerRepository.GetByIdCached(manufacturerId, "db.manu.id-" + manufacturerId); + } public virtual IList GetManufacturersByIds(int[] manufacturerIds) { @@ -162,15 +189,15 @@ public virtual void UpdateManufacturer(Manufacturer manufacturer) _requestCache.RemoveByPattern(PRODUCTMANUFACTURERS_PATTERN_KEY); } - public virtual void UpdateHasDiscountsApplied(Manufacturer manufacturer) - { - Guard.NotNull(manufacturer, nameof(manufacturer)); + public virtual void UpdateHasDiscountsApplied(Manufacturer manufacturer) + { + Guard.NotNull(manufacturer, nameof(manufacturer)); - manufacturer.HasDiscountsApplied = manufacturer.AppliedDiscounts.Count > 0; - UpdateManufacturer(manufacturer); - } + manufacturer.HasDiscountsApplied = manufacturer.AppliedDiscounts.Count > 0; + UpdateManufacturer(manufacturer); + } - public virtual void DeleteProductManufacturer(ProductManufacturer productManufacturer) + public virtual void DeleteProductManufacturer(ProductManufacturer productManufacturer) { if (productManufacturer == null) throw new ArgumentNullException("productManufacturer"); @@ -184,40 +211,24 @@ public virtual void DeleteProductManufacturer(ProductManufacturer productManufac public virtual IPagedList GetProductManufacturersByManufacturerId(int manufacturerId, int pageIndex, int pageSize, bool showHidden = false) { if (manufacturerId == 0) + { return new PagedList(new List(), pageIndex, pageSize); + } + + var storeId = _storeContext.CurrentStore.Id; + var storeToken = QuerySettings.IgnoreMultiStore ? "0" : storeId.ToString(); + var rolesToken = QuerySettings.IgnoreAcl || showHidden ? "0" : _workContext.CurrentCustomer.GetRolesIdent(); + var key = string.Format(PRODUCTMANUFACTURERS_ALLBYMANUFACTURERID_KEY, showHidden, manufacturerId, pageIndex, pageSize, rolesToken, storeToken); - string key = string.Format(PRODUCTMANUFACTURERS_ALLBYMANUFACTURERID_KEY, showHidden, manufacturerId, pageIndex, pageSize, _workContext.CurrentCustomer.Id, _storeContext.CurrentStore.Id); return _requestCache.Get(key, () => { var query = from pm in _productManufacturerRepository.Table join p in _productRepository.Table on pm.ProductId equals p.Id where pm.ManufacturerId == manufacturerId && !p.Deleted && (showHidden || p.Published) - orderby pm.DisplayOrder select pm; - if (!showHidden) - { - if (!QuerySettings.IgnoreMultiStore) - { - //Store mapping - var currentStoreId = _storeContext.CurrentStore.Id; - query = from pm in query - join m in _manufacturerRepository.Table on pm.ManufacturerId equals m.Id - join sm in _storeMappingRepository.Table - on new { c1 = m.Id, c2 = "Manufacturer" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into m_sm - from sm in m_sm.DefaultIfEmpty() - where !m.LimitedToStores || currentStoreId == sm.StoreId - select pm; - } - - //only distinct manufacturers (group by ID) - query = from pm in query - group pm by pm.Id into pmGroup - orderby pmGroup.Key - select pmGroup.FirstOrDefault(); - - query = query.OrderBy(pm => pm.DisplayOrder); - } + query = ApplyHiddenProductManufacturerFilter(query, storeId, showHidden); + query = query.OrderBy(pm => pm.DisplayOrder); var productManufacturers = new PagedList(query, pageIndex, pageSize); return productManufacturers; @@ -227,89 +238,75 @@ orderby pmGroup.Key public virtual IList GetProductManufacturersByProductId(int productId, bool showHidden = false) { if (productId == 0) + { return new List(); + } + + var storeId = _storeContext.CurrentStore.Id; + var storeToken = QuerySettings.IgnoreMultiStore ? "0" : storeId.ToString(); + var rolesToken = QuerySettings.IgnoreAcl || showHidden ? "0" : _workContext.CurrentCustomer.GetRolesIdent(); + var key = string.Format(PRODUCTMANUFACTURERS_ALLBYPRODUCTID_KEY, showHidden, productId, rolesToken, storeToken); - string key = string.Format(PRODUCTMANUFACTURERS_ALLBYPRODUCTID_KEY, showHidden, productId, _workContext.CurrentCustomer.Id, _storeContext.CurrentStore.Id); return _requestCache.Get(key, () => - { - var query = from pm in _productManufacturerRepository.Table - join m in _manufacturerRepository.Table on pm.ManufacturerId equals m.Id - where pm.ProductId == productId && !m.Deleted && (showHidden || m.Published) - orderby pm.DisplayOrder - select pm; - - if (!showHidden) - { - if (!QuerySettings.IgnoreMultiStore) - { - // Store mapping - var currentStoreId = _storeContext.CurrentStore.Id; - query = from pm in query - join m in _manufacturerRepository.Table on pm.ManufacturerId equals m.Id - join sm in _storeMappingRepository.Table - on new { c1 = m.Id, c2 = "Manufacturer" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into m_sm - from sm in m_sm.DefaultIfEmpty() - where !m.LimitedToStores || currentStoreId == sm.StoreId - select pm; - } - - // Only distinct manufacturers (group by ID) - query = from pm in query - group pm by pm.Id into mGroup - orderby mGroup.Key - select mGroup.FirstOrDefault(); - - query = query.OrderBy(pm => pm.DisplayOrder); - } - - query = query.Include(x => x.Manufacturer.Picture); - - var productManufacturers = query.ToList(); - return productManufacturers; - }); + { + var query = from pm in _productManufacturerRepository.Table + join m in _manufacturerRepository.Table on pm.ManufacturerId equals m.Id + where pm.ProductId == productId && !m.Deleted && (showHidden || m.Published) + select pm; + + query = ApplyHiddenProductManufacturerFilter(query, storeId, showHidden); + query = query.OrderBy(pm => pm.DisplayOrder); + query = query.Include(pm => pm.Manufacturer.MediaFile); + + var productManufacturers = query.ToList(); + return productManufacturers; + }); } - public virtual Multimap GetProductManufacturersByManufacturerIds(int[] manufacturerIds) - { - Guard.NotNull(manufacturerIds, nameof(manufacturerIds)); + public virtual Multimap GetProductManufacturersByManufacturerIds(int[] manufacturerIds, bool showHidden = false) + { + Guard.NotNull(manufacturerIds, nameof(manufacturerIds)); - var query = _productManufacturerRepository.TableUntracked - .Where(x => manufacturerIds.Contains(x.ManufacturerId)) - .OrderBy(x => x.DisplayOrder); + var query = _productManufacturerRepository.TableUntracked + .Where(x => manufacturerIds.Contains(x.ManufacturerId)); - var map = query - .ToList() - .ToMultimap(x => x.ManufacturerId, x => x); + query = ApplyHiddenProductManufacturerFilter(query, _storeContext.CurrentStore.Id, showHidden); + query = query.OrderBy(pm => pm.DisplayOrder); - return map; - } + var map = query + .ToList() + .ToMultimap(x => x.ManufacturerId, x => x); - public virtual Multimap GetProductManufacturersByProductIds(int[] productIds) - { - Guard.NotNull(productIds, nameof(productIds)); + return map; + } + + public virtual Multimap GetProductManufacturersByProductIds(int[] productIds, bool showHidden = false) + { + Guard.NotNull(productIds, nameof(productIds)); if (!productIds.Any()) { return new Multimap(); } - var query = - from pm in _productManufacturerRepository.TableUntracked - //join m in _manufacturerRepository.TableUntracked on pm.ManufacturerId equals m.Id // Eager loading does not work with this join - where !pm.Manufacturer.Deleted && productIds.Contains(pm.ProductId) - select pm; + var query = + from pm in _productManufacturerRepository.TableUntracked + //join m in _manufacturerRepository.TableUntracked on pm.ManufacturerId equals m.Id // Eager loading does not work with this join + where !pm.Manufacturer.Deleted && productIds.Contains(pm.ProductId) + select pm; - query = query.Include(x => x.Manufacturer.Picture); + query = ApplyHiddenProductManufacturerFilter(query, _storeContext.CurrentStore.Id, showHidden); + query = query.Include(x => x.Manufacturer.MediaFile); - var map = query - .OrderBy(x => x.ProductId) - .ThenBy(x => x.DisplayOrder) - .ToList() - .ToMultimap(x => x.ProductId, x => x); + var map = query + .OrderBy(x => x.ProductId) + .ThenBy(x => x.DisplayOrder) + .ToList() + .ToMultimap(x => x.ProductId, x => x); + + return map; + } - return map; - } - public virtual ProductManufacturer GetProductManufacturerById(int productManufacturerId) { if (productManufacturerId == 0) @@ -340,5 +337,88 @@ public virtual void UpdateProductManufacturer(ProductManufacturer productManufac _requestCache.RemoveByPattern(MANUFACTURERS_PATTERN_KEY); _requestCache.RemoveByPattern(PRODUCTMANUFACTURERS_PATTERN_KEY); } + + protected virtual IQueryable ApplyHiddenProductManufacturerFilter(IQueryable query, int storeId = 0, bool showHidden = false) + { + var entityName = nameof(Manufacturer); + var grouping = false; + + // Store mapping. + if (!showHidden && storeId > 0 && !QuerySettings.IgnoreMultiStore) + { + query = + from pm in query + join m in _manufacturerRepository.Table on pm.ManufacturerId equals m.Id + join sm in _storeMappingRepository.Table + on new { m1 = m.Id, m2 = entityName } equals new { m1 = sm.EntityId, m2 = sm.EntityName } into m_sm + from sm in m_sm.DefaultIfEmpty() + where !m.LimitedToStores || storeId == sm.StoreId + select pm; + + grouping = true; + } + + // ACL (access control list). + if (!showHidden && !QuerySettings.IgnoreAcl) + { + var allowedCustomerRolesIds = _workContext.CurrentCustomer.GetRoleIds(); + + query = + from pm in query + join m in _manufacturerRepository.Table on pm.ManufacturerId equals m.Id + join a in _aclRepository.Table + on new { m1 = m.Id, m2 = entityName } equals new { m1 = a.EntityId, m2 = a.EntityName } into ma + from a in ma.DefaultIfEmpty() + where !m.SubjectToAcl || allowedCustomerRolesIds.Contains(a.CustomerRoleId) + select pm; + + grouping = true; + } + + if (grouping) + { + query = + from pm in query + group pm by pm.Id into pmGroup + orderby pmGroup.Key + select pmGroup.FirstOrDefault(); + } + + return query; + } + + #region XML Sitemap + + public XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context) + { + if (!context.LoadSetting().XmlSitemapIncludesManufacturers) + return null; + + var query = GetManufacturers(false, context.RequestStoreId).OrderBy(x => x.DisplayOrder).ThenBy(x => x.Name); + return new ManufacturerXmlSitemapResult { Query = query }; + } + + class ManufacturerXmlSitemapResult : XmlSitemapProvider + { + public IQueryable Query { get; set; } + + public override int GetTotalCount() + { + return Query.Count(); + } + + public override IEnumerable Enlist() + { + var topics = Query.Select(x => new { x.Id, x.UpdatedOnUtc }).ToList(); + foreach (var x in topics) + { + yield return new NamedEntity { EntityName = "Manufacturer", Id = x.Id, LastMod = x.UpdatedOnUtc }; + } + } + + public override int Order => int.MinValue + 100; + } + + #endregion } } diff --git a/src/Libraries/SmartStore.Services/Catalog/ManufacturerTemplateService.cs b/src/Libraries/SmartStore.Services/Catalog/ManufacturerTemplateService.cs index eae06162de..82df97a567 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ManufacturerTemplateService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ManufacturerTemplateService.cs @@ -35,7 +35,7 @@ orderby pt.DisplayOrder var templates = query.ToList(); return templates; } - + public virtual ManufacturerTemplate GetManufacturerTemplateById(int manufacturerTemplateId) { if (manufacturerTemplateId == 0) diff --git a/src/Libraries/SmartStore.Services/Catalog/Modelling/CheckoutAttributeQueryItem.cs b/src/Libraries/SmartStore.Services/Catalog/Modelling/CheckoutAttributeQueryItem.cs index cc35919d93..81014679e0 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Modelling/CheckoutAttributeQueryItem.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Modelling/CheckoutAttributeQueryItem.cs @@ -2,48 +2,48 @@ namespace SmartStore.Services.Catalog.Modelling { - public class CheckoutAttributeQueryItem - { - public CheckoutAttributeQueryItem(int attributeId, string value) - { - Value = value ?? string.Empty; - AttributeId = attributeId; - } + public class CheckoutAttributeQueryItem + { + public CheckoutAttributeQueryItem(int attributeId, string value) + { + Value = value ?? string.Empty; + AttributeId = attributeId; + } - /// - /// Key used for form names. - /// - /// Checkout attribute identifier - /// Key - public static string CreateKey(int attributeId) - { - return $"cattr{attributeId}"; - } + /// + /// Key used for form names. + /// + /// Checkout attribute identifier + /// Key + public static string CreateKey(int attributeId) + { + return $"cattr{attributeId}"; + } - public string Value { get; private set; } - public int AttributeId { get; private set; } - public DateTime? Date { get; set; } - public bool IsFile { get; set; } - public bool IsText { get; set; } + public string Value { get; private set; } + public int AttributeId { get; private set; } + public DateTime? Date { get; set; } + public bool IsFile { get; set; } + public bool IsText { get; set; } - public override string ToString() - { - var key = CreateKey(AttributeId); + public override string ToString() + { + var key = CreateKey(AttributeId); - if (Date.HasValue) - { - return key + "-date"; - } - else if (IsFile) - { - return key + "-file"; - } - else if (IsText) - { - return key + "-text"; - } + if (Date.HasValue) + { + return key + "-date"; + } + else if (IsFile) + { + return key + "-file"; + } + else if (IsText) + { + return key + "-text"; + } - return key; - } - } + return key; + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/Modelling/GiftCardQueryItem.cs b/src/Libraries/SmartStore.Services/Catalog/Modelling/GiftCardQueryItem.cs index df61daf13b..26cc81b71e 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Modelling/GiftCardQueryItem.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Modelling/GiftCardQueryItem.cs @@ -1,40 +1,40 @@ namespace SmartStore.Services.Catalog.Modelling { - public class GiftCardQueryItem - { - public GiftCardQueryItem(string name, string value) - { - Guard.NotEmpty(name, nameof(name)); + public class GiftCardQueryItem + { + public GiftCardQueryItem(string name, string value) + { + Guard.NotEmpty(name, nameof(name)); - Name = name.ToLower(); - Value = value ?? string.Empty; + Name = name.ToLower(); + Value = value ?? string.Empty; - if (Name.StartsWith(".")) - { - Name = Name.Substring(1); - } - } + if (Name.StartsWith(".")) + { + Name = Name.Substring(1); + } + } - public static string CreateKey(int productId, int bundleItemId, string name) - { - if (name.HasValue()) - { - return $"giftcard{productId}-{bundleItemId}-.{name.EmptyNull().ToLower()}"; - } + public static string CreateKey(int productId, int bundleItemId, string name) + { + if (name.HasValue()) + { + return $"giftcard{productId}-{bundleItemId}-.{name.EmptyNull().ToLower()}"; + } - // Just return field prefix for partial views. - return $"giftcard{productId}-{bundleItemId}-"; - } + // Just return field prefix for partial views. + return $"giftcard{productId}-{bundleItemId}-"; + } - public string Name { get; private set; } - public string Value { get; private set; } + public string Name { get; private set; } + public string Value { get; private set; } - public int ProductId { get; set; } - public int BundleItemId { get; set; } + public int ProductId { get; set; } + public int BundleItemId { get; set; } - public override string ToString() - { - return CreateKey(ProductId, BundleItemId, Name); - } - } + public override string ToString() + { + return CreateKey(ProductId, BundleItemId, Name); + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/Modelling/IProductVariantQueryFactory.cs b/src/Libraries/SmartStore.Services/Catalog/Modelling/IProductVariantQueryFactory.cs index 119a770832..178e8d4efc 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Modelling/IProductVariantQueryFactory.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Modelling/IProductVariantQueryFactory.cs @@ -1,17 +1,17 @@ namespace SmartStore.Services.Catalog.Modelling { - public interface IProductVariantQueryFactory - { - /// - /// The last created product variant query. The MVC model binder uses this property to avoid repeated binding. - /// - ProductVariantQuery Current { get; } + public interface IProductVariantQueryFactory + { + /// + /// The last created product variant query. The MVC model binder uses this property to avoid repeated binding. + /// + ProductVariantQuery Current { get; } - /// - /// Creates a name value collection with product variants from the current - /// by looking up corresponding keys in posted form and/or query string - /// - /// Product variant query - ProductVariantQuery CreateFromQuery(); - } + /// + /// Creates a name value collection with product variants from the current + /// by looking up corresponding keys in posted form and/or query string + /// + /// Product variant query + ProductVariantQuery CreateFromQuery(); + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQuery.cs b/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQuery.cs index 1eca518b05..cf035adc29 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQuery.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQuery.cs @@ -3,77 +3,72 @@ namespace SmartStore.Services.Catalog.Modelling { - public class ProductVariantQuery - { - private readonly List _variants; - private readonly List _giftCards; - private readonly List _checkoutAttributes; + public class ProductVariantQuery + { + private readonly List _variants; + private readonly List _giftCards; + private readonly List _checkoutAttributes; - public ProductVariantQuery() - { - _variants = new List(); - _giftCards = new List(); - _checkoutAttributes = new List(); - } + public ProductVariantQuery() + { + _variants = new List(); + _giftCards = new List(); + _checkoutAttributes = new List(); + } - public IReadOnlyList Variants - { - get - { - return _variants; - } - } + public int VariantCombinationId { get; set; } - public IReadOnlyList GiftCards - { - get - { - return _giftCards; - } - } + public IReadOnlyList Variants => _variants; - public IReadOnlyList CheckoutAttributes - { - get - { - return _checkoutAttributes; - } - } + public IReadOnlyList GiftCards => _giftCards; - public void AddVariant(ProductVariantQueryItem item) - { - _variants.Add(item); - } + public IReadOnlyList CheckoutAttributes => _checkoutAttributes; - public void AddGiftCard(GiftCardQueryItem item) - { - _giftCards.Add(item); - } + public void AddVariant(ProductVariantQueryItem item) + { + var exists = _variants.Any(x => + x.ProductId == item.ProductId && + x.BundleItemId == item.BundleItemId && + x.AttributeId == item.AttributeId && + x.VariantAttributeId == item.VariantAttributeId && + x.Value == item.Value + ); - public void AddCheckoutAttribute(CheckoutAttributeQueryItem item) - { - _checkoutAttributes.Add(item); - } + if (!exists) + { + _variants.Add(item); + } + } - public string GetGiftCardValue(int productId, int bundleItemId, string name) - { - return _giftCards.FirstOrDefault(x => - x.ProductId == productId && - x.BundleItemId == bundleItemId && - x.Name.IsCaseInsensitiveEqual(name)) - ?.Value; - } + public void AddGiftCard(GiftCardQueryItem item) + { + _giftCards.Add(item); + } - public override string ToString() - { - var groups = new string[] - { - string.Join("&", Variants.Select(x => x.ToString())), - string.Join("&", GiftCards.Select(x => x.ToString())), - string.Join("&", CheckoutAttributes.Select(x => x.ToString())) - }; + public void AddCheckoutAttribute(CheckoutAttributeQueryItem item) + { + _checkoutAttributes.Add(item); + } - return string.Join("&", groups.Where(x => x.HasValue())); - } - } + public string GetGiftCardValue(int productId, int bundleItemId, string name) + { + return _giftCards.FirstOrDefault(x => + x.ProductId == productId && + x.BundleItemId == bundleItemId && + x.Name.IsCaseInsensitiveEqual(name)) + ?.Value; + } + + public override string ToString() + { + var groups = new string[] + { + string.Join("&", Variants.Select(x => x.ToString())), + string.Join("&", GiftCards.Select(x => x.ToString())), + string.Join("&", CheckoutAttributes.Select(x => x.ToString())) + }; + + return string.Join("&", groups.Where(x => x.HasValue())); + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQueryFactory.cs b/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQueryFactory.cs index 954eebf7d5..c7cb504693 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQueryFactory.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQueryFactory.cs @@ -4,74 +4,71 @@ using System.Text.RegularExpressions; using System.Web; using SmartStore.Collections; -using SmartStore.Core.Html; using SmartStore.Services.Search.Modelling; namespace SmartStore.Services.Catalog.Modelling { - public class ProductVariantQueryFactory : IProductVariantQueryFactory - { - internal static readonly Regex IsVariantKey = new Regex(@"pvari[0-9]+-[0-9]+-[0-9]+-[0-9]+", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - internal static readonly Regex IsVariantAliasKey = new Regex(@"\w+-[0-9]+-[0-9]+-[0-9]+", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - internal static readonly Regex IsGiftCardKey = new Regex(@"giftcard[0-9]+-[0-9]+-\.\w+$", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - internal static readonly Regex IsCheckoutAttributeKey = new Regex(@"cattr[0-9]+", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - - protected readonly HttpContextBase _httpContext; - protected readonly ICommonServices _services; - protected readonly ICatalogSearchQueryAliasMapper _catalogSearchQueryAliasMapper; - private Multimap _queryItems; - - public ProductVariantQueryFactory( - HttpContextBase httpContext, - ICommonServices services, - ICatalogSearchQueryAliasMapper catalogSearchQueryAliasMapper) - { - _httpContext = httpContext; - _services = services; - _catalogSearchQueryAliasMapper = catalogSearchQueryAliasMapper; - } - - protected Multimap QueryItems - { - get - { - if (_queryItems == null) - { - _queryItems = new Multimap(); - - if (_httpContext.Request?.Unvalidated != null) - { - var form = _httpContext.Request.Unvalidated.Form; - - if (form != null) - { - foreach (var key in form.AllKeys) - { - if (key.HasValue()) - { - _queryItems.AddRange(key, form[key].SplitSafe(",")); - } - } - } + public class ProductVariantQueryFactory : IProductVariantQueryFactory + { + internal static readonly Regex IsVariantKey = new Regex(@"pvari[0-9]+-[0-9]+-[0-9]+-[0-9]+", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); + internal static readonly Regex IsVariantAliasKey = new Regex(@"\w+-[0-9]+-[0-9]+-[0-9]+", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); + internal static readonly Regex IsGiftCardKey = new Regex(@"giftcard[0-9]+-[0-9]+-\.\w+$", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); + internal static readonly Regex IsCheckoutAttributeKey = new Regex(@"cattr[0-9]+", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); + + protected readonly HttpContextBase _httpContext; + protected readonly ICommonServices _services; + protected readonly ICatalogSearchQueryAliasMapper _catalogSearchQueryAliasMapper; + private Multimap _queryItems; + + public ProductVariantQueryFactory( + HttpContextBase httpContext, + ICommonServices services, + ICatalogSearchQueryAliasMapper catalogSearchQueryAliasMapper) + { + _httpContext = httpContext; + _services = services; + _catalogSearchQueryAliasMapper = catalogSearchQueryAliasMapper; + } - var query = _httpContext.Request.Unvalidated.QueryString; + protected Multimap QueryItems + { + get + { + if (_queryItems == null) + { + _queryItems = new Multimap(); + if (_httpContext.Request?.Unvalidated != null) + { + var form = _httpContext.Request.Unvalidated.Form; + if (form != null) + { + foreach (var key in form.AllKeys) + { + if (key.HasValue()) + { + _queryItems.AddRange(key, form[key].SplitSafe(",")); + } + } + } + + var query = _httpContext.Request.Unvalidated.QueryString; if (query != null) - { - foreach (var key in query.AllKeys) - { - if (key.HasValue()) - { - _queryItems.AddRange(key, query[key].SplitSafe(",")); - } - } - } - } - } - - return _queryItems; - } - } + { + foreach (var key in query.AllKeys) + { + if (key.HasValue()) + { + _queryItems.AddRange(key, query[key].SplitSafe(",")); + } + } + } + } + } + + return _queryItems; + } + } public ProductVariantQuery Current { get; private set; } @@ -108,6 +105,12 @@ public ProductVariantQuery CreateFromQuery() { ConvertVariantAlias(query, item.Key, item.Value, languageId); } + else if (item.Key.IsCaseInsensitiveEqual("pvari") && + int.TryParse(item.Value.FirstOrDefault()?.NullEmpty() ?? "0", out var variantCombinationId) && + variantCombinationId != 0) + { + query.VariantCombinationId = variantCombinationId; + } else { ConvertItems(_httpContext.Request, query, item.Key, item.Value); @@ -118,53 +121,65 @@ public ProductVariantQuery CreateFromQuery() } private DateTime? ConvertToDate(string key, string value) - { - var year = 0; - var month = 0; - var day = 0; - - if (key.EndsWith("-date")) - { - // Convert from one query string item. - var dateItems = value.SplitSafe("-"); - year = dateItems.SafeGet(0).ToInt(); - month = dateItems.SafeGet(1).ToInt(); - day = dateItems.SafeGet(2).ToInt(); - } - else if (key.EndsWith("-year")) - { - // Convert from three form controls. - var dateKey = key.Replace("-year", ""); - year = value.ToInt(); - month = QueryItems[dateKey + "-month"].FirstOrDefault()?.ToInt() ?? 0; - day = QueryItems[dateKey + "-day"].FirstOrDefault()?.ToInt() ?? 0; - } - - if (year > 0 && month > 0 && day > 0) - { - try - { - return new DateTime(year, month, day); - } - catch { } - } - - return null; - } - - protected virtual void ConvertVariant(ProductVariantQuery query, string key, ICollection values) - { - var ids = key.Replace("pvari", "").SplitSafe("-"); - if (ids.Length < 4) - return; - - var isDate = key.EndsWith("-date") || key.EndsWith("-year"); - var isFile = key.EndsWith("-file"); - var isText = key.EndsWith("-text"); - - if (isDate || isFile || isText) - { - var value = isText ? string.Join(",", values) : values.First(); + { + var year = 0; + var month = 0; + var day = 0; + + if (key.EndsWith("-date")) + { + // Convert from one query string item. + var dateItems = value.SplitSafe("-"); + year = dateItems.SafeGet(0).ToInt(); + month = dateItems.SafeGet(1).ToInt(); + day = dateItems.SafeGet(2).ToInt(); + } + else if (key.EndsWith("-year")) + { + // Convert from three form controls. + var dateKey = key.Replace("-year", ""); + year = value.ToInt(); + + if (QueryItems.ContainsKey(dateKey + "-month")) + { + var str = QueryItems[dateKey + "-month"].FirstOrDefault(); + month = str?.ToInt() ?? 0; + } + + if (QueryItems.ContainsKey(dateKey + "-day")) + { + var str = QueryItems[dateKey + "-day"].FirstOrDefault(); + day = str?.ToInt() ?? 0; + } + } + + if (year > 0 && month > 0 && day > 0) + { + try + { + return new DateTime(year, month, day); + } + catch + { + } + } + + return null; + } + + protected virtual void ConvertVariant(ProductVariantQuery query, string key, ICollection values) + { + var ids = key.Replace("pvari", "").SplitSafe("-"); + if (ids.Length < 4) + return; + + var isDate = key.EndsWith("-date") || key.EndsWith("-year"); + var isFile = key.EndsWith("-file"); + var isText = key.EndsWith("-text"); + + if (isDate || isFile || isText) + { + var value = isText ? string.Join(",", values) : values.First(); var variant = new ProductVariantQueryItem(value) { ProductId = ids[0].ToInt(), @@ -176,16 +191,16 @@ protected virtual void ConvertVariant(ProductVariantQuery query, string key, ICo }; if (isDate) - { - variant.Date = ConvertToDate(key, value); - } - - query.AddVariant(variant); - } - else - { - foreach (var value in values) - { + { + variant.Date = ConvertToDate(key, value); + } + + query.AddVariant(variant); + } + else + { + foreach (var value in values) + { var variant = new ProductVariantQueryItem(value) { ProductId = ids[0].ToInt(), @@ -195,42 +210,42 @@ protected virtual void ConvertVariant(ProductVariantQuery query, string key, ICo }; query.AddVariant(variant); - } - } - } - - protected virtual void ConvertVariantAlias(ProductVariantQuery query, string key, ICollection values, int languageId) - { - var ids = key.SplitSafe("-"); - var len = ids.Length; - if (len < 4) - return; - - var isDate = key.EndsWith("-date") || key.EndsWith("-year"); - var isFile = key.EndsWith("-file"); - var isText = key.EndsWith("-text"); - - if (isDate || isFile || isText) - { - ids = ids.Take(len - 1).ToArray(); - len = ids.Length; - } - - var alias = string.Join("-", ids.Take(len - 3)); - var attributeId = _catalogSearchQueryAliasMapper.GetVariantIdByAlias(alias, languageId); - if (attributeId == 0) - return; - - var productId = ids.SafeGet(len - 3).ToInt(); - var bundleItemId = ids.SafeGet(len - 2).ToInt(); - var variantAttributeId = ids.SafeGet(len - 1).ToInt(); - - if (productId == 0 || variantAttributeId == 0) - return; - - if (isDate || isFile || isText) - { - var value = isText ? string.Join(",", values) : values.First(); + } + } + } + + protected virtual void ConvertVariantAlias(ProductVariantQuery query, string key, ICollection values, int languageId) + { + var ids = key.SplitSafe("-"); + var len = ids.Length; + if (len < 4) + return; + + var isDate = key.EndsWith("-date") || key.EndsWith("-year"); + var isFile = key.EndsWith("-file"); + var isText = key.EndsWith("-text"); + + if (isDate || isFile || isText) + { + ids = ids.Take(len - 1).ToArray(); + len = ids.Length; + } + + var alias = string.Join("-", ids.Take(len - 3)); + var attributeId = _catalogSearchQueryAliasMapper.GetVariantIdByAlias(alias, languageId); + if (attributeId == 0) + return; + + var productId = ids.SafeGet(len - 3).ToInt(); + var bundleItemId = ids.SafeGet(len - 2).ToInt(); + var variantAttributeId = ids.SafeGet(len - 1).ToInt(); + + if (productId == 0 || variantAttributeId == 0) + return; + + if (isDate || isFile || isText) + { + var value = isText ? string.Join(",", values) : values.First(); var variant = new ProductVariantQueryItem(value) { ProductId = productId, @@ -243,27 +258,27 @@ protected virtual void ConvertVariantAlias(ProductVariantQuery query, string key }; if (isDate) - { - variant.Date = ConvertToDate(key, value); - } - - query.AddVariant(variant); - } - else - { - foreach (var value in values) - { - // We cannot use GetVariantOptionIdByAlias. It doesn't necessarily provide a ProductVariantAttributeValue.Id associated with this product. - //var optionId = _catalogSearchQueryAliasMapper.GetVariantOptionIdByAlias(value, attributeId, languageId); - var optionId = 0; - string valueAlias = null; - - var valueIds = value.SplitSafe("-"); - if (valueIds.Length >= 2) - { - optionId = valueIds.SafeGet(valueIds.Length - 1).ToInt(); - valueAlias = string.Join("-", valueIds.Take(valueIds.Length - 1)); - } + { + variant.Date = ConvertToDate(key, value); + } + + query.AddVariant(variant); + } + else + { + foreach (var value in values) + { + // We cannot use GetVariantOptionIdByAlias. It doesn't necessarily provide a ProductVariantAttributeValue.Id associated with this product. + //var optionId = _catalogSearchQueryAliasMapper.GetVariantOptionIdByAlias(value, attributeId, languageId); + var optionId = 0; + string valueAlias = null; + + var valueIds = value.SplitSafe("-"); + if (valueIds.Length >= 2) + { + optionId = valueIds.SafeGet(valueIds.Length - 1).ToInt(); + valueAlias = string.Join("-", valueIds.Take(valueIds.Length - 1)); + } var variant = new ProductVariantQueryItem(optionId == 0 ? value : optionId.ToString()) { @@ -275,42 +290,42 @@ protected virtual void ConvertVariantAlias(ProductVariantQuery query, string key }; if (optionId != 0) - { - variant.ValueAlias = valueAlias; - } - - query.AddVariant(variant); - } - } - } - - protected virtual void ConvertGiftCard(ProductVariantQuery query, string key, string value) - { - var elements = key.Replace("giftcard", "").SplitSafe("-"); - if (elements.Length > 2) - { - var giftCard = new GiftCardQueryItem(elements[2], value); - giftCard.ProductId = elements[0].ToInt(); - giftCard.BundleItemId = elements[1].ToInt(); - - query.AddGiftCard(giftCard); - } - } - - protected virtual void ConvertCheckoutAttribute(ProductVariantQuery query, string key, ICollection values) - { - var ids = key.Replace("cattr", "").SplitSafe("-"); - if (ids.Length <= 0) - return; - - var attributeId = ids[0].ToInt(); - var isDate = key.EndsWith("-date") || key.EndsWith("-year"); - var isFile = key.EndsWith("-file"); - var isText = key.EndsWith("-text"); - - if (isDate || isFile || isText) - { - var value = isText ? string.Join(",", values) : values.First(); + { + variant.ValueAlias = valueAlias; + } + + query.AddVariant(variant); + } + } + } + + protected virtual void ConvertGiftCard(ProductVariantQuery query, string key, string value) + { + var elements = key.Replace("giftcard", "").SplitSafe("-"); + if (elements.Length > 2) + { + var giftCard = new GiftCardQueryItem(elements[2], value); + giftCard.ProductId = elements[0].ToInt(); + giftCard.BundleItemId = elements[1].ToInt(); + + query.AddGiftCard(giftCard); + } + } + + protected virtual void ConvertCheckoutAttribute(ProductVariantQuery query, string key, ICollection values) + { + var ids = key.Replace("cattr", "").SplitSafe("-"); + if (ids.Length <= 0) + return; + + var attributeId = ids[0].ToInt(); + var isDate = key.EndsWith("-date") || key.EndsWith("-year"); + var isFile = key.EndsWith("-file"); + var isText = key.EndsWith("-text"); + + if (isDate || isFile || isText) + { + var value = isText ? string.Join(",", values) : values.First(); var attribute = new CheckoutAttributeQueryItem(attributeId, value) { IsFile = isFile, @@ -318,23 +333,23 @@ protected virtual void ConvertCheckoutAttribute(ProductVariantQuery query, strin }; if (isDate) - { - attribute.Date = ConvertToDate(key, value); - } - - query.AddCheckoutAttribute(attribute); - } - else - { - foreach (var value in values) - { - query.AddCheckoutAttribute(new CheckoutAttributeQueryItem(attributeId, value)); - } - } - } - - protected virtual void ConvertItems(HttpRequestBase request, ProductVariantQuery query, string key, ICollection values) - { - } - } + { + attribute.Date = ConvertToDate(key, value); + } + + query.AddCheckoutAttribute(attribute); + } + else + { + foreach (var value in values) + { + query.AddCheckoutAttribute(new CheckoutAttributeQueryItem(attributeId, value)); + } + } + } + + protected virtual void ConvertItems(HttpRequestBase request, ProductVariantQuery query, string key, ICollection values) + { + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQueryItem.cs b/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQueryItem.cs index a41f3ea64b..aacf8f9997 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQueryItem.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQueryItem.cs @@ -2,59 +2,59 @@ namespace SmartStore.Services.Catalog.Modelling { - public class ProductVariantQueryItem - { - public ProductVariantQueryItem(string value) - { - Value = value ?? string.Empty; - } - - /// - /// Key used for form names. - /// - /// Product identifier - /// Bundle item identifier. 0 if not a bundle item. - /// Product attribute identifier - /// Product variant attribute identifier - /// Key - public static string CreateKey(int productId, int bundleItemId, int attributeId, int variantAttributeId) - { - return $"pvari{productId}-{bundleItemId}-{attributeId}-{variantAttributeId}"; - } - - public string Value { get; private set; } - - public int ProductId { get; set; } - public int BundleItemId { get; set; } - public int AttributeId { get; set; } - public int VariantAttributeId { get; set; } - public DateTime? Date { get; set; } - public bool IsFile { get; set; } - public bool IsText { get; set; } - - public string Alias { get; set; } - public string ValueAlias { get; set; } - - public override string ToString() - { - var key = Alias.HasValue() - ? $"{Alias}-{ProductId}-{BundleItemId}-{VariantAttributeId}" - : CreateKey(ProductId, BundleItemId, AttributeId, VariantAttributeId); - - if (Date.HasValue) - { - return key + "-date"; - } - else if (IsFile) - { - return key + "-file"; - } - else if (IsText) - { - return key + "-text"; - } - - return key; - } - } + public class ProductVariantQueryItem + { + public ProductVariantQueryItem(string value) + { + Value = value ?? string.Empty; + } + + /// + /// Key used for form names. + /// + /// Product identifier + /// Bundle item identifier. 0 if not a bundle item. + /// Product attribute identifier + /// Product variant attribute identifier + /// Key + public static string CreateKey(int productId, int bundleItemId, int attributeId, int variantAttributeId) + { + return $"pvari{productId}-{bundleItemId}-{attributeId}-{variantAttributeId}"; + } + + public string Value { get; private set; } + + public int ProductId { get; set; } + public int BundleItemId { get; set; } + public int AttributeId { get; set; } + public int VariantAttributeId { get; set; } + public DateTime? Date { get; set; } + public bool IsFile { get; set; } + public bool IsText { get; set; } + + public string Alias { get; set; } + public string ValueAlias { get; set; } + + public override string ToString() + { + var key = Alias.HasValue() + ? $"{Alias}-{ProductId}-{BundleItemId}-{VariantAttributeId}" + : CreateKey(ProductId, BundleItemId, AttributeId, VariantAttributeId); + + if (Date.HasValue) + { + return key + "-date"; + } + else if (IsFile) + { + return key + "-file"; + } + else if (IsText) + { + return key + "-text"; + } + + return key; + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQueryModelBinder.cs b/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQueryModelBinder.cs index 536553d3ff..ebb79e92b8 100644 --- a/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQueryModelBinder.cs +++ b/src/Libraries/SmartStore.Services/Catalog/Modelling/ProductVariantQueryModelBinder.cs @@ -3,39 +3,39 @@ namespace SmartStore.Services.Catalog.Modelling { - [ModelBinderType(typeof(ProductVariantQuery))] - public class ProductAttributeQueryModelBinder : IModelBinder - { - private readonly IProductVariantQueryFactory _factory; + [ModelBinderType(typeof(ProductVariantQuery))] + public class ProductAttributeQueryModelBinder : IModelBinder + { + private readonly IProductVariantQueryFactory _factory; - public ProductAttributeQueryModelBinder(IProductVariantQueryFactory factory) - { - _factory = factory; - } + public ProductAttributeQueryModelBinder(IProductVariantQueryFactory factory) + { + _factory = factory; + } - public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) - { - if (_factory.Current != null) - { - // Don't bind again for current request - return _factory.Current; - } + public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) + { + if (_factory.Current != null) + { + // Don't bind again for current request + return _factory.Current; + } - if (controllerContext.IsChildAction) - { - // Never attempt to bind in child actions. We require the binding to happen in a parent action. - return _factory.Current; - } + if (controllerContext.IsChildAction) + { + // Never attempt to bind in child actions. We require the binding to happen in a parent action. + return _factory.Current; + } - var modelType = bindingContext.ModelType; + var modelType = bindingContext.ModelType; - if (modelType != typeof(ProductVariantQuery)) - { - return new ProductVariantQuery(); - } + if (modelType != typeof(ProductVariantQuery)) + { + return new ProductVariantQuery(); + } - var query = _factory.CreateFromQuery(); - return query; - } - } + var query = _factory.CreateFromQuery(); + return query; + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/PriceCalculationContext.cs b/src/Libraries/SmartStore.Services/Catalog/PriceCalculationContext.cs index eea8195b4f..14a4cf1495 100644 --- a/src/Libraries/SmartStore.Services/Catalog/PriceCalculationContext.cs +++ b/src/Libraries/SmartStore.Services/Catalog/PriceCalculationContext.cs @@ -11,72 +11,72 @@ namespace SmartStore.Services.Catalog /// Cargo data to reduce database round trips during price calculation /// public class PriceCalculationContext - { - protected List _productIds; - private List _productIdsTierPrices; - private List _productIdsAppliedDiscounts; - private List _bundledProductIds; + { + protected List _productIds; + private List _productIdsTierPrices; + private List _productIdsAppliedDiscounts; + private List _bundledProductIds; private List _groupedProductIds; private Func> _funcAttributes; - private Func> _funcAttributeCombinations; - private Func> _funcTierPrices; - private Func> _funcProductCategories; - private Func> _funcProductManufacturers; - private Func> _funcAppliedDiscounts; - private Func> _funcProductBundleItems; + private Func> _funcAttributeCombinations; + private Func> _funcTierPrices; + private Func> _funcProductCategories; + private Func> _funcProductManufacturers; + private Func> _funcAppliedDiscounts; + private Func> _funcProductBundleItems; private Func> _funcAssociatedProducts; private LazyMultimap _attributes; - private LazyMultimap _attributeCombinations; - private LazyMultimap _tierPrices; - private LazyMultimap _productCategories; - private LazyMultimap _productManufacturers; - private LazyMultimap _appliedDiscounts; - private LazyMultimap _productBundleItems; + private LazyMultimap _attributeCombinations; + private LazyMultimap _tierPrices; + private LazyMultimap _productCategories; + private LazyMultimap _productManufacturers; + private LazyMultimap _appliedDiscounts; + private LazyMultimap _productBundleItems; private LazyMultimap _associatedProducts; public PriceCalculationContext(IEnumerable products, - Func> attributes, - Func> attributeCombinations, - Func> tierPrices, - Func> productCategories, - Func> productManufacturers, - Func> appliedDiscounts, - Func> productBundleItems, + Func> attributes, + Func> attributeCombinations, + Func> tierPrices, + Func> productCategories, + Func> productManufacturers, + Func> appliedDiscounts, + Func> productBundleItems, Func> associatedProducts) - { - if (products == null) - { - _productIds = new List(); - _productIdsTierPrices = new List(); - _productIdsAppliedDiscounts = new List(); - _bundledProductIds = new List(); + { + if (products == null) + { + _productIds = new List(); + _productIdsTierPrices = new List(); + _productIdsAppliedDiscounts = new List(); + _bundledProductIds = new List(); _groupedProductIds = new List(); - } - else - { - _productIds = new List(products.Select(x => x.Id)); - _productIdsTierPrices = new List(products.Where(x => x.HasTierPrices).Select(x => x.Id)); - _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)); + } + else + { + _productIds = new List(products.Select(x => x.Id)); + _productIdsTierPrices = new List(products.Where(x => x.HasTierPrices).Select(x => x.Id)); + _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; - _funcTierPrices = tierPrices; - _funcProductCategories = productCategories; - _funcProductManufacturers = productManufacturers; - _funcAppliedDiscounts = appliedDiscounts; - _funcProductBundleItems = productBundleItems; + _funcAttributes = attributes; + _funcAttributeCombinations = attributeCombinations; + _funcTierPrices = tierPrices; + _funcProductCategories = productCategories; + _funcProductManufacturers = productManufacturers; + _funcAppliedDiscounts = appliedDiscounts; + _funcProductBundleItems = productBundleItems; _funcAssociatedProducts = associatedProducts; - } + } - public IReadOnlyList ProductIds => _productIds; + public IReadOnlyList ProductIds => _productIds; - public void Clear() - { + public void Clear() + { _attributes?.Clear(); _attributeCombinations?.Clear(); _tierPrices?.Clear(); @@ -88,91 +88,91 @@ public void Clear() _bundledProductIds.Clear(); _groupedProductIds.Clear(); - } - - public LazyMultimap Attributes - { - get - { - if (_attributes == null) - { - _attributes = new LazyMultimap(keys => _funcAttributes(keys), _productIds); - } - return _attributes; - } - } - - public LazyMultimap AttributeCombinations - { - get - { - if (_attributeCombinations == null) - { - _attributeCombinations = new LazyMultimap(keys => _funcAttributeCombinations(keys), _productIds); - } - return _attributeCombinations; - } - } - - public LazyMultimap TierPrices - { - get - { - if (_tierPrices == null) - { - _tierPrices = new LazyMultimap(keys => _funcTierPrices(keys), _productIdsTierPrices); - } - return _tierPrices; - } - } - - public LazyMultimap ProductCategories - { - get - { - if (_productCategories == null) - { - _productCategories = new LazyMultimap(keys => _funcProductCategories(keys), _productIds); - } - return _productCategories; - } - } - - public LazyMultimap ProductManufacturers - { - get - { - if (_productManufacturers == null) - { - _productManufacturers = new LazyMultimap(keys => _funcProductManufacturers(keys), _productIds); - } - return _productManufacturers; - } - } - - public LazyMultimap AppliedDiscounts - { - get - { - if (_appliedDiscounts == null) - { - _appliedDiscounts = new LazyMultimap(keys => _funcAppliedDiscounts(keys), _productIdsAppliedDiscounts); - } - return _appliedDiscounts; - } - } - - public LazyMultimap ProductBundleItems - { - get - { - if (_productBundleItems == null) - { - _productBundleItems = new LazyMultimap(keys => _funcProductBundleItems(keys), _bundledProductIds); - } - return _productBundleItems; - } - } + } + + public LazyMultimap Attributes + { + get + { + if (_attributes == null) + { + _attributes = new LazyMultimap(keys => _funcAttributes(keys), _productIds); + } + return _attributes; + } + } + + public LazyMultimap AttributeCombinations + { + get + { + if (_attributeCombinations == null) + { + _attributeCombinations = new LazyMultimap(keys => _funcAttributeCombinations(keys), _productIds); + } + return _attributeCombinations; + } + } + + public LazyMultimap TierPrices + { + get + { + if (_tierPrices == null) + { + _tierPrices = new LazyMultimap(keys => _funcTierPrices(keys), _productIdsTierPrices); + } + return _tierPrices; + } + } + + public LazyMultimap ProductCategories + { + get + { + if (_productCategories == null) + { + _productCategories = new LazyMultimap(keys => _funcProductCategories(keys), _productIds); + } + return _productCategories; + } + } + + public LazyMultimap ProductManufacturers + { + get + { + if (_productManufacturers == null) + { + _productManufacturers = new LazyMultimap(keys => _funcProductManufacturers(keys), _productIds); + } + return _productManufacturers; + } + } + + public LazyMultimap AppliedDiscounts + { + get + { + if (_appliedDiscounts == null) + { + _appliedDiscounts = new LazyMultimap(keys => _funcAppliedDiscounts(keys), _productIdsAppliedDiscounts); + } + return _appliedDiscounts; + } + } + + public LazyMultimap ProductBundleItems + { + get + { + if (_productBundleItems == null) + { + _productBundleItems = new LazyMultimap(keys => _funcProductBundleItems(keys), _bundledProductIds); + } + return _productBundleItems; + } + } public LazyMultimap AssociatedProducts { @@ -188,14 +188,14 @@ public LazyMultimap AssociatedProducts } public void Collect(IEnumerable productIds) - { - Attributes.Collect(productIds); - AttributeCombinations.Collect(productIds); - TierPrices.Collect(productIds); - ProductCategories.Collect(productIds); - AppliedDiscounts.Collect(productIds); - ProductBundleItems.Collect(productIds); + { + Attributes.Collect(productIds); + AttributeCombinations.Collect(productIds); + TierPrices.Collect(productIds); + ProductCategories.Collect(productIds); + AppliedDiscounts.Collect(productIds); + ProductBundleItems.Collect(productIds); AssociatedProducts.Collect(productIds); - } - } + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs b/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs index 5fc429449c..e0454c592a 100644 --- a/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/PriceCalculationService.cs @@ -23,51 +23,51 @@ public partial class PriceCalculationService : IPriceCalculationService { private readonly IDiscountService _discountService; private readonly ICategoryService _categoryService; - private readonly IManufacturerService _manufacturerService; - private readonly IProductAttributeParser _productAttributeParser; - private readonly IProductService _productService; + private readonly IManufacturerService _manufacturerService; + private readonly IProductAttributeParser _productAttributeParser; + private readonly IProductService _productService; private readonly CatalogSettings _catalogSettings; - private readonly IProductAttributeService _productAttributeService; - private readonly IDownloadService _downloadService; - private readonly ICommonServices _services; - private readonly HttpRequestBase _httpRequestBase; - private readonly ITaxService _taxService; - private readonly TaxSettings _taxSettings; - - public PriceCalculationService( + private readonly IProductAttributeService _productAttributeService; + private readonly IDownloadService _downloadService; + private readonly ICommonServices _services; + private readonly HttpRequestBase _httpRequestBase; + private readonly ITaxService _taxService; + private readonly TaxSettings _taxSettings; + + public PriceCalculationService( IDiscountService discountService, - ICategoryService categoryService, - IManufacturerService manufacturerService, - IProductAttributeParser productAttributeParser, - IProductService productService, + ICategoryService categoryService, + IManufacturerService manufacturerService, + IProductAttributeParser productAttributeParser, + IProductService productService, CatalogSettings catalogSettings, - IProductAttributeService productAttributeService, - IDownloadService downloadService, - ICommonServices services, - HttpRequestBase httpRequestBase, - ITaxService taxService, - TaxSettings taxSettings) + IProductAttributeService productAttributeService, + IDownloadService downloadService, + ICommonServices services, + HttpRequestBase httpRequestBase, + ITaxService taxService, + TaxSettings taxSettings) { _discountService = discountService; _categoryService = categoryService; - _manufacturerService = manufacturerService; + _manufacturerService = manufacturerService; _productAttributeParser = productAttributeParser; - _productService = productService; + _productService = productService; _catalogSettings = catalogSettings; - _productAttributeService = productAttributeService; - _downloadService = downloadService; - _services = services; - _httpRequestBase = httpRequestBase; - _taxService = taxService; - _taxSettings = taxSettings; + _productAttributeService = productAttributeService; + _downloadService = downloadService; + _services = services; + _httpRequestBase = httpRequestBase; + _taxService = taxService; + _taxSettings = taxSettings; } - + #region Utilities /// /// Gets allowed discounts /// - /// Product + /// Product /// Customer /// Discounts protected virtual ICollection GetAllowedDiscounts(Product product, Customer customer, PriceCalculationContext context = null) @@ -76,47 +76,47 @@ protected virtual ICollection GetAllowedDiscounts(Product product, Cus if (_catalogSettings.IgnoreDiscounts) return result; - // check discounts assigned to the product - if (product.HasDiscountsApplied) + // check discounts assigned to the product + if (product.HasDiscountsApplied) { // We use this property ("HasDiscountsApplied") for performance optimziation to avoid unnecessary database calls - IEnumerable appliedDiscounts = null; - - if (context == null) - { - appliedDiscounts = product.AppliedDiscounts; - } - else - { - if (!context.AppliedDiscounts.FullyLoaded) - { - context.AppliedDiscounts.LoadAll(); - } - appliedDiscounts = context.AppliedDiscounts.GetOrLoad(product.Id); - } - - if (appliedDiscounts != null) - { - foreach (var discount in appliedDiscounts) - { - if (discount.DiscountType == DiscountType.AssignedToSkus && !result.Contains(discount) && _discountService.IsDiscountValid(discount, customer)) - { - result.Add(discount); - } - } - } + IEnumerable appliedDiscounts = null; + + if (context == null) + { + appliedDiscounts = product.AppliedDiscounts; + } + else + { + if (!context.AppliedDiscounts.FullyLoaded) + { + context.AppliedDiscounts.LoadAll(); + } + appliedDiscounts = context.AppliedDiscounts.GetOrLoad(product.Id); + } + + if (appliedDiscounts != null) + { + foreach (var discount in appliedDiscounts) + { + if (discount.DiscountType == DiscountType.AssignedToSkus && !result.Contains(discount) && _discountService.IsDiscountValid(discount, customer)) + { + result.Add(discount); + } + } + } } // check discounts assigned to categories var discountsAssignedToCategories = _discountService.GetAllDiscounts(DiscountType.AssignedToCategories); if (discountsAssignedToCategories != null && discountsAssignedToCategories.Any()) { - IEnumerable productCategories = null; - - if (context == null) - productCategories = _categoryService.GetProductCategoriesByProductId(product.Id); - else - productCategories = context.ProductCategories.GetOrLoad(product.Id); + IEnumerable productCategories = null; + + if (context == null) + productCategories = _categoryService.GetProductCategoriesByProductId(product.Id); + else + productCategories = context.ProductCategories.GetOrLoad(product.Id); if (productCategories != null) { @@ -130,10 +130,10 @@ protected virtual ICollection GetAllowedDiscounts(Product product, Cus foreach (var discount in categoryDiscounts) { - if (discount.DiscountType == DiscountType.AssignedToCategories && !result.Contains(discount) && _discountService.IsDiscountValid(discount, customer)) - { - result.Add(discount); - } + if (discount.DiscountType == DiscountType.AssignedToCategories && !result.Contains(discount) && _discountService.IsDiscountValid(discount, customer)) + { + result.Add(discount); + } } } } @@ -144,34 +144,34 @@ protected virtual ICollection GetAllowedDiscounts(Product product, Cus var discountsAssignedToManufacturers = _discountService.GetAllDiscounts(DiscountType.AssignedToManufacturers); if (discountsAssignedToManufacturers != null && discountsAssignedToManufacturers.Any()) - { - IEnumerable productManufacturers = null; - - if (context == null) - productManufacturers = _manufacturerService.GetProductManufacturersByProductId(product.Id); - else - productManufacturers = context.ProductManufacturers.GetOrLoad(product.Id); - - if (productManufacturers != null) - { - foreach (var productManufacturer in productManufacturers) - { - var manu = productManufacturer.Manufacturer; - if (manu.HasDiscountsApplied) - { - var manuDiscounts = manu.AppliedDiscounts; - - foreach (var discount in manuDiscounts) - { - if (discount.DiscountType == DiscountType.AssignedToManufacturers && !result.Contains(discount) && _discountService.IsDiscountValid(discount, customer)) - { - result.Add(discount); - } - } - } - } - } - } + { + IEnumerable productManufacturers = null; + + if (context == null) + productManufacturers = _manufacturerService.GetProductManufacturersByProductId(product.Id); + else + productManufacturers = context.ProductManufacturers.GetOrLoad(product.Id); + + if (productManufacturers != null) + { + foreach (var productManufacturer in productManufacturers) + { + var manu = productManufacturer.Manufacturer; + if (manu.HasDiscountsApplied) + { + var manuDiscounts = manu.AppliedDiscounts; + + foreach (var discount in manuDiscounts) + { + if (discount.DiscountType == DiscountType.AssignedToManufacturers && !result.Contains(discount) && _discountService.IsDiscountValid(discount, customer)) + { + result.Add(discount); + } + } + } + } + } + } return result; } @@ -185,28 +185,28 @@ protected virtual ICollection GetAllowedDiscounts(Product product, Cus /// Price protected virtual decimal? GetMinimumTierPrice(Product product, Customer customer, int quantity, PriceCalculationContext context = null) { - if (!product.HasTierPrices) + if (!product.HasTierPrices) return decimal.Zero; - IEnumerable tierPrices = null; - - if (context == null) - { - tierPrices = product.TierPrices - .OrderBy(tp => tp.Quantity) - .FilterByStore(_services.StoreContext.CurrentStore.Id) - .FilterForCustomer(customer) - .ToList() - .RemoveDuplicatedQuantities(); - } - else - { - tierPrices = context.TierPrices.GetOrLoad(product.Id) - .RemoveDuplicatedQuantities(); - } - - if (tierPrices == null) - return decimal.Zero; + IEnumerable tierPrices = null; + + if (context == null) + { + tierPrices = product.TierPrices + .OrderBy(tp => tp.Quantity) + .FilterByStore(_services.StoreContext.CurrentStore.Id) + .FilterForCustomer(customer) + .ToList() + .RemoveDuplicatedQuantities(); + } + else + { + tierPrices = context.TierPrices.GetOrLoad(product.Id) + .RemoveDuplicatedQuantities(); + } + + if (tierPrices == null) + return decimal.Zero; int previousQty = 1; decimal? previousPrice = null; @@ -232,10 +232,10 @@ protected virtual ICollection GetAllowedDiscounts(Product product, Cus { previousPrice = product.Price - tierPrice.Price; } - + previousQty = tierPrice.Quantity; } - + return previousPrice; } @@ -281,162 +281,163 @@ protected virtual decimal GetTierPriceAttributeAdjustment(Product product, Custo { previousPrice = adjustment - (adjustment / 100 * tierPrice.Price); } - + previousQty = tierPrice.Quantity; } - + return previousPrice; } protected virtual decimal GetPreselectedPrice( Product product, Customer customer, - Currency currency, + Currency currency, PriceCalculationContext context, ProductBundleItemData bundleItem, IEnumerable bundleItems) { var taxRate = decimal.Zero; - var attributesTotalPriceBase = decimal.Zero; - var preSelectedPriceAdjustmentBase = decimal.Zero; - var isBundle = product.ProductType == ProductType.BundledProduct; - var isBundleItemPricing = bundleItem != null && bundleItem.Item.BundleProduct.BundlePerItemPricing; - var isBundlePricing = bundleItem != null && !bundleItem.Item.BundleProduct.BundlePerItemPricing; - var bundleItemId = bundleItem == null ? 0 : bundleItem.Item.Id; - - var query = new ProductVariantQuery(); - var selectedAttributeValues = new List(); - var attributes = context.Attributes.GetOrLoad(product.Id); - - // 1. Fill query with initially selected attributes. - foreach (var attribute in attributes.Where(x => x.ProductVariantAttributeValues.Any() && x.ShouldHaveValues())) - { - int preSelectedValueId = 0; - ProductVariantAttributeValue defaultValue = null; - var selectedValueIds = new List(); - var pvaValues = attribute.ProductVariantAttributeValues; - - foreach (var pvaValue in pvaValues) - { + var attributesTotalPriceBase = decimal.Zero; + var preSelectedPriceAdjustmentBase = decimal.Zero; + var isBundle = product.ProductType == ProductType.BundledProduct; + var isBundleItemPricing = bundleItem != null && bundleItem.Item.BundleProduct.BundlePerItemPricing; + var isBundlePricing = bundleItem != null && !bundleItem.Item.BundleProduct.BundlePerItemPricing; + var bundleItemId = bundleItem == null ? 0 : bundleItem.Item.Id; + + var query = new ProductVariantQuery(); + var selectedAttributeValues = new List(); + var attributes = context.Attributes.GetOrLoad(product.Id); + + // 1. Fill query with initially selected attributes. + foreach (var attribute in attributes.Where(x => x.ProductVariantAttributeValues.Any() && x.ShouldHaveValues())) + { + int preSelectedValueId = 0; + ProductVariantAttributeValue defaultValue = null; + var selectedValueIds = new List(); + var pvaValues = attribute.ProductVariantAttributeValues; + + foreach (var pvaValue in pvaValues) + { ProductBundleItemAttributeFilter attributeFilter = null; if (bundleItem?.Item?.FilterOut(pvaValue, out attributeFilter) ?? false) - continue; - - if (preSelectedValueId == 0 && attributeFilter != null && attributeFilter.IsPreSelected) - preSelectedValueId = attributeFilter.AttributeValueId; - - if (!isBundlePricing && pvaValue.IsPreSelected) - { - var includingTax = _services.WorkContext.TaxDisplayType == TaxDisplayType.IncludingTax; - var attributeValuePriceAdjustment = GetProductVariantAttributeValuePriceAdjustment(pvaValue, product, customer, context, 1); - var priceAdjustmentBase = _taxService.GetProductPrice(product, product.TaxCategoryId, attributeValuePriceAdjustment, - includingTax, customer, currency, _taxSettings.PricesIncludeTax, out taxRate); - - preSelectedPriceAdjustmentBase = decimal.Add(preSelectedPriceAdjustmentBase, priceAdjustmentBase); - } - } - - // Value pre-selected by a bundle item filter discards the default pre-selection. - if (preSelectedValueId != 0 && (defaultValue = pvaValues.FirstOrDefault(x => x.Id == preSelectedValueId)) != null) - { - //defaultValue.IsPreSelected = true; - selectedAttributeValues.Add(defaultValue); - query.AddVariant(new ProductVariantQueryItem(defaultValue.Id.ToString()) - { - ProductId = product.Id, - BundleItemId = bundleItemId, - AttributeId = attribute.ProductAttributeId, - VariantAttributeId = attribute.Id, - Alias = attribute.ProductAttribute.Alias, - ValueAlias = defaultValue.Alias - }); - } - else - { - foreach (var value in pvaValues.Where(x => x.IsPreSelected)) - { - selectedAttributeValues.Add(value); - query.AddVariant(new ProductVariantQueryItem(value.Id.ToString()) - { - ProductId = product.Id, - BundleItemId = bundleItemId, - AttributeId = attribute.ProductAttributeId, - VariantAttributeId = attribute.Id, - Alias = attribute.ProductAttribute.Alias, - ValueAlias = value.Alias - }); - } - } - } - - // 2. Find attribute combination for selected attributes and merge it. - if (!isBundle && query.Variants.Any()) - { - var attributeXml = query.CreateSelectedAttributesXml(product.Id, bundleItemId, attributes, _productAttributeParser, _services.Localization, - _downloadService, _catalogSettings, _httpRequestBase, new List()); - - var combinations = context.AttributeCombinations.GetOrLoad(product.Id); - - var selectedCombination = combinations.FirstOrDefault(x => _productAttributeParser.AreProductAttributesEqual(x.AttributesXml, attributeXml)); - - if (selectedCombination != null && selectedCombination.IsActive && selectedCombination.Price.HasValue) - { - product.MergedDataValues = new Dictionary { { "Price", selectedCombination.Price.Value } }; - - if (selectedCombination.BasePriceAmount.HasValue) - product.MergedDataValues.Add("BasePriceAmount", selectedCombination.BasePriceAmount.Value); - - if (selectedCombination.BasePriceBaseAmount.HasValue) - product.MergedDataValues.Add("BasePriceBaseAmount", selectedCombination.BasePriceBaseAmount.Value); - } - } - - if (_catalogSettings.EnableDynamicPriceUpdate && !isBundlePricing) - { - if (selectedAttributeValues.Count > 0) - { - selectedAttributeValues.Each(x => attributesTotalPriceBase += GetProductVariantAttributeValuePriceAdjustment(x, product, customer, context, 1)); - } - else - { - attributesTotalPriceBase = preSelectedPriceAdjustmentBase; - } - } - - if (bundleItem != null) - { - bundleItem.AdditionalCharge = attributesTotalPriceBase; - } - - var result = GetFinalPrice(product, bundleItems, customer, attributesTotalPriceBase, true, 1, bundleItem, context); - return result; - } - - public virtual PriceCalculationContext CreatePriceCalculationContext( - IEnumerable products = null, - Customer customer = null, - int? storeId = null) - { - if (customer == null) - customer = _services.WorkContext.CurrentCustomer; - - if (!storeId.HasValue) - storeId = _services.StoreContext.CurrentStore.Id; - - var context = new PriceCalculationContext(products, - x => _productAttributeService.GetProductVariantAttributesByProductIds(x, null), - x => _productAttributeService.GetProductVariantAttributeCombinations(x), - x => _productService.GetTierPricesByProductIds(x, customer, storeId.Value), - x => _categoryService.GetProductCategoriesByProductIds(x, true), - x => _manufacturerService.GetProductManufacturersByProductIds(x), - x => _productService.GetAppliedDiscountsByProductIds(x), - x => _productService.GetBundleItemsByProductIds(x, true), - x => _productService.GetAssociatedProductsByProductIds(x) - ); - - return context; - } + continue; + + if (preSelectedValueId == 0 && attributeFilter != null && attributeFilter.IsPreSelected) + preSelectedValueId = attributeFilter.AttributeValueId; + + if (!isBundlePricing && pvaValue.IsPreSelected) + { + var includingTax = _services.WorkContext.TaxDisplayType == TaxDisplayType.IncludingTax; + var attributeValuePriceAdjustment = GetProductVariantAttributeValuePriceAdjustment(pvaValue, product, customer, context, 1); + var priceAdjustmentBase = _taxService.GetProductPrice(product, product.TaxCategoryId, attributeValuePriceAdjustment, + includingTax, customer, currency, _taxSettings.PricesIncludeTax, out taxRate); + + preSelectedPriceAdjustmentBase = decimal.Add(preSelectedPriceAdjustmentBase, priceAdjustmentBase); + } + } + + // Value pre-selected by a bundle item filter discards the default pre-selection. + if (preSelectedValueId != 0 && (defaultValue = pvaValues.FirstOrDefault(x => x.Id == preSelectedValueId)) != null) + { + //defaultValue.IsPreSelected = true; + selectedAttributeValues.Add(defaultValue); + query.AddVariant(new ProductVariantQueryItem(defaultValue.Id.ToString()) + { + ProductId = product.Id, + BundleItemId = bundleItemId, + AttributeId = attribute.ProductAttributeId, + VariantAttributeId = attribute.Id, + Alias = attribute.ProductAttribute.Alias, + ValueAlias = defaultValue.Alias + }); + } + else + { + foreach (var value in pvaValues.Where(x => x.IsPreSelected)) + { + selectedAttributeValues.Add(value); + query.AddVariant(new ProductVariantQueryItem(value.Id.ToString()) + { + ProductId = product.Id, + BundleItemId = bundleItemId, + AttributeId = attribute.ProductAttributeId, + VariantAttributeId = attribute.Id, + Alias = attribute.ProductAttribute.Alias, + ValueAlias = value.Alias + }); + } + } + } + + // 2. Find attribute combination for selected attributes and merge it. + if (!isBundle && query.Variants.Any()) + { + var attributeXml = query.CreateSelectedAttributesXml(product.Id, bundleItemId, attributes, _productAttributeParser, _services.Localization, + _downloadService, _catalogSettings, _httpRequestBase, new List()); + + var combinations = context.AttributeCombinations.GetOrLoad(product.Id); + + var selectedCombination = combinations.FirstOrDefault(x => _productAttributeParser.AreProductAttributesEqual(x.AttributesXml, attributeXml)); + + if (selectedCombination != null && selectedCombination.IsActive && selectedCombination.Price.HasValue) + { + product.MergedDataValues = new Dictionary { { "Price", selectedCombination.Price.Value } }; + + if (selectedCombination.BasePriceAmount.HasValue) + product.MergedDataValues.Add("BasePriceAmount", selectedCombination.BasePriceAmount.Value); + + if (selectedCombination.BasePriceBaseAmount.HasValue) + product.MergedDataValues.Add("BasePriceBaseAmount", selectedCombination.BasePriceBaseAmount.Value); + } + } + + if (_catalogSettings.EnableDynamicPriceUpdate && !isBundlePricing) + { + if (selectedAttributeValues.Count > 0) + { + selectedAttributeValues.Each(x => attributesTotalPriceBase += GetProductVariantAttributeValuePriceAdjustment(x, product, customer, context, 1)); + } + else + { + attributesTotalPriceBase = preSelectedPriceAdjustmentBase; + } + } + + if (bundleItem != null) + { + bundleItem.AdditionalCharge = attributesTotalPriceBase; + } + + var result = GetFinalPrice(product, bundleItems, customer, attributesTotalPriceBase, true, 1, bundleItem, context); + return result; + } + + public virtual PriceCalculationContext CreatePriceCalculationContext( + IEnumerable products = null, + Customer customer = null, + int? storeId = null, + bool includeHidden = true) + { + if (customer == null) + customer = _services.WorkContext.CurrentCustomer; + + if (!storeId.HasValue) + storeId = _services.StoreContext.CurrentStore.Id; + + var context = new PriceCalculationContext(products, + x => _productAttributeService.GetProductVariantAttributesByProductIds(x, null), + x => _productAttributeService.GetProductVariantAttributeCombinations(x), + x => _productService.GetTierPricesByProductIds(x, customer, storeId.Value), + x => _categoryService.GetProductCategoriesByProductIds(x, includeHidden), + x => _manufacturerService.GetProductManufacturersByProductIds(x, includeHidden), + x => _productService.GetAppliedDiscountsByProductIds(x), + x => _productService.GetBundleItemsByProductIds(x, includeHidden), + x => _productService.GetAssociatedProductsByProductIds(x, includeHidden) + ); + + return context; + } #endregion @@ -449,28 +450,28 @@ public virtual PriceCalculationContext CreatePriceCalculationContext( /// Product special price public virtual decimal? GetSpecialPrice(Product product) { - if (product == null) - throw new ArgumentNullException("product"); + if (product == null) + throw new ArgumentNullException("product"); if (!product.SpecialPrice.HasValue) return null; //check date range DateTime now = DateTime.UtcNow; - if (product.SpecialPriceStartDateTimeUtc.HasValue) + if (product.SpecialPriceStartDateTimeUtc.HasValue) { - DateTime startDate = DateTime.SpecifyKind(product.SpecialPriceStartDateTimeUtc.Value, DateTimeKind.Utc); + DateTime startDate = DateTime.SpecifyKind(product.SpecialPriceStartDateTimeUtc.Value, DateTimeKind.Utc); if (startDate.CompareTo(now) > 0) return null; } - if (product.SpecialPriceEndDateTimeUtc.HasValue) + if (product.SpecialPriceEndDateTimeUtc.HasValue) { - DateTime endDate = DateTime.SpecifyKind(product.SpecialPriceEndDateTimeUtc.Value, DateTimeKind.Utc); + DateTime endDate = DateTime.SpecifyKind(product.SpecialPriceEndDateTimeUtc.Value, DateTimeKind.Utc); if (endDate.CompareTo(now) < 0) return null; } - return product.SpecialPrice.Value; + return product.SpecialPrice.Value; } /// @@ -479,10 +480,10 @@ public virtual PriceCalculationContext CreatePriceCalculationContext( /// Product /// A value indicating whether include discounts or not for final price computation /// Final price - public virtual decimal GetFinalPrice(Product product, bool includeDiscounts) + public virtual decimal GetFinalPrice(Product product, bool includeDiscounts) { var customer = _services.WorkContext.CurrentCustomer; - return GetFinalPrice(product, customer, includeDiscounts); + return GetFinalPrice(product, customer, includeDiscounts); } /// @@ -494,7 +495,7 @@ public virtual decimal GetFinalPrice(Product product, bool includeDiscounts) /// Final price public virtual decimal GetFinalPrice(Product product, Customer customer, bool includeDiscounts) { - return GetFinalPrice(product, customer, decimal.Zero, includeDiscounts); + return GetFinalPrice(product, customer, decimal.Zero, includeDiscounts); } /// @@ -505,29 +506,29 @@ public virtual decimal GetFinalPrice(Product product, Customer customer, bool in /// Additional charge /// A value indicating whether include discounts or not for final price computation /// Final price - public virtual decimal GetFinalPrice(Product product, - Customer customer, - decimal additionalCharge, + public virtual decimal GetFinalPrice(Product product, + Customer customer, + decimal additionalCharge, bool includeDiscounts) { return GetFinalPrice(product, customer, additionalCharge, includeDiscounts, 1); } - public virtual decimal GetFinalPrice( - Product product, + public virtual decimal GetFinalPrice( + Product product, Customer customer, - decimal additionalCharge, - bool includeDiscounts, + decimal additionalCharge, + bool includeDiscounts, int quantity, - ProductBundleItemData bundleItem = null, - PriceCalculationContext context = null, - bool isTierPrice = false) - { + ProductBundleItemData bundleItem = null, + PriceCalculationContext context = null, + bool isTierPrice = false) + { // Initial price. - decimal result = product.Price; + var result = product.Price; // Special price. - var specialPrice = GetSpecialPrice(product); + var specialPrice = GetSpecialPrice(product); if (specialPrice.HasValue) { result = specialPrice.Value; @@ -538,25 +539,39 @@ public virtual decimal GetFinalPrice( includeDiscounts = true; } - // Tier prices. + // Tier price. if (product.HasTierPrices && includeDiscounts && !(bundleItem != null && bundleItem.Item != null)) { - var tierPrice = GetMinimumTierPrice(product, customer, quantity, context); - var discountAmountTest = GetDiscountAmount(product, customer, additionalCharge, quantity, out var appliedDiscountTest, bundleItem); - var discountProductTest = result - discountAmountTest; - - //decimal? tierPrice = GetMinimumTierPrice(product, customer, quantity); - if (tierPrice.HasValue && tierPrice < discountProductTest) + var tierPrice = GetMinimumTierPrice(product, customer, quantity, context); + if (tierPrice.HasValue) { - includeDiscounts = false; - result = Math.Min(result, tierPrice.Value); + if (_catalogSettings.ApplyPercentageDiscountOnTierPrice && !isTierPrice) + { + var discountOnTierPrice = GetDiscountAmount(product, customer, decimal.Zero, quantity, out var appliedDiscount, bundleItem, context, tierPrice); + + if (appliedDiscount != null && appliedDiscount.UsePercentage) + { + result = Math.Min(result, tierPrice.Value) + additionalCharge - discountOnTierPrice; + + return Math.Max(result, decimal.Zero); + } + } + + var discountAmountTest = GetDiscountAmount(product, customer, additionalCharge, quantity, out var appliedDiscountTest, bundleItem); + var discountProductTest = result - discountAmountTest; + + if (tierPrice < discountProductTest) + { + includeDiscounts = false; + result = Math.Min(result, tierPrice.Value); + } } } // Discount + additional charge. if (includeDiscounts) { - var discountAmount = GetDiscountAmount(product, customer, additionalCharge, quantity, out var appliedDiscount, bundleItem, context); + var discountAmount = GetDiscountAmount(product, customer, additionalCharge, quantity, out var appliedDiscount, bundleItem, context); result = result + additionalCharge - discountAmount; } else @@ -564,55 +579,50 @@ public virtual decimal GetFinalPrice( result = result + additionalCharge; } - if (result < decimal.Zero) + return Math.Max(result, decimal.Zero); + } + + public virtual decimal GetFinalPrice( + Product product, + IEnumerable bundleItems, + Customer customer, + decimal additionalCharge, + bool includeDiscounts, + int quantity, + ProductBundleItemData bundleItem = null, + PriceCalculationContext context = null) + { + if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing) { - result = decimal.Zero; + var result = decimal.Zero; + var items = bundleItems; + + if (items == null) + { + items = context == null + ? _productService.GetBundleItems(product.Id) + : context.ProductBundleItems.GetOrLoad(product.Id).Select(x => new ProductBundleItemData(x)).ToList(); + } + + foreach (var itemData in items.Where(x => x != null && x.Item != null)) + { + var itemPrice = GetFinalPrice(itemData.Item.Product, customer, itemData.AdditionalCharge, includeDiscounts, 1, itemData, context); + + result += decimal.Multiply(itemPrice, itemData.Item.Quantity); + } + + return result < decimal.Zero ? decimal.Zero : result; } - return result; + return GetFinalPrice(product, customer, additionalCharge, includeDiscounts, quantity, bundleItem, context); } - public virtual decimal GetFinalPrice( - Product product, - IEnumerable bundleItems, - Customer customer, - decimal additionalCharge, - bool includeDiscounts, - int quantity, - ProductBundleItemData bundleItem = null, - PriceCalculationContext context = null) - { - if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing) - { - var result = decimal.Zero; - var items = bundleItems; - - if (items == null) - { - items = context == null - ? _productService.GetBundleItems(product.Id) - : context.ProductBundleItems.GetOrLoad(product.Id).Select(x => new ProductBundleItemData(x)).ToList(); - } - - foreach (var itemData in items.Where(x => x != null && x.Item != null)) - { - var itemPrice = GetFinalPrice(itemData.Item.Product, customer, itemData.AdditionalCharge, includeDiscounts, 1, itemData, context); - - result += decimal.Multiply(itemPrice, itemData.Item.Quantity); - } - - return result < decimal.Zero ? decimal.Zero : result; - } - - return GetFinalPrice(product, customer, additionalCharge, includeDiscounts, quantity, bundleItem, context); - } - - public virtual decimal GetLowestPrice(Product product, Customer customer, PriceCalculationContext context, out bool displayFromMessage) - { + public virtual decimal GetLowestPrice(Product product, Customer customer, PriceCalculationContext context, out bool displayFromMessage) + { Guard.NotNull(product, nameof(product)); - if (product.ProductType == ProductType.GroupedProduct) - throw Error.InvalidOperation("Choose the other override for products of type grouped product."); + if (product.ProductType == ProductType.GroupedProduct) + throw Error.InvalidOperation("Choose the other override for products of type grouped product."); // Note, attribute price adjustments will not be taken into account here. if (context == null) @@ -620,92 +630,95 @@ public virtual decimal GetLowestPrice(Product product, Customer customer, PriceC context = CreatePriceCalculationContext(customer: customer); } - var isBundlePerItemPricing = product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing; + var isBundlePerItemPricing = product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing; var lowestPrice = GetFinalPrice(product, null, customer, decimal.Zero, true, int.MaxValue, null, context); displayFromMessage = isBundlePerItemPricing; - if (product.LowestAttributeCombinationPrice.HasValue && product.LowestAttributeCombinationPrice.Value < lowestPrice) - { - lowestPrice = product.LowestAttributeCombinationPrice.Value; - displayFromMessage = true; - } - - if (lowestPrice == decimal.Zero && product.Price == decimal.Zero) - { - lowestPrice = product.LowestAttributeCombinationPrice ?? decimal.Zero; - } - - if (!displayFromMessage && product.ProductType != ProductType.BundledProduct) - { - var attributes = context.Attributes.GetOrLoad(product.Id); - displayFromMessage = attributes.Any(x => x.ProductVariantAttributeValues.Any(y => y.PriceAdjustment != decimal.Zero)); - } - - if (!displayFromMessage && product.HasTierPrices && !isBundlePerItemPricing) - { - var tierPrices = context.TierPrices.GetOrLoad(product.Id) - .RemoveDuplicatedQuantities(); - - displayFromMessage = (tierPrices.Count > 0 && !(tierPrices.Count == 1 && tierPrices.First().Quantity <= 1)); - } - - return lowestPrice; - } - - public virtual decimal? GetLowestPrice( - Product product, - Customer customer, - PriceCalculationContext context, - IEnumerable associatedProducts, - out Product lowestPriceProduct) - { - if (product == null) - throw new ArgumentNullException("product"); - - if (associatedProducts == null) - throw new ArgumentNullException("associatedProducts"); - - if (product.ProductType != ProductType.GroupedProduct) - throw Error.InvalidOperation("Choose the other override for products not of type grouped product."); - - lowestPriceProduct = null; - decimal? lowestPrice = null; + if (product.LowestAttributeCombinationPrice.HasValue) + { + if (product.LowestAttributeCombinationPrice.Value < lowestPrice) + { + lowestPrice = product.LowestAttributeCombinationPrice.Value; + } + displayFromMessage = true; + } + + if (lowestPrice == decimal.Zero && product.Price == decimal.Zero) + { + lowestPrice = product.LowestAttributeCombinationPrice ?? decimal.Zero; + } + + if (!displayFromMessage && product.ProductType != ProductType.BundledProduct) + { + var attributes = context.Attributes.GetOrLoad(product.Id); + displayFromMessage = attributes.Any(x => x.ProductVariantAttributeValues.Any(y => y.PriceAdjustment != decimal.Zero)); + } + + if (!displayFromMessage && product.HasTierPrices && !isBundlePerItemPricing) + { + var tierPrices = context.TierPrices.GetOrLoad(product.Id) + .RemoveDuplicatedQuantities(); + + displayFromMessage = tierPrices.Count > 0 && !(tierPrices.Count == 1 && tierPrices.First().Quantity <= 1); + } + + return lowestPrice; + } + + public virtual decimal? GetLowestPrice( + Product product, + Customer customer, + PriceCalculationContext context, + IEnumerable associatedProducts, + out Product lowestPriceProduct) + { + if (product == null) + throw new ArgumentNullException("product"); + + if (associatedProducts == null) + throw new ArgumentNullException("associatedProducts"); + + if (product.ProductType != ProductType.GroupedProduct) + throw Error.InvalidOperation("Choose the other override for products not of type grouped product."); + + lowestPriceProduct = null; + decimal? lowestPrice = null; if (context == null) { context = CreatePriceCalculationContext(customer: customer); } - foreach (var associatedProduct in associatedProducts) - { - var tmpPrice = GetFinalPrice(associatedProduct, customer, decimal.Zero, true, int.MaxValue, null, context); + foreach (var associatedProduct in associatedProducts) + { + var tmpPrice = GetFinalPrice(associatedProduct, customer, decimal.Zero, true, int.MaxValue, null, context); - if (associatedProduct.LowestAttributeCombinationPrice.HasValue && associatedProduct.LowestAttributeCombinationPrice.Value < tmpPrice) - { - tmpPrice = associatedProduct.LowestAttributeCombinationPrice.Value; - } + if (associatedProduct.LowestAttributeCombinationPrice.HasValue && associatedProduct.LowestAttributeCombinationPrice.Value < tmpPrice) + { + tmpPrice = associatedProduct.LowestAttributeCombinationPrice.Value; + } - if (!lowestPrice.HasValue || tmpPrice < lowestPrice.Value) - { - lowestPrice = tmpPrice; - lowestPriceProduct = associatedProduct; - } - } + if (!lowestPrice.HasValue || tmpPrice < lowestPrice.Value) + { + lowestPrice = tmpPrice; + lowestPriceProduct = associatedProduct; + } + } if (lowestPriceProduct == null) { lowestPriceProduct = associatedProducts.FirstOrDefault(); } - return lowestPrice; - } + return lowestPrice; + } - public virtual decimal GetPreselectedPrice(Product product, Customer customer, Currency currency, PriceCalculationContext context) - { + public virtual decimal GetPreselectedPrice(Product product, Customer customer, Currency currency, PriceCalculationContext context) + { Guard.NotNull(product, nameof(product)); - var result = decimal.Zero; + var result = decimal.Zero; if (context == null) { @@ -736,37 +749,37 @@ public virtual decimal GetPreselectedPrice(Product product, Customer customer, C result = GetPreselectedPrice(product, customer, currency, context, null, null); } - return result; - } + return result; + } - /// - /// Gets the product cost - /// - /// Product - /// Shopping cart item attributes in XML - /// Product cost - public virtual decimal GetProductCost(Product product, string attributesXml) - { - if (product == null) - throw new ArgumentNullException("product"); - - decimal result = product.ProductCost; - - _productAttributeParser - .ParseProductVariantAttributeValues(attributesXml) - .Where(x => x.ValueType == ProductVariantAttributeValueType.ProductLinkage) - .ToList() - .Each(x => - { - var linkedProduct = _productService.GetProductById(x.LinkedProductId); - - if (linkedProduct != null) - result += (linkedProduct.ProductCost * x.Quantity); - }); - - return result; - } + /// + /// Gets the product cost + /// + /// Product + /// Shopping cart item attributes in XML + /// Product cost + public virtual decimal GetProductCost(Product product, string attributesXml) + { + if (product == null) + throw new ArgumentNullException("product"); + + decimal result = product.ProductCost; + + _productAttributeParser + .ParseProductVariantAttributeValues(attributesXml) + .Where(x => x.ValueType == ProductVariantAttributeValueType.ProductLinkage) + .ToList() + .Each(x => + { + var linkedProduct = _productService.GetProductById(x.LinkedProductId); + + if (linkedProduct != null) + result += (linkedProduct.ProductCost * x.Quantity); + }); + + return result; + } /// /// Gets discount amount @@ -785,7 +798,7 @@ public virtual decimal GetDiscountAmount(Product product) /// Product /// The customer /// Discount amount - public virtual decimal GetDiscountAmount(Product product, + public virtual decimal GetDiscountAmount(Product product, Customer customer) { return GetDiscountAmount(product, customer, decimal.Zero); @@ -798,8 +811,8 @@ public virtual decimal GetDiscountAmount(Product product, /// The customer /// Additional charge /// Discount amount - public virtual decimal GetDiscountAmount(Product product, - Customer customer, + public virtual decimal GetDiscountAmount(Product product, + Customer customer, decimal additionalCharge) { return GetDiscountAmount(product, customer, additionalCharge, out var appliedDiscount); @@ -813,64 +826,64 @@ public virtual decimal GetDiscountAmount(Product product, /// Additional charge /// Applied discount /// Discount amount - public virtual decimal GetDiscountAmount(Product product, + public virtual decimal GetDiscountAmount(Product product, Customer customer, - decimal additionalCharge, + decimal additionalCharge, out Discount appliedDiscount) { return GetDiscountAmount(product, customer, additionalCharge, 1, out appliedDiscount); } public virtual decimal GetDiscountAmount( - Product product, + Product product, Customer customer, decimal additionalCharge, int quantity, out Discount appliedDiscount, - ProductBundleItemData bundleItem = null, - PriceCalculationContext context = null) + ProductBundleItemData bundleItem = null, + PriceCalculationContext context = null, + decimal? finalPrice = null) { appliedDiscount = null; - decimal appliedDiscountAmount = decimal.Zero; - decimal finalPriceWithoutDiscount = decimal.Zero; - - if (bundleItem != null && bundleItem.Item != null) - { - if (bundleItem.Item.Discount.HasValue && bundleItem.Item.BundleProduct.BundlePerItemPricing) - { - appliedDiscount = new Discount - { - UsePercentage = bundleItem.Item.DiscountPercentage, - DiscountPercentage = bundleItem.Item.Discount.Value, - DiscountAmount = bundleItem.Item.Discount.Value - }; - - finalPriceWithoutDiscount = GetFinalPrice(product, customer, additionalCharge, false, quantity, bundleItem, context); - appliedDiscountAmount = appliedDiscount.GetDiscountAmount(finalPriceWithoutDiscount); - } - } - else - { - // dont't apply when customer entered price or discounts should be ignored completely - if (product.CustomerEntersPrice || _catalogSettings.IgnoreDiscounts) - { - return appliedDiscountAmount; - } - - var allowedDiscounts = GetAllowedDiscounts(product, customer, context); - if (allowedDiscounts.Count == 0) - { - return appliedDiscountAmount; - } - - finalPriceWithoutDiscount = GetFinalPrice(product, customer, additionalCharge, false, quantity, bundleItem, context); - appliedDiscount = allowedDiscounts.GetPreferredDiscount(finalPriceWithoutDiscount); - - if (appliedDiscount != null) - { - appliedDiscountAmount = appliedDiscount.GetDiscountAmount(finalPriceWithoutDiscount); - } - } + var appliedDiscountAmount = decimal.Zero; + + if (bundleItem != null && bundleItem.Item != null) + { + if (bundleItem.Item.Discount.HasValue && bundleItem.Item.BundleProduct.BundlePerItemPricing) + { + appliedDiscount = new Discount + { + UsePercentage = bundleItem.Item.DiscountPercentage, + DiscountPercentage = bundleItem.Item.Discount.Value, + DiscountAmount = bundleItem.Item.Discount.Value + }; + + var finalPriceWithoutDiscount = finalPrice ?? GetFinalPrice(product, customer, additionalCharge, false, quantity, bundleItem, context); + appliedDiscountAmount = appliedDiscount.GetDiscountAmount(finalPriceWithoutDiscount); + } + } + else + { + // Don't apply when customer entered price or discounts should be ignored in any case. + if (product.CustomerEntersPrice || _catalogSettings.IgnoreDiscounts) + { + return appliedDiscountAmount; + } + + var allowedDiscounts = GetAllowedDiscounts(product, customer, context); + if (allowedDiscounts.Count == 0) + { + return appliedDiscountAmount; + } + + var finalPriceWithoutDiscount = finalPrice ?? GetFinalPrice(product, customer, additionalCharge, false, quantity, bundleItem, context); + appliedDiscount = allowedDiscounts.GetPreferredDiscount(finalPriceWithoutDiscount); + + if (appliedDiscount != null) + { + appliedDiscountAmount = appliedDiscount.GetDiscountAmount(finalPriceWithoutDiscount); + } + } return appliedDiscountAmount; } @@ -895,57 +908,57 @@ public virtual decimal GetSubTotal(OrganizedShoppingCartItem shoppingCartItem, b /// Shopping cart unit price (one item) public virtual decimal GetUnitPrice(OrganizedShoppingCartItem shoppingCartItem, bool includeDiscounts) { - decimal finalPrice = decimal.Zero; + decimal finalPrice = decimal.Zero; var customer = shoppingCartItem.Item.Customer; - var product = shoppingCartItem.Item.Product; + var product = shoppingCartItem.Item.Product; if (product != null) { - if (product.CustomerEntersPrice) + if (product.CustomerEntersPrice) { finalPrice = shoppingCartItem.Item.CustomerEnteredPrice; } - else if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing) - { - if (shoppingCartItem.ChildItems != null) - { - foreach (var bundleItem in shoppingCartItem.ChildItems) - { - bundleItem.Item.Product.MergeWithCombination(bundleItem.Item.AttributesXml, _productAttributeParser); - } - - var bundleItems = shoppingCartItem.ChildItems + else if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing) + { + if (shoppingCartItem.ChildItems != null) + { + foreach (var bundleItem in shoppingCartItem.ChildItems) + { + bundleItem.Item.Product.MergeWithCombination(bundleItem.Item.AttributesXml, _productAttributeParser); + } + + var bundleItems = shoppingCartItem.ChildItems .Where(x => x.BundleItemData != null && x.BundleItemData.Item != null) .Select(x => x.BundleItemData).ToList(); - finalPrice = GetFinalPrice(product, bundleItems, customer, decimal.Zero, includeDiscounts, shoppingCartItem.Item.Quantity); - } - } + finalPrice = GetFinalPrice(product, bundleItems, customer, decimal.Zero, includeDiscounts, shoppingCartItem.Item.Quantity); + } + } else { - product.MergeWithCombination(shoppingCartItem.Item.AttributesXml, _productAttributeParser); + product.MergeWithCombination(shoppingCartItem.Item.AttributesXml, _productAttributeParser); - var attributesTotalPrice = decimal.Zero; - var pvaValuesEnum = _productAttributeParser.ParseProductVariantAttributeValues(shoppingCartItem.Item.AttributesXml); + var attributesTotalPrice = decimal.Zero; + var pvaValuesEnum = _productAttributeParser.ParseProductVariantAttributeValues(shoppingCartItem.Item.AttributesXml); - if (pvaValuesEnum != null) - { - var pvaValues = pvaValuesEnum.ToList(); + if (pvaValuesEnum != null) + { + var pvaValues = pvaValuesEnum.ToList(); - foreach (var pvaValue in pvaValues) - { - attributesTotalPrice += GetProductVariantAttributeValuePriceAdjustment(pvaValue, product, customer, null, shoppingCartItem.Item.Quantity); - } - } + foreach (var pvaValue in pvaValues) + { + attributesTotalPrice += GetProductVariantAttributeValuePriceAdjustment(pvaValue, product, customer, null, shoppingCartItem.Item.Quantity); + } + } - finalPrice = GetFinalPrice(product, customer, attributesTotalPrice, includeDiscounts, shoppingCartItem.Item.Quantity, shoppingCartItem.BundleItemData); + finalPrice = GetFinalPrice(product, customer, attributesTotalPrice, includeDiscounts, shoppingCartItem.Item.Quantity, shoppingCartItem.BundleItemData); } } finalPrice = finalPrice.RoundIfEnabledFor(_services.WorkContext.WorkingCurrency); return finalPrice; } - + /// @@ -953,7 +966,7 @@ public virtual decimal GetUnitPrice(OrganizedShoppingCartItem shoppingCartItem, /// /// The shopping cart item /// Discount amount - public virtual decimal GetDiscountAmount(OrganizedShoppingCartItem shoppingCartItem) + public virtual decimal GetDiscountAmount(OrganizedShoppingCartItem shoppingCartItem) { return GetDiscountAmount(shoppingCartItem, out var appliedDiscount); } @@ -966,14 +979,14 @@ public virtual decimal GetDiscountAmount(OrganizedShoppingCartItem shoppingCartI /// Discount amount public virtual decimal GetDiscountAmount(OrganizedShoppingCartItem shoppingCartItem, out Discount appliedDiscount) { - appliedDiscount = null; + appliedDiscount = null; - var customer = shoppingCartItem.Item.Customer; - var totalDiscountAmount = decimal.Zero; - var product = shoppingCartItem.Item.Product; - var quantity = shoppingCartItem.Item.Quantity; + var customer = shoppingCartItem.Item.Customer; + var totalDiscountAmount = decimal.Zero; + var product = shoppingCartItem.Item.Product; + var quantity = shoppingCartItem.Item.Quantity; - if (product != null) + if (product != null) { var attributesTotalPrice = decimal.Zero; var pvaValues = _productAttributeParser.ParseProductVariantAttributeValues(shoppingCartItem.Item.AttributesXml).ToList(); @@ -983,26 +996,26 @@ public virtual decimal GetDiscountAmount(OrganizedShoppingCartItem shoppingCartI attributesTotalPrice += GetProductVariantAttributeValuePriceAdjustment(pvaValue, product, customer, null, quantity); } - var productDiscountAmount = GetDiscountAmount(product, customer, attributesTotalPrice, quantity, out appliedDiscount); - totalDiscountAmount = productDiscountAmount * quantity; + var productDiscountAmount = GetDiscountAmount(product, customer, attributesTotalPrice, quantity, out appliedDiscount); + totalDiscountAmount = productDiscountAmount * quantity; } totalDiscountAmount = totalDiscountAmount.RoundIfEnabledFor(_services.WorkContext.WorkingCurrency); - return totalDiscountAmount; + return totalDiscountAmount; } - /// - /// Gets the price adjustment of a variant attribute value - /// - /// Product variant attribute value - /// Price adjustment of a variant attribute value - public virtual decimal GetProductVariantAttributeValuePriceAdjustment(ProductVariantAttributeValue attributeValue, + /// + /// Gets the price adjustment of a variant attribute value + /// + /// Product variant attribute value + /// Price adjustment of a variant attribute value + public virtual decimal GetProductVariantAttributeValuePriceAdjustment(ProductVariantAttributeValue attributeValue, Product product, Customer customer, PriceCalculationContext context, int productQuantity = 1) - { + { Guard.NotNull(attributeValue, nameof(attributeValue)); - if (attributeValue.ValueType == ProductVariantAttributeValueType.Simple) + if (attributeValue.ValueType == ProductVariantAttributeValueType.Simple) { if (productQuantity > 1 && attributeValue.PriceAdjustment > 0) { @@ -1012,23 +1025,23 @@ public virtual decimal GetProductVariantAttributeValuePriceAdjustment(ProductVar return tierPriceAttributeAdjustment; } } - + return attributeValue.PriceAdjustment; } - + if (attributeValue.ValueType == ProductVariantAttributeValueType.ProductLinkage) - { - var linkedProduct = _productService.GetProductById(attributeValue.LinkedProductId); - - if (linkedProduct != null) - { - var productPrice = GetFinalPrice(linkedProduct, true) * attributeValue.Quantity; - return productPrice; - } - } - - return decimal.Zero; - } + { + var linkedProduct = _productService.GetProductById(attributeValue.LinkedProductId); + + if (linkedProduct != null) + { + var productPrice = GetFinalPrice(linkedProduct, false) * attributeValue.Quantity; + return productPrice; + } + } + + return decimal.Zero; + } #endregion } diff --git a/src/Libraries/SmartStore.Services/Catalog/PriceFormatter.cs b/src/Libraries/SmartStore.Services/Catalog/PriceFormatter.cs index 5ea1ec97ed..2a15907376 100644 --- a/src/Libraries/SmartStore.Services/Catalog/PriceFormatter.cs +++ b/src/Libraries/SmartStore.Services/Catalog/PriceFormatter.cs @@ -56,7 +56,7 @@ public string FormatPrice(decimal price, bool showCurrency, bool showTax) public string FormatPrice(decimal price, bool showCurrency, string currencyCode, bool showTax, Language language) { - var currency = _currencyService.GetCurrencyByCode(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; + var currency = _currencyService.GetCurrencyByCode(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; var priceIncludesTax = _workContext.TaxDisplayType == TaxDisplayType.IncludingTax; return FormatPrice(price, showCurrency, currency, language, priceIncludesTax, showTax); @@ -64,15 +64,15 @@ public string FormatPrice(decimal price, bool showCurrency, string currencyCode, public string FormatPrice(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax) { - bool showTax = _taxSettings.DisplayTaxSuffix; - return FormatPrice(price, showCurrency, currencyCode, language, priceIncludesTax, showTax); + bool showTax = _taxSettings.DisplayTaxSuffix; + return FormatPrice(price, showCurrency, currencyCode, language, priceIncludesTax, showTax); } - public string FormatPrice(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax, bool showTax) - { - var currency = _currencyService.GetCurrencyByCode(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; - return FormatPrice(price, showCurrency, currency, language, priceIncludesTax, showTax); - } + public string FormatPrice(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax, bool showTax) + { + var currency = _currencyService.GetCurrencyByCode(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; + return FormatPrice(price, showCurrency, currency, language, priceIncludesTax, showTax); + } public string FormatPrice(decimal price, bool showCurrency, Currency targetCurrency, Language language, bool priceIncludesTax) { @@ -80,29 +80,29 @@ public string FormatPrice(decimal price, bool showCurrency, Currency targetCurre return FormatPrice(price, showCurrency, targetCurrency, language, priceIncludesTax, showTax); } - public string FormatPrice(decimal price, bool showCurrency, Currency targetCurrency, Language language, bool priceIncludesTax, bool showTax) - { - var formatted = new Money(price, targetCurrency).ToString(showCurrency); + public string FormatPrice(decimal price, bool showCurrency, Currency targetCurrency, Language language, bool priceIncludesTax, bool showTax) + { + var formatted = new Money(price, targetCurrency).ToString(showCurrency); - if (showTax) - { - // Show tax suffix - var resKey = "Products." + (priceIncludesTax ? "InclTaxSuffix" : "ExclTaxSuffix"); - var taxFormatStr = _localizationService.GetResource(resKey, language.Id, false).NullEmpty() ?? (priceIncludesTax ? "{0} incl. tax" : "{0} excl. tax"); + if (showTax) + { + // Show tax suffix + var resKey = "Products." + (priceIncludesTax ? "InclTaxSuffix" : "ExclTaxSuffix"); + var taxFormatStr = _localizationService.GetResource(resKey, language.Id, false).NullEmpty() ?? (priceIncludesTax ? "{0} incl. tax" : "{0} excl. tax"); - formatted = string.Format(taxFormatStr, formatted); - } + formatted = string.Format(taxFormatStr, formatted); + } - return formatted; - } + return formatted; + } - public string FormatShippingPrice(decimal price, bool showCurrency) + public string FormatShippingPrice(decimal price, bool showCurrency) { var targetCurrency = _workContext.WorkingCurrency; var language = _workContext.WorkingLanguage; - var priceIncludesTax = _workContext.TaxDisplayType == TaxDisplayType.IncludingTax; + var priceIncludesTax = _workContext.TaxDisplayType == TaxDisplayType.IncludingTax; return FormatShippingPrice(price, showCurrency, targetCurrency, language, priceIncludesTax); } @@ -113,20 +113,20 @@ public string FormatShippingPrice(decimal price, bool showCurrency, Currency tar return FormatShippingPrice(price, showCurrency, targetCurrency, language, priceIncludesTax, showTax); } - public string FormatShippingPrice(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax, bool showTax) - { - var currency = _currencyService.GetCurrencyByCode(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; - return FormatPrice(price, showCurrency, currency, language, priceIncludesTax, showTax); - } + public string FormatShippingPrice(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax, bool showTax) + { + var currency = _currencyService.GetCurrencyByCode(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; + return FormatPrice(price, showCurrency, currency, language, priceIncludesTax, showTax); + } public string FormatShippingPrice(decimal price, bool showCurrency, Currency targetCurrency, Language language, bool priceIncludesTax, bool showTax) { return FormatPrice(price, showCurrency, targetCurrency, language, priceIncludesTax, showTax); } - + public string FormatShippingPrice(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax) { - var currency = _currencyService.GetCurrencyByCode(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; + var currency = _currencyService.GetCurrencyByCode(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; return FormatShippingPrice(price, showCurrency, currency, language, priceIncludesTax); } @@ -136,7 +136,7 @@ public string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency) { var targetCurrency = _workContext.WorkingCurrency; var language = _workContext.WorkingLanguage; - var priceIncludesTax = _workContext.TaxDisplayType == TaxDisplayType.IncludingTax; + var priceIncludesTax = _workContext.TaxDisplayType == TaxDisplayType.IncludingTax; return FormatPaymentMethodAdditionalFee(price, showCurrency, targetCurrency, language, priceIncludesTax); } @@ -147,11 +147,11 @@ public string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency, return FormatPaymentMethodAdditionalFee(price, showCurrency, targetCurrency, language, priceIncludesTax, showTax); } - public string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax, bool showTax) - { - var currency = _currencyService.GetCurrencyByCode(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; - return FormatPrice(price, showCurrency, currency, language, priceIncludesTax, showTax); - } + public string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax, bool showTax) + { + var currency = _currencyService.GetCurrencyByCode(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; + return FormatPrice(price, showCurrency, currency, language, priceIncludesTax, showTax); + } public string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency, Currency targetCurrency, Language language, bool priceIncludesTax, bool showTax) { @@ -160,7 +160,7 @@ public string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency, public string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency, string currencyCode, Language language, bool priceIncludesTax) { - var currency = _currencyService.GetCurrencyByCode(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; + var currency = _currencyService.GetCurrencyByCode(currencyCode) ?? new Currency { CurrencyCode = currencyCode }; return FormatPaymentMethodAdditionalFee(price, showCurrency, currency, language, priceIncludesTax); } diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductAttributeExtensions.cs b/src/Libraries/SmartStore.Services/Catalog/ProductAttributeExtensions.cs index 7ce8def364..b2674af953 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductAttributeExtensions.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductAttributeExtensions.cs @@ -5,7 +5,7 @@ namespace SmartStore.Services.Catalog { - public static class ProductAttributeExtensions + public static class ProductAttributeExtensions { /// /// A value indicating whether this product variant attribute should have values @@ -19,73 +19,73 @@ public static bool ShouldHaveValues(this ProductVariantAttribute productVariantA if (productVariantAttribute.AttributeControlType == AttributeControlType.TextBox || productVariantAttribute.AttributeControlType == AttributeControlType.MultilineTextbox || - productVariantAttribute.AttributeControlType == AttributeControlType.Datepicker || + productVariantAttribute.AttributeControlType == AttributeControlType.Datepicker || productVariantAttribute.AttributeControlType == AttributeControlType.FileUpload) return false; // All other attribute control types support values. return true; } - - public static string AddProductAttribute(this ProductVariantAttribute pva, string attributes, string value) - { - string result = string.Empty; - try - { - var xmlDoc = new XmlDocument(); - if (String.IsNullOrEmpty(attributes)) - { - var element1 = xmlDoc.CreateElement("Attributes"); - xmlDoc.AppendChild(element1); - } - else - { - xmlDoc.LoadXml(attributes); - } - var rootElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes"); - XmlElement pvaElement = null; - //find existing - var nodeList1 = xmlDoc.SelectNodes(@"//Attributes/ProductVariantAttribute"); - foreach (XmlNode node1 in nodeList1) - { - if (node1.Attributes != null && node1.Attributes["ID"] != null) - { - string str1 = node1.Attributes["ID"].InnerText.Trim(); - int id = 0; - if (int.TryParse(str1, out id)) - { - if (id == pva.Id) - { - pvaElement = (XmlElement)node1; - break; - } - } - } - } + public static string AddProductAttribute(this ProductVariantAttribute pva, string attributes, string value) + { + string result = string.Empty; + try + { + var xmlDoc = new XmlDocument(); + if (String.IsNullOrEmpty(attributes)) + { + var element1 = xmlDoc.CreateElement("Attributes"); + xmlDoc.AppendChild(element1); + } + else + { + xmlDoc.LoadXml(attributes); + } + var rootElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes"); + + XmlElement pvaElement = null; + //find existing + var nodeList1 = xmlDoc.SelectNodes(@"//Attributes/ProductVariantAttribute"); + foreach (XmlNode node1 in nodeList1) + { + if (node1.Attributes != null && node1.Attributes["ID"] != null) + { + string str1 = node1.Attributes["ID"].InnerText.Trim(); + int id = 0; + if (int.TryParse(str1, out id)) + { + if (id == pva.Id) + { + pvaElement = (XmlElement)node1; + break; + } + } + } + } - //create new one if not found - if (pvaElement == null) - { - pvaElement = xmlDoc.CreateElement("ProductVariantAttribute"); - pvaElement.SetAttribute("ID", pva.Id.ToString()); - rootElement.AppendChild(pvaElement); - } + //create new one if not found + if (pvaElement == null) + { + pvaElement = xmlDoc.CreateElement("ProductVariantAttribute"); + pvaElement.SetAttribute("ID", pva.Id.ToString()); + rootElement.AppendChild(pvaElement); + } - var pvavElement = xmlDoc.CreateElement("ProductVariantAttributeValue"); - pvaElement.AppendChild(pvavElement); + var pvavElement = xmlDoc.CreateElement("ProductVariantAttributeValue"); + pvaElement.AppendChild(pvavElement); - var pvavVElement = xmlDoc.CreateElement("Value"); - pvavVElement.InnerText = value; - pvavElement.AppendChild(pvavVElement); + var pvavVElement = xmlDoc.CreateElement("Value"); + pvavVElement.InnerText = value; + pvavElement.AppendChild(pvavVElement); - result = xmlDoc.OuterXml; - } - catch (Exception exc) - { - Debug.Write(exc.ToString()); - } - return result; - } - } + result = xmlDoc.OuterXml; + } + catch (Exception exc) + { + Debug.Write(exc.ToString()); + } + return result; + } + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductAttributeFormatter.cs b/src/Libraries/SmartStore.Services/Catalog/ProductAttributeFormatter.cs index 7d3c6db822..8e54c8d8a1 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductAttributeFormatter.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductAttributeFormatter.cs @@ -13,248 +13,259 @@ namespace SmartStore.Services.Catalog { - /// - /// Product attribute formatter - /// - public partial class ProductAttributeFormatter : IProductAttributeFormatter + /// + /// Product attribute formatter + /// + public partial class ProductAttributeFormatter : IProductAttributeFormatter { private readonly IWorkContext _workContext; private readonly IProductAttributeService _productAttributeService; private readonly IProductAttributeParser _productAttributeParser; - private readonly IPriceCalculationService _priceCalculationService; + private readonly IPriceCalculationService _priceCalculationService; private readonly ICurrencyService _currencyService; private readonly ILocalizationService _localizationService; private readonly ITaxService _taxService; private readonly IPriceFormatter _priceFormatter; private readonly IDownloadService _downloadService; private readonly IWebHelper _webHelper; - private readonly ShoppingCartSettings _shoppingCartSettings; - private readonly CatalogSettings _catalogSettings; + private readonly ShoppingCartSettings _shoppingCartSettings; + private readonly CatalogSettings _catalogSettings; - public ProductAttributeFormatter(IWorkContext workContext, + public ProductAttributeFormatter(IWorkContext workContext, IProductAttributeService productAttributeService, IProductAttributeParser productAttributeParser, - IPriceCalculationService priceCalculationService, + IPriceCalculationService priceCalculationService, ICurrencyService currencyService, ILocalizationService localizationService, ITaxService taxService, IPriceFormatter priceFormatter, IDownloadService downloadService, IWebHelper webHelper, - ShoppingCartSettings shoppingCartSettings, - CatalogSettings catalogSettings) + ShoppingCartSettings shoppingCartSettings, + CatalogSettings catalogSettings) { _workContext = workContext; _productAttributeService = productAttributeService; _productAttributeParser = productAttributeParser; - _priceCalculationService = priceCalculationService; + _priceCalculationService = priceCalculationService; _currencyService = currencyService; _localizationService = localizationService; _taxService = taxService; _priceFormatter = priceFormatter; _downloadService = downloadService; _webHelper = webHelper; - _shoppingCartSettings = shoppingCartSettings; - _catalogSettings = catalogSettings; + _shoppingCartSettings = shoppingCartSettings; + _catalogSettings = catalogSettings; } /// - /// Formats attributes + /// Formats attributes. /// /// Product /// Attributes - /// Attributes + /// Formatted attributes. public string FormatAttributes(Product product, string attributes) { - var customer = _workContext.CurrentCustomer; - return FormatAttributes(product, attributes, customer); + return FormatAttributes(product, attributes, _workContext.CurrentCustomer); } /// - /// Formats attributes + /// Formats attributes. /// /// Product /// Attributes /// Customer - /// Serapator + /// Separator /// A value indicating whether to encode (HTML) values /// A value indicating whether to render prices /// A value indicating whether to render product attributes /// A value indicating whether to render gift card attributes - /// A value indicating whether to HTML hyperink tags could be rendered (if required) - /// Attributes - public string FormatAttributes(Product product, string attributes, - Customer customer, string serapator = "
", bool htmlEncode = true, bool renderPrices = true, - bool renderProductAttributes = true, bool renderGiftCardAttributes = true, + /// A value indicating whether to render HTML hyperlinks + /// Formatted attributes. + public string FormatAttributes( + Product product, + string attributes, + Customer customer, + string separator = "
", + bool htmlEncode = true, + bool renderPrices = true, + bool renderProductAttributes = true, + bool renderGiftCardAttributes = true, bool allowHyperlinks = true) { var result = new StringBuilder(); - var languageId = _workContext.WorkingLanguage.Id; - // Attributes + // Attributes. if (renderProductAttributes) { + var languageId = _workContext.WorkingLanguage.Id; var pvaCollection = _productAttributeParser.ParseProductVariantAttributes(attributes); - for (int i = 0; i < pvaCollection.Count; i++) + + for (var i = 0; i < pvaCollection.Count; ++i) { var pva = pvaCollection[i]; var valuesStr = _productAttributeParser.ParseValues(attributes, pva.Id); - for (int j = 0; j < valuesStr.Count; j++) + + for (var j = 0; j < valuesStr.Count; ++j) { - string valueStr = valuesStr[j]; - string pvaAttribute = string.Empty; + var valueStr = valuesStr[j]; + var pvaAttribute = string.Empty; + if (!pva.ShouldHaveValues()) { - //no values + // No values. if (pva.AttributeControlType == AttributeControlType.MultilineTextbox) { - //multiline textbox + // Multiline textbox. string attributeName = pva.ProductAttribute.GetLocalized(a => a.Name, languageId); - //encode (if required) + if (htmlEncode) + { attributeName = HttpUtility.HtmlEncode(attributeName); + } + pvaAttribute = string.Format("{0}: {1}", attributeName, HtmlUtils.ConvertPlainTextToHtml(valueStr.HtmlEncode())); - //we never encode multiline textbox input + // We never encode multiline textbox input. } else if (pva.AttributeControlType == AttributeControlType.FileUpload) { - //file upload - Guid downloadGuid; - Guid.TryParse(valueStr, out downloadGuid); + Guid.TryParse(valueStr, out var downloadGuid); var download = _downloadService.GetDownloadByGuid(downloadGuid); - if (download != null) + + if (download?.MediaFile != null) { - //TODO add a method for getting URL (use routing because it handles all SEO friendly URLs) - string attributeText = ""; - var fileName = string.Format("{0}{1}", - download.Filename ?? download.DownloadGuid.ToString(), - download.Extension); - //encode (if required) + // TODO: add a method for getting URL (use routing because it handles all SEO friendly URLs). + var attributeText = ""; + var fileName = download.MediaFile.Name; + if (htmlEncode) + { fileName = HttpUtility.HtmlEncode(fileName); + } + if (allowHyperlinks) { - //hyperlinks are allowed - var downloadLink = string.Format("{0}download/getfileupload/?downloadId={1}", _webHelper.GetStoreLocation(false), download.DownloadGuid); + var downloadLink = string.Format("{0}download/getfileupload/?downloadId={1}", _webHelper.GetStoreLocation(), download.DownloadGuid); attributeText = string.Format("{1}", downloadLink, fileName); } else { - //hyperlinks aren't allowed - attributeText = fileName; + attributeText + = fileName; } string attributeName = pva.ProductAttribute.GetLocalized(a => a.Name, languageId); - //encode (if required) + if (htmlEncode) + { attributeName = HttpUtility.HtmlEncode(attributeName); + } + pvaAttribute = string.Format("{0}: {1}", attributeName, attributeText); } } else { - //other attributes (textbox, datepicker) + // Other attributes (textbox, datepicker). pvaAttribute = string.Format("{0}: {1}", pva.ProductAttribute.GetLocalized(a => a.Name, languageId), valueStr); - //encode (if required) + if (htmlEncode) + { pvaAttribute = HttpUtility.HtmlEncode(pvaAttribute); + } } } else { // Attributes with values. - int pvaId = 0; - if (int.TryParse(valueStr, out pvaId)) + if (int.TryParse(valueStr, out var pvaId)) { var pvaValue = _productAttributeService.GetProductVariantAttributeValueById(pvaId); if (pvaValue != null) { pvaAttribute = "{0}: {1}".FormatInvariant( - pva.ProductAttribute.GetLocalized(a => a.Name, languageId), - pvaValue.GetLocalized(a => a.Name, languageId)); + pva.ProductAttribute.GetLocalized(a => a.Name, languageId), + pvaValue.GetLocalized(a => a.Name, languageId)); if (renderPrices) { - decimal taxRate = decimal.Zero; - decimal attributeValuePriceAdjustment = _priceCalculationService.GetProductVariantAttributeValuePriceAdjustment(pvaValue, product, customer, null, 1); - decimal priceAdjustmentBase = _taxService.GetProductPrice(product, attributeValuePriceAdjustment, customer, out taxRate); - decimal priceAdjustment = _currencyService.ConvertFromPrimaryStoreCurrency(priceAdjustmentBase, _workContext.WorkingCurrency); + var attributeValuePriceAdjustment = _priceCalculationService.GetProductVariantAttributeValuePriceAdjustment(pvaValue, product, customer, null, 1); + var priceAdjustmentBase = _taxService.GetProductPrice(product, attributeValuePriceAdjustment, customer, out var taxRate); + var priceAdjustment = _currencyService.ConvertFromPrimaryStoreCurrency(priceAdjustmentBase, _workContext.WorkingCurrency); - if (_shoppingCartSettings.ShowLinkedAttributeValueQuantity && pvaValue.ValueType == ProductVariantAttributeValueType.ProductLinkage && - pvaValue.Quantity > 1) - { - pvaAttribute += string.Format(" {0}", pvaValue.Quantity); - } + if (_shoppingCartSettings.ShowLinkedAttributeValueQuantity && + pvaValue.ValueType == ProductVariantAttributeValueType.ProductLinkage && + pvaValue.Quantity > 1) + { + pvaAttribute += string.Format(" {0}", pvaValue.Quantity); + } - if (_catalogSettings.ShowVariantCombinationPriceAdjustment) - { - if (priceAdjustmentBase > 0) - { - pvaAttribute += " (+{0})".FormatInvariant(_priceFormatter.FormatPrice(priceAdjustment, true, false)); - } - else if (priceAdjustmentBase < decimal.Zero) - { - pvaAttribute += " (-{0})".FormatInvariant(_priceFormatter.FormatPrice(-priceAdjustment, true, false)); - } - } + if (_catalogSettings.ShowVariantCombinationPriceAdjustment) + { + if (priceAdjustmentBase > 0) + { + pvaAttribute += " (+{0})".FormatInvariant(_priceFormatter.FormatPrice(priceAdjustment, true, false)); + } + else if (priceAdjustmentBase < decimal.Zero) + { + pvaAttribute += " (-{0})".FormatInvariant(_priceFormatter.FormatPrice(-priceAdjustment, true, false)); + } + } } } - // Encode (if required) - if (htmlEncode) - { - pvaAttribute = HttpUtility.HtmlEncode(pvaAttribute); - } + if (htmlEncode) + { + pvaAttribute = HttpUtility.HtmlEncode(pvaAttribute); + } } } - if (!String.IsNullOrEmpty(pvaAttribute)) + if (!string.IsNullOrEmpty(pvaAttribute)) { if (i != 0 || j != 0) - result.Append(serapator); + { + result.Append(separator); + } result.Append(pvaAttribute); } } } } - //gift cards + // Gift cards. if (renderGiftCardAttributes) { if (product.IsGiftCard) { - string giftCardRecipientName = ""; - string giftCardRecipientEmail = ""; - string giftCardSenderName = ""; - string giftCardSenderEmail = ""; - string giftCardMessage = ""; - _productAttributeParser.GetGiftCardAttribute(attributes, out giftCardRecipientName, out giftCardRecipientEmail, - out giftCardSenderName, out giftCardSenderEmail, out giftCardMessage); + _productAttributeParser.GetGiftCardAttribute(attributes, out var giftCardRecipientName, out var giftCardRecipientEmail, + out var giftCardSenderName, out var giftCardSenderEmail, out var giftCardMessage); + + // Sender. + var giftCardFrom = product.GiftCardType == GiftCardType.Virtual + ? string.Format(_localizationService.GetResource("GiftCardAttribute.From.Virtual"), giftCardSenderName, giftCardSenderEmail) + : string.Format(_localizationService.GetResource("GiftCardAttribute.From.Physical"), giftCardSenderName); - //sender - var giftCardFrom = product.GiftCardType == GiftCardType.Virtual ? - string.Format(_localizationService.GetResource("GiftCardAttribute.From.Virtual"), giftCardSenderName, giftCardSenderEmail) : - string.Format(_localizationService.GetResource("GiftCardAttribute.From.Physical"), giftCardSenderName); - //recipient - var giftCardFor = product.GiftCardType == GiftCardType.Virtual ? - string.Format(_localizationService.GetResource("GiftCardAttribute.For.Virtual"), giftCardRecipientName, giftCardRecipientEmail) : - string.Format(_localizationService.GetResource("GiftCardAttribute.For.Physical"), giftCardRecipientName); + // Recipient. + var giftCardFor = product.GiftCardType == GiftCardType.Virtual + ? string.Format(_localizationService.GetResource("GiftCardAttribute.For.Virtual"), giftCardRecipientName, giftCardRecipientEmail) + : string.Format(_localizationService.GetResource("GiftCardAttribute.For.Physical"), giftCardRecipientName); - //encode (if required) if (htmlEncode) { giftCardFrom = HttpUtility.HtmlEncode(giftCardFrom); giftCardFor = HttpUtility.HtmlEncode(giftCardFor); } - if (!String.IsNullOrEmpty(result.ToString())) + if (!string.IsNullOrEmpty(result.ToString())) { - result.Append(serapator); + result.Append(separator); } result.Append(giftCardFrom); - result.Append(serapator); + result.Append(separator); result.Append(giftCardFor); } } + return result.ToString(); } } diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductAttributeParser.cs b/src/Libraries/SmartStore.Services/Catalog/ProductAttributeParser.cs index 7c6f33ce38..6fb44522a1 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductAttributeParser.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductAttributeParser.cs @@ -1,165 +1,178 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Xml; using System.Xml.Linq; using SmartStore.Collections; +using SmartStore.Core; using SmartStore.Core.Caching; using SmartStore.Core.Data; +using SmartStore.Core.Data.Hooks; using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Common; +using SmartStore.Core.Domain.Configuration; using SmartStore.Core.Logging; +using SmartStore.Data.Utilities; +using SmartStore.Utilities.ObjectPools; namespace SmartStore.Services.Catalog { - public partial class ProductAttributeParser : IProductAttributeParser + public partial class ProductAttributeParser : DbSaveHook, IProductAttributeParser { - // 0 = ProductId, 1 = AttributeXml - private const string ATTRIBUTECOMBINATION_BY_IDXML_KEY = "parsedattributecombination.id-{0}-{1}"; + // 0 = ProductId, 1 = AttributeXml + private const string ATTRIBUTECOMBINATION_BY_IDXML_KEY = "parsedattributecombination.id-{0}-{1}"; - // 0 = AttributeXml - private const string ATTRIBUTEVALUES_BY_XML_KEY = "parsedattributevalues-{0}"; + // 0 = AttributeXml + private const string ATTRIBUTEVALUES_BY_XML_KEY = "parsedattributevalues-{0}"; private const string ATTRIBUTEVALUES_PATTERN_KEY = "parsedattributevalues-*"; - private readonly IProductAttributeService _productAttributeService; - private readonly IRepository _pvacRepository; - private readonly IRequestCache _requestCache; + // 0 = ProductId + private const string UNAVAILABLE_COMBINATIONS_KEY = "attributecombination:unavailable-{0}"; + private const string UNAVAILABLE_COMBINATIONS_PATTERN_KEY = "attributecombination:unavailable-*"; - public ProductAttributeParser( - IProductAttributeService productAttributeService, - IRepository pvacRepository, - IRequestCache requestCache) + private readonly IProductAttributeService _productAttributeService; + private readonly IRepository _pvacRepository; + private readonly IRequestCache _requestCache; + private readonly ICacheManager _cache; + private readonly PerformanceSettings _performanceSettings; + + public ProductAttributeParser( + IProductAttributeService productAttributeService, + IRepository pvacRepository, + IRequestCache requestCache, + ICacheManager cache, + PerformanceSettings performanceSettings) { _productAttributeService = productAttributeService; - _pvacRepository = pvacRepository; - _requestCache = requestCache; - - Logger = NullLogger.Instance; - } + _pvacRepository = pvacRepository; + _requestCache = requestCache; + _cache = cache; + _performanceSettings = performanceSettings; + } - public ILogger Logger { get; set; } + public ILogger Logger { get; set; } = NullLogger.Instance; - #region Product attributes + #region Product attributes - public virtual int PrefetchProductVariantAttributes(IEnumerable attributesXml) - { + public virtual int PrefetchProductVariantAttributes(IEnumerable attributesXml) + { if (attributesXml == null || !attributesXml.Any()) { return 0; } - // Determine uncached attributes - var unfetched = attributesXml - .Where(xml => xml.HasValue()) - .Distinct() - .Where(xml => !_requestCache.Contains(ATTRIBUTEVALUES_BY_XML_KEY.FormatInvariant(xml))) - .ToArray(); - - var infos = new List(unfetched.Length); - - foreach (var xml in unfetched) - { - var valueIds = new HashSet(); - var map = DeserializeProductVariantAttributes(xml); - var attributes = _productAttributeService.GetProductVariantAttributesByIds(map.Keys); - - foreach (var attribute in attributes) - { - // Only types that have attribute values! Otherwise entered text is misinterpreted as an attribute value id. - if (!attribute.ShouldHaveValues()) - continue; - - var ids = - from id in map[attribute.Id] - where id.HasValue() - select id.ToInt(); - - valueIds.UnionWith(ids); - } - - var info = new AttributeMapInfo - { - AttributesXml = xml, - DeserializedMap = map, - AllValueIds = valueIds.ToArray() - }; - - infos.Add(info); - } - - // Get all value ids across all maps (each map has many attributes) - var allValueIds = infos.SelectMany(x => x.AllValueIds) - .Distinct() - .ToArray(); - - // Load ALL requested attribute values into a single dictionary in one go (key is Id) - var attributeValues = _productAttributeService.GetProductVariantAttributeValuesByIds(allValueIds).ToDictionarySafe(x => x.Id); - - // Create a single cache entry for each passed xml - foreach (var info in infos) - { - var cachedValues = new List(); - - // Ensure value id order in cached result list is correct - foreach (var id in info.AllValueIds) - { - if (attributeValues.TryGetValue(id, out var value)) - { - cachedValues.Add(value); - } - } - - // Put it in cache - var cacheKey = ATTRIBUTEVALUES_BY_XML_KEY.FormatInvariant(info.AttributesXml); - _requestCache.Put(cacheKey, cachedValues); - } + // Determine uncached attributes + var unfetched = attributesXml + .Where(xml => xml.HasValue()) + .Distinct() + .Where(xml => !_requestCache.Contains(ATTRIBUTEVALUES_BY_XML_KEY.FormatInvariant(xml))) + .ToArray(); + + var infos = new List(unfetched.Length); + + foreach (var xml in unfetched) + { + var valueIds = new HashSet(); + var map = DeserializeProductVariantAttributes(xml); + var attributes = _productAttributeService.GetProductVariantAttributesByIds(map.Keys); + + foreach (var attribute in attributes) + { + // Only types that have attribute values! Otherwise entered text is misinterpreted as an attribute value id. + if (!attribute.ShouldHaveValues()) + continue; + + var ids = + from id in map[attribute.Id] + where id.HasValue() + select id.ToInt(); + + valueIds.UnionWith(ids); + } + + var info = new AttributeMapInfo + { + AttributesXml = xml, + DeserializedMap = map, + AllValueIds = valueIds.ToArray() + }; + + infos.Add(info); + } + + // Get all value ids across all maps (each map has many attributes) + var allValueIds = infos.SelectMany(x => x.AllValueIds) + .Distinct() + .ToArray(); + + // Load ALL requested attribute values into a single dictionary in one go (key is Id) + var attributeValues = _productAttributeService.GetProductVariantAttributeValuesByIds(allValueIds).ToDictionarySafe(x => x.Id); + + // Create a single cache entry for each passed xml + foreach (var info in infos) + { + var cachedValues = new List(); + + // Ensure value id order in cached result list is correct + foreach (var id in info.AllValueIds) + { + if (attributeValues.TryGetValue(id, out var value)) + { + cachedValues.Add(value); + } + } + + // Put it in cache + var cacheKey = ATTRIBUTEVALUES_BY_XML_KEY.FormatInvariant(info.AttributesXml); + _requestCache.Put(cacheKey, cachedValues); + } return unfetched.Length; } - public virtual IList ParseProductVariantAttributes(string attributesXml) - { - var values = ParseProductVariantAttributeValues(attributesXml); - var attrMap = DeserializeProductVariantAttributes(attributesXml); - var attrs = _productAttributeService.GetProductVariantAttributesByIds(attrMap.Keys, values.Select(x => x.ProductVariantAttribute).Distinct().ToList()); + public virtual IList ParseProductVariantAttributes(string attributesXml) + { + var values = ParseProductVariantAttributeValues(attributesXml); + var attrMap = DeserializeProductVariantAttributes(attributesXml); + var attrs = _productAttributeService.GetProductVariantAttributesByIds(attrMap.Keys, values.Select(x => x.ProductVariantAttribute).Distinct().ToList()); - return attrs; - } + return attrs; + } - public virtual IEnumerable ParseProductVariantAttributeValues(string attributeXml) - { - if (attributeXml.IsEmpty()) - return new List(); + public virtual IEnumerable ParseProductVariantAttributeValues(string attributeXml) + { + if (attributeXml.IsEmpty()) + return new List(); - var cacheKey = ATTRIBUTEVALUES_BY_XML_KEY.FormatInvariant(attributeXml); + var cacheKey = ATTRIBUTEVALUES_BY_XML_KEY.FormatInvariant(attributeXml); - var result = _requestCache.Get(cacheKey, () => - { - var allValueIds = new HashSet(); - var attrMap = DeserializeProductVariantAttributes(attributeXml); - var attributes = _productAttributeService.GetProductVariantAttributesByIds(attrMap.Keys); + var result = _requestCache.Get(cacheKey, () => + { + var allValueIds = new HashSet(); + var attrMap = DeserializeProductVariantAttributes(attributeXml); + var attributes = _productAttributeService.GetProductVariantAttributesByIds(attrMap.Keys); - foreach (var attribute in attributes) - { - // Only types that have attribute values! Otherwise entered text is misinterpreted as an attribute value id. - if (!attribute.ShouldHaveValues()) - continue; + foreach (var attribute in attributes) + { + // Only types that have attribute values! Otherwise entered text is misinterpreted as an attribute value id. + if (!attribute.ShouldHaveValues()) + continue; - var ids = - from id in attrMap[attribute.Id] - where id.HasValue() - select id.ToInt(); + var ids = + from id in attrMap[attribute.Id] + where id.HasValue() + select id.ToInt(); - allValueIds.UnionWith(ids); - } + allValueIds.UnionWith(ids); + } - var values = _productAttributeService.GetProductVariantAttributeValuesByIds(allValueIds.ToArray()); + var values = _productAttributeService.GetProductVariantAttributeValuesByIds(allValueIds.ToArray()); - return values; - }); + return values; + }); - return result; - } + return result; + } public virtual void ClearCachedAttributeValues() { @@ -167,77 +180,83 @@ public virtual void ClearCachedAttributeValues() } public virtual Multimap DeserializeProductVariantAttributes(string attributesXml) - { - var attrs = new Multimap(); - if (String.IsNullOrEmpty(attributesXml)) - return attrs; - - try - { - var doc = XDocument.Parse(attributesXml); - - // Attributes/ProductVariantAttribute - foreach (var node1 in doc.Descendants("ProductVariantAttribute")) - { - string sid = node1.Attribute("ID").Value; - if (sid.HasValue()) - { - if (int.TryParse(sid, out var id)) - { - // ProductVariantAttributeValue/Value - foreach (var node2 in node1.Descendants("Value")) - { - attrs.Add(id, node2.Value); - } - } - } - } - } - catch (Exception exception) - { - Logger.Error(exception); - } - - return attrs; - } - - public virtual ICollection ParseProductVariantAttributeValues(Multimap attributeCombination, IEnumerable attributes) - { - var result = new HashSet(); - - if (attributeCombination == null || !attributeCombination.Any()) - return result; - - var allValueIds = new HashSet(); - - foreach (var pva in attributes.Where(x => x.ShouldHaveValues()).OrderBy(x => x.DisplayOrder).ToArray()) - { - if (attributeCombination.ContainsKey(pva.Id)) - { - var pvaValuesStr = attributeCombination[pva.Id]; - var ids = pvaValuesStr.Where(x => x.HasValue()).Select(x => x.ToInt()); - - allValueIds.UnionWith(ids); - } - } - - foreach (int id in allValueIds) - { - foreach (var attribute in attributes) - { - var attributeValue = attribute.ProductVariantAttributeValues.FirstOrDefault(x => x.Id == id); - if (attributeValue != null) - { - result.Add(attributeValue); - break; - } - } - } - - return result; - } - - public virtual IList ParseValues(string attributesXml, int productVariantAttributeId) + { + return DeserializeAttributesXml(attributesXml, null); + } + + private Multimap DeserializeAttributesXml(string attributesXml, int[] includeAttributesIds = null) + { + var attrs = new Multimap(); + if (String.IsNullOrEmpty(attributesXml)) + return attrs; + + try + { + var doc = XDocument.Parse(attributesXml); + + // Attributes/ProductVariantAttribute + foreach (var node1 in doc.Descendants("ProductVariantAttribute")) + { + string sid = node1.Attribute("ID").Value; + if (sid.HasValue()) + { + if (int.TryParse(sid, out var id) && + (includeAttributesIds == null || includeAttributesIds.Contains(id))) + { + // ProductVariantAttributeValue/Value + foreach (var node2 in node1.Descendants("Value")) + { + attrs.Add(id, node2.Value); + } + } + } + } + } + catch (Exception exception) + { + Logger.Error(exception); + } + + return attrs; + } + + public virtual ICollection ParseProductVariantAttributeValues(Multimap attributeCombination, IEnumerable attributes) + { + var result = new HashSet(); + + if (attributeCombination == null || !attributeCombination.Any()) + return result; + + var allValueIds = new HashSet(); + + foreach (var pva in attributes.Where(x => x.ShouldHaveValues()).OrderBy(x => x.DisplayOrder).ToArray()) + { + if (attributeCombination.ContainsKey(pva.Id)) + { + var pvaValuesStr = attributeCombination[pva.Id]; + var ids = pvaValuesStr.Where(x => x.HasValue()).Select(x => x.ToInt()); + + allValueIds.UnionWith(ids); + } + } + + foreach (int id in allValueIds) + { + foreach (var attribute in attributes) + { + var attributeValue = attribute.ProductVariantAttributeValues.FirstOrDefault(x => x.Id == id); + if (attributeValue != null) + { + result.Add(attributeValue); + break; + } + } + } + + return result; + } + + public virtual IList ParseValues(string attributesXml, int productVariantAttributeId) { var selectedProductVariantAttributeValues = new List(); try @@ -268,132 +287,142 @@ public virtual IList ParseValues(string attributesXml, int productVarian } catch (Exception exception) { - Logger.Error(exception); - } + Logger.Error(exception); + } return selectedProductVariantAttributeValues; } public virtual string AddProductAttribute(string attributesXml, ProductVariantAttribute pva, string value) { - return pva.AddProductAttribute(attributesXml, value); + return pva.AddProductAttribute(attributesXml, value); } - public virtual string CreateAttributesXml(Multimap attributes) - { - Guard.NotNull(attributes, nameof(attributes)); + public virtual string CreateAttributesXml(Multimap attributes) + { + Guard.NotNull(attributes, nameof(attributes)); - if (attributes.Count == 0) - return null; + if (attributes.Count == 0) + return null; - var doc = new XmlDocument(); - var root = doc.AppendChild(doc.CreateElement("Attributes")); + var doc = new XmlDocument(); + var root = doc.AppendChild(doc.CreateElement("Attributes")); - foreach (var attr in attributes) - { - var xelAttr = root.AppendChild(doc.CreateElement("ProductVariantAttribute")) as XmlElement; - xelAttr.SetAttribute("ID", attr.Key.ToString()); + foreach (var attr in attributes) + { + var xelAttr = root.AppendChild(doc.CreateElement("ProductVariantAttribute")) as XmlElement; + xelAttr.SetAttribute("ID", attr.Key.ToString()); - foreach (var val in attr.Value) - { - var xelAttrValue = xelAttr.AppendChild(doc.CreateElement("ProductVariantAttributeValue")); - var xelValue = xelAttrValue.AppendChild(doc.CreateElement("Value")); - xelValue.InnerText = val; - } - } + foreach (var val in attr.Value) + { + var xelAttrValue = xelAttr.AppendChild(doc.CreateElement("ProductVariantAttributeValue")); + var xelValue = xelAttrValue.AppendChild(doc.CreateElement("Value")); + xelValue.InnerText = val; + } + } - return doc.OuterXml; - } + return doc.OuterXml; + } - public virtual bool AreProductAttributesEqual(string attributeXml1, string attributeXml2) + public virtual bool AreProductAttributesEqual(string attributeXml1, string attributeXml2) { - if (attributeXml1 == attributeXml2) - return true; + if (attributeXml1 == attributeXml2) + return true; - var attributes1 = DeserializeProductVariantAttributes(attributeXml1); + var attributes1 = DeserializeProductVariantAttributes(attributeXml1); var attributes2 = DeserializeProductVariantAttributes(attributeXml2); - if (attributes1.Count != attributes2.Count) - return false; - - foreach (var kvp in attributes1) - { - if (!attributes2.ContainsKey(kvp.Key)) - { - // the second list does not contain this id: not equal! - return false; - } - - // compare the values - var values1 = kvp.Value; - var values2 = attributes2[kvp.Key]; - - if (values1.Count != values2.Count) - { - // number of values differ: not equal! - return false; - } - - foreach (var value1 in values1) - { - var str1 = value1.TrimSafe(); - - if (!values2.Any(x => x.TrimSafe().IsCaseInsensitiveEqual(str1))) - { - // the second values list for this attribute does not contain this value: not equal! - return false; - } - } - } + return AreProductAttributesEqual(attributes1, attributes2); + } + + private bool AreProductAttributesEqual(Multimap attributes1, Multimap attributes2) + { + if (attributes1.Count != attributes2.Count) + return false; + + foreach (var kvp in attributes1) + { + if (!attributes2.ContainsKey(kvp.Key)) + { + // the second list does not contain this id: not equal! + return false; + } + + // compare the values + var values1 = kvp.Value; + var values2 = attributes2[kvp.Key]; + + if (values1.Count != values2.Count) + { + // number of values differ: not equal! + return false; + } + + foreach (var value1 in values1) + { + var str1 = value1.TrimSafe(); + + if (!values2.Any(x => x.TrimSafe().IsCaseInsensitiveEqual(str1))) + { + // the second values list for this attribute does not contain this value: not equal! + return false; + } + } + } return true; } - public virtual ProductVariantAttributeCombination FindProductVariantAttributeCombination(int productId, string attributesXml) - { - if (attributesXml.IsEmpty()) - return null; + public virtual ProductVariantAttributeCombination FindProductVariantAttributeCombination(int productId, string attributesXml) + { + if (attributesXml.IsEmpty()) + return null; - var cacheKey = ATTRIBUTECOMBINATION_BY_IDXML_KEY.FormatInvariant(productId, attributesXml); + var cacheKey = ATTRIBUTECOMBINATION_BY_IDXML_KEY.FormatInvariant(productId, attributesXml); - var result = _requestCache.Get(cacheKey, () => - { - var query = from x in _pvacRepository.TableUntracked - where x.ProductId == productId - select new - { - x.Id, - x.AttributesXml - }; + var result = _requestCache.Get(cacheKey, () => + { + var query = from x in _pvacRepository.TableUntracked + where x.ProductId == productId + select new + { + x.Id, + x.AttributesXml + }; - var combinations = query.ToList(); - if (combinations.Count == 0) - return null; + var combinations = query.ToList(); + if (combinations.Count == 0) + return null; - foreach (var combination in combinations) - { - bool attributesEqual = AreProductAttributesEqual(combination.AttributesXml, attributesXml); - if (attributesEqual) - return _productAttributeService.GetProductVariantAttributeCombinationById(combination.Id); - } + var listTypeValues = ParseProductVariantAttributeValues(attributesXml); + var listTypeAttributesIds = listTypeValues.Select(x => x.ProductVariantAttributeId).ToArray(); + var listTypeAttributesMap = DeserializeAttributesXml(attributesXml, listTypeAttributesIds); - return null; - }); + foreach (var combination in combinations) + { + var combinationAttributesMap = DeserializeProductVariantAttributes(combination.AttributesXml); + bool attributesEqual = AreProductAttributesEqual(combinationAttributesMap, listTypeAttributesMap); + if (attributesEqual) + return _productAttributeService.GetProductVariantAttributeCombinationById(combination.Id); + } - return result; - } + return null; + }); - #endregion + return result; + } - #region Gift card attributes + #endregion - public string AddGiftCardAttribute( - string attributesXml, - string recipientName, + #region Gift card attributes + + public string AddGiftCardAttribute( + string attributesXml, + string recipientName, string recipientEmail, - string senderName, - string senderEmail, - string giftCardMessage) + string senderName, + string senderEmail, + string giftCardMessage) { var result = string.Empty; @@ -448,19 +477,19 @@ public string AddGiftCardAttribute( } catch (Exception exception) { - Logger.Error(exception); + Logger.Error(exception); } return result; } public void GetGiftCardAttribute( - string attributesXml, - out string recipientName, + string attributesXml, + out string recipientName, out string recipientEmail, - out string senderName, + out string senderName, out string senderEmail, - out string giftCardMessage) + out string giftCardMessage) { recipientName = string.Empty; recipientEmail = string.Empty; @@ -492,17 +521,218 @@ public void GetGiftCardAttribute( } catch (Exception exception) { - Logger.Error(exception); - } + Logger.Error(exception); + } } #endregion - class AttributeMapInfo - { - public string AttributesXml { get; set; } - public Multimap DeserializedMap { get; set; } - public int[] AllValueIds { get; set; } - } + #region Unavailable attribute combinations + + public virtual CombinationAvailabilityInfo IsCombinationAvailable( + Product product, + IEnumerable attributes, + IEnumerable selectedValues, + ProductVariantAttributeValue currentValue) + { + if (product == null || + _performanceSettings.MaxUnavailableAttributeCombinations <= 0 || + !(selectedValues?.Any() ?? false)) + { + return null; + } + + // Get unavailable combinations. + var unavailableCombinations = _cache.Get(UNAVAILABLE_COMBINATIONS_KEY.FormatInvariant(product.Id), () => + { + var data = new Dictionary(); + var query = _pvacRepository.TableUntracked.Where(x => x.ProductId == product.Id); + + if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes) + { + query = query.Where(x => !x.IsActive || (x.StockQuantity <= 0 && !x.AllowOutOfStockOrders)); + } + else + { + query = query.Where(x => !x.IsActive); + } + + // Do not proceed if there are too many unavailable combinations. + var unavailableCombinationsCount = query.Count(); + + if (unavailableCombinationsCount <= _performanceSettings.MaxUnavailableAttributeCombinations) + { + var pager = new FastPager(query); + + while (pager.ReadNextPage(out var combinations)) + { + foreach (var combination in combinations) + { + var map = DeserializeProductVariantAttributes(combination.AttributesXml); + if (map.Any()) + { + // :[,...] + var valuesKeys = map + .OrderBy(x => x.Key) + .Select(x => $"{x.Key}:{string.Join(",", x.Value.OrderBy(y => y))}"); + + data[string.Join("-", valuesKeys)] = new CombinationAvailabilityInfo + { + IsActive = combination.IsActive, + IsOutOfStock = combination.StockQuantity <= 0 && !combination.AllowOutOfStockOrders + }; + } + } + } + } + + return data; + }, TimeSpan.FromMinutes(10)); + + if (!unavailableCombinations.Any()) + { + return null; + } + + var builder = PooledStringBuilder.Rent(); + var selectedValuesMap = selectedValues.ToMultimap(x => x.ProductVariantAttributeId, x => x); + + if (attributes == null || currentValue == null) + { + // Create key to test selectedValues. + foreach (var kvp in selectedValuesMap.OrderBy(x => x.Key)) + { + Append(builder, kvp.Key, kvp.Value.Select(x => x.Id).Distinct()); + } + } + else + { + // Create key to test currentValue. + foreach (var attribute in attributes.OrderBy(x => x.Id)) + { + IEnumerable valueIds; + + var selectedIds = selectedValuesMap.ContainsKey(attribute.Id) + ? selectedValuesMap[attribute.Id].Select(x => x.Id) + : null; + + if (attribute.Id == currentValue.ProductVariantAttributeId) + { + // Attribute to be tested. + if (selectedIds != null && attribute.IsMultipleChoice) + { + // Take selected values and append current value. + valueIds = selectedIds.Append(currentValue.Id).Distinct(); + } + else + { + // Single selection attribute -> take current value. + valueIds = new[] { currentValue.Id }; + } + } + else + { + // Other attribute. + if (selectedIds != null) + { + // Take selected value(s). + valueIds = selectedIds; + } + else + { + // No selected value -> no unavailable combination. + return null; + } + } + + Append(builder, attribute.Id, valueIds); + } + } + + var key = builder.ToStringAndReturn(); + //$"{!unavailableCombinations.ContainsKey(key),-5} {currentValue.ProductVariantAttributeId}:{currentValue.Id} -> {key}".Dump(); + + if (unavailableCombinations.TryGetValue(key, out var availability)) + { + return availability; + } + + return null; + + static void Append(PooledStringBuilder psb, int pvaId, IEnumerable pvavIds) + { + var idsStr = string.Join(",", pvavIds.OrderBy(x => x)); + + if (psb.Length > 0) + { + psb.Append("-"); + } + psb.Append($"{pvaId}:{idsStr}"); + } + } + + private static readonly HashSet _combinationsInvalidationTypes = new HashSet(new[] + { + typeof(Setting), + typeof(Product), + typeof(ProductVariantAttributeCombination) + }); + + private void InvalidateUnavailableCombinations(BaseEntity entity, IHookedEntity entry) + { + var type = entry.EntityType; + + if (!_combinationsInvalidationTypes.Contains(type)) + { + throw new NotSupportedException(); + } + + var productId = 0; + + if (type == typeof(Setting)) + { + if (((Setting)entity).Name.IsCaseInsensitiveEqual("PerformanceSettings.MaxUnavailableAttributeCombinations")) + { + _cache.RemoveByPattern(UNAVAILABLE_COMBINATIONS_PATTERN_KEY); + } + } + else if (type == typeof(Product)) + { + productId = entity.Id; + } + else if (type == typeof(ProductVariantAttributeCombination)) + { + productId = ((ProductVariantAttributeCombination)entity).ProductId; + } + + if (productId != 0) + { + _cache.Remove(UNAVAILABLE_COMBINATIONS_KEY.FormatInvariant(productId)); + } + } + + #endregion + + protected override void OnUpdated(BaseEntity entity, IHookedEntity entry) + { + InvalidateUnavailableCombinations(entity, entry); + } + + protected override void OnInserted(BaseEntity entity, IHookedEntity entry) + { + InvalidateUnavailableCombinations(entity, entry); + } + + protected override void OnDeleted(BaseEntity entity, IHookedEntity entry) + { + InvalidateUnavailableCombinations(entity, entry); + } + + class AttributeMapInfo + { + public string AttributesXml { get; set; } + public Multimap DeserializedMap { get; set; } + public int[] AllValueIds { get; set; } + } } } diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductAttributeService.cs b/src/Libraries/SmartStore.Services/Catalog/ProductAttributeService.cs index 818343d2ef..39b45da0fb 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductAttributeService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductAttributeService.cs @@ -1,101 +1,90 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Data.Entity; +using System.Linq; using SmartStore.Collections; using SmartStore.Core; using SmartStore.Core.Caching; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Localization; -using SmartStore.Core.Domain.Media; -using SmartStore.Core.Events; using SmartStore.Core.Localization; using SmartStore.Data.Caching; using SmartStore.Services.Localization; -using SmartStore.Services.Media; namespace SmartStore.Services.Catalog { - public partial class ProductAttributeService : IProductAttributeService + public partial class ProductAttributeService : IProductAttributeService { - // 0 = ProductId, 1 = PageIndex, 2 = PageSize - private const string PRODUCTVARIANTATTRIBUTES_COMBINATIONS_BY_ID_KEY = "SmartStore.productvariantattribute.combinations.id-{0}-{1}-{2}"; - private const string PRODUCTVARIANTATTRIBUTES_PATTERN_KEY = "SmartStore.productvariantattribute.*"; - - // 0 = Attribute value ids, e.g. 16-254-1245 - private const string PRODUCTVARIANTATTRIBUTEVALUES_BY_IDS_KEY = "SmartStore.productvariantattributevalues.ids-{0}"; - private const string PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY = "SmartStore.productvariantattributevalues*"; - - private readonly IRepository _productAttributeRepository; - private readonly IRepository _productAttributeOptionRepository; - private readonly IRepository _productAttributeOptionsSetRepository; - private readonly IRepository _productVariantAttributeRepository; + // 0 = ProductId, 1 = PageIndex, 2 = PageSize + private const string PRODUCTVARIANTATTRIBUTES_COMBINATIONS_BY_ID_KEY = "SmartStore.productvariantattribute.combinations.id-{0}-{1}-{2}"; + private const string PRODUCTVARIANTATTRIBUTES_PATTERN_KEY = "SmartStore.productvariantattribute.*"; + + // 0 = Attribute value ids, e.g. 16-254-1245 + private const string PRODUCTVARIANTATTRIBUTEVALUES_BY_IDS_KEY = "SmartStore.productvariantattributevalues.ids-{0}"; + private const string PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY = "SmartStore.productvariantattributevalues*"; + + private readonly IRepository _productAttributeRepository; + private readonly IRepository _productAttributeOptionRepository; + private readonly IRepository _productAttributeOptionsSetRepository; + private readonly IRepository _productVariantAttributeRepository; private readonly IRepository _pvacRepository; private readonly IRepository _productVariantAttributeValueRepository; - private readonly IRepository _productBundleItemAttributeFilterRepository; - private readonly ILocalizedEntityService _localizedEntityService; - private readonly IEventPublisher _eventPublisher; + private readonly IRepository _productBundleItemAttributeFilterRepository; + private readonly ILocalizedEntityService _localizedEntityService; private readonly IRequestCache _requestCache; - private readonly IPictureService _pictureService; public ProductAttributeService( - IRequestCache requestCache, + IRequestCache requestCache, IRepository productAttributeRepository, - IRepository productAttributeOptionRepository, - IRepository productAttributeOptionsSetRepository, - IRepository productVariantAttributeRepository, + IRepository productAttributeOptionRepository, + IRepository productAttributeOptionsSetRepository, + IRepository productVariantAttributeRepository, IRepository pvacRepository, IRepository productVariantAttributeValueRepository, - IRepository productBundleItemAttributeFilterRepository, - ILocalizedEntityService localizedEntityService, - IEventPublisher eventPublisher, - IPictureService pictureService) + IRepository productBundleItemAttributeFilterRepository, + ILocalizedEntityService localizedEntityService) { _requestCache = requestCache; _productAttributeRepository = productAttributeRepository; - _productAttributeOptionRepository = productAttributeOptionRepository; - _productAttributeOptionsSetRepository = productAttributeOptionsSetRepository; + _productAttributeOptionRepository = productAttributeOptionRepository; + _productAttributeOptionsSetRepository = productAttributeOptionsSetRepository; _productVariantAttributeRepository = productVariantAttributeRepository; _pvacRepository = pvacRepository; _productVariantAttributeValueRepository = productVariantAttributeValueRepository; - _productBundleItemAttributeFilterRepository = productBundleItemAttributeFilterRepository; - _localizedEntityService = localizedEntityService; - _eventPublisher = eventPublisher; - _pictureService = pictureService; - - T = NullLocalizer.Instance; - } - - public Localizer T { get; set; } - - private IList GetSwitchedLoadedAttributeMappings(ICollection productVariantAttributeIds) - { - if (productVariantAttributeIds != null && productVariantAttributeIds.Count > 0) - { - if (productVariantAttributeIds.Count == 1) - { - var pva = GetProductVariantAttributeById(productVariantAttributeIds.First()); - if (pva != null) - { - return new List { pva }; - } - } - else - { - return _productVariantAttributeRepository - .GetMany(productVariantAttributeIds) - .OrderBy(x => x.DisplayOrder) - .ToList(); - } - } - - return new List(); - } - - #region Product attributes - - public virtual void DeleteProductAttribute(ProductAttribute productAttribute) + _productBundleItemAttributeFilterRepository = productBundleItemAttributeFilterRepository; + _localizedEntityService = localizedEntityService; + } + + public Localizer T { get; set; } = NullLocalizer.Instance; + + private IList GetSwitchedLoadedAttributeMappings(ICollection productVariantAttributeIds) + { + if (productVariantAttributeIds != null && productVariantAttributeIds.Count > 0) + { + if (productVariantAttributeIds.Count == 1) + { + var pva = GetProductVariantAttributeById(productVariantAttributeIds.First()); + if (pva != null) + { + return new List { pva }; + } + } + else + { + return _productVariantAttributeRepository + .GetMany(productVariantAttributeIds) + .OrderBy(x => x.DisplayOrder) + .ToList(); + } + } + + return new List(); + } + + #region Product attributes + + public virtual void DeleteProductAttribute(ProductAttribute productAttribute) { if (productAttribute == null) throw new ArgumentNullException("productAttribute"); @@ -104,35 +93,36 @@ public virtual void DeleteProductAttribute(ProductAttribute productAttribute) //cache _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTES_PATTERN_KEY); - _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); + _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); } - public virtual IList GetAllProductAttributes() + public virtual IPagedList GetAllProductAttributes(int pageIndex, int pageSize, bool untracked = true) { - var query = from pa in _productAttributeRepository.Table - orderby pa.DisplayOrder, pa.Name - select pa; - var productAttributes = query.ToListCached("db.prodattrs.all"); - return productAttributes; - } + var query = + from x in untracked ? _productAttributeRepository.TableUntracked : _productAttributeRepository.Table + orderby x.DisplayOrder + select x; + + return new PagedList(query, pageIndex, pageSize); + } public virtual ProductAttribute GetProductAttributeById(int productAttributeId) { if (productAttributeId == 0) return null; - return _productAttributeRepository.GetByIdCached(productAttributeId, "db.prodattr.id-" + productAttributeId); - } + return _productAttributeRepository.GetByIdCached(productAttributeId, "db.prodattr.id-" + productAttributeId); + } public virtual void InsertProductAttribute(ProductAttribute productAttribute) { if (productAttribute == null) throw new ArgumentNullException("productAttribute"); - _productAttributeRepository.Insert(productAttribute); - + _productAttributeRepository.Insert(productAttribute); + _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTES_PATTERN_KEY); - _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); + _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); } public virtual void UpdateProductAttribute(ProductAttribute productAttribute) @@ -140,162 +130,164 @@ public virtual void UpdateProductAttribute(ProductAttribute productAttribute) if (productAttribute == null) throw new ArgumentNullException("productAttribute"); - _productAttributeRepository.Update(productAttribute); + _productAttributeRepository.Update(productAttribute); _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTES_PATTERN_KEY); - _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); + _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); } - public virtual Multimap GetExportFieldMappings(string fieldPrefix) - { - Guard.NotEmpty(fieldPrefix, nameof(fieldPrefix)); - - var result = new Multimap(StringComparer.OrdinalIgnoreCase); + public virtual Multimap GetExportFieldMappings(string fieldPrefix) + { + Guard.NotEmpty(fieldPrefix, nameof(fieldPrefix)); - if (!fieldPrefix.EndsWith(":")) - { - fieldPrefix = fieldPrefix + ":"; - } + var result = new Multimap(StringComparer.OrdinalIgnoreCase); - var mappings = _productAttributeRepository.TableUntracked - .Where(x => !string.IsNullOrEmpty(x.ExportMappings)) - .Select(x => new - { - x.Id, - x.ExportMappings - }) - .ToList(); + if (!fieldPrefix.EndsWith(":")) + { + fieldPrefix = fieldPrefix + ":"; + } - foreach (var mapping in mappings) - { - var rows = mapping.ExportMappings.SplitSafe(Environment.NewLine) - .Where(x => x.StartsWith(fieldPrefix, StringComparison.InvariantCultureIgnoreCase)); + var mappings = _productAttributeRepository.TableUntracked + .Where(x => !string.IsNullOrEmpty(x.ExportMappings)) + .Select(x => new + { + x.Id, + x.ExportMappings + }) + .ToList(); - foreach (var row in rows) - { - var exportFieldName = row.Substring(fieldPrefix.Length).TrimEnd(); - if (exportFieldName.HasValue()) - { - result.Add(exportFieldName, mapping.Id); - } - } - } + foreach (var mapping in mappings) + { + var rows = mapping.ExportMappings.SplitSafe(Environment.NewLine) + .Where(x => x.StartsWith(fieldPrefix, StringComparison.InvariantCultureIgnoreCase)); + + foreach (var row in rows) + { + var exportFieldName = row.Substring(fieldPrefix.Length).TrimEnd(); + if (exportFieldName.HasValue()) + { + result.Add(exportFieldName, mapping.Id); + } + } + } - return result; - } + return result; + } - #endregion + #endregion - #region Product attribute options + #region Product attribute options - public virtual ProductAttributeOption GetProductAttributeOptionById(int id) - { - if (id == 0) - return null; + public virtual ProductAttributeOption GetProductAttributeOptionById(int id) + { + if (id == 0) + { + return null; + } - return _productAttributeOptionRepository.GetById(id); - } + return _productAttributeOptionRepository.GetById(id); + } - public virtual IList GetProductAttributeOptionsByOptionsSetId(int optionsSetId) - { - if (optionsSetId == 0) - return new List(); + public virtual IList GetProductAttributeOptionsByOptionsSetId(int optionsSetId) + { + if (optionsSetId == 0) + return new List(); - var entities = _productAttributeOptionRepository.Table - .Where(x => x.ProductAttributeOptionsSetId == optionsSetId) - .OrderBy(x => x.DisplayOrder) - .ThenBy(x => x.Name) - .ToList(); + var entities = _productAttributeOptionRepository.Table + .Where(x => x.ProductAttributeOptionsSetId == optionsSetId) + .OrderBy(x => x.DisplayOrder) + .ThenBy(x => x.Name) + .ToList(); - return entities; - } + return entities; + } - public virtual IList GetProductAttributeOptionsByAttributeId(int attributeId) - { - if (attributeId == 0) - return new List(); + public virtual IList GetProductAttributeOptionsByAttributeId(int attributeId) + { + if (attributeId == 0) + return new List(); - var entities = - from o in _productAttributeOptionRepository.Table - join os in _productAttributeOptionsSetRepository.Table on o.ProductAttributeOptionsSetId equals os.Id - where os.ProductAttributeId == attributeId - select o; + var entities = + from o in _productAttributeOptionRepository.Table + join os in _productAttributeOptionsSetRepository.Table on o.ProductAttributeOptionsSetId equals os.Id + where os.ProductAttributeId == attributeId + select o; - return entities.ToList(); - } + return entities.ToList(); + } - public virtual void DeleteProductAttributeOption(ProductAttributeOption productAttributeOption) - { - Guard.NotNull(productAttributeOption, nameof(productAttributeOption)); + public virtual void DeleteProductAttributeOption(ProductAttributeOption productAttributeOption) + { + Guard.NotNull(productAttributeOption, nameof(productAttributeOption)); - _productAttributeOptionRepository.Delete(productAttributeOption); - } + _productAttributeOptionRepository.Delete(productAttributeOption); + } - public virtual void InsertProductAttributeOption(ProductAttributeOption productAttributeOption) - { - Guard.NotNull(productAttributeOption, nameof(productAttributeOption)); + public virtual void InsertProductAttributeOption(ProductAttributeOption productAttributeOption) + { + Guard.NotNull(productAttributeOption, nameof(productAttributeOption)); - _productAttributeOptionRepository.Insert(productAttributeOption); - } + _productAttributeOptionRepository.Insert(productAttributeOption); + } - public virtual void UpdateProductAttributeOption(ProductAttributeOption productAttributeOption) - { - Guard.NotNull(productAttributeOption, nameof(productAttributeOption)); + public virtual void UpdateProductAttributeOption(ProductAttributeOption productAttributeOption) + { + Guard.NotNull(productAttributeOption, nameof(productAttributeOption)); - _productAttributeOptionRepository.Update(productAttributeOption); - } + _productAttributeOptionRepository.Update(productAttributeOption); + } - #endregion + #endregion - #region Product attribute options sets + #region Product attribute options sets - public virtual ProductAttributeOptionsSet GetProductAttributeOptionsSetById(int id) - { - if (id == 0) - return null; + public virtual ProductAttributeOptionsSet GetProductAttributeOptionsSetById(int id) + { + if (id == 0) + return null; - return _productAttributeOptionsSetRepository.GetById(id); - } + return _productAttributeOptionsSetRepository.GetById(id); + } - public virtual IList GetProductAttributeOptionsSetsByAttributeId(int productAttributeId) - { - if (productAttributeId == 0) - return new List(); + public virtual IList GetProductAttributeOptionsSetsByAttributeId(int productAttributeId) + { + if (productAttributeId == 0) + return new List(); - var entities = _productAttributeOptionsSetRepository.Table - .Where(x => x.ProductAttributeId == productAttributeId) - .OrderBy(x => x.Name) - .ToList(); + var entities = _productAttributeOptionsSetRepository.Table + .Where(x => x.ProductAttributeId == productAttributeId) + .OrderBy(x => x.Name) + .ToList(); - return entities; - } + return entities; + } - public virtual void DeleteProductAttributeOptionsSet(ProductAttributeOptionsSet productAttributeOptionsSet) - { - Guard.NotNull(productAttributeOptionsSet, nameof(productAttributeOptionsSet)); + public virtual void DeleteProductAttributeOptionsSet(ProductAttributeOptionsSet productAttributeOptionsSet) + { + Guard.NotNull(productAttributeOptionsSet, nameof(productAttributeOptionsSet)); - _productAttributeOptionsSetRepository.Delete(productAttributeOptionsSet); - } + _productAttributeOptionsSetRepository.Delete(productAttributeOptionsSet); + } - public virtual void InsertProductAttributeOptionsSet(ProductAttributeOptionsSet productAttributeOptionsSet) - { - Guard.NotNull(productAttributeOptionsSet, nameof(productAttributeOptionsSet)); + public virtual void InsertProductAttributeOptionsSet(ProductAttributeOptionsSet productAttributeOptionsSet) + { + Guard.NotNull(productAttributeOptionsSet, nameof(productAttributeOptionsSet)); - _productAttributeOptionsSetRepository.Insert(productAttributeOptionsSet); - } + _productAttributeOptionsSetRepository.Insert(productAttributeOptionsSet); + } - public virtual void UpdateProductAttributeOptionsSet(ProductAttributeOptionsSet productAttributeOptionsSet) - { - Guard.NotNull(productAttributeOptionsSet, nameof(productAttributeOptionsSet)); + public virtual void UpdateProductAttributeOptionsSet(ProductAttributeOptionsSet productAttributeOptionsSet) + { + Guard.NotNull(productAttributeOptionsSet, nameof(productAttributeOptionsSet)); - _productAttributeOptionsSetRepository.Update(productAttributeOptionsSet); - } + _productAttributeOptionsSetRepository.Update(productAttributeOptionsSet); + } - #endregion + #endregion - #region Product variant attributes mappings (ProductVariantAttribute) + #region Product variant attributes mappings (ProductVariantAttribute) - public virtual void DeleteProductVariantAttribute(ProductVariantAttribute productVariantAttribute) + public virtual void DeleteProductVariantAttribute(ProductVariantAttribute productVariantAttribute) { if (productVariantAttribute == null) throw new ArgumentNullException("productVariantAttribute"); @@ -303,89 +295,89 @@ public virtual void DeleteProductVariantAttribute(ProductVariantAttribute produc _productVariantAttributeRepository.Delete(productVariantAttribute); _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTES_PATTERN_KEY); - _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); + _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); } - public virtual IList GetProductVariantAttributesByProductId(int productId) + public virtual IList GetProductVariantAttributesByProductId(int productId) { - var query = from pva in _productVariantAttributeRepository.Table.Expand(x => x.ProductAttribute) - orderby pva.DisplayOrder - where pva.ProductId == productId - select pva; + var query = from pva in _productVariantAttributeRepository.Table.Expand(x => x.ProductAttribute) + orderby pva.DisplayOrder + where pva.ProductId == productId + select pva; - var productVariantAttributes = query.ToListCached("db.prodvarattrs.all-" + productId); - return productVariantAttributes; - } + var productVariantAttributes = query.ToListCached("db.prodvarattrs.all-" + productId); + return productVariantAttributes; + } - public virtual Multimap GetProductVariantAttributesByProductIds(int[] productIds, AttributeControlType? controlType) - { - Guard.NotNull(productIds, nameof(productIds)); + public virtual Multimap GetProductVariantAttributesByProductIds(int[] productIds, AttributeControlType? controlType) + { + Guard.NotNull(productIds, nameof(productIds)); if (!productIds.Any()) { return new Multimap(); } - var query = - from pva in _productVariantAttributeRepository.TableUntracked.Expand(x => x.ProductAttribute).Expand(x => x.ProductVariantAttributeValues) - where productIds.Contains(pva.ProductId) - select pva; + var query = + from pva in _productVariantAttributeRepository.TableUntracked.Expand(x => x.ProductAttribute).Expand(x => x.ProductVariantAttributeValues) + where productIds.Contains(pva.ProductId) + select pva; - if (controlType.HasValue) - { - query = query.Where(x => x.AttributeControlTypeId == ((int)controlType.Value)); - } + if (controlType.HasValue) + { + query = query.Where(x => x.AttributeControlTypeId == ((int)controlType.Value)); + } - var map = query - .OrderBy(x => x.ProductId) - .ThenBy(x => x.DisplayOrder) - .ToList() - .ToMultimap(x => x.ProductId, x => x); + var map = query + .OrderBy(x => x.ProductId) + .ThenBy(x => x.DisplayOrder) + .ToList() + .ToMultimap(x => x.ProductId, x => x); - return map; - } + return map; + } public virtual ProductVariantAttribute GetProductVariantAttributeById(int productVariantAttributeId) { if (productVariantAttributeId == 0) return null; - return _productVariantAttributeRepository.GetByIdCached(productVariantAttributeId, "db.prodvarattr.id-" + productVariantAttributeId); - } - - public virtual IList GetProductVariantAttributesByIds(IEnumerable productVariantAttributeIds, IEnumerable attributes = null) - { - if (productVariantAttributeIds != null) - { - if (attributes != null) - { - var ids = new List(); - var result = new List(); - - foreach (var id in productVariantAttributeIds) - { - var pva = attributes.FirstOrDefault(x => x.Id == id); - if (pva == null) - ids.Add(id); - else - result.Add(pva); - } - - if (ids.Count > 0) - { - var newLoadedMappings = GetSwitchedLoadedAttributeMappings(ids); - result.AddRange(newLoadedMappings); - } - - // sort by passed identifier sequence - return result.OrderBySequence(productVariantAttributeIds).ToList(); - } - - return GetSwitchedLoadedAttributeMappings(productVariantAttributeIds.ToList()); - } - - return new List(); - } + return _productVariantAttributeRepository.GetByIdCached(productVariantAttributeId, "db.prodvarattr.id-" + productVariantAttributeId); + } + + public virtual IList GetProductVariantAttributesByIds(IEnumerable productVariantAttributeIds, IEnumerable attributes = null) + { + if (productVariantAttributeIds != null) + { + if (attributes != null) + { + var ids = new List(); + var result = new List(); + + foreach (var id in productVariantAttributeIds) + { + var pva = attributes.FirstOrDefault(x => x.Id == id); + if (pva == null) + ids.Add(id); + else + result.Add(pva); + } + + if (ids.Count > 0) + { + var newLoadedMappings = GetSwitchedLoadedAttributeMappings(ids); + result.AddRange(newLoadedMappings); + } + + // sort by passed identifier sequence + return result.OrderBySequence(productVariantAttributeIds).ToList(); + } + + return GetSwitchedLoadedAttributeMappings(productVariantAttributeIds.ToList()); + } + + return new List(); + } public virtual IEnumerable GetProductVariantAttributeValuesByIds(params int[] productVariantAttributeValueIds) { @@ -394,39 +386,39 @@ public virtual IEnumerable GetProductVariantAttrib return Enumerable.Empty(); } - Array.Sort(productVariantAttributeValueIds); - - var key = PRODUCTVARIANTATTRIBUTEVALUES_BY_IDS_KEY.FormatInvariant(string.Join("-", productVariantAttributeValueIds)); - return _requestCache.Get(key, () => - { - var validTypeIds = new[] - { - (int)AttributeControlType.DropdownList, - (int)AttributeControlType.RadioList, - (int)AttributeControlType.Checkboxes, - (int)AttributeControlType.Boxes - }; + Array.Sort(productVariantAttributeValueIds); - var query = from x in _productVariantAttributeValueRepository.Table - let attr = x.ProductVariantAttribute - where productVariantAttributeValueIds.Contains(x.Id) && validTypeIds.Contains(attr.AttributeControlTypeId) - orderby x.ProductVariantAttribute.DisplayOrder, x.DisplayOrder - select x; - - query = query.Include(y => y.ProductVariantAttribute.ProductAttribute); - - return query.ToList(); - }); - } + var key = PRODUCTVARIANTATTRIBUTEVALUES_BY_IDS_KEY.FormatInvariant(string.Join("-", productVariantAttributeValueIds)); + return _requestCache.Get(key, () => + { + var validTypeIds = new[] + { + (int)AttributeControlType.DropdownList, + (int)AttributeControlType.RadioList, + (int)AttributeControlType.Checkboxes, + (int)AttributeControlType.Boxes + }; + + var query = from x in _productVariantAttributeValueRepository.Table + let attr = x.ProductVariantAttribute + where productVariantAttributeValueIds.Contains(x.Id) && validTypeIds.Contains(attr.AttributeControlTypeId) + orderby x.ProductVariantAttribute.DisplayOrder, x.DisplayOrder + select x; + + query = query.Include(y => y.ProductVariantAttribute.ProductAttribute); + + return query.ToList(); + }); + } public virtual void InsertProductVariantAttribute(ProductVariantAttribute productVariantAttribute) { - Guard.NotNull(productVariantAttribute, nameof(productVariantAttribute)); + Guard.NotNull(productVariantAttribute, nameof(productVariantAttribute)); - _productVariantAttributeRepository.Insert(productVariantAttribute); + _productVariantAttributeRepository.Insert(productVariantAttribute); - _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTES_PATTERN_KEY); - _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); + _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTES_PATTERN_KEY); + _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); } public virtual void UpdateProductVariantAttribute(ProductVariantAttribute productVariantAttribute) @@ -434,104 +426,86 @@ public virtual void UpdateProductVariantAttribute(ProductVariantAttribute produc if (productVariantAttribute == null) throw new ArgumentNullException("productVariantAttribute"); - _productVariantAttributeRepository.Update(productVariantAttribute); + _productVariantAttributeRepository.Update(productVariantAttribute); _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTES_PATTERN_KEY); - _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); - } - - public virtual int CopyAttributeOptions(ProductVariantAttribute productVariantAttribute, int productAttributeOptionsSetId, bool deleteExistingValues) - { - Guard.NotNull(productVariantAttribute, nameof(productVariantAttribute)); - Guard.NotZero(productVariantAttribute.Id, nameof(productVariantAttribute.Id)); - Guard.NotZero(productAttributeOptionsSetId, nameof(productAttributeOptionsSetId)); - - if (deleteExistingValues) - { - var existingValues = productVariantAttribute.ProductVariantAttributeValues.ToList(); - if (!existingValues.Any()) - existingValues = GetProductVariantAttributeValues(productVariantAttribute.Id).ToList(); - - existingValues.Each(x => DeleteProductVariantAttributeValue(x)); - } - - var result = 0; - var attributeOptions = _productAttributeOptionRepository.TableUntracked - .Where(x => x.ProductAttributeOptionsSetId == productAttributeOptionsSetId) - .ToList(); - - if (!attributeOptions.Any()) - return result; - - // Do not insert already existing values (identified by name field). - var existingValueNames = new HashSet(_productVariantAttributeValueRepository.TableUntracked - .Where(x => x.ProductVariantAttributeId == productVariantAttribute.Id) - .Select(x => x.Name) - .ToList()); - - Picture picture = null; - ProductVariantAttributeValue productVariantAttributeValue = null; - var pictureIds = attributeOptions.Where(x => x.PictureId != 0).Select(x => x.PictureId).Distinct().ToArray(); - var pictures = _pictureService.GetPicturesByIds(pictureIds, true).ToDictionarySafe(x => x.Id); - - using (_localizedEntityService.BeginScope()) - { - foreach (var option in attributeOptions) - { - if (existingValueNames.Contains(option.Name)) - continue; - - productVariantAttributeValue = option.Clone(); - productVariantAttributeValue.PictureId = 0; - productVariantAttributeValue.ProductVariantAttributeId = productVariantAttribute.Id; - - // Copy picture. - if (option.PictureId != 0 && pictures.TryGetValue(option.PictureId, out picture)) - { - var pictureBinary = _pictureService.LoadPictureBinary(picture); - - var newPicture = _pictureService.InsertPicture( - pictureBinary, - picture.MimeType, - picture.SeoFilename, - picture.IsNew, - picture.Width ?? 0, - picture.Height ?? 0, - picture.IsTransient - ); - - productVariantAttributeValue.PictureId = newPicture.Id; - } - - // No scope commit, we need new entity id. - _productVariantAttributeValueRepository.Insert(productVariantAttributeValue); - ++result; - - // Copy localized properties too. - var optionProperties = _localizedEntityService.GetLocalizedProperties(option.Id, "ProductAttributeOption"); - - foreach (var property in optionProperties) - { - _localizedEntityService.InsertLocalizedProperty(new LocalizedProperty - { - EntityId = productVariantAttributeValue.Id, - LocaleKeyGroup = "ProductVariantAttributeValue", - LocaleKey = property.LocaleKey, - LocaleValue = property.LocaleValue, - LanguageId = property.LanguageId - }); - } - } - } - - return result; - } - - #endregion - - #region Product variant attribute values (ProductVariantAttributeValue) - - public virtual void DeleteProductVariantAttributeValue(ProductVariantAttributeValue productVariantAttributeValue) + _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); + } + + public virtual int CopyAttributeOptions(ProductVariantAttribute productVariantAttribute, int productAttributeOptionsSetId, bool deleteExistingValues) + { + Guard.NotNull(productVariantAttribute, nameof(productVariantAttribute)); + Guard.NotZero(productVariantAttribute.Id, nameof(productVariantAttribute.Id)); + Guard.NotZero(productAttributeOptionsSetId, nameof(productAttributeOptionsSetId)); + + if (deleteExistingValues) + { + var existingValues = productVariantAttribute.ProductVariantAttributeValues.ToList(); + if (!existingValues.Any()) + { + existingValues = GetProductVariantAttributeValues(productVariantAttribute.Id).ToList(); + } + + existingValues.Each(x => DeleteProductVariantAttributeValue(x)); + } + + var result = 0; + var attributeOptions = _productAttributeOptionRepository.TableUntracked + .Where(x => x.ProductAttributeOptionsSetId == productAttributeOptionsSetId) + .ToList(); + + if (!attributeOptions.Any()) + { + return result; + } + + // Do not insert already existing values (identified by name field). + var existingValueNames = new HashSet(_productVariantAttributeValueRepository.TableUntracked + .Where(x => x.ProductVariantAttributeId == productVariantAttribute.Id) + .Select(x => x.Name) + .ToList()); + + using (_localizedEntityService.BeginScope()) + { + foreach (var option in attributeOptions) + { + if (existingValueNames.Contains(option.Name)) + { + continue; + } + + var pvav = option.Clone(); + pvav.ProductVariantAttributeId = productVariantAttribute.Id; + + // No scope commit, we need new entity id. + _productVariantAttributeValueRepository.Insert(pvav); + ++result; + + // Copy localized properties too. + var optionProperties = _localizedEntityService.GetLocalizedProperties(option.Id, "ProductAttributeOption"); + + foreach (var property in optionProperties) + { + _localizedEntityService.InsertLocalizedProperty(new LocalizedProperty + { + EntityId = pvav.Id, + LocaleKeyGroup = "ProductVariantAttributeValue", + LocaleKey = property.LocaleKey, + LocaleValue = property.LocaleValue, + LanguageId = property.LanguageId + }); + } + } + } + + return result; + } + + #endregion + + #region Product variant attribute values (ProductVariantAttributeValue) + + public virtual void DeleteProductVariantAttributeValue(ProductVariantAttributeValue productVariantAttributeValue) { if (productVariantAttributeValue == null) throw new ArgumentNullException("productVariantAttributeValue"); @@ -539,37 +513,37 @@ public virtual void DeleteProductVariantAttributeValue(ProductVariantAttributeVa _productVariantAttributeValueRepository.Delete(productVariantAttributeValue); _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTES_PATTERN_KEY); - _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); + _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); } public virtual IList GetProductVariantAttributeValues(int productVariantAttributeId) { - var query = from pvav in _productVariantAttributeValueRepository.Table - orderby pvav.DisplayOrder - where pvav.ProductVariantAttributeId == productVariantAttributeId - select pvav; + var query = from pvav in _productVariantAttributeValueRepository.Table + orderby pvav.DisplayOrder + where pvav.ProductVariantAttributeId == productVariantAttributeId + select pvav; - var productVariantAttributeValues = query.ToListCached("db.prodvarattrvalues.all-" + productVariantAttributeId); - return productVariantAttributeValues; - } + var productVariantAttributeValues = query.ToListCached("db.prodvarattrvalues.all-" + productVariantAttributeId); + return productVariantAttributeValues; + } public virtual ProductVariantAttributeValue GetProductVariantAttributeValueById(int productVariantAttributeValueId) { if (productVariantAttributeValueId == 0) return null; - return _productVariantAttributeValueRepository.GetByIdCached(productVariantAttributeValueId, "db.prodvarattrval.id-" + productVariantAttributeValueId); - } + return _productVariantAttributeValueRepository.GetByIdCached(productVariantAttributeValueId, "db.prodvarattrval.id-" + productVariantAttributeValueId); + } public virtual void InsertProductVariantAttributeValue(ProductVariantAttributeValue productVariantAttributeValue) { if (productVariantAttributeValue == null) throw new ArgumentNullException("productVariantAttributeValue"); - _productVariantAttributeValueRepository.Insert(productVariantAttributeValue); + _productVariantAttributeValueRepository.Insert(productVariantAttributeValue); _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTES_PATTERN_KEY); - _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); + _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); } public virtual void UpdateProductVariantAttributeValue(ProductVariantAttributeValue productVariantAttributeValue) @@ -577,31 +551,31 @@ public virtual void UpdateProductVariantAttributeValue(ProductVariantAttributeVa if (productVariantAttributeValue == null) throw new ArgumentNullException("productVariantAttributeValue"); - _productVariantAttributeValueRepository.Update(productVariantAttributeValue); + _productVariantAttributeValueRepository.Update(productVariantAttributeValue); _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTES_PATTERN_KEY); - _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); + _requestCache.RemoveByPattern(PRODUCTVARIANTATTRIBUTEVALUES_PATTERN_KEY); } #endregion #region Product variant attribute combinations (ProductVariantAttributeCombination) - - private void CombineAll(List> toCombine, List> result, int y, List tmp) - { - var combine = toCombine[y]; - - for (int i = 0; i < combine.Count; ++i) - { - var lst = new List(tmp); - lst.Add(combine[i]); - - if (y == (toCombine.Count - 1)) - result.Add(lst); - else - CombineAll(toCombine, result, y + 1, lst); - } - } + + private void CombineAll(List> toCombine, List> result, int y, List tmp) + { + var combine = toCombine[y]; + + for (int i = 0; i < combine.Count; ++i) + { + var lst = new List(tmp); + lst.Add(combine[i]); + + if (y == (toCombine.Count - 1)) + result.Add(lst); + else + CombineAll(toCombine, result, y + 1, lst); + } + } public virtual void DeleteProductVariantAttributeCombination(ProductVariantAttributeCombination combination) { @@ -611,116 +585,116 @@ public virtual void DeleteProductVariantAttributeCombination(ProductVariantAttri _pvacRepository.Delete(combination); } - public virtual IPagedList GetAllProductVariantAttributeCombinations( - int productId, - int pageIndex, - int pageSize, - bool untracked = true) - { - if (productId == 0) - { - return new PagedList(new List(), pageIndex, pageSize); - } - - string key = string.Format(PRODUCTVARIANTATTRIBUTES_COMBINATIONS_BY_ID_KEY, productId, 0, int.MaxValue); - return _requestCache.Get(key, () => - { - var query = from pvac in (untracked ? _pvacRepository.TableUntracked : _pvacRepository.Table) - orderby pvac.Id - where pvac.ProductId == productId - select pvac; - - var combinations = new PagedList(query, pageIndex, pageSize); - return combinations; - }); - } - - public virtual IList GetAllProductVariantAttributeCombinationPictureIds(int productId) - { - var pictureIds = new List(); - - if (productId == 0) - return pictureIds; - - var query = from pvac in _pvacRepository.TableUntracked - where - pvac.ProductId == productId - && pvac.IsActive - && !String.IsNullOrEmpty(pvac.AssignedPictureIds) - select pvac.AssignedPictureIds; - - var data = query.ToList(); - if (data.Any()) - { - int id; - var ids = string.Join(",", data).SplitSafe(",").Distinct(); - - foreach (string str in ids) - { - if (int.TryParse(str, out id) && !pictureIds.Exists(i => i == id)) - pictureIds.Add(id); - } - } - - return pictureIds; - } - - public virtual Multimap GetProductVariantAttributeCombinations(int[] productIds) - { - Guard.NotNull(productIds, nameof(productIds)); + public virtual IPagedList GetAllProductVariantAttributeCombinations( + int productId, + int pageIndex, + int pageSize, + bool untracked = true) + { + if (productId == 0) + { + return new PagedList(new List(), pageIndex, pageSize); + } + + string key = string.Format(PRODUCTVARIANTATTRIBUTES_COMBINATIONS_BY_ID_KEY, productId, 0, int.MaxValue); + return _requestCache.Get(key, () => + { + var query = from pvac in (untracked ? _pvacRepository.TableUntracked : _pvacRepository.Table) + orderby pvac.Id + where pvac.ProductId == productId + select pvac; + + var combinations = new PagedList(query, pageIndex, pageSize); + return combinations; + }); + } + + public virtual IList GetAllProductVariantAttributeCombinationPictureIds(int productId) + { + var pictureIds = new List(); + + if (productId == 0) + return pictureIds; + + var query = from pvac in _pvacRepository.TableUntracked + where + pvac.ProductId == productId + && pvac.IsActive + && !String.IsNullOrEmpty(pvac.AssignedMediaFileIds) + select pvac.AssignedMediaFileIds; + + var data = query.ToList(); + if (data.Any()) + { + int id; + var ids = string.Join(",", data).SplitSafe(",").Distinct(); + + foreach (string str in ids) + { + if (int.TryParse(str, out id) && !pictureIds.Exists(i => i == id)) + pictureIds.Add(id); + } + } + + return pictureIds; + } + + public virtual Multimap GetProductVariantAttributeCombinations(int[] productIds) + { + Guard.NotNull(productIds, nameof(productIds)); if (!productIds.Any()) { return new Multimap(); } - var query = - from pvac in _pvacRepository.TableUntracked - where productIds.Contains(pvac.ProductId) - select pvac; + var query = + from pvac in _pvacRepository.TableUntracked + where productIds.Contains(pvac.ProductId) + select pvac; - var map = query - .OrderBy(x => x.ProductId) - .ToList() - .ToMultimap(x => x.ProductId, x => x); + var map = query + .OrderBy(x => x.ProductId) + .ToList() + .ToMultimap(x => x.ProductId, x => x); - return map; - } + return map; + } - public virtual decimal? GetLowestCombinationPrice(int productId) - { - if (productId == 0) - return null; + public virtual decimal? GetLowestCombinationPrice(int productId) + { + if (productId == 0) + return null; - var query = - from pvac in _pvacRepository.Table - where pvac.ProductId == productId && pvac.Price != null && pvac.IsActive - orderby pvac.Price ascending - select pvac.Price; + var query = + from pvac in _pvacRepository.Table + where pvac.ProductId == productId && pvac.Price != null && pvac.IsActive + orderby pvac.Price ascending + select pvac.Price; - var price = query.FirstOrDefault(); - return price; - } + var price = query.FirstOrDefault(); + return price; + } public virtual ProductVariantAttributeCombination GetProductVariantAttributeCombinationById(int productVariantAttributeCombinationId) { if (productVariantAttributeCombinationId == 0) return null; - + var combination = _pvacRepository.GetById(productVariantAttributeCombinationId); return combination; } - public virtual ProductVariantAttributeCombination GetProductVariantAttributeCombinationBySku(string sku) - { + public virtual ProductVariantAttributeCombination GetProductVariantAttributeCombinationBySku(string sku) + { if (sku.IsEmpty()) { return null; } - var combination = _pvacRepository.Table.FirstOrDefault(x => x.Sku == sku && !x.Product.Deleted); - return combination; - } + var combination = _pvacRepository.Table.FirstOrDefault(x => x.Sku == sku && !x.Product.Deleted); + return combination; + } public virtual ProductVariantAttributeCombination GetAttributeCombinationByGtin(string gtin) { @@ -758,180 +732,180 @@ public virtual void UpdateProductVariantAttributeCombination(ProductVariantAttri if (combination == null) throw new ArgumentNullException("combination"); - //if (combination.IsDefaultCombination) - //{ - // EnsureSingleDefaultVariant(combination); - //} - //else - //{ - // // check if it was default before modification... - // // but make it Type-Safe (resistant to code refactoring ;-)) - // Expression> expr = x => x.IsDefaultCombination; - // string propertyToCheck = expr.ExtractPropertyInfo().Name; - - // object originalValue = null; - // if (_productVariantAttributeCombinationRepository.GetModifiedProperties(combination).TryGetValue(propertyToCheck, out originalValue)) - // { - // bool wasDefault = (bool)originalValue; - // if (wasDefault) - // { - // // we can't uncheck the default variant within a combination list, - // // we would't have a default combination anymore. - // combination.IsDefaultCombination = true; - // } - // } - //} + //if (combination.IsDefaultCombination) + //{ + // EnsureSingleDefaultVariant(combination); + //} + //else + //{ + // // check if it was default before modification... + // // but make it Type-Safe (resistant to code refactoring ;-)) + // Expression> expr = x => x.IsDefaultCombination; + // string propertyToCheck = expr.ExtractPropertyInfo().Name; + + // object originalValue = null; + // if (_productVariantAttributeCombinationRepository.GetModifiedProperties(combination).TryGetValue(propertyToCheck, out originalValue)) + // { + // bool wasDefault = (bool)originalValue; + // if (wasDefault) + // { + // // we can't uncheck the default variant within a combination list, + // // we would't have a default combination anymore. + // combination.IsDefaultCombination = true; + // } + // } + //} _pvacRepository.Update(combination); } - public virtual void CreateAllProductVariantAttributeCombinations(Product product) - { - // Delete all existing combinations. - _pvacRepository.DeleteAll(x => x.ProductId == product.Id); - - var attributes = GetProductVariantAttributesByProductId(product.Id); - if (attributes == null || attributes.Count <= 0) - return; - - var mappedAttributes = attributes - .SelectMany(x => x.ProductVariantAttributeValues) - .ToDictionarySafe(x => x.Id, x => x.ProductVariantAttribute); - - var toCombine = new List>(); - var resultMatrix = new List>(); - var tmp = new List(); - - foreach (var attr in attributes) - { - var attributeValues = attr.ProductVariantAttributeValues.ToList(); - if (attributeValues.Count > 0) - toCombine.Add(attributeValues); - } - - if (toCombine.Count > 0) - { - CombineAll(toCombine, resultMatrix, 0, tmp); - - using (var scope = new DbContextScope(ctx: _pvacRepository.Context, autoCommit: false, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false)) - { - ProductVariantAttributeCombination combination = null; - var idx = 0; - - foreach (var values in resultMatrix) - { - idx++; - var attributesXml = ""; - - foreach (var value in values) - { - attributesXml = mappedAttributes[value.Id].AddProductAttribute(attributesXml, value.Id.ToString()); - } - - combination = new ProductVariantAttributeCombination - { - ProductId = product.Id, - AttributesXml = attributesXml, - StockQuantity = 10000, - AllowOutOfStockOrders = true, - IsActive = true - }; - - _pvacRepository.Insert(combination); - } - - scope.Commit(); - } - } - - //foreach (var y in resultMatrix) - //{ - // var sb = new System.Text.StringBuilder(); - // foreach (var x in y) - // { - // sb.AppendFormat("{0} ", x.Name); - // } - // sb.ToString().Dump(); - //} - } - - public virtual bool VariantHasAttributeCombinations(int productId) - { - if (productId == 0) - return false; - - var query = - from c in _pvacRepository.Table - where c.ProductId == productId - select c; - - return query.Select(x => x.Id).Any(); - } + public virtual void CreateAllProductVariantAttributeCombinations(Product product) + { + // Delete all existing combinations. + _pvacRepository.DeleteAll(x => x.ProductId == product.Id); + + var attributes = GetProductVariantAttributesByProductId(product.Id); + if (attributes == null || attributes.Count <= 0) + return; + + var mappedAttributes = attributes + .SelectMany(x => x.ProductVariantAttributeValues) + .ToDictionarySafe(x => x.Id, x => x.ProductVariantAttribute); + + var toCombine = new List>(); + var resultMatrix = new List>(); + var tmp = new List(); + + foreach (var attr in attributes) + { + var attributeValues = attr.ProductVariantAttributeValues.ToList(); + if (attributeValues.Count > 0) + toCombine.Add(attributeValues); + } + + if (toCombine.Count > 0) + { + CombineAll(toCombine, resultMatrix, 0, tmp); + + using (var scope = new DbContextScope(ctx: _pvacRepository.Context, autoCommit: false, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false)) + { + ProductVariantAttributeCombination combination = null; + var idx = 0; + + foreach (var values in resultMatrix) + { + idx++; + var attributesXml = ""; + + foreach (var value in values) + { + attributesXml = mappedAttributes[value.Id].AddProductAttribute(attributesXml, value.Id.ToString()); + } + + combination = new ProductVariantAttributeCombination + { + ProductId = product.Id, + AttributesXml = attributesXml, + StockQuantity = 10000, + AllowOutOfStockOrders = true, + IsActive = true + }; + + _pvacRepository.Insert(combination); + } + + scope.Commit(); + } + } + + //foreach (var y in resultMatrix) + //{ + // var sb = new System.Text.StringBuilder(); + // foreach (var x in y) + // { + // sb.AppendFormat("{0} ", x.Name); + // } + // sb.ToString().Dump(); + //} + } + + public virtual bool VariantHasAttributeCombinations(int productId) + { + if (productId == 0) + return false; + + var query = + from c in _pvacRepository.Table + where c.ProductId == productId + select c; + + return query.Select(x => x.Id).Any(); + } #endregion - #region Product bundle item attribute filter - - public virtual void InsertProductBundleItemAttributeFilter(ProductBundleItemAttributeFilter attributeFilter) - { - if (attributeFilter == null) - throw new ArgumentNullException("attributeFilter"); - - if (attributeFilter.AttributeId != 0 && attributeFilter.AttributeValueId != 0) - { - _productBundleItemAttributeFilterRepository.Insert(attributeFilter); - } - } - - public virtual void UpdateProductBundleItemAttributeFilter(ProductBundleItemAttributeFilter attributeFilter) - { - if (attributeFilter == null) - throw new ArgumentNullException("attributeFilter"); - - _productBundleItemAttributeFilterRepository.Update(attributeFilter); - } - - public virtual void DeleteProductBundleItemAttributeFilter(ProductBundleItemAttributeFilter attributeFilter) - { - if (attributeFilter == null) - throw new ArgumentNullException("attributeFilter"); - - _productBundleItemAttributeFilterRepository.Delete(attributeFilter); - } - - public virtual void DeleteProductBundleItemAttributeFilter(ProductBundleItem bundleItem) - { - if (bundleItem != null && bundleItem.Id != 0) - { - var attributeFilterQuery = - from x in _productBundleItemAttributeFilterRepository.Table - where x.BundleItemId == bundleItem.Id - select x; - - attributeFilterQuery.ToList().Each(x => DeleteProductBundleItemAttributeFilter(x)); - } - } - - public virtual void DeleteProductBundleItemAttributeFilter(int attributeId, int attributeValueId) - { - var attributeFilterQuery = - from x in _productBundleItemAttributeFilterRepository.Table - where x.AttributeId == attributeId && x.AttributeValueId == attributeValueId - select x; - - attributeFilterQuery.ToList().Each(x => DeleteProductBundleItemAttributeFilter(x)); - } - - public virtual void DeleteProductBundleItemAttributeFilter(int attributeId) - { - var attributeFilterQuery = - from x in _productBundleItemAttributeFilterRepository.Table - where x.AttributeId == attributeId - select x; - - attributeFilterQuery.ToList().Each(x => DeleteProductBundleItemAttributeFilter(x)); - } - - #endregion - } + #region Product bundle item attribute filter + + public virtual void InsertProductBundleItemAttributeFilter(ProductBundleItemAttributeFilter attributeFilter) + { + if (attributeFilter == null) + throw new ArgumentNullException("attributeFilter"); + + if (attributeFilter.AttributeId != 0 && attributeFilter.AttributeValueId != 0) + { + _productBundleItemAttributeFilterRepository.Insert(attributeFilter); + } + } + + public virtual void UpdateProductBundleItemAttributeFilter(ProductBundleItemAttributeFilter attributeFilter) + { + if (attributeFilter == null) + throw new ArgumentNullException("attributeFilter"); + + _productBundleItemAttributeFilterRepository.Update(attributeFilter); + } + + public virtual void DeleteProductBundleItemAttributeFilter(ProductBundleItemAttributeFilter attributeFilter) + { + if (attributeFilter == null) + throw new ArgumentNullException("attributeFilter"); + + _productBundleItemAttributeFilterRepository.Delete(attributeFilter); + } + + public virtual void DeleteProductBundleItemAttributeFilter(ProductBundleItem bundleItem) + { + if (bundleItem != null && bundleItem.Id != 0) + { + var attributeFilterQuery = + from x in _productBundleItemAttributeFilterRepository.Table + where x.BundleItemId == bundleItem.Id + select x; + + attributeFilterQuery.ToList().Each(x => DeleteProductBundleItemAttributeFilter(x)); + } + } + + public virtual void DeleteProductBundleItemAttributeFilter(int attributeId, int attributeValueId) + { + var attributeFilterQuery = + from x in _productBundleItemAttributeFilterRepository.Table + where x.AttributeId == attributeId && x.AttributeValueId == attributeValueId + select x; + + attributeFilterQuery.ToList().Each(x => DeleteProductBundleItemAttributeFilter(x)); + } + + public virtual void DeleteProductBundleItemAttributeFilter(int attributeId) + { + var attributeFilterQuery = + from x in _productBundleItemAttributeFilterRepository.Table + where x.AttributeId == attributeId + select x; + + attributeFilterQuery.ToList().Each(x => DeleteProductBundleItemAttributeFilter(x)); + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductExtensions.cs b/src/Libraries/SmartStore.Services/Catalog/ProductExtensions.cs index 679dddfea8..8ecf7912c0 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductExtensions.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Directory; @@ -14,20 +15,21 @@ namespace SmartStore.Services.Catalog { public static class ProductExtensions { - public static ProductVariantAttributeCombination MergeWithCombination(this Product product, string selectedAttributes) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ProductVariantAttributeCombination MergeWithCombination(this Product product, string selectedAttributes) { return product.MergeWithCombination(selectedAttributes, EngineContext.Current.Resolve()); } - public static ProductVariantAttributeCombination MergeWithCombination(this Product product, string selectedAttributes, IProductAttributeParser productAttributeParser) + public static ProductVariantAttributeCombination MergeWithCombination(this Product product, string selectedAttributes, IProductAttributeParser productAttributeParser) { Guard.NotNull(productAttributeParser, "productAttributeParser"); - if (selectedAttributes.IsEmpty()) - return null; + if (selectedAttributes.IsEmpty()) + return null; - // Let's find appropriate record. - var combination = productAttributeParser.FindProductVariantAttributeCombination(product.Id, selectedAttributes); + // Let's find appropriate record. + var combination = productAttributeParser.FindProductVariantAttributeCombination(product.Id, selectedAttributes); if (combination != null && combination.IsActive) { @@ -38,93 +40,93 @@ public static ProductVariantAttributeCombination MergeWithCombination(this Produ product.MergedDataValues.Clear(); } - return combination; + return combination; } - public static void MergeWithCombination(this Product product, ProductVariantAttributeCombination combination) - { - Guard.NotNull(product, "product"); - - var values = product.MergedDataValues; - - if (values != null) - values.Clear(); - - if (combination == null) - return; - - if (values == null) - product.MergedDataValues = values = new Dictionary(); - - if (ManageInventoryMethod.ManageStockByAttributes == (ManageInventoryMethod)product.ManageInventoryMethodId) - { - values.Add("StockQuantity", combination.StockQuantity); - values.Add("BackorderModeId", combination.AllowOutOfStockOrders ? (int)BackorderMode.AllowQtyBelow0 : (int)BackorderMode.NoBackorders); - } - - if (combination.Sku.HasValue()) - values.Add("Sku", combination.Sku); - if (combination.Gtin.HasValue()) - values.Add("Gtin", combination.Gtin); - if (combination.ManufacturerPartNumber.HasValue()) - values.Add("ManufacturerPartNumber", combination.ManufacturerPartNumber); - - if (combination.Price.HasValue) - values.Add("Price", combination.Price.Value); - - if (combination.DeliveryTimeId.HasValue && combination.DeliveryTimeId.Value > 0) - values.Add("DeliveryTimeId", combination.DeliveryTimeId); - - if (combination.QuantityUnitId.HasValue && combination.QuantityUnitId.Value > 0) - values.Add("QuantityUnitId", combination.QuantityUnitId); - - if (combination.Length.HasValue) - values.Add("Length", combination.Length.Value); - if (combination.Width.HasValue) - values.Add("Width", combination.Width.Value); - if (combination.Height.HasValue) - values.Add("Height", combination.Height.Value); - - if (combination.BasePriceAmount.HasValue) - values.Add("BasePriceAmount", combination.BasePriceAmount); - if (combination.BasePriceBaseAmount.HasValue) - values.Add("BasePriceBaseAmount", combination.BasePriceBaseAmount); - } - - public static IList GetAllCombinationPictureIds(this IEnumerable combinations) - { - var pictureIds = new List(); - - if (combinations != null) - { - var data = combinations - .Where(x => x.IsActive && x.AssignedPictureIds != null) - .Select(x => x.AssignedPictureIds) - .ToList(); - - if (data.Count > 0) - { - var ids = string.Join(",", data).SplitSafe(",").Distinct(); - - foreach (string str in ids) - { - if (int.TryParse(str, out var id) && !pictureIds.Exists(i => i == id)) - pictureIds.Add(id); - } - } - } - - return pictureIds; - } - - /// - /// Finds a related product item by specified identifiers - /// - /// Source - /// The first product identifier - /// The second product identifier - /// Related product - public static RelatedProduct FindRelatedProduct(this IList source, + public static void MergeWithCombination(this Product product, ProductVariantAttributeCombination combination) + { + Guard.NotNull(product, "product"); + + var values = product.MergedDataValues; + + if (values != null) + values.Clear(); + + if (combination == null) + return; + + if (values == null) + product.MergedDataValues = values = new Dictionary(); + + if (ManageInventoryMethod.ManageStockByAttributes == (ManageInventoryMethod)product.ManageInventoryMethodId) + { + values.Add("StockQuantity", combination.StockQuantity); + values.Add("BackorderModeId", combination.AllowOutOfStockOrders ? (int)BackorderMode.AllowQtyBelow0 : (int)BackorderMode.NoBackorders); + } + + if (combination.Sku.HasValue()) + values.Add("Sku", combination.Sku); + if (combination.Gtin.HasValue()) + values.Add("Gtin", combination.Gtin); + if (combination.ManufacturerPartNumber.HasValue()) + values.Add("ManufacturerPartNumber", combination.ManufacturerPartNumber); + + if (combination.Price.HasValue) + values.Add("Price", combination.Price.Value); + + if (combination.DeliveryTimeId.HasValue && combination.DeliveryTimeId.Value > 0) + values.Add("DeliveryTimeId", combination.DeliveryTimeId); + + if (combination.QuantityUnitId.HasValue && combination.QuantityUnitId.Value > 0) + values.Add("QuantityUnitId", combination.QuantityUnitId); + + if (combination.Length.HasValue) + values.Add("Length", combination.Length.Value); + if (combination.Width.HasValue) + values.Add("Width", combination.Width.Value); + if (combination.Height.HasValue) + values.Add("Height", combination.Height.Value); + + if (combination.BasePriceAmount.HasValue) + values.Add("BasePriceAmount", combination.BasePriceAmount); + if (combination.BasePriceBaseAmount.HasValue) + values.Add("BasePriceBaseAmount", combination.BasePriceBaseAmount); + } + + public static IList GetAllCombinationPictureIds(this IEnumerable combinations) + { + var pictureIds = new List(); + + if (combinations != null) + { + var data = combinations + .Where(x => x.IsActive && x.AssignedMediaFileIds != null) + .Select(x => x.AssignedMediaFileIds) + .ToList(); + + if (data.Count > 0) + { + var ids = string.Join(",", data).SplitSafe(",").Distinct(); + + foreach (string str in ids) + { + if (int.TryParse(str, out var id) && !pictureIds.Exists(i => i == id)) + pictureIds.Add(id); + } + } + } + + return pictureIds; + } + + /// + /// Finds a related product item by specified identifiers + /// + /// Source + /// The first product identifier + /// The second product identifier + /// Related product + public static RelatedProduct FindRelatedProduct(this IList source, int productId1, int productId2) { foreach (RelatedProduct relatedProduct in source) @@ -152,20 +154,20 @@ public static CrossSellProduct FindCrossSellProduct(this IList return null; } - public static bool IsAvailableByStock(this Product product) - { - Guard.NotNull(product, nameof(product)); + public static bool IsAvailableByStock(this Product product) + { + Guard.NotNull(product, nameof(product)); - if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock || product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes) - { + if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock || product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes) + { if (product.StockQuantity <= 0 && product.BackorderMode == BackorderMode.NoBackorders) { return false; } - } + } - return true; - } + return true; + } /// /// Formats the stock availability/quantity message @@ -175,28 +177,28 @@ public static bool IsAvailableByStock(this Product product) /// The stock message public static string FormatStockMessage(this Product product, ILocalizationService localizationService) { - Guard.NotNull(product, nameof(product)); - Guard.NotNull(localizationService, nameof(localizationService)); + Guard.NotNull(product, nameof(product)); + Guard.NotNull(localizationService, nameof(localizationService)); - string stockMessage = string.Empty; + string stockMessage = string.Empty; if ((product.ManageInventoryMethod == ManageInventoryMethod.ManageStock || product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes) && product.DisplayStockAvailability) { - if (product.StockQuantity > 0) - { - if (product.DisplayStockQuantity) - stockMessage = string.Format(localizationService.GetResource("Products.Availability.InStockWithQuantity"), product.StockQuantity); - else - stockMessage = localizationService.GetResource("Products.Availability.InStock"); - } - else - { - if (product.BackorderMode == BackorderMode.NoBackorders || product.BackorderMode == BackorderMode.AllowQtyBelow0) - stockMessage = localizationService.GetResource("Products.Availability.OutOfStock"); - else if (product.BackorderMode == BackorderMode.AllowQtyBelow0AndNotifyCustomer) - stockMessage = localizationService.GetResource("Products.Availability.Backordering"); - } + if (product.StockQuantity > 0) + { + if (product.DisplayStockQuantity) + stockMessage = string.Format(localizationService.GetResource("Products.Availability.InStockWithQuantity"), product.StockQuantity); + else + stockMessage = localizationService.GetResource("Products.Availability.InStock"); + } + else + { + if (product.BackorderMode == BackorderMode.NoBackorders || product.BackorderMode == BackorderMode.AllowQtyBelow0) + stockMessage = localizationService.GetResource("Products.Availability.OutOfStock"); + else if (product.BackorderMode == BackorderMode.AllowQtyBelow0AndNotifyCustomer) + stockMessage = localizationService.GetResource("Products.Availability.Backordering"); + } } return stockMessage; @@ -204,16 +206,16 @@ public static string FormatStockMessage(this Product product, ILocalizationServi public static bool DisplayDeliveryTimeAccordingToStock(this Product product, CatalogSettings catalogSettings) { - Guard.NotNull(product, nameof(product)); + Guard.NotNull(product, nameof(product)); Guard.NotNull(catalogSettings, nameof(catalogSettings)); if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock || product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes) - { - if (catalogSettings.DeliveryTimeIdForEmptyStock.HasValue && product.StockQuantity <= 0) - return true; + { + if (catalogSettings.DeliveryTimeIdForEmptyStock.HasValue && product.StockQuantity <= 0) + return true; - return (product.StockQuantity > 0); - } + return (product.StockQuantity > 0); + } return true; } @@ -244,21 +246,21 @@ public static bool DisplayDeliveryTimeAccordingToStock(this Product product, Cat /// Catalog settings /// Whether the product is labeled as NEW public static bool IsNew(this Product product, CatalogSettings catalogSettings) - { - if (catalogSettings.LabelAsNewForMaxDays.HasValue) - { - return ((DateTime.UtcNow - product.CreatedOnUtc).Days <= catalogSettings.LabelAsNewForMaxDays.Value); - } + { + if (catalogSettings.LabelAsNewForMaxDays.HasValue) + { + return ((DateTime.UtcNow - product.CreatedOnUtc).Days <= catalogSettings.LabelAsNewForMaxDays.Value); + } - return false; - } + return false; + } - public static bool ProductTagExists(this Product product, int productTagId) + public static bool ProductTagExists(this Product product, int productTagId) { - Guard.NotNull(product, nameof(product)); + Guard.NotNull(product, nameof(product)); - var result = product.ProductTags.Any(x => x.Id == productTagId); - return result; + var result = product.ProductTags.Any(x => x.Id == productTagId); + return result; } /// @@ -268,9 +270,9 @@ public static bool ProductTagExists(this Product product, int productTagId) /// Result public static int[] ParseAllowedQuatities(this Product product) { - Guard.NotNull(product, nameof(product)); + Guard.NotNull(product, nameof(product)); - var result = new List(); + var result = new List(); if (!String.IsNullOrWhiteSpace(product.AllowedQuantities)) { product @@ -289,25 +291,25 @@ public static int[] ParseAllowedQuatities(this Product product) return result.ToArray(); } - public static int[] ParseRequiredProductIds(this Product product) - { - Guard.NotNull(product, nameof(product)); + public static int[] ParseRequiredProductIds(this Product product) + { + Guard.NotNull(product, nameof(product)); - if (String.IsNullOrEmpty(product.RequiredProductIds)) - return new int[0]; + if (String.IsNullOrEmpty(product.RequiredProductIds)) + return new int[0]; - var ids = new List(); + var ids = new List(); - foreach (var idStr in product.RequiredProductIds - .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => x.Trim())) - { - if (int.TryParse(idStr, out var id)) - ids.Add(id); - } + foreach (var idStr in product.RequiredProductIds + .Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) + .Select(x => x.Trim())) + { + if (int.TryParse(idStr, out var id)) + ids.Add(id); + } - return ids.ToArray(); - } + return ids.ToArray(); + } /// /// Gets the base price info @@ -323,21 +325,21 @@ public static int[] ParseRequiredProductIds(this Product product) /// Price adjustment /// The base price info public static string GetBasePriceInfo(this Product product, - ILocalizationService localizationService, - IPriceFormatter priceFormatter, + ILocalizationService localizationService, + IPriceFormatter priceFormatter, ICurrencyService currencyService, - ITaxService taxService, - IPriceCalculationService priceCalculationService, - Customer customer, + ITaxService taxService, + IPriceCalculationService priceCalculationService, + Customer customer, Currency currency, - decimal priceAdjustment = decimal.Zero) + decimal priceAdjustment = decimal.Zero) { - Guard.NotNull(product, nameof(product)); - Guard.NotNull(currencyService, nameof(currencyService)); - Guard.NotNull(taxService, nameof(taxService)); - Guard.NotNull(priceCalculationService, nameof(priceCalculationService)); - Guard.NotNull(customer, nameof(customer)); - Guard.NotNull(currency, nameof(currency)); + Guard.NotNull(product, nameof(product)); + Guard.NotNull(currencyService, nameof(currencyService)); + Guard.NotNull(taxService, nameof(taxService)); + Guard.NotNull(priceCalculationService, nameof(priceCalculationService)); + Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(currency, nameof(currency)); if (product.BasePriceHasValue && product.BasePriceAmount != decimal.Zero) { @@ -345,67 +347,67 @@ public static string GetBasePriceInfo(this Product product, var price = taxService.GetProductPrice(product, decimal.Add(currentPrice, priceAdjustment), customer, currency, out var taxrate); price = currencyService.ConvertFromPrimaryStoreCurrency(price, currency); - return product.GetBasePriceInfo(price, localizationService, priceFormatter, currency); - } + return product.GetBasePriceInfo(price, localizationService, priceFormatter, currency); + } return string.Empty; } - /// - /// Gets the base price info - /// - /// Product - /// The calculated product price - /// Localization service - /// Price formatter - /// Target currency - /// The base price info - public static string GetBasePriceInfo(this Product product, - decimal productPrice, - ILocalizationService localizationService, - IPriceFormatter priceFormatter, - Currency currency) - { - Guard.NotNull(product, nameof(product)); - Guard.NotNull(localizationService, nameof(localizationService)); - Guard.NotNull(priceFormatter, nameof(priceFormatter)); - Guard.NotNull(currency, nameof(currency)); - - if (product.BasePriceHasValue && product.BasePriceAmount != decimal.Zero) - { - var value = Convert.ToDecimal((productPrice / product.BasePriceAmount) * product.BasePriceBaseAmount); - var valueFormatted = priceFormatter.FormatPrice(value, true, currency); - var amountFormatted = Math.Round(product.BasePriceAmount.Value, 2).ToString("G29"); - var infoTemplate = localizationService.GetResource("Products.BasePriceInfo"); - - var result = infoTemplate.FormatInvariant( - amountFormatted, - product.BasePriceMeasureUnit, - valueFormatted, - product.BasePriceBaseAmount - ); - - return result; - } - - return string.Empty; - } - - public static string GetProductTypeLabel(this Product product, ILocalizationService localizationService) - { - if (product != null && product.ProductType != ProductType.SimpleProduct) - { - var key = "Admin.Catalog.Products.ProductType.{0}.Label".FormatInvariant(product.ProductType.ToString()); - return localizationService.GetResource(key); - } - - return string.Empty; - } - - public static bool CanBeBundleItem(this Product product) - { - return product != null && product.ProductType == ProductType.SimpleProduct && !product.IsRecurring && !product.IsDownload; - } + /// + /// Gets the base price info + /// + /// Product + /// The calculated product price + /// Localization service + /// Price formatter + /// Target currency + /// The base price info + public static string GetBasePriceInfo(this Product product, + decimal productPrice, + ILocalizationService localizationService, + IPriceFormatter priceFormatter, + Currency currency) + { + Guard.NotNull(product, nameof(product)); + Guard.NotNull(localizationService, nameof(localizationService)); + Guard.NotNull(priceFormatter, nameof(priceFormatter)); + Guard.NotNull(currency, nameof(currency)); + + if (product.BasePriceHasValue && product.BasePriceAmount != decimal.Zero) + { + var value = Convert.ToDecimal((productPrice / product.BasePriceAmount) * product.BasePriceBaseAmount); + var valueFormatted = priceFormatter.FormatPrice(value, true, currency); + var amountFormatted = product.BasePriceAmount.Value.ToString("G29"); + var infoTemplate = localizationService.GetResource("Products.BasePriceInfo"); + + var result = infoTemplate.FormatInvariant( + amountFormatted, + product.BasePriceMeasureUnit, + valueFormatted, + product.BasePriceBaseAmount + ); + + return result; + } + + return string.Empty; + } + + public static string GetProductTypeLabel(this Product product, ILocalizationService localizationService) + { + if (product != null && product.ProductType != ProductType.SimpleProduct) + { + var key = "Admin.Catalog.Products.ProductType.{0}.Label".FormatInvariant(product.ProductType.ToString()); + return localizationService.GetResource(key); + } + + return string.Empty; + } + + public static bool CanBeBundleItem(this Product product) + { + return product != null && product.ProductType == ProductType.SimpleProduct && !product.IsRecurring && !product.IsDownload; + } public static bool FilterOut(this ProductBundleItem bundleItem, ProductVariantAttributeValue value, out ProductBundleItemAttributeFilter filter) { @@ -420,61 +422,61 @@ public static bool FilterOut(this ProductBundleItem bundleItem, ProductVariantAt } public static string GetLocalizedName(this ProductBundleItem bundleItem) - { - if (bundleItem != null) - { - string name = bundleItem.GetLocalized(x => x.Name); - return (name.HasValue() ? name : bundleItem.Product.GetLocalized(x => x.Name)); - } - return null; - } - public static string GetLocalizedName(this ProductBundleItem bundleItem, int languageId) - { - if (bundleItem != null) - { - string name = bundleItem.GetLocalized(x => x.Name, languageId); - return (name.HasValue() ? name : bundleItem.Product.GetLocalized(x => x.Name, languageId)); - } - return null; - } - - public static ProductBundleItemOrderData ToOrderData(this ProductBundleItemData bundleItemData, decimal priceWithDiscount = decimal.Zero, - string attributesXml = null, string attributesInfo = null) - { + { + if (bundleItem != null) + { + string name = bundleItem.GetLocalized(x => x.Name); + return (name.HasValue() ? name : bundleItem.Product.GetLocalized(x => x.Name)); + } + return null; + } + public static string GetLocalizedName(this ProductBundleItem bundleItem, int languageId) + { + if (bundleItem != null) + { + string name = bundleItem.GetLocalized(x => x.Name, languageId); + return (name.HasValue() ? name : bundleItem.Product.GetLocalized(x => x.Name, languageId)); + } + return null; + } + + public static ProductBundleItemOrderData ToOrderData(this ProductBundleItemData bundleItemData, decimal priceWithDiscount = decimal.Zero, + string attributesXml = null, string attributesInfo = null) + { if (bundleItemData == null || bundleItemData.Item == null) { return null; } - var item = bundleItemData.Item; - string bundleItemName = item.GetLocalized(x => x.Name); - - var bundleData = new ProductBundleItemOrderData() - { - BundleItemId = item.Id, - ProductId = item.ProductId, - Sku = item.Product.Sku, - ProductName = bundleItemName ?? item.Product.GetLocalized(x => x.Name), - ProductSeName = item.Product.GetSeName(), - VisibleIndividually = item.Product.VisibleIndividually, - Quantity = item.Quantity, - DisplayOrder = item.DisplayOrder, - PriceWithDiscount = priceWithDiscount, - AttributesXml = attributesXml, - AttributesInfo = attributesInfo, - PerItemShoppingCart = item.BundleProduct.BundlePerItemShoppingCart - }; - - return bundleData; - } - public static void ToOrderData(this ProductBundleItemData bundleItemData, IList bundleData, decimal priceWithDiscount = decimal.Zero, - string attributesXml = null, string attributesInfo = null) - { - var item = bundleItemData.ToOrderData(priceWithDiscount, attributesXml, attributesInfo); - - if (item != null && item.ProductId != 0 && item.BundleItemId != 0) - bundleData.Add(item); - } + var item = bundleItemData.Item; + string bundleItemName = item.GetLocalized(x => x.Name); + + var bundleData = new ProductBundleItemOrderData() + { + BundleItemId = item.Id, + ProductId = item.ProductId, + Sku = item.Product.Sku, + ProductName = bundleItemName ?? item.Product.GetLocalized(x => x.Name), + ProductSeName = item.Product.GetSeName(), + VisibleIndividually = item.Product.Visibility != ProductVisibility.Hidden, + Quantity = item.Quantity, + DisplayOrder = item.DisplayOrder, + PriceWithDiscount = priceWithDiscount, + AttributesXml = attributesXml, + AttributesInfo = attributesInfo, + PerItemShoppingCart = item.BundleProduct.BundlePerItemShoppingCart + }; + + return bundleData; + } + public static void ToOrderData(this ProductBundleItemData bundleItemData, IList bundleData, decimal priceWithDiscount = decimal.Zero, + string attributesXml = null, string attributesInfo = null) + { + var item = bundleItemData.ToOrderData(priceWithDiscount, attributesXml, attributesInfo); + + if (item != null && item.ProductId != 0 && item.BundleItemId != 0) + bundleData.Add(item); + } } } diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductService.cs b/src/Libraries/SmartStore.Services/Catalog/ProductService.cs index bd20bc65e9..622f9c56c6 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductService.cs @@ -4,188 +4,205 @@ using System.Data.Entity; using System.Linq; using SmartStore.Collections; +using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Discounts; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Orders; -using SmartStore.Core.Events; using SmartStore.Data.Caching; -using SmartStore.Services.Messages; +using SmartStore.Services.Media; using SmartStore.Services.Orders; namespace SmartStore.Services.Catalog { public partial class ProductService : IProductService - { - private readonly IRepository _productRepository; + { + private readonly IRepository _productRepository; private readonly IRepository _relatedProductRepository; private readonly IRepository _crossSellProductRepository; private readonly IRepository _tierPriceRepository; - private readonly IRepository _productPictureRepository; + private readonly IRepository _productFileRepository; private readonly IRepository _productVariantAttributeCombinationRepository; - private readonly IRepository _productBundleItemRepository; - private readonly IRepository _shoppingCartItemRepository; - private readonly IProductAttributeService _productAttributeService; + private readonly IRepository _productBundleItemRepository; + private readonly IRepository _shoppingCartItemRepository; + private readonly IProductAttributeService _productAttributeService; private readonly IProductAttributeParser _productAttributeParser; private readonly IDbContext _dbContext; private readonly LocalizationSettings _localizationSettings; - private readonly ICommonServices _services; + private readonly ICommonServices _services; public ProductService( IRepository productRepository, IRepository relatedProductRepository, IRepository crossSellProductRepository, IRepository tierPriceRepository, - IRepository productPictureRepository, + IRepository productPictureRepository, IRepository productVariantAttributeCombinationRepository, - IRepository productBundleItemRepository, - IRepository shoppingCartItemRepository, - IProductAttributeService productAttributeService, + IRepository productBundleItemRepository, + IRepository shoppingCartItemRepository, + IProductAttributeService productAttributeService, IProductAttributeParser productAttributeParser, - IDbContext dbContext, + IDbContext dbContext, LocalizationSettings localizationSettings, - ICommonServices services) + ICommonServices services) { _productRepository = productRepository; _relatedProductRepository = relatedProductRepository; _crossSellProductRepository = crossSellProductRepository; _tierPriceRepository = tierPriceRepository; - _productPictureRepository = productPictureRepository; + _productFileRepository = productPictureRepository; _productVariantAttributeCombinationRepository = productVariantAttributeCombinationRepository; - _productBundleItemRepository = productBundleItemRepository; - _shoppingCartItemRepository = shoppingCartItemRepository; + _productBundleItemRepository = productBundleItemRepository; + _shoppingCartItemRepository = shoppingCartItemRepository; _productAttributeService = productAttributeService; _productAttributeParser = productAttributeParser; _dbContext = dbContext; _localizationSettings = localizationSettings; - _services = services; + _services = services; } - #region Utilities - - protected virtual int EnsureMutuallyRelatedProducts(List productIds) - { - int count = 0; - - foreach (int id1 in productIds) - { - var mutualAssociations = ( - from rp in _relatedProductRepository.Table - join p in _productRepository.Table on rp.ProductId2 equals p.Id - where rp.ProductId2 == id1 && !p.Deleted - select rp).ToList(); - - foreach (int id2 in productIds) - { - if (id1 == id2) - continue; - - if (!mutualAssociations.Any(x => x.ProductId1 == id2)) - { - int maxDisplayOrder = _relatedProductRepository.TableUntracked - .Where(x => x.ProductId1 == id2) - .OrderByDescending(x => x.DisplayOrder) - .Select(x => x.DisplayOrder) - .FirstOrDefault(); - - var newRelatedProduct = new RelatedProduct - { - ProductId1 = id2, - ProductId2 = id1, - DisplayOrder = maxDisplayOrder + 1 - }; - - InsertRelatedProduct(newRelatedProduct); - ++count; - } - } - } - - return count; - } - - protected virtual int EnsureMutuallyCrossSellProducts(List productIds) - { - int count = 0; - - foreach (int id1 in productIds) - { - var mutualAssociations = ( - from rp in _crossSellProductRepository.Table - join p in _productRepository.Table on rp.ProductId2 equals p.Id - where rp.ProductId2 == id1 && !p.Deleted - select rp).ToList(); - - foreach (int id2 in productIds) - { - if (id1 == id2) - continue; - - if (!mutualAssociations.Any(x => x.ProductId1 == id2)) - { - var newCrossSellProduct = new CrossSellProduct - { - ProductId1 = id2, - ProductId2 = id1 - }; - - InsertCrossSellProduct(newCrossSellProduct); - ++count; - } - } - } - - return count; - } - - #endregion - - #region Products - - public virtual void DeleteProduct(Product product) + #region Utilities + + protected virtual int EnsureMutuallyRelatedProducts(List productIds) + { + int count = 0; + + foreach (int id1 in productIds) + { + var mutualAssociations = ( + from rp in _relatedProductRepository.Table + join p in _productRepository.Table on rp.ProductId2 equals p.Id + where rp.ProductId2 == id1 && !p.Deleted + select rp).ToList(); + + foreach (int id2 in productIds) + { + if (id1 == id2) + continue; + + if (!mutualAssociations.Any(x => x.ProductId1 == id2)) + { + int maxDisplayOrder = _relatedProductRepository.TableUntracked + .Where(x => x.ProductId1 == id2) + .OrderByDescending(x => x.DisplayOrder) + .Select(x => x.DisplayOrder) + .FirstOrDefault(); + + var newRelatedProduct = new RelatedProduct + { + ProductId1 = id2, + ProductId2 = id1, + DisplayOrder = maxDisplayOrder + 1 + }; + + InsertRelatedProduct(newRelatedProduct); + ++count; + } + } + } + + return count; + } + + protected virtual int EnsureMutuallyCrossSellProducts(List productIds) + { + int count = 0; + + foreach (int id1 in productIds) + { + var mutualAssociations = ( + from rp in _crossSellProductRepository.Table + join p in _productRepository.Table on rp.ProductId2 equals p.Id + where rp.ProductId2 == id1 && !p.Deleted + select rp).ToList(); + + foreach (int id2 in productIds) + { + if (id1 == id2) + continue; + + if (!mutualAssociations.Any(x => x.ProductId1 == id2)) + { + var newCrossSellProduct = new CrossSellProduct + { + ProductId1 = id2, + ProductId2 = id1 + }; + + InsertCrossSellProduct(newCrossSellProduct); + ++count; + } + } + } + + return count; + } + + #endregion + + #region Products + + public virtual int CountAllProducts() { - Guard.NotNull(product, nameof(product)); + var query = _productRepository.Table; + query = query.Where(x => !x.Deleted); + + return query.Count(); - product.Deleted = true; - product.DeliveryTimeId = null; - product.QuantityUnitId = null; - product.CountryOfOriginId = null; + } + + public virtual int CountAllProductVariants() + { + var query = _productVariantAttributeCombinationRepository.Table; + query = query.Where(x => x.IsActive); + + return query.Count(); + } + + public virtual void DeleteProduct(Product product) + { + Guard.NotNull(product, nameof(product)); + + product.Deleted = true; + product.DeliveryTimeId = null; + product.QuantityUnitId = null; + product.CountryOfOriginId = null; UpdateProduct(product); - if (product.ProductType == ProductType.GroupedProduct) - { - var associatedProducts = _productRepository.Table - .Where(x => x.ParentGroupedProductId == product.Id) - .ToList(); + if (product.ProductType == ProductType.GroupedProduct) + { + var associatedProducts = _productRepository.Table + .Where(x => x.ParentGroupedProductId == product.Id) + .ToList(); - associatedProducts.ForEach(x => x.ParentGroupedProductId = 0); + associatedProducts.ForEach(x => x.ParentGroupedProductId = 0); - _dbContext.SaveChanges(); - } + _dbContext.SaveChanges(); + } } public virtual IList GetAllProductsDisplayedOnHomePage() { - var query = - from p in _productRepository.Table - orderby p.HomePageDisplayOrder - where p.Published && !p.Deleted && p.ShowOnHomePage - select p; + var query = + from p in _productRepository.Table + orderby p.HomePageDisplayOrder + where p.Published && !p.Deleted && p.ShowOnHomePage + select p; var products = query.ToList(); return products; } - + public virtual Product GetProductById(int productId) { if (productId == 0) return null; - return _productRepository.GetByIdCached(productId, "db.product.id-" + productId); - } + return _productRepository.GetByIdCached(productId, "db.product.id-" + productId); + } public virtual IList GetProductsByIds(int[] productIds, ProductLoadFlags flags = ProductLoadFlags.None) { @@ -198,118 +215,118 @@ public virtual IList GetProductsByIds(int[] productIds, ProductLoadFlag where productIds.Contains(p.Id) select p; - if (flags > ProductLoadFlags.None) - { - query = ApplyLoadFlags(query, flags); - } - - var products = query.ToList(); - - // Sort by passed identifier sequence. - return products.OrderBySequence(productIds).ToList(); - } - - public virtual Product GetProductBySystemName(string systemName) - { - if (systemName.IsEmpty()) - { - return null; - } - - var product = _productRepository.Table.FirstOrDefault(x => x.SystemName == systemName && x.IsSystemProduct); - return product; - } - - private IQueryable ApplyLoadFlags(IQueryable query, ProductLoadFlags flags) - { - if (flags.HasFlag(ProductLoadFlags.WithAttributeCombinations)) - { - query = query.Include(x => x.ProductVariantAttributeCombinations); - } - - if (flags.HasFlag(ProductLoadFlags.WithBundleItems)) - { - query = query.Include(x => x.ProductBundleItems.Select(y => y.Product)); - } - - if (flags.HasFlag(ProductLoadFlags.WithCategories)) - { - query = query.Include(x => x.ProductCategories.Select(y => y.Category)); - } - - if (flags.HasFlag(ProductLoadFlags.WithDiscounts)) - { - query = query.Include(x => x.AppliedDiscounts); - } - - if (flags.HasFlag(ProductLoadFlags.WithManufacturers)) - { - query = query.Include(x => x.ProductManufacturers.Select(y => y.Manufacturer)); - } - - if (flags.HasFlag(ProductLoadFlags.WithPictures)) - { - query = query.Include(x => x.ProductPictures); - } - - if (flags.HasFlag(ProductLoadFlags.WithReviews)) - { - query = query.Include(x => x.ProductReviews); - } - - if (flags.HasFlag(ProductLoadFlags.WithSpecificationAttributes)) - { - query = query.Include(x => x.ProductSpecificationAttributes.Select(y => y.SpecificationAttributeOption)); - } - - if (flags.HasFlag(ProductLoadFlags.WithTags)) - { - query = query.Include(x => x.ProductTags); - } - - if (flags.HasFlag(ProductLoadFlags.WithTierPrices)) - { - query = query.Include(x => x.TierPrices); - } - - if (flags.HasFlag(ProductLoadFlags.WithAttributes)) - { - query = query.Include(x => x.ProductVariantAttributes.Select(y => y.ProductAttribute)); - } - - if (flags.HasFlag(ProductLoadFlags.WithAttributeValues)) - { - query = query.Include(x => x.ProductVariantAttributes.Select(y => y.ProductVariantAttributeValues)); - } - - if (flags.HasFlag(ProductLoadFlags.WithDeliveryTime)) - { - query = query.Include(x => x.DeliveryTime); - } - - return query; - } + if (flags > ProductLoadFlags.None) + { + query = ApplyLoadFlags(query, flags); + } + + var products = query.ToList(); + + // Sort by passed identifier sequence. + return products.OrderBySequence(productIds).ToList(); + } + + public virtual Product GetProductBySystemName(string systemName) + { + if (systemName.IsEmpty()) + { + return null; + } + + var product = _productRepository.Table.FirstOrDefault(x => x.SystemName == systemName && x.IsSystemProduct); + return product; + } + + private IQueryable ApplyLoadFlags(IQueryable query, ProductLoadFlags flags) + { + if (flags.HasFlag(ProductLoadFlags.WithAttributeCombinations)) + { + query = query.Include(x => x.ProductVariantAttributeCombinations); + } + + if (flags.HasFlag(ProductLoadFlags.WithBundleItems)) + { + query = query.Include(x => x.ProductBundleItems.Select(y => y.Product)); + } + + if (flags.HasFlag(ProductLoadFlags.WithCategories)) + { + query = query.Include(x => x.ProductCategories.Select(y => y.Category)); + } + + if (flags.HasFlag(ProductLoadFlags.WithDiscounts)) + { + query = query.Include(x => x.AppliedDiscounts); + } + + if (flags.HasFlag(ProductLoadFlags.WithManufacturers)) + { + query = query.Include(x => x.ProductManufacturers.Select(y => y.Manufacturer)); + } + + if (flags.HasFlag(ProductLoadFlags.WithPictures)) + { + query = query.Include(x => x.ProductPictures); + } + + if (flags.HasFlag(ProductLoadFlags.WithReviews)) + { + query = query.Include(x => x.ProductReviews); + } + + if (flags.HasFlag(ProductLoadFlags.WithSpecificationAttributes)) + { + query = query.Include(x => x.ProductSpecificationAttributes.Select(y => y.SpecificationAttributeOption)); + } + + if (flags.HasFlag(ProductLoadFlags.WithTags)) + { + query = query.Include(x => x.ProductTags); + } + + if (flags.HasFlag(ProductLoadFlags.WithTierPrices)) + { + query = query.Include(x => x.TierPrices); + } + + if (flags.HasFlag(ProductLoadFlags.WithAttributes)) + { + query = query.Include(x => x.ProductVariantAttributes.Select(y => y.ProductAttribute)); + } + + if (flags.HasFlag(ProductLoadFlags.WithAttributeValues)) + { + query = query.Include(x => x.ProductVariantAttributes.Select(y => y.ProductVariantAttributeValues)); + } + + if (flags.HasFlag(ProductLoadFlags.WithDeliveryTime)) + { + query = query.Include(x => x.DeliveryTime); + } + + return query; + } public virtual void InsertProduct(Product product) { - Guard.NotNull(product, nameof(product)); + Guard.NotNull(product, nameof(product)); - _productRepository.Insert(product); + _productRepository.Insert(product); } - public virtual void UpdateProduct(Product product) + public virtual void UpdateProduct(Product product) { - Guard.NotNull(product, nameof(product)); + Guard.NotNull(product, nameof(product)); _productRepository.Update(product); } public virtual void UpdateProductReviewTotals(Product product) { - Guard.NotNull(product, nameof(product)); + Guard.NotNull(product, nameof(product)); - int approvedRatingSum = 0; - int notApprovedRatingSum = 0; + int approvedRatingSum = 0; + int notApprovedRatingSum = 0; int approvedTotalReviews = 0; int notApprovedTotalReviews = 0; var reviews = product.ProductReviews; @@ -318,7 +335,7 @@ public virtual void UpdateProductReviewTotals(Product product) if (pr.IsApproved) { approvedRatingSum += pr.Rating; - approvedTotalReviews ++; + approvedTotalReviews++; } else { @@ -336,51 +353,51 @@ public virtual void UpdateProductReviewTotals(Product product) public virtual IList GetLowStockProducts() { - // Track inventory for product - var query1 = from p in _productRepository.Table - orderby p.MinStockQuantity - where !p.Deleted && - p.ManageInventoryMethodId == (int)ManageInventoryMethod.ManageStock && - p.MinStockQuantity >= p.StockQuantity - select p; - var products1 = query1.ToList(); - - // Track inventory for product by product attributes - var query2 = from p in _productRepository.Table - from pvac in p.ProductVariantAttributeCombinations - where !p.Deleted && - p.ManageInventoryMethodId == (int)ManageInventoryMethod.ManageStockByAttributes && - pvac.StockQuantity <= 0 - select p; - - // only distinct products (group by ID) - // if we use standard Distinct() method, then all fields will be compared (low performance) - query2 = from p in query2 - group p by p.Id into pGroup - orderby pGroup.Key - select pGroup.FirstOrDefault(); - var products2 = query2.ToList(); - - var result = new List(); - result.AddRange(products1); - result.AddRange(products2); + // Track inventory for product + var query1 = from p in _productRepository.Table + orderby p.MinStockQuantity + where !p.Deleted && + p.ManageInventoryMethodId == (int)ManageInventoryMethod.ManageStock && + p.MinStockQuantity >= p.StockQuantity + select p; + var products1 = query1.ToList(); + + // Track inventory for product by product attributes + var query2 = from p in _productRepository.Table + from pvac in p.ProductVariantAttributeCombinations + where !p.Deleted && + p.ManageInventoryMethodId == (int)ManageInventoryMethod.ManageStockByAttributes && + pvac.StockQuantity <= 0 + select p; + + // only distinct products (group by ID) + // if we use standard Distinct() method, then all fields will be compared (low performance) + query2 = from p in query2 + group p by p.Id into pGroup + orderby pGroup.Key + select pGroup.FirstOrDefault(); + var products2 = query2.ToList(); + + var result = new List(); + result.AddRange(products1); + result.AddRange(products2); return result; } - public virtual Product GetProductBySku(string sku) - { - if (String.IsNullOrEmpty(sku)) - return null; + public virtual Product GetProductBySku(string sku) + { + if (String.IsNullOrEmpty(sku)) + return null; - sku = sku.Trim(); + sku = sku.Trim(); - var query = from p in _productRepository.Table - orderby p.DisplayOrder, p.Id - where !p.Deleted && p.Sku == sku - select p; - var product = query.FirstOrDefault(); - return product; - } + var query = from p in _productRepository.Table + orderby p.DisplayOrder, p.Id + where !p.Deleted && p.Sku == sku + select p; + var product = query.FirstOrDefault(); + return product; + } public virtual Product GetProductByGtin(string gtin) { @@ -397,134 +414,180 @@ orderby p.Id return product; } - public virtual Product GetProductByManufacturerPartNumber(string manufacturerPartNumber) - { - if (manufacturerPartNumber.IsEmpty()) - return null; - - manufacturerPartNumber = manufacturerPartNumber.Trim(); - - var product = _productRepository.Table - .Where(x => !x.Deleted && x.ManufacturerPartNumber == manufacturerPartNumber) - .OrderBy(x => x.Id) - .FirstOrDefault(); - - return product; - } - - public virtual Product GetProductByName(string name) - { - if (name.IsEmpty()) - return null; - - name = name.Trim(); - - var product = _productRepository.Table - .Where(x => !x.Deleted && x.Name == name) - .OrderBy(x => x.Id) - .FirstOrDefault(); - - return product; - } - - public virtual AdjustInventoryResult AdjustInventory(OrganizedShoppingCartItem sci, bool decrease) - { - if (sci == null) - throw new ArgumentNullException("cartItem"); - - if (sci.Item.Product.ProductType == ProductType.BundledProduct && sci.Item.Product.BundlePerItemShoppingCart) - { - if (sci.ChildItems != null) - { - foreach (var child in sci.ChildItems.Where(x => x.Item.Id != sci.Item.Id)) - { - AdjustInventory(child.Item.Product, decrease, sci.Item.Quantity * child.Item.Quantity, child.Item.AttributesXml); - } - } - return new AdjustInventoryResult(); - } - else - { - return AdjustInventory(sci.Item.Product, decrease, sci.Item.Quantity, sci.Item.AttributesXml); - } - } - - public virtual AdjustInventoryResult AdjustInventory(OrderItem orderItem, bool decrease, int quantity) - { - Guard.NotNull(orderItem, nameof(orderItem)); - - if (orderItem.Product.ProductType == ProductType.BundledProduct && orderItem.Product.BundlePerItemShoppingCart) - { - if (orderItem.BundleData.HasValue()) - { - var bundleData = orderItem.GetBundleData(); - if (bundleData.Count > 0) - { - var products = GetProductsByIds(bundleData.Select(x => x.ProductId).ToArray()); - - foreach (var item in bundleData) - { - var product = products.FirstOrDefault(x => x.Id == item.ProductId); - if (product != null) - AdjustInventory(product, decrease, quantity * item.Quantity, item.AttributesXml); - } - } - } - return new AdjustInventoryResult(); - } - else - { - return AdjustInventory(orderItem.Product, decrease, quantity, orderItem.AttributesXml); - } - } - - public virtual AdjustInventoryResult AdjustInventory(Product product, bool decrease, int quantity, string attributesXml) + public virtual Product GetProductByManufacturerPartNumber(string manufacturerPartNumber) + { + if (manufacturerPartNumber.IsEmpty()) + return null; + + manufacturerPartNumber = manufacturerPartNumber.Trim(); + + var product = _productRepository.Table + .Where(x => !x.Deleted && x.ManufacturerPartNumber == manufacturerPartNumber) + .OrderBy(x => x.Id) + .FirstOrDefault(); + + return product; + } + + public virtual Product GetProductByIdentificationNumber( + string identificationNumber, + out ProductVariantAttributeCombination attributeCombination, + bool includeHidden = false, + bool untracked = true) { - Guard.NotNull(product, nameof(product)); + attributeCombination = null; - var result = new AdjustInventoryResult(); + if (string.IsNullOrWhiteSpace(identificationNumber)) + { + return null; + } + + identificationNumber = identificationNumber.Trim(); + + var pq = + from x in untracked ? _productRepository.TableUntracked : _productRepository.Table + where !x.Deleted && (x.Sku == identificationNumber || x.ManufacturerPartNumber == identificationNumber || x.Gtin == identificationNumber) + select x; + + if (!includeHidden) + { + pq = pq.Where(x => x.Visibility <= ProductVisibility.SearchResults && x.Published); + } + + var product = pq.FirstOrDefault(); + if (product != null) + { + return product; + } + + var pvaq = + from x in untracked ? _productVariantAttributeCombinationRepository.TableUntracked : _productVariantAttributeCombinationRepository.Table + where !x.Product.Deleted && (x.Sku == identificationNumber || x.ManufacturerPartNumber == identificationNumber || x.Gtin == identificationNumber) + select x; + + if (!includeHidden) + { + pvaq = pvaq.Where(x => x.Product.Visibility <= ProductVisibility.SearchResults && x.Product.Published && x.IsActive); + } + + attributeCombination = pvaq.FirstOrDefault(); + + return attributeCombination?.Product; + } + + public virtual Product GetProductByName(string name) + { + if (name.IsEmpty()) + return null; + + name = name.Trim(); + + var product = _productRepository.Table + .Where(x => !x.Deleted && x.Name == name) + .OrderBy(x => x.Id) + .FirstOrDefault(); + + return product; + } + + public virtual AdjustInventoryResult AdjustInventory(OrganizedShoppingCartItem sci, bool decrease) + { + if (sci == null) + throw new ArgumentNullException("cartItem"); - switch (product.ManageInventoryMethod) + if (sci.Item.Product.ProductType == ProductType.BundledProduct && sci.Item.Product.BundlePerItemShoppingCart) + { + if (sci.ChildItems != null) + { + foreach (var child in sci.ChildItems.Where(x => x.Item.Id != sci.Item.Id)) + { + AdjustInventory(child.Item.Product, decrease, sci.Item.Quantity * child.Item.Quantity, child.Item.AttributesXml); + } + } + return new AdjustInventoryResult(); + } + else + { + return AdjustInventory(sci.Item.Product, decrease, sci.Item.Quantity, sci.Item.AttributesXml); + } + } + + public virtual AdjustInventoryResult AdjustInventory(OrderItem orderItem, bool decrease, int quantity) + { + Guard.NotNull(orderItem, nameof(orderItem)); + + if (orderItem.Product.ProductType == ProductType.BundledProduct && orderItem.Product.BundlePerItemShoppingCart) + { + if (orderItem.BundleData.HasValue()) + { + var bundleData = orderItem.GetBundleData(); + if (bundleData.Count > 0) + { + var products = GetProductsByIds(bundleData.Select(x => x.ProductId).ToArray()); + + foreach (var item in bundleData) + { + var product = products.FirstOrDefault(x => x.Id == item.ProductId); + if (product != null) + AdjustInventory(product, decrease, quantity * item.Quantity, item.AttributesXml); + } + } + } + return new AdjustInventoryResult(); + } + else + { + return AdjustInventory(orderItem.Product, decrease, quantity, orderItem.AttributesXml); + } + } + + public virtual AdjustInventoryResult AdjustInventory(Product product, bool decrease, int quantity, string attributesXml) + { + Guard.NotNull(product, nameof(product)); + + var result = new AdjustInventoryResult(); + + switch (product.ManageInventoryMethod) { case ManageInventoryMethod.DontManageStock: { //do nothing } - break; + break; case ManageInventoryMethod.ManageStock: { - result.StockQuantityOld = product.StockQuantity; + result.StockQuantityOld = product.StockQuantity; if (decrease) - result.StockQuantityNew = product.StockQuantity - quantity; - else - result.StockQuantityNew = product.StockQuantity + quantity; + result.StockQuantityNew = product.StockQuantity - quantity; + else + result.StockQuantityNew = product.StockQuantity + quantity; - bool newPublished = product.Published; - bool newDisableBuyButton = product.DisableBuyButton; - bool newDisableWishlistButton = product.DisableWishlistButton; + bool newPublished = product.Published; + bool newDisableBuyButton = product.DisableBuyButton; + bool newDisableWishlistButton = product.DisableWishlistButton; //check if minimum quantity is reached switch (product.LowStockActivity) { case LowStockActivity.DisableBuyButton: - newDisableBuyButton = product.MinStockQuantity >= result.StockQuantityNew; - newDisableWishlistButton = product.MinStockQuantity >= result.StockQuantityNew; + newDisableBuyButton = product.MinStockQuantity >= result.StockQuantityNew; + newDisableWishlistButton = product.MinStockQuantity >= result.StockQuantityNew; break; case LowStockActivity.Unpublish: - newPublished = product.MinStockQuantity <= result.StockQuantityNew; + newPublished = product.MinStockQuantity <= result.StockQuantityNew; break; } - product.StockQuantity = result.StockQuantityNew; - product.DisableBuyButton = newDisableBuyButton; - product.DisableWishlistButton = newDisableWishlistButton; - product.Published = newPublished; + product.StockQuantity = result.StockQuantityNew; + product.DisableBuyButton = newDisableBuyButton; + product.DisableWishlistButton = newDisableWishlistButton; + product.Published = newPublished; + + UpdateProduct(product); - UpdateProduct(product); - //send email notification - if (decrease && product.NotifyAdminForQuantityBelow > result.StockQuantityNew) - _services.MessageFactory.SendQuantityBelowStoreOwnerNotification(product, _localizationSettings.DefaultAdminLanguageId); + if (decrease && product.NotifyAdminForQuantityBelow > result.StockQuantityNew) + _services.MessageFactory.SendQuantityBelowStoreOwnerNotification(product, _localizationSettings.DefaultAdminLanguageId); } break; case ManageInventoryMethod.ManageStockByAttributes: @@ -532,13 +595,13 @@ public virtual AdjustInventoryResult AdjustInventory(Product product, bool decre var combination = _productAttributeParser.FindProductVariantAttributeCombination(product.Id, attributesXml); if (combination != null) { - result.StockQuantityOld = combination.StockQuantity; + result.StockQuantityOld = combination.StockQuantity; if (decrease) - result.StockQuantityNew = combination.StockQuantity - quantity; + result.StockQuantityNew = combination.StockQuantity - quantity; else - result.StockQuantityNew = combination.StockQuantity + quantity; + result.StockQuantityNew = combination.StockQuantity + quantity; - combination.StockQuantity = result.StockQuantityNew; + combination.StockQuantity = result.StockQuantityNew; _productAttributeService.UpdateProductVariantAttributeCombination(combination); } } @@ -547,56 +610,56 @@ public virtual AdjustInventoryResult AdjustInventory(Product product, bool decre break; } - var attributeValues = _productAttributeParser.ParseProductVariantAttributeValues(attributesXml); + var attributeValues = _productAttributeParser.ParseProductVariantAttributeValues(attributesXml); - attributeValues - .Where(x => x.ValueType == ProductVariantAttributeValueType.ProductLinkage) - .ToList() - .Each(x => - { - var linkedProduct = GetProductById(x.LinkedProductId); - if (linkedProduct != null) - AdjustInventory(linkedProduct, decrease, quantity * x.Quantity, ""); - }); + attributeValues + .Where(x => x.ValueType == ProductVariantAttributeValueType.ProductLinkage) + .ToList() + .Each(x => + { + var linkedProduct = GetProductById(x.LinkedProductId); + if (linkedProduct != null) + AdjustInventory(linkedProduct, decrease, quantity * x.Quantity, ""); + }); - return result; + return result; } - - public virtual void UpdateHasTierPricesProperty(Product product) + + public virtual void UpdateHasTierPricesProperty(Product product) { - Guard.NotNull(product, nameof(product)); + Guard.NotNull(product, nameof(product)); - var prevValue = product.HasTierPrices; - product.HasTierPrices = product.TierPrices.Count > 0; - if (prevValue != product.HasTierPrices) - UpdateProduct(product); + var prevValue = product.HasTierPrices; + product.HasTierPrices = product.TierPrices.Count > 0; + if (prevValue != product.HasTierPrices) + UpdateProduct(product); } - public virtual void UpdateLowestAttributeCombinationPriceProperty(Product product) - { - Guard.NotNull(product, nameof(product)); + public virtual void UpdateLowestAttributeCombinationPriceProperty(Product product) + { + Guard.NotNull(product, nameof(product)); - var prevValue = product.LowestAttributeCombinationPrice; + var prevValue = product.LowestAttributeCombinationPrice; - product.LowestAttributeCombinationPrice = _productAttributeService.GetLowestCombinationPrice(product.Id); + product.LowestAttributeCombinationPrice = _productAttributeService.GetLowestCombinationPrice(product.Id); - if (prevValue != product.LowestAttributeCombinationPrice) - UpdateProduct(product); - } + if (prevValue != product.LowestAttributeCombinationPrice) + UpdateProduct(product); + } - public virtual void UpdateHasDiscountsApplied(Product product) + public virtual void UpdateHasDiscountsApplied(Product product) { - Guard.NotNull(product, nameof(product)); + Guard.NotNull(product, nameof(product)); - var prevValue = product.HasDiscountsApplied; - product.HasDiscountsApplied = product.AppliedDiscounts.Count > 0; - if (prevValue != product.HasDiscountsApplied) - UpdateProduct(product); + var prevValue = product.HasDiscountsApplied; + product.HasDiscountsApplied = product.AppliedDiscounts.Count > 0; + if (prevValue != product.HasDiscountsApplied) + UpdateProduct(product); } - public virtual Multimap GetProductTagsByProductIds(int[] productIds) - { - Guard.NotNull(productIds, nameof(productIds)); + public virtual Multimap GetProductTagsByProductIds(int[] productIds, bool includeHidden = false) + { + Guard.NotNull(productIds, nameof(productIds)); var map = new Multimap(); if (!productIds.Any()) @@ -604,25 +667,34 @@ public virtual Multimap GetProductTagsByProductIds(int[] produc return map; } - var query = _productRepository.TableUntracked - .Expand(x => x.ProductTags) - .Where(x => productIds.Contains(x.Id)) - .Select(x => new - { - ProductId = x.Id, - Tags = x.ProductTags - }); + /// + var productQuery = _productRepository.TableUntracked + .Expand(x => x.ProductTags) + .Where(x => productIds.Contains(x.Id) && !x.Deleted && !x.IsSystemProduct); - var list = query.ToList(); + if (!includeHidden) + { + productQuery = productQuery.Where(x => x.Visibility == ProductVisibility.Full && x.Published); + } - foreach (var item in list) - { - foreach (var tag in item.Tags) - map.Add(item.ProductId, tag); - } + var items = productQuery + .Select(x => new + { + ProductId = x.Id, + Tags = x.ProductTags.Where(y => includeHidden || y.Published) + }) + .ToList(); - return map; - } + foreach (var item in items) + { + foreach (var tag in item.Tags) + { + map.Add(item.ProductId, tag); + } + } + + return map; + } public virtual Multimap GetAssociatedProductsByProductIds(int[] productIds, bool showHidden = false) { @@ -648,8 +720,8 @@ public virtual Multimap GetAssociatedProductsByProductIds(int[] pr } public virtual Multimap GetAppliedDiscountsByProductIds(int[] productIds) - { - Guard.NotNull(productIds, nameof(productIds)); + { + Guard.NotNull(productIds, nameof(productIds)); var map = new Multimap(); if (!productIds.Any()) @@ -658,21 +730,21 @@ public virtual Multimap GetAppliedDiscountsByProductIds(int[] pro } var query = _productRepository.Table // .TableUntracked does not seem to eager load - .Expand(x => x.AppliedDiscounts.Select(y => y.DiscountRequirements)) - .Where(x => productIds.Contains(x.Id)) - .Select(x => new - { - ProductId = x.Id, - Discounts = x.AppliedDiscounts - }); - - foreach (var item in query.ToList()) - { - map.AddRange(item.ProductId, item.Discounts); - } - - return map; - } + .Expand(x => x.AppliedDiscounts.Select(y => y.RuleSets)) + .Where(x => productIds.Contains(x.Id)) + .Select(x => new + { + ProductId = x.Id, + Discounts = x.AppliedDiscounts + }); + + foreach (var item in query.ToList()) + { + map.AddRange(item.ProductId, item.Discounts); + } + + return map; + } #endregion @@ -680,9 +752,9 @@ public virtual Multimap GetAppliedDiscountsByProductIds(int[] pro public virtual void DeleteRelatedProduct(RelatedProduct relatedProduct) { - Guard.NotNull(relatedProduct, nameof(relatedProduct)); + Guard.NotNull(relatedProduct, nameof(relatedProduct)); - _relatedProductRepository.Delete(relatedProduct); + _relatedProductRepository.Delete(relatedProduct); } public virtual IList GetRelatedProductsByProductId1(int productId1, bool showHidden = false) @@ -701,36 +773,36 @@ public virtual RelatedProduct GetRelatedProductById(int relatedProductId) { if (relatedProductId == 0) return null; - + var relatedProduct = _relatedProductRepository.GetById(relatedProductId); return relatedProduct; } public virtual void InsertRelatedProduct(RelatedProduct relatedProduct) { - Guard.NotNull(relatedProduct, nameof(relatedProduct)); + Guard.NotNull(relatedProduct, nameof(relatedProduct)); - _relatedProductRepository.Insert(relatedProduct); + _relatedProductRepository.Insert(relatedProduct); } public virtual void UpdateRelatedProduct(RelatedProduct relatedProduct) { - Guard.NotNull(relatedProduct, nameof(relatedProduct)); + Guard.NotNull(relatedProduct, nameof(relatedProduct)); - _relatedProductRepository.Update(relatedProduct); + _relatedProductRepository.Update(relatedProduct); } - public virtual int EnsureMutuallyRelatedProducts(int productId1) - { - var relatedProducts = GetRelatedProductsByProductId1(productId1, true); - var productIds = relatedProducts.Select(x => x.ProductId2).ToList(); + public virtual int EnsureMutuallyRelatedProducts(int productId1) + { + var relatedProducts = GetRelatedProductsByProductId1(productId1, true); + var productIds = relatedProducts.Select(x => x.ProductId2).ToList(); - if (productIds.Count > 0 && !productIds.Any(x => x == productId1)) - productIds.Add(productId1); + if (productIds.Count > 0 && !productIds.Any(x => x == productId1)) + productIds.Add(productId1); - int count = EnsureMutuallyRelatedProducts(productIds); - return count; - } + int count = EnsureMutuallyRelatedProducts(productIds); + return count; + } #endregion @@ -738,9 +810,9 @@ public virtual int EnsureMutuallyRelatedProducts(int productId1) public virtual void DeleteCrossSellProduct(CrossSellProduct crossSellProduct) { - Guard.NotNull(crossSellProduct, nameof(crossSellProduct)); + Guard.NotNull(crossSellProduct, nameof(crossSellProduct)); - _crossSellProductRepository.Delete(crossSellProduct); + _crossSellProductRepository.Delete(crossSellProduct); } public virtual IList GetCrossSellProductsByProductId1(int productId1, bool showHidden = false) @@ -755,21 +827,21 @@ orderby csp.Id return crossSellProducts; } - public virtual IList GetCrossSellProductsByProductIds(IEnumerable productIds, bool showHidden = false) - { - Guard.NotNull(productIds, nameof(productIds)); + public virtual IList GetCrossSellProductsByProductIds(IEnumerable productIds, bool showHidden = false) + { + Guard.NotNull(productIds, nameof(productIds)); - var query = from csp in _crossSellProductRepository.Table - join p in _productRepository.Table on csp.ProductId2 equals p.Id - where productIds.Contains(csp.ProductId1) && !p.Deleted && (showHidden || p.Published) - orderby csp.Id - select csp; + var query = from csp in _crossSellProductRepository.Table + join p in _productRepository.Table on csp.ProductId2 equals p.Id + where productIds.Contains(csp.ProductId1) && !p.Deleted && (showHidden || p.Published) + orderby csp.Id + select csp; - var crossSellProducts = query.ToList(); - return crossSellProducts; - } + var crossSellProducts = query.ToList(); + return crossSellProducts; + } - public virtual CrossSellProduct GetCrossSellProductById(int crossSellProductId) + public virtual CrossSellProduct GetCrossSellProductById(int crossSellProductId) { if (crossSellProductId == 0) return null; @@ -780,151 +852,152 @@ public virtual CrossSellProduct GetCrossSellProductById(int crossSellProductId) public virtual void InsertCrossSellProduct(CrossSellProduct crossSellProduct) { - Guard.NotNull(crossSellProduct, nameof(crossSellProduct)); + Guard.NotNull(crossSellProduct, nameof(crossSellProduct)); - _crossSellProductRepository.Insert(crossSellProduct); + _crossSellProductRepository.Insert(crossSellProduct); } public virtual void UpdateCrossSellProduct(CrossSellProduct crossSellProduct) { - Guard.NotNull(crossSellProduct, nameof(crossSellProduct)); + Guard.NotNull(crossSellProduct, nameof(crossSellProduct)); - _crossSellProductRepository.Update(crossSellProduct); + _crossSellProductRepository.Update(crossSellProduct); } - public virtual IList GetCrosssellProductsByShoppingCart(IList cart, int numberOfProducts) - { - var result = new List(); + public virtual IList GetCrosssellProductsByShoppingCart(IList cart, int numberOfProducts) + { + var result = new List(); - if (numberOfProducts == 0) - return result; + if (numberOfProducts == 0) + return result; - if (cart == null || cart.Count == 0) - return result; + if (cart == null || cart.Count == 0) + return result; - var cartProductIds = new HashSet(cart.Select(x => x.Item.ProductId)); - var csItems = GetCrossSellProductsByProductIds(cartProductIds); - var productIdsToLoad = new HashSet(csItems.Select(x => x.ProductId2).Except(cartProductIds)); + var cartProductIds = new HashSet(cart.Select(x => x.Item.ProductId)); + var csItems = GetCrossSellProductsByProductIds(cartProductIds); + var productIdsToLoad = new HashSet(csItems.Select(x => x.ProductId2).Except(cartProductIds)); - if (productIdsToLoad.Count > 0) - { - result.AddRange(GetProductsByIds(productIdsToLoad.Take(numberOfProducts).ToArray())); - } + if (productIdsToLoad.Count > 0) + { + result.AddRange(GetProductsByIds(productIdsToLoad.Take(numberOfProducts).ToArray())); + } - return result; - } + return result; + } - public virtual int EnsureMutuallyCrossSellProducts(int productId1) - { - var crossSellProducts = GetCrossSellProductsByProductId1(productId1, true); - var productIds = crossSellProducts.Select(x => x.ProductId2).ToList(); + public virtual int EnsureMutuallyCrossSellProducts(int productId1) + { + var crossSellProducts = GetCrossSellProductsByProductId1(productId1, true); + var productIds = crossSellProducts.Select(x => x.ProductId2).ToList(); - if (productIds.Count > 0 && !productIds.Any(x => x == productId1)) - productIds.Add(productId1); + if (productIds.Count > 0 && !productIds.Any(x => x == productId1)) + productIds.Add(productId1); - int count = EnsureMutuallyCrossSellProducts(productIds); - return count; - } + int count = EnsureMutuallyCrossSellProducts(productIds); + return count; + } #endregion - + #region Tier prices - + public virtual void DeleteTierPrice(TierPrice tierPrice) { - Guard.NotNull(tierPrice, nameof(tierPrice)); + Guard.NotNull(tierPrice, nameof(tierPrice)); - _tierPriceRepository.Delete(tierPrice); + _tierPriceRepository.Delete(tierPrice); } public virtual TierPrice GetTierPriceById(int tierPriceId) { if (tierPriceId == 0) return null; - + var tierPrice = _tierPriceRepository.GetById(tierPriceId); return tierPrice; } - public virtual Multimap GetTierPricesByProductIds(int[] productIds, Customer customer = null, int storeId = 0) - { - Guard.NotNull(productIds, nameof(productIds)); + public virtual Multimap GetTierPricesByProductIds(int[] productIds, Customer customer = null, int storeId = 0) + { + Guard.NotNull(productIds, nameof(productIds)); if (!productIds.Any()) { return new Multimap(); } - var query = - from x in _tierPriceRepository.TableUntracked - where productIds.Contains(x.ProductId) - select x; + var query = _tierPriceRepository.TableUntracked + .Include(x => x.CustomerRole) + .Where(x => productIds.Contains(x.ProductId)); - if (storeId != 0) - query = query.Where(x => x.StoreId == 0 || x.StoreId == storeId); + if (storeId != 0) + query = query.Where(x => x.StoreId == 0 || x.StoreId == storeId); - query = query.OrderBy(x => x.ProductId).ThenBy(x => x.Quantity); - - var list = query.ToList(); + var list = query + .ToList() + // Sorting locally is most likely faster + .OrderBy(x => x.ProductId) + .ThenBy(x => x.Quantity) + .AsEnumerable(); - if (customer != null) - list = list.FilterForCustomer(customer).ToList(); + if (customer != null) + list = list.FilterForCustomer(customer).ToList(); - var map = list - .ToMultimap(x => x.ProductId, x => x); + var map = list.ToMultimap(x => x.ProductId, x => x); - return map; - } + return map; + } public virtual void InsertTierPrice(TierPrice tierPrice) { - Guard.NotNull(tierPrice, nameof(tierPrice)); + Guard.NotNull(tierPrice, nameof(tierPrice)); - _tierPriceRepository.Insert(tierPrice); + _tierPriceRepository.Insert(tierPrice); } public virtual void UpdateTierPrice(TierPrice tierPrice) { - Guard.NotNull(tierPrice, nameof(tierPrice)); + Guard.NotNull(tierPrice, nameof(tierPrice)); - _tierPriceRepository.Update(tierPrice); + _tierPriceRepository.Update(tierPrice); } #endregion #region Product pictures - public virtual void DeleteProductPicture(ProductPicture productPicture) + public virtual void DeleteProductPicture(ProductMediaFile productPicture) { - Guard.NotNull(productPicture, nameof(productPicture)); + Guard.NotNull(productPicture, nameof(productPicture)); - UnassignDeletedPictureFromVariantCombinations(productPicture); + UnassignDeletedPictureFromVariantCombinations(productPicture); - _productPictureRepository.Delete(productPicture); + _productFileRepository.Delete(productPicture); } - private void UnassignDeletedPictureFromVariantCombinations(ProductPicture productPicture) + private void UnassignDeletedPictureFromVariantCombinations(ProductMediaFile productPicture) { - var picId = productPicture.Id; + var picId = productPicture.MediaFileId; bool touched = false; - var combinations = - from c in this._productVariantAttributeCombinationRepository.Table - where c.ProductId == productPicture.Product.Id && !String.IsNullOrEmpty(c.AssignedPictureIds) - select c; - - foreach (var c in combinations) - { - var ids = c.GetAssignedPictureIds().ToList(); - if (ids.Contains(picId)) - { - ids.Remove(picId); - //c.AssignedPictureIds = ids.Count > 0 ? String.Join(",", ids) : null; - c.SetAssignedPictureIds(ids.ToArray()); - touched = true; - // we will save after we're done. It's faster. - } - } + var combinations = + from c in _productVariantAttributeCombinationRepository.Table + where c.ProductId == productPicture.Product.Id && !String.IsNullOrEmpty(c.AssignedMediaFileIds) + select c; + + foreach (var c in combinations) + { + var ids = c.GetAssignedMediaIds().ToList(); + if (ids.Contains(picId)) + { + ids.Remove(picId); + //c.AssignedPictureIds = ids.Count > 0 ? String.Join(",", ids) : null; + c.SetAssignedMediaIds(ids.ToArray()); + touched = true; + // we will save after we're done. It's faster. + } + } // save in one shot! if (touched) @@ -933,186 +1006,217 @@ from c in this._productVariantAttributeCombinationRepository.Table } } - public virtual IList GetProductPicturesByProductId(int productId) + public virtual IList GetProductPicturesByProductId(int productId, int numberOfPictures = 0) { - var query = from pp in _productPictureRepository.Table - where pp.ProductId == productId - orderby pp.DisplayOrder - select pp; - var productPictures = query.ToList(); - return productPictures; + if (productId == 0) + { + return new List(); + } + + var query = _productFileRepository.Table + .Include(x => x.MediaFile) + .Where(x => x.ProductId == productId); + + if (numberOfPictures > 0) + { + query = query + .OrderBy(x => x.DisplayOrder) + .Take(() => numberOfPictures); + } + else + { + query = query.OrderBy(x => x.DisplayOrder); + } + + var productFiles = query.ToList(); + return productFiles; } - public virtual Multimap GetProductPicturesByProductIds(int[] productIds, bool onlyFirstPicture = false) - { - Guard.NotNull(productIds, nameof(productIds)); + public virtual Multimap GetProductPicturesByProductIds( + int[] productIds, + int? maxPicturesPerProduct = null, + MediaLoadFlags flags = MediaLoadFlags.None) + { + if (!(productIds?.Any() ?? false)) + { + return new Multimap(); + } - if (!productIds.Any()) + var take = maxPicturesPerProduct ?? int.MaxValue; + + var query = _productFileRepository.TableUntracked + .Where(pf => productIds.Contains(pf.ProductId)) + .GroupBy(pf => pf.ProductId, x => x) + .SelectMany(pf => pf.OrderBy(x => x.DisplayOrder).Take(take)); + + // For eager loading apply Include() after GroupBy(). + if (flags == MediaLoadFlags.None) + { + query = query.Include(pp => pp.MediaFile); + } + else { - return new Multimap(); + if (flags.HasFlag(MediaLoadFlags.WithBlob)) + { + query = query.Include(pp => pp.MediaFile.MediaStorage); + } + if (flags.HasFlag(MediaLoadFlags.WithFolder)) + { + query = query.Include(pp => pp.MediaFile.Folder); + } + if (flags.HasFlag(MediaLoadFlags.WithTags)) + { + query = query.Include(pp => pp.MediaFile.Tags); + } + if (flags.HasFlag(MediaLoadFlags.WithTracks)) + { + query = query.Include(pp => pp.MediaFile.Tracks); + } } - var query = - from pp in _productPictureRepository.TableUntracked.Expand(x => x.Picture) - where productIds.Contains(pp.ProductId) - orderby pp.ProductId, pp.DisplayOrder - select pp; - - if (onlyFirstPicture) - { - var map = query.GroupBy(x => x.ProductId, x => x) - .Select(x => x.FirstOrDefault()) - .ToList() - .ToMultimap(x => x.ProductId, x => x); - - return map; - } - else - { - var map = query - .ToList() - .ToMultimap(x => x.ProductId, x => x); - - return map; - } - } - - public virtual ProductPicture GetProductPictureById(int productPictureId) + var map = query + .ToList() + .ToMultimap(x => x.ProductId, x => x); + + return map; + } + + public virtual ProductMediaFile GetProductPictureById(int productPictureId) { if (productPictureId == 0) return null; - var pp = _productPictureRepository.GetById(productPictureId); + var pp = _productFileRepository.GetById(productPictureId); return pp; } - public virtual void InsertProductPicture(ProductPicture productPicture) + public virtual void InsertProductPicture(ProductMediaFile productPicture) { - Guard.NotNull(productPicture, nameof(productPicture)); + Guard.NotNull(productPicture, nameof(productPicture)); - _productPictureRepository.Insert(productPicture); + _productFileRepository.Insert(productPicture); } /// /// Updates a product picture /// /// Product picture - public virtual void UpdateProductPicture(ProductPicture productPicture) + public virtual void UpdateProductPicture(ProductMediaFile productPicture) { - Guard.NotNull(productPicture, nameof(productPicture)); + Guard.NotNull(productPicture, nameof(productPicture)); - _productPictureRepository.Update(productPicture); + _productFileRepository.Update(productPicture); } #endregion - #region Bundled products + #region Bundled products - public virtual void InsertBundleItem(ProductBundleItem bundleItem) - { - Guard.NotNull(bundleItem, nameof(bundleItem)); + public virtual void InsertBundleItem(ProductBundleItem bundleItem) + { + Guard.NotNull(bundleItem, nameof(bundleItem)); - if (bundleItem.BundleProductId == 0) - throw new SmartException("BundleProductId of a bundle item cannot be 0."); + if (bundleItem.BundleProductId == 0) + throw new SmartException("BundleProductId of a bundle item cannot be 0."); - if (bundleItem.ProductId == 0) - throw new SmartException("ProductId of a bundle item cannot be 0."); + if (bundleItem.ProductId == 0) + throw new SmartException("ProductId of a bundle item cannot be 0."); - if (bundleItem.ProductId == bundleItem.BundleProductId) - throw new SmartException("A bundle item cannot be an element of itself."); + if (bundleItem.ProductId == bundleItem.BundleProductId) + throw new SmartException("A bundle item cannot be an element of itself."); - _productBundleItemRepository.Insert(bundleItem); - } + _productBundleItemRepository.Insert(bundleItem); + } - public virtual void UpdateBundleItem(ProductBundleItem bundleItem) - { - Guard.NotNull(bundleItem, nameof(bundleItem)); + public virtual void UpdateBundleItem(ProductBundleItem bundleItem) + { + Guard.NotNull(bundleItem, nameof(bundleItem)); - _productBundleItemRepository.Update(bundleItem); - } + _productBundleItemRepository.Update(bundleItem); + } - public virtual void DeleteBundleItem(ProductBundleItem bundleItem) - { - Guard.NotNull(bundleItem, nameof(bundleItem)); + public virtual void DeleteBundleItem(ProductBundleItem bundleItem) + { + Guard.NotNull(bundleItem, nameof(bundleItem)); - // remove bundles from shopping carts (otherwise bundle item cannot be deleted) - var parentCartItemIds = _shoppingCartItemRepository.TableUntracked - .Where(x => x.BundleItemId == bundleItem.Id && x.ParentItemId != null) - .Select(x => x.ParentItemId) - .ToList(); + // remove bundles from shopping carts (otherwise bundle item cannot be deleted) + var parentCartItemIds = _shoppingCartItemRepository.TableUntracked + .Where(x => x.BundleItemId == bundleItem.Id && x.ParentItemId != null) + .Select(x => x.ParentItemId) + .ToList(); - if (parentCartItemIds.Any()) - { - var cartItems = _shoppingCartItemRepository.Table - .Where(x => parentCartItemIds.Contains(x.Id)) - .ToList(); + if (parentCartItemIds.Any()) + { + var cartItems = _shoppingCartItemRepository.Table + .Where(x => parentCartItemIds.Contains(x.Id)) + .ToList(); - foreach (var parentItem in cartItems) - { - var childItems = _shoppingCartItemRepository.Table - .Where(x => x.ParentItemId != null && x.ParentItemId.Value == parentItem.Id && x.Id != parentItem.Id) - .ToList(); + foreach (var parentItem in cartItems) + { + var childItems = _shoppingCartItemRepository.Table + .Where(x => x.ParentItemId != null && x.ParentItemId.Value == parentItem.Id && x.Id != parentItem.Id) + .ToList(); - childItems.Each(x => _shoppingCartItemRepository.Delete(x)); + childItems.Each(x => _shoppingCartItemRepository.Delete(x)); - _shoppingCartItemRepository.Delete(parentItem); - } - } + _shoppingCartItemRepository.Delete(parentItem); + } + } - // delete bundle item - _productBundleItemRepository.Delete(bundleItem); - } + // delete bundle item + _productBundleItemRepository.Delete(bundleItem); + } - public virtual ProductBundleItem GetBundleItemById(int bundleItemId) - { - if (bundleItemId == 0) - return null; + public virtual ProductBundleItem GetBundleItemById(int bundleItemId) + { + if (bundleItemId == 0) + return null; - return _productBundleItemRepository.GetById(bundleItemId); - } + return _productBundleItemRepository.GetById(bundleItemId); + } public virtual IList GetBundleItems(int bundleProductId, bool showHidden = false) - { - var query = - from pbi in _productBundleItemRepository.Table - join p in _productRepository.Table on pbi.ProductId equals p.Id - where pbi.BundleProductId == bundleProductId && !p.Deleted && (showHidden || (pbi.Published && p.Published)) - orderby pbi.DisplayOrder - select pbi; + { + var query = + from pbi in _productBundleItemRepository.Table + join p in _productRepository.Table on pbi.ProductId equals p.Id + where pbi.BundleProductId == bundleProductId && !p.Deleted && (showHidden || (pbi.Published && p.Published)) + orderby pbi.DisplayOrder + select pbi; - query = query.Include(x => x.Product); + query = query.Include(x => x.Product); - var bundleItemData = new List(); + var bundleItemData = new List(); - query.ToList().Each(x => bundleItemData.Add(new ProductBundleItemData(x))); + query.ToList().Each(x => bundleItemData.Add(new ProductBundleItemData(x))); - return bundleItemData; - } + return bundleItemData; + } public virtual Multimap GetBundleItemsByProductIds(int[] productIds, bool showHidden = false) - { - Guard.NotNull(productIds, nameof(productIds)); + { + Guard.NotNull(productIds, nameof(productIds)); if (!productIds.Any()) { return new Multimap(); } - var query = - from pbi in _productBundleItemRepository.TableUntracked - join p in _productRepository.TableUntracked on pbi.ProductId equals p.Id - where productIds.Contains(pbi.BundleProductId) && !p.Deleted && (showHidden || (pbi.Published && p.Published)) - orderby pbi.DisplayOrder - select pbi; + var query = + from pbi in _productBundleItemRepository.TableUntracked + join p in _productRepository.TableUntracked on pbi.ProductId equals p.Id + where productIds.Contains(pbi.BundleProductId) && !p.Deleted && (showHidden || (pbi.Published && p.Published)) + orderby pbi.DisplayOrder + select pbi; var map = query .Include(x => x.Product) .Include(x => x.BundleProduct) .ToList() - .ToMultimap(x => x.BundleProductId, x => x); + .ToMultimap(x => x.BundleProductId, x => x); - return map; - } + return map; + } public virtual bool IsBundleItem(int productId) { diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductTagService.cs b/src/Libraries/SmartStore.Services/Catalog/ProductTagService.cs index 798df87df0..0a58afc7b0 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductTagService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductTagService.cs @@ -7,197 +7,84 @@ using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Stores; -using SmartStore.Core.Events; namespace SmartStore.Services.Catalog { - /// - /// Product tag service - /// - public partial class ProductTagService : IProductTagService + public partial class ProductTagService : ScopedServiceBase, IProductTagService { - #region Constants - - /// - /// Key for caching - /// - /// - /// {0} : store ID - /// - private const string PRODUCTTAG_COUNT_KEY = "producttag:count-{0}"; - - /// - /// Key pattern to clear cache - /// - private const string PRODUCTTAG_PATTERN_KEY = "producttag:*"; - - #endregion + /// + /// Key for caching + /// + /// + /// {0} : store ID + /// {1} : include hidden + /// + private const string PRODUCTTAG_COUNT_KEY = "producttag:count-{0}-{1}"; - #region Fields + /// + /// Key pattern to clear cache + /// + private const string PRODUCTTAG_PATTERN_KEY = "producttag:*"; private readonly IRepository _productTagRepository; - private readonly IRepository _storeMappingRepository; - private readonly IDataProvider _dataProvider; - private readonly IDbContext _dbContext; - private readonly CommonSettings _commonSettings; - private readonly ICacheManager _cacheManager; - private readonly IEventPublisher _eventPublisher; - - #endregion + private readonly IRepository _storeMappingRepository; + private readonly IDataProvider _dataProvider; + private readonly IDbContext _dbContext; + private readonly CommonSettings _commonSettings; + private readonly ICacheManager _cacheManager; - #region Ctor - - /// - /// Ctor - /// - /// Product tag repository - /// Data provider - /// Database Context - /// Common settings - /// Cache manager - /// Event published public ProductTagService( - IRepository productTagRepository, - IRepository storeMappingRepository, - IDataProvider dataProvider, - IDbContext dbContext, - CommonSettings commonSettings, - ICacheManager cacheManager, - IEventPublisher eventPublisher) + IRepository productTagRepository, + IRepository storeMappingRepository, + IDataProvider dataProvider, + IDbContext dbContext, + CommonSettings commonSettings, + ICacheManager cacheManager) { _productTagRepository = productTagRepository; - _storeMappingRepository = storeMappingRepository; - _dataProvider = dataProvider; - _dbContext = dbContext; - _commonSettings = commonSettings; - _cacheManager = cacheManager; - _eventPublisher = eventPublisher; - - QuerySettings = DbQuerySettings.Default; - } - - public DbQuerySettings QuerySettings { get; set; } - - #endregion - - #region Nested classes - - private class ProductTagWithCount - { - public int ProductTagId { get; set; } - public int ProductCount { get; set; } - } - - #endregion - - #region Utilities - - /// - /// Get product count for each of existing product tag - /// - /// Store identifier - /// 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); - }); - } - - #endregion - - #region Methods + _storeMappingRepository = storeMappingRepository; + _dataProvider = dataProvider; + _dbContext = dbContext; + _commonSettings = commonSettings; + _cacheManager = cacheManager; - /// - /// Delete a product tag - /// - /// Product tag - public virtual void DeleteProductTag(ProductTag productTag) - { - if (productTag == null) - throw new ArgumentNullException("productTag"); - - _productTagRepository.Delete(productTag); - - //cache - _cacheManager.RemoveByPattern(PRODUCTTAG_PATTERN_KEY); + QuerySettings = DbQuerySettings.Default; } - /// - /// Gets all product tags - /// - /// Product tags - public virtual IList GetAllProductTags() + public DbQuerySettings QuerySettings { get; set; } + + public virtual IList GetAllProductTags(bool includeHidden = false) { var query = _productTagRepository.Table; + + if (!includeHidden) + { + query = query.Where(x => x.Published); + } + var productTags = query.ToList(); return productTags; } - /// - /// Gets all product tag names - /// - /// Product tag names as list public virtual IList GetAllProductTagNames() { - var query = from pt in _productTagRepository.Table + var query = from pt in _productTagRepository.TableUntracked orderby pt.Name ascending select pt.Name; return query.ToList(); } - /// - /// Gets product tag - /// - /// Product tag identifier - /// Product tag public virtual ProductTag GetProductTagById(int productTagId) { if (productTagId == 0) + { return null; + } var productTag = _productTagRepository.GetById(productTagId); return productTag; } - /// - /// Gets product tag by name - /// - /// Product tag name - /// Product tag public virtual ProductTag GetProductTagByName(string name) { var query = from pt in _productTagRepository.Table @@ -208,50 +95,217 @@ public virtual ProductTag GetProductTagByName(string name) return productTag; } - /// - /// Inserts a product tag - /// - /// Product tag + public virtual void DeleteProductTag(ProductTag productTag) + { + if (productTag != null) + { + _productTagRepository.Delete(productTag); + + HasChanges = true; + + if (!IsInScope) + { + _cacheManager.RemoveByPattern(PRODUCTTAG_PATTERN_KEY); + } + } + } + public virtual void InsertProductTag(ProductTag productTag) { - if (productTag == null) - throw new ArgumentNullException("productTag"); + Guard.NotNull(productTag, nameof(productTag)); _productTagRepository.Insert(productTag); - _cacheManager.RemoveByPattern(PRODUCTTAG_PATTERN_KEY); + HasChanges = true; + + if (!IsInScope) + { + _cacheManager.RemoveByPattern(PRODUCTTAG_PATTERN_KEY); + } } - /// - /// Updates the product tag - /// - /// Product tag public virtual void UpdateProductTag(ProductTag productTag) { - if (productTag == null) - throw new ArgumentNullException("productTag"); + Guard.NotNull(productTag, nameof(productTag)); _productTagRepository.Update(productTag); - _cacheManager.RemoveByPattern(PRODUCTTAG_PATTERN_KEY); + HasChanges = true; + + if (!IsInScope) + { + _cacheManager.RemoveByPattern(PRODUCTTAG_PATTERN_KEY); + } + } + + public virtual void UpdateProductTags(Product product, string[] tagNames) + { + Guard.NotNull(product, nameof(product)); + + _productTagRepository.Context.LoadCollection(product, (Product x) => x.ProductTags); + + if (!(tagNames?.Any() ?? false)) + { + // Remove all tag mappings. + if (product.ProductTags.Any()) + { + HasChanges = true; + product.ProductTags.Clear(); + } + } + else + { + // Remove tag mappings. + var tagsToRemove = new List(); + var newTagNames = new HashSet(tagNames + .Select(x => x.TrimSafe()) + .Where(x => x.HasValue()), + StringComparer.OrdinalIgnoreCase); + + foreach (var existingTag in product.ProductTags) + { + var found = false; + foreach (var tagName in newTagNames) + { + if (existingTag.Name.IsCaseInsensitiveEqual(tagName)) + { + found = true; + break; + } + } + if (!found) + { + tagsToRemove.Add(existingTag); + } + } + + foreach (var tag in tagsToRemove) + { + HasChanges = true; + product.ProductTags.Remove(tag); + } + + // Add tag mappings. + if (newTagNames.Any()) + { + var oldAutoCommit = _productTagRepository.AutoCommitEnabled; + + // True because tags must be saved and assigned an id prior adding a mapping. + _productTagRepository.AutoCommitEnabled = true; + + try + { + foreach (var name in newTagNames) + { + ProductTag tag = null; + var existingTag = GetProductTagByName(name); + + if (existingTag == null) + { + tag = new ProductTag { Name = name, Published = true }; + + HasChanges = true; + _productTagRepository.Insert(tag); + } + else + { + tag = existingTag; + } + + if (!product.ProductTags.Any(x => x.Id == tag.Id)) + { + HasChanges = true; + product.ProductTags.Add(tag); + } + } + } + finally + { + _productTagRepository.AutoCommitEnabled = oldAutoCommit; + } + } + } + + if (HasChanges && !IsInScope) + { + _cacheManager.RemoveByPattern(PRODUCTTAG_PATTERN_KEY); + } + } + + public virtual int GetProductCount(int productTagId, int storeId, bool includeHidden = false) + { + var dictionary = GetProductCount(storeId, includeHidden); + + if (dictionary.TryGetValue(productTagId, out var count)) + { + return count; + } + + return 0; + } + + protected virtual Dictionary GetProductCount(int storeId, bool includeHidden) + { + var storeToken = QuerySettings.IgnoreMultiStore ? "0" : storeId.ToString(); + var key = string.Format(PRODUCTTAG_COUNT_KEY, storeId, includeHidden.ToString().ToLower()); + + // TODO: ACL. Remove stored procedure. + + 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; + + var pIncludeHidden = _dataProvider.GetParameter(); + pIncludeHidden.ParameterName = "IncludeHidden"; + pIncludeHidden.Value = includeHidden; + pIncludeHidden.DbType = DbType.Boolean; + + tagCount = _dbContext.SqlQuery("Exec ProductTagCountLoadAll @StoreId, @IncludeHidden", pStoreId, pIncludeHidden); + } + else + { + // Stored procedures aren't supported. Use LINQ. + var query = _productTagRepository.TableUntracked; + if (!includeHidden) + { + query = query.Where(x => x.Published); + } + + tagCount = query + .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.Visibility == ProductVisibility.Full && p.Published && !p.IsSystemProduct && (includeHidden || pt.Published) + select p).Count() : + pt.Products.Count(p => !p.Deleted && p.Visibility == ProductVisibility.Full && p.Published && !p.IsSystemProduct && (includeHidden || pt.Published)) + }); + } + + return tagCount.ToDictionary(x => x.ProductTagId, x => x.ProductCount); + }); } - /// - /// Get number of products - /// - /// Product tag identifier - /// Store identifier - /// Number of products - public virtual int GetProductCount(int productTagId, int storeId) - { - var dictionary = GetProductCount(storeId); - - if (dictionary.ContainsKey(productTagId)) - return dictionary[productTagId]; - - return 0; - } - - #endregion + protected override void OnClearCache() + { + _cacheManager.RemoveByPattern(PRODUCTTAG_PATTERN_KEY); + } + + private class ProductTagWithCount + { + public int ProductTagId { get; set; } + public int ProductCount { get; set; } + } } } diff --git a/src/Libraries/SmartStore.Services/Catalog/ProductTemplateService.cs b/src/Libraries/SmartStore.Services/Catalog/ProductTemplateService.cs index fbf625225e..4ac7112d9b 100644 --- a/src/Libraries/SmartStore.Services/Catalog/ProductTemplateService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/ProductTemplateService.cs @@ -20,7 +20,7 @@ public partial class ProductTemplateService : IProductTemplateService private readonly IEventPublisher _eventPublisher; #endregion - + #region Ctor /// @@ -64,7 +64,7 @@ orderby pt.DisplayOrder var templates = query.ToList(); return templates; } - + /// /// Gets a product template /// @@ -101,7 +101,7 @@ public virtual void UpdateProductTemplate(ProductTemplate productTemplate) _productTemplateRepository.Update(productTemplate); } - + #endregion } } diff --git a/src/Libraries/SmartStore.Services/Catalog/RecentlyViewedProductsService.cs b/src/Libraries/SmartStore.Services/Catalog/RecentlyViewedProductsService.cs index 5ee34a2a5c..e7c26224f7 100644 --- a/src/Libraries/SmartStore.Services/Catalog/RecentlyViewedProductsService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/RecentlyViewedProductsService.cs @@ -3,26 +3,29 @@ using System.Linq; using System.Web; using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Customers; using SmartStore.Services.Security; namespace SmartStore.Services.Catalog { - /// - /// Recently viewed products service - /// - public partial class RecentlyViewedProductsService : IRecentlyViewedProductsService + /// + /// Recently viewed products service + /// + public partial class RecentlyViewedProductsService : IRecentlyViewedProductsService { #region Fields + private readonly ICommonServices _services; private readonly HttpContextBase _httpContext; private readonly IProductService _productService; - private readonly IAclService _aclService; - private readonly CatalogSettings _catalogSettings; + private readonly IAclService _aclService; + private readonly CatalogSettings _catalogSettings; + private readonly PrivacySettings _privacySettings; #endregion #region Ctor - + /// /// Ctor /// @@ -30,15 +33,19 @@ public partial class RecentlyViewedProductsService : IRecentlyViewedProductsServ /// Product service /// Catalog settings public RecentlyViewedProductsService( - HttpContextBase httpContext, - IProductService productService, - IAclService aclService, - CatalogSettings catalogSettings) + ICommonServices services, + HttpContextBase httpContext, + IProductService productService, + IAclService aclService, + CatalogSettings catalogSettings, + PrivacySettings privacySettings) { + _services = services; _httpContext = httpContext; _productService = productService; - _aclService = aclService; + _aclService = aclService; _catalogSettings = catalogSettings; + _privacySettings = privacySettings; } #endregion @@ -87,10 +94,10 @@ protected IList GetRecentlyViewedProductsIds(int number) public virtual IList GetRecentlyViewedProducts(int number) { var productIds = GetRecentlyViewedProductsIds(number); - var recentlyViewedProducts = _productService - .GetProductsByIds(productIds.ToArray()) - .Where(x => x.Published && !x.Deleted && _aclService.Authorize(x)) - .ToList(); + var recentlyViewedProducts = _productService + .GetProductsByIds(productIds.ToArray()) + .Where(x => x.Published && !x.Deleted && !x.IsSystemProduct && _aclService.Authorize(x)) + .ToList(); return recentlyViewedProducts; } @@ -102,37 +109,38 @@ public virtual IList GetRecentlyViewedProducts(int number) public virtual void AddProductToRecentlyViewedList(int productId) { if (!_catalogSettings.RecentlyViewedProductsEnabled) + { return; + } var oldProductIds = GetRecentlyViewedProductsIds(); var newProductIds = new List(oldProductIds); - if (!newProductIds.Contains(productId)) - { - newProductIds.Add(productId); - } + newProductIds.Remove(productId); + newProductIds.Insert(0, productId); - var recentlyViewedCookie = _httpContext.Request.Cookies.Get("SmartStore.RecentlyViewedProducts"); - if (recentlyViewedCookie == null) - { - recentlyViewedCookie = new HttpCookie("SmartStore.RecentlyViewedProducts"); - recentlyViewedCookie.HttpOnly = true; - } + var recentlyViewedCookie = _httpContext.Request.Cookies.Get("SmartStore.RecentlyViewedProducts") ?? new HttpCookie("SmartStore.RecentlyViewedProducts"); recentlyViewedCookie.Values.Clear(); - int maxProducts = _catalogSettings.RecentlyViewedProductsNumber; + var maxProducts = _catalogSettings.RecentlyViewedProductsNumber; if (maxProducts <= 0) - maxProducts = 10; + { + maxProducts = 8; + } - int skip = Math.Max(0, newProductIds.Count - maxProducts); - newProductIds.Skip(skip).Take(maxProducts).Each(x => { + newProductIds.Take(maxProducts).Each(x => + { recentlyViewedCookie.Values.Add("RecentlyViewedProductIds", x.ToString()); }); recentlyViewedCookie.Expires = DateTime.Now.AddDays(10.0); + recentlyViewedCookie.HttpOnly = true; + recentlyViewedCookie.Secure = _services.WebHelper.IsCurrentConnectionSecured(); + recentlyViewedCookie.SameSite = recentlyViewedCookie.Secure ? (SameSiteMode)_privacySettings.SameSiteMode : SameSiteMode.Lax; + _httpContext.Response.Cookies.Set(recentlyViewedCookie); } - + #endregion } } diff --git a/src/Libraries/SmartStore.Services/Catalog/Rules/IProductRuleProvider.cs b/src/Libraries/SmartStore.Services/Catalog/Rules/IProductRuleProvider.cs new file mode 100644 index 0000000000..96dac15098 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Catalog/Rules/IProductRuleProvider.cs @@ -0,0 +1,12 @@ +using SmartStore.Rules; +using SmartStore.Services.Search; + +namespace SmartStore.Services.Catalog.Rules +{ + public interface IProductRuleProvider : IRuleProvider + { + SearchFilterExpressionGroup CreateExpressionGroup(int ruleSetId); + + CatalogSearchResult Search(SearchFilterExpression[] filters, int pageIndex = 0, int pageSize = int.MaxValue); + } +} diff --git a/src/Libraries/SmartStore.Services/Catalog/Rules/ProductRuleEvaluatorTask.cs b/src/Libraries/SmartStore.Services/Catalog/Rules/ProductRuleEvaluatorTask.cs new file mode 100644 index 0000000000..71056d3463 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Catalog/Rules/ProductRuleEvaluatorTask.cs @@ -0,0 +1,157 @@ +using System.Collections.Generic; +using System.Data.Entity; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Data.Utilities; +using SmartStore.Rules; +using SmartStore.Services.Catalog.Rules; +using SmartStore.Services.Tasks; + +namespace SmartStore.Services.Catalog +{ + /// + /// Updates the system assignments to categories for rules. + /// + public partial class ProductRuleEvaluatorTask : AsyncTask + { + protected readonly IRepository _categoryRepository; + protected readonly IRepository _productCategoryRepository; + protected readonly IRuleFactory _ruleFactory; + protected readonly IProductRuleProvider _productRuleProvider; + + public ProductRuleEvaluatorTask( + IRepository categoryRepository, + IRepository productCategoryRepository, + IRuleFactory ruleFactory, + IProductRuleProvider productRuleProvider) + { + _categoryRepository = categoryRepository; + _productCategoryRepository = productCategoryRepository; + _ruleFactory = ruleFactory; + _productRuleProvider = productRuleProvider; + } + + public override async Task ExecuteAsync(TaskExecutionContext ctx) + { + var count = 0; + var numDeleted = 0; + var numAdded = 0; + var numCategories = 0; + var pageSize = 500; + var pageIndex = -1; + + var categoryIds = ctx.Parameters.ContainsKey("CategoryIds") + ? ctx.Parameters["CategoryIds"].ToIntArray() + : null; + + // Hooks are enabled because search index needs to be updated. + using (var scope = new DbContextScope(ctx: _productCategoryRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: true, autoCommit: false)) + { + // Delete existing system mappings. + var deleteQuery = _productCategoryRepository.Table.Where(x => x.IsSystemMapping); + if (categoryIds != null) + { + deleteQuery = deleteQuery.Where(x => categoryIds.Contains(x.CategoryId)); + } + + var pager = new FastPager(deleteQuery, pageSize); + + while (pager.ReadNextPage(out var mappings)) + { + if (ctx.CancellationToken.IsCancellationRequested) + return; + + if (mappings.Any()) + { + _productCategoryRepository.DeleteRange(mappings); + numDeleted += await scope.CommitAsync(); + } + } + + try + { + _productCategoryRepository.Context.DetachEntities(); + } + catch { } + + // Insert new product category mappings. + var categoryQuery = _categoryRepository.TableUntracked.Expand(x => x.RuleSets); + if (categoryIds != null) + { + categoryQuery = categoryQuery.Where(x => categoryIds.Contains(x.Id)); + } + + var categories = await categoryQuery + .Where(x => x.Published && !x.Deleted && x.RuleSets.Any(y => y.IsActive)) + .ToListAsync(); + + numCategories = categories.Count; + + foreach (var category in categories) + { + var ruleSetProductIds = new HashSet(); + + ctx.SetProgress(++count, categories.Count, $"Add product mappings for category \"{category.Name.NaIfEmpty()}\"."); + + // Execute active rule sets and collect product ids. + foreach (var ruleSet in category.RuleSets.Where(x => x.IsActive)) + { + if (ctx.CancellationToken.IsCancellationRequested) + return; + + if (_ruleFactory.CreateExpressionGroup(ruleSet, _productRuleProvider) is SearchFilterExpression expression) + { + pageIndex = -1; + while (true) + { + // Do not touch searchResult.Hits. We only need the product identifiers. + var searchResult = _productRuleProvider.Search(new SearchFilterExpression[] { expression }, ++pageIndex, pageSize); + ruleSetProductIds.AddRange(searchResult.HitsEntityIds); + + if (pageIndex >= (searchResult.TotalHitsCount / pageSize)) + { + break; + } + } + } + } + + // Add mappings. + if (ruleSetProductIds.Any()) + { + foreach (var chunk in ruleSetProductIds.Slice(500)) + { + if (ctx.CancellationToken.IsCancellationRequested) + return; + + foreach (var productId in chunk) + { + _productCategoryRepository.Insert(new ProductCategory + { + ProductId = productId, + CategoryId = category.Id, + IsSystemMapping = true + }); + + ++numAdded; + } + + await scope.CommitAsync(); + } + + try + { + _productCategoryRepository.Context.DetachEntities(); + } + catch { } + } + } + } + + Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Deleted {numDeleted} and added {numAdded} product mappings for {numCategories} categories."); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Catalog/Rules/ProductRuleProvider.cs b/src/Libraries/SmartStore.Services/Catalog/Rules/ProductRuleProvider.cs new file mode 100644 index 0000000000..1db01b8256 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Catalog/Rules/ProductRuleProvider.cs @@ -0,0 +1,495 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using SmartStore.Core; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Localization; +using SmartStore.Core.Plugins; +using SmartStore.Core.Search; +using SmartStore.Rules; +using SmartStore.Rules.Domain; +using SmartStore.Rules.Filters; +using SmartStore.Services.Localization; +using SmartStore.Services.Search; +using SmartStore.Services.Search.Extensions; + +namespace SmartStore.Services.Catalog.Rules +{ + public class ProductRuleProvider : RuleProviderBase, IProductRuleProvider + { + private readonly Lazy> _productAttributeRepository; + private readonly Lazy> _specificationAttributeRepository; + private readonly IRuleFactory _ruleFactory; + private readonly ICommonServices _services; + private readonly ICatalogSearchService _catalogSearchService; + private readonly ICategoryService _categoryService; + private readonly IPluginFinder _pluginFinder; + private readonly CatalogSettings _catalogSettings; + + public ProductRuleProvider( + Lazy> productAttributeRepository, + Lazy> specificationAttributeRepository, + IRuleFactory ruleFactory, + ICommonServices services, + ICatalogSearchService catalogSearchService, + ICategoryService categoryService, + IPluginFinder pluginFinder, + CatalogSettings catalogSettings) + : base(RuleScope.Product) + { + _productAttributeRepository = productAttributeRepository; + _specificationAttributeRepository = specificationAttributeRepository; + _ruleFactory = ruleFactory; + _services = services; + _catalogSearchService = catalogSearchService; + _categoryService = categoryService; + _pluginFinder = pluginFinder; + _catalogSettings = catalogSettings; + } + + public Localizer T { get; set; } = NullLocalizer.Instance; + + public SearchFilterExpressionGroup CreateExpressionGroup(int ruleSetId) + { + return _ruleFactory.CreateExpressionGroup(ruleSetId, this) as SearchFilterExpressionGroup; + } + + public override IRuleExpression VisitRule(RuleEntity rule) + { + var expression = new SearchFilterExpression(); + base.ConvertRule(rule, expression); + expression.Descriptor = ((RuleExpression)expression).Descriptor as SearchFilterDescriptor; + return expression; + } + + public override IRuleExpressionGroup VisitRuleSet(RuleSetEntity ruleSet) + { + var group = new SearchFilterExpressionGroup() + { + Id = ruleSet.Id, + LogicalOperator = ruleSet.LogicalOperator, + IsSubGroup = ruleSet.IsSubGroup, + Value = ruleSet.Id, + RawValue = ruleSet.Id.ToString(), + Provider = this + }; + + return group; + } + + public CatalogSearchResult Search(SearchFilterExpression[] filters, int pageIndex = 0, int pageSize = int.MaxValue) + { + var searchQuery = new CatalogSearchQuery() + .OriginatesFrom("Rule/Search") + .WithLanguage(_services.WorkContext.WorkingLanguage) + .WithCurrency(_services.WorkContext.WorkingCurrency) + .BuildFacetMap(false) + .CheckSpelling(0) + .Slice(pageIndex * pageSize, pageSize) + .SortBy(ProductSortingEnum.CreatedOn); + + if ((filters?.Length ?? 0) == 0) + { + return new CatalogSearchResult(searchQuery); + } + + SearchFilterExpressionGroup group; + + if (filters.Length == 1 && filters[0] is SearchFilterExpressionGroup group2) + { + group = group2; + } + else + { + group = new SearchFilterExpressionGroup(); + group.AddExpressions(filters); + } + + searchQuery = group.ApplyFilters(searchQuery); + + var searchResult = _catalogSearchService.Search(searchQuery); + return searchResult; + } + + protected override IEnumerable LoadDescriptors() + { + var language = _services.WorkContext.WorkingLanguage; + var oneStarStr = T("Search.Facet.1StarAndMore").Text; + var xStarsStr = T("Search.Facet.XStarsAndMore").Text; + + var stores = _services.StoreService.GetAllStores() + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.Name }) + .ToArray(); + + var visibilities = ((ProductVisibility[])Enum.GetValues(typeof(ProductVisibility))) + .Select(x => new RuleValueSelectListOption { Value = ((int)x).ToString(), Text = x.GetLocalizedEnum(_services.Localization) }) + .ToArray(); + + var productTypes = ((ProductType[])Enum.GetValues(typeof(ProductType))) + .Select(x => new RuleValueSelectListOption { Value = ((int)x).ToString(), Text = x.GetLocalizedEnum(_services.Localization) }) + .ToArray(); + + var ratings = FacetUtility.GetRatings() + .Reverse() + .Skip(1) + .Select(x => new RuleValueSelectListOption + { + Value = ((double)x.Value).ToString(CultureInfo.InvariantCulture), + Text = (double)x.Value == 1 ? oneStarStr : xStarsStr.FormatInvariant(x.Value) + }) + .ToArray(); + + #region Special filters + + CatalogSearchQuery categoryFilter(SearchFilterContext ctx, int[] x) + { + if (x?.Any() ?? false) + { + var ids = new HashSet(x); + + if (_catalogSettings.ShowProductsFromSubcategories) + { + var tree = _categoryService.GetCategoryTree(includeHidden: true); + + foreach (var id in x) + { + var node = tree.SelectNodeById(id); + if (node != null) + { + ids.AddRange(node.Flatten(false).Select(y => y.Id)); + } + } + } + + return ctx.Query.WithCategoryIds(_catalogSettings.IncludeFeaturedProductsInNormalLists ? (bool?)null : false, ids.ToArray()); + } + + return ctx.Query; + }; + + CatalogSearchQuery stockQuantityFilter(SearchFilterContext ctx, int x) + { + if (ctx.Expression.Operator == RuleOperator.IsEqualTo || ctx.Expression.Operator == RuleOperator.IsNotEqualTo) + { + return ctx.Query.WithStockQuantity(x, x, ctx.Expression.Operator == RuleOperator.IsEqualTo, ctx.Expression.Operator == RuleOperator.IsEqualTo); + } + else if (ctx.Expression.Operator == RuleOperator.GreaterThanOrEqualTo || ctx.Expression.Operator == RuleOperator.GreaterThan) + { + return ctx.Query.WithStockQuantity(x, null, ctx.Expression.Operator == RuleOperator.GreaterThanOrEqualTo, null); + } + else if (ctx.Expression.Operator == RuleOperator.LessThanOrEqualTo || ctx.Expression.Operator == RuleOperator.LessThan) + { + return ctx.Query.WithStockQuantity(null, x, null, ctx.Expression.Operator == RuleOperator.LessThanOrEqualTo); + } + + return ctx.Query; + }; + + CatalogSearchQuery priceFilter(SearchFilterContext ctx, decimal x) + { + if (ctx.Expression.Operator == RuleOperator.IsEqualTo || ctx.Expression.Operator == RuleOperator.IsNotEqualTo) + { + return ctx.Query.PriceBetween(x, x, ctx.Expression.Operator == RuleOperator.IsEqualTo, ctx.Expression.Operator == RuleOperator.IsEqualTo); + } + else if (ctx.Expression.Operator == RuleOperator.GreaterThanOrEqualTo || ctx.Expression.Operator == RuleOperator.GreaterThan) + { + return ctx.Query.PriceBetween(x, null, ctx.Expression.Operator == RuleOperator.GreaterThanOrEqualTo, null); + } + else if (ctx.Expression.Operator == RuleOperator.LessThanOrEqualTo || ctx.Expression.Operator == RuleOperator.LessThan) + { + return ctx.Query.PriceBetween(null, x, null, ctx.Expression.Operator == RuleOperator.LessThanOrEqualTo); + } + + return ctx.Query; + }; + + CatalogSearchQuery createdFilter(SearchFilterContext ctx, DateTime x) + { + if (ctx.Expression.Operator == RuleOperator.IsEqualTo || ctx.Expression.Operator == RuleOperator.IsNotEqualTo) + { + return ctx.Query.CreatedBetween(x, x, ctx.Expression.Operator == RuleOperator.IsEqualTo, ctx.Expression.Operator == RuleOperator.IsEqualTo); + } + else if (ctx.Expression.Operator == RuleOperator.GreaterThanOrEqualTo || ctx.Expression.Operator == RuleOperator.GreaterThan) + { + return ctx.Query.CreatedBetween(x, null, ctx.Expression.Operator == RuleOperator.GreaterThanOrEqualTo, null); + } + else if (ctx.Expression.Operator == RuleOperator.LessThanOrEqualTo || ctx.Expression.Operator == RuleOperator.LessThan) + { + return ctx.Query.CreatedBetween(null, x, null, ctx.Expression.Operator == RuleOperator.LessThanOrEqualTo); + } + + return ctx.Query; + }; + + #endregion + + var descriptors = new List + { + new SearchFilterDescriptor((ctx, x) => ctx.Query.HasStoreId(x)) + { + Name = "Store", + DisplayName = T("Admin.Rules.FilterDescriptor.Store"), + RuleType = RuleType.Int, + SelectList = new LocalRuleValueSelectList(stores), + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.AllowedCustomerRoles(x)) + { + Name = "CustomerRole", + DisplayName = T("Admin.Rules.FilterDescriptor.IsInCustomerRole"), + RuleType = RuleType.IntArray, + SelectList = new RemoteRuleValueSelectList("CustomerRole") { Multiple = true }, + Operators = new RuleOperator[] { RuleOperator.In } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.PublishedOnly(x)) + { + Name = "Published", + DisplayName = T("Admin.Catalog.Products.Fields.Published"), + RuleType = RuleType.Boolean, + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.AvailableOnly(x)) + { + Name = "AvailableByStock", + DisplayName = T("Products.Availability.InStock"), + RuleType = RuleType.Boolean, + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.AvailableByDate(x)) + { + Name = "AvailableByDate", + DisplayName = T("Admin.Rules.FilterDescriptor.AvailableByDate"), + RuleType = RuleType.Boolean, + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.WithVisibility((ProductVisibility)x)) + { + Name = "Visibility", + DisplayName = T("Admin.Catalog.Products.Fields.Visibility"), + RuleType = RuleType.Int, + SelectList = new LocalRuleValueSelectList(visibilities), + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.WithProductIds(x)) + { + Name = "Product", + DisplayName = T("Common.Entity.Product"), + RuleType = RuleType.IntArray, + SelectList = new RemoteRuleValueSelectList("Product") { Multiple = true }, + Operators = new RuleOperator[] { RuleOperator.In } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.IsProductType((ProductType)x)) + { + Name = "ProductType", + DisplayName = T("Admin.Catalog.Products.Fields.ProductType"), + RuleType = RuleType.Int, + SelectList = new LocalRuleValueSelectList(productTypes), + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor(categoryFilter) + { + Name = "Category", + DisplayName = T("Common.Entity.Category"), + RuleType = RuleType.IntArray, + SelectList = new RemoteRuleValueSelectList("Category") { Multiple = true }, + Operators = new RuleOperator[] { RuleOperator.In } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.WithManufacturerIds(null, x)) + { + Name = "Manufacturer", + DisplayName = T("Common.Entity.Manufacturer"), + RuleType = RuleType.IntArray, + SelectList = new RemoteRuleValueSelectList("Manufacturer") { Multiple = true }, + Operators = new RuleOperator[] { RuleOperator.In } + }, + // Same logic as the filter above product list. + new SearchFilterDescriptor((ctx, x) => ctx.Query.HasAnyCategory(!x)) + { + Name = "WithoutCategory", + DisplayName = T("Admin.Catalog.Products.List.SearchWithoutCategories"), + RuleType = RuleType.Boolean, + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.HasAnyManufacturer(!x)) + { + Name = "WithoutManufacturer", + DisplayName = T("Admin.Catalog.Products.List.SearchWithoutManufacturers"), + RuleType = RuleType.Boolean, + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.WithProductTagIds(x)) + { + Name = "ProductTag", + DisplayName = T("Admin.Catalog.Products.Fields.ProductTags"), + RuleType = RuleType.IntArray, + SelectList = new RemoteRuleValueSelectList("ProductTag") { Multiple = true }, + Operators = new RuleOperator[] { RuleOperator.In } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.WithDeliveryTimeIds(x)) + { + Name = "DeliveryTime", + DisplayName = T("Admin.Catalog.Products.Fields.DeliveryTime"), + RuleType = RuleType.IntArray, + Operators = new RuleOperator[] { RuleOperator.In }, + SelectList = new RemoteRuleValueSelectList("DeliveryTime") { Multiple = true } + }, + new SearchFilterDescriptor(stockQuantityFilter) + { + Name = "StockQuantity", + DisplayName = T("Admin.Catalog.Products.Fields.StockQuantity"), + RuleType = RuleType.Int + }, + new SearchFilterDescriptor(priceFilter) + { + Name = "Price", + DisplayName = T("Admin.Catalog.Products.Fields.Price"), + RuleType = RuleType.Money + }, + new SearchFilterDescriptor(createdFilter) + { + Name = "CreatedOn", + DisplayName = T("Common.CreatedOn"), + RuleType = RuleType.DateTime + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.WithRating(x, null)) + { + Name = "Rating", + DisplayName = T("Admin.Catalog.ProductReviews.Fields.Rating"), + RuleType = RuleType.Float, + Operators = new RuleOperator[] { RuleOperator.GreaterThanOrEqualTo }, + SelectList = new LocalRuleValueSelectList(ratings) + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.HomePageProductsOnly(x)) + { + Name = "HomepageProduct", + DisplayName = T("Admin.Catalog.Products.Fields.ShowOnHomePage"), + RuleType = RuleType.Boolean, + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.DownloadOnly(x)) + { + Name = "Download", + DisplayName = T("Admin.Catalog.Products.Fields.IsDownload"), + RuleType = RuleType.Boolean, + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.RecurringOnly(x)) + { + Name = "Recurring", + DisplayName = T("Admin.Catalog.Products.Fields.IsRecurring"), + RuleType = RuleType.Boolean, + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.ShipEnabledOnly(x)) + { + Name = "ShipEnabled", + DisplayName = T("Admin.Catalog.Products.Fields.IsShipEnabled"), + RuleType = RuleType.Boolean, + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.FreeShippingOnly(x)) + { + Name = "FreeShipping", + DisplayName = T("Admin.Catalog.Products.Fields.IsFreeShipping"), + RuleType = RuleType.Boolean, + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.TaxExemptOnly(x)) + { + Name = "TaxExempt", + DisplayName = T("Admin.Catalog.Products.Fields.IsTaxExempt"), + RuleType = RuleType.Boolean, + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.EsdOnly(x)) + { + Name = "Esd", + DisplayName = T("Admin.Catalog.Products.Fields.IsEsd"), + RuleType = RuleType.Boolean, + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + }, + new SearchFilterDescriptor((ctx, x) => ctx.Query.HasDiscount(x)) + { + Name = "Discount", + DisplayName = T("Admin.Catalog.Products.Fields.HasDiscountsApplied"), + RuleType = RuleType.Boolean, + Operators = new RuleOperator[] { RuleOperator.IsEqualTo } + } + }; + + if (_pluginFinder.GetPluginDescriptorBySystemName("SmartStore.MegaSearchPlus") != null) + { + ISearchFilter[] filters(string fieldName, int parentId, int[] valueIds) + { + return valueIds.Select(id => SearchFilter.ByField(fieldName, id).ExactMatch().NotAnalyzed().HasParent(parentId)).ToArray(); + } + + var pageIndex = -1; + var variantQuery = _productAttributeRepository.Value.TableUntracked + .Where(x => x.AllowFiltering) + .OrderBy(x => x.DisplayOrder); + + while (true) + { + var variants = PagedList.Create(variantQuery, ++pageIndex, 500); + foreach (var variant in variants) + { + var descriptor = new SearchFilterDescriptor((ctx, x) => ctx.Query.WithFilter(SearchFilter.Combined(filters("variantvalueid", variant.Id, x)))) + { + Name = $"Variant{variant.Id}", + DisplayName = variant.GetLocalized(x => x.Name, language, true, false), + GroupKey = "Admin.Catalog.Attributes.ProductAttributes", + RuleType = RuleType.IntArray, + SelectList = new RemoteRuleValueSelectList("VariantValue") { Multiple = true }, + Operators = new RuleOperator[] { RuleOperator.In } + }; + descriptor.Metadata["ParentId"] = variant.Id; + + descriptors.Add(descriptor); + } + if (!variants.HasNextPage) + { + break; + } + } + + pageIndex = -1; + var attributeQuery = _specificationAttributeRepository.Value.TableUntracked + .Where(x => x.AllowFiltering) + .OrderBy(x => x.DisplayOrder); + + while (true) + { + var attributes = PagedList.Create(attributeQuery, ++pageIndex, 500); + foreach (var attr in attributes) + { + var descriptor = new SearchFilterDescriptor((ctx, x) => ctx.Query.WithFilter(SearchFilter.Combined(filters("attrvalueid", attr.Id, x)))) + { + Name = $"Attribute{attr.Id}", + DisplayName = attr.GetLocalized(x => x.Name, language, true, false), + GroupKey = "Admin.Catalog.Attributes.SpecificationAttributes", + RuleType = RuleType.IntArray, + SelectList = new RemoteRuleValueSelectList("AttributeOption") { Multiple = true }, + Operators = new RuleOperator[] { RuleOperator.In } + }; + descriptor.Metadata["ParentId"] = attr.Id; + + descriptors.Add(descriptor); + } + if (!attributes.HasNextPage) + { + break; + } + } + } + + descriptors + .Where(x => x.RuleType == RuleType.Money) + .Each(x => x.Metadata["postfix"] = _services.StoreContext.CurrentStore.PrimaryStoreCurrency.CurrencyCode); + + return descriptors; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Catalog/Rules/SearchFilterDescriptor.cs b/src/Libraries/SmartStore.Services/Catalog/Rules/SearchFilterDescriptor.cs new file mode 100644 index 0000000000..e24d074935 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Catalog/Rules/SearchFilterDescriptor.cs @@ -0,0 +1,40 @@ +using System; +using SmartStore.Rules; +using SmartStore.Services.Search; + +namespace SmartStore.Services.Catalog.Rules +{ + public class SearchFilterContext + { + public CatalogSearchQuery Query { get; set; } + public SearchFilterExpression Expression { get; set; } + } + + + public abstract class SearchFilterDescriptor : RuleDescriptor + { + public SearchFilterDescriptor() + : base(RuleScope.Product) + { + } + + public abstract CatalogSearchQuery ApplyFilter(SearchFilterContext ctx); + } + + public class SearchFilterDescriptor : SearchFilterDescriptor + { + public SearchFilterDescriptor(Func filter) + { + Guard.NotNull(filter, nameof(filter)); + + Filter = filter; + } + + public Func Filter { get; protected set; } + + public override CatalogSearchQuery ApplyFilter(SearchFilterContext ctx) + { + return Filter(ctx, ctx.Expression.Value.Convert()); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Catalog/Rules/SearchFilterExpressionGroup.cs b/src/Libraries/SmartStore.Services/Catalog/Rules/SearchFilterExpressionGroup.cs new file mode 100644 index 0000000000..9aef7548ce --- /dev/null +++ b/src/Libraries/SmartStore.Services/Catalog/Rules/SearchFilterExpressionGroup.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Linq; +using SmartStore.Rules; +using SmartStore.Services.Search; + +namespace SmartStore.Services.Catalog.Rules +{ + public class SearchFilterExpression : RuleExpression + { + public new SearchFilterDescriptor Descriptor { get; set; } + } + + + public class SearchFilterExpressionGroup : SearchFilterExpression, IRuleExpressionGroup + { + private readonly List _expressions = new List(); + + public SearchFilterExpressionGroup() + { + LogicalOperator = LogicalRuleOperator.And; + Operator = RuleOperator.IsEqualTo; + } + + public int RefRuleId { get; set; } + public LogicalRuleOperator LogicalOperator { get; set; } + public bool IsSubGroup { get; set; } + public IRuleProvider Provider { get; set; } + + public IEnumerable Expressions => _expressions; + + public void AddExpressions(params IRuleExpression[] expressions) + { + Guard.NotNull(expressions, nameof(expressions)); + + _expressions.AddRange(expressions.OfType()); + } + + public CatalogSearchQuery ApplyFilters(CatalogSearchQuery query) + { + // HOWTO: LogicalRuleOperator.Or? LinqCatalogSearchService doesn't support it. Really ICombinedSearchFilter of all filters for MegaSearch (weird)? + var ctx = new SearchFilterContext { Query = query }; + + foreach (var expression in Expressions.Cast()) + { + ctx.Expression = expression; + ctx.Query = expression.Descriptor.ApplyFilter(ctx); + } + + return ctx.Query; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Catalog/SpecificationAttributeService.cs b/src/Libraries/SmartStore.Services/Catalog/SpecificationAttributeService.cs index ac5bda82b6..1ad17f078e 100644 --- a/src/Libraries/SmartStore.Services/Catalog/SpecificationAttributeService.cs +++ b/src/Libraries/SmartStore.Services/Catalog/SpecificationAttributeService.cs @@ -10,114 +10,114 @@ namespace SmartStore.Services.Catalog { - public partial class SpecificationAttributeService : ISpecificationAttributeService - { - private readonly IRepository _specificationAttributeRepository; - private readonly IRepository _specificationAttributeOptionRepository; - private readonly IRepository _productSpecificationAttributeRepository; - private readonly IEventPublisher _eventPublisher; - - public SpecificationAttributeService( - IRepository specificationAttributeRepository, - IRepository specificationAttributeOptionRepository, - IRepository productSpecificationAttributeRepository, - IEventPublisher eventPublisher) - { - _specificationAttributeRepository = specificationAttributeRepository; - _specificationAttributeOptionRepository = specificationAttributeOptionRepository; - _productSpecificationAttributeRepository = productSpecificationAttributeRepository; - _eventPublisher = eventPublisher; - - T = NullLocalizer.Instance; - } - - public Localizer T { get; set; } - - #region Specification attribute - - public virtual SpecificationAttribute GetSpecificationAttributeById(int specificationAttributeId) - { - if (specificationAttributeId == 0) - return null; - - return _specificationAttributeRepository.GetById(specificationAttributeId); - } - - public virtual IQueryable GetSpecificationAttributes() - { - var query = - from sa in _specificationAttributeRepository.Table - orderby sa.DisplayOrder, sa.Name - select sa; - - return query; - } - - public virtual IQueryable GetSpecificationAttributesByIds(int[] ids) - { - if (ids == null || ids.Length == 0) - return null; - - var query = - from sa in _specificationAttributeRepository.Table - where ids.Contains(sa.Id) - orderby sa.DisplayOrder, sa.Name - select sa; - - return query; - } - - public virtual void DeleteSpecificationAttribute(SpecificationAttribute specificationAttribute) - { - if (specificationAttribute == null) - throw new ArgumentNullException("specificationAttribute"); - - _specificationAttributeRepository.Delete(specificationAttribute); - } - - public virtual void InsertSpecificationAttribute(SpecificationAttribute specificationAttribute) - { - if (specificationAttribute == null) - throw new ArgumentNullException("specificationAttribute"); - - _specificationAttributeRepository.Insert(specificationAttribute); - } - - public virtual void UpdateSpecificationAttribute(SpecificationAttribute specificationAttribute) - { - if (specificationAttribute == null) - throw new ArgumentNullException("specificationAttribute"); - - _specificationAttributeRepository.Update(specificationAttribute); - } - - #endregion - - #region Specification attribute option - - public virtual SpecificationAttributeOption GetSpecificationAttributeOptionById(int specificationAttributeOptionId) - { - if (specificationAttributeOptionId == 0) - return null; - - return _specificationAttributeOptionRepository.GetById(specificationAttributeOptionId); - } - - public virtual IList GetSpecificationAttributeOptionsBySpecificationAttribute(int specificationAttributeId) - { + public partial class SpecificationAttributeService : ISpecificationAttributeService + { + private readonly IRepository _specificationAttributeRepository; + private readonly IRepository _specificationAttributeOptionRepository; + private readonly IRepository _productSpecificationAttributeRepository; + private readonly IEventPublisher _eventPublisher; + + public SpecificationAttributeService( + IRepository specificationAttributeRepository, + IRepository specificationAttributeOptionRepository, + IRepository productSpecificationAttributeRepository, + IEventPublisher eventPublisher) + { + _specificationAttributeRepository = specificationAttributeRepository; + _specificationAttributeOptionRepository = specificationAttributeOptionRepository; + _productSpecificationAttributeRepository = productSpecificationAttributeRepository; + _eventPublisher = eventPublisher; + + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + #region Specification attribute + + public virtual SpecificationAttribute GetSpecificationAttributeById(int specificationAttributeId) + { + if (specificationAttributeId == 0) + return null; + + return _specificationAttributeRepository.GetById(specificationAttributeId); + } + + public virtual IQueryable GetSpecificationAttributes() + { + var query = + from sa in _specificationAttributeRepository.Table + orderby sa.DisplayOrder, sa.Name + select sa; + + return query; + } + + public virtual IQueryable GetSpecificationAttributesByIds(int[] ids) + { + if (ids == null || ids.Length == 0) + return null; + + var query = + from sa in _specificationAttributeRepository.Table + where ids.Contains(sa.Id) + orderby sa.DisplayOrder, sa.Name + select sa; + + return query; + } + + public virtual void DeleteSpecificationAttribute(SpecificationAttribute specificationAttribute) + { + if (specificationAttribute == null) + throw new ArgumentNullException("specificationAttribute"); + + _specificationAttributeRepository.Delete(specificationAttribute); + } + + public virtual void InsertSpecificationAttribute(SpecificationAttribute specificationAttribute) + { + if (specificationAttribute == null) + throw new ArgumentNullException("specificationAttribute"); + + _specificationAttributeRepository.Insert(specificationAttribute); + } + + public virtual void UpdateSpecificationAttribute(SpecificationAttribute specificationAttribute) + { + if (specificationAttribute == null) + throw new ArgumentNullException("specificationAttribute"); + + _specificationAttributeRepository.Update(specificationAttribute); + } + + #endregion + + #region Specification attribute option + + public virtual SpecificationAttributeOption GetSpecificationAttributeOptionById(int specificationAttributeOptionId) + { + if (specificationAttributeOptionId == 0) + return null; + + return _specificationAttributeOptionRepository.GetById(specificationAttributeOptionId); + } + + public virtual IList GetSpecificationAttributeOptionsBySpecificationAttribute(int specificationAttributeId) + { if (specificationAttributeId == 0) { return new List(); } - var query = from sao in _specificationAttributeOptionRepository.Table - orderby sao.DisplayOrder - where sao.SpecificationAttributeId == specificationAttributeId - select sao; + var query = from sao in _specificationAttributeOptionRepository.Table + orderby sao.DisplayOrder + where sao.SpecificationAttributeId == specificationAttributeId + select sao; - var specificationAttributeOptions = query.ToList(); - return specificationAttributeOptions; - } + var specificationAttributeOptions = query.ToList(); + return specificationAttributeOptions; + } public virtual Multimap GetSpecificationAttributeOptionsBySpecificationAttributeIds(int[] specificationAttributeIds) { @@ -133,94 +133,94 @@ public virtual Multimap GetSpecificationAttri } public virtual void DeleteSpecificationAttributeOption(SpecificationAttributeOption specificationAttributeOption) - { - if (specificationAttributeOption == null) - throw new ArgumentNullException("specificationAttributeOption"); - - _specificationAttributeOptionRepository.Delete(specificationAttributeOption); - } - - public virtual void InsertSpecificationAttributeOption(SpecificationAttributeOption specificationAttributeOption) - { - if (specificationAttributeOption == null) - throw new ArgumentNullException("specificationAttributeOption"); - - _specificationAttributeOptionRepository.Insert(specificationAttributeOption); - } - - public virtual void UpdateSpecificationAttributeOption(SpecificationAttributeOption specificationAttributeOption) - { - if (specificationAttributeOption == null) - throw new ArgumentNullException("specificationAttributeOption"); - - _specificationAttributeOptionRepository.Update(specificationAttributeOption); - } - - #endregion - - #region Product specification attribute - - public virtual void DeleteProductSpecificationAttribute(ProductSpecificationAttribute productSpecificationAttribute) - { - if (productSpecificationAttribute == null) - throw new ArgumentNullException("productSpecificationAttribute"); - - _productSpecificationAttributeRepository.Delete(productSpecificationAttribute); - } - - public virtual IList GetProductSpecificationAttributesByProductId(int productId) - { - return GetProductSpecificationAttributesByProductId(productId, null, null); - } - - public virtual IList GetProductSpecificationAttributesByProductId(int productId, bool? allowFiltering, bool? showOnProductPage) - { - if (productId == 0) - { - return new List(); - } - - if (allowFiltering.HasValue || showOnProductPage.HasValue) - { - // Note: Join or Expand of SpecificationAttribute, both provides the same SQL. - var joinedQuery = - from psa in _productSpecificationAttributeRepository.Table - join sao in _specificationAttributeOptionRepository.Table on psa.SpecificationAttributeOptionId equals sao.Id - where psa.ProductId == productId - select new - { - ProductAttribute = psa, - Attribute = sao.SpecificationAttribute - }; - - if (allowFiltering.HasValue) - { - joinedQuery = joinedQuery.Where(x => - (x.ProductAttribute.AllowFiltering == null && x.Attribute.AllowFiltering == allowFiltering.Value) || - (x.ProductAttribute.AllowFiltering != null && x.ProductAttribute.AllowFiltering == allowFiltering.Value) - ); - } - - if (showOnProductPage.HasValue) - { - joinedQuery = joinedQuery.Where(x => - (x.ProductAttribute.ShowOnProductPage == null && x.Attribute.ShowOnProductPage == showOnProductPage.Value) || - (x.ProductAttribute.ShowOnProductPage != null && x.ProductAttribute.ShowOnProductPage == showOnProductPage.Value) - ); - } - - var query = joinedQuery.Select(x => x.ProductAttribute).OrderBy(x => x.DisplayOrder); - return query.ToListCached(); - } - else - { - var query = _productSpecificationAttributeRepository.Table - .Where(x => x.ProductId == productId) - .OrderBy(x => x.DisplayOrder); - - return query.ToListCached(); - } - } + { + if (specificationAttributeOption == null) + throw new ArgumentNullException("specificationAttributeOption"); + + _specificationAttributeOptionRepository.Delete(specificationAttributeOption); + } + + public virtual void InsertSpecificationAttributeOption(SpecificationAttributeOption specificationAttributeOption) + { + if (specificationAttributeOption == null) + throw new ArgumentNullException("specificationAttributeOption"); + + _specificationAttributeOptionRepository.Insert(specificationAttributeOption); + } + + public virtual void UpdateSpecificationAttributeOption(SpecificationAttributeOption specificationAttributeOption) + { + if (specificationAttributeOption == null) + throw new ArgumentNullException("specificationAttributeOption"); + + _specificationAttributeOptionRepository.Update(specificationAttributeOption); + } + + #endregion + + #region Product specification attribute + + public virtual void DeleteProductSpecificationAttribute(ProductSpecificationAttribute productSpecificationAttribute) + { + if (productSpecificationAttribute == null) + throw new ArgumentNullException("productSpecificationAttribute"); + + _productSpecificationAttributeRepository.Delete(productSpecificationAttribute); + } + + public virtual IList GetProductSpecificationAttributesByProductId(int productId) + { + return GetProductSpecificationAttributesByProductId(productId, null, null); + } + + public virtual IList GetProductSpecificationAttributesByProductId(int productId, bool? allowFiltering, bool? showOnProductPage) + { + if (productId == 0) + { + return new List(); + } + + if (allowFiltering.HasValue || showOnProductPage.HasValue) + { + // Note: Join or Expand of SpecificationAttribute, both provides the same SQL. + var joinedQuery = + from psa in _productSpecificationAttributeRepository.Table + join sao in _specificationAttributeOptionRepository.Table on psa.SpecificationAttributeOptionId equals sao.Id + where psa.ProductId == productId + select new + { + ProductAttribute = psa, + Attribute = sao.SpecificationAttribute + }; + + if (allowFiltering.HasValue) + { + joinedQuery = joinedQuery.Where(x => + (x.ProductAttribute.AllowFiltering == null && x.Attribute.AllowFiltering == allowFiltering.Value) || + (x.ProductAttribute.AllowFiltering != null && x.ProductAttribute.AllowFiltering == allowFiltering.Value) + ); + } + + if (showOnProductPage.HasValue) + { + joinedQuery = joinedQuery.Where(x => + (x.ProductAttribute.ShowOnProductPage == null && x.Attribute.ShowOnProductPage == showOnProductPage.Value) || + (x.ProductAttribute.ShowOnProductPage != null && x.ProductAttribute.ShowOnProductPage == showOnProductPage.Value) + ); + } + + var query = joinedQuery.Select(x => x.ProductAttribute).OrderBy(x => x.DisplayOrder); + return query.ToListCached(); + } + else + { + var query = _productSpecificationAttributeRepository.Table + .Where(x => x.ProductId == productId) + .OrderBy(x => x.DisplayOrder); + + return query.ToListCached(); + } + } public virtual Multimap GetProductSpecificationAttributesByProductIds(int[] productIds) { @@ -244,30 +244,30 @@ public virtual Multimap GetProductSpecificat } public virtual ProductSpecificationAttribute GetProductSpecificationAttributeById(int productSpecificationAttributeId) - { - if (productSpecificationAttributeId == 0) - return null; + { + if (productSpecificationAttributeId == 0) + return null; - var productSpecificationAttribute = _productSpecificationAttributeRepository.GetById(productSpecificationAttributeId); - return productSpecificationAttribute; - } + var productSpecificationAttribute = _productSpecificationAttributeRepository.GetById(productSpecificationAttributeId); + return productSpecificationAttribute; + } - public virtual void InsertProductSpecificationAttribute(ProductSpecificationAttribute productSpecificationAttribute) - { - if (productSpecificationAttribute == null) - throw new ArgumentNullException("productSpecificationAttribute"); + public virtual void InsertProductSpecificationAttribute(ProductSpecificationAttribute productSpecificationAttribute) + { + if (productSpecificationAttribute == null) + throw new ArgumentNullException("productSpecificationAttribute"); - _productSpecificationAttributeRepository.Insert(productSpecificationAttribute); - } + _productSpecificationAttributeRepository.Insert(productSpecificationAttribute); + } - public virtual void UpdateProductSpecificationAttribute(ProductSpecificationAttribute productSpecificationAttribute) - { - if (productSpecificationAttribute == null) - throw new ArgumentNullException("productSpecificationAttribute"); + public virtual void UpdateProductSpecificationAttribute(ProductSpecificationAttribute productSpecificationAttribute) + { + if (productSpecificationAttribute == null) + throw new ArgumentNullException("productSpecificationAttribute"); - _productSpecificationAttributeRepository.Update(productSpecificationAttribute); - } + _productSpecificationAttributeRepository.Update(productSpecificationAttribute); + } - #endregion - } + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/Catalog/TierPriceExtensions.cs b/src/Libraries/SmartStore.Services/Catalog/TierPriceExtensions.cs index c365f7ac12..5e6ff6108f 100644 --- a/src/Libraries/SmartStore.Services/Catalog/TierPriceExtensions.cs +++ b/src/Libraries/SmartStore.Services/Catalog/TierPriceExtensions.cs @@ -11,19 +11,19 @@ namespace SmartStore.Services.Catalog /// public static class TierPriceExtensions { - /// - /// Filter tier prices by a store - /// - /// Tier prices - /// Store identifier - /// Filtered tier prices + /// + /// Filter tier prices by a store + /// + /// Tier prices + /// Store identifier + /// Filtered tier prices public static IEnumerable FilterByStore(this IEnumerable source, int storeId) - { - if (source == null) - throw new ArgumentNullException("source"); + { + if (source == null) + throw new ArgumentNullException("source"); return source.Where(x => x.StoreId == 0 || x.StoreId == storeId); - } + } /// /// Filter tier prices for a customer @@ -33,8 +33,7 @@ public static IEnumerable FilterByStore(this IEnumerable s /// Filtered tier prices public static IEnumerable FilterForCustomer(this IEnumerable source, Customer customer) { - if (source == null) - throw new ArgumentNullException("source"); + Guard.NotNull(source, nameof(source)); foreach (var tierPrice in source) { @@ -44,7 +43,7 @@ public static IEnumerable FilterForCustomer(this IEnumerable cr.Active); + var customerRoles = customer.CustomerRoleMappings.Select(x => x.CustomerRole).Where(cr => cr.Active); if (!customerRoles.Any()) continue; @@ -57,12 +56,10 @@ public static IEnumerable FilterForCustomer(this IEnumerable @@ -74,19 +71,19 @@ public static ICollection RemoveDuplicatedQuantities(this ICollection { if (source == null) throw new ArgumentNullException("source"); - - //find duplicates + + // Find duplicates var query = from tierPrice in source group tierPrice by tierPrice.Quantity into g where g.Count() > 1 select new { Quantity = g.Key, TierPrices = g.ToList() }; foreach (var item in query) { - //find a tier price record with minimum price (we'll not remove it) + // Find a tier price record with minimum price (we'll not remove it) var minTierPrice = item.TierPrices.Aggregate((tp1, tp2) => (tp1.Price < tp2.Price ? tp1 : tp2)); - //remove all other records + // Remove all other records item.TierPrices.Remove(minTierPrice); - item.TierPrices.ForEach(x=> source.Remove(x)); + item.TierPrices.ForEach(x => source.Remove(x)); } return source; diff --git a/src/Libraries/SmartStore.Services/Cms/Blocks/BlockHandlerBase.cs b/src/Libraries/SmartStore.Services/Cms/Blocks/BlockHandlerBase.cs index 03d182c4a3..7344a6b0c6 100644 --- a/src/Libraries/SmartStore.Services/Cms/Blocks/BlockHandlerBase.cs +++ b/src/Libraries/SmartStore.Services/Cms/Blocks/BlockHandlerBase.cs @@ -9,38 +9,39 @@ using System.Web.Mvc.Html; using System.Web.Routing; using Newtonsoft.Json; +using SmartStore.ComponentModel; using SmartStore.Core.Logging; using SmartStore.Services.Localization; using SmartStore.Utilities; namespace SmartStore.Services.Cms.Blocks { - public abstract class BlockHandlerBase : IBlockHandler where T : IBlock - { - public ICommonServices Services { get; set; } + public abstract class BlockHandlerBase : IBlockHandler where T : IBlock + { + public ICommonServices Services { get; set; } public ILogger Logger { get; set; } - public ILocalizedEntityService LocalizedEntityService { get; set; } + public ILocalizedEntityService LocalizedEntityService { get; set; } - public virtual T Create(IBlockEntity entity) - { - return Activator.CreateInstance(); - } + public virtual T Create(IBlockEntity entity) + { + return Activator.CreateInstance(); + } - public virtual T Load(IBlockEntity entity, StoryViewMode viewMode) - { - Guard.NotNull(entity, nameof(entity)); + public virtual T Load(IBlockEntity entity, StoryViewMode viewMode) + { + Guard.NotNull(entity, nameof(entity)); - var block = Create(entity); - var json = entity.Model; + var block = Create(entity); + var json = entity.Model; - if (json.IsEmpty()) - { - return block; - } + if (json.IsEmpty()) + { + return block; + } - JsonConvert.PopulateObject(json, block); + JsonConvert.PopulateObject(json, block); if (block is IBindableBlock bindableBlock) { @@ -49,31 +50,32 @@ public virtual T Load(IBlockEntity entity, StoryViewMode viewMode) } return block; - } + } - public virtual bool IsValid(T block) - { - return true; - } + public virtual bool IsValid(T block) + { + return true; + } - public virtual void Save(T block, IBlockEntity entity) - { - Guard.NotNull(entity, nameof(entity)); + public virtual void Save(T block, IBlockEntity entity) + { + Guard.NotNull(entity, nameof(entity)); - if (block == null) - { - return; - } + if (block == null) + { + return; + } - var settings = new JsonSerializerSettings - { - TypeNameHandling = TypeNameHandling.Objects, - ReferenceLoopHandling = ReferenceLoopHandling.Ignore, - ObjectCreationHandling = ObjectCreationHandling.Replace, - NullValueHandling = NullValueHandling.Ignore - }; + var settings = new JsonSerializerSettings + { + ContractResolver = SmartContractResolver.Instance, + TypeNameHandling = TypeNameHandling.Objects, + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + ObjectCreationHandling = ObjectCreationHandling.Replace, + NullValueHandling = NullValueHandling.Ignore + }; - entity.Model = JsonConvert.SerializeObject(block, Formatting.None, settings); + entity.Model = JsonConvert.SerializeObject(block, Formatting.None, settings); // save BindEntintyName & BindEntintyId if (block is IBindableBlock bindableBlock) @@ -83,55 +85,65 @@ public virtual void Save(T block, IBlockEntity entity) } } + public virtual void AfterSave(IBlockContainer container, IBlockEntity entity) + { + // Default impl does nothing. + } + + public virtual void BeforeRender(IBlockContainer container, StoryViewMode viewMode, IBlockHtmlParts htmlParts) + { + // Default impl does nothing. + } + public virtual string Clone(IBlockEntity sourceEntity, IBlockEntity clonedEntity) - { - return sourceEntity.Model; - } - - public void Render(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHelper) - { - RenderCore(element, templates, htmlHelper, htmlHelper.ViewContext.Writer); - } - - public IHtmlString ToHtmlString(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHelper) - { - using (var writer = new StringWriter(CultureInfo.CurrentCulture)) - { - RenderCore(element, templates, htmlHelper, writer); - return MvcHtmlString.Create(writer.ToString()); - } - } - - protected virtual void RenderCore(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHelper, TextWriter textWriter) - { - RenderByView(element, templates, htmlHelper, textWriter); - } - - protected void RenderByView(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHelper, TextWriter textWriter) - { - Guard.NotNull(element, nameof(element)); - Guard.NotNull(templates, nameof(templates)); - Guard.NotNull(htmlHelper, nameof(htmlHelper)); - Guard.NotNull(textWriter, nameof(textWriter)); - - var viewContext = htmlHelper.ViewContext; - - if (!element.Metadata.IsInbuilt) - { - // Change "area" token in RouteData in order to begin search in the plugin's view folder. - var originalRouteData = htmlHelper.ViewContext.RouteData; - var routeData = new RouteData(originalRouteData.Route, originalRouteData.RouteHandler); - routeData.Values.Merge(originalRouteData.Values); - routeData.DataTokens["area"] = element.Metadata.AreaName; - - viewContext = new ViewContext - { - RouteData = routeData, - RequestContext = htmlHelper.ViewContext.RequestContext - }; - } - - var viewResult = FindFirstView(element.Metadata, templates, viewContext, out var searchedLocations); + { + return sourceEntity.Model; + } + + public void Render(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHelper) + { + RenderCore(element, templates, htmlHelper, htmlHelper.ViewContext.Writer); + } + + public IHtmlString ToHtmlString(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHelper) + { + using (var writer = new StringWriter(CultureInfo.CurrentCulture)) + { + RenderCore(element, templates, htmlHelper, writer); + return MvcHtmlString.Create(writer.ToString()); + } + } + + protected virtual void RenderCore(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHelper, TextWriter textWriter) + { + RenderByView(element, templates, htmlHelper, textWriter); + } + + protected void RenderByView(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHelper, TextWriter textWriter) + { + Guard.NotNull(element, nameof(element)); + Guard.NotNull(templates, nameof(templates)); + Guard.NotNull(htmlHelper, nameof(htmlHelper)); + Guard.NotNull(textWriter, nameof(textWriter)); + + var viewContext = htmlHelper.ViewContext; + + if (!element.Metadata.IsInbuilt) + { + // Change "area" token in RouteData in order to begin search in the plugin's view folder. + var originalRouteData = htmlHelper.ViewContext.RouteData; + var routeData = new RouteData(originalRouteData.Route, originalRouteData.RouteHandler); + routeData.Values.Merge(originalRouteData.Values); + routeData.DataTokens["area"] = element.Metadata.AreaName; + + viewContext = new ViewContext + { + RouteData = routeData, + RequestContext = htmlHelper.ViewContext.RequestContext + }; + } + + var viewResult = FindFirstView(element.Metadata, templates, viewContext, out var searchedLocations); if (viewResult == null) { @@ -140,36 +152,36 @@ protected void RenderByView(IBlockContainer element, IEnumerable templat throw new FileNotFoundException(msg); } - var viewData = new ViewDataDictionary(element.Block); - viewData.TemplateInfo.HtmlFieldPrefix = "Block"; - - viewContext = new ViewContext( - htmlHelper.ViewContext, - viewResult.View, - viewData, - htmlHelper.ViewContext.TempData, - textWriter); - - viewResult.View.Render(viewContext, textWriter); - } - - private ViewEngineResult FindFirstView(IBlockMetadata blockMetadata, IEnumerable templates, ViewContext viewContext, out ICollection searchedLocations) - { - searchedLocations = new List(); - - foreach (var template in templates) - { - var viewName = string.Concat("BlockTemplates/", blockMetadata.SystemName, "/", template); - var viewResult = ViewEngines.Engines.FindPartialView(viewContext, viewName); - searchedLocations.AddRange(viewResult.SearchedLocations); - if (viewResult.View != null) - { - return viewResult; - } - } - - return null; - } + var viewData = new ViewDataDictionary(element.Block); + viewData.TemplateInfo.HtmlFieldPrefix = "Block"; + + viewContext = new ViewContext( + htmlHelper.ViewContext, + viewResult.View, + viewData, + htmlHelper.ViewContext.TempData, + textWriter); + + viewResult.View.Render(viewContext, textWriter); + } + + private ViewEngineResult FindFirstView(IBlockMetadata blockMetadata, IEnumerable templates, ViewContext viewContext, out ICollection searchedLocations) + { + searchedLocations = new List(); + + foreach (var template in templates) + { + var viewName = string.Concat("BlockTemplates/", blockMetadata.SystemName, "/", template); + var viewResult = ViewEngines.Engines.FindPartialView(viewContext, viewName); + searchedLocations.AddRange(viewResult.SearchedLocations); + if (viewResult.View != null) + { + return viewResult; + } + } + + return null; + } protected void RenderByChildAction(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHelper, TextWriter textWriter) { @@ -261,9 +273,10 @@ void endRender() #endregion - protected virtual RouteInfo GetRoute(IBlockContainer element, string template) - => throw new NotImplementedException(); - + protected virtual RouteInfo GetRoute(IBlockContainer element, string template) + { + throw new NotImplementedException(); + } class ActionInvokerWithResultValidator : AsyncControllerActionInvoker { @@ -273,10 +286,13 @@ protected override void InvokeActionResult(ControllerContext controllerContext, { case PartialViewResult _: case ContentResult _: + case EmptyResult _: base.InvokeActionResult(controllerContext, actionResult); break; + case HttpNotFoundResult nfr: + throw new InvalidOperationException(nfr.StatusDescription.NullEmpty() ?? $"The resource was not found ({nfr.StatusCode})."); default: - throw new InvalidOperationException($"The action result type of an MVC route block must either be '{nameof(PartialViewResult)}' or '{nameof(ContentResult)}'"); + throw new InvalidOperationException($"The action result type of an MVC route block must either be '{nameof(PartialViewResult)}', '{nameof(ContentResult)}' or '{nameof(EmptyResult)}'"); } } } diff --git a/src/Libraries/SmartStore.Services/Cms/Blocks/BlockMetadata.cs b/src/Libraries/SmartStore.Services/Cms/Blocks/BlockMetadata.cs index ee65bc8ff5..3d62bf97ba 100644 --- a/src/Libraries/SmartStore.Services/Cms/Blocks/BlockMetadata.cs +++ b/src/Libraries/SmartStore.Services/Cms/Blocks/BlockMetadata.cs @@ -3,77 +3,77 @@ namespace SmartStore.Services.Cms.Blocks { - /// - /// Applies metadata to concrete block types which implement . - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public sealed class BlockAttribute : Attribute - { - public BlockAttribute(string systemName) - { - Guard.NotNull(systemName, nameof(systemName)); + /// + /// Applies metadata to concrete block types which implement . + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class BlockAttribute : Attribute + { + public BlockAttribute(string systemName) + { + Guard.NotNull(systemName, nameof(systemName)); - SystemName = systemName; - } + SystemName = systemName; + } - /// - /// The block system name, e.g. 'html', 'picture' etc. - /// - public string SystemName { get; set; } + /// + /// The block system name, e.g. 'html', 'picture' etc. + /// + public string SystemName { get; set; } - /// - /// The english friendly name of the block - /// - public string FriendlyName { get; set; } + /// + /// The english friendly name of the block + /// + public string FriendlyName { get; set; } - /// - /// The icon class name of the block, e.g. 'fa fa-sitemap' - /// - public string Icon { get; set; } + /// + /// The icon class name of the block, e.g. 'fa fa-sitemap' + /// + public string Icon { get; set; } - /// - /// The order of display - /// - public int DisplayOrder { get; set; } + /// + /// The order of display + /// + public int DisplayOrder { get; set; } - public bool IsInternal { get; set; } - } + public bool IsInternal { get; set; } + } - /// - /// Represents block registration metadata - /// - public interface IBlockMetadata : IProviderMetadata - { - string AreaName { get; } - string Icon { get; } - bool IsInternal { get; } - bool IsInbuilt { get; } - Type BlockClrType { get; } - Type BlockHandlerClrType { get; } - } + /// + /// Represents block registration metadata + /// + public interface IBlockMetadata : IProviderMetadata + { + string AreaName { get; } + string Icon { get; } + bool IsInternal { get; } + bool IsInbuilt { get; } + Type BlockClrType { get; } + Type BlockHandlerClrType { get; } + } - public class BlockMetadata : IBlockMetadata, ICloneable - { - public string AreaName { get; set; } - public string SystemName { get; set; } - public string FriendlyName { get; set; } - public string Description { get; set; } - public string ResourceKeyPattern { get; set; } - public string Icon { get; set; } - public int DisplayOrder { get; set; } - public bool IsInternal { get; set; } - public bool IsInbuilt { get; set; } - public Type BlockClrType { get; set; } - public Type BlockHandlerClrType { get; set; } + public class BlockMetadata : IBlockMetadata, ICloneable + { + public string AreaName { get; set; } + public string SystemName { get; set; } + public string FriendlyName { get; set; } + public string Description { get; set; } + public string ResourceKeyPattern { get; set; } + public string Icon { get; set; } + public int DisplayOrder { get; set; } + public bool IsInternal { get; set; } + public bool IsInbuilt { get; set; } + public Type BlockClrType { get; set; } + public Type BlockHandlerClrType { get; set; } - public BlockMetadata Clone() - { - return (BlockMetadata)this.MemberwiseClone(); - } + public BlockMetadata Clone() + { + return (BlockMetadata)this.MemberwiseClone(); + } - object ICloneable.Clone() - { - return this.MemberwiseClone(); - } - } + object ICloneable.Clone() + { + return this.MemberwiseClone(); + } + } } diff --git a/src/Libraries/SmartStore.Services/Cms/Blocks/IBindableBlock.cs b/src/Libraries/SmartStore.Services/Cms/Blocks/IBindableBlock.cs index 3638b5e31c..94e3ffb4e3 100644 --- a/src/Libraries/SmartStore.Services/Cms/Blocks/IBindableBlock.cs +++ b/src/Libraries/SmartStore.Services/Cms/Blocks/IBindableBlock.cs @@ -3,15 +3,15 @@ namespace SmartStore.Services.Cms.Blocks { - /// - /// When implemented on types, makes the block bindable to - /// product, category and manufacturer entities. The UI will display a 'Data binding' - /// section which allows selection of an entity. - /// - /// - /// The handler for a bindable block type MUST implement . - /// - public interface IBindableBlock : IBlock + /// + /// When implemented on types, makes the block bindable to + /// product, category and manufacturer entities. The UI will display a 'Data binding' + /// section which allows selection of an entity. + /// + /// + /// The handler for a bindable block type MUST implement . + /// + public interface IBindableBlock : IBlock { /// /// The name of the bound entity, e.g. 'product'. @@ -32,22 +32,22 @@ public interface IBindableBlock : IBlock /// bool CanBind { get; } - /// - /// Returns a value to indicate whether the binding source () has been loaded from the store already. - /// - bool IsLoaded { get; } + /// + /// Returns a value to indicate whether the binding source () has been loaded from the store already. + /// + bool IsLoaded { get; } /// /// The data item of the bound entity. /// IDictionary DataItem { get; set; } - /// - /// Returns a value to indicate whether the block has been bound already. - /// A block is considered bound if the binding source dictionary has been - /// applied to the block instance's bindable properties. - /// - bool IsBound { get; set; } + /// + /// Returns a value to indicate whether the block has been bound already. + /// A block is considered bound if the binding source dictionary has been + /// applied to the block instance's bindable properties. + /// + bool IsBound { get; set; } /// /// Resets the data item of the bound entity. After calling this method, @@ -62,19 +62,10 @@ public abstract class BindableBlockBase : IBindableBlock public virtual int? BindEntityId { get; set; } [JsonIgnore] - public bool CanBind - { - get - { - return BindEntityName.HasValue() && BindEntityId.HasValue; - } - } + public bool CanBind => BindEntityName.HasValue() && BindEntityId.HasValue; [JsonIgnore] - public bool IsLoaded - { - get { return DataItem != null; } - } + public bool IsLoaded => DataItem != null; [JsonIgnore] public IDictionary DataItem { get; set; } diff --git a/src/Libraries/SmartStore.Services/Cms/Blocks/IBindableBlockHandler.cs b/src/Libraries/SmartStore.Services/Cms/Blocks/IBindableBlockHandler.cs index 0658902f37..0e15cb8771 100644 --- a/src/Libraries/SmartStore.Services/Cms/Blocks/IBindableBlockHandler.cs +++ b/src/Libraries/SmartStore.Services/Cms/Blocks/IBindableBlockHandler.cs @@ -5,51 +5,51 @@ namespace SmartStore.Services.Cms.Blocks { - /// - /// Retrieves information about UI mapping between template tokens and form controls. - /// - /// - /// only works in conjunction with . - /// Make sure that the handled block type implements . - /// - public interface IBindableBlockHandler : IBlockHandler + /// + /// Retrieves information about UI mapping between template tokens and form controls. + /// + /// + /// only works in conjunction with . + /// Make sure that the handled block type implements . + /// + public interface IBindableBlockHandler : IBlockHandler { - /// - /// Build and returns the template mapping configuration. - /// - /// - TemplateMappingConfiguration GetMappingConfiguration(); - } + /// + /// Build and returns the template mapping configuration. + /// + /// + TemplateMappingConfiguration GetMappingConfiguration(); + } - /// - /// Contans information about UI mapping between template tokens and form controls. - /// - public class TemplateMappingConfiguration + /// + /// Contans information about UI mapping between template tokens and form controls. + /// + public class TemplateMappingConfiguration { - /// - /// A list of template tokens and their corresponding form controls. - /// - public IList Map { get; set; } + /// + /// A list of template tokens and their corresponding form controls. + /// + public IList Map { get; set; } - /// - /// The complete list of field names which can be bound to entity fields. - /// + /// + /// The complete list of field names which can be bound to entity fields. + /// public string[] BindableFields { get; set; } } - /// - /// Block data binding mapping definition for a single field. - /// + /// + /// Block data binding mapping definition for a single field. + /// public class TemplateMapping { - /// - /// Name of the bound field, e.g. 'Title', 'Description', 'Price' etc. - /// - public string FieldName { get; set; } + /// + /// Name of the bound field, e.g. 'Title', 'Description', 'Price' etc. + /// + public string FieldName { get; set; } - /// - /// The 'Liquid' template - /// + /// + /// The 'Liquid' template + /// public string Template { get; set; } } } diff --git a/src/Libraries/SmartStore.Services/Cms/Blocks/IBlock.cs b/src/Libraries/SmartStore.Services/Cms/Blocks/IBlock.cs index 5f08fb785d..4560ff9cff 100644 --- a/src/Libraries/SmartStore.Services/Cms/Blocks/IBlock.cs +++ b/src/Libraries/SmartStore.Services/Cms/Blocks/IBlock.cs @@ -3,11 +3,11 @@ namespace SmartStore.Services.Cms.Blocks { - /// - /// Marker interface for a PageBuilder block implementation. - /// Blocks are handled by corresponding implementations. - /// - public interface IBlock - { - } + /// + /// Marker interface for a PageBuilder block implementation. + /// Blocks are handled by corresponding implementations. + /// + public interface IBlock + { + } } diff --git a/src/Libraries/SmartStore.Services/Cms/Blocks/IBlockContainer.cs b/src/Libraries/SmartStore.Services/Cms/Blocks/IBlockContainer.cs index 9f5e50c931..a88a386a85 100644 --- a/src/Libraries/SmartStore.Services/Cms/Blocks/IBlockContainer.cs +++ b/src/Libraries/SmartStore.Services/Cms/Blocks/IBlockContainer.cs @@ -2,50 +2,50 @@ namespace SmartStore.Services.Cms.Blocks { - /// - /// Contains base data for a block instance. - /// - public interface IBlockContainer - { - /// - /// The type of the block, e.g. 'html'. - /// - string BlockType { get; } - - /// - /// The actual block instance. - /// - IBlock Block { get; } - - /// - /// The block metadata. - /// - IBlockMetadata Metadata { get; } - - /// - /// The block title / display name. - /// - string Title { get; } - - /// - /// Whether the block's text content color should be inversed so - /// that it gets readable on dark backgrounds. - /// - bool IsInversed { get; } - - /// - /// The block html id. - /// - string HtmlId { get; } - - /// - /// The block css class name(s). - /// - string CssClass { get; } - - /// - /// The block css style expression. - /// - string CssStyle { get; } - } + /// + /// Contains base data for a block instance. + /// + public interface IBlockContainer + { + /// + /// The type of the block, e.g. 'html'. + /// + string BlockType { get; } + + /// + /// The actual block instance. + /// + IBlock Block { get; } + + /// + /// The block metadata. + /// + IBlockMetadata Metadata { get; } + + /// + /// The block title / display name. + /// + string Title { get; } + + /// + /// Whether the block's text content color should be inversed so + /// that it gets readable on dark backgrounds. + /// + bool IsInversed { get; } + + /// + /// The block html id. + /// + string HtmlId { get; } + + /// + /// The block css class name(s). + /// + string CssClass { get; } + + /// + /// The block css style expression. + /// + string CssStyle { get; } + } } diff --git a/src/Libraries/SmartStore.Services/Cms/Blocks/IBlockEntity.cs b/src/Libraries/SmartStore.Services/Cms/Blocks/IBlockEntity.cs index 100f78e138..63dfdf82b3 100644 --- a/src/Libraries/SmartStore.Services/Cms/Blocks/IBlockEntity.cs +++ b/src/Libraries/SmartStore.Services/Cms/Blocks/IBlockEntity.cs @@ -2,20 +2,20 @@ namespace SmartStore.Services.Cms.Blocks { - /// - /// Represents a block instance's storage data. - /// - public interface IBlockEntity - { - int Id { get; set; } - int StoryId { get; } - string BlockType { get; set; } - string Model { get; set; } - string TagLine { get; set; } - string Title { get; set; } - string SubTitle { get; set; } - string Body { get; set; } - string Template { get; set; } + /// + /// Represents a block instance's storage data. + /// + public interface IBlockEntity + { + int Id { get; set; } + int StoryId { get; } + string BlockType { get; set; } + string Model { get; set; } + string TagLine { get; set; } + string Title { get; set; } + string SubTitle { get; set; } + string Body { get; set; } + string Template { get; set; } string Custom1 { get; set; } string Custom2 { get; set; } string Custom3 { get; set; } diff --git a/src/Libraries/SmartStore.Services/Cms/Blocks/IBlockHandler.cs b/src/Libraries/SmartStore.Services/Cms/Blocks/IBlockHandler.cs index 8f50bfdb37..58d74e18ec 100644 --- a/src/Libraries/SmartStore.Services/Cms/Blocks/IBlockHandler.cs +++ b/src/Libraries/SmartStore.Services/Cms/Blocks/IBlockHandler.cs @@ -5,90 +5,113 @@ namespace SmartStore.Services.Cms.Blocks { - /// - /// Indicates the view mode of a PageBuilder block. - /// - public enum StoryViewMode - { - /// - /// Final rendering result in public frontend - /// - Public, - /// - /// Preview mode in backend - /// - Preview, - /// - /// Visual block editing in backend's story editor - /// - GridEdit, - /// - /// Property dialog in backend - /// - Edit - } + /// + /// Indicates the view mode of a PageBuilder block. + /// + public enum StoryViewMode + { + /// + /// Final rendering result in public frontend + /// + Public, + /// + /// Preview mode in backend + /// + Preview, + /// + /// Visual block editing in backend's story editor + /// + GridEdit, + /// + /// Property dialog in backend + /// + Edit + } - /// - /// Handles rendering of corresponding implementations. - /// - public interface IBlockHandler - { - /// - /// Renders the block result. - /// - /// The block element to render. - /// A list of template names. The first valid template will be used for rendering. - /// Html helper instance. - void Render(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHeper); + public interface IBlockHtmlParts + { + void AddCssClass(string value); + void AddHtmlAttribute(string name, object value); + } - /// - /// returns the block's render result as string. - /// - /// The block element to render. - /// A list of valid template names. The first valid template will be used for rendering. - /// Html helper instance. - IHtmlString ToHtmlString(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHelper); - } + /// + /// Handles rendering of corresponding implementations. + /// + public interface IBlockHandler + { + /// + /// Called when the block is about to be rendered. Gives you the chance to add css classes or html attributes to the root element. + /// + /// The block instance wrapper / model. + /// The stories current view mode. + /// Allows adding classes or attributes. + void BeforeRender(IBlockContainer container, StoryViewMode viewMode, IBlockHtmlParts htmlParts); - /// - /// Handles loading, saving and rendering of corresponding implementations. - /// - public interface IBlockHandler : IBlockHandler where T : IBlock - { - /// - /// Creates the implementation instance for the passed block entity. - /// - /// The serialized entity to create a block instance for. - /// A concrete block implementation instance. - T Create(IBlockEntity entity); + /// + /// Renders the block result. + /// + /// The block element to render. + /// A list of template names. The first valid template will be used for rendering. + /// Html helper instance. + void Render(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHeper); - /// - /// Creates and loads the implementation instance for the passed block entity and view mode. - /// - /// The serialized entity to load a block instance for. - /// Depending on view mode, some data may be loaded or not. - /// A concrete block implementation instance. - T Load(IBlockEntity entity, StoryViewMode viewMode); + /// + /// Called after the entity has been saved to the database to perform operations which require an entity id (e.g. localization stuff). + /// + /// The block instance wrapper / model. + /// + /// The corresponding entity record for the block. + /// + void AfterSave(IBlockContainer container, IBlockEntity entity); - bool IsValid(T block); + /// + /// Clones . In most cases it is sufficient to directly return the serialized model, but there may be cases + /// where the inner data must be cloned first, e.g. if it contains a picture reference. + /// + /// The source entity. + /// The target entity clone which is about to be saved in the data storage. + /// The serialized block clone. + string Clone(IBlockEntity sourceEntity, IBlockEntity clonedEntity); - /// - /// Serializes the passed block instance and saves the result in the underlying data storage. - /// - /// The block instance to save. - /// - /// The corresponding entity record for the block. In most cases will - /// be converted to JSON and assigned to property, which then will be saved in the data storage. - /// - void Save(T block, IBlockEntity entity); + /// + /// returns the block's render result as string. + /// + /// The block element to render. + /// A list of valid template names. The first valid template will be used for rendering. + /// Html helper instance. + IHtmlString ToHtmlString(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHelper); + } - /// - /// Clones . In most cases it is sufficient to directly return the serialized model, but there may be cases - /// where the inner data must be cloned first, e.g. if it contains a picture reference. - /// - /// The source entity. - /// The target entity clone which is about to be saved in the data storage. - /// The serialized block clone. - string Clone(IBlockEntity sourceEntity, IBlockEntity clonedEntity); - } + /// + /// Handles loading, saving and rendering of corresponding implementations. + /// + public interface IBlockHandler : IBlockHandler where T : IBlock + { + /// + /// Creates the implementation instance for the passed block entity. + /// + /// The serialized entity to create a block instance for. + /// A concrete block implementation instance. + T Create(IBlockEntity entity); + + /// + /// Creates and loads the implementation instance for the passed block entity and view mode. + /// + /// The serialized entity to load a block instance for. + /// Depending on view mode, some data may be loaded or not. + /// A concrete block implementation instance. + T Load(IBlockEntity entity, StoryViewMode viewMode); + + bool IsValid(T block); + + /// + /// Serializes the passed block instance and saves the result in the property. + /// + /// The block instance to save. + /// + /// The corresponding entity record for the block. In most cases will + /// be converted to JSON and assigned to property, which then will be saved in the data storage. + /// + void Save(T block, IBlockEntity entity); + } } diff --git a/src/Libraries/SmartStore.Services/Cms/ILinkResolver.cs b/src/Libraries/SmartStore.Services/Cms/ILinkResolver.cs index 0a98489d7e..e3b14ebbae 100644 --- a/src/Libraries/SmartStore.Services/Cms/ILinkResolver.cs +++ b/src/Libraries/SmartStore.Services/Cms/ILinkResolver.cs @@ -8,18 +8,18 @@ namespace SmartStore.Services.Cms /// public partial interface ILinkResolver { - /// - /// Resolves a link expression. - /// - /// - /// Link expression, e.g. product:123, category:234, topic:2, topic:aboutus etc. - /// Supported entities are product, category, manufacturer and topic. Value of topic can - /// either be the topic id or system name. - /// - /// Customer roles to check access for. null to use current customer's roles. - /// Language identifier. 0 to use current working language. - /// Store identifier. 0 to use current store. - /// LinkResolverResult - LinkResolverResult Resolve(string linkExpression, IEnumerable roles = null, int languageId = 0, int storeId = 0); + /// + /// Resolves a link expression. + /// + /// + /// Link expression, e.g. product:123, category:234, topic:2, topic:aboutus etc. + /// Supported entities are product, category, manufacturer and topic. Value of topic can + /// either be the topic id or system name. + /// + /// Customer roles to check access for. null to use current customer's roles. + /// Language identifier. 0 to use current working language. + /// Store identifier. 0 to use current store. + /// LinkResolverResult + LinkResolverResult Resolve(string linkExpression, IEnumerable roles = null, int languageId = 0, int storeId = 0); } } diff --git a/src/Libraries/SmartStore.Services/Cms/IWidgetService.cs b/src/Libraries/SmartStore.Services/Cms/IWidgetService.cs index 8e4faf8bb5..d23e345b7b 100644 --- a/src/Libraries/SmartStore.Services/Cms/IWidgetService.cs +++ b/src/Libraries/SmartStore.Services/Cms/IWidgetService.cs @@ -15,14 +15,14 @@ public partial interface IWidgetService /// Widgets IEnumerable> LoadActiveWidgets(int storeId = 0); - + /// /// Load active widgets /// /// Widget zone - /// Load records allows only in specified store; pass 0 to load all records + /// Load records allows only in specified store; pass 0 to load all records /// Widgets - IEnumerable> LoadActiveWidgetsByWidgetZone(string widgetZone, int storeId = 0); + IEnumerable> LoadActiveWidgetsByWidgetZone(string widgetZone, int storeId = 0); /// /// Load widget by system name diff --git a/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs b/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs index de254b8fd4..ae89d1d75b 100644 --- a/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs +++ b/src/Libraries/SmartStore.Services/Cms/LinkResolver.cs @@ -2,12 +2,15 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Runtime.CompilerServices; using System.Web; using System.Web.Mvc; using SmartStore.Core; using SmartStore.Core.Data; +using SmartStore.Core.Domain.Blogs; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.News; using SmartStore.Core.Domain.Topics; using SmartStore.Services.Localization; using SmartStore.Services.Security; @@ -19,18 +22,18 @@ namespace SmartStore.Services.Cms { public partial class LinkResolver : ILinkResolver { - /// - /// {0} : Expression w/o q - /// {1} : LanguageId - /// {2} : Store - /// {3} : RolesIdent - /// - internal const string LINKRESOLVER_KEY = "linkresolver:{0}-{1}-{2}-{3}"; - - // 0: Expression - internal const string LINKRESOLVER_PATTERN_KEY = "linkresolver:{0}-*"; - - protected readonly ICommonServices _services; + /// + /// {0} : Expression w/o q + /// {1} : LanguageId + /// {2} : Store + /// {3} : RolesIdent + /// + internal const string LINKRESOLVER_KEY = "linkresolver:{0}-{1}-{2}-{3}"; + + // 0: Expression + internal const string LINKRESOLVER_PATTERN_KEY = "linkresolver:{0}-*"; + + protected readonly ICommonServices _services; protected readonly IUrlRecordService _urlRecordService; protected readonly ILocalizedEntityService _localizedEntityService; protected readonly IAclService _aclService; @@ -54,22 +57,20 @@ public LinkResolver( _storeMappingService = storeMappingService; _topicService = topicService; _urlHelper = urlHelper; - - QuerySettings = DbQuerySettings.Default; } - public DbQuerySettings QuerySettings { get; set; } + public DbQuerySettings QuerySettings { get; set; } = DbQuerySettings.Default; public virtual LinkResolverResult Resolve(string linkExpression, IEnumerable roles = null, int languageId = 0, int storeId = 0) { - if (linkExpression.IsEmpty()) - { - return new LinkResolverResult { Type = LinkType.Url, Status = LinkStatus.NotFound }; - } + if (linkExpression.IsEmpty()) + { + return new LinkResolverResult { Type = LinkType.Url, Status = LinkStatus.NotFound }; + } - if (roles == null) + if (roles == null) { - roles = _services.WorkContext.CurrentCustomer.CustomerRoles; + roles = _services.WorkContext.CurrentCustomer.CustomerRoleMappings.Select(x => x.CustomerRole); } if (languageId == 0) @@ -82,78 +83,96 @@ public virtual LinkResolverResult Resolve(string linkExpression, IEnumerable x.Active).Select(x => x.Id))); - - d = _services.Cache.Get(cacheKey, () => - { - var d2 = d.Clone(); - - switch (d2.Type) - { - case LinkType.Product: - GetEntityData(d2, storeId, languageId, x => new ResolverEntitySummary - { - Name = x.Name, - Published = x.Published, - Deleted = x.Deleted, - SubjectToAcl = x.SubjectToAcl, - LimitedToStores = x.LimitedToStores, + if (d.Type == LinkType.Url) + { + var url = d.Value.ToString(); + if (url.EmptyNull().StartsWith("~")) + { + url = VirtualPathUtility.ToAbsolute(url); + } + d.Link = d.Label = url; + } + else if (d.Type == LinkType.File) + { + d.Link = d.Label = d.Value.ToString(); + } + else + { + var cacheKey = LINKRESOLVER_KEY.FormatInvariant( + d.Expression.EmptyNull().ToLower(), + languageId, + storeId, + string.Join(",", roles.Where(x => x.Active).Select(x => x.Id))); + + d = _services.Cache.Get(cacheKey, () => + { + var d2 = d.Clone(); + + switch (d2.Type) + { + case LinkType.Product: + GetEntityData(d2, storeId, languageId, x => new ResolverEntitySummary + { + Name = x.Name, + Published = x.Published, + Deleted = x.Deleted, + SubjectToAcl = x.SubjectToAcl, + LimitedToStores = x.LimitedToStores, PictureId = x.MainPictureId - }); - break; - case LinkType.Category: - GetEntityData(d2, storeId, languageId, x => new ResolverEntitySummary - { - Name = x.Name, - Published = x.Published, - Deleted = x.Deleted, - SubjectToAcl = x.SubjectToAcl, - LimitedToStores = x.LimitedToStores, - PictureId = x.PictureId - }); - break; - case LinkType.Manufacturer: - GetEntityData(d2, storeId, languageId, x => new ResolverEntitySummary - { - Name = x.Name, - Published = x.Published, - Deleted = x.Deleted, - LimitedToStores = x.LimitedToStores, - PictureId = x.PictureId - }); - break; - case LinkType.Topic: - GetEntityData(d2, storeId, languageId, x => null); - break; - default: - throw new SmartException("Unknown link builder type."); - } - - return d2; - }); - } + }); + break; + case LinkType.Category: + GetEntityData(d2, storeId, languageId, x => new ResolverEntitySummary + { + Name = x.Name, + Published = x.Published, + Deleted = x.Deleted, + SubjectToAcl = x.SubjectToAcl, + LimitedToStores = x.LimitedToStores, + PictureId = x.MediaFileId + }); + break; + case LinkType.Manufacturer: + GetEntityData(d2, storeId, languageId, x => new ResolverEntitySummary + { + Name = x.Name, + Published = x.Published, + Deleted = x.Deleted, + LimitedToStores = x.LimitedToStores, + PictureId = x.MediaFileId + }); + break; + case LinkType.Topic: + GetEntityData(d2, storeId, languageId, x => null); + break; + case LinkType.BlogPost: + GetEntityData(d2, storeId, languageId, x => new ResolverEntitySummary + { + Name = x.Title, + Published = x.IsPublished, + LimitedToStores = x.LimitedToStores, + PictureId = x.MediaFileId + }); + break; + case LinkType.NewsItem: + GetEntityData(d2, storeId, languageId, x => new ResolverEntitySummary + { + Name = x.Title, + Published = x.Published, + LimitedToStores = x.LimitedToStores, + PictureId = x.MediaFileId + }); + break; + default: + throw new SmartException("Unknown link builder type."); + } + + return d2; + }); + } var result = new LinkResolverResult { @@ -174,19 +193,21 @@ public virtual LinkResolverResult Resolve(string linkExpression, IEnumerable -1) - { - type = expression.Substring(0, colonIndex); - if (type.StartsWith("http", StringComparison.OrdinalIgnoreCase)) - { - type = null; - colonIndex = -1; - } - } - - path = expression.Substring(colonIndex + 1); - - var qmIndex = path.IndexOf('?'); - if (qmIndex > -1) - { - query = path.Substring(qmIndex + 1); - path = path.Substring(0, qmIndex); - } - - return true; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool TokenizeExpression(string expression, out string type, out string path, out string query) + { + type = null; + path = null; + query = null; + + if (string.IsNullOrWhiteSpace(expression)) + { + return false; + } + + var colonIndex = expression.IndexOf(':'); + if (colonIndex > -1) + { + type = expression.Substring(0, colonIndex); + if (type.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + type = null; + colonIndex = -1; + } + } + + path = expression.Substring(colonIndex + 1); + + var qmIndex = path.IndexOf('?'); + if (qmIndex > -1) + { + query = path.Substring(qmIndex + 1); + path = path.Substring(0, qmIndex); + } + + return true; + } protected virtual string GetLocalized(int entityId, string localeKeyGroup, string localeKey, int languageId, string defaultValue) { @@ -238,46 +260,48 @@ protected virtual string GetLocalized(int entityId, string localeKeyGroup, strin protected virtual LinkResolverData Parse(string linkExpression) { - if (TokenizeExpression(linkExpression, out var type, out var path, out var query)) - { - if (!string.IsNullOrWhiteSpace(type) && Enum.TryParse(type, true, out LinkType linkType)) - { - var result = new LinkResolverData { Type = linkType, Expression = string.Concat(type, ":", path) }; - - switch (linkType) - { - case LinkType.Product: - case LinkType.Category: - case LinkType.Manufacturer: - case LinkType.Topic: - if (int.TryParse(path, out var id)) - { - // Reduce thrown exceptions in console - result.Value = id; - } - else - { - result.Value = path; - } - - result.QueryString = query; - break; - case LinkType.Url: - result.Value = path + (query.HasValue() ? "?" + query : ""); - break; - case LinkType.File: - result.Value = path; - result.QueryString = query; - break; - default: - throw new SmartException("Unknown link builder type."); - } - - return result; - } - } - - return new LinkResolverData { Type = LinkType.Url, Value = linkExpression.EmptyNull() }; + if (TokenizeExpression(linkExpression, out var type, out var path, out var query)) + { + if (!string.IsNullOrWhiteSpace(type) && Enum.TryParse(type, true, out LinkType linkType)) + { + var result = new LinkResolverData { Type = linkType, Expression = string.Concat(type, ":", path) }; + + switch (linkType) + { + case LinkType.Product: + case LinkType.Category: + case LinkType.Manufacturer: + case LinkType.Topic: + case LinkType.BlogPost: + case LinkType.NewsItem: + if (int.TryParse(path, out var id)) + { + // Reduce thrown exceptions in console + result.Value = id; + } + else + { + result.Value = path; + } + + result.QueryString = query; + break; + case LinkType.Url: + result.Value = path + (query.HasValue() ? "?" + query : ""); + break; + case LinkType.File: + result.Value = path; + result.QueryString = query; + break; + default: + throw new SmartException("Unknown link builder type."); + } + + return result; + } + } + + return new LinkResolverData { Type = LinkType.Url, Value = linkExpression.EmptyNull() }; } internal void GetEntityData( @@ -349,18 +373,23 @@ internal void GetEntityData( data.Status = summary.Deleted ? LinkStatus.NotFound : summary.Published ? LinkStatus.Ok : LinkStatus.Hidden; - - if (data.Type == LinkType.Topic) - { - data.Label = GetLocalized(data.Id, entityName, "ShortTitle", languageId, null) - ?? GetLocalized(data.Id, entityName, "Title", languageId, null) - ?? summary.ShortTitle.NullEmpty() - ?? summary.Title.NullEmpty() - ?? summary.Name; - } - else + + switch (data.Type) { - data.Label = GetLocalized(data.Id, entityName, "Name", languageId, summary.Name); + case LinkType.Topic: + data.Label = GetLocalized(data.Id, entityName, nameof(Topic.ShortTitle), languageId, null) + ?? GetLocalized(data.Id, entityName, "Title", languageId, null) + ?? summary.ShortTitle.NullEmpty() + ?? summary.Title.NullEmpty() + ?? summary.Name; + break; + case LinkType.BlogPost: + case LinkType.NewsItem: + data.Label = GetLocalized(data.Id, entityName, "Title", languageId, summary.Name); + break; + default: + data.Label = GetLocalized(data.Id, entityName, "Name", languageId, summary.Name); + break; } data.Slug = _urlRecordService.GetActiveSlug(data.Id, entityName, languageId).NullEmpty() ?? _urlRecordService.GetActiveSlug(data.Id, entityName, 0); diff --git a/src/Libraries/SmartStore.Services/Cms/LinkResolverInvalidator.cs b/src/Libraries/SmartStore.Services/Cms/LinkResolverInvalidator.cs index 58cda2a320..aba228ccff 100644 --- a/src/Libraries/SmartStore.Services/Cms/LinkResolverInvalidator.cs +++ b/src/Libraries/SmartStore.Services/Cms/LinkResolverInvalidator.cs @@ -12,83 +12,87 @@ namespace SmartStore.Services.Cms { - public partial class LinkResolverInvalidator : DbSaveHook - { - private readonly ICommonServices _services; + public partial class LinkResolverInvalidator : DbSaveHook + { + private readonly ICommonServices _services; - private static readonly HashSet _toxicProps = new HashSet - { - nameof(Topic.SystemName), - nameof(Topic.IsPublished), - nameof(Topic.SubjectToAcl), - nameof(Topic.LimitedToStores), - nameof(Topic.Title), - nameof(Topic.ShortTitle), - nameof(Product.Name), + private static readonly HashSet _toxicProps = new HashSet + { + nameof(Topic.SystemName), + nameof(Topic.IsPublished), + nameof(Topic.SubjectToAcl), + nameof(Topic.LimitedToStores), + nameof(Topic.Title), + nameof(Topic.ShortTitle), + nameof(Topic.Intro), + nameof(Product.Name), nameof(Product.Deleted), nameof(Product.MainPictureId), nameof(Category.Published), - nameof(Category.PictureId), + nameof(Category.MediaFileId), nameof(StoreMapping.StoreId), - nameof(UrlRecord.IsActive), - nameof(UrlRecord.Slug) - }; + nameof(UrlRecord.IsActive), + nameof(UrlRecord.Slug) + }; - public LinkResolverInvalidator(ICommonServices services) - { - _services = services; - } + public LinkResolverInvalidator(ICommonServices services) + { + _services = services; + } - public override void OnBeforeSave(IHookedEntity entry) - { - var cache = _services.Cache; - var e = entry.Entity; + public override void OnBeforeSave(IHookedEntity entry) + { + var cache = _services.Cache; + var e = entry.Entity; - var evict = e is Topic || e is Product || e is Category || e is Manufacturer || e is StoreMapping || e is UrlRecord; - if (evict && entry.InitialState == EntityState.Modified) - { - var modProps = entry.Entry.GetModifiedProperties(_services.DbContext); - evict = modProps.Keys.Any(x => _toxicProps.Contains(x)); - } + var evict = e is Topic || e is Product || e is Category || e is Manufacturer || e is StoreMapping || e is UrlRecord; + if (!evict) + throw new NotSupportedException(); // Perf - if (!evict) - return; + if (evict && entry.InitialState == EntityState.Modified) + { + var modProps = entry.Entry.GetModifiedProperties(_services.DbContext); + evict = modProps.Keys.Any(x => _toxicProps.Contains(x)); + } - int? evictTopicId = null; + if (!evict) + return; - if (e is Topic t) - { - cache.RemoveByPattern(BuildPatternKey("topic", t.Id)); - cache.RemoveByPattern(BuildPatternKey("topic", t.SystemName)); - } - else if (e is Category || e is Product || e is Manufacturer) - { - cache.RemoveByPattern(BuildPatternKey(e.GetEntityName().ToLowerInvariant(), e.Id)); - } - else if (e is UrlRecord ur) - { - cache.RemoveByPattern(BuildPatternKey(ur.EntityName.ToLowerInvariant(), ur.EntityId)); - evictTopicId = ur.EntityId; - } - else if (e is StoreMapping sm) - { - cache.RemoveByPattern(BuildPatternKey(sm.EntityName.ToLowerInvariant(), sm.EntityId)); - evictTopicId = sm.EntityId; - } + int? evictTopicId = null; - if (evictTopicId.HasValue) - { - var systemName = _services.DbContext.Set().Where(x => x.Id == evictTopicId.Value).Select(x => x.SystemName).FirstOrDefault(); - if (systemName.HasValue()) - { - cache.RemoveByPattern(BuildPatternKey("topic", systemName)); - } - } - } + if (e is Topic t) + { + cache.RemoveByPattern(BuildPatternKey("topic", t.Id)); + cache.RemoveByPattern(BuildPatternKey("topic", t.SystemName)); + } + else if (e is Category || e is Product || e is Manufacturer) + { + cache.RemoveByPattern(BuildPatternKey(e.GetEntityName().ToLowerInvariant(), e.Id)); + } + else if (e is UrlRecord ur) + { + cache.RemoveByPattern(BuildPatternKey(ur.EntityName.ToLowerInvariant(), ur.EntityId)); + evictTopicId = ur.EntityId; + } + else if (e is StoreMapping sm) + { + cache.RemoveByPattern(BuildPatternKey(sm.EntityName.ToLowerInvariant(), sm.EntityId)); + evictTopicId = sm.EntityId; + } - private string BuildPatternKey(string entityName, object ident) - { - return LinkResolver.LINKRESOLVER_PATTERN_KEY.FormatInvariant(String.Concat(entityName, ":", ident)); - } - } + if (evictTopicId.HasValue) + { + var systemName = _services.DbContext.Set().Where(x => x.Id == evictTopicId.Value).Select(x => x.SystemName).FirstOrDefault(); + if (systemName.HasValue()) + { + cache.RemoveByPattern(BuildPatternKey("topic", systemName)); + } + } + } + + private string BuildPatternKey(string entityName, object ident) + { + return LinkResolver.LINKRESOLVER_PATTERN_KEY.FormatInvariant(String.Concat(entityName, ":", ident)); + } + } } diff --git a/src/Libraries/SmartStore.Services/Cms/LinkResolverResult.cs b/src/Libraries/SmartStore.Services/Cms/LinkResolverResult.cs index 71002103a9..ac97fd982d 100644 --- a/src/Libraries/SmartStore.Services/Cms/LinkResolverResult.cs +++ b/src/Libraries/SmartStore.Services/Cms/LinkResolverResult.cs @@ -8,6 +8,8 @@ public enum LinkType Category, Manufacturer, Topic, + BlogPost, + NewsItem, Url = 20, File = 30 } @@ -15,8 +17,8 @@ public enum LinkType public enum LinkStatus { Ok, - Forbidden, - NotFound, + Forbidden, + NotFound, Hidden } @@ -24,17 +26,17 @@ public class LinkResolverResult { private string _link; - /// - /// The raw expression without query string. - /// - public string Expression { get; set; } + /// + /// The raw expression without query string. + /// + public string Expression { get; set; } - /// - /// The query string part. - /// - public string QueryString { get; set; } + /// + /// The query string part. + /// + public string QueryString { get; set; } - public LinkType Type { get; set; } + public LinkType Type { get; set; } public object Value { get; set; } public LinkStatus Status { get; set; } @@ -71,10 +73,10 @@ public string Link } public override string ToString() - { - return this.Link.EmptyNull(); - } - } + { + return this.Link.EmptyNull(); + } + } [Serializable] public partial class LinkResolverData : LinkResolverResult, ICloneable @@ -84,15 +86,15 @@ public partial class LinkResolverData : LinkResolverResult, ICloneableMenu entity. void DeleteMenu(MenuRecord menu); + // Gets the system names of all published menus + IEnumerable GetAllMenuSystemNames(); + /// /// Gets all menus. /// @@ -36,7 +40,7 @@ public partial interface IMenuStorage /// Menu entities. IPagedList GetAllMenus( string systemName = null, - int storeId = 0, + int storeId = 0, bool includeHidden = false, int pageIndex = 0, int pageSize = int.MaxValue); diff --git a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs index ff0332720c..748535a4f4 100644 --- a/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs +++ b/src/Libraries/SmartStore.Services/Cms/Menus/MenuStorage.cs @@ -15,7 +15,7 @@ namespace SmartStore.Services.Cms { public partial class MenuStorage : IMenuStorage { - private const string MENU_ALLSYSTEMNAMEs_CACHE_KEY = "MenuStorage:SystemNames"; + private const string MENU_ALLSYSTEMNAMES_CACHE_KEY = "MenuStorage:SystemNames"; private const string MENU_USER_CACHE_KEY = "MenuStorage:Menus:User-{0}-{1}"; private const string MENU_PATTERN_KEY = "MenuStorage:Menus:*"; @@ -110,6 +110,11 @@ public virtual void DeleteMenu(MenuRecord menu) _services.Cache.RemoveByPattern(MENU_PATTERN_KEY); } + public virtual IEnumerable GetAllMenuSystemNames() + { + return GetMenuSystemNames(true); + } + public virtual IPagedList GetAllMenus( string systemName = null, int storeId = 0, @@ -125,7 +130,7 @@ public virtual IEnumerable GetUserMenuInfos(IEnumerable { if (roles == null) { - roles = _services.WorkContext.CurrentCustomer.CustomerRoles; + roles = _services.WorkContext.CurrentCustomer.CustomerRoleMappings.Select(x => x.CustomerRole); } if (storeId == 0) @@ -137,7 +142,7 @@ public virtual IEnumerable GetUserMenuInfos(IEnumerable var userMenusInfo = _services.Cache.Get(cacheKey, () => { - var query = BuildMenuQuery(0, storeId, null, false, false, true, false, true); + var query = BuildMenuQuery(0, storeId, null, false, false, true, true, true); var data = query.Select(x => new { @@ -185,7 +190,7 @@ public virtual bool MenuExists(string systemName) return false; } - return GetMenuSystemNames(true).Contains(systemName); + return GetMenuSystemNames(true).Contains(systemName); } #region Menu items @@ -202,8 +207,8 @@ public virtual void InsertMenuItem(MenuItemRecord item) _menuItemRepository.Insert(item); - _services.Cache.RemoveByPattern(MENU_PATTERN_KEY); - } + _services.Cache.RemoveByPattern(MENU_PATTERN_KEY); + } public virtual void UpdateMenuItem(MenuItemRecord item) { @@ -217,8 +222,8 @@ public virtual void UpdateMenuItem(MenuItemRecord item) _menuItemRepository.Update(item); - _services.Cache.RemoveByPattern(MENU_PATTERN_KEY); - } + _services.Cache.RemoveByPattern(MENU_PATTERN_KEY); + } public virtual void DeleteMenuItem(MenuItemRecord item, bool deleteChilds = true) { @@ -231,21 +236,21 @@ public virtual void DeleteMenuItem(MenuItemRecord item, bool deleteChilds = true { _menuItemRepository.Delete(item); } - else - { - var ids = new HashSet { item.Id }; - GetChildIds(item.Id, ids); + else + { + var ids = new HashSet { item.Id }; + GetChildIds(item.Id, ids); - foreach (var chunk in ids.Slice(200)) - { - var items = _menuItemRepository.Table.Where(x => chunk.Contains(x.Id)).ToList(); - _menuItemRepository.DeleteRange(items); - } - } + foreach (var chunk in ids.Slice(200)) + { + var items = _menuItemRepository.Table.Where(x => chunk.Contains(x.Id)).ToList(); + _menuItemRepository.DeleteRange(items); + } + } - _services.Cache.RemoveByPattern(MENU_PATTERN_KEY); + _services.Cache.RemoveByPattern(MENU_PATTERN_KEY); - void GetChildIds(int parentId, HashSet ids) + void GetChildIds(int parentId, HashSet ids) { var childIds = _menuItemRepository.TableUntracked .Where(x => x.ParentItemId == parentId) @@ -296,21 +301,23 @@ public virtual IList GetMenuItems(string systemName, int storeId #region Utilities - private ISet GetMenuSystemNames(bool create) - { - if (create || _services.Cache.Contains(MENU_ALLSYSTEMNAMEs_CACHE_KEY)) - { - return _services.Cache.GetHashSet(MENU_ALLSYSTEMNAMEs_CACHE_KEY, () => - { - return _menuRepository.TableUntracked - .Where(x => x.Published) - .Select(x => x.SystemName) - .ToArray(); - }); - } - - return null; - } + private ISet GetMenuSystemNames(bool ensureCreated) + { + if (ensureCreated || _services.Cache.Contains(MENU_ALLSYSTEMNAMES_CACHE_KEY)) + { + return _services.Cache.GetHashSet(MENU_ALLSYSTEMNAMES_CACHE_KEY, () => + { + return _menuRepository.TableUntracked + .Where(x => x.Published) + .OrderByDescending(x => x.IsSystemMenu) + .ThenBy(x => x.Id) + .Select(x => x.SystemName) + .ToArray(); + }); + } + + return null; + } protected virtual IQueryable BuildMenuQuery( int id, @@ -345,7 +352,7 @@ protected virtual IQueryable BuildMenuQuery( if (storeId > 0 && !QuerySettings.IgnoreMultiStore) { - query = + query = from x in query join m in _storeMappingRepository.Table on new { x1 = x.Id, x2 = entityName } equals new { x1 = m.EntityId, x2 = m.EntityName } into sm @@ -358,9 +365,9 @@ from m in sm.DefaultIfEmpty() if (!includeHidden && !QuerySettings.IgnoreAcl) { - var allowedRoleIds = _services.WorkContext.CurrentCustomer.CustomerRoles.Where(x => x.Active).Select(x => x.Id).ToList(); + var allowedRoleIds = _services.WorkContext.CurrentCustomer.GetRoleIds(); - query = + query = from x in query join a in _aclRepository.Table on new { x1 = x.Id, x2 = entityName } equals new { x1 = a.EntityId, x2 = a.EntityName } into ac @@ -373,7 +380,7 @@ from a in ac.DefaultIfEmpty() if (applied && groupBy) { - query = + query = from x in query group x by x.Id into grp orderby grp.Key @@ -382,7 +389,10 @@ orderby grp.Key if (sort) { - query = query.OrderBy(x => x.SystemName).ThenBy(x => x.Title); + query = query + .OrderBy(x => x.DisplayOrder) + .ThenBy(x => x.SystemName) + .ThenBy(x => x.Title); } return query; @@ -394,6 +404,8 @@ protected virtual IQueryable BuildMenuItemQuery( int storeId, bool includeHidden) { + var applied = false; + var entityName = nameof(MenuItemRecord); var singleMenu = menuId != 0 || (systemName.HasValue() && storeId != 0); var menuQuery = BuildMenuQuery(menuId, storeId, systemName, null, includeHidden, !singleMenu, !singleMenu); @@ -409,6 +421,43 @@ where includeHidden || mi.Published orderby mi.ParentItemId, mi.DisplayOrder select mi; + if (storeId > 0 && !QuerySettings.IgnoreMultiStore) + { + query = + from x in query + join m in _storeMappingRepository.Table + on new { x1 = x.Id, x2 = entityName } equals new { x1 = m.EntityId, x2 = m.EntityName } into sm + from m in sm.DefaultIfEmpty() + where !x.LimitedToStores || storeId == m.StoreId + select x; + + applied = true; + } + + if (!includeHidden && !QuerySettings.IgnoreAcl) + { + var allowedRoleIds = _services.WorkContext.CurrentCustomer.GetRoleIds(); + + query = + from x in query + join a in _aclRepository.Table + on new { x1 = x.Id, x2 = entityName } equals new { x1 = a.EntityId, x2 = a.EntityName } into ac + from a in ac.DefaultIfEmpty() + where !x.SubjectToAcl || allowedRoleIds.Contains(a.CustomerRoleId) + select x; + + applied = true; + } + + if (applied) + { + query = + from x in query + group x by x.Id into grp + orderby grp.Key + select grp.FirstOrDefault(); + } + return query; } diff --git a/src/Libraries/SmartStore.Services/Cms/WidgetExtensions.cs b/src/Libraries/SmartStore.Services/Cms/WidgetExtensions.cs index 9e435b04d4..d5f860f1a2 100644 --- a/src/Libraries/SmartStore.Services/Cms/WidgetExtensions.cs +++ b/src/Libraries/SmartStore.Services/Cms/WidgetExtensions.cs @@ -3,28 +3,28 @@ using SmartStore.Core.Domain.Cms; using SmartStore.Core.Plugins; -namespace SmartStore.Services.Cms +namespace SmartStore.Services.Cms { public static class WidgetExtensions { public static bool IsWidgetActive(this Provider widget, WidgetSettings widgetSettings) { - Guard.NotNull(widget, nameof(widget)); + Guard.NotNull(widget, nameof(widget)); - return widget.ToLazy().IsWidgetActive(widgetSettings); + return widget.ToLazy().IsWidgetActive(widgetSettings); } - public static bool IsWidgetActive(this Lazy widget, WidgetSettings widgetSettings) - { - Guard.NotNull(widget, nameof(widget)); - Guard.NotNull(widgetSettings, nameof(widgetSettings)); + public static bool IsWidgetActive(this Lazy widget, WidgetSettings widgetSettings) + { + Guard.NotNull(widget, nameof(widget)); + Guard.NotNull(widgetSettings, nameof(widgetSettings)); - if (widgetSettings.ActiveWidgetSystemNames == null) - { - return false; - } + if (widgetSettings.ActiveWidgetSystemNames == null) + { + return false; + } - return widgetSettings.ActiveWidgetSystemNames.Contains(widget.Metadata.SystemName, StringComparer.OrdinalIgnoreCase); - } + return widgetSettings.ActiveWidgetSystemNames.Contains(widget.Metadata.SystemName, StringComparer.OrdinalIgnoreCase); + } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Cms/WidgetService.cs b/src/Libraries/SmartStore.Services/Cms/WidgetService.cs index a3d6a8b135..6f2acd907d 100644 --- a/src/Libraries/SmartStore.Services/Cms/WidgetService.cs +++ b/src/Libraries/SmartStore.Services/Cms/WidgetService.cs @@ -17,17 +17,17 @@ public partial class WidgetService : IWidgetService #region Fields & Consts - private const string WIDGETS_ACTIVE_KEY = "SmartStore.widgets.active-{0}"; - private const string WIDGETS_ZONEMAPPED_KEY = "SmartStore.widgets.zonemapped-{0}"; + private const string WIDGETS_ACTIVE_KEY = "SmartStore.widgets.active-{0}"; + private const string WIDGETS_ZONEMAPPED_KEY = "SmartStore.widgets.zonemapped-{0}"; private readonly IPluginFinder _pluginFinder; private readonly WidgetSettings _widgetSettings; - private readonly ISettingService _settingService; - private readonly IProviderManager _providerManager; - private readonly IRequestCache _requestCache; + private readonly ISettingService _settingService; + private readonly IProviderManager _providerManager; + private readonly IRequestCache _requestCache; #endregion - + #region Ctor /// @@ -35,34 +35,34 @@ public partial class WidgetService : IWidgetService /// /// Plugin finder /// Widget settings - /// Plugin service - public WidgetService( - IPluginFinder pluginFinder, - WidgetSettings widgetSettings, - ISettingService settingService, - IProviderManager providerManager, - IRequestCache requestCache) + /// Plugin service + public WidgetService( + IPluginFinder pluginFinder, + WidgetSettings widgetSettings, + ISettingService settingService, + IProviderManager providerManager, + IRequestCache requestCache) { this._pluginFinder = pluginFinder; this._widgetSettings = widgetSettings; - this._settingService = settingService; - this._providerManager = providerManager; - this._requestCache = requestCache; + this._settingService = settingService; + this._providerManager = providerManager; + this._requestCache = requestCache; } #endregion #region Methods - /// - /// Load all widgets - /// - /// Load records allows only in specified store; pass 0 to load all records - /// Widgets - public virtual IEnumerable> LoadAllWidgets(int storeId = 0) - { - return _providerManager.GetAllProviders(storeId); - } + /// + /// Load all widgets + /// + /// Load records allows only in specified store; pass 0 to load all records + /// Widgets + public virtual IEnumerable> LoadAllWidgets(int storeId = 0) + { + return _providerManager.GetAllProviders(storeId); + } /// /// Load active widgets @@ -71,12 +71,13 @@ public virtual IEnumerable> LoadAllWidgets(int storeId = 0) /// Widgets public virtual IEnumerable> LoadActiveWidgets(int storeId = 0) { - var activeWidgets = _requestCache.Get(WIDGETS_ACTIVE_KEY.FormatInvariant(storeId), () => { - var allWigets = LoadAllWidgets(storeId); - return allWigets.Where(p => _widgetSettings.ActiveWidgetSystemNames.Contains(p.Metadata.SystemName, StringComparer.InvariantCultureIgnoreCase)).ToList(); - }); + var activeWidgets = _requestCache.Get(WIDGETS_ACTIVE_KEY.FormatInvariant(storeId), () => + { + var allWigets = LoadAllWidgets(storeId); + return allWigets.Where(p => _widgetSettings.ActiveWidgetSystemNames.Contains(p.Metadata.SystemName, StringComparer.InvariantCultureIgnoreCase)).ToList(); + }); - return activeWidgets; + return activeWidgets; } /// @@ -88,44 +89,45 @@ public virtual IEnumerable> LoadActiveWidgets(int storeId = 0) public virtual IEnumerable> LoadActiveWidgetsByWidgetZone(string widgetZone, int storeId = 0) { if (widgetZone.IsEmpty()) - return Enumerable.Empty>(); - - var mappedWidgets = _requestCache.Get(WIDGETS_ZONEMAPPED_KEY.FormatInvariant(storeId), () => { - var activeWidgets = LoadActiveWidgets(storeId); - var map = new Multimap>(); - - foreach (var widget in activeWidgets) - { - var zones = widget.Value.GetWidgetZones(); - if (zones != null && zones.Any()) - { - foreach (var zone in zones.Select(x => x.ToLower())) - { - map.Add(zone, widget); - } - } - } - - return map; - }); - - widgetZone = widgetZone.ToLower(); - if (mappedWidgets.ContainsKey(widgetZone)) - { - return mappedWidgets[widgetZone]; - } - - return Enumerable.Empty>(); + return Enumerable.Empty>(); + + var mappedWidgets = _requestCache.Get(WIDGETS_ZONEMAPPED_KEY.FormatInvariant(storeId), () => + { + var activeWidgets = LoadActiveWidgets(storeId); + var map = new Multimap>(); + + foreach (var widget in activeWidgets) + { + var zones = widget.Value.GetWidgetZones(); + if (zones != null && zones.Any()) + { + foreach (var zone in zones.Select(x => x.ToLower())) + { + map.Add(zone, widget); + } + } + } + + return map; + }); + + widgetZone = widgetZone.ToLower(); + if (mappedWidgets.ContainsKey(widgetZone)) + { + return mappedWidgets[widgetZone]; + } + + return Enumerable.Empty>(); } /// /// Load widget by system name /// - /// System name + /// System name /// Found widget - public virtual Provider LoadWidgetBySystemName(string systemName) + public virtual Provider LoadWidgetBySystemName(string systemName) { - return _providerManager.GetProvider(systemName); + return _providerManager.GetProvider(systemName); } #endregion diff --git a/src/Libraries/SmartStore.Services/Common/AddressExtensions.cs b/src/Libraries/SmartStore.Services/Common/AddressExtensions.cs new file mode 100644 index 0000000000..4a3da77c14 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Common/AddressExtensions.cs @@ -0,0 +1,142 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Text; +using SmartStore.Core.Domain.Common; + +namespace SmartStore.Services.Common +{ + public static class AddressExtensions + { + /// + /// Find first occurrence of an address + /// + /// Addresses to be searched + /// Address to find + /// First occurrence of address + public static Address FindAddress(this ICollection
source, Address address) + { + return source.FindAddress( + address.FirstName, + address.LastName, + address.PhoneNumber, + address.Email, + address.FaxNumber, + address.Company, + address.Address1, + address.Address2, + address.City, + address.StateProvinceId, + address.ZipPostalCode, + address.CountryId + ); + } + + /// + /// Find an address + /// + /// Source + /// First name + /// Last name + /// Phone number + /// Email + /// Fax number + /// Company + /// Address 1 + /// Address 2 + /// City + /// State/province identifier + /// Zip postal code + /// Country identifier + /// Address + public static Address FindAddress( + this ICollection
source, + string firstName, + string lastName, + string phoneNumber, + string email, + string faxNumber, + string company, + string address1, + string address2, + string city, + int? stateProvinceId, + string zipPostalCode, + int? countryId) + { + Func addressMatcher = (x) => + { + return x.Email.IsCaseInsensitiveEqual(email) + && x.LastName.IsCaseInsensitiveEqual(lastName) + && x.FirstName.IsCaseInsensitiveEqual(firstName) + && x.Address1.IsCaseInsensitiveEqual(address1) + && x.Address2.IsCaseInsensitiveEqual(address2) + && x.Company.IsCaseInsensitiveEqual(company) + && x.ZipPostalCode.IsCaseInsensitiveEqual(zipPostalCode) + && x.City.IsCaseInsensitiveEqual(city) + && x.PhoneNumber.IsCaseInsensitiveEqual(phoneNumber) + && x.FaxNumber.IsCaseInsensitiveEqual(faxNumber) + && x.StateProvinceId == stateProvinceId + && x.CountryId == countryId; + }; + + return source.FirstOrDefault(addressMatcher); + } + + /// + /// Returns the full name of the address. + /// + public static string GetFullName(this Address address, bool withCompanyName = true) + { + if (address == null) + return null; + + string result = string.Empty; + if (address.FirstName.HasValue() || address.LastName.HasValue()) + { + result = string.Format("{0} {1}", address.FirstName, address.LastName).Trim(); + } + + if (withCompanyName && address.Company.HasValue()) + { + result = string.Concat(result, result.HasValue() ? ", " : "", address.Company); + } + + return result; + } + + public static string GetFullSalutaion(this Address address) + { + if (address == null) + return null; + + return string.Format("{0}{1}", + address.Salutation.EmptyNull(), + address.Title.HasValue() ? " " + address.Title : ""); + } + + /// + /// Checks whether the postal data of two addresses are equal. + /// + public static bool IsPostalDataEqual(this Address address, Address other) + { + if (address != null && other != null) + { + if (address.FirstName.IsCaseInsensitiveEqual(other.FirstName) && + address.LastName.IsCaseInsensitiveEqual(other.LastName) && + address.Company.IsCaseInsensitiveEqual(other.Company) && + address.Address1.IsCaseInsensitiveEqual(other.Address1) && + address.Address2.IsCaseInsensitiveEqual(other.Address2) && + address.ZipPostalCode.IsCaseInsensitiveEqual(other.ZipPostalCode) && + address.City.IsCaseInsensitiveEqual(other.City) && + address.StateProvinceId == other.StateProvinceId && + address.CountryId == other.CountryId) + { + return true; + } + } + + return false; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Common/AddressExtentions.cs b/src/Libraries/SmartStore.Services/Common/AddressExtentions.cs deleted file mode 100644 index 100749d906..0000000000 --- a/src/Libraries/SmartStore.Services/Common/AddressExtentions.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Text; -using SmartStore.Core.Domain.Common; - -namespace SmartStore.Services.Common -{ - public static class AddressExtentions - { - /// - /// Find first occurrence of an address - /// - /// Addresses to be searched - /// Address to find - /// First occurrence of address - public static Address FindAddress(this ICollection
source, Address address) - { - return source.FindAddress( - address.FirstName, - address.LastName, - address.PhoneNumber, - address.Email, - address.FaxNumber, - address.Company, - address.Address1, - address.Address2, - address.City, - address.StateProvinceId, - address.ZipPostalCode, - address.CountryId - ); - } - - /// - /// Find an address - /// - /// Source - /// First name - /// Last name - /// Phone number - /// Email - /// Fax number - /// Company - /// Address 1 - /// Address 2 - /// City - /// State/province identifier - /// Zip postal code - /// Country identifier - /// Address - public static Address FindAddress( - this ICollection
source, - string firstName, - string lastName, - string phoneNumber, - string email, - string faxNumber, - string company, - string address1, - string address2, - string city, - int? stateProvinceId, - string zipPostalCode, - int? countryId) - { - Func addressMatcher = (x) => - { - return x.Email.IsCaseInsensitiveEqual(email) - && x.LastName.IsCaseInsensitiveEqual(lastName) - && x.FirstName.IsCaseInsensitiveEqual(firstName) - && x.Address1.IsCaseInsensitiveEqual(address1) - && x.Address2.IsCaseInsensitiveEqual(address2) - && x.Company.IsCaseInsensitiveEqual(company) - && x.ZipPostalCode.IsCaseInsensitiveEqual(zipPostalCode) - && x.City.IsCaseInsensitiveEqual(city) - && x.PhoneNumber.IsCaseInsensitiveEqual(phoneNumber) - && x.FaxNumber.IsCaseInsensitiveEqual(faxNumber) - && x.StateProvinceId == stateProvinceId - && x.CountryId == countryId; - }; - - return source.FirstOrDefault(addressMatcher); - } - - /// - /// Returns the full name of the address. - /// - public static string GetFullName(this Address address, bool withCompanyName = true) - { - if (address == null) - return null; - - string result = string.Empty; - if (address.FirstName.HasValue() || address.LastName.HasValue()) - { - result = string.Format("{0} {1}", address.FirstName, address.LastName).Trim(); - } - - if (withCompanyName && address.Company.HasValue()) - { - result = string.Concat(result, result.HasValue() ? ", " : "", address.Company); - } - - return result; - } - - public static string GetFullSalutaion(this Address address) - { - if (address == null) - return null; - - return string.Format("{0}{1}", - address.Salutation.EmptyNull(), - address.Title.HasValue() ? " " + address.Title : ""); - } - - /// - /// Checks whether the postal data of two addresses are equal. - /// - public static bool IsPostalDataEqual(this Address address, Address other) - { - if (address != null && other != null) - { - if (address.FirstName.IsCaseInsensitiveEqual(other.FirstName) && - address.LastName.IsCaseInsensitiveEqual(other.LastName) && - address.Company.IsCaseInsensitiveEqual(other.Company) && - address.Address1.IsCaseInsensitiveEqual(other.Address1) && - address.Address2.IsCaseInsensitiveEqual(other.Address2) && - address.ZipPostalCode.IsCaseInsensitiveEqual(other.ZipPostalCode) && - address.City.IsCaseInsensitiveEqual(other.City) && - address.StateProvinceId == other.StateProvinceId && - address.CountryId == other.CountryId) - { - return true; - } - } - - return false; - } - } -} diff --git a/src/Libraries/SmartStore.Services/Common/AddressService.cs b/src/Libraries/SmartStore.Services/Common/AddressService.cs index de52593d01..3953689090 100644 --- a/src/Libraries/SmartStore.Services/Common/AddressService.cs +++ b/src/Libraries/SmartStore.Services/Common/AddressService.cs @@ -18,42 +18,42 @@ public partial class AddressService : IAddressService private readonly IRepository
_addressRepository; private readonly ICountryService _countryService; private readonly IStateProvinceService _stateProvinceService; - private readonly ICommonServices _services; + private readonly ICommonServices _services; private readonly AddressSettings _addressSettings; - private readonly ITemplateEngine _templateEngine; - private readonly IMessageModelProvider _messageModelProvider; - - public AddressService( - IRepository
addressRepository, - ICountryService countryService, - IStateProvinceService stateProvinceService, - ICommonServices services, - AddressSettings addressSettings, - ITemplateEngine templateEngine, - IMessageModelProvider messageModelProvider) + private readonly ITemplateEngine _templateEngine; + private readonly IMessageModelProvider _messageModelProvider; + + public AddressService( + IRepository
addressRepository, + ICountryService countryService, + IStateProvinceService stateProvinceService, + ICommonServices services, + AddressSettings addressSettings, + ITemplateEngine templateEngine, + IMessageModelProvider messageModelProvider) { _addressRepository = addressRepository; _countryService = countryService; _stateProvinceService = stateProvinceService; _services = services; _addressSettings = addressSettings; - _templateEngine = templateEngine; - _messageModelProvider = messageModelProvider; + _templateEngine = templateEngine; + _messageModelProvider = messageModelProvider; } public virtual void DeleteAddress(Address address) { - Guard.NotNull(address, nameof(address)); + Guard.NotNull(address, nameof(address)); _addressRepository.Delete(address); } - public virtual void DeleteAddress(int id) - { - var address = GetAddressById(id); - if (address != null) - DeleteAddress(address); - } + public virtual void DeleteAddress(int id) + { + var address = GetAddressById(id); + if (address != null) + DeleteAddress(address); + } public virtual int GetAddressTotalByCountryId(int countryId) { @@ -86,23 +86,23 @@ public virtual Address GetAddressById(int addressId) return address; } - public virtual IList
GetAddressByIds(int[] addressIds) - { - Guard.NotNull(addressIds, nameof(addressIds)); + public virtual IList
GetAddressByIds(int[] addressIds) + { + Guard.NotNull(addressIds, nameof(addressIds)); - var query = - from x in _addressRepository.TableUntracked.Expand(x => x.Country).Expand(x => x.StateProvince) - where addressIds.Contains(x.Id) - select x; + var query = + from x in _addressRepository.TableUntracked.Expand(x => x.Country).Expand(x => x.StateProvince) + where addressIds.Contains(x.Id) + select x; - return query.ToList(); - } + return query.ToList(); + } public virtual void InsertAddress(Address address) { - Guard.NotNull(address, nameof(address)); + Guard.NotNull(address, nameof(address)); - address.CreatedOnUtc = DateTime.UtcNow; + address.CreatedOnUtc = DateTime.UtcNow; //some validation if (address.CountryId == 0) @@ -115,10 +115,10 @@ public virtual void InsertAddress(Address address) public virtual void UpdateAddress(Address address) { - Guard.NotNull(address, nameof(address)); + Guard.NotNull(address, nameof(address)); - //some validation - if (address.CountryId == 0) + //some validation + if (address.CountryId == 0) address.CountryId = null; if (address.StateProvinceId == 0) address.StateProvinceId = null; @@ -128,9 +128,9 @@ public virtual void UpdateAddress(Address address) public virtual bool IsAddressValid(Address address) { - Guard.NotNull(address, nameof(address)); + Guard.NotNull(address, nameof(address)); - if (String.IsNullOrWhiteSpace(address.FirstName)) + if (String.IsNullOrWhiteSpace(address.FirstName)) return false; if (String.IsNullOrWhiteSpace(address.LastName)) @@ -184,10 +184,10 @@ public virtual bool IsAddressValid(Address address) } } - if (_addressSettings.CityEnabled && - _addressSettings.CityRequired && - String.IsNullOrWhiteSpace(address.City)) - return false; + if (_addressSettings.CityEnabled && + _addressSettings.CityRequired && + String.IsNullOrWhiteSpace(address.City)) + return false; if (_addressSettings.PhoneEnabled && _addressSettings.PhoneRequired && @@ -202,63 +202,63 @@ public virtual bool IsAddressValid(Address address) return true; } - public virtual string FormatAddress(CompanyInformationSettings settings, bool newLineToBr = false) - { - Guard.NotNull(settings, nameof(settings)); - - var address = new Address - { - Address1 = settings.Street, - Address2 = settings.Street2, - City = settings.City, - Company = settings.CompanyName, - FirstName = settings.Firstname, - LastName = settings.Lastname, - Salutation = settings.Salutation, - Title = settings.Title, - ZipPostalCode = settings.ZipCode, - CountryId = settings.CountryId, - Country = _countryService.GetCountryById(settings.CountryId) - }; - - return FormatAddress(address, newLineToBr); - } - - public virtual string FormatAddress(Address address, bool newLineToBr = false) - { - Guard.NotNull(address, nameof(address)); - - var messageContext = new MessageContext - { - Language = _services.WorkContext.WorkingLanguage, - Store = _services.StoreContext.CurrentStore, - Model = new TemplateModel() - }; - - _messageModelProvider.AddModelPart(address, messageContext, "Address"); - var model = messageContext.Model["Address"]; - - var result = FormatAddress(model, address?.Country?.AddressFormat, messageContext.FormatProvider); - - if (newLineToBr) - { - result = HtmlUtils.ConvertPlainTextToHtml(result); - } - - return result; - } - - public virtual string FormatAddress(object address, string template = null, IFormatProvider formatProvider = null) - { - Guard.NotNull(address, nameof(address)); - - template = template.NullEmpty() ?? Address.DefaultAddressFormat; - - var result = _templateEngine - .Render(template, address, formatProvider ?? CultureInfo.CurrentCulture) - .Compact(true); - - return result; - } - } + public virtual string FormatAddress(CompanyInformationSettings settings, bool newLineToBr = false) + { + Guard.NotNull(settings, nameof(settings)); + + var address = new Address + { + Address1 = settings.Street, + Address2 = settings.Street2, + City = settings.City, + Company = settings.CompanyName, + FirstName = settings.Firstname, + LastName = settings.Lastname, + Salutation = settings.Salutation, + Title = settings.Title, + ZipPostalCode = settings.ZipCode, + CountryId = settings.CountryId, + Country = _countryService.GetCountryById(settings.CountryId) + }; + + return FormatAddress(address, newLineToBr); + } + + public virtual string FormatAddress(Address address, bool newLineToBr = false) + { + Guard.NotNull(address, nameof(address)); + + var messageContext = new MessageContext + { + Language = _services.WorkContext.WorkingLanguage, + Store = _services.StoreContext.CurrentStore, + Model = new TemplateModel() + }; + + _messageModelProvider.AddModelPart(address, messageContext, "Address"); + var model = messageContext.Model["Address"]; + + var result = FormatAddress(model, address?.Country?.AddressFormat, messageContext.FormatProvider); + + if (newLineToBr) + { + result = HtmlUtils.ConvertPlainTextToHtml(result); + } + + return result; + } + + public virtual string FormatAddress(object address, string template = null, IFormatProvider formatProvider = null) + { + Guard.NotNull(address, nameof(address)); + + template = template.NullEmpty() ?? Address.DefaultAddressFormat; + + var result = _templateEngine + .Render(template, address, formatProvider ?? CultureInfo.CurrentCulture) + .Compact(true); + + return result; + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Common/GenericAttributeExtensions.cs b/src/Libraries/SmartStore.Services/Common/GenericAttributeExtensions.cs new file mode 100644 index 0000000000..ad5307de84 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Common/GenericAttributeExtensions.cs @@ -0,0 +1,51 @@ +using System; +using System.Linq; +using System.Runtime.CompilerServices; +using SmartStore.Core; +using SmartStore.Core.Infrastructure; +using SmartStore.Data; + +namespace SmartStore.Services.Common +{ + public static class GenericAttributeExtensions + { + /// + /// Gets an entity generic attribute value + /// + /// Property type + /// Entity + /// Key + /// Load a value specific for a certain store; pass 0 to load a value shared for all stores + /// Attribute value + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TProp GetAttribute(this BaseEntity entity, string key, int storeId = 0) + { + return GetAttribute( + entity, + key, + EngineContext.Current.Resolve(), + storeId); + } + + /// + /// Gets an entity generic attribute value + /// + /// Property type + /// Entity + /// Key + /// GenericAttributeService + /// Load a value specific for a certain store; pass 0 to load a value shared for all stores + /// Attribute value + public static TProp GetAttribute(this BaseEntity entity, string key, IGenericAttributeService genericAttributeService, int storeId = 0) + { + if (entity == null) + throw new ArgumentNullException(nameof(entity)); + + return (genericAttributeService ?? EngineContext.Current.Resolve()).GetAttribute( + entity.GetUnproxiedType().Name, + entity.Id, + key, + storeId); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Common/GenericAttributeExtentions.cs b/src/Libraries/SmartStore.Services/Common/GenericAttributeExtentions.cs deleted file mode 100644 index 47f7a8edf1..0000000000 --- a/src/Libraries/SmartStore.Services/Common/GenericAttributeExtentions.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Linq; -using SmartStore.Core; -using SmartStore.Core.Infrastructure; -using SmartStore.Data; - -namespace SmartStore.Services.Common -{ - public static class GenericAttributeExtentions - { - /// - /// Gets an entity generic attribute value - /// - /// Property type - /// Entity - /// Key - /// Load a value specific for a certain store; pass 0 to load a value shared for all stores - /// Attribute value - public static TProp GetAttribute(this BaseEntity entity, string key, int storeId = 0) - { - return GetAttribute( - entity, - key, - EngineContext.Current.Resolve(), - storeId); - } - - /// - /// Gets an entity generic attribute value - /// - /// Property type - /// Entity - /// Key - /// GenericAttributeService - /// Load a value specific for a certain store; pass 0 to load a value shared for all stores - /// Attribute value - public static TProp GetAttribute(this BaseEntity entity, string key, IGenericAttributeService genericAttributeService, int storeId = 0) - { - if (entity == null) - throw new ArgumentNullException(nameof(entity)); - - return (genericAttributeService ?? EngineContext.Current.Resolve()).GetAttribute( - entity.GetUnproxiedType().Name, - entity.Id, - key, - storeId); - } - } -} diff --git a/src/Libraries/SmartStore.Services/Common/GenericAttributeService.cs b/src/Libraries/SmartStore.Services/Common/GenericAttributeService.cs index 3fb471d1d0..6fca33e692 100644 --- a/src/Libraries/SmartStore.Services/Common/GenericAttributeService.cs +++ b/src/Libraries/SmartStore.Services/Common/GenericAttributeService.cs @@ -14,36 +14,36 @@ namespace SmartStore.Services.Common { public partial class GenericAttributeService : IGenericAttributeService { - private readonly IRepository _genericAttributeRepository; + private readonly IRepository _genericAttributeRepository; private readonly IEventPublisher _eventPublisher; - private readonly IRepository _orderRepository; - private readonly IDictionary> _valueCache; + private readonly IRepository _orderRepository; + private readonly IDictionary> _valueCache; - public GenericAttributeService( + public GenericAttributeService( IRepository genericAttributeRepository, IEventPublisher eventPublisher, - IRepository orderRepository) + IRepository orderRepository) { _genericAttributeRepository = genericAttributeRepository; _eventPublisher = eventPublisher; - _orderRepository = orderRepository; - _valueCache = new Dictionary>(); - } + _orderRepository = orderRepository; + _valueCache = new Dictionary>(); + } public virtual void DeleteAttribute(GenericAttribute attribute) { - Guard.NotNull(attribute, nameof(attribute)); + Guard.NotNull(attribute, nameof(attribute)); - int entityId = attribute.EntityId; - string keyGroup = attribute.KeyGroup; + int entityId = attribute.EntityId; + string keyGroup = attribute.KeyGroup; _genericAttributeRepository.Delete(attribute); - if (keyGroup.IsCaseInsensitiveEqual("Order") && entityId != 0) - { - var order = _orderRepository.GetById(entityId); - _eventPublisher.PublishOrderUpdated(order); - } + if (keyGroup.IsCaseInsensitiveEqual("Order") && entityId != 0) + { + var order = _orderRepository.GetById(entityId); + _eventPublisher.PublishOrderUpdated(order); + } } public virtual GenericAttribute GetAttributeById(int attributeId) @@ -57,171 +57,171 @@ public virtual GenericAttribute GetAttributeById(int attributeId) public virtual void InsertAttribute(GenericAttribute attribute) { - Guard.NotNull(attribute, nameof(attribute)); + Guard.NotNull(attribute, nameof(attribute)); - _genericAttributeRepository.Insert(attribute); + _genericAttributeRepository.Insert(attribute); - if (attribute.KeyGroup.IsCaseInsensitiveEqual("Order") && attribute.EntityId != 0) - { - var order = _orderRepository.GetById(attribute.EntityId); - _eventPublisher.PublishOrderUpdated(order); - } + if (attribute.KeyGroup.IsCaseInsensitiveEqual("Order") && attribute.EntityId != 0) + { + var order = _orderRepository.GetById(attribute.EntityId); + _eventPublisher.PublishOrderUpdated(order); + } } public virtual void UpdateAttribute(GenericAttribute attribute) { - Guard.NotNull(attribute, nameof(attribute)); + Guard.NotNull(attribute, nameof(attribute)); - _genericAttributeRepository.Update(attribute); + _genericAttributeRepository.Update(attribute); - if (attribute.KeyGroup.IsCaseInsensitiveEqual("Order") && attribute.EntityId != 0) - { - var order = _orderRepository.GetById(attribute.EntityId); - _eventPublisher.PublishOrderUpdated(order); - } + if (attribute.KeyGroup.IsCaseInsensitiveEqual("Order") && attribute.EntityId != 0) + { + var order = _orderRepository.GetById(attribute.EntityId); + _eventPublisher.PublishOrderUpdated(order); + } } - public virtual IList GetAttributesForEntity(int entityId, string entityName) + public virtual IList GetAttributesForEntity(int entityId, string entityName) { - var query = from ga in _genericAttributeRepository.Table - where ga.EntityId == entityId && ga.KeyGroup == entityName - select ga; - - return query.ToListCached("db.ga.{0}-{1}".FormatInvariant(entityId, entityName)); - } - - public virtual Multimap GetAttributesForEntity(int[] entityIds, string entityName) - { - Guard.NotNull(entityIds, nameof(entityIds)); - - var query = _genericAttributeRepository.TableUntracked - .Where(x => entityIds.Contains(x.EntityId) && x.KeyGroup == entityName); - - var map = query - .ToList() - .ToMultimap(x => x.EntityId, x => x); - - return map; - } - - public virtual TProp GetAttribute(string entityName, int entityId, string key, int storeId = 0) - { - if (entityName == null) - throw new ArgumentNullException(nameof(entityName)); - - if (key == null) - throw new ArgumentNullException(nameof(key)); - - var entityKey = entityName.ToLowerInvariant() + entityId; - if (!_valueCache.TryGetValue(entityKey, out var attrs)) - { - var list = GetAttributesForEntity(entityId, entityName); - if (list.Count > 0) - { - attrs = list.ToDictionarySafe(k => k.Key.ToLowerInvariant() + k.StoreId, v => (object)v.Value); - } - _valueCache[entityKey] = attrs; - } - - TProp result = default; - - if (attrs != null) - { - var entryKey = key.ToLowerInvariant() + storeId; - if (attrs.TryGetValue(entryKey, out var rawValue)) - { - if (rawValue.GetType() == typeof(TProp)) - { - result = (TProp)rawValue; - } - else - { - result = rawValue.Convert(); - // to skip repeated value type conversion - attrs[entryKey] = result; - } - } - } - - return result; - } - - public virtual IQueryable GetAttributes(string key, string entityName) - { - var query = - from ga in _genericAttributeRepository.Table - where ga.Key == key && ga.KeyGroup == entityName - select ga; - - return query; - } - - public virtual void SaveAttribute(BaseEntity entity, string key, TProp value, int storeId = 0) + var query = from ga in _genericAttributeRepository.Table + where ga.EntityId == entityId && ga.KeyGroup == entityName + select ga; + + return query.ToListCached("db.ga.{0}-{1}".FormatInvariant(entityId, entityName)); + } + + public virtual Multimap GetAttributesForEntity(int[] entityIds, string entityName) + { + Guard.NotNull(entityIds, nameof(entityIds)); + + var query = _genericAttributeRepository.TableUntracked + .Where(x => entityIds.Contains(x.EntityId) && x.KeyGroup == entityName); + + var map = query + .ToList() + .ToMultimap(x => x.EntityId, x => x); + + return map; + } + + public virtual TProp GetAttribute(string entityName, int entityId, string key, int storeId = 0) + { + if (entityName == null) + throw new ArgumentNullException(nameof(entityName)); + + if (key == null) + throw new ArgumentNullException(nameof(key)); + + var entityKey = entityName.ToLowerInvariant() + entityId; + if (!_valueCache.TryGetValue(entityKey, out var attrs)) + { + var list = GetAttributesForEntity(entityId, entityName); + if (list.Count > 0) + { + attrs = list.ToDictionarySafe(k => k.Key.ToLowerInvariant() + k.StoreId, v => (object)v.Value); + } + _valueCache[entityKey] = attrs; + } + + TProp result = default; + + if (attrs != null) + { + var entryKey = key.ToLowerInvariant() + storeId; + if (attrs.TryGetValue(entryKey, out var rawValue)) + { + if (rawValue.GetType() == typeof(TProp)) + { + result = (TProp)rawValue; + } + else + { + result = rawValue.Convert(); + // to skip repeated value type conversion + attrs[entryKey] = result; + } + } + } + + return result; + } + + public virtual IQueryable GetAttributes(string key, string entityName) { - Guard.NotNull(entity, nameof(entity)); + var query = + from ga in _genericAttributeRepository.Table + where ga.Key == key && ga.KeyGroup == entityName + select ga; - SaveAttribute(entity.Id, key, entity.GetUnproxiedType().Name, value, storeId); + return query; } - public virtual void SaveAttribute(int entityId, string key, string entityName, TProp value, int storeId = 0) - { - Guard.NotZero(entityId, nameof(entityId)); - - var valueStr = value.Convert(); - var props = GetAttributesForEntity(entityId, entityName); - - // should be culture invariant - var prop = props.FirstOrDefault(ga => ga.StoreId == storeId && ga.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase)); - - if (prop != null) - { - if (string.IsNullOrWhiteSpace(valueStr)) - { - // delete - DeleteAttribute(prop); - CacheTryRemove(entityName, entityId, prop); - } - else - { - // update - prop.Value = valueStr; - UpdateAttribute(prop); - CacheTryAddOrUpdate(entityName, entityId, prop); - } - } - else - { - if (!string.IsNullOrWhiteSpace(valueStr)) - { - // insert - prop = new GenericAttribute - { - EntityId = entityId, - Key = key, - KeyGroup = entityName, - Value = valueStr, - StoreId = storeId - }; - - InsertAttribute(prop); - - CacheTryAddOrUpdate(entityName, entityId, prop); - } - } - } - - private void CacheTryRemove(string entityName, int entityId, GenericAttribute attr) - { - var entityKey = entityName.ToLowerInvariant() + entityId; - var entryKey = attr.Key.ToLowerInvariant() + attr.StoreId; - _valueCache.Get(entityKey).TryRemove(entryKey, out _); - } - - private void CacheTryAddOrUpdate(string entityName, int entityId, GenericAttribute attr) - { - var entityKey = entityName.ToLowerInvariant() + entityId; - var entryKey = attr.Key.ToLowerInvariant() + attr.StoreId; - _valueCache.Get(entityKey).TryAdd(entryKey, attr.Value, true); - } - } + public virtual void SaveAttribute(BaseEntity entity, string key, TProp value, int storeId = 0) + { + Guard.NotNull(entity, nameof(entity)); + + SaveAttribute(entity.Id, key, entity.GetUnproxiedType().Name, value, storeId); + } + + public virtual void SaveAttribute(int entityId, string key, string entityName, TProp value, int storeId = 0) + { + Guard.NotZero(entityId, nameof(entityId)); + + var valueStr = value.Convert(); + var props = GetAttributesForEntity(entityId, entityName); + + // should be culture invariant + var prop = props.FirstOrDefault(ga => ga.StoreId == storeId && ga.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase)); + + if (prop != null) + { + if (string.IsNullOrWhiteSpace(valueStr)) + { + // delete + DeleteAttribute(prop); + CacheTryRemove(entityName, entityId, prop); + } + else + { + // update + prop.Value = valueStr; + UpdateAttribute(prop); + CacheTryAddOrUpdate(entityName, entityId, prop); + } + } + else + { + if (!string.IsNullOrWhiteSpace(valueStr)) + { + // insert + prop = new GenericAttribute + { + EntityId = entityId, + Key = key, + KeyGroup = entityName, + Value = valueStr, + StoreId = storeId + }; + + InsertAttribute(prop); + + CacheTryAddOrUpdate(entityName, entityId, prop); + } + } + } + + private void CacheTryRemove(string entityName, int entityId, GenericAttribute attr) + { + var entityKey = entityName.ToLowerInvariant() + entityId; + var entryKey = attr.Key.ToLowerInvariant() + attr.StoreId; + _valueCache.Get(entityKey).TryRemove(entryKey, out _); + } + + private void CacheTryAddOrUpdate(string entityName, int entityId, GenericAttribute attr) + { + var entityKey = entityName.ToLowerInvariant() + entityId; + var entryKey = attr.Key.ToLowerInvariant() + attr.StoreId; + _valueCache.Get(entityKey).TryAdd(entryKey, attr.Value, true); + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Common/IAddressService.cs b/src/Libraries/SmartStore.Services/Common/IAddressService.cs index 5ece5f704d..90b9bfb9de 100644 --- a/src/Libraries/SmartStore.Services/Common/IAddressService.cs +++ b/src/Libraries/SmartStore.Services/Common/IAddressService.cs @@ -16,7 +16,7 @@ public partial interface IAddressService /// Address void DeleteAddress(Address address); - void DeleteAddress(int id); + void DeleteAddress(int id); /// /// Gets total number of addresses by country identifier @@ -39,12 +39,12 @@ public partial interface IAddressService /// Address Address GetAddressById(int addressId); - /// - /// Gets addresses by address identifiers - /// - /// Address identifiers - /// Addresses - IList
GetAddressByIds(int[] addressIds); + /// + /// Gets addresses by address identifiers + /// + /// Address identifiers + /// Addresses + IList
GetAddressByIds(int[] addressIds); /// /// Inserts an address @@ -65,28 +65,28 @@ public partial interface IAddressService /// Result bool IsAddressValid(Address address); - /// - /// Formats the address according to the countries address formatting template - /// - /// Address to format - /// Whether new lines should be replaced with html BR tags - /// The formatted address - string FormatAddress(CompanyInformationSettings settings, bool newLineToBr = false); + /// + /// Formats the address according to the countries address formatting template + /// + /// Address to format + /// Whether new lines should be replaced with html BR tags + /// The formatted address + string FormatAddress(CompanyInformationSettings settings, bool newLineToBr = false); - /// - /// Formats the address according to the countries address formatting template - /// - /// Address to format - /// Whether new lines should be replaced with html BR tags - /// The formatted address - string FormatAddress(Address address, bool newLineToBr = false); + /// + /// Formats the address according to the countries address formatting template + /// + /// Address to format + /// Whether new lines should be replaced with html BR tags + /// The formatted address + string FormatAddress(Address address, bool newLineToBr = false); - /// - /// Formats the address according to the countries address formatting template - /// - /// Address to format. Usually passed by the template engine as a dictionary - /// The (liquid) formatting template. If null, the system global template will be used. - /// The formatted address - string FormatAddress(object address, string template = null, IFormatProvider formatProvider = null); - } + /// + /// Formats the address according to the countries address formatting template + /// + /// Address to format. Usually passed by the template engine as a dictionary + /// The (liquid) formatting template. If null, the system global template will be used. + /// The formatted address + string FormatAddress(object address, string template = null, IFormatProvider formatProvider = null); + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Common/IGenericAttributeService.cs b/src/Libraries/SmartStore.Services/Common/IGenericAttributeService.cs index dffd998afe..68653cfec1 100644 --- a/src/Libraries/SmartStore.Services/Common/IGenericAttributeService.cs +++ b/src/Libraries/SmartStore.Services/Common/IGenericAttributeService.cs @@ -44,51 +44,51 @@ public partial interface IGenericAttributeService /// Generic attributes IList GetAttributesForEntity(int entityId, string entityName); - /// - /// Get attributes - /// - /// Entity identifiers - /// Key group - /// Generic attributes - Multimap GetAttributesForEntity(int[] entityIds, string entityName); + /// + /// Get attributes + /// + /// Entity identifiers + /// Key group + /// Generic attributes + Multimap GetAttributesForEntity(int[] entityIds, string entityName); - /// - /// Get attribute value - /// - /// Key group - /// Entity identifier - /// Key - /// Store identifier; pass 0 if this attribute will be available for all stores - /// Converted generic attribute value - TProp GetAttribute(string entityName, int entityId, string key, int storeId = 0); + /// + /// Get attribute value + /// + /// Key group + /// Entity identifier + /// Key + /// Store identifier; pass 0 if this attribute will be available for all stores + /// Converted generic attribute value + TProp GetAttribute(string entityName, int entityId, string key, int storeId = 0); - /// - /// Get queryable attributes - /// - /// The key - /// The key group - /// Queryable attributes - IQueryable GetAttributes(string key, string entityName); + /// + /// Get queryable attributes + /// + /// The key + /// The key group + /// Queryable attributes + IQueryable GetAttributes(string key, string entityName); - /// + /// /// Save attribute value /// /// Property type /// Entity /// Key /// Value - /// Store identifier; pass 0 if this attribute will be available for all stores - void SaveAttribute(BaseEntity entity, string key, TProp value, int storeId = 0); + /// Store identifier; pass 0 if this attribute will be available for all stores + void SaveAttribute(BaseEntity entity, string key, TProp value, int storeId = 0); - /// - /// Save attribute value - /// - /// Property type - /// Entity identifier - /// The key - /// The key group - /// Property type - /// Store identifier; pass 0 if this attribute will be available for all stores - void SaveAttribute(int entityId, string key, string keyGroup, TProp value, int storeId = 0); - } + /// + /// Save attribute value + /// + /// Property type + /// Entity identifier + /// The key + /// The key group + /// Property type + /// Store identifier; pass 0 if this attribute will be available for all stores + void SaveAttribute(int entityId, string key, string keyGroup, TProp value, int storeId = 0); + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Common/IUserAgent.cs b/src/Libraries/SmartStore.Services/Common/IUserAgent.cs index ff20f7b696..5a3c5db1cb 100644 --- a/src/Libraries/SmartStore.Services/Common/IUserAgent.cs +++ b/src/Libraries/SmartStore.Services/Common/IUserAgent.cs @@ -5,148 +5,168 @@ using System.Threading.Tasks; namespace SmartStore.Services.Common -{ - public interface IUserAgent - { - string RawValue { get; set; } - - UserAgentInfo UserAgent { get; } - DeviceInfo Device { get; } - OSInfo OS { get; } +{ + public interface IUserAgent + { + string RawValue { get; set; } - bool IsBot { get; } - bool IsMobileDevice { get; } - bool IsTablet { get; } - bool IsPdfConverter { get; } - } + UserAgentInfo UserAgent { get; } + DeviceInfo Device { get; } + OSInfo OS { get; } - public sealed class DeviceInfo - { - public DeviceInfo(string family, bool isBot) - { - this.Family = family; - this.IsBot = isBot; - } - public override string ToString() - { - return this.Family; - } - public string Family { get; private set; } - public bool IsBot { get; private set; } - } + bool IsBot { get; } + bool IsMobileDevice { get; } + bool IsTablet { get; } + bool IsPdfConverter { get; } + } - public sealed class OSInfo - { - public OSInfo(string family, string major, string minor, string patch, string patchMinor) - { - this.Family = family; - this.Major = major; - this.Minor = minor; - this.Patch = patch; - this.PatchMinor = patchMinor; - } - public override string ToString() - { - var str = VersionString.Format(Major, Minor, Patch, PatchMinor); - return (this.Family + (!string.IsNullOrEmpty(str) ? (" " + str) : null)); - } - public string Family { get; private set; } - public string Major { get; private set; } - public string Minor { get; private set; } - public string Patch { get; private set; } - public string PatchMinor { get; private set; } + public sealed class DeviceInfo + { + public DeviceInfo(string family, bool isBot) + { + this.Family = family; + this.IsBot = isBot; + } + public override string ToString() + { + return this.Family; + } + public string Family { get; private set; } + public bool IsBot { get; private set; } + } - private static string FormatVersionString(params string[] parts) - { - return string.Join(".", (from v in parts - where !string.IsNullOrEmpty(v) - select v).ToArray()); - } - } + public sealed class OSInfo + { + public OSInfo(string family, string major, string minor, string patch, string patchMinor) + { + this.Family = family; + this.Major = major; + this.Minor = minor; + this.Patch = patch; + this.PatchMinor = patchMinor; + } + public override string ToString() + { + var str = VersionString.Format(Major, Minor, Patch, PatchMinor); + return (this.Family + (!string.IsNullOrEmpty(str) ? (" " + str) : null)); + } + public string Family { get; private set; } + public string Major { get; private set; } + public string Minor { get; private set; } + public string Patch { get; private set; } + public string PatchMinor { get; private set; } - public sealed class UserAgentInfo - { - private static readonly HashSet s_Bots = new HashSet(StringComparer.InvariantCultureIgnoreCase) - { - "BingPreview" - }; + private static string FormatVersionString(params string[] parts) + { + return string.Join(".", (from v in parts + where !string.IsNullOrEmpty(v) + select v).ToArray()); + } + } - private bool? _isBot; - private bool? _supportsWebP; + public sealed class UserAgentInfo + { + private static readonly HashSet s_Bots = new HashSet(StringComparer.InvariantCultureIgnoreCase) + { + "BingPreview" + }; - public UserAgentInfo(string family, string major, string minor, string patch) - { - this.Family = family; - this.Major = major; - this.Minor = minor; - this.Patch = patch; - } - public override string ToString() - { - var str = VersionString.Format(Major, Minor, Patch); - return (this.Family + (!string.IsNullOrEmpty(str) ? (" " + str) : null)); - } - public string Family { get; private set; } - public string Major { get; private set; } - public string Minor { get; private set; } - public string Patch { get; private set; } - public bool IsBot - { - get - { - if (!_isBot.HasValue) - { - _isBot = s_Bots.Contains(Family); - } - return _isBot.Value; - } - } - public bool SupportsWebP - { - get - { - if (_supportsWebP == null) - { - if (Family == "Chrome") - { - _supportsWebP = Major.ToInt() >= 49; - } - else if (Family.StartsWith("Chrome Mobile")) - { - _supportsWebP = Major.ToInt() >= 61; - } - else if (Family == "Opera") - { - _supportsWebP = Major.ToInt() >= 48; - } - else if (Family == "Opera Mini") - { - _supportsWebP = true; - } - else if (Family == "Android") - { - _supportsWebP = Major.ToInt() >= 5 || (Major.ToInt() == 4 && Minor.ToInt() >= 4); - } - else - { - _supportsWebP = false; - } - } + private bool? _isBot; + private bool? _supportsWebP; - return _supportsWebP.Value; - } - } - } + public UserAgentInfo(string family, string major, string minor, string patch) + { + this.Family = family; + this.Major = major; + this.Minor = minor; + this.Patch = patch; + } + public override string ToString() + { + var str = VersionString.Format(Major, Minor, Patch); + return (this.Family + (!string.IsNullOrEmpty(str) ? (" " + str) : null)); + } + public string Family { get; private set; } + public string Major { get; private set; } + public string Minor { get; private set; } + public string Patch { get; private set; } + public bool IsBot + { + get + { + if (!_isBot.HasValue) + { + _isBot = s_Bots.Contains(Family); + } + return _isBot.Value; + } + } + public bool SupportsWebP + { + get + { + if (_supportsWebP == null) + { + if (Family == "Chrome") + { + _supportsWebP = Major.ToInt() >= 32; + } + else if (Family.StartsWith("Chrome Mobile")) + { + _supportsWebP = Major.ToInt() >= 79; + } + else if (Family == "Firefox") + { + _supportsWebP = Major.ToInt() >= 65; + } + else if (Family.StartsWith("Firefox Mobile")) + { + _supportsWebP = Major.ToInt() >= 68; + } + else if (Family == "Edge") + { + _supportsWebP = Major.ToInt() >= 18; + } + else if (Family == "Opera") + { + _supportsWebP = Major.ToInt() >= 19; + } + else if (Family == "Opera Mini") + { + _supportsWebP = true; + } + else if (Family.StartsWith("Android")) + { + _supportsWebP = Major.ToInt() >= 5 || (Major.ToInt() == 4 && Minor.ToInt() >= 2); + } + else if (Family.StartsWith("Samsung Internet")) + { + _supportsWebP = Major.ToInt() >= 4; + } + else if (Family.StartsWith("Baidu")) + { + _supportsWebP = Major.ToInt() >= 8 || (Major.ToInt() == 7 && Minor.ToInt() >= 12); + } + else + { + _supportsWebP = false; + } + } - internal static class VersionString - { - public static string Format(params string[] parts) - { - return string.Join(".", (from v in parts - where !string.IsNullOrEmpty(v) - select v).ToArray()); - } - } + return _supportsWebP.Value; + } + } + } + + internal static class VersionString + { + public static string Format(params string[] parts) + { + return string.Join(".", (from v in parts + where !string.IsNullOrEmpty(v) + select v).ToArray()); + } + } } diff --git a/src/Libraries/SmartStore.Services/Common/MaintenanceService.cs b/src/Libraries/SmartStore.Services/Common/MaintenanceService.cs index d5b3c8d746..f6a6aae462 100644 --- a/src/Libraries/SmartStore.Services/Common/MaintenanceService.cs +++ b/src/Libraries/SmartStore.Services/Common/MaintenanceService.cs @@ -44,7 +44,7 @@ public MaintenanceService(IDataProvider dataProvider, IDbContext dbContext, /// /// Entity /// Integer ident; null if cannot get the result - public virtual int? GetTableIdent() where T: BaseEntity + public virtual int? GetTableIdent() where T : BaseEntity { if (_commonSettings.UseStoredProceduresIfSupported && _dataProvider.StoredProceduresSupported) { diff --git a/src/Libraries/SmartStore.Services/Common/MobileDeviceHelper.cs b/src/Libraries/SmartStore.Services/Common/MobileDeviceHelper.cs index 0586d08a98..b37a4d13fa 100644 --- a/src/Libraries/SmartStore.Services/Common/MobileDeviceHelper.cs +++ b/src/Libraries/SmartStore.Services/Common/MobileDeviceHelper.cs @@ -5,16 +5,16 @@ namespace SmartStore.Services.Common { public partial class MobileDeviceHelper : IMobileDeviceHelper { - private readonly IUserAgent _userAgent; + private readonly IUserAgent _userAgent; public MobileDeviceHelper(IUserAgent userAgent) { - _userAgent = userAgent; + _userAgent = userAgent; } public virtual bool IsMobileDevice() { - return _userAgent.IsMobileDevice && !_userAgent.IsTablet; + return _userAgent.IsMobileDevice && !_userAgent.IsTablet; } } diff --git a/src/Libraries/SmartStore.Services/Common/TempFileCleanupTask.cs b/src/Libraries/SmartStore.Services/Common/TempFileCleanupTask.cs index e02429fba3..4ef87fa3d7 100644 --- a/src/Libraries/SmartStore.Services/Common/TempFileCleanupTask.cs +++ b/src/Libraries/SmartStore.Services/Common/TempFileCleanupTask.cs @@ -3,14 +3,14 @@ namespace SmartStore.Services.Common { - /// - /// Task to cleanup temporary files - /// - public partial class TempFileCleanupTask : ITask - { - public void Execute(TaskExecutionContext ctx) - { - FileSystemHelper.ClearTempDirectories(); - } - } + /// + /// Task to cleanup temporary files + /// + public partial class TempFileCleanupTask : ITask + { + public void Execute(TaskExecutionContext ctx) + { + FileSystemHelper.ClearTempDirectories(); + } + } } diff --git a/src/Libraries/SmartStore.Services/Common/UAParserUserAgent.cs b/src/Libraries/SmartStore.Services/Common/UAParserUserAgent.cs index 03f767cc9d..9f1705d9a3 100644 --- a/src/Libraries/SmartStore.Services/Common/UAParserUserAgent.cs +++ b/src/Libraries/SmartStore.Services/Common/UAParserUserAgent.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text.RegularExpressions; using System.Web; using SmartStore.Utilities; @@ -7,258 +8,260 @@ namespace SmartStore.Services.Common { - public class UAParserUserAgent : IUserAgent - { - private readonly static uap.Parser s_uap; - private static readonly Regex s_pdfConverterPattern = new Regex(@"wkhtmltopdf", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + public class UAParserUserAgent : IUserAgent + { + private readonly static uap.Parser s_uap; + private static readonly Regex s_pdfConverterPattern = new Regex(@"wkhtmltopdf", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); - # region Mobile UAs, OS & Devices + #region Mobile UAs, OS & Devices - private static readonly HashSet s_MobileOS = new HashSet(StringComparer.InvariantCultureIgnoreCase) - { - "Android", - "iOS", - "Windows Mobile", - "Windows Phone", - "Windows CE", - "Symbian OS", - "BlackBerry OS", - "BlackBerry Tablet OS", - "Firefox OS", - "Brew MP", - "webOS", - "Bada", - "Kindle", - "Maemo" - }; + private static readonly HashSet s_MobileOS = new HashSet(StringComparer.InvariantCultureIgnoreCase) + { + "Android", + "iOS", + "Windows Mobile", + "Windows Phone", + "Windows CE", + "Symbian OS", + "BlackBerry OS", + "BlackBerry Tablet OS", + "Firefox OS", + "Brew MP", + "webOS", + "Bada", + "Kindle", + "Maemo" + }; - private static readonly HashSet s_MobileBrowsers = new HashSet(StringComparer.InvariantCultureIgnoreCase) - { - "Googlebot-Mobile", - "Baiduspider-mobile", - "Android", - "Firefox Mobile", - "Opera Mobile", - "Opera Mini", - "Mobile Safari", - "Amazon Silk", - "webOS Browser", - "MicroB", - "Ovi Browser", - "NetFront", - "NetFront NX", - "Chrome Mobile", - "Chrome Mobile iOS", - "UC Browser", - "Tizen Browser", - "Baidu Explorer", - "QQ Browser Mini", - "QQ Browser Mobile", - "IE Mobile", - "Polaris", - "ONE Browser", - "iBrowser Mini", - "Nokia Services (WAP) Browser", - "Nokia Browser", - "Nokia OSS Browser", - "BlackBerry WebKit", - "BlackBerry", "Palm", - "Palm Blazer", - "Palm Pre", - "Teleca Browser", - "SEMC-Browser", - "PlayStation Portable", - "Nokia", - "Maemo Browser", - "Obigo", - "Bolt", - "Iris", - "UP.Browser", - "Minimo", - "Bunjaloo", - "Jasmine", - "Dolfin", - "Polaris", - "Skyfire" - }; + private static readonly HashSet s_MobileBrowsers = new HashSet(StringComparer.InvariantCultureIgnoreCase) + { + "Googlebot-Mobile", + "Baiduspider-mobile", + "Android", + "Firefox Mobile", + "Opera Mobile", + "Opera Mini", + "Mobile Safari", + "Amazon Silk", + "webOS Browser", + "MicroB", + "Ovi Browser", + "NetFront", + "NetFront NX", + "Chrome Mobile", + "Chrome Mobile iOS", + "UC Browser", + "Tizen Browser", + "Baidu Explorer", + "QQ Browser Mini", + "QQ Browser Mobile", + "IE Mobile", + "Polaris", + "ONE Browser", + "iBrowser Mini", + "Nokia Services (WAP) Browser", + "Nokia Browser", + "Nokia OSS Browser", + "BlackBerry WebKit", + "BlackBerry", "Palm", + "Palm Blazer", + "Palm Pre", + "Teleca Browser", + "SEMC-Browser", + "PlayStation Portable", + "Nokia", + "Maemo Browser", + "Obigo", + "Bolt", + "Iris", + "UP.Browser", + "Minimo", + "Bunjaloo", + "Jasmine", + "Dolfin", + "Polaris", + "Skyfire" + }; - private static readonly HashSet s_MobileDevices = new HashSet(StringComparer.InvariantCultureIgnoreCase) - { - "BlackBerry", - "MI PAD", - "iPhone", - "iPad", - "iPod", - "Kindle", - "Kindle Fire", - "Nokia", - "Lumia", - "Palm", - "DoCoMo", - "HP TouchPad", - "Xoom", - "Motorola", - "Generic Feature Phone", - "Generic Smartphone" - }; + private static readonly HashSet s_MobileDevices = new HashSet(StringComparer.InvariantCultureIgnoreCase) + { + "BlackBerry", + "MI PAD", + "iPhone", + "iPad", + "iPod", + "Kindle", + "Kindle Fire", + "Nokia", + "Lumia", + "Palm", + "DoCoMo", + "HP TouchPad", + "Xoom", + "Motorola", + "Generic Feature Phone", + "Generic Smartphone" + }; - #endregion + #endregion - private readonly HttpContextBase _httpContext; + private readonly HttpContextBase _httpContext; - private string _rawValue; - private UserAgentInfo _userAgent; - private DeviceInfo _device; - private OSInfo _os; + private string _rawValue; + private UserAgentInfo _userAgent; + private DeviceInfo _device; + private OSInfo _os; - private bool? _isBot; - private bool? _isMobileDevice; - private bool? _isTablet; - private bool? _isPdfConverter; + private bool? _isBot; + private bool? _isMobileDevice; + private bool? _isTablet; + private bool? _isPdfConverter; - static UAParserUserAgent() - { - //s_uap = uap.Parser.GetDefault(); - s_uap = uap.Parser.FromYamlFile(CommonHelper.MapPath("~/App_Data/ua-parser.regexes.yaml")); - } + static UAParserUserAgent() + { + var path = CommonHelper.MapPath("~/App_Data/UAParser.regexes.yaml"); + s_uap = File.Exists(path) + ? uap.Parser.FromYaml(path) + : uap.Parser.GetDefault(); + } - public UAParserUserAgent(HttpContextBase httpContext) - { - this._httpContext = httpContext; - } + public UAParserUserAgent(HttpContextBase httpContext) + { + this._httpContext = httpContext; + } - public string RawValue - { - get - { - if (_rawValue == null) - { - if (_httpContext.Request != null) - { - _rawValue = _httpContext.Request.UserAgent.EmptyNull(); - } - else - { - _rawValue = ""; - } - } + public string RawValue + { + get + { + if (_rawValue == null) + { + if (_httpContext.Request != null) + { + _rawValue = _httpContext.Request.UserAgent.EmptyNull(); + } + else + { + _rawValue = ""; + } + } - return _rawValue; - } - // for (unit) test purpose - set - { - _rawValue = value; - _userAgent = null; - _device = null; - _os = null; - _isBot = null; - _isMobileDevice = null; - _isTablet = null; - _isPdfConverter = null; - } - } + return _rawValue; + } + // for (unit) test purpose + set + { + _rawValue = value; + _userAgent = null; + _device = null; + _os = null; + _isBot = null; + _isMobileDevice = null; + _isTablet = null; + _isPdfConverter = null; + } + } - public virtual UserAgentInfo UserAgent - { - get - { - if (_userAgent == null) - { - var tmp = s_uap.ParseUserAgent(this.RawValue); - _userAgent = new UserAgentInfo(tmp.Family, tmp.Major, tmp.Minor, tmp.Patch); - } - return _userAgent; - } - } + public virtual UserAgentInfo UserAgent + { + get + { + if (_userAgent == null) + { + var tmp = s_uap.ParseUserAgent(this.RawValue); + _userAgent = new UserAgentInfo(tmp.Family, tmp.Major, tmp.Minor, tmp.Patch); + } + return _userAgent; + } + } - public virtual DeviceInfo Device - { - get - { - if (_device == null) - { - var tmp = s_uap.ParseDevice(this.RawValue); - _device = new DeviceInfo(tmp.Family, tmp.IsSpider()); - } - return _device; - } - } + public virtual DeviceInfo Device + { + get + { + if (_device == null) + { + var tmp = s_uap.ParseDevice(this.RawValue); + _device = new DeviceInfo(tmp.Family, tmp.IsSpider()); + } + return _device; + } + } - public virtual OSInfo OS - { - get - { - if (_os == null) - { - var tmp = s_uap.ParseOS(this.RawValue); - _os = new OSInfo(tmp.Family, tmp.Major, tmp.Minor, tmp.Patch, tmp.PatchMinor); - } - return _os; - } - } + public virtual OSInfo OS + { + get + { + if (_os == null) + { + var tmp = s_uap.ParseOS(this.RawValue); + _os = new OSInfo(tmp.Family, tmp.Major, tmp.Minor, tmp.Patch, tmp.PatchMinor); + } + return _os; + } + } - public virtual bool IsBot - { - get - { - if (!_isBot.HasValue) - { - // empty useragent > bad bot! - _isBot = this.RawValue.IsEmpty() || _httpContext.Request.Browser.Crawler || this.Device.IsBot || this.UserAgent.IsBot; - } - return _isBot.Value; - } - } + public virtual bool IsBot + { + get + { + if (!_isBot.HasValue) + { + // empty useragent > bad bot! + _isBot = this.RawValue.IsEmpty() || _httpContext.Request.Browser.Crawler || this.Device.IsBot || this.UserAgent.IsBot; + } + return _isBot.Value; + } + } - public virtual bool IsMobileDevice - { - get - { - if (!_isMobileDevice.HasValue) - { - _isMobileDevice = - s_MobileOS.Contains(this.OS.Family) || - s_MobileBrowsers.Contains(this.UserAgent.Family) || - s_MobileDevices.Contains(this.Device.Family); - } - return _isMobileDevice.Value; - } - } + public virtual bool IsMobileDevice + { + get + { + if (!_isMobileDevice.HasValue) + { + _isMobileDevice = + s_MobileOS.Contains(this.OS.Family) || + s_MobileBrowsers.Contains(this.UserAgent.Family) || + s_MobileDevices.Contains(this.Device.Family); + } + return _isMobileDevice.Value; + } + } - public virtual bool IsTablet - { - get - { - if (!_isTablet.HasValue) - { - _isTablet = - Regex.IsMatch(this.Device.Family, "iPad|Kindle Fire|Nexus 10|Xoom|Transformer|MI PAD|IdeaTab", RegexOptions.CultureInvariant) || - this.OS.Family == "BlackBerry Tablet OS"; - } - return _isTablet.Value; - } - } + public virtual bool IsTablet + { + get + { + if (!_isTablet.HasValue) + { + _isTablet = + Regex.IsMatch(this.Device.Family, "iPad|Kindle Fire|Nexus 10|Xoom|Transformer|MI PAD|IdeaTab", RegexOptions.CultureInvariant) || + this.OS.Family == "BlackBerry Tablet OS"; + } + return _isTablet.Value; + } + } - public virtual bool IsPdfConverter - { - get - { - if (!_isPdfConverter.HasValue) - { - _isPdfConverter = s_pdfConverterPattern.IsMatch(this.RawValue); - } - return _isPdfConverter.Value; - } - } - } + public virtual bool IsPdfConverter + { + get + { + if (!_isPdfConverter.HasValue) + { + _isPdfConverter = s_pdfConverterPattern.IsMatch(this.RawValue); + } + return _isPdfConverter.Value; + } + } + } - internal static class DeviceExtensions - { - internal static bool IsSpider(this UAParser.Device device) - { - return device.Family.Equals("Spider", StringComparison.InvariantCultureIgnoreCase); - } - } + internal static class DeviceExtensions + { + internal static bool IsSpider(this UAParser.Device device) + { + return device.Family.Equals("Spider", StringComparison.InvariantCultureIgnoreCase); + } + } } diff --git a/src/Libraries/SmartStore.Services/CommonServices.cs b/src/Libraries/SmartStore.Services/CommonServices.cs index 28b7c20ff2..2d95688a0f 100644 --- a/src/Libraries/SmartStore.Services/CommonServices.cs +++ b/src/Libraries/SmartStore.Services/CommonServices.cs @@ -1,108 +1,106 @@ using System; -using System.Collections.Generic; -using System.Linq; +using Autofac; using SmartStore.Core; using SmartStore.Core.Caching; using SmartStore.Core.Data; using SmartStore.Core.Events; -using SmartStore.Services.Localization; using SmartStore.Core.Logging; -using SmartStore.Services.Security; +using SmartStore.Core.Security; using SmartStore.Services.Configuration; -using SmartStore.Services.Stores; -using Autofac; using SmartStore.Services.Helpers; +using SmartStore.Services.Localization; using SmartStore.Services.Media; using SmartStore.Services.Messages; +using SmartStore.Services.Stores; namespace SmartStore.Services { - public class CommonServices : ICommonServices - { - private readonly IComponentContext _container; - private readonly Lazy _env; - private readonly Lazy _cacheManager; - private readonly Lazy _requestCache; - private readonly Lazy _dbContext; - private readonly Lazy _storeContext; - private readonly Lazy _webHelper; - private readonly Lazy _workContext; - private readonly Lazy _eventPublisher; - private readonly Lazy _localization; - private readonly Lazy _customerActivity; - private readonly Lazy _pictureService; - private readonly Lazy _notifier; - private readonly Lazy _permissions; - private readonly Lazy _settings; - private readonly Lazy _storeService; - private readonly Lazy _dateTimeHelper; - private readonly Lazy _displayControl; - private readonly Lazy _chronometer; - private readonly Lazy _messageFactory; + public class CommonServices : ICommonServices + { + private readonly IComponentContext _container; + private readonly Lazy _env; + private readonly Lazy _cacheManager; + private readonly Lazy _requestCache; + private readonly Lazy _dbContext; + private readonly Lazy _storeContext; + private readonly Lazy _webHelper; + private readonly Lazy _workContext; + private readonly Lazy _eventPublisher; + private readonly Lazy _localization; + private readonly Lazy _customerActivity; + private readonly Lazy _mediaService; + private readonly Lazy _notifier; + private readonly Lazy _permissions; + private readonly Lazy _settings; + private readonly Lazy _storeService; + private readonly Lazy _dateTimeHelper; + private readonly Lazy _displayControl; + private readonly Lazy _chronometer; + private readonly Lazy _messageFactory; - public CommonServices( - IComponentContext container, - Lazy env, - Lazy cacheManager, - Lazy requestCache, - Lazy dbContext, - Lazy storeContext, - Lazy webHelper, - Lazy workContext, - Lazy eventPublisher, - Lazy localization, - Lazy customerActivity, - Lazy pictureService, - Lazy notifier, - Lazy permissions, - Lazy settings, - Lazy storeService, - Lazy dateTimeHelper, - Lazy displayControl, - Lazy chronometer, - Lazy messageFactory) - { - this._container = container; - this._env = env; - this._cacheManager = cacheManager; - this._requestCache = requestCache; - this._dbContext = dbContext; - this._storeContext = storeContext; - this._webHelper = webHelper; - this._workContext = workContext; - this._eventPublisher = eventPublisher; - this._localization = localization; - this._customerActivity = customerActivity; - this._pictureService = pictureService; - this._notifier = notifier; - this._permissions = permissions; - this._settings = settings; - this._storeService = storeService; - this._dateTimeHelper = dateTimeHelper; - this._displayControl = displayControl; - this._chronometer = chronometer; - this._messageFactory = messageFactory; - } + public CommonServices( + IComponentContext container, + Lazy env, + Lazy cacheManager, + Lazy requestCache, + Lazy dbContext, + Lazy storeContext, + Lazy webHelper, + Lazy workContext, + Lazy eventPublisher, + Lazy localization, + Lazy customerActivity, + Lazy mediaService, + Lazy notifier, + Lazy permissions, + Lazy settings, + Lazy storeService, + Lazy dateTimeHelper, + Lazy displayControl, + Lazy chronometer, + Lazy messageFactory) + { + _container = container; + _env = env; + _cacheManager = cacheManager; + _requestCache = requestCache; + _dbContext = dbContext; + _storeContext = storeContext; + _webHelper = webHelper; + _workContext = workContext; + _eventPublisher = eventPublisher; + _localization = localization; + _customerActivity = customerActivity; + _mediaService = mediaService; + _notifier = notifier; + _permissions = permissions; + _settings = settings; + _storeService = storeService; + _dateTimeHelper = dateTimeHelper; + _displayControl = displayControl; + _chronometer = chronometer; + _messageFactory = messageFactory; + } - public IComponentContext Container => _container; - public IApplicationEnvironment ApplicationEnvironment => _env.Value; - public ICacheManager Cache => _cacheManager.Value; - public IRequestCache RequestCache => _requestCache.Value; - public IDbContext DbContext => _dbContext.Value; - public IStoreContext StoreContext => _storeContext.Value; - public IWebHelper WebHelper => _webHelper.Value; - public IWorkContext WorkContext => _workContext.Value; - public IEventPublisher EventPublisher => _eventPublisher.Value; - public ILocalizationService Localization => _localization.Value; - public ICustomerActivityService CustomerActivity => _customerActivity.Value; - public IPictureService PictureService => _pictureService.Value; - public INotifier Notifier => _notifier.Value; - public IPermissionService Permissions => _permissions.Value; - public ISettingService Settings => _settings.Value; - public IStoreService StoreService => _storeService.Value; - public IDateTimeHelper DateTimeHelper => _dateTimeHelper.Value; - public IDisplayControl DisplayControl => _displayControl.Value; - public IChronometer Chronometer => _chronometer.Value; - public IMessageFactory MessageFactory => _messageFactory.Value; - } + public IComponentContext Container => _container; + public IApplicationEnvironment ApplicationEnvironment => _env.Value; + public ICacheManager Cache => _cacheManager.Value; + public IRequestCache RequestCache => _requestCache.Value; + public IDbContext DbContext => _dbContext.Value; + public IStoreContext StoreContext => _storeContext.Value; + public IWebHelper WebHelper => _webHelper.Value; + public IWorkContext WorkContext => _workContext.Value; + public IEventPublisher EventPublisher => _eventPublisher.Value; + public ILocalizationService Localization => _localization.Value; + public ICustomerActivityService CustomerActivity => _customerActivity.Value; + public IMediaService MediaService => _mediaService.Value; + public INotifier Notifier => _notifier.Value; + public IPermissionService Permissions => _permissions.Value; + public ISettingService Settings => _settings.Value; + public IStoreService StoreService => _storeService.Value; + public IDateTimeHelper DateTimeHelper => _dateTimeHelper.Value; + public IDisplayControl DisplayControl => _displayControl.Value; + public IChronometer Chronometer => _chronometer.Value; + public IMessageFactory MessageFactory => _messageFactory.Value; + } } diff --git a/src/Libraries/SmartStore.Services/Configuration/SettingService.cs b/src/Libraries/SmartStore.Services/Configuration/SettingService.cs index 1caeae24d0..df20288ba2 100644 --- a/src/Libraries/SmartStore.Services/Configuration/SettingService.cs +++ b/src/Libraries/SmartStore.Services/Configuration/SettingService.cs @@ -4,17 +4,19 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.CompilerServices; using Newtonsoft.Json; using SmartStore.ComponentModel; using SmartStore.Core.Caching; using SmartStore.Core.Configuration; using SmartStore.Core.Data; using SmartStore.Core.Domain.Configuration; +using SmartStore.Core.Events; using SmartStore.Core.Logging; namespace SmartStore.Services.Configuration { - public partial class SettingService : ScopedServiceBase, ISettingService + public partial class SettingService : ScopedServiceBase, ISettingService { private const string SETTINGS_ALL_KEY = "setting:all"; @@ -25,120 +27,118 @@ public SettingService(ICacheManager cacheManager, IRepository settingRe { _cacheManager = cacheManager; _settingRepository = settingRepository; + } + + public ILogger Logger { get; set; } = NullLogger.Instance; + + protected virtual IDictionary GetAllCachedSettings() + { + return _cacheManager.Get(SETTINGS_ALL_KEY, () => + { + var query = from s in _settingRepository.TableUntracked + orderby s.Name, s.StoreId + select s; + + var settings = query.ToList(); + var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var s in settings) + { + var settingKey = CreateCacheKey(s.Name, s.StoreId); + + var cachedSetting = new CachedSetting + { + Id = s.Id, + Name = s.Name, + Value = s.Value, + StoreId = s.StoreId + }; + + dictionary[settingKey] = cachedSetting; + } + + return dictionary; + }, independent: true, allowRecursion: true); + } - Logger = NullLogger.Instance; + protected virtual PropertyInfo GetPropertyInfo(Expression> keySelector) + { + var member = keySelector.Body as MemberExpression; + if (member == null) + { + throw new ArgumentException($"Expression '{keySelector}' refers to a method, not a property."); + } + + var propInfo = member.Member as PropertyInfo; + if (propInfo == null) + { + throw new ArgumentException($"Expression '{keySelector}' refers to a field, not a property."); + } + + return propInfo; } - public ILogger Logger { get; set; } - - protected virtual IDictionary GetAllCachedSettings() - { - return _cacheManager.Get(SETTINGS_ALL_KEY, () => - { - var query = from s in _settingRepository.TableUntracked - orderby s.Name, s.StoreId - select s; - - var settings = query.ToList(); - var dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var s in settings) - { - var settingKey = CreateCacheKey(s.Name, s.StoreId); - - var cachedSetting = new CachedSetting - { - Id = s.Id, - Name = s.Name, - Value = s.Value, - StoreId = s.StoreId - }; - - dictionary[settingKey] = cachedSetting; - } - - return dictionary; - }, independent: true); - } - - protected virtual PropertyInfo GetPropertyInfo(Expression> keySelector) - { - var member = keySelector.Body as MemberExpression; - if (member == null) - { - throw new ArgumentException($"Expression '{keySelector}' refers to a method, not a property."); - } - - var propInfo = member.Member as PropertyInfo; - if (propInfo == null) - { - throw new ArgumentException($"Expression '{keySelector}' refers to a field, not a property."); - } - - return propInfo; - } - - public virtual void InsertSetting(Setting setting, bool clearCache = true) + public virtual void InsertSetting(Setting setting, bool clearCache = true) { - Guard.NotNull(setting, nameof(setting)); + Guard.NotNull(setting, nameof(setting)); - _settingRepository.Insert(setting); + _settingRepository.Insert(setting); - HasChanges = true; + HasChanges = true; - if (clearCache) - ClearCache(); + if (clearCache) + ClearCache(); } public virtual void UpdateSetting(Setting setting, bool clearCache = true) { - Guard.NotNull(setting, nameof(setting)); + Guard.NotNull(setting, nameof(setting)); _settingRepository.Update(setting); - HasChanges = true; + HasChanges = true; - if (clearCache) - ClearCache(); + if (clearCache) + ClearCache(); } - private ISettings LoadSettingsJson(Type settingType, int storeId = 0) - { - string key = settingType.Namespace + "." + settingType.Name; + private ISettings LoadSettingsJson(Type settingType, int storeId = 0) + { + string key = settingType.Namespace + "." + settingType.Name; - var settings = (ISettings)Activator.CreateInstance(settingType); + var settings = (ISettings)Activator.CreateInstance(settingType); - var rawSetting = GetSettingByKey(key, storeId: storeId, loadSharedValueIfNotFound: true); - if (rawSetting.HasValue()) - { - JsonConvert.PopulateObject(rawSetting, settings); - } + var rawSetting = GetSettingByKey(key, storeId: storeId, loadSharedValueIfNotFound: true); + if (rawSetting.HasValue()) + { + JsonConvert.PopulateObject(rawSetting, settings); + } - return settings; - } + return settings; + } - private void SaveSettingsJson(ISettings settings) - { - Type t = settings.GetType(); - string key = t.Namespace + "." + t.Name; - var storeId = 0; + private void SaveSettingsJson(ISettings settings) + { + Type t = settings.GetType(); + string key = t.Namespace + "." + t.Name; + var storeId = 0; - var rawSettings = JsonConvert.SerializeObject(settings); - SetSetting(key, rawSettings, storeId, true); - } + var rawSettings = JsonConvert.SerializeObject(settings); + SetSetting(key, rawSettings, storeId, true); + } - private void DeleteSettingsJson() - { - Type t = typeof(T); - string key = t.Namespace + "." + t.Name; + private void DeleteSettingsJson() + { + Type t = typeof(T); + string key = t.Namespace + "." + t.Name; - var setting = GetAllSettings() - .FirstOrDefault(x => x.Name.Equals(key, StringComparison.InvariantCultureIgnoreCase)); + var setting = GetAllSettings() + .FirstOrDefault(x => x.Name.Equals(key, StringComparison.InvariantCultureIgnoreCase)); - if (setting != null) - { - DeleteSetting(setting); - } - } + if (setting != null) + { + DeleteSetting(setting); + } + } public virtual Setting GetSettingById(int settingId) @@ -150,377 +150,385 @@ public virtual Setting GetSettingById(int settingId) return setting; } - public virtual T GetSettingByKey( - string key, - T defaultValue = default, - int storeId = 0, - bool loadSharedValueIfNotFound = false) + public virtual Setting GetSettingEntityByKey(string key, int storeId = 0) { - Guard.NotEmpty(key, nameof(key)); + Guard.NotEmpty(key, nameof(key)); + + var query = _settingRepository.Table.Where(x => x.Name == key); + + if (storeId > 0) + { + query = query.Where(x => x.StoreId == storeId || x.StoreId == 0).OrderByDescending(x => x.StoreId); + } + else + { + query = query.Where(x => x.StoreId == 0); + } - var settings = GetAllCachedSettings(); + return query.FirstOrDefault(); + } + + public virtual T GetSettingByKey( + string key, + T defaultValue = default, + int storeId = 0, + bool loadSharedValueIfNotFound = false) + { + Guard.NotEmpty(key, nameof(key)); - var cacheKey = CreateCacheKey(key, storeId); + var settings = GetAllCachedSettings(); - if (settings.TryGetValue(cacheKey, out CachedSetting cachedSetting)) - { - return cachedSetting.Value.Convert(); - } + var cacheKey = CreateCacheKey(key, storeId); - // fallback to shared (storeId = 0) if desired - if (storeId > 0 && loadSharedValueIfNotFound) - { - cacheKey = CreateCacheKey(key, 0); - if (settings.TryGetValue(cacheKey, out cachedSetting)) - { - return cachedSetting.Value.Convert(); - } - } + if (settings.TryGetValue(cacheKey, out CachedSetting cachedSetting)) + { + return cachedSetting.Value.Convert(); + } + + // fallback to shared (storeId = 0) if desired + if (storeId > 0 && loadSharedValueIfNotFound) + { + cacheKey = CreateCacheKey(key, 0); + if (settings.TryGetValue(cacheKey, out cachedSetting)) + { + return cachedSetting.Value.Convert(); + } + } return defaultValue; } - public virtual IList GetAllSettings() - { - var query = from s in _settingRepository.Table - orderby s.Name, s.StoreId - select s; - var settings = query.ToList(); - return settings; - } - - public virtual bool SettingExists( - T settings, - Expression> keySelector, - int storeId = 0) - where T : ISettings, new() - { - var propInfo = GetPropertyInfo(keySelector); - var key = string.Concat(typeof(T).Name, ".", propInfo.Name); - - string setting = GetSettingByKey(key, storeId: storeId); - return setting != null; - } - - public T LoadSetting(int storeId = 0) where T : ISettings, new() - { - return (T)LoadSettingCore(typeof(T), storeId); - } - - public ISettings LoadSetting(Type settingType, int storeId = 0) - { - Guard.NotNull(settingType, nameof(settingType)); - Guard.HasDefaultConstructor(settingType); - - if (!typeof(ISettings).IsAssignableFrom(settingType)) - { - throw new ArgumentException($"The type to load settings for must be a subclass of the '{typeof(ISettings).FullName}' interface", nameof(settingType)); - } - - return LoadSettingCore(settingType, storeId); - } - - protected virtual ISettings LoadSettingCore(Type settingType, int storeId = 0) - { - var allSettings = GetAllCachedSettings(); - var instance = (ISettings)Activator.CreateInstance(settingType); - var prefix = settingType.Name; - - foreach (var fastProp in FastProperty.GetProperties(settingType).Values) - { - var prop = fastProp.Property; - - // Get properties we can read and write to - if (!prop.CanWrite) - continue; - - string key = prefix + "." + prop.Name; - - if (!allSettings.TryGetValue(CreateCacheKey(key, storeId), out var cachedSetting) && storeId > 0) - { - // // Fallback to shared (storeId = 0) - allSettings.TryGetValue(CreateCacheKey(key, 0), out cachedSetting); - } - - string setting = cachedSetting?.Value; - - if (setting == null) - { - if (fastProp.IsSequenceType) - { - if ((fastProp.GetValue(instance) as IEnumerable) != null) - { - // Instance of IEnumerable<> was already created, most likely in the constructor of the settings concrete class. - // In this case we shouldn't let the EnumerableConverter create a new instance but keep this one. - continue; - } - } - else - { - #region Obsolete ('EnumerableConverter' can handle this case now) - //if (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(List<>)) - //{ - // // convenience: don't return null for simple list types - // var listArg = prop.PropertyType.GetGenericArguments()[0]; - // object list = null; - - // if (listArg == typeof(int)) - // list = new List(); - // else if (listArg == typeof(decimal)) - // list = new List(); - // else if (listArg == typeof(string)) - // list = new List(); - - // if (list != null) - // { - // fastProp.SetValue(settings, list); - // } - //} - #endregion - - continue; - } - - } - - var converter = TypeConverterFactory.GetConverter(prop.PropertyType); - - if (converter == null || !converter.CanConvertFrom(typeof(string))) - continue; - - try - { - object value = converter.ConvertFrom(setting); - - // Set property - fastProp.SetValue(instance, value); - } - catch (Exception ex) - { - var msg = "Could not convert setting '{0}' to type '{1}'".FormatInvariant(key, prop.PropertyType.Name); - Logger.Error(ex, msg); - } - } - - return instance; - } - - public virtual void SetSetting(string key, T value, int storeId = 0, bool clearCache = true) + public virtual IList GetAllSettings() { - Guard.NotEmpty(key, nameof(key)); + var query = from s in _settingRepository.Table + orderby s.Name, s.StoreId + select s; + var settings = query.ToList(); + return settings; + } + + public virtual bool SettingExists( + T settings, + Expression> keySelector, + int storeId = 0) + where T : ISettings, new() + { + var propInfo = GetPropertyInfo(keySelector); + var key = string.Concat(typeof(T).Name, ".", propInfo.Name); + + string setting = GetSettingByKey(key, storeId: storeId); + return setting != null; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T LoadSetting(int storeId = 0) where T : ISettings, new() + { + return (T)LoadSettingCore(typeof(T), storeId); + } + + public ISettings LoadSetting(Type settingType, int storeId = 0) + { + Guard.NotNull(settingType, nameof(settingType)); + Guard.HasDefaultConstructor(settingType); - var str = value.Convert(); - var allSettings = GetAllCachedSettings(); - var cacheKey = CreateCacheKey(key, storeId); - var insert = false; - - if (allSettings.TryGetValue(cacheKey, out CachedSetting cachedSetting)) - { - var setting = GetSettingById(cachedSetting.Id); - if (setting != null) - { - // Update - if (setting.Value != str) - { - setting.Value = str; - UpdateSetting(setting, clearCache); - } - } - else - { - insert = true; - } - } - else - { - insert = true; - } - - if (insert) - { - // Insert - var setting = new Setting - { - Name = key.ToLowerInvariant(), - Value = str, - StoreId = storeId - }; - InsertSetting(setting, clearCache); - } + if (!typeof(ISettings).IsAssignableFrom(settingType)) + { + throw new ArgumentException($"The type to load settings for must be a subclass of the '{typeof(ISettings).FullName}' interface", nameof(settingType)); + } + + return LoadSettingCore(settingType, storeId); } - public void SaveSetting(T settings, int storeId = 0) where T : ISettings, new() + protected virtual ISettings LoadSettingCore(Type settingType, int storeId = 0) { - SaveSettingCore(settings, storeId); + var allSettings = GetAllCachedSettings(); + var instance = (ISettings)Activator.CreateInstance(settingType); + var prefix = settingType.Name; + + foreach (var fastProp in FastProperty.GetProperties(settingType).Values) + { + var prop = fastProp.Property; + + // Get properties we can read and write to + if (!prop.CanWrite) + continue; + + string key = prefix + "." + prop.Name; + + if (!allSettings.TryGetValue(CreateCacheKey(key, storeId), out var cachedSetting) && storeId > 0) + { + // Fallback to shared (storeId = 0) + allSettings.TryGetValue(CreateCacheKey(key, 0), out cachedSetting); + } + + string setting = cachedSetting?.Value; + + if (setting == null) + { + if (fastProp.IsSequenceType) + { + if ((fastProp.GetValue(instance) as IEnumerable) != null) + { + // Instance of IEnumerable<> was already created, most likely in the constructor of the settings concrete class. + // In this case we shouldn't let the EnumerableConverter create a new instance but keep this one. + continue; + } + } + else + { + continue; + } + + } + + var converter = TypeConverterFactory.GetConverter(prop.PropertyType); + + if (converter == null || !converter.CanConvertFrom(typeof(string))) + continue; + + try + { + object value = converter.ConvertFrom(setting); + + // Set property + fastProp.SetValue(instance, value); + } + catch (Exception ex) + { + var msg = "Could not convert setting '{0}' to type '{1}'".FormatInvariant(key, prop.PropertyType.Name); + Logger.Error(ex, msg); + } + } + + return instance; } - public void SaveSetting(ISettings settings, int storeId = 0) - { - SaveSettingCore(settings, storeId); - } + public virtual SaveSettingResult SetSetting(string key, T value, int storeId = 0, bool clearCache = true) + { + Guard.NotEmpty(key, nameof(key)); + + var str = value.Convert(); + var allSettings = GetAllCachedSettings(); + var cacheKey = CreateCacheKey(key, storeId); + var insert = false; + + if (allSettings.TryGetValue(cacheKey, out CachedSetting cachedSetting)) + { + var setting = GetSettingById(cachedSetting.Id); + if (setting != null) + { + // Update + if (setting.Value != str) + { + setting.Value = str; + UpdateSetting(setting, clearCache); + return SaveSettingResult.Modified; + } + } + else + { + insert = true; + } + } + else + { + insert = true; + } + + if (insert) + { + // Insert + var setting = new Setting + { + Name = key.ToLowerInvariant(), + Value = str, + StoreId = storeId + }; + InsertSetting(setting, clearCache); + return SaveSettingResult.Inserted; + } + + return SaveSettingResult.Unchanged; + } - protected virtual void SaveSettingCore(ISettings settings, int storeId = 0) - { - Guard.NotNull(settings, nameof(settings)); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool SaveSetting(T settings, int storeId = 0) where T : ISettings, new() + { + Guard.NotNull(settings, nameof(settings)); - using (BeginScope()) - { - var settingType = settings.GetType(); - var prefix = settingType.Name; + using (BeginScope(clearCache: true)) + { + var hasChanges = false; + var modifiedProperties = new Dictionary(StringComparer.OrdinalIgnoreCase); + var settingType = typeof(T); + var prefix = settingType.Name; - /* We do not clear cache after each setting update. + /* We do not clear cache after each setting update. * This behavior can increase performance because cached settings will not be cleared * and loaded from database after each update */ - foreach (var prop in FastProperty.GetProperties(settingType).Values) - { - // get properties we can read and write to - if (!prop.IsPublicSettable) - continue; - - var converter = TypeConverterFactory.GetConverter(prop.Property.PropertyType); - if (converter == null || !converter.CanConvertFrom(typeof(string))) - continue; - - string key = prefix + "." + prop.Name; - // Duck typing is not supported in C#. That's why we're using dynamic type - dynamic value = prop.GetValue(settings); - - SetSetting(key, value ?? "", storeId, false); - } - } - } - - public virtual void SaveSetting( - T settings, - Expression> keySelector, - int storeId = 0, - bool clearCache = true) where T : ISettings, new() - { - var propInfo = GetPropertyInfo(keySelector); - var key = string.Concat(typeof(T).Name, ".", propInfo.Name); - - // Duck typing is not supported in C#. That's why we're using dynamic type. - var fastProp = FastProperty.GetProperty(propInfo, PropertyCachingStrategy.EagerCached); - dynamic value = fastProp.GetValue(settings); - - SetSetting(key, value ?? "", storeId, clearCache); - } - - public virtual void UpdateSetting( - T settings, - Expression> keySelector, - bool overrideForStore, - int storeId = 0) where T : ISettings, new() - { - if (overrideForStore || storeId == 0) - { - SaveSetting(settings, keySelector, storeId, false); - } - else if (storeId > 0) - { - DeleteSetting(settings, keySelector, storeId); - } - } - - public virtual void DeleteSetting(Setting setting) - { - if (setting == null) - throw new ArgumentNullException("setting"); - - _settingRepository.Delete(setting); - - HasChanges = true; - - ClearCache(); - } + foreach (var prop in FastProperty.GetProperties(settingType).Values) + { + // get properties we can read and write to + if (!prop.IsPublicSettable) + continue; + + var converter = TypeConverterFactory.GetConverter(prop.Property.PropertyType); + if (converter == null || !converter.CanConvertFrom(typeof(string))) + continue; + + string key = prefix + "." + prop.Name; + // Duck typing is not supported in C#. That's why we're using dynamic type + dynamic currentValue = prop.GetValue(settings); + + if (SetSetting(key, currentValue ?? "", storeId, false) > SaveSettingResult.Unchanged) + { + hasChanges = true; + } + } + + return hasChanges; + } + } + + public virtual SaveSettingResult SaveSetting( + T settings, + Expression> keySelector, + int storeId = 0, + bool clearCache = true) where T : ISettings, new() + { + var propInfo = GetPropertyInfo(keySelector); + var key = string.Concat(typeof(T).Name, ".", propInfo.Name); + + // Duck typing is not supported in C#. That's why we're using dynamic type. + var fastProp = FastProperty.GetProperty(propInfo, PropertyCachingStrategy.EagerCached); + dynamic currentValue = fastProp.GetValue(settings); + + return SetSetting(key, currentValue ?? "", storeId, clearCache); + } + + public virtual SaveSettingResult UpdateSetting( + T settings, + Expression> keySelector, + bool overrideForStore, + int storeId = 0) where T : ISettings, new() + { + if (overrideForStore || storeId == 0) + { + return SaveSetting(settings, keySelector, storeId, false); + } + else if (storeId > 0 && DeleteSetting(settings, keySelector, storeId)) + { + return SaveSettingResult.Deleted; + } + + return SaveSettingResult.Unchanged; + } + + public virtual void DeleteSetting(Setting setting) + { + if (setting == null) + throw new ArgumentNullException("setting"); + + _settingRepository.Delete(setting); + + HasChanges = true; + + ClearCache(); + } public virtual void DeleteSetting() where T : ISettings, new() { - using (BeginScope()) - { - var settingsToDelete = new List(); - var allSettings = GetAllSettings(); - foreach (var prop in typeof(T).GetProperties()) - { - string key = typeof(T).Name + "." + prop.Name; - settingsToDelete.AddRange(allSettings.Where(x => x.Name.Equals(key, StringComparison.InvariantCultureIgnoreCase))); - } - - foreach (var setting in settingsToDelete) - { - DeleteSetting(setting); - } - } + using (BeginScope()) + { + var settingsToDelete = new List(); + var allSettings = GetAllSettings(); + foreach (var prop in typeof(T).GetProperties()) + { + string key = typeof(T).Name + "." + prop.Name; + settingsToDelete.AddRange(allSettings.Where(x => x.Name.Equals(key, StringComparison.InvariantCultureIgnoreCase))); + } + + foreach (var setting in settingsToDelete) + { + DeleteSetting(setting); + } + } + } + + public virtual bool DeleteSetting( + T settings, + Expression> keySelector, + int storeId = 0) where T : ISettings, new() + { + var propInfo = GetPropertyInfo(keySelector); + var key = string.Concat(typeof(T).Name, ".", propInfo.Name); + + return DeleteSetting(key, storeId); } - public virtual void DeleteSetting( - T settings, - Expression> keySelector, - int storeId = 0) where T : ISettings, new() - { - var propInfo = GetPropertyInfo(keySelector); - var key = string.Concat(typeof(T).Name, ".", propInfo.Name); - - DeleteSetting(key, storeId); - } - - public virtual void DeleteSetting(string key, int storeId = 0) - { - if (key.HasValue()) - { - key = key.Trim().ToLowerInvariant(); - - var setting = ( - from s in _settingRepository.Table - where s.StoreId == storeId && s.Name == key - select s).FirstOrDefault(); - - if (setting != null) - DeleteSetting(setting); - } - } - - public virtual int DeleteSettings(string rootKey) { - int result = 0; - - if (rootKey.IsEmpty()) - return 0; - - try - { - string sqlDelete = "DELETE FROM [Setting] WHERE [Name] LIKE '{0}%'".FormatWith(rootKey.EndsWith(".") ? rootKey : rootKey + "."); - result = _settingRepository.Context.ExecuteSqlCommand(sqlDelete); - - if (result > 0) - HasChanges = true; - - ClearCache(); - } - catch (Exception exc) - { - exc.Dump(); - } - - return result; - } - - protected override void OnClearCache() - { - _cacheManager.Remove(SETTINGS_ALL_KEY); - } - - protected string CreateCacheKey(string name, int storeId) - { - return name.Trim().ToLowerInvariant() + "/" + storeId.ToString(); - } + public virtual bool DeleteSetting(string key, int storeId = 0) + { + if (key.HasValue()) + { + key = key.Trim().ToLowerInvariant(); + + var setting = ( + from s in _settingRepository.Table + where s.StoreId == storeId && s.Name == key + select s).FirstOrDefault(); + + if (setting != null) + { + DeleteSetting(setting); + return true; + } + } + + return false; + } + + public virtual int DeleteSettings(string rootKey) + { + int result = 0; + + if (rootKey.IsEmpty()) + return 0; + + try + { + string sqlDelete = "DELETE FROM [Setting] WHERE [Name] LIKE '{0}%'".FormatWith(rootKey.EndsWith(".") ? rootKey : rootKey + "."); + result = _settingRepository.Context.ExecuteSqlCommand(sqlDelete); + + if (result > 0) + HasChanges = true; + + ClearCache(); + } + catch (Exception exc) + { + exc.Dump(); + } + + return result; + } + + protected override void OnClearCache() + { + _cacheManager.Remove(SETTINGS_ALL_KEY); + } + + protected string CreateCacheKey(string name, int storeId) + { + return name.Trim().ToLowerInvariant() + "/" + storeId.ToString(); + } } - [Serializable] - public class CachedSetting - { - public int Id { get; set; } - public string Name { get; set; } - public string Value { get; set; } - public int StoreId { get; set; } - } + [Serializable] + public class CachedSetting + { + public int Id { get; set; } + public string Name { get; set; } + public string Value { get; set; } + public int StoreId { get; set; } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Customers/ChangePasswordRequest.cs b/src/Libraries/SmartStore.Services/Customers/ChangePasswordRequest.cs index 54b0e26ef4..cb8e9a5306 100644 --- a/src/Libraries/SmartStore.Services/Customers/ChangePasswordRequest.cs +++ b/src/Libraries/SmartStore.Services/Customers/ChangePasswordRequest.cs @@ -10,7 +10,7 @@ public class ChangePasswordRequest public string NewPassword { get; set; } public string OldPassword { get; set; } - public ChangePasswordRequest(string email, bool validateRequest, + public ChangePasswordRequest(string email, bool validateRequest, PasswordFormat newPasswordFormat, string newPassword, string oldPassword = "") { this.Email = email; diff --git a/src/Libraries/SmartStore.Services/Customers/CookieManager.cs b/src/Libraries/SmartStore.Services/Customers/CookieManager.cs new file mode 100644 index 0000000000..486f1a42b1 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Customers/CookieManager.cs @@ -0,0 +1,179 @@ +using Newtonsoft.Json; +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Infrastructure; +using SmartStore.Core.Localization; +using SmartStore.Core.Logging; +using SmartStore.Core.Plugins; +using SmartStore.Services.Localization; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; + +namespace SmartStore.Services.Customers +{ + + /// + /// Class that will be serialized and stored as string in a cookie. + /// + public class ConsentCookie + { + public bool AllowAnalytics { get; set; } + public bool AllowThirdParty { get; set; } + } + + public partial class CookieManager : ICookieManager + { + private readonly ICommonServices _services; + private readonly ITypeFinder _typeFinder; + private readonly HttpContextBase _httpContext; + private readonly PrivacySettings _privacySettings; + + public const string ConsentCookieName = "CookieConsent"; + + private readonly static object _lock = new object(); + private static IList _cookiePublisherTypes = null; + + public CookieManager( + ICommonServices services, + ITypeFinder typeFinder, + HttpContextBase httpContext, + PrivacySettings privacySettings) + { + _services = services; + _typeFinder = typeFinder; + _httpContext = httpContext; + _privacySettings = privacySettings; + } + + public LocalizerEx T { get; set; } = NullLocalizer.InstanceEx; + + public ILogger Logger { get; set; } = NullLogger.Instance; + + protected virtual IEnumerable GetAllCookiePublishers() + { + if (_cookiePublisherTypes == null) + { + lock (_lock) + { + if (_cookiePublisherTypes == null) + { + _cookiePublisherTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true).ToList(); + } + } + } + + var cookiePublishers = _cookiePublisherTypes + .Select(x => EngineContext.Current.ContainerManager.ResolveUnregistered(x) as ICookiePublisher) + .ToArray(); + + return cookiePublishers; + } + + public virtual List GetAllCookieInfos(bool addSettingCookies = false) + { + var cookieInfos = new List(); + var plugins = GetAllCookiePublishers(); + + foreach (var plugin in plugins) + { + var typedInstance = plugin as ICookiePublisher; + var cookieInfo = typedInstance.GetCookieInfo(); + if (cookieInfo != null) + cookieInfos.AddRange(cookieInfo); + } + + // Add user defined cookies from privacy settings. + if (addSettingCookies && _privacySettings.CookieInfos.HasValue()) + { + var ciList = JsonConvert.DeserializeObject>(_privacySettings.CookieInfos); + + if (ciList != null && ciList.Count > 0) + { + foreach (var ci in ciList) + { + ci.Name = ci.GetLocalized(x => x.Name); + ci.Description = ci.GetLocalized(x => x.Description); + cookieInfos.Add(ci); + } + } + } + + return cookieInfos; + } + + public virtual bool IsCookieAllowed(ControllerContext context, CookieType cookieType) + { + // Ask whether cookie type is allowed. + var cookie = context.HttpContext.Request.Cookies[ConsentCookieName]; + if (cookie != null) + { + try + { + var cookieData = JsonConvert.DeserializeObject(cookie.Value); + if ((cookieData.AllowAnalytics && cookieType == CookieType.Analytics) || (cookieData.AllowThirdParty && cookieType == CookieType.ThirdParty) || cookieType == CookieType.Required) + { + return true; + } + } + catch + { + // Lets be tolerant in case of error. + return true; + } + } + + // If no cookie was set return false + return false; + } + + public virtual void SetConsentCookie(HttpResponseBase response, bool allowAnalytics = false, bool allowThirdParty = false) + { + var expiry = TimeSpan.FromDays(365); + + var cookieData = new ConsentCookie + { + AllowAnalytics = allowAnalytics, + AllowThirdParty = allowThirdParty + }; + + var secure = _services.WebHelper.IsCurrentConnectionSecured(); + var consentCookie = new HttpCookie(ConsentCookieName) + { + // Store JSON serialized object which contains current allowed types (Analytics & ThirdParty) in cookie. + Value = JsonConvert.SerializeObject(cookieData), + Expires = DateTime.UtcNow + expiry, + Secure = secure, + SameSite = secure ? (SameSiteMode)_privacySettings.SameSiteMode : SameSiteMode.Lax + }; + + response.Cookies.Set(consentCookie); + } + + public virtual ConsentCookie GetCookieData(ControllerContext context) + { + var httpContext = context.HttpContext; + + if (httpContext.Items.Contains(ConsentCookieName)) + { + return httpContext.Items[ConsentCookieName] as ConsentCookie; + } + + var cookie = httpContext.Request.Cookies[ConsentCookieName]; + + if (cookie != null) + { + try + { + var cookieData = JsonConvert.DeserializeObject(cookie.Value); + httpContext.Items[ConsentCookieName] = cookieData; + return cookieData; + } + catch { } + } + + return null; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Customers/CustomerContentService.cs b/src/Libraries/SmartStore.Services/Customers/CustomerContentService.cs index 8f83fb33a8..564247e889 100644 --- a/src/Libraries/SmartStore.Services/Customers/CustomerContentService.cs +++ b/src/Libraries/SmartStore.Services/Customers/CustomerContentService.cs @@ -1,43 +1,51 @@ using System; -using System.Collections.Generic; using System.Linq; +using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Customers; -using SmartStore.Core.Events; namespace SmartStore.Services.Customers { public partial class CustomerContentService : ICustomerContentService { private readonly IRepository _contentRepository; - private readonly IEventPublisher _eventPublisher; - public CustomerContentService(IRepository contentRepository, IEventPublisher eventPublisher) + public CustomerContentService(IRepository contentRepository) { _contentRepository = contentRepository; - _eventPublisher = eventPublisher; } public virtual void DeleteCustomerContent(CustomerContent content) { - Guard.NotNull(content, nameof(content)); + Guard.NotNull(content, nameof(content)); _contentRepository.Delete(content); } - public virtual IList GetAllCustomerContent(int customerId, bool? approved) + public virtual IPagedList GetAllCustomerContent( + int customerId, + bool? approved, + int pageIndex = 0, + int pageSize = int.MaxValue) { - var query = from c in _contentRepository.Table - orderby c.CreatedOnUtc descending - where !approved.HasValue || c.IsApproved == approved && - (customerId == 0 || c.CustomerId == customerId) - select c; + var query = _contentRepository.Table; - var content = query.ToList(); + if (approved.HasValue) + query = query.Where(c => c.IsApproved == approved); + if (customerId > 0) + query = query.Where(c => c.CustomerId == customerId); + + var content = new PagedList(query, pageIndex, pageSize); return content; } - public virtual IList GetAllCustomerContent(int customerId, bool? approved, DateTime? fromUtc = null, DateTime? toUtc = null) where T : CustomerContent + public virtual IPagedList GetAllCustomerContent( + int customerId, + bool? approved, + DateTime? fromUtc = null, + DateTime? toUtc = null, + int pageIndex = 0, + int pageSize = int.MaxValue) where T : CustomerContent { var query = _contentRepository.Table; @@ -52,7 +60,7 @@ public virtual IList GetAllCustomerContent(int customerId, bool? approved, query = query.OrderByDescending(c => c.CreatedOnUtc); - var content = query.OfType().ToList(); + var content = new PagedList(query.OfType(), pageIndex, pageSize); return content; } @@ -62,21 +70,21 @@ public virtual CustomerContent GetCustomerContentById(int contentId) return null; return _contentRepository.GetById(contentId); - + } public virtual void InsertCustomerContent(CustomerContent content) { - Guard.NotNull(content, nameof(content)); + Guard.NotNull(content, nameof(content)); - _contentRepository.Insert(content); + _contentRepository.Insert(content); } public virtual void UpdateCustomerContent(CustomerContent content) { - Guard.NotNull(content, nameof(content)); + Guard.NotNull(content, nameof(content)); - _contentRepository.Update(content); + _contentRepository.Update(content); } } } diff --git a/src/Libraries/SmartStore.Services/Customers/CustomerExtensions.cs b/src/Libraries/SmartStore.Services/Customers/CustomerExtensions.cs index 1ce27e546d..cb0880844c 100644 --- a/src/Libraries/SmartStore.Services/Customers/CustomerExtensions.cs +++ b/src/Libraries/SmartStore.Services/Customers/CustomerExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Runtime.CompilerServices; using System.Xml; using SmartStore.Core; using SmartStore.Core.Domain.Customers; @@ -16,7 +17,7 @@ namespace SmartStore.Services.Customers { - public static class CustomerExtentions + public static class CustomerExtentions { private static readonly string[] _systemColors = new string[] { "primary", "secondary", "success", "info", "warning", "danger", "light", "dark" }; @@ -29,12 +30,12 @@ public static class CustomerExtentions /// Result public static bool IsInCustomerRole(this Customer customer, string customerRoleSystemName, bool onlyActiveCustomerRoles = true) { - Guard.NotNull(customer, nameof(customer)); - Guard.NotEmpty(customerRoleSystemName, nameof(customerRoleSystemName)); + Guard.NotNull(customer, nameof(customer)); + Guard.NotEmpty(customerRoleSystemName, nameof(customerRoleSystemName)); - var result = customer.CustomerRoles - .Where(cr => !onlyActiveCustomerRoles || cr.Active) - .Where(cr => cr.SystemName == customerRoleSystemName) + var result = customer.CustomerRoleMappings + .Where(rm => !onlyActiveCustomerRoles || rm.CustomerRole.Active) + .Where(rm => rm.CustomerRole.SystemName == customerRoleSystemName) .FirstOrDefault() != null; return result; @@ -47,10 +48,10 @@ public static bool IsInCustomerRole(this Customer customer, string customerRoleS /// Result public static bool IsBackgroundTaskAccount(this Customer customer) { - Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(customer, nameof(customer)); - if (!customer.IsSystemAccount || customer.SystemName.IsEmpty()) - return false; + if (!customer.IsSystemAccount || customer.SystemName.IsEmpty()) + return false; var result = customer.SystemName.Equals(SystemCustomerNames.BackgroundTask, StringComparison.InvariantCultureIgnoreCase); return result; @@ -63,30 +64,30 @@ public static bool IsBackgroundTaskAccount(this Customer customer) /// Result public static bool IsSearchEngineAccount(this Customer customer) { - Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(customer, nameof(customer)); - if (!customer.IsSystemAccount || customer.SystemName.IsEmpty()) - return false; + if (!customer.IsSystemAccount || customer.SystemName.IsEmpty()) + return false; var result = customer.SystemName.Equals(SystemCustomerNames.SearchEngine, StringComparison.InvariantCultureIgnoreCase); return result; } - /// - /// Gets a value indicating whether customer is the pdf converter - /// - /// Customer - /// Result - public static bool IsPdfConverter(this Customer customer) - { - Guard.NotNull(customer, nameof(customer)); + /// + /// Gets a value indicating whether customer is the pdf converter + /// + /// Customer + /// Result + public static bool IsPdfConverter(this Customer customer) + { + Guard.NotNull(customer, nameof(customer)); - if (!customer.IsSystemAccount || customer.SystemName.IsEmpty()) - return false; + if (!customer.IsSystemAccount || customer.SystemName.IsEmpty()) + return false; - var result = customer.SystemName.Equals(SystemCustomerNames.PdfConverter, StringComparison.InvariantCultureIgnoreCase); - return result; - } + var result = customer.SystemName.Equals(SystemCustomerNames.PdfConverter, StringComparison.InvariantCultureIgnoreCase); + return result; + } /// /// Gets a value indicating whether customer is administrator @@ -94,21 +95,23 @@ public static bool IsPdfConverter(this Customer customer) /// Customer /// A value indicating whether we should look only in active customer roles /// Result + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAdmin(this Customer customer, bool onlyActiveCustomerRoles = true) { return IsInCustomerRole(customer, SystemCustomerRoleNames.Administrators, onlyActiveCustomerRoles); } - /// - /// Gets a value indicating whether customer is super administrator - /// - /// codehint: sm-add - /// Customer - /// A value indicating whether we should look only in active customer roles - /// Result - public static bool IsSuperAdmin(this Customer customer, bool onlyActiveCustomerRoles = true) { - return IsInCustomerRole(customer, SystemCustomerRoleNames.SuperAdministrators, onlyActiveCustomerRoles); - } + /// + /// Gets a value indicating whether customer is super administrator + /// + /// Customer + /// A value indicating whether we should look only in active customer roles + /// Result + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsSuperAdmin(this Customer customer, bool onlyActiveCustomerRoles = true) + { + return IsInCustomerRole(customer, SystemCustomerRoleNames.SuperAdministrators, onlyActiveCustomerRoles); + } /// /// Gets a value indicating whether customer is a forum moderator @@ -116,6 +119,7 @@ public static bool IsSuperAdmin(this Customer customer, bool onlyActiveCustomerR /// Customer /// A value indicating whether we should look only in active customer roles /// Result + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsForumModerator(this Customer customer, bool onlyActiveCustomerRoles = true) { return IsInCustomerRole(customer, SystemCustomerRoleNames.ForumModerators, onlyActiveCustomerRoles); @@ -127,6 +131,7 @@ public static bool IsForumModerator(this Customer customer, bool onlyActiveCusto /// Customer /// A value indicating whether we should look only in active customer roles /// Result + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsRegistered(this Customer customer, bool onlyActiveCustomerRoles = true) { return IsInCustomerRole(customer, SystemCustomerRoleNames.Registered, onlyActiveCustomerRoles); @@ -138,39 +143,64 @@ public static bool IsRegistered(this Customer customer, bool onlyActiveCustomerR /// Customer /// A value indicating whether we should look only in active customer roles /// Result + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsGuest(this Customer customer, bool onlyActiveCustomerRoles = true) { return IsInCustomerRole(customer, SystemCustomerRoleNames.Guests, onlyActiveCustomerRoles); } - + + /// + /// Gets the customer's full name. + /// + /// Customer. + /// Customer full name. public static string GetFullName(this Customer customer) { - if (customer == null) - return string.Empty; - - if (customer.FullName.HasValue()) - { - return customer.FullName; - } - - string name = customer.BillingAddress?.GetFullName(); - if (name.IsEmpty()) - { - name = customer.ShippingAddress?.GetFullName(); - } - if (name.IsEmpty()) - { - name = customer.Addresses.FirstOrDefault()?.GetFullName(); - } - - return name.TrimSafe(); - } + if (customer == null) + return string.Empty; + + if (customer.FullName.HasValue()) + { + return customer.FullName; + } + + string name = customer.BillingAddress?.GetFullName(); + if (name.IsEmpty()) + { + name = customer.ShippingAddress?.GetFullName(); + } + if (name.IsEmpty()) + { + name = customer.Addresses.FirstOrDefault()?.GetFullName(); + } + + return name.TrimSafe(); + } + + /// + /// Gets the display name of a customer (full name, user name or email). + /// + /// Customer. + /// Localizer. + /// Display name of a customer. + public static string GetDisplayName(this Customer customer, Localizer T) + { + if (customer != null) + { + return customer.IsGuest() + ? T("Customer.Guest").Text + : customer.GetFullName().NullEmpty() ?? customer.Username ?? customer.FindEmail(); + } + + return null; + } /// /// Formats the customer name. /// /// Customer entity. /// Formatted customer name. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string FormatUserName(this Customer customer) { return FormatUserName(customer, false); @@ -283,31 +313,31 @@ public static string FormatUserName( /// Find any email address of customer /// public static string FindEmail(this Customer customer) - { - if (customer != null) - { - return customer.Email.NullEmpty() - ?? customer.BillingAddress?.Email?.NullEmpty() - ?? customer.ShippingAddress?.Email?.NullEmpty(); - } - - return null; - } - - public static Language GetLanguage(this Customer customer) - { - if (customer == null) - return null; + { + if (customer != null) + { + return customer.Email.NullEmpty() + ?? customer.BillingAddress?.Email?.NullEmpty() + ?? customer.ShippingAddress?.Email?.NullEmpty(); + } + + return null; + } + + public static Language GetLanguage(this Customer customer) + { + if (customer == null) + return null; var language = EngineContext.Current.Resolve().GetLanguageById(customer.GetAttribute(SystemCustomerAttributeNames.LanguageId)); if (language == null || !language.Published) - { - language = EngineContext.Current.Resolve().WorkingLanguage; - } + { + language = EngineContext.Current.Resolve().WorkingLanguage; + } - return language; - } + return language; + } /// /// Returns a random system color suitable as avatar background color. @@ -336,160 +366,160 @@ public static string GetAvatarColor(this Customer customer, IGenericAttributeSer return color; } - #region Shopping cart - - public static int CountProductsInCart(this Customer customer, ShoppingCartType cartType, int? storeId = null) - { - if (customer != null) - { - var cartService = EngineContext.Current.Resolve(); - var count = cartService.CountItems(customer, cartType, storeId); - - return count; - } - - return 0; - } - - public static List GetCartItems(this Customer customer, ShoppingCartType cartType, int? storeId = null) - { - if (customer != null) - { - var cartService = EngineContext.Current.Resolve(); - var items = cartService.GetCartItems(customer, cartType, storeId); - - return items; - } - - return new List(); - } - - #endregion - - #region Gift cards - - /// - /// Gets coupon codes - /// - /// Customer - /// Coupon codes - public static string[] ParseAppliedGiftCardCouponCodes(this Customer customer) - { - var genericAttributeService = EngineContext.Current.Resolve(); - string existingGiftCartCouponCodes = customer.GetAttribute(SystemCustomerAttributeNames.GiftCardCouponCodes, - genericAttributeService); - - var couponCodes = new List(); - if (String.IsNullOrEmpty(existingGiftCartCouponCodes)) - return couponCodes.ToArray(); - - try - { - var xmlDoc = new XmlDocument(); - xmlDoc.LoadXml(existingGiftCartCouponCodes); - - var nodeList1 = xmlDoc.SelectNodes(@"//GiftCardCouponCodes/CouponCode"); - foreach (XmlNode node1 in nodeList1) - { - if (node1.Attributes != null && node1.Attributes["Code"] != null) - { - string code = node1.Attributes["Code"].InnerText.Trim(); - couponCodes.Add(code); - } - } - } - catch (Exception exc) - { - Debug.Write(exc.ToString()); - } - return couponCodes.ToArray(); - } - - /// - /// Adds a coupon code - /// - /// Customer - /// Coupon code - /// New coupon codes document - public static void ApplyGiftCardCouponCode(this Customer customer, string couponCode) - { - var genericAttributeService = EngineContext.Current.Resolve(); - string result = string.Empty; - try - { - string existingGiftCartCouponCodes = customer.GetAttribute(SystemCustomerAttributeNames.GiftCardCouponCodes, - genericAttributeService); - - couponCode = couponCode.Trim().ToLower(); - - var xmlDoc = new XmlDocument(); - if (String.IsNullOrEmpty(existingGiftCartCouponCodes)) - { - var element1 = xmlDoc.CreateElement("GiftCardCouponCodes"); - xmlDoc.AppendChild(element1); - } - else - { - xmlDoc.LoadXml(existingGiftCartCouponCodes); - } - var rootElement = (XmlElement)xmlDoc.SelectSingleNode(@"//GiftCardCouponCodes"); - - XmlElement gcElement = null; - //find existing - var nodeList1 = xmlDoc.SelectNodes(@"//GiftCardCouponCodes/CouponCode"); - foreach (XmlNode node1 in nodeList1) - { - if (node1.Attributes != null && node1.Attributes["Code"] != null) - { - string couponCodeAttribute = node1.Attributes["Code"].InnerText.Trim(); - if (couponCodeAttribute.ToLower() == couponCode.ToLower()) - { - gcElement = (XmlElement)node1; - break; - } - } - } - - //create new one if not found - if (gcElement == null) - { - gcElement = xmlDoc.CreateElement("CouponCode"); - gcElement.SetAttribute("Code", couponCode); - rootElement.AppendChild(gcElement); - } - - result = xmlDoc.OuterXml; - } - catch (Exception exc) - { - Debug.Write(exc.ToString()); - } - - //apply new value - genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.GiftCardCouponCodes, result); - } - - /// - /// Removes a coupon code - /// - /// Customer - /// Coupon code to remove - /// New coupon codes document - public static void RemoveGiftCardCouponCode(this Customer customer, string couponCode) - { - //get applied coupon codes - var existingCouponCodes = customer.ParseAppliedGiftCardCouponCodes(); - - //clear them - var genericAttributeService = EngineContext.Current.Resolve(); - genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.GiftCardCouponCodes, null); - - //save again except removed one - foreach (string existingCouponCode in existingCouponCodes) - if (!existingCouponCode.Equals(couponCode, StringComparison.InvariantCultureIgnoreCase)) - customer.ApplyGiftCardCouponCode(existingCouponCode); - } - - #endregion - } + #region Shopping cart + + public static int CountProductsInCart(this Customer customer, ShoppingCartType cartType, int? storeId = null) + { + if (customer != null) + { + var cartService = EngineContext.Current.Resolve(); + var count = cartService.CountItems(customer, cartType, storeId); + + return count; + } + + return 0; + } + + public static List GetCartItems(this Customer customer, ShoppingCartType cartType, int? storeId = null) + { + if (customer != null) + { + var cartService = EngineContext.Current.Resolve(); + var items = cartService.GetCartItems(customer, cartType, storeId); + + return items; + } + + return new List(); + } + + #endregion + + #region Gift cards + + /// + /// Gets coupon codes + /// + /// Customer + /// Coupon codes + public static string[] ParseAppliedGiftCardCouponCodes(this Customer customer) + { + var genericAttributeService = EngineContext.Current.Resolve(); + string existingGiftCartCouponCodes = customer.GetAttribute(SystemCustomerAttributeNames.GiftCardCouponCodes, + genericAttributeService); + + var couponCodes = new List(); + if (String.IsNullOrEmpty(existingGiftCartCouponCodes)) + return couponCodes.ToArray(); + + try + { + var xmlDoc = new XmlDocument(); + xmlDoc.LoadXml(existingGiftCartCouponCodes); + + var nodeList1 = xmlDoc.SelectNodes(@"//GiftCardCouponCodes/CouponCode"); + foreach (XmlNode node1 in nodeList1) + { + if (node1.Attributes != null && node1.Attributes["Code"] != null) + { + string code = node1.Attributes["Code"].InnerText.Trim(); + couponCodes.Add(code); + } + } + } + catch (Exception exc) + { + Debug.Write(exc.ToString()); + } + return couponCodes.ToArray(); + } + + /// + /// Adds a coupon code + /// + /// Customer + /// Coupon code + /// New coupon codes document + public static void ApplyGiftCardCouponCode(this Customer customer, string couponCode) + { + var genericAttributeService = EngineContext.Current.Resolve(); + string result = string.Empty; + try + { + string existingGiftCartCouponCodes = customer.GetAttribute(SystemCustomerAttributeNames.GiftCardCouponCodes, + genericAttributeService); + + couponCode = couponCode.Trim().ToLower(); + + var xmlDoc = new XmlDocument(); + if (String.IsNullOrEmpty(existingGiftCartCouponCodes)) + { + var element1 = xmlDoc.CreateElement("GiftCardCouponCodes"); + xmlDoc.AppendChild(element1); + } + else + { + xmlDoc.LoadXml(existingGiftCartCouponCodes); + } + var rootElement = (XmlElement)xmlDoc.SelectSingleNode(@"//GiftCardCouponCodes"); + + XmlElement gcElement = null; + //find existing + var nodeList1 = xmlDoc.SelectNodes(@"//GiftCardCouponCodes/CouponCode"); + foreach (XmlNode node1 in nodeList1) + { + if (node1.Attributes != null && node1.Attributes["Code"] != null) + { + string couponCodeAttribute = node1.Attributes["Code"].InnerText.Trim(); + if (couponCodeAttribute.ToLower() == couponCode.ToLower()) + { + gcElement = (XmlElement)node1; + break; + } + } + } + + //create new one if not found + if (gcElement == null) + { + gcElement = xmlDoc.CreateElement("CouponCode"); + gcElement.SetAttribute("Code", couponCode); + rootElement.AppendChild(gcElement); + } + + result = xmlDoc.OuterXml; + } + catch (Exception exc) + { + Debug.Write(exc.ToString()); + } + + //apply new value + genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.GiftCardCouponCodes, result); + } + + /// + /// Removes a coupon code + /// + /// Customer + /// Coupon code to remove + /// New coupon codes document + public static void RemoveGiftCardCouponCode(this Customer customer, string couponCode) + { + //get applied coupon codes + var existingCouponCodes = customer.ParseAppliedGiftCardCouponCodes(); + + //clear them + var genericAttributeService = EngineContext.Current.Resolve(); + genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.GiftCardCouponCodes, null); + + //save again except removed one + foreach (string existingCouponCode in existingCouponCodes) + if (!existingCouponCode.Equals(couponCode, StringComparison.InvariantCultureIgnoreCase)) + customer.ApplyGiftCardCouponCode(existingCouponCode); + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/Customers/CustomerMessageFactoryExtensions.cs b/src/Libraries/SmartStore.Services/Customers/CustomerMessageFactoryExtensions.cs index 398e45d2a8..af006f35e5 100644 --- a/src/Libraries/SmartStore.Services/Customers/CustomerMessageFactoryExtensions.cs +++ b/src/Libraries/SmartStore.Services/Customers/CustomerMessageFactoryExtensions.cs @@ -5,76 +5,76 @@ namespace SmartStore.Services.Customers { - public static class CustomerMessageFactoryExtensions - { - /// - /// Sends 'New customer' notification message to a store owner - /// - public static CreateMessageResult SendCustomerRegisteredNotificationMessage(this IMessageFactory factory, Customer customer, int languageId = 0) - { - Guard.NotNull(customer, nameof(customer)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.CustomerRegistered, languageId, customer: customer), true); - } + public static class CustomerMessageFactoryExtensions + { + /// + /// Sends 'New customer' notification message to a store owner + /// + public static CreateMessageResult SendCustomerRegisteredNotificationMessage(this IMessageFactory factory, Customer customer, int languageId = 0) + { + Guard.NotNull(customer, nameof(customer)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.CustomerRegistered, languageId, customer: customer), true); + } - /// - /// Sends a welcome message to a customer - /// - public static CreateMessageResult SendCustomerWelcomeMessage(this IMessageFactory factory, Customer customer, int languageId = 0) - { - Guard.NotNull(customer, nameof(customer)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.CustomerWelcome, languageId, customer: customer), true); - } + /// + /// Sends a welcome message to a customer + /// + public static CreateMessageResult SendCustomerWelcomeMessage(this IMessageFactory factory, Customer customer, int languageId = 0) + { + Guard.NotNull(customer, nameof(customer)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.CustomerWelcome, languageId, customer: customer), true); + } - /// - /// Sends an email validation message to a customer - /// - public static CreateMessageResult SendCustomerEmailValidationMessage(this IMessageFactory factory, Customer customer, int languageId = 0) - { - Guard.NotNull(customer, nameof(customer)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.CustomerEmailValidation, languageId, customer: customer), true); - } + /// + /// Sends an email validation message to a customer + /// + public static CreateMessageResult SendCustomerEmailValidationMessage(this IMessageFactory factory, Customer customer, int languageId = 0) + { + Guard.NotNull(customer, nameof(customer)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.CustomerEmailValidation, languageId, customer: customer), true); + } - /// - /// Sends password recovery message to a customer - /// - public static CreateMessageResult SendCustomerPasswordRecoveryMessage(this IMessageFactory factory, Customer customer, int languageId = 0) - { - Guard.NotNull(customer, nameof(customer)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.CustomerPasswordRecovery, languageId, customer: customer), true); - } + /// + /// Sends password recovery message to a customer + /// + public static CreateMessageResult SendCustomerPasswordRecoveryMessage(this IMessageFactory factory, Customer customer, int languageId = 0) + { + Guard.NotNull(customer, nameof(customer)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.CustomerPasswordRecovery, languageId, customer: customer), true); + } - /// - /// Sends wishlist "email a friend" message - /// - public static CreateMessageResult SendShareWishlistMessage(this IMessageFactory factory, Customer customer, - string fromEmail, string toEmail, string personalMessage, int languageId = 0) - { - Guard.NotNull(customer, nameof(customer)); + /// + /// Sends wishlist "email a friend" message + /// + public static CreateMessageResult SendShareWishlistMessage(this IMessageFactory factory, Customer customer, + string fromEmail, string toEmail, string personalMessage, int languageId = 0) + { + Guard.NotNull(customer, nameof(customer)); - var model = new NamedModelPart("Wishlist") - { - ["PersonalMessage"] = personalMessage, - ["From"] = fromEmail, - ["To"] = toEmail - }; + var model = new NamedModelPart("Wishlist") + { + ["PersonalMessage"] = personalMessage, + ["From"] = fromEmail, + ["To"] = toEmail + }; - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.ShareWishlist, languageId, customer: customer), true, model); - } + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.ShareWishlist, languageId, customer: customer), true, model); + } - /// - /// Sends a "new VAT sumitted" notification to a store owner - /// - public static CreateMessageResult SendNewVatSubmittedStoreOwnerNotification(this IMessageFactory factory, Customer customer, string vatName, string vatAddress, int languageId = 0) - { - Guard.NotNull(customer, nameof(customer)); + /// + /// Sends a "new VAT sumitted" notification to a store owner + /// + public static CreateMessageResult SendNewVatSubmittedStoreOwnerNotification(this IMessageFactory factory, Customer customer, string vatName, string vatAddress, int languageId = 0) + { + Guard.NotNull(customer, nameof(customer)); - var model = new NamedModelPart("VatValidationResult") - { - ["Name"] = vatName, - ["Address"] = vatAddress - }; + var model = new NamedModelPart("VatValidationResult") + { + ["Name"] = vatName, + ["Address"] = vatAddress + }; - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewVatSubmittedStoreOwner, languageId, customer: customer), true, model); - } - } + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewVatSubmittedStoreOwner, languageId, customer: customer), true, model); + } + } } diff --git a/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationRequest.cs b/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationRequest.cs index a0804fed2b..d4f93db0cf 100644 --- a/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationRequest.cs +++ b/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationRequest.cs @@ -12,7 +12,7 @@ public class CustomerRegistrationRequest public bool IsApproved { get; set; } public CustomerRegistrationRequest(Customer customer, string email, string username, - string password, + string password, PasswordFormat passwordFormat, bool isApproved = true) { diff --git a/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationResult.cs b/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationResult.cs index fc0dad7d05..12a94af9ae 100644 --- a/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationResult.cs +++ b/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationResult.cs @@ -1,22 +1,19 @@ using System.Collections.Generic; -namespace SmartStore.Services.Customers +namespace SmartStore.Services.Customers { - public class CustomerRegistrationResult + public class CustomerRegistrationResult { public IList Errors { get; set; } - public CustomerRegistrationResult() + public CustomerRegistrationResult() { this.Errors = new List(); } - public bool Success - { - get { return this.Errors.Count == 0; } - } + public bool Success => this.Errors.Count == 0; - public void AddError(string error) + public void AddError(string error) { this.Errors.Add(error); } diff --git a/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationService.cs b/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationService.cs index 6343755061..4cfc490e35 100644 --- a/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationService.cs +++ b/src/Libraries/SmartStore.Services/Customers/CustomerRegistrationService.cs @@ -9,18 +9,18 @@ namespace SmartStore.Services.Customers { - public partial class CustomerRegistrationService : ICustomerRegistrationService + public partial class CustomerRegistrationService : ICustomerRegistrationService { private readonly ICustomerService _customerService; private readonly IEncryptionService _encryptionService; private readonly INewsLetterSubscriptionService _newsLetterSubscriptionService; private readonly RewardPointsSettings _rewardPointsSettings; private readonly CustomerSettings _customerSettings; - private readonly IStoreContext _storeContext; + private readonly IStoreContext _storeContext; private readonly IEventPublisher _eventPublisher; - public CustomerRegistrationService(ICustomerService customerService, - IEncryptionService encryptionService, + public CustomerRegistrationService(ICustomerService customerService, + IEncryptionService encryptionService, INewsLetterSubscriptionService newsLetterSubscriptionService, RewardPointsSettings rewardPointsSettings, CustomerSettings customerSettings, IStoreContext storeContext, IEventPublisher eventPublisher) @@ -30,15 +30,15 @@ public CustomerRegistrationService(ICustomerService customerService, _newsLetterSubscriptionService = newsLetterSubscriptionService; _rewardPointsSettings = rewardPointsSettings; _customerSettings = customerSettings; - _storeContext = storeContext; + _storeContext = storeContext; _eventPublisher = eventPublisher; - T = NullLocalizer.Instance; - } + T = NullLocalizer.Instance; + } - public Localizer T { get; set; } + public Localizer T { get; set; } - public virtual bool ValidateCustomer(string usernameOrEmail, string password) + public virtual bool ValidateCustomer(string usernameOrEmail, string password) { Customer customer = null; @@ -95,8 +95,8 @@ public virtual bool ValidateCustomer(string usernameOrEmail, string password) public virtual CustomerRegistrationResult RegisterCustomer(CustomerRegistrationRequest request) { - Guard.NotNull(request, nameof(request)); - Guard.NotNull(request.Customer, nameof(request.Customer)); + Guard.NotNull(request, nameof(request)); + Guard.NotNull(request.Customer, nameof(request.Customer)); var result = new CustomerRegistrationResult(); @@ -124,7 +124,7 @@ public virtual CustomerRegistrationResult RegisterCustomer(CustomerRegistrationR return result; } - if (!request.Email.IsEmail()) + if (!request.Email.IsEmail()) { result.AddError(T("Common.WrongEmail")); return result; @@ -162,60 +162,60 @@ public virtual CustomerRegistrationResult RegisterCustomer(CustomerRegistrationR switch (request.PasswordFormat) { - case PasswordFormat.Clear: - request.Customer.Password = request.Password; - break; - case PasswordFormat.Encrypted: - request.Customer.Password = _encryptionService.EncryptText(request.Password); - break; - case PasswordFormat.Hashed: - string saltKey = _encryptionService.CreateSaltKey(5); - request.Customer.PasswordSalt = saltKey; - request.Customer.Password = _encryptionService.CreatePasswordHash(request.Password, saltKey, _customerSettings.HashedPasswordFormat); - break; + case PasswordFormat.Clear: + request.Customer.Password = request.Password; + break; + case PasswordFormat.Encrypted: + request.Customer.Password = _encryptionService.EncryptText(request.Password); + break; + case PasswordFormat.Hashed: + string saltKey = _encryptionService.CreateSaltKey(5); + request.Customer.PasswordSalt = saltKey; + request.Customer.Password = _encryptionService.CreatePasswordHash(request.Password, saltKey, _customerSettings.HashedPasswordFormat); + break; } request.Customer.Active = request.IsApproved; - if (_customerSettings.RegisterCustomerRoleId != 0) - { - var customerRole = _customerService.GetCustomerRoleById(_customerSettings.RegisterCustomerRoleId); - request.Customer.CustomerRoles.Add(customerRole); - } - - // Add to 'Registered' role - var registeredRole = _customerService.GetCustomerRoleBySystemName(SystemCustomerRoleNames.Registered); - if (registeredRole == null) - { - throw new SmartException(T("Admin.Customers.CustomerRoles.CannotFoundRole", "Registered")); - } - - request.Customer.CustomerRoles.Add(registeredRole); - - // Remove from 'Guests' role - var guestRole = request.Customer.CustomerRoles.FirstOrDefault(cr => cr.SystemName == SystemCustomerRoleNames.Guests); - if (guestRole != null) - { - request.Customer.CustomerRoles.Remove(guestRole); - } - - // Add reward points for customer registration (if enabled) - if (_rewardPointsSettings.Enabled && _rewardPointsSettings.PointsForRegistration > 0) - { - request.Customer.AddRewardPointsHistoryEntry(_rewardPointsSettings.PointsForRegistration, T("RewardPoints.Message.RegisteredAsCustomer")); - } + var registeredRole = _customerService.GetCustomerRoleBySystemName(SystemCustomerRoleNames.Registered); + if (registeredRole == null) + { + throw new SmartException(T("Admin.Customers.CustomerRoles.CannotFoundRole", "Registered")); + } + + if (_customerSettings.RegisterCustomerRoleId != 0) + { + var customerRole = _customerService.GetCustomerRoleById(_customerSettings.RegisterCustomerRoleId); + if (customerRole != null && customerRole.Id != registeredRole.Id) + { + _customerService.InsertCustomerRoleMapping(new CustomerRoleMapping { CustomerId = request.Customer.Id, CustomerRoleId = customerRole.Id }); + } + } + + // Add to 'Registered' role. + _customerService.InsertCustomerRoleMapping(new CustomerRoleMapping { CustomerId = request.Customer.Id, CustomerRoleId = registeredRole.Id }); + + // Remove from 'Guests' role. + var mappings = request.Customer.CustomerRoleMappings.Where(x => !x.IsSystemMapping && x.CustomerRole.SystemName == SystemCustomerRoleNames.Guests).ToList(); + mappings.Each(x => _customerService.DeleteCustomerRoleMapping(x)); + + // Add reward points for customer registration (if enabled) + if (_rewardPointsSettings.Enabled && _rewardPointsSettings.PointsForRegistration > 0) + { + request.Customer.AddRewardPointsHistoryEntry(_rewardPointsSettings.PointsForRegistration, T("RewardPoints.Message.RegisteredAsCustomer")); + } _customerService.UpdateCustomer(request.Customer); _eventPublisher.Publish(new CustomerRegisteredEvent { Customer = request.Customer }); return result; } - + public virtual PasswordChangeResult ChangePassword(ChangePasswordRequest request) { - Guard.NotNull(request, nameof(request)); + Guard.NotNull(request, nameof(request)); - var result = new PasswordChangeResult(); + var result = new PasswordChangeResult(); if (!request.Email.HasValue()) { result.AddError(T("Account.ChangePassword.Errors.EmailIsNotProvided")); @@ -297,12 +297,12 @@ public virtual PasswordChangeResult ChangePassword(ChangePasswordRequest request public virtual void SetEmail(Customer customer, string newEmail) { - Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(customer, nameof(customer)); newEmail = newEmail.Trim(); string oldEmail = customer.Email; - if (!newEmail.IsEmail()) + if (!newEmail.IsEmail()) throw new SmartException(T("Account.EmailUsernameErrors.NewEmailIsNotValid")); if (newEmail.Length > 100) @@ -329,9 +329,9 @@ public virtual void SetEmail(Customer customer, string newEmail) public virtual void SetUsername(Customer customer, string newUsername) { - Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(customer, nameof(customer)); - if (_customerSettings.CustomerLoginType == CustomerLoginType.Email) + if (_customerSettings.CustomerLoginType == CustomerLoginType.Email) throw new SmartException("Usernames are disabled"); if (!_customerSettings.AllowUsersToChangeUsernames) diff --git a/src/Libraries/SmartStore.Services/Customers/CustomerReportService.cs b/src/Libraries/SmartStore.Services/Customers/CustomerReportService.cs index 6209807af8..4aff7b3b81 100644 --- a/src/Libraries/SmartStore.Services/Customers/CustomerReportService.cs +++ b/src/Libraries/SmartStore.Services/Customers/CustomerReportService.cs @@ -1,151 +1,181 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Data.Entity; +using System.Linq; +using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Payments; using SmartStore.Core.Domain.Shipping; +using SmartStore.Core.Localization; using SmartStore.Services.Helpers; +using SmartStore.Services.Orders; namespace SmartStore.Services.Customers { - /// - /// Customer report service - /// public partial class CustomerReportService : ICustomerReportService { - #region Fields - private readonly IRepository _customerRepository; private readonly IRepository _orderRepository; private readonly ICustomerService _customerService; private readonly IDateTimeHelper _dateTimeHelper; - - #endregion - - #region Ctor - - /// - /// Ctor - /// - /// Customer repository - /// Order repository - /// Customer service - /// Date time helper - public CustomerReportService(IRepository customerRepository, - IRepository orderRepository, ICustomerService customerService, + + public CustomerReportService( + IRepository customerRepository, + IRepository orderRepository, + ICustomerService customerService, IDateTimeHelper dateTimeHelper) { - this._customerRepository = customerRepository; - this._orderRepository = orderRepository; - this._customerService = customerService; - this._dateTimeHelper = dateTimeHelper; + _customerRepository = customerRepository; + _orderRepository = orderRepository; + _customerService = customerService; + _dateTimeHelper = dateTimeHelper; } - #endregion - - #region Methods - - /// - /// Get best customers - /// - /// Order start time; null to load all - /// Order end time; null to load all - /// Order status; null to load all records - /// Order payment status; null to load all records - /// Order shippment status; null to load all records - /// 1 - order by order total, 2 - order by number of orders - /// Report - public virtual IList GetBestCustomersReport(DateTime? startTime, - DateTime? endTime, OrderStatus? os, PaymentStatus? ps, ShippingStatus? ss, int orderBy) + public Localizer T { get; set; } = NullLocalizer.Instance; + + public virtual IPagedList GetTopCustomersReport( + DateTime? startTime, + DateTime? endTime, + OrderStatus? orderStatus, + PaymentStatus? paymentStatus, + ShippingStatus? shippingStatus, + ReportSorting sorting, + int pageIndex = 0, + int pageSize = int.MaxValue) { - int? orderStatusId = null; - if (os.HasValue) - orderStatusId = (int)os.Value; - - int? paymentStatusId = null; - if (ps.HasValue) - paymentStatusId = (int)ps.Value; - - int? shippingStatusId = null; - if (ss.HasValue) - shippingStatusId = (int)ss.Value; - var query1 = from c in _customerRepository.Table - join o in _orderRepository.Table on c.Id equals o.CustomerId - where (!startTime.HasValue || startTime.Value <= o.CreatedOnUtc) && - (!endTime.HasValue || endTime.Value >= o.CreatedOnUtc) && - (!orderStatusId.HasValue || orderStatusId == o.OrderStatusId) && - (!paymentStatusId.HasValue || paymentStatusId == o.PaymentStatusId) && - (!shippingStatusId.HasValue || shippingStatusId == o.ShippingStatusId) && - (!o.Deleted) && - (!c.Deleted) - select new { c, o }; - - var query2 = from co in query1 - group co by co.c.Id into g - select new - { - CustomerId = g.Key, - OrderTotal = g.Sum(x => x.o.OrderTotal), - OrderCount = g.Count() - }; - switch (orderBy) + var orderStatusId = orderStatus.HasValue ? (int)orderStatus.Value : (int?)null; + var paymentStatusId = paymentStatus.HasValue ? (int)paymentStatus.Value : (int?)null; + var shippingStatusId = shippingStatus.HasValue ? (int)shippingStatus.Value : (int?)null; + + var query = + from c in _customerRepository.TableUntracked + join o in _orderRepository.TableUntracked on c.Id equals o.CustomerId + where (!startTime.HasValue || startTime.Value <= o.CreatedOnUtc) && + (!endTime.HasValue || endTime.Value >= o.CreatedOnUtc) && + (!orderStatusId.HasValue || orderStatusId == o.OrderStatusId) && + (!paymentStatusId.HasValue || paymentStatusId == o.PaymentStatusId) && + (!shippingStatusId.HasValue || shippingStatusId == o.ShippingStatusId) && + (!o.Deleted) && + (!c.Deleted) + select new { c, o }; + + var groupedQuery = + from co in query + group co by co.c.Id into g + select new TopCustomerReportLine + { + CustomerId = g.Key, + OrderTotal = g.Sum(x => x.o.OrderTotal), + OrderCount = g.Count() + }; + + switch (sorting) { - case 1: - { - query2 = query2.OrderByDescending(x => x.OrderTotal); - } + case ReportSorting.ByAmountAsc: + groupedQuery = groupedQuery.OrderBy(x => x.OrderTotal); + break; + case ReportSorting.ByAmountDesc: + groupedQuery = groupedQuery.OrderByDescending(x => x.OrderTotal); break; - case 2: - { - query2 = query2.OrderByDescending(x => x.OrderCount); - } + case ReportSorting.ByQuantityAsc: + groupedQuery = groupedQuery.OrderBy(x => x.OrderCount).ThenByDescending(x => x.OrderTotal); break; + case ReportSorting.ByQuantityDesc: default: - throw new ArgumentException("Wrong orderBy parameter", "orderBy"); + groupedQuery = groupedQuery.OrderByDescending(x => x.OrderCount).ThenByDescending(x => x.OrderTotal); + break; } - // load 20 customers - query2 = query2.Take(() => 20); - - var result = query2.ToList().Select(x => - { - return new BestCustomerReportLine() - { - CustomerId = x.CustomerId, - OrderTotal = x.OrderTotal, - OrderCount = x.OrderCount - }; - }).ToList(); + var result = new PagedList(groupedQuery, pageIndex, pageSize); return result; } - /// - /// Gets a report of customers registered in the last days - /// - /// Customers registered in the last days - /// Number of registered customers public virtual int GetRegisteredCustomersReport(int days) { DateTime date = _dateTimeHelper.ConvertToUserTime(DateTime.Now).AddDays(-days); var registeredCustomerRole = _customerService.GetCustomerRoleBySystemName(SystemCustomerRoleNames.Registered); if (registeredCustomerRole == null) + { return 0; + } + + var query = + from c in _customerRepository.Table + from rm in c.CustomerRoleMappings + where !c.Deleted && rm.CustomerRoleId == registeredCustomerRole.Id && c.CreatedOnUtc >= date + select c; + + var count = query.Count(); + return count; + } + + public virtual List GetRegisteredCustomersDate() + { + // Get registred customers in last 365 days, group by day and count them. + var date = _dateTimeHelper.ConvertToUserTime(DateTime.Now).AddDays(-365); + + var registeredCustomerRole = _customerService.GetCustomerRoleBySystemName(SystemCustomerRoleNames.Registered); + if (registeredCustomerRole == null) + { + return new List(); + } var query = from c in _customerRepository.Table - from cr in c.CustomerRoles + from rm in c.CustomerRoleMappings where !c.Deleted && - cr.Id == registeredCustomerRole.Id && - c.CreatedOnUtc >= date - //&& c.CreatedOnUtc <= DateTime.UtcNow - select c; - int count = query.Count(); - return count; + rm.CustomerRoleId == registeredCustomerRole.Id && + c.CreatedOnUtc >= date + group c.CreatedOnUtc by DbFunctions.TruncateTime(c.CreatedOnUtc) into dg + select new { Date = (DateTime)dg.Key, Count = dg.Count() }; + + var data = new List(); + data.Add(new RegistredCustomersDate(DateTime.Now.AddDays(-760), 0)); + + foreach (var item in query.ToList()) + { + data.Add(new RegistredCustomersDate(item.Date, item.Count)); + } + + return data; } - #endregion + public virtual int GetCustomerRegistrations(DateTime? startTimeUtc, DateTime? endTimeUtc) + { + var registeredCustomerRole = _customerService.GetCustomerRoleBySystemName(SystemCustomerRoleNames.Registered); + if (registeredCustomerRole == null) + { + return 0; + } + + var query = _customerRepository.Table; + query = query.Where(x => !x.Deleted); + if (startTimeUtc.HasValue) + { + query = query.Where(x => startTimeUtc.Value <= x.CreatedOnUtc); + } + if (endTimeUtc.HasValue) + { + query = query.Where(x => endTimeUtc.Value >= x.CreatedOnUtc); + } + + query = query.Where(x => x.CustomerRoleMappings.Any(y => y.CustomerRoleId == registeredCustomerRole.Id)); + + return query.Count(); + } + + public struct RegistredCustomersDate + { + public DateTime Date { get; set; } + public int Count { get; set; } + + public RegistredCustomersDate(DateTime date, int count) + { + Date = date; + Count = count; + } + } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Customers/CustomerSearchQuery.cs b/src/Libraries/SmartStore.Services/Customers/CustomerSearchQuery.cs index e0737ac075..1faec0c439 100644 --- a/src/Libraries/SmartStore.Services/Customers/CustomerSearchQuery.cs +++ b/src/Libraries/SmartStore.Services/Customers/CustomerSearchQuery.cs @@ -4,87 +4,87 @@ namespace SmartStore.Services.Customers { - public class CustomerSearchQuery : ICloneable - { - /// - /// Customer registration from; null to load all customers. Default: null. - /// - public DateTime? RegistrationFromUtc { get; set; } - - /// - /// Customer registration to; null to load all customers. Default: null. - /// - public DateTime? RegistrationToUtc { get; set; } - - /// - /// Customer last activity date (from)s. Default: null. - /// - public DateTime? LastActivityFromUtc { get; set; } - - /// - /// A list of customer role identifiers to filter by (at least one match); pass null or empty list in order to load all customers. Default: null. - /// - public int[] CustomerRoleIds { get; set; } - - /// - /// Affiliate identifie. Default: null. - /// - public int? AffiliateId { get; set; } - - /// - /// Email. Default: null. - /// - public string Email { get; set; } - - /// - /// UserName. Default: null. - /// - public string Username { get; set; } - - /// - /// Phone. Default: null. - /// - public string Phone { get; set; } - - /// - /// ZipPostalCode. Default: null. - /// - public string ZipPostalCode { get; set; } - - /// - /// CustomerNumber. Default: null. - /// - public string CustomerNumber { get; set; } - - /// - /// Day of birth. Default: null. - /// - public int? DayOfBirth { get; set; } - - /// - /// Month of birth. Default: null. - /// - public int? MonthOfBirth { get; set; } - - /// - /// Password format. Default: null. - /// - public PasswordFormat? PasswordFormat { get; set; } - - /// - /// Whether to only load customers with shopping cart. Default: false (meaning: no matter) - /// - public bool OnlyWithCart { get; set; } - - /// - /// What shopping cart type to filter; used when 'HasCart' param is 'true'. Default: null. - /// - public ShoppingCartType? CartType { get; set; } - - /// - /// Whether only (soft)-deleted records should be loaded. Default: false (meaning: only undeleted) - /// - public bool? Deleted { get; set; } = false; + public class CustomerSearchQuery : ICloneable + { + /// + /// Customer registration from; null to load all customers. Default: null. + /// + public DateTime? RegistrationFromUtc { get; set; } + + /// + /// Customer registration to; null to load all customers. Default: null. + /// + public DateTime? RegistrationToUtc { get; set; } + + /// + /// Customer last activity date (from)s. Default: null. + /// + public DateTime? LastActivityFromUtc { get; set; } + + /// + /// A list of customer role identifiers to filter by (at least one match); pass null or empty list in order to load all customers. Default: null. + /// + public int[] CustomerRoleIds { get; set; } + + /// + /// Affiliate identifie. Default: null. + /// + public int? AffiliateId { get; set; } + + /// + /// Email. Default: null. + /// + public string Email { get; set; } + + /// + /// UserName. Default: null. + /// + public string Username { get; set; } + + /// + /// Phone. Default: null. + /// + public string Phone { get; set; } + + /// + /// ZipPostalCode. Default: null. + /// + public string ZipPostalCode { get; set; } + + /// + /// CustomerNumber. Default: null. + /// + public string CustomerNumber { get; set; } + + /// + /// Day of birth. Default: null. + /// + public int? DayOfBirth { get; set; } + + /// + /// Month of birth. Default: null. + /// + public int? MonthOfBirth { get; set; } + + /// + /// Password format. Default: null. + /// + public PasswordFormat? PasswordFormat { get; set; } + + /// + /// Whether to only load customers with shopping cart. Default: false (meaning: no matter) + /// + public bool OnlyWithCart { get; set; } + + /// + /// What shopping cart type to filter; used when 'HasCart' param is 'true'. Default: null. + /// + public ShoppingCartType? CartType { get; set; } + + /// + /// Whether only (soft)-deleted records should be loaded. Default: false (meaning: only undeleted) + /// + public bool? Deleted { get; set; } = false; /// /// Whether only active customers should be loaded. @@ -96,29 +96,29 @@ public class CustomerSearchQuery : ICloneable /// public bool? IsSystemAccount { get; set; } = false; - /// - /// Searches in FullName (FirstName + LastName) and Company fields. Default: null. - /// - public string SearchTerm { get; set; } - - /// - /// Page index. Default: 0. - /// - public int PageIndex { get; set; } - - /// - /// Page index. Default: 50. - /// - public int PageSize { get; set; } = 50; - - public CustomerSearchQuery Clone() - { - return (CustomerSearchQuery)this.MemberwiseClone(); - } - - object ICloneable.Clone() - { - return this.MemberwiseClone(); - } - } + /// + /// Searches in FullName (FirstName + LastName) and Company fields. Default: null. + /// + public string SearchTerm { get; set; } + + /// + /// Page index. Default: 0. + /// + public int PageIndex { get; set; } + + /// + /// Page index. Default: 50. + /// + public int PageSize { get; set; } = 50; + + public CustomerSearchQuery Clone() + { + return (CustomerSearchQuery)this.MemberwiseClone(); + } + + object ICloneable.Clone() + { + return this.MemberwiseClone(); + } + } } diff --git a/src/Libraries/SmartStore.Services/Customers/CustomerService.cs b/src/Libraries/SmartStore.Services/Customers/CustomerService.cs index ae2cbf4c91..10c842935b 100644 --- a/src/Libraries/SmartStore.Services/Customers/CustomerService.cs +++ b/src/Libraries/SmartStore.Services/Customers/CustomerService.cs @@ -1,17 +1,16 @@ using System; using System.Collections.Generic; +using System.Data.Entity; using System.Diagnostics; using System.Linq; -using System.Linq.Expressions; +using System.Text; using System.Web; -using System.Data.Entity; using SmartStore.Collections; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Customers; -using SmartStore.Core.Domain.Forums; using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Shipping; using SmartStore.Core.Fakes; @@ -20,7 +19,7 @@ using SmartStore.Data.Caching; using SmartStore.Services.Common; using SmartStore.Services.Localization; -using System.Threading.Tasks; +using SmartStore.Services.Security; namespace SmartStore.Services.Customers { @@ -28,57 +27,60 @@ public partial class CustomerService : ICustomerService { private readonly IRepository _customerRepository; private readonly IRepository _customerRoleRepository; + private readonly IRepository _customerRoleMappingRepository; private readonly IRepository _gaRepository; - private readonly IRepository _rewardPointsHistoryRepository; + private readonly IRepository _rewardPointsHistoryRepository; private readonly IRepository _shoppingCartItemRepository; private readonly IGenericAttributeService _genericAttributeService; - private readonly RewardPointsSettings _rewardPointsSettings; - private readonly ICommonServices _services; - private readonly HttpContextBase _httpContext; - private readonly IUserAgent _userAgent; - private readonly CustomerSettings _customerSettings; - private readonly Lazy _gdprTool; - - public CustomerService( + private readonly Lazy _rewardPointsSettings; + private readonly ICommonServices _services; + private readonly HttpContextBase _httpContext; + private readonly IUserAgent _userAgent; + private readonly CustomerSettings _customerSettings; + private readonly Lazy _gdprTool; + + public CustomerService( IRepository customerRepository, IRepository customerRoleRepository, + IRepository customerRoleMappingRepository, IRepository gaRepository, - IRepository rewardPointsHistoryRepository, + IRepository rewardPointsHistoryRepository, IRepository shoppingCartItemRepository, IGenericAttributeService genericAttributeService, - RewardPointsSettings rewardPointsSettings, - ICommonServices services, - HttpContextBase httpContext, - IUserAgent userAgent, - CustomerSettings customerSettings, - Lazy gdprTool) + Lazy rewardPointsSettings, + ICommonServices services, + HttpContextBase httpContext, + IUserAgent userAgent, + CustomerSettings customerSettings, + Lazy gdprTool) { _customerRepository = customerRepository; _customerRoleRepository = customerRoleRepository; + _customerRoleMappingRepository = customerRoleMappingRepository; _gaRepository = gaRepository; - _rewardPointsHistoryRepository = rewardPointsHistoryRepository; + _rewardPointsHistoryRepository = rewardPointsHistoryRepository; _shoppingCartItemRepository = shoppingCartItemRepository; _genericAttributeService = genericAttributeService; - _rewardPointsSettings = rewardPointsSettings; - _services = services; - _httpContext = httpContext; - _userAgent = userAgent; - _customerSettings = customerSettings; - _gdprTool = gdprTool; - - T = NullLocalizer.Instance; - Logger = NullLogger.Instance; + _rewardPointsSettings = rewardPointsSettings; + _services = services; + _httpContext = httpContext; + _userAgent = userAgent; + _customerSettings = customerSettings; + _gdprTool = gdprTool; + + T = NullLocalizer.Instance; + Logger = NullLogger.Instance; } - public Localizer T { get; set; } + public Localizer T { get; set; } - public ILogger Logger { get; set; } + public ILogger Logger { get; set; } - #region Customers + #region Customers - public virtual IPagedList SearchCustomers(CustomerSearchQuery q) - { - Guard.NotNull(q, nameof(q)); + public virtual IPagedList SearchCustomers(CustomerSearchQuery q) + { + Guard.NotNull(q, nameof(q)); var isOrdered = false; IQueryable query = null; @@ -116,105 +118,105 @@ select grp query = _customerRepository.Table; } - if (q.Email.HasValue()) - { - query = query.Where(c => c.Email.Contains(q.Email)); - } - - if (q.Username.HasValue()) - { - query = query.Where(c => c.Username.Contains(q.Username)); - } - - if (q.CustomerNumber.HasValue()) - { - query = query.Where(c => c.CustomerNumber.Contains(q.CustomerNumber)); - } - - if (q.AffiliateId.GetValueOrDefault() > 0) - { - query = query.Where(c => c.AffiliateId == q.AffiliateId.Value); - } - - if (q.SearchTerm.HasValue()) - { - if (_customerSettings.CompanyEnabled) - { - query = query.Where(c => c.FullName.Contains(q.SearchTerm) || c.Company.Contains(q.SearchTerm)); - } - else - { - query = query.Where(c => c.FullName.Contains(q.SearchTerm)); - } - } - - if (q.DayOfBirth.GetValueOrDefault() > 0) - { - query = query.Where(c => c.BirthDate.Value.Day == q.DayOfBirth.Value); - } - - if (q.MonthOfBirth.GetValueOrDefault() > 0) - { - query = query.Where(c => c.BirthDate.Value.Month == q.MonthOfBirth.Value); - } - - if (q.RegistrationFromUtc.HasValue) - { - query = query.Where(c => q.RegistrationFromUtc.Value <= c.CreatedOnUtc); - } - - if (q.RegistrationToUtc.HasValue) - { - query = query.Where(c => q.RegistrationToUtc.Value >= c.CreatedOnUtc); - } - - if (q.LastActivityFromUtc.HasValue) - { - query = query.Where(c => q.LastActivityFromUtc.Value <= c.LastActivityDateUtc); - } - - if (q.CustomerRoleIds != null && q.CustomerRoleIds.Length > 0) - { - query = query.Where(c => c.CustomerRoles.Select(cr => cr.Id).Intersect(q.CustomerRoleIds).Count() > 0); - } - - if (q.Deleted.HasValue) - { - query = query.Where(c => c.Deleted == q.Deleted.Value); - } + if (q.Email.HasValue()) + { + query = query.Where(c => c.Email.Contains(q.Email)); + } + + if (q.Username.HasValue()) + { + query = query.Where(c => c.Username.Contains(q.Username)); + } + + if (q.CustomerNumber.HasValue()) + { + query = query.Where(c => c.CustomerNumber.Contains(q.CustomerNumber)); + } + + if (q.AffiliateId.GetValueOrDefault() > 0) + { + query = query.Where(c => c.AffiliateId == q.AffiliateId.Value); + } + + if (q.SearchTerm.HasValue()) + { + if (_customerSettings.CompanyEnabled) + { + query = query.Where(c => c.FullName.Contains(q.SearchTerm) || c.Company.Contains(q.SearchTerm)); + } + else + { + query = query.Where(c => c.FullName.Contains(q.SearchTerm)); + } + } + + if (q.DayOfBirth.GetValueOrDefault() > 0) + { + query = query.Where(c => c.BirthDate.Value.Day == q.DayOfBirth.Value); + } + + if (q.MonthOfBirth.GetValueOrDefault() > 0) + { + query = query.Where(c => c.BirthDate.Value.Month == q.MonthOfBirth.Value); + } + + if (q.RegistrationFromUtc.HasValue) + { + query = query.Where(c => q.RegistrationFromUtc.Value <= c.CreatedOnUtc); + } + + if (q.RegistrationToUtc.HasValue) + { + query = query.Where(c => q.RegistrationToUtc.Value >= c.CreatedOnUtc); + } + + if (q.LastActivityFromUtc.HasValue) + { + query = query.Where(c => q.LastActivityFromUtc.Value <= c.LastActivityDateUtc); + } + + if (q.CustomerRoleIds != null && q.CustomerRoleIds.Length > 0) + { + query = query.Where(c => c.CustomerRoleMappings.Select(rm => rm.CustomerRoleId).Intersect(q.CustomerRoleIds).Count() > 0); + } + + if (q.Deleted.HasValue) + { + query = query.Where(c => c.Deleted == q.Deleted.Value); + } if (q.Active.HasValue) { query = query.Where(c => c.Active == q.Active.Value); } - if (q.IsSystemAccount.HasValue) - { - query = q.IsSystemAccount.Value == true - ? query.Where(c => !string.IsNullOrEmpty(c.SystemName)) - : query.Where(c => string.IsNullOrEmpty(c.SystemName)); - } - - if (q.PasswordFormat.HasValue) - { - int passwordFormatId = (int)q.PasswordFormat.Value; - query = query.Where(c => c.PasswordFormatId == passwordFormatId); - } - - // Search by phone - if (q.Phone.HasValue()) - { - query = query - .Join(_gaRepository.Table, x => x.Id, y => y.EntityId, (x, y) => new { Customer = x, Attribute = y }) - .Where(z => z.Attribute.KeyGroup == "Customer" && - z.Attribute.Key == SystemCustomerAttributeNames.Phone && - z.Attribute.Value.Contains(q.Phone)) - .Select(z => z.Customer); - } - - // Search by zip - if (q.ZipPostalCode.HasValue()) - { + if (q.IsSystemAccount.HasValue) + { + query = q.IsSystemAccount.Value == true + ? query.Where(c => !string.IsNullOrEmpty(c.SystemName)) + : query.Where(c => string.IsNullOrEmpty(c.SystemName)); + } + + if (q.PasswordFormat.HasValue) + { + int passwordFormatId = (int)q.PasswordFormat.Value; + query = query.Where(c => c.PasswordFormatId == passwordFormatId); + } + + // Search by phone + if (q.Phone.HasValue()) + { + query = query + .Join(_gaRepository.Table, x => x.Id, y => y.EntityId, (x, y) => new { Customer = x, Attribute = y }) + .Where(z => z.Attribute.KeyGroup == "Customer" && + z.Attribute.Key == SystemCustomerAttributeNames.Phone && + z.Attribute.Value.Contains(q.Phone)) + .Select(z => z.Customer); + } + + // Search by zip + if (q.ZipPostalCode.HasValue()) + { query = query .Join(_gaRepository.Table, x => x.Id, y => y.EntityId, (x, y) => new { Customer = x, Attribute = y }) .Where(z => z.Attribute.KeyGroup == "Customer" && @@ -228,87 +230,91 @@ select grp query = query.OrderByDescending(c => c.CreatedOnUtc); } - var customers = new PagedList(query, q.PageIndex, q.PageSize); - return customers; - } + var customers = new PagedList(query, q.PageIndex, q.PageSize); + return customers; + } - public virtual IPagedList GetAllCustomersByPasswordFormat(PasswordFormat passwordFormat) + public virtual IPagedList GetAllCustomersByPasswordFormat(PasswordFormat passwordFormat) { - var q = new CustomerSearchQuery - { - PasswordFormat = passwordFormat, - PageIndex = 0, - PageSize = 500 - }; + var q = new CustomerSearchQuery + { + PasswordFormat = passwordFormat, + PageIndex = 0, + PageSize = 500 + }; - var customers = SearchCustomers(q); - return customers; + var customers = SearchCustomers(q); + return customers; } public virtual IPagedList GetOnlineCustomers(DateTime lastActivityFromUtc, int[] customerRoleIds, int pageIndex, int pageSize) { - var q = new CustomerSearchQuery - { - LastActivityFromUtc = lastActivityFromUtc, - CustomerRoleIds = customerRoleIds, - IsSystemAccount = false, - PageIndex = pageIndex, - PageSize = pageSize - }; + var q = new CustomerSearchQuery + { + LastActivityFromUtc = lastActivityFromUtc, + CustomerRoleIds = customerRoleIds, + IsSystemAccount = false, + PageIndex = pageIndex, + PageSize = pageSize + }; var customers = SearchCustomers(q); - customers.AlterQuery(x => x.OrderByDescending(c => c.LastActivityDateUtc)); + customers.AlterQuery(x => x.OrderByDescending(c => c.LastActivityDateUtc)); return customers; } public virtual void DeleteCustomer(Customer customer) { - Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(customer, nameof(customer)); if (customer.IsSystemAccount) throw new SmartException(string.Format("System customer account ({0}) cannot not be deleted", customer.SystemName)); - // Soft delete + // Soft delete customer.Deleted = true; - // Anonymize IP addresses - var language = customer.GetLanguage(); + // Anonymize IP addresses + var language = customer.GetLanguage(); - _gdprTool.Value.AnonymizeData(customer, x => x.LastIpAddress, IdentifierDataType.IpAddress, language); + _gdprTool.Value.AnonymizeData(customer, x => x.LastIpAddress, IdentifierDataType.IpAddress, language); - foreach (var post in customer.ForumPosts) - { - _gdprTool.Value.AnonymizeData(post, x => x.IPAddress, IdentifierDataType.IpAddress, language); - } + foreach (var post in customer.ForumPosts) + { + _gdprTool.Value.AnonymizeData(post, x => x.IPAddress, IdentifierDataType.IpAddress, language); + } - // Customer Content - foreach (var item in customer.CustomerContent) - { - _gdprTool.Value.AnonymizeData(item, x => x.IpAddress, IdentifierDataType.IpAddress, language); - } + // Customer Content + foreach (var item in customer.CustomerContent) + { + _gdprTool.Value.AnonymizeData(item, x => x.IpAddress, IdentifierDataType.IpAddress, language); + } - UpdateCustomer(customer); + UpdateCustomer(customer); } public virtual Customer GetCustomerById(int customerId) { if (customerId == 0) return null; - + // var customer = _customerRepository.GetById(customerId); - var customer = IncludeShoppingCart(_customerRepository.Table).SingleOrDefault(x => x.Id == customerId); + var customer = IncludeRoles(_customerRepository.Table).SingleOrDefault(x => x.Id == customerId); - return customer; + return customer; } - private IQueryable IncludeShoppingCart(IQueryable query) - { - return query - .Expand(x => x.ShoppingCartItems.Select(y => y.BundleItem)) - .Expand(x => x.ShoppingCartItems.Select(y => y.Product.AppliedDiscounts.Select(z => z.DiscountRequirements))); - } + private IQueryable IncludeRoles(IQueryable query) + { + return query + .Expand(x => x.CustomerRoleMappings.Select(y => y.CustomerRole)); + + /// The generated SQL is way too heavy (!). Discard later (??) + //return query + // .Expand(x => x.ShoppingCartItems.Select(y => y.BundleItem)) + // .Expand(x => x.ShoppingCartItems.Select(y => y.Product.AppliedDiscounts.Select(z => z.RuleSets))); + } public virtual IList GetCustomersByIds(int[] customerIds) { @@ -321,22 +327,22 @@ where customerIds.Contains(c.Id) var customers = query.ToList(); - // sort by passed identifier sequence - return customers.OrderBySequence(customerIds).ToList(); + // sort by passed identifier sequence + return customers.OrderBySequence(customerIds).ToList(); } - public virtual IList GetSystemAccountCustomers() - { - return _customerRepository.Table.Where(x => x.IsSystemAccount).ToList(); - } + public virtual IList GetSystemAccountCustomers() + { + return _customerRepository.Table.Where(x => x.IsSystemAccount).ToList(); + } public virtual Customer GetCustomerByGuid(Guid customerGuid) { if (customerGuid == Guid.Empty) return null; - var query = from c in IncludeShoppingCart(_customerRepository.Table) - where c.CustomerGuid == customerGuid + var query = from c in IncludeRoles(_customerRepository.Table) + where c.CustomerGuid == customerGuid orderby c.Id select c; @@ -349,8 +355,8 @@ public virtual Customer GetCustomerByEmail(string email) if (string.IsNullOrWhiteSpace(email)) return null; - var query = from c in IncludeShoppingCart(_customerRepository.Table) - orderby c.Id + var query = from c in IncludeRoles(_customerRepository.Table) + orderby c.Id where c.Email == email select c; @@ -363,13 +369,13 @@ public virtual Customer GetCustomerBySystemName(string systemName) if (string.IsNullOrWhiteSpace(systemName)) return null; - var query = from c in _customerRepository.Table - orderby c.Id - where c.SystemName == systemName - select c; + var query = from c in _customerRepository.Table + orderby c.Id + where c.SystemName == systemName + select c; - var customer = query.FirstOrDefault(); - return customer; + var customer = query.FirstOrDefault(); + return customer; } public virtual Customer GetCustomerByUsername(string username) @@ -377,8 +383,8 @@ public virtual Customer GetCustomerByUsername(string username) if (string.IsNullOrWhiteSpace(username)) return null; - var query = from c in IncludeShoppingCart(_customerRepository.Table) - orderby c.Id + var query = from c in IncludeRoles(_customerRepository.Table) + orderby c.Id where c.Username == username select c; @@ -388,284 +394,271 @@ orderby c.Id public virtual Customer InsertGuestCustomer(Guid? customerGuid = null) { - var customer = new Customer + var customer = new Customer { CustomerGuid = customerGuid ?? Guid.NewGuid(), Active = true, CreatedOnUtc = DateTime.UtcNow, LastActivityDateUtc = DateTime.UtcNow, }; - + // Add to 'Guests' role var guestRole = GetCustomerRoleBySystemName(SystemCustomerRoleNames.Guests); if (guestRole == null) + { throw new SmartException("'Guests' role could not be loaded"); + } + + using (new DbContextScope(autoCommit: true)) + { + // Ensure that entities are saved to db in any case + customer.CustomerRoleMappings.Add(new CustomerRoleMapping { CustomerId = customer.Id, CustomerRoleId = guestRole.Id }); + _customerRepository.Insert(customer); + + var clientIdent = _services.WebHelper.GetClientIdent(); + if (clientIdent.HasValue()) + { + _genericAttributeService.SaveAttribute(customer, "ClientIdent", clientIdent); + } + } + + //Logger.DebugFormat("Guest account created for anonymous visitor. Id: {0}, ClientIdent: {1}", customer.CustomerGuid, clientIdent ?? "n/a"); + + return customer; + } + + public virtual Customer FindGuestCustomerByClientIdent(string clientIdent = null, int maxAgeSeconds = 60) + { + if (_httpContext.IsFakeContext() || _userAgent.IsBot || _userAgent.IsPdfConverter) + { + return null; + } + + using (_services.Chronometer.Step("FindGuestCustomerByClientIdent")) + { + clientIdent = clientIdent.NullEmpty() ?? _services.WebHelper.GetClientIdent(); + if (clientIdent.IsEmpty()) + { + return null; + } + + var dateFrom = DateTime.UtcNow.AddSeconds(maxAgeSeconds * -1); + + IQueryable query; + if (DataSettings.Current.IsSqlServer) + { + query = from a in _gaRepository.Table + 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.Table + 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.Contains(clientIdent) // SQLCE doesn't like ntext in WHERE clauses + select c; + } + + return query.FirstOrDefault(); + } + } + + public virtual void InsertCustomer(Customer customer) + { + Guard.NotNull(customer, nameof(customer)); + + /// Validate unique user. + if (customer.Email.HasValue() && GetCustomerByEmail(customer.Email) != null) + { + throw new SmartException(T("Account.Register.Errors.EmailAlreadyExists")); + } + + if (customer.Username.HasValue() && + _customerSettings.CustomerLoginType != CustomerLoginType.Email && + GetCustomerByUsername(customer.Username) != null) + { + throw new SmartException(T("Account.Register.Errors.UsernameAlreadyExists")); + } + + _customerRepository.Insert(customer); + } - using (new DbContextScope(autoCommit: true)) - { - // Ensure that entities are saved to db in any case - customer.CustomerRoles.Add(guestRole); - _customerRepository.Insert(customer); - - var clientIdent = _services.WebHelper.GetClientIdent(); - if (clientIdent.HasValue()) - { - _genericAttributeService.SaveAttribute(customer, "ClientIdent", clientIdent); - } - } - - //Logger.DebugFormat("Guest account created for anonymous visitor. Id: {0}, ClientIdent: {1}", customer.CustomerGuid, clientIdent ?? "n/a"); - - return customer; - } - - public virtual Customer FindGuestCustomerByClientIdent(string clientIdent = null, int maxAgeSeconds = 60) - { - if (_httpContext.IsFakeContext() || _userAgent.IsBot || _userAgent.IsPdfConverter) - { - return null; - } - - using (_services.Chronometer.Step("FindGuestCustomerByClientIdent")) - { - clientIdent = clientIdent.NullEmpty() ?? _services.WebHelper.GetClientIdent(); - if (clientIdent.IsEmpty()) - { - return null; - } - - var dateFrom = DateTime.UtcNow.AddSeconds(maxAgeSeconds * -1); - - IQueryable query; - if (DataSettings.Current.IsSqlServer) - { - 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 - from c in Customers.DefaultIfEmpty() - where c.LastActivityDateUtc >= dateFrom - && c.Username == null - && c.Email == null - && a.KeyGroup == "Customer" - && a.Key == "ClientIdent" - && a.Value.Contains(clientIdent) // SQLCE doesn't like ntext in WHERE clauses - select c; - } - - return query.FirstOrDefault(); - } - } - - public virtual void InsertCustomer(Customer customer) - { - Guard.NotNull(customer, nameof(customer)); - - _customerRepository.Insert(customer); - } - public virtual void UpdateCustomer(Customer customer) { - Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(customer, nameof(customer)); - _customerRepository.Update(customer); + _customerRepository.Update(customer); } - public virtual void ResetCheckoutData( - Customer customer, - int storeId, - bool clearCouponCodes = false, - bool clearCheckoutAttributes = false, - bool clearRewardPoints = false, - bool clearShippingMethod = true, + public virtual void ResetCheckoutData( + Customer customer, + int storeId, + bool clearCouponCodes = false, + bool clearCheckoutAttributes = false, + bool clearRewardPoints = false, + bool clearShippingMethod = true, bool clearPaymentMethod = true, - bool clearCreditBalance = false) + bool clearCreditBalance = false) { - Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(customer, nameof(customer)); - if (clearCouponCodes) + if (clearCouponCodes) { - _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.DiscountCouponCode, null); - _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.GiftCardCouponCodes, null); + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.DiscountCouponCode, null); + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.GiftCardCouponCodes, null); } if (clearCheckoutAttributes) { - _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.CheckoutAttributes, null); + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.CheckoutAttributes, null); } if (clearRewardPoints) { - _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.UseRewardPointsDuringCheckout, false, storeId); + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.UseRewardPointsDuringCheckout, false, storeId); } - if (clearCreditBalance) - { - _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.UseCreditBalanceDuringCheckout, decimal.Zero, storeId); - } + if (clearCreditBalance) + { + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.UseCreditBalanceDuringCheckout, decimal.Zero, storeId); + } if (clearShippingMethod) { - _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.SelectedShippingOption, null, storeId); - _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.OfferedShippingOptions, null, storeId); + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.SelectedShippingOption, null, storeId); + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.OfferedShippingOptions, null, storeId); } if (clearPaymentMethod) { - _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.SelectedPaymentMethod, null, storeId); + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.SelectedPaymentMethod, null, storeId); } UpdateCustomer(customer); } - - public virtual async Task DeleteGuestCustomersAsync( - DateTime? registrationFrom, - DateTime? registrationTo, - bool onlyWithoutShoppingCart, - int maxItemsToDelete = 5000) - { - var ctx = _customerRepository.Context; - - using (var scope = new DbContextScope(ctx: ctx, autoDetectChanges: false, proxyCreation: true, validateOnSave: false, forceNoTracking: true, autoCommit: false)) - { - var guestRole = GetCustomerRoleBySystemName(SystemCustomerRoleNames.Guests); - if (guestRole == null) - throw new SmartException("'Guests' role could not be loaded"); - - var query = _customerRepository.Table; - - if (registrationFrom.HasValue) - query = query.Where(c => registrationFrom.Value <= c.CreatedOnUtc); - if (registrationTo.HasValue) - query = query.Where(c => registrationTo.Value >= c.CreatedOnUtc); - - query = query.Where(c => c.CustomerRoles.Select(cr => cr.Id).Contains(guestRole.Id)); - - if (onlyWithoutShoppingCart) - query = query.Where(c => !c.ShoppingCartItems.Any()); - - // no orders - query = JoinWith(query, x => x.CustomerId); - - // no customer content - query = JoinWith(query, x => x.CustomerId); - - // no private messages (guests can only receive but not send messages) - query = JoinWith(query, x => x.ToCustomerId); - - // no forum posts - query = JoinWith(query, x => x.CustomerId); - - // no forum topics - query = JoinWith(query, x => x.CustomerId); - - // don't delete system accounts - query = query.Where(c => !c.IsSystemAccount); - - // only distinct items - query = from c in query - group c by c.Id - into cGroup - orderby cGroup.Key - select cGroup.FirstOrDefault(); - query = query.OrderBy(c => c.Id); - - var customers = await query.Take(() => maxItemsToDelete).ToListAsync(); - - int numberOfDeletedCustomers = 0; - foreach (var c in customers) - { - try - { - // delete attributes (using GenericAttributeService would incorporate caching... which is bad in long running processes) - var gaQuery = from ga in _gaRepository.Table - where ga.EntityId == c.Id && ga.KeyGroup == "Customer" - select ga; - var attributes = await gaQuery.ToListAsync(); - - _gaRepository.DeleteRange(attributes); - - // delete customer - _customerRepository.Delete(c); - numberOfDeletedCustomers++; - - if (numberOfDeletedCustomers % 1000 == 0) - { - // save changes all 1000th item - try - { - await ctx.SaveChangesAsync(); - } - catch (Exception ex) - { - Debug.WriteLine(ex); - } - } - } - catch (Exception ex) - { - Debug.WriteLine(ex); - } - } - - // save the rest - await ctx.SaveChangesAsync(); - - return numberOfDeletedCustomers; - } - } - - private IQueryable JoinWith(IQueryable query, Expression> customerIdSelector) where T : BaseEntity - { - var inner = _customerRepository.Context.Set().AsNoTracking(); - - /* - * Lamda join created with LinqPad. ORIGINAL: - from c in customers - join inner in ctx.Set().AsNoTracking() on c.Id equals inner.CustomerId into c_inner - from inner in c_inner.DefaultIfEmpty() - where !c_inner.Any() - select c; - */ - query = query - .GroupJoin( - inner, - c => c.Id, - customerIdSelector, - (c, i) => new { Customer = c, Inner = i }) - .SelectMany( - x => x.Inner.DefaultIfEmpty(), - (a, b) => new { a, b } - ) - .Where(x => !(x.a.Inner.Any())) - .Select(x => x.a.Customer); - - return query; - } + + public virtual int DeleteGuestCustomers( + DateTime? registrationFrom, + DateTime? registrationTo, + bool onlyWithoutShoppingCart) + { + var genericAttributesSql = @" +DELETE TOP(50000) [g] + FROM [dbo].[GenericAttribute] AS [g] + LEFT OUTER JOIN [dbo].[Customer] AS [c] ON c.Id = g.EntityId + LEFT OUTER JOIN [dbo].[Order] AS [o] ON c.Id = o.CustomerId + LEFT OUTER JOIN [dbo].[CustomerContent] AS [cc] ON c.Id = cc.CustomerId + LEFT OUTER JOIN [dbo].[Forums_PrivateMessage] AS [pm] ON c.Id = pm.ToCustomerId + LEFT OUTER JOIN [dbo].[Forums_Post] AS [fp] ON c.Id = fp.CustomerId + LEFT OUTER JOIN [dbo].[Forums_Topic] AS [ft] ON c.Id = ft.CustomerId + WHERE g.KeyGroup = 'Customer' AND c.Username IS Null AND c.Email IS NULL AND c.IsSystemAccount = 0{0} + AND (NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[Order] AS [o1] WHERE c.Id = o1.CustomerId )) + AND (NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[CustomerContent] AS [cc1] WHERE c.Id = cc1.CustomerId )) + AND (NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[Forums_PrivateMessage] AS [pm1] WHERE c.Id = pm1.ToCustomerId )) + AND (NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[Forums_Post] AS [fp1] WHERE c.Id = fp1.CustomerId )) + AND (NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[Forums_Topic] AS [ft1] WHERE c.Id = ft1.CustomerId )) +"; + + var guestCustomersSql = @" +DELETE TOP(20000) [c] + FROM [dbo].[Customer] AS [c] + LEFT OUTER JOIN [dbo].[Order] AS [o] ON c.Id = o.CustomerId + LEFT OUTER JOIN [dbo].[CustomerContent] AS [cc] ON c.Id = cc.CustomerId + LEFT OUTER JOIN [dbo].[Forums_PrivateMessage] AS [pm] ON c.Id = pm.ToCustomerId + LEFT OUTER JOIN [dbo].[Forums_Post] AS [fp] ON c.Id = fp.CustomerId + LEFT OUTER JOIN [dbo].[Forums_Topic] AS [ft] ON c.Id = ft.CustomerId + WHERE c.Username IS Null AND c.Email IS NULL AND c.IsSystemAccount = 0{0} + AND (NOT EXISTS (SELECT 1 AS x FROM [dbo].[Order] AS [o1] WHERE c.Id = o1.CustomerId )) + AND (NOT EXISTS (SELECT 1 AS x FROM [dbo].[CustomerContent] AS [cc1] WHERE c.Id = cc1.CustomerId )) + AND (NOT EXISTS (SELECT 1 AS x FROM [dbo].[Forums_PrivateMessage] AS [pm1] WHERE c.Id = pm1.ToCustomerId )) + AND (NOT EXISTS (SELECT 1 AS x FROM [dbo].[Forums_Post] AS [fp1] WHERE c.Id = fp1.CustomerId )) + AND (NOT EXISTS (SELECT 1 AS x FROM [dbo].[Forums_Topic] AS [ft1] WHERE c.Id = ft1.CustomerId )) +"; + + var ctx = _customerRepository.Context; + var paramClauses = new StringBuilder(); + var parameters = new List(); + var numberOfDeletedCustomers = 0; + var numberOfDeletedAttributes = 0; + var pIndex = 0; + + if (registrationFrom.HasValue) + { + paramClauses.AppendFormat(" AND @p{0} <= c.CreatedOnUtc", pIndex++); + parameters.Add(registrationFrom.Value); + } + if (registrationTo.HasValue) + { + paramClauses.AppendFormat(" AND @p{0} >= c.CreatedOnUtc", pIndex++); + parameters.Add(registrationTo.Value); + } + if (onlyWithoutShoppingCart) + { + paramClauses.Append(" AND (NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[ShoppingCartItem] AS [sci] WHERE c.Id = sci.CustomerId ))"); + } + + genericAttributesSql = genericAttributesSql.FormatInvariant(paramClauses.ToString()); + guestCustomersSql = guestCustomersSql.FormatInvariant(paramClauses.ToString()); + + + // Delete generic attributes. + while (true) + { + var numDeleted = ctx.ExecuteSqlCommand(genericAttributesSql, false, null, parameters.ToArray()); + if (numDeleted <= 0) + { + break; + } + + numberOfDeletedAttributes += numDeleted; + } + + // Delete guest customers. + while (true) + { + var numDeleted = ctx.ExecuteSqlCommand(guestCustomersSql, false, null, parameters.ToArray()); + if (numDeleted <= 0) + { + break; + } + + numberOfDeletedCustomers += numDeleted; + } + + Debug.WriteLine("Deleted {0} guest customers including {1} generic attributes.", numberOfDeletedCustomers, numberOfDeletedAttributes); + + return numberOfDeletedCustomers; + } #endregion - + #region Customer roles public virtual void DeleteCustomerRole(CustomerRole role) { - Guard.NotNull(role, nameof(role)); + Guard.NotNull(role, nameof(role)); - if (role.IsSystemRole) + if (role.IsSystemRole) throw new SmartException("System role could not be deleted"); + var roleId = role.Id; + _customerRoleRepository.Delete(role); + + _services.Cache.RemoveByPattern(PermissionService.PERMISSION_TREE_KEY.FormatInvariant(roleId)); } public virtual CustomerRole GetCustomerRoleById(int roleId) @@ -681,75 +674,159 @@ public virtual CustomerRole GetCustomerRoleBySystemName(string systemName) if (String.IsNullOrWhiteSpace(systemName)) return null; - var query = from cr in _customerRoleRepository.Table - orderby cr.Id - where cr.SystemName == systemName - select cr; + var query = from cr in _customerRoleRepository.Table + orderby cr.Id + where cr.SystemName == systemName + select cr; - var customerRole = query.FirstOrDefaultCached(); - return customerRole; - } + var customerRole = query.FirstOrDefaultCached(); + return customerRole; + } - public virtual IList GetAllCustomerRoles(bool showHidden = false) + public virtual IPagedList GetAllCustomerRoles(bool showHidden = false, int pageIndex = 0, int pageSize = int.MaxValue) { - var query = from cr in _customerRoleRepository.Table - orderby cr.Name - where (showHidden || cr.Active) - select cr; + var query = from cr in _customerRoleRepository.Table + orderby cr.Name + where (showHidden || cr.Active) + select cr; + + return new PagedList(query, pageIndex, pageSize); + } - var customerRoles = query.ToListCached(); - return customerRoles; - } - public virtual void InsertCustomerRole(CustomerRole role) { - Guard.NotNull(role, nameof(role)); + Guard.NotNull(role, nameof(role)); - _customerRoleRepository.Insert(role); + _customerRoleRepository.Insert(role); } public virtual void UpdateCustomerRole(CustomerRole role) { - Guard.NotNull(role, nameof(role)); + Guard.NotNull(role, nameof(role)); + + _customerRoleRepository.Update(role); + + _services.Cache.RemoveByPattern(PermissionService.PERMISSION_TREE_KEY.FormatInvariant(role.Id)); + } + + #endregion + + #region Customer role mappings + + public virtual CustomerRoleMapping GetCustomerRoleMappingById(int mappingId) + { + if (mappingId == 0) + { + return null; + } - _customerRoleRepository.Update(role); + return _customerRoleMappingRepository.GetById(mappingId); + } + + public virtual IPagedList GetCustomerRoleMappings( + int[] customerIds, + int[] customerRoleIds, + bool? isSystemMapping, + int pageIndex, + int pageSize, + bool withCustomers = true) + { + var query = _customerRoleMappingRepository.TableUntracked; + + if (withCustomers) + { + query = query.Include(x => x.Customer); + } + + if (customerIds?.Any() ?? false) + { + query = query.Where(x => customerIds.Contains(x.CustomerId)); + } + if (customerRoleIds?.Any() ?? false) + { + query = query.Where(x => customerRoleIds.Contains(x.CustomerRoleId)); + } + if (isSystemMapping.HasValue) + { + query = query.Where(x => x.IsSystemMapping == isSystemMapping.Value); + } + + query = query.Where(x => !x.Customer.Deleted); + + if (withCustomers) + { + query = query + .OrderBy(x => x.IsSystemMapping) + .ThenByDescending(x => x.Customer.CreatedOnUtc); + } + else + { + query = query.OrderBy(x => x.IsSystemMapping); + } + + var mappings = new PagedList(query, pageIndex, pageSize); + return mappings; + } + + public virtual void InsertCustomerRoleMapping(CustomerRoleMapping mapping) + { + Guard.NotNull(mapping, nameof(mapping)); + + _customerRoleMappingRepository.Insert(mapping); + } + + public virtual void UpdateCustomerRoleMapping(CustomerRoleMapping mapping) + { + Guard.NotNull(mapping, nameof(mapping)); + + _customerRoleMappingRepository.Update(mapping); + } + + public virtual void DeleteCustomerRoleMapping(CustomerRoleMapping mapping) + { + if (mapping != null) + { + _customerRoleMappingRepository.Delete(mapping); + } } #endregion - #region Reward points + #region Reward points - public virtual void RewardPointsForProductReview(Customer customer, Product product, bool add) - { - if (_rewardPointsSettings.Enabled && _rewardPointsSettings.PointsForProductReview > 0) - { - string message = T(add ? "RewardPoints.Message.EarnedForProductReview" : "RewardPoints.Message.ReducedForProductReview", product.GetLocalized(x => x.Name)); + public virtual void RewardPointsForProductReview(Customer customer, Product product, bool add) + { + var rpSettings = _rewardPointsSettings.Value; - customer.AddRewardPointsHistoryEntry(_rewardPointsSettings.PointsForProductReview * (add ? 1 : -1), message); + if (rpSettings.Enabled && rpSettings.PointsForProductReview > 0) + { + string message = T(add ? "RewardPoints.Message.EarnedForProductReview" : "RewardPoints.Message.ReducedForProductReview", product.GetLocalized(x => x.Name)); - UpdateCustomer(customer); - } - } + customer.AddRewardPointsHistoryEntry(rpSettings.PointsForProductReview * (add ? 1 : -1), message); - public virtual Multimap GetRewardPointsHistoriesByCustomerIds(int[] customerIds) - { - Guard.NotNull(customerIds, nameof(customerIds)); + UpdateCustomer(customer); + } + } + + public virtual Multimap GetRewardPointsHistoriesByCustomerIds(int[] customerIds) + { + Guard.NotNull(customerIds, nameof(customerIds)); - var query = - from x in _rewardPointsHistoryRepository.TableUntracked - where customerIds.Contains(x.CustomerId) - select x; + var query = + from x in _rewardPointsHistoryRepository.TableUntracked + where customerIds.Contains(x.CustomerId) + select x; - var map = query - .OrderBy(x => x.CustomerId) - .ThenByDescending(x => x.CreatedOnUtc) - .ThenByDescending(x => x.Id) - .ToList() - .ToMultimap(x => x.CustomerId, x => x); + var map = query + .OrderBy(x => x.CustomerId) + .ThenByDescending(x => x.CreatedOnUtc) + .ThenByDescending(x => x.Id) + .ToList() + .ToMultimap(x => x.CustomerId, x => x); - return map; - } + return map; + } - #endregion Reward points - } + #endregion + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Customers/DeleteGuestsTask.cs b/src/Libraries/SmartStore.Services/Customers/DeleteGuestsTask.cs deleted file mode 100644 index 1238009e0d..0000000000 --- a/src/Libraries/SmartStore.Services/Customers/DeleteGuestsTask.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Threading.Tasks; -using SmartStore.Services.Tasks; - -namespace SmartStore.Services.Customers -{ - /// - /// Represents a task for deleting guest customers - /// - public partial class DeleteGuestsTask : AsyncTask - { - private readonly ICustomerService _customerService; - - public DeleteGuestsTask(ICustomerService customerService) - { - _customerService = customerService; - } - - public override async Task ExecuteAsync(TaskExecutionContext ctx) - { - //60*24 = 1 day - var olderThanMinutes = 1440; // TODO: move to settings - await _customerService.DeleteGuestCustomersAsync(null, DateTime.UtcNow.AddMinutes(-olderThanMinutes), true); - } - } -} diff --git a/src/Libraries/SmartStore.Services/Customers/Events/CustomerAnonymizedEvent.cs b/src/Libraries/SmartStore.Services/Customers/Events/CustomerAnonymizedEvent.cs index b25350d730..a33f396714 100644 --- a/src/Libraries/SmartStore.Services/Customers/Events/CustomerAnonymizedEvent.cs +++ b/src/Libraries/SmartStore.Services/Customers/Events/CustomerAnonymizedEvent.cs @@ -7,34 +7,34 @@ namespace SmartStore.Services.Customers { - /// - /// TODO - /// - public class CustomerAnonymizedEvent - { - private readonly IGdprTool _gdprTool; + /// + /// TODO + /// + public class CustomerAnonymizedEvent + { + private readonly IGdprTool _gdprTool; - public CustomerAnonymizedEvent(Customer customer, IGdprTool gdprTool) - { - Guard.NotNull(customer, nameof(customer)); + public CustomerAnonymizedEvent(Customer customer, IGdprTool gdprTool) + { + Guard.NotNull(customer, nameof(customer)); - Customer = customer; - _gdprTool = gdprTool; - } + Customer = customer; + _gdprTool = gdprTool; + } - public Customer Customer { get; private set; } + public Customer Customer { get; private set; } - /// - /// TODO - /// - /// - /// - /// - /// - public void AnonymizeData(TEntity entity, Expression> expression, IdentifierDataType type, Language language = null) - where TEntity : BaseEntity - { - _gdprTool.AnonymizeData(entity, expression, type, language); - } - } + /// + /// TODO + /// + /// + /// + /// + /// + public void AnonymizeData(TEntity entity, Expression> expression, IdentifierDataType type, Language language = null) + where TEntity : BaseEntity + { + _gdprTool.AnonymizeData(entity, expression, type, language); + } + } } diff --git a/src/Libraries/SmartStore.Services/Customers/Events/CustomerExportedEvent.cs b/src/Libraries/SmartStore.Services/Customers/Events/CustomerExportedEvent.cs index 0c143c8b2f..3c6ada03a5 100644 --- a/src/Libraries/SmartStore.Services/Customers/Events/CustomerExportedEvent.cs +++ b/src/Libraries/SmartStore.Services/Customers/Events/CustomerExportedEvent.cs @@ -4,18 +4,18 @@ namespace SmartStore.Services.Customers { - public class CustomerExportedEvent - { - public CustomerExportedEvent(Customer customer, IDictionary result) - { - Guard.NotNull(customer, nameof(customer)); - Guard.NotNull(result, nameof(result)); + public class CustomerExportedEvent + { + public CustomerExportedEvent(Customer customer, IDictionary result) + { + Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(result, nameof(result)); - Customer = customer; - Result = result; - } + Customer = customer; + Result = result; + } - public Customer Customer { get; private set; } - public IDictionary Result { get; private set; } - } + public Customer Customer { get; private set; } + public IDictionary Result { get; private set; } + } } diff --git a/src/Libraries/SmartStore.Services/Customers/Events/CustomerLogedInEvent.cs b/src/Libraries/SmartStore.Services/Customers/Events/CustomerLogedInEvent.cs index b224692e64..d69f9cd24c 100644 --- a/src/Libraries/SmartStore.Services/Customers/Events/CustomerLogedInEvent.cs +++ b/src/Libraries/SmartStore.Services/Customers/Events/CustomerLogedInEvent.cs @@ -3,15 +3,15 @@ namespace SmartStore.Services.Customers { - /// - /// An event message, which will be published after customer has logged in - /// + /// + /// An event message, which will be published after customer has logged in + /// public class CustomerLogedInEvent - { + { public Customer Customer - { - get; - set; - } - } + { + get; + set; + } + } } diff --git a/src/Libraries/SmartStore.Services/Customers/Events/CustomerRegisteredEvent.cs b/src/Libraries/SmartStore.Services/Customers/Events/CustomerRegisteredEvent.cs index 920c08fca6..66b6225adf 100644 --- a/src/Libraries/SmartStore.Services/Customers/Events/CustomerRegisteredEvent.cs +++ b/src/Libraries/SmartStore.Services/Customers/Events/CustomerRegisteredEvent.cs @@ -3,15 +3,15 @@ namespace SmartStore.Services.Customers { - /// - /// An event message, which will be published after customer has registered - /// + /// + /// An event message, which will be published after customer has registered + /// public class CustomerRegisteredEvent - { + { public Customer Customer - { - get; - set; - } - } + { + get; + set; + } + } } diff --git a/src/Libraries/SmartStore.Services/Customers/GdprTool.cs b/src/Libraries/SmartStore.Services/Customers/GdprTool.cs index 1c2cc5d174..45fecf45a6 100644 --- a/src/Libraries/SmartStore.Services/Customers/GdprTool.cs +++ b/src/Libraries/SmartStore.Services/Customers/GdprTool.cs @@ -29,128 +29,128 @@ namespace SmartStore.Services.Customers { - public enum IdentifierDataType - { - Text, - LongText, - Name, - UserName, - EmailAddress, - Url, - IpAddress, - PhoneNumber, - Address, - PostalCode, - DateTime - } - - public partial class GdprTool : IGdprTool - { - private readonly IMessageModelProvider _messageModelProvider; - private readonly IGenericAttributeService _genericAttributeService; - private readonly IShoppingCartService _shoppingCartService; - private readonly IForumService _forumService; - private readonly IBackInStockSubscriptionService _backInStockSubscriptionService; - private readonly ILanguageService _languageService; - private readonly ICommonServices _services; - - public static DateTime MinDate = new DateTime(1900, 1, 1); - - const string AnonymousEmail = "anonymous@example.com"; - - public GdprTool( - IMessageModelProvider messageModelProvider, - IGenericAttributeService genericAttributeService, - IShoppingCartService shoppingCartService, - IForumService forumService, - IBackInStockSubscriptionService backInStockSubscriptionService, - ILanguageService languageService, - ICommonServices services) - { - _messageModelProvider = messageModelProvider; - _genericAttributeService = genericAttributeService; - _shoppingCartService = shoppingCartService; - _forumService = forumService; - _backInStockSubscriptionService = backInStockSubscriptionService; - _languageService = languageService; - _services = services; - - T = NullLocalizer.InstanceEx; - Logger = NullLogger.Instance; - } - - public LocalizerEx T { get; set; } - - public ILogger Logger { get; set; } - - public virtual IDictionary ExportCustomer(Customer customer) - { - Guard.NotNull(customer, nameof(customer)); - - var ignoreMemberNames = new string[] - { - "WishlistUrl", "EditUrl", "PasswordRecoveryURL", - "BillingAddress.NameLine", "BillingAddress.StreetLine", "BillingAddress.CityLine", "BillingAddress.CountryLine", - "ShippingAddress.NameLine", "ShippingAddress.StreetLine", "ShippingAddress.CityLine", "ShippingAddress.CountryLine" - }; - - var model = _messageModelProvider.CreateModelPart(customer, true, ignoreMemberNames) as IDictionary; - - if (model != null) - { - // Roles - model["CustomerRoles"] = customer.CustomerRoles.Select(x => x.Name).ToArray(); - - // Generic attributes - var attributes = _genericAttributeService.GetAttributesForEntity(customer.Id, "Customer"); - if (attributes.Any()) - { - model["Attributes"] = _messageModelProvider.CreateModelPart(attributes, true); - } - - // Order history - var orders = customer.Orders.Where(x => !x.Deleted); - if (orders.Any()) - { - ignoreMemberNames = new string[] - { - "Disclaimer", "ConditionsOfUse", "Url", "CheckoutAttributes", - "Items.DownloadUrl", - "Items.Product.Description", "Items.Product.Url", "Items.Product.Thumbnail", "Items.Product.ThumbnailLg", - "Items.BundleItems.Product.Description", "Items.BundleItems.Product.Url", "Items.BundleItems.Product.Thumbnail", "Items.BundleItems.Product.ThumbnailLg", - "Billing.NameLine", "Billing.StreetLine", "Billing.CityLine", "Billing.CountryLine", - "Shipping.NameLine", "Shipping.StreetLine", "Shipping.CityLine", "Shipping.CountryLine" - }; - model["Orders"] = orders.Select(x => _messageModelProvider.CreateModelPart(x, true, ignoreMemberNames)).ToList(); - } - - // Return Request - var returnRequests = customer.ReturnRequests; - if (returnRequests.Any()) - { - model["ReturnRequests"] = returnRequests.Select(x => _messageModelProvider.CreateModelPart(x, true, "Url")).ToList(); - } - - // Wallet - var walletHistory = customer.WalletHistory; - if (walletHistory.Any()) - { - model["WalletHistory"] = walletHistory.Select(x => _messageModelProvider.CreateModelPart(x, true, "WalletUrl")).ToList(); - } - - // Forum topics - var forumTopics = customer.ForumTopics; - if (forumTopics.Any()) - { - model["ForumTopics"] = forumTopics.Select(x => _messageModelProvider.CreateModelPart(x, true, "Url")).ToList(); - } - - // Forum posts - var forumPosts = customer.ForumPosts; - if (forumPosts.Any()) - { - model["ForumPosts"] = forumPosts.Select(x => _messageModelProvider.CreateModelPart(x, true)).ToList(); - } + public enum IdentifierDataType + { + Text, + LongText, + Name, + UserName, + EmailAddress, + Url, + IpAddress, + PhoneNumber, + Address, + PostalCode, + DateTime + } + + public partial class GdprTool : IGdprTool + { + private readonly IMessageModelProvider _messageModelProvider; + private readonly IGenericAttributeService _genericAttributeService; + private readonly IShoppingCartService _shoppingCartService; + private readonly IForumService _forumService; + private readonly IBackInStockSubscriptionService _backInStockSubscriptionService; + private readonly ILanguageService _languageService; + private readonly ICommonServices _services; + + public static DateTime MinDate = new DateTime(1900, 1, 1); + + const string AnonymousEmail = "anonymous@example.com"; + + public GdprTool( + IMessageModelProvider messageModelProvider, + IGenericAttributeService genericAttributeService, + IShoppingCartService shoppingCartService, + IForumService forumService, + IBackInStockSubscriptionService backInStockSubscriptionService, + ILanguageService languageService, + ICommonServices services) + { + _messageModelProvider = messageModelProvider; + _genericAttributeService = genericAttributeService; + _shoppingCartService = shoppingCartService; + _forumService = forumService; + _backInStockSubscriptionService = backInStockSubscriptionService; + _languageService = languageService; + _services = services; + + T = NullLocalizer.InstanceEx; + Logger = NullLogger.Instance; + } + + public LocalizerEx T { get; set; } + + public ILogger Logger { get; set; } + + public virtual IDictionary ExportCustomer(Customer customer) + { + Guard.NotNull(customer, nameof(customer)); + + var ignoreMemberNames = new string[] + { + "WishlistUrl", "EditUrl", "PasswordRecoveryURL", + "BillingAddress.NameLine", "BillingAddress.StreetLine", "BillingAddress.CityLine", "BillingAddress.CountryLine", + "ShippingAddress.NameLine", "ShippingAddress.StreetLine", "ShippingAddress.CityLine", "ShippingAddress.CountryLine" + }; + + var model = _messageModelProvider.CreateModelPart(customer, true, ignoreMemberNames) as IDictionary; + + if (model != null) + { + // Roles + model["CustomerRoles"] = customer.CustomerRoleMappings.Select(x => x.CustomerRole.Name).ToArray(); + + // Generic attributes + var attributes = _genericAttributeService.GetAttributesForEntity(customer.Id, "Customer"); + if (attributes.Any()) + { + model["Attributes"] = _messageModelProvider.CreateModelPart(attributes, true); + } + + // Order history + var orders = customer.Orders.Where(x => !x.Deleted); + if (orders.Any()) + { + ignoreMemberNames = new string[] + { + "Disclaimer", "ConditionsOfUse", "Url", "CheckoutAttributes", + "Items.DownloadUrl", + "Items.Product.Description", "Items.Product.Url", "Items.Product.Thumbnail", "Items.Product.ThumbnailLg", + "Items.BundleItems.Product.Description", "Items.BundleItems.Product.Url", "Items.BundleItems.Product.Thumbnail", "Items.BundleItems.Product.ThumbnailLg", + "Billing.NameLine", "Billing.StreetLine", "Billing.CityLine", "Billing.CountryLine", + "Shipping.NameLine", "Shipping.StreetLine", "Shipping.CityLine", "Shipping.CountryLine" + }; + model["Orders"] = orders.Select(x => _messageModelProvider.CreateModelPart(x, true, ignoreMemberNames)).ToList(); + } + + // Return Request + var returnRequests = customer.ReturnRequests; + if (returnRequests.Any()) + { + model["ReturnRequests"] = returnRequests.Select(x => _messageModelProvider.CreateModelPart(x, true, "Url")).ToList(); + } + + // Wallet + var walletHistory = customer.WalletHistory; + if (walletHistory.Any()) + { + model["WalletHistory"] = walletHistory.Select(x => _messageModelProvider.CreateModelPart(x, true, "WalletUrl")).ToList(); + } + + // Forum topics + var forumTopics = customer.ForumTopics; + if (forumTopics.Any()) + { + model["ForumTopics"] = forumTopics.Select(x => _messageModelProvider.CreateModelPart(x, true, "Url")).ToList(); + } + + // Forum posts + var forumPosts = customer.ForumPosts; + if (forumPosts.Any()) + { + model["ForumPosts"] = forumPosts.Select(x => _messageModelProvider.CreateModelPart(x, true)).ToList(); + } // Forum post votes var forumPostVotes = customer.CustomerContent.OfType(); @@ -162,307 +162,322 @@ public virtual IDictionary ExportCustomer(Customer customer) // Product reviews var productReviews = customer.CustomerContent.OfType(); - if (productReviews.Any()) - { - model["ProductReviews"] = productReviews.Select(x => _messageModelProvider.CreateModelPart(x, true)).ToList(); - } - - // News comments - var newsComments = customer.CustomerContent.OfType(); - if (newsComments.Any()) - { - model["NewsComments"] = newsComments.Select(x => _messageModelProvider.CreateModelPart(x, true)).ToList(); - } - - // Blog comments - var blogComments = customer.CustomerContent.OfType(); - if (blogComments.Any()) - { - model["BlogComments"] = blogComments.Select(x => _messageModelProvider.CreateModelPart(x, true)).ToList(); - } - - // Product review helpfulness - var helpfulness = customer.CustomerContent.OfType(); - if (helpfulness.Any()) - { - ignoreMemberNames = new string[] { "CustomerId", "UpdatedOn" }; - model["ProductReviewHelpfulness"] = helpfulness.Select(x => _messageModelProvider.CreateModelPart(x, true, ignoreMemberNames)).ToList(); - } - - // Poll voting - var pollVotings = customer.CustomerContent.OfType(); - if (pollVotings.Any()) - { - ignoreMemberNames = new string[] { "CustomerId", "UpdatedOn" }; - model["PollVotings"] = pollVotings.Select(x => _messageModelProvider.CreateModelPart(x, true, ignoreMemberNames)).ToList(); - } - - // Forum subscriptions - var forumSubscriptions = _forumService.GetAllSubscriptions(customer.Id, 0, 0, 0, int.MaxValue); - if (forumSubscriptions.Any()) - { - model["ForumSubscriptions"] = forumSubscriptions.Select(x => _messageModelProvider.CreateModelPart(x, true, "CustomerId")).ToList(); - } - - // BackInStock subscriptions - var backInStockSubscriptions = _backInStockSubscriptionService.GetAllSubscriptionsByCustomerId(customer.Id, 0, 0, int.MaxValue); - if (backInStockSubscriptions.Any()) - { - model["BackInStockSubscriptions"] = backInStockSubscriptions.Select(x => _messageModelProvider.CreateModelPart(x, true, "CustomerId")).ToList(); - } - - // INFO: we're not going to export: - // - Private messages - // - Activity log - // It doesn't feel right and GDPR rules are not very clear about this. Let's wait and see :-) - - // Publish event to give plugin devs a chance to attach external data. - _services.EventPublisher.Publish(new CustomerExportedEvent(customer, model)); - } - - return model; - } - - public virtual void AnonymizeCustomer(Customer customer, bool pseudomyzeContent) - { - Guard.NotNull(customer, nameof(customer)); - - var language = customer.GetLanguage(); - var customerName = customer.GetFullName() ?? customer.Username ?? customer.FindEmail(); - - using (var scope = new DbContextScope(_services.DbContext, autoCommit: false)) - { - // Set to deleted - customer.Deleted = true; - - // Unassign roles - customer.CustomerRoles.Clear(); - customer.CustomerRoles.Add(_services.Resolve().GetCustomerRoleBySystemName(SystemCustomerRoleNames.Guests)); - - // Delete shopping cart & wishlist (TBD: (mc) Really?!?) - _shoppingCartService.DeleteExpiredShoppingCartItems(DateTime.UtcNow, customer.Id); - - // Delete forum subscriptions - var forumSubscriptions = _forumService.GetAllSubscriptions(customer.Id, 0, 0, 0, int.MaxValue); - foreach (var forumSub in forumSubscriptions) - { - _forumService.DeleteSubscription(forumSub); - } - - // Delete BackInStock subscriptions - var backInStockSubscriptions = _backInStockSubscriptionService.GetAllSubscriptionsByCustomerId(customer.Id, 0, 0, int.MaxValue); - foreach (var stockSub in backInStockSubscriptions) - { - _backInStockSubscriptionService.DeleteSubscription(stockSub); - } - - // Generic attributes - var attributes = _genericAttributeService.GetAttributesForEntity(customer.Id, "Customer"); - foreach (var attr in attributes) - { - // we don't need to mask generic attrs, we just delete them. - _genericAttributeService.DeleteAttribute(attr); - } - - // Customer Data - AnonymizeData(customer, x => x.Username, IdentifierDataType.UserName, language); - AnonymizeData(customer, x => x.Email, IdentifierDataType.EmailAddress, language); - AnonymizeData(customer, x => x.LastIpAddress, IdentifierDataType.IpAddress, language); - if (pseudomyzeContent) - { - AnonymizeData(customer, x => x.AdminComment, IdentifierDataType.LongText, language); - AnonymizeData(customer, x => x.LastLoginDateUtc, IdentifierDataType.DateTime, language); - AnonymizeData(customer, x => x.LastActivityDateUtc, IdentifierDataType.DateTime, language); - } - - // Addresses - foreach (var address in customer.Addresses) - { - AnonymizeAddress(address, language); - } - - // Private messages - if (pseudomyzeContent) - { - var privateMessages = _forumService.GetAllPrivateMessages(0, customer.Id, 0, null, null, null, 0, int.MaxValue); - foreach (var msg in privateMessages) - { - AnonymizeData(msg, x => x.Subject, IdentifierDataType.Text, language); - AnonymizeData(msg, x => x.Text, IdentifierDataType.LongText, language); - } - } - - // Forum topics - if (pseudomyzeContent) - { - foreach (var topic in customer.ForumTopics) - { - AnonymizeData(topic, x => x.Subject, IdentifierDataType.Text, language); - } - } - - // Forum posts - foreach (var post in customer.ForumPosts) - { - AnonymizeData(post, x => x.IPAddress, IdentifierDataType.IpAddress, language); - if (pseudomyzeContent) - { - AnonymizeData(post, x => x.Text, IdentifierDataType.LongText, language); - } - } - - // Customer Content - foreach (var item in customer.CustomerContent) - { - AnonymizeData(item, x => x.IpAddress, IdentifierDataType.IpAddress, language); - - if (pseudomyzeContent) - { - switch (item) - { - case ProductReview c: - AnonymizeData(c, x => x.ReviewText, IdentifierDataType.LongText, language); - AnonymizeData(c, x => x.Title, IdentifierDataType.Text, language); - break; - case NewsComment c: - AnonymizeData(c, x => x.CommentText, IdentifierDataType.LongText, language); - AnonymizeData(c, x => x.CommentTitle, IdentifierDataType.Text, language); - break; - case BlogComment c: - AnonymizeData(c, x => x.CommentText, IdentifierDataType.LongText, language); - break; - } - } - } - - //// Anonymize Order IPs - //// TBD: Don't! Doesn't feel right because of fraud detection etc. - //foreach (var order in customer.Orders) - //{ - // AnonymizeData(order, x => x.CustomerIp, IdentifierDataType.IpAddress, language); - //} - - // SAVE!!! - //_services.DbContext.DetachAll(); // TEST - scope.Commit(); - - // Log - Logger.Info(T("Gdpr.Anonymize.Success", language.Id, customerName)); - } - } - - private void AnonymizeAddress(Address address, Language language) - { - AnonymizeData(address, x => x.Address1, IdentifierDataType.Address, language); - AnonymizeData(address, x => x.Address2, IdentifierDataType.Address, language); - AnonymizeData(address, x => x.City, IdentifierDataType.Address, language); - AnonymizeData(address, x => x.Company, IdentifierDataType.Address, language); - AnonymizeData(address, x => x.Email, IdentifierDataType.EmailAddress, language); - AnonymizeData(address, x => x.FaxNumber, IdentifierDataType.PhoneNumber, language); - AnonymizeData(address, x => x.FirstName, IdentifierDataType.Name, language); - AnonymizeData(address, x => x.LastName, IdentifierDataType.Name, language); - AnonymizeData(address, x => x.PhoneNumber, IdentifierDataType.PhoneNumber, language); - AnonymizeData(address, x => x.ZipPostalCode, IdentifierDataType.PostalCode, language); - } - - public virtual void AnonymizeData(TEntity entity, Expression> expression, IdentifierDataType type, Language language = null) - where TEntity : BaseEntity - { - Guard.NotNull(entity, nameof(entity)); - Guard.NotNull(expression, nameof(expression)); - - var originalValue = expression.Compile().Invoke(entity); - object maskedValue = null; - - if (originalValue is DateTime d) - { - maskedValue = MinDate; - } - else if (originalValue is string s) - { - if (s.IsEmpty()) - { - return; - } - - language = language ?? (entity as Customer)?.GetLanguage(); - - switch (type) - { - case IdentifierDataType.Address: - case IdentifierDataType.Name: - case IdentifierDataType.Text: - maskedValue = T("Gdpr.DeletedText", language.Id).Text; - break; - case IdentifierDataType.LongText: - maskedValue = T("Gdpr.DeletedLongText", language.Id).Text; - break; - case IdentifierDataType.EmailAddress: - //maskedValue = s.Hash(Encoding.ASCII, true) + "@anony.mous"; - maskedValue = HashCodeCombiner.Start() - .Add(entity.GetHashCode()) - .Add(s) - .CombinedHashString + "@anony.mous"; - break; - case IdentifierDataType.Url: - maskedValue = "https://anony.mous"; - break; - case IdentifierDataType.IpAddress: - maskedValue = AnonymizeIpAddress(s); - break; - case IdentifierDataType.UserName: - maskedValue = T("Gdpr.Anonymous", language.Id).Text.ToLower(); - break; - case IdentifierDataType.PhoneNumber: - maskedValue = "555-00000"; - break; - case IdentifierDataType.PostalCode: - maskedValue = "00000"; - break; - case IdentifierDataType.DateTime: - maskedValue = MinDate.ToString(CultureInfo.InvariantCulture); - break; - } - } - - if (maskedValue != null) - { - var pi = expression.ExtractPropertyInfo(); - pi.SetValue(entity, maskedValue); - } - } - - /// - /// Returns an anonymized IPv4 or IPv6 address. - /// - /// The IPv4 or IPv6 address to be anonymized. - /// The anonymized IP address. - protected virtual string AnonymizeIpAddress(string ipAddress) - { - try - { - var ip = IPAddress.Parse(ipAddress); - - switch (ip.AddressFamily) - { - case AddressFamily.InterNetwork: - break; - case AddressFamily.InterNetworkV6: - // Map to IPv4 first - ip = ip.MapToIPv4(); - break; - default: - // we only support IPv4 and IPv6 - return "0.0.0.0"; - } - - // Keep the first 3 bytes and append ".0" - return string.Join(".", ip.GetAddressBytes().Take(3)) + ".0"; - } - catch - { - return null; - } - } - } + if (productReviews.Any()) + { + model["ProductReviews"] = productReviews.Select(x => _messageModelProvider.CreateModelPart(x, true)).ToList(); + } + + // News comments + var newsComments = customer.CustomerContent.OfType(); + if (newsComments.Any()) + { + model["NewsComments"] = newsComments.Select(x => _messageModelProvider.CreateModelPart(x, true)).ToList(); + } + + // Blog comments + var blogComments = customer.CustomerContent.OfType(); + if (blogComments.Any()) + { + model["BlogComments"] = blogComments.Select(x => _messageModelProvider.CreateModelPart(x, true)).ToList(); + } + + // Product review helpfulness + var helpfulness = customer.CustomerContent.OfType(); + if (helpfulness.Any()) + { + ignoreMemberNames = new string[] { "CustomerId", "UpdatedOn" }; + model["ProductReviewHelpfulness"] = helpfulness.Select(x => _messageModelProvider.CreateModelPart(x, true, ignoreMemberNames)).ToList(); + } + + // Poll voting + var pollVotings = customer.CustomerContent.OfType(); + if (pollVotings.Any()) + { + ignoreMemberNames = new string[] { "CustomerId", "UpdatedOn" }; + model["PollVotings"] = pollVotings.Select(x => _messageModelProvider.CreateModelPart(x, true, ignoreMemberNames)).ToList(); + } + + // Forum subscriptions + var forumSubscriptions = _forumService.GetAllSubscriptions(customer.Id, 0, 0, 0, int.MaxValue); + if (forumSubscriptions.Any()) + { + model["ForumSubscriptions"] = forumSubscriptions.Select(x => _messageModelProvider.CreateModelPart(x, true, "CustomerId")).ToList(); + } + + // BackInStock subscriptions + var backInStockSubscriptions = _backInStockSubscriptionService.GetAllSubscriptionsByCustomerId(customer.Id, 0, 0, int.MaxValue); + if (backInStockSubscriptions.Any()) + { + model["BackInStockSubscriptions"] = backInStockSubscriptions.Select(x => _messageModelProvider.CreateModelPart(x, true, "CustomerId")).ToList(); + } + + // INFO: we're not going to export: + // - Private messages + // - Activity log + // It doesn't feel right and GDPR rules are not very clear about this. Let's wait and see :-) + + // Publish event to give plugin devs a chance to attach external data. + _services.EventPublisher.Publish(new CustomerExportedEvent(customer, model)); + } + + return model; + } + + public virtual void AnonymizeCustomer(Customer customer, bool pseudomyzeContent) + { + Guard.NotNull(customer, nameof(customer)); + + var language = customer.GetLanguage(); + var customerName = customer.GetFullName() ?? customer.Username ?? customer.FindEmail(); + + using (var scope = new DbContextScope(_services.DbContext, autoCommit: false)) + { + // Set to deleted + customer.Deleted = true; + + // Unassign roles + var customerService = _services.Resolve(); + var roleMappings = customer.CustomerRoleMappings.ToList(); + var guestRole = customerService.GetCustomerRoleBySystemName(SystemCustomerRoleNames.Guests); + var insertGuestMapping = !roleMappings.Any(x => x.CustomerRoleId == guestRole.Id); + + roleMappings + .Where(x => x.CustomerRoleId != guestRole.Id) + .Each(x => customerService.DeleteCustomerRoleMapping(x)); + + if (insertGuestMapping) + { + customerService.InsertCustomerRoleMapping(new CustomerRoleMapping { CustomerId = customer.Id, CustomerRoleId = guestRole.Id }); + } + + // Delete shopping cart & wishlist (TBD: (mc) Really?!?) + _shoppingCartService.DeleteExpiredShoppingCartItems(DateTime.UtcNow, customer.Id); + + // Delete forum subscriptions + var forumSubscriptions = _forumService.GetAllSubscriptions(customer.Id, 0, 0, 0, int.MaxValue); + foreach (var forumSub in forumSubscriptions) + { + _forumService.DeleteSubscription(forumSub); + } + + // Delete BackInStock subscriptions + var backInStockSubscriptions = _backInStockSubscriptionService.GetAllSubscriptionsByCustomerId(customer.Id, 0, 0, int.MaxValue); + foreach (var stockSub in backInStockSubscriptions) + { + _backInStockSubscriptionService.DeleteSubscription(stockSub); + } + + // Generic attributes + var attributes = _genericAttributeService.GetAttributesForEntity(customer.Id, "Customer"); + foreach (var attr in attributes) + { + // we don't need to mask generic attrs, we just delete them. + _genericAttributeService.DeleteAttribute(attr); + } + + // Customer Data + AnonymizeData(customer, x => x.Username, IdentifierDataType.UserName, language); + AnonymizeData(customer, x => x.Email, IdentifierDataType.EmailAddress, language); + AnonymizeData(customer, x => x.LastIpAddress, IdentifierDataType.IpAddress, language); + AnonymizeData(customer, x => x.FirstName, IdentifierDataType.Name, language); + AnonymizeData(customer, x => x.LastName, IdentifierDataType.Name, language); + AnonymizeData(customer, x => x.BirthDate, IdentifierDataType.DateTime, language); + + if (pseudomyzeContent) + { + AnonymizeData(customer, x => x.AdminComment, IdentifierDataType.LongText, language); + AnonymizeData(customer, x => x.LastLoginDateUtc, IdentifierDataType.DateTime, language); + AnonymizeData(customer, x => x.LastActivityDateUtc, IdentifierDataType.DateTime, language); + } + + // Addresses + foreach (var address in customer.Addresses) + { + AnonymizeAddress(address, language); + } + + // Private messages + if (pseudomyzeContent) + { + var privateMessages = _forumService.GetAllPrivateMessages(0, customer.Id, 0, null, null, null, 0, int.MaxValue); + foreach (var msg in privateMessages) + { + AnonymizeData(msg, x => x.Subject, IdentifierDataType.Text, language); + AnonymizeData(msg, x => x.Text, IdentifierDataType.LongText, language); + } + } + + // Forum topics + if (pseudomyzeContent) + { + foreach (var topic in customer.ForumTopics) + { + AnonymizeData(topic, x => x.Subject, IdentifierDataType.Text, language); + } + } + + // Forum posts + foreach (var post in customer.ForumPosts) + { + AnonymizeData(post, x => x.IPAddress, IdentifierDataType.IpAddress, language); + if (pseudomyzeContent) + { + AnonymizeData(post, x => x.Text, IdentifierDataType.LongText, language); + } + } + + // Customer Content + foreach (var item in customer.CustomerContent) + { + AnonymizeData(item, x => x.IpAddress, IdentifierDataType.IpAddress, language); + + if (pseudomyzeContent) + { + switch (item) + { + case ProductReview c: + AnonymizeData(c, x => x.ReviewText, IdentifierDataType.LongText, language); + AnonymizeData(c, x => x.Title, IdentifierDataType.Text, language); + break; + case NewsComment c: + AnonymizeData(c, x => x.CommentText, IdentifierDataType.LongText, language); + AnonymizeData(c, x => x.CommentTitle, IdentifierDataType.Text, language); + break; + case BlogComment c: + AnonymizeData(c, x => x.CommentText, IdentifierDataType.LongText, language); + break; + } + } + } + + //// Anonymize Order IPs + //// TBD: Don't! Doesn't feel right because of fraud detection etc. + //foreach (var order in customer.Orders) + //{ + // AnonymizeData(order, x => x.CustomerIp, IdentifierDataType.IpAddress, language); + //} + + // SAVE!!! + //_services.DbContext.DetachAll(); // TEST + scope.Commit(); + + // Log + Logger.Info(T("Gdpr.Anonymize.Success", language.Id, customerName)); + } + } + + private void AnonymizeAddress(Address address, Language language) + { + AnonymizeData(address, x => x.Address1, IdentifierDataType.Address, language); + AnonymizeData(address, x => x.Address2, IdentifierDataType.Address, language); + AnonymizeData(address, x => x.City, IdentifierDataType.Address, language); + AnonymizeData(address, x => x.Company, IdentifierDataType.Address, language); + AnonymizeData(address, x => x.Email, IdentifierDataType.EmailAddress, language); + AnonymizeData(address, x => x.FaxNumber, IdentifierDataType.PhoneNumber, language); + AnonymizeData(address, x => x.FirstName, IdentifierDataType.Name, language); + AnonymizeData(address, x => x.LastName, IdentifierDataType.Name, language); + AnonymizeData(address, x => x.PhoneNumber, IdentifierDataType.PhoneNumber, language); + AnonymizeData(address, x => x.ZipPostalCode, IdentifierDataType.PostalCode, language); + } + + public virtual void AnonymizeData(TEntity entity, Expression> expression, IdentifierDataType type, Language language = null) + where TEntity : BaseEntity + { + Guard.NotNull(entity, nameof(entity)); + Guard.NotNull(expression, nameof(expression)); + + var originalValue = expression.Compile().Invoke(entity); + object maskedValue = null; + + if (originalValue is DateTime d) + { + maskedValue = MinDate; + } + else if (originalValue is string s) + { + if (s.IsEmpty()) + { + return; + } + + language = language ?? (entity as Customer)?.GetLanguage(); + + switch (type) + { + case IdentifierDataType.Address: + case IdentifierDataType.Name: + case IdentifierDataType.Text: + maskedValue = T("Gdpr.DeletedText", language.Id).Text; + break; + case IdentifierDataType.LongText: + maskedValue = T("Gdpr.DeletedLongText", language.Id).Text; + break; + case IdentifierDataType.EmailAddress: + //maskedValue = s.Hash(Encoding.ASCII, true) + "@anony.mous"; + maskedValue = HashCodeCombiner.Start() + .Add(entity.GetHashCode()) + .Add(s) + .CombinedHashString + "@anony.mous"; + break; + case IdentifierDataType.Url: + maskedValue = "https://anony.mous"; + break; + case IdentifierDataType.IpAddress: + maskedValue = AnonymizeIpAddress(s); + break; + case IdentifierDataType.UserName: + maskedValue = T("Gdpr.Anonymous", language.Id).Text.ToLower(); + break; + case IdentifierDataType.PhoneNumber: + maskedValue = "555-00000"; + break; + case IdentifierDataType.PostalCode: + maskedValue = "00000"; + break; + case IdentifierDataType.DateTime: + maskedValue = MinDate.ToString(CultureInfo.InvariantCulture); + break; + } + } + + if (maskedValue != null) + { + var pi = expression.ExtractPropertyInfo(); + pi.SetValue(entity, maskedValue); + } + } + + /// + /// Returns an anonymized IPv4 or IPv6 address. + /// + /// The IPv4 or IPv6 address to be anonymized. + /// The anonymized IP address. + protected virtual string AnonymizeIpAddress(string ipAddress) + { + try + { + var ip = IPAddress.Parse(ipAddress); + + switch (ip.AddressFamily) + { + case AddressFamily.InterNetwork: + break; + case AddressFamily.InterNetworkV6: + // Map to IPv4 first + ip = ip.MapToIPv4(); + break; + default: + // we only support IPv4 and IPv6 + return "0.0.0.0"; + } + + // Keep the first 3 bytes and append ".0" + return string.Join(".", ip.GetAddressBytes().Take(3)) + ".0"; + } + catch + { + return null; + } + } + } } diff --git a/src/Libraries/SmartStore.Services/Customers/ICookieManager.cs b/src/Libraries/SmartStore.Services/Customers/ICookieManager.cs new file mode 100644 index 0000000000..7a246b92a4 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Customers/ICookieManager.cs @@ -0,0 +1,38 @@ +using SmartStore.Core.Plugins; +using System.Collections.Generic; +using System.Web; +using System.Web.Mvc; + +namespace SmartStore.Services.Customers +{ + public interface ICookieManager + { + /// + /// Gets CookieInfos of all plugins which are publishing cookies to be display in CookieManager dialog. + /// + /// List of CookieInfos. + List GetAllCookieInfos(bool addSettingCookies = false); + + /// + /// Gets a value which specifies whether it is allowed to set a cookie of a certain type. + /// + /// Type of the cookie. + /// Value which indicates whether cookies of a certain type are allowed to be set. + bool IsCookieAllowed(ControllerContext context, CookieType cookieType); + + /// + /// Gets the data of the cookie. + /// + /// + /// Cookie which contains data of user selection. + ConsentCookie GetCookieData(ControllerContext context); + + /// + /// Sets the cookie. + /// + /// Cookie will be set to this response. + /// Defines whether analytical cookies are allowed to be set. + /// Defines whether third party cookies are allowed to be set. + void SetConsentCookie(HttpResponseBase response, bool allowAnalytics = false, bool allowThirdParty = false); + } +} diff --git a/src/Libraries/SmartStore.Services/Customers/ICustomerContentService.cs b/src/Libraries/SmartStore.Services/Customers/ICustomerContentService.cs index 42484688bc..002e76a6ca 100644 --- a/src/Libraries/SmartStore.Services/Customers/ICustomerContentService.cs +++ b/src/Libraries/SmartStore.Services/Customers/ICustomerContentService.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using SmartStore.Core; using SmartStore.Core.Domain.Customers; namespace SmartStore.Services.Customers @@ -20,9 +20,15 @@ public partial interface ICustomerContentService /// /// Customer identifier; 0 to load all records /// A value indicating whether to content is approved; null to load all records + /// Page index. + /// Page size. /// Customer content - IList GetAllCustomerContent(int customerId, bool? approved); - + IPagedList GetAllCustomerContent( + int customerId, + bool? approved, + int pageIndex = 0, + int pageSize = int.MaxValue); + /// /// Gets all customer content /// @@ -31,9 +37,16 @@ public partial interface ICustomerContentService /// A value indicating whether to content is approved; null to load all records /// Item creation from; null to load all records /// Item item creation to; null to load all records + /// Page index. + /// Page size. /// Customer content - IList GetAllCustomerContent(int customerId, bool? approved, - DateTime? fromUtc = null, DateTime? toUtc = null) where T : CustomerContent; + IPagedList GetAllCustomerContent( + int customerId, + bool? approved, + DateTime? fromUtc = null, + DateTime? toUtc = null, + int pageIndex = 0, + int pageSize = int.MaxValue) where T : CustomerContent; /// /// Gets a customer content diff --git a/src/Libraries/SmartStore.Services/Customers/ICustomerReportService.cs b/src/Libraries/SmartStore.Services/Customers/ICustomerReportService.cs index 045582170a..6095b259dc 100644 --- a/src/Libraries/SmartStore.Services/Customers/ICustomerReportService.cs +++ b/src/Libraries/SmartStore.Services/Customers/ICustomerReportService.cs @@ -1,35 +1,61 @@ using System; using System.Collections.Generic; +using SmartStore.Core; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Payments; using SmartStore.Core.Domain.Shipping; +using SmartStore.Services.Orders; +using static SmartStore.Services.Customers.CustomerReportService; namespace SmartStore.Services.Customers { /// - /// Customer report service interface + /// Customer report service interface. /// public partial interface ICustomerReportService { /// - /// Get best customers + /// Get best customers. /// - /// Order start time; null to load all - /// Order end time; null to load all - /// Order status; null to load all records - /// Order payment status; null to load all records - /// Order shippment status; null to load all records - /// 1 - order by order total, 2 - order by number of orders + /// Order start time. null to load all customers. + /// Order end time. null to load all customers. + /// Order status. null to load all customers. + /// Order payment status. null to load all customers. + /// Order shippment status. null to load all customers. + /// Sorting of report items. + /// Page index. + /// Page size. /// Report - IList GetBestCustomersReport(DateTime? startTime, - DateTime? endTime, OrderStatus? os, PaymentStatus? ps, ShippingStatus? ss, int orderBy); - + IPagedList GetTopCustomersReport( + DateTime? startTime, + DateTime? endTime, + OrderStatus? orderStatus, + PaymentStatus? paymentStatus, + ShippingStatus? shippingStatus, + ReportSorting sorting, + int pageIndex = 0, + int pageSize = int.MaxValue); + /// /// Gets a report of customers registered in the last days /// /// Customers registered in the last days /// Number of registered customers int GetRegisteredCustomersReport(int days); + + /// + /// Get customers registrations sorted by date + /// + /// Customer registrations + List GetRegisteredCustomersDate(); + + /// + /// Get customer registration count + /// + /// Start time UTC + /// End time UTC + /// Number of registrations + int GetCustomerRegistrations(DateTime? startTimeUtc, DateTime? endTimeUtc); } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Customers/ICustomerService.cs b/src/Libraries/SmartStore.Services/Customers/ICustomerService.cs index 5736bf5fa0..91b0079fc3 100644 --- a/src/Libraries/SmartStore.Services/Customers/ICustomerService.cs +++ b/src/Libraries/SmartStore.Services/Customers/ICustomerService.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; -using System.Threading.Tasks; using SmartStore.Collections; using SmartStore.Core; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Customers; -using SmartStore.Core.Domain.Orders; namespace SmartStore.Services.Customers { @@ -14,37 +12,37 @@ namespace SmartStore.Services.Customers /// public partial interface ICustomerService { - #region Customers - - /// - /// Finds customer records matching all criteria specified by - /// - /// The filter query - /// Customer collection - IPagedList SearchCustomers(CustomerSearchQuery q); - - /// - /// Gets all customers by customer format (including deleted ones) - /// - /// Password format - /// Customers - IPagedList GetAllCustomersByPasswordFormat(PasswordFormat passwordFormat); - - /// - /// Gets online customers - /// - /// Customer last activity date (from) - /// A list of customer role identifiers to filter by (at least one match); pass null or empty list in order to load all customers; - /// Page index - /// Page size - /// Customer collection - IPagedList GetOnlineCustomers(DateTime lastActivityFromUtc, int[] customerRoleIds, int pageIndex, int pageSize); - - /// - /// Delete a customer - /// - /// Customer - void DeleteCustomer(Customer customer); + #region Customers + + /// + /// Finds customer records matching all criteria specified by + /// + /// The filter query + /// Customer collection + IPagedList SearchCustomers(CustomerSearchQuery q); + + /// + /// Gets all customers by customer format (including deleted ones) + /// + /// Password format + /// Customers + IPagedList GetAllCustomersByPasswordFormat(PasswordFormat passwordFormat); + + /// + /// Gets online customers + /// + /// Customer last activity date (from) + /// A list of customer role identifiers to filter by (at least one match); pass null or empty list in order to load all customers; + /// Page index + /// Page size + /// Customer collection + IPagedList GetOnlineCustomers(DateTime lastActivityFromUtc, int[] customerRoleIds, int pageIndex, int pageSize); + + /// + /// Delete a customer + /// + /// Customer + void DeleteCustomer(Customer customer); /// /// Gets a customer @@ -60,18 +58,18 @@ public partial interface ICustomerService /// Customers IList GetCustomersByIds(int[] customerIds); - /// - /// Get system account customers - /// - /// System account customers - IList GetSystemAccountCustomers(); + /// + /// Get system account customers + /// + /// System account customers + IList GetSystemAccountCustomers(); - /// - /// Gets a customer by GUID - /// - /// Customer GUID - /// A customer - Customer GetCustomerByGuid(Guid customerGuid); + /// + /// Gets a customer by GUID + /// + /// Customer GUID + /// A customer + Customer GetCustomerByGuid(Guid customerGuid); /// /// Get customer by email @@ -79,7 +77,7 @@ public partial interface ICustomerService /// Email /// Customer Customer GetCustomerByEmail(string email); - + /// /// Get customer by system name /// @@ -94,29 +92,29 @@ public partial interface ICustomerService /// Customer Customer GetCustomerByUsername(string username); - /// - /// Insert a guest customer - /// - /// The customer GUID. Pass null to create a random one. - /// Customer - Customer InsertGuestCustomer(Guid? customerGuid = null); - - /// - /// Tries to find a guest/anonymous customer record by client ident. This method should be called when an - /// anonymous visitor rejects cookies and therefore cannot be identified automatically. - /// - /// - /// The client ident string, which is a hashed combination of client IP address and user agent. - /// Call to obtain an ident string, or pass null to let this method obtain it automatically. - /// The max age of the newly created guest customer record. The shorter, the better (default is 1 min.) - /// The identified customer or null - Customer FindGuestCustomerByClientIdent(string clientIdent = null, int maxAgeSeconds = 60); - - /// - /// Insert a customer - /// - /// Customer - void InsertCustomer(Customer customer); + /// + /// Insert a guest customer + /// + /// The customer GUID. Pass null to create a random one. + /// Customer + Customer InsertGuestCustomer(Guid? customerGuid = null); + + /// + /// Tries to find a guest/anonymous customer record by client ident. This method should be called when an + /// anonymous visitor rejects cookies and therefore cannot be identified automatically. + /// + /// + /// The client ident string, which is a hashed combination of client IP address and user agent. + /// Call to obtain an ident string, or pass null to let this method obtain it automatically. + /// The max age of the newly created guest customer record. The shorter, the better (default is 1 min.) + /// The identified customer or null + Customer FindGuestCustomerByClientIdent(string clientIdent = null, int maxAgeSeconds = 60); + + /// + /// Insert a customer + /// + /// Customer + void InsertCustomer(Customer customer); /// /// Updates the customer @@ -124,31 +122,34 @@ public partial interface ICustomerService /// Customer void UpdateCustomer(Customer customer); - /// - /// Reset data required for checkout - /// - /// Customer - /// Store identifier - /// A value indicating whether to clear coupon code - /// A value indicating whether to clear selected checkout attributes - /// A value indicating whether to clear "Use reward points" flag - /// A value indicating whether to clear selected shipping method - /// A value indicating whether to clear selected payment method - /// A value indicating whether to clear credit balance. - void ResetCheckoutData(Customer customer, int storeId, + /// + /// Reset data required for checkout + /// + /// Customer + /// Store identifier + /// A value indicating whether to clear coupon code + /// A value indicating whether to clear selected checkout attributes + /// A value indicating whether to clear "Use reward points" flag + /// A value indicating whether to clear selected shipping method + /// A value indicating whether to clear selected payment method + /// A value indicating whether to clear credit balance. + void ResetCheckoutData(Customer customer, int storeId, bool clearCouponCodes = false, bool clearCheckoutAttributes = false, bool clearRewardPoints = false, bool clearShippingMethod = true, bool clearPaymentMethod = true, - bool clearCreditBalance = false); + bool clearCreditBalance = false); - /// - /// Delete guest customer records - /// - /// Customer registration from; null to load all customers - /// Customer registration to; null to load all customers - /// A value indicating whether to delete customers only without shopping cart - /// Number of deleted customers - Task DeleteGuestCustomersAsync(DateTime? registrationFrom, DateTime? registrationTo, bool onlyWithoutShoppingCart, int maxItemsToDelete = 5000); + /// + /// Delete guest customer records including generic attributes. + /// + /// Customer registration from. null to ignore. + /// Customer registration to. null to ignore. + /// A value indicating whether to delete only customers without shopping cart. + /// Number of deleted guest customers. + int DeleteGuestCustomers( + DateTime? registrationFrom, + DateTime? registrationTo, + bool onlyWithoutShoppingCart); #endregion @@ -177,9 +178,11 @@ void ResetCheckoutData(Customer customer, int storeId, /// /// Gets all customer roles /// + /// Page index. + /// Page size. /// A value indicating whether to show hidden records /// Customer role collection - IList GetAllCustomerRoles(bool showHidden = false); + IPagedList GetAllCustomerRoles(bool showHidden = false, int pageIndex = 0, int pageSize = int.MaxValue); /// /// Inserts a customer role @@ -195,23 +198,70 @@ void ResetCheckoutData(Customer customer, int storeId, #endregion - #region Reward points - - /// - /// Add or remove reward points for a product review - /// - /// The customer - /// The product - /// Whether to add or remove points - void RewardPointsForProductReview(Customer customer, Product product, bool add); - - /// - /// Gets reward points histories - /// - /// Customer identifiers - /// Reward points histories - Multimap GetRewardPointsHistoriesByCustomerIds(int[] customerIds); - - #endregion Reward points - } + #region Customer role mappings + + /// + /// Gets a customer role mapping by identifier. + /// + /// Mapping identifier. + /// Customer role mapping. + CustomerRoleMapping GetCustomerRoleMappingById(int mappingId); + + /// + /// Gets customer role mappings. + /// + /// Customer identifiers to be filtered by. + /// Customer role identifiers to be filtered by. + /// Whether to filter by system or user mappings. + /// Page index. + /// Page size. + /// Whether to include customers through navigation property. + /// Customer role mappings. + IPagedList GetCustomerRoleMappings( + int[] customerIds, + int[] customerRoleIds, + bool? isSystemMapping, + int pageIndex, + int pageSize, + bool withCustomers = true); + + /// + /// Inserts a customer role mapping. + /// + /// Customer role mapping. + void InsertCustomerRoleMapping(CustomerRoleMapping mapping); + + /// + /// Updates a customer role mapping. + /// + /// Customer role mapping. + void UpdateCustomerRoleMapping(CustomerRoleMapping mapping); + + /// + /// Deletes a customer role mapping. + /// + /// Customer role mapping. + void DeleteCustomerRoleMapping(CustomerRoleMapping mapping); + + #endregion + + #region Reward points + + /// + /// Add or remove reward points for a product review + /// + /// The customer + /// The product + /// Whether to add or remove points + void RewardPointsForProductReview(Customer customer, Product product, bool add); + + /// + /// Gets reward points histories + /// + /// Customer identifiers + /// Reward points histories + Multimap GetRewardPointsHistoriesByCustomerIds(int[] customerIds); + + #endregion + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Customers/IGdprTool.cs b/src/Libraries/SmartStore.Services/Customers/IGdprTool.cs index febf53638e..ca85e4f96c 100644 --- a/src/Libraries/SmartStore.Services/Customers/IGdprTool.cs +++ b/src/Libraries/SmartStore.Services/Customers/IGdprTool.cs @@ -8,33 +8,33 @@ namespace SmartStore.Services.Customers { - public interface IGdprTool - { - /// - /// Exports all data stored for a customer into a dictionary. Exported data contains all - /// personal data, addresses, order history, reviews, forum posts, private messages etc. - /// - /// The customer to export data for. - /// The exported data - /// This method fulfills the "GDPR Data Portability" requirement. - IDictionary ExportCustomer(Customer customer); + public interface IGdprTool + { + /// + /// Exports all data stored for a customer into a dictionary. Exported data contains all + /// personal data, addresses, order history, reviews, forum posts, private messages etc. + /// + /// The customer to export data for. + /// The exported data + /// This method fulfills the "GDPR Data Portability" requirement. + IDictionary ExportCustomer(Customer customer); - /// - /// Anonymizes a customer's (personal) data. - /// - /// The customer to anonymize. - /// - /// This method fulfills the "GDPR Right to be forgotten" requirement. - void AnonymizeCustomer(Customer customer, bool pseudomyzeContent); + /// + /// Anonymizes a customer's (personal) data. + /// + /// The customer to anonymize. + /// + /// This method fulfills the "GDPR Right to be forgotten" requirement. + void AnonymizeCustomer(Customer customer, bool pseudomyzeContent); - /// - /// TODO - /// - /// - /// - /// - /// - /// - void AnonymizeData(TEntity entity, Expression> expression, IdentifierDataType type, Language language = null) where TEntity : BaseEntity; - } + /// + /// TODO + /// + /// + /// + /// + /// + /// + void AnonymizeData(TEntity entity, Expression> expression, IdentifierDataType type, Language language = null) where TEntity : BaseEntity; + } } diff --git a/src/Libraries/SmartStore.Services/Customers/Importer/CustomerImporter.cs b/src/Libraries/SmartStore.Services/Customers/Importer/CustomerImporter.cs index b0b18e0bf2..ce9703ed4d 100644 --- a/src/Libraries/SmartStore.Services/Customers/Importer/CustomerImporter.cs +++ b/src/Libraries/SmartStore.Services/Customers/Importer/CustomerImporter.cs @@ -9,6 +9,8 @@ using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Domain.Forums; using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.Tax; +using SmartStore.Core.Security; using SmartStore.Services.Affiliates; using SmartStore.Services.Common; using SmartStore.Services.DataExchange.Import; @@ -16,302 +18,314 @@ using SmartStore.Services.Directory; using SmartStore.Services.Helpers; using SmartStore.Services.Media; -using SmartStore.Services.Security; using SmartStore.Utilities; namespace SmartStore.Services.Customers.Importer { public class CustomerImporter : EntityImporterBase - { - private const string _attributeKeyGroup = "Customer"; - - private readonly IRepository _customerRepository; - private readonly IRepository _customerRoleRepository; - private readonly IRepository _pictureRepository; - private readonly ICommonServices _services; - private readonly IGenericAttributeService _genericAttributeService; - private readonly IPictureService _pictureService; - private readonly IAffiliateService _affiliateService; - private readonly ICountryService _countryService; - private readonly IStateProvinceService _stateProvinceService; - private readonly FileDownloadManager _fileDownloadManager; - private readonly CustomerSettings _customerSettings; - private readonly DateTimeSettings _dateTimeSettings; - private readonly ForumSettings _forumSettings; - - public CustomerImporter( - IRepository customerRepository, - IRepository customerRoleRepository, - IRepository pictureRepository, - ICommonServices services, - IGenericAttributeService genericAttributeService, - IPictureService pictureService, - IAffiliateService affiliateService, - ICountryService countryService, - IStateProvinceService stateProvinceService, - FileDownloadManager fileDownloadManager, - CustomerSettings customerSettings, - DateTimeSettings dateTimeSettings, - ForumSettings forumSettings) - { - _customerRepository = customerRepository; - _customerRoleRepository = customerRoleRepository; - _pictureRepository = pictureRepository; - _services = services; - _genericAttributeService = genericAttributeService; - _pictureService = pictureService; - _affiliateService = affiliateService; - _countryService = countryService; - _stateProvinceService = stateProvinceService; - _fileDownloadManager = fileDownloadManager; - _customerSettings = customerSettings; - _dateTimeSettings = dateTimeSettings; - _forumSettings = forumSettings; - } - - protected override void Import(ImportExecuteContext context) - { - var customer = _services.WorkContext.CurrentCustomer; - var allowManagingCustomerRoles = _services.Permissions.Authorize(StandardPermissionProvider.ManageCustomerRoles, customer); - - var allAffiliateIds = _affiliateService.GetAllAffiliates(true) - .Select(x => x.Id) - .ToList(); - - var allCountries = new Dictionary(); - foreach (var country in _countryService.GetAllCountries(true)) - { - if (!allCountries.ContainsKey(country.TwoLetterIsoCode)) - allCountries.Add(country.TwoLetterIsoCode, country.Id); - - if (!allCountries.ContainsKey(country.ThreeLetterIsoCode)) - allCountries.Add(country.ThreeLetterIsoCode, country.Id); - } - - var allStateProvinces = _stateProvinceService.GetAllStateProvinces(true) - .ToDictionarySafe(x => new Tuple(x.CountryId, x.Abbreviation), x => x.Id); - - var allCustomerNumbers = new HashSet( - _customerRepository.Table.Where(x => !String.IsNullOrEmpty(x.CustomerNumber)).Select(x => x.CustomerNumber), - StringComparer.OrdinalIgnoreCase); - - var allCustomerRoles = _customerRoleRepository.Table - .Where(x => !string.IsNullOrEmpty(x.SystemName)) - .ToDictionarySafe(x => x.SystemName, StringComparer.OrdinalIgnoreCase); - - using (var scope = new DbContextScope(ctx: _services.DbContext, hooksEnabled: false, autoDetectChanges: false, proxyCreation: false, validateOnSave: false, autoCommit: false)) - { - var segmenter = context.DataSegmenter; - - Initialize(context); - AddInfoForDeprecatedFields(context); - - while (context.Abort == DataExchangeAbortion.None && segmenter.ReadNextBatch()) - { - var batch = segmenter.GetCurrentBatch(); - - _customerRepository.Context.DetachAll(true); - - context.SetProgress(segmenter.CurrentSegmentFirstRowIndex - 1, segmenter.TotalRows); - - // =========================================================================== - // Process customers - // =========================================================================== - try - { - ProcessCustomers(context, batch, allAffiliateIds, allCustomerNumbers); - } - catch (Exception exception) - { - context.Result.AddError(exception, segmenter.CurrentSegment, "ProcessCustomers"); - } - - // reduce batch to saved (valid) records. - // No need to perform import operations on errored records. - batch = batch.Where(x => x.Entity != null && !x.IsTransient).ToArray(); - - // update result object - context.Result.NewRecords += batch.Count(x => x.IsNew && !x.IsTransient); - context.Result.ModifiedRecords += batch.Count(x => !x.IsNew && !x.IsTransient); - - // =========================================================================== - // Process customer roles - // =========================================================================== - try - { - _customerRepository.Context.AutoDetectChangesEnabled = true; - ProcessCustomerRoles(context, batch, allCustomerRoles); - } - catch (Exception exception) - { - context.Result.AddError(exception, segmenter.CurrentSegment, "ProcessCustomerRoles"); - } - finally - { - _customerRepository.Context.AutoDetectChangesEnabled = false; - } - - // =========================================================================== - // Process generic attributes - // =========================================================================== - try - { - ProcessGenericAttributes(context, batch, allCountries, allStateProvinces); - } - catch (Exception exception) - { - context.Result.AddError(exception, segmenter.CurrentSegment, "ProcessGenericAttributes"); - } - - // =========================================================================== - // Process avatars - // =========================================================================== - if (_customerSettings.AllowCustomersToUploadAvatars) - { - try - { - ProcessAvatars(context, batch); - } - catch (Exception exception) - { - context.Result.AddError(exception, segmenter.CurrentSegment, "ProcessAvatars"); - } - } - - // =========================================================================== - // Process addresses - // =========================================================================== - try - { - _services.DbContext.AutoDetectChangesEnabled = true; - ProcessAddresses(context, batch, allCountries, allStateProvinces); - } - catch (Exception exception) - { - context.Result.AddError(exception, segmenter.CurrentSegment, "ProcessAddresses"); - } - finally - { - _services.DbContext.AutoDetectChangesEnabled = false; - } + { + private const string _attributeKeyGroup = "Customer"; + + private readonly IRepository _customerRepository; + private readonly IRepository _customerRoleRepository; + private readonly ICommonServices _services; + private readonly ICustomerService _customerService; + private readonly IGenericAttributeService _genericAttributeService; + private readonly IMediaService _mediaService; + private readonly IAffiliateService _affiliateService; + private readonly ICountryService _countryService; + private readonly IStateProvinceService _stateProvinceService; + private readonly FileDownloadManager _fileDownloadManager; + private readonly CustomerSettings _customerSettings; + private readonly DateTimeSettings _dateTimeSettings; + private readonly ForumSettings _forumSettings; + private readonly TaxSettings _taxSettings; + private readonly PrivacySettings _privacySettings; + + public CustomerImporter( + IRepository customerRepository, + IRepository customerRoleRepository, + ICommonServices services, + ICustomerService customerService, + IGenericAttributeService genericAttributeService, + IMediaService mediaService, + IAffiliateService affiliateService, + ICountryService countryService, + IStateProvinceService stateProvinceService, + FileDownloadManager fileDownloadManager, + CustomerSettings customerSettings, + DateTimeSettings dateTimeSettings, + ForumSettings forumSettings, + TaxSettings taxSettings, + PrivacySettings privacySettings) + { + _customerRepository = customerRepository; + _customerRoleRepository = customerRoleRepository; + _mediaService = mediaService; + _services = services; + _customerService = customerService; + _genericAttributeService = genericAttributeService; + _affiliateService = affiliateService; + _countryService = countryService; + _stateProvinceService = stateProvinceService; + _fileDownloadManager = fileDownloadManager; + _customerSettings = customerSettings; + _dateTimeSettings = dateTimeSettings; + _forumSettings = forumSettings; + _taxSettings = taxSettings; + _privacySettings = privacySettings; + } + + protected override void Import(ImportExecuteContext context) + { + var customer = _services.WorkContext.CurrentCustomer; + var allowManagingCustomerRoles = _services.Permissions.Authorize(Permissions.Customer.EditRole, customer); + + var allAffiliateIds = _affiliateService.GetAllAffiliates(true) + .Select(x => x.Id) + .ToList(); + + var allCountries = new Dictionary(); + foreach (var country in _countryService.GetAllCountries(true)) + { + if (!allCountries.ContainsKey(country.TwoLetterIsoCode)) + allCountries.Add(country.TwoLetterIsoCode, country.Id); + + if (!allCountries.ContainsKey(country.ThreeLetterIsoCode)) + allCountries.Add(country.ThreeLetterIsoCode, country.Id); + } + + var allStateProvinces = _stateProvinceService.GetAllStateProvinces(true) + .ToDictionarySafe(x => new Tuple(x.CountryId, x.Abbreviation), x => x.Id); + + var allCustomerNumbers = new HashSet( + _customerRepository.Table.Where(x => !string.IsNullOrEmpty(x.CustomerNumber)).Select(x => x.CustomerNumber), + StringComparer.OrdinalIgnoreCase); + + using (var scope = new DbContextScope(ctx: _services.DbContext, hooksEnabled: false, autoDetectChanges: false, proxyCreation: false, validateOnSave: false, autoCommit: false)) + { + var segmenter = context.DataSegmenter; + + Initialize(context); + AddInfoForDeprecatedFields(context); + + while (context.Abort == DataExchangeAbortion.None && segmenter.ReadNextBatch()) + { + var batch = segmenter.GetCurrentBatch(); + + _customerRepository.Context.DetachAll(true); + + context.SetProgress(segmenter.CurrentSegmentFirstRowIndex - 1, segmenter.TotalRows); + + // =========================================================================== + // Process customers + // =========================================================================== + try + { + ProcessCustomers(context, batch, allAffiliateIds, allCustomerNumbers); + } + catch (Exception ex) + { + context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessCustomers"); + } + + // reduce batch to saved (valid) records. + // No need to perform import operations on errored records. + batch = batch.Where(x => x.Entity != null && !x.IsTransient).ToArray(); + + // update result object + context.Result.NewRecords += batch.Count(x => x.IsNew && !x.IsTransient); + context.Result.ModifiedRecords += batch.Count(x => !x.IsNew && !x.IsTransient); + + // =========================================================================== + // Process customer roles + // =========================================================================== + if (allowManagingCustomerRoles && context.DataSegmenter.HasColumn("CustomerRoleSystemNames")) + { + try + { + _customerRepository.AutoCommitEnabled = true; + ProcessCustomerRoles(context, batch); + } + catch (Exception ex) + { + context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessCustomerRoles"); + } + finally + { + _customerRepository.AutoCommitEnabled = false; + } + } + + // =========================================================================== + // Process generic attributes + // =========================================================================== + try + { + ProcessGenericAttributes(context, batch, allCountries, allStateProvinces); + } + catch (Exception ex) + { + context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessGenericAttributes"); + } + + // =========================================================================== + // Process avatars + // =========================================================================== + if (_customerSettings.AllowCustomersToUploadAvatars) + { + try + { + ProcessAvatars(context, batch); + } + catch (Exception ex) + { + context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessAvatars"); + } + } + + // =========================================================================== + // Process addresses + // =========================================================================== + try + { + _services.DbContext.AutoDetectChangesEnabled = true; + ProcessAddresses(context, batch, allCountries, allStateProvinces); + } + catch (Exception ex) + { + context.Result.AddError(ex, segmenter.CurrentSegment, "ProcessAddresses"); + } + finally + { + _services.DbContext.AutoDetectChangesEnabled = false; + } context.Services.EventPublisher.Publish(new ImportBatchExecutedEvent(context, batch)); } - } - } - - protected virtual int ProcessCustomers( - ImportExecuteContext context, - IEnumerable> batch, - List allAffiliateIds, - HashSet allCustomerNumbers) - { - _customerRepository.AutoCommitEnabled = true; - - var currentCustomer = _services.WorkContext.CurrentCustomer; - var customerQuery = _customerRepository.Table.Expand(x => x.Addresses); - var hasCustomerRoleSystemNames = context.DataSegmenter.HasColumn("CustomerRoleSystemNames"); - - foreach (var row in batch) - { - Customer customer = null; - var id = row.GetDataValue("Id"); - var email = row.GetDataValue("Email"); - - foreach (var keyName in context.KeyFieldNames) - { - switch (keyName) - { - case "Id": - if (id != 0) - { - customer = customerQuery.FirstOrDefault(x => x.Id == id); - } - break; - case "CustomerGuid": - var customerGuid = row.GetDataValue("CustomerGuid"); - if (customerGuid.HasValue()) - { - var guid = new Guid(customerGuid); - customer = customerQuery.FirstOrDefault(x => x.CustomerGuid == guid); - } - break; - case "Email": - if (email.HasValue()) - { - customer = customerQuery.FirstOrDefault(x => x.Email == email); - } - break; - case "Username": - var userName = row.GetDataValue("Username"); - if (userName.HasValue()) - { - customer = customerQuery.FirstOrDefault(x => x.Username == userName); - } - break; - } - - if (customer != null) - break; - } - - if (customer == null) - { - if (context.UpdateOnly) - { - ++context.Result.SkippedRecords; - continue; - } - - customer = new Customer - { - CustomerGuid = new Guid(), - AffiliateId = 0, - Active = true - }; - } - else - { - _customerRepository.Context.LoadCollection(customer, (Customer x) => x.CustomerRoles); - } - - var affiliateId = row.GetDataValue("AffiliateId"); - - row.Initialize(customer, email ?? id.ToString()); - - row.SetProperty(context.Result, (x) => x.CustomerGuid); - row.SetProperty(context.Result, (x) => x.Username); - row.SetProperty(context.Result, (x) => x.Email); - row.SetProperty(context.Result, (x) => x.FirstName); - row.SetProperty(context.Result, (x) => x.LastName); - - if (_customerSettings.TitleEnabled) - row.SetProperty(context.Result, (x) => x.Title); - - if (_customerSettings.CompanyEnabled) - row.SetProperty(context.Result, (x) => x.Company); - - if (_customerSettings.DateOfBirthEnabled) - row.SetProperty(context.Result, (x) => x.BirthDate); - - if (email.HasValue() && currentCustomer.Email.IsCaseInsensitiveEqual(email)) - { - context.Result.AddInfo("Security. Ignored password of current customer (who started this import).", row.GetRowInfo(), "Password"); - } - else - { - row.SetProperty(context.Result, (x) => x.Password); - row.SetProperty(context.Result, (x) => x.PasswordFormatId); - row.SetProperty(context.Result, (x) => x.PasswordSalt); - } - - row.SetProperty(context.Result, (x) => x.AdminComment); - row.SetProperty(context.Result, (x) => x.IsTaxExempt); - row.SetProperty(context.Result, (x) => x.Active); - - row.SetProperty(context.Result, (x) => x.CreatedOnUtc, UtcNow); - row.SetProperty(context.Result, (x) => x.LastActivityDateUtc, UtcNow); + } + } + + protected virtual int ProcessCustomers( + ImportExecuteContext context, + IEnumerable> batch, + List allAffiliateIds, + HashSet allCustomerNumbers) + { + _customerRepository.AutoCommitEnabled = true; + + var currentCustomer = _services.WorkContext.CurrentCustomer; + var customerQuery = _customerRepository.Table + .Expand(x => x.Addresses) + .Expand(x => x.CustomerRoleMappings.Select(rm => rm.CustomerRole)); + + foreach (var row in batch) + { + Customer customer = null; + var id = row.GetDataValue("Id"); + var email = row.GetDataValue("Email"); + var userName = row.GetDataValue("Username"); + + foreach (var keyName in context.KeyFieldNames) + { + switch (keyName) + { + case "Id": + if (id != 0) + { + customer = customerQuery.FirstOrDefault(x => x.Id == id); + } + break; + case "CustomerGuid": + var customerGuid = row.GetDataValue("CustomerGuid"); + if (customerGuid.HasValue()) + { + var guid = new Guid(customerGuid); + customer = customerQuery.FirstOrDefault(x => x.CustomerGuid == guid); + } + break; + case "Email": + if (email.HasValue()) + { + customer = customerQuery.FirstOrDefault(x => x.Email == email); + } + break; + case "Username": + if (userName.HasValue()) + { + customer = customerQuery.FirstOrDefault(x => x.Username == userName); + } + break; + } + + if (customer != null) + break; + } + + if (customer == null) + { + if (context.UpdateOnly) + { + ++context.Result.SkippedRecords; + continue; + } + + customer = new Customer + { + CustomerGuid = new Guid(), + AffiliateId = 0, + Active = true + }; + } + else + { + _customerRepository.Context.LoadCollection(customer, (Customer x) => x.CustomerRoleMappings); + } + + var affiliateId = row.GetDataValue("AffiliateId"); + + row.Initialize(customer, email ?? id.ToString()); + + row.SetProperty(context.Result, (x) => x.CustomerGuid); + row.SetProperty(context.Result, (x) => x.Username); + row.SetProperty(context.Result, (x) => x.Email); + row.SetProperty(context.Result, (x) => x.Salutation); + row.SetProperty(context.Result, (x) => x.FullName); + row.SetProperty(context.Result, (x) => x.FirstName); + row.SetProperty(context.Result, (x) => x.LastName); + + if (_customerSettings.TitleEnabled) + row.SetProperty(context.Result, (x) => x.Title); + + if (_customerSettings.CompanyEnabled) + row.SetProperty(context.Result, (x) => x.Company); + + if (_customerSettings.DateOfBirthEnabled) + row.SetProperty(context.Result, (x) => x.BirthDate); + + if (_privacySettings.StoreLastIpAddress) + row.SetProperty(context.Result, (x) => x.LastIpAddress); + + if (email.HasValue() && currentCustomer.Email.IsCaseInsensitiveEqual(email)) + { + context.Result.AddInfo("Security. Ignored password of current customer (who started this import).", row.GetRowInfo(), "Password"); + } + else + { + row.SetProperty(context.Result, (x) => x.Password); + row.SetProperty(context.Result, (x) => x.PasswordFormatId); + row.SetProperty(context.Result, (x) => x.PasswordSalt); + } + row.SetProperty(context.Result, (x) => x.AdminComment); + row.SetProperty(context.Result, (x) => x.IsTaxExempt); + row.SetProperty(context.Result, (x) => x.Active); + + row.SetProperty(context.Result, (x) => x.CreatedOnUtc, UtcNow); + row.SetProperty(context.Result, (x) => x.LastActivityDateUtc, UtcNow); + + if (_taxSettings.EuVatEnabled) + row.SetProperty(context.Result, (x) => x.VatNumberStatusId); if (_dateTimeSettings.AllowCustomersToSetTimeZone) row.SetProperty(context.Result, (x) => x.TimeZoneId); @@ -321,213 +335,228 @@ protected virtual int ProcessCustomers( if (affiliateId > 0 && allAffiliateIds.Contains(affiliateId)) customer.AffiliateId = affiliateId; - - string customerNumber = null; - - if (_customerSettings.CustomerNumberMethod == CustomerNumberMethod.AutomaticallySet && row.IsTransient) - customerNumber = row.Entity.Id.ToString(); - else if (_customerSettings.CustomerNumberMethod == CustomerNumberMethod.Enabled && !row.IsTransient && row.HasDataValue("CustomerNumber")) - customerNumber = row.GetDataValue("CustomerNumber"); - - if (customerNumber.HasValue() || !allCustomerNumbers.Contains(customerNumber)) - { - row.Entity.CustomerNumber = customerNumber; - - if (!customerNumber.IsEmpty()) - allCustomerNumbers.Add(customerNumber); - } - - if (row.IsTransient) - { - _customerRepository.Insert(customer); - } - else - { - _customerRepository.Update(customer); - } - } - - var num = _customerRepository.Context.SaveChanges(); - return num; - } - - protected virtual int ProcessCustomerRoles( - ImportExecuteContext context, - IEnumerable> batch, - Dictionary allCustomerRoles) - { - CustomerRole role = null; - var hasCustomerRoleSystemNames = context.DataSegmenter.HasColumn("CustomerRoleSystemNames"); - - // Deprecated fields. Still processed for backward compatibility. Use "CustomerRoleSystemNames" instead. - var hasIsGuest = context.DataSegmenter.HasColumn("IsGuest"); - var hasIsRegistered = context.DataSegmenter.HasColumn("IsRegistered"); - var hasIsForumModerator = context.DataSegmenter.HasColumn("IsForumModerator"); - - foreach (var row in batch) - { - var customer = row.Entity; - - // Deprecated customer role fields. - if (hasIsGuest) - { - UpsertRole(customer, row.GetDataValue("IsGuest"), allCustomerRoles, SystemCustomerRoleNames.Guests); - } - if (hasIsRegistered) - { - UpsertRole(customer, row.GetDataValue("IsRegistered"), allCustomerRoles, SystemCustomerRoleNames.Registered); - } - if (hasIsForumModerator) - { - UpsertRole(customer, row.GetDataValue("IsForumModerator"), allCustomerRoles, SystemCustomerRoleNames.ForumModerators); - } - - // New customer role field. - if (hasCustomerRoleSystemNames) - { - var importRoleSystemNames = row.GetDataValue>("CustomerRoleSystemNames"); - var assignedRoles = customer.CustomerRoles.ToDictionarySafe(x => x.SystemName, StringComparer.OrdinalIgnoreCase); - - // Roles to remove. - foreach (var customerRole in assignedRoles) - { - var systemName = customerRole.Key; - if (!systemName.IsCaseInsensitiveEqual(SystemCustomerRoleNames.Administrators) && - !systemName.IsCaseInsensitiveEqual(SystemCustomerRoleNames.SuperAdministrators) && - !importRoleSystemNames.Contains(systemName)) - { - customer.CustomerRoles.Remove(customerRole.Value); - } - } - - // Roles to add. - foreach (var systemName in importRoleSystemNames) - { - if (systemName.IsCaseInsensitiveEqual(SystemCustomerRoleNames.Administrators) || - systemName.IsCaseInsensitiveEqual(SystemCustomerRoleNames.SuperAdministrators)) - { - context.Result.AddInfo("Security. Ignored administrator role.", row.GetRowInfo(), "CustomerRoleSystemNames"); - } - else if (!assignedRoles.ContainsKey(systemName) && allCustomerRoles.TryGetValue(systemName, out role)) - { - // Never insert roles! - context.Services.DbContext.SetToUnchanged(role); - - customer.CustomerRoles.Add(role); - } - } - } - } - - return context.Services.DbContext.SaveChanges(); - } - - protected virtual int ProcessAddresses( - ImportExecuteContext context, - IEnumerable> batch, - Dictionary allCountries, - Dictionary, int> allStateProvinces) - { - foreach (var row in batch) - { - ImportAddress("BillingAddress.", row, context, allCountries, allStateProvinces); - ImportAddress("ShippingAddress.", row, context, allCountries, allStateProvinces); - } - - return _services.DbContext.SaveChanges(); - } - - private void ImportAddress( - string fieldPrefix, - ImportRow row, - ImportExecuteContext context, - Dictionary allCountries, - Dictionary, int> allStateProvinces) - { - // last name is mandatory for an address to be imported or updated - if (!row.HasDataValue(fieldPrefix + "LastName")) - return; - - var address = new Address - { - CreatedOnUtc = UtcNow - }; - - var childRow = new ImportRow
(row.Segmenter, row.DataRow, row.Position); - childRow.Initialize(address, row.EntityDisplayName); - - childRow.SetProperty(context.Result, fieldPrefix + "LastName", x => x.LastName); - childRow.SetProperty(context.Result, fieldPrefix + "FirstName", x => x.FirstName); - childRow.SetProperty(context.Result, fieldPrefix + "Email", x => x.Email); - childRow.SetProperty(context.Result, fieldPrefix + "Company", x => x.Company); - childRow.SetProperty(context.Result, fieldPrefix + "City", x => x.City); - childRow.SetProperty(context.Result, fieldPrefix + "Address1", x => x.Address1); - childRow.SetProperty(context.Result, fieldPrefix + "Address2", x => x.Address2); - childRow.SetProperty(context.Result, fieldPrefix + "ZipPostalCode", x => x.ZipPostalCode); - childRow.SetProperty(context.Result, fieldPrefix + "PhoneNumber", x => x.PhoneNumber); - childRow.SetProperty(context.Result, fieldPrefix + "FaxNumber", x => x.FaxNumber); - - childRow.SetProperty(context.Result, fieldPrefix + "CountryId", x => x.CountryId); - if (childRow.Entity.CountryId == null) - { - // try with country code - childRow.SetProperty(context.Result, fieldPrefix + "CountryCode", x => x.CountryId, converter: (val, ci) => CountryCodeToId(allCountries, val.ToString())); - } - - var countryId = childRow.Entity.CountryId; - - if (countryId.HasValue) - { - childRow.SetProperty(context.Result, fieldPrefix + "StateProvinceId", x => x.StateProvinceId); - if (childRow.Entity.StateProvinceId == null) - { - // try with state abbreviation - childRow.SetProperty(context.Result, fieldPrefix + "StateAbbreviation", x => x.StateProvinceId, converter: (val, ci) => StateAbbreviationToId(allStateProvinces, countryId, val.ToString())); - } - } - - if (!childRow.IsDirty) - { - // Not one single property could be set. Get out! - return; - } - - var appliedAddress = row.Entity.Addresses.FindAddress(address); - - if (appliedAddress == null) - { - appliedAddress = address; - row.Entity.Addresses.Add(appliedAddress); - } - - if (fieldPrefix == "BillingAddress.") - { - row.Entity.BillingAddress = appliedAddress; - } - else if (fieldPrefix == "ShippingAddress.") - { - row.Entity.ShippingAddress = appliedAddress; - } - - _customerRepository.Update(row.Entity); - } - - protected virtual int ProcessGenericAttributes( - ImportExecuteContext context, - IEnumerable> batch, - Dictionary allCountries, - Dictionary, int> allStateProvinces) - { - foreach (var row in batch) - { + + string customerNumber = null; + + if (_customerSettings.CustomerNumberMethod == CustomerNumberMethod.AutomaticallySet && row.IsTransient) + { + customerNumber = row.Entity.Id.ToString(); + } + else if (_customerSettings.CustomerNumberMethod == CustomerNumberMethod.Enabled && !row.IsTransient && row.HasDataValue("CustomerNumber")) + { + customerNumber = row.GetDataValue("CustomerNumber"); + } + + if (customerNumber.HasValue() || !allCustomerNumbers.Contains(customerNumber)) + { + row.Entity.CustomerNumber = customerNumber; + + if (!customerNumber.IsEmpty()) + { + allCustomerNumbers.Add(customerNumber); + } + } + + if (row.IsTransient) + { + _customerRepository.Insert(customer); + } + else + { + _customerRepository.Update(customer); + } + } + + var num = _customerRepository.Context.SaveChanges(); + return num; + } + + protected virtual int ProcessCustomerRoles( + ImportExecuteContext context, + IEnumerable> batch) + { + Dictionary allCustomerRoles = null; + + foreach (var row in batch) + { + var customer = row.Entity; + var importRoleSystemNames = row.GetDataValue>("CustomerRoleSystemNames"); + + var assignedRoles = customer.CustomerRoleMappings + .Where(x => !x.IsSystemMapping) + .Select(x => x.CustomerRole) + .ToDictionarySafe(x => x.SystemName, StringComparer.OrdinalIgnoreCase); + + // Roles to remove. + foreach (var customerRole in assignedRoles) + { + var systemName = customerRole.Key; + if (!systemName.IsCaseInsensitiveEqual(SystemCustomerRoleNames.Administrators) && + !systemName.IsCaseInsensitiveEqual(SystemCustomerRoleNames.SuperAdministrators) && + !importRoleSystemNames.Contains(systemName)) + { + var mappings = customer.CustomerRoleMappings.Where(x => !x.IsSystemMapping && x.CustomerRoleId == customerRole.Value.Id).ToList(); + mappings.Each(x => _customerService.DeleteCustomerRoleMapping(x)); + } + } + + // Roles to add. + foreach (var systemName in importRoleSystemNames) + { + if (systemName.IsCaseInsensitiveEqual(SystemCustomerRoleNames.Administrators) || + systemName.IsCaseInsensitiveEqual(SystemCustomerRoleNames.SuperAdministrators)) + { + context.Result.AddInfo("Security. Ignored administrator role.", row.GetRowInfo(), "CustomerRoleSystemNames"); + } + else if (!assignedRoles.ContainsKey(systemName)) + { + // Add role mapping, never insert roles. + // Be careful not to insert the roles several times! + if (allCustomerRoles == null) + { + allCustomerRoles = _customerRoleRepository.TableUntracked + .Where(x => !string.IsNullOrEmpty(x.SystemName)) + .ToDictionarySafe(x => x.SystemName, StringComparer.OrdinalIgnoreCase); + } + + if (allCustomerRoles.TryGetValue(systemName, out var role)) + { + _customerService.InsertCustomerRoleMapping(new CustomerRoleMapping { CustomerId = customer.Id, CustomerRoleId = role.Id }); + } + } + } + } + + return _services.DbContext.SaveChanges(); + } + + protected virtual int ProcessAddresses( + ImportExecuteContext context, + IEnumerable> batch, + Dictionary allCountries, + Dictionary, int> allStateProvinces) + { + foreach (var row in batch) + { + ImportAddress("BillingAddress.", row, context, allCountries, allStateProvinces); + ImportAddress("ShippingAddress.", row, context, allCountries, allStateProvinces); + } + + return _services.DbContext.SaveChanges(); + } + + private void ImportAddress( + string fieldPrefix, + ImportRow row, + ImportExecuteContext context, + Dictionary allCountries, + Dictionary, int> allStateProvinces) + { + // Last name is mandatory for an address to be imported or updated. + if (!row.HasDataValue(fieldPrefix + "LastName")) + { + return; + } + + Address address = null; + + if (fieldPrefix == "BillingAddress.") + { + address = row.Entity.BillingAddress ?? new Address { CreatedOnUtc = UtcNow }; + } + else if (fieldPrefix == "ShippingAddress.") + { + address = row.Entity.ShippingAddress ?? new Address { CreatedOnUtc = UtcNow }; + } + + var childRow = new ImportRow
(row.Segmenter, row.DataRow, row.Position); + childRow.Initialize(address, row.EntityDisplayName); + + childRow.SetProperty(context.Result, fieldPrefix + "Salutation", x => x.Salutation); + childRow.SetProperty(context.Result, fieldPrefix + "Title", x => x.Title); + childRow.SetProperty(context.Result, fieldPrefix + "FirstName", x => x.FirstName); + childRow.SetProperty(context.Result, fieldPrefix + "LastName", x => x.LastName); + childRow.SetProperty(context.Result, fieldPrefix + "Email", x => x.Email); + childRow.SetProperty(context.Result, fieldPrefix + "Company", x => x.Company); + childRow.SetProperty(context.Result, fieldPrefix + "City", x => x.City); + childRow.SetProperty(context.Result, fieldPrefix + "Address1", x => x.Address1); + childRow.SetProperty(context.Result, fieldPrefix + "Address2", x => x.Address2); + childRow.SetProperty(context.Result, fieldPrefix + "ZipPostalCode", x => x.ZipPostalCode); + childRow.SetProperty(context.Result, fieldPrefix + "PhoneNumber", x => x.PhoneNumber); + childRow.SetProperty(context.Result, fieldPrefix + "FaxNumber", x => x.FaxNumber); + + childRow.SetProperty(context.Result, fieldPrefix + "CountryId", x => x.CountryId); + if (childRow.Entity.CountryId == null) + { + // Try with country code. + childRow.SetProperty(context.Result, fieldPrefix + "CountryCode", x => x.CountryId, converter: (val, ci) => CountryCodeToId(allCountries, val.ToString())); + } + + var countryId = childRow.Entity.CountryId; + if (countryId.HasValue) + { + if (row.HasDataValue(fieldPrefix + "StateProvinceId")) + { + childRow.SetProperty(context.Result, fieldPrefix + "StateProvinceId", x => x.StateProvinceId); + } + else + { + // Try with state abbreviation. + childRow.SetProperty(context.Result, fieldPrefix + "StateAbbreviation", x => x.StateProvinceId, converter: (val, ci) => StateAbbreviationToId(allStateProvinces, countryId, val.ToString())); + } + } + + if (!childRow.IsDirty) + { + // Not one single property could be set. Get out! + return; + } + + if (address.Id == 0) + { + // Avoid importing two addresses if billing and shipping address are equal. + var appliedAddress = row.Entity.Addresses.FindAddress(address); + if (appliedAddress == null) + { + appliedAddress = address; + row.Entity.Addresses.Add(appliedAddress); + } + + // Map address to customer. + if (fieldPrefix == "BillingAddress.") + { + row.Entity.BillingAddress = appliedAddress; + } + else if (fieldPrefix == "ShippingAddress.") + { + row.Entity.ShippingAddress = appliedAddress; + } + } + + _customerRepository.Update(row.Entity); + } + + protected virtual int ProcessGenericAttributes( + ImportExecuteContext context, + IEnumerable> batch, + Dictionary allCountries, + Dictionary, int> allStateProvinces) + { + foreach (var row in batch) + { + if (_taxSettings.EuVatEnabled) + { + SaveAttribute(row, SystemCustomerAttributeNames.VatNumber); + } + if (_customerSettings.StreetAddressEnabled) - SaveAttribute(row, SystemCustomerAttributeNames.StreetAddress); + SaveAttribute(row, SystemCustomerAttributeNames.StreetAddress); - if (_customerSettings.StreetAddress2Enabled) - SaveAttribute(row, SystemCustomerAttributeNames.StreetAddress2); + if (_customerSettings.StreetAddress2Enabled) + SaveAttribute(row, SystemCustomerAttributeNames.StreetAddress2); - if (_customerSettings.CityEnabled) - SaveAttribute(row, SystemCustomerAttributeNames.City); + if (_customerSettings.CityEnabled) + SaveAttribute(row, SystemCustomerAttributeNames.City); if (_customerSettings.ZipPostalCodeEnabled) SaveAttribute(row, SystemCustomerAttributeNames.ZipPostalCode); @@ -536,203 +565,172 @@ protected virtual int ProcessGenericAttributes( SaveAttribute(row, SystemCustomerAttributeNames.CountryId); if (_customerSettings.CountryEnabled && _customerSettings.StateProvinceEnabled) - SaveAttribute(row, SystemCustomerAttributeNames.StateProvinceId); + SaveAttribute(row, SystemCustomerAttributeNames.StateProvinceId); - if (_customerSettings.PhoneEnabled) - SaveAttribute(row, SystemCustomerAttributeNames.Phone); + if (_customerSettings.PhoneEnabled) + SaveAttribute(row, SystemCustomerAttributeNames.Phone); - if (_customerSettings.FaxEnabled) - SaveAttribute(row, SystemCustomerAttributeNames.Fax); + if (_customerSettings.FaxEnabled) + SaveAttribute(row, SystemCustomerAttributeNames.Fax); - if (_forumSettings.ForumsEnabled) - SaveAttribute(row, SystemCustomerAttributeNames.ForumPostCount); + if (_forumSettings.ForumsEnabled) + SaveAttribute(row, SystemCustomerAttributeNames.ForumPostCount); - if (_forumSettings.SignaturesEnabled) - SaveAttribute(row, SystemCustomerAttributeNames.Signature); + if (_forumSettings.SignaturesEnabled) + SaveAttribute(row, SystemCustomerAttributeNames.Signature); var countryId = CountryCodeToId(allCountries, row.GetDataValue("CountryCode")); var stateId = StateAbbreviationToId(allStateProvinces, countryId, row.GetDataValue("StateAbbreviation")); if (countryId.HasValue) SaveAttribute(row, SystemCustomerAttributeNames.CountryId, countryId.Value); - + if (stateId.HasValue) - SaveAttribute(row, SystemCustomerAttributeNames.StateProvinceId, stateId.Value); - - } - - return _services.DbContext.SaveChanges(); - } - - protected virtual int ProcessAvatars( - ImportExecuteContext context, - IEnumerable> batch) - { - foreach (var row in batch) - { - var urlOrPath = row.GetDataValue("AvatarPictureUrl"); - if (urlOrPath.IsEmpty()) - continue; - - var equalPictureId = 0; - var currentPictures = new List(); - var seoName = _pictureService.GetPictureSeName(row.EntityDisplayName); - - var image = CreateDownloadImage(context, urlOrPath, seoName, 1); - if (image == null) - continue; - - if (image.Url.HasValue() && !image.Success.HasValue) - { - AsyncRunner.RunSync(() => _fileDownloadManager.DownloadAsync(DownloaderContext, new FileDownloadManagerItem[] { image })); - } - - if ((image.Success ?? false) && File.Exists(image.Path)) - { - Succeeded(image); - var pictureBinary = File.ReadAllBytes(image.Path); - - if (pictureBinary != null && pictureBinary.Length > 0) - { - var pictureId = row.Entity.GetAttribute(SystemCustomerAttributeNames.AvatarPictureId); - if (pictureId != 0) - { - var picture = _pictureRepository.TableUntracked.Expand(x => x.MediaStorage).FirstOrDefault(x => x.Id == pictureId); - if (picture != null) - { - currentPictures.Add(picture); - } - } - - pictureBinary = _pictureService.FindEqualPicture(pictureBinary, currentPictures, out equalPictureId); - - if (pictureBinary != null && pictureBinary.Length > 0) - { - var picture = _pictureService.InsertPicture(pictureBinary, image.MimeType, seoName, true, false, false); - - if (picture != null) - { - SaveAttribute(row, SystemCustomerAttributeNames.AvatarPictureId, picture.Id); - } - } - else - { - context.Result.AddInfo("Found equal picture in data store. Skipping field.", row.GetRowInfo(), "AvatarPictureUrl"); - } - } - } - else - { - context.Result.AddInfo("Download of an image failed.", row.GetRowInfo(), "AvatarPictureUrl"); - } - } - - return _services.DbContext.SaveChanges(); - } - - public static string[] SupportedKeyFields - { - get - { - return new string[] { "Id", "CustomerGuid", "Email", "Username" }; - } - } - - public static string[] DefaultKeyFields - { - get - { - return new string[] { "Email" }; - } - } - - private int? CountryCodeToId(Dictionary allCountries, string code) - { - int countryId; - if (code.HasValue() && allCountries.TryGetValue(code, out countryId) && countryId != 0) - { - return countryId; - } - - return null; - } - - private int? StateAbbreviationToId(Dictionary, int> allStateProvinces, int? countryId, string abbreviation) - { - if (countryId.HasValue && abbreviation.HasValue()) - { - var key = Tuple.Create(countryId.Value, abbreviation); - - int stateId; - if (allStateProvinces.TryGetValue(key, out stateId) && stateId != 0) - { - return stateId; - } - } - - return null; - } - - private void SaveAttribute(ImportRow row, string key) - { - SaveAttribute(row, key, row.GetDataValue(key)); - } - - private void SaveAttribute(ImportRow row, string key) - { - SaveAttribute(row, key, row.GetDataValue(key)); - } - - private void SaveAttribute(ImportRow row, string key, TPropType value) - { - if (row.IsTransient) - return; - - if (row.IsNew || value != null) - { - _genericAttributeService.SaveAttribute(row.Entity.Id, key, _attributeKeyGroup, value); - } - } - - private void UpsertRole(Customer customer, bool value, Dictionary allRoles, string customerRoleSystemName) - { - CustomerRole role = null; - if (allRoles.TryGetValue(customerRoleSystemName, out role)) - { - var hasRole = customer.CustomerRoles.Any(x => x.SystemName == role.SystemName); - - if (value && !hasRole) - { - // Never insert roles! - _services.DbContext.SetToUnchanged(role); - - customer.CustomerRoles.Add(role); - } - else if (!value && hasRole) - { - customer.CustomerRoles.Remove(role); - } - } - } - - private void AddInfoForDeprecatedFields(ImportExecuteContext context) - { - if (context.DataSegmenter.HasColumn("IsGuest")) - { - context.Result.AddInfo("Deprecated field. Use CustomerRoleSystemNames instead.", null, "IsGuest"); - } - if (context.DataSegmenter.HasColumn("IsRegistered")) - { - context.Result.AddInfo("Deprecated field. Use CustomerRoleSystemNames instead.", null, "IsRegistered"); - } - if (context.DataSegmenter.HasColumn("IsAdministrator")) - { - context.Result.AddInfo("Deprecated field. Use CustomerRoleSystemNames instead.", null, "IsAdministrator"); - } - if (context.DataSegmenter.HasColumn("IsForumModerator")) - { - context.Result.AddInfo("Deprecated field. Use CustomerRoleSystemNames instead.", null, "IsForumModerator"); - } - } - } + SaveAttribute(row, SystemCustomerAttributeNames.StateProvinceId, stateId.Value); + + } + + return _services.DbContext.SaveChanges(); + } + + protected virtual int ProcessAvatars( + ImportExecuteContext context, + IEnumerable> batch) + { + foreach (var row in batch) + { + var urlOrPath = row.GetDataValue("AvatarPictureUrl"); + if (urlOrPath.IsEmpty()) + { + continue; + } + + var image = CreateDownloadImage(context, urlOrPath, 1); + if (image == null) + { + continue; + } + + if (image.Url.HasValue() && !image.Success.HasValue) + { + AsyncRunner.RunSync(() => _fileDownloadManager.DownloadAsync(DownloaderContext, new FileDownloadManagerItem[] { image })); + } + + if ((image.Success ?? false) && File.Exists(image.Path)) + { + Succeeded(image); + using (var stream = File.OpenRead(image.Path)) + { + if (stream?.Length > 0) + { + MediaFile sourceFile = null; + var currentFiles = new List(); + var fileId = row.Entity.GetAttribute(SystemCustomerAttributeNames.AvatarPictureId); + var file = _mediaService.GetFileById(fileId, MediaLoadFlags.AsNoTracking); + + if (file != null) + { + currentFiles.Add(file); + } + + if (_mediaService.FindEqualFile(stream, currentFiles.Select(x => x.File), true, out var _)) + { + context.Result.AddInfo($"Found equal image in customer data for {image.FileName}. Skipping file.", row.GetRowInfo(), "AvatarPictureUrl"); + } + else + { + // An avatar may not be assigned to several customers. A customer could otherwise delete the avatar of another. + // Overwriting is probably too dangerous here, because we could overwrite the avatar of another customer, so better rename. + var path = _mediaService.CombinePaths(SystemAlbumProvider.Customers, image.FileName); + sourceFile = _mediaService.SaveFile(path, stream, false, DuplicateFileHandling.Rename)?.File; + } + + if (sourceFile?.Id > 0) + { + SaveAttribute(row, SystemCustomerAttributeNames.AvatarPictureId, sourceFile.Id); + } + } + } + } + else + { + context.Result.AddInfo("Download of avatar failed.", row.GetRowInfo(), "AvatarPictureUrl"); + } + } + + return _services.DbContext.SaveChanges(); + } + + public static string[] SupportedKeyFields => new string[] { "Id", "CustomerGuid", "Email", "Username" }; + + public static string[] DefaultKeyFields => new string[] { "Email", "CustomerGuid" }; + + private int? CountryCodeToId(Dictionary allCountries, string code) + { + int countryId; + if (code.HasValue() && allCountries.TryGetValue(code, out countryId) && countryId != 0) + { + return countryId; + } + + return null; + } + + private int? StateAbbreviationToId(Dictionary, int> allStateProvinces, int? countryId, string abbreviation) + { + if (countryId.HasValue && abbreviation.HasValue()) + { + var key = Tuple.Create(countryId.Value, abbreviation); + + int stateId; + if (allStateProvinces.TryGetValue(key, out stateId) && stateId != 0) + { + return stateId; + } + } + + return null; + } + + private void SaveAttribute(ImportRow row, string key) + { + SaveAttribute(row, key, row.GetDataValue(key)); + } + + private void SaveAttribute(ImportRow row, string key) + { + SaveAttribute(row, key, row.GetDataValue(key)); + } + + private void SaveAttribute(ImportRow row, string key, TPropType value) + { + if (row.IsTransient) + return; + + if (row.IsNew || value != null) + { + _genericAttributeService.SaveAttribute(row.Entity.Id, key, _attributeKeyGroup, value); + } + } + + private void AddInfoForDeprecatedFields(ImportExecuteContext context) + { + if (context.DataSegmenter.HasColumn("IsGuest")) + { + context.Result.AddInfo("Deprecated field. Use CustomerRoleSystemNames instead.", null, "IsGuest"); + } + if (context.DataSegmenter.HasColumn("IsRegistered")) + { + context.Result.AddInfo("Deprecated field. Use CustomerRoleSystemNames instead.", null, "IsRegistered"); + } + if (context.DataSegmenter.HasColumn("IsAdministrator")) + { + context.Result.AddInfo("Deprecated field. Use CustomerRoleSystemNames instead.", null, "IsAdministrator"); + } + if (context.DataSegmenter.HasColumn("IsForumModerator")) + { + context.Result.AddInfo("Deprecated field. Use CustomerRoleSystemNames instead.", null, "IsForumModerator"); + } + } + } } diff --git a/src/Libraries/SmartStore.Services/Customers/PasswordChangeResult.cs b/src/Libraries/SmartStore.Services/Customers/PasswordChangeResult.cs index 6270fd3a4b..0b035a39b6 100644 --- a/src/Libraries/SmartStore.Services/Customers/PasswordChangeResult.cs +++ b/src/Libraries/SmartStore.Services/Customers/PasswordChangeResult.cs @@ -1,22 +1,19 @@ using System.Collections.Generic; -namespace SmartStore.Services.Customers +namespace SmartStore.Services.Customers { public class PasswordChangeResult { public IList Errors { get; set; } - public PasswordChangeResult() + public PasswordChangeResult() { this.Errors = new List(); } - public bool Success - { - get { return (this.Errors.Count == 0); } - } + public bool Success => (this.Errors.Count == 0); - public void AddError(string error) + public void AddError(string error) { this.Errors.Add(error); } diff --git a/src/Libraries/SmartStore.Services/Customers/Rules/ITargetGroupService.cs b/src/Libraries/SmartStore.Services/Customers/Rules/ITargetGroupService.cs index ab018d0f3e..ac24dc9358 100644 --- a/src/Libraries/SmartStore.Services/Customers/Rules/ITargetGroupService.cs +++ b/src/Libraries/SmartStore.Services/Customers/Rules/ITargetGroupService.cs @@ -15,8 +15,8 @@ public interface ITargetGroupService : IRuleProvider FilterExpressionGroup CreateExpressionGroup(int ruleSetId); IPagedList ProcessFilter( - FilterExpression filter, - int pageIndex = 0, + FilterExpression filter, + int pageIndex = 0, int pageSize = int.MaxValue); IPagedList ProcessFilter( diff --git a/src/Libraries/SmartStore.Services/Customers/Rules/TargetGroupEvaluatorTask.cs b/src/Libraries/SmartStore.Services/Customers/Rules/TargetGroupEvaluatorTask.cs new file mode 100644 index 0000000000..8a8481effa --- /dev/null +++ b/src/Libraries/SmartStore.Services/Customers/Rules/TargetGroupEvaluatorTask.cs @@ -0,0 +1,141 @@ +using System.Collections.Generic; +using System.Data.Entity; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using SmartStore.Core.Caching; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Customers; +using SmartStore.Data.Utilities; +using SmartStore.Rules; +using SmartStore.Rules.Filters; +using SmartStore.Services.Security; +using SmartStore.Services.Tasks; + +namespace SmartStore.Services.Customers +{ + /// + /// Updates the system assignments to customer roles for rules. + /// + public partial class TargetGroupEvaluatorTask : AsyncTask + { + protected readonly IRepository _customerRoleRepository; + protected readonly IRepository _customerRoleMappingRepository; + protected readonly IRuleFactory _ruleFactory; + protected readonly ITargetGroupService _targetGroupService; + protected readonly ICacheManager _cacheManager; + + public TargetGroupEvaluatorTask( + IRepository customerRoleRepository, + IRepository customerRoleMappingRepository, + IRuleFactory ruleFactory, + ITargetGroupService targetGroupService, + ICacheManager cacheManager) + { + _customerRoleRepository = customerRoleRepository; + _customerRoleMappingRepository = customerRoleMappingRepository; + _ruleFactory = ruleFactory; + _targetGroupService = targetGroupService; + _cacheManager = cacheManager; + } + + public override async Task ExecuteAsync(TaskExecutionContext ctx) + { + var count = 0; + var numDeleted = 0; + var numAdded = 0; + var roleQuery = _customerRoleRepository.TableUntracked.Expand(x => x.RuleSets); + + if (ctx.Parameters.ContainsKey("CustomerRoleIds")) + { + var roleIds = ctx.Parameters["CustomerRoleIds"].ToIntArray(); + roleQuery = roleQuery.Where(x => roleIds.Contains(x.Id)); + + numDeleted = _customerRoleMappingRepository.Context.ExecuteSqlCommand( + "Delete From [dbo].[CustomerRoleMapping] Where [CustomerRoleId] In ({0}) And [IsSystemMapping] = 1", + false, + null, + string.Join(",", roleIds)); + } + else + { + numDeleted = _customerRoleMappingRepository.Context.ExecuteSqlCommand("Delete From [dbo].[CustomerRoleMapping] Where [IsSystemMapping] = 1"); + } + + var roles = await roleQuery + .Where(x => x.Active && x.RuleSets.Any(y => y.IsActive)) + .ToListAsync(); + + using (var scope = new DbContextScope(ctx: _customerRoleMappingRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false)) + { + foreach (var role in roles) + { + var ruleSetCustomerIds = new HashSet(); + + ctx.SetProgress(++count, roles.Count, $"Add customer assignments for role \"{role.SystemName.NaIfEmpty()}\"."); + + // Execute active rule sets and collect customer ids. + foreach (var ruleSet in role.RuleSets.Where(x => x.IsActive)) + { + if (ctx.CancellationToken.IsCancellationRequested) + return; + + if (_ruleFactory.CreateExpressionGroup(ruleSet, _targetGroupService) is FilterExpression expression) + { + var filterResult = _targetGroupService.ProcessFilter(expression, 0, 500); + var resultPager = new FastPager(filterResult.SourceQuery, 500); + + while (true) + { + var customerIds = await resultPager.ReadNextPageAsync(x => x.Id, x => x); + if (!(customerIds?.Any() ?? false)) + { + break; + } + + ruleSetCustomerIds.AddRange(customerIds); + } + } + } + + // Add mappings. + if (ruleSetCustomerIds.Any()) + { + foreach (var chunk in ruleSetCustomerIds.Slice(500)) + { + if (ctx.CancellationToken.IsCancellationRequested) + return; + + foreach (var customerId in chunk) + { + _customerRoleMappingRepository.Insert(new CustomerRoleMapping + { + CustomerId = customerId, + CustomerRoleId = role.Id, + IsSystemMapping = true + }); + + ++numAdded; + } + + await scope.CommitAsync(); + } + + try + { + _customerRoleMappingRepository.Context.DetachEntities(); + } + catch { } + } + } + } + + if (numAdded > 0 || numDeleted > 0) + { + _cacheManager.RemoveByPattern(AclService.ACL_SEGMENT_PATTERN); + } + + Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Deleted {numDeleted} and added {numAdded} customer assignments for {roles.Count} roles."); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Customers/Rules/TargetGroupFilterDescriptor.cs b/src/Libraries/SmartStore.Services/Customers/Rules/TargetGroupFilterDescriptor.cs index 9cc07202c9..266db9b3d1 100644 --- a/src/Libraries/SmartStore.Services/Customers/Rules/TargetGroupFilterDescriptor.cs +++ b/src/Libraries/SmartStore.Services/Customers/Rules/TargetGroupFilterDescriptor.cs @@ -15,7 +15,7 @@ public class TargetGroupFilterDescriptor : FilterDescriptor private readonly IRuleFactory _ruleFactory; private readonly WeakReference _ruleVisitor; - public TargetGroupFilterDescriptor(IRuleFactory ruleFactory, IRuleVisitor ruleVisitor) + public TargetGroupFilterDescriptor(IRuleFactory ruleFactory, IRuleVisitor ruleVisitor) : base(x => true) { _ruleFactory = ruleFactory; @@ -25,7 +25,7 @@ public TargetGroupFilterDescriptor(IRuleFactory ruleFactory, IRuleVisitor ruleVi public override Expression GetExpression(RuleOperator op, Expression valueExpression, bool liftToNull) { var ruleSetId = ((ConstantExpression)valueExpression).Value.Convert(); - + // Get other expression group _ruleVisitor.TryGetTarget(out var visitor); var otherGroup = _ruleFactory.CreateExpressionGroup(ruleSetId, visitor) as FilterExpressionGroup; diff --git a/src/Libraries/SmartStore.Services/Customers/Rules/TargetGroupService.cs b/src/Libraries/SmartStore.Services/Customers/Rules/TargetGroupService.cs index 2a52dd9f06..2b54efadc3 100644 --- a/src/Libraries/SmartStore.Services/Customers/Rules/TargetGroupService.cs +++ b/src/Libraries/SmartStore.Services/Customers/Rules/TargetGroupService.cs @@ -1,17 +1,19 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Data.Entity; +using System.Linq; using SmartStore.Core; using SmartStore.Core.Data; -using SmartStore.Rules; -using SmartStore.Rules.Filters; -using SmartStore.Rules.Domain; using SmartStore.Core.Domain.Customers; -using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Orders; +using SmartStore.Core.Domain.Payments; +using SmartStore.Core.Domain.Shipping; +using SmartStore.Core.Domain.Tax; +using SmartStore.Core.Localization; +using SmartStore.Rules; +using SmartStore.Rules.Domain; +using SmartStore.Rules.Filters; +using SmartStore.Services.Localization; namespace SmartStore.Services.Customers { @@ -22,7 +24,7 @@ public class TargetGroupService : RuleProviderBase, ITargetGroupService private readonly ICommonServices _services; public TargetGroupService( - IRuleFactory ruleFactory, + IRuleFactory ruleFactory, IRepository rsCustomer, ICommonServices services) : base(RuleScope.Customer) { @@ -31,6 +33,8 @@ public TargetGroupService( _services = services; } + public Localizer T { get; set; } = NullLocalizer.Instance; + public FilterExpressionGroup CreateExpressionGroup(int ruleSetId) { return _ruleFactory.CreateExpressionGroup(ruleSetId, this) as FilterExpressionGroup; @@ -61,22 +65,21 @@ public override IRuleExpressionGroup VisitRuleSet(RuleSetEntity ruleSet) } public IPagedList ProcessFilter( - FilterExpression filter, - int pageIndex = 0, + FilterExpression filter, + int pageIndex = 0, int pageSize = int.MaxValue) { Guard.NotNull(filter, nameof(filter)); return ProcessFilter( new[] { filter }, - LogicalRuleOperator.And, - pageIndex, + LogicalRuleOperator.And, + pageIndex, pageSize); } - public IPagedList ProcessFilter( - int[] ruleSetIds, + int[] ruleSetIds, LogicalRuleOperator logicalOperator, int pageIndex = 0, int pageSize = int.MaxValue) @@ -85,6 +88,7 @@ public IPagedList ProcessFilter( var filters = ruleSetIds .Select(id => _ruleFactory.CreateExpressionGroup(id, this)) + .Where(x => x != null) .Cast() .ToArray(); @@ -105,7 +109,7 @@ public IPagedList ProcessFilter( } // TODO: really untracked? - var query = _rsCustomer.TableUntracked.Where(x => !x.Deleted); + var query = _rsCustomer.TableUntracked.Where(x => !x.Deleted && !x.IsSystemAccount); FilterExpressionGroup group = null; @@ -123,274 +127,316 @@ public IPagedList ProcessFilter( var predicate = group.ToPredicate(false); // Apply predicate to query - query = query.Where(predicate).Cast(); + query = query + .Where(predicate) + .Cast() + .OrderByDescending(c => c.CreatedOnUtc); return new PagedList(query, pageIndex, pageSize); } protected override IEnumerable LoadDescriptors() { - return new List + var stores = _services.StoreService.GetAllStores() + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.Name }) + .ToArray(); + + var vatNumberStatus = ((VatNumberStatus[])Enum.GetValues(typeof(VatNumberStatus))) + .Select(x => new RuleValueSelectListOption { Value = ((int)x).ToString(), Text = x.GetLocalizedEnum(_services.Localization) }) + .ToArray(); + + var taxDisplayTypes = ((TaxDisplayType[])Enum.GetValues(typeof(TaxDisplayType))) + .Select(x => new RuleValueSelectListOption { Value = ((int)x).ToString(), Text = x.GetLocalizedEnum(_services.Localization) }) + .ToArray(); + + var shippingStatus = ((ShippingStatus[])Enum.GetValues(typeof(ShippingStatus))) + .Select(x => new RuleValueSelectListOption { Value = ((int)x).ToString(), Text = x.GetLocalizedEnum(_services.Localization) }) + .ToArray(); + + var paymentStatus = ((PaymentStatus[])Enum.GetValues(typeof(PaymentStatus))) + .Select(x => new RuleValueSelectListOption { Value = ((int)x).ToString(), Text = x.GetLocalizedEnum(_services.Localization) }) + .ToArray(); + + var descriptors = new List { + new FilterDescriptor(x => x.Active) + { + Name = "Active", + DisplayName = T("Admin.Rules.FilterDescriptor.Active"), + RuleType = RuleType.Boolean + }, + new FilterDescriptor(x => x.Salutation) + { + Name = "Salutation", + DisplayName = T("Admin.Rules.FilterDescriptor.Salutation"), + RuleType = RuleType.String + }, + new FilterDescriptor(x => x.Title) + { + Name = "Title", + DisplayName = T("Admin.Rules.FilterDescriptor.Title"), + RuleType = RuleType.String + }, + new FilterDescriptor(x => x.Company) + { + Name = "Company", + DisplayName = T("Admin.Rules.FilterDescriptor.Company"), + RuleType = RuleType.String + }, + new FilterDescriptor(x => x.Gender) + { + Name = "Gender", + DisplayName = T("Admin.Rules.FilterDescriptor.Gender"), + RuleType = RuleType.String + }, + new FilterDescriptor(x => x.CustomerNumber) + { + Name = "CustomerNumber", + DisplayName = T("Admin.Rules.FilterDescriptor.CustomerNumber"), + RuleType = RuleType.String + }, + new AnyFilterDescriptor(x => x.CustomerRoleMappings, rm => rm.CustomerRoleId) + { + Name = "IsInCustomerRole", + DisplayName = T("Admin.Rules.FilterDescriptor.IsInCustomerRole"), + RuleType = RuleType.IntArray, + SelectList = new RemoteRuleValueSelectList("CustomerRole") { Multiple = true } + }, new FilterDescriptor(x => x.IsTaxExempt) { Name = "TaxExempt", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.TaxExempt"), - RuleType = RuleType.Boolean, - Constraints = new IRuleConstraint[0] + DisplayName = T("Admin.Rules.FilterDescriptor.TaxExempt"), + RuleType = RuleType.Boolean }, - new FilterDescriptor(x => x.BillingAddress.CountryId) + new FilterDescriptor(x => x.VatNumberStatusId) + { + Name = "VatNumberStatus", + DisplayName = T("Admin.Rules.FilterDescriptor.VatNumberStatus"), + RuleType = RuleType.Int, + SelectList = new LocalRuleValueSelectList(vatNumberStatus) + }, + new FilterDescriptor(x => x.TaxDisplayTypeId) + { + Name = "TaxDisplayType", + DisplayName = T("Admin.Rules.FilterDescriptor.TaxDisplayType"), + RuleType = RuleType.Int, + SelectList = new LocalRuleValueSelectList(taxDisplayTypes) + }, + new FilterDescriptor(x => x.TimeZoneId) + { + Name = "TimeZone", + DisplayName = T("Admin.Rules.FilterDescriptor.TimeZone"), + RuleType = RuleType.String + }, + new FilterDescriptor(x => x.LastUserAgent) + { + Name = "LastUserAgent", + DisplayName = T("Admin.Rules.FilterDescriptor.LastUserAgent"), + RuleType = RuleType.String + }, + new FilterDescriptor(x => x.BillingAddress != null ? x.BillingAddress.CountryId : 0) { Name = "BillingCountry", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.BillingCountry"), + DisplayName = T("Admin.Rules.FilterDescriptor.BillingCountry"), RuleType = RuleType.IntArray, - Constraints = new IRuleConstraint[0], SelectList = new RemoteRuleValueSelectList("Country") { Multiple = true } }, - new FilterDescriptor(x => x.ShippingAddress.CountryId) + new FilterDescriptor(x => x.ShippingAddress != null ? x.ShippingAddress.CountryId : 0) { Name = "ShippingCountry", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.ShippingCountry"), + DisplayName = T("Admin.Rules.FilterDescriptor.ShippingCountry"), RuleType = RuleType.IntArray, - Constraints = new IRuleConstraint[0], SelectList = new RemoteRuleValueSelectList("Country") { Multiple = true } }, - new FilterDescriptor(x => DbFunctions.DiffDays(x.LastActivityDateUtc, DateTime.UtcNow)) + new FilterDescriptor(x => x.ReturnRequests.Count()) { - Name = "LastActivityDays", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.LastActivityDays"), - RuleType = RuleType.NullableInt, - Constraints = new IRuleConstraint[0] + Name = "ReturnRequestCount", + DisplayName = T("Admin.Rules.FilterDescriptor.ReturnRequestCount"), + RuleType = RuleType.Int }, - new FilterDescriptor(x => x.Orders.Count(y => y.OrderStatusId == 30)) + + new FilterDescriptor(x => DbFunctions.DiffDays(x.LastActivityDateUtc, DateTime.UtcNow)) { - Name = "CompletedOrderCount", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.CompletedOrderCount"), - RuleType = RuleType.Int, - Constraints = new IRuleConstraint[0] + Name = "LastActivityDays", + DisplayName = T("Admin.Rules.FilterDescriptor.LastActivityDays"), + RuleType = RuleType.NullableInt }, - new FilterDescriptor(x => x.Orders.Count(y => y.OrderStatusId == 40)) + new FilterDescriptor(x => DbFunctions.DiffDays(x.LastLoginDateUtc, DateTime.UtcNow)) { - Name = "CancelledOrderCount", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.CancelledOrderCount"), - RuleType = RuleType.Int, - Constraints = new IRuleConstraint[0] + Name = "LastLoginDays", + DisplayName = T("Admin.Rules.FilterDescriptor.LastLoginDays"), + RuleType = RuleType.NullableInt }, - new FilterDescriptor(x => x.Orders.Count(y => y.OrderStatusId == 10 || y.OrderStatusId == 20)) + new FilterDescriptor(x => DbFunctions.DiffDays(x.LastForumVisit, DateTime.UtcNow)) { - Name = "NewOrderCount", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.NewOrderCount"), - RuleType = RuleType.Int, - Constraints = new IRuleConstraint[0] + Name = "LastForumVisitDays", + DisplayName = T("Admin.Rules.FilterDescriptor.LastForumVisit"), + RuleType = RuleType.NullableInt }, - new AnyFilterDescriptor(x => x.Orders.SelectMany(o => o.OrderItems), oi => oi.ProductId) + new FilterDescriptor(x => DbFunctions.DiffDays(x.CreatedOnUtc, DateTime.UtcNow)) { - Name = "HasPurchasedProduct", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.HasPurchasedProduct"), - RuleType = RuleType.IntArray, - Constraints = new IRuleConstraint[0] + Name = "CreatedDays", + DisplayName = T("Admin.Rules.FilterDescriptor.CreatedDays"), + RuleType = RuleType.NullableInt }, - new AllFilterDescriptor(x => x.Orders.SelectMany(o => o.OrderItems), oi => oi.ProductId) + new FilterDescriptor(x => DbFunctions.DiffDays(x.BirthDate, DateTime.UtcNow)) { - Name = "HasPurchasedAllProducts", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.HasPurchasedAllProducts"), - RuleType = RuleType.IntArray, - Constraints = new IRuleConstraint[0] + Name = "BirthDateDays", + DisplayName = T("Admin.Rules.FilterDescriptor.BirthDate"), + RuleType = RuleType.NullableInt }, - new TargetGroupFilterDescriptor(_ruleFactory, this) { Name = "RuleSet", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.RuleSet"), + DisplayName = T("Admin.Rules.FilterDescriptor.RuleSet"), RuleType = RuleType.Int, Operators = new[] { RuleOperator.IsEqualTo, RuleOperator.IsNotEqualTo }, - Constraints = new IRuleConstraint[0], SelectList = new RemoteRuleValueSelectList("TargetGroup") }, - // TODO: more ... - new FilterDescriptor(x => x.Active) + new AnyFilterDescriptor(x => x.Orders.Where(o => !o.Deleted), o => o.StoreId) { - Name = "Active", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.Active"), - RuleType = RuleType.Boolean, - Constraints = new IRuleConstraint[0] + Name = "OrderInStore", + DisplayName = T("Admin.Rules.FilterDescriptor.OrderInStore"), + GroupKey = "Admin.Orders", + RuleType = RuleType.IntArray, + SelectList = new LocalRuleValueSelectList(stores) { Multiple = true } }, - new FilterDescriptor(x => DbFunctions.DiffDays(x.LastLoginDateUtc, DateTime.UtcNow)) + new FilterDescriptor(x => x.Orders.Count(o => !o.Deleted && (o.OrderStatusId == 10 || o.OrderStatusId == 20))) { - Name = "LastLoginDays", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.LastLoginDays"), - RuleType = RuleType.NullableInt, - Constraints = new IRuleConstraint[0] + Name = "NewOrderCount", + DisplayName = T("Admin.Rules.FilterDescriptor.NewOrderCount"), + GroupKey = "Admin.Orders", + RuleType = RuleType.Int }, - new FilterDescriptor(x => DbFunctions.DiffDays(x.CreatedOnUtc, DateTime.UtcNow)) + new FilterDescriptor(x => x.Orders.Count(o => !o.Deleted && o.OrderStatusId == 30)) { - Name = "CreatedDays", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.CreatedDays"), - RuleType = RuleType.NullableInt, - Constraints = new IRuleConstraint[0] + Name = "CompletedOrderCount", + DisplayName = T("Admin.Rules.FilterDescriptor.CompletedOrderCount"), + GroupKey = "Admin.Orders", + RuleType = RuleType.Int }, - new FilterDescriptor(x => x.Salutation) + new FilterDescriptor(x => x.Orders.Count(o => !o.Deleted && o.OrderStatusId == 40)) { - Name = "Salutation", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.Salutation"), - RuleType = RuleType.String, - Constraints = new IRuleConstraint[0] + Name = "CancelledOrderCount", + DisplayName = T("Admin.Rules.FilterDescriptor.CancelledOrderCount"), + GroupKey = "Admin.Orders", + RuleType = RuleType.Int }, - new FilterDescriptor(x => x.Title) + new FilterDescriptor(x => DbFunctions.DiffDays(x.Orders.Where(o => !o.Deleted).Max(o => o.CreatedOnUtc), DateTime.UtcNow)) { - Name = "Title", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.Title"), - RuleType = RuleType.String, - Constraints = new IRuleConstraint[0] + Name = "LastOrderDateDays", + DisplayName = T("Admin.Rules.FilterDescriptor.LastOrderDateDays"), + GroupKey = "Admin.Orders", + RuleType = RuleType.NullableInt }, - new FilterDescriptor(x => x.Company) + new AnyFilterDescriptor(x => x.Orders.Where(o => !o.Deleted), o => o.OrderTotal) { - Name = "Company", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.Company"), - RuleType = RuleType.String, - Constraints = new IRuleConstraint[0] + Name = "OrderTotal", + DisplayName = T("Admin.Rules.FilterDescriptor.OrderTotal"), + GroupKey = "Admin.Orders", + RuleType = RuleType.Money }, - new FilterDescriptor(x => x.CustomerNumber) + new AnyFilterDescriptor(x => x.Orders.Where(o => !o.Deleted), o => o.OrderSubtotalInclTax) { - Name = "CustomerNumber", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.CustomerNumber"), - RuleType = RuleType.String, - Constraints = new IRuleConstraint[0] + Name = "OrderSubtotalInclTax", + DisplayName = T("Admin.Rules.FilterDescriptor.OrderSubtotalInclTax"), + GroupKey = "Admin.Orders", + RuleType = RuleType.Money }, - - // TODO - //new FilterDescriptor(x => DbFunctions.DiffDays(x.BirthDate, DateTime.UtcNow)) - //{ - // Name = "BirthDate", - // DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.BirthDate"), - // RuleType = RuleType.NullableInt, - // Constraints = new IRuleConstraint[0] - //}, - - // Moved from Customer attrs > Gender, VatNumberStatusId, TimeZoneId, TaxDisplayTypeId - new FilterDescriptor(x => x.Gender) + new AnyFilterDescriptor(x => x.Orders.Where(o => !o.Deleted), o => o.OrderSubtotalExclTax) { - Name = "Gender", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.Gender"), - RuleType = RuleType.String, - Constraints = new IRuleConstraint[0] + Name = "OrderSubtotalExclTax", + DisplayName = T("Admin.Rules.FilterDescriptor.OrderSubtotalExclTax"), + GroupKey = "Admin.Orders", + RuleType = RuleType.Money }, - - // TODO: handle status - new FilterDescriptor(x => x.VatNumberStatusId) + new AnyFilterDescriptor(x => x.Orders.Where(o => !o.Deleted), o => o.ShippingStatusId) { - Name = "VatNumberStatusId", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.VatNumberStatusId"), + Name = "ShippingStatus", + DisplayName = T("Admin.Rules.FilterDescriptor.ShippingStatus"), + GroupKey = "Admin.Orders", RuleType = RuleType.Int, - Constraints = new IRuleConstraint[0] + SelectList = new LocalRuleValueSelectList(shippingStatus) }, - new FilterDescriptor(x => x.TimeZoneId) + new AnyFilterDescriptor(x => x.Orders.Where(o => !o.Deleted), o => o.PaymentStatusId) { - Name = "TimeZoneId", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.TimeZoneId"), - RuleType = RuleType.String, - Constraints = new IRuleConstraint[0] - }, - new FilterDescriptor(x => x.TaxDisplayTypeId) - { - Name = "TaxDisplayTypeId", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.TaxDisplayTypeId"), + Name = "PaymentStatus", + DisplayName = T("Admin.Rules.FilterDescriptor.PaymentStatus"), + GroupKey = "Admin.Orders", RuleType = RuleType.Int, - Constraints = new IRuleConstraint[0] + SelectList = new LocalRuleValueSelectList(paymentStatus) }, - - // TODO: later - //new FilterDescriptor(x => DbFunctions.DiffDays(x.LastForumVisit, DateTime.UtcNow)) - //{ - // Name = "LastForumVisit", - // DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.LastForumVisit"), - // RuleType = RuleType.NullableInt, - // Constraints = new IRuleConstraint[0] - //}, - - // TODO: must be in order table - //new FilterDescriptor(x => x.LastUserAgent) - //{ - // Name = "LastUserAgent", - // DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.LastUserAgent"), - // RuleType = RuleType.String, - // Constraints = new IRuleConstraint[0] - //}, - - // customer roles - new AllFilterDescriptor(x => x.CustomerRoles, cr => cr.Id) + new AnyFilterDescriptor(x => x.Orders.Where(o => !o.Deleted).SelectMany(o => o.OrderItems), oi => oi.ProductId) { - Name = "IsInCustomerRole", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.IsInCustomerRole"), + Name = "HasPurchasedProduct", + DisplayName = T("Admin.Rules.FilterDescriptor.HasPurchasedProduct"), + GroupKey = "Admin.Orders", RuleType = RuleType.IntArray, - Constraints = new IRuleConstraint[0] + SelectList = new RemoteRuleValueSelectList("Product") { Multiple = true } }, - - // Orders > StoreId, LastOrderDate (CreatedOnUtc), AcceptThirdPartyEmailHandOver, OrderTotal, OrderSubtotalInclTax, OrderSubtotalExclTax - // ShippingStatusId, PaymentStatusId, (TODO ???) - // PaymentMethodSystemName, ShippingRateComputationMethodSystemName - // CustomerCurrencyCode, CustomerLanguageId, CustomerTaxDisplayTypeId (TODO: Really??? from customer, order or both?) - - new AnyFilterDescriptor(x => x.Orders, o => o.StoreId) + new AllFilterDescriptor(x => x.Orders.Where(o => !o.Deleted).SelectMany(o => o.OrderItems), oi => oi.ProductId) { - Name = "OrderCountInStore", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.OrderCountInStore"), + Name = "HasPurchasedAllProducts", + DisplayName = T("Admin.Rules.FilterDescriptor.HasPurchasedAllProducts"), + GroupKey = "Admin.Orders", RuleType = RuleType.IntArray, - Constraints = new IRuleConstraint[0] + SelectList = new RemoteRuleValueSelectList("Product") { Multiple = true } }, - new FilterDescriptor(x => DbFunctions.DiffDays(x.Orders.Max(y => y.CreatedOnUtc), DateTime.UtcNow)) - { - Name = "LastOrderDateDays", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.LastOrderDateDays"), - RuleType = RuleType.NullableInt, - Constraints = new IRuleConstraint[0] - }, - new AnyFilterDescriptor(x => x.Orders, o => o.AcceptThirdPartyEmailHandOver) + new AnyFilterDescriptor(x => x.Orders.Where(o => !o.Deleted), o => o.AcceptThirdPartyEmailHandOver) { Name = "AcceptThirdPartyEmailHandOver", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.AcceptThirdPartyEmailHandOver"), - RuleType = RuleType.Boolean, - Constraints = new IRuleConstraint[0] + DisplayName = T("Admin.Rules.FilterDescriptor.AcceptThirdPartyEmailHandOver"), + GroupKey = "Admin.Orders", + RuleType = RuleType.Boolean }, - - // TODO: customer - new FilterDescriptor(x => x.OrderTotal) + new AnyFilterDescriptor(x => x.Orders.Where(o => !o.Deleted), o => o.CustomerCurrencyCode) { - Name = "OrderTotal", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.OrderTotal"), - RuleType = RuleType.Money, - Constraints = new IRuleConstraint[0] + Name = "CurrencyCode", + DisplayName = T("Admin.Rules.FilterDescriptor.Currency"), + GroupKey = "Admin.Orders", + RuleType = RuleType.String, + SelectList = new RemoteRuleValueSelectList("Currency") }, - new FilterDescriptor(x => x.OrderSubtotalInclTax) + new AnyFilterDescriptor(x => x.Orders.Where(o => !o.Deleted), o => o.CustomerLanguageId) { - Name = "OrderSubtotalInclTax", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.OrderSubtotalInclTax"), - RuleType = RuleType.Money, - Constraints = new IRuleConstraint[0] + Name = "OrderLanguage", + DisplayName = T("Admin.Rules.FilterDescriptor.Language"), + GroupKey = "Admin.Orders", + RuleType = RuleType.Int, + SelectList = new RemoteRuleValueSelectList("Language") }, - new FilterDescriptor(x => x.OrderSubtotalExclTax) + new AnyFilterDescriptor(x => x.Orders.Where(o => !o.Deleted), o => o.PaymentMethodSystemName) { - Name = "OrderSubtotalExclTax", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.OrderSubtotalExclTax"), - RuleType = RuleType.Money, - Constraints = new IRuleConstraint[0] + Name = "PaymentMethod", + DisplayName = T("Admin.Rules.FilterDescriptor.PaidBy"), + GroupKey = "Admin.Orders", + RuleType = RuleType.String, + SelectList = new RemoteRuleValueSelectList("PaymentMethod") }, - new FilterDescriptor(x => x.PaymentMethodSystemName) + new AnyFilterDescriptor(x => x.Orders.Where(o => !o.Deleted), o => o.ShippingMethod) { - Name = "PaymentMethodSystemName", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.PaymentMethodSystemName"), + Name = "ShippingMethod", + DisplayName = T("Admin.Rules.FilterDescriptor.ShippingMethod"), + GroupKey = "Admin.Orders", RuleType = RuleType.String, - Constraints = new IRuleConstraint[0] + SelectList = new RemoteRuleValueSelectList("ShippingMethod") }, - new FilterDescriptor(x => x.ShippingRateComputationMethodSystemName) + new AnyFilterDescriptor(x => x.Orders.Where(o => !o.Deleted), o => o.ShippingRateComputationMethodSystemName) { - Name = "ShippingRateComputationMethodSystemName", - DisplayName = _services.Localization.GetResource("Admin.Rules.FilterDescriptor.ShippingRateComputationMethodSystemName"), + Name = "ShippingRateComputationMethod", + DisplayName = T("Admin.Rules.FilterDescriptor.ShippingRateComputationMethod"), + GroupKey = "Admin.Orders", RuleType = RuleType.String, - Constraints = new IRuleConstraint[0] + SelectList = new RemoteRuleValueSelectList("ShippingRateComputationMethod") }, }; + + descriptors + .Where(x => x.RuleType == RuleType.Money) + .Each(x => x.Metadata["postfix"] = _services.StoreContext.CurrentStore.PrimaryStoreCurrency.CurrencyCode); + + return descriptors; } } } diff --git a/src/Libraries/SmartStore.Services/Customers/Tasks/DeleteGuestsTask.cs b/src/Libraries/SmartStore.Services/Customers/Tasks/DeleteGuestsTask.cs new file mode 100644 index 0000000000..881138aaa0 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Customers/Tasks/DeleteGuestsTask.cs @@ -0,0 +1,32 @@ +using System; +using SmartStore.Core.Domain.Common; +using SmartStore.Services.Tasks; + +namespace SmartStore.Services.Customers +{ + /// + /// Represents a task for deleting guest customers. + /// + public partial class DeleteGuestsTask : ITask + { + private readonly ICustomerService _customerService; + private readonly CommonSettings _commonSettings; + + public DeleteGuestsTask( + ICustomerService customerService, + CommonSettings commonSettings) + { + _customerService = customerService; + _commonSettings = commonSettings; + } + + public void Execute(TaskExecutionContext ctx) + { + Guard.NotNegative(_commonSettings.MaxGuestsRegistrationAgeInMinutes, nameof(_commonSettings.MaxGuestsRegistrationAgeInMinutes)); + + var registrationTo = DateTime.UtcNow.AddMinutes(-_commonSettings.MaxGuestsRegistrationAgeInMinutes); + + _customerService.DeleteGuestCustomers(null, registrationTo, true); + } + } +} diff --git a/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvConfiguration.cs b/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvConfiguration.cs index 1680cf2d55..7ac7deff8f 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvConfiguration.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvConfiguration.cs @@ -4,293 +4,275 @@ namespace SmartStore.Services.DataExchange.Csv { - /// - /// Specifies the action to take when a parsing error has occured. - /// - public enum ParseErrorAction - { - /// - /// Raises the event. - /// - RaiseEvent = 0, - - /// - /// Tries to advance to next line. - /// - AdvanceToNextLine = 1, - - /// - /// Throws an exception. - /// - ThrowException = 2, - } - - /// - /// Specifies the action to take when a field is missing. - /// - public enum MissingFieldAction - { - /// - /// Treat as a parsing error. - /// - ParseError = 0, - - /// - /// Replaces by an empty value. - /// - ReplaceByEmpty = 1, - - /// - /// Replaces by a null value (). - /// - ReplaceByNull = 2, - } - - public class CsvConfiguration - { - private char _delimiter; - private char _escape; - private char _quote; - private string _quoteString; - private char[] _quotableChars; - - public CsvConfiguration() - { - _escape = '"'; - _delimiter = ';'; - _quote = '"'; - _quoteString = new String(new char[] { _escape, _quote }); - - Comment = '#'; - HasHeaders = true; - SkipEmptyLines = true; - SupportsMultiline = true; - DefaultHeaderName = "Column"; - - BuildQuotableChars(); + /// + /// Specifies the action to take when a parsing error has occured. + /// + public enum ParseErrorAction + { + /// + /// Raises the event. + /// + RaiseEvent = 0, + + /// + /// Tries to advance to next line. + /// + AdvanceToNextLine = 1, + + /// + /// Throws an exception. + /// + ThrowException = 2, + } + + /// + /// Specifies the action to take when a field is missing. + /// + public enum MissingFieldAction + { + /// + /// Treat as a parsing error. + /// + ParseError = 0, + + /// + /// Replaces by an empty value. + /// + ReplaceByEmpty = 1, + + /// + /// Replaces by a null value (). + /// + ReplaceByNull = 2, + } + + public class CsvConfiguration + { + private char _delimiter; + private char _escape; + private char _quote; + private string _quoteString; + private char[] _quotableChars; + + public CsvConfiguration() + { + _escape = '"'; + _delimiter = ';'; + _quote = '"'; + _quoteString = new String(new char[] { _escape, _quote }); + + Comment = '#'; + HasHeaders = true; + SkipEmptyLines = true; + SupportsMultiline = true; + DefaultHeaderName = "Column"; + + BuildQuotableChars(); } - private void BuildQuotableChars() - { - _quotableChars = new char[] { '\r', '\n', _delimiter, _quote }; + private void BuildQuotableChars() + { + _quotableChars = new char[] { '\r', '\n', _delimiter, _quote }; } - internal char[] QuotableChars - { - get { return _quotableChars; } - } - - /// - /// Gets an Excel friendly configuration where the result can be directly edited by Excel - /// - public static CsvConfiguration ExcelFriendlyConfiguration - { - get - { - return new CsvConfiguration - { - Delimiter = ';', - Quote = '"', - Escape = '"', - SupportsMultiline = false - }; - } - } - - /// - /// Gets an array with preset characters - /// - public static char[] PresetCharacters - { - get { return new char[] { '\n', '\r', '\0' }; } - } - - /// - /// Gets the comment character indicating that a line is commented out (default: #). - /// - /// The comment character indicating that a line is commented out. - public char Comment - { - get; - set; - } - - /// - /// Gets the escape character letting insert quotation characters inside a quoted field (default: "). - /// - /// The escape character letting insert quotation characters inside a quoted field. - public char Escape - { - get { return _escape; } - set - { - if (value == _escape) - return; - - if (PresetCharacters.Contains(value)) - { - throw new SmartException("'{0}' is not a valid escape char.".FormatInvariant(value)); - } - if (value == _delimiter) - { - throw new SmartException("Escape and delimiter chars cannot be equal in CSV files."); - } - - _escape = value; - _quoteString = new String(new char[] { _escape, _quote }); - } - } - - /// - /// Gets the delimiter character separating each field (default: ;). - /// - /// The delimiter character separating each field. - public char Delimiter - { - get { return _delimiter; } - set - { - if (value == _delimiter) - return; - - if (PresetCharacters.Contains(value)) - { - throw new SmartException("'{0}' is not a valid delimiter char.".FormatInvariant(value)); - } - if (value == _quote) - { - throw new SmartException("Quote and delimiter chars cannot be equal in CSV files."); - } - - _delimiter = value; - BuildQuotableChars(); - - } - } - - /// - /// Gets the quotation character wrapping every field (default: "). - /// - /// The quotation character wrapping every field. - public char Quote - { - get { return _quote; } - set - { - if (value == _quote) - return; - - if (PresetCharacters.Contains(value)) - { - throw new SmartException("'{0}' is not a valid quote char.".FormatInvariant(value)); - } - if (value == _delimiter) - { - throw new SmartException("Quote and delimiter chars cannot be equal in CSV files."); - } - - _quote = value; - _quoteString = new String(new char[] { _escape, _quote }); - BuildQuotableChars(); - } - } - - /// - /// Gets the concatenation of escape and quote char - /// - [JsonIgnore] - public string QuoteString - { - get - { - return _quoteString; - } - } - - public bool QuoteAllFields - { - get; - set; - } - - /// - /// Indicates if field names are located on the first non commented line (default: true). - /// - /// if field names are located on the first non commented line, otherwise, . - public bool HasHeaders - { - get; - set; - } - - /// - /// Indicates if spaces at the start and end of a field are trimmed (default: false). - /// - /// if spaces at the start and end of a field are trimmed, otherwise, . - public bool TrimValues - { - get; - set; - } - - /// - /// Contains the value which denotes a DbNull-value. - /// - public string NullValue - { - get; - set; - } - - /// - /// Gets or sets the default action to take when a parsing error has occured. - /// - /// The default action to take when a parsing error has occured. - public ParseErrorAction DefaultParseErrorAction - { - get; - set; - } - - /// - /// Gets or sets the action to take when a field is missing. - /// - /// The action to take when a field is missing. - public MissingFieldAction MissingFieldAction - { - get; - set; - } - - /// - /// Gets or sets a value indicating if the reader supports multiline fields (default: true). - /// - /// A value indicating if the reader supports multiline field. - public bool SupportsMultiline - { - get; - set; - } - - /// - /// Gets or sets a value indicating if the reader will skip empty lines (default: true). - /// - /// A value indicating if the reader will skip empty lines. - public bool SkipEmptyLines - { - get; - set; - } - - /// - /// Gets or sets the default header name when it is an empty string or only whitespaces (default: Column). - /// The header index will be appended to the specified name. - /// - /// The default header name when it is an empty string or only whitespaces. - public string DefaultHeaderName - { - get; - set; - } - } + internal char[] QuotableChars => _quotableChars; + + /// + /// Gets an Excel friendly configuration where the result can be directly edited by Excel + /// + public static CsvConfiguration ExcelFriendlyConfiguration => new CsvConfiguration + { + Delimiter = ';', + Quote = '"', + Escape = '"', + SupportsMultiline = false + }; + + /// + /// Gets an array with preset characters + /// + public static char[] PresetCharacters => new char[] { '\n', '\r', '\0' }; + + /// + /// Gets the comment character indicating that a line is commented out (default: #). + /// + /// The comment character indicating that a line is commented out. + public char Comment + { + get; + set; + } + + /// + /// Gets the escape character letting insert quotation characters inside a quoted field (default: "). + /// + /// The escape character letting insert quotation characters inside a quoted field. + public char Escape + { + get => _escape; + set + { + if (value == _escape) + return; + + if (PresetCharacters.Contains(value)) + { + throw new SmartException("'{0}' is not a valid escape char.".FormatInvariant(value)); + } + if (value == _delimiter) + { + throw new SmartException("Escape and delimiter chars cannot be equal in CSV files."); + } + + _escape = value; + _quoteString = new String(new char[] { _escape, _quote }); + } + } + + /// + /// Gets the delimiter character separating each field (default: ;). + /// + /// The delimiter character separating each field. + public char Delimiter + { + get => _delimiter; + set + { + if (value == _delimiter) + return; + + if (PresetCharacters.Contains(value)) + { + throw new SmartException("'{0}' is not a valid delimiter char.".FormatInvariant(value)); + } + if (value == _quote) + { + throw new SmartException("Quote and delimiter chars cannot be equal in CSV files."); + } + + _delimiter = value; + BuildQuotableChars(); + + } + } + + /// + /// Gets the quotation character wrapping every field (default: "). + /// + /// The quotation character wrapping every field. + public char Quote + { + get => _quote; + set + { + if (value == _quote) + return; + + if (PresetCharacters.Contains(value)) + { + throw new SmartException("'{0}' is not a valid quote char.".FormatInvariant(value)); + } + if (value == _delimiter) + { + throw new SmartException("Quote and delimiter chars cannot be equal in CSV files."); + } + + _quote = value; + _quoteString = new String(new char[] { _escape, _quote }); + BuildQuotableChars(); + } + } + + /// + /// Gets the concatenation of escape and quote char + /// + [JsonIgnore] + public string QuoteString => _quoteString; + + public bool QuoteAllFields + { + get; + set; + } + + /// + /// Indicates if field names are located on the first non commented line (default: true). + /// + /// if field names are located on the first non commented line, otherwise, . + public bool HasHeaders + { + get; + set; + } + + /// + /// Indicates if spaces at the start and end of a field are trimmed (default: false). + /// + /// if spaces at the start and end of a field are trimmed, otherwise, . + public bool TrimValues + { + get; + set; + } + + /// + /// Contains the value which denotes a DbNull-value. + /// + public string NullValue + { + get; + set; + } + + /// + /// Gets or sets the default action to take when a parsing error has occured. + /// + /// The default action to take when a parsing error has occured. + public ParseErrorAction DefaultParseErrorAction + { + get; + set; + } + + /// + /// Gets or sets the action to take when a field is missing. + /// + /// The action to take when a field is missing. + public MissingFieldAction MissingFieldAction + { + get; + set; + } + + /// + /// Gets or sets a value indicating if the reader supports multiline fields (default: true). + /// + /// A value indicating if the reader supports multiline field. + public bool SupportsMultiline + { + get; + set; + } + + /// + /// Gets or sets a value indicating if the reader will skip empty lines (default: true). + /// + /// A value indicating if the reader will skip empty lines. + public bool SkipEmptyLines + { + get; + set; + } + + /// + /// Gets or sets the default header name when it is an empty string or only whitespaces (default: Column). + /// The header index will be appended to the specified name. + /// + /// The default header name when it is an empty string or only whitespaces. + public string DefaultHeaderName + { + get; + set; + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvConfigurationConverter.cs b/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvConfigurationConverter.cs index 4a81792a19..1d99e78f11 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvConfigurationConverter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvConfigurationConverter.cs @@ -5,61 +5,61 @@ namespace SmartStore.Services.DataExchange.Csv { - public class CsvConfigurationConverter : TypeConverterBase - { - public CsvConfigurationConverter() - : base(typeof(object)) - { - } + public class CsvConfigurationConverter : DefaultTypeConverter + { + public CsvConfigurationConverter() + : base(typeof(object)) + { + } - public override bool CanConvertFrom(Type type) - { - return type == typeof(string); - } + public override bool CanConvertFrom(Type type) + { + return type == typeof(string); + } - public override bool CanConvertTo(Type type) - { - return type == typeof(string); - } + public override bool CanConvertTo(Type type) + { + return type == typeof(string); + } - public override object ConvertFrom(CultureInfo culture, object value) - { - if (value is string) - { - return JsonConvert.DeserializeObject((string)value); - } + public override object ConvertFrom(CultureInfo culture, object value) + { + if (value is string) + { + return JsonConvert.DeserializeObject((string)value); + } - return base.ConvertFrom(culture, value); - } + return base.ConvertFrom(culture, value); + } - public T ConvertFrom(string value) - { - if (value.HasValue()) - return (T)ConvertFrom(CultureInfo.InvariantCulture, value); + public T ConvertFrom(string value) + { + if (value.HasValue()) + return (T)ConvertFrom(CultureInfo.InvariantCulture, value); - return default(T); - } + return default(T); + } - public override object ConvertTo(CultureInfo culture, string format, object value, Type to) - { - if (to == typeof(string)) - { - if (value is CsvConfiguration) - { - return JsonConvert.SerializeObject(value); - } - else - { - return string.Empty; - } - } + public override object ConvertTo(CultureInfo culture, string format, object value, Type to) + { + if (to == typeof(string)) + { + if (value is CsvConfiguration) + { + return JsonConvert.SerializeObject(value); + } + else + { + return string.Empty; + } + } - return base.ConvertTo(culture, format, value, to); - } + return base.ConvertTo(culture, format, value, to); + } - public string ConvertTo(object value) - { - return (string)ConvertTo(CultureInfo.InvariantCulture, null, value, typeof(string)); - } - } + public string ConvertTo(object value) + { + return (string)ConvertTo(CultureInfo.InvariantCulture, null, value, typeof(string)); + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvDataReader.cs b/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvDataReader.cs index 0a172ff592..664fb186fe 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvDataReader.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvDataReader.cs @@ -15,374 +15,314 @@ namespace SmartStore.Services.DataExchange.Csv { - /// - /// Represents a reader that provides fast, non-cached, forward-only access to CSV data. - /// - public class CsvDataReader : DisposableObject, IDataReader, IEnumerable - { - private readonly CsvReader _csv; - private readonly IDataReader _reader; - - /// - /// Initializes a new instance of the CsvDataReader class. - /// - /// A pointing to the CSV file. - public CsvDataReader(TextReader reader) - : this(reader, new CsvConfiguration()) - { - } - - /// - /// Initializes a new instance of the CsvDataReader class. - /// - /// A pointing to the CSV file. - public CsvDataReader(TextReader reader, CsvConfiguration configuration) - { - Guard.NotNull(reader, nameof(reader)); - Guard.NotNull(configuration, nameof(configuration)); - - this.Configuration = configuration; - - _csv = new CsvReader( - reader, - configuration.HasHeaders, - configuration.Delimiter, - configuration.Quote, - configuration.Escape, - configuration.Comment, - configuration.TrimValues ? ValueTrimmingOptions.All : ValueTrimmingOptions.None, - 4096, - configuration.NullValue) - { - SupportsMultiline = configuration.SupportsMultiline, - SkipEmptyLines = configuration.SkipEmptyLines, - DefaultHeaderName = configuration.DefaultHeaderName, - DefaultParseErrorAction = (LumenWorks.Framework.IO.Csv.ParseErrorAction)((int)configuration.DefaultParseErrorAction), - MissingFieldAction = (LumenWorks.Framework.IO.Csv.MissingFieldAction)((int)configuration.MissingFieldAction) - }; - - _reader = _csv; - } - - public CsvConfiguration Configuration - { - get; - private set; - } - - #region Public wrapper members - - /// - /// Gets the field headers. - /// - /// The field headers or an empty array if headers are not supported. - /// - /// The instance has been disposed of. - /// - public string[] GetFieldHeaders() - { - return _csv.GetFieldHeaders(); - } - - public bool EndOfStream - { - get - { - return _csv.EndOfStream; - } - } - - /// - /// Gets the current row index in the CSV file (0-based). - /// - /// The current row index in the CSV file. - public long CurrentRowIndex - { - get - { - return _csv.CurrentRecordIndex; - } - } - - public void CopyCurrentRowTo(string[] array) - { - _csv.CopyCurrentRecordTo(array); - } - - /// - /// Gets the current row's raw CSV data. - /// - /// The current raw CSV data. - public string GetRawData() - { - return _csv.GetCurrentRawData(); - } - - /// - /// Gets the field with the specified name. must be . - /// - /// - /// The field with the specified name. - /// - /// - /// is or an empty string. - /// - /// - /// The CSV does not have headers ( property is ). - /// - /// - /// not found. - /// - /// - /// The CSV appears to be corrupt at the current position. - /// - /// - /// The instance has been disposed of. - /// - public string this[string field] - { - get - { - return _csv[field]; - } - } - - /// - /// Gets the field at the specified index. - /// - /// The field at the specified index. - /// - /// must be included in [0, [. - /// - /// - /// No record read yet. Call ReadLine() first. - /// - /// - /// The CSV appears to be corrupt at the current position. - /// - /// - /// The instance has been disposed of. - /// - public virtual string this[int index] - { - get - { - return _csv[index]; - } - } - - #endregion - - #region IDataReader (implicit) - - public int FieldCount - { - get - { - return _reader.FieldCount; - } - } - - public bool Read() - { - return _csv.ReadNextRecord(); - } - - public int GetOrdinal(string name) - { - return _reader.GetOrdinal(name); - } - - #endregion - - #region IDataReader (explicit) - - object IDataRecord.this[string name] - { - get - { - return _reader[name]; - } - } - - object IDataRecord.this[int i] - { - get - { - return _reader[i]; - } - } - - int IDataReader.Depth - { - get - { - return _reader.Depth; - } - } - - bool IDataReader.IsClosed - { - get - { - return _reader.IsClosed; - } - } - - int IDataReader.RecordsAffected - { - get - { - return _reader.RecordsAffected; - } - } - - void IDataReader.Close() - { - _reader.Close(); - } - - bool IDataRecord.GetBoolean(int i) - { - return _reader.GetBoolean(i); - } - - byte IDataRecord.GetByte(int i) - { - return _reader.GetByte(i); - } - - long IDataRecord.GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) - { - return _reader.GetBytes(i, fieldOffset, buffer, bufferoffset, length); - } - - char IDataRecord.GetChar(int i) - { - return _reader.GetChar(i); - } - - long IDataRecord.GetChars(int i, long fieldOffset, char[] buffer, int bufferoffset, int length) - { - return _reader.GetChars(i, fieldOffset, buffer, bufferoffset, length); - } - - IDataReader IDataRecord.GetData(int i) - { - return _reader.GetData(i); - } - - string IDataRecord.GetDataTypeName(int i) - { - return _reader.GetDataTypeName(i); - } - - DateTime IDataRecord.GetDateTime(int i) - { - return _reader.GetDateTime(i); - } - - decimal IDataRecord.GetDecimal(int i) - { - return _reader.GetDecimal(i); - } - - double IDataRecord.GetDouble(int i) - { - return _reader.GetDouble(i); - } - - Type IDataRecord.GetFieldType(int i) - { - return _reader.GetFieldType(i); - } - - float IDataRecord.GetFloat(int i) - { - return _reader.GetFloat(i); - } - - Guid IDataRecord.GetGuid(int i) - { - return _reader.GetGuid(i); - } - - short IDataRecord.GetInt16(int i) - { - return _reader.GetInt16(i); - } - - int IDataRecord.GetInt32(int i) - { - return _reader.GetInt32(i); - } - - long IDataRecord.GetInt64(int i) - { - return _reader.GetInt64(i); - } - - string IDataRecord.GetName(int i) - { - return _reader.GetName(i); - } - - DataTable IDataReader.GetSchemaTable() - { - return _reader.GetSchemaTable(); - } - - string IDataRecord.GetString(int i) - { - return _reader.GetString(i); - } - - object IDataRecord.GetValue(int i) - { - return _reader.GetGuid(i); - } - - int IDataRecord.GetValues(object[] values) - { - return _reader.GetValues(values); - } - - bool IDataRecord.IsDBNull(int i) - { - return _reader.IsDBNull(i); - } - - bool IDataReader.NextResult() - { - return _reader.NextResult(); - } - - #endregion - - #region IEnumerable - - IEnumerator IEnumerable.GetEnumerator() - { - return _csv.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return _csv.GetEnumerator(); - } - - #endregion - - #region Disposable - - protected override void OnDispose(bool disposing) - { - if (disposing) - { - _csv.Dispose(); - } - } - - #endregion - } + /// + /// Represents a reader that provides fast, non-cached, forward-only access to CSV data. + /// + public class CsvDataReader : DisposableObject, IDataReader, IEnumerable + { + private readonly CsvReader _csv; + private readonly IDataReader _reader; + + /// + /// Initializes a new instance of the CsvDataReader class. + /// + /// A pointing to the CSV file. + public CsvDataReader(TextReader reader) + : this(reader, new CsvConfiguration()) + { + } + + /// + /// Initializes a new instance of the CsvDataReader class. + /// + /// A pointing to the CSV file. + public CsvDataReader(TextReader reader, CsvConfiguration configuration) + { + Guard.NotNull(reader, nameof(reader)); + Guard.NotNull(configuration, nameof(configuration)); + + this.Configuration = configuration; + + _csv = new CsvReader( + reader, + configuration.HasHeaders, + configuration.Delimiter, + configuration.Quote, + configuration.Escape, + configuration.Comment, + configuration.TrimValues ? ValueTrimmingOptions.All : ValueTrimmingOptions.None, + 4096, + configuration.NullValue) + { + SupportsMultiline = configuration.SupportsMultiline, + SkipEmptyLines = configuration.SkipEmptyLines, + DefaultHeaderName = configuration.DefaultHeaderName, + DefaultParseErrorAction = (LumenWorks.Framework.IO.Csv.ParseErrorAction)((int)configuration.DefaultParseErrorAction), + MissingFieldAction = (LumenWorks.Framework.IO.Csv.MissingFieldAction)((int)configuration.MissingFieldAction) + }; + + _reader = _csv; + } + + public CsvConfiguration Configuration + { + get; + private set; + } + + #region Public wrapper members + + /// + /// Gets the field headers. + /// + /// The field headers or an empty array if headers are not supported. + /// + /// The instance has been disposed of. + /// + public string[] GetFieldHeaders() + { + return _csv.GetFieldHeaders(); + } + + public bool EndOfStream => _csv.EndOfStream; + + /// + /// Gets the current row index in the CSV file (0-based). + /// + /// The current row index in the CSV file. + public long CurrentRowIndex => _csv.CurrentRecordIndex; + + public void CopyCurrentRowTo(string[] array) + { + _csv.CopyCurrentRecordTo(array); + } + + /// + /// Gets the current row's raw CSV data. + /// + /// The current raw CSV data. + public string GetRawData() + { + return _csv.GetCurrentRawData(); + } + + /// + /// Gets the field with the specified name. must be . + /// + /// + /// The field with the specified name. + /// + /// + /// is or an empty string. + /// + /// + /// The CSV does not have headers ( property is ). + /// + /// + /// not found. + /// + /// + /// The CSV appears to be corrupt at the current position. + /// + /// + /// The instance has been disposed of. + /// + public string this[string field] => _csv[field]; + + /// + /// Gets the field at the specified index. + /// + /// The field at the specified index. + /// + /// must be included in [0, [. + /// + /// + /// No record read yet. Call ReadLine() first. + /// + /// + /// The CSV appears to be corrupt at the current position. + /// + /// + /// The instance has been disposed of. + /// + public virtual string this[int index] => _csv[index]; + + #endregion + + #region IDataReader (implicit) + + public int FieldCount => _reader.FieldCount; + + public bool Read() + { + return _csv.ReadNextRecord(); + } + + public int GetOrdinal(string name) + { + return _reader.GetOrdinal(name); + } + + #endregion + + #region IDataReader (explicit) + + object IDataRecord.this[string name] => _reader[name]; + + object IDataRecord.this[int i] => _reader[i]; + + int IDataReader.Depth => _reader.Depth; + + bool IDataReader.IsClosed => _reader.IsClosed; + + int IDataReader.RecordsAffected => _reader.RecordsAffected; + + void IDataReader.Close() + { + _reader.Close(); + } + + bool IDataRecord.GetBoolean(int i) + { + return _reader.GetBoolean(i); + } + + byte IDataRecord.GetByte(int i) + { + return _reader.GetByte(i); + } + + long IDataRecord.GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) + { + return _reader.GetBytes(i, fieldOffset, buffer, bufferoffset, length); + } + + char IDataRecord.GetChar(int i) + { + return _reader.GetChar(i); + } + + long IDataRecord.GetChars(int i, long fieldOffset, char[] buffer, int bufferoffset, int length) + { + return _reader.GetChars(i, fieldOffset, buffer, bufferoffset, length); + } + + IDataReader IDataRecord.GetData(int i) + { + return _reader.GetData(i); + } + + string IDataRecord.GetDataTypeName(int i) + { + return _reader.GetDataTypeName(i); + } + + DateTime IDataRecord.GetDateTime(int i) + { + return _reader.GetDateTime(i); + } + + decimal IDataRecord.GetDecimal(int i) + { + return _reader.GetDecimal(i); + } + + double IDataRecord.GetDouble(int i) + { + return _reader.GetDouble(i); + } + + Type IDataRecord.GetFieldType(int i) + { + return _reader.GetFieldType(i); + } + + float IDataRecord.GetFloat(int i) + { + return _reader.GetFloat(i); + } + + Guid IDataRecord.GetGuid(int i) + { + return _reader.GetGuid(i); + } + + short IDataRecord.GetInt16(int i) + { + return _reader.GetInt16(i); + } + + int IDataRecord.GetInt32(int i) + { + return _reader.GetInt32(i); + } + + long IDataRecord.GetInt64(int i) + { + return _reader.GetInt64(i); + } + + string IDataRecord.GetName(int i) + { + return _reader.GetName(i); + } + + DataTable IDataReader.GetSchemaTable() + { + return _reader.GetSchemaTable(); + } + + string IDataRecord.GetString(int i) + { + return _reader.GetString(i); + } + + object IDataRecord.GetValue(int i) + { + return _reader.GetGuid(i); + } + + int IDataRecord.GetValues(object[] values) + { + return _reader.GetValues(values); + } + + bool IDataRecord.IsDBNull(int i) + { + return _reader.IsDBNull(i); + } + + bool IDataReader.NextResult() + { + return _reader.NextResult(); + } + + #endregion + + #region IEnumerable + + IEnumerator IEnumerable.GetEnumerator() + { + return _csv.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _csv.GetEnumerator(); + } + + #endregion + + #region Disposable + + protected override void OnDispose(bool disposing) + { + if (disposing) + { + _csv.Dispose(); + } + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvWriter.cs b/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvWriter.cs index f2f360556b..d4cdc66359 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvWriter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Csv/CsvWriter.cs @@ -14,181 +14,181 @@ namespace SmartStore.Services.DataExchange.Csv { - /// - /// SImple utility class used to write CSV files. - /// - public class CsvWriter : DisposableObject - { - private TextWriter _writer; - private readonly IList _currentRow = new List(); - private int? _fieldCount; - - public CsvWriter(TextWriter writer) - : this(writer, new CsvConfiguration()) - { - } - - public CsvWriter(TextWriter writer, CsvConfiguration configuration) - { - Guard.NotNull(writer, nameof(writer)); - Guard.NotNull(configuration, nameof(configuration)); - - _writer = writer; + /// + /// SImple utility class used to write CSV files. + /// + public class CsvWriter : DisposableObject + { + private TextWriter _writer; + private readonly IList _currentRow = new List(); + private int? _fieldCount; + + public CsvWriter(TextWriter writer) + : this(writer, new CsvConfiguration()) + { + } + + public CsvWriter(TextWriter writer, CsvConfiguration configuration) + { + Guard.NotNull(writer, nameof(writer)); + Guard.NotNull(configuration, nameof(configuration)); + + _writer = writer; this.Configuration = configuration; - } - - public CsvConfiguration Configuration - { - get; - private set; - } - - /// - /// Writes a sequence of fields to the CSV file. The fields - /// may get quotes added to it. - /// When all fields are written for a row, - /// must be called - /// to complete writing of the current row. - /// - /// The fields to write. - public virtual void WriteFields(IEnumerable fields) - { - Guard.NotNull(fields, nameof(fields)); - fields.Each(x => WriteField(x)); - } - - /// - /// Writes a sequence of fields to the CSV file. This will - /// ignore any need to quote and ignore the - /// - /// and just quote based on the shouldQuote - /// parameter. - /// When all fields are written for a row, - /// must be called - /// to complete writing of the current row. - /// - /// The fields to write. - /// True to quote the fields, otherwise false. - public virtual void WriteFields(IEnumerable fields, bool shouldQuote) - { - Guard.NotNull(fields, nameof(fields)); - fields.Each(x => WriteField(x, shouldQuote)); - } - - /// - /// Writes the field to the CSV file. The field - /// may get quotes added to it. - /// When all fields are written for a row, - /// must be called - /// to complete writing of the current row. - /// - /// The field to write. - public virtual void WriteField(string field) - { - var shouldQuote = Configuration.QuoteAllFields; - - if (!string.IsNullOrEmpty(field)) - { - if (Configuration.TrimValues) - { - field = field.Trim(); - } - - if (!Configuration.SupportsMultiline) - { - field = field.Replace('\r', ' ').Replace('\n', ' '); - } - - if (shouldQuote - || field[0] == ' ' - || field[field.Length - 1] == ' ' - || field.IndexOfAny(Configuration.QuotableChars) > -1) - { - shouldQuote = true; - } - } - - WriteField(field, shouldQuote); - } - - /// - /// Writes the field to the CSV file. This will - /// ignore any need to quote and ignore the - /// - /// and just quote based on the shouldQuote - /// parameter. - /// When all fields are written for a row, - /// must be called - /// to complete writing of the current row. - /// - /// The field to write. - /// True to quote the field, otherwise false. - public virtual void WriteField(string field, bool shouldQuote) - { - // All quotes must be escaped. - if (shouldQuote && !string.IsNullOrEmpty(field)) - { - field = field.Replace(Configuration.Quote.ToString(), Configuration.QuoteString); - } - - if (shouldQuote) - { - field = Configuration.Quote + (field ?? string.Empty) + Configuration.Quote; - } - - _currentRow.Add(field ?? string.Empty); - } - - /// - /// Ends writing of the current row - /// and starts a new row. - /// - public virtual void NextRow() - { - WriteRow(_currentRow.ToArray()); - _currentRow.Clear(); - } - - public string CurrentRawValue() - { - var row = string.Join(Configuration.Delimiter.ToString(), _currentRow); - return row; - } - - private void WriteRow(string[] fields) - { - CheckDisposed(); - - if (fields.Length == 0) - { - throw new SmartException("Cannot write an empty row to the CSV file."); - } - - if (!_fieldCount.HasValue) - { - _fieldCount = fields.Length; - } - - if (_fieldCount.Value != fields.Length) - { - throw new SmartException("The field count of the current row does not match the previous row's field count."); - } - - var row = string.Join(Configuration.Delimiter.ToString(), fields); - _writer.WriteLine(row); - } - - protected override void OnDispose(bool disposing) - { - if (disposing) - { - if (_writer != null) - { - _writer.Dispose(); - _writer = null; - } - } - } - - } + } + + public CsvConfiguration Configuration + { + get; + private set; + } + + /// + /// Writes a sequence of fields to the CSV file. The fields + /// may get quotes added to it. + /// When all fields are written for a row, + /// must be called + /// to complete writing of the current row. + /// + /// The fields to write. + public virtual void WriteFields(IEnumerable fields) + { + Guard.NotNull(fields, nameof(fields)); + fields.Each(x => WriteField(x)); + } + + /// + /// Writes a sequence of fields to the CSV file. This will + /// ignore any need to quote and ignore the + /// + /// and just quote based on the shouldQuote + /// parameter. + /// When all fields are written for a row, + /// must be called + /// to complete writing of the current row. + /// + /// The fields to write. + /// True to quote the fields, otherwise false. + public virtual void WriteFields(IEnumerable fields, bool shouldQuote) + { + Guard.NotNull(fields, nameof(fields)); + fields.Each(x => WriteField(x, shouldQuote)); + } + + /// + /// Writes the field to the CSV file. The field + /// may get quotes added to it. + /// When all fields are written for a row, + /// must be called + /// to complete writing of the current row. + /// + /// The field to write. + public virtual void WriteField(string field) + { + var shouldQuote = Configuration.QuoteAllFields; + + if (!string.IsNullOrEmpty(field)) + { + if (Configuration.TrimValues) + { + field = field.Trim(); + } + + if (!Configuration.SupportsMultiline) + { + field = field.Replace('\r', ' ').Replace('\n', ' '); + } + + if (shouldQuote + || field[0] == ' ' + || field[field.Length - 1] == ' ' + || field.IndexOfAny(Configuration.QuotableChars) > -1) + { + shouldQuote = true; + } + } + + WriteField(field, shouldQuote); + } + + /// + /// Writes the field to the CSV file. This will + /// ignore any need to quote and ignore the + /// + /// and just quote based on the shouldQuote + /// parameter. + /// When all fields are written for a row, + /// must be called + /// to complete writing of the current row. + /// + /// The field to write. + /// True to quote the field, otherwise false. + public virtual void WriteField(string field, bool shouldQuote) + { + // All quotes must be escaped. + if (shouldQuote && !string.IsNullOrEmpty(field)) + { + field = field.Replace(Configuration.Quote.ToString(), Configuration.QuoteString); + } + + if (shouldQuote) + { + field = Configuration.Quote + (field ?? string.Empty) + Configuration.Quote; + } + + _currentRow.Add(field ?? string.Empty); + } + + /// + /// Ends writing of the current row + /// and starts a new row. + /// + public virtual void NextRow() + { + WriteRow(_currentRow.ToArray()); + _currentRow.Clear(); + } + + public string CurrentRawValue() + { + var row = string.Join(Configuration.Delimiter.ToString(), _currentRow); + return row; + } + + private void WriteRow(string[] fields) + { + CheckDisposed(); + + if (fields.Length == 0) + { + throw new SmartException("Cannot write an empty row to the CSV file."); + } + + if (!_fieldCount.HasValue) + { + _fieldCount = fields.Length; + } + + if (_fieldCount.Value != fields.Length) + { + throw new SmartException("The field count of the current row does not match the previous row's field count."); + } + + var row = string.Join(Configuration.Delimiter.ToString(), fields); + _writer.WriteLine(row); + } + + protected override void OnDispose(bool disposing) + { + if (disposing) + { + if (_writer != null) + { + _writer.Dispose(); + _writer = null; + } + } + } + + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Excel/ExcelDataReader.cs b/src/Libraries/SmartStore.Services/DataExchange/Excel/ExcelDataReader.cs index 709c6deff9..9031e7792d 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Excel/ExcelDataReader.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Excel/ExcelDataReader.cs @@ -10,368 +10,347 @@ namespace SmartStore.Services.DataExchange.Excel { - public class ExcelDataReader : DisposableObject, IDataReader - { - private ExcelPackage _package; - private ExcelWorksheet _sheet; - private int _totalRows; - private int _totalColumns; - - private bool _initialized; - private string[] _columns; - private Dictionary _columnIndexes; - private int _currentRowIndex; - private bool _eof; - - private readonly object _lock = new object(); - - public ExcelDataReader(Stream source, bool hasHeaders) - { - Guard.NotNull(source, nameof(source)); - - _package = new ExcelPackage(source); - - // get the first worksheet in the workbook - _sheet = _package.Workbook.Worksheets.FirstOrDefault(); - if (_sheet == null) - { - throw Error.InvalidOperation("The excel package does not contain any worksheet."); - } - - if (_sheet.Dimension == null) - { - throw Error.InvalidOperation("The excel worksheet does not contain any data."); - } - - HasHeaders = hasHeaders; - DefaultHeaderName = "Column"; - - _totalColumns = _sheet.Dimension.End.Column; - _totalRows = _sheet.Dimension.End.Row - (hasHeaders ? 1 : 0); - - _currentRowIndex = -1; - } - - #region Configuration - - public bool HasHeaders - { - get; - private set; - } - - public string DefaultHeaderName - { - get; - set; - } - - #endregion - - #region public members - - public bool MoveToStart() - { - return MoveTo(0); - } - - public bool MoveToEnd() - { - return MoveTo(_totalRows - 1); - } - - public bool MoveTo(int row) - { - EnsureInitialize(); - ValidateDataReader(validateInitialized: false); - - if (row < 0 || row >= _totalRows) - return false; - - _currentRowIndex = row; - _eof = false; - - return true; - } - - public bool EndOfStream - { - get { return _eof; } - } - - public int TotalRows - { - get - { - EnsureInitialize(); - return _totalRows; - } - } - - public IReadOnlyCollection GetColumnHeaders() - { - EnsureInitialize(); - return _columns.AsReadOnly(); - } - - public int CurrentRowIndex - { - get - { - return _currentRowIndex; - } - } - - public string GetFormatted(int i) - { - ValidateDataReader(); - return _sheet.Cells[ExcelRowIndex(_currentRowIndex), i + 1].Text; - } - - public int GetColumnIndex(string name) - { - Guard.NotEmpty(name, nameof(name)); - - EnsureInitialize(); - - int index; - - if (_columnIndexes != null && _columnIndexes.TryGetValue(name, out index)) - return index; - else - return -1; - } - - #endregion - - #region IDataReader - - bool IDataReader.NextResult() - { - ValidateDataReader(validateInitialized: false); - return false; - } - - public bool Read() - { - ValidateDataReader(validateInitialized: false); - return ReadNextRow(false); - } - - int IDataReader.Depth - { - get - { - ValidateDataReader(validateInitialized: false); - return 0; - } - } - - bool IDataReader.IsClosed - { - get - { - return _eof; - } - } - - int IDataReader.RecordsAffected - { - get - { - return -1; - } - } - - void IDataReader.Close() - { - Dispose(); - } - - - public int FieldCount - { - get - { - EnsureInitialize(); - return _totalColumns; - } - } - - public object this[string name] - { - get - { - int index = GetColumnIndex(name); - - if (index < 0) - Error.Argument("name", "'{0}' column header not found.".FormatInvariant(name)); - - return this[index]; - } - } - - public object this[int i] - { - get - { - ValidateDataReader(); - // Excel indexes start from 1 - return _sheet.GetValue(ExcelRowIndex(_currentRowIndex), i + 1); - } - } - - public bool GetBoolean(int i) - { - object value = this[i]; - - int result; - if (Int32.TryParse(value.ToString(), out result)) - return (result != 0); - else - return Boolean.Parse(value.ToString()); - } - - public byte GetByte(int i) - { - return this[i].Convert(CultureInfo.CurrentCulture); - } - - public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) - { - ValidateDataReader(); - return CopyFieldToArray(i, fieldOffset, buffer, bufferoffset, length); - } - - public char GetChar(int i) - { - return this[i].Convert(CultureInfo.CurrentCulture); - } - - public long GetChars(int i, long fieldOffset, char[] buffer, int bufferoffset, int length) - { - ValidateDataReader(); - return CopyFieldToArray(i, fieldOffset, buffer, bufferoffset, length); - } - - IDataReader IDataRecord.GetData(int i) - { - ValidateDataReader(); - - if (i == 0) - return this; - else - return null; - } - - string IDataRecord.GetDataTypeName(int i) - { - return this[i].GetType().FullName; - } - - public DateTime GetDateTime(int i) - { - return this[i].Convert(CultureInfo.CurrentCulture); - } - - public decimal GetDecimal(int i) - { - return this[i].Convert(CultureInfo.CurrentCulture); - } - - public double GetDouble(int i) - { - return this[i].Convert(CultureInfo.CurrentCulture); - } - - public Type GetFieldType(int i) - { - return this[i].GetType(); - } - - public float GetFloat(int i) - { - return this[i].Convert(CultureInfo.CurrentCulture); - } - - public Guid GetGuid(int i) - { - return this[i].Convert(CultureInfo.CurrentCulture); - } - - public short GetInt16(int i) - { - return this[i].Convert(CultureInfo.CurrentCulture); - } - - public int GetInt32(int i) - { - return this[i].Convert(CultureInfo.CurrentCulture); - } - - public long GetInt64(int i) - { - return this[i].Convert(CultureInfo.CurrentCulture); - } - - public string GetName(int i) - { - EnsureInitialize(); - ValidateDataReader(validateInitialized: false); - - if (i < 0 || i >= _columns.Length) - { - throw new ArgumentOutOfRangeException("i", i, - "Column index must be included within [0, {0}], but specified column index was: '{1}'.".FormatInvariant(_columns.Length, i)); - } - - return _columns[i]; - } - - public int GetOrdinal(string name) - { - return GetColumnIndex(name); - } - - DataTable IDataReader.GetSchemaTable() - { - EnsureInitialize(); - ValidateDataReader(validateInitialized: false); - - var schema = new DataTable("SchemaTable") - { - Locale = CultureInfo.InvariantCulture, - MinimumCapacity = _columns.Length - }; + public class ExcelDataReader : DisposableObject, IDataReader + { + private ExcelPackage _package; + private ExcelWorksheet _sheet; + private int _totalRows; + private int _totalColumns; + + private bool _initialized; + private string[] _columns; + private Dictionary _columnIndexes; + private int _currentRowIndex; + private bool _eof; + + private readonly object _lock = new object(); + + public ExcelDataReader(Stream source, bool hasHeaders) + { + Guard.NotNull(source, nameof(source)); + + _package = new ExcelPackage(source); + + // get the first worksheet in the workbook + _sheet = _package.Workbook.Worksheets.FirstOrDefault(); + if (_sheet == null) + { + throw Error.InvalidOperation("The excel package does not contain any worksheet."); + } + + if (_sheet.Dimension == null) + { + throw Error.InvalidOperation("The excel worksheet does not contain any data."); + } + + HasHeaders = hasHeaders; + DefaultHeaderName = "Column"; + + _totalColumns = _sheet.Dimension.End.Column; + _totalRows = _sheet.Dimension.End.Row - (hasHeaders ? 1 : 0); + + _currentRowIndex = -1; + } + + #region Configuration + + public bool HasHeaders + { + get; + private set; + } + + public string DefaultHeaderName + { + get; + set; + } + + #endregion - schema.Columns.Add(SchemaTableColumn.AllowDBNull, typeof(bool)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.BaseColumnName, typeof(string)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.BaseSchemaName, typeof(string)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.BaseTableName, typeof(string)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.ColumnName, typeof(string)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.ColumnOrdinal, typeof(int)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.ColumnSize, typeof(int)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.DataType, typeof(object)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.IsAliased, typeof(bool)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.IsExpression, typeof(bool)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.IsKey, typeof(bool)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.IsLong, typeof(bool)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.IsUnique, typeof(bool)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.NumericPrecision, typeof(short)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.NumericScale, typeof(short)).ReadOnly = true; - schema.Columns.Add(SchemaTableColumn.ProviderType, typeof(int)).ReadOnly = true; - - schema.Columns.Add(SchemaTableOptionalColumn.BaseCatalogName, typeof(string)).ReadOnly = true; - schema.Columns.Add(SchemaTableOptionalColumn.BaseServerName, typeof(string)).ReadOnly = true; - schema.Columns.Add(SchemaTableOptionalColumn.IsAutoIncrement, typeof(bool)).ReadOnly = true; - schema.Columns.Add(SchemaTableOptionalColumn.IsHidden, typeof(bool)).ReadOnly = true; - schema.Columns.Add(SchemaTableOptionalColumn.IsReadOnly, typeof(bool)).ReadOnly = true; - schema.Columns.Add(SchemaTableOptionalColumn.IsRowVersion, typeof(bool)).ReadOnly = true; - - // null marks columns that will change for each row - object[] schemaRow = - { - true, // 00- AllowDBNull + #region public members + + public bool MoveToStart() + { + return MoveTo(0); + } + + public bool MoveToEnd() + { + return MoveTo(_totalRows - 1); + } + + public bool MoveTo(int row) + { + EnsureInitialize(); + ValidateDataReader(validateInitialized: false); + + if (row < 0 || row >= _totalRows) + return false; + + _currentRowIndex = row; + _eof = false; + + return true; + } + + public bool EndOfStream => _eof; + + public int TotalRows + { + get + { + EnsureInitialize(); + return _totalRows; + } + } + + public IReadOnlyCollection GetColumnHeaders() + { + EnsureInitialize(); + return _columns.AsReadOnly(); + } + + public int CurrentRowIndex => _currentRowIndex; + + public string GetFormatted(int i) + { + ValidateDataReader(); + return _sheet.Cells[ExcelRowIndex(_currentRowIndex), i + 1].Text; + } + + public int GetColumnIndex(string name) + { + Guard.NotEmpty(name, nameof(name)); + + EnsureInitialize(); + + int index; + + if (_columnIndexes != null && _columnIndexes.TryGetValue(name, out index)) + return index; + else + return -1; + } + + #endregion + + #region IDataReader + + bool IDataReader.NextResult() + { + ValidateDataReader(validateInitialized: false); + return false; + } + + public bool Read() + { + ValidateDataReader(validateInitialized: false); + return ReadNextRow(false); + } + + int IDataReader.Depth + { + get + { + ValidateDataReader(validateInitialized: false); + return 0; + } + } + + bool IDataReader.IsClosed => _eof; + + int IDataReader.RecordsAffected => -1; + + void IDataReader.Close() + { + Dispose(); + } + + + public int FieldCount + { + get + { + EnsureInitialize(); + return _totalColumns; + } + } + + public object this[string name] + { + get + { + int index = GetColumnIndex(name); + + if (index < 0) + Error.Argument("name", "'{0}' column header not found.".FormatInvariant(name)); + + return this[index]; + } + } + + public object this[int i] + { + get + { + ValidateDataReader(); + // Excel indexes start from 1 + return _sheet.GetValue(ExcelRowIndex(_currentRowIndex), i + 1); + } + } + + public bool GetBoolean(int i) + { + object value = this[i]; + + int result; + if (Int32.TryParse(value.ToString(), out result)) + return (result != 0); + else + return Boolean.Parse(value.ToString()); + } + + public byte GetByte(int i) + { + return this[i].Convert(CultureInfo.CurrentCulture); + } + + public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) + { + ValidateDataReader(); + return CopyFieldToArray(i, fieldOffset, buffer, bufferoffset, length); + } + + public char GetChar(int i) + { + return this[i].Convert(CultureInfo.CurrentCulture); + } + + public long GetChars(int i, long fieldOffset, char[] buffer, int bufferoffset, int length) + { + ValidateDataReader(); + return CopyFieldToArray(i, fieldOffset, buffer, bufferoffset, length); + } + + IDataReader IDataRecord.GetData(int i) + { + ValidateDataReader(); + + if (i == 0) + return this; + else + return null; + } + + string IDataRecord.GetDataTypeName(int i) + { + return this[i].GetType().FullName; + } + + public DateTime GetDateTime(int i) + { + return this[i].Convert(CultureInfo.CurrentCulture); + } + + public decimal GetDecimal(int i) + { + return this[i].Convert(CultureInfo.CurrentCulture); + } + + public double GetDouble(int i) + { + return this[i].Convert(CultureInfo.CurrentCulture); + } + + public Type GetFieldType(int i) + { + return this[i].GetType(); + } + + public float GetFloat(int i) + { + return this[i].Convert(CultureInfo.CurrentCulture); + } + + public Guid GetGuid(int i) + { + return this[i].Convert(CultureInfo.CurrentCulture); + } + + public short GetInt16(int i) + { + return this[i].Convert(CultureInfo.CurrentCulture); + } + + public int GetInt32(int i) + { + return this[i].Convert(CultureInfo.CurrentCulture); + } + + public long GetInt64(int i) + { + return this[i].Convert(CultureInfo.CurrentCulture); + } + + public string GetName(int i) + { + EnsureInitialize(); + ValidateDataReader(validateInitialized: false); + + if (i < 0 || i >= _columns.Length) + { + throw new ArgumentOutOfRangeException("i", i, + "Column index must be included within [0, {0}], but specified column index was: '{1}'.".FormatInvariant(_columns.Length, i)); + } + + return _columns[i]; + } + + public int GetOrdinal(string name) + { + return GetColumnIndex(name); + } + + DataTable IDataReader.GetSchemaTable() + { + EnsureInitialize(); + ValidateDataReader(validateInitialized: false); + + var schema = new DataTable("SchemaTable") + { + Locale = CultureInfo.InvariantCulture, + MinimumCapacity = _columns.Length + }; + + schema.Columns.Add(SchemaTableColumn.AllowDBNull, typeof(bool)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.BaseColumnName, typeof(string)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.BaseSchemaName, typeof(string)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.BaseTableName, typeof(string)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.ColumnName, typeof(string)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.ColumnOrdinal, typeof(int)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.ColumnSize, typeof(int)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.DataType, typeof(object)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.IsAliased, typeof(bool)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.IsExpression, typeof(bool)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.IsKey, typeof(bool)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.IsLong, typeof(bool)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.IsUnique, typeof(bool)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.NumericPrecision, typeof(short)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.NumericScale, typeof(short)).ReadOnly = true; + schema.Columns.Add(SchemaTableColumn.ProviderType, typeof(int)).ReadOnly = true; + + schema.Columns.Add(SchemaTableOptionalColumn.BaseCatalogName, typeof(string)).ReadOnly = true; + schema.Columns.Add(SchemaTableOptionalColumn.BaseServerName, typeof(string)).ReadOnly = true; + schema.Columns.Add(SchemaTableOptionalColumn.IsAutoIncrement, typeof(bool)).ReadOnly = true; + schema.Columns.Add(SchemaTableOptionalColumn.IsHidden, typeof(bool)).ReadOnly = true; + schema.Columns.Add(SchemaTableOptionalColumn.IsReadOnly, typeof(bool)).ReadOnly = true; + schema.Columns.Add(SchemaTableOptionalColumn.IsRowVersion, typeof(bool)).ReadOnly = true; + + // null marks columns that will change for each row + object[] schemaRow = + { + true, // 00- AllowDBNull null, // 01- BaseColumnName string.Empty, // 02- BaseSchemaName string.Empty, // 03- BaseTableName @@ -395,218 +374,218 @@ DataTable IDataReader.GetSchemaTable() false // 21- IsRowVersion }; - int r = ExcelRowIndex(0); - - for (int i = 0; i < _columns.Length; i++) - { - schemaRow[1] = _columns[i]; // Base column name - schemaRow[4] = _columns[i]; // Column name - schemaRow[5] = i; // Column ordinal - - // get data type from 1st row only - var firstValue = _sheet.Cells[r, i + 1].Value; - schemaRow[7] = firstValue != null ? firstValue.GetType() : typeof(string); - - schema.Rows.Add(schemaRow); - } - - return schema; - } - - public string GetString(int i) - { - return this[i].Convert(CultureInfo.CurrentCulture); - } - - public object GetValue(int i) - { - ValidateDataReader(); - return ((IDataRecord)this).IsDBNull(i) ? DBNull.Value : this[i]; - } - - int IDataRecord.GetValues(object[] values) - { - var record = (IDataRecord)this; - - for (int i = 0; i < _totalColumns; i++) - { - values[i] = record.GetValue(i); - } - - return _totalColumns; - } - - bool IDataRecord.IsDBNull(int i) - { - return this[i] == null; - } - - #endregion - - #region Helper - - /// - /// Reads the next record. - /// - /// - /// Indicates if the reader will proceed to the next record after having read headers. - /// if it stops after having read headers; otherwise, . - /// - /// if a record has been successfully reads; otherwise, . - /// - /// The instance has been disposed of. - /// - protected virtual bool ReadNextRow(bool onlyReadHeaders) - { - if (_eof) - return false; - - CheckDisposed(); - - if (!_initialized) - { - _columns = new string[_totalColumns]; - _columnIndexes = new Dictionary(_totalColumns, StringComparer.OrdinalIgnoreCase); - - for (int i = 1; i <= _totalColumns; i++) - { - // Excel indexes start from 1 - string name = null; - if (HasHeaders) - { - name = _sheet.GetValue(1, i).NullEmpty(); - } - - name = name ?? (DefaultHeaderName ?? "Column") + i; - - _columns[i - 1] = name; - _columnIndexes[name] = i - 1; - } - - if (_columns.Select(x => x.ToLower()).Distinct().ToArray().Length != _columns.Length) - { - _columns = null; - _columnIndexes = null; - throw Error.InvalidOperation("The first row cannot contain duplicate column names."); - } - - _initialized = true; - - if (!onlyReadHeaders) - { - return ReadNextRow(false); - } - } - else - { - _currentRowIndex++; - if (_currentRowIndex >= _totalRows) - { - _eof = true; - return false; - } - } - - return true; - } - - private int ExcelRowIndex(int i) - { - // Excel indexes start from 1 - return i + (HasHeaders ? 2 : 1); - } - - /// - /// Ensures that the reader is initialized. - /// - private void EnsureInitialize() - { - if (!_initialized) - { - ReadNextRow(true); - } - - Debug.Assert(_columns != null); - Debug.Assert(_columns.Length > 0 || (_columns.Length == 0 && _columnIndexes == null)); - } - - private void ValidateDataReader(bool validateInitialized = true, bool validateNotClosed = true) - { - if (validateInitialized && (!_initialized || _currentRowIndex < 0)) - throw new InvalidOperationException("No current record. Call Read() to initialize the reader."); - - if (validateNotClosed && IsDisposed) - throw new InvalidOperationException("This operation is invalid when the reader is closed."); - } - - private long CopyFieldToArray(int column, long columnOffset, Array destinationArray, int destinationOffset, int length) - { - EnsureInitialize(); - - if (column < 0 || column >= _totalColumns) - { - throw new ArgumentOutOfRangeException("column", column, - "Column index must be included within [0, {0}], but specified column index was: '{1}'.".FormatInvariant(_totalColumns, column)); - } - - if (columnOffset < 0 || columnOffset >= int.MaxValue) - throw new ArgumentOutOfRangeException("fieldOffset"); - - // Array.Copy(...) will do the remaining argument checks - - if (length == 0) - return 0; - - string value = this[column].ToString(); - - if (value == null) - value = string.Empty; - - if (destinationArray.GetType() == typeof(char[])) - Array.Copy(value.ToCharArray((int)columnOffset, length), 0, destinationArray, destinationOffset, length); - else - { - char[] chars = value.ToCharArray((int)columnOffset, length); - byte[] source = new byte[chars.Length]; - ; - - for (int i = 0; i < chars.Length; i++) - source[i] = Convert.ToByte(chars[i]); - - Array.Copy(source, 0, destinationArray, destinationOffset, length); - } - - return length; - } - - #endregion - - #region IDisposable Support - - protected override void OnDispose(bool disposing) - { - if (disposing) - { - try - { - _sheet = null; - if (_package != null) - { - lock (_lock) - { - if (_package != null) - { - _package.Dispose(); - _package = null; - _eof = true; - } - } - } - } - catch { } - } - } - - #endregion - } + int r = ExcelRowIndex(0); + + for (int i = 0; i < _columns.Length; i++) + { + schemaRow[1] = _columns[i]; // Base column name + schemaRow[4] = _columns[i]; // Column name + schemaRow[5] = i; // Column ordinal + + // get data type from 1st row only + var firstValue = _sheet.Cells[r, i + 1].Value; + schemaRow[7] = firstValue != null ? firstValue.GetType() : typeof(string); + + schema.Rows.Add(schemaRow); + } + + return schema; + } + + public string GetString(int i) + { + return this[i].Convert(CultureInfo.CurrentCulture); + } + + public object GetValue(int i) + { + ValidateDataReader(); + return ((IDataRecord)this).IsDBNull(i) ? DBNull.Value : this[i]; + } + + int IDataRecord.GetValues(object[] values) + { + var record = (IDataRecord)this; + + for (int i = 0; i < _totalColumns; i++) + { + values[i] = record.GetValue(i); + } + + return _totalColumns; + } + + bool IDataRecord.IsDBNull(int i) + { + return this[i] == null; + } + + #endregion + + #region Helper + + /// + /// Reads the next record. + /// + /// + /// Indicates if the reader will proceed to the next record after having read headers. + /// if it stops after having read headers; otherwise, . + /// + /// if a record has been successfully reads; otherwise, . + /// + /// The instance has been disposed of. + /// + protected virtual bool ReadNextRow(bool onlyReadHeaders) + { + if (_eof) + return false; + + CheckDisposed(); + + if (!_initialized) + { + _columns = new string[_totalColumns]; + _columnIndexes = new Dictionary(_totalColumns, StringComparer.OrdinalIgnoreCase); + + for (int i = 1; i <= _totalColumns; i++) + { + // Excel indexes start from 1 + string name = null; + if (HasHeaders) + { + name = _sheet.GetValue(1, i).NullEmpty(); + } + + name = name ?? (DefaultHeaderName ?? "Column") + i; + + _columns[i - 1] = name; + _columnIndexes[name] = i - 1; + } + + if (_columns.Select(x => x.ToLower()).Distinct().ToArray().Length != _columns.Length) + { + _columns = null; + _columnIndexes = null; + throw Error.InvalidOperation("The first row cannot contain duplicate column names."); + } + + _initialized = true; + + if (!onlyReadHeaders) + { + return ReadNextRow(false); + } + } + else + { + _currentRowIndex++; + if (_currentRowIndex >= _totalRows) + { + _eof = true; + return false; + } + } + + return true; + } + + private int ExcelRowIndex(int i) + { + // Excel indexes start from 1 + return i + (HasHeaders ? 2 : 1); + } + + /// + /// Ensures that the reader is initialized. + /// + private void EnsureInitialize() + { + if (!_initialized) + { + ReadNextRow(true); + } + + Debug.Assert(_columns != null); + Debug.Assert(_columns.Length > 0 || (_columns.Length == 0 && _columnIndexes == null)); + } + + private void ValidateDataReader(bool validateInitialized = true, bool validateNotClosed = true) + { + if (validateInitialized && (!_initialized || _currentRowIndex < 0)) + throw new InvalidOperationException("No current record. Call Read() to initialize the reader."); + + if (validateNotClosed && IsDisposed) + throw new InvalidOperationException("This operation is invalid when the reader is closed."); + } + + private long CopyFieldToArray(int column, long columnOffset, Array destinationArray, int destinationOffset, int length) + { + EnsureInitialize(); + + if (column < 0 || column >= _totalColumns) + { + throw new ArgumentOutOfRangeException("column", column, + "Column index must be included within [0, {0}], but specified column index was: '{1}'.".FormatInvariant(_totalColumns, column)); + } + + if (columnOffset < 0 || columnOffset >= int.MaxValue) + throw new ArgumentOutOfRangeException("fieldOffset"); + + // Array.Copy(...) will do the remaining argument checks + + if (length == 0) + return 0; + + string value = this[column].ToString(); + + if (value == null) + value = string.Empty; + + if (destinationArray.GetType() == typeof(char[])) + Array.Copy(value.ToCharArray((int)columnOffset, length), 0, destinationArray, destinationOffset, length); + else + { + char[] chars = value.ToCharArray((int)columnOffset, length); + byte[] source = new byte[chars.Length]; + ; + + for (int i = 0; i < chars.Length; i++) + source[i] = Convert.ToByte(chars[i]); + + Array.Copy(source, 0, destinationArray, destinationOffset, length); + } + + return length; + } + + #endregion + + #region IDisposable Support + + protected override void OnDispose(bool disposing) + { + if (disposing) + { + try + { + _sheet = null; + if (_package != null) + { + lock (_lock) + { + if (_package != null) + { + _package.Dispose(); + _package = null; + _eof = true; + } + } + } + } + catch { } + } + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/DataExportResult.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/DataExportResult.cs index 57c85801ab..80d2c7b437 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/DataExportResult.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/DataExportResult.cs @@ -6,62 +6,59 @@ namespace SmartStore.Services.DataExchange.Export { [Serializable] - public class DataExportResult - { - public DataExportResult() - { - Files = new List(); - } + public class DataExportResult + { + public DataExportResult() + { + Files = new List(); + } - /// - /// Whether the export succeeded - /// - public bool Succeeded - { - get { return LastError.IsEmpty(); } - } + /// + /// Whether the export succeeded + /// + public bool Succeeded => LastError.IsEmpty(); - /// - /// Last error - /// - [XmlIgnore] - public string LastError { get; set; } + /// + /// Last error + /// + [XmlIgnore] + public string LastError { get; set; } - /// - /// Files created by last export - /// - public List Files { get; set; } + /// + /// Files created by last export + /// + public List Files { get; set; } - /// - /// The path of the folder with the export files - /// - [XmlIgnore] - public string FileFolder { get; set; } + /// + /// The path of the folder with the export files + /// + [XmlIgnore] + public string FileFolder { get; set; } - [Serializable] - public class ExportFileInfo - { - /// - /// Store identifier, can be 0. - /// - public int StoreId { get; set; } + [Serializable] + public class ExportFileInfo + { + /// + /// Store identifier, can be 0. + /// + public int StoreId { get; set; } - /// - /// Name of file. - /// - public string FileName { get; set; } + /// + /// Name of file. + /// + public string FileName { get; set; } - /// - /// Short optional text that describes the content of the file. - /// - public string Label { get; set; } + /// + /// Short optional text that describes the content of the file. + /// + public string Label { get; set; } /// /// The related entity type. /// public RelatedEntityType? RelatedType { get; set; } } - } + } public class DataExportPreviewResult diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/DataExportTask.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/DataExportTask.cs index 0d935149e8..08225564bd 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/DataExportTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/DataExportTask.cs @@ -4,55 +4,55 @@ namespace SmartStore.Services.DataExchange.Export { - // Note, namespace persisted in ScheduleTask.Type! - public partial class DataExportTask : ITask - { - private readonly IDataExporter _exporter; - private readonly IExportProfileService _exportProfileService; - - public DataExportTask( - IDataExporter exporter, - IExportProfileService exportProfileService) - { - _exporter = exporter; - _exportProfileService = exportProfileService; + // Note, namespace persisted in ScheduleTask.Type! + public partial class DataExportTask : ITask + { + private readonly IDataExporter _exporter; + private readonly IExportProfileService _exportProfileService; + + public DataExportTask( + IDataExporter exporter, + IExportProfileService exportProfileService) + { + _exporter = exporter; + _exportProfileService = exportProfileService; } - public Localizer T { get; set; } - - public void Execute(TaskExecutionContext ctx) - { - var profileId = ctx.ScheduleTaskHistory.ScheduleTask.Alias.ToInt(); - var profile = _exportProfileService.GetExportProfileById(profileId); - - // Load provider. - var provider = _exportProfileService.LoadProvider(profile.ProviderSystemName); - if (provider == null) - throw new SmartException(T("Admin.Common.ProviderNotLoaded", profile.ProviderSystemName.NaIfEmpty())); - - // Build export request. - var request = new DataExportRequest(profile, provider); - - request.ProgressValueSetter = delegate (int val, int max, string msg) - { - ctx.SetProgress(val, max, msg, true); - }; - - if (ctx.Parameters.ContainsKey("SelectedIds")) - { - request.EntitiesToExport = ctx.Parameters["SelectedIds"] - .SplitSafe(",") - .Select(x => x.ToInt()) - .ToList(); - } - - if (ctx.Parameters.ContainsKey("ActionOrigin")) - { - request.ActionOrigin = ctx.Parameters["ActionOrigin"]; - } - - // Process! - _exporter.Export(request, ctx.CancellationToken); - } - } + public Localizer T { get; set; } + + public void Execute(TaskExecutionContext ctx) + { + var profileId = ctx.ScheduleTaskHistory.ScheduleTask.Alias.ToInt(); + var profile = _exportProfileService.GetExportProfileById(profileId); + + // Load provider. + var provider = _exportProfileService.LoadProvider(profile.ProviderSystemName); + if (provider == null) + throw new SmartException(T("Admin.Common.ProviderNotLoaded", profile.ProviderSystemName.NaIfEmpty())); + + // Build export request. + var request = new DataExportRequest(profile, provider); + + request.ProgressValueSetter = delegate (int val, int max, string msg) + { + ctx.SetProgress(val, max, msg, true); + }; + + if (ctx.Parameters.ContainsKey("SelectedIds")) + { + request.EntitiesToExport = ctx.Parameters["SelectedIds"] + .SplitSafe(",") + .Select(x => x.ToInt()) + .ToList(); + } + + if (ctx.Parameters.ContainsKey("ActionOrigin")) + { + request.ActionOrigin = ctx.Parameters["ActionOrigin"]; + } + + // Process! + _exporter.Export(request, ctx.CancellationToken); + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs index 6d1b16defc..076b161bf9 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/DataExporter.cs @@ -1,29 +1,33 @@ using System; using System.Collections.Generic; +using System.Data.Entity; using System.IO; using System.IO.Compression; using System.Linq; using System.Text; using System.Threading; using System.Web; -using System.Data.Entity; +using SmartStore.Collections; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.DataExchange; +using SmartStore.Core.Domain.Directory; using SmartStore.Core.Domain.Discounts; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Media; using SmartStore.Core.Domain.Messages; using SmartStore.Core.Domain.Orders; +using SmartStore.Core.Domain.Seo; using SmartStore.Core.Domain.Shipping; using SmartStore.Core.Domain.Stores; using SmartStore.Core.Domain.Tax; using SmartStore.Core.Email; using SmartStore.Core.Localization; using SmartStore.Core.Logging; +using SmartStore.Core.Security; using SmartStore.Services.Catalog; using SmartStore.Services.Catalog.Extensions; using SmartStore.Services.Common; @@ -36,171 +40,163 @@ using SmartStore.Services.Messages; using SmartStore.Services.Orders; using SmartStore.Services.Search; -using SmartStore.Services.Security; using SmartStore.Services.Seo; using SmartStore.Services.Shipping; 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 { public partial class DataExporter : IDataExporter - { - private static readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); - - #region Dependencies - - private readonly ICommonServices _services; - private readonly IDbContext _dbContext; - private readonly HttpContextBase _httpContext; - private readonly Lazy _priceFormatter; - private readonly Lazy _exportProfileService; + { + #region Dependencies + + private readonly ICommonServices _services; + private readonly IDbContext _dbContext; + private readonly HttpContextBase _httpContext; + private readonly Lazy _priceFormatter; + private readonly Lazy _exportProfileService; private readonly Lazy _localizedEntityService; - private readonly Lazy _languageService; + private readonly Lazy _languageService; private readonly Lazy _urlRecordService; - private readonly Lazy _pictureService; - private readonly Lazy _priceCalculationService; - private readonly Lazy _currencyService; - private readonly Lazy _taxService; - private readonly Lazy _categoryService; - private readonly Lazy _productAttributeParser; - private readonly Lazy _productAttributeService; + private readonly Lazy _mediaService; + private readonly Lazy _priceCalculationService; + private readonly Lazy _currencyService; + private readonly Lazy _taxService; + private readonly Lazy _categoryService; + private readonly Lazy _productAttributeParser; + private readonly Lazy _productAttributeService; private readonly Lazy _specificationAttributeService; private readonly Lazy _productTemplateService; - private readonly Lazy _categoryTemplateService; - private readonly Lazy _productService; - private readonly Lazy _orderService; - private readonly Lazy _manufacturerService; - private readonly ICustomerService _customerService; - private readonly Lazy _addressService; - private readonly Lazy _countryService; + private readonly Lazy _categoryTemplateService; + private readonly Lazy _productService; + private readonly Lazy _orderService; + private readonly Lazy _manufacturerService; + private readonly ICustomerService _customerService; + private readonly Lazy _addressService; + private readonly Lazy _countryService; private readonly Lazy _shipmentService; - private readonly Lazy _genericAttributeService; - private readonly Lazy _emailAccountService; - private readonly Lazy _queuedEmailService; - private readonly Lazy _emailSender; - private readonly Lazy _deliveryTimeService; - private readonly Lazy _quantityUnitService; - private readonly Lazy _catalogSearchService; + private readonly Lazy _genericAttributeService; + private readonly Lazy _emailAccountService; + private readonly Lazy _queuedEmailService; + private readonly Lazy _emailSender; + private readonly Lazy _deliveryTimeService; + private readonly Lazy _quantityUnitService; + private readonly Lazy _catalogSearchService; private readonly Lazy _downloadService; private readonly Lazy _productUrlHelper; - private readonly Lazy>_customerRepository; - private readonly Lazy> _subscriptionRepository; - private readonly Lazy> _orderRepository; - private readonly Lazy> _shoppingCartItemRepository; - - private readonly Lazy _mediaSettings; - private readonly Lazy _contactDataSettings; - private readonly Lazy _customerSettings; - private readonly Lazy _catalogSettings; - private readonly Lazy _localizationSettings; - private readonly Lazy _taxSettings; + private readonly Lazy> _customerRepository; + private readonly Lazy> _subscriptionRepository; + private readonly Lazy> _orderRepository; + private readonly Lazy> _shoppingCartItemRepository; + + private readonly Lazy _mediaSettings; + private readonly Lazy _contactDataSettings; + private readonly Lazy _customerSettings; + private readonly Lazy _catalogSettings; + private readonly Lazy _localizationSettings; + private readonly Lazy _taxSettings; private readonly Lazy _seoSettings; public DataExporter( - ICommonServices services, - IDbContext dbContext, - HttpContextBase httpContext, - Lazy priceFormatter, - Lazy exportProfileService, + ICommonServices services, + IDbContext dbContext, + HttpContextBase httpContext, + Lazy priceFormatter, + Lazy exportProfileService, Lazy localizedEntityService, - Lazy languageService, - Lazy urlRecordService, - Lazy pictureService, - Lazy priceCalculationService, - Lazy currencyService, - Lazy taxService, - Lazy categoryService, - Lazy productAttributeParser, - Lazy productAttributeService, + Lazy languageService, + Lazy urlRecordService, + Lazy mediaService, + Lazy priceCalculationService, + Lazy currencyService, + Lazy taxService, + Lazy categoryService, + Lazy productAttributeParser, + Lazy productAttributeService, Lazy specificationAttributeService, Lazy productTemplateService, - Lazy categoryTemplateService, - Lazy productService, - Lazy orderService, - Lazy manufacturerService, - ICustomerService customerService, - Lazy addressService, - Lazy countryService, - Lazy shipmentService, - Lazy genericAttributeService, - Lazy emailAccountService, - Lazy queuedEmailService, + Lazy categoryTemplateService, + Lazy productService, + Lazy orderService, + Lazy manufacturerService, + ICustomerService customerService, + Lazy addressService, + Lazy countryService, + Lazy shipmentService, + Lazy genericAttributeService, + Lazy emailAccountService, + Lazy queuedEmailService, Lazy emailSender, - Lazy deliveryTimeService, - Lazy quantityUnitService, - Lazy catalogSearchService, + Lazy deliveryTimeService, + Lazy quantityUnitService, + Lazy catalogSearchService, Lazy downloadService, Lazy productUrlHelper, - Lazy> customerRepository, - Lazy> subscriptionRepository, - Lazy> orderRepository, - Lazy> shoppingCartItemRepository, - Lazy mediaSettings, - Lazy contactDataSettings, - Lazy customerSettings, - Lazy catalogSettings, - Lazy localizationSettings, - Lazy taxSettings, + Lazy> customerRepository, + Lazy> subscriptionRepository, + Lazy> orderRepository, + Lazy> shoppingCartItemRepository, + Lazy mediaSettings, + Lazy contactDataSettings, + Lazy customerSettings, + Lazy catalogSettings, + Lazy localizationSettings, + Lazy taxSettings, Lazy seoSettings) - { - _services = services; - _dbContext = dbContext; - _httpContext = httpContext; - _priceFormatter = priceFormatter; - _exportProfileService = exportProfileService; - _localizedEntityService = localizedEntityService; - _languageService = languageService; - _urlRecordService = urlRecordService; - _pictureService = pictureService; - _priceCalculationService = priceCalculationService; - _currencyService = currencyService; - _taxService = taxService; - _categoryService = categoryService; - _productAttributeParser = productAttributeParser; - _productAttributeService = productAttributeService; + { + _services = services; + _dbContext = dbContext; + _httpContext = httpContext; + _priceFormatter = priceFormatter; + _exportProfileService = exportProfileService; + _localizedEntityService = localizedEntityService; + _languageService = languageService; + _urlRecordService = urlRecordService; + _mediaService = mediaService; + _priceCalculationService = priceCalculationService; + _currencyService = currencyService; + _taxService = taxService; + _categoryService = categoryService; + _productAttributeParser = productAttributeParser; + _productAttributeService = productAttributeService; _specificationAttributeService = specificationAttributeService; - _productTemplateService = productTemplateService; - _categoryTemplateService = categoryTemplateService; - _productService = productService; - _orderService = orderService; - _manufacturerService = manufacturerService; - _customerService = customerService; - _addressService = addressService; - _countryService = countryService; - _shipmentService = shipmentService; - _genericAttributeService = genericAttributeService; - _emailAccountService = emailAccountService; - _queuedEmailService = queuedEmailService; - _emailSender = emailSender; - _deliveryTimeService = deliveryTimeService; - _quantityUnitService = quantityUnitService; - _catalogSearchService = catalogSearchService; + _productTemplateService = productTemplateService; + _categoryTemplateService = categoryTemplateService; + _productService = productService; + _orderService = orderService; + _manufacturerService = manufacturerService; + _customerService = customerService; + _addressService = addressService; + _countryService = countryService; + _shipmentService = shipmentService; + _genericAttributeService = genericAttributeService; + _emailAccountService = emailAccountService; + _queuedEmailService = queuedEmailService; + _emailSender = emailSender; + _deliveryTimeService = deliveryTimeService; + _quantityUnitService = quantityUnitService; + _catalogSearchService = catalogSearchService; _downloadService = downloadService; - _productUrlHelper = productUrlHelper; - - _customerRepository = customerRepository; - _subscriptionRepository = subscriptionRepository; - _orderRepository = orderRepository; - _shoppingCartItemRepository = shoppingCartItemRepository; - - _mediaSettings = mediaSettings; - _contactDataSettings = contactDataSettings; - _customerSettings = customerSettings; - _catalogSettings = catalogSettings; - _localizationSettings = localizationSettings; - _taxSettings = taxSettings; + _productUrlHelper = productUrlHelper; + + _customerRepository = customerRepository; + _subscriptionRepository = subscriptionRepository; + _orderRepository = orderRepository; + _shoppingCartItemRepository = shoppingCartItemRepository; + + _mediaSettings = mediaSettings; + _contactDataSettings = contactDataSettings; + _customerSettings = customerSettings; + _catalogSettings = catalogSettings; + _localizationSettings = localizationSettings; + _taxSettings = taxSettings; _seoSettings = seoSettings; + } - T = NullLocalizer.Instance; - } - - public Localizer T { get; set; } + public Localizer T { get; set; } = NullLocalizer.Instance; #endregion @@ -229,11 +225,11 @@ private UrlRecordCollection CreateUrlRecordCollection(string entityName, IEnumer } private void SetProgress(DataExporterContext ctx, int loadedRecords) - { - try - { - if (!ctx.IsPreview && loadedRecords > 0) - { + { + try + { + if (!ctx.IsPreview && loadedRecords > 0) + { var totalRecords = ctx.StatsPerStore.Sum(x => x.Value.TotalRecords); if (ctx.Request.Profile.Limit > 0 && totalRecords > ctx.Request.Profile.Limit) @@ -241,195 +237,143 @@ private void SetProgress(DataExporterContext ctx, int loadedRecords) 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")); - ctx.Request.ProgressValueSetter.Invoke(ctx.RecordCount, totalRecords, msg); - } - } - catch { } - } - - private void SetProgress(DataExporterContext ctx, string message) - { - try - { - if (!ctx.IsPreview && message.HasValue()) - { - ctx.Request.ProgressValueSetter.Invoke(0, 0, message); - } - } - catch { } - } - - private bool HasPermission(DataExporterContext ctx) - { - if (ctx.Request.HasPermission) - { - return true; - } - - var customer = _services.WorkContext.CurrentCustomer; - - if (customer.SystemName == SystemCustomerNames.BackgroundTask) - { - return true; - } - - switch (ctx.Request.Provider.Value.EntityType) - { - case ExportEntityType.Product: - case ExportEntityType.Category: - case ExportEntityType.Manufacturer: - return _services.Permissions.Authorize(StandardPermissionProvider.ManageCatalog, customer); - - case ExportEntityType.Customer: - return _services.Permissions.Authorize(StandardPermissionProvider.ManageCustomers, customer); - - case ExportEntityType.Order: - case ExportEntityType.ShoppingCartItem: - return _services.Permissions.Authorize(StandardPermissionProvider.ManageOrders, customer); - - case ExportEntityType.NewsLetterSubscription: - return _services.Permissions.Authorize(StandardPermissionProvider.ManageNewsletterSubscribers, customer); - } - - return true; - } - - private void StreamToFile(DataExporterContext ctx, Stream stream, string path, Action onDisposed) + ctx.RecordCount = Math.Min(ctx.RecordCount + loadedRecords, totalRecords); + var msg = ctx.ProgressInfo.FormatInvariant(ctx.RecordCount.ToString("N0"), totalRecords.ToString("N0")); + ctx.Request.ProgressValueSetter.Invoke(ctx.RecordCount, totalRecords, msg); + } + } + catch { } + } + + private void SetProgress(DataExporterContext ctx, string message) { - if (stream != null) + try { - try + if (!ctx.IsPreview && message.HasValue()) + { + ctx.Request.ProgressValueSetter.Invoke(0, 0, message); + } + } + catch { } + } + + private bool HasPermission(DataExporterContext ctx) + { + if (ctx.Request.HasPermission) + { + return true; + } + + var customer = _services.WorkContext.CurrentCustomer; + + if (customer.SystemName == SystemCustomerNames.BackgroundTask) + { + return true; + } + + return _services.Permissions.Authorize(Permissions.Configuration.Export.Execute); + } + + private void DetachAllEntitiesAndClear(DataExporterContext ctx) + { + try + { + ctx.AssociatedProductContext?.Clear(); + + if (ctx.ProductExportContext != null) { - if (ctx.IsFileBasedExport && path.HasValue() && stream.Length > 0) + _dbContext.DetachEntities(x => { - if (!stream.CanSeek) - { - ctx.Log.Warn("Data stream seems to be closed!"); - } + return x is Product || x is Discount || x is ProductVariantAttributeCombination || x is ProductVariantAttribute || x is ProductVariantAttributeValue || x is ProductAttribute || + x is MediaFile || x is ProductBundleItem || x is ProductBundleItemAttributeFilter || x is ProductCategory || x is ProductManufacturer || x is Category || x is Manufacturer || + x is ProductMediaFile || x is ProductTag || x is ProductSpecificationAttribute || x is SpecificationAttributeOption || x is SpecificationAttribute || x is TierPrice || x is ProductReview || + x is ProductReviewHelpfulness || x is DeliveryTime || x is QuantityUnit || x is Download || x is MediaStorage || x is GenericAttribute || x is UrlRecord; + }); - stream.Seek(0, SeekOrigin.Begin); + ctx.ProductExportContext.Clear(); + } - using (_rwLock.GetWriteLock()) - using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) - { - stream.CopyTo(fileStream); - } - } + if (ctx.OrderExportContext != null) + { + _dbContext.DetachEntities(x => + { + return x is Order || x is Address || x is GenericAttribute || x is Customer || + x is OrderItem || x is RewardPointsHistory || x is Shipment || x is ProductVariantAttributeCombination; + }); + + ctx.OrderExportContext.Clear(); } - catch (Exception ex) + + if (ctx.CategoryExportContext != null) { - ctx.ExecuteContext.Abort = DataExchangeAbortion.Hard; - ctx.Log.ErrorFormat(ex, $"Failed to stream file {path}."); - ctx.Result.LastError = ex.ToAllMessages(true); + _dbContext.DetachEntities(x => + { + return x is Category || x is MediaFile || x is ProductCategory; + }); + + ctx.CategoryExportContext.Clear(); } - finally + + if (ctx.ManufacturerExportContext != null) { - stream.Dispose(); - onDisposed(stream); + _dbContext.DetachEntities(x => + { + return x is Manufacturer || x is MediaFile || x is ProductManufacturer; + }); + + ctx.ManufacturerExportContext.Clear(); } - } - if (ctx.ExecuteContext.Abort == DataExchangeAbortion.Hard && ctx.IsFileBasedExport && path.HasValue()) + if (ctx.CustomerExportContext != null) + { + _dbContext.DetachEntities(x => + { + return x is Customer || x is GenericAttribute || x is CustomerContent; + }); + + ctx.CustomerExportContext.Clear(); + } + + switch (ctx.Request.Provider.Value.EntityType) + { + case ExportEntityType.ShoppingCartItem: + _dbContext.DetachEntities(x => + { + return x is ShoppingCartItem || x is Customer || x is Product || x is ProductVariantAttributeCombination; + }); + break; + case ExportEntityType.NewsLetterSubscription: + _dbContext.DetachEntities(x => + { + return x is NewsLetterSubscription || x is Customer; + }); + break; + } + } + catch (Exception ex) { - FileSystemHelper.DeleteFile(path); + ctx.Log.Warn(ex, "Detaching entities failed."); } - } - private void DetachAllEntitiesAndClear(DataExporterContext ctx) - { - try - { - ctx.AssociatedProductContext?.Clear(); - ctx.TranslationsPerPage?.Clear(); - ctx.UrlRecordsPerPage?.Clear(); + //((DbContext)_dbContext).DumpAttachedEntities(); + } - if (ctx.ProductExportContext != null) - { - _dbContext.DetachEntities(x => - { - return x is Product || x is Discount || x is ProductVariantAttributeCombination || x is ProductVariantAttribute || - x is Picture || x is ProductBundleItem || x is ProductCategory || x is ProductManufacturer || - x is ProductPicture || x is ProductTag || x is ProductSpecificationAttribute || x is TierPrice; - }); - - ctx.ProductExportContext.Clear(); - } - - if (ctx.OrderExportContext != null) - { - _dbContext.DetachEntities(x => - { - return x is Order || x is Address || x is GenericAttribute || x is Customer || - x is OrderItem || x is RewardPointsHistory || x is Shipment; - }); - - ctx.OrderExportContext.Clear(); - } - - if (ctx.CategoryExportContext != null) - { - _dbContext.DetachEntities(x => - { - return x is Category || x is Picture || x is ProductCategory; - }); - - ctx.CategoryExportContext.Clear(); - } - - if (ctx.ManufacturerExportContext != null) - { - _dbContext.DetachEntities(x => - { - return x is Manufacturer || x is Picture || x is ProductManufacturer; - }); - - ctx.ManufacturerExportContext.Clear(); - } - - if (ctx.CustomerExportContext != null) - { - _dbContext.DetachEntities(x => - { - return x is Customer || x is GenericAttribute || x is CustomerContent; - }); - - ctx.CustomerExportContext.Clear(); - } - - if (ctx.Request.Provider.Value.EntityType == ExportEntityType.ShoppingCartItem) - { - _dbContext.DetachEntities(x => - { - return x is ShoppingCartItem || x is Customer || x is Product; - }); - } - } - catch (Exception ex) - { - ctx.Log.Warn(ex, "Detaching entities failed."); - } - } - - private IExportDataSegmenterProvider CreateSegmenter(DataExporterContext ctx) - { + private IExportDataSegmenterProvider CreateSegmenter(DataExporterContext ctx) + { var stats = ctx.StatsPerStore[ctx.Store.Id]; var offset = Math.Max(ctx.Request.Profile.Offset, 0); - var limit = Math.Max(ctx.Request.Profile.Limit, 0); - var recordsPerSegment = ctx.IsPreview ? 0 : Math.Max(ctx.Request.Profile.BatchSize, 0); + 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) - { - case ExportEntityType.Product: - ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter - ( - () => GetProducts(ctx), - entities => - { + { + case ExportEntityType.Product: + ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter + ( + () => GetProducts(ctx), + entities => + { // Load data behind navigation properties for current queue in one go. ctx.ProductExportContext = CreateProductExportContext(entities, ctx.ContextCustomer, ctx.Store.Id); ctx.AssociatedProductContext = null; @@ -471,25 +415,25 @@ private IExportDataSegmenterProvider CreateSegmenter(DataExporterContext ctx) ctx.TranslationsPerPage[nameof(ProductAttribute)] = CreateTranslationCollection(nameof(ProductAttribute), pa); ctx.TranslationsPerPage[nameof(ProductVariantAttributeValue)] = CreateTranslationCollection(nameof(ProductVariantAttributeValue), pvav); }, - entity => Convert(ctx, entity), - offset, PageSize, limit, recordsPerSegment, totalRecords - ); - break; - - case ExportEntityType.Order: - ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter - ( - () => GetOrders(ctx), - entities => - { - ctx.OrderExportContext = new OrderExportContext(entities, - x => _customerService.GetCustomersByIds(x), - x => _genericAttributeService.Value.GetAttributesForEntity(x, "Customer"), - x => _customerService.GetRewardPointsHistoriesByCustomerIds(x), - x => _addressService.Value.GetAddressByIds(x), - x => _orderService.Value.GetOrderItemsByOrderIds(x), - x => _shipmentService.Value.GetShipmentsByOrderIds(x) - ); + entity => Convert(ctx, entity), + offset, PageSize, limit, recordsPerSegment, totalRecords + ); + break; + + case ExportEntityType.Order: + ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter + ( + () => GetOrders(ctx), + entities => + { + ctx.OrderExportContext = new OrderExportContext(entities, + x => _customerService.GetCustomersByIds(x), + x => _genericAttributeService.Value.GetAttributesForEntity(x, "Customer"), + x => _customerService.GetRewardPointsHistoriesByCustomerIds(x), + x => _addressService.Value.GetAddressByIds(x), + x => _orderService.Value.GetOrderItemsByOrderIds(x), + x => _shipmentService.Value.GetShipmentsByOrderIds(x) + ); ctx.OrderExportContext.OrderItems.LoadAll(); @@ -499,72 +443,72 @@ private IExportDataSegmenterProvider CreateSegmenter(DataExporterContext ctx) 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 - ); - break; - - case ExportEntityType.Manufacturer: - ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter - ( - () => GetManufacturers(ctx), - entities => - { - ctx.ManufacturerExportContext = new ManufacturerExportContext(entities, - x => _manufacturerService.Value.GetProductManufacturersByManufacturerIds(x), - x => _pictureService.Value.GetPicturesByIds(x) - ); - }, - entity => Convert(ctx, entity), - offset, PageSize, limit, recordsPerSegment, totalRecords - ); - break; - - case ExportEntityType.Category: - ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter - ( - () => GetCategories(ctx), - entities => - { - ctx.CategoryExportContext = new CategoryExportContext(entities, - x => _categoryService.Value.GetProductCategoriesByCategoryIds(x), - x => _pictureService.Value.GetPicturesByIds(x) - ); - }, - entity => Convert(ctx, entity), - offset, PageSize, limit, recordsPerSegment, totalRecords - ); - break; - - case ExportEntityType.Customer: - ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter - ( - () => GetCustomers(ctx), - entities => - { - ctx.CustomerExportContext = new CustomerExportContext(entities, - x => _genericAttributeService.Value.GetAttributesForEntity(x, "Customer") - ); - }, - entity => Convert(ctx, entity), - offset, PageSize, limit, recordsPerSegment, totalRecords - ); - break; - - case ExportEntityType.NewsLetterSubscription: - ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter - ( - () => GetNewsLetterSubscriptions(ctx), - null, - entity => Convert(ctx, entity), - offset, PageSize, limit, recordsPerSegment, totalRecords - ); - break; - - case ExportEntityType.ShoppingCartItem: - ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter - ( - () => GetShoppingCartItems(ctx), + entity => Convert(ctx, entity), + offset, PageSize, limit, recordsPerSegment, totalRecords + ); + break; + + case ExportEntityType.Manufacturer: + ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter + ( + () => GetManufacturers(ctx), + entities => + { + ctx.ManufacturerExportContext = new ManufacturerExportContext(entities, + x => _manufacturerService.Value.GetProductManufacturersByManufacturerIds(x, true), + x => _mediaService.Value.GetFilesByIds(x) + ); + }, + entity => Convert(ctx, entity), + offset, PageSize, limit, recordsPerSegment, totalRecords + ); + break; + + case ExportEntityType.Category: + ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter + ( + () => GetCategories(ctx), + entities => + { + ctx.CategoryExportContext = new CategoryExportContext(entities, + x => _categoryService.Value.GetProductCategoriesByCategoryIds(x), + x => _mediaService.Value.GetFilesByIds(x) + ); + }, + entity => Convert(ctx, entity), + offset, PageSize, limit, recordsPerSegment, totalRecords + ); + break; + + case ExportEntityType.Customer: + ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter + ( + () => GetCustomers(ctx), + entities => + { + ctx.CustomerExportContext = new CustomerExportContext(entities, + x => _genericAttributeService.Value.GetAttributesForEntity(x, "Customer") + ); + }, + entity => Convert(ctx, entity), + offset, PageSize, limit, recordsPerSegment, totalRecords + ); + break; + + case ExportEntityType.NewsLetterSubscription: + ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter + ( + () => GetNewsLetterSubscriptions(ctx), + null, + entity => Convert(ctx, entity), + offset, PageSize, limit, recordsPerSegment, totalRecords + ); + break; + + case ExportEntityType.ShoppingCartItem: + ctx.ExecuteContext.DataSegmenter = new ExportDataSegmenter + ( + () => GetShoppingCartItems(ctx), entities => { var products = entities.Select(x => x.Product); @@ -572,23 +516,33 @@ private IExportDataSegmenterProvider CreateSegmenter(DataExporterContext ctx) ctx.UrlRecordsPerPage[nameof(Product)] = CreateUrlRecordCollection(nameof(Product), products); }, entity => Convert(ctx, entity), - offset, PageSize, limit, recordsPerSegment, totalRecords - ); - break; + offset, PageSize, limit, recordsPerSegment, totalRecords + ); + break; - default: - ctx.ExecuteContext.DataSegmenter = null; - break; - } + default: + ctx.ExecuteContext.DataSegmenter = null; + break; + } - return ctx.ExecuteContext.DataSegmenter as IExportDataSegmenterProvider; - } + return ctx.ExecuteContext.DataSegmenter as IExportDataSegmenterProvider; + } + + private Stream CreateStream(string path) + { + if (path.HasValue()) + { + return new FileStream(path, FileMode.Create, FileAccess.Write); + } + + return new MemoryStream(); + } private IEnumerable GetDataUnitsForRelatedEntities(DataExporterContext ctx) { // Related data is data without own export provider or importer. For a flat formatted export // they have to be exported together with metadata to know what to be edited. - RelatedEntityType[] types = null; + RelatedEntityType[] types; switch (ctx.Request.Provider.Value.EntityType) { @@ -610,6 +564,12 @@ private IEnumerable GetDataUnitsForRelatedEntities(DataExporterC foreach (var type in types) { + var unit = new ExportDataUnit + { + RelatedType = type, + DisplayInFileDialog = true + }; + // Convention: Must end with type name because that's how the import identifies the entity. // Be careful in case of accidents with file names. They must not be too long. var fileName = $"{ctx.Store.Id}-{context.FileIndex.ToString("D4")}-{type.ToString()}"; @@ -619,314 +579,342 @@ private IEnumerable GetDataUnitsForRelatedEntities(DataExporterC fileName = $"{CommonHelper.GenerateRandomDigitCode(4)}-{fileName}"; } - result.Add(new ExportDataUnit - { - RelatedType = type, - DisplayInFileDialog = true, - FileName = fileName + fileExtension, - DataStream = new MemoryStream() - }); + unit.FileName = fileName + fileExtension; + unit.DataStream = new ExportFileStream(CreateStream(Path.Combine(context.Folder, unit.FileName))); + + result.Add(unit); } return result; } - private bool CallProvider(DataExporterContext ctx, string method, string path) - { - if (method != "Execute" && method != "OnExecuted") - { - throw new SmartException($"Unknown export method {method.NaIfEmpty()}."); - } - - var context = ctx.ExecuteContext; - var provider = ctx.Request.Provider.Value; - - try - { - context.DataStream = new MemoryStream(); + private bool CallProvider(DataExporterContext ctx, string method, string path) + { + if (method != "Execute" && method != "OnExecuted") + { + throw new SmartException($"Unknown export method {method.NaIfEmpty()}."); + } - if (method == "Execute") - { - provider.Execute(context); - } - else if (method == "OnExecuted") + try + { + using (ctx.ExecuteContext.DataStream = new ExportFileStream(CreateStream(ctx.IsFileBasedExport ? path : null))) { - provider.OnExecuted(context); + if (method == "Execute") + { + ctx.Request.Provider.Value.Execute(ctx.ExecuteContext); + } + else if (method == "OnExecuted") + { + ctx.Request.Provider.Value.OnExecuted(ctx.ExecuteContext); + } } } 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); - } - finally - { - StreamToFile(ctx, context.DataStream, path, x => context.DataStream = null); + { + ctx.ExecuteContext.Abort = DataExchangeAbortion.Hard; + ctx.Log.ErrorFormat(ex, $"The provider failed at the {method.NaIfEmpty()} method."); + ctx.Result.LastError = ex.ToAllMessages(true); + } + finally + { + ctx.ExecuteContext.DataStream?.Dispose(); + ctx.ExecuteContext.DataStream = null; + + if (ctx.ExecuteContext.Abort == DataExchangeAbortion.Hard && ctx.IsFileBasedExport) + { + FileSystemHelper.DeleteFile(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()}."); + var unitFileInfos = new StringBuilder(); + var relatedDataUnits = ctx.ExecuteContext.ExtraDataUnits + .Where(x => x.RelatedType.HasValue && x.FileName.HasValue() && x.DataStream != null) + .ToList(); - foreach (var unit in context.ExtraDataUnits.Where(x => x.RelatedType.HasValue && x.DataStream != null)) + foreach (var unit in relatedDataUnits) { // 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); + unit.DataStream?.Dispose(); + unit.DataStream = null; + + var unitPath = Path.Combine(ctx.ExecuteContext.Folder, unit.FileName); + + if (ctx.ExecuteContext.Abort == DataExchangeAbortion.Hard) + { + if (ctx.IsFileBasedExport) + { + FileSystemHelper.DeleteFile(unitPath); + } + } + else + { + unitFileInfos.Append($"\r\nProvider reports {unit.RecordsSucceeded.ToString("N0")} successfully exported record(s) of type {unit.RelatedType.Value.ToString()} to {unitPath}."); + } + } + + if (ctx.ExecuteContext.Abort != DataExchangeAbortion.Hard) + { + ctx.Log.Info($"Provider reports {ctx.ExecuteContext.RecordsSucceeded.ToString("N0")} successfully exported record(s) of type {ctx.Request.Provider.Value.EntityType.ToString()} to {path.NaIfEmpty()}."); + } + + ctx.Log.Info(unitFileInfos.ToString()); + } + } + + return ctx.ExecuteContext.Abort != DataExchangeAbortion.Hard; + } + + private bool Deploy(DataExporterContext ctx, string zipPath) + { + var allSucceeded = true; + var deployments = ctx.Request.Profile.Deployments.OrderBy(x => x.DeploymentTypeId).Where(x => x.Enabled); + + if (deployments.Count() == 0) + return false; + + var context = new ExportDeploymentContext + { + T = T, + Log = ctx.Log, + FolderContent = ctx.FolderContent, + ZipPath = zipPath, + CreateZipArchive = ctx.Request.Profile.CreateZipArchive + }; + + foreach (var deployment in deployments) + { + IFilePublisher publisher = null; + + context.Result = new DataDeploymentResult + { + LastExecutionUtc = DateTime.UtcNow + }; + + try + { + switch (deployment.DeploymentType) + { + case ExportDeploymentType.Email: + publisher = new EmailFilePublisher(_emailAccountService.Value, _queuedEmailService.Value); + break; + case ExportDeploymentType.FileSystem: + publisher = new FileSystemFilePublisher(); + break; + case ExportDeploymentType.Ftp: + publisher = new FtpFilePublisher(); + break; + case ExportDeploymentType.Http: + publisher = new HttpFilePublisher(); + break; + case ExportDeploymentType.PublicFolder: + publisher = new PublicFolderPublisher(); + break; + } + + if (publisher != null) + { + publisher.Publish(context, deployment); + + if (!context.Result.Succeeded) + allSucceeded = false; + } + } + catch (Exception ex) + { + allSucceeded = false; - sb.Append($"\r\nProvider reports {unit.RecordsSucceeded.ToString("N0")} successfully exported record(s) of type {unit.RelatedType.Value.ToString()} to {unitPath.NaIfEmpty()}."); + if (context.Result != null) + { + context.Result.LastError = ex.ToAllMessages(true); } - ctx.Log.Info(sb.ToString()); + ctx.Log.Error(ex, $"Deployment \"{deployment.Name}\" of type {deployment.DeploymentType.ToString()} failed."); + } + + deployment.ResultInfo = XmlHelper.Serialize(context.Result); + + _exportProfileService.Value.UpdateExportDeployment(deployment); + } + + return allSucceeded; + } + + private void SendCompletionEmail(DataExporterContext ctx, string zipPath) + { + var emailAccount = _emailAccountService.Value.GetEmailAccountById(ctx.Request.Profile.EmailAccountId); + if (emailAccount == null) + { + return; + } + + var downloadUrl = "{0}Admin/Export/DownloadExportFile/{1}?name=".FormatInvariant(_services.WebHelper.GetStoreLocation(ctx.Store.SslEnabled), ctx.Request.Profile.Id); + var languageId = ctx.Projection.LanguageId ?? 0; + var smtpContext = new SmtpContext(emailAccount); + var message = new EmailMessage(); + + var storeInfo = "{0} ({1})".FormatInvariant(ctx.Store.Name, ctx.Store.Url); + var intro = _services.Localization.GetResource("Admin.DataExchange.Export.CompletedEmail.Body", languageId).FormatInvariant(storeInfo); + var body = new StringBuilder(intro); + + if (ctx.Result.LastError.HasValue()) + { + body.AppendFormat("

{0}

", ctx.Result.LastError); + } + + if (ctx.IsFileBasedExport && File.Exists(zipPath)) + { + var fileName = Path.GetFileName(zipPath); + body.AppendFormat("

{2}

", downloadUrl, HttpUtility.UrlEncode(fileName), fileName); + } + + if (ctx.IsFileBasedExport && ctx.Result.Files.Any()) + { + body.Append("

"); + foreach (var file in ctx.Result.Files) + { + body.AppendFormat("

", downloadUrl, HttpUtility.UrlEncode(file.FileName), file.FileName); } - } - - return context.Abort != DataExchangeAbortion.Hard; - } - - private bool Deploy(DataExporterContext ctx, string zipPath) - { - var allSucceeded = true; - var deployments = ctx.Request.Profile.Deployments.OrderBy(x => x.DeploymentTypeId).Where(x => x.Enabled); - - if (deployments.Count() == 0) - return false; - - var context = new ExportDeploymentContext - { - T = T, - Log = ctx.Log, - FolderContent = ctx.FolderContent, - ZipPath = zipPath, - CreateZipArchive = ctx.Request.Profile.CreateZipArchive - }; - - foreach (var deployment in deployments) - { - IFilePublisher publisher = null; - - context.Result = new DataDeploymentResult - { - LastExecutionUtc = DateTime.UtcNow - }; - - try - { - switch (deployment.DeploymentType) - { - case ExportDeploymentType.Email: - publisher = new EmailFilePublisher(_emailAccountService.Value, _queuedEmailService.Value); - break; - case ExportDeploymentType.FileSystem: - publisher = new FileSystemFilePublisher(); - break; - case ExportDeploymentType.Ftp: - publisher = new FtpFilePublisher(); - break; - case ExportDeploymentType.Http: - publisher = new HttpFilePublisher(); - break; - case ExportDeploymentType.PublicFolder: - publisher = new PublicFolderPublisher(); - break; - } - - if (publisher != null) - { - publisher.Publish(context, deployment); - - if (!context.Result.Succeeded) - allSucceeded = false; - } - } - catch (Exception ex) - { - allSucceeded = false; - - if (context.Result != null) - { - context.Result.LastError = ex.ToAllMessages(true); - } - - ctx.Log.Error(ex, $"Deployment \"{deployment.Name}\" of type {deployment.DeploymentType.ToString()} failed."); - } - - deployment.ResultInfo = XmlHelper.Serialize(context.Result); - - _exportProfileService.Value.UpdateExportDeployment(deployment); - } - - return allSucceeded; - } - - private void SendCompletionEmail(DataExporterContext ctx, string zipPath) - { - var emailAccount = _emailAccountService.Value.GetEmailAccountById(ctx.Request.Profile.EmailAccountId); - if (emailAccount == null) - { - return; - } - - var downloadUrl = "{0}Admin/Export/DownloadExportFile/{1}?name=".FormatInvariant(_services.WebHelper.GetStoreLocation(ctx.Store.SslEnabled), ctx.Request.Profile.Id); - var languageId = ctx.Projection.LanguageId ?? 0; - var smtpContext = new SmtpContext(emailAccount); - var message = new EmailMessage(); - - var storeInfo = "{0} ({1})".FormatInvariant(ctx.Store.Name, ctx.Store.Url); - var intro =_services.Localization.GetResource("Admin.DataExchange.Export.CompletedEmail.Body", languageId).FormatInvariant(storeInfo); - var body = new StringBuilder(intro); - - if (ctx.Result.LastError.HasValue()) - { - body.AppendFormat("

{0}

", ctx.Result.LastError); - } - - if (ctx.IsFileBasedExport && File.Exists(zipPath)) - { - var fileName = Path.GetFileName(zipPath); - body.AppendFormat("

{2}

", downloadUrl, HttpUtility.UrlEncode(fileName), fileName); - } - - if (ctx.IsFileBasedExport && ctx.Result.Files.Any()) - { - body.Append("

"); - foreach (var file in ctx.Result.Files) - { - body.AppendFormat("

", downloadUrl, HttpUtility.UrlEncode(file.FileName), file.FileName); - } - body.Append("

"); - } - - message.From = new EmailAddress(emailAccount.Email, emailAccount.DisplayName); - - if (ctx.Request.Profile.CompletedEmailAddresses.HasValue()) - message.To.AddRange(ctx.Request.Profile.CompletedEmailAddresses.SplitSafe(",").Where(x => x.IsEmail()).Select(x => new EmailAddress(x))); - - if (message.To.Count == 0 && _contactDataSettings.Value.CompanyEmailAddress.HasValue()) - message.To.Add(new EmailAddress(_contactDataSettings.Value.CompanyEmailAddress)); - - if (message.To.Count == 0) - message.To.Add(new EmailAddress(emailAccount.Email, emailAccount.DisplayName)); - - message.Subject = _services.Localization.GetResource("Admin.DataExchange.Export.CompletedEmail.Subject", languageId) - .FormatInvariant(ctx.Request.Profile.Name); - - message.Body = body.ToString(); - - _emailSender.Value.SendEmail(smtpContext, message); - - //_queuedEmailService.Value.InsertQueuedEmail(new QueuedEmail - //{ - // From = emailAccount.Email, - // FromName = emailAccount.DisplayName, - // To = message.To.First().Address, - // Subject = message.Subject, - // Body = message.Body, - // CreatedOnUtc = DateTime.UtcNow, - // EmailAccountId = emailAccount.Id, - // SendManually = true - //}); - //_dbContext.SaveChanges(); - } - - #endregion - - #region Getting data - - public virtual ProductExportContext CreateProductExportContext( - IEnumerable products = null, - Customer customer = null, - int? storeId = null, - int? maxPicturesPerProduct = null, - bool showHidden = true) - { - if (customer == null) - customer = _services.WorkContext.CurrentCustomer; - - if (!storeId.HasValue) - storeId = _services.StoreContext.CurrentStore.Id; - - var context = new ProductExportContext(products, - x => _productAttributeService.Value.GetProductVariantAttributesByProductIds(x, null), - x => _productAttributeService.Value.GetProductVariantAttributeCombinations(x), - x => _specificationAttributeService.Value.GetProductSpecificationAttributesByProductIds(x), - x => _productService.Value.GetTierPricesByProductIds(x, customer, storeId.GetValueOrDefault()), - x => _categoryService.Value.GetProductCategoriesByProductIds(x, null, showHidden), - x => _manufacturerService.Value.GetProductManufacturersByProductIds(x), - x => _productService.Value.GetAppliedDiscountsByProductIds(x), - x => _productService.Value.GetBundleItemsByProductIds(x, showHidden), - x => _productService.Value.GetAssociatedProductsByProductIds(x), - x => _pictureService.Value.GetPicturesByProductIds(x, maxPicturesPerProduct, true), - x => _productService.Value.GetProductPicturesByProductIds(x), - x => _productService.Value.GetProductTagsByProductIds(x), + body.Append("

"); + } + + message.From = new EmailAddress(emailAccount.Email, emailAccount.DisplayName); + + if (ctx.Request.Profile.CompletedEmailAddresses.HasValue()) + message.To.AddRange(ctx.Request.Profile.CompletedEmailAddresses.SplitSafe(",").Where(x => x.IsEmail()).Select(x => new EmailAddress(x))); + + if (message.To.Count == 0 && _contactDataSettings.Value.CompanyEmailAddress.HasValue()) + message.To.Add(new EmailAddress(_contactDataSettings.Value.CompanyEmailAddress)); + + if (message.To.Count == 0) + message.To.Add(new EmailAddress(emailAccount.Email, emailAccount.DisplayName)); + + message.Subject = _services.Localization.GetResource("Admin.DataExchange.Export.CompletedEmail.Subject", languageId) + .FormatInvariant(ctx.Request.Profile.Name); + + message.Body = body.ToString(); + + _emailSender.Value.SendEmail(smtpContext, message); + + //_queuedEmailService.Value.InsertQueuedEmail(new QueuedEmail + //{ + // From = emailAccount.Email, + // FromName = emailAccount.DisplayName, + // To = message.To.First().Address, + // Subject = message.Subject, + // Body = message.Body, + // CreatedOnUtc = DateTime.UtcNow, + // EmailAccountId = emailAccount.Id, + // SendManually = true + //}); + //_dbContext.SaveChanges(); + } + + #endregion + + #region Getting data + + public virtual ProductExportContext CreateProductExportContext( + IEnumerable products = null, + Customer customer = null, + int? storeId = null, + int? maxPicturesPerProduct = null, + bool includeHidden = true) + { + if (customer == null) + customer = _services.WorkContext.CurrentCustomer; + + if (!storeId.HasValue) + storeId = _services.StoreContext.CurrentStore.Id; + + var context = new ProductExportContext(products, + x => _productAttributeService.Value.GetProductVariantAttributesByProductIds(x, null), + x => _productAttributeService.Value.GetProductVariantAttributeCombinations(x), + x => _specificationAttributeService.Value.GetProductSpecificationAttributesByProductIds(x), + x => _productService.Value.GetTierPricesByProductIds(x, customer, storeId.GetValueOrDefault()), + x => _categoryService.Value.GetProductCategoriesByProductIds(x, null, includeHidden), + x => _manufacturerService.Value.GetProductManufacturersByProductIds(x, includeHidden), + x => _productService.Value.GetAppliedDiscountsByProductIds(x), + x => _productService.Value.GetBundleItemsByProductIds(x, includeHidden), + x => _productService.Value.GetAssociatedProductsByProductIds(x, includeHidden), + x => _productService.Value.GetProductPicturesByProductIds(x, maxPicturesPerProduct), + x => _productService.Value.GetProductTagsByProductIds(x, includeHidden), x => _downloadService.Value.GetDownloadsByEntityIds(x, nameof(Product)) - ); + ); - return context; - } + return context; + } private IQueryable GetProductQuery(DataExporterContext ctx, int? skip, int take) - { - IQueryable query = null; - - if (ctx.Request.ProductQuery == null) - { - var f = ctx.Filter; - var createdFrom = f.CreatedFrom.HasValue ? (DateTime?)_services.DateTimeHelper.ConvertToUtcTime(f.CreatedFrom.Value, _services.DateTimeHelper.CurrentTimeZone) : null; - var createdTo = f.CreatedTo.HasValue ? (DateTime?)_services.DateTimeHelper.ConvertToUtcTime(f.CreatedTo.Value, _services.DateTimeHelper.CurrentTimeZone) : null; - - var searchQuery = new CatalogSearchQuery() - .WithCurrency(ctx.ContextCurrency) - .WithLanguage(ctx.ContextLanguage) - .VisibleIndividuallyOnly(true) - .HasStoreId(ctx.Request.Profile.PerStore ? ctx.Store.Id : f.StoreId) - .PriceBetween(f.PriceMinimum, f.PriceMaximum) - .WithStockQuantity(f.AvailabilityMinimum, f.AvailabilityMaximum) - .CreatedBetween(createdFrom, createdTo); - - if (f.IsPublished.HasValue) - searchQuery = searchQuery.PublishedOnly(f.IsPublished.Value); - - if (f.ProductType.HasValue) - searchQuery = searchQuery.IsProductType(f.ProductType.Value); - - if (f.ProductTagId.HasValue) - searchQuery = searchQuery.WithProductTagIds(f.ProductTagId.Value); - - if (f.WithoutManufacturers.HasValue) - searchQuery = searchQuery.HasAnyManufacturer(!f.WithoutManufacturers.Value); - else if (f.ManufacturerId.HasValue) - searchQuery = searchQuery.WithManufacturerIds(f.FeaturedProducts, f.ManufacturerId.Value); - - if (f.WithoutCategories.HasValue) - searchQuery = searchQuery.HasAnyCategory(!f.WithoutCategories.Value); - else if (f.CategoryIds != null && f.CategoryIds.Length > 0) - searchQuery = searchQuery.WithCategoryIds(f.FeaturedProducts, f.CategoryIds); - - if (ctx.Request.EntitiesToExport.Count > 0) - searchQuery = searchQuery.WithProductIds(ctx.Request.EntitiesToExport.ToArray()); - else - searchQuery = searchQuery.WithProductId(f.IdMinimum, f.IdMaximum); - - query = _catalogSearchService.Value.PrepareQuery(searchQuery); - } - else - { - query = ctx.Request.ProductQuery; - } + { + IQueryable query = null; + + // Skip used for preview (ordinary paging) or initial query (offset option). + var skipValue = skip.GetValueOrDefault(); + if (skipValue == 0 && ctx.LastId == 0) + { + // Initial query. Apply offset. + skipValue = Math.Max(ctx.Request.Profile.Offset, 0); + } + + if (ctx.Request.ProductQuery == null) + { + var f = ctx.Filter; + var createdFrom = f.CreatedFrom.HasValue ? (DateTime?)_services.DateTimeHelper.ConvertToUtcTime(f.CreatedFrom.Value, _services.DateTimeHelper.CurrentTimeZone) : null; + var createdTo = f.CreatedTo.HasValue ? (DateTime?)_services.DateTimeHelper.ConvertToUtcTime(f.CreatedTo.Value, _services.DateTimeHelper.CurrentTimeZone) : null; + + var searchQuery = new CatalogSearchQuery() + .WithCurrency(ctx.ContextCurrency) + .WithLanguage(ctx.ContextLanguage) + .HasStoreId(ctx.Request.Profile.PerStore ? ctx.Store.Id : f.StoreId) + .PriceBetween(f.PriceMinimum, f.PriceMaximum) + .WithStockQuantity(f.AvailabilityMinimum, f.AvailabilityMaximum) + .CreatedBetween(createdFrom, createdTo); + + if (f.Visibility.HasValue) + searchQuery = searchQuery.WithVisibility(f.Visibility.Value); + + if (f.IsPublished.HasValue) + searchQuery = searchQuery.PublishedOnly(f.IsPublished.Value); + + if (f.ProductType.HasValue) + searchQuery = searchQuery.IsProductType(f.ProductType.Value); + + if (f.ProductTagId.HasValue) + searchQuery = searchQuery.WithProductTagIds(f.ProductTagId.Value); + + if (f.WithoutManufacturers.HasValue) + searchQuery = searchQuery.HasAnyManufacturer(!f.WithoutManufacturers.Value); + else if (f.ManufacturerId.HasValue) + searchQuery = searchQuery.WithManufacturerIds(f.FeaturedProducts, f.ManufacturerId.Value); + + if (f.WithoutCategories.HasValue) + searchQuery = searchQuery.HasAnyCategory(!f.WithoutCategories.Value); + else if (f.CategoryIds != null && f.CategoryIds.Length > 0) + searchQuery = searchQuery.WithCategoryIds(f.FeaturedProducts, f.CategoryIds); + + if (ctx.Request.EntitiesToExport.Count > 0) + searchQuery = searchQuery.WithProductIds(ctx.Request.EntitiesToExport.ToArray()); + else + searchQuery = searchQuery.WithProductId(f.IdMinimum, f.IdMaximum); + + query = _catalogSearchService.Value.PrepareQuery(searchQuery); + } + else + { + query = ctx.Request.ProductQuery; + } query = query.OrderBy(x => x.Id); - // Skip required for preview. - var skipValue = skip.GetValueOrDefault(); if (skipValue > 0) { query = query.Skip(() => skipValue); } else if (ctx.LastId > 0) { + // Fast, ID based paging. query = query.Where(x => x.Id > ctx.LastId); } @@ -935,11 +923,13 @@ private IQueryable GetProductQuery(DataExporterContext ctx, int? skip, query = query.Take(() => take); } - return query; - } + return query; + } + + private List GetProducts(DataExporterContext ctx) + { + DetachAllEntitiesAndClear(ctx); - private List GetProducts(DataExporterContext ctx) - { var stats = ctx.StatsPerStore[ctx.Store.Id]; if (ctx.LastId >= stats.MaxId) { @@ -962,25 +952,25 @@ private List GetProducts(DataExporterContext ctx) associatedProducts = _productService.Value.GetAssociatedProductsByProductIds(groupedProductIds, true); } - foreach (var product in products) - { - if (product.ProductType == ProductType.SimpleProduct || product.ProductType == ProductType.BundledProduct) - { + 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); } - } - else if (product.ProductType == ProductType.GroupedProduct) - { - if (ctx.Projection.NoGroupedProducts) - { + } + 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) + if (ctx.Projection.OnlyIndividuallyVisibleAssociated && associatedProduct.Visibility == ProductVisibility.Hidden) { continue; } @@ -995,36 +985,42 @@ private List GetProducts(DataExporterContext ctx) } } } - } - else - { - if (ctx.EntityIdsPerSegment.Add(product.Id)) - { - result.Add(product); - } - } - } - } + } + else + { + if (ctx.EntityIdsPerSegment.Add(product.Id)) + { + result.Add(product); + } + } + } + } ctx.LastId = products.Last().Id; SetProgress(ctx, products.Count); - return result; - } - - private IQueryable GetOrderQuery(DataExporterContext ctx, int? skip, int take) - { - var query = _orderService.Value.GetOrders( - ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId, - ctx.Projection.CustomerId ?? 0, - ctx.Filter.CreatedFrom.HasValue ? (DateTime?)_services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.CreatedFrom.Value, _services.DateTimeHelper.CurrentTimeZone) : null, - ctx.Filter.CreatedTo.HasValue ? (DateTime?)_services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.CreatedTo.Value, _services.DateTimeHelper.CurrentTimeZone) : null, - ctx.Filter.OrderStatusIds, - ctx.Filter.PaymentStatusIds, - ctx.Filter.ShippingStatusIds, - null, - null, - null); + return result; + } + + private IQueryable GetOrderQuery(DataExporterContext ctx, int? skip, int take) + { + var skipValue = skip.GetValueOrDefault(); + if (skipValue == 0 && ctx.LastId == 0) + { + skipValue = Math.Max(ctx.Request.Profile.Offset, 0); + } + + var query = _orderService.Value.GetOrders( + ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId, + ctx.Projection.CustomerId ?? 0, + ctx.Filter.CreatedFrom.HasValue ? (DateTime?)_services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.CreatedFrom.Value, _services.DateTimeHelper.CurrentTimeZone) : null, + ctx.Filter.CreatedTo.HasValue ? (DateTime?)_services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.CreatedTo.Value, _services.DateTimeHelper.CurrentTimeZone) : null, + ctx.Filter.OrderStatusIds, + ctx.Filter.PaymentStatusIds, + ctx.Filter.ShippingStatusIds, + null, + null, + null); if (ctx.Request.EntitiesToExport.Any()) { @@ -1033,7 +1029,6 @@ private IQueryable GetOrderQuery(DataExporterContext ctx, int? skip, int query = query.OrderBy(x => x.Id); - var skipValue = skip.GetValueOrDefault(); if (skipValue > 0) { query = query.Skip(() => skipValue); @@ -1048,11 +1043,13 @@ private IQueryable GetOrderQuery(DataExporterContext ctx, int? skip, int query = query.Take(() => take); } - return query; - } + return query; + } + + private List GetOrders(DataExporterContext ctx) + { + DetachAllEntitiesAndClear(ctx); - private List GetOrders(DataExporterContext ctx) - { var stats = ctx.StatsPerStore[ctx.Store.Id]; if (ctx.LastId >= stats.MaxId) { @@ -1066,21 +1063,28 @@ private List GetOrders(DataExporterContext ctx) return null; } - if (ctx.Projection.OrderStatusChange != ExportOrderStatusChange.None) - { - ctx.SetLoadedEntityIds(orders.Select(x => x.Id)); - } + if (ctx.Projection.OrderStatusChange != ExportOrderStatusChange.None) + { + ctx.SetLoadedEntityIds(orders.Select(x => x.Id)); + } ctx.LastId = orders.Last().Id; SetProgress(ctx, orders.Count); - return orders; - } + return orders; + } + + private IQueryable GetManufacturerQuery(DataExporterContext ctx, int? skip, int take) + { + var storeId = ctx.Request.Profile.PerStore ? ctx.Store.Id : 0; + + var skipValue = skip.GetValueOrDefault(); + if (skipValue == 0 && ctx.LastId == 0) + { + skipValue = Math.Max(ctx.Request.Profile.Offset, 0); + } - 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); + var query = _manufacturerService.Value.GetManufacturers(true, storeId); if (ctx.Request.EntitiesToExport.Any()) { @@ -1089,7 +1093,6 @@ private IQueryable GetManufacturerQuery(DataExporterContext ctx, i query = query.OrderBy(x => x.Id); - var skipValue = skip.GetValueOrDefault(); if (skipValue > 0) { query = query.Skip(() => skipValue); @@ -1104,11 +1107,13 @@ private IQueryable GetManufacturerQuery(DataExporterContext ctx, i query = query.Take(() => take); } - return query; - } + return query; + } + + private List GetManufacturers(DataExporterContext ctx) + { + DetachAllEntitiesAndClear(ctx); - private List GetManufacturers(DataExporterContext ctx) - { var stats = ctx.StatsPerStore[ctx.Store.Id]; if (ctx.LastId >= stats.MaxId) { @@ -1125,13 +1130,20 @@ private List GetManufacturers(DataExporterContext ctx) ctx.LastId = manus.Last().Id; SetProgress(ctx, manus.Count); - return manus; - } + return manus; + } + + private IQueryable GetCategoryQuery(DataExporterContext ctx, int? skip, int take) + { + var storeId = ctx.Request.Profile.PerStore ? ctx.Store.Id : 0; + + var skipValue = skip.GetValueOrDefault(); + if (skipValue == 0 && ctx.LastId == 0) + { + skipValue = Math.Max(ctx.Request.Profile.Offset, 0); + } - 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); + var query = _categoryService.Value.BuildCategoriesQuery(null, true, null, storeId); if (ctx.Request.EntitiesToExport.Any()) { @@ -1140,7 +1152,6 @@ private IQueryable GetCategoryQuery(DataExporterContext ctx, int? skip query = query.OrderBy(x => x.Id); - var skipValue = skip.GetValueOrDefault(); if (skipValue > 0) { query = query.Skip(() => skipValue); @@ -1155,11 +1166,13 @@ private IQueryable GetCategoryQuery(DataExporterContext ctx, int? skip query = query.Take(() => take); } - return query; - } + return query; + } + + private List GetCategories(DataExporterContext ctx) + { + DetachAllEntitiesAndClear(ctx); - private List GetCategories(DataExporterContext ctx) - { var stats = ctx.StatsPerStore[ctx.Store.Id]; if (ctx.LastId >= stats.MaxId) { @@ -1176,18 +1189,24 @@ private List GetCategories(DataExporterContext ctx) ctx.LastId = categories.Last().Id; SetProgress(ctx, categories.Count); - return categories; - } + return categories; + } + + private IQueryable GetCustomerQuery(DataExporterContext ctx, int? skip, int take) + { + var skipValue = skip.GetValueOrDefault(); + if (skipValue == 0 && ctx.LastId == 0) + { + skipValue = Math.Max(ctx.Request.Profile.Offset, 0); + } - private IQueryable GetCustomerQuery(DataExporterContext ctx, int? skip, int take) - { - var query = _customerRepository.Value.TableUntracked - .Expand(x => x.BillingAddress) - .Expand(x => x.ShippingAddress) - .Expand(x => x.Addresses.Select(y => y.Country)) - .Expand(x => x.Addresses.Select(y => y.StateProvince)) - .Expand(x => x.CustomerRoles) - .Where(x => !x.Deleted); + var query = _customerRepository.Value.TableUntracked + .Expand(x => x.BillingAddress) + .Expand(x => x.ShippingAddress) + .Expand(x => x.Addresses.Select(y => y.Country)) + .Expand(x => x.Addresses.Select(y => y.StateProvince)) + .Expand(x => x.CustomerRoleMappings.Select(rm => rm.CustomerRole)) + .Where(x => !x.Deleted); if (ctx.Filter.IsActiveCustomer.HasValue) { @@ -1201,58 +1220,58 @@ private IQueryable GetCustomerQuery(DataExporterContext ctx, int? skip if (ctx.Filter.CustomerRoleIds != null && ctx.Filter.CustomerRoleIds.Any()) { - query = query.Where(x => x.CustomerRoles.Select(y => y.Id).Intersect(ctx.Filter.CustomerRoleIds).Any()); + query = query.Where(x => x.CustomerRoleMappings.Select(y => y.CustomerRoleId).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)); + query = query.Where(x => x.BillingAddress != null && ctx.Filter.BillingCountryIds.Contains(x.BillingAddress.CountryId ?? 0)); } 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.LastActivityFrom.HasValue) - { - var activityFrom = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.LastActivityFrom.Value, _services.DateTimeHelper.CurrentTimeZone); - query = query.Where(x => activityFrom <= x.LastActivityDateUtc); - } - - if (ctx.Filter.LastActivityTo.HasValue) - { - var activityTo = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.LastActivityTo.Value, _services.DateTimeHelper.CurrentTimeZone); - query = query.Where(x => activityTo >= x.LastActivityDateUtc); - } - - if (ctx.Filter.HasSpentAtLeastAmount.HasValue) - { - query = query - .Join(_orderRepository.Value.Table, x => x.Id, y => y.CustomerId, (x, y) => new { Customer = x, Order = y }) - .GroupBy(x => x.Customer.Id) - .Select(x => new - { - Customer = x.FirstOrDefault().Customer, - OrderTotal = x.Sum(y => y.Order.OrderTotal) - }) - .Where(x => x.OrderTotal >= ctx.Filter.HasSpentAtLeastAmount.Value) - .Select(x => x.Customer); - } - - if (ctx.Filter.HasPlacedAtLeastOrders.HasValue) - { - query = query - .Join(_orderRepository.Value.Table, x => x.Id, y => y.CustomerId, (x, y) => new { Customer = x, Order = y }) - .GroupBy(x => x.Customer.Id) - .Select(x => new - { - Customer = x.FirstOrDefault().Customer, - OrderCount = x.Count() - }) - .Where(x => x.OrderCount >= ctx.Filter.HasPlacedAtLeastOrders.Value) - .Select(x => x.Customer); - } + query = query.Where(x => x.ShippingAddress != null && ctx.Filter.ShippingCountryIds.Contains(x.ShippingAddress.CountryId ?? 0)); + } + + if (ctx.Filter.LastActivityFrom.HasValue) + { + var activityFrom = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.LastActivityFrom.Value, _services.DateTimeHelper.CurrentTimeZone); + query = query.Where(x => activityFrom <= x.LastActivityDateUtc); + } + + if (ctx.Filter.LastActivityTo.HasValue) + { + var activityTo = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.LastActivityTo.Value, _services.DateTimeHelper.CurrentTimeZone); + query = query.Where(x => activityTo >= x.LastActivityDateUtc); + } + + if (ctx.Filter.HasSpentAtLeastAmount.HasValue) + { + query = query + .Join(_orderRepository.Value.Table, x => x.Id, y => y.CustomerId, (x, y) => new { Customer = x, Order = y }) + .GroupBy(x => x.Customer.Id) + .Select(x => new + { + Customer = x.FirstOrDefault().Customer, + OrderTotal = x.Sum(y => y.Order.OrderTotal) + }) + .Where(x => x.OrderTotal >= ctx.Filter.HasSpentAtLeastAmount.Value) + .Select(x => x.Customer); + } + + if (ctx.Filter.HasPlacedAtLeastOrders.HasValue) + { + query = query + .Join(_orderRepository.Value.Table, x => x.Id, y => y.CustomerId, (x, y) => new { Customer = x, Order = y }) + .GroupBy(x => x.Customer.Id) + .Select(x => new + { + Customer = x.FirstOrDefault().Customer, + OrderCount = x.Count() + }) + .Where(x => x.OrderCount >= ctx.Filter.HasPlacedAtLeastOrders.Value) + .Select(x => x.Customer); + } if (ctx.Request.EntitiesToExport.Any()) { @@ -1261,7 +1280,6 @@ private IQueryable GetCustomerQuery(DataExporterContext ctx, int? skip query = query.OrderBy(x => x.Id); - var skipValue = skip.GetValueOrDefault(); if (skipValue > 0) { query = query.Skip(() => skipValue); @@ -1276,11 +1294,13 @@ private IQueryable GetCustomerQuery(DataExporterContext ctx, int? skip query = query.Take(() => take); } - return query; - } + return query; + } + + private List GetCustomers(DataExporterContext ctx) + { + DetachAllEntitiesAndClear(ctx); - private List GetCustomers(DataExporterContext ctx) - { var stats = ctx.StatsPerStore[ctx.Store.Id]; if (ctx.LastId >= stats.MaxId) { @@ -1297,23 +1317,39 @@ private List GetCustomers(DataExporterContext ctx) ctx.LastId = customers.Last().Id; SetProgress(ctx, customers.Count); - return customers; - } + return customers; + } + + private IQueryable GetNewsLetterSubscriptionQuery(DataExporterContext ctx, int? skip, int take) + { + var storeId = ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId; + + var skipValue = skip.GetValueOrDefault(); + if (skipValue == 0 && ctx.LastId == 0) + { + skipValue = Math.Max(ctx.Request.Profile.Offset, 0); + } - private IQueryable GetNewsLetterSubscriptionQuery(DataExporterContext ctx, int? skip, int take) - { - var storeId = ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId; + var customerQuery = _customerRepository.Value.TableUntracked.Where(x => !x.Deleted); - var query = _subscriptionRepository.Value.TableUntracked; + var query = + from ns in _subscriptionRepository.Value.TableUntracked + join c in customerQuery on ns.Email equals c.Email into customers + from c in customers.DefaultIfEmpty() + select new NewsletterSubscriber + { + Subscription = ns, + Customer = c + }; if (storeId > 0) { - query = query.Where(x => x.StoreId == storeId); + query = query.Where(x => x.Subscription.StoreId == storeId); } if (ctx.Filter.IsActiveSubscriber.HasValue) { - query = query.Where(x => x.Active == ctx.Filter.IsActiveSubscriber.Value); + query = query.Where(x => x.Subscription.Active == ctx.Filter.IsActiveSubscriber.Value); } if (ctx.Filter.WorkingLanguageId != null && ctx.Filter.WorkingLanguageId != 0) @@ -1323,41 +1359,45 @@ private IQueryable GetNewsLetterSubscriptionQuery(DataEx if (isDefaultLanguage) { - query = query.Where(x => x.WorkingLanguageId == 0 || x.WorkingLanguageId == ctx.Filter.WorkingLanguageId); + query = query.Where(x => x.Subscription.WorkingLanguageId == 0 || x.Subscription.WorkingLanguageId == ctx.Filter.WorkingLanguageId); } else { - query = query.Where(x => x.WorkingLanguageId == ctx.Filter.WorkingLanguageId); + query = query.Where(x => x.Subscription.WorkingLanguageId == ctx.Filter.WorkingLanguageId); } } if (ctx.Filter.CreatedFrom.HasValue) - { - var createdFrom = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.CreatedFrom.Value, _services.DateTimeHelper.CurrentTimeZone); - query = query.Where(x => createdFrom <= x.CreatedOnUtc); - } + { + var createdFrom = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.CreatedFrom.Value, _services.DateTimeHelper.CurrentTimeZone); + query = query.Where(x => createdFrom <= x.Subscription.CreatedOnUtc); + } + + if (ctx.Filter.CreatedTo.HasValue) + { + var createdTo = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.CreatedTo.Value, _services.DateTimeHelper.CurrentTimeZone); + query = query.Where(x => createdTo >= x.Subscription.CreatedOnUtc); + } - if (ctx.Filter.CreatedTo.HasValue) - { - var createdTo = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.CreatedTo.Value, _services.DateTimeHelper.CurrentTimeZone); - query = query.Where(x => createdTo >= x.CreatedOnUtc); - } + if (ctx.Filter.CustomerRoleIds != null && ctx.Filter.CustomerRoleIds.Any()) + { + query = query.Where(x => x.Customer.CustomerRoleMappings.Select(y => y.CustomerRoleId).Intersect(ctx.Filter.CustomerRoleIds).Any()); + } if (ctx.Request.EntitiesToExport.Any()) { - query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Id)); + query = query.Where(x => ctx.Request.EntitiesToExport.Contains(x.Subscription.Id)); } - query = query.OrderBy(x => x.Id); + query = query.OrderBy(x => x.Subscription.Id); - var skipValue = skip.GetValueOrDefault(); if (skipValue > 0) { query = query.Skip(() => skipValue); } else if (ctx.LastId > 0) { - query = query.Where(x => x.Id > ctx.LastId); + query = query.Where(x => x.Subscription.Id > ctx.LastId); } if (take != int.MaxValue) @@ -1365,11 +1405,13 @@ private IQueryable GetNewsLetterSubscriptionQuery(DataEx query = query.Take(() => take); } - return query; - } + return query.Select(x => x.Subscription); + } + + private List GetNewsLetterSubscriptions(DataExporterContext ctx) + { + DetachAllEntitiesAndClear(ctx); - private List GetNewsLetterSubscriptions(DataExporterContext ctx) - { var stats = ctx.StatsPerStore[ctx.Store.Id]; if (ctx.LastId >= stats.MaxId) { @@ -1386,36 +1428,42 @@ private List GetNewsLetterSubscriptions(DataExporterCont ctx.LastId = subscriptions.Last().Id; SetProgress(ctx, subscriptions.Count); - return subscriptions; - } + return subscriptions; + } + + private IQueryable GetShoppingCartItemQuery(DataExporterContext ctx, int? skip, int take) + { + var storeId = ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId; - private IQueryable GetShoppingCartItemQuery(DataExporterContext ctx, int? skip, int take) - { - var storeId = ctx.Request.Profile.PerStore ? ctx.Store.Id : ctx.Filter.StoreId; + var skipValue = skip.GetValueOrDefault(); + if (skipValue == 0 && ctx.LastId == 0) + { + skipValue = Math.Max(ctx.Request.Profile.Offset, 0); + } - var query = _shoppingCartItemRepository.Value.TableUntracked - .Expand(x => x.Customer) - .Expand(x => x.Customer.CustomerRoles) - .Expand(x => x.Product) - .Where(x => !x.Customer.Deleted); + var query = _shoppingCartItemRepository.Value.TableUntracked + .Expand(x => x.Customer) + .Expand(x => x.Customer.CustomerRoleMappings.Select(rm => rm.CustomerRole)) + .Expand(x => x.Product) + .Where(x => !x.Customer.Deleted); if (storeId > 0) { query = query.Where(x => x.StoreId == storeId); } - if (ctx.Request.ActionOrigin.IsCaseInsensitiveEqual("CurrentCarts")) - { - query = query.Where(x => x.ShoppingCartTypeId == (int)ShoppingCartType.ShoppingCart); - } - else if (ctx.Request.ActionOrigin.IsCaseInsensitiveEqual("CurrentWishlists")) - { - query = query.Where(x => x.ShoppingCartTypeId == (int)ShoppingCartType.Wishlist); - } - else if (ctx.Filter.ShoppingCartTypeId.HasValue) - { - query = query.Where(x => x.ShoppingCartTypeId == ctx.Filter.ShoppingCartTypeId.Value); - } + if (ctx.Request.ActionOrigin.IsCaseInsensitiveEqual("CurrentCarts")) + { + query = query.Where(x => x.ShoppingCartTypeId == (int)ShoppingCartType.ShoppingCart); + } + else if (ctx.Request.ActionOrigin.IsCaseInsensitiveEqual("CurrentWishlists")) + { + query = query.Where(x => x.ShoppingCartTypeId == (int)ShoppingCartType.Wishlist); + } + else if (ctx.Filter.ShoppingCartTypeId.HasValue) + { + query = query.Where(x => x.ShoppingCartTypeId == ctx.Filter.ShoppingCartTypeId.Value); + } if (ctx.Filter.IsActiveCustomer.HasValue) { @@ -1429,41 +1477,41 @@ private IQueryable GetShoppingCartItemQuery(DataExporterContex 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.LastActivityFrom.HasValue) - { - var activityFrom = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.LastActivityFrom.Value, _services.DateTimeHelper.CurrentTimeZone); - query = query.Where(x => activityFrom <= x.Customer.LastActivityDateUtc); - } - - if (ctx.Filter.LastActivityTo.HasValue) - { - var activityTo = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.LastActivityTo.Value, _services.DateTimeHelper.CurrentTimeZone); - query = query.Where(x => activityTo >= x.Customer.LastActivityDateUtc); - } - - if (ctx.Filter.CreatedFrom.HasValue) - { - var createdFrom = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.CreatedFrom.Value, _services.DateTimeHelper.CurrentTimeZone); - query = query.Where(x => createdFrom <= x.CreatedOnUtc); - } - - if (ctx.Filter.CreatedTo.HasValue) - { - var createdTo = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.CreatedTo.Value, _services.DateTimeHelper.CurrentTimeZone); - query = query.Where(x => createdTo >= x.CreatedOnUtc); - } - - if (ctx.Projection.NoBundleProducts) - { - query = query.Where(x => x.Product.ProductTypeId != (int)ProductType.BundledProduct); - } - else - { - query = query.Where(x => x.BundleItemId == null); - } + query = query.Where(x => x.Customer.CustomerRoleMappings.Select(y => y.CustomerRoleId).Intersect(ctx.Filter.CustomerRoleIds).Any()); + } + + if (ctx.Filter.LastActivityFrom.HasValue) + { + var activityFrom = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.LastActivityFrom.Value, _services.DateTimeHelper.CurrentTimeZone); + query = query.Where(x => activityFrom <= x.Customer.LastActivityDateUtc); + } + + if (ctx.Filter.LastActivityTo.HasValue) + { + var activityTo = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.LastActivityTo.Value, _services.DateTimeHelper.CurrentTimeZone); + query = query.Where(x => activityTo >= x.Customer.LastActivityDateUtc); + } + + if (ctx.Filter.CreatedFrom.HasValue) + { + var createdFrom = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.CreatedFrom.Value, _services.DateTimeHelper.CurrentTimeZone); + query = query.Where(x => createdFrom <= x.CreatedOnUtc); + } + + if (ctx.Filter.CreatedTo.HasValue) + { + var createdTo = _services.DateTimeHelper.ConvertToUtcTime(ctx.Filter.CreatedTo.Value, _services.DateTimeHelper.CurrentTimeZone); + query = query.Where(x => createdTo >= x.CreatedOnUtc); + } + + if (ctx.Projection.NoBundleProducts) + { + query = query.Where(x => x.Product.ProductTypeId != (int)ProductType.BundledProduct); + } + else + { + query = query.Where(x => x.BundleItemId == null); + } if (ctx.Request.EntitiesToExport.Any()) { @@ -1472,7 +1520,6 @@ private IQueryable GetShoppingCartItemQuery(DataExporterContex query = query.OrderBy(x => x.Id); - var skipValue = skip.GetValueOrDefault(); if (skipValue > 0) { query = query.Skip(() => skipValue); @@ -1487,11 +1534,13 @@ private IQueryable GetShoppingCartItemQuery(DataExporterContex query = query.Take(take); } - return query; - } + return query; + } + + private List GetShoppingCartItems(DataExporterContext ctx) + { + DetachAllEntitiesAndClear(ctx); - private List GetShoppingCartItems(DataExporterContext ctx) - { var stats = ctx.StatsPerStore[ctx.Store.Id]; if (ctx.LastId >= stats.MaxId) { @@ -1508,21 +1557,21 @@ private List GetShoppingCartItems(DataExporterContext ctx) ctx.LastId = cartItems.Last().Id; SetProgress(ctx, cartItems.Count); - return cartItems; - } + return cartItems; + } - #endregion + #endregion - private List Init(DataExporterContext ctx) - { - List result = null; + private List Init(DataExporterContext ctx) + { + List result = null; - ctx.ContextCurrency = _currencyService.Value.GetCurrencyById(ctx.Projection.CurrencyId ?? 0) ?? _services.WorkContext.WorkingCurrency; - ctx.ContextCustomer = _customerService.GetCustomerById(ctx.Projection.CustomerId ?? 0) ?? _services.WorkContext.CurrentCustomer; - ctx.ContextLanguage = _languageService.Value.GetLanguageById(ctx.Projection.LanguageId ?? 0) ?? _services.WorkContext.WorkingLanguage; + ctx.ContextCurrency = _currencyService.Value.GetCurrencyById(ctx.Projection.CurrencyId ?? 0) ?? _services.WorkContext.WorkingCurrency; + ctx.ContextCustomer = _customerService.GetCustomerById(ctx.Projection.CustomerId ?? 0) ?? _services.WorkContext.CurrentCustomer; + ctx.ContextLanguage = _languageService.Value.GetLanguageById(ctx.Projection.LanguageId ?? 0) ?? _services.WorkContext.WorkingLanguage; - ctx.Stores = _services.StoreService.GetAllStores().ToDictionary(x => x.Id, x => x); - ctx.Languages = _languageService.Value.GetAllLanguages(true).ToDictionary(x => x.Id, x => x); + 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) { @@ -1531,7 +1580,7 @@ private List Init(DataExporterContext ctx) 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), + 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()); @@ -1557,21 +1606,21 @@ private List Init(DataExporterContext ctx) } 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; - 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. - foreach (var store in result) - { - ctx.Store = store; + { + 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; + 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. + foreach (var store in result) + { + ctx.Store = store; IQueryable query = null; @@ -1608,20 +1657,16 @@ private List Init(DataExporterContext ctx) 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(); - } } ctx.StatsPerStore[store.Id] = stats; - } + } return result; - } + } - private void ExportCoreInner(DataExporterContext ctx, Store store) - { + private void ExportCoreInner(DataExporterContext ctx, Store store) + { var context = ctx.ExecuteContext; var profile = ctx.Request.Profile; var provider = ctx.Request.Provider; @@ -1631,13 +1676,13 @@ private void ExportCoreInner(DataExporterContext ctx, Store store) return; } - { - var logHead = new StringBuilder(); - logHead.AppendLine(); - logHead.AppendLine(new string('-', 40)); - logHead.AppendLine("SmartStore.NET: v." + SmartStoreVersion.CurrentFullVersion); - logHead.Append("Export profile: " + profile.Name); - logHead.AppendLine(profile.Id == 0 ? " (volatile)" : $" (Id {profile.Id})"); + { + var logHead = new StringBuilder(); + logHead.AppendLine(); + logHead.AppendLine(new string('-', 40)); + logHead.AppendLine("Smartstore: v." + SmartStoreVersion.CurrentFullVersion); + logHead.Append("Export profile: " + profile.Name); + logHead.AppendLine(profile.Id == 0 ? " (volatile)" : $" (Id {profile.Id})"); if (provider.Metadata.FriendlyName.HasValue()) { @@ -1648,82 +1693,82 @@ private void ExportCoreInner(DataExporterContext ctx, Store store) logHead.AppendLine("Export provider: " + profile.ProviderSystemName); } - var plugin = provider.Metadata.PluginDescriptor; - logHead.Append("Plugin: "); - logHead.AppendLine(plugin == null ? "".NaIfEmpty() : $"{plugin.FriendlyName} ({plugin.SystemName}) v.{plugin.Version.ToString()}"); - logHead.AppendLine("Entity: " + provider.Value.EntityType.ToString()); + var plugin = provider.Metadata.PluginDescriptor; + logHead.Append("Plugin: "); + logHead.AppendLine(plugin == null ? "".NaIfEmpty() : $"{plugin.FriendlyName} ({plugin.SystemName}) v.{plugin.Version.ToString()}"); + logHead.AppendLine("Entity: " + provider.Value.EntityType.ToString()); - try - { - var uri = new Uri(store.Url); - logHead.AppendLine($"Store: {uri.DnsSafeHost.NaIfEmpty()} (Id {store.Id})"); - } - catch { } + try + { + var uri = new Uri(store.Url); + logHead.AppendLine($"Store: {uri.DnsSafeHost.NaIfEmpty()} (Id {store.Id})"); + } + catch { } - var customer = _services.WorkContext.CurrentCustomer; - logHead.Append("Executed by: " + (customer.Email.HasValue() ? customer.Email : customer.SystemName)); + var customer = _services.WorkContext.CurrentCustomer; + logHead.Append("Executed by: " + (customer.Email.HasValue() ? customer.Email : customer.SystemName)); - ctx.Log.Info(logHead.ToString()); - } + ctx.Log.Info(logHead.ToString()); + } var dataExchangeSettings = _services.Settings.LoadSetting(store.Id); var publicDeployment = profile.Deployments.FirstOrDefault(x => x.DeploymentType == ExportDeploymentType.PublicFolder); ctx.Store = store; + ctx.LastId = 0; context.FileIndex = 0; context.Store = ToDynamic(ctx, ctx.Store); - context.MaxFileNameLength = dataExchangeSettings.MaxFileNameLength; - context.HasPublicDeployment = publicDeployment != null; - context.PublicFolderPath = publicDeployment.GetDeploymentFolder(true); - context.PublicFolderUrl = publicDeployment.GetPublicFolderUrl(_services, ctx.Store); + context.MaxFileNameLength = dataExchangeSettings.MaxFileNameLength; + context.HasPublicDeployment = publicDeployment != null; + context.PublicFolderPath = publicDeployment.GetDeploymentFolder(true); + context.PublicFolderUrl = publicDeployment.GetPublicFolderUrl(_services, ctx.Store); var fileExtension = provider.Value.FileExtension.HasValue() ? provider.Value.FileExtension.ToLower().EnsureStartsWith(".") : ""; - using (var segmenter = CreateSegmenter(ctx)) - { - if (segmenter == null) - { - throw new SmartException($"Unsupported entity type '{provider.Value.EntityType.ToString()}'."); - } + using (var segmenter = CreateSegmenter(ctx)) + { + if (segmenter == null) + { + throw new SmartException($"Unsupported entity type '{provider.Value.EntityType.ToString()}'."); + } - if (segmenter.TotalRecords <= 0) - { - ctx.Log.Info("There are no records to export."); - } + if (segmenter.TotalRecords <= 0) + { + ctx.Log.Info("There are no records to export."); + } - while (context.Abort == DataExchangeAbortion.None && segmenter.HasData) - { - segmenter.RecordPerSegmentCount = 0; - context.RecordsSucceeded = 0; + while (context.Abort == DataExchangeAbortion.None && segmenter.HasData) + { + segmenter.RecordPerSegmentCount = 0; + context.RecordsSucceeded = 0; - string path = null; + string path = null; - if (ctx.IsFileBasedExport) - { + if (ctx.IsFileBasedExport) + { context.FileIndex = context.FileIndex + 1; context.FileName = profile.ResolveFileNamePattern(ctx.Store, context.FileIndex, context.MaxFileNameLength) + fileExtension; - path = Path.Combine(context.Folder, context.FileName); + path = Path.Combine(context.Folder, context.FileName); if (profile.ExportRelatedData && ctx.Supports(ExportFeatures.UsesRelatedDataUnits)) { context.ExtraDataUnits.AddRange(GetDataUnitsForRelatedEntities(ctx)); } - } - - if (CallProvider(ctx, "Execute", path)) - { - if (ctx.IsFileBasedExport && File.Exists(path)) - { - ctx.Result.Files.Add(new DataExportResult.ExportFileInfo - { - StoreId = ctx.Store.Id, - FileName = context.FileName + } + + if (CallProvider(ctx, "Execute", path)) + { + if (ctx.IsFileBasedExport && File.Exists(path)) + { + ctx.Result.Files.Add(new DataExportResult.ExportFileInfo + { + StoreId = ctx.Store.Id, + FileName = context.FileName }); } - } + } ctx.EntityIdsPerSegment.Clear(); - DetachAllEntitiesAndClear(ctx); if (context.IsMaxFailures) { @@ -1733,20 +1778,21 @@ private void ExportCoreInner(DataExporterContext ctx, Store store) { ctx.Log.Warn("Export aborted. A cancellation has been requested."); } - } + } - if (context.Abort != DataExchangeAbortion.Hard) - { + if (context.Abort != DataExchangeAbortion.Hard) + { var calledExecuted = false; - context.ExtraDataUnits.ForEach(x => - { + context.ExtraDataUnits.ForEach(x => + { context.DataStreamId = x.Id; var success = true; var path = x.FileName.HasValue() ? Path.Combine(context.Folder, x.FileName) : null; if (!x.RelatedType.HasValue) { + // Unit added by provider. calledExecuted = true; success = CallProvider(ctx, "OnExecuted", path); } @@ -1773,179 +1819,177 @@ private void ExportCoreInner(DataExporterContext ctx, Store store) context.ExtraDataUnits.Clear(); } - } + } - private void ExportCoreOuter(DataExporterContext ctx) - { + private void ExportCoreOuter(DataExporterContext ctx) + { var profile = ctx.Request.Profile; - if (profile == null || !profile.Enabled) + var logPath = profile.GetExportLogPath(); + var zipPath = profile.GetExportZipPath(); + + FileSystemHelper.DeleteFile(logPath); + FileSystemHelper.DeleteFile(zipPath); + FileSystemHelper.ClearDirectory(ctx.FolderContent, false); + + using (var logger = new TraceLogger(logPath)) { - return; - } + try + { + ctx.Log = logger; + ctx.ExecuteContext.Log = logger; + ctx.ProgressInfo = T("Admin.DataExchange.Export.ProgressInfo"); - var logPath = profile.GetExportLogPath(); - var zipPath = profile.GetExportZipPath(); + if (!ctx.Request.Provider.IsValid()) + throw new SmartException("Export aborted because the export provider is not valid."); - FileSystemHelper.DeleteFile(logPath); - FileSystemHelper.DeleteFile(zipPath); - FileSystemHelper.ClearDirectory(ctx.FolderContent, false); - - using (var logger = new TraceLogger(logPath)) - { - try - { - ctx.Log = logger; - ctx.ExecuteContext.Log = logger; - ctx.ProgressInfo = T("Admin.DataExchange.Export.ProgressInfo"); - - if (!ctx.Request.Provider.IsValid()) - throw new SmartException("Export aborted because the export provider is not valid."); - - if (!HasPermission(ctx)) - throw new SmartException("You do not have permission to perform the selected export."); - - foreach (var item in ctx.Request.CustomData) - { - ctx.ExecuteContext.CustomProperties.Add(item.Key, item.Value); - } - - if (profile.ProviderConfigData.HasValue()) - { - var configInfo = ctx.Request.Provider.Value.ConfigurationInfo; - if (configInfo != null) - { - ctx.ExecuteContext.ConfigurationData = XmlHelper.Deserialize(profile.ProviderConfigData, configInfo.ModelType); - } - } + if (!HasPermission(ctx)) + throw new SmartException("You do not have permission to perform the selected export."); + + foreach (var item in ctx.Request.CustomData) + { + ctx.ExecuteContext.CustomProperties.Add(item.Key, item.Value); + } + + if (profile.ProviderConfigData.HasValue()) + { + var configInfo = ctx.Request.Provider.Value.ConfigurationInfo; + if (configInfo != null) + { + ctx.ExecuteContext.ConfigurationData = XmlHelper.Deserialize(profile.ProviderConfigData, configInfo.ModelType); + } + } // lazyLoading: false, proxyCreation: false impossible due to 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); + { + 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 || - ctx.Request.Provider.Value.EntityType == ExportEntityType.Order) - { - ctx.Countries = _countryService.Value.GetAllCountries(true).ToDictionary(x => x.Id, x => x); - } - - if (ctx.Request.Provider.Value.EntityType == ExportEntityType.Customer) - { - var subscriptionEmails = _subscriptionRepository.Value.TableUntracked - .Where(x => x.Active) - .Select(x => x.Email) - .Distinct() - .ToList(); - - ctx.NewsletterSubscriptions = new HashSet(subscriptionEmails, StringComparer.OrdinalIgnoreCase); - } - - var stores = Init(ctx); - - ctx.ExecuteContext.Language = ToDynamic(ctx, ctx.ContextLanguage); - ctx.ExecuteContext.Customer = ToDynamic(ctx, ctx.ContextCustomer); - ctx.ExecuteContext.Currency = ToDynamic(ctx, ctx.ContextCurrency); - ctx.ExecuteContext.Profile = ToDynamic(ctx, profile); - - stores.ForEach(x => ExportCoreInner(ctx, x)); - } - - if (!ctx.IsPreview && ctx.ExecuteContext.Abort != DataExchangeAbortion.Hard) - { - if (ctx.IsFileBasedExport) - { - if (profile.CreateZipArchive) - { - ZipFile.CreateFromDirectory(ctx.FolderContent, zipPath, CompressionLevel.Fastest, false); - } - - if (profile.Deployments.Any(x => x.Enabled)) - { - SetProgress(ctx, T("Common.Publishing")); - - var allDeploymentsSucceeded = Deploy(ctx, zipPath); - - if (allDeploymentsSucceeded && profile.Cleanup) - { - logger.Info("Cleaning up export folder"); - - FileSystemHelper.ClearDirectory(ctx.FolderContent, false); - } - } - } - - if (profile.EmailAccountId != 0 && !ctx.Supports(ExportFeatures.CanOmitCompletionMail)) - { - SendCompletionEmail(ctx, zipPath); - } - } - } - catch (Exception ex) - { - logger.ErrorsAll(ex); - ctx.Result.LastError = ex.ToAllMessages(true); - } - finally - { - try - { - if (!ctx.IsPreview && profile.Id != 0) - { + ctx.Request.Provider.Value.EntityType == ExportEntityType.Order) + { + ctx.Countries = _countryService.Value.GetAllCountries(true).ToDictionary(x => x.Id, x => x); + } + + if (ctx.Request.Provider.Value.EntityType == ExportEntityType.Customer) + { + var subscriptionEmails = _subscriptionRepository.Value.TableUntracked + .Where(x => x.Active) + .Select(x => x.Email) + .Distinct() + .ToList(); + + ctx.NewsletterSubscriptions = new HashSet(subscriptionEmails, StringComparer.OrdinalIgnoreCase); + } + + var stores = Init(ctx); + + ctx.ExecuteContext.Language = ToDynamic(ctx, ctx.ContextLanguage); + ctx.ExecuteContext.Customer = ToDynamic(ctx, ctx.ContextCustomer); + ctx.ExecuteContext.Currency = ToDynamic(ctx, ctx.ContextCurrency); + ctx.ExecuteContext.Profile = ToDynamic(ctx, profile); + + stores.ForEach(x => ExportCoreInner(ctx, x)); + } + + if (!ctx.IsPreview && ctx.ExecuteContext.Abort != DataExchangeAbortion.Hard) + { + if (ctx.IsFileBasedExport) + { + if (profile.CreateZipArchive) + { + ZipFile.CreateFromDirectory(ctx.FolderContent, zipPath, CompressionLevel.Fastest, false); + } + + if (profile.Deployments.Any(x => x.Enabled)) + { + SetProgress(ctx, T("Common.Publishing")); + + var allDeploymentsSucceeded = Deploy(ctx, zipPath); + + if (allDeploymentsSucceeded && profile.Cleanup) + { + logger.Info("Cleaning up export folder"); + + FileSystemHelper.ClearDirectory(ctx.FolderContent, false); + } + } + } + + if (profile.EmailAccountId != 0 && !ctx.Supports(ExportFeatures.CanOmitCompletionMail)) + { + SendCompletionEmail(ctx, zipPath); + } + } + } + catch (Exception ex) + { + logger.ErrorsAll(ex); + ctx.Result.LastError = ex.ToAllMessages(true); + } + finally + { + try + { + if (!ctx.IsPreview && profile.Id != 0) + { ctx.Result.Files = ctx.Result.Files.OrderBy(x => x.RelatedType).ToList(); - profile.ResultInfo = XmlHelper.Serialize(ctx.Result); + profile.ResultInfo = XmlHelper.Serialize(ctx.Result); - _exportProfileService.Value.UpdateExportProfile(profile); - } - } - catch (Exception ex) - { - logger.ErrorsAll(ex); - } + _exportProfileService.Value.UpdateExportProfile(profile); + } + } + catch (Exception ex) + { + logger.ErrorsAll(ex); + } - DetachAllEntitiesAndClear(ctx); + DetachAllEntitiesAndClear(ctx); try - { - ctx.NewsletterSubscriptions.Clear(); - ctx.ProductTemplates.Clear(); - ctx.CategoryTemplates.Clear(); - ctx.Countries.Clear(); - ctx.Languages.Clear(); - ctx.QuantityUnits.Clear(); - ctx.DeliveryTimes.Clear(); - ctx.Stores.Clear(); + { + ctx.NewsletterSubscriptions.Clear(); + ctx.ProductTemplates.Clear(); + ctx.CategoryTemplates.Clear(); + ctx.Countries.Clear(); + ctx.Languages.Clear(); + ctx.QuantityUnits.Clear(); + ctx.DeliveryTimes.Clear(); + ctx.Stores.Clear(); ctx.Translations.Clear(); ctx.UrlRecords.Clear(); - ctx.Request.CustomData.Clear(); + ctx.TranslationsPerPage?.Clear(); + ctx.UrlRecordsPerPage?.Clear(); + + ctx.Request.CustomData.Clear(); - ctx.ExecuteContext.CustomProperties.Clear(); - ctx.ExecuteContext.Log = null; - ctx.Log = null; - } - catch (Exception ex) - { - logger.ErrorsAll(ex); - } - } - } + ctx.ExecuteContext.CustomProperties.Clear(); + ctx.ExecuteContext.Log = null; + ctx.Log = null; + } + catch (Exception ex) + { + logger.ErrorsAll(ex); + } + } + } if (ctx.IsPreview || ctx.ExecuteContext.Abort == DataExchangeAbortion.Hard) { return; } - // Post process order entities. - if (ctx.EntityIdsLoaded.Any() && ctx.Request.Provider.Value.EntityType == ExportEntityType.Order && ctx.Projection.OrderStatusChange != ExportOrderStatusChange.None) - { - using (var logger = new TraceLogger(logPath)) - { - try - { - int? orderStatusId = null; + // Post process order entities. + if (ctx.EntityIdsLoaded.Any() && ctx.Request.Provider.Value.EntityType == ExportEntityType.Order && ctx.Projection.OrderStatusChange != ExportOrderStatusChange.None) + { + using (var logger = new TraceLogger(logPath)) + { + try + { + int? orderStatusId = null; if (ctx.Projection.OrderStatusChange == ExportOrderStatusChange.Processing) { @@ -1956,109 +2000,127 @@ private void ExportCoreOuter(DataExporterContext ctx) orderStatusId = (int)OrderStatus.Complete; } - using (var scope = new DbContextScope(_dbContext, false, null, false, false, false, false)) - { - foreach (var chunk in ctx.EntityIdsLoaded.Slice(128)) - { - var entities = _orderRepository.Value.Table.Where(x => chunk.Contains(x.Id)).ToList(); + using (var scope = new DbContextScope(_dbContext, false, null, false, false, false, false)) + { + foreach (var chunk in ctx.EntityIdsLoaded.Slice(128)) + { + var entities = _orderRepository.Value.Table.Where(x => chunk.Contains(x.Id)).ToList(); - entities.ForEach(x => x.OrderStatusId = (orderStatusId ?? x.OrderStatusId)); + entities.ForEach(x => x.OrderStatusId = (orderStatusId ?? x.OrderStatusId)); - _dbContext.SaveChanges(); - } - } + _dbContext.SaveChanges(); + } + } - logger.Info($"Updated order status for {ctx.EntityIdsLoaded.Count()} order(s)."); - } - catch (Exception ex) - { - logger.ErrorsAll(ex); + logger.Info($"Updated order status for {ctx.EntityIdsLoaded.Count()} order(s)."); + } + catch (Exception ex) + { + logger.ErrorsAll(ex); ctx.Result.LastError = ex.ToAllMessages(true); - } - } - } - } + } + } + } + } + + /// + /// The name of the public export folder + /// + public static string PublicFolder => "Exchange"; - /// - /// The name of the public export folder - /// - public static string PublicFolder => "Exchange"; + public static int PageSize => 100; + + public DataExportResult Export(DataExportRequest request, CancellationToken cancellationToken) + { + var ctx = new DataExporterContext(request, cancellationToken); - public static int PageSize => 100; + if (!(request?.Profile?.Enabled ?? false)) + { + return ctx.Result; + } - public DataExportResult Export(DataExportRequest request, CancellationToken cancellationToken) - { - var ctx = new DataExporterContext(request, cancellationToken); + var lockKey = $"dataexporter:profile:{request.Profile.Id}"; + if (KeyedLock.IsLockHeld(lockKey)) + { + ctx.Result.LastError = $"The execution of the profile \"{request.Profile.Name.NaIfEmpty()}\" (ID {request.Profile.Id}) is locked."; + return ctx.Result; + } - ExportCoreOuter(ctx); - cancellationToken.ThrowIfCancellationRequested(); + using (KeyedLock.Lock(lockKey)) + { + ExportCoreOuter(ctx); + } - return ctx.Result; - } + cancellationToken.ThrowIfCancellationRequested(); + return ctx.Result; + } - public DataExportPreviewResult Preview(DataExportRequest request, int pageIndex) - { + public DataExportPreviewResult Preview(DataExportRequest request, int pageIndex) + { var result = new DataExportPreviewResult(); var cancellation = new CancellationTokenSource(TimeSpan.FromMinutes(5.0)); - var ctx = new DataExporterContext(request, cancellation.Token, true); + var ctx = new DataExporterContext(request, cancellation.Token, true); - var unused = Init(ctx); - var skip = Math.Max(ctx.Request.Profile.Offset, 0) + (pageIndex * PageSize); + Init(ctx); - if (!HasPermission(ctx)) - { - throw new SmartException(T("Admin.AccessDenied")); - } + var limit = Math.Max(request.Profile.Limit, 0); + var take = limit > 0 && limit < PageSize ? limit : PageSize; + var skip = Math.Max(ctx.Request.Profile.Offset, 0) + (pageIndex * take); + + if (!HasPermission(ctx)) + { + throw new SmartException(T("Admin.AccessDenied")); + } result.TotalRecords = ctx.StatsPerStore.First().Value.TotalRecords; switch (request.Provider.Value.EntityType) - { - case ExportEntityType.Product: - { - var items = GetProductQuery(ctx, skip, PageSize).ToList(); - items.Each(x => result.Data.Add(ToDynamic(ctx, x))); - } - break; - case ExportEntityType.Order: - { - var items = GetOrderQuery(ctx, skip, PageSize).ToList(); - items.Each(x => result.Data.Add(ToDynamic(ctx, x))); - } - break; - case ExportEntityType.Category: - { - var items = GetCategoryQuery(ctx, skip, PageSize).ToList(); - items.Each(x => result.Data.Add(ToDynamic(ctx, x))); - } - break; - case ExportEntityType.Manufacturer: - { - var items = GetManufacturerQuery(ctx, skip, PageSize).ToList(); - items.Each(x => result.Data.Add(ToDynamic(ctx, x))); - } - break; - case ExportEntityType.Customer: - { - var items = GetCustomerQuery(ctx, skip, PageSize).ToList(); - items.Each(x => result.Data.Add(ToDynamic(ctx, x))); - } - break; - case ExportEntityType.NewsLetterSubscription: - { - var items = GetNewsLetterSubscriptionQuery(ctx, skip, PageSize).ToList(); - items.Each(x => result.Data.Add(ToDynamic(ctx, x))); - } - break; - case ExportEntityType.ShoppingCartItem: - { - var items = GetShoppingCartItemQuery(ctx, skip, PageSize).ToList(); - items.Each(x => result.Data.Add(ToDynamic(ctx, x))); - } - break; - } + { + case ExportEntityType.Product: + { + var items = GetProductQuery(ctx, skip, take).ToList(); + items.Each(x => result.Data.Add(ToDynamic(ctx, x))); + } + break; + case ExportEntityType.Order: + { + var items = GetOrderQuery(ctx, skip, take).ToList(); + items.Each(x => result.Data.Add(ToDynamic(ctx, x))); + } + break; + case ExportEntityType.Category: + { + var items = GetCategoryQuery(ctx, skip, take).ToList(); + items.Each(x => result.Data.Add(ToDynamic(ctx, x))); + } + break; + case ExportEntityType.Manufacturer: + { + var items = GetManufacturerQuery(ctx, skip, take).ToList(); + items.Each(x => result.Data.Add(ToDynamic(ctx, x))); + } + break; + case ExportEntityType.Customer: + { + var items = GetCustomerQuery(ctx, skip, take).ToList(); + items.Each(x => result.Data.Add(ToDynamic(ctx, x))); + } + break; + case ExportEntityType.NewsLetterSubscription: + { + var items = GetNewsLetterSubscriptionQuery(ctx, skip, take).ToList(); + items.Each(x => result.Data.Add(ToDynamic(ctx, x))); + } + break; + case ExportEntityType.ShoppingCartItem: + { + var items = GetShoppingCartItemQuery(ctx, skip, take).ToList(); + items.Each(x => result.Data.Add(ToDynamic(ctx, x))); + } + break; + } return result; - } - } + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/DataDeploymentResult.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/DataDeploymentResult.cs index 7fff7a46e5..9b901f138f 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/DataDeploymentResult.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/DataDeploymentResult.cs @@ -2,22 +2,19 @@ namespace SmartStore.Services.DataExchange.Export.Deployment { - [Serializable] - public class DataDeploymentResult - { - /// - /// Whether the deployment succeeded - /// - public bool Succeeded - { - get { return LastError.IsEmpty(); } - } + [Serializable] + public class DataDeploymentResult + { + /// + /// Whether the deployment succeeded + /// + public bool Succeeded => LastError.IsEmpty(); - public string LastError { get; set; } + public string LastError { get; set; } - /// - /// Last execution date - /// - public DateTime LastExecutionUtc { get; set; } - } + /// + /// Last execution date + /// + public DateTime LastExecutionUtc { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/EmailFilePublisher.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/EmailFilePublisher.cs index 3415948ae2..ccb036af4d 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/EmailFilePublisher.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/EmailFilePublisher.cs @@ -11,56 +11,56 @@ namespace SmartStore.Services.DataExchange.Export.Deployment { - public class EmailFilePublisher : IFilePublisher - { - private IEmailAccountService _emailAccountService; - private IQueuedEmailService _queuedEmailService; + public class EmailFilePublisher : IFilePublisher + { + private IEmailAccountService _emailAccountService; + private IQueuedEmailService _queuedEmailService; - public EmailFilePublisher( - IEmailAccountService emailAccountService, - IQueuedEmailService queuedEmailService) - { - _emailAccountService = emailAccountService; - _queuedEmailService = queuedEmailService; - } + public EmailFilePublisher( + IEmailAccountService emailAccountService, + IQueuedEmailService queuedEmailService) + { + _emailAccountService = emailAccountService; + _queuedEmailService = queuedEmailService; + } - public virtual void Publish(ExportDeploymentContext context, ExportDeployment deployment) - { - var emailAccount = _emailAccountService.GetEmailAccountById(deployment.EmailAccountId); - var smtpContext = new SmtpContext(emailAccount); - var count = 0; + public virtual void Publish(ExportDeploymentContext context, ExportDeployment deployment) + { + var emailAccount = _emailAccountService.GetEmailAccountById(deployment.EmailAccountId); + var smtpContext = new SmtpContext(emailAccount); + var count = 0; - foreach (var email in deployment.EmailAddresses.SplitSafe(",").Where(x => x.IsEmail())) - { - var queuedEmail = new QueuedEmail - { - From = emailAccount.ToEmailAddress(), - SendManually = false, - To = email, - Subject = deployment.EmailSubject.NaIfEmpty(), - Body = deployment.EmailSubject.NaIfEmpty(), - CreatedOnUtc = DateTime.UtcNow, - EmailAccountId = deployment.EmailAccountId - }; + foreach (var email in deployment.EmailAddresses.SplitSafe(",").Where(x => x.IsEmail())) + { + var queuedEmail = new QueuedEmail + { + From = emailAccount.ToEmailAddress(), + SendManually = false, + To = email, + Subject = deployment.EmailSubject.NaIfEmpty(), + Body = deployment.EmailSubject.NaIfEmpty(), + CreatedOnUtc = DateTime.UtcNow, + EmailAccountId = deployment.EmailAccountId + }; - foreach (var path in context.GetDeploymentFiles()) - { - var name = Path.GetFileName(path); + foreach (var path in context.GetDeploymentFiles()) + { + var name = Path.GetFileName(path); - queuedEmail.Attachments.Add(new QueuedEmailAttachment - { - StorageLocation = EmailAttachmentStorageLocation.Blob, - MediaStorage = new MediaStorage { Data = File.ReadAllBytes(path) }, - Name = name, - MimeType = MimeTypes.MapNameToMimeType(name) - }); - } + queuedEmail.Attachments.Add(new QueuedEmailAttachment + { + StorageLocation = EmailAttachmentStorageLocation.Blob, + MediaStorage = new MediaStorage { Data = File.ReadAllBytes(path) }, + Name = name, + MimeType = MimeTypes.MapNameToMimeType(name) + }); + } - _queuedEmailService.InsertQueuedEmail(queuedEmail); - ++count; - } + _queuedEmailService.InsertQueuedEmail(queuedEmail); + ++count; + } - context.Log.Info("{0} email(s) created and queued for deployment.".FormatInvariant(count)); - } - } + context.Log.Info("{0} email(s) created and queued for deployment.".FormatInvariant(count)); + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/FileSystemFilePublisher.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/FileSystemFilePublisher.cs index 346cf02176..04363d9663 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/FileSystemFilePublisher.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/FileSystemFilePublisher.cs @@ -5,21 +5,21 @@ namespace SmartStore.Services.DataExchange.Export.Deployment { - public class FileSystemFilePublisher : IFilePublisher - { - public virtual void Publish(ExportDeploymentContext context, ExportDeployment deployment) - { - var targetFolder = deployment.GetDeploymentFolder(true); + public class FileSystemFilePublisher : IFilePublisher + { + public virtual void Publish(ExportDeploymentContext context, ExportDeployment deployment) + { + var targetFolder = deployment.GetDeploymentFolder(true); - if (targetFolder.IsEmpty()) - return; + if (targetFolder.IsEmpty()) + return; - if (!FileSystemHelper.CopyDirectory(new DirectoryInfo(context.FolderContent), new DirectoryInfo(targetFolder))) - { - context.Result.LastError = context.T("Admin.DataExchange.Export.Deployment.CopyFileFailed"); - } + if (!FileSystemHelper.CopyDirectory(new DirectoryInfo(context.FolderContent), new DirectoryInfo(targetFolder))) + { + 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/FtpFilePublisher.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/FtpFilePublisher.cs index 64bf43b067..bd6a2e8ad8 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/FtpFilePublisher.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/FtpFilePublisher.cs @@ -6,170 +6,170 @@ namespace SmartStore.Services.DataExchange.Export.Deployment { - public class FtpFilePublisher : IFilePublisher - { - private ExportDeploymentContext _context; - private ExportDeployment _deployment; - private int _succeededFiles; - private string _ftpRootUrl; - - private string GetRelativePath(string path) - { - var relativePath = path.Substring(_context.FolderContent.Length).Replace("\\", "/"); - if (relativePath.StartsWith("/")) - { - return relativePath.Substring(1); - } - - return relativePath; - } - - private FtpWebRequest CreateRequest(string url, bool keepAlive = true, long? contentLength = null) - { - var request = (FtpWebRequest)WebRequest.Create(url); - request.Method = WebRequestMethods.Ftp.UploadFile; - request.KeepAlive = keepAlive; - request.UseBinary = true; - request.Proxy = null; - request.UsePassive = _deployment.PassiveMode; - request.EnableSsl = _deployment.UseSsl; - - if (_deployment.Username.HasValue()) - { - request.Credentials = new NetworkCredential(_deployment.Username, _deployment.Password); - } - - if (contentLength.HasValue) - { - request.ContentLength = contentLength.Value; - } - - return request; - } - - private bool UploadFile(string path, string fileUrl, bool keepAlive = true) - { - var succeeded = false; - var bytesRead = 0; - var buffLength = 32768; - byte[] buff = new byte[buffLength]; - - var request = CreateRequest(fileUrl, keepAlive, (new FileInfo(path)).Length); - request.Method = WebRequestMethods.Ftp.UploadFile; - - var requestStream = request.GetRequestStream(); - using (var stream = new FileStream(path, FileMode.Open)) - { - while ((bytesRead = stream.Read(buff, 0, buffLength)) != 0) - { - requestStream.Write(buff, 0, bytesRead); - } - } - - requestStream.Close(); - - using (var response = (FtpWebResponse)request.GetResponse()) - { - var statusCode = (int)response.StatusCode; - succeeded = statusCode >= 200 && statusCode <= 299; - - if (succeeded) - { - ++_succeededFiles; - } - else - { - _context.Result.LastError = _context.T("Admin.Common.FtpStatus", statusCode, response.StatusCode.ToString()); - _context.Log.Error("The FTP transfer failed. FTP status {0} ({1}). File {3}".FormatInvariant(statusCode, response.StatusCode.ToString(), path)); - } - } - - return succeeded; - } - - private bool DirectoryExists(string directoryUrl) - { - var result = false; - - try - { - var request = CreateRequest(directoryUrl.EnsureEndsWith("/")); - request.Method = WebRequestMethods.Ftp.ListDirectory; - - using (request.GetResponse()) - { - result = true; - } - } - catch (WebException) - { - result = false; - } - - return result; - } - - private bool FtpCopyDirectory(DirectoryInfo source) - { - var files = source.GetFiles(); - var len = files.Length; - - for (var i = 0; i < len; ++i) - { - var path = files[i].FullName; - var relativePath = GetRelativePath(path); - - UploadFile(path, _ftpRootUrl + relativePath, i != (len - 1)); - } - - foreach (var sourceSubDir in source.GetDirectories()) - { - var relativePath = GetRelativePath(sourceSubDir.FullName); - - if (!DirectoryExists(_ftpRootUrl + relativePath)) - { - var request = CreateRequest(_ftpRootUrl + relativePath, true); - request.Method = WebRequestMethods.Ftp.MakeDirectory; - using (var response = (FtpWebResponse)request.GetResponse()) - { - response.Close(); - } - } - - FtpCopyDirectory(sourceSubDir); - } - - return true; - } - - public virtual void Publish(ExportDeploymentContext context, ExportDeployment deployment) - { - _context = context; - _deployment = deployment; - _succeededFiles = 0; - _ftpRootUrl = deployment.Url; - - if (!_ftpRootUrl.StartsWith("ftp://", StringComparison.InvariantCultureIgnoreCase)) - { - _ftpRootUrl = "ftp://" + _ftpRootUrl; - } - - _ftpRootUrl = _ftpRootUrl.EnsureEndsWith("/"); - - if (context.CreateZipArchive) - { - if (File.Exists(context.ZipPath)) - { - var fileUrl = _ftpRootUrl + Path.GetFileName(context.ZipPath); - UploadFile(context.ZipPath, fileUrl, false); - } - } - else - { - FtpCopyDirectory(new DirectoryInfo(context.FolderContent)); - } - - context.Log.Info("{0} file(s) successfully uploaded via FTP.".FormatInvariant(_succeededFiles)); - } - } + public class FtpFilePublisher : IFilePublisher + { + private ExportDeploymentContext _context; + private ExportDeployment _deployment; + private int _succeededFiles; + private string _ftpRootUrl; + + private string GetRelativePath(string path) + { + var relativePath = path.Substring(_context.FolderContent.Length).Replace("\\", "/"); + if (relativePath.StartsWith("/")) + { + return relativePath.Substring(1); + } + + return relativePath; + } + + private FtpWebRequest CreateRequest(string url, bool keepAlive = true, long? contentLength = null) + { + var request = (FtpWebRequest)WebRequest.Create(url); + request.Method = WebRequestMethods.Ftp.UploadFile; + request.KeepAlive = keepAlive; + request.UseBinary = true; + request.Proxy = null; + request.UsePassive = _deployment.PassiveMode; + request.EnableSsl = _deployment.UseSsl; + + if (_deployment.Username.HasValue()) + { + request.Credentials = new NetworkCredential(_deployment.Username, _deployment.Password); + } + + if (contentLength.HasValue) + { + request.ContentLength = contentLength.Value; + } + + return request; + } + + private bool UploadFile(string path, string fileUrl, bool keepAlive = true) + { + var succeeded = false; + var bytesRead = 0; + var buffLength = 32768; + byte[] buff = new byte[buffLength]; + + var request = CreateRequest(fileUrl, keepAlive, (new FileInfo(path)).Length); + request.Method = WebRequestMethods.Ftp.UploadFile; + + var requestStream = request.GetRequestStream(); + using (var stream = new FileStream(path, FileMode.Open)) + { + while ((bytesRead = stream.Read(buff, 0, buffLength)) != 0) + { + requestStream.Write(buff, 0, bytesRead); + } + } + + requestStream.Close(); + + using (var response = (FtpWebResponse)request.GetResponse()) + { + var statusCode = (int)response.StatusCode; + succeeded = statusCode >= 200 && statusCode <= 299; + + if (succeeded) + { + ++_succeededFiles; + } + else + { + _context.Result.LastError = _context.T("Admin.Common.FtpStatus", statusCode, response.StatusCode.ToString()); + _context.Log.Error("The FTP transfer failed. FTP status {0} ({1}). File {3}".FormatInvariant(statusCode, response.StatusCode.ToString(), path)); + } + } + + return succeeded; + } + + private bool DirectoryExists(string directoryUrl) + { + var result = false; + + try + { + var request = CreateRequest(directoryUrl.EnsureEndsWith("/")); + request.Method = WebRequestMethods.Ftp.ListDirectory; + + using (request.GetResponse()) + { + result = true; + } + } + catch (WebException) + { + result = false; + } + + return result; + } + + private bool FtpCopyDirectory(DirectoryInfo source) + { + var files = source.GetFiles(); + var len = files.Length; + + for (var i = 0; i < len; ++i) + { + var path = files[i].FullName; + var relativePath = GetRelativePath(path); + + UploadFile(path, _ftpRootUrl + relativePath, i != (len - 1)); + } + + foreach (var sourceSubDir in source.GetDirectories()) + { + var relativePath = GetRelativePath(sourceSubDir.FullName); + + if (!DirectoryExists(_ftpRootUrl + relativePath)) + { + var request = CreateRequest(_ftpRootUrl + relativePath, true); + request.Method = WebRequestMethods.Ftp.MakeDirectory; + using (var response = (FtpWebResponse)request.GetResponse()) + { + response.Close(); + } + } + + FtpCopyDirectory(sourceSubDir); + } + + return true; + } + + public virtual void Publish(ExportDeploymentContext context, ExportDeployment deployment) + { + _context = context; + _deployment = deployment; + _succeededFiles = 0; + _ftpRootUrl = deployment.Url; + + if (!_ftpRootUrl.StartsWith("ftp://", StringComparison.InvariantCultureIgnoreCase)) + { + _ftpRootUrl = "ftp://" + _ftpRootUrl; + } + + _ftpRootUrl = _ftpRootUrl.EnsureEndsWith("/"); + + if (context.CreateZipArchive) + { + if (File.Exists(context.ZipPath)) + { + var fileUrl = _ftpRootUrl + Path.GetFileName(context.ZipPath); + UploadFile(context.ZipPath, fileUrl, false); + } + } + else + { + FtpCopyDirectory(new DirectoryInfo(context.FolderContent)); + } + + context.Log.Info("{0} file(s) successfully uploaded via FTP.".FormatInvariant(_succeededFiles)); + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/HttpFilePublisher.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/HttpFilePublisher.cs index 9bcdf2f5fd..a484b85a50 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/HttpFilePublisher.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/HttpFilePublisher.cs @@ -8,70 +8,70 @@ namespace SmartStore.Services.DataExchange.Export.Deployment { - public class HttpFilePublisher : IFilePublisher - { - public virtual void Publish(ExportDeploymentContext context, ExportDeployment deployment) - { - var succeededFiles = 0; - var url = deployment.Url; + public class HttpFilePublisher : IFilePublisher + { + public virtual void Publish(ExportDeploymentContext context, ExportDeployment deployment) + { + var succeededFiles = 0; + var url = deployment.Url; - if (!url.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) && !url.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase)) - url = "http://" + url; + if (!url.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase) && !url.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase)) + url = "http://" + url; - if (deployment.HttpTransmissionType == ExportHttpTransmissionType.MultipartFormDataPost) - { - var countFiles = 0; - ICredentials credentials = null; + if (deployment.HttpTransmissionType == ExportHttpTransmissionType.MultipartFormDataPost) + { + var countFiles = 0; + ICredentials credentials = null; - if (deployment.Username.HasValue()) - credentials = new NetworkCredential(deployment.Username, deployment.Password); + if (deployment.Username.HasValue()) + credentials = new NetworkCredential(deployment.Username, deployment.Password); - using (var handler = new HttpClientHandler { Credentials = credentials }) - using (var client = new HttpClient(handler)) - using (var formData = new MultipartFormDataContent()) - { - foreach (var path in context.GetDeploymentFiles()) - { - byte[] fileData = File.ReadAllBytes(path); - formData.Add(new ByteArrayContent(fileData), "file {0}".FormatInvariant(++countFiles), Path.GetFileName(path)); - } + using (var handler = new HttpClientHandler { Credentials = credentials }) + using (var client = new HttpClient(handler)) + using (var formData = new MultipartFormDataContent()) + { + foreach (var path in context.GetDeploymentFiles()) + { + byte[] fileData = File.ReadAllBytes(path); + formData.Add(new ByteArrayContent(fileData), "file {0}".FormatInvariant(++countFiles), Path.GetFileName(path)); + } - var response = client.PostAsync(url, formData).Result; + var response = client.PostAsync(url, formData).Result; - if (response.IsSuccessStatusCode) - { - succeededFiles = countFiles; - } - else if (response.Content != null) - { - context.Result.LastError = context.T("Admin.Common.HttpStatus", (int)response.StatusCode, response.StatusCode.ToString()); + if (response.IsSuccessStatusCode) + { + succeededFiles = countFiles; + } + else if (response.Content != null) + { + context.Result.LastError = context.T("Admin.Common.HttpStatus", (int)response.StatusCode, response.StatusCode.ToString()); - var content = response.Content.ReadAsStringAsync().Result; + var content = response.Content.ReadAsStringAsync().Result; - var msg = "Multipart form data upload failed. HTTP status {0} ({1}). Response: {2}".FormatInvariant( - (int)response.StatusCode, response.StatusCode.ToString(), content.NaIfEmpty().Truncate(2000, "...")); + var msg = "Multipart form data upload failed. HTTP status {0} ({1}). Response: {2}".FormatInvariant( + (int)response.StatusCode, response.StatusCode.ToString(), content.NaIfEmpty().Truncate(2000, "...")); - context.Log.Error(msg); - } - } - } - else - { - using (var webClient = new WebClient()) - { - if (deployment.Username.HasValue()) - webClient.Credentials = new NetworkCredential(deployment.Username, deployment.Password); + context.Log.Error(msg); + } + } + } + else + { + using (var webClient = new WebClient()) + { + if (deployment.Username.HasValue()) + webClient.Credentials = new NetworkCredential(deployment.Username, deployment.Password); - foreach (var path in context.GetDeploymentFiles()) - { - webClient.UploadFile(url, path); + foreach (var path in context.GetDeploymentFiles()) + { + webClient.UploadFile(url, path); - ++succeededFiles; - } - } - } + ++succeededFiles; + } + } + } - context.Log.Info("{0} file(s) successfully uploaded via HTTP ({1}).".FormatInvariant(succeededFiles, deployment.HttpTransmissionType.ToString())); - } - } + context.Log.Info("{0} file(s) successfully uploaded via HTTP ({1}).".FormatInvariant(succeededFiles, deployment.HttpTransmissionType.ToString())); + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/IFilePublisher.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/IFilePublisher.cs index 1ae3301ec4..16bfa4d000 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/IFilePublisher.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/IFilePublisher.cs @@ -6,37 +6,37 @@ namespace SmartStore.Services.DataExchange.Export.Deployment { - public interface IFilePublisher - { - void Publish(ExportDeploymentContext context, ExportDeployment deployment); + public interface IFilePublisher + { + void Publish(ExportDeploymentContext context, ExportDeployment deployment); } - public class ExportDeploymentContext - { - public Localizer T { get; set; } - public ILogger Log { get; set; } + public class ExportDeploymentContext + { + public Localizer T { get; set; } + public ILogger Log { get; set; } - public string FolderContent { get; set; } + public string FolderContent { get; set; } - public string ZipPath { get; set; } - public bool CreateZipArchive { get; set; } + public string ZipPath { get; set; } + public bool CreateZipArchive { get; set; } - public DataDeploymentResult Result { get; set; } + public DataDeploymentResult Result { get; set; } - public IEnumerable GetDeploymentFiles() - { - if (!CreateZipArchive) - { - return System.IO.Directory.EnumerateFiles(FolderContent, "*", SearchOption.AllDirectories); - } + public IEnumerable GetDeploymentFiles() + { + if (!CreateZipArchive) + { + return System.IO.Directory.EnumerateFiles(FolderContent, "*", SearchOption.AllDirectories); + } - if (File.Exists(ZipPath)) - { - return new string[] { ZipPath }; - } + if (File.Exists(ZipPath)) + { + return new string[] { ZipPath }; + } - return new string[0]; - } - } + return new string[0]; + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/PublicFolderPublisher.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/PublicFolderPublisher.cs index 7e9d67beb3..f5143a2033 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/PublicFolderPublisher.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Deployment/PublicFolderPublisher.cs @@ -5,40 +5,40 @@ namespace SmartStore.Services.DataExchange.Export.Deployment { - public class PublicFolderPublisher : IFilePublisher - { - public virtual void Publish(ExportDeploymentContext context, ExportDeployment deployment) - { - var destinationFolder = deployment.GetDeploymentFolder(true); + public class PublicFolderPublisher : IFilePublisher + { + public virtual void Publish(ExportDeploymentContext context, ExportDeployment deployment) + { + var destinationFolder = deployment.GetDeploymentFolder(true); - if (destinationFolder.IsEmpty()) - return; + if (destinationFolder.IsEmpty()) + return; - if (!System.IO.Directory.Exists(destinationFolder)) - { - System.IO.Directory.CreateDirectory(destinationFolder); - } + if (!System.IO.Directory.Exists(destinationFolder)) + { + System.IO.Directory.CreateDirectory(destinationFolder); + } - if (context.CreateZipArchive) - { - if (File.Exists(context.ZipPath)) - { - var destinationFile = Path.Combine(destinationFolder, Path.GetFileName(context.ZipPath)); + if (context.CreateZipArchive) + { + if (File.Exists(context.ZipPath)) + { + var destinationFile = Path.Combine(destinationFolder, Path.GetFileName(context.ZipPath)); - File.Copy(context.ZipPath, destinationFile, true); + File.Copy(context.ZipPath, destinationFile, true); - context.Log.Info($"Copied zipped export data to {destinationFile}."); - } - } - else - { - if (!FileSystemHelper.CopyDirectory(new DirectoryInfo(context.FolderContent), new DirectoryInfo(destinationFolder))) - { - context.Result.LastError = context.T("Admin.DataExchange.Export.Deployment.CopyFileFailed"); - } + context.Log.Info($"Copied zipped export data to {destinationFile}."); + } + } + else + { + if (!FileSystemHelper.CopyDirectory(new DirectoryInfo(context.FolderContent), new DirectoryInfo(destinationFolder))) + { + 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 6c32615d8f..946769b317 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/DynamicEntityHelper.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/DynamicEntityHelper.cs @@ -24,6 +24,7 @@ using SmartStore.Core.Domain.Stores; using SmartStore.Core.Domain.Tax; using SmartStore.Core.Html; +using SmartStore.Core.Logging; using SmartStore.Services.Catalog; using SmartStore.Services.Catalog.Modelling; using SmartStore.Services.Customers; @@ -31,60 +32,61 @@ using SmartStore.Services.DataExchange.Export.Internal; using SmartStore.Services.Localization; using SmartStore.Services.Media; +using SmartStore.Services.Media.Imaging; using SmartStore.Services.Seo; using SmartStore.Utilities; namespace SmartStore.Services.DataExchange.Export { public partial class DataExporter - { - private readonly string[] _orderCustomerAttributes = new string[] - { - SystemCustomerAttributeNames.VatNumber, + { + private readonly string[] _orderCustomerAttributes = new string[] + { + SystemCustomerAttributeNames.VatNumber, SystemCustomerAttributeNames.ImpersonatedCustomerId - }; - - private void PrepareProductDescription(DataExporterContext ctx, dynamic dynObject, Product product) - { - try - { - var languageId = ctx.LanguageId; - string description = ""; - - // Description merging. - if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.None) - { - // Export empty description. - } - else if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.ShortDescriptionOrNameIfEmpty) - { - description = dynObject.FullDescription; - - if (description.IsEmpty()) - description = dynObject.ShortDescription; - if (description.IsEmpty()) - description = dynObject.Name; - } - else if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.ShortDescription) - { - description = dynObject.ShortDescription; - } - else if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.Description) - { - description = dynObject.FullDescription; - } - else if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.NameAndShortDescription) - { - description = ((string)dynObject.Name).Grow((string)dynObject.ShortDescription, " "); - } - else if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.NameAndDescription) - { - description = ((string)dynObject.Name).Grow((string)dynObject.FullDescription, " "); - } - else if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.ManufacturerAndNameAndShortDescription || - ctx.Projection.DescriptionMerging == ExportDescriptionMerging.ManufacturerAndNameAndDescription) - { - var productManus = ctx.ProductExportContext.ProductManufacturers.GetOrLoad(product.Id); + }; + + private void PrepareProductDescription(DataExporterContext ctx, dynamic dynObject, Product product) + { + try + { + var languageId = ctx.LanguageId; + string description = ""; + + // Description merging. + if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.None) + { + // Export empty description. + } + else if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.ShortDescriptionOrNameIfEmpty) + { + description = dynObject.FullDescription; + + if (description.IsEmpty()) + description = dynObject.ShortDescription; + if (description.IsEmpty()) + description = dynObject.Name; + } + else if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.ShortDescription) + { + description = dynObject.ShortDescription; + } + else if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.Description) + { + description = dynObject.FullDescription; + } + else if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.NameAndShortDescription) + { + description = ((string)dynObject.Name).Grow((string)dynObject.ShortDescription, " "); + } + else if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.NameAndDescription) + { + description = ((string)dynObject.Name).Grow((string)dynObject.FullDescription, " "); + } + else if (ctx.Projection.DescriptionMerging == ExportDescriptionMerging.ManufacturerAndNameAndShortDescription || + ctx.Projection.DescriptionMerging == ExportDescriptionMerging.ManufacturerAndNameAndDescription) + { + var productManus = ctx.ProductExportContext.ProductManufacturers.GetOrLoad(product.Id); if (productManus != null && productManus.Any()) { @@ -93,88 +95,90 @@ private void PrepareProductDescription(DataExporterContext ctx, dynamic dynObjec description = translations.GetValue(languageId, manufacturer.Id, nameof(manufacturer.Name)) ?? manufacturer.Name; } - description = description.Grow((string)dynObject.Name, " "); + description = description.Grow((string)dynObject.Name, " "); description = ctx.Projection.DescriptionMerging == ExportDescriptionMerging.ManufacturerAndNameAndShortDescription ? description.Grow((string)dynObject.ShortDescription, " ") : description.Grow((string)dynObject.FullDescription, " "); - } - - // 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 = CommonHelper.GenerateRandomInteger(0, appendText.Length - 1); - description = description.Grow(appendText.SafeGet(rnd), " "); - } - } - - // 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. - if (description.HasValue() && ctx.Projection.DescriptionToPlainText) - { - //Regex reg = new Regex("<[^>]+>", RegexOptions.IgnoreCase); - //description = HttpUtility.HtmlDecode(reg.Replace(description, "")); - - description = HtmlUtils.ConvertHtmlToPlainText(description); - description = HtmlUtils.StripTags(HttpUtility.HtmlDecode(description)); - } - - dynObject.FullDescription = description; - } - catch { } - } - - private decimal? ConvertPrice(DataExporterContext ctx, Product product, decimal? price) - { - if (price.HasValue) - { - if (ctx.Projection.ConvertNetToGrossPrices) - { - decimal taxRate; - price = _taxService.Value.GetProductPrice(product, product.TaxCategoryId, price.Value, true, ctx.ContextCustomer, ctx.ContextCurrency, - _taxSettings.Value.PricesIncludeTax, out taxRate); - } - - if (price != decimal.Zero) - { - price = _currencyService.Value.ConvertFromPrimaryStoreCurrency(price.Value, ctx.ContextCurrency, ctx.Store); - } - } - - return price; - } - - private decimal CalculatePrice( - DataExporterContext ctx, - Product product, - ProductVariantAttributeCombination combination, - ICollection attributeValues) - { - var price = product.Price; - var productContext = ctx.ProductExportContext as PriceCalculationContext; + } + + // 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 = CommonHelper.GenerateRandomInteger(0, appendText.Length - 1); + description = description.Grow(appendText.SafeGet(rnd), " "); + } + } + + // 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. + if (description.HasValue() && ctx.Projection.DescriptionToPlainText) + { + //Regex reg = new Regex("<[^>]+>", RegexOptions.IgnoreCase); + //description = HttpUtility.HtmlDecode(reg.Replace(description, "")); + + description = HtmlUtils.ConvertHtmlToPlainText(description); + description = HtmlUtils.StripTags(HttpUtility.HtmlDecode(description)); + } + + dynObject.FullDescription = description.TrimSafe(); + } + catch { } + } + + private decimal? ConvertPrice(DataExporterContext ctx, Product product, decimal? price) + { + if (price.HasValue) + { + if (ctx.Projection.ConvertNetToGrossPrices) + { + decimal taxRate; + price = _taxService.Value.GetProductPrice(product, product.TaxCategoryId, price.Value, true, ctx.ContextCustomer, ctx.ContextCurrency, + _taxSettings.Value.PricesIncludeTax, out taxRate); + } + + if (price != decimal.Zero) + { + price = _currencyService.Value.ConvertFromPrimaryStoreCurrency(price.Value, ctx.ContextCurrency, ctx.Store); + } + } + + return price; + } + + private decimal CalculatePrice( + DataExporterContext ctx, + Product product, + ProductVariantAttributeCombination combination, + ICollection attributeValues) + { + var price = product.Price; + var productContext = ctx.ProductExportContext as PriceCalculationContext; var associatedProductContext = ctx.AssociatedProductContext as PriceCalculationContext; if (combination != null) - { - // price for attribute combination - var attributesTotalPriceBase = decimal.Zero; + { + // price for attribute combination + var attributesTotalPriceBase = decimal.Zero; - if (attributeValues != null) - { - attributeValues.Each(x => attributesTotalPriceBase += _priceCalculationService.Value.GetProductVariantAttributeValuePriceAdjustment(x, product, ctx.ContextCustomer, productContext)); - } + if (attributeValues != null) + { + attributeValues.Each(x => attributesTotalPriceBase += _priceCalculationService.Value.GetProductVariantAttributeValuePriceAdjustment(x, product, ctx.ContextCustomer, productContext)); + } - price = _priceCalculationService.Value.GetFinalPrice(product, null, ctx.ContextCustomer, attributesTotalPriceBase, true, 1, null, productContext); - } - else if (ctx.Projection.PriceType.HasValue) + price = _priceCalculationService.Value.GetFinalPrice(product, null, ctx.ContextCustomer, attributesTotalPriceBase, true, 1, null, productContext); + } + else if (ctx.Projection.PriceType.HasValue) { var priceType = ctx.Projection.PriceType.Value; @@ -214,10 +218,10 @@ private decimal CalculatePrice( price = _priceCalculationService.Value.GetLowestPrice(product, ctx.ContextCustomer, productContext, out _); } } - } + } - return ConvertPrice(ctx, product, price) ?? price; - } + return ConvertPrice(ctx, product, price) ?? price; + } private List GetLocalized( DataExporterContext ctx, @@ -282,229 +286,238 @@ private List GetLocalized( } private dynamic ToDynamic(DataExporterContext ctx, ExportProfile profile) - { - if (profile == null) - return null; + { + if (profile == null) + return null; - dynamic result = new DynamicEntity(profile); - return result; - } + dynamic result = new DynamicEntity(profile); + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, Currency currency) - { + private dynamic ToDynamic(DataExporterContext ctx, Currency currency) + { if (currency == null) { return null; } - dynamic result = new DynamicEntity(currency); + 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); - return result; - } + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, Language language) - { + private dynamic ToDynamic(DataExporterContext ctx, Language language) + { if (language == null) { return null; } - dynamic result = new DynamicEntity(language); - return result; - } + dynamic result = new DynamicEntity(language); + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, Country country) - { + private dynamic ToDynamic(DataExporterContext ctx, Country country) + { if (country == null) { return null; } - dynamic result = new DynamicEntity(country); + 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._Localized = GetLocalized(ctx, translations, null, country, x => x.Name); - return result; - } + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, Address address) - { + private dynamic ToDynamic(DataExporterContext ctx, Address address) + { if (address == null) { return null; } - dynamic result = new DynamicEntity(address); + dynamic result = new DynamicEntity(address); result.Country = ToDynamic(ctx, address.Country); - if (address.StateProvinceId.GetValueOrDefault() > 0) - { - dynamic sp = new DynamicEntity(address.StateProvince); + 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 = translations.GetValue(ctx.LanguageId, address.StateProvince.Id, nameof(StateProvince.Name)) ?? address.StateProvince.Name; + sp._Localized = GetLocalized(ctx, translations, null, address.StateProvince, x => x.Name); - result.StateProvince = sp; - } - else - { - result.StateProvince = null; - } + result.StateProvince = sp; + } + else + { + result.StateProvince = null; + } - return result; - } + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, RewardPointsHistory points) - { + private dynamic ToDynamic(DataExporterContext ctx, RewardPointsHistory points) + { if (points == null) { return null; } - dynamic result = new DynamicEntity(points); - return result; - } + dynamic result = new DynamicEntity(points); + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, Customer customer) - { + private dynamic ToDynamic(DataExporterContext ctx, Customer customer) + { if (customer == null) { return null; } - dynamic result = new DynamicEntity(customer); + dynamic result = new DynamicEntity(customer); - result.BillingAddress = null; - result.ShippingAddress = null; - result.Addresses = null; + result.BillingAddress = null; + result.ShippingAddress = null; + result.Addresses = null; - result.RewardPointsHistory = null; - result._RewardPointsBalance = 0; + result.RewardPointsHistory = null; + result._RewardPointsBalance = 0; - result._GenericAttributes = null; - result._HasNewsletterSubscription = false; + result._GenericAttributes = null; + result._HasNewsletterSubscription = false; - result._FullName = null; - result._AvatarPictureUrl = null; + result._FullName = null; + result._AvatarPictureUrl = null; - result.CustomerRoles = customer.CustomerRoles - .Select(x => - { - dynamic dyn = new DynamicEntity(x); - return dyn; - }) - .ToList(); + result.CustomerRoles = customer.CustomerRoleMappings + .Select(x => + { + dynamic dyn = new DynamicEntity(x.CustomerRole); + return dyn; + }) + .ToList(); - return result; - } + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, Store store) - { + private dynamic ToDynamic(DataExporterContext ctx, Store store) + { if (store == null) { return null; } - dynamic result = new DynamicEntity(store); + dynamic result = new DynamicEntity(store); - result.PrimaryStoreCurrency = ToDynamic(ctx, store.PrimaryStoreCurrency); - result.PrimaryExchangeRateCurrency = ToDynamic(ctx, store.PrimaryExchangeRateCurrency); + result.PrimaryStoreCurrency = ToDynamic(ctx, store.PrimaryStoreCurrency); + result.PrimaryExchangeRateCurrency = ToDynamic(ctx, store.PrimaryExchangeRateCurrency); - return result; - } + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, DeliveryTime deliveryTime) - { + private dynamic ToDynamic(DataExporterContext ctx, DeliveryTime deliveryTime) + { if (deliveryTime == null) { return null; } - dynamic result = new DynamicEntity(deliveryTime); + 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._Localized = GetLocalized(ctx, translations, null, deliveryTime, x => x.Name); - return result; - } + return result; + } - private void ToDeliveryTime(DataExporterContext ctx, dynamic parent, int? deliveryTimeId) - { - if (ctx.DeliveryTimes != null) - { + private void ToDeliveryTime(DataExporterContext ctx, dynamic parent, int? deliveryTimeId) + { + if (ctx.DeliveryTimes != null) + { parent.DeliveryTime = deliveryTimeId.HasValue && ctx.DeliveryTimes.ContainsKey(deliveryTimeId.Value) ? ToDynamic(ctx, ctx.DeliveryTimes[deliveryTimeId.Value]) : null; - } - } + } + } - private dynamic ToDynamic(DataExporterContext ctx, QuantityUnit quantityUnit) - { + private dynamic ToDynamic(DataExporterContext ctx, QuantityUnit quantityUnit) + { if (quantityUnit == null) { return null; } - dynamic result = new DynamicEntity(quantityUnit); + 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.NamePlural = translations.GetValue(ctx.LanguageId, quantityUnit.Id, nameof(quantityUnit.NamePlural)) ?? quantityUnit.NamePlural; result.Description = translations.GetValue(ctx.LanguageId, quantityUnit.Id, nameof(quantityUnit.Description)) ?? quantityUnit.Description; - result._Localized = GetLocalized(ctx, translations, null, quantityUnit, - x => x.Name, + result._Localized = GetLocalized(ctx, translations, null, quantityUnit, + x => x.Name, x => x.NamePlural, - x => x.Description); + x => x.Description); - return result; - } + return result; + } - private void ToQuantityUnit(DataExporterContext ctx, dynamic parent, int? quantityUnitId) - { - if (ctx.QuantityUnits != null) - { + private void ToQuantityUnit(DataExporterContext ctx, dynamic parent, int? quantityUnitId) + { + if (ctx.QuantityUnits != null) + { parent.QuantityUnit = quantityUnitId.HasValue && ctx.QuantityUnits.ContainsKey(quantityUnitId.Value) ? ToDynamic(ctx, ctx.QuantityUnits[quantityUnitId.Value]) : null; - } - } + } + } + + private dynamic ToDynamic(DataExporterContext ctx, MediaFile file, int thumbPictureSize, int detailsPictureSize) + { + return ToDynamic(ctx, _mediaService.Value.ConvertMediaFile(file), thumbPictureSize, detailsPictureSize); + } - private dynamic ToDynamic(DataExporterContext ctx, Picture picture, int thumbPictureSize, int detailsPictureSize) - { - if (picture == null) + private dynamic ToDynamic(DataExporterContext ctx, MediaFileInfo file, int thumbPictureSize, int detailsPictureSize) + { + if (file == 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); + try + { + var host = _services.StoreService.GetHost(ctx.Store); + + dynamic result = new DynamicEntity(file.File); - if (pictureInfo != null) - { - result._FileName = System.IO.Path.GetFileName(pictureInfo.Path); - result._RelativeUrl = _pictureService.Value.GetUrl(pictureInfo, 0, FallbackPictureType.NoFallback); - 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._FileName = file.Name; + result._RelativeUrl = _mediaService.Value.GetUrl(file, null, null); + result._ThumbImageUrl = _mediaService.Value.GetUrl(file, thumbPictureSize, host); + result._ImageUrl = _mediaService.Value.GetUrl(file, detailsPictureSize, host); + result._FullSizeImageUrl = _mediaService.Value.GetUrl(file, null, host); - return result; - } + return result; + } + catch (Exception ex) + { + ctx.Log.ErrorFormat(ex, $"Failed to get details for file with ID {file.File.Id}."); + return null; + } + } - private dynamic ToDynamic(DataExporterContext ctx, ProductVariantAttribute pva) - { + private dynamic ToDynamic(DataExporterContext ctx, ProductVariantAttribute pva) + { if (pva == null) { return null; @@ -514,56 +527,56 @@ private dynamic ToDynamic(DataExporterContext ctx, ProductVariantAttribute pva) var attribute = pva.ProductAttribute; dynamic result = new DynamicEntity(pva); - dynamic dynAttribute = new DynamicEntity(attribute); + dynamic dynAttribute = new DynamicEntity(attribute); var paTranslations = ctx.TranslationsPerPage[nameof(ProductAttribute)]; var pvavTranslations = ctx.TranslationsPerPage[nameof(ProductVariantAttributeValue)]; dynAttribute.Name = paTranslations.GetValue(languageId, attribute.Id, nameof(attribute.Name)) ?? attribute.Name; - dynAttribute.Description = paTranslations.GetValue(languageId, attribute.Id, nameof(attribute.Description)) ?? attribute.Description; + dynAttribute.Description = paTranslations.GetValue(languageId, attribute.Id, nameof(attribute.Description)) ?? attribute.Description; - dynAttribute.Values = pva.ProductVariantAttributeValues - .OrderBy(x => x.DisplayOrder) - .Select(x => - { - dynamic dyn = new DynamicEntity(x); + dynAttribute.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._Localized = GetLocalized(ctx, pvavTranslations, null, x, y => y.Name); - return dyn; - }) - .ToList(); + return dyn; + }) + .ToList(); - dynAttribute._Localized = GetLocalized(ctx, paTranslations, null, attribute, - x => x.Name, - x => x.Description); + dynAttribute._Localized = GetLocalized(ctx, paTranslations, null, attribute, + x => x.Name, + x => x.Description); - result.Attribute = dynAttribute; + result.Attribute = dynAttribute; - return result; - } + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, ProductVariantAttributeCombination pvac) - { - if (pvac == null) - return null; + private dynamic ToDynamic(DataExporterContext ctx, ProductVariantAttributeCombination pvac) + { + if (pvac == null) + return null; - dynamic result = new DynamicEntity(pvac); + dynamic result = new DynamicEntity(pvac); - ToDeliveryTime(ctx, result, pvac.DeliveryTimeId); - ToQuantityUnit(ctx, result, pvac.QuantityUnitId); + ToDeliveryTime(ctx, result, pvac.DeliveryTimeId); + ToQuantityUnit(ctx, result, pvac.QuantityUnitId); - return result; - } + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, Manufacturer manufacturer) - { + private dynamic ToDynamic(DataExporterContext ctx, Manufacturer manufacturer) + { if (manufacturer == null) { return null; } - dynamic result = new DynamicEntity(manufacturer); + dynamic result = new DynamicEntity(manufacturer); var translations = ctx.Translations[nameof(Manufacturer)]; var urlRecords = ctx.UrlRecords[nameof(Manufacturer)]; @@ -571,103 +584,105 @@ private dynamic ToDynamic(DataExporterContext ctx, Manufacturer manufacturer) result.Name = translations.GetValue(ctx.LanguageId, manufacturer.Id, nameof(manufacturer.Name)) ?? manufacturer.Name; if (!ctx.IsPreview) - { - result.SeName = ctx.UrlRecords[nameof(Manufacturer)].GetSlug(ctx.LanguageId, manufacturer.Id); + { + 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._Localized = GetLocalized(ctx, translations, urlRecords, manufacturer, - x => x.Name, - x => x.Description, - x => x.MetaKeywords, - x => x.MetaDescription, - x => x.MetaTitle); - } - - return result; - } - - private dynamic ToDynamic(DataExporterContext ctx, Category category) - { + result.BottomDescription = translations.GetValue(ctx.LanguageId, manufacturer.Id, nameof(manufacturer.BottomDescription)) ?? manufacturer.BottomDescription; + 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._Localized = GetLocalized(ctx, translations, urlRecords, manufacturer, + x => x.Name, + x => x.Description, + x => x.BottomDescription, + x => x.MetaKeywords, + x => x.MetaDescription, + x => x.MetaTitle); + } + + return result; + } + + private dynamic ToDynamic(DataExporterContext ctx, Category category) + { if (category == null) { return null; } - dynamic result = new DynamicEntity(category); + 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.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; - if (!ctx.IsPreview) - { + 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._CategoryTemplateViewPath = ctx.CategoryTemplates.ContainsKey(category.CategoryTemplateId) - ? ctx.CategoryTemplates[category.CategoryTemplateId] - : ""; - - result._Localized = GetLocalized(ctx, translations, urlRecords, category, - x => x.Name, - x => x.FullName, - x => x.Description, - x => x.BottomDescription, - x => x.MetaKeywords, - x => x.MetaDescription, - x => x.MetaTitle); - } - - return result; - } - - private dynamic ToDynamic(DataExporterContext ctx, Product product, string seName = null) - { + 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._CategoryTemplateViewPath = ctx.CategoryTemplates.ContainsKey(category.CategoryTemplateId) + ? ctx.CategoryTemplates[category.CategoryTemplateId] + : ""; + + result._Localized = GetLocalized(ctx, translations, urlRecords, category, + x => x.Name, + x => x.FullName, + x => x.Description, + x => x.BottomDescription, + x => x.MetaKeywords, + x => x.MetaDescription, + x => x.MetaTitle); + } + + return result; + } + + private dynamic ToDynamic(DataExporterContext ctx, Product product, string seName = null) + { if (product == null) { return null; } - dynamic result = new DynamicEntity(product); + 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.Downloads = null; - result.TierPrices = null; - result.ProductAttributes = null; - result.ProductAttributeCombinations = null; - result.ProductPictures = null; - result.ProductCategories = null; - result.ProductManufacturers = null; - result.ProductTags = null; - result.ProductSpecificationAttributes = null; - result.ProductBundleItems = null; + result.TierPrices = null; + result.ProductAttributes = null; + result.ProductAttributeCombinations = null; + result.ProductPictures = null; + result.ProductCategories = null; + result.ProductManufacturers = null; + result.ProductTags = null; + result.ProductSpecificationAttributes = null; + result.ProductBundleItems = null; result.Name = localizedName; - if (!ctx.IsPreview) - { - result.SeName = seName ?? ctx.UrlRecordsPerPage[nameof(Product)].GetSlug(ctx.LanguageId, product.Id); + 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.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._ProductTemplateViewPath = ctx.ProductTemplates.ContainsKey(product.ProductTemplateId) - ? ctx.ProductTemplates[product.ProductTemplateId] - : string.Empty; + result._ProductTemplateViewPath = ctx.ProductTemplates.ContainsKey(product.ProductTemplateId) + ? ctx.ProductTemplates[product.ProductTemplateId] + : string.Empty; // A standard, language independent base price formatting: / var basePriceInfo = string.Empty; @@ -689,34 +704,45 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, string seNam result._BasePriceInfo = basePriceInfo; ToDeliveryTime(ctx, result, product.DeliveryTimeId); - ToQuantityUnit(ctx, result, product.QuantityUnitId); + ToQuantityUnit(ctx, result, product.QuantityUnitId); + + result.CountryOfOrigin = product.CountryOfOriginId.HasValue + ? ToDynamic(ctx, product.CountryOfOrigin) + : null; result._Localized = GetLocalized(ctx, translations, urlRecords, product, - x => x.Name, - x => x.ShortDescription, - x => x.FullDescription, - x => x.MetaKeywords, - x => x.MetaDescription, - x => x.MetaTitle, - x => x.BundleTitleText); - } - - return result; - } - - private dynamic ToDynamic(DataExporterContext ctx, Product product, bool isParent, DynamicProductContext productContext) - { - product.MergeWithCombination(productContext.Combination); - - var numberOfPictures = ctx.Projection.NumberOfPictures ?? int.MaxValue; - var productDetailsPictureSize = ctx.Projection.PictureSize > 0 ? ctx.Projection.PictureSize : _mediaSettings.Value.ProductDetailsPictureSize; - - IEnumerable productPictures = ctx.ProductExportContext.ProductPictures.GetOrLoad(product.Id); - var productManufacturers = ctx.ProductExportContext.ProductManufacturers.GetOrLoad(product.Id); - var productCategories = ctx.ProductExportContext.ProductCategories.GetOrLoad(product.Id); - var productAttributes = ctx.ProductExportContext.Attributes.GetOrLoad(product.Id); - var productTags = ctx.ProductExportContext.ProductTags.GetOrLoad(product.Id); - var specificationAttributes = ctx.ProductExportContext.SpecificationAttributes.GetOrLoad(product.Id); + x => x.Name, + x => x.ShortDescription, + x => x.FullDescription, + x => x.MetaKeywords, + x => x.MetaDescription, + x => x.MetaTitle, + x => x.BundleTitleText); + } + + return result; + } + + private dynamic ToDynamic(DataExporterContext ctx, Product product, bool isParent, DynamicProductContext productContext) + { + product.MergeWithCombination(productContext.Combination); + + var numberOfPictures = ctx.Projection.NumberOfMediaFiles ?? int.MaxValue; + var productDetailsPictureSize = _mediaSettings.Value.ProductDetailsPictureSize; + ProcessImageQuery imageQuery = null; + + if (ctx.Projection.PictureSize > 0) + { + productDetailsPictureSize = _mediaSettings.Value.GetNextValidThumbnailSize(ctx.Projection.PictureSize); + imageQuery = new ProcessImageQuery { MaxWidth = productDetailsPictureSize }; + } + + IEnumerable productPictures = ctx.ProductExportContext.ProductPictures.GetOrLoad(product.Id); + var productManufacturers = ctx.ProductExportContext.ProductManufacturers.GetOrLoad(product.Id); + var productCategories = ctx.ProductExportContext.ProductCategories.GetOrLoad(product.Id); + var productAttributes = ctx.ProductExportContext.Attributes.GetOrLoad(product.Id); + var productTags = ctx.ProductExportContext.ProductTags.GetOrLoad(product.Id); + var specificationAttributes = ctx.ProductExportContext.SpecificationAttributes.GetOrLoad(product.Id); Multimap variantAttributes = null; ICollection variantAttributeValues = null; string attributesXml = null; @@ -731,11 +757,11 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, bool isParen if (productContext.Combination != null) { - var pictureIds = productContext.Combination.GetAssignedPictureIds(); - if (pictureIds.Any()) - { - productPictures = productPictures.Where(x => pictureIds.Contains(x.PictureId)); - } + var mediaIds = productContext.Combination.GetAssignedMediaIds(); + if (mediaIds.Any()) + { + productPictures = productPictures.Where(x => mediaIds.Contains(x.MediaFileId)); + } attributesXml = productContext.Combination.AttributesXml; variantAttributes = _productAttributeParser.Value.DeserializeProductVariantAttributes(attributesXml); @@ -779,136 +805,136 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, bool isParen else { dynObject._DetailUrl = productContext.AbsoluteProductUrl; - } - - dynObject.Price = CalculatePrice(ctx, product, productContext.Combination, variantAttributeValues); - - // Category path - { - var categoryPath = string.Empty; - var pc = productCategories.OrderBy(x => x.DisplayOrder).FirstOrDefault(); - - if (pc != null) - { - var node = _categoryService.Value.GetCategoryTree(pc.CategoryId, true, ctx.Store.Id); - if (node != null) - { - categoryPath = _categoryService.Value.GetCategoryPath(node, ctx.Projection.LanguageId, null, " > "); - } - } - - dynObject._CategoryPath = categoryPath; - } - - if (ctx.Countries != null) - { - dynObject.CountryOfOrigin = product.CountryOfOriginId.HasValue && ctx.Countries.ContainsKey(product.CountryOfOriginId.Value) - ? ToDynamic(ctx, ctx.Countries[product.CountryOfOriginId.Value]) - : null; - } - - dynObject.ProductPictures = productPictures - .OrderBy(x => x.DisplayOrder) - .Select(x => - { - dynamic dyn = new DynamicEntity(x); - - dyn.Picture = ToDynamic(ctx, x.Picture, _mediaSettings.Value.ProductThumbPictureSize, productDetailsPictureSize); - - return dyn; - }) - .ToList(); - - dynObject.ProductManufacturers = productManufacturers - .OrderBy(x => x.DisplayOrder) - .Select(x => - { - dynamic dyn = new DynamicEntity(x); - - dyn.Manufacturer = ToDynamic(ctx, x.Manufacturer); - - dyn.Manufacturer.Picture = x.Manufacturer != null && x.Manufacturer.PictureId.HasValue - ? ToDynamic(ctx, x.Manufacturer.Picture, _mediaSettings.Value.ManufacturerThumbPictureSize, _mediaSettings.Value.ManufacturerThumbPictureSize) - : null; - - return dyn; - }) - .ToList(); - - dynObject.ProductCategories = productCategories - .OrderBy(x => x.DisplayOrder) - .Select(x => - { - dynamic dyn = new DynamicEntity(x); - - dyn.Category = ToDynamic(ctx, x.Category); - - if (x.Category != null && x.Category.PictureId.HasValue) - dyn.Category.Picture = ToDynamic(ctx, x.Category.Picture, _mediaSettings.Value.CategoryThumbPictureSize, _mediaSettings.Value.CategoryThumbPictureSize); - - if (dynObject._CategoryName == null) - dynObject._CategoryName = (string)dyn.Category.Name; - - return dyn; - }) - .ToList(); - - dynObject.ProductAttributes = productAttributes - .OrderBy(x => x.DisplayOrder) - .Select(x => ToDynamic(ctx, x)) - .ToList(); - - // Do not export combinations if a combination is exported as a product. - if (productContext.Combinations != null && productContext.Combination == null) - { - dynObject.ProductAttributeCombinations = productContext.Combinations + } + + dynObject.Price = CalculatePrice(ctx, product, productContext.Combination, variantAttributeValues); + + // Category path + { + var categoryPath = string.Empty; + var pc = productCategories.OrderBy(x => x.DisplayOrder).FirstOrDefault(); + + if (pc != null) + { + var node = _categoryService.Value.GetCategoryTree(pc.CategoryId, true, ctx.Store.Id); + if (node != null) + { + categoryPath = _categoryService.Value.GetCategoryPath(node, ctx.Projection.LanguageId, null, " > "); + } + } + + dynObject._CategoryPath = categoryPath; + } + + if (ctx.Countries != null) + { + dynObject.CountryOfOrigin = product.CountryOfOriginId.HasValue && ctx.Countries.ContainsKey(product.CountryOfOriginId.Value) + ? ToDynamic(ctx, ctx.Countries[product.CountryOfOriginId.Value]) + : null; + } + + dynObject.ProductPictures = productPictures + .OrderBy(x => x.DisplayOrder) + .Select(x => + { + dynamic dyn = new DynamicEntity(x); + + dyn.Picture = ToDynamic(ctx, x.MediaFile, _mediaSettings.Value.ProductThumbPictureSize, productDetailsPictureSize); + + return dyn; + }) + .ToList(); + + dynObject.ProductManufacturers = productManufacturers + .OrderBy(x => x.DisplayOrder) + .Select(x => + { + dynamic dyn = new DynamicEntity(x); + + dyn.Manufacturer = ToDynamic(ctx, x.Manufacturer); + + dyn.Manufacturer.Picture = x.Manufacturer != null && x.Manufacturer.MediaFileId.HasValue + ? ToDynamic(ctx, x.Manufacturer.MediaFile, _mediaSettings.Value.ManufacturerThumbPictureSize, _mediaSettings.Value.ManufacturerThumbPictureSize) + : null; + + return dyn; + }) + .ToList(); + + dynObject.ProductCategories = productCategories + .OrderBy(x => x.DisplayOrder) + .Select(x => + { + dynamic dyn = new DynamicEntity(x); + + dyn.Category = ToDynamic(ctx, x.Category); + + if (x.Category != null && x.Category.MediaFileId.HasValue) + dyn.Category.Picture = ToDynamic(ctx, x.Category.MediaFile, _mediaSettings.Value.CategoryThumbPictureSize, _mediaSettings.Value.CategoryThumbPictureSize); + + if (dynObject._CategoryName == null) + dynObject._CategoryName = (string)dyn.Category.Name; + + return dyn; + }) + .ToList(); + + dynObject.ProductAttributes = productAttributes + .OrderBy(x => x.DisplayOrder) + .Select(x => ToDynamic(ctx, x)) + .ToList(); + + // Do not export combinations if a combination is exported as a product. + if (productContext.Combinations != null && productContext.Combination == null) + { + dynObject.ProductAttributeCombinations = productContext.Combinations + .Select(x => + { + dynamic dyn = ToDynamic(ctx, x); + var assignedPictures = new List(); + + foreach (int pictureId in x.GetAssignedMediaIds().Take(numberOfPictures)) + { + var assignedPicture = productPictures.FirstOrDefault(y => y.MediaFileId == pictureId); + if (assignedPicture != null && assignedPicture.MediaFile != null) + { + assignedPictures.Add(ToDynamic(ctx, assignedPicture.MediaFile, _mediaSettings.Value.ProductThumbPictureSize, productDetailsPictureSize)); + } + } + + dyn.Pictures = assignedPictures; + + return dyn; + }) + .ToList(); + } + else + { + dynObject.ProductAttributeCombinations = Enumerable.Empty(); + } + + if (product.HasTierPrices) + { + var tierPrices = ctx.ProductExportContext.TierPrices.GetOrLoad(product.Id) + .RemoveDuplicatedQuantities(); + + dynObject.TierPrices = tierPrices .Select(x => - { - dynamic dyn = ToDynamic(ctx, x); - var assignedPictures = new List(); - - foreach (int pictureId in x.GetAssignedPictureIds().Take(numberOfPictures)) - { - var assignedPicture = productPictures.FirstOrDefault(y => y.PictureId == pictureId); - if (assignedPicture != null && assignedPicture.Picture != null) - { - assignedPictures.Add(ToDynamic(ctx, assignedPicture.Picture, _mediaSettings.Value.ProductThumbPictureSize, productDetailsPictureSize)); - } - } - - dyn.Pictures = assignedPictures; - - return dyn; - }) - .ToList(); - } - else - { - dynObject.ProductAttributeCombinations = Enumerable.Empty(); - } - - if (product.HasTierPrices) - { - var tierPrices = ctx.ProductExportContext.TierPrices.GetOrLoad(product.Id) - .RemoveDuplicatedQuantities(); - - dynObject.TierPrices = tierPrices - .Select(x => - { - dynamic dyn = new DynamicEntity(x); - return dyn; - }) - .ToList(); - } - - if (product.HasDiscountsApplied) - { - var appliedDiscounts = ctx.ProductExportContext.AppliedDiscounts.GetOrLoad(product.Id); - - dynObject.AppliedDiscounts = appliedDiscounts - .Select(x => ToDynamic(ctx, x)) - .ToList(); - } + { + dynamic dyn = new DynamicEntity(x); + return dyn; + }) + .ToList(); + } + + if (product.HasDiscountsApplied) + { + var appliedDiscounts = ctx.ProductExportContext.AppliedDiscounts.GetOrLoad(product.Id); + + dynObject.AppliedDiscounts = appliedDiscounts + .Select(x => ToDynamic(ctx, x)) + .ToList(); + } if (product.IsDownload) { @@ -920,56 +946,56 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, bool isParen } dynObject.ProductTags = productTags - .Select(x => - { - dynamic dyn = new DynamicEntity(x); + .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.SeName = SeoExtensions.GetSeName(localizedName, _seoSettings.Value); + dyn._Localized = GetLocalized(ctx, translations, null, x, y => y.Name); - return dyn; - }) - .ToList(); + return dyn; + }) + .ToList(); - dynObject.ProductSpecificationAttributes = specificationAttributes - .Select(x => ToDynamic(ctx, x)) - .ToList(); + dynObject.ProductSpecificationAttributes = specificationAttributes + .Select(x => ToDynamic(ctx, x)) + .ToList(); - if (product.ProductType == ProductType.BundledProduct) - { - var bundleItems = ctx.ProductExportContext.ProductBundleItems.GetOrLoad(product.Id); + if (product.ProductType == ProductType.BundledProduct) + { + var bundleItems = ctx.ProductExportContext.ProductBundleItems.GetOrLoad(product.Id); - dynObject.ProductBundleItems = bundleItems - .Select(x => - { - dynamic dyn = new DynamicEntity(x); + dynObject.ProductBundleItems = bundleItems + .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._Localized = GetLocalized(ctx, translations, null, x, y => y.Name, y => y.ShortDescription); - return dyn; - }) - .ToList(); - } + return dyn; + }) + .ToList(); + } - #endregion + #endregion - #region More data based on export features + #region More data based on export features - if (ctx.Supports(ExportFeatures.CanProjectDescription)) - { - PrepareProductDescription(ctx, dynObject, product); - } + if (ctx.Supports(ExportFeatures.CanProjectDescription)) + { + PrepareProductDescription(ctx, dynObject, product); + } - if (ctx.Supports(ExportFeatures.OffersBrandFallback)) - { - string brand = null; - var productManus = ctx.ProductExportContext.ProductManufacturers.GetOrLoad(product.Id); + if (ctx.Supports(ExportFeatures.OffersBrandFallback)) + { + string brand = null; + var productManus = ctx.ProductExportContext.ProductManufacturers.GetOrLoad(product.Id); if (productManus != null && productManus.Any()) { @@ -982,185 +1008,187 @@ private dynamic ToDynamic(DataExporterContext ctx, Product product, bool isParen brand = ctx.Projection.Brand; } - dynObject._Brand = brand; - } - - if (ctx.Supports(ExportFeatures.CanIncludeMainPicture)) - { - if (productPictures != null && productPictures.Any()) - { - var firstPicture = productPictures.First().Picture; - dynObject._MainPictureUrl = _pictureService.Value.GetUrl(firstPicture, ctx.Projection.PictureSize, host: _services.StoreService.GetHost(ctx.Store)); - dynObject._MainPictureRelativeUrl = _pictureService.Value.GetUrl(firstPicture, ctx.Projection.PictureSize); - } - else if (!_catalogSettings.Value.HideProductDefaultPictures) - { - dynObject._MainPictureUrl = _pictureService.Value.GetFallbackUrl(ctx.Projection.PictureSize, host: _services.StoreService.GetHost(ctx.Store)); - dynObject._MainPictureRelativeUrl = _pictureService.Value.GetFallbackUrl(ctx.Projection.PictureSize); - } - else - { - dynObject._MainPictureUrl = null; - dynObject._MainPictureRelativeUrl = null; - } - } - - if (ctx.Supports(ExportFeatures.UsesSkuAsMpnFallback) && product.ManufacturerPartNumber.IsEmpty()) - { - dynObject.ManufacturerPartNumber = product.Sku; - } - - if (ctx.Supports(ExportFeatures.OffersShippingTimeFallback)) - { - dynamic deliveryTime = dynObject.DeliveryTime; - dynObject._ShippingTime = deliveryTime == null ? ctx.Projection.ShippingTime : deliveryTime.Name; - } - - if (ctx.Supports(ExportFeatures.OffersShippingCostsFallback)) - { - dynObject._FreeShippingThreshold = ctx.Projection.FreeShippingThreshold; - - if (product.IsFreeShipping || (ctx.Projection.FreeShippingThreshold.HasValue && (decimal)dynObject.Price >= ctx.Projection.FreeShippingThreshold.Value)) - dynObject._ShippingCosts = decimal.Zero; - else - dynObject._ShippingCosts = ctx.Projection.ShippingCosts; - } - - if (ctx.Supports(ExportFeatures.UsesOldPrice)) - { - if (product.OldPrice != decimal.Zero && product.OldPrice != (decimal)dynObject.Price && !(product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing)) - { - if (ctx.Projection.ConvertNetToGrossPrices) - { - decimal taxRate; - dynObject._OldPrice = _taxService.Value.GetProductPrice(product, product.TaxCategoryId, product.OldPrice, true, ctx.ContextCustomer, - ctx.ContextCurrency, _taxSettings.Value.PricesIncludeTax, out taxRate); - } - else - { - dynObject._OldPrice = product.OldPrice; - } - } - else - { - dynObject._OldPrice = null; - } - } - - if (ctx.Supports(ExportFeatures.UsesSpecialPrice)) - { - dynObject._SpecialPrice = null; // special price which is valid now - dynObject._FutureSpecialPrice = null; // special price which is valid now and in future - dynObject._RegularPrice = null; // price as if a special price would not exist - - if (!(product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing)) - { - if (product.SpecialPrice.HasValue && product.SpecialPriceEndDateTimeUtc.HasValue) - { - var endDate = DateTime.SpecifyKind(product.SpecialPriceEndDateTimeUtc.Value, DateTimeKind.Utc); - if (endDate > DateTime.UtcNow) - { - dynObject._FutureSpecialPrice = ConvertPrice(ctx, product, product.SpecialPrice.Value); - } - } - - var specialPrice = _priceCalculationService.Value.GetSpecialPrice(product); - - dynObject._SpecialPrice = ConvertPrice(ctx, product, specialPrice); - - if (specialPrice.HasValue || dynObject._FutureSpecialPrice != null) - { - decimal tmpSpecialPrice = product.SpecialPrice.Value; - product.SpecialPrice = null; - - dynObject._RegularPrice = CalculatePrice(ctx, product, productContext.Combination, variantAttributeValues); - - product.SpecialPrice = tmpSpecialPrice; - } - } - } - - #endregion - - return dynObject; - } - - private dynamic ToDynamic(DataExporterContext ctx, Order order) - { + dynObject._Brand = brand; + } + + if (ctx.Supports(ExportFeatures.CanIncludeMainPicture)) + { + if (productPictures != null && productPictures.Any()) + { + var file = _mediaService.Value.ConvertMediaFile(productPictures.Select(x => x.MediaFile).First()); + + dynObject._MainPictureUrl = _mediaService.Value.GetUrl(file, imageQuery, _services.StoreService.GetHost(ctx.Store)); + dynObject._MainPictureRelativeUrl = _mediaService.Value.GetUrl(file, imageQuery); + } + else if (!_catalogSettings.Value.HideProductDefaultPictures) + { + // Get fallback image URL. + dynObject._MainPictureUrl = _mediaService.Value.GetUrl(null, imageQuery, _services.StoreService.GetHost(ctx.Store)); + dynObject._MainPictureRelativeUrl = _mediaService.Value.GetUrl(null, imageQuery); + } + else + { + dynObject._MainPictureUrl = null; + dynObject._MainPictureRelativeUrl = null; + } + } + + if (ctx.Supports(ExportFeatures.UsesSkuAsMpnFallback) && product.ManufacturerPartNumber.IsEmpty()) + { + dynObject.ManufacturerPartNumber = product.Sku; + } + + if (ctx.Supports(ExportFeatures.OffersShippingTimeFallback)) + { + dynamic deliveryTime = dynObject.DeliveryTime; + dynObject._ShippingTime = deliveryTime == null ? ctx.Projection.ShippingTime : deliveryTime.Name; + } + + if (ctx.Supports(ExportFeatures.OffersShippingCostsFallback)) + { + dynObject._FreeShippingThreshold = ctx.Projection.FreeShippingThreshold; + + if (product.IsFreeShipping || (ctx.Projection.FreeShippingThreshold.HasValue && (decimal)dynObject.Price >= ctx.Projection.FreeShippingThreshold.Value)) + dynObject._ShippingCosts = decimal.Zero; + else + dynObject._ShippingCosts = ctx.Projection.ShippingCosts; + } + + if (ctx.Supports(ExportFeatures.UsesOldPrice)) + { + if (product.OldPrice != decimal.Zero && product.OldPrice != (decimal)dynObject.Price && !(product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing)) + { + if (ctx.Projection.ConvertNetToGrossPrices) + { + decimal taxRate; + dynObject._OldPrice = _taxService.Value.GetProductPrice(product, product.TaxCategoryId, product.OldPrice, true, ctx.ContextCustomer, + ctx.ContextCurrency, _taxSettings.Value.PricesIncludeTax, out taxRate); + } + else + { + dynObject._OldPrice = product.OldPrice; + } + } + else + { + dynObject._OldPrice = null; + } + } + + if (ctx.Supports(ExportFeatures.UsesSpecialPrice)) + { + dynObject._SpecialPrice = null; // special price which is valid now + dynObject._FutureSpecialPrice = null; // special price which is valid now and in future + dynObject._RegularPrice = null; // price as if a special price would not exist + + if (!(product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing)) + { + if (product.SpecialPrice.HasValue && product.SpecialPriceEndDateTimeUtc.HasValue) + { + var endDate = DateTime.SpecifyKind(product.SpecialPriceEndDateTimeUtc.Value, DateTimeKind.Utc); + if (endDate > DateTime.UtcNow) + { + dynObject._FutureSpecialPrice = ConvertPrice(ctx, product, product.SpecialPrice.Value); + } + } + + var specialPrice = _priceCalculationService.Value.GetSpecialPrice(product); + + dynObject._SpecialPrice = ConvertPrice(ctx, product, specialPrice); + + if (specialPrice.HasValue || dynObject._FutureSpecialPrice != null) + { + decimal tmpSpecialPrice = product.SpecialPrice.Value; + product.SpecialPrice = null; + + dynObject._RegularPrice = CalculatePrice(ctx, product, productContext.Combination, variantAttributeValues); + + product.SpecialPrice = tmpSpecialPrice; + } + } + } + + #endregion + + return dynObject; + } + + private dynamic ToDynamic(DataExporterContext ctx, Order order) + { if (order == null) { return null; } - dynamic result = new DynamicEntity(order); + 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.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.Customer = null; - result.BillingAddress = null; - result.ShippingAddress = null; - result.Shipments = null; + result.Customer = null; + result.BillingAddress = null; + result.ShippingAddress = null; + result.Shipments = null; - result.Store = ctx.Stores.ContainsKey(order.StoreId) - ? ToDynamic(ctx, ctx.Stores[order.StoreId]) - : null; + result.Store = ctx.Stores.ContainsKey(order.StoreId) + ? ToDynamic(ctx, ctx.Stores[order.StoreId]) + : null; - if (!ctx.IsPreview) - { - result.RedeemedRewardPointsEntry = ToDynamic(ctx, order.RedeemedRewardPointsEntry); - } + if (!ctx.IsPreview) + { + result.RedeemedRewardPointsEntry = ToDynamic(ctx, order.RedeemedRewardPointsEntry); + } - return result; - } + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, OrderItem orderItem) - { + private dynamic ToDynamic(DataExporterContext ctx, OrderItem orderItem) + { if (orderItem == null) { return null; } - dynamic result = new DynamicEntity(orderItem); + dynamic result = new DynamicEntity(orderItem); - orderItem.Product.MergeWithCombination(orderItem.AttributesXml, _productAttributeParser.Value); - result.Product = ToDynamic(ctx, orderItem.Product); + orderItem.Product.MergeWithCombination(orderItem.AttributesXml, _productAttributeParser.Value); + result.Product = ToDynamic(ctx, orderItem.Product); - return result; - } + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, Shipment shipment) - { + private dynamic ToDynamic(DataExporterContext ctx, Shipment shipment) + { if (shipment == null) { return null; } - dynamic result = new DynamicEntity(shipment); + dynamic result = new DynamicEntity(shipment); - result.ShipmentItems = shipment.ShipmentItems - .Select(x => - { - dynamic exp = new DynamicEntity(x); - return exp; - }) - .ToList(); + result.ShipmentItems = shipment.ShipmentItems + .Select(x => + { + dynamic exp = new DynamicEntity(x); + return exp; + }) + .ToList(); - return result; - } + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, Discount discount) - { + private dynamic ToDynamic(DataExporterContext ctx, Discount discount) + { if (discount == null) { return null; } - dynamic result = new DynamicEntity(discount); - return result; - } + dynamic result = new DynamicEntity(discount); + return result; + } private dynamic ToDynamic(DataExporterContext ctx, Download download) { @@ -1174,7 +1202,7 @@ private dynamic ToDynamic(DataExporterContext ctx, Download download) } private dynamic ToDynamic(DataExporterContext ctx, ProductSpecificationAttribute psa) - { + { if (psa == null) { return null; @@ -1184,81 +1212,81 @@ private dynamic ToDynamic(DataExporterContext ctx, ProductSpecificationAttribute var attribute = option.SpecificationAttribute; dynamic result = new DynamicEntity(psa); - dynamic dynAttribute = new DynamicEntity(attribute); + dynamic dynAttribute = new DynamicEntity(attribute); var saTranslations = ctx.TranslationsPerPage[nameof(SpecificationAttribute)]; var saoTranslations = ctx.TranslationsPerPage[nameof(SpecificationAttributeOption)]; dynAttribute.Name = saTranslations.GetValue(ctx.LanguageId, attribute.Id, nameof(attribute.Name)) ?? attribute.Name; - dynAttribute._Localized = GetLocalized(ctx, saTranslations, null, attribute, x => x.Name); + dynAttribute._Localized = GetLocalized(ctx, saTranslations, null, attribute, x => x.Name); - dynAttribute.Alias = saTranslations.GetValue(ctx.LanguageId, attribute.Id, nameof(attribute.Alias)) ?? attribute.Alias; - dynAttribute._Localized = GetLocalized(ctx, saTranslations, null, attribute, x => x.Alias); + dynAttribute.Alias = saTranslations.GetValue(ctx.LanguageId, attribute.Id, nameof(attribute.Alias)) ?? attribute.Alias; + dynAttribute._Localized = GetLocalized(ctx, saTranslations, null, attribute, x => x.Alias); dynamic dynOption = new DynamicEntity(option); dynOption.Name = saoTranslations.GetValue(ctx.LanguageId, option.Id, nameof(option.Name)) ?? option.Name; - dynOption._Localized = GetLocalized(ctx, saoTranslations, null, option, x => x.Name); + dynOption._Localized = GetLocalized(ctx, saoTranslations, null, option, x => x.Name); - dynOption.Alias = saoTranslations.GetValue(ctx.LanguageId, option.Id, nameof(option.Alias)) ?? option.Alias; - dynOption._Localized = GetLocalized(ctx, saoTranslations, null, option, x => x.Alias); + dynOption.Alias = saoTranslations.GetValue(ctx.LanguageId, option.Id, nameof(option.Alias)) ?? option.Alias; + dynOption._Localized = GetLocalized(ctx, saoTranslations, null, option, x => x.Alias); - dynOption.SpecificationAttribute = dynAttribute; - result.SpecificationAttributeOption = dynOption; + dynOption.SpecificationAttribute = dynAttribute; + result.SpecificationAttributeOption = dynOption; - return result; - } + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, GenericAttribute genericAttribute) - { + private dynamic ToDynamic(DataExporterContext ctx, GenericAttribute genericAttribute) + { if (genericAttribute == null) { return null; } - dynamic result = new DynamicEntity(genericAttribute); - return result; - } + dynamic result = new DynamicEntity(genericAttribute); + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, NewsLetterSubscription subscription) - { + private dynamic ToDynamic(DataExporterContext ctx, NewsLetterSubscription subscription) + { if (subscription == null) { return null; } - dynamic result = new DynamicEntity(subscription); + dynamic result = new DynamicEntity(subscription); - result.Store = ctx.Stores.ContainsKey(subscription.StoreId) - ? ToDynamic(ctx, ctx.Stores[subscription.StoreId]) - : null; + result.Store = ctx.Stores.ContainsKey(subscription.StoreId) + ? ToDynamic(ctx, ctx.Stores[subscription.StoreId]) + : null; - return result; - } + return result; + } - private dynamic ToDynamic(DataExporterContext ctx, ShoppingCartItem shoppingCartItem) - { + private dynamic ToDynamic(DataExporterContext ctx, ShoppingCartItem shoppingCartItem) + { if (shoppingCartItem == null) { return null; } - dynamic result = new DynamicEntity(shoppingCartItem); + dynamic result = new DynamicEntity(shoppingCartItem); - shoppingCartItem.Product.MergeWithCombination(shoppingCartItem.AttributesXml, _productAttributeParser.Value); + shoppingCartItem.Product.MergeWithCombination(shoppingCartItem.AttributesXml, _productAttributeParser.Value); - result.Store = ctx.Stores.ContainsKey(shoppingCartItem.StoreId) - ? ToDynamic(ctx, ctx.Stores[shoppingCartItem.StoreId]) - : null; + result.Store = ctx.Stores.ContainsKey(shoppingCartItem.StoreId) + ? ToDynamic(ctx, ctx.Stores[shoppingCartItem.StoreId]) + : null; - result.Customer = ToDynamic(ctx, shoppingCartItem.Customer); - result.Product = ToDynamic(ctx, shoppingCartItem.Product); + result.Customer = ToDynamic(ctx, shoppingCartItem.Customer); + result.Product = ToDynamic(ctx, shoppingCartItem.Product); - return result; - } + return result; + } - private List Convert(DataExporterContext ctx, Product product) - { + private List Convert(DataExporterContext ctx, Product product) + { var result = new List(); var productContext = new DynamicProductContext(); @@ -1273,279 +1301,281 @@ private List Convert(DataExporterContext ctx, Product product) ctx.ContextLanguage); if (ctx.Projection.AttributeCombinationAsProduct && productContext.Combinations.Where(x => x.IsActive).Any()) - { - if (ctx.Supports(ExportFeatures.UsesAttributeCombinationParent)) - { - var dynObject = ToDynamic(ctx, product, true, productContext); - result.Add(dynObject); - } - - var dbContext = _dbContext as DbContext; - - foreach (var combination in productContext.Combinations.Where(x => x.IsActive)) - { - product = _dbContext.Attach(product); - var entry = dbContext.Entry(product); - - // The returned object is not the entity and is not being tracked by the context. - // It also does not have any relationships set to other objects. - // CurrentValues only includes database (thus primitive) values. - var productClone = entry.CurrentValues.ToObject() as Product; - _dbContext.DetachEntity(product); + { + if (ctx.Supports(ExportFeatures.UsesAttributeCombinationParent)) + { + var dynObject = ToDynamic(ctx, product, true, productContext); + result.Add(dynObject); + } + + var dbContext = _dbContext as DbContext; + + foreach (var combination in productContext.Combinations.Where(x => x.IsActive)) + { + product = _dbContext.Attach(product); + var entry = dbContext.Entry(product); + + // The returned object is not the entity and is not being tracked by the context. + // It also does not have any relationships set to other objects. + // CurrentValues only includes database (thus primitive) values. + var productClone = entry.CurrentValues.ToObject() as Product; + _dbContext.DetachEntity(product); productContext.Combination = combination; - var dynObject = ToDynamic(ctx, productClone, false, productContext); - result.Add(dynObject); - } - } - else - { - var dynObject = ToDynamic(ctx, product, false, productContext); - result.Add(dynObject); - } - - if (result.Any()) - { - _services.EventPublisher.Publish(new RowExportingEvent - { - Row = result.First(), - EntityType = ExportEntityType.Product, - ExportRequest = ctx.Request, - ExecuteContext = ctx.ExecuteContext - }); - } - - return result; - } - - private List Convert(DataExporterContext ctx, Order order) - { - var result = new List(); - - ctx.OrderExportContext.Addresses.Collect(order.ShippingAddressId.HasValue ? order.ShippingAddressId.Value : 0); - ctx.OrderExportContext.Addresses.GetOrLoad(order.BillingAddressId); - - var customers = ctx.OrderExportContext.Customers.GetOrLoad(order.CustomerId); - var genericAttributes = ctx.OrderExportContext.CustomerGenericAttributes.GetOrLoad(order.CustomerId); - var rewardPointsHistories = ctx.OrderExportContext.RewardPointsHistories.GetOrLoad(order.CustomerId); - var orderItems = ctx.OrderExportContext.OrderItems.GetOrLoad(order.Id); - var shipments = ctx.OrderExportContext.Shipments.GetOrLoad(order.Id); - - dynamic dynObject = ToDynamic(ctx, order); - - dynObject.Customer = ToDynamic(ctx, customers.FirstOrDefault(x => x.Id == order.CustomerId)); - - // We do not export all customer generic attributes because otherwise the export file gets too large. - dynObject.Customer._GenericAttributes = genericAttributes - .Where(x => x.Value.HasValue() && _orderCustomerAttributes.Contains(x.Key)) - .Select(x => ToDynamic(ctx, x)) - .ToList(); - - dynObject.Customer.RewardPointsHistory = rewardPointsHistories - .Select(x => ToDynamic(ctx, x)) - .ToList(); - - if (rewardPointsHistories.Count > 0) - { - dynObject.Customer._RewardPointsBalance = rewardPointsHistories - .OrderByDescending(x => x.CreatedOnUtc) - .ThenByDescending(x => x.Id) - .FirstOrDefault() - .PointsBalance; - } - - if (ctx.OrderExportContext.Addresses.ContainsKey(order.BillingAddressId)) - { - dynObject.BillingAddress = ToDynamic(ctx, ctx.OrderExportContext.Addresses[order.BillingAddressId].FirstOrDefault()); - } - - if (order.ShippingAddressId.HasValue && ctx.OrderExportContext.Addresses.ContainsKey(order.ShippingAddressId.Value)) - { - dynObject.ShippingAddress = ToDynamic(ctx, ctx.OrderExportContext.Addresses[order.ShippingAddressId.Value].FirstOrDefault()); - } - - dynObject.OrderItems = orderItems - .Select(x => ToDynamic(ctx, x)) - .ToList(); - - dynObject.Shipments = shipments - .Select(x => ToDynamic(ctx, x)) - .ToList(); - - result.Add(dynObject); - - _services.EventPublisher.Publish(new RowExportingEvent - { - Row = dynObject, - EntityType = ExportEntityType.Order, - ExportRequest = ctx.Request, - ExecuteContext = ctx.ExecuteContext - }); - - return result; - } - - private List Convert(DataExporterContext ctx, Manufacturer manufacturer) - { - var result = new List(); - - var productManufacturers = ctx.ManufacturerExportContext.ProductManufacturers.GetOrLoad(manufacturer.Id); - - dynamic dynObject = ToDynamic(ctx, manufacturer); - - if (manufacturer.PictureId.HasValue) - { - var numberOfPictures = (ctx.Projection.NumberOfPictures ?? int.MaxValue); - var pictures = ctx.ManufacturerExportContext.Pictures.GetOrLoad(manufacturer.PictureId.Value).Take(numberOfPictures); - - if (pictures.Any()) - dynObject.Picture = ToDynamic(ctx, pictures.First(), _mediaSettings.Value.ManufacturerThumbPictureSize, _mediaSettings.Value.ManufacturerThumbPictureSize); - } - - dynObject.ProductManufacturers = productManufacturers - .OrderBy(x => x.DisplayOrder) - .Select(x => - { - dynamic dyn = new DynamicEntity(x); - - return dyn; - }) - .ToList(); - - result.Add(dynObject); - - _services.EventPublisher.Publish(new RowExportingEvent - { - Row = dynObject, - EntityType = ExportEntityType.Manufacturer, - ExportRequest = ctx.Request, - ExecuteContext = ctx.ExecuteContext - }); - - return result; - } - - private List Convert(DataExporterContext ctx, Category category) - { - var result = new List(); - - var productCategories = ctx.CategoryExportContext.ProductCategories.GetOrLoad(category.Id); - - dynamic dynObject = ToDynamic(ctx, category); - - if (category.PictureId.HasValue) - { - var numberOfPictures = (ctx.Projection.NumberOfPictures ?? int.MaxValue); - var pictures = ctx.CategoryExportContext.Pictures.GetOrLoad(category.PictureId.Value).Take(numberOfPictures); - - if (pictures.Any()) - dynObject.Picture = ToDynamic(ctx, pictures.First(), _mediaSettings.Value.CategoryThumbPictureSize, _mediaSettings.Value.CategoryThumbPictureSize); - } - - dynObject.ProductCategories = productCategories - .OrderBy(x => x.DisplayOrder) - .Select(x => - { - dynamic dyn = new DynamicEntity(x); - - return dyn; - }) - .ToList(); - - result.Add(dynObject); - - _services.EventPublisher.Publish(new RowExportingEvent - { - Row = dynObject, - EntityType = ExportEntityType.Category, - ExportRequest = ctx.Request, - ExecuteContext = ctx.ExecuteContext - }); - - return result; - } - - private List Convert(DataExporterContext ctx, Customer customer) - { - var result = new List(); - - var genericAttributes = ctx.CustomerExportContext.GenericAttributes.GetOrLoad(customer.Id); - - dynamic dynObject = ToDynamic(ctx, customer); - - dynObject.BillingAddress = ToDynamic(ctx, customer.BillingAddress); - dynObject.ShippingAddress = ToDynamic(ctx, customer.ShippingAddress); - - dynObject.Addresses = customer.Addresses - .Select(x => ToDynamic(ctx, x)) - .ToList(); - - dynObject._GenericAttributes = genericAttributes - .Select(x => ToDynamic(ctx, x)) - .ToList(); - - dynObject._HasNewsletterSubscription = ctx.NewsletterSubscriptions.Contains(customer.Email, StringComparer.CurrentCultureIgnoreCase); - dynObject._FullName = customer.GetFullName(); - - if (_customerSettings.Value.AllowCustomersToUploadAvatars) - { - var pictureId = genericAttributes.FirstOrDefault(x => x.Key == SystemCustomerAttributeNames.AvatarPictureId); - if (pictureId != null) - { - // reduce traffic and do not export default avatar - dynObject._AvatarPictureUrl = _pictureService.Value.GetUrl(pictureId.Value.ToInt(), _mediaSettings.Value.AvatarPictureSize, false, _services.StoreService.GetHost(ctx.Store)); - } - } - - result.Add(dynObject); - - _services.EventPublisher.Publish(new RowExportingEvent - { - Row = dynObject, - EntityType = ExportEntityType.Customer, - ExportRequest = ctx.Request, - ExecuteContext = ctx.ExecuteContext - }); - - return result; - } - - private List Convert(DataExporterContext ctx, NewsLetterSubscription subscription) - { - var result = new List(); - dynamic dynObject = ToDynamic(ctx, subscription); - result.Add(dynObject); - - _services.EventPublisher.Publish(new RowExportingEvent - { - Row = dynObject, - EntityType = ExportEntityType.NewsLetterSubscription, - ExportRequest = ctx.Request, - ExecuteContext = ctx.ExecuteContext - }); - - return result; - } - - private List Convert(DataExporterContext ctx, ShoppingCartItem shoppingCartItem) - { - var result = new List(); - dynamic dynObject = ToDynamic(ctx, shoppingCartItem); - - result.Add(dynObject); - - _services.EventPublisher.Publish(new RowExportingEvent - { - Row = dynObject, - EntityType = ExportEntityType.ShoppingCartItem, - ExportRequest = ctx.Request, - ExecuteContext = ctx.ExecuteContext - }); - - return result; - } - } + var dynObject = ToDynamic(ctx, productClone, false, productContext); + result.Add(dynObject); + } + } + else + { + var dynObject = ToDynamic(ctx, product, false, productContext); + result.Add(dynObject); + } + + if (result.Any()) + { + _services.EventPublisher.Publish(new RowExportingEvent + { + Row = result.First(), + EntityType = ExportEntityType.Product, + ExportRequest = ctx.Request, + ExecuteContext = ctx.ExecuteContext + }); + } + + return result; + } + + private List Convert(DataExporterContext ctx, Order order) + { + var result = new List(); + + ctx.OrderExportContext.Addresses.Collect(order.ShippingAddressId.HasValue ? order.ShippingAddressId.Value : 0); + ctx.OrderExportContext.Addresses.GetOrLoad(order.BillingAddressId); + + var customers = ctx.OrderExportContext.Customers.GetOrLoad(order.CustomerId); + var genericAttributes = ctx.OrderExportContext.CustomerGenericAttributes.GetOrLoad(order.CustomerId); + var rewardPointsHistories = ctx.OrderExportContext.RewardPointsHistories.GetOrLoad(order.CustomerId); + var orderItems = ctx.OrderExportContext.OrderItems.GetOrLoad(order.Id); + var shipments = ctx.OrderExportContext.Shipments.GetOrLoad(order.Id); + + dynamic dynObject = ToDynamic(ctx, order); + + dynObject.Customer = ToDynamic(ctx, customers.FirstOrDefault(x => x.Id == order.CustomerId)); + + // We do not export all customer generic attributes because otherwise the export file gets too large. + dynObject.Customer._GenericAttributes = genericAttributes + .Where(x => x.Value.HasValue() && _orderCustomerAttributes.Contains(x.Key)) + .Select(x => ToDynamic(ctx, x)) + .ToList(); + + dynObject.Customer.RewardPointsHistory = rewardPointsHistories + .Select(x => ToDynamic(ctx, x)) + .ToList(); + + if (rewardPointsHistories.Count > 0) + { + dynObject.Customer._RewardPointsBalance = rewardPointsHistories + .OrderByDescending(x => x.CreatedOnUtc) + .ThenByDescending(x => x.Id) + .FirstOrDefault() + .PointsBalance; + } + + if (ctx.OrderExportContext.Addresses.ContainsKey(order.BillingAddressId)) + { + dynObject.BillingAddress = ToDynamic(ctx, ctx.OrderExportContext.Addresses[order.BillingAddressId].FirstOrDefault()); + } + + if (order.ShippingAddressId.HasValue && ctx.OrderExportContext.Addresses.ContainsKey(order.ShippingAddressId.Value)) + { + dynObject.ShippingAddress = ToDynamic(ctx, ctx.OrderExportContext.Addresses[order.ShippingAddressId.Value].FirstOrDefault()); + } + + dynObject.OrderItems = orderItems + .Select(x => ToDynamic(ctx, x)) + .ToList(); + + dynObject.Shipments = shipments + .Select(x => ToDynamic(ctx, x)) + .ToList(); + + result.Add(dynObject); + + _services.EventPublisher.Publish(new RowExportingEvent + { + Row = dynObject, + EntityType = ExportEntityType.Order, + ExportRequest = ctx.Request, + ExecuteContext = ctx.ExecuteContext + }); + + return result; + } + + private List Convert(DataExporterContext ctx, Manufacturer manufacturer) + { + var result = new List(); + var productManufacturers = ctx.ManufacturerExportContext.ProductManufacturers.GetOrLoad(manufacturer.Id); + + dynamic dynObject = ToDynamic(ctx, manufacturer); + + if (manufacturer.MediaFileId.HasValue) + { + var numberOfFiles = ctx.Projection.NumberOfMediaFiles ?? int.MaxValue; + var files = ctx.ManufacturerExportContext.Files.GetOrLoad(manufacturer.MediaFileId.Value).Take(numberOfFiles); + + if (files.Any()) + { + dynObject.Picture = ToDynamic(ctx, files.First(), _mediaSettings.Value.ManufacturerThumbPictureSize, _mediaSettings.Value.ManufacturerThumbPictureSize); + } + } + + dynObject.ProductManufacturers = productManufacturers + .OrderBy(x => x.DisplayOrder) + .Select(x => + { + dynamic dyn = new DynamicEntity(x); + return dyn; + }) + .ToList(); + + result.Add(dynObject); + + _services.EventPublisher.Publish(new RowExportingEvent + { + Row = dynObject, + EntityType = ExportEntityType.Manufacturer, + ExportRequest = ctx.Request, + ExecuteContext = ctx.ExecuteContext + }); + + return result; + } + + private List Convert(DataExporterContext ctx, Category category) + { + var result = new List(); + var productCategories = ctx.CategoryExportContext.ProductCategories.GetOrLoad(category.Id); + + dynamic dynObject = ToDynamic(ctx, category); + + if (category.MediaFileId.HasValue) + { + var numberOfFiles = ctx.Projection.NumberOfMediaFiles ?? int.MaxValue; + var files = ctx.CategoryExportContext.Files.GetOrLoad(category.MediaFileId.Value).Take(numberOfFiles); + + if (files.Any()) + { + dynObject.Picture = ToDynamic(ctx, files.First(), _mediaSettings.Value.CategoryThumbPictureSize, _mediaSettings.Value.CategoryThumbPictureSize); + } + } + + dynObject.ProductCategories = productCategories + .OrderBy(x => x.DisplayOrder) + .Select(x => + { + dynamic dyn = new DynamicEntity(x); + return dyn; + }) + .ToList(); + + result.Add(dynObject); + + _services.EventPublisher.Publish(new RowExportingEvent + { + Row = dynObject, + EntityType = ExportEntityType.Category, + ExportRequest = ctx.Request, + ExecuteContext = ctx.ExecuteContext + }); + + return result; + } + + private List Convert(DataExporterContext ctx, Customer customer) + { + var result = new List(); + + var genericAttributes = ctx.CustomerExportContext.GenericAttributes.GetOrLoad(customer.Id); + + dynamic dynObject = ToDynamic(ctx, customer); + + dynObject.BillingAddress = ToDynamic(ctx, customer.BillingAddress); + dynObject.ShippingAddress = ToDynamic(ctx, customer.ShippingAddress); + + dynObject.Addresses = customer.Addresses + .Select(x => ToDynamic(ctx, x)) + .ToList(); + + dynObject._GenericAttributes = genericAttributes + .Select(x => ToDynamic(ctx, x)) + .ToList(); + + dynObject._HasNewsletterSubscription = ctx.NewsletterSubscriptions.Contains(customer.Email, StringComparer.CurrentCultureIgnoreCase); + dynObject._FullName = customer.GetFullName(); + dynObject._AvatarPictureUrl = null; + + if (_customerSettings.Value.AllowCustomersToUploadAvatars) + { + // Reduce traffic and do not export default avatar. + var fileId = genericAttributes.FirstOrDefault(x => x.Key == SystemCustomerAttributeNames.AvatarPictureId)?.Value?.ToInt() ?? 0; + var file = _mediaService.Value.GetFileById(fileId, MediaLoadFlags.AsNoTracking); + if (file != null) + { + dynObject._AvatarPictureUrl = _mediaService.Value.GetUrl(file, new ProcessImageQuery { MaxSize = _mediaSettings.Value.AvatarPictureSize }, _services.StoreService.GetHost(ctx.Store)); + } + } + + result.Add(dynObject); + + _services.EventPublisher.Publish(new RowExportingEvent + { + Row = dynObject, + EntityType = ExportEntityType.Customer, + ExportRequest = ctx.Request, + ExecuteContext = ctx.ExecuteContext + }); + + return result; + } + + private List Convert(DataExporterContext ctx, NewsLetterSubscription subscription) + { + var result = new List(); + dynamic dynObject = ToDynamic(ctx, subscription); + result.Add(dynObject); + + _services.EventPublisher.Publish(new RowExportingEvent + { + Row = dynObject, + EntityType = ExportEntityType.NewsLetterSubscription, + ExportRequest = ctx.Request, + ExecuteContext = ctx.ExecuteContext + }); + + return result; + } + + private List Convert(DataExporterContext ctx, ShoppingCartItem shoppingCartItem) + { + var result = new List(); + dynamic dynObject = ToDynamic(ctx, shoppingCartItem); + + result.Add(dynObject); + + _services.EventPublisher.Publish(new RowExportingEvent + { + Row = dynObject, + EntityType = ExportEntityType.ShoppingCartItem, + ExportRequest = ctx.Request, + ExecuteContext = ctx.ExecuteContext + }); + + return result; + } + } internal class DynamicProductContext diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Events/RowExportingEvent.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Events/RowExportingEvent.cs index 0b96526e00..38db26a02b 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Events/RowExportingEvent.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Events/RowExportingEvent.cs @@ -2,34 +2,34 @@ namespace SmartStore.Services.DataExchange.Export.Events { - // TODO: Another event message must be implemented, say 'ColumnsBuildingEvent' - // The consumer of this event (most likely a plugin) could push a list of specific column headers - // into the global export definition. + // TODO: Another event message must be implemented, say 'ColumnsBuildingEvent' + // The consumer of this event (most likely a plugin) could push a list of specific column headers + // into the global export definition. - public class RowExportingEvent - { - public dynamic Row - { - get; - internal set; - } + public class RowExportingEvent + { + public dynamic Row + { + get; + internal set; + } - public ExportEntityType EntityType - { - get; - internal set; - } + public ExportEntityType EntityType + { + get; + internal set; + } - public DataExportRequest ExportRequest - { - get; - internal set; - } + public DataExportRequest ExportRequest + { + get; + internal set; + } - public ExportExecuteContext ExecuteContext - { - get; - internal set; - } - } + public ExportExecuteContext ExecuteContext + { + get; + internal set; + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportConfigurationInfo.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportConfigurationInfo.cs index 2cfb17796d..f9406c82ff 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportConfigurationInfo.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportConfigurationInfo.cs @@ -2,24 +2,24 @@ namespace SmartStore.Services.DataExchange.Export { - /// - /// Serves information about export provider specific configuration - /// - public class ExportConfigurationInfo - { - /// - /// The partial view name for the configuration - /// - public string PartialViewName { get; set; } + /// + /// Serves information about export provider specific configuration + /// + public class ExportConfigurationInfo + { + /// + /// The partial view name for the configuration + /// + public string PartialViewName { get; set; } - /// - /// Type of the view model - /// - public Type ModelType { get; set; } + /// + /// Type of the view model + /// + public Type ModelType { get; set; } - /// - /// Callback to initialize the view model. Can be null. - /// - public Action Initialize { get; set; } - } + /// + /// Callback to initialize the view model. Can be null. + /// + public Action Initialize { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportDataSegmenter.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportDataSegmenter.cs index cb44a39618..6fe190a54e 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportDataSegmenter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportDataSegmenter.cs @@ -4,80 +4,80 @@ namespace SmartStore.Services.DataExchange.Export { - public interface IExportDataSegmenterConsumer - { - /// - /// Total number of records - /// - int TotalRecords { get; } - - /// - /// Gets current data segment - /// - IReadOnlyCollection CurrentSegment { get; } - - /// - /// Reads the next segment - /// - /// - bool ReadNextSegment(); - } - - internal interface IExportDataSegmenterProvider : IExportDataSegmenterConsumer, IDisposable - { - /// - /// Whether there is data available - /// - bool HasData { get; } - - /// - /// Record per segment counter - /// - int RecordPerSegmentCount { get; set; } - } - - public class ExportDataSegmenter : IExportDataSegmenterProvider where T : BaseEntity - { - private readonly Func> _load; - private readonly Action> _loaded; - private readonly Func> _convert; - - private readonly int _offset; - private readonly int _take; - private readonly int _limit; - private readonly int _recordsPerSegment; - private readonly int _totalRecords; + public interface IExportDataSegmenterConsumer + { + /// + /// Total number of records + /// + int TotalRecords { get; } + + /// + /// Gets current data segment + /// + IReadOnlyCollection CurrentSegment { get; } + + /// + /// Reads the next segment + /// + /// + bool ReadNextSegment(); + } + + internal interface IExportDataSegmenterProvider : IExportDataSegmenterConsumer, IDisposable + { + /// + /// Whether there is data available + /// + bool HasData { get; } + + /// + /// Record per segment counter + /// + int RecordPerSegmentCount { get; set; } + } + + public class ExportDataSegmenter : IExportDataSegmenterProvider where T : BaseEntity + { + private readonly Func> _load; + private readonly Action> _loaded; + private readonly Func> _convert; + + private readonly int _offset; + private readonly int _take; + private readonly int _limit; + private readonly int _recordsPerSegment; + private readonly int _totalRecords; private Queue _data; private bool _endOfData; - public ExportDataSegmenter( - Func> load, - Action> loaded, - Func> convert, - int offset, - int take, - int limit, - int recordsPerSegment, - int totalRecords) - { - _load = load; - _loaded = loaded; - _convert = convert; - _offset = offset; - _take = take; - _limit = limit; - _recordsPerSegment = recordsPerSegment; - _totalRecords = totalRecords; - } - - /// - /// Total number of records. - /// - public int TotalRecords - { - get - { + public ExportDataSegmenter( + Func> load, + Action> loaded, + Func> convert, + int offset, + int take, + int limit, + int recordsPerSegment, + int totalRecords) + { + _load = load; + _loaded = loaded; + _convert = convert; + _offset = offset; + _take = take; + _limit = limit; + _recordsPerSegment = recordsPerSegment; + _totalRecords = totalRecords; + } + + /// + /// Total number of records. + /// + public int TotalRecords + { + get + { var total = Math.Max(_totalRecords - _offset, 0); if (_limit > 0 && _limit < total) @@ -85,9 +85,9 @@ public int TotalRecords return _limit; } - return total; - } - } + return total; + } + } /// /// Number of processed records. @@ -99,13 +99,13 @@ public int TotalRecords /// public int RecordPerSegmentCount { get; set; } - /// - /// Whether there is data available. - /// - public bool HasData - { - get - { + /// + /// Whether there is data available. + /// + public bool HasData + { + get + { if (_limit > 0 && RecordCount >= _limit) { return false; @@ -121,23 +121,23 @@ public bool HasData return false; } - return true; - } - } + return RecordCount < TotalRecords; + } + } - /// - /// Gets current data segment. - /// - public IReadOnlyCollection CurrentSegment - { - get - { - T entity; - var records = new List(); + /// + /// Gets current data segment. + /// + public IReadOnlyCollection CurrentSegment + { + get + { + T entity; + var records = new List(); - while (_data.Count > 0 && (entity = _data.Dequeue()) != null) - { - _convert(entity).Each(x => records.Add(x)); + while (_data.Count > 0 && (entity = _data.Dequeue()) != null) + { + _convert(entity).Each(x => records.Add(x)); if (++RecordCount >= _limit && _limit > 0) { @@ -148,18 +148,18 @@ public IReadOnlyCollection CurrentSegment { return records; } - } + } - return records; - } - } + return records; + } + } /// /// Read next segment. /// /// true next segment available. false no more data. public bool ReadNextSegment() - { + { if (_limit > 0 && RecordCount >= _limit) { return false; @@ -200,22 +200,22 @@ public bool ReadNextSegment() _data = new Queue(newData); } - // Give provider the opportunity to make something with entity ids. - _loaded?.Invoke(_data.AsReadOnly()); + // 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. + /// + public void Dispose() + { + RecordCount = 0; + RecordPerSegmentCount = 0; - /// - /// Dispose and reset segmenter instance. - /// - public void Dispose() - { - RecordCount = 0; - RecordPerSegmentCount = 0; - _endOfData = false; _data?.Clear(); } - } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportExecuteContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportExecuteContext.cs index f28ec5b11f..7b18db60da 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportExecuteContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportExecuteContext.cs @@ -10,18 +10,18 @@ namespace SmartStore.Services.DataExchange.Export { public class ExportExecuteContext - { - private DataExportResult _result; - private CancellationToken _cancellation; - private DataExchangeAbortion _providerAbort; - - internal ExportExecuteContext(DataExportResult result, CancellationToken cancellation, string folder) - { - _result = result; - _cancellation = cancellation; - Folder = folder; - ExtraDataUnits = new List(); - CustomProperties = new Dictionary(); + { + private DataExportResult _result; + private CancellationToken _cancellation; + private DataExchangeAbortion _providerAbort; + + internal ExportExecuteContext(DataExportResult result, CancellationToken cancellation, string folder) + { + _result = result; + _cancellation = cancellation; + Folder = folder; + ExtraDataUnits = new List(); + CustomProperties = new Dictionary(); } /// @@ -29,93 +29,87 @@ internal ExportExecuteContext(DataExportResult result, CancellationToken cancell /// public int ProfileId { get; internal set; } - /// - /// The export profile - /// - public dynamic Profile { get; internal set; } - - /// - /// Provides the data to be exported - /// - public IExportDataSegmenterConsumer DataSegmenter { get; set; } - - /// - /// The store context to be used for the export - /// - public dynamic Store { get; internal set; } - - /// - /// The customer context to be used for the export - /// - public dynamic Customer { get; internal set; } - - /// - /// The currency context to be used for the export - /// - public dynamic Currency { get; internal set; } - - /// - /// The language context to be used for the export - /// - public dynamic Language { get; internal set; } - - /// - /// Filter settings - /// - public ExportFilter Filter { get; internal set; } - - /// - /// Projection settings - /// - public ExportProjection Projection { get; internal set; } - - /// - /// To log information into the export log file - /// - public ILogger Log { get; internal set; } - - /// - /// Indicates whether and how to abort the export - /// - public DataExchangeAbortion Abort - { - get - { - if (_cancellation.IsCancellationRequested || IsMaxFailures) - return DataExchangeAbortion.Hard; - - return _providerAbort; - } - set - { - _providerAbort = value; - } - } - - public bool IsMaxFailures - { - get { return RecordsFailed > 11; } - } - - /// - /// Identifier of current data stream. Can be null. - /// - public string DataStreamId { get; set; } - - /// - /// Stream used to write data to - /// - public Stream DataStream { get; internal set; } - - /// - /// List with extra data units/streams required by provider - /// - public List ExtraDataUnits { get; private set; } - - /// - /// The maximum allowed file name length - /// - public int MaxFileNameLength { get; internal set; } + /// + /// The export profile + /// + public dynamic Profile { get; internal set; } + + /// + /// Provides the data to be exported + /// + public IExportDataSegmenterConsumer DataSegmenter { get; set; } + + /// + /// The store context to be used for the export + /// + public dynamic Store { get; internal set; } + + /// + /// The customer context to be used for the export + /// + public dynamic Customer { get; internal set; } + + /// + /// The currency context to be used for the export + /// + public dynamic Currency { get; internal set; } + + /// + /// The language context to be used for the export + /// + public dynamic Language { get; internal set; } + + /// + /// Filter settings + /// + public ExportFilter Filter { get; internal set; } + + /// + /// Projection settings + /// + public ExportProjection Projection { get; internal set; } + + /// + /// To log information into the export log file + /// + public ILogger Log { get; internal set; } + + /// + /// Indicates whether and how to abort the export + /// + public DataExchangeAbortion Abort + { + get + { + if (_cancellation.IsCancellationRequested || IsMaxFailures) + return DataExchangeAbortion.Hard; + + return _providerAbort; + } + set => _providerAbort = value; + } + + public bool IsMaxFailures => RecordsFailed > 11; + + /// + /// Identifier of current data stream. Can be null. + /// + public string DataStreamId { get; set; } + + /// + /// Stream used to write data to + /// + public Stream DataStream { get; internal set; } + + /// + /// List with extra data units/streams required by provider + /// + public List ExtraDataUnits { get; private set; } + + /// + /// The maximum allowed file name length + /// + public int MaxFileNameLength { get; internal set; } /// /// Index of current export file. @@ -127,60 +121,60 @@ public bool IsMaxFailures /// public string FileName { get; internal set; } - /// - /// The path of the export content folder - /// - public string Folder { get; private set; } - - /// - /// Whether the profile has a public deployment into "Exchange" folder - /// - public bool HasPublicDeployment { get; internal set; } - - /// - /// The local path to the public export folder "Exchange". null if the profile has no public deployment. - /// - public string PublicFolderPath { get; internal set; } - - /// - /// The URL of the public export folder "Exchange". null if the profile has no public deployment. - /// - public string PublicFolderUrl { get; internal set; } - - /// - /// Provider specific configuration data - /// - public object ConfigurationData { get; internal set; } - - /// - /// Use this dictionary for any custom data required along the export - /// - public Dictionary CustomProperties { get; set; } - - /// - /// Number of successful processed records - /// - public int RecordsSucceeded { get; set; } - - /// - /// Number of failed records - /// - public int RecordsFailed { get; set; } - - /// - /// Processes an exception that occurred while exporting a record - /// + /// + /// The path of the export content folder + /// + public string Folder { get; private set; } + + /// + /// Whether the profile has a public deployment into "Exchange" folder + /// + public bool HasPublicDeployment { get; internal set; } + + /// + /// The local path to the public export folder "Exchange". null if the profile has no public deployment. + /// + public string PublicFolderPath { get; internal set; } + + /// + /// The URL of the public export folder "Exchange". null if the profile has no public deployment. + /// + public string PublicFolderUrl { get; internal set; } + + /// + /// Provider specific configuration data + /// + public object ConfigurationData { get; internal set; } + + /// + /// Use this dictionary for any custom data required along the export + /// + public Dictionary CustomProperties { get; set; } + + /// + /// Number of successful processed records + /// + public int RecordsSucceeded { get; set; } + + /// + /// Number of failed records + /// + public int RecordsFailed { get; set; } + + /// + /// Processes an exception that occurred while exporting a record + /// /// Identifier of the current entity - /// Exception - public void RecordException(Exception exception, int entityId) - { - ++RecordsFailed; + /// Exception + public void RecordException(Exception exception, int entityId) + { + ++RecordsFailed; - Log.ErrorFormat("Error while processing record with id {0}. {1}".FormatInvariant(entityId, exception.ToString())); + Log.ErrorFormat("Error while processing record with id {0}. {1}".FormatInvariant(entityId, exception.ToString())); - if (IsMaxFailures) - _result.LastError = exception.ToString(); - } + if (IsMaxFailures) + _result.LastError = exception.ToString(); + } /// /// Processes an out-of-memory exception and hard aborts the export @@ -202,34 +196,34 @@ public void RecordOutOfMemoryException(OutOfMemoryException exception, int entit public ProgressValueSetter ProgressValueSetter { get; internal set; } - /// - /// Allows to set a progress message - /// - /// Output message - public void SetProgress(string message) - { - if (ProgressValueSetter != null && message.HasValue()) - { - try - { - ProgressValueSetter.Invoke(0, 0, message); - } - catch { } - } - } - } - - public class ExportDataUnit - { + /// + /// Allows to set a progress message + /// + /// Output message + public void SetProgress(string message) + { + if (ProgressValueSetter != null && message.HasValue()) + { + try + { + ProgressValueSetter.Invoke(0, 0, message); + } + catch { } + } + } + } + + public class ExportDataUnit + { /// /// Your Id to identify this stream within a list of streams /// public string Id { get; set; } - /// - /// Stream used to write data to - /// - public Stream DataStream { get; internal set; } + /// + /// Stream used to write data to + /// + public Stream DataStream { get; internal set; } /// /// The related entity type. @@ -241,15 +235,15 @@ public class ExportDataUnit /// public string FileName { get; set; } - /// - /// Short optional text that describes the content of the file - /// - public string Label { get; set; } + /// + /// Short optional text that describes the content of the file + /// + public string Label { get; set; } - /// - /// Whether to display the file in the profile file dialog - /// - public bool DisplayInFileDialog { get; set; } + /// + /// Whether to display the file in the profile file dialog + /// + public bool DisplayInFileDialog { get; set; } /// /// Number of successful processed records. diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportExtensions.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportExtensions.cs index 4c9fd6fdb1..6fc708c721 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportExtensions.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportExtensions.cs @@ -16,39 +16,39 @@ namespace SmartStore.Services.DataExchange.Export { public static class ExportExtensions - { - /// - /// Returns a value indicating whether the export provider is valid - /// - /// Export provider - /// true provider is valid, false provider is invalid. - public static bool IsValid(this Provider provider) - { - return provider != null && provider.Value != null; - } - - /// - /// Gets the localized friendly name or the system name as fallback - /// - /// Export provider - /// Localization service - /// Provider name - public static string GetName(this Provider provider, ILocalizationService localizationService) - { - var systemName = provider.Metadata.SystemName; - var resourceName = provider.Metadata.ResourceKeyPattern.FormatInvariant(systemName, "FriendlyName"); - var name = localizationService.GetResource(resourceName, 0, false, systemName, true); - - return (name.IsEmpty() ? systemName : name); - } - - /// - /// Get temporary folder for an export profile - /// - /// Export profile - /// Folder path - public static string GetExportFolder(this ExportProfile profile, bool content = false, bool create = false) - { + { + /// + /// Returns a value indicating whether the export provider is valid + /// + /// Export provider + /// true provider is valid, false provider is invalid. + public static bool IsValid(this Provider provider) + { + return provider != null && provider.Value != null; + } + + /// + /// Gets the localized friendly name or the system name as fallback + /// + /// Export provider + /// Localization service + /// Provider name + public static string GetName(this Provider provider, ILocalizationService localizationService) + { + var systemName = provider.Metadata.SystemName; + var resourceName = provider.Metadata.ResourceKeyPattern.FormatInvariant(systemName, "FriendlyName"); + var name = localizationService.GetResource(resourceName, 0, false, systemName, true); + + return (name.IsEmpty() ? systemName : name); + } + + /// + /// Get temporary folder for an export profile + /// + /// Export profile + /// Folder path + public static string GetExportFolder(this ExportProfile profile, bool content = false, bool create = false) + { Guard.IsTrue(profile.FolderName.EmptyNull().Length > 2, nameof(profile.FolderName), "The export folder name must be at least 3 characters long."); var path = CommonHelper.MapPath(string.Concat(profile.FolderName, content ? "/Content" : "")); @@ -67,76 +67,76 @@ public static string GetExportFolder(this ExportProfile profile, bool content = /// Export profile /// Log file path public static string GetExportLogPath(this ExportProfile profile) - { - return Path.Combine(profile.GetExportFolder(), "log.txt"); - } - - /// - /// Gets the ZIP path for a profile - /// - /// Export profile - /// ZIP file path - public static string GetExportZipPath(this ExportProfile profile) - { - var name = (new DirectoryInfo(profile.FolderName)).Name; - if (name.IsEmpty()) - name = "ExportData"; - - return Path.Combine(profile.GetExportFolder(), name.ToValidFileName() + ".zip"); - } - - /// - /// Gets existing export files for an export profile - /// - /// Export profile - /// Export provider - /// List of file names - public static IEnumerable GetExportFiles(this ExportProfile profile, Provider provider) - { - var exportFolder = profile.GetExportFolder(true); - - if (System.IO.Directory.Exists(exportFolder) && provider.Value.FileExtension.HasValue()) - { - var filter = "*.{0}".FormatInvariant(provider.Value.FileExtension.ToLower()); - - return System.IO.Directory.EnumerateFiles(exportFolder, filter, SearchOption.AllDirectories).OrderBy(x => x); - } - - return Enumerable.Empty(); - } - - /// - /// Get number of existing export files - /// - /// Export profile - /// Export provider - /// Number of export files - public static int GetExportFileCount(this ExportProfile profile, Provider provider) - { - var result = profile.GetExportFiles(provider).Count(); - - if (File.Exists(profile.GetExportZipPath())) - ++result; - - return result; - } - - /// - /// Resolves the file name pattern for an export profile - /// - /// Export profile - /// Store - /// One based file index - /// The maximum length of the file name - /// Resolved file name pattern - public static string ResolveFileNamePattern(this ExportProfile profile, Store store, int fileIndex, int maxFileNameLength) - { - var sb = new StringBuilder(profile.FileNamePattern); - - sb.Replace("%Profile.Id%", profile.Id.ToString()); - sb.Replace("%Profile.FolderName%", profile.FolderName); - sb.Replace("%Store.Id%", store.Id.ToString()); - sb.Replace("%File.Index%", fileIndex.ToString("D4")); + { + return Path.Combine(profile.GetExportFolder(), "log.txt"); + } + + /// + /// Gets the ZIP path for a profile + /// + /// Export profile + /// ZIP file path + public static string GetExportZipPath(this ExportProfile profile) + { + var name = (new DirectoryInfo(profile.FolderName)).Name; + if (name.IsEmpty()) + name = "ExportData"; + + return Path.Combine(profile.GetExportFolder(), name.ToValidFileName() + ".zip"); + } + + /// + /// Gets existing export files for an export profile + /// + /// Export profile + /// Export provider + /// List of file names + public static IEnumerable GetExportFiles(this ExportProfile profile, Provider provider) + { + var exportFolder = profile.GetExportFolder(true); + + if (System.IO.Directory.Exists(exportFolder) && provider.Value.FileExtension.HasValue()) + { + var filter = "*.{0}".FormatInvariant(provider.Value.FileExtension.ToLower()); + + return System.IO.Directory.EnumerateFiles(exportFolder, filter, SearchOption.AllDirectories).OrderBy(x => x); + } + + return Enumerable.Empty(); + } + + /// + /// Get number of existing export files + /// + /// Export profile + /// Export provider + /// Number of export files + public static int GetExportFileCount(this ExportProfile profile, Provider provider) + { + var result = profile.GetExportFiles(provider).Count(); + + if (File.Exists(profile.GetExportZipPath())) + ++result; + + return result; + } + + /// + /// Resolves the file name pattern for an export profile + /// + /// Export profile + /// Store + /// One based file index + /// The maximum length of the file name + /// Resolved file name pattern + public static string ResolveFileNamePattern(this ExportProfile profile, Store store, int fileIndex, int maxFileNameLength) + { + var sb = new StringBuilder(profile.FileNamePattern); + + sb.Replace("%Profile.Id%", profile.Id.ToString()); + sb.Replace("%Profile.FolderName%", profile.FolderName); + sb.Replace("%Store.Id%", store.Id.ToString()); + sb.Replace("%File.Index%", fileIndex.ToString("D4")); if (profile.FileNamePattern.Contains("%Profile.SeoName%")) { @@ -155,113 +155,113 @@ public static string ResolveFileNamePattern(this ExportProfile profile, Store st sb.Replace("%Timestamp%", DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture)); } - var result = sb.ToString() - .ToValidFileName("") - .Truncate(maxFileNameLength); - - return result; - } - - /// - /// Get path of the deployment folder - /// - /// Export deployment - /// Deployment folder path - public static string GetDeploymentFolder(this ExportDeployment deployment, bool create = false) - { - if (deployment == null) - return null; - - string path = null; - - if (deployment.DeploymentType == ExportDeploymentType.PublicFolder) - { - if (deployment.SubFolder.HasValue()) - path = Path.Combine(HttpRuntime.AppDomainAppPath, DataExporter.PublicFolder, deployment.SubFolder); - else - path = Path.Combine(HttpRuntime.AppDomainAppPath, DataExporter.PublicFolder); - } - else if (deployment.DeploymentType == ExportDeploymentType.FileSystem) - { - if (deployment.FileSystemPath.IsEmpty()) - return null; - - if (Path.IsPathRooted(deployment.FileSystemPath)) - { - path = deployment.FileSystemPath; - } - else - { - path = PathHelper.NormalizeAppRelativePath(deployment.FileSystemPath); - path = CommonHelper.MapPath(path); - } - } - - if (create && !System.IO.Directory.Exists(path)) - System.IO.Directory.CreateDirectory(path); - - return path; - } - - /// - /// Get url of the public folder and take filtering and projection into consideration - /// - /// Export deployment - /// Common services - /// Store entity - /// Absolute URL of the public folder (always ends with /) or null - public static string GetPublicFolderUrl(this ExportDeployment deployment, ICommonServices services, Store store = null) - { - if (deployment != null && deployment.DeploymentType == ExportDeploymentType.PublicFolder) - { - if (store == null) - { - var filter = XmlHelper.Deserialize(deployment.Profile.Filtering); - var storeId = filter.StoreId; - - if (storeId == 0) - { - var projection = XmlHelper.Deserialize(deployment.Profile.Projection); - storeId = (projection.StoreId ?? 0); - } - - store = (storeId == 0 ? services.StoreContext.CurrentStore : services.StoreService.GetStoreById(storeId)); - } - - var url = string.Concat( - store.Url.EnsureEndsWith("/"), - DataExporter.PublicFolder.EnsureEndsWith("/"), - deployment.SubFolder.HasValue() ? deployment.SubFolder.EnsureEndsWith("/") : "" - ); - - return url; - } - - return null; - } - - /// - /// Get icon class for a deployment type - /// - /// Deployment type - /// Icon class - public static string GetIconClass(this ExportDeploymentType type) - { - switch (type) - { - case ExportDeploymentType.FileSystem: - return "far fa-folder-open"; - case ExportDeploymentType.Email: - return "far fa-envelope"; - case ExportDeploymentType.Http: - return "fa fa-globe"; - case ExportDeploymentType.Ftp: - return "far fa-copy"; - case ExportDeploymentType.PublicFolder: - return "fa fa-unlock"; - default: - return "fa fa-question"; - } - } - } + var result = sb.ToString() + .ToValidFileName("") + .Truncate(maxFileNameLength); + + return result; + } + + /// + /// Get path of the deployment folder + /// + /// Export deployment + /// Deployment folder path + public static string GetDeploymentFolder(this ExportDeployment deployment, bool create = false) + { + if (deployment == null) + return null; + + string path = null; + + if (deployment.DeploymentType == ExportDeploymentType.PublicFolder) + { + if (deployment.SubFolder.HasValue()) + path = Path.Combine(HttpRuntime.AppDomainAppPath, DataExporter.PublicFolder, deployment.SubFolder); + else + path = Path.Combine(HttpRuntime.AppDomainAppPath, DataExporter.PublicFolder); + } + else if (deployment.DeploymentType == ExportDeploymentType.FileSystem) + { + if (deployment.FileSystemPath.IsEmpty()) + return null; + + if (Path.IsPathRooted(deployment.FileSystemPath)) + { + path = deployment.FileSystemPath; + } + else + { + path = PathHelper.NormalizeAppRelativePath(deployment.FileSystemPath); + path = CommonHelper.MapPath(path); + } + } + + if (create && !System.IO.Directory.Exists(path)) + System.IO.Directory.CreateDirectory(path); + + return path; + } + + /// + /// Get url of the public folder and take filtering and projection into consideration + /// + /// Export deployment + /// Common services + /// Store entity + /// Absolute URL of the public folder (always ends with /) or null + public static string GetPublicFolderUrl(this ExportDeployment deployment, ICommonServices services, Store store = null) + { + if (deployment != null && deployment.DeploymentType == ExportDeploymentType.PublicFolder) + { + if (store == null) + { + var filter = XmlHelper.Deserialize(deployment.Profile.Filtering); + var storeId = filter.StoreId; + + if (storeId == 0) + { + var projection = XmlHelper.Deserialize(deployment.Profile.Projection); + storeId = (projection.StoreId ?? 0); + } + + store = (storeId == 0 ? services.StoreContext.CurrentStore : services.StoreService.GetStoreById(storeId)); + } + + var url = string.Concat( + store.Url.EnsureEndsWith("/"), + DataExporter.PublicFolder.EnsureEndsWith("/"), + deployment.SubFolder.HasValue() ? deployment.SubFolder.EnsureEndsWith("/") : "" + ); + + return url; + } + + return null; + } + + /// + /// Get icon class for a deployment type + /// + /// Deployment type + /// Icon class + public static string GetIconClass(this ExportDeploymentType type) + { + switch (type) + { + case ExportDeploymentType.FileSystem: + return "far fa-folder-open"; + case ExportDeploymentType.Email: + return "far fa-envelope"; + case ExportDeploymentType.Http: + return "fa fa-globe"; + case ExportDeploymentType.Ftp: + return "far fa-copy"; + case ExportDeploymentType.PublicFolder: + return "fa fa-unlock"; + default: + return "fa fa-question"; + } + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportFeaturesAttribute.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportFeaturesAttribute.cs index eee6f1554e..3595e44867 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportFeaturesAttribute.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportFeaturesAttribute.cs @@ -3,13 +3,13 @@ namespace SmartStore.Services.DataExchange.Export { - /// - /// Declares data processing types supported by an export provider. - /// Projection type controls whether to display corresponding projection fields while editing an export profile. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public sealed class ExportFeaturesAttribute : Attribute - { - public ExportFeatures Features { get; set; } - } + /// + /// Declares data processing types supported by an export provider. + /// Projection type controls whether to display corresponding projection fields while editing an export profile. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class ExportFeaturesAttribute : Attribute + { + public ExportFeatures Features { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportFileStream.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportFileStream.cs new file mode 100644 index 0000000000..9b56d230fb --- /dev/null +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportFileStream.cs @@ -0,0 +1,89 @@ +using System.IO; + +namespace SmartStore.Services.DataExchange.Export +{ + public class ExportFileStream : Stream + { + private readonly Stream _stream; + private readonly long _flushBytesNumber; + private long _bytesCounter; + + /// Stream instance to write to. + /// Number of bytes when to write to the hard disk. Default is each 4 MB. + public ExportFileStream(Stream stream, long flushBytesNumber = 4194304) + { + Guard.NotNull(stream, nameof(stream)); + + _stream = stream; + _flushBytesNumber = flushBytesNumber; + } + + public override bool CanRead => _stream.CanRead; + + public override bool CanSeek => _stream.CanSeek; + + public override bool CanWrite => _stream.CanWrite; + + public override long Length => _stream.Length; + + public override long Position + { + get => _stream.Position; + set => _stream.Position = value; + } + + public override void Flush() + { + _stream.Flush(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + return _stream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return _stream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + _stream.SetLength(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + _stream.Write(buffer, offset, count); + + if (_flushBytesNumber > 0) + { + _bytesCounter += count; + + if (_bytesCounter > _flushBytesNumber) + { + _bytesCounter = 0; + + if (_stream is FileStream fStream) + { + fStream.Flush(true); + } + else + { + _stream.Flush(); + } + } + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _stream?.Dispose(); + } + + base.Dispose(disposing); + } + } +} diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportProfileService.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportProfileService.cs index e3a4f541b6..d5c2c24001 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportProfileService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportProfileService.cs @@ -16,232 +16,232 @@ namespace SmartStore.Services.DataExchange.Export { - public partial class ExportProfileService : IExportProfileService - { - private const string _defaultFileNamePattern = "%Store.Id%-%Profile.Id%-%File.Index%-%Profile.SeoName%"; - - private readonly IRepository _exportProfileRepository; - private readonly IRepository _exportDeploymentRepository; - private readonly IEventPublisher _eventPublisher; - private readonly IScheduleTaskService _scheduleTaskService; - private readonly IProviderManager _providerManager; - private readonly DataExchangeSettings _dataExchangeSettings; - private readonly ILocalizationService _localizationService; - - public ExportProfileService( - IRepository exportProfileRepository, - IRepository exportDeploymentRepository, - IEventPublisher eventPublisher, - IScheduleTaskService scheduleTaskService, - IProviderManager providerManager, - DataExchangeSettings dataExchangeSettings, - ILocalizationService localizationService) - { - _exportProfileRepository = exportProfileRepository; - _exportDeploymentRepository = exportDeploymentRepository; - _eventPublisher = eventPublisher; - _scheduleTaskService = scheduleTaskService; - _providerManager = providerManager; - _dataExchangeSettings = dataExchangeSettings; - _localizationService = localizationService; - } - - public virtual ExportProfile InsertExportProfile( - string providerSystemName, - string name, - string fileExtension, - ExportFeatures features, - bool isSystemProfile = false, - string profileSystemName = null, - int cloneFromProfileId = 0) - { - Guard.NotEmpty(providerSystemName, nameof(providerSystemName)); - - if (name.IsEmpty()) - { - name = providerSystemName; - } - - if (!isSystemProfile) - { - var profileCount = _exportProfileRepository.Table.Count(x => x.ProviderSystemName == providerSystemName); - - name = string.Concat(_localizationService.GetResource("Common.My"), " ", name, " ", profileCount + 1); - } - - var cloneProfile = GetExportProfileById(cloneFromProfileId); - - ScheduleTask task = null; - ExportProfile profile = null; - - if (cloneProfile == null) - { - task = new ScheduleTask - { - CronExpression = "0 */6 * * *", // Every six hours. - Type = typeof(DataExportTask).AssemblyQualifiedNameWithoutVersion(), - Enabled = false, - StopOnError = false, - IsHidden = true - }; - } - else - { - task = cloneProfile.ScheduleTask.Clone(); - } - - task.Name = string.Concat(name, " Task"); - - _scheduleTaskService.InsertTask(task); - - if (cloneProfile == null) - { - profile = new ExportProfile - { - FileNamePattern = _defaultFileNamePattern - }; - - if (isSystemProfile) - { - profile.Enabled = true; - profile.PerStore = false; - profile.CreateZipArchive = false; - profile.Cleanup = false; - } - else - { - // What we do here is to preset typical settings for feed creation - // but on the other hand they may be untypical for generic data export\exchange. - var projection = new ExportProjection - { - RemoveCriticalCharacters = true, - CriticalCharacters = "¼,½,¾", - PriceType = PriceDisplayType.PreSelectedPrice, - NoGroupedProducts = features.HasFlag(ExportFeatures.CanOmitGroupedProducts) ? true : false, - OnlyIndividuallyVisibleAssociated = true, - DescriptionMerging = ExportDescriptionMerging.Description - }; - - var filter = new ExportFilter - { - IsPublished = true, - ShoppingCartTypeId = (int)ShoppingCartType.ShoppingCart - }; - - profile.Projection = XmlHelper.Serialize(projection); - profile.Filtering = XmlHelper.Serialize(filter); - } - } - else - { - profile = cloneProfile.Clone(); - } - - profile.IsSystemProfile = isSystemProfile; - profile.Name = name; - profile.ProviderSystemName = providerSystemName; - profile.SchedulingTaskId = task.Id; - - var cleanedSystemName = providerSystemName - .Replace("Exports.", "") - .Replace("Feeds.", "") - .Replace("/", "") - .Replace("-", ""); - - var folderName = SeoHelper.GetSeName(cleanedSystemName, true, false, false) - .ToValidPath() - .Truncate(_dataExchangeSettings.MaxFileNameLength); - - var path = DataSettings.Current.TenantPath + "/ExportProfiles"; - profile.FolderName = path + "/" + FileSystemHelper.CreateNonExistingDirectoryName(CommonHelper.MapPath(path), folderName); - - profile.SystemName = profileSystemName.IsEmpty() && isSystemProfile - ? cleanedSystemName - : profileSystemName; - - _exportProfileRepository.Insert(profile); - - - task.Alias = profile.Id.ToString(); - _scheduleTaskService.UpdateTask(task); - - if (fileExtension.HasValue() && !isSystemProfile) - { - if (cloneProfile == null) - { - if (features.HasFlag(ExportFeatures.CreatesInitialPublicDeployment)) - { - var subFolder = FileSystemHelper.CreateNonExistingDirectoryName(CommonHelper.MapPath("~/" + DataExporter.PublicFolder), folderName); - - profile.Deployments.Add(new ExportDeployment - { - ProfileId = profile.Id, - Enabled = true, - DeploymentType = ExportDeploymentType.PublicFolder, - Name = profile.Name, - SubFolder = subFolder - }); - - UpdateExportProfile(profile); - } - } - else - { - foreach (var deployment in cloneProfile.Deployments) - { - profile.Deployments.Add(deployment.Clone()); - } - - UpdateExportProfile(profile); - } - } - - return profile; - } - - public virtual ExportProfile InsertExportProfile( - Provider provider, - bool isSystemProfile = false, - string profileSystemName = null, - int cloneFromProfileId = 0) - { - Guard.NotNull(provider, nameof(provider)); - - var profile = InsertExportProfile( - provider.Metadata.SystemName, - provider.GetName(_localizationService), - provider.Value.FileExtension, - provider.Metadata.ExportFeatures, - isSystemProfile, - profileSystemName, - cloneFromProfileId); - - return profile; - } - - public virtual void UpdateExportProfile(ExportProfile profile) - { - Guard.NotNull(profile, nameof(profile)); - - profile.FolderName = PathHelper.NormalizeAppRelativePath(profile.FolderName); + public partial class ExportProfileService : IExportProfileService + { + private const string _defaultFileNamePattern = "%Store.Id%-%Profile.Id%-%File.Index%-%Profile.SeoName%"; + + private readonly IRepository _exportProfileRepository; + private readonly IRepository _exportDeploymentRepository; + private readonly IEventPublisher _eventPublisher; + private readonly IScheduleTaskService _scheduleTaskService; + private readonly IProviderManager _providerManager; + private readonly DataExchangeSettings _dataExchangeSettings; + private readonly ILocalizationService _localizationService; + + public ExportProfileService( + IRepository exportProfileRepository, + IRepository exportDeploymentRepository, + IEventPublisher eventPublisher, + IScheduleTaskService scheduleTaskService, + IProviderManager providerManager, + DataExchangeSettings dataExchangeSettings, + ILocalizationService localizationService) + { + _exportProfileRepository = exportProfileRepository; + _exportDeploymentRepository = exportDeploymentRepository; + _eventPublisher = eventPublisher; + _scheduleTaskService = scheduleTaskService; + _providerManager = providerManager; + _dataExchangeSettings = dataExchangeSettings; + _localizationService = localizationService; + } + + public virtual ExportProfile InsertExportProfile( + string providerSystemName, + string name, + string fileExtension, + ExportFeatures features, + bool isSystemProfile = false, + string profileSystemName = null, + int cloneFromProfileId = 0) + { + Guard.NotEmpty(providerSystemName, nameof(providerSystemName)); + + if (name.IsEmpty()) + { + name = providerSystemName; + } + + if (!isSystemProfile) + { + var profileCount = _exportProfileRepository.Table.Count(x => x.ProviderSystemName == providerSystemName); + + name = string.Concat(_localizationService.GetResource("Common.My"), " ", name, " ", profileCount + 1); + } + + var cloneProfile = GetExportProfileById(cloneFromProfileId); + + ScheduleTask task = null; + ExportProfile profile = null; + + if (cloneProfile == null) + { + task = new ScheduleTask + { + CronExpression = "0 */6 * * *", // Every six hours. + Type = typeof(DataExportTask).AssemblyQualifiedNameWithoutVersion(), + Enabled = false, + StopOnError = false, + IsHidden = true + }; + } + else + { + task = cloneProfile.ScheduleTask.Clone(); + } + + task.Name = string.Concat(name, " Task"); + + _scheduleTaskService.InsertTask(task); + + if (cloneProfile == null) + { + profile = new ExportProfile + { + FileNamePattern = _defaultFileNamePattern + }; + + if (isSystemProfile) + { + profile.Enabled = true; + profile.PerStore = false; + profile.CreateZipArchive = false; + profile.Cleanup = false; + } + else + { + // What we do here is to preset typical settings for feed creation + // but on the other hand they may be untypical for generic data export\exchange. + var projection = new ExportProjection + { + RemoveCriticalCharacters = true, + CriticalCharacters = "¼,½,¾", + PriceType = PriceDisplayType.PreSelectedPrice, + NoGroupedProducts = features.HasFlag(ExportFeatures.CanOmitGroupedProducts) ? true : false, + OnlyIndividuallyVisibleAssociated = true, + DescriptionMerging = ExportDescriptionMerging.Description + }; + + var filter = new ExportFilter + { + IsPublished = true, + ShoppingCartTypeId = (int)ShoppingCartType.ShoppingCart + }; + + profile.Projection = XmlHelper.Serialize(projection); + profile.Filtering = XmlHelper.Serialize(filter); + } + } + else + { + profile = cloneProfile.Clone(); + } + + profile.IsSystemProfile = isSystemProfile; + profile.Name = name; + profile.ProviderSystemName = providerSystemName; + profile.SchedulingTaskId = task.Id; + + var cleanedSystemName = providerSystemName + .Replace("Exports.", "") + .Replace("Feeds.", "") + .Replace("/", "") + .Replace("-", ""); + + var folderName = SeoHelper.GetSeName(cleanedSystemName, true, false, false) + .ToValidPath() + .Truncate(_dataExchangeSettings.MaxFileNameLength); + + var path = DataSettings.Current.TenantPath + "/ExportProfiles"; + profile.FolderName = path + "/" + FileSystemHelper.CreateNonExistingDirectoryName(CommonHelper.MapPath(path), folderName); + + profile.SystemName = profileSystemName.IsEmpty() && isSystemProfile + ? cleanedSystemName + : profileSystemName; + + _exportProfileRepository.Insert(profile); + + + task.Alias = profile.Id.ToString(); + _scheduleTaskService.UpdateTask(task); + + if (fileExtension.HasValue() && !isSystemProfile) + { + if (cloneProfile == null) + { + if (features.HasFlag(ExportFeatures.CreatesInitialPublicDeployment)) + { + var subFolder = FileSystemHelper.CreateNonExistingDirectoryName(CommonHelper.MapPath("~/" + DataExporter.PublicFolder), folderName); + + profile.Deployments.Add(new ExportDeployment + { + ProfileId = profile.Id, + Enabled = true, + DeploymentType = ExportDeploymentType.PublicFolder, + Name = profile.Name, + SubFolder = subFolder + }); + + UpdateExportProfile(profile); + } + } + else + { + foreach (var deployment in cloneProfile.Deployments) + { + profile.Deployments.Add(deployment.Clone()); + } + + UpdateExportProfile(profile); + } + } + + return profile; + } + + public virtual ExportProfile InsertExportProfile( + Provider provider, + bool isSystemProfile = false, + string profileSystemName = null, + int cloneFromProfileId = 0) + { + Guard.NotNull(provider, nameof(provider)); + + var profile = InsertExportProfile( + provider.Metadata.SystemName, + provider.GetName(_localizationService), + provider.Value.FileExtension, + provider.Metadata.ExportFeatures, + isSystemProfile, + profileSystemName, + cloneFromProfileId); + + return profile; + } + + public virtual void UpdateExportProfile(ExportProfile profile) + { + Guard.NotNull(profile, nameof(profile)); + + profile.FolderName = PathHelper.NormalizeAppRelativePath(profile.FolderName); if (!PathHelper.IsSafeAppRootPath(profile.FolderName)) { throw new SmartException(_localizationService.GetResource("Admin.DataExchange.Export.FolderName.Validate")); } - _exportProfileRepository.Update(profile); - } + _exportProfileRepository.Update(profile); + } - public virtual void DeleteExportProfile(ExportProfile profile, bool force = false) - { - Guard.NotNull(profile, nameof(profile)); + public virtual void DeleteExportProfile(ExportProfile profile, bool force = false) + { + Guard.NotNull(profile, nameof(profile)); - if (!force && profile.IsSystemProfile) - throw new SmartException(_localizationService.GetResource("Admin.DataExchange.Export.CannotDeleteSystemProfile")); + if (!force && profile.IsSystemProfile) + throw new SmartException(_localizationService.GetResource("Admin.DataExchange.Export.CannotDeleteSystemProfile")); - int scheduleTaskId = profile.SchedulingTaskId; - var folder = profile.GetExportFolder(); + int scheduleTaskId = profile.SchedulingTaskId; + var folder = profile.GetExportFolder(); var deployments = profile.Deployments.Where(x => !x.IsTransientRecord()).ToList(); if (deployments.Any()) @@ -252,123 +252,123 @@ public virtual void DeleteExportProfile(ExportProfile profile, bool force = fals _exportProfileRepository.Delete(profile); - var scheduleTask = _scheduleTaskService.GetTaskById(scheduleTaskId); - _scheduleTaskService.DeleteTask(scheduleTask); + var scheduleTask = _scheduleTaskService.GetTaskById(scheduleTaskId); + _scheduleTaskService.DeleteTask(scheduleTask); + + if (System.IO.Directory.Exists(folder)) + { + FileSystemHelper.ClearDirectory(folder, true); + } + } + + public virtual IQueryable GetExportProfiles(bool? enabled = null) + { + var query = _exportProfileRepository.Table + .Expand(x => x.ScheduleTask) + .Expand(x => x.Deployments); + + if (enabled.HasValue) + { + query = query.Where(x => x.Enabled == enabled.Value); + } + + query = query + .OrderBy(x => x.IsSystemProfile) + .ThenBy(x => x.Name); + + return query; + } + + public virtual ExportProfile GetExportProfileById(int id) + { + if (id == 0) + return null; + + var profile = _exportProfileRepository.Table + .Expand(x => x.ScheduleTask) + .Expand(x => x.Deployments) + .FirstOrDefault(x => x.Id == id); + + return profile; + } + + public virtual ExportProfile GetSystemExportProfile(string providerSystemName) + { + if (providerSystemName.IsEmpty()) + return null; + + var query = GetExportProfiles(true); + + var profile = query + .Where(x => x.IsSystemProfile && x.ProviderSystemName == providerSystemName) + .FirstOrDefault(); + + return profile; + } + + public virtual IList GetExportProfilesBySystemName(string providerSystemName) + { + if (providerSystemName.IsEmpty()) + return new List(); + + var profiles = _exportProfileRepository.Table + .Expand(x => x.ScheduleTask) + .Expand(x => x.Deployments) + .Where(x => x.ProviderSystemName == providerSystemName) + .ToList(); + + return profiles; + } + + + public virtual IEnumerable> LoadAllExportProviders(int storeId = 0, bool showHidden = true) + { + var allProviders = _providerManager.GetAllProviders(storeId) + .Where(x => x.IsValid() && (showHidden || !x.Metadata.IsHidden)) + //.OrderBy(x => x.Metadata.SystemName) + .OrderBy(x => x.Metadata.FriendlyName); + + return allProviders; + } + + public virtual Provider LoadProvider(string systemName, int storeId = 0) + { + var provider = _providerManager.GetProvider(systemName, storeId); + + return (provider.IsValid() ? provider : null); + } + + public virtual ExportDeployment GetExportDeploymentById(int id) + { + if (id == 0) + return null; + + var deployment = _exportDeploymentRepository.Table + .Expand(x => x.Profile) + .FirstOrDefault(x => x.Id == id); + + return deployment; + } + + public virtual void UpdateExportDeployment(ExportDeployment deployment) + { + if (deployment == null) + throw new ArgumentNullException("deployment"); + + if (deployment.DeploymentType == ExportDeploymentType.FileSystem && deployment.FileSystemPath == "~/") + { + throw new SmartException("Invalid deployment path."); + } + + _exportDeploymentRepository.Update(deployment); + } - if (System.IO.Directory.Exists(folder)) - { - FileSystemHelper.ClearDirectory(folder, true); - } - } + public virtual void DeleteExportDeployment(ExportDeployment deployment) + { + if (deployment == null) + throw new ArgumentNullException("deployment"); - public virtual IQueryable GetExportProfiles(bool? enabled = null) - { - var query = _exportProfileRepository.Table - .Expand(x => x.ScheduleTask) - .Expand(x => x.Deployments); - - if (enabled.HasValue) - { - query = query.Where(x => x.Enabled == enabled.Value); - } - - query = query - .OrderBy(x => x.IsSystemProfile) - .ThenBy(x => x.Name); - - return query; - } - - public virtual ExportProfile GetExportProfileById(int id) - { - if (id == 0) - return null; - - var profile = _exportProfileRepository.Table - .Expand(x => x.ScheduleTask) - .Expand(x => x.Deployments) - .FirstOrDefault(x => x.Id == id); - - return profile; - } - - public virtual ExportProfile GetSystemExportProfile(string providerSystemName) - { - if (providerSystemName.IsEmpty()) - return null; - - var query = GetExportProfiles(true); - - var profile = query - .Where(x => x.IsSystemProfile && x.ProviderSystemName == providerSystemName) - .FirstOrDefault(); - - return profile; - } - - public virtual IList GetExportProfilesBySystemName(string providerSystemName) - { - if (providerSystemName.IsEmpty()) - return new List(); - - var profiles = _exportProfileRepository.Table - .Expand(x => x.ScheduleTask) - .Expand(x => x.Deployments) - .Where(x => x.ProviderSystemName == providerSystemName) - .ToList(); - - return profiles; - } - - - public virtual IEnumerable> LoadAllExportProviders(int storeId = 0, bool showHidden = true) - { - var allProviders = _providerManager.GetAllProviders(storeId) - .Where(x => x.IsValid() && (showHidden || !x.Metadata.IsHidden)) - //.OrderBy(x => x.Metadata.SystemName) - .OrderBy(x => x.Metadata.FriendlyName); - - return allProviders; - } - - public virtual Provider LoadProvider(string systemName, int storeId = 0) - { - var provider = _providerManager.GetProvider(systemName, storeId); - - return (provider.IsValid() ? provider : null); - } - - public virtual ExportDeployment GetExportDeploymentById(int id) - { - if (id == 0) - return null; - - var deployment = _exportDeploymentRepository.Table - .Expand(x => x.Profile) - .FirstOrDefault(x => x.Id == id); - - return deployment; - } - - public virtual void UpdateExportDeployment(ExportDeployment deployment) - { - if (deployment == null) - throw new ArgumentNullException("deployment"); - - if (deployment.DeploymentType == ExportDeploymentType.FileSystem && deployment.FileSystemPath == "~/") - { - throw new SmartException("Invalid deployment path."); - } - - _exportDeploymentRepository.Update(deployment); - } - - public virtual void DeleteExportDeployment(ExportDeployment deployment) - { - if (deployment == null) - throw new ArgumentNullException("deployment"); - - _exportDeploymentRepository.Delete(deployment); - } - } + _exportDeploymentRepository.Delete(deployment); + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportProviderBase.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportProviderBase.cs index c77638f60b..6bed98e1d8 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportProviderBase.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportProviderBase.cs @@ -2,49 +2,40 @@ namespace SmartStore.Services.DataExchange.Export { - public abstract class ExportProviderBase : IExportProvider - { - /// - /// The exported entity type - /// - public virtual ExportEntityType EntityType - { - get { return ExportEntityType.Product; } - } + public abstract class ExportProviderBase : IExportProvider + { + /// + /// The exported entity type + /// + public virtual ExportEntityType EntityType => ExportEntityType.Product; - /// - /// File extension of the export files (without dot). Return null for a non file based, on-the-fly export. - /// - public virtual string FileExtension - { - get { return null; } - } + /// + /// File extension of the export files (without dot). Return null for a non file based, on-the-fly export. + /// + public virtual string FileExtension => null; - /// - /// Get provider specific configuration information. Return null when no provider specific configuration is required. - /// - public virtual ExportConfigurationInfo ConfigurationInfo - { - get { return null; } - } + /// + /// Get provider specific configuration information. Return null when no provider specific configuration is required. + /// + public virtual ExportConfigurationInfo ConfigurationInfo => null; - public void Execute(ExportExecuteContext context) - { - Export(context); - } + public void Execute(ExportExecuteContext context) + { + Export(context); + } - /// - /// Export data to a file - /// - /// Export execution context - protected abstract void Export(ExportExecuteContext context); + /// + /// Export data to a file + /// + /// Export execution context + protected abstract void Export(ExportExecuteContext context); - /// - /// Called once per store when export execution ended - /// - /// Export execution context - public virtual void OnExecuted(ExportExecuteContext context) - { - } - } + /// + /// Called once per store when export execution ended + /// + /// Export execution context + public virtual void OnExecuted(ExportExecuteContext context) + { + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportXmlHelper.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportXmlHelper.cs index 4af3ac1836..1ba4e09442 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/ExportXmlHelper.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/ExportXmlHelper.cs @@ -13,193 +13,187 @@ namespace SmartStore.Services.DataExchange.Export { - public class ExportXmlHelper : IDisposable - { - protected XmlWriter _writer; - protected CultureInfo _culture; - protected bool _doNotDispose; - - public ExportXmlHelper(XmlWriter writer, bool doNotDispose = false, CultureInfo culture = null) - { - _writer = writer; - _doNotDispose = doNotDispose; - _culture = (culture == null ? CultureInfo.InvariantCulture : culture); - } - public ExportXmlHelper(Stream stream, XmlWriterSettings settings = null, CultureInfo culture = null) - { - if (settings == null) - { - settings = DefaultSettings; - } - - _writer = XmlWriter.Create(stream, settings); - _culture = (culture == null ? CultureInfo.InvariantCulture : culture); - } - - public static XmlWriterSettings DefaultSettings - { - get - { - return new XmlWriterSettings - { - Encoding = Encoding.UTF8, - CheckCharacters = false, - Indent = true, - IndentChars = "\t" - }; - } - } - - public ExportXmlExclude Exclude { get; set; } - - public XmlWriter Writer => _writer; - - public void Dispose() - { - if (_writer != null && !_doNotDispose) - { - _writer.Dispose(); - } - } - - public void WriteLocalized(dynamic parentNode) - { - if (parentNode == null || parentNode._Localized == null) - return; - - _writer.WriteStartElement("Localized"); - foreach (dynamic item in parentNode._Localized) - { - _writer.WriteStartElement((string)item.LocaleKey); - _writer.WriteAttributeString("culture", (string)item.Culture); - _writer.WriteString(((string)item.LocaleValue).RemoveInvalidXmlChars()); - _writer.WriteEndElement(); // item.LocaleKey - } - _writer.WriteEndElement(); // Localized - } - - public void WriteGenericAttributes(dynamic parentNode) - { - if (parentNode == null || parentNode._GenericAttributes == null) - return; - - _writer.WriteStartElement("GenericAttributes"); - foreach (dynamic genericAttribute in parentNode._GenericAttributes) - { - GenericAttribute entity = genericAttribute.Entity; - - _writer.WriteStartElement("GenericAttribute"); - _writer.Write("Id", entity.Id.ToString()); - _writer.Write("EntityId", entity.EntityId.ToString()); - _writer.Write("KeyGroup", entity.KeyGroup); - _writer.Write("Key", entity.Key); - _writer.Write("Value", (string)genericAttribute.Value); - _writer.Write("StoreId", entity.StoreId.ToString()); - _writer.WriteEndElement(); // GenericAttribute - } - _writer.WriteEndElement(); // GenericAttributes - } - - public void WriteAddress(dynamic address, string node) - { - if (address == null) - return; - - Address entity = address.Entity; - - if (node.HasValue()) - { - _writer.WriteStartElement(node); - } - - _writer.Write("Id", entity.Id.ToString()); - _writer.Write("Salutation", entity.Salutation); - _writer.Write("Title", entity.Title); - _writer.Write("FirstName", entity.FirstName); - _writer.Write("LastName", entity.LastName); - _writer.Write("Email", entity.Email); - _writer.Write("Company", entity.Company); - _writer.Write("CountryId", entity.CountryId.HasValue ? entity.CountryId.Value.ToString() : ""); - _writer.Write("StateProvinceId", entity.StateProvinceId.HasValue ? entity.StateProvinceId.Value.ToString() : ""); - _writer.Write("City", entity.City); - _writer.Write("Address1", entity.Address1); - _writer.Write("Address2", entity.Address2); - _writer.Write("ZipPostalCode", entity.ZipPostalCode); - _writer.Write("PhoneNumber", entity.PhoneNumber); - _writer.Write("FaxNumber", entity.FaxNumber); - _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); - - if (address.Country != null) - { - dynamic country = address.Country; - Country entityCountry = address.Country.Entity; - - _writer.WriteStartElement("Country"); - _writer.Write("Id", entityCountry.Id.ToString()); - _writer.Write("Name", (string)country.Name); - _writer.Write("AllowsBilling", entityCountry.AllowsBilling.ToString()); - _writer.Write("AllowsShipping", entityCountry.AllowsShipping.ToString()); - _writer.Write("TwoLetterIsoCode", entityCountry.TwoLetterIsoCode); - _writer.Write("ThreeLetterIsoCode", entityCountry.ThreeLetterIsoCode); - _writer.Write("NumericIsoCode", entityCountry.NumericIsoCode.ToString()); - _writer.Write("SubjectToVat", entityCountry.SubjectToVat.ToString()); - _writer.Write("Published", entityCountry.Published.ToString()); - _writer.Write("DisplayOrder", entityCountry.DisplayOrder.ToString()); - _writer.Write("LimitedToStores", entityCountry.LimitedToStores.ToString()); - - WriteLocalized(country); - - _writer.WriteEndElement(); // Country - } - - if (address.StateProvince != null) - { - dynamic stateProvince = address.StateProvince; - StateProvince entityStateProvince = address.StateProvince.Entity; - - _writer.WriteStartElement("StateProvince"); - _writer.Write("Id", entityStateProvince.Id.ToString()); - _writer.Write("CountryId", entityStateProvince.CountryId.ToString()); - _writer.Write("Name", (string)stateProvince.Name); - _writer.Write("Abbreviation", (string)stateProvince.Abbreviation); - _writer.Write("Published", entityStateProvince.Published.ToString()); - _writer.Write("DisplayOrder", entityStateProvince.DisplayOrder.ToString()); - - WriteLocalized(stateProvince); - - _writer.WriteEndElement(); // StateProvince - } - - if (node.HasValue()) - { - _writer.WriteEndElement(); - } - } - - public void WriteCurrency(dynamic currency, string node) - { - if (currency == null) - return; - - Currency entity = currency.Entity; - - if (node.HasValue()) - { - _writer.WriteStartElement(node); - } - - _writer.Write("Id", entity.Id.ToString()); - _writer.Write("Name", (string)currency.Name); - _writer.Write("CurrencyCode", entity.CurrencyCode); - _writer.Write("Rate", entity.Rate.ToString(_culture)); - _writer.Write("DisplayLocale", entity.DisplayLocale); - _writer.Write("CustomFormatting", entity.CustomFormatting); - _writer.Write("LimitedToStores", entity.LimitedToStores.ToString()); - _writer.Write("Published", entity.Published.ToString()); - _writer.Write("DisplayOrder", entity.DisplayOrder.ToString()); - _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); - _writer.Write("UpdatedOnUtc", entity.UpdatedOnUtc.ToString(_culture)); - _writer.Write("DomainEndings", entity.DomainEndings); + public class ExportXmlHelper : IDisposable + { + protected XmlWriter _writer; + protected CultureInfo _culture; + protected bool _doNotDispose; + + public ExportXmlHelper(XmlWriter writer, bool doNotDispose = false, CultureInfo culture = null) + { + _writer = writer; + _doNotDispose = doNotDispose; + _culture = culture == null ? CultureInfo.InvariantCulture : culture; + } + public ExportXmlHelper(Stream stream, XmlWriterSettings settings = null, CultureInfo culture = null) + { + if (settings == null) + { + settings = DefaultSettings; + } + + _writer = XmlWriter.Create(stream, settings); + _culture = culture == null ? CultureInfo.InvariantCulture : culture; + } + + public static XmlWriterSettings DefaultSettings => new XmlWriterSettings + { + Encoding = Encoding.UTF8, + CheckCharacters = false, + Indent = false, + NewLineHandling = NewLineHandling.None + }; + + public ExportXmlExclude Exclude { get; set; } + + public XmlWriter Writer => _writer; + + public void Dispose() + { + if (_writer != null && !_doNotDispose) + { + _writer.Dispose(); + } + } + + public void WriteLocalized(dynamic parentNode) + { + if (parentNode == null || parentNode._Localized == null) + return; + + _writer.WriteStartElement("Localized"); + foreach (dynamic item in parentNode._Localized) + { + _writer.WriteStartElement((string)item.LocaleKey); + _writer.WriteAttributeString("culture", (string)item.Culture); + _writer.WriteString(((string)item.LocaleValue).RemoveInvalidXmlChars()); + _writer.WriteEndElement(); // item.LocaleKey + } + _writer.WriteEndElement(); // Localized + } + + public void WriteGenericAttributes(dynamic parentNode) + { + if (parentNode == null || parentNode._GenericAttributes == null) + return; + + _writer.WriteStartElement("GenericAttributes"); + foreach (dynamic genericAttribute in parentNode._GenericAttributes) + { + GenericAttribute entity = genericAttribute.Entity; + + _writer.WriteStartElement("GenericAttribute"); + _writer.Write("Id", entity.Id.ToString()); + _writer.Write("EntityId", entity.EntityId.ToString()); + _writer.Write("KeyGroup", entity.KeyGroup); + _writer.Write("Key", entity.Key); + _writer.Write("Value", (string)genericAttribute.Value); + _writer.Write("StoreId", entity.StoreId.ToString()); + _writer.WriteEndElement(); // GenericAttribute + } + _writer.WriteEndElement(); // GenericAttributes + } + + public void WriteAddress(dynamic address, string node) + { + if (address == null) + return; + + Address entity = address.Entity; + + if (node.HasValue()) + { + _writer.WriteStartElement(node); + } + + _writer.Write("Id", entity.Id.ToString()); + _writer.Write("Salutation", entity.Salutation); + _writer.Write("Title", entity.Title); + _writer.Write("FirstName", entity.FirstName); + _writer.Write("LastName", entity.LastName); + _writer.Write("Email", entity.Email); + _writer.Write("Company", entity.Company); + _writer.Write("CountryId", entity.CountryId.HasValue ? entity.CountryId.Value.ToString() : ""); + _writer.Write("StateProvinceId", entity.StateProvinceId.HasValue ? entity.StateProvinceId.Value.ToString() : ""); + _writer.Write("City", entity.City); + _writer.Write("Address1", entity.Address1); + _writer.Write("Address2", entity.Address2); + _writer.Write("ZipPostalCode", entity.ZipPostalCode); + _writer.Write("PhoneNumber", entity.PhoneNumber); + _writer.Write("FaxNumber", entity.FaxNumber); + _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); + + if (address.Country != null) + { + dynamic country = address.Country; + Country entityCountry = address.Country.Entity; + + _writer.WriteStartElement("Country"); + _writer.Write("Id", entityCountry.Id.ToString()); + _writer.Write("Name", (string)country.Name); + _writer.Write("AllowsBilling", entityCountry.AllowsBilling.ToString()); + _writer.Write("AllowsShipping", entityCountry.AllowsShipping.ToString()); + _writer.Write("TwoLetterIsoCode", entityCountry.TwoLetterIsoCode); + _writer.Write("ThreeLetterIsoCode", entityCountry.ThreeLetterIsoCode); + _writer.Write("NumericIsoCode", entityCountry.NumericIsoCode.ToString()); + _writer.Write("SubjectToVat", entityCountry.SubjectToVat.ToString()); + _writer.Write("Published", entityCountry.Published.ToString()); + _writer.Write("DisplayOrder", entityCountry.DisplayOrder.ToString()); + _writer.Write("LimitedToStores", entityCountry.LimitedToStores.ToString()); + + WriteLocalized(country); + + _writer.WriteEndElement(); // Country + } + + if (address.StateProvince != null) + { + dynamic stateProvince = address.StateProvince; + StateProvince entityStateProvince = address.StateProvince.Entity; + + _writer.WriteStartElement("StateProvince"); + _writer.Write("Id", entityStateProvince.Id.ToString()); + _writer.Write("CountryId", entityStateProvince.CountryId.ToString()); + _writer.Write("Name", (string)stateProvince.Name); + _writer.Write("Abbreviation", (string)stateProvince.Abbreviation); + _writer.Write("Published", entityStateProvince.Published.ToString()); + _writer.Write("DisplayOrder", entityStateProvince.DisplayOrder.ToString()); + + WriteLocalized(stateProvince); + + _writer.WriteEndElement(); // StateProvince + } + + if (node.HasValue()) + { + _writer.WriteEndElement(); + } + } + + public void WriteCurrency(dynamic currency, string node) + { + if (currency == null) + return; + + Currency entity = currency.Entity; + + if (node.HasValue()) + { + _writer.WriteStartElement(node); + } + + _writer.Write("Id", entity.Id.ToString()); + _writer.Write("Name", (string)currency.Name); + _writer.Write("CurrencyCode", entity.CurrencyCode); + _writer.Write("Rate", entity.Rate.ToString(_culture)); + _writer.Write("DisplayLocale", entity.DisplayLocale); + _writer.Write("CustomFormatting", entity.CustomFormatting); + _writer.Write("LimitedToStores", entity.LimitedToStores.ToString()); + _writer.Write("Published", entity.Published.ToString()); + _writer.Write("DisplayOrder", entity.DisplayOrder.ToString()); + _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); + _writer.Write("UpdatedOnUtc", entity.UpdatedOnUtc.ToString(_culture)); + _writer.Write("DomainEndings", entity.DomainEndings); _writer.Write("RoundOrderItemsEnabled", entity.RoundOrderItemsEnabled.ToString()); _writer.Write("RoundNumDecimals", entity.RoundNumDecimals.ToString()); _writer.Write("RoundOrderTotalEnabled", entity.RoundOrderTotalEnabled.ToString()); @@ -208,404 +202,414 @@ public void WriteCurrency(dynamic currency, string node) WriteLocalized(currency); - if (node.HasValue()) - { - _writer.WriteEndElement(); - } - } - - public void WriteCountry(dynamic country, string node) - { - if (country == null) - return; - - Country entity = country.Entity; - - if (node.HasValue()) - { - _writer.WriteStartElement(node); - } - - _writer.Write("Id", entity.Id.ToString()); - _writer.Write("Name", entity.Name); - _writer.Write("AllowsBilling", entity.AllowsBilling.ToString()); - _writer.Write("AllowsShipping", entity.AllowsShipping.ToString()); - _writer.Write("TwoLetterIsoCode", entity.TwoLetterIsoCode); - _writer.Write("ThreeLetterIsoCode", entity.ThreeLetterIsoCode); - _writer.Write("NumericIsoCode", entity.NumericIsoCode.ToString()); - _writer.Write("SubjectToVat", entity.SubjectToVat.ToString()); - _writer.Write("Published", entity.Published.ToString()); - _writer.Write("DisplayOrder", entity.DisplayOrder.ToString()); - _writer.Write("LimitedToStores", entity.LimitedToStores.ToString()); - - WriteLocalized(country); - - if (node.HasValue()) - { - _writer.WriteEndElement(); - } - } - - public void WriteRewardPointsHistory(dynamic rewardPoints, string node) - { - if (rewardPoints == null) - return; - - if (node.HasValue()) - { - _writer.WriteStartElement(node); - } - - foreach (dynamic rewardPoint in rewardPoints) - { - RewardPointsHistory entity = rewardPoint.Entity; - - _writer.WriteStartElement("RewardPointsHistory"); - _writer.Write("Id", entity.ToString()); - _writer.Write("CustomerId", entity.ToString()); - _writer.Write("Points", entity.Points.ToString()); - _writer.Write("PointsBalance", entity.PointsBalance.ToString()); - _writer.Write("UsedAmount", entity.UsedAmount.ToString(_culture)); - _writer.Write("Message", (string)rewardPoint.Message); - _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); - _writer.WriteEndElement(); // RewardPointsHistory - } - - if (node.HasValue()) - { - _writer.WriteEndElement(); - } - } - - public void WriteDeliveryTime(dynamic deliveryTime, string node) - { - if (deliveryTime == null) - return; - - DeliveryTime entity = deliveryTime.Entity; - - if (node.HasValue()) - { - _writer.WriteStartElement(node); - } - - _writer.Write("Id", entity.Id.ToString()); - _writer.Write("Name", (string)deliveryTime.Name); - _writer.Write("DisplayLocale", entity.DisplayLocale); - _writer.Write("ColorHexValue", entity.ColorHexValue); - _writer.Write("DisplayOrder", entity.DisplayOrder.ToString()); - _writer.Write("IsDefault", entity.IsDefault.ToString()); - - WriteLocalized(deliveryTime); - - if (node.HasValue()) - { - _writer.WriteEndElement(); - } - } - - public void WriteQuantityUnit(dynamic quantityUnit, string node) - { - if (quantityUnit == null) - return; - - QuantityUnit entity = quantityUnit.Entity; - - if (node.HasValue()) - { - _writer.WriteStartElement(node); - } - - _writer.Write("Id", entity.Id.ToString()); - _writer.Write("Name", (string)quantityUnit.Name); + if (node.HasValue()) + { + _writer.WriteEndElement(); + } + } + + public void WriteCountry(dynamic country, string node) + { + if (country == null) + return; + + Country entity = country.Entity; + + if (node.HasValue()) + { + _writer.WriteStartElement(node); + } + + _writer.Write("Id", entity.Id.ToString()); + _writer.Write("Name", entity.Name); + _writer.Write("AllowsBilling", entity.AllowsBilling.ToString()); + _writer.Write("AllowsShipping", entity.AllowsShipping.ToString()); + _writer.Write("TwoLetterIsoCode", entity.TwoLetterIsoCode); + _writer.Write("ThreeLetterIsoCode", entity.ThreeLetterIsoCode); + _writer.Write("NumericIsoCode", entity.NumericIsoCode.ToString()); + _writer.Write("SubjectToVat", entity.SubjectToVat.ToString()); + _writer.Write("Published", entity.Published.ToString()); + _writer.Write("DisplayOrder", entity.DisplayOrder.ToString()); + _writer.Write("LimitedToStores", entity.LimitedToStores.ToString()); + + WriteLocalized(country); + + if (node.HasValue()) + { + _writer.WriteEndElement(); + } + } + + public void WriteRewardPointsHistory(dynamic rewardPoints, string node) + { + if (rewardPoints == null) + return; + + if (node.HasValue()) + { + _writer.WriteStartElement(node); + } + + foreach (dynamic rewardPoint in rewardPoints) + { + RewardPointsHistory entity = rewardPoint.Entity; + + _writer.WriteStartElement("RewardPointsHistory"); + _writer.Write("Id", entity.ToString()); + _writer.Write("CustomerId", entity.ToString()); + _writer.Write("Points", entity.Points.ToString()); + _writer.Write("PointsBalance", entity.PointsBalance.ToString()); + _writer.Write("UsedAmount", entity.UsedAmount.ToString(_culture)); + _writer.Write("Message", (string)rewardPoint.Message); + _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); + _writer.WriteEndElement(); // RewardPointsHistory + } + + if (node.HasValue()) + { + _writer.WriteEndElement(); + } + } + + public void WriteDeliveryTime(dynamic deliveryTime, string node) + { + if (deliveryTime == null) + return; + + DeliveryTime entity = deliveryTime.Entity; + + if (node.HasValue()) + { + _writer.WriteStartElement(node); + } + + _writer.Write("Id", entity.Id.ToString()); + _writer.Write("Name", (string)deliveryTime.Name); + _writer.Write("DisplayLocale", entity.DisplayLocale); + _writer.Write("ColorHexValue", entity.ColorHexValue); + _writer.Write("DisplayOrder", entity.DisplayOrder.ToString()); + _writer.Write("IsDefault", entity.IsDefault.ToString()); + _writer.Write("MinDays", entity.MinDays.HasValue ? entity.MinDays.Value.ToString() : ""); + _writer.Write("MaxDays", entity.MaxDays.HasValue ? entity.MaxDays.Value.ToString() : ""); + + WriteLocalized(deliveryTime); + + if (node.HasValue()) + { + _writer.WriteEndElement(); + } + } + + public void WriteQuantityUnit(dynamic quantityUnit, string node) + { + if (quantityUnit == null) + return; + + QuantityUnit entity = quantityUnit.Entity; + + if (node.HasValue()) + { + _writer.WriteStartElement(node); + } + + _writer.Write("Id", entity.Id.ToString()); + _writer.Write("Name", (string)quantityUnit.Name); _writer.Write("NamePlural", (string)quantityUnit.NamePlural); _writer.Write("Description", (string)quantityUnit.Description); - _writer.Write("DisplayLocale", entity.DisplayLocale); - _writer.Write("DisplayOrder", entity.DisplayOrder.ToString()); - _writer.Write("IsDefault", entity.IsDefault.ToString()); - - WriteLocalized(quantityUnit); - - if (node.HasValue()) - { - _writer.WriteEndElement(); - } - } - - public void WritePicture(dynamic picture, string node) - { - if (picture == null) - return; - - Picture entity = picture.Entity; - - if (node.HasValue()) - { - _writer.WriteStartElement(node); - } - - _writer.Write("Id", entity.Id.ToString()); - _writer.Write("SeoFilename", (string)picture.SeoFilename); - _writer.Write("MimeType", (string)picture.MimeType); - _writer.Write("ThumbImageUrl", (string)picture._ThumbImageUrl); - _writer.Write("ImageUrl", (string)picture._ImageUrl); - _writer.Write("FullSizeImageUrl", (string)picture._FullSizeImageUrl); - _writer.Write("FileName", (string)picture._FileName); - - if (node.HasValue()) - { - _writer.WriteEndElement(); - } - } - - public void WriteCategory(dynamic category, string node) - { - if (category == null) - return; - - Category entity = category.Entity; - - if (node.HasValue()) - { - _writer.WriteStartElement(node); - } - - _writer.Write("Id", entity.Id.ToString()); - - if (!Exclude.HasFlag(ExportXmlExclude.Category)) - { - _writer.Write("Name", (string)category.Name); - _writer.Write("FullName", (string)category.FullName); - _writer.Write("Description", (string)category.Description); - _writer.Write("BottomDescription", (string)category.BottomDescription); - _writer.Write("CategoryTemplateId", entity.CategoryTemplateId.ToString()); - _writer.Write("CategoryTemplateViewPath", (string)category._CategoryTemplateViewPath); - _writer.Write("MetaKeywords", (string)category.MetaKeywords); - _writer.Write("MetaDescription", (string)category.MetaDescription); - _writer.Write("MetaTitle", (string)category.MetaTitle); - _writer.Write("SeName", (string)category.SeName); - _writer.Write("ParentCategoryId", entity.ParentCategoryId.ToString()); - _writer.Write("PictureId", entity.PictureId.ToString()); - _writer.Write("PageSize", entity.PageSize.ToString()); - _writer.Write("AllowCustomersToSelectPageSize", entity.AllowCustomersToSelectPageSize.ToString()); - _writer.Write("PageSizeOptions", entity.PageSizeOptions); - _writer.Write("ShowOnHomePage", entity.ShowOnHomePage.ToString()); - _writer.Write("HasDiscountsApplied", entity.HasDiscountsApplied.ToString()); - _writer.Write("Published", entity.Published.ToString()); - _writer.Write("Deleted", entity.Deleted.ToString()); - _writer.Write("DisplayOrder", entity.DisplayOrder.ToString()); - _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); - _writer.Write("UpdatedOnUtc", entity.UpdatedOnUtc.ToString(_culture)); - _writer.Write("SubjectToAcl", entity.SubjectToAcl.ToString()); - _writer.Write("LimitedToStores", entity.LimitedToStores.ToString()); - _writer.Write("Alias", (string)category.Alias); - _writer.Write("DefaultViewMode", entity.DefaultViewMode); - - WritePicture(category.Picture, "Picture"); - - WriteLocalized(category); - } - - if (node.HasValue()) - { - _writer.WriteEndElement(); - } - } - - public void WriteManufacturer(dynamic manufacturer, string node) - { - if (manufacturer == null) - return; - - Manufacturer entity = manufacturer.Entity; - - if (node.HasValue()) - { - _writer.WriteStartElement(node); - } - - _writer.Write("Id", entity.Id.ToString()); - _writer.Write("Name", (string)manufacturer.Name); - _writer.Write("SeName", (string)manufacturer.SeName); - _writer.Write("Description", (string)manufacturer.Description); - _writer.Write("ManufacturerTemplateId", entity.ManufacturerTemplateId.ToString()); - _writer.Write("MetaKeywords", (string)manufacturer.MetaKeywords); - _writer.Write("MetaDescription", (string)manufacturer.MetaDescription); - _writer.Write("MetaTitle", (string)manufacturer.MetaTitle); - _writer.Write("PictureId", entity.PictureId.ToString()); - _writer.Write("PageSize", entity.PageSize.ToString()); - _writer.Write("AllowCustomersToSelectPageSize", entity.AllowCustomersToSelectPageSize.ToString()); - _writer.Write("PageSizeOptions", entity.PageSizeOptions); - _writer.Write("Published", entity.Published.ToString()); - _writer.Write("Deleted", entity.Deleted.ToString()); - _writer.Write("DisplayOrder", entity.DisplayOrder.ToString()); - _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); - _writer.Write("UpdatedOnUtc", entity.UpdatedOnUtc.ToString(_culture)); - _writer.Write("HasDiscountsApplied", entity.HasDiscountsApplied.ToString()); - - WritePicture(manufacturer.Picture, "Picture"); - - WriteLocalized(manufacturer); - - if (node.HasValue()) - { - _writer.WriteEndElement(); - } - } - - public void WriteProduct(dynamic product, string node) - { - if (product == null) - return; - - Product entity = product.Entity; - - if (node.HasValue()) - { - _writer.WriteStartElement(node); - } - - decimal? basePriceAmount = product.BasePriceAmount; - int? basePriceBaseAmount = product.BasePriceBaseAmount; - decimal? lowestAttributeCombinationPrice = product.LowestAttributeCombinationPrice; - - _writer.Write("Id", entity.Id.ToString()); - _writer.Write("Name", (string)product.Name); - _writer.Write("SeName", (string)product.SeName); - _writer.Write("ShortDescription", (string)product.ShortDescription); - _writer.Write("FullDescription", (string)product.FullDescription); - _writer.Write("AdminComment", (string)product.AdminComment); - _writer.Write("ProductTemplateId", entity.ProductTemplateId.ToString()); - _writer.Write("ProductTemplateViewPath", (string)product._ProductTemplateViewPath); - _writer.Write("ShowOnHomePage", entity.ShowOnHomePage.ToString()); - _writer.Write("HomePageDisplayOrder", entity.HomePageDisplayOrder.ToString()); - _writer.Write("MetaKeywords", (string)product.MetaKeywords); - _writer.Write("MetaDescription", (string)product.MetaDescription); - _writer.Write("MetaTitle", (string)product.MetaTitle); - _writer.Write("AllowCustomerReviews", entity.AllowCustomerReviews.ToString()); - _writer.Write("ApprovedRatingSum", entity.ApprovedRatingSum.ToString()); - _writer.Write("NotApprovedRatingSum", entity.NotApprovedRatingSum.ToString()); - _writer.Write("ApprovedTotalReviews", entity.ApprovedTotalReviews.ToString()); - _writer.Write("NotApprovedTotalReviews", entity.NotApprovedTotalReviews.ToString()); - _writer.Write("Published", entity.Published.ToString()); - _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); - _writer.Write("UpdatedOnUtc", entity.UpdatedOnUtc.ToString(_culture)); - _writer.Write("SubjectToAcl", entity.SubjectToAcl.ToString()); - _writer.Write("LimitedToStores", entity.LimitedToStores.ToString()); - _writer.Write("ProductTypeId", entity.ProductTypeId.ToString()); - _writer.Write("ParentGroupedProductId", entity.ParentGroupedProductId.ToString()); - _writer.Write("Sku", (string)product.Sku); - _writer.Write("ManufacturerPartNumber", (string)product.ManufacturerPartNumber); - _writer.Write("Gtin", (string)product.Gtin); - _writer.Write("IsGiftCard", entity.IsGiftCard.ToString()); - _writer.Write("GiftCardTypeId", entity.GiftCardTypeId.ToString()); - _writer.Write("RequireOtherProducts", entity.RequireOtherProducts.ToString()); - _writer.Write("RequiredProductIds", entity.RequiredProductIds); - _writer.Write("AutomaticallyAddRequiredProducts", entity.AutomaticallyAddRequiredProducts.ToString()); - _writer.Write("IsDownload", entity.IsDownload.ToString()); - _writer.Write("UnlimitedDownloads", entity.UnlimitedDownloads.ToString()); - _writer.Write("MaxNumberOfDownloads", entity.MaxNumberOfDownloads.ToString()); - _writer.Write("DownloadExpirationDays", entity.DownloadExpirationDays.HasValue ? entity.DownloadExpirationDays.Value.ToString() : ""); - _writer.Write("DownloadActivationTypeId", entity.DownloadActivationTypeId.ToString()); - _writer.Write("HasSampleDownload", entity.HasSampleDownload.ToString()); - _writer.Write("SampleDownloadId", entity.SampleDownloadId.HasValue ? entity.SampleDownloadId.Value.ToString() : ""); - _writer.Write("HasUserAgreement", entity.HasUserAgreement.ToString()); - _writer.Write("UserAgreementText", entity.UserAgreementText); - _writer.Write("IsRecurring", entity.IsRecurring.ToString()); - _writer.Write("RecurringCycleLength", entity.RecurringCycleLength.ToString()); - _writer.Write("RecurringCyclePeriodId", entity.RecurringCyclePeriodId.ToString()); - _writer.Write("RecurringTotalCycles", entity.RecurringTotalCycles.ToString()); - _writer.Write("IsShipEnabled", entity.IsShipEnabled.ToString()); - _writer.Write("IsFreeShipping", entity.IsFreeShipping.ToString()); - _writer.Write("AdditionalShippingCharge", entity.AdditionalShippingCharge.ToString(_culture)); - _writer.Write("IsTaxExempt", entity.IsTaxExempt.ToString()); - _writer.Write("TaxCategoryId", entity.TaxCategoryId.ToString()); - _writer.Write("ManageInventoryMethodId", entity.ManageInventoryMethodId.ToString()); - _writer.Write("StockQuantity", entity.StockQuantity.ToString()); - _writer.Write("DisplayStockAvailability", entity.DisplayStockAvailability.ToString()); - _writer.Write("DisplayStockQuantity", entity.DisplayStockQuantity.ToString()); - _writer.Write("MinStockQuantity", entity.MinStockQuantity.ToString()); - _writer.Write("LowStockActivityId", entity.LowStockActivityId.ToString()); - _writer.Write("NotifyAdminForQuantityBelow", entity.NotifyAdminForQuantityBelow.ToString()); - _writer.Write("BackorderModeId", entity.BackorderModeId.ToString()); - _writer.Write("AllowBackInStockSubscriptions", entity.AllowBackInStockSubscriptions.ToString()); - _writer.Write("OrderMinimumQuantity", entity.OrderMinimumQuantity.ToString()); - _writer.Write("OrderMaximumQuantity", entity.OrderMaximumQuantity.ToString()); - _writer.Write("QuantityStep", entity.QuantityStep.ToString()); - _writer.Write("QuantiyControlType", ((int)entity.QuantiyControlType).ToString()); - _writer.Write("HideQuantityControl", entity.HideQuantityControl.ToString()); + _writer.Write("DisplayLocale", entity.DisplayLocale); + _writer.Write("DisplayOrder", entity.DisplayOrder.ToString()); + _writer.Write("IsDefault", entity.IsDefault.ToString()); + + WriteLocalized(quantityUnit); + + if (node.HasValue()) + { + _writer.WriteEndElement(); + } + } + + public void WritePicture(dynamic picture, string node) + { + if (picture == null) + return; + + MediaFile entity = picture.Entity; + + if (node.HasValue()) + { + _writer.WriteStartElement(node); + } + + var seoName = (string)picture.Name; + seoName = Path.GetFileNameWithoutExtension(seoName.EmptyNull()); + + _writer.Write("Id", entity.Id.ToString()); + _writer.Write("SeoFilename", seoName); + _writer.Write("MimeType", (string)picture.MimeType); + _writer.Write("ThumbImageUrl", (string)picture._ThumbImageUrl); + _writer.Write("ImageUrl", (string)picture._ImageUrl); + _writer.Write("FullSizeImageUrl", (string)picture._FullSizeImageUrl); + _writer.Write("FileName", (string)picture._FileName); + + if (node.HasValue()) + { + _writer.WriteEndElement(); + } + } + + public void WriteCategory(dynamic category, string node) + { + if (category == null) + return; + + Category entity = category.Entity; + + if (node.HasValue()) + { + _writer.WriteStartElement(node); + } + + _writer.Write("Id", entity.Id.ToString()); + + if (!Exclude.HasFlag(ExportXmlExclude.Category)) + { + _writer.Write("Name", (string)category.Name); + _writer.Write("FullName", (string)category.FullName); + _writer.Write("Description", (string)category.Description); + _writer.Write("BottomDescription", (string)category.BottomDescription); + _writer.Write("CategoryTemplateId", entity.CategoryTemplateId.ToString()); + _writer.Write("CategoryTemplateViewPath", (string)category._CategoryTemplateViewPath); + _writer.Write("MetaKeywords", (string)category.MetaKeywords); + _writer.Write("MetaDescription", (string)category.MetaDescription); + _writer.Write("MetaTitle", (string)category.MetaTitle); + _writer.Write("SeName", (string)category.SeName); + _writer.Write("ParentCategoryId", entity.ParentCategoryId.ToString()); + _writer.Write("PictureId", entity.MediaFileId.ToString()); + _writer.Write("PageSize", entity.PageSize.ToString()); + _writer.Write("AllowCustomersToSelectPageSize", entity.AllowCustomersToSelectPageSize.ToString()); + _writer.Write("PageSizeOptions", entity.PageSizeOptions); + _writer.Write("ShowOnHomePage", entity.ShowOnHomePage.ToString()); + _writer.Write("HasDiscountsApplied", entity.HasDiscountsApplied.ToString()); + _writer.Write("Published", entity.Published.ToString()); + _writer.Write("Deleted", entity.Deleted.ToString()); + _writer.Write("DisplayOrder", entity.DisplayOrder.ToString()); + _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); + _writer.Write("UpdatedOnUtc", entity.UpdatedOnUtc.ToString(_culture)); + _writer.Write("SubjectToAcl", entity.SubjectToAcl.ToString()); + _writer.Write("LimitedToStores", entity.LimitedToStores.ToString()); + _writer.Write("Alias", (string)category.Alias); + _writer.Write("DefaultViewMode", entity.DefaultViewMode); + + WritePicture(category.Picture, "Picture"); + + WriteLocalized(category); + } + + if (node.HasValue()) + { + _writer.WriteEndElement(); + } + } + + public void WriteManufacturer(dynamic manufacturer, string node) + { + if (manufacturer == null) + return; + + Manufacturer entity = manufacturer.Entity; + + if (node.HasValue()) + { + _writer.WriteStartElement(node); + } + + _writer.Write("Id", entity.Id.ToString()); + _writer.Write("Name", (string)manufacturer.Name); + _writer.Write("SeName", (string)manufacturer.SeName); + _writer.Write("Description", (string)manufacturer.Description); + _writer.Write("BottomDescription", (string)manufacturer.BottomDescription); + _writer.Write("ManufacturerTemplateId", entity.ManufacturerTemplateId.ToString()); + _writer.Write("MetaKeywords", (string)manufacturer.MetaKeywords); + _writer.Write("MetaDescription", (string)manufacturer.MetaDescription); + _writer.Write("MetaTitle", (string)manufacturer.MetaTitle); + _writer.Write("PictureId", entity.MediaFileId.ToString()); + _writer.Write("PageSize", entity.PageSize.ToString()); + _writer.Write("AllowCustomersToSelectPageSize", entity.AllowCustomersToSelectPageSize.ToString()); + _writer.Write("PageSizeOptions", entity.PageSizeOptions); + _writer.Write("Published", entity.Published.ToString()); + _writer.Write("Deleted", entity.Deleted.ToString()); + _writer.Write("DisplayOrder", entity.DisplayOrder.ToString()); + _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); + _writer.Write("UpdatedOnUtc", entity.UpdatedOnUtc.ToString(_culture)); + _writer.Write("HasDiscountsApplied", entity.HasDiscountsApplied.ToString()); + _writer.Write("SubjectToAcl", entity.SubjectToAcl.ToString()); + _writer.Write("LimitedToStores", entity.LimitedToStores.ToString()); + + WritePicture(manufacturer.Picture, "Picture"); + + WriteLocalized(manufacturer); + + if (node.HasValue()) + { + _writer.WriteEndElement(); + } + } + + public void WriteProduct(dynamic product, string node) + { + if (product == null) + return; + + Product entity = product.Entity; + + if (node.HasValue()) + { + _writer.WriteStartElement(node); + } + + decimal? basePriceAmount = product.BasePriceAmount; + int? basePriceBaseAmount = product.BasePriceBaseAmount; + decimal? lowestAttributeCombinationPrice = product.LowestAttributeCombinationPrice; + + _writer.Write("Id", entity.Id.ToString()); + _writer.Write("Name", (string)product.Name); + _writer.Write("SeName", (string)product.SeName); + _writer.Write("ShortDescription", (string)product.ShortDescription); + _writer.Write("FullDescription", (string)product.FullDescription); + _writer.Write("AdminComment", (string)product.AdminComment); + _writer.Write("ProductTemplateId", entity.ProductTemplateId.ToString()); + _writer.Write("ProductTemplateViewPath", (string)product._ProductTemplateViewPath); + _writer.Write("ShowOnHomePage", entity.ShowOnHomePage.ToString()); + _writer.Write("HomePageDisplayOrder", entity.HomePageDisplayOrder.ToString()); + _writer.Write("MetaKeywords", (string)product.MetaKeywords); + _writer.Write("MetaDescription", (string)product.MetaDescription); + _writer.Write("MetaTitle", (string)product.MetaTitle); + _writer.Write("AllowCustomerReviews", entity.AllowCustomerReviews.ToString()); + _writer.Write("ApprovedRatingSum", entity.ApprovedRatingSum.ToString()); + _writer.Write("NotApprovedRatingSum", entity.NotApprovedRatingSum.ToString()); + _writer.Write("ApprovedTotalReviews", entity.ApprovedTotalReviews.ToString()); + _writer.Write("NotApprovedTotalReviews", entity.NotApprovedTotalReviews.ToString()); + _writer.Write("Published", entity.Published.ToString()); + _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); + _writer.Write("UpdatedOnUtc", entity.UpdatedOnUtc.ToString(_culture)); + _writer.Write("SubjectToAcl", entity.SubjectToAcl.ToString()); + _writer.Write("LimitedToStores", entity.LimitedToStores.ToString()); + _writer.Write("ProductTypeId", entity.ProductTypeId.ToString()); + _writer.Write("ParentGroupedProductId", entity.ParentGroupedProductId.ToString()); + _writer.Write("Sku", (string)product.Sku); + _writer.Write("ManufacturerPartNumber", (string)product.ManufacturerPartNumber); + _writer.Write("Gtin", (string)product.Gtin); + _writer.Write("IsGiftCard", entity.IsGiftCard.ToString()); + _writer.Write("GiftCardTypeId", entity.GiftCardTypeId.ToString()); + _writer.Write("RequireOtherProducts", entity.RequireOtherProducts.ToString()); + _writer.Write("RequiredProductIds", entity.RequiredProductIds); + _writer.Write("AutomaticallyAddRequiredProducts", entity.AutomaticallyAddRequiredProducts.ToString()); + _writer.Write("IsDownload", entity.IsDownload.ToString()); + _writer.Write("UnlimitedDownloads", entity.UnlimitedDownloads.ToString()); + _writer.Write("MaxNumberOfDownloads", entity.MaxNumberOfDownloads.ToString()); + _writer.Write("DownloadExpirationDays", entity.DownloadExpirationDays.HasValue ? entity.DownloadExpirationDays.Value.ToString() : ""); + _writer.Write("DownloadActivationTypeId", entity.DownloadActivationTypeId.ToString()); + _writer.Write("HasSampleDownload", entity.HasSampleDownload.ToString()); + _writer.Write("SampleDownloadId", entity.SampleDownloadId.HasValue ? entity.SampleDownloadId.Value.ToString() : ""); + _writer.Write("HasUserAgreement", entity.HasUserAgreement.ToString()); + _writer.Write("UserAgreementText", entity.UserAgreementText); + _writer.Write("IsRecurring", entity.IsRecurring.ToString()); + _writer.Write("RecurringCycleLength", entity.RecurringCycleLength.ToString()); + _writer.Write("RecurringCyclePeriodId", entity.RecurringCyclePeriodId.ToString()); + _writer.Write("RecurringTotalCycles", entity.RecurringTotalCycles.ToString()); + _writer.Write("IsShipEnabled", entity.IsShipEnabled.ToString()); + _writer.Write("IsFreeShipping", entity.IsFreeShipping.ToString()); + _writer.Write("AdditionalShippingCharge", entity.AdditionalShippingCharge.ToString(_culture)); + _writer.Write("IsTaxExempt", entity.IsTaxExempt.ToString()); + _writer.Write("TaxCategoryId", entity.TaxCategoryId.ToString()); + _writer.Write("ManageInventoryMethodId", entity.ManageInventoryMethodId.ToString()); + _writer.Write("StockQuantity", entity.StockQuantity.ToString()); + _writer.Write("DisplayStockAvailability", entity.DisplayStockAvailability.ToString()); + _writer.Write("DisplayStockQuantity", entity.DisplayStockQuantity.ToString()); + _writer.Write("MinStockQuantity", entity.MinStockQuantity.ToString()); + _writer.Write("LowStockActivityId", entity.LowStockActivityId.ToString()); + _writer.Write("NotifyAdminForQuantityBelow", entity.NotifyAdminForQuantityBelow.ToString()); + _writer.Write("BackorderModeId", entity.BackorderModeId.ToString()); + _writer.Write("AllowBackInStockSubscriptions", entity.AllowBackInStockSubscriptions.ToString()); + _writer.Write("OrderMinimumQuantity", entity.OrderMinimumQuantity.ToString()); + _writer.Write("OrderMaximumQuantity", entity.OrderMaximumQuantity.ToString()); + _writer.Write("QuantityStep", entity.QuantityStep.ToString()); + _writer.Write("QuantiyControlType", ((int)entity.QuantiyControlType).ToString()); + _writer.Write("HideQuantityControl", entity.HideQuantityControl.ToString()); _writer.Write("AllowedQuantities", entity.AllowedQuantities); - _writer.Write("DisableBuyButton", entity.DisableBuyButton.ToString()); - _writer.Write("DisableWishlistButton", entity.DisableWishlistButton.ToString()); - _writer.Write("AvailableForPreOrder", entity.AvailableForPreOrder.ToString()); - _writer.Write("CallForPrice", entity.CallForPrice.ToString()); - _writer.Write("Price", entity.Price.ToString(_culture)); - _writer.Write("OldPrice", entity.OldPrice.ToString(_culture)); - _writer.Write("ProductCost", entity.ProductCost.ToString(_culture)); - _writer.Write("SpecialPrice", entity.SpecialPrice.HasValue ? entity.SpecialPrice.Value.ToString(_culture) : ""); - _writer.Write("SpecialPriceStartDateTimeUtc", entity.SpecialPriceStartDateTimeUtc.HasValue ? entity.SpecialPriceStartDateTimeUtc.Value.ToString(_culture) : ""); - _writer.Write("SpecialPriceEndDateTimeUtc", entity.SpecialPriceEndDateTimeUtc.HasValue ? entity.SpecialPriceEndDateTimeUtc.Value.ToString(_culture) : ""); - _writer.Write("CustomerEntersPrice", entity.CustomerEntersPrice.ToString()); - _writer.Write("MinimumCustomerEnteredPrice", entity.MinimumCustomerEnteredPrice.ToString(_culture)); - _writer.Write("MaximumCustomerEnteredPrice", entity.MaximumCustomerEnteredPrice.ToString(_culture)); - _writer.Write("HasTierPrices", entity.HasTierPrices.ToString()); - _writer.Write("HasDiscountsApplied", entity.HasDiscountsApplied.ToString()); - _writer.Write("MainPictureId", entity.MainPictureId.HasValue ? entity.MainPictureId.Value.ToString() : ""); - _writer.Write("Weight", ((decimal)product.Weight).ToString(_culture)); - _writer.Write("Length", ((decimal)product.Length).ToString(_culture)); - _writer.Write("Width", ((decimal)product.Width).ToString(_culture)); - _writer.Write("Height", ((decimal)product.Height).ToString(_culture)); - _writer.Write("AvailableStartDateTimeUtc", entity.AvailableStartDateTimeUtc.HasValue ? entity.AvailableStartDateTimeUtc.Value.ToString(_culture) : ""); - _writer.Write("AvailableEndDateTimeUtc", entity.AvailableEndDateTimeUtc.HasValue ? entity.AvailableEndDateTimeUtc.Value.ToString(_culture) : ""); - _writer.Write("BasePriceEnabled", ((bool)product.BasePriceEnabled).ToString()); - _writer.Write("BasePriceMeasureUnit", (string)product.BasePriceMeasureUnit); - _writer.Write("BasePriceAmount", basePriceAmount.HasValue ? basePriceAmount.Value.ToString(_culture) : ""); - _writer.Write("BasePriceBaseAmount", basePriceBaseAmount.HasValue ? basePriceBaseAmount.Value.ToString() : ""); - _writer.Write("BasePriceHasValue", ((bool)product.BasePriceHasValue).ToString()); - _writer.Write("BasePriceInfo", (string)product._BasePriceInfo); - _writer.Write("VisibleIndividually", entity.VisibleIndividually.ToString()); - _writer.Write("DisplayOrder", entity.DisplayOrder.ToString()); - _writer.Write("IsSystemProduct", entity.IsSystemProduct.ToString()); - _writer.Write("BundleTitleText", entity.BundleTitleText); - _writer.Write("BundlePerItemPricing", entity.BundlePerItemPricing.ToString()); - _writer.Write("BundlePerItemShipping", entity.BundlePerItemShipping.ToString()); - _writer.Write("BundlePerItemShoppingCart", entity.BundlePerItemShoppingCart.ToString()); - _writer.Write("LowestAttributeCombinationPrice", lowestAttributeCombinationPrice.HasValue ? lowestAttributeCombinationPrice.Value.ToString(_culture) : ""); - _writer.Write("IsEsd", entity.IsEsd.ToString()); - _writer.Write("CustomsTariffNumber", entity.CustomsTariffNumber); - - WriteLocalized(product); - - WriteDeliveryTime(product.DeliveryTime, "DeliveryTime"); - - WriteQuantityUnit(product.QuantityUnit, "QuantityUnit"); - - WriteCountry(product.CountryOfOrigin, "CountryOfOrigin"); - - if (product.AppliedDiscounts != null) - { - _writer.WriteStartElement("AppliedDiscounts"); - foreach (dynamic discount in product.AppliedDiscounts) - { - Discount entityDiscount = discount.Entity; - - _writer.WriteStartElement("AppliedDiscount"); - _writer.Write("Id", entityDiscount.Id.ToString()); - _writer.Write("Name", (string)discount.Name); - _writer.Write("DiscountTypeId", entityDiscount.DiscountTypeId.ToString()); - _writer.Write("UsePercentage", entityDiscount.UsePercentage.ToString()); - _writer.Write("DiscountPercentage", entityDiscount.DiscountPercentage.ToString(_culture)); - _writer.Write("DiscountAmount", entityDiscount.DiscountAmount.ToString(_culture)); - _writer.Write("StartDateUtc", entityDiscount.StartDateUtc.HasValue ? entityDiscount.StartDateUtc.Value.ToString(_culture) : ""); - _writer.Write("EndDateUtc", entityDiscount.EndDateUtc.HasValue ? entityDiscount.EndDateUtc.Value.ToString(_culture) : ""); - _writer.Write("RequiresCouponCode", entityDiscount.RequiresCouponCode.ToString()); - _writer.Write("CouponCode", entityDiscount.CouponCode); - _writer.Write("DiscountLimitationId", entityDiscount.DiscountLimitationId.ToString()); - _writer.Write("LimitationTimes", entityDiscount.LimitationTimes.ToString()); - _writer.WriteEndElement(); // AppliedDiscount - } - _writer.WriteEndElement(); // AppliedDiscounts - } + _writer.Write("DisableBuyButton", entity.DisableBuyButton.ToString()); + _writer.Write("DisableWishlistButton", entity.DisableWishlistButton.ToString()); + _writer.Write("AvailableForPreOrder", entity.AvailableForPreOrder.ToString()); + _writer.Write("CallForPrice", entity.CallForPrice.ToString()); + _writer.Write("Price", entity.Price.ToString(_culture)); + _writer.Write("OldPrice", entity.OldPrice.ToString(_culture)); + _writer.Write("ProductCost", entity.ProductCost.ToString(_culture)); + _writer.Write("SpecialPrice", entity.SpecialPrice.HasValue ? entity.SpecialPrice.Value.ToString(_culture) : ""); + _writer.Write("SpecialPriceStartDateTimeUtc", entity.SpecialPriceStartDateTimeUtc.HasValue ? entity.SpecialPriceStartDateTimeUtc.Value.ToString(_culture) : ""); + _writer.Write("SpecialPriceEndDateTimeUtc", entity.SpecialPriceEndDateTimeUtc.HasValue ? entity.SpecialPriceEndDateTimeUtc.Value.ToString(_culture) : ""); + _writer.Write("CustomerEntersPrice", entity.CustomerEntersPrice.ToString()); + _writer.Write("MinimumCustomerEnteredPrice", entity.MinimumCustomerEnteredPrice.ToString(_culture)); + _writer.Write("MaximumCustomerEnteredPrice", entity.MaximumCustomerEnteredPrice.ToString(_culture)); + _writer.Write("HasTierPrices", entity.HasTierPrices.ToString()); + _writer.Write("HasDiscountsApplied", entity.HasDiscountsApplied.ToString()); + _writer.Write("MainPictureId", entity.MainPictureId.HasValue ? entity.MainPictureId.Value.ToString() : ""); + _writer.Write("Weight", ((decimal)product.Weight).ToString(_culture)); + _writer.Write("Length", ((decimal)product.Length).ToString(_culture)); + _writer.Write("Width", ((decimal)product.Width).ToString(_culture)); + _writer.Write("Height", ((decimal)product.Height).ToString(_culture)); + _writer.Write("AvailableStartDateTimeUtc", entity.AvailableStartDateTimeUtc.HasValue ? entity.AvailableStartDateTimeUtc.Value.ToString(_culture) : ""); + _writer.Write("AvailableEndDateTimeUtc", entity.AvailableEndDateTimeUtc.HasValue ? entity.AvailableEndDateTimeUtc.Value.ToString(_culture) : ""); + _writer.Write("BasePriceEnabled", ((bool)product.BasePriceEnabled).ToString()); + _writer.Write("BasePriceMeasureUnit", (string)product.BasePriceMeasureUnit); + _writer.Write("BasePriceAmount", basePriceAmount.HasValue ? basePriceAmount.Value.ToString(_culture) : ""); + _writer.Write("BasePriceBaseAmount", basePriceBaseAmount.HasValue ? basePriceBaseAmount.Value.ToString() : ""); + _writer.Write("BasePriceHasValue", ((bool)product.BasePriceHasValue).ToString()); + _writer.Write("BasePriceInfo", (string)product._BasePriceInfo); + _writer.Write("Visibility", ((int)entity.Visibility).ToString()); + _writer.Write("Condition", ((int)entity.Condition).ToString()); + _writer.Write("DisplayOrder", entity.DisplayOrder.ToString()); + _writer.Write("IsSystemProduct", entity.IsSystemProduct.ToString()); + _writer.Write("BundleTitleText", entity.BundleTitleText); + _writer.Write("BundlePerItemPricing", entity.BundlePerItemPricing.ToString()); + _writer.Write("BundlePerItemShipping", entity.BundlePerItemShipping.ToString()); + _writer.Write("BundlePerItemShoppingCart", entity.BundlePerItemShoppingCart.ToString()); + _writer.Write("LowestAttributeCombinationPrice", lowestAttributeCombinationPrice.HasValue ? lowestAttributeCombinationPrice.Value.ToString(_culture) : ""); + _writer.Write("AttributeChoiceBehaviour", ((int)entity.AttributeChoiceBehaviour).ToString()); + _writer.Write("IsEsd", entity.IsEsd.ToString()); + _writer.Write("CustomsTariffNumber", entity.CustomsTariffNumber); + + WriteLocalized(product); + + WriteDeliveryTime(product.DeliveryTime, "DeliveryTime"); + + WriteQuantityUnit(product.QuantityUnit, "QuantityUnit"); + + WriteCountry(product.CountryOfOrigin, "CountryOfOrigin"); + + if (product.AppliedDiscounts != null) + { + _writer.WriteStartElement("AppliedDiscounts"); + foreach (dynamic discount in product.AppliedDiscounts) + { + Discount entityDiscount = discount.Entity; + + _writer.WriteStartElement("AppliedDiscount"); + _writer.Write("Id", entityDiscount.Id.ToString()); + _writer.Write("Name", (string)discount.Name); + _writer.Write("DiscountTypeId", entityDiscount.DiscountTypeId.ToString()); + _writer.Write("UsePercentage", entityDiscount.UsePercentage.ToString()); + _writer.Write("DiscountPercentage", entityDiscount.DiscountPercentage.ToString(_culture)); + _writer.Write("DiscountAmount", entityDiscount.DiscountAmount.ToString(_culture)); + _writer.Write("StartDateUtc", entityDiscount.StartDateUtc.HasValue ? entityDiscount.StartDateUtc.Value.ToString(_culture) : ""); + _writer.Write("EndDateUtc", entityDiscount.EndDateUtc.HasValue ? entityDiscount.EndDateUtc.Value.ToString(_culture) : ""); + _writer.Write("RequiresCouponCode", entityDiscount.RequiresCouponCode.ToString()); + _writer.Write("CouponCode", entityDiscount.CouponCode); + _writer.Write("DiscountLimitationId", entityDiscount.DiscountLimitationId.ToString()); + _writer.Write("LimitationTimes", entityDiscount.LimitationTimes.ToString()); + _writer.WriteEndElement(); // AppliedDiscount + } + _writer.WriteEndElement(); // AppliedDiscounts + } if (product.Downloads != null) { @@ -613,86 +617,92 @@ public void WriteProduct(dynamic product, string node) foreach (dynamic download in product.Downloads) { Download downloadEntity = download.Entity; + var mediaFile = downloadEntity.MediaFile; _writer.WriteStartElement("Download"); _writer.Write("Id", downloadEntity.Id.ToString()); _writer.Write("DownloadGuid", downloadEntity.DownloadGuid.ToString()); _writer.Write("UseDownloadUrl", downloadEntity.UseDownloadUrl.ToString()); _writer.Write("DownloadUrl", downloadEntity.DownloadUrl); - _writer.Write("ContentType", downloadEntity.ContentType); - _writer.Write("Filename", downloadEntity.Filename); - _writer.Write("Extension", downloadEntity.Extension); - _writer.Write("IsNew", downloadEntity.IsNew.ToString()); _writer.Write("IsTransient", downloadEntity.IsTransient.ToString()); _writer.Write("UpdatedOnUtc", downloadEntity.UpdatedOnUtc.ToString(_culture)); - _writer.Write("MediaStorageId", downloadEntity.MediaStorageId.HasValue ? downloadEntity.MediaStorageId.Value.ToString() : ""); _writer.Write("EntityId", downloadEntity.EntityId.ToString()); _writer.Write("EntityName", downloadEntity.EntityName); _writer.Write("FileVersion", downloadEntity.FileVersion); _writer.Write("Changelog", downloadEntity.Changelog); + if (!downloadEntity.UseDownloadUrl && mediaFile != null) + { + _writer.Write("ContentType", mediaFile.MimeType); + _writer.Write("Filename", mediaFile.Name); + _writer.Write("Extension", mediaFile.Extension); + _writer.Write("MediaStorageId", mediaFile.MediaStorageId.HasValue ? mediaFile.MediaStorageId.Value.ToString() : ""); + } _writer.WriteEndElement(); // Download } _writer.WriteEndElement(); // Downloads } - if (product.TierPrices != null) - { - _writer.WriteStartElement("TierPrices"); - foreach (dynamic tierPrice in product.TierPrices) - { - TierPrice entityTierPrice = tierPrice.Entity; - - _writer.WriteStartElement("TierPrice"); - _writer.Write("Id", entityTierPrice.Id.ToString()); - _writer.Write("ProductId", entityTierPrice.ProductId.ToString()); - _writer.Write("StoreId", entityTierPrice.StoreId.ToString()); - _writer.Write("CustomerRoleId", entityTierPrice.CustomerRoleId.HasValue ? entityTierPrice.CustomerRoleId.Value.ToString() : ""); - _writer.Write("Quantity", entityTierPrice.Quantity.ToString()); - _writer.Write("Price", entityTierPrice.Price.ToString(_culture)); + if (product.TierPrices != null) + { + _writer.WriteStartElement("TierPrices"); + foreach (dynamic tierPrice in product.TierPrices) + { + TierPrice entityTierPrice = tierPrice.Entity; + + _writer.WriteStartElement("TierPrice"); + _writer.Write("Id", entityTierPrice.Id.ToString()); + _writer.Write("ProductId", entityTierPrice.ProductId.ToString()); + _writer.Write("StoreId", entityTierPrice.StoreId.ToString()); + _writer.Write("CustomerRoleId", entityTierPrice.CustomerRoleId.HasValue ? entityTierPrice.CustomerRoleId.Value.ToString() : ""); + _writer.Write("Quantity", entityTierPrice.Quantity.ToString()); + _writer.Write("Price", entityTierPrice.Price.ToString(_culture)); _writer.Write("CalculationMethod", ((int)entityTierPrice.CalculationMethod).ToString()); - _writer.WriteEndElement(); // TierPrice - } - _writer.WriteEndElement(); // TierPrices - } - - if (product.ProductTags != null) - { - _writer.WriteStartElement("ProductTags"); - foreach (dynamic tag in product.ProductTags) - { - _writer.WriteStartElement("ProductTag"); - _writer.Write("Id", ((int)tag.Id).ToString()); - _writer.Write("Name", (string)tag.Name); - _writer.Write("SeName", (string)tag.SeName); - - WriteLocalized(tag); - - _writer.WriteEndElement(); // ProductTag - } - _writer.WriteEndElement(); // ProductTags - } - - if (product.ProductAttributes != null) - { - _writer.WriteStartElement("ProductAttributes"); - foreach (dynamic pva in product.ProductAttributes) - { - ProductVariantAttribute entityPva = pva.Entity; + _writer.WriteEndElement(); // TierPrice + } + _writer.WriteEndElement(); // TierPrices + } + + if (product.ProductTags != null) + { + _writer.WriteStartElement("ProductTags"); + foreach (dynamic tag in product.ProductTags) + { + ProductTag entityTag = tag.Entity; + + _writer.WriteStartElement("ProductTag"); + _writer.Write("Id", ((int)tag.Id).ToString()); + _writer.Write("Name", (string)tag.Name); + _writer.Write("SeName", (string)tag.SeName); + _writer.Write("Published", entityTag.Published.ToString()); + + WriteLocalized(tag); + + _writer.WriteEndElement(); // ProductTag + } + _writer.WriteEndElement(); // ProductTags + } + + if (product.ProductAttributes != null) + { + _writer.WriteStartElement("ProductAttributes"); + foreach (dynamic pva in product.ProductAttributes) + { + ProductVariantAttribute entityPva = pva.Entity; ProductAttribute entityPa = pva.Attribute.Entity; _writer.WriteStartElement("ProductAttribute"); - _writer.Write("Id", entityPva.Id.ToString()); - _writer.Write("TextPrompt", (string)pva.TextPrompt); - _writer.Write("IsRequired", entityPva.IsRequired.ToString()); - _writer.Write("AttributeControlTypeId", entityPva.AttributeControlTypeId.ToString()); - _writer.Write("DisplayOrder", entityPva.DisplayOrder.ToString()); - - _writer.WriteStartElement("Attribute"); - _writer.Write("Id", entityPa.Id.ToString()); - _writer.Write("Alias", entityPa.Alias); - _writer.Write("Name", entityPa.Name); - _writer.Write("Description", entityPa.Description); + _writer.Write("Id", entityPva.Id.ToString()); + _writer.Write("TextPrompt", (string)pva.TextPrompt); + _writer.Write("IsRequired", entityPva.IsRequired.ToString()); + _writer.Write("AttributeControlTypeId", entityPva.AttributeControlTypeId.ToString()); + _writer.Write("DisplayOrder", entityPva.DisplayOrder.ToString()); + + _writer.WriteStartElement("Attribute"); + _writer.Write("Id", entityPa.Id.ToString()); + _writer.Write("Alias", entityPa.Alias); + _writer.Write("Name", entityPa.Name); + _writer.Write("Description", entityPa.Description); _writer.Write("AllowFiltering", entityPa.AllowFiltering.ToString()); _writer.Write("DisplayOrder", entityPa.DisplayOrder.ToString()); _writer.Write("FacetTemplateHint", ((int)entityPa.FacetTemplateHint).ToString()); @@ -700,249 +710,251 @@ public void WriteProduct(dynamic product, string node) WriteLocalized(pva.Attribute); - _writer.WriteEndElement(); // Attribute - - _writer.WriteStartElement("AttributeValues"); - foreach (dynamic value in pva.Attribute.Values) - { - ProductVariantAttributeValue entityPvav = value.Entity; - - _writer.WriteStartElement("AttributeValue"); - _writer.Write("Id", entityPvav.Id.ToString()); - _writer.Write("Alias", (string)value.Alias); - _writer.Write("Name", (string)value.Name); - _writer.Write("Color", (string)value.Color); - _writer.Write("PriceAdjustment", ((decimal)value.PriceAdjustment).ToString(_culture)); - _writer.Write("WeightAdjustment", ((decimal)value.WeightAdjustment).ToString(_culture)); - _writer.Write("IsPreSelected", entityPvav.IsPreSelected.ToString()); - _writer.Write("DisplayOrder", entityPvav.DisplayOrder.ToString()); - _writer.Write("ValueTypeId", entityPvav.ValueTypeId.ToString()); - _writer.Write("LinkedProductId", entityPvav.LinkedProductId.ToString()); - _writer.Write("Quantity", entityPvav.Quantity.ToString()); - - WriteLocalized(value); - - _writer.WriteEndElement(); // AttributeValue - } - _writer.WriteEndElement(); // AttributeValues - - _writer.WriteEndElement(); // ProductAttribute - } - _writer.WriteEndElement(); // ProductAttributes - } - - if (product.ProductAttributeCombinations != null) - { - _writer.WriteStartElement("ProductAttributeCombinations"); - foreach (dynamic combination in product.ProductAttributeCombinations) - { - ProductVariantAttributeCombination entityPvac = combination.Entity; - - _writer.WriteStartElement("ProductAttributeCombination"); - _writer.Write("Id", entityPvac.Id.ToString()); - _writer.Write("StockQuantity", entityPvac.StockQuantity.ToString()); - _writer.Write("AllowOutOfStockOrders", entityPvac.AllowOutOfStockOrders.ToString()); - _writer.Write("AttributesXml", entityPvac.AttributesXml); - _writer.Write("Sku", entityPvac.Sku); - _writer.Write("Gtin", entityPvac.Gtin); - _writer.Write("ManufacturerPartNumber", entityPvac.ManufacturerPartNumber); - _writer.Write("Price", entityPvac.Price.HasValue ? entityPvac.Price.Value.ToString(_culture) : ""); - _writer.Write("Length", entityPvac.Length.HasValue ? entityPvac.Length.Value.ToString(_culture) : ""); - _writer.Write("Width", entityPvac.Width.HasValue ? entityPvac.Width.Value.ToString(_culture) : ""); - _writer.Write("Height", entityPvac.Height.HasValue ? entityPvac.Height.Value.ToString(_culture) : ""); - _writer.Write("BasePriceAmount", entityPvac.BasePriceAmount.HasValue ? entityPvac.BasePriceAmount.Value.ToString(_culture) : ""); - _writer.Write("BasePriceBaseAmount", entityPvac.BasePriceBaseAmount.HasValue ? entityPvac.BasePriceBaseAmount.Value.ToString() : ""); - _writer.Write("AssignedPictureIds", entityPvac.AssignedPictureIds); - _writer.Write("IsActive", entityPvac.IsActive.ToString()); - - WriteDeliveryTime(combination.DeliveryTime, "DeliveryTime"); - - WriteQuantityUnit(combination.QuantityUnit, "QuantityUnit"); - - _writer.WriteStartElement("Pictures"); - foreach (dynamic assignedPicture in combination.Pictures) - { - WritePicture(assignedPicture, "Picture"); - } - _writer.WriteEndElement(); // Pictures - - _writer.WriteEndElement(); // ProductAttributeCombination - } - _writer.WriteEndElement(); // ProductAttributeCombinations - } - - if (product.ProductPictures != null) - { - _writer.WriteStartElement("ProductPictures"); - foreach (dynamic productPicture in product.ProductPictures) - { - ProductPicture entityProductPicture = productPicture.Entity; - - _writer.WriteStartElement("ProductPicture"); - _writer.Write("Id", entityProductPicture.Id.ToString()); - _writer.Write("DisplayOrder", entityProductPicture.DisplayOrder.ToString()); - - WritePicture(productPicture.Picture, "Picture"); - - _writer.WriteEndElement(); // ProductPicture - } - _writer.WriteEndElement(); // ProductPictures - } - - if (product.ProductCategories != null) - { - _writer.WriteStartElement("ProductCategories"); - foreach (dynamic productCategory in product.ProductCategories) - { - ProductCategory entityProductCategory = productCategory.Entity; - - _writer.WriteStartElement("ProductCategory"); - _writer.Write("Id", entityProductCategory.Id.ToString()); - _writer.Write("DisplayOrder", entityProductCategory.DisplayOrder.ToString()); - _writer.Write("IsFeaturedProduct", entityProductCategory.IsFeaturedProduct.ToString()); - - WriteCategory(productCategory.Category, "Category"); - - _writer.WriteEndElement(); // ProductCategory - } - _writer.WriteEndElement(); // ProductCategories - } - - if (product.ProductManufacturers != null) - { - _writer.WriteStartElement("ProductManufacturers"); - foreach (dynamic productManu in product.ProductManufacturers) - { - ProductManufacturer entityProductManu = productManu.Entity; - - _writer.WriteStartElement("ProductManufacturer"); - - _writer.Write("Id", entityProductManu.Id.ToString()); - _writer.Write("DisplayOrder", entityProductManu.DisplayOrder.ToString()); - _writer.Write("IsFeaturedProduct", entityProductManu.IsFeaturedProduct.ToString()); - - WriteManufacturer(productManu.Manufacturer, "Manufacturer"); - - _writer.WriteEndElement(); // ProductManufacturer - } - _writer.WriteEndElement(); // ProductManufacturers - } - - if (product.ProductSpecificationAttributes != null) - { - _writer.WriteStartElement("ProductSpecificationAttributes"); - foreach (dynamic psa in product.ProductSpecificationAttributes) - { - ProductSpecificationAttribute entityPsa = psa.Entity; - - _writer.WriteStartElement("ProductSpecificationAttribute"); - - _writer.Write("Id", entityPsa.Id.ToString()); - _writer.Write("ProductId", entityPsa.ProductId.ToString()); - _writer.Write("SpecificationAttributeOptionId", entityPsa.SpecificationAttributeOptionId.ToString()); - _writer.Write("AllowFiltering", entityPsa.AllowFiltering.ToString()); - _writer.Write("ShowOnProductPage", entityPsa.ShowOnProductPage.ToString()); - _writer.Write("DisplayOrder", entityPsa.DisplayOrder.ToString()); - - dynamic option = psa.SpecificationAttributeOption; - SpecificationAttributeOption entitySao = option.Entity; - SpecificationAttribute entitySa = option.SpecificationAttribute.Entity; - - _writer.WriteStartElement("SpecificationAttributeOption"); - _writer.Write("Id", entitySao.Id.ToString()); - _writer.Write("SpecificationAttributeId", entitySao.SpecificationAttributeId.ToString()); - _writer.Write("DisplayOrder", entitySao.DisplayOrder.ToString()); - _writer.Write("Name", (string)option.Name); - _writer.Write("Alias", (string)option.Alias); - - WriteLocalized(option); - - _writer.WriteStartElement("SpecificationAttribute"); - _writer.Write("Id", entitySa.Id.ToString()); - _writer.Write("Name", (string)option.SpecificationAttribute.Name); - _writer.Write("Alias", (string)option.SpecificationAttribute.Alias); - _writer.Write("DisplayOrder", entitySa.DisplayOrder.ToString()); - _writer.Write("AllowFiltering", entitySa.AllowFiltering.ToString()); - _writer.Write("ShowOnProductPage", entitySa.ShowOnProductPage.ToString()); - _writer.Write("FacetSorting", ((int)entitySa.FacetSorting).ToString()); - _writer.Write("FacetTemplateHint", ((int)entitySa.FacetTemplateHint).ToString()); + _writer.WriteEndElement(); // Attribute + + _writer.WriteStartElement("AttributeValues"); + foreach (dynamic value in pva.Attribute.Values) + { + ProductVariantAttributeValue entityPvav = value.Entity; + + _writer.WriteStartElement("AttributeValue"); + _writer.Write("Id", entityPvav.Id.ToString()); + _writer.Write("Alias", (string)value.Alias); + _writer.Write("Name", (string)value.Name); + _writer.Write("Color", (string)value.Color); + _writer.Write("PriceAdjustment", ((decimal)value.PriceAdjustment).ToString(_culture)); + _writer.Write("WeightAdjustment", ((decimal)value.WeightAdjustment).ToString(_culture)); + _writer.Write("IsPreSelected", entityPvav.IsPreSelected.ToString()); + _writer.Write("DisplayOrder", entityPvav.DisplayOrder.ToString()); + _writer.Write("ValueTypeId", entityPvav.ValueTypeId.ToString()); + _writer.Write("LinkedProductId", entityPvav.LinkedProductId.ToString()); + _writer.Write("Quantity", entityPvav.Quantity.ToString()); + + WriteLocalized(value); + + _writer.WriteEndElement(); // AttributeValue + } + _writer.WriteEndElement(); // AttributeValues + + _writer.WriteEndElement(); // ProductAttribute + } + _writer.WriteEndElement(); // ProductAttributes + } + + if (product.ProductAttributeCombinations != null) + { + _writer.WriteStartElement("ProductAttributeCombinations"); + foreach (dynamic combination in product.ProductAttributeCombinations) + { + ProductVariantAttributeCombination entityPvac = combination.Entity; + + _writer.WriteStartElement("ProductAttributeCombination"); + _writer.Write("Id", entityPvac.Id.ToString()); + _writer.Write("StockQuantity", entityPvac.StockQuantity.ToString()); + _writer.Write("AllowOutOfStockOrders", entityPvac.AllowOutOfStockOrders.ToString()); + _writer.Write("AttributesXml", entityPvac.AttributesXml); + _writer.Write("Sku", entityPvac.Sku); + _writer.Write("Gtin", entityPvac.Gtin); + _writer.Write("ManufacturerPartNumber", entityPvac.ManufacturerPartNumber); + _writer.Write("Price", entityPvac.Price.HasValue ? entityPvac.Price.Value.ToString(_culture) : ""); + _writer.Write("Length", entityPvac.Length.HasValue ? entityPvac.Length.Value.ToString(_culture) : ""); + _writer.Write("Width", entityPvac.Width.HasValue ? entityPvac.Width.Value.ToString(_culture) : ""); + _writer.Write("Height", entityPvac.Height.HasValue ? entityPvac.Height.Value.ToString(_culture) : ""); + _writer.Write("BasePriceAmount", entityPvac.BasePriceAmount.HasValue ? entityPvac.BasePriceAmount.Value.ToString(_culture) : ""); + _writer.Write("BasePriceBaseAmount", entityPvac.BasePriceBaseAmount.HasValue ? entityPvac.BasePriceBaseAmount.Value.ToString() : ""); + _writer.Write("AssignedPictureIds", entityPvac.AssignedMediaFileIds); + _writer.Write("IsActive", entityPvac.IsActive.ToString()); + + WriteDeliveryTime(combination.DeliveryTime, "DeliveryTime"); + + WriteQuantityUnit(combination.QuantityUnit, "QuantityUnit"); + + _writer.WriteStartElement("Pictures"); + foreach (dynamic assignedPicture in combination.Pictures) + { + WritePicture(assignedPicture, "Picture"); + } + _writer.WriteEndElement(); // Pictures + + _writer.WriteEndElement(); // ProductAttributeCombination + } + _writer.WriteEndElement(); // ProductAttributeCombinations + } + + if (product.ProductPictures != null) + { + _writer.WriteStartElement("ProductPictures"); + foreach (dynamic productPicture in product.ProductPictures) + { + ProductMediaFile entityProductPicture = productPicture.Entity; + + _writer.WriteStartElement("ProductPicture"); + _writer.Write("Id", entityProductPicture.Id.ToString()); + _writer.Write("DisplayOrder", entityProductPicture.DisplayOrder.ToString()); + + WritePicture(productPicture.Picture, "Picture"); + + _writer.WriteEndElement(); // ProductPicture + } + _writer.WriteEndElement(); // ProductPictures + } + + if (product.ProductCategories != null) + { + _writer.WriteStartElement("ProductCategories"); + foreach (dynamic productCategory in product.ProductCategories) + { + ProductCategory entityProductCategory = productCategory.Entity; + + _writer.WriteStartElement("ProductCategory"); + _writer.Write("Id", entityProductCategory.Id.ToString()); + _writer.Write("DisplayOrder", entityProductCategory.DisplayOrder.ToString()); + _writer.Write("IsFeaturedProduct", entityProductCategory.IsFeaturedProduct.ToString()); + + WriteCategory(productCategory.Category, "Category"); + + _writer.WriteEndElement(); // ProductCategory + } + _writer.WriteEndElement(); // ProductCategories + } + + if (product.ProductManufacturers != null) + { + _writer.WriteStartElement("ProductManufacturers"); + foreach (dynamic productManu in product.ProductManufacturers) + { + ProductManufacturer entityProductManu = productManu.Entity; + + _writer.WriteStartElement("ProductManufacturer"); + + _writer.Write("Id", entityProductManu.Id.ToString()); + _writer.Write("DisplayOrder", entityProductManu.DisplayOrder.ToString()); + _writer.Write("IsFeaturedProduct", entityProductManu.IsFeaturedProduct.ToString()); + + WriteManufacturer(productManu.Manufacturer, "Manufacturer"); + + _writer.WriteEndElement(); // ProductManufacturer + } + _writer.WriteEndElement(); // ProductManufacturers + } + + if (product.ProductSpecificationAttributes != null) + { + _writer.WriteStartElement("ProductSpecificationAttributes"); + foreach (dynamic psa in product.ProductSpecificationAttributes) + { + ProductSpecificationAttribute entityPsa = psa.Entity; + + _writer.WriteStartElement("ProductSpecificationAttribute"); + + _writer.Write("Id", entityPsa.Id.ToString()); + _writer.Write("ProductId", entityPsa.ProductId.ToString()); + _writer.Write("SpecificationAttributeOptionId", entityPsa.SpecificationAttributeOptionId.ToString()); + _writer.Write("AllowFiltering", entityPsa.AllowFiltering.ToString()); + _writer.Write("ShowOnProductPage", entityPsa.ShowOnProductPage.ToString()); + _writer.Write("DisplayOrder", entityPsa.DisplayOrder.ToString()); + + dynamic option = psa.SpecificationAttributeOption; + SpecificationAttributeOption entitySao = option.Entity; + SpecificationAttribute entitySa = option.SpecificationAttribute.Entity; + + _writer.WriteStartElement("SpecificationAttributeOption"); + _writer.Write("Id", entitySao.Id.ToString()); + _writer.Write("SpecificationAttributeId", entitySao.SpecificationAttributeId.ToString()); + _writer.Write("DisplayOrder", entitySao.DisplayOrder.ToString()); + _writer.Write("NumberValue", ((decimal)option.NumberValue).ToString(_culture)); + _writer.Write("Color", (string)option.Color); + _writer.Write("Name", (string)option.Name); + _writer.Write("Alias", (string)option.Alias); + + WriteLocalized(option); + + _writer.WriteStartElement("SpecificationAttribute"); + _writer.Write("Id", entitySa.Id.ToString()); + _writer.Write("Name", (string)option.SpecificationAttribute.Name); + _writer.Write("Alias", (string)option.SpecificationAttribute.Alias); + _writer.Write("DisplayOrder", entitySa.DisplayOrder.ToString()); + _writer.Write("AllowFiltering", entitySa.AllowFiltering.ToString()); + _writer.Write("ShowOnProductPage", entitySa.ShowOnProductPage.ToString()); + _writer.Write("FacetSorting", ((int)entitySa.FacetSorting).ToString()); + _writer.Write("FacetTemplateHint", ((int)entitySa.FacetTemplateHint).ToString()); _writer.Write("IndexOptionNames", entitySa.IndexOptionNames.ToString()); - WriteLocalized(option.SpecificationAttribute); - - _writer.WriteEndElement(); // SpecificationAttribute - _writer.WriteEndElement(); // SpecificationAttributeOption - - _writer.WriteEndElement(); // ProductSpecificationAttribute - } - _writer.WriteEndElement(); // ProductSpecificationAttributes - } - - if (product.ProductBundleItems != null) - { - _writer.WriteStartElement("ProductBundleItems"); - foreach (dynamic bundleItem in product.ProductBundleItems) - { - ProductBundleItem entityPbi = bundleItem.Entity; - - _writer.WriteStartElement("ProductBundleItem"); - _writer.Write("Id", entityPbi.Id.ToString()); - _writer.Write("ProductId", entityPbi.ProductId.ToString()); - _writer.Write("BundleProductId", entityPbi.BundleProductId.ToString()); - _writer.Write("Quantity", entityPbi.Quantity.ToString()); - _writer.Write("Discount", entityPbi.Discount.HasValue ? entityPbi.Discount.Value.ToString(_culture) : ""); - _writer.Write("DiscountPercentage", entityPbi.DiscountPercentage.ToString()); - _writer.Write("Name", (string)bundleItem.Name); - _writer.Write("ShortDescription", (string)bundleItem.ShortDescription); - _writer.Write("FilterAttributes", entityPbi.FilterAttributes.ToString()); - _writer.Write("HideThumbnail", entityPbi.HideThumbnail.ToString()); - _writer.Write("Visible", entityPbi.Visible.ToString()); - _writer.Write("Published", entityPbi.Published.ToString()); - _writer.Write("DisplayOrder", ((int)bundleItem.DisplayOrder).ToString()); - _writer.Write("CreatedOnUtc", entityPbi.CreatedOnUtc.ToString(_culture)); - _writer.Write("UpdatedOnUtc", entityPbi.UpdatedOnUtc.ToString(_culture)); - - WriteLocalized(bundleItem); - - _writer.WriteEndElement(); // ProductBundleItem - } - _writer.WriteEndElement(); // ProductBundleItems - } - - if (node.HasValue()) - { - _writer.WriteEndElement(); - } - } - - public void WriteCustomer(dynamic customer, string node) - { - if (customer == null) - return; - - Customer entity = customer.Entity; - - if (node.HasValue()) - { - _writer.WriteStartElement(node); - } - - _writer.Write("Id", entity.Id.ToString()); - _writer.Write("CustomerGuid", entity.CustomerGuid.ToString()); - _writer.Write("Username", entity.Username); - _writer.Write("Email", entity.Email); - _writer.Write("AdminComment", entity.AdminComment); - _writer.Write("IsTaxExempt", entity.IsTaxExempt.ToString()); - _writer.Write("AffiliateId", entity.AffiliateId.ToString()); - _writer.Write("Active", entity.Active.ToString()); - _writer.Write("Deleted", entity.Deleted.ToString()); - _writer.Write("IsSystemAccount", entity.IsSystemAccount.ToString()); - _writer.Write("SystemName", entity.SystemName); - _writer.Write("LastIpAddress", entity.LastIpAddress); - _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" + WriteLocalized(option.SpecificationAttribute); + + _writer.WriteEndElement(); // SpecificationAttribute + _writer.WriteEndElement(); // SpecificationAttributeOption + + _writer.WriteEndElement(); // ProductSpecificationAttribute + } + _writer.WriteEndElement(); // ProductSpecificationAttributes + } + + if (product.ProductBundleItems != null) + { + _writer.WriteStartElement("ProductBundleItems"); + foreach (dynamic bundleItem in product.ProductBundleItems) + { + ProductBundleItem entityPbi = bundleItem.Entity; + + _writer.WriteStartElement("ProductBundleItem"); + _writer.Write("Id", entityPbi.Id.ToString()); + _writer.Write("ProductId", entityPbi.ProductId.ToString()); + _writer.Write("BundleProductId", entityPbi.BundleProductId.ToString()); + _writer.Write("Quantity", entityPbi.Quantity.ToString()); + _writer.Write("Discount", entityPbi.Discount.HasValue ? entityPbi.Discount.Value.ToString(_culture) : ""); + _writer.Write("DiscountPercentage", entityPbi.DiscountPercentage.ToString()); + _writer.Write("Name", (string)bundleItem.Name); + _writer.Write("ShortDescription", (string)bundleItem.ShortDescription); + _writer.Write("FilterAttributes", entityPbi.FilterAttributes.ToString()); + _writer.Write("HideThumbnail", entityPbi.HideThumbnail.ToString()); + _writer.Write("Visible", entityPbi.Visible.ToString()); + _writer.Write("Published", entityPbi.Published.ToString()); + _writer.Write("DisplayOrder", ((int)bundleItem.DisplayOrder).ToString()); + _writer.Write("CreatedOnUtc", entityPbi.CreatedOnUtc.ToString(_culture)); + _writer.Write("UpdatedOnUtc", entityPbi.UpdatedOnUtc.ToString(_culture)); + + WriteLocalized(bundleItem); + + _writer.WriteEndElement(); // ProductBundleItem + } + _writer.WriteEndElement(); // ProductBundleItems + } + + if (node.HasValue()) + { + _writer.WriteEndElement(); + } + } + + public void WriteCustomer(dynamic customer, string node) + { + if (customer == null) + return; + + Customer entity = customer.Entity; + + if (node.HasValue()) + { + _writer.WriteStartElement(node); + } + + _writer.Write("Id", entity.Id.ToString()); + _writer.Write("CustomerGuid", entity.CustomerGuid.ToString()); + _writer.Write("Username", entity.Username); + _writer.Write("Email", entity.Email); + _writer.Write("AdminComment", entity.AdminComment); + _writer.Write("IsTaxExempt", entity.IsTaxExempt.ToString()); + _writer.Write("AffiliateId", entity.AffiliateId.ToString()); + _writer.Write("Active", entity.Active.ToString()); + _writer.Write("Deleted", entity.Deleted.ToString()); + _writer.Write("IsSystemAccount", entity.IsSystemAccount.ToString()); + _writer.Write("SystemName", entity.SystemName); + _writer.Write("LastIpAddress", entity.LastIpAddress); + _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); _writer.Write("Title", entity.Title); _writer.Write("FirstName", entity.FirstName); _writer.Write("LastName", entity.LastName); @@ -952,92 +964,92 @@ public void WriteCustomer(dynamic customer, string node) _writer.Write("BirthDate", entity.BirthDate.HasValue ? entity.BirthDate.Value.ToString(_culture) : ""); _writer.Write("RewardPointsBalance", ((int)customer._RewardPointsBalance).ToString()); - if (customer.CustomerRoles != null) - { - _writer.WriteStartElement("CustomerRoles"); - foreach (dynamic role in customer.CustomerRoles) - { - CustomerRole entityRole = role.Entity; - - _writer.WriteStartElement("CustomerRole"); - _writer.Write("Id", entityRole.Id.ToString()); - _writer.Write("Name", (string)role.Name); - _writer.Write("FreeShipping", entityRole.FreeShipping.ToString()); - _writer.Write("TaxExempt", entityRole.TaxExempt.ToString()); - _writer.Write("TaxDisplayType", entityRole.TaxDisplayType.HasValue ? entityRole.TaxDisplayType.Value.ToString() : ""); - _writer.Write("Active", entityRole.Active.ToString()); - _writer.Write("IsSystemRole", entityRole.IsSystemRole.ToString()); - _writer.Write("SystemName", entityRole.SystemName); - _writer.WriteEndElement(); // CustomerRole - } - _writer.WriteEndElement(); // CustomerRoles - } - - WriteRewardPointsHistory(customer.RewardPointsHistory, "RewardPointsHistories"); - WriteAddress(customer.BillingAddress, "BillingAddress"); - WriteAddress(customer.ShippingAddress, "ShippingAddress"); - - if (customer.Addresses != null) - { - _writer.WriteStartElement("Addresses"); - foreach (dynamic address in customer.Addresses) - { - WriteAddress(address, "Address"); - } - _writer.WriteEndElement(); // Addresses - } - - WriteGenericAttributes(customer); - - if (node.HasValue()) - { - _writer.WriteEndElement(); - } - } - - public void WriteShoppingCartItem(dynamic shoppingCartItem, string node) - { - if (shoppingCartItem == null) - return; - - ShoppingCartItem entity = shoppingCartItem.Entity; - - if (node.HasValue()) - { - _writer.WriteStartElement(node); - } - - _writer.Write("Id", entity.Id.ToString()); - _writer.Write("StoreId", entity.StoreId.ToString()); - _writer.Write("ParentItemId", entity.ParentItemId.HasValue ? entity.ParentItemId.Value.ToString() : ""); - _writer.Write("BundleItemId", entity.BundleItemId.HasValue ? entity.BundleItemId.Value.ToString() : ""); - _writer.Write("ShoppingCartTypeId", entity.ShoppingCartTypeId.ToString()); - _writer.Write("CustomerId", entity.CustomerId.ToString()); - _writer.Write("ProductId", entity.ProductId.ToString()); - _writer.Write("AttributesXml", entity.AttributesXml, null, true); - _writer.Write("CustomerEnteredPrice", entity.CustomerEnteredPrice.ToString(_culture)); - _writer.Write("Quantity", entity.Quantity.ToString()); - _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); - _writer.Write("UpdatedOnUtc", entity.UpdatedOnUtc.ToString(_culture)); - - WriteCustomer(shoppingCartItem.Customer, "Customer"); - WriteProduct(shoppingCartItem.Product, "Product"); - - if (node.HasValue()) - { - _writer.WriteEndElement(); - } - } - } - - - /// - /// Allows to exclude XML nodes from export - /// - [Flags] - public enum ExportXmlExclude - { - None = 0, - Category = 1 - } + if (customer.CustomerRoles != null) + { + _writer.WriteStartElement("CustomerRoles"); + foreach (dynamic role in customer.CustomerRoles) + { + CustomerRole entityRole = role.Entity; + + _writer.WriteStartElement("CustomerRole"); + _writer.Write("Id", entityRole.Id.ToString()); + _writer.Write("Name", (string)role.Name); + _writer.Write("FreeShipping", entityRole.FreeShipping.ToString()); + _writer.Write("TaxExempt", entityRole.TaxExempt.ToString()); + _writer.Write("TaxDisplayType", entityRole.TaxDisplayType.HasValue ? entityRole.TaxDisplayType.Value.ToString() : ""); + _writer.Write("Active", entityRole.Active.ToString()); + _writer.Write("IsSystemRole", entityRole.IsSystemRole.ToString()); + _writer.Write("SystemName", entityRole.SystemName); + _writer.WriteEndElement(); // CustomerRole + } + _writer.WriteEndElement(); // CustomerRoles + } + + WriteRewardPointsHistory(customer.RewardPointsHistory, "RewardPointsHistories"); + WriteAddress(customer.BillingAddress, "BillingAddress"); + WriteAddress(customer.ShippingAddress, "ShippingAddress"); + + if (customer.Addresses != null) + { + _writer.WriteStartElement("Addresses"); + foreach (dynamic address in customer.Addresses) + { + WriteAddress(address, "Address"); + } + _writer.WriteEndElement(); // Addresses + } + + WriteGenericAttributes(customer); + + if (node.HasValue()) + { + _writer.WriteEndElement(); + } + } + + public void WriteShoppingCartItem(dynamic shoppingCartItem, string node) + { + if (shoppingCartItem == null) + return; + + ShoppingCartItem entity = shoppingCartItem.Entity; + + if (node.HasValue()) + { + _writer.WriteStartElement(node); + } + + _writer.Write("Id", entity.Id.ToString()); + _writer.Write("StoreId", entity.StoreId.ToString()); + _writer.Write("ParentItemId", entity.ParentItemId.HasValue ? entity.ParentItemId.Value.ToString() : ""); + _writer.Write("BundleItemId", entity.BundleItemId.HasValue ? entity.BundleItemId.Value.ToString() : ""); + _writer.Write("ShoppingCartTypeId", entity.ShoppingCartTypeId.ToString()); + _writer.Write("CustomerId", entity.CustomerId.ToString()); + _writer.Write("ProductId", entity.ProductId.ToString()); + _writer.Write("AttributesXml", entity.AttributesXml, null, true); + _writer.Write("CustomerEnteredPrice", entity.CustomerEnteredPrice.ToString(_culture)); + _writer.Write("Quantity", entity.Quantity.ToString()); + _writer.Write("CreatedOnUtc", entity.CreatedOnUtc.ToString(_culture)); + _writer.Write("UpdatedOnUtc", entity.UpdatedOnUtc.ToString(_culture)); + + WriteCustomer(shoppingCartItem.Customer, "Customer"); + WriteProduct(shoppingCartItem.Product, "Product"); + + if (node.HasValue()) + { + _writer.WriteEndElement(); + } + } + } + + + /// + /// Allows to exclude XML nodes from export + /// + [Flags] + public enum ExportXmlExclude + { + None = 0, + Category = 1 + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/IDataExporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/IDataExporter.cs index b014d487f5..428048be79 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/IDataExporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/IDataExporter.cs @@ -10,67 +10,67 @@ namespace SmartStore.Services.DataExchange.Export { - public interface IDataExporter - { - DataExportResult Export(DataExportRequest request, CancellationToken cancellationToken); + public interface IDataExporter + { + DataExportResult Export(DataExportRequest request, CancellationToken cancellationToken); DataExportPreviewResult Preview(DataExportRequest request, int pageIndex); - /// - /// Creates a product export context for fast retrieval (eager loading) of product navigation properties - /// - /// Products. null to lazy load data if required. - /// Customer, null to use current customer. - /// Store identifier, null to use current store. - /// Pictures per product, null to load all pictures per product. - /// A value indicating whether to show hidden records - /// Product export context - ProductExportContext CreateProductExportContext( - IEnumerable products = null, - Customer customer = null, - int? storeId = null, - int? maxPicturesPerProduct = null, - bool showHidden = true); - } + /// + /// Creates a product export context for fast retrieval (eager loading) of product navigation properties. + /// + /// Products. null to lazy load data if required. + /// Customer, null to use current customer. + /// Store identifier, null to use current store. + /// Pictures per product, null to load all pictures per product. + /// A value indicating whether to include hidden records. + /// Product export context + ProductExportContext CreateProductExportContext( + IEnumerable products = null, + Customer customer = null, + int? storeId = null, + int? maxPicturesPerProduct = null, + bool includeHidden = true); + } - public class DataExportRequest - { - private readonly static ProgressValueSetter _voidProgressValueSetter = DataExportRequest.SetProgress; + public class DataExportRequest + { + private readonly static ProgressValueSetter _voidProgressValueSetter = DataExportRequest.SetProgress; - public DataExportRequest(ExportProfile profile, Provider provider) - { - Guard.NotNull(profile, nameof(profile)); - Guard.NotNull(provider, nameof(provider)); + public DataExportRequest(ExportProfile profile, Provider provider) + { + Guard.NotNull(profile, nameof(profile)); + Guard.NotNull(provider, nameof(provider)); - Profile = profile; - Provider = provider; - ProgressValueSetter = _voidProgressValueSetter; + Profile = profile; + Provider = provider; + ProgressValueSetter = _voidProgressValueSetter; - EntitiesToExport = new List(); - CustomData = new Dictionary(StringComparer.OrdinalIgnoreCase); - } + EntitiesToExport = new List(); + CustomData = new Dictionary(StringComparer.OrdinalIgnoreCase); + } - public ExportProfile Profile { get; private set; } + public ExportProfile Profile { get; private set; } - public Provider Provider { get; private set; } + public Provider Provider { get; private set; } - public ProgressValueSetter ProgressValueSetter { get; set; } + public ProgressValueSetter ProgressValueSetter { get; set; } - public bool HasPermission { get; set; } + public bool HasPermission { get; set; } - public IList EntitiesToExport { get; set; } + public IList EntitiesToExport { get; set; } - public string ActionOrigin { get; set; } + public string ActionOrigin { get; set; } - public IDictionary CustomData { get; private set; } + public IDictionary CustomData { get; private set; } - public IQueryable ProductQuery { get; set; } + public IQueryable ProductQuery { get; set; } - private static void SetProgress(int val, int max, string msg) - { - // do nothing - } - } + private static void SetProgress(int val, int max, string msg) + { + // do nothing + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/IExportProfileService.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/IExportProfileService.cs index b0b0c8a6bd..f5c0ab977c 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/IExportProfileService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/IExportProfileService.cs @@ -6,117 +6,117 @@ namespace SmartStore.Services.DataExchange.Export { - public interface IExportProfileService - { - /// - /// Inserts an export profile - /// - /// Provider system name - /// The name of the profile - /// File extension supported by provider - /// Features supportde by provider - /// Whether the new profile is a system profile - /// Profile system name - /// Identifier of a profile the settings should be copied from - /// New export profile - ExportProfile InsertExportProfile( - string providerSystemName, - string name, - string fileExtension, - ExportFeatures features, - bool isSystemProfile = false, - string profileSystemName = null, - int cloneFromProfileId = 0); + public interface IExportProfileService + { + /// + /// Inserts an export profile + /// + /// Provider system name + /// The name of the profile + /// File extension supported by provider + /// Features supportde by provider + /// Whether the new profile is a system profile + /// Profile system name + /// Identifier of a profile the settings should be copied from + /// New export profile + ExportProfile InsertExportProfile( + string providerSystemName, + string name, + string fileExtension, + ExportFeatures features, + bool isSystemProfile = false, + string profileSystemName = null, + int cloneFromProfileId = 0); - /// - /// Inserts an export profile - /// - /// Export provider - /// Whether the new profile is a system profile - /// Profile system name - /// Identifier of a profile the settings should be copied from - /// New export profile - ExportProfile InsertExportProfile( - Provider provider, - bool isSystemProfile = false, - string profileSystemName = null, - int cloneFromProfileId = 0); + /// + /// Inserts an export profile + /// + /// Export provider + /// Whether the new profile is a system profile + /// Profile system name + /// Identifier of a profile the settings should be copied from + /// New export profile + ExportProfile InsertExportProfile( + Provider provider, + bool isSystemProfile = false, + string profileSystemName = null, + int cloneFromProfileId = 0); - /// - /// Updates an export profile - /// - /// Export profile - void UpdateExportProfile(ExportProfile profile); + /// + /// Updates an export profile + /// + /// Export profile + void UpdateExportProfile(ExportProfile profile); - /// - /// Deletes an export profile - /// - /// Export profile - /// Whether to delete system profiles - void DeleteExportProfile(ExportProfile profile, bool force = false); + /// + /// Deletes an export profile + /// + /// Export profile + /// Whether to delete system profiles + void DeleteExportProfile(ExportProfile profile, bool force = false); - /// - /// Get queryable export profiles - /// - /// Whether to filter enabled or disabled profiles - /// Export profiles - IQueryable GetExportProfiles(bool? enabled = null); + /// + /// Get queryable export profiles + /// + /// Whether to filter enabled or disabled profiles + /// Export profiles + IQueryable GetExportProfiles(bool? enabled = null); - /// - /// Gets an export profile by identifier - /// - /// Export profile identifier - /// Export profile - ExportProfile GetExportProfileById(int id); + /// + /// Gets an export profile by identifier + /// + /// Export profile identifier + /// Export profile + ExportProfile GetExportProfileById(int id); - /// - /// Gets system export profile by provider system name - /// - /// Provider system name - /// - ExportProfile GetSystemExportProfile(string providerSystemName); + /// + /// Gets system export profile by provider system name + /// + /// Provider system name + /// + ExportProfile GetSystemExportProfile(string providerSystemName); - /// - /// Gets export profiles by provider system name - /// - /// Provider system name - /// List of export profiles - IList GetExportProfilesBySystemName(string providerSystemName); + /// + /// Gets export profiles by provider system name + /// + /// Provider system name + /// List of export profiles + IList GetExportProfilesBySystemName(string providerSystemName); - /// - /// Load all export providers - /// - /// Store identifier - /// Whether to load hidden providers - /// Export providers - IEnumerable> LoadAllExportProviders(int storeId = 0, bool showHidden = true); + /// + /// Load all export providers + /// + /// Store identifier + /// Whether to load hidden providers + /// Export providers + IEnumerable> LoadAllExportProviders(int storeId = 0, bool showHidden = true); - /// - /// Load export provider by system name - /// - /// Provider system name - /// Store identifier - /// Export provider - Provider LoadProvider(string systemName, int storeId = 0); + /// + /// Load export provider by system name + /// + /// Provider system name + /// Store identifier + /// Export provider + Provider LoadProvider(string systemName, int storeId = 0); - /// - /// Get export deployment by identifier - /// - /// Export deployment identifier - /// Export deployment - ExportDeployment GetExportDeploymentById(int id); + /// + /// Get export deployment by identifier + /// + /// Export deployment identifier + /// Export deployment + ExportDeployment GetExportDeploymentById(int id); - /// - /// Update an export deployment - /// - /// Export deployment - void UpdateExportDeployment(ExportDeployment deployment); + /// + /// Update an export deployment + /// + /// Export deployment + void UpdateExportDeployment(ExportDeployment deployment); - /// - /// Deleted an export deployment - /// - /// Export deployment - void DeleteExportDeployment(ExportDeployment deployment); - } + /// + /// Deleted an export deployment + /// + /// Export deployment + void DeleteExportDeployment(ExportDeployment deployment); + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/IExportProvider.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/IExportProvider.cs index 60be2e11a9..f73c64a3ea 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/IExportProvider.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/IExportProvider.cs @@ -3,33 +3,33 @@ namespace SmartStore.Services.DataExchange.Export { - public partial interface IExportProvider : IProvider, IUserEditable - { - /// - /// The exported entity type - /// - ExportEntityType EntityType { get; } + public partial interface IExportProvider : IProvider, IUserEditable + { + /// + /// The exported entity type + /// + ExportEntityType EntityType { get; } - /// - /// File extension of the export files (without dot). Return null for a non file based, on-the-fly export. - /// - string FileExtension { get; } + /// + /// File extension of the export files (without dot). Return null for a non file based, on-the-fly export. + /// + string FileExtension { get; } - /// - /// Get provider specific configuration information. Return null when no provider specific configuration is required. - /// - ExportConfigurationInfo ConfigurationInfo { get; } + /// + /// Get provider specific configuration information. Return null when no provider specific configuration is required. + /// + ExportConfigurationInfo ConfigurationInfo { get; } - /// - /// Export data to a file - /// - /// Export execution context - void Execute(ExportExecuteContext context); + /// + /// Export data to a file + /// + /// Export execution context + void Execute(ExportExecuteContext context); - /// - /// Called once per store when export execution ended - /// - /// Export execution context - void OnExecuted(ExportExecuteContext context); - } + /// + /// Called once per store when export execution ended + /// + /// Export execution context + void OnExecuted(ExportExecuteContext context); + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/CategoryExportContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/CategoryExportContext.cs index ab6665b31e..db7aa9e92f 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/CategoryExportContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/CategoryExportContext.cs @@ -3,74 +3,75 @@ using System.Linq; using SmartStore.Collections; using SmartStore.Core.Domain.Catalog; -using SmartStore.Core.Domain.Media; +using SmartStore.Services.Media; namespace SmartStore.Services.DataExchange.Export.Internal { - internal class CategoryExportContext - { - protected List _categoryIds; - protected List _pictureIds; + internal class CategoryExportContext + { + protected List _categoryIds; + protected List _fileIds; - private Func> _funcProductCategories; - private Func> _funcPictures; + private Func> _funcProductCategories; + private Func> _funcFiles; - private LazyMultimap _productCategories; - private LazyMultimap _pictures; + private LazyMultimap _productCategories; + private LazyMultimap _files; - public CategoryExportContext( - IEnumerable categories, - Func> productCategories, - Func> pictures) - { - if (categories == null) - { - _categoryIds = new List(); - _pictureIds = new List(); - } - else - { - _categoryIds = new List(categories.Select(x => x.Id)); - _pictureIds = new List(categories.Where(x => (x.PictureId ?? 0) != 0).Select(x => x.PictureId ?? 0)); - } + public CategoryExportContext( + IEnumerable categories, + Func> productCategories, + Func> files) + { + if (categories == null) + { + _categoryIds = new List(); + _fileIds = new List(); + } + else + { + _categoryIds = new List(categories.Select(x => x.Id)); + _fileIds = categories.Select(x => x.MediaFileId ?? 0) + .Where(x => x != 0) + .Distinct() + .ToList(); + } - _funcProductCategories = productCategories; - _funcPictures = pictures; - } + _funcProductCategories = productCategories; + _funcFiles = files; + } - public void Clear() - { - if (_productCategories != null) - _productCategories.Clear(); - if (_pictures != null) - _pictures.Clear(); + public void Clear() + { + _productCategories?.Clear(); + _files?.Clear(); - _categoryIds.Clear(); - _pictureIds.Clear(); - } + _categoryIds?.Clear(); + _fileIds?.Clear(); + } - public LazyMultimap ProductCategories - { - get - { - if (_productCategories == null) - { - _productCategories = new LazyMultimap(keys => _funcProductCategories(keys), _categoryIds); - } - return _productCategories; - } - } + public LazyMultimap ProductCategories + { + get + { + if (_productCategories == null) + { + _productCategories = new LazyMultimap(keys => _funcProductCategories(keys), _categoryIds); + } + return _productCategories; + } + } - public LazyMultimap Pictures - { - get - { - if (_pictures == null) - { - _pictures = new LazyMultimap(keys => _funcPictures(keys).ToMultimap(x => x.Id, x => x), _pictureIds); - } - return _pictures; - } - } - } + public LazyMultimap Files + { + get + { + if (_files == null) + { + _files = new LazyMultimap(keys => _funcFiles(keys).ToMultimap(x => x.Id, x => x), _fileIds); + } + return _files; + } + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/CustomerExportContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/CustomerExportContext.cs index 918cb3e110..bf2dc56700 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/CustomerExportContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/CustomerExportContext.cs @@ -7,48 +7,48 @@ namespace SmartStore.Services.DataExchange.Export.Internal { - public class CustomerExportContext - { - protected List _customerIds; - - private Func> _funcGenericAttributes; - - private LazyMultimap _genericAttributes; - - public CustomerExportContext( - IEnumerable customers, - Func> genericAttributes) - { - if (customers == null) - { - _customerIds = new List(); - } - else - { - _customerIds = new List(customers.Select(x => x.Id)); - } - - _funcGenericAttributes = genericAttributes; - } - - public void Clear() - { - if (_genericAttributes != null) - _genericAttributes.Clear(); - - _customerIds.Clear(); - } - - public LazyMultimap GenericAttributes - { - get - { - if (_genericAttributes == null) - { - _genericAttributes = new LazyMultimap(keys => _funcGenericAttributes(keys), _customerIds); - } - return _genericAttributes; - } - } - } + public class CustomerExportContext + { + protected List _customerIds; + + private Func> _funcGenericAttributes; + + private LazyMultimap _genericAttributes; + + public CustomerExportContext( + IEnumerable customers, + Func> genericAttributes) + { + if (customers == null) + { + _customerIds = new List(); + } + else + { + _customerIds = new List(customers.Select(x => x.Id)); + } + + _funcGenericAttributes = genericAttributes; + } + + public void Clear() + { + if (_genericAttributes != null) + _genericAttributes.Clear(); + + _customerIds.Clear(); + } + + public LazyMultimap GenericAttributes + { + get + { + if (_genericAttributes == null) + { + _genericAttributes = new LazyMultimap(keys => _funcGenericAttributes(keys), _customerIds); + } + return _genericAttributes; + } + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/DataExporterContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/DataExporterContext.cs index 317d4400e3..be7aa0d58b 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/DataExporterContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/DataExporterContext.cs @@ -13,74 +13,74 @@ namespace SmartStore.Services.DataExchange.Export.Internal { internal class DataExporterContext - { - public DataExporterContext( - DataExportRequest request, - CancellationToken cancellationToken, - bool isPreview = false) - { - Request = request; - CancellationToken = cancellationToken; - Filter = XmlHelper.Deserialize(request.Profile.Filtering); - Projection = XmlHelper.Deserialize(request.Profile.Projection); - IsPreview = isPreview; - - if (request.Profile.Projection.IsEmpty()) - { - Projection.DescriptionMergingId = (int)ExportDescriptionMerging.Description; - } - - FolderContent = request.Profile.GetExportFolder(true, true); - - DeliveryTimes = new Dictionary(); - QuantityUnits = new Dictionary(); - Stores = new Dictionary(); - Languages = new Dictionary(); - Countries = new Dictionary(); - ProductTemplates = new Dictionary(); - CategoryTemplates = new Dictionary(); - NewsletterSubscriptions = new HashSet(); + { + public DataExporterContext( + DataExportRequest request, + CancellationToken cancellationToken, + bool isPreview = false) + { + Request = request; + CancellationToken = cancellationToken; + Filter = XmlHelper.Deserialize(request.Profile.Filtering); + Projection = XmlHelper.Deserialize(request.Profile.Projection); + IsPreview = isPreview; + + if (request.Profile.Projection.IsEmpty()) + { + Projection.DescriptionMergingId = (int)ExportDescriptionMerging.Description; + } + + FolderContent = request.Profile.GetExportFolder(true, true); + + DeliveryTimes = new Dictionary(); + QuantityUnits = new Dictionary(); + Stores = new Dictionary(); + Languages = new Dictionary(); + Countries = new Dictionary(); + ProductTemplates = new Dictionary(); + CategoryTemplates = new Dictionary(); + NewsletterSubscriptions = new HashSet(); Translations = new Dictionary(); TranslationsPerPage = new Dictionary(); UrlRecords = new Dictionary(); UrlRecordsPerPage = new Dictionary(); StatsPerStore = new Dictionary(); - EntityIdsLoaded = new List(); - EntityIdsPerSegment = new HashSet(); - - Result = new DataExportResult - { - FileFolder = IsFileBasedExport ? FolderContent : null - }; - - ExecuteContext = new ExportExecuteContext(Result, CancellationToken, FolderContent); - ExecuteContext.Filter = Filter; - ExecuteContext.Projection = Projection; - ExecuteContext.ProfileId = request.Profile.Id; - - if (!IsPreview) - { - ExecuteContext.ProgressValueSetter = Request.ProgressValueSetter; - } - } - - /// - /// All entity identifiers per export. - /// - public List EntityIdsLoaded { get; set; } - public void SetLoadedEntityIds(IEnumerable ids) - { - EntityIdsLoaded = EntityIdsLoaded - .Union(ids) - .Distinct() - .ToList(); - } - - /// - /// All entity identifiers per segment (to avoid exporting products multiple times). - /// - public HashSet EntityIdsPerSegment { get; set; } + EntityIdsLoaded = new List(); + EntityIdsPerSegment = new HashSet(); + + Result = new DataExportResult + { + FileFolder = IsFileBasedExport ? FolderContent : null + }; + + ExecuteContext = new ExportExecuteContext(Result, CancellationToken, FolderContent); + ExecuteContext.Filter = Filter; + ExecuteContext.Projection = Projection; + ExecuteContext.ProfileId = request.Profile.Id; + + if (!IsPreview) + { + ExecuteContext.ProgressValueSetter = Request.ProgressValueSetter; + } + } + + /// + /// All entity identifiers per export. + /// + public List EntityIdsLoaded { get; set; } + public void SetLoadedEntityIds(IEnumerable ids) + { + EntityIdsLoaded = EntityIdsLoaded + .Union(ids) + .Distinct() + .ToList(); + } + + /// + /// All entity identifiers per segment (to avoid exporting products multiple times). + /// + public HashSet EntityIdsPerSegment { get; set; } public int LastId { get; set; } public string ProgressInfo { get; set; } @@ -88,40 +88,37 @@ public void SetLoadedEntityIds(IEnumerable ids) public Dictionary StatsPerStore { get; 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); - } - - public ExportFilter Filter { get; private set; } - public ExportProjection Projection { get; private set; } - public Currency ContextCurrency { get; set; } - public Customer ContextCustomer { get; set; } - public Language ContextLanguage { get; 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); + } + + public ExportFilter Filter { get; private set; } + public ExportProjection Projection { get; private set; } + 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 Store Store { get; set; } + public Store Store { get; set; } - public string FolderContent { get; private set; } + public string FolderContent { get; private set; } - public bool IsFileBasedExport - { - get { return Request.Provider == null || Request.Provider.Value == null || Request.Provider.Value.FileExtension.HasValue(); } - } + public bool IsFileBasedExport => Request.Provider == null || Request.Provider.Value == null || Request.Provider.Value.FileExtension.HasValue(); - // Data loaded once per export. - public Dictionary DeliveryTimes { get; set; } - public Dictionary QuantityUnits { get; set; } - public Dictionary Stores { get; set; } - public Dictionary Languages { get; set; } - public Dictionary Countries { get; set; } - public Dictionary ProductTemplates { get; set; } - public Dictionary CategoryTemplates { get; set; } - public HashSet NewsletterSubscriptions { get; set; } + // Data loaded once per export. + public Dictionary DeliveryTimes { get; set; } + public Dictionary QuantityUnits { get; set; } + public Dictionary Stores { get; set; } + public Dictionary Languages { get; set; } + public Dictionary Countries { get; set; } + public Dictionary ProductTemplates { get; set; } + public Dictionary CategoryTemplates { get; set; } + public HashSet NewsletterSubscriptions { get; set; } /// /// All translations for global scopes (like Category, Manufacturer etc.) @@ -133,9 +130,9 @@ public bool IsFileBasedExport public ProductExportContext ProductExportContext { get; set; } public ProductExportContext AssociatedProductContext { get; set; } public OrderExportContext OrderExportContext { get; set; } - public ManufacturerExportContext ManufacturerExportContext { get; set; } - public CategoryExportContext CategoryExportContext { get; set; } - public CustomerExportContext CustomerExportContext { get; set; } + public ManufacturerExportContext ManufacturerExportContext { get; set; } + public CategoryExportContext CategoryExportContext { get; set; } + public CustomerExportContext CustomerExportContext { get; set; } /// /// All per page translations (like ProductVariantAttributeValue etc.) @@ -144,13 +141,12 @@ public bool IsFileBasedExport public Dictionary UrlRecordsPerPage { get; set; } public ExportExecuteContext ExecuteContext { get; set; } - public DataExportResult Result { 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/Export/Internal/DynamicEntity.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/DynamicEntity.cs index 874d43e7e3..db0b6ed93c 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/DynamicEntity.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/DynamicEntity.cs @@ -3,37 +3,37 @@ namespace SmartStore.Services.DataExchange.Export.Internal { - internal class DynamicEntity : HybridExpando - { - public DynamicEntity(DynamicEntity dynamicEntity) - : this(dynamicEntity.WrappedObject) - { - MergeRange(dynamicEntity); - } + internal class DynamicEntity : HybridExpando + { + public DynamicEntity(DynamicEntity dynamicEntity) + : this(dynamicEntity.WrappedObject) + { + MergeRange(dynamicEntity); + } - public DynamicEntity(object entity) - : base(entity) - { - base.Properties["Entity"] = entity; - } + public DynamicEntity(object entity) + : base(entity) + { + base.Properties["Entity"] = entity; + } - public void Merge(string name, object value) - { - Properties[name] = value; - } + public void Merge(string name, object value) + { + Properties[name] = value; + } - public void MergeRange(IDictionary other) - { - foreach (var kvp in other) - { - Properties[kvp.Key] = kvp.Value; - } - } + public void MergeRange(IDictionary other) + { + foreach (var kvp in other) + { + Properties[kvp.Key] = kvp.Value; + } + } - protected override bool TrySetMemberCore(string name, object value) - { - Properties[name] = value; - return true; - } - } + protected override bool TrySetMemberCore(string name, object value) + { + Properties[name] = value; + return true; + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/ManufacturerExportContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/ManufacturerExportContext.cs index 82c607f5ad..9cddbe30cd 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/ManufacturerExportContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/ManufacturerExportContext.cs @@ -3,74 +3,76 @@ using System.Linq; using SmartStore.Collections; using SmartStore.Core.Domain.Catalog; -using SmartStore.Core.Domain.Media; +using SmartStore.Services.Media; namespace SmartStore.Services.DataExchange.Export.Internal { - internal class ManufacturerExportContext - { - protected List _manufacturerIds; - protected List _pictureIds; + internal class ManufacturerExportContext + { + protected List _manufacturerIds; + protected List _fileIds; - private Func> _funcProductManufacturers; - private Func> _funcPictures; + private Func> _funcProductManufacturers; + private Func> _funcFiles; - private LazyMultimap _productManufacturers; - private LazyMultimap _pictures; + private LazyMultimap _productManufacturers; + private LazyMultimap _files; - public ManufacturerExportContext( - IEnumerable manufacturers, - Func> productManufacturers, - Func> pictures) - { - if (manufacturers == null) - { - _manufacturerIds = new List(); - _pictureIds = new List(); - } - else - { - _manufacturerIds = new List(manufacturers.Select(x => x.Id)); - _pictureIds = new List(manufacturers.Where(x => (x.PictureId ?? 0) != 0).Select(x => x.PictureId ?? 0)); - } + public ManufacturerExportContext( + IEnumerable manufacturers, + Func> productManufacturers, + Func> files) + { + if (manufacturers == null) + { + _manufacturerIds = new List(); + _fileIds = new List(); + } + else + { + _manufacturerIds = new List(manufacturers.Select(x => x.Id)); + _fileIds = manufacturers + .Select(x => x.MediaFileId ?? 0) + .Where(x => x != 0) + .Distinct() + .ToList(); + } - _funcProductManufacturers = productManufacturers; - _funcPictures = pictures; - } + _funcProductManufacturers = productManufacturers; + _funcFiles = files; + } - public void Clear() - { - if (_productManufacturers != null) - _productManufacturers.Clear(); - if (_pictures != null) - _pictures.Clear(); + public void Clear() + { + _productManufacturers?.Clear(); + _files?.Clear(); - _manufacturerIds.Clear(); - _pictureIds.Clear(); - } + _manufacturerIds?.Clear(); + _fileIds?.Clear(); + } - public LazyMultimap ProductManufacturers - { - get - { - if (_productManufacturers == null) - { - _productManufacturers = new LazyMultimap(keys => _funcProductManufacturers(keys), _manufacturerIds); - } - return _productManufacturers; - } - } + public LazyMultimap ProductManufacturers + { + get + { + if (_productManufacturers == null) + { + _productManufacturers = new LazyMultimap(keys => _funcProductManufacturers(keys), _manufacturerIds); + } + return _productManufacturers; + } + } - public LazyMultimap Pictures - { - get - { - if (_pictures == null) - { - _pictures = new LazyMultimap(keys => _funcPictures(keys).ToMultimap(x => x.Id, x => x), _pictureIds); - } - return _pictures; - } - } - } + public LazyMultimap Files + { + get + { + if (_files == null) + { + _files = new LazyMultimap(keys => _funcFiles(keys).ToMultimap(x => x.Id, x => x), _fileIds); + } + return _files; + } + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/OrderExportContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/OrderExportContext.cs index 02f56914fb..f3eb5075b1 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/OrderExportContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/Internal/OrderExportContext.cs @@ -9,150 +9,150 @@ namespace SmartStore.Services.DataExchange.Export.Internal { - internal class OrderExportContext - { - protected List _orderIds; - protected List _customerIds; - protected List _addressIds; + internal class OrderExportContext + { + protected List _orderIds; + protected List _customerIds; + protected List _addressIds; - private Func> _funcCustomers; - private Func> _funcCustomerGenericAttributes; - private Func> _funcRewardPointsHistories; - private Func> _funcAddresses; - private Func> _funcOrderItems; - private Func> _funcShipments; + private Func> _funcCustomers; + private Func> _funcCustomerGenericAttributes; + private Func> _funcRewardPointsHistories; + private Func> _funcAddresses; + private Func> _funcOrderItems; + private Func> _funcShipments; - private LazyMultimap _customers; - private LazyMultimap _customerGenericAttributes; - private LazyMultimap _rewardPointsHistories; - private LazyMultimap
_addresses; - private LazyMultimap _orderItems; - private LazyMultimap _shipments; + private LazyMultimap _customers; + private LazyMultimap _customerGenericAttributes; + private LazyMultimap _rewardPointsHistories; + private LazyMultimap
_addresses; + private LazyMultimap _orderItems; + private LazyMultimap _shipments; - public OrderExportContext(IEnumerable orders, - Func> customers, - Func> customerGenericAttributes, - Func> rewardPointsHistory, - Func> addresses, - Func> orderItems, - Func> shipments) - { - if (orders == null) - { - _orderIds = new List(); - _customerIds = new List(); - _addressIds = new List(); - } - else - { - _orderIds = new List(orders.Select(x => x.Id)); - _customerIds = new List(orders.Select(x => x.CustomerId)); + public OrderExportContext(IEnumerable orders, + Func> customers, + Func> customerGenericAttributes, + Func> rewardPointsHistory, + Func> addresses, + Func> orderItems, + Func> shipments) + { + if (orders == null) + { + _orderIds = new List(); + _customerIds = new List(); + _addressIds = new List(); + } + else + { + _orderIds = new List(orders.Select(x => x.Id)); + _customerIds = new List(orders.Select(x => x.CustomerId)); - _addressIds = orders.Select(x => x.BillingAddressId) - .Union(orders.Select(x => x.ShippingAddressId ?? 0)) - .Where(x => x != 0) - .Distinct() - .ToList(); - } + _addressIds = orders.Select(x => x.BillingAddressId) + .Union(orders.Select(x => x.ShippingAddressId ?? 0)) + .Where(x => x != 0) + .Distinct() + .ToList(); + } - _funcCustomers = customers; - _funcCustomerGenericAttributes = customerGenericAttributes; - _funcRewardPointsHistories = rewardPointsHistory; - _funcAddresses = addresses; - _funcOrderItems = orderItems; - _funcShipments = shipments; - } + _funcCustomers = customers; + _funcCustomerGenericAttributes = customerGenericAttributes; + _funcRewardPointsHistories = rewardPointsHistory; + _funcAddresses = addresses; + _funcOrderItems = orderItems; + _funcShipments = shipments; + } - public void Clear() - { - if (_customers != null) - _customers.Clear(); - if (_customerGenericAttributes != null) - _customerGenericAttributes.Clear(); - if (_rewardPointsHistories != null) - _rewardPointsHistories.Clear(); - if (_addresses != null) - _addresses.Clear(); - if (_orderItems != null) - _orderItems.Clear(); - if (_shipments != null) - _shipments.Clear(); + public void Clear() + { + if (_customers != null) + _customers.Clear(); + if (_customerGenericAttributes != null) + _customerGenericAttributes.Clear(); + if (_rewardPointsHistories != null) + _rewardPointsHistories.Clear(); + if (_addresses != null) + _addresses.Clear(); + if (_orderItems != null) + _orderItems.Clear(); + if (_shipments != null) + _shipments.Clear(); - _orderIds.Clear(); - _customerIds.Clear(); - _addressIds.Clear(); - } + _orderIds.Clear(); + _customerIds.Clear(); + _addressIds.Clear(); + } - public LazyMultimap Customers - { - get - { - if (_customers == null) - { - _customers = new LazyMultimap(keys => _funcCustomers(keys).ToMultimap(x => x.Id, x => x), _customerIds); - } - return _customers; - } - } + public LazyMultimap Customers + { + get + { + if (_customers == null) + { + _customers = new LazyMultimap(keys => _funcCustomers(keys).ToMultimap(x => x.Id, x => x), _customerIds); + } + return _customers; + } + } - public LazyMultimap CustomerGenericAttributes - { - get - { - if (_customerGenericAttributes == null) - { - _customerGenericAttributes = new LazyMultimap(keys => _funcCustomerGenericAttributes(keys), _customerIds); - } - return _customerGenericAttributes; - } - } + public LazyMultimap CustomerGenericAttributes + { + get + { + if (_customerGenericAttributes == null) + { + _customerGenericAttributes = new LazyMultimap(keys => _funcCustomerGenericAttributes(keys), _customerIds); + } + return _customerGenericAttributes; + } + } - public LazyMultimap RewardPointsHistories - { - get - { - if (_rewardPointsHistories == null) - { - _rewardPointsHistories = new LazyMultimap(keys => _funcRewardPointsHistories(keys), _customerIds); - } - return _rewardPointsHistories; - } - } + public LazyMultimap RewardPointsHistories + { + get + { + if (_rewardPointsHistories == null) + { + _rewardPointsHistories = new LazyMultimap(keys => _funcRewardPointsHistories(keys), _customerIds); + } + return _rewardPointsHistories; + } + } - public LazyMultimap
Addresses - { - get - { - if (_addresses == null) - { - _addresses = new LazyMultimap
(keys => _funcAddresses(keys).ToMultimap(x => x.Id, x => x), _addressIds); - } - return _addresses; - } - } + public LazyMultimap
Addresses + { + get + { + if (_addresses == null) + { + _addresses = new LazyMultimap
(keys => _funcAddresses(keys).ToMultimap(x => x.Id, x => x), _addressIds); + } + return _addresses; + } + } - public LazyMultimap OrderItems - { - get - { - if (_orderItems == null) - { - _orderItems = new LazyMultimap(keys => _funcOrderItems(keys), _orderIds); - } - return _orderItems; - } - } + public LazyMultimap OrderItems + { + get + { + if (_orderItems == null) + { + _orderItems = new LazyMultimap(keys => _funcOrderItems(keys), _orderIds); + } + return _orderItems; + } + } - public LazyMultimap Shipments - { - get - { - if (_shipments == null) - { - _shipments = new LazyMultimap(keys => _funcShipments(keys), _orderIds); - } - return _shipments; - } - } - } + public LazyMultimap Shipments + { + get + { + if (_shipments == null) + { + _shipments = new LazyMultimap(keys => _funcShipments(keys), _orderIds); + } + return _shipments; + } + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Export/ProductExportContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Export/ProductExportContext.cs index 5a3b22a4bd..bf8562da11 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Export/ProductExportContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Export/ProductExportContext.cs @@ -12,109 +12,92 @@ namespace SmartStore.Services.DataExchange.Export /// Cargo data to reduce database round trips during work with product batches (export, list model creation etc.) ///
public class ProductExportContext : PriceCalculationContext - { - private Func> _funcProductPictures; - private Func> _funcProductTags; - private Func> _funcSpecificationAttributes; - private Func> _funcPictures; + { + private Func> _funcProductPictures; + private Func> _funcProductTags; + private Func> _funcSpecificationAttributes; private Func> _funcDownloads; - private LazyMultimap _productPictures; - private LazyMultimap _productTags; - private LazyMultimap _specificationAttributes; - private LazyMultimap _pictures; + private LazyMultimap _productPictures; + private LazyMultimap _productTags; + private LazyMultimap _specificationAttributes; private LazyMultimap _downloads; public ProductExportContext( - IEnumerable products, - Func> attributes, - Func> attributeCombinations, - Func> specificationAttributes, - Func> tierPrices, - Func> productCategories, - Func> productManufacturers, - Func> appliedDiscounts, - Func> productBundleItems, + IEnumerable products, + Func> attributes, + Func> attributeCombinations, + Func> specificationAttributes, + Func> tierPrices, + Func> productCategories, + Func> productManufacturers, + Func> appliedDiscounts, + Func> productBundleItems, Func> associatedProducts, - Func> pictures, - Func> productPictures, - Func> productTags, + Func> productPictures, + Func> productTags, Func> downloads) - : base(products, - attributes, - attributeCombinations, - tierPrices, - productCategories, - productManufacturers, - appliedDiscounts, - productBundleItems, + : base(products, + attributes, + attributeCombinations, + tierPrices, + productCategories, + productManufacturers, + appliedDiscounts, + productBundleItems, associatedProducts) - { - _funcPictures = pictures; - _funcProductPictures = productPictures; - _funcProductTags = productTags; - _funcSpecificationAttributes = specificationAttributes; + { + _funcProductPictures = productPictures; + _funcProductTags = productTags; + _funcSpecificationAttributes = specificationAttributes; _funcDownloads = downloads; - } + } - public new void Clear() - { + public new void Clear() + { _productPictures?.Clear(); _productTags?.Clear(); _specificationAttributes?.Clear(); - _pictures?.Clear(); _downloads?.Clear(); - base.Clear(); - } - - public LazyMultimap Pictures - { - get - { - if (_pictures == null) - { - _pictures = new LazyMultimap(keys => _funcPictures(keys), _productIds); - } - return _pictures; - } - } + base.Clear(); + } - public LazyMultimap ProductPictures - { - get - { - if (_productPictures == null) - { - _productPictures = new LazyMultimap(keys => _funcProductPictures(keys), _productIds); - } - return _productPictures; - } - } + public LazyMultimap ProductPictures + { + get + { + if (_productPictures == null) + { + _productPictures = new LazyMultimap(keys => _funcProductPictures(keys), _productIds); + } + return _productPictures; + } + } - public LazyMultimap ProductTags - { - get - { - if (_productTags == null) - { - _productTags = new LazyMultimap(keys => _funcProductTags(keys), _productIds); - } - return _productTags; - } - } + public LazyMultimap ProductTags + { + get + { + if (_productTags == null) + { + _productTags = new LazyMultimap(keys => _funcProductTags(keys), _productIds); + } + return _productTags; + } + } - public LazyMultimap SpecificationAttributes - { - get - { - if (_specificationAttributes == null) - { - _specificationAttributes = new LazyMultimap(keys => _funcSpecificationAttributes(keys), _productIds); - } - return _specificationAttributes; - } - } + public LazyMultimap SpecificationAttributes + { + get + { + if (_specificationAttributes == null) + { + _specificationAttributes = new LazyMultimap(keys => _funcSpecificationAttributes(keys), _productIds); + } + return _specificationAttributes; + } + } public LazyMultimap Downloads { @@ -127,5 +110,5 @@ public LazyMultimap Downloads return _downloads; } } - } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/ISyncMappingService.cs b/src/Libraries/SmartStore.Services/DataExchange/ISyncMappingService.cs index 9bdbc6d790..8828902596 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/ISyncMappingService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/ISyncMappingService.cs @@ -5,169 +5,169 @@ namespace SmartStore.Services.DataExchange { - public partial interface ISyncMappingService - { - /// - /// Inserts a sync mapping entity - /// - /// Sync mapping - void InsertSyncMapping(SyncMapping mapping); - - /// - /// Inserts a range of sync mapping entities - /// - /// A sequence of sync mappings - void InsertSyncMappings(IEnumerable mappings); - - /// - /// Gets all sync mappings - /// - /// The context (external application) name. Leave null to load all records regardless of context. - /// The entity name. Leave null to load all records regardless of entity name. - /// Array of entity identifiers - /// SyncMappings - IList GetAllSyncMappings(string contextName = null, string entityName = null, int[] entityIds = null); - - /// - /// Gets a sync mapping record by (target) entity id, name and context name. - /// - /// Entity nd - /// Entity name - /// Context name - /// SyncMapping - SyncMapping GetSyncMappingByEntity(int entityId, string entityName, string contextName); - - /// - /// Gets a sync mapping record by (external) source key, entity name and context name. - /// - /// Source key - /// Entity name - /// Context name - /// SyncMappings - SyncMapping GetSyncMappingBySource(string sourceKey, string entityName, string contextName); - - /// - /// Deletes a sync mapping entity - /// - /// Sync mapping - void DeleteSyncMapping(SyncMapping mapping); - - /// - /// Deletes a range of sync mapping entities - /// - /// Sync mappings - void DeleteSyncMappings(IEnumerable mappings); - - /// - /// Deletes all sync mapping entities referencing the specified entity - /// - /// The entity - void DeleteSyncMappingsFor(T entity) where T : BaseEntity; - - /// - /// Deletes a range of sync mapping entities - /// - /// The context (external application) name. - /// The entity name. Leave null to delete all context specific mappings regardless of entity name. - /// SyncMappings - void DeleteSyncMappings(string contextName, string entityName = null); - - /// - /// Updates a sync mapping entity - /// - /// Sync mapping - void UpdateSyncMapping(SyncMapping mapping); - } - - - public static class ISyncMappingServiceExtensions - { - - public static SyncMapping InsertSyncMapping(this ISyncMappingService svc, T entity, string contextName, string sourceKey) where T : BaseEntity - { - Guard.NotNull(entity, nameof(entity)); - Guard.NotEmpty(contextName, nameof(contextName)); - Guard.NotEmpty(sourceKey, nameof(sourceKey)); - - if (entity is SyncMapping) - { - throw Error.InvalidOperation("Cannot insert a sync mapping record for a SyncMapping entity"); - } - - if (entity.IsTransientRecord()) - { - throw Error.InvalidOperation("Cannot insert a sync mapping record for a transient (unsaved) entity"); - } - - var mapping = new SyncMapping - { - ContextName = contextName, - EntityId = entity.Id, - EntityName = entity.GetEntityName(), - SourceKey = sourceKey - }; - - return mapping; - } - - public static SyncMapping GetSyncMappingByEntity(this ISyncMappingService svc, T entity, string contextName) where T : BaseEntity - { - Guard.NotNull(entity, nameof(entity)); - Guard.NotEmpty(contextName, nameof(contextName)); - - if (entity is SyncMapping) - { - throw Error.InvalidOperation("Cannot get a sync mapping record for a SyncMapping entity"); - } - - if (entity.IsTransientRecord()) - { - throw Error.InvalidOperation("Cannot get a sync mapping record for a transient (unsaved) entity"); - } - - return svc.GetSyncMappingByEntity(entity.Id, entity.GetEntityName(), contextName); - } - - /// - /// Inserts a range of sync mapping entities by specifying two equal sized sequences for entity ids and source keys. - /// - /// Context name for all mappings - /// Entity name for all mappings - /// A sequence of entity ids - /// A sequence of source keys - /// List of persisted sync mappings - /// Both sequences must contain at least one element and must be of equal size. - public static IList InsertSyncMappings(this ISyncMappingService svc, string contextName, string entityName, IEnumerable entityIds, IEnumerable sourceKeys) - { - Guard.NotEmpty(contextName, nameof(contextName)); - Guard.NotEmpty(entityName, nameof(entityName)); - Guard.NotNull(entityIds, nameof(entityIds)); - Guard.NotNull(sourceKeys, nameof(sourceKeys)); - - if (!entityIds.Any() || !sourceKeys.Any() || entityIds.Count() != sourceKeys.Count()) - { - throw Error.InvalidOperation("Both sequences must contain at least one element and must be of equal size."); - } - - var mappings = new List(); - var arrIds = entityIds.ToArray(); - var arrKeys = sourceKeys.ToArray(); - - for (int i = 0; i < arrIds.Length; i++) - { - mappings.Add(new SyncMapping - { - ContextName = contextName, - EntityName = entityName, - EntityId = arrIds[i], - SourceKey = arrKeys[i] - }); - } - - svc.InsertSyncMappings(mappings); - - return mappings; - } - - } + public partial interface ISyncMappingService + { + /// + /// Inserts a sync mapping entity + /// + /// Sync mapping + void InsertSyncMapping(SyncMapping mapping); + + /// + /// Inserts a range of sync mapping entities + /// + /// A sequence of sync mappings + void InsertSyncMappings(IEnumerable mappings); + + /// + /// Gets all sync mappings + /// + /// The context (external application) name. Leave null to load all records regardless of context. + /// The entity name. Leave null to load all records regardless of entity name. + /// Array of entity identifiers + /// SyncMappings + IList GetAllSyncMappings(string contextName = null, string entityName = null, int[] entityIds = null); + + /// + /// Gets a sync mapping record by (target) entity id, name and context name. + /// + /// Entity nd + /// Entity name + /// Context name + /// SyncMapping + SyncMapping GetSyncMappingByEntity(int entityId, string entityName, string contextName); + + /// + /// Gets a sync mapping record by (external) source key, entity name and context name. + /// + /// Source key + /// Entity name + /// Context name + /// SyncMappings + SyncMapping GetSyncMappingBySource(string sourceKey, string entityName, string contextName); + + /// + /// Deletes a sync mapping entity + /// + /// Sync mapping + void DeleteSyncMapping(SyncMapping mapping); + + /// + /// Deletes a range of sync mapping entities + /// + /// Sync mappings + void DeleteSyncMappings(IEnumerable mappings); + + /// + /// Deletes all sync mapping entities referencing the specified entity + /// + /// The entity + void DeleteSyncMappingsFor(T entity) where T : BaseEntity; + + /// + /// Deletes a range of sync mapping entities + /// + /// The context (external application) name. + /// The entity name. Leave null to delete all context specific mappings regardless of entity name. + /// SyncMappings + void DeleteSyncMappings(string contextName, string entityName = null); + + /// + /// Updates a sync mapping entity + /// + /// Sync mapping + void UpdateSyncMapping(SyncMapping mapping); + } + + + public static class ISyncMappingServiceExtensions + { + + public static SyncMapping InsertSyncMapping(this ISyncMappingService svc, T entity, string contextName, string sourceKey) where T : BaseEntity + { + Guard.NotNull(entity, nameof(entity)); + Guard.NotEmpty(contextName, nameof(contextName)); + Guard.NotEmpty(sourceKey, nameof(sourceKey)); + + if (entity is SyncMapping) + { + throw Error.InvalidOperation("Cannot insert a sync mapping record for a SyncMapping entity"); + } + + if (entity.IsTransientRecord()) + { + throw Error.InvalidOperation("Cannot insert a sync mapping record for a transient (unsaved) entity"); + } + + var mapping = new SyncMapping + { + ContextName = contextName, + EntityId = entity.Id, + EntityName = entity.GetEntityName(), + SourceKey = sourceKey + }; + + return mapping; + } + + public static SyncMapping GetSyncMappingByEntity(this ISyncMappingService svc, T entity, string contextName) where T : BaseEntity + { + Guard.NotNull(entity, nameof(entity)); + Guard.NotEmpty(contextName, nameof(contextName)); + + if (entity is SyncMapping) + { + throw Error.InvalidOperation("Cannot get a sync mapping record for a SyncMapping entity"); + } + + if (entity.IsTransientRecord()) + { + throw Error.InvalidOperation("Cannot get a sync mapping record for a transient (unsaved) entity"); + } + + return svc.GetSyncMappingByEntity(entity.Id, entity.GetEntityName(), contextName); + } + + /// + /// Inserts a range of sync mapping entities by specifying two equal sized sequences for entity ids and source keys. + /// + /// Context name for all mappings + /// Entity name for all mappings + /// A sequence of entity ids + /// A sequence of source keys + /// List of persisted sync mappings + /// Both sequences must contain at least one element and must be of equal size. + public static IList InsertSyncMappings(this ISyncMappingService svc, string contextName, string entityName, IEnumerable entityIds, IEnumerable sourceKeys) + { + Guard.NotEmpty(contextName, nameof(contextName)); + Guard.NotEmpty(entityName, nameof(entityName)); + Guard.NotNull(entityIds, nameof(entityIds)); + Guard.NotNull(sourceKeys, nameof(sourceKeys)); + + if (!entityIds.Any() || !sourceKeys.Any() || entityIds.Count() != sourceKeys.Count()) + { + throw Error.InvalidOperation("Both sequences must contain at least one element and must be of equal size."); + } + + var mappings = new List(); + var arrIds = entityIds.ToArray(); + var arrKeys = sourceKeys.ToArray(); + + for (int i = 0; i < arrIds.Length; i++) + { + mappings.Add(new SyncMapping + { + ContextName = contextName, + EntityName = entityName, + EntityId = arrIds[i], + SourceKey = arrKeys[i] + }); + } + + svc.InsertSyncMappings(mappings); + + return mappings; + } + + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/ColumnMapping/ColumnMap.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/ColumnMapping/ColumnMap.cs index ec1ae4335b..b2a0b9302a 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/ColumnMapping/ColumnMap.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/ColumnMapping/ColumnMap.cs @@ -1,147 +1,147 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Newtonsoft.Json; namespace SmartStore.Services.DataExchange.Import { - public class ColumnMap - { - // maps source column to property - private readonly Dictionary _map = new Dictionary(StringComparer.OrdinalIgnoreCase); - - private static bool IsIndexed(string name) - { - return (name.EmptyNull().EndsWith("]") && name.EmptyNull().Contains("[")); - } - - private static string CreateSourceName(string name, string index) - { - if (index.HasValue()) - { - name += String.Concat("[", index, "]"); - } - - return name; - } - - public IReadOnlyDictionary Mappings - { - get { return _map; } - } - - public static bool ParseSourceName(string sourceName, out string nameWithoutIndex, out string index) - { - nameWithoutIndex = sourceName; - index = null; - - var result = true; - - if (sourceName.HasValue() && IsIndexed(sourceName)) - { - var x1 = sourceName.IndexOf('['); - var x2 = sourceName.IndexOf(']', x1); - - if (x1 != -1 && x2 != -1 && x2 > x1) - { - nameWithoutIndex = sourceName.Substring(0, x1); - index = sourceName.Substring(x1 + 1, x2 - x1 - 1); - } - else - { - result = false; - } - } - - return result; - } - - public void AddMapping(string sourceName, string mappedName, string defaultValue = null) - { - AddMapping(sourceName, null, mappedName, defaultValue); + public class ColumnMap + { + // maps source column to property + private readonly Dictionary _map = new Dictionary(StringComparer.OrdinalIgnoreCase); + + private static bool IsIndexed(string name) + { + return (name.EmptyNull().EndsWith("]") && name.EmptyNull().Contains("[")); } - public void AddMapping(string sourceName, string index, string mappedName, string defaultValue = null) - { - Guard.NotEmpty(sourceName, nameof(sourceName)); - Guard.NotEmpty(mappedName, nameof(mappedName)); - - var key = CreateSourceName(sourceName, index); - - _map[key] = new ColumnMappingItem - { - SoureName = key, - MappedName = mappedName, - Default = defaultValue - }; - } - - /// - /// Gets a mapped column value - /// - /// The name of the column to get a mapped value for. - /// The column index, e.g. a language code (de, en etc.) - /// The mapped column value OR - if the name is unmapped - a value with the passed [] - public ColumnMappingItem GetMapping(string sourceName, string index) - { - return GetMapping(CreateSourceName(sourceName, index)); - } - - /// - /// Gets a mapped column value - /// - /// The name of the column to get a mapped value for. - /// The mapped column value OR - if the name is unmapped - the value of the passed - public ColumnMappingItem GetMapping(string sourceName) - { - ColumnMappingItem result; - - if (_map.TryGetValue(sourceName, out result)) - { - return result; - } - - return new ColumnMappingItem { SoureName = sourceName, MappedName = sourceName }; - } - } - - - [JsonObject(MemberSerialization.OptIn)] - public class ColumnMappingItem - { - private bool? _ignored; - - /// - /// The source name - /// - [JsonIgnore] - public string SoureName { get; set; } - - /// - /// The mapped name - /// - [JsonProperty] - public string MappedName { get; set; } - - /// - /// An optional default value - /// - [JsonProperty] - public string Default { get; set; } - - /// - /// Indicates whether to explicitly ignore this property - /// - public bool IgnoreProperty - { - get - { - if (_ignored == null) - { - _ignored = Default != null && Default == "[IGNOREPROPERTY]"; - } - - return _ignored.Value; - } - } - } + private static string CreateSourceName(string name, string index) + { + if (index.HasValue()) + { + name += String.Concat("[", index, "]"); + } + + return name; + } + + public IReadOnlyDictionary Mappings => _map; + + public static bool ParseSourceName(string sourceName, out string nameWithoutIndex, out string index) + { + nameWithoutIndex = sourceName; + index = null; + + var result = true; + + if (sourceName.HasValue() && IsIndexed(sourceName)) + { + var x1 = sourceName.IndexOf('['); + var x2 = sourceName.IndexOf(']', x1); + + if (x1 != -1 && x2 != -1 && x2 > x1) + { + nameWithoutIndex = sourceName.Substring(0, x1); + index = sourceName.Substring(x1 + 1, x2 - x1 - 1); + } + else + { + result = false; + } + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddMapping(string sourceName, string mappedName, string defaultValue = null) + { + AddMapping(sourceName, null, mappedName, defaultValue); + } + + public void AddMapping(string sourceName, string index, string mappedName, string defaultValue = null) + { + Guard.NotEmpty(sourceName, nameof(sourceName)); + Guard.NotEmpty(mappedName, nameof(mappedName)); + + var key = CreateSourceName(sourceName, index); + + _map[key] = new ColumnMappingItem + { + SoureName = key, + MappedName = mappedName, + Default = defaultValue + }; + } + + /// + /// Gets a mapped column value + /// + /// The name of the column to get a mapped value for. + /// The column index, e.g. a language code (de, en etc.) + /// The mapped column value OR - if the name is unmapped - a value with the passed [] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ColumnMappingItem GetMapping(string sourceName, string index) + { + return GetMapping(CreateSourceName(sourceName, index)); + } + + /// + /// Gets a mapped column value + /// + /// The name of the column to get a mapped value for. + /// The mapped column value OR - if the name is unmapped - the value of the passed + public ColumnMappingItem GetMapping(string sourceName) + { + ColumnMappingItem result; + + if (_map.TryGetValue(sourceName, out result)) + { + return result; + } + + return new ColumnMappingItem { SoureName = sourceName, MappedName = sourceName }; + } + } + + + [JsonObject(MemberSerialization.OptIn)] + public class ColumnMappingItem + { + private bool? _ignored; + + /// + /// The source name + /// + [JsonIgnore] + public string SoureName { get; set; } + + /// + /// The mapped name + /// + [JsonProperty] + public string MappedName { get; set; } + + /// + /// An optional default value + /// + [JsonProperty] + public string Default { get; set; } + + /// + /// Indicates whether to explicitly ignore this property + /// + public bool IgnoreProperty + { + get + { + if (_ignored == null) + { + _ignored = Default != null && Default == "[IGNOREPROPERTY]"; + } + + return _ignored.Value; + } + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/ColumnMapping/ColumnMapConverter.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/ColumnMapping/ColumnMapConverter.cs index c46b90b82a..4cd4cb5684 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/ColumnMapping/ColumnMapConverter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/ColumnMapping/ColumnMapConverter.cs @@ -6,69 +6,69 @@ namespace SmartStore.Services.DataExchange.Import { - public class ColumnMapConverter : TypeConverterBase - { - public ColumnMapConverter() - : base(typeof(object)) - { - } + public class ColumnMapConverter : DefaultTypeConverter + { + public ColumnMapConverter() + : base(typeof(object)) + { + } - public override bool CanConvertFrom(Type type) - { - return type == typeof(string); - } + public override bool CanConvertFrom(Type type) + { + return type == typeof(string); + } - public override bool CanConvertTo(Type type) - { - return type == typeof(string); - } + public override bool CanConvertTo(Type type) + { + return type == typeof(string); + } - public override object ConvertFrom(CultureInfo culture, object value) - { - if (value is string) - { - var dict = JsonConvert.DeserializeObject>((string)value); - var map = new ColumnMap(); + public override object ConvertFrom(CultureInfo culture, object value) + { + if (value is string) + { + var dict = JsonConvert.DeserializeObject>((string)value); + var map = new ColumnMap(); - foreach (var kvp in dict) - { - map.AddMapping(kvp.Key, null, kvp.Value.MappedName, kvp.Value.Default); - } + foreach (var kvp in dict) + { + map.AddMapping(kvp.Key, null, kvp.Value.MappedName, kvp.Value.Default); + } - return map; + return map; } - return base.ConvertFrom(culture, value); - } + return base.ConvertFrom(culture, value); + } - public T ConvertFrom(string value) - { - if (value.HasValue()) - return (T)ConvertFrom(CultureInfo.InvariantCulture, value); + public T ConvertFrom(string value) + { + if (value.HasValue()) + return (T)ConvertFrom(CultureInfo.InvariantCulture, value); - return default(T); - } + return default(T); + } - public override object ConvertTo(CultureInfo culture, string format, object value, Type to) - { - if (to == typeof(string)) - { - if (value is ColumnMap) - { - return JsonConvert.SerializeObject(((ColumnMap)value).Mappings); - } - else - { - return string.Empty; - } - } + public override object ConvertTo(CultureInfo culture, string format, object value, Type to) + { + if (to == typeof(string)) + { + if (value is ColumnMap) + { + return JsonConvert.SerializeObject(((ColumnMap)value).Mappings); + } + else + { + return string.Empty; + } + } - return base.ConvertTo(culture, format, value, to); - } + return base.ConvertTo(culture, format, value, to); + } - public string ConvertTo(object value) - { - return (string)ConvertTo(CultureInfo.InvariantCulture, null, value, typeof(string)); - } - } + public string ConvertTo(object value) + { + return (string)ConvertTo(CultureInfo.InvariantCulture, null, value, typeof(string)); + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/DataImportTask.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/DataImportTask.cs index 541850340f..0f813ba6ab 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/DataImportTask.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/DataImportTask.cs @@ -2,33 +2,33 @@ namespace SmartStore.Services.DataExchange.Import { - // note: namespace persisted in ScheduleTask.Type - public partial class DataImportTask : ITask - { - private readonly IDataImporter _importer; - private readonly IImportProfileService _importProfileService; + // note: namespace persisted in ScheduleTask.Type + public partial class DataImportTask : ITask + { + private readonly IDataImporter _importer; + private readonly IImportProfileService _importProfileService; - public DataImportTask( - IDataImporter importer, - IImportProfileService importProfileService) - { - _importer = importer; - _importProfileService = importProfileService; - } + public DataImportTask( + IDataImporter importer, + IImportProfileService importProfileService) + { + _importer = importer; + _importProfileService = importProfileService; + } - public void Execute(TaskExecutionContext ctx) - { - var profileId = ctx.ScheduleTaskHistory.ScheduleTask.Alias.ToInt(); - var profile = _importProfileService.GetImportProfileById(profileId); + public void Execute(TaskExecutionContext ctx) + { + var profileId = ctx.ScheduleTaskHistory.ScheduleTask.Alias.ToInt(); + var profile = _importProfileService.GetImportProfileById(profileId); - var request = new DataImportRequest(profile); + var request = new DataImportRequest(profile); - request.ProgressValueSetter = delegate (int val, int max, string msg) - { - ctx.SetProgress(val, max, msg, true); - }; + request.ProgressValueSetter = delegate (int val, int max, string msg) + { + ctx.SetProgress(val, max, msg, true); + }; - _importer.Import(request, ctx.CancellationToken); - } - } + _importer.Import(request, ctx.CancellationToken); + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/DataImporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/DataImporter.cs index e413acdde9..8939212ee5 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/DataImporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/DataImporter.cs @@ -1,8 +1,9 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using System.Threading; -using System.Linq; using SmartStore.Core; using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Customers; @@ -10,87 +11,81 @@ using SmartStore.Core.Email; using SmartStore.Core.Localization; using SmartStore.Core.Logging; +using SmartStore.Core.Security; +using SmartStore.Data.Caching; using SmartStore.Services.DataExchange.Csv; +using SmartStore.Services.DataExchange.Import.Events; using SmartStore.Services.DataExchange.Import.Internal; using SmartStore.Services.Localization; using SmartStore.Services.Messages; -using SmartStore.Services.Security; -using SmartStore.Utilities; -using SmartStore.Data.Caching; using SmartStore.Services.Seo; -using System.Collections.Generic; -using SmartStore.Services.DataExchange.Import.Events; +using SmartStore.Utilities; namespace SmartStore.Services.DataExchange.Import { - public partial class DataImporter : IDataImporter - { - private readonly ICommonServices _services; - private readonly IImportProfileService _importProfileService; - private readonly ILanguageService _languageService; - private readonly Func _importerFactory; - private readonly Lazy _emailAccountService; - private readonly Lazy _emailSender; - private readonly Lazy _contactDataSettings; - private readonly Lazy _dataExchangeSettings; - private readonly IDbCache _dbCache; - private readonly IUrlRecordService _urlRecordService; - private readonly ILocalizedEntityService _localizedEntityService; - - public DataImporter( - ICommonServices services, - IImportProfileService importProfileService, - ILanguageService languageService, - Func importerFactory, - Lazy emailAccountService, - Lazy emailSender, - Lazy contactDataSettings, - Lazy dataExchangeSettings, - IDbCache dbCache, - IUrlRecordService urlRecordService, - ILocalizedEntityService localizedEntityService) - { - _services = services; - _importProfileService = importProfileService; - _languageService = languageService; - _importerFactory = importerFactory; - _emailAccountService = emailAccountService; - _emailSender = emailSender; - _contactDataSettings = contactDataSettings; - _dataExchangeSettings = dataExchangeSettings; - _dbCache = dbCache; - _urlRecordService = urlRecordService; - _localizedEntityService = localizedEntityService; - - T = NullLocalizer.Instance; - } - - public Localizer T { get; set; } - - private bool HasPermission(DataImporterContext ctx) - { - if (ctx.Request.HasPermission) - return true; - - var customer = _services.WorkContext.CurrentCustomer; - - if (customer.SystemName == SystemCustomerNames.BackgroundTask) - return true; - - if (ctx.Request.Profile.EntityType == ImportEntityType.Product || ctx.Request.Profile.EntityType == ImportEntityType.Category) - return _services.Permissions.Authorize(StandardPermissionProvider.ManageCatalog, customer); - - if (ctx.Request.Profile.EntityType == ImportEntityType.Customer) - return _services.Permissions.Authorize(StandardPermissionProvider.ManageCustomers, customer); - - if (ctx.Request.Profile.EntityType == ImportEntityType.NewsLetterSubscription) - return _services.Permissions.Authorize(StandardPermissionProvider.ManageNewsletterSubscribers, customer); - - return true; - } - - private void LogResults(DataImporterContext ctx) - { + public partial class DataImporter : IDataImporter + { + private readonly ICommonServices _services; + private readonly IImportProfileService _importProfileService; + private readonly ILanguageService _languageService; + private readonly Func _importerFactory; + private readonly Lazy _emailAccountService; + private readonly Lazy _emailSender; + private readonly Lazy _contactDataSettings; + private readonly Lazy _dataExchangeSettings; + private readonly IDbCache _dbCache; + private readonly IUrlRecordService _urlRecordService; + private readonly ILocalizedEntityService _localizedEntityService; + + public DataImporter( + ICommonServices services, + IImportProfileService importProfileService, + ILanguageService languageService, + Func importerFactory, + Lazy emailAccountService, + Lazy emailSender, + Lazy contactDataSettings, + Lazy dataExchangeSettings, + IDbCache dbCache, + IUrlRecordService urlRecordService, + ILocalizedEntityService localizedEntityService) + { + _services = services; + _importProfileService = importProfileService; + _languageService = languageService; + _importerFactory = importerFactory; + _emailAccountService = emailAccountService; + _emailSender = emailSender; + _contactDataSettings = contactDataSettings; + _dataExchangeSettings = dataExchangeSettings; + _dbCache = dbCache; + _urlRecordService = urlRecordService; + _localizedEntityService = localizedEntityService; + + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + private bool HasPermission(DataImporterContext ctx) + { + if (ctx.Request.HasPermission) + { + return true; + } + + var customer = _services.WorkContext.CurrentCustomer; + + if (customer.SystemName == SystemCustomerNames.BackgroundTask) + { + return true; + } + + return _services.Permissions.Authorize(Permissions.Configuration.Import.Execute); + } + + private void LogResults(DataImporterContext ctx) + { var sb = new StringBuilder(); foreach (var item in ctx.Results) @@ -129,74 +124,74 @@ private void LogResults(DataImporterContext ctx) } } } - } + } - private void SendCompletionEmail(DataImporterContext ctx) - { - var emailAccount = _emailAccountService.Value.GetDefaultEmailAccount(); - var smtpContext = new SmtpContext(emailAccount); - var message = new EmailMessage(); + private void SendCompletionEmail(DataImporterContext ctx) + { + var emailAccount = _emailAccountService.Value.GetDefaultEmailAccount(); + var smtpContext = new SmtpContext(emailAccount); + var message = new EmailMessage(); - var store = _services.StoreContext.CurrentStore; - var storeInfo = "{0} ({1})".FormatInvariant(store.Name, store.Url); - var intro = _services.Localization.GetResource("Admin.DataExchange.Import.CompletedEmail.Body").FormatInvariant(storeInfo); - var body = new StringBuilder(intro); - var result = ctx.ExecuteContext.Result; + var store = _services.StoreContext.CurrentStore; + var storeInfo = "{0} ({1})".FormatInvariant(store.Name, store.Url); + var intro = _services.Localization.GetResource("Admin.DataExchange.Import.CompletedEmail.Body").FormatInvariant(storeInfo); + var body = new StringBuilder(intro); + var result = ctx.ExecuteContext.Result; - if (result.LastError.HasValue()) - { - body.AppendFormat("

{0}

", result.LastError); - } + if (result.LastError.HasValue()) + { + body.AppendFormat("

{0}

", result.LastError); + } - body.Append("

"); + body.Append("

"); - body.AppendFormat("

{0}: {1} · {2}: {3}
", - T("Admin.Common.TotalRows"), result.TotalRecords, - T("Admin.Common.Skipped"), result.SkippedRecords); + body.AppendFormat("
{0}: {1} · {2}: {3}
", + T("Admin.Common.TotalRows"), result.TotalRecords, + T("Admin.Common.Skipped"), result.SkippedRecords); - body.AppendFormat("
{0}: {1} · {2}: {3}
", - T("Admin.Common.NewRecords"), result.NewRecords, - T("Admin.Common.Updated"), result.ModifiedRecords); + body.AppendFormat("
{0}: {1} · {2}: {3}
", + T("Admin.Common.NewRecords"), result.NewRecords, + T("Admin.Common.Updated"), result.ModifiedRecords); - body.AppendFormat("
{0}: {1} · {2}: {3}
", - T("Admin.Common.Errors"), result.Errors, - T("Admin.Common.Warnings"), result.Warnings); + body.AppendFormat("
{0}: {1} · {2}: {3}
", + T("Admin.Common.Errors"), result.Errors, + T("Admin.Common.Warnings"), result.Warnings); - body.Append("

"); + body.Append("

"); - message.From = new EmailAddress(emailAccount.Email, emailAccount.DisplayName); + message.From = new EmailAddress(emailAccount.Email, emailAccount.DisplayName); - if (_contactDataSettings.Value.WebmasterEmailAddress.HasValue()) - message.To.Add(new EmailAddress(_contactDataSettings.Value.WebmasterEmailAddress)); + if (_contactDataSettings.Value.WebmasterEmailAddress.HasValue()) + message.To.Add(new EmailAddress(_contactDataSettings.Value.WebmasterEmailAddress)); - if (message.To.Count == 0 && _contactDataSettings.Value.CompanyEmailAddress.HasValue()) - message.To.Add(new EmailAddress(_contactDataSettings.Value.CompanyEmailAddress)); + if (message.To.Count == 0 && _contactDataSettings.Value.CompanyEmailAddress.HasValue()) + message.To.Add(new EmailAddress(_contactDataSettings.Value.CompanyEmailAddress)); - if (message.To.Count == 0) - message.To.Add(new EmailAddress(emailAccount.Email, emailAccount.DisplayName)); + if (message.To.Count == 0) + message.To.Add(new EmailAddress(emailAccount.Email, emailAccount.DisplayName)); - message.Subject = T("Admin.DataExchange.Import.CompletedEmail.Subject").Text.FormatInvariant(ctx.Request.Profile.Name); + message.Subject = T("Admin.DataExchange.Import.CompletedEmail.Subject").Text.FormatInvariant(ctx.Request.Profile.Name); - message.Body = body.ToString(); + message.Body = body.ToString(); - _emailSender.Value.SendEmail(smtpContext, message); + _emailSender.Value.SendEmail(smtpContext, message); - //Core.Infrastructure.EngineContext.Current.Resolve().InsertQueuedEmail(new QueuedEmail - //{ - // From = emailAccount.Email, - // FromName = emailAccount.DisplayName, - // To = message.To.First().Address, - // Subject = message.Subject, - // Body = message.Body, - // CreatedOnUtc = DateTime.UtcNow, - // EmailAccountId = emailAccount.Id, - // SendManually = true - //}); - //_services.DbContext.SaveChanges(); - } + //Core.Infrastructure.EngineContext.Current.Resolve().InsertQueuedEmail(new QueuedEmail + //{ + // From = emailAccount.Email, + // FromName = emailAccount.DisplayName, + // To = message.To.First().Address, + // Subject = message.Subject, + // Body = message.Body, + // CreatedOnUtc = DateTime.UtcNow, + // EmailAccountId = emailAccount.Id, + // SendManually = true + //}); + //_services.DbContext.SaveChanges(); + } - private void ImportCoreInner(DataImporterContext ctx, ImportFile file) - { + private void ImportCoreInner(DataImporterContext ctx, ImportFile file) + { var context = ctx.ExecuteContext; var profile = ctx.Request.Profile; @@ -205,24 +200,24 @@ private void ImportCoreInner(DataImporterContext ctx, ImportFile file) return; } - if (!File.Exists(file.Path)) - { - throw new SmartException($"File does not exist {file.Path}."); - } + if (!File.Exists(file.Path)) + { + throw new SmartException($"File does not exist {file.Path}."); + } - using (var stream = new FileStream(file.Path, FileMode.Open, FileAccess.Read, FileShare.Read)) - { + using (var stream = new FileStream(file.Path, FileMode.Open, FileAccess.Read, FileShare.Read)) + { var csvConfiguration = file.IsCsv ? (new CsvConfigurationConverter().ConvertFrom(profile.FileTypeConfiguration) ?? CsvConfiguration.ExcelFriendlyConfiguration) : CsvConfiguration.ExcelFriendlyConfiguration; context.DataTable = LightweightDataTable.FromFile( file.Name, - stream, - stream.Length, + stream, + stream.Length, csvConfiguration, - profile.Skip, - profile.Take > 0 ? profile.Take : int.MaxValue); + profile.Skip, + profile.Take > 0 ? profile.Take : int.MaxValue); context.ColumnMap = file.RelatedType.HasValue ? new ColumnMap() : ctx.ColumnMap; context.File = file; @@ -249,22 +244,22 @@ private void ImportCoreInner(DataImporterContext ctx, ImportFile file) context.Result.AddWarning("Import aborted. A cancellation has been requested."); } } - } - } + } + } - private void ImportCoreOuter(DataImporterContext ctx) - { + private void ImportCoreOuter(DataImporterContext ctx) + { var customer = _services.WorkContext.CurrentCustomer; var profile = ctx.Request.Profile; var logPath = profile.GetImportLogPath(); - FileSystemHelper.DeleteFile(logPath); + FileSystemHelper.DeleteFile(logPath); - using (var logger = new TraceLogger(logPath)) - { + using (var logger = new TraceLogger(logPath)) + { var scopes = new List(); try - { + { var files = profile.GetImportFiles(profile.ImportRelatedData); var groupedFiles = files.GroupBy(x => x.RelatedType); @@ -275,26 +270,27 @@ private void ImportCoreOuter(DataImporterContext ctx) throw new SmartException("You do not have permission to perform the selected import."); _dbCache.Enabled = false; - scopes.Add(_localizedEntityService.BeginScope()); - scopes.Add(_urlRecordService.BeginScope()); + _services.MediaService.ImagePostProcessingEnabled = false; + scopes.Add(_localizedEntityService.BeginScope()); + scopes.Add(_urlRecordService.BeginScope()); - ctx.Log = logger; + ctx.Log = logger; ctx.Importer = _importerFactory(profile.EntityType); ctx.ExecuteContext.Request = ctx.Request; - ctx.ExecuteContext.DataExchangeSettings = _dataExchangeSettings.Value; - ctx.ExecuteContext.Services = _services; - ctx.ExecuteContext.Log = logger; - ctx.ExecuteContext.Languages = _languageService.GetAllLanguages(true); - ctx.ExecuteContext.UpdateOnly = profile.UpdateOnly; - ctx.ExecuteContext.KeyFieldNames = profile.KeyFieldNames.SplitSafe(","); - ctx.ExecuteContext.ImportFolder = profile.GetImportFolder(); - ctx.ExecuteContext.ExtraData = XmlHelper.Deserialize(profile.ExtraData); + ctx.ExecuteContext.DataExchangeSettings = _dataExchangeSettings.Value; + ctx.ExecuteContext.Services = _services; + ctx.ExecuteContext.Log = logger; + ctx.ExecuteContext.Languages = _languageService.GetAllLanguages(true); + ctx.ExecuteContext.UpdateOnly = profile.UpdateOnly; + ctx.ExecuteContext.KeyFieldNames = profile.KeyFieldNames.SplitSafe(","); + ctx.ExecuteContext.ImportFolder = profile.GetImportFolder(); + ctx.ExecuteContext.ExtraData = XmlHelper.Deserialize(profile.ExtraData); var sb = new StringBuilder(); sb.AppendLine(); sb.AppendLine(new string('-', 40)); - sb.AppendLine("SmartStore.NET: v." + SmartStoreVersion.CurrentFullVersion); + sb.AppendLine("Smartstore: v." + SmartStoreVersion.CurrentFullVersion); sb.AppendLine("Import profile: {0} {1}".FormatInvariant(profile.Name, profile.Id == 0 ? " (volatile)" : $" (Id {profile.Id})")); foreach (var fileGroup in groupedFiles) { @@ -313,87 +309,88 @@ private void ImportCoreOuter(DataImporterContext ctx) fileGroup.Each(x => ImportCoreInner(ctx, x)); } - } - catch (Exception ex) - { + } + catch (Exception ex) + { logger.ErrorsAll(ex); - } - finally - { - try - { - _dbCache.Enabled = true; - scopes.Each(x => x.Dispose()); - - _services.EventPublisher.Publish(new ImportExecutedEvent(ctx.ExecuteContext)); - } - catch (Exception ex) - { + } + finally + { + try + { + _dbCache.Enabled = true; + _services.MediaService.ImagePostProcessingEnabled = true; + scopes.Each(x => x.Dispose()); + + _services.EventPublisher.Publish(new ImportExecutedEvent(ctx.ExecuteContext)); + } + catch (Exception ex) + { logger.ErrorsAll(ex); } - try - { - // Database context sharing problem: if there are entities in modified state left by the provider due to SaveChanges failure, - // then all subsequent SaveChanges would fail too (e.g. IImportProfileService.UpdateImportProfile, IScheduledTaskService.UpdateTask...). - // so whatever it is, detach\dispose all what the tracker still has tracked. + try + { + // Database context sharing problem: if there are entities in modified state left by the provider due to SaveChanges failure, + // then all subsequent SaveChanges would fail too (e.g. IImportProfileService.UpdateImportProfile, IScheduledTaskService.UpdateTask...). + // so whatever it is, detach\dispose all what the tracker still has tracked. - _services.DbContext.DetachAll(false); - } - catch (Exception ex) - { + _services.DbContext.DetachAll(false); + } + catch (Exception ex) + { logger.ErrorsAll(ex); } - try - { - SendCompletionEmail(ctx); - } - catch (Exception ex) - { + try + { + SendCompletionEmail(ctx); + } + catch (Exception ex) + { logger.ErrorsAll(ex); } - try - { - LogResults(ctx); - } - catch (Exception ex) - { - logger.ErrorsAll(ex); - } - - try - { + try + { + LogResults(ctx); + } + catch (Exception ex) + { + logger.ErrorsAll(ex); + } + + try + { if (ctx.Results.TryGetValue(string.Empty, out var result)) { profile.ResultInfo = XmlHelper.Serialize(result.Clone()); _importProfileService.UpdateImportProfile(profile); } - } - catch (Exception ex) - { - logger.ErrorsAll(ex); - } - - try - { - ctx.Request.CustomData.Clear(); + } + catch (Exception ex) + { + logger.ErrorsAll(ex); + } + + try + { + ctx.Request.CustomData.Clear(); ctx.Results.Clear(); ctx.Log = null; - } - catch (Exception ex) - { - logger.ErrorsAll(ex); - } - } - } - } - - public void Import(DataImportRequest request, CancellationToken cancellationToken) - { - Guard.NotNull(request, nameof(request)); - Guard.NotNull(cancellationToken, nameof(cancellationToken)); + } + catch (Exception ex) + { + logger.ErrorsAll(ex); + } + } + } + } + + public void Import(DataImportRequest request, CancellationToken cancellationToken) + { + Guard.NotNull(request, nameof(request)); + Guard.NotNull(cancellationToken, nameof(cancellationToken)); if (request.Profile != null && request.Profile.Enabled) { @@ -402,6 +399,6 @@ public void Import(DataImportRequest request, CancellationToken cancellationToke } cancellationToken.ThrowIfCancellationRequested(); - } - } + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/DataTable/IDataTable.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/DataTable/IDataTable.cs index f35c328fb6..251afcbe6c 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/DataTable/IDataTable.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/DataTable/IDataTable.cs @@ -3,71 +3,71 @@ namespace SmartStore.Services.DataExchange.Import { - public interface IDataColumn - { - string Name { get; } - Type Type { get; } - } + public interface IDataColumn + { + string Name { get; } + Type Type { get; } + } - public interface IDataRow - { - object[] Values { get; } - object this[int index] { get; set; } - object this[string name] { get; set; } + public interface IDataRow + { + object[] Values { get; } + object this[int index] { get; set; } + object this[string name] { get; set; } - IDataTable Table { get; } - } + IDataTable Table { get; } + } - public interface IDataTable - { - bool HasColumn(string name); - int GetColumnIndex(string name); + public interface IDataTable + { + bool HasColumn(string name); + int GetColumnIndex(string name); IList Columns { get; } - IList Rows { get; } - } + IList Rows { get; } + } - public static class IDataRowExtensions - { - public static object GetValue(this IDataRow row, int index) - { - return row[index]; - } + public static class IDataRowExtensions + { + public static object GetValue(this IDataRow row, int index) + { + return row[index]; + } - public static object GetValue(this IDataRow row, string name) - { - return row[name]; - } + public static object GetValue(this IDataRow row, string name) + { + return row[name]; + } - public static void SetValue(this IDataRow row, int index, object value) - { - row[index] = value; - } + public static void SetValue(this IDataRow row, int index, object value) + { + row[index] = value; + } - public static void SetValue(this IDataRow row, string name, object value) - { - row[name] = value; - } + public static void SetValue(this IDataRow row, string name, object value) + { + row[name] = value; + } - public static bool TryGetValue(this IDataRow row, string name, out object value) - { - value = null; + public static bool TryGetValue(this IDataRow row, string name, out object value) + { + value = null; - var index = row.Table.GetColumnIndex(name); - if (index < 0) - return false; + var index = row.Table.GetColumnIndex(name); + if (index < 0) + return false; - value = row[index]; - return true; - } + value = row[index]; + return true; + } - public static bool TrySetValue(this IDataRow row, string name, object value) - { - var index = row.Table.GetColumnIndex(name); - if (index < 0) - return false; + public static bool TrySetValue(this IDataRow row, string name, object value) + { + var index = row.Table.GetColumnIndex(name); + if (index < 0) + return false; - row[index] = value; - return true; - } - } + row[index] = value; + return true; + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/DataTable/LightweightDataTable.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/DataTable/LightweightDataTable.cs index f1a33eb808..628dc0894c 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/DataTable/LightweightDataTable.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/DataTable/LightweightDataTable.cs @@ -12,395 +12,377 @@ namespace SmartStore.Services.DataExchange.Import { - public class LightweightDataTable : IDataTable - { - private readonly IList _columns; - private readonly IList _rows; - private readonly IDictionary _columnIndexes; - private readonly IDictionary _alternativeColumnIndexes; - - public LightweightDataTable(IList columns, IList data) - { - Guard.NotNull(columns, nameof(columns)); - Guard.NotNull(data, nameof(data)); - - if (columns.Select(x => x.Name.ToLower()).Distinct().ToArray().Length != columns.Count) - { - throw Error.Argument("columns", "The columns collection cannot contain duplicate column names."); - } - - _columns = new ReadOnlyCollection(columns); - - TrimData(data); - - var rows = data.Select(x => new LightweightDataRow(this, x)).Cast().ToList(); - _rows = new ReadOnlyCollection(rows); - - _columnIndexes = new Dictionary(StringComparer.OrdinalIgnoreCase); - _alternativeColumnIndexes = new Dictionary(StringComparer.OrdinalIgnoreCase); - - for (var i = 0; i < columns.Count; i++) - { - var name = columns[i].Name; - var alternativeName = GetAlternativeColumnNameFor(name); - - _columnIndexes[name] = i; - - if (!alternativeName.IsCaseInsensitiveEqual(name)) - _alternativeColumnIndexes[alternativeName] = i; - } - } - - private static void TrimData(IList data) - { - // When a user deletes content instead of whole rows from an excel sheet, - // our data table contains completely empty rows at the end. - // Here we get rid of them as they are absolutely useless. - for (int i = data.Count - 1; i >= 0; i--) - { - var allColumnsEmpty = data[i].All(x => x == null || x == DBNull.Value); - if (allColumnsEmpty) - { - data.RemoveAt(i); - //i--; - } - else - { - // get out here on the first occurence of a NON-empty row - break; - } - } - } - - public bool HasColumn(string name) - { - if (name.HasValue()) - { - return (_columnIndexes.ContainsKey(name) || _alternativeColumnIndexes.ContainsKey(name)); - } - - return false; - } - - public int GetColumnIndex(string name) - { - int index; - - if (name.HasValue()) - { - if (_columnIndexes.TryGetValue(name, out index)) - return index; - - if (_alternativeColumnIndexes.TryGetValue(name, out index)) - return index; - } - - return -1; - } - - public IList Columns - { - get - { - return _columns; - } - } - - public IList Rows - { - get - { - return _rows; - } - } - - public static string GetAlternativeColumnNameFor(string name) - { - if (name.IsEmpty()) - return name; - - return name - .Replace(" ", "") - .Replace("-", "") - .Replace("_", ""); - } - - public static IDataTable FromPostedFile( - HttpPostedFileBase file, - int skip = 0, - int take = int.MaxValue) - { - Guard.NotNull(file, nameof(file)); - - return FromFile(file.FileName, file.InputStream, file.ContentLength, new CsvConfiguration(), skip, take); - } - - public static IDataTable FromPostedFile( - HttpPostedFileBase file, - CsvConfiguration configuration, - int skip = 0, - int take = int.MaxValue) - { - Guard.NotNull(file, nameof(file)); - - return FromFile(file.FileName, file.InputStream, file.ContentLength, configuration, skip, take); - } - - public static IDataTable FromFile( - string fileName, - Stream stream, - long contentLength, - CsvConfiguration configuration, - int skip = 0, - int take = int.MaxValue) - { - Guard.NotEmpty(fileName, nameof(fileName)); - Guard.NotNull(stream, nameof(stream)); - Guard.NotNull(configuration, nameof(configuration)); - - if (contentLength == 0) - { - throw Error.Argument("fileName", "The posted file '{0}' does not contain any data.".FormatInvariant(fileName)); - } - - IDataReader dataReader = null; - - try - { - var fileExt = System.IO.Path.GetExtension(fileName).ToLowerInvariant(); - - switch (fileExt) - { - case ".xlsx": - dataReader = new ExcelDataReader(stream, true); // TODO: let the user specify if excel file has headers - break; - default: - dataReader = new CsvDataReader(new StreamReader(stream), configuration); - break; - } - - var table = LightweightDataTable.FromDataReader(dataReader, skip, take); - - if (table.Columns.Count == 0 || table.Rows.Count == 0) - { - throw Error.InvalidOperation("The posted file '{0}' does not contain any columns or data rows.".FormatInvariant(fileName)); - } - - return table; - } - catch (Exception ex) - { - throw ex; - } - finally - { - if (dataReader != null) - { - if (!dataReader.IsClosed) - { - dataReader.Dispose(); - } - dataReader = null; - } - } - } - - public static IDataTable FromDataReader( - IDataReader reader, - int skip = 0, - int take = int.MaxValue) - { - Guard.NotNull(reader, nameof(reader)); - - if (reader.IsClosed) - throw new ArgumentException("This operation is invalid when the reader is closed.", "reader"); - - var columns = new List(reader.FieldCount); - var data = new List(); - - var schema = reader.GetSchemaTable(); - - var nameCol = schema.Columns[SchemaTableColumn.ColumnName]; - var typeCol = schema.Columns[SchemaTableColumn.DataType]; - - foreach (DataRow schemaRow in schema.Rows) - { - var column = new LightweightDataColumn((string)schemaRow[nameCol], (Type)schemaRow[typeCol]); - columns.Add(column); - } - - var fieldCount = reader.FieldCount; - - take = Math.Min(take, int.MaxValue - skip); - - int i = -1; - while (reader.Read()) - { - i++; - - if (skip > i) - continue; - - if (i >= skip + take) - break; - - var values = new object[fieldCount]; - reader.GetValues(values); - data.Add(values); - } - - var table = new LightweightDataTable(columns, data); - - return table; - } - } - - internal class LightweightDataRow : DynamicObject, IDataRow - { - private readonly IDataTable _table; - private readonly object[] _values; - - public LightweightDataRow(IDataTable table, object[] values) - { - Guard.NotNull(values, nameof(values)); - - if (table.Columns.Count != values.Length) - { - throw new ArgumentOutOfRangeException( - "values", - "The number of row values must match the number of columns. Expected: {0}, actual: {1}".FormatInvariant(table.Columns.Count, values.Length)); - } - - _table = table; - _values = values; - } - - public IDataTable Table - { - get { return _table; } - } - - public object[] Values - { - get { return _values; } - } - - public object this[string name] - { - get - { - var index = _table.GetColumnIndex(name); - if (index < 0) - throw new KeyNotFoundException(); - - return _values[index]; - } - set - { - var index = _table.GetColumnIndex(name); - if (index < 0) - throw new KeyNotFoundException(); - - _values[index] = value; - } - } - - public object this[int index] - { - get - { - ValidateColumnIndex(index); - return _values[index]; - } - set - { - ValidateColumnIndex(index); - _values[index] = value; - } - } - - private void ValidateColumnIndex(int index) - { - if (index < 0 || index >= _table.Columns.Count) - { - throw new ArgumentOutOfRangeException("index", index, - "Column index must be included within [0, {0}], but specified column index was: '{1}'.".FormatInvariant(_table.Columns.Count, index)); - } - } - - public override IEnumerable GetDynamicMemberNames() - { - return _table.Columns.Select(x => x.Name); - } - - - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - result = null; - - if (this.TryGetValue(binder.Name, out result)) - { - return true; - } - - return false; - } - - public override bool TrySetMember(SetMemberBinder binder, object value) - { - return this.TrySetValue(binder.Name, value); - } - - public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) - { - result = null; - - try - { - result = _values[(int)indexes[0]]; - return true; - } - catch - { - return false; - } - } - - public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) - { - try - { - _values[(int)indexes[0]] = value; - return true; - } - catch - { - return false; - } - } - } - - internal class LightweightDataColumn : IDataColumn - { - public LightweightDataColumn(string name, Type type) - { - Guard.NotEmpty(name, nameof(name)); - Guard.NotNull(type, nameof(type)); - - this.Name = name; - this.Type = type; - } - - public string Name - { - get; - private set; - } - - public Type Type - { - get; - private set; - } - } + public class LightweightDataTable : IDataTable + { + private readonly IList _columns; + private readonly IList _rows; + private readonly IDictionary _columnIndexes; + private readonly IDictionary _alternativeColumnIndexes; + + public LightweightDataTable(IList columns, IList data) + { + Guard.NotNull(columns, nameof(columns)); + Guard.NotNull(data, nameof(data)); + + if (columns.Select(x => x.Name.ToLower()).Distinct().ToArray().Length != columns.Count) + { + throw Error.Argument("columns", "The columns collection cannot contain duplicate column names."); + } + + _columns = new ReadOnlyCollection(columns); + + TrimData(data); + + var rows = data.Select(x => new LightweightDataRow(this, x)).Cast().ToList(); + _rows = new ReadOnlyCollection(rows); + + _columnIndexes = new Dictionary(StringComparer.OrdinalIgnoreCase); + _alternativeColumnIndexes = new Dictionary(StringComparer.OrdinalIgnoreCase); + + for (var i = 0; i < columns.Count; i++) + { + var name = columns[i].Name; + var alternativeName = GetAlternativeColumnNameFor(name); + + _columnIndexes[name] = i; + + if (!alternativeName.IsCaseInsensitiveEqual(name)) + _alternativeColumnIndexes[alternativeName] = i; + } + } + + private static void TrimData(IList data) + { + // When a user deletes content instead of whole rows from an excel sheet, + // our data table contains completely empty rows at the end. + // Here we get rid of them as they are absolutely useless. + for (int i = data.Count - 1; i >= 0; i--) + { + var allColumnsEmpty = data[i].All(x => x == null || x == DBNull.Value); + if (allColumnsEmpty) + { + data.RemoveAt(i); + //i--; + } + else + { + // get out here on the first occurence of a NON-empty row + break; + } + } + } + + public bool HasColumn(string name) + { + if (name.HasValue()) + { + return (_columnIndexes.ContainsKey(name) || _alternativeColumnIndexes.ContainsKey(name)); + } + + return false; + } + + public int GetColumnIndex(string name) + { + int index; + + if (name.HasValue()) + { + if (_columnIndexes.TryGetValue(name, out index)) + return index; + + if (_alternativeColumnIndexes.TryGetValue(name, out index)) + return index; + } + + return -1; + } + + public IList Columns => _columns; + + public IList Rows => _rows; + + public static string GetAlternativeColumnNameFor(string name) + { + if (name.IsEmpty()) + return name; + + return name + .Replace(" ", "") + .Replace("-", "") + .Replace("_", ""); + } + + public static IDataTable FromPostedFile( + HttpPostedFileBase file, + int skip = 0, + int take = int.MaxValue) + { + Guard.NotNull(file, nameof(file)); + + return FromFile(file.FileName, file.InputStream, file.ContentLength, new CsvConfiguration(), skip, take); + } + + public static IDataTable FromPostedFile( + HttpPostedFileBase file, + CsvConfiguration configuration, + int skip = 0, + int take = int.MaxValue) + { + Guard.NotNull(file, nameof(file)); + + return FromFile(file.FileName, file.InputStream, file.ContentLength, configuration, skip, take); + } + + public static IDataTable FromFile( + string fileName, + Stream stream, + long contentLength, + CsvConfiguration configuration, + int skip = 0, + int take = int.MaxValue) + { + Guard.NotEmpty(fileName, nameof(fileName)); + Guard.NotNull(stream, nameof(stream)); + Guard.NotNull(configuration, nameof(configuration)); + + if (contentLength == 0) + { + throw Error.Argument("fileName", "The posted file '{0}' does not contain any data.".FormatInvariant(fileName)); + } + + IDataReader dataReader = null; + + try + { + var fileExt = System.IO.Path.GetExtension(fileName).ToLowerInvariant(); + + switch (fileExt) + { + case ".xlsx": + dataReader = new ExcelDataReader(stream, true); // TODO: let the user specify if excel file has headers + break; + default: + dataReader = new CsvDataReader(new StreamReader(stream), configuration); + break; + } + + var table = LightweightDataTable.FromDataReader(dataReader, skip, take); + + if (table.Columns.Count == 0 || table.Rows.Count == 0) + { + throw Error.InvalidOperation("The posted file '{0}' does not contain any columns or data rows.".FormatInvariant(fileName)); + } + + return table; + } + catch (Exception ex) + { + throw ex; + } + finally + { + if (dataReader != null) + { + if (!dataReader.IsClosed) + { + dataReader.Dispose(); + } + dataReader = null; + } + } + } + + public static IDataTable FromDataReader( + IDataReader reader, + int skip = 0, + int take = int.MaxValue) + { + Guard.NotNull(reader, nameof(reader)); + + if (reader.IsClosed) + throw new ArgumentException("This operation is invalid when the reader is closed.", "reader"); + + var columns = new List(reader.FieldCount); + var data = new List(); + + var schema = reader.GetSchemaTable(); + + var nameCol = schema.Columns[SchemaTableColumn.ColumnName]; + var typeCol = schema.Columns[SchemaTableColumn.DataType]; + + foreach (DataRow schemaRow in schema.Rows) + { + var column = new LightweightDataColumn((string)schemaRow[nameCol], (Type)schemaRow[typeCol]); + columns.Add(column); + } + + var fieldCount = reader.FieldCount; + + take = Math.Min(take, int.MaxValue - skip); + + int i = -1; + while (reader.Read()) + { + i++; + + if (skip > i) + continue; + + if (i >= skip + take) + break; + + var values = new object[fieldCount]; + reader.GetValues(values); + data.Add(values); + } + + var table = new LightweightDataTable(columns, data); + + return table; + } + } + + internal class LightweightDataRow : DynamicObject, IDataRow + { + private readonly IDataTable _table; + private readonly object[] _values; + + public LightweightDataRow(IDataTable table, object[] values) + { + Guard.NotNull(values, nameof(values)); + + if (table.Columns.Count != values.Length) + { + throw new ArgumentOutOfRangeException( + "values", + "The number of row values must match the number of columns. Expected: {0}, actual: {1}".FormatInvariant(table.Columns.Count, values.Length)); + } + + _table = table; + _values = values; + } + + public IDataTable Table => _table; + + public object[] Values => _values; + + public object this[string name] + { + get + { + var index = _table.GetColumnIndex(name); + if (index < 0) + throw new KeyNotFoundException(); + + return _values[index]; + } + set + { + var index = _table.GetColumnIndex(name); + if (index < 0) + throw new KeyNotFoundException(); + + _values[index] = value; + } + } + + public object this[int index] + { + get + { + ValidateColumnIndex(index); + return _values[index]; + } + set + { + ValidateColumnIndex(index); + _values[index] = value; + } + } + + private void ValidateColumnIndex(int index) + { + if (index < 0 || index >= _table.Columns.Count) + { + throw new ArgumentOutOfRangeException("index", index, + "Column index must be included within [0, {0}], but specified column index was: '{1}'.".FormatInvariant(_table.Columns.Count, index)); + } + } + + public override IEnumerable GetDynamicMemberNames() + { + return _table.Columns.Select(x => x.Name); + } + + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + result = null; + + if (this.TryGetValue(binder.Name, out result)) + { + return true; + } + + return false; + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + return this.TrySetValue(binder.Name, value); + } + + public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) + { + result = null; + + try + { + result = _values[(int)indexes[0]]; + return true; + } + catch + { + return false; + } + } + + public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) + { + try + { + _values[(int)indexes[0]] = value; + return true; + } + catch + { + return false; + } + } + } + + internal class LightweightDataColumn : IDataColumn + { + public LightweightDataColumn(string name, Type type) + { + Guard.NotEmpty(name, nameof(name)); + Guard.NotNull(type, nameof(type)); + + this.Name = name; + this.Type = type; + } + + public string Name + { + get; + private set; + } + + public Type Type + { + get; + private set; + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/EntityImporterBase.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/EntityImporterBase.cs index 4452f00382..e33149ae8e 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/EntityImporterBase.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/EntityImporterBase.cs @@ -3,10 +3,10 @@ using System.IO; using System.Linq; using System.Linq.Expressions; -using System.Net.Mime; using Autofac; using SmartStore.Core; using SmartStore.Core.Data; +using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Seo; using SmartStore.Core.Domain.Stores; @@ -19,53 +19,56 @@ namespace SmartStore.Services.DataExchange.Import { public abstract class EntityImporterBase : IEntityImporter - { - private const string IMAGE_DOWNLOAD_FOLDER = @"Content\DownloadedImages"; - - public DateTime UtcNow - { - get; - private set; - } - + { + private const string IMAGE_DOWNLOAD_FOLDER = @"Content\DownloadedImages"; + + public DateTime UtcNow + { + get; + private set; + } + + /// + /// URL > file name. To avoid downloading image several times. + /// public Dictionary DownloadedItems - { - get; - private set; - } - - public string ImageDownloadFolder - { - get; - private set; - } - - public string ImageFolder - { - get; - private set; - } - - public FileDownloadManagerContext DownloaderContext - { - get; - private set; - } - - public void Execute(ImportExecuteContext context) - { - Import(context); - } - - protected abstract void Import(ImportExecuteContext context); - - protected void Initialize(ImportExecuteContext context) - { - UtcNow = DateTime.UtcNow; - DownloadedItems = new Dictionary(); - ImageDownloadFolder = Path.Combine(context.ImportFolder, IMAGE_DOWNLOAD_FOLDER); - - var settings = context.DataExchangeSettings; + { + get; + private set; + } + + public string ImageDownloadFolder + { + get; + private set; + } + + public string ImageFolder + { + get; + private set; + } + + public FileDownloadManagerContext DownloaderContext + { + get; + private set; + } + + public void Execute(ImportExecuteContext context) + { + Import(context); + } + + protected abstract void Import(ImportExecuteContext context); + + protected void Initialize(ImportExecuteContext context) + { + UtcNow = DateTime.UtcNow; + DownloadedItems = new Dictionary(); + ImageDownloadFolder = Path.Combine(context.ImportFolder, IMAGE_DOWNLOAD_FOLDER); + + var settings = context.DataExchangeSettings; ImageFolder = settings.ImageImportFolder.HasValue() ? Path.Combine(context.ImportFolder, settings.ImageImportFolder) @@ -76,222 +79,234 @@ protected void Initialize(ImportExecuteContext context) System.IO.Directory.CreateDirectory(ImageDownloadFolder); } - DownloaderContext = new FileDownloadManagerContext - { - Timeout = TimeSpan.FromMinutes(settings.ImageDownloadTimeout), - Logger = context.Log, - CancellationToken = context.CancellationToken - }; - - context.Result.TotalRecords = context.DataSegmenter.TotalRows; - } - - public FileDownloadManagerItem CreateDownloadImage(ImportExecuteContext context, string urlOrPath, string seoName, int displayOrder) - { - var item = new FileDownloadManagerItem - { - Id = displayOrder, - DisplayOrder = displayOrder, - MimeType = MimeTypes.MapNameToMimeType(urlOrPath) - }; - - if (item.MimeType.IsEmpty()) - { - item.MimeType = MediaTypeNames.Image.Jpeg; - } - - if (urlOrPath.IsWebUrl()) - { - item.Url = context.Services.WebHelper.ModifyQueryString(urlOrPath, "q=100", null); - item.FileName = "{0}-{1}".FormatInvariant(seoName, item.Id).ToValidFileName(); - - if (DownloadedItems.ContainsKey(urlOrPath)) - { - item.Path = Path.Combine(ImageDownloadFolder, DownloadedItems[urlOrPath]); - item.Success = true; - } - else - { - var extension = MimeTypes.MapMimeTypeToExtension(item.MimeType).NullEmpty() ?? ".jpg"; - - item.Path = Path.Combine(ImageDownloadFolder, item.FileName + extension.EnsureStartsWith(".")); - } - } - else if (Path.IsPathRooted(urlOrPath)) - { - item.Path = urlOrPath; - item.Success = true; - } - else - { - item.Path = Path.Combine(ImageFolder, urlOrPath); - item.Success = true; - } - - return item; - } - - public void Succeeded(FileDownloadManagerItem item) - { - if ((item.Success ?? false) && item.Url.HasValue() && !DownloadedItems.ContainsKey(item.Url)) - { - DownloadedItems.Add(item.Url, Path.GetFileName(item.Path)); - } - } - - protected virtual int ProcessLocalizations( - ImportExecuteContext context, - IEnumerable> batch, - IDictionary>> localizableProperties) where TEntity : BaseEntity, ILocalizedEntity - { - Guard.NotNull(context, nameof(context)); - Guard.NotNull(batch, nameof(batch)); - Guard.NotNull(localizableProperties, nameof(localizableProperties)); - - // Perf: determine whether our localizable properties actually have - // counterparts in the source BEFORE import batch begins. This way we spare ourself - // to query over and over for values. - var localizedProps = (from kvp in localizableProperties - where context.DataSegmenter.GetColumnIndexes(kvp.Key).Length > 0 - select kvp.Key).ToArray(); - - if (localizedProps.Length == 0) - { - return 0; - } - - var localizedEntityService = context.Services.Resolve(); - - bool shouldSave = false; - - foreach (var row in batch) - { - foreach (var prop in localizedProps) - { - var lambda = localizableProperties[prop]; - foreach (var lang in context.Languages) - { - var code = lang.UniqueSeoCode; - string value; - - if (row.TryGetDataValue(prop /* ColumnName */, code, out value)) - { - localizedEntityService.SaveLocalizedValue(row.Entity, lambda, value, lang.Id); - shouldSave = true; - } - } - } - } - - if (shouldSave) - { - // commit whole batch at once - return context.Services.DbContext.SaveChanges(); - } - - return 0; - } - - protected virtual int ProcessStoreMappings( - ImportExecuteContext context, - IEnumerable> batch) where TEntity : BaseEntity, IStoreMappingSupported - { - var storeMappingService = context.Services.Resolve(); - var storeMappingRepository = context.Services.Resolve>(); - - storeMappingRepository.AutoCommitEnabled = false; - - foreach (var row in batch) - { - var storeIds = row.GetDataValue>("StoreIds"); - if (!storeIds.IsNullOrEmpty()) - { - storeMappingService.SaveStoreMappings(row.Entity, storeIds.ToArray()); - } - } - - // commit whole batch at once - return context.Services.DbContext.SaveChanges(); - } - - protected virtual int ProcessSlugs( - ImportExecuteContext context, - IEnumerable> batch, - string entityName) where TEntity : BaseEntity, ISlugSupported - { - var slugMap = new Dictionary(); - UrlRecord urlRecord = null; - - var urlRecordService = context.Services.Resolve(); - var urlRecordRepository = context.Services.Resolve>(); - var seoSettings = context.Services.Resolve(); - - Func slugLookup = ((s) => - { - return (slugMap.ContainsKey(s) ? slugMap[s] : null); - }); - - foreach (var row in batch) - { - try - { - string seName = null; - string localizedName = null; - - if (row.TryGetDataValue("SeName", out seName) || row.IsNew || row.NameChanged) - { - seName = row.Entity.ValidateSeName(seName, row.EntityDisplayName, true, urlRecordService, seoSettings, extraSlugLookup: slugLookup); - - if (row.IsNew) - { - // dont't bother validating SeName for new entities. - urlRecord = new UrlRecord - { - EntityId = row.Entity.Id, - EntityName = entityName, - Slug = seName, - LanguageId = 0, - IsActive = true, - }; - urlRecordRepository.Insert(urlRecord); - } - else - { - urlRecord = urlRecordService.SaveSlug(row.Entity, seName, 0); - } - - if (urlRecord != null) - { - // a new record was inserted to the store: keep track of it for this batch. - slugMap[seName] = urlRecord; - } - } - - // process localized SeNames - foreach (var lang in context.Languages) - { - var hasSeName = row.TryGetDataValue("SeName", lang.UniqueSeoCode, out seName); - var hasLocalizedName = row.TryGetDataValue("Name", lang.UniqueSeoCode, out localizedName); - - if (hasSeName || hasLocalizedName) - { - seName = row.Entity.ValidateSeName(seName, localizedName, false, urlRecordService, seoSettings, lang.Id, slugLookup); - urlRecord = urlRecordService.SaveSlug(row.Entity, seName, lang.Id); - if (urlRecord != null) - { - slugMap[seName] = urlRecord; - } - } - } - } - catch (Exception exception) - { - context.Result.AddWarning(exception.Message, row.GetRowInfo(), "SeName"); - } - } - - // commit whole batch at once - return context.Services.DbContext.SaveChanges(); - } - } + DownloaderContext = new FileDownloadManagerContext + { + Timeout = TimeSpan.FromMinutes(settings.ImageDownloadTimeout), + Logger = context.Log, + CancellationToken = context.CancellationToken + }; + + context.Result.TotalRecords = context.DataSegmenter.TotalRows; + } + + public FileDownloadManagerItem CreateDownloadImage(ImportExecuteContext context, string urlOrPath, int displayOrder) + { + try + { + var item = new FileDownloadManagerItem + { + Id = displayOrder, + DisplayOrder = displayOrder + }; + + if (urlOrPath.IsWebUrl()) + { + // We append quality to avoid importing of image duplicates. + item.Url = context.Services.WebHelper.ModifyQueryString(urlOrPath, "q=100", null); + + if (DownloadedItems.ContainsKey(urlOrPath)) + { + // URL has already been downloaded. + item.Success = true; + item.FileName = DownloadedItems[urlOrPath]; + } + else + { + var localPath = string.Empty; + + try + { + // Exclude query string parts! + localPath = new Uri(urlOrPath).LocalPath; + } + catch { } + + item.FileName = Path.GetFileName(localPath).ToValidFileName().NullEmpty() ?? Path.GetRandomFileName(); + } + + item.Path = Path.Combine(ImageDownloadFolder, item.FileName); + } + else + { + item.Success = true; + item.FileName = Path.GetFileName(urlOrPath).ToValidFileName().NullEmpty() ?? Path.GetRandomFileName(); + + item.Path = Path.IsPathRooted(urlOrPath) + ? urlOrPath + : Path.Combine(ImageFolder, urlOrPath); + } + + item.MimeType = MimeTypes.MapNameToMimeType(item.FileName); + + return item; + } + catch + { + context.Result.AddWarning($"Failed to prepare image download for '{urlOrPath.NaIfEmpty()}'. Skipping file."); + + return null; + } + } + + public void Succeeded(FileDownloadManagerItem item) + { + if ((item.Success ?? false) && item.Url.HasValue() && !DownloadedItems.ContainsKey(item.Url)) + { + DownloadedItems.Add(item.Url, Path.GetFileName(item.Path)); + } + } + + protected virtual int ProcessLocalizations( + ImportExecuteContext context, + IEnumerable> batch, + IDictionary>> localizableProperties) where TEntity : BaseEntity, ILocalizedEntity + { + Guard.NotNull(context, nameof(context)); + Guard.NotNull(batch, nameof(batch)); + Guard.NotNull(localizableProperties, nameof(localizableProperties)); + + // Perf: determine whether our localizable properties actually have + // counterparts in the source BEFORE import batch begins. This way we spare ourself + // to query over and over for values. + var localizedProps = (from kvp in localizableProperties + where context.DataSegmenter.GetColumnIndexes(kvp.Key).Length > 0 + select kvp.Key).ToArray(); + + if (localizedProps.Length == 0) + { + return 0; + } + + var localizedEntityService = context.Services.Resolve(); + + bool shouldSave = false; + + foreach (var row in batch) + { + foreach (var prop in localizedProps) + { + var lambda = localizableProperties[prop]; + foreach (var lang in context.Languages) + { + var code = lang.UniqueSeoCode; + string value; + + if (row.TryGetDataValue(prop /* ColumnName */, code, out value)) + { + localizedEntityService.SaveLocalizedValue(row.Entity, lambda, value, lang.Id); + shouldSave = true; + } + } + } + } + + if (shouldSave) + { + // commit whole batch at once + return context.Services.DbContext.SaveChanges(); + } + + return 0; + } + + protected virtual int ProcessStoreMappings( + ImportExecuteContext context, + IEnumerable> batch) where TEntity : BaseEntity, IStoreMappingSupported + { + var storeMappingService = context.Services.Resolve(); + var storeMappingRepository = context.Services.Resolve>(); + + storeMappingRepository.AutoCommitEnabled = false; + + foreach (var row in batch) + { + var storeIds = row.GetDataValue>("StoreIds"); + if (!storeIds.IsNullOrEmpty()) + { + storeMappingService.SaveStoreMappings(row.Entity, storeIds.ToArray()); + } + } + + // commit whole batch at once + return context.Services.DbContext.SaveChanges(); + } + + protected virtual int ProcessSlugs( + ImportExecuteContext context, + IEnumerable> batch, + string entityName) where TEntity : BaseEntity, ISlugSupported + { + var slugMap = new Dictionary(); + UrlRecord urlRecord = null; + + var urlRecordService = context.Services.Resolve(); + var seoSettings = context.Services.Resolve(); + + UrlRecord slugLookup(string s) + { + return slugMap.ContainsKey(s) ? slugMap[s] : null; + } + + foreach (var row in batch) + { + try + { + string localizedName = null; + + if (row.TryGetDataValue("SeName", out string seName) || row.IsNew || row.NameChanged) + { + seName = row.Entity.ValidateSeName(seName, row.EntityDisplayName, true, urlRecordService, seoSettings, extraSlugLookup: slugLookup); + + if (row.IsNew) + { + // Don't bother validating SeName for new entities. + urlRecord = new UrlRecord + { + EntityId = row.Entity.Id, + EntityName = entityName, + Slug = seName, + LanguageId = 0, + IsActive = true, + }; + urlRecordService.InsertUrlRecord(urlRecord); + } + else + { + urlRecord = urlRecordService.SaveSlug(row.Entity, seName, 0); + } + + if (urlRecord != null) + { + // A new record was inserted to the store: keep track of it for this batch. + slugMap[seName] = urlRecord; + } + } + + // Process localized SeNames. + foreach (var lang in context.Languages) + { + var hasSeName = row.TryGetDataValue("SeName", lang.UniqueSeoCode, out seName); + var hasLocalizedName = row.TryGetDataValue("Name", lang.UniqueSeoCode, out localizedName); + + if (hasSeName || hasLocalizedName) + { + seName = row.Entity.ValidateSeName(seName, localizedName, false, urlRecordService, seoSettings, lang.Id, slugLookup); + urlRecord = urlRecordService.SaveSlug(row.Entity, seName, lang.Id); + if (urlRecord != null) + { + slugMap[seName] = urlRecord; + } + } + } + } + catch (Exception ex) + { + context.Result.AddWarning(ex.Message, row.GetRowInfo(), "SeName"); + } + } + + // Commit whole batch at once. + return context.Services.DbContext.SaveChanges(); + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/Events/ImportExecutedEvent.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/Events/ImportExecutedEvent.cs index c59979e207..073a74ba18 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/Events/ImportExecutedEvent.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/Events/ImportExecutedEvent.cs @@ -2,19 +2,19 @@ namespace SmartStore.Services.DataExchange.Import.Events { - public class ImportExecutedEvent - { - public ImportExecutedEvent(ImportExecuteContext context) - { - Guard.NotNull(context, nameof(context)); + public class ImportExecutedEvent + { + public ImportExecutedEvent(ImportExecuteContext context) + { + Guard.NotNull(context, nameof(context)); - Context = context; - } + Context = context; + } - public ImportExecuteContext Context - { - get; - private set; - } - } + public ImportExecuteContext Context + { + get; + private set; + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/Events/ImportExecutingEvent.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/Events/ImportExecutingEvent.cs index 3c0160b306..537606f9c2 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/Events/ImportExecutingEvent.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/Events/ImportExecutingEvent.cs @@ -2,19 +2,19 @@ namespace SmartStore.Services.DataExchange.Import.Events { - public class ImportExecutingEvent - { - public ImportExecutingEvent(ImportExecuteContext context) - { - Guard.NotNull(context, nameof(context)); + public class ImportExecutingEvent + { + public ImportExecutingEvent(ImportExecuteContext context) + { + Guard.NotNull(context, nameof(context)); - Context = context; - } + Context = context; + } - public ImportExecuteContext Context - { - get; - private set; - } - } + public ImportExecuteContext Context + { + get; + private set; + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/IDataImporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/IDataImporter.cs index 9eee0876b3..dfd6d6cd5c 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/IDataImporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/IDataImporter.cs @@ -6,41 +6,41 @@ namespace SmartStore.Services.DataExchange.Import { - public interface IDataImporter - { - void Import(DataImportRequest request, CancellationToken cancellationToken); - } + public interface IDataImporter + { + void Import(DataImportRequest request, CancellationToken cancellationToken); + } - public class DataImportRequest - { - private readonly static ProgressValueSetter _voidProgressValueSetter = DataImportRequest.SetProgress; + public class DataImportRequest + { + private readonly static ProgressValueSetter _voidProgressValueSetter = DataImportRequest.SetProgress; - public DataImportRequest(ImportProfile profile) - { - Guard.NotNull(profile, nameof(profile)); + public DataImportRequest(ImportProfile profile) + { + Guard.NotNull(profile, nameof(profile)); - Profile = profile; - ProgressValueSetter = _voidProgressValueSetter; + Profile = profile; + ProgressValueSetter = _voidProgressValueSetter; - EntitiesToImport = new List(); - CustomData = new Dictionary(StringComparer.OrdinalIgnoreCase); - } + EntitiesToImport = new List(); + CustomData = new Dictionary(StringComparer.OrdinalIgnoreCase); + } - public ImportProfile Profile { get; private set; } + public ImportProfile Profile { get; private set; } - public ProgressValueSetter ProgressValueSetter { get; set; } + public ProgressValueSetter ProgressValueSetter { get; set; } - public bool HasPermission { get; set; } + public bool HasPermission { get; set; } - public IList EntitiesToImport { get; set; } + public IList EntitiesToImport { get; set; } - public IDictionary CustomData { get; private set; } + public IDictionary CustomData { get; private set; } - private static void SetProgress(int val, int max, string msg) - { - // do nothing - } - } + private static void SetProgress(int val, int max, string msg) + { + // do nothing + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/IEntityImporter.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/IEntityImporter.cs index bc64b63531..f4b69ac913 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/IEntityImporter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/IEntityImporter.cs @@ -1,7 +1,7 @@ namespace SmartStore.Services.DataExchange.Import { - public partial interface IEntityImporter - { - void Execute(ImportExecuteContext context); - } + public partial interface IEntityImporter + { + void Execute(ImportExecuteContext context); + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/IImportProfileService.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/IImportProfileService.cs index 6f7b7d4f8e..4da8a9e535 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/IImportProfileService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/IImportProfileService.cs @@ -5,62 +5,62 @@ namespace SmartStore.Services.DataExchange.Import { - public interface IImportProfileService - { - /// - /// Gets a new profile name - /// - /// Entity type - /// Suggestion for a new profile name - string GetNewProfileName(ImportEntityType entityType); + public interface IImportProfileService + { + /// + /// Gets a new profile name + /// + /// Entity type + /// Suggestion for a new profile name + string GetNewProfileName(ImportEntityType entityType); - /// - /// Inserts an import profile - /// - /// Name of the import file - /// Profile name - /// Entity type - /// Inserted import profile - ImportProfile InsertImportProfile(string fileName, string name, ImportEntityType entityType); + /// + /// Inserts an import profile + /// + /// Name of the import file + /// Profile name + /// Entity type + /// Inserted import profile + ImportProfile InsertImportProfile(string fileName, string name, ImportEntityType entityType); - /// - /// Updates an import profile - /// - /// Import profile - void UpdateImportProfile(ImportProfile profile); + /// + /// Updates an import profile + /// + /// Import profile + void UpdateImportProfile(ImportProfile profile); - /// - /// Deletes an import profile - /// - /// Import profile - void DeleteImportProfile(ImportProfile profile); + /// + /// Deletes an import profile + /// + /// Import profile + void DeleteImportProfile(ImportProfile profile); - /// - /// Get queryable import profiles - /// - /// Whether to filter enabled or disabled profiles - /// Import profiles - IQueryable GetImportProfiles(bool? enabled = null); + /// + /// Get queryable import profiles + /// + /// Whether to filter enabled or disabled profiles + /// Import profiles + IQueryable GetImportProfiles(bool? enabled = null); - /// - /// Gets an import profile by identifier - /// - /// Import profile identifier - /// Import profile - ImportProfile GetImportProfileById(int id); + /// + /// Gets an import profile by identifier + /// + /// Import profile identifier + /// Import profile + ImportProfile GetImportProfileById(int id); - /// - /// Gets an import profile by name - /// - /// Name of the import profile - /// Import profile - ImportProfile GetImportProfileByName(string name); + /// + /// Gets an import profile by name + /// + /// Name of the import profile + /// Import profile + ImportProfile GetImportProfileByName(string name); - /// - /// Get all importable entity properties and their localized values - /// - /// Import entity type - /// Importable entity properties - Dictionary GetImportableEntityProperties(ImportEntityType entityType); - } + /// + /// Get all importable entity properties and their localized values + /// + /// Import entity type + /// Importable entity properties + Dictionary GetImportableEntityProperties(ImportEntityType entityType); + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportDataSegmenter.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportDataSegmenter.cs index ef0a714fec..ba2df75006 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportDataSegmenter.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportDataSegmenter.cs @@ -3,241 +3,215 @@ using System.Data; using System.Globalization; using System.Linq; +using System.Runtime.CompilerServices; using SmartStore.Core; namespace SmartStore.Services.DataExchange.Import { - public class ImportDataSegmenter - { - private const int BATCHSIZE = 100; - - private readonly IDataTable _table; - private object[] _currentBatch; - private readonly IPageable _pageable; - private bool _bof; - private CultureInfo _culture; - private ColumnMap _columnMap; - - private readonly IDictionary _columnIndexes = new Dictionary(StringComparer.OrdinalIgnoreCase); - - public ImportDataSegmenter(IDataTable table, ColumnMap map) - { - Guard.NotNull(table, nameof(table)); - Guard.NotNull(map, nameof(map)); - - _table = table; - _columnMap = map; - - _bof = true; - _pageable = new PagedList(0, BATCHSIZE, table.Rows.Count); - _culture = CultureInfo.InvariantCulture; + public class ImportDataSegmenter + { + private const int BATCHSIZE = 100; + + private readonly IDataTable _table; + private object[] _currentBatch; + private readonly IPageable _pageable; + private bool _bof; + private CultureInfo _culture; + private ColumnMap _columnMap; + + private readonly IDictionary _columnIndexes = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public ImportDataSegmenter(IDataTable table, ColumnMap map) + { + Guard.NotNull(table, nameof(table)); + Guard.NotNull(map, nameof(map)); + + _table = table; + _columnMap = map; + + _bof = true; + _pageable = new PagedList(0, BATCHSIZE, table.Rows.Count); + _culture = CultureInfo.InvariantCulture; } - public CultureInfo Culture - { - get - { - return _culture; - } - set - { - _culture = value ?? CultureInfo.InvariantCulture; - } - } - - public ColumnMap ColumnMap - { - get - { - return _columnMap; - } - set - { - _columnMap = value ?? new ColumnMap(); - } - } - - public int TotalRows - { - get { return _table.Rows.Count; } - } - - public int TotalColumns - { - get { return _table.Columns.Count; } - } - - public int CurrentSegment - { - get { return _bof ? 0 : _pageable.PageNumber; } - } - - public int CurrentSegmentFirstRowIndex - { - get { return _pageable.FirstItemIndex; } - } - - public int TotalSegments - { - get { return _pageable.TotalPages; } - } - - public int BatchSize - { - get { return BATCHSIZE; } - } - - /// - /// Determines whether a specific column exists in the underlying data table. - /// - /// The name of the column to find - /// - /// If true and a column with the passed does not exist, - /// this method tests for the existence of any indexed column with the same name. - /// - /// true if the column exists, false otherwise - /// - /// This method takes mapped column names into account. - /// - public bool HasColumn(string name, bool withAnyIndex = false) - { - var result = HasColumn(name, null); - - if (!result && withAnyIndex) - { - // Column does not exist, but withAnyIndex is true: - // Test for existence of any indexed column. - result = GetColumnIndexes(name).Length > 0; - } - - return result; - } - - /// - /// Determines whether the column name[index] exists in the underlying data table. - /// - /// The name of the column to find - /// The index of the column - /// true if the column exists, false otherwise - /// - /// This method takes mapped column names into account. - /// - public bool HasColumn(string name, string index) - { - return _table.HasColumn(_columnMap.GetMapping(name, index).MappedName); - } - - /// - /// Indicates whether to ignore the property that is mapped to columnName - /// - /// The name of the column - /// true ignore, false do not ignore - public bool IsIgnored(string columnName) - { - return IsIgnored(columnName, null); - } - - /// - /// Indicates whether to ignore the property that is mapped to columnName - /// - /// The name of the column - /// The index of the column - /// true ignore, false do not ignore - public bool IsIgnored(string columnName, string index) - { - var mapping = _columnMap.GetMapping(columnName, index); - - return mapping.IgnoreProperty; - } - - /// - /// Returns an array of exisiting index names for a column - /// - /// The name of the columns without index qualification - /// An array of index names - /// - /// If following columns exist in source: Attr[Color], Attr[Size] - /// This method returns: string[] { "Color", "Size" } - /// - public string[] GetColumnIndexes(string name) - { - string[] indexes; - - if (!_columnIndexes.TryGetValue(name, out indexes)) - { - var startsWith = name + "["; - - var columns1 = _columnMap.Mappings - .Where(x => x.Key.StartsWith(startsWith, StringComparison.OrdinalIgnoreCase)) - .Select(x => x.Key); - - var columns2 = _table.Columns - .Where(x => x.Name.StartsWith(startsWith, StringComparison.OrdinalIgnoreCase)) - .Select(x => x.Name); - - indexes = columns1.Concat(columns2) - .Distinct(StringComparer.OrdinalIgnoreCase) - .Select(x => x.Substring(x.IndexOf("[", StringComparison.OrdinalIgnoreCase) + 1).TrimEnd(']')) - .ToArray(); - - _columnIndexes[name] = indexes; - } - - return indexes; - } - - public void Reset() - { - if (_pageable.PageIndex != 0 && _currentBatch != null) - { - _currentBatch = null; - } - _bof = true; - _pageable.PageIndex = 0; - } - - public bool ReadNextBatch() - { - if (_currentBatch != null) - { - _currentBatch = null; - } - - if (_bof) - { - _bof = false; - return _pageable.TotalCount > 0; - } - - if (_pageable.HasNextPage) - { - _pageable.PageIndex++; - return true; - } - - Reset(); - return false; - } - - public IEnumerable> GetCurrentBatch() where T : BaseEntity - { - if (_currentBatch == null) - { - int start = _pageable.FirstItemIndex - 1; - int end = _pageable.LastItemIndex - 1; - - _currentBatch = new ImportRow[(end - start) + 1]; - - // Determine values per row - int i = 0; - for (int r = start; r <= end; r++) - { - _currentBatch[i] = new ImportRow(this, _table.Rows[r], r); - i++; - } - } - - return _currentBatch.Cast>(); - } - } + public CultureInfo Culture + { + get => _culture; + set => _culture = value ?? CultureInfo.InvariantCulture; + } + + public ColumnMap ColumnMap + { + get => _columnMap; + set => _columnMap = value ?? new ColumnMap(); + } + + public int TotalRows => _table.Rows.Count; + + public int TotalColumns => _table.Columns.Count; + + public int CurrentSegment => _bof ? 0 : _pageable.PageNumber; + + public int CurrentSegmentFirstRowIndex => _pageable.FirstItemIndex; + + public int TotalSegments => _pageable.TotalPages; + + public int BatchSize => BATCHSIZE; + + /// + /// Determines whether a specific column exists in the underlying data table. + /// + /// The name of the column to find + /// + /// If true and a column with the passed does not exist, + /// this method tests for the existence of any indexed column with the same name. + /// + /// true if the column exists, false otherwise + /// + /// This method takes mapped column names into account. + /// + public bool HasColumn(string name, bool withAnyIndex = false) + { + var result = HasColumn(name, null); + + if (!result && withAnyIndex) + { + // Column does not exist, but withAnyIndex is true: + // Test for existence of any indexed column. + result = GetColumnIndexes(name).Length > 0; + } + + return result; + } + + /// + /// Determines whether the column name[index] exists in the underlying data table. + /// + /// The name of the column to find + /// The index of the column + /// true if the column exists, false otherwise + /// + /// This method takes mapped column names into account. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool HasColumn(string name, string index) + { + return _table.HasColumn(_columnMap.GetMapping(name, index).MappedName); + } + + /// + /// Indicates whether to ignore the property that is mapped to columnName + /// + /// The name of the column + /// true ignore, false do not ignore + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsIgnored(string columnName) + { + return IsIgnored(columnName, null); + } + + /// + /// Indicates whether to ignore the property that is mapped to columnName + /// + /// The name of the column + /// The index of the column + /// true ignore, false do not ignore + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsIgnored(string columnName, string index) + { + var mapping = _columnMap.GetMapping(columnName, index); + + return mapping.IgnoreProperty; + } + + /// + /// Returns an array of exisiting index names for a column + /// + /// The name of the columns without index qualification + /// An array of index names + /// + /// If following columns exist in source: Attr[Color], Attr[Size] + /// This method returns: string[] { "Color", "Size" } + /// + public string[] GetColumnIndexes(string name) + { + string[] indexes; + + if (!_columnIndexes.TryGetValue(name, out indexes)) + { + var startsWith = name + "["; + + var columns1 = _columnMap.Mappings + .Where(x => x.Key.StartsWith(startsWith, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Key); + + var columns2 = _table.Columns + .Where(x => x.Name.StartsWith(startsWith, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Name); + + indexes = columns1.Concat(columns2) + .Distinct(StringComparer.OrdinalIgnoreCase) + .Select(x => x.Substring(x.IndexOf("[", StringComparison.OrdinalIgnoreCase) + 1).TrimEnd(']')) + .ToArray(); + + _columnIndexes[name] = indexes; + } + + return indexes; + } + + public void Reset() + { + if (_pageable.PageIndex != 0 && _currentBatch != null) + { + _currentBatch = null; + } + _bof = true; + _pageable.PageIndex = 0; + } + + public bool ReadNextBatch() + { + if (_currentBatch != null) + { + _currentBatch = null; + } + + if (_bof) + { + _bof = false; + return _pageable.TotalCount > 0; + } + + if (_pageable.HasNextPage) + { + _pageable.PageIndex++; + return true; + } + + Reset(); + return false; + } + + public IEnumerable> GetCurrentBatch() where T : BaseEntity + { + if (_currentBatch == null) + { + int start = _pageable.FirstItemIndex - 1; + int end = _pageable.LastItemIndex - 1; + + _currentBatch = new ImportRow[(end - start) + 1]; + + // Determine values per row + int i = 0; + for (int r = start; r <= end; r++) + { + _currentBatch[i] = new ImportRow(this, _table.Rows[r], r); + i++; + } + } + + return _currentBatch.Cast>(); + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportExecuteContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportExecuteContext.cs index d5b3ec8692..840f8dcd3c 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportExecuteContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportExecuteContext.cs @@ -7,65 +7,62 @@ namespace SmartStore.Services.DataExchange.Import { public class ImportExecuteContext - { - private DataExchangeAbortion _abortion; - private ProgressValueSetter _progressValueSetter; - private string _progressInfo; - - private IDataTable _dataTable; - private ImportDataSegmenter _segmenter; - - public ImportExecuteContext( - CancellationToken cancellation, - ProgressValueSetter progressValueSetter, - string progressInfo) - { - _progressValueSetter = progressValueSetter; - _progressInfo = progressInfo; - - CancellationToken = cancellation; - CustomProperties = new Dictionary(); - } - - public DataImportRequest Request - { - get; - internal set; - } - - /// - /// Import settings - /// - public DataExchangeSettings DataExchangeSettings - { - get; - internal set; - } - - /// - /// The data source (CSV, Excel etc.) - /// - public IDataTable DataTable - { - get - { - return _dataTable; - } - internal set - { - _dataTable = value; - _segmenter = null; - } - } - - /// - /// Mapping information between database and data source - /// - public ColumnMap ColumnMap - { - get; - internal set; - } + { + private DataExchangeAbortion _abortion; + private ProgressValueSetter _progressValueSetter; + private string _progressInfo; + + private IDataTable _dataTable; + private ImportDataSegmenter _segmenter; + + public ImportExecuteContext( + CancellationToken cancellation, + ProgressValueSetter progressValueSetter, + string progressInfo) + { + _progressValueSetter = progressValueSetter; + _progressInfo = progressInfo; + + CancellationToken = cancellation; + CustomProperties = new Dictionary(); + } + + public DataImportRequest Request + { + get; + internal set; + } + + /// + /// Import settings + /// + public DataExchangeSettings DataExchangeSettings + { + get; + internal set; + } + + /// + /// The data source (CSV, Excel etc.) + /// + public IDataTable DataTable + { + get => _dataTable; + internal set + { + _dataTable = value; + _segmenter = null; + } + } + + /// + /// Mapping information between database and data source + /// + public ColumnMap ColumnMap + { + get; + internal set; + } /// /// Infos about the import file. @@ -80,148 +77,139 @@ public ImportFile File /// Whether to only update existing records /// public bool UpdateOnly - { - get; - internal set; - } - - /// - /// Name of key fields to identify existing records for updating - /// - public string[] KeyFieldNames - { - get; - internal set; - } - - /// - /// All active languages - /// - public IList Languages - { - get; - internal set; - } - - /// - /// To log information into the import log file - /// - public ILogger Log - { - get; - internal set; - } - - /// - /// Common Services - /// - public ICommonServices Services - { - get; - internal set; - } - - /// - /// Cancellation token - /// - public CancellationToken CancellationToken - { - get; - private set; - } - - /// - /// The import folder - /// - public string ImportFolder - { - get; - internal set; - } - - /// - /// Use this dictionary for any custom data required along the import - /// - public Dictionary CustomProperties - { - get; - set; - } - - /// - /// Result of the import - /// - public ImportResult Result - { - get; - set; - } - - /// - /// Extra import configuration data - /// - public ImportExtraData ExtraData - { - get; - internal set; - } - - /// - /// Indicates whether and how to abort the import - /// - public DataExchangeAbortion Abort - { - get - { - if (CancellationToken.IsCancellationRequested || IsMaxFailures) - return DataExchangeAbortion.Hard; - - return _abortion; - } - set - { - _abortion = value; - } - } - - public bool IsMaxFailures - { - get - { - return Result.Errors > 11; - } - } - - public ImportDataSegmenter DataSegmenter - { - get - { - if (_segmenter == null) - { - if (this.DataTable == null || this.ColumnMap == null) - { - throw new SmartException("A DataTable and a ColumnMap must be specified before accessing the DataSegmenter property."); - } - _segmenter = new ImportDataSegmenter(DataTable, ColumnMap); - } - - return _segmenter; - } - } - - /// - /// Allows to set a progress message - /// - /// Progress value - /// /// Progress maximum - public void SetProgress(int value, int maximum) - { - try - { - _progressValueSetter?.Invoke(value, maximum, _progressInfo.FormatInvariant(value, maximum)); - } - catch { } - } + { + get; + internal set; + } + + /// + /// Name of key fields to identify existing records for updating + /// + public string[] KeyFieldNames + { + get; + internal set; + } + + /// + /// All active languages + /// + public IList Languages + { + get; + internal set; + } + + /// + /// To log information into the import log file + /// + public ILogger Log + { + get; + set; + } + + /// + /// Common Services + /// + public ICommonServices Services + { + get; + internal set; + } + + /// + /// Cancellation token + /// + public CancellationToken CancellationToken + { + get; + private set; + } + + /// + /// The import folder + /// + public string ImportFolder + { + get; + internal set; + } + + /// + /// Use this dictionary for any custom data required along the import + /// + public Dictionary CustomProperties + { + get; + set; + } + + /// + /// Result of the import + /// + public ImportResult Result + { + get; + set; + } + + /// + /// Extra import configuration data + /// + public ImportExtraData ExtraData + { + get; + internal set; + } + + /// + /// Indicates whether and how to abort the import + /// + public DataExchangeAbortion Abort + { + get + { + if (CancellationToken.IsCancellationRequested || IsMaxFailures) + return DataExchangeAbortion.Hard; + + return _abortion; + } + set => _abortion = value; + } + + public bool IsMaxFailures => Result.Errors > 11; + + public ImportDataSegmenter DataSegmenter + { + get + { + if (_segmenter == null) + { + if (this.DataTable == null || this.ColumnMap == null) + { + throw new SmartException("A DataTable and a ColumnMap must be specified before accessing the DataSegmenter property."); + } + _segmenter = new ImportDataSegmenter(DataTable, ColumnMap); + } + + return _segmenter; + } + } + + /// + /// Allows to set a progress message + /// + /// Progress value + /// /// Progress maximum + public void SetProgress(int value, int maximum) + { + try + { + _progressValueSetter?.Invoke(value, maximum, _progressInfo.FormatInvariant(value, maximum)); + } + catch { } + } /// /// Allows to set a message @@ -236,5 +224,5 @@ public void SetProgress(string message) } catch { } } - } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportExtensions.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportExtensions.cs index 9b7918fa18..9d7bd8f02b 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportExtensions.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportExtensions.cs @@ -8,36 +8,36 @@ namespace SmartStore.Services.DataExchange.Import { public static class ImportExtensions - { - /// - /// Get folder for import files - /// - /// Import profile - /// Folder path - public static string GetImportFolder( - this ImportProfile profile, - bool content = false, - bool create = false, - bool absolutePath = true) - { - var path = string.Concat( - DataSettings.Current.TenantPath, - "/ImportProfiles/", - profile.FolderName, - content ? "/Content" : ""); + { + /// + /// Get folder for import files + /// + /// Import profile + /// Folder path + public static string GetImportFolder( + this ImportProfile profile, + bool content = false, + bool create = false, + bool absolutePath = true) + { + var path = string.Concat( + DataSettings.Current.TenantPath, + "/ImportProfiles/", + profile.FolderName, + content ? "/Content" : ""); - if (absolutePath) - { - path = CommonHelper.MapPath(path); + if (absolutePath) + { + path = CommonHelper.MapPath(path); - if (create && !System.IO.Directory.Exists(path)) - { - System.IO.Directory.CreateDirectory(path); - } - } + if (create && !System.IO.Directory.Exists(path)) + { + System.IO.Directory.CreateDirectory(path); + } + } - return path; - } + return path; + } /// /// Gets import files for an import profile. @@ -78,8 +78,8 @@ public static List GetImportFiles(this ImportProfile profile, bool i /// Import profile /// Log file path public static string GetImportLogPath(this ImportProfile profile) - { - return Path.Combine(profile.GetImportFolder(), "log.txt"); - } - } + { + return Path.Combine(profile.GetImportFolder(), "log.txt"); + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportExtraData.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportExtraData.cs index ecbe710677..24fd3984c0 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportExtraData.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportExtraData.cs @@ -2,12 +2,12 @@ namespace SmartStore.Services.DataExchange.Import { - [Serializable] - public class ImportExtraData - { - /// - /// Number of images per object to be imported - /// - public int? NumberOfPictures { get; set; } - } + [Serializable] + public class ImportExtraData + { + /// + /// Number of images per object to be imported + /// + public int? NumberOfPictures { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportFile.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportFile.cs index 6b0af36ef2..ac09239070 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportFile.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportFile.cs @@ -43,13 +43,7 @@ public ImportFile(string path) /// /// Indicates whether the file has an CSV file extension. /// - internal bool IsCsv - { - get - { - return (new string[] { ".csv", ".txt", ".tab" }).Contains(Extension, StringComparer.OrdinalIgnoreCase); - } - } + internal bool IsCsv => (new string[] { ".csv", ".txt", ".tab" }).Contains(Extension, StringComparer.OrdinalIgnoreCase); /// /// Related entity type. diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportMessage.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportMessage.cs index b21728d75b..47f3c16a08 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportMessage.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportMessage.cs @@ -2,89 +2,83 @@ namespace SmartStore.Services.DataExchange.Import { - - public class ImportMessage - { - public ImportMessage(string message, ImportMessageType messageType = ImportMessageType.Info) - { - Guard.NotEmpty(message, nameof(message)); - - this.Message = message; - this.MessageType = messageType; - } - - public ImportRowInfo AffectedItem - { - get; - set; - } - - public string AffectedField - { - get; - set; - } - - public ImportMessageType MessageType - { - get; - set; - } - - public string Message - { - get; - set; - } - - public string FullMessage - { - get; - set; - } - - public override string ToString() - { - var result = Message.NaIfEmpty(); - - string appendix = null; - - if (AffectedItem != null) - appendix = appendix.Grow("Pos: " + (AffectedItem.Position + 1).ToString(), ", "); - - if (AffectedField.HasValue()) - appendix = appendix.Grow("Field: " + AffectedField, ", "); - - if (appendix.HasValue()) - result = "{0} [{1}]".FormatInvariant(result, appendix); - - return result; - } - } - - public class ImportRowInfo : Tuple - { - public ImportRowInfo(int position, string entityName) - : base(position, entityName) - { - } - - public int Position - { - get { return base.Item1; } - } - - public string EntityName - { - get { return base.Item2; } - } - } - - public enum ImportMessageType - { - Info = 0, - Warning = 5, - Error = 10 - } + + public class ImportMessage + { + public ImportMessage(string message, ImportMessageType messageType = ImportMessageType.Info) + { + Guard.NotEmpty(message, nameof(message)); + + this.Message = message; + this.MessageType = messageType; + } + + public ImportRowInfo AffectedItem + { + get; + set; + } + + public string AffectedField + { + get; + set; + } + + public ImportMessageType MessageType + { + get; + set; + } + + public string Message + { + get; + set; + } + + public string FullMessage + { + get; + set; + } + + public override string ToString() + { + var result = Message.NaIfEmpty(); + + string appendix = null; + + if (AffectedItem != null) + appendix = appendix.Grow("Pos: " + (AffectedItem.Position + 1).ToString(), ", "); + + if (AffectedField.HasValue()) + appendix = appendix.Grow("Field: " + AffectedField, ", "); + + if (appendix.HasValue()) + result = "{0} [{1}]".FormatInvariant(result, appendix); + + return result; + } + } + + public class ImportRowInfo : Tuple + { + public ImportRowInfo(int position, string entityName) + : base(position, entityName) + { + } + + public int Position => base.Item1; + + public string EntityName => base.Item2; + } + + public enum ImportMessageType + { + Info = 0, + Warning = 5, + Error = 10 + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportProfileService.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportProfileService.cs index da6ad91076..ab3fb9563b 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportProfileService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportProfileService.cs @@ -9,7 +9,6 @@ using SmartStore.Core.Domain; using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Domain.Tasks; -using SmartStore.Core.Events; using SmartStore.Core.Localization; using SmartStore.Services.Catalog.Importer; using SmartStore.Services.Customers.Importer; @@ -20,387 +19,419 @@ namespace SmartStore.Services.DataExchange.Import { - public partial class ImportProfileService : IImportProfileService - { - private static object _lock = new object(); - private static Dictionary> _entityProperties = null; - - private readonly IRepository _importProfileRepository; - private readonly IEventPublisher _eventPublisher; - private readonly IScheduleTaskService _scheduleTaskService; - private readonly ILocalizationService _localizationService; - private readonly ILanguageService _languageService; - private readonly DataExchangeSettings _dataExchangeSettings; - - public ImportProfileService( - IRepository importProfileRepository, - IEventPublisher eventPublisher, - IScheduleTaskService scheduleTaskService, - ILocalizationService localizationService, - ILanguageService languageService, - DataExchangeSettings dataExchangeSettings) - { - _importProfileRepository = importProfileRepository; - _eventPublisher = eventPublisher; - _scheduleTaskService = scheduleTaskService; - _localizationService = localizationService; - _languageService = languageService; - _dataExchangeSettings = dataExchangeSettings; - } - - private string GetLocalizedPropertyName(ImportEntityType type, string property) - { - if (property.IsEmpty()) - return ""; - - string key = null; - string prefixKey = null; - - if (property.StartsWith("BillingAddress.")) - prefixKey = "Admin.Orders.Fields.BillingAddress"; - else if (property.StartsWith("ShippingAddress.")) - prefixKey = "Admin.Orders.Fields.ShippingAddress"; - - #region Get resource key - - switch (property) - { - case "Id": - key = "Admin.Common.Entity.Fields.Id"; - break; - case "LimitedToStores": - key = "Admin.Common.Store.LimitedTo"; - break; - case "DisplayOrder": - key = "Common.DisplayOrder"; - break; - case "Deleted": - key = "Admin.Common.Deleted"; - break; - case "CreatedOnUtc": - case "BillingAddress.CreatedOnUtc": - case "ShippingAddress.CreatedOnUtc": - key = "Common.CreatedOn"; - break; - case "UpdatedOnUtc": - key = "Common.UpdatedOn"; - break; - case "HasDiscountsApplied": - key = "Admin.Catalog.Products.Fields.HasDiscountsApplied"; - break; - case "DefaultViewMode": - key = "Admin.Configuration.Settings.Catalog.DefaultViewMode"; - break; - case "StoreId": - key = "Admin.Common.Store"; - break; - case "ParentGroupedProductId": - key = "Admin.Catalog.Products.Fields.AssociatedToProductName"; - break; - case "PasswordFormatId": - key = "Admin.Configuration.Settings.CustomerUser.DefaultPasswordFormat"; - break; - case "LastIpAddress": - key = "Admin.Customers.Customers.Fields.IPAddress"; - break; - default: - switch (type) - { - case ImportEntityType.Product: - key = "Admin.Catalog.Products.Fields." + property; - break; - case ImportEntityType.Category: - key = "Admin.Catalog.Categories.Fields." + property; - break; - case ImportEntityType.Customer: - if (property.StartsWith("BillingAddress.") || property.StartsWith("ShippingAddress.")) - key = "Admin.Address.Fields." + property.Substring(property.IndexOf('.') + 1); - else - key = "Admin.Customers.Customers.Fields." + property; - break; - case ImportEntityType.NewsLetterSubscription: - key = "Admin.Promotions.NewsLetterSubscriptions.Fields." + property; - break; - } - break; - } - - #endregion - - if (key.IsEmpty()) - return ""; - - var result = _localizationService.GetResource(key, 0, false, "", true); - - if (result.IsEmpty()) - { - if (key.EndsWith("Id")) - result = _localizationService.GetResource(key.Substring(0, key.Length - 2), 0, false, "", true); - else if (key.EndsWith("Utc")) - result = _localizationService.GetResource(key.Substring(0, key.Length - 3), 0, false, "", true); - } - - if (result.IsEmpty()) - { - Debug.WriteLine("Missing string resource mapping for {0} - {1}".FormatInvariant(type.ToString(), property)); - result = property.SplitPascalCase(); - } - - if (prefixKey.HasValue()) - { - result = string.Concat(_localizationService.GetResource(prefixKey, 0, false, "", true), " - ", result); - } - - return result; - } - - public string GetNewProfileName(ImportEntityType entityType) - { - var defaultNames = _localizationService.GetResource("Admin.DataExchange.Import.DefaultProfileNames").SplitSafe(";"); - - var result = defaultNames.SafeGet((int)entityType); - - if (result.IsEmpty()) - result = entityType.ToString(); - - var profileCount = _importProfileRepository.Table.Count(x => x.EntityTypeId == (int)entityType); - - result = string.Concat(result, " ", profileCount + 1); - - return result; - } - - public virtual ImportProfile InsertImportProfile(string fileName, string name, ImportEntityType entityType) - { - Guard.NotEmpty(fileName, nameof(fileName)); - - if (name.IsEmpty()) - name = GetNewProfileName(entityType); - - var task = new ScheduleTask - { - CronExpression = "0 */24 * * *", - Type = typeof(DataImportTask).AssemblyQualifiedNameWithoutVersion(), - Enabled = false, - StopOnError = false, - IsHidden = true - }; - - task.Name = string.Concat(name, " Task"); - - _scheduleTaskService.InsertTask(task); - - var profile = new ImportProfile - { - Name = name, - EntityType = entityType, - Enabled = true, - SchedulingTaskId = task.Id - }; - - if (Path.GetExtension(fileName).IsCaseInsensitiveEqual(".xlsx")) - profile.FileType = ImportFileType.XLSX; - else - profile.FileType = ImportFileType.CSV; - - string[] keyFieldNames = null; - - switch (entityType) - { - case ImportEntityType.Product: - keyFieldNames = ProductImporter.DefaultKeyFields; - break; - case ImportEntityType.Category: - keyFieldNames = CategoryImporter.DefaultKeyFields; - break; - case ImportEntityType.Customer: - keyFieldNames = CustomerImporter.DefaultKeyFields; - break; - case ImportEntityType.NewsLetterSubscription: - keyFieldNames = NewsLetterSubscriptionImporter.DefaultKeyFields; - break; - } - - profile.KeyFieldNames = string.Join(",", keyFieldNames); - - profile.FolderName = SeoHelper.GetSeName(name, true, false, false) - .ToValidPath() - .Truncate(_dataExchangeSettings.MaxFileNameLength); - - var path = DataSettings.Current.TenantPath + "/ImportProfiles"; - profile.FolderName = FileSystemHelper.CreateNonExistingDirectoryName(CommonHelper.MapPath(path), profile.FolderName); - - _importProfileRepository.Insert(profile); - - task.Alias = profile.Id.ToString(); - _scheduleTaskService.UpdateTask(task); - - return profile; - } - - public virtual void UpdateImportProfile(ImportProfile profile) - { - if (profile == null) - throw new ArgumentNullException("profile"); - - _importProfileRepository.Update(profile); - } - - public virtual void DeleteImportProfile(ImportProfile profile) - { - if (profile == null) - throw new ArgumentNullException("profile"); - - var scheduleTaskId = profile.SchedulingTaskId; - var folder = profile.GetImportFolder(); - - _importProfileRepository.Delete(profile); - - var scheduleTask = _scheduleTaskService.GetTaskById(scheduleTaskId); - _scheduleTaskService.DeleteTask(scheduleTask); - - if (System.IO.Directory.Exists(folder)) - { - FileSystemHelper.ClearDirectory(folder, true); - } - } - - public virtual IQueryable GetImportProfiles(bool? enabled = null) - { - var query = _importProfileRepository.Table - .Expand(x => x.ScheduleTask); - - if (enabled.HasValue) - { - query = query.Where(x => x.Enabled == enabled.Value); - } - - query = query - .OrderBy(x => x.EntityTypeId) - .ThenBy(x => x.Name); - - return query; - } - - public virtual ImportProfile GetImportProfileById(int id) - { - if (id == 0) - return null; - - var profile = _importProfileRepository.Table - .Expand(x => x.ScheduleTask) - .FirstOrDefault(x => x.Id == id); - - return profile; - } - - public virtual ImportProfile GetImportProfileByName(string name) - { - if (name.IsEmpty()) - return null; - - var profile = _importProfileRepository.Table - .Expand(x => x.ScheduleTask) - .FirstOrDefault(x => x.Name == name); - - return profile; - } - - public virtual Dictionary GetImportableEntityProperties(ImportEntityType entityType) - { - if (_entityProperties == null) - { - lock (_lock) - { - if (_entityProperties == null) - { - _entityProperties = new Dictionary>(); - - var context = ((IObjectContextAdapter)_importProfileRepository.Context).ObjectContext; - var container = context.MetadataWorkspace.GetEntityContainer(context.DefaultContainerName, DataSpace.CSpace); - - var allLanguages = _languageService.GetAllLanguages(true); - var allLanguageNames = allLanguages.ToDictionarySafe(x => x.UniqueSeoCode, x => LocalizationHelper.GetLanguageNativeName(x.LanguageCulture) ?? x.Name); - - var localizableProperties = new Dictionary - { - { ImportEntityType.Product, new string[] { "Name", "ShortDescription", "FullDescription", "MetaKeywords", "MetaDescription", "MetaTitle", "SeName" } }, - { ImportEntityType.Category, new string[] { "Name", "FullName", "Description", "BottomDescription", "MetaKeywords", "MetaDescription", "MetaTitle", "SeName" } }, - { ImportEntityType.Customer, new string[] { } }, - { ImportEntityType.NewsLetterSubscription, new string[] { } } - }; - - var addressSet = container.GetEntitySetByName("Addresses", true); - - var addressProperties = addressSet.ElementType.Members - .Where(x => !x.Name.IsCaseInsensitiveEqual("Id") && x.BuiltInTypeKind.HasFlag(BuiltInTypeKind.EdmProperty)) - .Select(x => x.Name) - .ToList(); - - - foreach (ImportEntityType type in Enum.GetValues(typeof(ImportEntityType))) - { - EntitySet entitySet = null; - - try - { - if (type == ImportEntityType.Category) - entitySet = container.GetEntitySetByName("Categories", true); - else - entitySet = container.GetEntitySetByName(type.ToString() + "s", true); - } - catch (Exception) - { - throw new SmartException("There is no entity set for ImportEntityType {0}. Note, the enum value must equal the entity name.".FormatInvariant(type.ToString())); - } - - var dic = entitySet.ElementType.Members - .Where(x => !x.Name.IsCaseInsensitiveEqual("Id") && x.BuiltInTypeKind.HasFlag(BuiltInTypeKind.EdmProperty)) - .Select(x => x.Name) - .ToDictionary(x => x, x => "", StringComparer.OrdinalIgnoreCase); - - // lack of abstractness? - if ((type == ImportEntityType.Product || type == ImportEntityType.Category) && !dic.ContainsKey("SeName")) - { - dic.Add("SeName", ""); - } - - // shipping and billing address - if (type == ImportEntityType.Customer) - { - foreach (var property in addressProperties) - { - dic.Add("BillingAddress." + property, ""); - dic.Add("ShippingAddress." + property, ""); - } - } - - // add localized property names - foreach (var key in dic.Keys.ToList()) - { - var localizedValue = GetLocalizedPropertyName(type, key); - - dic[key] = localizedValue.NaIfEmpty(); - - if (localizableProperties[type].Contains(key)) - { - foreach (var language in allLanguages) - { - dic.Add( - "{0}[{1}]".FormatInvariant(key, language.UniqueSeoCode.EmptyNull().ToLower()), - "{0} {1}".FormatInvariant(localizedValue.NaIfEmpty(), allLanguageNames[language.UniqueSeoCode]) - ); - } - } - } - - _entityProperties.Add(type, dic); - } - } - } - } - - return (_entityProperties.ContainsKey(entityType) ? _entityProperties[entityType] : null); - } - } + public partial class ImportProfileService : IImportProfileService + { + private static object _lock = new object(); + private static Dictionary> _entityProperties = null; + + private readonly IRepository _importProfileRepository; + private readonly IScheduleTaskService _scheduleTaskService; + private readonly ILocalizationService _localizationService; + private readonly ILanguageService _languageService; + private readonly DataExchangeSettings _dataExchangeSettings; + + public ImportProfileService( + IRepository importProfileRepository, + IScheduleTaskService scheduleTaskService, + ILocalizationService localizationService, + ILanguageService languageService, + DataExchangeSettings dataExchangeSettings) + { + _importProfileRepository = importProfileRepository; + _scheduleTaskService = scheduleTaskService; + _localizationService = localizationService; + _languageService = languageService; + _dataExchangeSettings = dataExchangeSettings; + } + + private string GetLocalizedPropertyName(ImportEntityType type, string property) + { + // TODO: find a better solution for this. + if (property.IsEmpty()) + { + return string.Empty; + } + + string key = null; + string prefixKey = null; + + if (property.StartsWith("BillingAddress.")) + { + prefixKey = "Admin.Orders.Fields.BillingAddress"; + } + else if (property.StartsWith("ShippingAddress.")) + { + prefixKey = "Admin.Orders.Fields.ShippingAddress"; + } + + #region Get resource key + + switch (property) + { + case "Id": + key = "Admin.Common.Entity.Fields.Id"; + break; + case "DisplayOrder": + case "HomePageDisplayOrder": + key = "Common.DisplayOrder"; + break; + case "Deleted": + key = "Admin.Common.Deleted"; + break; + case "WorkingLanguageId": + key = "Common.Language"; + break; + case "CreatedOnUtc": + case "BillingAddress.CreatedOnUtc": + case "ShippingAddress.CreatedOnUtc": + key = "Common.CreatedOn"; + break; + case "UpdatedOnUtc": + key = "Common.UpdatedOn"; + break; + case "HasDiscountsApplied": + key = "Admin.Catalog.Products.Fields.HasDiscountsApplied"; + break; + case "DefaultViewMode": + key = "Admin.Configuration.Settings.Catalog.DefaultViewMode"; + break; + case "StoreId": + key = "Admin.Common.Store"; + break; + case "LimitedToStores": + key = "Admin.Common.Store.LimitedTo"; + break; + case "SubjectToAcl": + key = "Admin.Common.CustomerRole.LimitedTo"; + break; + case "ParentGroupedProductId": + key = "Admin.Catalog.Products.Fields.AssociatedToProductName"; + break; + case "PasswordFormatId": + key = "Admin.Configuration.Settings.CustomerUser.DefaultPasswordFormat"; + break; + case "LastIpAddress": + key = "Admin.Customers.Customers.Fields.IPAddress"; + break; + case "CustomerNumber": + key = "Account.Fields.CustomerNumber"; + break; + case "BirthDate": + key = "Admin.Customers.Customers.Fields.DateOfBirth"; + break; + case "Salutation": + key = "Address.Fields.Salutation"; + break; + case "VisibleIndividually": + key = "Admin.Catalog.Products.Fields.Visibility"; + break; + default: + switch (type) + { + case ImportEntityType.Product: + key = "Admin.Catalog.Products.Fields." + property; + break; + case ImportEntityType.Category: + key = "Admin.Catalog.Categories.Fields." + property; + break; + case ImportEntityType.Customer: + if (property.StartsWith("BillingAddress.") || property.StartsWith("ShippingAddress.")) + key = "Address.Fields." + property.Substring(property.IndexOf('.') + 1); + else + key = "Admin.Customers.Customers.Fields." + property; + break; + case ImportEntityType.NewsLetterSubscription: + key = "Admin.Promotions.NewsLetterSubscriptions.Fields." + property; + break; + } + break; + } + + #endregion + + if (key.IsEmpty()) + { + return string.Empty; + } + + var result = _localizationService.GetResource(key, 0, false, "", true); + + if (result.IsEmpty()) + { + if (key.EndsWith("Id")) + { + result = _localizationService.GetResource(key.Substring(0, key.Length - 2), 0, false, "", true); + } + else if (key.EndsWith("Utc")) + { + result = _localizationService.GetResource(key.Substring(0, key.Length - 3), 0, false, "", true); + } + } + + if (result.IsEmpty()) + { + Debug.WriteLine("Missing string resource mapping for {0} - {1}".FormatInvariant(type.ToString(), property)); + result = property.SplitPascalCase(); + } + + if (prefixKey.HasValue()) + { + result = string.Concat(_localizationService.GetResource(prefixKey, 0, false, "", true), " - ", result); + } + + return result; + } + + public string GetNewProfileName(ImportEntityType entityType) + { + var defaultNames = _localizationService.GetResource("Admin.DataExchange.Import.DefaultProfileNames").SplitSafe(";"); + + var result = defaultNames.SafeGet((int)entityType); + + if (result.IsEmpty()) + result = entityType.ToString(); + + var profileCount = _importProfileRepository.Table.Count(x => x.EntityTypeId == (int)entityType); + + result = string.Concat(result, " ", profileCount + 1); + + return result; + } + + public virtual ImportProfile InsertImportProfile(string fileName, string name, ImportEntityType entityType) + { + Guard.NotEmpty(fileName, nameof(fileName)); + + if (name.IsEmpty()) + name = GetNewProfileName(entityType); + + var task = new ScheduleTask + { + CronExpression = "0 */24 * * *", + Type = typeof(DataImportTask).AssemblyQualifiedNameWithoutVersion(), + Enabled = false, + StopOnError = false, + IsHidden = true + }; + + task.Name = string.Concat(name, " Task"); + + _scheduleTaskService.InsertTask(task); + + var profile = new ImportProfile + { + Name = name, + EntityType = entityType, + Enabled = true, + SchedulingTaskId = task.Id + }; + + if (Path.GetExtension(fileName).IsCaseInsensitiveEqual(".xlsx")) + profile.FileType = ImportFileType.XLSX; + else + profile.FileType = ImportFileType.CSV; + + string[] keyFieldNames = null; + + switch (entityType) + { + case ImportEntityType.Product: + keyFieldNames = ProductImporter.DefaultKeyFields; + break; + case ImportEntityType.Category: + keyFieldNames = CategoryImporter.DefaultKeyFields; + break; + case ImportEntityType.Customer: + keyFieldNames = CustomerImporter.DefaultKeyFields; + break; + case ImportEntityType.NewsLetterSubscription: + keyFieldNames = NewsLetterSubscriptionImporter.DefaultKeyFields; + break; + } + + profile.KeyFieldNames = string.Join(",", keyFieldNames); + + profile.FolderName = SeoHelper.GetSeName(name, true, false, false) + .ToValidPath() + .Truncate(_dataExchangeSettings.MaxFileNameLength); + + var path = DataSettings.Current.TenantPath + "/ImportProfiles"; + profile.FolderName = FileSystemHelper.CreateNonExistingDirectoryName(CommonHelper.MapPath(path), profile.FolderName); + + _importProfileRepository.Insert(profile); + + task.Alias = profile.Id.ToString(); + _scheduleTaskService.UpdateTask(task); + + return profile; + } + + public virtual void UpdateImportProfile(ImportProfile profile) + { + if (profile == null) + throw new ArgumentNullException("profile"); + + _importProfileRepository.Update(profile); + } + + public virtual void DeleteImportProfile(ImportProfile profile) + { + if (profile == null) + throw new ArgumentNullException("profile"); + + var scheduleTaskId = profile.SchedulingTaskId; + var folder = profile.GetImportFolder(); + + _importProfileRepository.Delete(profile); + + var scheduleTask = _scheduleTaskService.GetTaskById(scheduleTaskId); + _scheduleTaskService.DeleteTask(scheduleTask); + + if (System.IO.Directory.Exists(folder)) + { + FileSystemHelper.ClearDirectory(folder, true); + } + } + + public virtual IQueryable GetImportProfiles(bool? enabled = null) + { + var query = _importProfileRepository.Table + .Expand(x => x.ScheduleTask); + + if (enabled.HasValue) + { + query = query.Where(x => x.Enabled == enabled.Value); + } + + query = query + .OrderBy(x => x.EntityTypeId) + .ThenBy(x => x.Name); + + return query; + } + + public virtual ImportProfile GetImportProfileById(int id) + { + if (id == 0) + return null; + + var profile = _importProfileRepository.Table + .Expand(x => x.ScheduleTask) + .FirstOrDefault(x => x.Id == id); + + return profile; + } + + public virtual ImportProfile GetImportProfileByName(string name) + { + if (name.IsEmpty()) + return null; + + var profile = _importProfileRepository.Table + .Expand(x => x.ScheduleTask) + .FirstOrDefault(x => x.Name == name); + + return profile; + } + + public virtual Dictionary GetImportableEntityProperties(ImportEntityType entityType) + { + if (_entityProperties == null) + { + lock (_lock) + { + if (_entityProperties == null) + { + _entityProperties = new Dictionary>(); + + var context = ((IObjectContextAdapter)_importProfileRepository.Context).ObjectContext; + var container = context.MetadataWorkspace.GetEntityContainer(context.DefaultContainerName, DataSpace.CSpace); + + var allLanguages = _languageService.GetAllLanguages(true); + var allLanguageNames = allLanguages.ToDictionarySafe(x => x.UniqueSeoCode, x => LocalizationHelper.GetLanguageNativeName(x.LanguageCulture) ?? x.Name); + + var localizableProperties = new Dictionary + { + { ImportEntityType.Product, new string[] { "Name", "ShortDescription", "FullDescription", "MetaKeywords", "MetaDescription", "MetaTitle", "SeName" } }, + { ImportEntityType.Category, new string[] { "Name", "FullName", "Description", "BottomDescription", "MetaKeywords", "MetaDescription", "MetaTitle", "SeName" } }, + { ImportEntityType.Customer, new string[] { } }, + { ImportEntityType.NewsLetterSubscription, new string[] { } } + }; + + var addressSet = container.GetEntitySetByName("Addresses", true); + + var addressProperties = addressSet.ElementType.Members + .Where(x => !x.Name.IsCaseInsensitiveEqual("Id") && x.BuiltInTypeKind.HasFlag(BuiltInTypeKind.EdmProperty)) + .Select(x => x.Name) + .ToList(); + + + foreach (ImportEntityType type in Enum.GetValues(typeof(ImportEntityType))) + { + EntitySet entitySet = null; + + try + { + if (type == ImportEntityType.Category) + entitySet = container.GetEntitySetByName("Categories", true); + else + entitySet = container.GetEntitySetByName(type.ToString() + "s", true); + } + catch (Exception) + { + throw new SmartException("There is no entity set for ImportEntityType {0}. Note, the enum value must equal the entity name.".FormatInvariant(type.ToString())); + } + + var dic = entitySet.ElementType.Members + .Where(x => !x.Name.IsCaseInsensitiveEqual("Id") && x.BuiltInTypeKind.HasFlag(BuiltInTypeKind.EdmProperty)) + .Select(x => x.Name) + .ToDictionarySafe(x => x, x => "", StringComparer.OrdinalIgnoreCase); + + // lack of abstractness? + if ((type == ImportEntityType.Product || type == ImportEntityType.Category) && !dic.ContainsKey("SeName")) + { + dic["SeName"] = ""; + } + + if (type == ImportEntityType.Product) + { + dic["Specification"] = ""; + } + + // shipping and billing address + if (type == ImportEntityType.Customer) + { + foreach (var property in addressProperties) + { + dic["BillingAddress." + property] = ""; + dic["ShippingAddress." + property] = ""; + } + } + + // add localized property names + foreach (var key in dic.Keys.ToList()) + { + var localizedValue = GetLocalizedPropertyName(type, key); + + dic[key] = localizedValue.NaIfEmpty(); + + if (localizableProperties[type].Contains(key)) + { + foreach (var language in allLanguages) + { + dic["{0}[{1}]".FormatInvariant(key, language.UniqueSeoCode.EmptyNull().ToLower())] = + "{0} {1}".FormatInvariant(localizedValue.NaIfEmpty(), allLanguageNames[language.UniqueSeoCode]); + } + } + } + + _entityProperties[type] = dic; + } + } + } + } + + return _entityProperties.ContainsKey(entityType) ? _entityProperties[entityType] : null; + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportResult.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportResult.cs index a0de84eae2..8f45413f35 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportResult.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportResult.cs @@ -5,177 +5,162 @@ namespace SmartStore.Services.DataExchange.Import { - public class ImportResult : ICloneable - { - public ImportResult() - { - this.Messages = new List(); - Clear(); - } - - public DateTime StartDateUtc - { - get; - set; - } - - public DateTime EndDateUtc - { - get; - set; - } - - public int TotalRecords - { - get; - set; - } - - public int SkippedRecords - { - get; - set; - } - - public int NewRecords - { - get; - set; - } - - public int ModifiedRecords - { - get; - set; - } - - public int AffectedRecords - { - get { return NewRecords + ModifiedRecords; } - } - - public bool Cancelled - { - get; - set; - } - - public void Clear() - { - Messages.Clear(); - StartDateUtc = EndDateUtc = DateTime.UtcNow; - TotalRecords = 0; - SkippedRecords = 0; - NewRecords = 0; - ModifiedRecords = 0; - Cancelled = false; - } - - public ImportMessage AddInfo(string message, ImportRowInfo affectedRow = null, string affectedField = null) - { - return this.AddMessage(message, ImportMessageType.Info, affectedRow, affectedField); - } - - public ImportMessage AddWarning(string message, ImportRowInfo affectedRow = null, string affectedField = null) - { - return this.AddMessage(message, ImportMessageType.Warning, affectedRow, affectedField); - } - - public ImportMessage AddError(string message, ImportRowInfo affectedRow = null, string affectedField = null) - { - return this.AddMessage(message, ImportMessageType.Error, affectedRow, affectedField); - } - - public ImportMessage AddError(Exception exception, int? affectedBatch = null, string stage = null) - { - var prefix = new List(); - if (affectedBatch.HasValue) - { - prefix.Add("Batch: " + affectedBatch.Value); - } - if (stage.HasValue()) - { - prefix.Add("Stage: " + stage); - } - - string msg = string.Empty; - if (prefix.Any()) - { - msg = "[{0}] ".FormatCurrent(String.Join(", ", prefix)); - } - - msg += exception.ToAllMessages(); - - return this.AddMessage(msg, ImportMessageType.Error, fullMessage: exception.StackTrace); - } - - public ImportMessage AddError(Exception exception, string message) - { - return AddMessage( - message ?? exception.ToAllMessages(), - ImportMessageType.Error, - null, - null, - exception.StackTrace); - } - - public ImportMessage AddMessage(string message, ImportMessageType severity, ImportRowInfo affectedRow = null, string affectedField = null, string fullMessage = null) - { - var msg = new ImportMessage(message, severity); - - msg.AffectedItem = affectedRow; - msg.AffectedField = affectedField; - msg.FullMessage = fullMessage; - - this.Messages.Add(msg); - return msg; - } - - public IList Messages - { - get; - private set; - } - - public bool HasWarnings - { - get { return this.Messages.Any(x => x.MessageType == ImportMessageType.Warning); } - } - - public int Warnings - { - get { return Messages.Count(x => x.MessageType == ImportMessageType.Warning); } - } - - public bool HasErrors - { - get { return this.Messages.Any(x => x.MessageType == ImportMessageType.Error); } - } - - public int Errors - { - get { return Messages.Count(x => x.MessageType == ImportMessageType.Error); } - } - - public string LastError - { - get - { - var lastError = Messages.LastOrDefault(x => x.MessageType == ImportMessageType.Error); - if (lastError != null) - return lastError.Message; - - return null; - } - } - - object ICloneable.Clone() - { - return this.MemberwiseClone(); - } - - public SerializableImportResult Clone() - { + public class ImportResult : ICloneable + { + public ImportResult() + { + this.Messages = new List(); + Clear(); + } + + public DateTime StartDateUtc + { + get; + set; + } + + public DateTime EndDateUtc + { + get; + set; + } + + public int TotalRecords + { + get; + set; + } + + public int SkippedRecords + { + get; + set; + } + + public int NewRecords + { + get; + set; + } + + public int ModifiedRecords + { + get; + set; + } + + public int AffectedRecords => NewRecords + ModifiedRecords; + + public bool Cancelled + { + get; + set; + } + + public void Clear() + { + Messages.Clear(); + StartDateUtc = EndDateUtc = DateTime.UtcNow; + TotalRecords = 0; + SkippedRecords = 0; + NewRecords = 0; + ModifiedRecords = 0; + Cancelled = false; + } + + public ImportMessage AddInfo(string message, ImportRowInfo affectedRow = null, string affectedField = null) + { + return this.AddMessage(message, ImportMessageType.Info, affectedRow, affectedField); + } + + public ImportMessage AddWarning(string message, ImportRowInfo affectedRow = null, string affectedField = null) + { + return this.AddMessage(message, ImportMessageType.Warning, affectedRow, affectedField); + } + + public ImportMessage AddError(string message, ImportRowInfo affectedRow = null, string affectedField = null) + { + return this.AddMessage(message, ImportMessageType.Error, affectedRow, affectedField); + } + + public ImportMessage AddError(Exception exception, int? affectedBatch = null, string stage = null) + { + var prefix = new List(); + if (affectedBatch.HasValue) + { + prefix.Add("Batch: " + affectedBatch.Value); + } + if (stage.HasValue()) + { + prefix.Add("Stage: " + stage); + } + + string msg = string.Empty; + if (prefix.Any()) + { + msg = "[{0}] ".FormatCurrent(String.Join(", ", prefix)); + } + + msg += exception.ToAllMessages(); + + return this.AddMessage(msg, ImportMessageType.Error, fullMessage: exception.StackTrace); + } + + public ImportMessage AddError(Exception exception, string message) + { + return AddMessage( + message ?? exception.ToAllMessages(), + ImportMessageType.Error, + null, + null, + exception.StackTrace); + } + + public ImportMessage AddMessage(string message, ImportMessageType severity, ImportRowInfo affectedRow = null, string affectedField = null, string fullMessage = null) + { + var msg = new ImportMessage(message, severity); + + msg.AffectedItem = affectedRow; + msg.AffectedField = affectedField; + msg.FullMessage = fullMessage; + + this.Messages.Add(msg); + return msg; + } + + public IList Messages + { + get; + private set; + } + + public bool HasWarnings => this.Messages.Any(x => x.MessageType == ImportMessageType.Warning); + + public int Warnings => Messages.Count(x => x.MessageType == ImportMessageType.Warning); + + public bool HasErrors => this.Messages.Any(x => x.MessageType == ImportMessageType.Error); + + public int Errors => Messages.Count(x => x.MessageType == ImportMessageType.Error); + + public string LastError + { + get + { + var lastError = Messages.LastOrDefault(x => x.MessageType == ImportMessageType.Error); + if (lastError != null) + return lastError.Message; + + return null; + } + } + + object ICloneable.Clone() + { + return this.MemberwiseClone(); + } + + public SerializableImportResult Clone() + { var result = new SerializableImportResult(); result.StartDateUtc = StartDateUtc; result.EndDateUtc = EndDateUtc; @@ -191,22 +176,22 @@ public SerializableImportResult Clone() return result; } - } - - - [Serializable] - public partial class SerializableImportResult - { - public DateTime StartDateUtc { get; set; } - public DateTime EndDateUtc { get; set; } - public int TotalRecords { get; set; } - public int SkippedRecords { get; set; } - public int NewRecords { get; set; } - public int ModifiedRecords { get; set; } - public int AffectedRecords { get; set; } - public bool Cancelled { get; set; } - public int Warnings { get; set; } - public int Errors { get; set; } - public string LastError { get; set; } - } + } + + + [Serializable] + public partial class SerializableImportResult + { + public DateTime StartDateUtc { get; set; } + public DateTime EndDateUtc { get; set; } + public int TotalRecords { get; set; } + public int SkippedRecords { get; set; } + public int NewRecords { get; set; } + public int ModifiedRecords { get; set; } + public int AffectedRecords { get; set; } + public bool Cancelled { get; set; } + public int Warnings { get; set; } + public int Errors { get; set; } + public string LastError { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportRow.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportRow.cs index a709f5288e..35cbf0b5bd 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/ImportRow.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/ImportRow.cs @@ -1,332 +1,308 @@ using System; using System.Globalization; using System.Linq.Expressions; +using System.Runtime.CompilerServices; using SmartStore.ComponentModel; using SmartStore.Core; namespace SmartStore.Services.DataExchange.Import { - public class ImportRow where T : BaseEntity - { - private const string ExplicitNull = "[NULL]"; - private const string ExplicitIgnore = "[IGNORE]"; - - private bool _initialized = false; - private T _entity; - private string _entityDisplayName; - private readonly int _position; - private bool _isNew; - private bool _isDirty; - private ImportRowInfo _rowInfo; - - private readonly ImportDataSegmenter _segmenter; - private readonly IDataRow _row; - - public ImportRow(ImportDataSegmenter parent, IDataRow row, int position) - { - _segmenter = parent; - _row = row; - _position = position; - } - - public void Initialize(T entity, string entityDisplayName) - { - _entity = entity; - _entityDisplayName = entityDisplayName; - _isNew = _entity.Id == 0; - - _initialized = true; - } - - private void CheckInitialized() - { - if (_initialized) - { - throw Error.InvalidOperation("A row must be initialized before interacting with the entity or the data store"); - } - } - - private TProp GetDefaultValue(ColumnMappingItem mapping, TProp defaultValue, ImportResult result = null) - { - if (mapping != null && mapping.Default.HasValue()) - { - try - { - return mapping.Default.Convert(_segmenter.Culture); - } - catch (Exception exception) - { - if (result != null) - { - var msg = "Failed to convert default value '{0}'. Please specify a convertable default value. Column: {1}"; - result.AddWarning(msg.FormatInvariant(mapping.Default, exception.Message), this.GetRowInfo(), mapping.SoureName); - } - } - } - - return defaultValue; - } - - public bool IsTransient - { - get { return _entity.Id == 0; } - } - - public bool IsNew - { - get { return _isNew; } - } - - public bool IsDirty - { - get { return _isDirty; } - } - - public ImportDataSegmenter Segmenter - { - get { return _segmenter; } - } - - public T Entity - { - get { return _entity; } - } - - public IDataRow DataRow - { - get { return _row; } - } - - public string EntityDisplayName - { - get { return _entityDisplayName; } - } - - public bool NameChanged - { - get; - set; - } - - public int Position - { - get { return _position; } - } - - /// - /// Determines whether a specific column exists in the underlying data table - /// and contains a non-null, convertible value. - /// - /// The name of the column - /// - /// If true and a column with the passed does not exist, - /// this method seeks for any indexed column with the same name. - /// - /// true if the column exists and contains a value, false otherwise - /// - /// This method takes mapped column names into account. - /// - public bool HasDataValue(string columnName, bool withAnyIndex = false) - { - var result = HasDataValue(columnName, null); - - if (!result && withAnyIndex) - { - // Column does not have a value, but withAnyIndex is true: - // Test for values in any indexed column. - var indexes = _segmenter.GetColumnIndexes(columnName); - foreach (var idx in indexes) - { - result = HasDataValue(columnName, idx); - if (result) - break; - } - } - - return result; - } - - /// - /// Determines whether the column name[index] exists in the underlying data table - /// and contains a non-null, convertible value. - /// - /// The name of the column - /// The index of the column - /// true if the column exists and contains a value, false otherwise - /// - /// This method takes mapped column names into account. - /// - public bool HasDataValue(string columnName, string index) - { - var mapping = _segmenter.ColumnMap.GetMapping(columnName, index); - - object value; - return (_row.TryGetValue(mapping.MappedName, out value) && value != null && value != DBNull.Value); - } - - public TProp GetDataValue(string columnName, bool force = false) - { - TProp value; - TryGetDataValue(columnName, null, out value, force); - return value; - } - - public TProp GetDataValue(string columnName, string index, bool force = false) - { - TProp value; - TryGetDataValue(columnName, index, out value, force); - return value; - } - - public bool TryGetDataValue(string columnName, out TProp value, bool force = false) - { - return TryGetDataValue(columnName, null, out value, force); - } - - public bool TryGetDataValue(string columnName, string index, out TProp value, bool force = false) - { - var mapping = _segmenter.ColumnMap.GetMapping(columnName, index); - - if (!force && mapping.IgnoreProperty) - { - value = default(TProp); - return false; - } - - object rawValue; - if (_row.TryGetValue(mapping.MappedName, out rawValue) && rawValue != null && rawValue != DBNull.Value && !rawValue.ToString().IsCaseInsensitiveEqual(ExplicitIgnore)) - { - value = rawValue.ToString().IsCaseInsensitiveEqual(ExplicitNull) - ? default(TProp) - : rawValue.Convert(_segmenter.Culture); - return true; - } - - if (IsNew) - { - // only transient/new entities should fallback to possible defaults. - value = GetDefaultValue(mapping, default(TProp)); - return true; - } - - value = default(TProp); - return false; - } - - public bool SetProperty( - ImportResult result, - Expression> prop, - TProp defaultValue = default(TProp), - Func converter = null) - { - return SetProperty( - result, - null, // columnName - prop, - defaultValue, - converter); - } - - public bool SetProperty( - ImportResult result, - string columnName, - Expression> prop, - TProp defaultValue = default(TProp), - Func converter = null) - { - // TBD: (MC) do not check or validate for perf reason? - //CheckInitialized(); - - var isPropertySet = false; - var pi = prop.ExtractPropertyInfo(); - var propName = pi.Name; - var target = _entity; - - columnName = columnName ?? propName; - - try - { - object value; - var mapping = _segmenter.ColumnMap.GetMapping(columnName); - - if (mapping.IgnoreProperty) - { - // explicitly ignore this property - } - else if (_row.TryGetValue(mapping.MappedName, out value) && value != null && value != DBNull.Value && !value.ToString().IsCaseInsensitiveEqual(ExplicitIgnore)) - { - // source contains field value. Set it. - TProp converted; - if (converter != null) - { - converted = converter(value, _segmenter.Culture); - } - else if (value.ToString().IsCaseInsensitiveEqual(ExplicitNull)) - { - // prop is "explicitly" set to null. Don't fallback to any default! - converted = default(TProp); - } - else - { - converted = value.Convert(_segmenter.Culture); - } - - var fastProp = FastProperty.GetProperty(target.GetUnproxiedType(), propName, PropertyCachingStrategy.EagerCached); - fastProp.SetValue(target, converted); - isPropertySet = true; - } - else - { - // source field value does not exist or is null/empty - if (IsNew) - { - // if entity is new and source field value is null, determine default value in this particular order: - // 2.) Default value in field mapping table - // 3.) passed default value argument - defaultValue = GetDefaultValue(mapping, defaultValue, result); - - // source does not contain field data or is empty... - if (defaultValue != null) - { - // ...but the entity is new. In this case set the default value if given. - var fastProp = FastProperty.GetProperty(target.GetUnproxiedType(), propName, PropertyCachingStrategy.EagerCached); - fastProp.SetValue(target, defaultValue); - isPropertySet = true; - } - } - } - } - catch (Exception exception) - { - result.AddWarning("Conversion failed: " + exception.Message, this.GetRowInfo(), propName); - } - - if (isPropertySet && !_isDirty) - { - _isDirty = true; - } - - return isPropertySet; - } - - public ImportRowInfo GetRowInfo() - { - if (_rowInfo == null) - { - _rowInfo = new ImportRowInfo(this.Position, this.EntityDisplayName); - } - - return _rowInfo; - } - - public override string ToString() - { - var str = "Pos: {0} - Name: {1}, IsNew: {2}, IsTransient: {3}".FormatCurrent( - Position, - EntityDisplayName.EmptyNull(), - _initialized ? IsNew.ToString() : "-", - _initialized ? IsTransient.ToString() : "-"); - return str; - } - } + public class ImportRow where T : BaseEntity + { + private const string ExplicitNull = "[NULL]"; + private const string ExplicitIgnore = "[IGNORE]"; + + private bool _initialized = false; + private T _entity; + private string _entityDisplayName; + private readonly int _position; + private bool _isNew; + private bool _isDirty; + private ImportRowInfo _rowInfo; + + private readonly ImportDataSegmenter _segmenter; + private readonly IDataRow _row; + + public ImportRow(ImportDataSegmenter parent, IDataRow row, int position) + { + _segmenter = parent; + _row = row; + _position = position; + } + + public void Initialize(T entity, string entityDisplayName) + { + _entity = entity; + _entityDisplayName = entityDisplayName; + _isNew = _entity.Id == 0; + + _initialized = true; + } + + private void CheckInitialized() + { + if (_initialized) + { + throw Error.InvalidOperation("A row must be initialized before interacting with the entity or the data store"); + } + } + + private TProp GetDefaultValue(ColumnMappingItem mapping, TProp defaultValue, ImportResult result = null) + { + if (mapping != null && mapping.Default.HasValue()) + { + try + { + return mapping.Default.Convert(_segmenter.Culture); + } + catch (Exception exception) + { + if (result != null) + { + var msg = "Failed to convert default value '{0}'. Please specify a convertable default value. Column: {1}"; + result.AddWarning(msg.FormatInvariant(mapping.Default, exception.Message), this.GetRowInfo(), mapping.SoureName); + } + } + } + + return defaultValue; + } + + public bool IsTransient => _entity.Id == 0; + + public bool IsNew => _isNew; + + public bool IsDirty => _isDirty; + + public ImportDataSegmenter Segmenter => _segmenter; + + public T Entity => _entity; + + public IDataRow DataRow => _row; + + public string EntityDisplayName => _entityDisplayName; + + public bool NameChanged + { + get; + set; + } + + public int Position => _position; + + /// + /// Determines whether a specific column exists in the underlying data table + /// and contains a non-null, convertible value. + /// + /// The name of the column + /// + /// If true and a column with the passed does not exist, + /// this method seeks for any indexed column with the same name. + /// + /// true if the column exists and contains a value, false otherwise + /// + /// This method takes mapped column names into account. + /// + public bool HasDataValue(string columnName, bool withAnyIndex = false) + { + var result = HasDataValue(columnName, null); + + if (!result && withAnyIndex) + { + // Column does not have a value, but withAnyIndex is true: + // Test for values in any indexed column. + var indexes = _segmenter.GetColumnIndexes(columnName); + foreach (var idx in indexes) + { + result = HasDataValue(columnName, idx); + if (result) + break; + } + } + + return result; + } + + /// + /// Determines whether the column name[index] exists in the underlying data table + /// and contains a non-null, convertible value. + /// + /// The name of the column + /// The index of the column + /// true if the column exists and contains a value, false otherwise + /// + /// This method takes mapped column names into account. + /// + public bool HasDataValue(string columnName, string index) + { + var mapping = _segmenter.ColumnMap.GetMapping(columnName, index); + return (_row.TryGetValue(mapping.MappedName, out var value) && value != null && value != DBNull.Value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TProp GetDataValue(string columnName, bool force = false) + { + TryGetDataValue(columnName, null, out var value, force); + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TProp GetDataValue(string columnName, string index, bool force = false) + { + TryGetDataValue(columnName, index, out var value, force); + return value; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetDataValue(string columnName, out TProp value, bool force = false) + { + return TryGetDataValue(columnName, null, out value, force); + } + + public bool TryGetDataValue(string columnName, string index, out TProp value, bool force = false) + { + var mapping = _segmenter.ColumnMap.GetMapping(columnName, index); + + if (!force && mapping.IgnoreProperty) + { + value = default(TProp); + return false; + } + + if (_row.TryGetValue(mapping.MappedName, out var rawValue) && rawValue != null && rawValue != DBNull.Value && !rawValue.ToString().IsCaseInsensitiveEqual(ExplicitIgnore)) + { + value = rawValue.ToString().IsCaseInsensitiveEqual(ExplicitNull) + ? default(TProp) + : rawValue.Convert(_segmenter.Culture); + return true; + } + + if (IsNew) + { + // only transient/new entities should fallback to possible defaults. + value = GetDefaultValue(mapping, default(TProp)); + return true; + } + + value = default(TProp); + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool SetProperty( + ImportResult result, + Expression> prop, + TProp defaultValue = default(TProp), + Func converter = null) + { + return SetProperty( + result, + null, // columnName + prop, + defaultValue, + converter); + } + + public bool SetProperty( + ImportResult result, + string columnName, + Expression> prop, + TProp defaultValue = default(TProp), + Func converter = null) + { + // TBD: (MC) do not check or validate for perf reason? + //CheckInitialized(); + + var isPropertySet = false; + var pi = prop.ExtractPropertyInfo(); + var propName = pi.Name; + var target = _entity; + + columnName = columnName ?? propName; + + try + { + object value; + var mapping = _segmenter.ColumnMap.GetMapping(columnName); + + if (mapping.IgnoreProperty) + { + // explicitly ignore this property + } + else if (_row.TryGetValue(mapping.MappedName, out value) && value != null && value != DBNull.Value && !value.ToString().IsCaseInsensitiveEqual(ExplicitIgnore)) + { + // source contains field value. Set it. + TProp converted; + if (converter != null) + { + converted = converter(value, _segmenter.Culture); + } + else if (value.ToString().IsCaseInsensitiveEqual(ExplicitNull)) + { + // prop is "explicitly" set to null. Don't fallback to any default! + converted = default(TProp); + } + else + { + converted = value.Convert(_segmenter.Culture); + } + + var fastProp = FastProperty.GetProperty(target.GetUnproxiedType(), propName, PropertyCachingStrategy.EagerCached); + fastProp.SetValue(target, converted); + isPropertySet = true; + } + else + { + // source field value does not exist or is null/empty + if (IsNew) + { + // if entity is new and source field value is null, determine default value in this particular order: + // 2.) Default value in field mapping table + // 3.) passed default value argument + defaultValue = GetDefaultValue(mapping, defaultValue, result); + + // source does not contain field data or is empty... + if (defaultValue != null) + { + // ...but the entity is new. In this case set the default value if given. + var fastProp = FastProperty.GetProperty(target.GetUnproxiedType(), propName, PropertyCachingStrategy.EagerCached); + fastProp.SetValue(target, defaultValue); + isPropertySet = true; + } + } + } + } + catch (Exception exception) + { + result.AddWarning("Conversion failed: " + exception.Message, this.GetRowInfo(), propName); + } + + if (isPropertySet && !_isDirty) + { + _isDirty = true; + } + + return isPropertySet; + } + + public ImportRowInfo GetRowInfo() + { + if (_rowInfo == null) + { + _rowInfo = new ImportRowInfo(this.Position, this.EntityDisplayName); + } + + return _rowInfo; + } + + public override string ToString() + { + var str = "Pos: {0} - Name: {1}, IsNew: {2}, IsTransient: {3}".FormatCurrent( + Position, + EntityDisplayName.EmptyNull(), + _initialized ? IsNew.ToString() : "-", + _initialized ? IsTransient.ToString() : "-"); + return str; + } + } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/Import/Internal/DataImporterContext.cs b/src/Libraries/SmartStore.Services/DataExchange/Import/Internal/DataImporterContext.cs index f9f36594cc..3bb371dc5b 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/Import/Internal/DataImporterContext.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/Import/Internal/DataImporterContext.cs @@ -4,17 +4,17 @@ namespace SmartStore.Services.DataExchange.Import.Internal { - internal class DataImporterContext - { - public DataImporterContext( - DataImportRequest request, - CancellationToken cancellationToken, - string progressInfo) - { - Request = request; - CancellationToken = cancellationToken; + internal class DataImporterContext + { + public DataImporterContext( + DataImportRequest request, + CancellationToken cancellationToken, + string progressInfo) + { + Request = request; + CancellationToken = cancellationToken; - ExecuteContext = new ImportExecuteContext(CancellationToken, Request.ProgressValueSetter, progressInfo) + ExecuteContext = new ImportExecuteContext(CancellationToken, Request.ProgressValueSetter, progressInfo) { Request = request }; @@ -23,12 +23,12 @@ public DataImporterContext( Results = new Dictionary(); } - public DataImportRequest Request { get; private set; } - public CancellationToken CancellationToken { get; private set; } + public DataImportRequest Request { get; private set; } + public CancellationToken CancellationToken { get; private set; } - public TraceLogger Log { get; set; } - public ImportExecuteContext ExecuteContext { get; set; } - public IEntityImporter Importer { get; set; } + public TraceLogger Log { get; set; } + public ImportExecuteContext ExecuteContext { get; set; } + public IEntityImporter Importer { get; set; } public ColumnMap ColumnMap { get; private set; } public Dictionary Results { get; private set; } } diff --git a/src/Libraries/SmartStore.Services/DataExchange/SyncMappingService.cs b/src/Libraries/SmartStore.Services/DataExchange/SyncMappingService.cs index 8b455a6955..bd3add028f 100644 --- a/src/Libraries/SmartStore.Services/DataExchange/SyncMappingService.cs +++ b/src/Libraries/SmartStore.Services/DataExchange/SyncMappingService.cs @@ -8,137 +8,137 @@ namespace SmartStore.Services.DataExchange { - public partial class SyncMappingService : ISyncMappingService - { - private readonly IRepository _syncMappingsRepository; - - public SyncMappingService(IRepository syncMappingsRepository) - { - this._syncMappingsRepository = syncMappingsRepository; - } - - public void InsertSyncMapping(SyncMapping mapping) - { - Guard.NotNull(mapping, nameof(mapping)); - - _syncMappingsRepository.Insert(mapping); - } - - public void InsertSyncMappings(IEnumerable mappings) - { - Guard.NotNull(mappings, nameof(mappings)); - - _syncMappingsRepository.InsertRange(mappings); - } - - public IList GetAllSyncMappings(string contextName = null, string entityName = null, int[] entityIds = null) - { - var query = _syncMappingsRepository.Table; - - if (entityName.HasValue()) - { - query = query.Where(x => x.EntityName == entityName); - } - - if (contextName.HasValue()) - { - query = query.Where(x => x.ContextName == contextName); - } - - if (entityIds != null && entityIds.Any()) - { - query = query.Where(x => entityIds.Contains(x.EntityId)); - } - - return query.ToList(); - } - - public SyncMapping GetSyncMappingByEntity(int entityId, string entityName, string contextName) - { - Guard.IsPositive(entityId, nameof(entityId)); - Guard.NotEmpty(entityName, nameof(entityName)); - Guard.NotEmpty(contextName, nameof(contextName)); - - var query = from x in _syncMappingsRepository.Table - where - x.EntityId == entityId - && x.EntityName == entityName - && x.ContextName == contextName - select x; - - return query.FirstOrDefault(); - } - - public SyncMapping GetSyncMappingBySource(string sourceKey, string entityName, string contextName) - { - Guard.NotEmpty(sourceKey, nameof(sourceKey)); - Guard.NotEmpty(entityName, nameof(entityName)); - Guard.NotEmpty(contextName, nameof(contextName)); - - var query = from x in _syncMappingsRepository.Table - where - x.SourceKey == sourceKey - && x.EntityName == entityName - && x.ContextName == contextName - select x; - - return query.FirstOrDefault(); - } - - public void DeleteSyncMapping(SyncMapping mapping) - { - Guard.NotNull(mapping, nameof(mapping)); - - _syncMappingsRepository.Delete(mapping); - } - - public void DeleteSyncMappings(IEnumerable mappings) - { - Guard.NotNull(mappings, nameof(mappings)); - - _syncMappingsRepository.DeleteRange(mappings); - } - - public void DeleteSyncMappingsFor(T entity) where T : BaseEntity - { - Guard.NotNull(entity, nameof(entity)); - - if (entity is SyncMapping) - { - throw Error.InvalidOperation("Cannot delete a sync mapping record for a SyncMapping entity"); - } - - if (entity.IsTransientRecord()) - { - return; - } - - _syncMappingsRepository.DeleteAll(x => x.EntityId == entity.Id && x.EntityName == entity.GetEntityName()); - } - - public void DeleteSyncMappings(string contextName, string entityName = null) - { - Guard.NotEmpty(contextName, nameof(contextName)); - - if (entityName.HasValue()) - { - _syncMappingsRepository.DeleteAll(x => x.ContextName == contextName && x.EntityName == entityName); - } - else - { - _syncMappingsRepository.DeleteAll(x => x.ContextName == contextName); - } - } - - - public void UpdateSyncMapping(SyncMapping mapping) - { - Guard.NotNull(mapping, nameof(mapping)); - - mapping.SyncedOnUtc = DateTime.UtcNow; - _syncMappingsRepository.Update(mapping); - } - - } + public partial class SyncMappingService : ISyncMappingService + { + private readonly IRepository _syncMappingsRepository; + + public SyncMappingService(IRepository syncMappingsRepository) + { + this._syncMappingsRepository = syncMappingsRepository; + } + + public void InsertSyncMapping(SyncMapping mapping) + { + Guard.NotNull(mapping, nameof(mapping)); + + _syncMappingsRepository.Insert(mapping); + } + + public void InsertSyncMappings(IEnumerable mappings) + { + Guard.NotNull(mappings, nameof(mappings)); + + _syncMappingsRepository.InsertRange(mappings); + } + + public IList GetAllSyncMappings(string contextName = null, string entityName = null, int[] entityIds = null) + { + var query = _syncMappingsRepository.Table; + + if (entityName.HasValue()) + { + query = query.Where(x => x.EntityName == entityName); + } + + if (contextName.HasValue()) + { + query = query.Where(x => x.ContextName == contextName); + } + + if (entityIds != null && entityIds.Any()) + { + query = query.Where(x => entityIds.Contains(x.EntityId)); + } + + return query.ToList(); + } + + public SyncMapping GetSyncMappingByEntity(int entityId, string entityName, string contextName) + { + Guard.IsPositive(entityId, nameof(entityId)); + Guard.NotEmpty(entityName, nameof(entityName)); + Guard.NotEmpty(contextName, nameof(contextName)); + + var query = from x in _syncMappingsRepository.Table + where + x.EntityId == entityId + && x.EntityName == entityName + && x.ContextName == contextName + select x; + + return query.FirstOrDefault(); + } + + public SyncMapping GetSyncMappingBySource(string sourceKey, string entityName, string contextName) + { + Guard.NotEmpty(sourceKey, nameof(sourceKey)); + Guard.NotEmpty(entityName, nameof(entityName)); + Guard.NotEmpty(contextName, nameof(contextName)); + + var query = from x in _syncMappingsRepository.Table + where + x.SourceKey == sourceKey + && x.EntityName == entityName + && x.ContextName == contextName + select x; + + return query.FirstOrDefault(); + } + + public void DeleteSyncMapping(SyncMapping mapping) + { + Guard.NotNull(mapping, nameof(mapping)); + + _syncMappingsRepository.Delete(mapping); + } + + public void DeleteSyncMappings(IEnumerable mappings) + { + Guard.NotNull(mappings, nameof(mappings)); + + _syncMappingsRepository.DeleteRange(mappings); + } + + public void DeleteSyncMappingsFor(T entity) where T : BaseEntity + { + Guard.NotNull(entity, nameof(entity)); + + if (entity is SyncMapping) + { + throw Error.InvalidOperation("Cannot delete a sync mapping record for a SyncMapping entity"); + } + + if (entity.IsTransientRecord()) + { + return; + } + + _syncMappingsRepository.DeleteAll(x => x.EntityId == entity.Id && x.EntityName == entity.GetEntityName()); + } + + public void DeleteSyncMappings(string contextName, string entityName = null) + { + Guard.NotEmpty(contextName, nameof(contextName)); + + if (entityName.HasValue()) + { + _syncMappingsRepository.DeleteAll(x => x.ContextName == contextName && x.EntityName == entityName); + } + else + { + _syncMappingsRepository.DeleteAll(x => x.ContextName == contextName); + } + } + + + public void UpdateSyncMapping(SyncMapping mapping) + { + Guard.NotNull(mapping, nameof(mapping)); + + mapping.SyncedOnUtc = DateTime.UtcNow; + _syncMappingsRepository.Update(mapping); + } + + } } diff --git a/src/Libraries/SmartStore.Services/Directory/CountryService.cs b/src/Libraries/SmartStore.Services/Directory/CountryService.cs index 13f6b21646..6e05e0e600 100644 --- a/src/Libraries/SmartStore.Services/Directory/CountryService.cs +++ b/src/Libraries/SmartStore.Services/Directory/CountryService.cs @@ -14,28 +14,28 @@ public partial class CountryService : ICountryService private const string COUNTRIES_SHIPPING_KEY = "SmartStore.country.shipping-{0}"; private const string COUNTRIES_PATTERN_KEY = "SmartStore.country.*"; - private readonly ICommonServices _services; - private readonly IRepository _countryRepository; - private readonly IRepository _storeMappingRepository; + private readonly ICommonServices _services; + private readonly IRepository _countryRepository; + private readonly IRepository _storeMappingRepository; - public CountryService( - ICommonServices services, + public CountryService( + ICommonServices services, IRepository countryRepository, - IRepository storeMappingRepository) + IRepository storeMappingRepository) { _countryRepository = countryRepository; - _services = services; - _storeMappingRepository = storeMappingRepository; + _services = services; + _storeMappingRepository = storeMappingRepository; } - public DbQuerySettings QuerySettings { get; set; } + public DbQuerySettings QuerySettings { get; set; } public virtual void DeleteCountry(Country country) { - Guard.NotNull(country, nameof(country)); + Guard.NotNull(country, nameof(country)); + + _countryRepository.Delete(country); - _countryRepository.Delete(country); - _services.RequestCache.RemoveByPattern(COUNTRIES_PATTERN_KEY); } @@ -44,30 +44,30 @@ public virtual IList GetAllCountries(bool showHidden = false) string key = string.Format(COUNTRIES_ALL_KEY, showHidden); return _services.RequestCache.Get(key, () => { - var query = _countryRepository.Table; + var query = _countryRepository.Table; - if (!showHidden) - query = query.Where(c => c.Published); + if (!showHidden) + query = query.Where(c => c.Published); - query = query.OrderBy(c => c.DisplayOrder).ThenBy(c => c.Name); + query = query.OrderBy(c => c.DisplayOrder).ThenBy(c => c.Name); - if (!showHidden && !QuerySettings.IgnoreMultiStore) - { - var currentStoreId = _services.StoreContext.CurrentStore.Id; - query = from c in query - join sc in _storeMappingRepository.Table - on new { c1 = c.Id, c2 = "Country" } equals new { c1 = sc.EntityId, c2 = sc.EntityName } into c_sm - from sc in c_sm.DefaultIfEmpty() - where !c.LimitedToStores || currentStoreId == sc.StoreId - select c; + if (!showHidden && !QuerySettings.IgnoreMultiStore) + { + var currentStoreId = _services.StoreContext.CurrentStore.Id; + query = from c in query + join sc in _storeMappingRepository.Table + on new { c1 = c.Id, c2 = "Country" } equals new { c1 = sc.EntityId, c2 = sc.EntityName } into c_sm + from sc in c_sm.DefaultIfEmpty() + where !c.LimitedToStores || currentStoreId == sc.StoreId + select c; - query = from c in query - group c by c.Id into cGroup - orderby cGroup.Key - select cGroup.FirstOrDefault(); + query = from c in query + group c by c.Id into cGroup + orderby cGroup.Key + select cGroup.FirstOrDefault(); - query = query.OrderBy(c => c.DisplayOrder).ThenBy(c => c.Name); - } + query = query.OrderBy(c => c.DisplayOrder).ThenBy(c => c.Name); + } var countries = query.ToList(); return countries; @@ -79,9 +79,9 @@ public virtual IList GetAllCountriesForBilling(bool showHidden = false) string key = string.Format(COUNTRIES_BILLING_KEY, showHidden); return _services.RequestCache.Get(key, () => { - var allCountries = GetAllCountries(showHidden); + var allCountries = GetAllCountries(showHidden); - var countries = allCountries.Where(x => x.AllowsBilling).ToList(); + var countries = allCountries.Where(x => x.AllowsBilling).ToList(); return countries; }); } @@ -90,8 +90,8 @@ public virtual IList GetAllCountriesForShipping(bool showHidden = false { string key = string.Format(COUNTRIES_SHIPPING_KEY, showHidden); return _services.RequestCache.Get(key, () => - { - var allCountries = GetAllCountries(showHidden); + { + var allCountries = GetAllCountries(showHidden); var countries = allCountries.Where(x => x.AllowsShipping).ToList(); return countries; @@ -106,22 +106,22 @@ public virtual Country GetCountryById(int countryId) return _countryRepository.GetById(countryId); } - public virtual Country GetCountryByTwoOrThreeLetterIsoCode(string letterIsoCode) - { - if (letterIsoCode.HasValue()) - { - if (letterIsoCode.Length == 2) - return GetCountryByTwoLetterIsoCode(letterIsoCode); - else if (letterIsoCode.Length == 3) - return GetCountryByThreeLetterIsoCode(letterIsoCode); - } - return null; - } + public virtual Country GetCountryByTwoOrThreeLetterIsoCode(string letterIsoCode) + { + if (letterIsoCode.HasValue()) + { + if (letterIsoCode.Length == 2) + return GetCountryByTwoLetterIsoCode(letterIsoCode); + else if (letterIsoCode.Length == 3) + return GetCountryByThreeLetterIsoCode(letterIsoCode); + } + return null; + } public virtual Country GetCountryByTwoLetterIsoCode(string twoLetterIsoCode) { - if (twoLetterIsoCode.IsEmpty()) - return null; + if (twoLetterIsoCode.IsEmpty()) + return null; var query = from c in _countryRepository.Table where c.TwoLetterIsoCode == twoLetterIsoCode @@ -133,8 +133,8 @@ public virtual Country GetCountryByTwoLetterIsoCode(string twoLetterIsoCode) public virtual Country GetCountryByThreeLetterIsoCode(string threeLetterIsoCode) { - if (threeLetterIsoCode.IsEmpty()) - return null; + if (threeLetterIsoCode.IsEmpty()) + return null; var query = from c in _countryRepository.Table where c.ThreeLetterIsoCode == threeLetterIsoCode @@ -146,20 +146,20 @@ public virtual Country GetCountryByThreeLetterIsoCode(string threeLetterIsoCode) public virtual void InsertCountry(Country country) { - Guard.NotNull(country, nameof(country)); + Guard.NotNull(country, nameof(country)); - _countryRepository.Insert(country); + _countryRepository.Insert(country); - _services.RequestCache.RemoveByPattern(COUNTRIES_PATTERN_KEY); + _services.RequestCache.RemoveByPattern(COUNTRIES_PATTERN_KEY); } public virtual void UpdateCountry(Country country) { - Guard.NotNull(country, nameof(country)); + Guard.NotNull(country, nameof(country)); _countryRepository.Update(country); - _services.RequestCache.RemoveByPattern(COUNTRIES_PATTERN_KEY); + _services.RequestCache.RemoveByPattern(COUNTRIES_PATTERN_KEY); } - } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Directory/CurrencyExtensions.cs b/src/Libraries/SmartStore.Services/Directory/CurrencyExtensions.cs index 03a9dfc7ee..a347e8f800 100644 --- a/src/Libraries/SmartStore.Services/Directory/CurrencyExtensions.cs +++ b/src/Libraries/SmartStore.Services/Directory/CurrencyExtensions.cs @@ -5,23 +5,23 @@ namespace SmartStore.Services.Directory { - public static class CurrencyExtensions - { - public static bool HasDomainEnding(this Currency currency, string domain) - { + public static class CurrencyExtensions + { + public static bool HasDomainEnding(this Currency currency, string domain) + { if (currency == null || domain.IsEmpty() || currency.DomainEndings.IsEmpty()) return false; - var endings = currency.DomainEndings.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); + var endings = currency.DomainEndings.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); return endings.Any(x => domain.EndsWith(x, StringComparison.InvariantCultureIgnoreCase)); - } + } - public static Currency GetByDomainEnding(this IEnumerable currencies, string domain) - { + public static Currency GetByDomainEnding(this IEnumerable currencies, string domain) + { if (currencies == null || domain.IsEmpty()) return null; return currencies.FirstOrDefault(x => x.Published && x.HasDomainEnding(domain)); - } - } + } + } } diff --git a/src/Libraries/SmartStore.Services/Directory/CurrencyService.cs b/src/Libraries/SmartStore.Services/Directory/CurrencyService.cs index d417801b19..09243d5246 100644 --- a/src/Libraries/SmartStore.Services/Directory/CurrencyService.cs +++ b/src/Libraries/SmartStore.Services/Directory/CurrencyService.cs @@ -15,45 +15,45 @@ namespace SmartStore.Services.Directory public partial class CurrencyService : ICurrencyService { private readonly IRepository _currencyRepository; - private readonly IStoreMappingService _storeMappingService; + private readonly IStoreMappingService _storeMappingService; private readonly CurrencySettings _currencySettings; private readonly IPluginFinder _pluginFinder; private readonly IEventPublisher _eventPublisher; - private readonly IProviderManager _providerManager; - private readonly IStoreContext _storeContext; + private readonly IProviderManager _providerManager; + private readonly IStoreContext _storeContext; public CurrencyService( IRepository currencyRepository, - IStoreMappingService storeMappingService, + IStoreMappingService storeMappingService, CurrencySettings currencySettings, IPluginFinder pluginFinder, IEventPublisher eventPublisher, - IProviderManager providerManager, - IStoreContext storeContext) + IProviderManager providerManager, + IStoreContext storeContext) { _currencyRepository = currencyRepository; - _storeMappingService = storeMappingService; + _storeMappingService = storeMappingService; _currencySettings = currencySettings; _pluginFinder = pluginFinder; _eventPublisher = eventPublisher; - _providerManager = providerManager; - _storeContext = storeContext; + _providerManager = providerManager; + _storeContext = storeContext; } public virtual IList GetCurrencyLiveRates(string exchangeRateCurrencyCode) { var exchangeRateProvider = LoadActiveExchangeRateProvider(); - if (exchangeRateProvider != null) - { - return exchangeRateProvider.Value.GetCurrencyLiveRates(exchangeRateCurrencyCode); - } - return new List(); + if (exchangeRateProvider != null) + { + return exchangeRateProvider.Value.GetCurrencyLiveRates(exchangeRateCurrencyCode); + } + return new List(); } public virtual void DeleteCurrency(Currency currency) { - Guard.NotNull(currency, nameof(currency)); - + Guard.NotNull(currency, nameof(currency)); + _currencyRepository.Delete(currency); } @@ -62,49 +62,49 @@ public virtual Currency GetCurrencyById(int currencyId) if (currencyId == 0) return null; - return _currencyRepository.GetByIdCached(currencyId, "db.cur.id-" + currencyId); - } + return _currencyRepository.GetByIdCached(currencyId, "db.cur.id-" + currencyId); + } public virtual Currency GetCurrencyByCode(string currencyCode) { - Guard.NotNull(currencyCode, nameof(currencyCode)); + Guard.NotNull(currencyCode, nameof(currencyCode)); - return GetAllCurrencies(true).FirstOrDefault(c => c.CurrencyCode.ToLower() == currencyCode.ToLower()); + return GetAllCurrencies(true).FirstOrDefault(c => c.CurrencyCode.ToLower() == currencyCode.ToLower()); } - public virtual IList GetAllCurrencies(bool showHidden = false, int storeId = 0) + public virtual IList GetAllCurrencies(bool showHidden = false, int storeId = 0) { - var query = _currencyRepository.Table; + var query = _currencyRepository.Table; - if (!showHidden) - query = query.Where(c => c.Published); + if (!showHidden) + query = query.Where(c => c.Published); - query = query.OrderBy(c => c.DisplayOrder); + query = query.OrderBy(c => c.DisplayOrder); - var currencies = query.ToListCached(); + var currencies = query.ToListCached(); - // store mapping - if (storeId > 0) - { - currencies = currencies - .Where(c => _storeMappingService.Authorize(c, storeId)) - .ToList(); - } - return currencies; + // store mapping + if (storeId > 0) + { + currencies = currencies + .Where(c => _storeMappingService.Authorize(c, storeId)) + .ToList(); + } + return currencies; } public virtual void InsertCurrency(Currency currency) { - Guard.NotNull(currency, nameof(currency)); + Guard.NotNull(currency, nameof(currency)); - _currencyRepository.Insert(currency); + _currencyRepository.Insert(currency); } public virtual void UpdateCurrency(Currency currency) { - Guard.NotNull(currency, nameof(currency)); + Guard.NotNull(currency, nameof(currency)); - _currencyRepository.Update(currency); + _currencyRepository.Update(currency); } public virtual decimal ConvertCurrency(decimal amount, decimal exchangeRate) @@ -115,7 +115,7 @@ public virtual decimal ConvertCurrency(decimal amount, decimal exchangeRate) return decimal.Zero; } - public virtual decimal ConvertCurrency(decimal amount, Currency sourceCurrency, Currency targetCurrency, Store store = null) + public virtual decimal ConvertCurrency(decimal amount, Currency sourceCurrency, Currency targetCurrency, Store store = null) { decimal result = amount; if (sourceCurrency.Id == targetCurrency.Id) @@ -130,10 +130,10 @@ public virtual decimal ConvertCurrency(decimal amount, Currency sourceCurrency, return result; } - public virtual decimal ConvertToPrimaryExchangeRateCurrency(decimal amount, Currency sourceCurrency, Store store = null) + public virtual decimal ConvertToPrimaryExchangeRateCurrency(decimal amount, Currency sourceCurrency, Store store = null) { decimal result = amount; - var primaryExchangeRateCurrency = (store == null ? _storeContext.CurrentStore.PrimaryExchangeRateCurrency : store.PrimaryExchangeRateCurrency); + var primaryExchangeRateCurrency = (store == null ? _storeContext.CurrentStore.PrimaryExchangeRateCurrency : store.PrimaryExchangeRateCurrency); if (result != decimal.Zero && sourceCurrency.Id != primaryExchangeRateCurrency.Id) { @@ -145,7 +145,7 @@ public virtual decimal ConvertToPrimaryExchangeRateCurrency(decimal amount, Curr return result; } - public virtual decimal ConvertFromPrimaryExchangeRateCurrency(decimal amount, Currency targetCurrency, Store store = null) + public virtual decimal ConvertFromPrimaryExchangeRateCurrency(decimal amount, Currency targetCurrency, Store store = null) { decimal result = amount; var primaryExchangeRateCurrency = (store == null ? _storeContext.CurrentStore.PrimaryExchangeRateCurrency : store.PrimaryExchangeRateCurrency); @@ -163,7 +163,7 @@ public virtual decimal ConvertFromPrimaryExchangeRateCurrency(decimal amount, Cu public virtual decimal ConvertToPrimaryStoreCurrency(decimal amount, Currency sourceCurrency, Store store = null) { decimal result = amount; - var primaryStoreCurrency = (store == null ? _storeContext.CurrentStore.PrimaryStoreCurrency : store.PrimaryStoreCurrency); + var primaryStoreCurrency = (store == null ? _storeContext.CurrentStore.PrimaryStoreCurrency : store.PrimaryStoreCurrency); if (result != decimal.Zero && sourceCurrency.Id != primaryStoreCurrency.Id) { @@ -175,27 +175,27 @@ public virtual decimal ConvertToPrimaryStoreCurrency(decimal amount, Currency so return result; } - public virtual decimal ConvertFromPrimaryStoreCurrency(decimal amount, Currency targetCurrency, Store store = null) + public virtual decimal ConvertFromPrimaryStoreCurrency(decimal amount, Currency targetCurrency, Store store = null) { decimal result = amount; - var primaryStoreCurrency = (store == null ? _storeContext.CurrentStore.PrimaryStoreCurrency : store.PrimaryStoreCurrency); + var primaryStoreCurrency = (store == null ? _storeContext.CurrentStore.PrimaryStoreCurrency : store.PrimaryStoreCurrency); result = ConvertCurrency(amount, primaryStoreCurrency, targetCurrency, store); return result; } public virtual Provider LoadActiveExchangeRateProvider() { - return LoadExchangeRateProviderBySystemName(_currencySettings.ActiveExchangeRateProviderSystemName) ?? LoadAllExchangeRateProviders().FirstOrDefault(); + return LoadExchangeRateProviderBySystemName(_currencySettings.ActiveExchangeRateProviderSystemName) ?? LoadAllExchangeRateProviders().FirstOrDefault(); } public virtual Provider LoadExchangeRateProviderBySystemName(string systemName) { - return _providerManager.GetProvider(systemName); + return _providerManager.GetProvider(systemName); } public virtual IEnumerable> LoadAllExchangeRateProviders() { - return _providerManager.GetAllProviders(); + return _providerManager.GetAllProviders(); } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Directory/DeliveryTimeService.cs b/src/Libraries/SmartStore.Services/Directory/DeliveryTimeService.cs index 2b3ba237fa..284303d652 100644 --- a/src/Libraries/SmartStore.Services/Directory/DeliveryTimeService.cs +++ b/src/Libraries/SmartStore.Services/Directory/DeliveryTimeService.cs @@ -1,57 +1,95 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Globalization; using System.Linq; +using System.Threading; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Directory; -using SmartStore.Core.Events; +using SmartStore.Core.Domain.Shipping; using SmartStore.Core.Localization; -using SmartStore.Core.Plugins; using SmartStore.Data.Caching; +using SmartStore.Data.Utilities; using SmartStore.Services.Catalog; -using SmartStore.Services.Customers; +using SmartStore.Services.Helpers; namespace SmartStore.Services.Directory { public partial class DeliveryTimeService : IDeliveryTimeService { + // Two letter ISO code to shortest month-day format pattern. + private readonly static ConcurrentDictionary _monthDayFormats = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private readonly IRepository _deliveryTimeRepository; private readonly IRepository _productRepository; private readonly IRepository _attributeCombinationRepository; - private readonly ICustomerService _customerService; - private readonly IPluginFinder _pluginFinder; - private readonly IEventPublisher _eventPublisher; - private readonly CatalogSettings _catalogSettings; + private readonly IDateTimeHelper _dateTimeHelper; + private readonly CatalogSettings _catalogSettings; + private readonly ShippingSettings _shippingSettings; public DeliveryTimeService( IRepository deliveryTimeRepository, IRepository productRepository, IRepository attributeCombinationRepository, - ICustomerService customerService, - IPluginFinder pluginFinder, - IEventPublisher eventPublisher, - CatalogSettings catalogSettings) + IDateTimeHelper dateTimeHelper, + CatalogSettings catalogSettings, + ShippingSettings shippingSettings) { - this._deliveryTimeRepository = deliveryTimeRepository; - this._customerService = customerService; - this._pluginFinder = pluginFinder; - this._eventPublisher = eventPublisher; - this._productRepository = productRepository; - this._attributeCombinationRepository = attributeCombinationRepository; - this._catalogSettings = catalogSettings; - - T = NullLocalizer.Instance; - } + _deliveryTimeRepository = deliveryTimeRepository; + _productRepository = productRepository; + _attributeCombinationRepository = attributeCombinationRepository; + _dateTimeHelper = dateTimeHelper; + _catalogSettings = catalogSettings; + _shippingSettings = shippingSettings; + } - public Localizer T { get; set; } + public Localizer T { get; set; } = NullLocalizer.Instance; - public virtual void DeleteDeliveryTime(DeliveryTime deliveryTime) + public virtual void DeleteDeliveryTime(DeliveryTime deliveryTime) { if (deliveryTime == null) - throw new ArgumentNullException("deliveryTime"); + { + return; + } + + // Remove associations to deleted products. + using (var scope = new DbContextScope(_productRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false)) + { + var productsQuery = _productRepository.Table.Where(x => x.Deleted && x.DeliveryTimeId == deliveryTime.Id); + var productsPager = new FastPager(productsQuery, 500); + + while (productsPager.ReadNextPage(out var products)) + { + if (products.Any()) + { + products.Each(x => x.DeliveryTimeId = null); + scope.Commit(); + } + } + + var attributeCombinationQuery = + from ac in _attributeCombinationRepository.Table + join p in _productRepository.Table on ac.ProductId equals p.Id + where p.Deleted && ac.DeliveryTimeId == deliveryTime.Id + select ac; + var attributeCombinationPager = new FastPager(attributeCombinationQuery, 1000); + + while (attributeCombinationPager.ReadNextPage(out var attributeCombinations)) + { + if (attributeCombinations.Any()) + { + attributeCombinations.Each(x => x.DeliveryTimeId = null); + scope.Commit(); + } + } + } - if (this.IsAssociated(deliveryTime.Id)) + // Warn if there are associations to active products. + if (IsAssociated(deliveryTime.Id)) + { throw new SmartException(T("Admin.Configuration.DeliveryTimes.CannotDeleteAssignedProducts")); + } _deliveryTimeRepository.Delete(deliveryTime); } @@ -61,10 +99,10 @@ public virtual bool IsAssociated(int deliveryTimeId) if (deliveryTimeId == 0) return false; - var query = - from p in _productRepository.Table - where p.DeliveryTimeId == deliveryTimeId || p.ProductVariantAttributeCombinations.Any(c => c.DeliveryTimeId == deliveryTimeId) - select p.Id; + var query = + from p in _productRepository.Table + where p.DeliveryTimeId == deliveryTimeId || p.ProductVariantAttributeCombinations.Any(c => c.DeliveryTimeId == deliveryTimeId) + select p.Id; return query.Count() > 0; } @@ -82,56 +120,210 @@ public virtual DeliveryTime GetDeliveryTimeById(int deliveryTimeId) return null; } } - - return _deliveryTimeRepository.GetByIdCached(deliveryTimeId, "deliverytime-{0}".FormatInvariant(deliveryTimeId)); + + return _deliveryTimeRepository.GetByIdCached(deliveryTimeId, "deliverytime-{0}".FormatInvariant(deliveryTimeId)); } public virtual DeliveryTime GetDeliveryTime(Product product) - { + { var deliveryTimeId = product.GetDeliveryTimeIdAccordingToStock(_catalogSettings); return GetDeliveryTimeById(deliveryTimeId ?? 0); - } + } public virtual IList GetAllDeliveryTimes() { - var query = _deliveryTimeRepository.Table.OrderBy(c => c.DisplayOrder); - var deliveryTimes = query.ToListCached("db.delivtimes.all"); - return deliveryTimes; - } + var query = _deliveryTimeRepository.Table.OrderBy(c => c.DisplayOrder); + var deliveryTimes = query.ToListCached("db.delivtimes.all"); + return deliveryTimes; + } public virtual void InsertDeliveryTime(DeliveryTime deliveryTime) { - if (deliveryTime == null) - throw new ArgumentNullException("deliveryTime"); + Guard.NotNull(deliveryTime, nameof(deliveryTime)); _deliveryTimeRepository.Insert(deliveryTime); } public virtual void UpdateDeliveryTime(DeliveryTime deliveryTime) { - if (deliveryTime == null) - throw new ArgumentNullException("deliveryTime"); + Guard.NotNull(deliveryTime, nameof(deliveryTime)); _deliveryTimeRepository.Update(deliveryTime); } public virtual void SetToDefault(DeliveryTime deliveryTime) { - if (deliveryTime == null) - throw new ArgumentNullException("deliveryTime"); + Guard.NotNull(deliveryTime, nameof(deliveryTime)); var deliveryTimes = GetAllDeliveryTimes(); - foreach(var time in deliveryTimes) + foreach (var time in deliveryTimes) { time.IsDefault = time.Equals(deliveryTime) ? true : false; _deliveryTimeRepository.Update(time); } } - + public virtual DeliveryTime GetDefaultDeliveryTime() { return _deliveryTimeRepository.Table.Where(x => x.IsDefault == true).FirstOrDefault(); } + + public virtual (DateTime? minDate, DateTime? maxDate) GetDeliveryDate(DeliveryTime deliveryTime) + { + var currentDate = TimeZoneInfo.ConvertTime(DateTime.UtcNow, _dateTimeHelper.DefaultStoreTimeZone); + return GetDeliveryDate(deliveryTime, currentDate); + } + + public virtual (DateTime? minDate, DateTime? maxDate) GetDeliveryDate(DeliveryTime deliveryTime, DateTime fromDate) + { + var minDate = deliveryTime?.MinDays != null + ? AddDays(fromDate, deliveryTime.MinDays.Value) + : (DateTime?)null; + + var maxDate = deliveryTime?.MaxDays != null + ? AddDays(fromDate, deliveryTime.MaxDays.Value) + : (DateTime?)null; + + return (minDate, maxDate); + } + + public virtual string GetFormattedDeliveryDate(DeliveryTime deliveryTime, DateTime? fromDate = null, CultureInfo culture = null) + { + if (deliveryTime == null || (!deliveryTime.MinDays.HasValue && !deliveryTime.MaxDays.HasValue)) + { + return null; + } + + if (culture == null) + { + culture = Thread.CurrentThread.CurrentUICulture; + } + + var currentDate = fromDate ?? TimeZoneInfo.ConvertTime(DateTime.UtcNow, _dateTimeHelper.DefaultStoreTimeZone); + var (min, max) = GetDeliveryDate(deliveryTime, currentDate); + + if (min.HasValue) + { + min = _dateTimeHelper.ConvertToUserTime(min.Value); + } + if (max.HasValue) + { + max = _dateTimeHelper.ConvertToUserTime(max.Value); + } + + // Convention: always separate weekday with comma and format month in shortest form. + if (min.HasValue && max.HasValue) + { + if (min == max) + { + return T("DeliveryTimes.Dates.DeliveryOn", Format(min.Value, "dddd, ", false, "DeliveryTimes.Dates.OnTomorrow")); + } + else if (min < max) + { + return T("DeliveryTimes.Dates.Between", + Format(min.Value, "ddd, ", min.Value.Month == max.Value.Month && min.Value.Year == max.Value.Year, null), + Format(max.Value, "ddd, ", false, null)); + } + } + else if (min.HasValue) + { + return T("DeliveryTimes.Dates.NotBefore", Format(min.Value, "dddd, ")); + } + else if (max.HasValue) + { + return T("DeliveryTimes.Dates.Until", Format(max.Value, "dddd, ")); + } + + return null; + + string Format(DateTime date, string patternPrefix, bool noMonth = false, string tomorrowKey = "DeliveryTimes.Dates.Tomorrow") + { + // Offer some way to skip our formatting and to force a custom formatting. + if (!string.IsNullOrEmpty(_shippingSettings.DeliveryTimesDateFormat)) + { + return date.ToString(_shippingSettings.DeliveryTimesDateFormat, culture) ?? "-"; + } + + if (tomorrowKey != null && (date - currentDate).TotalDays == 1) + { + return T(tomorrowKey); + } + + string patternSuffix = null; + + if (noMonth) + { + // MonthDayPattern can contain non-interpreted text like "de", "mh" or even "'d'" (e.g. 21 de septiembre). + patternSuffix = _monthDayFormats.GetOrAdd(culture.TwoLetterISOLanguageName + "-nomonth", _ => + { + var mdp = culture.DateTimeFormat.MonthDayPattern; + return mdp.Contains("d. ") || mdp.Contains("dd. ") ? "d." : "d"; + }); + } + else + { + patternSuffix = _monthDayFormats.GetOrAdd(culture.TwoLetterISOLanguageName, _ => + { + return culture.DateTimeFormat.MonthDayPattern.Replace("MMMM", "MMM").TrimSafe(); + }); + } + + return date.ToString(patternPrefix + patternSuffix, culture) ?? "-"; + } + } + + #region Utilities + + /// + /// + protected virtual DateTime AddDays(DateTime date, int days) + { + Guard.NotNegative(days, nameof(days)); + + // now.Hour: 0-23. TodayDeliveryHour: 1-24. + if (_shippingSettings.TodayShipmentHour.HasValue && date.Hour < _shippingSettings.TodayShipmentHour) + { + if ((date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday) || !_shippingSettings.DeliveryOnWorkweekDaysOnly) + { + days -= 1; + } + } + + // Normalization. Do not support today delivery. + if (days < 1) + { + days = 1; + } + + if (!_shippingSettings.DeliveryOnWorkweekDaysOnly) + { + return date.AddDays(days); + } + + // Add days for non workweek days. + if (date.DayOfWeek == DayOfWeek.Saturday) + { + date = date.AddDays(2); + days -= 1; + } + else if (date.DayOfWeek == DayOfWeek.Sunday) + { + date = date.AddDays(1); + days -= 1; + } + + date = date.AddDays(days / 5 * 7); + int extraDays = days % 5; + + if ((int)date.DayOfWeek + extraDays > 5) + { + extraDays += 2; + } + + return date.AddDays(extraDays); + } + + #endregion } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Directory/EcbExchangeRateProvider.cs b/src/Libraries/SmartStore.Services/Directory/EcbExchangeRateProvider.cs index 2e941fadff..6fab09e930 100644 --- a/src/Libraries/SmartStore.Services/Directory/EcbExchangeRateProvider.cs +++ b/src/Libraries/SmartStore.Services/Directory/EcbExchangeRateProvider.cs @@ -9,27 +9,27 @@ namespace SmartStore.Services.Directory { - [SystemName("CurrencyExchange.ECB")] - [FriendlyName("ECB currency exchange rate provider")] - [DisplayOrder(0)] - public class EcbExchangeRateProvider : IExchangeRateProvider + [SystemName("CurrencyExchange.ECB")] + [FriendlyName("ECB currency exchange rate provider")] + [DisplayOrder(0)] + public class EcbExchangeRateProvider : IExchangeRateProvider { - /// - /// Gets live currency exchange rates - /// - /// Exchange rate currency code - /// Currency exchange rates - public IList GetCurrencyLiveRates(string exchangeRateCurrencyCode) + /// + /// Gets live currency exchange rates + /// + /// Exchange rate currency code + /// Currency exchange rates + public IList GetCurrencyLiveRates(string exchangeRateCurrencyCode) { - if (exchangeRateCurrencyCode.IsEmpty()) - return new List(); + if (exchangeRateCurrencyCode.IsEmpty()) + return new List(); - var result = new Dictionary(); - var targetCurrency = exchangeRateCurrencyCode.ToUpper(); - var updateDate = DateTime.UtcNow; + var result = new Dictionary(); + var targetCurrency = exchangeRateCurrencyCode.ToUpper(); + var updateDate = DateTime.UtcNow; - // old url still works too: http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml - var request = WebRequest.Create("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml") as HttpWebRequest; + // old url still works too: http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml + var request = WebRequest.Create("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml") as HttpWebRequest; using (var response = request.GetResponse()) { @@ -41,72 +41,72 @@ public IList GetCurrencyLiveRates(string exchangeRateCurrencyCode) nsmgr.AddNamespace("gesmes", "http://www.gesmes.org/xml/2002-08-01"); var node = document.SelectSingleNode("gesmes:Envelope/ns:Cube/ns:Cube", nsmgr); - var ecbDate = DateTime.ParseExact(node.Attributes["time"].Value, "yyyy-MM-dd", null); + var ecbDate = DateTime.ParseExact(node.Attributes["time"].Value, "yyyy-MM-dd", null); - // see https://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html - updateDate = new DateTime(ecbDate.Year, ecbDate.Month, ecbDate.Day, 16, 0, 0); + // see https://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html + updateDate = new DateTime(ecbDate.Year, ecbDate.Month, ecbDate.Day, 16, 0, 0); - var provider = new NumberFormatInfo(); + var provider = new NumberFormatInfo(); provider.NumberDecimalSeparator = "."; provider.NumberGroupSeparator = ""; foreach (XmlNode childNode in node.ChildNodes) { - // parse ECB rate - var rate = new ExchangeRate - { - CurrencyCode = childNode.Attributes["currency"].Value.EmptyNull().ToUpper(), - Rate = decimal.Parse(childNode.Attributes["rate"].Value, provider), - UpdatedOn = updateDate - }; - - // add it to result dictionary - if (!result.ContainsKey(rate.CurrencyCode) && rate.Rate != decimal.Zero) - { - result.Add(rate.CurrencyCode, rate); - } + // parse ECB rate + var rate = new ExchangeRate + { + CurrencyCode = childNode.Attributes["currency"].Value.EmptyNull().ToUpper(), + Rate = decimal.Parse(childNode.Attributes["rate"].Value, provider), + UpdatedOn = updateDate + }; + + // add it to result dictionary + if (!result.ContainsKey(rate.CurrencyCode) && rate.Rate != decimal.Zero) + { + result.Add(rate.CurrencyCode, rate); + } } } - // if target currency is EUR then we are ready because ECB always returns EUR rates - if (targetCurrency != "EUR") - { - // cross calculation not possible if ECB list does not contain target currency - if (!result.ContainsKey(targetCurrency)) - return new List(); - - // get clone of EUR rates - var euroRates = result.ToDictionary(x => x.Key, x => x.Value.Rate); - - // cross calculate rates for target currency - result.Each(x => - { - if (x.Value.CurrencyCode == "EUR") - { - x.Value.Rate = 1.0M / euroRates[targetCurrency]; - } - else - { - x.Value.Rate = euroRates[x.Value.CurrencyCode] / euroRates[targetCurrency]; - } - }); - - // remove target currency form result because its rate is always 1.0 - result.Remove(targetCurrency); - - // add EUR rate - if (!result.ContainsKey("EUR")) - { - result.Add("EUR", new ExchangeRate - { - CurrencyCode = "EUR", - Rate = 1.0M / euroRates[targetCurrency], - UpdatedOn = updateDate - }); - } - } - - return result.Select(x => x.Value).ToList(); + // if target currency is EUR then we are ready because ECB always returns EUR rates + if (targetCurrency != "EUR") + { + // cross calculation not possible if ECB list does not contain target currency + if (!result.ContainsKey(targetCurrency)) + return new List(); + + // get clone of EUR rates + var euroRates = result.ToDictionary(x => x.Key, x => x.Value.Rate); + + // cross calculate rates for target currency + result.Each(x => + { + if (x.Value.CurrencyCode == "EUR") + { + x.Value.Rate = 1.0M / euroRates[targetCurrency]; + } + else + { + x.Value.Rate = euroRates[x.Value.CurrencyCode] / euroRates[targetCurrency]; + } + }); + + // remove target currency form result because its rate is always 1.0 + result.Remove(targetCurrency); + + // add EUR rate + if (!result.ContainsKey("EUR")) + { + result.Add("EUR", new ExchangeRate + { + CurrencyCode = "EUR", + Rate = 1.0M / euroRates[targetCurrency], + UpdatedOn = updateDate + }); + } + } + + return result.Select(x => x.Value).ToList(); } } } diff --git a/src/Libraries/SmartStore.Services/Directory/GeoCountryLookup.cs b/src/Libraries/SmartStore.Services/Directory/GeoCountryLookup.cs index 71ec189d47..c2dfaeac4e 100644 --- a/src/Libraries/SmartStore.Services/Directory/GeoCountryLookup.cs +++ b/src/Libraries/SmartStore.Services/Directory/GeoCountryLookup.cs @@ -1,5 +1,6 @@ using System; using System.Net; +using MaxMind.Db; using MaxMind.GeoIP2; using SmartStore.Core; using SmartStore.Core.Caching; @@ -11,11 +12,13 @@ namespace SmartStore.Services.Directory public partial class GeoCountryLookup : DisposableObject, IGeoCountryLookup { private readonly DatabaseReader _reader; - private readonly object _lock = new object(); public GeoCountryLookup() { - _reader = new DatabaseReader(CommonHelper.MapPath("~/App_Data/GeoLite2/GeoLite2-Country.mmdb")); + // Old database not usable anymore due to US (Florida?) legal issues from 01 Jan 2020 on. + //_reader = new DatabaseReader(CommonHelper.MapPath("~/App_Data/GeoLite2/GeoLite2-Country.mmdb")); + + _reader = new DatabaseReader(CommonHelper.MapPath("~/App_Data/Geo/dbip-country-lite.mmdb")); } public LookupCountryResponse LookupCountry(string addr) diff --git a/src/Libraries/SmartStore.Services/Directory/ICountryService.cs b/src/Libraries/SmartStore.Services/Directory/ICountryService.cs index 001d2d55fd..fb62e956d6 100644 --- a/src/Libraries/SmartStore.Services/Directory/ICountryService.cs +++ b/src/Libraries/SmartStore.Services/Directory/ICountryService.cs @@ -43,12 +43,12 @@ public partial interface ICountryService /// Country Country GetCountryById(int countryId); - /// - /// Gets a country by two or three letter ISO code - /// - /// Country two or three letter ISO code - /// Country - Country GetCountryByTwoOrThreeLetterIsoCode(string letterIsoCode); + /// + /// Gets a country by two or three letter ISO code + /// + /// Country two or three letter ISO code + /// Country + Country GetCountryByTwoOrThreeLetterIsoCode(string letterIsoCode); /// /// Gets a country by two letter ISO code @@ -75,5 +75,5 @@ public partial interface ICountryService /// /// Country void UpdateCountry(Country country); - } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Directory/ICurrencyService.cs b/src/Libraries/SmartStore.Services/Directory/ICurrencyService.cs index 14be063172..68c2bbbf3e 100644 --- a/src/Libraries/SmartStore.Services/Directory/ICurrencyService.cs +++ b/src/Libraries/SmartStore.Services/Directory/ICurrencyService.cs @@ -112,14 +112,14 @@ public partial interface ICurrencyService /// Store to get the primary store currency from /// Converted value decimal ConvertFromPrimaryStoreCurrency(decimal amount, Currency targetCurrency, Store store = null); - - + + /// /// Load active exchange rate provider /// /// Active exchange rate provider - Provider LoadActiveExchangeRateProvider(); + Provider LoadActiveExchangeRateProvider(); /// /// Load exchange rate provider by system name diff --git a/src/Libraries/SmartStore.Services/Directory/IDeliveryTimeService.cs b/src/Libraries/SmartStore.Services/Directory/IDeliveryTimeService.cs index 36663e2e6c..1a5e4ef5c3 100644 --- a/src/Libraries/SmartStore.Services/Directory/IDeliveryTimeService.cs +++ b/src/Libraries/SmartStore.Services/Directory/IDeliveryTimeService.cs @@ -1,6 +1,9 @@ +using System; using System.Collections.Generic; +using System.Globalization; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Directory; +using SmartStore.Services.Helpers; namespace SmartStore.Services.Directory { @@ -14,7 +17,7 @@ public partial interface IDeliveryTimeService /// at least one dependant entity /// bool IsAssociated(int deliveryTimeId); - + /// /// Deletes delivery time /// @@ -28,12 +31,12 @@ public partial interface IDeliveryTimeService /// DeliveryTime DeliveryTime GetDeliveryTimeById(int deliveryTimeId); - /// - /// Gets the delivery time for a product - /// - /// The product - /// Delivery time - DeliveryTime GetDeliveryTime(Product product); + /// + /// Gets the delivery time for a product + /// + /// The product + /// Delivery time + DeliveryTime GetDeliveryTime(Product product); /// /// Gets all delivery times @@ -65,5 +68,30 @@ public partial interface IDeliveryTimeService /// The product /// Delivery time DeliveryTime GetDefaultDeliveryTime(); + + /// + /// Calculates the delivery date based on current shop date. + /// + /// Delivery time. + /// Calculated minimum and maximum date. + (DateTime? minDate, DateTime? maxDate) GetDeliveryDate(DeliveryTime deliveryTime); + + /// + /// Calculates the delivery date. + /// + /// Delivery time. + /// The date from which the delivery date should be calculated. + /// Calculated minimum and maximum date. + (DateTime? minDate, DateTime? maxDate) GetDeliveryDate(DeliveryTime deliveryTime, DateTime fromDate); + + /// + /// Gets the formatted delivery date. + /// + /// Delivery time. + /// The date from which the delivery date should be calculated. + /// null to use store's local time . + /// Culture to use for formatting. null to use UI culture of current thread. + /// Formatted delivery date. + string GetFormattedDeliveryDate(DeliveryTime deliveryTime, DateTime? fromDate = null, CultureInfo culture = null); } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Directory/IMeasureService.cs b/src/Libraries/SmartStore.Services/Directory/IMeasureService.cs index 5c57d3e851..1a56905e7b 100644 --- a/src/Libraries/SmartStore.Services/Directory/IMeasureService.cs +++ b/src/Libraries/SmartStore.Services/Directory/IMeasureService.cs @@ -74,7 +74,7 @@ decimal ConvertToPrimaryMeasureDimension(decimal quantity, /// Converted value decimal ConvertFromPrimaryMeasureDimension(decimal quantity, MeasureDimension targetMeasureDimension); - + /// /// Deletes measure weight diff --git a/src/Libraries/SmartStore.Services/Directory/IQuantityUnitService.cs b/src/Libraries/SmartStore.Services/Directory/IQuantityUnitService.cs index b198394f2e..49c41a7119 100644 --- a/src/Libraries/SmartStore.Services/Directory/IQuantityUnitService.cs +++ b/src/Libraries/SmartStore.Services/Directory/IQuantityUnitService.cs @@ -14,7 +14,7 @@ public partial interface IQuantityUnitService /// at least one dependant entity /// bool IsAssociated(int quantityUnitId); - + /// /// Deletes measure unit /// @@ -28,10 +28,10 @@ public partial interface IQuantityUnitService /// QuantityUnit QuantityUnit GetQuantityUnitById(int? quantityUnitId); - /// + /// /// Gets the measure unit for a product - /// - /// The product + /// + /// The product /// measure unit QuantityUnit GetQuantityUnit(Product product); diff --git a/src/Libraries/SmartStore.Services/Directory/IStateProvinceService.cs b/src/Libraries/SmartStore.Services/Directory/IStateProvinceService.cs index e62ff53527..3c62eaa12c 100644 --- a/src/Libraries/SmartStore.Services/Directory/IStateProvinceService.cs +++ b/src/Libraries/SmartStore.Services/Directory/IStateProvinceService.cs @@ -15,19 +15,19 @@ public partial interface IStateProvinceService /// The state/province void DeleteStateProvince(StateProvince stateProvince); - /// - /// Get all states/provinces - /// - /// A value indicating whether to show hidden records - /// - IQueryable GetAllStateProvinces(bool showHidden = false); + /// + /// Get all states/provinces + /// + /// A value indicating whether to show hidden records + /// + IQueryable GetAllStateProvinces(bool showHidden = false); - /// - /// Gets a state/province - /// - /// The state/province identifier - /// State/province - StateProvince GetStateProvinceById(int stateProvinceId); + /// + /// Gets a state/province + /// + /// The state/province identifier + /// State/province + StateProvince GetStateProvinceById(int stateProvinceId); /// /// Gets a state/province @@ -35,7 +35,7 @@ public partial interface IStateProvinceService /// The state/province abbreviation /// State/province StateProvince GetStateProvinceByAbbreviation(string abbreviation); - + /// /// Gets a state/province collection by country identifier /// diff --git a/src/Libraries/SmartStore.Services/Directory/MeasureService.cs b/src/Libraries/SmartStore.Services/Directory/MeasureService.cs index 1ff51e67a9..3d81a13d16 100644 --- a/src/Libraries/SmartStore.Services/Directory/MeasureService.cs +++ b/src/Libraries/SmartStore.Services/Directory/MeasureService.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using SmartStore.Core; -using SmartStore.Core.Caching; using SmartStore.Core.Data; using SmartStore.Core.Domain.Directory; using SmartStore.Core.Events; @@ -15,18 +13,15 @@ public partial class MeasureService : IMeasureService private readonly IRepository _measureDimensionRepository; private readonly IRepository _measureWeightRepository; private readonly MeasureSettings _measureSettings; - private readonly IEventPublisher _eventPublisher; public MeasureService( IRepository measureDimensionRepository, IRepository measureWeightRepository, - MeasureSettings measureSettings, - IEventPublisher eventPublisher) + MeasureSettings measureSettings) { _measureDimensionRepository = measureDimensionRepository; _measureWeightRepository = measureWeightRepository; _measureSettings = measureSettings; - _eventPublisher = eventPublisher; } #region Dimensions @@ -38,14 +33,14 @@ public virtual void DeleteMeasureDimension(MeasureDimension measureDimension) _measureDimensionRepository.Delete(measureDimension); } - + public virtual MeasureDimension GetMeasureDimensionById(int measureDimensionId) { if (measureDimensionId == 0) return null; - return _measureDimensionRepository.GetByIdCached(measureDimensionId, "db.dimension.id-" + measureDimensionId); - } + return _measureDimensionRepository.GetByIdCached(measureDimensionId, "db.dimension.id-" + measureDimensionId); + } public virtual MeasureDimension GetMeasureDimensionBySystemKeyword(string systemKeyword) { @@ -61,12 +56,12 @@ public virtual MeasureDimension GetMeasureDimensionBySystemKeyword(string system public virtual IList GetAllMeasureDimensions() { - var query = from md in _measureDimensionRepository.Table - orderby md.DisplayOrder - select md; - var measureDimensions = query.ToListCached("db.dimension.all"); - return measureDimensions; - } + var query = from md in _measureDimensionRepository.Table + orderby md.DisplayOrder + select md; + var measureDimensions = query.ToListCached("db.dimension.all"); + return measureDimensions; + } public virtual void InsertMeasureDimension(MeasureDimension measure) { @@ -84,8 +79,7 @@ public virtual void UpdateMeasureDimension(MeasureDimension measure) _measureDimensionRepository.Update(measure); } - public virtual decimal ConvertDimension(decimal quantity, - MeasureDimension sourceMeasureDimension, MeasureDimension targetMeasureDimension, bool round = true) + public virtual decimal ConvertDimension(decimal quantity, MeasureDimension sourceMeasureDimension, MeasureDimension targetMeasureDimension, bool round = true) { decimal result = quantity; if (result != decimal.Zero && sourceMeasureDimension.Id != targetMeasureDimension.Id) @@ -98,8 +92,7 @@ public virtual decimal ConvertDimension(decimal quantity, return result; } - public virtual decimal ConvertToPrimaryMeasureDimension(decimal quantity, - MeasureDimension sourceMeasureDimension) + public virtual decimal ConvertToPrimaryMeasureDimension(decimal quantity, MeasureDimension sourceMeasureDimension) { decimal result = quantity; var baseDimensionIn = GetMeasureDimensionById(_measureSettings.BaseDimensionId); @@ -113,8 +106,7 @@ public virtual decimal ConvertToPrimaryMeasureDimension(decimal quantity, return result; } - public virtual decimal ConvertFromPrimaryMeasureDimension(decimal quantity, - MeasureDimension targetMeasureDimension) + public virtual decimal ConvertFromPrimaryMeasureDimension(decimal quantity, MeasureDimension targetMeasureDimension) { decimal result = quantity; var baseDimensionIn = GetMeasureDimensionById(_measureSettings.BaseDimensionId); @@ -145,8 +137,8 @@ public virtual MeasureWeight GetMeasureWeightById(int measureWeightId) if (measureWeightId == 0) return null; - return _measureWeightRepository.GetByIdCached(measureWeightId, "db.weight.id-" + measureWeightId); - } + return _measureWeightRepository.GetByIdCached(measureWeightId, "db.weight.id-" + measureWeightId); + } public virtual MeasureWeight GetMeasureWeightBySystemKeyword(string systemKeyword) { @@ -162,13 +154,13 @@ public virtual MeasureWeight GetMeasureWeightBySystemKeyword(string systemKeywor public virtual IList GetAllMeasureWeights() { - var query = from mw in _measureWeightRepository.Table - orderby mw.DisplayOrder - select mw; + var query = from mw in _measureWeightRepository.Table + orderby mw.DisplayOrder + select mw; - var measureWeights = query.ToListCached("db.weight.all"); - return measureWeights; - } + var measureWeights = query.ToListCached("db.weight.all"); + return measureWeights; + } public virtual void InsertMeasureWeight(MeasureWeight measure) { diff --git a/src/Libraries/SmartStore.Services/Directory/QuantityUnitService.cs b/src/Libraries/SmartStore.Services/Directory/QuantityUnitService.cs index 1048718152..a3bb181539 100644 --- a/src/Libraries/SmartStore.Services/Directory/QuantityUnitService.cs +++ b/src/Libraries/SmartStore.Services/Directory/QuantityUnitService.cs @@ -15,20 +15,20 @@ public partial class QuantityUnitService : IQuantityUnitService private readonly IRepository _quantityUnitRepository; private readonly IRepository _productRepository; private readonly IEventPublisher _eventPublisher; - private readonly CatalogSettings _catalogSettings; - + private readonly CatalogSettings _catalogSettings; + public QuantityUnitService( IRepository quantityUnitRepository, IRepository productRepository, IRepository attributeCombinationRepository, ICustomerService customerService, IEventPublisher eventPublisher, - CatalogSettings catalogSettings) + CatalogSettings catalogSettings) { this._quantityUnitRepository = quantityUnitRepository; this._eventPublisher = eventPublisher; this._productRepository = productRepository; - this._catalogSettings = catalogSettings; + this._catalogSettings = catalogSettings; } public virtual void DeleteQuantityUnit(QuantityUnit quantityUnit) @@ -47,19 +47,19 @@ public virtual bool IsAssociated(int quantityUnitId) if (quantityUnitId == 0) return false; - var query = - from p in _productRepository.Table + var query = + from p in _productRepository.Table where p.QuantityUnitId == quantityUnitId || p.ProductVariantAttributeCombinations.Any(c => c.QuantityUnitId == quantityUnitId) - select p.Id; + select p.Id; return query.Count() > 0; } public virtual QuantityUnit GetQuantityUnitById(int? quantityUnitId) { - if (quantityUnitId == null || quantityUnitId == 0) + if (quantityUnitId == null || quantityUnitId == 0) { - if(_catalogSettings.ShowDefaultQuantityUnit) + if (_catalogSettings.ShowDefaultQuantityUnit) { return GetAllQuantityUnits().Where(x => x.IsDefault == true).FirstOrDefault(); } @@ -68,25 +68,25 @@ public virtual QuantityUnit GetQuantityUnitById(int? quantityUnitId) return null; } } - + return _quantityUnitRepository.GetById(quantityUnitId); } public virtual QuantityUnit GetQuantityUnit(Product product) - { - if (product == null) - return null; + { + if (product == null) + return null; return GetQuantityUnitById(product.QuantityUnitId ?? 0); - } + } public virtual IList GetAllQuantityUnits() { - var query = _quantityUnitRepository.Table.OrderBy(c => c.DisplayOrder); + var query = _quantityUnitRepository.Table.OrderBy(c => c.DisplayOrder); - var quantityUnits = query.ToListCached("db.qtyunit.all"); - return quantityUnits; - } + var quantityUnits = query.ToListCached("db.qtyunit.all"); + return quantityUnits; + } public virtual void InsertQuantityUnit(QuantityUnit quantityUnit) { @@ -101,7 +101,8 @@ public virtual void UpdateQuantityUnit(QuantityUnit quantityUnit) if (quantityUnit == null) throw new ArgumentNullException("quantityUnit"); - if (quantityUnit.IsDefault == true) { + if (quantityUnit.IsDefault == true) + { var temp = new List(); temp.Add(quantityUnit); @@ -109,8 +110,8 @@ public virtual void UpdateQuantityUnit(QuantityUnit quantityUnit) var query = GetAllQuantityUnits() .Where(x => x.IsDefault == true) .Except(temp); - - foreach(var qu in query) + + foreach (var qu in query) { qu.IsDefault = false; _quantityUnitRepository.Update(qu); diff --git a/src/Libraries/SmartStore.Services/Directory/StateProvinceService.cs b/src/Libraries/SmartStore.Services/Directory/StateProvinceService.cs index db65144d52..14e99909ed 100644 --- a/src/Libraries/SmartStore.Services/Directory/StateProvinceService.cs +++ b/src/Libraries/SmartStore.Services/Directory/StateProvinceService.cs @@ -8,7 +8,7 @@ namespace SmartStore.Services.Directory { - public partial class StateProvinceService : IStateProvinceService + public partial class StateProvinceService : IStateProvinceService { private readonly IRepository _stateProvinceRepository; private readonly IEventPublisher _eventPublisher; @@ -25,21 +25,21 @@ public virtual void DeleteStateProvince(StateProvince stateProvince) { if (stateProvince == null) throw new ArgumentNullException("stateProvince"); - + _stateProvinceRepository.Delete(stateProvince); } - public virtual IQueryable GetAllStateProvinces(bool showHidden = false) - { - var query = _stateProvinceRepository.Table; + public virtual IQueryable GetAllStateProvinces(bool showHidden = false) + { + var query = _stateProvinceRepository.Table; - if (!showHidden) - query = query.Where(x => x.Published); + if (!showHidden) + query = query.Where(x => x.Published); - return query; - } + return query; + } - public virtual StateProvince GetStateProvinceById(int stateProvinceId) + public virtual StateProvince GetStateProvinceById(int stateProvinceId) { if (stateProvinceId == 0) return null; @@ -55,18 +55,23 @@ public virtual StateProvince GetStateProvinceByAbbreviation(string abbreviation) var stateProvince = query.FirstOrDefault(); return stateProvince; } - + public virtual IList GetStateProvincesByCountryId(int countryId, bool showHidden = false) { - var query = from sp in _stateProvinceRepository.Table - orderby sp.DisplayOrder - where sp.CountryId == countryId && - (showHidden || sp.Published) - select sp; - - var stateProvinces = query.ToListCached("db.regions.{0}.{1}".FormatInvariant(countryId, showHidden)); - return stateProvinces; - } + if (countryId == 0) + { + return new List(); + } + + var query = from sp in _stateProvinceRepository.Table + orderby sp.DisplayOrder + where sp.CountryId == countryId && + (showHidden || sp.Published) + select sp; + + var stateProvinces = query.ToListCached("db.regions.{0}.{1}".FormatInvariant(countryId, showHidden)); + return stateProvinces; + } public virtual void InsertStateProvince(StateProvince stateProvince) { diff --git a/src/Libraries/SmartStore.Services/Directory/UpdateExchangeRateTask.cs b/src/Libraries/SmartStore.Services/Directory/UpdateExchangeRateTask.cs index b96dce8613..6fc51c7256 100644 --- a/src/Libraries/SmartStore.Services/Directory/UpdateExchangeRateTask.cs +++ b/src/Libraries/SmartStore.Services/Directory/UpdateExchangeRateTask.cs @@ -11,16 +11,16 @@ public partial class UpdateExchangeRateTask : ITask { private readonly ICurrencyService _currencyService; private readonly CurrencySettings _currencySettings; - private readonly ICommonServices _services; + private readonly ICommonServices _services; public UpdateExchangeRateTask( - ICurrencyService currencyService, - CurrencySettings currencySettings, - ICommonServices services) + ICurrencyService currencyService, + CurrencySettings currencySettings, + ICommonServices services) { this._currencyService = currencyService; this._currencySettings = currencySettings; - this._services = services; + this._services = services; } /// @@ -45,17 +45,17 @@ public void Execute(TaskExecutionContext ctx) var currency = _currencyService.GetCurrencyByCode(exchageRate.CurrencyCode); if (currency != null) { - if (currency.Rate != exchageRate.Rate) - { - currency.Rate = exchageRate.Rate; - _currencyService.UpdateCurrency(currency); - } + if (currency.Rate != exchageRate.Rate) + { + currency.Rate = exchageRate.Rate; + _currencyService.UpdateCurrency(currency); + } } } // save new update time value _currencySettings.LastUpdateTime = DateTime.UtcNow.ToBinary(); - _services.Settings.SaveSetting(_currencySettings); + _services.Settings.SaveSetting(_currencySettings); } } } diff --git a/src/Libraries/SmartStore.Services/Discounts/CheckDiscountRequirementRequest.cs b/src/Libraries/SmartStore.Services/Discounts/CheckDiscountRequirementRequest.cs deleted file mode 100644 index 2988d3df77..0000000000 --- a/src/Libraries/SmartStore.Services/Discounts/CheckDiscountRequirementRequest.cs +++ /dev/null @@ -1,27 +0,0 @@ -using SmartStore.Core.Domain.Customers; -using SmartStore.Core.Domain.Discounts; -using SmartStore.Core.Domain.Stores; - -namespace SmartStore.Services.Discounts -{ - /// - /// Represents a discount requirement request - /// - public partial class CheckDiscountRequirementRequest - { - /// - /// Gets or sets the discount - /// - public DiscountRequirement DiscountRequirement { get; set; } - - /// - /// Gets or sets the customer - /// - public Customer Customer { get; set; } - - /// - /// Gets or sets the store - /// - public Store Store { get; set; } - } -} diff --git a/src/Libraries/SmartStore.Services/Discounts/DiscountExtentions.cs b/src/Libraries/SmartStore.Services/Discounts/DiscountExtentions.cs index df737a6d23..e6c7a85c9d 100644 --- a/src/Libraries/SmartStore.Services/Discounts/DiscountExtentions.cs +++ b/src/Libraries/SmartStore.Services/Discounts/DiscountExtentions.cs @@ -3,7 +3,7 @@ namespace SmartStore.Services.Discounts { - public static class DiscountExtentions + public static class DiscountExtentions { /// /// Gets the discount amount for the specified value @@ -13,29 +13,29 @@ public static class DiscountExtentions /// The discount amount public static decimal GetDiscountAmount(this Discount discount, decimal amount) { - Guard.NotNull(discount, nameof(discount)); + Guard.NotNull(discount, nameof(discount)); var result = decimal.Zero; - if (discount.UsePercentage) - { - result = (decimal)((((float)amount) * ((float)discount.DiscountPercentage)) / 100f); - } - else - { - result = discount.DiscountAmount; - } + if (discount.UsePercentage) + { + result = (decimal)((((float)amount) * ((float)discount.DiscountPercentage)) / 100f); + } + else + { + result = discount.DiscountAmount; + } return result; } - /// - /// Get the discount that achieves the highest discount amount other than zero. - /// - /// List of discounts - /// Amount without discount (for percentage discounts) - /// Discount that achieves the highest discount amount other than zero. - public static Discount GetPreferredDiscount(this ICollection discounts, decimal amount) + /// + /// Get the discount that achieves the highest discount amount other than zero. + /// + /// List of discounts + /// Amount without discount (for percentage discounts) + /// Discount that achieves the highest discount amount other than zero. + public static Discount GetPreferredDiscount(this ICollection discounts, decimal amount) { Discount preferredDiscount = null; decimal? maximumDiscountValue = null; @@ -43,14 +43,14 @@ public static Discount GetPreferredDiscount(this ICollection discounts foreach (var discount in discounts) { var currentDiscountValue = discount.GetDiscountAmount(amount); - if (currentDiscountValue != decimal.Zero) - { - if (!maximumDiscountValue.HasValue || currentDiscountValue > maximumDiscountValue) - { - maximumDiscountValue = currentDiscountValue; - preferredDiscount = discount; - } - } + if (currentDiscountValue != decimal.Zero) + { + if (!maximumDiscountValue.HasValue || currentDiscountValue > maximumDiscountValue) + { + maximumDiscountValue = currentDiscountValue; + preferredDiscount = discount; + } + } } return preferredDiscount; diff --git a/src/Libraries/SmartStore.Services/Discounts/DiscountService.cs b/src/Libraries/SmartStore.Services/Discounts/DiscountService.cs index 660e5f23a1..5ab5f8a1f9 100644 --- a/src/Libraries/SmartStore.Services/Discounts/DiscountService.cs +++ b/src/Libraries/SmartStore.Services/Discounts/DiscountService.cs @@ -8,7 +8,7 @@ using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Discounts; using SmartStore.Core.Domain.Orders; -using SmartStore.Core.Plugins; +using SmartStore.Services.Cart.Rules; using SmartStore.Services.Common; using SmartStore.Services.Customers; @@ -20,42 +20,39 @@ public partial class DiscountService : IDiscountService private const string DISCOUNTS_PATTERN_KEY = "SmartStore.discount.*"; private readonly IRepository _discountRepository; - private readonly IRepository _discountRequirementRepository; private readonly IRepository _discountUsageHistoryRepository; private readonly IRequestCache _requestCache; - private readonly IStoreContext _storeContext; - private readonly IGenericAttributeService _genericAttributeService; - private readonly IProviderManager _providerManager; - private readonly IDictionary _discountValidityCache; + private readonly IStoreContext _storeContext; + private readonly IGenericAttributeService _genericAttributeService; + private readonly ICartRuleProvider _cartRuleProvider; + private readonly IDictionary _discountValidityCache; public DiscountService( IRequestCache requestCache, IRepository discountRepository, - IRepository discountRequirementRepository, IRepository discountUsageHistoryRepository, - IStoreContext storeContext, - IGenericAttributeService genericAttributeService, - IProviderManager providerManager) + IStoreContext storeContext, + IGenericAttributeService genericAttributeService, + ICartRuleProvider cartRuleProvider) { _requestCache = requestCache; _discountRepository = discountRepository; - _discountRequirementRepository = discountRequirementRepository; _discountUsageHistoryRepository = discountUsageHistoryRepository; - _storeContext = storeContext; - _genericAttributeService = genericAttributeService; - _providerManager = providerManager; - _discountValidityCache = new Dictionary(); - } - - /// - /// Checks discount limitation for customer - /// - /// Discount - /// Customer - /// Value indicating whether discount can be used - protected virtual bool CheckDiscountLimitations(Discount discount, Customer customer) + _storeContext = storeContext; + _genericAttributeService = genericAttributeService; + _cartRuleProvider = cartRuleProvider; + _discountValidityCache = new Dictionary(); + } + + /// + /// Checks discount limitation for customer + /// + /// Discount + /// Customer + /// Value indicating whether discount can be used + protected virtual bool CheckDiscountLimitations(Discount discount, Customer customer) { - Guard.NotNull(discount, nameof(discount)); + Guard.NotNull(discount, nameof(discount)); switch (discount.DiscountLimitation) { @@ -90,9 +87,9 @@ protected virtual bool CheckDiscountLimitations(Discount discount, Customer cust public virtual void DeleteDiscount(Discount discount) { - Guard.NotNull(discount, nameof(discount)); + Guard.NotNull(discount, nameof(discount)); - _discountRepository.Delete(discount); + _discountRepository.Delete(discount); _requestCache.RemoveByPattern(DISCOUNTS_PATTERN_KEY); } @@ -106,29 +103,25 @@ public virtual Discount GetDiscountById(int discountId) public virtual IEnumerable GetAllDiscounts(DiscountType? discountType, string couponCode = "", bool showHidden = false) { - int? discountTypeId = null; - if (discountType.HasValue) - discountTypeId = (int)discountType.Value; - - // we load all discounts, and filter them by passed "discountType" parameter later - // we do it because we know that this method is invoked several times per HTTP request with distinct "discountType" parameter - // that's why we access the database only once - string key = string.Format(DISCOUNTS_ALL_KEY, showHidden, couponCode); + var discountTypeId = discountType.HasValue ? (int)discountType.Value : 0; + + // We load all discounts and filter them by passed "discountType" parameter later because + // this method is invoked several times per HTTP request with distinct "discountType" parameter. + var key = string.Format(DISCOUNTS_ALL_KEY, showHidden, couponCode); var result = _requestCache.Get(key, () => { var query = _discountRepository.Table; if (!showHidden) { - // The function 'CurrentUtcDateTime' is not supported by SQL Server Compact. - // That's why we pass the date value - var nowUtc = DateTime.UtcNow.Date; + var utcNow = DateTime.UtcNow; + query = query.Where(d => - (!d.StartDateUtc.HasValue || d.StartDateUtc <= nowUtc) - && (!d.EndDateUtc.HasValue || d.EndDateUtc >= nowUtc)); + (!d.StartDateUtc.HasValue || d.StartDateUtc <= utcNow) && + (!d.EndDateUtc.HasValue || d.EndDateUtc >= utcNow)); } - if (!String.IsNullOrWhiteSpace(couponCode)) + if (!string.IsNullOrWhiteSpace(couponCode)) { couponCode = couponCode.Trim(); query = query.Where(d => d.CouponCode == couponCode); @@ -138,15 +131,15 @@ public virtual IEnumerable GetAllDiscounts(DiscountType? discountType, var discounts = query.ToList(); - var map = new Multimap(); - discounts.Each(x => map.Add(x.DiscountTypeId, x)); + var map = new Multimap(); + discounts.Each(x => map.Add(x.DiscountTypeId, x)); - return map; + return map; }); if (discountTypeId > 0) { - return result[discountTypeId.Value]; + return result[discountTypeId]; } return result.SelectMany(x => x.Value); @@ -154,7 +147,7 @@ public virtual IEnumerable GetAllDiscounts(DiscountType? discountType, public virtual void InsertDiscount(Discount discount) { - Guard.NotNull(discount, nameof(discount)); + Guard.NotNull(discount, nameof(discount)); _discountRepository.Insert(discount); @@ -163,30 +156,12 @@ public virtual void InsertDiscount(Discount discount) public virtual void UpdateDiscount(Discount discount) { - Guard.NotNull(discount, nameof(discount)); + Guard.NotNull(discount, nameof(discount)); _discountRepository.Update(discount); _requestCache.RemoveByPattern(DISCOUNTS_PATTERN_KEY); } - public virtual void DeleteDiscountRequirement(DiscountRequirement discountRequirement) - { - Guard.NotNull(discountRequirement, nameof(discountRequirement)); - - _discountRequirementRepository.Delete(discountRequirement); - _requestCache.RemoveByPattern(DISCOUNTS_PATTERN_KEY); - } - - public virtual Provider LoadDiscountRequirementRuleBySystemName(string systemName, int storeId = 0) - { - return _providerManager.GetProvider(systemName, storeId); - } - - public virtual IEnumerable> LoadAllDiscountRequirementRules(int storeId = 0) - { - return _providerManager.GetAllProviders(storeId); - } - public virtual Discount GetDiscountByCouponCode(string couponCode, bool showHidden = false) { if (String.IsNullOrWhiteSpace(couponCode)) @@ -196,30 +171,30 @@ public virtual Discount GetDiscountByCouponCode(string couponCode, bool showHidd return discount; } - public virtual bool IsDiscountValid(Discount discount, Customer customer) + public virtual bool IsDiscountValid(Discount discount, Customer customer) { - var couponCodeToValidate = ""; + var couponCodeToValidate = ""; if (customer != null) - { - couponCodeToValidate = customer.GetAttribute(SystemCustomerAttributeNames.DiscountCouponCode, _genericAttributeService); - } + { + couponCodeToValidate = customer.GetAttribute(SystemCustomerAttributeNames.DiscountCouponCode, _genericAttributeService); + } var valid = IsDiscountValid(discount, customer, couponCodeToValidate); - return valid; + return valid; } public virtual bool IsDiscountValid(Discount discount, Customer customer, string couponCodeToValidate) { - Guard.NotNull(discount, nameof(discount)); + Guard.NotNull(discount, nameof(discount)); - var cacheKey = new DiscountKey(discount, customer, couponCodeToValidate); - if (_discountValidityCache.TryGetValue(cacheKey, out var result)) - { - return result; - } + var cacheKey = new DiscountKey(discount, customer, couponCodeToValidate); + if (_discountValidityCache.TryGetValue(cacheKey, out var result)) + { + return result; + } - // Check coupon code - if (discount.RequiresCouponCode) + // Check coupon code. + if (discount.RequiresCouponCode) { if (discount.CouponCode.IsEmpty()) return Cached(false); @@ -228,9 +203,8 @@ public virtual bool IsDiscountValid(Discount discount, Customer customer, string return Cached(false); } - // Check date range + // Check date range. var now = DateTime.UtcNow; - var store = _storeContext.CurrentStore; if (discount.StartDateUtc.HasValue) { @@ -249,40 +223,27 @@ public virtual bool IsDiscountValid(Discount discount, Customer customer, string if (!CheckDiscountLimitations(discount, customer)) return Cached(false); - // better not to apply discounts if there are gift cards in the cart cause the customer could "earn" money through that. - if (discount.DiscountType == DiscountType.AssignedToOrderTotal || discount.DiscountType == DiscountType.AssignedToOrderSubTotal) - { - var cart = customer.GetCartItems(ShoppingCartType.ShoppingCart, store.Id); - if (cart.Any(x => x.Item?.Product != null && x.Item.Product.IsGiftCard)) - return Cached(false); - } - - // discount requirements - var requirements = discount.DiscountRequirements; - foreach (var req in requirements) + // Better not to apply discounts if there are gift cards in the cart cause the customer could "earn" money through that. + if (discount.DiscountType == DiscountType.AssignedToOrderTotal || discount.DiscountType == DiscountType.AssignedToOrderSubTotal) { - var requirementRule = LoadDiscountRequirementRuleBySystemName(req.DiscountRequirementRuleSystemName, store.Id); - if (requirementRule == null) - continue; - - var request = new CheckDiscountRequirementRequest - { - DiscountRequirement = req, - Customer = customer, - Store = store - }; - - if (!requirementRule.Value.CheckRequirement(request)) + var cart = customer.GetCartItems(ShoppingCartType.ShoppingCart, _storeContext.CurrentStore.Id); + if (cart.Any(x => x.Item?.Product != null && x.Item.Product.IsGiftCard)) return Cached(false); } + // Rule sets. + if (!_cartRuleProvider.RuleMatches(discount)) + { + return Cached(false); + } + return Cached(true); - bool Cached(bool value) - { - _discountValidityCache[cacheKey] = value; - return value; - } + bool Cached(bool value) + { + _discountValidityCache[cacheKey] = value; + return value; + } } public virtual DiscountUsageHistory GetDiscountUsageHistoryById(int discountUsageHistoryId) @@ -310,34 +271,34 @@ public virtual IPagedList GetAllDiscountUsageHistory(int? public virtual void InsertDiscountUsageHistory(DiscountUsageHistory discountUsageHistory) { - Guard.NotNull(discountUsageHistory, nameof(discountUsageHistory)); + Guard.NotNull(discountUsageHistory, nameof(discountUsageHistory)); - _discountUsageHistoryRepository.Insert(discountUsageHistory); + _discountUsageHistoryRepository.Insert(discountUsageHistory); _requestCache.RemoveByPattern(DISCOUNTS_PATTERN_KEY); } public virtual void UpdateDiscountUsageHistory(DiscountUsageHistory discountUsageHistory) { - Guard.NotNull(discountUsageHistory, nameof(discountUsageHistory)); + Guard.NotNull(discountUsageHistory, nameof(discountUsageHistory)); - _discountUsageHistoryRepository.Update(discountUsageHistory); + _discountUsageHistoryRepository.Update(discountUsageHistory); _requestCache.RemoveByPattern(DISCOUNTS_PATTERN_KEY); } public virtual void DeleteDiscountUsageHistory(DiscountUsageHistory discountUsageHistory) { - Guard.NotNull(discountUsageHistory, nameof(discountUsageHistory)); + Guard.NotNull(discountUsageHistory, nameof(discountUsageHistory)); - _discountUsageHistoryRepository.Delete(discountUsageHistory); + _discountUsageHistoryRepository.Delete(discountUsageHistory); _requestCache.RemoveByPattern(DISCOUNTS_PATTERN_KEY); } - class DiscountKey : Tuple - { - public DiscountKey(Discount discount, Customer customer, string customerCouponCode) - : base(discount, customer, customerCouponCode) - { - } - } + class DiscountKey : Tuple + { + public DiscountKey(Discount discount, Customer customer, string customerCouponCode) + : base(discount, customer, customerCouponCode) + { + } + } } } diff --git a/src/Libraries/SmartStore.Services/Discounts/IDiscountRequirementRule.cs b/src/Libraries/SmartStore.Services/Discounts/IDiscountRequirementRule.cs deleted file mode 100644 index 15325d3141..0000000000 --- a/src/Libraries/SmartStore.Services/Discounts/IDiscountRequirementRule.cs +++ /dev/null @@ -1,25 +0,0 @@ -using SmartStore.Core.Plugins; - -namespace SmartStore.Services.Discounts -{ - /// - /// Represents a discount requirement rule - /// - public partial interface IDiscountRequirementRule : IProvider - { - /// - /// Check discount requirement - /// - /// Object that contains all information required to check the requirement (Current customer, discount, etc) - /// true - requirement is met; otherwise, false - bool CheckRequirement(CheckDiscountRequirementRequest request); - - /// - /// Get URL for rule configuration - /// - /// Discount identifier - /// Discount requirement identifier (if editing) - /// URL - string GetConfigurationUrl(int discountId, int? discountRequirementId); - } -} diff --git a/src/Libraries/SmartStore.Services/Discounts/IDiscountService.cs b/src/Libraries/SmartStore.Services/Discounts/IDiscountService.cs index 8aa55980f7..159328a87e 100644 --- a/src/Libraries/SmartStore.Services/Discounts/IDiscountService.cs +++ b/src/Libraries/SmartStore.Services/Discounts/IDiscountService.cs @@ -1,9 +1,7 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using SmartStore.Core; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Discounts; -using SmartStore.Core.Plugins; namespace SmartStore.Services.Discounts { @@ -25,13 +23,13 @@ public partial interface IDiscountService /// Discount Discount GetDiscountById(int discountId); - /// - /// Gets all discounts - /// - /// Discount type; null to load all discount - /// A value indicating whether to show hidden records - /// Discount collection - IEnumerable GetAllDiscounts(DiscountType? discountType, string couponCode = "", bool showHidden = false); + /// + /// Gets all discounts + /// + /// Discount type; null to load all discount + /// A value indicating whether to show hidden records + /// Discount collection + IEnumerable GetAllDiscounts(DiscountType? discountType, string couponCode = "", bool showHidden = false); /// /// Inserts a discount @@ -45,26 +43,6 @@ public partial interface IDiscountService /// Discount void UpdateDiscount(Discount discount); - /// - /// Delete discount requirement - /// - /// Discount requirement - void DeleteDiscountRequirement(DiscountRequirement discountRequirement); - - /// - /// Load discount requirement rule by system name - /// - /// System name - /// Found discount requirement rule - Provider LoadDiscountRequirementRuleBySystemName(string systemName, int storeId = 0); - - /// - /// Load all discount requirement rules - /// - /// Discount requirement rules - IEnumerable> LoadAllDiscountRequirementRules(int storeId = 0); - - /// /// Get discount by coupon code /// @@ -96,7 +74,7 @@ public partial interface IDiscountService /// Discount usage history record identifier /// Discount usage history DiscountUsageHistory GetDiscountUsageHistoryById(int discountUsageHistoryId); - + /// /// Gets all discount usage history records /// @@ -105,7 +83,7 @@ public partial interface IDiscountService /// Page index /// Page size /// Discount usage history records - IPagedList GetAllDiscountUsageHistory(int? discountId, + IPagedList GetAllDiscountUsageHistory(int? discountId, int? customerId, int pageIndex, int pageSize); /// @@ -113,7 +91,7 @@ IPagedList GetAllDiscountUsageHistory(int? discountId, /// /// Discount usage history record void InsertDiscountUsageHistory(DiscountUsageHistory discountUsageHistory); - + /// /// Update discount usage history record /// diff --git a/src/Libraries/SmartStore.Services/Events/ConsumerRegistry.cs b/src/Libraries/SmartStore.Services/Events/ConsumerRegistry.cs index 3f89138f56..7c03c1b4e1 100644 --- a/src/Libraries/SmartStore.Services/Events/ConsumerRegistry.cs +++ b/src/Libraries/SmartStore.Services/Events/ConsumerRegistry.cs @@ -2,125 +2,124 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Text; using System.Threading.Tasks; using SmartStore.Collections; using SmartStore.Core.Events; namespace SmartStore.Services.Events { - public class ConsumerRegistry : IConsumerRegistry - { - private readonly Multimap _descriptorMap = new Multimap(); - - public ConsumerRegistry(IEnumerable> consumers) - { - foreach (var consumer in consumers) - { - var metadata = consumer.Metadata; - - if (!metadata.IsActive) - continue; - - var methods = FindMethods(metadata); - var messageTypes = new Dictionary(); - - foreach (var method in methods) - { - var descriptor = new ConsumerDescriptor(metadata) - { - IsAsync = method.ReturnType == typeof(Task), - FireForget = method.HasAttribute(false) - }; - - //if (descriptor.IsAsync && descriptor.FireForget) - //{ - // throw new NotSupportedException($"An asynchronous message consumer method cannot be called as fire & forget. Method: '{method}'."); - //} - - if (method.ReturnType != typeof(Task) && method.ReturnType != typeof(void)) - { - throw new NotSupportedException($"A message consumer method's return type must either be 'void' or '${typeof(Task).FullName}'. Method: '{method}'."); - } - - if (method.Name.EndsWith("Async") && !descriptor.IsAsync) - { - throw new NotSupportedException($"A synchronous message consumer method name should not end with 'Async'. Method: '{method}'."); - } - - var parameters = method.GetParameters(); - if (parameters.Length == 0) - { - throw new NotSupportedException($"A message consumer method must have at least one parameter identifying the message to consume. Method: '{method}'."); - } - - if (parameters.Any(x => x.ParameterType.IsByRef || x.IsOut || x.IsOptional)) - { - throw new NotSupportedException($"'out', 'ref' and optional parameters are not allowed in consumer methods. Method: '{method}'."); - } - - var p = parameters[0]; - var messageType = p.ParameterType; - - if (messageType.IsGenericType && messageType.GetGenericTypeDefinition() == typeof(ConsumeContext<>)) - { - messageType = messageType.GetGenericArguments()[0]; - descriptor.WithEnvelope = true; - } - - if (messageTypes.TryGetValue(messageType, out var method2)) - { - // We won't allow methods with different signatures, but same message type: there can only be one! - throw new AmbigousConsumerException(method2, method); - } - - messageTypes.Add(messageType, method); - - if (messageType.IsPublic && (messageType.IsClass || messageType.IsInterface)) - { - // The method signature is valid: add to dictionary. - descriptor.MessageParameter = p; - descriptor.Parameters = parameters.Skip(1).ToArray(); - descriptor.MessageType = messageType; - descriptor.Method = method; - - _descriptorMap.Add(messageType, descriptor); - } - else - { - // TODO: message - throw new NotSupportedException(); - } - } - } - } - - private IEnumerable FindMethods(EventConsumerMetadata metadata) - { - var methods = metadata.ContainerType.GetMethods(BindingFlags.Public | BindingFlags.Instance); - - var validNames = new HashSet(new[] { "Handle", "HandleEvent", "Consume", "HandleAsync", "HandleEventAsync", "ConsumeAsync" }); - - foreach (var method in methods) - { - if (validNames.Contains(method.Name)) - { - yield return method; - } - } - } - - public virtual IEnumerable GetConsumers(object message) - { - Guard.NotNull(message, nameof(message)); - - var type = message.GetType(); - if (_descriptorMap.ContainsKey(type)) - { - return _descriptorMap[type]; - } - - return Enumerable.Empty(); - } - } + public class ConsumerRegistry : IConsumerRegistry + { + private readonly Multimap _descriptorMap = new Multimap(); + + public ConsumerRegistry(IEnumerable> consumers) + { + foreach (var consumer in consumers) + { + var metadata = consumer.Metadata; + + if (!metadata.IsActive) + continue; + + var methods = FindMethods(metadata); + var messageTypes = new Dictionary(); + + foreach (var method in methods) + { + var descriptor = new ConsumerDescriptor(metadata) + { + IsAsync = method.ReturnType == typeof(Task), + FireForget = method.HasAttribute(false) + }; + + //if (descriptor.IsAsync && descriptor.FireForget) + //{ + // throw new NotSupportedException($"An asynchronous message consumer method cannot be called as fire & forget. Method: '{method}'."); + //} + + if (method.ReturnType != typeof(Task) && method.ReturnType != typeof(void)) + { + throw new NotSupportedException($"A message consumer method's return type must either be 'void' or '${typeof(Task).FullName}'. Method: '{method}'."); + } + + if (method.Name.EndsWith("Async") && !descriptor.IsAsync) + { + throw new NotSupportedException($"A synchronous message consumer method name should not end with 'Async'. Method: '{method}'."); + } + + var parameters = method.GetParameters(); + if (parameters.Length == 0) + { + throw new NotSupportedException($"A message consumer method must have at least one parameter identifying the message to consume. Method: '{method}'."); + } + + if (parameters.Any(x => x.ParameterType.IsByRef || x.IsOut || x.IsOptional)) + { + throw new NotSupportedException($"'out', 'ref' and optional parameters are not allowed in consumer methods. Method: '{method}'."); + } + + var p = parameters[0]; + var messageType = p.ParameterType; + + if (messageType.IsGenericType && messageType.GetGenericTypeDefinition() == typeof(ConsumeContext<>)) + { + messageType = messageType.GetGenericArguments()[0]; + descriptor.WithEnvelope = true; + } + + if (messageTypes.TryGetValue(messageType, out var method2)) + { + // We won't allow methods with different signatures, but same message type: there can only be one! + throw new AmbigousConsumerException(method2, method); + } + + messageTypes.Add(messageType, method); + + if (messageType.IsPublic && (messageType.IsClass || messageType.IsInterface)) + { + // The method signature is valid: add to dictionary. + descriptor.MessageParameter = p; + descriptor.Parameters = parameters.Skip(1).ToArray(); + descriptor.MessageType = messageType; + descriptor.Method = method; + + _descriptorMap.Add(messageType, descriptor); + } + else + { + // TODO: message + throw new NotSupportedException(); + } + } + } + } + + private IEnumerable FindMethods(EventConsumerMetadata metadata) + { + var methods = metadata.ContainerType.GetMethods(BindingFlags.Public | BindingFlags.Instance); + + var validNames = new HashSet(new[] { "Handle", "HandleEvent", "Consume", "HandleAsync", "HandleEventAsync", "ConsumeAsync" }); + + foreach (var method in methods) + { + if (validNames.Contains(method.Name)) + { + yield return method; + } + } + } + + public virtual IEnumerable GetConsumers(object message) + { + Guard.NotNull(message, nameof(message)); + + var type = message.GetType(); + if (_descriptorMap.ContainsKey(type)) + { + return _descriptorMap[type]; + } + + return Enumerable.Empty(); + } + } } diff --git a/src/Libraries/SmartStore.Services/Events/ConsumerResolver.cs b/src/Libraries/SmartStore.Services/Events/ConsumerResolver.cs index ebab9eff71..cecb542b00 100644 --- a/src/Libraries/SmartStore.Services/Events/ConsumerResolver.cs +++ b/src/Libraries/SmartStore.Services/Events/ConsumerResolver.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Text; -using System.Threading.Tasks; using Autofac; using SmartStore.Core; using SmartStore.Core.Events; @@ -15,59 +12,59 @@ namespace SmartStore.Services.Events { - public class ConsumerResolver : IConsumerResolver - { - private readonly Work _container; + public class ConsumerResolver : IConsumerResolver + { + private readonly Work _container; - public ConsumerResolver(Work container) - { - _container = container; - } + public ConsumerResolver(Work container) + { + _container = container; + } - public virtual IConsumer Resolve(ConsumerDescriptor descriptor) - { - if (descriptor.PluginDescriptor == null || IsActiveForStore(descriptor.PluginDescriptor)) - { - return _container.Value.ResolveKeyed(descriptor.ContainerType); - } + public virtual IConsumer Resolve(ConsumerDescriptor descriptor) + { + if (descriptor.PluginDescriptor == null || IsActiveForStore(descriptor.PluginDescriptor)) + { + return _container.Value.ResolveKeyed(descriptor.ContainerType); + } - return null; - } + return null; + } - public virtual object ResolveParameter(ParameterInfo p, IComponentContext c = null) - { - return (c ?? _container.Value).Resolve(p.ParameterType); - } + public virtual object ResolveParameter(ParameterInfo p, IComponentContext c = null) + { + return (c ?? _container.Value).Resolve(p.ParameterType); + } - private bool IsActiveForStore(PluginDescriptor plugin) - { - int storeId = 0; - if (EngineContext.Current.IsFullyInitialized) - { - storeId = _container.Value.Resolve().CurrentStore.Id; - } + private bool IsActiveForStore(PluginDescriptor plugin) + { + int storeId = 0; + if (EngineContext.Current.IsFullyInitialized) + { + storeId = _container.Value.Resolve().CurrentStore.Id; + } - if (storeId == 0) - { - return true; - } + if (storeId == 0) + { + return true; + } - var settingService = _container.Value.Resolve(); + var settingService = _container.Value.Resolve(); - var limitedToStoresSetting = settingService.GetSettingByKey(plugin.GetSettingKey("LimitedToStores")); - if (limitedToStoresSetting.IsEmpty()) - { - return true; - } + var limitedToStoresSetting = settingService.GetSettingByKey(plugin.GetSettingKey("LimitedToStores")); + if (limitedToStoresSetting.IsEmpty()) + { + return true; + } - var limitedToStores = limitedToStoresSetting.ToIntArray(); - if (limitedToStores.Length > 0) - { - var flag = limitedToStores.Contains(storeId); - return flag; - } + var limitedToStores = limitedToStoresSetting.ToIntArray(); + if (limitedToStores.Length > 0) + { + var flag = limitedToStores.Contains(storeId); + return flag; + } - return true; - } - } + return true; + } + } } diff --git a/src/Libraries/SmartStore.Services/Events/DefaultMessageBus.cs b/src/Libraries/SmartStore.Services/Events/DefaultMessageBus.cs index f4c72f778b..564d7ddef1 100644 --- a/src/Libraries/SmartStore.Services/Events/DefaultMessageBus.cs +++ b/src/Libraries/SmartStore.Services/Events/DefaultMessageBus.cs @@ -7,29 +7,29 @@ namespace SmartStore.Services.Events { - public class DefaultMessageBus : IMessageBus - { - private readonly IMessageBroker _messageBroker; + public class DefaultMessageBus : IMessageBus + { + private readonly IMessageBroker _messageBroker; - public DefaultMessageBus(IEnumerable messageBrokers) - { - _messageBroker = messageBrokers.FirstOrDefault(); - } + public DefaultMessageBus(IEnumerable messageBrokers) + { + _messageBroker = messageBrokers.FirstOrDefault(); + } - public void Subscribe(string channel, Action handler) - { - if (_messageBroker == null) - return; + public void Subscribe(string channel, Action handler) + { + if (_messageBroker == null) + return; - _messageBroker.Subscribe(channel, handler); - } + _messageBroker.Subscribe(channel, handler); + } - public void Publish(string channel, string message) - { - if (_messageBroker == null) - return; + public void Publish(string channel, string message) + { + if (_messageBroker == null) + return; - _messageBroker.Publish(channel, message); - } - } + _messageBroker.Publish(channel, message); + } + } } diff --git a/src/Libraries/SmartStore.Services/Events/EventPublisher.cs b/src/Libraries/SmartStore.Services/Events/EventPublisher.cs index e6529d9469..026abf2efd 100644 --- a/src/Libraries/SmartStore.Services/Events/EventPublisher.cs +++ b/src/Libraries/SmartStore.Services/Events/EventPublisher.cs @@ -1,51 +1,49 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using SmartStore.ComponentModel; using SmartStore.Core.Events; using SmartStore.Core.Logging; namespace SmartStore.Services.Events { - public class EventPublisher : IEventPublisher - { - private readonly IConsumerRegistry _registry; - private readonly IConsumerResolver _resolver; - private readonly IConsumerInvoker _invoker; - - public EventPublisher(IConsumerRegistry registry, IConsumerResolver resolver, IConsumerInvoker invoker) - { - _registry = registry; - _resolver = resolver; - _invoker = invoker; - - Logger = NullLogger.Instance; - } - - public ILogger Logger { get; set; } - - public void Publish(T message) where T : class - { - var descriptors = _registry.GetConsumers(message); - - if (!descriptors.Any()) - { - return; - } - - var envelopeType = typeof(ConsumeContext<>).MakeGenericType(typeof(T)); - var envelope = (ConsumeContext)FastActivator.CreateInstance(envelopeType, message); - - foreach (var descriptor in descriptors) - { - var consumer = _resolver.Resolve(descriptor); - if (consumer != null) - { - _invoker.Invoke(descriptor, consumer, envelope); - } - } - } - } + public class EventPublisher : IEventPublisher + { + private readonly IConsumerRegistry _registry; + private readonly IConsumerResolver _resolver; + private readonly IConsumerInvoker _invoker; + + public EventPublisher(IConsumerRegistry registry, IConsumerResolver resolver, IConsumerInvoker invoker) + { + _registry = registry; + _resolver = resolver; + _invoker = invoker; + + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + public void Publish(T message) where T : class + { + var descriptors = _registry.GetConsumers(message); + + if (!descriptors.Any()) + { + return; + } + + var envelopeType = typeof(ConsumeContext<>).MakeGenericType(typeof(T)); + var envelope = (ConsumeContext)FastActivator.CreateInstance(envelopeType, message); + + foreach (var descriptor in descriptors) + { + var consumer = _resolver.Resolve(descriptor); + if (consumer != null) + { + _invoker.Invoke(descriptor, consumer, envelope); + } + } + } + } } diff --git a/src/Libraries/SmartStore.Services/Events/IMessageBroker.cs b/src/Libraries/SmartStore.Services/Events/IMessageBroker.cs index ce54a1965b..dcaf55717f 100644 --- a/src/Libraries/SmartStore.Services/Events/IMessageBroker.cs +++ b/src/Libraries/SmartStore.Services/Events/IMessageBroker.cs @@ -2,9 +2,9 @@ namespace SmartStore.Services.Events { - public interface IMessageBroker - { - void Subscribe(string channel, Action handler); - void Publish(string channel, string message); - } + public interface IMessageBroker + { + void Subscribe(string channel, Action handler); + void Publish(string channel, string message); + } } diff --git a/src/Libraries/SmartStore.Services/Events/IMessageBus.cs b/src/Libraries/SmartStore.Services/Events/IMessageBus.cs index d7245ba7e2..f2d764e4d6 100644 --- a/src/Libraries/SmartStore.Services/Events/IMessageBus.cs +++ b/src/Libraries/SmartStore.Services/Events/IMessageBus.cs @@ -2,9 +2,9 @@ namespace SmartStore.Services.Events { - public interface IMessageBus - { - void Subscribe(string channel, Action handler); - void Publish(string channel, string message); - } + public interface IMessageBus + { + void Subscribe(string channel, Action handler); + void Publish(string channel, string message); + } } diff --git a/src/Libraries/SmartStore.Services/Forums/ForumExtensions.cs b/src/Libraries/SmartStore.Services/Forums/ForumExtensions.cs index 54a67d93b6..1b4de19af4 100644 --- a/src/Libraries/SmartStore.Services/Forums/ForumExtensions.cs +++ b/src/Libraries/SmartStore.Services/Forums/ForumExtensions.cs @@ -23,12 +23,12 @@ public static string FormatPostText(this ForumPost post) return string.Empty; } - text = HtmlUtils.ConvertPlainTextToHtml(text.HtmlEncode()); + text = HtmlUtils.ConvertPlainTextToHtml(text.HtmlEncode()); - if (EngineContext.Current.Resolve().ForumEditor == EditorType.BBCodeEditor) - { - text = BBCodeHelper.ToHtml(text); - } + if (EngineContext.Current.Resolve().ForumEditor == EditorType.BBCodeEditor) + { + text = BBCodeHelper.ToHtml(text); + } return text; } @@ -74,8 +74,8 @@ public static string FormatForumSignatureText(this string text) return string.Empty; } - return HtmlUtils.ConvertPlainTextToHtml(text.HtmlEncode()); - } + return HtmlUtils.ConvertPlainTextToHtml(text.HtmlEncode()); + } /// /// Formats the private message text @@ -92,10 +92,10 @@ public static string FormatPrivateMessageText(this PrivateMessage message) return string.Empty; } - text = HtmlUtils.ConvertPlainTextToHtml(text.HtmlEncode()); + text = HtmlUtils.ConvertPlainTextToHtml(text.HtmlEncode()); return BBCodeHelper.ToHtml(text); } - + /// /// Get forum last topic /// diff --git a/src/Libraries/SmartStore.Services/Forums/ForumMessageFactoryExtensions.cs b/src/Libraries/SmartStore.Services/Forums/ForumMessageFactoryExtensions.cs index 0e8a1140be..ea779210d8 100644 --- a/src/Libraries/SmartStore.Services/Forums/ForumMessageFactoryExtensions.cs +++ b/src/Libraries/SmartStore.Services/Forums/ForumMessageFactoryExtensions.cs @@ -6,45 +6,45 @@ namespace SmartStore.Services.Forums { - public static class ForumMessageFactoryExtensions - { - /// - /// Sends a forum subscription message to a customer - /// - public static CreateMessageResult SendNewForumTopicMessage(this IMessageFactory factory, Customer customer, ForumTopic forumTopic, int languageId = 0) - { - Guard.NotNull(customer, nameof(customer)); - Guard.NotNull(forumTopic, nameof(forumTopic)); + public static class ForumMessageFactoryExtensions + { + /// + /// Sends a forum subscription message to a customer + /// + public static CreateMessageResult SendNewForumTopicMessage(this IMessageFactory factory, Customer customer, ForumTopic forumTopic, int languageId = 0) + { + Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(forumTopic, nameof(forumTopic)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewForumTopic, languageId, customer: customer), true, forumTopic, forumTopic.Forum); - } + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewForumTopic, languageId, customer: customer), true, forumTopic, forumTopic.Forum); + } - /// - /// Sends a forum subscription message to a customer - /// - /// Friendly forum topic page to use for URL generation (1-based) - public static CreateMessageResult SendNewForumPostMessage(this IMessageFactory factory, Customer customer, ForumPost forumPost, int topicPageIndex, int languageId = 0) - { - Guard.NotNull(customer, nameof(customer)); - Guard.NotNull(forumPost, nameof(forumPost)); + /// + /// Sends a forum subscription message to a customer + /// + /// Friendly forum topic page to use for URL generation (1-based) + public static CreateMessageResult SendNewForumPostMessage(this IMessageFactory factory, Customer customer, ForumPost forumPost, int topicPageIndex, int languageId = 0) + { + Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(forumPost, nameof(forumPost)); - var bag = new ModelPart - { - ["TopicPageIndex"] = topicPageIndex - }; + var bag = new ModelPart + { + ["TopicPageIndex"] = topicPageIndex + }; - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewForumPost, languageId, customer: customer), true, bag, forumPost, forumPost.ForumTopic, forumPost.ForumTopic.Forum); - } + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewForumPost, languageId, customer: customer), true, bag, forumPost, forumPost.ForumTopic, forumPost.ForumTopic.Forum); + } - /// - /// Sends a private message notification - /// - public static CreateMessageResult SendPrivateMessageNotification(this IMessageFactory factory, Customer customer, PrivateMessage privateMessage, int languageId = 0) - { - Guard.NotNull(customer, nameof(customer)); - Guard.NotNull(privateMessage, nameof(privateMessage)); + /// + /// Sends a private message notification + /// + public static CreateMessageResult SendPrivateMessageNotification(this IMessageFactory factory, Customer customer, PrivateMessage privateMessage, int languageId = 0) + { + Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(privateMessage, nameof(privateMessage)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewPrivateMessage, languageId, privateMessage.StoreId, customer), true, privateMessage); - } - } + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewPrivateMessage, languageId, privateMessage.StoreId, customer), true, privateMessage); + } + } } diff --git a/src/Libraries/SmartStore.Services/Forums/ForumService.cs b/src/Libraries/SmartStore.Services/Forums/ForumService.cs index 11b744b8ab..6430f209ce 100644 --- a/src/Libraries/SmartStore.Services/Forums/ForumService.cs +++ b/src/Libraries/SmartStore.Services/Forums/ForumService.cs @@ -10,10 +10,16 @@ using SmartStore.Data.Caching; using SmartStore.Services.Common; using SmartStore.Services.Customers; +using SmartStore.Services.Seo; +using SmartStore.Core.Domain.Seo; +using SmartStore.Core.Domain.Localization; +using System.Web.Mvc; +using System; +using SmartStore.Data.Utilities; namespace SmartStore.Services.Forums { - public partial class ForumService : IForumService + public partial class ForumService : IForumService, IXmlSitemapPublisher { private readonly IRepository _forumGroupRepository; private readonly IRepository _forumRepository; @@ -27,6 +33,8 @@ public partial class ForumService : IForumService private readonly IRepository _customerRepository; private readonly IGenericAttributeService _genericAttributeService; private readonly ICustomerService _customerService; + private readonly Lazy _urlHelper; + private readonly Lazy _urlRecordService; private readonly ICommonServices _services; public ForumService( @@ -42,6 +50,8 @@ public ForumService( IRepository customerRepository, IGenericAttributeService genericAttributeService, ICustomerService customerService, + Lazy urlHelper, + Lazy urlRecordService, ICommonServices services) { _forumGroupRepository = forumGroupRepository; @@ -56,6 +66,8 @@ public ForumService( _customerRepository = customerRepository; _genericAttributeService = genericAttributeService; _customerService = customerService; + _urlHelper = urlHelper; + _urlRecordService = urlRecordService; _services = services; } @@ -173,7 +185,7 @@ from sm in fg_sm.DefaultIfEmpty() if (!showHidden && !QuerySettings.IgnoreAcl) { - var allowedCustomerRolesIds = _services.WorkContext.CurrentCustomer.CustomerRoles.Where(x => x.Active).Select(x => x.Id).ToList(); + var allowedCustomerRolesIds = _services.WorkContext.CurrentCustomer.GetRoleIds(); query = from fg in query @@ -261,12 +273,12 @@ public virtual void DeleteForum(Forum forum) } // Delete forum subscriptions (topics). - var queryTopicIds = + var queryTopicIds = from ft in _forumTopicRepository.Table where ft.ForumId == forum.Id select ft.Id; - var queryFs1 = + var queryFs1 = from fs in _forumSubscriptionRepository.Table where queryTopicIds.Contains(fs.TopicId) select fs; @@ -277,7 +289,7 @@ where queryTopicIds.Contains(fs.TopicId) } // Delete forum subscriptions (forum). - var queryFs2 = + var queryFs2 = from fs in _forumSubscriptionRepository.Table where fs.ForumId == forum.Id select fs; @@ -380,7 +392,7 @@ from sm in fg_sm.DefaultIfEmpty() if (!showHidden && !QuerySettings.IgnoreAcl) { - var allowedCustomerRolesIds = customer.CustomerRoles.Where(x => x.Active).Select(x => x.Id).ToList(); + var allowedCustomerRolesIds = customer.GetRoleIds(); query = from ft in query @@ -703,17 +715,17 @@ public virtual PrivateMessage GetPrivateMessageById(int messageId) return privateMessage; } - public virtual IPagedList GetAllPrivateMessages( - int storeId, - int fromCustomerId, - int toCustomerId, - bool? isRead, - bool? isDeletedByAuthor, - bool? isDeletedByRecipient, - int pageIndex, - int pageSize) + public virtual IPagedList GetAllPrivateMessages( + int storeId, + int fromCustomerId, + int toCustomerId, + bool? isRead, + bool? isDeletedByAuthor, + bool? isDeletedByRecipient, + int pageIndex, + int pageSize) { - var query = _forumPrivateMessageRepository.Table; + var query = _forumPrivateMessageRepository.Table; if (storeId > 0) { @@ -740,9 +752,9 @@ public virtual IPagedList GetAllPrivateMessages( query = query.Where(pm => isDeletedByRecipient.Value == pm.IsDeletedByRecipient); } - query = query.OrderByDescending(pm => pm.CreatedOnUtc); + query = query.OrderByDescending(pm => pm.CreatedOnUtc); - var privateMessages = new PagedList(query, pageIndex, pageSize); + var privateMessages = new PagedList(query, pageIndex, pageSize); return privateMessages; } @@ -758,11 +770,11 @@ public virtual void InsertPrivateMessage(PrivateMessage message) throw new SmartException("Recipient could not be loaded"); } - _genericAttributeService.SaveAttribute(customerTo, SystemCustomerAttributeNames.NotifiedAboutNewPrivateMessages, false, message.StoreId); + _genericAttributeService.SaveAttribute(customerTo, SystemCustomerAttributeNames.NotifiedAboutNewPrivateMessages, false, message.StoreId); if (_forumSettings.NotifyAboutPrivateMessages) { - _services.MessageFactory.SendPrivateMessageNotification(customerTo, message, _services.WorkContext.WorkingLanguage.Id); + _services.MessageFactory.SendPrivateMessageNotification(customerTo, message, _services.WorkContext.WorkingLanguage.Id); } } @@ -785,7 +797,7 @@ public virtual void DeletePrivateMessage(PrivateMessage message) if (message != null) { _forumPrivateMessageRepository.Delete(message); - } + } } #endregion @@ -805,21 +817,21 @@ public virtual ForumSubscription GetSubscriptionById(int subscriptionId) public virtual IPagedList GetAllSubscriptions(int customerId, int forumId, int topicId, int pageIndex, int pageSize) { - var fsQuery = - from fs in _forumSubscriptionRepository.Table - join c in _customerRepository.Table on fs.CustomerId equals c.Id - where - (customerId == 0 || fs.CustomerId == customerId) && - (forumId == 0 || fs.ForumId == forumId) && - (topicId == 0 || fs.TopicId == topicId) && - (c.Active && !c.Deleted) - select fs.SubscriptionGuid; - - var query = - from fs in _forumSubscriptionRepository.Table - where fsQuery.Contains(fs.SubscriptionGuid) - orderby fs.CreatedOnUtc descending, fs.SubscriptionGuid descending - select fs; + var fsQuery = + from fs in _forumSubscriptionRepository.Table + join c in _customerRepository.Table on fs.CustomerId equals c.Id + where + (customerId == 0 || fs.CustomerId == customerId) && + (forumId == 0 || fs.ForumId == forumId) && + (topicId == 0 || fs.TopicId == topicId) && + (c.Active && !c.Deleted) + select fs.SubscriptionGuid; + + var query = + from fs in _forumSubscriptionRepository.Table + where fsQuery.Contains(fs.SubscriptionGuid) + orderby fs.CreatedOnUtc descending, fs.SubscriptionGuid descending + select fs; var forumSubscriptions = new PagedList(query, pageIndex, pageSize); return forumSubscriptions; @@ -983,5 +995,119 @@ public virtual bool IsCustomerAllowedToSubscribe(Customer customer) } #endregion + + #region XML Sitemap + + public XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context) + { + if (!context.LoadSetting().XmlSitemapIncludesForum || !context.LoadSetting().ForumsEnabled) + return null; + + return new ForumXmlSitemapResult(context, this, _urlHelper.Value, _urlRecordService.Value); + } + + class ForumXmlSitemapResult : XmlSitemapProvider + { + private readonly IForumService _forumService; + private readonly XmlSitemapBuildContext _context; + private readonly UrlHelper _urlHelper; + private readonly IUrlRecordService _urlRecordService; + + private readonly IList _groups; + private readonly IList _forums; + private readonly IQueryable _topicsQuery; + + public ForumXmlSitemapResult( + XmlSitemapBuildContext context, + IForumService forumService, + UrlHelper urlHelper, + IUrlRecordService urlRecordService) + { + _forumService = forumService; + _context = context; + _urlHelper = urlHelper; + _urlRecordService = urlRecordService; + + _groups = _forumService.GetAllForumGroups(context.RequestStoreId, false); + _forums = _groups.SelectMany(x => x.Forums).ToList(); + _topicsQuery = _forumService.GetAllTopics(0, 0, int.MaxValue, false).SourceQuery; + } + + public override int GetTotalCount() + { + // INFO: we gonna create nodes for all groups, forums within groups and all topics + return _groups.Count + _forums.Count + _topicsQuery.Count(); + } + + public override XmlSitemapNode CreateNode(UrlHelper urlHelper, string baseUrl, NamedEntity entity, UrlRecordCollection slugs, Language language) + { + var path = string.Empty; + + switch (entity.EntityName) + { + case nameof(ForumGroup): + path = urlHelper.RouteUrl("ForumGroupSlug", new { id = entity.Id, slug = slugs.GetSlug(language.Id, entity.Id, true) }); + break; + case nameof(Forum): + path = urlHelper.RouteUrl("ForumSlug", new { id = entity.Id, slug = slugs.GetSlug(language.Id, entity.Id, true) }); + break; + case nameof(ForumTopic): + path = urlHelper.RouteUrl("TopicSlug", new { id = entity.Id, slug = entity.Slug }); + break; + } + + if (path.HasValue()) + { + return new XmlSitemapNode + { + LastMod = entity.LastMod, + Loc = baseUrl + path.TrimStart('/') + }; + } + + return null; + } + + public override IEnumerable Enlist() + { + // Enlist forum groups + foreach (var group in _groups) + { + yield return new NamedEntity { EntityName = nameof(ForumGroup), Id = group.Id, LastMod = group.UpdatedOnUtc }; + } + + // Enlist forums + foreach (var forum in _forums) + { + yield return new NamedEntity { EntityName = nameof(Forum), Id = forum.Id, LastMod = forum.UpdatedOnUtc }; + } + + // Enlist topics + var pager = new FastPager(_topicsQuery.AsNoTracking(), _context.MaximumNodeCount); + + while (pager.ReadNextPage(x => new { x.Id, x.UpdatedOnUtc, x.Subject }, x => x.Id, out var topics)) + { + if (_context.CancellationToken.IsCancellationRequested) + { + break; + } + + foreach (var x in topics) + { + yield return new NamedEntity + { + EntityName = nameof(ForumTopic), + Slug = (new ForumTopic { Subject = x.Subject }).GetSeName(), + Id = x.Id, + LastMod = x.UpdatedOnUtc + }; + } + } + } + + public override int Order => 1000; + } + + #endregion } } diff --git a/src/Libraries/SmartStore.Services/Helpers/DateTimeHelper.cs b/src/Libraries/SmartStore.Services/Helpers/DateTimeHelper.cs index f0f08b37ea..37a1b86fd3 100644 --- a/src/Libraries/SmartStore.Services/Helpers/DateTimeHelper.cs +++ b/src/Libraries/SmartStore.Services/Helpers/DateTimeHelper.cs @@ -1,9 +1,9 @@ using System; using System.Collections.ObjectModel; using System.Diagnostics; +using System.Runtime.CompilerServices; using SmartStore.Core; using SmartStore.Core.Domain.Customers; -using SmartStore.Services.Common; using SmartStore.Services.Configuration; using SmartStore.Services.Customers; @@ -12,7 +12,6 @@ namespace SmartStore.Services.Helpers public partial class DateTimeHelper : IDateTimeHelper { private readonly IWorkContext _workContext; - private readonly IGenericAttributeService _genericAttributeService; private readonly ISettingService _settingService; private readonly DateTimeSettings _dateTimeSettings; private readonly ICustomerService _customerService; @@ -20,45 +19,47 @@ public partial class DateTimeHelper : IDateTimeHelper private TimeZoneInfo _cachedUserTimeZone; public DateTimeHelper( - IWorkContext workContext, - IGenericAttributeService genericAttributeService, - ISettingService settingService, + IWorkContext workContext, + ISettingService settingService, DateTimeSettings dateTimeSettings, ICustomerService customerService) { _workContext = workContext; - _genericAttributeService = genericAttributeService; _settingService = settingService; _dateTimeSettings = dateTimeSettings; _customerService = customerService; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual TimeZoneInfo FindTimeZoneById(string id) { return TimeZoneInfo.FindSystemTimeZoneById(id); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual ReadOnlyCollection GetSystemTimeZones() { return TimeZoneInfo.GetSystemTimeZones(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual DateTime ConvertToUserTime(DateTime dt) { return ConvertToUserTime(dt, dt.Kind); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual DateTime ConvertToUserTime(DateTime dt, DateTimeKind sourceDateTimeKind) { - dt = DateTime.SpecifyKind(dt, sourceDateTimeKind); - var currentUserTimeZoneInfo = this.CurrentTimeZone; - return TimeZoneInfo.ConvertTime(dt, currentUserTimeZoneInfo); + return TimeZoneInfo.ConvertTime( + DateTime.SpecifyKind(dt, sourceDateTimeKind), + this.CurrentTimeZone); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual DateTime ConvertToUserTime(DateTime dt, TimeZoneInfo sourceTimeZone) { - var currentUserTimeZoneInfo = this.CurrentTimeZone; - return ConvertToUserTime(dt, sourceTimeZone, currentUserTimeZoneInfo); + return ConvertToUserTime(dt, sourceTimeZone, this.CurrentTimeZone); } public virtual DateTime ConvertToUserTime(DateTime dt, TimeZoneInfo sourceTimeZone, TimeZoneInfo destinationTimeZone) @@ -66,11 +67,13 @@ public virtual DateTime ConvertToUserTime(DateTime dt, TimeZoneInfo sourceTimeZo return TimeZoneInfo.ConvertTime(dt, sourceTimeZone, destinationTimeZone); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual DateTime ConvertToUtcTime(DateTime dt) { return ConvertToUtcTime(dt, dt.Kind); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual DateTime ConvertToUtcTime(DateTime dt, DateTimeKind sourceDateTimeKind) { dt = DateTime.SpecifyKind(dt, sourceDateTimeKind); @@ -92,21 +95,21 @@ public virtual DateTime ConvertToUtcTime(DateTime dt, TimeZoneInfo sourceTimeZon public virtual TimeZoneInfo GetCustomerTimeZone(Customer customer) { - if (_cachedUserTimeZone != null) - return _cachedUserTimeZone; + if (_cachedUserTimeZone != null) + return _cachedUserTimeZone; - // registered user - TimeZoneInfo timeZone = null; + // registered user + TimeZoneInfo timeZone = null; if (_dateTimeSettings.AllowCustomersToSetTimeZone) { string timeZoneId = string.Empty; if (customer != null) - timeZoneId = customer.TimeZoneId; + timeZoneId = customer.TimeZoneId; try { - if (timeZoneId.HasValue()) - timeZone = FindTimeZoneById(timeZoneId); + if (timeZoneId.HasValue()) + timeZone = FindTimeZoneById(timeZoneId); } catch (Exception ex) { @@ -118,9 +121,9 @@ public virtual TimeZoneInfo GetCustomerTimeZone(Customer customer) if (timeZone == null) timeZone = this.DefaultStoreTimeZone; - _cachedUserTimeZone = timeZone; + _cachedUserTimeZone = timeZone; - return timeZone; + return timeZone; } public virtual TimeZoneInfo DefaultStoreTimeZone @@ -131,7 +134,7 @@ public virtual TimeZoneInfo DefaultStoreTimeZone try { if (_dateTimeSettings.DefaultStoreTimeZoneId.HasValue()) - timeZoneInfo = FindTimeZoneById(_dateTimeSettings.DefaultStoreTimeZoneId); + timeZoneInfo = FindTimeZoneById(_dateTimeSettings.DefaultStoreTimeZoneId); } catch (Exception ex) { @@ -153,17 +156,14 @@ public virtual TimeZoneInfo DefaultStoreTimeZone _dateTimeSettings.DefaultStoreTimeZoneId = defaultTimeZoneId; _settingService.SaveSetting(_dateTimeSettings); - _cachedUserTimeZone = null; + _cachedUserTimeZone = null; - } + } } public virtual TimeZoneInfo CurrentTimeZone { - get - { - return GetCustomerTimeZone(_workContext.CurrentCustomer); - } + get => GetCustomerTimeZone(_workContext.CurrentCustomer); set { if (!_dateTimeSettings.AllowCustomersToSetTimeZone) @@ -178,8 +178,8 @@ public virtual TimeZoneInfo CurrentTimeZone _workContext.CurrentCustomer.TimeZoneId = timeZoneId; _customerService.UpdateCustomer(_workContext.CurrentCustomer); - _cachedUserTimeZone = null; - } + _cachedUserTimeZone = null; + } } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Hooks/AclEntityHook.cs b/src/Libraries/SmartStore.Services/Hooks/AclEntityHook.cs index b1518ba862..9ebb286f0b 100644 --- a/src/Libraries/SmartStore.Services/Hooks/AclEntityHook.cs +++ b/src/Libraries/SmartStore.Services/Hooks/AclEntityHook.cs @@ -3,40 +3,41 @@ using SmartStore.Core.Data; using SmartStore.Core.Data.Hooks; using SmartStore.Core.Domain.Security; +using SmartStore.Data; using SmartStore.Services.Security; namespace SmartStore.Services.Common { - public class AclEntityHook : DbSaveHook - { - private readonly Lazy _aclService; - private readonly HashSet _toDelete = new HashSet(); + public class AclEntityHook : DbSaveHook + { + private readonly Lazy _aclService; + private readonly HashSet _toDelete = new HashSet(); - public AclEntityHook(Lazy aclService) - { - _aclService = aclService; - } + public AclEntityHook(Lazy aclService) + { + _aclService = aclService; + } - protected override void OnDeleted(IAclSupported entity, IHookedEntity entry) - { - var entityType = entry.EntityType; + protected override void OnDeleted(IAclSupported entity, IHookedEntity entry) + { + var entityType = entry.EntityType; - var records = _aclService.Value.GetAclRecordsFor(entityType.Name, entry.Entity.Id); - _toDelete.AddRange(records); - } + var records = _aclService.Value.GetAclRecordsFor(entityType.Name, entry.Entity.Id); + _toDelete.AddRange(records); + } - public override void OnAfterSaveCompleted() - { - if (_toDelete.Count == 0) - return; + public override void OnAfterSaveCompleted() + { + if (_toDelete.Count == 0) + return; - using (var scope = new DbContextScope(autoCommit: false)) - { - _toDelete.Each(x => _aclService.Value.DeleteAclRecord(x)); - scope.Commit(); - } + using (var scope = new DbContextScope(autoCommit: false)) + { + _toDelete.Each(x => _aclService.Value.DeleteAclRecord(x)); + scope.Commit(); + } - _toDelete.Clear(); - } - } + _toDelete.Clear(); + } + } } diff --git a/src/Libraries/SmartStore.Services/Hooks/AuditableHook.cs b/src/Libraries/SmartStore.Services/Hooks/AuditableHook.cs index cb22b6a757..8d5a068e62 100644 --- a/src/Libraries/SmartStore.Services/Hooks/AuditableHook.cs +++ b/src/Libraries/SmartStore.Services/Hooks/AuditableHook.cs @@ -1,26 +1,27 @@ using System; using SmartStore.Core; using SmartStore.Core.Data.Hooks; +using SmartStore.Data; namespace SmartStore.Services.Hooks { - [Important] - public class AuditableHook : DbSaveHook - { - protected override void OnInserting(IAuditable entity, IHookedEntity entry) - { - var now = DateTime.UtcNow; + [Important] + public class AuditableHook : DbSaveHook + { + protected override void OnInserting(IAuditable entity, IHookedEntity entry) + { + var now = DateTime.UtcNow; - if (entity.CreatedOnUtc == DateTime.MinValue) - entity.CreatedOnUtc = now; + if (entity.CreatedOnUtc == DateTime.MinValue) + entity.CreatedOnUtc = now; - if (entity.UpdatedOnUtc == DateTime.MinValue) - entity.UpdatedOnUtc = now; - } + if (entity.UpdatedOnUtc == DateTime.MinValue) + entity.UpdatedOnUtc = now; + } - protected override void OnUpdating(IAuditable entity, IHookedEntity entry) - { - entity.UpdatedOnUtc = DateTime.UtcNow; - } - } + protected override void OnUpdating(IAuditable entity, IHookedEntity entry) + { + entity.UpdatedOnUtc = DateTime.UtcNow; + } + } } diff --git a/src/Libraries/SmartStore.Services/Hooks/FixProductMainPictureHook.cs b/src/Libraries/SmartStore.Services/Hooks/FixProductMainPictureHook.cs index 91c9121160..8651c5e009 100644 --- a/src/Libraries/SmartStore.Services/Hooks/FixProductMainPictureHook.cs +++ b/src/Libraries/SmartStore.Services/Hooks/FixProductMainPictureHook.cs @@ -7,48 +7,48 @@ namespace SmartStore.Services.Common { - public class FixProductMainPictureHook : DbSaveHook - { - private readonly IRepository _rsProduct; - private readonly HashSet _products = new HashSet(); - - public FixProductMainPictureHook(IRepository rsProduct) - { - _rsProduct = rsProduct; - } - - protected override void OnDeleting(ProductPicture entity, IHookedEntity entry) - { - Fix(entity); - } - - protected override void OnInserting(ProductPicture entity, IHookedEntity entry) - { - Fix(entity); - } - - protected override void OnUpdating(ProductPicture entity, IHookedEntity entry) - { - Fix(entity); - } - - private void Fix(ProductPicture entity) - { - var product = entity.Product ?? _rsProduct.GetById(entity.ProductId); - if (product != null) - { - _products.Add(product); - } - } - - public override void OnBeforeSaveCompleted() - { - foreach (var product in _products) - { - DataMigrator.FixProductMainPictureId(_rsProduct.Context, product); - } - - _products.Clear(); - } - } + public class FixProductMainPictureHook : DbSaveHook + { + private readonly IRepository _rsProduct; + private readonly HashSet _products = new HashSet(); + + public FixProductMainPictureHook(IRepository rsProduct) + { + _rsProduct = rsProduct; + } + + protected override void OnDeleting(ProductMediaFile entity, IHookedEntity entry) + { + Fix(entity); + } + + protected override void OnInserting(ProductMediaFile entity, IHookedEntity entry) + { + Fix(entity); + } + + protected override void OnUpdating(ProductMediaFile entity, IHookedEntity entry) + { + Fix(entity); + } + + private void Fix(ProductMediaFile entity) + { + var product = entity.Product ?? _rsProduct.GetById(entity.ProductId); + if (product != null) + { + _products.Add(product); + } + } + + public override void OnBeforeSaveCompleted() + { + foreach (var product in _products) + { + DataMigrator.FixProductMainPictureId(_rsProduct.Context, product); + } + + _products.Clear(); + } + } } diff --git a/src/Libraries/SmartStore.Services/Hooks/LocalizedEntityHook.cs b/src/Libraries/SmartStore.Services/Hooks/LocalizedEntityHook.cs index c0f0942078..8622f4a66b 100644 --- a/src/Libraries/SmartStore.Services/Hooks/LocalizedEntityHook.cs +++ b/src/Libraries/SmartStore.Services/Hooks/LocalizedEntityHook.cs @@ -3,42 +3,43 @@ using SmartStore.Core.Data; using SmartStore.Core.Data.Hooks; using SmartStore.Core.Domain.Localization; +using SmartStore.Data; using SmartStore.Services.Localization; namespace SmartStore.Services.Hooks { - public class LocalizedEntityHook : DbSaveHook - { - private readonly Lazy _localizedEntityService; - private readonly HashSet _toDelete = new HashSet(); + public class LocalizedEntityHook : DbSaveHook + { + private readonly Lazy _localizedEntityService; + private readonly HashSet _toDelete = new HashSet(); - public LocalizedEntityHook(Lazy localizedEntityService) - { - _localizedEntityService = localizedEntityService; - } + public LocalizedEntityHook(Lazy localizedEntityService) + { + _localizedEntityService = localizedEntityService; + } - protected override void OnDeleted(ILocalizedEntity entity, IHookedEntity entry) - { - var entityType = entry.EntityType; - var localizedEntities = _localizedEntityService.Value.GetLocalizedProperties(entry.Entity.Id, entityType.Name); - _toDelete.AddRange(localizedEntities); - } + protected override void OnDeleted(ILocalizedEntity entity, IHookedEntity entry) + { + var entityType = entry.EntityType; + var localizedEntities = _localizedEntityService.Value.GetLocalizedProperties(entry.Entity.Id, entityType.Name); + _toDelete.AddRange(localizedEntities); + } - public override void OnAfterSaveCompleted() - { - if (_toDelete.Count == 0) - return; + public override void OnAfterSaveCompleted() + { + if (_toDelete.Count == 0) + return; - using (var scope = new DbContextScope(autoCommit: false)) - { - using (_localizedEntityService.Value.BeginScope()) - { - _toDelete.Each(x => _localizedEntityService.Value.DeleteLocalizedProperty(x)); - } + using (var scope = new DbContextScope(autoCommit: false)) + { + using (_localizedEntityService.Value.BeginScope()) + { + _toDelete.Each(x => _localizedEntityService.Value.DeleteLocalizedProperty(x)); + } - scope.Commit(); - _toDelete.Clear(); - } - } - } + scope.Commit(); + _toDelete.Clear(); + } + } + } } diff --git a/src/Libraries/SmartStore.Services/Hooks/LocalizedEntityRelationHook.cs b/src/Libraries/SmartStore.Services/Hooks/LocalizedEntityRelationHook.cs index 61555fb8eb..b20ace7997 100644 --- a/src/Libraries/SmartStore.Services/Hooks/LocalizedEntityRelationHook.cs +++ b/src/Libraries/SmartStore.Services/Hooks/LocalizedEntityRelationHook.cs @@ -11,101 +11,106 @@ namespace SmartStore.Services.Hooks { - /// - /// Deletes localized properties of entities that were deleted by referential integrity. - /// Otherwise these localized properties would remain in the database. - /// - public class LocalizedEntityRelationHook : DbSaveHook - { - private readonly Lazy> _productAttributeRepository; - private readonly Lazy _localizedEntityService; - private readonly Lazy _productAttributeService; - private readonly Lazy _specificationAttributeService; + /// + /// Deletes localized properties of entities that were deleted by referential integrity. + /// Otherwise these localized properties would remain in the database. + /// + public class LocalizedEntityRelationHook : DbSaveHook + { + private readonly Lazy> _productAttributeRepository; + private readonly Lazy _localizedEntityService; + private readonly Lazy _productAttributeService; + private readonly Lazy _specificationAttributeService; - private readonly HashSet _toDelete = new HashSet(); + private readonly HashSet _toDelete = new HashSet(); - private static readonly HashSet _candidateTypes = new HashSet(new Type[] - { - typeof(SpecificationAttribute), - typeof(ProductVariantAttribute), - typeof(ProductAttribute), - typeof(ProductAttributeOptionsSet) - }); + private static readonly HashSet _candidateTypes = new HashSet(new Type[] + { + typeof(SpecificationAttribute), + typeof(ProductVariantAttribute), + typeof(ProductAttribute), + typeof(ProductAttributeOptionsSet) + }); - public LocalizedEntityRelationHook( - Lazy> productAttributeRepository, - Lazy localizedEntityService, - Lazy productAttributeService, - Lazy specificationAttributeService) - { - _productAttributeRepository = productAttributeRepository; - _localizedEntityService = localizedEntityService; - _productAttributeService = productAttributeService; - _specificationAttributeService = specificationAttributeService; - } + public LocalizedEntityRelationHook( + Lazy> productAttributeRepository, + Lazy localizedEntityService, + Lazy productAttributeService, + Lazy specificationAttributeService) + { + _productAttributeRepository = productAttributeRepository; + _localizedEntityService = localizedEntityService; + _productAttributeService = productAttributeService; + _specificationAttributeService = specificationAttributeService; + } - protected override void OnDeleting(BaseEntity entity, IHookedEntity entry) - { - var type = entry.EntityType; + protected override void OnDeleting(BaseEntity entity, IHookedEntity entry) + { + var type = entry.EntityType; - if (!_candidateTypes.Contains(type)) - throw new NotSupportedException(); + if (!_candidateTypes.Contains(type)) + throw new NotSupportedException(); - if (type == typeof(SpecificationAttribute)) - { - var attribute = (SpecificationAttribute)entry.Entity; - var attributeOptions = attribute.SpecificationAttributeOptions.ToList(); - if (!attributeOptions.Any()) - attributeOptions = _specificationAttributeService.Value.GetSpecificationAttributeOptionsBySpecificationAttribute(attribute.Id).ToList(); + if (type == typeof(SpecificationAttribute)) + { + var attribute = (SpecificationAttribute)entry.Entity; + var attributeOptions = attribute.SpecificationAttributeOptions.ToList(); + if (!attributeOptions.Any()) + attributeOptions = _specificationAttributeService.Value.GetSpecificationAttributeOptionsBySpecificationAttribute(attribute.Id).ToList(); - attributeOptions.ForEach(x => _toDelete.AddRange(_localizedEntityService.Value.GetLocalizedProperties(x.Id, "SpecificationAttributeOption"))); - } - else if (type == typeof(ProductVariantAttribute)) - { - var attribute = (ProductVariantAttribute)entry.Entity; - var attributeOptions = attribute.ProductVariantAttributeValues.ToList(); - if (!attributeOptions.Any()) - attributeOptions = _productAttributeService.Value.GetProductVariantAttributeValues(attribute.Id).ToList(); + attributeOptions.ForEach(x => _toDelete.AddRange(_localizedEntityService.Value.GetLocalizedProperties(x.Id, "SpecificationAttributeOption"))); + } + else if (type == typeof(ProductVariantAttribute)) + { + var attribute = (ProductVariantAttribute)entry.Entity; + var attributeOptions = attribute.ProductVariantAttributeValues.ToList(); + if (!attributeOptions.Any()) + attributeOptions = _productAttributeService.Value.GetProductVariantAttributeValues(attribute.Id).ToList(); - attributeOptions.ForEach(x => _toDelete.AddRange(_localizedEntityService.Value.GetLocalizedProperties(x.Id, "ProductVariantAttributeValue"))); - } - else if (type == typeof(ProductAttribute)) - { - var optionIds = ( - from a in _productAttributeRepository.Value.TableUntracked - from os in a.ProductAttributeOptionsSets - from ao in os.ProductAttributeOptions - where a.Id == entry.Entity.Id - select ao.Id).ToList(); + attributeOptions.ForEach(x => _toDelete.AddRange(_localizedEntityService.Value.GetLocalizedProperties(x.Id, "ProductVariantAttributeValue"))); + } + else if (type == typeof(ProductAttribute)) + { + var optionIds = ( + from a in _productAttributeRepository.Value.TableUntracked + from os in a.ProductAttributeOptionsSets + from ao in os.ProductAttributeOptions + where a.Id == entry.Entity.Id + select ao.Id).ToList(); - optionIds.ForEach(x => _toDelete.AddRange(_localizedEntityService.Value.GetLocalizedProperties(x, "ProductAttributeOption"))); - } - else if (type == typeof(ProductAttributeOptionsSet)) - { - var set = (ProductAttributeOptionsSet)entry.Entity; - var attributeOptions = set.ProductAttributeOptions.ToList(); - if (!attributeOptions.Any()) - attributeOptions = _productAttributeService.Value.GetProductAttributeOptionsByOptionsSetId(set.Id).ToList(); + optionIds.ForEach(x => _toDelete.AddRange(_localizedEntityService.Value.GetLocalizedProperties(x, "ProductAttributeOption"))); + } + else if (type == typeof(ProductAttributeOptionsSet)) + { + var set = (ProductAttributeOptionsSet)entry.Entity; + var attributeOptions = set.ProductAttributeOptions.ToList(); + if (!attributeOptions.Any()) + attributeOptions = _productAttributeService.Value.GetProductAttributeOptionsByOptionsSetId(set.Id).ToList(); - attributeOptions.ForEach(x => _toDelete.AddRange(_localizedEntityService.Value.GetLocalizedProperties(x.Id, "ProductAttributeOption"))); - } - } + attributeOptions.ForEach(x => _toDelete.AddRange(_localizedEntityService.Value.GetLocalizedProperties(x.Id, "ProductAttributeOption"))); + } + } - public override void OnAfterSaveCompleted() - { - if (_toDelete.Count == 0) - return; + public override void OnAfterSave(IHookedEntity entry) + { + // Do not remove, otherwise OnAfterSaveCompleted is not called! + } - using (var scope = new DbContextScope(autoCommit: false)) - { - using (_localizedEntityService.Value.BeginScope()) - { - _toDelete.Each(x => _localizedEntityService.Value.DeleteLocalizedProperty(x)); - } + public override void OnAfterSaveCompleted() + { + if (_toDelete.Count == 0) + return; - scope.Commit(); - _toDelete.Clear(); - } - } - } + using (var scope = new DbContextScope(autoCommit: false)) + { + using (_localizedEntityService.Value.BeginScope()) + { + _toDelete.Each(x => _localizedEntityService.Value.DeleteLocalizedProperty(x)); + } + + scope.Commit(); + _toDelete.Clear(); + } + } + } } diff --git a/src/Libraries/SmartStore.Services/Hooks/PictureHook.cs b/src/Libraries/SmartStore.Services/Hooks/PictureHook.cs deleted file mode 100644 index 81f434a3f5..0000000000 --- a/src/Libraries/SmartStore.Services/Hooks/PictureHook.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using SmartStore.Core; -using SmartStore.Core.Data; -using SmartStore.Core.Data.Hooks; -using SmartStore.Core.Domain.Catalog; -using SmartStore.Services.Media; -using SmartStore.Services.Catalog; - -namespace SmartStore.Services.Hooks -{ - /// - /// Deletes associated pictures of deleted entities - /// - public class PictureHook : DbSaveHook - { - private readonly Lazy _pictureService; - private readonly Lazy _productAttributeService; - - private readonly HashSet _toDelete = new HashSet(); - - private static readonly HashSet _candidateTypes = new HashSet(new Type[] - { - typeof(ProductAttributeOption), - typeof(ProductAttributeOptionsSet), - typeof(ProductAttribute), - typeof(ProductVariantAttribute), - typeof(ProductVariantAttributeValue) - }); - - public PictureHook( - Lazy pictureService, - Lazy productAttributeService) - { - _pictureService = pictureService; - _productAttributeService = productAttributeService; - } - - protected override void OnDeleting(BaseEntity entity, IHookedEntity entry) - { - var type = entry.EntityType; - - if (!_candidateTypes.Contains(type)) - throw new NotSupportedException(); - - if (type == typeof(ProductAttributeOption)) - { - var pictureId = ((ProductAttributeOption)entry.Entity).PictureId; - if (pictureId != 0) - { - _toDelete.Add(pictureId); - } - } - else if (type == typeof(ProductAttributeOptionsSet)) - { - var options = _productAttributeService.Value.GetProductAttributeOptionsByOptionsSetId(entity.Id); - _toDelete.AddRange(options.Where(x => x.PictureId != 0).Select(x => x.PictureId)); - } - else if (type == typeof(ProductAttribute)) - { - var options = _productAttributeService.Value.GetProductAttributeOptionsByAttributeId(entity.Id); - _toDelete.AddRange(options.Where(x => x.PictureId != 0).Select(x => x.PictureId)); - } - else if (type == typeof(ProductVariantAttribute)) - { - var options = _productAttributeService.Value.GetProductVariantAttributeValues(entity.Id); - _toDelete.AddRange(options.Where(x => x.PictureId != 0).Select(x => x.PictureId)); - } - else if (type == typeof(ProductVariantAttributeValue)) - { - var pictureId = ((ProductVariantAttributeValue)entry.Entity).PictureId; - if (pictureId != 0) - { - _toDelete.Add(pictureId); - } - } - } - - public override void OnAfterSaveCompleted() - { - if (_toDelete.Count == 0) - return; - - using (var scope = new DbContextScope(autoCommit: false)) - { - var pictures = _pictureService.Value.GetPicturesByIds(_toDelete.ToArray()); - - pictures.Each(x => _pictureService.Value.DeletePicture(x)); - - scope.Commit(); - _toDelete.Clear(); - } - } - } -} diff --git a/src/Libraries/SmartStore.Services/Hooks/ProductVariantAttributeHook.cs b/src/Libraries/SmartStore.Services/Hooks/ProductVariantAttributeHook.cs index 3a31edcde0..95e22e27ed 100644 --- a/src/Libraries/SmartStore.Services/Hooks/ProductVariantAttributeHook.cs +++ b/src/Libraries/SmartStore.Services/Hooks/ProductVariantAttributeHook.cs @@ -7,33 +7,33 @@ namespace SmartStore.Services.Hooks { - public class ProductVariantAttributeHook : DbSaveHook - { - private readonly Lazy _productAttributeService; - private readonly HashSet _toDelete = new HashSet(); + public class ProductVariantAttributeHook : DbSaveHook + { + private readonly Lazy _productAttributeService; + private readonly HashSet _toDelete = new HashSet(); - public ProductVariantAttributeHook(Lazy productAttributeService) - { - _productAttributeService = productAttributeService; - } + public ProductVariantAttributeHook(Lazy productAttributeService) + { + _productAttributeService = productAttributeService; + } - protected override void OnDeleted(ProductVariantAttribute entity, IHookedEntity entry) - { - _toDelete.Add(entity.Id); - } + protected override void OnDeleted(ProductVariantAttribute entity, IHookedEntity entry) + { + _toDelete.Add(entity.Id); + } - public override void OnAfterSaveCompleted() - { - if (_toDelete.Count == 0) - return; + public override void OnAfterSaveCompleted() + { + if (_toDelete.Count == 0) + return; - using (var scope = new DbContextScope(autoCommit: false)) - { - _toDelete.Each(x => _productAttributeService.Value.DeleteProductBundleItemAttributeFilter(x)); - scope.Commit(); - } + using (var scope = new DbContextScope(autoCommit: false)) + { + _toDelete.Each(x => _productAttributeService.Value.DeleteProductBundleItemAttributeFilter(x)); + scope.Commit(); + } - _toDelete.Clear(); - } - } + _toDelete.Clear(); + } + } } diff --git a/src/Libraries/SmartStore.Services/Hooks/ProductVariantAttributeValueHook.cs b/src/Libraries/SmartStore.Services/Hooks/ProductVariantAttributeValueHook.cs index a1d4d15db5..6d04b04640 100644 --- a/src/Libraries/SmartStore.Services/Hooks/ProductVariantAttributeValueHook.cs +++ b/src/Libraries/SmartStore.Services/Hooks/ProductVariantAttributeValueHook.cs @@ -7,33 +7,33 @@ namespace SmartStore.Services.Hooks { - public class ProductVariantAttributeValueHook : DbSaveHook - { - private readonly Lazy _productAttributeService; - private readonly HashSet _toDelete = new HashSet(); + public class ProductVariantAttributeValueHook : DbSaveHook + { + private readonly Lazy _productAttributeService; + private readonly HashSet _toDelete = new HashSet(); - public ProductVariantAttributeValueHook(Lazy productAttributeService) - { - _productAttributeService = productAttributeService; - } + public ProductVariantAttributeValueHook(Lazy productAttributeService) + { + _productAttributeService = productAttributeService; + } - protected override void OnDeleted(ProductVariantAttributeValue entity, IHookedEntity entry) - { - _toDelete.Add(entity); - } + protected override void OnDeleted(ProductVariantAttributeValue entity, IHookedEntity entry) + { + _toDelete.Add(entity); + } - public override void OnAfterSaveCompleted() - { - if (_toDelete.Count == 0) - return; + public override void OnAfterSaveCompleted() + { + if (_toDelete.Count == 0) + return; - using (var scope = new DbContextScope(autoCommit: false)) - { - _toDelete.Each(x => _productAttributeService.Value.DeleteProductBundleItemAttributeFilter(x.ProductVariantAttributeId, x.Id)); - scope.Commit(); - } + using (var scope = new DbContextScope(autoCommit: false)) + { + _toDelete.Each(x => _productAttributeService.Value.DeleteProductBundleItemAttributeFilter(x.ProductVariantAttributeId, x.Id)); + scope.Commit(); + } - _toDelete.Clear(); - } - } + _toDelete.Clear(); + } + } } diff --git a/src/Libraries/SmartStore.Services/Hooks/SearchQueryAliasHook.cs b/src/Libraries/SmartStore.Services/Hooks/SearchQueryAliasHook.cs index 3efd100e04..143352aea0 100644 --- a/src/Libraries/SmartStore.Services/Hooks/SearchQueryAliasHook.cs +++ b/src/Libraries/SmartStore.Services/Hooks/SearchQueryAliasHook.cs @@ -15,421 +15,421 @@ namespace SmartStore.Services.Hooks { - public class SearchQueryAliasHook : DbSaveHook - { - private readonly IComponentContext _ctx; - private readonly Lazy _catalogSearchQueryAliasMapper; - private readonly Lazy> _localizedPropertyRepository; - - private string _errorMessage; - - private static readonly HashSet _candidateTypes = new HashSet(new Type[] - { - typeof(LocalizedProperty), - typeof(SpecificationAttribute), - typeof(SpecificationAttributeOption), - typeof(ProductSpecificationAttribute), - typeof(ProductAttribute), - typeof(ProductAttributeOption), - typeof(ProductVariantAttribute), - typeof(ProductVariantAttributeValue) - }); - - public SearchQueryAliasHook( - IComponentContext ctx, - Lazy catalogSearchQueryAliasMapper, - Lazy> localizedPropertyRepository) - { - _ctx = ctx; - _catalogSearchQueryAliasMapper = catalogSearchQueryAliasMapper; - _localizedPropertyRepository = localizedPropertyRepository; - - T = NullLocalizer.Instance; - } - - public Localizer T { get; set; } - - #region Utilities - - private bool IsPropertyModified(IHookedEntity entry, string propertyName) - { - var result = false; - - if (entry.State != EntityState.Detached) - { - var prop = entry.Entry.Property(propertyName); - if (prop != null) - { - switch (entry.State) - { - case EntityState.Added: - // OriginalValues cannot be used for entities in the Added state. - result = prop.CurrentValue != null; - break; - case EntityState.Deleted: - // CurrentValues cannot be used for entities in the Deleted state. - result = prop.OriginalValue != null; - break; - default: - result = - (prop.CurrentValue != null && !prop.CurrentValue.Equals(prop.OriginalValue)) || - (prop.OriginalValue != null && !prop.OriginalValue.Equals(prop.CurrentValue)); - break; - } - } - } - - return result; - } - - private void RevertChanges(IHookedEntity entry, string errorMessage) - { - // throw exception in OnBeforeSaveCompleted - _errorMessage = errorMessage; - - // revert changes - if (entry.State == EntityState.Modified) - { - entry.State = EntityState.Unchanged; - } - else if (entry.State == EntityState.Added) - { - entry.State = EntityState.Detached; - } - } - - private bool HasAliasDuplicate( - IHookedEntity entry, - BaseEntity baseEntity, - Func, TEntity, bool> hasDuplicate = null) - where TEntity : BaseEntity - { - if (entry.InitialState == EntityState.Added || entry.InitialState == EntityState.Modified) - { - var entity = baseEntity as ISearchAlias; - if (entity != null) - { - entity.Alias = SeoExtensions.GetSeName(entity.Alias); - if (entity.Alias.HasValue()) - { - var repository = _ctx.Resolve>(); - var allEntities = repository.TableUntracked as IQueryable; - - //if (allEntities != null && allEntities.Any(x => x.Id != entity.Id && x.Alias == entity.Alias)) - if (allEntities != null) - { - var duplicateExists = hasDuplicate == null - ? allEntities.Any(x => x.Id != entity.Id && x.Alias == entity.Alias) - : hasDuplicate(repository.TableUntracked, (TEntity)entity); - - if (duplicateExists) - { - RevertChanges(entry, string.Concat(T("Common.Error.AliasAlreadyExists", entity.Alias), " ", T("Common.Error.ChooseDifferentValue"))); - return true; - } - } - } - } - } - - return false; - } - - private bool HasAliasDuplicate(IHookedEntity entry, BaseEntity baseEntity) where T1 : BaseEntity where T2 : BaseEntity - { - if (entry.InitialState == EntityState.Added || entry.InitialState == EntityState.Modified) - { - var entity = baseEntity as ISearchAlias; - if (entity != null) - { - entity.Alias = SeoExtensions.GetSeName(entity.Alias); - if (entity.Alias.HasValue()) - { - var entities1 = _ctx.Resolve>().TableUntracked as IQueryable; - var entities2 = _ctx.Resolve>().TableUntracked as IQueryable; - - var duplicate1 = entities1.FirstOrDefault(x => x.Alias == entity.Alias); - var duplicate2 = entities2.FirstOrDefault(x => x.Alias == entity.Alias); - - if (duplicate1 != null || duplicate2 != null) - { - var type = entry.EntityType; - - if (duplicate1 != null && duplicate1.Id == entity.Id && type == typeof(T1)) - return false; - if (duplicate2 != null && duplicate2.Id == entity.Id && type == typeof(T2)) - return false; - - RevertChanges(entry, string.Concat(T("Common.Error.AliasAlreadyExists", entity.Alias), " ", T("Common.Error.ChooseDifferentValue"))); - return true; - } - } - } - } - - return false; - } - - private bool HasAliasDuplicate(LocalizedProperty property) - { - var existingProperties = _localizedPropertyRepository.Value.Table.Where(x => - x.Id != property.Id && - x.LocaleKey == "Alias" && - x.LocaleKeyGroup == property.LocaleKeyGroup && - x.LanguageId == property.LanguageId && - x.LocaleValue == property.LocaleValue).ToList(); - - if (existingProperties.Count == 0) - { - // Check cases where alias has to be globally unique. - string otherKeyGroup = null; - - if (property.LocaleKeyGroup.IsCaseInsensitiveEqual("SpecificationAttribute")) - otherKeyGroup = "ProductAttribute"; - else if (property.LocaleKeyGroup.IsCaseInsensitiveEqual("ProductAttribute")) - otherKeyGroup = "SpecificationAttribute"; - - if (otherKeyGroup.HasValue()) - { - existingProperties = _localizedPropertyRepository.Value.Table.Where(x => - x.LocaleKey == "Alias" && - x.LocaleKeyGroup == otherKeyGroup && - x.LanguageId == property.LanguageId && - x.LocaleValue == property.LocaleValue).ToList(); - } - - if (existingProperties.Count == 0) - return false; - } - - var toDelete = new List(); - - foreach (var prop in existingProperties) - { - // Check if the related entity exists. The user would not be able to solve an invalidated alias when the related entity does not exist anymore. - var relatedEntityExists = true; - - if (prop.LocaleKeyGroup.IsCaseInsensitiveEqual("SpecificationAttribute")) - { - relatedEntityExists = _ctx.Resolve>().GetById(prop.EntityId) != null; - } - else if (prop.LocaleKeyGroup.IsCaseInsensitiveEqual("SpecificationAttributeOption")) - { - relatedEntityExists = _ctx.Resolve>().GetById(prop.EntityId) != null; - } - else if (prop.LocaleKeyGroup.IsCaseInsensitiveEqual("ProductAttribute")) - { - relatedEntityExists = _ctx.Resolve>().GetById(prop.EntityId) != null; - } - else if (prop.LocaleKeyGroup.IsCaseInsensitiveEqual("ProductAttributeOption")) - { - relatedEntityExists = _ctx.Resolve>().GetById(prop.EntityId) != null; - } - //else if (prop.LocaleKeyGroup.IsCaseInsensitiveEqual("ProductVariantAttributeValue")) - //{ - // relatedEntityExists = _ctx.Resolve>().GetById(prop.EntityId) != null; - //} - - if (relatedEntityExists) - { - // We cannot delete any localized property because we are going to throw duplicate alias exception in OnBeforeSaveCompleted. - return true; - } - else - { - // Delete accidentally dead localized properties. - toDelete.Add(prop); - } - } - - if (toDelete.Any()) - { - try - { - _localizedPropertyRepository.Value.DeleteRange(toDelete); - } - catch (Exception exception) - { - exception.Dump(); - } - } - - return false; - } - - private bool HasEntityDuplicate( - IHookedEntity entry, - BaseEntity baseEntity, - Func getName, - Expression> getDuplicate) where TEntity : BaseEntity - { - if (entry.InitialState == EntityState.Added || entry.InitialState == EntityState.Modified) - { - var repository = _ctx.Resolve>(); - var allEntities = repository.TableUntracked as IQueryable; - - var existingEntity = allEntities.FirstOrDefault(getDuplicate); - if (existingEntity != null && existingEntity.Id != baseEntity.Id) - { - RevertChanges(entry, string.Concat(T("Common.Error.OptionAlreadyExists", getName(existingEntity).NaIfEmpty()), " ", T("Common.Error.ChooseDifferentValue"))); - return true; - } - } - - return false; - } - - #endregion - - protected override void OnDeleting(BaseEntity entity, IHookedEntity entry) - { - HookObject(entity, entry); - } - - protected override void OnInserting(BaseEntity entity, IHookedEntity entry) - { - HookObject(entity, entry); - } - - protected override void OnUpdating(BaseEntity entity, IHookedEntity entry) - { - HookObject(entity, entry); - } - - public override void OnBeforeSaveCompleted() - { - if (_errorMessage.HasValue()) - { - var message = string.Copy(_errorMessage); - _errorMessage = null; - - throw new SmartException(message); - } - } - - private void HookObject(BaseEntity baseEntity, IHookedEntity entry) - { - var type = entry.EntityType; - - if (!_candidateTypes.Contains(type)) - throw new NotSupportedException(); - - if (type == typeof(SpecificationAttribute)) - { - if (HasAliasDuplicate(entry, baseEntity)) - return; - - if (IsPropertyModified(entry, "Alias")) - _catalogSearchQueryAliasMapper.Value.ClearAttributeCache(); - } - else if (type == typeof(SpecificationAttributeOption)) - { - var entity = (SpecificationAttributeOption)baseEntity; - - if (HasEntityDuplicate(entry, baseEntity, x => x.Name, - x => x.SpecificationAttributeId == entity.SpecificationAttributeId && x.Name == entity.Name)) - return; - - if (HasAliasDuplicate(entry, baseEntity)) - return; - - if (IsPropertyModified(entry, "Alias")) - _catalogSearchQueryAliasMapper.Value.ClearAttributeCache(); - } - else if (type == typeof(ProductSpecificationAttribute)) - { - var entity = (ProductSpecificationAttribute)baseEntity; - - if (HasEntityDuplicate(entry, baseEntity, x => x.SpecificationAttributeOption?.Name, - x => x.ProductId == entity.ProductId && x.SpecificationAttributeOptionId == entity.SpecificationAttributeOptionId)) - return; - } - else if (type == typeof(ProductAttribute)) - { - if (HasAliasDuplicate(entry, baseEntity)) - return; - - if (IsPropertyModified(entry, "Alias")) - _catalogSearchQueryAliasMapper.Value.ClearVariantCache(); - } - else if (type == typeof(ProductAttributeOption)) - { - var entity = (ProductAttributeOption)baseEntity; - - if (HasEntityDuplicate(entry, baseEntity, x => x.Name, - x => x.ProductAttributeOptionsSetId == entity.ProductAttributeOptionsSetId && x.Name == entity.Name)) - return; - - // ClearVariantCache() not necessary - if (HasAliasDuplicate(entry, baseEntity)) - return; - } - else if (type == typeof(ProductVariantAttribute)) - { - var entity = (ProductVariantAttribute)baseEntity; - - if (HasEntityDuplicate(entry, baseEntity, x => x.ProductAttribute?.Name, - x => x.ProductId == entity.ProductId && x.ProductAttributeId == entity.ProductAttributeId)) - return; - } - else if (type == typeof(ProductVariantAttributeValue)) - { - var entity = (ProductVariantAttributeValue)baseEntity; - - if (HasEntityDuplicate(entry, baseEntity, x => x.Name, - x => x.ProductVariantAttributeId == entity.ProductVariantAttributeId && x.Name == entity.Name)) - return; - - if (HasAliasDuplicate(entry, baseEntity, - (all, e) => all.Any(x => x.Id != e.Id && x.ProductVariantAttributeId == e.ProductVariantAttributeId && x.Alias == e.Alias))) - return; - - if (IsPropertyModified(entry, "Alias")) - _catalogSearchQueryAliasMapper.Value.ClearVariantCache(); - } - else if (type == typeof(LocalizedProperty)) - { - // note: not fired when SpecificationAttribute or SpecificationAttributeOption deleted. - // not necessary anyway because cache cleared by above code. - var prop = (LocalizedProperty)baseEntity; - var keyGroup = prop.LocaleKeyGroup; - - if (!prop.LocaleKey.IsCaseInsensitiveEqual("Alias")) - return; - - // validating ProductVariantAttributeValue goes too far here. - if (!keyGroup.IsCaseInsensitiveEqual("SpecificationAttribute") && - !keyGroup.IsCaseInsensitiveEqual("SpecificationAttributeOption") && - !keyGroup.IsCaseInsensitiveEqual("ProductAttribute") && - !keyGroup.IsCaseInsensitiveEqual("ProductAttributeOption")) - { - return; - } - - // check alias duplicate - if (entry.InitialState == EntityState.Added || entry.InitialState == EntityState.Modified) - { - prop.LocaleValue = SeoExtensions.GetSeName(prop.LocaleValue); - if (prop.LocaleValue.HasValue() && HasAliasDuplicate(prop)) - { - RevertChanges(entry, string.Concat(T("Common.Error.AliasAlreadyExists", prop.LocaleValue), " ", T("Common.Error.ChooseDifferentValue"))); - return; - } - } - - if (IsPropertyModified(entry, "LocaleValue")) - { - if (keyGroup.IsCaseInsensitiveEqual("SpecificationAttribute") || keyGroup.IsCaseInsensitiveEqual("SpecificationAttributeOption")) - { - _catalogSearchQueryAliasMapper.Value.ClearAttributeCache(); - } - else if (keyGroup.IsCaseInsensitiveEqual("ProductAttribute") || keyGroup.IsCaseInsensitiveEqual("ProductVariantAttributeValue")) - { - // not necessary for ProductAttributeOption - _catalogSearchQueryAliasMapper.Value.ClearVariantCache(); - } - } - } - } - } + public class SearchQueryAliasHook : DbSaveHook + { + private readonly IComponentContext _ctx; + private readonly Lazy _catalogSearchQueryAliasMapper; + private readonly Lazy> _localizedPropertyRepository; + + private string _errorMessage; + + private static readonly HashSet _candidateTypes = new HashSet(new Type[] + { + typeof(LocalizedProperty), + typeof(SpecificationAttribute), + typeof(SpecificationAttributeOption), + typeof(ProductSpecificationAttribute), + typeof(ProductAttribute), + typeof(ProductAttributeOption), + typeof(ProductVariantAttribute), + typeof(ProductVariantAttributeValue) + }); + + public SearchQueryAliasHook( + IComponentContext ctx, + Lazy catalogSearchQueryAliasMapper, + Lazy> localizedPropertyRepository) + { + _ctx = ctx; + _catalogSearchQueryAliasMapper = catalogSearchQueryAliasMapper; + _localizedPropertyRepository = localizedPropertyRepository; + + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + #region Utilities + + private bool IsPropertyModified(IHookedEntity entry, string propertyName) + { + var result = false; + + if (entry.State != EntityState.Detached) + { + var prop = entry.Entry.Property(propertyName); + if (prop != null) + { + switch (entry.State) + { + case EntityState.Added: + // OriginalValues cannot be used for entities in the Added state. + result = prop.CurrentValue != null; + break; + case EntityState.Deleted: + // CurrentValues cannot be used for entities in the Deleted state. + result = prop.OriginalValue != null; + break; + default: + result = + (prop.CurrentValue != null && !prop.CurrentValue.Equals(prop.OriginalValue)) || + (prop.OriginalValue != null && !prop.OriginalValue.Equals(prop.CurrentValue)); + break; + } + } + } + + return result; + } + + private void RevertChanges(IHookedEntity entry, string errorMessage) + { + // throw exception in OnBeforeSaveCompleted + _errorMessage = errorMessage; + + // revert changes + if (entry.State == EntityState.Modified) + { + entry.State = EntityState.Unchanged; + } + else if (entry.State == EntityState.Added) + { + entry.State = EntityState.Detached; + } + } + + private bool HasAliasDuplicate( + IHookedEntity entry, + BaseEntity baseEntity, + Func, TEntity, bool> hasDuplicate = null) + where TEntity : BaseEntity + { + if (entry.InitialState == EntityState.Added || entry.InitialState == EntityState.Modified) + { + var entity = baseEntity as ISearchAlias; + if (entity != null) + { + entity.Alias = SeoExtensions.GetSeName(entity.Alias); + if (entity.Alias.HasValue()) + { + var repository = _ctx.Resolve>(); + var allEntities = repository.TableUntracked as IQueryable; + + //if (allEntities != null && allEntities.Any(x => x.Id != entity.Id && x.Alias == entity.Alias)) + if (allEntities != null) + { + var duplicateExists = hasDuplicate == null + ? allEntities.Any(x => x.Id != entity.Id && x.Alias == entity.Alias) + : hasDuplicate(repository.TableUntracked, (TEntity)entity); + + if (duplicateExists) + { + RevertChanges(entry, string.Concat(T("Common.Error.AliasAlreadyExists", entity.Alias), " ", T("Common.Error.ChooseDifferentValue"))); + return true; + } + } + } + } + } + + return false; + } + + private bool HasAliasDuplicate(IHookedEntity entry, BaseEntity baseEntity) where T1 : BaseEntity where T2 : BaseEntity + { + if (entry.InitialState == EntityState.Added || entry.InitialState == EntityState.Modified) + { + var entity = baseEntity as ISearchAlias; + if (entity != null) + { + entity.Alias = SeoExtensions.GetSeName(entity.Alias); + if (entity.Alias.HasValue()) + { + var entities1 = _ctx.Resolve>().TableUntracked as IQueryable; + var entities2 = _ctx.Resolve>().TableUntracked as IQueryable; + + var duplicate1 = entities1.FirstOrDefault(x => x.Alias == entity.Alias); + var duplicate2 = entities2.FirstOrDefault(x => x.Alias == entity.Alias); + + if (duplicate1 != null || duplicate2 != null) + { + var type = entry.EntityType; + + if (duplicate1 != null && duplicate1.Id == entity.Id && type == typeof(T1)) + return false; + if (duplicate2 != null && duplicate2.Id == entity.Id && type == typeof(T2)) + return false; + + RevertChanges(entry, string.Concat(T("Common.Error.AliasAlreadyExists", entity.Alias), " ", T("Common.Error.ChooseDifferentValue"))); + return true; + } + } + } + } + + return false; + } + + private bool HasAliasDuplicate(LocalizedProperty property) + { + var existingProperties = _localizedPropertyRepository.Value.Table.Where(x => + x.Id != property.Id && + x.LocaleKey == "Alias" && + x.LocaleKeyGroup == property.LocaleKeyGroup && + x.LanguageId == property.LanguageId && + x.LocaleValue == property.LocaleValue).ToList(); + + if (existingProperties.Count == 0) + { + // Check cases where alias has to be globally unique. + string otherKeyGroup = null; + + if (property.LocaleKeyGroup.IsCaseInsensitiveEqual("SpecificationAttribute")) + otherKeyGroup = "ProductAttribute"; + else if (property.LocaleKeyGroup.IsCaseInsensitiveEqual("ProductAttribute")) + otherKeyGroup = "SpecificationAttribute"; + + if (otherKeyGroup.HasValue()) + { + existingProperties = _localizedPropertyRepository.Value.Table.Where(x => + x.LocaleKey == "Alias" && + x.LocaleKeyGroup == otherKeyGroup && + x.LanguageId == property.LanguageId && + x.LocaleValue == property.LocaleValue).ToList(); + } + + if (existingProperties.Count == 0) + return false; + } + + var toDelete = new List(); + + foreach (var prop in existingProperties) + { + // Check if the related entity exists. The user would not be able to solve an invalidated alias when the related entity does not exist anymore. + var relatedEntityExists = true; + + if (prop.LocaleKeyGroup.IsCaseInsensitiveEqual("SpecificationAttribute")) + { + relatedEntityExists = _ctx.Resolve>().GetById(prop.EntityId) != null; + } + else if (prop.LocaleKeyGroup.IsCaseInsensitiveEqual("SpecificationAttributeOption")) + { + relatedEntityExists = _ctx.Resolve>().GetById(prop.EntityId) != null; + } + else if (prop.LocaleKeyGroup.IsCaseInsensitiveEqual("ProductAttribute")) + { + relatedEntityExists = _ctx.Resolve>().GetById(prop.EntityId) != null; + } + else if (prop.LocaleKeyGroup.IsCaseInsensitiveEqual("ProductAttributeOption")) + { + relatedEntityExists = _ctx.Resolve>().GetById(prop.EntityId) != null; + } + //else if (prop.LocaleKeyGroup.IsCaseInsensitiveEqual("ProductVariantAttributeValue")) + //{ + // relatedEntityExists = _ctx.Resolve>().GetById(prop.EntityId) != null; + //} + + if (relatedEntityExists) + { + // We cannot delete any localized property because we are going to throw duplicate alias exception in OnBeforeSaveCompleted. + return true; + } + else + { + // Delete accidentally dead localized properties. + toDelete.Add(prop); + } + } + + if (toDelete.Any()) + { + try + { + _localizedPropertyRepository.Value.DeleteRange(toDelete); + } + catch (Exception exception) + { + exception.Dump(); + } + } + + return false; + } + + private bool HasEntityDuplicate( + IHookedEntity entry, + BaseEntity baseEntity, + Func getName, + Expression> getDuplicate) where TEntity : BaseEntity + { + if (entry.InitialState == EntityState.Added || entry.InitialState == EntityState.Modified) + { + var repository = _ctx.Resolve>(); + var allEntities = repository.TableUntracked as IQueryable; + + var existingEntity = allEntities.FirstOrDefault(getDuplicate); + if (existingEntity != null && existingEntity.Id != baseEntity.Id) + { + RevertChanges(entry, string.Concat(T("Common.Error.OptionAlreadyExists", getName(existingEntity).NaIfEmpty()), " ", T("Common.Error.ChooseDifferentValue"))); + return true; + } + } + + return false; + } + + #endregion + + protected override void OnDeleting(BaseEntity entity, IHookedEntity entry) + { + HookObject(entity, entry); + } + + protected override void OnInserting(BaseEntity entity, IHookedEntity entry) + { + HookObject(entity, entry); + } + + protected override void OnUpdating(BaseEntity entity, IHookedEntity entry) + { + HookObject(entity, entry); + } + + public override void OnBeforeSaveCompleted() + { + if (_errorMessage.HasValue()) + { + var message = string.Copy(_errorMessage); + _errorMessage = null; + + throw new SmartException(message); + } + } + + private void HookObject(BaseEntity baseEntity, IHookedEntity entry) + { + var type = entry.EntityType; + + if (!_candidateTypes.Contains(type)) + throw new NotSupportedException(); + + if (type == typeof(SpecificationAttribute)) + { + if (HasAliasDuplicate(entry, baseEntity)) + return; + + if (IsPropertyModified(entry, "Alias")) + _catalogSearchQueryAliasMapper.Value.ClearAttributeCache(); + } + else if (type == typeof(SpecificationAttributeOption)) + { + var entity = (SpecificationAttributeOption)baseEntity; + + if (HasEntityDuplicate(entry, baseEntity, x => x.Name, + x => x.SpecificationAttributeId == entity.SpecificationAttributeId && x.Name == entity.Name)) + return; + + if (HasAliasDuplicate(entry, baseEntity)) + return; + + if (IsPropertyModified(entry, "Alias")) + _catalogSearchQueryAliasMapper.Value.ClearAttributeCache(); + } + else if (type == typeof(ProductSpecificationAttribute)) + { + var entity = (ProductSpecificationAttribute)baseEntity; + + if (HasEntityDuplicate(entry, baseEntity, x => x.SpecificationAttributeOption?.Name, + x => x.ProductId == entity.ProductId && x.SpecificationAttributeOptionId == entity.SpecificationAttributeOptionId)) + return; + } + else if (type == typeof(ProductAttribute)) + { + if (HasAliasDuplicate(entry, baseEntity)) + return; + + if (IsPropertyModified(entry, "Alias")) + _catalogSearchQueryAliasMapper.Value.ClearVariantCache(); + } + else if (type == typeof(ProductAttributeOption)) + { + var entity = (ProductAttributeOption)baseEntity; + + if (HasEntityDuplicate(entry, baseEntity, x => x.Name, + x => x.ProductAttributeOptionsSetId == entity.ProductAttributeOptionsSetId && x.Name == entity.Name)) + return; + + // ClearVariantCache() not necessary + if (HasAliasDuplicate(entry, baseEntity)) + return; + } + else if (type == typeof(ProductVariantAttribute)) + { + var entity = (ProductVariantAttribute)baseEntity; + + if (HasEntityDuplicate(entry, baseEntity, x => x.ProductAttribute?.Name, + x => x.ProductId == entity.ProductId && x.ProductAttributeId == entity.ProductAttributeId)) + return; + } + else if (type == typeof(ProductVariantAttributeValue)) + { + var entity = (ProductVariantAttributeValue)baseEntity; + + if (HasEntityDuplicate(entry, baseEntity, x => x.Name, + x => x.ProductVariantAttributeId == entity.ProductVariantAttributeId && x.Name == entity.Name)) + return; + + if (HasAliasDuplicate(entry, baseEntity, + (all, e) => all.Any(x => x.Id != e.Id && x.ProductVariantAttributeId == e.ProductVariantAttributeId && x.Alias == e.Alias))) + return; + + if (IsPropertyModified(entry, "Alias")) + _catalogSearchQueryAliasMapper.Value.ClearVariantCache(); + } + else if (type == typeof(LocalizedProperty)) + { + // note: not fired when SpecificationAttribute or SpecificationAttributeOption deleted. + // not necessary anyway because cache cleared by above code. + var prop = (LocalizedProperty)baseEntity; + var keyGroup = prop.LocaleKeyGroup; + + if (!prop.LocaleKey.IsCaseInsensitiveEqual("Alias")) + return; + + // validating ProductVariantAttributeValue goes too far here. + if (!keyGroup.IsCaseInsensitiveEqual("SpecificationAttribute") && + !keyGroup.IsCaseInsensitiveEqual("SpecificationAttributeOption") && + !keyGroup.IsCaseInsensitiveEqual("ProductAttribute") && + !keyGroup.IsCaseInsensitiveEqual("ProductAttributeOption")) + { + return; + } + + // check alias duplicate + if (entry.InitialState == EntityState.Added || entry.InitialState == EntityState.Modified) + { + prop.LocaleValue = SeoExtensions.GetSeName(prop.LocaleValue); + if (prop.LocaleValue.HasValue() && HasAliasDuplicate(prop)) + { + RevertChanges(entry, string.Concat(T("Common.Error.AliasAlreadyExists", prop.LocaleValue), " ", T("Common.Error.ChooseDifferentValue"))); + return; + } + } + + if (IsPropertyModified(entry, "LocaleValue")) + { + if (keyGroup.IsCaseInsensitiveEqual("SpecificationAttribute") || keyGroup.IsCaseInsensitiveEqual("SpecificationAttributeOption")) + { + _catalogSearchQueryAliasMapper.Value.ClearAttributeCache(); + } + else if (keyGroup.IsCaseInsensitiveEqual("ProductAttribute") || keyGroup.IsCaseInsensitiveEqual("ProductVariantAttributeValue")) + { + // not necessary for ProductAttributeOption + _catalogSearchQueryAliasMapper.Value.ClearVariantCache(); + } + } + } + } + } } diff --git a/src/Libraries/SmartStore.Services/Hooks/SettingSaveHook.cs b/src/Libraries/SmartStore.Services/Hooks/SettingSaveHook.cs new file mode 100644 index 0000000000..8099c8c115 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Hooks/SettingSaveHook.cs @@ -0,0 +1,24 @@ +using SmartStore.Core.Data.Hooks; +using SmartStore.Core.Domain.Configuration; +using SmartStore.Core.Logging; +using SmartStore.Services.Localization; + +namespace SmartStore.Services.Hooks +{ + public class SettingSaveHook : DbSaveHook + { + private readonly ICustomerActivityService _customerActivityService; + private readonly ILocalizationService _localizationService; + + public SettingSaveHook(ICustomerActivityService customerActivityService, ILocalizationService localizationService) + { + _customerActivityService = customerActivityService; + _localizationService = localizationService; + } + + protected override void OnUpdated(Setting entity, IHookedEntity entry) + { + _customerActivityService.InsertActivity("EditSettings", _localizationService.GetResource("ActivityLog.EditSettings"), entity.Name, entity.Value); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Hooks/SlugSupportedHook.cs b/src/Libraries/SmartStore.Services/Hooks/SlugSupportedHook.cs index e744e41f2d..d8591ba072 100644 --- a/src/Libraries/SmartStore.Services/Hooks/SlugSupportedHook.cs +++ b/src/Libraries/SmartStore.Services/Hooks/SlugSupportedHook.cs @@ -3,42 +3,43 @@ using SmartStore.Core.Data; using SmartStore.Core.Data.Hooks; using SmartStore.Core.Domain.Seo; +using SmartStore.Data; using SmartStore.Services.Seo; namespace SmartStore.Services.Common { - public class SlugSupportedHook : DbSaveHook - { - private readonly Lazy _urlRecordService; - private readonly HashSet _toDelete = new HashSet(); + public class SlugSupportedHook : DbSaveHook + { + private readonly Lazy _urlRecordService; + private readonly HashSet _toDelete = new HashSet(); - public SlugSupportedHook(Lazy urlRecordService) - { - _urlRecordService = urlRecordService; - } + public SlugSupportedHook(Lazy urlRecordService) + { + _urlRecordService = urlRecordService; + } - protected override void OnDeleted(ISlugSupported entity, IHookedEntity entry) - { - var entityType = entry.EntityType; - var records = _urlRecordService.Value.GetUrlRecordsFor(entityType.Name, entry.Entity.Id); - _toDelete.AddRange(records); - } + protected override void OnDeleted(ISlugSupported entity, IHookedEntity entry) + { + var entityType = entry.EntityType; + var records = _urlRecordService.Value.GetUrlRecordsFor(entityType.Name, entry.Entity.Id); + _toDelete.AddRange(records); + } - public override void OnAfterSaveCompleted() - { - if (_toDelete.Count == 0) - return; + public override void OnAfterSaveCompleted() + { + if (_toDelete.Count == 0) + return; - using (var scope = new DbContextScope(autoCommit: false)) - { - using (_urlRecordService.Value.BeginScope()) - { - _toDelete.Each(x => _urlRecordService.Value.DeleteUrlRecord(x)); - } + using (var scope = new DbContextScope(autoCommit: false)) + { + using (_urlRecordService.Value.BeginScope()) + { + _toDelete.Each(x => _urlRecordService.Value.DeleteUrlRecord(x)); + } - scope.Commit(); - _toDelete.Clear(); - } - } - } + scope.Commit(); + _toDelete.Clear(); + } + } + } } diff --git a/src/Libraries/SmartStore.Services/Hooks/SoftDeletableHook.cs b/src/Libraries/SmartStore.Services/Hooks/SoftDeletableHook.cs index a7fed44987..0bd7d36ded 100644 --- a/src/Libraries/SmartStore.Services/Hooks/SoftDeletableHook.cs +++ b/src/Libraries/SmartStore.Services/Hooks/SoftDeletableHook.cs @@ -1,4 +1,6 @@ -using Autofac; +using System; +using System.Linq; +using Autofac; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Data.Hooks; @@ -7,65 +9,108 @@ using SmartStore.Services.Security; using SmartStore.Services.Seo; using SmartStore.Data; +using SmartStore.Core.Domain.Catalog; +using System.Collections.Generic; +using SmartStore.Services.Catalog; +using SmartStore.Services.Media; +using SmartStore.Core.Domain.Media; namespace SmartStore.Services.Hooks { - public class SoftDeletablePreUpdateHook : DbSaveHook - { - private readonly IComponentContext _ctx; - - public SoftDeletablePreUpdateHook(IComponentContext ctx) - { - _ctx = ctx; - } - - protected override void OnUpdating(ISoftDeletable entity, IHookedEntity entry) - { - var baseEntity = entry.Entity; - - var prop = entry.Entry.Property("Deleted"); - var deletedModified = !prop.CurrentValue.Equals(prop.OriginalValue); - if (!deletedModified) - return; - - var entityType = entry.EntityType; - - // mark orphaned ACL records as idle - var aclSupported = baseEntity as IAclSupported; - if (aclSupported != null && aclSupported.SubjectToAcl) - { - var shouldSetIdle = entity.Deleted; - var rsAclRecord = _ctx.Resolve>(); - - var aclService = _ctx.Resolve(); - var records = aclService.GetAclRecordsFor(entityType.Name, baseEntity.Id); - foreach (var record in records) - { - record.IsIdle = shouldSetIdle; - aclService.UpdateAclRecord(record); - } - } - - // Delete orphaned inactive UrlRecords. - // We keep the active ones on purpose in order to be able to fully restore a soft deletable entity once we implemented the "recycle bin" feature - var slugSupported = baseEntity as ISlugSupported; - if (slugSupported != null && entity.Deleted) - { - var rsUrlRecord = _ctx.Resolve>(); - - var urlRecordService = _ctx.Resolve(); - var activeRecords = urlRecordService.GetUrlRecordsFor(entityType.Name, baseEntity.Id); - using (urlRecordService.BeginScope()) - { - foreach (var record in activeRecords) - { - if (!record.IsActive) - { - urlRecordService.DeleteUrlRecord(record); - } - } - } - } - } - } + public class SoftDeletablePreUpdateHook : DbSaveHook + { + private readonly IComponentContext _ctx; + + public SoftDeletablePreUpdateHook(IComponentContext ctx) + { + _ctx = ctx; + } + + protected override void OnUpdating(ISoftDeletable entity, IHookedEntity entry) + { + var baseEntity = entry.Entity; + + var prop = entry.Entry.Property(nameof(ISoftDeletable.Deleted)); + var deletedModified = !prop.CurrentValue.Equals(prop.OriginalValue); + if (!deletedModified) + return; + + var entityType = entry.EntityType; + + // Mark orphaned ACL records as idle + if (baseEntity is IAclSupported aclSupported && aclSupported.SubjectToAcl) + { + var shouldSetIdle = entity.Deleted; + var aclService = _ctx.Resolve(); + var records = aclService.GetAclRecordsFor(entityType.Name, baseEntity.Id); + foreach (var record in records) + { + record.IsIdle = shouldSetIdle; + aclService.UpdateAclRecord(record); + } + } + + if (!entity.Deleted) + return; // Nothing to process from here on. + + // Delete orphaned inactive UrlRecords. + // We keep the active ones on purpose in order to be able to fully restore a soft deletable entity once we implemented the "recycle bin" feature + if (baseEntity is ISlugSupported) + { + var urlRecordService = _ctx.Resolve(); + var activeRecords = urlRecordService.GetUrlRecordsFor(entityType.Name, baseEntity.Id); + using (urlRecordService.BeginScope()) + { + foreach (var record in activeRecords) + { + if (!record.IsActive) + { + urlRecordService.DeleteUrlRecord(record); + } + } + } + } + + // Unassign any media file and let the media tracker reliably clean up orphaned files later. + // TODO: Once we implement "recycle bin" feature for catalog entities, don't unassign anymore. + if (baseEntity is Product product) + { + product.MainPictureId = null; + + var productService = _ctx.Resolve(); + var mediaTracker = _ctx.Resolve(); + var tracks = new List(); + + foreach (var pm in product.ProductPictures.ToList()) + { + productService.DeleteProductPicture(pm); + + tracks.Add(new MediaTrack + { + EntityId = pm.Id, + EntityName = nameof(ProductMediaFile), + MediaFileId = pm.MediaFileId, + Property = nameof(pm.MediaFileId), + Operation = MediaTrackOperation.Untrack + }); + } + + // We have to untrack manually, because the tracker hook will not run in the scope of another hook. + mediaTracker.TrackMany(SystemAlbumProvider.Catalog, tracks); + } + + // Although nested hooks do not run, the following stuff will still trigger the tracker hook, + // because this hook runs before the tracker hook. + + if (baseEntity is Category category) + { + category.MediaFileId = null; + } + + if (baseEntity is Manufacturer manufacturer) + { + manufacturer.MediaFileId = null; + } + } + } } diff --git a/src/Libraries/SmartStore.Services/Hooks/StoreMappingEntityHook.cs b/src/Libraries/SmartStore.Services/Hooks/StoreMappingEntityHook.cs index 422ba4c281..7bb0e65992 100644 --- a/src/Libraries/SmartStore.Services/Hooks/StoreMappingEntityHook.cs +++ b/src/Libraries/SmartStore.Services/Hooks/StoreMappingEntityHook.cs @@ -4,43 +4,44 @@ using SmartStore.Core.Data; using SmartStore.Core.Data.Hooks; using SmartStore.Core.Domain.Stores; +using SmartStore.Data; using SmartStore.Services.Stores; namespace SmartStore.Services.Common { - public class StoreMappingEntityHook : DbSaveHook - { - private readonly Lazy _storeMappingService; - private readonly HashSet _toDelete = new HashSet(); - - public StoreMappingEntityHook(Lazy storeMappingService) - { - _storeMappingService = storeMappingService; - } - - protected override void OnDeleted(IStoreMappingSupported entity, IHookedEntity entry) - { - var entityType = entry.EntityType; - - var records = _storeMappingService.Value - .GetStoreMappingsFor(entityType.Name, entry.Entity.Id) - .ToList(); - - _toDelete.AddRange(records); - } - - public override void OnAfterSaveCompleted() - { - if (_toDelete.Count == 0) - return; - - using (var scope = new DbContextScope(autoCommit: false)) - { - _toDelete.Each(x => _storeMappingService.Value.DeleteStoreMapping(x)); - scope.Commit(); - } - - _toDelete.Clear(); - } - } + public class StoreMappingEntityHook : DbSaveHook + { + private readonly Lazy _storeMappingService; + private readonly HashSet _toDelete = new HashSet(); + + public StoreMappingEntityHook(Lazy storeMappingService) + { + _storeMappingService = storeMappingService; + } + + protected override void OnDeleted(IStoreMappingSupported entity, IHookedEntity entry) + { + var entityType = entry.EntityType; + + var records = _storeMappingService.Value + .GetStoreMappingsFor(entityType.Name, entry.Entity.Id) + .ToList(); + + _toDelete.AddRange(records); + } + + public override void OnAfterSaveCompleted() + { + if (_toDelete.Count == 0) + return; + + using (var scope = new DbContextScope(autoCommit: false)) + { + _toDelete.Each(x => _storeMappingService.Value.DeleteStoreMapping(x)); + scope.Commit(); + } + + _toDelete.Clear(); + } + } } diff --git a/src/Libraries/SmartStore.Services/Hooks/StoreSaveHook.cs b/src/Libraries/SmartStore.Services/Hooks/StoreSaveHook.cs index 12b02a4cc1..dafd38a099 100644 --- a/src/Libraries/SmartStore.Services/Hooks/StoreSaveHook.cs +++ b/src/Libraries/SmartStore.Services/Hooks/StoreSaveHook.cs @@ -1,59 +1,50 @@ -using System; -using SmartStore.Services.Media; +using System.Web; using SmartStore.Core.Data.Hooks; using SmartStore.Core.Domain.Stores; -using SmartStore.Services.Tasks; using SmartStore.Services.Stores; -using System.Web; +using SmartStore.Services.Tasks; using SmartStore.Utilities; namespace SmartStore.Services.Hooks { - public class StoreSaveHook : DbSaveHook - { - private readonly IPictureService _pictureService; - private readonly ITaskScheduler _taskScheduler; - private readonly IStoreService _storeService; - private readonly HttpContextBase _httpContext; + public class StoreSaveHook : DbSaveHook + { + private readonly ITaskScheduler _taskScheduler; + private readonly IStoreService _storeService; + private readonly HttpContextBase _httpContext; - public StoreSaveHook(IPictureService pictureService, ITaskScheduler taskScheduler, IStoreService storeService, HttpContextBase httpContext) - { - _pictureService = pictureService; - _taskScheduler = taskScheduler; - _storeService = storeService; - _httpContext = httpContext; - } + public StoreSaveHook(ITaskScheduler taskScheduler, IStoreService storeService, HttpContextBase httpContext) + { + _taskScheduler = taskScheduler; + _storeService = storeService; + _httpContext = httpContext; + } - protected override void OnUpdating(Store entity, IHookedEntity entry) - { - if (entry.IsPropertyModified(nameof(entity.ContentDeliveryNetwork))) - { - _pictureService.ClearUrlCache(); - } - } + protected override void OnUpdating(Store entity, IHookedEntity entry) + { + } - protected override void OnInserted(Store entity, IHookedEntity entry) - { - TryChangeSchedulerBaseUrl(); - } + protected override void OnInserted(Store entity, IHookedEntity entry) + { + TryChangeSchedulerBaseUrl(); + } - protected override void OnUpdated(Store entity, IHookedEntity entry) - { - TryChangeSchedulerBaseUrl(); - } + protected override void OnUpdated(Store entity, IHookedEntity entry) + { + TryChangeSchedulerBaseUrl(); + } - protected override void OnDeleted(Store entity, IHookedEntity entry) - { - _pictureService.ClearUrlCache(); - TryChangeSchedulerBaseUrl(); - } + protected override void OnDeleted(Store entity, IHookedEntity entry) + { + TryChangeSchedulerBaseUrl(); + } - private void TryChangeSchedulerBaseUrl() - { - if (CommonHelper.GetAppSetting("sm:TaskSchedulerBaseUrl").IsWebUrl() == false) - { - _taskScheduler.SetBaseUrl(_storeService, _httpContext); - } - } - } + private void TryChangeSchedulerBaseUrl() + { + if (CommonHelper.GetAppSetting("sm:TaskSchedulerBaseUrl").IsWebUrl() == false) + { + _taskScheduler.SetBaseUrl(_storeService, _httpContext); + } + } + } } diff --git a/src/Libraries/SmartStore.Services/Hooks/UpdateCustomerFullNameHook.cs b/src/Libraries/SmartStore.Services/Hooks/UpdateCustomerFullNameHook.cs index ba90b19cc8..d47e38523e 100644 --- a/src/Libraries/SmartStore.Services/Hooks/UpdateCustomerFullNameHook.cs +++ b/src/Libraries/SmartStore.Services/Hooks/UpdateCustomerFullNameHook.cs @@ -1,6 +1,5 @@ -using System; +using System.Collections.Generic; using System.Linq; -using System.Collections.Generic; using Autofac; using SmartStore.Core.Data; using SmartStore.Core.Data.Hooks; @@ -8,56 +7,61 @@ namespace SmartStore.Services.Hooks { - [Important] - public class UpdateCustomerFullNameHook : DbSaveHook - { - private static readonly HashSet _candidateProps = new HashSet(new string[] - { - nameof(Customer.Title), - nameof(Customer.Salutation), - nameof(Customer.FirstName), - nameof(Customer.LastName) - }); - - private readonly IComponentContext _ctx; - - public UpdateCustomerFullNameHook(IComponentContext ctx) - { - _ctx = ctx; - } - - protected override void OnUpdating(Customer entity, IHookedEntity entry) - { - UpdateFullName(entity, entry); - } - - protected override void OnInserting(Customer entity, IHookedEntity entry) - { - UpdateFullName(entity, entry); - } - - private void UpdateFullName(Customer entity, IHookedEntity entry) - { - var shouldUpdate = entity.IsTransientRecord(); - - if (!entity.IsTransientRecord()) - { - var modProps = _ctx.Resolve().GetModifiedProperties(entity); - shouldUpdate = _candidateProps.Any(x => modProps.ContainsKey(x)); - } - - if (shouldUpdate) - { - var parts = new[] - { - entity.Salutation, - entity.Title, - entity.FirstName, - entity.LastName - }; - - entity.FullName = string.Join(" ", parts.Where(x => x.HasValue())).NullEmpty(); - } - } - } + [Important] + public class UpdateCustomerFullNameHook : DbSaveHook + { + private static readonly HashSet _candidateProps = new HashSet(new string[] + { + nameof(Customer.Title), + nameof(Customer.Salutation), + nameof(Customer.FirstName), + nameof(Customer.LastName) + }); + + private readonly IComponentContext _ctx; + + public UpdateCustomerFullNameHook(IComponentContext ctx) + { + _ctx = ctx; + } + + protected override void OnUpdating(Customer entity, IHookedEntity entry) + { + UpdateFullName(entity, entry); + } + + protected override void OnInserting(Customer entity, IHookedEntity entry) + { + UpdateFullName(entity, entry); + } + + private void UpdateFullName(Customer entity, IHookedEntity entry) + { + var shouldUpdate = entity.IsTransientRecord(); + + if (!shouldUpdate) + { + shouldUpdate = entity.FullName.IsEmpty() && (entity.FirstName.HasValue() || entity.LastName.HasValue()); + } + + if (!shouldUpdate) + { + var modProps = _ctx.Resolve().GetModifiedProperties(entity); + shouldUpdate = _candidateProps.Any(x => modProps.ContainsKey(x)); + } + + if (shouldUpdate) + { + var parts = new[] + { + entity.Salutation, + entity.Title, + entity.FirstName, + entity.LastName + }; + + entity.FullName = string.Join(" ", parts.Where(x => x.HasValue())).NullEmpty(); + } + } + } } diff --git a/src/Libraries/SmartStore.Services/ICommonServices.cs b/src/Libraries/SmartStore.Services/ICommonServices.cs index 0f2030395f..8b9e8b0baa 100644 --- a/src/Libraries/SmartStore.Services/ICommonServices.cs +++ b/src/Libraries/SmartStore.Services/ICommonServices.cs @@ -1,75 +1,74 @@ using System; -using System.Collections.Generic; +using Autofac; using SmartStore.Core; using SmartStore.Core.Caching; using SmartStore.Core.Data; using SmartStore.Core.Events; -using SmartStore.Services.Localization; using SmartStore.Core.Logging; -using SmartStore.Services.Security; +using SmartStore.Core.Security; using SmartStore.Services.Configuration; -using SmartStore.Services.Stores; using SmartStore.Services.Helpers; -using Autofac; +using SmartStore.Services.Localization; using SmartStore.Services.Media; using SmartStore.Services.Messages; +using SmartStore.Services.Stores; namespace SmartStore.Services -{ - public interface ICommonServices - { - IComponentContext Container { get; } - IApplicationEnvironment ApplicationEnvironment { get; } - ICacheManager Cache { get; } - IRequestCache RequestCache { get; } - IDisplayControl DisplayControl { get; } - IDbContext DbContext { get; } - IStoreContext StoreContext { get; } - IWebHelper WebHelper { get; } - IWorkContext WorkContext { get; } - IEventPublisher EventPublisher { get; } - ILocalizationService Localization { get; } - ICustomerActivityService CustomerActivity { get; } - IPictureService PictureService { get; } - INotifier Notifier { get; } - IPermissionService Permissions { get; } - ISettingService Settings { get; } - IStoreService StoreService { get; } - IDateTimeHelper DateTimeHelper { get; } - IChronometer Chronometer { get; } - IMessageFactory MessageFactory { get; } - } +{ + public interface ICommonServices + { + IComponentContext Container { get; } + IApplicationEnvironment ApplicationEnvironment { get; } + ICacheManager Cache { get; } + IRequestCache RequestCache { get; } + IDisplayControl DisplayControl { get; } + IDbContext DbContext { get; } + IStoreContext StoreContext { get; } + IWebHelper WebHelper { get; } + IWorkContext WorkContext { get; } + IEventPublisher EventPublisher { get; } + ILocalizationService Localization { get; } + ICustomerActivityService CustomerActivity { get; } + IMediaService MediaService { get; } + INotifier Notifier { get; } + IPermissionService Permissions { get; } + ISettingService Settings { get; } + IStoreService StoreService { get; } + IDateTimeHelper DateTimeHelper { get; } + IChronometer Chronometer { get; } + IMessageFactory MessageFactory { get; } + } - public static class ICommonServicesExtensions - { - public static TService Resolve(this ICommonServices services) - { - return services.Container.Resolve(); - } + public static class ICommonServicesExtensions + { + public static TService Resolve(this ICommonServices services) + { + return services.Container.Resolve(); + } - public static TService Resolve(this ICommonServices services, object serviceKey) - { - return services.Container.ResolveKeyed(serviceKey); - } + public static TService Resolve(this ICommonServices services, object serviceKey) + { + return services.Container.ResolveKeyed(serviceKey); + } - public static TService ResolveNamed(this ICommonServices services, string serviceName) - { - return services.Container.ResolveNamed(serviceName); - } + public static TService ResolveNamed(this ICommonServices services, string serviceName) + { + return services.Container.ResolveNamed(serviceName); + } - public static object Resolve(this ICommonServices services, Type serviceType) - { - return services.Resolve(null, serviceType); - } + public static object Resolve(this ICommonServices services, Type serviceType) + { + return services.Resolve(null, serviceType); + } - public static object Resolve(this ICommonServices services, object serviceKey, Type serviceType) - { - return services.Container.ResolveKeyed(serviceKey, serviceType); - } + public static object Resolve(this ICommonServices services, object serviceKey, Type serviceType) + { + return services.Container.ResolveKeyed(serviceKey, serviceType); + } - public static object ResolveNamed(this ICommonServices services, string serviceName, Type serviceType) - { - return services.Container.ResolveNamed(serviceName, serviceType); - } - } + public static object ResolveNamed(this ICommonServices services, string serviceName, Type serviceType) + { + return services.Container.ResolveNamed(serviceName, serviceType); + } + } } diff --git a/src/Libraries/SmartStore.Services/IScopedService.cs b/src/Libraries/SmartStore.Services/IScopedService.cs index 3c081b953f..9494016f64 100644 --- a/src/Libraries/SmartStore.Services/IScopedService.cs +++ b/src/Libraries/SmartStore.Services/IScopedService.cs @@ -2,23 +2,23 @@ namespace SmartStore.Services { - public interface IScopedService - { - /// - /// Creates a long running unit of work in which cache eviction is suppressed - /// - /// Specifies whether the cache should be evicted completely on batch disposal - /// A disposable unit of work - IDisposable BeginScope(bool clearCache = true); + public interface IScopedService + { + /// + /// Creates a long running unit of work in which cache eviction is suppressed + /// + /// Specifies whether the cache should be evicted completely on batch disposal + /// A disposable unit of work + IDisposable BeginScope(bool clearCache = true); - /// - /// Gets a value indicating whether data has changed during a request, making cache eviction necessary. - /// - /// - /// Cache eviction sets this member to false - /// - bool HasChanges { get; } + /// + /// Gets a value indicating whether data has changed during a request, making cache eviction necessary. + /// + /// + /// Cache eviction sets this member to false + /// + bool HasChanges { get; } - void ClearCache(); - } + void ClearCache(); + } } diff --git a/src/Libraries/SmartStore.Services/Localization/ILanguageService.cs b/src/Libraries/SmartStore.Services/Localization/ILanguageService.cs index a717f199ca..a0e7e7009e 100644 --- a/src/Libraries/SmartStore.Services/Localization/ILanguageService.cs +++ b/src/Libraries/SmartStore.Services/Localization/ILanguageService.cs @@ -62,34 +62,34 @@ public partial interface ILanguageService /// Language void UpdateLanguage(Language language); - /// - /// Determines whether a language is active/published - /// - /// The SEO code of the language to check - /// The store id - /// true when the language is published, false otherwise - bool IsPublishedLanguage(string seoCode, int storeId = 0); + /// + /// Determines whether a language is active/published + /// + /// The SEO code of the language to check + /// The store id + /// true when the language is published, false otherwise + bool IsPublishedLanguage(string seoCode, int storeId = 0); - /// - /// Determines whether a language is active/published - /// - /// The id of the language to check - /// The store id - /// true when the language is published, false otherwise - bool IsPublishedLanguage(int languageId, int storeId = 0); + /// + /// Determines whether a language is active/published + /// + /// The id of the language to check + /// The store id + /// true when the language is published, false otherwise + bool IsPublishedLanguage(int languageId, int storeId = 0); - /// - /// Gets the seo code of the default (first) active language - /// - /// The store id - /// The seo code - string GetDefaultLanguageSeoCode(int storeId = 0); + /// + /// Gets the seo code of the default (first) active language + /// + /// The store id + /// The seo code + string GetDefaultLanguageSeoCode(int storeId = 0); - /// - /// Gets the id of the default (first) active language - /// - /// The store id - /// The language id - int GetDefaultLanguageId(int storeId = 0); + /// + /// Gets the id of the default (first) active language + /// + /// The store id + /// The language id + int GetDefaultLanguageId(int storeId = 0); } } diff --git a/src/Libraries/SmartStore.Services/Localization/ILocalizationService.cs b/src/Libraries/SmartStore.Services/Localization/ILocalizationService.cs index 09f2d9bf1a..00ac0273ee 100644 --- a/src/Libraries/SmartStore.Services/Localization/ILocalizationService.cs +++ b/src/Libraries/SmartStore.Services/Localization/ILocalizationService.cs @@ -5,14 +5,15 @@ using SmartStore.Core; using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Domain.Localization; +using SmartStore.Core.IO; using SmartStore.Core.Plugins; namespace SmartStore.Services.Localization { - /// - /// Localization manager interface - /// - public partial interface ILocalizationService + /// + /// Localization manager interface + /// + public partial interface ILocalizationService { /// /// Deletes a locale string resource @@ -20,12 +21,12 @@ public partial interface ILocalizationService /// Locale string resource void DeleteLocaleStringResource(LocaleStringResource localeStringResource); - /// - /// Deletes all string resources with its key beginning with rootKey. - /// - /// e.g. Plugins.Import.Biz - /// Number of deleted string resources - int DeleteLocaleStringResources(string key, bool keyIsRootKey = true); + /// + /// Deletes all string resources with its key beginning with rootKey. + /// + /// e.g. Plugins.Import.Biz + /// Number of deleted string resources + int DeleteLocaleStringResources(string key, bool keyIsRootKey = true); /// /// Gets a locale string resource @@ -57,19 +58,19 @@ public partial interface ILocalizationService /// Locale string resources IQueryable All(int languageId); - /// - /// Gets all locale string resources by language identifier starting with a prefix - /// - /// Pattern - /// Language identifier - /// Locale string resources matching language id and starting with - IList GetResourcesByPattern(string pattern, int languageId); + /// + /// Gets all locale string resources by language identifier starting with a prefix + /// + /// Pattern + /// Language identifier + /// Locale string resources matching language id and starting with + IList GetResourcesByPattern(string pattern, int languageId); - /// - /// Inserts a locale string resource - /// - /// Locale string resource - void InsertLocaleStringResource(LocaleStringResource localeStringResource); + /// + /// Inserts a locale string resource + /// + /// Locale string resource + void InsertLocaleStringResource(LocaleStringResource localeStringResource); /// /// Updates the locale string resource @@ -96,36 +97,41 @@ public partial interface ILocalizationService /// Result in XML format string ExportResourcesToXml(Language language); - /// - /// Import language resources from XML file - /// - /// codehint: sm-edit - /// Language - /// XML document - /// Prefix for resource key name - /// Specifies whether resource should be inserted or updated (or both) - /// Specifies whether user touched resources should also be updated - /// The number of processed (added or updated) resource entries - int ImportResourcesFromXml(Language language, + /// + /// Import language resources from XML file + /// + /// Language + /// XML document + /// Prefix for resource key name + /// Specifies whether resource should be inserted or updated (or both) + /// Specifies whether user touched resources should also be updated + /// The number of processed (added or updated) resource entries + int ImportResourcesFromXml(Language language, XmlDocument xmlDocument, string rootKey = null, bool sourceIsPlugin = false, ImportModeFlags mode = ImportModeFlags.Insert | ImportModeFlags.Update, bool updateTouchedResources = false); - /// - /// Import plugin resources from xml files in plugin's localization directory. Notes: Deletes existing resources before importing. - /// - /// codehint: sm-add - /// Descriptor of the plugin - /// Load them into the passed list rather than into database - /// Specifies whether user touched resources should also be updated - /// Import only files for particular languages - void ImportPluginResourcesFromXml( - PluginDescriptor pluginDescriptor, - IList targetList = null, - bool updateTouchedResources = true, - IList filterLanguages = null); + /// + /// Creates a directory hasher used to determine plugin localization changes across app startups. + /// + /// Descriptor of the plugin + /// The hasher impl + DirectoryHasher CreatePluginResourcesHasher(PluginDescriptor pluginDescriptor); + + /// + /// Import plugin resources from xml files in plugin's localization directory. Notes: Deletes existing resources before importing. + /// + /// Descriptor of the plugin + /// Load them into the passed list rather than into database + /// Specifies whether user touched resources should also be updated + /// Import only files for particular languages + void ImportPluginResourcesFromXml( + PluginDescriptor pluginDescriptor, + IList targetList = null, + bool updateTouchedResources = true, + IList filterLanguages = null); /// /// Flattens all nested LocaleResource child nodes into a new document diff --git a/src/Libraries/SmartStore.Services/Localization/ILocalizedEntityService.cs b/src/Libraries/SmartStore.Services/Localization/ILocalizedEntityService.cs index 979ef91ca5..c07ffe0766 100644 --- a/src/Libraries/SmartStore.Services/Localization/ILocalizedEntityService.cs +++ b/src/Libraries/SmartStore.Services/Localization/ILocalizedEntityService.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq.Expressions; using SmartStore.Core; +using SmartStore.Core.Configuration; using SmartStore.Core.Domain.Localization; namespace SmartStore.Services.Localization @@ -10,7 +11,7 @@ namespace SmartStore.Services.Localization /// Localized entity service interface /// public partial interface ILocalizedEntityService : IScopedService - { + { /// /// Deletes a localized property /// @@ -42,44 +43,44 @@ 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); + /// + /// 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); + /// + /// 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 @@ -93,18 +94,45 @@ public partial interface ILocalizedEntityService : IScopedService /// Type /// Entity /// Key selector - /// Locale value + /// Locale value /// Language ID void SaveLocalizedValue( - T entity, + T entity, Expression> keySelector, - string localeValue, + string value, int languageId) where T : BaseEntity, ILocalizedEntity; + + /// + /// Save localized value + /// + /// Type + /// Property type + /// Entity + /// Key selector + /// Locale value + /// Language ID void SaveLocalizedValue( - T entity, + T entity, Expression> keySelector, - TPropType localeValue, + TPropType value, int languageId) where T : BaseEntity, ILocalizedEntity; + + /// + /// Save localized setting value + /// + /// Setting impl type + /// Property type + /// Settings instance + /// Key selector + /// Locale value + /// Language ID + /// Store ID + void SaveLocalizedSetting( + TSetting settings, + Expression> keySelector, + TPropType value, + int languageId, + int storeId = 0) where TSetting : class, ISettings; } } diff --git a/src/Libraries/SmartStore.Services/Localization/LanguageService.cs b/src/Libraries/SmartStore.Services/Localization/LanguageService.cs index 3bcb3646e3..65f58ab09a 100644 --- a/src/Libraries/SmartStore.Services/Localization/LanguageService.cs +++ b/src/Libraries/SmartStore.Services/Localization/LanguageService.cs @@ -19,40 +19,38 @@ public partial class LanguageService : ILanguageService private const string LANGUAGES_PATTERN_KEY = "SmartStore.language.*"; private readonly IRepository _languageRepository; - private readonly IStoreMappingService _storeMappingService; - private readonly IStoreService _storeService; - private readonly IStoreContext _storeContext; + private readonly IStoreMappingService _storeMappingService; + private readonly IStoreService _storeService; + private readonly IStoreContext _storeContext; private readonly IRequestCache _requestCache; - private readonly ICacheManager _cache; - private readonly ISettingService _settingService; + private readonly ICacheManager _cache; + private readonly ISettingService _settingService; private readonly LocalizationSettings _localizationSettings; - private readonly IEventPublisher _eventPublisher; public LanguageService( - IRequestCache requestCache, - ICacheManager cache, - IRepository languageRepository, + IRequestCache requestCache, + ICacheManager cache, + IRepository languageRepository, ISettingService settingService, LocalizationSettings localizationSettings, IEventPublisher eventPublisher, - IStoreMappingService storeMappingService, - IStoreService storeService, - IStoreContext storeContext) + IStoreMappingService storeMappingService, + IStoreService storeService, + IStoreContext storeContext) { _requestCache = requestCache; - _cache = cache; + _cache = cache; _languageRepository = languageRepository; _settingService = settingService; _localizationSettings = localizationSettings; - _eventPublisher = eventPublisher; - _storeMappingService = storeMappingService; - _storeService = storeService; - _storeContext = storeContext; + _storeMappingService = storeMappingService; + _storeService = storeService; + _storeContext = storeContext; } public virtual void DeleteLanguage(Language language) { - Guard.NotNull(language, nameof(language)); + Guard.NotNull(language, nameof(language)); // Update default admin area language (if required) if (_localizationSettings.DefaultAdminLanguageId == language.Id) @@ -67,35 +65,35 @@ public virtual void DeleteLanguage(Language language) } } } - + _languageRepository.Delete(language); // cache - _requestCache.RemoveByPattern(LANGUAGES_PATTERN_KEY); + _requestCache.RemoveByPattern(LANGUAGES_PATTERN_KEY); } - public virtual IList GetAllLanguages(bool showHidden = false, int storeId = 0) + public virtual IList GetAllLanguages(bool showHidden = false, int storeId = 0) { - var query = _languageRepository.Table; + var query = _languageRepository.Table; - if (!showHidden) - { - query = query.Where(x => x.Published); - } + if (!showHidden) + { + query = query.Where(x => x.Published); + } - query = query.OrderBy(x => x.DisplayOrder); + query = query.OrderBy(x => x.DisplayOrder); - var languages = query.ToListCached("db.lang.all.{0}".FormatInvariant(showHidden)); + var languages = query.ToListCached("db.lang.all.{0}".FormatInvariant(showHidden)); - // store mapping - if (storeId > 0) - { - languages = languages - .Where(l => _storeMappingService.Authorize(l, storeId)) - .ToList(); - } + // store mapping + if (storeId > 0) + { + languages = languages + .Where(l => _storeMappingService.Authorize(l, storeId)) + .ToList(); + } - return languages; + return languages; } public virtual int GetLanguagesCount(bool showHidden = false) @@ -106,7 +104,7 @@ public virtual int GetLanguagesCount(bool showHidden = false) var query = _languageRepository.Table; if (!showHidden) query = query.Where(l => l.Published); - return query.Select(x => x.Id).Count(); + return query.Count(); }); } @@ -115,28 +113,28 @@ public virtual Language GetLanguageById(int languageId) if (languageId == 0) return null; - return _languageRepository.GetByIdCached(languageId, "db.lang.id-" + languageId); - } + return _languageRepository.GetByIdCached(languageId, "db.lang.id-" + languageId); + } public virtual Language GetLanguageByCulture(string culture) { if (!culture.HasValue()) return null; - return _languageRepository.Table - .Where(x => culture.Equals(x.LanguageCulture, StringComparison.InvariantCultureIgnoreCase)) - .FirstOrDefaultCached("db.lang.culture-" + culture); - } + return _languageRepository.Table + .Where(x => culture.Equals(x.LanguageCulture, StringComparison.InvariantCultureIgnoreCase)) + .FirstOrDefaultCached("db.lang.culture-" + culture); + } public virtual Language GetLanguageBySeoCode(string seoCode) { if (!seoCode.HasValue()) return null; - return _languageRepository.Table - .Where(x => seoCode.Equals(x.UniqueSeoCode, StringComparison.InvariantCultureIgnoreCase)) - .FirstOrDefaultCached("db.lang.seo-" + seoCode); - } + return _languageRepository.Table + .Where(x => seoCode.Equals(x.UniqueSeoCode, StringComparison.InvariantCultureIgnoreCase)) + .FirstOrDefaultCached("db.lang.seo-" + seoCode); + } public virtual void InsertLanguage(Language language) { @@ -153,7 +151,7 @@ public virtual void UpdateLanguage(Language language) { if (language == null) throw new ArgumentNullException("language"); - + //update language _languageRepository.Update(language); @@ -161,109 +159,109 @@ public virtual void UpdateLanguage(Language language) _requestCache.RemoveByPattern(LANGUAGES_PATTERN_KEY); } - public virtual bool IsPublishedLanguage(string seoCode, int storeId = 0) - { - if (storeId <= 0) - storeId = _storeContext.CurrentStore.Id; - - var map = this.GetStoreLanguageMap(); - if (map.ContainsKey(storeId)) - { - return map[storeId].Any(x => x.UniqueSeoCode == seoCode); - } - - return false; - } - - public virtual bool IsPublishedLanguage(int languageId, int storeId = 0) - { - if (languageId <= 0) - return false; - - if (storeId <= 0) - storeId = _storeContext.CurrentStore.Id; - - var map = this.GetStoreLanguageMap(); - if (map.ContainsKey(storeId)) - { - return map[storeId].Any(x => x.Id == languageId); - } - - return false; - } - - public virtual string GetDefaultLanguageSeoCode(int storeId = 0) - { - if (storeId <= 0) - storeId = _storeContext.CurrentStore.Id; - - var map = this.GetStoreLanguageMap(); - if (map.ContainsKey(storeId)) - { - return map[storeId].FirstOrDefault().UniqueSeoCode; - } - - return null; - } - - public virtual int GetDefaultLanguageId(int storeId = 0) - { - if (storeId <= 0) - storeId = _storeContext.CurrentStore.Id; - - var map = this.GetStoreLanguageMap(); - if (map.ContainsKey(storeId)) - { - return map[storeId].FirstOrDefault().Id; - } - - return 0; - } - - /// - /// Gets a map of active/published store languages - /// - /// A map of store languages where key is the store id and values are tuples of language ids and seo codes - protected virtual Multimap GetStoreLanguageMap() - { - var result = _cache.Get(ServiceCacheBuster.STORE_LANGUAGE_MAP_KEY, () => - { - var map = new Multimap(); - - var allStores = _storeService.GetAllStores(); - foreach (var store in allStores) - { - var languages = GetAllLanguages(false, store.Id); - if (!languages.Any()) - { - // language-less stores aren't allowed but could exist accidentally. Correct this. - var firstStoreLang = GetAllLanguages(true, store.Id).FirstOrDefault(); - if (firstStoreLang == null) - { - // absolute fallback - firstStoreLang = GetAllLanguages(true).FirstOrDefault(); - } - map.Add(store.Id, new MinifiedLanguage { Id = firstStoreLang.Id, UniqueSeoCode = firstStoreLang.UniqueSeoCode }); - } - else - { - foreach (var lang in languages) - { - map.Add(store.Id, new MinifiedLanguage { Id = lang.Id, UniqueSeoCode = lang.UniqueSeoCode }); - } - } - } - - return map; - }, TimeSpan.FromDays(1)); - - return result; - } - - public class MinifiedLanguage - { - public int Id { get; set; } - public string UniqueSeoCode { get; set; } - } + public virtual bool IsPublishedLanguage(string seoCode, int storeId = 0) + { + if (storeId <= 0) + storeId = _storeContext.CurrentStore.Id; + + var map = this.GetStoreLanguageMap(); + if (map.ContainsKey(storeId)) + { + return map[storeId].Any(x => x.UniqueSeoCode == seoCode); + } + + return false; + } + + public virtual bool IsPublishedLanguage(int languageId, int storeId = 0) + { + if (languageId <= 0) + return false; + + if (storeId <= 0) + storeId = _storeContext.CurrentStore.Id; + + var map = this.GetStoreLanguageMap(); + if (map.ContainsKey(storeId)) + { + return map[storeId].Any(x => x.Id == languageId); + } + + return false; + } + + public virtual string GetDefaultLanguageSeoCode(int storeId = 0) + { + if (storeId <= 0) + storeId = _storeContext.CurrentStore.Id; + + var map = this.GetStoreLanguageMap(); + if (map.ContainsKey(storeId)) + { + return map[storeId].FirstOrDefault().UniqueSeoCode; + } + + return null; + } + + public virtual int GetDefaultLanguageId(int storeId = 0) + { + if (storeId <= 0) + storeId = _storeContext.CurrentStore.Id; + + var map = this.GetStoreLanguageMap(); + if (map.ContainsKey(storeId)) + { + return map[storeId].FirstOrDefault().Id; + } + + return 0; + } + + /// + /// Gets a map of active/published store languages + /// + /// A map of store languages where key is the store id and values are tuples of language ids and seo codes + protected virtual Multimap GetStoreLanguageMap() + { + var result = _cache.Get(ServiceCacheBuster.STORE_LANGUAGE_MAP_KEY, () => + { + var map = new Multimap(); + + var allStores = _storeService.GetAllStores(); + foreach (var store in allStores) + { + var languages = GetAllLanguages(false, store.Id); + if (!languages.Any()) + { + // language-less stores aren't allowed but could exist accidentally. Correct this. + var firstStoreLang = GetAllLanguages(true, store.Id).FirstOrDefault(); + if (firstStoreLang == null) + { + // absolute fallback + firstStoreLang = GetAllLanguages(true).FirstOrDefault(); + } + map.Add(store.Id, new MinifiedLanguage { Id = firstStoreLang.Id, UniqueSeoCode = firstStoreLang.UniqueSeoCode }); + } + else + { + foreach (var lang in languages) + { + map.Add(store.Id, new MinifiedLanguage { Id = lang.Id, UniqueSeoCode = lang.UniqueSeoCode }); + } + } + } + + return map; + }, TimeSpan.FromDays(1)); + + return result; + } + + public class MinifiedLanguage + { + public int Id { get; set; } + public string UniqueSeoCode { get; set; } + } } } diff --git a/src/Libraries/SmartStore.Services/Localization/LocalizationExtensions.cs b/src/Libraries/SmartStore.Services/Localization/LocalizationExtensions.cs index eef621f93f..34accb3b46 100644 --- a/src/Libraries/SmartStore.Services/Localization/LocalizationExtensions.cs +++ b/src/Libraries/SmartStore.Services/Localization/LocalizationExtensions.cs @@ -1,10 +1,10 @@ using System; -using System.Globalization; using System.Linq.Expressions; -using System.Reflection; +using System.Runtime.CompilerServices; using System.Xml; using SmartStore.ComponentModel; using SmartStore.Core; +using SmartStore.Core.Configuration; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.DataExchange; using SmartStore.Core.Domain.Localization; @@ -14,331 +14,344 @@ namespace SmartStore.Services.Localization { - public static class LocalizationExtensions + public static class LocalizationExtensions { - /// - /// Get localized property of an entity - /// - /// Entity type - /// Entity - /// Key selector - /// When true, additionally checks whether the localized value contains empty HTML only and falls back to the default value if so. - /// Localized property - public static LocalizedValue GetLocalized(this T entity, Expression> keySelector, bool detectEmptyHtml = false) + #region GetLocalized BaseEntity + + /// + /// Get localized property of an entity + /// + /// Entity type + /// Entity + /// Key selector + /// When true, additionally checks whether the localized value contains empty HTML only and falls back to the default value if so. + /// Localized property + public static LocalizedValue GetLocalized(this T entity, Expression> keySelector, bool detectEmptyHtml = false) + where T : BaseEntity, ILocalizedEntity + { + var invoker = keySelector.CompileFast(); + return EngineContext.Current.Resolve().GetLocalizedValue( + entity, + entity.Id, + entity.GetEntityName(), + invoker.Property.Name, + (Func)invoker, + null, + detectEmptyHtml: detectEmptyHtml); + } + + /// + /// Get localized property of an entity + /// + /// Entity type + /// Entity + /// Key selector + /// Language identifier + /// A value indicating whether to return default value (if localized is not found) + /// A value indicating whether to ensure that we have at least two published languages; otherwise, load only default value + /// When true, additionally checks whether the localized value contains empty HTML only and falls back to the default value if so. + /// Localized property + public static LocalizedValue GetLocalized(this T entity, + Expression> keySelector, + int languageId, + bool returnDefaultValue = true, + bool ensureTwoPublishedLanguages = true, + bool detectEmptyHtml = false) + where T : BaseEntity, ILocalizedEntity + { + var invoker = keySelector.CompileFast(); + return EngineContext.Current.Resolve().GetLocalizedValue( + entity, + entity.Id, + entity.GetEntityName(), + invoker.Property.Name, + invoker, + languageId, + returnDefaultValue, + ensureTwoPublishedLanguages, + detectEmptyHtml); + } + + /// + /// Get localized property of an entity + /// + /// Entity type + /// Entity + /// Key selector + /// Language identifier + /// A value indicating whether to return default value (if localized is not found) + /// A value indicating whether to ensure that we have at least two published languages; otherwise, load only default value + /// When true, additionally checks whether the localized value contains empty HTML only and falls back to the default value if so. + /// Localized property + public static LocalizedValue GetLocalized(this T entity, + string localeKey, + TProp fallback, + Language language, + bool returnDefaultValue = true, + bool ensureTwoPublishedLanguages = true, + bool detectEmptyHtml = false) + where T : BaseEntity, ILocalizedEntity + { + return EngineContext.Current.Resolve().GetLocalizedValue( + entity, + entity.Id, + entity.GetEntityName(), + localeKey, + x => fallback, + language, + returnDefaultValue, + ensureTwoPublishedLanguages, + detectEmptyHtml); + } + + /// + /// Get localized property of an entity + /// + /// Entity type + /// Entity + /// Key selector + /// Language + /// A value indicating whether to return default value (if localized is not found) + /// A value indicating whether to ensure that we have at least two published languages; otherwise, load only default value + /// When true, additionally checks whether the localized value contains empty HTML only and falls back to the default value if so. + /// Localized property + public static LocalizedValue GetLocalized(this T entity, + Expression> keySelector, + Language language, + bool returnDefaultValue = true, + bool ensureTwoPublishedLanguages = true, + bool detectEmptyHtml = false) where T : BaseEntity, ILocalizedEntity { - return GetLocalizedEx( - entity, - entity.GetEntityName(), - LocaleKeyFromExpression(keySelector.Body), - x => keySelector.Compile().Invoke(x), - EngineContext.Current.Resolve().WorkingLanguage, - detectEmptyHtml: detectEmptyHtml); - } + var invoker = keySelector.CompileFast(); + return EngineContext.Current.Resolve().GetLocalizedValue( + entity, + entity.Id, + entity.GetEntityName(), + invoker.Property.Name, + invoker, + language, + returnDefaultValue, + ensureTwoPublishedLanguages, + detectEmptyHtml); + } /// /// Get localized property of an entity /// /// Entity type + /// Property type /// Entity /// Key selector /// Language identifier /// A value indicating whether to return default value (if localized is not found) /// A value indicating whether to ensure that we have at least two published languages; otherwise, load only default value - /// When true, additionally checks whether the localized value contains empty HTML only and falls back to the default value if so. + /// When true, additionally checks whether the localized value contains empty HTML only and falls back to the default value if so. /// Localized property - public static LocalizedValue GetLocalized(this T entity, - Expression> keySelector, - int languageId, - bool returnDefaultValue = true, - bool ensureTwoPublishedLanguages = true, - bool detectEmptyHtml = false) + public static LocalizedValue GetLocalized(this T entity, + Expression> keySelector, + int languageId, + bool returnDefaultValue = true, + bool ensureTwoPublishedLanguages = true, + bool detectEmptyHtml = false) where T : BaseEntity, ILocalizedEntity { - return GetLocalizedEx( - entity, - entity.GetEntityName(), - LocaleKeyFromExpression(keySelector.Body), - x => keySelector.Compile().Invoke(x), - EngineContext.Current.Resolve().GetLanguageById(languageId), - returnDefaultValue, - ensureTwoPublishedLanguages, - detectEmptyHtml); + var invoker = keySelector.CompileFast(); + return EngineContext.Current.Resolve().GetLocalizedValue( + entity, + entity.Id, + entity.GetEntityName(), + invoker.Property.Name, + (Func)invoker, + languageId, + returnDefaultValue, + ensureTwoPublishedLanguages, + detectEmptyHtml); + } + + /// + /// Get localized property of an entity + /// + /// Entity type + /// Property type + /// Entity + /// Key selector + /// Language + /// A value indicating whether to return default value (if localized is not found) + /// A value indicating whether to ensure that we have at least two published languages; otherwise, load only default value + /// When true, additionally checks whether the localized value contains empty HTML only and falls back to the default value if so. + /// Localized property + public static LocalizedValue GetLocalized(this T entity, + Expression> keySelector, + Language language, + bool returnDefaultValue = true, + bool ensureTwoPublishedLanguages = true, + bool detectEmptyHtml = false) + where T : BaseEntity, ILocalizedEntity + { + var invoker = keySelector.CompileFast(); + return EngineContext.Current.Resolve().GetLocalizedValue( + entity, + entity.Id, + entity.GetEntityName(), + invoker.Property.Name, + (Func)invoker, + language, + returnDefaultValue, + ensureTwoPublishedLanguages, + detectEmptyHtml); + } + + #endregion + + #region GetLocalized ICategoryNode + + /// + /// Get localized property of an instance + /// + /// Node + /// Key selector + /// Localized property + public static LocalizedValue GetLocalized(this ICategoryNode node, Expression> keySelector) + { + var invoker = keySelector.CompileFast(); + return EngineContext.Current.Resolve().GetLocalizedValue( + node, + node.Id, + "Category", + invoker.Property.Name, + (Func)invoker, + null); + } + + /// + /// Get localized property of an instance + /// + /// Node + /// Key selector + /// /// Language identifier + /// Localized property + public static LocalizedValue GetLocalized(this ICategoryNode node, Expression> keySelector, int languageId) + { + var invoker = keySelector.CompileFast(); + return EngineContext.Current.Resolve().GetLocalizedValue( + node, + node.Id, + nameof(Category), + invoker.Property.Name, + (Func)invoker, + languageId); + } + + /// + /// Get localized property of an instance + /// + /// Node + /// Key selector + /// /// Language + /// Localized property + public static LocalizedValue GetLocalized(this ICategoryNode node, Expression> keySelector, Language language) + { + var invoker = keySelector.CompileFast(); + return EngineContext.Current.Resolve().GetLocalizedValue( + node, + node.Id, + nameof(Category), + invoker.Property.Name, + (Func)invoker, + language); + } + + #endregion + + #region GetLocalized ISettings + + /// + /// Get localized property of an implementation + /// + /// The settings instance + /// Key selector + /// Localized property + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static LocalizedValue GetLocalizedSetting(this TSetting settings, + Expression> keySelector, + int? storeId = null, + bool returnDefaultValue = true, + bool ensureTwoPublishedLanguages = true, + bool detectEmptyHtml = false) + where TSetting : class, ISettings + { + return GetLocalizedSetting(settings, keySelector, null, storeId, returnDefaultValue, ensureTwoPublishedLanguages, detectEmptyHtml); } - /// - /// Get localized property of an entity - /// - /// Entity type - /// Entity - /// Key selector - /// Language identifier - /// A value indicating whether to return default value (if localized is not found) - /// A value indicating whether to ensure that we have at least two published languages; otherwise, load only default value - /// When true, additionally checks whether the localized value contains empty HTML only and falls back to the default value if so. - /// Localized property - public static LocalizedValue GetLocalized(this T entity, - string localeKey, - TProp fallback, - Language language, - bool returnDefaultValue = true, - bool ensureTwoPublishedLanguages = true, - bool detectEmptyHtml = false) - where T : BaseEntity, ILocalizedEntity - { - return GetLocalizedEx( - entity, - entity.GetEntityName(), - localeKey, - x => fallback, - language, - returnDefaultValue, - ensureTwoPublishedLanguages, - detectEmptyHtml); - } - - /// - /// Get localized property of an entity - /// - /// Entity type - /// Entity - /// Key selector - /// Language - /// A value indicating whether to return default value (if localized is not found) - /// A value indicating whether to ensure that we have at least two published languages; otherwise, load only default value - /// When true, additionally checks whether the localized value contains empty HTML only and falls back to the default value if so. - /// Localized property - public static LocalizedValue GetLocalized(this T entity, - Expression> keySelector, - Language language, - bool returnDefaultValue = true, - bool ensureTwoPublishedLanguages = true, - bool detectEmptyHtml = false) - where T : BaseEntity, ILocalizedEntity - { - return GetLocalizedEx( - entity, - entity.GetEntityName(), - LocaleKeyFromExpression(keySelector.Body), - x => keySelector.Compile().Invoke(x), - language, - returnDefaultValue, - ensureTwoPublishedLanguages, - detectEmptyHtml); - } - - /// - /// Get localized property of an entity - /// - /// Entity type - /// Property type - /// Entity - /// Key selector - /// Language identifier - /// A value indicating whether to return default value (if localized is not found) - /// A value indicating whether to ensure that we have at least two published languages; otherwise, load only default value - /// When true, additionally checks whether the localized value contains empty HTML only and falls back to the default value if so. - /// Localized property - public static LocalizedValue GetLocalized(this T entity, - Expression> keySelector, - int languageId, - bool returnDefaultValue = true, - bool ensureTwoPublishedLanguages = true, - bool detectEmptyHtml = false) - where T : BaseEntity, ILocalizedEntity - { - return GetLocalizedEx( - entity, - entity.GetEntityName(), - LocaleKeyFromExpression(keySelector.Body), - x => keySelector.Compile().Invoke(x), - EngineContext.Current.Resolve().GetLanguageById(languageId), - returnDefaultValue, - ensureTwoPublishedLanguages, - detectEmptyHtml); - } - - /// - /// Get localized property of an entity - /// - /// Entity type - /// Property type - /// Entity - /// Key selector - /// Language - /// A value indicating whether to return default value (if localized is not found) - /// A value indicating whether to ensure that we have at least two published languages; otherwise, load only default value - /// When true, additionally checks whether the localized value contains empty HTML only and falls back to the default value if so. - /// Localized property - public static LocalizedValue GetLocalized(this T entity, - Expression> keySelector, - Language language, - bool returnDefaultValue = true, - bool ensureTwoPublishedLanguages = true, - bool detectEmptyHtml = false) - where T : BaseEntity, ILocalizedEntity - { - return GetLocalizedEx( - entity, - entity.GetEntityName(), - LocaleKeyFromExpression(keySelector.Body), - x => keySelector.Compile().Invoke(x), - language, - returnDefaultValue, - ensureTwoPublishedLanguages, - detectEmptyHtml); - } - - /// - /// Get localized property of an instance - /// - /// Node - /// Key selector - /// Localized property - public static LocalizedValue GetLocalized(this ICategoryNode node, Expression> keySelector) - { - return GetLocalizedEx( - node, - "Category", - LocaleKeyFromExpression(keySelector.Body), - x => keySelector.Compile().Invoke(x), - EngineContext.Current.Resolve().WorkingLanguage); - } - - /// - /// Get localized property of an instance - /// - /// Node - /// Key selector - /// /// Language identifier - /// Localized property - public static LocalizedValue GetLocalized(this ICategoryNode node, Expression> keySelector, int languageId) - { - return GetLocalizedEx( - node, - "Category", - LocaleKeyFromExpression(keySelector.Body), - x => keySelector.Compile().Invoke(x), - EngineContext.Current.Resolve().GetLanguageById(languageId)); - } - - /// - /// Get localized property of an instance - /// - /// Node - /// Key selector - /// /// Language - /// Localized property - public static LocalizedValue GetLocalized(this ICategoryNode node, Expression> keySelector, Language language) - { - return GetLocalizedEx( - node, - "Category", - LocaleKeyFromExpression(keySelector.Body), - x => keySelector.Compile().Invoke(x), - language); - } - - internal static LocalizedValue GetLocalizedEx(T entity, - string localeKeyGroup, - string localeKey, - Func fallback, - Language requestLanguage, - bool returnDefaultValue = true, - bool ensureTwoPublishedLanguages = true, - bool detectEmptyHtml = false) - where T : ILocalizedEntity - { - if (entity == null) - throw new ArgumentNullException(nameof(entity)); - - TProp result = default; - var str = string.Empty; - - var languageService = EngineContext.Current.Resolve(); - - Language currentLanguage = null; - if (requestLanguage != null) - { - // Ensure that we have at least two published languages - var loadLocalizedValue = true; - if (ensureTwoPublishedLanguages) - { - var totalPublishedLanguages = languageService.GetLanguagesCount(false); - loadLocalizedValue = totalPublishedLanguages >= 2; - } - - // Localized value - if (loadLocalizedValue) - { - var leService = EngineContext.Current.Resolve(); - str = leService.GetLocalizedValue(requestLanguage.Id, entity.Id, localeKeyGroup, localeKey); - - if (detectEmptyHtml && str.HasValue() && str.RemoveHtml().IsEmpty()) - { - str = string.Empty; - } - - if (str.HasValue()) - { - currentLanguage = requestLanguage; - result = (TProp)str.Convert(typeof(TProp), CultureInfo.InvariantCulture); - } - } - } - - // Set default value if required - if (returnDefaultValue && str.IsEmpty()) - { - currentLanguage = languageService.GetLanguageById(languageService.GetDefaultLanguageId()); - result = fallback(entity); - } - - if (requestLanguage == null) - { - requestLanguage = EngineContext.Current.Resolve().WorkingLanguage; - } - - if (currentLanguage == null) - { - currentLanguage = requestLanguage; - } - - return new LocalizedValue(result, requestLanguage, currentLanguage); - } - - private static string LocaleKeyFromExpression(Expression expression) - { - if (!(expression is MemberExpression member)) - { - throw new ArgumentException($"Expression '{expression}' refers to a method, not to a property."); - } - - var propInfo = member.Member as PropertyInfo; - if (propInfo == null) - { - throw new ArgumentException($"Expression '{expression}' refers to a field, not to a property."); - } - - return propInfo.Name; - } - - - /// - /// Get localized value of enum - /// - /// Enum - /// Enum value - /// Localization service - /// Work context - /// Localized value - public static string GetLocalizedEnum(this T enumValue, ILocalizationService localizationService, IWorkContext workContext) + /// + /// Get localized property of an implementation + /// + /// The settings instance + /// Key selector + /// Language id, object instance or null + /// Localized property + public static LocalizedValue GetLocalizedSetting(this TSetting settings, + Expression> keySelector, + object requestLanguageIdOrObj, // Id or Language + int? storeId = null, + bool returnDefaultValue = true, + bool ensureTwoPublishedLanguages = true, + bool detectEmptyHtml = false) + where TSetting : class, ISettings + { + var helper = EngineContext.Current.Resolve(); + var invoker = keySelector.CompileFast(); + + if (storeId == null) + { + storeId = EngineContext.Current.Resolve().CurrentStore.Id; + } + + // Make fallback only when storeId is 0 and the paramter says so. + var localizedValue = GetValue(storeId.Value, storeId == 0 && returnDefaultValue); + + if (storeId > 0 && string.IsNullOrEmpty(localizedValue.Value)) + { + localizedValue = GetValue(0, returnDefaultValue); + } + + return localizedValue; + + LocalizedValue GetValue(int id /* storeId */, bool doFallback) + { + return helper.GetLocalizedValue( + settings, + id, + typeof(TSetting).Name, + invoker.Property.Name, + (Func)invoker, + requestLanguageIdOrObj, + doFallback, + ensureTwoPublishedLanguages, + detectEmptyHtml); + } + } + + #endregion + + #region GetLocalizedEnum + + /// + /// Get localized value of enum + /// + /// Enum + /// Enum value + /// Localization service + /// Work context + /// Whether to load the hint. + /// Localized value + public static string GetLocalizedEnum(this T enumValue, ILocalizationService localizationService, IWorkContext workContext, bool hint = false) where T : struct { - if (workContext == null) - throw new ArgumentNullException(nameof(workContext)); + Guard.NotNull(workContext, nameof(workContext)); - return GetLocalizedEnum(enumValue, localizationService, workContext.WorkingLanguage.Id); + return GetLocalizedEnum(enumValue, localizationService, workContext.WorkingLanguage.Id, hint); } /// @@ -348,30 +361,42 @@ public static string GetLocalizedEnum(this T enumValue, ILocalizationService /// Enum value /// Localization service /// Language identifier + /// Whether to load the hint. /// Localized value - public static string GetLocalizedEnum(this T enumValue, ILocalizationService localizationService, int languageId = 0) + public static string GetLocalizedEnum(this T enumValue, ILocalizationService localizationService, int languageId = 0, bool hint = false) where T : struct { - if (localizationService == null) - throw new ArgumentNullException(nameof(localizationService)); + Guard.NotNull(localizationService, nameof(localizationService)); - if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type"); + if (!typeof(T).IsEnum) + { + throw new ArgumentException("T must be an enumerated type."); + } - //localized value - string resourceName = string.Format("Enums.{0}.{1}", - typeof(T).ToString(), - //Convert.ToInt32(enumValue) + var resourceName = string.Format("Enums.{0}.{1}", + typeof(T).ToString(), enumValue.ToString()); - string result = localizationService.GetResource(resourceName, languageId, false, "", true); + if (hint) + { + resourceName += ".Hint"; + } + + var result = localizationService.GetResource(resourceName, languageId, false, "", true); - // Set default value if required - if (String.IsNullOrEmpty(result)) + // Set default value if required. + if (string.IsNullOrEmpty(result)) + { result = Inflector.Titleize(enumValue.ToString()); + } return result; } + #endregion + + #region Plugin Localization + /// /// Delete a locale resource /// @@ -391,9 +416,9 @@ public static void DeletePluginLocaleResource(this BasePlugin plugin, string res /// Localization service /// Language service /// Resource name - public static void DeletePluginLocaleResource(this BasePlugin plugin, - ILocalizationService localizationService, - ILanguageService languageService, + public static void DeletePluginLocaleResource(this BasePlugin plugin, + ILocalizationService localizationService, + ILanguageService languageService, string resourceName) { // actually plugin instance is not required @@ -425,8 +450,8 @@ public static void AddOrUpdatePluginLocaleResource(this BasePlugin plugin, { var localizationService = EngineContext.Current.Resolve(); var languageService = EngineContext.Current.Resolve(); - AddOrUpdatePluginLocaleResource(plugin, localizationService, - languageService, resourceName, resourceValue); + AddOrUpdatePluginLocaleResource(plugin, localizationService, + languageService, resourceName, resourceValue); } /// @@ -437,11 +462,11 @@ public static void AddOrUpdatePluginLocaleResource(this BasePlugin plugin, /// Language service /// Resource name /// Resource value - public static void AddOrUpdatePluginLocaleResource(this BasePlugin plugin, - ILocalizationService localizationService, - ILanguageService languageService, - string resourceName, - string resourceValue) + public static void AddOrUpdatePluginLocaleResource(this BasePlugin plugin, + ILocalizationService localizationService, + ILanguageService languageService, + string resourceName, + string resourceValue) { // Actually plugin instance is not required if (plugin == null) @@ -450,7 +475,7 @@ public static void AddOrUpdatePluginLocaleResource(this BasePlugin plugin, throw new ArgumentNullException("localizationService"); if (languageService == null) throw new ArgumentNullException("languageService"); - + foreach (var lang in languageService.GetAllLanguages(true)) { var lsr = localizationService.GetLocaleStringResourceByName(resourceName, lang.Id, false); @@ -487,63 +512,63 @@ public static void AddPluginLocaleResource(this BasePlugin plugin, ILocalization } } - /// - /// Get localized property value of a plugin - /// - /// Plugin - /// Plugin - /// Localization service - /// Name of the property - /// Language identifier - /// A value indicating whether to return default value (if localized is not found) - /// Localized value - public static string GetLocalizedValue(this T plugin, ILocalizationService localizationService, string propertyName, int languageId = 0, bool returnDefaultValue = true) - where T : IPlugin - { - if (plugin == null) - throw new ArgumentNullException(nameof(plugin)); - - if (plugin.PluginDescriptor == null) - throw new ArgumentNullException("PluginDescriptor cannot be loaded"); - - return plugin.PluginDescriptor.GetLocalizedValue(localizationService, propertyName, languageId, returnDefaultValue); - } - - /// - /// Get localized property value of a plugin - /// - /// Plugin descriptor - /// Localization service - /// Name of the property - /// Language identifier - /// A value indicating whether to return default value (if localized is not found) - /// Localized value - public static string GetLocalizedValue(this PluginDescriptor descriptor, ILocalizationService localizationService, string propertyName, int languageId = 0, bool returnDefaultValue = true) - { - if (localizationService == null) - throw new ArgumentNullException(nameof(localizationService)); - - if (descriptor == null) - throw new ArgumentNullException(nameof(descriptor)); - - if (propertyName == null) - throw new ArgumentNullException(nameof(propertyName)); - - string systemName = descriptor.SystemName; - string resourceName = string.Format("Plugins.{0}.{1}", propertyName, systemName); - string result = localizationService.GetResource(resourceName, languageId, false, "", true); - - if (String.IsNullOrEmpty(result) && returnDefaultValue) - { - var fastProp = FastProperty.GetProperty(descriptor.GetType(), propertyName); - if (fastProp != null) - { - result = fastProp.GetValue(descriptor) as string; - } - } - - return result; - } + /// + /// Get localized property value of a plugin + /// + /// Plugin + /// Plugin + /// Localization service + /// Name of the property + /// Language identifier + /// A value indicating whether to return default value (if localized is not found) + /// Localized value + public static string GetLocalizedValue(this T plugin, ILocalizationService localizationService, string propertyName, int languageId = 0, bool returnDefaultValue = true) + where T : IPlugin + { + if (plugin == null) + throw new ArgumentNullException(nameof(plugin)); + + if (plugin.PluginDescriptor == null) + throw new ArgumentNullException("PluginDescriptor cannot be loaded"); + + return plugin.PluginDescriptor.GetLocalizedValue(localizationService, propertyName, languageId, returnDefaultValue); + } + + /// + /// Get localized property value of a plugin + /// + /// Plugin descriptor + /// Localization service + /// Name of the property + /// Language identifier + /// A value indicating whether to return default value (if localized is not found) + /// Localized value + public static string GetLocalizedValue(this PluginDescriptor descriptor, ILocalizationService localizationService, string propertyName, int languageId = 0, bool returnDefaultValue = true) + { + if (localizationService == null) + throw new ArgumentNullException(nameof(localizationService)); + + if (descriptor == null) + throw new ArgumentNullException(nameof(descriptor)); + + if (propertyName == null) + throw new ArgumentNullException(nameof(propertyName)); + + string systemName = descriptor.SystemName; + string resourceName = string.Format("Plugins.{0}.{1}", propertyName, systemName); + string result = localizationService.GetResource(resourceName, languageId, false, "", true); + + if (String.IsNullOrEmpty(result) && returnDefaultValue) + { + var fastProp = FastProperty.GetProperty(descriptor.GetType(), propertyName); + if (fastProp != null) + { + result = fastProp.GetValue(descriptor) as string; + } + } + + return result; + } /// /// Save localized plugin descriptor value @@ -555,7 +580,7 @@ public static string GetLocalizedValue(this PluginDescriptor descriptor, ILocali /// Name of the property /// Localized value public static void SaveLocalizedValue(this T plugin, ILocalizationService localizationService, int languageId, - string propertyName, string value) where T : IPlugin + string propertyName, string value) where T : IPlugin { if (plugin == null) throw new ArgumentNullException("plugin"); @@ -563,7 +588,7 @@ public static void SaveLocalizedValue(this T plugin, ILocalizationService loc if (plugin.PluginDescriptor == null) throw new ArgumentNullException("PluginDescriptor cannot be loaded"); - plugin.PluginDescriptor.SaveLocalizedValue(localizationService, languageId, propertyName, value); + plugin.PluginDescriptor.SaveLocalizedValue(localizationService, languageId, propertyName, value); } /// @@ -575,7 +600,7 @@ public static void SaveLocalizedValue(this T plugin, ILocalizationService loc /// Name of the property /// Localized value public static void SaveLocalizedValue(this PluginDescriptor descriptor, ILocalizationService localizationService, int languageId, - string propertyName, string value) + string propertyName, string value) { if (localizationService == null) throw new ArgumentNullException(nameof(localizationService)); @@ -586,8 +611,8 @@ public static void SaveLocalizedValue(this PluginDescriptor descriptor, ILocaliz if (descriptor == null) throw new ArgumentNullException(nameof(descriptor)); - if (propertyName == null) - throw new ArgumentNullException(nameof(propertyName)); + if (propertyName == null) + throw new ArgumentNullException(nameof(propertyName)); string systemName = descriptor.SystemName; string resourceName = string.Format("Plugins.{0}.{1}", propertyName, systemName); @@ -628,8 +653,8 @@ public static void SaveLocalizedValue(this PluginDescriptor descriptor, ILocaliz /// /// Language /// XML - public static void ImportResourcesFromXml(this ILocalizationService service, - Language language, + public static void ImportResourcesFromXml(this ILocalizationService service, + Language language, string xml, string rootKey = null, bool sourceIsPlugin = false, @@ -647,5 +672,7 @@ public static void ImportResourcesFromXml(this ILocalizationService service, service.ImportResourcesFromXml(language, xmlDoc, rootKey, sourceIsPlugin, mode, updateTouchedResources); } + + #endregion } } diff --git a/src/Libraries/SmartStore.Services/Localization/LocalizationService.cs b/src/Libraries/SmartStore.Services/Localization/LocalizationService.cs index d85eafb668..abe7e3a8a9 100644 --- a/src/Libraries/SmartStore.Services/Localization/LocalizationService.cs +++ b/src/Libraries/SmartStore.Services/Localization/LocalizationService.cs @@ -17,83 +17,82 @@ using SmartStore.Core.Plugins; using SmartStore.Core.Localization; using System.Globalization; +using SmartStore.Core.IO; namespace SmartStore.Services.Localization { - public partial class LocalizationService : ILocalizationService + public partial class LocalizationService : ILocalizationService { - /// - /// 0 = segment (first 3 chars of key), 1 = language id - /// - const string LOCALESTRINGRESOURCES_SEGMENT_KEY = "localization:{0}-lang-{1}"; - const string LOCALESTRINGRESOURCES_SEGMENT_PATTERN = "localization:{0}*"; + /// + /// 0 = language id + /// + const string LOCALESTRINGRESOURCES_SEGMENT_KEY = "localization:{0}"; + const string LOCALESTRINGRESOURCES_SEGMENT_PATTERN = "localization:*"; - private readonly IRepository _lsrRepository; + private readonly IRepository _lsrRepo; private readonly IWorkContext _workContext; private readonly ILogger _logger; private readonly ILanguageService _languageService; private readonly ICacheManager _cacheManager; private readonly IDbContext _dbContext; - private readonly IEventPublisher _eventPublisher; - private int _notFoundLogCount = 0; - private int? _defaultLanguageId; + private int _notFoundLogCount = 0; + private int? _defaultLanguageId; public LocalizationService( - ICacheManager cacheManager, - ILogger logger, - IWorkContext workContext, - IRepository lsrRepository, - ILanguageService languageService, - IEventPublisher eventPublisher) + ICacheManager cacheManager, + ILogger logger, + IWorkContext workContext, + IRepository lsrRepository, + ILanguageService languageService) { _cacheManager = cacheManager; _logger = logger; _workContext = workContext; - _lsrRepository = lsrRepository; + _lsrRepo = lsrRepository; _languageService = languageService; - _dbContext = lsrRepository.Context; - _eventPublisher = eventPublisher; + _dbContext = lsrRepository.Context; } public virtual void DeleteLocaleStringResource(LocaleStringResource resource) { - Guard.NotNull(resource, nameof(resource)); + Guard.NotNull(resource, nameof(resource)); // cache - ClearCacheSegment(resource.ResourceName, resource.LanguageId); - + ClearCacheSegment(resource.LanguageId); + // db - _lsrRepository.Delete(resource); + _lsrRepo.Delete(resource); } - public virtual int DeleteLocaleStringResources(string key, bool keyIsRootKey = true) { - int result = 0; + public virtual int DeleteLocaleStringResources(string key, bool keyIsRootKey = true) + { + int result = 0; - if (key.HasValue()) + if (key.HasValue()) { - try + try { - var sqlDelete = "Delete From LocaleStringResource Where ResourceName Like '{0}%'".FormatWith(key.EndsWith(".") || !keyIsRootKey ? key : key + "."); - result = _dbContext.ExecuteSqlCommand(sqlDelete); + var sqlDelete = "Delete From LocaleStringResource Where ResourceName Like '{0}%'".FormatWith(key.EndsWith(".") || !keyIsRootKey ? key : key + "."); + result = _dbContext.ExecuteSqlCommand(sqlDelete); - ClearCacheSegment(key); - } - catch (Exception exc) + ClearCacheSegment(null); + } + catch (Exception exc) { - exc.Dump(); - } - } + exc.Dump(); + } + } - return result; - } + return result; + } public virtual LocaleStringResource GetLocaleStringResourceById(int localeStringResourceId) { if (localeStringResourceId == 0) return null; - var localeStringResource = _lsrRepository.GetById(localeStringResourceId); + var localeStringResource = _lsrRepo.GetById(localeStringResourceId); return localeStringResource; } @@ -107,7 +106,7 @@ public virtual LocaleStringResource GetLocaleStringResourceByName(string resourc public virtual LocaleStringResource GetLocaleStringResourceByName(string resourceName, int languageId, bool logIfNotFound = true) { - var query = from lsr in _lsrRepository.Table + var query = from lsr in _lsrRepo.Table orderby lsr.ResourceName where lsr.LanguageId == languageId && lsr.ResourceName == resourceName select lsr; @@ -121,160 +120,150 @@ orderby lsr.ResourceName public virtual IQueryable All(int languageId) { - var query = from lsr in _lsrRepository.Table + var query = from lsr in _lsrRepo.Table orderby lsr.ResourceName where lsr.LanguageId == languageId select lsr; - return query; + return query; } - public virtual IList GetResourcesByPattern(string pattern, int languageId) - { - Guard.NotEmpty(pattern, nameof(pattern)); + public virtual IList GetResourcesByPattern(string pattern, int languageId) + { + Guard.NotEmpty(pattern, nameof(pattern)); - var query = from l in _lsrRepository.Table - where l.ResourceName.StartsWith(pattern) && l.LanguageId == languageId - select l; + var query = from l in _lsrRepo.Table + where l.ResourceName.StartsWith(pattern) && l.LanguageId == languageId + select l; - var resources = query.ToList(); - return resources; - } + var resources = query.ToList(); + return resources; + } - public virtual void InsertLocaleStringResource(LocaleStringResource resource) + public virtual void InsertLocaleStringResource(LocaleStringResource resource) { - Guard.NotNull(resource, nameof(resource)); + Guard.NotNull(resource, nameof(resource)); - _lsrRepository.Insert(resource); + _lsrRepo.Insert(resource); - // cache - ClearCacheSegment(resource.ResourceName, resource.LanguageId); + // cache + ClearCacheSegment(resource.LanguageId); } public virtual void UpdateLocaleStringResource(LocaleStringResource resource) { - Guard.NotNull(resource, nameof(resource)); + Guard.NotNull(resource, nameof(resource)); // cache - object origKey = null; - if (_dbContext.TryGetModifiedProperty(resource, "ResourceName", out origKey)) - { - ClearCacheSegment((string)origKey, resource.LanguageId); - } - ClearCacheSegment(resource.ResourceName, resource.LanguageId); - - _lsrRepository.Update(resource); + ClearCacheSegment(resource.LanguageId); + + _lsrRepo.Update(resource); } - protected virtual IDictionary GetCacheSegment(string forKey, int languageId) - { - Guard.NotEmpty(forKey, nameof(forKey)); - - var segmentKey = GetSegmentKeyPart(forKey); - var cacheKey = BuildCacheSegmentKey(segmentKey, languageId); - - return _cacheManager.Get(cacheKey, () => - { - var resources = _lsrRepository.TableUntracked - .Where(x => x.ResourceName.StartsWith(segmentKey) && x.LanguageId == languageId) - //.OrderBy(x => x.ResourceName) - .ToList(); - - var dict = new Dictionary(resources.Count); - - foreach (var res in resources) - { - dict[res.ResourceName.ToLowerInvariant()] = res.ResourceValue; - } - - return dict; - }); - } - - /// - /// Clears the cached resource segment from the cache - /// - /// The resource key for which a segment key should be created - /// Language Id. If null, segments for all cached languages will be invalidated - protected virtual void ClearCacheSegment(string forKey, int? languageId = null) - { - var segmentKey = GetSegmentKeyPart(forKey); - - if (languageId.HasValue && languageId.Value > 0) - { - _cacheManager.Remove(BuildCacheSegmentKey(segmentKey, languageId.Value)); - } - else - { - _cacheManager.RemoveByPattern(LOCALESTRINGRESOURCES_SEGMENT_PATTERN.FormatInvariant(segmentKey)); - } - } + protected virtual IDictionary GetCacheSegment(int languageId) + { + var cacheKey = BuildCacheSegmentKey(languageId); + + return _cacheManager.Get(cacheKey, () => + { + var resources = _lsrRepo.TableUntracked + .Where(x => x.LanguageId == languageId) + .Select(x => new { x.ResourceName, x.ResourceValue }) + //.OrderBy(x => x.ResourceName) + .ToList(); + + var dict = new Dictionary(resources.Count); + + foreach (var res in resources) + { + dict[res.ResourceName.ToLowerInvariant()] = res.ResourceValue; + } + + return dict; + }); + } + + /// + /// Clears the cached resource segment from the cache + /// + /// Language Id. If null, segments for all cached languages will be invalidated + protected virtual void ClearCacheSegment(int? languageId = null) + { + if (languageId.HasValue && languageId.Value > 0) + { + _cacheManager.Remove(BuildCacheSegmentKey(languageId.Value)); + } + else + { + _cacheManager.RemoveByPattern(LOCALESTRINGRESOURCES_SEGMENT_PATTERN); + } + } public virtual string GetResource( - string resourceKey, - int languageId = 0, - bool logIfNotFound = true, - string defaultValue = "", - bool returnEmptyIfNotFound = false) + string resourceKey, + int languageId = 0, + bool logIfNotFound = true, + string defaultValue = "", + bool returnEmptyIfNotFound = false) { if (languageId <= 0) { if (_workContext.WorkingLanguage == null) - { - return defaultValue; - } - + { + return defaultValue; + } + languageId = _workContext.WorkingLanguage.Id; } - - string result = string.Empty; + + string result = string.Empty; resourceKey = resourceKey.EmptyNull().Trim().ToLowerInvariant(); - var cachedSegment = GetCacheSegment(resourceKey, languageId); + var cachedSegment = GetCacheSegment(languageId); if (!cachedSegment.TryGetValue(resourceKey, out result)) { - if (logIfNotFound) - { - if (_notFoundLogCount < 50) - { - _logger.Warn(string.Format("Resource string ({0}) does not exist. Language ID = {1}", resourceKey, languageId)); - } - else if (_notFoundLogCount == 50) - { - _logger.Warn("Too many language resources do not exist (> 50). Stopped logging missing resources to prevent performance drop."); - } - - _notFoundLogCount++; - } - + if (logIfNotFound) + { + if (_notFoundLogCount < 50) + { + _logger.Warn(string.Format("Resource string ({0}) does not exist. Language ID = {1}", resourceKey, languageId)); + } + else if (_notFoundLogCount == 50) + { + _logger.Warn("Too many language resources do not exist (> 50). Stopped logging missing resources to prevent performance drop."); + } + + _notFoundLogCount++; + } + if (!String.IsNullOrEmpty(defaultValue)) { result = defaultValue; } else { - // try fallback to default language - if (!_defaultLanguageId.HasValue) - { - _defaultLanguageId = _languageService.GetDefaultLanguageId(); - } - - var defaultLangId = _defaultLanguageId.Value; - if (defaultLangId > 0 && defaultLangId != languageId) - { - var fallbackResult = GetResource(resourceKey, defaultLangId, false, resourceKey); - if (fallbackResult != resourceKey) - { - result = fallbackResult; - } - } - - if (!returnEmptyIfNotFound && result.IsEmpty()) - { - result = resourceKey; - } + // try fallback to default language + if (!_defaultLanguageId.HasValue) + { + _defaultLanguageId = _languageService.GetDefaultLanguageId(); + } + + var defaultLangId = _defaultLanguageId.Value; + if (defaultLangId > 0 && defaultLangId != languageId) + { + var fallbackResult = GetResource(resourceKey, defaultLangId, false, resourceKey); + if (fallbackResult != resourceKey) + { + result = fallbackResult; + } + } + + if (!returnEmptyIfNotFound && result.IsEmpty()) + { + result = resourceKey; + } } } @@ -283,7 +272,7 @@ public virtual string GetResource( public virtual string ExportResourcesToXml(Language language) { - Guard.NotNull(language, nameof(language)); + Guard.NotNull(language, nameof(language)); var sb = new StringBuilder(); var stringWriter = new StringWriter(sb); @@ -292,20 +281,20 @@ public virtual string ExportResourcesToXml(Language language) xmlWriter.WriteStartElement("Language"); xmlWriter.WriteAttributeString("Name", language.Name); - using (var scope = new DbContextScope(forceNoTracking: true)) - { - var resources = All(language.Id).ToList(); - foreach (var resource in resources) - { - if (resource.IsFromPlugin.GetValueOrDefault() == false) - { - xmlWriter.WriteStartElement("LocaleResource"); - xmlWriter.WriteAttributeString("Name", resource.ResourceName); - xmlWriter.WriteElementString("Value", null, resource.ResourceValue); - xmlWriter.WriteEndElement(); - } - } - } + using (var scope = new DbContextScope(forceNoTracking: true)) + { + var resources = All(language.Id).ToList(); + foreach (var resource in resources) + { + if (resource.IsFromPlugin.GetValueOrDefault() == false) + { + xmlWriter.WriteStartElement("LocaleResource"); + xmlWriter.WriteAttributeString("Name", resource.ResourceName); + xmlWriter.WriteElementString("Value", null, resource.ResourceValue); + xmlWriter.WriteEndElement(); + } + } + } xmlWriter.WriteEndElement(); xmlWriter.WriteEndDocument(); @@ -313,277 +302,283 @@ public virtual string ExportResourcesToXml(Language language) return stringWriter.ToString(); } - public virtual void ImportPluginResourcesFromXml( - PluginDescriptor pluginDescriptor, - IList targetList = null, - bool updateTouchedResources = true, - IList filterLanguages = null) - { - var directory = new DirectoryInfo(Path.Combine(pluginDescriptor.Assembly.OriginalFile.Directory.FullName, "Localization")); - - if (!directory.Exists) - return; - - if (targetList == null && updateTouchedResources) - { - DeleteLocaleStringResources(pluginDescriptor.ResourceRootKey); - } - - var unprocessedLanguages = new List(); - - var defaultLanguageId = _languageService.GetDefaultLanguageId(); - var languages = filterLanguages ?? _languageService.GetAllLanguages(true); - - string code = null; - foreach (var language in languages) - { - code = ImportPluginResourcesForLanguage( - language, - null, - directory, - pluginDescriptor.ResourceRootKey, - targetList, - updateTouchedResources, - false); - - if (code == null) - { - unprocessedLanguages.Add(language); - } - } - - if (filterLanguages == null && unprocessedLanguages.Count > 0) - { - // There were unprocessed languages (no corresponding resource file could be found). - // In order for GetResource() to be able to gracefully fallback to the default language's resources, - // we need to import resources for the current default language.... - var processedLanguages = languages.Except(unprocessedLanguages).ToList(); - if (!processedLanguages.Any(x => x.Id == defaultLanguageId)) - { - // ...but only if no resource file could be mapped to the default language before, - // namely because in this case the following operation would be redundant. - var defaultLanguage = _languageService.GetLanguageById(_languageService.GetDefaultLanguageId()); - if (defaultLanguage != null) - { - ImportPluginResourcesForLanguage( - defaultLanguage, - "en-us", - directory, - pluginDescriptor.ResourceRootKey, - targetList, - updateTouchedResources, - true); - } - } - } - } - - private string ImportPluginResourcesForLanguage( - Language language, - string fileCode, - DirectoryInfo directory, - string resourceRootKey, - IList targetList, - bool updateTouchedResources, - bool canFallBackToAnyResourceFile) - { - var fileNamePattern = "resources.{0}.xml"; - - var codeCandidates = GetResourceFileCodeCandidates( - fileCode ?? language.LanguageCulture, - directory, - canFallBackToAnyResourceFile); - - string path = null; - string code = null; - - foreach (var candidate in codeCandidates) - { - var pathCandidate = Path.Combine(directory.FullName, fileNamePattern.FormatInvariant(candidate)); - if (File.Exists(pathCandidate)) - { - code = candidate; - path = pathCandidate; - break; - } - } - - if (code != null) - { - var doc = new XmlDocument(); - - doc.Load(path); - doc = FlattenResourceFile(doc); - - if (targetList == null) - { - ImportResourcesFromXml(language, doc, resourceRootKey, true, updateTouchedResources: updateTouchedResources); - } - else - { - var nodes = doc.SelectNodes(@"//Language/LocaleResource"); - foreach (XmlNode node in nodes) - { - var valueNode = node.SelectSingleNode("Value"); - var res = new LocaleStringResource - { - ResourceName = node.Attributes["Name"].InnerText.Trim(), - ResourceValue = (valueNode == null ? "" : valueNode.InnerText), - LanguageId = language.Id, - IsFromPlugin = true - }; - - if (res.ResourceName.HasValue()) - { - targetList.Add(res); - } - } - } - } - - return code; - } - - private IEnumerable GetResourceFileCodeCandidates(string code, DirectoryInfo directory, bool canFallBackToAnyResourceFile) - { - // exact match (de-DE) - yield return code; - - // neutral culture (de) - var ci = CultureInfo.GetCultureInfo(code); - if (ci.Parent != null && !ci.IsNeutralCulture) - { - code = ci.Parent.Name; - yield return code; - } - - var rgFileName = new Regex("^resources.(.+?).xml$", RegexOptions.Compiled | RegexOptions.IgnoreCase); - - // any other region with same language (de-*) - foreach (var fi in directory.EnumerateFiles("resources.{0}-*.xml".FormatInvariant(code), SearchOption.TopDirectoryOnly)) - { - code = rgFileName.Match(fi.Name).Groups[1].Value; - if (LocalizationHelper.IsValidCultureCode(code)) - { - yield return code; - yield break; - } - } - - if (canFallBackToAnyResourceFile) - { - foreach (var fi in directory.EnumerateFiles("resources.*.xml", SearchOption.TopDirectoryOnly)) - { - code = rgFileName.Match(fi.Name).Groups[1].Value; - if (LocalizationHelper.IsValidCultureCode(code)) - { - yield return code; - yield break; - } - } - } - } - - public virtual int ImportResourcesFromXml( - Language language, - XmlDocument xmlDocument, - string rootKey = null, - bool sourceIsPlugin = false, + public DirectoryHasher CreatePluginResourcesHasher(PluginDescriptor pluginDescriptor) + { + return new DirectoryHasher(Path.Combine(pluginDescriptor.PhysicalPath, "Localization"), "resources.*.xml"); + } + + public virtual void ImportPluginResourcesFromXml( + PluginDescriptor pluginDescriptor, + IList targetList = null, + bool updateTouchedResources = true, + IList filterLanguages = null) + { + var directory = new DirectoryInfo(Path.Combine(pluginDescriptor.PhysicalPath, "Localization")); + + if (!directory.Exists) + return; + + if (targetList == null && updateTouchedResources) + { + DeleteLocaleStringResources(pluginDescriptor.ResourceRootKey); + } + + var unprocessedLanguages = new List(); + + var defaultLanguageId = _languageService.GetDefaultLanguageId(); + var languages = filterLanguages ?? _languageService.GetAllLanguages(true); + + string code = null; + foreach (var language in languages) + { + code = ImportPluginResourcesForLanguage( + language, + null, + directory, + pluginDescriptor.ResourceRootKey, + targetList, + updateTouchedResources, + false); + + if (code == null) + { + unprocessedLanguages.Add(language); + } + } + + if (filterLanguages == null && unprocessedLanguages.Count > 0) + { + // There were unprocessed languages (no corresponding resource file could be found). + // In order for GetResource() to be able to gracefully fallback to the default language's resources, + // we need to import resources for the current default language.... + var processedLanguages = languages.Except(unprocessedLanguages).ToList(); + if (!processedLanguages.Any(x => x.Id == defaultLanguageId)) + { + // ...but only if no resource file could be mapped to the default language before, + // namely because in this case the following operation would be redundant. + var defaultLanguage = _languageService.GetLanguageById(_languageService.GetDefaultLanguageId()); + if (defaultLanguage != null) + { + ImportPluginResourcesForLanguage( + defaultLanguage, + "en-us", + directory, + pluginDescriptor.ResourceRootKey, + targetList, + updateTouchedResources, + true); + } + } + } + + try + { + var hasher = CreatePluginResourcesHasher(pluginDescriptor); + hasher.Persist(); + } + catch { } + } + + private string ImportPluginResourcesForLanguage( + Language language, + string fileCode, + DirectoryInfo directory, + string resourceRootKey, + IList targetList, + bool updateTouchedResources, + bool canFallBackToAnyResourceFile) + { + var fileNamePattern = "resources.{0}.xml"; + + var codeCandidates = GetResourceFileCodeCandidates( + fileCode ?? language.LanguageCulture, + directory, + canFallBackToAnyResourceFile); + + string path = null; + string code = null; + + foreach (var candidate in codeCandidates) + { + var pathCandidate = Path.Combine(directory.FullName, fileNamePattern.FormatInvariant(candidate)); + if (File.Exists(pathCandidate)) + { + code = candidate; + path = pathCandidate; + break; + } + } + + if (code != null) + { + var doc = new XmlDocument(); + + doc.Load(path); + doc = FlattenResourceFile(doc); + + if (targetList == null) + { + ImportResourcesFromXml(language, doc, resourceRootKey, true, updateTouchedResources: updateTouchedResources); + } + else + { + var nodes = doc.SelectNodes(@"//Language/LocaleResource"); + foreach (XmlNode node in nodes) + { + var valueNode = node.SelectSingleNode("Value"); + var res = new LocaleStringResource + { + ResourceName = node.Attributes["Name"].InnerText.Trim(), + ResourceValue = (valueNode == null ? "" : valueNode.InnerText), + LanguageId = language.Id, + IsFromPlugin = true + }; + + if (res.ResourceName.HasValue()) + { + targetList.Add(res); + } + } + } + } + + return code; + } + + private IEnumerable GetResourceFileCodeCandidates(string code, DirectoryInfo directory, bool canFallBackToAnyResourceFile) + { + // exact match (de-DE) + yield return code; + + // neutral culture (de) + var ci = CultureInfo.GetCultureInfo(code); + if (ci.Parent != null && !ci.IsNeutralCulture) + { + code = ci.Parent.Name; + yield return code; + } + + var rgFileName = new Regex("^resources.(.+?).xml$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + + // any other region with same language (de-*) + foreach (var fi in directory.EnumerateFiles("resources.{0}-*.xml".FormatInvariant(code), SearchOption.TopDirectoryOnly)) + { + code = rgFileName.Match(fi.Name).Groups[1].Value; + if (LocalizationHelper.IsValidCultureCode(code)) + { + yield return code; + yield break; + } + } + + if (canFallBackToAnyResourceFile) + { + foreach (var fi in directory.EnumerateFiles("resources.*.xml", SearchOption.TopDirectoryOnly)) + { + code = rgFileName.Match(fi.Name).Groups[1].Value; + if (LocalizationHelper.IsValidCultureCode(code)) + { + yield return code; + yield break; + } + } + } + } + + public virtual int ImportResourcesFromXml( + Language language, + XmlDocument xmlDocument, + string rootKey = null, + bool sourceIsPlugin = false, ImportModeFlags mode = ImportModeFlags.Insert | ImportModeFlags.Update, bool updateTouchedResources = false) - { - using (var scope = new DbContextScope(autoDetectChanges: false, proxyCreation: false, validateOnSave: false, autoCommit: false, forceNoTracking: true, hooksEnabled: false)) - { - var toAdd = new List(); - var toUpdate = new List(); - var nodes = xmlDocument.SelectNodes(@"//Language/LocaleResource"); - - var resources = language.LocaleStringResources.ToDictionarySafe(x => x.ResourceName, StringComparer.OrdinalIgnoreCase); - - LocaleStringResource resource; - - foreach (var xel in nodes.Cast()) - { - string name = xel.GetAttribute("Name").TrimSafe(); - string value = ""; - var valueNode = xel.SelectSingleNode("Value"); - if (valueNode != null) - value = valueNode.InnerText; - - if (String.IsNullOrEmpty(name)) - continue; - - if (rootKey.HasValue()) - { - if (!xel.GetAttributeText("AppendRootKey").IsCaseInsensitiveEqual("false")) - name = "{0}.{1}".FormatWith(rootKey, name); - } - - resource = null; - - // do not use "Insert"/"Update" methods because they clear cache - // let's bulk insert - //var resource = language.LocaleStringResources.Where(x => x.ResourceName.Equals(name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); - if (resources.TryGetValue(name, out resource)) - { - if (mode.HasFlag(ImportModeFlags.Update)) - { - if (updateTouchedResources || !resource.IsTouched.GetValueOrDefault()) - { - if (value != resource.ResourceValue) - { - resource.ResourceValue = value; - resource.IsTouched = null; - toUpdate.Add(resource); - } - } - } - } - else - { - if (mode.HasFlag(ImportModeFlags.Insert)) - { - toAdd.Add( - new LocaleStringResource - { - LanguageId = language.Id, - ResourceName = name, - ResourceValue = value, - IsFromPlugin = sourceIsPlugin - }); - } - } - } - - //_lsrRepository.AutoCommitEnabled = true; - - if (toAdd.Any() || toUpdate.Any()) - { - var segmentKeys = new HashSet(); - - toAdd.Each(x => segmentKeys.Add(GetSegmentKeyPart(x.ResourceName))); - toUpdate.Each(x => segmentKeys.Add(GetSegmentKeyPart(x.ResourceName))); - - _lsrRepository.InsertRange(toAdd); - toAdd.Clear(); - - _lsrRepository.UpdateRange(toUpdate); - toUpdate.Clear(); - - int num = _lsrRepository.Context.SaveChanges(); - - // clear cache - foreach (var segmentKey in segmentKeys) - { - ClearCacheSegment(segmentKey, language.Id); - } - - return num; - } - - return 0; - } - } + { + using (var scope = new DbContextScope(autoDetectChanges: false, proxyCreation: false, validateOnSave: false, autoCommit: false, forceNoTracking: true, hooksEnabled: false)) + { + var toAdd = new List(); + var toUpdate = new List(); + var nodes = xmlDocument.SelectNodes(@"//Language/LocaleResource"); + + var resources = language.LocaleStringResources.ToDictionarySafe(x => x.ResourceName, StringComparer.OrdinalIgnoreCase); + + LocaleStringResource resource; + + foreach (var xel in nodes.Cast()) + { + string name = xel.GetAttribute("Name").TrimSafe(); + string value = ""; + var valueNode = xel.SelectSingleNode("Value"); + if (valueNode != null) + value = valueNode.InnerText; + + if (String.IsNullOrEmpty(name)) + continue; + + if (rootKey.HasValue()) + { + if (!xel.GetAttributeText("AppendRootKey").IsCaseInsensitiveEqual("false")) + name = "{0}.{1}".FormatWith(rootKey, name); + } + + resource = null; + + // do not use "Insert"/"Update" methods because they clear cache + // let's bulk insert + //var resource = language.LocaleStringResources.Where(x => x.ResourceName.Equals(name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); + if (resources.TryGetValue(name, out resource)) + { + if (mode.HasFlag(ImportModeFlags.Update)) + { + if (updateTouchedResources || !resource.IsTouched.GetValueOrDefault()) + { + if (value != resource.ResourceValue) + { + resource.ResourceValue = value; + resource.IsTouched = null; + toUpdate.Add(resource); + } + } + } + } + else + { + if (mode.HasFlag(ImportModeFlags.Insert)) + { + toAdd.Add( + new LocaleStringResource + { + LanguageId = language.Id, + ResourceName = name, + ResourceValue = value, + IsFromPlugin = sourceIsPlugin + }); + } + } + } + + //_lsrRepository.AutoCommitEnabled = true; + + if (toAdd.Any() || toUpdate.Any()) + { + var segmentKeys = new HashSet(); + + _lsrRepo.InsertRange(toAdd); + toAdd.Clear(); + + _lsrRepo.UpdateRange(toUpdate); + toUpdate.Clear(); + + int num = _lsrRepo.Context.SaveChanges(); + + // clear cache + ClearCacheSegment(language.Id); + + return num; + } + + return 0; + } + } public virtual XmlDocument FlattenResourceFile(XmlDocument source) { @@ -647,19 +642,19 @@ private void RecursivelyWriteResource(LocaleStringResourceParent resource, XmlWr writer.WriteString(resource.NameWithNamespace); writer.WriteEndAttribute(); - if (resource.AppendRootKey.HasValue) - { - writer.WriteStartAttribute("AppendRootKey", ""); - writer.WriteString(resource.AppendRootKey.Value ? "true" : "false"); - writer.WriteEndAttribute(); - parentAppendRootKey = resource.AppendRootKey; - } - else if (parentAppendRootKey.HasValue) - { - writer.WriteStartAttribute("AppendRootKey", ""); - writer.WriteString(parentAppendRootKey.Value ? "true" : "false"); - writer.WriteEndAttribute(); - } + if (resource.AppendRootKey.HasValue) + { + writer.WriteStartAttribute("AppendRootKey", ""); + writer.WriteString(resource.AppendRootKey.Value ? "true" : "false"); + writer.WriteEndAttribute(); + parentAppendRootKey = resource.AppendRootKey; + } + else if (parentAppendRootKey.HasValue) + { + writer.WriteStartAttribute("AppendRootKey", ""); + writer.WriteString(parentAppendRootKey.Value ? "true" : "false"); + writer.WriteEndAttribute(); + } writer.WriteStartElement("Value", ""); writer.WriteString(resource.ResourceValue); @@ -670,7 +665,7 @@ private void RecursivelyWriteResource(LocaleStringResourceParent resource, XmlWr foreach (var child in resource.ChildLocaleStringResources) { - RecursivelyWriteResource(child, writer, resource.AppendRootKey ?? parentAppendRootKey); + RecursivelyWriteResource(child, writer, resource.AppendRootKey ?? parentAppendRootKey); } } @@ -685,16 +680,10 @@ private void RecursivelySortChildrenResource(LocaleStringResourceParent resource } } - private string BuildCacheSegmentKey(string segment, int languageId) - { - return String.Format(LOCALESTRINGRESOURCES_SEGMENT_KEY, segment, languageId); - } - - private string GetSegmentKeyPart(string forKey) - { - return forKey.Substring(0, Math.Min(forKey.Length, 3)).ToLowerInvariant(); - } - + private string BuildCacheSegmentKey(int languageId) + { + return String.Format(LOCALESTRINGRESOURCES_SEGMENT_KEY, languageId); + } private class LocaleStringResourceParent : LocaleStringResource { @@ -715,11 +704,11 @@ public LocaleStringResourceParent(XmlNode localStringResource, string nameSpace } ResourceName = resName; - var appendRootKeyAttribute = localStringResource.Attributes["AppendRootKey"]; - if (appendRootKeyAttribute != null) - { - AppendRootKey = appendRootKeyAttribute.Value.ToBool(true); - } + var appendRootKeyAttribute = localStringResource.Attributes["AppendRootKey"]; + if (appendRootKeyAttribute != null) + { + AppendRootKey = appendRootKeyAttribute.Value.ToBool(true); + } if (resValueNode == null || string.IsNullOrEmpty(resValueNode.InnerText.Trim())) { @@ -743,7 +732,7 @@ public LocaleStringResourceParent(XmlNode localStringResource, string nameSpace public bool IsPersistable { get; set; } - public bool? AppendRootKey { get; set; } + public bool? AppendRootKey { get; set; } public string NameWithNamespace { diff --git a/src/Libraries/SmartStore.Services/Localization/LocalizedEntityHelper.cs b/src/Libraries/SmartStore.Services/Localization/LocalizedEntityHelper.cs new file mode 100644 index 0000000000..2dd34267c7 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Localization/LocalizedEntityHelper.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using SmartStore.Core; +using SmartStore.Core.Domain.Localization; +using SmartStore.Core.Html; +using SmartStore.Services.Seo; + +namespace SmartStore.Services.Localization +{ + public class LocalizedEntityHelper + { + private readonly ILanguageService _languageService; + private readonly ILocalizedEntityService _localizedEntityService; + private readonly IUrlRecordService _urlRecordService; + private readonly IWorkContext _workContext; + + private readonly int _languageCount; + private readonly Language _defaultLanguage; + + public LocalizedEntityHelper( + ILanguageService languageService, + ILocalizedEntityService localizedEntityService, + IUrlRecordService urlRecordService, + IWorkContext workContext) + { + _languageService = languageService; + _localizedEntityService = localizedEntityService; + _urlRecordService = urlRecordService; + _workContext = workContext; + + _languageCount = _languageService.GetLanguagesCount(false); + _defaultLanguage = _languageService.GetLanguageById(_languageService.GetDefaultLanguageId()); + } + + public LocalizedValue GetLocalizedValue(T obj, + int id, // T is BaseEntity = EntityId, T is ISetting = StoreId + string localeKeyGroup, + string localeKey, + Func fallback, + object requestLanguageIdOrObj, // Id or Language + bool returnDefaultValue = true, + bool ensureTwoPublishedLanguages = true, + bool detectEmptyHtml = false) + where T : class + { + if (obj == null) + throw new ArgumentNullException(nameof(obj)); + + TProp result = default; + var str = string.Empty; + + Language currentLanguage = null; + Language requestLanguage = null; + + if (!(requestLanguageIdOrObj is Language)) + { + if (requestLanguageIdOrObj is int requestLanguageId) + { + requestLanguage = _languageService.GetLanguageById(requestLanguageId); + } + } + else + { + requestLanguage = (Language)requestLanguageIdOrObj; + } + + if (requestLanguage == null) + { + requestLanguage = _workContext.WorkingLanguage; + } + + // Ensure that we have at least two published languages + var loadLocalizedValue = true; + if (ensureTwoPublishedLanguages) + { + loadLocalizedValue = _languageCount > 1; + } + + // Localized value + if (loadLocalizedValue) + { + str = _localizedEntityService.GetLocalizedValue(requestLanguage.Id, id, localeKeyGroup, localeKey); + + if (detectEmptyHtml && HtmlUtils.IsEmptyHtml(str)) + { + str = string.Empty; + } + + if (!string.IsNullOrEmpty(str)) + { + currentLanguage = requestLanguage; + result = str.Convert(CultureInfo.InvariantCulture); + } + } + + // Set default value if required + if (returnDefaultValue && string.IsNullOrEmpty(str)) + { + currentLanguage = _defaultLanguage; + result = fallback(obj); + } + + if (currentLanguage == null) + { + currentLanguage = requestLanguage; + } + + return new LocalizedValue(result, requestLanguage, currentLanguage); + } + + public string GetSeName( + string entityName, + int entityId, + int? languageId, + bool returnDefaultValue = true, + bool ensureTwoPublishedLanguages = true) + { + string result = string.Empty; + + if (languageId == null) + { + languageId = _workContext.WorkingLanguage.Id; + } + + if (languageId > 0) + { + // Ensure that we have at least two published languages + bool loadLocalizedValue = true; + if (ensureTwoPublishedLanguages) + { + loadLocalizedValue = _languageCount > 1; + } + + // Localized value + if (loadLocalizedValue) + { + result = _urlRecordService.GetActiveSlug(entityId, entityName, languageId.Value); + } + } + + // Set default value if required + if (String.IsNullOrEmpty(result) && returnDefaultValue) + { + result = _urlRecordService.GetActiveSlug(entityId, entityName, 0); + } + + return result; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Localization/LocalizedEntityService.cs b/src/Libraries/SmartStore.Services/Localization/LocalizedEntityService.cs index 8d3c9bd41c..2371b71bc7 100644 --- a/src/Libraries/SmartStore.Services/Localization/LocalizedEntityService.cs +++ b/src/Libraries/SmartStore.Services/Localization/LocalizedEntityService.cs @@ -3,174 +3,176 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading; using SmartStore.Core; using SmartStore.Core.Caching; +using SmartStore.Core.Configuration; using SmartStore.Core.Data; using SmartStore.Core.Domain.Common; using SmartStore.Core.Domain.Localization; namespace SmartStore.Services.Localization { - /// - /// Provides information about localizable entities - /// - public partial class LocalizedEntityService : ScopedServiceBase, ILocalizedEntityService + /// + /// Provides information about localizable entities + /// + public partial class LocalizedEntityService : ScopedServiceBase, ILocalizedEntityService { - /// - /// 0 = segment (keygroup.key.idrange), 1 = language id - /// - const string LOCALIZEDPROPERTY_SEGMENT_KEY = "localizedproperty:{0}-lang-{1}"; - const string LOCALIZEDPROPERTY_SEGMENT_PATTERN = "localizedproperty:{0}*"; - const string LOCALIZEDPROPERTY_ALLSEGMENTS_PATTERN = "localizedproperty:*"; - - private readonly IRepository _localizedPropertyRepository; + /// + /// 0 = segment (keygroup.key.idrange), 1 = language id + /// + const string LOCALIZEDPROPERTY_SEGMENT_KEY = "localizedproperty:{0}-lang-{1}"; + const string LOCALIZEDPROPERTY_SEGMENT_PATTERN = "localizedproperty:{0}*"; + const string LOCALIZEDPROPERTY_ALLSEGMENTS_PATTERN = "localizedproperty:*"; + + private readonly IRepository _localizedPropertyRepository; private readonly ICacheManager _cacheManager; - private readonly PerformanceSettings _performanceSettings; + private readonly PerformanceSettings _performanceSettings; - private readonly IDictionary _prefetchedCollections; - private static int _lastCacheSegmentSize = -1; + private readonly IDictionary _prefetchedCollections; + private static int _lastCacheSegmentSize = -1; public LocalizedEntityService( - ICacheManager cacheManager, - IRepository localizedPropertyRepository, - PerformanceSettings performanceSettings) + ICacheManager cacheManager, + IRepository localizedPropertyRepository, + PerformanceSettings performanceSettings) { _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() - { - _cacheManager.RemoveByPattern(LOCALIZEDPROPERTY_ALLSEGMENTS_PATTERN); - } - - protected virtual IDictionary GetCacheSegment(string localeKeyGroup, string localeKey, int entityId, int languageId) - { - Guard.NotEmpty(localeKeyGroup, nameof(localeKeyGroup)); - Guard.NotEmpty(localeKey, nameof(localeKey)); - - var segmentKey = GetSegmentKeyPart(localeKeyGroup, localeKey, entityId, out var minEntityId, out var maxEntityId); - var cacheKey = BuildCacheSegmentKey(segmentKey, languageId); - - // TODO: (MC) skip caching product.fulldescription (?), OR - // ...additionally segment by entity id ranges. - - return _cacheManager.Get(cacheKey, () => - { - var properties = _localizedPropertyRepository.TableUntracked - .Where(x => x.EntityId >= minEntityId && x.EntityId <= maxEntityId && x.LocaleKey == localeKey && x.LocaleKeyGroup == localeKeyGroup && x.LanguageId == languageId) - .ToList(); - - var dict = new Dictionary(properties.Count); - - foreach (var prop in properties) - { - dict[prop.EntityId] = prop.LocaleValue.EmptyNull(); - } - - return dict; - }); - } - - /// - /// Clears the cached segment from the cache - /// - protected virtual void ClearCacheSegment(string localeKeyGroup, string localeKey, int entityId, int? languageId = null) - { - try - { - if (IsInScope) - return; - - var segmentKey = GetSegmentKeyPart(localeKeyGroup, localeKey, entityId); - - if (languageId.HasValue && languageId.Value > 0) - { - _cacheManager.Remove(BuildCacheSegmentKey(segmentKey, languageId.Value)); - } - else - { - _cacheManager.RemoveByPattern(LOCALIZEDPROPERTY_SEGMENT_PATTERN.FormatInvariant(segmentKey)); - } - } - catch { } - } - - 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); - } - - if (languageId <= 0) - return string.Empty; - - var props = GetCacheSegment(localeKeyGroup, localeKey, entityId, languageId); - - if (!props.TryGetValue(entityId, out var val)) - { - return string.Empty; - } - - return val; - } - - protected string GetLocalizedValueUncached(int languageId, int entityId, string localeKeyGroup, string localeKey) - { - if (languageId <= 0) - return string.Empty; - - var query = from lp in _localizedPropertyRepository.TableUntracked - where - lp.EntityId == entityId && - lp.LocaleKey == localeKey && - lp.LocaleKeyGroup == localeKeyGroup && - lp.LanguageId == languageId - select lp.LocaleValue; - - return query.FirstOrDefault().EmptyNull(); - } - - public virtual IList GetLocalizedProperties(int entityId, string localeKeyGroup) + _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() + { + _cacheManager.RemoveByPattern(LOCALIZEDPROPERTY_ALLSEGMENTS_PATTERN); + } + + protected virtual IDictionary GetCacheSegment(string localeKeyGroup, string localeKey, int entityId, int languageId) + { + Guard.NotEmpty(localeKeyGroup, nameof(localeKeyGroup)); + Guard.NotEmpty(localeKey, nameof(localeKey)); + + var segmentKey = GetSegmentKeyPart(localeKeyGroup, localeKey, entityId, out var minEntityId, out var maxEntityId); + var cacheKey = BuildCacheSegmentKey(segmentKey, languageId); + + // TODO: (MC) skip caching product.fulldescription (?), OR + // ...additionally segment by entity id ranges. + + return _cacheManager.Get(cacheKey, () => + { + var properties = _localizedPropertyRepository.TableUntracked + .Where(x => x.EntityId >= minEntityId && x.EntityId <= maxEntityId && x.LocaleKey == localeKey && x.LocaleKeyGroup == localeKeyGroup && x.LanguageId == languageId) + .ToList(); + + var dict = new Dictionary(properties.Count); + + foreach (var prop in properties) + { + dict[prop.EntityId] = prop.LocaleValue ?? string.Empty; + } + + return dict; + }); + } + + /// + /// Clears the cached segment from the cache + /// + protected virtual void ClearCacheSegment(string localeKeyGroup, string localeKey, int entityId, int? languageId = null) + { + try + { + if (IsInScope) + return; + + var segmentKey = GetSegmentKeyPart(localeKeyGroup, localeKey, entityId); + + if (languageId.HasValue && languageId.Value > 0) + { + _cacheManager.Remove(BuildCacheSegmentKey(segmentKey, languageId.Value)); + } + else + { + _cacheManager.RemoveByPattern(LOCALIZEDPROPERTY_SEGMENT_PATTERN.FormatInvariant(segmentKey)); + } + } + catch { } + } + + 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); + } + + if (languageId <= 0) + return string.Empty; + + var props = GetCacheSegment(localeKeyGroup, localeKey, entityId, languageId); + + if (!props.TryGetValue(entityId, out var val)) + { + return string.Empty; + } + + return val; + } + + protected string GetLocalizedValueUncached(int languageId, int entityId, string localeKeyGroup, string localeKey) + { + if (languageId <= 0) + return string.Empty; + + var query = from lp in _localizedPropertyRepository.TableUntracked + where + lp.EntityId == entityId && + lp.LocaleKey == localeKey && + lp.LocaleKeyGroup == localeKeyGroup && + lp.LanguageId == languageId + select lp.LocaleValue; + + return query.FirstOrDefault().EmptyNull(); + } + + public virtual IList GetLocalizedProperties(int entityId, string localeKeyGroup) { if (localeKeyGroup.IsEmpty()) return new List(); @@ -184,151 +186,199 @@ orderby x.Id 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 - where - lp.EntityId == entityId && - lp.LocaleKey == localeKey && - lp.LocaleKeyGroup == localeKeyGroup && - lp.LanguageId == languageId - select lp; - - return query.FirstOrDefault(); - } - - public virtual void InsertLocalizedProperty(LocalizedProperty property) + public virtual void PrefetchLocalizedProperties(string localeKeyGroup, int languageId, int[] entityIds, bool isRange = false, bool isSorted = false) { - Guard.NotNull(property, nameof(property)); + if (languageId == 0) + return; + + var collection = GetLocalizedPropertyCollectionInternal(localeKeyGroup, languageId, entityIds, isRange, isSorted); - // db - _localizedPropertyRepository.Insert(property); - HasChanges = true; + if (_prefetchedCollections.TryGetValue(localeKeyGroup, out var existing)) + { + collection.MergeWith(existing); + } + else + { + _prefetchedCollections[localeKeyGroup] = collection; + } + } - // cache - ClearCacheSegment(property.LocaleKeyGroup, property.LocaleKey, property.EntityId, property.LanguageId); + public virtual LocalizedPropertyCollection GetLocalizedPropertyCollection(string localeKeyGroup, int[] entityIds, bool isRange = false, bool isSorted = false) + { + return GetLocalizedPropertyCollectionInternal(localeKeyGroup, 0, entityIds, isRange, isSorted); + } + + protected 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 splitEntityIds = false; + 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; + + if (entityIds.Length > 5000) + { + splitEntityIds = true; + } + else + { + query = query.Where(x => entityIds.Contains(x.EntityId)); + } + } + } + + if (languageId > 0) + { + query = query.Where(x => x.LanguageId == languageId); + } + + if (splitEntityIds) + { + var items = new List(); + foreach (var chunk in entityIds.Slice(5000)) + { + items.AddRange(query.Where(x => chunk.Contains(x.EntityId)).ToList()); + } + + return new LocalizedPropertyCollection(localeKeyGroup, requestedSet, items); + } + else + { + 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 + where + lp.EntityId == entityId && + lp.LocaleKey == localeKey && + lp.LocaleKeyGroup == localeKeyGroup && + lp.LanguageId == languageId + select lp; + + return query.FirstOrDefault(); + } + + public virtual void InsertLocalizedProperty(LocalizedProperty property) + { + Guard.NotNull(property, nameof(property)); + + // db + _localizedPropertyRepository.Insert(property); + HasChanges = true; + + // cache + ClearCacheSegment(property.LocaleKeyGroup, property.LocaleKey, property.EntityId, property.LanguageId); } public virtual void UpdateLocalizedProperty(LocalizedProperty property) { - Guard.NotNull(property, nameof(property)); + Guard.NotNull(property, nameof(property)); - // db - _localizedPropertyRepository.Update(property); - HasChanges = true; + // db + _localizedPropertyRepository.Update(property); + HasChanges = true; - // cache - ClearCacheSegment(property.LocaleKeyGroup, property.LocaleKey, property.EntityId, property.LanguageId); - } + // cache + ClearCacheSegment(property.LocaleKeyGroup, property.LocaleKey, property.EntityId, property.LanguageId); + } - public virtual void DeleteLocalizedProperty(LocalizedProperty property) - { - Guard.NotNull(property, nameof(property)); + public virtual void DeleteLocalizedProperty(LocalizedProperty property) + { + Guard.NotNull(property, nameof(property)); - // cache - ClearCacheSegment(property.LocaleKeyGroup, property.LocaleKey, property.EntityId, property.LanguageId); + // cache + ClearCacheSegment(property.LocaleKeyGroup, property.LocaleKey, property.EntityId, property.LanguageId); - // db - _localizedPropertyRepository.Delete(property); - HasChanges = true; - } + // db + _localizedPropertyRepository.Delete(property); + HasChanges = true; + } - public virtual LocalizedProperty GetLocalizedPropertyById(int localizedPropertyId) - { - if (localizedPropertyId == 0) - return null; + public virtual LocalizedProperty GetLocalizedPropertyById(int localizedPropertyId) + { + if (localizedPropertyId == 0) + return null; - var localizedProperty = _localizedPropertyRepository.GetById(localizedPropertyId); - return localizedProperty; - } + var localizedProperty = _localizedPropertyRepository.GetById(localizedPropertyId); + return localizedProperty; + } - public virtual void SaveLocalizedValue( - T entity, + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void SaveLocalizedValue( + T entity, Expression> keySelector, - string localeValue, + string value, int languageId) where T : BaseEntity, ILocalizedEntity { - SaveLocalizedValue(entity, keySelector, localeValue, languageId); + SaveLocalizedValue(entity, entity.Id, entity.GetEntityName(), keySelector, value, languageId); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual void SaveLocalizedValue( - T entity, + T entity, Expression> keySelector, - TPropType localeValue, + TPropType value, int languageId) where T : BaseEntity, ILocalizedEntity { - Guard.NotNull(entity, nameof(entity)); - Guard.NotZero(languageId, nameof(languageId)); + SaveLocalizedValue(entity, entity.Id, entity.GetEntityName(), keySelector, value, languageId); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void SaveLocalizedSetting( + TSetting settings, + Expression> keySelector, + TPropType value, + int languageId, + int storeId = 0) where TSetting : class, ISettings + { + // INFO: unfortunately we have to misuse the "EntityId" prop and store StoreId instead. + SaveLocalizedValue(settings, storeId, typeof(TSetting).Name, keySelector, value, languageId); + } + + protected virtual void SaveLocalizedValue( + T obj, + int id, // T is BaseEntity = EntityId, T is ISetting = StoreId + string keyGroup, + Expression> keySelector, + TPropType value, + int languageId) where T : class + { + Guard.NotNull(obj, nameof(obj)); + Guard.NotEmpty(keyGroup, nameof(keyGroup)); + Guard.NotZero(languageId, nameof(languageId)); var member = keySelector.Body as MemberExpression; if (member == null) @@ -342,36 +392,35 @@ public virtual void SaveLocalizedValue( throw new ArgumentException($"Expression '{keySelector}' refers to a field, not a property."); } - var keyGroup = entity.GetEntityName(); var key = propInfo.Name; - var valueStr = localeValue.Convert(); - var prop = GetLocalizedProperty(languageId, entity.Id, keyGroup, key); + var valueStr = value.Convert(); + var prop = GetLocalizedProperty(languageId, id, keyGroup, key); if (prop != null) { - if (valueStr.IsEmpty()) + if (string.IsNullOrEmpty(valueStr)) { - // delete + // Delete DeleteLocalizedProperty(prop); } else { - // update - if (prop.LocaleValue != valueStr) - { - prop.LocaleValue = valueStr; - UpdateLocalizedProperty(prop); - } + // Update + if (prop.LocaleValue != valueStr) + { + prop.LocaleValue = valueStr; + UpdateLocalizedProperty(prop); + } } } else { - if (valueStr.HasValue()) + if (!string.IsNullOrEmpty(valueStr)) { // insert prop = new LocalizedProperty { - EntityId = entity.Id, + EntityId = id, LanguageId = languageId, LocaleKey = key, LocaleKeyGroup = keyGroup, @@ -382,20 +431,20 @@ public virtual void SaveLocalizedValue( } } - private string BuildCacheSegmentKey(string segment, int languageId) - { - return String.Format(LOCALIZEDPROPERTY_SEGMENT_KEY, segment, languageId); - } - - private string GetSegmentKeyPart(string localeKeyGroup, string localeKey, int entityId) - { - return GetSegmentKeyPart(localeKeyGroup, localeKey, entityId, out var minId, out var maxId); - } - - private string GetSegmentKeyPart(string localeKeyGroup, string localeKey, int entityId, out int minId, out int maxId) - { - maxId = entityId.GetRange(_performanceSettings.CacheSegmentSize, out minId); - return (localeKeyGroup + "." + localeKey + "." + maxId.ToString()).ToLowerInvariant(); - } - } + private string BuildCacheSegmentKey(string segment, int languageId) + { + return String.Format(LOCALIZEDPROPERTY_SEGMENT_KEY, segment, languageId); + } + + private string GetSegmentKeyPart(string localeKeyGroup, string localeKey, int entityId) + { + return GetSegmentKeyPart(localeKeyGroup, localeKey, entityId, out _, out _); + } + + private string GetSegmentKeyPart(string localeKeyGroup, string localeKey, int entityId, out int minId, out int maxId) + { + (minId, maxId) = entityId.GetRange(_performanceSettings.CacheSegmentSize); + return (localeKeyGroup + "." + localeKey + "." + minId.ToString()).ToLowerInvariant(); + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Localization/LocalizedUrlHelper.cs b/src/Libraries/SmartStore.Services/Localization/LocalizedUrlHelper.cs index 6f908c3ccc..ceffa079a2 100644 --- a/src/Libraries/SmartStore.Services/Localization/LocalizedUrlHelper.cs +++ b/src/Libraries/SmartStore.Services/Localization/LocalizedUrlHelper.cs @@ -3,9 +3,9 @@ namespace SmartStore.Services.Localization { - public class LocalizedUrlHelper + public class LocalizedUrlHelper { - public LocalizedUrlHelper(HttpRequestBase httpRequest, bool rawUrl = false) + public LocalizedUrlHelper(HttpRequestBase httpRequest, bool rawUrl = false) : this(httpRequest.ApplicationPath, rawUrl ? httpRequest.RawUrl : httpRequest.AppRelativeCurrentExecutionFilePath, rawUrl) { Guard.NotNull(httpRequest, nameof(httpRequest)); @@ -34,8 +34,7 @@ public LocalizedUrlHelper(string applicationPath, string relativePath, bool rawU public bool IsLocalizedUrl() { - string seoCode; - return IsLocalizedUrl(out seoCode); + return IsLocalizedUrl(out _); } public bool IsLocalizedUrl(out string seoCode) @@ -56,7 +55,7 @@ public bool IsLocalizedUrl(out string seoCode) firstPart = firstPart.Substring(0, firstSlash); } - if (LocalizationHelper.IsValidCultureCode(firstPart)) + if (LocalizationHelper.IsValidCultureCode(firstPart)) { seoCode = firstPart; return true; @@ -106,7 +105,7 @@ public string PrependSeoCode(string seoCode, bool safe = false) public string GetAbsolutePath() { string path = this.ApplicationPath.EnsureEndsWith("/"); - path = path + this.RelativePath; + path += this.RelativePath; if (path.Length > 1 && path[0] != '/') { diff --git a/src/Libraries/SmartStore.Services/Localization/LocalizedValue.cs b/src/Libraries/SmartStore.Services/Localization/LocalizedValue.cs index 625f53f986..c383f2c992 100644 --- a/src/Libraries/SmartStore.Services/Localization/LocalizedValue.cs +++ b/src/Libraries/SmartStore.Services/Localization/LocalizedValue.cs @@ -8,154 +8,139 @@ namespace SmartStore.Services.Localization { - public class LocalizedValue - { - // Regex for all types of brackets which need to be "swapped": ({[]}) - private readonly static Regex _rgBrackets = new Regex(@"\(|\{|\[|\]|\}|\)", RegexOptions.Compiled | RegexOptions.CultureInvariant); - - /// - /// Fixes the flow of brackets within a text if the current page language has RTL flow. - /// - /// The test to fix. - /// Current language - /// - public static string FixBrackets(string str, Language currentLanguage) - { - if (!currentLanguage.Rtl || str.IsEmpty()) - { - return str; - } - - var controlChar = "‎"; - return _rgBrackets.Replace(str, m => - { - return controlChar + m.Value + controlChar; - }); - } - } - - [Serializable] - public class LocalizedValue : IHtmlString, IEquatable>, IComparable, IComparable> - { - private T _value; - private readonly Language _requestLanguage; - private readonly Language _currentLanguage; - - public LocalizedValue(T value, Language requestLanguage, Language currentLanguage) - { - _value = value; - _requestLanguage = requestLanguage; - _currentLanguage = currentLanguage; - } - - public T Value - { - get { return _value; } - } - - [JsonIgnore] - public Language RequestLanguage - { - get { return _requestLanguage; } - } - - [JsonIgnore] - public Language CurrentLanguage - { - get { return _currentLanguage; } - } - - public bool IsFallback - { - get { return _requestLanguage != _currentLanguage; } - } - - 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) - { - return default; - } - - return obj.Value; - } - - public string ToHtmlString() - { - return this.ToString(); - } - - public override string ToString() - { - if (_value == null) - { - return null; - } - - if (typeof(T) == typeof(string)) - { - return _value as string; - } - - return _value.Convert(CultureInfo.GetCultureInfo(_currentLanguage.LanguageCulture)); - } - - public override int GetHashCode() - { - var hashCode = 0; - if (_value != null) - hashCode ^= _value.GetHashCode(); - return hashCode; - } - - public override bool Equals(object other) - { - return this.Equals(other as LocalizedValue); - } - - public bool Equals(LocalizedValue other) - { - if (ReferenceEquals(null, other)) - return false; - if (ReferenceEquals(this, other)) - return true; - - return object.Equals(_value, other._value); - } - - public int CompareTo(object other) - { - return CompareTo(other as LocalizedValue); - } - - public int CompareTo(LocalizedValue other) - { - if (other == null) - { - return 1; - } - - if (Value is IComparable val1) - { - return val1.CompareTo(other.Value); - } - - if (Value is IComparable val2) - { - return val2.CompareTo(other.Value); - } - - return 0; - } - } + public class LocalizedValue + { + // Regex for all types of brackets which need to be "swapped": ({[]}) + private readonly static Regex _rgBrackets = new Regex(@"\(|\{|\[|\]|\}|\)", RegexOptions.Compiled | RegexOptions.CultureInvariant); + + /// + /// Fixes the flow of brackets within a text if the current page language has RTL flow. + /// + /// The test to fix. + /// Current language + /// + public static string FixBrackets(string str, Language currentLanguage) + { + if (!currentLanguage.Rtl || str.IsEmpty()) + { + return str; + } + + var controlChar = "‎"; + return _rgBrackets.Replace(str, m => + { + return controlChar + m.Value + controlChar; + }); + } + } + + [Serializable] + public class LocalizedValue : IHtmlString, IEquatable>, IComparable, IComparable> + { + private T _value; + private readonly Language _requestLanguage; + private readonly Language _currentLanguage; + + public LocalizedValue(T value, Language requestLanguage, Language currentLanguage) + { + _value = value; + _requestLanguage = requestLanguage; + _currentLanguage = currentLanguage; + } + + public T Value => _value; + + [JsonIgnore] + public Language RequestLanguage => _requestLanguage; + + [JsonIgnore] + public Language CurrentLanguage => _currentLanguage; + + public bool IsFallback => _requestLanguage != _currentLanguage; + + public bool BidiOverride => _requestLanguage != _currentLanguage && _requestLanguage.Rtl != _currentLanguage.Rtl; + + public void ChangeValue(T value) + { + _value = value; + } + + public static implicit operator T(LocalizedValue obj) + { + if (obj == null) + { + return default; + } + + return obj.Value; + } + + public string ToHtmlString() + { + return this.ToString(); + } + + public override string ToString() + { + if (_value == null) + { + return null; + } + + if (typeof(T) == typeof(string)) + { + return _value as string; + } + + return _value.Convert(CultureInfo.GetCultureInfo(_currentLanguage.LanguageCulture)); + } + + public override int GetHashCode() + { + var hashCode = 0; + if (_value != null) + hashCode ^= _value.GetHashCode(); + return hashCode; + } + + public override bool Equals(object other) + { + return this.Equals(other as LocalizedValue); + } + + public bool Equals(LocalizedValue other) + { + if (ReferenceEquals(null, other)) + return false; + if (ReferenceEquals(this, other)) + return true; + + return object.Equals(_value, other._value); + } + + public int CompareTo(object other) + { + return CompareTo(other as LocalizedValue); + } + + public int CompareTo(LocalizedValue other) + { + if (other == null) + { + return 1; + } + + if (Value is IComparable val1) + { + return val1.CompareTo(other.Value); + } + + if (Value is IComparable val2) + { + return val2.CompareTo(other.Value); + } + + return 0; + } + } } diff --git a/src/Libraries/SmartStore.Services/Logging/CustomerActivityService.cs b/src/Libraries/SmartStore.Services/Logging/CustomerActivityService.cs index 6c45b687c9..447d5e1fc1 100644 --- a/src/Libraries/SmartStore.Services/Logging/CustomerActivityService.cs +++ b/src/Libraries/SmartStore.Services/Logging/CustomerActivityService.cs @@ -10,38 +10,38 @@ namespace SmartStore.Services.Logging { - /// - /// Customer activity service - /// - public class CustomerActivityService : ICustomerActivityService + /// + /// Customer activity service + /// + public class CustomerActivityService : ICustomerActivityService { - #region Fields + #region Fields - private const int _deleteNumberOfEntries = 1000; + private const int _deleteNumberOfEntries = 1000; - private readonly IRepository _activityLogRepository; + private readonly IRepository _activityLogRepository; private readonly IRepository _activityLogTypeRepository; - private readonly IRepository _customerRepository; - private readonly IWorkContext _workContext; + private readonly IRepository _customerRepository; + private readonly IWorkContext _workContext; private readonly IDbContext _dbContext; private readonly static object s_lock = new object(); private readonly static ConcurrentDictionary s_logTypes = new ConcurrentDictionary(); - #endregion + #endregion - #region Ctor + #region Ctor - public CustomerActivityService( + public CustomerActivityService( IRepository activityLogRepository, IRepository activityLogTypeRepository, - IRepository customerRepository, - IWorkContext workContext, + IRepository customerRepository, + IWorkContext workContext, IDbContext dbContext) { this._activityLogRepository = activityLogRepository; this._activityLogTypeRepository = activityLogTypeRepository; - this._customerRepository = customerRepository; + this._customerRepository = customerRepository; this._workContext = workContext; this._dbContext = dbContext; } @@ -111,7 +111,8 @@ private void EnsureLogTypesAreLoaded() var query = from alt in _activityLogTypeRepository.Table orderby alt.Name select alt; - query.Each(x => { + query.Each(x => + { s_logTypes[x.SystemKeyword] = x; }); } @@ -176,9 +177,9 @@ public virtual ActivityLog InsertActivity(string systemKeyword, string comment, if (activityType == null || !activityType.Enabled) return null; - comment = comment.EmptyNull(); + comment = comment.EmptyNull(); comment = string.Format(comment, commentParams); - comment = comment.Truncate(4000); + comment = comment.Truncate(4000); var activity = new ActivityLog(); activity.ActivityLogTypeId = activityType.Id; @@ -203,45 +204,45 @@ public virtual void DeleteActivity(ActivityLog activityLog) _activityLogRepository.Delete(activityLog); } - /// - /// Gets all activity log items - /// - /// Log item creation from; null to load all customers - /// Log item creation to; null to load all customers - /// Customer identifier; null to load all customers - /// Activity log type identifier - /// Page index - /// Page size - /// Customer email - /// Customer system name - /// Activity log collection - public virtual IPagedList GetAllActivities( - DateTime? createdOnFrom, + /// + /// Gets all activity log items + /// + /// Log item creation from; null to load all customers + /// Log item creation to; null to load all customers + /// Customer identifier; null to load all customers + /// Activity log type identifier + /// Page index + /// Page size + /// Customer email + /// Customer system name + /// Activity log collection + public virtual IPagedList GetAllActivities( + DateTime? createdOnFrom, DateTime? createdOnTo, - int? customerId, - int activityLogTypeId, + int? customerId, + int activityLogTypeId, int pageIndex, - int pageSize, - string email = null, - bool? customerSystemAccount = null) + int pageSize, + string email = null, + bool? customerSystemAccount = null) { var query = _activityLogRepository.Table; - if (email.HasValue() || customerSystemAccount.HasValue) - { - var queryCustomers = _customerRepository.Table; + if (email.HasValue() || customerSystemAccount.HasValue) + { + var queryCustomers = _customerRepository.Table; - if (email.HasValue()) - queryCustomers = queryCustomers.Where(x => x.Email.Contains(email)); + if (email.HasValue()) + queryCustomers = queryCustomers.Where(x => x.Email.Contains(email)); - if (customerSystemAccount.HasValue) - queryCustomers = queryCustomers.Where(x => x.IsSystemAccount == customerSystemAccount.Value); + if (customerSystemAccount.HasValue) + queryCustomers = queryCustomers.Where(x => x.IsSystemAccount == customerSystemAccount.Value); - query = - from al in _activityLogRepository.Table - join c in queryCustomers on al.CustomerId equals c.Id - select al; - } + query = + from al in _activityLogRepository.Table + join c in queryCustomers on al.CustomerId equals c.Id + select al; + } if (createdOnFrom.HasValue) query = query.Where(al => createdOnFrom.Value <= al.CreatedOnUtc); @@ -279,54 +280,54 @@ public virtual ActivityLog GetActivityById(int activityLogId) return activityLog; } - public virtual IList GetActivityByIds(int[] activityLogIds) - { - if (activityLogIds == null || activityLogIds.Length == 0) - return new List(); + public virtual IList GetActivityByIds(int[] activityLogIds) + { + if (activityLogIds == null || activityLogIds.Length == 0) + return new List(); - var query = _activityLogRepository.Table - .Where(x => activityLogIds.Contains(x.Id)) - .OrderByDescending(x => x.CreatedOnUtc); + var query = _activityLogRepository.Table + .Where(x => activityLogIds.Contains(x.Id)) + .OrderByDescending(x => x.CreatedOnUtc); - return query.ToList(); - } + return query.ToList(); + } - /// - /// Clears activity log - /// - public virtual void ClearAllActivities() + /// + /// Clears activity log + /// + public virtual void ClearAllActivities() { - try - { - _dbContext.ExecuteSqlCommand("TRUNCATE TABLE [ActivityLog]"); - } - catch - { - try - { - for (int i = 0; i < 100000; ++i) - { - if (_dbContext.ExecuteSqlCommand("Delete Top ({0}) From [ActivityLog]", false, null, _deleteNumberOfEntries) < _deleteNumberOfEntries) - break; - } - } - catch { } - - try - { - _dbContext.ExecuteSqlCommand("DBCC CHECKIDENT('ActivityLog', RESEED, 0)"); - } - catch - { - try - { - _dbContext.ExecuteSqlCommand("Alter Table [ActivityLog] Alter Column [Id] Identity(1,1)"); - } - catch { } - } - } - } - - #endregion + try + { + _dbContext.ExecuteSqlCommand("TRUNCATE TABLE [ActivityLog]"); + } + catch + { + try + { + for (int i = 0; i < 100000; ++i) + { + if (_dbContext.ExecuteSqlCommand("Delete Top ({0}) From [ActivityLog]", false, null, _deleteNumberOfEntries) < _deleteNumberOfEntries) + break; + } + } + catch { } + + try + { + _dbContext.ExecuteSqlCommand("DBCC CHECKIDENT('ActivityLog', RESEED, 0)"); + } + catch + { + try + { + _dbContext.ExecuteSqlCommand("Alter Table [ActivityLog] Alter Column [Id] Identity(1,1)"); + } + catch { } + } + } + } + + #endregion } } diff --git a/src/Libraries/SmartStore.Services/Logging/DbLogService.cs b/src/Libraries/SmartStore.Services/Logging/DbLogService.cs index d725fadef6..4efc4187b9 100644 --- a/src/Libraries/SmartStore.Services/Logging/DbLogService.cs +++ b/src/Libraries/SmartStore.Services/Logging/DbLogService.cs @@ -8,141 +8,141 @@ namespace SmartStore.Services.Logging { - public partial class DbLogService : ILogService - { - private const int _deleteNumberOfEntries = 1000; - - private readonly IRepository _logRepository; - private readonly IDbContext _dbContext; - private readonly ILoggerFactory _loggerFactory; - - public DbLogService(IRepository logRepository, ILoggerFactory loggerFactory) - { - _logRepository = logRepository; - _dbContext = logRepository.Context; - _loggerFactory = loggerFactory; - } - - public virtual void DeleteLog(Log log) - { - Guard.NotNull(log, nameof(log)); - - _logRepository.Delete(log); - } - - public virtual void ClearLog() - { - try - { - _dbContext.ExecuteSqlCommand("TRUNCATE TABLE [Log]"); - } - catch - { - try - { - for (int i = 0; i < 100000; ++i) - { - if (_dbContext.ExecuteSqlCommand("Delete Top ({0}) From [Log]", false, null, _deleteNumberOfEntries) < _deleteNumberOfEntries) - break; - } - } - catch { } - - try - { - _dbContext.ExecuteSqlCommand("DBCC CHECKIDENT('Log', RESEED, 0)"); - } - catch - { - try - { - _dbContext.ExecuteSqlCommand("Alter Table [Log] Alter Column [Id] Identity(1,1)"); - } - catch { } - } - } - - _dbContext.ShrinkDatabase(); - } - - public virtual void ClearLog(DateTime toUtc, LogLevel logLevel) - { - try - { - string sqlDelete = "Delete Top ({0}) From [Log] Where LogLevelId < {1} And CreatedOnUtc <= {2}"; - - for (int i = 0; i < 100000; ++i) - { - if (_dbContext.ExecuteSqlCommand(sqlDelete, false, null, _deleteNumberOfEntries, (int)logLevel, toUtc) < _deleteNumberOfEntries) - break; - } - - _dbContext.ShrinkDatabase(); - } - catch { } - } - - public virtual IPagedList GetAllLogs( - DateTime? fromUtc, - DateTime? toUtc, - string logger, - string message, - LogLevel? logLevel, - int pageIndex, - int pageSize) - { - // force flush to get most recent entries - _loggerFactory.FlushAll(); - - var query = _logRepository.Table; - - if (fromUtc.HasValue) - query = query.Where(l => fromUtc.Value <= l.CreatedOnUtc); - - if (toUtc.HasValue) - query = query.Where(l => toUtc.Value >= l.CreatedOnUtc); - - if (logger.HasValue()) - query = query.Where(l => l.Logger.Contains(logger)); - - if (logLevel.HasValue) - { - int logLevelId = (int)logLevel.Value; - query = query.Where(l => logLevelId == l.LogLevelId); - } - - if (message.HasValue()) - query = query.Where(l => l.ShortMessage.Contains(message) || l.FullMessage.Contains(message)); - - query = query.OrderByDescending(l => l.CreatedOnUtc); - - //query = _logRepository.Expand(query, x => x.Customer); - - var log = new PagedList(query, pageIndex, pageSize); - return log; - } - - public virtual Log GetLogById(int logId) - { - if (logId == 0) - return null; - - var log = _logRepository.GetById(logId); - return log; - } - - public virtual IList GetLogByIds(int[] logIds) - { - if (logIds == null || logIds.Length == 0) - return new List(); - - var query = from l in _logRepository.Table - where logIds.Contains(l.Id) - select l; - var logItems = query.ToList(); - - // sort by passed identifier sequence - return logItems.OrderBySequence(logIds).ToList(); - } - } + public partial class DbLogService : ILogService + { + private const int _deleteNumberOfEntries = 1000; + + private readonly IRepository _logRepository; + private readonly IDbContext _dbContext; + private readonly ILoggerFactory _loggerFactory; + + public DbLogService(IRepository logRepository, ILoggerFactory loggerFactory) + { + _logRepository = logRepository; + _dbContext = logRepository.Context; + _loggerFactory = loggerFactory; + } + + public virtual void DeleteLog(Log log) + { + Guard.NotNull(log, nameof(log)); + + _logRepository.Delete(log); + } + + public virtual void ClearLog() + { + try + { + _dbContext.ExecuteSqlCommand("TRUNCATE TABLE [Log]"); + } + catch + { + try + { + for (int i = 0; i < 100000; ++i) + { + if (_dbContext.ExecuteSqlCommand("Delete Top ({0}) From [Log]", false, null, _deleteNumberOfEntries) < _deleteNumberOfEntries) + break; + } + } + catch { } + + try + { + _dbContext.ExecuteSqlCommand("DBCC CHECKIDENT('Log', RESEED, 0)"); + } + catch + { + try + { + _dbContext.ExecuteSqlCommand("Alter Table [Log] Alter Column [Id] Identity(1,1)"); + } + catch { } + } + } + + _dbContext.ShrinkDatabase(); + } + + public virtual void ClearLog(DateTime toUtc, LogLevel logLevel) + { + try + { + string sqlDelete = "Delete Top ({0}) From [Log] Where LogLevelId < {1} And CreatedOnUtc <= {2}"; + + for (int i = 0; i < 100000; ++i) + { + if (_dbContext.ExecuteSqlCommand(sqlDelete, false, null, _deleteNumberOfEntries, (int)logLevel, toUtc) < _deleteNumberOfEntries) + break; + } + + _dbContext.ShrinkDatabase(); + } + catch { } + } + + public virtual IPagedList GetAllLogs( + DateTime? fromUtc, + DateTime? toUtc, + string logger, + string message, + LogLevel? logLevel, + int pageIndex, + int pageSize) + { + // force flush to get most recent entries + _loggerFactory.FlushAll(); + + var query = _logRepository.Table; + + if (fromUtc.HasValue) + query = query.Where(l => fromUtc.Value <= l.CreatedOnUtc); + + if (toUtc.HasValue) + query = query.Where(l => toUtc.Value >= l.CreatedOnUtc); + + if (logger.HasValue()) + query = query.Where(l => l.Logger.Contains(logger)); + + if (logLevel.HasValue) + { + int logLevelId = (int)logLevel.Value; + query = query.Where(l => logLevelId == l.LogLevelId); + } + + if (message.HasValue()) + query = query.Where(l => l.ShortMessage.Contains(message) || l.FullMessage.Contains(message)); + + query = query.OrderByDescending(l => l.CreatedOnUtc); + + //query = _logRepository.Expand(query, x => x.Customer); + + var log = new PagedList(query, pageIndex, pageSize); + return log; + } + + public virtual Log GetLogById(int logId) + { + if (logId == 0) + return null; + + var log = _logRepository.GetById(logId); + return log; + } + + public virtual IList GetLogByIds(int[] logIds) + { + if (logIds == null || logIds.Length == 0) + return new List(); + + var query = from l in _logRepository.Table + where logIds.Contains(l.Id) + select l; + var logItems = query.ToList(); + + // sort by passed identifier sequence + return logItems.OrderBySequence(logIds).ToList(); + } + } } diff --git a/src/Libraries/SmartStore.Services/Logging/DeleteLogsTask.cs b/src/Libraries/SmartStore.Services/Logging/DeleteLogsTask.cs index 23faa952be..135c819c1f 100644 --- a/src/Libraries/SmartStore.Services/Logging/DeleteLogsTask.cs +++ b/src/Libraries/SmartStore.Services/Logging/DeleteLogsTask.cs @@ -1,31 +1,31 @@ using System; -using SmartStore.Core.Domain.Logging; +using SmartStore.Core.Domain.Common; using SmartStore.Core.Logging; using SmartStore.Services.Tasks; namespace SmartStore.Services.Logging { /// - /// Represents a task for deleting log entries + /// Represents a task for deleting log entries. /// public partial class DeleteLogsTask : ITask { private readonly ILogService _logService; + private readonly CommonSettings _commonSettings; - public DeleteLogsTask(ILogService logService) + public DeleteLogsTask( + ILogService logService, + CommonSettings commonSettings) { _logService = logService; + _commonSettings = commonSettings; } - /// - /// Executes a task - /// - public void Execute(TaskExecutionContext ctx) + public void Execute(TaskExecutionContext ctx) { - var olderThanDays = 7; // TODO: move to settings - var toUtc = DateTime.UtcNow.AddDays(-olderThanDays); + var toUtc = DateTime.UtcNow.AddDays(-_commonSettings.MaxLogAgeInDays); - _logService.ClearLog(toUtc, LogLevel.Error); + _logService.ClearLog(toUtc, LogLevel.Error); } } } diff --git a/src/Libraries/SmartStore.Services/Logging/ILogService.cs b/src/Libraries/SmartStore.Services/Logging/ILogService.cs index 926f87aea0..e2e4131346 100644 --- a/src/Libraries/SmartStore.Services/Logging/ILogService.cs +++ b/src/Libraries/SmartStore.Services/Logging/ILogService.cs @@ -9,52 +9,52 @@ namespace SmartStore.Services.Logging { - public interface ILogService - { - /// - /// Deletes a log item - /// - /// Log item - void DeleteLog(Log log); + public interface ILogService + { + /// + /// Deletes a log item + /// + /// Log item + void DeleteLog(Log log); - /// - /// Clears a log - /// - void ClearLog(); - void ClearLog(DateTime toUtc, LogLevel logLevel); + /// + /// Clears a log + /// + void ClearLog(); + void ClearLog(DateTime toUtc, LogLevel logLevel); - /// - /// Gets all log items - /// - /// Log item creation from; null to load all records - /// Log item creation to; null to load all records - /// Message - /// Logger name - /// Log level; null to load all records - /// Page index - /// Page size - /// Log item collection - IPagedList GetAllLogs( - DateTime? fromUtc, - DateTime? toUtc, - string logger, - string message, - LogLevel? logLevel, - int pageIndex, - int pageSize); + /// + /// Gets all log items + /// + /// Log item creation from; null to load all records + /// Log item creation to; null to load all records + /// Message + /// Logger name + /// Log level; null to load all records + /// Page index + /// Page size + /// Log item collection + IPagedList GetAllLogs( + DateTime? fromUtc, + DateTime? toUtc, + string logger, + string message, + LogLevel? logLevel, + int pageIndex, + int pageSize); - /// - /// Gets a log item - /// - /// Log item identifier - /// Log item - Log GetLogById(int logId); + /// + /// Gets a log item + /// + /// Log item identifier + /// Log item + Log GetLogById(int logId); - /// - /// Get log items by identifiers - /// - /// Log item identifiers - /// Log items - IList GetLogByIds(int[] logIds); - } + /// + /// Get log items by identifiers + /// + /// Log item identifiers + /// Log items + IList GetLogByIds(int[] logIds); + } } diff --git a/src/Libraries/SmartStore.Services/Media/Album/AlbumInfo.cs b/src/Libraries/SmartStore.Services/Media/Album/AlbumInfo.cs new file mode 100644 index 0000000000..4e1422fa65 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Album/AlbumInfo.cs @@ -0,0 +1,63 @@ +using System; + +namespace SmartStore.Services.Media +{ + /// + /// Common info about a media album. + /// + public class AlbumInfo + { + /// + /// Storage id of the album + /// + public int Id { get; set; } + + /// + /// System name of the album + /// + public string Name { get; set; } + + /// + /// The type of the concrete implementation. + /// + public Type ProviderType { get; set; } + + /// + /// An album created by is always a system album. + /// + public bool IsSystemAlbum { get; set; } + + /// + /// true when the provider () also implements . + /// + public bool IsTrackDetector { get; set; } + + /// + /// Info about how to display the album by the media manager UI + /// + public AlbumDisplayHint DisplayHint { get; set; } + + /// + /// Reflection info about trackable properties. + /// + public TrackedMediaProperty[] TrackedProperties { get; set; } = new TrackedMediaProperty[0]; + } + + public class AlbumDisplayHint + { + /// + /// Gets or sets the album folder icon display HTML color + /// + public string Color { get; set; } + + /// + /// Gets or sets the album overlay icon class name. + /// + public string OverlayIcon { get; set; } + + /// + /// Gets or sets the album overlay icon display HTML color + /// + public string OverlayColor { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Album/AlbumRegistry.cs b/src/Libraries/SmartStore.Services/Media/Album/AlbumRegistry.cs new file mode 100644 index 0000000000..00b553a95a --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Album/AlbumRegistry.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SmartStore.Core.Caching; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Services.Media +{ + public class AlbumRegistry : IAlbumRegistry + { + internal const string AlbumInfosKey = "media:albums:all"; + + private readonly IDbContext _dbContext; + private readonly ICacheManager _cache; + private readonly IEnumerable> _albumProviders; + + public AlbumRegistry( + IDbContext dbContext, + ICacheManager cache, + IEnumerable> albumProviders) + { + _dbContext = dbContext; + _cache = cache; + _albumProviders = albumProviders; + } + + public virtual IReadOnlyCollection GetAllAlbums() + { + return GetAlbumDictionary().Values; + } + + public IEnumerable GetAlbumNames(bool withTrackDetectors = false) + { + var dict = GetAlbumDictionary(); + + if (!withTrackDetectors) + { + return dict.Keys; + } + + return dict.Where(x => x.Value.IsTrackDetector).Select(x => x.Key).ToArray(); + } + + private Dictionary GetAlbumDictionary() + { + var albums = _cache.Get(AlbumInfosKey, () => + { + return LoadAllAlbums().ToDictionary(x => x.Name); + }, TimeSpan.FromHours(24)); + + return albums; + } + + protected virtual IEnumerable LoadAllAlbums() + { + var setFolders = _dbContext.Set(); + + var dbAlbums = setFolders + .AsNoTracking() + .OfType() + .Select(x => new { x.Id, x.Name }) + .ToDictionary(x => x.Name); + + foreach (var lazyProvider in _albumProviders) + { + var provider = lazyProvider.Value; + var albums = provider.GetAlbums().DistinctBy(x => x.Name).ToArray(); + + foreach (var album in albums) + { + var info = new AlbumInfo + { + Name = album.Name, + ProviderType = provider.GetType(), + IsSystemAlbum = typeof(SystemAlbumProvider) == provider.GetType(), + DisplayHint = provider.GetDisplayHint(album) ?? new AlbumDisplayHint() + }; + + if (provider is IMediaTrackDetector detector) + { + var propertyTable = new TrackedMediaPropertyTable(album.Name); + detector.ConfigureTracks(album.Name, propertyTable); + + info.IsTrackDetector = true; + info.TrackedProperties = propertyTable.GetProperties(); + } + + if (dbAlbums.TryGetValue(album.Name, out var dbAlbum)) + { + info.Id = dbAlbum.Id; + } + else + { + setFolders.Add(album); + _dbContext.SaveChanges(); + info.Id = album.Id; + } + + yield return info; + } + } + } + + public virtual AlbumInfo GetAlbumByName(string name) + { + Guard.NotEmpty(name, nameof(name)); + + return GetAlbumDictionary().Get(name); + } + + public virtual AlbumInfo GetAlbumById(int id) + { + if (id > 0) + { + return GetAlbumDictionary().FirstOrDefault(x => x.Value.Id == id).Value; + } + + return null; + } + + public void UninstallAlbum(string albumName) + { + var album = GetAlbumValidated(albumName, true); + + throw new NotImplementedException(); + + //ClearCache(); + } + + public void DeleteAlbum(string albumName, string moveFilesToAlbum) + { + var album = GetAlbumValidated(albumName, true); + var destinationAlbum = GetAlbumValidated(moveFilesToAlbum, false); + + throw new NotImplementedException(); + + //ClearCache(); + } + + private AlbumInfo GetAlbumValidated(string albumName, bool throwWhenSystemAlbum) + { + Guard.NotEmpty(albumName, nameof(albumName)); + + var album = GetAlbumByName(albumName); + + if (album == null) + { + throw new InvalidOperationException($"The album '{albumName}' does not exist."); + } + + if (album.IsSystemAlbum && throwWhenSystemAlbum) + { + throw new InvalidOperationException($"The media album '{albumName}' is a system album and cannot be deleted."); + } + + return album; + } + + public void ClearCache() + { + _cache.Remove(AlbumInfosKey); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Album/FolderService.cs b/src/Libraries/SmartStore.Services/Media/Album/FolderService.cs new file mode 100644 index 0000000000..260e3c0282 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Album/FolderService.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Data.Entity; +using SmartStore.Collections; +using SmartStore.Core.Caching; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Services.Media +{ + public partial class FolderService : ScopedServiceBase, IFolderService + { + internal static TimeSpan FolderTreeCacheDuration = TimeSpan.FromHours(3); + + internal const string FolderTreeKey = "mediafolder:tree"; + + private readonly IAlbumRegistry _albumRegistry; + private readonly IRepository _folderRepo; + private readonly ICacheManager _cache; + + public FolderService( + IAlbumRegistry albumRegistry, + IRepository folderRepo, + ICacheManager cache) + { + _albumRegistry = albumRegistry; + _folderRepo = folderRepo; + _cache = cache; + } + + #region Cached nodes + + public TreeNode GetRootNode() + { + var cacheKey = FolderTreeKey; + + var root = _cache.Get(cacheKey, () => + { + var query = from x in _folderRepo.TableUntracked + orderby x.ParentId, x.Name + select x; + + var unsortedNodes = query.ToList().Select(x => + { + var item = new MediaFolderNode + { + Id = x.Id, + Name = x.Name, + ParentId = x.ParentId, + CanDetectTracks = x.CanDetectTracks, + FilesCount = x.FilesCount, + Slug = x.Slug + }; + + if (x is MediaAlbum album) + { + item.IsAlbum = true; + item.AlbumName = album.Name; + item.Path = album.Name; + item.ResKey = album.ResKey; + item.IncludePath = album.IncludePath; + item.Order = album.Order ?? 0; + + var albumInfo = _albumRegistry.GetAlbumByName(album.Name); + if (albumInfo != null) + { + var displayHint = albumInfo.DisplayHint; + item.Color = displayHint.Color; + item.OverlayColor = displayHint.OverlayColor; + item.OverlayIcon = displayHint.OverlayIcon; + } + } + + return item; + }); + + var nodeMap = unsortedNodes.ToMultimap(x => x.ParentId ?? 0, x => x); + var rootNode = new TreeNode(new MediaFolderNode { Name = "Root", Id = 0 }); + + AddChildTreeNodes(rootNode, 0, nodeMap); + + return rootNode; + }, FolderTreeCacheDuration); + + return root; + + static void AddChildTreeNodes(TreeNode parentNode, int parentId, Multimap nodeMap) + { + var parent = parentNode?.Value; + if (parent == null) + { + return; + } + + var nodes = Enumerable.Empty(); + + if (nodeMap.ContainsKey(parentId)) + { + nodes = parentId == 0 + ? nodeMap[parentId].OrderBy(x => x.Order) + : nodeMap[parentId].OrderBy(x => x.Name); + } + + foreach (var node in nodes) + { + var newNode = new TreeNode(node); + + // Inherit some props from parent node + if (!node.IsAlbum) + { + node.AlbumName = parent.AlbumName; + node.CanDetectTracks = parent.CanDetectTracks; + node.IncludePath = parent.IncludePath; + node.Path = (parent.Path + "/" + (node.Slug.NullEmpty() ?? node.Name)).Trim('/').ToLower(); + } + + // We gonna query nodes by path also, therefore we need 2 keys per node (FolderId and computed path) + newNode.Id = new object[] { node.Id, node.Path }; + + parentNode.Append(newNode); + + AddChildTreeNodes(newNode, node.Id, nodeMap); + } + } + } + + public TreeNode GetNodeById(int id) + { + if (id <= 0) + return null; + + return GetRootNode().SelectNodeById(id); + } + + public TreeNode GetNodeByPath(string path) + { + Guard.NotEmpty(path, nameof(path)); + return GetRootNode().SelectNodeById(NormalizePath(path)); + } + + public bool CheckUniqueFolderName(string path, out string newName) + { + Guard.NotEmpty(path, nameof(path)); + + // TODO: (mm) (mc) throw when path is not a folder path + + newName = null; + + var node = GetNodeByPath(path); + if (node == null) + { + return false; + } + + var sourceName = node.Value.Name; + var names = new HashSet(node.Parent.Children.Select(x => x.Value.Name), StringComparer.OrdinalIgnoreCase); + + int i = 1; + while (true) + { + var test = sourceName + "-" + i; + if (!names.Contains(test)) + { + // Found our gap + newName = test; + return true; + } + + i++; + } + } + + protected override void OnClearCache() + { + _cache.Remove(FolderTreeKey); + } + + #endregion + + #region Storage + + public MediaFolder GetFolderById(int id, bool withFiles = false) + { + if (id <= 0) + return null; + + if (!withFiles) + { + return _folderRepo.GetById(id); + } + + return _folderRepo.Table.Include(x => x.Files).FirstOrDefault(x => x.Id == id); + } + + public void InsertFolder(MediaFolder folder) + { + Guard.NotNull(folder, nameof(folder)); + + _folderRepo.Insert(folder); + + HasChanges = true; + if (!IsInScope) + { + OnClearCache(); + } + } + + public void DeleteFolder(MediaFolder folder) + { + Guard.NotNull(folder, nameof(folder)); + + _folderRepo.Delete(folder); + + HasChanges = true; + if (!IsInScope) + { + OnClearCache(); + } + } + + public void UpdateFolder(MediaFolder folder) + { + Guard.NotNull(folder, nameof(folder)); + + _folderRepo.Update(folder); + + HasChanges = true; + if (!IsInScope) + { + OnClearCache(); + } + } + + #endregion + + #region Utils + + protected internal static string NormalizePath(string path, bool forQuery = true) + { + path = path.Replace('\\', '/').Trim('/'); + return forQuery ? path.ToLower() : path; + } + + #endregion + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Album/IAlbumProvider.cs b/src/Libraries/SmartStore.Services/Media/Album/IAlbumProvider.cs new file mode 100644 index 0000000000..3653492cd7 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Album/IAlbumProvider.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Services.Media +{ + /// + /// Creates album entities and provides media manager UI infos about them. + /// Implementation classes are instantiated once on app startup. Impl does NOT + /// need to be registered in DI. + /// + public interface IAlbumProvider + { + /// + /// Creates a list of album entities the provider is responsible for. + /// A special bootstrapper checks whether an album already exists in the + /// database (by key). The entity is inserted + /// to the database if it does not exist yet, or skipped otherwise. + /// + /// List of entities. + IEnumerable GetAlbums(); + + /// + /// Gets UI display info about an album. + /// + /// Album to get info for. + /// UI display hint object. + AlbumDisplayHint GetDisplayHint(MediaAlbum album); + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Media/Album/IAlbumRegistry.cs b/src/Libraries/SmartStore.Services/Media/Album/IAlbumRegistry.cs new file mode 100644 index 0000000000..31d2415a9a --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Album/IAlbumRegistry.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; + +namespace SmartStore.Services.Media +{ + /// + /// Manages all registered albums. + /// + public interface IAlbumRegistry + { + /// + /// Enlists all albums. + /// + IReadOnlyCollection GetAllAlbums(); + + /// + /// Gets the names of all albums. + /// + /// If true skips all albums without track detection capability. + IEnumerable GetAlbumNames(bool withTrackDetectors = false); + + /// + /// Gets an album by name. + /// + /// Name of album to retrieve. + AlbumInfo GetAlbumByName(string name); + + /// + /// Gets an album by ID. + /// + /// Storage id of album to retrieve. + AlbumInfo GetAlbumById(int id); + + /// + /// Deletes album and all containing files + /// + /// Name of album to delete + void UninstallAlbum(string albumName); + + /// + /// Deletes album but keeps containing files + /// + /// Name of album to delete + /// Name of album to move files to + void DeleteAlbum(string albumName, string moveFilesToAlbum); + + /// + /// Clears album cache and reloads all entities from database. + /// + void ClearCache(); + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Album/IFolderService.cs b/src/Libraries/SmartStore.Services/Media/Album/IFolderService.cs new file mode 100644 index 0000000000..6d7fe41f33 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Album/IFolderService.cs @@ -0,0 +1,63 @@ +using System; +using SmartStore.Collections; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Services.Media +{ + /// + /// Reads media folder objects either from cache (as ) or from storage. + /// Methods with in their signature always work against the cache and enable very fast + /// data retrieval. + /// The tree cache is invalidated automatically after any storage action. + /// + public interface IFolderService : IScopedService + { + /// + /// Gets the root folder node from cache. + /// + TreeNode GetRootNode(); + + /// + /// Gets a folder node by storage id. + /// + TreeNode GetNodeById(int id); + + /// + /// Gets a folder node by path, e.g. "catalog/subfolder1/subfolder2". + /// The first token always refers to an album. This method operates very fast + /// because all possible pathes are cached. + /// + TreeNode GetNodeByPath(string path); + + /// + /// Checks whether any given path does already exist and - if true - + /// outputs a unique leaf folder name that can be used to save a folder + /// to the database. + /// + /// The path to check. + /// If method return value is true: the new unique folder name, otherwise: null. + /// true when passed path exists already. + bool CheckUniqueFolderName(string path, out string newName); + + /// + /// Gets a folder entity by storage id from the database (bypassing the cache). + /// + /// Whether all containing files should be eager loaded. + MediaFolder GetFolderById(int id, bool withFiles = false); + + /// + /// Inserts a new folder to the database. + /// + void InsertFolder(MediaFolder folder); + + /// + /// Deletes a folder from the database. + /// + void DeleteFolder(MediaFolder folder); + + /// + /// Updates a folder in the database. + /// + void UpdateFolder(MediaFolder folder); + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Album/MediaFolderInfo.cs b/src/Libraries/SmartStore.Services/Media/Album/MediaFolderInfo.cs new file mode 100644 index 0000000000..1f51f35926 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Album/MediaFolderInfo.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using SmartStore.Collections; +using SmartStore.Core.IO; + +namespace SmartStore.Services.Media +{ + public partial class MediaFolderInfo : IFolder + { + public MediaFolderInfo(TreeNode node) + { + Node = node; + } + + [JsonIgnore] + public TreeNode Node { get; } + + [JsonProperty("filesCount")] + public int FilesCount => Node.Value.FilesCount; + + public static implicit operator TreeNode(MediaFolderInfo folderInfo) + { + return folderInfo.Node; + } + + [JsonProperty("id")] + public int Id => Node.Value.Id; + + #region IFolder + + [JsonProperty("path")] + public string Path => Node.Value.Path; + + [JsonProperty("name")] + public string Name => Node.Value.Name; + + [JsonIgnore] + public long Size => 0; + + [JsonIgnore] + public bool Exists => Node.Value.Id > 0; + + [JsonIgnore] + public DateTime LastUpdated => DateTime.UtcNow; + + [JsonIgnore] + public IFolder Parent => Node.Parent == null ? null : new MediaFolderInfo(Node.Parent); + + #endregion + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Album/MediaFolderNode.cs b/src/Libraries/SmartStore.Services/Media/Album/MediaFolderNode.cs new file mode 100644 index 0000000000..2d2fcd73e4 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Album/MediaFolderNode.cs @@ -0,0 +1,39 @@ +using System; + +namespace SmartStore.Services.Media +{ + public class MediaFolderNode + { + /// + /// Whether the folder is a root album node + /// + public bool IsAlbum { get; set; } + + /// + /// The root album name + /// + public string AlbumName { get; set; } + + /// + /// Entity Id + /// + public int Id { get; set; } + + /// + /// Folder name + /// + public string Name { get; set; } + public string Path { get; set; } + public string Slug { get; set; } + public bool CanDetectTracks { get; set; } + public int? ParentId { get; set; } + public int FilesCount { get; set; } + public string ResKey { get; set; } + public bool IncludePath { get; set; } + public int Order { get; set; } + + public string Color { get; set; } + public string OverlayIcon { get; set; } + public string OverlayColor { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Album/SystemAlbumProvider.cs b/src/Libraries/SmartStore.Services/Media/Album/SystemAlbumProvider.cs new file mode 100644 index 0000000000..e809483acc --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Album/SystemAlbumProvider.cs @@ -0,0 +1,396 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Data.Entity; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Media; +using SmartStore.Data.Utilities; +using SmartStore.Core.Domain.Blogs; +using SmartStore.Core.Domain.News; +using SmartStore.Core.Domain.Common; +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.Messages; +using SmartStore.Core.Domain.Stores; +using SmartStore.Core.Domain.Orders; + +namespace SmartStore.Services.Media +{ + public partial class SystemAlbumProvider : IAlbumProvider, IMediaTrackDetector + { + private readonly IDbContext _dbContext; + + public SystemAlbumProvider(IDbContext dbContext) + { + _dbContext = dbContext; + } + + public const string Catalog = "catalog"; + public const string Content = "content"; + public const string Downloads = "download"; + public const string Messages = "message"; + public const string Customers = "customer"; + public const string Files = "file"; + + #region Album Provider + + public IEnumerable GetAlbums() + { + return new[] + { + new MediaAlbum + { + Name = Catalog, + ResKey = "Admin.Catalog", + CanDetectTracks = true, + Order = int.MinValue + }, + new MediaAlbum + { + Name = Content, + ResKey = "Admin.Media.Album.Content", + CanDetectTracks = true, + Order = int.MinValue + 10 + }, + new MediaAlbum + { + Name = Downloads, + ResKey = "Common.Downloads", + CanDetectTracks = true, + Order = int.MinValue + 30 + }, + new MediaAlbum + { + Name = Messages, + ResKey = "Admin.Media.Album.Message", + CanDetectTracks = true, + Order = int.MinValue + 40 + }, + new MediaAlbum + { + Name = Customers, + ResKey = "Admin.Customers", + CanDetectTracks = true, // TBD + Order = int.MinValue + 50 + }, + new MediaAlbum + { + Name = Files, + ResKey = "Admin.Media.Album.File", + CanDetectTracks = false, + IncludePath = true, + Order = int.MaxValue + } + }; + } + + public AlbumDisplayHint GetDisplayHint(MediaAlbum album) + { + + if (album.Name == Catalog) + { + return new AlbumDisplayHint { OverlayIcon = "fa fa-cube" }; + } + if (album.Name == Content) + { + return new AlbumDisplayHint { OverlayIcon = "fa fa-sitemap" }; + } + if (album.Name == Downloads) + { + return new AlbumDisplayHint { OverlayIcon = "fa fa-download" }; + } + if (album.Name == Messages) + { + return new AlbumDisplayHint { OverlayIcon = "fa fa-envelope" }; + } + if (album.Name == Customers) + { + return new AlbumDisplayHint { OverlayIcon = "fa fa-user" }; + } + if (album.Name == Files) + { + // TODO: var(--success) should be system default. + return new AlbumDisplayHint { Color = "var(--success)" }; + } + + return null; + } + + #endregion + + #region Tracking + + public void ConfigureTracks(string albumName, TrackedMediaPropertyTable table) + { + if (albumName == Catalog) + { + table.Register(x => x.MediaFileId); + table.Register(x => x.MediaFileId); + table.Register(x => x.MediaFileId); + table.Register(x => x.MediaFileId); + table.Register(x => x.MediaFileId); + table.Register(x => x.MediaFileId); + table.Register(x => x.MediaFileId); + } + else if (albumName == Content) + { + table.Register(x => x.MediaFileId); + table.Register(x => x.PreviewMediaFileId); + table.Register(x => x.MediaFileId); + table.Register(x => x.PreviewMediaFileId); + table.Register(x => x.LogoMediaFileId); + table.Register(x => x.FavIconMediaFileId); + table.Register(x => x.PngIconMediaFileId); + table.Register(x => x.AppleTouchIconMediaFileId); + table.Register(x => x.MsTileImageMediaFileId); + } + else if (albumName == Downloads) + { + table.Register(x => x.MediaFileId); + } + else if (albumName == Messages) + { + // TODO: (mm) (mc) These props are localizable + table.Register(x => x.Attachment1FileId); + table.Register(x => x.Attachment2FileId); + table.Register(x => x.Attachment3FileId); + } + } + + public IEnumerable DetectAllTracks(string albumName) + { + var ctx = _dbContext; + var entityName = string.Empty; + + // Products + if (albumName == Catalog) + { + // Products + { + var name = nameof(ProductMediaFile); + var p = new FastPager(ctx.Set().AsNoTracking(), 5000); + while (p.ReadNextPage(x => new { x.Id, x.ProductId, x.MediaFileId }, x => x.Id, out var list)) + { + foreach (var x in list) + { + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId, Property = nameof(x.MediaFileId) }; + } + list.Clear(); + } + } + + GC.Collect(); + + // ProductAttributeOption + { + var name = nameof(ProductAttributeOption); + var p = new FastPager(ctx.Set().AsNoTracking().Where(x => x.MediaFileId > 0), 5000); + while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list)) + { + foreach (var x in list) + { + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId, Property = nameof(x.MediaFileId) }; + } + list.Clear(); + } + } + + // ProductVariantAttributeValue + { + var name = nameof(ProductVariantAttributeValue); + var p = new FastPager(ctx.Set().AsNoTracking().Where(x => x.MediaFileId > 0), 5000); + while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list)) + { + foreach (var x in list) + { + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId, Property = nameof(x.MediaFileId) }; + } + list.Clear(); + } + } + + // CheckoutAttributeValue + { + var name = nameof(CheckoutAttributeValue); + var p = new FastPager(ctx.Set().AsNoTracking().Where(x => x.MediaFileId.HasValue), 5000); + while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list)) + { + foreach (var x in list) + { + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value, Property = nameof(x.MediaFileId) }; + } + list.Clear(); + } + } + + GC.Collect(); + + // SpecificationAttributeOption + { + var name = nameof(SpecificationAttributeOption); + var p = new FastPager(ctx.Set().AsNoTracking().Where(x => x.MediaFileId > 0), 5000); + while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list)) + { + foreach (var x in list) + { + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId, Property = nameof(x.MediaFileId) }; + } + list.Clear(); + } + } + + // Categories + { + var name = nameof(Category); + var p = new FastPager(ctx.Set().AsNoTracking().Where(x => x.MediaFileId.HasValue), 5000); + while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list)) + { + foreach (var x in list) + { + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value, Property = nameof(x.MediaFileId) }; + } + list.Clear(); + } + } + + GC.Collect(); + + // Brands + { + var name = nameof(Manufacturer); + var p = new FastPager(ctx.Set().AsNoTracking().Where(x => x.MediaFileId.HasValue), 5000); + while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list)) + { + foreach (var x in list) + { + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value, Property = nameof(x.MediaFileId) }; + } + list.Clear(); + } + } + + yield break; + } + + // Content + if (albumName == Content) + { + // Blog + { + var name = nameof(BlogPost); + var p = new FastPager(ctx.Set().AsNoTracking().Where(x => x.MediaFileId.HasValue || x.PreviewMediaFileId.HasValue)); + while (p.ReadNextPage(x => new { x.Id, x.MediaFileId, x.PreviewMediaFileId }, x => x.Id, out var list)) + { + foreach (var x in list) + { + if (x.MediaFileId.HasValue) + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value, Property = nameof(x.MediaFileId) }; + if (x.PreviewMediaFileId.HasValue) + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.PreviewMediaFileId.Value, Property = nameof(x.PreviewMediaFileId) }; + } + list.Clear(); + } + } + + // NewsItem + { + var name = nameof(NewsItem); + var p = new FastPager(ctx.Set().AsNoTracking().Where(x => x.MediaFileId.HasValue || x.PreviewMediaFileId.HasValue)); + while (p.ReadNextPage(x => new { x.Id, x.MediaFileId, x.PreviewMediaFileId }, x => x.Id, out var list)) + { + foreach (var x in list) + { + if (x.MediaFileId.HasValue) + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value, Property = nameof(x.MediaFileId) }; + if (x.PreviewMediaFileId.HasValue) + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.PreviewMediaFileId.Value, Property = nameof(x.PreviewMediaFileId) }; + } + list.Clear(); + } + } + + // Store + { + var name = nameof(Store); + var p = new FastPager(ctx.Set().AsNoTracking().Where(x => x.LogoMediaFileId > 0)); + while (p.ReadNextPage(x => new { x.Id, x.LogoMediaFileId }, x => x.Id, out var list)) + { + foreach (var x in list) + { + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.LogoMediaFileId, Property = nameof(x.LogoMediaFileId) }; + } + list.Clear(); + } + } + + yield break; + } + + // Downloads + if (albumName == Downloads) + { + var name = nameof(Download); + var p = new FastPager(ctx.Set().AsNoTracking().Where(x => x.MediaFileId.HasValue), 5000); + while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list)) + { + foreach (var x in list) + { + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value, Property = nameof(x.MediaFileId) }; + } + list.Clear(); + } + + yield break; + } + + // Messages + if (albumName == Messages) + { + var name = nameof(MessageTemplate); + var p = new FastPager(ctx.Set().AsNoTracking()); + while (p.ReadNextPage(x => new { x.Id, x.Attachment1FileId, x.Attachment2FileId, x.Attachment3FileId }, x => x.Id, out var list)) + { + foreach (var x in list) + { + if (x.Attachment1FileId.HasValue) + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.Attachment1FileId.Value, Property = nameof(x.Attachment1FileId) }; + if (x.Attachment2FileId.HasValue) + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.Attachment2FileId.Value, Property = nameof(x.Attachment2FileId) }; + if (x.Attachment3FileId.HasValue) + yield return new MediaTrack { EntityId = x.Id, EntityName = name, MediaFileId = x.Attachment3FileId.Value, Property = nameof(x.Attachment3FileId) }; + } + list.Clear(); + } + + yield break; + } + + // Customer + if (albumName == Customers) + { + var name = nameof(Customer); + var key = SystemCustomerAttributeNames.AvatarPictureId; + + // Avatars + var p = new FastPager(ctx.Set().AsNoTracking() + .Where(x => x.KeyGroup == nameof(Customer) && x.Key == key)); + while (p.ReadNextPage(x => new { x.Id, x.EntityId, x.Value }, x => x.Id, out var list)) + { + foreach (var x in list) + { + var id = x.Value.ToInt(); + if (id > 0) + { + yield return new MediaTrack { EntityId = x.EntityId, EntityName = name, MediaFileId = id, Property = key }; + } + } + list.Clear(); + } + + yield break; + } + } + + #endregion + } +} diff --git a/src/Libraries/SmartStore.Services/Media/CachedImageResult.cs b/src/Libraries/SmartStore.Services/Media/CachedImageResult.cs deleted file mode 100644 index 8761739ab8..0000000000 --- a/src/Libraries/SmartStore.Services/Media/CachedImageResult.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using SmartStore.Core.IO; - -namespace SmartStore.Services.Media -{ - /// - /// Contains information about a cached image - /// - /// - /// An instance of this object is always returned, even when - /// the requested image does not physically exists in the storage. - /// - public class CachedImageResult - { - private bool? _exists; - private string _mimeType; - - public CachedImageResult(IFile file) - { - Guard.NotNull(file, nameof(file)); - - File = file; - } - - /// - /// The abstracted file object - /// - public IFile File { get; internal set; } - - /// - /// true when the image exists in the cache, false otherwise. - /// - public bool Exists - { - get - { - return _exists ?? (_exists = File.Exists).Value; - } - // For internal use - set - { - _exists = value; - } - } - - /// - /// The name of the file (without path) - /// - public string FileName - { - get { return System.IO.Path.GetFileName(this.Path); } - } - - public long FileSize - { - get { return !Exists ? 0 : File.Size; } - } - - /// - /// The file extension (without 'dot') - /// - public string Extension { get; set; } - - /// - /// The filemime type - /// - public string MimeType - { - get => _mimeType ?? (_mimeType = MimeTypes.MapNameToMimeType(FileName)); - } - - /// - /// The path relative to the cache root folder - /// - public string Path { get; set; } - - /// - /// The last modified date or null if the file does not exist - /// - public DateTime? LastModifiedUtc - { - get { return Exists ? File.LastUpdated : (DateTime?)null; } - } - - /// - /// Checks whether the file is remote (outside the application's physical root) - /// - public bool IsRemote { get; set; } - } -} diff --git a/src/Libraries/SmartStore.Services/Media/DefaultImageProcessor.cs b/src/Libraries/SmartStore.Services/Media/DefaultImageProcessor.cs deleted file mode 100644 index 1c5e8dc893..0000000000 --- a/src/Libraries/SmartStore.Services/Media/DefaultImageProcessor.cs +++ /dev/null @@ -1,318 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Drawing; -using ImageProcessor; -using ImageProcessor.Imaging; -using ImageProcessor.Imaging.Formats; -using SmartStore.Core.Logging; -using ImageProcessor.Configuration; -using SmartStore.Core.Events; -using SmartStore.Utilities; - -namespace SmartStore.Services.Media -{ - public partial class DefaultImageProcessor : IImageProcessor - { - private static long _totalProcessingTime; - - private readonly IEventPublisher _eventPublisher; - - public DefaultImageProcessor(IEventPublisher eventPublisher) - { - _eventPublisher = eventPublisher; - - Logger = NullLogger.Instance; - } - - public ILogger Logger { get; set; } - - public bool IsSupportedImage(string fileName) - { - var ext = Path.GetExtension(fileName); - if (ext != null) - { - var extension = ext.Trim('.').ToLower(); - return ImageProcessorBootstrapper.Instance.SupportedImageFormats - .SelectMany(x => x.FileExtensions) - .Any(x => x == extension); - } - - return false; - } - - public ProcessImageResult ProcessImage(ProcessImageQuery query) - { - Guard.NotNull(query, nameof(query)); - - ValidateQuery(query); - - var watch = new Stopwatch(); - byte[] inBuffer = null; - - try - { - watch.Start(); - - using (var processor = new ImageFactory(preserveExifData: false, fixGamma: false)) - { - var source = query.Source; - - // Load source - if (source is byte[] b) - { - inBuffer = b; - } - else if (source is Stream s) - { - inBuffer = s.ToByteArray(); - } - else if (source is Image img) - { - processor.Load(img); - } - else if (source is string str) - { - var path = NormalizePath(str); - using (var fs = File.OpenRead(path)) - { - inBuffer = fs.ToByteArray(); - } - } - else - { - throw new ProcessImageException("Invalid source type '{0}' in query.".FormatInvariant(query.Source.GetType().FullName), query); - } - - if (inBuffer != null) - { - processor.Load(inBuffer); - } - - // Pre-process event - _eventPublisher.Publish(new ImageProcessingEvent(query, processor)); - - var result = new ProcessImageResult - { - Query = query, - SourceWidth = processor.Image.Width, - SourceHeight = processor.Image.Height, - SourceMimeType = processor.CurrentImageFormat.MimeType - }; - - // Core processing - ProcessImageCore(query, processor, out var fxApplied); - - // Create & prepare result - var outStream = new MemoryStream(); - processor.Save(outStream); - - var fmt = processor.CurrentImageFormat; - result.FileExtension = fmt.DefaultExtension == "jpeg" ? "jpg" : fmt.DefaultExtension; - result.MimeType = fmt.MimeType; - - result.HasAppliedVisualEffects = fxApplied; - result.Width = processor.Image.Width; - result.Height = processor.Image.Height; - - if (inBuffer != null) - { - // Check whether it is more beneficial to return the source instead of the result. - // Prefer result only if its size is smaller than the source size. - // Result size may be larger if a high-compressed image has been uploaded. - // During image processing the source compression algorithm gets lost and the image may be larger in size - // after encoding with default encoders. - var compare = - // only when image was not altered visually... - !fxApplied - // ...size has not changed - && result.Width == result.SourceWidth - && result.Height == result.SourceHeight - // ...and format has not changed - && result.MimeType == result.SourceMimeType; - - if (compare && inBuffer.LongLength <= outStream.GetBuffer().LongLength) - { - // Source is smaller. Throw away result and get back to source. - outStream.Dispose(); - result.OutputStream = new MemoryStream(inBuffer, 0, inBuffer.Length, true, true); - } - } - - // Set output stream - if (result.OutputStream == null) - { - result.OutputStream = outStream; - } - - // Post-process event - _eventPublisher.Publish(new ImageProcessedEvent(query, processor, result)); - - result.OutputStream.Position = 0; - - result.ProcessTimeMs = watch.ElapsedMilliseconds; - - return result; - } - } - catch (Exception ex) - { - var pex = new ProcessImageException(query, ex); - Logger.Error(pex); - throw pex; - } - finally - { - if (query.DisposeSource && query.Source is IDisposable source) - { - source.Dispose(); - } - - watch.Stop(); - _totalProcessingTime += watch.ElapsedMilliseconds; - } - } - - /// - /// Processes the loaded image. Inheritors should NOT save the image, this is done by the main method. - /// - /// Query - /// Processor instance - /// - /// Should be true if any effect has been applied that potentially changes the image visually (like background color, contrast, sharpness etc.). - /// Resize and compression quality does NOT count as FX. - /// - protected virtual void ProcessImageCore(ProcessImageQuery query, ImageFactory processor, out bool fxApplied) - { - fxApplied = false; - - // Resize - var size = query.MaxWidth != null || query.MaxHeight != null - ? new Size(query.MaxWidth ?? 0, query.MaxHeight ?? 0) - : Size.Empty; - - if (!size.IsEmpty) - { - processor.Resize(new ResizeLayer( - size, - resizeMode: ConvertScaleMode(query.ScaleMode), - anchorPosition: ConvertAnchorPosition(query.AnchorPosition), - upscale: false)); - } - - if (query.BackgroundColor.HasValue()) - { - processor.BackgroundColor(ColorTranslator.FromHtml(query.BackgroundColor)); - fxApplied = true; - } - - // Format - if (query.Format != null) - { - var format = query.Format as ISupportedImageFormat; - - if (format == null && query.Format is string) - { - var requestedFormat = ((string)query.Format).ToLowerInvariant(); - switch (requestedFormat) - { - case "jpg": - case "jpeg": - format = new JpegFormat(); - break; - case "png": - format = new PngFormat(); - break; - case "gif": - format = new GifFormat(); - break; - } - } - - if (format != null) - { - processor.Format(format); - } - } - - // Set Quality - if (query.Quality.HasValue) - { - processor.Quality(query.Quality.Value); - } - } - - private string NormalizePath(string path) - { - if (path.IsWebUrl()) - { - throw new NotSupportedException($"Remote images cannot be processed: Path: {path}"); - } - - if (!PathHelper.IsAbsolutePhysicalPath(path)) - { - path = CommonHelper.MapPath(path); - } - - return path; - } - - private void ValidateQuery(ProcessImageQuery query) - { - if (query.Source == null) - { - throw new ArgumentException("During image processing 'ProcessImageQuery.Source' must not be null.", nameof(query)); - } - } - - public long TotalProcessingTimeMs - { - get { return _totalProcessingTime; } - } - - private ResizeMode ConvertScaleMode(string mode) - { - switch (mode.EmptyNull().ToLower()) - { - case "boxpad": - return ResizeMode.BoxPad; - case "crop": - return ResizeMode.Crop; - case "min": - return ResizeMode.Min; - case "pad": - return ResizeMode.Pad; - case "stretch": - return ResizeMode.Stretch; - default: - return ResizeMode.Max; - } - } - - private AnchorPosition ConvertAnchorPosition(string anchor) - { - switch (anchor.EmptyNull().ToLower()) - { - case "top": - return AnchorPosition.Top; - case "bottom": - return AnchorPosition.Bottom; - case "left": - return AnchorPosition.Left; - case "right": - return AnchorPosition.Right; - case "top-left": - return AnchorPosition.TopLeft; - case "top-right": - return AnchorPosition.TopRight; - case "bottom-left": - return AnchorPosition.BottomLeft; - case "bottom-right": - return AnchorPosition.BottomRight; - default: - return AnchorPosition.Center; - } - } - } -} diff --git a/src/Libraries/SmartStore.Services/Media/DownloadService.cs b/src/Libraries/SmartStore.Services/Media/DownloadService.cs index 16a0feee63..89297b15b0 100644 --- a/src/Libraries/SmartStore.Services/Media/DownloadService.cs +++ b/src/Libraries/SmartStore.Services/Media/DownloadService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using NuGet; using SmartStore.Collections; @@ -9,109 +10,96 @@ using SmartStore.Core.Domain.Media; using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Payments; -using SmartStore.Core.Events; -using SmartStore.Core.Plugins; -using SmartStore.Services.Configuration; -using SmartStore.Services.Media.Storage; namespace SmartStore.Services.Media { public partial class DownloadService : IDownloadService { private readonly IRepository _downloadRepository; - private readonly IEventPublisher _eventPubisher; - private readonly Provider _storageProvider; - - public DownloadService( - IRepository downloadRepository, - IEventPublisher eventPubisher, - ISettingService settingService, - IProviderManager providerManager) + private readonly IMediaService _mediaService; + + public DownloadService(IRepository downloadRepository, IMediaService mediaService) { _downloadRepository = downloadRepository; - _eventPubisher = eventPubisher; - - var systemName = settingService.GetSettingByKey("Media.Storage.Provider", DatabaseMediaStorageProvider.SystemName); - - _storageProvider = providerManager.GetProvider(systemName); - } - - private void UpdateDownloadCore(Download download, byte[] downloadBinary, bool updateDataStorage) - { - download.UpdatedOnUtc = DateTime.UtcNow; - - _downloadRepository.Update(download); - - if (updateDataStorage) - { - // save to storage - _storageProvider.Value.Save(download.ToMedia(), downloadBinary); - } - } + _mediaService = mediaService; + } - public virtual Download GetDownloadById(int downloadId) + public virtual Download GetDownloadById(int downloadId) { if (downloadId == 0) return null; - - var download = _downloadRepository.GetById(downloadId); + + var download = _downloadRepository.Table.Expand(x => x.MediaFile).FirstOrDefault(x => x.Id == downloadId); return download; } - public virtual IList GetDownloadsByIds(int[] downloadIds) - { - if (downloadIds == null || downloadIds.Length == 0) - return new List(); + public virtual IList GetDownloadsByIds(int[] downloadIds) + { + if (downloadIds == null || downloadIds.Length == 0) + return new List(); - var query = from dl in _downloadRepository.Table - where downloadIds.Contains(dl.Id) - select dl; + var query = from dl in _downloadRepository.Table.Expand(x => x.MediaFile) + where downloadIds.Contains(dl.Id) + select dl; - var downloads = query.ToList(); + var downloads = query.ToList(); - // sort by passed identifier sequence - return downloads.OrderBySequence(downloadIds).ToList(); - } - - public virtual IList GetDownloadsFor(TEntity entity) where TEntity : BaseEntity - { - Guard.NotNull(entity, nameof(entity)); + // sort by passed identifier sequence + return downloads.OrderBySequence(downloadIds).ToList(); + } - return GetDownloadsFor(entity.Id, entity.GetUnproxiedType().Name); - } + public virtual IList GetDownloadsFor(TEntity entity, bool versionedFilesOnly = false) where TEntity : BaseEntity + { + Guard.NotNull(entity, nameof(entity)); - public virtual IList GetDownloadsFor(int entityId, string entityName) - { - if (entityId > 0) - { - var downloads = (from x in _downloadRepository.Table - where x.EntityId == entityId && x.EntityName == entityName - select x).ToList(); + return GetDownloadsFor(entity.Id, entity.GetUnproxiedType().Name, versionedFilesOnly); + } - if (downloads.Any()) - { - var idsOrderedByVersion = downloads - .Select(x => new { x.Id, Version = new SemanticVersion(x.FileVersion.HasValue() ? x.FileVersion : "1.0.0.0") }) - .OrderBy(x => x.Version) - .Select(x => x.Id); + public virtual IList GetDownloadsFor(int entityId, string entityName, bool versionedFilesOnly = false) + { + if (entityId > 0) + { + var downloads = (from x in _downloadRepository.Table.Expand(x => x.MediaFile) + where x.EntityId == entityId && x.EntityName == entityName + select x).ToList(); + + if (versionedFilesOnly) + { + downloads = downloads.Where(x => !string.IsNullOrEmpty(x.FileVersion)).ToList(); + } + + if (downloads.Any()) + { + var idsOrderedByVersion = downloads + .Select(x => + { + if (SemanticVersion.TryParse(x.FileVersion.HasValue() ? x.FileVersion : "1.0.0.0", out var version)) + { + return new { x.Id, Version = version }; + } + + return new { x.Id, Version = new SemanticVersion(0, 0, 0, 0) }; + }) + .OrderByDescending(x => x.Version) + .Select(x => x.Id); - downloads = new List(downloads.OrderBySequence(idsOrderedByVersion)); + downloads = new List(downloads.OrderBySequence(idsOrderedByVersion)); - return downloads; - } - } + return downloads; + } + } - return new List(); - } + return new List(); + } public virtual Download GetDownloadByVersion(int entityId, string entityName, string fileVersion) { if (entityId > 0 && fileVersion.HasValue() && entityName.HasValue()) { - var download = (from x in _downloadRepository.Table - where x.EntityId == entityId && x.EntityName.Equals(entityName) && x.FileVersion.Equals(fileVersion) - select x).FirstOrDefault(); - + var download = (from x in _downloadRepository.Table.Expand(x => x.MediaFile) + where x.EntityId == entityId && x.EntityName.Equals(entityName) && x.FileVersion.Equals(fileVersion) + select x).FirstOrDefault(); + return download; } @@ -123,9 +111,9 @@ public virtual Multimap GetDownloadsByEntityIds(int[] entityIds, Guard.NotNull(entityIds, nameof(entityIds)); Guard.NotEmpty(entityName, nameof(entityName)); - var query = _downloadRepository.TableUntracked + var query = _downloadRepository.TableUntracked.Expand(x => x.MediaFile) .Where(x => entityIds.Contains(x.EntityId) && x.EntityName == entityName) - .OrderBy(x => x.FileVersion); + .OrderBy(x => x.FileVersion); var map = query .ToList() @@ -137,52 +125,56 @@ public virtual Multimap GetDownloadsByEntityIds(int[] entityIds, public virtual Download GetDownloadByGuid(Guid downloadGuid) { if (downloadGuid == Guid.Empty) + { return null; + } + + var query = from x in _downloadRepository.Table.Expand(x => x.MediaFile) + where x.DownloadGuid == downloadGuid + select x; - var query = from o in _downloadRepository.Table - where o.DownloadGuid == downloadGuid - select o; - var order = query.FirstOrDefault(); - return order; + var download = query.FirstOrDefault(); + return download; } public virtual void DeleteDownload(Download download) { - Guard.NotNull(download, nameof(download)); - - // delete from storage - _storageProvider.Value.Remove(download.ToMedia()); + Guard.NotNull(download, nameof(download)); - // delete entity - _downloadRepository.Delete(download); + _downloadRepository.Delete(download); } - public virtual void InsertDownload(Download download, byte[] downloadBinary) + public virtual void InsertDownload(Download download) { - Guard.NotNull(download, nameof(download)); + Guard.NotNull(download, nameof(download)); _downloadRepository.Insert(download); - - // save to storage - _storageProvider.Value.Save(download.ToMedia(), downloadBinary); } - public virtual void UpdateDownload(Download download) - { - Guard.NotNull(download, nameof(download)); + public virtual MediaFileInfo InsertDownload(Download download, Stream stream, string fileName) + { + Guard.NotNull(download, nameof(download)); - // we use an overload because a byte array cannot be nullable - UpdateDownloadCore(download, null, false); - } + var path = _mediaService.CombinePaths(SystemAlbumProvider.Downloads, fileName); + var file = _mediaService.SaveFile(path, stream, dupeFileHandling: DuplicateFileHandling.Rename); + file.File.Hidden = true; + download.MediaFile = file.File; - public virtual void UpdateDownload(Download download, byte[] downloadBinary) + _downloadRepository.Insert(download); + + return file; + } + + public virtual void UpdateDownload(Download download) { - Guard.NotNull(download, nameof(download)); + Guard.NotNull(download, nameof(download)); - UpdateDownloadCore(download, downloadBinary, true); + download.UpdatedOnUtc = DateTime.UtcNow; + + _downloadRepository.Update(download); } - public virtual bool IsDownloadAllowed(OrderItem orderItem) + public virtual bool IsDownloadAllowed(OrderItem orderItem) { if (orderItem == null) return false; @@ -196,7 +188,7 @@ public virtual bool IsDownloadAllowed(OrderItem orderItem) return false; var product = orderItem.Product; - if (product == null || !product.IsDownload) + if (product == null || product.Deleted || !product.IsDownload) return false; //payment status @@ -257,11 +249,10 @@ public virtual bool IsLicenseDownloadAllowed(OrderItem orderItem) orderItem.LicenseDownloadId > 0; } - public virtual byte[] LoadDownloadBinary(Download download) - { - Guard.NotNull(download, nameof(download)); - - return _storageProvider.Value.Load(download.ToMedia()); - } - } + public virtual byte[] LoadDownloadBinary(Download download) + { + Guard.NotNull(download, nameof(download)); + return _mediaService.StorageProvider.Load(download.MediaFile); + } + } } diff --git a/src/Libraries/SmartStore.Services/Media/Events.cs b/src/Libraries/SmartStore.Services/Media/Events.cs index 0ab3361917..583f34a90a 100644 --- a/src/Libraries/SmartStore.Services/Media/Events.cs +++ b/src/Libraries/SmartStore.Services/Media/Events.cs @@ -1,99 +1,93 @@ using System; using System.Drawing; +using System.IO; using System.Web; -using ImageProcessor; +using SmartStore.Services.Media.Imaging; namespace SmartStore.Services.Media { - /// - /// Published for every uploaded image which does NOT exceed maximum - /// allowed size. This gives subscribers the chance to still process the image, - /// e.g. to achive better compression before saving image data to storage. - /// This event does NOT get published when the uploaded image is about to be processed anyway. - /// - /// - /// A subscriber should NOT resize the image. But if you do - and you shouldn't :-) - , don't forget to set . - /// - public class ImageUploadValidatedEvent - { - public ImageUploadValidatedEvent(ProcessImageQuery query, Size size) - { - Query = query; - Size = size; - } + /// + /// Published for every uploaded image which does NOT exceed maximum + /// allowed size. This gives subscribers the chance to still process the image, + /// e.g. to achive better compression before saving image data to storage. + /// This event does NOT get published when the uploaded image is about to be processed anyway. + /// + /// + /// A subscriber should NOT resize the image. But if you do - and you shouldn't :-) - , don't forget to set . + /// + public class ImageUploadedEvent + { + public ImageUploadedEvent(ProcessImageQuery query, Size size) + { + Query = query; + Size = size; + } - /// - /// Contains the source (as byte[]), max size, format and default image quality instructions. - /// - public ProcessImageQuery Query { get; private set; } + /// + /// Contains the source (as byte[], STream or path string), max size, format and default image quality instructions. + /// + public ProcessImageQuery Query { get; private set; } - /// - /// The original size of the uploaded image. May be empty. - /// - public Size Size { get; private set; } + /// + /// The original size of the uploaded image. May be empty. + /// + public Size Size { get; private set; } - /// - /// The processing result. If null, the original data - /// from Query.Source will be put to storage. - /// - public byte[] ResultBuffer { get; set; } + /// + /// The processing result. If null, the original data + /// from Query.Source will be put to storage. + /// + public IImage ResultImage { get; set; } + } - /// - /// Size of the result image. - /// - public Size ResultSize { get; set; } - } + /// + /// Published after image query has been created and initialized + /// by the media middleware controller with data from HttpContent.Request.QueryString. + /// This event implies that a thumbnail is about to be created. + /// + public class ImageQueryCreatedEvent + { + public ImageQueryCreatedEvent(ProcessImageQuery query, HttpContextBase httpContext, string mimeType, string extension) + { + Query = query; + HttpContext = httpContext; + MimeType = mimeType; + Extension = extension; + } - /// - /// Published after image query has been created and initialized - /// by the media middleware controller with data from HttpContent.Request.QueryString. - /// This event implies that a thumbnail is about to be created. - /// - public class ImageQueryCreatedEvent - { - public ImageQueryCreatedEvent(ProcessImageQuery query, HttpContextBase httpContext, string mimeType, string extension) - { - Query = query; - HttpContext = httpContext; - MimeType = mimeType; - Extension = extension; - } + public ProcessImageQuery Query { get; private set; } + public HttpContextBase HttpContext { get; private set; } + public string MimeType { get; private set; } + public string Extension { get; private set; } + } - public ProcessImageQuery Query { get; private set; } - public HttpContextBase HttpContext { get; private set; } - public string MimeType { get; private set; } - public string Extension { get; private set; } - } + /// + /// Published before processing begins, but after the source has been loaded. + /// + public class ImageProcessingEvent + { + public ImageProcessingEvent(ProcessImageQuery query, IImage image) + { + Query = query; + Image = image; + } - /// - /// Published before processing begins, but after the source has been loaded. - /// - public class ImageProcessingEvent - { - public ImageProcessingEvent(ProcessImageQuery query, ImageFactory processor) - { - Query = query; - Processor = processor; - } + public ProcessImageQuery Query { get; private set; } + public IImage Image { get; private set; } + } - public ProcessImageQuery Query { get; private set; } - public ImageFactory Processor { get; private set; } - } + /// + /// Published after processing finishes. + /// + public class ImageProcessedEvent + { + public ImageProcessedEvent(ProcessImageQuery query, ProcessImageResult result) + { + Query = query; + Result = result; + } - /// - /// Published after processing finishes and the result is saved to a stream (ProcessImageResult.Result) - /// - public class ImageProcessedEvent - { - public ImageProcessedEvent(ProcessImageQuery query, ImageFactory processor, ProcessImageResult result) - { - Query = query; - Processor = processor; - Result = result; - } - - public ProcessImageQuery Query { get; private set; } - public ImageFactory Processor { get; private set; } - public ProcessImageResult Result { get; private set; } - } + public ProcessImageQuery Query { get; private set; } + public ProcessImageResult Result { get; private set; } + } } diff --git a/src/Libraries/SmartStore.Services/Media/Extensions/IFolderServiceExtensions.cs b/src/Libraries/SmartStore.Services/Media/Extensions/IFolderServiceExtensions.cs new file mode 100644 index 0000000000..e48e0b7949 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Extensions/IFolderServiceExtensions.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SmartStore.Collections; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Services.Media +{ + public static class IFolderServiceExtensions + { + /// + /// Gets a folder entity by path from the database, e.g. "catalog/subfolder1/subfolder2". + /// The first token always refers to an album. This method bypasses the cache. + /// + public static MediaFolder GetFolderByPath(this IFolderService service, string path, bool withFiles = false) + { + return service.GetFolderById(service.GetNodeByPath(path)?.Value?.Id ?? 0, withFiles); + } + + /// + /// Finds the folder node for a given object. + /// + /// The found folder node or null. + public static TreeNode FindNode(this IFolderService service, MediaFile mediaFile) + { + return service.GetNodeById(mediaFile?.FolderId ?? 0); + } + + /// + /// Finds the root album node for a given object. + /// + /// The found album node or null. + public static TreeNode FindAlbum(this IFolderService service, MediaFile mediaFile) + { + return FindNode(service, mediaFile)?.Closest(x => x.Value.IsAlbum); + } + + /// + /// Finds the root album node for a given folder id. + /// + /// The found album node or null. + public static TreeNode FindAlbum(this IFolderService service, int folderId) + { + return service.GetNodeById(folderId)?.Closest(x => x.Value.IsAlbum); + } + + /// + /// Checks whether all passed files are contained in the same album. + /// + public static bool AreInSameAlbum(this IFolderService service, params MediaFile[] files) + { + return files.Select(x => FindAlbum(service, x)).Distinct().Count() <= 1; + } + + /// + /// Checks whether all passed folder ids are children of the same album. + /// + public static bool AreInSameAlbum(this IFolderService service, params int[] folderIds) + { + return folderIds.Select(x => FindAlbum(service, x)).Distinct().Count() <= 1; + } + + public static IEnumerable GetNodesFlattened(this IFolderService service, string path, bool includeSelf = true) + { + var node = service.GetNodeByPath(path); + if (node == null) + { + return Enumerable.Empty(); + } + + return node.FlattenNodes(includeSelf).Select(x => x.Value); + } + + public static IEnumerable GetNodesFlattened(this IFolderService service, int folderId, bool includeSelf = true) + { + var node = service.GetNodeById(folderId); + if (node == null) + { + return Enumerable.Empty(); + } + + return node.FlattenNodes(includeSelf).Select(x => x.Value); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Extensions/IMediaServiceExtensions.cs b/src/Libraries/SmartStore.Services/Media/Extensions/IMediaServiceExtensions.cs new file mode 100644 index 0000000000..47cc315cfa --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Extensions/IMediaServiceExtensions.cs @@ -0,0 +1,148 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.IO; +using SmartStore.Services.Media.Imaging; + +namespace SmartStore.Services.Media +{ + public static class IMediaServiceExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MediaSearchResult SearchFiles(this IMediaService service, MediaSearchQuery query, MediaLoadFlags flags = MediaLoadFlags.AsNoTracking) + { + return service.SearchFiles(query, null, flags); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static async Task SearchFilesAsync(this IMediaService service, MediaSearchQuery query, MediaLoadFlags flags = MediaLoadFlags.AsNoTracking) + { + return await service.SearchFilesAsync(query, null, flags); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetUrl(this IMediaService service, int? fileId, ProcessImageQuery imageQuery, string host = null, bool doFallback = true) + { + return service.GetUrl(service.GetFileById(fileId ?? 0, MediaLoadFlags.AsNoTracking), imageQuery, host, doFallback); + } + + public static string GetUrl(this IMediaService service, int? fileId, int thumbnailSize, string host = null, bool doFallback = true) + { + ProcessImageQuery query = thumbnailSize > 0 + ? new ProcessImageQuery { MaxSize = thumbnailSize } + : null; + + return service.GetUrl(service.GetFileById(fileId ?? 0, MediaLoadFlags.AsNoTracking), query, host, doFallback); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetUrl(this IMediaService service, MediaFile file, int thumbnailSize, string host = null, bool doFallback = true) + { + ProcessImageQuery query = thumbnailSize > 0 + ? new ProcessImageQuery { MaxSize = thumbnailSize } + : null; + + return service.GetUrl(file != null ? service.ConvertMediaFile(file) : null, query, host, doFallback); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetUrl(this IMediaService service, MediaFileInfo file, int thumbnailSize, string host = null, bool doFallback = true) + { + ProcessImageQuery query = thumbnailSize > 0 + ? new ProcessImageQuery { MaxSize = thumbnailSize } + : null; + + return service.GetUrl(file, query, host, doFallback); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetFallbackUrl(this IMediaService service, int thumbnailSize = 0) + { + ProcessImageQuery query = thumbnailSize > 0 + ? new ProcessImageQuery { MaxSize = thumbnailSize } + : null; + + return service.GetUrl((MediaFileInfo)null, query, null, true); + } + + /// + /// Tries to find an equal file by comparing the source buffer to a list of files. + /// + /// Binary source file data to find a match for. + /// The sequence of files to seek within for duplicates. + /// A file from the collection whose content is equal to . + /// The passed file binary when no file equals in the sequence, null otherwise. + public static byte[] FindEqualFile(this IMediaService service, byte[] sourceBuffer, IEnumerable files, out MediaFile equalFile) + { + Guard.NotNull(sourceBuffer, nameof(sourceBuffer)); + + if (!service.FindEqualFile(new MemoryStream(sourceBuffer), files, false, out equalFile)) + { + return sourceBuffer; + } + + return null; + } + + /// + /// Tries to find an equal file by file name, then by comparing the binary contents of the matched files to binary content. + /// + /// The full physical path to the source file to find a duplicate for (e.g. a local or downloaded file during an import process). + /// The id of the folder in which to look for duplicates. + /// Whether to search in subfolders too. + /// The first file whose content is equal to the content of . + /// true when a duplicate file was found, false otherwise. + public static bool FindEqualFile(this IMediaService service, string sourcePath, int targetFolderId, bool deepSearch, out MediaFile equalFile) + { + Guard.NotEmpty(sourcePath, nameof(sourcePath)); + + var fi = new FileInfo(sourcePath); + if (!fi.Exists) + { + equalFile = null; + return false; + } + + return FindEqualFile(service, fi.OpenRead(), fi.Name, targetFolderId, deepSearch, out equalFile); + } + + /// + /// Tries to find an equal file by file name, then by comparing the binary contents of the matched files to content. + /// + /// The source file stream to find a duplicate for (e.g. a local or downloaded file during an import process). + /// The file name used to determine potential duplicates to check against. + /// The id of the folder in which to look for duplicates. + /// Whether to search in subfolders too. + /// The first file whose content is equal to the content of . + /// true when a duplicate file was found, false otherwise. + public static bool FindEqualFile(this IMediaService service, Stream source, string fileName, int targetFolderId, bool deepSearch, out MediaFile equalFile) + { + Guard.NotNull(source, nameof(source)); + Guard.NotEmpty(fileName, nameof(fileName)); + Guard.IsPositive(targetFolderId, nameof(targetFolderId)); + + equalFile = null; + + var query = new MediaSearchQuery + { + FolderId = targetFolderId, + DeepSearch = deepSearch, + ExactMatch = true, + Term = fileName, + IncludeAltForTerm = false + }; + + var matches = service.SearchFiles(query); + + if (matches.TotalCount == 0) + { + return false; + } + + return service.FindEqualFile(source, matches.Select(x => x.File), true, out equalFile); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Extensions/IMediaTrackerExtensions.cs b/src/Libraries/SmartStore.Services/Media/Extensions/IMediaTrackerExtensions.cs new file mode 100644 index 0000000000..6f14b61f58 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Extensions/IMediaTrackerExtensions.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using SmartStore.Core; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Services.Media +{ + public static class IMediaTrackerExtensions + { + public static void Track(this IMediaTracker tracker, T entity, Expression> path) where T : BaseEntity, new() + { + Guard.NotNull(path, nameof(path)); + + var getter = path.CompileFast(); + var mediaFileId = getter.Invoke(entity); + if (mediaFileId > 0) + tracker.Track(entity, mediaFileId, path.ExtractPropertyInfo().Name); + } + + public static void Track(this IMediaTracker tracker, T entity, Expression> path) where T : BaseEntity, new() + { + Guard.NotNull(path, nameof(path)); + + var getter = path.CompileFast(); + var mediaFileId = getter.Invoke(entity); + if (mediaFileId > 0) + tracker.Track(entity, mediaFileId.Value, path.ExtractPropertyInfo().Name); + } + + public static void Untrack(this IMediaTracker tracker, T entity, Expression> path) where T : BaseEntity, new() + { + Guard.NotNull(path, nameof(path)); + + var getter = path.CompileFast(); + var mediaFileId = getter.Invoke(entity); + if (mediaFileId > 0) + tracker.Untrack(entity, mediaFileId, path.ExtractPropertyInfo().Name); + } + + public static void Untrack(this IMediaTracker tracker, T entity, Expression> path) where T : BaseEntity, new() + { + Guard.NotNull(path, nameof(path)); + + var getter = path.CompileFast(); + var mediaFileId = getter.Invoke(entity); + if (mediaFileId > 0) + tracker.Untrack(entity, mediaFileId.Value, path.ExtractPropertyInfo().Name); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Track(this IMediaTracker tracker, MediaTrack track) + { + if (track == null) + return; + + tracker.TrackMany(new[] { track }); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void UpdateTracks(this IMediaTracker tracker, BaseEntity entity, int? prevMediaFileId, int? currentMediaFileId, string propertyName) + { + UpdateTracks(tracker, entity.Id, entity.GetEntityName(), prevMediaFileId, currentMediaFileId, propertyName); + } + + public static void UpdateTracks(this IMediaTracker tracker, int entityId, string entityName, int? prevMediaFileId, int? currentMediaFileId, string propertyName) + { + var tracks = new List(2); + bool isModified = prevMediaFileId != currentMediaFileId; + + if (prevMediaFileId > 0 && isModified) + { + tracks.Add(new MediaTrack + { + EntityId = entityId, + EntityName = entityName, + MediaFileId = prevMediaFileId.Value, + Property = propertyName, + Operation = MediaTrackOperation.Untrack + }); + } + + if (currentMediaFileId > 0 && isModified) + { + tracks.Add(new MediaTrack + { + EntityId = entityId, + EntityName = entityName, + MediaFileId = currentMediaFileId.Value, + Property = propertyName, + Operation = MediaTrackOperation.Track + }); + } + + if (tracks.Count > 0) + { + tracker.TrackMany(tracks); + } + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Extensions/MediaFileExtensions.cs b/src/Libraries/SmartStore.Services/Media/Extensions/MediaFileExtensions.cs new file mode 100644 index 0000000000..4b54a3295b --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Extensions/MediaFileExtensions.cs @@ -0,0 +1,70 @@ +using System.IO; +using SmartStore.Core; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.Messages; +using SmartStore.Core.IO; +using SmartStore.Services.Media.Storage; + +namespace SmartStore.Services.Media +{ + public static class MediaFileExtensions + { + /// + /// Applies Blob to file + /// + /// The file binary (can be null) + public static void ApplyBlob(this MediaFile file, byte[] blob) + { + Guard.NotNull(file, nameof(file)); + + if (blob == null || blob.LongLength == 0) + { + file.MediaStorageId = null; + file.MediaStorage = null; + } + else + { + if (file.MediaStorage != null) + { + file.MediaStorage.Data = blob; + } + else + { + file.MediaStorage = new MediaStorage { Data = blob }; + } + } + } + + /// + /// Refreshes file metadata like size, dimensions etc. + /// + /// The file stream (can be null) + public static void RefreshMetadata(this MediaFile file, Stream stream) + { + Guard.NotNull(file, nameof(file)); + + file.Size = stream != null ? (int)stream.Length : 0; + file.Width = null; + file.Height = null; + file.PixelSize = null; + + if (stream != null && file.MediaType == MediaType.Image) + { + try + { + var size = ImageHeader.GetDimensions(stream, file.MimeType); + file.Width = size.Width; + file.Height = size.Height; + file.PixelSize = size.Width * size.Height; + } + catch + { + // Don't attempt again + file.Width = 0; + file.Height = 0; + file.PixelSize = 0; + } + } + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Handlers/IMediaHandler.cs b/src/Libraries/SmartStore.Services/Media/Handlers/IMediaHandler.cs new file mode 100644 index 0000000000..7c681b75e1 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Handlers/IMediaHandler.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SmartStore.Services.Media +{ + public interface IMediaHandler + { + int Order { get; } + Task ExecuteAsync(MediaHandlerContext context); + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Handlers/ImageHandler.cs b/src/Libraries/SmartStore.Services/Media/Handlers/ImageHandler.cs new file mode 100644 index 0000000000..471adb27d1 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Handlers/ImageHandler.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SmartStore.Core.Logging; +using SmartStore.Services.Media.Imaging; + +namespace SmartStore.Services.Media +{ + public class ImageHandler : ImageHandlerBase + { + private readonly IImageProcessor _imageProcessor; + + public ImageHandler(IImageProcessor imageProcessor, IImageCache imageCache, MediaExceptionFactory exceptionFactory) + : base(imageCache, exceptionFactory) + { + _imageProcessor = imageProcessor; + } + + protected override bool IsProcessable(MediaHandlerContext context) + { + return context.ImageQuery.NeedsProcessing(true) && _imageProcessor.Factory.IsSupportedImage(context.PathData.Extension); + } + + protected override Task ProcessImageAsync(MediaHandlerContext context, CachedImage cachedImage, Stream inputStream) + { + var processQuery = new ProcessImageQuery(context.ImageQuery) + { + Source = inputStream, + Format = context.ImageQuery.Format ?? cachedImage.Extension, + FileName = cachedImage.FileName, + DisposeSource = false + }; + + using (var result = _imageProcessor.ProcessImage(processQuery, false)) + { + Logger.DebugFormat($"Processed image '{cachedImage.FileName}' in {result.ProcessTimeMs} ms.", null); + + var ext = result.Image.Format.DefaultExtension; + + if (!cachedImage.Extension.IsCaseInsensitiveEqual(ext)) + { + // jpg <> jpeg + cachedImage.Path = Path.ChangeExtension(cachedImage.Path, ext); + cachedImage.Extension = ext; + } + + context.ResultImage = result.Image; + } + + return Task.CompletedTask; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Handlers/ImageHandlerBase.cs b/src/Libraries/SmartStore.Services/Media/Handlers/ImageHandlerBase.cs new file mode 100644 index 0000000000..cf00df9baa --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Handlers/ImageHandlerBase.cs @@ -0,0 +1,135 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using SmartStore.Core.Localization; +using SmartStore.Core.Logging; +using SmartStore.Services.Media.Imaging; +using SmartStore.Utilities.Threading; + +namespace SmartStore.Services.Media +{ + public abstract class ImageHandlerBase : IMediaHandler + { + protected ImageHandlerBase(IImageCache imageCache, MediaExceptionFactory exceptionFactory) + { + ImageCache = imageCache; + ExceptionFactory = exceptionFactory; + } + + public ILogger Logger { get; set; } = NullLogger.Instance; + public IImageCache ImageCache { get; set; } + public MediaExceptionFactory ExceptionFactory { get; set; } + public Localizer T { get; set; } = NullLocalizer.Instance; + + public virtual int Order => -100; + + public async Task ExecuteAsync(MediaHandlerContext context) + { + if (!IsProcessable(context)) + { + return; + } + + var query = context.ImageQuery; + var pathData = context.PathData; + + var cachedImage = ImageCache.Get(context.MediaFileId, pathData, query); + + if (!pathData.Extension.IsCaseInsensitiveEqual(cachedImage.Extension)) + { + // The query requests another format. + // Adjust extension and mime type fo proper ETag creation. + pathData.Extension = cachedImage.Extension; + pathData.MimeType = cachedImage.MimeType; + } + + var exists = cachedImage.Exists; + + if (exists && cachedImage.FileSize == 0) + { + // Empty file means: thumb extraction failed before and will most likely fail again. + // Don't bother proceeding. + context.Exception = ExceptionFactory.ExtractThumbnail(cachedImage.FileName); + context.Executed = true; + return; + } + + if (!exists) + { + // Lock concurrent requests to same resource + using (await KeyedLock.LockAsync("ImageHandlerBase.Execute." + cachedImage.Path)) + { + ImageCache.RefreshInfo(cachedImage); + + // File could have been processed by another request in the meantime, check again. + if (!cachedImage.Exists) + { + // Call inner function + var sourceFile = context.SourceFile; + if (sourceFile == null || sourceFile.Size == 0) + { + context.Executed = true; + return; + } + + var inputStream = sourceFile.OpenRead(); + if (inputStream == null) + { + context.Exception = ExceptionFactory.ExtractThumbnail(sourceFile.Path, T("Admin.Media.Exception.NullInputStream")); + context.Executed = true; + return; + } + + try + { + await ProcessImageAsync(context, cachedImage, inputStream); + } + catch (Exception ex) + { + Logger.Error(ex); + + if (ex is ExtractThumbnailException) + { + // Thumbnail extraction failed and we must assume that it always will fail. + // Therefore we create an empty file to prevent repetitive processing. + using (var memStream = new MemoryStream()) + { + await ImageCache.PutAsync(cachedImage, memStream); + } + } + + context.Exception = ex; + context.Executed = true; + return; + } + finally + { + if (inputStream != null) + { + inputStream.Dispose(); + } + } + + if (context.ResultImage != null) + { + ImageCache.Put(cachedImage, context.ResultImage); + context.ResultFile = cachedImage.File; + } + + context.Executed = true; + return; + + } + } + } + + // Cached image existed already + context.ResultFile = cachedImage.File; + context.Executed = true; + } + + protected abstract bool IsProcessable(MediaHandlerContext context); + + protected abstract Task ProcessImageAsync(MediaHandlerContext context, CachedImage cachedImage, Stream inputStream); + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Handlers/MediaHandlerContext.cs b/src/Libraries/SmartStore.Services/Media/Handlers/MediaHandlerContext.cs new file mode 100644 index 0000000000..0ac54584ec --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Handlers/MediaHandlerContext.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Mvc; +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.IO; +using SmartStore.Core.Security; +using SmartStore.Services.Media.Imaging; +using SmartStore.Utilities; + +namespace SmartStore.Services.Media +{ + public class MediaHandlerContext + { + private Lazy _lazySourceFile; + private IFile _sourceFile; + + public MediaHandlerContext() + { + _lazySourceFile = new Lazy(() => GetSourceFile(), false); + } + + public IMediaService MediaService { get; set; } + public IPermissionService PermissionService { get; set; } + public Customer CurrentCustomer { get; set; } + public HttpContextBase HttpContext { get; set; } + + public int MediaFileId { get; set; } + public string RawPath { get; set; } + + public MediaPathData PathData { get; set; } + public ProcessImageQuery ImageQuery { get; set; } + + public IFile SourceFile + { + get => _sourceFile ?? _lazySourceFile.Value; + set => _sourceFile = value; + } + + public bool Executed { get; set; } + public Exception Exception { get; set; } + public IFile ResultFile { get; set; } + public IImage ResultImage { get; set; } + + private IFile GetSourceFile() + { + if (MediaFileId == 0) + { + // This is most likely a request for a default placeholder image + var fallbackImagePath = Path.Combine(MediaUrlGenerator.FallbackImagesRootPath, RawPath).Replace('\\', '/'); + var fi = new FileInfo(CommonHelper.MapPath("~/" + fallbackImagePath, false)); + if (!fi.Exists) + return null; + + return new LocalFileSystem.LocalFile(fallbackImagePath, fi); + } + else + { + // Get file from DB + var mediaFile = MediaService.GetFileById(MediaFileId, MediaLoadFlags.AsNoTracking); + + // File must exist + if (mediaFile == null) + return null; + + // Serve deleted or hidden files only with sufficient permission + if ((mediaFile.Deleted || mediaFile.Hidden) && !PermissionService.Authorize(Permissions.Media.Update, CurrentCustomer)) + return null; + + //// File's mime must match requested mime + //if (!mediaFile.MimeType.IsCaseInsensitiveEqual(prevMime ?? pathData.MimeType)) + // return null; + + // File path must match requested path and file title + // TODO: (mm) (mc) what about MIME and EXT? + if (mediaFile.FolderId != PathData.Folder?.Id || !mediaFile.Title.IsCaseInsensitiveEqual(PathData.FileTitle)) + return null; + + return mediaFile; + } + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/IDownloadService.cs b/src/Libraries/SmartStore.Services/Media/IDownloadService.cs index 245bfe9bb3..39e097e40e 100644 --- a/src/Libraries/SmartStore.Services/Media/IDownloadService.cs +++ b/src/Libraries/SmartStore.Services/Media/IDownloadService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using SmartStore.Collections; using SmartStore.Core; using SmartStore.Core.Domain.Media; @@ -16,27 +17,27 @@ public partial interface IDownloadService /// Download Download GetDownloadById(int downloadId); - /// - /// Gets downloads by identifiers - /// - /// Download identifiers - /// List of download entities - IList GetDownloadsByIds(int[] downloadIds); + /// + /// Gets downloads by identifiers + /// + /// Download identifiers + /// List of download entities + IList GetDownloadsByIds(int[] downloadIds); - /// - /// Gets downloads assigned to an entity - /// - /// Entity to get download for - /// List of download entities sorted by FileVersion - IList GetDownloadsFor(TEntity entity) where TEntity : BaseEntity; + /// + /// Gets downloads assigned to an entity + /// + /// Entity to get download for + /// List of download entities sorted by FileVersion + IList GetDownloadsFor(TEntity entity, bool versionedFilesOnly = false) where TEntity : BaseEntity; - /// - /// Gets downloads by entity identifier - /// - /// Entity identifier - /// Entity name - /// List of download entities - IList GetDownloadsFor(int entityId, string entityName); + /// + /// Gets downloads by entity identifier + /// + /// Entity identifier + /// Entity name + /// List of download entities + IList GetDownloadsFor(int entityId, string entityName, bool versionedFilesOnly = false); /// /// Gets downloads by entity identifier & fileversion @@ -72,21 +73,22 @@ public partial interface IDownloadService /// Inserts a download /// /// Download - /// Download binary, can be null - void InsertDownload(Download download, byte[] downloadBinary); + void InsertDownload(Download download); - /// - /// Updates the download but leaves the binary data untouched - /// - /// Download - void UpdateDownload(Download download); + /// + /// Inserts a download. + /// + /// Download entity. + /// Data stream. + /// File name. + /// The inserted object instance. + MediaFileInfo InsertDownload(Download download, Stream stream, string fileName); - /// - /// Updates the download including the associated binary data - /// - /// Download - /// New download binary data associated with the download entity - void UpdateDownload(Download download, byte[] downloadBinary); + /// + /// Updates the download. + /// + /// Download. + void UpdateDownload(Download download); /// /// Gets a value indicating whether download is allowed @@ -102,11 +104,11 @@ public partial interface IDownloadService /// True if license download is allowed; otherwise, false. bool IsLicenseDownloadAllowed(OrderItem orderItem); - /// - /// Load binary data of a download - /// - /// Download - /// Binary data - byte[] LoadDownloadBinary(Download download); - } + /// + /// Load binary data of a download + /// + /// Download + /// Binary data + byte[] LoadDownloadBinary(Download download); + } } diff --git a/src/Libraries/SmartStore.Services/Media/IImageCache.cs b/src/Libraries/SmartStore.Services/Media/IImageCache.cs deleted file mode 100644 index 97b08e2cf7..0000000000 --- a/src/Libraries/SmartStore.Services/Media/IImageCache.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using SmartStore.Core.Domain.Media; -using SmartStore.Core.IO; - -namespace SmartStore.Services.Media -{ - /// - /// Image cache interface - /// - public interface IImageCache - { - /// - /// Adds an image to the cache. - /// - /// An instance of the object, which is returned by the Get() method. - /// The image binary buffer. - /// true when the operation succeded, false otherwise - void Put(CachedImageResult cachedImage, byte[] buffer); - - /// - /// Asynchronously adds an image to the cache. - /// - /// An instance of the object, which is returned by the Get() method. - /// The image binary buffer. - /// true when the operation succeded, false otherwise - Task PutAsync(CachedImageResult cachedImage, byte[] buffer); - - /// - /// Gets an instance of the object, which contains information about a cached image. - /// - /// The picture id of the image to be resolved. - /// The seo friendly picture name of the image to be resolved. - /// The extension of the image to be resolved. - /// The image processing query. - /// An instance of the object - /// If the requested image does not exist in the cache, the value of the Exists property will be false. - CachedImageResult Get(int? pictureId, string seoFileName, string extension, ProcessImageQuery query = null); - - /// - /// Gets an instance of the object, which contains information about a cached image. - /// Use this overload to get thumbnail info about uploaded media manager asset files. - /// - /// The file to get info about. - /// The image processing query. - /// An instance of the object - /// If the requested image does not exist in the cache, the value of the Exists property will be false. - CachedImageResult Get(IFile file, ProcessImageQuery query); - - /// - /// Opens a readonly file stream to the cached image - /// - /// An instance of the object, which is returned by the GetCachedImage() method. - /// File stream - Stream Open(CachedImageResult cachedImage); - - /// - /// Deletes all cached images for the given - /// - /// The for which to delete cached images - void Delete(Picture picture); - - /// - /// Deletes all cached images for the given - /// - /// The for which to delete cached images - void Delete(IFile file); - - /// - /// Deletes all cached images (nukes all files in the cache folder) - /// - void Clear(); - - /// - /// Refreshes the file info. - /// - void RefreshInfo(CachedImageResult cachedImage); - - /// - /// Calculates statistics about the image cache data. - /// - /// The total count of files in the cache. - /// The total size of files in the cache (in bytes) - void CacheStatistics(out long fileCount, out long totalSize); - - /// - /// Resolves a publicly accessible http url to the image - /// - /// The path of the image relative to the cache root path - /// The image http url - string GetPublicUrl(string imagePath); - } -} diff --git a/src/Libraries/SmartStore.Services/Media/IImageProcessor.cs b/src/Libraries/SmartStore.Services/Media/IImageProcessor.cs deleted file mode 100644 index 3ff8fe25f9..0000000000 --- a/src/Libraries/SmartStore.Services/Media/IImageProcessor.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; - -namespace SmartStore.Services.Media -{ - /// - /// A service interface responsible for resizing/processing images. - /// - public interface IImageProcessor - { - /// - /// Determines whether the given file name is processable by the image resizer - /// - /// The name of the file (without path but including extension) - /// A value indicating whether processing is possible - bool IsSupportedImage(string fileName); - - /// - /// Processes an image - /// - /// Resize request - /// The resizing result encapsulated in type - ProcessImageResult ProcessImage(ProcessImageQuery query); - - /// - /// Gets the cumulative total processing time since app start in miliseconds - /// - long TotalProcessingTimeMs { get; } - } -} diff --git a/src/Libraries/SmartStore.Services/Media/IMediaService.cs b/src/Libraries/SmartStore.Services/Media/IMediaService.cs new file mode 100644 index 0000000000..f259253086 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/IMediaService.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; +using SmartStore.Core.Domain.Media; +using SmartStore.Services.Media.Imaging; +using SmartStore.Services.Media.Storage; + +namespace SmartStore.Services.Media +{ + #region Enums + + [Flags] + public enum MediaLoadFlags + { + None = 0, + WithBlob = 1 << 0, + WithTags = 1 << 1, + WithTracks = 1 << 2, + WithFolder = 1 << 3, + AsNoTracking = 1 << 4, + Full = WithBlob | WithTags | WithTracks | WithFolder, + FullNoTracking = Full | AsNoTracking + } + + public enum SpecialMediaFolder + { + AllFiles = -500, + Trash = -400, + Orphans = -300, + TransientFiles = -200, + UnassignedFiles = -100 + } + + public enum FileHandling + { + SoftDelete, + MoveToRoot, + Delete + } + + public enum DuplicateFileHandling + { + ThrowError, + Overwrite, + Rename + } + + public enum DuplicateEntryHandling + { + ThrowError, + Overwrite, + // Folder: Overwrite, File: Rename + Rename, + Skip + } + + public enum MimeValidationType + { + NoValidation, + MimeTypeMustMatch, + MediaTypeMustMatch + } + + #endregion + + #region Result & Cargo objects + + public class DuplicateFileInfo + { + [JsonProperty("source")] + public MediaFileInfo SourceFile { get; set; } + + [JsonProperty("dest")] + public MediaFileInfo DestinationFile { get; set; } + + [JsonProperty("uniquePath")] + public string UniquePath { get; set; } + } + + public class FolderOperationResult + { + public string Operation { get; set; } + public MediaFolderInfo Folder { get; set; } + public DuplicateEntryHandling DuplicateEntryHandling { get; set; } + public IList DuplicateFiles { get; set; } + } + + public class FolderDeleteResult + { + public HashSet DeletedFolderIds { get; set; } = new HashSet(); + public IList DeletedFileNames { get; set; } = new List(); + public IList TrackedFileNames { get; set; } = new List(); + public IList LockedFileNames { get; set; } = new List(); + } + + public class FileOperationResult + { + public string Operation { get; set; } + public MediaFileInfo SourceFile { get; set; } + public MediaFileInfo DestinationFile { get; set; } + public DuplicateFileHandling DuplicateFileHandling { get; set; } + public bool IsDuplicate { get; set; } + public string UniquePath { get; set; } + } + + #endregion + + /// + /// Media service interface. + /// + public partial interface IMediaService + { + /// + /// Gets the underlying storage provider + /// + IMediaStorageProvider StorageProvider { get; } + + /// + /// Gets or sets a value indicating whether image post-processing is enabled. + /// It is recommended to turn this off during long-running processes - like product imports - + /// as post-processing can heavily decrease processing time. + /// + public bool ImagePostProcessingEnabled { get; set; } + + /// + /// Determines the number of files that match the filter criteria in . + /// + /// The query that defines the criteria. + /// The number of matching files. + int CountFiles(MediaSearchQuery query); + + /// + /// Determines the number of files that match the filter criteria in asynchronously. + /// + /// The query that defines the criteria. + /// The number of matching files. + Task CountFilesAsync(MediaSearchQuery query); + + /// + /// Determines the number of files that match the filter criteria in and groups them by folders. + /// + /// The filter that defines the criteria. + /// The grouped file counts (all, trash, unassigned, transient, all folders as dictionary) + FileCountResult CountFilesGrouped(MediaFilesFilter filter); + + /// + /// Searches files that match the filter criteria in . + /// + /// The query that defines the criteria. + /// An optional modifier function for the LINQ query that was internally derived from . Can be null. + /// Flags that affect the loading behaviour (eager-loading, tracking etc.) + /// The search result. + MediaSearchResult SearchFiles(MediaSearchQuery query, Func, IQueryable> queryModifier, MediaLoadFlags flags = MediaLoadFlags.AsNoTracking); + + /// + /// Searches files that match the filter criteria in . + /// + /// The query that defines the criteria. + /// An optional modifier function for the LINQ query that was internally derived from . Can be null. + /// Flags that affect the loading behaviour (eager-loading, tracking etc.) + /// The search result. + Task SearchFilesAsync(MediaSearchQuery query, Func, IQueryable> queryModifier, MediaLoadFlags flags = MediaLoadFlags.AsNoTracking); + + bool FileExists(string path); + MediaFileInfo GetFileByPath(string path, MediaLoadFlags flags = MediaLoadFlags.None); + MediaFileInfo GetFileById(int id, MediaLoadFlags flags = MediaLoadFlags.None); + MediaFileInfo GetFileByName(int folderId, string fileName, MediaLoadFlags flags = MediaLoadFlags.None); + IList GetFilesByIds(int[] ids, MediaLoadFlags flags = MediaLoadFlags.AsNoTracking); + bool CheckUniqueFileName(string path, out string newPath); + string CombinePaths(params string[] paths); + + /// + /// Tries to find an equal file by comparing the source stream to a list of files. + /// + /// The source stream to find a match for. + /// The sequence of files to seek within for duplicates. + /// Whether to leave the source stream open. + /// A file from the collection whose content is equal to . + /// true when a duplicate file was found, false otherwise. + bool FindEqualFile(Stream source, IEnumerable files, bool leaveOpen, out MediaFile equalFile); + + MediaFileInfo SaveFile(string path, Stream stream, bool isTransient = true, DuplicateFileHandling dupeFileHandling = DuplicateFileHandling.ThrowError); + Task SaveFileAsync(string path, Stream stream, bool isTransient = true, DuplicateFileHandling dupeFileHandling = DuplicateFileHandling.ThrowError); + void DeleteFile(MediaFile file, bool permanent, bool force = false); + FileOperationResult CopyFile(MediaFileInfo mediaFile, string destinationFileName, DuplicateFileHandling dupeFileHandling = DuplicateFileHandling.ThrowError); + MediaFileInfo MoveFile(MediaFile file, string destinationFileName, DuplicateFileHandling dupeFileHandling = DuplicateFileHandling.ThrowError); + MediaFileInfo ReplaceFile(MediaFile file, Stream inStream, string newFileName); + Task ReplaceFileAsync(MediaFile file, Stream inStream, string newFileName); + + bool FolderExists(string path); + MediaFolderInfo CreateFolder(string path); + MediaFolderInfo MoveFolder(string path, string destinationPath); + FolderOperationResult CopyFolder(string path, string destinationPath, DuplicateEntryHandling dupeEntryHandling = DuplicateEntryHandling.Skip); + FolderDeleteResult DeleteFolder(string path, FileHandling fileHandling = FileHandling.SoftDelete); + + MediaFileInfo ConvertMediaFile(MediaFile file); + string GetUrl(MediaFileInfo file, ProcessImageQuery query, string host = null, bool doFallback = true); + } +} diff --git a/src/Libraries/SmartStore.Services/Media/IMediaUrlGenerator.cs b/src/Libraries/SmartStore.Services/Media/IMediaUrlGenerator.cs new file mode 100644 index 0000000000..d90b168755 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/IMediaUrlGenerator.cs @@ -0,0 +1,35 @@ +using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.Stores; +using SmartStore.Services.Media.Imaging; + +namespace SmartStore.Services.Media +{ + /// + /// Generates URLs for media files + /// + public interface IMediaUrlGenerator + { + /// + /// Generates a public URL for a media file + /// + /// The file to create a URL for + /// Query for image processing / thumbnails. + /// + /// Store host for an absolute URL that also contains scheme and host parts. + /// null: tries to resolve host automatically based on or . + /// String.Empty: bypasses automatic host resolution and does NOT prepend host to path. + /// Any string: host name to use explicitly. + /// + /// + /// Specifies behaviour in case URL generation fails. + /// false: return null. + /// true: return URL to a fallback image which is ~/Content/images/default-image.png by default but can be modified with hidden setting Media.DefaultImageName + /// + /// The passed file's public URL. + string GenerateUrl( + MediaFileInfo file, + ProcessImageQuery imageQuery, + string host = null, + bool doFallback = true); + } +} diff --git a/src/Libraries/SmartStore.Services/Media/ImageCache.cs b/src/Libraries/SmartStore.Services/Media/ImageCache.cs deleted file mode 100644 index 68c98f8926..0000000000 --- a/src/Libraries/SmartStore.Services/Media/ImageCache.cs +++ /dev/null @@ -1,310 +0,0 @@ -using System; -using System.Collections.Specialized; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using System.Web; -using System.Web.Hosting; -using SmartStore.Core; -using SmartStore.Core.Domain.Media; -using SmartStore.Core.IO; -using SmartStore.Core.Logging; - -namespace SmartStore.Services.Media -{ - public class ImageCache : IImageCache - { - public const string IdFormatString = "0000000"; - internal const int MaxDirLength = 4; - - private readonly MediaSettings _mediaSettings; - private readonly string _thumbsRootDir; - private readonly IMediaFileSystem _fileSystem; - private readonly IImageProcessor _imageProcessor; - - public ImageCache(MediaSettings mediaSettings, IMediaFileSystem fileSystem, IImageProcessor imageProcessor) - { - _mediaSettings = mediaSettings; - _fileSystem = fileSystem; - _imageProcessor = imageProcessor; - - _thumbsRootDir = "Thumbs/"; - - Logger = NullLogger.Instance; - } - - public ILogger Logger - { - get; - set; - } - - public void Put(CachedImageResult cachedImage, byte[] buffer) - { - if (PreparePut(cachedImage, buffer)) - { - var path = BuildPath(cachedImage.Path); - - _fileSystem.WriteAllBytes(path, buffer); - - cachedImage.Exists = true; - cachedImage.File = _fileSystem.GetFile(path); - } - } - - public async Task PutAsync(CachedImageResult cachedImage, byte[] buffer) - { - if (PreparePut(cachedImage, buffer)) - { - var path = BuildPath(cachedImage.Path); - - // save file - await _fileSystem.WriteAllBytesAsync(path, buffer); - - // Refresh info - cachedImage.Exists = true; - cachedImage.File = _fileSystem.GetFile(path); - } - } - - private bool PreparePut(CachedImageResult cachedImage, byte[] buffer) - { - Guard.NotNull(cachedImage, nameof(cachedImage)); - - if (buffer == null || buffer.Length == 0) - { - return false; - } - - if (cachedImage.Exists) - { - _fileSystem.DeleteFile(BuildPath(cachedImage.Path)); - } - - // create folder if needed - string imageDir = System.IO.Path.GetDirectoryName(cachedImage.Path); - if (imageDir.HasValue()) - { - _fileSystem.TryCreateFolder(BuildPath(imageDir)); - } - - return true; - } - - public virtual CachedImageResult Get(int? pictureId, string seoFileName, string extension, ProcessImageQuery query = null) - { - Guard.NotEmpty(extension, nameof(extension)); - - extension = query?.GetResultExtension() ?? extension.TrimStart('.').ToLower(); - var imagePath = GetCachedImagePath(pictureId, seoFileName, extension, query); - - var file = _fileSystem.GetFile(BuildPath(imagePath)); - - var result = new CachedImageResult(file) - { - Path = imagePath, - Extension = extension, - IsRemote = _fileSystem.IsCloudStorage - }; - - if (file.Exists && file.Size <= 0) - { - result.Exists = false; - } - - return result; - } - - public virtual CachedImageResult Get(IFile file, ProcessImageQuery query) - { - Guard.NotNull(file, nameof(file)); - Guard.NotNull(query, nameof(query)); - - var imagePath = GetCachedImagePath(file, query); - var thumbFile = _fileSystem.GetFile(BuildPath(imagePath)); - - var result = new CachedImageResult(thumbFile) - { - Path = imagePath, - Extension = file.Extension.TrimStart('.'), - IsRemote = _fileSystem.IsCloudStorage - }; - - if (file.Exists && file.Size <= 0) - { - result.Exists = false; - } - - return result; - } - - public virtual Stream Open(CachedImageResult cachedImage) - { - Guard.NotNull(cachedImage, nameof(cachedImage)); - - return _fileSystem.GetFile(BuildPath(cachedImage.Path)).OpenRead(); - } - - public virtual string GetPublicUrl(string imagePath) - { - if (imagePath.IsEmpty()) - return null; - - return _fileSystem.GetPublicUrl(BuildPath(imagePath), true).EmptyNull(); - } - - public virtual void RefreshInfo(CachedImageResult cachedImage) - { - Guard.NotNull(cachedImage, nameof(cachedImage)); - - var file = _fileSystem.GetFile(cachedImage.File.Path); - cachedImage.File = file; - cachedImage.Exists = file.Exists && file.Size > 0; - } - - public virtual void Delete(Picture picture) - { - var filter = string.Format("{0}*.*", picture.Id.ToString(IdFormatString)); - - var files = _fileSystem.SearchFiles(_thumbsRootDir, filter); - foreach (var file in files) - { - _fileSystem.DeleteFile(file); - } - } - - public virtual void Delete(IFile file) - { - // TODO: (mc) this could lead to more thumbs getting deleted as desired. But who cares? :-) - var filter = string.Format("{0}*.*", file.Title); - - var files = _fileSystem.SearchFiles(BuildPath(file.Directory), filter); - foreach (var f in files) - { - _fileSystem.DeleteFile(f); - } - } - - public virtual void Clear() - { - for (int i = 0; i < 10; i++) - { - try - { - foreach (var file in _fileSystem.ListFiles(_thumbsRootDir)) - { - if (!file.Name.IsCaseInsensitiveEqual("placeholder") && !file.Name.IsCaseInsensitiveEqual("placeholder.txt")) - { - _fileSystem.DeleteFile(file.Path); - } - } - foreach (var dir in _fileSystem.ListFolders(_thumbsRootDir)) - { - _fileSystem.DeleteFolder(dir.Path); - } - - return; - } - catch (Exception ex) - { - Logger.Error(ex); - } - } - } - - public void CacheStatistics(out long fileCount, out long totalSize) - { - fileCount = 0; - totalSize = 0; - - if (!_fileSystem.FolderExists(_thumbsRootDir)) - { - return; - } - - fileCount = _fileSystem.SearchFiles(_thumbsRootDir, "*.*").Count(); - totalSize = _fileSystem.ListFolders(_thumbsRootDir).Sum(x => x.Size); - } - - #region Utils - - /// - /// Returns the file name with the subfolder (when multidirs are enabled) - /// - /// - /// File name without extension - /// Dot-less file extension - /// - /// - private string GetCachedImagePath(int? pictureId, string seoFileName, string extension, ProcessImageQuery query = null) - { - string imageFileName = null; - - string firstPart = ""; - if (pictureId.GetValueOrDefault() > 0) - { - firstPart = pictureId.Value.ToString(IdFormatString) + (seoFileName.IsEmpty() ? "" : "-"); - } - - if (firstPart.IsEmpty() && seoFileName.IsEmpty()) - { - // files without name? No way! - return null; - } - - seoFileName = seoFileName.EmptyNull(); - - if (query == null || !query.NeedsProcessing()) - { - imageFileName = String.Concat(firstPart, seoFileName); - } - else - { - imageFileName = String.Concat(firstPart, seoFileName, query.CreateHash()); - } - - if (_mediaSettings.MultipleThumbDirectories && imageFileName != null && imageFileName.Length > MaxDirLength) - { - // Get the first four letters of the file name - var subDirectoryName = imageFileName.Substring(0, MaxDirLength); - imageFileName = String.Concat(subDirectoryName, "/", imageFileName); - } - - return String.Concat(imageFileName, ".", extension); - } - - /// - /// Returns the images thumb path as is plus query (required for uploaded images) - /// - /// Image file to get thumbnail for - /// - /// - private string GetCachedImagePath(IFile file, ProcessImageQuery query) - { - if (!_imageProcessor.IsSupportedImage(file.Name)) - { - throw new InvalidOperationException("Thumbnails for '{0}' files are not supported".FormatInvariant(file.Extension)); - } - - // TODO: (mc) prevent creating thumbs for thumbs AND check equality of source and target - - var imageFileName = String.Concat(file.Title, query.CreateHash()); - var extension = (query.GetResultExtension() ?? file.Extension).EnsureStartsWith(".").ToLower(); - var path = _fileSystem.Combine(file.Directory, imageFileName + extension); - - return path.TrimStart('/', '\\'); - } - - private string BuildPath(string imagePath) - { - if (imagePath.IsEmpty()) - return null; - - return String.Concat(_thumbsRootDir, imagePath); - } - - #endregion - - } - -} diff --git a/src/Libraries/SmartStore.Services/Media/ImageCacheExtensions.cs b/src/Libraries/SmartStore.Services/Media/ImageCacheExtensions.cs deleted file mode 100644 index 98934a6561..0000000000 --- a/src/Libraries/SmartStore.Services/Media/ImageCacheExtensions.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using SmartStore.Core.Domain.Media; -using SmartStore.Core.IO; - -namespace SmartStore.Services.Media -{ - public static class ImageCacheExtensions - { - /// - /// Gets an instance of the object, which contains information about a cached image. - /// - /// The picture object for which to resolve a cached image. - /// The image processing query. - /// An instance of the object - /// If the requested image does not exist in the cache, the value of the Exists property will be false. - public static CachedImageResult Get(this IImageCache imageCache, Picture picture, ProcessImageQuery query = null) - { - Guard.NotNull(picture, nameof(picture)); - - return imageCache.Get(picture.Id, picture.SeoFilename, MimeTypes.MapMimeTypeToExtension(picture.MimeType), query); - } - - /// - /// Adds an image to the cache. - /// - /// The picture id, which will be part of the resulting file name. - /// The seo friendly picture name, which will be part of the resulting file name. - /// The extension of the resulting file - /// The image binary data. - /// The image processing query. This object, if not null, is hashed and appended to the resulting file name. - public static void Put(this IImageCache imageCache, int? pictureId, string seoFileName, string extension, byte[] buffer, ProcessImageQuery query = null) - { - var cachedImage = imageCache.Get(pictureId, seoFileName, extension, query); - imageCache.Put(cachedImage, buffer); - } - - /// - /// Adds an image to the cache. - /// - /// The picture object needed for building the resulting file name. - /// The image binary data. - /// The image processing query. This object, if not null, is hashed and appended to the resulting file name. - public static void Put(this IImageCache imageCache, Picture picture, byte[] buffer, ProcessImageQuery query = null) - { - Guard.NotNull(picture, nameof(picture)); - imageCache.Put(picture.Id, picture.SeoFilename, MimeTypes.MapMimeTypeToExtension(picture.MimeType), buffer, query); - } - } -} diff --git a/src/Libraries/SmartStore.Services/Media/ImageTypes.cs b/src/Libraries/SmartStore.Services/Media/ImageTypes.cs deleted file mode 100644 index 953922bb98..0000000000 --- a/src/Libraries/SmartStore.Services/Media/ImageTypes.cs +++ /dev/null @@ -1,19 +0,0 @@ - -namespace SmartStore.Services.Media -{ - public static class ImageTypes - { - public static readonly string Jpg = "image/jpeg"; - public static readonly string Bmp = "image/bmp"; - public static readonly string Png = "image/png"; - public static readonly string Exif = "image/exif"; //todo not sure - public static readonly string Tif = "image/tiff"; - public static readonly string Tiff = "image/tiff"; - public static readonly string Tff = "image/tff"; - public static readonly string Jpe = "image/jpeg"; - public static readonly string Jif = "image/jif"; - public static readonly string Jfif = "image/jpeg"; - public static readonly string Jfi = "image/jpeg"; //todo notsure - public static readonly string Gif = "image/gif"; - } -} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/BitDepth.cs b/src/Libraries/SmartStore.Services/Media/Imaging/BitDepth.cs new file mode 100644 index 0000000000..8ee167be7b --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/BitDepth.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SmartStore.Services.Media.Imaging +{ + /// + /// Provides enumeration for the available bit depths. + /// + public enum BitDepth + { + /// + /// 1 bit per pixel + /// + Bit1 = 1, + + /// + /// 4 bits per pixel + /// + Bit4 = 4, + + /// + /// 8 bits per pixel + /// + Bit8 = 8, + + /// + /// 16 bits per pixel + /// + Bit16 = 16, + + /// + /// 24 bits per pixel + /// + Bit24 = 24, + + /// + /// 32 bits per pixel + /// + Bit32 = 32 + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/CachedImage.cs b/src/Libraries/SmartStore.Services/Media/Imaging/CachedImage.cs new file mode 100644 index 0000000000..421c730b4c --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/CachedImage.cs @@ -0,0 +1,83 @@ +using System; +using System.Drawing; +using SmartStore.Core.IO; + +namespace SmartStore.Services.Media.Imaging +{ + /// + /// Contains information about a cached image + /// + /// + /// An instance of this object is always returned, even when + /// the requested image does not physically exists in the storage. + /// + public class CachedImage + { + private bool? _exists; + private string _mimeType; + private Size? _size; + + public CachedImage(IFile file) + { + Guard.NotNull(file, nameof(file)); + + File = file; + } + + /// + /// The abstracted file object + /// + public IFile File { get; internal set; } + + /// + /// true when the image exists in the cache, false otherwise. + /// + public bool Exists + { + get => _exists ?? (_exists = File.Exists).Value; + // For internal use + set => _exists = value; + } + + /// + /// The name of the file (without path) + /// + public string FileName => System.IO.Path.GetFileName(this.Path); + + public long FileSize => !Exists ? 0 : File.Size; + + /// + /// The file extension (without 'dot') + /// + public string Extension { get; set; } + + /// + /// The filemime type + /// + public string MimeType => _mimeType ?? (_mimeType = MimeTypes.MapNameToMimeType(FileName)); + + /// + /// The image width and height. + /// + public Size Size + { + get => _size ?? (_size = File.Dimensions).Value; + internal set => _size = value; + } + + /// + /// The path relative to the cache root folder + /// + public string Path { get; set; } + + /// + /// The last modified date or null if the file does not exist + /// + public DateTime? LastModifiedUtc => Exists ? File.LastUpdated : (DateTime?)null; + + /// + /// Checks whether the file is remote (outside the application's physical root) + /// + public bool IsRemote { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/DefaultImageProcessor.cs b/src/Libraries/SmartStore.Services/Media/Imaging/DefaultImageProcessor.cs new file mode 100644 index 0000000000..503fccb3bf --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/DefaultImageProcessor.cs @@ -0,0 +1,203 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Drawing; +using SmartStore.Core.Logging; +using SmartStore.Core.Events; +using SmartStore.Utilities; +using SmartStore.Core.IO; + +namespace SmartStore.Services.Media.Imaging +{ + public class DefaultImageProcessor : IImageProcessor + { + private static long _totalProcessingTime; + + private readonly IEventPublisher _eventPublisher; + + public DefaultImageProcessor(IImageFactory imageFactory, IEventPublisher eventPublisher) + { + Factory = imageFactory; + _eventPublisher = eventPublisher; + } + + public ILogger Logger { get; set; } = NullLogger.Instance; + + public IImageFactory Factory { get; } + + public ProcessImageResult ProcessImage(ProcessImageQuery query, bool disposeOutput = true) + { + Guard.NotNull(query, nameof(query)); + + ValidateQuery(query); + + var watch = new Stopwatch(); + long len; + IProcessableImage image = null; + + try + { + watch.Start(); + + var source = query.Source; + + // Load source + if (source is byte[] b) + { + using var memStream = new MemoryStream(b); + image = Factory.LoadImage(memStream); + len = b.LongLength; + } + else if (source is Stream s) + { + image = Factory.LoadImage(s); + len = s.Length; + } + else if (source is string str) + { + str = NormalizePath(str); + image = Factory.LoadImage(str); + len = (new FileInfo(str)).Length; + } + else if (source is IFile file) + { + using (var fs = file.OpenRead()) + { + image = Factory.LoadImage(fs); + len = file.Size; + } + } + else + { + throw new ProcessImageException("Invalid source type '{0}' in query.".FormatInvariant(query.Source.GetType().FullName), query); + } + + var sourceFormat = image.Format; + + // Pre-process event + _eventPublisher.Publish(new ImageProcessingEvent(query, image)); + + var result = new ProcessImageResult + { + Query = query, + SourceFormat = image.Format, + Image = image, + DisposeImage = disposeOutput + }; + + // Core processing + ProcessImageCore(query, image, out var fxApplied); + + result.HasAppliedVisualEffects = fxApplied; + + // Post-process event + _eventPublisher.Publish(new ImageProcessedEvent(query, result)); + + result.ProcessTimeMs = watch.ElapsedMilliseconds; + + return result; + } + catch (Exception ex) + { + throw new ProcessImageException(query, ex); + } + finally + { + if (query.DisposeSource && query.Source is IDisposable source) + { + source.Dispose(); + } + + watch.Stop(); + _totalProcessingTime += watch.ElapsedMilliseconds; + } + } + + /// + /// Processes the loaded image. Inheritors should NOT save the image, this is done by the caller. + /// + /// Query + /// Image instance + /// + /// Should be true if any effect has been applied that potentially changes the image visually (like background color, contrast, sharpness etc.). + /// Resize and compression quality does NOT count as FX. + /// + protected virtual void ProcessImageCore(ProcessImageQuery query, IProcessableImage image, out bool fxApplied) + { + bool fxAppliedInternal = false; + + // Resize + var size = query.MaxWidth != null || query.MaxHeight != null + ? new Size(query.MaxWidth ?? 0, query.MaxHeight ?? 0) + : Size.Empty; + + image.Transform(transformer => + { + if (!size.IsEmpty) + { + transformer.Resize(new ResizeOptions + { + Size = size, + ResizeMode = ProcessImageQuery.ConvertScaleMode(query.ScaleMode), + AnchorPosition = ProcessImageQuery.ConvertAnchorPosition(query.AnchorPosition) + }); ; + } + + if (query.BackgroundColor.HasValue()) + { + transformer.BackgroundColor(ColorTranslator.FromHtml(query.BackgroundColor)); + fxAppliedInternal = true; + } + + // Format + if (query.Format != null) + { + var requestedFormat = query.Format as IImageFormat; + + if (requestedFormat == null && query.Format is string) + { + requestedFormat = Factory.GetImageFormat(((string)query.Format).ToLowerInvariant()); + } + + if (requestedFormat != null && requestedFormat.DefaultMimeType != image.Format.DefaultMimeType) + { + transformer.Format(requestedFormat); + } + } + + // Quality + if (query.Quality.HasValue) + { + transformer.Quality(query.Quality.Value); + } + }); + + fxApplied = fxAppliedInternal; + } + + protected virtual string NormalizePath(string path) + { + if (path.IsWebUrl()) + { + throw new NotSupportedException($"Remote images cannot be processed: Path: {path}"); + } + + if (!PathHelper.IsAbsolutePhysicalPath(path)) + { + path = CommonHelper.MapPath(path); + } + + return path; + } + + private void ValidateQuery(ProcessImageQuery query) + { + if (query.Source == null) + { + throw new ArgumentException("During image processing 'ProcessImageQuery.Source' must not be null.", nameof(query)); + } + } + + public long TotalProcessingTimeMs => _totalProcessingTime; + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/IImage.cs b/src/Libraries/SmartStore.Services/Media/Imaging/IImage.cs new file mode 100644 index 0000000000..f5d573a31c --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/IImage.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SmartStore.Services.Media.Imaging +{ + /// + /// Represents a processable image. + /// + public interface IProcessableImage : IImage + { + /// + /// Transforms the image. + /// + /// + IImage Transform(Action transformer); + } + + /// + /// Represents an image. + /// + public interface IImage : IImageInfo, IDisposable + { + /// + /// Gets the original width and height of the source image before any transform has been applied. + /// + Size SourceSize { get; } + + /// + /// Saves the current image to the specified output stream. + /// + /// The stream to save the image to. + /// The current . + IImage Save(Stream stream); + + /// + /// Saves the current image to the specified file path. + /// + /// The path to save the image to. + /// The current . + IImage Save(string path); + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/IImageCache.cs b/src/Libraries/SmartStore.Services/Media/Imaging/IImageCache.cs new file mode 100644 index 0000000000..0e37de0865 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/IImageCache.cs @@ -0,0 +1,67 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Services.Media.Imaging +{ + /// + /// Image cache interface + /// + public interface IImageCache + { + /// + /// Adds an image to the cache. + /// + /// An instance of the object which is returned by the Get() method. + /// The image object. + void Put(CachedImage cachedImage, IImage image); + + /// + /// Adds an image to the cache. + /// + /// An instance of the object which is returned by the Get() method. + /// The file input stream. + void Put(CachedImage cachedImage, Stream stream); + + /// + /// Asynchronously adds an image to the cache. + /// + /// An instance of the object which is returned by the Get() method. + /// The file input stream. + Task PutAsync(CachedImage cachedImage, Stream stream); + + /// + /// Gets an instance of the object which contains information about a cached image. + /// + /// The id of the file to resolve a thumbnail for. + /// The path data. + /// The image processing query. + /// An instance of the object + /// If the requested thumbnail does not exist in the cache, the value of the Exists property will be false. + CachedImage Get(int? mediaFileId, MediaPathData pathData, ProcessImageQuery query = null); + + /// + /// Deletes all cached images for the given + /// + /// The for which to delete cached images + void Delete(MediaFile picture); + + /// + /// Deletes all cached images (nukes all files in the cache folder) + /// + void Clear(); + + /// + /// Refreshes the file info. + /// + void RefreshInfo(CachedImage cachedImage); + + /// + /// Calculates statistics about the image cache data. + /// + /// The total count of files in the cache. + /// The total size of files in the cache (in bytes) + void CacheStatistics(out long fileCount, out long totalSize); + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/IImageFactory.cs b/src/Libraries/SmartStore.Services/Media/Imaging/IImageFactory.cs new file mode 100644 index 0000000000..7ebc1f1df0 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/IImageFactory.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SmartStore.Services.Media.Imaging +{ + /// + /// A provider interface for imaging libraries. + /// + public interface IImageFactory + { + /// + /// Determines whether the given file name is processable by the image provider + /// + /// The file extension to check. + /// A value indicating whether processing is possible + bool IsSupportedImage(string extension); + + /// + /// Resolves the image format instance for a given file extension. + /// + /// The (dot-less) file extension. + /// An object that adapts the library specific format implementation. + IImageFormat GetImageFormat(string extension); + + /// + /// Loads an image by path. + /// + /// The full physical path to the image file. + /// Whether to preserve exif metadata. Defaults to false. + /// An object that adapts the library specific imaging implementation. + IProcessableImage LoadImage(string path, bool preserveExif = false); + + /// + /// Loads an image by stream. + /// + /// The stream that contains image data. + /// Whether to preserve exif metadata. Defaults to false. + /// An object that adapts the library specific imaging implementation. + IProcessableImage LoadImage(Stream stream, bool preserveExif = false); + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/IImageFormat.cs b/src/Libraries/SmartStore.Services/Media/Imaging/IImageFormat.cs new file mode 100644 index 0000000000..25e8516973 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/IImageFormat.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace SmartStore.Services.Media.Imaging +{ + /// + /// Defines the contract for an image format. + /// + public interface IImageFormat // TODO: >> IEquatable & Equals & GetHashCode() + { + /// + /// Gets the name that describes this image format. + /// + string Name { get; } + + /// + /// Gets the default file extension. + /// + string DefaultExtension { get; } + + /// + /// Gets the default mimetype that the image format uses + /// + string DefaultMimeType { get; } + + /// + /// Gets the file extensions this image format commonly uses. + /// + IEnumerable FileExtensions { get; } + + /// + /// Gets all the mimetypes that have been used by this image format. + /// + IEnumerable MimeTypes { get; } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/IImageInfo.cs b/src/Libraries/SmartStore.Services/Media/Imaging/IImageInfo.cs new file mode 100644 index 0000000000..9ab9cedad4 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/IImageInfo.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SmartStore.Services.Media.Imaging +{ + /// + /// Encapsulates properties that describe basic image information including dimensions, pixel type information + /// and additional metadata. + /// + public interface IImageInfo + { + /// + /// Gets width and height. + /// + Size Size { get; } + + /// + /// Gets the color depth in number of bits per pixel (1, 4, 8, 16, 24, 32) + /// + BitDepth BitDepth { get; } + + /// + /// Gets the format of the image. + /// + IImageFormat Format { get; } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/IImageProcessor.cs b/src/Libraries/SmartStore.Services/Media/Imaging/IImageProcessor.cs new file mode 100644 index 0000000000..91f0e94c6e --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/IImageProcessor.cs @@ -0,0 +1,29 @@ +using System; +using System.IO; + +namespace SmartStore.Services.Media.Imaging +{ + /// + /// A service interface responsible for processing images. + /// + public interface IImageProcessor + { + /// + /// The image factory adapter used to load and process images. + /// + IImageFactory Factory { get; } + + /// + /// Processes an image + /// + /// Resize request + /// Whether to dispose the output stream when instance gets disposed. + /// The resizing result encapsulated in type + ProcessImageResult ProcessImage(ProcessImageQuery query, bool disposeOutput = true); + + /// + /// Gets the cumulative total processing time since app start in miliseconds + /// + long TotalProcessingTimeMs { get; } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/IImageTransformer.cs b/src/Libraries/SmartStore.Services/Media/Imaging/IImageTransformer.cs new file mode 100644 index 0000000000..7fc73cc6d5 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/IImageTransformer.cs @@ -0,0 +1,185 @@ +using System; +using System.Drawing; + +namespace SmartStore.Services.Media.Imaging +{ + public interface IImageTransformer + { + /// + /// Changes the opacity of the current image. + /// + /// The percentage by which to alter the images opacity. Any integer between 0 and 100. + IImageTransformer Alpha(int percentage); + + /// + /// Changes the background color of the current image. + /// + /// + /// The to paint the image background with. + /// + IImageTransformer BackgroundColor(Color color); + + /// + /// Alters the bit depth of the current image. + /// + /// The new bit depth. + IImageTransformer BitDepth(BitDepth bitDepth); + + /// + /// Changes the brightness of the current image. + /// + /// The percentage by which to alter the images brightness. Any integer between -100 and 100. + IImageTransformer Brightness(int percentage); + + /// + /// Changes the contrast of the current image. + /// + /// The percentage by which to alter the images contrast. Any integer between -100 and 100. + IImageTransformer Contrast(int percentage); + + /// + /// Crops the current image to the given location and size. + /// + /// The rectangle containing the coordinates to crop the image to. + IImageTransformer Crop(Rectangle rect); + + /// + /// Crops an image to the area of greatest entropy. + /// + /// The threshold in bytes to control the entropy. + IImageTransformer EntropyCrop(byte threshold = 128); + + /// + /// Flips the current image either horizontally or vertically. + /// + /// Whether to flip the image vertically. + /// Whether to flip the image both vertically and horizontally. + IImageTransformer Flip(bool flipVertically = false, bool flipBoth = false); + + /// + /// Sets the output format of the current image to the matching ImageFormat. + /// + /// The new format to set the image to. + IImageTransformer Format(IImageFormat format); + + /// + /// Adjust the gamma (intensity of the light) component of the given image. + /// + /// The value to adjust the gamma by (typically between .2 and 5). + IImageTransformer Gamma(float value); + + /// + /// Uses a Gaussian kernel to blur the current image. + /// + /// The size to set the Gaussian kernel to. + /// The sigma and threshold values applied to the kernel are 1.4 and 0 respectively. + IImageTransformer GaussianBlur(int size); + + /// + /// Uses a Gaussian kernel to sharpen the current image. + /// + /// The size to set the Gaussian kernel to. + /// The sigma and threshold values applied to the kernel are 1.4 and 0 respectively. + IImageTransformer GaussianSharpen(int size); + + /// + /// Alters the hue of the current image changing the overall color. + /// + /// The angle by which to alter the images hue. Any integer between 0 and 360. + /// Whether to rotate the hue of the current image altering each color. + IImageTransformer Hue(int degrees, bool rotate = false); + + /// + /// Applies an image mask to the current image. + /// + /// The mask image. + /// Size of the mask. + /// Opacity of the mask. + /// + /// Position of the mask. + /// Point is used to place the image mask if it has not the same dimensions as the original image. + /// Pass null to center the mask. + /// + IImageTransformer Mask(IImage image, Size size, int opacity, Point? position); + + /// + /// Adds an image overlay to the current image. + /// + /// The overlay image. + /// Size of the layer. + /// Opacity of the layer. + /// + /// Position of the layer. + /// Point is used to place the image overlay if it has not the same dimensions as the original image. + /// Pass null to center the overlay. + /// + IImageTransformer Overlay(IImage image, Size size, int opacity, Point? position); + + /// + /// Alters the output quality of the current image. + /// + /// A value between 1 and 100 to set the quality to. + /// This method will only effect the output quality of jpeg and webp images. + IImageTransformer Quality(int percentage); + + /// + /// Rotates the current image by the given angle. + /// + /// The angle at which to rotate the image in degrees. + /// If true, rotates the image without expanding the canvas to fit the image. + /// + /// Whether to keep the original image dimensions. . + /// Only applicable if is true. + /// If set to true, the image is zoomed to fit the bounding area. + /// If set to false, the area is cropped to fit the rotated image. + /// + IImageTransformer Rotate(float degrees, bool bounded = false, bool keepSize = false); + + /// + /// Resizes the current image according to the given options. + /// + /// The options. + /// + IImageTransformer Resize(ResizeOptions options); + + /// + /// Adds rounded corners to the current image. + /// + /// The radius at which the corner will be rounded. + IImageTransformer RoundedCorners(int radius); + + /// + /// Changes the saturation of the current image. + /// + /// The percentage by which to alter the images saturation. Any integer between -100 and 100. + IImageTransformer Saturation(int percentage); + + /// + /// Tints the current image with the given color. + /// + /// The color to tint the image with. + IImageTransformer Tint(Color color); + + /// + /// Adds a vignette image effect to the current image. + /// + /// The color of the vignette. Defaults to black. + /// + IImageTransformer Vignette(Color? color = null); + } + + public static class IImageTransformerExtensions + { + /// + /// Resizes the current image to the given dimensions. + /// + /// + /// The containing the width and height to set the image to. + /// + /// + public static IImageTransformer Resize(this IImageTransformer transformer, Size size) + { + return transformer.Resize(new ResizeOptions { Size = size }); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/ImageCache.cs b/src/Libraries/SmartStore.Services/Media/Imaging/ImageCache.cs new file mode 100644 index 0000000000..a841a21bcb --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/ImageCache.cs @@ -0,0 +1,223 @@ +using System; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.Logging; + +namespace SmartStore.Services.Media.Imaging +{ + public class ImageCache : IImageCache + { + public const string IdFormatString = "0000000"; + internal const int MaxDirLength = 4; + + private readonly MediaSettings _mediaSettings; + private readonly IMediaFileSystem _fileSystem; + private readonly string _thumbsRootDir; + + public ImageCache( + MediaSettings mediaSettings, + IMediaFileSystem fileSystem, + MediaHelper mediaHelper) + { + _mediaSettings = mediaSettings; + _fileSystem = fileSystem; + + _thumbsRootDir = "Thumbs/"; + } + + public ILogger Logger { get; set; } = NullLogger.Instance; + + public void Put(CachedImage cachedImage, IImage image) + { + var path = BuildPath(cachedImage.Path); + using var stream = _fileSystem.GetFile(path).OpenWrite(); + + if (PreparePut(cachedImage, stream)) + { + image.Save(stream); + image.Dispose(); + PostPut(cachedImage, path); + } + } + + public void Put(CachedImage cachedImage, Stream stream) + { + if (PreparePut(cachedImage, stream)) + { + var path = BuildPath(cachedImage.Path); + _fileSystem.SaveStream(path, stream); + PostPut(cachedImage, path); + } + } + + public async Task PutAsync(CachedImage cachedImage, Stream stream) + { + if (PreparePut(cachedImage, stream)) + { + var path = BuildPath(cachedImage.Path); + await _fileSystem.SaveStreamAsync(path, stream); + PostPut(cachedImage, path); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void PostPut(CachedImage cachedImage, string path) + { + cachedImage.Exists = true; + cachedImage.File = _fileSystem.GetFile(path); + } + + private bool PreparePut(CachedImage cachedImage, Stream stream) + { + Guard.NotNull(cachedImage, nameof(cachedImage)); + + if (stream == null) + { + return false; + } + + // create folder if needed + string imageDir = System.IO.Path.GetDirectoryName(cachedImage.Path); + if (imageDir.HasValue()) + { + _fileSystem.CreateFolder(BuildPath(imageDir)); + } + + return true; + } + + public virtual CachedImage Get(int? mediaFileId, MediaPathData data, ProcessImageQuery query = null) + { + Guard.NotNull(data, nameof(data)); + + var resultExtension = query?.GetResultExtension(); + if (resultExtension != null) + { + data.Extension = resultExtension; + } + + var imagePath = GetCachedImagePath(mediaFileId, data, query); + var file = _fileSystem.GetFile(BuildPath(imagePath)); + + var result = new CachedImage(file) + { + Path = imagePath, + Extension = data.Extension, + IsRemote = _fileSystem.IsCloudStorage + }; + + return result; + } + + public virtual void RefreshInfo(CachedImage cachedImage) + { + Guard.NotNull(cachedImage, nameof(cachedImage)); + + var file = _fileSystem.GetFile(cachedImage.File.Path); + cachedImage.File = file; + cachedImage.Exists = file.Exists; + } + + public virtual void Delete(MediaFile picture) + { + var filter = string.Format("{0}*.*", picture.Id.ToString(IdFormatString)); + + var files = _fileSystem.SearchFiles(_thumbsRootDir, filter); + foreach (var file in files) + { + _fileSystem.DeleteFile(file); + } + } + + public virtual void Clear() + { + for (int i = 0; i < 10; i++) + { + + try + { + _fileSystem.DeleteFolder(_thumbsRootDir); + _fileSystem.CreateFolder(_thumbsRootDir); + } + catch (Exception ex) + { + Logger.Error(ex); + } + } + } + + public void CacheStatistics(out long fileCount, out long totalSize) + { + fileCount = 0; + totalSize = 0; + + if (!_fileSystem.FolderExists(_thumbsRootDir)) + { + return; + } + + fileCount = _fileSystem.SearchFiles(_thumbsRootDir, "*.*").Count(); + totalSize = _fileSystem.ListFolders(_thumbsRootDir).Sum(x => x.Size); + } + + #region Utils + + protected string GetCachedImagePath(int? mediaFileId, MediaPathData data, ProcessImageQuery query = null) + { + string result = ""; + + // xxxxxxx + if (mediaFileId.GetValueOrDefault() > 0) + { + result = mediaFileId.Value.ToString(IdFormatString); + } + + //// INFO: (mm) don't include folder id in pathes for now. It results in more complex image cache invalidation code. + //// xxxxxxx-f + //if (data.Folder != null) + //{ + // result = result.Grow(data.Folder.Id.ToString(CultureInfo.InvariantCulture), "-"); + //} + + // xxxxxxx-f-abc + result = result.Grow(data.FileTitle, "-"); + + if (result.IsEmpty()) + { + // files without name? No way! + return null; + } + + if (query != null && query.NeedsProcessing()) + { + // xxxxxxx-f-abc-w100-h100 + result += query.CreateHash(); + } + + if (_mediaSettings.MultipleThumbDirectories && result.Length > MaxDirLength) + { + // Get the first four letters of the file name + // 0001/xxxxxxx-f-abc-w100-h100 + var subDirectoryName = result.Substring(0, MaxDirLength); + result = subDirectoryName + "/" + result; + } + + // 0001/xxxxxxx-f-abc-w100-h100.png + return result.Grow(data.Extension, "."); + } + + private string BuildPath(string imagePath) + { + if (imagePath.IsEmpty()) + return null; + + return _thumbsRootDir + imagePath; + } + + #endregion + } + +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/ImageSharpImageProcessor.cs b/src/Libraries/SmartStore.Services/Media/Imaging/ImageSharpImageProcessor.cs new file mode 100644 index 0000000000..67363b4ea3 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/ImageSharpImageProcessor.cs @@ -0,0 +1,406 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +//using System.Drawing; +//using ImageProcessor; +//using ImageProcessor.Imaging; +//using ImageProcessor.Imaging.Formats; +using SmartStore.Core.Logging; +//using ImageProcessor.Configuration; +using SixLabors.ImageSharp; +using SmartStore.Core.Events; +using SmartStore.Utilities; +using SmartStore.Core.IO; +using SixLabors.ImageSharp.Formats; +using ImageSharpConfig = SixLabors.ImageSharp.Configuration; +using System.Collections.Concurrent; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Formats.Jpeg; + +namespace SmartStore.Services.Media +{ + public partial class ImageSharpImageProcessor : IImageProcessor + { + class SourceImage + { + public Image Image { get; set; } + public IImageFormat Format { get; set; } + public Stream Stream { get; set; } + public long Length { get; set; } + public int Width { get; set; } + public int Height { get; set; } + } + + private static readonly ConcurrentDictionary _supportedFormats = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private static long _totalProcessingTime; + + private readonly IEventPublisher _eventPublisher; + + static ImageSharpImageProcessor() + { + ImageSharpConfig.Default.MemoryAllocator = new SixLabors.ImageSharp.Memory.SimpleGcMemoryAllocator(); + + } + + public ImageSharpImageProcessor(IEventPublisher eventPublisher) + { + _eventPublisher = eventPublisher; + } + + public ILogger Logger { get; set; } = NullLogger.Instance; + + public bool IsSupportedImage(string extension) + { + if (extension.IsEmpty()) + { + return false; + } + + if (extension[0] == '.' && extension.Length > 1) + { + extension = extension.Substring(1); + } + + return _supportedFormats.GetOrAdd(extension, k => + { + return ImageSharpConfig.Default.ImageFormats + .SelectMany(x => x.FileExtensions) + .Any(x => x.Equals(extension, StringComparison.OrdinalIgnoreCase)); + }); + + //return ImageProcessorBootstrapper.Instance.SupportedImageFormats + // .SelectMany(x => x.FileExtensions) + // .Any(x => x.Equals(extension, StringComparison.OrdinalIgnoreCase)); + } + + public ProcessImageResult ProcessImage(ProcessImageQuery query, bool disposeOutput = true) + { + Guard.NotNull(query, nameof(query)); + + ValidateQuery(query); + + var watch = new Stopwatch(); + SourceImage source = null; + + try + { + watch.Start(); + + // Load source + source = LoadImage(query); + + // Pre-process event + _eventPublisher.Publish(new ImageProcessingEvent(query, source.Image)); + + var result = new ProcessImageResult + { + Query = query, + SourceWidth = source.Image.Width, + SourceHeight = source.Image.Height, + SourceMimeType = source.Format.DefaultMimeType, + DisposeOutputStream = disposeOutput + }; + + // Core processing + ProcessImageCore(query, source.Image, out var fxApplied); + + // Create & prepare result + //var outStream = new MemoryStream(); + //source.Image.Save(outStream, source.Format); // TODO: outFormat + var dest = SaveImage(source, query); + + var fmt = dest.Format; + result.FileExtension = fmt.FileExtensions.First(); + result.MimeType = fmt.DefaultMimeType; + + result.HasAppliedVisualEffects = fxApplied; + result.Width = source.Image.Width; + result.Height = source.Image.Height; + + if (source.Length > 0) + { + // Check whether it is more beneficial to return the source instead of the result. + // Prefer result only if its size is smaller than the source size. + // Result size may be larger if a high-compressed image has been uploaded. + // During image processing the source compression algorithm gets lost and the image may be larger in size + // after encoding with default encoders. + var compare = + // only when image was not altered visually... + !fxApplied + // ...size has not changed + && result.Width == result.SourceWidth + && result.Height == result.SourceHeight + // ...and format has not changed + && result.MimeType == result.SourceMimeType; + + if (compare && source.Length <= dest.OutStream.Length) + { + // Source is smaller. Throw away result and get back to source. + dest.OutStream.Dispose(); + dest.OutStream = null; + source.Stream.Position = 0; + result.OutputStream = source.Stream; + } + } + + // Set output stream + if (result.OutputStream == null) + { + result.OutputStream = dest.OutStream; + } + + // Post-process event + _eventPublisher.Publish(new ImageProcessedEvent(query, source.Image, result)); + + result.OutputStream.Position = 0; + + result.ProcessTimeMs = watch.ElapsedMilliseconds; + + return result; + } + catch (Exception ex) + { + throw new ProcessImageException(query, ex); + } + finally + { + if (source?.Image != null) + { + source?.Image.Dispose(); + } + + if (source?.Stream != null && query.DisposeSource) + { + source?.Stream.Dispose(); + } + + watch.Stop(); + _totalProcessingTime += watch.ElapsedMilliseconds; + } + } + + private SourceImage LoadImage(ProcessImageQuery query) + { + var source = query.Source; + + Image image; + IImageFormat format; + Stream stream; + long len; + + // Load source + if (source is byte[] b) + { + stream = new MemoryStream(b); + len = b.LongLength; + } + else if (source is Stream s) + { + stream = s; + len = s.Length; + } + else if (source is string str) + { + var fi = new FileInfo(NormalizePath(str)); + stream = fi.OpenRead(); + len = fi.Length; + } + else if (source is IFile file) + { + len = file.Size; + stream = file.OpenRead(); + } + else + { + throw new ProcessImageException("Invalid source type '{0}' in query.".FormatInvariant(query.Source.GetType().FullName), query); + } + + image = Image.Load(stream, out format); + + return new SourceImage + { + Image = image, + Format = format, + Stream = stream, + Length = len, + Width = image.Width, + Height = image.Height + }; + } + + private (MemoryStream OutStream, IImageFormat Format, IImageEncoder Encoder) SaveImage(SourceImage source, ProcessImageQuery query) + { + // Format + IImageFormat format = null; + if (query.Format != null) + { + format = query.Format as IImageFormat; + + if (format == null && query.Format is string) + { + var requestedFormat = ((string)query.Format).ToLowerInvariant(); + format = ImageSharpConfig.Default.ImageFormatsManager.FindFormatByFileExtension(requestedFormat); + } + } + + format ??= source.Format; + + // Encoder + var encoder = ImageSharpConfig.Default.ImageFormatsManager.FindEncoder(format); + + // Set Quality + if (query.Quality.HasValue && encoder is JpegEncoder) + { + // Create new encoder instance to avoid altering a shared static encoder instance. + encoder = new JpegEncoder { Quality = query.Quality }; + } + + var outStream = new MemoryStream(); + source.Image.Save(outStream, encoder); + + return (outStream, format, encoder); + } + + /// + /// Processes the loaded image. Inheritors should NOT save the image, this is done by the main method. + /// + /// Query + /// Processor instance + /// + /// Should be true if any effect has been applied that potentially changes the image visually (like background color, contrast, sharpness etc.). + /// Resize and compression quality does NOT count as FX. + /// + protected virtual void ProcessImageCore(ProcessImageQuery query, Image image, out bool fxApplied) + { + bool fxAppliedPrivate = false; + + // Resize + var size = query.MaxWidth != null || query.MaxHeight != null + ? new Size(query.MaxWidth ?? 0, query.MaxHeight ?? 0) + : Size.Empty; + + image.Mutate(x => + { + if (!size.IsEmpty && (image.Width > size.Width || image.Height > size.Height)) + { + //image.Resize(new ResizeLayer( + // size, + // resizeMode: ConvertScaleMode(query.ScaleMode), + // anchorPosition: ConvertAnchorPosition(query.AnchorPosition), + // upscale: false)); + + x.Resize(new ResizeOptions + { + Size = size, + Mode = ConvertScaleMode(query.ScaleMode), + Position = ConvertAnchorPosition(query.AnchorPosition) + }); + } + + if (query.BackgroundColor.HasValue()) + { + x.BackgroundColor(Color.ParseHex(query.BackgroundColor)); + fxAppliedPrivate = true; + } + }); + + //// Format + //if (query.Format != null) + //{ + // var format = query.Format as IImageFormat; + + // if (format == null && query.Format is string) + // { + // var requestedFormat = ((string)query.Format).ToLowerInvariant(); + // format = ImageSharpConfig.Default.ImageFormatsManager.FindFormatByFileExtension(requestedFormat); + // } + + // if (format != null) + // { + // image.Format(format); + // } + //} + + //// Set Quality + //if (query.Quality.HasValue) + //{ + // image.Quality(query.Quality.Value); + //} + + fxApplied = fxAppliedPrivate; + } + + private string NormalizePath(string path) + { + if (path.IsWebUrl()) + { + throw new NotSupportedException($"Remote images cannot be processed: Path: {path}"); + } + + if (!PathHelper.IsAbsolutePhysicalPath(path)) + { + path = CommonHelper.MapPath(path); + } + + return path; + } + + private void ValidateQuery(ProcessImageQuery query) + { + if (query.Source == null) + { + throw new ArgumentException("During image processing 'ProcessImageQuery.Source' must not be null.", nameof(query)); + } + } + + public long TotalProcessingTimeMs + { + get { return _totalProcessingTime; } + } + + private ResizeMode ConvertScaleMode(string mode) + { + switch (mode.EmptyNull().ToLower()) + { + case "boxpad": + return ResizeMode.BoxPad; + case "crop": + return ResizeMode.Crop; + case "min": + return ResizeMode.Min; + case "pad": + return ResizeMode.Pad; + case "stretch": + return ResizeMode.Stretch; + default: + return ResizeMode.Max; + } + } + + private AnchorPositionMode ConvertAnchorPosition(string anchor) + { + switch (anchor.EmptyNull().ToLower()) + { + case "top": + return AnchorPositionMode.Top; + case "bottom": + return AnchorPositionMode.Bottom; + case "left": + return AnchorPositionMode.Left; + case "right": + return AnchorPositionMode.Right; + case "top-left": + return AnchorPositionMode.TopLeft; + case "top-right": + return AnchorPositionMode.TopRight; + case "bottom-left": + return AnchorPositionMode.BottomLeft; + case "bottom-right": + return AnchorPositionMode.BottomRight; + default: + return AnchorPositionMode.Center; + } + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/ImageWrapper.cs b/src/Libraries/SmartStore.Services/Media/Imaging/ImageWrapper.cs new file mode 100644 index 0000000000..622b2db499 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/ImageWrapper.cs @@ -0,0 +1,88 @@ +using System; +using System.Drawing; +using System.IO; + +namespace SmartStore.Services.Media.Imaging +{ + public class ImageWrapper : DisposableObject, IImage + { + private readonly bool _disposeStream; + + public ImageWrapper(Stream stream, Size size, IImageFormat format, BitDepth bitDepth = BitDepth.Bit24, bool disposeStream = true) + { + Guard.NotNull(stream, nameof(stream)); + Guard.NotNull(format, nameof(format)); + + InStream = stream; + Format = format; + Size = size; + SourceSize = size; + BitDepth = bitDepth; + _disposeStream = disposeStream; + } + + #region IImageInfo + + /// + public Size Size { get; } + + /// + public BitDepth BitDepth { get; set; } = BitDepth.Bit24; + + /// + public IImageFormat Format { get; set; } + + #endregion + + public Stream InStream { get; } + + /// + public Size SourceSize { get; set; } + + /// + public IImage Save(string path) + { + Guard.NotEmpty(path, nameof(path)); + + var di = new DirectoryInfo(Path.GetDirectoryName(path)); + if (!di.Exists) + { + di.Create(); + } + + using var stream = File.OpenWrite(path); + return Save(stream); + } + + /// + public IImage Save(Stream stream) + { + Guard.NotNull(stream, nameof(stream)); + + if (stream.CanSeek) + { + stream.SetLength(0); + } + + InStream.CopyTo(stream); + + if (stream.CanSeek) + { + stream.Position = 0; + } + + if (InStream.CanSeek) + { + InStream.Position = 0; + } + + return this; + } + + protected override void OnDispose(bool disposing) + { + if (disposing && _disposeStream) + InStream.Dispose(); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/Impl/IPImage.cs b/src/Libraries/SmartStore.Services/Media/Imaging/Impl/IPImage.cs new file mode 100644 index 0000000000..ed3257e879 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/Impl/IPImage.cs @@ -0,0 +1,75 @@ +using System; +using System.Drawing; +using System.IO; +using ImageProcessor; + +namespace SmartStore.Services.Media.Imaging.Impl +{ + public class IPImage : DisposableObject, IProcessableImage + { + private readonly ImageFactory _image; + + public IPImage(ImageFactory image) + { + _image = image; + SourceSize = new Size(image.Image.Width, image.Image.Height); + } + + #region IImageInfo + + /// + public Size Size => _image.Image.Size; + + /// + public BitDepth BitDepth + { + get => (BitDepth)Convert.ToInt32(_image.CurrentBitDepth); + set => _image.BitDepth(Convert.ToInt64(value)); + } + + /// + public IImageFormat Format + { + get => new IPImageFormat(_image.CurrentImageFormat); + set + { + Guard.NotNull(value, nameof(value)); + _image.Format(((IPImageFormat)value).WrappedFormat); + } + } + + #endregion + + public ImageFactory ImageFactory => _image; + + /// + public Size SourceSize { get; } + + /// + public IImage Transform(Action transformer) + { + transformer(new IPImageTransformer(_image)); + return this; + } + + /// + public IImage Save(Stream stream) + { + _image.Save(stream); + return this; + } + + /// + public IImage Save(string path) + { + _image.Save(path); + return this; + } + + protected override void OnDispose(bool disposing) + { + if (disposing) + _image.Dispose(); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/Impl/IPImageFactory.cs b/src/Libraries/SmartStore.Services/Media/Imaging/Impl/IPImageFactory.cs new file mode 100644 index 0000000000..f1fdc2633d --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/Impl/IPImageFactory.cs @@ -0,0 +1,60 @@ +using System; +using System.IO; +using System.Linq; +using ImageProcessor; +using ImageProcessor.Configuration; +using ImageProcessor.Imaging.Formats; +using ImageProcessor.Plugins.WebP.Imaging.Formats; + +namespace SmartStore.Services.Media.Imaging.Impl +{ + public class IPImageFactory : IImageFactory + { + static IPImageFactory() + { + ImageProcessorBootstrapper.Instance.AddImageFormats(new WebPFormat { Quality = 100 }); + } + + public bool IsSupportedImage(string extension) + { + return GetInternalImageFormat(extension) != null; + } + + public IImageFormat GetImageFormat(string extension) + { + var internalFormat = GetInternalImageFormat(extension); + if (internalFormat != null) + { + return new IPImageFormat(internalFormat); + } + + return null; + } + + private ISupportedImageFormat GetInternalImageFormat(string extension) + { + if (extension.IsEmpty()) + { + return null; + } + + if (extension[0] == '.' && extension.Length > 1) + { + extension = extension.Substring(1); + } + + return ImageProcessorBootstrapper.Instance.SupportedImageFormats + .FirstOrDefault(x => x.FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); + } + + public IProcessableImage LoadImage(string path, bool preserveExif = false) + { + return new IPImage(new ImageFactory(preserveExif, fixGamma: false).Load(path)); + } + + public IProcessableImage LoadImage(Stream stream, bool preserveExif = false) + { + return new IPImage(new ImageFactory(preserveExif, fixGamma: false).Load(stream)); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/Impl/IPImageFormat.cs b/src/Libraries/SmartStore.Services/Media/Imaging/Impl/IPImageFormat.cs new file mode 100644 index 0000000000..8f84be635b --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/Impl/IPImageFormat.cs @@ -0,0 +1,28 @@ +using ImageProcessor.Imaging.Formats; +using System.Collections.Generic; + +namespace SmartStore.Services.Media.Imaging.Impl +{ + internal class IPImageFormat : IImageFormat + { + private readonly ISupportedImageFormat _format; + + public IPImageFormat(ISupportedImageFormat format) + { + Guard.NotNull(format, nameof(format)); + _format = format; + } + + public ISupportedImageFormat WrappedFormat => _format; + + public string Name => _format.DefaultExtension; + + public string DefaultExtension => _format.DefaultExtension == "jpeg" ? "jpg" : _format.DefaultExtension; + + public string DefaultMimeType => _format.MimeType; + + public IEnumerable FileExtensions => _format.FileExtensions; + + public IEnumerable MimeTypes => new[] { _format.MimeType }; + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/Impl/IPImageTransformer.cs b/src/Libraries/SmartStore.Services/Media/Imaging/Impl/IPImageTransformer.cs new file mode 100644 index 0000000000..fbd2708d1f --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/Impl/IPImageTransformer.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ImageProcessor; +using ImageProcessor.Imaging; + +namespace SmartStore.Services.Media.Imaging.Impl +{ + internal class IPImageTransformer : IImageTransformer + { + private readonly ImageFactory _image; + + public IPImageTransformer(ImageFactory image) + { + _image = image; + } + + public IImageTransformer Resize(Size size) + { + return Resize(new ResizeOptions { Size = size }); + } + + public IImageTransformer Resize(ResizeOptions options) + { + Guard.NotNull(options, nameof(options)); + + var layer = new ResizeLayer( + size: options.Size, + resizeMode: (ImageProcessor.Imaging.ResizeMode)options.ResizeMode, + anchorPosition: (ImageProcessor.Imaging.AnchorPosition)options.AnchorPosition, + upscale: options.Upscale, + centerCoordinates: options.CenterCoordinates, + maxSize: null, + restrictedSizes: null, + anchorPoint: options.AnchorPoint); + + _image.Resize(layer); + return this; + } + + public IImageTransformer BackgroundColor(Color color) + { + _image.BackgroundColor(color); + return this; + } + + public IImageTransformer Alpha(int percentage) + { + _image.Alpha(percentage); + return this; + } + + public IImageTransformer Brightness(int percentage) + { + _image.Brightness(percentage); + return this; + } + + public IImageTransformer Contrast(int percentage) + { + _image.Contrast(percentage); + return this; + } + + public IImageTransformer Crop(Rectangle rect) + { + _image.Crop(rect); + return this; + } + + public IImageTransformer EntropyCrop(byte threshold = 128) + { + _image.EntropyCrop(threshold); + return this; + } + + public IImageTransformer Flip(bool flipVertically = false, bool flipBoth = false) + { + _image.Flip(flipVertically, flipBoth); + return this; + } + + public IImageTransformer Format(IImageFormat format) + { + if (format is IPImageFormat ipFormat) + { + _image.Format(ipFormat.WrappedFormat); + return this; + } + + throw new ArgumentException("Passed image type must be '{0}'.".FormatCurrent(typeof(IPImageFormat)), nameof(format)); + } + + public IImageTransformer Gamma(float value) + { + _image.Gamma(value); + return this; + } + + public IImageTransformer GaussianBlur(int size) + { + _image.GaussianBlur(size); + return this; + } + + public IImageTransformer GaussianSharpen(int size) + { + _image.GaussianSharpen(size); + return this; + } + + public IImageTransformer Hue(int degrees, bool rotate = false) + { + _image.Hue(degrees, rotate); + return this; + } + + public IImageTransformer Quality(int percentage) + { + _image.Quality(percentage); + return this; + } + + public IImageTransformer BitDepth(BitDepth bitDepth) + { + _image.BitDepth((long)bitDepth); + return this; + } + + public IImageTransformer Overlay(IImage image, Size size, int opacity, Point? position) + { + if (image is IPImage ipImage) + { + _image.Overlay(new ImageLayer + { + Image = ipImage.ImageFactory.Image, + Size = size, + Opacity = opacity, + Position = position + }); + + return this; + } + + throw new ArgumentException("Passed image type must be '{0}'.".FormatCurrent(typeof(IPImage)), nameof(image)); + } + + public IImageTransformer Mask(IImage image, Size size, int opacity, Point? position) + { + if (image is IPImage ipImage) + { + _image.Mask(new ImageLayer + { + Image = ipImage.ImageFactory.Image, + Size = size, + Opacity = opacity, + Position = position + }); + + return this; + } + + throw new ArgumentException("Passed image type must be '{0}'.".FormatCurrent(typeof(IPImage)), nameof(image)); + } + + public IImageTransformer Rotate(float degrees, bool bounded = false, bool keepSize = false) + { + if (bounded) + _image.Rotate(degrees); + else + _image.RotateBounded(degrees, keepSize); + + return this; + } + + public IImageTransformer RoundedCorners(int radius) + { + _image.RoundedCorners(radius); + return this; + } + + public IImageTransformer Saturation(int percentage) + { + _image.Saturation(percentage); + return this; + } + + public IImageTransformer Tint(Color color) + { + _image.Tint(color); + return this; + } + + public IImageTransformer Vignette(Color? color = null) + { + _image.Vignette(color); + return this; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/ProcessImageException.cs b/src/Libraries/SmartStore.Services/Media/Imaging/ProcessImageException.cs new file mode 100644 index 0000000000..a51f9fefbd --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/ProcessImageException.cs @@ -0,0 +1,47 @@ +using System; + +namespace SmartStore.Services.Media.Imaging +{ + public sealed class ProcessImageException : Exception + { + public ProcessImageException() + : this((string)null, null) + { + } + + public ProcessImageException(ProcessImageQuery query) + : this(query, null) + { + } + + public ProcessImageException(string message, ProcessImageQuery query) + : base(message) + { + Query = query; + } + + public ProcessImageException(ProcessImageQuery query, Exception innerException) + : base(CreateMessage(query, innerException), innerException) + { + Query = query; + } + + private static string CreateMessage(ProcessImageQuery query, Exception innerException) + { + var fileName = query?.FileName; + + var msg = fileName.HasValue() + ? "Error while processing image '{0}'".FormatCurrent(fileName.NaIfEmpty()) + : "Error while processing image"; + + if (innerException != null) + { + msg += " (" + innerException.Message + ")"; + } + + return msg; + } + + public ProcessImageQuery Query { get; private set; } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/ProcessImageQuery.cs b/src/Libraries/SmartStore.Services/Media/Imaging/ProcessImageQuery.cs new file mode 100644 index 0000000000..1c754697e7 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/ProcessImageQuery.cs @@ -0,0 +1,295 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.IO; +using System.Collections.Specialized; +using SmartStore.Collections; +using System.Drawing; + +namespace SmartStore.Services.Media.Imaging +{ + public class ProcessImageQuery : QueryString + { + // Key = Supported token name, Value = Validator + private readonly static Dictionary> _supportedTokens = new Dictionary> + { + ["w"] = (k, v) => ValidateSizeToken(k, v), + ["h"] = (k, v) => ValidateSizeToken(k, v), + ["size"] = (k, v) => ValidateSizeToken(k, v), + ["q"] = (k, v) => ValidateQualityToken(k, v), + ["m"] = (k, v) => ValidateScaleModeToken(k, v) + }; + + public ProcessImageQuery() + : this(null, new NameValueCollection()) + { + } + + public ProcessImageQuery(byte[] source) + : this(source, new NameValueCollection()) + { + } + + public ProcessImageQuery(Stream source) + : this(source, new NameValueCollection()) + { + } + + public ProcessImageQuery(Image source) + : this(source, new NameValueCollection()) + { + } + + public ProcessImageQuery(string source) + : this(source, new NameValueCollection()) + { + } + + public ProcessImageQuery(object source, NameValueCollection query) + { + Guard.NotNull(query, nameof(query)); + + Source = source; + DisposeSource = true; + Notify = true; + + // Add tokens sanitized + query.AllKeys.Each(key => Add(key, query[key], false)); + } + + public ProcessImageQuery(ProcessImageQuery query) + : base(query) + { + Guard.NotNull(query, nameof(query)); + + Source = query.Source; + Format = query.Format; + DisposeSource = query.DisposeSource; + } + + /// + /// The source image's physical path, app-relative virtual path, or a Stream, byte array, Image or IFile instance. + /// + public object Source { get; set; } + + public string FileName { get; set; } + + /// + /// Whether to dispose the source stream after processing completes + /// + public bool DisposeSource { get; set; } + + /// + /// Whether to execute an applicable post processor which + /// can reduce the resulting file size drastically, but also + /// can slow down processing time. + /// + public bool ExecutePostProcessor { get; set; } = true; + + public int? MaxWidth + { + get => Get("w") ?? Get("size"); + set => Set("w", value); + } + + public int? MaxHeight + { + get => Get("h") ?? Get("size"); + set => Set("h", value); + } + + public int? MaxSize + { + get => Get("size"); + set => Set("size", value); + } + + public int? Quality + { + get => Get("q"); + set => Set("q", value); + } + + /// + /// max (default) | boxpad | crop | min | pad | stretch + /// + public string ScaleMode + { + get => Get("m"); + set => Set("m", value); + } + + /// + /// center (default) | top | bottom | left | right | top-left | top-right | bottom-left | bottom-right + /// + public string AnchorPosition + { + get => Get("pos"); + set => Set("pos", value); + } + + public string BackgroundColor + { + get => Get("bg"); + set => Set("bg", value); + } + + /// + /// Gets or sets the output file format either as a string ("png", "jpg", "gif" and "svg"), + /// or as a format object instance. + /// When format is not specified, the original format of the source image is used (unless it is not a web safe format - jpeg is the fallback in that scenario). + /// + public object Format { get; set; } + + public bool IsValidationMode { get; set; } + + public bool Notify { get; set; } + + public override QueryString Add(string name, string value, bool isUnique) + { + // Keep away invalid tokens from underlying query + if (name != null && _supportedTokens.TryGetValue(name, out var validator) && validator(name, value)) + { + return base.Add(name, value, isUnique); + } + + return this; + } + + private T Get(string name) + { + return this[name].Convert(); + } + + private void Set(string name, T val) + { + if (val == null) + Remove(name); + else + Add(name, val.Convert(), true); + } + + + public bool NeedsProcessing(bool ignoreQualityFlag = false) + { + if (this.Count == 0) + return false; + + if (object.Equals(Format, "svg")) + { + // SVG cannot be processed. + return false; + } + + if (ignoreQualityFlag && this.Count == 1 && this["q"] != null) + { + // Return false if ignoreQualityFlag is true and "q" is the only flag. + return false; + } + + if (this.Count == 1 && Quality >= 90) + { + // If "q" is the only flag and its value is >= 90, we don't need to process + return false; + } + + return true; + } + + public string CreateHash() + { + var hash = string.Empty; + + foreach (var key in this.AllKeys) + { + if (key == "m" && this["m"] == "max") + continue; // Mode 'max' is default and can be omitted + + hash += "-" + key + this[key]; + } + + return hash; + } + + public string GetResultExtension() + { + if (Format == null) + { + return null; + } + else if (Format is IImageFormat imageFormat) + { + return imageFormat.DefaultExtension; + } + else if (Format is string str) + { + return str; + } + + return null; + } + + #region Static Helpers + + public static ResizeMode ConvertScaleMode(string mode) + { + switch (mode.EmptyNull().ToLower()) + { + case "boxpad": + return ResizeMode.BoxPad; + case "crop": + return ResizeMode.Crop; + case "min": + return ResizeMode.Min; + case "pad": + return ResizeMode.Pad; + case "stretch": + return ResizeMode.Stretch; + default: + return ResizeMode.Max; + } + } + + public static AnchorPosition ConvertAnchorPosition(string anchor) + { + switch (anchor.EmptyNull().ToLower()) + { + case "top": + return SmartStore.Services.Media.Imaging.AnchorPosition.Top; + case "bottom": + return SmartStore.Services.Media.Imaging.AnchorPosition.Bottom; + case "left": + return SmartStore.Services.Media.Imaging.AnchorPosition.Left; + case "right": + return SmartStore.Services.Media.Imaging.AnchorPosition.Right; + case "top-left": + return SmartStore.Services.Media.Imaging.AnchorPosition.TopLeft; + case "top-right": + return SmartStore.Services.Media.Imaging.AnchorPosition.TopRight; + case "bottom-left": + return SmartStore.Services.Media.Imaging.AnchorPosition.BottomLeft; + case "bottom-right": + return SmartStore.Services.Media.Imaging.AnchorPosition.BottomRight; + default: + return SmartStore.Services.Media.Imaging.AnchorPosition.Center; + } + } + + private static bool ValidateSizeToken(string key, string value) + { + return uint.TryParse(value, out var size) && size < 10000; + } + + private static bool ValidateQualityToken(string key, string value) + { + return uint.TryParse(value, out var q) && q <= 100; + } + + private static bool ValidateScaleModeToken(string key, string value) + { + return (new[] { "max", "boxpad", "crop", "min", "pad", "stretch" }).Contains(value); + } + + #endregion + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/ProcessImageResult.cs b/src/Libraries/SmartStore.Services/Media/Imaging/ProcessImageResult.cs new file mode 100644 index 0000000000..6313e9d198 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/ProcessImageResult.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; + +namespace SmartStore.Services.Media.Imaging +{ + public class ProcessImageResult : DisposableObject + { + public ProcessImageQuery Query { get; set; } + + public IImageFormat SourceFormat { get; set; } + public IImage Image { get; set; } + internal bool DisposeImage { get; set; } + + /// + /// Is true if any effect has been applied that changed the image visually (like background color, contrast, sharpness etc.). + /// Resize and compression quality does NOT count as FX. + /// + public bool HasAppliedVisualEffects { get; set; } + + public long ProcessTimeMs { get; set; } + + protected override void OnDispose(bool disposing) + { + if (disposing && DisposeImage && Image != null) + { + Image.Dispose(); + Image = null; + } + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/ResizeOptions.cs b/src/Libraries/SmartStore.Services/Media/Imaging/ResizeOptions.cs new file mode 100644 index 0000000000..421dc6239b --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/ResizeOptions.cs @@ -0,0 +1,181 @@ +using System; +using System.Drawing; +using System.Linq; + +namespace SmartStore.Services.Media.Imaging +{ + /// + /// Enumerated resize modes to apply to resized images. + /// + public enum ResizeMode + { + /// + /// Pads the resized image to fit the bounds of its container. + /// If only one dimension is passed, will maintain the original aspect ratio. + /// + Pad, + + /// + /// Stretches the resized image to fit the bounds of its container. + /// + Stretch, + + /// + /// Crops the resized image to fit the bounds of its container. + /// + Crop, + + /// + /// Constrains the resized image to fit the bounds of its container maintaining + /// the original aspect ratio. + /// + Max, + + /// + /// Resizes the image until the shortest side reaches the set given dimension. + /// Sets to false only allowing downscaling. + /// + Min, + + /// + /// Pads the image to fit the bound of the container without resizing the + /// original source. Sets to true. + /// When downscaling, performs the same functionality as + /// + BoxPad + } + + /// + /// Enumerated anchor positions to apply to resized images. + /// + public enum AnchorPosition + { + /// + /// Anchors the position of the image to the center of it's bounding container. + /// + Center, + + /// + /// Anchors the position of the image to the top of it's bounding container. + /// + Top, + + /// + /// Anchors the position of the image to the bottom of it's bounding container. + /// + Bottom, + + /// + /// Anchors the position of the image to the left of it's bounding container. + /// + Left, + + /// + /// Anchors the position of the image to the right of it's bounding container. + /// + Right, + + /// + /// Anchors the position of the image to the top left side of it's bounding container. + /// + TopLeft, + + /// + /// Anchors the position of the image to the top right side of it's bounding container. + /// + TopRight, + + /// + /// Anchors the position of the image to the bottom right side of it's bounding container. + /// + BottomRight, + + /// + /// Anchors the position of the image to the bottom left side of it's bounding container. + /// + BottomLeft + } + + public class ResizeOptions + { + /// + /// Gets or sets the size. + /// + public Size Size { get; set; } + + /// + /// Gets or sets the resize mode. + /// + public ResizeMode ResizeMode { get; set; } = ResizeMode.Pad; + + /// + /// Gets or sets the anchor position. + /// + public AnchorPosition AnchorPosition { get; set; } = AnchorPosition.Center; + + /// + /// Gets or sets a value indicating whether to allow up-scaling of images. + /// For this is always true. + /// + public bool Upscale { get; set; } + + /// + /// Gets or sets the center coordinates. + /// + public float[] CenterCoordinates { get; set; } + + /// + /// Gets or sets the anchor point. + /// + public Point? AnchorPoint { get; set; } + + /// + /// Returns a value that indicates whether the specified object is an + /// object that is equivalent to + /// this object. + /// + /// + /// The object to test. + /// + /// + /// True if the given object is an object that is equivalent to + /// this object; otherwise, false. + /// + public override bool Equals(object obj) + { + if (!(obj is ResizeOptions options)) + { + return false; + } + + return this.Size == options.Size + && this.ResizeMode == options.ResizeMode + && this.AnchorPosition == options.AnchorPosition + && this.Upscale == options.Upscale + && ((this.CenterCoordinates != null + && options.CenterCoordinates != null + && this.CenterCoordinates.SequenceEqual(options.CenterCoordinates)) + || (this.CenterCoordinates == options.CenterCoordinates)) + && this.AnchorPoint == options.AnchorPoint; + } + + /// + /// Returns the hash code for this instance. + /// + /// + /// A 32-bit signed integer that is the hash code for this instance. + /// + public override int GetHashCode() + { + unchecked + { + int hashCode = this.Size.GetHashCode(); + hashCode = (hashCode * 397) ^ (int)this.ResizeMode; + hashCode = (hashCode * 397) ^ (int)this.AnchorPosition; + hashCode = (hashCode * 397) ^ this.Upscale.GetHashCode(); + hashCode = (hashCode * 397) ^ (this.CenterCoordinates?.GetHashCode() ?? 0); + return (hashCode * 397) ^ this.AnchorPoint.GetHashCode(); + } + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Imaging/UnsupportedImageFormat.cs b/src/Libraries/SmartStore.Services/Media/Imaging/UnsupportedImageFormat.cs new file mode 100644 index 0000000000..13b5cdd1ed --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Imaging/UnsupportedImageFormat.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; + +namespace SmartStore.Services.Media.Imaging +{ + public class UnsupportedImageFormat : IImageFormat + { + public UnsupportedImageFormat(string mimeType, string extension) + { + Guard.NotEmpty(mimeType, nameof(mimeType)); + Guard.NotEmpty(extension, nameof(extension)); + + if (extension[0] == '.' && extension.Length > 1) + { + extension = extension.Substring(1); + } + + Name = extension; + DefaultMimeType = mimeType; + DefaultExtension = extension; + } + + public string Name { get; } + public string DefaultExtension { get; } + public string DefaultMimeType { get; } + + public IEnumerable FileExtensions => new string[] { DefaultExtension }; + + public IEnumerable MimeTypes => new string[] { DefaultMimeType }; + } +} diff --git a/src/Libraries/SmartStore.Services/Media/IPictureService.cs b/src/Libraries/SmartStore.Services/Media/Legacy/IPictureService.cs similarity index 86% rename from src/Libraries/SmartStore.Services/Media/IPictureService.cs rename to src/Libraries/SmartStore.Services/Media/Legacy/IPictureService.cs index 84c6e2013c..3bee9d67b5 100644 --- a/src/Libraries/SmartStore.Services/Media/IPictureService.cs +++ b/src/Libraries/SmartStore.Services/Media/Legacy/IPictureService.cs @@ -11,7 +11,7 @@ namespace SmartStore.Services.Media { - public partial interface IPictureService + public partial interface IPictureService : IScopedService { /// /// Validates input picture dimensions and prevents that the image size exceeds global max size @@ -37,7 +37,7 @@ public partial interface IPictureService /// The sequence of pictures to seek within for duplicates /// Id of equal picture if any /// The picture binary for path when no picture equals in the sequence, null otherwise. - byte[] FindEqualPicture(byte[] pictureBinary, IEnumerable pictures, out int equalPictureId); + byte[] FindEqualPicture(byte[] pictureBinary, IEnumerable pictures, out int equalPictureId); /// /// Get picture SEO friendly name @@ -52,28 +52,28 @@ public partial interface IPictureService /// The picture identifier /// The SEO filename /// Picture - Picture SetSeoFilename(int pictureId, string seoFilename); + MediaFile SetSeoFilename(int pictureId, string seoFilename); /// /// Opens the picture stream from the underlying storage provider for reading /// /// Picture /// Picture stream - Stream OpenPictureStream(Picture picture); + Stream OpenPictureStream(MediaFile picture); /// /// Loads the picture binary from the underlying storage provider /// /// Picture /// Picture binary - byte[] LoadPictureBinary(Picture picture); + byte[] LoadPictureBinary(MediaFile picture); /// /// Asynchronously loads the picture binary from the underlying storage provider /// /// Picture /// Picture binary - Task LoadPictureBinaryAsync(Picture picture); + Task LoadPictureBinaryAsync(MediaFile picture); /// /// Gets the size of a picture @@ -110,7 +110,7 @@ public partial interface IPictureService /// /// /// - PictureInfo GetPictureInfo(Picture picture); + PictureInfo GetPictureInfo(MediaFile picture); /// /// Builds a url for a given . @@ -123,14 +123,14 @@ public partial interface IPictureService string GetUrl(int pictureId, int targetSize = 0, FallbackPictureType fallbackType = FallbackPictureType.Entity, string host = null); /// - /// Builds a url for a given instance. + /// Builds a url for a given instance. /// /// Picture instance /// The target picture size (longest side) /// Store location URL; null to use determine the current store location automatically /// Specifies the kind of fallback url to return if the argument is null. /// Picture URL - string GetUrl(Picture picture, int targetSize = 0, FallbackPictureType fallbackType = FallbackPictureType.Entity, string host = null); + string GetUrl(MediaFile picture, int targetSize = 0, FallbackPictureType fallbackType = FallbackPictureType.Entity, string host = null); /// /// Builds a url for a given instance. @@ -151,18 +151,12 @@ public partial interface IPictureService /// Picture URL string GetFallbackUrl(int targetSize = 0, FallbackPictureType fallbackType = FallbackPictureType.Entity, string host = null); - /// - /// Clears the url cache completely or for a particular store - /// - /// The total count of removed cache entries - int ClearUrlCache(); - /// /// Gets a picture /// /// Picture identifier /// Picture - Picture GetPictureById(int pictureId); + MediaFile GetPictureById(int pictureId); /// /// Gets a collection of pictures @@ -170,7 +164,7 @@ public partial interface IPictureService /// Current page /// Items on each page /// Paged list of pictures - IPagedList GetPictures(int pageIndex, int pageSize); + IPagedList GetPictures(int pageIndex, int pageSize); /// /// Gets pictures by product identifier @@ -178,7 +172,7 @@ public partial interface IPictureService /// Product identifier /// Number of records to return. 0 if you want to get all items /// Pictures - IList GetPicturesByProductId(int productId, int recordsToReturn = 0); + IList GetPicturesByProductId(int productId, int recordsToReturn = 0); /// /// Gets a pictures map by product identifiers @@ -187,7 +181,7 @@ public partial interface IPictureService /// Max number of pictures to retrieve per product, null to load all pictures /// Whether the blob in MediaStorage table should be eager loaded /// A lookup map of product ids and pictures - Multimap GetPicturesByProductIds(int[] productIds, int? maxPicturesPerProduct = null, bool withBlobs = false); + Multimap GetPicturesByProductIds(int[] productIds, int? maxPicturesPerProduct = null, bool withBlobs = false); /// /// Gets pictures by picture identifier @@ -195,13 +189,13 @@ public partial interface IPictureService /// Picture identifier /// Whether the blob in MediaStorage table should be eager loaded /// Pictures - IList GetPicturesByIds(int[] pictureIds, bool withBlobs = false); + IList GetPicturesByIds(int[] pictureIds, bool withBlobs = false); /// /// Deletes a picture /// /// Picture - void DeletePicture(Picture picture); + void DeletePicture(MediaFile picture); /// /// Inserts a picture @@ -209,19 +203,18 @@ public partial interface IPictureService /// The picture binary /// The picture MIME type /// The SEO filename - /// A value indicating whether the picture is new /// Picture width /// Picture height /// A value indicating whether the picture is initially in transient state /// Picture - Picture InsertPicture( + MediaFile InsertPicture( byte[] pictureBinary, string mimeType, string seoFilename, - bool isNew, int width = 0, int height = 0, - bool isTransient = true); + bool isTransient = true, + string album = null); /// /// Inserts a picture @@ -233,7 +226,13 @@ Picture InsertPicture( /// A value indicating whether the picture is initially in transient state /// A value indicating whether to validated provided picture binary /// Picture - Picture InsertPicture(byte[] pictureBinary, string mimeType, string seoFilename, bool isNew, bool isTransient = true, bool validateBinary = true); + MediaFile InsertPicture( + byte[] pictureBinary, + string mimeType, + string seoFilename, + bool isTransient = true, + bool validateBinary = true, + string album = null); /// /// Updates the picture @@ -242,32 +241,35 @@ Picture InsertPicture( /// The picture binary /// The picture MIME type /// The SEO filename - /// A value indicating whether the picture is new /// A value indicating whether to validated provided picture binary - void UpdatePicture(Picture picture, byte[] pictureBinary, string mimeType, string seoFilename, bool isNew, bool validateBinary = true); + void UpdatePicture( + MediaFile picture, + byte[] pictureBinary, + string mimeType, + string seoFilename, + bool validateBinary = true); } public static class IPictureServiceExtensions { - public static Picture UpdatePicture(this IPictureService pictureService, + public static MediaFile UpdatePicture(this IPictureService pictureService, int pictureId, byte[] pictureBinary, string mimeType, - string seoFilename, - bool isNew, + string seoFilename, bool validateBinary = true) { var picture = pictureService.GetPictureById(pictureId); if (picture != null) { - pictureService.UpdatePicture(picture, pictureBinary, mimeType, seoFilename, isNew, validateBinary); + pictureService.UpdatePicture(picture, pictureBinary, mimeType, seoFilename, validateBinary); } return picture; } - public static Size GetPictureSize(this IPictureService pictureService, Picture picture) + public static Size GetPictureSize(this IPictureService pictureService, MediaFile picture) { return ImageHeader.GetDimensions(pictureService.OpenPictureStream(picture), picture.MimeType, false); } @@ -306,7 +308,7 @@ public static string GetUrl(this IPictureService pictureService, int pictureId, /// The host (including scheme) to prepend to the url. /// Specifies whether to return a fallback url if the picture does not exist in the storage (default: true). /// Generated url which can be processed by the media middleware controller - public static string GetUrl(this IPictureService pictureService, Picture picture, int targetSize, bool fallback, string host = null) + public static string GetUrl(this IPictureService pictureService, MediaFile picture, int targetSize, bool fallback, string host = null) { var fallbackType = fallback ? FallbackPictureType.Entity : FallbackPictureType.NoFallback; return pictureService.GetUrl(picture, targetSize, fallbackType, host); diff --git a/src/Libraries/SmartStore.Services/Media/Legacy/PictureService.cs b/src/Libraries/SmartStore.Services/Media/Legacy/PictureService.cs new file mode 100644 index 0000000000..e460dd94ef --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Legacy/PictureService.cs @@ -0,0 +1,821 @@ +using System; +using System.Collections.Generic; +using System.Data.Entity; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Hosting; +using SmartStore.Collections; +using SmartStore.Core; +using SmartStore.Core.Caching; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.Events; +using SmartStore.Core.IO; +using SmartStore.Core.Logging; +using SmartStore.Core.Plugins; +using SmartStore.Services.Configuration; +using SmartStore.Services.Media.Storage; +using SmartStore.Utilities; + +namespace SmartStore.Services.Media +{ + [Serializable] + public class PictureInfo + { + public int Id { get; set; } + /// + /// The virtual path to the image file to be processed by the media middleware controller, e.g. "/media/image/1234/image.jpg" + /// + public string Path { get; set; } + public int? Width { get; set; } + public int? Height { get; set; } + public string MimeType { get; set; } + public string Extension { get; set; } + } + + public partial class PictureService : ScopedServiceBase, IPictureService + { + // 0 = Id + private const string MEDIACACHE_LOOKUP_KEY = "media:info-{0}"; + private const string MEDIACACHE_LOOKUP_KEY_PATTERN = "media:info-*"; + + private readonly IRepository _pictureRepository; + private readonly IRepository _productPictureRepository; + private readonly IAlbumRegistry _albumRegistry; + private readonly IMediaTypeResolver _mediaTypeResolver; + private readonly IMediaUrlGenerator _mediaUrlGenerator; + private readonly ISettingService _settingService; + private readonly IEventPublisher _eventPublisher; + private readonly MediaSettings _mediaSettings; + private readonly IImageProcessor _imageProcessor; + private readonly IImageCache _imageCache; + private readonly Provider _storageProvider; + private readonly HttpContextBase _httpContext; + private readonly ICacheManager _cacheManager; + + private readonly string _host; + private readonly string _appPath; + + private static readonly string _processedImagesRootPath; + private static readonly string _fallbackImagesRootPath; + + static PictureService() + { + _processedImagesRootPath = MediaFileSystem.GetMediaPublicPath() + "image/"; + _fallbackImagesRootPath = "content/images/"; + } + + public PictureService( + IRepository pictureRepository, + IRepository productPictureRepository, + IAlbumRegistry albumRegistry, + IMediaTypeResolver mediaTypeResolver, + IMediaUrlGenerator mediaUrlGenerator, + ISettingService settingService, + IEventPublisher eventPublisher, + MediaSettings mediaSettings, + IImageProcessor imageProcessor, + IImageCache imageCache, + IProviderManager providerManager, + IStoreContext storeContext, + HttpContextBase httpContext, + ICacheManager cacheManager) + { + _pictureRepository = pictureRepository; + _productPictureRepository = productPictureRepository; + _albumRegistry = albumRegistry; + _mediaTypeResolver = mediaTypeResolver; + _mediaUrlGenerator = mediaUrlGenerator; + _settingService = settingService; + _eventPublisher = eventPublisher; + _mediaSettings = mediaSettings; + _imageProcessor = imageProcessor; + _imageCache = imageCache; + _httpContext = httpContext; + _cacheManager = cacheManager; + + var systemName = settingService.GetSettingByKey("Media.Storage.Provider", DatabaseMediaStorageProvider.SystemName); + _storageProvider = providerManager.GetProvider(systemName); + + Logger = NullLogger.Instance; + + string appPath = "/"; + + if (HostingEnvironment.IsHosted) + { + appPath = HostingEnvironment.ApplicationVirtualPath.EmptyNull(); + + var cdn = storeContext.CurrentStore.ContentDeliveryNetwork; + if (cdn.HasValue() && !_httpContext.IsDebuggingEnabled && !_httpContext.Request.IsLocal) + { + _host = cdn; + } + else if (mediaSettings.AutoGenerateAbsoluteUrls) + { + var uri = httpContext.Request.Url; + _host = "//{0}{1}".FormatInvariant(uri.Authority, appPath); + } + else + { + _host = appPath; + } + } + + _host = _host.EmptyNull().EnsureEndsWith("/"); + _appPath = appPath.EnsureEndsWith("/"); + } + + public ILogger Logger { get; set; } + + #region Utilities + + public static string FallbackImagesRootPath + { + get { return "~/" + _fallbackImagesRootPath; } + } + + protected virtual string GetFallbackImageFileName(FallbackPictureType defaultPictureType = FallbackPictureType.Entity) + { + string defaultImageFileName; + + switch (defaultPictureType) + { + case FallbackPictureType.Entity: + defaultImageFileName = _settingService.GetSettingByKey("Media.DefaultImageName", "default-image.png"); + break; + default: + defaultImageFileName = _settingService.GetSettingByKey("Media.DefaultImageName", "default-image.png"); + break; + } + + return defaultImageFileName; + } + + #endregion + + #region Imaging + + public virtual byte[] ValidatePicture(byte[] pictureBinary, string mimeType) + { + return ValidatePicture(pictureBinary, mimeType, out _); + } + + public virtual byte[] ValidatePicture(byte[] pictureBinary, string mimeType, out Size size) + { + Guard.NotNull(pictureBinary, nameof(pictureBinary)); + Guard.NotEmpty(mimeType, nameof(mimeType)); + + size = Size.Empty; + + var originalSize = ImageHeader.GetDimensions(pictureBinary, mimeType); + + if (mimeType == "image/svg+xml") + { + size = originalSize; + return pictureBinary; + } + + var maxSize = _mediaSettings.MaximumImageSize; + + var query = new ProcessImageQuery(pictureBinary) + { + Quality = _mediaSettings.DefaultImageQuality, + Format = MimeTypes.MapMimeTypeToExtension(mimeType), + IsValidationMode = true + }; + + if (originalSize.IsEmpty || (originalSize.Height <= maxSize && originalSize.Width <= maxSize)) + { + // Give subscribers the chance to (pre)-process + var evt = new ImageUploadValidatedEvent(query, originalSize); + _eventPublisher.Publish(evt); + + if (evt.ResultStream != null) + { + // Maybe subscriber forgot to set this, so check + size = evt.ResultSize.IsEmpty ? originalSize : evt.ResultSize; + return evt.ResultStream.ToByteArray(); + } + else + { + size = originalSize; + return pictureBinary; + } + } + + query.MaxSize = maxSize; + + using (var result = _imageProcessor.ProcessImage(query)) + { + size = new Size(result.Width, result.Height); + var buffer = result.OutputStream.ToByteArray(); + return buffer; + } + } + + 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); + + using (var otherStream = OpenPictureStream(picture)) + { + if (myStream.ContentsEqual(otherStream)) + { + equalPictureId = picture.Id; + return null; + } + } + } + + return pictureBinary; + } + catch + { + return null; + } + finally + { + myStream.Dispose(); + } + } + + public virtual Stream OpenPictureStream(MediaFile picture) + { + Guard.NotNull(picture, nameof(picture)); + + return _storageProvider.Value.OpenRead(picture); + } + + public virtual byte[] LoadPictureBinary(MediaFile picture) + { + Guard.NotNull(picture, nameof(picture)); + + return _storageProvider.Value.Load(picture); + } + + public virtual Task LoadPictureBinaryAsync(MediaFile picture) + { + Guard.NotNull(picture, nameof(picture)); + + return _storageProvider.Value.LoadAsync(picture); + } + + public virtual Size GetPictureSize(byte[] pictureBinary, string mimeType = null) + { + return ImageHeader.GetDimensions(pictureBinary, mimeType); + } + + public IDictionary GetPictureInfos(IEnumerable pictureIds) + { + Guard.NotNull(pictureIds, nameof(pictureIds)); + + var allRequestedInfos = (from id in pictureIds.Distinct().Where(x => x > 0) + let cacheKey = MEDIACACHE_LOOKUP_KEY.FormatInvariant(id) + select new + { + PictureId = id, + CacheKey = cacheKey, + Info = _cacheManager.Contains(cacheKey) ? _cacheManager.Get(cacheKey, independent: true) : (PictureInfo)null + }).ToList(); + + var result = new Dictionary(allRequestedInfos.Count); + var uncachedPictureIds = allRequestedInfos.Where(x => x.Info == null).Select(x => x.PictureId).ToArray(); + var uncachedPictures = new Dictionary(); + + if (uncachedPictureIds.Length > 0) + { + uncachedPictures = GetPicturesByIds(uncachedPictureIds, false).ToDictionary(x => x.Id); + } + + foreach (var info in allRequestedInfos) + { + if (info.Info != null) + { + result.Add(info.PictureId, info.Info); + } + else + { + // TBD: (mc) Does this need a locking strategy? Apparently yes. But it is hard to accomplish for a random sequence + // without locking the whole thing and loosing performance. Better no lock (?) + var newInfo = CreatePictureInfo(uncachedPictures.Get(info.PictureId)); + result.Add(info.PictureId, newInfo); + GetCache().Put(info.CacheKey, newInfo); + HasChanges = true; + } + } + + return result; + } + + public PictureInfo GetPictureInfo(int? pictureId) + { + if (pictureId.GetValueOrDefault() < 1) + return null; + + var cacheKey = MEDIACACHE_LOOKUP_KEY.FormatInvariant(pictureId.GetValueOrDefault()); + + var info = _cacheManager.Get(cacheKey, independent: true); + if (info == null) + { + info = CreatePictureInfo(GetPictureById(pictureId.GetValueOrDefault())); + GetCache().Put(cacheKey, info); + } + + return info; + } + + public PictureInfo GetPictureInfo(MediaFile picture) + { + if (picture == null) + return null; + + var cacheKey = MEDIACACHE_LOOKUP_KEY.FormatInvariant(picture.Id); + + var info = _cacheManager.Get(cacheKey, independent: true); + if (info == null) + { + info = CreatePictureInfo(picture); + GetCache().Put(cacheKey, info); + } + + return info; + } + + public virtual string GetUrl(int pictureId, int targetSize = 0, FallbackPictureType fallbackType = FallbackPictureType.Entity, string host = null) + { + return GetUrl(GetPictureInfo(pictureId), targetSize, fallbackType, host); + } + + public virtual string GetUrl(MediaFile picture, int targetSize = 0, FallbackPictureType fallbackType = FallbackPictureType.Entity, string host = null) + { + return GetUrl(GetPictureInfo(picture), targetSize, fallbackType, host); + } + + public string GetUrl(PictureInfo info, int targetSize = 0, FallbackPictureType fallbackType = FallbackPictureType.Entity, string host = null) + { + string path = null; + string query = null; + + if (info?.Path != null) + { + path = info.Path; + } + else if (fallbackType > FallbackPictureType.NoFallback) + { + path = String.Concat(_processedImagesRootPath, "0/", GetFallbackImageFileName(fallbackType)); + } + + if (path != null) + { + if (targetSize > 0) + { + // TBD: (mc) let pass query string as NameValueCollection (?) + query = "?size=" + targetSize; + } + + path = BuildUrlCore(path, query, host); + } + + return path; + } + + public virtual string GetFallbackUrl(int targetSize = 0, FallbackPictureType fallbackType = FallbackPictureType.Entity, string host = null) + { + return GetUrl((PictureInfo)null, targetSize, fallbackType, host); + } + + /// + /// Creates a cacheable counterpart for a Picture object instance + /// + /// + /// + protected virtual PictureInfo CreatePictureInfo(MediaFile picture) + { + if (picture == null) + return null; + + // Temp code during v4 media manager dev (START) + var fileName = picture.Name.NullEmpty() ?? picture.Id.ToString(ImageCache.IdFormatString); + var extension = string.Empty; + var nameHasExtension = Path.GetExtension(fileName).Length > 1; + if (!nameHasExtension) + { + extension = picture.Extension; + if (extension.IsEmpty()) + { + extension = MimeTypes.MapMimeTypeToExtension(picture.MimeType); + } + if (extension.HasValue()) + { + extension = "." + extension; + } + } + // Temp code during v4 media manager dev (END) + + // Build virtual path with pattern "media/image/{id}/{Name}.{Extension}" + var path = "{0}{1}/{2}".FormatInvariant( + _processedImagesRootPath, + picture.Id, + fileName + extension); + + // Do some maintenance stuff + EnsurePictureSizeResolved(picture, true); + + extension = (picture.Extension.NullEmpty() ?? Path.GetExtension(picture.Name).NullEmpty() ?? MimeTypes.MapMimeTypeToExtension(picture.MimeType)).TrimStart('.'); + + return new PictureInfo + { + Id = picture.Id, + MimeType = picture.MimeType, + Extension = extension, + Path = path, + Width = picture?.Width, + Height = picture?.Height, + }; + } + + protected virtual string BuildUrlCore(string virtualPath, string query, string host) + { + // TBD: (mc) No arg check because of performance?! + + if (host == null) + { + host = _host; + } + else if (host == string.Empty) + { + host = _appPath; + } + else + { + host = host.EnsureEndsWith("/"); + } + + var url = host; + + // Strip leading "/", the host/apppath has this already + if (virtualPath[0] == '/') + { + virtualPath = virtualPath.Substring(1); + } + + // Append media path + url += virtualPath; + + // Append query + if (query != null && query.Length > 0) + { + if (query[0] != '?') url += "?"; + url += query; + } + + return url; + } + + private void EnsurePictureSizeResolved(MediaFile picture, bool saveOnResolve) + { + if (picture.MediaType == MediaType.Image && picture.Width == null && picture.Height == null) + { + var stream = _storageProvider.Value.OpenRead(picture); + + if (stream != null) + { + try + { + var size = ImageHeader.GetDimensions(stream, picture.MimeType, true); + picture.Width = size.Width; + picture.Height = size.Height; + picture.UpdatedOnUtc = DateTime.UtcNow; + + if (saveOnResolve) + { + try + { + _pictureRepository.Update(picture); + } + catch (InvalidOperationException ioe) + { + // Ignore exception for pictures that already have been processed. + if (!ioe.IsAlreadyAttachedEntityException()) + { + throw; + } + } + } + } + finally + { + stream.Dispose(); + } + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ICacheManager GetCache() + { + return IsInScope ? NullCache.Instance : _cacheManager; + } + + protected override void OnClearCache() + { + _cacheManager.RemoveByPattern(MEDIACACHE_LOOKUP_KEY_PATTERN); + } + + #endregion + + #region Metadata Storage + + public virtual string GetPictureSeName(string name) + { + return SeoHelper.GetSeName(name, true, false, false); + } + + public virtual MediaFile SetSeoFilename(int pictureId, string seoFilename) + { + var picture = GetPictureById(pictureId); + + //// TODO: (mm) Think about file name change requirement thoroughly + //// update if it has been changed + //if (picture != null && seoFilename != picture.Name) + //{ + // UpdatePicture(picture, null, picture.MimeType, seoFilename, true, false); + //} + + return picture; + } + + public virtual MediaFile GetPictureById(int pictureId) + { + if (pictureId == 0) + return null; + + var picture = _pictureRepository.GetById(pictureId); + return picture; + } + + public virtual IPagedList GetPictures(int pageIndex, int pageSize) + { + var query = from p in _pictureRepository.Table + orderby p.Id descending + select p; + + var pics = new PagedList(query, pageIndex, pageSize); + return pics; + } + + public virtual IList GetPicturesByProductId(int productId, int recordsToReturn = 0) + { + if (productId == 0) + return new List(); + + var query = from p in _pictureRepository.Table + join pp in _productPictureRepository.Table on p.Id equals pp.MediaFileId + orderby pp.DisplayOrder + where pp.ProductId == productId + select p; + + if (recordsToReturn > 0) + query = query.Take(() => recordsToReturn); + + var pics = query.ToList(); + return pics; + } + + public virtual Multimap GetPicturesByProductIds(int[] productIds, int? maxPicturesPerProduct = null, bool withBlobs = false) + { + Guard.NotNull(productIds, nameof(productIds)); + + if (maxPicturesPerProduct.HasValue) + { + Guard.IsPositive(maxPicturesPerProduct.Value, nameof(maxPicturesPerProduct)); + } + + var map = new Multimap(); + + if (!productIds.Any()) + return map; + + int take = maxPicturesPerProduct ?? int.MaxValue; + + var query = from pp in _productPictureRepository.TableUntracked + where productIds.Contains(pp.ProductId) + group pp by pp.ProductId into g + select new + { + ProductId = g.Key, + Pictures = g.OrderBy(x => x.DisplayOrder) + .Take(take) + .Select(x => new { x.MediaFileId, x.ProductId }) + }; + + var groupingResult = query.ToDictionary(x => x.ProductId, x => x.Pictures); + + using (var scope = new DbContextScope(ctx: _pictureRepository.Context, forceNoTracking: null)) + { + // EF doesn't support eager loading with grouped queries. We must hack a little bit. + var pictureIds = groupingResult.SelectMany(x => x.Value).Select(x => x.MediaFileId).Distinct().ToArray(); + var pictures = GetPicturesByIds(pictureIds, withBlobs).ToDictionarySafe(x => x.Id); + + foreach (var p in groupingResult.SelectMany(x => x.Value)) + { + if (pictures.ContainsKey(p.MediaFileId)) + { + map.Add(p.ProductId, pictures[p.MediaFileId]); + } + } + } + + return map; + } + + public virtual IList GetPicturesByIds(int[] pictureIds, bool withBlobs = false) + { + Guard.NotNull(pictureIds, nameof(pictureIds)); + + var query = _pictureRepository.Table + .Where(x => pictureIds.Contains(x.Id)); + + if (withBlobs) + { + query = query.Include(x => x.MediaStorage); + } + + return query.ToList(); + } + + public virtual void DeletePicture(MediaFile picture) + { + Guard.NotNull(picture, nameof(picture)); + + // delete thumbs + _imageCache.Delete(picture); + + // delete from url cache + _cacheManager.Remove(MEDIACACHE_LOOKUP_KEY.FormatInvariant(picture.Id)); + HasChanges = true; + + // delete from storage + _storageProvider.Value.Remove(picture); + + // delete entity + _pictureRepository.Delete(picture); + } + + public virtual MediaFile InsertPicture( + byte[] pictureBinary, + string mimeType, + string filename, + bool isTransient = true, + bool validateBinary = true, + string album = null) + { + var size = Size.Empty; + + if (validateBinary) + { + pictureBinary = ValidatePicture(pictureBinary, mimeType, out size); + } + + return InsertPicture(pictureBinary, mimeType, filename, size.Width, size.Height, isTransient, album); + } + + public virtual MediaFile InsertPicture( + byte[] pictureBinary, + string mimeType, + string fileName, + int width, + int height, + bool isTransient = true, + string album = null) + { + var mime = mimeType.EmptyNull().Truncate(100); + var name = NormalizeFileName(fileName, ref mime, out string ext); + + var file = new MediaFile + { + MimeType = mime, + Extension = ext, + Name = name, + IsTransient = isTransient + }; + + file.MediaType = _mediaTypeResolver.Resolve(file); + + if (album.HasValue()) + { + var albumId = _albumRegistry.GetAlbumByName(album)?.Id; + if (albumId > 0) + { + file.FolderId = albumId; + } + } + + var stream = pictureBinary?.ToStream(); + + file.RefreshMetadata(stream); + _pictureRepository.Insert(file); + + // Save to storage. + _storageProvider.Value.Save(file, stream); + + return file; + } + + public virtual void UpdatePicture( + MediaFile picture, + byte[] pictureBinary, + string mimeType, + string fileName, + bool validateBinary = true) + { + if (picture == null) + return; + + var mime = mimeType.EmptyNull().Truncate(100); + var name = NormalizeFileName(fileName, ref mime, out string ext); + + var size = Size.Empty; + + if (validateBinary && pictureBinary != null) + { + pictureBinary = ValidatePicture(pictureBinary, mime, out size); + picture.Size = pictureBinary.Length; + } + + // delete old thumbs if a picture has been changed + if (name != picture.Name) + { + _imageCache.Delete(picture); + + // delete from url cache + _cacheManager.Remove(MEDIACACHE_LOOKUP_KEY.FormatInvariant(picture.Id)); + HasChanges = true; + } + + picture.Extension = ext; + picture.MimeType = mime; + picture.Name = name; + + if (!size.IsEmpty) + { + picture.Width = size.Width; + picture.Height = size.Height; + picture.PixelSize = size.Width * size.Height; + } + + picture.MediaType = _mediaTypeResolver.Resolve(picture); + + var stream = pictureBinary?.ToStream(); + + if (stream != null) + { + picture.RefreshMetadata(stream); + } + + _pictureRepository.Update(picture); + + // save to storage + if (stream != null) + { + _storageProvider.Value.Save(picture, stream); + } + } + + private string NormalizeFileName(string name, ref string mime, out string ext) + { + // TODO: (mm) temp helper only, refactor and remove later + var title = Path.GetFileNameWithoutExtension(name); + + if (title != name) + { + // File name includes extension already + title = GetPictureSeName(title.Truncate(200)); + ext = Path.GetExtension(name).Trim('.'); + mime = MimeTypes.MapNameToMimeType(name); + return title + "." + ext; + } + + // Filename is extensionless + title = GetPictureSeName(title.Truncate(200)); + ext = MimeTypes.MapMimeTypeToExtension(mime); + return title.Grow(ext, "."); + } + + #endregion + } +} diff --git a/src/Libraries/SmartStore.Services/Media/MediaExceptionFactory.cs b/src/Libraries/SmartStore.Services/Media/MediaExceptionFactory.cs new file mode 100644 index 0000000000..5ae2854de5 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/MediaExceptionFactory.cs @@ -0,0 +1,148 @@ +using System; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.Localization; +using SmartStore.Utilities; + +namespace SmartStore.Services.Media +{ + #region Exception classes + + public sealed class MediaFileNotFoundException : SmartException + { + public MediaFileNotFoundException(string message) : base(message) { } + } + + public sealed class MediaFolderNotFoundException : SmartException + { + public MediaFolderNotFoundException(string message) : base(message) { } + } + + public sealed class DuplicateMediaFileException : SmartException + { + public DuplicateMediaFileException(string message, MediaFileInfo dupeFile, string uniquePath) : base(message) + { + File = dupeFile; + UniquePath = uniquePath; + } + + public MediaFileInfo File { get; } + public string UniquePath { get; } + } + + public sealed class DuplicateMediaFolderException : SmartException + { + public DuplicateMediaFolderException(string message, MediaFolderNode dupeFolder) : base(message) + { + Folder = dupeFolder; + } + + public MediaFolderNode Folder { get; } + } + + public sealed class NotSameAlbumException : SmartException + { + public NotSameAlbumException(string message) : base(message) { } + } + + public sealed class DeniedMediaTypeException : SmartException + { + public DeniedMediaTypeException(string message) : base(message) { } + } + + public sealed class ExtractThumbnailException : SmartException + { + public ExtractThumbnailException(string message) : base(message) { } + public ExtractThumbnailException(string message, Exception innerException) : base(message, innerException) { } + } + + public sealed class MaxMediaFileSizeExceededException : SmartException + { + public MaxMediaFileSizeExceededException(string message) : base(message) { } + } + + public sealed class DeleteTrackedFileException : SmartException + { + public DeleteTrackedFileException(string message, MediaFile file, Exception innerException) : base(message, innerException) + { + File = file; + } + + public MediaFile File { get; } + } + + #endregion + + public class MediaExceptionFactory + { + public Localizer T { get; set; } = NullLocalizer.Instance; + + public MediaFileNotFoundException FileNotFound(string path) + { + return new MediaFileNotFoundException(T("Admin.Media.Exception.FileNotFound", path)); + } + + public MediaFolderNotFoundException FolderNotFound(string path) + { + return new MediaFolderNotFoundException(T("Admin.Media.Exception.FolderNotFound", path)); + } + + public DuplicateMediaFileException DuplicateFile(string fullPath, MediaFileInfo dupeFile, string uniquePath) + { + return new DuplicateMediaFileException(T("Admin.Media.Exception.DuplicateFile", fullPath), dupeFile, uniquePath); + } + + public DuplicateMediaFolderException DuplicateFolder(string fullPath, MediaFolderNode dupeFolder) + { + return new DuplicateMediaFolderException(T("Admin.Media.Exception.DuplicateFolder", fullPath), dupeFolder); + } + + public NotSameAlbumException NotSameAlbum(string sourcePath, string destPath) + { + return new NotSameAlbumException(T("Admin.Media.Exception.NotSameAlbum", sourcePath, destPath)); + } + + public DeniedMediaTypeException DeniedMediaType(string fileName, string currentType, string[] acceptedTypes = null) + { + var msg = T("Admin.Media.Exception.DeniedMediaType", fileName, currentType); + if (acceptedTypes != null && acceptedTypes.Length > 0) + { + var types = string.Join(", ", acceptedTypes); + msg += T("Admin.Media.Exception.DeniedMediaType.Hint", types, currentType); + } + + return new DeniedMediaTypeException(msg); + } + + public ExtractThumbnailException ExtractThumbnail(string path, string reason = null) + { + return new ExtractThumbnailException(T("Admin.Media.Exception.ExtractThumbnail", path, reason.NaIfEmpty())); + } + + public ExtractThumbnailException ExtractThumbnail(string path, Exception innerException) + { + Guard.NotNull(innerException, nameof(innerException)); + return new ExtractThumbnailException(T("Admin.Media.Exception.ExtractThumbnail", path, innerException.Message), innerException); + } + + public MaxMediaFileSizeExceededException MaxFileSizeExceeded(string fileName, long fileSize, long maxSize) + { + return new MaxMediaFileSizeExceededException(T( + "Admin.Media.Exception.MaxFileSizeExceeded", + fileName.NaIfEmpty(), + Prettifier.BytesToString(fileSize), + Prettifier.BytesToString(maxSize))); + } + + public DeleteTrackedFileException DeleteTrackedFile(MediaFile file, Exception innerException) + { + Guard.NotNull(file, nameof(file)); + return new DeleteTrackedFileException(T("Admin.Media.Exception.DeleteReferenzedFile", file.Name), file, innerException); + } + + public InvalidOperationException IdenticalPaths(MediaFileInfo file) + { + Guard.NotNull(file, nameof(file)); + return new InvalidOperationException(T("Admin.Media.Exception.FileNamesIdentical", file.Path)); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/MediaExtensions.cs b/src/Libraries/SmartStore.Services/Media/MediaExtensions.cs deleted file mode 100644 index 40d85dd39e..0000000000 --- a/src/Libraries/SmartStore.Services/Media/MediaExtensions.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using SmartStore.Core; -using SmartStore.Core.Data; -using SmartStore.Core.Domain.Media; -using SmartStore.Core.Infrastructure; - -namespace SmartStore.Services.Media -{ - public static class MediaHelper - { - public static void UpdateDownloadTransientStateFor(TEntity entity, Expression> downloadIdProp, bool save = false) where TEntity : BaseEntity - { - Guard.NotNull(entity, nameof(entity)); - Guard.NotNull(downloadIdProp, nameof(downloadIdProp)); - - var propName = downloadIdProp.ExtractMemberInfo().Name; - int currentDownloadId = downloadIdProp.Compile().Invoke(entity); - var rs = EngineContext.Current.Resolve>(); - - UpdateTransientStateForEntityInternal(entity, propName, currentDownloadId, rs, null, save); - } - - public static void UpdateDownloadTransientStateFor(TEntity entity, Expression> downloadIdProp, bool save = false) where TEntity : BaseEntity - { - Guard.NotNull(entity, nameof(entity)); - Guard.NotNull(downloadIdProp, nameof(downloadIdProp)); - - var propName = downloadIdProp.ExtractMemberInfo().Name; - int currentDownloadId = downloadIdProp.Compile().Invoke(entity).GetValueOrDefault(); - var rs = EngineContext.Current.Resolve>(); - - UpdateTransientStateForEntityInternal(entity, propName, currentDownloadId, rs, null, save); - } - - public static void UpdatePictureTransientStateFor(TEntity entity, Expression> pictureIdProp, bool save = false) where TEntity : BaseEntity - { - Guard.NotNull(entity, nameof(entity)); - Guard.NotNull(pictureIdProp, nameof(pictureIdProp)); - - var propName = pictureIdProp.ExtractMemberInfo().Name; - int currentPictureId = pictureIdProp.Compile().Invoke(entity); - var rs = EngineContext.Current.Resolve>(); - - UpdateTransientStateForEntityInternal(entity, propName, currentPictureId, rs, GetPictureDeleteAction(), save); - } - - public static void UpdatePictureTransientStateFor(TEntity entity, Expression> pictureIdProp, bool save = false) where TEntity : BaseEntity - { - Guard.NotNull(entity, nameof(entity)); - Guard.NotNull(pictureIdProp, nameof(pictureIdProp)); - - var propName = pictureIdProp.ExtractMemberInfo().Name; - int currentPictureId = pictureIdProp.Compile().Invoke(entity).GetValueOrDefault(); - var rs = EngineContext.Current.Resolve>(); - - UpdateTransientStateForEntityInternal(entity, propName, currentPictureId, rs, GetPictureDeleteAction(), save); - } - - public static void UpdateDownloadTransientState(int? prevDownloadId, int? currentDownloadId, bool save = false) - { - var rs = EngineContext.Current.Resolve>(); - UpdateTransientStateCore(prevDownloadId.GetValueOrDefault(), currentDownloadId.GetValueOrDefault(), rs, null, save); - } - - public static void UpdateDownloadTransientState(int prevDownloadId, int currentDownloadId, bool save = false) - { - var rs = EngineContext.Current.Resolve>(); - UpdateTransientStateCore(prevDownloadId, currentDownloadId, rs, null, save); - } - - public static void UpdatePictureTransientState(int? prevPictureId, int? currentPictureId, bool save = false) - { - var rs = EngineContext.Current.Resolve>(); - UpdateTransientStateCore(prevPictureId.GetValueOrDefault(), currentPictureId.GetValueOrDefault(), rs, GetPictureDeleteAction(), save); - } - - public static void UpdatePictureTransientState(int prevPictureId, int currentPictureId, bool save = false) - { - var rs = EngineContext.Current.Resolve>(); - UpdateTransientStateCore(prevPictureId, currentPictureId, rs, GetPictureDeleteAction(), save); - } - - private static Action GetPictureDeleteAction() - { - Action deleteAction = pic => - { - var svc = EngineContext.Current.Resolve(); - svc.DeletePicture((Picture)pic); - }; - - return deleteAction; - } - - internal static void UpdateTransientStateForEntityInternal( - TEntity entity, - string propName, - int currentMediaId, - IRepository rs, - Action deleteAction, - bool save) where TEntity : BaseEntity where TMedia : BaseEntity - { - object obj = null; - int prevMediaId = 0; - if (rs.Context.TryGetModifiedProperty(entity, propName, out obj)) - { - prevMediaId = ((int?)obj).GetValueOrDefault(); - } - - UpdateTransientStateCore(prevMediaId, currentMediaId, rs, deleteAction, save); - } - - internal static void UpdateTransientStateCore( - int prevMediaId, - int currentMediaId, - IRepository rs, - Action deleteAction, - bool save) - where TMedia : BaseEntity - { - var autoCommit = rs.AutoCommitEnabled; - rs.AutoCommitEnabled = false; - - try - { - TMedia media = null; - bool shouldSave = false; - - bool isModified = prevMediaId != currentMediaId; - - if (currentMediaId > 0 && isModified) - { - // new entity with a media or media replaced - media = rs.GetById(currentMediaId); - if (media != null) - { - var transient = media as ITransient; - if (transient != null) - { - transient.IsTransient = false; - shouldSave = true; - } - } - } - - if (prevMediaId > 0 && isModified) - { - // ID changed, so delete old record - media = rs.GetById(prevMediaId); - if (media != null) - { - if (deleteAction == null) - { - rs.Delete(media); - } - else - { - deleteAction(media); - } - shouldSave = true; - } - } - - if (save && shouldSave) - { - rs.Context.SaveChanges(); - } - } - finally - { - rs.AutoCommitEnabled = autoCommit; - } - } - } - -} diff --git a/src/Libraries/SmartStore.Services/Media/MediaFileInfo.cs b/src/Libraries/SmartStore.Services/Media/MediaFileInfo.cs new file mode 100644 index 0000000000..b69a3bb069 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/MediaFileInfo.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Newtonsoft.Json; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.IO; +using SmartStore.Services.Media.Imaging; +using SmartStore.Services.Media.Storage; + +namespace SmartStore.Services.Media +{ + public partial class MediaFileInfo : IFile, ICloneable + { + private string _alt; + private string _title; + + private readonly IMediaStorageProvider _storageProvider; + private readonly IMediaUrlGenerator _urlGenerator; + + public MediaFileInfo(MediaFile file, IMediaStorageProvider storageProvider, IMediaUrlGenerator urlGenerator, string directory) + { + File = file; + Directory = directory.EmptyNull(); + + if (File.Width.HasValue && File.Height.HasValue) + { + Dimensions = new Size(File.Width.Value, File.Height.Value); + } + + _storageProvider = storageProvider; + _urlGenerator = urlGenerator; + } + + #region Clone + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + object ICloneable.Clone() + { + return Clone(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MediaFileInfo Clone() + { + var clone = new MediaFileInfo(File, _storageProvider, _urlGenerator, Directory) + { + ThumbSize = this.ThumbSize, + _alt = this._alt, + _title = this._title + }; + + return clone; + } + + #endregion + + [JsonIgnore] + public MediaFile File { get; } + + [JsonProperty("id")] + public int Id => File.Id; + + [JsonProperty("folderId")] + public int? FolderId => File.FolderId; + + [JsonProperty("mime")] + public string MimeType => File.MimeType; + + [JsonProperty("type")] + public string MediaType => File.MediaType; + + [JsonProperty("isTransient")] + public bool IsTransient => File.IsTransient; + + [JsonProperty("deleted")] + public bool Deleted => File.Deleted; + + [JsonProperty("hidden")] + public bool Hidden => File.Hidden; + + [JsonProperty("createdOn")] + public DateTime CreatedOn => File.CreatedOnUtc; + + [JsonProperty("alt")] + public string Alt + { + get => _alt ?? File.Alt; + set => _alt = value; + } + + [JsonProperty("titleAttr")] + public string TitleAttribute + { + get => _title ?? File.Title; + set => _title = value; + } + + public static explicit operator MediaFile(MediaFileInfo fileInfo) + { + return fileInfo.File; + } + + #region Url + + private readonly IDictionary<(int size, string host), string> _cachedUrls = new Dictionary<(int, string), string>(); + + [JsonProperty("url")] + internal string Url => GetUrl(0, string.Empty); + + [JsonProperty("thumbUrl")] + internal string ThumbUrl => GetUrl(ThumbSize, string.Empty); + + [JsonIgnore] + internal int ThumbSize + { + // For serialization of "ThumbUrl" in MediaManager + get; set; + } = 256; + + public string GetUrl(int maxSize = 0, string host = null) + { + var cacheKey = (maxSize, host); + if (!_cachedUrls.TryGetValue(cacheKey, out var url)) + { + url = _urlGenerator.GenerateUrl(this, null, host, false); + + if (maxSize > 0) + { + // (perf) Instead of calling GenerateUrl() with a processing query we simply + // append the query part to the string. + url += "?size=" + maxSize.ToString(CultureInfo.InvariantCulture); + } + + _cachedUrls[cacheKey] = url; + } + + return url; + } + + #endregion + + #region IFile + + [JsonProperty("path")] + public string Path => (Directory + "/" + File.Name).Trim('/'); + + [JsonProperty("dir")] + public string Directory { get; } + + [JsonProperty("name")] + public string Name => File.Name; + + [JsonProperty("title")] + public string Title => System.IO.Path.GetFileNameWithoutExtension(File.Name); + + [JsonProperty("size")] + public long Size => File.Size; + + [JsonProperty("lastUpdated")] + public DateTime LastUpdated => File.UpdatedOnUtc; + + [JsonProperty("ext")] + public string Extension => "." + File.Extension; + + [JsonProperty("dimensions")] + public Size Dimensions { get; } + + [JsonIgnore] + public bool Exists => File?.Id > 0; + + public Stream OpenRead() + { + return _storageProvider?.OpenRead(File); + } + + public Stream CreateFile() + { + throw new NotSupportedException(); + } + + public Task CreateFileAsync() + { + throw new NotSupportedException(); + } + + public Stream OpenWrite() + { + throw new NotSupportedException(); + } + + #endregion + } +} diff --git a/src/Libraries/SmartStore.Services/Media/MediaFileSystem.cs b/src/Libraries/SmartStore.Services/Media/MediaFileSystem.cs index 6317105fde..26599c17d8 100644 --- a/src/Libraries/SmartStore.Services/Media/MediaFileSystem.cs +++ b/src/Libraries/SmartStore.Services/Media/MediaFileSystem.cs @@ -5,51 +5,52 @@ namespace SmartStore.Services.Media { - public interface IMediaFileSystem : IFileSystem - { - } - - public class MediaFileSystem : LocalFileSystem, IMediaFileSystem - { - private static string _mediaPublicPath; - - public MediaFileSystem() - : base(GetMediaBasePath(), "~/" + GetMediaPublicPath()) - { - this.TryCreateFolder("Storage"); - this.TryCreateFolder("Thumbs"); - this.TryCreateFolder("Uploaded"); - this.TryCreateFolder("QueuedEmailAttachment"); - this.TryCreateFolder("Downloads"); - } - - public static string GetMediaBasePath() - { - var path = CommonHelper.GetAppSetting("sm:MediaStoragePath")?.Trim().NullEmpty(); - if (path == null) - { - path = "/App_Data/Tenants/" + DataSettings.Current.TenantName + "/Media"; - } - - return path; - } - - public static string GetMediaPublicPath() - { - if (_mediaPublicPath == null) - { - var path = CommonHelper.GetAppSetting("sm:MediaPublicPath")?.Trim().NullEmpty() ?? "media"; - - if (path.IsWebUrl()) - { - throw new NotSupportedException("Fully qualified URLs are not supported for the 'sm:MediaPublicPath' setting."); - } - - _mediaPublicPath = path.TrimStart('~', '/').Replace('\\', '/').ToLower().EnsureEndsWith("/"); - } - - - return _mediaPublicPath; - } - } + public interface IMediaFileSystem : IFileSystem + { + } + + public class MediaFileSystem : LocalFileSystem, IMediaFileSystem + { + private static string _mediaPublicPath; + + public MediaFileSystem() + : base(GetMediaBasePath(), "~/" + GetMediaPublicPath()) + { + if (DataSettings.DatabaseIsInstalled()) + { + this.CreateFolder("Storage"); + this.CreateFolder("Thumbs"); + this.CreateFolder("QueuedEmailAttachment"); + } + } + + public static string GetMediaBasePath() + { + var path = CommonHelper.GetAppSetting("sm:MediaStoragePath")?.Trim().NullEmpty(); + if (path == null) + { + path = "/App_Data/Tenants/" + DataSettings.Current.TenantName + "/Media"; + } + + return path; + } + + public static string GetMediaPublicPath() + { + if (_mediaPublicPath == null) + { + var path = CommonHelper.GetAppSetting("sm:MediaPublicPath")?.Trim().NullEmpty() ?? "media"; + + if (path.IsWebUrl()) + { + throw new NotSupportedException("Fully qualified URLs are not supported for the 'sm:MediaPublicPath' setting."); + } + + _mediaPublicPath = path.TrimStart('~', '/').Replace('\\', '/').ToLower().EnsureEndsWith("/"); + } + + + return _mediaPublicPath; + } + } } diff --git a/src/Libraries/SmartStore.Services/Media/MediaHelper.cs b/src/Libraries/SmartStore.Services/Media/MediaHelper.cs new file mode 100644 index 0000000000..aee99c3af7 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/MediaHelper.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace SmartStore.Services.Media +{ + public partial class MediaHelper + { + #region Static + + private readonly static char[] _invalidFileNameChars = Path.GetInvalidFileNameChars().Concat(new[] { '&' }).ToArray(); + private readonly static char[] _invalidFolderNameChars = Path.GetInvalidPathChars().Concat(new[] { '&', '/', '\\' }).ToArray(); + + public static string NormalizeFileName(string fileName) + { + return string.Join("-", fileName.ToSafe().Split(_invalidFileNameChars)); + } + + public static string NormalizeFolderName(string folderName) + { + return string.Join("-", folderName.ToSafe().Split(_invalidFolderNameChars)); + } + + #endregion + + private readonly IFolderService _folderService; + + public MediaHelper(IFolderService folderService) + { + _folderService = folderService; + } + + public bool TokenizePath(string path, bool normalizeFileName, out MediaPathData data) + { + data = null; + + if (path.IsEmpty()) + { + return false; + } + + var dir = Path.GetDirectoryName(path); + if (dir.HasValue()) + { + var node = _folderService.GetNodeByPath(dir); + if (node != null) + { + data = new MediaPathData(node, path.Substring(dir.Length + 1), normalizeFileName); + return true; + } + } + + return false; + } + + public bool CheckUniqueFileName(string title, string ext, string destFileName, out string uniqueName) + { + return CheckUniqueFileName(title, ext, new HashSet(new[] { destFileName }, StringComparer.CurrentCultureIgnoreCase), out uniqueName); + } + + public bool CheckUniqueFileName(string title, string ext, HashSet destFileNames, out string uniqueName) + { + uniqueName = null; + + if (destFileNames.Count == 0) + { + return false; + } + + int i = 1; + while (true) + { + var test = string.Concat(title, "-", i, ".", ext.TrimStart('.')); + if (!destFileNames.Contains(test)) + { + // Found our gap + uniqueName = test; + return true; + } + + i++; + } + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/MediaModule.cs b/src/Libraries/SmartStore.Services/Media/MediaModule.cs new file mode 100644 index 0000000000..80b3f5a9e1 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/MediaModule.cs @@ -0,0 +1,75 @@ +using System; +using Autofac; +using SmartStore.Core.Data; +using SmartStore.Core.Infrastructure; +using SmartStore.Core.Plugins; +using SmartStore.Services.Configuration; +using SmartStore.Services.Media.Imaging; +using SmartStore.Services.Media.Imaging.Impl; +using SmartStore.Services.Media.Migration; +using SmartStore.Services.Media.Storage; + +namespace SmartStore.Services.Media +{ + public class MediaModule : Module + { + private readonly ITypeFinder _typeFinder; + + public MediaModule(ITypeFinder typeFinder) + { + _typeFinder = typeFinder; + } + + protected override void Load(ContainerBuilder builder) + { + // Utils + builder.RegisterType().InstancePerRequest(); + builder.RegisterType().InstancePerRequest(); + builder.RegisterType().InstancePerRequest(); + builder.RegisterType().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + // ImageProcessor adapter factory + builder.RegisterType().As().SingleInstance(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + // Register factory for currently active media storage provider + if (DataSettings.DatabaseIsInstalled()) + { + builder.Register(MediaStorageProviderFactory); + } + else + { + builder.Register>(c => () => new FileSystemMediaStorageProvider(new MediaFileSystem())); + } + + // Register all album providers + var albumProviderTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true); + foreach (var type in albumProviderTypes) + { + builder.RegisterType(type).As().Keyed(type).InstancePerRequest(); + } + + // Handlers + builder.RegisterType().As().InstancePerRequest(); + } + + private static Func MediaStorageProviderFactory(IComponentContext c) + { + var systemName = c.Resolve().GetSettingByKey("Media.Storage.Provider", FileSystemMediaStorageProvider.SystemName); + var provider = c.Resolve().GetProvider(systemName); + return () => provider.Value; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/MediaPathData.cs b/src/Libraries/SmartStore.Services/Media/MediaPathData.cs new file mode 100644 index 0000000000..f6016760ee --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/MediaPathData.cs @@ -0,0 +1,78 @@ +using System; +using System.IO; +using SmartStore.Collections; +using SmartStore.Core.IO; + +namespace SmartStore.Services.Media +{ + public class MediaPathData + { + private string _name; + private string _title; + private string _ext; + private string _mime; + + public MediaPathData(TreeNode node, string fileName, bool normalizeFileName = false) + { + Guard.NotNull(node, nameof(node)); + Guard.NotEmpty(fileName, nameof(fileName)); + + Node = node; + _name = normalizeFileName + ? MediaHelper.NormalizeFileName(fileName) + : fileName; + } + + public MediaPathData(string path, bool normalizeFileName = false) + { + Guard.NotEmpty(path, nameof(path)); + + _name = normalizeFileName + ? MediaHelper.NormalizeFileName(Path.GetFileName(path)) + : Path.GetFileName(path); + } + + public MediaPathData(MediaPathData pathData) + { + Node = pathData.Node; + _name = pathData.FileName; + _title = pathData._title; + _ext = pathData._ext; + _mime = pathData._mime; + } + + public TreeNode Node { get; } + public MediaFolderNode Folder => Node.Value; + + public string FileName + { + get => _name; + set + { + Guard.NotEmpty(value, nameof(value)); + + _name = value; + _title = null; + _ext = null; + _mime = null; + } + } + + public string FullPath => Folder.Path + "/" + _name; + + public string FileTitle => _title ?? (_title = Path.GetFileNameWithoutExtension(_name)); + + public string Extension + { + get => _ext ?? (_ext = Path.GetExtension(_name).EmptyNull().TrimStart('.')); + set => _ext = value?.TrimStart('.'); + } + + public string MimeType + { + get => _mime ?? (_mime = MimeTypes.MapNameToMimeType(_name)); + set => _mime = value; + } + } + +} diff --git a/src/Libraries/SmartStore.Services/Media/MediaService.Folder.cs b/src/Libraries/SmartStore.Services/Media/MediaService.Folder.cs new file mode 100644 index 0000000000..37cf4491b0 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/MediaService.Folder.cs @@ -0,0 +1,459 @@ +using System; +using System.Collections.Generic; +using System.Data.Entity; +using System.IO; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Runtime.CompilerServices; +using SmartStore.Collections; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Services.Media +{ + public partial class MediaService : IMediaService + { + #region Folder + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool FolderExists(string path) + { + return _folderService.GetNodeByPath(path) != null; + } + + public MediaFolderInfo CreateFolder(string path) + { + Guard.NotEmpty(path, nameof(path)); + + path = FolderService.NormalizePath(path, false); + ValidateFolderPath(path, "CreateFolder", nameof(path)); + + var dupe = _folderService.GetNodeByPath(path); + if (dupe != null) + { + throw _exceptionFactory.DuplicateFolder(path, dupe.Value); + } + + var sep = "/"; + var folderNames = path.Split(new[] { sep }, StringSplitOptions.RemoveEmptyEntries); + bool flag = false; + int folderId = 0; + + path = string.Empty; + + using (_folderService.BeginScope(true)) + { + for (int i = 0; i < folderNames.Length; i++) + { + var folderName = MediaHelper.NormalizeFolderName(folderNames[i]); + path += (i > 0 ? sep : string.Empty) + folderName; + + if (!flag) + { + // Find the last existing node in path trail + var currentNode = _folderService.GetNodeByPath(path)?.Value; + if (currentNode != null) + { + folderId = currentNode.Id; + } + else + { + if (i == 0) throw new NotSupportedException(T("Admin.Media.Exception.TopLevelAlbum", path)); + flag = true; + } + } + + if (flag) + { + // Create missing folders in trail + using (new DbContextScope(autoCommit: true)) + { + var mediaFolder = new MediaFolder { Name = folderName, ParentId = folderId }; + _folderService.InsertFolder(mediaFolder); + folderId = mediaFolder.Id; + } + } + } + } + + return new MediaFolderInfo(_folderService.GetNodeById(folderId)); + } + + public MediaFolderInfo MoveFolder(string path, string destinationPath) + { + Guard.NotEmpty(path, nameof(path)); + Guard.NotEmpty(destinationPath, nameof(destinationPath)); + + path = FolderService.NormalizePath(path); + ValidateFolderPath(path, "MoveFolder", nameof(path)); + + var node = _folderService.GetNodeByPath(path); + if (node == null) + { + throw _exceptionFactory.FolderNotFound(path); + } + + if (node.Value.IsAlbum) + { + throw new NotSupportedException(T("Admin.Media.Exception.AlterRootAlbum", node.Value.Name)); + } + + var folder = _folderService.GetFolderById(node.Value.Id); + if (folder == null) + { + throw _exceptionFactory.FolderNotFound(path); + } + + ValidateFolderPath(destinationPath, "MoveFolder", nameof(destinationPath)); + + // Destination must not exist + if (FolderExists(destinationPath)) + { + throw new ArgumentException(T("Admin.Media.Exception.DuplicateFolder", destinationPath)); + } + + var destParent = FolderService.NormalizePath(Path.GetDirectoryName(destinationPath)); + + // Get destination parent + var destParentNode = _folderService.GetNodeByPath(destParent); + if (destParentNode == null) + { + throw _exceptionFactory.FolderNotFound(destinationPath); + } + + // Cannot move outside source album + if (!_folderService.AreInSameAlbum(folder.Id, destParentNode.Value.Id)) + { + throw _exceptionFactory.NotSameAlbum(node.Value.Path, destParent); + } + + if (destParentNode.IsDescendantOfOrSelf(node)) + { + throw new ArgumentException(T("Admin.Media.Exception.DescendantFolder", destinationPath, node.Value.Path)); + } + + // Set new values + folder.ParentId = destParentNode.Value.Id; + folder.Name = Path.GetFileName(destinationPath); + + // Commit + _folderService.UpdateFolder(folder); + + _folderService.ClearCache(); + + return new MediaFolderInfo(_folderService.GetNodeById(folder.Id)); + } + + public FolderOperationResult CopyFolder(string path, string destinationPath, DuplicateEntryHandling dupeEntryHandling = DuplicateEntryHandling.Skip) + { + Guard.NotEmpty(path, nameof(path)); + Guard.NotEmpty(destinationPath, nameof(destinationPath)); + + path = FolderService.NormalizePath(path); + ValidateFolderPath(path, "CopyFolder", nameof(path)); + + destinationPath = FolderService.NormalizePath(destinationPath); + if (destinationPath.EnsureEndsWith("/").StartsWith(path.EnsureEndsWith("/"))) + { + throw new ArgumentException(T("Admin.Media.Exception.DescendantFolder", destinationPath, path), nameof(destinationPath)); + } + + var node = _folderService.GetNodeByPath(path); + if (node == null) + { + throw _exceptionFactory.FolderNotFound(path); + } + + if (node.Value.IsAlbum) + { + throw new NotSupportedException(T("Admin.Media.Exception.CopyRootAlbum", node.Value.Name)); + } + + using (new DbContextScope(autoCommit: false, validateOnSave: false, autoDetectChanges: false)) + { + destinationPath += "/" + node.Value.Name; + var dupeFiles = new List(); + + // >>>> Do the heavy stuff + var folder = InternalCopyFolder(node, destinationPath, dupeEntryHandling, dupeFiles); + + var result = new FolderOperationResult + { + Operation = "copy", + DuplicateEntryHandling = dupeEntryHandling, + Folder = folder, + DuplicateFiles = dupeFiles + }; + + return result; + } + } + + private MediaFolderInfo InternalCopyFolder(TreeNode sourceNode, string destPath, DuplicateEntryHandling dupeEntryHandling, IList dupeFiles) + { + // Get dest node + var destNode = _folderService.GetNodeByPath(destPath); + + // Dupe handling + if (destNode != null && dupeEntryHandling == DuplicateEntryHandling.ThrowError) + { + throw _exceptionFactory.DuplicateFolder(sourceNode.Value.Path, destNode.Value); + } + + var doDupeCheck = destNode != null; + + // Create dest folder + if (destNode == null) + { + destNode = CreateFolder(destPath); + } + + var ctx = _fileRepo.Context; + + // INFO: we gonna change file name during the files loop later. + var destPathData = new MediaPathData(destNode, "placeholder.txt"); + + // Get all source files in one go + var files = _searcher.SearchFiles( + new MediaSearchQuery { FolderId = sourceNode.Value.Id }, + MediaLoadFlags.AsNoTracking | MediaLoadFlags.WithTags).Load(); + + IDictionary destFiles = null; + HashSet destNames = null; + + if (doDupeCheck) + { + // Get all files in destination folder for faster dupe selection + destFiles = _searcher.SearchFiles(new MediaSearchQuery { FolderId = destNode.Value.Id }, MediaLoadFlags.None).ToDictionarySafe(x => x.Name); + + // Make a HashSet from all file names in the destination folder for faster unique file name lookups + destNames = new HashSet(destFiles.Keys, StringComparer.CurrentCultureIgnoreCase); + } + + // Holds source and copy together, 'cause we perform a two-pass copy (file first, then data) + var tuples = new List<(MediaFile, MediaFile)>(500); + + // Copy files batched + foreach (var batch in files.Slice(500)) + { + foreach (var file in batch) + { + destPathData.FileName = file.Name; + + // >>> Do copy + var copy = InternalCopyFile( + file, + destPathData, + false /* copyData */, + dupeEntryHandling, + () => destFiles?.Get(file.Name), + UniqueFileNameChecker, + out var isDupe); + + if (copy != null) + { + if (isDupe) + { + dupeFiles.Add(new DuplicateFileInfo + { + SourceFile = ConvertMediaFile(file, sourceNode.Value), + DestinationFile = ConvertMediaFile(copy, destNode.Value), + UniquePath = destPathData.FullPath + }); + } + if (!isDupe || dupeEntryHandling != DuplicateEntryHandling.Skip) + { + // When dupe: add to processing queue only if file was NOT skipped + tuples.Add((file, copy)); + } + } + } + + // Save batch to DB (1st pass) + ctx.SaveChanges(); + + // Now copy file data + foreach (var op in tuples) + { + InternalCopyFileData(op.Item1, op.Item2); + } + + // Save batch to DB (2nd pass) + ctx.SaveChanges(); + + ctx.DetachEntities(); + ctx.DetachEntities(); + tuples.Clear(); + } + + // Copy folders + foreach (var node in sourceNode.Children) + { + destPath = destNode.Value.Path + "/" + node.Value.Name; + InternalCopyFolder(node, destPath, dupeEntryHandling, dupeFiles); + } + + return new MediaFolderInfo(destNode); + + void UniqueFileNameChecker(MediaPathData pathData) + { + if (destNames != null && _helper.CheckUniqueFileName(pathData.FileTitle, pathData.Extension, destNames, out var uniqueName)) + { + pathData.FileName = uniqueName; + } + } + } + + public FolderDeleteResult DeleteFolder(string path, FileHandling fileHandling = FileHandling.SoftDelete) + { + Guard.NotEmpty(path, nameof(path)); + + path = FolderService.NormalizePath(path); + ValidateFolderPath(path, "DeleteFolder", nameof(path)); + + var root = _folderService.GetNodeByPath(path); + if (root == null) + { + throw _exceptionFactory.FolderNotFound(path); + } + + // Collect all affected subfolders also + var allNodes = root.FlattenNodes(true).Reverse().ToArray(); + var result = new FolderDeleteResult(); + + using (new DbContextScope(autoDetectChanges: false)) + { + using (_folderService.BeginScope(true)) + { + // Delete all from DB + foreach (var node in allNodes) + { + var folder = _folderService.GetFolderById(node.Value.Id); + if (folder != null) + { + InternalDeleteFolder(folder, node, root, result, fileHandling); + } + } + } + } + + return result; + } + + private void InternalDeleteFolder( + MediaFolder folder, + TreeNode node, + TreeNode root, + FolderDeleteResult result, + FileHandling strategy) + { + // (perf) We gonna check file tracks, so we should preload all tracks. + _fileRepo.Context.LoadCollection(folder, (MediaFolder x) => x.Files, false, q => q.Include(f => f.Tracks)); + + var files = folder.Files.ToList(); + var lockedFiles = new List(files.Count); + var trackedFiles = new List(files.Count); + + // First delete files + if (folder.Files.Any()) + { + var albumId = strategy == FileHandling.MoveToRoot + ? _folderService.FindAlbum(folder.Id).Value.Id + : (int?)null; + + foreach (var batch in files.Slice(500)) + { + foreach (var file in batch) + { + if (strategy == FileHandling.Delete && file.Tracks.Any()) + { + // Don't delete tracked files + trackedFiles.Add(file); + continue; + } + + if (strategy == FileHandling.Delete) + { + try + { + result.DeletedFileNames.Add(file.Name); + DeleteFile(file, true); + } + catch (DeleteTrackedFileException) + { + trackedFiles.Add(file); + } + catch (IOException) + { + lockedFiles.Add(file); + } + } + else if (strategy == FileHandling.SoftDelete) + { + DeleteFile(file, false); + file.FolderId = null; + result.DeletedFileNames.Add(file.Name); + } + else if (strategy == FileHandling.MoveToRoot) + { + file.FolderId = albumId; + result.DeletedFileNames.Add(file.Name); + } + } + + _fileRepo.Context.SaveChanges(); + } + + if (lockedFiles.Any()) + { + // Retry deletion of failed files due to locking. + // INFO: By default "LocalFileSystem" waits for 500ms until the lock is revoked or it throws. + foreach (var lockedFile in lockedFiles.ToArray()) + { + try + { + DeleteFile(lockedFile, true); + lockedFiles.Remove(lockedFile); + } + catch { } + } + + _fileRepo.Context.SaveChanges(); + } + } + + if (lockedFiles.Count > 0) + { + var fullPath = CombinePaths(root.Value.Path, lockedFiles[0].Name); + throw new IOException(T("Admin.Media.Exception.InUse", fullPath)); + } + + if (lockedFiles.Count == 0 && trackedFiles.Count == 0 && node.Children.All(x => result.DeletedFolderIds.Contains(x.Value.Id))) + { + // Don't delete folder if a containing file could not be deleted, + // any tracked file was found or any of its child folders could not be deleted.. + _folderService.DeleteFolder(folder); + //_fileRepo.Context.SaveChanges(); + result.DeletedFolderIds.Add(folder.Id); + } + + result.LockedFileNames = lockedFiles.Select(x => x.Name).ToList(); + result.TrackedFileNames = trackedFiles.Select(x => x.Name).ToList(); + } + + #endregion + + #region Utils + + private void ValidateFolderPath(string path, string operation, string paramName) + { + if (!IsPath(path)) + { + // Destination cannot be an album + throw new ArgumentException(T("Admin.Media.Exception.PathSpecification", path, operation), paramName); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Media/MediaService.cs b/src/Libraries/SmartStore.Services/Media/MediaService.cs new file mode 100644 index 0000000000..dc6ef6ce92 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/MediaService.cs @@ -0,0 +1,1108 @@ +using System; +using System.Collections.Generic; +using System.Data.Entity; +using System.Data.Entity.Infrastructure; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.Events; +using SmartStore.Core.IO; +using SmartStore.Core.Localization; +using SmartStore.Core.Logging; +using SmartStore.Data.Caching; +using SmartStore.Services.Localization; +using SmartStore.Services.Media.Imaging; +using SmartStore.Services.Media.Storage; + +namespace SmartStore.Services.Media +{ + public partial class MediaService : IMediaService + { + private readonly IRepository _fileRepo; + private readonly IFolderService _folderService; + private readonly IMediaSearcher _searcher; + private readonly IMediaTypeResolver _typeResolver; + private readonly IMediaUrlGenerator _urlGenerator; + private readonly IEventPublisher _eventPublisher; + private readonly ILanguageService _languageService; + private readonly ILocalizedEntityService _localizedEntityService; + private readonly MediaSettings _mediaSettings; + private readonly IImageProcessor _imageProcessor; + private readonly IImageCache _imageCache; + private readonly MediaExceptionFactory _exceptionFactory; + private readonly IMediaStorageProvider _storageProvider; + private readonly MediaHelper _helper; + + public MediaService( + IRepository fileRepo, + IFolderService folderService, + IMediaSearcher searcher, + IMediaTypeResolver typeResolver, + IMediaUrlGenerator urlGenerator, + IEventPublisher eventPublisher, + ILanguageService languageService, + ILocalizedEntityService localizedEntityService, + MediaSettings mediaSettings, + IImageProcessor imageProcessor, + IImageCache imageCache, + MediaExceptionFactory exceptionFactory, + Func storageProvider, + MediaHelper helper) + { + _fileRepo = fileRepo; + _folderService = folderService; + _searcher = searcher; + _typeResolver = typeResolver; + _urlGenerator = urlGenerator; + _eventPublisher = eventPublisher; + _languageService = languageService; + _localizedEntityService = localizedEntityService; + _mediaSettings = mediaSettings; + _imageProcessor = imageProcessor; + _imageCache = imageCache; + _exceptionFactory = exceptionFactory; + _storageProvider = storageProvider(); + _helper = helper; + } + + public Localizer T { get; set; } = NullLocalizer.Instance; + public ILogger Logger { get; set; } = NullLogger.Instance; + + public IMediaStorageProvider StorageProvider => _storageProvider; + + public bool ImagePostProcessingEnabled { get; set; } = true; + + #region Query + + public int CountFiles(MediaSearchQuery query) + { + Guard.NotNull(query, nameof(query)); + + var q = _searcher.PrepareQuery(query, MediaLoadFlags.None); + var count = q.Count(); + return count; + } + + public Task CountFilesAsync(MediaSearchQuery query) + { + Guard.NotNull(query, nameof(query)); + + var q = _searcher.PrepareQuery(query, MediaLoadFlags.None); + return q.CountAsync(); + } + + public FileCountResult CountFilesGrouped(MediaFilesFilter filter) + { + Guard.NotNull(filter, nameof(filter)); + + // Base db query + var q = _searcher.ApplyFilterQuery(filter); + + // Get ids of untrackable folders, 'cause no orphan check can be made for them. + var untrackableFolderIds = _folderService.GetRootNode() + .SelectNodes(x => !x.Value.CanDetectTracks) + .Select(x => x.Value.Id) + .ToArray(); + + // Determine counts + var result = (from f in q + group f by 1 into g + select new FileCountResult + { + Total = g.Count(), + Trash = g.Count(x => x.Deleted), + Unassigned = g.Count(x => !x.Deleted && x.FolderId == null), + Transient = g.Count(x => !x.Deleted && x.IsTransient == true), + Orphan = g.Count(x => !x.Deleted && x.FolderId > 0 && !untrackableFolderIds.Contains(x.FolderId.Value) && !x.Tracks.Any()) + }).FirstOrDefault() ?? new FileCountResult(); + + if (result.Total == 0) + { + result.Folders = new Dictionary(); + return result; + } + + // Determine file count for each folder + var byFolders = from f in q + where f.FolderId > 0 && !f.Deleted + group f by f.FolderId.Value into grp + select grp; + + result.Folders = byFolders + .Select(grp => new { FolderId = grp.Key, Count = grp.Count() }) + .ToDictionary(k => k.FolderId, v => v.Count); + + result.Filter = filter; + + return result; + } + + public MediaSearchResult SearchFiles( + MediaSearchQuery query, + Func, IQueryable> queryModifier, + MediaLoadFlags flags = MediaLoadFlags.AsNoTracking) + { + Guard.NotNull(query, nameof(query)); + + var files = _searcher.SearchFiles(query, flags); + if (queryModifier != null) + { + files.AlterQuery(queryModifier); + } + + return new MediaSearchResult(files.Load(), ConvertMediaFile); + } + + public async Task SearchFilesAsync( + MediaSearchQuery query, + Func, IQueryable> queryModifier, + MediaLoadFlags flags = MediaLoadFlags.AsNoTracking) + { + Guard.NotNull(query, nameof(query)); + + var files = _searcher.SearchFiles(query, flags); + if (queryModifier != null) + { + files.AlterQuery(queryModifier); + } + + return new MediaSearchResult(await files.LoadAsync(), ConvertMediaFile); + } + + #endregion + + #region Read + + public bool FileExists(string path) + { + Guard.NotEmpty(path, nameof(path)); + + if (_helper.TokenizePath(path, false, out var tokens)) + { + return _fileRepo.Table.Any(x => x.FolderId == tokens.Folder.Id && x.Name == tokens.FileName); + } + + return false; + } + + public MediaFileInfo GetFileByPath(string path, MediaLoadFlags flags = MediaLoadFlags.None) + { + Guard.NotEmpty(path, nameof(path)); + + if (_helper.TokenizePath(path, false, out var tokens)) + { + var table = _searcher.ApplyLoadFlags(_fileRepo.Table, flags); + + var entity = table.FirstOrDefault(x => x.FolderId == tokens.Folder.Id && x.Name == tokens.FileName); + if (entity != null) + { + EnsureMetadataResolved(entity, true); + return ConvertMediaFile(entity, tokens.Folder); + } + } + + return null; + } + + public MediaFileInfo GetFileById(int id, MediaLoadFlags flags = MediaLoadFlags.None) + { + if (id <= 0) + return null; + + var query = _fileRepo.Table.Where(x => x.Id == id); + var entity = _searcher.ApplyLoadFlags(query, flags).FirstOrDefault(); + + if (entity != null) + { + EnsureMetadataResolved(entity, true); + return ConvertMediaFile(entity, _folderService.FindNode(entity)?.Value); + } + + return null; + } + + public MediaFileInfo GetFileByName(int folderId, string fileName, MediaLoadFlags flags = MediaLoadFlags.None) + { + Guard.IsPositive(folderId, nameof(folderId)); + Guard.NotEmpty(fileName, nameof(fileName)); + + var query = _fileRepo.Table.Where(x => x.Name == fileName && x.FolderId == folderId); + var entity = _searcher.ApplyLoadFlags(query, flags).FirstOrDefault(); + + if (entity != null) + { + EnsureMetadataResolved(entity, true); + var dir = _folderService.FindNode(entity)?.Value?.Path; + return ConvertMediaFile(entity, _folderService.FindNode(entity)?.Value); + } + + return null; + } + + public IList GetFilesByIds(int[] ids, MediaLoadFlags flags = MediaLoadFlags.AsNoTracking) + { + Guard.NotNull(ids, nameof(ids)); + + var query = _fileRepo.Table.Where(x => ids.Contains(x.Id)); + var result = _searcher.ApplyLoadFlags(query, flags).ToList(); + + return result.OrderBySequence(ids).Select(ConvertMediaFile).ToList(); + } + + public bool CheckUniqueFileName(string path, out string newPath) + { + Guard.NotEmpty(path, nameof(path)); + + // TODO: (mm) (mc) throw when path is not a file path + + newPath = null; + + if (!_helper.TokenizePath(path, false, out var pathData)) + { + return false; + } + + if (CheckUniqueFileName(pathData)) + { + newPath = pathData.FullPath; + } + + return newPath != null; + } + + protected internal virtual bool CheckUniqueFileName(MediaPathData pathData) + { + // (perf) First make fast check + var exists = _fileRepo.Table.Any(x => x.Name == pathData.FileName && x.FolderId == pathData.Folder.Id); + if (!exists) + { + return false; + } + + var q = new MediaSearchQuery + { + FolderId = pathData.Folder.Id, + Term = string.Concat(pathData.FileTitle, "*.", pathData.Extension), + Deleted = null + }; + + var query = _searcher.PrepareQuery(q, MediaLoadFlags.AsNoTracking).Select(x => x.Name); + var files = new HashSet(query.ToList(), StringComparer.CurrentCultureIgnoreCase); + + if (_helper.CheckUniqueFileName(pathData.FileTitle, pathData.Extension, files, out var uniqueName)) + { + pathData.FileName = uniqueName; + return true; + } + + return false; + } + + public string CombinePaths(params string[] paths) + { + return FolderService.NormalizePath(Path.Combine(paths), false); + } + + public bool FindEqualFile(Stream source, IEnumerable files, bool leaveOpen, out MediaFile equalFile) + { + Guard.NotNull(source, nameof(source)); + Guard.NotNull(files, nameof(files)); + + equalFile = null; + + try + { + foreach (var file in files) + { + source.Seek(0, SeekOrigin.Begin); + + using (var other = _storageProvider.OpenRead(file)) + { + if (source.ContentsEqual(other, true)) + { + equalFile = file; + return true; + } + } + } + + return false; + } + catch + { + return false; + } + finally + { + if (!leaveOpen) + { + source.Dispose(); + } + } + } + + #endregion + + #region Create/Update/Delete/Replace + + public MediaFileInfo ReplaceFile(MediaFile file, Stream inStream, string newFileName) + { + Guard.NotNull(file, nameof(file)); + Guard.NotNull(inStream, nameof(inStream)); + Guard.NotEmpty(newFileName, nameof(newFileName)); + + var fileInfo = ConvertMediaFile(file); + var pathData = CreatePathData(fileInfo.Path); + pathData.FileName = newFileName; + + var storageItem = ProcessFile(ref file, pathData, inStream, false, DuplicateFileHandling.Overwrite, MimeValidationType.MediaTypeMustMatch); + + using (var scope = new DbContextScope(_fileRepo.Context, autoCommit: false)) + { + try + { + _storageProvider.Save(file, storageItem); + scope.Commit(); + } + catch (Exception ex) + { + Logger.Error(ex); + } + } + + return fileInfo; + } + + public async Task ReplaceFileAsync(MediaFile file, Stream inStream, string newFileName) + { + Guard.NotNull(file, nameof(file)); + Guard.NotNull(inStream, nameof(inStream)); + Guard.NotEmpty(newFileName, nameof(newFileName)); + + var fileInfo = ConvertMediaFile(file); + var pathData = CreatePathData(fileInfo.Path); + pathData.FileName = newFileName; + + var storageItem = ProcessFile(ref file, pathData, inStream, false, DuplicateFileHandling.Overwrite, MimeValidationType.MediaTypeMustMatch); + + using (var scope = new DbContextScope(_fileRepo.Context, autoCommit: false)) + { + try + { + await _storageProvider.SaveAsync(file, storageItem); + await scope.CommitAsync(); + } + catch (Exception ex) + { + Logger.Error(ex); + } + } + + return fileInfo; + } + + public MediaFileInfo SaveFile(string path, Stream stream, bool isTransient = true, DuplicateFileHandling dupeFileHandling = DuplicateFileHandling.ThrowError) + { + var pathData = CreatePathData(path); + + var file = _fileRepo.Table.FirstOrDefault(x => x.Name == pathData.FileName && x.FolderId == pathData.Folder.Id); + var isNewFile = file == null; + var storageItem = ProcessFile(ref file, pathData, stream, isTransient, dupeFileHandling); + + using (var scope = new DbContextScope(_fileRepo.Context, autoCommit: false)) + { + if (file.Id == 0) + { + _fileRepo.Insert(file); + scope.Commit(); + } + + try + { + _storageProvider.Save(file, storageItem); + scope.Commit(); + } + catch (Exception ex) + { + if (isNewFile) + { + // New file's metadata should be removed on storage save failure immediately + DeleteFile(file, true, true); + scope.Commit(); + } + + Logger.Error(ex); + } + } + + return ConvertMediaFile(file, pathData.Folder); + } + + public async Task SaveFileAsync(string path, Stream stream, bool isTransient = true, DuplicateFileHandling dupeFileHandling = DuplicateFileHandling.ThrowError) + { + var pathData = CreatePathData(path); + + var file = await _fileRepo.Table.FirstOrDefaultAsync(x => x.Name == pathData.FileName && x.FolderId == pathData.Folder.Id); + var isNewFile = file == null; + var storageItem = ProcessFile(ref file, pathData, stream, isTransient, dupeFileHandling); + + using (var scope = new DbContextScope(_fileRepo.Context, autoCommit: false)) + { + if (file.Id == 0) + { + _fileRepo.Insert(file); + await scope.CommitAsync(); + } + + try + { + await _storageProvider.SaveAsync(file, storageItem); + await scope.CommitAsync(); + } + catch (Exception ex) + { + if (isNewFile) + { + // New file's metadata should be removed on storage save failure immediately + DeleteFile(file, true, true); + await scope.CommitAsync(); + } + + Logger.Error(ex); + } + } + + return ConvertMediaFile(file, pathData.Folder); + } + + public void DeleteFile(MediaFile file, bool permanent, bool force = false) + { + Guard.NotNull(file, nameof(file)); + + // Delete thumb + _imageCache.Delete(file); + + if (!permanent) + { + file.Deleted = true; + _fileRepo.Update(file); + } + else + { + try + { + if (!force && file.Tracks.Any()) + { + throw _exceptionFactory.DeleteTrackedFile(file, null); + } + + // Delete entity + _fileRepo.Delete(file); + + // Delete from storage + _storageProvider.Remove(file); + } + catch (DbUpdateException ex) + { + if (ex.IsUniquenessViolationException()) + { + throw _exceptionFactory.DeleteTrackedFile(file, ex); + } + else + { + throw; + } + } + catch (Exception) + { + throw; + } + } + } + + protected MediaPathData CreatePathData(string path) + { + Guard.NotEmpty(path, nameof(path)); + + if (!_helper.TokenizePath(path, true, out var pathData)) + { + throw new ArgumentException(T("Admin.Media.Exception.InvalidPathExample", path), nameof(path)); + } + + if (pathData.Extension.IsEmpty()) + { + throw new ArgumentException(T("Admin.Media.Exception.FileExtension", path), nameof(path)); + } + + return pathData; + } + + protected MediaStorageItem ProcessFile( + ref MediaFile file, + MediaPathData pathData, + Stream inStream, + bool isTransient = true, + DuplicateFileHandling dupeFileHandling = DuplicateFileHandling.ThrowError, + MimeValidationType mediaValidationType = MimeValidationType.MimeTypeMustMatch) + { + if (file != null) + { + if (dupeFileHandling == DuplicateFileHandling.ThrowError) + { + var fullPath = pathData.FullPath; + CheckUniqueFileName(pathData); + throw _exceptionFactory.DuplicateFile(fullPath, ConvertMediaFile(file, pathData.Folder), pathData.FullPath); + } + else if (dupeFileHandling == DuplicateFileHandling.Rename) + { + if (CheckUniqueFileName(pathData)) + { + file = null; + } + } + } + + if (file != null && mediaValidationType != MimeValidationType.NoValidation) + { + if (mediaValidationType == MimeValidationType.MimeTypeMustMatch) + { + ValidateMimeTypes("Save", file.MimeType, pathData.MimeType); + } + else if (mediaValidationType == MimeValidationType.MediaTypeMustMatch) + { + ValidateMediaTypes("Save", _typeResolver.Resolve(pathData.Extension), file.MediaType); + } + + // Restore file if soft-deleted + file.Deleted = false; + + // Delete thumbnail + _imageCache.Delete(file); + } + + file = file ?? new MediaFile + { + IsTransient = isTransient, + FolderId = pathData.Node.Value.Id + }; + + // Untrackable folders can never contain transient files. + if (!pathData.Folder.CanDetectTracks) + { + file.IsTransient = false; + } + + var name = pathData.FileName; + if (name != pathData.FileName) + { + pathData.FileName = name; + } + + file.Name = pathData.FileName; + file.Extension = pathData.Extension; + file.MimeType = pathData.MimeType; + if (file.MediaType == null) + { + file.MediaType = _typeResolver.Resolve(pathData.Extension, pathData.MimeType); + } + + // Process image + if (inStream != null && inStream.Length > 0 && file.MediaType == MediaType.Image && ProcessImage(file, inStream, out var outImage)) + { + file.Width = outImage.Size.Width; + file.Height = outImage.Size.Height; + file.PixelSize = outImage.Size.Width * outImage.Size.Height; + + return MediaStorageItem.FromImage(outImage); + } + else + { + file.RefreshMetadata(inStream); + + return MediaStorageItem.FromStream(inStream); + } + } + + protected bool ProcessImage(MediaFile file, Stream inStream, out IImage outImage) + { + outImage = null; + + var originalSize = Size.Empty; + var format = _imageProcessor.Factory.GetImageFormat(file.Extension) ?? new UnsupportedImageFormat(file.MimeType, file.Extension); + + try + { + originalSize = ImageHeader.GetDimensions(inStream, file.MimeType); + } + catch { } + + if (format is UnsupportedImageFormat) + { + outImage = new ImageWrapper(inStream, originalSize, format); + return true; + } + + var maxSize = _mediaSettings.MaximumImageSize; + + var query = new ProcessImageQuery(inStream) + { + Quality = _mediaSettings.DefaultImageQuality, + Format = file.Extension, + DisposeSource = true, + ExecutePostProcessor = ImagePostProcessingEnabled, + IsValidationMode = true + }; + + if (originalSize.IsEmpty || (originalSize.Height <= maxSize && originalSize.Width <= maxSize)) + { + // Give subscribers the chance to (pre)-process + var evt = new ImageUploadedEvent(query, originalSize); + _eventPublisher.Publish(evt); + outImage = evt.ResultImage ?? new ImageWrapper(inStream, originalSize, format); + + return true; + } + + query.MaxSize = maxSize; + + using (var result = _imageProcessor.ProcessImage(query, false)) + { + outImage = result.Image; + return true; + } + } + + #endregion + + #region Copy & Move + + public FileOperationResult CopyFile(MediaFileInfo mediaFile, string destinationFileName, DuplicateFileHandling dupeFileHandling = DuplicateFileHandling.ThrowError) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + Guard.NotEmpty(destinationFileName, nameof(destinationFileName)); + + var destPathData = CreateDestinationPathData(mediaFile.File, destinationFileName); + var destFileName = destPathData.FileName; + var destFolderId = destPathData.Folder.Id; + + var dupe = mediaFile.FolderId == destPathData.Folder.Id + // Source folder equals dest folder, so same file + ? mediaFile.File + // Another dest folder, check for duplicate by file name + : _fileRepo.Table.FirstOrDefault(x => x.Name == destFileName && x.FolderId == destFolderId); + + var copy = InternalCopyFile( + mediaFile.File, + destPathData, + true /* copyData */, + (DuplicateEntryHandling)((int)dupeFileHandling), + () => dupe, + p => CheckUniqueFileName(p), + out var isDupe); + + return new FileOperationResult + { + Operation = "copy", + DuplicateFileHandling = dupeFileHandling, + SourceFile = mediaFile, + DestinationFile = ConvertMediaFile(copy, destPathData.Folder), + IsDuplicate = isDupe, + UniquePath = isDupe ? destPathData.FullPath : (string)null + }; + } + + private MediaFile InternalCopyFile( + MediaFile file, + MediaPathData destPathData, + bool copyData, + DuplicateEntryHandling dupeEntryHandling, + Func dupeFileSelector, + Action uniqueFileNameChecker, + out bool isDupe) + { + // Find dupe and handle + isDupe = false; + var dupe = dupeFileSelector(); + if (dupe != null) + { + switch (dupeEntryHandling) + { + case DuplicateEntryHandling.Skip: + isDupe = true; + uniqueFileNameChecker(destPathData); + return dupe; + case DuplicateEntryHandling.ThrowError: + var fullPath = destPathData.FullPath; + uniqueFileNameChecker(destPathData); + throw _exceptionFactory.DuplicateFile(fullPath, ConvertMediaFile(dupe), destPathData.FullPath); + case DuplicateEntryHandling.Rename: + uniqueFileNameChecker(destPathData); + dupe = null; + break; + case DuplicateEntryHandling.Overwrite: + if (file.FolderId == destPathData.Folder.Id) + { + throw new IOException(T("Admin.Media.Exception.Overwrite")); + } + break; + } + } + + isDupe = dupe != null; + var copy = dupe ?? new MediaFile(); + + // Simple clone + MapMediaFile(file, copy); + + // Set folder id + copy.FolderId = destPathData.Folder.Id; + + // A copied file cannot stay in deleted state + copy.Deleted = false; + + // Set name stuff + if (!copy.Name.IsCaseInsensitiveEqual(destPathData.FileName)) + { + copy.Name = destPathData.FileName; + copy.Extension = destPathData.Extension; + copy.MimeType = destPathData.MimeType; + } + + // Save to DB + if (isDupe) + { + _fileRepo.Update(copy); + } + else + { + _fileRepo.Insert(copy); + } + + // Copy data: blob, alt, title etc. + if (copyData) + { + InternalCopyFileData(file, copy); + } + + return copy; + } + + private void InternalCopyFileData(MediaFile file, MediaFile copy) + { + _storageProvider.Save(copy, MediaStorageItem.FromStream(_storageProvider.OpenRead(file))); + _imageCache.Delete(copy); + + using (var scope = new DbContextScope(_fileRepo.Context, autoCommit: false)) + { + // Tags. + _fileRepo.Context.LoadCollection(file, (MediaFile x) => x.Tags); + + var existingTagsIds = copy.Tags.Select(x => x.Id).ToList(); + + foreach (var tag in file.Tags) + { + if (!existingTagsIds.Contains(tag.Id)) + { + copy.Tags.Add(tag); + existingTagsIds.Add(tag.Id); + } + } + + // Localized values. + var languages = _languageService.GetAllLanguages(true); + + foreach (var language in languages) + { + var title = file.GetLocalized(x => x.Title, language.Id, false, false).Value; + if (title.HasValue()) + { + _localizedEntityService.SaveLocalizedValue(copy, x => x.Title, title, language.Id); + } + + var alt = file.GetLocalized(x => x.Alt, language.Id, false, false).Value; + if (alt.HasValue()) + { + _localizedEntityService.SaveLocalizedValue(copy, x => x.Alt, alt, language.Id); + } + } + + scope.Commit(); + _fileRepo.Context.DetachEntities(); + } + } + + public MediaFileInfo MoveFile(MediaFile file, string destinationFileName, DuplicateFileHandling dupeFileHandling = DuplicateFileHandling.ThrowError) + { + if (ValidateMoveOperation(file, destinationFileName, dupeFileHandling, out var nameChanged, out var destPathData)) + { + file.FolderId = destPathData.Folder.Id; + + // A moved file cannot stay in deleted state + file.Deleted = false; + + if (nameChanged) + { + var title = destPathData.FileTitle; + var ext = destPathData.Extension.ToLower(); + + file.Name = title + "." + ext; + + if (ext != file.Extension.ToLower()) + { + _storageProvider.ChangeExtension(file, ext); + file.Extension = ext; + } + + _imageCache.Delete(file); + } + + _fileRepo.Update(file); + } + + return ConvertMediaFile(file, destPathData.Folder); + } + + private bool ValidateMoveOperation( + MediaFile file, + string destinationFileName, + DuplicateFileHandling dupeFileHandling, + out bool nameChanged, + out MediaPathData destPathData) + { + Guard.NotNull(file, nameof(file)); + Guard.NotEmpty(destinationFileName, nameof(destinationFileName)); + + destPathData = CreateDestinationPathData(file, destinationFileName); + + var destFileName = destPathData.FileName; + var destFolderId = destPathData.Folder.Id; + var folderChanged = destFolderId != file.FolderId; + var shouldRestore = false; + + nameChanged = !destFileName.IsCaseInsensitiveEqual(file.Name); + + if (file.FolderId.HasValue && folderChanged) + { + // When "Move" operation: ensure file stays in source album. + ValidateAlbums("Move", file.FolderId.Value, destFolderId); + } + + if (nameChanged) + { + // Ensure both MIME types are equal + ValidateMimeTypes("Move", file.MimeType, destPathData.MimeType); + } + + // Check whether destination file exists + if (!folderChanged && file.Deleted) + { + // Special case where a file is moved from trash to its origin location. + // In this case the file should just be restored without any dupe check. + shouldRestore = true; + } + else + { + var dupe = _fileRepo.Table.FirstOrDefault(x => x.Name == destFileName && x.FolderId == destFolderId); + if (dupe != null) + { + if (!folderChanged) + { + throw _exceptionFactory.IdenticalPaths(ConvertMediaFile(file, destPathData.Folder)); + } + + switch (dupeFileHandling) + { + case DuplicateFileHandling.ThrowError: + var fullPath = destPathData.FullPath; + _helper.CheckUniqueFileName(destPathData.FileTitle, destPathData.Extension, dupe.Name, out _); + throw _exceptionFactory.DuplicateFile(fullPath, ConvertMediaFile(dupe, destPathData.Folder), destPathData.FullPath); + case DuplicateFileHandling.Rename: + if (_helper.CheckUniqueFileName(destPathData.FileTitle, destPathData.Extension, dupe.Name, out var uniqueName)) + { + nameChanged = true; + destPathData.FileName = uniqueName; + return true; + } + break; + case DuplicateFileHandling.Overwrite: + DeleteFile(dupe, true); + break; + } + } + } + + return folderChanged || nameChanged || shouldRestore; + } + + #endregion + + #region URL generation + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public string GetUrl(MediaFileInfo file, ProcessImageQuery imageQuery, string host = null, bool doFallback = true) + { + return _urlGenerator.GenerateUrl(file, imageQuery, host, doFallback); + } + + #endregion + + #region Utils + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MediaFileInfo ConvertMediaFile(MediaFile file) + { + return ConvertMediaFile(file, _folderService.FindNode(file)?.Value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected MediaFileInfo ConvertMediaFile(MediaFile file, MediaFolderNode folder) + { + var mediaFile = new MediaFileInfo(file, _storageProvider, _urlGenerator, folder?.Path) + { + ThumbSize = _mediaSettings.ProductThumbPictureSize + }; + + return mediaFile; + } + + private void EnsureMetadataResolved(MediaFile file, bool saveOnResolve) + { + var mediaType = _typeResolver.Resolve(file.Extension, file.MimeType); + + var resolveDimensions = mediaType == MediaType.Image && file.Width == null && file.Height == null; + var resolveSize = file.Size <= 0; + + Stream stream = null; + + if (resolveDimensions || resolveSize) + { + stream = _storageProvider.OpenRead(file); + } + + // Resolve image dimensions + if (stream != null) + { + try + { + if (resolveSize) + { + file.Size = (int)stream.Length; + } + + if (resolveDimensions) + { + var size = ImageHeader.GetDimensions(stream, file.MimeType, true); + file.Width = size.Width; + file.Height = size.Height; + file.PixelSize = size.Width * size.Height; + } + + if (saveOnResolve) + { + try + { + _fileRepo.Update(file); + } + catch (InvalidOperationException ioe) + { + // Ignore exception for pictures that already have been processed. + if (!ioe.IsAlreadyAttachedEntityException()) + { + throw; + } + } + } + } + finally + { + stream.Dispose(); + } + } + } + + private static void MapMediaFile(MediaFile from, MediaFile to) + { + to.Alt = from.Alt; + to.Deleted = from.Deleted; + to.Extension = from.Extension; + to.FolderId = from.FolderId; + to.Height = from.Height; + to.Hidden = from.Hidden; + to.IsTransient = from.IsTransient; // TBD: (mm) really? + to.MediaType = from.MediaType; + to.Metadata = from.Metadata; + to.MimeType = from.MimeType; + to.Name = from.Name; + to.PixelSize = from.PixelSize; + to.Size = from.Size; + to.Title = from.Title; + to.Version = from.Version; + to.Width = from.Width; + } + + private MediaPathData CreateDestinationPathData(MediaFile file, string destinationFileName) + { + if (!_helper.TokenizePath(destinationFileName, true, out var pathData)) + { + // Passed path is NOT a path, but a file name + + if (IsPath(destinationFileName)) + { + // ...but file name includes path chars, which is not allowed + throw new ArgumentException( + T("Admin.Media.Exception.InvalidPath", Path.GetDirectoryName(destinationFileName)), + nameof(destinationFileName)); + } + + if (file.FolderId == null) + { + throw new NotSupportedException(T("Admin.Media.Exception.FolderAssignment")); + } + + pathData = new MediaPathData(_folderService.GetNodeById(file.FolderId.Value), destinationFileName); + } + + return pathData; + } + + private void ValidateMimeTypes(string operation, string mime1, string mime2) + { + if (!mime1.Equals(mime2, StringComparison.OrdinalIgnoreCase)) + { + // TODO: (mm) Create this and all other generic exceptions by MediaExceptionFactory + throw new NotSupportedException(T("Admin.Media.Exception.MimeType", operation, mime1, mime2)); + } + } + + private void ValidateMediaTypes(string operation, string mime1, string mime2) + { + if (mime1 != mime2) + { + // TODO: (mm) Create this and all other generic exceptions by MediaExceptionFactory + throw new NotSupportedException(T("Admin.Media.Exception.MediaType", operation, mime1, mime2)); + } + } + + private void ValidateAlbums(string operation, int folderId1, int folderId2) + { + if (!_folderService.AreInSameAlbum(folderId1, folderId2)) + { + throw _exceptionFactory.NotSameAlbum( + _folderService.GetNodeById(folderId1).Value.Path, + _folderService.GetNodeById(folderId2).Value.Path); + } + } + + private static bool IsPath(string path) + { + return path.IndexOfAny(new[] { '/', '\\' }) > -1; + } + + #endregion + } +} diff --git a/src/Libraries/SmartStore.Services/Media/MediaServiceFileSystemAdapter.cs b/src/Libraries/SmartStore.Services/Media/MediaServiceFileSystemAdapter.cs new file mode 100644 index 0000000000..c19f699cc1 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/MediaServiceFileSystemAdapter.cs @@ -0,0 +1,298 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SmartStore.Collections; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.IO; +using SmartStore.Services.Media.Storage; + +namespace SmartStore.Services.Media +{ + public partial class MediaServiceFileSystemAdapter : IMediaFileSystem + { + private readonly IMediaService _mediaService; + private readonly MediaHelper _mediaHelper; + private readonly IFolderService _folderService; + private readonly IMediaStorageProvider _storageProvider; + private readonly MediaExceptionFactory _exceptionFactory; + private readonly string _mediaRootPath; + + public MediaServiceFileSystemAdapter( + IMediaService mediaService, + IFolderService folderService, + MediaHelper mediaHelper, + MediaExceptionFactory exceptionFactory) + { + _mediaService = mediaService; + _folderService = folderService; + _mediaHelper = mediaHelper; + _storageProvider = mediaService.StorageProvider; + _exceptionFactory = exceptionFactory; + _mediaRootPath = MediaFileSystem.GetMediaPublicPath(); + } + + protected string Fix(string path) + { + return path.Replace('\\', '/'); + } + + #region IFileSystem + + public bool IsCloudStorage => _storageProvider.IsCloudStorage; + + public string Root => string.Empty; + + public string GetPublicUrl(IFile file, bool forCloud = false) + { + if (file is MediaFileInfo mediaFile) + { + return _mediaService.GetUrl(mediaFile, null, string.Empty); + } + + throw new ArgumentException("Type of file must be '{0}'.".FormatInvariant(typeof(MediaFileInfo).FullName), nameof(file)); + } + + public string GetPublicUrl(string path, bool forCloud = false) + { + throw new NotSupportedException(); + } + + public string GetStoragePath(string url) + { + url = Fix(url).TrimStart('/'); + + if (!url.StartsWith(_mediaRootPath, StringComparison.OrdinalIgnoreCase)) + { + // Is a folder path, no need to strip off public URL stuff. + return url; + } + + // Strip off root, e.g. "media/" + var path = url.Substring(_mediaRootPath.Length); + + // Strip off media id from path, e.g. "123/" + var firstSlashIndex = path.IndexOf('/'); + + return path.Substring(firstSlashIndex); + } + + public string Combine(string path1, string path2) + { + return Fix(Path.Combine(path1, path2)); + } + + public IEnumerable ListFiles(string path) + { + var node = _folderService.GetNodeByPath(path); + if (node == null) + { + throw _exceptionFactory.FolderNotFound(path); + } + + var query = new MediaSearchQuery + { + FolderId = node.Value.Id + }; + + return _mediaService.SearchFiles(query); + } + + public IEnumerable ListFolders(string path) + { + var node = _folderService.GetNodeByPath(path); + if (node == null) + { + throw _exceptionFactory.FolderNotFound(path); + } + + return node.Children.Select(x => new MediaFolderInfo(x)); + } + + public long CountFiles(string path, string pattern, Func predicate, bool deep = true) + { + if (predicate == null) + { + var node = _folderService.GetNodeByPath(path); + if (node == null) + { + throw _exceptionFactory.FolderNotFound(path); + } + + var query = new MediaSearchQuery + { + FolderId = node.Value.Id, + DeepSearch = deep, + Term = pattern + }; + + return _mediaService.CountFiles(query); + } + + var files = SearchFiles(path, pattern, deep); + return files.Count(predicate); + } + + public IEnumerable SearchFiles(string path, string pattern, bool deep = true) + { + var node = _folderService.GetNodeByPath(path); + if (node == null) + { + throw _exceptionFactory.FolderNotFound(path); + } + + var query = new MediaSearchQuery + { + FolderId = node.Value.Id, + DeepSearch = deep, + Term = pattern + }; + + return _mediaService.SearchFiles(query).Select(x => x.Path).ToList(); + } + + public IFile CreateFile(string path) + { + return _mediaService.SaveFile(path, null, false, DuplicateFileHandling.ThrowError); + } + + public async Task CreateFileAsync(string path) + { + return await _mediaService.SaveFileAsync(path, null, false, DuplicateFileHandling.ThrowError); + } + + public IFolder CreateFolder(string path) + { + return _mediaService.CreateFolder(path); + } + + public void DeleteFile(string path) + { + var file = _mediaService.GetFileByPath(path); + if (file?.Exists == true) + { + _mediaService.DeleteFile((MediaFile)file, true); + } + } + + public void DeleteFolder(string path) + { + _mediaService.DeleteFolder(path, FileHandling.Delete); + } + + public bool FileExists(string path) + { + return _mediaService.FileExists(path); + } + + public bool FolderExists(string path) + { + return _mediaService.FolderExists(path); + } + + public IFile GetFile(string path) + { + var file = _mediaService.GetFileByPath(path); + if (file == null) + { + var mediaFile = new MediaFile + { + Name = Path.GetFileName(path), + Extension = Path.GetExtension(path).TrimStart('.'), + MimeType = MimeTypes.MapNameToMimeType(path), + FolderId = _folderService.GetNodeByPath(Fix(Path.GetDirectoryName(path)))?.Value.Id + }; + + file = _mediaService.ConvertMediaFile(mediaFile); + } + + return file; + } + + public IFolder GetFolder(string path) + { + var node = _folderService.GetNodeByPath(path); + if (node == null) + { + node = new TreeNode(new MediaFolderNode { Path = path, Name = Path.GetFileName(Fix(path)) }); + } + + return new MediaFolderInfo(node); + } + + public IFolder GetFolderForFile(string path) + { + if (!_mediaHelper.TokenizePath(path, false, out var pathData)) + { + throw _exceptionFactory.FolderNotFound(Fix(Path.GetDirectoryName(path))); + } + + return new MediaFolderInfo(pathData.Node); + } + + public bool CheckUniqueFileName(string path, out string newPath) + { + return _mediaService.CheckUniqueFileName(path, out newPath); + } + + public void CopyFile(string path, string newPath, bool overwrite = false) + { + Guard.NotEmpty(path, nameof(path)); + Guard.NotEmpty(newPath, nameof(newPath)); + + var sourceFile = _mediaService.GetFileByPath(path); + if (sourceFile == null) + { + throw _exceptionFactory.FileNotFound(path); + } + + _mediaService.CopyFile( + sourceFile, + newPath, + overwrite ? DuplicateFileHandling.Overwrite : DuplicateFileHandling.ThrowError); + } + + public void RenameFile(string path, string newPath) + { + Guard.NotEmpty(path, nameof(path)); + Guard.NotEmpty(newPath, nameof(newPath)); + + var sourceFile = (MediaFile)_mediaService.GetFileByPath(path); + if (sourceFile == null) + { + throw _exceptionFactory.FileNotFound(path); + } + + _mediaService.MoveFile(sourceFile, newPath); + } + + public void RenameFolder(string path, string newPath) + { + _mediaService.MoveFolder(path, newPath); + } + + public void CopyFolder(string path, string destinationPath, bool overwrite = true) + { + var dupeEntryHandling = overwrite + ? DuplicateEntryHandling.Overwrite + : DuplicateEntryHandling.ThrowError; + + _mediaService.CopyFolder(path, destinationPath, dupeEntryHandling); + } + + public void SaveStream(string path, Stream inputStream) + { + _mediaService.SaveFile(path, inputStream, false, DuplicateFileHandling.Overwrite); + } + + public async Task SaveStreamAsync(string path, Stream inputStream) + { + await _mediaService.SaveFileAsync(path, inputStream, false, DuplicateFileHandling.Overwrite); + } + + #endregion + } +} diff --git a/src/Libraries/SmartStore.Services/Media/MediaTypes/IMediaTypeResolver.cs b/src/Libraries/SmartStore.Services/Media/MediaTypes/IMediaTypeResolver.cs new file mode 100644 index 0000000000..eab1084e47 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/MediaTypes/IMediaTypeResolver.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.IO; + +namespace SmartStore.Services.Media +{ + public interface IMediaTypeResolver + { + /// + /// Resolves the conceptual media type for a given file extension or mime type. + /// + /// The file extension (with dot). + /// Used to map to an extension if parameter is null or empty. + /// The media type or as fallback. + MediaType Resolve(string extension, string mimeType = null); + + /// + /// Parses and expands a given type filter, returning a distinct list of all suitable file extensions. + /// If is empty or '*', all supported file extensions are returned. + /// + /// A comma separated list of either file extensions and/or media type names, e.g.: "image,.mp4,audio,.pdf". Extensions must start with a dot. + /// All suitable file extensions. + IEnumerable ParseTypeFilter(string typeFilter); + + /// + /// Parses and expands a given type filter, returning a distinct list of all suitable file extensions. + /// + /// A list of either file extensions and/or media type names, e.g.: [ "image", ".mp4", "audio", ".pdf" ]. Extensions must start with a dot. + /// All suitable file extensions. + IEnumerable ParseTypeFilter(string[] typeFilter); + + /// + /// Get the cached "file extension to media type name" map. + /// "Key" is the dot-less file extension and "Value" is the corresponding media type name. + /// + /// The cached dictionary + IReadOnlyDictionary GetExtensionMediaTypeMap(); + } + + public static class IMediaTypeResolverExtensions + { + public static MediaType Resolve(this IMediaTypeResolver resolver, MediaFile file) + { + Guard.NotNull(file, nameof(file)); + return resolver.Resolve(file.Extension, file.MimeType); + } + + //public static MediaType Resolve(this IMediaTypeResolver resolver, IFile file) + //{ + // Guard.NotNull(file, nameof(file)); + // return resolver.Resolve(file.Extension?.TrimStart('.')); + //} + } +} diff --git a/src/Libraries/SmartStore.Services/Media/MediaTypes/MediaType.cs b/src/Libraries/SmartStore.Services/Media/MediaTypes/MediaType.cs new file mode 100644 index 0000000000..ed885caf94 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/MediaTypes/MediaType.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace SmartStore.Services.Media +{ + public class MediaType : IEquatable + { + private readonly static IDictionary _defaultExtensionsMap = new Dictionary + { + ["image"] = new[] { "png", "jpg", "jpeg", "jfif", "gif", "webp", "bmp", "svg", "ico" }, + ["video"] = new[] { "mp4", "m4v", "mkv", "wmv", "avi", "asf", "mpg", "mpeg", "webm", "flv", "ogv", "mov", "3gp" }, + ["audio"] = new[] { "mp3", "wav", "wma", "aac", "flac", "oga", "wav", "m4a", "ogg" }, + ["document"] = new[] { "pdf", "doc", "docx", "ppt", "pptx", "pps", "ppsx", "docm", "odt", "ods", "dot", "dotx", "dotm", "psd", "xls", "xlsx", "rtf" }, + ["text"] = new[] { "txt", "xml", "csv", "htm", "html", "json", "css", "js" }, + ["bin"] = new string[0] + }; + + private readonly static IDictionary _map = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public readonly static MediaType Image = new MediaType("image", _defaultExtensionsMap["image"]); + public readonly static MediaType Video = new MediaType("video", _defaultExtensionsMap["video"]); + public readonly static MediaType Audio = new MediaType("audio", _defaultExtensionsMap["audio"]); + public readonly static MediaType Document = new MediaType("document", _defaultExtensionsMap["document"]); + public readonly static MediaType Text = new MediaType("text", _defaultExtensionsMap["text"]); + public readonly static MediaType Binary = new MediaType("bin", _defaultExtensionsMap["bin"]); + + protected MediaType(string name, params string[] defaultExtensions) + { + Guard.NotEmpty(name, nameof(name)); + + Name = name; + DefaultExtensions = defaultExtensions.OrderBy(x => x).ToArray(); + + _map[name] = this; + } + + public string Name { get; private set; } + + public string[] DefaultExtensions { get; private set; } + + public static IEnumerable AllExtensions => _defaultExtensionsMap.SelectMany(x => x.Value); + + public override string ToString() + { + return Name; + } + + public static implicit operator string(MediaType obj) + { + return obj?.Name; + } + + public static implicit operator MediaType(string obj) + { + return GetMediaType(obj); + } + + internal static MediaType GetMediaType(string name) + { + if (name.IsEmpty()) + { + return null; + } + + if (_map.TryGetValue(name, out var instance)) + { + return instance; + } + + return null; + } + + public override bool Equals(object obj) + { + if (obj is null) + return false; + + if (ReferenceEquals(this, obj)) + return true; + + return obj.GetType() == GetType() && Equals((MediaType)obj); + } + + public bool Equals(MediaType other) + { + return string.Equals(Name, other.Name); + } + + public override int GetHashCode() + { + return Name?.GetHashCode() ?? 0; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/MediaTypes/MediaTypeResolver.cs b/src/Libraries/SmartStore.Services/Media/MediaTypes/MediaTypeResolver.cs new file mode 100644 index 0000000000..04cfe4640a --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/MediaTypes/MediaTypeResolver.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SmartStore.Core.Caching; +using SmartStore.Core.Data.Hooks; +using SmartStore.Core.Domain.Configuration; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.IO; +using SmartStore.Utilities; + +namespace SmartStore.Services.Media +{ + public partial class MediaTypeResolver : DbSaveHook, IMediaTypeResolver + { + const string MapCacheKey = "media:exttypemap"; + + private readonly ICacheManager _cache; + private readonly Lazy _mediaSettings; + + private static HashSet _mapInvalidatorSettingKeys = new HashSet(StringComparer.OrdinalIgnoreCase) + { + TypeHelper.NameOf(x => x.ImageTypes, true), + TypeHelper.NameOf(x => x.VideoTypes, true), + TypeHelper.NameOf(x => x.AudioTypes, true), + TypeHelper.NameOf(x => x.DocumentTypes, true), + TypeHelper.NameOf(x => x.TextTypes, true), + TypeHelper.NameOf(x => x.BinTypes, true) + }; + + public MediaTypeResolver(ICacheManager cache, Lazy mediaSettings) + { + _cache = cache; + _mediaSettings = mediaSettings; + } + + public MediaType Resolve(string extension, string mimeType = null) + { + if (extension.IsEmpty() && mimeType.HasValue()) + { + extension = MimeTypes.MapMimeTypeToExtension(mimeType); + } + + var map = GetExtensionMediaTypeMap(); + + string mediaType = null; + if (extension.HasValue() && map.TryGetValue(extension.TrimStart('.').ToLower(), out mediaType)) + { + return (MediaType)mediaType; + } + + if (mimeType.HasValue()) + { + // Get first mime token (e.g. IMAGE/png, VIDEO/mp4 etc.) + var mimeGroup = mimeType.Split('/')[0]; + mediaType = MediaType.GetMediaType(mimeGroup); + } + + return (MediaType)mediaType ?? MediaType.Binary; + } + + public IEnumerable ParseTypeFilter(string typeFilter) + { + if (typeFilter.IsEmpty() || typeFilter == "*") + { + return GetExtensionMediaTypeMap().Keys; + } + else + { + return ParseTypeFilter(typeFilter.SplitSafe(",")); + } + } + + public IEnumerable ParseTypeFilter(string[] typeFilter) + { + if (typeFilter == null || typeFilter.Length == 0) + return Enumerable.Empty(); + + var extensions = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (var filter in typeFilter.Select(x => x.Trim())) + { + if (filter[0] == '.') + { + extensions.Add(filter.Substring(1)); + } + else + { + extensions.AddRange(GetExtensionMediaTypeMap().Where(x => filter == "*" || x.Value == filter).Select(x => x.Key)); + } + } + + return extensions; + } + + public IReadOnlyDictionary GetExtensionMediaTypeMap() + { + return _cache.Get(MapCacheKey, () => + { + var mediaSettings = _mediaSettings.Value; + var map = new Dictionary(); + + AddExtensionsToMap(mediaSettings.ImageTypes, MediaType.Image); + AddExtensionsToMap(mediaSettings.VideoTypes, MediaType.Video); + AddExtensionsToMap(mediaSettings.AudioTypes, MediaType.Audio); + AddExtensionsToMap(mediaSettings.DocumentTypes, MediaType.Document); + AddExtensionsToMap(mediaSettings.TextTypes, MediaType.Text); + AddExtensionsToMap(mediaSettings.BinTypes, MediaType.Binary); + + return map; + + void AddExtensionsToMap(string extensions, MediaType forType) + { + var arr = extensions.EmptyNull() + .Replace(Environment.NewLine, " ") + .ToLower() + .Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries); + + if (arr.Length == 0) + { + arr = forType.DefaultExtensions; + } + + arr.Each(x => map[x.Trim()] = forType.Name); + } + }); + } + + #region Invalidation Hook + + public override void OnAfterSave(IHookedEntity entry) + { + var setting = entry.Entity as Setting; + if (_mapInvalidatorSettingKeys.Contains(setting.Name)) + { + _cache.Remove(MapCacheKey); + } + } + + #endregion + } +} diff --git a/src/Libraries/SmartStore.Services/Media/MediaUrlGenerator.cs b/src/Libraries/SmartStore.Services/Media/MediaUrlGenerator.cs new file mode 100644 index 0000000000..21f31b28c4 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/MediaUrlGenerator.cs @@ -0,0 +1,121 @@ +using System.Globalization; +using System.Web; +using System.Web.Hosting; +using SmartStore.Core; +using SmartStore.Core.Domain.Media; +using SmartStore.Services.Configuration; +using SmartStore.Services.Media.Imaging; + +namespace SmartStore.Services.Media +{ + public partial class MediaUrlGenerator : IMediaUrlGenerator + { + private readonly HttpContextBase _httpContext; + + private readonly string _host; + private readonly string _appPath; + private readonly string _fallbackImageFileName; + + private static readonly string _processedImagesRootPath; + private static readonly string _fallbackImagesRootPath; + + static MediaUrlGenerator() + { + _processedImagesRootPath = MediaFileSystem.GetMediaPublicPath(); + _fallbackImagesRootPath = "content/images/"; + } + + public MediaUrlGenerator( + ISettingService settingService, + MediaSettings mediaSettings, + IStoreContext storeContext, + HttpContextBase httpContext) + { + _httpContext = httpContext; + + string appPath = "/"; + + if (HostingEnvironment.IsHosted) + { + appPath = HostingEnvironment.ApplicationVirtualPath.EmptyNull(); + + var cdn = storeContext.CurrentStore.ContentDeliveryNetwork; + if (cdn.HasValue() && !_httpContext.IsDebuggingEnabled && !_httpContext.Request.IsLocal) + { + _host = cdn; + } + else if (mediaSettings.AutoGenerateAbsoluteUrls) + { + var uri = httpContext.Request.Url; + _host = "//{0}{1}".FormatInvariant(uri.Authority, appPath); + } + else + { + _host = appPath; + } + } + + _host = _host.EmptyNull().EnsureEndsWith("/"); + _appPath = appPath.EnsureEndsWith("/"); + _fallbackImageFileName = settingService.GetSettingByKey("Media.DefaultImageName", "default-image.png"); + } + + public static string FallbackImagesRootPath => _fallbackImagesRootPath; + + public virtual string GenerateUrl( + MediaFileInfo file, + ProcessImageQuery imageQuery, + string host = null, + bool doFallback = true) + { + string path; + + // Build virtual path with pattern "media/{id}/{album}/{dir}/{NameWithExt}" + if (file?.Path != null) + { + path = _processedImagesRootPath + file.Id.ToString(CultureInfo.InvariantCulture) + "/" + file.Path; + } + else if (doFallback) + { + path = _processedImagesRootPath + "0/" + _fallbackImageFileName; + } + else + { + return null; + } + + if (host == null) + { + host = _host; + } + else if (host == string.Empty) + { + host = _appPath; + } + else + { + host = host.EnsureEndsWith("/"); + } + + var url = host; + + // Strip leading "/", the host/apppath has this already + if (path[0] == '/') + { + path = path.Substring(1); + } + + // Append media path + url += path; + + // Append query + var query = imageQuery?.ToString(false); + if (query != null && query.Length > 0) + { + url += query; + } + + return url; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Migration/MediaMigrator.cs b/src/Libraries/SmartStore.Services/Media/Migration/MediaMigrator.cs new file mode 100644 index 0000000000..5ad929d277 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Migration/MediaMigrator.cs @@ -0,0 +1,672 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.Plugins; +using SmartStore.Data.Utilities; +using SmartStore.Data.Setup; +using SmartStore.Services.Media.Storage; +using SmartStore.Core.IO; +using SmartStore.Data; +using System.Runtime.CompilerServices; +using SmartStore.Core.Domain.Messages; +using System.Diagnostics; +using System.Globalization; +using SmartStore.Core; +using SmartStore.Services.Media.Imaging; + +namespace SmartStore.Services.Media.Migration +{ + public class MediaMigrator + { + public class FileMin + { + public int Id { get; set; } + public string Name { get; set; } + public bool IsDupe { get; set; } + public string UniqueName { get; set; } + public int UniqueIndex { get; set; } + } + + internal static bool Executed; + internal const string MigrationName = "MediaManager"; + + private readonly ICommonServices _services; + private readonly IProviderManager _providerManager; + private readonly IMediaTypeResolver _mediaTypeResolver; + private readonly IAlbumRegistry _albumRegistry; + private readonly IFolderService _folderService; + private readonly IMediaTracker _mediaTracker; + private readonly IMediaStorageProvider _mediaStorageProvider; + private readonly IMediaFileSystem _mediaFileSystem; + private readonly IImageCache _imageCache; + private readonly bool _isFsProvider; + + public MediaMigrator( + ICommonServices services, + IProviderManager providerManager, + Func mediaStorageProvider, + IMediaTypeResolver mediaTypeResolver, + IAlbumRegistry albumRegistry, + IFolderService folderService, + IMediaTracker mediaTracker, + IImageCache imageCache, + IMediaFileSystem mediaFileSystem) + { + _services = services; + _providerManager = providerManager; + _mediaTypeResolver = mediaTypeResolver; + _albumRegistry = albumRegistry; + _folderService = folderService; + _mediaTracker = mediaTracker; + _mediaFileSystem = mediaFileSystem; + _imageCache = imageCache; + _mediaStorageProvider = mediaStorageProvider(); + _isFsProvider = _mediaStorageProvider is FileSystemMediaStorageProvider; + } + + public void Migrate() + { + var ctx = _services.DbContext as SmartObjectContext; + + // We're going to add new hooked entities, but during migration + // we don't need any hooking. + MediaTrackerHook.Silent = true; + + long elapsed = 0; + var watch = new Stopwatch(); + + try + { + Execute("CreateAlbums", () => CreateAlbums()); + Execute("CreateSettings", () => CreateSettings(ctx)); + Execute("MigrateDownloads", () => MigrateDownloads(ctx)); + Execute("MigrateMediaFiles", () => MigrateMediaFiles(ctx)); + Execute("MigrateUploadedFiles", () => MigrateUploadedFiles(ctx)); + Execute("DetectTracks", () => DetectTracks()); + Execute("ClearImageCache", () => ClearImageCache()); + + _folderService.ClearCache(); + } + catch (Exception ex) + { + throw ex; + } + finally + { + MediaTrackerHook.Silent = false; + Executed = true; + } + + void Execute(string step, Action action) + { + watch.Start(); + action(); + watch.Stop(); + var time = watch.ElapsedMilliseconds - elapsed; + var str = "MediaMigrator > {0}: {1} ms.".FormatCurrent(step, time); + elapsed = watch.ElapsedMilliseconds; + Debug.WriteLine(str); + } + } + + public void CreateSettings(SmartObjectContext ctx) + { + var prefix = nameof(MediaSettings) + "."; + + ctx.MigrateSettings(x => + { + x.Add(prefix + nameof(MediaSettings.ImageTypes), string.Join(" ", MediaType.Image.DefaultExtensions)); + x.Add(prefix + nameof(MediaSettings.VideoTypes), string.Join(" ", MediaType.Video.DefaultExtensions)); + x.Add(prefix + nameof(MediaSettings.AudioTypes), string.Join(" ", MediaType.Audio.DefaultExtensions)); + x.Add(prefix + nameof(MediaSettings.DocumentTypes), string.Join(" ", MediaType.Document.DefaultExtensions)); + x.Add(prefix + nameof(MediaSettings.TextTypes), string.Join(" ", MediaType.Text.DefaultExtensions)); + }); + } + + public void CreateAlbums() + { + // Enforce full album registration + _albumRegistry.GetAllAlbums(); + } + + public void MigrateDownloads(SmartObjectContext ctx) + { + var sql = "SELECT * FROM [Download] WHERE [MediaFileId] IS NULL AND [UseDownloadUrl] = 0"; + var downloadStubs = ctx.SqlQuery(sql).ToDictionary(x => x.Id); + + var downloadsFolderId = _albumRegistry.GetAlbumByName(SystemAlbumProvider.Downloads)?.Id; + var messagesFolderId = _albumRegistry.GetAlbumByName(SystemAlbumProvider.Messages)?.Id; + var newFiles = new List(); + + using (var scope = new DbContextScope(ctx, + validateOnSave: false, + hooksEnabled: false, + autoCommit: false, + autoDetectChanges: false)) + { + var messageTemplates = ctx.Set() + .Where(x => x.Attachment1FileId.HasValue || x.Attachment2FileId.HasValue || x.Attachment3FileId.HasValue) + .ToList(); + + // Key = Download.Id + var messageTemplatesDict = new Dictionary(); + foreach (var mt in messageTemplates) + { + if (mt.Attachment1FileId.HasValue) messageTemplatesDict[mt.Attachment1FileId.Value] = mt; + if (mt.Attachment2FileId.HasValue) messageTemplatesDict[mt.Attachment2FileId.Value] = mt; + if (mt.Attachment3FileId.HasValue) messageTemplatesDict[mt.Attachment3FileId.Value] = mt; + } + + var hasPostProcessor = _isFsProvider || messageTemplatesDict.Count > 0; + + var query = ctx.Set().Where(x => x.MediaFileId == null); + var pager = new FastPager(query, 1000); + + while (pager.ReadNextPage(out var downloads)) + { + foreach (var d in downloads) + { + var stub = downloadStubs.Get(d.Id); + if (stub == null) + continue; + + if (stub.UseDownloadUrl || string.IsNullOrEmpty(stub.Extension)) + { + // Something weird has happened in the past + continue; + } + + if (stub.Filename == "undefined" || string.IsNullOrEmpty(stub.Filename)) + { + stub.Filename = stub.Id.ToString(CultureInfo.InvariantCulture); + } + + var isMailAttachment = false; + if (messageTemplatesDict.TryGetValue(stub.Id, out var mt)) + { + isMailAttachment = true; + } + + // Create and insert new MediaFile entity for the download + var file = new MediaFile + { + CreatedOnUtc = stub.UpdatedOnUtc, + UpdatedOnUtc = stub.UpdatedOnUtc, + Extension = stub.Extension.TrimStart('.'), + Name = stub.Filename.Truncate(292), // Extension appended later in MigrateFiles() + MimeType = stub.ContentType, + MediaType = MediaType.Image, // Resolved later in MigrateFiles() + FolderId = isMailAttachment ? messagesFolderId : downloadsFolderId, + IsTransient = stub.IsTransient, + MediaStorageId = stub.MediaStorageId, + Version = 0 // Ensure that this record gets processed by MigrateFiles() + }; + + // Assign new file to download + d.MediaFile = file; + + // To be able to move files later + if (hasPostProcessor) + { + newFiles.Add(file); + } + } + + // Save to DB + int num = scope.Commit(); + + if (hasPostProcessor) + { + var downloadsDict = downloads.ToDictionary(x => x.Id); + + if (_isFsProvider) + { + // Copy files from "Media/Downloads" to "Media/Storage" folder + MoveDownloadFiles(newFiles.ToDictionary(x => x.Id), downloadsDict, downloadStubs); + } + + // MessageTemplate attachments (Download > MediaFile) + if (messageTemplatesDict.Count > 0) + { + ReRefMessageTemplateAttachments(ctx, messageTemplatesDict, downloadsDict); + } + + newFiles.Clear(); + } + + // Breathe + ctx.DetachEntities(); + ctx.DetachEntities(deep: true); + } + } + } + + private void ReRefMessageTemplateAttachments( + SmartObjectContext ctx, + Dictionary messageTemplatesDict, + Dictionary downloads) + { + bool hasChanges = false; + + foreach (var kvp in messageTemplatesDict) + { + var downloadId = kvp.Key; + var mt = kvp.Value; + var idxProp = Array.IndexOf(new int?[] { mt.Attachment1FileId, mt.Attachment2FileId, mt.Attachment3FileId }, downloadId) + 1; + + if (idxProp > 0) + { + var d = downloads.Get(downloadId); + if (d?.MediaFileId != null) + { + // Change Download.Id ref to MediaFile.Id + if (idxProp == 1) mt.Attachment1FileId = d.MediaFileId; + if (idxProp == 2) mt.Attachment2FileId = d.MediaFileId; + if (idxProp == 3) mt.Attachment3FileId = d.MediaFileId; + + // We don't need Download entity anymore + ctx.Set().Remove(d); + + hasChanges = true; + } + } + } + + if (hasChanges) + { + ctx.SaveChanges(); + } + } + + private void MoveDownloadFiles( + Dictionary newFilesDict, + Dictionary downloadsDict, + Dictionary downloadStubs) + { + if (_mediaFileSystem.FolderExists("Downloads")) + { + var downloadFiles = _mediaFileSystem.ListFiles("Downloads"); + foreach (var downloadFile in downloadFiles) + { + if (int.TryParse(downloadFile.Title, out var downloadId) && downloadId > 0 && downloadsDict.TryGetValue(downloadId, out var d)) + { + var stub = downloadStubs.Get(d.Id); + if (stub == null || d.MediaFileId == null) + continue; + + var file = newFilesDict.Get(d.MediaFileId.Value); + if (file != null) + { + try + { + // Copy now + var newPath = GetStoragePath(file); + if (!_mediaFileSystem.FileExists(newPath)) + { + _mediaFileSystem.CopyFile(downloadFile.Path, newPath); + } + } + catch { } + } + } + } + } + } + + public void MigrateMediaFiles(SmartObjectContext ctx) + { + string prevName = null; + int fileIndex = 0; + + var query = ctx.Set().OrderBy(x => x.Name); + var totalCount = query.Count(); + var pageSize = Math.Max(1000, Math.Min(5000, totalCount / 200)); + var pageIndex = 0; + + using (var scope = new DbContextScope(ctx, + hooksEnabled: false, + autoCommit: false, + proxyCreation: false, + validateOnSave: false, + lazyLoading: false)) + { + while (true) + { + var files = new PagedList(query, pageIndex, pageSize); + if (files.Count == 0) + break; + + foreach (var file in files) + { + if (file.Version > 0) + continue; + + if (file.Extension.IsEmpty()) + { + file.Extension = MimeTypes.MapMimeTypeToExtension(file.MimeType); + } + + var name = file.Name; + var fixedName = name; + if (name.IsEmpty()) + { + name = fixedName = file.Id.ToString(System.Globalization.CultureInfo.InvariantCulture); + } + else + { + name = fixedName = MediaHelper.NormalizeFileName(file.Name.Truncate(292)); + if (name == prevName) + { + // Make file name unique + fixedName = name + "-" + ++fileIndex; + } + else + { + fileIndex = 0; + } + } + + prevName = name; + + file.Name = fixedName + "." + file.Extension; + file.CreatedOnUtc = file.UpdatedOnUtc; + file.Version = 1; + + ProcessMediaFile(file); + } + + // Save to DB + int num = scope.Commit(); + + // Breathe + ctx.DetachEntities(deep: true); + + if (!files.HasNextPage) + break; + + pageIndex++; + } + } + } + + public void MigrateMediaFiles_Old(SmartObjectContext ctx) + { + var query = ctx.Set(); + //.Where(x => x.Version == 0) + //.Include(x => x.MediaStorage); + + var pager = new FastPager(query, 1000); + + using (var scope = new DbContextScope(ctx, + hooksEnabled: false, + autoCommit: false, + proxyCreation: false, + validateOnSave: false, + lazyLoading: false)) + { + while (pager.ReadNextPage(out var files)) + { + foreach (var file in files) + { + if (file.Version > 0) + continue; + + if (file.Extension.IsEmpty()) + { + file.Extension = MimeTypes.MapMimeTypeToExtension(file.MimeType); + } + + var name = file.Name; + if (name.IsEmpty()) + { + name = file.Id.ToString(System.Globalization.CultureInfo.InvariantCulture); + } + else + { + name = MediaHelper.NormalizeFileName(file.Name.Truncate(292)); + } + + file.Name = name + "." + file.Extension; + file.CreatedOnUtc = file.UpdatedOnUtc; + file.Version = 1; + + ProcessMediaFile(file); + } + + // Save to DB + int num = scope.Commit(); + + // Breathe + ctx.DetachEntities(deep: true); + } + } + } + + public void MigrateUploadedFiles(SmartObjectContext ctx) + { + var fileSet = ctx.Set(); + var folderSet = ctx.Set(); + + using (var scope = new DbContextScope(ctx, + hooksEnabled: false, + autoCommit: false, + validateOnSave: false, + lazyLoading: false, + autoDetectChanges: false)) + { + + var albumId = _albumRegistry.GetAlbumByName(SystemAlbumProvider.Files)?.Id; + var rootFolder = _mediaFileSystem.GetFolder("Uploaded"); + if (!rootFolder.Exists) + return; + + ProcessFolder(rootFolder, albumId.Value); + + void ProcessFolder(IFolder folder, int mediaFolderId) + { + var newFiles = new List(); + + foreach (var uploadedFile in _mediaFileSystem.ListFiles(folder.Path)) + { + var file = new MediaFile + { + CreatedOnUtc = uploadedFile.LastUpdated, + UpdatedOnUtc = uploadedFile.LastUpdated, + Extension = uploadedFile.Extension.TrimStart('.'), + Name = uploadedFile.Name, + MimeType = MimeTypes.MapNameToMimeType(uploadedFile.Name), + Size = Convert.ToInt32(uploadedFile.Size), + FolderId = mediaFolderId, + Version = 2 + }; + + ProcessMediaFile(file); + + newFiles.Add(new FilePair { MediaFile = file, UploadedFile = uploadedFile }); + fileSet.Add(file); + } + + // Process/save files of current folder + try + { + // Save files to DB + int num = scope.Commit(); + + // Copy/Move files + foreach (var newFile in newFiles) + { + if (_isFsProvider) + { + var newPath = GetStoragePath(newFile.MediaFile); + if (!_mediaFileSystem.FileExists(newPath)) + { + // TODO: (mm) (mc) should we actually MOVE the file? + _mediaFileSystem.CopyFile(newFile.UploadedFile.Path, newPath); + } + } + else + { + _mediaStorageProvider.Save(newFile.MediaFile, MediaStorageItem.FromFile(newFile.UploadedFile)); + } + } + + if (!_isFsProvider) + { + // MediaFile.MediaStorageId has been updated, we need to save again. + num = scope.Commit(); + } + } + catch + { + throw; + } + finally + { + newFiles.Clear(); + + // Breathe + ctx.DetachEntities(deep: true); + } + + foreach (var uploadedFolder in _mediaFileSystem.ListFolders(folder.Path)) + { + var mediaFolder = new MediaFolder + { + Name = uploadedFolder.Name, + ParentId = mediaFolderId + }; + + // Add folder and save ASAP, we need the folder id + folderSet.Add(mediaFolder); + ctx.SaveChanges(); + + ProcessFolder(uploadedFolder, mediaFolder.Id); + } + } + } + } + + private void ProcessMediaFile(MediaFile file) + { + if (file.Size == 0) + { + file.Size = Convert.ToInt32(_mediaStorageProvider.GetSize(file)); + } + + file.MediaType = _mediaTypeResolver.Resolve(file); + + if (file.MediaType == MediaType.Image && file.Width == null && file.Height == null) + { + // Resolve image width and height + var stream = _mediaStorageProvider.OpenRead(file); + if (stream != null) + { + try + { + var size = ImageHeader.GetDimensions(stream, file.MimeType, true); + file.Width = size.Width; + file.Height = size.Height; + file.PixelSize = size.Width * size.Height; + } + catch + { + // Don't attempt again + file.Width = 0; + file.Height = 0; + } + finally + { + stream.Dispose(); + } + } + } + + if (file.Width.HasValue && file.Height.HasValue) + { + file.PixelSize = file.Width.Value * file.Height.Value; + // TODO: Metadata JSON + } + } + + public void DetectTracks() + { + foreach (var albumName in _albumRegistry.GetAlbumNames(true)) + { + //if (albumName == SystemAlbumProvider.Downloads || albumName == SystemAlbumProvider.Messages) + // continue; // Download and MessageTemplate tracks already added in MigrateDownload() + + _mediaTracker.DetectAllTracks(albumName, true); + } + } + + public void ClearImageCache() + { + _imageCache.Clear(); + } + + private string GetStoragePath(DownloadStub stub) + { + var fileName = BuildFileName(stub.Id, stub.Extension, stub.ContentType); + return _mediaFileSystem.Combine("Downloads", fileName); + } + + private string GetStoragePath(QueuedEmailAttachmentStub stub) + { + var fileName = BuildFileName(stub.Id, Path.GetExtension(stub.Name), stub.MimeType); + return _mediaFileSystem.Combine("QueuedEmailAttachment", fileName); + } + + private string GetStoragePath(MediaFile file, bool createFolder = true) + { + var fileName = BuildFileName(file.Id, file.Extension, file.MimeType); + var subfolder = _mediaFileSystem.Combine("Storage", fileName.Substring(0, ImageCache.MaxDirLength)); + + if (createFolder) + { + _mediaFileSystem.CreateFolder(subfolder); + } + + return _mediaFileSystem.Combine(subfolder, fileName); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private string BuildFileName(int id, string ext, string mime) + { + if (ext.IsEmpty()) + ext = MimeTypes.MapMimeTypeToExtension(mime); + + return id.ToString(ImageCache.IdFormatString) + "." + ext.EmptyNull().TrimStart('.'); + } + + public class DownloadStub + { + public int Id { get; set; } + public bool UseDownloadUrl { get; set; } + public string ContentType { get; set; } + public string Filename { get; set; } + public string Extension { get; set; } + public bool IsTransient { get; set; } + public DateTime UpdatedOnUtc { get; set; } + public int? MediaStorageId { get; set; } + } + + public class QueuedEmailAttachmentStub + { + public int Id { get; set; } + public int QueuedEmailId { get; set; } + public string MimeType { get; set; } + public string Name { get; set; } + public int? MediaStorageId { get; set; } + public int? FileId { get; set; } + } + + class FilePair + { + public MediaFile MediaFile { get; set; } + public IFile UploadedFile { get; set; } + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Migration/MediaMigrator3.cs b/src/Libraries/SmartStore.Services/Media/Migration/MediaMigrator3.cs new file mode 100644 index 0000000000..6163b4acf1 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Migration/MediaMigrator3.cs @@ -0,0 +1,83 @@ +using System.Linq; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Services.Media.Migration +{ + public class MediaMigrator3 + { + internal static bool Executed; + internal const string MigrationName = "MediaManager3"; + + private readonly IDbContext _db; + + public MediaMigrator3(IDbContext db) + { + _db = db; + } + + public void Migrate() + { + var setFolders = _db.Set(); + + // Insert new Albums: Catalog & Content + // Entity has a unique index of ParentId and Name. + var catalogAlbum = setFolders.FirstOrDefault(x => x.ParentId == null && x.Name == SystemAlbumProvider.Catalog) as MediaAlbum; + if (catalogAlbum == null) + { + catalogAlbum = new MediaAlbum { Name = SystemAlbumProvider.Catalog, ResKey = "Admin.Catalog", CanDetectTracks = true, Order = int.MinValue }; + setFolders.Add(catalogAlbum); + } + + var contentAlbum = setFolders.FirstOrDefault(x => x.ParentId == null && x.Name == SystemAlbumProvider.Content) as MediaAlbum; + if (contentAlbum == null) + { + contentAlbum = new MediaAlbum { Name = SystemAlbumProvider.Content, ResKey = "Admin.Media.Album.Content", CanDetectTracks = true, Order = int.MinValue + 10 }; + setFolders.Add(contentAlbum); + } + + _db.SaveChanges(); + + // Load all db albums into a dictionary (Key = AlbumName) + var albums = setFolders.OfType().ToDictionary(x => x.Name); + + // Reorganize files (product/category/brand >> catalog) + foreach (var oldName in new[] { "product", "category", "brand" }) + { + UpdateFolderId(oldName, catalogAlbum); + } + + // Reorganize files (news/blog/forum >> content) + foreach (var oldName in new[] { "blog", "news", "forum" }) + { + UpdateFolderId(oldName, contentAlbum); + } + + // Put all unassigned files to content album + _db.ExecuteSqlCommand($"UPDATE [MediaFile] SET [FolderId] = {contentAlbum.Id} WHERE ([FolderId] IS NULL)"); + + // Delete all obsolete albums + var namesToDelete = new[] { "product", "category", "brand", "blog", "news", "forum" }; + var toDelete = albums + .Select(x => x.Value) + .Where(x => namesToDelete.Contains(x.Name)) + .ToList(); + + if (toDelete.Any()) + { + setFolders.RemoveRange(toDelete); + } + + _db.SaveChanges(); + + void UpdateFolderId(string oldAlbumName, MediaAlbum newAlbum) + { + var oldAlbum = albums.Get(oldAlbumName); + if (oldAlbum != null) + { + _db.ExecuteSqlCommand($"UPDATE [MediaFile] SET [FolderId] = {newAlbum.Id} WHERE [FolderId] = {oldAlbum.Id}"); + } + } + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Migration/SeedingDbMigrationConsumer.cs b/src/Libraries/SmartStore.Services/Media/Migration/SeedingDbMigrationConsumer.cs new file mode 100644 index 0000000000..27c922ed2d --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Migration/SeedingDbMigrationConsumer.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using SmartStore.Core.Events; +using SmartStore.Data.Setup; +using Autofac; + +namespace SmartStore.Services.Media.Migration +{ + public class SeedingDbMigrationConsumer : IConsumer + { + public void Handle(SeedingDbMigrationEvent message, IComponentContext componentContext) + { + if (message.MigrationName == MediaMigrator.MigrationName) + { + componentContext.Resolve().Migrate(); + } + else if (message.MigrationName == MediaMigrator3.MigrationName) + { + componentContext.Resolve().Migrate(); + } + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/PictureService.cs b/src/Libraries/SmartStore.Services/Media/PictureService.cs deleted file mode 100644 index 9e421212eb..0000000000 --- a/src/Libraries/SmartStore.Services/Media/PictureService.cs +++ /dev/null @@ -1,747 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Drawing; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Web; -using System.Web.Hosting; -using SmartStore.Collections; -using SmartStore.Core; -using SmartStore.Core.Caching; -using SmartStore.Core.Data; -using SmartStore.Core.Domain.Catalog; -using SmartStore.Core.Domain.Media; -using SmartStore.Core.Events; -using SmartStore.Core.IO; -using SmartStore.Core.Logging; -using SmartStore.Core.Plugins; -using SmartStore.Services.Configuration; -using SmartStore.Services.Media.Storage; -using SmartStore.Utilities; - -namespace SmartStore.Services.Media -{ - [Serializable] - public class PictureInfo - { - public int Id { get; set; } - /// - /// The virtual path to the image file to be processed by the media middleware controller, e.g. "/media/image/1234/image.jpg" - /// - public string Path { get; set; } - public int? Width { get; set; } - public int? Height { get; set; } - public string MimeType { get; set; } - public string Extension { get; set; } - } - - public partial class PictureService : IPictureService - { - // 0 = Id - private const string MEDIACACHE_LOOKUP_KEY = "media:info-{0}"; - private const string MEDIACACHE_LOOKUP_KEY_PATTERN = "media:info-*"; - - private readonly IRepository _pictureRepository; - private readonly IRepository _productPictureRepository; - private readonly ISettingService _settingService; - private readonly IEventPublisher _eventPublisher; - private readonly MediaSettings _mediaSettings; - private readonly IImageProcessor _imageProcessor; - private readonly IImageCache _imageCache; - private readonly Provider _storageProvider; - private readonly HttpContextBase _httpContext; - private readonly ICacheManager _cacheManager; - - private readonly string _host; - private readonly string _appPath; - - private static readonly string _processedImagesRootPath; - private static readonly string _fallbackImagesRootPath; - - static PictureService() - { - _processedImagesRootPath = MediaFileSystem.GetMediaPublicPath() + "image/"; - _fallbackImagesRootPath = "content/images/"; - } - - public PictureService( - IRepository pictureRepository, - IRepository productPictureRepository, - ISettingService settingService, - IEventPublisher eventPublisher, - MediaSettings mediaSettings, - IImageProcessor imageProcessor, - IImageCache imageCache, - IProviderManager providerManager, - IStoreContext storeContext, - HttpContextBase httpContext, - ICacheManager cacheManager) - { - _pictureRepository = pictureRepository; - _productPictureRepository = productPictureRepository; - _settingService = settingService; - _eventPublisher = eventPublisher; - _mediaSettings = mediaSettings; - _imageProcessor = imageProcessor; - _imageCache = imageCache; - _httpContext = httpContext; - _cacheManager = cacheManager; - - var systemName = settingService.GetSettingByKey("Media.Storage.Provider", DatabaseMediaStorageProvider.SystemName); - _storageProvider = providerManager.GetProvider(systemName); - - Logger = NullLogger.Instance; - - string appPath = "/"; - - if (HostingEnvironment.IsHosted) - { - appPath = HostingEnvironment.ApplicationVirtualPath.EmptyNull(); - - var cdn = storeContext.CurrentStore.ContentDeliveryNetwork; - if (cdn.HasValue() && !_httpContext.IsDebuggingEnabled && !_httpContext.Request.IsLocal) - { - _host = cdn; - } - else if (mediaSettings.AutoGenerateAbsoluteUrls) - { - var uri = httpContext.Request.Url; - _host = "//{0}{1}".FormatInvariant(uri.Authority, appPath); - } - else - { - _host = appPath; - } - } - - _host = _host.EmptyNull().EnsureEndsWith("/"); - _appPath = appPath.EnsureEndsWith("/"); - - } - - public ILogger Logger { get; set; } - - #region Utilities - - public static string FallbackImagesRootPath - { - get { return "~/" + _fallbackImagesRootPath; } - } - - protected virtual string GetFallbackImageFileName(FallbackPictureType defaultPictureType = FallbackPictureType.Entity) - { - string defaultImageFileName; - - switch (defaultPictureType) - { - case FallbackPictureType.Entity: - defaultImageFileName = _settingService.GetSettingByKey("Media.DefaultImageName", "default-image.png"); - break; - default: - defaultImageFileName = _settingService.GetSettingByKey("Media.DefaultImageName", "default-image.png"); - break; - } - - return defaultImageFileName; - } - - #endregion - - #region Imaging - - public virtual byte[] ValidatePicture(byte[] pictureBinary, string mimeType) - { - return ValidatePicture(pictureBinary, mimeType, out _); - } - - public virtual byte[] ValidatePicture(byte[] pictureBinary, string mimeType, out Size size) - { - Guard.NotNull(pictureBinary, nameof(pictureBinary)); - Guard.NotEmpty(mimeType, nameof(mimeType)); - - size = Size.Empty; - - var originalSize = ImageHeader.GetDimensions(pictureBinary, mimeType); - var maxSize = _mediaSettings.MaximumImageSize; - - var query = new ProcessImageQuery(pictureBinary) - { - Quality = _mediaSettings.DefaultImageQuality, - Format = MimeTypes.MapMimeTypeToExtension(mimeType), - IsValidationMode = true - }; - - if (originalSize.IsEmpty || (originalSize.Height <= maxSize && originalSize.Width <= maxSize)) - { - // Give subscribers the chance to (pre)-process - var evt = new ImageUploadValidatedEvent(query, originalSize); - _eventPublisher.Publish(evt); - - if (evt.ResultBuffer != null) - { - // Maybe subscriber forgot to set this, so check - size = evt.ResultSize.IsEmpty ? originalSize : evt.ResultSize; - return evt.ResultBuffer; - } - else - { - size = originalSize; - return pictureBinary; - } - } - - query.MaxWidth = maxSize; - query.MaxHeight = maxSize; - - using (var result = _imageProcessor.ProcessImage(query)) - { - size = new Size(result.Width, result.Height); - var buffer = result.OutputStream.GetBuffer(); - return buffer; - } - } - - 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); - - using (var otherStream = OpenPictureStream(picture)) - { - if (myStream.ContentsEqual(otherStream)) - { - equalPictureId = picture.Id; - return null; - } - } - } - - return pictureBinary; - } - catch - { - return null; - } - finally - { - myStream.Dispose(); - } - } - - public virtual Stream OpenPictureStream(Picture picture) - { - Guard.NotNull(picture, nameof(picture)); - - return _storageProvider.Value.OpenRead(picture.ToMedia()); - } - - public virtual byte[] LoadPictureBinary(Picture picture) - { - Guard.NotNull(picture, nameof(picture)); - - return _storageProvider.Value.Load(picture.ToMedia()); - } - - public virtual Task LoadPictureBinaryAsync(Picture picture) - { - Guard.NotNull(picture, nameof(picture)); - - return _storageProvider.Value.LoadAsync(picture.ToMedia()); - } - - public virtual Size GetPictureSize(byte[] pictureBinary, string mimeType = null) - { - return ImageHeader.GetDimensions(pictureBinary, mimeType); - } - - public IDictionary GetPictureInfos(IEnumerable pictureIds) - { - Guard.NotNull(pictureIds, nameof(pictureIds)); - - var allRequestedInfos = (from id in pictureIds.Distinct().Where(x => x > 0) - let cacheKey = MEDIACACHE_LOOKUP_KEY.FormatInvariant(id) - select new - { - PictureId = id, - CacheKey = cacheKey, - Info = _cacheManager.Contains(cacheKey) ? _cacheManager.Get(cacheKey, independent: true) : (PictureInfo)null - }).ToList(); - - var result = new Dictionary(allRequestedInfos.Count); - var uncachedPictureIds = allRequestedInfos.Where(x => x.Info == null).Select(x => x.PictureId).ToArray(); - var uncachedPictures = new Dictionary(); - - if (uncachedPictureIds.Length > 0) - { - uncachedPictures = GetPicturesByIds(uncachedPictureIds, false).ToDictionary(x => x.Id); - } - - foreach (var info in allRequestedInfos) - { - if (info.Info != null) - { - result.Add(info.PictureId, info.Info); - } - else - { - // TBD: (mc) Does this need a locking strategy? Apparently yes. But it is hard to accomplish for a random sequence - // without locking the whole thing and loosing performance. Better no lock (?) - var newInfo = CreatePictureInfo(uncachedPictures.Get(info.PictureId)); - result.Add(info.PictureId, newInfo); - _cacheManager.Put(info.CacheKey, newInfo); - } - } - - return result; - } - - public PictureInfo GetPictureInfo(int? pictureId) - { - if (pictureId.GetValueOrDefault() < 1) - return null; - - var cacheKey = MEDIACACHE_LOOKUP_KEY.FormatInvariant(pictureId.GetValueOrDefault()); - var info = _cacheManager.Get(cacheKey, () => - { - return CreatePictureInfo(GetPictureById(pictureId.GetValueOrDefault())); - }, independent: true); - - return info; - } - - public PictureInfo GetPictureInfo(Picture picture) - { - if (picture == null) - return null; - - var cacheKey = MEDIACACHE_LOOKUP_KEY.FormatInvariant(picture.Id); - var info = _cacheManager.Get(cacheKey, () => - { - return CreatePictureInfo(picture); - }, independent: true); - - return info; - } - - public virtual string GetUrl(int pictureId, int targetSize = 0, FallbackPictureType fallbackType = FallbackPictureType.Entity, string host = null) - { - return GetUrl(GetPictureInfo(pictureId), targetSize, fallbackType, host); - } - - public virtual string GetUrl(Picture picture, int targetSize = 0, FallbackPictureType fallbackType = FallbackPictureType.Entity, string host = null) - { - return GetUrl(GetPictureInfo(picture), targetSize, fallbackType, host); - } - - public string GetUrl(PictureInfo info, int targetSize = 0, FallbackPictureType fallbackType = FallbackPictureType.Entity, string host = null) - { - string path = null; - string query = null; - - if (info?.Path != null) - { - path = info.Path; - } - else if (fallbackType > FallbackPictureType.NoFallback) - { - path = String.Concat(_processedImagesRootPath, "0/", GetFallbackImageFileName(fallbackType)); - } - - if (path != null) - { - if (targetSize > 0) - { - // TBD: (mc) let pass query string as NameValueCollection (?) - query = "?size=" + targetSize; - } - - path = BuildUrlCore(path, query, host); - } - - return path; - } - - public virtual string GetFallbackUrl(int targetSize = 0, FallbackPictureType fallbackType = FallbackPictureType.Entity, string host = null) - { - return GetUrl((PictureInfo)null, targetSize, fallbackType, host); - } - - /// - /// Creates a cacheable counterpart for a Picture object instance - /// - /// - /// - protected virtual PictureInfo CreatePictureInfo(Picture picture) - { - if (picture == null) - return null; - - var extension = MimeTypes.MapMimeTypeToExtension(picture.MimeType); - - // Build virtual path with pattern "media/image/{id}/{SeoFileName}.{extension}" - var path = "{0}{1}/{2}.{3}".FormatInvariant( - _processedImagesRootPath, - picture.Id, - picture.SeoFilename.NullEmpty() ?? picture.Id.ToString(ImageCache.IdFormatString), - extension); - - // Do some maintenance stuff - EnsurePictureSizeResolved(picture, true); - - if (picture.IsNew) - { - _imageCache.Delete(picture); - - // We do not validate picture binary here to ensure that no exception ("Parameter is not valid") will be thrown - UpdatePicture( - picture, - null, - picture.MimeType, - picture.SeoFilename, - false, - false); - } - - return new PictureInfo - { - Id = picture.Id, - MimeType = picture.MimeType, - Extension = extension, - Path = path, - Width = picture?.Width, - Height = picture?.Height, - }; - } - - protected virtual string BuildUrlCore(string virtualPath, string query, string host) - { - // TBD: (mc) No arg check because of performance?! - - if (host == null) - { - host = _host; - } - else if (host == string.Empty) - { - host = _appPath; - } - else - { - host = host.EnsureEndsWith("/"); - } - - var sb = new StringBuilder(host, 100); - - // Strip leading "/", the host/apppath has this already - if (virtualPath[0] == '/') - { - virtualPath = virtualPath.Substring(1); - } - - // Append media path - sb.Append(virtualPath); - - // Append query - if (query != null && query.Length > 0) - { - if (query[0] != '?') sb.Append("?"); - sb.Append(query); - } - - return sb.ToString(); - } - - private void EnsurePictureSizeResolved(Picture picture, bool saveOnResolve) - { - if (picture.Width == null && picture.Height == null) - { - var mediaItem = picture.ToMedia(); - var stream = _storageProvider.Value.OpenRead(mediaItem); - - if (stream != null) - { - try - { - var size = ImageHeader.GetDimensions(stream, picture.MimeType, true); - picture.Width = size.Width; - picture.Height = size.Height; - picture.UpdatedOnUtc = DateTime.UtcNow; - - if (saveOnResolve) - { - try - { - _pictureRepository.Update(picture); - } - catch (InvalidOperationException ioe) - { - // Ignore exception for pictures that already have been processed. - if (!ioe.IsAlreadyAttachedEntityException()) - { - throw; - } - } - } - } - finally - { - stream.Dispose(); - } - } - } - } - - public int ClearUrlCache() - { - return _cacheManager.RemoveByPattern(MEDIACACHE_LOOKUP_KEY_PATTERN); - } - - #endregion - - #region Metadata Storage - - public virtual string GetPictureSeName(string name) - { - return SeoHelper.GetSeName(name, true, false, false); - } - - public virtual Picture SetSeoFilename(int pictureId, string seoFilename) - { - var picture = GetPictureById(pictureId); - - // update if it has been changed - if (picture != null && seoFilename != picture.SeoFilename) - { - UpdatePicture(picture, null, picture.MimeType, seoFilename, true, false); - } - - return picture; - } - - public virtual Picture GetPictureById(int pictureId) - { - if (pictureId == 0) - return null; - - var picture = _pictureRepository.GetById(pictureId); - return picture; - } - - public virtual IPagedList GetPictures(int pageIndex, int pageSize) - { - var query = from p in _pictureRepository.Table - orderby p.Id descending - select p; - - var pics = new PagedList(query, pageIndex, pageSize); - return pics; - } - - public virtual IList GetPicturesByProductId(int productId, int recordsToReturn = 0) - { - if (productId == 0) - return new List(); - - var query = from p in _pictureRepository.Table - join pp in _productPictureRepository.Table on p.Id equals pp.PictureId - orderby pp.DisplayOrder - where pp.ProductId == productId - select p; - - if (recordsToReturn > 0) - query = query.Take(() => recordsToReturn); - - var pics = query.ToList(); - return pics; - } - - public virtual Multimap GetPicturesByProductIds(int[] productIds, int? maxPicturesPerProduct = null, bool withBlobs = false) - { - Guard.NotNull(productIds, nameof(productIds)); - - if (maxPicturesPerProduct.HasValue) - { - Guard.IsPositive(maxPicturesPerProduct.Value, nameof(maxPicturesPerProduct)); - } - - var map = new Multimap(); - - if (!productIds.Any()) - return map; - - int take = maxPicturesPerProduct ?? int.MaxValue; - - var query = from pp in _productPictureRepository.TableUntracked - where productIds.Contains(pp.ProductId) - group pp by pp.ProductId into g - select new - { - ProductId = g.Key, - Pictures = g.OrderBy(x => x.DisplayOrder) - .Take(take) - .Select(x => new { x.PictureId, x.ProductId }) - }; - - var groupingResult = query.ToDictionary(x => x.ProductId, x => x.Pictures); - - using (var scope = new DbContextScope(ctx: _pictureRepository.Context, forceNoTracking: null)) - { - // EF doesn't support eager loading with grouped queries. We must hack a little bit. - var pictureIds = groupingResult.SelectMany(x => x.Value).Select(x => x.PictureId).Distinct().ToArray(); - var pictures = GetPicturesByIds(pictureIds, withBlobs).ToDictionarySafe(x => x.Id); - - foreach (var p in groupingResult.SelectMany(x => x.Value)) - { - if (pictures.ContainsKey(p.PictureId)) - { - map.Add(p.ProductId, pictures[p.PictureId]); - } - } - } - - return map; - } - - public virtual IList GetPicturesByIds(int[] pictureIds, bool withBlobs = false) - { - Guard.NotNull(pictureIds, nameof(pictureIds)); - - var query = _pictureRepository.Table - .Where(x => pictureIds.Contains(x.Id)); - - if (withBlobs) - { - query = query.Include(x => x.MediaStorage); - } - - return query.ToList(); - } - - public virtual void DeletePicture(Picture picture) - { - Guard.NotNull(picture, nameof(picture)); - - // delete thumbs - _imageCache.Delete(picture); - - // delete from url cache - _cacheManager.Remove(MEDIACACHE_LOOKUP_KEY.FormatInvariant(picture.Id)); - - // delete from storage - _storageProvider.Value.Remove(picture.ToMedia()); - - // delete entity - _pictureRepository.Delete(picture); - } - - public virtual Picture InsertPicture( - byte[] pictureBinary, - string mimeType, - string seoFilename, - bool isNew, - int width, - int height, - bool isTransient = true) - { - var picture = _pictureRepository.Create(); - picture.MimeType = mimeType.EmptyNull().Truncate(20); - picture.SeoFilename = seoFilename.Truncate(100); - picture.IsNew = isNew; - picture.IsTransient = isTransient; - picture.UpdatedOnUtc = DateTime.UtcNow; - - if (width > 0 && height > 0) - { - picture.Width = width; - picture.Height = height; - } - - _pictureRepository.Insert(picture); - - // Save to storage. - _storageProvider.Value.Save(picture.ToMedia(), pictureBinary); - - return picture; - } - - public virtual Picture InsertPicture( - byte[] pictureBinary, - string mimeType, - string seoFilename, - bool isNew, - bool isTransient = true, - bool validateBinary = true) - { - var size = Size.Empty; - - if (validateBinary) - { - pictureBinary = ValidatePicture(pictureBinary, mimeType, out size); - } - - return InsertPicture(pictureBinary, mimeType, seoFilename, isNew, size.Width, size.Height, isTransient); - } - - public virtual void UpdatePicture( - Picture picture, - byte[] pictureBinary, - string mimeType, - string seoFilename, - bool isNew, - bool validateBinary = true) - { - if (picture == null) - return; - - mimeType = mimeType.EmptyNull().Truncate(20); - seoFilename = seoFilename.Truncate(100); - - var size = Size.Empty; - - if (validateBinary && pictureBinary != null) - { - pictureBinary = ValidatePicture(pictureBinary, mimeType, out size); - } - - // delete old thumbs if a picture has been changed - if (seoFilename != picture.SeoFilename) - { - _imageCache.Delete(picture); - - // delete from url cache - _cacheManager.Remove(MEDIACACHE_LOOKUP_KEY.FormatInvariant(picture.Id)); - } - - picture.MimeType = mimeType; - picture.SeoFilename = seoFilename; - picture.IsNew = isNew; - picture.UpdatedOnUtc = DateTime.UtcNow; - - if (!size.IsEmpty) - { - picture.Width = size.Width; - picture.Height = size.Height; - } - - _pictureRepository.Update(picture); - - // save to storage - if (pictureBinary != null) - { - _storageProvider.Value.Save(picture.ToMedia(), pictureBinary); - } - } - - #endregion - } -} diff --git a/src/Libraries/SmartStore.Services/Media/ProcessImageException.cs b/src/Libraries/SmartStore.Services/Media/ProcessImageException.cs deleted file mode 100644 index d16095ff8c..0000000000 --- a/src/Libraries/SmartStore.Services/Media/ProcessImageException.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; - -namespace SmartStore.Services.Media -{ - public sealed class ProcessImageException : Exception - { - public ProcessImageException() - : this((string)null, null) - { - } - - public ProcessImageException(ProcessImageQuery query) - : this(query, null) - { - } - - public ProcessImageException(string message, ProcessImageQuery query) - : base(message) - { - Query = query; - } - - public ProcessImageException(ProcessImageQuery query, Exception innerException) - : base(CreateMessage(query, innerException), innerException) - { - Query = query; - } - - private static string CreateMessage(ProcessImageQuery query, Exception innerException) - { - var fileName = query?.FileName; - - var msg = fileName.HasValue() - ? "Error while processing image '{0}'".FormatCurrent(fileName) - : "Error while processing image"; - - if (innerException != null) - { - msg += " (" + innerException.Message + ")"; - } - - return msg; - } - - public ProcessImageQuery Query { get; private set; } - } -} diff --git a/src/Libraries/SmartStore.Services/Media/ProcessImageQuery.cs b/src/Libraries/SmartStore.Services/Media/ProcessImageQuery.cs deleted file mode 100644 index 9b3c5998e2..0000000000 --- a/src/Libraries/SmartStore.Services/Media/ProcessImageQuery.cs +++ /dev/null @@ -1,224 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.IO; -using System.Collections.Specialized; -using System.Text; -using SmartStore.Collections; -using System.Drawing; -using ImageProcessor.Imaging.Formats; - -namespace SmartStore.Services.Media -{ - public class ProcessImageQuery : QueryString - { - private readonly static HashSet _supportedTokens = new HashSet { "w", "h", "q", "m", "size" }; - - public ProcessImageQuery() - : this(null, new NameValueCollection()) - { - } - - public ProcessImageQuery(byte[] source) - : this(source, new NameValueCollection()) - { - } - - public ProcessImageQuery(Stream source) - : this(source, new NameValueCollection()) - { - } - - public ProcessImageQuery(Image source) - : this(source, new NameValueCollection()) - { - } - - public ProcessImageQuery(string source) - : this(source, new NameValueCollection()) - { - } - - public ProcessImageQuery(object source, NameValueCollection query) - : base(SanitizeCollection(query)) - { - Guard.NotNull(query, nameof(query)); - - Source = source; - DisposeSource = true; - Notify = true; - } - - public ProcessImageQuery(ProcessImageQuery query) - : base(SanitizeCollection(query)) - { - Guard.NotNull(query, nameof(query)); - - Source = query.Source; - Format = query.Format; - DisposeSource = query.DisposeSource; - } - - private static NameValueCollection SanitizeCollection(NameValueCollection query) - { - // We just need the supported flags - var sanitizable = query.AllKeys.Any(x => !_supportedTokens.Contains(x)); - if (sanitizable) - { - var copy = new NameValueCollection(query); - foreach (var key in copy.AllKeys) - { - if (!_supportedTokens.Contains(key)) - { - copy.Remove(key); - } - } - - return copy; - } - - return query; - } - - /// - /// The source image's physical path, app-relative virtual path, or a Stream, byte array or Image instance. - /// - public object Source { get; set; } - - public string FileName { get; set; } - - /// - /// Whether to dispose the source stream after resizing completes - /// - public bool DisposeSource { get; set; } - - /// - /// Whether to execute an applicable post processor which - /// can reduce the resulting file size drastically, but also - /// can slow down processing time. - /// - public bool ExecutePostProcessor { get; set; } - - public int? MaxWidth - { - get { return Get("w"); } - set { Set("w", value); } - } - - public int? MaxHeight - { - get { return Get("h"); } - set { Set("h", value); } - } - - public int? Quality - { - get { return Get("q"); } - set { Set("q", value); } - } - - /// - /// max (default) | boxpad | crop | min | pad | stretch - /// - public string ScaleMode - { - get { return Get("m"); } - set { Set("m", value); } - } - - /// - /// center (default) | top | bottom | left | right | top-left | top-right | bottom-left | bottom-right - /// - public string AnchorPosition - { - get { return Get("pos"); } - set { Set("pos", value); } - } - - public string BackgroundColor - { - get { return Get("bg"); } - set { Set("bg", value); } - } - - /// - /// Gets or sets the output file format either as a string ("png", "jpg", and "gif"), - /// or as a format object instance. - /// When format is not specified, the original format of the source image is used (unless it is not a web safe format - jpeg is the fallback in that scenario). - /// - public object Format { get; set; } - - public bool IsValidationMode { get; set; } - - public bool Notify { get; set; } - - private T Get(string name) - { - return base[name].Convert(); - } - - private void Set(string name, T val) - { - if (val == null) - base.Remove(name); - else - base.Add(name, val.Convert(), true); - } - - - public bool NeedsProcessing(bool ignoreQualityFlag = false) - { - if (base.Count == 0) - return false; - - if (ignoreQualityFlag && base.Count == 1 && base["q"] != null) - { - // Return false if ignoreQualityFlag is true and "q" is the only flag. - return false; - } - - if (base.Count == 1 && Quality == 100) - { - // If "q" is the only flag and its value is 100, we don't need to process - return false; - } - - return true; - } - - public string CreateHash() - { - var sb = new StringBuilder(); - - foreach (var key in base.AllKeys) - { - if (key == "m" && base["m"] == "max") - continue; // Mode 'max' is default and can be omitted - - sb.Append("-"); - sb.Append(key); - sb.Append(base[key]); - } - - return sb.ToString(); - } - - public string GetResultExtension() - { - if (Format == null) - { - return null; - } - else if (Format is ISupportedImageFormat) - { - return ((ISupportedImageFormat)Format).DefaultExtension; - } - else if (Format is string) - { - return (string)Format; - } - - return null; - } - } -} diff --git a/src/Libraries/SmartStore.Services/Media/ProcessImageResult.cs b/src/Libraries/SmartStore.Services/Media/ProcessImageResult.cs deleted file mode 100644 index 9ff224d143..0000000000 --- a/src/Libraries/SmartStore.Services/Media/ProcessImageResult.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.IO; - -namespace SmartStore.Services.Media -{ - public class ProcessImageResult : DisposableObject - { - public ProcessImageQuery Query { get; set; } - - public MemoryStream OutputStream { get; set; } - - public int? SourceWidth { get; set; } - public int? SourceHeight { get; set; } - public string SourceMimeType { get; set; } - - public string FileExtension { get; set; } - public string MimeType { get; set; } - public int Width { get; set; } - public int Height { get; set; } - - /// - /// Is true if any effect has been applied that changed the image visually (like background color, contrast, sharpness etc.). - /// Resize and compression quality does NOT count as FX. - /// - public bool HasAppliedVisualEffects { get; set; } - - public long ProcessTimeMs { get; set; } - - protected override void OnDispose(bool disposing) - { - if (disposing && OutputStream != null) - { - OutputStream.Dispose(); - OutputStream = null; - } - } - } -} diff --git a/src/Libraries/SmartStore.Services/Media/Search/FileCountResult.cs b/src/Libraries/SmartStore.Services/Media/Search/FileCountResult.cs new file mode 100644 index 0000000000..23b3f1d283 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Search/FileCountResult.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace SmartStore.Services.Media +{ + public partial class FileCountResult + { + [JsonIgnore] + public MediaFilesFilter Filter { get; set; } + + [JsonProperty("total")] + public int Total { get; set; } + + [JsonProperty("trash")] + public int Trash { get; set; } + + [JsonProperty("unassigned")] + public int Unassigned { get; set; } + + [JsonProperty("transient")] + public int Transient { get; set; } + + [JsonProperty("orphan")] + public int Orphan { get; set; } + + [JsonProperty("folders")] + public IDictionary Folders { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Search/IMediaSearcher.cs b/src/Libraries/SmartStore.Services/Media/Search/IMediaSearcher.cs new file mode 100644 index 0000000000..0c8ee885b4 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Search/IMediaSearcher.cs @@ -0,0 +1,16 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using SmartStore.Core; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Services.Media +{ + public interface IMediaSearcher + { + IPagedList SearchFiles(MediaSearchQuery query, MediaLoadFlags flags = MediaLoadFlags.AsNoTracking); + IQueryable PrepareQuery(MediaSearchQuery query, MediaLoadFlags flags); + IQueryable ApplyFilterQuery(MediaFilesFilter filter, IQueryable sourceQuery = null); + IQueryable ApplyLoadFlags(IQueryable query, MediaLoadFlags flags); + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Media/Search/MediaSearchQuery.cs b/src/Libraries/SmartStore.Services/Media/Search/MediaSearchQuery.cs new file mode 100644 index 0000000000..3fceaf4ddd --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Search/MediaSearchQuery.cs @@ -0,0 +1,88 @@ +using System.Runtime.Serialization; +using Newtonsoft.Json; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Services.Media +{ + public enum ImageDimension + { + VerySmall = 0, + Small = 1, + Medium = 2, + Large = 3, + VeryLarge = 4 + } + + [DataContract] + public partial class MediaFilesFilter + { + [DataMember] + [JsonProperty("mediaTypes")] + public string[] MediaTypes { get; set; } + + [DataMember] + [JsonProperty("mimeTypes")] + public string[] MimeTypes { get; set; } + + [DataMember] + [JsonProperty("extensions")] + public string[] Extensions { get; set; } + + [DataMember] + [JsonProperty("dimensions")] + public ImageDimension[] Dimensions { get; set; } + + [DataMember] + [JsonProperty("tags")] + public int[] Tags { get; set; } + + [DataMember] + [JsonProperty("hidden")] + public bool? Hidden { get; set; } + + [DataMember] + [JsonProperty("deleted")] + public bool? Deleted { get; set; } + + [DataMember] + [JsonProperty("term")] + public string Term { get; set; } + + [DataMember] + [JsonProperty("exact")] + public bool ExactMatch { get; set; } + + [DataMember] + [JsonProperty("includeAlt")] + public bool IncludeAltForTerm { get; set; } + } + + [DataContract] + public partial class MediaSearchQuery : MediaFilesFilter + { + [DataMember] + [JsonProperty("folderId")] + public int? FolderId { get; set; } + + [DataMember] + [JsonProperty("deep")] + public bool DeepSearch { get; set; } + + + [DataMember] + [JsonProperty("page")] + public int PageIndex { get; set; } + + [DataMember] + [JsonProperty("pageSize")] + public int PageSize { get; set; } = int.MaxValue; + + [DataMember] + [JsonProperty("sortBy")] + public string SortBy { get; set; } = nameof(MediaFile.Id); + + [DataMember] + [JsonProperty("sortDesc")] + public bool SortDesc { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Search/MediaSearchResult.cs b/src/Libraries/SmartStore.Services/Media/Search/MediaSearchResult.cs new file mode 100644 index 0000000000..09483acfe0 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Search/MediaSearchResult.cs @@ -0,0 +1,23 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using SmartStore.Core; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Services.Media +{ + public partial class MediaSearchResult : PagedListBase, IEnumerable + { + private readonly IList _files; + + public MediaSearchResult(IPagedList pageable, Func converter) : base(pageable) + { + _files = pageable.Select(converter).ToList(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _files.GetEnumerator(); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Search/MediaSearcher.cs b/src/Libraries/SmartStore.Services/Media/Search/MediaSearcher.cs new file mode 100644 index 0000000000..ba0c18c8e4 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Search/MediaSearcher.cs @@ -0,0 +1,299 @@ +using System; +using System.Collections.Generic; +using System.Data.Entity; +using System.Linq; +using System.Linq.Dynamic.Core; +using SmartStore.Linq; +using SmartStore.Core; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.Infrastructure.DependencyManagement; +using System.Linq.Expressions; + +namespace SmartStore.Services.Media +{ + public class MediaSearcher : IMediaSearcher + { + private readonly IRepository _fileRepo; + private readonly Work _folderService; + + public MediaSearcher( + IRepository fileRepo, + Work folderService) + { + _fileRepo = fileRepo; + _folderService = folderService; + } + + public virtual IPagedList SearchFiles(MediaSearchQuery query, MediaLoadFlags flags = MediaLoadFlags.AsNoTracking) + { + var q = PrepareQuery(query, flags); + return new PagedList(q, query.PageIndex, query.PageSize); + } + + public virtual IQueryable PrepareQuery(MediaSearchQuery query, MediaLoadFlags flags) + { + Guard.NotNull(query, nameof(query)); + + var q = _fileRepo.Table; + bool? shouldIncludeDeleted = false; + + // Folder + if (query.FolderId > 0) + { + if (query.DeepSearch) + { + var folderIds = _folderService.Value.GetNodesFlattened(query.FolderId.Value, true).Select(x => x.Id).ToArray(); + q = q.Where(x => x.FolderId != null && folderIds.Contains(x.FolderId.Value)); + } + else + { + q = q.Where(x => x.FolderId == query.FolderId); + } + } + else if (query.FolderId < 0) + { + // Special folders + if (query.FolderId == (int)SpecialMediaFolder.AllFiles) + { + shouldIncludeDeleted = null; + } + else if (query.FolderId == (int)SpecialMediaFolder.Trash) + { + shouldIncludeDeleted = true; + } + else if (query.FolderId == (int)SpecialMediaFolder.Orphans) + { + // Get ids of untrackable folders, 'cause no orphan check can be made for them. + var untrackableFolderIds = _folderService.Value.GetRootNode() + .SelectNodes(x => !x.Value.CanDetectTracks) + .Select(x => x.Value.Id) + .ToArray(); + + q = q.Where(x => x.FolderId > 0 && !untrackableFolderIds.Contains(x.FolderId.Value) && !x.Tracks.Any()); + } + else if (query.FolderId == (int)SpecialMediaFolder.TransientFiles) + { + q = q.Where(x => x.IsTransient); + } + else if (query.FolderId == (int)SpecialMediaFolder.UnassignedFiles) + { + q = q.Where(x => x.FolderId == null); + } + } + else + { + // (perf) Composite index + q = q.Where(x => x.FolderId == null || x.FolderId.HasValue); + } + + q = ApplyFilterQuery(query, q); + + if (query.Deleted == null && shouldIncludeDeleted.HasValue) + { + q = q.Where(x => x.Deleted == shouldIncludeDeleted.Value); + } + + // Sorting + var ordering = query.SortBy.NullEmpty() ?? "Id"; + if (query.SortDesc) ordering += " descending"; + q = q.OrderBy(ordering); + + return ApplyLoadFlags(q, flags); + } + + public virtual IQueryable ApplyFilterQuery(MediaFilesFilter filter, IQueryable sourceQuery = null) + { + Guard.NotNull(filter, nameof(filter)); + + var q = sourceQuery ?? _fileRepo.Table; + + // Term + if (filter.Term.HasValue() && filter.Term != "*") + { + // Convert file pattern to SQL 'LIKE' expression + q = ApplySearchTerm(q, filter.Term, filter.IncludeAltForTerm, filter.ExactMatch); + } + + // MediaType + if (filter.MediaTypes != null && filter.MediaTypes.Length > 0) + { + if (filter.MediaTypes.Length == 1) + { + var right = filter.MediaTypes[0]; + q = q.Where(x => x.MediaType == right); + } + else if (filter.MediaTypes.Length > 1) + { + q = q.Where(x => filter.MediaTypes.Contains(x.MediaType)); + } + else + { + // (perf) Composite index + q = q.Where(x => !string.IsNullOrEmpty(x.MediaType)); + } + } + + // Extension + if (filter.Extensions != null && filter.Extensions.Length > 0) + { + if (filter.Extensions.Length == 1) + { + var right = filter.Extensions[0]; + q = q.Where(x => x.Extension == right); + } + else if (filter.Extensions.Length > 1) + { + q = q.Where(x => filter.Extensions.Contains(x.Extension)); + } + else + { + // (perf) Composite index + q = q.Where(x => !string.IsNullOrEmpty(x.Extension)); + } + } + + // Tags + if (filter.Tags != null && filter.Tags.Length > 0) + { + q = q.Where(x => x.Tags.Any(t => filter.Tags.Contains(t.Id))); + } + + // Image dimension + if (filter.Dimensions != null && filter.Dimensions.Length > 0) + { + var predicates = new List>>(5); + + foreach (var dim in filter.Dimensions.OrderBy(x => x).Distinct()) + { + if (dim == ImageDimension.VerySmall) + predicates.Add(x => x.PixelSize > 0 && x.PixelSize <= 50000); + else if (dim == ImageDimension.Small) + predicates.Add(x => x.PixelSize > 50000 && x.PixelSize <= 250000); + else if (dim == ImageDimension.Medium) + predicates.Add(x => x.PixelSize > 250000 && x.PixelSize <= 1000000); + else if (dim == ImageDimension.Large) + predicates.Add(x => x.PixelSize > 1000000 && x.PixelSize <= 2000000); + else if (dim == ImageDimension.VeryLarge) + predicates.Add(x => x.PixelSize > 2000000); + } + + if (predicates.Any()) + { + var predicate = PredicateBuilder.New(predicates.First()); + for (var i = 1; i < predicates.Count; i++) + { + predicate = PredicateBuilder.Or(predicate, predicates[i]); + } + q = q.Where(predicate); + } + } + + // Deleted + if (filter.Deleted != null) + { + q = q.Where(x => x.Deleted == filter.Deleted.Value); + } + + #region Currently unindexed + + // MimeType + if (filter.MimeTypes != null && filter.MimeTypes.Length > 0) + { + if (filter.MimeTypes.Length == 1) + { + var right = filter.MimeTypes[0]; + q = q.Where(x => x.MimeType == right); + } + else if (filter.MimeTypes.Length > 1) + { + q = q.Where(x => filter.MimeTypes.Contains(x.MimeType)); + } + } + + // Hidden + if (filter.Hidden != null) + { + q = q.Where(x => x.Hidden == filter.Hidden.Value); + } + + #endregion + + return q; + } + + public virtual IQueryable ApplyLoadFlags(IQueryable query, MediaLoadFlags flags) + { + if (flags == MediaLoadFlags.None) + { + return query; + } + + if (flags.HasFlag(MediaLoadFlags.AsNoTracking)) + { + query = query.AsNoTracking(); + } + + if (flags.HasFlag(MediaLoadFlags.WithBlob)) + { + query = query.Include(x => x.MediaStorage); + } + + if (flags.HasFlag(MediaLoadFlags.WithFolder)) + { + query = query.Include(x => x.Folder); + } + + if (flags.HasFlag(MediaLoadFlags.WithTags)) + { + query = query.Include(x => x.Tags); + } + + if (flags.HasFlag(MediaLoadFlags.WithTracks)) + { + query = query.Include(x => x.Tracks); + } + + return query; + } + + + private IQueryable ApplySearchTerm(IQueryable query, string term, bool includeAlt, bool exactMatch) + { + var hasAnyCharToken = term.IndexOf('*') > -1; + var hasSingleCharToken = term.IndexOf('?') > -1; + var hasAnyWildcard = hasAnyCharToken || hasSingleCharToken; + + if (!hasAnyWildcard) + { + return !includeAlt + ? (exactMatch ? query.Where(x => x.Name == term) : query.Where(x => x.Name.Contains(term))) + : (exactMatch ? query.Where(x => x.Name == term || x.Alt == term) : query.Where(x => x.Name.Contains(term) || x.Alt.Contains(term))); + } + else + { + // Convert file pattern to SQL LIKE expression: + // my*new_file-?.png > my%new/_file-_.png + + var hasUnderscore = term.IndexOf('_') > -1; + + if (hasUnderscore) + { + term = term.Replace("_", "/_"); + } + if (hasAnyCharToken) + { + term = term.Replace('*', '%'); + } + if (hasSingleCharToken) + { + term = term.Replace('?', '_'); + } + + return !includeAlt + ? query.Where(x => DbFunctions.Like(x.Name, term, "/")) + : query.Where(x => DbFunctions.Like(x.Name, term, "/") || DbFunctions.Like(x.Alt, term, "/")); + } + } + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Media/Storage/DatabaseMediaStorageProvider.cs b/src/Libraries/SmartStore.Services/Media/Storage/DatabaseMediaStorageProvider.cs index 32c4cae971..0fd3e2f053 100644 --- a/src/Libraries/SmartStore.Services/Media/Storage/DatabaseMediaStorageProvider.cs +++ b/src/Libraries/SmartStore.Services/Media/Storage/DatabaseMediaStorageProvider.cs @@ -1,161 +1,304 @@ -using System.IO; +using System; +using System.Data.Common; +using System.Data.Entity; +using System.Data.Entity.Infrastructure; +using System.Data.SqlClient; +using System.IO; using System.Threading.Tasks; -using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Media; using SmartStore.Core.Plugins; +using SmartStore.Data; +using SmartStore.Data.Utilities; namespace SmartStore.Services.Media.Storage { - [SystemName("MediaStorage.SmartStoreDatabase")] - [FriendlyName("Database")] - [DisplayOrder(0)] - public class DatabaseMediaStorageProvider : IMediaStorageProvider, ISupportsMediaMoving - { - private readonly IDbContext _dbContext; - private readonly IRepository _mediaStorageRepository; - - public DatabaseMediaStorageProvider( - IDbContext dbContext, - IRepository mediaStorageRepository) - { - _dbContext = dbContext; - _mediaStorageRepository = mediaStorageRepository; - } - - public static string SystemName - { - get { return "MediaStorage.SmartStoreDatabase"; } - } - - public Stream OpenRead(MediaItem media) - { - Guard.NotNull(media, nameof(media)); - - return media.Entity?.MediaStorage?.Data?.ToStream(); - } - - public byte[] Load(MediaItem media) - { - Guard.NotNull(media, nameof(media)); - - return media.Entity?.MediaStorage?.Data ?? new byte[0]; - } - - public Task LoadAsync(MediaItem media) - { - return Task.FromResult(Load(media)); - } - - public void Save(MediaItem media, byte[] data) - { - Guard.NotNull(media, nameof(media)); - - if (data == null || data.LongLength == 0) - { - // remove media storage if any - if ((media.Entity.MediaStorageId ?? 0) != 0 && media.Entity != null && media.Entity.MediaStorage != null) - { - _mediaStorageRepository.Delete(media.Entity.MediaStorage); - } - } - else - { - if (media.Entity.MediaStorage == null) - { - // entity has no media storage -> insert - var newStorage = new MediaStorage { Data = data }; - - _mediaStorageRepository.Insert(newStorage); - - if (newStorage.Id == 0) - { - // actually we should never get here - _dbContext.SaveChanges(); - } - - media.Entity.MediaStorageId = newStorage.Id; - - //// Required because during import the ChangeTracker doesn't treat media.Entity as changed entry. - //_dbContext.ChangeState((BaseEntity)media.Entity, System.Data.Entity.EntityState.Modified); - - _dbContext.SaveChanges(); - } - else - { - // update existing media storage - media.Entity.MediaStorage.Data = data; - - _mediaStorageRepository.Update(media.Entity.MediaStorage); - } - } - } - - public Task SaveAsync(MediaItem media, byte[] data) - { - Save(media, data); - return Task.FromResult(0); - } - - public void Remove(params MediaItem[] medias) - { - foreach (var media in medias) - { - if ((media.Entity.MediaStorageId ?? 0) != 0) - { - // this also nulls media.Entity.MediaStorageId - _mediaStorageRepository.Delete(media.Entity.MediaStorage); - } - } - } - - - public void MoveTo(ISupportsMediaMoving target, MediaMoverContext context, MediaItem media) - { - Guard.NotNull(target, nameof(target)); - Guard.NotNull(context, nameof(context)); - Guard.NotNull(media, nameof(media)); - - if (media.Entity.MediaStorage != null) - { - // let target store data (into a file for example) - target.Receive(context, media, media.Entity.MediaStorage.Data); - - // remove picture binary from DB - try - { - _mediaStorageRepository.Delete(media.Entity.MediaStorage); - } - catch { } - - media.Entity.MediaStorageId = null; - - context.ShrinkDatabase = true; - } - } - - public void Receive(MediaMoverContext context, MediaItem media, byte[] data) - { - Guard.NotNull(context, nameof(context)); - Guard.NotNull(media, nameof(media)); - - // store data for later bulk commit - if (data != null && data.LongLength > 0) - { - // requires autoDetectChanges set to true or remove explicit entity detaching - media.Entity.MediaStorage = new MediaStorage { Data = data }; - } - } - - public Task ReceiveAsync(MediaMoverContext context, MediaItem media, byte[] data) - { - Receive(context, media, data); - return Task.FromResult(0); - } - - public void OnCompleted(MediaMoverContext context, bool succeeded) - { - // nothing to do - } - } + [SystemName("MediaStorage.SmartStoreDatabase")] + [FriendlyName("Database")] + [DisplayOrder(0)] + public class DatabaseMediaStorageProvider : IMediaStorageProvider, ISupportsMediaMoving + { + class MediaStorageBlobStream : SqlBlobStream + { + public MediaStorageBlobStream( + IDbConnectionFactory connectionFactory, + string connectionString, + int mediaStorageId) + : base(connectionFactory, connectionString, "MediaStorage", "Data", "Id", mediaStorageId) + { + } + } + + private readonly IDbContext _dbContext; + private readonly IRepository _mediaStorageRepo; + private readonly IRepository _mediaFileRepo; + private readonly Lazy _dataProvider; + + private readonly bool _isSqlServer; + + public DatabaseMediaStorageProvider( + IDbContext dbContext, + IRepository mediaStorageRepo, + IRepository mediaFileRepo, + Lazy dataProvider) + { + _dbContext = dbContext; + _mediaStorageRepo = mediaStorageRepo; + _mediaFileRepo = mediaFileRepo; + _dataProvider = dataProvider; + _isSqlServer = DataSettings.Current.IsSqlServer; + } + + public static string SystemName => "MediaStorage.SmartStoreDatabase"; + + private Stream CreateBlobStream(int mediaStorageId) + { + return new MediaStorageBlobStream(_dataProvider.Value.GetConnectionFactory(), DataSettings.Current.DataConnectionString, mediaStorageId); + } + + public bool IsCloudStorage { get; } = false; + + public string GetPublicUrl(MediaFile mediaFile) + { + return null; + } + + public long GetSize(MediaFile mediaFile) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + + var id = mediaFile.MediaStorageId ?? 0; + if (id == 0) + { + return 0; + } + + if (_isSqlServer) + { + using (var stream = CreateBlobStream(id)) + { + return stream.Length; + } + } + else + { + return mediaFile.MediaStorage?.Data?.LongLength ?? 0; + } + } + + public Stream OpenRead(MediaFile mediaFile) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + + if (_isSqlServer) + { + if (mediaFile.MediaStorageId > 0) + { + return CreateBlobStream(mediaFile.MediaStorageId.Value); + } + + return null; + } + else + { + return mediaFile.MediaStorage?.Data?.ToStream(); + } + } + + public byte[] Load(MediaFile mediaFile) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + + if (mediaFile.MediaStorageId == null) + { + return new byte[0]; + } + + using (var stream = CreateBlobStream(mediaFile.MediaStorageId.Value)) + { + return stream.ToByteArray(); + } + } + + public async Task LoadAsync(MediaFile mediaFile) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + + if (mediaFile.MediaStorageId == null) + { + return new byte[0]; + } + + using (var stream = CreateBlobStream(mediaFile.MediaStorageId.Value)) + { + return await stream.ToByteArrayAsync(); + } + } + + public void Save(MediaFile mediaFile, MediaStorageItem item) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + + if (item == null) + { + mediaFile.ApplyBlob(null); + } + else + { + SaveInternal(mediaFile, item); + } + + _mediaFileRepo.Update(mediaFile); + } + + private void SaveInternal(MediaFile mediaFile, MediaStorageItem item) + { + using (item) + { + if (_isSqlServer) + { + SaveFast(mediaFile, item); + } + else + { + var buffer = item.SourceStream.ToByteArray(); + mediaFile.ApplyBlob(buffer); + mediaFile.Size = buffer.Length; + } + } + + _mediaFileRepo.Update(mediaFile); + } + + public async Task SaveAsync(MediaFile mediaFile, MediaStorageItem item) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + + if (item == null) + { + mediaFile.ApplyBlob(null); + } + else + { + await SaveInternalAsync(mediaFile, item); + } + + _mediaFileRepo.Update(mediaFile); + } + + private async Task SaveInternalAsync(MediaFile mediaFile, MediaStorageItem item) + { + using (item) + { + if (_isSqlServer) + { + SaveFast(mediaFile, item); + } + else + { + var buffer = await item.SourceStream.ToByteArrayAsync(); + mediaFile.ApplyBlob(buffer); + mediaFile.Size = buffer.Length; + } + } + + _mediaFileRepo.Update(mediaFile); + } + + private int SaveFast(MediaFile mediaFile, MediaStorageItem item) + { + var sourceStream = item.SourceStream; + mediaFile.Size = (int)sourceStream.Length; + + if (mediaFile.MediaStorageId == null) + { + // Insert new blob + var sql = "INSERT INTO [MediaStorage] (Data) Values(@p0)"; + mediaFile.MediaStorageId = ((DbContext)_dbContext).InsertInto(sql, sourceStream); + return mediaFile.MediaStorageId.Value; + } + else + { + // Update existing blob + var sql = "UPDATE [MediaStorage] SET [Data] = @p0 WHERE Id = @p1"; + _dbContext.ExecuteSqlCommand(sql, false, null, sourceStream, mediaFile.MediaStorageId.Value); + return mediaFile.MediaStorageId.Value; + } + } + + public void Remove(params MediaFile[] mediaFiles) + { + using (var scope = new DbContextScope(ctx: _mediaFileRepo.Context, autoCommit: false)) + { + foreach (var media in mediaFiles) + { + media.ApplyBlob(null); + } + + scope.Commit(); + } + } + + public void ChangeExtension(MediaFile mediaFile, string extension) + { + // Do nothing + } + + public void MoveTo(ISupportsMediaMoving target, MediaMoverContext context, MediaFile mediaFile) + { + Guard.NotNull(target, nameof(target)); + Guard.NotNull(context, nameof(context)); + Guard.NotNull(mediaFile, nameof(mediaFile)); + + if (mediaFile.MediaStorageId != null) + { + // Let target store data (into a file for example) + target.Receive(context, mediaFile, OpenRead(mediaFile)); + + // Remove picture binary from DB + try + { + _mediaStorageRepo.Delete(mediaFile.MediaStorageId.Value); + //mediaFile.MediaStorageId = null; + //mediaFile.MediaStorage = null; + + _mediaFileRepo.Update(mediaFile); + } + catch { } + + context.ShrinkDatabase = true; + } + } + + public void Receive(MediaMoverContext context, MediaFile mediaFile, Stream stream) + { + Guard.NotNull(context, nameof(context)); + Guard.NotNull(mediaFile, nameof(mediaFile)); + + // Store data for later bulk commit + if (stream != null && stream.Length > 0) + { + // Requires AutoDetectChanges set to true or remove explicit entity detaching + Save(mediaFile, MediaStorageItem.FromStream(stream)); + } + } + + public async Task ReceiveAsync(MediaMoverContext context, MediaFile mediaFile, Stream stream) + { + Guard.NotNull(context, nameof(context)); + Guard.NotNull(mediaFile, nameof(mediaFile)); + + // Store data for later bulk commit + if (stream != null && stream.Length > 0) + { + // Requires AutoDetectChanges set to true or remove explicit entity detaching + await SaveAsync(mediaFile, MediaStorageItem.FromStream(stream)); + } + } + + public void OnCompleted(MediaMoverContext context, bool succeeded) + { + // nothing to do + } + } } diff --git a/src/Libraries/SmartStore.Services/Media/Storage/FileSystemMediaStorageProvider.cs b/src/Libraries/SmartStore.Services/Media/Storage/FileSystemMediaStorageProvider.cs index f7f3acc322..232c3c0aee 100644 --- a/src/Libraries/SmartStore.Services/Media/Storage/FileSystemMediaStorageProvider.cs +++ b/src/Libraries/SmartStore.Services/Media/Storage/FileSystemMediaStorageProvider.cs @@ -1,199 +1,298 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; +using SmartStore.Core.Data; using SmartStore.Core.Domain.Media; using SmartStore.Core.IO; using SmartStore.Core.Plugins; +using SmartStore.Services.Media.Imaging; namespace SmartStore.Services.Media.Storage { - [SystemName("MediaStorage.SmartStoreFileSystem")] - [FriendlyName("File system")] - [DisplayOrder(1)] - public class FileSystemMediaStorageProvider : IMediaStorageProvider, ISupportsMediaMoving - { - private readonly IMediaFileSystem _fileSystem; - - public FileSystemMediaStorageProvider(IMediaFileSystem fileSystem) - { - _fileSystem = fileSystem; - } - - public static string SystemName - { - get { return "MediaStorage.SmartStoreFileSystem"; } - } - - protected string GetPath(MediaItem media) - { - var fileName = media.GetFileName(); - - var picture = media.Entity as Picture; - if (picture != null) - { - var subfolder = fileName.Substring(0, ImageCache.MaxDirLength); - fileName = _fileSystem.Combine(subfolder, fileName); - } - - return _fileSystem.Combine(media.Path, fileName); - } - - public Stream OpenRead(MediaItem media) - { - var file = _fileSystem.GetFile(GetPath(media)); - - return file.Exists ? file.OpenRead() : null; - } - - public byte[] Load(MediaItem media) - { - Guard.NotNull(media, nameof(media)); - - var filePath = GetPath(media); - return _fileSystem.ReadAllBytes(filePath) ?? new byte[0]; - } - - public async Task LoadAsync(MediaItem media) - { - Guard.NotNull(media, nameof(media)); - - var filePath = GetPath(media); - return (await _fileSystem.ReadAllBytesAsync(filePath)) ?? new byte[0]; - } - - public void Save(MediaItem media, byte[] data) - { - Guard.NotNull(media, nameof(media)); - - // TODO: (?) if the new file extension differs from the old one then the old file never gets deleted - - var filePath = GetPath(media); - - if (data != null && data.LongLength != 0) - { - _fileSystem.WriteAllBytes(filePath, data); - } - else if (_fileSystem.FileExists(filePath)) - { - _fileSystem.DeleteFile(filePath); - } - } - - public async Task SaveAsync(MediaItem media, byte[] data) - { - Guard.NotNull(media, nameof(media)); - - // TODO: (?) if the new file extension differs from the old one then the old file never gets deleted - - var filePath = GetPath(media); - - if (data != null && data.LongLength != 0) - { - await _fileSystem.WriteAllBytesAsync(filePath, data); - } - else if (_fileSystem.FileExists(filePath)) - { - _fileSystem.DeleteFile(filePath); - } - } - - public void Remove(params MediaItem[] medias) - { - foreach (var media in medias) - { - var filePath = GetPath(media); - _fileSystem.DeleteFile(filePath); - } - } - - - public void MoveTo(ISupportsMediaMoving target, MediaMoverContext context, MediaItem media) - { - Guard.NotNull(target, nameof(target)); - Guard.NotNull(context, nameof(context)); - Guard.NotNull(media, nameof(media)); - - var filePath = GetPath(media); - - try - { - // read data from file - var data = _fileSystem.ReadAllBytes(filePath); - - // let target store data (into database for example) - target.Receive(context, media, data); - - // remember file path: we must be able to rollback IO operations on transaction failure - context.AffectedFiles.Add(filePath); - } - catch (Exception exception) - { - Debug.WriteLine(exception.Message); - } - } - - public void Receive(MediaMoverContext context, MediaItem media, byte[] data) - { - Guard.NotNull(context, nameof(context)); - Guard.NotNull(media, nameof(media)); - - // store data into file - if (data != null && data.LongLength != 0) - { - var filePath = GetPath(media); - - if (!_fileSystem.FileExists(filePath)) - { - // TBD: (mc) We only save the file if it doesn't exist yet. - // This should save time and bandwidth in the case where the target - // is a cloud based file system (like Azure BLOB). - // In such a scenario it'd be advisable to copy the files manually - // with other - maybe more performant - tools before performing the provider switch. - _fileSystem.WriteAllBytes(filePath, data); - context.AffectedFiles.Add(filePath); - } - } - } - - public async Task ReceiveAsync(MediaMoverContext context, MediaItem media, byte[] data) - { - Guard.NotNull(context, nameof(context)); - Guard.NotNull(media, nameof(media)); - - // store data into file - if (data != null && data.LongLength != 0) - { - var filePath = GetPath(media); - - await _fileSystem.WriteAllBytesAsync(filePath, data); - - context.AffectedFiles.Add(filePath); - } - } - - public void OnCompleted(MediaMoverContext context, bool succeeded) - { - if (context.AffectedFiles.Any()) - { - var toFileSystem = context.TargetSystemName.IsCaseInsensitiveEqual(SystemName); - - if ((!toFileSystem && succeeded) || (toFileSystem && !succeeded)) - { - // FS > DB sucessful OR DB > FS failed: delete all physical files - // run a background task for the deletion of files (fire & forget) - - Task.Factory.StartNew(state => - { - var files = state as string[]; - foreach (var file in files) - { - _fileSystem.DeleteFile(file); - } - }, context.AffectedFiles.ToArray()).ConfigureAwait(false); - } - } - } - } + [SystemName("MediaStorage.SmartStoreFileSystem")] + [FriendlyName("File system")] + [DisplayOrder(1)] + public class FileSystemMediaStorageProvider : IMediaStorageProvider, ISupportsMediaMoving + { + const string MediaRootPath = "Storage"; + + private readonly IMediaFileSystem _fileSystem; + private readonly IDictionary _pathCache = new Dictionary(); + + public FileSystemMediaStorageProvider(IMediaFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + public static string SystemName => "MediaStorage.SmartStoreFileSystem"; + + public bool IsCloudStorage => _fileSystem.IsCloudStorage; + + public string GetPublicUrl(MediaFile mediaFile) + { + return _fileSystem.GetPublicUrl(GetPath(mediaFile), true); + } + + protected string GetPath(MediaFile mediaFile) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + + if (_pathCache.TryGetValue(mediaFile.Id, out var path)) + { + return path; + } + + var ext = mediaFile.Extension.NullEmpty() ?? MimeTypes.MapMimeTypeToExtension(mediaFile.MimeType); + + var fileName = mediaFile.Id.ToString(ImageCache.IdFormatString).Grow(ext, "."); + var subfolder = fileName.Substring(0, ImageCache.MaxDirLength); + + path = _fileSystem.Combine(subfolder, fileName); + path = _fileSystem.Combine(MediaRootPath, path); + + _pathCache[mediaFile.Id] = path; + + return path; + } + + public long GetSize(MediaFile mediaFile) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + + if (mediaFile.Size > 0) + { + return mediaFile.Size; + } + + var file = _fileSystem.GetFile(GetPath(mediaFile)); + if (file.Exists) + { + // Hopefully a future commit will save this + mediaFile.Size = (int)file.Size; + } + + return mediaFile.Size; + } + + public Stream OpenRead(MediaFile mediaFile) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + + var file = _fileSystem.GetFile(GetPath(mediaFile)); + return file.Exists ? file.OpenRead() : null; + } + + public byte[] Load(MediaFile mediaFile) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + + var filePath = GetPath(mediaFile); + return _fileSystem.ReadAllBytes(filePath) ?? new byte[0]; + } + + public async Task LoadAsync(MediaFile mediaFile) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + + var filePath = GetPath(mediaFile); + return (await _fileSystem.ReadAllBytesAsync(filePath)) ?? new byte[0]; + } + + public void Save(MediaFile mediaFile, MediaStorageItem item) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + + // TODO: (?) if the new file extension differs from the old one then the old file never gets deleted + + var filePath = GetPath(mediaFile); + + if (item != null) + { + // Create folder if it does not exist yet + var dir = Path.GetDirectoryName(filePath); + if (!_fileSystem.FolderExists(dir)) + { + _fileSystem.CreateFolder(dir); + } + + using (item) + { + using var outStream = _fileSystem.GetFile(filePath).OpenWrite(); + item.SaveTo(outStream, mediaFile); + } + } + else if (_fileSystem.FileExists(filePath)) + { + // Remove media storage if any + _fileSystem.DeleteFile(filePath); + } + } + + public async Task SaveAsync(MediaFile mediaFile, MediaStorageItem item) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + + // TODO: (?) if the new file extension differs from the old one then the old file never gets deleted + + var filePath = GetPath(mediaFile); + + if (item != null) + { + // Create folder if it does not exist yet + var dir = Path.GetDirectoryName(filePath); + if (!_fileSystem.FolderExists(dir)) + { + _fileSystem.CreateFolder(dir); + } + + using (item) + { + using var outStream = _fileSystem.GetFile(filePath).OpenWrite(); + await item.SaveToAsync(outStream, mediaFile); + } + } + else if (_fileSystem.FileExists(filePath)) + { + // Remove media storage if any + _fileSystem.DeleteFile(filePath); + } + } + + public void Remove(params MediaFile[] mediaFiles) + { + foreach (var media in mediaFiles) + { + _fileSystem.DeleteFile(GetPath(media)); + } + } + + public void ChangeExtension(MediaFile mediaFile, string extension) + { + Guard.NotNull(mediaFile, nameof(mediaFile)); + Guard.NotEmpty(extension, nameof(extension)); + + var sourcePath = GetPath(mediaFile); + var newPath = Path.ChangeExtension(sourcePath, extension); + + _fileSystem.RenameFile(sourcePath, newPath); + } + + public void MoveTo(ISupportsMediaMoving target, MediaMoverContext context, MediaFile mediaFile) + { + Guard.NotNull(target, nameof(target)); + Guard.NotNull(context, nameof(context)); + Guard.NotNull(mediaFile, nameof(mediaFile)); + + var filePath = GetPath(mediaFile); + + try + { + // Let target store data (into database for example) + target.Receive(context, mediaFile, OpenRead(mediaFile)); + + // Remember file path: we must be able to rollback IO operations on transaction failure + context.AffectedFiles.Add(filePath); + } + catch (Exception exception) + { + Debug.WriteLine(exception.Message); + } + } + + public void Receive(MediaMoverContext context, MediaFile mediaFile, Stream stream) + { + Guard.NotNull(context, nameof(context)); + Guard.NotNull(mediaFile, nameof(mediaFile)); + + // store data into file + if (stream != null && stream.Length > 0) + { + var filePath = GetPath(mediaFile); + + if (!_fileSystem.FileExists(filePath)) + { + // TBD: (mc) We only save the file if it doesn't exist yet. + // This should save time and bandwidth in the case where the target + // is a cloud based file system (like Azure BLOB). + // In such a scenario it'd be advisable to copy the files manually + // with other - maybe more performant - tools before performing the provider switch. + + // Create folder if it does not exist yet + var dir = Path.GetDirectoryName(filePath); + if (!_fileSystem.FolderExists(dir)) + { + _fileSystem.CreateFolder(dir); + } + + using (stream) + { + _fileSystem.SaveStream(filePath, stream); + } + + context.AffectedFiles.Add(filePath); + } + } + } + + public async Task ReceiveAsync(MediaMoverContext context, MediaFile mediaFile, Stream stream) + { + Guard.NotNull(context, nameof(context)); + Guard.NotNull(mediaFile, nameof(mediaFile)); + + // store data into file + if (stream != null && stream.Length > 0) + { + var filePath = GetPath(mediaFile); + + if (!_fileSystem.FileExists(filePath)) + { + // TBD: (mc) We only save the file if it doesn't exist yet. + // This should save time and bandwidth in the case where the target + // is a cloud based file system (like Azure BLOB). + // In such a scenario it'd be advisable to copy the files manually + // with other - maybe more performant - tools before performing the provider switch. + + // Create folder if it does not exist yet + var dir = Path.GetDirectoryName(filePath); + if (!_fileSystem.FolderExists(dir)) + { + _fileSystem.CreateFolder(dir); + } + + using (stream) + { + await _fileSystem.SaveStreamAsync(filePath, stream); + } + + context.AffectedFiles.Add(filePath); + } + } + } + + public void OnCompleted(MediaMoverContext context, bool succeeded) + { + if (context.AffectedFiles.Any()) + { + var toFileSystem = context.TargetSystemName.IsCaseInsensitiveEqual(SystemName); + + if ((!toFileSystem && succeeded) || (toFileSystem && !succeeded)) + { + // FS > DB sucessful OR DB > FS failed/aborted: delete all physical files. + // Run a background task for the deletion of files (fire & forget) + + Task.Factory.StartNew(state => + { + var files = state as string[]; + foreach (var file in files) + { + _fileSystem.DeleteFile(file); + } + }, context.AffectedFiles.ToArray()).ConfigureAwait(false); + } + } + } + } } diff --git a/src/Libraries/SmartStore.Services/Media/Storage/IMediaMover.cs b/src/Libraries/SmartStore.Services/Media/Storage/IMediaMover.cs index 3283aefe32..4bea7cd4e2 100644 --- a/src/Libraries/SmartStore.Services/Media/Storage/IMediaMover.cs +++ b/src/Libraries/SmartStore.Services/Media/Storage/IMediaMover.cs @@ -2,14 +2,14 @@ namespace SmartStore.Services.Media.Storage { - public interface IMediaMover - { - /// - /// Moves media items from one storage provider to another - /// - /// The source media storage provider - /// The target media storage provider - /// true success, failure - bool Move(Provider sourceProvider, Provider targetProvider); - } + public interface IMediaMover + { + /// + /// Moves media items from one storage provider to another + /// + /// The source media storage provider + /// The target media storage provider + /// true success, failure + bool Move(Provider sourceProvider, Provider targetProvider); + } } diff --git a/src/Libraries/SmartStore.Services/Media/Storage/IMediaStorageProvider.cs b/src/Libraries/SmartStore.Services/Media/Storage/IMediaStorageProvider.cs index 36302f7b38..eeb715cf05 100644 --- a/src/Libraries/SmartStore.Services/Media/Storage/IMediaStorageProvider.cs +++ b/src/Libraries/SmartStore.Services/Media/Storage/IMediaStorageProvider.cs @@ -1,47 +1,74 @@ using System.IO; using System.Threading.Tasks; +using SmartStore.Core.Domain.Media; using SmartStore.Core.Plugins; namespace SmartStore.Services.Media.Storage { - public interface IMediaStorageProvider : IProvider - { - /// - /// Opens the media item for reading - /// - /// Media storage item - Stream OpenRead(MediaItem media); - - /// - /// Loads media item data - /// - /// Media storage item - byte[] Load(MediaItem media); - - /// - /// Asynchronously loads media item data - /// - /// Media storage item - Task LoadAsync(MediaItem media); - - /// - /// Saves media item data - /// - /// Media storage item - /// New binary data - void Save(MediaItem media, byte[] data); - - /// - /// Asynchronously saves media item data - /// - /// Media storage item - /// New binary data - Task SaveAsync(MediaItem media, byte[] data); - - /// - /// Remove media storage item(s) - /// - /// Media storage items - void Remove(params MediaItem[] medias); - } + public interface IMediaStorageProvider : IProvider + { + /// + /// Gets a value indicating whether the provider saves data in a remote cloud storage (e.g. Azure) + /// + bool IsCloudStorage { get; } + + /// + /// Retrieves the public URL for a given file within the storage provider. + /// Returns null if the file is stored as a database BLOB. + /// + /// Media file item + /// The public URL. + string GetPublicUrl(MediaFile mediaFile); + + /// + /// Gets the size of the media item in bytes. + /// + /// Media file item + long GetSize(MediaFile mediaFile); + + /// + /// Opens the media item for reading + /// + /// Media file item + Stream OpenRead(MediaFile mediaFile); + + /// + /// Loads media item data + /// + /// Media storage item + byte[] Load(MediaFile mediaFile); + + /// + /// Asynchronously loads media item data + /// + /// Media file item + Task LoadAsync(MediaFile mediaFile); + + /// + /// Saves media item data + /// + /// Media file item + /// The source item + void Save(MediaFile mediaFile, MediaStorageItem item); + + /// + /// Asynchronously saves media item data + /// + /// Media file item + /// The source item + Task SaveAsync(MediaFile mediaFile, MediaStorageItem item); + + /// + /// Remove media storage item(s) + /// + /// Media file items + void Remove(params MediaFile[] mediaFiles); + + /// + /// Changes the extension of the stored file if the provider supports + /// + /// Media file item + /// The nex file extension + void ChangeExtension(MediaFile mediaFile, string extension); + } } diff --git a/src/Libraries/SmartStore.Services/Media/Storage/ISupportsMediaMoving.cs b/src/Libraries/SmartStore.Services/Media/Storage/ISupportsMediaMoving.cs index e21f745e74..7a658c1f2a 100644 --- a/src/Libraries/SmartStore.Services/Media/Storage/ISupportsMediaMoving.cs +++ b/src/Libraries/SmartStore.Services/Media/Storage/ISupportsMediaMoving.cs @@ -1,38 +1,40 @@ -using System.Threading.Tasks; +using System.IO; +using System.Threading.Tasks; +using SmartStore.Core.Domain.Media; namespace SmartStore.Services.Media.Storage { - public interface ISupportsMediaMoving - { - /// - /// Moves a media item to the target provider - /// - /// Target provider - /// Media storage mover context - /// Media storage item - void MoveTo(ISupportsMediaMoving target, MediaMoverContext context, MediaItem media); + public interface ISupportsMediaMoving + { + /// + /// Moves a media item to the target provider + /// + /// Target provider + /// Media storage mover context + /// Media file item + void MoveTo(ISupportsMediaMoving target, MediaMoverContext context, MediaFile mediaFile); - /// - /// Data received by the source provider to be stored by the target provider - /// - /// Media storage mover context - /// Media storage item - /// Binary data - void Receive(MediaMoverContext context, MediaItem media, byte[] data); + /// + /// Data received by the source provider to be stored by the target provider + /// + /// Media storage mover context + /// Media file item + /// Source stream + void Receive(MediaMoverContext context, MediaFile mediaFile, Stream stream); - /// - /// Data received by the source provider to be stored by the target provider (async) - /// - /// Media storage mover context - /// Media storage item - /// Binary data - Task ReceiveAsync(MediaMoverContext context, MediaItem media, byte[] data); + /// + /// Data received by the source provider to be stored by the target provider (async) + /// + /// Media storage mover context + /// Media file item + /// Source stream + Task ReceiveAsync(MediaMoverContext context, MediaFile mediaFile, Stream stream); - /// - /// Called when batch media moving completes - /// - /// Media storage mover context - /// Whether media moving succeeded - void OnCompleted(MediaMoverContext context, bool succeeded); - } + /// + /// Called when batch media moving completes + /// + /// Media storage mover context + /// Whether media moving succeeded + void OnCompleted(MediaMoverContext context, bool succeeded); + } } diff --git a/src/Libraries/SmartStore.Services/Media/Storage/MediaItem.cs b/src/Libraries/SmartStore.Services/Media/Storage/MediaItem.cs deleted file mode 100644 index d71c20df3b..0000000000 --- a/src/Libraries/SmartStore.Services/Media/Storage/MediaItem.cs +++ /dev/null @@ -1,27 +0,0 @@ -using SmartStore.Core.Domain.Media; - -namespace SmartStore.Services.Media.Storage -{ - public partial class MediaItem - { - /// - /// Entity of the media storage item - /// - public IHasMedia Entity { get; set; } - - /// - /// Storage path - /// - public string Path { get; set; } - - /// - /// Mime type - /// - public string MimeType { get; set; } - - /// - /// File extension - /// - public string FileExtension { get; set; } - } -} diff --git a/src/Libraries/SmartStore.Services/Media/Storage/MediaMover.cs b/src/Libraries/SmartStore.Services/Media/Storage/MediaMover.cs index 825f871858..bd5b52efcf 100644 --- a/src/Libraries/SmartStore.Services/Media/Storage/MediaMover.cs +++ b/src/Libraries/SmartStore.Services/Media/Storage/MediaMover.cs @@ -1,5 +1,7 @@ using System; +using System.Data.Entity; using System.Linq; +using System.Web.UI; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Media; @@ -7,167 +9,116 @@ using SmartStore.Core.Localization; using SmartStore.Core.Logging; using SmartStore.Core.Plugins; +using SmartStore.Data.Utilities; namespace SmartStore.Services.Media.Storage { - public class MediaMover : IMediaMover - { - private const int PAGE_SIZE = 100; - - private readonly IRepository _pictureRepository; - private readonly IRepository _downloadRepository; - private readonly IRepository _queuedEmailAttachmentRepository; - private readonly ICommonServices _services; - private readonly ILogger _logger; - - public MediaMover( - IRepository pictureRepository, - IRepository downloadRepository, - IRepository queuedEmailAttachmentRepository, - ICommonServices services, - ILogger logger) - { - _pictureRepository = pictureRepository; - _downloadRepository = downloadRepository; - _queuedEmailAttachmentRepository = queuedEmailAttachmentRepository; - _services = services; - _logger = logger; - - T = NullLocalizer.Instance; - } - - public Localizer T { get; set; } - - protected virtual void PageEntities(IOrderedQueryable query, Action moveEntity) where TEntity : BaseEntity, IHasMedia - { - var pageIndex = 0; - IPagedList entities = null; - - do - { - if (entities != null) - { - // detach all entities from previous page to save memory - _services.DbContext.DetachEntities(entities); - entities.Clear(); - entities = null; - } - - // load max 100 entities at once - entities = new PagedList(query, pageIndex++, PAGE_SIZE); - - entities.Each(x => moveEntity(x)); - - // save the current batch to database - _services.DbContext.SaveChanges(); - } - while (entities.HasNextPage); - } - - public virtual bool Move(Provider sourceProvider, Provider targetProvider) - { - Guard.NotNull(sourceProvider, nameof(sourceProvider)); - Guard.NotNull(targetProvider, nameof(targetProvider)); - - var success = false; - var utcNow = DateTime.UtcNow; - var context = new MediaMoverContext(sourceProvider.Metadata.SystemName, targetProvider.Metadata.SystemName); - - var source = sourceProvider.Value as ISupportsMediaMoving; - var target = targetProvider.Value as ISupportsMediaMoving; - - // source and target must support media storage moving - if (source == null) - { - throw new ArgumentException(T("Admin.Media.StorageMovingNotSupported", sourceProvider.Metadata.SystemName)); - } - - if (target == null) - { - throw new ArgumentException(T("Admin.Media.StorageMovingNotSupported", targetProvider.Metadata.SystemName)); - } - - // source and target provider must not be equal - if (sourceProvider.Metadata.SystemName.IsCaseInsensitiveEqual(targetProvider.Metadata.SystemName)) - { - throw new ArgumentException(T("Admin.Media.CannotMoveToSameProvider")); - } - - // we are about to process data in chunks but want to commit ALL at once when ALL chunks have been processed successfully. - // autoDetectChanges true required for newly inserted binary data. - using (var scope = new DbContextScope(ctx: _services.DbContext, autoDetectChanges: true, proxyCreation: false, validateOnSave: false, autoCommit: false)) - using (var transaction = _services.DbContext.BeginTransaction()) - { - try - { - // pictures - var queryPictures = _pictureRepository.Table - .Expand(x => x.MediaStorage) - .OrderBy(x => x.Id); - - PageEntities(queryPictures, picture => - { - // move item from source to target - source.MoveTo(target, context, picture.ToMedia()); - - picture.UpdatedOnUtc = utcNow; - ++context.MovedItems; - }); - - // downloads - var queryDownloads = _downloadRepository.Table - .Expand(x => x.MediaStorage) - .OrderBy(x => x.Id); - - PageEntities(queryDownloads, download => - { - source.MoveTo(target, context, download.ToMedia()); - - download.UpdatedOnUtc = utcNow; - ++context.MovedItems; - }); - - // queued email attachments - var queryQueuedEmailAttachments = _queuedEmailAttachmentRepository.Table - .Expand(x => x.MediaStorage) - .Where(x => x.StorageLocation == EmailAttachmentStorageLocation.Blob) - .OrderBy(x => x.Id); - - PageEntities(queryQueuedEmailAttachments, attachment => - { - source.MoveTo(target, context, attachment.ToMedia()); - - ++context.MovedItems; - }); - - transaction.Commit(); - success = true; - } - catch (Exception exception) - { - success = false; - transaction.Rollback(); - - _services.Notifier.Error(exception.Message); - _logger.Error(exception); - } - } - - if (success) - { - _services.Settings.SetSetting("Media.Storage.Provider", targetProvider.Metadata.SystemName); - } - - // inform both provider about ending - source.OnCompleted(context, success); - target.OnCompleted(context, success); - - if (success && context.ShrinkDatabase) - { - _services.DbContext.ShrinkDatabase(); - } - - return success; - } - } + public class MediaMover : IMediaMover + { + private const int PAGE_SIZE = 50; + + private readonly IRepository _mediaFileRepo; + private readonly ICommonServices _services; + private readonly ILogger _logger; + + public MediaMover(IRepository mediaFileRepo, ICommonServices services, ILogger logger) + { + _mediaFileRepo = mediaFileRepo; + _services = services; + _logger = logger; + } + + public Localizer T { get; set; } = NullLocalizer.Instance; + + public virtual bool Move(Provider sourceProvider, Provider targetProvider) + { + Guard.NotNull(sourceProvider, nameof(sourceProvider)); + Guard.NotNull(targetProvider, nameof(targetProvider)); + + var success = false; + var utcNow = DateTime.UtcNow; + var context = new MediaMoverContext(sourceProvider.Metadata.SystemName, targetProvider.Metadata.SystemName); + + var source = sourceProvider.Value as ISupportsMediaMoving; + var target = targetProvider.Value as ISupportsMediaMoving; + + // Source and target must support media storage moving + if (source == null) + { + throw new ArgumentException(T("Admin.Media.StorageMovingNotSupported", sourceProvider.Metadata.SystemName)); + } + + if (target == null) + { + throw new ArgumentException(T("Admin.Media.StorageMovingNotSupported", targetProvider.Metadata.SystemName)); + } + + // Source and target provider must not be equal + if (sourceProvider.Metadata.SystemName.IsCaseInsensitiveEqual(targetProvider.Metadata.SystemName)) + { + throw new ArgumentException(T("Admin.Media.CannotMoveToSameProvider")); + } + + // We are about to process data in chunks but want to commit ALL at once after ALL chunks have been processed successfully. + // AutoDetectChanges true required for newly inserted binary data. + using (var scope = new DbContextScope(ctx: _services.DbContext, + autoDetectChanges: true, + proxyCreation: false, + validateOnSave: false, + autoCommit: false)) + { + using (var transaction = scope.DbContext.BeginTransaction()) + { + try + { + var pager = new FastPager(_mediaFileRepo.Table, PAGE_SIZE); + while (pager.ReadNextPage(out var files)) + { + foreach (var file in files) + { + // Move item from source to target + source.MoveTo(target, context, file); + + file.UpdatedOnUtc = utcNow; + ++context.MovedItems; + } + + scope.DbContext.SaveChanges(); + + // Detach all entities from previous page to save memory + scope.DbContext.DetachEntities(files, deep: true); + } + + transaction.Commit(); + success = true; + } + catch (Exception exception) + { + success = false; + transaction.Rollback(); + + _services.Notifier.Error(exception); + _logger.Error(exception); + } + } + } + + + if (success) + { + _services.Settings.SetSetting("Media.Storage.Provider", targetProvider.Metadata.SystemName); + } + + // inform both provider about ending + source.OnCompleted(context, success); + target.OnCompleted(context, success); + + if (success && context.ShrinkDatabase) + { + _services.DbContext.ShrinkDatabase(); + } + + return success; + } + } } diff --git a/src/Libraries/SmartStore.Services/Media/Storage/MediaMoverContext.cs b/src/Libraries/SmartStore.Services/Media/Storage/MediaMoverContext.cs index f0d8fc8068..46465ef509 100644 --- a/src/Libraries/SmartStore.Services/Media/Storage/MediaMoverContext.cs +++ b/src/Libraries/SmartStore.Services/Media/Storage/MediaMoverContext.cs @@ -2,44 +2,44 @@ namespace SmartStore.Services.Media.Storage { - public class MediaMoverContext - { - internal MediaMoverContext(string sourceSystemName, string targetSystemName) - { - SourceSystemName = sourceSystemName; - TargetSystemName = targetSystemName; - AffectedFiles = new List(); - CustomProperties = new Dictionary(); - } + public class MediaMoverContext + { + internal MediaMoverContext(string sourceSystemName, string targetSystemName) + { + SourceSystemName = sourceSystemName; + TargetSystemName = targetSystemName; + AffectedFiles = new List(); + CustomProperties = new Dictionary(); + } - /// - /// The system name of the source provider - /// - public string SourceSystemName { get; private set; } + /// + /// The system name of the source provider + /// + public string SourceSystemName { get; private set; } - /// - /// The system name of the target provider - /// - public string TargetSystemName { get; private set; } + /// + /// The system name of the target provider + /// + public string TargetSystemName { get; private set; } - /// - /// Current number of moved media items - /// - public int MovedItems { get; internal set; } + /// + /// Current number of moved media items + /// + public int MovedItems { get; internal set; } - /// - /// Paths of affected media files - /// - public List AffectedFiles { get; private set; } + /// + /// Paths of affected media files + /// + public List AffectedFiles { get; private set; } - /// - /// Whether to shrink database after succesful moving - /// - public bool ShrinkDatabase { get; set; } + /// + /// Whether to shrink database after succesful moving + /// + public bool ShrinkDatabase { get; set; } - /// - /// Use this dictionary for any custom data required along the move operation - /// - public Dictionary CustomProperties { get; set; } - } + /// + /// Use this dictionary for any custom data required along the move operation + /// + public Dictionary CustomProperties { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/Media/Storage/MediaStorageExtensions.cs b/src/Libraries/SmartStore.Services/Media/Storage/MediaStorageExtensions.cs deleted file mode 100644 index eaca77dd54..0000000000 --- a/src/Libraries/SmartStore.Services/Media/Storage/MediaStorageExtensions.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.IO; -using SmartStore.Core; -using SmartStore.Core.Domain.Media; -using SmartStore.Core.Domain.Messages; -using SmartStore.Core.IO; - -namespace SmartStore.Services.Media.Storage -{ - public static class MediaStorageExtensions - { - /// - /// Converts a picture entity into a media storage item - /// - /// Picture entity - /// Media storage item - public static MediaItem ToMedia(this Picture picture) - { - Guard.NotNull(picture, nameof(picture)); - - var media = new MediaItem - { - Entity = picture, - Path = "Storage", - MimeType = picture.MimeType - }; - - return media; - } - - /// - /// Converts a download entity into a media storage item - /// - /// Download entity - /// Media storage item - public static MediaItem ToMedia(this Download download) - { - Guard.NotNull(download, nameof(download)); - - var media = new MediaItem - { - Entity = download, - MimeType = download.ContentType, - FileExtension = download.Extension, - Path = "Downloads" - }; - - return media; - } - - /// - /// Converts a queued email attachment entity into a media storage item - /// - /// Queued email attachment - /// Media storage item - public static MediaItem ToMedia(this QueuedEmailAttachment attachment) - { - Guard.NotNull(attachment, nameof(attachment)); - - var media = new MediaItem - { - Entity = attachment, - MimeType = attachment.MimeType, - FileExtension = Path.GetExtension(attachment.Name), - Path = "QueuedEmailAttachment" - }; - - return media; - } - - /// - /// Create a file name for a media item - /// - /// Media storage item - /// File name including extension - public static string GetFileName(this MediaItem media) - { - if (media != null) - { - var extension = media.FileExtension; - - if (extension.IsEmpty()) - extension = MimeTypes.MapMimeTypeToExtension(media.MimeType); - - var baseEntity = media.Entity as BaseEntity; - - var fileName = string.Concat( - baseEntity.Id.ToString(ImageCache.IdFormatString), - extension.EmptyNull().EnsureStartsWith(".")); - - return fileName; - } - - return null; - } - } -} diff --git a/src/Libraries/SmartStore.Services/Media/Storage/MediaStorageItem.cs b/src/Libraries/SmartStore.Services/Media/Storage/MediaStorageItem.cs new file mode 100644 index 0000000000..9fb06c72a4 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Storage/MediaStorageItem.cs @@ -0,0 +1,161 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.IO; +using SmartStore.Services.Media.Imaging; + +namespace SmartStore.Services.Media.Storage +{ + public abstract class MediaStorageItem : IDisposable + { + private Stream _sourceStream; + + public Stream SourceStream + { + get + { + if (_sourceStream == null) + { + _sourceStream = GetSourceStream(); + } + + if (_sourceStream.CanSeek) + { + _sourceStream.Position = 0; + } + + return _sourceStream; + } + } + + protected abstract Stream GetSourceStream(); + public abstract void SaveTo(Stream stream, MediaFile mediaFile); + public abstract Task SaveToAsync(Stream stream, MediaFile mediaFile); + + protected static int GetLength(Stream stream) + { + if (stream.CanSeek) + { + return (int)stream.Length; + } + else if (stream.Position > 0) + { + return (int)stream.Position; + } + + return 0; + } + + public virtual void Dispose() + { + if (_sourceStream != null) + { + _sourceStream.Dispose(); + _sourceStream = null; + } + } + + #region Factories + + public static MediaStorageItem FromImage(IImage image) + { + return new ImageStorageItem(image); + } + + public static MediaStorageItem FromStream(Stream stream) + { + return new StreamStorageItem(stream); + } + + public static MediaStorageItem FromFile(IFile file) + { + return new StreamStorageItem(file.OpenRead()); + } + + #endregion + + #region Impls + + public class ImageStorageItem : MediaStorageItem + { + private readonly IImage _image; + + public ImageStorageItem(IImage image) + { + _image = image; + } + + protected override Stream GetSourceStream() + { + var memStream = new MemoryStream(); + _image.Save(memStream); + return memStream; + } + + public override void SaveTo(Stream stream, MediaFile mediaFile) + { + _image.Save(stream); + mediaFile.Size = GetLength(stream); + } + + public override Task SaveToAsync(Stream stream, MediaFile mediaFile) + { + _image.Save(stream); + mediaFile.Size = GetLength(stream); + return Task.CompletedTask; + } + } + + public class StreamStorageItem : MediaStorageItem + { + private readonly Stream _stream; + + public StreamStorageItem(Stream stream) + { + _stream = stream; + } + + protected override Stream GetSourceStream() + { + return _stream; + } + + public override void SaveTo(Stream stream, MediaFile mediaFile) + { + if (stream.CanSeek) + { + stream.SetLength(0); + } + + SourceStream.CopyTo(stream); + + if (stream.CanSeek) + { + stream.Position = 0; + } + + mediaFile.Size = GetLength(stream); + } + + public override async Task SaveToAsync(Stream stream, MediaFile mediaFile) + { + if (stream.CanSeek) + { + stream.SetLength(0); + } + + await SourceStream.CopyToAsync(stream); + + if (stream.CanSeek) + { + stream.Position = 0; + } + + mediaFile.Size = GetLength(stream); + } + } + + #endregion + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Tracking/IMediaTrackDetector.cs b/src/Libraries/SmartStore.Services/Media/Tracking/IMediaTrackDetector.cs new file mode 100644 index 0000000000..3136e59ce4 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Tracking/IMediaTrackDetector.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Services.Media +{ + public interface IMediaTrackDetector + { + void ConfigureTracks(string albumName, TrackedMediaPropertyTable table); + IEnumerable DetectAllTracks(string albumName); + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Media/Tracking/IMediaTracker.cs b/src/Libraries/SmartStore.Services/Media/Tracking/IMediaTracker.cs new file mode 100644 index 0000000000..4d3d3f4730 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Tracking/IMediaTracker.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using SmartStore.Core; +using SmartStore.Core.Configuration; +using SmartStore.Core.Domain.Media; + +namespace SmartStore.Services.Media +{ + public interface IMediaTracker + { + IDisposable BeginScope(bool makeFilesTransientWhenOrphaned); + + void Track(BaseEntity entity, int mediaFileId, string propertyName); + void Untrack(BaseEntity entity, int mediaFileId, string propertyName); + + void Track(TSetting settings, int? prevMediaFileId, Expression> path) where TSetting : ISettings, new(); + void Track(TSetting settings, int? prevMediaFileId, Expression> path) where TSetting : ISettings, new(); + + void TrackMany(IEnumerable tracks); + void TrackMany(string albumName, IEnumerable tracks, bool isMigration = false); + + int DeleteAllTracks(string albumName); + void DetectAllTracks(string albumName, bool isMigration = false); + bool TryGetTrackedPropertiesFor(Type forType, out IEnumerable properties); + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Tracking/MediaTracker.cs b/src/Libraries/SmartStore.Services/Media/Tracking/MediaTracker.cs new file mode 100644 index 0000000000..fae7b431e9 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Tracking/MediaTracker.cs @@ -0,0 +1,375 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Data.Entity; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Media; +using SmartStore.Core; +using System.Reflection; +using SmartStore.Core.Caching; +using SmartStore.Collections; +using Autofac.Features.Indexed; +using SmartStore.Utilities; +using System.Runtime.CompilerServices; +using SmartStore.Services.Configuration; +using SmartStore.Core.Configuration; +using System.Linq.Expressions; +using SmartStore.ComponentModel; +using SmartStore.Core.Localization; + +namespace SmartStore.Services.Media +{ + public class MediaTracker : IMediaTracker + { + public Localizer T { get; set; } = NullLocalizer.Instance; + + internal const string TrackedPropertiesKey = "media:trackedprops:all"; + + private readonly ICacheManager _cache; + private readonly IDbContext _dbContext; + private readonly ISettingService _settingService; + private readonly IStoreContext _storeContext; + private readonly IAlbumRegistry _albumRegistry; + private readonly IFolderService _folderService; + private readonly MediaSettings _mediaSettings; + private readonly IIndex _albumProviderFactory; + + private bool _makeFilesTransientWhenOrphaned; + + public MediaTracker( + ICacheManager cache, + IDbContext dbContext, + ISettingService settingService, + IStoreContext storeContext, + IAlbumRegistry albumRegistry, + IFolderService folderService, + MediaSettings mediaSettings, + IIndex albumProviderFactory) + { + _cache = cache; + _dbContext = dbContext; + _settingService = settingService; + _storeContext = storeContext; + _albumRegistry = albumRegistry; + _folderService = folderService; + _mediaSettings = mediaSettings; + _albumProviderFactory = albumProviderFactory; + + _makeFilesTransientWhenOrphaned = _mediaSettings.MakeFilesTransientWhenOrphaned; + } + + public IDisposable BeginScope(bool makeFilesTransientWhenOrphaned) + { + var makeTransient = _makeFilesTransientWhenOrphaned; + _makeFilesTransientWhenOrphaned = makeFilesTransientWhenOrphaned; + + return new ActionDisposable(() => _makeFilesTransientWhenOrphaned = makeTransient); + } + + public void Track(TSetting settings, int? prevMediaFileId, Expression> path) where TSetting : ISettings, new() + { + Guard.NotNull(settings, nameof(settings)); + Guard.NotNull(path, nameof(path)); + + TrackSetting(settings, path.ExtractPropertyInfo().Name, prevMediaFileId, path.CompileFast().Invoke(settings)); + } + + public void Track(TSetting settings, int? prevMediaFileId, Expression> path) where TSetting : ISettings, new() + { + Guard.NotNull(settings, nameof(settings)); + Guard.NotNull(path, nameof(path)); + + TrackSetting(settings, path.ExtractPropertyInfo().Name, prevMediaFileId, path.CompileFast().Invoke(settings)); + } + + protected void TrackSetting( + TSetting settings, + string propertyName, + int? prevMediaFileId, + int? currentMediaFileId) where TSetting : ISettings, new() + { + Guard.NotNull(settings, nameof(settings)); + Guard.NotEmpty(propertyName, nameof(propertyName)); + + var key = nameof(TSetting) + "." + propertyName; + var storeId = _storeContext.CurrentStoreIdIfMultiStoreMode; + + var settingEntity = _settingService.GetSettingEntityByKey(key, storeId); + if (settingEntity != null) + { + this.UpdateTracks(settingEntity, prevMediaFileId, currentMediaFileId, propertyName); + } + } + + public void Track(BaseEntity entity, int mediaFileId, string propertyName) + { + Guard.NotNull(entity, nameof(entity)); + Guard.NotEmpty(propertyName, nameof(propertyName)); + + TrackSingle(entity, mediaFileId, propertyName, MediaTrackOperation.Track); + } + + public void Untrack(BaseEntity entity, int mediaFileId, string propertyName) + { + Guard.NotNull(entity, nameof(entity)); + Guard.NotEmpty(propertyName, nameof(propertyName)); + + TrackSingle(entity, mediaFileId, propertyName, MediaTrackOperation.Untrack); + } + + protected virtual void TrackSingle(BaseEntity entity, int mediaFileId, string propertyName, MediaTrackOperation operation) + { + Guard.NotNull(entity, nameof(entity)); + + if (mediaFileId < 1 || entity.IsTransientRecord()) + return; + + var file = _dbContext.Set().Find(mediaFileId); + if (file != null) + { + var album = _folderService.FindAlbum(file)?.Value; + if (album == null) + { + throw new InvalidOperationException(T("Admin.Media.Exception.TrackUnassignedFile")); + } + else if (!album.CanDetectTracks) + { + // No support for tracking on album level, so get outta here. + return; + } + + var track = new MediaTrack + { + EntityId = entity.Id, + EntityName = entity.GetEntityName(), + MediaFileId = mediaFileId, + Property = propertyName, + Album = album.Name + }; + + if (operation == MediaTrackOperation.Track) + { + file.Tracks.Add(track); + } + else + { + var dbTrack = file.Tracks.FirstOrDefault(x => x == track); + if (dbTrack != null) + { + file.Tracks.Remove(track); + _dbContext.ChangeState(dbTrack, System.Data.Entity.EntityState.Deleted); + } + } + + if (file.Tracks.Count > 0) + { + // A file with tracks can NEVER be transient + file.IsTransient = false; + } + else if (_makeFilesTransientWhenOrphaned) + { + // But an untracked file can OPTIONALLY be transient + file.IsTransient = true; + } + + _dbContext.SaveChanges(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void TrackMany(IEnumerable actions) + { + TrackManyCore(actions, null, false); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void TrackMany(string albumName, IEnumerable tracks, bool isMigration = false) + { + TrackManyCore(tracks, albumName, isMigration); + } + + protected virtual void TrackManyCore(IEnumerable tracks, string albumName, bool isMigration) + { + Guard.NotNull(tracks, nameof(tracks)); + + if (!tracks.Any()) + return; + + var ctx = _dbContext; + + using (var scope = new DbContextScope(ctx, + validateOnSave: false, + hooksEnabled: false, + autoDetectChanges: false)) + { + // Get the album (necessary later to set FolderId)... + MediaFolderNode albumNode = albumName.HasValue() + ? _folderService.GetNodeByPath(albumName)?.Value + : null; + + // Get distinct ids of all detected files... + var mediaFileIds = tracks.Select(x => x.MediaFileId).Distinct().ToArray(); + + // fetch these files from database... + var query = ctx.Set().Include(x => x.Tracks).Where(x => mediaFileIds.Contains(x.Id)); + if (isMigration) + { + query = query.Where(x => x.Version == 1); + } + var files = query.ToDictionary(x => x.Id); + + // for each media file relation to an entity... + foreach (var track in tracks) + { + // fetch the file from local dictionary by its id... + if (files.TryGetValue(track.MediaFileId, out var file)) + { + if (isMigration) + { + // set album id as folder id (during initial migration there are no sub-folders) + file.FolderId = albumNode?.Id; + + // remember that we processed tracks for this file already + file.Version = 2; + } + + if (track.Album.IsEmpty()) + { + if (albumNode != null) + { + // Overwrite track album if scope album was passed. + track.Album = albumNode.Name; + } + else if (file.FolderId.HasValue) + { + // Determine album from file + albumNode = _folderService.FindAlbum(file)?.Value; + track.Album = albumNode?.Name; + } + } + + if (track.Album.IsEmpty()) + continue; // cannot track without album + + if ((albumNode ?? _folderService.FindAlbum(file)?.Value)?.CanDetectTracks == false) + continue; // should not track in albums that do not support track detection + + // add or remove the track from file + if (track.Operation == MediaTrackOperation.Track) + { + file.Tracks.Add(track); + } + else + { + var dbTrack = file.Tracks.FirstOrDefault(x => x == track); + if (dbTrack != null) + { + file.Tracks.Remove(track); + _dbContext.ChangeState(dbTrack, System.Data.Entity.EntityState.Deleted); + } + } + + if (file.Tracks.Count > 0) + { + // A file with tracks can NEVER be transient + file.IsTransient = false; + } + else if (_makeFilesTransientWhenOrphaned) + { + // But an untracked file can OPTIONALLY be transient + file.IsTransient = true; + } + } + } + + // Save whole batch to database + int num = ctx.SaveChanges(); + + if (num > 0) + { + // Breathe + ctx.DetachEntities(deep: true); + } + } + } + + public int DeleteAllTracks(string albumName) + { + Guard.NotEmpty(albumName, nameof(albumName)); + + string sql = "DELETE FROM [MediaTrack] WHERE [Album] = '{0}'".FormatCurrent(albumName); + return _dbContext.ExecuteSqlCommand(sql); + } + + public void DetectAllTracks(string albumName, bool isMigration = false) + { + Guard.NotEmpty(albumName, nameof(albumName)); + + // Get album info... + var albumInfo = _albumRegistry.GetAlbumByName(albumName); + if (albumInfo == null) + { + throw new InvalidOperationException(T("Admin.Media.Exception.AlbumNonexistent", albumName)); + } + + // load corresponding detector provider for current album... + var provider = _albumProviderFactory[albumInfo.ProviderType] as IMediaTrackDetector; + if (provider == null) + { + throw new InvalidOperationException(T("Admin.Media.Exception.AlbumNoTrack", albumName)); + } + + if (!isMigration) + { + // first delete all tracks for current album... + DeleteAllTracks(albumName); + } + + // >>>>> DO detection (potentially a very long process)... + var tracks = provider.DetectAllTracks(albumName); + + // (perf) batch result data... + foreach (var batch in tracks.Slice(500)) + { + // process the batch + TrackManyCore(batch, albumName, isMigration); + } + } + + public bool TryGetTrackedPropertiesFor(Type forType, out IEnumerable properties) + { + properties = null; + + var allTrackedProps = GetAllTrackedProperties(); + if (allTrackedProps.ContainsKey(forType)) + { + properties = allTrackedProps[forType]; + return true; + } + + return false; + } + + protected virtual Multimap GetAllTrackedProperties() + { + var propsMap = _cache.Get(TrackedPropertiesKey, () => + { + var map = new Multimap(); + var props = _albumRegistry.GetAllAlbums() + .Where(x => x.TrackedProperties != null && x.TrackedProperties.Length > 0) + .SelectMany(x => x.TrackedProperties); + + foreach (var prop in props) + { + map.Add(prop.EntityType, prop); + } + + return map; + }); + + return propsMap; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Tracking/MediaTrackerHook.cs b/src/Libraries/SmartStore.Services/Media/Tracking/MediaTrackerHook.cs new file mode 100644 index 0000000000..82f77370f1 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Tracking/MediaTrackerHook.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using SmartStore.ComponentModel; +using SmartStore.Core; +using SmartStore.Core.Data; +using SmartStore.Core.Data.Hooks; +using SmartStore.Core.Domain.Media; +using SmartStore.Data; + +namespace SmartStore.Services.Media +{ + [Important] + public sealed class MediaTrackerHook : DbSaveHook + { + // Track items for the current (SaveChanges) unit. + private readonly HashSet _actionsUnit = new HashSet(); + + // Track items already processed during the current request. + private readonly HashSet _actionsAll = new HashSet(); + + // Entities that are not saved yet but contain effective changes. We won't track if an error occurred during save. + private readonly IDictionary> _actionsTemp = new Dictionary>(); + + private readonly Lazy _mediaTracker; + private readonly IDbContext _dbContext; + + public MediaTrackerHook(Lazy mediaTracker, IDbContext dbContext) + { + _mediaTracker = mediaTracker; + _dbContext = dbContext; + } + + internal static bool Silent { get; set; } + + protected override void OnUpdating(BaseEntity entity, IHookedEntity entry) + { + HookObject(entry, true); + } + + protected override void OnDeleted(BaseEntity entity, IHookedEntity entry) + { + HookObject(entry, false); + } + + protected override void OnInserted(BaseEntity entity, IHookedEntity entry) + { + HookObject(entry, false); + } + + protected override void OnUpdated(BaseEntity entity, IHookedEntity entry) + { + HookObject(entry, false); + } + + private void HookObject(IHookedEntity entry, bool beforeSave) + { + if (Silent) + return; + + var type = entry.EntityType; + + if (!_mediaTracker.Value.TryGetTrackedPropertiesFor(type, out var properties)) + { + throw new NotSupportedException(); + } + + var state = entry.InitialState; + var actions = new HashSet(); + + foreach (var prop in properties) + { + if (beforeSave) + { + if (entry.Entry.TryGetModifiedProperty(_dbContext, prop.Name, out object prevValue)) + { + // Untrack the previous file relation (if not null) + TryAddTrack(prop.Album, entry.Entity, prop.Name, prevValue, MediaTrackOperation.Untrack, actions); + + // Track the new file relation (if not null) + TryAddTrack(prop.Album, entry.Entity, prop.Name, entry.Entry.CurrentValues[prop.Name], MediaTrackOperation.Track, actions); + + _actionsTemp[entry.Entity] = actions; + } + } + else + { + switch (state) + { + case EntityState.Added: + case EntityState.Deleted: + var value = FastProperty.GetProperty(type, prop.Name).GetValue(entry.Entity); + TryAddTrack(prop.Album, entry.Entity, prop.Name, value, state == EntityState.Added ? MediaTrackOperation.Track : MediaTrackOperation.Untrack); + break; + case EntityState.Modified: + if (_actionsTemp.TryGetValue(entry.Entity, out actions)) + { + _actionsUnit.AddRange(actions); + } + break; + } + } + } + } + + private void TryAddTrack(string album, BaseEntity entity, string prop, object value, MediaTrackOperation operation, HashSet actions = null) + { + if (value == null) + return; + + if ((int)value > 0) + { + (actions ?? _actionsUnit).Add(new MediaTrack + { + Album = album, + EntityId = entity.Id, + EntityName = entity.GetEntityName(), + Property = prop, + MediaFileId = (int)value, + Operation = operation + }); + } + } + + public override void OnAfterSaveCompleted() + { + // Remove already processed items during this request. + _actionsUnit.ExceptWith(_actionsAll); + + if (_actionsUnit.Count == 0) + { + return; + } + + _actionsAll.UnionWith(_actionsUnit); + + // Commit all track items in one go + var tracker = _mediaTracker.Value; + using (tracker.BeginScope(false)) + { + tracker.TrackMany(_actionsUnit); + } + + _actionsUnit.Clear(); + _actionsTemp.Clear(); + } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/Tracking/TrackedMediaPropertyTable.cs b/src/Libraries/SmartStore.Services/Media/Tracking/TrackedMediaPropertyTable.cs new file mode 100644 index 0000000000..c85cf70d0e --- /dev/null +++ b/src/Libraries/SmartStore.Services/Media/Tracking/TrackedMediaPropertyTable.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using SmartStore.Collections; +using SmartStore.Core; + +namespace SmartStore.Services.Media +{ + public class TrackedMediaPropertyTable + { + private readonly IList _propertyList = new List(); + + protected internal TrackedMediaPropertyTable(string album) + { + Album = album; + } + + public void Register(Expression> foreignKeyProperty) where T : BaseEntity + { + RegisterInternal(typeof(T), foreignKeyProperty.ExtractPropertyInfo().Name); + } + + public void Register(Expression> foreignKeyProperty) where T : BaseEntity + { + RegisterInternal(typeof(T), foreignKeyProperty.ExtractPropertyInfo().Name); + } + + internal string Album { get; set; } + + private void RegisterInternal(Type entityType, string propertyName) + { + _propertyList.Add(new TrackedMediaProperty + { + Name = propertyName, + EntityType = entityType, + Album = Album, + }); + } + + internal TrackedMediaProperty[] GetProperties() + { + return _propertyList.ToArray(); + } + } + + public class TrackedMediaProperty + { + public string Name { get; set; } + public Type EntityType { get; set; } + public string Album { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Services/Media/TransientMediaClearTask.cs b/src/Libraries/SmartStore.Services/Media/TransientMediaClearTask.cs index 4810d8f0bc..0c1c48f8ab 100644 --- a/src/Libraries/SmartStore.Services/Media/TransientMediaClearTask.cs +++ b/src/Libraries/SmartStore.Services/Media/TransientMediaClearTask.cs @@ -10,48 +10,48 @@ namespace SmartStore.Services.Media { /// /// Represents a task for deleting transient media from the database - /// (pictures and downloads which have been uploaded but never assigned to an entity). + /// (files and downloads which have been uploaded but never assigned to an entity). /// public partial class TransientMediaClearTask : AsyncTask { - private readonly IPictureService _pictureService; + private readonly IMediaService _mediaService; private readonly IDownloadService _downloadService; - private readonly IRepository _pictureRepository; - private readonly IRepository _downloadRepository; + private readonly IRepository _fileRepository; + private readonly IRepository _downloadRepository; - public TransientMediaClearTask( - IPictureService pictureService, + public TransientMediaClearTask( + IMediaService mediaService, IDownloadService downloadService, - IRepository pictureRepository, - IRepository downloadRepository) + IRepository fileRepository, + IRepository downloadRepository) { - _pictureService = pictureService; + _mediaService = mediaService; _downloadService = downloadService; - _pictureRepository = pictureRepository; - _downloadRepository = downloadRepository; + _fileRepository = fileRepository; + _downloadRepository = downloadRepository; } - public override async Task ExecuteAsync(TaskExecutionContext ctx) + public override async Task ExecuteAsync(TaskExecutionContext ctx) { - // Delete all media records which are in transient state since at least 3 hours. - var olderThan = DateTime.UtcNow.AddHours(-3); - var pictureAutoCommit = _pictureRepository.AutoCommitEnabled; + // Delete all media records which are in transient state since at least 3 hours. + var olderThan = DateTime.UtcNow.AddHours(-3); + var fileAutoCommit = _fileRepository.AutoCommitEnabled; var downloadAutoCommit = _downloadRepository.AutoCommitEnabled; - - _pictureRepository.AutoCommitEnabled = false; + + _fileRepository.AutoCommitEnabled = false; _downloadRepository.AutoCommitEnabled = false; - try - { - using (var scope = new DbContextScope(autoDetectChanges: false, validateOnSave: false, hooksEnabled: false)) - { - var pictures = await _pictureRepository.Table.Where(x => x.IsTransient && x.UpdatedOnUtc < olderThan).ToListAsync(); - foreach (var picture in pictures) - { - _pictureService.DeletePicture(picture); - } + try + { + using (var scope = new DbContextScope(autoDetectChanges: false, validateOnSave: false, hooksEnabled: false)) + { + var files = await _fileRepository.Table.Where(x => x.IsTransient && x.UpdatedOnUtc < olderThan).ToListAsync(); + foreach (var file in files) + { + _mediaService.DeleteFile(file, true); + } - await _pictureRepository.Context.SaveChangesAsync(); + await _fileRepository.Context.SaveChangesAsync(); var downloads = await _downloadRepository.Table.Where(x => x.IsTransient && x.UpdatedOnUtc < olderThan).ToListAsync(); foreach (var download in downloads) @@ -61,21 +61,21 @@ public override async Task ExecuteAsync(TaskExecutionContext ctx) await _downloadRepository.Context.SaveChangesAsync(); - if (DataSettings.Current.IsSqlServer && (pictures.Any() || downloads.Any())) + if (DataSettings.Current.IsSqlServer && (files.Any() || downloads.Any())) { try { - _pictureRepository.Context.ExecuteSqlCommand("DBCC SHRINKDATABASE(0)", true); + _fileRepository.Context.ExecuteSqlCommand("DBCC SHRINKDATABASE(0)", true); } catch { } } } } finally - { - _pictureRepository.AutoCommitEnabled = pictureAutoCommit; + { + _fileRepository.AutoCommitEnabled = fileAutoCommit; _downloadRepository.AutoCommitEnabled = downloadAutoCommit; - } + } } } } diff --git a/src/Libraries/SmartStore.Services/Messages/CampaignService.cs b/src/Libraries/SmartStore.Services/Messages/CampaignService.cs index ec574a44dc..58a4e4360a 100644 --- a/src/Libraries/SmartStore.Services/Messages/CampaignService.cs +++ b/src/Libraries/SmartStore.Services/Messages/CampaignService.cs @@ -3,65 +3,58 @@ using System.Linq; using SmartStore.Core.Data; using SmartStore.Core.Domain.Messages; -using SmartStore.Services.Customers; -using SmartStore.Core.Email; -using SmartStore.Services.Stores; -using SmartStore.Templating; using SmartStore.Core.Localization; +using SmartStore.Services.Security; +using SmartStore.Services.Stores; namespace SmartStore.Services.Messages { public partial class CampaignService : ICampaignService { - private readonly ICommonServices _services; - private readonly IRepository _campaignRepository; - private readonly IMessageTemplateService _messageTemplateService; - private readonly IEmailSender _emailSender; - private readonly IQueuedEmailService _queuedEmailService; - private readonly ICustomerService _customerService; - private readonly IStoreMappingService _storeMappingService; + private readonly ICommonServices _services; + private readonly IRepository _campaignRepository; + private readonly IMessageTemplateService _messageTemplateService; + private readonly INewsLetterSubscriptionService _newsLetterSubscriptionService; + private readonly IStoreMappingService _storeMappingService; + private readonly IAclService _aclService; public CampaignService( - ICommonServices services, - IRepository campaignRepository, - IMessageTemplateService messageTemplateService, - IEmailSender emailSender, - IQueuedEmailService queuedEmailService, - ICustomerService customerService, - IStoreMappingService storeMappingService) + ICommonServices services, + IRepository campaignRepository, + IMessageTemplateService messageTemplateService, + INewsLetterSubscriptionService newsLetterSubscriptionService, + IStoreMappingService storeMappingService, + IAclService aclService) { - _services = services; - _campaignRepository = campaignRepository; - _messageTemplateService = messageTemplateService; - _emailSender = emailSender; - _queuedEmailService = queuedEmailService; - _customerService = customerService; - _storeMappingService = storeMappingService; - - T = NullLocalizer.Instance; + _services = services; + _campaignRepository = campaignRepository; + _messageTemplateService = messageTemplateService; + _newsLetterSubscriptionService = newsLetterSubscriptionService; + _storeMappingService = storeMappingService; + _aclService = aclService; } - public Localizer T { get; set; } - + public Localizer T { get; set; } = NullLocalizer.Instance; + public virtual void InsertCampaign(Campaign campaign) { - Guard.NotNull(campaign, nameof(campaign)); + Guard.NotNull(campaign, nameof(campaign)); _campaignRepository.Insert(campaign); } public virtual void UpdateCampaign(Campaign campaign) { - Guard.NotNull(campaign, nameof(campaign)); + Guard.NotNull(campaign, nameof(campaign)); - _campaignRepository.Update(campaign); + _campaignRepository.Update(campaign); } public virtual void DeleteCampaign(Campaign campaign) { - Guard.NotNull(campaign, nameof(campaign)); + Guard.NotNull(campaign, nameof(campaign)); - _campaignRepository.Delete(campaign); + _campaignRepository.Delete(campaign); } public virtual Campaign GetCampaignById(int campaignId) @@ -85,64 +78,109 @@ orderby c.CreatedOnUtc return campaigns; } - public virtual int SendCampaign(Campaign campaign, IEnumerable subscriptions) + public virtual int SendCampaign(Campaign campaign) { - Guard.NotNull(campaign, nameof(campaign)); + Guard.NotNull(campaign, nameof(campaign)); - if (subscriptions == null || subscriptions.Count() <= 0) - return 0; + var totalEmailsSent = 0; + var pageIndex = -1; + int[] storeIds = null; + int[] rolesIds = null; + var alreadyProcessedEmails = new HashSet(StringComparer.InvariantCultureIgnoreCase); - int totalEmailsQueued = 0; + if (campaign.LimitedToStores) + { + storeIds = _storeMappingService.GetStoreMappings(campaign) + .Select(x => x.StoreId) + .Distinct() + .ToArray(); + } - var subscriptionData = subscriptions - .Where(x => _storeMappingService.Authorize(campaign, x.StoreId)) - .GroupBy(x => x.Email); + if (campaign.SubjectToAcl) + { + rolesIds = _aclService.GetAclRecords(campaign) + .Select(x => x.CustomerRoleId) + .Distinct() + .ToArray(); + } - foreach (var group in subscriptionData) + while (true) { - var subscription = group.First(); // only one email per email address - var customer = _customerService.GetCustomerByEmail(subscription.Email); - var messageContext = new MessageContext - { - MessageTemplate = GetCampaignTemplate(), - Customer = customer - }; - - var msg = _services.MessageFactory.CreateMessage(messageContext, true, subscription, campaign); - - if (msg.Email?.Id != null) - { - totalEmailsQueued++; - } - } - - return totalEmailsQueued; + var subscribers = _newsLetterSubscriptionService.GetAllNewsLetterSubscriptions(null, ++pageIndex, 500, false, storeIds, rolesIds); + + foreach (var subscriber in subscribers) + { + // Create only one message per subscription email. + if (alreadyProcessedEmails.Contains(subscriber.Subscription.Email)) + { + continue; + } + + if (subscriber.Customer != null && !subscriber.Customer.Active) + { + continue; + } + + var result = SendCampaign(campaign, subscriber); + if ((result?.Email?.Id ?? 0) != 0) + { + alreadyProcessedEmails.Add(subscriber.Subscription.Email); + + ++totalEmailsSent; + } + } + + if (!subscribers.HasNextPage) + { + break; + } + } + + return totalEmailsSent; } - public virtual CreateMessageResult Preview(Campaign campaign) - { - Guard.NotNull(campaign, nameof(campaign)); + public virtual CreateMessageResult SendCampaign(Campaign campaign, NewsletterSubscriber subscriber) + { + Guard.NotNull(campaign, nameof(campaign)); + + if (subscriber?.Subscription == null) + { + return null; + } + + var messageContext = new MessageContext + { + MessageTemplate = GetCampaignTemplate(), + Customer = subscriber.Customer + }; - var messageContext = new MessageContext - { - MessageTemplate = GetCampaignTemplate(), - TestMode = true - }; + var message = _services.MessageFactory.CreateMessage(messageContext, true, subscriber.Subscription, campaign); + return message; + } - var subscription =_services.MessageFactory.GetTestModels(messageContext).OfType().FirstOrDefault(); + public virtual CreateMessageResult Preview(Campaign campaign) + { + Guard.NotNull(campaign, nameof(campaign)); - var result = _services.MessageFactory.CreateMessage(messageContext, false /* do NOT queue */, subscription, campaign); + var messageContext = new MessageContext + { + MessageTemplate = GetCampaignTemplate(), + TestMode = true + }; - return result; - } + var subscription = _services.MessageFactory.GetTestModels(messageContext).OfType().FirstOrDefault(); - private MessageTemplate GetCampaignTemplate() - { - var messageTemplate = _messageTemplateService.GetMessageTemplateByName(MessageTemplateNames.SystemCampaign, _services.StoreContext.CurrentStore.Id); - if (messageTemplate == null) - throw new SmartException(T("Common.Error.NoMessageTemplate", MessageTemplateNames.SystemCampaign)); + var message = _services.MessageFactory.CreateMessage(messageContext, false /* do NOT queue */, subscription, campaign); + return message; + } + + private MessageTemplate GetCampaignTemplate() + { + var messageTemplate = _messageTemplateService.GetMessageTemplateByName(MessageTemplateNames.SystemCampaign, _services.StoreContext.CurrentStore.Id); + if (messageTemplate == null) + throw new SmartException(T("Common.Error.NoMessageTemplate", MessageTemplateNames.SystemCampaign)); - return messageTemplate; - } - } + return messageTemplate; + } + } } diff --git a/src/Libraries/SmartStore.Services/Messages/CreateAttachmentsConsumer.cs b/src/Libraries/SmartStore.Services/Messages/CreateAttachmentsConsumer.cs index b1fffacea2..d3875f252d 100644 --- a/src/Libraries/SmartStore.Services/Messages/CreateAttachmentsConsumer.cs +++ b/src/Libraries/SmartStore.Services/Messages/CreateAttachmentsConsumer.cs @@ -13,82 +13,82 @@ namespace SmartStore.Services.Messages { - public class CreateAttachmentsConsumer : IConsumer - { - private readonly PdfSettings _pdfSettings; - private readonly HttpRequestBase _httpRequest; - private readonly Lazy _fileDownloadManager; + public class CreateAttachmentsConsumer : IConsumer + { + private readonly PdfSettings _pdfSettings; + private readonly HttpRequestBase _httpRequest; + private readonly Lazy _fileDownloadManager; - public CreateAttachmentsConsumer( - PdfSettings pdfSettings, - HttpRequestBase httpRequest, - Lazy fileDownloadManager) - { - this._pdfSettings = pdfSettings; - this._httpRequest = httpRequest; - this._fileDownloadManager = fileDownloadManager; + public CreateAttachmentsConsumer( + PdfSettings pdfSettings, + HttpRequestBase httpRequest, + Lazy fileDownloadManager) + { + this._pdfSettings = pdfSettings; + this._httpRequest = httpRequest; + this._fileDownloadManager = fileDownloadManager; - Logger = NullLogger.Instance; - T = NullLocalizer.Instance; - } + Logger = NullLogger.Instance; + T = NullLocalizer.Instance; + } - public ILogger Logger { get; set; } - public Localizer T { get; set; } + public ILogger Logger { get; set; } + public Localizer T { get; set; } - public void HandleEvent(MessageQueuingEvent message) - { - var qe = message.QueuedEmail; - var ctx = message.MessageContext; - var model = message.MessageModel; + public void HandleEvent(MessageQueuingEvent message) + { + var qe = message.QueuedEmail; + var ctx = message.MessageContext; + var model = message.MessageModel; - var handledTemplates = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "OrderPlaced.CustomerNotification", _pdfSettings.AttachOrderPdfToOrderPlacedEmail }, - { "OrderCompleted.CustomerNotification", _pdfSettings.AttachOrderPdfToOrderCompletedEmail } - }; - - if (handledTemplates.TryGetValue(ctx.MessageTemplate.Name, out var shouldHandle) && shouldHandle) - { - if (model.Get("Order") is IDictionary order && order.Get("ID") is int orderId) - { - try - { - var qea = CreatePdfInvoiceAttachment(orderId); - qe.Attachments.Add(qea); - } - catch (Exception ex) - { - Logger.Error(ex, T("Admin.System.QueuedEmails.ErrorCreatingAttachment")); - } - } - } - } + var handledTemplates = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "OrderPlaced.CustomerNotification", _pdfSettings.AttachOrderPdfToOrderPlacedEmail }, + { "OrderCompleted.CustomerNotification", _pdfSettings.AttachOrderPdfToOrderCompletedEmail } + }; - private QueuedEmailAttachment CreatePdfInvoiceAttachment(int orderId) - { - var urlHelper = new UrlHelper(_httpRequest.RequestContext); - var path = urlHelper.Action("Print", "Order", new { id = orderId, pdf = true, area = "" }); + if (handledTemplates.TryGetValue(ctx.MessageTemplate.Name, out var shouldHandle) && shouldHandle) + { + if (model.Get("Order") is IDictionary order && order.Get("ID") is int orderId) + { + try + { + var qea = CreatePdfInvoiceAttachment(orderId); + qe.Attachments.Add(qea); + } + catch (Exception ex) + { + Logger.Error(ex, T("Admin.System.QueuedEmails.ErrorCreatingAttachment")); + } + } + } + } - var fileResponse = _fileDownloadManager.Value.DownloadFile(path, true, 5000); + private QueuedEmailAttachment CreatePdfInvoiceAttachment(int orderId) + { + var urlHelper = new UrlHelper(_httpRequest.RequestContext); + var path = urlHelper.Action("Print", "Order", new { id = orderId, pdf = true, area = "" }); - if (fileResponse == null) - { - throw new InvalidOperationException(T("Admin.System.QueuedEmails.ErrorEmptyAttachmentResult", path)); - } + var fileResponse = _fileDownloadManager.Value.DownloadFile(path, true, 5000); - if (!fileResponse.ContentType.IsCaseInsensitiveEqual("application/pdf")) - { - throw new InvalidOperationException(T("Admin.System.QueuedEmails.ErrorNoPdfAttachment")); - } + if (fileResponse == null) + { + throw new InvalidOperationException(T("Admin.System.QueuedEmails.ErrorEmptyAttachmentResult", path)); + } - return new QueuedEmailAttachment - { - StorageLocation = EmailAttachmentStorageLocation.Blob, - MediaStorage = new MediaStorage { Data = fileResponse.Data }, - MimeType = fileResponse.ContentType, - Name = fileResponse.FileName - }; - } + if (!fileResponse.ContentType.IsCaseInsensitiveEqual("application/pdf")) + { + throw new InvalidOperationException(T("Admin.System.QueuedEmails.ErrorNoPdfAttachment")); + } - } + return new QueuedEmailAttachment + { + StorageLocation = EmailAttachmentStorageLocation.Blob, + MediaStorage = new MediaStorage { Data = fileResponse.Data }, + MimeType = fileResponse.ContentType, + Name = fileResponse.FileName + }; + } + + } } diff --git a/src/Libraries/SmartStore.Services/Messages/EmailAccountService.cs b/src/Libraries/SmartStore.Services/Messages/EmailAccountService.cs index 8fafb3b117..3d8978db89 100644 --- a/src/Libraries/SmartStore.Services/Messages/EmailAccountService.cs +++ b/src/Libraries/SmartStore.Services/Messages/EmailAccountService.cs @@ -10,16 +10,16 @@ namespace SmartStore.Services.Messages { public partial class EmailAccountService : IEmailAccountService { - private readonly IRepository _emailAccountRepository; + private readonly IRepository _emailAccountRepository; private readonly EmailAccountSettings _emailAccountSettings; private readonly IEventPublisher _eventPublisher; - private EmailAccount _defaultEmailAccount; + private EmailAccount _defaultEmailAccount; public EmailAccountService( - IRepository emailAccountRepository, - EmailAccountSettings emailAccountSettings, - IEventPublisher eventPublisher) + IRepository emailAccountRepository, + EmailAccountSettings emailAccountSettings, + IEventPublisher eventPublisher) { this._emailAccountRepository = emailAccountRepository; this._emailAccountSettings = emailAccountSettings; @@ -31,11 +31,11 @@ public virtual void InsertEmailAccount(EmailAccount emailAccount) if (emailAccount == null) throw new ArgumentNullException("emailAccount"); - emailAccount.Email = emailAccount.Email.EmptyNull(); - emailAccount.DisplayName = emailAccount.DisplayName.EmptyNull(); - emailAccount.Host = emailAccount.Host.EmptyNull(); - emailAccount.Username = emailAccount.Username.EmptyNull(); - emailAccount.Password = emailAccount.Password.EmptyNull(); + emailAccount.Email = emailAccount.Email.EmptyNull(); + emailAccount.DisplayName = emailAccount.DisplayName.EmptyNull(); + emailAccount.Host = emailAccount.Host.EmptyNull(); + emailAccount.Username = emailAccount.Username.EmptyNull(); + emailAccount.Password = emailAccount.Password.EmptyNull(); emailAccount.Email = emailAccount.Email.Trim(); emailAccount.DisplayName = emailAccount.DisplayName.Trim(); @@ -43,15 +43,15 @@ public virtual void InsertEmailAccount(EmailAccount emailAccount) emailAccount.Username = emailAccount.Username.Trim(); emailAccount.Password = emailAccount.Password.Trim(); - emailAccount.Email = emailAccount.Email.Truncate(255); - emailAccount.DisplayName = emailAccount.DisplayName.Truncate(255); - emailAccount.Host = emailAccount.Host.Truncate(255); - emailAccount.Username = emailAccount.Username.Truncate(255); - emailAccount.Password = emailAccount.Password.Truncate(255); + emailAccount.Email = emailAccount.Email.Truncate(255); + emailAccount.DisplayName = emailAccount.DisplayName.Truncate(255); + emailAccount.Host = emailAccount.Host.Truncate(255); + emailAccount.Username = emailAccount.Username.Truncate(255); + emailAccount.Password = emailAccount.Password.Truncate(255); _emailAccountRepository.Insert(emailAccount); - _defaultEmailAccount = null; + _defaultEmailAccount = null; } public virtual void UpdateEmailAccount(EmailAccount emailAccount) @@ -59,11 +59,11 @@ public virtual void UpdateEmailAccount(EmailAccount emailAccount) if (emailAccount == null) throw new ArgumentNullException("emailAccount"); - emailAccount.Email = emailAccount.Email.EmptyNull(); - emailAccount.DisplayName = emailAccount.DisplayName.EmptyNull(); - emailAccount.Host = emailAccount.Host.EmptyNull(); - emailAccount.Username = emailAccount.Username.EmptyNull(); - emailAccount.Password = emailAccount.Password.EmptyNull(); + emailAccount.Email = emailAccount.Email.EmptyNull(); + emailAccount.DisplayName = emailAccount.DisplayName.EmptyNull(); + emailAccount.Host = emailAccount.Host.EmptyNull(); + emailAccount.Username = emailAccount.Username.EmptyNull(); + emailAccount.Password = emailAccount.Password.EmptyNull(); emailAccount.Email = emailAccount.Email.Trim(); emailAccount.DisplayName = emailAccount.DisplayName.Trim(); @@ -71,15 +71,15 @@ public virtual void UpdateEmailAccount(EmailAccount emailAccount) emailAccount.Username = emailAccount.Username.Trim(); emailAccount.Password = emailAccount.Password.Trim(); - emailAccount.Email = emailAccount.Email.Truncate(255); - emailAccount.DisplayName = emailAccount.DisplayName.Truncate(255); - emailAccount.Host = emailAccount.Host.Truncate(255); - emailAccount.Username = emailAccount.Username.Truncate(255); - emailAccount.Password = emailAccount.Password.Truncate(255); + emailAccount.Email = emailAccount.Email.Truncate(255); + emailAccount.DisplayName = emailAccount.DisplayName.Truncate(255); + emailAccount.Host = emailAccount.Host.Truncate(255); + emailAccount.Username = emailAccount.Username.Truncate(255); + emailAccount.Password = emailAccount.Password.Truncate(255); _emailAccountRepository.Update(emailAccount); - _defaultEmailAccount = null; + _defaultEmailAccount = null; } public virtual void DeleteEmailAccount(EmailAccount emailAccount) @@ -92,7 +92,7 @@ public virtual void DeleteEmailAccount(EmailAccount emailAccount) _emailAccountRepository.Delete(emailAccount); - _defaultEmailAccount = null; + _defaultEmailAccount = null; } public virtual EmailAccount GetEmailAccountById(int emailAccountId) @@ -100,22 +100,22 @@ public virtual EmailAccount GetEmailAccountById(int emailAccountId) if (emailAccountId == 0) return null; - return _emailAccountRepository.GetByIdCached(emailAccountId, "db.emailaccount.id-" + emailAccountId); - } - - public virtual EmailAccount GetDefaultEmailAccount() - { - if (_defaultEmailAccount == null) - { - _defaultEmailAccount = GetEmailAccountById(_emailAccountSettings.DefaultEmailAccountId); - if (_defaultEmailAccount == null) - { - _defaultEmailAccount = GetAllEmailAccounts().FirstOrDefault(); - } - } - - return _defaultEmailAccount; - } + return _emailAccountRepository.GetByIdCached(emailAccountId, "db.emailaccount.id-" + emailAccountId); + } + + public virtual EmailAccount GetDefaultEmailAccount() + { + if (_defaultEmailAccount == null) + { + _defaultEmailAccount = GetEmailAccountById(_emailAccountSettings.DefaultEmailAccountId); + if (_defaultEmailAccount == null) + { + _defaultEmailAccount = GetAllEmailAccounts().FirstOrDefault(); + } + } + + return _defaultEmailAccount; + } public virtual IList GetAllEmailAccounts() { diff --git a/src/Libraries/SmartStore.Services/Messages/Events.cs b/src/Libraries/SmartStore.Services/Messages/Events.cs index 02bbf52c66..50084a7461 100644 --- a/src/Libraries/SmartStore.Services/Messages/Events.cs +++ b/src/Libraries/SmartStore.Services/Messages/Events.cs @@ -3,85 +3,85 @@ namespace SmartStore.Services.Messages { - /// - /// Published after the creation of a single message model part has been completed. - /// - /// Type of source entity - public class MessageModelPartCreatedEvent where T : class - { - public MessageModelPartCreatedEvent(T source, dynamic part) - { - Source = source; - Part = part; - } + /// + /// Published after the creation of a single message model part has been completed. + /// + /// Type of source entity + public class MessageModelPartCreatedEvent where T : class + { + public MessageModelPartCreatedEvent(T source, dynamic part) + { + Source = source; + Part = part; + } - /// - /// The source object for which the model part has been created, e.g. a Product entity. - /// - public T Source { get; private set; } + /// + /// The source object for which the model part has been created, e.g. a Product entity. + /// + public T Source { get; private set; } - /// - /// The resulting model part. - /// - public dynamic Part { get; private set; } - } + /// + /// The resulting model part. + /// + public dynamic Part { get; private set; } + } - /// - /// Published when a system mapper is missing for a particular model type (e.g. a custom entity in a plugin). - /// Implementors should subscribe to this event in order to provide a corresponding dynamic model part. - /// The result model should be assigned to the property. If this property - /// is still null, the source is used as model part instead. - /// - public class MessageModelPartMappingEvent - { - public MessageModelPartMappingEvent(object source) - { - Source = source; - } + /// + /// Published when a system mapper is missing for a particular model type (e.g. a custom entity in a plugin). + /// Implementors should subscribe to this event in order to provide a corresponding dynamic model part. + /// The result model should be assigned to the property. If this property + /// is still null, the source is used as model part instead. + /// + public class MessageModelPartMappingEvent + { + public MessageModelPartMappingEvent(object source) + { + Source = source; + } - /// - /// The source object for which a model part should be created. - /// - public object Source { get; private set; } + /// + /// The source object for which a model part should be created. + /// + public object Source { get; private set; } - /// - /// The resulting model part. - /// - public dynamic Result { get; set; } + /// + /// The resulting model part. + /// + public dynamic Result { get; set; } - /// - /// The name of the model part. If null the source's type name is used. - /// - public string ModelPartName { get; set; } - } + /// + /// The name of the model part. If null the source's type name is used. + /// + public string ModelPartName { get; set; } + } - /// - /// Published after the message model has been completely created. - /// - public class MessageModelCreatedEvent - { - public MessageModelCreatedEvent(MessageContext messageContext, TemplateModel model) - { - MessageContext = messageContext; - Model = model; - } + /// + /// Published after the message model has been completely created. + /// + public class MessageModelCreatedEvent + { + public MessageModelCreatedEvent(MessageContext messageContext, TemplateModel model) + { + MessageContext = messageContext; + Model = model; + } - public MessageContext MessageContext{ get; private set; } + public MessageContext MessageContext { get; private set; } - /// - /// The result message model. - /// - public TemplateModel Model { get; private set; } - } + /// + /// The result message model. + /// + public TemplateModel Model { get; private set; } + } - /// - /// An event message which gets published just before a new instance - /// of is persisted to the database - /// - public class MessageQueuingEvent - { - public QueuedEmail QueuedEmail { get; set; } - public MessageContext MessageContext { get; set; } - public TemplateModel MessageModel { get; set; } - } + /// + /// An event message which gets published just before a new instance + /// of is persisted to the database + /// + public class MessageQueuingEvent + { + public QueuedEmail QueuedEmail { get; set; } + public MessageContext MessageContext { get; set; } + public TemplateModel MessageModel { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/Messages/ICampaignService.cs b/src/Libraries/SmartStore.Services/Messages/ICampaignService.cs index 0954ec1eb7..6b51ea7a3a 100644 --- a/src/Libraries/SmartStore.Services/Messages/ICampaignService.cs +++ b/src/Libraries/SmartStore.Services/Messages/ICampaignService.cs @@ -35,20 +35,27 @@ public partial interface ICampaignService /// /// Campaign collection IList GetAllCampaigns(); - + /// - /// Sends a campaign to specified emails + /// Sends a campaign to all newsletter subscribers. /// - /// Campaign - /// Subscriptions - /// Total emails sent - int SendCampaign(Campaign campaign, IEnumerable subscriptions); - - /// - /// Creates a campaign email without sending it for previewing and testing purposes. - /// - /// The campaign to preview - /// The preview result - CreateMessageResult Preview(Campaign campaign); - } + /// Campaign. + /// Number of queued messages. + int SendCampaign(Campaign campaign); + + /// + /// Sends a campaign to specified subscriber. + /// + /// Campaign. + /// Newsletter subscriber. + /// Message result. + CreateMessageResult SendCampaign(Campaign campaign, NewsletterSubscriber subscriber); + + /// + /// Creates a campaign email without sending it for previewing and testing purposes. + /// + /// The campaign to preview + /// The preview result + CreateMessageResult Preview(Campaign campaign); + } } diff --git a/src/Libraries/SmartStore.Services/Messages/IEmailAccountService.cs b/src/Libraries/SmartStore.Services/Messages/IEmailAccountService.cs index 6b3df6f6eb..ccc2eb4e31 100644 --- a/src/Libraries/SmartStore.Services/Messages/IEmailAccountService.cs +++ b/src/Libraries/SmartStore.Services/Messages/IEmailAccountService.cs @@ -30,11 +30,11 @@ public partial interface IEmailAccountService /// Email account EmailAccount GetEmailAccountById(int emailAccountId); - /// - /// Gets the default email account - /// - /// Emil account - EmailAccount GetDefaultEmailAccount(); + /// + /// Gets the default email account + /// + /// Emil account + EmailAccount GetDefaultEmailAccount(); /// /// Gets all email accounts diff --git a/src/Libraries/SmartStore.Services/Messages/IMessageFactory.cs b/src/Libraries/SmartStore.Services/Messages/IMessageFactory.cs index 525e50d09c..9e1c60c210 100644 --- a/src/Libraries/SmartStore.Services/Messages/IMessageFactory.cs +++ b/src/Libraries/SmartStore.Services/Messages/IMessageFactory.cs @@ -6,104 +6,104 @@ namespace SmartStore.Services.Messages { - /// - /// Contains the result data of a call - /// - public class CreateMessageResult - { - /// - /// The queued email instance which can be saved to the database - /// - public QueuedEmail Email { get; set; } + /// + /// Contains the result data of a call + /// + public class CreateMessageResult + { + /// + /// The queued email instance which can be saved to the database + /// + public QueuedEmail Email { get; set; } - /// - /// The final model which contains all global and template specific model parts. - /// - public TemplateModel Model { get; set; } + /// + /// The final model which contains all global and template specific model parts. + /// + public TemplateModel Model { get; set; } - /// - /// The message context used to create the message. - /// - public MessageContext MessageContext { get; set; } - } + /// + /// The message context used to create the message. + /// + public MessageContext MessageContext { get; set; } + } - /// - /// Creates and optionally queues email messages - /// - public interface IMessageFactory - { - /// - /// Creates an email message - /// - /// Contains all data required for creating a message - /// If true, the created email message will automatically be queued for sending (saved in database as a ) - /// - /// All model objects that are necessary to render the template (in no particular order). - /// The passed object instances will be converted to special types which the underlying can handle. - /// is responsible for the conversion. See also . - /// - /// Contains the message creation result. - CreateMessageResult CreateMessage(MessageContext messageContext, bool queue, params object[] modelParts); + /// + /// Creates and optionally queues email messages + /// + public interface IMessageFactory + { + /// + /// Creates an email message + /// + /// Contains all data required for creating a message + /// If true, the created email message will automatically be queued for sending (saved in database as a ) + /// + /// All model objects that are necessary to render the template (in no particular order). + /// The passed object instances will be converted to special types which the underlying can handle. + /// is responsible for the conversion. See also . + /// + /// Contains the message creation result. + CreateMessageResult CreateMessage(MessageContext messageContext, bool queue, params object[] modelParts); - /// - /// Queues a message created by - /// - /// The message context used to create the message. - /// The instance of to queue, e.g. obtained from - void QueueMessage(MessageContext messageContext, QueuedEmail queuedEmail); + /// + /// Queues a message created by + /// + /// The message context used to create the message. + /// The instance of to queue, e.g. obtained from + void QueueMessage(MessageContext messageContext, QueuedEmail queuedEmail); - /// - /// Gets an array of suitable test model parts during preview mode. The message template defines - /// which model part types are required (a comma-separated type list in ). - /// The framework tries to load a random entity for each defined type from the database. If the table does not contain any records, - /// gets called internally to obtain a test model wrapper with sample data. - /// - /// The message context used to create the message. - /// An array of model parts which can be passed to - object[] GetTestModels(MessageContext messageContext); - } + /// + /// Gets an array of suitable test model parts during preview mode. The message template defines + /// which model part types are required (a comma-separated type list in ). + /// The framework tries to load a random entity for each defined type from the database. If the table does not contain any records, + /// gets called internally to obtain a test model wrapper with sample data. + /// + /// The message context used to create the message. + /// An array of model parts which can be passed to + object[] GetTestModels(MessageContext messageContext); + } - public static class IMessageFactoryExtensions - { - /// - /// Sends the "ContactUs" message to the store owner - /// - public static CreateMessageResult SendContactUsMessage(this IMessageFactory factory, Customer customer, - string senderEmail, string senderName, string subject, string message, EmailAddress senderEmailAddress, int languageId = 0) - { - var model = new NamedModelPart("Message") - { - ["Subject"] = subject.NullEmpty(), - ["Message"] = message.NullEmpty(), - ["SenderEmail"] = senderEmail.NullEmpty(), - ["SenderName"] = senderName.HasValue() ? senderName.NullEmpty() : senderEmail.NullEmpty() - }; + public static class IMessageFactoryExtensions + { + /// + /// Sends the "ContactUs" message to the store owner + /// + public static CreateMessageResult SendContactUsMessage(this IMessageFactory factory, Customer customer, + string senderEmail, string senderName, string subject, string message, EmailAddress senderEmailAddress, int languageId = 0) + { + var model = new NamedModelPart("Message") + { + ["Subject"] = subject.NullEmpty(), + ["Message"] = message.NullEmpty(), + ["SenderEmail"] = senderEmail.NullEmpty(), + ["SenderName"] = senderName.HasValue() ? senderName.NullEmpty() : senderEmail.NullEmpty() + }; - var messageContext = MessageContext.Create(MessageTemplateNames.SystemContactUs, languageId, customer: customer); - if (senderEmailAddress != null) - { - messageContext.SenderEmailAddress = senderEmailAddress; - } + var messageContext = MessageContext.Create(MessageTemplateNames.SystemContactUs, languageId, customer: customer); + if (senderEmailAddress != null) + { + messageContext.SenderEmailAddress = senderEmailAddress; + } - return factory.CreateMessage(messageContext, true, model); - } + return factory.CreateMessage(messageContext, true, model); + } - /// - /// Sends a newsletter subscription activation message - /// - public static CreateMessageResult SendNewsLetterSubscriptionActivationMessage(this IMessageFactory factory, NewsLetterSubscription subscription, int languageId = 0) - { - Guard.NotNull(subscription, nameof(subscription)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewsLetterSubscriptionActivation, languageId), true, subscription); - } + /// + /// Sends a newsletter subscription activation message + /// + public static CreateMessageResult SendNewsLetterSubscriptionActivationMessage(this IMessageFactory factory, NewsLetterSubscription subscription, int languageId = 0) + { + Guard.NotNull(subscription, nameof(subscription)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewsLetterSubscriptionActivation, languageId), true, subscription); + } - /// - /// Sends a newsletter subscription deactivation message - /// - public static CreateMessageResult SendNewsLetterSubscriptionDeactivationMessage(this IMessageFactory factory, NewsLetterSubscription subscription, int languageId = 0) - { - Guard.NotNull(subscription, nameof(subscription)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewsLetterSubscriptionDeactivation, languageId), true, subscription); - } - } + /// + /// Sends a newsletter subscription deactivation message + /// + public static CreateMessageResult SendNewsLetterSubscriptionDeactivationMessage(this IMessageFactory factory, NewsLetterSubscription subscription, int languageId = 0) + { + Guard.NotNull(subscription, nameof(subscription)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewsLetterSubscriptionDeactivation, languageId), true, subscription); + } + } } diff --git a/src/Libraries/SmartStore.Services/Messages/IMessageModelProvider.cs b/src/Libraries/SmartStore.Services/Messages/IMessageModelProvider.cs index 6819624dea..18d9b01c80 100644 --- a/src/Libraries/SmartStore.Services/Messages/IMessageModelProvider.cs +++ b/src/Libraries/SmartStore.Services/Messages/IMessageModelProvider.cs @@ -13,108 +13,108 @@ namespace SmartStore.Services.Messages { - /// - /// Responsible for building the message template model - /// - public interface IMessageModelProvider - { - /// - /// Creates and adds all global model parts to the template model (): - /// - /// - /// Context (contains meta infos like template name, language etc.) - /// Customer (obtained from property) - /// Store (obtained from property) - /// Email (the ) - /// Theme (some theming variables, mostly colors) - /// - /// - /// - /// Contains all data required for building a model part and creating a message - void AddGlobalModelParts(MessageContext messageContext); + /// + /// Responsible for building the message template model + /// + public interface IMessageModelProvider + { + /// + /// Creates and adds all global model parts to the template model (): + /// + /// + /// Context (contains meta infos like template name, language etc.) + /// Customer (obtained from property) + /// Store (obtained from property) + /// Email (the ) + /// Theme (some theming variables, mostly colors) + /// + /// + /// + /// Contains all data required for building a model part and creating a message + void AddGlobalModelParts(MessageContext messageContext); - /// - /// Adds a template specific model part to the template model. - /// The passed object instance () will be converted to a special type which the underlying can handle. - /// - /// Supported types are: , , , , , - /// , , , , , - /// , , , , , - /// , - /// - /// - /// Furthermore, any object implementing or can also be passed as model part. - /// The first merges all entries within the passed object with the special Bag entry, the latter creates a whole - /// new entry using the name provided by its property. - /// - /// - /// If an unsupported object is passed, the framework will publish the event, giving - /// a subscriber the chance to provide a converted model object and a part name. - /// - /// - /// The model part instance to convert and add to the final model. - /// Contains all data required for building a model part and creating a message - /// - /// The name to use for the model part in the final model. If null, the framework tries to infer the name. - /// See also - /// - void AddModelPart(object part, MessageContext messageContext, string name = null); + /// + /// Adds a template specific model part to the template model. + /// The passed object instance () will be converted to a special type which the underlying can handle. + /// + /// Supported types are: , , , , , + /// , , , , , + /// , , , , , + /// , + /// + /// + /// Furthermore, any object implementing or can also be passed as model part. + /// The first merges all entries within the passed object with the special Bag entry, the latter creates a whole + /// new entry using the name provided by its property. + /// + /// + /// If an unsupported object is passed, the framework will publish the event, giving + /// a subscriber the chance to provide a converted model object and a part name. + /// + /// + /// The model part instance to convert and add to the final model. + /// Contains all data required for building a model part and creating a message + /// + /// The name to use for the model part in the final model. If null, the framework tries to infer the name. + /// See also + /// + void AddModelPart(object part, MessageContext messageContext, string name = null); - /// - /// Creates a serializable model object for the passed entity/object. - /// - /// Supported types are: , , , , , - /// , , , , , - /// , , , , , - /// , - /// - /// - /// Furthermore, any object implementing or can also be passed as model part. - /// The first merges all entries within the passed object with the special Bag entry, the latter creates a whole - /// new entry using the name provided by its property. - /// - /// - /// If an unsupported object is passed, null is returned - /// - /// - /// The model part instance to convert. - /// Whether members/properties with null values should be excluded from the result model. - /// Optional list of member/property names to exclude from the result model. - object CreateModelPart(object part, bool ignoreNullMembers, params string[] ignoreMemberNames); + /// + /// Creates a serializable model object for the passed entity/object. + /// + /// Supported types are: , , , , , + /// , , , , , + /// , , , , , + /// , + /// + /// + /// Furthermore, any object implementing or can also be passed as model part. + /// The first merges all entries within the passed object with the special Bag entry, the latter creates a whole + /// new entry using the name provided by its property. + /// + /// + /// If an unsupported object is passed, null is returned + /// + /// + /// The model part instance to convert. + /// Whether members/properties with null values should be excluded from the result model. + /// Optional list of member/property names to exclude from the result model. + object CreateModelPart(object part, bool ignoreNullMembers, params string[] ignoreMemberNames); - /// - /// Tries to infer the model part name by type: - /// - /// When is a plain object: type name - /// When is : ModelPartName property - /// - /// - /// The model part instance to resolve a name for. - /// The inferred name or null - string ResolveModelName(object model); + /// + /// Tries to infer the model part name by type: + /// + /// When is a plain object: type name + /// When is : ModelPartName property + /// + /// + /// The model part instance to resolve a name for. + /// The inferred name or null + string ResolveModelName(object model); - /// - /// Build a model metadata tree for a final template model. Model trees are used - /// on the client to provide autocomplete information. - /// - /// The final template model to build a model tree for. - /// A hierarchy of instances. - TreeNode BuildModelTree(TemplateModel model); + /// + /// Build a model metadata tree for a final template model. Model trees are used + /// on the client to provide autocomplete information. + /// + /// The final template model to build a model tree for. + /// A hierarchy of instances. + TreeNode BuildModelTree(TemplateModel model); - /// - /// Gets the last known model metadata tree for a particular template. - /// See also - /// - /// Name of the template to get metadata for. - /// A hierarchy of instances. - TreeNode GetLastModelTree(string messageTemplateName); + /// + /// Gets the last known model metadata tree for a particular template. + /// See also + /// + /// Name of the template to get metadata for. + /// A hierarchy of instances. + TreeNode GetLastModelTree(string messageTemplateName); - /// - /// Gets the last known model metadata tree for a particular template. - /// See also - /// - /// The template to get metadata for. - /// A hierarchy of instances. - TreeNode GetLastModelTree(MessageTemplate template); - } + /// + /// Gets the last known model metadata tree for a particular template. + /// See also + /// + /// The template to get metadata for. + /// A hierarchy of instances. + TreeNode GetLastModelTree(MessageTemplate template); + } } diff --git a/src/Libraries/SmartStore.Services/Messages/IMessageTemplateService.cs b/src/Libraries/SmartStore.Services/Messages/IMessageTemplateService.cs index 445671bc92..ecfa00d4f9 100644 --- a/src/Libraries/SmartStore.Services/Messages/IMessageTemplateService.cs +++ b/src/Libraries/SmartStore.Services/Messages/IMessageTemplateService.cs @@ -8,11 +8,11 @@ namespace SmartStore.Services.Messages /// public partial interface IMessageTemplateService { - /// - /// Delete a message template - /// - /// Message template - void DeleteMessageTemplate(MessageTemplate messageTemplate); + /// + /// Delete a message template + /// + /// Message template + void DeleteMessageTemplate(MessageTemplate messageTemplate); /// /// Inserts a message template @@ -48,11 +48,11 @@ public partial interface IMessageTemplateService /// Message template list IList GetAllMessageTemplates(int storeId); - /// - /// Create a copy of message template with all depended data - /// - /// Message template - /// Message template copy - MessageTemplate CopyMessageTemplate(MessageTemplate messageTemplate); + /// + /// Create a copy of message template with all depended data + /// + /// Message template + /// Message template copy + MessageTemplate CopyMessageTemplate(MessageTemplate messageTemplate); } } diff --git a/src/Libraries/SmartStore.Services/Messages/IModelPart.cs b/src/Libraries/SmartStore.Services/Messages/IModelPart.cs index 16ed798135..dd93f45a7f 100644 --- a/src/Libraries/SmartStore.Services/Messages/IModelPart.cs +++ b/src/Libraries/SmartStore.Services/Messages/IModelPart.cs @@ -4,41 +4,41 @@ namespace SmartStore.Services.Messages { - /// - /// Used to transfer miscellaneous data to the template engine - /// which is merged with the generic "Bag" part. - /// - public interface IModelPart : IDictionary - { - } - - public interface INamedModelPart : IDictionary - { - string ModelPartName { get; } - } - - - #region Impl - - public class ModelPart : HybridExpando, IModelPart - { - } - - public class NamedModelPart : HybridExpando, INamedModelPart - { - public NamedModelPart(string modelPartName) - : base(true) - { - Guard.NotEmpty(modelPartName, nameof(modelPartName)); - ModelPartName = modelPartName; - } - - public string ModelPartName - { - get; - private set; - } - } - - #endregion + /// + /// Used to transfer miscellaneous data to the template engine + /// which is merged with the generic "Bag" part. + /// + public interface IModelPart : IDictionary + { + } + + public interface INamedModelPart : IDictionary + { + string ModelPartName { get; } + } + + + #region Impl + + public class ModelPart : HybridExpando, IModelPart + { + } + + public class NamedModelPart : HybridExpando, INamedModelPart + { + public NamedModelPart(string modelPartName) + : base(true) + { + Guard.NotEmpty(modelPartName, nameof(modelPartName)); + ModelPartName = modelPartName; + } + + public string ModelPartName + { + get; + private set; + } + } + + #endregion } diff --git a/src/Libraries/SmartStore.Services/Messages/INewsLetterSubscriptionService.cs b/src/Libraries/SmartStore.Services/Messages/INewsLetterSubscriptionService.cs index a579ff6859..2dfd4fb106 100644 --- a/src/Libraries/SmartStore.Services/Messages/INewsLetterSubscriptionService.cs +++ b/src/Libraries/SmartStore.Services/Messages/INewsLetterSubscriptionService.cs @@ -4,8 +4,8 @@ namespace SmartStore.Services.Messages { - public partial interface INewsLetterSubscriptionService - { + public partial interface INewsLetterSubscriptionService + { /// /// Inserts a newsletter subscription /// @@ -27,21 +27,21 @@ public partial interface INewsLetterSubscriptionService /// if set to true [publish subscription events]. void DeleteNewsLetterSubscription(NewsLetterSubscription newsLetterSubscription, bool publishSubscriptionEvents = true); - /// - /// Adds or deletes a newsletter subscription - /// - /// true add subscription, false delete - /// Email address - /// Store identifier - /// true added subscription, false removed subscription, null did nothing - bool? AddNewsLetterSubscriptionFor(bool add, string email, int storeId); + /// + /// Adds or deletes a newsletter subscription + /// + /// true add subscription, false delete + /// Email address + /// Store identifier + /// true added subscription, false removed subscription, null did nothing + bool? AddNewsLetterSubscriptionFor(bool add, string email, int storeId); - /// - /// Gets a newsletter subscription by newsletter subscription identifier - /// - /// The newsletter subscription identifier - /// NewsLetter subscription - NewsLetterSubscription GetNewsLetterSubscriptionById(int newsLetterSubscriptionId); + /// + /// Gets a newsletter subscription by newsletter subscription identifier + /// + /// The newsletter subscription identifier + /// NewsLetter subscription + NewsLetterSubscription GetNewsLetterSubscriptionById(int newsLetterSubscriptionId); /// /// Gets a newsletter subscription by newsletter subscription GUID @@ -59,14 +59,21 @@ public partial interface INewsLetterSubscriptionService NewsLetterSubscription GetNewsLetterSubscriptionByEmail(string email, int storeId = 0); /// - /// Gets the newsletter subscription list + /// Gets a list of newsletter subscriptions including associated customers. /// - /// Email to search or string. Empty to load all records. - /// Page index - /// Page size - /// A value indicating whether the not active subscriptions should be loaded - /// The store identifier - /// NewsLetterSubscription entity list - IPagedList GetAllNewsLetterSubscriptions(string email, int pageIndex, int pageSize, bool showHidden = false, int storeId = 0); + /// Filter by email. + /// Page index. + /// Page size. + /// A value indicating whether the not active subscriptions should be loaded. + /// Filter by store identifiers. + /// <Filter by customer role identifiers. + /// List of newsletter scubscribers. + IPagedList GetAllNewsLetterSubscriptions( + string email, + int pageIndex, + int pageSize, + bool showHidden = false, + int[] storeIds = null, + int[] customerRolesIds = null); } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Messages/IQueuedEmailService.cs b/src/Libraries/SmartStore.Services/Messages/IQueuedEmailService.cs index 82a6de9bac..4a86533945 100644 --- a/src/Libraries/SmartStore.Services/Messages/IQueuedEmailService.cs +++ b/src/Libraries/SmartStore.Services/Messages/IQueuedEmailService.cs @@ -5,7 +5,7 @@ namespace SmartStore.Services.Messages { - public partial interface IQueuedEmailService + public partial interface IQueuedEmailService { /// /// Inserts a queued email @@ -25,11 +25,11 @@ public partial interface IQueuedEmailService /// Queued email void DeleteQueuedEmail(QueuedEmail queuedEmail); - /// - /// Deletes all queued emails - /// - /// The count of deleted entries - int DeleteAllQueuedEmails(); + /// + /// Deletes all queued emails + /// + /// The count of deleted entries + int DeleteAllQueuedEmails(); /// /// Gets a queued email by identifier @@ -52,31 +52,31 @@ public partial interface IQueuedEmailService /// Email item collection IPagedList SearchEmails(SearchEmailsQuery query); - /// - /// Sends a queued email asynchronously. - /// - /// Queued email - /// Whether the operation succeeded - Task SendEmailAsync(QueuedEmail queuedEmail); + /// + /// Sends a queued email asynchronously. + /// + /// Queued email + /// Whether the operation succeeded + Task SendEmailAsync(QueuedEmail queuedEmail); - /// - /// Gets a queued email attachment by identifier - /// - /// Queued email attachment identifier - /// Queued email attachment - QueuedEmailAttachment GetQueuedEmailAttachmentById(int id); + /// + /// Gets a queued email attachment by identifier + /// + /// Queued email attachment identifier + /// Queued email attachment + QueuedEmailAttachment GetQueuedEmailAttachmentById(int id); - /// - /// Deleted a queued email attachment - /// - /// Queued email attachment - void DeleteQueuedEmailAttachment(QueuedEmailAttachment attachment); + /// + /// Deleted a queued email attachment + /// + /// Queued email attachment + void DeleteQueuedEmailAttachment(QueuedEmailAttachment attachment); - /// - /// Load binary data of a queued email attachment - /// - /// Queued email attachment - /// Binary data if attachment.StorageLocation is EmailAttachmentStorageLocation.Blob, otherwise null - byte[] LoadQueuedEmailAttachmentBinary(QueuedEmailAttachment attachment); - } + /// + /// Load binary data of a queued email attachment + /// + /// Queued email attachment + /// Binary data if attachment.StorageLocation is EmailAttachmentStorageLocation.Blob, otherwise null + byte[] LoadQueuedEmailAttachmentBinary(QueuedEmailAttachment attachment); + } } diff --git a/src/Libraries/SmartStore.Services/Messages/Importer/NewsLetterSubscriptionImporter.cs b/src/Libraries/SmartStore.Services/Messages/Importer/NewsLetterSubscriptionImporter.cs index 93ba2c8044..d8cc243209 100644 --- a/src/Libraries/SmartStore.Services/Messages/Importer/NewsLetterSubscriptionImporter.cs +++ b/src/Libraries/SmartStore.Services/Messages/Importer/NewsLetterSubscriptionImporter.cs @@ -8,146 +8,134 @@ namespace SmartStore.Services.Messages.Importer { - public class NewsLetterSubscriptionImporter : IEntityImporter - { - private readonly IRepository _subscriptionRepository; - - public NewsLetterSubscriptionImporter(IRepository subscriptionRepository) - { - _subscriptionRepository = subscriptionRepository; - } - - public static string[] SupportedKeyFields - { - get - { - return new string[] { "Email" }; - } - } - - public static string[] DefaultKeyFields - { - get - { - return new string[] { "Email" }; - } - } - - public void Execute(ImportExecuteContext context) - { - var utcNow = DateTime.UtcNow; - var currentStoreId = context.Services.StoreContext.CurrentStore.Id; - - using (var scope = new DbContextScope(ctx: context.Services.DbContext, hooksEnabled: false, autoDetectChanges: false, proxyCreation: false, validateOnSave: false, autoCommit: false)) - { - var segmenter = context.DataSegmenter; - - context.Result.TotalRecords = segmenter.TotalRows; - - while (context.Abort == DataExchangeAbortion.None && segmenter.ReadNextBatch()) - { - var batch = segmenter.GetCurrentBatch(); - - // Perf: detach all entities - _subscriptionRepository.Context.DetachEntities(false); - - context.SetProgress(segmenter.CurrentSegmentFirstRowIndex - 1, segmenter.TotalRows); - - foreach (var row in batch) - { - try - { - var active = true; - var email = row.GetDataValue("Email"); - var storeId = row.GetDataValue("StoreId"); - - if (storeId == 0) - { - storeId = currentStoreId; - } - - if (row.HasDataValue("Active") && row.TryGetDataValue("Active", out active)) - { - } - else - { - active = true; // default - } - - if (email.IsEmpty()) - { - context.Result.AddWarning("Skipped empty email address", row.GetRowInfo(), "Email"); - continue; - } - - if (email.Length > 255) - { - context.Result.AddWarning("Skipped email address '{0}'. It exceeds the maximum allowed length of 255".FormatInvariant(email), row.GetRowInfo(), "Email"); - continue; - } - - if (!email.IsEmail()) - { - context.Result.AddWarning("Skipped invalid email address '{0}'".FormatInvariant(email), row.GetRowInfo(), "Email"); - continue; - } - - NewsLetterSubscription subscription = null; - - foreach (var keyName in context.KeyFieldNames) - { - switch (keyName) - { - case "Email": - subscription = _subscriptionRepository.Table - .OrderBy(x => x.Id) - .FirstOrDefault(x => x.Email == email && x.StoreId == storeId); - break; - } - - if (subscription != null) - break; - } - - if (subscription == null) - { - if (context.UpdateOnly) - { - ++context.Result.SkippedRecords; - continue; - } - - subscription = new NewsLetterSubscription - { - Active = active, - CreatedOnUtc = utcNow, - Email = email, - NewsLetterSubscriptionGuid = Guid.NewGuid(), - StoreId = storeId - }; - - _subscriptionRepository.Insert(subscription); - context.Result.NewRecords++; - } - else - { - subscription.Active = active; - - _subscriptionRepository.Update(subscription); - context.Result.ModifiedRecords++; - } - } - catch (Exception exception) - { - context.Result.AddError(exception.ToAllMessages(), row.GetRowInfo()); - } - } // for - - _subscriptionRepository.Context.SaveChanges(); + public class NewsLetterSubscriptionImporter : IEntityImporter + { + private readonly IRepository _subscriptionRepository; + + public NewsLetterSubscriptionImporter(IRepository subscriptionRepository) + { + _subscriptionRepository = subscriptionRepository; + } + + public static string[] SupportedKeyFields => new string[] { "Email" }; + + public static string[] DefaultKeyFields => new string[] { "Email" }; + + public void Execute(ImportExecuteContext context) + { + var utcNow = DateTime.UtcNow; + var currentStoreId = context.Services.StoreContext.CurrentStore.Id; + + using (var scope = new DbContextScope(ctx: context.Services.DbContext, hooksEnabled: false, autoDetectChanges: false, proxyCreation: false, validateOnSave: false, autoCommit: false)) + { + var segmenter = context.DataSegmenter; + + context.Result.TotalRecords = segmenter.TotalRows; + + while (context.Abort == DataExchangeAbortion.None && segmenter.ReadNextBatch()) + { + var batch = segmenter.GetCurrentBatch(); + + // Perf: detach all entities + _subscriptionRepository.Context.DetachEntities(false); + + context.SetProgress(segmenter.CurrentSegmentFirstRowIndex - 1, segmenter.TotalRows); + + foreach (var row in batch) + { + try + { + var active = true; + var email = row.GetDataValue("Email"); + var storeId = row.GetDataValue("StoreId"); + + if (storeId == 0) + { + storeId = currentStoreId; + } + + if (row.HasDataValue("Active") && row.TryGetDataValue("Active", out active)) + { + } + else + { + active = true; // default + } + + if (email.IsEmpty()) + { + context.Result.AddWarning("Skipped empty email address", row.GetRowInfo(), "Email"); + continue; + } + + if (email.Length > 255) + { + context.Result.AddWarning("Skipped email address '{0}'. It exceeds the maximum allowed length of 255".FormatInvariant(email), row.GetRowInfo(), "Email"); + continue; + } + + if (!email.IsEmail()) + { + context.Result.AddWarning("Skipped invalid email address '{0}'".FormatInvariant(email), row.GetRowInfo(), "Email"); + continue; + } + + NewsLetterSubscription subscription = null; + + foreach (var keyName in context.KeyFieldNames) + { + switch (keyName) + { + case "Email": + subscription = _subscriptionRepository.Table + .OrderBy(x => x.Id) + .FirstOrDefault(x => x.Email == email && x.StoreId == storeId); + break; + } + + if (subscription != null) + break; + } + + if (subscription == null) + { + if (context.UpdateOnly) + { + ++context.Result.SkippedRecords; + continue; + } + + subscription = new NewsLetterSubscription + { + Active = active, + CreatedOnUtc = utcNow, + Email = email, + NewsLetterSubscriptionGuid = Guid.NewGuid(), + StoreId = storeId + }; + + _subscriptionRepository.Insert(subscription); + context.Result.NewRecords++; + } + else + { + subscription.Active = active; + + _subscriptionRepository.Update(subscription); + context.Result.ModifiedRecords++; + } + } + catch (Exception exception) + { + context.Result.AddError(exception.ToAllMessages(), row.GetRowInfo()); + } + } // for + + _subscriptionRepository.Context.SaveChanges(); context.Services.EventPublisher.Publish(new ImportBatchExecutedEvent(context, batch)); } // while - } - } - } + } + } + } } diff --git a/src/Libraries/SmartStore.Services/Messages/MessageContext.cs b/src/Libraries/SmartStore.Services/Messages/MessageContext.cs index 056ab70126..20e93cf778 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageContext.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageContext.cs @@ -14,88 +14,85 @@ namespace SmartStore.Services.Messages /// for the creation of message templates. /// public class MessageContext - { - private IFormatProvider _formatProvider; + { + private IFormatProvider _formatProvider; - /// - /// The source message template. Required if is empty. - /// - public MessageTemplate MessageTemplate { get; set; } + /// + /// The source message template. Required if is empty. + /// + public MessageTemplate MessageTemplate { get; set; } - /// - /// The source message template name. Required if is null. - /// - public string MessageTemplateName { get; set; } + /// + /// The source message template name. Required if is null. + /// + public string MessageTemplateName { get; set; } - /// - /// If null, the email account specifies the sender. - /// - public EmailAddress SenderEmailAddress { get; set; } + /// + /// If null, the email account specifies the sender. + /// + public EmailAddress SenderEmailAddress { get; set; } - /// - /// If null, obtained from WorkContext.CurrentCustomer. - /// - public Customer Customer { get; set; } + /// + /// If null, obtained from WorkContext.CurrentCustomer. + /// + public Customer Customer { get; set; } - /// - /// If null, obtained from WorkContext.WorkingLanguage. - /// - public int? LanguageId { get; set; } + /// + /// If null, obtained from WorkContext.WorkingLanguage. + /// + public int? LanguageId { get; set; } - /// - /// If null, obtained from StoreContext.CurrentStore. - /// - public int? StoreId { get; set; } + /// + /// If null, obtained from StoreContext.CurrentStore. + /// + public int? StoreId { get; set; } - internal Language Language { get; set; } - internal Store Store { get; set; } - public EmailAccount EmailAccount { get; internal set; } + public Language Language { get; set; } + public Store Store { get; set; } + public EmailAccount EmailAccount { get; internal set; } - public bool TestMode { get; set; } + public bool TestMode { get; set; } - /// - /// If null, obtained from . - /// - public Uri BaseUri { get; set; } + /// + /// If null, obtained from . + /// + public Uri BaseUri { get; set; } - /// - /// The final template model containing all global and template specific model parts. - /// - public TemplateModel Model { get; set; } + /// + /// The final template model containing all global and template specific model parts. + /// + public TemplateModel Model { get; set; } - /// - /// If null, inferred from . - /// - public IFormatProvider FormatProvider - { - get - { - if (_formatProvider == null) - { - var culture = this.Language?.LanguageCulture; - if (culture != null && LocalizationHelper.IsValidCultureCode(culture)) - { - _formatProvider = CultureInfo.GetCultureInfo(culture); - } - } + /// + /// If null, inferred from . + /// + public IFormatProvider FormatProvider + { + get + { + if (_formatProvider == null) + { + var culture = this.Language?.LanguageCulture; + if (culture != null && LocalizationHelper.IsValidCultureCode(culture)) + { + _formatProvider = CultureInfo.GetCultureInfo(culture); + } + } - return _formatProvider ?? CultureInfo.CurrentCulture; - } - set - { - _formatProvider = value; - } - } + return _formatProvider ?? CultureInfo.CurrentCulture; + } + set => _formatProvider = value; + } - public static MessageContext Create(string messageTemplateName, int languageId, int? storeId = null, Customer customer = null) - { - return new MessageContext - { - MessageTemplateName = messageTemplateName, - LanguageId = languageId, - StoreId = storeId, - Customer = customer - }; - } - } + public static MessageContext Create(string messageTemplateName, int languageId, int? storeId = null, Customer customer = null) + { + return new MessageContext + { + MessageTemplateName = messageTemplateName, + LanguageId = languageId, + StoreId = storeId, + Customer = customer + }; + } + } } diff --git a/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs b/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs index a70a2b480e..7c2ab26c7b 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageFactory.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Data.Entity; -using System.Globalization; -using System.IO; using System.Linq; using System.Linq.Expressions; using Newtonsoft.Json; @@ -26,620 +24,641 @@ namespace SmartStore.Services.Messages { - public partial class MessageFactory : IMessageFactory - { - const string LoremIpsum = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua."; - - private readonly ICommonServices _services; - private readonly ITemplateEngine _templateEngine; - private readonly ITemplateManager _templateManager; - private readonly IMessageModelProvider _modelProvider; - private readonly IMessageTemplateService _messageTemplateService; - private readonly IQueuedEmailService _queuedEmailService; - private readonly ILanguageService _languageService; - private readonly IEmailAccountService _emailAccountService; - private readonly EmailAccountSettings _emailAccountSettings; - private readonly IDownloadService _downloadService; - - public MessageFactory( - ICommonServices services, - ITemplateEngine templateEngine, - ITemplateManager templateManager, - IMessageModelProvider modelProvider, - IMessageTemplateService messageTemplateService, - IQueuedEmailService queuedEmailService, - ILanguageService languageService, - IEmailAccountService emailAccountService, - EmailAccountSettings emailAccountSettings, - IDownloadService downloadService) - { - _services = services; - _templateEngine = templateEngine; - _templateManager = templateManager; - _modelProvider = modelProvider; - _messageTemplateService = messageTemplateService; - _queuedEmailService = queuedEmailService; - _languageService = languageService; - _emailAccountService = emailAccountService; - _emailAccountSettings = emailAccountSettings; - _downloadService = downloadService; - - T = NullLocalizer.Instance; - Logger = NullLogger.Instance; - } - - public Localizer T { get; set; } - public ILogger Logger { get; set; } - - public virtual CreateMessageResult CreateMessage(MessageContext messageContext, bool queue, params object[] modelParts) - { - Guard.NotNull(messageContext, nameof(messageContext)); - - modelParts = modelParts ?? new object[0]; - - // Handle TestMode - if (messageContext.TestMode && modelParts.Length == 0) - { - modelParts = GetTestModels(messageContext); - } - - ValidateMessageContext(messageContext, ref modelParts); - - // Create and assign model - var model = messageContext.Model = new TemplateModel(); - - // Do not create message if the template does not exist, is not authorized or not active. - if (messageContext.MessageTemplate == null) - { - return new CreateMessageResult { Model = model, MessageContext = messageContext }; - } - - // Add all global template model parts - _modelProvider.AddGlobalModelParts(messageContext); - - // Add specific template models for passed parts - foreach (var part in modelParts) - { - if (model != null) - { - _modelProvider.AddModelPart(part, messageContext); - } - } - - // Give implementors the chance to customize the final template model - _services.EventPublisher.Publish(new MessageModelCreatedEvent(messageContext, model)); - - var messageTemplate = messageContext.MessageTemplate; - var languageId = messageContext.Language.Id; - - // Render templates - var to = RenderEmailAddress(messageTemplate.To, messageContext); - var replyTo = RenderEmailAddress(messageTemplate.ReplyTo, messageContext, false); - var bcc = RenderTemplate(messageTemplate.GetLocalized((x) => x.BccEmailAddresses, languageId), messageContext, false); - - var subject = RenderTemplate(messageTemplate.GetLocalized((x) => x.Subject, languageId), messageContext); - ((dynamic)model).Email.Subject = subject; - - var body = RenderBodyTemplate(messageContext); - - // CSS inliner - body = InlineCss(body, model); - - // Model tree - var modelTree = _modelProvider.BuildModelTree(model); - var modelTreeJson = JsonConvert.SerializeObject(modelTree, Formatting.None); - if (modelTreeJson != messageTemplate.LastModelTree) - { - messageContext.MessageTemplate.LastModelTree = modelTreeJson; - if (!messageTemplate.IsTransientRecord()) - { - _messageTemplateService.UpdateMessageTemplate(messageContext.MessageTemplate); - } - } - - // Create queued email from template - var qe = new QueuedEmail - { - Priority = 5, - From = messageContext.SenderEmailAddress ?? messageContext.EmailAccount.ToEmailAddress(), - To = to.ToString(), - Bcc = bcc, - ReplyTo = replyTo?.ToString(), - Subject = subject, - Body = body, - CreatedOnUtc = DateTime.UtcNow, - EmailAccountId = messageContext.EmailAccount.Id, - SendManually = messageTemplate.SendManually - }; - - // Create and add attachments (if any) - CreateAttachments(qe, messageContext); - - if (queue) - { - // Put to queue - QueueMessage(messageContext, qe); - } - - return new CreateMessageResult { Email = qe, Model = model, MessageContext = messageContext }; - } - - public virtual void QueueMessage(MessageContext messageContext, QueuedEmail queuedEmail) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(queuedEmail, nameof(queuedEmail)); - - // Publish event so that integrators can add attachments, alter the email etc. - _services.EventPublisher.Publish(new MessageQueuingEvent - { - QueuedEmail = queuedEmail, - MessageContext = messageContext, - MessageModel = messageContext.Model - }); - - _queuedEmailService.InsertQueuedEmail(queuedEmail); - } - - private EmailAddress RenderEmailAddress(string email, MessageContext ctx, bool required = true) - { - string parsed = null; - - try - { - parsed = RenderTemplate(email, ctx, required); - - if (required || parsed.HasValue()) - { - return parsed.Convert(); - } - else - { - return null; - } - } - catch (Exception ex) - { - if (ctx.TestMode) - { - return new EmailAddress("john@doe.com", "John Doe"); - } - - var ex2 = new SmartException($"Failed to parse email address for variable '{email}'. Value was '{parsed.EmptyNull()}': {ex.Message}", ex); - _services.Notifier.Error(ex2.Message); - throw ex2; - } - } - - private string RenderTemplate(string template, MessageContext ctx, bool required = true) - { - if (!required && template.IsEmpty()) - { - return null; - } - - return _templateEngine.Render(template, ctx.Model, ctx.FormatProvider); - } - - private string RenderBodyTemplate(MessageContext ctx) - { - var key = BuildTemplateKey(ctx); - var source = ctx.MessageTemplate.GetLocalized((x) => x.Body, ctx.Language); - var fromCache = true; - var template = _templateManager.GetOrAdd(key, GetBodyTemplate); - - if (fromCache && template.Source != source) - { - // The template was resolved from template cache, but it has expired - // because the source text has changed. - template = _templateEngine.Compile(source); - _templateManager.Put(key, template); - } - - return template.Render(ctx.Model, ctx.FormatProvider); - - string GetBodyTemplate() - { - fromCache = false; - return source; - } - } - - private string BuildTemplateKey(MessageContext messageContext) - { - var prefix = messageContext.MessageTemplate.IsTransientRecord() ? "TransientTemplate/" : "MessageTemplate/"; - return prefix + messageContext.MessageTemplate.Name + "/" + messageContext.Language.Id + "/Body"; - } - - private string InlineCss(string html, dynamic model) - { - Uri baseUri = null; - - try - { - // 'Store' is a global model part, so we pretty can be sure it exists - baseUri = new Uri((string)model.Store.Url); - } - catch { } - - var pm = new PreMailer.Net.PreMailer(html, baseUri); - var result = pm.MoveCssInline(true, "#ignore"); - return result.Html; - } - - protected virtual void CreateAttachments(QueuedEmail queuedEmail, MessageContext messageContext) - { - var messageTemplate = messageContext.MessageTemplate; - var languageId = messageContext.Language.Id; - - // create attachments if any - var fileIds = (new int?[] - { - messageTemplate.GetLocalized(x => x.Attachment1FileId, languageId), - messageTemplate.GetLocalized(x => x.Attachment2FileId, languageId), - messageTemplate.GetLocalized(x => x.Attachment3FileId, languageId) - }) - .Where(x => x.HasValue) - .Select(x => x.Value) - .ToArray(); - - if (fileIds.Any()) - { - var files = _downloadService.GetDownloadsByIds(fileIds); - foreach (var file in files) - { - queuedEmail.Attachments.Add(new QueuedEmailAttachment - { - StorageLocation = EmailAttachmentStorageLocation.FileReference, - FileId = file.Id, - Name = (file.Filename.NullEmpty() ?? file.Id.ToString()) + file.Extension.EmptyNull(), - MimeType = file.ContentType.NullEmpty() ?? "application/octet-stream" - }); - } - } - } - - - private void ValidateMessageContext(MessageContext ctx, ref object[] modelParts) - { - var t = ctx.MessageTemplate; - if (t != null) - { - if (t.To.IsEmpty() || t.Subject.IsEmpty() || t.Name.IsEmpty()) - { - throw new InvalidOperationException("Message template validation failed, because at least one of the following properties has not been set: Name, To, Subject."); - } - } - - if (ctx.StoreId.GetValueOrDefault() == 0) - { - ctx.Store = _services.StoreContext.CurrentStore; - ctx.StoreId = ctx.Store.Id; - } - else - { - ctx.Store = _services.StoreService.GetStoreById(ctx.StoreId.Value); - } - - if (ctx.BaseUri == null) - { - ctx.BaseUri = new Uri(_services.StoreService.GetHost(ctx.Store)); - } - - if (ctx.LanguageId.GetValueOrDefault() == 0) - { - ctx.Language = _services.WorkContext.WorkingLanguage; - ctx.LanguageId = ctx.Language.Id; - } - else - { - ctx.Language = _languageService.GetLanguageById(ctx.LanguageId.Value); - } - - EnsureLanguageIsActive(ctx); - - var parts = modelParts?.AsEnumerable() ?? Enumerable.Empty(); - - if (ctx.Customer == null) - { - // Try to move Customer from parts to MessageContext - var customer = parts.OfType().FirstOrDefault(); - if (customer != null) - { - // Exclude the found customer from parts list - parts = parts.Where(x => !object.ReferenceEquals(x, customer)); - } - - ctx.Customer = customer ?? _services.WorkContext.CurrentCustomer; - } - - if (ctx.Customer.IsSystemAccount) - { - throw new ArgumentException("Cannot create messages for system customer accounts.", nameof(ctx)); - } - - if (ctx.MessageTemplate == null) - { - if (ctx.MessageTemplateName.IsEmpty()) - { - throw new ArgumentException("'MessageTemplateName' must not be empty if 'MessageTemplate' is null.", nameof(ctx)); - } - - ctx.MessageTemplate = _messageTemplateService.GetMessageTemplateByName(ctx.MessageTemplateName, ctx.Store.Id); - - if (ctx.MessageTemplate != null && !ctx.TestMode && !ctx.MessageTemplate.IsActive) - { - ctx.MessageTemplate = null; - } - } - - if (ctx.EmailAccount == null && ctx.MessageTemplate != null) - { - ctx.EmailAccount = GetEmailAccountOfMessageTemplate(ctx.MessageTemplate, ctx.Language.Id); - } - - // Sort parts: "IModelPart" instances must come first - var bagParts = parts.OfType(); - if (bagParts.Any()) - { - parts = bagParts.Concat(parts.Except(bagParts)); - } - - modelParts = parts.Where(x => x != null).ToArray(); - } - - protected EmailAccount GetEmailAccountOfMessageTemplate(MessageTemplate messageTemplate, int languageId) - { - // Note that the email account to be used can be specified separately for each language, that's why we use GetLocalized here. - var accountId = messageTemplate.GetLocalized(x => x.EmailAccountId, languageId); - var account = _emailAccountService.GetEmailAccountById(accountId); - - if (account == null) - { - account = _emailAccountService.GetDefaultEmailAccount(); - } - - if (account == null) - { - throw new SmartException(T("Common.Error.NoEmailAccount")); - } - - return account; - } - - private void EnsureLanguageIsActive(MessageContext ctx) - { - var language = ctx.Language; - - if (language == null || !language.Published) - { - // Load any language from the specified store - language = _languageService.GetLanguageById(_languageService.GetDefaultLanguageId(ctx.StoreId.Value)); - } - - if (language == null || !language.Published) - { - // Load any language - language = _languageService.GetAllLanguages().FirstOrDefault(); - } - - ctx.Language = language ?? throw new SmartException(T("Common.Error.NoActiveLanguage")); - } - - #region TestModels - - public virtual object[] GetTestModels(MessageContext messageContext) - { - var templateName = (messageContext.MessageTemplate?.Name ?? messageContext.MessageTemplateName); - - var factories = new Dictionary>(StringComparer.OrdinalIgnoreCase) - { - { "BlogComment", () => GetRandomEntity(x => true) }, - { "Product", () => GetRandomEntity(x => !x.Deleted && !x.IsSystemProduct && x.VisibleIndividually && x.Published) }, - { "Customer", () => GetRandomEntity(x => !x.Deleted && !x.IsSystemAccount && !string.IsNullOrEmpty(x.Email)) }, - { "Order", () => GetRandomEntity(x => !x.Deleted) }, - { "Shipment", () => GetRandomEntity(x => !x.Order.Deleted) }, - { "OrderNote", () => GetRandomEntity(x => !x.Order.Deleted) }, - { "RecurringPayment", () => GetRandomEntity(x => !x.Deleted) }, - { "NewsLetterSubscription", () => GetRandomEntity(x => true) }, - { "Campaign", () => GetRandomEntity(x => true) }, - { "ReturnRequest", () => GetRandomEntity(x => true) }, - { "OrderItem", () => GetRandomEntity(x => !x.Order.Deleted) }, - { "ForumTopic", () => GetRandomEntity(x => true) }, - { "ForumPost", () => GetRandomEntity(x => true) }, - { "PrivateMessage", () => GetRandomEntity(x => true) }, - { "GiftCard", () => GetRandomEntity(x => true) }, - { "ProductReview", () => GetRandomEntity(x => !x.Product.Deleted && !x.Product.IsSystemProduct && x.Product.VisibleIndividually && x.Product.Published) }, - { "NewsComment", () => GetRandomEntity(x => x.NewsItem.Published) }, - { "WalletHistory", () => GetRandomEntity(x => true) } - }; - - var modelNames = messageContext.MessageTemplate.ModelTypes - .SplitSafe(",") - .Select(x => x.Trim()) - .Distinct() - .ToArray(); - - var models = new Dictionary(); - var result = new List(); - - foreach (var modelName in modelNames) - { - var model = GetModelFromExpression(modelName, models, factories); - if (model != null) - { - result.Add(model); - } - } - - // Some models are special - var isTransientTemplate = messageContext.MessageTemplate != null && messageContext.MessageTemplate.IsTransientRecord(); - - if (!isTransientTemplate) - { - switch (templateName) - { - case MessageTemplateNames.SystemContactUs: - result.Add(new NamedModelPart("Message") - { - ["Subject"] = "Test subject", - ["Message"] = LoremIpsum, - ["SenderEmail"] = "jane@doe.com", - ["SenderName"] = "Jane Doe" - }); - break; - case MessageTemplateNames.ProductQuestion: - result.Add(new NamedModelPart("Message") - { - ["Message"] = LoremIpsum, - ["SenderEmail"] = "jane@doe.com", - ["SenderName"] = "Jane Doe", - ["SenderPhone"] = "123456789" - }); - break; - case MessageTemplateNames.ShareProduct: - result.Add(new NamedModelPart("Message") - { - ["Body"] = LoremIpsum, - ["From"] = "jane@doe.com", - ["To"] = "john@doe.com", - }); - break; - case MessageTemplateNames.ShareWishlist: - result.Add(new NamedModelPart("Wishlist") - { - ["PersonalMessage"] = LoremIpsum, - ["From"] = "jane@doe.com", - ["To"] = "john@doe.com", - }); - break; - case MessageTemplateNames.NewVatSubmittedStoreOwner: - result.Add(new NamedModelPart("VatValidationResult") - { - ["Name"] = "VatName", - ["Address"] = "VatAddress" - }); - break; - } - } - - return result.ToArray(); - } - - private object GetModelFromExpression(string expression, IDictionary models, IDictionary> factories) - { - object currentModel = null; - int dotIndex = 0; - int len = expression.Length; - bool bof = true; - string token = null; - - for (var i = 0; i < len; i++) - { - if (expression[i] == '.') - { - bof = false; - token = expression.Substring(0, i); - } - else if (i == len - 1) - { - // End reached - token = expression; - } - else - { - continue; - } - - if (!models.TryGetValue(token, out currentModel)) - { - if (bof) - { - // It's a simple dot-less expression where the token - // is actually the model name - currentModel = factories.Get(token)?.Invoke(); - } - else - { - // Sub-token, e.g. "Order.Customer" - // Get "Customer" part, this is our property name, NOT the model name - var propName = token.Substring(dotIndex + 1); - // Get parent model "Order" - var parentModel = models.Get(token.Substring(0, dotIndex)); - if (parentModel == null) - break; - - if (parentModel is ITestModel) - { - // When the parent model is a test model, we need to create a random instance - // instead of using the property value (which is null/void in this case) - currentModel = factories.Get(propName)?.Invoke(); - } - else - { - // Get "Customer" property of Order - var fastProp = FastProperty.GetProperty(parentModel.GetType(), propName, PropertyCachingStrategy.Uncached); - if (fastProp != null) - { - // Get "Customer" value - var propValue = fastProp.GetValue(parentModel); - if (propValue != null) - { - currentModel = propValue; - //// Resolve logical model name... - //var modelName = _modelProvider.ResolveModelName(propValue); - //if (modelName != null) - //{ - // // ...and create the value - // currentModel = factories.Get(modelName)?.Invoke(); - //} - } - } - } - } - - if (currentModel == null) - break; - - // Put it in dict as e.g. "Order.Customer" - models[token] = currentModel; - } - - if (!bof) - { - dotIndex = i; - } - } - - return currentModel; - } - - private object GetRandomEntity(Expression> predicate) where T : BaseEntity, new() - { - var dbSet = _services.DbContext.Set(); - - var query = dbSet.Where(predicate); - - // Determine how many entities match the given predicate - var count = query.Count(); - - object result; - - if (count > 0) - { - // Fetch a random one - var skip = CommonHelper.GenerateRandomInteger(0, count); - result = query.OrderBy(x => x.Id).Skip(() => skip).FirstOrDefault(); - } - else - { - // No entity macthes the predicate. Provide a fallback test entity - var entity = Activator.CreateInstance(); - result = _templateEngine.CreateTestModelFor(entity, entity.GetUnproxiedType().Name); - } - - return result; - } - - #endregion - } + public partial class MessageFactory : IMessageFactory + { + const string LoremIpsum = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua."; + + private readonly ICommonServices _services; + private readonly ITemplateEngine _templateEngine; + private readonly ITemplateManager _templateManager; + private readonly IMessageModelProvider _modelProvider; + private readonly IMessageTemplateService _messageTemplateService; + private readonly IQueuedEmailService _queuedEmailService; + private readonly ILanguageService _languageService; + private readonly IEmailAccountService _emailAccountService; + private readonly EmailAccountSettings _emailAccountSettings; + private readonly IMediaService _mediaService; + + public MessageFactory( + ICommonServices services, + ITemplateEngine templateEngine, + ITemplateManager templateManager, + IMessageModelProvider modelProvider, + IMessageTemplateService messageTemplateService, + IQueuedEmailService queuedEmailService, + ILanguageService languageService, + IEmailAccountService emailAccountService, + EmailAccountSettings emailAccountSettings, + IMediaService mediaService) + { + _services = services; + _templateEngine = templateEngine; + _templateManager = templateManager; + _modelProvider = modelProvider; + _messageTemplateService = messageTemplateService; + _queuedEmailService = queuedEmailService; + _languageService = languageService; + _emailAccountService = emailAccountService; + _emailAccountSettings = emailAccountSettings; + _mediaService = mediaService; + } + + public Localizer T { get; set; } = NullLocalizer.Instance; + public ILogger Logger { get; set; } = NullLogger.Instance; + + public virtual CreateMessageResult CreateMessage(MessageContext messageContext, bool queue, params object[] modelParts) + { + Guard.NotNull(messageContext, nameof(messageContext)); + + modelParts = modelParts ?? new object[0]; + + // Handle TestMode + if (messageContext.TestMode && modelParts.Length == 0) + { + modelParts = GetTestModels(messageContext); + } + + ValidateMessageContext(messageContext, ref modelParts); + + // Create and assign model + var model = messageContext.Model = new TemplateModel(); + + // Do not create message if the template does not exist, is not authorized or not active. + if (messageContext.MessageTemplate == null) + { + return new CreateMessageResult { Model = model, MessageContext = messageContext }; + } + + // Add all global template model parts + _modelProvider.AddGlobalModelParts(messageContext); + + // Add specific template models for passed parts + foreach (var part in modelParts) + { + if (model != null) + { + _modelProvider.AddModelPart(part, messageContext); + } + } + + // Give implementors the chance to customize the final template model + _services.EventPublisher.Publish(new MessageModelCreatedEvent(messageContext, model)); + + var messageTemplate = messageContext.MessageTemplate; + var languageId = messageContext.Language.Id; + + // Render templates + var to = RenderEmailAddress(messageTemplate.To, messageContext); + var replyTo = RenderEmailAddress(messageTemplate.ReplyTo, messageContext, false); + var bcc = RenderTemplate(messageTemplate.GetLocalized((x) => x.BccEmailAddresses, languageId), messageContext, false); + + var subject = RenderTemplate(messageTemplate.GetLocalized((x) => x.Subject, languageId), messageContext); + ((dynamic)model).Email.Subject = subject; + + var body = RenderBodyTemplate(messageContext); + + // CSS inliner + body = InlineCss(body, model); + + // Model tree + var modelTree = _modelProvider.BuildModelTree(model); + var modelTreeJson = JsonConvert.SerializeObject(modelTree, Formatting.None); + if (modelTreeJson != messageTemplate.LastModelTree) + { + messageContext.MessageTemplate.LastModelTree = modelTreeJson; + if (!messageTemplate.IsTransientRecord()) + { + _messageTemplateService.UpdateMessageTemplate(messageContext.MessageTemplate); + } + } + + // Create queued email from template + var qe = new QueuedEmail + { + Priority = 5, + From = messageContext.SenderEmailAddress ?? messageContext.EmailAccount.ToEmailAddress(), + To = to.ToString(), + Bcc = bcc, + ReplyTo = replyTo?.ToString(), + Subject = subject, + Body = body, + CreatedOnUtc = DateTime.UtcNow, + EmailAccountId = messageContext.EmailAccount.Id, + SendManually = messageTemplate.SendManually + }; + + // Create and add attachments (if any) + CreateAttachments(qe, messageContext); + + if (queue) + { + // Put to queue + QueueMessage(messageContext, qe); + } + + return new CreateMessageResult { Email = qe, Model = model, MessageContext = messageContext }; + } + + public virtual void QueueMessage(MessageContext messageContext, QueuedEmail queuedEmail) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(queuedEmail, nameof(queuedEmail)); + + // Publish event so that integrators can add attachments, alter the email etc. + _services.EventPublisher.Publish(new MessageQueuingEvent + { + QueuedEmail = queuedEmail, + MessageContext = messageContext, + MessageModel = messageContext.Model + }); + + _queuedEmailService.InsertQueuedEmail(queuedEmail); + } + + private EmailAddress RenderEmailAddress(string email, MessageContext ctx, bool required = true) + { + string parsed = null; + + try + { + parsed = RenderTemplate(email, ctx, required); + + if (required || parsed.HasValue()) + { + return parsed.Convert(); + } + else + { + return null; + } + } + catch (Exception ex) + { + if (ctx.TestMode) + { + return new EmailAddress("john@doe.com", "John Doe"); + } + + var ex2 = new SmartException($"Failed to parse email address for variable '{email}'. Value was '{parsed.EmptyNull()}': {ex.Message}", ex); + _services.Notifier.Error(ex2.Message); + throw ex2; + } + } + + private string RenderTemplate(string template, MessageContext ctx, bool required = true) + { + if (!required && template.IsEmpty()) + { + return null; + } + + return _templateEngine.Render(template, ctx.Model, ctx.FormatProvider); + } + + private string RenderBodyTemplate(MessageContext ctx) + { + var key = BuildTemplateKey(ctx); + var source = ctx.MessageTemplate.GetLocalized((x) => x.Body, ctx.Language); + var fromCache = true; + var template = _templateManager.GetOrAdd(key, GetBodyTemplate); + + if (fromCache && template.Source != source) + { + // The template was resolved from template cache, but it has expired + // because the source text has changed. + template = _templateEngine.Compile(source); + _templateManager.Put(key, template); + } + + return template.Render(ctx.Model, ctx.FormatProvider); + + string GetBodyTemplate() + { + fromCache = false; + return source; + } + } + + private string BuildTemplateKey(MessageContext messageContext) + { + var prefix = messageContext.MessageTemplate.IsTransientRecord() ? "TransientTemplate/" : "MessageTemplate/"; + return prefix + messageContext.MessageTemplate.Name + "/" + messageContext.Language.Id + "/Body"; + } + + private string InlineCss(string html, dynamic model) + { + Uri baseUri = null; + + try + { + // 'Store' is a global model part, so we pretty can be sure it exists + baseUri = new Uri((string)model.Store.Url); + } + catch { } + + var pm = new PreMailer.Net.PreMailer(html, baseUri); + var result = pm.MoveCssInline(true, "#ignore"); + return result.Html; + } + + protected virtual void CreateAttachments(QueuedEmail queuedEmail, MessageContext messageContext) + { + var messageTemplate = messageContext.MessageTemplate; + var languageId = messageContext.Language.Id; + + // Create attachments if any. + var fileIds = (new int?[] + { + messageTemplate.GetLocalized(x => x.Attachment1FileId, languageId), + messageTemplate.GetLocalized(x => x.Attachment2FileId, languageId), + messageTemplate.GetLocalized(x => x.Attachment3FileId, languageId) + }) + .Where(x => x.HasValue) + .Select(x => x.Value) + .Distinct() + .ToArray(); + + if (fileIds.Any()) + { + var files = _mediaService.GetFilesByIds(fileIds); + foreach (var file in files) + { + queuedEmail.Attachments.Add(new QueuedEmailAttachment + { + StorageLocation = EmailAttachmentStorageLocation.FileReference, + MediaFileId = file.Id, + Name = file.Name, + MimeType = file.MimeType + }); + } + } + } + + + private void ValidateMessageContext(MessageContext ctx, ref object[] modelParts) + { + var t = ctx.MessageTemplate; + if (t != null) + { + if (t.To.IsEmpty() || t.Subject.IsEmpty() || t.Name.IsEmpty()) + { + throw new InvalidOperationException("Message template validation failed, because at least one of the following properties has not been set: Name, To, Subject."); + } + } + + if (ctx.StoreId.GetValueOrDefault() == 0) + { + ctx.Store = _services.StoreContext.CurrentStore; + ctx.StoreId = ctx.Store.Id; + } + else + { + ctx.Store = _services.StoreService.GetStoreById(ctx.StoreId.Value); + } + + if (ctx.BaseUri == null) + { + ctx.BaseUri = new Uri(_services.StoreService.GetHost(ctx.Store)); + } + + if (ctx.LanguageId.GetValueOrDefault() == 0) + { + ctx.Language = _services.WorkContext.WorkingLanguage; + ctx.LanguageId = ctx.Language.Id; + } + else + { + ctx.Language = _languageService.GetLanguageById(ctx.LanguageId.Value); + } + + EnsureLanguageIsActive(ctx); + + var parts = modelParts?.AsEnumerable() ?? Enumerable.Empty(); + + if (ctx.Customer == null) + { + // Try to move Customer from parts to MessageContext + var customer = parts.OfType().FirstOrDefault(); + if (customer != null) + { + // Exclude the found customer from parts list + parts = parts.Where(x => !object.ReferenceEquals(x, customer)); + } + + ctx.Customer = customer ?? _services.WorkContext.CurrentCustomer; + } + + if (ctx.Customer.IsSystemAccount) + { + throw new ArgumentException("Cannot create messages for system customer accounts.", nameof(ctx)); + } + + if (ctx.MessageTemplate == null) + { + if (ctx.MessageTemplateName.IsEmpty()) + { + throw new ArgumentException("'MessageTemplateName' must not be empty if 'MessageTemplate' is null.", nameof(ctx)); + } + + ctx.MessageTemplate = _messageTemplateService.GetMessageTemplateByName(ctx.MessageTemplateName, ctx.Store.Id); + + if (ctx.MessageTemplate != null && !ctx.TestMode && !ctx.MessageTemplate.IsActive) + { + ctx.MessageTemplate = null; + } + } + + if (ctx.EmailAccount == null && ctx.MessageTemplate != null) + { + ctx.EmailAccount = GetEmailAccountOfMessageTemplate(ctx.MessageTemplate, ctx.Language.Id); + } + + // Sort parts: "IModelPart" instances must come first + var bagParts = parts.OfType(); + if (bagParts.Any()) + { + parts = bagParts.Concat(parts.Except(bagParts)); + } + + modelParts = parts.Where(x => x != null).ToArray(); + } + + protected EmailAccount GetEmailAccountOfMessageTemplate(MessageTemplate messageTemplate, int languageId) + { + // Note that the email account to be used can be specified separately for each language, that's why we use GetLocalized here. + var accountId = messageTemplate.GetLocalized(x => x.EmailAccountId, languageId); + var account = _emailAccountService.GetEmailAccountById(accountId); + + if (account == null) + { + account = _emailAccountService.GetDefaultEmailAccount(); + } + + if (account == null) + { + throw new SmartException(T("Common.Error.NoEmailAccount")); + } + + return account; + } + + private void EnsureLanguageIsActive(MessageContext ctx) + { + var language = ctx.Language; + + if (language == null || !language.Published) + { + // Load any language from the specified store + language = _languageService.GetLanguageById(_languageService.GetDefaultLanguageId(ctx.StoreId.Value)); + } + + if (language == null || !language.Published) + { + // Load any language + language = _languageService.GetAllLanguages().FirstOrDefault(); + } + + ctx.Language = language ?? throw new SmartException(T("Common.Error.NoActiveLanguage")); + } + + #region TestModels + + public virtual object[] GetTestModels(MessageContext messageContext) + { + var templateName = messageContext.MessageTemplate?.Name ?? messageContext.MessageTemplateName; + + var factories = new Dictionary>(StringComparer.OrdinalIgnoreCase) + { + { "BlogComment", () => GetRandomEntity(x => true) }, + { "Product", () => GetRandomEntity(x => !x.Deleted && !x.IsSystemProduct && x.Visibility != ProductVisibility.Hidden && x.Published) }, + { "Customer", () => GetRandomEntity(x => !x.Deleted && !x.IsSystemAccount && !string.IsNullOrEmpty(x.Email)) }, + { "Order", () => GetRandomEntity(x => !x.Deleted) }, + { "Shipment", () => GetRandomEntity(x => !x.Order.Deleted) }, + { "OrderNote", () => GetRandomEntity(x => !x.Order.Deleted) }, + { "RecurringPayment", () => GetRandomEntity(x => !x.Deleted) }, + { "NewsLetterSubscription", () => GetRandomEntity(x => true) }, + { "Campaign", () => GetRandomEntity(x => true) }, + { "ReturnRequest", () => GetRandomEntity(x => true) }, + { "OrderItem", () => GetRandomEntity(x => !x.Order.Deleted) }, + { "ForumTopic", () => GetRandomEntity(x => true) }, + { "ForumPost", () => GetRandomEntity(x => true) }, + { "PrivateMessage", () => GetRandomEntity(x => true) }, + { "GiftCard", () => GetRandomEntity(x => true) }, + { "ProductReview", () => GetRandomEntity(x => !x.Product.Deleted && !x.Product.IsSystemProduct && x.Product.Visibility != ProductVisibility.Hidden && x.Product.Published) }, + { "NewsComment", () => GetRandomEntity(x => x.NewsItem.Published) }, + { "WalletHistory", () => GetRandomEntity(x => true) } + }; + + var modelNames = messageContext.MessageTemplate.ModelTypes + .SplitSafe(",") + .Select(x => x.Trim()) + .Distinct() + .ToArray(); + + var models = new Dictionary(); + var result = new List(); + + foreach (var modelName in modelNames) + { + var model = GetModelFromExpression(modelName, models, factories); + if (model != null) + { + result.Add(model); + } + } + + // Some models are special. + var isTransientTemplate = messageContext.MessageTemplate != null && messageContext.MessageTemplate.IsTransientRecord(); + + if (!isTransientTemplate) + { + switch (templateName) + { + case MessageTemplateNames.SystemContactUs: + result.Add(new NamedModelPart("Message") + { + ["Subject"] = "Test subject", + ["Message"] = LoremIpsum, + ["SenderEmail"] = "jane@doe.com", + ["SenderName"] = "Jane Doe" + }); + break; + case MessageTemplateNames.ProductQuestion: + result.Add(new NamedModelPart("Message") + { + ["Message"] = LoremIpsum, + ["SenderEmail"] = "jane@doe.com", + ["SenderName"] = "Jane Doe", + ["SenderPhone"] = "123456789" + }); + break; + case MessageTemplateNames.ShareProduct: + result.Add(new NamedModelPart("Message") + { + ["Body"] = LoremIpsum, + ["From"] = "jane@doe.com", + ["To"] = "john@doe.com", + }); + break; + case MessageTemplateNames.ShareWishlist: + result.Add(new NamedModelPart("Wishlist") + { + ["PersonalMessage"] = LoremIpsum, + ["From"] = "jane@doe.com", + ["To"] = "john@doe.com", + }); + break; + case MessageTemplateNames.NewVatSubmittedStoreOwner: + result.Add(new NamedModelPart("VatValidationResult") + { + ["Name"] = "VatName", + ["Address"] = "VatAddress" + }); + break; + case MessageTemplateNames.SystemGeneric: + result.Add(new NamedModelPart("Generic") + { + ["Email"] = "john@doe.com", + ["Subject"] = "Subject", + ["Body"] = LoremIpsum + }); + break; + } + } + + return result.ToArray(); + } + + private object GetModelFromExpression(string expression, IDictionary models, IDictionary> factories) + { + object currentModel = null; + int dotIndex = 0; + int len = expression.Length; + bool bof = true; + string token = null; + + for (var i = 0; i < len; i++) + { + if (expression[i] == '.') + { + bof = false; + token = expression.Substring(0, i); + } + else if (i == len - 1) + { + // End reached + token = expression; + } + else + { + continue; + } + + if (!models.TryGetValue(token, out currentModel)) + { + if (bof) + { + // It's a simple dot-less expression where the token + // is actually the model name + currentModel = factories.Get(token)?.Invoke(); + } + else + { + // Sub-token, e.g. "Order.Customer" + // Get "Customer" part, this is our property name, NOT the model name + var propName = token.Substring(dotIndex + 1); + // Get parent model "Order" + var parentModel = models.Get(token.Substring(0, dotIndex)); + if (parentModel == null) + break; + + if (parentModel is ITestModel) + { + // When the parent model is a test model, we need to create a random instance + // instead of using the property value (which is null/void in this case) + currentModel = factories.Get(propName)?.Invoke(); + } + else + { + // Get "Customer" property of Order + var fastProp = FastProperty.GetProperty(parentModel.GetType(), propName, PropertyCachingStrategy.Uncached); + if (fastProp != null) + { + // Get "Customer" value + var propValue = fastProp.GetValue(parentModel); + if (propValue != null) + { + currentModel = propValue; + //// Resolve logical model name... + //var modelName = _modelProvider.ResolveModelName(propValue); + //if (modelName != null) + //{ + // // ...and create the value + // currentModel = factories.Get(modelName)?.Invoke(); + //} + } + } + } + } + + if (currentModel == null) + break; + + // Put it in dict as e.g. "Order.Customer" + models[token] = currentModel; + } + + if (!bof) + { + dotIndex = i; + } + } + + return currentModel; + } + + private object GetRandomEntity(Expression> predicate) where T : BaseEntity, new() + { + var dbSet = _services.DbContext.Set(); + + var query = dbSet.Where(predicate); + + // Determine how many entities match the given predicate. + var count = query.Count(); + + object result; + + if (count > 0) + { + // Fetch a random one. + var skip = CommonHelper.GenerateRandomInteger(0, count); + result = query.OrderBy(x => x.Id).Skip(() => skip).FirstOrDefault(); + } + else + { + // No entity matches the predicate. Provide a fallback test entity. + var entity = Activator.CreateInstance(); + + if (entity is NewsLetterSubscription subscription) + { + // Campaign preview requires NewsLetterSubscription entity. + subscription.NewsLetterSubscriptionGuid = Guid.NewGuid(); + subscription.Email = "john@doe.com"; + subscription.Active = true; + subscription.CreatedOnUtc = DateTime.UtcNow; + subscription.WorkingLanguageId = _services.WorkContext.WorkingLanguage.Id; + + result = entity; + } + else + { + result = _templateEngine.CreateTestModelFor(entity, entity.GetUnproxiedType().Name); + } + } + + return result; + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.OrderParts.cs b/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.OrderParts.cs index b88fda1d9a..469a57cd4b 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.OrderParts.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.OrderParts.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Dynamic; using System.Linq; using SmartStore.ComponentModel; @@ -18,295 +17,292 @@ using SmartStore.Services.Media; using SmartStore.Services.Orders; using SmartStore.Services.Payments; +using SmartStore.Services.Shipping; namespace SmartStore.Services.Messages { - public partial class MessageModelProvider - { - protected virtual object CreateModelPart(Order part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var allow = new HashSet - { - nameof(part.Id), - nameof(part.OrderNumber), - nameof(part.OrderGuid), - nameof(part.StoreId), - nameof(part.OrderStatus), - nameof(part.PaymentStatus), - nameof(part.ShippingStatus), - nameof(part.CustomerTaxDisplayType), - nameof(part.TaxRatesDictionary), - nameof(part.VatNumber), - nameof(part.AffiliateId), - nameof(part.CustomerIp), - nameof(part.CardType), - nameof(part.CardName), - nameof(part.MaskedCreditCardNumber), - nameof(part.DirectDebitAccountHolder), - nameof(part.DirectDebitBankCode), // TODO: (mc) Liquid > Bank data (?) + public partial class MessageModelProvider + { + protected virtual object CreateModelPart(Order part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var allow = new HashSet + { + nameof(part.Id), + nameof(part.OrderNumber), + nameof(part.OrderGuid), + nameof(part.StoreId), + nameof(part.OrderStatus), + nameof(part.PaymentStatus), + nameof(part.ShippingStatus), + nameof(part.CustomerTaxDisplayType), + nameof(part.TaxRatesDictionary), + nameof(part.VatNumber), + nameof(part.AffiliateId), + nameof(part.CustomerIp), + nameof(part.CardType), + nameof(part.CardName), + nameof(part.MaskedCreditCardNumber), + nameof(part.DirectDebitAccountHolder), + nameof(part.DirectDebitBankCode), // TODO: (mc) Liquid > Bank data (?) nameof(part.PurchaseOrderNumber), - nameof(part.ShippingMethod), - nameof(part.PaymentMethodSystemName), - nameof(part.ShippingRateComputationMethodSystemName) + nameof(part.ShippingMethod), + nameof(part.PaymentMethodSystemName), + nameof(part.ShippingRateComputationMethodSystemName) // TODO: (mc) Liquid > More whitelisting? }; - var m = new HybridExpando(part, allow, MemberOptMethod.Allow); - var d = m as dynamic; - - d.ID = part.Id; - d.Billing = CreateModelPart(part.BillingAddress, messageContext); - if (part.ShippingAddress != null) - { - d.Shipping = part.ShippingAddress.IsPostalDataEqual(part.BillingAddress) == true ? null : CreateModelPart(part.ShippingAddress, messageContext); - } - d.CustomerEmail = part.BillingAddress.Email.NullEmpty(); - d.CustomerComment = part.CustomerOrderComment.NullEmpty(); - d.Disclaimer = GetTopic("Disclaimer", messageContext); - d.ConditionsOfUse = GetTopic("ConditionsOfUse", messageContext); - d.Status = part.OrderStatus.GetLocalizedEnum(_services.Localization, messageContext.Language.Id); - d.CreatedOn = ToUserDate(part.CreatedOnUtc, messageContext); - d.PaidOn = ToUserDate(part.PaidDateUtc, messageContext); - - // Payment method - var paymentMethodName = part.PaymentMethodSystemName; - var paymentMethod = _services.Resolve().GetProvider(part.PaymentMethodSystemName); - if (paymentMethod != null) - { - paymentMethodName = GetLocalizedValue(messageContext, paymentMethod.Metadata, nameof(paymentMethod.Metadata.FriendlyName), x => x.FriendlyName); - } - d.PaymentMethod = paymentMethodName.NullEmpty(); - - d.Url = part.Customer != null && !part.Customer.IsGuest() - ? BuildActionUrl("Details", "Order", new { id = part.Id, area = "" }, messageContext) - : null; - - // Overrides - m.Properties["OrderNumber"] = part.GetOrderNumber().NullEmpty(); - m.Properties["AcceptThirdPartyEmailHandOver"] = GetBoolResource(part.AcceptThirdPartyEmailHandOver, messageContext); - - // Items, Totals & Co. - d.Items = part.OrderItems.Where(x => x.Product != null).Select(x => CreateModelPart(x, messageContext)).ToList(); - d.Totals = CreateOrderTotalsPart(part, messageContext); - - // Checkout Attributes - if (part.CheckoutAttributeDescription.HasValue()) - { - d.CheckoutAttributes = HtmlUtils.ConvertPlainTextToTable(HtmlUtils.ConvertHtmlToPlainText(part.CheckoutAttributeDescription)).NullEmpty(); - } - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateOrderTotalsPart(Order order, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(order, nameof(order)); - - var language = messageContext.Language; - var currencyService = _services.Resolve(); - var paymentService = _services.Resolve(); - var priceFormatter = _services.Resolve(); - var taxSettings = _services.Settings.LoadSetting(messageContext.Store.Id); - - var taxRates = new SortedDictionary(); - Money cusTaxTotal = null; - Money cusDiscount = null; - Money cusRounding = null; - Money cusTotal = null; - - var subTotals = GetSubTotals(order, messageContext); - - // Shipping - bool dislayShipping = order.ShippingStatus != ShippingStatus.ShippingNotRequired; - - // Payment method fee - bool displayPaymentMethodFee = true; - if (order.PaymentMethodAdditionalFeeExclTax == decimal.Zero) - { - displayPaymentMethodFee = false; - } - - // Tax - bool displayTax = true; - bool displayTaxRates = true; - if (taxSettings.HideTaxInOrderSummary && order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax) - { - displayTax = false; - displayTaxRates = false; - } - else - { - if (order.OrderTax == 0 && taxSettings.HideZeroTax) - { - displayTax = false; - displayTaxRates = false; - } - else - { - taxRates = new SortedDictionary(); - foreach (var tr in order.TaxRatesDictionary) - { - taxRates.Add(tr.Key, currencyService.ConvertCurrency(tr.Value, order.CurrencyRate)); - } - - displayTaxRates = taxSettings.DisplayTaxRates && taxRates.Count > 0; - displayTax = !displayTaxRates; - - cusTaxTotal = FormatPrice(order.OrderTax, order, messageContext); - } - } - - // Discount - bool dislayDiscount = false; - if (order.OrderDiscount > decimal.Zero) - { - cusDiscount = FormatPrice(-order.OrderDiscount, order, messageContext); - dislayDiscount = true; - } - - // Total - var roundingAmount = decimal.Zero; - var orderTotal = order.GetOrderTotalInCustomerCurrency(currencyService, paymentService, out roundingAmount); - cusTotal = FormatPrice(orderTotal, order.CustomerCurrencyCode, messageContext); - - // Rounding - if (roundingAmount != decimal.Zero) - { - cusRounding = FormatPrice(roundingAmount, order.CustomerCurrencyCode, messageContext); - } - - // Model - dynamic m = new ExpandoObject(); - - m.SubTotal = subTotals.SubTotal; - m.SubTotalDiscount = subTotals.DisplaySubTotalDiscount ? subTotals.SubTotalDiscount : null; - m.Shipping = dislayShipping ? subTotals.ShippingTotal : null; - m.Payment = displayPaymentMethodFee ? subTotals.PaymentFee : null; - m.Tax = displayTax ? cusTaxTotal : null; - m.Discount = dislayDiscount ? cusDiscount : null; - m.RoundingDiff = cusRounding; - m.Total = cusTotal; - m.IsGross = order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax; - - // TaxRates - m.TaxRates = !displayTaxRates ? (object[])null : taxRates.Select(x => - { - return new - { - Rate = T("Order.TaxRateLine", language.Id, priceFormatter.FormatTaxRate(x.Key)).Text, - Value = FormatPrice(x.Value, order, messageContext) - }; - }).ToArray(); - - - // Gift Cards - m.GiftCardUsage = order.GiftCardUsageHistory.Count == 0 ? (object[])null : order.GiftCardUsageHistory.Select(x => - { - return new - { - GiftCard = T("Order.GiftCardInfo", language.Id, x.GiftCard.GiftCardCouponCode).Text, - UsedAmount = FormatPrice(-x.UsedValue, order, messageContext), - RemainingAmount = FormatPrice(x.GiftCard.GetGiftCardRemainingAmount(), order, messageContext) - }; - }).ToArray(); - - // Reward Points - m.RedeemedRewardPoints = order.RedeemedRewardPointsEntry == null ? null : new - { - Title = T("Order.RewardPoints", language.Id, -order.RedeemedRewardPointsEntry.Points).Text, - Amount = FormatPrice(-order.RedeemedRewardPointsEntry.UsedAmount, order, messageContext) - }; + var m = new HybridExpando(part, allow, MemberOptMethod.Allow); + var d = m as dynamic; + + d.ID = part.Id; + d.Billing = CreateModelPart(part.BillingAddress, messageContext); + if (part.ShippingAddress != null) + { + d.Shipping = part.ShippingAddress.IsPostalDataEqual(part.BillingAddress) == true ? null : CreateModelPart(part.ShippingAddress, messageContext); + } + d.CustomerEmail = part.BillingAddress.Email.NullEmpty(); + d.CustomerComment = part.CustomerOrderComment.NullEmpty(); + d.Disclaimer = GetTopic("Disclaimer", messageContext); + d.ConditionsOfUse = GetTopic("ConditionsOfUse", messageContext); + d.Status = part.OrderStatus.GetLocalizedEnum(_services.Localization, messageContext.Language.Id); + d.CreatedOn = ToUserDate(part.CreatedOnUtc, messageContext); + d.PaidOn = ToUserDate(part.PaidDateUtc, messageContext); + + // Payment method + var paymentMethodName = part.PaymentMethodSystemName; + var paymentMethod = _services.Resolve().GetProvider(part.PaymentMethodSystemName); + if (paymentMethod != null) + { + paymentMethodName = GetLocalizedValue(messageContext, paymentMethod.Metadata, nameof(paymentMethod.Metadata.FriendlyName), x => x.FriendlyName); + } + d.PaymentMethod = paymentMethodName.NullEmpty(); + + d.Url = part.Customer != null && !part.Customer.IsGuest() + ? BuildActionUrl("Details", "Order", new { id = part.Id, area = "" }, messageContext) + : null; + + // Overrides + m.Properties["OrderNumber"] = part.GetOrderNumber().NullEmpty(); + m.Properties["AcceptThirdPartyEmailHandOver"] = GetBoolResource(part.AcceptThirdPartyEmailHandOver, messageContext); + + // Items, Totals & Co. + d.Items = part.OrderItems.Where(x => x.Product != null).Select(x => CreateModelPart(x, messageContext)).ToList(); + d.Totals = CreateOrderTotalsPart(part, messageContext); + + // Checkout Attributes + if (part.CheckoutAttributeDescription.HasValue()) + { + d.CheckoutAttributes = HtmlUtils.ConvertPlainTextToTable(HtmlUtils.ConvertHtmlToPlainText(part.CheckoutAttributeDescription)).NullEmpty(); + } + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateOrderTotalsPart(Order order, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(order, nameof(order)); + + var language = messageContext.Language; + var currencyService = _services.Resolve(); + var paymentService = _services.Resolve(); + var priceFormatter = _services.Resolve(); + var taxSettings = _services.Settings.LoadSetting(messageContext.Store.Id); + + var taxRates = new SortedDictionary(); + Money cusTaxTotal = null; + Money cusDiscount = null; + Money cusRounding = null; + Money cusTotal = null; + + var subTotals = GetSubTotals(order, messageContext); + + // Shipping + bool dislayShipping = order.ShippingStatus != ShippingStatus.ShippingNotRequired; + + // Payment method fee + bool displayPaymentMethodFee = true; + if (order.PaymentMethodAdditionalFeeExclTax == decimal.Zero) + { + displayPaymentMethodFee = false; + } + + // Tax + bool displayTax = true; + bool displayTaxRates = true; + if (taxSettings.HideTaxInOrderSummary && order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax) + { + displayTax = false; + displayTaxRates = false; + } + else + { + if (order.OrderTax == 0 && taxSettings.HideZeroTax) + { + displayTax = false; + displayTaxRates = false; + } + else + { + taxRates = new SortedDictionary(); + foreach (var tr in order.TaxRatesDictionary) + { + taxRates.Add(tr.Key, currencyService.ConvertCurrency(tr.Value, order.CurrencyRate)); + } + + displayTaxRates = taxSettings.DisplayTaxRates && taxRates.Count > 0; + displayTax = !displayTaxRates; + + cusTaxTotal = FormatPrice(order.OrderTax, order, messageContext); + } + } + + // Discount + bool dislayDiscount = false; + if (order.OrderDiscount > decimal.Zero) + { + cusDiscount = FormatPrice(-order.OrderDiscount, order, messageContext); + dislayDiscount = true; + } - return m; - } + // Total + var roundingAmount = decimal.Zero; + var orderTotal = order.GetOrderTotalInCustomerCurrency(currencyService, paymentService, out roundingAmount); + cusTotal = FormatPrice(orderTotal, order.CustomerCurrencyCode, messageContext); + + // Rounding + if (roundingAmount != decimal.Zero) + { + cusRounding = FormatPrice(roundingAmount, order.CustomerCurrencyCode, messageContext); + } + + // Model + dynamic m = new ExpandoObject(); + + m.SubTotal = subTotals.SubTotal; + m.SubTotalDiscount = subTotals.DisplaySubTotalDiscount ? subTotals.SubTotalDiscount : null; + m.Shipping = dislayShipping ? subTotals.ShippingTotal : null; + m.Payment = displayPaymentMethodFee ? subTotals.PaymentFee : null; + m.Tax = displayTax ? cusTaxTotal : null; + m.Discount = dislayDiscount ? cusDiscount : null; + m.RoundingDiff = cusRounding; + m.Total = cusTotal; + m.IsGross = order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax; + + // TaxRates + m.TaxRates = !displayTaxRates ? (object[])null : taxRates.Select(x => + { + return new + { + Rate = T("Order.TaxRateLine", language.Id, priceFormatter.FormatTaxRate(x.Key)).Text, + Value = FormatPrice(x.Value, order, messageContext) + }; + }).ToArray(); + + + // Gift Cards + m.GiftCardUsage = order.GiftCardUsageHistory.Count == 0 ? (object[])null : order.GiftCardUsageHistory.Select(x => + { + return new + { + GiftCard = T("Order.GiftCardInfo", language.Id, x.GiftCard.GiftCardCouponCode).Text, + UsedAmount = FormatPrice(-x.UsedValue, order, messageContext), + RemainingAmount = FormatPrice(x.GiftCard.GetGiftCardRemainingAmount(), order, messageContext) + }; + }).ToArray(); + + // Reward Points + m.RedeemedRewardPoints = order.RedeemedRewardPointsEntry == null ? null : new + { + Title = T("Order.RewardPoints", language.Id, -order.RedeemedRewardPointsEntry.Points).Text, + Amount = FormatPrice(-order.RedeemedRewardPointsEntry.UsedAmount, order, messageContext) + }; - private (Money SubTotal, Money SubTotalDiscount, Money ShippingTotal, Money PaymentFee, bool DisplaySubTotalDiscount) GetSubTotals(Order order, MessageContext messageContext) - { - var language = messageContext.Language; - var currencyService = _services.Resolve(); - var priceFormatter = _services.Resolve(); + return m; + } - var isNet = order.CustomerTaxDisplayType == TaxDisplayType.ExcludingTax; + private (Money SubTotal, Money SubTotalDiscount, Money ShippingTotal, Money PaymentFee, bool DisplaySubTotalDiscount) GetSubTotals(Order order, MessageContext messageContext) + { + var isNet = order.CustomerTaxDisplayType == TaxDisplayType.ExcludingTax; - var subTotal = isNet ? order.OrderSubtotalExclTax : order.OrderSubtotalInclTax; - var subTotalDiscount = isNet ? order.OrderSubTotalDiscountExclTax : order.OrderSubTotalDiscountInclTax; - var shipping = isNet ? order.OrderShippingExclTax : order.OrderShippingInclTax; - var payment = isNet ? order.PaymentMethodAdditionalFeeExclTax : order.PaymentMethodAdditionalFeeInclTax; + var subTotal = isNet ? order.OrderSubtotalExclTax : order.OrderSubtotalInclTax; + var subTotalDiscount = isNet ? order.OrderSubTotalDiscountExclTax : order.OrderSubTotalDiscountInclTax; + var shipping = isNet ? order.OrderShippingExclTax : order.OrderShippingInclTax; + var payment = isNet ? order.PaymentMethodAdditionalFeeExclTax : order.PaymentMethodAdditionalFeeInclTax; - // Subtotal - var cusSubTotal = FormatPrice(subTotal, order, messageContext); + // Subtotal + var cusSubTotal = FormatPrice(subTotal, order, messageContext); - // Shipping - var cusShipTotal = FormatPrice(shipping, order, messageContext); + // Shipping + var cusShipTotal = FormatPrice(shipping, order, messageContext); - // Payment method additional fee - var cusPaymentMethodFee = FormatPrice(payment, order, messageContext); + // Payment method additional fee + var cusPaymentMethodFee = FormatPrice(payment, order, messageContext); - // Discount (applied to order subtotal) - Money cusSubTotalDiscount = null; - bool dislaySubTotalDiscount = false; - if (subTotalDiscount > decimal.Zero) - { - cusSubTotalDiscount = FormatPrice(-subTotalDiscount, order, messageContext); - dislaySubTotalDiscount = true; - } + // Discount (applied to order subtotal) + Money cusSubTotalDiscount = null; + bool dislaySubTotalDiscount = false; + if (subTotalDiscount > decimal.Zero) + { + cusSubTotalDiscount = FormatPrice(-subTotalDiscount, order, messageContext); + dislaySubTotalDiscount = true; + } - return (cusSubTotal, cusSubTotalDiscount, cusShipTotal, cusPaymentMethodFee, dislaySubTotalDiscount); - } + return (cusSubTotal, cusSubTotalDiscount, cusShipTotal, cusPaymentMethodFee, dislaySubTotalDiscount); + } - protected virtual object CreateModelPart(OrderItem part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); + protected virtual object CreateModelPart(OrderItem part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); - var productAttributeParser = _services.Resolve(); - var downloadService = _services.Resolve(); + var productAttributeParser = _services.Resolve(); + var downloadService = _services.Resolve(); var deliveryTimeService = _services.Resolve(); var order = part.Order; - var isNet = order.CustomerTaxDisplayType == TaxDisplayType.ExcludingTax; - var product = part.Product; - product.MergeWithCombination(part.AttributesXml, productAttributeParser); - - // Bundle items. - object bundleItems = null; - if (product.ProductType == ProductType.BundledProduct && part.BundleData.HasValue()) - { - var bundleData = part.GetBundleData(); - if (bundleData.Any()) - { - var productService = _services.Resolve(); - var products = productService.GetProductsByIds(bundleData.Select(x => x.ProductId).ToArray()); - var productsDic = products.ToDictionarySafe(x => x.Id, x => x); - - bundleItems = bundleData - .OrderBy(x => x.DisplayOrder) - .Select(x => - { - productsDic.TryGetValue(x.ProductId, out Product bundleItemProduct); - return CreateModelPart(x, part, bundleItemProduct, messageContext); - }) - .ToList(); - } - } - - var m = new Dictionary - { - { "DownloadUrl", !downloadService.IsDownloadAllowed(part) ? null : BuildActionUrl("GetDownload", "Download", new { id = part.OrderItemGuid, area = "" }, messageContext) }, - { "AttributeDescription", part.AttributeDescription.NullEmpty() }, - { "Weight", part.ItemWeight }, - { "TaxRate", part.TaxRate }, - { "Qty", part.Quantity }, - { "UnitPrice", FormatPrice(isNet ? part.UnitPriceExclTax : part.UnitPriceInclTax, part.Order, messageContext) }, - { "LineTotal", FormatPrice(isNet ? part.PriceExclTax : part.PriceInclTax, part.Order, messageContext) }, - { "Product", CreateModelPart(product, messageContext, part.AttributesXml) }, - { "BundleItems", bundleItems }, - { "IsGross", !isNet }, + var isNet = order.CustomerTaxDisplayType == TaxDisplayType.ExcludingTax; + var product = part.Product; + product.MergeWithCombination(part.AttributesXml, productAttributeParser); + + // Bundle items. + object bundleItems = null; + if (product.ProductType == ProductType.BundledProduct && part.BundleData.HasValue()) + { + var bundleData = part.GetBundleData(); + if (bundleData.Any()) + { + var productService = _services.Resolve(); + var products = productService.GetProductsByIds(bundleData.Select(x => x.ProductId).ToArray()); + var productsDic = products.ToDictionarySafe(x => x.Id, x => x); + + bundleItems = bundleData + .OrderBy(x => x.DisplayOrder) + .Select(x => + { + productsDic.TryGetValue(x.ProductId, out Product bundleItemProduct); + return CreateModelPart(x, part, bundleItemProduct, messageContext); + }) + .ToList(); + } + } + + var m = new Dictionary + { + { "DownloadUrl", !downloadService.IsDownloadAllowed(part) ? null : BuildActionUrl("GetDownload", "Download", new { id = part.OrderItemGuid, area = "" }, messageContext) }, + { "AttributeDescription", part.AttributeDescription.NullEmpty() }, + { "Weight", part.ItemWeight }, + { "TaxRate", part.TaxRate }, + { "Qty", part.Quantity }, + { "UnitPrice", FormatPrice(isNet ? part.UnitPriceExclTax : part.UnitPriceInclTax, part.Order, messageContext) }, + { "LineTotal", FormatPrice(isNet ? part.PriceExclTax : part.PriceInclTax, part.Order, messageContext) }, + { "Product", CreateModelPart(product, messageContext, part.AttributesXml) }, + { "BundleItems", bundleItems }, + { "IsGross", !isNet }, { "DisplayDeliveryTime", part.DisplayDeliveryTime }, }; @@ -322,145 +318,171 @@ protected virtual object CreateModelPart(OrderItem part, MessageContext messageC } } - PublishModelPartCreatedEvent(part, m); + PublishModelPartCreatedEvent(part, m); - return m; - } + return m; + } - protected virtual object CreateModelPart(ProductBundleItemOrderData part, OrderItem orderItem, Product product, MessageContext messageContext) - { - Guard.NotNull(part, nameof(part)); - Guard.NotNull(orderItem, nameof(orderItem)); - Guard.NotNull(product, nameof(product)); - Guard.NotNull(messageContext, nameof(messageContext)); + protected virtual object CreateModelPart(ProductBundleItemOrderData part, OrderItem orderItem, Product product, MessageContext messageContext) + { + Guard.NotNull(part, nameof(part)); + Guard.NotNull(orderItem, nameof(orderItem)); + Guard.NotNull(product, nameof(product)); + Guard.NotNull(messageContext, nameof(messageContext)); - var priceWithDiscount = FormatPrice(part.PriceWithDiscount, orderItem.Order, messageContext); + var priceWithDiscount = FormatPrice(part.PriceWithDiscount, orderItem.Order, messageContext); - var m = new Dictionary - { - { "AttributeDescription", part.AttributesInfo.NullEmpty() }, - { "Quantity", part.Quantity > 1 && part.PerItemShoppingCart ? part.Quantity.ToString() : null }, - { "PerItemShoppingCart", part.PerItemShoppingCart }, - { "PriceWithDiscount", priceWithDiscount }, - { "Product", CreateModelPart(product, messageContext, part.AttributesXml) } - }; + var m = new Dictionary + { + { "AttributeDescription", part.AttributesInfo.NullEmpty() }, + { "Quantity", part.Quantity > 1 && part.PerItemShoppingCart ? part.Quantity.ToString() : null }, + { "PerItemShoppingCart", part.PerItemShoppingCart }, + { "PriceWithDiscount", priceWithDiscount }, + { "Product", CreateModelPart(product, messageContext, part.AttributesXml) } + }; - PublishModelPartCreatedEvent(part, m); + PublishModelPartCreatedEvent(part, m); - return m; - } + return m; + } - protected virtual object CreateModelPart(OrderNote part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); + protected virtual object CreateModelPart(OrderNote part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); - var m = new Dictionary - { - { "Id", part.Id }, - { "CreatedOn", ToUserDate(part.CreatedOnUtc, messageContext) }, - { "Text", part.FormatOrderNoteText().NullEmpty() } - }; + var m = new Dictionary + { + { "Id", part.Id }, + { "CreatedOn", ToUserDate(part.CreatedOnUtc, messageContext) }, + { "Text", part.FormatOrderNoteText().NullEmpty() } + }; - PublishModelPartCreatedEvent(part, m); + PublishModelPartCreatedEvent(part, m); - return m; - } + return m; + } - protected virtual object CreateModelPart(ShoppingCartItem part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); + protected virtual object CreateModelPart(ShoppingCartItem part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); - var m = new Dictionary - { - { "Id", part.Id }, - { "Quantity", part.Quantity }, - { "Product", CreateModelPart(part.Product, messageContext, part.AttributesXml) }, - }; + var m = new Dictionary + { + { "Id", part.Id }, + { "Quantity", part.Quantity }, + { "Product", CreateModelPart(part.Product, messageContext, part.AttributesXml) }, + }; - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(Shipment part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var orderItems = new List(); - var orderItemIds = part.ShipmentItems.Select(x => x.OrderItemId).ToArray(); - foreach (var orderItemId in orderItemIds) - { - var orderItem = _services.Resolve().GetOrderItemById(orderItemId); - if (orderItem != null) - { - orderItems.Add(orderItem); - } - } - - var m = new Dictionary - { - { "Id", part.Id }, - { "TrackingNumber", part.TrackingNumber.NullEmpty() }, - { "TotalWeight", part.TotalWeight }, - { "CreatedOn", ToUserDate(part.CreatedOnUtc, messageContext) }, - { "DeliveredOn", ToUserDate(part.DeliveryDateUtc, messageContext) }, - { "ShippedOn", ToUserDate(part.ShippedDateUtc, messageContext) }, - { "Url", BuildActionUrl("ShipmentDetails", "Order", new { id = part.Id, area = "" }, messageContext)}, - { "Items", orderItems.Where(x => x.Product != null).Select(x => CreateModelPart(x, messageContext)).ToList() }, - }; + PublishModelPartCreatedEvent(part, m); - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(RecurringPayment part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary - { - { "Id", part.Id }, - { "CreatedOn", ToUserDate(part.CreatedOnUtc, messageContext) }, - { "StartedOn", ToUserDate(part.StartDateUtc, messageContext) }, - { "NextOn", ToUserDate(part.NextPaymentDate, messageContext) }, - { "CycleLength", part.CycleLength }, - { "CyclePeriod", part.CyclePeriod.GetLocalizedEnum(_services.Localization, messageContext.Language.Id) }, - { "CyclesRemaining", part.CyclesRemaining }, - { "TotalCycles", part.TotalCycles }, - { "Url", BuildActionUrl("Edit", "RecurringPayment", new { id = part.Id, area = "admin" }, messageContext) } - }; + return m; + } - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(ReturnRequest part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary - { - { "Id", part.Id }, - { "Reason", part.ReasonForReturn.NullEmpty() }, - { "Status", part.ReturnRequestStatus.GetLocalizedEnum(_services.Localization, messageContext.Language.Id) }, - { "RequestedAction", part.RequestedAction.NullEmpty() }, - { "CustomerComments", HtmlUtils.StripTags(part.CustomerComments).NullEmpty() }, - { "StaffNotes", HtmlUtils.StripTags(part.StaffNotes).NullEmpty() }, - { "Quantity", part.Quantity }, - { "RefundToWallet", part.RefundToWallet }, - { "Url", BuildActionUrl("Edit", "ReturnRequest", new { id = part.Id, area = "admin" }, messageContext) } - }; + protected virtual object CreateModelPart(Shipment part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var itemParts = new List(); + var orderService = _services.Resolve(); + var orderItems = orderService.GetOrderItemsByOrderIds(new int[] { part.OrderId })[part.OrderId]; + var orderItemsDic = orderItems.ToDictionarySafe(x => x.Id); + + foreach (var shipmentItem in part.ShipmentItems) + { + if (orderItemsDic.TryGetValue(shipmentItem.OrderItemId, out var orderItem) && orderItem.Product != null) + { + var itemPart = CreateModelPart(orderItem, messageContext) as Dictionary; + itemPart["Qty"] = shipmentItem.Quantity; + + itemParts.Add(itemPart); + } + } + + var trackingUrl = part.TrackingUrl; + + if (trackingUrl.IsEmpty() && part.TrackingNumber.HasValue() && part.Order.ShippingRateComputationMethodSystemName.HasValue()) + { + // Try to get URL from tracker. + var srcm = _services.Resolve().LoadShippingRateComputationMethodBySystemName(part.Order.ShippingRateComputationMethodSystemName); + if (srcm != null && srcm.Value.IsActive) + { + var tracker = srcm.Value.ShipmentTracker; + if (tracker != null) + { + var shippingSettings = _services.Settings.LoadSetting(part.Order.StoreId); + if (srcm.IsShippingRateComputationMethodActive(shippingSettings)) + { + trackingUrl = tracker.GetUrl(part.TrackingNumber); + } + } + } + } + + var m = new Dictionary + { + { "Id", part.Id }, + { "TrackingNumber", part.TrackingNumber.NullEmpty() }, + { "TrackingUrl", trackingUrl.NullEmpty() }, + { "TotalWeight", part.TotalWeight }, + { "CreatedOn", ToUserDate(part.CreatedOnUtc, messageContext) }, + { "DeliveredOn", ToUserDate(part.DeliveryDateUtc, messageContext) }, + { "ShippedOn", ToUserDate(part.ShippedDateUtc, messageContext) }, + { "Url", BuildActionUrl("ShipmentDetails", "Order", new { id = part.Id, area = "" }, messageContext)}, + { "Items", itemParts }, + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(RecurringPayment part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary + { + { "Id", part.Id }, + { "CreatedOn", ToUserDate(part.CreatedOnUtc, messageContext) }, + { "StartedOn", ToUserDate(part.StartDateUtc, messageContext) }, + { "NextOn", ToUserDate(part.NextPaymentDate, messageContext) }, + { "CycleLength", part.CycleLength }, + { "CyclePeriod", part.CyclePeriod.GetLocalizedEnum(_services.Localization, messageContext.Language.Id) }, + { "CyclesRemaining", part.CyclesRemaining }, + { "TotalCycles", part.TotalCycles }, + { "Url", BuildActionUrl("Edit", "RecurringPayment", new { id = part.Id, area = "admin" }, messageContext) } + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(ReturnRequest part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary + { + { "Id", part.Id }, + { "Reason", part.ReasonForReturn.NullEmpty() }, + { "Status", part.ReturnRequestStatus.GetLocalizedEnum(_services.Localization, messageContext.Language.Id) }, + { "RequestedAction", part.RequestedAction.NullEmpty() }, + { "CustomerComments", HtmlUtils.StripTags(part.CustomerComments).NullEmpty() }, + { "StaffNotes", HtmlUtils.StripTags(part.StaffNotes).NullEmpty() }, + { "Quantity", part.Quantity }, + { "RefundToWallet", part.RefundToWallet }, + { "Url", BuildActionUrl("Edit", "ReturnRequest", new { id = part.Id, area = "admin" }, messageContext) } + }; - PublishModelPartCreatedEvent(part, m); + PublishModelPartCreatedEvent(part, m); - return m; - } - } + return m; + } + } } diff --git a/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.Utils.cs b/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.Utils.cs index 9e5a8856e2..26d3ef994d 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.Utils.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.Utils.cs @@ -1,188 +1,192 @@ using System; +using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; +using SmartStore.Core; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.Directory; +using SmartStore.Core.Domain.Orders; +using SmartStore.Core.Html; using SmartStore.Core.Plugins; using SmartStore.Services.Catalog; using SmartStore.Services.Common; using SmartStore.Services.Customers; -using SmartStore.Services.Topics; -using SmartStore.Services.Media; using SmartStore.Services.Directory; using SmartStore.Services.Localization; -using SmartStore.Core.Domain.Orders; -using SmartStore.Core.Html; +using SmartStore.Services.Media; +using SmartStore.Services.Topics; using SmartStore.Utilities; -using System.Collections.Generic; -using SmartStore.Core.Domain.Directory; -using SmartStore.Core; namespace SmartStore.Services.Messages { - public partial class MessageModelProvider - { - private void ApplyCustomerContentPart(IDictionary model, CustomerContent content, MessageContext ctx) - { - model["CustomerId"] = content.CustomerId; - model["IpAddress"] = content.IpAddress; - model["CreatedOn"] = ToUserDate(content.CreatedOnUtc, ctx); - model["UpdatedOn"] = ToUserDate(content.UpdatedOnUtc, ctx); - } - - private string BuildUrl(string url, MessageContext ctx) - { - return ctx.BaseUri.GetLeftPart(UriPartial.Authority) + url.EnsureStartsWith("/"); - } - - private string BuildRouteUrl(object routeValues, MessageContext ctx) - { - return ctx.BaseUri.GetLeftPart(UriPartial.Authority) + _urlHelper.RouteUrl(routeValues); - } - - private string BuildRouteUrl(string routeName, object routeValues, MessageContext ctx) - { - return ctx.BaseUri.GetLeftPart(UriPartial.Authority) + _urlHelper.RouteUrl(routeName, routeValues); - } - - private string BuildActionUrl(string action, string controller, object routeValues, MessageContext ctx) - { - return ctx.BaseUri.GetLeftPart(UriPartial.Authority) + _urlHelper.Action(action, controller, routeValues); - } - - private void PublishModelPartCreatedEvent(T source, dynamic part) where T : class - { - _services.EventPublisher.Publish(new MessageModelPartCreatedEvent(source, part)); - } - - private string GetLocalizedValue(MessageContext messageContext, ProviderMetadata metadata, string propertyName, Expression> fallback) - { - // TODO: (mc) this actually belongs to PluginMediator, but we simply cannot add a dependency to framework from here. Refactor later! - - Guard.NotNull(metadata, nameof(metadata)); - - string systemName = metadata.SystemName; - var resourceName = metadata.ResourceKeyPattern.FormatInvariant(metadata.SystemName, propertyName); - string result = _services.Localization.GetResource(resourceName, messageContext.Language.Id, false, "", true); - - if (result.IsEmpty()) - result = fallback.Compile()(metadata); - - return result; - } - - private object GetTopic(string topicSystemName, MessageContext ctx) - { - var topicService = _services.Resolve(); - - // Load by store - var topic = topicService.GetTopicBySystemName(topicSystemName, ctx.StoreId ?? 0, false); - - string body = topic?.GetLocalized(x => x.Body, ctx.Language); - if (body.HasValue()) - { - body = HtmlUtils.RelativizeFontSizes(body); - } - - return new - { - Title = topic?.GetLocalized(x => x.Title, ctx.Language).Value.NullEmpty(), - Body = body.NullEmpty() - }; - } - - private string GetDisplayNameForCustomer(Customer customer) - { - return customer.GetFullName().NullEmpty() ?? customer.Username ?? customer.FindEmail(); - } - - private string GetBoolResource(bool value, MessageContext ctx) - { - return _services.Localization.GetResource(value ? "Common.Yes" : "Common.No", ctx.Language.Id); - } - - private DateTime? ToUserDate(DateTime? utcDate, MessageContext messageContext) - { - if (utcDate == null) - return null; - - return _services.DateTimeHelper.ConvertToUserTime( - utcDate.Value, - TimeZoneInfo.Utc, - _services.DateTimeHelper.GetCustomerTimeZone(messageContext.Customer)); - } - - private Money FormatPrice(decimal price, Order order, MessageContext messageContext) - { - return FormatPrice(price, order.CustomerCurrencyCode, messageContext, order.CurrencyRate); - } - - private Money FormatPrice(decimal price, MessageContext messageContext, decimal exchangeRate = 1) - { - return FormatPrice(price, (Currency)null, messageContext, exchangeRate); - } - - private Money FormatPrice(decimal price, string currencyCode, MessageContext messageContext, decimal exchangeRate = 1) - { - return FormatPrice( - price, - _services.Resolve().GetCurrencyByCode(currencyCode) ?? new Currency { CurrencyCode = currencyCode }, - messageContext, - exchangeRate); - } - - private Money FormatPrice(decimal price, Currency currency, MessageContext messageContext, decimal exchangeRate = 1) - { - if (exchangeRate != 1) - { - price = _services.Resolve().ConvertCurrency(price, exchangeRate); - } - - if (currency == null) - { - currency = _services.Resolve().WorkingCurrency; - } - - return new Money(price, currency); - } - - private PictureInfo GetPictureFor(Product product, string attributesXml) - { - var pictureService = _services.PictureService; - var attrParser = _services.Resolve(); - - PictureInfo pictureInfo = null; - - if (attributesXml.HasValue()) - { - var combination = attrParser.FindProductVariantAttributeCombination(product.Id, attributesXml); - - if (combination != null) - { - var picturesIds = combination.GetAssignedPictureIds(); - if (picturesIds != null && picturesIds.Length > 0) - { - pictureInfo = pictureService.GetPictureInfo(picturesIds[0]); - } - } - } - - if (pictureInfo == null) - { - pictureInfo = pictureService.GetPictureInfo(product.MainPictureId); - } - - if (pictureInfo == null && !product.VisibleIndividually && product.ParentGroupedProductId > 0) - { - pictureInfo = pictureService.GetPictureInfo(pictureService.GetPicturesByProductId(product.ParentGroupedProductId, 1).FirstOrDefault()); - } - - return pictureInfo; - } - - private object[] Concat(params object[] values) - { - return values.Where(x => CommonHelper.IsTruthy(x)).ToArray(); - } - } + public partial class MessageModelProvider + { + private void ApplyCustomerContentPart(IDictionary model, CustomerContent content, MessageContext ctx) + { + model["CustomerId"] = content.CustomerId; + model["IpAddress"] = content.IpAddress; + model["CreatedOn"] = ToUserDate(content.CreatedOnUtc, ctx); + model["UpdatedOn"] = ToUserDate(content.UpdatedOnUtc, ctx); + } + + private string BuildUrl(string url, MessageContext ctx) + { + return ctx.BaseUri.GetLeftPart(UriPartial.Authority) + url.EnsureStartsWith("/"); + } + + private string BuildRouteUrl(object routeValues, MessageContext ctx) + { + return ctx.BaseUri.GetLeftPart(UriPartial.Authority) + _urlHelper.RouteUrl(routeValues); + } + + private string BuildRouteUrl(string routeName, object routeValues, MessageContext ctx) + { + return ctx.BaseUri.GetLeftPart(UriPartial.Authority) + _urlHelper.RouteUrl(routeName, routeValues); + } + + private string BuildActionUrl(string action, string controller, object routeValues, MessageContext ctx) + { + return ctx.BaseUri.GetLeftPart(UriPartial.Authority) + _urlHelper.Action(action, controller, routeValues); + } + + private void PublishModelPartCreatedEvent(T source, dynamic part) where T : class + { + _services.EventPublisher.Publish(new MessageModelPartCreatedEvent(source, part)); + } + + private string GetLocalizedValue(MessageContext messageContext, ProviderMetadata metadata, string propertyName, Func fallback) + { + // TODO: (mc) this actually belongs to PluginMediator, but we simply cannot add a dependency to framework from here. Refactor later! + + Guard.NotNull(metadata, nameof(metadata)); + + string systemName = metadata.SystemName; + var resourceName = metadata.ResourceKeyPattern.FormatInvariant(metadata.SystemName, propertyName); + string result = _services.Localization.GetResource(resourceName, messageContext.Language.Id, false, "", true); + + if (result.IsEmpty()) + result = fallback(metadata); + + return result; + } + + private object GetTopic(string topicSystemName, MessageContext ctx) + { + var topicService = _services.Resolve(); + + // Load by store + var topic = topicService.GetTopicBySystemName(topicSystemName, ctx.StoreId ?? 0, false); + + string body = topic?.GetLocalized(x => x.Body, ctx.Language); + if (body.HasValue()) + { + body = HtmlUtils.RelativizeFontSizes(body); + } + + return new + { + Title = topic?.GetLocalized(x => x.Title, ctx.Language).Value.NullEmpty(), + Body = body.NullEmpty() + }; + } + + private string GetDisplayNameForCustomer(Customer customer) + { + return customer.GetFullName().NullEmpty() ?? customer.Username ?? customer.FindEmail(); + } + + private string GetBoolResource(bool value, MessageContext ctx) + { + return _services.Localization.GetResource(value ? "Common.Yes" : "Common.No", ctx.Language.Id); + } + + private DateTime? ToUserDate(DateTime? utcDate, MessageContext messageContext) + { + if (utcDate == null) + return null; + + return _services.DateTimeHelper.ConvertToUserTime( + utcDate.Value, + TimeZoneInfo.Utc, + _services.DateTimeHelper.GetCustomerTimeZone(messageContext.Customer)); + } + + private Money FormatPrice(decimal price, Order order, MessageContext messageContext) + { + return FormatPrice(price, order.CustomerCurrencyCode, messageContext, order.CurrencyRate); + } + + private Money FormatPrice(decimal price, MessageContext messageContext, decimal exchangeRate = 1) + { + return FormatPrice(price, (Currency)null, messageContext, exchangeRate); + } + + private Money FormatPrice(decimal price, string currencyCode, MessageContext messageContext, decimal exchangeRate = 1) + { + return FormatPrice( + price, + _services.Resolve().GetCurrencyByCode(currencyCode) ?? new Currency { CurrencyCode = currencyCode }, + messageContext, + exchangeRate); + } + + private Money FormatPrice(decimal price, Currency currency, MessageContext messageContext, decimal exchangeRate = 1) + { + if (exchangeRate != 1) + { + price = _services.Resolve().ConvertCurrency(price, exchangeRate); + } + + if (currency == null) + { + currency = _services.Resolve().WorkingCurrency; + } + + return new Money(price, currency); + } + + + private MediaFileInfo GetMediaFileFor(Product product, string attributesXml) + { + var attrParser = _services.Resolve(); + var productService = _services.Resolve(); + + MediaFileInfo file = null; + + if (attributesXml.HasValue()) + { + var combination = attrParser.FindProductVariantAttributeCombination(product.Id, attributesXml); + + if (combination != null) + { + var fileIds = combination.GetAssignedMediaIds(); + if (fileIds?.Any() ?? false) + { + file = _services.MediaService.GetFileById(fileIds[0], MediaLoadFlags.AsNoTracking); + } + } + } + + if (file == null) + { + file = _services.MediaService.GetFileById(product.MainPictureId ?? 0, MediaLoadFlags.AsNoTracking); + } + + if (file == null && product.Visibility == ProductVisibility.Hidden && product.ParentGroupedProductId > 0) + { + var productFile = productService.GetProductPicturesByProductId(product.ParentGroupedProductId, 1).FirstOrDefault(); + if (productFile != null) + { + file = _services.MediaService.ConvertMediaFile(productFile.MediaFile); + } + } + + return file; + } + + private object[] Concat(params object[] values) + { + return values.Where(x => CommonHelper.IsTruthy(x)).ToArray(); + } + } } diff --git a/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs b/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs index 15bc75cec2..37045b041f 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageModelProvider.cs @@ -26,7 +26,6 @@ using SmartStore.Core.Html; using SmartStore.Core.Localization; using SmartStore.Core.Logging; -using SmartStore.Services.Catalog; using SmartStore.Services.Catalog.Extensions; using SmartStore.Services.Common; using SmartStore.Services.Customers; @@ -41,855 +40,865 @@ namespace SmartStore.Services.Messages { - public enum ModelTreeMemberKind - { - Primitive, - Complex, - Collection, - Root - } - - public class ModelTreeMember - { - public string Name { get; set; } - public ModelTreeMemberKind Kind { get; set; } - } - - public partial class MessageModelProvider : IMessageModelProvider - { - private readonly ICommonServices _services; - private readonly ITemplateEngine _templateEngine; - private readonly IMessageTemplateService _messageTemplateService; - private readonly IEmailAccountService _emailAccountService; - private readonly UrlHelper _urlHelper; - - public MessageModelProvider( - ICommonServices services, - ITemplateEngine templateEngine, - IMessageTemplateService messageTemplateService, - IEmailAccountService emailAccountService, - UrlHelper urlHelper) - { - _services = services; - _templateEngine = templateEngine; - _messageTemplateService = messageTemplateService; - _emailAccountService = emailAccountService; - _urlHelper = urlHelper; - - T = NullLocalizer.InstanceEx; - Logger = NullLogger.Instance; - } - - public LocalizerEx T { get; set; } - public ILogger Logger { get; set; } - - public virtual void AddGlobalModelParts(MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - - var model = messageContext.Model; - - model["Context"] = new Dictionary - { - { "TemplateName", messageContext.MessageTemplate.Name }, - { "LanguageId", messageContext.Language.Id }, - { "LanguageCulture", messageContext.Language.LanguageCulture }, - { "LanguageRtl", messageContext.Language.Rtl }, - { "BaseUrl", messageContext.BaseUri.ToString() } - }; - - dynamic email = new ExpandoObject(); - email.Email = messageContext.EmailAccount.Email; - email.SenderName = messageContext.EmailAccount.DisplayName; - email.DisplayName = messageContext.EmailAccount.DisplayName; // Alias - model["Email"] = email; - - model["Theme"] = CreateThemeModelPart(messageContext); - model["Customer"] = CreateModelPart(messageContext.Customer, messageContext); - model["Store"] = CreateModelPart(messageContext.Store, messageContext); - } - - public object CreateModelPart(object part, bool ignoreNullMembers, params string[] ignoreMemberNames) - { - Guard.NotNull(part, nameof(part)); - - var store = _services.StoreContext.CurrentStore; - var messageContext = new MessageContext - { - Language = _services.WorkContext.WorkingLanguage, - Store = store, - BaseUri = new Uri(_services.StoreService.GetHost(store)), - Model = new TemplateModel() - }; - - if (part is Customer x) - { - // This case is not handled in AddModelPart core method. - messageContext.Customer = x; - messageContext.Model["Part"] = CreateModelPart(x, messageContext); - } - else - { - messageContext.Customer = _services.WorkContext.CurrentCustomer; - AddModelPart(part, messageContext, "Part"); - } - - object result = null; - - if (messageContext.Model.Any()) - { - result = messageContext.Model.FirstOrDefault().Value; - - if (result is IDictionary dict) - { - SanitizeModelDictionary(dict, ignoreNullMembers, ignoreMemberNames); - } - } - - return result; - } - - private void SanitizeModelDictionary(IDictionary dict, bool ignoreNullMembers, params string[] ignoreMemberNames) - { - if (ignoreNullMembers || ignoreMemberNames.Length > 0) - { - foreach (var key in dict.Keys.ToArray()) - { - var expando = dict as HybridExpando; - var value = dict[key]; - - if ((ignoreNullMembers && value == null) || ignoreMemberNames.Contains(key)) - { - if (expando != null) - expando.Override(key, null); // INFO: we cannot remove entries from HybridExpando - else - dict.Remove(key); - continue; - } - - if (value != null && value.GetType().IsSequenceType()) - { - var ignoreMemberNames2 = ignoreMemberNames - .Where(x => x.StartsWith(key + ".", StringComparison.OrdinalIgnoreCase)) - .Select(x => x.Substring(key.Length + 1)) - .ToArray(); - - if (value is IDictionary dict2) - { - SanitizeModelDictionary(dict2, ignoreNullMembers, ignoreMemberNames2); - } - else - { - var list = ((IEnumerable)value).OfType>(); - foreach (var dict3 in list) - { - SanitizeModelDictionary(dict3, ignoreNullMembers, ignoreMemberNames2); - } - } - } - } - } - } - - public virtual void AddModelPart(object part, MessageContext messageContext, string name = null) - { - Guard.NotNull(part, nameof(part)); - Guard.NotNull(messageContext, nameof(messageContext)); - - var model = messageContext.Model; - - name = name.NullEmpty() ?? ResolveModelName(part); - - object modelPart = null; - - switch (part) - { - case INamedModelPart x: - modelPart = x; - break; - case IModelPart x: - MergeModelBag(x, model, messageContext); - break; - case Order x: - modelPart = CreateModelPart(x, messageContext); - break; - case Product x: - modelPart = CreateModelPart(x, messageContext); - break; - case Customer x: - modelPart = CreateModelPart(x, messageContext); - break; - case Address x: - modelPart = CreateModelPart(x, messageContext); - break; - case Shipment x: - modelPart = CreateModelPart(x, messageContext); - break; - case OrderNote x: - modelPart = CreateModelPart(x, messageContext); - break; - case RecurringPayment x: - modelPart = CreateModelPart(x, messageContext); - break; - case ReturnRequest x: - modelPart = CreateModelPart(x, messageContext); - break; - case GiftCard x: - modelPart = CreateModelPart(x, messageContext); - break; - case NewsLetterSubscription x: - modelPart = CreateModelPart(x, messageContext); - break; - case Campaign x: - modelPart = CreateModelPart(x, messageContext); - break; - case ProductReview x: - modelPart = CreateModelPart(x, messageContext); - break; - case BlogComment x: - modelPart = CreateModelPart(x, messageContext); - break; - case NewsComment x: - modelPart = CreateModelPart(x, messageContext); - break; - case ForumTopic x: - modelPart = CreateModelPart(x, messageContext); - break; - case ForumPost x: - modelPart = CreateModelPart(x, messageContext); - break; + public enum ModelTreeMemberKind + { + Primitive, + Complex, + Collection, + Root + } + + public class ModelTreeMember + { + public string Name { get; set; } + public ModelTreeMemberKind Kind { get; set; } + } + + public partial class MessageModelProvider : IMessageModelProvider + { + private readonly ICommonServices _services; + private readonly ITemplateEngine _templateEngine; + private readonly IMessageTemplateService _messageTemplateService; + private readonly IEmailAccountService _emailAccountService; + private readonly UrlHelper _urlHelper; + + public MessageModelProvider( + ICommonServices services, + ITemplateEngine templateEngine, + IMessageTemplateService messageTemplateService, + IEmailAccountService emailAccountService, + UrlHelper urlHelper) + { + _services = services; + _templateEngine = templateEngine; + _messageTemplateService = messageTemplateService; + _emailAccountService = emailAccountService; + _urlHelper = urlHelper; + + T = NullLocalizer.InstanceEx; + Logger = NullLogger.Instance; + } + + public LocalizerEx T { get; set; } + public ILogger Logger { get; set; } + + public virtual void AddGlobalModelParts(MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + + var model = messageContext.Model; + + model["Context"] = new Dictionary + { + { "TemplateName", messageContext.MessageTemplate.Name }, + { "LanguageId", messageContext.Language.Id }, + { "LanguageCulture", messageContext.Language.LanguageCulture }, + { "LanguageRtl", messageContext.Language.Rtl }, + { "BaseUrl", messageContext.BaseUri.ToString() } + }; + + dynamic email = new ExpandoObject(); + email.Email = messageContext.EmailAccount.Email; + email.SenderName = messageContext.EmailAccount.DisplayName; + email.DisplayName = messageContext.EmailAccount.DisplayName; // Alias + model["Email"] = email; + + model["Theme"] = CreateThemeModelPart(messageContext); + model["Customer"] = CreateModelPart(messageContext.Customer, messageContext); + model["Store"] = CreateModelPart(messageContext.Store, messageContext); + } + + public object CreateModelPart(object part, bool ignoreNullMembers, params string[] ignoreMemberNames) + { + Guard.NotNull(part, nameof(part)); + + var store = _services.StoreContext.CurrentStore; + var messageContext = new MessageContext + { + Language = _services.WorkContext.WorkingLanguage, + Store = store, + BaseUri = new Uri(_services.StoreService.GetHost(store)), + Model = new TemplateModel() + }; + + if (part is Customer x) + { + // This case is not handled in AddModelPart core method. + messageContext.Customer = x; + messageContext.Model["Part"] = CreateModelPart(x, messageContext); + } + else + { + messageContext.Customer = _services.WorkContext.CurrentCustomer; + AddModelPart(part, messageContext, "Part"); + } + + object result = null; + + if (messageContext.Model.Any()) + { + result = messageContext.Model.FirstOrDefault().Value; + + if (result is IDictionary dict) + { + SanitizeModelDictionary(dict, ignoreNullMembers, ignoreMemberNames); + } + } + + return result; + } + + private void SanitizeModelDictionary(IDictionary dict, bool ignoreNullMembers, params string[] ignoreMemberNames) + { + if (ignoreNullMembers || ignoreMemberNames.Length > 0) + { + foreach (var key in dict.Keys.ToArray()) + { + var expando = dict as HybridExpando; + var value = dict[key]; + + if ((ignoreNullMembers && value == null) || ignoreMemberNames.Contains(key)) + { + if (expando != null) + expando.Override(key, null); // INFO: we cannot remove entries from HybridExpando + else + dict.Remove(key); + continue; + } + + if (value != null && value.GetType().IsSequenceType()) + { + var ignoreMemberNames2 = ignoreMemberNames + .Where(x => x.StartsWith(key + ".", StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Substring(key.Length + 1)) + .ToArray(); + + if (value is IDictionary dict2) + { + SanitizeModelDictionary(dict2, ignoreNullMembers, ignoreMemberNames2); + } + else + { + var list = ((IEnumerable)value).OfType>(); + foreach (var dict3 in list) + { + SanitizeModelDictionary(dict3, ignoreNullMembers, ignoreMemberNames2); + } + } + } + } + } + } + + public virtual void AddModelPart(object part, MessageContext messageContext, string name = null) + { + Guard.NotNull(part, nameof(part)); + Guard.NotNull(messageContext, nameof(messageContext)); + + var model = messageContext.Model; + + name = name.NullEmpty() ?? ResolveModelName(part); + + object modelPart = null; + + switch (part) + { + case INamedModelPart x: + modelPart = x; + break; + case IModelPart x: + MergeModelBag(x, model, messageContext); + break; + case Order x: + modelPart = CreateModelPart(x, messageContext); + break; + case Product x: + modelPart = CreateModelPart(x, messageContext); + break; + case Customer x: + modelPart = CreateModelPart(x, messageContext); + break; + case Address x: + modelPart = CreateModelPart(x, messageContext); + break; + case Shipment x: + modelPart = CreateModelPart(x, messageContext); + break; + case OrderNote x: + modelPart = CreateModelPart(x, messageContext); + break; + case RecurringPayment x: + modelPart = CreateModelPart(x, messageContext); + break; + case ReturnRequest x: + modelPart = CreateModelPart(x, messageContext); + break; + case GiftCard x: + modelPart = CreateModelPart(x, messageContext); + break; + case NewsLetterSubscription x: + modelPart = CreateModelPart(x, messageContext); + break; + case Campaign x: + modelPart = CreateModelPart(x, messageContext); + break; + case ProductReview x: + modelPart = CreateModelPart(x, messageContext); + break; + case BlogComment x: + modelPart = CreateModelPart(x, messageContext); + break; + case NewsComment x: + modelPart = CreateModelPart(x, messageContext); + break; + case ForumTopic x: + modelPart = CreateModelPart(x, messageContext); + break; + case ForumPost x: + modelPart = CreateModelPart(x, messageContext); + break; case ForumPostVote x: modelPart = CreateModelPart(x, messageContext); break; - case Forum x: - modelPart = CreateModelPart(x, messageContext); - break; - case PrivateMessage x: - modelPart = CreateModelPart(x, messageContext); - break; - case IEnumerable x: - modelPart = CreateModelPart(x, messageContext); - break; - case PollVotingRecord x: - modelPart = CreateModelPart(x, messageContext); - break; - case ProductReviewHelpfulness x: - modelPart = CreateModelPart(x, messageContext); - break; - case ForumSubscription x: - modelPart = CreateModelPart(x, messageContext); - break; - case BackInStockSubscription x: - modelPart = CreateModelPart(x, messageContext); - break; - default: - var partType = part.GetType(); - modelPart = part; - - if (partType.IsPlainObjectType() && !partType.IsAnonymous()) - { - var evt = new MessageModelPartMappingEvent(part); - _services.EventPublisher.Publish(evt); - - if (evt.Result != null && !object.ReferenceEquals(evt.Result, part)) - { - modelPart = evt.Result; - name = evt.ModelPartName.NullEmpty() ?? ResolveModelName(evt.Result) ?? name; - } - else - { - modelPart = part; - } - - modelPart = evt.Result ?? part; - name = evt.ModelPartName.NullEmpty() ?? name; - } - - break; - } - - if (modelPart != null) - { - if (name.IsEmpty()) - { - throw new SmartException($"Could not resolve a model key for part '{modelPart.GetType().Name}'. Use an instance of 'NamedModelPart' class to pass model with name."); - } - - if (model.TryGetValue(name, out var existing)) - { - // A model part with the same name exists in model already... - if (existing is IDictionary x) - { - // but it's a dictionary which we can easily merge with - x.Merge(FastProperty.ObjectToDictionary(modelPart), true); - } - else - { - // Wrap in HybridExpando and merge - var he = new HybridExpando(existing, true); - he.Merge(FastProperty.ObjectToDictionary(modelPart), true); - model[name] = he; - } - } - else - { - // Put part to model as new property - model[name] = modelPart; - } - } - } - - public string ResolveModelName(object model) - { - Guard.NotNull(model, nameof(model)); - - string name = null; - var type = model.GetType(); - - try - { - if (model is BaseEntity be) - { - name = be.GetUnproxiedType().Name; - } - else if (model is ITestModel te) - { - name = te.ModelName; - } - else if (model is INamedModelPart mp) - { - name = mp.ModelPartName; - } - else if (type.IsPlainObjectType()) - { - name = type.Name; - } - } - catch { } - - return name; - } - - #region Global model part handlers - - protected virtual object CreateThemeModelPart(MessageContext messageContext) - { - var m = new Dictionary - { - { "FontFamily", "-apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif" }, - { "BodyBg", "#f2f4f6" }, - { "BodyColor", "#555" }, - { "TitleColor", "#2f3133" }, - { "ContentBg", "#fff" }, - { "ShadeColor", "#e2e2e2" }, - { "LinkColor", "#0066c0" }, - { "BrandPrimary", "#3f51b5" }, - { "BrandSuccess", "#4caf50" }, - { "BrandWarning", "#ff9800" }, - { "BrandDanger", "#f44336" }, - { "MutedColor", "#a5a5a5" }, - }; - - return m; - } - - protected virtual object CreateCompanyModelPart(MessageContext messageContext) - { - var settings = _services.Settings.LoadSetting(messageContext.Store.Id); - dynamic m = new HybridExpando(settings, true); - - m.NameLine = Concat(settings.Salutation, settings.Title, settings.Firstname, settings.Lastname); - m.StreetLine = Concat(settings.Street, settings.Street2); - m.CityLine = Concat(settings.ZipCode, settings.City); - m.CountryLine = Concat(settings.CountryName, settings.Region); - - PublishModelPartCreatedEvent(settings, m); - return m; - } - - protected virtual object CreateBankModelPart(MessageContext messageContext) - { - var settings = _services.Settings.LoadSetting(messageContext.Store.Id); - var m = new HybridExpando(settings, true); - PublishModelPartCreatedEvent(settings, m); - return m; - } - - protected virtual object CreateContactModelPart(MessageContext messageContext) - { - var settings = _services.Settings.LoadSetting(messageContext.Store.Id); - var contact = new HybridExpando(settings, true) as dynamic; - - // Aliases - contact.Phone = new - { - Company = settings.CompanyTelephoneNumber.NullEmpty(), - Hotline = settings.HotlineTelephoneNumber.NullEmpty(), - Mobile = settings.MobileTelephoneNumber.NullEmpty(), - Fax = settings.CompanyFaxNumber.NullEmpty() - }; - - contact.Email = new - { - Company = settings.CompanyEmailAddress.NullEmpty(), - Webmaster = settings.WebmasterEmailAddress.NullEmpty(), - Support = settings.SupportEmailAddress.NullEmpty(), - Contact = settings.ContactEmailAddress.NullEmpty() - }; - - PublishModelPartCreatedEvent(settings, contact); - - return contact; - } - - #endregion - - #region Generic model part handlers - - protected virtual void MergeModelBag(IModelPart part, IDictionary model, MessageContext messageContext) - { - if (!(model.Get("Bag") is IDictionary bag)) - { - model["Bag"] = bag = new Dictionary(); - } - - var source = part as IDictionary; - bag.Merge(source); - } - - #endregion - - #region Entity specific model part handlers - - protected virtual object CreateModelPart(Store part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var host = messageContext.BaseUri.ToString(); - var logoInfo = _services.PictureService.GetPictureInfo(messageContext.Store.LogoPictureId); - - // Issue: https://github.com/smartstore/SmartStoreNET/issues/1321 - - var m = new Dictionary - { - { "Email", messageContext.EmailAccount.Email }, - { "EmailName", messageContext.EmailAccount.DisplayName }, - { "Name", part.Name }, - { "Url", host }, - { "Cdn", part.ContentDeliveryNetwork }, - { "PrimaryStoreCurrency", part.PrimaryStoreCurrency?.CurrencyCode }, - { "PrimaryExchangeRateCurrency", part.PrimaryExchangeRateCurrency?.CurrencyCode }, - { "Logo", CreateModelPart(logoInfo, messageContext, host, null, new Size(400, 75)) }, - { "Company", CreateCompanyModelPart(messageContext) }, - { "Contact", CreateContactModelPart(messageContext) }, - { "Bank", CreateBankModelPart(messageContext) }, - { "Copyright", T("Content.CopyrightNotice", messageContext.Language.Id, DateTime.Now.Year.ToString(), part.Name).Text } - }; - - var he = new HybridExpando(true); - he.Merge(m, true); - - PublishModelPartCreatedEvent(part, he); - - return he; - } - - protected virtual object CreateModelPart(PictureInfo part, MessageContext messageContext, - string href, - int? targetSize = null, - Size? clientMaxSize = null, - string alt = null) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotEmpty(href, nameof(href)); - - if (part == null) - return null; - - var width = part.Width; - var height = part.Height; - - if (width.HasValue && height.HasValue && (targetSize.HasValue || clientMaxSize.HasValue)) - { - var maxSize = clientMaxSize ?? new Size(targetSize.Value, targetSize.Value); - var size = ImagingHelper.Rescale(new Size(width.Value, height.Value), maxSize); - width = size.Width; - height = size.Height; - } - - var m = new - { - Src = _services.PictureService.GetUrl(part, targetSize.GetValueOrDefault(), FallbackPictureType.NoFallback, messageContext.BaseUri.ToString()), - Href = href, - Width = width, - Height = height, - Alt = alt - }; - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(Product part, MessageContext messageContext, string attributesXml = null) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var mediaSettings = _services.Resolve(); - var shoppingCartSettings = _services.Resolve(); - var catalogSettings = _services.Resolve(); - var deliveryTimeService = _services.Resolve(); - var quantityUnitService = _services.Resolve(); - var productUrlHelper = _services.Resolve(); - - var currency = _services.WorkContext.WorkingCurrency; - var additionalShippingCharge = new Money( - _services.Resolve().ConvertFromPrimaryStoreCurrency(part.AdditionalShippingCharge, currency), - currency, - true); - var url = BuildUrl(productUrlHelper.GetProductUrl(part.Id, part.GetSeName(messageContext.Language.Id), attributesXml), messageContext); - var pictureInfo = GetPictureFor(part, attributesXml); - var name = part.GetLocalized(x => x.Name, messageContext.Language.Id).Value; - var alt = T("Media.Product.ImageAlternateTextFormat", messageContext.Language.Id, name).Text; - - var m = new Dictionary - { - { "Id", part.Id }, - { "Sku", catalogSettings.ShowProductSku ? part.Sku : null }, - { "Name", name }, - { "Description", part.GetLocalized(x => x.ShortDescription, messageContext.Language).Value.NullEmpty() }, - { "StockQuantity", part.StockQuantity }, - { "AdditionalShippingCharge", additionalShippingCharge }, - { "Url", url }, - { "Thumbnail", CreateModelPart(pictureInfo, messageContext, url, mediaSettings.MessageProductThumbPictureSize, new Size(50, 50), alt) }, - { "ThumbnailLg", CreateModelPart(pictureInfo, messageContext, url, mediaSettings.ProductThumbPictureSize, new Size(120, 120), alt) }, - { "DeliveryTime", null }, - { "QtyUnit", null } - }; - - if (shoppingCartSettings.ShowDeliveryTimes && part.IsShipEnabled) - { - if (deliveryTimeService.GetDeliveryTimeById(part.DeliveryTimeId ?? 0) is DeliveryTime dt) - { - m["DeliveryTime"] = new Dictionary - { - { "Color", dt.ColorHexValue }, - { "Name", dt.GetLocalized(x => x.Name, messageContext.Language).Value }, - }; - } - } - - if (quantityUnitService.GetQuantityUnitById(part.QuantityUnitId) is QuantityUnit qu) - { - m["QtyUnit"] = qu.GetLocalized(x => x.Name, messageContext.Language).Value; - } - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(Customer part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var email = part.FindEmail(); - var pwdRecoveryToken = part.GetAttribute(SystemCustomerAttributeNames.PasswordRecoveryToken).NullEmpty(); - var accountActivationToken = part.GetAttribute(SystemCustomerAttributeNames.AccountActivationToken).NullEmpty(); + case Forum x: + modelPart = CreateModelPart(x, messageContext); + break; + case PrivateMessage x: + modelPart = CreateModelPart(x, messageContext); + break; + case IEnumerable x: + modelPart = CreateModelPart(x, messageContext); + break; + case PollVotingRecord x: + modelPart = CreateModelPart(x, messageContext); + break; + case ProductReviewHelpfulness x: + modelPart = CreateModelPart(x, messageContext); + break; + case ForumSubscription x: + modelPart = CreateModelPart(x, messageContext); + break; + case BackInStockSubscription x: + modelPart = CreateModelPart(x, messageContext); + break; + default: + var partType = part.GetType(); + modelPart = part; + + if (partType.IsPlainObjectType() && !partType.IsAnonymous()) + { + var evt = new MessageModelPartMappingEvent(part); + _services.EventPublisher.Publish(evt); + + if (evt.Result != null && !object.ReferenceEquals(evt.Result, part)) + { + modelPart = evt.Result; + name = evt.ModelPartName.NullEmpty() ?? ResolveModelName(evt.Result) ?? name; + } + else + { + modelPart = part; + } + + modelPart = evt.Result ?? part; + name = evt.ModelPartName.NullEmpty() ?? name; + } + + break; + } + + if (modelPart != null) + { + if (name.IsEmpty()) + { + throw new SmartException($"Could not resolve a model key for part '{modelPart.GetType().Name}'. Use an instance of 'NamedModelPart' class to pass model with name."); + } + + if (model.TryGetValue(name, out var existing)) + { + // A model part with the same name exists in model already... + if (existing is IDictionary x) + { + // but it's a dictionary which we can easily merge with + x.Merge(FastProperty.ObjectToDictionary(modelPart), true); + } + else + { + // Wrap in HybridExpando and merge + var he = new HybridExpando(existing, true); + he.Merge(FastProperty.ObjectToDictionary(modelPart), true); + model[name] = he; + } + } + else + { + // Put part to model as new property + model[name] = modelPart; + } + } + } + + public string ResolveModelName(object model) + { + Guard.NotNull(model, nameof(model)); + + string name = null; + var type = model.GetType(); + + try + { + if (model is BaseEntity be) + { + name = be.GetUnproxiedType().Name; + } + else if (model is ITestModel te) + { + name = te.ModelName; + } + else if (model is INamedModelPart mp) + { + name = mp.ModelPartName; + } + else if (type.IsPlainObjectType()) + { + name = type.Name; + } + } + catch { } + + return name; + } + + #region Global model part handlers + + protected virtual object CreateThemeModelPart(MessageContext messageContext) + { + var m = new Dictionary + { + { "FontFamily", "-apple-system, system-ui, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif" }, + { "BodyBg", "#f2f4f6" }, + { "BodyColor", "#555" }, + { "TitleColor", "#2f3133" }, + { "ContentBg", "#fff" }, + { "ShadeColor", "#e2e2e2" }, + { "LinkColor", "#0066c0" }, + { "BrandPrimary", "#3f51b5" }, + { "BrandSuccess", "#4caf50" }, + { "BrandWarning", "#ff9800" }, + { "BrandDanger", "#f44336" }, + { "MutedColor", "#a5a5a5" }, + }; + + return m; + } + + protected virtual object CreateCompanyModelPart(MessageContext messageContext) + { + var settings = _services.Settings.LoadSetting(messageContext.Store.Id); + var countryService = _services.Resolve(); + var country = countryService.GetCountryById(settings.CountryId); + dynamic m = new HybridExpando(settings, true); + + m.NameLine = Concat(settings.Salutation, settings.Title, settings.Firstname, settings.Lastname); + m.StreetLine = Concat(settings.Street, settings.Street2); + m.CityLine = Concat(settings.ZipCode, settings.City); + + if (country != null) + { + m.CountryLine = Concat(country.GetLocalized(x => x.Name), settings.Region); + } + + PublishModelPartCreatedEvent(settings, m); + return m; + } + + protected virtual object CreateBankModelPart(MessageContext messageContext) + { + var settings = _services.Settings.LoadSetting(messageContext.Store.Id); + var m = new HybridExpando(settings, true); + PublishModelPartCreatedEvent(settings, m); + return m; + } + + protected virtual object CreateContactModelPart(MessageContext messageContext) + { + var settings = _services.Settings.LoadSetting(messageContext.Store.Id); + var contact = new HybridExpando(settings, true) as dynamic; + + // Aliases + contact.Phone = new + { + Company = settings.CompanyTelephoneNumber.NullEmpty(), + Hotline = settings.HotlineTelephoneNumber.NullEmpty(), + Mobile = settings.MobileTelephoneNumber.NullEmpty(), + Fax = settings.CompanyFaxNumber.NullEmpty() + }; + + contact.Email = new + { + Company = settings.CompanyEmailAddress.NullEmpty(), + Webmaster = settings.WebmasterEmailAddress.NullEmpty(), + Support = settings.SupportEmailAddress.NullEmpty(), + Contact = settings.ContactEmailAddress.NullEmpty() + }; + + PublishModelPartCreatedEvent(settings, contact); + + return contact; + } + + #endregion + + #region Generic model part handlers + + protected virtual void MergeModelBag(IModelPart part, IDictionary model, MessageContext messageContext) + { + if (!(model.Get("Bag") is IDictionary bag)) + { + model["Bag"] = bag = new Dictionary(); + } + + var source = part as IDictionary; + bag.Merge(source); + } + + #endregion + + #region Entity specific model part handlers + + protected virtual object CreateModelPart(Store part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var host = messageContext.BaseUri.ToString(); + var logoFile = _services.MediaService.GetFileById(messageContext.Store.LogoMediaFileId, MediaLoadFlags.AsNoTracking); + + // Issue: https://github.com/smartstore/SmartStoreNET/issues/1321 + + var m = new Dictionary + { + { "Email", messageContext.EmailAccount.Email }, + { "EmailName", messageContext.EmailAccount.DisplayName }, + { "Name", part.Name }, + { "Url", host }, + { "Cdn", part.ContentDeliveryNetwork }, + { "PrimaryStoreCurrency", part.PrimaryStoreCurrency?.CurrencyCode }, + { "PrimaryExchangeRateCurrency", part.PrimaryExchangeRateCurrency?.CurrencyCode }, + { "Logo", CreateModelPart(logoFile, messageContext, host, null, new Size(400, 75)) }, + { "Company", CreateCompanyModelPart(messageContext) }, + { "Contact", CreateContactModelPart(messageContext) }, + { "Bank", CreateBankModelPart(messageContext) }, + { "Copyright", T("Content.CopyrightNotice", messageContext.Language.Id, DateTime.Now.Year.ToString(), part.Name).Text } + }; + + var he = new HybridExpando(true); + he.Merge(m, true); + + PublishModelPartCreatedEvent(part, he); + + return he; + } + + protected virtual object CreateModelPart( + MediaFileInfo part, + MessageContext messageContext, + string href, + int? targetSize = null, + Size? clientMaxSize = null, + string alt = null) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotEmpty(href, nameof(href)); + + if (part == null) + { + return null; + } + + var width = part.File.Width; + var height = part.File.Height; + + if (width.HasValue && height.HasValue && (targetSize.HasValue || clientMaxSize.HasValue)) + { + var maxSize = clientMaxSize ?? new Size(targetSize.Value, targetSize.Value); + var size = ImagingHelper.Rescale(new Size(width.Value, height.Value), maxSize); + width = size.Width; + height = size.Height; + } + + var m = new + { + Src = _services.MediaService.GetUrl(part, targetSize.GetValueOrDefault(), messageContext.BaseUri.ToString(), false), + Href = href, + Width = width, + Height = height, + Alt = alt + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(Product part, MessageContext messageContext, string attributesXml = null) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var mediaSettings = _services.Resolve(); + var shoppingCartSettings = _services.Resolve(); + var catalogSettings = _services.Resolve(); + var deliveryTimeService = _services.Resolve(); + var quantityUnitService = _services.Resolve(); + var productUrlHelper = _services.Resolve(); + + var currency = _services.WorkContext.WorkingCurrency; + var additionalShippingCharge = new Money( + _services.Resolve().ConvertFromPrimaryStoreCurrency(part.AdditionalShippingCharge, currency), + currency, + true); + var url = BuildUrl(productUrlHelper.GetProductUrl(part.Id, part.GetSeName(messageContext.Language.Id), attributesXml), messageContext); + var file = GetMediaFileFor(part, attributesXml); + var name = part.GetLocalized(x => x.Name, messageContext.Language.Id).Value; + var alt = T("Media.Product.ImageAlternateTextFormat", messageContext.Language.Id, name).Text; + + var m = new Dictionary + { + { "Id", part.Id }, + { "Sku", catalogSettings.ShowProductSku ? part.Sku : null }, + { "Name", name }, + { "Description", part.GetLocalized(x => x.ShortDescription, messageContext.Language).Value.NullEmpty() }, + { "StockQuantity", part.StockQuantity }, + { "AdditionalShippingCharge", additionalShippingCharge }, + { "Url", url }, + { "Thumbnail", CreateModelPart(file, messageContext, url, mediaSettings.MessageProductThumbPictureSize, new Size(50, 50), alt) }, + { "ThumbnailLg", CreateModelPart(file, messageContext, url, mediaSettings.ProductThumbPictureSize, new Size(120, 120), alt) }, + { "DeliveryTime", null }, + { "QtyUnit", null } + }; + + if (part.IsShipEnabled && shoppingCartSettings.DeliveryTimesInShoppingCart != DeliveryTimesPresentation.None) + { + if (deliveryTimeService.GetDeliveryTimeById(part.DeliveryTimeId ?? 0) is DeliveryTime dt) + { + m["DeliveryTime"] = new Dictionary + { + { "Color", dt.ColorHexValue }, + { "Name", dt.GetLocalized(x => x.Name, messageContext.Language).Value }, + }; + } + } + + if (quantityUnitService.GetQuantityUnitById(part.QuantityUnitId) is QuantityUnit qu) + { + m["QtyUnit"] = qu.GetLocalized(x => x.Name, messageContext.Language).Value; + } + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(Customer part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var email = part.FindEmail(); + var pwdRecoveryToken = part.GetAttribute(SystemCustomerAttributeNames.PasswordRecoveryToken).NullEmpty(); + var accountActivationToken = part.GetAttribute(SystemCustomerAttributeNames.AccountActivationToken).NullEmpty(); var customerVatStatus = (VatNumberStatus)part.VatNumberStatusId; int rewardPointsBalance = part.GetRewardPointsBalance(); - decimal rewardPointsAmountBase = _services.Resolve().ConvertRewardPointsToAmount(rewardPointsBalance); - decimal rewardPointsAmount = _services.Resolve().ConvertFromPrimaryStoreCurrency(rewardPointsAmountBase, _services.WorkContext.WorkingCurrency); + decimal rewardPointsAmountBase = _services.Resolve().ConvertRewardPointsToAmount(rewardPointsBalance); + decimal rewardPointsAmount = _services.Resolve().ConvertFromPrimaryStoreCurrency(rewardPointsAmountBase, _services.WorkContext.WorkingCurrency); + + var m = new Dictionary + { + ["Id"] = part.Id, + ["CustomerGuid"] = part.CustomerGuid, + ["Username"] = part.Username, + ["Email"] = email, + ["IsTaxExempt"] = part.IsTaxExempt, + ["LastIpAddress"] = part.LastIpAddress, + ["CreatedOn"] = ToUserDate(part.CreatedOnUtc, messageContext), + ["LastLoginOn"] = ToUserDate(part.LastLoginDateUtc, messageContext), + ["LastActivityOn"] = ToUserDate(part.LastActivityDateUtc, messageContext), + + ["FullName"] = GetDisplayNameForCustomer(part).NullEmpty(), + ["VatNumber"] = part.GetAttribute(SystemCustomerAttributeNames.VatNumber).NullEmpty(), + ["VatNumberStatus"] = customerVatStatus.GetLocalizedEnum(_services.Localization, messageContext.Language.Id).NullEmpty(), + ["CustomerNumber"] = part.CustomerNumber.NullEmpty(), + ["IsRegistered"] = part.IsRegistered(), + + // URLs + ["WishlistUrl"] = BuildRouteUrl("Wishlist", new { customerGuid = part.CustomerGuid }, messageContext), + ["EditUrl"] = BuildActionUrl("Edit", "Customer", new { id = part.Id, area = "admin" }, messageContext), + ["PasswordRecoveryURL"] = pwdRecoveryToken == null ? null : BuildActionUrl("passwordrecoveryconfirm", "customer", + new { token = part.GetAttribute(SystemCustomerAttributeNames.PasswordRecoveryToken), email, area = "" }, + messageContext), + ["AccountActivationURL"] = accountActivationToken == null ? null : BuildActionUrl("activation", "customer", + new { token = part.GetAttribute(SystemCustomerAttributeNames.AccountActivationToken), email, area = "" }, + messageContext), + + // Addresses + ["BillingAddress"] = CreateModelPart(part.BillingAddress ?? new Address(), messageContext), + ["ShippingAddress"] = part.ShippingAddress == null ? null : CreateModelPart(part.ShippingAddress, messageContext), + + // Reward Points + ["RewardPointsAmount"] = rewardPointsAmount, + ["RewardPointsBalance"] = FormatPrice(rewardPointsAmount, messageContext), + ["RewardPointsHistory"] = part.RewardPointsHistory.Count == 0 ? null : part.RewardPointsHistory.Select(x => CreateModelPart(x, messageContext)).ToList(), + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(GiftCard part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary + { + { "Id", part.Id }, + { "SenderName", part.SenderName.NullEmpty() }, + { "SenderEmail", part.SenderEmail.NullEmpty() }, + { "RecipientName", part.RecipientName.NullEmpty() }, + { "RecipientEmail", part.RecipientEmail.NullEmpty() }, + { "Amount", FormatPrice(part.Amount, messageContext) }, + { "CouponCode", part.GiftCardCouponCode.NullEmpty() } + }; + + // Message + var message = (string)null; + if (part.Message.HasValue()) + { + message = HtmlUtils.StripTags(part.Message); + } + m["Message"] = message; + + // RemainingAmount + Money remainingAmount = null; + var order = part?.PurchasedWithOrderItem?.Order; + if (order != null) + { + remainingAmount = FormatPrice(part.GetGiftCardRemainingAmount(), order, messageContext); + } + m["RemainingAmount"] = remainingAmount; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(NewsLetterSubscription part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var gid = part.NewsLetterSubscriptionGuid; var m = new Dictionary - { - ["Id"] = part.Id, - ["CustomerGuid"] = part.CustomerGuid, - ["Username"] = part.Username, - ["Email"] = email, - ["IsTaxExempt"] = part.IsTaxExempt, - ["LastIpAddress"] = part.LastIpAddress, - ["CreatedOn"] = ToUserDate(part.CreatedOnUtc, messageContext), - ["LastLoginOn"] = ToUserDate(part.LastLoginDateUtc, messageContext), - ["LastActivityOn"] = ToUserDate(part.LastActivityDateUtc, messageContext), - - ["FullName"] = GetDisplayNameForCustomer(part).NullEmpty(), - ["VatNumber"] = part.GetAttribute(SystemCustomerAttributeNames.VatNumber).NullEmpty(), - ["VatNumberStatus"] = customerVatStatus.GetLocalizedEnum(_services.Localization, messageContext.Language.Id).NullEmpty(), - ["CustomerNumber"] = part.CustomerNumber.NullEmpty(), - ["IsRegistered"] = part.IsRegistered(), - - // URLs - ["WishlistUrl"] = BuildRouteUrl("Wishlist", new { customerGuid = part.CustomerGuid }, messageContext), - ["EditUrl"] = BuildActionUrl("Edit", "Customer", new { id = part.Id, area = "admin" }, messageContext), - ["PasswordRecoveryURL"] = pwdRecoveryToken == null ? null : BuildActionUrl("passwordrecoveryconfirm", "customer", - new { token = part.GetAttribute(SystemCustomerAttributeNames.PasswordRecoveryToken), email, area = "" }, - messageContext), - ["AccountActivationURL"] = accountActivationToken == null ? null : BuildActionUrl("activation", "customer", - new { token = part.GetAttribute(SystemCustomerAttributeNames.AccountActivationToken), email, area = "" }, - messageContext), - - // Addresses - ["BillingAddress"] = CreateModelPart(part.BillingAddress ?? new Address(), messageContext), - ["ShippingAddress"] = part.ShippingAddress == null ? null : CreateModelPart(part.ShippingAddress, messageContext), - - // Reward Points - ["RewardPointsAmount"] = rewardPointsAmount, - ["RewardPointsBalance"] = FormatPrice(rewardPointsAmount, messageContext), - ["RewardPointsHistory"] = part.RewardPointsHistory.Count == 0 ? null : part.RewardPointsHistory.Select(x => CreateModelPart(x, messageContext)).ToList(), - }; - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(GiftCard part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary - { - { "Id", part.Id }, - { "SenderName", part.SenderName.NullEmpty() }, - { "SenderEmail", part.SenderEmail.NullEmpty() }, - { "RecipientName", part.RecipientName.NullEmpty() }, - { "RecipientEmail", part.RecipientEmail.NullEmpty() }, - { "Amount", FormatPrice(part.Amount, messageContext) }, - { "CouponCode", part.GiftCardCouponCode.NullEmpty() } - }; - - // Message - var message = (string)null; - if (part.Message.HasValue()) - { - message = HtmlUtils.StripTags(part.Message); - } - m["Message"] = message; - - // RemainingAmount - Money remainingAmount = null; - var order = part?.PurchasedWithOrderItem?.Order; - if (order != null) - { - remainingAmount = FormatPrice(part.GetGiftCardRemainingAmount(), order, messageContext); - } - m["RemainingAmount"] = remainingAmount; - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(NewsLetterSubscription part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var gid = part.NewsLetterSubscriptionGuid; - - var m = new Dictionary - { - { "Id", part.Id }, - { "Email", part.Email.NullEmpty() }, - { "ActivationUrl", gid == Guid.Empty ? null : BuildRouteUrl("NewsletterActivation", new { token = part.NewsLetterSubscriptionGuid, active = true }, messageContext) }, - { "DeactivationUrl", gid == Guid.Empty ? null : BuildRouteUrl("NewsletterActivation", new { token = part.NewsLetterSubscriptionGuid, active = false }, messageContext) } - }; - - var customer = messageContext.Customer; - if (customer != null && customer.Email.IsCaseInsensitiveEqual(part.Email.EmptyNull())) - { - // Set FullName only if a customer account exists for the subscriber's email address. - m["FullName"] = customer.GetFullName(); - } - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(Campaign part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var protocol = messageContext.BaseUri.Scheme; - var host = messageContext.BaseUri.Authority + messageContext.BaseUri.AbsolutePath; - var body = HtmlUtils.RelativizeFontSizes(part.Body.EmptyNull()); - - // We must render the body separately - body = _templateEngine.Render(body, messageContext.Model, messageContext.FormatProvider); - - var m = new Dictionary - { - { "Id", part.Id }, - { "Subject", part.Subject.NullEmpty() }, - { "Body", WebHelper.MakeAllUrlsAbsolute(body, protocol, host).NullEmpty() }, - { "CreatedOn", ToUserDate(part.CreatedOnUtc, messageContext) } - }; - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(ProductReview part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary - { - { "Title", part.Title.NullEmpty() }, - { "Text", HtmlUtils.StripTags(part.ReviewText).NullEmpty() }, - { "Rating", part.Rating } - }; - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(ProductReviewHelpfulness part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary - { - { "ProductReviewId", part.ProductReviewId }, - { "ReviewTitle", part.ProductReview.Title }, - { "WasHelpful", part.WasHelpful } - }; - - ApplyCustomerContentPart(m, part, messageContext); - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(PollVotingRecord part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary - { - { "PollAnswerId", part.PollAnswerId }, - { "PollAnswerName", part.PollAnswer.Name }, - { "PollId", part.PollAnswer.PollId } - }; - - ApplyCustomerContentPart(m, part, messageContext); - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(PrivateMessage part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary - { - { "Subject", part.Subject.NullEmpty() }, - { "Text", part.FormatPrivateMessageText().NullEmpty() }, - { "FromEmail", part.FromCustomer?.FindEmail().NullEmpty() }, - { "ToEmail", part.ToCustomer?.FindEmail().NullEmpty() }, - { "FromName", part.FromCustomer?.GetFullName().NullEmpty() }, - { "ToName", part.ToCustomer?.GetFullName().NullEmpty() }, - { "Url", BuildActionUrl("View", "PrivateMessages", new { id = part.Id, area = "" }, messageContext) } - }; - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(BlogComment part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary - { - { "PostTitle", part.BlogPost.Title.NullEmpty() }, - { "PostUrl", BuildRouteUrl("BlogPost", new { SeName = part.BlogPost.GetSeName(part.BlogPost.LanguageId, ensureTwoPublishedLanguages: false) }, messageContext) }, - { "Text", part.CommentText.NullEmpty() } - }; - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(NewsComment part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary - { - { "NewsTitle", part.NewsItem.Title.NullEmpty() }, - { "Title", part.CommentTitle.NullEmpty() }, - { "Text", HtmlUtils.StripTags(part.CommentText).NullEmpty() }, - { "NewsUrl", BuildRouteUrl("NewsItem", new { SeName = part.NewsItem.GetSeName(messageContext.Language.Id) }, messageContext) } - }; - - PublishModelPartCreatedEvent(part, m); - - return m; - } + { + { "Id", part.Id }, + { "Email", part.Email.NullEmpty() }, + { "ActivationUrl", gid == Guid.Empty ? null : BuildRouteUrl("NewsletterActivation", new { token = part.NewsLetterSubscriptionGuid, active = true }, messageContext) }, + { "DeactivationUrl", gid == Guid.Empty ? null : BuildRouteUrl("NewsletterActivation", new { token = part.NewsLetterSubscriptionGuid, active = false }, messageContext) } + }; - protected virtual object CreateModelPart(ForumTopic part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var pageIndex = messageContext.Model.GetFromBag("TopicPageIndex"); - - var url = pageIndex > 0 ? - BuildRouteUrl("TopicSlugPaged", new { id = part.Id, slug = part.GetSeName(), page = pageIndex }, messageContext) : - BuildRouteUrl("TopicSlug", new { id = part.Id, slug = part.GetSeName() }, messageContext); - - var m = new Dictionary - { - { "Subject", part.Subject.NullEmpty() }, - { "NumReplies", part.NumReplies }, - { "NumPosts", part.NumPosts }, - { "NumViews", part.Views }, - { "Body", part.GetFirstPost(_services.Resolve())?.FormatPostText().NullEmpty() }, - { "Url", url }, - }; - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(ForumPost part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary - { - { "Author", part.Customer.FormatUserName().NullEmpty() }, - { "Body", part.FormatPostText().NullEmpty() } - }; + var customer = messageContext.Customer; + if (customer != null && customer.Email.IsCaseInsensitiveEqual(part.Email.EmptyNull())) + { + // Set FullName only if a customer account exists for the subscriber's email address. + m["FullName"] = customer.GetFullName(); + } - PublishModelPartCreatedEvent(part, m); - - return m; - } + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(Campaign part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var protocol = messageContext.BaseUri.Scheme; + var host = messageContext.BaseUri.Authority + messageContext.BaseUri.AbsolutePath; + var body = HtmlUtils.RelativizeFontSizes(part.Body.EmptyNull()); + + // We must render the body separately + body = _templateEngine.Render(body, messageContext.Model, messageContext.FormatProvider); + + var m = new Dictionary + { + { "Id", part.Id }, + { "Subject", part.Subject.NullEmpty() }, + { "Body", WebHelper.MakeAllUrlsAbsolute(body, protocol, host).NullEmpty() }, + { "CreatedOn", ToUserDate(part.CreatedOnUtc, messageContext) } + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(ProductReview part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary + { + { "Title", part.Title.NullEmpty() }, + { "Text", HtmlUtils.StripTags(part.ReviewText).NullEmpty() }, + { "Rating", part.Rating } + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(ProductReviewHelpfulness part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary + { + { "ProductReviewId", part.ProductReviewId }, + { "ReviewTitle", part.ProductReview.Title }, + { "WasHelpful", part.WasHelpful } + }; + + ApplyCustomerContentPart(m, part, messageContext); + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(PollVotingRecord part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary + { + { "PollAnswerId", part.PollAnswerId }, + { "PollAnswerName", part.PollAnswer.Name }, + { "PollId", part.PollAnswer.PollId } + }; + + ApplyCustomerContentPart(m, part, messageContext); + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(PrivateMessage part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary + { + { "Subject", part.Subject.NullEmpty() }, + { "Text", part.FormatPrivateMessageText().NullEmpty() }, + { "FromEmail", part.FromCustomer?.FindEmail().NullEmpty() }, + { "ToEmail", part.ToCustomer?.FindEmail().NullEmpty() }, + { "FromName", part.FromCustomer?.GetFullName().NullEmpty() }, + { "ToName", part.ToCustomer?.GetFullName().NullEmpty() }, + { "Url", BuildActionUrl("View", "PrivateMessages", new { id = part.Id, area = "" }, messageContext) } + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(BlogComment part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary + { + { "PostTitle", part.BlogPost.GetLocalized(x => x.Title, messageContext.Language).Value.NullEmpty() }, + { "PostUrl", BuildRouteUrl("BlogPost", new { SeName = part.BlogPost.GetSeName(messageContext.Language.Id) }, messageContext) }, + { "Text", part.CommentText.NullEmpty() } + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(NewsComment part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary + { + { "NewsTitle", part.NewsItem.GetLocalized(x => x.Title, messageContext.Language).Value.NullEmpty() }, + { "Title", part.CommentTitle.NullEmpty() }, + { "Text", HtmlUtils.StripTags(part.CommentText).NullEmpty() }, + { "NewsUrl", BuildRouteUrl("NewsItem", new { SeName = part.NewsItem.GetSeName(messageContext.Language.Id) }, messageContext) } + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(ForumTopic part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var pageIndex = messageContext.Model.GetFromBag("TopicPageIndex"); + + var url = pageIndex > 0 ? + BuildRouteUrl("TopicSlugPaged", new { id = part.Id, slug = part.GetSeName(), page = pageIndex }, messageContext) : + BuildRouteUrl("TopicSlug", new { id = part.Id, slug = part.GetSeName() }, messageContext); + + var m = new Dictionary + { + { "Subject", part.Subject.NullEmpty() }, + { "NumReplies", part.NumReplies }, + { "NumPosts", part.NumPosts }, + { "NumViews", part.Views }, + { "Body", part.GetFirstPost(_services.Resolve())?.FormatPostText().NullEmpty() }, + { "Url", url }, + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(ForumPost part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary + { + { "Author", part.Customer.FormatUserName().NullEmpty() }, + { "Body", part.FormatPostText().NullEmpty() } + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } protected virtual object CreateModelPart(ForumPostVote part, MessageContext messageContext) { @@ -912,271 +921,285 @@ protected virtual object CreateModelPart(ForumPostVote part, MessageContext mess } protected virtual object CreateModelPart(Forum part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary - { - { "Name", part.GetLocalized(x => x.Name, messageContext.Language).Value.NullEmpty() }, - { "GroupName", part.ForumGroup?.GetLocalized(x => x.Name, messageContext.Language)?.Value.NullEmpty() }, - { "NumPosts", part.NumPosts }, - { "NumTopics", part.NumTopics }, - { "Url", BuildRouteUrl("ForumSlug", new { id = part.Id, slug = part.GetSeName(messageContext.Language.Id) }, messageContext) }, - }; - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(Address part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var settings = _services.Resolve(); - var languageId = messageContext.Language?.Id ?? messageContext.LanguageId; - - var salutation = part.Salutation.NullEmpty(); - var title = part.Title.NullEmpty(); - var company = settings.CompanyEnabled ? part.Company : null; - var firstName = part.FirstName.NullEmpty(); - var lastName = part.LastName.NullEmpty(); - var street1 = settings.StreetAddressEnabled ? part.Address1 : null; - var street2 = settings.StreetAddress2Enabled ? part.Address2 : null; - var zip = settings.ZipPostalCodeEnabled ? part.ZipPostalCode : null; - var city = settings.CityEnabled ? part.City : null; - var country = settings.CountryEnabled ? part.Country?.GetLocalized(x => x.Name, languageId ?? 0)?.Value.NullEmpty() : null; - var state = settings.StateProvinceEnabled ? part.StateProvince?.GetLocalized(x => x.Name, languageId ?? 0)?.Value.NullEmpty() : null; - - var m = new Dictionary - { - { "Title", title }, - { "Salutation", salutation }, - { "FullSalutation", part.GetFullSalutaion().NullEmpty() }, - { "FullName", part.GetFullName(false).NullEmpty() }, - { "Company", company }, - { "FirstName", firstName }, - { "LastName", lastName }, - { "Street1", street1 }, - { "Street2", street2 }, - { "Country", country }, - { "CountryId", part.Country?.Id }, - { "CountryAbbrev2", settings.CountryEnabled ? part.Country?.TwoLetterIsoCode.NullEmpty() : null }, - { "CountryAbbrev3", settings.CountryEnabled ? part.Country?.ThreeLetterIsoCode.NullEmpty() : null }, - { "State", state }, - { "StateAbbrev", settings.StateProvinceEnabled ? part.StateProvince?.Abbreviation.NullEmpty() : null }, - { "City", city }, - { "ZipCode", zip }, - { "Email", part.Email.NullEmpty() }, - { "Phone", settings.PhoneEnabled ? part.PhoneNumber : null }, - { "Fax", settings.FaxEnabled ? part.FaxNumber : null } - }; - - m["NameLine"] = Concat(salutation, title, firstName, lastName); - m["StreetLine"] = Concat(street1, street2); - m["CityLine"] = Concat(zip, city); - m["CountryLine"] = Concat(country, state); - - PublishModelPartCreatedEvent
(part, m); - - return m; - } - - protected virtual object CreateModelPart(RewardPointsHistory part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary - { - { "Id", part.Id }, - { "CreatedOn", ToUserDate(part.CreatedOnUtc, messageContext) }, - { "Message", part.Message.NullEmpty() }, - { "Points", part.Points }, - { "PointsBalance", part.PointsBalance }, - { "UsedAmount", part.UsedAmount } - }; - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(IEnumerable part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary(); - - foreach (var attr in part) - { - m[attr.Key] = attr.Value; - } - - PublishModelPartCreatedEvent>(part, m); - - return m; - } - - protected virtual object CreateModelPart(ForumSubscription part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary - { - { "SubscriptionGuid", part.SubscriptionGuid }, - { "CustomerId", part.CustomerId }, - { "ForumId", part.ForumId }, - { "TopicId", part.TopicId }, - { "CreatedOn", ToUserDate(part.CreatedOnUtc, messageContext) } - }; - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - protected virtual object CreateModelPart(BackInStockSubscription part, MessageContext messageContext) - { - Guard.NotNull(messageContext, nameof(messageContext)); - Guard.NotNull(part, nameof(part)); - - var m = new Dictionary - { - { "StoreId", part.StoreId }, - { "CustomerId", part.CustomerId }, - { "ProductId", part.ProductId }, - { "CreatedOn", ToUserDate(part.CreatedOnUtc, messageContext) } - }; - - PublishModelPartCreatedEvent(part, m); - - return m; - } - - #endregion - - #region Model Tree - - public TreeNode GetLastModelTree(string messageTemplateName) - { - Guard.NotEmpty(messageTemplateName, nameof(messageTemplateName)); - - var template = _messageTemplateService.GetMessageTemplateByName(messageTemplateName, _services.StoreContext.CurrentStore.Id); - - if (template != null) - { - return GetLastModelTree(template); - } - - return null; - } - - public TreeNode GetLastModelTree(MessageTemplate template) - { - Guard.NotNull(template, nameof(template)); - - if (template.LastModelTree.IsEmpty()) - { - return null; - } - - return Newtonsoft.Json.JsonConvert.DeserializeObject>(template.LastModelTree); - } - - public TreeNode BuildModelTree(TemplateModel model) - { - Guard.NotNull(model, nameof(model)); - - var root = new TreeNode(new ModelTreeMember { Name = "Model", Kind = ModelTreeMemberKind.Root }); - - foreach (var kvp in model) - { - root.Append(BuildModelTreePart(kvp.Key, kvp.Value)); - } - - return root; - } - - private TreeNode BuildModelTreePart(string modelName, object instance) - { - var t = instance?.GetType(); - TreeNode node = null; - - if (t == null || t.IsPredefinedType()) - { - node = new TreeNode(new ModelTreeMember { Name = modelName, Kind = ModelTreeMemberKind.Primitive }); - } - else if (t.IsSequenceType() && !(instance is IDictionary)) - { - node = new TreeNode(new ModelTreeMember { Name = modelName, Kind = ModelTreeMemberKind.Collection }); - } - else - { - node = new TreeNode(new ModelTreeMember { Name = modelName, Kind = ModelTreeMemberKind.Complex }); - - if (instance is IDictionary dict) - { - foreach (var kvp in dict) - { - node.Append(BuildModelTreePart(kvp.Key, kvp.Value)); - } - } - else if (instance is IDynamicMetaObjectProvider dyn) - { - foreach (var name in dyn.GetMetaObject(Expression.Constant(dyn)).GetDynamicMemberNames()) - { - // we don't want to go deeper in "pure" dynamic objects - node.Append(new TreeNode(new ModelTreeMember { Name = name, Kind = ModelTreeMemberKind.Primitive })); - } - } - else - { - node.AppendRange(BuildModelTreePartForClass(instance)); - } - } - - return node; - } - - private IEnumerable> BuildModelTreePartForClass(object instance) - { - var type = instance?.GetType(); - - if (type == null) - { - yield break; - } - - foreach (var prop in FastProperty.GetProperties(type).Values) - { - var pi = prop.Property; - - if (pi.PropertyType.IsPredefinedType()) - { - yield return new TreeNode(new ModelTreeMember { Name = prop.Name, Kind = ModelTreeMemberKind.Primitive }); - } - else if (typeof(IDictionary).IsAssignableFrom(pi.PropertyType)) - { - yield return BuildModelTreePart(prop.Name, prop.GetValue(instance)); - } - else if (pi.PropertyType.IsSequenceType()) - { - yield return new TreeNode(new ModelTreeMember { Name = prop.Name, Kind = ModelTreeMemberKind.Collection }); - } - else - { - var node = new TreeNode(new ModelTreeMember { Name = prop.Name, Kind = ModelTreeMemberKind.Complex }); - node.AppendRange(BuildModelTreePartForClass(prop.GetValue(instance))); - yield return node; - } - } - } - - #endregion - } + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary + { + { "Name", part.GetLocalized(x => x.Name, messageContext.Language).Value.NullEmpty() }, + { "GroupName", part.ForumGroup?.GetLocalized(x => x.Name, messageContext.Language)?.Value.NullEmpty() }, + { "NumPosts", part.NumPosts }, + { "NumTopics", part.NumTopics }, + { "Url", BuildRouteUrl("ForumSlug", new { id = part.Id, slug = part.GetSeName(messageContext.Language.Id) }, messageContext) }, + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(Address part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var settings = _services.Resolve(); + var languageId = messageContext.Language?.Id ?? messageContext.LanguageId; + + var salutation = part.Salutation.NullEmpty(); + var title = part.Title.NullEmpty(); + var company = settings.CompanyEnabled ? part.Company : null; + var firstName = part.FirstName.NullEmpty(); + var lastName = part.LastName.NullEmpty(); + var street1 = settings.StreetAddressEnabled ? part.Address1 : null; + var street2 = settings.StreetAddress2Enabled ? part.Address2 : null; + var zip = settings.ZipPostalCodeEnabled ? part.ZipPostalCode : null; + var city = settings.CityEnabled ? part.City : null; + var country = settings.CountryEnabled ? part.Country?.GetLocalized(x => x.Name, languageId ?? 0)?.Value.NullEmpty() : null; + var state = settings.StateProvinceEnabled ? part.StateProvince?.GetLocalized(x => x.Name, languageId ?? 0)?.Value.NullEmpty() : null; + + var m = new Dictionary + { + { "Title", title }, + { "Salutation", salutation }, + { "FullSalutation", part.GetFullSalutaion().NullEmpty() }, + { "FullName", part.GetFullName(false).NullEmpty() }, + { "Company", company }, + { "FirstName", firstName }, + { "LastName", lastName }, + { "Street1", street1 }, + { "Street2", street2 }, + { "Country", country }, + { "CountryId", part.Country?.Id }, + { "CountryAbbrev2", settings.CountryEnabled ? part.Country?.TwoLetterIsoCode.NullEmpty() : null }, + { "CountryAbbrev3", settings.CountryEnabled ? part.Country?.ThreeLetterIsoCode.NullEmpty() : null }, + { "State", state }, + { "StateAbbrev", settings.StateProvinceEnabled ? part.StateProvince?.Abbreviation.NullEmpty() : null }, + { "City", city }, + { "ZipCode", zip }, + { "Email", part.Email.NullEmpty() }, + { "Phone", settings.PhoneEnabled ? part.PhoneNumber : null }, + { "Fax", settings.FaxEnabled ? part.FaxNumber : null } + }; + + m["NameLine"] = Concat(salutation, title, firstName, lastName); + m["StreetLine"] = Concat(street1, street2); + m["CityLine"] = Concat(zip, city); + m["CountryLine"] = Concat(country, state); + + PublishModelPartCreatedEvent
(part, m); + + return m; + } + + protected virtual object CreateModelPart(RewardPointsHistory part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary + { + { "Id", part.Id }, + { "CreatedOn", ToUserDate(part.CreatedOnUtc, messageContext) }, + { "Message", part.Message.NullEmpty() }, + { "Points", part.Points }, + { "PointsBalance", part.PointsBalance }, + { "UsedAmount", part.UsedAmount } + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(IEnumerable part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary(); + + foreach (var attr in part) + { + m[attr.Key] = attr.Value; + } + + PublishModelPartCreatedEvent>(part, m); + + return m; + } + + protected virtual object CreateModelPart(ForumSubscription part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary + { + { "SubscriptionGuid", part.SubscriptionGuid }, + { "CustomerId", part.CustomerId }, + { "ForumId", part.ForumId }, + { "TopicId", part.TopicId }, + { "CreatedOn", ToUserDate(part.CreatedOnUtc, messageContext) } + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + protected virtual object CreateModelPart(BackInStockSubscription part, MessageContext messageContext) + { + Guard.NotNull(messageContext, nameof(messageContext)); + Guard.NotNull(part, nameof(part)); + + var m = new Dictionary + { + { "StoreId", part.StoreId }, + { "CustomerId", part.CustomerId }, + { "ProductId", part.ProductId }, + { "CreatedOn", ToUserDate(part.CreatedOnUtc, messageContext) } + }; + + PublishModelPartCreatedEvent(part, m); + + return m; + } + + #endregion + + #region Model Tree + + public TreeNode GetLastModelTree(string messageTemplateName) + { + Guard.NotEmpty(messageTemplateName, nameof(messageTemplateName)); + + var template = _messageTemplateService.GetMessageTemplateByName(messageTemplateName, _services.StoreContext.CurrentStore.Id); + + if (template != null) + { + return GetLastModelTree(template); + } + + return null; + } + + public TreeNode GetLastModelTree(MessageTemplate template) + { + Guard.NotNull(template, nameof(template)); + + if (template.LastModelTree.IsEmpty()) + { + return null; + } + + return Newtonsoft.Json.JsonConvert.DeserializeObject>(template.LastModelTree); + } + + public TreeNode BuildModelTree(TemplateModel model) + { + Guard.NotNull(model, nameof(model)); + + var root = new TreeNode(new ModelTreeMember { Name = "Model", Kind = ModelTreeMemberKind.Root }); + + foreach (var kvp in model) + { + root.Append(BuildModelTreePart(kvp.Key, kvp.Value)); + } + + return root; + } + + private TreeNode BuildModelTreePart(string modelName, object instance) + { + var t = instance?.GetType(); + TreeNode node = null; + + if (t == null || t.IsPredefinedType()) + { + node = new TreeNode(new ModelTreeMember { Name = modelName, Kind = ModelTreeMemberKind.Primitive }); + } + else if (t.IsSequenceType() && !(instance is IDictionary)) + { + node = new TreeNode(new ModelTreeMember { Name = modelName, Kind = ModelTreeMemberKind.Collection }); + } + else + { + node = new TreeNode(new ModelTreeMember { Name = modelName, Kind = ModelTreeMemberKind.Complex }); + + if (instance is IDictionary dict) + { + foreach (var kvp in dict) + { + node.Append(BuildModelTreePart(kvp.Key, kvp.Value)); + } + } + else if (instance is IDynamicMetaObjectProvider dyn) + { + foreach (var name in dyn.GetMetaObject(Expression.Constant(dyn)).GetDynamicMemberNames()) + { + // we don't want to go deeper in "pure" dynamic objects + node.Append(new TreeNode(new ModelTreeMember { Name = name, Kind = ModelTreeMemberKind.Primitive })); + } + } + else + { + node.AppendRange(BuildModelTreePartForClass(instance)); + } + } + + return node; + } + + private IEnumerable> BuildModelTreePartForClass(object instance, HashSet instanceLookup = null) + { + var type = instance?.GetType(); + + if (type == null) + { + yield break; + } + + foreach (var prop in FastProperty.GetProperties(type).Values) + { + var pi = prop.Property; + + if (pi.PropertyType.IsPredefinedType()) + { + yield return new TreeNode(new ModelTreeMember { Name = prop.Name, Kind = ModelTreeMemberKind.Primitive }); + } + else if (typeof(IDictionary).IsAssignableFrom(pi.PropertyType)) + { + yield return BuildModelTreePart(prop.Name, prop.GetValue(instance)); + } + else if (pi.PropertyType.IsSequenceType()) + { + yield return new TreeNode(new ModelTreeMember { Name = prop.Name, Kind = ModelTreeMemberKind.Collection }); + } + else if (pi.PropertyType.IsClass) + { + if (instanceLookup == null) + { + instanceLookup = new HashSet(ReferenceEqualityComparer.Default) { instance }; + } + + var childInstance = prop.GetValue(instance); + if (childInstance != null) + { + if (!instanceLookup.Contains(childInstance)) + { + instanceLookup.Add(childInstance); + + var node = new TreeNode(new ModelTreeMember { Name = prop.Name, Kind = ModelTreeMemberKind.Complex }); + node.AppendRange(BuildModelTreePartForClass(childInstance, instanceLookup)); + yield return node; + } + } + } + } + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/Messages/MessageTemplateService.cs b/src/Libraries/SmartStore.Services/Messages/MessageTemplateService.cs index e9e25c6677..ee5e0689ad 100644 --- a/src/Libraries/SmartStore.Services/Messages/MessageTemplateService.cs +++ b/src/Libraries/SmartStore.Services/Messages/MessageTemplateService.cs @@ -5,71 +5,67 @@ using SmartStore.Core.Data; using SmartStore.Core.Domain.Messages; using SmartStore.Core.Domain.Stores; -using SmartStore.Core.Events; using SmartStore.Services.Localization; using SmartStore.Services.Stores; namespace SmartStore.Services.Messages { - public partial class MessageTemplateService: IMessageTemplateService + public partial class MessageTemplateService : IMessageTemplateService { private const string MESSAGETEMPLATES_ALL_KEY = "SmartStore.messagetemplate.all-{0}"; private const string MESSAGETEMPLATES_BY_NAME_KEY = "SmartStore.messagetemplate.name-{0}-{1}"; private const string MESSAGETEMPLATES_PATTERN_KEY = "SmartStore.messagetemplate.*"; private readonly IRepository _messageTemplateRepository; - private readonly IRepository _storeMappingRepository; - private readonly ILanguageService _languageService; - private readonly IStoreMappingService _storeMappingService; - private readonly ILocalizedEntityService _localizedEntityService; - private readonly IEventPublisher _eventPublisher; + private readonly IRepository _storeMappingRepository; + private readonly ILanguageService _languageService; + private readonly IStoreMappingService _storeMappingService; + private readonly ILocalizedEntityService _localizedEntityService; private readonly IRequestCache _requestCache; public MessageTemplateService( - IRequestCache requestCache, - IRepository storeMappingRepository, - ILanguageService languageService, - ILocalizedEntityService localizedEntityService, - IStoreMappingService storeMappingService, - IRepository messageTemplateRepository, - IEventPublisher eventPublisher) + IRequestCache requestCache, + IRepository storeMappingRepository, + ILanguageService languageService, + ILocalizedEntityService localizedEntityService, + IStoreMappingService storeMappingService, + IRepository messageTemplateRepository) { - _requestCache = requestCache; - _storeMappingRepository = storeMappingRepository; - _languageService = languageService; - _localizedEntityService = localizedEntityService; - _storeMappingService = storeMappingService; - _messageTemplateRepository = messageTemplateRepository; - _eventPublisher = eventPublisher; - - QuerySettings = DbQuerySettings.Default; - } + _requestCache = requestCache; + _storeMappingRepository = storeMappingRepository; + _languageService = languageService; + _localizedEntityService = localizedEntityService; + _storeMappingService = storeMappingService; + _messageTemplateRepository = messageTemplateRepository; + + QuerySettings = DbQuerySettings.Default; + } - public DbQuerySettings QuerySettings { get; set; } + public DbQuerySettings QuerySettings { get; set; } - public virtual void DeleteMessageTemplate(MessageTemplate messageTemplate) - { - Guard.NotNull(messageTemplate, nameof(messageTemplate)); + public virtual void DeleteMessageTemplate(MessageTemplate messageTemplate) + { + Guard.NotNull(messageTemplate, nameof(messageTemplate)); - _messageTemplateRepository.Delete(messageTemplate); + _messageTemplateRepository.Delete(messageTemplate); - _requestCache.RemoveByPattern(MESSAGETEMPLATES_PATTERN_KEY); - } + _requestCache.RemoveByPattern(MESSAGETEMPLATES_PATTERN_KEY); + } public virtual void InsertMessageTemplate(MessageTemplate messageTemplate) { - Guard.NotNull(messageTemplate, nameof(messageTemplate)); + Guard.NotNull(messageTemplate, nameof(messageTemplate)); - _messageTemplateRepository.Insert(messageTemplate); + _messageTemplateRepository.Insert(messageTemplate); _requestCache.RemoveByPattern(MESSAGETEMPLATES_PATTERN_KEY); } public virtual void UpdateMessageTemplate(MessageTemplate messageTemplate) { - Guard.NotNull(messageTemplate, nameof(messageTemplate)); + Guard.NotNull(messageTemplate, nameof(messageTemplate)); - _messageTemplateRepository.Update(messageTemplate); + _messageTemplateRepository.Update(messageTemplate); _requestCache.RemoveByPattern(MESSAGETEMPLATES_PATTERN_KEY); } @@ -82,109 +78,110 @@ public virtual MessageTemplate GetMessageTemplateById(int messageTemplateId) return _messageTemplateRepository.GetById(messageTemplateId); } - public virtual MessageTemplate GetMessageTemplateByName(string messageTemplateName, int storeId) + public virtual MessageTemplate GetMessageTemplateByName(string messageTemplateName, int storeId) { - Guard.NotEmpty(messageTemplateName, nameof(messageTemplateName)); + Guard.NotEmpty(messageTemplateName, nameof(messageTemplateName)); string key = string.Format(MESSAGETEMPLATES_BY_NAME_KEY, messageTemplateName, storeId); return _requestCache.Get(key, () => { - var query = _messageTemplateRepository.Table; - query = query.Where(t => t.Name == messageTemplateName); - query = query.OrderBy(t => t.Id); - var templates = query.ToList(); + var query = _messageTemplateRepository.Table; + query = query.Where(t => t.Name == messageTemplateName); + query = query.OrderBy(t => t.Id); + var templates = query.ToList(); - //store mapping - if (storeId > 0) - { - return templates.Where(t => _storeMappingService.Authorize(t, storeId)).FirstOrDefault(); - } + //store mapping + if (storeId > 0) + { + return templates.Where(t => _storeMappingService.Authorize(t, storeId)).FirstOrDefault(); + } return templates.FirstOrDefault(); }); } - public virtual IList GetAllMessageTemplates(int storeId) + public virtual IList GetAllMessageTemplates(int storeId) { - string key = string.Format(MESSAGETEMPLATES_ALL_KEY, storeId); - return _requestCache.Get(key, () => + string key = string.Format(MESSAGETEMPLATES_ALL_KEY, storeId); + return _requestCache.Get(key, () => { - var query = _messageTemplateRepository.Table; - query = query.OrderBy(t => t.Name); - - // Store mapping - if (storeId > 0 && !QuerySettings.IgnoreMultiStore) - { - query = from t in query - join sm in _storeMappingRepository.Table - on new { c1 = t.Id, c2 = "MessageTemplate" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into t_sm - from sm in t_sm.DefaultIfEmpty() - where !t.LimitedToStores || storeId == sm.StoreId - select t; - - // Only distinct items (group by ID) - query = from t in query - group t by t.Id into tGroup - orderby tGroup.Key - select tGroup.FirstOrDefault(); - query = query.OrderBy(t => t.Name); - } - - return query.ToList(); + var query = _messageTemplateRepository.Table; + query = query.OrderBy(t => t.Name); + + // Store mapping + if (storeId > 0 && !QuerySettings.IgnoreMultiStore) + { + query = from t in query + join sm in _storeMappingRepository.Table + on new { c1 = t.Id, c2 = "MessageTemplate" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into t_sm + from sm in t_sm.DefaultIfEmpty() + where !t.LimitedToStores || storeId == sm.StoreId + select t; + + // Only distinct items (group by ID) + query = from t in query + group t by t.Id into tGroup + orderby tGroup.Key + select tGroup.FirstOrDefault(); + query = query.OrderBy(t => t.Name); + } + + return query.ToList(); }); } - public virtual MessageTemplate CopyMessageTemplate(MessageTemplate messageTemplate) - { - Guard.NotNull(messageTemplate, nameof(messageTemplate)); - - var mtCopy = new MessageTemplate - { - Name = messageTemplate.Name, - To = messageTemplate.To, - ReplyTo = messageTemplate.ReplyTo, - BccEmailAddresses = messageTemplate.BccEmailAddresses, - Subject = messageTemplate.Subject, - Body = messageTemplate.Body, - IsActive = messageTemplate.IsActive, - EmailAccountId = messageTemplate.EmailAccountId, - LimitedToStores = messageTemplate.LimitedToStores - // INFO: we do not copy attachments - }; - - InsertMessageTemplate(mtCopy); - - var languages = _languageService.GetAllLanguages(true); - - // localization - foreach (var lang in languages) - { - string bccEmailAddresses = messageTemplate.GetLocalized(x => x.BccEmailAddresses, lang, false, false); - if (bccEmailAddresses.HasValue()) - _localizedEntityService.SaveLocalizedValue(mtCopy, x => x.BccEmailAddresses, bccEmailAddresses, lang.Id); - - string subject = messageTemplate.GetLocalized(x => x.Subject, lang, false, false); - if (subject.HasValue()) - _localizedEntityService.SaveLocalizedValue(mtCopy, x => x.Subject, subject, lang.Id); - - string body = messageTemplate.GetLocalized(x => x.Body, lang, false, false); - if (body.HasValue()) - _localizedEntityService.SaveLocalizedValue(mtCopy, x => x.Body, subject, lang.Id); - - int emailAccountId = messageTemplate.GetLocalized(x => x.EmailAccountId, lang, false, false); - if (emailAccountId > 0) - _localizedEntityService.SaveLocalizedValue(mtCopy, x => x.EmailAccountId, emailAccountId, lang.Id); - } - - // store mapping - var selectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(messageTemplate); - foreach (var id in selectedStoreIds) - { - _storeMappingService.InsertStoreMapping(mtCopy, id); - } - - return mtCopy; - } + public virtual MessageTemplate CopyMessageTemplate(MessageTemplate messageTemplate) + { + Guard.NotNull(messageTemplate, nameof(messageTemplate)); + + var mtCopy = new MessageTemplate + { + Name = messageTemplate.Name, + To = messageTemplate.To, + ReplyTo = messageTemplate.ReplyTo, + BccEmailAddresses = messageTemplate.BccEmailAddresses, + Subject = messageTemplate.Subject, + Body = messageTemplate.Body, + IsActive = messageTemplate.IsActive, + EmailAccountId = messageTemplate.EmailAccountId, + LimitedToStores = messageTemplate.LimitedToStores, + ModelTypes = messageTemplate.ModelTypes + // INFO: we do not copy attachments + }; + + InsertMessageTemplate(mtCopy); + + var languages = _languageService.GetAllLanguages(true); + + // localization + foreach (var lang in languages) + { + string bccEmailAddresses = messageTemplate.GetLocalized(x => x.BccEmailAddresses, lang, false, false); + if (bccEmailAddresses.HasValue()) + _localizedEntityService.SaveLocalizedValue(mtCopy, x => x.BccEmailAddresses, bccEmailAddresses, lang.Id); + + string subject = messageTemplate.GetLocalized(x => x.Subject, lang, false, false); + if (subject.HasValue()) + _localizedEntityService.SaveLocalizedValue(mtCopy, x => x.Subject, subject, lang.Id); + + string body = messageTemplate.GetLocalized(x => x.Body, lang, false, false); + if (body.HasValue()) + _localizedEntityService.SaveLocalizedValue(mtCopy, x => x.Body, subject, lang.Id); + + int emailAccountId = messageTemplate.GetLocalized(x => x.EmailAccountId, lang, false, false); + if (emailAccountId > 0) + _localizedEntityService.SaveLocalizedValue(mtCopy, x => x.EmailAccountId, emailAccountId, lang.Id); + } + + // store mapping + var selectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(messageTemplate); + foreach (var id in selectedStoreIds) + { + _storeMappingService.InsertStoreMapping(mtCopy, id); + } + + return mtCopy; + } } } diff --git a/src/Libraries/SmartStore.Services/Messages/NewsLetterSubscriptionService.cs b/src/Libraries/SmartStore.Services/Messages/NewsLetterSubscriptionService.cs index 5dc78f8647..b116b8c3ac 100644 --- a/src/Libraries/SmartStore.Services/Messages/NewsLetterSubscriptionService.cs +++ b/src/Libraries/SmartStore.Services/Messages/NewsLetterSubscriptionService.cs @@ -2,35 +2,35 @@ using System.Linq; using SmartStore.Core; using SmartStore.Core.Data; +using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Messages; -using SmartStore.Core.Events; namespace SmartStore.Services.Messages { - public class NewsLetterSubscriptionService : INewsLetterSubscriptionService + public class NewsLetterSubscriptionService : INewsLetterSubscriptionService { private readonly IRepository _subscriptionRepository; - private readonly ICommonServices _services; + private readonly IRepository _customerRepository; + private readonly ICommonServices _services; - public NewsLetterSubscriptionService(IRepository subscriptionRepository, ICommonServices services) + public NewsLetterSubscriptionService( + IRepository subscriptionRepository, + IRepository customerRepository, + ICommonServices services) { _subscriptionRepository = subscriptionRepository; - _services = services; - } - - /// - /// Inserts a newsletter subscription - /// - /// NewsLetter subscription - /// if set to true [publish subscription events]. + _customerRepository = customerRepository; + _services = services; + } + public void InsertNewsLetterSubscription(NewsLetterSubscription newsLetterSubscription, bool publishSubscriptionEvents = true) { - Guard.NotNull(newsLetterSubscription, nameof(newsLetterSubscription)); + Guard.NotNull(newsLetterSubscription, nameof(newsLetterSubscription)); - if (newsLetterSubscription.StoreId == 0) - { - throw new SmartException("News letter subscription must be assigned to a valid store."); - } + if (newsLetterSubscription.StoreId == 0) + { + throw new SmartException("News letter subscription must be assigned to a valid store."); + } // Handle e-mail newsLetterSubscription.Email = EnsureSubscriberEmailOrThrow(newsLetterSubscription.Email); @@ -45,19 +45,14 @@ public void InsertNewsLetterSubscription(NewsLetterSubscription newsLetterSubscr } } - /// - /// Updates a newsletter subscription - /// - /// NewsLetter subscription - /// if set to true [publish subscription events]. public void UpdateNewsLetterSubscription(NewsLetterSubscription newsLetterSubscription, bool publishSubscriptionEvents = true) { - Guard.NotNull(newsLetterSubscription, nameof(newsLetterSubscription)); + Guard.NotNull(newsLetterSubscription, nameof(newsLetterSubscription)); - if (newsLetterSubscription.StoreId == 0) - { - throw new SmartException("News letter subscription must be assigned to a valid store."); - } + if (newsLetterSubscription.StoreId == 0) + { + throw new SmartException("News letter subscription must be assigned to a valid store."); + } // Handle e-mail newsLetterSubscription.Email = EnsureSubscriberEmailOrThrow(newsLetterSubscription.Email); @@ -75,8 +70,8 @@ public void UpdateNewsLetterSubscription(NewsLetterSubscription newsLetterSubscr // If the previous entry was false, but this one is true, publish a subscribe. PublishSubscriptionEvent(newsLetterSubscription.Email, true, publishSubscriptionEvents); } - - if ((originalSubscription.Active && newsLetterSubscription.Active) && + + if ((originalSubscription.Active && newsLetterSubscription.Active) && (originalSubscription.Email != newsLetterSubscription.Email)) { // If the two emails are different publish an unsubscribe. @@ -90,14 +85,9 @@ public void UpdateNewsLetterSubscription(NewsLetterSubscription newsLetterSubscr } } - /// - /// Deletes a newsletter subscription - /// - /// NewsLetter subscription - /// if set to true [publish subscription events]. public virtual void DeleteNewsLetterSubscription(NewsLetterSubscription newsLetterSubscription, bool publishSubscriptionEvents = true) { - Guard.NotNull(newsLetterSubscription, nameof(newsLetterSubscription)); + Guard.NotNull(newsLetterSubscription, nameof(newsLetterSubscription)); _subscriptionRepository.Delete(newsLetterSubscription); @@ -105,60 +95,55 @@ public virtual void DeleteNewsLetterSubscription(NewsLetterSubscription newsLett PublishSubscriptionEvent(newsLetterSubscription.Email, false, publishSubscriptionEvents); } - public virtual bool? AddNewsLetterSubscriptionFor(bool add, string email, int storeId) - { - bool? result = null; - - if (email.IsEmail()) - { - var newsletter = GetNewsLetterSubscriptionByEmail(email, storeId); - if (newsletter != null) - { - if (add) - { - if (!newsletter.Active) - { - _services.MessageFactory.SendNewsLetterSubscriptionActivationMessage(newsletter, _services.WorkContext.WorkingLanguage.Id); - } - UpdateNewsLetterSubscription(newsletter); - result = true; - } - else - { - DeleteNewsLetterSubscription(newsletter); - result = false; - } - } - else - { - if (add) - { - newsletter = new NewsLetterSubscription - { - NewsLetterSubscriptionGuid = Guid.NewGuid(), - Email = email, - Active = false, - CreatedOnUtc = DateTime.UtcNow, - StoreId = storeId, + public virtual bool? AddNewsLetterSubscriptionFor(bool add, string email, int storeId) + { + bool? result = null; + + if (email.IsEmail()) + { + var newsletter = GetNewsLetterSubscriptionByEmail(email, storeId); + if (newsletter != null) + { + if (add) + { + if (!newsletter.Active) + { + _services.MessageFactory.SendNewsLetterSubscriptionActivationMessage(newsletter, _services.WorkContext.WorkingLanguage.Id); + } + UpdateNewsLetterSubscription(newsletter); + result = true; + } + else + { + DeleteNewsLetterSubscription(newsletter); + result = false; + } + } + else + { + if (add) + { + newsletter = new NewsLetterSubscription + { + NewsLetterSubscriptionGuid = Guid.NewGuid(), + Email = email, + Active = false, + CreatedOnUtc = DateTime.UtcNow, + StoreId = storeId, WorkingLanguageId = _services.WorkContext.WorkingLanguage.Id - }; - InsertNewsLetterSubscription(newsletter); - - _services.MessageFactory.SendNewsLetterSubscriptionActivationMessage(newsletter, _services.WorkContext.WorkingLanguage.Id); - - result = true; - } - } - } - return result; - } - - /// - /// Gets a newsletter subscription by newsletter subscription identifier - /// - /// The newsletter subscription identifier - /// NewsLetter subscription - public virtual NewsLetterSubscription GetNewsLetterSubscriptionById(int newsLetterSubscriptionId) + }; + InsertNewsLetterSubscription(newsletter); + + _services.MessageFactory.SendNewsLetterSubscriptionActivationMessage(newsletter, _services.WorkContext.WorkingLanguage.Id); + + result = true; + } + } + } + return result; + } + + public virtual NewsLetterSubscription GetNewsLetterSubscriptionById(int newsLetterSubscriptionId) { if (newsLetterSubscriptionId == 0) return null; @@ -166,11 +151,6 @@ public virtual NewsLetterSubscription GetNewsLetterSubscriptionById(int newsLett return queuedEmail; } - /// - /// Gets a newsletter subscription by newsletter subscription GUID - /// - /// The newsletter subscription GUID - /// NewsLetter subscription public virtual NewsLetterSubscription GetNewsLetterSubscriptionByGuid(Guid newsLetterSubscriptionGuid) { if (newsLetterSubscriptionGuid == Guid.Empty) return null; @@ -183,100 +163,101 @@ orderby nls.Id return newsLetterSubscriptions.FirstOrDefault(); } - /// - /// Gets a newsletter subscription by email - /// - /// The newsletter subscription email - /// The store identifier - /// NewsLetter subscription public virtual NewsLetterSubscription GetNewsLetterSubscriptionByEmail(string email, int storeId = 0) { - if (!email.IsEmail()) - return null; + if (!email.IsEmail()) + return null; email = email.Trim(); - var query = _subscriptionRepository.Table - .Where(x => x.Email == email); + var query = _subscriptionRepository.Table + .Where(x => x.Email == email); - if (storeId > 0) - query = query.Where(x => x.StoreId == storeId); + if (storeId > 0) + query = query.Where(x => x.StoreId == storeId); - var subscription = query.OrderBy(x => x.Id).FirstOrDefault(); + var subscription = query.OrderBy(x => x.Id).FirstOrDefault(); - return subscription; + return subscription; } - /// - /// Gets the newsletter subscription list - /// - /// Email to search or string. Empty to load all records. - /// Page index - /// Page size - /// A value indicating whether the not active subscriptions should be loaded - /// The store identifier - /// NewsLetterSubscription entity list - public virtual IPagedList GetAllNewsLetterSubscriptions(string email, int pageIndex, int pageSize, bool showHidden = false, int storeId = 0) + public virtual IPagedList GetAllNewsLetterSubscriptions( + string email, + int pageIndex, + int pageSize, + bool showHidden = false, + int[] storeIds = null, + int[] customerRolesIds = null) { - var query = _subscriptionRepository.Table; - - if (!String.IsNullOrEmpty(email)) - { - query = query.Where(nls => nls.Email.Contains(email)); - } - - if (!showHidden) + var customerQuery = _customerRepository.Table.Where(x => !x.Deleted); + + // Note, changing the shape makes eager loading for customer entity impossible here. + var query = + from ns in _subscriptionRepository.Table + join c in customerQuery on ns.Email equals c.Email into customers + from c in customers.DefaultIfEmpty() + select new NewsletterSubscriber + { + Subscription = ns, + Customer = c + }; + + if (email.HasValue()) { - query = query.Where(nls => nls.Active); + query = query.Where(x => x.Subscription.Email.Contains(email)); } - if (storeId > 0) - { - query = query.Where(x => x.StoreId == storeId); - } + if (!showHidden) + { + query = query.Where(x => x.Subscription.Active); + } - query = query.OrderBy(nls => nls.Email).ThenBy(x => x.StoreId); + if (storeIds?.Any() ?? false) + { + query = query.Where(x => storeIds.Contains(x.Subscription.StoreId)); + } - var newsletterSubscriptions = new PagedList(query, pageIndex, pageSize); - return newsletterSubscriptions; + if (customerRolesIds?.Any() ?? false) + { + query = query.Where(x => x.Customer.CustomerRoleMappings + .Where(rm => rm.CustomerRole.Active) + .Select(rm => rm.CustomerRoleId) + .Intersect(customerRolesIds).Count() > 0); + } + + query = query + .OrderBy(x => x.Subscription.Email) + .ThenBy(x => x.Subscription.StoreId); + + var result = new PagedList(query, pageIndex, pageSize); + return result; } - /// - /// Publishes the subscription event. - /// - /// The email. - /// if set to true [is subscribe]. - /// if set to true [publish subscription events]. private void PublishSubscriptionEvent(string email, bool isSubscribe, bool publishSubscriptionEvents) { if (publishSubscriptionEvents) { - if (isSubscribe) + if (isSubscribe) { - _services.EventPublisher.Publish(new EmailSubscribedEvent(email)); - } + _services.EventPublisher.Publish(new EmailSubscribedEvent(email)); + } else { - _services.EventPublisher.Publish(new EmailUnsubscribedEvent(email)); - } + _services.EventPublisher.Publish(new EmailUnsubscribedEvent(email)); + } } } - /// - /// Ensures the subscriber email or throw. - /// - /// The email. - /// - private static string EnsureSubscriberEmailOrThrow(string email) - { - string output = email.EmptyNull().Trim().Truncate(255); - - if (!output.IsEmail()) - { - throw Error.ArgumentOutOfRange("email", "Email is not valid", email); - } - - return output; - } + private static string EnsureSubscriberEmailOrThrow(string email) + { + string output = email.EmptyNull().Trim().Truncate(255); + + if (!output.IsEmail()) + { + throw Error.ArgumentOutOfRange("email", "Email is not valid", email); + } + + return output; + } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Messages/NewsletterSubscriber.cs b/src/Libraries/SmartStore.Services/Messages/NewsletterSubscriber.cs new file mode 100644 index 0000000000..e7c2f01691 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Messages/NewsletterSubscriber.cs @@ -0,0 +1,21 @@ +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.Messages; + +namespace SmartStore.Services.Messages +{ + /// + /// Represents a newsletter subscriber and associated customer. + /// + public class NewsletterSubscriber + { + /// + /// Newsletter subscription. + /// + public NewsLetterSubscription Subscription { get; set; } + + /// + /// The customer associated with the newsletter subscription. Can be null. + /// + public Customer Customer { get; set; } + } +} diff --git a/src/Libraries/SmartStore.Services/Messages/QueuedEmailService.cs b/src/Libraries/SmartStore.Services/Messages/QueuedEmailService.cs index 00ee3e397e..8e0a9846fc 100644 --- a/src/Libraries/SmartStore.Services/Messages/QueuedEmailService.cs +++ b/src/Libraries/SmartStore.Services/Messages/QueuedEmailService.cs @@ -7,6 +7,7 @@ using System.Web; using SmartStore.Core; using SmartStore.Core.Data; +using SmartStore.Core.Domain.Media; using SmartStore.Core.Domain.Messages; using SmartStore.Core.Email; using SmartStore.Core.Events; @@ -19,102 +20,55 @@ namespace SmartStore.Services.Messages { - public partial class QueuedEmailService : IQueuedEmailService + public partial class QueuedEmailService : IQueuedEmailService { private readonly IRepository _queuedEmailRepository; - private readonly IRepository _queuedEmailAttachmentRepository; - private readonly IEmailSender _emailSender; - private readonly ICommonServices _services; - private readonly IDownloadService _downloadService; - private readonly IProviderManager _providerManager; - private readonly Provider _storageProvider; - - public QueuedEmailService( - IRepository queuedEmailRepository, - IRepository queuedEmailAttachmentRepository, - IEmailSender emailSender, - ICommonServices services, - IDownloadService downloadService, - IProviderManager providerManager) + private readonly IRepository _queuedEmailAttachmentRepository; + private readonly IEmailSender _emailSender; + private readonly ICommonServices _services; + + public QueuedEmailService( + IRepository queuedEmailRepository, + IRepository queuedEmailAttachmentRepository, + IEmailSender emailSender, + ICommonServices services) { _queuedEmailRepository = queuedEmailRepository; - _queuedEmailAttachmentRepository = queuedEmailAttachmentRepository; - _emailSender = emailSender; - _services = services; - _downloadService = downloadService; - _providerManager = providerManager; - - T = NullLocalizer.Instance; - Logger = NullLogger.Instance; + _queuedEmailAttachmentRepository = queuedEmailAttachmentRepository; + _emailSender = emailSender; + _services = services; + } - var systemName = services.Settings.GetSettingByKey("Media.Storage.Provider", DatabaseMediaStorageProvider.SystemName); + public Localizer T { get; set; } = NullLocalizer.Instance; + public ILogger Logger { get; set; } = NullLogger.Instance; - _storageProvider = providerManager.GetProvider(systemName); - } - public Localizer T { get; set; } - public ILogger Logger { get; set; } - public virtual void InsertQueuedEmail(QueuedEmail queuedEmail) { - Guard.NotNull(queuedEmail, nameof(queuedEmail)); - - _queuedEmailRepository.Insert(queuedEmail); - - // blob data always stored in database at this point -> move it if current provider is not database provider - if (!_storageProvider.Metadata.SystemName.IsCaseInsensitiveEqual(DatabaseMediaStorageProvider.SystemName)) - { - var blobs = queuedEmail.Attachments.Where(x => x.StorageLocation == EmailAttachmentStorageLocation.Blob && x.MediaStorage != null); - if (blobs.Any()) - { - var databaseProvider = _providerManager.GetProvider(DatabaseMediaStorageProvider.SystemName); - - foreach (var blob in blobs) - { - var media = blob.ToMedia(); - - try - { - // move it to current storage provider - _storageProvider.Value.Save(media, blob.MediaStorage.Data); - } - catch (Exception exception) - { - Logger.Error(exception, T("Admin.Media.ProviderFailedToSave", _storageProvider.Metadata.SystemName, "QueuedEmailService.InsertQueuedEmail")); - } - finally - { - try - { - // always remove data from database to avoid inconsistent records - databaseProvider.Value.Remove(media); - } - catch { } - } - } - } - } + Guard.NotNull(queuedEmail, nameof(queuedEmail)); + + _queuedEmailRepository.Insert(queuedEmail); } public virtual void UpdateQueuedEmail(QueuedEmail queuedEmail) { - Guard.NotNull(queuedEmail, nameof(queuedEmail)); + Guard.NotNull(queuedEmail, nameof(queuedEmail)); - _queuedEmailRepository.Update(queuedEmail); + _queuedEmailRepository.Update(queuedEmail); } public virtual void DeleteQueuedEmail(QueuedEmail queuedEmail) { - Guard.NotNull(queuedEmail, nameof(queuedEmail)); + Guard.NotNull(queuedEmail, nameof(queuedEmail)); _queuedEmailRepository.Delete(queuedEmail); } - public virtual int DeleteAllQueuedEmails() - { - // do not delete e-mails which are about to be sent - return _queuedEmailRepository.DeleteAll(x => x.SentOnUtc.HasValue || x.SentTries >= 3); - } + public virtual int DeleteAllQueuedEmails() + { + // do not delete e-mails which are about to be sent + return _queuedEmailRepository.DeleteAll(x => x.SentOnUtc.HasValue || x.SentTries >= 3); + } public virtual QueuedEmail GetQueuedEmailById(int queuedEmailId) { @@ -136,29 +90,29 @@ where queuedEmailIds.Contains(qe.Id) var queuedEmails = query.ToList(); - // sort by passed identifier sequence - return queuedEmails.OrderBySequence(queuedEmailIds).ToList(); - } + // sort by passed identifier sequence + return queuedEmails.OrderBySequence(queuedEmailIds).ToList(); + } public virtual IPagedList SearchEmails(SearchEmailsQuery query) { - Guard.NotNull(query, nameof(query)); - + Guard.NotNull(query, nameof(query)); + var q = _queuedEmailRepository.Table; - if (query.Expand.HasValue()) - { - var expands = query.Expand.Split(','); - foreach (var expand in expands) - { - q = q.Expand(expand.Trim()); - } - } + if (query.Expand.HasValue()) + { + var expands = query.Expand.Split(','); + foreach (var expand in expands) + { + q = q.Expand(expand.Trim()); + } + } if (query.From.HasValue()) - q = q.Where(qe => qe.From.Contains(query.From.Trim())); + q = q.Where(qe => qe.From.Contains(query.From.Trim())); - if (query.To.HasValue()) + if (query.To.HasValue()) q = q.Where(qe => qe.To.Contains(query.To.Trim())); if (query.StartTime.HasValue) @@ -170,14 +124,14 @@ public virtual IPagedList SearchEmails(SearchEmailsQuery query) if (query.UnsentOnly) q = q.Where(qe => !qe.SentOnUtc.HasValue); - if (query.SendManually.HasValue) - q = q.Where(qe => qe.SendManually == query.SendManually.Value); + if (query.SendManually.HasValue) + q = q.Where(qe => qe.SendManually == query.SendManually.Value); q = q.Where(qe => qe.SentTries < query.MaxSendTries); - - q = q.OrderByDescending(qe => qe.Priority); - q = query.OrderByLatest ? + q = q.OrderByDescending(qe => qe.Priority); + + q = query.OrderByLatest ? ((IOrderedQueryable)q).ThenByDescending(qe => qe.CreatedOnUtc) : ((IOrderedQueryable)q).ThenBy(qe => qe.CreatedOnUtc); @@ -185,168 +139,164 @@ public virtual IPagedList SearchEmails(SearchEmailsQuery query) return queuedEmails; } - public virtual async Task SendEmailAsync(QueuedEmail queuedEmail) - { - var result = false; - - try - { - var smtpContext = new SmtpContext(queuedEmail.EmailAccount); - var msg = ConvertEmail(queuedEmail); - - await _emailSender.SendEmailAsync(smtpContext, msg); - - queuedEmail.SentOnUtc = DateTime.UtcNow; - result = true; - } - catch (Exception ex) - { - Logger.Error(ex, string.Concat(T("Admin.Common.ErrorSendingEmail"), ": ", ex.Message)); - } - finally - { - queuedEmail.SentTries = queuedEmail.SentTries + 1; - UpdateQueuedEmail(queuedEmail); - } - - return result; - } - - private void AddEmailAddresses(string addresses, ICollection target) - { - var arr = addresses.IsEmpty() ? null : addresses.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); - if (arr != null && arr.Length > 0) - { - target.AddRange(arr.Where(x => x.Trim().HasValue()).Select(x => new EmailAddress(x))); - } - } - - internal EmailMessage ConvertEmail(QueuedEmail qe) - { - // 'internal' for testing purposes - - var msg = new EmailMessage( - new EmailAddress(qe.To), - qe.Subject.Replace("\r\n", string.Empty), - qe.Body, - new EmailAddress(qe.From)); - - if (qe.ReplyTo.HasValue()) - { - msg.ReplyTo.Add(new EmailAddress(qe.ReplyTo)); - } - - AddEmailAddresses(qe.CC, msg.Cc); - AddEmailAddresses(qe.Bcc, msg.Bcc); - - if (qe.Attachments != null && qe.Attachments.Count > 0) - { - foreach (var qea in qe.Attachments) - { - Attachment attachment = null; - - if (qea.StorageLocation == EmailAttachmentStorageLocation.Blob) - { - var data = _storageProvider.Value.Load(qea.ToMedia()); - - if (data != null && data.LongLength > 0) - { - attachment = new Attachment(data.ToStream(), qea.Name, qea.MimeType); - } - } - else if (qea.StorageLocation == EmailAttachmentStorageLocation.Path) - { - var path = qea.Path; - if (path.HasValue()) - { - if (path[0] == '~' || path[0] == '/') - { - path = CommonHelper.MapPath(VirtualPathUtility.ToAppRelative(path), false); - } - if (File.Exists(path)) - { - attachment = new Attachment(path, qea.MimeType); - attachment.Name = qea.Name; - } - } - } - else if (qea.StorageLocation == EmailAttachmentStorageLocation.FileReference) - { - var file = qea.File; - if (file != null && file.UseDownloadUrl == false) - { - var data = _downloadService.LoadDownloadBinary(file); - - attachment = new Attachment(data.ToStream(), file.Filename + file.Extension, file.ContentType); - } - } - - if (attachment != null) - { - msg.Attachments.Add(attachment); - } - } - } - - return msg; - } - - #region Attachments - - public virtual QueuedEmailAttachment GetQueuedEmailAttachmentById(int id) - { - if (id == 0) - return null; - - var qea = _queuedEmailAttachmentRepository.GetById(id); - return qea; - } - - public virtual void DeleteQueuedEmailAttachment(QueuedEmailAttachment attachment) - { - Guard.NotNull(attachment, nameof(attachment)); - - // delete from storage - if (attachment.StorageLocation == EmailAttachmentStorageLocation.Blob) - { - _storageProvider.Value.Remove(attachment.ToMedia()); - } - - _queuedEmailAttachmentRepository.Delete(attachment); - } - - public virtual byte[] LoadQueuedEmailAttachmentBinary(QueuedEmailAttachment attachment) - { - Guard.NotNull(attachment, nameof(attachment)); - - if (attachment.StorageLocation == EmailAttachmentStorageLocation.Blob) - { - return _storageProvider.Value.Load(attachment.ToMedia()); - } - - return null; - } - - #endregion - } - - public class SearchEmailsQuery - { - public string From { get; set; } - public string To { get; set; } - public DateTime? StartTime { get; set; } - public DateTime? EndTime { get; set; } - public bool UnsentOnly { get; set; } - public int MaxSendTries { get; set; } - public bool OrderByLatest { get; set; } - public int PageIndex { get; set; } - public int PageSize { get; set; } - public bool? SendManually { get; set; } - - /// - /// Navigation properties to eager load (comma separataed) - /// - public string Expand { get; set; } - } + public virtual async Task SendEmailAsync(QueuedEmail queuedEmail) + { + var result = false; + + try + { + var smtpContext = new SmtpContext(queuedEmail.EmailAccount); + var msg = ConvertEmail(queuedEmail); + + await _emailSender.SendEmailAsync(smtpContext, msg); + + queuedEmail.SentOnUtc = DateTime.UtcNow; + result = true; + } + catch (Exception ex) + { + Logger.Error(ex, string.Concat(T("Admin.Common.ErrorSendingEmail"), ": ", ex.Message)); + } + finally + { + queuedEmail.SentTries += 1; + UpdateQueuedEmail(queuedEmail); + } + + return result; + } + + private void AddEmailAddresses(string addresses, ICollection target) + { + var arr = addresses.IsEmpty() ? null : addresses.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + if (arr != null && arr.Length > 0) + { + target.AddRange(arr.Where(x => x.Trim().HasValue()).Select(x => new EmailAddress(x))); + } + } + + internal EmailMessage ConvertEmail(QueuedEmail qe) + { + // 'internal' for testing purposes + + var msg = new EmailMessage( + new EmailAddress(qe.To), + qe.Subject.Replace("\r\n", string.Empty), + qe.Body, + new EmailAddress(qe.From)); + + if (qe.ReplyTo.HasValue()) + { + msg.ReplyTo.Add(new EmailAddress(qe.ReplyTo)); + } + + AddEmailAddresses(qe.CC, msg.Cc); + AddEmailAddresses(qe.Bcc, msg.Bcc); + + if (qe.Attachments != null && qe.Attachments.Count > 0) + { + foreach (var qea in qe.Attachments) + { + Attachment attachment = null; + + if (qea.StorageLocation == EmailAttachmentStorageLocation.Blob) + { + var data = qea.MediaStorage?.Data; + + if (data != null && data.LongLength > 0) + { + attachment = new Attachment(data.ToStream(), qea.Name, qea.MimeType); + } + } + else if (qea.StorageLocation == EmailAttachmentStorageLocation.Path) + { + var path = qea.Path; + if (path.HasValue()) + { + if (path[0] == '~' || path[0] == '/') + { + path = CommonHelper.MapPath(VirtualPathUtility.ToAppRelative(path), false); + } + if (File.Exists(path)) + { + attachment = new Attachment(path, qea.MimeType) { Name = qea.Name }; + } + } + } + else if (qea.StorageLocation == EmailAttachmentStorageLocation.FileReference) + { + var file = qea.MediaFile; + if (file != null) + { + var mediaFile = _services.MediaService.ConvertMediaFile(file); + var stream = mediaFile.OpenRead(); + if (stream != null) + { + attachment = new Attachment(stream, file.Name, file.MimeType); + } + } + } + + if (attachment != null) + { + msg.Attachments.Add(attachment); + } + } + } + + return msg; + } + + #region Attachments + + public virtual QueuedEmailAttachment GetQueuedEmailAttachmentById(int id) + { + if (id == 0) + return null; + + var qea = _queuedEmailAttachmentRepository.GetById(id); + return qea; + } + + public virtual void DeleteQueuedEmailAttachment(QueuedEmailAttachment attachment) + { + Guard.NotNull(attachment, nameof(attachment)); + + _queuedEmailAttachmentRepository.Delete(attachment); + } + + public virtual byte[] LoadQueuedEmailAttachmentBinary(QueuedEmailAttachment attachment) + { + Guard.NotNull(attachment, nameof(attachment)); + + if (attachment.StorageLocation == EmailAttachmentStorageLocation.Blob) + { + return attachment.MediaStorage?.Data ?? new byte[0]; + } + + return null; + } + + #endregion + } + + public class SearchEmailsQuery + { + public string From { get; set; } + public string To { get; set; } + public DateTime? StartTime { get; set; } + public DateTime? EndTime { get; set; } + public bool UnsentOnly { get; set; } + public int MaxSendTries { get; set; } + public bool OrderByLatest { get; set; } + public int PageIndex { get; set; } + public int PageSize { get; set; } + public bool? SendManually { get; set; } + + /// + /// Navigation properties to eager load (comma separataed) + /// + public string Expand { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/Messages/QueuedMessagesClearTask.cs b/src/Libraries/SmartStore.Services/Messages/QueuedMessagesClearTask.cs index 1af83916e8..6c2bae3d97 100644 --- a/src/Libraries/SmartStore.Services/Messages/QueuedMessagesClearTask.cs +++ b/src/Libraries/SmartStore.Services/Messages/QueuedMessagesClearTask.cs @@ -19,16 +19,16 @@ public QueuedMessagesClearTask( IRepository qeRepository, CommonSettings commonSettings) { - _qeRepository = qeRepository; + _qeRepository = qeRepository; _commonSettings = commonSettings; } - public override async Task ExecuteAsync(TaskExecutionContext ctx) + public override async Task ExecuteAsync(TaskExecutionContext ctx) { - var olderThan = DateTime.UtcNow.AddDays(-Math.Abs(_commonSettings.MaxQueuedMessagesAgeInDays)); - await _qeRepository.DeleteAllAsync(x => x.SentOnUtc.HasValue && x.CreatedOnUtc < olderThan); + var olderThan = DateTime.UtcNow.AddDays(-Math.Abs(_commonSettings.MaxQueuedMessagesAgeInDays)); + await _qeRepository.DeleteAllAsync(x => x.SentOnUtc.HasValue && x.CreatedOnUtc < olderThan); - _qeRepository.Context.ShrinkDatabase(); + _qeRepository.Context.ShrinkDatabase(); } } } diff --git a/src/Libraries/SmartStore.Services/Messages/QueuedMessagesSendTask.cs b/src/Libraries/SmartStore.Services/Messages/QueuedMessagesSendTask.cs index abcd1c2315..eccd83e7ab 100644 --- a/src/Libraries/SmartStore.Services/Messages/QueuedMessagesSendTask.cs +++ b/src/Libraries/SmartStore.Services/Messages/QueuedMessagesSendTask.cs @@ -16,33 +16,33 @@ public QueuedMessagesSendTask(IQueuedEmailService queuedEmailService) _queuedEmailService = queuedEmailService; } - public override async Task ExecuteAsync(TaskExecutionContext ctx) + public override async Task ExecuteAsync(TaskExecutionContext ctx) { - const int pageSize = 1000; - const int maxTries = 3; + const int pageSize = 1000; + const int maxTries = 3; - for (int i = 0; i < 9999999; ++i) - { - var q = new SearchEmailsQuery - { - MaxSendTries = maxTries, - PageIndex = i, - PageSize = pageSize, - Expand = "Attachments", - UnsentOnly = true, - SendManually = false - }; + for (int i = 0; i < 9999999; ++i) + { + var q = new SearchEmailsQuery + { + MaxSendTries = maxTries, + PageIndex = i, + PageSize = pageSize, + Expand = "Attachments", + UnsentOnly = true, + SendManually = false + }; - var queuedEmails = await _queuedEmailService.SearchEmails(q).LoadAsync(); + var queuedEmails = await _queuedEmailService.SearchEmails(q).LoadAsync(); - foreach (var queuedEmail in queuedEmails) - { - await _queuedEmailService.SendEmailAsync(queuedEmail); - } + foreach (var queuedEmail in queuedEmails) + { + await _queuedEmailService.SendEmailAsync(queuedEmail); + } - if (!queuedEmails.HasNextPage) - break; - } + if (!queuedEmails.HasNextPage) + break; + } } } } diff --git a/src/Libraries/SmartStore.Services/Messages/TemplateModel.cs b/src/Libraries/SmartStore.Services/Messages/TemplateModel.cs index 61037c1e01..c7e92c361a 100644 --- a/src/Libraries/SmartStore.Services/Messages/TemplateModel.cs +++ b/src/Libraries/SmartStore.Services/Messages/TemplateModel.cs @@ -4,21 +4,21 @@ namespace SmartStore.Services.Messages { - public class TemplateModel : HybridExpando - { - public T GetFromBag(string key) - { - Guard.NotEmpty(key, nameof(key)); + public class TemplateModel : HybridExpando + { + public T GetFromBag(string key) + { + Guard.NotEmpty(key, nameof(key)); - if (base.Contains("Bag") && base["Bag"] is IDictionary bag) - { - if (bag.TryGetValue(key, out var value) && value is T result) - { - return result; - } - } + if (base.Contains("Bag") && base["Bag"] is IDictionary bag) + { + if (bag.TryGetValue(key, out var value) && value is T result) + { + return result; + } + } - return default(T); - } - } + return default(T); + } + } } diff --git a/src/Libraries/SmartStore.Services/News/INewsService.cs b/src/Libraries/SmartStore.Services/News/INewsService.cs index d25a8d8afa..76652ed2ce 100644 --- a/src/Libraries/SmartStore.Services/News/INewsService.cs +++ b/src/Libraries/SmartStore.Services/News/INewsService.cs @@ -6,58 +6,72 @@ namespace SmartStore.Services.News { /// - /// News service interface + /// News service interface. /// public partial interface INewsService { /// - /// Deletes a news + /// Inserts a news item. /// - /// News item - void DeleteNews(NewsItem newsItem); + /// News item. + void InsertNews(NewsItem news); /// - /// Gets a news + /// Updates the news item. /// - /// The news identifier - /// News - NewsItem GetNewsById(int newsId); + /// News item. + void UpdateNews(NewsItem news); - /// - /// Get news by identifiers - /// - /// News identifiers - /// News query - IQueryable GetNewsByIds(int[] newsIds); + /// + /// Deletes a news. + /// + /// News item. + void DeleteNews(NewsItem news); /// - /// Gets all news + /// Update news item comment totals. /// - /// Language identifier; 0 if you want to get all records - /// Store identifier; 0 if you want to get all records - /// Page index - /// Page size - /// A value indicating whether to show hidden records - /// The maximum age of returned news - /// News items - IPagedList GetAllNews(int languageId, int storeId, int pageIndex, int pageSize, bool showHidden = false, DateTime? maxAge = null); + /// News item. + void UpdateCommentTotals(NewsItem news); /// - /// Inserts a news item + /// Gets a news. /// - /// News item - void InsertNews(NewsItem news); + /// The news identifier. + /// News item. + NewsItem GetNewsById(int newsId); /// - /// Updates the news item + /// Get news by identifiers. /// - /// News item - void UpdateNews(NewsItem news); + /// News identifiers. + /// News query. + IQueryable GetNewsByIds(int[] newsIds); /// - /// Update news item comment totals + /// Gets all news. /// - /// News item - void UpdateCommentTotals(NewsItem newsItem); + /// Store identifier. 0 to get all news items. + /// Page index. + /// Page size. + /// Filter by a language identifier. 0 to get all news items. + /// A value indicating whether to show hidden records. + /// The maximum age of returned news. + /// Search for a term or phrase in the title. + /// Search for a term or phrase in the intro. + /// Search for a term or phrase in the full description. + /// Indicates whether to load entities tracked or untracked. + /// News items. + IPagedList GetAllNews( + int storeId, + int pageIndex, + int pageSize, + int languageId = 0, + bool showHidden = false, + DateTime? maxAge = null, + string title = "", + string intro = "", + string full = "", + bool untracked = true); } } diff --git a/src/Libraries/SmartStore.Services/News/NewsMessageFactoryExtensions.cs b/src/Libraries/SmartStore.Services/News/NewsMessageFactoryExtensions.cs index be7b2f8284..5138eddb03 100644 --- a/src/Libraries/SmartStore.Services/News/NewsMessageFactoryExtensions.cs +++ b/src/Libraries/SmartStore.Services/News/NewsMessageFactoryExtensions.cs @@ -5,15 +5,15 @@ namespace SmartStore.Services.News { - public static class NewsMessageFactoryExtensions - { - /// - /// Sends a news comment notification message to a store owner - /// - public static CreateMessageResult SendNewsCommentNotificationMessage(this IMessageFactory factory, NewsComment newsComment, int languageId = 0) - { - Guard.NotNull(newsComment, nameof(newsComment)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewsCommentStoreOwner, languageId, customer: newsComment.Customer), true, newsComment); - } - } + public static class NewsMessageFactoryExtensions + { + /// + /// Sends a news comment notification message to a store owner + /// + public static CreateMessageResult SendNewsCommentNotificationMessage(this IMessageFactory factory, NewsComment newsComment, int languageId = 0) + { + Guard.NotNull(newsComment, nameof(newsComment)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewsCommentStoreOwner, languageId, customer: newsComment.Customer), true, newsComment); + } + } } diff --git a/src/Libraries/SmartStore.Services/News/NewsService.cs b/src/Libraries/SmartStore.Services/News/NewsService.cs index c7c041db1a..f4c50f8865 100644 --- a/src/Libraries/SmartStore.Services/News/NewsService.cs +++ b/src/Libraries/SmartStore.Services/News/NewsService.cs @@ -1,115 +1,137 @@ using System; +using System.Collections.Generic; using System.Linq; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.News; +using SmartStore.Core.Domain.Seo; using SmartStore.Core.Domain.Stores; -using SmartStore.Core.Events; +using SmartStore.Services.Seo; namespace SmartStore.Services.News { - /// - /// News service - /// - public partial class NewsService : INewsService + public partial class NewsService : INewsService, IXmlSitemapPublisher { - #region Fields - private readonly IRepository _newsItemRepository; - private readonly IRepository _storeMappingRepository; - private readonly ICommonServices _services; - - private readonly NewsSettings _newsSettings; + private readonly IRepository _storeMappingRepository; - #endregion + public NewsService( + IRepository newsItemRepository, + IRepository storeMappingRepository) + { + _newsItemRepository = newsItemRepository; + _storeMappingRepository = storeMappingRepository; + } - #region Ctor + public DbQuerySettings QuerySettings { get; set; } = DbQuerySettings.Default; - public NewsService(IRepository newsItemRepository, - IRepository storeMappingRepository, - ICommonServices services, - NewsSettings newsSettings) + public virtual void InsertNews(NewsItem news) { - _newsItemRepository = newsItemRepository; - _storeMappingRepository = storeMappingRepository; - _services = services; - _newsSettings = newsSettings; + Guard.NotNull(news, nameof(news)); - this.QuerySettings = DbQuerySettings.Default; - } + _newsItemRepository.Insert(news); + } - public DbQuerySettings QuerySettings { get; set; } + public virtual void UpdateNews(NewsItem news) + { + Guard.NotNull(news, nameof(news)); - #endregion + _newsItemRepository.Update(news); + } - #region Methods + public virtual void DeleteNews(NewsItem news) + { + if (news != null) + { + _newsItemRepository.Delete(news); + } + } - /// - /// Deletes a news - /// - /// News item - public virtual void DeleteNews(NewsItem newsItem) + public virtual void UpdateCommentTotals(NewsItem news) { - if (newsItem == null) - throw new ArgumentNullException("newsItem"); + Guard.NotNull(news, nameof(news)); + + var approvedCommentCount = 0; + var notApprovedCommentCount = 0; + var newsComments = news.NewsComments; - _newsItemRepository.Delete(newsItem); + foreach (var nc in newsComments) + { + if (nc.IsApproved) + { + approvedCommentCount++; + } + else + { + notApprovedCommentCount++; + } + } + + news.ApprovedCommentCount = approvedCommentCount; + news.NotApprovedCommentCount = notApprovedCommentCount; + UpdateNews(news); } - /// - /// Gets a news - /// - /// The news identifier - /// News public virtual NewsItem GetNewsById(int newsId) { if (newsId == 0) + { return null; + } return _newsItemRepository.GetById(newsId); } - /// - /// Get news by identifiers - /// - /// News identifiers - /// News query - public virtual IQueryable GetNewsByIds(int[] newsIds) - { - if (newsIds == null || newsIds.Length == 0) - return null; - - var query = - from x in _newsItemRepository.Table - where newsIds.Contains(x.Id) - select x; - - return query; - } - - /// - /// Gets all news - /// - /// Language identifier; 0 if you want to get all records - /// Store identifier; 0 if you want to get all records - /// Page index - /// Page size - /// A value indicating whether to show hidden records - /// The maximum age of returned news - /// News items - public virtual IPagedList GetAllNews(int languageId, int storeId, int pageIndex, int pageSize, bool showHidden = false, DateTime? maxAge = null) + public virtual IQueryable GetNewsByIds(int[] newsIds) + { + if (newsIds == null || newsIds.Length == 0) + { + return null; + } + + var query = + from x in _newsItemRepository.Table + where newsIds.Contains(x.Id) + select x; + + return query; + } + + public virtual IPagedList GetAllNews( + int storeId, + int pageIndex, + int pageSize, + int languageId = 0, + bool showHidden = false, + DateTime? maxAge = null, + string title = "", + string intro = "", + string full = "", + bool untracked = true) { - var query = _newsItemRepository.Table; + var query = untracked ? _newsItemRepository.TableUntracked : _newsItemRepository.Table; - if (languageId > 0) - { - query = query.Where(n => languageId == n.LanguageId); - } + if (maxAge.HasValue) + { + query = query.Where(n => n.CreatedOnUtc >= maxAge.Value); + } + if (title.HasValue()) + { + query = query.Where(n => n.Title.Contains(title)); + } + if (intro.HasValue()) + { + query = query.Where(n => n.Short.Contains(intro)); + } + if (full.HasValue()) + { + query = query.Where(n => n.Full.Contains(full)); + } - if (maxAge.HasValue) - { - query = query.Where(n => n.CreatedOnUtc >= maxAge.Value); - } + if (languageId != 0) + { + query = query.Where(n => !n.LanguageId.HasValue || n.LanguageId == languageId); + } if (!showHidden) { @@ -119,78 +141,63 @@ public virtual IPagedList GetAllNews(int languageId, int storeId, int query = query.Where(n => !n.EndDateUtc.HasValue || n.EndDateUtc >= utcNow); } - query = query.OrderByDescending(n => n.CreatedOnUtc); - - //Store mapping - if (storeId > 0 && !QuerySettings.IgnoreMultiStore) - { - query = from n in query - join sm in _storeMappingRepository.Table - on new { c1 = n.Id, c2 = "NewsItem" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into n_sm - from sm in n_sm.DefaultIfEmpty() - where !n.LimitedToStores || storeId == sm.StoreId - select n; + query = query.OrderByDescending(n => n.CreatedOnUtc); - //only distinct items (group by ID) - query = from n in query - group n by n.Id into nGroup - orderby nGroup.Key - select nGroup.FirstOrDefault(); - - query = query.OrderByDescending(n => n.CreatedOnUtc); - } + if (storeId > 0 && !QuerySettings.IgnoreMultiStore) + { + query = from n in query + join sm in _storeMappingRepository.Table + on new { c1 = n.Id, c2 = "NewsItem" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into n_sm + from sm in n_sm.DefaultIfEmpty() + where !n.LimitedToStores || storeId == sm.StoreId + select n; + + // Only distinct items (group by ID). + query = from n in query + group n by n.Id into nGroup + orderby nGroup.Key + select nGroup.FirstOrDefault(); + + query = query.OrderByDescending(n => n.CreatedOnUtc); + } var news = new PagedList(query, pageIndex, pageSize); return news; } - /// - /// Inserts a news item - /// - /// News item - public virtual void InsertNews(NewsItem news) + #region XML Sitemap + + public XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context) { - if (news == null) - throw new ArgumentNullException("news"); + if (!context.LoadSetting().XmlSitemapIncludesNews || !context.LoadSetting().Enabled) + { + return null; + } - _newsItemRepository.Insert(news); + var query = GetAllNews(context.RequestStoreId, 0, int.MaxValue).SourceQuery; + return new NewsXmlSitemapResult { Query = query }; } - /// - /// Updates the news item - /// - /// News item - public virtual void UpdateNews(NewsItem news) + class NewsXmlSitemapResult : XmlSitemapProvider { - if (news == null) - throw new ArgumentNullException("news"); + public IQueryable Query { get; set; } - _newsItemRepository.Update(news); - } - - /// - /// Update news item comment totals - /// - /// News item - public virtual void UpdateCommentTotals(NewsItem newsItem) - { - if (newsItem == null) - throw new ArgumentNullException("newsItem"); + public override int GetTotalCount() + { + return Query.Count(); + } - int approvedCommentCount = 0; - int notApprovedCommentCount = 0; - var newsComments = newsItem.NewsComments; - foreach (var nc in newsComments) + public override IEnumerable Enlist() { - if (nc.IsApproved) - approvedCommentCount++; - else - notApprovedCommentCount++; + var newsItems = Query.Select(x => new { x.Id, x.CreatedOnUtc, x.LanguageId }).ToList(); + + foreach (var x in newsItems) + { + yield return new NamedEntity { EntityName = "NewsItem", Id = x.Id, LastMod = x.CreatedOnUtc, LanguageId = x.LanguageId }; + } } - newsItem.ApprovedCommentCount = approvedCommentCount; - newsItem.NotApprovedCommentCount = notApprovedCommentCount; - UpdateNews(newsItem); + public override int Order => 100; } #endregion diff --git a/src/Libraries/SmartStore.Services/Orders/AddToCartContext.cs b/src/Libraries/SmartStore.Services/Orders/AddToCartContext.cs index e747b73fa3..e409494c69 100644 --- a/src/Libraries/SmartStore.Services/Orders/AddToCartContext.cs +++ b/src/Libraries/SmartStore.Services/Orders/AddToCartContext.cs @@ -6,37 +6,31 @@ namespace SmartStore.Services.Orders { - public class AddToCartContext - { - public AddToCartContext() - { - Warnings = new List(); - CustomerEnteredPrice = decimal.Zero; - ChildItems = new List(); - } + public class AddToCartContext + { + public AddToCartContext() + { + Warnings = new List(); + CustomerEnteredPrice = decimal.Zero; + ChildItems = new List(); + } - public List Warnings { get; set; } + public List Warnings { get; set; } - public ShoppingCartItem Item { get; set; } - public List ChildItems { get; set; } - public ProductBundleItem BundleItem { get; set; } + public ShoppingCartItem Item { get; set; } + public List ChildItems { get; set; } + public ProductBundleItem BundleItem { get; set; } - public Customer Customer { get; set; } - public Product Product { get; set; } - public ShoppingCartType CartType { get; set; } - public ProductVariantQuery VariantQuery { get; set; } - public string AttributesXml { get; set; } - public decimal CustomerEnteredPrice { get; set; } - public int Quantity { get; set; } - public bool AddRequiredProducts { get; set; } - public int? StoreId { get; set; } + public Customer Customer { get; set; } + public Product Product { get; set; } + public ShoppingCartType CartType { get; set; } + public ProductVariantQuery VariantQuery { get; set; } + public string AttributesXml { get; set; } + public decimal CustomerEnteredPrice { get; set; } + public int Quantity { get; set; } + public bool AddRequiredProducts { get; set; } + public int? StoreId { get; set; } - public int BundleItemId - { - get - { - return (BundleItem == null ? 0 : BundleItem.Id); - } - } - } + public int BundleItemId => (BundleItem == null ? 0 : BundleItem.Id); + } } diff --git a/src/Libraries/SmartStore.Services/Orders/AutoUpdateOrderItemContext.cs b/src/Libraries/SmartStore.Services/Orders/AutoUpdateOrderItemContext.cs index 38528c835c..007306cb6c 100644 --- a/src/Libraries/SmartStore.Services/Orders/AutoUpdateOrderItemContext.cs +++ b/src/Libraries/SmartStore.Services/Orders/AutoUpdateOrderItemContext.cs @@ -4,101 +4,108 @@ namespace SmartStore.Services.Orders { - public class AutoUpdateOrderItemContext - { - /// - /// [IN] Whether order item is new - /// - public bool IsNewOrderItem { get; set; } - - /// - /// [IN] Order item - /// - public OrderItem OrderItem { get; set; } - - /// - /// [IN] Whether to adjust the inventory - /// - public bool AdjustInventory { get; set; } - - /// - /// [IN] Whether to update order totals if order is in pending state - /// - public bool UpdateTotals { get; set; } - - /// - /// [IN] Whether to update reward points - /// - public bool UpdateRewardPoints { get; set; } - - /// - /// [IN] Quantity old - /// - public int QuantityOld { get; set; } - - /// - /// [IN] Quantity new - /// - public int QuantityNew { get; set; } - - /// - /// [OUT] Inventory changes - /// - public AdjustInventoryResult Inventory { get; set; } - - /// - /// [OUT] Reward points old - /// - public int RewardPointsOld { get; set; } - - /// - /// [OUT] Reward points new - /// - public int RewardPointsNew { get; set; } - - public static string InfoKey { get { return "UpdateOrderItemContextInfo"; } } - - /// - /// The value to which the quantity amount has changed - /// - public int QuantityDelta - { - get - { - return QuantityNew - QuantityOld; - } - } - - public decimal QuantityChangeFactor - { - get - { - if (QuantityOld != 0) - return (decimal)QuantityNew / (decimal)QuantityOld; - - return 1.0M; - } - } - - public string ToString(ILocalizationService localizationService) - { - if (Inventory == null && RewardPointsOld == 0 && RewardPointsNew == 0) - return ""; - - string stockOld = null; - string stockNew = null; - - if (Inventory != null && Inventory.HasClearStockQuantityResult) - { - stockOld = Inventory.StockQuantityOld.ToString(); - stockNew = Inventory.StockQuantityNew.ToString(); - } - - string result = localizationService.GetResource("Admin.Orders.OrderItem.Update.Info").FormatWith( - stockOld.NaIfEmpty(), stockNew.NaIfEmpty(), RewardPointsOld, RewardPointsNew - ); - - return result; - } - } + /// + /// Contains data that is used to recalculated details of an order. + /// + public class AutoUpdateOrderItemContext + { + /// + /// [IN] Whether order item is new + /// + public bool IsNewOrderItem { get; set; } + + /// + /// [IN] Order item + /// + public OrderItem OrderItem { get; set; } + + /// + /// [IN] Whether to adjust the inventory + /// + public bool AdjustInventory { get; set; } + + /// + /// [IN] Whether to update order totals if order is in pending state + /// + public bool UpdateTotals { get; set; } + + /// + /// [IN] Whether to update reward points + /// + public bool UpdateRewardPoints { get; set; } + + /// + /// [IN] Quantity old + /// + public int QuantityOld { get; set; } + + /// + /// [IN] Quantity new + /// + public int QuantityNew { get; set; } + + /// + /// [IN] Old price incl. tax. + /// + public decimal? PriceInclTaxOld { get; set; } + + /// + /// [IN] Old price excl. tax. + /// + public decimal? PriceExclTaxOld { get; set; } + + /// + /// [OUT] Inventory changes + /// + public AdjustInventoryResult Inventory { get; set; } + + /// + /// [OUT] Reward points old + /// + public int RewardPointsOld { get; set; } + + /// + /// [OUT] Reward points new + /// + public int RewardPointsNew { get; set; } + + public static string InfoKey => "UpdateOrderItemContextInfo"; + + /// + /// The value to which the quantity amount has changed + /// + public int QuantityDelta => QuantityNew - QuantityOld; + + public decimal QuantityChangeFactor + { + get + { + if (QuantityOld != 0) + return (decimal)QuantityNew / (decimal)QuantityOld; + + return 1.0M; + } + } + + public string ToString(ILocalizationService localizationService) + { + if (Inventory == null && RewardPointsOld == 0 && RewardPointsNew == 0) + return ""; + + string stockOld = null; + string stockNew = null; + + if (Inventory != null && Inventory.HasClearStockQuantityResult) + { + stockOld = Inventory.StockQuantityOld.ToString(); + stockNew = Inventory.StockQuantityNew.ToString(); + } + + string result = localizationService.GetResource("Admin.Orders.OrderItem.Update.Info").FormatWith( + stockOld.NaIfEmpty(), stockNew.NaIfEmpty(), RewardPointsOld, RewardPointsNew + ); + + return result; + } + } } diff --git a/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeExtensions.cs b/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeExtensions.cs index ceda7c8b9d..1529de1509 100644 --- a/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeExtensions.cs +++ b/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeExtensions.cs @@ -26,7 +26,7 @@ public static bool ShouldHaveValues(this CheckoutAttribute checkoutAttribute) checkoutAttribute.AttributeControlType == AttributeControlType.Datepicker || checkoutAttribute.AttributeControlType == AttributeControlType.FileUpload) return false; - + //other attribute controle types support values return true; } diff --git a/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeFormatter.cs b/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeFormatter.cs index 627a5b692d..84fd3b046f 100644 --- a/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeFormatter.cs +++ b/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeFormatter.cs @@ -68,9 +68,9 @@ public string FormatAttributes(string attributes) /// A value indicating whether to HTML hyperink tags could be rendered (if required) /// Attributes public string FormatAttributes(string attributes, - Customer customer, - string serapator = "
", - bool htmlEncode = true, + Customer customer, + string serapator = "
", + bool htmlEncode = true, bool renderPrices = true, bool allowHyperlinks = true) { @@ -101,8 +101,8 @@ public string FormatAttributes(string attributes, if (htmlEncode) attributeName = HttpUtility.HtmlEncode(attributeName); - caAttribute = string.Format("{0}: {1}", attributeName, - HtmlUtils.ConvertPlainTextToHtml(valueStr.EmptyNull().Replace(":", "").HtmlEncode())); + caAttribute = string.Format("{0}: {1}", attributeName, + HtmlUtils.ConvertPlainTextToHtml(valueStr.EmptyNull().Replace(":", "").HtmlEncode())); //we never encode multiline textbox input } @@ -112,13 +112,11 @@ public string FormatAttributes(string attributes, Guid downloadGuid; Guid.TryParse(valueStr, out downloadGuid); var download = _downloadService.GetDownloadByGuid(downloadGuid); - if (download != null) + if (download?.MediaFile != null) { //TODO add a method for getting URL (use routing because it handles all SEO friendly URLs) string attributeText = ""; - var fileName = string.Format("{0}{1}", - download.Filename ?? download.DownloadGuid.ToString(), - download.Extension); + var fileName = download.MediaFile.Name; //encode (if required) if (htmlEncode) fileName = HttpUtility.HtmlEncode(fileName); @@ -186,6 +184,6 @@ public string FormatAttributes(string attributes, return result.ToString(); } - + } } diff --git a/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeParser.cs b/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeParser.cs index ed51878fd0..4393805397 100644 --- a/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeParser.cs +++ b/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeParser.cs @@ -91,39 +91,39 @@ public IList ParseValues(string attributes, int checkoutAttributeId) { var selectedCheckoutAttributeValues = new List(); - if (attributes.HasValue()) - { - try - { - var xmlDoc = new XmlDocument(); - xmlDoc.LoadXml(attributes); - - var nodeList1 = xmlDoc.SelectNodes(@"//Attributes/CheckoutAttribute"); - foreach (XmlNode node1 in nodeList1) - { - if (node1.Attributes != null && node1.Attributes["ID"] != null) - { - var str1 = node1.Attributes["ID"].InnerText.Trim(); - if (int.TryParse(str1, out var id)) - { - if (id == checkoutAttributeId) - { - var nodeList2 = node1.SelectNodes(@"CheckoutAttributeValue/Value"); - foreach (XmlNode node2 in nodeList2) - { - string value = node2.InnerText.Trim(); - selectedCheckoutAttributeValues.Add(value); - } - } - } - } - } - } - catch (Exception ex) - { - Debug.Write(ex.ToString()); - } - } + if (attributes.HasValue()) + { + try + { + var xmlDoc = new XmlDocument(); + xmlDoc.LoadXml(attributes); + + var nodeList1 = xmlDoc.SelectNodes(@"//Attributes/CheckoutAttribute"); + foreach (XmlNode node1 in nodeList1) + { + if (node1.Attributes != null && node1.Attributes["ID"] != null) + { + var str1 = node1.Attributes["ID"].InnerText.Trim(); + if (int.TryParse(str1, out var id)) + { + if (id == checkoutAttributeId) + { + var nodeList2 = node1.SelectNodes(@"CheckoutAttributeValue/Value"); + foreach (XmlNode node2 in nodeList2) + { + string value = node2.InnerText.Trim(); + selectedCheckoutAttributeValues.Add(value); + } + } + } + } + } + } + catch (Exception ex) + { + Debug.Write(ex.ToString()); + } + } return selectedCheckoutAttributeValues; } diff --git a/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeService.cs b/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeService.cs index 63a0d65b12..afa40cda5f 100644 --- a/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeService.cs +++ b/src/Libraries/SmartStore.Services/Orders/CheckoutAttributeService.cs @@ -9,32 +9,32 @@ namespace SmartStore.Services.Orders { - public partial class CheckoutAttributeService : ICheckoutAttributeService + public partial class CheckoutAttributeService : ICheckoutAttributeService { private readonly IRepository _checkoutAttributeRepository; private readonly IRepository _checkoutAttributeValueRepository; - private readonly IRepository _storeMappingRepository; - private readonly IEventPublisher _eventPublisher; + private readonly IRepository _storeMappingRepository; + private readonly IEventPublisher _eventPublisher; public CheckoutAttributeService( IRepository checkoutAttributeRepository, IRepository checkoutAttributeValueRepository, - IRepository storeMappingRepository, - IEventPublisher eventPublisher) + IRepository storeMappingRepository, + IEventPublisher eventPublisher) { _checkoutAttributeRepository = checkoutAttributeRepository; _checkoutAttributeValueRepository = checkoutAttributeValueRepository; - _storeMappingRepository = storeMappingRepository; + _storeMappingRepository = storeMappingRepository; _eventPublisher = eventPublisher; - this.QuerySettings = DbQuerySettings.Default; - } + this.QuerySettings = DbQuerySettings.Default; + } - public DbQuerySettings QuerySettings { get; set; } + public DbQuerySettings QuerySettings { get; set; } - #region Checkout attributes + #region Checkout attributes - public virtual void DeleteCheckoutAttribute(CheckoutAttribute checkoutAttribute) + public virtual void DeleteCheckoutAttribute(CheckoutAttribute checkoutAttribute) { if (checkoutAttribute == null) throw new ArgumentNullException("checkoutAttribute"); @@ -42,39 +42,39 @@ public virtual void DeleteCheckoutAttribute(CheckoutAttribute checkoutAttribute) _checkoutAttributeRepository.Delete(checkoutAttribute); } - public virtual IQueryable GetCheckoutAttributes(int storeId = 0, bool showHidden = false) - { - var query = _checkoutAttributeRepository.Table; - - if (!showHidden) - query = query.Where(x => x.IsActive); - - if (storeId > 0 && !QuerySettings.IgnoreMultiStore) - { - query = - from x in query - join sm in _storeMappingRepository.Table on new { c1 = x.Id, c2 = "CheckoutAttribute" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into x_sm - from sm in x_sm.DefaultIfEmpty() - where !x.LimitedToStores || storeId == sm.StoreId - select x; - - query = - from x in query - group x by x.Id into grp - orderby grp.Key - select grp.FirstOrDefault(); - } - - query = query.OrderBy(x => x.DisplayOrder); - - return query; - } + public virtual IQueryable GetCheckoutAttributes(int storeId = 0, bool showHidden = false) + { + var query = _checkoutAttributeRepository.Table; + + if (!showHidden) + query = query.Where(x => x.IsActive); + + if (storeId > 0 && !QuerySettings.IgnoreMultiStore) + { + query = + from x in query + join sm in _storeMappingRepository.Table on new { c1 = x.Id, c2 = "CheckoutAttribute" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into x_sm + from sm in x_sm.DefaultIfEmpty() + where !x.LimitedToStores || storeId == sm.StoreId + select x; + + query = + from x in query + group x by x.Id into grp + orderby grp.Key + select grp.FirstOrDefault(); + } + + query = query.OrderBy(x => x.DisplayOrder); + + return query; + } - public virtual IList GetAllCheckoutAttributes(int storeId = 0, bool showHidden = false) + public virtual IList GetAllCheckoutAttributes(int storeId = 0, bool showHidden = false) { - var query = GetCheckoutAttributes(storeId, showHidden); - return query.ToListCached("db.checkoutattrs.{0}.{1}".FormatInvariant(storeId, showHidden)); - } + var query = GetCheckoutAttributes(storeId, showHidden); + return query.ToListCached("db.checkoutattrs.{0}.{1}".FormatInvariant(storeId, showHidden)); + } /// /// Gets a checkout attribute @@ -86,8 +86,8 @@ public virtual CheckoutAttribute GetCheckoutAttributeById(int checkoutAttributeI if (checkoutAttributeId == 0) return null; - return _checkoutAttributeRepository.GetByIdCached(checkoutAttributeId, "db.checkoutattr.id-" + checkoutAttributeId); - } + return _checkoutAttributeRepository.GetByIdCached(checkoutAttributeId, "db.checkoutattr.id-" + checkoutAttributeId); + } public virtual void InsertCheckoutAttribute(CheckoutAttribute checkoutAttribute) { @@ -119,21 +119,21 @@ public virtual void DeleteCheckoutAttributeValue(CheckoutAttributeValue checkout public virtual IList GetCheckoutAttributeValues(int checkoutAttributeId) { - var query = from cav in _checkoutAttributeValueRepository.Table - orderby cav.DisplayOrder - where cav.CheckoutAttributeId == checkoutAttributeId - select cav; - var checkoutAttributeValues = query.ToListCached(); - return checkoutAttributeValues; - } - + var query = from cav in _checkoutAttributeValueRepository.Table + orderby cav.DisplayOrder + where cav.CheckoutAttributeId == checkoutAttributeId + select cav; + var checkoutAttributeValues = query.ToListCached(); + return checkoutAttributeValues; + } + public virtual CheckoutAttributeValue GetCheckoutAttributeValueById(int checkoutAttributeValueId) { if (checkoutAttributeValueId == 0) return null; - return _checkoutAttributeValueRepository.GetByIdCached(checkoutAttributeValueId, "db.checkoutattrval.id-" + checkoutAttributeValueId); - } + return _checkoutAttributeValueRepository.GetByIdCached(checkoutAttributeValueId, "db.checkoutattrval.id-" + checkoutAttributeValueId); + } public virtual void InsertCheckoutAttributeValue(CheckoutAttributeValue checkoutAttributeValue) { @@ -150,7 +150,7 @@ public virtual void UpdateCheckoutAttributeValue(CheckoutAttributeValue checkout _checkoutAttributeValueRepository.Update(checkoutAttributeValue); } - + #endregion } } diff --git a/src/Libraries/SmartStore.Services/Orders/CheckoutState.cs b/src/Libraries/SmartStore.Services/Orders/CheckoutState.cs index a2da7e33e2..103405a4a7 100644 --- a/src/Libraries/SmartStore.Services/Orders/CheckoutState.cs +++ b/src/Libraries/SmartStore.Services/Orders/CheckoutState.cs @@ -3,35 +3,35 @@ namespace SmartStore.Services.Orders { - [Serializable] - public partial class CheckoutState - { - public CheckoutState() - { - CustomProperties = new Dictionary(); - PaymentData = new Dictionary(); - } + [Serializable] + public partial class CheckoutState + { + public CheckoutState() + { + CustomProperties = new Dictionary(); + PaymentData = new Dictionary(); + } - public static string CheckoutStateSessionKey { get { return "SmCheckoutState"; } } + public static string CheckoutStateSessionKey => "SmCheckoutState"; - /// - /// The payment summary as displayed on the checkout confirmation page - /// - public string PaymentSummary { get; set; } + /// + /// The payment summary as displayed on the checkout confirmation page + /// + public string PaymentSummary { get; set; } - /// - /// Indicates whether the payment method selection page was skipped - /// - public bool IsPaymentSelectionSkipped { get; set; } + /// + /// Indicates whether the payment method selection page was skipped + /// + public bool IsPaymentSelectionSkipped { get; set; } - /// - /// Use this dictionary for any custom data required along checkout flow - /// - public IDictionary CustomProperties { get; set; } + /// + /// Use this dictionary for any custom data required along checkout flow + /// + public IDictionary CustomProperties { get; set; } - /// - /// Payment data entered on payment method selection page - /// - public IDictionary PaymentData { get; set; } - } + /// + /// Payment data entered on payment method selection page + /// + public IDictionary PaymentData { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/Orders/EventPublisherExtensions.cs b/src/Libraries/SmartStore.Services/Orders/EventPublisherExtensions.cs index 00bab7dad1..8e5bd4f491 100644 --- a/src/Libraries/SmartStore.Services/Orders/EventPublisherExtensions.cs +++ b/src/Libraries/SmartStore.Services/Orders/EventPublisherExtensions.cs @@ -26,28 +26,28 @@ public static void PublishOrderPlaced(this IEventPublisher eventPublisher, Order eventPublisher.Publish(new OrderPlacedEvent(order)); } - /// - /// Publishes the order updated event. - /// - /// The event publisher. - /// The order. - public static void PublishOrderUpdated(this IEventPublisher eventPublisher, Order order) - { - if (order != null) - eventPublisher.Publish(new OrderUpdatedEvent(order)); - } + /// + /// Publishes the order updated event. + /// + /// The event publisher. + /// The order. + public static void PublishOrderUpdated(this IEventPublisher eventPublisher, Order order) + { + if (order != null) + eventPublisher.Publish(new OrderUpdatedEvent(order)); + } - /// - /// Publishes the migrate shopping cart event. - /// - /// The event publisher. - /// The source customer entity. - /// The destination customer entity. - /// Store identifier. - public static void PublishMigrateShoppingCart(this IEventPublisher eventPublisher, Customer fromCustomer, Customer toCustomer, int storeId) - { - if (fromCustomer != null && toCustomer != null) - eventPublisher.Publish(new MigrateShoppingCartEvent(fromCustomer, toCustomer, storeId)); - } + /// + /// Publishes the migrate shopping cart event. + /// + /// The event publisher. + /// The source customer entity. + /// The destination customer entity. + /// Store identifier. + public static void PublishMigrateShoppingCart(this IEventPublisher eventPublisher, Customer fromCustomer, Customer toCustomer, int storeId) + { + if (fromCustomer != null && toCustomer != null) + eventPublisher.Publish(new MigrateShoppingCartEvent(fromCustomer, toCustomer, storeId)); + } } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Orders/GiftCardService.cs b/src/Libraries/SmartStore.Services/Orders/GiftCardService.cs index d89896397d..752dfc3a22 100644 --- a/src/Libraries/SmartStore.Services/Orders/GiftCardService.cs +++ b/src/Libraries/SmartStore.Services/Orders/GiftCardService.cs @@ -15,7 +15,7 @@ namespace SmartStore.Services.Orders public partial class GiftCardService : IGiftCardService { #region Fields - + private readonly IRepository _giftCardRepository; private readonly IEventPublisher _eventPublisher; @@ -35,7 +35,7 @@ public GiftCardService(IRepository giftCardRepository, IEventPublisher } #endregion - + #region Methods /// @@ -74,7 +74,7 @@ public virtual GiftCard GetGiftCardById(int giftCardId) /// Value indicating whether gift card is activated; null to load all records /// Gift card coupon code; null or string.empty to load all records /// Gift cards - public virtual IList GetAllGiftCards(int? purchasedWithOrderId, + public virtual IList GetAllGiftCards(int? purchasedWithOrderId, DateTime? startTime = null, DateTime? endTime = null, bool? isGiftCardActivated = null, string giftCardCouponCode = "") { diff --git a/src/Libraries/SmartStore.Services/Orders/ICheckoutAttributeFormatter.cs b/src/Libraries/SmartStore.Services/Orders/ICheckoutAttributeFormatter.cs index 7394315ccd..86735a79ba 100644 --- a/src/Libraries/SmartStore.Services/Orders/ICheckoutAttributeFormatter.cs +++ b/src/Libraries/SmartStore.Services/Orders/ICheckoutAttributeFormatter.cs @@ -27,8 +27,8 @@ public partial interface ICheckoutAttributeFormatter /// A value indicating whether to HTML hyperink tags could be rendered (if required) /// Attributes string FormatAttributes(string attributes, - Customer customer, - string serapator = "
", + Customer customer, + string serapator = "
", bool htmlEncode = true, bool renderPrices = true, bool allowHyperlinks = true); diff --git a/src/Libraries/SmartStore.Services/Orders/ICheckoutAttributeService.cs b/src/Libraries/SmartStore.Services/Orders/ICheckoutAttributeService.cs index 9a520ca28b..e7c70c9c93 100644 --- a/src/Libraries/SmartStore.Services/Orders/ICheckoutAttributeService.cs +++ b/src/Libraries/SmartStore.Services/Orders/ICheckoutAttributeService.cs @@ -4,10 +4,10 @@ namespace SmartStore.Services.Orders { - /// - /// Checkout attribute service - /// - public partial interface ICheckoutAttributeService + /// + /// Checkout attribute service + /// + public partial interface ICheckoutAttributeService { #region Checkout attributes @@ -17,21 +17,21 @@ public partial interface ICheckoutAttributeService /// Checkout attribute void DeleteCheckoutAttribute(CheckoutAttribute checkoutAttribute); - /// - /// Gets checkout attributes - /// - /// Whether to filter result by store identifier - /// A value indicating whether to show hidden records - /// Checkout attributes query - IQueryable GetCheckoutAttributes(int storeId = 0, bool showHidden = false); + /// + /// Gets checkout attributes + /// + /// Whether to filter result by store identifier + /// A value indicating whether to show hidden records + /// Checkout attributes query + IQueryable GetCheckoutAttributes(int storeId = 0, bool showHidden = false); - /// - /// Gets all checkout attributes - /// - /// Whether to filter result by store identifier - /// A value indicating whether to show hidden records - /// Checkout attribute collection - IList GetAllCheckoutAttributes(int storeId = 0, bool showHidden = false); + /// + /// Gets all checkout attributes + /// + /// Whether to filter result by store identifier + /// A value indicating whether to show hidden records + /// Checkout attribute collection + IList GetAllCheckoutAttributes(int storeId = 0, bool showHidden = false); /// /// Gets a checkout attribute @@ -68,7 +68,7 @@ public partial interface ICheckoutAttributeService /// The checkout attribute identifier /// Checkout attribute value collection IList GetCheckoutAttributeValues(int checkoutAttributeId); - + /// /// Gets a checkout attribute value /// @@ -87,7 +87,7 @@ public partial interface ICheckoutAttributeService /// /// Checkout attribute value void UpdateCheckoutAttributeValue(CheckoutAttributeValue checkoutAttributeValue); - + #endregion } } diff --git a/src/Libraries/SmartStore.Services/Orders/IGiftCardService.cs b/src/Libraries/SmartStore.Services/Orders/IGiftCardService.cs index 6c7255852b..fa819a1e39 100644 --- a/src/Libraries/SmartStore.Services/Orders/IGiftCardService.cs +++ b/src/Libraries/SmartStore.Services/Orders/IGiftCardService.cs @@ -53,12 +53,12 @@ IList GetAllGiftCards(int? purchasedWithOrderId, DateTime? startTime = /// Purchased with order item identifier /// Gift card entries IList GetGiftCardsByPurchasedWithOrderItemId(int purchasedWithOrderItemId); - + /// /// Get active gift cards that are applied by a customer /// /// Customer - /// Store identifier + /// Store identifier /// Active gift cards IList GetActiveGiftCardsAppliedByCustomer(Customer customer, int storeId); diff --git a/src/Libraries/SmartStore.Services/Orders/IOrderProcessingService.cs b/src/Libraries/SmartStore.Services/Orders/IOrderProcessingService.cs index 5ac6d24daf..0801067fd1 100644 --- a/src/Libraries/SmartStore.Services/Orders/IOrderProcessingService.cs +++ b/src/Libraries/SmartStore.Services/Orders/IOrderProcessingService.cs @@ -59,7 +59,6 @@ IList GetOrderPlacementWarnings( /// The order void DeleteOrder(Order order); - /// /// Process next recurring psayment /// @@ -80,17 +79,13 @@ IList GetOrderPlacementWarnings( /// value indicating whether a customer can cancel recurring payment bool CanCancelRecurringPayment(Customer customerToValidate, RecurringPayment recurringPayment); - - - - /// /// Send a shipment /// /// Shipment /// True to notify customer void Ship(Shipment shipment, bool notifyCustomer); - + /// /// Marks a shipment as delivered /// @@ -98,8 +93,6 @@ IList GetOrderPlacementWarnings( /// True to notify customer void Deliver(Shipment shipment, bool notifyCustomer); - - /// /// Gets a value indicating whether cancel is allowed /// @@ -114,12 +107,11 @@ IList GetOrderPlacementWarnings( /// True to notify customer void CancelOrder(Order order, bool notifyCustomer); - /// - /// Auto update order details - /// - /// Context parameters - void AutoUpdateOrderDetails(AutoUpdateOrderItemContext context); - + /// + /// Auto update order details + /// + /// Context parameters + void AutoUpdateOrderDetails(AutoUpdateOrderItemContext context); /// /// Gets a value indicating whether order can be marked as authorized @@ -134,20 +126,18 @@ IList GetOrderPlacementWarnings( /// Order void MarkAsAuthorized(Order order); + /// + /// Gets a value indicating whether the order can be marked as completed + /// + /// Order + /// A value indicating whether the order can be marked as completed + bool CanCompleteOrder(Order order); - /// - /// Gets a value indicating whether the order can be marked as completed - /// - /// Order - /// A value indicating whether the order can be marked as completed - bool CanCompleteOrder(Order order); - - /// - /// Marks the order as completed - /// - /// Order - void CompleteOrder(Order order); - + /// + /// Marks the order as completed + /// + /// Order + void CompleteOrder(Order order); /// /// Gets a value indicating whether capture from admin panel is allowed @@ -176,8 +166,6 @@ IList GetOrderPlacementWarnings( /// Order void MarkOrderAsPaid(Order order); - - /// /// Gets a value indicating whether refund from admin panel is allowed /// @@ -236,8 +224,6 @@ IList GetOrderPlacementWarnings( /// Amount to refund void PartiallyRefundOffline(Order order, decimal amountToRefund); - - /// /// Gets a value indicating whether void from admin panel is allowed /// @@ -265,15 +251,12 @@ IList GetOrderPlacementWarnings( /// Order void VoidOffline(Order order); - - - /// /// Place order items in current user shopping cart. /// /// The order void ReOrder(Order order); - + /// /// Check whether return request is allowed /// @@ -281,28 +264,36 @@ IList GetOrderPlacementWarnings( /// Result bool IsReturnRequestAllowed(Order order); - /// - /// Valdiate minimum order sub-total amount + /// Valdiate minimum order amount. + /// Gets min order amount from customer role. + /// When no min order amount is defined in customer role, default order settings are used as fallback if present. /// /// Shopping cart - /// true - OK; false - minimum order sub-total amount is not reached - bool ValidateMinOrderSubtotalAmount(IList cart); + /// true - OK; false - minimum order amount is not reached + (bool valid, decimal orderTotalMinimum) IsAboveOrderTotalMinimum(IList cart, int[] customerRoleIds); /// - /// Valdiate minimum order total amount + /// Valdiate maximum order amount. + /// Gets max order amount from customer role. + /// When no max order amount is defined in customer role, default order settings are used as fallback if present. /// - /// Shopping cart - /// true - OK; false - minimum order total amount is not reached - bool ValidateMinOrderTotalAmount(IList cart); - - /// - /// Adds a shipment to an order - /// - /// Order - /// Tracking number - /// Quantities by order item identifiers. null to use the remaining total number of products for each order item. - /// New shipment, null if no shipment was added - Shipment AddShipment(Order order, string trackingNumber, Dictionary quantities); + /// Shopping cart, customer role ids + /// true - OK; false - maximum order amount is exceeded + (bool valid, decimal orderTotalMaximum) IsBelowOrderTotalMaximum(IList cart, int[] customerRoleIds); + + /// + /// Adds a shipment to an order. + /// + /// Order. + /// Tracking number. + /// Tracking URL. + /// Quantities by order item identifiers. null to use the remaining total number of products for each order item. + /// New shipment, null if no shipment was added. + Shipment AddShipment( + Order order, + string trackingNumber, + string trackingUrl, + Dictionary quantities); } } diff --git a/src/Libraries/SmartStore.Services/Orders/IOrderReportService.cs b/src/Libraries/SmartStore.Services/Orders/IOrderReportService.cs index 381a207c40..4894bf6549 100644 --- a/src/Libraries/SmartStore.Services/Orders/IOrderReportService.cs +++ b/src/Libraries/SmartStore.Services/Orders/IOrderReportService.cs @@ -1,20 +1,29 @@ using System; -using System.Collections.Generic; +using System.Linq; using SmartStore.Core; using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Dashboard; using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Payments; using SmartStore.Core.Domain.Shipping; namespace SmartStore.Services.Orders { + public enum ReportSorting + { + ByQuantityAsc = 0, + ByQuantityDesc, + ByAmountAsc, + ByAmountDesc + } + /// /// Order report service interface /// public partial interface IOrderReportService { /// - /// Get order average report + /// Get order average report. /// /// Store identifier /// Filter by order status @@ -24,38 +33,58 @@ public partial interface IOrderReportService /// End date /// Billing email. Leave empty to load all records. /// A value indicating whether to ignore cancelled orders - /// Result - OrderAverageReportLine GetOrderAverageReportLine(int storeId, int[] orderStatusIds, - int[] paymentStatusIds, int[] shippingStatusIds, DateTime? startTimeUtc, - DateTime? endTimeUtc, string billingEmail, bool ignoreCancelledOrders = false); - + /// Order average report line. + OrderAverageReportLine GetOrderAverageReportLine(int storeId, + int[] orderStatusIds, + int[] paymentStatusIds, + int[] shippingStatusIds, + DateTime? startTimeUtc, + DateTime? endTimeUtc, + string billingEmail, + bool ignoreCancelledOrders = false); + /// - /// Get order average report + /// Get order average report. /// - /// Store identifier - /// Order status - /// Result - OrderAverageReportLineSummary OrderAverageReport(int storeId, OrderStatus os); + /// Order queryable. + /// Order average report line. + OrderAverageReportLine GetOrderAverageReportLine(IQueryable orderQuery); + + /// + /// Get order average report. + /// + /// Store identifier + /// Order status + /// Order average report. + OrderAverageReportLineSummary OrderAverageReport(int storeId, OrderStatus orderStatus); /// - /// Get best sellers report + /// Get best sellers report. /// /// Store identifier /// Order start time; null to load all /// Order end time; null to load all - /// Order status; null to load all records - /// Order payment status; null to load all records - /// Shipping status; null to load all records + /// Order status; null to load all records + /// Order payment status; null to load all records + /// Shipping status; null to load all records /// Billing country identifier; 0 to load all records - /// Records to return - /// 1 - order by quantity, 2 - order by total amount + /// Page index. + /// Page size. + /// Sorting of report items. /// A value indicating whether to show hidden records - /// Result - IList BestSellersReport(int storeId, - DateTime? startTime, DateTime? endTime, - OrderStatus? os, PaymentStatus? ps, ShippingStatus? ss, - int billingCountryId = 0, int recordsToReturn = 5, - int orderBy = 1, bool showHidden = false); + /// Best selling products. + IPagedList BestSellersReport( + int storeId, + DateTime? startTime, + DateTime? endTime, + OrderStatus? orderStatus, + PaymentStatus? paymentStatus, + ShippingStatus? shippingStatus, + int billingCountryId = 0, + int pageIndex = 0, + int pageSize = int.MaxValue, + ReportSorting sorting = ReportSorting.ByQuantityDesc, + bool showHidden = false); /// /// Gets a the count of purchases for a product @@ -83,21 +112,43 @@ IList BestSellersReport(int storeId, /// Page size /// A value indicating whether to show hidden records /// Products - IPagedList ProductsNeverSold(DateTime? startTime, - DateTime? endTime, int pageIndex, int pageSize, bool showHidden = false); + IPagedList ProductsNeverSold(DateTime? startTime, DateTime? endTime, int pageIndex, int pageSize, bool showHidden = false); /// - /// Get profit report + /// Get order profit. /// - /// Store identifier - /// Start date - /// End date - /// Filter by order status - /// Filter by payment status - /// Filter by shipping status - /// Billing email. Leave empty to load all records. - /// Result - decimal ProfitReport(int storeId, int[] orderStatusIds, int[] paymentStatusIds, int[] shippingStatusIds, - DateTime? startTimeUtc, DateTime? endTimeUtc, string billingEmail); + /// Order queryable. + /// Order profit. + decimal GetProfit(IQueryable orderQuery); + + + /// + /// Get paged list of incomplete orders + /// + /// Store identifier + /// Start time limitation + /// End time limitation + /// List of incomplete orders + IPagedList GetIncompleteOrders(int storeId, DateTime? startTimeUtc, DateTime? endTimeUtc); + + /// + /// Get paged list of orders as ChartDataPoints + /// + /// Store identifier + /// Start time UTC + /// End time UTC + /// Page index + /// Page size + /// + IPagedList GetOrdersDashboardData(int storeId, DateTime? startTimeUtc, DateTime? endTimeUtc, int pageIndex, int pageSize); + + /// + /// Get orders total + /// + /// Store identifier + /// Start time UTC + /// End time UTC + /// + decimal GetOrdersTotal(int storeId, DateTime? startTimeUtc, DateTime? endTimeUtc); } -} +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Orders/IOrderService.cs b/src/Libraries/SmartStore.Services/Orders/IOrderService.cs index e0d8dddfc4..51e4d43746 100644 --- a/src/Libraries/SmartStore.Services/Orders/IOrderService.cs +++ b/src/Libraries/SmartStore.Services/Orders/IOrderService.cs @@ -48,21 +48,21 @@ public partial interface IOrderService /// Order Order GetOrderByGuid(Guid orderGuid); - /// - /// Get order by payment authorization data - /// - /// System name of the payment method - /// Authorization transaction Id - /// Order entity - Order GetOrderByPaymentAuthorization(string paymentMethodSystemName, string authorizationTransactionId); - - /// - /// Get order by payment capture data - /// - /// System name of the payment method - /// Capture transaction Id - /// Order entity - Order GetOrderByPaymentCapture(string paymentMethodSystemName, string captureTransactionId); + /// + /// Get order by payment authorization data + /// + /// System name of the payment method + /// Authorization transaction Id + /// Order entity + Order GetOrderByPaymentAuthorization(string paymentMethodSystemName, string authorizationTransactionId); + + /// + /// Get order by payment capture data + /// + /// System name of the payment method + /// Capture transaction Id + /// Order entity + Order GetOrderByPaymentCapture(string paymentMethodSystemName, string captureTransactionId); /// /// Deletes an order @@ -70,32 +70,32 @@ public partial interface IOrderService /// The order void DeleteOrder(Order order); - /// - /// Get orders - /// - /// Store identifier; null to load all orders - /// Customer identifier; null 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. - /// Filter by order number - /// Billing name. Leave empty to load all records. + /// + /// Get orders + /// + /// Store identifier; null to load all orders + /// Customer identifier; null 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. + /// Filter by order number + /// Billing name. Leave empty to load all records. /// Filter by payment method system names. - /// Order query - IQueryable GetOrders( - int storeId, - int customerId, - DateTime? startTime, - DateTime? endTime, - int[] orderStatusIds, - int[] paymentStatusIds, - int[] shippingStatusIds, - string billingEmail, - string orderNumber, - string billingName = null, + /// Order query + IQueryable GetOrders( + int storeId, + int customerId, + DateTime? startTime, + DateTime? endTime, + int[] orderStatusIds, + int[] paymentStatusIds, + int[] shippingStatusIds, + string billingEmail, + string orderNumber, + string billingName = null, string[] paymentMethods = null); /// @@ -117,8 +117,8 @@ IQueryable GetOrders( /// Filter by payment method system names. /// Order collection 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, + int[] orderStatusIds, int[] paymentStatusIds, int[] shippingStatusIds, + string billingEmail, string orderGuid, string orderNumber, int pageIndex, int pageSize, string billingName = null, string[] paymentMethods = null); /// @@ -163,24 +163,24 @@ IPagedList SearchOrders(int storeId, int customerId, DateTime? startTime, /// Order Order GetOrderByAuthorizationTransactionIdAndPaymentMethod(string authorizationTransactionId, string paymentMethodSystemName); - /// - /// Shortcut to add an order - /// - /// Order - /// Order note - /// Whether to display the note to the customer - void AddOrderNote(Order order, string note, bool displayToCustomer = false); + /// + /// Shortcut to add an order + /// + /// Order + /// Order note + /// Whether to display the note to the customer + void AddOrderNote(Order order, string note, bool displayToCustomer = false); - #endregion + #endregion - #region Orders items + #region Orders items - /// - /// Gets an order item - /// - /// Order item identifier - /// Order item - OrderItem GetOrderItemById(int orderItemId); + /// + /// Gets an order item + /// + /// Order item identifier + /// Order item + OrderItem GetOrderItemById(int orderItemId); /// /// Gets an order item @@ -206,12 +206,12 @@ IList GetAllOrderItems(int? orderId, OrderStatus? os, PaymentStatus? ps, ShippingStatus? ss, bool loadDownloableProductsOnly = false); - /// - /// Get order items by order identifiers - /// - /// Order identifiers - /// Order items - Multimap GetOrderItemsByOrderIds(int[] orderIds); + /// + /// Get order items by order identifiers + /// + /// Order identifiers + /// Order items + Multimap GetOrderItemsByOrderIds(int[] orderIds); /// /// Delete an order item @@ -252,13 +252,21 @@ IList GetAllOrderItems(int? orderId, /// Search recurring payments /// /// The customer identifier; 0 to load all records - /// The store identifier; 0 to load all records + /// The store 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 + /// Page index + /// Page size /// Recurring payment collection - IList SearchRecurringPayments(int storeId, - int customerId, int initialOrderId, OrderStatus? initialOrderStatus, bool showHidden = false); + IPagedList SearchRecurringPayments( + int storeId, + int customerId, + int initialOrderId, + OrderStatus? initialOrderStatus, + bool showHidden = false, + int pageIndex = 0, + int pageSize = int.MaxValue); #endregion @@ -276,20 +284,20 @@ IList SearchRecurringPayments(int storeId, /// Return request identifier /// Return request ReturnRequest GetReturnRequestById(int returnRequestId); - + /// /// Search return requests /// - /// Store identifier; 0 to load all entries + /// 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 + /// Page index + /// Page size + /// Return Request Id /// Return requests - IPagedList SearchReturnRequests(int storeId, int customerId, int orderItemId, ReturnRequestStatus? rs, int pageIndex, int pageSize, int id = 0); - + IPagedList SearchReturnRequests(int storeId, int customerId, int orderItemId, ReturnRequestStatus? rs, int pageIndex, int pageSize, int id = 0); + #endregion } } diff --git a/src/Libraries/SmartStore.Services/Orders/IOrderTotalCalculationService.cs b/src/Libraries/SmartStore.Services/Orders/IOrderTotalCalculationService.cs index 3f5ee14bb5..627723b438 100644 --- a/src/Libraries/SmartStore.Services/Orders/IOrderTotalCalculationService.cs +++ b/src/Libraries/SmartStore.Services/Orders/IOrderTotalCalculationService.cs @@ -72,7 +72,7 @@ decimal GetOrderSubtotalDiscount(Customer customer, /// Applied discount /// Adjusted shipping rate decimal AdjustShippingRate(decimal shippingRate, IList cart, - ShippingOption shippingOption, IList shippingMethods, out Discount appliedDiscount); + ShippingOption shippingOption, IList shippingMethods, out Discount appliedDiscount); /// /// Gets shopping cart additional shipping charge @@ -114,14 +114,14 @@ decimal AdjustShippingRate(decimal shippingRate, IList cart, bool includingTax, out decimal taxRate, out Discount appliedDiscount); - /// - /// Gets a shipping discount - /// - /// Customer - /// Shipping total - /// Applied discount - /// Shipping discount - decimal GetShippingDiscount(Customer customer, decimal shippingTotal, out Discount appliedDiscount); + /// + /// Gets a shipping discount + /// + /// Customer + /// Shipping total + /// Applied discount + /// Shipping discount + decimal GetShippingDiscount(Customer customer, decimal shippingTotal, out Discount appliedDiscount); @@ -161,7 +161,7 @@ ShoppingCartTotal GetShoppingCartTotal( IList cart, bool ignoreRewardPoints = false, bool usePaymentMethodAdditionalFee = true, - bool ignoreCreditBalance = false); + bool ignoreCreditBalance = false); /// diff --git a/src/Libraries/SmartStore.Services/Orders/IShoppingCartService.cs b/src/Libraries/SmartStore.Services/Orders/IShoppingCartService.cs index 64b10fc2cb..924a63437b 100644 --- a/src/Libraries/SmartStore.Services/Orders/IShoppingCartService.cs +++ b/src/Libraries/SmartStore.Services/Orders/IShoppingCartService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using SmartStore.Core; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Orders; @@ -11,42 +12,42 @@ namespace SmartStore.Services.Orders /// public partial interface IShoppingCartService { - /// - /// Gets the shopping cart items count - /// - /// Customer. Cannot be null. - /// Type of cart to get items count for - /// Store id - /// Sum of all item quantities - int CountItems(Customer customer, ShoppingCartType cartType, int? storeId = null); + /// + /// Gets the shopping cart items count + /// + /// Customer. Cannot be null. + /// Type of cart to get items count for + /// Store id + /// Sum of all item quantities + int CountItems(Customer customer, ShoppingCartType cartType, int? storeId = null); - /// - /// Gets the shopping cart items - /// - /// Customer. Cannot be null. - /// Type of cart to get items for - /// Store id - /// All cart items - List GetCartItems(Customer customer, ShoppingCartType cartType, int? storeId = null); + /// + /// Gets the shopping cart items + /// + /// Customer. Cannot be null. + /// Type of cart to get items for + /// Store id + /// All cart items + List GetCartItems(Customer customer, ShoppingCartType cartType, int? storeId = null); - /// - /// Delete shopping cart item - /// - /// Shopping cart item - /// A value indicating whether to reset checkout data - /// A value indicating whether to ensure that only active checkout attributes are attached to the current customer - /// A value indicating whether to delete child cart items - void DeleteShoppingCartItem( - ShoppingCartItem shoppingCartItem, - bool resetCheckoutData = true, - bool ensureOnlyActiveCheckoutAttributes = false, - bool deleteChildCartItems = true); + /// + /// Delete shopping cart item + /// + /// Shopping cart item + /// A value indicating whether to reset checkout data + /// A value indicating whether to ensure that only active checkout attributes are attached to the current customer + /// A value indicating whether to delete child cart items + void DeleteShoppingCartItem( + ShoppingCartItem shoppingCartItem, + bool resetCheckoutData = true, + bool ensureOnlyActiveCheckoutAttributes = false, + bool deleteChildCartItems = true); - void DeleteShoppingCartItem( - int shoppingCartItemId, - bool resetCheckoutData = true, - bool ensureOnlyActiveCheckoutAttributes = false, - bool deleteChildCartItems = true); + void DeleteShoppingCartItem( + int shoppingCartItemId, + bool resetCheckoutData = true, + bool ensureOnlyActiveCheckoutAttributes = false, + bool deleteChildCartItems = true); /// /// Deletes expired shopping cart items @@ -66,11 +67,11 @@ void DeleteShoppingCartItem( /// Automatically add required products if enabled /// Warnings IList GetRequiredProductWarnings( - Customer customer, - ShoppingCartType shoppingCartType, - Product product, - int storeId, - bool automaticallyAddRequiredProductsIfEnabled); + Customer customer, + ShoppingCartType shoppingCartType, + Product product, + int storeId, + bool automaticallyAddRequiredProductsIfEnabled); /// /// Validates a product for standard properties @@ -83,32 +84,33 @@ IList GetRequiredProductWarnings( /// Quantity /// Warnings IList GetStandardWarnings( - Customer customer, - ShoppingCartType shoppingCartType, - Product product, - string selectedAttributes, - decimal customerEnteredPrice, - int quantity); + Customer customer, + ShoppingCartType shoppingCartType, + Product product, + string selectedAttributes, + decimal customerEnteredPrice, + int quantity, + int storeId = 0); - /// - /// Validates shopping cart item attributes - /// - /// The customer - /// Shopping cart type - /// Product - /// Selected attributes - /// The product variant attribute combination instance (reduces database roundtrips) - /// Quantity - /// Product bundle item - /// Warnings - IList GetShoppingCartItemAttributeWarnings( - Customer customer, - ShoppingCartType shoppingCartType, - Product product, - string selectedAttributes, - int quantity = 1, - ProductBundleItem bundleItem = null, - ProductVariantAttributeCombination combination = null); + /// + /// Validates shopping cart item attributes + /// + /// The customer + /// Shopping cart type + /// Product + /// Selected attributes + /// The product variant attribute combination instance (reduces database roundtrips) + /// Quantity + /// Product bundle item + /// Warnings + IList GetShoppingCartItemAttributeWarnings( + Customer customer, + ShoppingCartType shoppingCartType, + Product product, + string selectedAttributes, + int quantity = 1, + ProductBundleItem bundleItem = null, + ProductVariantAttributeCombination combination = null); /// /// Validates shopping cart item (gift card) @@ -119,14 +121,14 @@ IList GetShoppingCartItemAttributeWarnings( /// Warnings IList GetShoppingCartItemGiftCardWarnings(ShoppingCartType shoppingCartType, Product product, string selectedAttributes); - /// - /// Validates bundle items - /// - /// Shopping cart type - /// Product bundle item - /// Warnings - IList GetBundleItemWarnings(ProductBundleItem bundleItem); - IList GetBundleItemWarnings(IList cartItems); + /// + /// Validates bundle items + /// + /// Shopping cart type + /// Product bundle item + /// Warnings + IList GetBundleItemWarnings(ProductBundleItem bundleItem); + IList GetBundleItemWarnings(IList cartItems); /// /// Validates shopping cart item @@ -148,21 +150,21 @@ IList GetShoppingCartItemAttributeWarnings( /// Child cart items to validate bundle items /// Warnings IList GetShoppingCartItemWarnings( - Customer customer, - ShoppingCartType shoppingCartType, - Product product, - int storeId, - string selectedAttributes, - decimal customerEnteredPrice, - int quantity, - bool automaticallyAddRequiredProductsIfEnabled, - bool getStandardWarnings = true, - bool getAttributesWarnings = true, - bool getGiftCardWarnings = true, - bool getRequiredProductWarnings = true, - bool getBundleWarnings = true, - ProductBundleItem bundleItem = null, - IList childItems = null); + Customer customer, + ShoppingCartType shoppingCartType, + Product product, + int storeId, + string selectedAttributes, + decimal customerEnteredPrice, + int quantity, + bool automaticallyAddRequiredProductsIfEnabled, + bool getStandardWarnings = true, + bool getAttributesWarnings = true, + bool getGiftCardWarnings = true, + bool getRequiredProductWarnings = true, + bool getBundleWarnings = true, + ProductBundleItem bundleItem = null, + IList childItems = null); /// /// Validates whether this shopping cart is valid @@ -183,47 +185,47 @@ IList GetShoppingCartItemWarnings( /// Price entered by a customer /// Found shopping cart item OrganizedShoppingCartItem FindShoppingCartItemInTheCart( - IList shoppingCart, + IList shoppingCart, ShoppingCartType shoppingCartType, - Product product, + Product product, string selectedAttributes = "", decimal customerEnteredPrice = decimal.Zero); - /// - /// Add product to cart - /// - /// The customer - /// The product - /// Cart type - /// Store identifier - /// Selected attributes - /// Price entered by customer - /// Quantity - /// Whether to add required products - /// Add to cart context - /// List with warnings - List AddToCart( - Customer customer, - Product product, - ShoppingCartType cartType, - int storeId, - string selectedAttributes, - decimal customerEnteredPrice, - int quantity, - bool automaticallyAddRequiredProductsIfEnabled, - AddToCartContext ctx = null); + /// + /// Add product to cart + /// + /// The customer + /// The product + /// Cart type + /// Store identifier + /// Selected attributes + /// Price entered by customer + /// Quantity + /// Whether to add required products + /// Add to cart context + /// List with warnings + List AddToCart( + Customer customer, + Product product, + ShoppingCartType cartType, + int storeId, + string selectedAttributes, + decimal customerEnteredPrice, + int quantity, + bool automaticallyAddRequiredProductsIfEnabled, + AddToCartContext ctx = null); - /// - /// Add product to cart - /// - /// Add to cart context - void AddToCart(AddToCartContext ctx); + /// + /// Add product to cart + /// + /// Add to cart context + void AddToCart(AddToCartContext ctx); - /// - /// Stores the shopping card items in the database - /// - /// Add to cart context - void AddToCartStoring(AddToCartContext ctx); + /// + /// Stores the shopping card items in the database + /// + /// Add to cart context + void AddToCartStoring(AddToCartContext ctx); /// /// Validates if all required attributes are selected @@ -250,16 +252,16 @@ List AddToCart( /// To customer void MigrateShoppingCart(Customer fromCustomer, Customer toCustomer); - /// - /// Copies a shopping cart item. - /// - /// Shopping cart item - /// The customer - /// Shopping cart type - /// Store Id - /// Add required products if enabled - /// List with add-to-cart warnings. - IList Copy(OrganizedShoppingCartItem sci, Customer customer, ShoppingCartType cartType, int storeId, bool addRequiredProductsIfEnabled); + /// + /// Copies a shopping cart item. + /// + /// Shopping cart item + /// The customer + /// Shopping cart type + /// Store Id + /// Add required products if enabled + /// List with add-to-cart warnings. + IList Copy(OrganizedShoppingCartItem sci, Customer customer, ShoppingCartType cartType, int storeId, bool addRequiredProductsIfEnabled); /// /// Gets the subtotal of cart items for the current user @@ -278,11 +280,23 @@ List AddToCart( /// /// Formatted subtotal of cart items for the current user string GetFormattedCurrentCartSubTotal(); - + /// /// Gets the formatted subtotal of cart items for the current user /// /// Formatted subtotal of cart items for the current user string GetFormattedCurrentCartSubTotal(IList cart); + + /// + /// Get open carts subtotal + /// + /// subtotal + decimal GetAllOpenCartSubTotal(); + + /// + /// Get open wishlists subtotal + /// + /// subtotal + decimal GetAllOpenWishlistSubTotal(); } } diff --git a/src/Libraries/SmartStore.Services/Orders/OrderExtensions.cs b/src/Libraries/SmartStore.Services/Orders/OrderExtensions.cs index 5a5803d728..2c032bd403 100644 --- a/src/Libraries/SmartStore.Services/Orders/OrderExtensions.cs +++ b/src/Libraries/SmartStore.Services/Orders/OrderExtensions.cs @@ -10,23 +10,23 @@ namespace SmartStore.Services.Orders { - public static class OrderExtensions + public static class OrderExtensions { - private static int AggregateQuantity(IEnumerable shipments, OrderItem orderItem) - { - var result = 0; - - foreach (var shipment in shipments) - { - var item = shipment.ShipmentItems.FirstOrDefault(x => x.OrderItemId == orderItem.Id); - if (item != null) - { - result += item.Quantity; - } - } - - return result; - } + private static int AggregateQuantity(IEnumerable shipments, OrderItem orderItem) + { + var result = 0; + + foreach (var shipment in shipments) + { + var item = shipment.ShipmentItems.FirstOrDefault(x => x.OrderItemId == orderItem.Id); + if (item != null) + { + result += item.Quantity; + } + } + + return result; + } /// /// Formats the order note text @@ -35,7 +35,7 @@ private static int AggregateQuantity(IEnumerable shipments, OrderItem /// Formatted text public static string FormatOrderNoteText(this OrderNote orderNote) { - Guard.NotNull(orderNote, nameof(orderNote)); + Guard.NotNull(orderNote, nameof(orderNote)); if (orderNote.Note.IsEmpty()) return string.Empty; @@ -43,25 +43,25 @@ public static string FormatOrderNoteText(this OrderNote orderNote) return HtmlUtils.ConvertPlainTextToHtml(orderNote.Note); } - public static List GetBundleData(this OrderItem orderItem) - { - if (orderItem != null && orderItem.BundleData.HasValue()) - { - var data = orderItem.BundleData.Convert>(); - return data; - } - return new List(); - } + public static List GetBundleData(this OrderItem orderItem) + { + if (orderItem != null && orderItem.BundleData.HasValue()) + { + var data = orderItem.BundleData.Convert>(); + return data; + } + return new List(); + } - public static void SetBundleData(this OrderItem orderItem, List bundleData) - { - string rawData = null; + public static void SetBundleData(this OrderItem orderItem, List bundleData) + { + string rawData = null; - if (bundleData != null && bundleData.Count > 0) - rawData = bundleData.Convert(); + if (bundleData != null && bundleData.Count > 0) + rawData = bundleData.Convert(); - orderItem.BundleData = rawData; - } + orderItem.BundleData = rawData; + } /// /// Get the order total in the currency of the customer @@ -107,143 +107,143 @@ public static decimal GetOrderTotalInCustomerCurrency( /// Order /// A value indicating whether an order has items to dispatch public static bool HasItemsToDispatch(this Order order) - { - Guard.NotNull(order, nameof(order)); - - foreach (var orderItem in order.OrderItems.Where(x => x.Product.IsShipEnabled)) - { - var notDispatchedItems = orderItem.GetNotDispatchedItemsCount(); - if (notDispatchedItems <= 0) - continue; - - // yes, we have at least one item to ship - return true; - } - return false; - } - - /// - /// Gets a value indicating whether an order has items to deliver - /// - /// Order - /// A value indicating whether an order has items to deliver - public static bool HasItemsToDeliver(this Order order) - { - Guard.NotNull(order, nameof(order)); - - foreach (var orderItem in order.OrderItems.Where(x => x.Product.IsShipEnabled)) - { - var dispatchedItems = orderItem.GetDispatchedItemsCount(); - var deliveredItems = orderItem.GetDeliveredItemsCount(); - - if (dispatchedItems <= deliveredItems) - continue; - - // yes, we have at least one item to deliver - return true; - } - return false; - } - - /// - /// Gets a value indicating whether an order has items to be added to a shipment - /// - /// Order - /// A value indicating whether an order has items to be added to a shipment - public static bool CanAddItemsToShipment(this Order order) - { - Guard.NotNull(order, nameof(order)); - - foreach (var orderItem in order.OrderItems.Where(x => x.Product.IsShipEnabled)) - { - var canBeAddedToShipment = orderItem.GetItemsCanBeAddedToShipmentCount(); - if (canBeAddedToShipment <= 0) - continue; - - // yes, we have at least one item to create a new shipment - return true; - } - return false; - } - - - /// - /// Gets the total number of items which can be added to new shipments - /// - /// Order item - /// Total number of items which can be added to new shipments - public static int GetItemsCanBeAddedToShipmentCount(this OrderItem orderItem) - { - Guard.NotNull(orderItem, nameof(orderItem)); - - var itemsCount = orderItem.GetShipmentItemsCount(); - - return Math.Max(orderItem.Quantity - itemsCount, 0); - } - - /// - /// Gets the total number of items in all shipments - /// - /// Order item - /// Total number of items in all shipmentss - public static int GetShipmentItemsCount(this OrderItem orderItem) { - Guard.NotNull(orderItem, nameof(orderItem)); + Guard.NotNull(order, nameof(order)); + + foreach (var orderItem in order.OrderItems.Where(x => x.Product.IsShipEnabled)) + { + var notDispatchedItems = orderItem.GetNotDispatchedItemsCount(); + if (notDispatchedItems <= 0) + continue; + + // yes, we have at least one item to ship + return true; + } + return false; + } + + /// + /// Gets a value indicating whether an order has items to deliver + /// + /// Order + /// A value indicating whether an order has items to deliver + public static bool HasItemsToDeliver(this Order order) + { + Guard.NotNull(order, nameof(order)); + + foreach (var orderItem in order.OrderItems.Where(x => x.Product.IsShipEnabled)) + { + var dispatchedItems = orderItem.GetDispatchedItemsCount(); + var deliveredItems = orderItem.GetDeliveredItemsCount(); + + if (dispatchedItems <= deliveredItems) + continue; + + // yes, we have at least one item to deliver + return true; + } + return false; + } + + /// + /// Gets a value indicating whether an order has items to be added to a shipment + /// + /// Order + /// A value indicating whether an order has items to be added to a shipment + public static bool CanAddItemsToShipment(this Order order) + { + Guard.NotNull(order, nameof(order)); + + foreach (var orderItem in order.OrderItems.Where(x => x.Product.IsShipEnabled)) + { + var canBeAddedToShipment = orderItem.GetItemsCanBeAddedToShipmentCount(); + if (canBeAddedToShipment <= 0) + continue; + + // yes, we have at least one item to create a new shipment + return true; + } + return false; + } + + + /// + /// Gets the total number of items which can be added to new shipments + /// + /// Order item + /// Total number of items which can be added to new shipments + public static int GetItemsCanBeAddedToShipmentCount(this OrderItem orderItem) + { + Guard.NotNull(orderItem, nameof(orderItem)); + + var itemsCount = orderItem.GetShipmentItemsCount(); + + return Math.Max(orderItem.Quantity - itemsCount, 0); + } + + /// + /// Gets the total number of items in all shipments + /// + /// Order item + /// Total number of items in all shipmentss + public static int GetShipmentItemsCount(this OrderItem orderItem) + { + Guard.NotNull(orderItem, nameof(orderItem)); + + var shipments = orderItem.Order.Shipments.ToList(); + return AggregateQuantity(shipments, orderItem); + } + + /// + /// Gets the total number of dispatched items + /// + /// Order item + /// Total number of dispatched items + public static int GetDispatchedItemsCount(this OrderItem orderItem) + { + Guard.NotNull(orderItem, nameof(orderItem)); var shipments = orderItem.Order.Shipments.ToList(); - return AggregateQuantity(shipments, orderItem); + return AggregateQuantity(shipments.Where(x => x.ShippedDateUtc.HasValue), orderItem); } - /// - /// Gets the total number of dispatched items - /// - /// Order item - /// Total number of dispatched items - public static int GetDispatchedItemsCount(this OrderItem orderItem) - { - Guard.NotNull(orderItem, nameof(orderItem)); - - var shipments = orderItem.Order.Shipments.ToList(); - return AggregateQuantity(shipments.Where(x => x.ShippedDateUtc.HasValue), orderItem); - } - - /// - /// Gets the total number of not dispatched items - /// - /// Order item - /// Total number of not dispatched items - public static int GetNotDispatchedItemsCount(this OrderItem orderItem) + /// + /// Gets the total number of not dispatched items + /// + /// Order item + /// Total number of not dispatched items + public static int GetNotDispatchedItemsCount(this OrderItem orderItem) { - Guard.NotNull(orderItem, nameof(orderItem)); + Guard.NotNull(orderItem, nameof(orderItem)); var shipments = orderItem.Order.Shipments.ToList(); - return AggregateQuantity(shipments.Where(x => !x.ShippedDateUtc.HasValue), orderItem); + return AggregateQuantity(shipments.Where(x => !x.ShippedDateUtc.HasValue), orderItem); } - /// - /// Gets the total number of already delivered items - /// - /// Order item - /// Total number of already delivered items - public static int GetDeliveredItemsCount(this OrderItem orderItem) - { - Guard.NotNull(orderItem, nameof(orderItem)); - - var shipments = orderItem.Order.Shipments.ToList(); - return AggregateQuantity(shipments.Where(x => x.DeliveryDateUtc.HasValue), orderItem); - } - - /// - /// Gets the total number of not delivered items - /// - /// Order item - /// Total number of already delivered items - public static int GetNotDeliveredItemsCount(this OrderItem orderItem) - { - Guard.NotNull(orderItem, nameof(orderItem)); - - var shipments = orderItem.Order.Shipments.ToList(); - return AggregateQuantity(shipments.Where(x => !x.DeliveryDateUtc.HasValue), orderItem); - } - } + /// + /// Gets the total number of already delivered items + /// + /// Order item + /// Total number of already delivered items + public static int GetDeliveredItemsCount(this OrderItem orderItem) + { + Guard.NotNull(orderItem, nameof(orderItem)); + + var shipments = orderItem.Order.Shipments.ToList(); + return AggregateQuantity(shipments.Where(x => x.DeliveryDateUtc.HasValue), orderItem); + } + + /// + /// Gets the total number of not delivered items + /// + /// Order item + /// Total number of already delivered items + public static int GetNotDeliveredItemsCount(this OrderItem orderItem) + { + Guard.NotNull(orderItem, nameof(orderItem)); + + var shipments = orderItem.Order.Shipments.ToList(); + return AggregateQuantity(shipments.Where(x => !x.DeliveryDateUtc.HasValue), orderItem); + } + } } diff --git a/src/Libraries/SmartStore.Services/Orders/OrderMessageFactoryExtensions.cs b/src/Libraries/SmartStore.Services/Orders/OrderMessageFactoryExtensions.cs index 50bb45426e..e640cf85d1 100644 --- a/src/Libraries/SmartStore.Services/Orders/OrderMessageFactoryExtensions.cs +++ b/src/Libraries/SmartStore.Services/Orders/OrderMessageFactoryExtensions.cs @@ -6,121 +6,121 @@ namespace SmartStore.Services.Orders { - public static class OrderMessageFactoryExtensions - { - /// - /// Sends an order placed notification to a store owner - /// - public static CreateMessageResult SendOrderPlacedStoreOwnerNotification(this IMessageFactory factory, Order order, int languageId = 0) - { - Guard.NotNull(order, nameof(order)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.OrderPlacedStoreOwner, languageId, order.StoreId), true, order, order.Customer); - } - - /// - /// Sends an order placed notification to a customer - /// - public static CreateMessageResult SendOrderPlacedCustomerNotification(this IMessageFactory factory, Order order, int languageId = 0) - { - Guard.NotNull(order, nameof(order)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.OrderPlacedCustomer, languageId, order.StoreId, order.Customer), true, order); - } - - /// - /// Sends a shipment sent notification to a customer - /// - public static CreateMessageResult SendShipmentSentCustomerNotification(this IMessageFactory factory, Shipment shipment, int languageId = 0) - { - Guard.NotNull(shipment, nameof(shipment)); - Guard.NotNull(shipment.Order, nameof(shipment.Order)); - - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.ShipmentSentCustomer, languageId, shipment.Order.StoreId, shipment.Order.Customer), true, shipment, shipment.Order); - } - - /// - /// Sends a shipment delivered notification to a customer - /// - public static CreateMessageResult SendShipmentDeliveredCustomerNotification(this IMessageFactory factory, Shipment shipment, int languageId = 0) - { - Guard.NotNull(shipment, nameof(shipment)); - Guard.NotNull(shipment.Order, nameof(shipment.Order)); - - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.ShipmentDeliveredCustomer, languageId, shipment.Order.StoreId, shipment.Order.Customer), true, shipment, shipment.Order); - } - - /// - /// Sends an order completed notification to a customer - /// - public static CreateMessageResult SendOrderCompletedCustomerNotification(this IMessageFactory factory, Order order, int languageId = 0) - { - Guard.NotNull(order, nameof(order)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.OrderCompletedCustomer, languageId, order.StoreId, order.Customer), true, order); - } - - /// - /// Sends an order cancelled notification to a customer - /// - public static CreateMessageResult SendOrderCancelledCustomerNotification(this IMessageFactory factory, Order order, int languageId = 0) - { - Guard.NotNull(order, nameof(order)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.OrderCancelledCustomer, languageId, order.StoreId, order.Customer), true, order); - } - - /// - /// Sends a new order note added notification to a customer - /// - public static CreateMessageResult SendNewOrderNoteAddedCustomerNotification(this IMessageFactory factory, OrderNote orderNote, int languageId = 0) - { - Guard.NotNull(orderNote, nameof(orderNote)); - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.OrderNoteAddedCustomer, languageId, orderNote.Order?.StoreId, orderNote.Order?.Customer), true, orderNote, orderNote.Order); - } - - /// - /// Sends a "Recurring payment cancelled" notification to a store owner - /// - public static CreateMessageResult SendRecurringPaymentCancelledStoreOwnerNotification(this IMessageFactory factory, RecurringPayment recurringPayment, int languageId = 0) - { - Guard.NotNull(recurringPayment, nameof(recurringPayment)); - - var order = recurringPayment.InitialOrder; - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.RecurringPaymentCancelledStoreOwner, languageId, order?.StoreId, order?.Customer), true, - recurringPayment, order); - } - - /// - /// Sends 'New Return Request' message to a store owner - /// - public static CreateMessageResult SendNewReturnRequestStoreOwnerNotification(this IMessageFactory factory, ReturnRequest returnRequest, OrderItem orderItem, int languageId = 0) - { - Guard.NotNull(returnRequest, nameof(returnRequest)); - Guard.NotNull(orderItem, nameof(orderItem)); - - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewReturnRequestStoreOwner, languageId, orderItem.Order?.StoreId, returnRequest.Customer), true, - returnRequest, orderItem, orderItem.Order, orderItem.Product); - } - - /// - /// Sends 'Return Request status changed' message to a customer - /// - public static CreateMessageResult SendReturnRequestStatusChangedCustomerNotification(this IMessageFactory factory, ReturnRequest returnRequest, OrderItem orderItem, int languageId = 0) - { - Guard.NotNull(returnRequest, nameof(returnRequest)); - Guard.NotNull(orderItem, nameof(orderItem)); - - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.ReturnRequestStatusChangedCustomer, languageId, orderItem.Order?.StoreId, returnRequest.Customer), true, returnRequest); - } - - /// - /// Sends a gift card notification - /// - public static CreateMessageResult SendGiftCardNotification(this IMessageFactory factory, GiftCard giftCard, int languageId = 0) - { - Guard.NotNull(giftCard, nameof(giftCard)); - - var orderItem = giftCard.PurchasedWithOrderItem; - var customer = orderItem?.Order?.Customer; - var storeId = orderItem?.Order?.StoreId; - return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.GiftCardCustomer, languageId, storeId, customer), true, giftCard, orderItem, orderItem?.Product); - } - } + public static class OrderMessageFactoryExtensions + { + /// + /// Sends an order placed notification to a store owner + /// + public static CreateMessageResult SendOrderPlacedStoreOwnerNotification(this IMessageFactory factory, Order order, int languageId = 0) + { + Guard.NotNull(order, nameof(order)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.OrderPlacedStoreOwner, languageId, order.StoreId), true, order, order.Customer); + } + + /// + /// Sends an order placed notification to a customer + /// + public static CreateMessageResult SendOrderPlacedCustomerNotification(this IMessageFactory factory, Order order, int languageId = 0) + { + Guard.NotNull(order, nameof(order)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.OrderPlacedCustomer, languageId, order.StoreId, order.Customer), true, order); + } + + /// + /// Sends a shipment sent notification to a customer + /// + public static CreateMessageResult SendShipmentSentCustomerNotification(this IMessageFactory factory, Shipment shipment, int languageId = 0) + { + Guard.NotNull(shipment, nameof(shipment)); + Guard.NotNull(shipment.Order, nameof(shipment.Order)); + + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.ShipmentSentCustomer, languageId, shipment.Order.StoreId, shipment.Order.Customer), true, shipment, shipment.Order); + } + + /// + /// Sends a shipment delivered notification to a customer + /// + public static CreateMessageResult SendShipmentDeliveredCustomerNotification(this IMessageFactory factory, Shipment shipment, int languageId = 0) + { + Guard.NotNull(shipment, nameof(shipment)); + Guard.NotNull(shipment.Order, nameof(shipment.Order)); + + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.ShipmentDeliveredCustomer, languageId, shipment.Order.StoreId, shipment.Order.Customer), true, shipment, shipment.Order); + } + + /// + /// Sends an order completed notification to a customer + /// + public static CreateMessageResult SendOrderCompletedCustomerNotification(this IMessageFactory factory, Order order, int languageId = 0) + { + Guard.NotNull(order, nameof(order)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.OrderCompletedCustomer, languageId, order.StoreId, order.Customer), true, order); + } + + /// + /// Sends an order cancelled notification to a customer + /// + public static CreateMessageResult SendOrderCancelledCustomerNotification(this IMessageFactory factory, Order order, int languageId = 0) + { + Guard.NotNull(order, nameof(order)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.OrderCancelledCustomer, languageId, order.StoreId, order.Customer), true, order); + } + + /// + /// Sends a new order note added notification to a customer + /// + public static CreateMessageResult SendNewOrderNoteAddedCustomerNotification(this IMessageFactory factory, OrderNote orderNote, int languageId = 0) + { + Guard.NotNull(orderNote, nameof(orderNote)); + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.OrderNoteAddedCustomer, languageId, orderNote.Order?.StoreId, orderNote.Order?.Customer), true, orderNote, orderNote.Order); + } + + /// + /// Sends a "Recurring payment cancelled" notification to a store owner + /// + public static CreateMessageResult SendRecurringPaymentCancelledStoreOwnerNotification(this IMessageFactory factory, RecurringPayment recurringPayment, int languageId = 0) + { + Guard.NotNull(recurringPayment, nameof(recurringPayment)); + + var order = recurringPayment.InitialOrder; + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.RecurringPaymentCancelledStoreOwner, languageId, order?.StoreId, order?.Customer), true, + recurringPayment, order); + } + + /// + /// Sends 'New Return Request' message to a store owner + /// + public static CreateMessageResult SendNewReturnRequestStoreOwnerNotification(this IMessageFactory factory, ReturnRequest returnRequest, OrderItem orderItem, int languageId = 0) + { + Guard.NotNull(returnRequest, nameof(returnRequest)); + Guard.NotNull(orderItem, nameof(orderItem)); + + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.NewReturnRequestStoreOwner, languageId, orderItem.Order?.StoreId, returnRequest.Customer), true, + returnRequest, orderItem, orderItem.Order, orderItem.Product); + } + + /// + /// Sends 'Return Request status changed' message to a customer + /// + public static CreateMessageResult SendReturnRequestStatusChangedCustomerNotification(this IMessageFactory factory, ReturnRequest returnRequest, OrderItem orderItem, int languageId = 0) + { + Guard.NotNull(returnRequest, nameof(returnRequest)); + Guard.NotNull(orderItem, nameof(orderItem)); + + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.ReturnRequestStatusChangedCustomer, languageId, orderItem.Order?.StoreId, returnRequest.Customer), true, returnRequest); + } + + /// + /// Sends a gift card notification + /// + public static CreateMessageResult SendGiftCardNotification(this IMessageFactory factory, GiftCard giftCard, int languageId = 0) + { + Guard.NotNull(giftCard, nameof(giftCard)); + + var orderItem = giftCard.PurchasedWithOrderItem; + var customer = orderItem?.Order?.Customer; + var storeId = orderItem?.Order?.StoreId; + return factory.CreateMessage(MessageContext.Create(MessageTemplateNames.GiftCardCustomer, languageId, storeId, customer), true, giftCard, orderItem, orderItem?.Product); + } + } } diff --git a/src/Libraries/SmartStore.Services/Orders/OrderProcessingService.cs b/src/Libraries/SmartStore.Services/Orders/OrderProcessingService.cs index 492edc7d35..bc58b098d2 100644 --- a/src/Libraries/SmartStore.Services/Orders/OrderProcessingService.cs +++ b/src/Libraries/SmartStore.Services/Orders/OrderProcessingService.cs @@ -25,6 +25,7 @@ using SmartStore.Services.Directory; using SmartStore.Services.Discounts; using SmartStore.Services.Localization; +using SmartStore.Services.Media; using SmartStore.Services.Messages; using SmartStore.Services.Payments; using SmartStore.Services.Security; @@ -36,7 +37,7 @@ namespace SmartStore.Services.Orders public partial class OrderProcessingService : IOrderProcessingService { #region Fields - + private readonly IOrderService _orderService; private readonly IWebHelper _webHelper; private readonly ILocalizationService _localizationService; @@ -51,6 +52,7 @@ public partial class OrderProcessingService : IOrderProcessingService private readonly IGiftCardService _giftCardService; private readonly IShoppingCartService _shoppingCartService; private readonly ICheckoutAttributeFormatter _checkoutAttributeFormatter; + private readonly ICheckoutAttributeParser _checkoutAttributeParser; private readonly IShippingService _shippingService; private readonly IShipmentService _shipmentService; private readonly ITaxService _taxService; @@ -58,22 +60,21 @@ public partial class OrderProcessingService : IOrderProcessingService private readonly IDiscountService _discountService; private readonly IEncryptionService _encryptionService; private readonly IWorkContext _workContext; - private readonly IStoreContext _storeContext; + private readonly IStoreContext _storeContext; private readonly IMessageFactory _messageFactory; private readonly ICustomerActivityService _customerActivityService; private readonly ICurrencyService _currencyService; - private readonly IAffiliateService _affiliateService; + private readonly IAffiliateService _affiliateService; private readonly IEventPublisher _eventPublisher; - private readonly IGenericAttributeService _genericAttributeService; - private readonly INewsLetterSubscriptionService _newsLetterSubscriptionService; + private readonly IGenericAttributeService _genericAttributeService; + private readonly INewsLetterSubscriptionService _newsLetterSubscriptionService; + private readonly IDownloadService _downloadService; - private readonly PaymentSettings _paymentSettings; private readonly RewardPointsSettings _rewardPointsSettings; private readonly OrderSettings _orderSettings; private readonly TaxSettings _taxSettings; private readonly LocalizationSettings _localizationSettings; - private readonly CurrencySettings _currencySettings; - private readonly ShoppingCartSettings _shoppingCartSettings; + private readonly ShoppingCartSettings _shoppingCartSettings; private readonly CatalogSettings _catalogSettings; #endregion @@ -81,7 +82,7 @@ public partial class OrderProcessingService : IOrderProcessingService #region Ctor public OrderProcessingService( - IOrderService orderService, + IOrderService orderService, IWebHelper webHelper, ILocalizationService localizationService, ILanguageService languageService, @@ -95,6 +96,7 @@ public OrderProcessingService( IGiftCardService giftCardService, IShoppingCartService shoppingCartService, ICheckoutAttributeFormatter checkoutAttributeFormatter, + ICheckoutAttributeParser checkoutAttributeParser, IShippingService shippingService, IShipmentService shipmentService, ITaxService taxService, @@ -102,21 +104,20 @@ public OrderProcessingService( IDiscountService discountService, IEncryptionService encryptionService, IWorkContext workContext, - IStoreContext storeContext, + IStoreContext storeContext, IMessageFactory messageFactory, ICustomerActivityService customerActivityService, ICurrencyService currencyService, - IAffiliateService affiliateService, + IAffiliateService affiliateService, IEventPublisher eventPublisher, - IGenericAttributeService genericAttributeService, - INewsLetterSubscriptionService newsLetterSubscriptionService, - PaymentSettings paymentSettings, + IGenericAttributeService genericAttributeService, + INewsLetterSubscriptionService newsLetterSubscriptionService, + IDownloadService downloadService, RewardPointsSettings rewardPointsSettings, OrderSettings orderSettings, TaxSettings taxSettings, LocalizationSettings localizationSettings, - CurrencySettings currencySettings, - ShoppingCartSettings shoppingCartSettings, + ShoppingCartSettings shoppingCartSettings, CatalogSettings catalogSettings) { _orderService = orderService; @@ -133,8 +134,9 @@ public OrderProcessingService( _giftCardService = giftCardService; _shoppingCartService = shoppingCartService; _checkoutAttributeFormatter = checkoutAttributeFormatter; + _checkoutAttributeParser = checkoutAttributeParser; _workContext = workContext; - _storeContext = storeContext; + _storeContext = storeContext; _messageFactory = messageFactory; _shippingService = shippingService; _shipmentService = shipmentService; @@ -144,54 +146,50 @@ public OrderProcessingService( _encryptionService = encryptionService; _customerActivityService = customerActivityService; _currencyService = currencyService; - _affiliateService = affiliateService; + _affiliateService = affiliateService; _eventPublisher = eventPublisher; - _genericAttributeService = genericAttributeService; - _newsLetterSubscriptionService = newsLetterSubscriptionService; - _paymentSettings = paymentSettings; + _genericAttributeService = genericAttributeService; + _newsLetterSubscriptionService = newsLetterSubscriptionService; + _downloadService = downloadService; _rewardPointsSettings = rewardPointsSettings; _orderSettings = orderSettings; _taxSettings = taxSettings; _localizationSettings = localizationSettings; - _currencySettings = currencySettings; - _shoppingCartSettings = shoppingCartSettings; + _shoppingCartSettings = shoppingCartSettings; _catalogSettings = catalogSettings; + } - T = NullLocalizer.Instance; - Logger = NullLogger.Instance; - } + public Localizer T { get; set; } = NullLocalizer.Instance; + public ILogger Logger { get; set; } = NullLogger.Instance; - public Localizer T { get; set; } - public ILogger Logger { get; set; } + #endregion - #endregion + #region Utilities - #region Utilities + protected string FormatTaxRates(SortedDictionary taxRates) + { + var result = string.Empty; - protected string FormatTaxRates(SortedDictionary taxRates) - { - var result = string.Empty; + foreach (var rate in taxRates) + { + result += "{0}:{1}; ".FormatInvariant( + rate.Key.ToString(CultureInfo.InvariantCulture), + rate.Value.ToString(CultureInfo.InvariantCulture)); + } - foreach (var rate in taxRates) - { - result += "{0}:{1}; ".FormatInvariant( - rate.Key.ToString(CultureInfo.InvariantCulture), - rate.Value.ToString(CultureInfo.InvariantCulture)); - } - - return result; - } + return result; + } - private void ProcessErrors(Order order, IList errors, string messageKey) - { - if (errors.Any()) - { - var msg = string.Concat(T(messageKey, order.GetOrderNumber()), " ", string.Join(" ", errors)); + private void ProcessErrors(Order order, IList errors, string messageKey) + { + if (errors.Any()) + { + var msg = string.Concat(T(messageKey, order.GetOrderNumber()), " ", string.Join(" ", errors)); - _orderService.AddOrderNote(order, msg); - Logger.Error(msg); - } - } + _orderService.AddOrderNote(order, msg); + Logger.Error(msg); + } + } /// /// Award reward points @@ -210,12 +208,12 @@ protected void AwardRewardPoints(Order order, decimal? amount = null) if (order.Customer == null || order.Customer.IsGuest()) return; - //Ensure that reward points were not added before. We should not add reward points if they were already earned for this order - if (order.RewardPointsWereAdded) - return; + //Ensure that reward points were not added before. We should not add reward points if they were already earned for this order + if (order.RewardPointsWereAdded) + return; - // Truncate same as Floor for positive amounts - var points = (int)Math.Truncate((amount ?? order.OrderTotal) / _rewardPointsSettings.PointsForPurchases_Amount * _rewardPointsSettings.PointsForPurchases_Points); + // Truncate same as Floor for positive amounts + var points = (int)Math.Truncate((amount ?? order.OrderTotal) / _rewardPointsSettings.PointsForPurchases_Amount * _rewardPointsSettings.PointsForPurchases_Points); if (points == 0) return; @@ -239,32 +237,32 @@ protected void ReduceRewardPoints(Order order, decimal? amount = null) if (_rewardPointsSettings.PointsForPurchases_Amount <= decimal.Zero) return; - //ensure that reward points were already earned for this order before - if (!order.RewardPointsWereAdded) - return; + //ensure that reward points were already earned for this order before + if (!order.RewardPointsWereAdded) + return; //Ensure that reward points are applied only to registered users if (order.Customer == null || order.Customer.IsGuest()) return; - // Truncate increases the risk of inaccuracy of rounding - //int points = (int)Math.Truncate((amount ?? order.OrderTotal) / _rewardPointsSettings.PointsForPurchases_Amount * _rewardPointsSettings.PointsForPurchases_Points); + // Truncate increases the risk of inaccuracy of rounding + //int points = (int)Math.Truncate((amount ?? order.OrderTotal) / _rewardPointsSettings.PointsForPurchases_Amount * _rewardPointsSettings.PointsForPurchases_Points); - int points = (int)Math.Round((amount ?? order.OrderTotal) / _rewardPointsSettings.PointsForPurchases_Amount * _rewardPointsSettings.PointsForPurchases_Points); + int points = (int)Math.Round((amount ?? order.OrderTotal) / _rewardPointsSettings.PointsForPurchases_Amount * _rewardPointsSettings.PointsForPurchases_Points); - if (order.RewardPointsRemaining.HasValue && order.RewardPointsRemaining.Value < points) - points = order.RewardPointsRemaining.Value; + if (order.RewardPointsRemaining.HasValue && order.RewardPointsRemaining.Value < points) + points = order.RewardPointsRemaining.Value; - if (points == 0) - return; + if (points == 0) + return; //reduce reward points order.Customer.AddRewardPointsHistoryEntry(-points, T("RewardPoints.Message.ReducedForOrder", order.GetOrderNumber())); - if (!order.RewardPointsRemaining.HasValue) - order.RewardPointsRemaining = (int)Math.Round(order.OrderTotal / _rewardPointsSettings.PointsForPurchases_Amount * _rewardPointsSettings.PointsForPurchases_Points); + if (!order.RewardPointsRemaining.HasValue) + order.RewardPointsRemaining = (int)Math.Round(order.OrderTotal / _rewardPointsSettings.PointsForPurchases_Amount * _rewardPointsSettings.PointsForPurchases_Points); - order.RewardPointsRemaining = Math.Max(order.RewardPointsRemaining.Value - points, 0); + order.RewardPointsRemaining = Math.Max(order.RewardPointsRemaining.Value - points, 0); _orderService.UpdateOrder(order); } @@ -295,7 +293,7 @@ protected void SetActivatedValueForPurchasedGiftCards(Order order, bool activate customerLang = _languageService.GetAllLanguages().FirstOrDefault(); var qe = _messageFactory.SendGiftCardNotification(gc, customerLang.Id); - isRecipientNotified = qe?.Email.Id != null; + isRecipientNotified = qe?.Email.Id != null; } } @@ -333,7 +331,7 @@ protected void SetOrderStatus(Order order, OrderStatus os, bool notifyCustomer) order.OrderStatusId = (int)os; _orderService.UpdateOrder(order); - _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderStatusChanged", os.GetLocalizedEnum(_localizationService))); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderStatusChanged", os.GetLocalizedEnum(_localizationService))); if (prevOrderStatus != OrderStatus.Complete && os == OrderStatus.Complete && notifyCustomer) { @@ -341,7 +339,7 @@ protected void SetOrderStatus(Order order, OrderStatus os, bool notifyCustomer) var msg = _messageFactory.SendOrderCompletedCustomerNotification(order, order.CustomerLanguageId); if (msg?.Email?.Id != null) { - _orderService.AddOrderNote(order, T("Admin.OrderNotice.CustomerCompletedEmailQueued", msg.Email.Id)); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.CustomerCompletedEmailQueued", msg.Email.Id)); } } @@ -351,7 +349,7 @@ protected void SetOrderStatus(Order order, OrderStatus os, bool notifyCustomer) var msg = _messageFactory.SendOrderCancelledCustomerNotification(order, order.CustomerLanguageId); if (msg?.Email?.Id != null) { - _orderService.AddOrderNote(order, T("Admin.OrderNotice.CustomerCancelledEmailQueued", msg.Email.Id)); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.CustomerCancelledEmailQueued", msg.Email.Id)); } } @@ -527,24 +525,34 @@ public virtual IList GetOrderPlacementWarnings( } } - // Min totals validation. - var minOrderSubtotalAmountOk = ValidateMinOrderSubtotalAmount(cart); - if (!minOrderSubtotalAmountOk) + // Order total minimum validation + var customerRoleIds = customer.GetRoleIds(); + var (isAboveOrderTotalMinimum, orderTotalMinimum) = IsAboveOrderTotalMinimum(cart, customerRoleIds); + if (!isAboveOrderTotalMinimum) { - var minOrderSubtotalAmount = _currencyService.ConvertFromPrimaryStoreCurrency(_orderSettings.MinOrderSubtotalAmount, _workContext.WorkingCurrency); - warnings.Add(T("Checkout.MinOrderSubtotalAmount", _priceFormatter.FormatPrice(minOrderSubtotalAmount, true, false))); + orderTotalMinimum = _currencyService.ConvertFromPrimaryStoreCurrency( + orderTotalMinimum, + _workContext.WorkingCurrency); + + var msg = T("Checkout.MinOrderSubtotalAmount", _priceFormatter.FormatPrice(orderTotalMinimum, true, false)); + warnings.Add(msg); return warnings; } - var minOrderTotalAmountOk = ValidateMinOrderTotalAmount(cart); - if (!minOrderTotalAmountOk) + // Order total maximum validation + var (isBelowOrderTotalMaximum, orderTotalMaximum) = IsBelowOrderTotalMaximum(cart, customerRoleIds); + if (isAboveOrderTotalMinimum && !isBelowOrderTotalMaximum) { - var minOrderTotalAmount = _currencyService.ConvertFromPrimaryStoreCurrency(_orderSettings.MinOrderTotalAmount, _workContext.WorkingCurrency); - warnings.Add(T("Checkout.MinOrderTotalAmount", _priceFormatter.FormatPrice(minOrderTotalAmount, true, false))); + orderTotalMaximum = _currencyService.ConvertFromPrimaryStoreCurrency( + orderTotalMaximum, + _workContext.WorkingCurrency); + + var msg = T("Checkout.MaxOrderSubtotalAmount", _priceFormatter.FormatPrice(orderTotalMaximum, true, false)); + warnings.Add(msg); return warnings; } - // Total validations. + // Total validations var orderShippingTotalInclTax = _orderTotalCalculationService.GetShoppingCartShippingTotal(cart, true, out var orderShippingTaxRate, out var shippingTotalDiscount); var orderShippingTotalExclTax = _orderTotalCalculationService.GetShoppingCartShippingTotal(cart, false); @@ -633,12 +641,8 @@ public virtual IList GetOrderPlacementWarnings( // Payment. if (!warnings.Any() && !skipPaymentWorkflow) { - var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(paymentMethodSystemName); - if (paymentMethod == null) - { - warnings.Add(T("Payment.CouldNotLoadMethod")); - } - else if (!paymentMethod.IsPaymentMethodActive(_paymentSettings)) + var isPaymentMethodActive = _paymentService.IsPaymentMethodActive(paymentMethodSystemName, customer, cart, processPaymentRequest.StoreId); + if (!isPaymentMethodActive) { warnings.Add(T("Payment.MethodNotAvailable")); } @@ -707,7 +711,7 @@ public virtual IList GetOrderPlacementWarnings( } public virtual PlaceOrderResult PlaceOrder( - ProcessPaymentRequest processPaymentRequest, + ProcessPaymentRequest processPaymentRequest, Dictionary extraData) { // Think about moving functionality of processing recurring orders (after the initial order was placed) to ProcessNextRecurringPayment() method. @@ -719,7 +723,7 @@ public virtual PlaceOrderResult PlaceOrder( } var result = new PlaceOrderResult(); - var utcNow = DateTime.UtcNow; + var utcNow = DateTime.UtcNow; try { @@ -741,13 +745,13 @@ public virtual PlaceOrderResult PlaceOrder( processPaymentRequest.PaymentMethodSystemName = initialOrder.PaymentMethodSystemName; } - // Affilites. - var affiliateId = 0; - var affiliate = _affiliateService.GetAffiliateById(customer.AffiliateId); - if (affiliate != null && affiliate.Active && !affiliate.Deleted) - { - affiliateId = affiliate.Id; - } + // Affilites. + var affiliateId = 0; + var affiliate = _affiliateService.GetAffiliateById(customer.AffiliateId); + if (affiliate != null && affiliate.Active && !affiliate.Deleted) + { + affiliateId = affiliate.Id; + } // Customer currency. var customerCurrencyCode = string.Empty; @@ -770,24 +774,24 @@ public virtual PlaceOrderResult PlaceOrder( // Customer language. Language customerLanguage = null; if (!processPaymentRequest.IsRecurringPayment) - { + { customerLanguage = _languageService.GetLanguageById(customer.GetAttribute(SystemCustomerAttributeNames.LanguageId, processPaymentRequest.StoreId)); } - else - { - customerLanguage = _languageService.GetLanguageById(initialOrder.CustomerLanguageId); - } + else + { + customerLanguage = _languageService.GetLanguageById(initialOrder.CustomerLanguageId); + } + + if (customerLanguage == null || !customerLanguage.Published) + { + customerLanguage = _workContext.WorkingLanguage; + } - if (customerLanguage == null || !customerLanguage.Published) - { - customerLanguage = _workContext.WorkingLanguage; - } - // Tax display type. var customerTaxDisplayType = TaxDisplayType.IncludingTax; if (!processPaymentRequest.IsRecurringPayment) { - customerTaxDisplayType = _workContext.GetTaxDisplayTypeFor(customer, processPaymentRequest.StoreId); + customerTaxDisplayType = _workContext.GetTaxDisplayTypeFor(customer, processPaymentRequest.StoreId); } else { @@ -798,12 +802,12 @@ public virtual PlaceOrderResult PlaceOrder( string checkoutAttributeDescription, checkoutAttributesXml; if (!processPaymentRequest.IsRecurringPayment) { - checkoutAttributesXml = customer.GetAttribute(SystemCustomerAttributeNames.CheckoutAttributes); - checkoutAttributeDescription = _checkoutAttributeFormatter.FormatAttributes(checkoutAttributesXml, customer); + checkoutAttributesXml = customer.GetAttribute(SystemCustomerAttributeNames.CheckoutAttributes); + checkoutAttributeDescription = _checkoutAttributeFormatter.FormatAttributes(checkoutAttributesXml, customer); } else { - checkoutAttributesXml = initialOrder.CheckoutAttributesXml; + checkoutAttributesXml = initialOrder.CheckoutAttributesXml; checkoutAttributeDescription = initialOrder.CheckoutAttributeDescription; } @@ -872,7 +876,7 @@ public virtual PlaceOrderResult PlaceOrder( // Shipping total. decimal? orderShippingTotalInclTax, orderShippingTotalExclTax = null; - decimal orderShippingTaxRate = decimal.Zero; + decimal orderShippingTaxRate = decimal.Zero; if (!processPaymentRequest.IsRecurringPayment) { orderShippingTotalInclTax = _orderTotalCalculationService.GetShoppingCartShippingTotal(cart, true, out orderShippingTaxRate, out var shippingTotalDiscount); @@ -887,11 +891,11 @@ public virtual PlaceOrderResult PlaceOrder( { orderShippingTotalInclTax = initialOrder.OrderShippingInclTax; orderShippingTotalExclTax = initialOrder.OrderShippingExclTax; - orderShippingTaxRate = initialOrder.OrderShippingTaxRate; + orderShippingTaxRate = initialOrder.OrderShippingTaxRate; } // Payment total. - decimal paymentAdditionalFeeInclTax, paymentAdditionalFeeExclTax, paymentAdditionalFeeTaxRate; + decimal paymentAdditionalFeeInclTax, paymentAdditionalFeeExclTax, paymentAdditionalFeeTaxRate; if (!processPaymentRequest.IsRecurringPayment) { var paymentAdditionalFee = _paymentService.GetAdditionalHandlingFee(cart, processPaymentRequest.PaymentMethodSystemName); @@ -902,7 +906,7 @@ public virtual PlaceOrderResult PlaceOrder( { paymentAdditionalFeeInclTax = initialOrder.PaymentMethodAdditionalFeeInclTax; paymentAdditionalFeeExclTax = initialOrder.PaymentMethodAdditionalFeeExclTax; - paymentAdditionalFeeTaxRate = initialOrder.PaymentMethodAdditionalFeeTaxRate; + paymentAdditionalFeeTaxRate = initialOrder.PaymentMethodAdditionalFeeTaxRate; } // Tax total. @@ -914,13 +918,13 @@ public virtual PlaceOrderResult PlaceOrder( orderTaxTotal = _orderTotalCalculationService.GetTaxTotal(cart, out var taxRatesDictionary); // VAT number. - var customerVatStatus = (VatNumberStatus)customer.VatNumberStatusId; - if (_taxSettings.EuVatEnabled && customerVatStatus == VatNumberStatus.Valid) - { - vatNumber = customer.GetAttribute(SystemCustomerAttributeNames.VatNumber); - } + var customerVatStatus = (VatNumberStatus)customer.VatNumberStatusId; + if (_taxSettings.EuVatEnabled && customerVatStatus == VatNumberStatus.Valid) + { + vatNumber = customer.GetAttribute(SystemCustomerAttributeNames.VatNumber); + } - taxRates = FormatTaxRates(taxRatesDictionary); + taxRates = FormatTaxRates(taxRatesDictionary); } else { @@ -928,7 +932,7 @@ public virtual PlaceOrderResult PlaceOrder( vatNumber = initialOrder.VatNumber; } - processPaymentRequest.OrderTax = orderTaxTotal; + processPaymentRequest.OrderTax = orderTaxTotal; // Order total (and applied discounts, gift cards, reward points). ShoppingCartTotal cartTotal = null; @@ -937,11 +941,11 @@ public virtual PlaceOrderResult PlaceOrder( { cartTotal = _orderTotalCalculationService.GetShoppingCartTotal(cart); - // Discount history. - if (cartTotal.AppliedDiscount != null && !appliedDiscounts.Any(x => x.Id == cartTotal.AppliedDiscount.Id)) - { - appliedDiscounts.Add(cartTotal.AppliedDiscount); - } + // Discount history. + if (cartTotal.AppliedDiscount != null && !appliedDiscounts.Any(x => x.Id == cartTotal.AppliedDiscount.Id)) + { + appliedDiscounts.Add(cartTotal.AppliedDiscount); + } } else { @@ -953,66 +957,66 @@ public virtual PlaceOrderResult PlaceOrder( #endregion - #region Addresses & pre-payment workflow - - // Give payment processor the opportunity to fullfill billing address. - var preProcessPaymentResult = _paymentService.PreProcessPayment(processPaymentRequest); + #region Addresses & pre-payment workflow - if (!preProcessPaymentResult.Success) - { - result.Errors.AddRange(preProcessPaymentResult.Errors); - result.Errors.Add(T("Common.Error.PreProcessPayment")); - return result; - } + // Give payment processor the opportunity to fullfill billing address. + var preProcessPaymentResult = _paymentService.PreProcessPayment(processPaymentRequest); + + if (!preProcessPaymentResult.Success) + { + result.Errors.AddRange(preProcessPaymentResult.Errors); + result.Errors.Add(T("Common.Error.PreProcessPayment")); + return result; + } var billingAddress = !processPaymentRequest.IsRecurringPayment ? (Address)customer.BillingAddress.Clone() : (Address)initialOrder.BillingAddress.Clone(); - Address shippingAddress = null; - if (shoppingCartRequiresShipping) - { + Address shippingAddress = null; + if (shoppingCartRequiresShipping) + { shippingAddress = !processPaymentRequest.IsRecurringPayment ? (Address)customer.ShippingAddress.Clone() : (Address)initialOrder.ShippingAddress.Clone(); - } + } - #endregion + #endregion - #region Payment workflow + #region Payment workflow - // Skip payment workflow if order total equals zero. + // Skip payment workflow if order total equals zero. var skipPaymentWorkflow = cartTotal.TotalAmount.Value == decimal.Zero; // Payment workflow. Provider paymentMethod = null; - if (!skipPaymentWorkflow) - { - paymentMethod = _paymentService.LoadPaymentMethodBySystemName(processPaymentRequest.PaymentMethodSystemName); - } - else - { - processPaymentRequest.PaymentMethodSystemName = ""; - } + if (!skipPaymentWorkflow) + { + paymentMethod = _paymentService.LoadPaymentMethodBySystemName(processPaymentRequest.PaymentMethodSystemName); + } + else + { + processPaymentRequest.PaymentMethodSystemName = ""; + } // Recurring or standard shopping cart? var isRecurringShoppingCart = false; - if (!processPaymentRequest.IsRecurringPayment) - { - isRecurringShoppingCart = cart.IsRecurring(); - if (isRecurringShoppingCart) - { - var unused = cart.GetRecurringCycleInfo(_localizationService, out var recurringCycleLength, out var recurringCyclePeriod, out var recurringTotalCycles); - - processPaymentRequest.RecurringCycleLength = recurringCycleLength; - processPaymentRequest.RecurringCyclePeriod = recurringCyclePeriod; - processPaymentRequest.RecurringTotalCycles = recurringTotalCycles; - } - } - else - { - isRecurringShoppingCart = true; - } + if (!processPaymentRequest.IsRecurringPayment) + { + isRecurringShoppingCart = cart.IsRecurring(); + if (isRecurringShoppingCart) + { + var unused = cart.GetRecurringCycleInfo(_localizationService, out var recurringCycleLength, out var recurringCyclePeriod, out var recurringTotalCycles); + + processPaymentRequest.RecurringCycleLength = recurringCycleLength; + processPaymentRequest.RecurringCyclePeriod = recurringCyclePeriod; + processPaymentRequest.RecurringTotalCycles = recurringTotalCycles; + } + } + else + { + isRecurringShoppingCart = true; + } // Process payment. ProcessPaymentResult processPaymentResult = null; @@ -1058,7 +1062,7 @@ public virtual PlaceOrderResult PlaceOrder( processPaymentRequest.CreditCardExpireMonth = initialOrder.AllowStoringCreditCardNumber ? Convert.ToInt32(_encryptionService.DecryptText(initialOrder.CardExpirationMonth)) : 0; processPaymentRequest.CreditCardExpireYear = initialOrder.AllowStoringCreditCardNumber ? Convert.ToInt32(_encryptionService.DecryptText(initialOrder.CardExpirationYear)) : 0; } - catch {} + catch { } var recurringPaymentType = _paymentService.GetRecurringPaymentType(processPaymentRequest.PaymentMethodSystemName); switch (recurringPaymentType) @@ -1073,8 +1077,8 @@ public virtual PlaceOrderResult PlaceOrder( processPaymentResult = new ProcessPaymentResult(); break; default: - throw new SmartException(T("Payment.RecurringPaymentTypeUnknown")); - } + throw new SmartException(T("Payment.RecurringPaymentTypeUnknown")); + } } else { @@ -1102,14 +1106,14 @@ public virtual PlaceOrderResult PlaceOrder( #region Save order details var shippingStatus = ShippingStatus.NotYetShipped; - if (!shoppingCartRequiresShipping) - { - shippingStatus = ShippingStatus.ShippingNotRequired; - } - + if (!shoppingCartRequiresShipping) + { + shippingStatus = ShippingStatus.ShippingNotRequired; + } + var order = new Order { - StoreId = processPaymentRequest.StoreId, + StoreId = processPaymentRequest.StoreId, OrderGuid = processPaymentRequest.OrderGuid, CustomerId = customer.Id, CustomerLanguageId = customerLanguage.Id, @@ -1121,22 +1125,22 @@ public virtual PlaceOrderResult PlaceOrder( OrderSubTotalDiscountExclTax = orderSubTotalDiscountExclTax, OrderShippingInclTax = orderShippingTotalInclTax.Value, OrderShippingExclTax = orderShippingTotalExclTax.Value, - OrderShippingTaxRate = orderShippingTaxRate, + OrderShippingTaxRate = orderShippingTaxRate, PaymentMethodAdditionalFeeInclTax = paymentAdditionalFeeInclTax, PaymentMethodAdditionalFeeExclTax = paymentAdditionalFeeExclTax, - PaymentMethodAdditionalFeeTaxRate = paymentAdditionalFeeTaxRate, + PaymentMethodAdditionalFeeTaxRate = paymentAdditionalFeeTaxRate, TaxRates = taxRates, OrderTax = orderTaxTotal, OrderTotalRounding = cartTotal.RoundingAmount, OrderTotal = cartTotal.TotalAmount.Value, RefundedAmount = decimal.Zero, OrderDiscount = cartTotal.DiscountAmount, - CreditBalance = cartTotal.CreditBalance, + CreditBalance = cartTotal.CreditBalance, CheckoutAttributeDescription = checkoutAttributeDescription, CheckoutAttributesXml = checkoutAttributesXml, CustomerCurrencyCode = customerCurrencyCode, CurrencyRate = customerCurrencyRate, - AffiliateId = affiliateId, + AffiliateId = affiliateId, OrderStatus = OrderStatus.Pending, AllowStoringCreditCardNumber = processPaymentResult.AllowStoringCreditCardNumber, CardType = processPaymentResult.AllowStoringCreditCardNumber ? _encryptionService.EncryptText(processPaymentRequest.CreditCardType) : string.Empty, @@ -1173,10 +1177,10 @@ public virtual PlaceOrderResult PlaceOrder( CustomerOrderComment = extraData.ContainsKey("CustomerComment") ? extraData["CustomerComment"] : "" }; - if (extraData.ContainsKey("AcceptThirdPartyEmailHandOver") && _shoppingCartSettings.ThirdPartyEmailHandOver != CheckoutThirdPartyEmailHandOver.None) - { - order.AcceptThirdPartyEmailHandOver = extraData["AcceptThirdPartyEmailHandOver"].ToBool(); - } + if (extraData.ContainsKey("AcceptThirdPartyEmailHandOver") && _shoppingCartSettings.ThirdPartyEmailHandOver != CheckoutThirdPartyEmailHandOver.None) + { + order.AcceptThirdPartyEmailHandOver = extraData["AcceptThirdPartyEmailHandOver"].ToBool(); + } _orderService.InsertOrder(order); @@ -1187,14 +1191,14 @@ public virtual PlaceOrderResult PlaceOrder( // Move shopping cart items to order products. foreach (var sc in cart) { - sc.Item.Product.MergeWithCombination(sc.Item.AttributesXml); + sc.Item.Product.MergeWithCombination(sc.Item.AttributesXml); // Prices. decimal taxRate = decimal.Zero; - decimal unitPriceTaxRate = decimal.Zero; + decimal unitPriceTaxRate = decimal.Zero; decimal scUnitPrice = _priceCalculationService.GetUnitPrice(sc, true); decimal scSubTotal = _priceCalculationService.GetSubTotal(sc, true); - decimal scUnitPriceInclTax = _taxService.GetProductPrice(sc.Item.Product, scUnitPrice, true, customer, out unitPriceTaxRate); + decimal scUnitPriceInclTax = _taxService.GetProductPrice(sc.Item.Product, scUnitPrice, true, customer, out unitPriceTaxRate); decimal scUnitPriceExclTax = _taxService.GetProductPrice(sc.Item.Product, scUnitPrice, false, customer, out taxRate); decimal scSubTotalInclTax = _taxService.GetProductPrice(sc.Item.Product, scSubTotal, true, customer, out taxRate); decimal scSubTotalExclTax = _taxService.GetProductPrice(sc.Item.Product, scSubTotal, false, customer, out taxRate); @@ -1205,15 +1209,15 @@ public virtual PlaceOrderResult PlaceOrder( decimal discountAmountInclTax = _taxService.GetProductPrice(sc.Item.Product, discountAmount, true, customer, out taxRate); decimal discountAmountExclTax = _taxService.GetProductPrice(sc.Item.Product, discountAmount, false, customer, out taxRate); - if (scDiscount != null && !appliedDiscounts.Any(x => x.Id == scDiscount.Id)) - { - appliedDiscounts.Add(scDiscount); - } + if (scDiscount != null && !appliedDiscounts.Any(x => x.Id == scDiscount.Id)) + { + appliedDiscounts.Add(scDiscount); + } var attributeDescription = _productAttributeFormatter.FormatAttributes(sc.Item.Product, sc.Item.AttributesXml, customer); var itemWeight = _shippingService.GetShoppingCartItemWeight(sc); - var displayDeliveryTime = - _shoppingCartSettings.ShowDeliveryTimes && + var displayDeliveryTime = + _shoppingCartSettings.DeliveryTimesInShoppingCart != DeliveryTimesPresentation.None && sc.Item.Product.DeliveryTimeId.HasValue && sc.Item.Product.IsShipEnabled && sc.Item.Product.DisplayDeliveryTimeAccordingToStock(_catalogSettings); @@ -1228,7 +1232,7 @@ public virtual PlaceOrderResult PlaceOrder( UnitPriceExclTax = scUnitPriceExclTax, PriceInclTax = scSubTotalInclTax, PriceExclTax = scSubTotalExclTax, - TaxRate = unitPriceTaxRate, + TaxRate = unitPriceTaxRate, AttributeDescription = attributeDescription, AttributesXml = sc.Item.AttributesXml, Quantity = sc.Item.Quantity, @@ -1238,27 +1242,27 @@ public virtual PlaceOrderResult PlaceOrder( IsDownloadActivated = false, LicenseDownloadId = 0, ItemWeight = itemWeight, - ProductCost = _priceCalculationService.GetProductCost(sc.Item.Product, sc.Item.AttributesXml), + ProductCost = _priceCalculationService.GetProductCost(sc.Item.Product, sc.Item.AttributesXml), DeliveryTimeId = sc.Item.Product.GetDeliveryTimeIdAccordingToStock(_catalogSettings), DisplayDeliveryTime = displayDeliveryTime }; if (sc.Item.Product.ProductType == ProductType.BundledProduct && sc.ChildItems != null) - { - var listBundleData = new List(); + { + var listBundleData = new List(); - foreach (var childItem in sc.ChildItems) - { - var bundleItemSubTotal = _taxService.GetProductPrice(childItem.Item.Product, _priceCalculationService.GetSubTotal(childItem, true), out taxRate); + foreach (var childItem in sc.ChildItems) + { + var bundleItemSubTotal = _taxService.GetProductPrice(childItem.Item.Product, _priceCalculationService.GetSubTotal(childItem, true), out taxRate); - var attributesInfo = _productAttributeFormatter.FormatAttributes(childItem.Item.Product, childItem.Item.AttributesXml, order.Customer, - renderPrices: false, allowHyperlinks: true); + var attributesInfo = _productAttributeFormatter.FormatAttributes(childItem.Item.Product, childItem.Item.AttributesXml, order.Customer, + renderPrices: false, allowHyperlinks: true); - childItem.BundleItemData.ToOrderData(listBundleData, bundleItemSubTotal, childItem.Item.AttributesXml, attributesInfo); - } + childItem.BundleItemData.ToOrderData(listBundleData, bundleItemSubTotal, childItem.Item.AttributesXml, attributesInfo); + } - orderItem.SetBundleData(listBundleData); - } + orderItem.SetBundleData(listBundleData); + } order.OrderItems.Add(orderItem); _orderService.UpdateOrder(order); @@ -1267,12 +1271,12 @@ public virtual PlaceOrderResult PlaceOrder( if (sc.Item.Product.IsGiftCard) { _productAttributeParser.GetGiftCardAttribute( - sc.Item.AttributesXml, + sc.Item.AttributesXml, out var giftCardRecipientName, - out var giftCardRecipientEmail, - out var giftCardSenderName, - out var giftCardSenderEmail, - out var giftCardMessage); + out var giftCardRecipientEmail, + out var giftCardSenderName, + out var giftCardSenderEmail, + out var giftCardMessage); for (int i = 0; i < sc.Item.Quantity; i++) { @@ -1289,20 +1293,20 @@ public virtual PlaceOrderResult PlaceOrder( SenderEmail = giftCardSenderEmail, Message = giftCardMessage, IsRecipientNotified = false, - CreatedOnUtc = utcNow + CreatedOnUtc = utcNow }; _giftCardService.InsertGiftCard(gc); } } - _productService.AdjustInventory(sc, true); + _productService.AdjustInventory(sc, true); } - // Clear shopping cart. - if (!processPaymentRequest.IsMultiOrder) - { - cart.ToList().ForEach(sci => _shoppingCartService.DeleteShoppingCartItem(sci.Item, false)); - } + // Clear shopping cart. + if (!processPaymentRequest.IsMultiOrder) + { + cart.ToList().ForEach(sci => _shoppingCartService.DeleteShoppingCartItem(sci.Item, false)); + } } else { @@ -1320,7 +1324,7 @@ public virtual PlaceOrderResult PlaceOrder( UnitPriceExclTax = orderItem.UnitPriceExclTax, PriceInclTax = orderItem.PriceInclTax, PriceExclTax = orderItem.PriceExclTax, - TaxRate = orderItem.TaxRate, + TaxRate = orderItem.TaxRate, AttributeDescription = orderItem.AttributeDescription, AttributesXml = orderItem.AttributesXml, Quantity = orderItem.Quantity, @@ -1330,8 +1334,8 @@ public virtual PlaceOrderResult PlaceOrder( IsDownloadActivated = false, LicenseDownloadId = 0, ItemWeight = orderItem.ItemWeight, - BundleData = orderItem.BundleData, - ProductCost = orderItem.ProductCost, + BundleData = orderItem.BundleData, + ProductCost = orderItem.ProductCost, DeliveryTimeId = orderItem.DeliveryTimeId, DisplayDeliveryTime = orderItem.DisplayDeliveryTime }; @@ -1342,12 +1346,12 @@ public virtual PlaceOrderResult PlaceOrder( if (orderItem.Product.IsGiftCard) { _productAttributeParser.GetGiftCardAttribute( - orderItem.AttributesXml, - out var giftCardRecipientName, - out var giftCardRecipientEmail, - out var giftCardSenderName, - out var giftCardSenderEmail, - out var giftCardMessage); + orderItem.AttributesXml, + out var giftCardRecipientName, + out var giftCardRecipientEmail, + out var giftCardSenderName, + out var giftCardSenderEmail, + out var giftCardMessage); for (int i = 0; i < orderItem.Quantity; i++) { @@ -1364,54 +1368,54 @@ public virtual PlaceOrderResult PlaceOrder( SenderEmail = giftCardSenderEmail, Message = giftCardMessage, IsRecipientNotified = false, - CreatedOnUtc = utcNow + CreatedOnUtc = utcNow }; _giftCardService.InsertGiftCard(gc); } } - _productService.AdjustInventory(orderItem, true, orderItem.Quantity); + _productService.AdjustInventory(orderItem, true, orderItem.Quantity); } } // Discount usage history. - if (!processPaymentRequest.IsRecurringPayment) - { - foreach (var discount in appliedDiscounts) - { - var duh = new DiscountUsageHistory - { - Discount = discount, - Order = order, - CreatedOnUtc = utcNow - }; - _discountService.InsertDiscountUsageHistory(duh); - } - } + if (!processPaymentRequest.IsRecurringPayment) + { + foreach (var discount in appliedDiscounts) + { + var duh = new DiscountUsageHistory + { + Discount = discount, + Order = order, + CreatedOnUtc = utcNow + }; + _discountService.InsertDiscountUsageHistory(duh); + } + } // Gift card usage history. - if (!processPaymentRequest.IsRecurringPayment && cartTotal.AppliedGiftCards != null) - { - foreach (var agc in cartTotal.AppliedGiftCards) - { - var amountUsed = agc.AmountCanBeUsed; - var gcuh = new GiftCardUsageHistory - { - GiftCard = agc.GiftCard, - UsedWithOrder = order, - UsedValue = amountUsed, - CreatedOnUtc = utcNow - }; - agc.GiftCard.GiftCardUsageHistory.Add(gcuh); - _giftCardService.UpdateGiftCard(agc.GiftCard); - } - } + if (!processPaymentRequest.IsRecurringPayment && cartTotal.AppliedGiftCards != null) + { + foreach (var agc in cartTotal.AppliedGiftCards) + { + var amountUsed = agc.AmountCanBeUsed; + var gcuh = new GiftCardUsageHistory + { + GiftCard = agc.GiftCard, + UsedWithOrder = order, + UsedValue = amountUsed, + CreatedOnUtc = utcNow + }; + agc.GiftCard.GiftCardUsageHistory.Add(gcuh); + _giftCardService.UpdateGiftCard(agc.GiftCard); + } + } // Reward points history. if (cartTotal.RedeemedRewardPointsAmount > decimal.Zero) { customer.AddRewardPointsHistoryEntry(-cartTotal.RedeemedRewardPoints, - _localizationService.GetResource("RewardPoints.Message.RedeemedForOrder", order.CustomerLanguageId).FormatInvariant(order.GetOrderNumber()), + _localizationService.GetResource("RewardPoints.Message.RedeemedForOrder", order.CustomerLanguageId).FormatInvariant(order.GetOrderNumber()), order, cartTotal.RedeemedRewardPointsAmount); @@ -1429,7 +1433,7 @@ public virtual PlaceOrderResult PlaceOrder( TotalCycles = processPaymentRequest.RecurringTotalCycles, StartDateUtc = utcNow, IsActive = true, - CreatedOnUtc = utcNow, + CreatedOnUtc = utcNow, InitialOrder = order, }; _orderService.InsertRecurringPayment(rp); @@ -1461,40 +1465,64 @@ public virtual PlaceOrderResult PlaceOrder( } } - #endregion + #endregion - #region Notifications, notes and attributes + #region Notifications, notes and attributes + + // Notes, messages. + _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderPlaced")); - // Notes, messages. - _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderPlaced")); - // Send email notifications. var msg = _messageFactory.SendOrderPlacedStoreOwnerNotification(order, _localizationSettings.DefaultAdminLanguageId); if (msg?.Email?.Id != null) { - _orderService.AddOrderNote(order, T("Admin.OrderNotice.MerchantEmailQueued", msg.Email.Id)); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.MerchantEmailQueued", msg.Email.Id)); } - msg = _messageFactory.SendOrderPlacedCustomerNotification(order, order.CustomerLanguageId); + msg = _messageFactory.SendOrderPlacedCustomerNotification(order, order.CustomerLanguageId); if (msg?.Email?.Id != null) { - _orderService.AddOrderNote(order, T("Admin.OrderNotice.CustomerEmailQueued", msg.Email.Id)); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.CustomerEmailQueued", msg.Email.Id)); } // Check order status. CheckOrderStatus(order); - // Reset checkout data. + // Reset checkout data. if (!processPaymentRequest.IsRecurringPayment && !processPaymentRequest.IsMultiOrder) - { - _customerService.ResetCheckoutData(customer, processPaymentRequest.StoreId, true, true, true, clearCreditBalance: true); - } + { + _customerService.ResetCheckoutData(customer, processPaymentRequest.StoreId, true, true, true, clearCreditBalance: true); + } - // Check for generic attributes to be inserted automatically. - foreach (var customProperty in processPaymentRequest.CustomProperties.Where(x => x.Key.HasValue() && x.Value.AutoCreateGenericAttribute)) - { - _genericAttributeService.SaveAttribute(order, customProperty.Key, customProperty.Value.Value, order.StoreId); - } + // Check for generic attributes to be inserted automatically. + foreach (var customProperty in processPaymentRequest.CustomProperties.Where(x => x.Key.HasValue() && x.Value.AutoCreateGenericAttribute)) + { + _genericAttributeService.SaveAttribute(order, customProperty.Key, customProperty.Value.Value, order.StoreId); + } + + // Handle transiancy of uploaded files for checkout attributes + if (order.CheckoutAttributesXml.HasValue()) + { + var checkoutAttrs = _checkoutAttributeParser.ParseCheckoutAttributes(order.CheckoutAttributesXml); + + foreach (var attr in checkoutAttrs) + { + if (attr.AttributeControlType == AttributeControlType.FileUpload) + { + var value = _checkoutAttributeParser.ParseValues(order.CheckoutAttributesXml, attr.Id).FirstOrDefault(); + Guid.TryParse(value, out var downloadGuid); + if (downloadGuid != null) + { + var download = _downloadService.GetDownloadByGuid(downloadGuid); + if (download != null) + { + download.IsTransient = false; + _downloadService.UpdateDownload(download); + } + } + } + } + } // Uncomment this line to support transactions. //scope.Complete(); @@ -1506,51 +1534,51 @@ public virtual PlaceOrderResult PlaceOrder( { _customerActivityService.InsertActivity("PublicStore.PlaceOrder", T("ActivityLog.PublicStore.PlaceOrder", order.GetOrderNumber())); } - + if (order.PaymentStatus == PaymentStatus.Paid) { _eventPublisher.PublishOrderPaid(order); } - #endregion + #endregion - #region Newsletter subscription + #region Newsletter subscription - if (extraData.ContainsKey("SubscribeToNewsLetter") && _shoppingCartSettings.NewsLetterSubscription != CheckoutNewsLetterSubscription.None) - { - var addSubscription = extraData["SubscribeToNewsLetter"].ToBool(); + if (extraData.ContainsKey("SubscribeToNewsLetter") && _shoppingCartSettings.NewsLetterSubscription != CheckoutNewsLetterSubscription.None) + { + var addSubscription = extraData["SubscribeToNewsLetter"].ToBool(); - bool? nsResult = _newsLetterSubscriptionService.AddNewsLetterSubscriptionFor(addSubscription, customer.Email, order.StoreId); + bool? nsResult = _newsLetterSubscriptionService.AddNewsLetterSubscriptionFor(addSubscription, customer.Email, order.StoreId); - if (nsResult.HasValue) - { + if (nsResult.HasValue) + { _orderService.AddOrderNote(order, T(nsResult.Value ? "Admin.OrderNotice.NewsLetterSubscriptionAdded" : "Admin.OrderNotice.NewsLetterSubscriptionRemoved")); - } - } + } + } - #endregion - } - } + #endregion + } + } else { - result.AddError(T("Payment.PayingFailed")); + result.AddError(T("Payment.PayingFailed")); - foreach (var paymentError in processPaymentResult.Errors) - { - result.AddError(paymentError); - } + foreach (var paymentError in processPaymentResult.Errors) + { + result.AddError(paymentError); + } } } catch (Exception ex) { - Logger.Error(ex); + Logger.Error(ex); result.AddError(ex.Message); } - if (result.Errors.Any()) - { - Logger.Error(string.Join(" ", result.Errors)); - } + if (result.Errors.Any()) + { + Logger.Error(string.Join(" ", result.Errors)); + } return result; } @@ -1615,7 +1643,7 @@ public virtual void ProcessNextRecurringPayment(RecurringPayment recurringPaymen var paymentInfo = new ProcessPaymentRequest { - StoreId = initialOrder.StoreId, + StoreId = initialOrder.StoreId, CustomerId = customer.Id, OrderGuid = Guid.NewGuid(), IsRecurringPayment = true, @@ -1644,13 +1672,13 @@ public virtual void ProcessNextRecurringPayment(RecurringPayment recurringPaymen _orderService.UpdateRecurringPayment(recurringPayment); } else if (result.Errors.Count > 0) - { + { throw new SmartException(string.Join(" ", result.Errors)); } } catch (Exception exception) { - Logger.ErrorsAll(exception); + Logger.ErrorsAll(exception); throw; } } @@ -1675,7 +1703,7 @@ public virtual IList CancelRecurringPayment(RecurringPayment recurringPa { request.Order = initialOrder; - result = _paymentService.CancelRecurringPayment(request); + result = _paymentService.CancelRecurringPayment(request); if (result.Success) { @@ -1683,7 +1711,7 @@ public virtual IList CancelRecurringPayment(RecurringPayment recurringPa recurringPayment.IsActive = false; _orderService.UpdateRecurringPayment(recurringPayment); - _orderService.AddOrderNote(initialOrder, T("Admin.OrderNotice.RecurringPaymentCancelled")); + _orderService.AddOrderNote(initialOrder, T("Admin.OrderNotice.RecurringPaymentCancelled")); //notify a store owner _messageFactory.SendRecurringPaymentCancelledStoreOwnerNotification(recurringPayment, _localizationSettings.DefaultAdminLanguageId); @@ -1691,17 +1719,17 @@ public virtual IList CancelRecurringPayment(RecurringPayment recurringPa } catch (Exception exception) { - if (result == null) - { - result = new CancelRecurringPaymentResult(); - } + if (result == null) + { + result = new CancelRecurringPaymentResult(); + } result.AddError(exception.ToAllMessages()); } - ProcessErrors(initialOrder, result.Errors, "Admin.OrderNotice.RecurringPaymentCancellationError"); + ProcessErrors(initialOrder, result.Errors, "Admin.OrderNotice.RecurringPaymentCancellationError"); - return result.Errors; + return result.Errors; } /// @@ -1771,7 +1799,7 @@ public virtual void Ship(Shipment shipment, bool notifyCustomer) _orderService.UpdateOrder(order); - _orderService.AddOrderNote(order, T("Admin.OrderNotice.ShipmentSent", shipment.Id)); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.ShipmentSent", shipment.Id)); if (notifyCustomer) { @@ -1779,7 +1807,7 @@ public virtual void Ship(Shipment shipment, bool notifyCustomer) var msg = _messageFactory.SendShipmentSentCustomerNotification(shipment, order.CustomerLanguageId); if (msg?.Email?.Id != null) { - _orderService.AddOrderNote(order, T("Admin.OrderNotice.CustomerShippedEmailQueued", msg.Email.Id)); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.CustomerShippedEmailQueued", msg.Email.Id)); } } @@ -1799,22 +1827,22 @@ public virtual void Deliver(Shipment shipment, bool notifyCustomer) var order = shipment.Order; if (order == null) - throw new SmartException(T("Order.NotFound", shipment.OrderId)); + throw new SmartException(T("Order.NotFound", shipment.OrderId)); - if (shipment.DeliveryDateUtc.HasValue) + if (shipment.DeliveryDateUtc.HasValue) throw new SmartException(T("Shipment.AlreadyDelivered")); shipment.DeliveryDateUtc = DateTime.UtcNow; _shipmentService.UpdateShipment(shipment); - if (!order.CanAddItemsToShipment() && !order.HasItemsToDispatch() && !order.HasItemsToDeliver()) - { - order.ShippingStatusId = (int)ShippingStatus.Delivered; - } + if (!order.CanAddItemsToShipment() && !order.HasItemsToDispatch() && !order.HasItemsToDeliver()) + { + order.ShippingStatusId = (int)ShippingStatus.Delivered; + } _orderService.UpdateOrder(order); - _orderService.AddOrderNote(order, T("Admin.OrderNotice.ShipmentDelivered", shipment.Id)); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.ShipmentDelivered", shipment.Id)); if (notifyCustomer) { @@ -1822,7 +1850,7 @@ public virtual void Deliver(Shipment shipment, bool notifyCustomer) var msg = _messageFactory.SendShipmentDeliveredCustomerNotification(shipment, order.CustomerLanguageId); if (msg?.Email?.Id != null) { - _orderService.AddOrderNote(order, T("Admin.OrderNotice.CustomerDeliveredEmailQueued", msg.Email.Id)); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.CustomerDeliveredEmailQueued", msg.Email.Id)); } } @@ -1865,7 +1893,7 @@ public virtual void CancelOrder(Order order, bool notifyCustomer) // Cancel order. SetOrderStatus(order, OrderStatus.Cancelled, notifyCustomer); - _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderCancelled")); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderCancelled")); // Cancel recurring payments. var recurringPayments = _orderService.SearchRecurringPayments(0, 0, order.Id, null); @@ -1875,88 +1903,93 @@ public virtual void CancelOrder(Order order, bool notifyCustomer) } // Adjust inventory. - foreach (var orderItem in order.OrderItems) - { - _productService.AdjustInventory(orderItem, false, orderItem.Quantity); - } + foreach (var orderItem in order.OrderItems) + { + _productService.AdjustInventory(orderItem, false, orderItem.Quantity); + } } - /// - /// Auto update order details - /// - /// Context parameters - public virtual void AutoUpdateOrderDetails(AutoUpdateOrderItemContext context) - { - var oi = context.OrderItem; + /// + /// Auto update order details + /// + /// Context parameters + public virtual void AutoUpdateOrderDetails(AutoUpdateOrderItemContext context) + { + var oi = context.OrderItem; + + context.RewardPointsOld = context.RewardPointsNew = oi.Order.Customer.GetRewardPointsBalance(); - context.RewardPointsOld = context.RewardPointsNew = oi.Order.Customer.GetRewardPointsBalance(); + if (context.UpdateTotals && oi.Order.OrderStatusId <= (int)OrderStatus.Pending) + { + var currency = _currencyService.GetCurrencyByCode(oi.Order.CustomerCurrencyCode); - if (context.UpdateTotals && oi.Order.OrderStatusId <= (int)OrderStatus.Pending) - { - var currency = _currencyService.GetCurrencyByCode(oi.Order.CustomerCurrencyCode); + decimal priceInclTax = (context.QuantityNew * oi.UnitPriceInclTax).RoundIfEnabledFor(currency); + decimal priceExclTax = (context.QuantityNew * oi.UnitPriceExclTax).RoundIfEnabledFor(currency); - decimal priceInclTax = (context.QuantityNew * oi.UnitPriceInclTax).RoundIfEnabledFor(currency); - decimal priceExclTax = (context.QuantityNew * oi.UnitPriceExclTax).RoundIfEnabledFor(currency); + decimal deltaPriceInclTax = context.IsNewOrderItem + ? priceInclTax + : priceInclTax - (context.PriceInclTaxOld ?? oi.PriceInclTax); - decimal deltaPriceInclTax = priceInclTax - (context.IsNewOrderItem ? decimal.Zero : oi.PriceInclTax); - decimal deltaPriceExclTax = priceExclTax - (context.IsNewOrderItem ? decimal.Zero : oi.PriceExclTax); + decimal deltaPriceExclTax = context.IsNewOrderItem + ? priceExclTax + : priceExclTax - (context.PriceExclTaxOld ?? oi.PriceExclTax); - oi.Quantity = context.QuantityNew; - oi.PriceInclTax = priceInclTax.RoundIfEnabledFor(currency); - oi.PriceExclTax = priceExclTax.RoundIfEnabledFor(currency); + oi.Quantity = context.QuantityNew; + oi.PriceInclTax = priceInclTax.RoundIfEnabledFor(currency); + oi.PriceExclTax = priceExclTax.RoundIfEnabledFor(currency); - decimal subtotalInclTax = oi.Order.OrderSubtotalInclTax + deltaPriceInclTax; - decimal subtotalExclTax = oi.Order.OrderSubtotalExclTax + deltaPriceExclTax; + decimal subtotalInclTax = oi.Order.OrderSubtotalInclTax + deltaPriceInclTax; + decimal subtotalExclTax = oi.Order.OrderSubtotalExclTax + deltaPriceExclTax; - oi.Order.OrderSubtotalInclTax = subtotalInclTax.RoundIfEnabledFor(currency); - oi.Order.OrderSubtotalExclTax = subtotalExclTax.RoundIfEnabledFor(currency); + oi.Order.OrderSubtotalInclTax = subtotalInclTax.RoundIfEnabledFor(currency); + oi.Order.OrderSubtotalExclTax = subtotalExclTax.RoundIfEnabledFor(currency); - decimal discountInclTax = oi.DiscountAmountInclTax * context.QuantityChangeFactor; - decimal discountExclTax = oi.DiscountAmountExclTax * context.QuantityChangeFactor; + decimal discountInclTax = oi.DiscountAmountInclTax * context.QuantityChangeFactor; + decimal discountExclTax = oi.DiscountAmountExclTax * context.QuantityChangeFactor; - decimal deltaDiscountInclTax = discountInclTax - oi.DiscountAmountInclTax; - decimal deltaDiscountExclTax = discountExclTax - oi.DiscountAmountExclTax; + //decimal deltaDiscountInclTax = discountInclTax - oi.DiscountAmountInclTax; + //decimal deltaDiscountExclTax = discountExclTax - oi.DiscountAmountExclTax; - oi.DiscountAmountInclTax = discountInclTax.RoundIfEnabledFor(currency); - oi.DiscountAmountExclTax = discountExclTax.RoundIfEnabledFor(currency); + oi.DiscountAmountInclTax = discountInclTax.RoundIfEnabledFor(currency); + oi.DiscountAmountExclTax = discountExclTax.RoundIfEnabledFor(currency); - decimal total = Math.Max(oi.Order.OrderTotal + deltaPriceInclTax, 0); - decimal tax = Math.Max(oi.Order.OrderTax + (deltaPriceInclTax - deltaPriceExclTax), 0); + decimal total = Math.Max(oi.Order.OrderTotal + deltaPriceInclTax, 0); + decimal tax = Math.Max(oi.Order.OrderTax + (deltaPriceInclTax - deltaPriceExclTax), 0); - oi.Order.OrderTotal = total.RoundIfEnabledFor(currency); - oi.Order.OrderTax = tax.RoundIfEnabledFor(currency); + oi.Order.OrderTotal = total.RoundIfEnabledFor(currency); + oi.Order.OrderTax = tax.RoundIfEnabledFor(currency); - // Update tax rate value. - var deltaTax = deltaPriceInclTax - deltaPriceExclTax; - if (deltaTax != decimal.Zero) - { - var taxRates = oi.Order.TaxRatesDictionary; + // Update tax rate value. + var deltaTax = deltaPriceInclTax - deltaPriceExclTax; + if (deltaTax != decimal.Zero) + { + var taxRates = oi.Order.TaxRatesDictionary; - taxRates[oi.TaxRate] = taxRates.ContainsKey(oi.TaxRate) - ? Math.Max(taxRates[oi.TaxRate] + deltaTax, 0) - : Math.Max(deltaTax, 0); + taxRates[oi.TaxRate] = taxRates.ContainsKey(oi.TaxRate) + ? Math.Max(taxRates[oi.TaxRate] + deltaTax, 0) + : Math.Max(deltaTax, 0); - oi.Order.TaxRates = FormatTaxRates(taxRates); - } + oi.Order.TaxRates = FormatTaxRates(taxRates); + } - _orderService.UpdateOrder(oi.Order); - } + _orderService.UpdateOrder(oi.Order); + } - if (context.AdjustInventory && context.QuantityDelta != 0) - { - context.Inventory = _productService.AdjustInventory(oi, context.QuantityDelta > 0, Math.Abs(context.QuantityDelta)); - } + if (context.AdjustInventory && context.QuantityDelta != 0) + { + context.Inventory = _productService.AdjustInventory(oi, context.QuantityDelta > 0, Math.Abs(context.QuantityDelta)); + } - if (context.UpdateRewardPoints && context.QuantityDelta < 0) - { - // we reduce but we do not award points subsequently. they can be awarded once per order anyway (see Order.RewardPointsWereAdded). - // UpdateRewardPoints only visible for unpending orders (see RewardPointsSettingsValidator). - // note: reducing can of cource only work if oi.UnitPriceExclTax has not been changed! - decimal reduceAmount = Math.Abs(context.QuantityDelta) * oi.UnitPriceInclTax; - ReduceRewardPoints(oi.Order, reduceAmount); - context.RewardPointsNew = oi.Order.Customer.GetRewardPointsBalance(); - } - } + if (context.UpdateRewardPoints && context.QuantityDelta < 0) + { + // We reduce but we do not award points subsequently. They can be awarded once per order anyway (see Order.RewardPointsWereAdded). + // UpdateRewardPoints only visible for unpending orders (see RewardPointsSettingsValidator). + // Note: reducing can of cource only work if oi.UnitPriceExclTax has not been changed! + decimal reduceAmount = Math.Abs(context.QuantityDelta) * oi.UnitPriceInclTax; + ReduceRewardPoints(oi.Order, reduceAmount); + context.RewardPointsNew = oi.Order.Customer.GetRewardPointsBalance(); + } + } /// @@ -1990,49 +2023,49 @@ public virtual void MarkAsAuthorized(Order order) order.PaymentStatusId = (int)PaymentStatus.Authorized; _orderService.UpdateOrder(order); - _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderMarkedAsAuthorized")); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderMarkedAsAuthorized")); //check order status CheckOrderStatus(order); } - /// - /// Gets a value indicating whether the order can be marked as completed - /// - /// Order - /// A value indicating whether the order can be marked as completed - public virtual bool CanCompleteOrder(Order order) - { - if (order == null) - throw new ArgumentNullException("order"); - - return (order.OrderStatus != OrderStatus.Complete && order.OrderStatus != OrderStatus.Cancelled); - } - - /// - /// Marks the order as completed - /// - /// Order - public virtual void CompleteOrder(Order order) - { + /// + /// Gets a value indicating whether the order can be marked as completed + /// + /// Order + /// A value indicating whether the order can be marked as completed + public virtual bool CanCompleteOrder(Order order) + { + if (order == null) + throw new ArgumentNullException("order"); + + return (order.OrderStatus != OrderStatus.Complete && order.OrderStatus != OrderStatus.Cancelled); + } + + /// + /// Marks the order as completed + /// + /// Order + public virtual void CompleteOrder(Order order) + { if (!CanCompleteOrder(order)) throw new SmartException(T("Order.CannotMarkCompleted")); - if (CanMarkOrderAsPaid(order)) - { - MarkOrderAsPaid(order); - } + if (CanMarkOrderAsPaid(order)) + { + MarkOrderAsPaid(order); + } - if (order.ShippingStatus != ShippingStatus.ShippingNotRequired) - { - order.ShippingStatusId = (int)ShippingStatus.Delivered; - } + if (order.ShippingStatus != ShippingStatus.ShippingNotRequired) + { + order.ShippingStatusId = (int)ShippingStatus.Delivered; + } - _orderService.UpdateOrder(order); + _orderService.UpdateOrder(order); - CheckOrderStatus(order); - } + CheckOrderStatus(order); + } /// @@ -2078,10 +2111,10 @@ public virtual IList Capture(Order order) if (result.Success) { var paidDate = order.PaidDateUtc; - if (result.NewPaymentStatus == PaymentStatus.Paid) - { - paidDate = DateTime.UtcNow; - } + if (result.NewPaymentStatus == PaymentStatus.Paid) + { + paidDate = DateTime.UtcNow; + } order.CaptureTransactionId = result.CaptureTransactionId; order.CaptureTransactionResult = result.CaptureTransactionResult; @@ -2090,7 +2123,7 @@ public virtual IList Capture(Order order) _orderService.UpdateOrder(order); - _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderCaptured")); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderCaptured")); CheckOrderStatus(order); @@ -2103,17 +2136,17 @@ public virtual IList Capture(Order order) } catch (Exception exception) { - if (result == null) - { - result = new CapturePaymentResult(); - } + if (result == null) + { + result = new CapturePaymentResult(); + } - result.AddError(exception.ToAllMessages()); - } + result.AddError(exception.ToAllMessages()); + } - ProcessErrors(order, result.Errors, "Admin.OrderNotice.OrderCaptureError"); + ProcessErrors(order, result.Errors, "Admin.OrderNotice.OrderCaptureError"); - return result.Errors; + return result.Errors; } /// @@ -2152,9 +2185,9 @@ public virtual void MarkOrderAsPaid(Order order) order.PaymentStatusId = (int)PaymentStatus.Paid; order.PaidDateUtc = DateTime.UtcNow; - _orderService.UpdateOrder(order); + _orderService.UpdateOrder(order); - _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderMarkedAsPaid")); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderMarkedAsPaid")); CheckOrderStatus(order); @@ -2180,20 +2213,20 @@ public virtual bool CanRefund(Order order) if (order.OrderTotal == decimal.Zero) return false; - // Only partial refunds allowed if already refunded. - if (order.RefundedAmount > decimal.Zero) - return false; + // Only partial refunds allowed if already refunded. + if (order.RefundedAmount > decimal.Zero) + return false; - // Uncomment the lines below in order to allow this operation for cancelled orders. - //if (order.OrderStatus == OrderStatus.Cancelled) - // return false; + // Uncomment the lines below in order to allow this operation for cancelled orders. + //if (order.OrderStatus == OrderStatus.Cancelled) + // return false; - if (order.PaymentStatus == PaymentStatus.Paid && _paymentService.SupportRefund(order.PaymentMethodSystemName)) + if (order.PaymentStatus == PaymentStatus.Paid && _paymentService.SupportRefund(order.PaymentMethodSystemName)) return true; return false; } - + /// /// Refunds an order (from admin panel) /// @@ -2215,7 +2248,7 @@ public virtual IList Refund(Order order) request.AmountToRefund = order.OrderTotal; request.IsPartialRefund = false; - result = _paymentService.Refund(request); + result = _paymentService.Refund(request); if (result.Success) { @@ -2228,7 +2261,7 @@ public virtual IList Refund(Order order) _orderService.UpdateOrder(order); - _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderRefunded", _priceFormatter.FormatPrice(request.AmountToRefund, true, false))); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderRefunded", _priceFormatter.FormatPrice(request.AmountToRefund, true, false))); CheckOrderStatus(order); } @@ -2236,17 +2269,17 @@ public virtual IList Refund(Order order) } catch (Exception exception) { - if (result == null) - { - result = new RefundPaymentResult(); - } + if (result == null) + { + result = new RefundPaymentResult(); + } - result.AddError(exception.ToAllMessages()); + result.AddError(exception.ToAllMessages()); } - ProcessErrors(order, result.Errors, "Admin.OrderNotice.OrderRefundError"); + ProcessErrors(order, result.Errors, "Admin.OrderNotice.OrderRefundError"); - return result.Errors; + return result.Errors; } /// @@ -2262,15 +2295,15 @@ public virtual bool CanRefundOffline(Order order) if (order.OrderTotal == decimal.Zero) return false; - // Only partial refunds allowed if already refunded. - if (order.RefundedAmount > decimal.Zero) - return false; + // Only partial refunds allowed if already refunded. + if (order.RefundedAmount > decimal.Zero) + return false; - // Uncomment the lines below in order to allow this operation for cancelled orders. - //if (order.OrderStatus == OrderStatus.Cancelled) - // return false; + // Uncomment the lines below in order to allow this operation for cancelled orders. + //if (order.OrderStatus == OrderStatus.Cancelled) + // return false; - if (order.PaymentStatus == PaymentStatus.Paid) + if (order.PaymentStatus == PaymentStatus.Paid) return true; return false; @@ -2300,7 +2333,7 @@ public virtual void RefundOffline(Order order) _orderService.UpdateOrder(order); - _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderMarkedAsRefunded", _priceFormatter.FormatPrice(amountToRefund, true, false))); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderMarkedAsRefunded", _priceFormatter.FormatPrice(amountToRefund, true, false))); CheckOrderStatus(order); } @@ -2374,7 +2407,7 @@ public virtual IList PartiallyRefund(Order order, decimal amountToRefund _orderService.UpdateOrder(order); - _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderPartiallyRefunded", _priceFormatter.FormatPrice(amountToRefund, true, false))); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderPartiallyRefunded", _priceFormatter.FormatPrice(amountToRefund, true, false))); //check order status CheckOrderStatus(order); @@ -2382,17 +2415,17 @@ public virtual IList PartiallyRefund(Order order, decimal amountToRefund } catch (Exception exception) { - if (result == null) - { - result = new RefundPaymentResult(); - } + if (result == null) + { + result = new RefundPaymentResult(); + } - result.AddError(exception.ToAllMessages()); - } + result.AddError(exception.ToAllMessages()); + } - ProcessErrors(order, result.Errors, "Admin.OrderNotice.OrderPartiallyRefundError"); + ProcessErrors(order, result.Errors, "Admin.OrderNotice.OrderPartiallyRefundError"); - return result.Errors; + return result.Errors; } /// @@ -2436,7 +2469,7 @@ public virtual void PartiallyRefundOffline(Order order, decimal amountToRefund) { if (order == null) throw new ArgumentNullException("order"); - + if (!CanPartiallyRefundOffline(order, amountToRefund)) throw new SmartException(T("Order.CannotPartialRefund")); @@ -2450,7 +2483,7 @@ public virtual void PartiallyRefundOffline(Order order, decimal amountToRefund) _orderService.UpdateOrder(order); - _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderMarkedAsPartiallyRefunded", _priceFormatter.FormatPrice(amountToRefund, true, false))); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderMarkedAsPartiallyRefunded", _priceFormatter.FormatPrice(amountToRefund, true, false))); //check order status CheckOrderStatus(order); @@ -2510,7 +2543,7 @@ public virtual IList Void(Order order) _orderService.UpdateOrder(order); - _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderVoided")); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderVoided")); //check order status CheckOrderStatus(order); @@ -2518,15 +2551,15 @@ public virtual IList Void(Order order) } catch (Exception exception) { - if (result == null) - { - result = new VoidPaymentResult(); - } + if (result == null) + { + result = new VoidPaymentResult(); + } - result.AddError(exception.ToAllMessages()); - } + result.AddError(exception.ToAllMessages()); + } - ProcessErrors(order, result.Errors, "Admin.OrderNotice.OrderVoidError"); + ProcessErrors(order, result.Errors, "Admin.OrderNotice.OrderVoidError"); return result.Errors; } @@ -2564,13 +2597,13 @@ public virtual void VoidOffline(Order order) throw new ArgumentNullException("order"); if (!CanVoidOffline(order)) - throw new SmartException(T("Order.CannotVoid")); + throw new SmartException(T("Order.CannotVoid")); - order.PaymentStatusId = (int)PaymentStatus.Voided; + order.PaymentStatusId = (int)PaymentStatus.Voided; _orderService.UpdateOrder(order); - _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderMarkedAsVoided")); + _orderService.AddOrderNote(order, T("Admin.OrderNotice.OrderMarkedAsVoided")); //check orer status CheckOrderStatus(order); @@ -2589,34 +2622,29 @@ public virtual void ReOrder(Order order) foreach (var orderItem in order.OrderItems) { - var isBundle = (orderItem.Product.ProductType == ProductType.BundledProduct); + var isBundle = (orderItem.Product.ProductType == ProductType.BundledProduct); - var addToCartContext = new AddToCartContext(); + var addToCartContext = new AddToCartContext(); - addToCartContext.Warnings = _shoppingCartService.AddToCart(order.Customer, orderItem.Product, ShoppingCartType.ShoppingCart, order.StoreId, - orderItem.AttributesXml, isBundle ? decimal.Zero : orderItem.UnitPriceExclTax, orderItem.Quantity, false, addToCartContext); + addToCartContext.Warnings = _shoppingCartService.AddToCart(order.Customer, orderItem.Product, ShoppingCartType.ShoppingCart, order.StoreId, + orderItem.AttributesXml, isBundle ? decimal.Zero : orderItem.UnitPriceExclTax, orderItem.Quantity, false, addToCartContext); - if (isBundle && orderItem.BundleData.HasValue() && addToCartContext.Warnings.Count == 0) - { - foreach (var bundleData in orderItem.GetBundleData()) - { - var bundleItem = _productService.GetBundleItemById(bundleData.BundleItemId); - addToCartContext.BundleItem = bundleItem; + if (isBundle && orderItem.BundleData.HasValue() && addToCartContext.Warnings.Count == 0) + { + foreach (var bundleData in orderItem.GetBundleData()) + { + var bundleItem = _productService.GetBundleItemById(bundleData.BundleItemId); + addToCartContext.BundleItem = bundleItem; - addToCartContext.Warnings = _shoppingCartService.AddToCart(order.Customer, bundleItem.Product, ShoppingCartType.ShoppingCart, order.StoreId, - bundleData.AttributesXml, decimal.Zero, bundleData.Quantity, false, addToCartContext); - } - } + addToCartContext.Warnings = _shoppingCartService.AddToCart(order.Customer, bundleItem.Product, ShoppingCartType.ShoppingCart, order.StoreId, + bundleData.AttributesXml, decimal.Zero, bundleData.Quantity, false, addToCartContext); + } + } - _shoppingCartService.AddToCartStoring(addToCartContext); + _shoppingCartService.AddToCartStoring(addToCartContext); } } - - /// - /// Check whether return request is allowed - /// - /// Order - /// Result + public virtual bool IsReturnRequestAllowed(Order order) { if (!_orderSettings.ReturnRequestsEnabled) @@ -2643,129 +2671,158 @@ public virtual bool IsReturnRequestAllowed(Order order) return numberOfDaysReturnRequestAvailableValid; } + public virtual (bool valid, decimal orderTotalMinimum) IsAboveOrderTotalMinimum(IList cart, int[] customerRoleIds) + { + var query = _customerService.GetAllCustomerRoles().SourceQuery + .Where(x => x.OrderTotalMinimum > decimal.Zero && customerRoleIds.Contains(x.Id)); + + var customerRole = _orderSettings.MultipleOrderTotalRestrictionsExpandRange + ? query.OrderBy(x => x.OrderTotalMinimum).FirstOrDefault() + : query.OrderByDescending(x => x.OrderTotalMinimum).FirstOrDefault(); + + var minimumOrderTotal = customerRole == null + ? _orderSettings.OrderTotalMinimum : customerRole.OrderTotalMinimum; + + return IsAboveOrderTotalMinimum(cart, minimumOrderTotal ?? decimal.Zero); + } /// - /// Valdiate minimum order sub-total amount + /// Validate order total minimum. /// - /// Shopping cart - /// true - OK; false - minimum order sub-total amount is not reached - public virtual bool ValidateMinOrderSubtotalAmount(IList cart) + /// Shopping cart, minimum order total + /// true - OK; false - minimum order total is not reached + private (bool valid, decimal orderTotalMinimum) IsAboveOrderTotalMinimum(IList cart, decimal minimumOrderTotal) { if (cart == null) throw new ArgumentNullException("cart"); - //min order amount sub-total validation - if (cart.Count > 0 && _orderSettings.MinOrderSubtotalAmount > decimal.Zero) + if (cart.Count > 0 && minimumOrderTotal > decimal.Zero) { - decimal orderSubTotalDiscountAmountBase = decimal.Zero; - Discount orderSubTotalAppliedDiscount = null; - decimal subTotalWithoutDiscountBase = decimal.Zero; - decimal subTotalWithDiscountBase = decimal.Zero; - - _orderTotalCalculationService.GetShoppingCartSubTotal(cart, - out orderSubTotalDiscountAmountBase, out orderSubTotalAppliedDiscount, out subTotalWithoutDiscountBase, out subTotalWithDiscountBase); + _orderTotalCalculationService.GetShoppingCartSubTotal( + cart, out _, out _, out var cartSubtotal, out _); - if (subTotalWithoutDiscountBase < _orderSettings.MinOrderSubtotalAmount) - return false; + if (cartSubtotal < minimumOrderTotal) + return (false, minimumOrderTotal); } - return true; + return (true, minimumOrderTotal); + } + + public virtual (bool valid, decimal orderTotalMaximum) IsBelowOrderTotalMaximum(IList cart, int[] customerRoleIds) + { + var query = _customerService.GetAllCustomerRoles().SourceQuery + .Where(x => x.OrderTotalMaximum > decimal.Zero && customerRoleIds.Contains(x.Id)); + + var customerRole = _orderSettings.MultipleOrderTotalRestrictionsExpandRange + ? query.OrderByDescending(x => x.OrderTotalMaximum).FirstOrDefault() + : query.OrderBy(x => x.OrderTotalMaximum).FirstOrDefault(); + + var maximumOrderTotal = customerRole == null + ? _orderSettings.OrderTotalMaximum : customerRole.OrderTotalMaximum; + + return IsBelowOrderTotalMaximum(cart, maximumOrderTotal ?? decimal.Zero); } /// - /// Valdiate minimum order total amount + /// Validate order total maximum. /// - /// Shopping cart - /// true - OK; false - minimum order total amount is not reached - public virtual bool ValidateMinOrderTotalAmount(IList cart) + /// Shopping cart, maximum order total + /// true - OK; false - maximum order total is exceeded + private (bool valid, decimal orderTotalMaximum) IsBelowOrderTotalMaximum(IList cart, decimal maximumOrderTotal) { if (cart == null) throw new ArgumentNullException("cart"); - if (cart.Count > 0 && _orderSettings.MinOrderTotalAmount > decimal.Zero) + if (cart.Count > 0 && maximumOrderTotal > decimal.Zero) { - decimal? shoppingCartTotalBase = _orderTotalCalculationService.GetShoppingCartTotal(cart); + _orderTotalCalculationService.GetShoppingCartSubTotal( + cart, out _, out _, out var cartSubtotal, out _); - if (shoppingCartTotalBase.HasValue && shoppingCartTotalBase.Value < _orderSettings.MinOrderTotalAmount) - return false; + if (cartSubtotal > maximumOrderTotal) + return (false, maximumOrderTotal); } - return true; + return (true, maximumOrderTotal); } - public virtual Shipment AddShipment(Order order, string trackingNumber, Dictionary quantities) - { - Guard.NotNull(order, nameof(order)); - - Shipment shipment = null; - decimal? totalWeight = null; - - foreach (var orderItem in order.OrderItems) - { - if (!orderItem.Product.IsShipEnabled) - continue; - - //ensure that this product can be shipped (have at least one item to ship) - var maxQtyToAdd = orderItem.GetItemsCanBeAddedToShipmentCount(); - if (maxQtyToAdd <= 0) - continue; - - var qtyToAdd = 0; - - if (quantities != null && quantities.ContainsKey(orderItem.Id)) - qtyToAdd = quantities[orderItem.Id]; - else if (quantities == null) - qtyToAdd = maxQtyToAdd; - - if (qtyToAdd <= 0) - continue; - - if (qtyToAdd > maxQtyToAdd) - qtyToAdd = maxQtyToAdd; - - var orderItemTotalWeight = orderItem.ItemWeight.HasValue ? orderItem.ItemWeight * qtyToAdd : null; - if (orderItemTotalWeight.HasValue) - { - if (!totalWeight.HasValue) - totalWeight = 0; - - totalWeight += orderItemTotalWeight.Value; - } - - if (shipment == null) - { - shipment = new Shipment - { - OrderId = order.Id, - Order = order, // otherwise order updated event would not be fired during InsertShipment - TrackingNumber = trackingNumber, - TotalWeight = null, - ShippedDateUtc = null, - DeliveryDateUtc = null, - CreatedOnUtc = DateTime.UtcNow, - }; - } - - var shipmentItem = new ShipmentItem - { - OrderItemId = orderItem.Id, - Quantity = qtyToAdd - }; - - shipment.ShipmentItems.Add(shipmentItem); - } - - if (shipment != null && shipment.ShipmentItems.Count > 0) - { - shipment.TotalWeight = totalWeight; - - _shipmentService.InsertShipment(shipment); - - return shipment; - } - - return null; - } + public virtual Shipment AddShipment( + Order order, + string trackingNumber, + string trackingUrl, + Dictionary quantities) + { + Guard.NotNull(order, nameof(order)); + + Shipment shipment = null; + decimal? totalWeight = null; + + foreach (var orderItem in order.OrderItems) + { + if (!orderItem.Product.IsShipEnabled) + continue; + + // Ensure that this product can be shipped (have at least one item to ship). + var maxQtyToAdd = orderItem.GetItemsCanBeAddedToShipmentCount(); + if (maxQtyToAdd <= 0) + continue; + + var qtyToAdd = 0; + + if (quantities != null && quantities.ContainsKey(orderItem.Id)) + qtyToAdd = quantities[orderItem.Id]; + else if (quantities == null) + qtyToAdd = maxQtyToAdd; + + if (qtyToAdd <= 0) + continue; + + if (qtyToAdd > maxQtyToAdd) + qtyToAdd = maxQtyToAdd; + + var orderItemTotalWeight = orderItem.ItemWeight.HasValue ? orderItem.ItemWeight * qtyToAdd : null; + if (orderItemTotalWeight.HasValue) + { + if (!totalWeight.HasValue) + totalWeight = 0; + + totalWeight += orderItemTotalWeight.Value; + } + + if (shipment == null) + { + shipment = new Shipment + { + OrderId = order.Id, + Order = order, // Otherwise order updated event would not be fired during InsertShipment. + TrackingNumber = trackingNumber, + TrackingUrl = trackingUrl, + TotalWeight = null, + ShippedDateUtc = null, + DeliveryDateUtc = null, + CreatedOnUtc = DateTime.UtcNow, + }; + } + + var shipmentItem = new ShipmentItem + { + OrderItemId = orderItem.Id, + Quantity = qtyToAdd + }; + + shipment.ShipmentItems.Add(shipmentItem); + } + + if (shipment != null && shipment.ShipmentItems.Count > 0) + { + shipment.TotalWeight = totalWeight; + + _shipmentService.InsertShipment(shipment); + + return shipment; + } + + return null; + } #endregion } diff --git a/src/Libraries/SmartStore.Services/Orders/OrderReportService.cs b/src/Libraries/SmartStore.Services/Orders/OrderReportService.cs index a39d0e395c..a3e30eea97 100644 --- a/src/Libraries/SmartStore.Services/Orders/OrderReportService.cs +++ b/src/Libraries/SmartStore.Services/Orders/OrderReportService.cs @@ -1,137 +1,142 @@ using System; -using System.Collections.Generic; +using System.Data.Entity; using System.Globalization; using System.Linq; -using System.Data.Entity; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Dashboard; using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Payments; using SmartStore.Core.Domain.Shipping; -using SmartStore.Services.Catalog; using SmartStore.Services.Helpers; namespace SmartStore.Services.Orders { - /// - /// Order report service - /// public partial class OrderReportService : IOrderReportService { - #region Fields - private readonly IRepository _orderRepository; private readonly IRepository _orderItemRepository; private readonly IRepository _productRepository; - private readonly IDateTimeHelper _dateTimeHelper; - private readonly IProductService _productService; - #endregion - - #region Ctor - - /// - /// Ctor - /// - /// Order repository - /// Order item repository - /// Product repository - /// Datetime helper - /// Product service - public OrderReportService(IRepository orderRepository, + public OrderReportService( + IRepository orderRepository, IRepository orderItemRepository, IRepository productRepository, - IDateTimeHelper dateTimeHelper, IProductService productService) + IDateTimeHelper dateTimeHelper) { - this._orderRepository = orderRepository; - this._orderItemRepository = orderItemRepository; - this._productRepository = productRepository; - this._dateTimeHelper = dateTimeHelper; - this._productService = productService; + _orderRepository = orderRepository; + _orderItemRepository = orderItemRepository; + _productRepository = productRepository; + _dateTimeHelper = dateTimeHelper; } - #endregion - - #region Methods - - /// - /// Get order average report - /// - /// Store identifier - /// Filter by order status - /// Filter by payment status - /// Filter by shipping status - /// Start date - /// End date - /// Billing email. Leave empty to load all records. - /// A value indicating whether to ignore cancelled orders - /// Result - public virtual OrderAverageReportLine GetOrderAverageReportLine(int storeId, int[] orderStatusIds, - int[] paymentStatusIds, int[] shippingStatusIds, DateTime? startTimeUtc, DateTime? endTimeUtc, - string billingEmail, bool ignoreCancelledOrders = false) + public virtual OrderAverageReportLine GetOrderAverageReportLine( + int storeId, + int[] orderStatusIds, + 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 (storeId > 0) + { + query = query.Where(o => o.StoreId == storeId); + } if (ignoreCancelledOrders) { - int cancelledOrderStatusId = (int)OrderStatus.Cancelled; + var 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 (!string.IsNullOrEmpty(billingEmail)) + { + query = query.Where(o => o.BillingAddress != null && !string.IsNullOrEmpty(o.BillingAddress.Email) && o.BillingAddress.Email.Contains(billingEmail)); + } + if (orderStatusIds != null && orderStatusIds.Any()) + { + query = query.Where(x => orderStatusIds.Contains(x.OrderStatusId)); + } + if (paymentStatusIds != null && paymentStatusIds.Any()) + { + query = query.Where(x => paymentStatusIds.Contains(x.PaymentStatusId)); + } + if (shippingStatusIds != null && shippingStatusIds.Any()) + { + query = query.Where(x => shippingStatusIds.Contains(x.ShippingStatusId)); + } - if (paymentStatusIds != null && paymentStatusIds.Count() > 0) - query = query.Where(x => paymentStatusIds.Contains(x.PaymentStatusId)); + return GetOrderAverageReportLine(query); + } - if (shippingStatusIds != null && shippingStatusIds.Count() > 0) - query = query.Where(x => shippingStatusIds.Contains(x.ShippingStatusId)); + public virtual OrderAverageReportLine GetOrderAverageReportLine(IQueryable orderQuery) + { + var item = ( + from oq in orderQuery + 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(); - 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 (item == null) + { + 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; } - /// - /// Get order average report - /// - /// Store identifier - /// Order status - /// Result - public virtual OrderAverageReportLineSummary OrderAverageReport(int storeId, OrderStatus os) + public virtual OrderAverageReportLineSummary OrderAverageReport(int storeId, OrderStatus os) { - var item = new OrderAverageReportLineSummary(); - item.OrderStatus = os; + var item = new OrderAverageReportLineSummary + { + OrderStatus = os + }; DateTime nowDt = _dateTimeHelper.ConvertToUserTime(DateTime.Now); TimeZoneInfo timeZone = _dateTimeHelper.CurrentTimeZone; - var orderStatusId = new int[] { (int)os }; + var orderStatusId = new int[] { (int)os }; - //today + // Today. DateTime t1 = new DateTime(nowDt.Year, nowDt.Month, nowDt.Day); if (!timeZone.IsInvalidTime(t1)) { DateTime? startTime1 = _dateTimeHelper.ConvertToUtcTime(t1, timeZone); DateTime? endTime1 = null; - var todayResult = GetOrderAverageReportLine(storeId, orderStatusId, null, null, startTime1, endTime1, null); + var todayResult = GetOrderAverageReportLine(storeId, orderStatusId, null, null, startTime1, endTime1, null); item.SumTodayOrders = todayResult.SumOrders; item.CountTodayOrders = todayResult.CountOrders; } - //week + + // Week. DayOfWeek fdow = CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek; DateTime today = new DateTime(nowDt.Year, nowDt.Month, nowDt.Day); DateTime t2 = today.AddDays(-(today.DayOfWeek - fdow)); @@ -139,165 +144,146 @@ public virtual OrderAverageReportLineSummary OrderAverageReport(int storeId, Ord { DateTime? startTime2 = _dateTimeHelper.ConvertToUtcTime(t2, timeZone); DateTime? endTime2 = null; - var weekResult = GetOrderAverageReportLine(storeId, orderStatusId, null, null, startTime2, endTime2, null); + var weekResult = GetOrderAverageReportLine(storeId, orderStatusId, null, null, startTime2, endTime2, null); item.SumThisWeekOrders = weekResult.SumOrders; item.CountThisWeekOrders = weekResult.CountOrders; } - //month + + // Month. DateTime t3 = new DateTime(nowDt.Year, nowDt.Month, 1); if (!timeZone.IsInvalidTime(t3)) { DateTime? startTime3 = _dateTimeHelper.ConvertToUtcTime(t3, timeZone); DateTime? endTime3 = null; - var monthResult = GetOrderAverageReportLine(storeId, orderStatusId, null, null, startTime3, endTime3, null); + var monthResult = GetOrderAverageReportLine(storeId, orderStatusId, null, null, startTime3, endTime3, null); item.SumThisMonthOrders = monthResult.SumOrders; item.CountThisMonthOrders = monthResult.CountOrders; } - //year + + // Year. DateTime t4 = new DateTime(nowDt.Year, 1, 1); if (!timeZone.IsInvalidTime(t4)) { DateTime? startTime4 = _dateTimeHelper.ConvertToUtcTime(t4, timeZone); DateTime? endTime4 = null; - var yearResult = GetOrderAverageReportLine(storeId, orderStatusId, null, null, startTime4, endTime4, null); + var yearResult = GetOrderAverageReportLine(storeId, orderStatusId, null, null, startTime4, endTime4, null); item.SumThisYearOrders = yearResult.SumOrders; item.CountThisYearOrders = yearResult.CountOrders; } - //all time + + // All time. DateTime? startTime5 = null; DateTime? endTime5 = null; - var allTimeResult = GetOrderAverageReportLine(storeId, orderStatusId, null, null, startTime5, endTime5, null); + var allTimeResult = GetOrderAverageReportLine(storeId, orderStatusId, null, null, startTime5, endTime5, null); item.SumAllTimeOrders = allTimeResult.SumOrders; item.CountAllTimeOrders = allTimeResult.CountOrders; return item; } - /// - /// Get best sellers report - /// - /// Store identifier - /// Order start time; null to load all - /// Order end time; null to load all - /// Order status; null to load all records - /// Order payment status; null to load all records - /// Shipping status; null to load all records - /// Billing country identifier; 0 to load all records - /// Records to return - /// 1 - order by quantity, 2 - order by total amount - /// A value indicating whether to show hidden records - /// Result - public virtual IList BestSellersReport(int storeId, - DateTime? startTime, DateTime? endTime, - OrderStatus? os, PaymentStatus? ps, ShippingStatus? ss, + public virtual IPagedList BestSellersReport( + int storeId, + DateTime? startTime, + DateTime? endTime, + OrderStatus? orderStatus, + PaymentStatus? paymentStatus, + ShippingStatus? shippingStatus, int billingCountryId = 0, - int recordsToReturn = 5, int orderBy = 1, bool showHidden = false) + int pageIndex = 0, + int pageSize = int.MaxValue, + ReportSorting sorting = ReportSorting.ByQuantityDesc, + bool showHidden = false) { - int? orderStatusId = null; - if (os.HasValue) - orderStatusId = (int)os.Value; - - int? paymentStatusId = null; - if (ps.HasValue) - paymentStatusId = (int)ps.Value; - - int? shippingStatusId = null; - if (ss.HasValue) - shippingStatusId = (int)ss.Value; - - - var query1 = from orderItem in _orderItemRepository.Table - join o in _orderRepository.Table on orderItem.OrderId equals o.Id - join p in _productRepository.Table on orderItem.ProductId equals p.Id - where (storeId == 0 || storeId == o.StoreId) && - (!startTime.HasValue || startTime.Value <= o.CreatedOnUtc) && - (!endTime.HasValue || endTime.Value >= o.CreatedOnUtc) && - (!orderStatusId.HasValue || orderStatusId == o.OrderStatusId) && - (!paymentStatusId.HasValue || paymentStatusId == o.PaymentStatusId) && - (!shippingStatusId.HasValue || shippingStatusId == o.ShippingStatusId) && - (!o.Deleted) && - (!p.Deleted) && (!p.IsSystemProduct) && - (billingCountryId == 0 || o.BillingAddress.CountryId == billingCountryId) && - (showHidden || p.Published) - select orderItem; - - var query2 = - //group by products - from orderItem in query1 + var orderStatusId = orderStatus.HasValue ? (int)orderStatus.Value : (int?)null; + var paymentStatusId = paymentStatus.HasValue ? (int)paymentStatus.Value : (int?)null; + var shippingStatusId = shippingStatus.HasValue ? (int)shippingStatus : (int?)null; + + var query = + from orderItem in _orderItemRepository.TableUntracked + join o in _orderRepository.TableUntracked on orderItem.OrderId equals o.Id + join p in _productRepository.TableUntracked on orderItem.ProductId equals p.Id + where (storeId == 0 || storeId == o.StoreId) && + (!startTime.HasValue || startTime.Value <= o.CreatedOnUtc) && + (!endTime.HasValue || endTime.Value >= o.CreatedOnUtc) && + (!orderStatusId.HasValue || orderStatusId == o.OrderStatusId) && + (!paymentStatusId.HasValue || paymentStatusId == o.PaymentStatusId) && + (!shippingStatusId.HasValue || shippingStatusId == o.ShippingStatusId) && + (!o.Deleted) && + (!p.Deleted) && (!p.IsSystemProduct) && + (billingCountryId == 0 || o.BillingAddress.CountryId == billingCountryId) && + (showHidden || p.Published) + select orderItem; + + // Group by product ID. + var groupedQuery = + from orderItem in query group orderItem by orderItem.ProductId into g - select new + select new BestsellersReportLine { - EntityId = g.Key, + ProductId = g.Key, TotalAmount = g.Sum(x => x.PriceExclTax), - TotalQuantity = g.Sum(x => x.Quantity), + TotalQuantity = g.Sum(x => x.Quantity) }; - switch (orderBy) + switch (sorting) { - case 1: - { - query2 = query2.OrderByDescending(x => x.TotalQuantity); - } + case ReportSorting.ByAmountAsc: + groupedQuery = groupedQuery.OrderBy(x => x.TotalAmount); + break; + case ReportSorting.ByAmountDesc: + groupedQuery = groupedQuery.OrderByDescending(x => x.TotalAmount); break; - case 2: - { - query2 = query2.OrderByDescending(x => x.TotalAmount); - } + case ReportSorting.ByQuantityAsc: + groupedQuery = groupedQuery.OrderBy(x => x.TotalQuantity).ThenByDescending(x => x.TotalAmount); break; + case ReportSorting.ByQuantityDesc: default: - throw new ArgumentException("Wrong orderBy parameter", "orderBy"); + groupedQuery = groupedQuery.OrderByDescending(x => x.TotalQuantity).ThenByDescending(x => x.TotalAmount); + break; } - if (recordsToReturn != 0 && recordsToReturn != int.MaxValue) - query2 = query2.Take(() => recordsToReturn); - - var result = query2.ToList().Select(x => - { - var reportLine = new BestsellersReportLine() - { - ProductId = x.EntityId, - TotalAmount = x.TotalAmount, - TotalQuantity = x.TotalQuantity - }; - return reportLine; - }).ToList(); - + var result = new PagedList(groupedQuery, pageIndex, pageSize); return result; } public virtual int GetPurchaseCount(int productId) { if (productId == 0) - throw new ArgumentException("Product ID is not specified"); - - var query = from orderItem in _orderItemRepository.Table - where orderItem.ProductId == productId - group orderItem by orderItem.Id into g - select new { ProductsPurchased = g.Sum(x => x.Quantity) }; - + { + return 0; + } + + var query = + from orderItem in _orderItemRepository.Table + where orderItem.ProductId == productId + group orderItem by orderItem.Id into g + select new { ProductsPurchased = g.Sum(x => x.Quantity) }; + return query.Select(x => x.ProductsPurchased).FirstOrDefault(); } - + public virtual int[] GetAlsoPurchasedProductsIds(int storeId, int productId, int recordsToReturn = 5, bool showHidden = false) { if (productId == 0) - throw new ArgumentException("Product ID is not specified"); + { + return new int[0]; + } - //this inner query should retrieve all orders that have contained the productID - var query1 = from orderItem in _orderItemRepository.Table - where orderItem.ProductId == productId - select orderItem.OrderId; + // This inner query should retrieve all orders that have contained the productID. + var query1 = from orderItem in _orderItemRepository.Table + where orderItem.ProductId == productId + select orderItem.OrderId; var query2 = from orderItem in _orderItemRepository.Table join p in _productRepository.Table on orderItem.ProductId equals p.Id where (query1.Contains(orderItem.OrderId)) && (p.Id != productId) && (showHidden || p.Published) && - (!orderItem.Order.Deleted) && - (storeId == 0 || orderItem.Order.StoreId == storeId) && + (!orderItem.Order.Deleted) && + (storeId == 0 || orderItem.Order.StoreId == storeId) && (!p.Deleted) && (!p.IsSystemProduct) && - (showHidden || p.Published) - select new { orderItem = orderItem, p }; + (showHidden || p.Published) + select new { orderItem, p }; var query3 = from orderItem_p in query2 group orderItem_p by orderItem_p.p.Id into g @@ -306,102 +292,140 @@ group orderItem_p by orderItem_p.p.Id into g ProductId = g.Key, ProductsPurchased = g.Sum(x => x.orderItem.Quantity), }; + query3 = query3.OrderByDescending(x => x.ProductsPurchased); - if (recordsToReturn > 0) - query3 = query3.Take(() => recordsToReturn); + if (recordsToReturn > 0) + { + query3 = query3.Take(() => recordsToReturn); + } - var report = query3.ToList(); + var report = query3.ToList(); - var ids = new List(); - foreach (var line in report) - ids.Add(line.ProductId); + var ids = report + .Select(x => x.ProductId) + .ToArray(); - return ids.ToArray(); + return ids; } - /// - /// Gets a list of products that were never sold - /// - /// Order start time; null to load all - /// Order end time; null to load all - /// Page index - /// Page size - /// A value indicating whether to show hidden records - /// Products - public virtual IPagedList ProductsNeverSold(DateTime? startTime, - DateTime? endTime, int pageIndex, int pageSize, bool showHidden = false) + public virtual IPagedList ProductsNeverSold(DateTime? startTime, DateTime? endTime, int pageIndex, int pageSize, bool showHidden = false) { - var groupedProductId = (int)ProductType.GroupedProduct; - - // This inner query should retrieve all purchased order product varint identifiers. - var query1 = (from orderItem in _orderItemRepository.Table - join o in _orderRepository.Table on orderItem.OrderId equals o.Id - where (!startTime.HasValue || startTime.Value <= o.CreatedOnUtc) && - (!endTime.HasValue || endTime.Value >= o.CreatedOnUtc) && - (!o.Deleted) + var groupedProductId = (int)ProductType.GroupedProduct; + + var query1 = (from orderItem in _orderItemRepository.TableUntracked + join o in _orderRepository.TableUntracked on orderItem.OrderId equals o.Id + where !o.Deleted && + (!startTime.HasValue || startTime.Value <= o.CreatedOnUtc) && + (!endTime.HasValue || endTime.Value >= o.CreatedOnUtc) select orderItem.ProductId).Distinct(); - var query2 = from p in _productRepository.Table + var query2 = from p in _productRepository.TableUntracked where !query1.Contains(p.Id) && - p.ProductTypeId != groupedProductId && - !p.Deleted && - (showHidden || p.Published) - orderby p.Name - select p; - - var products = new PagedList(query2, pageIndex, pageSize); - return products; + !p.Deleted && !p.IsSystemProduct && + (showHidden || p.Published) && + p.ProductTypeId != groupedProductId + orderby p.Name + select p; + + var products = new PagedList(query2, pageIndex, pageSize); + return products; } - /// - /// Get profit report - /// - /// Store identifier - /// Start date - /// End date - /// Filter by order status - /// Filter by payment status - /// Filter by shipping status - /// Billing email. Leave empty to load all records. - /// Result - public virtual decimal ProfitReport(int storeId, int[] orderStatusIds, int[] paymentStatusIds, int[] shippingStatusIds, - DateTime? startTimeUtc, DateTime? endTimeUtc, string billingEmail) + public virtual decimal GetProfit(IQueryable orderQuery) { - //We cannot use String.IsNullOrEmpty(billingEmail) in SQL Compact - bool dontSearchEmail = String.IsNullOrEmpty(billingEmail); + var query = + from orderItem in _orderItemRepository.Table + join o in orderQuery on orderItem.OrderId equals o.Id + select orderItem; - var orders = - from o in _orderRepository.Table - where (storeId == 0 || storeId == o.StoreId) && - (!startTimeUtc.HasValue || startTimeUtc.Value <= o.CreatedOnUtc) && - (!endTimeUtc.HasValue || endTimeUtc.Value >= o.CreatedOnUtc) && - (!o.Deleted) && - (dontSearchEmail || (o.BillingAddress != null && !String.IsNullOrEmpty(o.BillingAddress.Email) && o.BillingAddress.Email.Contains(billingEmail))) - select o; + var productCost = Convert.ToDecimal(query.Sum(oi => (decimal?)oi.ProductCost * oi.Quantity)); + var summary = GetOrderAverageReportLine(orderQuery); + var profit = summary.SumOrders - summary.SumTax - productCost; - if (orderStatusIds != null && orderStatusIds.Count() > 0) - orders = orders.Where(x => orderStatusIds.Contains(x.OrderStatusId)); + return profit; + } - if (paymentStatusIds != null && paymentStatusIds.Count() > 0) - orders = orders.Where(x => paymentStatusIds.Contains(x.PaymentStatusId)); + public virtual IPagedList GetIncompleteOrders(int storeId, DateTime? startTimeUtc, DateTime? endTimeUtc) + { + var query = _orderRepository.Table; + query = query.Where(x => !x.Deleted && x.OrderStatusId != (int)OrderStatus.Cancelled); - if (shippingStatusIds != null && shippingStatusIds.Count() > 0) - orders = orders.Where(x => shippingStatusIds.Contains(x.ShippingStatusId)); + if (storeId > 0) + { + query = query.Where(x => x.StoreId == storeId); + } + if (startTimeUtc.HasValue) + { + query = query.Where(x => startTimeUtc.Value <= x.CreatedOnUtc); + } + if (endTimeUtc.HasValue) + { + query = query.Where(x => endTimeUtc.Value >= x.CreatedOnUtc); + } - var query = - from orderItem in _orderItemRepository.Table - join o in orders on orderItem.OrderId equals o.Id - select orderItem; + query = query.Where(x => + x.ShippingStatusId == (int)ShippingStatus.NotYetShipped + || x.PaymentStatusId == (int)PaymentStatus.Pending + ); + var dataPoints = query.Select(x => new OrderDataPoint + { + CreatedOn = x.CreatedOnUtc, + OrderTotal = x.OrderTotal, + OrderStatusId = x.OrderStatusId, + PaymentStatusId = x.PaymentStatusId, + ShippingStatusId = x.ShippingStatusId + }); + + return new PagedList(dataPoints, 0, int.MaxValue); + } - var productCost = Convert.ToDecimal(query.Sum(orderItem => (decimal?)orderItem.ProductCost * orderItem.Quantity)); + public virtual IPagedList GetOrdersDashboardData(int storeId, DateTime? startTimeUtc, DateTime? endTimeUtc, int pageIndex, int pageSize) + { + var query = _orderRepository.Table; + query = query.Where(x => !x.Deleted); - var reportSummary = GetOrderAverageReportLine(storeId, orderStatusIds, paymentStatusIds, shippingStatusIds, startTimeUtc, endTimeUtc, billingEmail); - var profit = reportSummary.SumOrders - reportSummary.SumTax - productCost; + if (storeId > 0) + { + query = query.Where(x => x.StoreId == storeId); + } + if (startTimeUtc.HasValue) + { + query = query.Where(x => startTimeUtc.Value <= x.CreatedOnUtc); + } + if (endTimeUtc.HasValue) + { + query = query.Where(x => endTimeUtc.Value >= x.CreatedOnUtc); + } - return profit; + var dataPoints = query.Select(x => new OrderDataPoint + { + CreatedOn = x.CreatedOnUtc, + OrderTotal = x.OrderTotal, + OrderStatusId = x.OrderStatusId + }); + return new PagedList(dataPoints, pageIndex, pageSize); } - #endregion + public virtual decimal GetOrdersTotal(int storeId, DateTime? startTimeUtc, DateTime? endTimeUtc) + { + var query = _orderRepository.Table; + query = query.Where(x => !x.Deleted); + + if (storeId > 0) + { + query = query.Where(x => x.StoreId == storeId); + } + if (startTimeUtc.HasValue) + { + query = query.Where(x => startTimeUtc.Value <= x.CreatedOnUtc); + } + if (endTimeUtc.HasValue) + { + query = query.Where(x => endTimeUtc.Value >= x.CreatedOnUtc); + } + + return query.Sum(x => (decimal?)x.OrderTotal) ?? decimal.Zero; + } } } diff --git a/src/Libraries/SmartStore.Services/Orders/OrderService.cs b/src/Libraries/SmartStore.Services/Orders/OrderService.cs index 0d9d8f965d..92ec02f77f 100644 --- a/src/Libraries/SmartStore.Services/Orders/OrderService.cs +++ b/src/Libraries/SmartStore.Services/Orders/OrderService.cs @@ -19,7 +19,7 @@ public partial class OrderService : IOrderService private readonly IRepository _orderRepository; private readonly IRepository _orderItemRepository; private readonly IRepository _orderNoteRepository; - private readonly IRepository _productRepository; + private readonly IRepository _productRepository; private readonly IRepository _recurringPaymentRepository; private readonly IRepository _customerRepository; private readonly IRepository _returnRequestRepository; @@ -28,16 +28,16 @@ public partial class OrderService : IOrderService public OrderService(IRepository orderRepository, IRepository orderItemRepository, IRepository orderNoteRepository, - IRepository productRepository, + IRepository productRepository, IRepository recurringPaymentRepository, - IRepository customerRepository, + IRepository customerRepository, IRepository returnRequestRepository, IEventPublisher eventPublisher) { _orderRepository = orderRepository; _orderItemRepository = orderItemRepository; _orderNoteRepository = orderNoteRepository; - _productRepository = productRepository; + _productRepository = productRepository; _recurringPaymentRepository = recurringPaymentRepository; _customerRepository = customerRepository; _returnRequestRepository = returnRequestRepository; @@ -62,9 +62,9 @@ where orderIds.Contains(o.Id) select o; var orders = query.ToList(); - // sort by passed identifier sequence - return orders.OrderBySequence(orderIds).ToList(); - } + // sort by passed identifier sequence + return orders.OrderBySequence(orderIds).ToList(); + } public virtual Order GetOrderByNumber(string orderNumber) { @@ -99,114 +99,114 @@ public virtual Order GetOrderByGuid(Guid orderGuid) return order; } - public virtual Order GetOrderByPaymentAuthorization(string paymentMethodSystemName, string authorizationTransactionId) - { - if (paymentMethodSystemName.IsEmpty() || authorizationTransactionId.IsEmpty()) - return null; - - var query = - from o in _orderRepository.Table - where o.PaymentMethodSystemName == paymentMethodSystemName && o.AuthorizationTransactionId == authorizationTransactionId - select o; - - return query.FirstOrDefault(); - } - - public virtual Order GetOrderByPaymentCapture(string paymentMethodSystemName, string captureTransactionId) - { - if (paymentMethodSystemName.IsEmpty() || captureTransactionId.IsEmpty()) - return null; - - var query = - from o in _orderRepository.Table - where o.PaymentMethodSystemName == paymentMethodSystemName && o.CaptureTransactionId == captureTransactionId - select o; - - return query.FirstOrDefault(); - } - - public virtual IQueryable GetOrders( - int storeId, - int customerId, - DateTime? startTime, - DateTime? endTime, - int[] orderStatusIds, - int[] paymentStatusIds, - int[] shippingStatusIds, - string billingEmail, - string orderNumber, - string billingName = null, + public virtual Order GetOrderByPaymentAuthorization(string paymentMethodSystemName, string authorizationTransactionId) + { + if (paymentMethodSystemName.IsEmpty() || authorizationTransactionId.IsEmpty()) + return null; + + var query = + from o in _orderRepository.Table + where o.PaymentMethodSystemName == paymentMethodSystemName && o.AuthorizationTransactionId == authorizationTransactionId + select o; + + return query.FirstOrDefault(); + } + + public virtual Order GetOrderByPaymentCapture(string paymentMethodSystemName, string captureTransactionId) + { + if (paymentMethodSystemName.IsEmpty() || captureTransactionId.IsEmpty()) + return null; + + var query = + from o in _orderRepository.Table + where o.PaymentMethodSystemName == paymentMethodSystemName && o.CaptureTransactionId == captureTransactionId + select o; + + return query.FirstOrDefault(); + } + + public virtual IQueryable GetOrders( + int storeId, + int customerId, + DateTime? startTime, + DateTime? endTime, + int[] orderStatusIds, + int[] paymentStatusIds, + int[] shippingStatusIds, + string billingEmail, + string orderNumber, + string billingName = null, string[] paymentMethods = null) - { - var query = _orderRepository.Table; + { + var query = _orderRepository.Table; - if (storeId > 0) - query = query.Where(x => x.StoreId == storeId); + if (customerId > 0) + query = query.Where(x => x.CustomerId == customerId); - if (customerId > 0) - query = query.Where(x => x.CustomerId == customerId); + if (storeId > 0) + query = query.Where(x => x.StoreId == storeId); - if (startTime.HasValue) - query = query.Where(x => startTime.Value <= x.CreatedOnUtc); + if (startTime.HasValue) + query = query.Where(x => startTime.Value <= x.CreatedOnUtc); - if (endTime.HasValue) - query = query.Where(x => endTime.Value >= x.CreatedOnUtc); + if (endTime.HasValue) + query = query.Where(x => endTime.Value >= x.CreatedOnUtc); - if (billingEmail.HasValue()) - query = query.Where(x => x.BillingAddress != null && !String.IsNullOrEmpty(x.BillingAddress.Email) && x.BillingAddress.Email.Contains(billingEmail)); + if (billingEmail.HasValue()) + query = query.Where(x => x.BillingAddress != null && !String.IsNullOrEmpty(x.BillingAddress.Email) && x.BillingAddress.Email.Contains(billingEmail)); - if (billingName.HasValue()) - { - query = query.Where(x => x.BillingAddress != null && ( - (!String.IsNullOrEmpty(x.BillingAddress.LastName) && x.BillingAddress.LastName.Contains(billingName)) || - (!String.IsNullOrEmpty(x.BillingAddress.FirstName) && x.BillingAddress.FirstName.Contains(billingName)) - )); - } + if (billingName.HasValue()) + { + query = query.Where(x => x.BillingAddress != null && ( + (!String.IsNullOrEmpty(x.BillingAddress.LastName) && x.BillingAddress.LastName.Contains(billingName)) || + (!String.IsNullOrEmpty(x.BillingAddress.FirstName) && x.BillingAddress.FirstName.Contains(billingName)) + )); + } - if (orderNumber.HasValue()) - query = query.Where(x => x.OrderNumber.ToLower().Contains(orderNumber.ToLower())); + if (orderNumber.HasValue()) + query = query.Where(x => x.OrderNumber.ToLower().Contains(orderNumber.ToLower())); - if (orderStatusIds != null && orderStatusIds.Count() > 0) - query = query.Where(x => orderStatusIds.Contains(x.OrderStatusId)); + 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 (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 (shippingStatusIds != null && shippingStatusIds.Count() > 0) + query = query.Where(x => shippingStatusIds.Contains(x.ShippingStatusId)); if (paymentMethods != null && paymentMethods.Any()) { query = query.Where(x => paymentMethods.Contains(x.PaymentMethodSystemName)); } - query = query.Where(x => !x.Deleted); + query = query.Where(x => !x.Deleted); - return query; - } + return query; + } - 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, + 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, string[] paymentMethods = null) { - var query = GetOrders(storeId, customerId, startTime, endTime, orderStatusIds, paymentStatusIds, shippingStatusIds, - billingEmail, orderNumber, billingName, paymentMethods); - - query = query.OrderByDescending(x => x.CreatedOnUtc); - - if (orderGuid.HasValue()) - { - // 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())); - - return new PagedList(orders, pageIndex, pageSize); - } - else - { - return new PagedList(query, pageIndex, pageSize); - } + var query = GetOrders(storeId, customerId, startTime, endTime, orderStatusIds, paymentStatusIds, shippingStatusIds, + billingEmail, orderNumber, billingName, paymentMethods); + + query = query.OrderByDescending(x => x.CreatedOnUtc); + + if (orderGuid.HasValue()) + { + // 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())); + + return new PagedList(orders, pageIndex, pageSize); + } + else + { + return new PagedList(query, pageIndex, pageSize); + } } public virtual IPagedList GetAllOrders(int affiliateId, int pageIndex, int pageSize) @@ -245,63 +245,63 @@ public virtual void UpdateOrder(Order order) _orderRepository.Update(order); - _eventPublisher.PublishOrderUpdated(order); + _eventPublisher.PublishOrderUpdated(order); } - public virtual void DeleteOrder(Order order) - { - if (order == null) - throw new ArgumentNullException("order"); + public virtual void DeleteOrder(Order order) + { + if (order == null) + throw new ArgumentNullException("order"); - order.Deleted = true; - UpdateOrder(order); - } + order.Deleted = true; + UpdateOrder(order); + } public virtual void DeleteOrderNote(OrderNote orderNote) { if (orderNote == null) throw new ArgumentNullException("orderNote"); - int orderId = orderNote.OrderId; + int orderId = orderNote.OrderId; _orderNoteRepository.Delete(orderNote); - var order = GetOrderById(orderId); - _eventPublisher.PublishOrderUpdated(order); + var order = GetOrderById(orderId); + _eventPublisher.PublishOrderUpdated(order); } public virtual Order GetOrderByAuthorizationTransactionIdAndPaymentMethod(string authorizationTransactionId, string paymentMethodSystemName) - { + { var query = _orderRepository.Table; if (!String.IsNullOrWhiteSpace(authorizationTransactionId)) query = query.Where(o => o.AuthorizationTransactionId == authorizationTransactionId); - + if (!String.IsNullOrWhiteSpace(paymentMethodSystemName)) query = query.Where(o => o.PaymentMethodSystemName == paymentMethodSystemName); - + query = query.OrderByDescending(o => o.CreatedOnUtc); var order = query.FirstOrDefault(); return order; } - public virtual void AddOrderNote(Order order, string note, bool displayToCustomer = false) - { - if (order != null && note.HasValue()) - { - order.OrderNotes.Add(new OrderNote - { - Note = note, - DisplayToCustomer = displayToCustomer, - CreatedOnUtc = DateTime.UtcNow - }); - - UpdateOrder(order); - } - } + public virtual void AddOrderNote(Order order, string note, bool displayToCustomer = false) + { + if (order != null && note.HasValue()) + { + order.OrderNotes.Add(new OrderNote + { + Note = note, + DisplayToCustomer = displayToCustomer, + CreatedOnUtc = DateTime.UtcNow + }); + + UpdateOrder(order); + } + } - #region Order items + #region Order items - public virtual OrderItem GetOrderItemById(int orderItemId) + public virtual OrderItem GetOrderItemById(int orderItemId) { if (orderItemId == 0) return null; @@ -313,7 +313,7 @@ public virtual OrderItem GetOrderItemByGuid(Guid orderItemGuid) { if (orderItemGuid == Guid.Empty) return null; - + var query = from orderItem in _orderItemRepository.Table where orderItem.OrderItemGuid == orderItemGuid select orderItem; @@ -337,11 +337,11 @@ public virtual IList GetAllOrderItems(int? orderId, int? shippingStatusId = null; if (ss.HasValue) shippingStatusId = (int)ss.Value; - + var query = from orderItem in _orderItemRepository.Table join o in _orderRepository.Table on orderItem.OrderId equals o.Id - join p in _productRepository.Table on orderItem.ProductId equals p.Id + join p in _productRepository.Table on orderItem.ProductId equals p.Id where (!orderId.HasValue || orderId.Value == 0 || orderId == o.Id) && (!customerId.HasValue || customerId.Value == 0 || customerId == o.CustomerId) && (!startTime.HasValue || startTime.Value <= o.CreatedOnUtc) && @@ -358,38 +358,38 @@ join p in _productRepository.Table on orderItem.ProductId equals p.Id return orderItems; } - public virtual Multimap GetOrderItemsByOrderIds(int[] orderIds) - { - Guard.NotNull(orderIds, nameof(orderIds)); + public virtual Multimap GetOrderItemsByOrderIds(int[] orderIds) + { + Guard.NotNull(orderIds, nameof(orderIds)); - var query = - from x in _orderItemRepository.TableUntracked.Expand(x => x.Product) - where orderIds.Contains(x.OrderId) - select x; + var query = + from x in _orderItemRepository.TableUntracked.Expand(x => x.Product) + where orderIds.Contains(x.OrderId) + select x; - var map = query - .OrderBy(x => x.OrderId) - .ToList() - .ToMultimap(x => x.OrderId, x => x); + var map = query + .OrderBy(x => x.OrderId) + .ToList() + .ToMultimap(x => x.OrderId, x => x); - return map; - } + return map; + } public virtual void DeleteOrderItem(OrderItem orderItem) { if (orderItem == null) throw new ArgumentNullException("orderItem"); - int orderId = orderItem.OrderId; + int orderId = orderItem.OrderId; _orderItemRepository.Delete(orderItem); - var order = GetOrderById(orderId); - _eventPublisher.PublishOrderUpdated(order); + var order = GetOrderById(orderId); + _eventPublisher.PublishOrderUpdated(order); } #endregion - + #region Recurring payments public virtual void DeleteRecurringPayment(RecurringPayment recurringPayment) @@ -406,7 +406,7 @@ public virtual RecurringPayment GetRecurringPaymentById(int recurringPaymentId) if (recurringPaymentId == 0) return null; - return _recurringPaymentRepository.GetById(recurringPaymentId); + return _recurringPaymentRepository.GetById(recurringPaymentId); } public virtual void InsertRecurringPayment(RecurringPayment recurringPayment) @@ -416,7 +416,7 @@ public virtual void InsertRecurringPayment(RecurringPayment recurringPayment) _recurringPaymentRepository.Insert(recurringPayment); - _eventPublisher.PublishOrderUpdated(recurringPayment.InitialOrder); + _eventPublisher.PublishOrderUpdated(recurringPayment.InitialOrder); } public virtual void UpdateRecurringPayment(RecurringPayment recurringPayment) @@ -426,16 +426,24 @@ public virtual void UpdateRecurringPayment(RecurringPayment recurringPayment) _recurringPaymentRepository.Update(recurringPayment); - _eventPublisher.PublishOrderUpdated(recurringPayment.InitialOrder); + _eventPublisher.PublishOrderUpdated(recurringPayment.InitialOrder); } - public virtual IList SearchRecurringPayments(int storeId, - int customerId, int initialOrderId, OrderStatus? initialOrderStatus, - bool showHidden = false) + public virtual IPagedList SearchRecurringPayments( + int storeId, + int customerId, + int initialOrderId, + OrderStatus? initialOrderStatus, + bool showHidden = false, + int pageIndex = 0, + int pageSize = int.MaxValue) { int? initialOrderStatusId = null; + if (initialOrderStatus.HasValue) + { initialOrderStatusId = (int)initialOrderStatus.Value; + } var query1 = from rp in _recurringPaymentRepository.Table join c in _customerRepository.Table on rp.InitialOrder.CustomerId equals c.Id @@ -445,7 +453,7 @@ join c in _customerRepository.Table on rp.InitialOrder.CustomerId equals c.Id (showHidden || !c.Deleted) && (showHidden || rp.IsActive) && (customerId == 0 || rp.InitialOrder.CustomerId == customerId) && - (storeId == 0 || rp.InitialOrder.StoreId == storeId) && + (storeId == 0 || rp.InitialOrder.StoreId == storeId) && (initialOrderId == 0 || rp.InitialOrder.Id == initialOrderId) && (!initialOrderStatusId.HasValue || initialOrderStatusId.Value == 0 || rp.InitialOrder.OrderStatusId == initialOrderStatusId.Value) select rp.Id; @@ -454,9 +462,8 @@ join c in _customerRepository.Table on rp.InitialOrder.CustomerId equals c.Id where query1.Contains(rp.Id) orderby rp.StartDateUtc, rp.Id select rp; - - var recurringPayments = query2.ToList(); - return recurringPayments; + + return new PagedList(query2, pageIndex, pageSize); } #endregion @@ -468,12 +475,12 @@ public virtual void DeleteReturnRequest(ReturnRequest returnRequest) if (returnRequest == null) throw new ArgumentNullException("returnRequest"); - int orderItemId = returnRequest.OrderItemId; + int orderItemId = returnRequest.OrderItemId; _returnRequestRepository.Delete(returnRequest); - var orderItem = GetOrderItemById(orderItemId); - _eventPublisher.PublishOrderUpdated(orderItem.Order); + var orderItem = GetOrderItemById(orderItemId); + _eventPublisher.PublishOrderUpdated(orderItem.Order); } public virtual ReturnRequest GetReturnRequestById(int returnRequestId) @@ -484,21 +491,21 @@ public virtual ReturnRequest GetReturnRequestById(int returnRequestId) return _returnRequestRepository.GetById(returnRequestId); } - public virtual IPagedList SearchReturnRequests(int storeId, int customerId, int orderItemId, ReturnRequestStatus? rs, int pageIndex, int pageSize, int id = 0) + public virtual IPagedList SearchReturnRequests(int storeId, int customerId, int orderItemId, ReturnRequestStatus? rs, int pageIndex, int pageSize, int id = 0) { var query = _returnRequestRepository.Table; - if (storeId > 0) - query = query.Where(rr => storeId == rr.StoreId); - - if (customerId > 0) + if (storeId > 0) + query = query.Where(rr => storeId == rr.StoreId); + + if (customerId > 0) query = query.Where(rr => customerId == rr.CustomerId); - if (orderItemId > 0) - query = query.Where(rr => rr.OrderItemId == orderItemId); + if (orderItemId > 0) + query = query.Where(rr => rr.OrderItemId == orderItemId); - if (id != 0) - query = query.Where(rr => rr.Id == id); + if (id != 0) + query = query.Where(rr => rr.Id == id); if (rs.HasValue) { @@ -508,7 +515,7 @@ public virtual IPagedList SearchReturnRequests(int storeId, int c query = query.OrderByDescending(rr => rr.CreatedOnUtc).ThenByDescending(rr => rr.Id); - var returnRequests = new PagedList(query, pageIndex, pageSize); + var returnRequests = new PagedList(query, pageIndex, pageSize); return returnRequests; } diff --git a/src/Libraries/SmartStore.Services/Orders/OrderTotalCalculationService.cs b/src/Libraries/SmartStore.Services/Orders/OrderTotalCalculationService.cs index 7bcf2910a0..9a0aa20a08 100644 --- a/src/Libraries/SmartStore.Services/Orders/OrderTotalCalculationService.cs +++ b/src/Libraries/SmartStore.Services/Orders/OrderTotalCalculationService.cs @@ -27,16 +27,16 @@ namespace SmartStore.Services.Orders /// public partial class OrderTotalCalculationService : IOrderTotalCalculationService { - private const string CART_TAXING_INFO_KEY = "CartTaxingInfos"; + private const string CART_TAXING_INFO_KEY = "CartTaxingInfos"; - #region Fields + #region Fields - private readonly IWorkContext _workContext; - private readonly IStoreContext _storeContext; + private readonly IWorkContext _workContext; + private readonly IStoreContext _storeContext; private readonly IPriceCalculationService _priceCalculationService; private readonly ITaxService _taxService; private readonly IShippingService _shippingService; - private readonly IProviderManager _providerManager; + private readonly IProviderManager _providerManager; private readonly ICheckoutAttributeParser _checkoutAttributeParser; private readonly IDiscountService _discountService; private readonly IGiftCardService _giftCardService; @@ -74,11 +74,11 @@ public partial class OrderTotalCalculationService : IOrderTotalCalculationServic /// Catalog settings public OrderTotalCalculationService( IWorkContext workContext, - IStoreContext storeContext, + IStoreContext storeContext, IPriceCalculationService priceCalculationService, ITaxService taxService, IShippingService shippingService, - IProviderManager providerManager, + IProviderManager providerManager, ICheckoutAttributeParser checkoutAttributeParser, IDiscountService discountService, IGiftCardService giftCardService, @@ -93,210 +93,210 @@ public OrderTotalCalculationService( CatalogSettings catalogSettings) { _workContext = workContext; - _storeContext = storeContext; + _storeContext = storeContext; _priceCalculationService = priceCalculationService; _taxService = taxService; _shippingService = shippingService; - _providerManager = providerManager; + _providerManager = providerManager; _checkoutAttributeParser = checkoutAttributeParser; _discountService = discountService; _giftCardService = giftCardService; _genericAttributeService = genericAttributeService; _paymentService = paymentService; _currencyService = currencyService; - _productAttributeParser = productAttributeParser; + _productAttributeParser = productAttributeParser; _taxSettings = taxSettings; _rewardPointsSettings = rewardPointsSettings; _shippingSettings = shippingSettings; _shoppingCartSettings = shoppingCartSettings; _catalogSettings = catalogSettings; - T = NullLocalizer.Instance; - } - - public Localizer T { get; set; } - - #endregion - - #region Utilities - - private Func GetTaxingInfo = cartItem => (CartTaxingInfo)cartItem.CustomProperties[CART_TAXING_INFO_KEY]; - - protected virtual void PrepareAuxiliaryServicesTaxingInfos(IList cart) - { - // no additional infos required - if (!cart.Any() || _taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.SpecifiedTaxCategory) - return; - - // additional infos already collected - if (cart.First().CustomProperties.ContainsKey(CART_TAXING_INFO_KEY)) - return; - - // instance taxing info objects - cart.Each(x => x.CustomProperties[CART_TAXING_INFO_KEY] = new CartTaxingInfo()); - - // collect infos - if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestCartAmount) - { - // calculate all subtotals - cart.Each(x => GetTaxingInfo(x).SubTotalWithoutDiscount = _priceCalculationService.GetSubTotal(x, false)); - - // items with the highest subtotal - var highestAmountItems = cart - .GroupBy(x => x.Item.Product.TaxCategoryId) - .OrderByDescending(x => x.Sum(y => GetTaxingInfo(y).SubTotalWithoutDiscount)) - .First(); - - // mark items - highestAmountItems.Each(x => GetTaxingInfo(x).HasHighestCartAmount = true); - } - else if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestTaxRate) - { - var customer = cart.GetCustomer(); - var maxTaxRate = decimal.Zero; - var maxTaxCategoryId = 0; - - // get tax category id with the highest rate - cart.Each(x => - { - var taxRate = _taxService.GetTaxRate(x.Item.Product, x.Item.Product.TaxCategoryId, customer); - if (taxRate > maxTaxRate) - { - maxTaxRate = taxRate; - maxTaxCategoryId = x.Item.Product.TaxCategoryId; - } - }); - - // mark items - cart.Where(x => x.Item.Product.TaxCategoryId == maxTaxCategoryId) - .Each(x => GetTaxingInfo(x).HasHighestTaxRate = true); - } - //else if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.ProRata) - //{ - // // calculate all subtotals - // cart.Each(x => GetTaxingInfo(x).SubTotalWithoutDiscount = _priceCalculationService.GetSubTotal(x, false)); - - // // sum over all subtotals - // var subTotalSum = cart.Sum(x => GetTaxingInfo(x).SubTotalWithoutDiscount); - - // // calculate pro rata weightings - // cart.Each(x => - // { - // var taxingInfo = GetTaxingInfo(x); - // taxingInfo.ProRataWeighting = taxingInfo.SubTotalWithoutDiscount / subTotalSum; - // }); - //} - } - - protected virtual decimal? GetAdjustedShippingTotal(IList cart, out Discount appliedDiscount) - { - appliedDiscount = null; - - decimal? shippingTotal = null; - ShippingOption shippingOption = null; - var customer = cart.GetCustomer(); - var storeId = _storeContext.CurrentStore.Id; - - if (customer != null) - { - shippingOption = customer.GetAttribute(SystemCustomerAttributeNames.SelectedShippingOption, _genericAttributeService, storeId); - } - - if (shippingOption != null) - { - // use last shipping option (get from cache) - var shippingMethods = _shippingService.GetAllShippingMethods(null, storeId); - shippingTotal = AdjustShippingRate(shippingOption.Rate, cart, shippingOption, shippingMethods, out appliedDiscount); - } - else - { - // use fixed rate (if possible) - Address shippingAddress = null; - if (customer != null) - shippingAddress = customer.ShippingAddress; - - var shippingRateComputationMethods = _shippingService.LoadActiveShippingRateComputationMethods(storeId); - if (!shippingRateComputationMethods.Any()) - throw new SmartException(T("Shipping.CouldNotLoadMethod")); - - if (shippingRateComputationMethods.Count() == 1) - { - var shippingRateComputationMethod = shippingRateComputationMethods.First(); - var getShippingOptionRequest = _shippingService.CreateShippingOptionRequest(cart, shippingAddress, storeId); - var fixedRate = shippingRateComputationMethod.Value.GetFixedRate(getShippingOptionRequest); - - if (fixedRate.HasValue) - { - shippingTotal = AdjustShippingRate(fixedRate.Value, cart, null, null, out appliedDiscount); - } - } - } - - return shippingTotal; - } - - protected virtual decimal GetShippingTaxAmount( - decimal shipping, - Customer customer, - int taxCategoryId, - SortedDictionary taxRates) - { - var taxRate = decimal.Zero; - var shippingExclTax = _taxService.GetShippingPrice(shipping, false, customer, taxCategoryId, out taxRate); - var shippingInclTax = _taxService.GetShippingPrice(shipping, true, customer, taxCategoryId, out taxRate); - - var shippingTax = shippingInclTax - shippingExclTax; - - if (shippingTax < decimal.Zero) - shippingTax = decimal.Zero; - - if (taxRate > decimal.Zero && shippingTax > decimal.Zero) - { - if (taxRates.ContainsKey(taxRate)) - taxRates[taxRate] = taxRates[taxRate] + shippingTax; - else - taxRates.Add(taxRate, shippingTax); - } - - return shippingTax; - } - - protected virtual decimal GetPaymentFeeTaxAmount( - decimal paymentFee, - Customer customer, - int taxCategoryId, - SortedDictionary taxRates) - { - var taxRate = decimal.Zero; - var paymentFeeExclTax = _taxService.GetPaymentMethodAdditionalFee(paymentFee, false, customer, taxCategoryId, out taxRate); - var paymentFeeInclTax = _taxService.GetPaymentMethodAdditionalFee(paymentFee, true, customer, taxCategoryId, out taxRate); - - var paymentFeeTax = paymentFeeInclTax - paymentFeeExclTax; - - if (taxRate > decimal.Zero && paymentFeeTax != decimal.Zero) - { - if (taxRates.ContainsKey(taxRate)) - taxRates[taxRate] = taxRates[taxRate] + paymentFeeTax; - else - taxRates.Add(taxRate, paymentFeeTax); - } - - return paymentFeeTax; - } - - #endregion - - #region Methods - - /// - /// Gets shopping cart subtotal - /// - /// Cart - /// Applied discount amount - /// Applied discount - /// Sub total (without discount) - /// Sub total (with discount) - public virtual void GetShoppingCartSubTotal(IList cart, + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + #endregion + + #region Utilities + + private Func GetTaxingInfo = cartItem => (CartTaxingInfo)cartItem.CustomProperties[CART_TAXING_INFO_KEY]; + + protected virtual void PrepareAuxiliaryServicesTaxingInfos(IList cart) + { + // no additional infos required + if (!cart.Any() || _taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.SpecifiedTaxCategory) + return; + + // additional infos already collected + if (cart.First().CustomProperties.ContainsKey(CART_TAXING_INFO_KEY)) + return; + + // instance taxing info objects + cart.Each(x => x.CustomProperties[CART_TAXING_INFO_KEY] = new CartTaxingInfo()); + + // collect infos + if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestCartAmount) + { + // calculate all subtotals + cart.Each(x => GetTaxingInfo(x).SubTotalWithoutDiscount = _priceCalculationService.GetSubTotal(x, false)); + + // items with the highest subtotal + var highestAmountItems = cart + .GroupBy(x => x.Item.Product.TaxCategoryId) + .OrderByDescending(x => x.Sum(y => GetTaxingInfo(y).SubTotalWithoutDiscount)) + .First(); + + // mark items + highestAmountItems.Each(x => GetTaxingInfo(x).HasHighestCartAmount = true); + } + else if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestTaxRate) + { + var customer = cart.GetCustomer(); + var maxTaxRate = decimal.Zero; + var maxTaxCategoryId = 0; + + // get tax category id with the highest rate + cart.Each(x => + { + var taxRate = _taxService.GetTaxRate(x.Item.Product, x.Item.Product.TaxCategoryId, customer); + if (taxRate > maxTaxRate) + { + maxTaxRate = taxRate; + maxTaxCategoryId = x.Item.Product.TaxCategoryId; + } + }); + + // mark items + cart.Where(x => x.Item.Product.TaxCategoryId == maxTaxCategoryId) + .Each(x => GetTaxingInfo(x).HasHighestTaxRate = true); + } + //else if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.ProRata) + //{ + // // calculate all subtotals + // cart.Each(x => GetTaxingInfo(x).SubTotalWithoutDiscount = _priceCalculationService.GetSubTotal(x, false)); + + // // sum over all subtotals + // var subTotalSum = cart.Sum(x => GetTaxingInfo(x).SubTotalWithoutDiscount); + + // // calculate pro rata weightings + // cart.Each(x => + // { + // var taxingInfo = GetTaxingInfo(x); + // taxingInfo.ProRataWeighting = taxingInfo.SubTotalWithoutDiscount / subTotalSum; + // }); + //} + } + + protected virtual decimal? GetAdjustedShippingTotal(IList cart, out Discount appliedDiscount) + { + appliedDiscount = null; + + decimal? shippingTotal = null; + ShippingOption shippingOption = null; + var customer = cart.GetCustomer(); + var storeId = _storeContext.CurrentStore.Id; + + if (customer != null) + { + shippingOption = customer.GetAttribute(SystemCustomerAttributeNames.SelectedShippingOption, _genericAttributeService, storeId); + } + + if (shippingOption != null) + { + // use last shipping option (get from cache) + var shippingMethods = _shippingService.GetAllShippingMethods(null, storeId); + shippingTotal = AdjustShippingRate(shippingOption.Rate, cart, shippingOption, shippingMethods, out appliedDiscount); + } + else + { + // use fixed rate (if possible) + Address shippingAddress = null; + if (customer != null) + shippingAddress = customer.ShippingAddress; + + var shippingRateComputationMethods = _shippingService.LoadActiveShippingRateComputationMethods(storeId); + if (!shippingRateComputationMethods.Any()) + throw new SmartException(T("Shipping.CouldNotLoadMethod")); + + if (shippingRateComputationMethods.Count() == 1) + { + var shippingRateComputationMethod = shippingRateComputationMethods.First(); + var getShippingOptionRequest = _shippingService.CreateShippingOptionRequest(cart, shippingAddress, storeId); + var fixedRate = shippingRateComputationMethod.Value.GetFixedRate(getShippingOptionRequest); + + if (fixedRate.HasValue) + { + shippingTotal = AdjustShippingRate(fixedRate.Value, cart, null, null, out appliedDiscount); + } + } + } + + return shippingTotal; + } + + protected virtual decimal GetShippingTaxAmount( + decimal shipping, + Customer customer, + int taxCategoryId, + SortedDictionary taxRates) + { + var taxRate = decimal.Zero; + var shippingExclTax = _taxService.GetShippingPrice(shipping, false, customer, taxCategoryId, out taxRate); + var shippingInclTax = _taxService.GetShippingPrice(shipping, true, customer, taxCategoryId, out taxRate); + + var shippingTax = shippingInclTax - shippingExclTax; + + if (shippingTax < decimal.Zero) + shippingTax = decimal.Zero; + + if (taxRate > decimal.Zero && shippingTax > decimal.Zero) + { + if (taxRates.ContainsKey(taxRate)) + taxRates[taxRate] = taxRates[taxRate] + shippingTax; + else + taxRates.Add(taxRate, shippingTax); + } + + return shippingTax; + } + + protected virtual decimal GetPaymentFeeTaxAmount( + decimal paymentFee, + Customer customer, + int taxCategoryId, + SortedDictionary taxRates) + { + var taxRate = decimal.Zero; + var paymentFeeExclTax = _taxService.GetPaymentMethodAdditionalFee(paymentFee, false, customer, taxCategoryId, out taxRate); + var paymentFeeInclTax = _taxService.GetPaymentMethodAdditionalFee(paymentFee, true, customer, taxCategoryId, out taxRate); + + var paymentFeeTax = paymentFeeInclTax - paymentFeeExclTax; + + if (taxRate > decimal.Zero && paymentFeeTax != decimal.Zero) + { + if (taxRates.ContainsKey(taxRate)) + taxRates[taxRate] = taxRates[taxRate] + paymentFeeTax; + else + taxRates.Add(taxRate, paymentFeeTax); + } + + return paymentFeeTax; + } + + #endregion + + #region Methods + + /// + /// Gets shopping cart subtotal + /// + /// Cart + /// Applied discount amount + /// Applied discount + /// Sub total (without discount) + /// Sub total (with discount) + public virtual void GetShoppingCartSubTotal(IList cart, out decimal discountAmount, out Discount appliedDiscount, out decimal subTotalWithoutDiscount, out decimal subTotalWithDiscount) { @@ -312,12 +312,12 @@ public virtual void GetShoppingCartSubTotal(IList car break; } - GetShoppingCartSubTotal(cart, - includingTax, - out discountAmount, - out appliedDiscount, - out subTotalWithoutDiscount, - out subTotalWithDiscount); + GetShoppingCartSubTotal(cart, + includingTax, + out discountAmount, + out appliedDiscount, + out subTotalWithoutDiscount, + out subTotalWithDiscount); } /// @@ -336,13 +336,13 @@ public virtual void GetShoppingCartSubTotal(IList car { SortedDictionary taxRates = null; - GetShoppingCartSubTotal(cart, - includingTax, - out discountAmount, - out appliedDiscount, - out subTotalWithoutDiscount, - out subTotalWithDiscount, - out taxRates); + GetShoppingCartSubTotal(cart, + includingTax, + out discountAmount, + out appliedDiscount, + out subTotalWithoutDiscount, + out subTotalWithDiscount, + out taxRates); } /// @@ -357,10 +357,10 @@ public virtual void GetShoppingCartSubTotal(IList car /// Tax rates (of order sub total) public virtual void GetShoppingCartSubTotal(IList cart, bool includingTax, - out decimal discountAmount, - out Discount appliedDiscount, - out decimal subTotalWithoutDiscount, - out decimal subTotalWithDiscount, + out decimal discountAmount, + out Discount appliedDiscount, + out decimal subTotalWithoutDiscount, + out decimal subTotalWithDiscount, out SortedDictionary taxRates) { discountAmount = decimal.Zero; @@ -374,42 +374,42 @@ public virtual void GetShoppingCartSubTotal(IList car // get the customer var customer = cart.GetCustomer(); - var currency = _workContext.WorkingCurrency; + var currency = _workContext.WorkingCurrency; - // sub totals - decimal subTotalExclTaxWithoutDiscount = decimal.Zero; + // sub totals + decimal subTotalExclTaxWithoutDiscount = decimal.Zero; decimal subTotalInclTaxWithoutDiscount = decimal.Zero; foreach (var shoppingCartItem in cart) - { - if (shoppingCartItem.Item.Product == null) - continue; - - decimal taxRate, sciSubTotal, sciExclTax, sciInclTax = decimal.Zero; - - shoppingCartItem.Item.Product.MergeWithCombination(shoppingCartItem.Item.AttributesXml, _productAttributeParser); - - if (currency.RoundOrderItemsEnabled) - { - // Gross > Net RoundFix - int qty = shoppingCartItem.Item.Quantity; - - sciSubTotal = _priceCalculationService.GetUnitPrice(shoppingCartItem, true); - - // Adaption to eliminate rounding issues - sciExclTax = _taxService.GetProductPrice(shoppingCartItem.Item.Product, sciSubTotal, false, customer, out taxRate); - sciExclTax = sciExclTax.RoundIfEnabledFor(currency) * qty; - sciInclTax = _taxService.GetProductPrice(shoppingCartItem.Item.Product, sciSubTotal, true, customer, out taxRate); - sciInclTax = sciInclTax.RoundIfEnabledFor(currency) * qty; - } - else - { - sciSubTotal = _priceCalculationService.GetSubTotal(shoppingCartItem, true); - sciExclTax = _taxService.GetProductPrice(shoppingCartItem.Item.Product, sciSubTotal, false, customer, out taxRate); - sciInclTax = _taxService.GetProductPrice(shoppingCartItem.Item.Product, sciSubTotal, true, customer, out taxRate); - } - - subTotalExclTaxWithoutDiscount += sciExclTax; + { + if (shoppingCartItem.Item.Product == null) + continue; + + decimal taxRate, sciSubTotal, sciExclTax, sciInclTax = decimal.Zero; + + shoppingCartItem.Item.Product.MergeWithCombination(shoppingCartItem.Item.AttributesXml, _productAttributeParser); + + if (currency.RoundOrderItemsEnabled) + { + // Gross > Net RoundFix + int qty = shoppingCartItem.Item.Quantity; + + sciSubTotal = _priceCalculationService.GetUnitPrice(shoppingCartItem, true); + + // Adaption to eliminate rounding issues + sciExclTax = _taxService.GetProductPrice(shoppingCartItem.Item.Product, sciSubTotal, false, customer, out taxRate); + sciExclTax = sciExclTax.RoundIfEnabledFor(currency) * qty; + sciInclTax = _taxService.GetProductPrice(shoppingCartItem.Item.Product, sciSubTotal, true, customer, out taxRate); + sciInclTax = sciInclTax.RoundIfEnabledFor(currency) * qty; + } + else + { + sciSubTotal = _priceCalculationService.GetSubTotal(shoppingCartItem, true); + sciExclTax = _taxService.GetProductPrice(shoppingCartItem.Item.Product, sciSubTotal, false, customer, out taxRate); + sciInclTax = _taxService.GetProductPrice(shoppingCartItem.Item.Product, sciSubTotal, true, customer, out taxRate); + } + + subTotalExclTaxWithoutDiscount += sciExclTax; subTotalInclTaxWithoutDiscount += sciInclTax; // tax rates @@ -430,8 +430,8 @@ public virtual void GetShoppingCartSubTotal(IList car // checkout attributes if (customer != null) { - var checkoutAttributesXml = customer.GetAttribute(SystemCustomerAttributeNames.CheckoutAttributes, _genericAttributeService); - var caValues = _checkoutAttributeParser.ParseCheckoutAttributeValues(checkoutAttributesXml); + var checkoutAttributesXml = customer.GetAttribute(SystemCustomerAttributeNames.CheckoutAttributes, _genericAttributeService); + var caValues = _checkoutAttributeParser.ParseCheckoutAttributeValues(checkoutAttributesXml); if (caValues != null) { foreach (var caValue in caValues) @@ -460,20 +460,20 @@ public virtual void GetShoppingCartSubTotal(IList car } } - //subtotal without discount - if (includingTax) - { - subTotalWithoutDiscount = subTotalInclTaxWithoutDiscount; - } - else - { - subTotalWithoutDiscount = subTotalExclTaxWithoutDiscount; - } - - if (subTotalWithoutDiscount < decimal.Zero) - { - subTotalWithoutDiscount = decimal.Zero; - } + //subtotal without discount + if (includingTax) + { + subTotalWithoutDiscount = subTotalInclTaxWithoutDiscount; + } + else + { + subTotalWithoutDiscount = subTotalExclTaxWithoutDiscount; + } + + if (subTotalWithoutDiscount < decimal.Zero) + { + subTotalWithoutDiscount = decimal.Zero; + } subTotalWithoutDiscount = subTotalWithoutDiscount.RoundIfEnabledFor(currency); @@ -526,11 +526,11 @@ public virtual void GetShoppingCartSubTotal(IList car discountAmount = discountAmountExclTax; } - //round - if (subTotalWithDiscount < decimal.Zero) - { - subTotalWithDiscount = decimal.Zero; - } + //round + if (subTotalWithDiscount < decimal.Zero) + { + subTotalWithDiscount = decimal.Zero; + } subTotalWithDiscount = subTotalWithDiscount.RoundIfEnabledFor(currency); } @@ -542,8 +542,7 @@ public virtual void GetShoppingCartSubTotal(IList car /// Order subtotal /// Applied discount /// Order discount - public virtual decimal GetOrderSubtotalDiscount(Customer customer, - decimal orderSubTotal, out Discount appliedDiscount) + public virtual decimal GetOrderSubtotalDiscount(Customer customer, decimal orderSubTotal, out Discount appliedDiscount) { appliedDiscount = null; decimal discountAmount = decimal.Zero; @@ -552,16 +551,16 @@ public virtual decimal GetOrderSubtotalDiscount(Customer customer, var allDiscounts = _discountService.GetAllDiscounts(DiscountType.AssignedToOrderSubTotal); var allowedDiscounts = new List(); - if (allDiscounts != null) - { - foreach (var discount in allDiscounts) - { - if (discount.DiscountType == DiscountType.AssignedToOrderSubTotal && !allowedDiscounts.Any(x => x.Id == discount.Id) && _discountService.IsDiscountValid(discount, customer)) - { - allowedDiscounts.Add(discount); - } - } - } + if (allDiscounts != null) + { + foreach (var discount in allDiscounts) + { + if (discount.DiscountType == DiscountType.AssignedToOrderSubTotal && !allowedDiscounts.Any(x => x.Id == discount.Id) && _discountService.IsDiscountValid(discount, customer)) + { + allowedDiscounts.Add(discount); + } + } + } appliedDiscount = allowedDiscounts.GetPreferredDiscount(orderSubTotal); if (appliedDiscount != null) @@ -573,21 +572,21 @@ public virtual decimal GetOrderSubtotalDiscount(Customer customer, return discountAmount; } - + /// /// Gets shopping cart additional shipping charge /// /// Cart /// Additional shipping charge - public virtual decimal GetShoppingCartAdditionalShippingCharge(IList cart) + public virtual decimal GetShoppingCartAdditionalShippingCharge(IList cart) { if (IsFreeShipping(cart)) return decimal.Zero; - decimal additionalShippingCharge = decimal.Zero; + decimal additionalShippingCharge = decimal.Zero; - foreach (var sci in cart) - { + foreach (var sci in cart) + { if (_shippingSettings.ChargeOnlyHighestProductShippingSurcharge) { @@ -596,7 +595,7 @@ public virtual decimal GetShoppingCartAdditionalShippingCharge(IList cart) Customer customer = cart.GetCustomer(); if (customer != null) { - //check whether customer is in a customer role with free shipping applied - var customerRoles = customer.CustomerRoles.Where(cr => cr.Active); + // Check whether customer is in a customer role with free shipping applied. + var customerRoles = customer.CustomerRoleMappings + .Select(x => x.CustomerRole) + .Where(x => x.Active); + foreach (var customerRole in customerRoles) + { if (customerRole.FreeShipping) + { return true; + } + } } bool shoppingCartRequiresShipping = cart.RequiresShipping(); @@ -674,65 +680,65 @@ public virtual bool IsFreeShipping(IList cart) /// Cart /// Applied discount /// Adjusted shipping rate - public virtual decimal AdjustShippingRate(decimal shippingRate, IList cart, - ShippingOption shippingOption, IList shippingMethods, out Discount appliedDiscount) + public virtual decimal AdjustShippingRate(decimal shippingRate, IList cart, + ShippingOption shippingOption, IList shippingMethods, out Discount appliedDiscount) { appliedDiscount = null; - if (IsFreeShipping(cart)) - { - return decimal.Zero; - } - - var adjustedRate = decimal.Zero; - var bundlePerItemShipping = decimal.Zero; - var ignoreAdditionalShippingCharge = false; - - foreach (var sci in cart) - { - if (sci.Item.Product != null && sci.Item.Product.ProductType == ProductType.BundledProduct && sci.Item.Product.BundlePerItemShipping) - { - if (sci.ChildItems != null) - { - foreach (var childItem in sci.ChildItems.Where(x => x.Item.IsShipEnabled && !x.Item.IsFreeShipping)) - { - bundlePerItemShipping += shippingRate; - } - } - } - else if (adjustedRate == decimal.Zero) - { - adjustedRate = shippingRate; - } - } - - adjustedRate += bundlePerItemShipping; - - if (shippingOption != null && shippingMethods != null) - { - var shippingMethod = shippingMethods.FirstOrDefault(x => x.Id == shippingOption.ShippingMethodId); - if (shippingMethod != null) - { - ignoreAdditionalShippingCharge = shippingMethod.IgnoreCharges; - } - } - - // Additional shipping charges. - if (!ignoreAdditionalShippingCharge) - { - decimal additionalShippingCharge = GetShoppingCartAdditionalShippingCharge(cart); - adjustedRate += additionalShippingCharge; - } + if (IsFreeShipping(cart)) + { + return decimal.Zero; + } + + var adjustedRate = decimal.Zero; + var bundlePerItemShipping = decimal.Zero; + var ignoreAdditionalShippingCharge = false; + + foreach (var sci in cart) + { + if (sci.Item.Product != null && sci.Item.Product.ProductType == ProductType.BundledProduct && sci.Item.Product.BundlePerItemShipping) + { + if (sci.ChildItems != null) + { + foreach (var childItem in sci.ChildItems.Where(x => x.Item.IsShipEnabled && !x.Item.IsFreeShipping)) + { + bundlePerItemShipping += shippingRate; + } + } + } + else if (adjustedRate == decimal.Zero) + { + adjustedRate = shippingRate; + } + } + + adjustedRate += bundlePerItemShipping; + + if (shippingOption != null && shippingMethods != null) + { + var shippingMethod = shippingMethods.FirstOrDefault(x => x.Id == shippingOption.ShippingMethodId); + if (shippingMethod != null) + { + ignoreAdditionalShippingCharge = shippingMethod.IgnoreCharges; + } + } + + // Additional shipping charges. + if (!ignoreAdditionalShippingCharge) + { + decimal additionalShippingCharge = GetShoppingCartAdditionalShippingCharge(cart); + adjustedRate += additionalShippingCharge; + } // Discount. var customer = cart.GetCustomer(); var discountAmount = GetShippingDiscount(customer, adjustedRate, out appliedDiscount); adjustedRate = adjustedRate - discountAmount; - if (adjustedRate < decimal.Zero) - { - adjustedRate = decimal.Zero; - } + if (adjustedRate < decimal.Zero) + { + adjustedRate = decimal.Zero; + } adjustedRate = adjustedRate.RoundIfEnabledFor(_workContext.WorkingCurrency); return adjustedRate; @@ -767,9 +773,9 @@ public virtual decimal AdjustShippingRate(decimal shippingRate, IList cart, bool includingTax) { var taxRate = decimal.Zero; - Discount appliedDiscount = null; + Discount appliedDiscount = null; - return GetShoppingCartShippingTotal(cart, includingTax, out taxRate, out appliedDiscount); + return GetShoppingCartShippingTotal(cart, includingTax, out taxRate, out appliedDiscount); } /// @@ -781,93 +787,93 @@ public virtual decimal AdjustShippingRate(decimal shippingRate, IListApplied discount /// Shipping total public virtual decimal? GetShoppingCartShippingTotal( - IList cart, - bool includingTax, + IList cart, + bool includingTax, out decimal taxRate, - out Discount appliedDiscount) + out Discount appliedDiscount) { - appliedDiscount = null; - taxRate = decimal.Zero; + appliedDiscount = null; + taxRate = decimal.Zero; - if (IsFreeShipping(cart)) - return decimal.Zero; + if (IsFreeShipping(cart)) + return decimal.Zero; - decimal? shippingTotalTaxed = null; - var taxCategoryId = 0; - var customer = cart.GetCustomer(); - var currency = _workContext.WorkingCurrency; + decimal? shippingTotalTaxed = null; + var taxCategoryId = 0; + var customer = cart.GetCustomer(); + var currency = _workContext.WorkingCurrency; - var shippingTotal = GetAdjustedShippingTotal(cart, out appliedDiscount); + var shippingTotal = GetAdjustedShippingTotal(cart, out appliedDiscount); - if (!shippingTotal.HasValue) - return null; + if (!shippingTotal.HasValue) + return null; - if (shippingTotal.Value < decimal.Zero) - { - shippingTotal = decimal.Zero; - } + if (shippingTotal.Value < decimal.Zero) + { + shippingTotal = decimal.Zero; + } shippingTotal = shippingTotal.Value.RoundIfEnabledFor(currency); - PrepareAuxiliaryServicesTaxingInfos(cart); - - // commented out cause requires several plugins to be updated and migration of Order.OrderShippingTaxRate and Order.PaymentMethodAdditionalFeeTaxRate - //if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.ProRata) - //{ - // // calculate proRataShipping: get product weightings for cart and multiply them with the shipping amount - // shippingTotalTaxed = decimal.Zero; - - // var tmpTaxRate = decimal.Zero; - // var taxRates = new List(); - - // foreach (var item in cart) - // { - // var proRataShipping = shippingTotal.Value * GetTaxingInfo(item).ProRataWeighting; - // shippingTotalTaxed += _taxService.GetShippingPrice(proRataShipping, includingTax, customer, item.Item.Product.TaxCategoryId, out tmpTaxRate); - - // taxRates.Add(tmpTaxRate); - // } - - // // a tax rate is only defined if all rates are equal. return zero tax rate in all other cases. - // if (taxRates.Any() && taxRates.Distinct().Count() == 1) - // { - // taxRate = taxRates.First(); - // } - //} - //else - //{ - - if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestCartAmount) - { - var cartItem = cart.FirstOrDefault(x => GetTaxingInfo(x).HasHighestCartAmount); - taxCategoryId = (cartItem != null ? cartItem.Item.Product.TaxCategoryId : 0); - } - else if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestTaxRate) - { - var cartItem = cart.FirstOrDefault(x => GetTaxingInfo(x).HasHighestTaxRate); - taxCategoryId = (cartItem != null ? cartItem.Item.Product.TaxCategoryId : 0); - } - - // fallback to setting - if (taxCategoryId == 0) - { - taxCategoryId = _taxSettings.ShippingTaxClassId; - } - - shippingTotalTaxed = _taxService.GetShippingPrice(shippingTotal.Value, includingTax, customer, taxCategoryId, out taxRate); + PrepareAuxiliaryServicesTaxingInfos(cart); + + // commented out cause requires several plugins to be updated and migration of Order.OrderShippingTaxRate and Order.PaymentMethodAdditionalFeeTaxRate + //if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.ProRata) + //{ + // // calculate proRataShipping: get product weightings for cart and multiply them with the shipping amount + // shippingTotalTaxed = decimal.Zero; + + // var tmpTaxRate = decimal.Zero; + // var taxRates = new List(); + + // foreach (var item in cart) + // { + // var proRataShipping = shippingTotal.Value * GetTaxingInfo(item).ProRataWeighting; + // shippingTotalTaxed += _taxService.GetShippingPrice(proRataShipping, includingTax, customer, item.Item.Product.TaxCategoryId, out tmpTaxRate); + + // taxRates.Add(tmpTaxRate); + // } + + // // a tax rate is only defined if all rates are equal. return zero tax rate in all other cases. + // if (taxRates.Any() && taxRates.Distinct().Count() == 1) + // { + // taxRate = taxRates.First(); + // } + //} + //else + //{ + + if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestCartAmount) + { + var cartItem = cart.FirstOrDefault(x => GetTaxingInfo(x).HasHighestCartAmount); + taxCategoryId = (cartItem != null ? cartItem.Item.Product.TaxCategoryId : 0); + } + else if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestTaxRate) + { + var cartItem = cart.FirstOrDefault(x => GetTaxingInfo(x).HasHighestTaxRate); + taxCategoryId = (cartItem != null ? cartItem.Item.Product.TaxCategoryId : 0); + } + + // fallback to setting + if (taxCategoryId == 0) + { + taxCategoryId = _taxSettings.ShippingTaxClassId; + } + + shippingTotalTaxed = _taxService.GetShippingPrice(shippingTotal.Value, includingTax, customer, taxCategoryId, out taxRate); shippingTotalTaxed = shippingTotalTaxed.Value.RoundIfEnabledFor(currency); - return shippingTotalTaxed; + return shippingTotalTaxed; } - /// - /// Gets a shipping discount - /// - /// Customer - /// Shipping total - /// Applied discount - /// Shipping discount - public virtual decimal GetShippingDiscount(Customer customer, decimal shippingTotal, out Discount appliedDiscount) + /// + /// Gets a shipping discount + /// + /// Customer + /// Shipping total + /// Applied discount + /// Shipping discount + public virtual decimal GetShippingDiscount(Customer customer, decimal shippingTotal, out Discount appliedDiscount) { appliedDiscount = null; decimal shippingDiscountAmount = decimal.Zero; @@ -877,16 +883,16 @@ public virtual decimal GetShippingDiscount(Customer customer, decimal shippingTo var allDiscounts = _discountService.GetAllDiscounts(DiscountType.AssignedToShipping); var allowedDiscounts = new List(); - if (allDiscounts != null) - { - foreach (var discount in allDiscounts) - { - if (discount.DiscountType == DiscountType.AssignedToShipping && !allowedDiscounts.Any(x => x.Id == discount.Id) && _discountService.IsDiscountValid(discount, customer)) - { - allowedDiscounts.Add(discount); - } - } - } + if (allDiscounts != null) + { + foreach (var discount in allDiscounts) + { + if (discount.DiscountType == DiscountType.AssignedToShipping && !allowedDiscounts.Any(x => x.Id == discount.Id) && _discountService.IsDiscountValid(discount, customer)) + { + allowedDiscounts.Add(discount); + } + } + } appliedDiscount = allowedDiscounts.GetPreferredDiscount(shippingTotal); if (appliedDiscount != null) @@ -894,10 +900,10 @@ public virtual decimal GetShippingDiscount(Customer customer, decimal shippingTo shippingDiscountAmount = appliedDiscount.GetDiscountAmount(shippingTotal); } - if (shippingDiscountAmount < decimal.Zero) - { - shippingDiscountAmount = decimal.Zero; - } + if (shippingDiscountAmount < decimal.Zero) + { + shippingDiscountAmount = decimal.Zero; + } shippingDiscountAmount = shippingDiscountAmount.RoundIfEnabledFor(_workContext.WorkingCurrency); return shippingDiscountAmount; @@ -936,27 +942,27 @@ public virtual decimal GetTaxTotal(IList cart, out So taxRates = new SortedDictionary(); - var subTotalTax = decimal.Zero; - var shippingTax = decimal.Zero; - var paymentFeeTax = decimal.Zero; - var customer = cart.GetCustomer(); - var currency = _workContext.WorkingCurrency; + var subTotalTax = decimal.Zero; + var shippingTax = decimal.Zero; + var paymentFeeTax = decimal.Zero; + var customer = cart.GetCustomer(); + var currency = _workContext.WorkingCurrency; - //// (VATFIX) - if (_taxService.IsVatExempt(null, customer)) - { - taxRates.Add(decimal.Zero, decimal.Zero); - return decimal.Zero; - } - //// (VATFIX) + //// (VATFIX) + if (_taxService.IsVatExempt(null, customer)) + { + taxRates.Add(decimal.Zero, decimal.Zero); + return decimal.Zero; + } + //// (VATFIX) - #region order sub total (items + checkout attributes) + #region order sub total (items + checkout attributes) - var orderSubTotalDiscountAmount = decimal.Zero; + var orderSubTotalDiscountAmount = decimal.Zero; var subTotalWithoutDiscountBase = decimal.Zero; var subTotalWithDiscountBase = decimal.Zero; - Discount appliedDiscount = null; - SortedDictionary orderSubTotalTaxRates = null; + Discount appliedDiscount = null; + SortedDictionary orderSubTotalTaxRates = null; GetShoppingCartSubTotal(cart, false, out orderSubTotalDiscountAmount, out appliedDiscount, @@ -978,114 +984,114 @@ public virtual decimal GetTaxTotal(IList cart, out So } } - #endregion + #endregion - #region shipping tax amount + #region shipping tax amount - if (_taxSettings.ShippingIsTaxable && !IsFreeShipping(cart)) - { - var taxCategoryId = 0; - var shippingTotal = GetAdjustedShippingTotal(cart, out appliedDiscount); + if (_taxSettings.ShippingIsTaxable && !IsFreeShipping(cart)) + { + var taxCategoryId = 0; + var shippingTotal = GetAdjustedShippingTotal(cart, out appliedDiscount); - if (shippingTotal.HasValue) - { - if (shippingTotal.Value < decimal.Zero) - { - shippingTotal = decimal.Zero; - } + if (shippingTotal.HasValue) + { + if (shippingTotal.Value < decimal.Zero) + { + shippingTotal = decimal.Zero; + } shippingTotal = shippingTotal.Value.RoundIfEnabledFor(currency); - PrepareAuxiliaryServicesTaxingInfos(cart); - - // commented out cause requires several plugins to be updated and migration of Order.OrderShippingTaxRate and Order.PaymentMethodAdditionalFeeTaxRate - //if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.ProRata) - //{ - // // calculate proRataShipping: get product weightings for cart and multiply them with the shipping amount - // foreach (var item in cart) - // { - // var proRataShipping = shippingTotal.Value * GetTaxingInfo(item).ProRataWeighting; - // shippingTax += GetShippingTaxAmount(proRataShipping, customer, item.Item.Product.TaxCategoryId, taxRates); - // } - //} - //else - //{ - - if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestCartAmount) - { - var cartItem = cart.FirstOrDefault(x => GetTaxingInfo(x).HasHighestCartAmount); - taxCategoryId = (cartItem != null ? cartItem.Item.Product.TaxCategoryId : 0); - } - else if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestTaxRate) - { - var cartItem = cart.FirstOrDefault(x => GetTaxingInfo(x).HasHighestTaxRate); - taxCategoryId = (cartItem != null ? cartItem.Item.Product.TaxCategoryId : 0); - } - - // fallback to setting - if (taxCategoryId == 0) - { - taxCategoryId = _taxSettings.ShippingTaxClassId; - } - - shippingTax = GetShippingTaxAmount(shippingTotal.Value, customer, taxCategoryId, taxRates); + PrepareAuxiliaryServicesTaxingInfos(cart); + + // commented out cause requires several plugins to be updated and migration of Order.OrderShippingTaxRate and Order.PaymentMethodAdditionalFeeTaxRate + //if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.ProRata) + //{ + // // calculate proRataShipping: get product weightings for cart and multiply them with the shipping amount + // foreach (var item in cart) + // { + // var proRataShipping = shippingTotal.Value * GetTaxingInfo(item).ProRataWeighting; + // shippingTax += GetShippingTaxAmount(proRataShipping, customer, item.Item.Product.TaxCategoryId, taxRates); + // } + //} + //else + //{ + + if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestCartAmount) + { + var cartItem = cart.FirstOrDefault(x => GetTaxingInfo(x).HasHighestCartAmount); + taxCategoryId = (cartItem != null ? cartItem.Item.Product.TaxCategoryId : 0); + } + else if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestTaxRate) + { + var cartItem = cart.FirstOrDefault(x => GetTaxingInfo(x).HasHighestTaxRate); + taxCategoryId = (cartItem != null ? cartItem.Item.Product.TaxCategoryId : 0); + } + + // fallback to setting + if (taxCategoryId == 0) + { + taxCategoryId = _taxSettings.ShippingTaxClassId; + } + + shippingTax = GetShippingTaxAmount(shippingTotal.Value, customer, taxCategoryId, taxRates); shippingTax = shippingTax.RoundIfEnabledFor(currency); - } - } + } + } - #endregion + #endregion - #region payment fee tax amount + #region payment fee tax amount - if (usePaymentMethodAdditionalFee && _taxSettings.PaymentMethodAdditionalFeeIsTaxable && customer != null) - { - var paymentMethodSystemName = customer.GetAttribute(SystemCustomerAttributeNames.SelectedPaymentMethod, _genericAttributeService, _storeContext.CurrentStore.Id); - var provider = _providerManager.GetProvider(paymentMethodSystemName); + if (usePaymentMethodAdditionalFee && _taxSettings.PaymentMethodAdditionalFeeIsTaxable && customer != null) + { + var paymentMethodSystemName = customer.GetAttribute(SystemCustomerAttributeNames.SelectedPaymentMethod, _genericAttributeService, _storeContext.CurrentStore.Id); + var provider = _providerManager.GetProvider(paymentMethodSystemName); - if (provider != null) - { - var taxCategoryId = 0; - var paymentFee = provider.Value.GetAdditionalHandlingFee(cart); + if (provider != null) + { + var taxCategoryId = 0; + var paymentFee = provider.Value.GetAdditionalHandlingFee(cart); paymentFee = paymentFee.RoundIfEnabledFor(currency); - PrepareAuxiliaryServicesTaxingInfos(cart); - - // commented out cause requires several plugins to be updated and migration of Order.OrderShippingTaxRate and Order.PaymentMethodAdditionalFeeTaxRate - //if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.ProRata) - //{ - // // calculate proRataShipping: get product weightings for cart and multiply them with the shipping amount - // foreach (var item in cart) - // { - // var proRataPaymentFees = paymentFee * GetTaxingInfo(item).ProRataWeighting; - // paymentFeeTax += GetPaymentFeeTaxAmount(proRataPaymentFees, customer, item.Item.Product.TaxCategoryId, taxRates); - // } - //} - //else - //{ - - if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestCartAmount) - { - var cartItem = cart.FirstOrDefault(x => GetTaxingInfo(x).HasHighestCartAmount); - taxCategoryId = (cartItem != null ? cartItem.Item.Product.TaxCategoryId : 0); - } - else if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestTaxRate) - { - var cartItem = cart.FirstOrDefault(x => GetTaxingInfo(x).HasHighestTaxRate); - taxCategoryId = (cartItem != null ? cartItem.Item.Product.TaxCategoryId : 0); - } - - // fallback to setting - if (taxCategoryId == 0) - taxCategoryId = _taxSettings.PaymentMethodAdditionalFeeTaxClassId; - - paymentFeeTax = GetPaymentFeeTaxAmount(paymentFee, customer, taxCategoryId, taxRates); - } - } - - #endregion - - // add at least one tax rate (0%) - if (taxRates.Count == 0) + PrepareAuxiliaryServicesTaxingInfos(cart); + + // commented out cause requires several plugins to be updated and migration of Order.OrderShippingTaxRate and Order.PaymentMethodAdditionalFeeTaxRate + //if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.ProRata) + //{ + // // calculate proRataShipping: get product weightings for cart and multiply them with the shipping amount + // foreach (var item in cart) + // { + // var proRataPaymentFees = paymentFee * GetTaxingInfo(item).ProRataWeighting; + // paymentFeeTax += GetPaymentFeeTaxAmount(proRataPaymentFees, customer, item.Item.Product.TaxCategoryId, taxRates); + // } + //} + //else + //{ + + if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestCartAmount) + { + var cartItem = cart.FirstOrDefault(x => GetTaxingInfo(x).HasHighestCartAmount); + taxCategoryId = (cartItem != null ? cartItem.Item.Product.TaxCategoryId : 0); + } + else if (_taxSettings.AuxiliaryServicesTaxingType == AuxiliaryServicesTaxType.HighestTaxRate) + { + var cartItem = cart.FirstOrDefault(x => GetTaxingInfo(x).HasHighestTaxRate); + taxCategoryId = (cartItem != null ? cartItem.Item.Product.TaxCategoryId : 0); + } + + // fallback to setting + if (taxCategoryId == 0) + taxCategoryId = _taxSettings.PaymentMethodAdditionalFeeTaxClassId; + + paymentFeeTax = GetPaymentFeeTaxAmount(paymentFee, customer, taxCategoryId, taxRates); + } + } + + #endregion + + // add at least one tax rate (0%) + if (taxRates.Count == 0) taxRates.Add(decimal.Zero, decimal.Zero); // summarize taxes @@ -1108,7 +1114,7 @@ public virtual ShoppingCartTotal GetShoppingCartTotal( IList cart, bool ignoreRewardPoints = false, bool usePaymentMethodAdditionalFee = true, - bool ignoreCreditBalance = false) + bool ignoreCreditBalance = false) { var customer = cart.GetCustomer(); var store = _storeContext.CurrentStore; @@ -1241,47 +1247,47 @@ public virtual ShoppingCartTotal GetShoppingCartTotal( } } - #endregion + #endregion - if (resultTemp < decimal.Zero) + if (resultTemp < decimal.Zero) { resultTemp = decimal.Zero; } resultTemp = resultTemp.RoundIfEnabledFor(currency); - var roundingAmount = decimal.Zero; + var roundingAmount = decimal.Zero; var roundingAmountConverted = decimal.Zero; // Return null if we have errors: var orderTotal = shoppingCartShipping.HasValue ? resultTemp : (decimal?)null; var orderTotalConverted = orderTotal; - var appliedCreditBalance = decimal.Zero; + var appliedCreditBalance = decimal.Zero; - if (orderTotal.HasValue) + if (orderTotal.HasValue) { orderTotal = orderTotal.Value - redeemedRewardPointsAmount; - // Credit balance. - if (!ignoreCreditBalance && customer != null && orderTotal > decimal.Zero) - { - var creditBalance = customer.GetAttribute(SystemCustomerAttributeNames.UseCreditBalanceDuringCheckout, _genericAttributeService, store.Id); - if (creditBalance > decimal.Zero) - { - if (creditBalance > orderTotal) - { - // Normalize used amount. - appliedCreditBalance = orderTotal.Value; - _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.UseCreditBalanceDuringCheckout, orderTotal.Value, store.Id); - } - else - { - appliedCreditBalance = creditBalance; - } - } - } - - orderTotal = orderTotal.Value - appliedCreditBalance; - orderTotal = orderTotal.Value.RoundIfEnabledFor(currency); + // Credit balance. + if (!ignoreCreditBalance && customer != null && orderTotal > decimal.Zero) + { + var creditBalance = customer.GetAttribute(SystemCustomerAttributeNames.UseCreditBalanceDuringCheckout, _genericAttributeService, store.Id); + if (creditBalance > decimal.Zero) + { + if (creditBalance > orderTotal) + { + // Normalize used amount. + appliedCreditBalance = orderTotal.Value; + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.UseCreditBalanceDuringCheckout, orderTotal.Value, store.Id); + } + else + { + appliedCreditBalance = creditBalance; + } + } + } + + orderTotal = orderTotal.Value - appliedCreditBalance; + orderTotal = orderTotal.Value.RoundIfEnabledFor(currency); orderTotalConverted = _currencyService.ConvertFromPrimaryStoreCurrency(orderTotal.Value, currency, store); @@ -1304,7 +1310,7 @@ public virtual ShoppingCartTotal GetShoppingCartTotal( result.AppliedGiftCards = appliedGiftCards; result.RedeemedRewardPoints = redeemedRewardPoints; result.RedeemedRewardPointsAmount = redeemedRewardPointsAmount; - result.CreditBalance = appliedCreditBalance; + result.CreditBalance = appliedCreditBalance; result.ConvertedFromPrimaryStoreCurrency.TotalAmount = orderTotalConverted; result.ConvertedFromPrimaryStoreCurrency.RoundingAmount = roundingAmountConverted; @@ -1329,16 +1335,16 @@ public virtual decimal GetOrderTotalDiscount(Customer customer, decimal orderTot var allDiscounts = _discountService.GetAllDiscounts(DiscountType.AssignedToOrderTotal); var allowedDiscounts = new List(); - if (allDiscounts != null) - { - foreach (var discount in allDiscounts) - { - if (discount.DiscountType == DiscountType.AssignedToOrderTotal && !allowedDiscounts.Any(x => x.Id == discount.Id) &&_discountService.IsDiscountValid(discount, customer)) - { - allowedDiscounts.Add(discount); - } - } - } + if (allDiscounts != null) + { + foreach (var discount in allDiscounts) + { + if (discount.DiscountType == DiscountType.AssignedToOrderTotal && !allowedDiscounts.Any(x => x.Id == discount.Id) && _discountService.IsDiscountValid(discount, customer)) + { + allowedDiscounts.Add(discount); + } + } + } appliedDiscount = allowedDiscounts.GetPreferredDiscount(orderTotal); if (appliedDiscount != null) @@ -1383,13 +1389,13 @@ public virtual int ConvertAmountToRewardPoints(decimal amount) if (amount <= 0) return 0; - if (_rewardPointsSettings.ExchangeRate > 0) - { - if (_rewardPointsSettings.RoundDownRewardPoints) - result = (int)Math.Floor(amount / _rewardPointsSettings.ExchangeRate); - else - result = (int)Math.Ceiling(amount / _rewardPointsSettings.ExchangeRate); - } + if (_rewardPointsSettings.ExchangeRate > 0) + { + if (_rewardPointsSettings.RoundDownRewardPoints) + result = (int)Math.Floor(amount / _rewardPointsSettings.ExchangeRate); + else + result = (int)Math.Ceiling(amount / _rewardPointsSettings.ExchangeRate); + } return result; } @@ -1398,16 +1404,16 @@ public virtual int ConvertAmountToRewardPoints(decimal amount) } - internal class CartTaxingInfo - { - internal CartTaxingInfo() - { - ProRataWeighting = decimal.Zero; - } + internal class CartTaxingInfo + { + internal CartTaxingInfo() + { + ProRataWeighting = decimal.Zero; + } - public decimal SubTotalWithoutDiscount { get; internal set; } - public bool HasHighestCartAmount { get; internal set; } - public bool HasHighestTaxRate { get; internal set; } - public decimal ProRataWeighting { get; internal set; } - } + public decimal SubTotalWithoutDiscount { get; internal set; } + public bool HasHighestCartAmount { get; internal set; } + public bool HasHighestTaxRate { get; internal set; } + public decimal ProRataWeighting { get; internal set; } + } } diff --git a/src/Libraries/SmartStore.Services/Orders/PlaceOrderResult.cs b/src/Libraries/SmartStore.Services/Orders/PlaceOrderResult.cs index 2ed059da87..780352bef5 100644 --- a/src/Libraries/SmartStore.Services/Orders/PlaceOrderResult.cs +++ b/src/Libraries/SmartStore.Services/Orders/PlaceOrderResult.cs @@ -10,22 +10,19 @@ public partial class PlaceOrderResult { public IList Errors { get; set; } - public PlaceOrderResult() + public PlaceOrderResult() { this.Errors = new List(); } - public bool Success - { - get { return (this.Errors.Count == 0); } - } + public bool Success => (this.Errors.Count == 0); public void AddError(string error) { this.Errors.Add(error); } - + /// /// Gets or sets the placed order /// diff --git a/src/Libraries/SmartStore.Services/Orders/ShoppingCartExtensions.cs b/src/Libraries/SmartStore.Services/Orders/ShoppingCartExtensions.cs index 1f6c7970dd..3e9653b209 100644 --- a/src/Libraries/SmartStore.Services/Orders/ShoppingCartExtensions.cs +++ b/src/Libraries/SmartStore.Services/Orders/ShoppingCartExtensions.cs @@ -61,11 +61,11 @@ public static bool IsRecurring(this IList shoppingCar /// Cycle period /// Total cycles /// Error (if exists); otherwise, empty string - public static string GetRecurringCycleInfo(this IList shoppingCart, - ILocalizationService localizationService, - out int cycleLength, - out RecurringProductCyclePeriod cyclePeriod, - out int totalCycles) + public static string GetRecurringCycleInfo(this IList shoppingCart, + ILocalizationService localizationService, + out int cycleLength, + out RecurringProductCyclePeriod cyclePeriod, + out int totalCycles) { string error = ""; @@ -85,7 +85,7 @@ public static string GetRecurringCycleInfo(this IList throw new SmartException(string.Format("Product (Id={0}) cannot be loaded", sci.Item.ProductId)); } - string conflictError = localizationService.GetResource("ShoppingCart.ConflictingShipmentSchedules"); + string conflictError = localizationService.GetResource("ShoppingCart.ConflictingShipmentSchedules"); if (product.IsRecurring) { // cycle length @@ -123,7 +123,7 @@ public static string GetRecurringCycleInfo(this IList } } - if (_cycleLength.HasValue && _cyclePeriod.HasValue && _totalCycles.HasValue) + if (_cycleLength.HasValue && _cyclePeriod.HasValue && _totalCycles.HasValue) { cycleLength = _cycleLength.Value; cyclePeriod = _cyclePeriod.Value; @@ -146,14 +146,14 @@ public static Customer GetCustomer(this IList shoppin return shoppingCart[0].Item.Customer; } - public static IEnumerable Filter(this IEnumerable shoppingCart, ShoppingCartType type, int? storeId = null) - { - var enumerable = shoppingCart.Where(x => x.ShoppingCartType == type); + public static IEnumerable Filter(this IEnumerable shoppingCart, ShoppingCartType type, int? storeId = null) + { + var enumerable = shoppingCart.Where(x => x.ShoppingCartType == type); - if (storeId.HasValue) - enumerable = enumerable.Where(x => x.StoreId == storeId.Value); + if (storeId.HasValue) + enumerable = enumerable.Where(x => x.StoreId == storeId.Value); - return enumerable; - } + return enumerable; + } } } diff --git a/src/Libraries/SmartStore.Services/Orders/ShoppingCartService.cs b/src/Libraries/SmartStore.Services/Orders/ShoppingCartService.cs index d0880c760d..66041200a3 100644 --- a/src/Libraries/SmartStore.Services/Orders/ShoppingCartService.cs +++ b/src/Libraries/SmartStore.Services/Orders/ShoppingCartService.cs @@ -3,13 +3,16 @@ using System.Diagnostics; using System.Linq; using SmartStore.Core; +using SmartStore.Core.Caching; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Orders; using SmartStore.Core.Events; using SmartStore.Core.Localization; +using SmartStore.Core.Security; using SmartStore.Services.Catalog; +using SmartStore.Services.Catalog.Extensions; using SmartStore.Services.Common; using SmartStore.Services.Customers; using SmartStore.Services.Directory; @@ -17,26 +20,23 @@ using SmartStore.Services.Media; using SmartStore.Services.Security; using SmartStore.Services.Stores; -using SmartStore.Core.Domain.Discounts; -using SmartStore.Services.Catalog.Extensions; -using SmartStore.Core.Caching; namespace SmartStore.Services.Orders { - public partial class ShoppingCartService : IShoppingCartService + public partial class ShoppingCartService : IShoppingCartService { - // 0 = CustomerId, 1 = CartType, 2 = StoreId - const string CARTITEMS_KEY = "sm.cartitems-{0}-{1}-{2}"; - const string CARTITEMS_PATTERN_KEY = "sm.cartitems-*"; + // 0 = CustomerId, 1 = CartType, 2 = StoreId + const string CARTITEMS_KEY = "sm.cartitems-{0}-{1}-{2}"; + const string CARTITEMS_PATTERN_KEY = "sm.cartitems-*"; - private readonly IRepository _sciRepository; + private readonly IRepository _sciRepository; private readonly IWorkContext _workContext; - private readonly IStoreContext _storeContext; + private readonly IStoreContext _storeContext; private readonly ICurrencyService _currencyService; private readonly IProductService _productService; private readonly ILocalizationService _localizationService; private readonly IProductAttributeParser _productAttributeParser; - private readonly IProductAttributeService _productAttributeService; + private readonly IProductAttributeService _productAttributeService; private readonly ICheckoutAttributeService _checkoutAttributeService; private readonly ICheckoutAttributeParser _checkoutAttributeParser; private readonly IPriceFormatter _priceFormatter; @@ -45,44 +45,44 @@ public partial class ShoppingCartService : IShoppingCartService private readonly IEventPublisher _eventPublisher; private readonly IPermissionService _permissionService; private readonly IAclService _aclService; - private readonly IStoreMappingService _storeMappingService; - private readonly IGenericAttributeService _genericAttributeService; - private readonly IDownloadService _downloadService; - private readonly CatalogSettings _catalogSettings; + private readonly IStoreMappingService _storeMappingService; + private readonly IGenericAttributeService _genericAttributeService; + private readonly IDownloadService _downloadService; + private readonly CatalogSettings _catalogSettings; private readonly IOrderTotalCalculationService _orderTotalCalculationService; - private readonly IRequestCache _requestCache; + private readonly IRequestCache _requestCache; - public ShoppingCartService( - IRepository sciRepository, - IWorkContext workContext, - IStoreContext storeContext, - ICurrencyService currencyService, + public ShoppingCartService( + IRepository sciRepository, + IWorkContext workContext, + IStoreContext storeContext, + ICurrencyService currencyService, IProductService productService, ILocalizationService localizationService, IProductAttributeParser productAttributeParser, - IProductAttributeService productAttributeService, + IProductAttributeService productAttributeService, ICheckoutAttributeService checkoutAttributeService, ICheckoutAttributeParser checkoutAttributeParser, IPriceFormatter priceFormatter, ICustomerService customerService, ShoppingCartSettings shoppingCartSettings, IEventPublisher eventPublisher, - IPermissionService permissionService, + IPermissionService permissionService, IAclService aclService, - IStoreMappingService storeMappingService, - IGenericAttributeService genericAttributeService, - IDownloadService downloadService, - CatalogSettings catalogSettings, + IStoreMappingService storeMappingService, + IGenericAttributeService genericAttributeService, + IDownloadService downloadService, + CatalogSettings catalogSettings, IOrderTotalCalculationService orderTotalCalculationService, - IRequestCache requestCache) + IRequestCache requestCache) { _sciRepository = sciRepository; _workContext = workContext; - _storeContext = storeContext; + _storeContext = storeContext; _currencyService = currencyService; _productService = productService; _localizationService = localizationService; _productAttributeParser = productAttributeParser; - _productAttributeService = productAttributeService; + _productAttributeService = productAttributeService; _checkoutAttributeService = checkoutAttributeService; _checkoutAttributeParser = checkoutAttributeParser; _priceFormatter = priceFormatter; @@ -91,206 +91,206 @@ public ShoppingCartService( _eventPublisher = eventPublisher; _permissionService = permissionService; _aclService = aclService; - _storeMappingService = storeMappingService; - _genericAttributeService = genericAttributeService; - _downloadService = downloadService; - _catalogSettings = catalogSettings; + _storeMappingService = storeMappingService; + _genericAttributeService = genericAttributeService; + _downloadService = downloadService; + _catalogSettings = catalogSettings; _orderTotalCalculationService = orderTotalCalculationService; - _requestCache = requestCache; - + _requestCache = requestCache; + T = NullLocalizer.Instance; - } - - public Localizer T { get; set; } - - public virtual int CountItems(Customer customer, ShoppingCartType cartType, int? storeId = null) - { - Guard.NotNull(customer, nameof(customer)); - - var query = _sciRepository.Table.Where(x => x.ParentItemId == null && x.CustomerId == customer.Id && x.ShoppingCartTypeId == (int)cartType); - if (storeId.GetValueOrDefault() > 0) - { - query = query.Where(x => x.StoreId == storeId.Value); - } - - return query.Sum(x => (int?)x.Quantity) ?? 0; - } - - public virtual List GetCartItems(Customer customer, ShoppingCartType cartType, int? storeId = null) - { - Guard.NotNull(customer, nameof(customer)); - - var cacheKey = CARTITEMS_KEY.FormatInvariant(customer.Id, (int)cartType, storeId.GetValueOrDefault()); - var result = _requestCache.Get(cacheKey, () => - { - var query = _sciRepository.Table - .Expand(x => x.Product.ProductVariantAttributes) - .Where(x => x.CustomerId == customer.Id && x.ShoppingCartTypeId == (int)cartType); - - if (storeId.HasValue) - { - query = query.Where(x => x.StoreId == storeId.Value); - } - - var items = query.OrderByDescending(x => x.Id).ToList(); - - // Perf: Prefetch (load) all attribute values in any of the attribute definitions across all cart items (including any bundle part) - _productAttributeParser.PrefetchProductVariantAttributes(items.Select(x => x.AttributesXml)); - - return OrganizeCartItems(items); - }); - - return result; - } - - protected virtual List OrganizeCartItems(IEnumerable cart) - { - var result = new List(); - - if (cart == null || !cart.Any()) - return result; - - var parentItems = cart.Where(x => x.ParentItemId == null).ToArray(); - - foreach (var parent in parentItems) - { - var parentItem = new OrganizedShoppingCartItem(parent); - - var children = cart.Where(x => x.ParentItemId != null - && x.ParentItemId == parent.Id - && x.Id != parent.Id - && x.ShoppingCartTypeId == parent.ShoppingCartTypeId - && x.Product.CanBeBundleItem()) - .ToArray(); - - foreach (var child in children) - { - var childItem = new OrganizedShoppingCartItem(child); - - if (child.AttributesXml.HasValue() && parent.Product != null && parent.Product.BundlePerItemPricing && child.AttributesXml != null && child.BundleItem != null) - { - child.Product.MergeWithCombination(child.AttributesXml, _productAttributeParser); - - var attrValues = _productAttributeParser.ParseProductVariantAttributeValues(child.AttributesXml); - - if (attrValues != null && attrValues.Any()) - { - childItem.BundleItemData.AdditionalCharge = decimal.Zero; - foreach (var v in attrValues) - { - childItem.BundleItemData.AdditionalCharge += v.PriceAdjustment; - } - } - } - - parentItem.ChildItems.Add(childItem); - } - - result.Add(parentItem); - } - - return result; - } - - public virtual void DeleteShoppingCartItem( - ShoppingCartItem shoppingCartItem, - bool resetCheckoutData = true, - bool ensureOnlyActiveCheckoutAttributes = false, - bool deleteChildCartItems = true) + } + + public Localizer T { get; set; } + + public virtual int CountItems(Customer customer, ShoppingCartType cartType, int? storeId = null) + { + Guard.NotNull(customer, nameof(customer)); + + var query = _sciRepository.Table.Where(x => x.ParentItemId == null && x.CustomerId == customer.Id && x.ShoppingCartTypeId == (int)cartType); + if (storeId.GetValueOrDefault() > 0) + { + query = query.Where(x => x.StoreId == storeId.Value); + } + + return query.Sum(x => (int?)x.Quantity) ?? 0; + } + + public virtual List GetCartItems(Customer customer, ShoppingCartType cartType, int? storeId = null) { - Guard.NotNull(shoppingCartItem, nameof(shoppingCartItem)); + Guard.NotNull(customer, nameof(customer)); + + var cacheKey = CARTITEMS_KEY.FormatInvariant(customer.Id, (int)cartType, storeId.GetValueOrDefault()); + var result = _requestCache.Get(cacheKey, () => + { + var query = _sciRepository.Table + .Expand(x => x.Product.ProductVariantAttributes) + .Where(x => x.CustomerId == customer.Id && x.ShoppingCartTypeId == (int)cartType); + + if (storeId.HasValue) + { + query = query.Where(x => x.StoreId == storeId.Value); + } + + var items = query.OrderByDescending(x => x.Id).ToList(); + + // Perf: Prefetch (load) all attribute values in any of the attribute definitions across all cart items (including any bundle part) + _productAttributeParser.PrefetchProductVariantAttributes(items.Select(x => x.AttributesXml)); + + return OrganizeCartItems(items); + }); + + return result; + } + + protected virtual List OrganizeCartItems(IEnumerable cart) + { + var result = new List(); + + if (cart == null || !cart.Any()) + return result; + + var parentItems = cart.Where(x => x.ParentItemId == null).ToArray(); + + foreach (var parent in parentItems) + { + var parentItem = new OrganizedShoppingCartItem(parent); + + var children = cart.Where(x => x.ParentItemId != null + && x.ParentItemId == parent.Id + && x.Id != parent.Id + && x.ShoppingCartTypeId == parent.ShoppingCartTypeId + && x.Product.CanBeBundleItem()) + .ToArray(); + + foreach (var child in children) + { + var childItem = new OrganizedShoppingCartItem(child); + + if (child.AttributesXml.HasValue() && parent.Product != null && parent.Product.BundlePerItemPricing && child.AttributesXml != null && child.BundleItem != null) + { + child.Product.MergeWithCombination(child.AttributesXml, _productAttributeParser); + + var attrValues = _productAttributeParser.ParseProductVariantAttributeValues(child.AttributesXml); + + if (attrValues != null && attrValues.Any()) + { + childItem.BundleItemData.AdditionalCharge = decimal.Zero; + foreach (var v in attrValues) + { + childItem.BundleItemData.AdditionalCharge += v.PriceAdjustment; + } + } + } + + parentItem.ChildItems.Add(childItem); + } + + result.Add(parentItem); + } + + return result; + } + + public virtual void DeleteShoppingCartItem( + ShoppingCartItem shoppingCartItem, + bool resetCheckoutData = true, + bool ensureOnlyActiveCheckoutAttributes = false, + bool deleteChildCartItems = true) + { + Guard.NotNull(shoppingCartItem, nameof(shoppingCartItem)); var customer = shoppingCartItem.Customer; - var storeId = shoppingCartItem.StoreId; - int cartItemId = shoppingCartItem.Id; + var storeId = shoppingCartItem.StoreId; + int cartItemId = shoppingCartItem.Id; // reset checkout data if (resetCheckoutData && customer != null) { - _customerService.ResetCheckoutData(shoppingCartItem.Customer, shoppingCartItem.StoreId); + _customerService.ResetCheckoutData(shoppingCartItem.Customer, shoppingCartItem.StoreId); } // delete item _sciRepository.Delete(shoppingCartItem); - // cache busting - _requestCache.RemoveByPattern(CARTITEMS_PATTERN_KEY); + // cache busting + _requestCache.RemoveByPattern(CARTITEMS_PATTERN_KEY); - // validate checkout attributes - if (ensureOnlyActiveCheckoutAttributes && shoppingCartItem.ShoppingCartType == ShoppingCartType.ShoppingCart && customer != null) + // validate checkout attributes + if (ensureOnlyActiveCheckoutAttributes && shoppingCartItem.ShoppingCartType == ShoppingCartType.ShoppingCart && customer != null) { - var cart = GetCartItems(customer, ShoppingCartType.ShoppingCart, storeId); + var cart = GetCartItems(customer, ShoppingCartType.ShoppingCart, storeId); - var checkoutAttributesXml = customer.GetAttribute(SystemCustomerAttributeNames.CheckoutAttributes, _genericAttributeService); - checkoutAttributesXml = _checkoutAttributeParser.EnsureOnlyActiveAttributes(checkoutAttributesXml, cart); - _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.CheckoutAttributes, checkoutAttributesXml); + var checkoutAttributesXml = customer.GetAttribute(SystemCustomerAttributeNames.CheckoutAttributes, _genericAttributeService); + checkoutAttributesXml = _checkoutAttributeParser.EnsureOnlyActiveAttributes(checkoutAttributesXml, cart); + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.CheckoutAttributes, checkoutAttributesXml); } - // delete child items - if (deleteChildCartItems && customer != null) - { - var childCartItems = _sciRepository.Table - .Where(x => x.CustomerId == customer.Id && x.ParentItemId != null && x.ParentItemId.Value == cartItemId && x.Id != cartItemId) - .ToList(); - - foreach (var cartItem in childCartItems) - { - DeleteShoppingCartItem(cartItem, resetCheckoutData, ensureOnlyActiveCheckoutAttributes, false); - } - } + // delete child items + if (deleteChildCartItems && customer != null) + { + var childCartItems = _sciRepository.Table + .Where(x => x.CustomerId == customer.Id && x.ParentItemId != null && x.ParentItemId.Value == cartItemId && x.Id != cartItemId) + .ToList(); + + foreach (var cartItem in childCartItems) + { + DeleteShoppingCartItem(cartItem, resetCheckoutData, ensureOnlyActiveCheckoutAttributes, false); + } + } } - public virtual void DeleteShoppingCartItem( - int shoppingCartItemId, - bool resetCheckoutData = true, - bool ensureOnlyActiveCheckoutAttributes = false, - bool deleteChildCartItems = true) - { - if (shoppingCartItemId != 0) - { - var shoppingCartItem = _sciRepository.GetById(shoppingCartItemId); - DeleteShoppingCartItem(shoppingCartItem, resetCheckoutData, ensureOnlyActiveCheckoutAttributes, deleteChildCartItems); - } - } + public virtual void DeleteShoppingCartItem( + int shoppingCartItemId, + bool resetCheckoutData = true, + bool ensureOnlyActiveCheckoutAttributes = false, + bool deleteChildCartItems = true) + { + if (shoppingCartItemId != 0) + { + var shoppingCartItem = _sciRepository.GetById(shoppingCartItemId); + DeleteShoppingCartItem(shoppingCartItem, resetCheckoutData, ensureOnlyActiveCheckoutAttributes, deleteChildCartItems); + } + } public virtual int DeleteExpiredShoppingCartItems(DateTime olderThanUtc, int? customerId = null) { var query = - from sci in _sciRepository.Table - where sci.UpdatedOnUtc < olderThanUtc && sci.ParentItemId == null - select sci; + from sci in _sciRepository.Table + where sci.UpdatedOnUtc < olderThanUtc && sci.ParentItemId == null + select sci; - if (customerId.GetValueOrDefault() > 0) - { - query = query.Where(x => x.CustomerId == customerId.Value); - } + if (customerId.GetValueOrDefault() > 0) + { + query = query.Where(x => x.CustomerId == customerId.Value); + } var cartItems = query.ToList(); - foreach (var parentItem in cartItems) - { - var childItems = _sciRepository.Table.Where(x => x.ParentItemId == parentItem.Id && x.Id != parentItem.Id).ToList(); + foreach (var parentItem in cartItems) + { + var childItems = _sciRepository.Table.Where(x => x.ParentItemId == parentItem.Id && x.Id != parentItem.Id).ToList(); - foreach (var childItem in childItems) - { - _sciRepository.Delete(childItem); - } + foreach (var childItem in childItems) + { + _sciRepository.Delete(childItem); + } - _sciRepository.Delete(parentItem); - } + _sciRepository.Delete(parentItem); + } return cartItems.Count; } - + public virtual IList GetRequiredProductWarnings( - Customer customer, - ShoppingCartType shoppingCartType, - Product product, - int storeId, - bool automaticallyAddRequiredProductsIfEnabled) + Customer customer, + ShoppingCartType shoppingCartType, + Product product, + int storeId, + bool automaticallyAddRequiredProductsIfEnabled) { - Guard.NotNull(customer, nameof(customer)); - Guard.NotNull(product, nameof(product)); + Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(product, nameof(product)); var cart = GetCartItems(customer, shoppingCartType, storeId); var warnings = new List(); @@ -300,11 +300,11 @@ public virtual IList GetRequiredProductWarnings( var requiredProducts = new List(); foreach (var id in product.ParseRequiredProductIds()) { - var rp = _productService.GetProductById(id); + var rp = _productService.GetProductById(id); if (rp != null) requiredProducts.Add(rp); } - + foreach (var rp in requiredProducts) { // ensure that product is in the cart @@ -328,7 +328,7 @@ public virtual IList GetRequiredProductWarnings( if (automaticallyAddRequiredProductsIfEnabled) { // pass 'false' for 'automaticallyAddRequiredProducsIfEnabled' to prevent circular references - var addToCartWarnings = AddToCart(customer, rp, shoppingCartType, storeId, "", decimal.Zero, 1, false, null); + var addToCartWarnings = AddToCart(customer, rp, shoppingCartType, storeId, "", decimal.Zero, 1, false, null); if (addToCartWarnings.Count > 0) { @@ -336,17 +336,17 @@ public virtual IList GetRequiredProductWarnings( // don't display specific errors from 'addToCartWarnings' variable // display only generic error - warnings.Add(T("ShoppingCart.RequiredProductWarning", rp.GetLocalized(x => x.Name))); + warnings.Add(T("ShoppingCart.RequiredProductWarning", rp.GetLocalized(x => x.Name))); } } else { - warnings.Add(T("ShoppingCart.RequiredProductWarning", rp.GetLocalized(x => x.Name))); + warnings.Add(T("ShoppingCart.RequiredProductWarning", rp.GetLocalized(x => x.Name))); } } else { - warnings.Add(T("ShoppingCart.RequiredProductWarning", rp.GetLocalized(x => x.Name))); + warnings.Add(T("ShoppingCart.RequiredProductWarning", rp.GetLocalized(x => x.Name))); } } } @@ -354,19 +354,25 @@ public virtual IList GetRequiredProductWarnings( return warnings; } - + public virtual IList GetStandardWarnings( - Customer customer, - ShoppingCartType shoppingCartType, - Product product, - string selectedAttributes, - decimal customerEnteredPrice, - int quantity) + Customer customer, + ShoppingCartType shoppingCartType, + Product product, + string selectedAttributes, + decimal customerEnteredPrice, + int quantity, + int storeId = 0) { - Guard.NotNull(customer, nameof(customer)); - Guard.NotNull(product, nameof(product)); + Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(product, nameof(product)); - var warnings = new List(); + if (storeId == 0) + { + storeId = _storeContext.CurrentStore.Id; + } + + var warnings = new List(); // deleted? if (product.Deleted) @@ -375,36 +381,36 @@ public virtual IList GetStandardWarnings( return warnings; } - // check if the product type is available for order - if (product.ProductType == ProductType.GroupedProduct) - { - warnings.Add(T("ShoppingCart.ProductNotAvailableForOrder")); - } + // check if the product type is available for order + if (product.ProductType == ProductType.GroupedProduct) + { + warnings.Add(T("ShoppingCart.ProductNotAvailableForOrder")); + } - // validate bundle - if (product.ProductType == ProductType.BundledProduct) - { - if (product.BundlePerItemPricing && customerEnteredPrice != decimal.Zero) - warnings.Add(T("ShoppingCart.Bundle.NoCustomerEnteredPrice")); - } + // validate bundle + if (product.ProductType == ProductType.BundledProduct) + { + if (product.BundlePerItemPricing && customerEnteredPrice != decimal.Zero) + warnings.Add(T("ShoppingCart.Bundle.NoCustomerEnteredPrice")); + } // published? if (!product.Published) { warnings.Add(T("ShoppingCart.ProductUnpublished")); } - + // ACL if (!_aclService.Authorize(product, customer)) { warnings.Add(T("ShoppingCart.ProductUnpublished")); } - // Store mapping - if (!_storeMappingService.Authorize(product, _storeContext.CurrentStore.Id)) - { - warnings.Add(T("ShoppingCart.ProductUnpublished")); - } + // Store mapping + if (!_storeMappingService.Authorize(product, storeId)) + { + warnings.Add(T("ShoppingCart.ProductUnpublished")); + } // disabled "add to cart" button if (shoppingCartType == ShoppingCartType.ShoppingCart && product.DisableBuyButton) @@ -434,9 +440,9 @@ public virtual IList GetStandardWarnings( var maximumCustomerEnteredPrice = _currencyService.ConvertFromPrimaryStoreCurrency(product.MaximumCustomerEnteredPrice, _workContext.WorkingCurrency); warnings.Add(T("ShoppingCart.CustomerEnteredPrice.RangeError", - _priceFormatter.FormatPrice(minimumCustomerEnteredPrice, true, false), + _priceFormatter.FormatPrice(minimumCustomerEnteredPrice, true, false), _priceFormatter.FormatPrice(maximumCustomerEnteredPrice, true, false)) - ); + ); } } @@ -471,7 +477,7 @@ public virtual IList GetStandardWarnings( break; case ManageInventoryMethod.ManageStock: { - if ((BackorderMode)product.BackorderMode == BackorderMode.NoBackorders) + if (product.BackorderMode == BackorderMode.NoBackorders) { if (product.StockQuantity < quantity) { @@ -489,17 +495,17 @@ public virtual IList GetStandardWarnings( { var combination = _productAttributeParser.FindProductVariantAttributeCombination(product.Id, selectedAttributes); - if (combination != null) + if (combination != null) { - if (!combination.AllowOutOfStockOrders && combination.StockQuantity < quantity) - { - int maximumQuantityCanBeAdded = combination.StockQuantity; - - if (maximumQuantityCanBeAdded <= 0) - warnings.Add(T("ShoppingCart.OutOfStock")); - else - warnings.Add(T("ShoppingCart.QuantityExceedsStock", maximumQuantityCanBeAdded)); - } + if (!combination.AllowOutOfStockOrders && combination.StockQuantity < quantity) + { + int maximumQuantityCanBeAdded = combination.StockQuantity; + + if (maximumQuantityCanBeAdded <= 0) + warnings.Add(T("ShoppingCart.OutOfStock")); + else + warnings.Add(T("ShoppingCart.QuantityExceedsStock", maximumQuantityCanBeAdded)); + } } } break; @@ -534,24 +540,24 @@ public virtual IList GetStandardWarnings( return warnings; } - public virtual IList GetShoppingCartItemAttributeWarnings( - Customer customer, - ShoppingCartType shoppingCartType, - Product product, - string selectedAttributes, - int quantity = 1, - ProductBundleItem bundleItem = null, - ProductVariantAttributeCombination combination = null) + public virtual IList GetShoppingCartItemAttributeWarnings( + Customer customer, + ShoppingCartType shoppingCartType, + Product product, + string selectedAttributes, + int quantity = 1, + ProductBundleItem bundleItem = null, + ProductVariantAttributeCombination combination = null) { - Guard.NotNull(product, nameof(product)); + Guard.NotNull(product, nameof(product)); var warnings = new List(); - if (product.ProductType == ProductType.BundledProduct) - return warnings; // customer cannot select anything cause bundles have no attributes + if (product.ProductType == ProductType.BundledProduct) + return warnings; // customer cannot select anything cause bundles have no attributes - if (bundleItem != null && !bundleItem.BundleProduct.BundlePerItemPricing) - return warnings; // customer cannot select anything... selectedAttribute is always empty + if (bundleItem != null && !bundleItem.BundleProduct.BundlePerItemPricing) + return warnings; // customer cannot select anything... selectedAttribute is always empty // selected attributes var pva1Collection = _productAttributeParser.ParseProductVariantAttributes(selectedAttributes); @@ -559,11 +565,11 @@ public virtual IList GetShoppingCartItemAttributeWarnings( { var pv1 = pva1.Product; - if (pv1 == null || pv1.Id != product.Id) - { - warnings.Add(T("ShoppingCart.AttributeError")); - return warnings; - } + if (pv1 == null || pv1.Id != product.Id) + { + warnings.Add(T("ShoppingCart.AttributeError")); + return warnings; + } } // existing product attributes @@ -590,83 +596,83 @@ public virtual IList GetShoppingCartItemAttributeWarnings( } } - if (!found && bundleItem != null && bundleItem.FilterAttributes && !bundleItem.AttributeFilters.Any(x => x.AttributeId == pva2.ProductAttributeId)) - { - found = true; // attribute is filtered out on bundle item level... it cannot be selected by customer - } + if (!found && bundleItem != null && bundleItem.FilterAttributes && !bundleItem.AttributeFilters.Any(x => x.AttributeId == pva2.ProductAttributeId)) + { + found = true; // attribute is filtered out on bundle item level... it cannot be selected by customer + } if (!found) { - warnings.Add(T("ShoppingCart.SelectAttribute", pva2.TextPrompt.HasValue() ? pva2.TextPrompt : pva2.ProductAttribute.GetLocalized(a => a.Name))); + warnings.Add(T("ShoppingCart.SelectAttribute", pva2.TextPrompt.HasValue() ? pva2.TextPrompt : pva2.ProductAttribute.GetLocalized(a => a.Name))); } } } - // check if there is a selected attribute combination and if it is active - if (warnings.Count == 0 && selectedAttributes.HasValue()) - { - if (combination == null) - { - combination = _productAttributeParser.FindProductVariantAttributeCombination(product.Id, selectedAttributes); - } - - if (combination != null && !combination.IsActive) - { - warnings.Add(T("ShoppingCart.NotAvailable")); - } - } - - if (warnings.Count == 0) - { - var pvaValues = _productAttributeParser.ParseProductVariantAttributeValues(selectedAttributes).ToList(); - foreach (var pvaValue in pvaValues) - { - if (pvaValue.ValueType == ProductVariantAttributeValueType.ProductLinkage) - { - var linkedProduct = _productService.GetProductById(pvaValue.LinkedProductId); - if (linkedProduct != null) - { - var linkageWarnings = GetShoppingCartItemWarnings( - customer, - shoppingCartType, - linkedProduct, - _storeContext.CurrentStore.Id, - "", - decimal.Zero, - quantity * pvaValue.Quantity, - false, - true, - true, - true, - true); - - foreach (var linkageWarning in linkageWarnings) - { - warnings.Add(T("ShoppingCart.ProductLinkageAttributeWarning", - pvaValue.ProductVariantAttribute.ProductAttribute.GetLocalized(a => a.Name), - pvaValue.GetLocalized(a => a.Name), - linkageWarning) - ); - } - } - else - { - warnings.Add(T("ShoppingCart.ProductLinkageProductNotLoading", pvaValue.LinkedProductId)); - } - } - } - } + // check if there is a selected attribute combination and if it is active + if (warnings.Count == 0 && selectedAttributes.HasValue()) + { + if (combination == null) + { + combination = _productAttributeParser.FindProductVariantAttributeCombination(product.Id, selectedAttributes); + } + + if (combination != null && !combination.IsActive) + { + warnings.Add(T("ShoppingCart.NotAvailable")); + } + } + + if (warnings.Count == 0) + { + var pvaValues = _productAttributeParser.ParseProductVariantAttributeValues(selectedAttributes).ToList(); + foreach (var pvaValue in pvaValues) + { + if (pvaValue.ValueType == ProductVariantAttributeValueType.ProductLinkage) + { + var linkedProduct = _productService.GetProductById(pvaValue.LinkedProductId); + if (linkedProduct != null) + { + var linkageWarnings = GetShoppingCartItemWarnings( + customer, + shoppingCartType, + linkedProduct, + _storeContext.CurrentStore.Id, + "", + decimal.Zero, + quantity * pvaValue.Quantity, + false, + true, + true, + true, + true); + + foreach (var linkageWarning in linkageWarnings) + { + warnings.Add(T("ShoppingCart.ProductLinkageAttributeWarning", + pvaValue.ProductVariantAttribute.ProductAttribute.GetLocalized(a => a.Name), + pvaValue.GetLocalized(a => a.Name), + linkageWarning) + ); + } + } + else + { + warnings.Add(T("ShoppingCart.ProductLinkageProductNotLoading", pvaValue.LinkedProductId)); + } + } + } + } return warnings; } - public virtual bool AreAllAttributesForCombinationSelected(string selectedAttributes, Product product) + public virtual bool AreAllAttributesForCombinationSelected(string selectedAttributes, Product product) { - Guard.NotNull(product, nameof(product)); + Guard.NotNull(product, nameof(product)); - var hasAttributeCombinations = _sciRepository.Context.QueryForCollection(product, (Product p) => p.ProductVariantAttributeCombinations).Any(); - if (!hasAttributeCombinations) - return true; + var hasAttributeCombinations = _sciRepository.Context.QueryForCollection(product, (Product p) => p.ProductVariantAttributeCombinations).Any(); + if (!hasAttributeCombinations) + return true; // selected attributes var pva1Collection = _productAttributeParser.ParseProductVariantAttributes(selectedAttributes); @@ -711,9 +717,9 @@ public virtual bool AreAllAttributesForCombinationSelected(string selectedAttrib public virtual IList GetShoppingCartItemGiftCardWarnings(ShoppingCartType shoppingCartType, Product product, string selectedAttributes) { - Guard.NotNull(product, nameof(product)); + Guard.NotNull(product, nameof(product)); - var warnings = new List(); + var warnings = new List(); // gift cards if (product.IsGiftCard) @@ -725,115 +731,115 @@ public virtual IList GetShoppingCartItemGiftCardWarnings(ShoppingCartTyp string giftCardMessage = string.Empty; _productAttributeParser.GetGiftCardAttribute( - selectedAttributes, - out giftCardRecipientName, - out giftCardRecipientEmail, - out giftCardSenderName, - out giftCardSenderEmail, - out giftCardMessage); - - if (String.IsNullOrEmpty(giftCardRecipientName)) - { - warnings.Add(T("ShoppingCart.RecipientNameError")); - } + selectedAttributes, + out giftCardRecipientName, + out giftCardRecipientEmail, + out giftCardSenderName, + out giftCardSenderEmail, + out giftCardMessage); + + if (String.IsNullOrEmpty(giftCardRecipientName)) + { + warnings.Add(T("ShoppingCart.RecipientNameError")); + } if (product.GiftCardType == GiftCardType.Virtual) { - // validate for virtual gift cards only - if (String.IsNullOrEmpty(giftCardRecipientEmail) || !giftCardRecipientEmail.IsEmail()) - { - warnings.Add(T("ShoppingCart.RecipientEmailError")); - } + // validate for virtual gift cards only + if (String.IsNullOrEmpty(giftCardRecipientEmail) || !giftCardRecipientEmail.IsEmail()) + { + warnings.Add(T("ShoppingCart.RecipientEmailError")); + } } - if (String.IsNullOrEmpty(giftCardSenderName)) - { - warnings.Add(T("ShoppingCart.SenderNameError")); - } + if (String.IsNullOrEmpty(giftCardSenderName)) + { + warnings.Add(T("ShoppingCart.SenderNameError")); + } if (product.GiftCardType == GiftCardType.Virtual) { - // validate for virtual gift cards only - if (String.IsNullOrEmpty(giftCardSenderEmail) || !giftCardSenderEmail.IsEmail()) - { - warnings.Add(T("ShoppingCart.SenderEmailError")); - } + // validate for virtual gift cards only + if (String.IsNullOrEmpty(giftCardSenderEmail) || !giftCardSenderEmail.IsEmail()) + { + warnings.Add(T("ShoppingCart.SenderEmailError")); + } + } + } + + return warnings; + } + + public virtual IList GetBundleItemWarnings(ProductBundleItem bundleItem) + { + var warnings = new List(); + + if (bundleItem != null) + { + var name = bundleItem.GetLocalizedName(); + + if (!bundleItem.Published) + { + warnings.Add(T("ShoppingCart.Bundle.BundleItemUnpublished", name)); + } + + if (bundleItem.ProductId == 0 || bundleItem.BundleProductId == 0 || bundleItem.Product == null || bundleItem.BundleProduct == null) + { + warnings.Add(T("ShoppingCart.Bundle.MissingProduct", name)); + } + + if (bundleItem.Quantity <= 0) + { + warnings.Add(T("ShoppingCart.Bundle.Quantity", name)); + } + + if (bundleItem.Product.IsDownload || bundleItem.Product.IsRecurring) + { + warnings.Add(T("ShoppingCart.Bundle.ProductResrictions", name)); } } return warnings; } - public virtual IList GetBundleItemWarnings(ProductBundleItem bundleItem) - { - var warnings = new List(); - - if (bundleItem != null) - { - var name = bundleItem.GetLocalizedName(); - - if (!bundleItem.Published) - { - warnings.Add(T("ShoppingCart.Bundle.BundleItemUnpublished", name)); - } - - if (bundleItem.ProductId == 0 || bundleItem.BundleProductId == 0 || bundleItem.Product == null || bundleItem.BundleProduct == null) - { - warnings.Add(T("ShoppingCart.Bundle.MissingProduct", name)); - } - - if (bundleItem.Quantity <= 0) - { - warnings.Add(T("ShoppingCart.Bundle.Quantity", name)); - } - - if (bundleItem.Product.IsDownload || bundleItem.Product.IsRecurring) - { - warnings.Add(T("ShoppingCart.Bundle.ProductResrictions", name)); - } - } - - return warnings; - } - - public virtual IList GetBundleItemWarnings(IList cartItems) - { - var warnings = new List(); - - if (cartItems != null) - { - foreach (var sci in cartItems.Where(x => x.Item.BundleItem != null)) - { - warnings.AddRange(GetBundleItemWarnings(sci.Item.BundleItem)); - } - } - return warnings; - } - + public virtual IList GetBundleItemWarnings(IList cartItems) + { + var warnings = new List(); + + if (cartItems != null) + { + foreach (var sci in cartItems.Where(x => x.Item.BundleItem != null)) + { + warnings.AddRange(GetBundleItemWarnings(sci.Item.BundleItem)); + } + } + return warnings; + } + public virtual IList GetShoppingCartItemWarnings( - Customer customer, - ShoppingCartType shoppingCartType, - Product product, - int storeId, - string selectedAttributes, - decimal customerEnteredPrice, - int quantity, - bool automaticallyAddRequiredProductsIfEnabled, - bool getStandardWarnings = true, - bool getAttributesWarnings = true, - bool getGiftCardWarnings = true, - bool getRequiredProductWarnings = true, - bool getBundleWarnings = true, - ProductBundleItem bundleItem = null, - IList childItems = null) + Customer customer, + ShoppingCartType shoppingCartType, + Product product, + int storeId, + string selectedAttributes, + decimal customerEnteredPrice, + int quantity, + bool automaticallyAddRequiredProductsIfEnabled, + bool getStandardWarnings = true, + bool getAttributesWarnings = true, + bool getGiftCardWarnings = true, + bool getRequiredProductWarnings = true, + bool getBundleWarnings = true, + ProductBundleItem bundleItem = null, + IList childItems = null) { - Guard.NotNull(product, nameof(product)); + Guard.NotNull(product, nameof(product)); + + var warnings = new List(); - var warnings = new List(); - // standard properties if (getStandardWarnings) - warnings.AddRange(GetStandardWarnings(customer, shoppingCartType, product, selectedAttributes, customerEnteredPrice, quantity)); + warnings.AddRange(GetStandardWarnings(customer, shoppingCartType, product, selectedAttributes, customerEnteredPrice, quantity, storeId)); // selected attributes if (getAttributesWarnings) @@ -845,22 +851,22 @@ public virtual IList GetShoppingCartItemWarnings( // required products if (getRequiredProductWarnings) - warnings.AddRange(GetRequiredProductWarnings(customer, shoppingCartType, product, storeId, automaticallyAddRequiredProductsIfEnabled)); - - // bundle and bundle item warnings - if (getBundleWarnings) - { - if (bundleItem != null) - warnings.AddRange(GetBundleItemWarnings(bundleItem)); - - if (childItems != null) - warnings.AddRange(GetBundleItemWarnings(childItems)); - } - + warnings.AddRange(GetRequiredProductWarnings(customer, shoppingCartType, product, storeId, automaticallyAddRequiredProductsIfEnabled)); + + // bundle and bundle item warnings + if (getBundleWarnings) + { + if (bundleItem != null) + warnings.AddRange(GetBundleItemWarnings(bundleItem)); + + if (childItems != null) + warnings.AddRange(GetBundleItemWarnings(childItems)); + } + return warnings; } - public virtual IList GetShoppingCartWarnings(IList shoppingCart, string checkoutAttributes, bool validateCheckoutAttributes) + public virtual IList GetShoppingCartWarnings(IList shoppingCart, string checkoutAttributes, bool validateCheckoutAttributes) { var warnings = new List(); @@ -882,17 +888,17 @@ public virtual IList GetShoppingCartWarnings(IList GetShoppingCartWarnings(IList GetShoppingCartWarnings(IList shoppingCart, + public virtual OrganizedShoppingCartItem FindShoppingCartItemInTheCart( + IList shoppingCart, ShoppingCartType shoppingCartType, Product product, string selectedAttributes = "", decimal customerEnteredPrice = decimal.Zero) { - Guard.NotNull(shoppingCart, nameof(shoppingCart)); - Guard.NotNull(product, nameof(product)); + Guard.NotNull(shoppingCart, nameof(shoppingCart)); + Guard.NotNull(product, nameof(product)); - if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing) - return null; // too complex + if (product.ProductType == ProductType.BundledProduct && product.BundlePerItemPricing) + return null; // too complex foreach (var sci in shoppingCart.Where(a => a.Item.ShoppingCartType == shoppingCartType && a.Item.ParentItemId == null).ToArray()) { @@ -991,12 +997,12 @@ public virtual OrganizedShoppingCartItem FindShoppingCartItemInTheCart( string giftCardMessage1 = string.Empty; _productAttributeParser.GetGiftCardAttribute( - selectedAttributes, - out giftCardRecipientName1, - out giftCardRecipientEmail1, - out giftCardSenderName1, - out giftCardSenderEmail1, - out giftCardMessage1); + selectedAttributes, + out giftCardRecipientName1, + out giftCardRecipientEmail1, + out giftCardSenderName1, + out giftCardSenderEmail1, + out giftCardMessage1); string giftCardRecipientName2 = string.Empty; string giftCardRecipientEmail2 = string.Empty; @@ -1005,283 +1011,283 @@ public virtual OrganizedShoppingCartItem FindShoppingCartItemInTheCart( string giftCardMessage2 = string.Empty; _productAttributeParser.GetGiftCardAttribute( - sci.Item.AttributesXml, - out giftCardRecipientName2, - out giftCardRecipientEmail2, - out giftCardSenderName2, - out giftCardSenderEmail2, - out giftCardMessage2); - - - if (giftCardRecipientName1.ToLowerInvariant() != giftCardRecipientName2.ToLowerInvariant() || - giftCardSenderName1.ToLowerInvariant() != giftCardSenderName2.ToLowerInvariant()) - { - giftCardInfoSame = false; - } + sci.Item.AttributesXml, + out giftCardRecipientName2, + out giftCardRecipientEmail2, + out giftCardSenderName2, + out giftCardSenderEmail2, + out giftCardMessage2); + + + if (giftCardRecipientName1.ToLowerInvariant() != giftCardRecipientName2.ToLowerInvariant() || + giftCardSenderName1.ToLowerInvariant() != giftCardSenderName2.ToLowerInvariant()) + { + giftCardInfoSame = false; + } + } + + // Products with CustomerEntersPrice are equal if the price is the same. + // But a system product may only be placed once in the shopping cart. + var customerEnteredPricesEqual = true; + if (sci.Item.Product.CustomerEntersPrice && !sci.Item.Product.IsSystemProduct) + { + customerEnteredPricesEqual = Math.Round(sci.Item.CustomerEnteredPrice, 2) == Math.Round(customerEnteredPrice, 2); } - // Products with CustomerEntersPrice are equal if the price is the same. - // But a system product may only be placed once in the shopping cart. - var customerEnteredPricesEqual = true; - if (sci.Item.Product.CustomerEntersPrice && !sci.Item.Product.IsSystemProduct) - { - customerEnteredPricesEqual = Math.Round(sci.Item.CustomerEnteredPrice, 2) == Math.Round(customerEnteredPrice, 2); - } - - // found? - if (attributesEqual && giftCardInfoSame && customerEnteredPricesEqual) - { - return sci; - } + // found? + if (attributesEqual && giftCardInfoSame && customerEnteredPricesEqual) + { + return sci; + } } } return null; } - public virtual List AddToCart( - Customer customer, - Product product, - ShoppingCartType cartType, - int storeId, - string selectedAttributes, - decimal customerEnteredPrice, - int quantity, - bool automaticallyAddRequiredProductsIfEnabled, - AddToCartContext ctx = null) - { - Guard.NotNull(customer, nameof(customer)); - Guard.NotNull(product, nameof(product)); - - var warnings = new List(); - var bundleItem = (ctx == null ? null : ctx.BundleItem); - - if (ctx != null && bundleItem != null && ctx.Warnings.Count > 0) - return ctx.Warnings; // warnings while adding bundle items to cart -> no need for further processing - - if (cartType == ShoppingCartType.ShoppingCart && !_permissionService.Authorize(StandardPermissionProvider.EnableShoppingCart, customer)) - { - warnings.Add(T("ShoppingCart.IsDisabled")); - return warnings; - } - if (cartType == ShoppingCartType.Wishlist && !_permissionService.Authorize(StandardPermissionProvider.EnableWishlist, customer)) - { - warnings.Add(T("Wishlist.IsDisabled")); - return warnings; - } - - if (quantity <= 0) - { - warnings.Add(T("ShoppingCart.QuantityShouldPositive")); - return warnings; - } - - //if (parentItemId.HasValue && (parentItemId.Value == 0 || bundleItem == null || bundleItem.Id == 0)) - //{ - // warnings.Add(T("ShoppingCart.Bundle.BundleItemNotFound", bundleItem.GetLocalizedName())); - // return warnings; - //} - - // reset checkout info - _customerService.ResetCheckoutData(customer, storeId); - - var cart = GetCartItems(customer, cartType, storeId); - OrganizedShoppingCartItem existingCartItem = null; - - if (bundleItem == null) - { - existingCartItem = FindShoppingCartItemInTheCart(cart, cartType, product, selectedAttributes, customerEnteredPrice); - } - - if (existingCartItem != null) - { - // update existing shopping cart item - int newQuantity = existingCartItem.Item.Quantity + quantity; - - warnings.AddRange( - GetShoppingCartItemWarnings(customer, cartType, product, storeId, selectedAttributes, customerEnteredPrice, newQuantity, - automaticallyAddRequiredProductsIfEnabled, bundleItem: bundleItem) - ); - - if (warnings.Count == 0) - { - existingCartItem.Item.AttributesXml = selectedAttributes; - existingCartItem.Item.Quantity = newQuantity; - existingCartItem.Item.UpdatedOnUtc = DateTime.UtcNow; - _customerService.UpdateCustomer(customer); - } - } - else - { - // new shopping cart item - warnings.AddRange( - GetShoppingCartItemWarnings(customer, cartType, product, storeId, selectedAttributes, customerEnteredPrice, quantity, - automaticallyAddRequiredProductsIfEnabled, bundleItem: bundleItem) - ); - - if (warnings.Count == 0) - { - // maximum items validation - if (cartType == ShoppingCartType.ShoppingCart && cart.Count >= _shoppingCartSettings.MaximumShoppingCartItems) - { - warnings.Add(T("ShoppingCart.MaximumShoppingCartItems")); - return warnings; - } - else if (cartType == ShoppingCartType.Wishlist && cart.Count >= _shoppingCartSettings.MaximumWishlistItems) - { - warnings.Add(T("ShoppingCart.MaximumWishlistItems")); - return warnings; - } - - var now = DateTime.UtcNow; - var cartItem = new ShoppingCartItem - { - ShoppingCartType = cartType, - StoreId = storeId, - Product = product, - AttributesXml = selectedAttributes, - CustomerEnteredPrice = customerEnteredPrice, - Quantity = quantity, - ParentItemId = null - }; - - if (bundleItem != null) - { - cartItem.BundleItemId = bundleItem.Id; - } - - if (ctx == null) - { - customer.ShoppingCartItems.Add(cartItem); - _customerService.UpdateCustomer(customer); - } - else - { - if (bundleItem == null) - { - Debug.Assert(ctx.Item == null, "Add to cart item already specified"); - ctx.Item = cartItem; - } - else - { - ctx.ChildItems.Add(cartItem); - } - } - } - } - - _requestCache.RemoveByPattern(CARTITEMS_PATTERN_KEY); - - return warnings; - } - - public virtual void AddToCart(AddToCartContext ctx) - { - var customer = ctx.Customer ?? _workContext.CurrentCustomer; - var storeId = ctx.StoreId ?? _storeContext.CurrentStore.Id; - - _customerService.ResetCheckoutData(customer, storeId); - - if (ctx.VariantQuery != null) - { - var attributes = _productAttributeService.GetProductVariantAttributesByProductId(ctx.Product.Id); - - ctx.AttributesXml = ctx.VariantQuery.CreateSelectedAttributesXml(ctx.Product.Id, ctx.BundleItemId, attributes, _productAttributeParser, - _localizationService, _downloadService, _catalogSettings, null, ctx.Warnings); - - if (ctx.Product.ProductType == ProductType.BundledProduct && ctx.AttributesXml.HasValue()) - { - ctx.Warnings.Add(T("ShoppingCart.Bundle.NoAttributes")); - } - - if (ctx.Product.IsGiftCard) - { - //ctx.Attributes = ctx.AttributeForm.AddGiftCardAttribute(ctx.Attributes, ctx.Product.Id, _productAttributeParser, ctx.BundleItemId); - ctx.AttributesXml = _productAttributeParser.AddGiftCardAttribute( - ctx.AttributesXml, - ctx.VariantQuery.GetGiftCardValue(ctx.Product.Id, ctx.BundleItemId, "RecipientName"), - ctx.VariantQuery.GetGiftCardValue(ctx.Product.Id, ctx.BundleItemId, "RecipientEmail"), - ctx.VariantQuery.GetGiftCardValue(ctx.Product.Id, ctx.BundleItemId, "SenderName"), - ctx.VariantQuery.GetGiftCardValue(ctx.Product.Id, ctx.BundleItemId, "SenderEmail"), - ctx.VariantQuery.GetGiftCardValue(ctx.Product.Id, ctx.BundleItemId, "Message")); - } - } - - ctx.Warnings.AddRange( - AddToCart(customer, ctx.Product, ctx.CartType, storeId, ctx.AttributesXml, ctx.CustomerEnteredPrice, ctx.Quantity, ctx.AddRequiredProducts, ctx) - ); - - if (ctx.Product.ProductType == ProductType.BundledProduct && ctx.Warnings.Count <= 0 && ctx.BundleItem == null) - { - foreach (var bundleItem in _productService.GetBundleItems(ctx.Product.Id).Select(x => x.Item)) - { - AddToCart(new AddToCartContext - { - BundleItem = bundleItem, - Warnings = ctx.Warnings, - Item = ctx.Item, - ChildItems = ctx.ChildItems, - Product = bundleItem.Product, - Customer = customer, - VariantQuery = ctx.VariantQuery, - CartType = ctx.CartType, - Quantity = bundleItem.Quantity, - AddRequiredProducts = ctx.AddRequiredProducts, - StoreId = storeId - }); - - if (ctx.Warnings.Count > 0) - { - ctx.ChildItems.Clear(); - break; - } - } - } - - if (ctx.BundleItem == null) - { - AddToCartStoring(ctx); - } - } - - public virtual void AddToCartStoring(AddToCartContext ctx) - { - if (ctx.Warnings.Count == 0 && ctx.Item != null) - { - var customer = ctx.Customer ?? _workContext.CurrentCustomer; - - customer.ShoppingCartItems.Add(ctx.Item); - _customerService.UpdateCustomer(customer); - - foreach (var childItem in ctx.ChildItems) - { - childItem.ParentItemId = ctx.Item.Id; - - customer.ShoppingCartItems.Add(childItem); - _customerService.UpdateCustomer(customer); - } - } - } + public virtual List AddToCart( + Customer customer, + Product product, + ShoppingCartType cartType, + int storeId, + string selectedAttributes, + decimal customerEnteredPrice, + int quantity, + bool automaticallyAddRequiredProductsIfEnabled, + AddToCartContext ctx = null) + { + Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(product, nameof(product)); + + var warnings = new List(); + var bundleItem = (ctx == null ? null : ctx.BundleItem); + + if (ctx != null && bundleItem != null && ctx.Warnings.Count > 0) + return ctx.Warnings; // warnings while adding bundle items to cart -> no need for further processing + + if (cartType == ShoppingCartType.ShoppingCart && !_permissionService.Authorize(Permissions.Cart.AccessShoppingCart, customer)) + { + warnings.Add(T("ShoppingCart.IsDisabled")); + return warnings; + } + if (cartType == ShoppingCartType.Wishlist && !_permissionService.Authorize(Permissions.Cart.AccessWishlist, customer)) + { + warnings.Add(T("Wishlist.IsDisabled")); + return warnings; + } + + if (quantity <= 0) + { + warnings.Add(T("ShoppingCart.QuantityShouldPositive")); + return warnings; + } + + //if (parentItemId.HasValue && (parentItemId.Value == 0 || bundleItem == null || bundleItem.Id == 0)) + //{ + // warnings.Add(T("ShoppingCart.Bundle.BundleItemNotFound", bundleItem.GetLocalizedName())); + // return warnings; + //} + + // reset checkout info + _customerService.ResetCheckoutData(customer, storeId); + + var cart = GetCartItems(customer, cartType, storeId); + OrganizedShoppingCartItem existingCartItem = null; + + if (bundleItem == null) + { + existingCartItem = FindShoppingCartItemInTheCart(cart, cartType, product, selectedAttributes, customerEnteredPrice); + } + + if (existingCartItem != null && !_shoppingCartSettings.AddProductsToBasketInSinglePositions) + { + // update existing shopping cart item + int newQuantity = existingCartItem.Item.Quantity + quantity; + + warnings.AddRange( + GetShoppingCartItemWarnings(customer, cartType, product, storeId, selectedAttributes, customerEnteredPrice, newQuantity, + automaticallyAddRequiredProductsIfEnabled, bundleItem: bundleItem) + ); + + if (warnings.Count == 0) + { + existingCartItem.Item.AttributesXml = selectedAttributes; + existingCartItem.Item.Quantity = newQuantity; + existingCartItem.Item.UpdatedOnUtc = DateTime.UtcNow; + _customerService.UpdateCustomer(customer); + } + } + else + { + // new shopping cart item + warnings.AddRange( + GetShoppingCartItemWarnings(customer, cartType, product, storeId, selectedAttributes, customerEnteredPrice, quantity, + automaticallyAddRequiredProductsIfEnabled, bundleItem: bundleItem) + ); + + if (warnings.Count == 0) + { + // maximum items validation + if (cartType == ShoppingCartType.ShoppingCart && cart.Count >= _shoppingCartSettings.MaximumShoppingCartItems) + { + warnings.Add(T("ShoppingCart.MaximumShoppingCartItems")); + return warnings; + } + else if (cartType == ShoppingCartType.Wishlist && cart.Count >= _shoppingCartSettings.MaximumWishlistItems) + { + warnings.Add(T("ShoppingCart.MaximumWishlistItems")); + return warnings; + } + + var now = DateTime.UtcNow; + var cartItem = new ShoppingCartItem + { + ShoppingCartType = cartType, + StoreId = storeId, + Product = product, + AttributesXml = selectedAttributes, + CustomerEnteredPrice = customerEnteredPrice, + Quantity = quantity, + ParentItemId = null + }; + + if (bundleItem != null) + { + cartItem.BundleItemId = bundleItem.Id; + } + + if (ctx == null) + { + customer.ShoppingCartItems.Add(cartItem); + _customerService.UpdateCustomer(customer); + } + else + { + if (bundleItem == null) + { + Debug.Assert(ctx.Item == null, "Add to cart item already specified"); + ctx.Item = cartItem; + } + else + { + ctx.ChildItems.Add(cartItem); + } + } + } + } + + _requestCache.RemoveByPattern(CARTITEMS_PATTERN_KEY); + + return warnings; + } + + public virtual void AddToCart(AddToCartContext ctx) + { + var customer = ctx.Customer ?? _workContext.CurrentCustomer; + var storeId = ctx.StoreId ?? _storeContext.CurrentStore.Id; + + _customerService.ResetCheckoutData(customer, storeId); + + if (ctx.VariantQuery != null) + { + var attributes = _productAttributeService.GetProductVariantAttributesByProductId(ctx.Product.Id); + + ctx.AttributesXml = ctx.VariantQuery.CreateSelectedAttributesXml(ctx.Product.Id, ctx.BundleItemId, attributes, _productAttributeParser, + _localizationService, _downloadService, _catalogSettings, null, ctx.Warnings); + + if (ctx.Product.ProductType == ProductType.BundledProduct && ctx.AttributesXml.HasValue()) + { + ctx.Warnings.Add(T("ShoppingCart.Bundle.NoAttributes")); + } + + if (ctx.Product.IsGiftCard) + { + //ctx.Attributes = ctx.AttributeForm.AddGiftCardAttribute(ctx.Attributes, ctx.Product.Id, _productAttributeParser, ctx.BundleItemId); + ctx.AttributesXml = _productAttributeParser.AddGiftCardAttribute( + ctx.AttributesXml, + ctx.VariantQuery.GetGiftCardValue(ctx.Product.Id, ctx.BundleItemId, "RecipientName"), + ctx.VariantQuery.GetGiftCardValue(ctx.Product.Id, ctx.BundleItemId, "RecipientEmail"), + ctx.VariantQuery.GetGiftCardValue(ctx.Product.Id, ctx.BundleItemId, "SenderName"), + ctx.VariantQuery.GetGiftCardValue(ctx.Product.Id, ctx.BundleItemId, "SenderEmail"), + ctx.VariantQuery.GetGiftCardValue(ctx.Product.Id, ctx.BundleItemId, "Message")); + } + } + + ctx.Warnings.AddRange( + AddToCart(customer, ctx.Product, ctx.CartType, storeId, ctx.AttributesXml, ctx.CustomerEnteredPrice, ctx.Quantity, ctx.AddRequiredProducts, ctx) + ); + + if (ctx.Product.ProductType == ProductType.BundledProduct && ctx.Warnings.Count <= 0 && ctx.BundleItem == null) + { + foreach (var bundleItem in _productService.GetBundleItems(ctx.Product.Id).Select(x => x.Item)) + { + AddToCart(new AddToCartContext + { + BundleItem = bundleItem, + Warnings = ctx.Warnings, + Item = ctx.Item, + ChildItems = ctx.ChildItems, + Product = bundleItem.Product, + Customer = customer, + VariantQuery = ctx.VariantQuery, + CartType = ctx.CartType, + Quantity = bundleItem.Quantity, + AddRequiredProducts = ctx.AddRequiredProducts, + StoreId = storeId + }); + + if (ctx.Warnings.Count > 0) + { + ctx.ChildItems.Clear(); + break; + } + } + } + + if (ctx.BundleItem == null) + { + AddToCartStoring(ctx); + } + } + + public virtual void AddToCartStoring(AddToCartContext ctx) + { + if (ctx.Warnings.Count == 0 && ctx.Item != null) + { + var customer = ctx.Customer ?? _workContext.CurrentCustomer; + + customer.ShoppingCartItems.Add(ctx.Item); + _customerService.UpdateCustomer(customer); + + foreach (var childItem in ctx.ChildItems) + { + childItem.ParentItemId = ctx.Item.Id; + + customer.ShoppingCartItems.Add(childItem); + _customerService.UpdateCustomer(customer); + } + } + } public virtual IList UpdateShoppingCartItem(Customer customer, int shoppingCartItemId, int newQuantity, bool resetCheckoutData) { - Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(customer, nameof(customer)); - var warnings = new List(); + var warnings = new List(); - var shoppingCartItem = customer.ShoppingCartItems.FirstOrDefault(sci => sci.Id == shoppingCartItemId && sci.ParentItemId == null); + var shoppingCartItem = customer.ShoppingCartItems.FirstOrDefault(sci => sci.Id == shoppingCartItemId && sci.ParentItemId == null); if (shoppingCartItem != null) { if (resetCheckoutData) { // reset checkout data - _customerService.ResetCheckoutData(customer, shoppingCartItem.StoreId); + _customerService.ResetCheckoutData(customer, shoppingCartItem.StoreId); } if (newQuantity > 0) { // check warnings warnings.AddRange(GetShoppingCartItemWarnings(customer, shoppingCartItem.ShoppingCartType, shoppingCartItem.Product, shoppingCartItem.StoreId, - shoppingCartItem.AttributesXml, shoppingCartItem.CustomerEnteredPrice, newQuantity, false)); + shoppingCartItem.AttributesXml, shoppingCartItem.CustomerEnteredPrice, newQuantity, false)); if (warnings.Count == 0) { @@ -1298,101 +1304,88 @@ public virtual IList UpdateShoppingCartItem(Customer customer, int shopp } } - _requestCache.RemoveByPattern(CARTITEMS_PATTERN_KEY); + _requestCache.RemoveByPattern(CARTITEMS_PATTERN_KEY); - return warnings; + return warnings; } - + public virtual void MigrateShoppingCart(Customer fromCustomer, Customer toCustomer) { - Guard.NotNull(fromCustomer, nameof(fromCustomer)); - Guard.NotNull(toCustomer, nameof(toCustomer)); + Guard.NotNull(fromCustomer, nameof(fromCustomer)); + Guard.NotNull(toCustomer, nameof(toCustomer)); - if (fromCustomer.Id == toCustomer.Id) + if (fromCustomer.Id == toCustomer.Id) return; - int storeId = 0; - var cartItems = OrganizeCartItems(fromCustomer.ShoppingCartItems); + int storeId = 0; + var cartItems = OrganizeCartItems(fromCustomer.ShoppingCartItems); - if (cartItems.Count <= 0) - return; + if (cartItems.Count <= 0) + return; - foreach (var cartItem in cartItems) - { - if (storeId == 0) - storeId = cartItem.Item.StoreId; + foreach (var cartItem in cartItems) + { + if (storeId == 0) + storeId = cartItem.Item.StoreId; - Copy(cartItem, toCustomer, cartItem.Item.ShoppingCartType, cartItem.Item.StoreId, false); - } + Copy(cartItem, toCustomer, cartItem.Item.ShoppingCartType, cartItem.Item.StoreId, false); + } - _eventPublisher.PublishMigrateShoppingCart(fromCustomer, toCustomer, storeId); + _eventPublisher.PublishMigrateShoppingCart(fromCustomer, toCustomer, storeId); - foreach (var cartItem in cartItems) - { - DeleteShoppingCartItem(cartItem.Item); - } + foreach (var cartItem in cartItems) + { + DeleteShoppingCartItem(cartItem.Item); + } } - - public virtual IList Copy(OrganizedShoppingCartItem sci, Customer customer, ShoppingCartType cartType, int storeId, bool addRequiredProductsIfEnabled) - { - Guard.NotNull(customer, nameof(customer)); - Guard.NotNull(sci, nameof(sci)); - var addToCartContext = new AddToCartContext - { - Customer = customer - }; + public virtual IList Copy(OrganizedShoppingCartItem sci, Customer customer, ShoppingCartType cartType, int storeId, bool addRequiredProductsIfEnabled) + { + Guard.NotNull(customer, nameof(customer)); + Guard.NotNull(sci, nameof(sci)); + + var addToCartContext = new AddToCartContext + { + Customer = customer + }; - addToCartContext.Warnings = AddToCart(customer, sci.Item.Product, cartType, storeId, sci.Item.AttributesXml, sci.Item.CustomerEnteredPrice, - sci.Item.Quantity, addRequiredProductsIfEnabled, addToCartContext); + addToCartContext.Warnings = AddToCart(customer, sci.Item.Product, cartType, storeId, sci.Item.AttributesXml, sci.Item.CustomerEnteredPrice, + sci.Item.Quantity, addRequiredProductsIfEnabled, addToCartContext); - if (addToCartContext.Warnings.Count == 0 && sci.ChildItems != null) - { - foreach (var childItem in sci.ChildItems) - { - addToCartContext.BundleItem = childItem.Item.BundleItem; + if (addToCartContext.Warnings.Count == 0 && sci.ChildItems != null) + { + foreach (var childItem in sci.ChildItems) + { + addToCartContext.BundleItem = childItem.Item.BundleItem; - addToCartContext.Warnings = AddToCart(customer, childItem.Item.Product, cartType, storeId, childItem.Item.AttributesXml, childItem.Item.CustomerEnteredPrice, - childItem.Item.Quantity, false, addToCartContext); - } - } + addToCartContext.Warnings = AddToCart(customer, childItem.Item.Product, cartType, storeId, childItem.Item.AttributesXml, childItem.Item.CustomerEnteredPrice, + childItem.Item.Quantity, false, addToCartContext); + } + } - AddToCartStoring(addToCartContext); + AddToCartStoring(addToCartContext); - _requestCache.RemoveByPattern(CARTITEMS_PATTERN_KEY); + _requestCache.RemoveByPattern(CARTITEMS_PATTERN_KEY); - return addToCartContext.Warnings; - } + return addToCartContext.Warnings; + } public virtual decimal GetCurrentCartSubTotal() { var cart = GetCartItems(_workContext.CurrentCustomer, ShoppingCartType.ShoppingCart, _storeContext.CurrentStore.Id); return GetCurrentCartSubTotal(cart); } - + public virtual decimal GetCurrentCartSubTotal(IList cart) { - decimal subtotal = 0; - - if (cart.Count > 0) + if (cart.Any()) { - decimal subtotalBase = decimal.Zero; - decimal orderSubTotalDiscountAmountBase = decimal.Zero; - Discount orderSubTotalAppliedDiscount = null; - decimal subTotalWithoutDiscountBase = decimal.Zero; - decimal subTotalWithDiscountBase = decimal.Zero; - - _orderTotalCalculationService.GetShoppingCartSubTotal(cart, - out orderSubTotalDiscountAmountBase, - out orderSubTotalAppliedDiscount, - out subTotalWithoutDiscountBase, - out subTotalWithDiscountBase); - - subtotalBase = subTotalWithoutDiscountBase; - subtotal = _currencyService.ConvertFromPrimaryStoreCurrency(subtotalBase, _workContext.WorkingCurrency); + _orderTotalCalculationService.GetShoppingCartSubTotal(cart, out _, out _, out var subTotalWithoutDiscountBase, out _); + + return _currencyService.ConvertFromPrimaryStoreCurrency(subTotalWithoutDiscountBase, _workContext.WorkingCurrency); } - return subtotal; + return decimal.Zero; } public virtual string GetFormattedCurrentCartSubTotal() @@ -1404,5 +1397,23 @@ public virtual string GetFormattedCurrentCartSubTotal(IList x.ShoppingCartTypeId == (int)ShoppingCartType.ShoppingCart && x.Product != null) + .Sum(x => (decimal?)(x.Product.Price * x.Quantity)) ?? decimal.Zero; + + return subTotal; + } + + public decimal GetAllOpenWishlistSubTotal() + { + var subTotal = _sciRepository.Table + .Where(x => x.ShoppingCartTypeId == (int)ShoppingCartType.Wishlist && x.Product != null) + .Sum(x => (decimal?)(x.Product.Price * x.Quantity)) ?? decimal.Zero; + + return subTotal; + } } } diff --git a/src/Libraries/SmartStore.Services/Orders/ShoppingCartTotal.cs b/src/Libraries/SmartStore.Services/Orders/ShoppingCartTotal.cs index 65155d6db7..65e170e814 100644 --- a/src/Libraries/SmartStore.Services/Orders/ShoppingCartTotal.cs +++ b/src/Libraries/SmartStore.Services/Orders/ShoppingCartTotal.cs @@ -56,12 +56,12 @@ public static implicit operator ShoppingCartTotal(decimal? obj) /// public decimal RedeemedRewardPointsAmount { get; set; } - /// - /// Credit balance. - /// - public decimal CreditBalance { get; set; } + /// + /// Credit balance. + /// + public decimal CreditBalance { get; set; } - public ConvertedAmounts ConvertedFromPrimaryStoreCurrency { get; set; } + public ConvertedAmounts ConvertedFromPrimaryStoreCurrency { get; set; } public override string ToString() { diff --git a/src/Libraries/SmartStore.Services/Orders/ValidatingCartEventConsumer.cs b/src/Libraries/SmartStore.Services/Orders/ValidatingCartEventConsumer.cs new file mode 100644 index 0000000000..9dfcbc0f81 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Orders/ValidatingCartEventConsumer.cs @@ -0,0 +1,62 @@ +using SmartStore.Core; +using SmartStore.Core.Events; +using SmartStore.Services.Cart; +using SmartStore.Services.Catalog; +using SmartStore.Services.Directory; +using SmartStore.Services.Localization; + +namespace SmartStore.Services.Orders +{ + public class ValidatingCartEventConsumer : IConsumer + { + private readonly IOrderProcessingService _orderProcessingService; + private readonly ILocalizationService _localizationService; + private readonly ICurrencyService _currencyService; + private readonly IPriceFormatter _priceFormatter; + private readonly IWorkContext _workContext; + + public ValidatingCartEventConsumer( + IOrderProcessingService orderProcessingService, + ILocalizationService localizationService, + ICurrencyService currencyService, + IPriceFormatter priceFormatter, + IWorkContext workContext) + { + _orderProcessingService = orderProcessingService; + _localizationService = localizationService; + _currencyService = currencyService; + _priceFormatter = priceFormatter; + _workContext = workContext; + } + + public void HandleEvent(ValidatingCartEvent message) + { + // Default Order Totals restriction + var roleIds = _workContext.OriginalCustomerIfImpersonated?.GetRoleIds() ?? message.Customer.GetRoleIds(); + + // Minimum order totals validation + var (isAboveMin, min) = _orderProcessingService.IsAboveOrderTotalMinimum(message.Cart, roleIds); + if (!isAboveMin) + { + min = _currencyService.ConvertFromPrimaryStoreCurrency(min, _workContext.WorkingCurrency); + message.Warnings.Add(string.Format( + _localizationService.GetResource("Checkout.MinOrderSubtotalAmount"), + _priceFormatter.FormatPrice(min, true, false)) + ); + + return; + } + + // Maximum order totals validation + var (isBelowMax, max) = _orderProcessingService.IsBelowOrderTotalMaximum(message.Cart, roleIds); + if (!isBelowMax) + { + max = _currencyService.ConvertFromPrimaryStoreCurrency(max, _workContext.WorkingCurrency); + message.Warnings.Add(string.Format( + _localizationService.GetResource("Checkout.MaxOrderSubtotalAmount"), + _priceFormatter.FormatPrice(max, true, false)) + ); + } + } + } +} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Payments/CancelRecurringPaymentResult.cs b/src/Libraries/SmartStore.Services/Payments/CancelRecurringPaymentResult.cs index 02478591cc..b98e608271 100644 --- a/src/Libraries/SmartStore.Services/Payments/CancelRecurringPaymentResult.cs +++ b/src/Libraries/SmartStore.Services/Payments/CancelRecurringPaymentResult.cs @@ -9,17 +9,14 @@ public partial class CancelRecurringPaymentResult { public IList Errors { get; set; } - public CancelRecurringPaymentResult() + public CancelRecurringPaymentResult() { this.Errors = new List(); } - public bool Success - { - get { return (this.Errors.Count == 0); } - } + public bool Success => (this.Errors.Count == 0); - public void AddError(string error) + public void AddError(string error) { this.Errors.Add(error); } diff --git a/src/Libraries/SmartStore.Services/Payments/CapturePaymentHook.cs b/src/Libraries/SmartStore.Services/Payments/CapturePaymentHook.cs index 1b30b02387..7cb73dbdd4 100644 --- a/src/Libraries/SmartStore.Services/Payments/CapturePaymentHook.cs +++ b/src/Libraries/SmartStore.Services/Payments/CapturePaymentHook.cs @@ -9,84 +9,81 @@ namespace SmartStore.Services.Payments { - public class CapturePaymentHook : DbSaveHook - { - private readonly Lazy _services; - private readonly Lazy _orderProcessingService; - private readonly HashSet _toCapture = new HashSet(); + public class CapturePaymentHook : DbSaveHook + { + private readonly Lazy _services; + private readonly Lazy _orderProcessingService; + private readonly HashSet _toCapture = new HashSet(); - public CapturePaymentHook( - Lazy services, - Lazy orderProcessingService) - { - _services = services; - _orderProcessingService = orderProcessingService; - } + public CapturePaymentHook( + Lazy services, + Lazy orderProcessingService) + { + _services = services; + _orderProcessingService = orderProcessingService; + } - private bool IsStatusPropertyModifiedTo(IHookedEntity entry, string propertyName, int statusId) - { - var prop = entry.Entry.Property(propertyName); + private bool IsStatusPropertyModifiedTo(IHookedEntity entry, string propertyName, int statusId) + { + var prop = entry.Entry.Property(propertyName); - if (prop != null && prop.CurrentValue != null) - { - if (!prop.CurrentValue.Equals(prop.OriginalValue)) - { - return (int)prop.CurrentValue == statusId; - } - } + if (prop != null && prop.CurrentValue != null) + { + if (!prop.CurrentValue.Equals(prop.OriginalValue)) + { + return (int)prop.CurrentValue == statusId; + } + } - return false; - } + return false; + } - protected override void OnUpdating(Order entity, IHookedEntity entry) - { - if (entry.State == Core.Data.EntityState.Modified) - { - var isShipped = IsStatusPropertyModifiedTo(entry, nameof(entity.ShippingStatusId), (int)ShippingStatus.Shipped); - var isDelivered = IsStatusPropertyModifiedTo(entry, nameof(entity.ShippingStatusId), (int)ShippingStatus.Delivered); + protected override void OnUpdating(Order entity, IHookedEntity entry) + { + var isShipped = IsStatusPropertyModifiedTo(entry, nameof(entity.ShippingStatusId), (int)ShippingStatus.Shipped); + var isDelivered = IsStatusPropertyModifiedTo(entry, nameof(entity.ShippingStatusId), (int)ShippingStatus.Delivered); - if (isShipped || isDelivered) - { - var settings = _services.Value.Settings.LoadSetting(entity.StoreId); - if (settings.CapturePaymentReason.HasValue) - { - if (isShipped && settings.CapturePaymentReason.Value == CapturePaymentReason.OrderShipped) - { - _toCapture.Add(entity); - } - else if (isDelivered && settings.CapturePaymentReason.Value == CapturePaymentReason.OrderDelivered) - { - _toCapture.Add(entity); - } - } - } + if (isShipped || isDelivered) + { + var settings = _services.Value.Settings.LoadSetting(entity.StoreId); + if (settings.CapturePaymentReason.HasValue) + { + if (isShipped && settings.CapturePaymentReason.Value == CapturePaymentReason.OrderShipped) + { + _toCapture.Add(entity); + } + else if (isDelivered && settings.CapturePaymentReason.Value == CapturePaymentReason.OrderDelivered) + { + _toCapture.Add(entity); + } + } + } - //if (IsStatusPropertyModifiedTo(entry, nameof(entity.OrderStatusId), (int)OrderStatus.Complete)) - //{ - // That's too late. The payment is already marked as paid and the capture process would never be executed. - //} - } - } + //if (IsStatusPropertyModifiedTo(entry, nameof(entity.OrderStatusId), (int)OrderStatus.Complete)) + //{ + // That's too late. The payment is already marked as paid and the capture process would never be executed. + //} + } - public override void OnAfterSave(IHookedEntity entry) - { - // Do not remove. - } + public override void OnAfterSave(IHookedEntity entry) + { + // Do not remove. + } - public override void OnAfterSaveCompleted() - { - if (_toCapture.Any()) - { - foreach (var order in _toCapture) - { - if (_orderProcessingService.Value.CanCapture(order)) - { - _orderProcessingService.Value.Capture(order); - } - } + public override void OnAfterSaveCompleted() + { + if (_toCapture.Any()) + { + foreach (var order in _toCapture) + { + if (_orderProcessingService.Value.CanCapture(order)) + { + _orderProcessingService.Value.Capture(order); + } + } - _toCapture.Clear(); - } - } - } + _toCapture.Clear(); + } + } + } } diff --git a/src/Libraries/SmartStore.Services/Payments/CapturePaymentResult.cs b/src/Libraries/SmartStore.Services/Payments/CapturePaymentResult.cs index dd7d95be5a..dba210f775 100644 --- a/src/Libraries/SmartStore.Services/Payments/CapturePaymentResult.cs +++ b/src/Libraries/SmartStore.Services/Payments/CapturePaymentResult.cs @@ -11,21 +11,18 @@ public partial class CapturePaymentResult private PaymentStatus _newPaymentStatus = PaymentStatus.Pending; public IList Errors { get; set; } - public CapturePaymentResult() + public CapturePaymentResult() { this.Errors = new List(); } - public bool Success - { - get { return (this.Errors.Count == 0); } - } + public bool Success => (this.Errors.Count == 0); public void AddError(string error) { this.Errors.Add(error); } - + /// /// Gets or sets the capture transaction identifier /// @@ -41,14 +38,8 @@ public void AddError(string error) /// public PaymentStatus NewPaymentStatus { - get - { - return _newPaymentStatus; - } - set - { - _newPaymentStatus = value; - } + get => _newPaymentStatus; + set => _newPaymentStatus = value; } } } diff --git a/src/Libraries/SmartStore.Services/Payments/IPaymentMethod.cs b/src/Libraries/SmartStore.Services/Payments/IPaymentMethod.cs index 9dc48a1b85..2b439f7b26 100644 --- a/src/Libraries/SmartStore.Services/Payments/IPaymentMethod.cs +++ b/src/Libraries/SmartStore.Services/Payments/IPaymentMethod.cs @@ -13,12 +13,12 @@ public partial interface IPaymentMethod : IProvider, IUserEditable { #region Methods - /// - /// Pre process payment - /// - /// Payment info required for an order processing - /// Pre process payment result - PreProcessPaymentResult PreProcessPayment(ProcessPaymentRequest processPaymentRequest); + /// + /// Pre process payment + /// + /// Payment info required for an order processing + /// Pre process payment result + PreProcessPaymentResult PreProcessPayment(ProcessPaymentRequest processPaymentRequest); /// /// Process a payment @@ -97,16 +97,16 @@ public partial interface IPaymentMethod : IProvider, IUserEditable #region Properties - /// - /// Gets a value indicating whether the payment method is active and should be offered to customers - /// - bool IsActive { get; } + /// + /// Gets a value indicating whether the payment method is active and should be offered to customers + /// + bool IsActive { get; } - /// - /// Gets a value indicating whether the payment method requires user input - /// before proceeding (e.g. CreditCard, DirectDebit etc.) - /// - bool RequiresInteraction { get; } + /// + /// Gets a value indicating whether the payment method requires user input + /// before proceeding (e.g. CreditCard, DirectDebit etc.) + /// + bool RequiresInteraction { get; } /// /// Gets a value indicating whether capture is supported diff --git a/src/Libraries/SmartStore.Services/Payments/IPaymentMethodFilter.cs b/src/Libraries/SmartStore.Services/Payments/IPaymentMethodFilter.cs index 4ecd12457d..b04726c2b8 100644 --- a/src/Libraries/SmartStore.Services/Payments/IPaymentMethodFilter.cs +++ b/src/Libraries/SmartStore.Services/Payments/IPaymentMethodFilter.cs @@ -5,44 +5,40 @@ namespace SmartStore.Services.Payments { + /// + /// Allows to filter out payment methods. + /// public partial interface IPaymentMethodFilter - { - /// - /// Gets a value indicating whether a payment method should be filtered out - /// - /// Payment filter request - /// true filter out method, false do not filter out method - bool IsExcluded(PaymentFilterRequest request); + { + /// + /// Gets a value indicating whether a payment method should be filtered out. + /// + /// Payment filter request. + /// true filter out method, false do not filter out method. + bool IsExcluded(PaymentFilterRequest request); + } - /// - /// Get URL for filter configuration - /// - /// Payment provider system name - /// URL for filter configuration - string GetConfigurationUrl(string systemName); - } + public partial class PaymentFilterRequest + { + /// + /// The payment method to be checked + /// + public Provider PaymentMethod { get; set; } - public partial class PaymentFilterRequest - { - /// - /// The payment method to be checked - /// - public Provider PaymentMethod { get; set; } + /// + /// The context shopping cart + /// + public IList Cart { get; set; } - /// - /// The context shopping cart - /// - public IList Cart { get; set; } + /// + /// The context store identifier + /// + public int StoreId { get; set; } - /// - /// The context store identifier - /// - public int StoreId { get; set; } - - /// - /// The context customer - /// - public Customer Customer { get; set; } - } + /// + /// The context customer + /// + public Customer Customer { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/Payments/IPaymentService.cs b/src/Libraries/SmartStore.Services/Payments/IPaymentService.cs index d4fbe4e740..e699eb5323 100644 --- a/src/Libraries/SmartStore.Services/Payments/IPaymentService.cs +++ b/src/Libraries/SmartStore.Services/Payments/IPaymentService.cs @@ -6,45 +6,47 @@ namespace SmartStore.Services.Payments { - /// - /// Payment service interface - /// - public partial interface IPaymentService + public partial interface IPaymentService { - /// - /// Determines whether a payment method is active\enabled for a shop - /// - bool IsPaymentMethodActive(string systemName, int storeId = 0); - - /// - /// Determines whether a payment method is excluded by a filter - /// - bool IsPaymentMethodFiltered(PaymentFilterRequest filterRequest); - - /// - /// Load active payment methods - /// - /// Filter payment methods by customer and apply payment method restrictions; null to load all records - /// Filter payment methods by cart amount; null to load all records - /// Filter payment methods by store identifier; pass 0 to load all records - /// Filter payment methods by payment method types - /// Provide a fallback payment method if none is active - /// Payment methods - IEnumerable> LoadActivePaymentMethods( - Customer customer = null, - IList cart = null, - int storeId = 0, - PaymentMethodType[] types = null, - bool provideFallbackMethod = true); - - /// - /// Load payment provider by system name - /// - /// System name - /// true to load only active provider - /// Load records allowed only in specified store; pass 0 to load all records - /// Found payment provider - Provider LoadPaymentMethodBySystemName(string systemName, bool onlyWhenActive = false, int storeId = 0); + /// + /// Checks whether a payment method is active for a shop. + /// + bool IsPaymentMethodActive(string systemName, int storeId = 0); + + /// + /// Checks whether a payment method is active, not filtered out and match applied rule sets. + /// A payment method that meets these requirements appears in the checkout. + /// + bool IsPaymentMethodActive( + string systemName, + Customer customer = null, + IList cart = null, + int storeId = 0); + + /// + /// Loads payment methods that are active, not filtered out and match applied rule sets. + /// + /// Filter payment methods by customer. null to load all. + /// Filter payment methods by cart. null to load all. + /// Filter payment methods by store identifier. 0 to load all. + /// Filter payment methods by payment method types. + /// Provide a fallback payment method if there is no match. + /// Filtered payment methods. + IEnumerable> LoadActivePaymentMethods( + Customer customer = null, + IList cart = null, + int storeId = 0, + PaymentMethodType[] types = null, + bool provideFallbackMethod = true); + + /// + /// Load payment provider by system name + /// + /// System name + /// true to load only active provider + /// Load records allowed only in specified store; pass 0 to load all records + /// Found payment provider + Provider LoadPaymentMethodBySystemName(string systemName, bool onlyWhenActive = false, int storeId = 0); /// /// Load all payment providers @@ -53,46 +55,45 @@ IEnumerable> LoadActivePaymentMethods( /// Payment providers IEnumerable> LoadAllPaymentMethods(int storeId = 0); + /// + /// Gets all payment methods. + /// + /// Load records allowed only in specified store; pass 0 to load all records. + /// Dictionary of payment methods. Key is the payment method system name. + IDictionary GetAllPaymentMethods(int storeId = 0); - /// - /// Gets all payment method extra data - /// - /// Load records allowed only in specified store; pass 0 to load all records - /// List of payment method objects - IList GetAllPaymentMethods(int storeId = 0); - - /// - /// Gets payment method extra data by system name - /// - /// Provider system name - /// Payment method entity - PaymentMethod GetPaymentMethodBySystemName(string systemName); - - /// - /// Insert payment method extra data - /// - /// Payment method - void InsertPaymentMethod(PaymentMethod paymentMethod); - - /// - /// Updates payment method extra data - /// - /// Payment method - void UpdatePaymentMethod(PaymentMethod paymentMethod); - - /// - /// Delete payment method extra data - /// - /// Payment method - void DeletePaymentMethod(PaymentMethod paymentMethod); - - - /// - /// Pre process a payment - /// - /// Payment info required for an order processing - /// Pre process payment result - PreProcessPaymentResult PreProcessPayment(ProcessPaymentRequest processPaymentRequest); + /// + /// Gets payment method extra data by system name + /// + /// Provider system name + /// Payment method entity + PaymentMethod GetPaymentMethodBySystemName(string systemName); + + /// + /// Insert payment method extra data + /// + /// Payment method + void InsertPaymentMethod(PaymentMethod paymentMethod); + + /// + /// Updates payment method extra data + /// + /// Payment method + void UpdatePaymentMethod(PaymentMethod paymentMethod); + + /// + /// Delete payment method extra data + /// + /// Payment method + void DeletePaymentMethod(PaymentMethod paymentMethod); + + + /// + /// Pre process a payment + /// + /// Payment info required for an order processing + /// Pre process payment result + PreProcessPaymentResult PreProcessPayment(ProcessPaymentRequest processPaymentRequest); /// /// Process a payment @@ -217,10 +218,10 @@ IEnumerable> LoadActivePaymentMethods( /// Masked credit card number string GetMaskedCreditCardNumber(string creditCardNumber); - /// - /// Gets all payment filters - /// - /// List of payment filters - IList GetAllPaymentMethodFilters(); + /// + /// Gets all payment filters + /// + /// List of payment filters + IList GetAllPaymentMethodFilters(); } } diff --git a/src/Libraries/SmartStore.Services/Payments/PaymentExtentions.cs b/src/Libraries/SmartStore.Services/Payments/PaymentExtentions.cs index 85504dad98..f157ef74d9 100644 --- a/src/Libraries/SmartStore.Services/Payments/PaymentExtentions.cs +++ b/src/Libraries/SmartStore.Services/Payments/PaymentExtentions.cs @@ -9,7 +9,7 @@ namespace SmartStore.Services.Payments { - public static class PaymentExtentions + public static class PaymentExtentions { /// /// Is payment method active? @@ -28,10 +28,10 @@ public static bool IsPaymentMethodActive(this Provider paymentMe if (paymentSettings.ActivePaymentMethodSystemNames == null) return false; - if (!paymentMethod.Value.IsActive) - return false; + if (!paymentMethod.Value.IsActive) + return false; - return paymentSettings.ActivePaymentMethodSystemNames.Contains(paymentMethod.Metadata.SystemName, StringComparer.OrdinalIgnoreCase); + return paymentSettings.ActivePaymentMethodSystemNames.Contains(paymentMethod.Metadata.SystemName, StringComparer.OrdinalIgnoreCase); } /// @@ -44,10 +44,10 @@ public static bool IsPaymentMethodActive(this Provider paymentMe /// Is fee amount specified as percentage or fixed value? /// Result public static decimal CalculateAdditionalFee(this IPaymentMethod paymentMethod, - IOrderTotalCalculationService orderTotalCalculationService, - IList cart, - decimal fee, - bool usePercentage) + IOrderTotalCalculationService orderTotalCalculationService, + IList cart, + decimal fee, + bool usePercentage) { if (paymentMethod == null) throw new ArgumentNullException("paymentMethod"); @@ -70,43 +70,43 @@ public static decimal CalculateAdditionalFee(this IPaymentMethod paymentMethod, return result; } - public static RouteInfo GetConfigurationRoute(this IPaymentMethod method) - { - Guard.NotNull(method, nameof(method)); - - string action; - string controller; - RouteValueDictionary routeValues; - - var configurable = method as IConfigurable; - - if (configurable != null) - { - configurable.GetConfigurationRoute(out action, out controller, out routeValues); - if (action.HasValue()) - { - return new RouteInfo(action, controller, routeValues); - } - } - - return null; - } - - public static RouteInfo GetPaymentInfoRoute(this IPaymentMethod method) - { - Guard.NotNull(method, nameof(method)); - - string action; - string controller; - RouteValueDictionary routeValues; - - method.GetPaymentInfoRoute(out action, out controller, out routeValues); - if (action.HasValue()) - { - return new RouteInfo(action, controller, routeValues); - } - - return null; - } + public static RouteInfo GetConfigurationRoute(this IPaymentMethod method) + { + Guard.NotNull(method, nameof(method)); + + string action; + string controller; + RouteValueDictionary routeValues; + + var configurable = method as IConfigurable; + + if (configurable != null) + { + configurable.GetConfigurationRoute(out action, out controller, out routeValues); + if (action.HasValue()) + { + return new RouteInfo(action, controller, routeValues); + } + } + + return null; + } + + public static RouteInfo GetPaymentInfoRoute(this IPaymentMethod method) + { + Guard.NotNull(method, nameof(method)); + + string action; + string controller; + RouteValueDictionary routeValues; + + method.GetPaymentInfoRoute(out action, out controller, out routeValues); + if (action.HasValue()) + { + return new RouteInfo(action, controller, routeValues); + } + + return null; + } } } diff --git a/src/Libraries/SmartStore.Services/Payments/PaymentMethodBase.cs b/src/Libraries/SmartStore.Services/Payments/PaymentMethodBase.cs index 3510b71b2a..bfa3fd2948 100644 --- a/src/Libraries/SmartStore.Services/Payments/PaymentMethodBase.cs +++ b/src/Libraries/SmartStore.Services/Payments/PaymentMethodBase.cs @@ -9,254 +9,206 @@ namespace SmartStore.Services.Payments { - /// - /// Base class for payment methods - /// - public abstract class PaymentMethodBase : IPaymentMethod - { - - protected PaymentMethodBase() - { - T = NullLocalizer.Instance; - } - - public Localizer T { get; set; } - - #region Methods - - /// - /// Pre process payment - /// - /// Payment info required for an order processing - /// Pre process payment result - public virtual PreProcessPaymentResult PreProcessPayment(ProcessPaymentRequest processPaymentRequest) - { - var result = new PreProcessPaymentResult(); - return result; - } - - /// - /// Process payment - /// - /// Payment info required for an order processing - /// Process payment result - public abstract ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest); - - /// - /// Post process payment (used by payment gateways that require redirecting to a third-party URL) - /// - /// Payment info required for an order processing - public virtual void PostProcessPayment(PostProcessPaymentRequest postProcessPaymentRequest) - { - // NoImpl - } - - /// - /// Gets additional handling fee - /// - /// Additional handling fee - public virtual decimal GetAdditionalHandlingFee(IList cart) - { - return 0; - } - - /// - /// Captures payment - /// - /// Capture payment request - /// Capture payment result - public virtual CapturePaymentResult Capture(CapturePaymentRequest capturePaymentRequest) - { - var result = new CapturePaymentResult(); - result.AddError(T("Common.Payment.NoCaptureSupport")); - return result; - } - - /// - /// Refunds a payment - /// - /// Request - /// Result - public virtual RefundPaymentResult Refund(RefundPaymentRequest refundPaymentRequest) - { - var result = new RefundPaymentResult(); - result.AddError(T("Common.Payment.NoRefundSupport")); - return result; - } - - /// - /// Voids a payment - /// - /// Request - /// Result - public virtual VoidPaymentResult Void(VoidPaymentRequest voidPaymentRequest) - { - var result = new VoidPaymentResult(); - result.AddError(T("Common.Payment.NoVoidSupport")); - return result; - } - - /// - /// Process recurring payment - /// - /// Payment info required for an order processing - /// Process payment result - public virtual ProcessPaymentResult ProcessRecurringPayment(ProcessPaymentRequest processPaymentRequest) - { - var result = new ProcessPaymentResult(); - result.AddError(T("Common.Payment.NoRecurringPaymentSupport")); - return result; - } - - /// - /// Cancels a recurring payment - /// - /// Request - /// Result - public virtual CancelRecurringPaymentResult CancelRecurringPayment(CancelRecurringPaymentRequest cancelPaymentRequest) - { - var result = new CancelRecurringPaymentResult(); - result.AddError(T("Common.Payment.NoRecurringPaymentSupport")); - return result; - } - - /// - /// Gets a value indicating whether customers can complete a payment after order is placed but not completed (for redirection payment methods) - /// - /// Order - /// Result - public virtual bool CanRePostProcessPayment(Order order) - { - return false; - } - - /// - /// Gets a route for provider configuration - /// - /// Action name - /// Controller name - /// Route values - public abstract void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues); - - /// - /// Gets a route for payment info - /// - /// Action name - /// Controller name - /// Route values - public abstract void GetPaymentInfoRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues); - - /// - /// Gets a route for the payment info handler controller action - /// - /// Order - /// Result - /// - /// The defined route is being redirected to during the checkout process > PaymentInfo page. - /// Implementors should return null if no redirection occurs. - /// - public virtual RouteInfo GetPaymentInfoHandlerRoute() - { - return null; - } - - public abstract Type GetControllerType(); - - #endregion - - #region Properties - - /// - /// Gets a value indicating whether the payment method is active and should be offered to customers - /// - public virtual bool IsActive - { - get - { - return true; - } - } - - /// - /// Gets a value indicating whether the payment method requires user input - /// before proceeding (e.g. CreditCard, DirectDebit etc.) - /// - public virtual bool RequiresInteraction - { - get - { - return false; - } - } - - /// - /// Gets a value indicating whether capture is supported - /// - public virtual bool SupportCapture - { - get - { - return false; - } - } - - /// - /// Gets a value indicating whether partial refund is supported - /// - public virtual bool SupportPartiallyRefund - { - get - { - return false; - } - } - - /// - /// Gets a value indicating whether refund is supported - /// - public virtual bool SupportRefund - { - get - { - return false; - } - } - - /// - /// Gets a value indicating whether void is supported - /// - public virtual bool SupportVoid - { - get - { - return false; - } - } - - /// - /// Gets a recurring payment type of payment method - /// - public virtual RecurringPaymentType RecurringPaymentType - { - get - { - return RecurringPaymentType.NotSupported; - } - } - - /// - /// Gets a payment method type - /// - public virtual PaymentMethodType PaymentMethodType - { - get - { - return PaymentMethodType.Unknown; - } - } - - #endregion - - } + /// + /// Base class for payment methods + /// + public abstract class PaymentMethodBase : IPaymentMethod + { + + protected PaymentMethodBase() + { + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + #region Methods + + /// + /// Pre process payment + /// + /// Payment info required for an order processing + /// Pre process payment result + public virtual PreProcessPaymentResult PreProcessPayment(ProcessPaymentRequest processPaymentRequest) + { + var result = new PreProcessPaymentResult(); + return result; + } + + /// + /// Process payment + /// + /// Payment info required for an order processing + /// Process payment result + public abstract ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest); + + /// + /// Post process payment (used by payment gateways that require redirecting to a third-party URL) + /// + /// Payment info required for an order processing + public virtual void PostProcessPayment(PostProcessPaymentRequest postProcessPaymentRequest) + { + // NoImpl + } + + /// + /// Gets additional handling fee + /// + /// Additional handling fee + public virtual decimal GetAdditionalHandlingFee(IList cart) + { + return 0; + } + + /// + /// Captures payment + /// + /// Capture payment request + /// Capture payment result + public virtual CapturePaymentResult Capture(CapturePaymentRequest capturePaymentRequest) + { + var result = new CapturePaymentResult(); + result.AddError(T("Common.Payment.NoCaptureSupport")); + return result; + } + + /// + /// Refunds a payment + /// + /// Request + /// Result + public virtual RefundPaymentResult Refund(RefundPaymentRequest refundPaymentRequest) + { + var result = new RefundPaymentResult(); + result.AddError(T("Common.Payment.NoRefundSupport")); + return result; + } + + /// + /// Voids a payment + /// + /// Request + /// Result + public virtual VoidPaymentResult Void(VoidPaymentRequest voidPaymentRequest) + { + var result = new VoidPaymentResult(); + result.AddError(T("Common.Payment.NoVoidSupport")); + return result; + } + + /// + /// Process recurring payment + /// + /// Payment info required for an order processing + /// Process payment result + public virtual ProcessPaymentResult ProcessRecurringPayment(ProcessPaymentRequest processPaymentRequest) + { + var result = new ProcessPaymentResult(); + result.AddError(T("Common.Payment.NoRecurringPaymentSupport")); + return result; + } + + /// + /// Cancels a recurring payment + /// + /// Request + /// Result + public virtual CancelRecurringPaymentResult CancelRecurringPayment(CancelRecurringPaymentRequest cancelPaymentRequest) + { + var result = new CancelRecurringPaymentResult(); + result.AddError(T("Common.Payment.NoRecurringPaymentSupport")); + return result; + } + + /// + /// Gets a value indicating whether customers can complete a payment after order is placed but not completed (for redirection payment methods) + /// + /// Order + /// Result + public virtual bool CanRePostProcessPayment(Order order) + { + return false; + } + + /// + /// Gets a route for provider configuration + /// + /// Action name + /// Controller name + /// Route values + public abstract void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues); + + /// + /// Gets a route for payment info + /// + /// Action name + /// Controller name + /// Route values + public abstract void GetPaymentInfoRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues); + + /// + /// Gets a route for the payment info handler controller action + /// + /// Order + /// Result + /// + /// The defined route is being redirected to during the checkout process > PaymentInfo page. + /// Implementors should return null if no redirection occurs. + /// + public virtual RouteInfo GetPaymentInfoHandlerRoute() + { + return null; + } + + public abstract Type GetControllerType(); + + #endregion + + #region Properties + + /// + /// Gets a value indicating whether the payment method is active and should be offered to customers + /// + public virtual bool IsActive => true; + + /// + /// Gets a value indicating whether the payment method requires user input + /// before proceeding (e.g. CreditCard, DirectDebit etc.) + /// + public virtual bool RequiresInteraction => false; + + /// + /// Gets a value indicating whether capture is supported + /// + public virtual bool SupportCapture => false; + + /// + /// Gets a value indicating whether partial refund is supported + /// + public virtual bool SupportPartiallyRefund => false; + + /// + /// Gets a value indicating whether refund is supported + /// + public virtual bool SupportRefund => false; + + /// + /// Gets a value indicating whether void is supported + /// + public virtual bool SupportVoid => false; + + /// + /// Gets a recurring payment type of payment method + /// + public virtual RecurringPaymentType RecurringPaymentType => RecurringPaymentType.NotSupported; + + /// + /// Gets a payment method type + /// + public virtual PaymentMethodType PaymentMethodType => PaymentMethodType.Unknown; + + #endregion + + } } diff --git a/src/Libraries/SmartStore.Services/Payments/PaymentMethodType.cs b/src/Libraries/SmartStore.Services/Payments/PaymentMethodType.cs index cfa24c23b7..5caf5afd51 100644 --- a/src/Libraries/SmartStore.Services/Payments/PaymentMethodType.cs +++ b/src/Libraries/SmartStore.Services/Payments/PaymentMethodType.cs @@ -25,9 +25,9 @@ public enum PaymentMethodType : int /// All payment information is entered on the site and is available via button /// StandardAndButton = 25, - /// - /// Payment information is entered in checkout and customer is redirected to complete payment (e.g. 3D Secure) after order has been placed - /// - StandardAndRedirection = 30 + /// + /// Payment information is entered in checkout and customer is redirected to complete payment (e.g. 3D Secure) after order has been placed + /// + StandardAndRedirection = 30 } } diff --git a/src/Libraries/SmartStore.Services/Payments/PaymentPluginBase.cs b/src/Libraries/SmartStore.Services/Payments/PaymentPluginBase.cs index 3ab7d5b6e3..81ee03d9ef 100644 --- a/src/Libraries/SmartStore.Services/Payments/PaymentPluginBase.cs +++ b/src/Libraries/SmartStore.Services/Payments/PaymentPluginBase.cs @@ -2,27 +2,27 @@ namespace SmartStore.Services.Payments { - public abstract class PaymentPluginBase : PaymentMethodBase, IPlugin - { - /// - /// Gets or sets the plugin descriptor - /// - public virtual PluginDescriptor PluginDescriptor { get; set; } + public abstract class PaymentPluginBase : PaymentMethodBase, IPlugin + { + /// + /// Gets or sets the plugin descriptor + /// + public virtual PluginDescriptor PluginDescriptor { get; set; } - /// - /// Install plugin - /// - public virtual void Install() - { - PluginManager.MarkPluginAsInstalled(this.PluginDescriptor.SystemName); - } + /// + /// Install plugin + /// + public virtual void Install() + { + PluginManager.MarkPluginAsInstalled(this.PluginDescriptor.SystemName); + } - /// - /// Uninstall plugin - /// - public virtual void Uninstall() - { - PluginManager.MarkPluginAsUninstalled(this.PluginDescriptor.SystemName); - } - } + /// + /// Uninstall plugin + /// + public virtual void Uninstall() + { + PluginManager.MarkPluginAsUninstalled(this.PluginDescriptor.SystemName); + } + } } diff --git a/src/Libraries/SmartStore.Services/Payments/PaymentService.cs b/src/Libraries/SmartStore.Services/Payments/PaymentService.cs index 4f6298a3b5..c6d32257e2 100644 --- a/src/Libraries/SmartStore.Services/Payments/PaymentService.cs +++ b/src/Libraries/SmartStore.Services/Payments/PaymentService.cs @@ -10,102 +10,98 @@ using SmartStore.Core.Localization; using SmartStore.Core.Logging; using SmartStore.Core.Plugins; +using SmartStore.Services.Cart.Rules; using SmartStore.Services.Stores; namespace SmartStore.Services.Payments { - /// - /// Payment service - /// public partial class PaymentService : IPaymentService { - private readonly static object _lock = new object(); - private static IList _paymentMethodFilterTypes = null; - - private readonly IRepository _paymentMethodRepository; - private readonly IRepository _storeMappingRepository; - private readonly IStoreMappingService _storeMappingService; - private readonly PaymentSettings _paymentSettings; - private readonly ShoppingCartSettings _shoppingCartSettings; - private readonly IProviderManager _providerManager; - private readonly ICommonServices _services; - private readonly ITypeFinder _typeFinder; - - public PaymentService( - IRepository paymentMethodRepository, - IRepository storeMappingRepository, - IStoreMappingService storeMappingService, - PaymentSettings paymentSettings, - ShoppingCartSettings shoppingCartSettings, - IProviderManager providerManager, - ICommonServices services, - ITypeFinder typeFinder) + private const string PAYMENT_METHODS_ALL_KEY = "SmartStore.paymentmethod.all-{0}-"; + private const string PAYMENT_METHODS_PATTERN_KEY = "SmartStore.paymentmethod.*"; + + private readonly static object _lock = new object(); + private static IList _paymentMethodFilterTypes = null; + + private readonly IRepository _paymentMethodRepository; + private readonly IRepository _storeMappingRepository; + private readonly IStoreMappingService _storeMappingService; + private readonly PaymentSettings _paymentSettings; + private readonly ICartRuleProvider _cartRuleProvider; + private readonly IProviderManager _providerManager; + private readonly ICommonServices _services; + private readonly ITypeFinder _typeFinder; + + public PaymentService( + IRepository paymentMethodRepository, + IRepository storeMappingRepository, + IStoreMappingService storeMappingService, + PaymentSettings paymentSettings, + ICartRuleProvider cartRuleProvider, + IProviderManager providerManager, + ICommonServices services, + ITypeFinder typeFinder) { - _paymentMethodRepository = paymentMethodRepository; - _storeMappingRepository = storeMappingRepository; - _storeMappingService = storeMappingService; - _paymentSettings = paymentSettings; - _shoppingCartSettings = shoppingCartSettings; - _providerManager = providerManager; - _services = services; - _typeFinder = typeFinder; - - T = NullLocalizer.Instance; - Logger = NullLogger.Instance; - QuerySettings = DbQuerySettings.Default; - } - - public Localizer T { get; set; } - public ILogger Logger { get; set; } - public DbQuerySettings QuerySettings { get; set; } + _paymentMethodRepository = paymentMethodRepository; + _storeMappingRepository = storeMappingRepository; + _storeMappingService = storeMappingService; + _paymentSettings = paymentSettings; + _cartRuleProvider = cartRuleProvider; + _providerManager = providerManager; + _services = services; + _typeFinder = typeFinder; + } + + public Localizer T { get; set; } = NullLocalizer.Instance; + public ILogger Logger { get; set; } = NullLogger.Instance; + public DbQuerySettings QuerySettings { get; set; } = DbQuerySettings.Default; #region Methods - public virtual bool IsPaymentMethodActive(string systemName, int storeId = 0) - { - var method = LoadPaymentMethodBySystemName(systemName, true, storeId); - return method != null; - } - - public virtual bool IsPaymentMethodFiltered(PaymentFilterRequest filterRequest) - { - Guard.NotNull(filterRequest, nameof(filterRequest)); - - var allFilters = GetAllPaymentMethodFilters(); - return allFilters.Any(x => x.IsExcluded(filterRequest)); - } - - /// - /// Load active payment methods - /// - /// Filter payment methods by customer and apply payment method restrictions; null to load all records - /// Filter payment methods by cart amount; null to load all records - /// Filter payment methods by store identifier; pass 0 to load all records - /// Filter payment methods by payment method types - /// Provide a fallback payment method if none is active - /// Payment methods - public virtual IEnumerable> LoadActivePaymentMethods( - Customer customer = null, - IList cart = null, - int storeId = 0, - PaymentMethodType[] types = null, - bool provideFallbackMethod = true) + public virtual bool IsPaymentMethodActive(string systemName, int storeId = 0) + { + var method = LoadPaymentMethodBySystemName(systemName, true, storeId); + return method != null; + } + + public virtual bool IsPaymentMethodActive( + string systemName, + Customer customer = null, + IList cart = null, + int storeId = 0) + { + Guard.NotEmpty(systemName, nameof(systemName)); + + var activePaymentMethods = LoadActivePaymentMethods(customer, cart, storeId, null, false).ToList(); + var method = activePaymentMethods.FirstOrDefault(x => x.Metadata.SystemName.IsCaseInsensitiveEqual(systemName)); + + return method != null; + } + + public virtual IEnumerable> LoadActivePaymentMethods( + Customer customer = null, + IList cart = null, + int storeId = 0, + PaymentMethodType[] types = null, + bool provideFallbackMethod = true) { - var filterRequest = new PaymentFilterRequest - { - Cart = cart, - StoreId = storeId, - Customer = customer - }; + var filterRequest = new PaymentFilterRequest + { + Cart = cart, + StoreId = storeId, + Customer = customer + }; var allFilters = GetAllPaymentMethodFilters(); var allProviders = types != null && types.Any() ? LoadAllPaymentMethods(storeId).Where(x => types.Contains(x.Value.PaymentMethodType)) : LoadAllPaymentMethods(storeId); - var activeProviders = allProviders - .Where(p => - { + var paymentMethods = GetAllPaymentMethods(storeId); + + var activeProviders = allProviders + .Where(p => + { try { // Only active payment methods. @@ -114,6 +110,15 @@ public virtual IEnumerable> LoadActivePaymentMethods( return false; } + // Rule sets. + if (paymentMethods.TryGetValue(p.Metadata.SystemName, out var pm)) + { + if (!_cartRuleProvider.RuleMatches(pm)) + { + return false; + } + } + filterRequest.PaymentMethod = p; // Only payment methods that have not been filtered out. @@ -124,24 +129,25 @@ public virtual IEnumerable> LoadActivePaymentMethods( } catch (Exception ex) { - Logger.Error(ex); + Logger.Error(ex); } - return true; - }); + return true; + }); - if (!activeProviders.Any() && provideFallbackMethod) - { - var fallbackMethod = allProviders.FirstOrDefault(x => x.IsPaymentMethodActive(_paymentSettings)); + if (!activeProviders.Any() && provideFallbackMethod) + { + var fallbackMethod = allProviders.FirstOrDefault(x => x.IsPaymentMethodActive(_paymentSettings)); if (fallbackMethod == null) { - fallbackMethod = allProviders.FirstOrDefault(); + fallbackMethod = allProviders.FirstOrDefault(x => x.Metadata?.PluginDescriptor?.SystemName?.IsCaseInsensitiveEqual("SmartStore.OfflinePayment") ?? false) + ?? allProviders.FirstOrDefault(); } - if (fallbackMethod != null) - { - return new Provider[] { fallbackMethod }; - } + if (fallbackMethod != null) + { + return new Provider[] { fallbackMethod }; + } if (DataSettings.DatabaseIsInstalled()) { @@ -152,149 +158,161 @@ public virtual IEnumerable> LoadActivePaymentMethods( return activeProviders; } - public virtual Provider LoadPaymentMethodBySystemName(string systemName, bool onlyWhenActive = false, int storeId = 0) + public virtual Provider LoadPaymentMethodBySystemName(string systemName, bool onlyWhenActive = false, int storeId = 0) + { + var provider = _providerManager.GetProvider(systemName, storeId); + if (provider != null) + { + if (onlyWhenActive && !provider.IsPaymentMethodActive(_paymentSettings)) + { + return null; + } + + if (!QuerySettings.IgnoreMultiStore && storeId > 0) + { + // Return provider if paymentMethod is null! + var paymentMethod = _paymentMethodRepository.TableUntracked.FirstOrDefault(x => x.PaymentMethodSystemName == systemName); + if (paymentMethod != null && !_storeMappingService.Authorize(paymentMethod, storeId)) + { + return null; + } + } + } + + return provider; + } + + public virtual IEnumerable> LoadAllPaymentMethods(int storeId = 0) + { + var providers = _providerManager.GetAllProviders(storeId); + + if (providers.Any() && !QuerySettings.IgnoreMultiStore && storeId > 0) + { + var unauthorizedMethods = _paymentMethodRepository.TableUntracked + .Where(x => x.LimitedToStores) + .ToList(); + + var unauthorizedMethodNames = unauthorizedMethods + .Where(x => !_storeMappingService.Authorize(x, storeId)) + .Select(x => x.PaymentMethodSystemName) + .ToList(); + + return providers.Where(x => !unauthorizedMethodNames.Contains(x.Metadata.SystemName)); + } + + return providers; + } + + public virtual IDictionary GetAllPaymentMethods(int storeId = 0) { - var provider = _providerManager.GetProvider(systemName, storeId); - if (provider != null) - { - if (onlyWhenActive && !provider.IsPaymentMethodActive(_paymentSettings)) - { - return null; - } - - if (!QuerySettings.IgnoreMultiStore && storeId > 0) - { - // Return provider if paymentMethod is null! - var paymentMethod = _paymentMethodRepository.TableUntracked.FirstOrDefault(x => x.PaymentMethodSystemName == systemName); - if (paymentMethod != null && !_storeMappingService.Authorize(paymentMethod, storeId)) - { - return null; - } - } - } - - return provider; + var result = _services.RequestCache.Get(PAYMENT_METHODS_ALL_KEY.FormatInvariant(storeId), () => + { + var query = _paymentMethodRepository.TableUntracked; + + if (!QuerySettings.IgnoreMultiStore && storeId > 0) + { + query = + from x in query + join sm in _storeMappingRepository.TableUntracked + on new { c1 = x.Id, c2 = "PaymentMethod" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into m_sm + from sm in m_sm.DefaultIfEmpty() + where !x.LimitedToStores || storeId == sm.StoreId + select x; + + query = + from x in query + group x by x.Id into mGroup + orderby mGroup.Key + select mGroup.FirstOrDefault(); + } + + var methods = query + .ToList() + .ToDictionarySafe(x => x.PaymentMethodSystemName.EmptyNull(), x => x, StringComparer.OrdinalIgnoreCase); + + return methods; + }); + + return result; } - public virtual IEnumerable> LoadAllPaymentMethods(int storeId = 0) + /// + /// Gets payment method extra data by system name + /// + /// Provider system name + /// Payment method entity + public virtual PaymentMethod GetPaymentMethodBySystemName(string systemName) { - var providers = _providerManager.GetAllProviders(storeId); + if (systemName.HasValue()) + { + return _paymentMethodRepository.Table.FirstOrDefault(x => x.PaymentMethodSystemName == systemName); + } - if (providers.Any() && !QuerySettings.IgnoreMultiStore && storeId > 0) - { - var unauthorizedMethods = _paymentMethodRepository.TableUntracked - .Where(x => x.LimitedToStores) - .ToList(); + return null; + } - var unauthorizedMethodNames = unauthorizedMethods - .Where(x => !_storeMappingService.Authorize(x, storeId)) - .Select(x => x.PaymentMethodSystemName) - .ToList(); + /// + /// Insert payment method extra data + /// + /// Payment method + public virtual void InsertPaymentMethod(PaymentMethod paymentMethod) + { + Guard.NotNull(paymentMethod, nameof(paymentMethod)); - return providers.Where(x => !unauthorizedMethodNames.Contains(x.Metadata.SystemName)); - } + _paymentMethodRepository.Insert(paymentMethod); + + _services.RequestCache.RemoveByPattern(PAYMENT_METHODS_PATTERN_KEY); + } + + /// + /// Updates payment method extra data + /// + /// Payment method + public virtual void UpdatePaymentMethod(PaymentMethod paymentMethod) + { + Guard.NotNull(paymentMethod, nameof(paymentMethod)); + + _paymentMethodRepository.Update(paymentMethod); + + _services.RequestCache.RemoveByPattern(PAYMENT_METHODS_PATTERN_KEY); + } + + /// + /// Delete payment method extra data + /// + /// Payment method + public virtual void DeletePaymentMethod(PaymentMethod paymentMethod) + { + if (paymentMethod != null) + { + _paymentMethodRepository.Delete(paymentMethod); - return providers; + _services.RequestCache.RemoveByPattern(PAYMENT_METHODS_PATTERN_KEY); + } } - public virtual IList GetAllPaymentMethods(int storeId = 0) - { - var query = _paymentMethodRepository.TableUntracked; - - if (!QuerySettings.IgnoreMultiStore && storeId > 0) - { - query = - from x in query - join sm in _storeMappingRepository.TableUntracked - on new { c1 = x.Id, c2 = "PaymentMethod" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into m_sm - from sm in m_sm.DefaultIfEmpty() - where !x.LimitedToStores || storeId == sm.StoreId - select x; - - query = - from x in query - group x by x.Id into mGroup - orderby mGroup.Key - select mGroup.FirstOrDefault(); - } - - var methods = query.ToList(); - return methods; - } - - /// - /// Gets payment method extra data by system name - /// - /// Provider system name - /// Payment method entity - public virtual PaymentMethod GetPaymentMethodBySystemName(string systemName) - { - if (systemName.HasValue()) - { - return _paymentMethodRepository.Table.FirstOrDefault(x => x.PaymentMethodSystemName == systemName); - } - - return null; - } - - /// - /// Insert payment method extra data - /// - /// Payment method - public virtual void InsertPaymentMethod(PaymentMethod paymentMethod) - { - if (paymentMethod == null) - throw new ArgumentNullException("paymentMethod"); - - _paymentMethodRepository.Insert(paymentMethod); - } - - /// - /// Updates payment method extra data - /// - /// Payment method - public virtual void UpdatePaymentMethod(PaymentMethod paymentMethod) - { - if (paymentMethod == null) - throw new ArgumentNullException("paymentMethod"); - - _paymentMethodRepository.Update(paymentMethod); - } - - /// - /// Delete payment method extra data - /// - /// Payment method - public virtual void DeletePaymentMethod(PaymentMethod paymentMethod) - { - if (paymentMethod == null) - throw new ArgumentNullException("paymentMethod"); - - _paymentMethodRepository.Delete(paymentMethod); - } - - - /// - /// Pre process a payment - /// - /// Payment info required for an order processing - /// Pre process payment result - public virtual PreProcessPaymentResult PreProcessPayment(ProcessPaymentRequest processPaymentRequest) - { - if (processPaymentRequest.OrderTotal == decimal.Zero) - { - var result = new PreProcessPaymentResult(); - return result; - } - else - { - var paymentMethod = LoadPaymentMethodBySystemName(processPaymentRequest.PaymentMethodSystemName); - if (paymentMethod == null) - throw new SmartException(T("Payment.CouldNotLoadMethod")); - - return paymentMethod.Value.PreProcessPayment(processPaymentRequest); - } - } + + /// + /// Pre process a payment + /// + /// Payment info required for an order processing + /// Pre process payment result + public virtual PreProcessPaymentResult PreProcessPayment(ProcessPaymentRequest processPaymentRequest) + { + if (processPaymentRequest.OrderTotal == decimal.Zero) + { + var result = new PreProcessPaymentResult(); + return result; + } + else + { + var paymentMethod = LoadPaymentMethodBySystemName(processPaymentRequest.PaymentMethodSystemName); + if (paymentMethod == null) + throw new SmartException(T("Payment.CouldNotLoadMethod")); + + return paymentMethod.Value.PreProcessPayment(processPaymentRequest); + } + } /// /// Process a payment @@ -320,9 +338,9 @@ public virtual ProcessPaymentResult ProcessPayment(ProcessPaymentRequest process processPaymentRequest.CreditCardNumber = processPaymentRequest.CreditCardNumber.Replace("-", ""); } - var paymentMethod = LoadPaymentMethodBySystemName(processPaymentRequest.PaymentMethodSystemName); + var paymentMethod = LoadPaymentMethodBySystemName(processPaymentRequest.PaymentMethodSystemName); - if (paymentMethod == null) + if (paymentMethod == null) throw new SmartException(T("Payment.CouldNotLoadMethod")); return paymentMethod.Value.ProcessPayment(processPaymentRequest); @@ -336,14 +354,14 @@ public virtual ProcessPaymentResult ProcessPayment(ProcessPaymentRequest process /// Payment info required for an order processing public virtual void PostProcessPayment(PostProcessPaymentRequest postProcessPaymentRequest) { - if (postProcessPaymentRequest.Order.PaymentMethodSystemName.HasValue()) - { - var paymentMethod = LoadPaymentMethodBySystemName(postProcessPaymentRequest.Order.PaymentMethodSystemName); - if (paymentMethod == null) - throw new SmartException(T("Payment.CouldNotLoadMethod")); - - paymentMethod.Value.PostProcessPayment(postProcessPaymentRequest); - } + if (postProcessPaymentRequest.Order.PaymentMethodSystemName.HasValue()) + { + var paymentMethod = LoadPaymentMethodBySystemName(postProcessPaymentRequest.Order.PaymentMethodSystemName); + if (paymentMethod == null) + throw new SmartException(T("Payment.CouldNotLoadMethod")); + + paymentMethod.Value.PostProcessPayment(postProcessPaymentRequest); + } } /// @@ -363,7 +381,7 @@ public virtual bool CanRePostProcessPayment(Order order) if (paymentMethod == null) return false; //Payment method couldn't be loaded (for example, was uninstalled) - if (paymentMethod.Value.PaymentMethodType != PaymentMethodType.Redirection && paymentMethod.Value.PaymentMethodType != PaymentMethodType.StandardAndRedirection) + if (paymentMethod.Value.PaymentMethodType != PaymentMethodType.Redirection && paymentMethod.Value.PaymentMethodType != PaymentMethodType.StandardAndRedirection) return false; //this option is available only for redirection payment methods if (order.Deleted) @@ -375,7 +393,7 @@ public virtual bool CanRePostProcessPayment(Order order) if (order.PaymentStatus != PaymentStatus.Pending) return false; //payment status should be Pending - return paymentMethod.Value.CanRePostProcessPayment(order); + return paymentMethod.Value.CanRePostProcessPayment(order); } @@ -389,11 +407,11 @@ public virtual bool CanRePostProcessPayment(Order order) public virtual decimal GetAdditionalHandlingFee(IList cart, string paymentMethodSystemName) { var paymentMethod = LoadPaymentMethodBySystemName(paymentMethodSystemName); - var paymentMethodAdditionalFee = (paymentMethod != null ? paymentMethod.Value.GetAdditionalHandlingFee(cart) : decimal.Zero); + var paymentMethodAdditionalFee = (paymentMethod != null ? paymentMethod.Value.GetAdditionalHandlingFee(cart) : decimal.Zero); paymentMethodAdditionalFee = paymentMethodAdditionalFee.RoundIfEnabledFor(_services.WorkContext.WorkingCurrency); - return paymentMethodAdditionalFee; + return paymentMethodAdditionalFee; } @@ -408,7 +426,7 @@ public virtual bool SupportCapture(string paymentMethodSystemName) var paymentMethod = LoadPaymentMethodBySystemName(paymentMethodSystemName); if (paymentMethod == null) return false; - return paymentMethod.Value.SupportCapture; + return paymentMethod.Value.SupportCapture; } /// @@ -422,20 +440,20 @@ public virtual CapturePaymentResult Capture(CapturePaymentRequest capturePayment if (paymentMethod == null) throw new SmartException(T("Payment.CouldNotLoadMethod")); - try - { - return paymentMethod.Value.Capture(capturePaymentRequest); - } - catch (NotSupportedException) - { - var result = new CapturePaymentResult(); - result.AddError(T("Common.Payment.NoCaptureSupport")); - return result; - } - catch - { - throw; - } + try + { + return paymentMethod.Value.Capture(capturePaymentRequest); + } + catch (NotSupportedException) + { + var result = new CapturePaymentResult(); + result.AddError(T("Common.Payment.NoCaptureSupport")); + return result; + } + catch + { + throw; + } } @@ -450,7 +468,7 @@ public virtual bool SupportPartiallyRefund(string paymentMethodSystemName) var paymentMethod = LoadPaymentMethodBySystemName(paymentMethodSystemName); if (paymentMethod == null) return false; - return paymentMethod.Value.SupportPartiallyRefund; + return paymentMethod.Value.SupportPartiallyRefund; } /// @@ -463,7 +481,7 @@ public virtual bool SupportRefund(string paymentMethodSystemName) var paymentMethod = LoadPaymentMethodBySystemName(paymentMethodSystemName); if (paymentMethod == null) return false; - return paymentMethod.Value.SupportRefund; + return paymentMethod.Value.SupportRefund; } /// @@ -477,20 +495,20 @@ public virtual RefundPaymentResult Refund(RefundPaymentRequest refundPaymentRequ if (paymentMethod == null) throw new SmartException(T("Payment.CouldNotLoadMethod")); - try - { - return paymentMethod.Value.Refund(refundPaymentRequest); - } - catch (NotSupportedException) - { - var result = new RefundPaymentResult(); - result.AddError(T("Common.Payment.NoRefundSupport")); - return result; - } - catch - { - throw; - } + try + { + return paymentMethod.Value.Refund(refundPaymentRequest); + } + catch (NotSupportedException) + { + var result = new RefundPaymentResult(); + result.AddError(T("Common.Payment.NoRefundSupport")); + return result; + } + catch + { + throw; + } } @@ -505,7 +523,7 @@ public virtual bool SupportVoid(string paymentMethodSystemName) var paymentMethod = LoadPaymentMethodBySystemName(paymentMethodSystemName); if (paymentMethod == null) return false; - return paymentMethod.Value.SupportVoid; + return paymentMethod.Value.SupportVoid; } /// @@ -519,20 +537,20 @@ public virtual VoidPaymentResult Void(VoidPaymentRequest voidPaymentRequest) if (paymentMethod == null) throw new SmartException(T("Payment.CouldNotLoadMethod")); - try - { - return paymentMethod.Value.Void(voidPaymentRequest); - } - catch (NotSupportedException) - { - var result = new VoidPaymentResult(); - result.AddError(T("Common.Payment.NoVoidSupport")); - return result; - } - catch - { - throw; - } + try + { + return paymentMethod.Value.Void(voidPaymentRequest); + } + catch (NotSupportedException) + { + var result = new VoidPaymentResult(); + result.AddError(T("Common.Payment.NoVoidSupport")); + return result; + } + catch + { + throw; + } } @@ -548,7 +566,7 @@ public virtual RecurringPaymentType GetRecurringPaymentType(string paymentMethod if (paymentMethod == null) return RecurringPaymentType.NotSupported; - return paymentMethod.Value.RecurringPaymentType; + return paymentMethod.Value.RecurringPaymentType; } /// @@ -572,20 +590,20 @@ public virtual ProcessPaymentResult ProcessRecurringPayment(ProcessPaymentReques if (paymentMethod == null) throw new SmartException(T("Payment.CouldNotLoadMethod")); - try - { - return paymentMethod.Value.ProcessRecurringPayment(processPaymentRequest); - } - catch (NotSupportedException) - { - var result = new ProcessPaymentResult(); - result.AddError(T("Common.Payment.NoRecurringPaymentSupport")); - return result; - } - catch - { - throw; - } + try + { + return paymentMethod.Value.ProcessRecurringPayment(processPaymentRequest); + } + catch (NotSupportedException) + { + var result = new ProcessPaymentResult(); + result.AddError(T("Common.Payment.NoRecurringPaymentSupport")); + return result; + } + catch + { + throw; + } } } @@ -603,20 +621,20 @@ public virtual CancelRecurringPaymentResult CancelRecurringPayment(CancelRecurri if (paymentMethod == null) throw new SmartException(T("Payment.CouldNotLoadMethod")); - try - { - return paymentMethod.Value.CancelRecurringPayment(cancelPaymentRequest); - } - catch (NotSupportedException) - { - var result = new CancelRecurringPaymentResult(); - result.AddError(T("Common.Payment.NoRecurringPaymentSupport")); - return result; - } - catch - { - throw; - } + try + { + return paymentMethod.Value.CancelRecurringPayment(cancelPaymentRequest); + } + catch (NotSupportedException) + { + var result = new CancelRecurringPaymentResult(); + result.AddError(T("Common.Payment.NoRecurringPaymentSupport")); + return result; + } + catch + { + throw; + } } @@ -632,7 +650,7 @@ public virtual PaymentMethodType GetPaymentMethodType(string paymentMethodSystem if (paymentMethod == null) return PaymentMethodType.Unknown; - return paymentMethod.Value.PaymentMethodType; + return paymentMethod.Value.PaymentMethodType; } /// @@ -657,26 +675,26 @@ public virtual string GetMaskedCreditCardNumber(string creditCardNumber) return maskedChars + last4; } - public virtual IList GetAllPaymentMethodFilters() - { - if (_paymentMethodFilterTypes == null) - { - lock (_lock) - { - if (_paymentMethodFilterTypes == null) - { - _paymentMethodFilterTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true).ToList(); - } - } - } - - var paymentMethodFilters = _paymentMethodFilterTypes - .Select(x => EngineContext.Current.ContainerManager.ResolveUnregistered(x) as IPaymentMethodFilter) - .ToList(); - - return paymentMethodFilters; - } - - #endregion - } + public virtual IList GetAllPaymentMethodFilters() + { + if (_paymentMethodFilterTypes == null) + { + lock (_lock) + { + if (_paymentMethodFilterTypes == null) + { + _paymentMethodFilterTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true).ToList(); + } + } + } + + var paymentMethodFilters = _paymentMethodFilterTypes + .Select(x => EngineContext.Current.ContainerManager.ResolveUnregistered(x) as IPaymentMethodFilter) + .ToList(); + + return paymentMethodFilters; + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/Payments/PostProcessPaymentRequest.cs b/src/Libraries/SmartStore.Services/Payments/PostProcessPaymentRequest.cs index f14a6cc6aa..45fff023f4 100644 --- a/src/Libraries/SmartStore.Services/Payments/PostProcessPaymentRequest.cs +++ b/src/Libraries/SmartStore.Services/Payments/PostProcessPaymentRequest.cs @@ -12,14 +12,14 @@ public partial class PostProcessPaymentRequest /// public Order Order { get; set; } - /// - /// Whether the customer clicked the button to re-post the payment process - /// - public bool IsRePostProcessPayment { get; set; } + /// + /// Whether the customer clicked the button to re-post the payment process + /// + public bool IsRePostProcessPayment { get; set; } - /// - /// URL to a payment provider to fulfill the payment. The .NET core will redirect to it. - /// - public string RedirectUrl { get; set; } + /// + /// URL to a payment provider to fulfill the payment. The .NET core will redirect to it. + /// + public string RedirectUrl { get; set; } } } diff --git a/src/Libraries/SmartStore.Services/Payments/PreProcessPaymentResult.cs b/src/Libraries/SmartStore.Services/Payments/PreProcessPaymentResult.cs index d59c10eb48..3dbf7b775f 100644 --- a/src/Libraries/SmartStore.Services/Payments/PreProcessPaymentResult.cs +++ b/src/Libraries/SmartStore.Services/Payments/PreProcessPaymentResult.cs @@ -1,10 +1,10 @@  namespace SmartStore.Services.Payments { - /// - /// Represents a pre process payment result - /// - public partial class PreProcessPaymentResult : ProcessPaymentResultBase - { - } + /// + /// Represents a pre process payment result + /// + public partial class PreProcessPaymentResult : ProcessPaymentResultBase + { + } } diff --git a/src/Libraries/SmartStore.Services/Payments/ProcessPaymentRequest.cs b/src/Libraries/SmartStore.Services/Payments/ProcessPaymentRequest.cs index 334a261ff0..e03d989008 100644 --- a/src/Libraries/SmartStore.Services/Payments/ProcessPaymentRequest.cs +++ b/src/Libraries/SmartStore.Services/Payments/ProcessPaymentRequest.cs @@ -11,17 +11,17 @@ namespace SmartStore.Services.Payments [Serializable] public partial class ProcessPaymentRequest { - public ProcessPaymentRequest() - { - CustomProperties = new Dictionary(); + public ProcessPaymentRequest() + { + CustomProperties = new Dictionary(); IsMultiOrder = false; ShoppingCartItemIds = new List(); } - /// - /// Gets or sets a store identifier - /// - public int StoreId { get; set; } + /// + /// Gets or sets a store identifier + /// + public int StoreId { get; set; } /// /// Gets or sets a customer @@ -38,10 +38,10 @@ public ProcessPaymentRequest() /// public decimal OrderTotal { get; set; } - /// - /// Gets or sets an order tax total - /// - public decimal OrderTax { get; set; } + /// + /// Gets or sets an order tax total + /// + public decimal OrderTax { get; set; } /// /// Gets or sets a payment method identifier @@ -53,10 +53,10 @@ public ProcessPaymentRequest() /// public bool IsMultiOrder { get; set; } - /// - /// Use that dictionary for any payment method or checkout flow specific data - /// - public Dictionary CustomProperties { get; set; } + /// + /// Use that dictionary for any payment method or checkout flow specific data + /// + public Dictionary CustomProperties { get; set; } public IList ShoppingCartItemIds { get; set; } @@ -112,16 +112,16 @@ public ProcessPaymentRequest() /// public string PurchaseOrderNumber { get; set; } - public int CreditCardStartYear { get; set; } - public int CreditCardStartMonth { get; set; } - public string CreditCardIssueNumber { get; set; } - public string DirectDebitAccountHolder { get; set; } - public string DirectDebitAccountNumber { get; set; } - public string DirectDebitBankCode { get; set; } - public string DirectDebitCountry { get; set; } - public string DirectDebitBankName { get; set; } - public string DirectDebitIban { get; set; } - public string DirectDebitBic { get; set; } + public int CreditCardStartYear { get; set; } + public int CreditCardStartMonth { get; set; } + public string CreditCardIssueNumber { get; set; } + public string DirectDebitAccountHolder { get; set; } + public string DirectDebitAccountNumber { get; set; } + public string DirectDebitBankCode { get; set; } + public string DirectDebitCountry { get; set; } + public string DirectDebitBankName { get; set; } + public string DirectDebitIban { get; set; } + public string DirectDebitBic { get; set; } public bool IsShippingMethodSet { get; set; } @@ -138,7 +138,7 @@ public ProcessPaymentRequest() /// Gets or sets an initial (parent) order identifier if order is recurring /// public int InitialOrderId { get; set; } - + /// /// Gets or sets the cycle length /// @@ -158,17 +158,17 @@ public ProcessPaymentRequest() } - [Serializable] - public partial class CustomPaymentRequestValue - { - /// - /// The value of the custom property - /// - public object Value { get; set; } + [Serializable] + public partial class CustomPaymentRequestValue + { + /// + /// The value of the custom property + /// + public object Value { get; set; } - /// - /// Indicates whether to automatically create a generic attribute if an order has been placed - /// - public bool AutoCreateGenericAttribute { get; set; } - } + /// + /// Indicates whether to automatically create a generic attribute if an order has been placed + /// + public bool AutoCreateGenericAttribute { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/Payments/ProcessPaymentResult.cs b/src/Libraries/SmartStore.Services/Payments/ProcessPaymentResult.cs index 28977963d6..64ef7ad40e 100644 --- a/src/Libraries/SmartStore.Services/Payments/ProcessPaymentResult.cs +++ b/src/Libraries/SmartStore.Services/Payments/ProcessPaymentResult.cs @@ -44,7 +44,7 @@ public partial class ProcessPaymentResult : ProcessPaymentResultBase /// Gets or sets the subscription transaction identifier /// public string SubscriptionTransactionId { get; set; } - + /// /// Gets or sets a value indicating whether storing of credit card number, CVV2 is allowed /// @@ -60,14 +60,8 @@ public partial class ProcessPaymentResult : ProcessPaymentResultBase /// public PaymentStatus NewPaymentStatus { - get - { - return _newPaymentStatus; - } - set - { - _newPaymentStatus = value; - } + get => _newPaymentStatus; + set => _newPaymentStatus = value; } } } diff --git a/src/Libraries/SmartStore.Services/Payments/ProcessPaymentResultBase.cs b/src/Libraries/SmartStore.Services/Payments/ProcessPaymentResultBase.cs index 7657c293cc..cbce0a3ea8 100644 --- a/src/Libraries/SmartStore.Services/Payments/ProcessPaymentResultBase.cs +++ b/src/Libraries/SmartStore.Services/Payments/ProcessPaymentResultBase.cs @@ -2,23 +2,20 @@ namespace SmartStore.Services.Payments { - public partial class ProcessPaymentResultBase - { - public ProcessPaymentResultBase() - { - this.Errors = new List(); - } + public partial class ProcessPaymentResultBase + { + public ProcessPaymentResultBase() + { + this.Errors = new List(); + } - public IList Errors { get; set; } + public IList Errors { get; set; } - public bool Success - { - get { return (this.Errors.Count == 0); } - } + public bool Success => (this.Errors.Count == 0); - public void AddError(string error) - { - this.Errors.Add(error); - } - } + public void AddError(string error) + { + this.Errors.Add(error); + } + } } diff --git a/src/Libraries/SmartStore.Services/Payments/RefundPaymentResult.cs b/src/Libraries/SmartStore.Services/Payments/RefundPaymentResult.cs index bda98cf05e..b880e6029c 100644 --- a/src/Libraries/SmartStore.Services/Payments/RefundPaymentResult.cs +++ b/src/Libraries/SmartStore.Services/Payments/RefundPaymentResult.cs @@ -11,17 +11,14 @@ public partial class RefundPaymentResult private PaymentStatus _newPaymentStatus = PaymentStatus.Pending; public IList Errors { get; set; } - public RefundPaymentResult() + public RefundPaymentResult() { this.Errors = new List(); } - public bool Success - { - get { return (this.Errors.Count == 0); } - } + public bool Success => (this.Errors.Count == 0); - public void AddError(string error) + public void AddError(string error) { this.Errors.Add(error); } @@ -33,14 +30,8 @@ public void AddError(string error) /// public PaymentStatus NewPaymentStatus { - get - { - return _newPaymentStatus; - } - set - { - _newPaymentStatus = value; - } + get => _newPaymentStatus; + set => _newPaymentStatus = value; } #endregion diff --git a/src/Libraries/SmartStore.Services/Payments/VoidPaymentResult.cs b/src/Libraries/SmartStore.Services/Payments/VoidPaymentResult.cs index 5b13365083..8ec3cba1d6 100644 --- a/src/Libraries/SmartStore.Services/Payments/VoidPaymentResult.cs +++ b/src/Libraries/SmartStore.Services/Payments/VoidPaymentResult.cs @@ -11,17 +11,14 @@ public partial class VoidPaymentResult private PaymentStatus _newPaymentStatus = PaymentStatus.Pending; public IList Errors { get; set; } - public VoidPaymentResult() + public VoidPaymentResult() { this.Errors = new List(); } - public bool Success - { - get { return (this.Errors.Count == 0); } - } + public bool Success => (this.Errors.Count == 0); - public void AddError(string error) + public void AddError(string error) { this.Errors.Add(error); } @@ -33,14 +30,8 @@ public void AddError(string error) /// public PaymentStatus NewPaymentStatus { - get - { - return _newPaymentStatus; - } - set - { - _newPaymentStatus = value; - } + get => _newPaymentStatus; + set => _newPaymentStatus = value; } #endregion diff --git a/src/Libraries/SmartStore.Services/Pdf/Content/IPdfContent.cs b/src/Libraries/SmartStore.Services/Pdf/Content/IPdfContent.cs index 9a1319426c..6f983e1afe 100644 --- a/src/Libraries/SmartStore.Services/Pdf/Content/IPdfContent.cs +++ b/src/Libraries/SmartStore.Services/Pdf/Content/IPdfContent.cs @@ -4,20 +4,20 @@ namespace SmartStore.Services.Pdf { - public interface IPdfContent - { - PdfContentKind Kind { get; } + public interface IPdfContent + { + PdfContentKind Kind { get; } - string Process(string flag); + string Process(string flag); - void WriteArguments(string flag, StringBuilder builder); + void WriteArguments(string flag, StringBuilder builder); - void Teardown(); - } + void Teardown(); + } - public enum PdfContentKind - { - Html, - Url - } + public enum PdfContentKind + { + Html, + Url + } } diff --git a/src/Libraries/SmartStore.Services/Pdf/Content/PdfHtmlContent.cs b/src/Libraries/SmartStore.Services/Pdf/Content/PdfHtmlContent.cs index 226e742928..493a48b521 100644 --- a/src/Libraries/SmartStore.Services/Pdf/Content/PdfHtmlContent.cs +++ b/src/Libraries/SmartStore.Services/Pdf/Content/PdfHtmlContent.cs @@ -9,107 +9,104 @@ namespace SmartStore.Services.Pdf { - public class PdfHtmlContent : IPdfContent - { - private static readonly Uri _engineBaseUri; - - private string _html; - private string _originalHtml; - private bool _processed; - private string _tempFilePath; - private PdfContentKind _kind = PdfContentKind.Html; - - static PdfHtmlContent() - { - var baseUrl = CommonHelper.GetAppSetting("sm:PdfEngineBaseUrl").TrimSafe().NullEmpty(); - if (baseUrl != null) - { - Uri uri; - if (Uri.TryCreate(baseUrl, UriKind.Absolute, out uri)) - { - _engineBaseUri = uri; - } - } - } - - public PdfHtmlContent(string html) - { - Guard.NotEmpty(html, nameof(html)); - - _originalHtml = html; - _html = html; - } - - public PdfContentKind Kind - { - get { return _kind; } - } - - public string Process(string flag) - { - if (!_processed) - { - if (_engineBaseUri != null) - { - _html = WebHelper.MakeAllUrlsAbsolute(_html, _engineBaseUri.Scheme, _engineBaseUri.Authority); - } - else if (HttpContext.Current?.Request != null) - { - _html = WebHelper.MakeAllUrlsAbsolute(_html, new HttpRequestWrapper(HttpContext.Current.Request)); - } - - if (!flag.IsCaseInsensitiveEqual("page")) - { - CreateTempFile(); - } - - _processed = true; - } - - return _tempFilePath ?? _html; - } - - private void CreateTempFile() - { - // TODO: (mc) This is a very weak mechanism to determine if html is partial. Find a better way! - bool isPartial = !_html.Trim().StartsWith("\r\n\r\n{0}\r\n", html); - } - - public void WriteArguments(string flag, StringBuilder builder) - { - // noop - } - - public void Teardown() - { - if (_tempFilePath != null && File.Exists(_tempFilePath)) - { - try - { - File.Delete(_tempFilePath); - } - catch { } - } - - _kind = PdfContentKind.Html; - _html = _originalHtml; - _tempFilePath = null; - _processed = false; - } - } + public class PdfHtmlContent : IPdfContent + { + private static readonly Uri _engineBaseUri; + + private string _html; + private string _originalHtml; + private bool _processed; + private string _tempFilePath; + private PdfContentKind _kind = PdfContentKind.Html; + + static PdfHtmlContent() + { + var baseUrl = CommonHelper.GetAppSetting("sm:PdfEngineBaseUrl").TrimSafe().NullEmpty(); + if (baseUrl != null) + { + Uri uri; + if (Uri.TryCreate(baseUrl, UriKind.Absolute, out uri)) + { + _engineBaseUri = uri; + } + } + } + + public PdfHtmlContent(string html) + { + Guard.NotEmpty(html, nameof(html)); + + _originalHtml = html; + _html = html; + } + + public PdfContentKind Kind => _kind; + + public string Process(string flag) + { + if (!_processed) + { + if (_engineBaseUri != null) + { + _html = WebHelper.MakeAllUrlsAbsolute(_html, _engineBaseUri.Scheme, _engineBaseUri.Authority); + } + else if (HttpContext.Current?.Request != null) + { + _html = WebHelper.MakeAllUrlsAbsolute(_html, new HttpRequestWrapper(HttpContext.Current.Request)); + } + + if (!flag.IsCaseInsensitiveEqual("page")) + { + CreateTempFile(); + } + + _processed = true; + } + + return _tempFilePath ?? _html; + } + + private void CreateTempFile() + { + // TODO: (mc) This is a very weak mechanism to determine if html is partial. Find a better way! + bool isPartial = !_html.Trim().StartsWith("\r\n\r\n{0}\r\n", html); + } + + public void WriteArguments(string flag, StringBuilder builder) + { + // noop + } + + public void Teardown() + { + if (_tempFilePath != null && File.Exists(_tempFilePath)) + { + try + { + File.Delete(_tempFilePath); + } + catch { } + } + + _kind = PdfContentKind.Html; + _html = _originalHtml; + _tempFilePath = null; + _processed = false; + } + } } diff --git a/src/Libraries/SmartStore.Services/Pdf/Content/PdfUrlContent.cs b/src/Libraries/SmartStore.Services/Pdf/Content/PdfUrlContent.cs index ecb19891bd..6065e91a84 100644 --- a/src/Libraries/SmartStore.Services/Pdf/Content/PdfUrlContent.cs +++ b/src/Libraries/SmartStore.Services/Pdf/Content/PdfUrlContent.cs @@ -10,147 +10,144 @@ namespace SmartStore.Services.Pdf { - public class PdfUrlContent : IPdfContent - { - private static readonly Uri _engineBaseUri; - private readonly string _url; - - static PdfUrlContent() - { - var baseUrl = CommonHelper.GetAppSetting("sm:PdfEngineBaseUrl").TrimSafe().NullEmpty(); - if (baseUrl != null) - { - Uri uri; - if (Uri.TryCreate(baseUrl, UriKind.Absolute, out uri)) - { - _engineBaseUri = uri; - } - } - } - - public PdfUrlContent(string url) - { - Guard.NotEmpty(url, nameof(url)); - _url = url; - - this.SendAuthCookie = false; - this.Post = new Dictionary(); - this.Cookies = new Dictionary(); - } - - #region Options - - /// - /// Send FormsAuthentication cookie to authorize - /// - public bool SendAuthCookie { get; set; } - - /// - /// HTTP Authentication username. - /// - public string UserName { get; set; } - - /// - /// HTTP Authentication password. - /// - public string Password { get; set; } - - /// - /// Sets cookies. - /// - public IDictionary Post { get; set; } - - /// - /// Sets post values. - /// - public IDictionary Cookies { get; set; } - - #endregion - - public PdfContentKind Kind - { - get { return PdfContentKind.Url; } - } - - protected virtual string GetAbsoluteUrl() - { - if (_engineBaseUri != null) - { - var url = _url; - if (url.StartsWith("~")) - { - url = VirtualPathUtility.ToAbsolute(url); - } - - return _engineBaseUri.ToString() + url.TrimStart('/'); - } - else if (HttpContext.Current?.Request != null) - { - return WebHelper.GetAbsoluteUrl(_url, new HttpRequestWrapper(HttpContext.Current.Request)); - } - - return _url; - } - - public string Process(string flag) - { - return GetAbsoluteUrl(); - } - - public void WriteArguments(string flag, StringBuilder builder) - { - if (UserName.HasValue()) - { - builder.AppendFormat(CultureInfo.InvariantCulture, " --username {0}", UserName); - } - - if (Password.HasValue()) - { - builder.AppendFormat(CultureInfo.InvariantCulture, " --password {0}", Password); - } - - if (Post != null && Post.Count > 0) - { - CreateRepeatableFlags("--post", Post, builder); - } - - if (Cookies != null && Cookies.Count > 0) - { - CreateRepeatableFlags("--cookie", Cookies, builder); - } - - // Send FormsAuthentication Cookie - var ctx = HttpContext.Current; - if (SendAuthCookie && ctx != null && ctx.Request != null && ctx.Request.Cookies != null) - { - var authCookieName = FormsAuthentication.FormsCookieName; - if (Cookies == null || !Cookies.ContainsKey(authCookieName)) - { - HttpCookie authCookie = null; - if (ctx.Request.Cookies.AllKeys.Contains(authCookieName)) - { - authCookie = ctx.Request.Cookies[authCookieName]; - } - if (authCookie != null) - { - var authCookieValue = authCookie.Value; - builder.AppendFormat(CultureInfo.InvariantCulture, " {0} {1} {2}", "--cookie", authCookieName, authCookieValue); - } - } - } - } - - private void CreateRepeatableFlags(string flagName, IDictionary dict, StringBuilder sb) - { - foreach (var kvp in dict) - { - sb.AppendFormat(CultureInfo.InvariantCulture, " {0} {1} {2}", flagName, kvp.Key, kvp.Value.EmptyNull()); - } - } - - public void Teardown() - { - // noop - } - } + public class PdfUrlContent : IPdfContent + { + private static readonly Uri _engineBaseUri; + private readonly string _url; + + static PdfUrlContent() + { + var baseUrl = CommonHelper.GetAppSetting("sm:PdfEngineBaseUrl").TrimSafe().NullEmpty(); + if (baseUrl != null) + { + Uri uri; + if (Uri.TryCreate(baseUrl, UriKind.Absolute, out uri)) + { + _engineBaseUri = uri; + } + } + } + + public PdfUrlContent(string url) + { + Guard.NotEmpty(url, nameof(url)); + _url = url; + + this.SendAuthCookie = false; + this.Post = new Dictionary(); + this.Cookies = new Dictionary(); + } + + #region Options + + /// + /// Send FormsAuthentication cookie to authorize + /// + public bool SendAuthCookie { get; set; } + + /// + /// HTTP Authentication username. + /// + public string UserName { get; set; } + + /// + /// HTTP Authentication password. + /// + public string Password { get; set; } + + /// + /// Sets cookies. + /// + public IDictionary Post { get; set; } + + /// + /// Sets post values. + /// + public IDictionary Cookies { get; set; } + + #endregion + + public PdfContentKind Kind => PdfContentKind.Url; + + protected virtual string GetAbsoluteUrl() + { + if (_engineBaseUri != null) + { + var url = _url; + if (url.StartsWith("~")) + { + url = VirtualPathUtility.ToAbsolute(url); + } + + return _engineBaseUri.ToString() + url.TrimStart('/'); + } + else if (HttpContext.Current?.Request != null) + { + return WebHelper.GetAbsoluteUrl(_url, new HttpRequestWrapper(HttpContext.Current.Request)); + } + + return _url; + } + + public string Process(string flag) + { + return GetAbsoluteUrl(); + } + + public void WriteArguments(string flag, StringBuilder builder) + { + if (UserName.HasValue()) + { + builder.AppendFormat(CultureInfo.InvariantCulture, " --username {0}", UserName); + } + + if (Password.HasValue()) + { + builder.AppendFormat(CultureInfo.InvariantCulture, " --password {0}", Password); + } + + if (Post != null && Post.Count > 0) + { + CreateRepeatableFlags("--post", Post, builder); + } + + if (Cookies != null && Cookies.Count > 0) + { + CreateRepeatableFlags("--cookie", Cookies, builder); + } + + // Send FormsAuthentication Cookie + var ctx = HttpContext.Current; + if (SendAuthCookie && ctx != null && ctx.Request != null && ctx.Request.Cookies != null) + { + var authCookieName = FormsAuthentication.FormsCookieName; + if (Cookies == null || !Cookies.ContainsKey(authCookieName)) + { + HttpCookie authCookie = null; + if (ctx.Request.Cookies.AllKeys.Contains(authCookieName)) + { + authCookie = ctx.Request.Cookies[authCookieName]; + } + if (authCookie != null) + { + var authCookieValue = authCookie.Value; + builder.AppendFormat(CultureInfo.InvariantCulture, " {0} {1} {2}", "--cookie", authCookieName, authCookieValue); + } + } + } + } + + private void CreateRepeatableFlags(string flagName, IDictionary dict, StringBuilder sb) + { + foreach (var kvp in dict) + { + sb.AppendFormat(CultureInfo.InvariantCulture, " {0} {1} {2}", flagName, kvp.Key, kvp.Value.EmptyNull()); + } + } + + public void Teardown() + { + // noop + } + } } diff --git a/src/Libraries/SmartStore.Services/Pdf/IPdfConverter.cs b/src/Libraries/SmartStore.Services/Pdf/IPdfConverter.cs index 69bcad9872..0812726e74 100644 --- a/src/Libraries/SmartStore.Services/Pdf/IPdfConverter.cs +++ b/src/Libraries/SmartStore.Services/Pdf/IPdfConverter.cs @@ -5,13 +5,13 @@ namespace SmartStore.Services.Pdf { - public interface IPdfConverter - { - /// - /// Converts html content to PDF - /// - /// The settings to be used for the conversion process - /// The PDF binary data - byte[] Convert(PdfConvertSettings settings); - } + public interface IPdfConverter + { + /// + /// Converts html content to PDF + /// + /// The settings to be used for the conversion process + /// The PDF binary data + byte[] Convert(PdfConvertSettings settings); + } } diff --git a/src/Libraries/SmartStore.Services/Pdf/Options/IPdfOptions.cs b/src/Libraries/SmartStore.Services/Pdf/Options/IPdfOptions.cs index 468a73ae6e..4f1f8e4c08 100644 --- a/src/Libraries/SmartStore.Services/Pdf/Options/IPdfOptions.cs +++ b/src/Libraries/SmartStore.Services/Pdf/Options/IPdfOptions.cs @@ -3,14 +3,14 @@ namespace SmartStore.Services.Pdf { - public interface IPdfOptions - { - /// - /// Processes the options by converting them to native arguments - /// - /// The section flag - /// The builder - /// Possible flags are: page | header | footer | cover | toc - void Process(string flag, StringBuilder builder); - } + public interface IPdfOptions + { + /// + /// Processes the options by converting them to native arguments + /// + /// The section flag + /// The builder + /// Possible flags are: page | header | footer | cover | toc + void Process(string flag, StringBuilder builder); + } } diff --git a/src/Libraries/SmartStore.Services/Pdf/Options/PdfHeaderFooterOptions.cs b/src/Libraries/SmartStore.Services/Pdf/Options/PdfHeaderFooterOptions.cs index 2456fd4743..49569a396b 100644 --- a/src/Libraries/SmartStore.Services/Pdf/Options/PdfHeaderFooterOptions.cs +++ b/src/Libraries/SmartStore.Services/Pdf/Options/PdfHeaderFooterOptions.cs @@ -5,92 +5,89 @@ namespace SmartStore.Services.Pdf { - public class PdfHeaderFooterOptions : IPdfOptions - { + public class PdfHeaderFooterOptions : IPdfOptions + { - public PdfHeaderFooterOptions() - { - Spacing = 5; - FontSize = 10; - } + public PdfHeaderFooterOptions() + { + Spacing = 5; + FontSize = 10; + } - /// - /// Spacing between footer and content in mm (default 0) - /// - public float? Spacing { get; set; } + /// + /// Spacing between footer and content in mm (default 0) + /// + public float? Spacing { get; set; } - /// - /// Display line below the header / above the footer - /// - public bool ShowLine { get; set; } + /// + /// Display line below the header / above the footer + /// + public bool ShowLine { get; set; } - /// - /// Set font name (default Arial) - /// - public string FontName { get; set; } + /// + /// Set font name (default Arial) + /// + public string FontName { get; set; } - /// - /// Set font size (default 12) - /// - public float? FontSize { get; set; } + /// + /// Set font size (default 12) + /// + public float? FontSize { get; set; } - /// - /// Left aligned text - /// - public string TextLeft { get; set; } + /// + /// Left aligned text + /// + public string TextLeft { get; set; } - /// - /// Centered text - /// - public string TextCenter { get; set; } + /// + /// Centered text + /// + public string TextCenter { get; set; } - /// - /// Right aligned text - /// - public string TextRight { get; set; } + /// + /// Right aligned text + /// + public string TextRight { get; set; } - public bool HasText - { - get { return TextLeft.HasValue() || TextCenter.HasValue() || TextRight.HasValue(); } - } - - public void Process(string flag, StringBuilder builder) - { - if (Spacing.HasValue) - { - builder.AppendFormat(CultureInfo.InvariantCulture, " --{0}-spacing {1}", flag, Spacing.Value); - } + public bool HasText => TextLeft.HasValue() || TextCenter.HasValue() || TextRight.HasValue(); - if (ShowLine) - { - builder.AppendFormat(" --{0}-line", flag); - } + public void Process(string flag, StringBuilder builder) + { + if (Spacing.HasValue) + { + builder.AppendFormat(CultureInfo.InvariantCulture, " --{0}-spacing {1}", flag, Spacing.Value); + } - if (HasText) - { - if (FontName.HasValue()) - { - builder.AppendFormat(CultureInfo.InvariantCulture, " --{0}-font-name \"{1}\"", flag, FontName); - } - if (FontSize.HasValue) - { - builder.AppendFormat(CultureInfo.InvariantCulture, " --{0}-font-size {1}", flag, FontSize.Value); - } - } + if (ShowLine) + { + builder.AppendFormat(" --{0}-line", flag); + } - if (TextLeft.HasValue()) - { - builder.AppendFormat(CultureInfo.CurrentCulture, " --{0}-left \"{1}\"", flag, TextLeft); - } - if (TextCenter.HasValue()) - { - builder.AppendFormat(CultureInfo.CurrentCulture, " --{0}-center \"{1}\"", flag, TextCenter); - } - if (TextRight.HasValue()) - { - builder.AppendFormat(CultureInfo.CurrentCulture, " --{0}-right \"{1}\"", flag, TextRight); - } - } + if (HasText) + { + if (FontName.HasValue()) + { + builder.AppendFormat(CultureInfo.InvariantCulture, " --{0}-font-name \"{1}\"", flag, FontName); + } + if (FontSize.HasValue) + { + builder.AppendFormat(CultureInfo.InvariantCulture, " --{0}-font-size {1}", flag, FontSize.Value); + } + } - } + if (TextLeft.HasValue()) + { + builder.AppendFormat(CultureInfo.CurrentCulture, " --{0}-left \"{1}\"", flag, TextLeft); + } + if (TextCenter.HasValue()) + { + builder.AppendFormat(CultureInfo.CurrentCulture, " --{0}-center \"{1}\"", flag, TextCenter); + } + if (TextRight.HasValue()) + { + builder.AppendFormat(CultureInfo.CurrentCulture, " --{0}-right \"{1}\"", flag, TextRight); + } + } + + } } diff --git a/src/Libraries/SmartStore.Services/Pdf/Options/PdfPageOptions.cs b/src/Libraries/SmartStore.Services/Pdf/Options/PdfPageOptions.cs index 2db0049016..f9c2d82d80 100644 --- a/src/Libraries/SmartStore.Services/Pdf/Options/PdfPageOptions.cs +++ b/src/Libraries/SmartStore.Services/Pdf/Options/PdfPageOptions.cs @@ -4,69 +4,69 @@ namespace SmartStore.Services.Pdf { - public class PdfPageOptions : IPdfOptions - { + public class PdfPageOptions : IPdfOptions + { - public PdfPageOptions() - { - this.Zoom = 1f; - this.UsePrintMediaType = true; - } + public PdfPageOptions() + { + this.Zoom = 1f; + this.UsePrintMediaType = true; + } - /// - /// Get or set zoom factor - /// - public float Zoom { get; set; } + /// + /// Get or set zoom factor + /// + public float Zoom { get; set; } - /// - /// Indicates whether the page background should be disabled. - /// - public bool BackgroundDisabled { get; set; } + /// + /// Indicates whether the page background should be disabled. + /// + public bool BackgroundDisabled { get; set; } - /// - /// Use print media-type instead of screen - /// - public bool UsePrintMediaType { get; set; } + /// + /// Use print media-type instead of screen + /// + public bool UsePrintMediaType { get; set; } - /// - /// Specifies a user style sheet to load with every page - /// - public string UserStylesheetUrl { get; set; } + /// + /// Specifies a user style sheet to load with every page + /// + public string UserStylesheetUrl { get; set; } - /// - /// Custom page pdf tool options - /// - public string CustomFlags { get; set; } + /// + /// Custom page pdf tool options + /// + public string CustomFlags { get; set; } - public virtual void Process(string flag, StringBuilder builder) - { - if (UserStylesheetUrl.HasValue()) - { - builder.AppendFormat(CultureInfo.InvariantCulture, " --user-style-sheet \"{0}\"", UserStylesheetUrl); - } + public virtual void Process(string flag, StringBuilder builder) + { + if (UserStylesheetUrl.HasValue()) + { + builder.AppendFormat(CultureInfo.InvariantCulture, " --user-style-sheet \"{0}\"", UserStylesheetUrl); + } - if (UsePrintMediaType) - { - builder.Append(" --print-media-type"); - } + if (UsePrintMediaType) + { + builder.Append(" --print-media-type"); + } - if (BackgroundDisabled) - { - builder.Append(" --no-background"); - } + if (BackgroundDisabled) + { + builder.Append(" --no-background"); + } - if (Zoom != 1 /*&& flag.IsCaseInsensitiveEqual("page")*/) - { - builder.AppendFormat(" --zoom {0}", Zoom); - } + if (Zoom != 1 /*&& flag.IsCaseInsensitiveEqual("page")*/) + { + builder.AppendFormat(" --zoom {0}", Zoom); + } - if (CustomFlags.HasValue()) - { - builder.AppendFormat(" {0}", CustomFlags); - } - } + if (CustomFlags.HasValue()) + { + builder.AppendFormat(" {0}", CustomFlags); + } + } - } + } } diff --git a/src/Libraries/SmartStore.Services/Pdf/Options/PdfTocOptions.cs b/src/Libraries/SmartStore.Services/Pdf/Options/PdfTocOptions.cs index 89692ca7e6..3b116de25b 100644 --- a/src/Libraries/SmartStore.Services/Pdf/Options/PdfTocOptions.cs +++ b/src/Libraries/SmartStore.Services/Pdf/Options/PdfTocOptions.cs @@ -4,75 +4,75 @@ namespace SmartStore.Services.Pdf { - public class PdfTocOptions : PdfPageOptions - { - public PdfTocOptions() - : base() - { - } - - /// - /// TOC creation enabled - /// - public bool Enabled { get; set; } - - /// - /// The header text of the toc (default Table of Contents) - /// - public string TocHeaderText { get; set; } - - /// - /// Do not use dotted lines in the toc - /// - public bool DisableDottedLines { get; set; } - - /// - /// Do not link from toc to sections - /// - public bool DisableTocLinks { get; set; } - - /// - /// For each level of headings in the toc indent by this length (default 1em) - /// - public string TocLevelIndendation { get; set; } - - /// - /// For each level of headings in the toc the font is scaled by this factor (default 0.8) - /// - public float? TocTextSizeShrink { get; set; } - - - public override void Process(string flag, StringBuilder builder) - { - builder.Append(" toc"); - - if (TocHeaderText.HasValue()) - { - builder.AppendFormat(" --toc-header-text \"{0}\"", TocHeaderText.Replace("\"", "\\\"")); - } - - if (DisableDottedLines) - { - builder.Append(" --disable-dotted-lines"); - } - - if (DisableTocLinks) - { - builder.Append(" --disable-toc-links"); - } - - if (TocLevelIndendation.HasValue()) - { - builder.AppendFormat(" --toc-level-indentation {0}", TocLevelIndendation); - } - - if (TocTextSizeShrink.HasValue) - { - builder.AppendFormat(" --toc-text-size-shrink {0}", TocTextSizeShrink.Value); - } - - base.Process(flag, builder); - } - - } + public class PdfTocOptions : PdfPageOptions + { + public PdfTocOptions() + : base() + { + } + + /// + /// TOC creation enabled + /// + public bool Enabled { get; set; } + + /// + /// The header text of the toc (default Table of Contents) + /// + public string TocHeaderText { get; set; } + + /// + /// Do not use dotted lines in the toc + /// + public bool DisableDottedLines { get; set; } + + /// + /// Do not link from toc to sections + /// + public bool DisableTocLinks { get; set; } + + /// + /// For each level of headings in the toc indent by this length (default 1em) + /// + public string TocLevelIndendation { get; set; } + + /// + /// For each level of headings in the toc the font is scaled by this factor (default 0.8) + /// + public float? TocTextSizeShrink { get; set; } + + + public override void Process(string flag, StringBuilder builder) + { + builder.Append(" toc"); + + if (TocHeaderText.HasValue()) + { + builder.AppendFormat(" --toc-header-text \"{0}\"", TocHeaderText.Replace("\"", "\\\"")); + } + + if (DisableDottedLines) + { + builder.Append(" --disable-dotted-lines"); + } + + if (DisableTocLinks) + { + builder.Append(" --disable-toc-links"); + } + + if (TocLevelIndendation.HasValue()) + { + builder.AppendFormat(" --toc-level-indentation {0}", TocLevelIndendation); + } + + if (TocTextSizeShrink.HasValue) + { + builder.AppendFormat(" --toc-text-size-shrink {0}", TocTextSizeShrink.Value); + } + + base.Process(flag, builder); + } + + } } diff --git a/src/Libraries/SmartStore.Services/Pdf/PdfConvertSettings.cs b/src/Libraries/SmartStore.Services/Pdf/PdfConvertSettings.cs index 8aa22eb59e..c099e3ddb4 100644 --- a/src/Libraries/SmartStore.Services/Pdf/PdfConvertSettings.cs +++ b/src/Libraries/SmartStore.Services/Pdf/PdfConvertSettings.cs @@ -2,110 +2,110 @@ namespace SmartStore.Services.Pdf { - public class PdfConvertSettings - { - public PdfConvertSettings() - { - this.Margins = new PdfPageMargins(); - this.Orientation = PdfPagePrientation.Default; - this.Size = PdfPageSize.Default; - this.PageOptions = new PdfPageOptions(); - this.HeaderOptions = new PdfHeaderFooterOptions(); - this.FooterOptions = new PdfHeaderFooterOptions(); - this.CoverOptions = new PdfPageOptions(); - this.TocOptions = new PdfTocOptions(); - } - - /// - /// The title of the generated pdf file (The title of the first document is used if not specified) - /// - public string Title { get; set; } - - /// - /// Get or set option to generate grayscale PDF - /// - public bool Grayscale { get; set; } - - /// - /// Get or set option to generate low quality PDF (shrink the result document space) - /// - public bool LowQuality { get; set; } - - /// - /// Get or set PDF page margins (in mm) - /// - public PdfPageMargins Margins { get; set; } - - /// - /// Get or set PDF page orientation - /// - public PdfPagePrientation Orientation { get; set; } - - /// - /// Get or set PDF page width (in mm) - /// - public float? PageWidth { get; set; } - - /// - /// Get or set PDF page height (in mm) - /// - public float? PageHeight { get; set; } - - /// - /// Get or set PDF page orientation - /// - public PdfPageSize Size { get; set; } - - /// - /// Custom global pdf tool options - /// - public string CustomFlags { get; set; } - - - - /// - /// Cover content - /// - public IPdfContent Cover { get; set; } - - /// - /// Cover content options - /// - public PdfPageOptions CoverOptions { get; set; } - - /// - /// Toc (table of contents) options - /// - public PdfTocOptions TocOptions { get; set; } - - /// - /// Page content (required) - /// - public IPdfContent Page { get; set; } - - /// - /// Page content options - /// - public PdfPageOptions PageOptions { get; set; } - - /// - /// Footer content - /// - public IPdfContent Footer { get; set; } - - /// - /// Footer content options - /// - public PdfHeaderFooterOptions FooterOptions { get; set; } - - /// - /// Header content - /// - public IPdfContent Header { get; set; } - - /// - /// Header content options - /// - public PdfHeaderFooterOptions HeaderOptions { get; set; } - } + public class PdfConvertSettings + { + public PdfConvertSettings() + { + this.Margins = new PdfPageMargins(); + this.Orientation = PdfPagePrientation.Default; + this.Size = PdfPageSize.Default; + this.PageOptions = new PdfPageOptions(); + this.HeaderOptions = new PdfHeaderFooterOptions(); + this.FooterOptions = new PdfHeaderFooterOptions(); + this.CoverOptions = new PdfPageOptions(); + this.TocOptions = new PdfTocOptions(); + } + + /// + /// The title of the generated pdf file (The title of the first document is used if not specified) + /// + public string Title { get; set; } + + /// + /// Get or set option to generate grayscale PDF + /// + public bool Grayscale { get; set; } + + /// + /// Get or set option to generate low quality PDF (shrink the result document space) + /// + public bool LowQuality { get; set; } + + /// + /// Get or set PDF page margins (in mm) + /// + public PdfPageMargins Margins { get; set; } + + /// + /// Get or set PDF page orientation + /// + public PdfPagePrientation Orientation { get; set; } + + /// + /// Get or set PDF page width (in mm) + /// + public float? PageWidth { get; set; } + + /// + /// Get or set PDF page height (in mm) + /// + public float? PageHeight { get; set; } + + /// + /// Get or set PDF page orientation + /// + public PdfPageSize Size { get; set; } + + /// + /// Custom global pdf tool options + /// + public string CustomFlags { get; set; } + + + + /// + /// Cover content + /// + public IPdfContent Cover { get; set; } + + /// + /// Cover content options + /// + public PdfPageOptions CoverOptions { get; set; } + + /// + /// Toc (table of contents) options + /// + public PdfTocOptions TocOptions { get; set; } + + /// + /// Page content (required) + /// + public IPdfContent Page { get; set; } + + /// + /// Page content options + /// + public PdfPageOptions PageOptions { get; set; } + + /// + /// Footer content + /// + public IPdfContent Footer { get; set; } + + /// + /// Footer content options + /// + public PdfHeaderFooterOptions FooterOptions { get; set; } + + /// + /// Header content + /// + public IPdfContent Header { get; set; } + + /// + /// Header content options + /// + public PdfHeaderFooterOptions HeaderOptions { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/Pdf/PdfPageMargins.cs b/src/Libraries/SmartStore.Services/Pdf/PdfPageMargins.cs index a33aec6bbe..c49c723c56 100644 --- a/src/Libraries/SmartStore.Services/Pdf/PdfPageMargins.cs +++ b/src/Libraries/SmartStore.Services/Pdf/PdfPageMargins.cs @@ -2,16 +2,16 @@ namespace SmartStore.Services.Pdf { - public class PdfPageMargins - { - public PdfPageMargins() - { - Left = 20; - } - - public float? Bottom { get; set; } - public float? Left { get; set; } - public float? Right { get; set; } - public float? Top { get; set; } - } + public class PdfPageMargins + { + public PdfPageMargins() + { + Left = 20; + } + + public float? Bottom { get; set; } + public float? Left { get; set; } + public float? Right { get; set; } + public float? Top { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/Pdf/PdfPagePrientation.cs b/src/Libraries/SmartStore.Services/Pdf/PdfPagePrientation.cs index 6c46162167..3ea74faebb 100644 --- a/src/Libraries/SmartStore.Services/Pdf/PdfPagePrientation.cs +++ b/src/Libraries/SmartStore.Services/Pdf/PdfPagePrientation.cs @@ -2,10 +2,10 @@ namespace SmartStore.Services.Pdf { - public enum PdfPagePrientation - { - Default, - Landscape, - Portrait - } + public enum PdfPagePrientation + { + Default, + Landscape, + Portrait + } } diff --git a/src/Libraries/SmartStore.Services/Pdf/PdfPageSize.cs b/src/Libraries/SmartStore.Services/Pdf/PdfPageSize.cs index c6865b5b0e..670c8b4a9d 100644 --- a/src/Libraries/SmartStore.Services/Pdf/PdfPageSize.cs +++ b/src/Libraries/SmartStore.Services/Pdf/PdfPageSize.cs @@ -2,11 +2,11 @@ namespace SmartStore.Services.Pdf { - public enum PdfPageSize - { - Default, - A4, - A3, - Letter - } + public enum PdfPageSize + { + Default, + A4, + A3, + Letter + } } diff --git a/src/Libraries/SmartStore.Services/Pdf/WkHtmlToPdfConverter.cs b/src/Libraries/SmartStore.Services/Pdf/WkHtmlToPdfConverter.cs index 72181616b6..3b2c9e288d 100644 --- a/src/Libraries/SmartStore.Services/Pdf/WkHtmlToPdfConverter.cs +++ b/src/Libraries/SmartStore.Services/Pdf/WkHtmlToPdfConverter.cs @@ -7,157 +7,158 @@ namespace SmartStore.Services.Pdf { - public class WkHtmlToPdfConverter : IPdfConverter - { - public WkHtmlToPdfConverter() - { - Logger = NullLogger.Instance; - } - - public ILogger Logger { get; set; } - - - public byte[] Convert(PdfConvertSettings settings) - { - Guard.NotNull(settings, nameof(settings)); - if (settings.Page == null) - { - throw Error.InvalidOperation("The 'Page' property of the 'settings' argument cannot be null."); - } - - try - { - var converter = CreateWkConverter(settings); - - var input = settings.Page.Process("page"); - - if (settings.Page.Kind == PdfContentKind.Url) - { - Logger.DebugFormat("Generating PDF from URL '{0}'. CustomWkHtmlPageArgs: {1}", input, converter.CustomWkHtmlPageArgs); - return converter.GeneratePdfFromFile(input, null); - } - else - { - Logger.DebugFormat("Generating PDF from HTML. CustomWkHtmlPageArgs: {0}", converter.CustomWkHtmlPageArgs); - return converter.GeneratePdf(input, null); - } - } - catch (Exception ex) - { - Logger.Error(ex, "Html to Pdf conversion error"); - throw; - } - finally - { - TeardownContent(settings.Cover); - TeardownContent(settings.Footer); - TeardownContent(settings.Header); - TeardownContent(settings.Page); - } - } - - private void TeardownContent(IPdfContent content) - { - if (content != null) - content.Teardown(); - } - - internal HtmlToPdfConverter CreateWkConverter(PdfConvertSettings settings) - { - // Global options - var converter = new HtmlToPdfConverter - { - Grayscale = settings.Grayscale, - LowQuality = settings.LowQuality, - Orientation = (PageOrientation)(int)settings.Orientation, - PageHeight = settings.PageHeight, - PageWidth = settings.PageWidth, - Size = (PageSize)(int)settings.Size, - PdfToolPath = FileSystemHelper.TempDir("wkhtmltopdf") - }; - - if (settings.Margins != null) - { - converter.Margins.Bottom = settings.Margins.Bottom; - converter.Margins.Left = settings.Margins.Left; - converter.Margins.Right = settings.Margins.Right; - converter.Margins.Top = settings.Margins.Top; - } - - var sb = new StringBuilder(settings.CustomFlags); - - // doc title - if (settings.Title.HasValue()) - { - sb.AppendFormat(CultureInfo.CurrentCulture, " --title \"{0}\"", settings.Title); - } - - // Cover content & options - if (settings.Cover != null) - { - var path = settings.Cover.Process("cover"); - if (path.HasValue()) - { - sb.AppendFormat(" cover \"{0}\" ", path); - settings.Cover.WriteArguments("cover", sb); - if (settings.CoverOptions != null) - { - settings.CoverOptions.Process("cover", sb); - } - } - } - - // Toc options - if (settings.TocOptions != null && settings.TocOptions.Enabled) - { - settings.TocOptions.Process("toc", sb); - } - - // apply cover & toc - converter.CustomWkHtmlArgs = sb.ToString().Trim().NullEmpty(); - sb.Clear(); - - // Page options - if (settings.PageOptions != null) - { - settings.PageOptions.Process("page", sb); - } - - // Header content & options - if (settings.Header != null) - { - var path = settings.Header.Process("header"); - if (path.HasValue()) - { - sb.AppendFormat(" --header-html \"{0}\"", path); - settings.Header.WriteArguments("header", sb); - } - } - if (settings.HeaderOptions != null && (settings.Header != null || settings.HeaderOptions.HasText)) - { - settings.HeaderOptions.Process("header", sb); - } - - // Footer content & options - if (settings.Footer != null) - { - var path = settings.Footer.Process("footer"); - if (path.HasValue()) - { - sb.AppendFormat(" --footer-html \"{0}\"", path); - settings.Footer.WriteArguments("footer", sb); - } - } - if (settings.FooterOptions != null && (settings.Footer != null || settings.FooterOptions.HasText)) - { - settings.FooterOptions.Process("footer", sb); - } - - // apply settings - converter.CustomWkHtmlPageArgs = sb.ToString().Trim().NullEmpty(); - - return converter; - } - - } + public class WkHtmlToPdfConverter : IPdfConverter + { + public WkHtmlToPdfConverter() + { + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + + public byte[] Convert(PdfConvertSettings settings) + { + Guard.NotNull(settings, nameof(settings)); + if (settings.Page == null) + { + throw Error.InvalidOperation("The 'Page' property of the 'settings' argument cannot be null."); + } + + try + { + var converter = CreateWkConverter(settings); + + var input = settings.Page.Process("page"); + + if (settings.Page.Kind == PdfContentKind.Url) + { + Logger.DebugFormat("Generating PDF from URL '{0}'. CustomWkHtmlPageArgs: {1}", input, converter.CustomWkHtmlPageArgs); + return converter.GeneratePdfFromFile(input, null); + } + else + { + Logger.DebugFormat("Generating PDF from HTML. CustomWkHtmlPageArgs: {0}", converter.CustomWkHtmlPageArgs); + return converter.GeneratePdf(input, null); + } + } + catch (Exception ex) + { + Logger.Error(ex, "Html to Pdf conversion error"); + throw; + } + finally + { + TeardownContent(settings.Cover); + TeardownContent(settings.Footer); + TeardownContent(settings.Header); + TeardownContent(settings.Page); + } + } + + private void TeardownContent(IPdfContent content) + { + if (content != null) + content.Teardown(); + } + + internal HtmlToPdfConverter CreateWkConverter(PdfConvertSettings settings) + { + // Global options + var converter = new HtmlToPdfConverter + { + Grayscale = settings.Grayscale, + LowQuality = settings.LowQuality, + Orientation = (PageOrientation)(int)settings.Orientation, + PageHeight = settings.PageHeight, + PageWidth = settings.PageWidth, + Size = (PageSize)(int)settings.Size, + PdfToolPath = FileSystemHelper.TempDir("wkhtmltopdf"), + TempFilesPath = FileSystemHelper.TempDirTenant() + }; + + if (settings.Margins != null) + { + converter.Margins.Bottom = settings.Margins.Bottom; + converter.Margins.Left = settings.Margins.Left; + converter.Margins.Right = settings.Margins.Right; + converter.Margins.Top = settings.Margins.Top; + } + + var sb = new StringBuilder(settings.CustomFlags); + + // doc title + if (settings.Title.HasValue()) + { + sb.AppendFormat(CultureInfo.CurrentCulture, " --title \"{0}\"", settings.Title); + } + + // Cover content & options + if (settings.Cover != null) + { + var path = settings.Cover.Process("cover"); + if (path.HasValue()) + { + sb.AppendFormat(" cover \"{0}\" ", path); + settings.Cover.WriteArguments("cover", sb); + if (settings.CoverOptions != null) + { + settings.CoverOptions.Process("cover", sb); + } + } + } + + // Toc options + if (settings.TocOptions != null && settings.TocOptions.Enabled) + { + settings.TocOptions.Process("toc", sb); + } + + // apply cover & toc + converter.CustomWkHtmlArgs = sb.ToString().Trim().NullEmpty(); + sb.Clear(); + + // Page options + if (settings.PageOptions != null) + { + settings.PageOptions.Process("page", sb); + } + + // Header content & options + if (settings.Header != null) + { + var path = settings.Header.Process("header"); + if (path.HasValue()) + { + sb.AppendFormat(" --header-html \"{0}\"", path); + settings.Header.WriteArguments("header", sb); + } + } + if (settings.HeaderOptions != null && (settings.Header != null || settings.HeaderOptions.HasText)) + { + settings.HeaderOptions.Process("header", sb); + } + + // Footer content & options + if (settings.Footer != null) + { + var path = settings.Footer.Process("footer"); + if (path.HasValue()) + { + sb.AppendFormat(" --footer-html \"{0}\"", path); + settings.Footer.WriteArguments("footer", sb); + } + } + if (settings.FooterOptions != null && (settings.Footer != null || settings.FooterOptions.HasText)) + { + settings.FooterOptions.Process("footer", sb); + } + + // apply settings + converter.CustomWkHtmlPageArgs = sb.ToString().Trim().NullEmpty(); + + return converter; + } + + } } diff --git a/src/Libraries/SmartStore.Services/Polls/IPollService.cs b/src/Libraries/SmartStore.Services/Polls/IPollService.cs index bdf19bab73..2aaf474e8c 100644 --- a/src/Libraries/SmartStore.Services/Polls/IPollService.cs +++ b/src/Libraries/SmartStore.Services/Polls/IPollService.cs @@ -22,7 +22,7 @@ public partial interface IPollService /// A value indicating whether to show hidden records /// Poll Poll GetPollBySystemKeyword(string systemKeyword, int languageId, bool showHidden = false); - + /// /// Gets poll collection /// @@ -51,14 +51,14 @@ public partial interface IPollService /// /// Poll void UpdatePoll(Poll poll); - + /// /// Gets a poll answer /// /// Poll answer identifier /// Poll answer PollAnswer GetPollAnswerById(int pollAnswerId); - + /// /// Deletes a poll answer /// diff --git a/src/Libraries/SmartStore.Services/Polls/PollService.cs b/src/Libraries/SmartStore.Services/Polls/PollService.cs index e12cb2f6af..3c06bebc46 100644 --- a/src/Libraries/SmartStore.Services/Polls/PollService.cs +++ b/src/Libraries/SmartStore.Services/Polls/PollService.cs @@ -15,55 +15,55 @@ public partial class PollService : IPollService private readonly IRepository _pollRepository; private readonly IRepository _pollAnswerRepository; private readonly IRepository _pollVotingRecords; - private readonly IRepository _storeMappingRepository; + private readonly IRepository _storeMappingRepository; private readonly IEventPublisher _eventPublisher; - private readonly IStoreContext _storeContext; + private readonly IStoreContext _storeContext; public PollService( - IRepository pollRepository, + IRepository pollRepository, IRepository pollAnswerRepository, IRepository pollVotingRecords, - IRepository storeMappingRepository, - IEventPublisher eventPublisher, - IStoreContext storeContext) + IRepository storeMappingRepository, + IEventPublisher eventPublisher, + IStoreContext storeContext) { this._pollRepository = pollRepository; this._pollAnswerRepository = pollAnswerRepository; this._pollVotingRecords = pollVotingRecords; - this._storeMappingRepository = storeMappingRepository; + this._storeMappingRepository = storeMappingRepository; this._eventPublisher = eventPublisher; - this._storeContext = storeContext; + this._storeContext = storeContext; - this.QuerySettings = DbQuerySettings.Default; + this.QuerySettings = DbQuerySettings.Default; } - public DbQuerySettings QuerySettings { get; set; } - - private IQueryable Filter(IQueryable query, bool showHidden) - { - if (!showHidden) - { - var utcNow = DateTime.UtcNow; - query = query.Where(p => p.Published); - query = query.Where(p => !p.StartDateUtc.HasValue || p.StartDateUtc <= utcNow); - query = query.Where(p => !p.EndDateUtc.HasValue || p.EndDateUtc >= utcNow); - - if (!QuerySettings.IgnoreMultiStore) - { - var currentStoreId = _storeContext.CurrentStore.Id; - - query = - from p in query - join sm in _storeMappingRepository.Table - on new { c1 = p.Id, c2 = "Poll" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into p_sm - from sm in p_sm.DefaultIfEmpty() - where !p.LimitedToStores || currentStoreId == sm.StoreId - select p; - } - } - - return query; - } + public DbQuerySettings QuerySettings { get; set; } + + private IQueryable Filter(IQueryable query, bool showHidden) + { + if (!showHidden) + { + var utcNow = DateTime.UtcNow; + query = query.Where(p => p.Published); + query = query.Where(p => !p.StartDateUtc.HasValue || p.StartDateUtc <= utcNow); + query = query.Where(p => !p.EndDateUtc.HasValue || p.EndDateUtc >= utcNow); + + if (!QuerySettings.IgnoreMultiStore) + { + var currentStoreId = _storeContext.CurrentStore.Id; + + query = + from p in query + join sm in _storeMappingRepository.Table + on new { c1 = p.Id, c2 = "Poll" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into p_sm + from sm in p_sm.DefaultIfEmpty() + where !p.LimitedToStores || currentStoreId == sm.StoreId + select p; + } + } + + return query; + } public virtual Poll GetPollById(int pollId) { @@ -73,38 +73,38 @@ public virtual Poll GetPollById(int pollId) return _pollRepository.GetById(pollId); } - public virtual Poll GetPollBySystemKeyword(string systemKeyword, int languageId, bool showHidden = false) + public virtual Poll GetPollBySystemKeyword(string systemKeyword, int languageId, bool showHidden = false) { if (String.IsNullOrWhiteSpace(systemKeyword)) return null; - var query = - from p in _pollRepository.Table - where p.SystemKeyword == systemKeyword && p.LanguageId == languageId - select p; + var query = + from p in _pollRepository.Table + where p.SystemKeyword == systemKeyword && p.LanguageId == languageId + select p; - query = Filter(query, showHidden); + query = Filter(query, showHidden); var poll = query.FirstOrDefault(); return poll; } - + public virtual IPagedList GetPolls(int languageId, bool loadShownOnHomePageOnly, int pageIndex, int pageSize, bool showHidden = false) { var query = _pollRepository.Table; - query = Filter(query, showHidden); - - if (loadShownOnHomePageOnly) + query = Filter(query, showHidden); + + if (loadShownOnHomePageOnly) { query = query.Where(p => p.ShowOnHomePage); } - - if (languageId > 0) + + if (languageId > 0) { query = query.Where(p => p.LanguageId == languageId); } - - query = query.OrderBy(p => p.DisplayOrder); + + query = query.OrderBy(p => p.DisplayOrder); var polls = new PagedList(query, pageIndex, pageSize); return polls; @@ -133,7 +133,7 @@ public virtual void UpdatePoll(Poll poll) _pollRepository.Update(poll); } - + public virtual PollAnswer GetPollAnswerById(int pollAnswerId) { if (pollAnswerId == 0) @@ -145,7 +145,7 @@ public virtual PollAnswer GetPollAnswerById(int pollAnswerId) var pollAnswer = query.SingleOrDefault(); return pollAnswer; } - + public virtual void DeletePollAnswer(PollAnswer pollAnswer) { if (pollAnswer == null) diff --git a/src/Libraries/SmartStore.Services/Properties/Settings.Designer.cs b/src/Libraries/SmartStore.Services/Properties/Settings.Designer.cs index ee12dcdfeb..89246c6f2e 100644 --- a/src/Libraries/SmartStore.Services/Properties/Settings.Designer.cs +++ b/src/Libraries/SmartStore.Services/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace SmartStore.Services.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -27,19 +27,9 @@ public static Settings Default { [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.WebServiceUrl)] [global::System.Configuration.DefaultSettingValueAttribute("http://ec.europa.eu/taxation_customs/vies/services/checkVatService")] - public string SmartStore_Services_EuropaCheckVatService_checkVatService { + public string SmartStore_Services_EuropeCheckVatService_checkVatService { get { - return ((string)(this["SmartStore_Services_EuropaCheckVatService_checkVatService"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.WebServiceUrl)] - [global::System.Configuration.DefaultSettingValueAttribute("http://ec.europa.eu/taxation_customs/vies/services/checkVatService")] - public string Nop_Services_EuropaCheckVatService_checkVatService { - get { - return ((string)(this["Nop_Services_EuropaCheckVatService_checkVatService"])); + return ((string)(this["SmartStore_Services_EuropeCheckVatService_checkVatService"])); } } } diff --git a/src/Libraries/SmartStore.Services/Properties/Settings.settings b/src/Libraries/SmartStore.Services/Properties/Settings.settings index ad884b468b..47fd2b1df3 100644 --- a/src/Libraries/SmartStore.Services/Properties/Settings.settings +++ b/src/Libraries/SmartStore.Services/Properties/Settings.settings @@ -2,10 +2,7 @@ - - http://ec.europa.eu/taxation_customs/vies/services/checkVatService - - + http://ec.europa.eu/taxation_customs/vies/services/checkVatService diff --git a/src/Libraries/SmartStore.Services/Rules/DefaultRuleOptionsProvider.cs b/src/Libraries/SmartStore.Services/Rules/DefaultRuleOptionsProvider.cs new file mode 100644 index 0000000000..dda9ef39d8 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Rules/DefaultRuleOptionsProvider.cs @@ -0,0 +1,434 @@ +using System; +using System.Collections.Generic; +using System.Data.Entity; +using System.Globalization; +using System.Linq; +using SmartStore.Core; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Localization; +using SmartStore.Core.Plugins; +using SmartStore.Core.Search; +using SmartStore.Rules; +using SmartStore.Services.Catalog; +using SmartStore.Services.Customers; +using SmartStore.Services.Directory; +using SmartStore.Services.Localization; +using SmartStore.Services.Payments; +using SmartStore.Services.Search; +using SmartStore.Services.Shipping; + +namespace SmartStore.Services.Rules +{ + public class DefaultRuleOptionsProvider : IRuleOptionsProvider + { + protected readonly ICommonServices _services; + protected readonly Lazy> _attrOptionRepository; + protected readonly Lazy> _variantValueRepository; + protected readonly Lazy _currencyService; + protected readonly Lazy _customerService; + protected readonly Lazy _languageService; + protected readonly Lazy _countryService; + protected readonly Lazy _deliveryTimeService; + protected readonly Lazy _catalogSearchService; + protected readonly Lazy _productService; + protected readonly Lazy _productTagService; + protected readonly Lazy _categoryService; + protected readonly Lazy _manufacturerService; + protected readonly Lazy _shippingService; + protected readonly Lazy _ruleStorage; + protected readonly Lazy _providerManager; + protected readonly Lazy _searchSettings; + + public DefaultRuleOptionsProvider( + ICommonServices services, + Lazy> attrOptionRepository, + Lazy> variantValueRepository, + Lazy currencyService, + Lazy customerService, + Lazy languageService, + Lazy countryService, + Lazy deliveryTimeService, + Lazy catalogSearchService, + Lazy productService, + Lazy productTagService, + Lazy categoryService, + Lazy manufacturerService, + Lazy shippingService, + Lazy ruleStorage, + Lazy providerManager, + Lazy searchSettings) + { + _services = services; + _attrOptionRepository = attrOptionRepository; + _variantValueRepository = variantValueRepository; + _currencyService = currencyService; + _customerService = customerService; + _languageService = languageService; + _countryService = countryService; + _deliveryTimeService = deliveryTimeService; + _catalogSearchService = catalogSearchService; + _productService = productService; + _productTagService = productTagService; + _categoryService = categoryService; + _manufacturerService = manufacturerService; + _shippingService = shippingService; + _ruleStorage = ruleStorage; + _providerManager = providerManager; + _searchSettings = searchSettings; + } + + public virtual bool Matches(string dataSource) + { + switch (dataSource.EmptyNull()) + { + case "CartRule": + case "Category": + case "Country": + case "Currency": + case "DeliveryTime": + case "CustomerRole": + case "Language": + case "Manufacturer": + case "PaymentMethod": + case "Product": + case "ProductTag": + case "ShippingMethod": + case "ShippingRateComputationMethod": + case "TargetGroup": + case "VariantValue": + case "AttributeOption": + return true; + default: + return false; + } + } + + public virtual RuleOptionsResult GetOptions( + RuleOptionsRequestReason reason, + IRuleExpression expression, + int pageIndex, + int pageSize, + string searchTerm) + { + Guard.NotNull(expression, nameof(expression)); + + return GetOptions(reason, expression.Descriptor, expression.RawValue, pageIndex, pageSize, searchTerm); + } + + public virtual RuleOptionsResult GetOptions( + RuleOptionsRequestReason reason, + RuleDescriptor descriptor, + string value, + int pageIndex, + int pageSize, + string searchTerm) + { + Guard.NotNull(descriptor, nameof(descriptor)); + + var result = new RuleOptionsResult(); + + if (!(descriptor.SelectList is RemoteRuleValueSelectList list)) + { + return result; + } + + var language = _services.WorkContext.WorkingLanguage; + var byId = descriptor.RuleType == RuleType.Int || descriptor.RuleType == RuleType.IntArray; + List options = null; + + switch (list.DataSource) + { + case "Product": + if (reason == RuleOptionsRequestReason.SelectedDisplayNames) + { + options = _productService.Value.GetProductsByIds(value.ToIntArray()) + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.GetLocalized(y => y.Name, language, true, false), Hint = x.Sku }) + .ToList(); + } + else + { + result.IsPaged = true; + options = SearchProducts(result, searchTerm, pageIndex * pageSize, pageSize); + } + break; + case "Country": + options = _countryService.Value.GetAllCountries(true) + .Select(x => new RuleValueSelectListOption { Value = byId ? x.Id.ToString() : x.TwoLetterIsoCode, Text = x.GetLocalized(y => y.Name, language, true, false) }) + .ToList(); + break; + case "Currency": + options = _currencyService.Value.GetAllCurrencies(true) + .Select(x => new RuleValueSelectListOption { Value = byId ? x.Id.ToString() : x.CurrencyCode, Text = x.GetLocalized(y => y.Name, language, true, false) }) + .ToList(); + break; + case "DeliveryTime": + options = _deliveryTimeService.Value.GetAllDeliveryTimes() + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.GetLocalized(y => y.Name, language, true, false) }) + .ToList(); + break; + case "CustomerRole": + options = _customerService.Value.GetAllCustomerRoles(true) + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.Name }) + .ToList(); + break; + case "Language": + options = _languageService.Value.GetAllLanguages(true) + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = GetCultureDisplayName(x) ?? x.Name }) + .ToList(); + break; + case "Store": + options = _services.StoreService.GetAllStores() + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.Name }) + .ToList(); + break; + case "CartRule": + case "TargetGroup": + if (reason == RuleOptionsRequestReason.SelectedDisplayNames) + { + options = _ruleStorage.Value.GetRuleSetsByIds(value.ToIntArray(), false) + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.Name }) + .ToList(); + } + else + { + var ruleSets = _ruleStorage.Value.GetAllRuleSets(false, false, descriptor.Scope, pageIndex, pageSize, false, true); + result.IsPaged = true; + result.HasMoreData = ruleSets.HasNextPage; + + options = ruleSets + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.Name }) + .ToList(); + } + break; + case "Category": + if (reason == RuleOptionsRequestReason.SelectedDisplayNames) + { + options = _categoryService.Value.GetCategoriesByIds(value.ToIntArray()) + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.GetCategoryPath(_categoryService.Value).NullEmpty() ?? x.Name }) + .ToList(); + } + else + { + var categories = _categoryService.Value.GetCategoryTree(0, true).Flatten(false); + options = categories + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.GetCategoryPath(_categoryService.Value).NullEmpty() ?? x.Name }) + .ToList(); + } + break; + case "Manufacturer": + options = _manufacturerService.Value.GetAllManufacturers(true) + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.GetLocalized(y => y.Name, language, true, false) }) + .ToList(); + break; + case "PaymentMethod": + options = _providerManager.Value.GetAllProviders() + .Select(x => x.Metadata) + .Select(x => new RuleValueSelectListOption { Value = x.SystemName, Text = GetLocalized(x, "FriendlyName") ?? x.FriendlyName.NullEmpty() ?? x.SystemName, Hint = x.SystemName }) + .OrderBy(x => x.Text) + .ToList(); + break; + case "ShippingRateComputationMethod": + options = _providerManager.Value.GetAllProviders() + .Select(x => x.Metadata) + .Select(x => new RuleValueSelectListOption { Value = x.SystemName, Text = GetLocalized(x, "FriendlyName") ?? x.FriendlyName.NullEmpty() ?? x.SystemName, Hint = x.SystemName }) + .OrderBy(x => x.Text) + .ToList(); + break; + case "ShippingMethod": + options = _shippingService.Value.GetAllShippingMethods() + .Select(x => new RuleValueSelectListOption { Value = byId ? x.Id.ToString() : x.Name, Text = byId ? x.GetLocalized(y => y.Name, language, true, false) : x.Name }) + .ToList(); + break; + case "ProductTag": + options = _productTagService.Value.GetAllProductTags(true) + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.GetLocalized(y => y.Name, language, true, false) }) + .OrderBy(x => x.Text) + .ToList(); + break; + case "VariantValue": + if (reason == RuleOptionsRequestReason.SelectedDisplayNames) + { + var ids = value.ToIntArray(); + var variantValues = _variantValueRepository.Value.TableUntracked + .Where(x => ids.Contains(x.Id)) + .ToList(); + + options = variantValues + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.GetLocalized(y => y.Name, language, true, false) }) + .ToList(); + } + else if (descriptor.Metadata.TryGetValue("ParentId", out var objParentId)) + { + options = new List(); + var pIndex = -1; + var existingValues = new HashSet(StringComparer.InvariantCultureIgnoreCase); + var multiValueTypeIds = new int[] { (int)AttributeControlType.Checkboxes, (int)AttributeControlType.RadioList, (int)AttributeControlType.DropdownList, (int)AttributeControlType.Boxes }; + var query = _variantValueRepository.Value.TableUntracked + .Where(x => + x.ProductVariantAttribute.ProductAttributeId == (int)objParentId && + x.ProductVariantAttribute.ProductAttribute.AllowFiltering && + multiValueTypeIds.Contains(x.ProductVariantAttribute.AttributeControlTypeId) && + x.ValueTypeId == (int)ProductVariantAttributeValueType.Simple + ) + .OrderBy(x => x.DisplayOrder); + + while (true) + { + var variantValues = PagedList.Create(query, ++pIndex, 500); + foreach (var variantValue in variantValues) + { + var name = variantValue.GetLocalized(x => x.Name, language, true, false); + if (!existingValues.Contains(name)) + { + existingValues.Add(name); + options.Add(new RuleValueSelectListOption { Value = variantValue.Id.ToString(), Text = name }); + } + } + if (!variantValues.HasNextPage) + { + break; + } + } + } + break; + case "AttributeOption": + if (reason == RuleOptionsRequestReason.SelectedDisplayNames) + { + var ids = value.ToIntArray(); + var attributeOptions = _attrOptionRepository.Value.TableUntracked + .Where(x => ids.Contains(x.Id)) + .ToList(); + + options = attributeOptions + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.GetLocalized(y => y.Name, language, true, false) }) + .ToList(); + } + else if (descriptor.Metadata.TryGetValue("ParentId", out var objParentId)) + { + var query = _attrOptionRepository.Value.TableUntracked + .Where(x => x.SpecificationAttributeId == (int)objParentId) + .OrderBy(x => x.DisplayOrder); + + var attributeOptions = PagedList.Create(query, pageIndex, pageSize); + + result.IsPaged = true; + result.HasMoreData = attributeOptions.HasNextPage; + + options = attributeOptions + .Select(x => new RuleValueSelectListOption { Value = x.Id.ToString(), Text = x.GetLocalized(y => y.Name, language, true, false) }) + .ToList(); + } + break; + default: + throw new SmartException($"Unknown data source \"{list.DataSource.NaIfEmpty()}\"."); + } + + if (options != null) + { + if (reason == RuleOptionsRequestReason.SelectedDisplayNames) + { + // Get display names of selected options. + if (value.HasValue()) + { + var selectedValues = value.SplitSafe(","); + result.Options.AddRange(options.Where(x => selectedValues.Contains(x.Value))); + } + } + else + { + // Get select list options. + if (!result.IsPaged && searchTerm.HasValue() && options.Any()) + { + // Apply the search term if the options are not paged. + result.Options.AddRange(options.Where(x => (x.Text?.IndexOf(searchTerm, 0, StringComparison.CurrentCultureIgnoreCase) ?? -1) != -1)); + } + else + { + result.Options.AddRange(options); + } + } + } + + return result; + } + + protected virtual string GetCultureDisplayName(Language language) + { + if (language?.LanguageCulture?.HasValue() ?? false) + { + try + { + return new CultureInfo(language.LanguageCulture).DisplayName; + } + catch { } + } + + return null; + } + + protected virtual string GetLocalized(ProviderMetadata metadata, string propertyName) + { + var resourceName = metadata.ResourceKeyPattern.FormatInvariant(metadata.SystemName, propertyName); + + return _services.Localization.GetResource(resourceName, _services.WorkContext.WorkingLanguage.Id, false, "", true).NullEmpty(); + } + + protected virtual List SearchProducts(RuleOptionsResult result, string term, int skip, int take) + { + List products; + var fields = new List { "name" }; + + if (_searchSettings.Value.SearchFields.Contains("sku")) + { + fields.Add("sku"); + } + if (_searchSettings.Value.SearchFields.Contains("shortdescription")) + { + fields.Add("shortdescription"); + } + + var searchQuery = new CatalogSearchQuery(fields.ToArray(), term); + + if (_searchSettings.Value.UseCatalogSearchInBackend) + { + searchQuery = searchQuery + .Slice(skip, take) + .SortBy(ProductSortingEnum.NameAsc); + + var searchResult = _catalogSearchService.Value.Search(searchQuery); + result.HasMoreData = searchResult.Hits.HasNextPage; + + products = searchResult.Hits + .Select(x => new RuleValueSelectListOption + { + Value = x.Id.ToString(), + Text = x.Name, + Hint = x.Sku + }) + .ToList(); + } + else + { + var query = _catalogSearchService.Value.PrepareQuery(searchQuery); + + var pageIndex = take == 0 ? 0 : Math.Max(skip / take, 0); + result.HasMoreData = (pageIndex + 1) * take < query.Count(); + + products = query + .Select(x => new RuleValueSelectListOption + { + Value = x.Id.ToString(), + Text = x.Name, + Hint = x.Sku + }) + .OrderBy(x => x.Text) + .Skip(() => skip) + .Take(() => take) + .ToList(); + } + + return products; + } + } +} diff --git a/src/Libraries/SmartStore.Services/ScopedServiceBase.cs b/src/Libraries/SmartStore.Services/ScopedServiceBase.cs index 961fa5cd6f..c75c0849f6 100644 --- a/src/Libraries/SmartStore.Services/ScopedServiceBase.cs +++ b/src/Libraries/SmartStore.Services/ScopedServiceBase.cs @@ -3,65 +3,65 @@ namespace SmartStore.Services { - public abstract class ScopedServiceBase : IScopedService - { - /// - /// Creates a long running unit of work in which cache eviction is suppressed - /// - /// Specifies whether the cache should be evicted completely on batch disposal - /// A disposable unit of work - public IDisposable BeginScope(bool clearCache = true) - { - if (IsInScope) - { - // nested batches are not supported - return ActionDisposable.Empty; - } + public abstract class ScopedServiceBase : IScopedService + { + /// + /// Creates a long running unit of work in which cache eviction is suppressed + /// + /// Specifies whether the cache should be evicted completely on batch disposal + /// A disposable unit of work + public IDisposable BeginScope(bool clearCache = true) + { + if (IsInScope) + { + // nested batches are not supported + return ActionDisposable.Empty; + } - OnBeginScope(); - IsInScope = true; + OnBeginScope(); + IsInScope = true; - return new ActionDisposable(() => - { - IsInScope = false; - OnEndScope(); - if (clearCache && HasChanges) - { - ClearCache(); - } - }); - } + return new ActionDisposable(() => + { + IsInScope = false; + OnEndScope(); + if (clearCache && HasChanges) + { + ClearCache(); + } + }); + } - /// - /// Gets a value indicating whether data has changed during a request, making cache eviction necessary. - /// - /// - /// Cache eviction sets this member to false - /// - public bool HasChanges - { - get; - protected set; - } + /// + /// Gets a value indicating whether data has changed during a request, making cache eviction necessary. + /// + /// + /// Cache eviction sets this member to false + /// + public bool HasChanges + { + get; + protected set; + } - protected bool IsInScope - { - get; - private set; - } + protected bool IsInScope + { + get; + private set; + } - public void ClearCache() - { - if (!IsInScope) - { - OnClearCache(); - HasChanges = false; - } - } + public void ClearCache() + { + if (!IsInScope) + { + OnClearCache(); + HasChanges = false; + } + } - protected abstract void OnClearCache(); + protected abstract void OnClearCache(); - protected virtual void OnBeginScope() { } - protected virtual void OnEndScope() { } - } + protected virtual void OnBeginScope() { } + protected virtual void OnEndScope() { } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchEvents.cs b/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchEvents.cs index abfc4f73b5..04d68d3a09 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchEvents.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchEvents.cs @@ -3,29 +3,31 @@ namespace SmartStore.Services.Search { public class CatalogSearchingEvent - { - public CatalogSearchingEvent(CatalogSearchQuery query) + { + public CatalogSearchingEvent(CatalogSearchQuery query, bool direct) { - Guard.NotNull(query, nameof(query)); + Guard.NotNull(query, nameof(query)); - Query = query; - } + Query = query; + Direct = direct; + } + public bool Direct { get; private set; } public CatalogSearchQuery Query { get; private set; } } - public class CatalogSearchedEvent - { - public CatalogSearchedEvent(CatalogSearchQuery query, CatalogSearchResult result) - { - Guard.NotNull(query, nameof(query)); - Guard.NotNull(result, nameof(result)); + public class CatalogSearchedEvent + { + public CatalogSearchedEvent(CatalogSearchQuery query, CatalogSearchResult result) + { + Guard.NotNull(query, nameof(query)); + Guard.NotNull(result, nameof(result)); - Query = query; - Result = result; - } + Query = query; + Result = result; + } - public CatalogSearchQuery Query { get; private set; } - public CatalogSearchResult Result { get; private set; } - } + public CatalogSearchQuery Query { get; private set; } + public CatalogSearchResult Result { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchQuery.cs b/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchQuery.cs index cfc21d3a9f..d19d862cc5 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchQuery.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchQuery.cs @@ -2,339 +2,460 @@ using System.Linq; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Customers; -using SmartStore.Core.Domain.Directory; using SmartStore.Core.Search; namespace SmartStore.Services.Search { - public partial class CatalogSearchQuery : SearchQuery, ICloneable - { - /// - /// Initializes a new instance of the class without a search term being set - /// - public CatalogSearchQuery() - : base((string[])null, null) - { - } - - public CatalogSearchQuery(string field, string term, SearchMode mode = SearchMode.Contains, bool escape = true, bool isFuzzySearch = false) - : base(field.HasValue() ? new[] { field } : null, term, mode, escape, isFuzzySearch) - { - } - - public CatalogSearchQuery(string[] fields, string term, SearchMode mode = SearchMode.Contains, bool escape = true, bool isFuzzySearch = false) - : base(fields, term, mode, escape, isFuzzySearch) - { - } - - public CatalogSearchQuery Clone() - { - return (CatalogSearchQuery)this.MemberwiseClone(); - } - - 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) - { - switch (sort) - { - case ProductSortingEnum.CreatedOnAsc: - case ProductSortingEnum.CreatedOn: - return SortBy(SearchSort.ByDateTimeField("createdon", sort == ProductSortingEnum.CreatedOn)); - - case ProductSortingEnum.NameAsc: - case ProductSortingEnum.NameDesc: - return SortBy(SearchSort.ByStringField("name", sort == ProductSortingEnum.NameDesc)); - - case ProductSortingEnum.PriceAsc: - case ProductSortingEnum.PriceDesc: - return SortBy(SearchSort.ByDoubleField("price", sort == ProductSortingEnum.PriceDesc)); - - case ProductSortingEnum.Relevance: - return SortBy(SearchSort.ByRelevance()); - - default: - return this; - } - } - - /// - /// Only products that are visible in frontend - /// - /// Customer whose customer roles should be checked, can be null - /// Catalog search query - public CatalogSearchQuery VisibleOnly(Customer customer) - { - if (customer != null) - { - var allowedCustomerRoleIds = customer.CustomerRoles.Where(x => x.Active).Select(x => x.Id).ToArray(); - - return VisibleOnly(allowedCustomerRoleIds); - } - - return VisibleOnly(new int[0]); - } - - /// - /// Only products that are visible in frontend - /// - /// Allowed customer role id, can be null - /// Catalog search query - public CatalogSearchQuery VisibleOnly(params int[] allowedCustomerRoleIds) - { - var utcNow = DateTime.UtcNow; - - PublishedOnly(true); - - WithFilter(SearchFilter.ByRange("availablestart", null, utcNow, false, false).Mandatory().NotAnalyzed()); - WithFilter(SearchFilter.ByRange("availableend", utcNow, null, false, false).Mandatory().NotAnalyzed()); - - if (allowedCustomerRoleIds != null && allowedCustomerRoleIds.Length > 0) - { - var roleIds = allowedCustomerRoleIds.Where(x => x != 0).Distinct().ToList(); - if (roleIds.Any()) - { - roleIds.Insert(0, 0); - WithFilter(SearchFilter.Combined(roleIds.Select(x => SearchFilter.ByField("roleid", x).ExactMatch().NotAnalyzed()).ToArray())); - } - } - - return this; - } - - public CatalogSearchQuery PublishedOnly(bool value) - { - return WithFilter(SearchFilter.ByField("published", value).Mandatory().ExactMatch().NotAnalyzed()); - } - - public CatalogSearchQuery VisibleIndividuallyOnly(bool value) - { - return WithFilter(SearchFilter.ByField("visibleindividually", value).Mandatory().ExactMatch().NotAnalyzed()); - } - - public CatalogSearchQuery HomePageProductsOnly(bool value) - { - return WithFilter(SearchFilter.ByField("showonhomepage", value).Mandatory().ExactMatch().NotAnalyzed()); - } - - public CatalogSearchQuery HasParentGroupedProduct(params int[] parentProductIds) - { - if (parentProductIds.Length == 0) - { - return this; - } - - return WithFilter(SearchFilter.Combined(parentProductIds.Select(x => SearchFilter.ByField("parentid", x).ExactMatch().NotAnalyzed()).ToArray())); - } - - public override CatalogSearchQuery HasStoreId(int id) - { - base.HasStoreId(id); - - if (id == 0) - { - WithFilter(SearchFilter.ByField("storeid", 0).ExactMatch().NotAnalyzed()); - } - else - { - WithFilter(SearchFilter.Combined( - SearchFilter.ByField("storeid", 0).ExactMatch().NotAnalyzed(), - SearchFilter.ByField("storeid", id).ExactMatch().NotAnalyzed()) - ); - } - - return this; - } - - public CatalogSearchQuery IsProductType(ProductType type) - { - return WithFilter(SearchFilter.ByField("typeid", (int)type).Mandatory().ExactMatch().NotAnalyzed()); - } - - public CatalogSearchQuery WithProductIds(params int[] ids) - { - if (ids.Length == 0) - { - return this; - } - - return WithFilter(SearchFilter.Combined(ids.Select(x => SearchFilter.ByField("id", x).ExactMatch().NotAnalyzed()).ToArray())); - } - - public CatalogSearchQuery WithProductId(int? fromId, int? toId) - { - if (fromId == null && toId == null) - { - return this; - } - - return WithFilter(SearchFilter.ByRange("id", fromId, toId, fromId.HasValue, toId.HasValue).Mandatory().ExactMatch().NotAnalyzed()); - } - - /// - /// Category ids filter - /// - /// - /// A value indicating whether loaded products are marked as featured (relates only to categories and manufacturers). - /// false to load featured products only, true to load unfeatured products only, null to load all products - /// - /// The category ids - /// Query - public CatalogSearchQuery WithCategoryIds(bool? featuredOnly, params int[] ids) - { - if (ids.Length == 0) - { - return this; - } - - var fieldName = featuredOnly.HasValue + public partial class CatalogSearchQuery : SearchQuery, ICloneable + { + /// + /// Initializes a new instance of the class without a search term being set + /// + public CatalogSearchQuery() + : base((string[])null, null) + { + } + + public CatalogSearchQuery(string field, string term, SearchMode mode = SearchMode.Contains, bool escape = true, bool isFuzzySearch = false) + : base(field.HasValue() ? new[] { field } : null, term, mode, escape, isFuzzySearch) + { + } + + public CatalogSearchQuery(string[] fields, string term, SearchMode mode = SearchMode.Contains, bool escape = true, bool isFuzzySearch = false) + : base(fields, term, mode, escape, isFuzzySearch) + { + } + + public CatalogSearchQuery Clone() + { + return (CatalogSearchQuery)this.MemberwiseClone(); + } + + 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) + { + switch (sort) + { + case ProductSortingEnum.CreatedOnAsc: + case ProductSortingEnum.CreatedOn: + return SortBy(SearchSort.ByDateTimeField("createdon", sort == ProductSortingEnum.CreatedOn)); + + case ProductSortingEnum.NameAsc: + case ProductSortingEnum.NameDesc: + return SortBy(SearchSort.ByStringField("name", sort == ProductSortingEnum.NameDesc)); + + case ProductSortingEnum.PriceAsc: + case ProductSortingEnum.PriceDesc: + return SortBy(SearchSort.ByDoubleField("price", sort == ProductSortingEnum.PriceDesc)); + + case ProductSortingEnum.Relevance: + return SortBy(SearchSort.ByRelevance()); + + default: + return this; + } + } + + /// + /// Only products that are visible in frontend. + /// + /// Customer whose customer roles are to be checked. Can be null. + /// Catalog search query + public CatalogSearchQuery VisibleOnly(Customer customer) + { + if (customer != null) + { + var allowedCustomerRoleIds = customer.GetRoleIds(); + + return VisibleOnly(allowedCustomerRoleIds); + } + + return VisibleOnly(new int[0]); + } + + /// + /// Only products that are visible in frontend. + /// + /// List of allowed customer role ids. Can be null. + public CatalogSearchQuery VisibleOnly(params int[] allowedCustomerRoleIds) + { + PublishedOnly(true); + AvailableByDate(true); + AllowedCustomerRoles(allowedCustomerRoleIds); + + return this; + } + + public CatalogSearchQuery AllowedCustomerRoles(params int[] customerRoleIds) + { + if (customerRoleIds != null && customerRoleIds.Any()) + { + var roleIds = customerRoleIds.Where(x => x != 0).Distinct().ToList(); + if (roleIds.Any()) + { + roleIds.Insert(0, 0); + return WithFilter(SearchFilter.Combined(roleIds.Select(x => SearchFilter.ByField("roleid", x).ExactMatch().NotAnalyzed()).ToArray())); + } + } + + return this; + } + + public CatalogSearchQuery PublishedOnly(bool value) + { + return WithFilter(SearchFilter.ByField("published", value).Mandatory().ExactMatch().NotAnalyzed()); + } + + /// + /// Filters products based on their stock level. + /// + public CatalogSearchQuery AvailableOnly(bool value) + { + return WithFilter(SearchFilter.ByField("available", value).Mandatory().ExactMatch().NotAnalyzed()); + } + + /// + /// Filters products by their availability date. + /// + public CatalogSearchQuery AvailableByDate(bool value) + { + var utcNow = DateTime.UtcNow; + + if (value) + { + WithFilter(SearchFilter.ByRange("availablestart", null, utcNow, false, false).Mandatory().NotAnalyzed()); + WithFilter(SearchFilter.ByRange("availableend", utcNow, null, false, false).Mandatory().NotAnalyzed()); + } + else + { + WithFilter(SearchFilter.ByRange("availablestart", utcNow, null, false, false).Mandatory().NotAnalyzed()); + WithFilter(SearchFilter.ByRange("availableend", null, utcNow, false, false).Mandatory().NotAnalyzed()); + } + + return this; + } + + public CatalogSearchQuery WithVisibility(ProductVisibility value) + { + return WithFilter(SearchFilter.ByField("visibility", (int)value).Mandatory().ExactMatch().NotAnalyzed()); + } + + public CatalogSearchQuery HasParentGroupedProduct(params int[] parentProductIds) + { + return CreateFilter("parentid", parentProductIds); + } + + public override CatalogSearchQuery HasStoreId(int id) + { + base.HasStoreId(id); + + if (id == 0) + { + // 0 is ignored in queries, i.e. no filtering takes place. + // This should be kept here so that search engines do not provide different results. + //WithFilter(SearchFilter.ByField("storeid", 0).ExactMatch().NotAnalyzed()); + } + else + { + WithFilter(SearchFilter.Combined( + SearchFilter.ByField("storeid", 0).ExactMatch().NotAnalyzed(), + SearchFilter.ByField("storeid", id).ExactMatch().NotAnalyzed()) + ); + } + + return this; + } + + public CatalogSearchQuery IsProductType(ProductType type) + { + return WithFilter(SearchFilter.ByField("typeid", (int)type).Mandatory().ExactMatch().NotAnalyzed()); + } + + public CatalogSearchQuery WithProductIds(params int[] ids) + { + return CreateFilter("id", ids); + } + + public CatalogSearchQuery WithProductId(int? fromId, int? toId) + { + if (fromId == null && toId == null) + { + return this; + } + + return WithFilter(SearchFilter.ByRange("id", fromId, toId, fromId.HasValue, toId.HasValue).Mandatory().ExactMatch().NotAnalyzed()); + } + + /// + /// Filter by category identifiers. + /// + /// + /// A value indicating whether loaded products are marked as featured (relates only to categories and manufacturers). + /// false to load featured products only, true to load unfeatured products only, null to load all products. + /// + /// The category identifiers. + /// Search query. + public CatalogSearchQuery WithCategoryIds(bool? featuredOnly, params int[] ids) + { + var fieldName = featuredOnly.HasValue ? featuredOnly.Value ? "featuredcategoryid" : "notfeaturedcategoryid" : "categoryid"; - return WithFilter(SearchFilter.Combined(ids.Select(x => SearchFilter.ByField(fieldName, x).ExactMatch().NotAnalyzed()).ToArray())); - } - - /// Includes only published categories - public CatalogSearchQuery HasAnyCategory(bool value) - { - if (value) - { - return WithFilter(SearchFilter.ByRange("categoryid", 1, int.MaxValue, true, true).Mandatory().NotAnalyzed()); - } - else - { - return WithFilter(SearchFilter.ByField("categoryid", 0).Mandatory().ExactMatch().NotAnalyzed()); - } - } - - public CatalogSearchQuery WithManufacturerIds(bool? featuredOnly, params int[] ids) - { - if (ids.Length == 0) - { - return this; - } - - var fieldName = featuredOnly.HasValue + return CreateFilter(fieldName, ids); + } + + /// Includes only published categories. + public CatalogSearchQuery HasAnyCategory(bool value) + { + if (value) + { + return WithFilter(SearchFilter.ByRange("categoryid", 1, int.MaxValue, true, true).Mandatory().NotAnalyzed()); + } + else + { + return WithFilter(SearchFilter.ByField("categoryid", 0).Mandatory().ExactMatch().NotAnalyzed()); + } + } + + public CatalogSearchQuery WithManufacturerIds(bool? featuredOnly, params int[] ids) + { + var fieldName = featuredOnly.HasValue ? featuredOnly.Value ? "featuredmanufacturerid" : "notfeaturedmanufacturerid" : "manufacturerid"; - return WithFilter(SearchFilter.Combined(ids.Select(x => SearchFilter.ByField(fieldName, x).ExactMatch().NotAnalyzed()).ToArray())); - } - - /// Includes only published manufacturers - public CatalogSearchQuery HasAnyManufacturer(bool value) - { - if (value) - { - return WithFilter(SearchFilter.ByRange("manufacturerid", 1, int.MaxValue, true, true).Mandatory().NotAnalyzed()); - } - else - { - return WithFilter(SearchFilter.ByField("manufacturerid", 0).Mandatory().ExactMatch().NotAnalyzed()); - } - } - - public CatalogSearchQuery WithProductTagIds(params int[] ids) - { - if (ids.Length == 0) - { - return this; - } - - return WithFilter(SearchFilter.Combined(ids.Select(x => SearchFilter.ByField("tagid", x).ExactMatch().NotAnalyzed()).ToArray())); - } - - public CatalogSearchQuery WithStockQuantity(int? fromQuantity, int? toQuantity) - { - if (fromQuantity == null && toQuantity == null) - { - return this; - } - - return WithFilter(SearchFilter.ByRange("stockquantity", fromQuantity, toQuantity, fromQuantity.HasValue, toQuantity.HasValue).Mandatory().ExactMatch().NotAnalyzed()); - } - - public CatalogSearchQuery AvailableOnly(bool value) - { - return WithFilter(SearchFilter.ByField("available", value).Mandatory().ExactMatch().NotAnalyzed()); - } - - public CatalogSearchQuery PriceBetween(decimal? fromPrice, decimal? toPrice) - { - if (fromPrice == null && toPrice == null) - { - return this; - } - - Guard.NotEmpty(CurrencyCode, nameof(CurrencyCode)); - - var fieldName = "price_c-" + CurrencyCode.EmptyNull().ToLower(); - - return WithFilter(SearchFilter.ByRange(fieldName, - fromPrice.HasValue ? decimal.ToDouble(fromPrice.Value) : (double?)null, - toPrice.HasValue ? decimal.ToDouble(toPrice.Value) : (double?)null, - fromPrice.HasValue, - toPrice.HasValue).Mandatory().ExactMatch().NotAnalyzed() - ); - } - - public CatalogSearchQuery CreatedBetween(DateTime? fromUtc, DateTime? toUtc) - { - if (fromUtc == null && toUtc == null) - { - return this; - } - - return WithFilter(SearchFilter.ByRange("createdon", fromUtc, toUtc, fromUtc.HasValue, toUtc.HasValue).Mandatory().ExactMatch().NotAnalyzed()); - } - - public CatalogSearchQuery WithRating(double? fromRate, double? toRate) - { - if (fromRate == null && toRate == null) - { - return this; - } - if (fromRate.HasValue) - { - Guard.InRange(fromRate.Value, 0.0, 5.0, nameof(fromRate.Value)); - } - if (toRate.HasValue) - { - Guard.InRange(toRate.Value, 0.0, 5.0, nameof(toRate.Value)); - } - - return WithFilter(SearchFilter.ByRange("rating", fromRate, toRate, fromRate.HasValue, toRate.HasValue).Mandatory().ExactMatch().NotAnalyzed()); - } - - public CatalogSearchQuery WithDeliveryTimeIds(params int[] ids) - { - if (ids.Length == 0) - { - return this; - } - - return WithFilter(SearchFilter.Combined(ids.Select(x => SearchFilter.ByField("deliveryid", x).ExactMatch().NotAnalyzed()).ToArray())); - } - - #endregion - } + return CreateFilter(fieldName, ids); + } + + /// Includes only published manufacturers. + public CatalogSearchQuery HasAnyManufacturer(bool value) + { + if (value) + { + return WithFilter(SearchFilter.ByRange("manufacturerid", 1, int.MaxValue, true, true).Mandatory().NotAnalyzed()); + } + else + { + return WithFilter(SearchFilter.ByField("manufacturerid", 0).Mandatory().ExactMatch().NotAnalyzed()); + } + } + + public CatalogSearchQuery WithProductTagIds(params int[] ids) + { + return CreateFilter("tagid", ids); + } + + public CatalogSearchQuery WithDeliveryTimeIds(params int[] ids) + { + return CreateFilter("deliveryid", ids); + } + + public CatalogSearchQuery WithCondition(params ProductCondition[] conditions) + { + var len = conditions?.Length ?? 0; + if (len > 0) + { + if (len == 1) + { + return WithFilter(SearchFilter.ByField("condition", (int)conditions[0]).Mandatory().ExactMatch().NotAnalyzed()); + } + + return WithFilter(SearchFilter.Combined(conditions.Select(x => SearchFilter.ByField("condition", (int)x).ExactMatch().NotAnalyzed()).ToArray())); + } + + return this; + } + + public CatalogSearchQuery HomePageProductsOnly(bool value) + { + return WithFilter(SearchFilter.ByField("showonhomepage", value).Mandatory().ExactMatch().NotAnalyzed()); + } + + public CatalogSearchQuery DownloadOnly(bool value) + { + return WithFilter(SearchFilter.ByField("download", value).Mandatory().ExactMatch().NotAnalyzed()); + } + + public CatalogSearchQuery RecurringOnly(bool value) + { + return WithFilter(SearchFilter.ByField("recurring", value).Mandatory().ExactMatch().NotAnalyzed()); + } + + public CatalogSearchQuery ShipEnabledOnly(bool value) + { + return WithFilter(SearchFilter.ByField("shipenabled", value).Mandatory().ExactMatch().NotAnalyzed()); + } + + public CatalogSearchQuery FreeShippingOnly(bool value) + { + return WithFilter(SearchFilter.ByField("shipfree", value).Mandatory().ExactMatch().NotAnalyzed()); + } + + public CatalogSearchQuery TaxExemptOnly(bool value) + { + return WithFilter(SearchFilter.ByField("taxexempt", value).Mandatory().ExactMatch().NotAnalyzed()); + } + + public CatalogSearchQuery EsdOnly(bool value) + { + return WithFilter(SearchFilter.ByField("esd", value).Mandatory().ExactMatch().NotAnalyzed()); + } + + public CatalogSearchQuery HasDiscount(bool value) + { + return WithFilter(SearchFilter.ByField("discount", value).Mandatory().ExactMatch().NotAnalyzed()); + } + + public CatalogSearchQuery WithStockQuantity( + int? fromQuantity, + int? toQuantity, + bool? includeFrom = null, + bool? includeTo = null) + { + if (fromQuantity == null && toQuantity == null) + { + return this; + } + + if (fromQuantity.HasValue && toQuantity.HasValue && fromQuantity == toQuantity) + { + var forbidden = includeFrom.HasValue && includeTo.HasValue && !includeFrom.Value && !includeTo.Value; + + return WithFilter(SearchFilter.ByField("stockquantity", fromQuantity.Value).Mandatory(!forbidden).ExactMatch().NotAnalyzed()); + } + else + { + var filter = SearchFilter.ByRange( + "stockquantity", + fromQuantity, + toQuantity, + includeFrom ?? fromQuantity.HasValue, + includeTo ?? toQuantity.HasValue); + + return WithFilter(filter.Mandatory().ExactMatch().NotAnalyzed()); + } + } + + public CatalogSearchQuery PriceBetween( + decimal? fromPrice, + decimal? toPrice, + bool? includeFrom = null, + bool? includeTo = null) + { + if (fromPrice == null && toPrice == null) + { + return this; + } + + Guard.NotEmpty(CurrencyCode, nameof(CurrencyCode)); + + var fieldName = "price_c-" + CurrencyCode.EmptyNull().ToLower(); + + if (fromPrice.HasValue && toPrice.HasValue && fromPrice == toPrice) + { + var forbidden = includeFrom.HasValue && includeTo.HasValue && !includeFrom.Value && !includeTo.Value; + + return WithFilter(SearchFilter.ByField(fieldName, decimal.ToDouble(fromPrice.Value)).Mandatory(!forbidden).ExactMatch().NotAnalyzed()); + } + else + { + var filter = SearchFilter.ByRange(fieldName, + fromPrice.HasValue ? decimal.ToDouble(fromPrice.Value) : (double?)null, + toPrice.HasValue ? decimal.ToDouble(toPrice.Value) : (double?)null, + includeFrom ?? fromPrice.HasValue, + includeTo ?? toPrice.HasValue); + + return WithFilter(filter.Mandatory().ExactMatch().NotAnalyzed()); + } + } + + public CatalogSearchQuery CreatedBetween( + DateTime? fromUtc, + DateTime? toUtc, + bool? includeFrom = null, + bool? includeTo = null) + { + if (fromUtc == null && toUtc == null) + { + return this; + } + + if (fromUtc.HasValue && toUtc.HasValue && fromUtc == toUtc) + { + var forbidden = includeFrom.HasValue && includeTo.HasValue && !includeFrom.Value && !includeTo.Value; + + return WithFilter(SearchFilter.ByField("createdon", fromUtc.Value).Mandatory(!forbidden).ExactMatch().NotAnalyzed()); + } + else + { + var filter = SearchFilter.ByRange( + "createdon", + fromUtc, + toUtc, + includeFrom ?? fromUtc.HasValue, + includeTo ?? toUtc.HasValue); + + return WithFilter(filter.Mandatory().ExactMatch().NotAnalyzed()); + } + } + + public CatalogSearchQuery WithRating( + double? fromRate, + double? toRate, + bool? includeFrom = null, + bool? includeTo = null) + { + if (fromRate == null && toRate == null) + { + return this; + } + if (fromRate.HasValue) + { + Guard.InRange(fromRate.Value, 0.0, 5.0, nameof(fromRate.Value)); + } + if (toRate.HasValue) + { + Guard.InRange(toRate.Value, 0.0, 5.0, nameof(toRate.Value)); + } + + if (fromRate.HasValue && toRate.HasValue && fromRate == toRate) + { + var forbidden = includeFrom.HasValue && includeTo.HasValue && !includeFrom.Value && !includeTo.Value; + + return WithFilter(SearchFilter.ByField("rating", fromRate.Value).Mandatory(!forbidden).ExactMatch().NotAnalyzed()); + } + else + { + var filter = SearchFilter.ByRange( + "rating", + fromRate, + toRate, + includeFrom ?? fromRate.HasValue, + includeTo ?? toRate.HasValue); + + return WithFilter(filter.Mandatory().ExactMatch().NotAnalyzed()); + } + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchResult.cs b/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchResult.cs index c19da2f48f..671840c654 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchResult.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchResult.cs @@ -8,57 +8,64 @@ namespace SmartStore.Services.Search { public partial class CatalogSearchResult - { + { private readonly Func> _hitsFactory; - private IPagedList _hits; + private IPagedList _hits; - public CatalogSearchResult( - ISearchEngine engine, - CatalogSearchQuery query, - int totalHitsCount, - Func> hitsFactory, - string[] spellCheckerSuggestions, - IDictionary facets) - { - Guard.NotNull(query, nameof(query)); + public CatalogSearchResult( + ISearchEngine engine, + CatalogSearchQuery query, + int totalHitsCount, + int[] hitsEntityIds, + Func> hitsFactory, + string[] spellCheckerSuggestions, + IDictionary facets) + { + Guard.NotNull(query, nameof(query)); - Engine = engine; - Query = query; - SpellCheckerSuggestions = spellCheckerSuggestions ?? new string[0]; - Facets = facets ?? new Dictionary(); + Engine = engine; + Query = query; + SpellCheckerSuggestions = spellCheckerSuggestions ?? new string[0]; + Facets = facets ?? new Dictionary(); - _hitsFactory = hitsFactory ?? (() => new List()); - TotalHitsCount = totalHitsCount; - } + _hitsFactory = hitsFactory ?? (() => new List()); + HitsEntityIds = hitsEntityIds ?? new int[0]; + TotalHitsCount = totalHitsCount; + } - /// - /// Constructor for an instance without any search hits - /// - /// Catalog search query - public CatalogSearchResult(CatalogSearchQuery query) - : this(null, query, 0, () => new List(), null, null) - { - } + /// + /// Constructor for an instance without any search hits + /// + /// Catalog search query + public CatalogSearchResult(CatalogSearchQuery query) + : this(null, query, 0, null, () => new List(), null, null) + { + } - /// - /// Products found - /// - public IPagedList Hits - { - get - { - if (_hits == null) - { - var products = TotalHitsCount == 0 - ? new List() - : _hitsFactory.Invoke(); + /// + /// Entity identifiers of found products. + /// + public int[] HitsEntityIds { get; private set; } - _hits = new PagedList(products, Query.PageIndex, Query.Take, TotalHitsCount); - } + /// + /// Products found + /// + public IPagedList Hits + { + get + { + if (_hits == null) + { + var products = TotalHitsCount == 0 + ? new List() + : _hitsFactory.Invoke(); + + _hits = new PagedList(products, Query.PageIndex, Query.Take, TotalHitsCount); + } - return _hits; - } - } + return _hits; + } + } public int TotalHitsCount { get; } @@ -66,30 +73,30 @@ public IPagedList Hits /// The original catalog search query /// public CatalogSearchQuery Query - { - get; - private set; - } + { + get; + private set; + } - /// - /// Gets spell checking suggestions/corrections - /// - public string[] SpellCheckerSuggestions - { - get; - set; - } + /// + /// Gets spell checking suggestions/corrections + /// + public string[] SpellCheckerSuggestions + { + get; + set; + } - public IDictionary Facets - { - get; - private set; - } + public IDictionary Facets + { + get; + private set; + } - public ISearchEngine Engine - { - get; - private set; - } - } + public ISearchEngine Engine + { + 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..480c5190d0 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchService.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/CatalogSearchService.cs @@ -2,61 +2,65 @@ using System.Collections.Generic; using System.Linq; using System.Web.Mvc; +using System.Data.Entity; using Autofac; using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Seo; using SmartStore.Core.Localization; using SmartStore.Core.Logging; using SmartStore.Core.Search; using SmartStore.Core.Search.Facets; using SmartStore.Services.Catalog; +using SmartStore.Services.Seo; +using SmartStore.Data.Utilities; namespace SmartStore.Services.Search { - public partial class CatalogSearchService : SearchServiceBase, ICatalogSearchService + public partial class CatalogSearchService : SearchServiceBase, ICatalogSearchService, IXmlSitemapPublisher { - private readonly ICommonServices _services; - private readonly IIndexManager _indexManager; - private readonly Lazy _productService; - private readonly ILogger _logger; - private readonly IPriceFormatter _priceFormatter; - private readonly UrlHelper _urlHelper; - - public CatalogSearchService( - ICommonServices services, - IIndexManager indexManager, - Lazy productService, - ILogger logger, - IPriceFormatter priceFormatter, - UrlHelper urlHelper) - { - _services = services; - _indexManager = indexManager; - _productService = productService; - _logger = logger; - _priceFormatter = priceFormatter; - _urlHelper = urlHelper; - - T = NullLocalizer.Instance; - } - - public Localizer T { get; set; } - - /// - /// Bypasses the index provider and directly searches in the database - /// - /// Search query - /// LOad flags - /// Catalog search result - protected virtual CatalogSearchResult SearchDirect(CatalogSearchQuery searchQuery, ProductLoadFlags loadFlags = ProductLoadFlags.None) - { - // Fallback to linq search. - var linqCatalogSearchService = _services.Container.ResolveNamed("linq"); - - var result = linqCatalogSearchService.Search(searchQuery, loadFlags, true); - ApplyFacetLabels(result.Facets); - - return result; - } + private readonly ICommonServices _services; + private readonly IIndexManager _indexManager; + private readonly Lazy _productService; + private readonly ILogger _logger; + private readonly IPriceFormatter _priceFormatter; + private readonly UrlHelper _urlHelper; + + public CatalogSearchService( + ICommonServices services, + IIndexManager indexManager, + Lazy productService, + ILogger logger, + IPriceFormatter priceFormatter, + UrlHelper urlHelper) + { + _services = services; + _indexManager = indexManager; + _productService = productService; + _logger = logger; + _priceFormatter = priceFormatter; + _urlHelper = urlHelper; + + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + /// + /// Bypasses the index provider and directly searches in the database + /// + /// Search query + /// LOad flags + /// Catalog search result + protected virtual CatalogSearchResult SearchDirect(CatalogSearchQuery searchQuery, ProductLoadFlags loadFlags = ProductLoadFlags.None) + { + // Fallback to linq search. + var linqCatalogSearchService = _services.Container.ResolveNamed("linq"); + + var result = linqCatalogSearchService.Search(searchQuery, loadFlags, true); + ApplyFacetLabels(result.Facets); + + return result; + } protected virtual void ApplyFacetLabels(IDictionary facets) { @@ -138,115 +142,168 @@ protected virtual string FormatPrice(decimal price) } public CatalogSearchResult Search( - CatalogSearchQuery searchQuery, - ProductLoadFlags loadFlags = ProductLoadFlags.None, - bool direct = false) - { - Guard.NotNull(searchQuery, nameof(searchQuery)); - Guard.NotNegative(searchQuery.Take, nameof(searchQuery.Take)); - - var provider = _indexManager.GetIndexProvider("Catalog"); - - if (!direct && provider != null) - { - var indexStore = provider.GetIndexStore("Catalog"); - if (indexStore.Exists) - { - var searchEngine = provider.GetSearchEngine(indexStore, searchQuery); - var stepPrefix = searchEngine.GetType().Name + " - "; - - int totalCount = 0; - string[] spellCheckerSuggestions = null; - IEnumerable searchHits; - Func> hitsFactory = null; - IDictionary facets = null; - - _services.EventPublisher.Publish(new CatalogSearchingEvent(searchQuery)); - - if (searchQuery.Take > 0) - { - using (_services.Chronometer.Step(stepPrefix + "Search")) - { - totalCount = searchEngine.Count(); - // Fix paging boundaries - if (searchQuery.Skip > 0 && searchQuery.Skip >= totalCount) - { - searchQuery.Slice((totalCount / searchQuery.Take) * searchQuery.Take, searchQuery.Take); - } - } - - if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithHits)) - { - using (_services.Chronometer.Step(stepPrefix + "Hits")) - { - searchHits = searchEngine.Search(); - } - - using (_services.Chronometer.Step(stepPrefix + "Collect")) - { - var productIds = searchHits.Select(x => x.EntityId).ToArray(); - hitsFactory = () => _productService.Value.GetProductsByIds(productIds, loadFlags); - } - } - - if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithFacets)) - { - try - { - using (_services.Chronometer.Step(stepPrefix + "Facets")) - { - facets = searchEngine.GetFacetMap(); - ApplyFacetLabels(facets); - } - } - catch (Exception ex) - { - _logger.Error(ex); - } - } - } - - if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithSuggestions)) - { - try - { - using (_services.Chronometer.Step(stepPrefix + "Spellcheck")) - { - spellCheckerSuggestions = searchEngine.CheckSpelling(); - } - } - catch (Exception ex) - { - // Spell checking should not break the search. - _logger.Error(ex); - } - } - - var result = new CatalogSearchResult( - searchEngine, - searchQuery, - totalCount, - hitsFactory, - spellCheckerSuggestions, - facets); - - _services.EventPublisher.Publish(new CatalogSearchedEvent(searchQuery, result)); - - return result; - } - else if (searchQuery.Origin.IsCaseInsensitiveEqual("Search/Search")) - { - IndexingRequiredNotification(_services, _urlHelper); - } - } - - return SearchDirect(searchQuery); - } - - public IQueryable PrepareQuery(CatalogSearchQuery searchQuery, IQueryable baseQuery = null) - { - var linqCatalogSearchService = _services.Container.ResolveNamed("linq"); - return linqCatalogSearchService.PrepareQuery(searchQuery, baseQuery); - } - } + CatalogSearchQuery searchQuery, + ProductLoadFlags loadFlags = ProductLoadFlags.None, + bool direct = false) + { + Guard.NotNull(searchQuery, nameof(searchQuery)); + Guard.NotNegative(searchQuery.Take, nameof(searchQuery.Take)); + + var provider = _indexManager.GetIndexProvider("Catalog"); + + if (!direct && provider != null) + { + var indexStore = provider.GetIndexStore("Catalog"); + if (indexStore.Exists) + { + var searchEngine = provider.GetSearchEngine(indexStore, searchQuery); + var stepPrefix = searchEngine.GetType().Name + " - "; + + int totalCount = 0; + int[] hitsEntityIds = null; + string[] spellCheckerSuggestions = null; + IEnumerable searchHits; + Func> hitsFactory = null; + IDictionary facets = null; + + _services.EventPublisher.Publish(new CatalogSearchingEvent(searchQuery, false)); + + if (searchQuery.Take > 0) + { + using (_services.Chronometer.Step(stepPrefix + "Search")) + { + totalCount = searchEngine.Count(); + // Fix paging boundaries + if (searchQuery.Skip > 0 && searchQuery.Skip >= totalCount) + { + searchQuery.Slice((totalCount / searchQuery.Take) * searchQuery.Take, searchQuery.Take); + } + } + + if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithHits)) + { + using (_services.Chronometer.Step(stepPrefix + "Hits")) + { + searchHits = searchEngine.Search(); + } + + using (_services.Chronometer.Step(stepPrefix + "Collect")) + { + hitsEntityIds = searchHits.Select(x => x.EntityId).ToArray(); + hitsFactory = () => _productService.Value.GetProductsByIds(hitsEntityIds, loadFlags); + } + } + + if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithFacets)) + { + try + { + using (_services.Chronometer.Step(stepPrefix + "Facets")) + { + facets = searchEngine.GetFacetMap(); + ApplyFacetLabels(facets); + } + } + catch (Exception ex) + { + _logger.Error(ex); + } + } + } + + if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithSuggestions)) + { + try + { + using (_services.Chronometer.Step(stepPrefix + "Spellcheck")) + { + spellCheckerSuggestions = searchEngine.CheckSpelling(); + } + } + catch (Exception ex) + { + // Spell checking should not break the search. + _logger.Error(ex); + } + } + + var result = new CatalogSearchResult( + searchEngine, + searchQuery, + totalCount, + hitsEntityIds, + hitsFactory, + spellCheckerSuggestions, + facets); + + var searchedEvent = new CatalogSearchedEvent(searchQuery, result); + _services.EventPublisher.Publish(searchedEvent); + + return searchedEvent.Result; + } + else if (searchQuery.Origin.IsCaseInsensitiveEqual("Search/Search")) + { + IndexingRequiredNotification(_services, _urlHelper); + } + } + + return SearchDirect(searchQuery); + } + + public IQueryable PrepareQuery(CatalogSearchQuery searchQuery, IQueryable baseQuery = null) + { + var linqCatalogSearchService = _services.Container.ResolveNamed("linq"); + return linqCatalogSearchService.PrepareQuery(searchQuery, baseQuery); + } + + #region XML Sitemap + + public XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context) + { + if (!context.LoadSetting().XmlSitemapIncludesProducts) + return null; + + var searchQuery = new CatalogSearchQuery() + .VisibleOnly(_services.WorkContext.CurrentCustomer) + .WithVisibility(ProductVisibility.Full) + .HasStoreId(context.RequestStoreId); + + var query = PrepareQuery(searchQuery); + + return new ProductXmlSitemapResult { Query = query, Context = context }; + } + + class ProductXmlSitemapResult : XmlSitemapProvider + { + public IQueryable Query { get; set; } + public XmlSitemapBuildContext Context { get; set; } + + public override int GetTotalCount() + { + return Query.Count(); + } + + public override IEnumerable Enlist() + { + var pager = new FastPager(Query.AsNoTracking(), Context.MaximumNodeCount); + + while (pager.ReadNextPage(x => new { x.Id, x.UpdatedOnUtc }, x => x.Id, out var products)) + { + if (Context.CancellationToken.IsCancellationRequested) + { + break; + } + + foreach (var x in products) + { + yield return new NamedEntity { EntityName = "Product", Id = x.Id, LastMod = x.UpdatedOnUtc }; + } + } + } + + public override int Order => int.MaxValue; + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/ICatalogSearchService.cs b/src/Libraries/SmartStore.Services/Search/Catalog/ICatalogSearchService.cs index 21ffebbbe1..8327b3bade 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/ICatalogSearchService.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/ICatalogSearchService.cs @@ -4,23 +4,23 @@ namespace SmartStore.Services.Search { - public partial interface ICatalogSearchService - { - /// - /// Searches for products - /// - /// Search term, filters and other parameters used for searching - /// Which product navigation properties to eager load - /// Bypasses the index provider (if available) and directly searches in the database - /// Catalog search result - CatalogSearchResult Search(CatalogSearchQuery searchQuery, ProductLoadFlags loadFlags = ProductLoadFlags.None, bool direct = false); + public partial interface ICatalogSearchService + { + /// + /// Searches for products + /// + /// Search term, filters and other parameters used for searching + /// Which product navigation properties to eager load + /// Bypasses the index provider (if available) and directly searches in the database + /// Catalog search result + CatalogSearchResult Search(CatalogSearchQuery searchQuery, ProductLoadFlags loadFlags = ProductLoadFlags.None, bool direct = false); - /// - /// Builds a product query using linq search - /// - /// Search term, filters and other parameters used for searching - /// Optional query used to build the product query. - /// Product queryable - IQueryable PrepareQuery(CatalogSearchQuery searchQuery, IQueryable baseQuery = null); - } + /// + /// Builds a product query using linq search + /// + /// Search term, filters and other parameters used for searching + /// Optional query used to build the product query. + /// Product queryable + IQueryable PrepareQuery(CatalogSearchQuery searchQuery, IQueryable baseQuery = null); + } } diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/LinqCatalogSearchService.cs b/src/Libraries/SmartStore.Services/Search/Catalog/LinqCatalogSearchService.cs index fcd96b6675..6e709dca0d 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/LinqCatalogSearchService.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/LinqCatalogSearchService.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Data.Entity; +using System.Linq; using SmartStore.Core.Data; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Security; using SmartStore.Core.Domain.Stores; -using SmartStore.Core.Events; using SmartStore.Core.Search; using SmartStore.Core.Search.Facets; using SmartStore.Services.Catalog; @@ -18,57 +17,48 @@ namespace SmartStore.Services.Search { public partial class LinqCatalogSearchService : SearchServiceBase, ICatalogSearchService { - private static int[] _priceThresholds = new int[] { 10, 25, 50, 100, 250, 500, 1000 }; - - private readonly IProductService _productService; - private readonly IRepository _productRepository; - private readonly IRepository _productManufacturerRepository; - private readonly IRepository _productCategoryRepository; - private readonly IRepository _localizedPropertyRepository; - private readonly IRepository _storeMappingRepository; - private readonly IRepository _aclRepository; - private readonly IEventPublisher _eventPublisher; - private readonly ICommonServices _services; - private readonly IDeliveryTimeService _deliveryTimeService; - private readonly IManufacturerService _manufacturerService; - private readonly ICategoryService _categoryService; - - public LinqCatalogSearchService( - IProductService productService, - IRepository productRepository, - IRepository productManufacturerRepository, - IRepository productCategoryRepository, - IRepository localizedPropertyRepository, - IRepository storeMappingRepository, - IRepository aclRepository, - IEventPublisher eventPublisher, - ICommonServices services, - IDeliveryTimeService deliveryTimeService, - IManufacturerService manufacturerService, - ICategoryService categoryService) - { - _productService = productService; - _productRepository = productRepository; - _productManufacturerRepository = productManufacturerRepository; - _productCategoryRepository = productCategoryRepository; - _localizedPropertyRepository = localizedPropertyRepository; - _storeMappingRepository = storeMappingRepository; - _aclRepository = aclRepository; - _eventPublisher = eventPublisher; - _services = services; - _deliveryTimeService = deliveryTimeService; - _manufacturerService = manufacturerService; - _categoryService = categoryService; - - QuerySettings = DbQuerySettings.Default; - } - - public DbQuerySettings QuerySettings { get; set; } + private static int[] _priceThresholds = new int[] { 10, 25, 50, 100, 250, 500, 1000 }; + + private readonly ICommonServices _services; + private readonly IProductService _productService; + private readonly IRepository _productRepository; + private readonly IRepository _localizedPropertyRepository; + private readonly IRepository _storeMappingRepository; + private readonly IRepository _aclRepository; + private readonly IDeliveryTimeService _deliveryTimeService; + private readonly IManufacturerService _manufacturerService; + private readonly ICategoryService _categoryService; + + public LinqCatalogSearchService( + ICommonServices services, + IProductService productService, + IRepository productRepository, + IRepository localizedPropertyRepository, + IRepository storeMappingRepository, + IRepository aclRepository, + IDeliveryTimeService deliveryTimeService, + IManufacturerService manufacturerService, + ICategoryService categoryService) + { + _services = services; + _productService = productService; + _productRepository = productRepository; + _localizedPropertyRepository = localizedPropertyRepository; + _storeMappingRepository = storeMappingRepository; + _aclRepository = aclRepository; + _deliveryTimeService = deliveryTimeService; + _manufacturerService = manufacturerService; + _categoryService = categoryService; + + QuerySettings = DbQuerySettings.Default; + } + + public DbQuerySettings QuerySettings { get; set; } #region Utilities - private IQueryable QueryCategories(IQueryable query, List ids, bool? featuredOnly) - { + private IQueryable QueryCategories(IQueryable query, List ids, bool? featuredOnly) + { return from p in query from pc in p.ProductCategories.Where(pc => ids.Contains(pc.CategoryId)) @@ -77,7 +67,7 @@ from pc in p.ProductCategories.Where(pc => ids.Contains(pc.CategoryId)) } private IQueryable QueryManufacturers(IQueryable query, List ids, bool? featuredOnly) - { + { return from p in query from pm in p.ProductManufacturers.Where(pm => ids.Contains(pm.ManufacturerId)) @@ -85,712 +75,832 @@ from pm in p.ProductManufacturers.Where(pm => ids.Contains(pm.ManufacturerId)) select p; } - protected virtual IQueryable ApplySearchTerm(IQueryable query, CatalogSearchQuery searchQuery) - { - var term = searchQuery.Term; - var fields = searchQuery.Fields; - var languageId = searchQuery.LanguageId ?? 0; - - if (term.HasValue() && fields != null && fields.Length != 0 && fields.Any(x => x.HasValue())) - { - // SearchMode.ExactMatch doesn't make sense here - if (searchQuery.Mode == SearchMode.StartsWith) - { - query = - from p in query - join lp in _localizedPropertyRepository.Table on p.Id equals lp.EntityId into plp - from lp in plp.DefaultIfEmpty() - where - (fields.Contains("name") && p.Name.StartsWith(term)) || - (fields.Contains("sku") && p.Sku.StartsWith(term)) || - (fields.Contains("shortdescription") && p.ShortDescription.StartsWith(term)) || - (languageId != 0 && lp.LanguageId == languageId && lp.LocaleKeyGroup == "Product" && lp.LocaleKey == "Name" && lp.LocaleValue.StartsWith(term)) || - (languageId != 0 && lp.LanguageId == languageId && lp.LocaleKeyGroup == "Product" && lp.LocaleKey == "ShortDescription" && lp.LocaleValue.StartsWith(term)) - select p; - } - else - { - query = - from p in query - join lp in _localizedPropertyRepository.Table on p.Id equals lp.EntityId into plp - from lp in plp.DefaultIfEmpty() - where - (fields.Contains("name") && p.Name.Contains(term)) || - (fields.Contains("sku") && p.Sku.Contains(term)) || - (fields.Contains("shortdescription") && p.ShortDescription.Contains(term)) || - (languageId != 0 && lp.LanguageId == languageId && lp.LocaleKeyGroup == "Product" && lp.LocaleKey == "Name" && lp.LocaleValue.Contains(term)) || - (languageId != 0 && lp.LanguageId == languageId && lp.LocaleKeyGroup == "Product" && lp.LocaleKey == "ShortDescription" && lp.LocaleValue.Contains(term)) - select p; - } - } - - return query; - } - - protected virtual IQueryable GetProductQuery(CatalogSearchQuery searchQuery, IQueryable baseQuery) - { - var ordered = false; - var utcNow = DateTime.UtcNow; + protected virtual IQueryable ApplySearchTerm(IQueryable query, CatalogSearchQuery searchQuery, out bool isGroupingRequired) + { + isGroupingRequired = false; + + var term = searchQuery.Term; + var fields = searchQuery.Fields; + var languageId = searchQuery.LanguageId ?? 0; + + if (term.HasValue() && fields != null && fields.Length != 0 && fields.Any(x => x.HasValue())) + { + isGroupingRequired = true; + + // SearchMode.ExactMatch doesn't make sense here + if (searchQuery.Mode == SearchMode.StartsWith) + { + query = + from p in query + join lp in _localizedPropertyRepository.Table on p.Id equals lp.EntityId into plp + from lp in plp.DefaultIfEmpty() + where + (fields.Contains("name") && p.Name.StartsWith(term)) || + (fields.Contains("sku") && p.Sku.StartsWith(term)) || + (fields.Contains("shortdescription") && p.ShortDescription.StartsWith(term)) || + (languageId != 0 && lp.LanguageId == languageId && lp.LocaleKeyGroup == "Product" && lp.LocaleKey == "Name" && lp.LocaleValue.StartsWith(term)) || + (languageId != 0 && lp.LanguageId == languageId && lp.LocaleKeyGroup == "Product" && lp.LocaleKey == "ShortDescription" && lp.LocaleValue.StartsWith(term)) + select p; + } + else + { + query = + from p in query + join lp in _localizedPropertyRepository.Table on p.Id equals lp.EntityId into plp + from lp in plp.DefaultIfEmpty() + where + (fields.Contains("name") && p.Name.Contains(term)) || + (fields.Contains("sku") && p.Sku.Contains(term)) || + (fields.Contains("shortdescription") && p.ShortDescription.Contains(term)) || + (languageId != 0 && lp.LanguageId == languageId && lp.LocaleKeyGroup == "Product" && lp.LocaleKey == "Name" && lp.LocaleValue.Contains(term)) || + (languageId != 0 && lp.LanguageId == languageId && lp.LocaleKeyGroup == "Product" && lp.LocaleKey == "ShortDescription" && lp.LocaleValue.Contains(term)) + select p; + } + } + + return query; + } + + protected virtual IQueryable GetProductQuery(CatalogSearchQuery searchQuery, IQueryable baseQuery) + { + var ordered = false; + var utcNow = DateTime.UtcNow; var categoryId = 0; var manufacturerId = 0; - var query = baseQuery ?? _productRepository.Table; + var query = baseQuery ?? _productRepository.Table; - query = query.Where(x => !x.Deleted && !x.IsSystemProduct); - query = ApplySearchTerm(query, searchQuery); + query = query.Where(x => !x.Deleted && !x.IsSystemProduct); + query = ApplySearchTerm(query, searchQuery, out var isGroupingRequired); - #region Filters + #region Filters - var filters = new List(); - FlattenFilters(searchQuery.Filters, filters); + var filters = new List(); + FlattenFilters(searchQuery.Filters, filters); - var productIds = GetIdList(filters, "id"); - if (productIds.Any()) - { - query = query.Where(x => productIds.Contains(x.Id)); - } + var productIds = GetIdList(filters, "id"); + if (productIds.Any()) + { + query = query.Where(x => productIds.Contains(x.Id)); + } - var categoryIds = GetIdList(filters, "categoryid"); - if (categoryIds.Any()) - { + var categoryIds = GetIdList(filters, "categoryid"); + if (categoryIds.Any()) + { + isGroupingRequired = true; categoryId = categoryIds.First(); - if (categoryIds.Count == 1 && categoryId == 0) - { - // Has no category. - query = query.Where(x => x.ProductCategories.Count == 0); - } - else - { - query = QueryCategories(query, categoryIds, null); - } - } + if (categoryIds.Count == 1 && categoryId == 0) + { + // Has no category. + query = query.Where(x => x.ProductCategories.Count == 0); + } + else + { + query = QueryCategories(query, categoryIds, null); + } + } var featuredCategoryIds = GetIdList(filters, "featuredcategoryid"); var notFeaturedCategoryIds = GetIdList(filters, "notfeaturedcategoryid"); if (featuredCategoryIds.Any()) { + isGroupingRequired = true; categoryId = categoryId == 0 ? featuredCategoryIds.First() : categoryId; query = QueryCategories(query, featuredCategoryIds, true); } if (notFeaturedCategoryIds.Any()) { + isGroupingRequired = true; categoryId = categoryId == 0 ? notFeaturedCategoryIds.First() : categoryId; query = QueryCategories(query, notFeaturedCategoryIds, false); } - var manufacturerIds = GetIdList(filters, "manufacturerid"); - if (manufacturerIds.Any()) - { + var manufacturerIds = GetIdList(filters, "manufacturerid"); + if (manufacturerIds.Any()) + { + isGroupingRequired = true; manufacturerId = manufacturerIds.First(); - if (manufacturerIds.Count == 1 && manufacturerId == 0) - { - // Has no manufacturer. - query = query.Where(x => x.ProductManufacturers.Count == 0); - } - else - { + if (manufacturerIds.Count == 1 && manufacturerId == 0) + { + // Has no manufacturer. + query = query.Where(x => x.ProductManufacturers.Count == 0); + } + else + { query = QueryManufacturers(query, manufacturerIds, null); - } - } + } + } var featuredManuIds = GetIdList(filters, "featuredmanufacturerid"); var notFeaturedManuIds = GetIdList(filters, "notfeaturedmanufacturerid"); if (featuredManuIds.Any()) { + isGroupingRequired = true; manufacturerId = manufacturerId == 0 ? featuredManuIds.First() : manufacturerId; query = QueryManufacturers(query, featuredManuIds, true); } if (notFeaturedManuIds.Any()) { + isGroupingRequired = true; manufacturerId = manufacturerId == 0 ? notFeaturedManuIds.First() : manufacturerId; query = QueryManufacturers(query, notFeaturedManuIds, false); } - var tagIds = GetIdList(filters, "tagid"); - if (tagIds.Any()) - { + 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)) - select p; - } - - if (!QuerySettings.IgnoreAcl) - { - var roleIds = GetIdList(filters, "roleid"); - if (roleIds.Any()) - { + from p in query + from pt in p.ProductTags.Where(pt => tagIds.Contains(pt.Id)) + select p; + } + + if (!QuerySettings.IgnoreAcl) + { + 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 - from acl in pacl.DefaultIfEmpty() - where !p.SubjectToAcl || roleIds.Contains(acl.CustomerRoleId) - select p; - } - } - - var deliverTimeIds = GetIdList(filters, "deliveryid"); - if (deliverTimeIds.Any()) - { - query = query.Where(x => x.DeliveryTimeId != null && deliverTimeIds.Contains(x.DeliveryTimeId.Value)); - } - - var parentProductIds = GetIdList(filters, "parentid"); - if (parentProductIds.Any()) - { - query = query.Where(x => parentProductIds.Contains(x.ParentGroupedProductId)); - } - - foreach (IAttributeSearchFilter filter in filters) - { - var rangeFilter = filter as IRangeSearchFilter; - - if (filter.FieldName == "id") - { - if (rangeFilter != null) - { - var lower = filter.Term as int?; - var upper = rangeFilter.UpperTerm as int?; - - if (lower.HasValue) - { - if (rangeFilter.IncludesLower) - query = query.Where(x => x.Id >= lower.Value); - else - query = query.Where(x => x.Id > lower.Value); - } - - if (upper.HasValue) - { - if (rangeFilter.IncludesUpper) - query = query.Where(x => x.Id <= upper.Value); - else - query = query.Where(x => x.Id < upper.Value); - } - } - } - else if (filter.FieldName == "categoryid") - { - if (rangeFilter != null && 1 == ((filter.Term as int?) ?? 0) && int.MaxValue == ((rangeFilter.UpperTerm as int?) ?? 0)) - { - // Has any category. - query = query.Where(x => x.ProductCategories.Count > 0); - } - } - else if (filter.FieldName == "manufacturerid") - { - if (rangeFilter != null && 1 == ((filter.Term as int?) ?? 0) && int.MaxValue == ((rangeFilter.UpperTerm as int?) ?? 0)) - { - // Has any manufacturer. - query = query.Where(x => x.ProductManufacturers.Count > 0); - } - } - else if (filter.FieldName == "published") - { - query = query.Where(x => x.Published == (bool)filter.Term); - } - else if (filter.FieldName == "availablestart") - { - if (rangeFilter != null) - { - var lower = filter.Term as DateTime?; - var upper = rangeFilter.UpperTerm as DateTime?; - - if (lower.HasValue) - { - if (rangeFilter.IncludesLower) - query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc >= lower.Value); - else - query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc > lower.Value); - } - - if (upper.HasValue) - { - if (rangeFilter.IncludesLower) - query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc <= upper.Value); - else - query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc < upper.Value); - } - } - } - else if (filter.FieldName == "availableend") - { - if (rangeFilter != null) - { - var lower = filter.Term as DateTime?; - var upper = rangeFilter.UpperTerm as DateTime?; - - if (lower.HasValue) - { - if (rangeFilter.IncludesLower) - query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc >= lower.Value); - else - query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc > lower.Value); - } - - if (upper.HasValue) - { - if (rangeFilter.IncludesLower) - query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc <= upper.Value); - else - query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc < upper.Value); - } - } - } - else if (filter.FieldName == "visibleindividually") - { - query = query.Where(x => x.VisibleIndividually == (bool)filter.Term); - } - else if (filter.FieldName == "showonhomepage") - { - query = query.Where(p => p.ShowOnHomePage == (bool)filter.Term); - } - else if (filter.FieldName == "typeid") - { - query = query.Where(x => x.ProductTypeId == (int)filter.Term); - } - else if (filter.FieldName == "stockquantity") - { - if (rangeFilter != null) - { - var lower = filter.Term as int?; - var upper = rangeFilter.UpperTerm as int?; - - if (lower.HasValue) - { - if (rangeFilter.IncludesLower) - query = query.Where(x => x.StockQuantity >= lower.Value); - else - query = query.Where(x => x.StockQuantity > lower.Value); - } - - if (upper.HasValue) - { - if (rangeFilter.IncludesUpper) - query = query.Where(x => x.StockQuantity <= upper.Value); - else - query = query.Where(x => x.StockQuantity < upper.Value); - } - } - } - else if (filter.FieldName == "rating") - { - query = query.Where(x => x.ApprovedTotalReviews != 0 && ((double)x.ApprovedRatingSum / (double)x.ApprovedTotalReviews) >= (double)filter.Term); - } - else if (filter.FieldName == "available") - { - // We cannot include ManageInventoryMethod.ManageStockByAttributes because it's only functional with MergeWithCombination. - var manageStock = (int)ManageInventoryMethod.ManageStock; - - query = query.Where(x => - x.ManageInventoryMethodId != manageStock || - (x.ManageInventoryMethodId == manageStock && (x.StockQuantity > 0 || x.BackorderModeId != (int)BackorderMode.NoBackorders)) - ); - } - else if (filter.FieldName.StartsWith("price")) - { - if (rangeFilter != null) - { - var lower = filter.Term as double?; - var upper = rangeFilter.UpperTerm as double?; - - if (lower.HasValue) - { - var minPrice = Convert.ToDecimal(lower.Value); - - query = query.Where(x => - ((x.SpecialPrice.HasValue && - ((!x.SpecialPriceStartDateTimeUtc.HasValue || x.SpecialPriceStartDateTimeUtc.Value < utcNow) && - (!x.SpecialPriceEndDateTimeUtc.HasValue || x.SpecialPriceEndDateTimeUtc.Value > utcNow))) && - (x.SpecialPrice >= minPrice)) - || - ((!x.SpecialPrice.HasValue || - ((x.SpecialPriceStartDateTimeUtc.HasValue && x.SpecialPriceStartDateTimeUtc.Value > utcNow) || - (x.SpecialPriceEndDateTimeUtc.HasValue && x.SpecialPriceEndDateTimeUtc.Value < utcNow))) && - (x.Price >= minPrice)) - ); - } - - if (upper.HasValue) - { - var maxPrice = Convert.ToDecimal(upper); - - query = query.Where(x => - ((x.SpecialPrice.HasValue && - ((!x.SpecialPriceStartDateTimeUtc.HasValue || x.SpecialPriceStartDateTimeUtc.Value < utcNow) && - (!x.SpecialPriceEndDateTimeUtc.HasValue || x.SpecialPriceEndDateTimeUtc.Value > utcNow))) && - (x.SpecialPrice <= maxPrice)) - || - ((!x.SpecialPrice.HasValue || - ((x.SpecialPriceStartDateTimeUtc.HasValue && x.SpecialPriceStartDateTimeUtc.Value > utcNow) || - (x.SpecialPriceEndDateTimeUtc.HasValue && x.SpecialPriceEndDateTimeUtc.Value < utcNow))) && - (x.Price <= maxPrice)) - ); - } - } - } - else if (filter.FieldName == "createdon") - { - if (rangeFilter != null) - { - var lower = filter.Term as DateTime?; - var upper = rangeFilter.UpperTerm as DateTime?; - - if (lower.HasValue) - { - if (rangeFilter.IncludesLower) - query = query.Where(x => x.CreatedOnUtc >= lower.Value); - else - query = query.Where(x => x.CreatedOnUtc > lower.Value); - } - - if (upper.HasValue) - { - if (rangeFilter.IncludesLower) - query = query.Where(x => x.CreatedOnUtc <= upper.Value); - else - query = query.Where(x => x.CreatedOnUtc < upper.Value); - } - } - } - else if (filter.FieldName == "storeid") - { - if (!QuerySettings.IgnoreMultiStore) - { - var storeId = (int)filter.Term; - if (storeId != 0) - { + 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 + from acl in pacl.DefaultIfEmpty() + where !p.SubjectToAcl || roleIds.Contains(acl.CustomerRoleId) + select p; + } + } + + var deliverTimeIds = GetIdList(filters, "deliveryid"); + if (deliverTimeIds.Any()) + { + query = query.Where(x => x.DeliveryTimeId != null && deliverTimeIds.Contains(x.DeliveryTimeId.Value)); + } + + var parentProductIds = GetIdList(filters, "parentid"); + if (parentProductIds.Any()) + { + query = query.Where(x => parentProductIds.Contains(x.ParentGroupedProductId)); + } + + var conditions = GetIdList(filters, "condition"); + if (conditions.Any()) + { + query = query.Where(x => conditions.Contains((int)x.Condition)); + } + + foreach (IAttributeSearchFilter filter in filters) + { + var rf = filter as IRangeSearchFilter; + + if (filter.FieldName == "id") + { + if (rf != null) + { + var lower = filter.Term as int?; + var upper = rf.UpperTerm as int?; + + if (lower.HasValue) + { + if (rf.IncludesLower) + query = query.Where(x => x.Id >= lower.Value); + else + query = query.Where(x => x.Id > lower.Value); + } + + if (upper.HasValue) + { + if (rf.IncludesUpper) + query = query.Where(x => x.Id <= upper.Value); + else + query = query.Where(x => x.Id < upper.Value); + } + } + } + else if (filter.FieldName == "categoryid") + { + if (rf != null && 1 == ((filter.Term as int?) ?? 0) && int.MaxValue == ((rf.UpperTerm as int?) ?? 0)) + { + isGroupingRequired = true; + // Has any category. + query = query.Where(x => x.ProductCategories.Count > 0); + } + } + else if (filter.FieldName == "manufacturerid") + { + if (rf != null && 1 == ((filter.Term as int?) ?? 0) && int.MaxValue == ((rf.UpperTerm as int?) ?? 0)) + { + isGroupingRequired = true; + // Has any manufacturer. + query = query.Where(x => x.ProductManufacturers.Count > 0); + } + } + else if (filter.FieldName == "published") + { + query = query.Where(x => x.Published == (bool)filter.Term); + } + else if (filter.FieldName == "availablestart") + { + if (rf != null) + { + var lower = filter.Term as DateTime?; + var upper = rf.UpperTerm as DateTime?; + + if (lower.HasValue) + { + if (rf.IncludesLower) + query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc >= lower.Value); + else + query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc > lower.Value); + } + + if (upper.HasValue) + { + if (rf.IncludesLower) + query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc <= upper.Value); + else + query = query.Where(x => !x.AvailableStartDateTimeUtc.HasValue || x.AvailableStartDateTimeUtc < upper.Value); + } + } + } + else if (filter.FieldName == "availableend") + { + if (rf != null) + { + var lower = filter.Term as DateTime?; + var upper = rf.UpperTerm as DateTime?; + + if (lower.HasValue) + { + if (rf.IncludesLower) + query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc >= lower.Value); + else + query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc > lower.Value); + } + + if (upper.HasValue) + { + if (rf.IncludesLower) + query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc <= upper.Value); + else + query = query.Where(x => !x.AvailableEndDateTimeUtc.HasValue || x.AvailableEndDateTimeUtc < upper.Value); + } + } + } + else if (filter.FieldName == "visibility") + { + var visibility = (ProductVisibility)filter.Term; + switch (visibility) + { + case ProductVisibility.SearchResults: + query = query.Where(x => x.Visibility <= visibility); + break; + default: + query = query.Where(x => x.Visibility == visibility); + break; + } + } + else if (filter.FieldName == "showonhomepage") + { + query = query.Where(p => p.ShowOnHomePage == (bool)filter.Term); + } + else if (filter.FieldName == "download") + { + query = query.Where(p => p.IsDownload == (bool)filter.Term); + } + else if (filter.FieldName == "recurring") + { + query = query.Where(p => p.IsRecurring == (bool)filter.Term); + } + else if (filter.FieldName == "shipenabled") + { + query = query.Where(p => p.IsShipEnabled == (bool)filter.Term); + } + else if (filter.FieldName == "shipfree") + { + query = query.Where(p => p.IsFreeShipping == (bool)filter.Term); + } + else if (filter.FieldName == "taxexempt") + { + query = query.Where(p => p.IsTaxExempt == (bool)filter.Term); + } + else if (filter.FieldName == "esd") + { + query = query.Where(p => p.IsEsd == (bool)filter.Term); + } + else if (filter.FieldName == "discount") + { + query = query.Where(p => p.HasDiscountsApplied == (bool)filter.Term); + } + else if (filter.FieldName == "typeid") + { + query = query.Where(x => x.ProductTypeId == (int)filter.Term); + } + else if (filter.FieldName == "stockquantity") + { + if (rf != null) + { + var lower = filter.Term as int?; + var upper = rf.UpperTerm as int?; + + if (lower.HasValue) + { + if (rf.IncludesLower) + query = query.Where(x => x.StockQuantity >= lower.Value); + else + query = query.Where(x => x.StockQuantity > lower.Value); + } + + if (upper.HasValue) + { + if (rf.IncludesUpper) + query = query.Where(x => x.StockQuantity <= upper.Value); + else + query = query.Where(x => x.StockQuantity < upper.Value); + } + } + else + { + if (filter.Occurence == SearchFilterOccurence.MustNot) + query = query.Where(x => x.StockQuantity != (int)filter.Term); + else + query = query.Where(x => x.StockQuantity == (int)filter.Term); + } + } + else if (filter.FieldName == "rating") + { + if (rf != null) + { + var lower = filter.Term as double?; + var upper = rf.UpperTerm as double?; + + if (lower.HasValue) + { + if (rf.IncludesLower) + query = query.Where(x => x.ApprovedTotalReviews != 0 && ((double)x.ApprovedRatingSum / (double)x.ApprovedTotalReviews) >= lower.Value); + else + query = query.Where(x => x.ApprovedTotalReviews != 0 && ((double)x.ApprovedRatingSum / (double)x.ApprovedTotalReviews) > lower.Value); + } + + if (upper.HasValue) + { + if (rf.IncludesUpper) + query = query.Where(x => x.ApprovedTotalReviews != 0 && ((double)x.ApprovedRatingSum / (double)x.ApprovedTotalReviews) <= upper.Value); + else + query = query.Where(x => x.ApprovedTotalReviews != 0 && ((double)x.ApprovedRatingSum / (double)x.ApprovedTotalReviews) < upper.Value); + } + } + else + { + if (filter.Occurence == SearchFilterOccurence.MustNot) + query = query.Where(x => x.ApprovedTotalReviews != 0 && ((double)x.ApprovedRatingSum / (double)x.ApprovedTotalReviews) != (double)filter.Term); + else + query = query.Where(x => x.ApprovedTotalReviews != 0 && ((double)x.ApprovedRatingSum / (double)x.ApprovedTotalReviews) == (double)filter.Term); + } + } + else if (filter.FieldName == "available") + { + query = query.Where(x => + x.ManageInventoryMethodId == (int)ManageInventoryMethod.DontManageStock || + (x.ManageInventoryMethodId == (int)ManageInventoryMethod.ManageStock && (x.StockQuantity > 0 || x.BackorderModeId != (int)BackorderMode.NoBackorders)) || + (x.ManageInventoryMethodId == (int)ManageInventoryMethod.ManageStockByAttributes && x.ProductVariantAttributeCombinations.Any(pvac => pvac.StockQuantity > 0 || pvac.AllowOutOfStockOrders)) + ); + } + else if (filter.FieldName.StartsWith("price")) + { + if (rf != null) + { + var lower = filter.Term as double?; + var upper = rf.UpperTerm as double?; + + if (lower.HasValue) + { + var minPrice = Convert.ToDecimal(lower.Value); + + query = query.Where(x => + ((x.SpecialPrice.HasValue && + ((!x.SpecialPriceStartDateTimeUtc.HasValue || x.SpecialPriceStartDateTimeUtc.Value < utcNow) && + (!x.SpecialPriceEndDateTimeUtc.HasValue || x.SpecialPriceEndDateTimeUtc.Value > utcNow))) && + (x.SpecialPrice >= minPrice)) + || + ((!x.SpecialPrice.HasValue || + ((x.SpecialPriceStartDateTimeUtc.HasValue && x.SpecialPriceStartDateTimeUtc.Value > utcNow) || + (x.SpecialPriceEndDateTimeUtc.HasValue && x.SpecialPriceEndDateTimeUtc.Value < utcNow))) && + (x.Price >= minPrice)) + ); + } + + if (upper.HasValue) + { + var maxPrice = Convert.ToDecimal(upper); + + query = query.Where(x => + ((x.SpecialPrice.HasValue && + ((!x.SpecialPriceStartDateTimeUtc.HasValue || x.SpecialPriceStartDateTimeUtc.Value < utcNow) && + (!x.SpecialPriceEndDateTimeUtc.HasValue || x.SpecialPriceEndDateTimeUtc.Value > utcNow))) && + (x.SpecialPrice <= maxPrice)) + || + ((!x.SpecialPrice.HasValue || + ((x.SpecialPriceStartDateTimeUtc.HasValue && x.SpecialPriceStartDateTimeUtc.Value > utcNow) || + (x.SpecialPriceEndDateTimeUtc.HasValue && x.SpecialPriceEndDateTimeUtc.Value < utcNow))) && + (x.Price <= maxPrice)) + ); + } + } + else + { + var price = Convert.ToDecimal(filter.Term); + + if (filter.Occurence == SearchFilterOccurence.MustNot) + { + query = query.Where(x => + ((x.SpecialPrice.HasValue && + ((!x.SpecialPriceStartDateTimeUtc.HasValue || x.SpecialPriceStartDateTimeUtc.Value < utcNow) && + (!x.SpecialPriceEndDateTimeUtc.HasValue || x.SpecialPriceEndDateTimeUtc.Value > utcNow))) && + (x.SpecialPrice != price)) + || + ((!x.SpecialPrice.HasValue || + ((x.SpecialPriceStartDateTimeUtc.HasValue && x.SpecialPriceStartDateTimeUtc.Value > utcNow) || + (x.SpecialPriceEndDateTimeUtc.HasValue && x.SpecialPriceEndDateTimeUtc.Value < utcNow))) && + (x.Price != price)) + ); + } + else + { + query = query.Where(x => + ((x.SpecialPrice.HasValue && + ((!x.SpecialPriceStartDateTimeUtc.HasValue || x.SpecialPriceStartDateTimeUtc.Value < utcNow) && + (!x.SpecialPriceEndDateTimeUtc.HasValue || x.SpecialPriceEndDateTimeUtc.Value > utcNow))) && + (x.SpecialPrice == price)) + || + ((!x.SpecialPrice.HasValue || + ((x.SpecialPriceStartDateTimeUtc.HasValue && x.SpecialPriceStartDateTimeUtc.Value > utcNow) || + (x.SpecialPriceEndDateTimeUtc.HasValue && x.SpecialPriceEndDateTimeUtc.Value < utcNow))) && + (x.Price == price)) + ); + } + } + } + else if (filter.FieldName == "createdon") + { + if (rf != null) + { + var lower = filter.Term as DateTime?; + var upper = rf.UpperTerm as DateTime?; + + if (lower.HasValue) + { + if (rf.IncludesLower) + query = query.Where(x => x.CreatedOnUtc >= lower.Value); + else + query = query.Where(x => x.CreatedOnUtc > lower.Value); + } + + if (upper.HasValue) + { + if (rf.IncludesLower) + query = query.Where(x => x.CreatedOnUtc <= upper.Value); + else + query = query.Where(x => x.CreatedOnUtc < upper.Value); + } + } + else + { + if (filter.Occurence == SearchFilterOccurence.MustNot) + query = query.Where(x => x.CreatedOnUtc != (DateTime)filter.Term); + else + query = query.Where(x => x.CreatedOnUtc == (DateTime)filter.Term); + } + } + else if (filter.FieldName == "storeid") + { + if (!QuerySettings.IgnoreMultiStore) + { + 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 - from sm in psm.DefaultIfEmpty() - where !p.LimitedToStores || sm.StoreId == storeId - select p; - } - } - } - } + 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 + from sm in psm.DefaultIfEmpty() + where !p.LimitedToStores || sm.StoreId == storeId + select p; + } + } + } + } #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) + { + query = + from p in query + group p by p.Id into grp + orderby grp.Key + select grp.FirstOrDefault(); + } #region Sorting foreach (var sort in searchQuery.Sorting) - { - if (sort.FieldName.IsEmpty()) - { - // Sort by relevance. - if (categoryId != 0) - { - query = OrderBy(ref ordered, query, x => x.ProductCategories.Where(pc => pc.CategoryId == categoryId).FirstOrDefault().DisplayOrder); - } - else if (manufacturerId != 0) - { - query = OrderBy(ref ordered, query, x => x.ProductManufacturers.Where(pm => pm.ManufacturerId == manufacturerId).FirstOrDefault().DisplayOrder); - } - else if (FindFilter(searchQuery.Filters, "parentid") != null) - { - query = OrderBy(ref ordered, query, x => x.DisplayOrder); - } - else - { - query = OrderBy(ref ordered, query, x => x.Name); - } - } - else if (sort.FieldName == "createdon") - { - query = OrderBy(ref ordered, query, x => x.CreatedOnUtc, sort.Descending); - } - else if (sort.FieldName == "name") - { - query = OrderBy(ref ordered, query, x => x.Name, sort.Descending); - } - else if (sort.FieldName == "price") - { - query = OrderBy(ref ordered, query, x => x.Price, sort.Descending); - } - else - { - query = OrderBy(ref ordered, query, x => x.Name); - } - } - - if (!ordered) - { - if (FindFilter(searchQuery.Filters, "parentid") != null) - { - query = query.OrderBy(x => x.DisplayOrder); - } - else - { - query = query.OrderBy(x => x.Id); - } - } - - #endregion - - return query; - } - - protected virtual IDictionary GetFacets(CatalogSearchQuery searchQuery, int totalHits) - { - var result = new Dictionary(); - var storeId = searchQuery.StoreId ?? _services.StoreContext.CurrentStore.Id; - var languageId = searchQuery.LanguageId ?? _services.WorkContext.WorkingLanguage.Id; - - foreach (var key in searchQuery.FacetDescriptors.Keys) - { - var descriptor = searchQuery.FacetDescriptors[key]; - var facets = new List(); - var kind = FacetGroup.GetKindByKey("Catalog", key); - - switch (kind) - { - case FacetGroupKind.Category: - case FacetGroupKind.Brand: - case FacetGroupKind.DeliveryTime: - case FacetGroupKind.Rating: - case FacetGroupKind.Price: - if (totalHits == 0 && !descriptor.Values.Any(x => x.IsSelected)) - { - continue; - } - break; - } - - if (kind == FacetGroupKind.Category) - { - var categoryTree = _categoryService.GetCategoryTree(0, false, storeId); - var categories = categoryTree.Flatten(false); - - if (descriptor.MaxChoicesCount > 0) - { - categories = categories.Take(descriptor.MaxChoicesCount); - } - - var nameQuery = _localizedPropertyRepository.TableUntracked - .Where(x => x.LocaleKeyGroup == "Category" && x.LocaleKey == "Name" && x.LanguageId == languageId); - var names = nameQuery.ToList().ToDictionarySafe(x => x.EntityId, x => x.LocaleValue); - - foreach (var category in categories) - { - string label = null; - names.TryGetValue(category.Id, out label); - - facets.Add(new Facet(new FacetValue(category.Id, IndexTypeCode.Int32) - { - IsSelected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(category.Id)), - Label = label.HasValue() ? label : category.Name, - DisplayOrder = category.DisplayOrder - })); - } - } - else if (kind == FacetGroupKind.Brand) - { - var manufacturers = _manufacturerService.GetAllManufacturers(null, storeId); - if (descriptor.MaxChoicesCount > 0) - { - manufacturers = manufacturers.Take(descriptor.MaxChoicesCount).ToList(); - } - - var nameQuery = _localizedPropertyRepository.TableUntracked - .Where(x => x.LocaleKeyGroup == "Manufacturer" && x.LocaleKey == "Name" && x.LanguageId == languageId); - var names = nameQuery.ToList().ToDictionarySafe(x => x.EntityId, x => x.LocaleValue); - - foreach (var manu in manufacturers) - { - string label = null; - names.TryGetValue(manu.Id, out label); - - facets.Add(new Facet(new FacetValue(manu.Id, IndexTypeCode.Int32) - { - IsSelected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(manu.Id)), - Label = label.HasValue() ? label : manu.Name, - DisplayOrder = manu.DisplayOrder - })); - } - } - else if (kind == FacetGroupKind.DeliveryTime) - { - var deliveryTimes = _deliveryTimeService.GetAllDeliveryTimes(); - var nameQuery = _localizedPropertyRepository.TableUntracked - .Where(x => x.LocaleKeyGroup == "DeliveryTime" && x.LocaleKey == "Name" && x.LanguageId == languageId); - var names = nameQuery.ToList().ToDictionarySafe(x => x.EntityId, x => x.LocaleValue); - - foreach (var deliveryTime in deliveryTimes) - { - if (descriptor.MaxChoicesCount > 0 && facets.Count >= descriptor.MaxChoicesCount) - break; - - string label = null; - names.TryGetValue(deliveryTime.Id, out label); - - facets.Add(new Facet(new FacetValue(deliveryTime.Id, IndexTypeCode.Int32) - { - IsSelected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(deliveryTime.Id)), - Label = label.HasValue() ? label : deliveryTime.Name, - DisplayOrder = deliveryTime.DisplayOrder - })); - } - } - else if (kind == FacetGroupKind.Price) - { - 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); - minPrice = FacetUtility.MakePriceEven(minPrice); - maxPrice = FacetUtility.MakePriceEven(maxPrice); - - for (var i = 0; i < _priceThresholds.Length; ++i) - { - if (descriptor.MaxChoicesCount > 0 && facets.Count >= descriptor.MaxChoicesCount) - break; - - var price = _priceThresholds[i]; - if (price < minPrice) - continue; - - if (price >= maxPrice) - i = int.MaxValue - 1; - - var selected = descriptor.Values.Any(x => x.IsSelected && x.Value == null && x.UpperValue != null && (double)x.UpperValue == price); - if (selected) - hasActivePredefinedFacet = true; - - facets.Add(new Facet(new FacetValue(null, price, IndexTypeCode.Double, false, true) - { - DisplayOrder = ++count, - IsSelected = selected - })); - } - - // Add facet for custom price range. - var priceDescriptorValue = descriptor.Values.FirstOrDefault(); - - var customPriceFacetValue = new FacetValue( - priceDescriptorValue != null && !hasActivePredefinedFacet ? priceDescriptorValue.Value : null, - priceDescriptorValue != null && !hasActivePredefinedFacet ? priceDescriptorValue.UpperValue : null, - IndexTypeCode.Double, - true, - true); - - customPriceFacetValue.IsSelected = customPriceFacetValue.Value != null || customPriceFacetValue.UpperValue != null; - - if (!(totalHits == 0 && !customPriceFacetValue.IsSelected)) - { - facets.Insert(0, new Facet("custom", customPriceFacetValue)); - } - } - else if (kind == FacetGroupKind.Rating) - { - foreach (var rating in FacetUtility.GetRatings()) - { - var newFacet = new Facet(rating); - newFacet.Value.IsSelected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(rating.Value)); - facets.Add(newFacet); - } - } - else if (kind == FacetGroupKind.Availability || kind == FacetGroupKind.NewArrivals) - { - var value = descriptor.Values.FirstOrDefault(); - if (value != null) - { - if (kind == FacetGroupKind.NewArrivals && totalHits == 0 && !value.IsSelected) - { - continue; - } - - var newValue = value.Clone(); - newValue.Value = true; - newValue.TypeCode = IndexTypeCode.Boolean; - newValue.IsRange = false; - newValue.IsSelected = value.IsSelected; - - facets.Add(new Facet(newValue)); - } - } - - if (facets.Any(x => x.Published)) - { - //facets.Each(x => $"{key} {x.Value.ToString()}".Dump()); - - result.Add(key, new FacetGroup( + { + if (sort.FieldName.IsEmpty()) + { + // Sort by relevance. + if (categoryId != 0) + { + query = OrderBy(ref ordered, query, x => x.ProductCategories.Where(pc => pc.CategoryId == categoryId).FirstOrDefault().DisplayOrder); + } + else if (manufacturerId != 0) + { + query = OrderBy(ref ordered, query, x => x.ProductManufacturers.Where(pm => pm.ManufacturerId == manufacturerId).FirstOrDefault().DisplayOrder); + } + } + else if (sort.FieldName == "createdon") + { + query = OrderBy(ref ordered, query, x => x.CreatedOnUtc, sort.Descending); + } + else if (sort.FieldName == "name") + { + query = OrderBy(ref ordered, query, x => x.Name, sort.Descending); + } + else if (sort.FieldName == "price") + { + query = OrderBy(ref ordered, query, x => x.Price, sort.Descending); + } + } + + if (!ordered) + { + if (FindFilter(searchQuery.Filters, "parentid") != null) + { + query = query.OrderBy(x => x.DisplayOrder); + } + else + { + query = query.OrderBy(x => x.Id); + } + } + + #endregion + + return query; + } + + protected virtual IDictionary GetFacets(CatalogSearchQuery searchQuery, int totalHits) + { + var result = new Dictionary(); + var storeId = searchQuery.StoreId ?? _services.StoreContext.CurrentStore.Id; + var languageId = searchQuery.LanguageId ?? _services.WorkContext.WorkingLanguage.Id; + + foreach (var key in searchQuery.FacetDescriptors.Keys) + { + var descriptor = searchQuery.FacetDescriptors[key]; + var facets = new List(); + var kind = FacetGroup.GetKindByKey("Catalog", key); + + switch (kind) + { + case FacetGroupKind.Category: + case FacetGroupKind.Brand: + case FacetGroupKind.DeliveryTime: + case FacetGroupKind.Rating: + case FacetGroupKind.Price: + if (totalHits == 0 && !descriptor.Values.Any(x => x.IsSelected)) + { + continue; + } + break; + } + + if (kind == FacetGroupKind.Category) + { + var categoryTree = _categoryService.GetCategoryTree(0, false, storeId); + var categories = categoryTree.Flatten(false); + + if (descriptor.MaxChoicesCount > 0) + { + categories = categories.Take(descriptor.MaxChoicesCount); + } + + var nameQuery = _localizedPropertyRepository.TableUntracked + .Where(x => x.LocaleKeyGroup == "Category" && x.LocaleKey == "Name" && x.LanguageId == languageId); + var names = nameQuery.ToList().ToDictionarySafe(x => x.EntityId, x => x.LocaleValue); + + foreach (var category in categories) + { + names.TryGetValue(category.Id, out var label); + + facets.Add(new Facet(new FacetValue(category.Id, IndexTypeCode.Int32) + { + IsSelected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(category.Id)), + Label = label.HasValue() ? label : category.Name, + DisplayOrder = category.DisplayOrder + })); + } + } + else if (kind == FacetGroupKind.Brand) + { + var manufacturers = _manufacturerService.GetAllManufacturers(null, storeId); + if (descriptor.MaxChoicesCount > 0) + { + manufacturers = manufacturers.Take(descriptor.MaxChoicesCount).ToList(); + } + + var nameQuery = _localizedPropertyRepository.TableUntracked + .Where(x => x.LocaleKeyGroup == "Manufacturer" && x.LocaleKey == "Name" && x.LanguageId == languageId); + var names = nameQuery.ToList().ToDictionarySafe(x => x.EntityId, x => x.LocaleValue); + + foreach (var manu in manufacturers) + { + names.TryGetValue(manu.Id, out var label); + + facets.Add(new Facet(new FacetValue(manu.Id, IndexTypeCode.Int32) + { + IsSelected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(manu.Id)), + Label = label.HasValue() ? label : manu.Name, + DisplayOrder = manu.DisplayOrder + })); + } + } + else if (kind == FacetGroupKind.DeliveryTime) + { + var deliveryTimes = _deliveryTimeService.GetAllDeliveryTimes(); + var nameQuery = _localizedPropertyRepository.TableUntracked + .Where(x => x.LocaleKeyGroup == "DeliveryTime" && x.LocaleKey == "Name" && x.LanguageId == languageId); + var names = nameQuery.ToList().ToDictionarySafe(x => x.EntityId, x => x.LocaleValue); + + foreach (var deliveryTime in deliveryTimes) + { + if (descriptor.MaxChoicesCount > 0 && facets.Count >= descriptor.MaxChoicesCount) + break; + + names.TryGetValue(deliveryTime.Id, out var label); + + facets.Add(new Facet(new FacetValue(deliveryTime.Id, IndexTypeCode.Int32) + { + IsSelected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(deliveryTime.Id)), + Label = label.HasValue() ? label : deliveryTime.Name, + DisplayOrder = deliveryTime.DisplayOrder + })); + } + } + else if (kind == FacetGroupKind.Price) + { + 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); + minPrice = FacetUtility.MakePriceEven(minPrice); + maxPrice = FacetUtility.MakePriceEven(maxPrice); + + for (var i = 0; i < _priceThresholds.Length; ++i) + { + if (descriptor.MaxChoicesCount > 0 && facets.Count >= descriptor.MaxChoicesCount) + break; + + var price = _priceThresholds[i]; + if (price < minPrice) + continue; + + if (price >= maxPrice) + i = int.MaxValue - 1; + + var selected = descriptor.Values.Any(x => x.IsSelected && x.Value == null && x.UpperValue != null && (double)x.UpperValue == price); + if (selected) + hasActivePredefinedFacet = true; + + facets.Add(new Facet(new FacetValue(null, price, IndexTypeCode.Double, false, true) + { + DisplayOrder = ++count, + IsSelected = selected + })); + } + + // Add facet for custom price range. + var priceDescriptorValue = descriptor.Values.FirstOrDefault(); + + var customPriceFacetValue = new FacetValue( + priceDescriptorValue != null && !hasActivePredefinedFacet ? priceDescriptorValue.Value : null, + priceDescriptorValue != null && !hasActivePredefinedFacet ? priceDescriptorValue.UpperValue : null, + IndexTypeCode.Double, + true, + true); + + customPriceFacetValue.IsSelected = customPriceFacetValue.Value != null || customPriceFacetValue.UpperValue != null; + + if (!(totalHits == 0 && !customPriceFacetValue.IsSelected)) + { + facets.Insert(0, new Facet("custom", customPriceFacetValue)); + } + } + else if (kind == FacetGroupKind.Rating) + { + foreach (var rating in FacetUtility.GetRatings()) + { + var newFacet = new Facet(rating); + newFacet.Value.IsSelected = descriptor.Values.Any(x => x.IsSelected && x.Value.Equals(rating.Value)); + facets.Add(newFacet); + } + } + else if (kind == FacetGroupKind.Availability || kind == FacetGroupKind.NewArrivals) + { + var value = descriptor.Values.FirstOrDefault(); + if (value != null) + { + if (kind == FacetGroupKind.NewArrivals && totalHits == 0 && !value.IsSelected) + { + continue; + } + + var newValue = value.Clone(); + newValue.Value = true; + newValue.TypeCode = IndexTypeCode.Boolean; + newValue.IsRange = false; + newValue.IsSelected = value.IsSelected; + + facets.Add(new Facet(newValue)); + } + } + + if (facets.Any(x => x.Published)) + { + //facets.Each(x => $"{key} {x.Value.ToString()}".Dump()); + + result.Add(key, new FacetGroup( "Catalog", key, - descriptor.Label, - descriptor.IsMultiSelect, - false, - descriptor.DisplayOrder, - facets.OrderBy(descriptor))); - } - } - - return result; - } + descriptor.Label, + descriptor.IsMultiSelect, + false, + descriptor.DisplayOrder, + facets.OrderBy(descriptor))); + } + } + + return result; + } #endregion - public CatalogSearchResult Search(CatalogSearchQuery searchQuery, ProductLoadFlags loadFlags = ProductLoadFlags.None, bool direct = false) - { - _eventPublisher.Publish(new CatalogSearchingEvent(searchQuery)); + public CatalogSearchResult Search(CatalogSearchQuery searchQuery, ProductLoadFlags loadFlags = ProductLoadFlags.None, bool direct = false) + { + _services.EventPublisher.Publish(new CatalogSearchingEvent(searchQuery, true)); - var totalHits = 0; - Func> hitsFactory = null; - IDictionary facets = null; + var totalHits = 0; + int[] hitsEntityIds = null; + Func> hitsFactory = null; + IDictionary facets = null; - if (searchQuery.Take > 0) - { - var query = GetProductQuery(searchQuery, null); + if (searchQuery.Take > 0) + { + var query = GetProductQuery(searchQuery, null); - totalHits = query.Count(); + totalHits = query.Count(); - // Fix paging boundaries - if (searchQuery.Skip > 0 && searchQuery.Skip >= totalHits) - { - searchQuery.Slice((totalHits / searchQuery.Take) * searchQuery.Take, searchQuery.Take); - } + // Fix paging boundaries + if (searchQuery.Skip > 0 && searchQuery.Skip >= totalHits) + { + searchQuery.Slice((totalHits / searchQuery.Take) * searchQuery.Take, searchQuery.Take); + } - if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithHits)) - { + if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithHits)) + { var skip = searchQuery.PageIndex * searchQuery.Take; query = query - .Skip(() => skip) - .Take(() => searchQuery.Take); - - var ids = query.Select(x => x.Id).ToArray(); - hitsFactory = () => _productService.GetProductsByIds(ids, loadFlags); - } - - if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithFacets) && searchQuery.FacetDescriptors.Any()) - { - facets = GetFacets(searchQuery, totalHits); - } - } - - var result = new CatalogSearchResult( - null, - searchQuery, - totalHits, - hitsFactory, - null, - facets); - - _eventPublisher.Publish(new CatalogSearchedEvent(searchQuery, result)); - - return result; - } - - public IQueryable PrepareQuery(CatalogSearchQuery searchQuery, IQueryable baseQuery = null) - { - return GetProductQuery(searchQuery, baseQuery); - } - } + .Skip(() => skip) + .Take(() => searchQuery.Take); + + hitsEntityIds = query.Select(x => x.Id).ToArray(); + hitsFactory = () => _productService.GetProductsByIds(hitsEntityIds, loadFlags); + } + + if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithFacets) && searchQuery.FacetDescriptors.Any()) + { + facets = GetFacets(searchQuery, totalHits); + } + } + + var result = new CatalogSearchResult( + null, + searchQuery, + totalHits, + hitsEntityIds, + hitsFactory, + null, + facets); + + _services.EventPublisher.Publish(new CatalogSearchedEvent(searchQuery, result)); + + return result; + } + + public IQueryable PrepareQuery(CatalogSearchQuery searchQuery, IQueryable baseQuery = null) + { + return GetProductQuery(searchQuery, baseQuery); + } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryAliasMapper.cs b/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryAliasMapper.cs index 6cfae38fd0..18e94da4a2 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryAliasMapper.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryAliasMapper.cs @@ -15,60 +15,60 @@ namespace SmartStore.Services.Search.Modelling { public class CatalogSearchQueryAliasMapper : ICatalogSearchQueryAliasMapper - { - private const string ALL_ATTRIBUTE_ID_BY_ALIAS_KEY = "search:attribute.id.alias.mappings.all"; - private const string ALL_ATTRIBUTE_ALIAS_BY_ID_KEY = "search:attribute.alias.id.mappings.all"; - private const string ALL_COMMONFACET_ALIAS_BY_KIND_KEY = "search:commonfacet.alias.kind.mappings.all"; - - private const string ALL_VARIANT_ID_BY_ALIAS_KEY = "search:variant.id.alias.mappings.all"; - private const string ALL_VARIANT_ALIAS_BY_ID_KEY = "search:variant.alias.id.mappings.all"; - - private readonly ICacheManager _cacheManager; - private readonly IRepository _localizedPropertyRepository; - private readonly IRepository _productAttributeRepository; - private readonly IRepository _productVariantAttributeValueRepository; - private readonly ISpecificationAttributeService _specificationAttributeService; - private readonly ISettingService _settingService; - private readonly ILanguageService _languageService; - - public CatalogSearchQueryAliasMapper( - ICacheManager cacheManager, - IRepository localizedPropertyRepository, - IRepository productAttributeRepository, - IRepository productVariantAttributeValueRepository, - ISpecificationAttributeService specificationAttributeService, - ISettingService settingService, - ILanguageService languageService) - { - _cacheManager = cacheManager; - _localizedPropertyRepository = localizedPropertyRepository; - _productAttributeRepository = productAttributeRepository; - _productVariantAttributeValueRepository = productVariantAttributeValueRepository; - _specificationAttributeService = specificationAttributeService; - _settingService = settingService; - _languageService = languageService; - } - - protected string CreateKey(string prefix, int languageId, string alias) - { - return $"{prefix}.{languageId}.{alias}"; - } - protected string CreateKey(string prefix, int languageId, int attributeId) - { - return $"{prefix}.{languageId}.{attributeId}"; - } - - protected string CreateOptionKey(string prefix, int languageId, int attributeId, string optionAlias) - { - return $"{prefix}.{languageId}.{attributeId}.{optionAlias}"; - } - protected string CreateOptionKey(string prefix, int languageId, int optionId) - { - return $"{prefix}.{languageId}.{optionId}"; - } - - protected void CachedLocalizedAlias(string localeKeyGroup, Action caching) - { + { + private const string ALL_ATTRIBUTE_ID_BY_ALIAS_KEY = "search:attribute.id.alias.mappings.all"; + private const string ALL_ATTRIBUTE_ALIAS_BY_ID_KEY = "search:attribute.alias.id.mappings.all"; + private const string ALL_COMMONFACET_ALIAS_BY_KIND_KEY = "search:commonfacet.alias.kind.mappings.all"; + + private const string ALL_VARIANT_ID_BY_ALIAS_KEY = "search:variant.id.alias.mappings.all"; + private const string ALL_VARIANT_ALIAS_BY_ID_KEY = "search:variant.alias.id.mappings.all"; + + private readonly ICacheManager _cacheManager; + private readonly IRepository _localizedPropertyRepository; + private readonly IRepository _productAttributeRepository; + private readonly IRepository _productVariantAttributeValueRepository; + private readonly ISpecificationAttributeService _specificationAttributeService; + private readonly ISettingService _settingService; + private readonly ILanguageService _languageService; + + public CatalogSearchQueryAliasMapper( + ICacheManager cacheManager, + IRepository localizedPropertyRepository, + IRepository productAttributeRepository, + IRepository productVariantAttributeValueRepository, + ISpecificationAttributeService specificationAttributeService, + ISettingService settingService, + ILanguageService languageService) + { + _cacheManager = cacheManager; + _localizedPropertyRepository = localizedPropertyRepository; + _productAttributeRepository = productAttributeRepository; + _productVariantAttributeValueRepository = productVariantAttributeValueRepository; + _specificationAttributeService = specificationAttributeService; + _settingService = settingService; + _languageService = languageService; + } + + protected string CreateKey(string prefix, int languageId, string alias) + { + return $"{prefix}.{languageId}.{alias}"; + } + protected string CreateKey(string prefix, int languageId, int attributeId) + { + return $"{prefix}.{languageId}.{attributeId}"; + } + + protected string CreateOptionKey(string prefix, int languageId, int attributeId, string optionAlias) + { + return $"{prefix}.{languageId}.{attributeId}.{optionAlias}"; + } + protected string CreateOptionKey(string prefix, int languageId, int optionId) + { + return $"{prefix}.{languageId}.{optionId}"; + } + + protected void CachedLocalizedAlias(string localeKeyGroup, Action caching) + { var properties = _localizedPropertyRepository.TableUntracked .Where(x => x.LocaleKeyGroup == localeKeyGroup && x.LocaleKey == "Alias") .ToList(); @@ -78,418 +78,418 @@ protected void CachedLocalizedAlias(string localeKeyGroup, Action !string.IsNullOrWhiteSpace(x.LocaleValue)) .ToList() .ForEach(caching); - } - - #region Specification Attributes - - protected virtual IDictionary GetAttributeIdByAliasMappings() - { - return _cacheManager.Get(ALL_ATTRIBUTE_ID_BY_ALIAS_KEY, () => - { - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - var optionIdMappings = new Dictionary(); - IPagedList attributes = null; - var attributeId = 0; - var pageIndex = 0; - - var query = _specificationAttributeService.GetSpecificationAttributes() - .Expand(x => x.SpecificationAttributeOptions) - .OrderBy(x => x.Id); - - do - { - attributes = new PagedList(query, pageIndex++, 500); - - foreach (var attribute in attributes) - { - if (attribute.Alias.HasValue()) - { - result[CreateKey("attr", 0, attribute.Alias)] = attribute.Id; - } - - foreach (var option in attribute.SpecificationAttributeOptions) - { - optionIdMappings[option.Id] = option.SpecificationAttributeId; - - if (option.Alias.HasValue()) - { - result[CreateOptionKey("attr.option", 0, attribute.Id, option.Alias)] = option.Id; - } - } - } - } - while (attributes.HasNextPage); - - attributes.Clear(); - - CachedLocalizedAlias("SpecificationAttribute", x => result[CreateKey("attr", x.LanguageId, x.LocaleValue)] = x.EntityId); - CachedLocalizedAlias("SpecificationAttributeOption", x => - { - if (optionIdMappings.TryGetValue(x.EntityId, out attributeId)) - result[CreateOptionKey("attr.option", x.LanguageId, attributeId, x.LocaleValue)] = x.EntityId; - }); - - return result; - }); - } - - protected virtual IDictionary GetAttributeAliasByIdMappings() - { - return _cacheManager.Get(ALL_ATTRIBUTE_ALIAS_BY_ID_KEY, () => - { - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - IPagedList attributes = null; - var pageIndex = 0; - - var query = _specificationAttributeService.GetSpecificationAttributes() - .Expand(x => x.SpecificationAttributeOptions) - .OrderBy(x => x.Id); - - do - { - attributes = new PagedList(query, pageIndex++, 500); - - foreach (var attribute in attributes) - { - if (attribute.Alias.HasValue()) - { - result[CreateKey("attr", 0, attribute.Id)] = attribute.Alias; - } - - foreach (var option in attribute.SpecificationAttributeOptions.Where(x => x.Alias.HasValue())) - { - result[CreateOptionKey("attr.option", 0, option.Id)] = option.Alias; - } - } - } - while (attributes.HasNextPage); - - attributes.Clear(); - - CachedLocalizedAlias("SpecificationAttribute", x => result[CreateKey("attr", x.LanguageId, x.EntityId)] = x.LocaleValue); - CachedLocalizedAlias("SpecificationAttributeOption", x => result[CreateOptionKey("attr.option", x.LanguageId, x.EntityId)] = x.LocaleValue); - - return result; - }); - } - - public void ClearAttributeCache() - { - _cacheManager.Remove(ALL_ATTRIBUTE_ID_BY_ALIAS_KEY); - _cacheManager.Remove(ALL_ATTRIBUTE_ALIAS_BY_ID_KEY); - } - - public int GetAttributeIdByAlias(string attributeAlias, int languageId = 0) - { - var result = 0; - - if (attributeAlias.HasValue()) - { - var mappings = GetAttributeIdByAliasMappings(); - - if (!mappings.TryGetValue(CreateKey("attr", languageId, attributeAlias), out result) && languageId != 0) - { - mappings.TryGetValue(CreateKey("attr", 0, attributeAlias), out result); - } - } - - return result; - } - - public int GetAttributeOptionIdByAlias(string optionAlias, int attributeId, int languageId = 0) - { - var result = 0; - - if (optionAlias.HasValue() && attributeId != 0) - { - var mappings = GetAttributeIdByAliasMappings(); - - if (!mappings.TryGetValue(CreateOptionKey("attr.option", languageId, attributeId, optionAlias), out result) && languageId != 0) - { - mappings.TryGetValue(CreateOptionKey("attr.option", 0, attributeId, optionAlias), out result); - } - } - - return result; - } - - public string GetAttributeAliasById(int attributeId, int languageId = 0) - { - string result = null; - - if (attributeId != 0) - { - var mappings = GetAttributeAliasByIdMappings(); - - if (!mappings.TryGetValue(CreateKey("attr", languageId, attributeId), out result) && languageId != 0) - { - mappings.TryGetValue(CreateKey("attr", 0, attributeId), out result); - } - } - - return result; - } - - public string GetAttributeOptionAliasById(int optionId, int languageId = 0) - { - string result = null; - - if (optionId != 0) - { - var mappings = GetAttributeAliasByIdMappings(); - - if (!mappings.TryGetValue(CreateOptionKey("attr.option", languageId, optionId), out result) && languageId != 0) - { - mappings.TryGetValue(CreateOptionKey("attr.option", 0, optionId), out result); - } - } - - return result; - } - - #endregion - - #region Product Variants - - protected virtual IDictionary GetVariantIdByAliasMappings() - { - return _cacheManager.Get(ALL_VARIANT_ID_BY_ALIAS_KEY, () => - { - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - IPagedList variants = null; - IPagedList options = null; - var variantId = 0; - var pageIndex = 0; - - var variantQuery = _productAttributeRepository.TableUntracked - .Where(x => !string.IsNullOrEmpty(x.Alias)) - .OrderBy(x => x.Id); - - var optionQuery = _productVariantAttributeValueRepository.TableUntracked - .Expand(x => x.ProductVariantAttribute) - .Expand("ProductVariantAttribute.ProductAttribute") - .Where(x => !string.IsNullOrEmpty(x.Alias)) - .OrderBy(x => x.Id); - - do - { - variants = new PagedList(variantQuery, pageIndex++, 500); - - foreach (var variant in variants) - { - result[CreateKey("vari", 0, variant.Alias)] = variant.Id; - } - } - while (variants.HasNextPage); - pageIndex = 0; - variants.Clear(); - - do - { - options = new PagedList(optionQuery, pageIndex++, 500); - - foreach (var option in options) - { - var variant = option.ProductVariantAttribute.ProductAttribute; - result[CreateOptionKey("vari.option", 0, variant.Id, option.Alias)] = option.Id; - } - } - while (options.HasNextPage); - options.Clear(); - - var optionIdMappings = _productVariantAttributeValueRepository.TableUntracked - .Expand(x => x.ProductVariantAttribute.ProductAttribute) - .Select(x => new - { - OptionId = x.Id, - VariantId = x.ProductVariantAttribute.ProductAttribute.Id - }) - .ToDictionary(x => x.OptionId, x => x.VariantId); - - CachedLocalizedAlias("ProductAttribute", x => result[CreateKey("vari", x.LanguageId, x.LocaleValue)] = x.EntityId); - CachedLocalizedAlias("ProductVariantAttributeValue", x => - { - if (optionIdMappings.TryGetValue(x.EntityId, out variantId)) - result[CreateOptionKey("vari.option", x.LanguageId, variantId, x.LocaleValue)] = x.EntityId; - }); - - return result; - }); - } - - protected virtual IDictionary GetVariantAliasByIdMappings() - { - return _cacheManager.Get(ALL_VARIANT_ALIAS_BY_ID_KEY, () => - { - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - IPagedList variants = null; - IPagedList options = null; - var pageIndex = 0; - - var variantQuery = _productAttributeRepository.TableUntracked - .Where(x => !string.IsNullOrEmpty(x.Alias)) - .OrderBy(x => x.Id); - - var optionQuery = _productVariantAttributeValueRepository.TableUntracked - .Where(x => !string.IsNullOrEmpty(x.Alias)) - .OrderBy(x => x.Id); - - do - { - variants = new PagedList(variantQuery, pageIndex++, 500); - - foreach (var variant in variants) - { - result[CreateKey("vari", 0, variant.Id)] = variant.Alias; - } - } - while (variants.HasNextPage); - pageIndex = 0; - variants.Clear(); - - do - { - options = new PagedList(optionQuery, pageIndex++, 500); - - foreach (var option in options) - { - result[CreateOptionKey("attr.option", 0, option.Id)] = option.Alias; - } - } - while (options.HasNextPage); - options.Clear(); - - CachedLocalizedAlias("ProductAttribute", x => result[CreateKey("vari", x.LanguageId, x.EntityId)] = x.LocaleValue); - CachedLocalizedAlias("ProductVariantAttributeValue", x => result[CreateOptionKey("vari.option", x.LanguageId, x.EntityId)] = x.LocaleValue); - - return result; - }); - } - - public void ClearVariantCache() - { - _cacheManager.Remove(ALL_VARIANT_ID_BY_ALIAS_KEY); - _cacheManager.Remove(ALL_VARIANT_ALIAS_BY_ID_KEY); - } - - public int GetVariantIdByAlias(string variantAlias, int languageId = 0) - { - var result = 0; - - if (variantAlias.HasValue()) - { - var mappings = GetVariantIdByAliasMappings(); - - if (!mappings.TryGetValue(CreateKey("vari", languageId, variantAlias), out result) && languageId != 0) - { - mappings.TryGetValue(CreateKey("vari", 0, variantAlias), out result); - } - } - - return result; - } - - public int GetVariantOptionIdByAlias(string optionAlias, int variantId, int languageId = 0) - { - var result = 0; - - if (optionAlias.HasValue() && variantId != 0) - { - var mappings = GetVariantIdByAliasMappings(); - - if (!mappings.TryGetValue(CreateOptionKey("vari.option", languageId, variantId, optionAlias), out result) && languageId != 0) - { - mappings.TryGetValue(CreateOptionKey("vari.option", 0, variantId, optionAlias), out result); - } - } - - return result; - } - - public string GetVariantAliasById(int variantId, int languageId = 0) - { - string result = null; - - if (variantId != 0) - { - var mappings = GetVariantAliasByIdMappings(); - - if (!mappings.TryGetValue(CreateKey("vari", languageId, variantId), out result) && languageId != 0) - { - mappings.TryGetValue(CreateKey("vari", 0, variantId), out result); - } - } - - return result; - } - - public string GetVariantOptionAliasById(int optionId, int languageId = 0) - { - string result = null; - - if (optionId != 0) - { - var mappings = GetVariantAliasByIdMappings(); - - if (!mappings.TryGetValue(CreateOptionKey("vari.option", languageId, optionId), out result) && languageId != 0) - { - mappings.TryGetValue(CreateOptionKey("vari.option", 0, optionId), out result); - } - } - - return result; - } - - #endregion - - #region Common Facets - - protected virtual IDictionary GetCommonFacetAliasByGroupKindMappings() - { - return _cacheManager.Get(ALL_COMMONFACET_ALIAS_BY_KIND_KEY, () => - { - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - - var groupKinds = new FacetGroupKind[] - { - FacetGroupKind.Category, - FacetGroupKind.Brand, - FacetGroupKind.Price, - FacetGroupKind.Rating, - FacetGroupKind.DeliveryTime, - FacetGroupKind.Availability, - FacetGroupKind.NewArrivals - }; - - foreach (var language in _languageService.GetAllLanguages()) - { - foreach (var groupKind in groupKinds) - { - var key = FacetUtility.GetFacetAliasSettingKey(groupKind, language.Id); - var value = _settingService.GetSettingByKey(key); - if (value.HasValue()) - { - result.Add(key, value); - } - } - } - - return result; - }); - } - - public void ClearCommonFacetCache() - { - _cacheManager.Remove(ALL_COMMONFACET_ALIAS_BY_KIND_KEY); - } - - public string GetCommonFacetAliasByGroupKind(FacetGroupKind kind, int languageId) - { - var mappings = GetCommonFacetAliasByGroupKindMappings(); - - return mappings.Get(FacetUtility.GetFacetAliasSettingKey(kind, languageId)); - } - - #endregion - } + } + + #region Specification Attributes + + protected virtual IDictionary GetAttributeIdByAliasMappings() + { + return _cacheManager.Get(ALL_ATTRIBUTE_ID_BY_ALIAS_KEY, () => + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + var optionIdMappings = new Dictionary(); + IPagedList attributes = null; + var attributeId = 0; + var pageIndex = 0; + + var query = _specificationAttributeService.GetSpecificationAttributes() + .Expand(x => x.SpecificationAttributeOptions) + .OrderBy(x => x.Id); + + do + { + attributes = new PagedList(query, pageIndex++, 500); + + foreach (var attribute in attributes) + { + if (attribute.Alias.HasValue()) + { + result[CreateKey("attr", 0, attribute.Alias)] = attribute.Id; + } + + foreach (var option in attribute.SpecificationAttributeOptions) + { + optionIdMappings[option.Id] = option.SpecificationAttributeId; + + if (option.Alias.HasValue()) + { + result[CreateOptionKey("attr.option", 0, attribute.Id, option.Alias)] = option.Id; + } + } + } + } + while (attributes.HasNextPage); + + attributes.Clear(); + + CachedLocalizedAlias("SpecificationAttribute", x => result[CreateKey("attr", x.LanguageId, x.LocaleValue)] = x.EntityId); + CachedLocalizedAlias("SpecificationAttributeOption", x => + { + if (optionIdMappings.TryGetValue(x.EntityId, out attributeId)) + result[CreateOptionKey("attr.option", x.LanguageId, attributeId, x.LocaleValue)] = x.EntityId; + }); + + return result; + }); + } + + protected virtual IDictionary GetAttributeAliasByIdMappings() + { + return _cacheManager.Get(ALL_ATTRIBUTE_ALIAS_BY_ID_KEY, () => + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + IPagedList attributes = null; + var pageIndex = 0; + + var query = _specificationAttributeService.GetSpecificationAttributes() + .Expand(x => x.SpecificationAttributeOptions) + .OrderBy(x => x.Id); + + do + { + attributes = new PagedList(query, pageIndex++, 500); + + foreach (var attribute in attributes) + { + if (attribute.Alias.HasValue()) + { + result[CreateKey("attr", 0, attribute.Id)] = attribute.Alias; + } + + foreach (var option in attribute.SpecificationAttributeOptions.Where(x => x.Alias.HasValue())) + { + result[CreateOptionKey("attr.option", 0, option.Id)] = option.Alias; + } + } + } + while (attributes.HasNextPage); + + attributes.Clear(); + + CachedLocalizedAlias("SpecificationAttribute", x => result[CreateKey("attr", x.LanguageId, x.EntityId)] = x.LocaleValue); + CachedLocalizedAlias("SpecificationAttributeOption", x => result[CreateOptionKey("attr.option", x.LanguageId, x.EntityId)] = x.LocaleValue); + + return result; + }); + } + + public void ClearAttributeCache() + { + _cacheManager.Remove(ALL_ATTRIBUTE_ID_BY_ALIAS_KEY); + _cacheManager.Remove(ALL_ATTRIBUTE_ALIAS_BY_ID_KEY); + } + + public int GetAttributeIdByAlias(string attributeAlias, int languageId = 0) + { + var result = 0; + + if (attributeAlias.HasValue()) + { + var mappings = GetAttributeIdByAliasMappings(); + + if (!mappings.TryGetValue(CreateKey("attr", languageId, attributeAlias), out result) && languageId != 0) + { + mappings.TryGetValue(CreateKey("attr", 0, attributeAlias), out result); + } + } + + return result; + } + + public int GetAttributeOptionIdByAlias(string optionAlias, int attributeId, int languageId = 0) + { + var result = 0; + + if (optionAlias.HasValue() && attributeId != 0) + { + var mappings = GetAttributeIdByAliasMappings(); + + if (!mappings.TryGetValue(CreateOptionKey("attr.option", languageId, attributeId, optionAlias), out result) && languageId != 0) + { + mappings.TryGetValue(CreateOptionKey("attr.option", 0, attributeId, optionAlias), out result); + } + } + + return result; + } + + public string GetAttributeAliasById(int attributeId, int languageId = 0) + { + string result = null; + + if (attributeId != 0) + { + var mappings = GetAttributeAliasByIdMappings(); + + if (!mappings.TryGetValue(CreateKey("attr", languageId, attributeId), out result) && languageId != 0) + { + mappings.TryGetValue(CreateKey("attr", 0, attributeId), out result); + } + } + + return result; + } + + public string GetAttributeOptionAliasById(int optionId, int languageId = 0) + { + string result = null; + + if (optionId != 0) + { + var mappings = GetAttributeAliasByIdMappings(); + + if (!mappings.TryGetValue(CreateOptionKey("attr.option", languageId, optionId), out result) && languageId != 0) + { + mappings.TryGetValue(CreateOptionKey("attr.option", 0, optionId), out result); + } + } + + return result; + } + + #endregion + + #region Product Variants + + protected virtual IDictionary GetVariantIdByAliasMappings() + { + return _cacheManager.Get(ALL_VARIANT_ID_BY_ALIAS_KEY, () => + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + IPagedList variants = null; + IPagedList options = null; + var variantId = 0; + var pageIndex = 0; + + var variantQuery = _productAttributeRepository.TableUntracked + .Where(x => !string.IsNullOrEmpty(x.Alias)) + .OrderBy(x => x.Id); + + var optionQuery = _productVariantAttributeValueRepository.TableUntracked + .Expand(x => x.ProductVariantAttribute) + .Expand("ProductVariantAttribute.ProductAttribute") + .Where(x => !string.IsNullOrEmpty(x.Alias)) + .OrderBy(x => x.Id); + + do + { + variants = new PagedList(variantQuery, pageIndex++, 500); + + foreach (var variant in variants) + { + result[CreateKey("vari", 0, variant.Alias)] = variant.Id; + } + } + while (variants.HasNextPage); + pageIndex = 0; + variants.Clear(); + + do + { + options = new PagedList(optionQuery, pageIndex++, 500); + + foreach (var option in options) + { + var variant = option.ProductVariantAttribute.ProductAttribute; + result[CreateOptionKey("vari.option", 0, variant.Id, option.Alias)] = option.Id; + } + } + while (options.HasNextPage); + options.Clear(); + + var optionIdMappings = _productVariantAttributeValueRepository.TableUntracked + .Expand(x => x.ProductVariantAttribute.ProductAttribute) + .Select(x => new + { + OptionId = x.Id, + VariantId = x.ProductVariantAttribute.ProductAttribute.Id + }) + .ToDictionary(x => x.OptionId, x => x.VariantId); + + CachedLocalizedAlias("ProductAttribute", x => result[CreateKey("vari", x.LanguageId, x.LocaleValue)] = x.EntityId); + CachedLocalizedAlias("ProductVariantAttributeValue", x => + { + if (optionIdMappings.TryGetValue(x.EntityId, out variantId)) + result[CreateOptionKey("vari.option", x.LanguageId, variantId, x.LocaleValue)] = x.EntityId; + }); + + return result; + }); + } + + protected virtual IDictionary GetVariantAliasByIdMappings() + { + return _cacheManager.Get(ALL_VARIANT_ALIAS_BY_ID_KEY, () => + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + IPagedList variants = null; + IPagedList options = null; + var pageIndex = 0; + + var variantQuery = _productAttributeRepository.TableUntracked + .Where(x => !string.IsNullOrEmpty(x.Alias)) + .OrderBy(x => x.Id); + + var optionQuery = _productVariantAttributeValueRepository.TableUntracked + .Where(x => !string.IsNullOrEmpty(x.Alias)) + .OrderBy(x => x.Id); + + do + { + variants = new PagedList(variantQuery, pageIndex++, 500); + + foreach (var variant in variants) + { + result[CreateKey("vari", 0, variant.Id)] = variant.Alias; + } + } + while (variants.HasNextPage); + pageIndex = 0; + variants.Clear(); + + do + { + options = new PagedList(optionQuery, pageIndex++, 500); + + foreach (var option in options) + { + result[CreateOptionKey("attr.option", 0, option.Id)] = option.Alias; + } + } + while (options.HasNextPage); + options.Clear(); + + CachedLocalizedAlias("ProductAttribute", x => result[CreateKey("vari", x.LanguageId, x.EntityId)] = x.LocaleValue); + CachedLocalizedAlias("ProductVariantAttributeValue", x => result[CreateOptionKey("vari.option", x.LanguageId, x.EntityId)] = x.LocaleValue); + + return result; + }); + } + + public void ClearVariantCache() + { + _cacheManager.Remove(ALL_VARIANT_ID_BY_ALIAS_KEY); + _cacheManager.Remove(ALL_VARIANT_ALIAS_BY_ID_KEY); + } + + public int GetVariantIdByAlias(string variantAlias, int languageId = 0) + { + var result = 0; + + if (variantAlias.HasValue()) + { + var mappings = GetVariantIdByAliasMappings(); + + if (!mappings.TryGetValue(CreateKey("vari", languageId, variantAlias), out result) && languageId != 0) + { + mappings.TryGetValue(CreateKey("vari", 0, variantAlias), out result); + } + } + + return result; + } + + public int GetVariantOptionIdByAlias(string optionAlias, int variantId, int languageId = 0) + { + var result = 0; + + if (optionAlias.HasValue() && variantId != 0) + { + var mappings = GetVariantIdByAliasMappings(); + + if (!mappings.TryGetValue(CreateOptionKey("vari.option", languageId, variantId, optionAlias), out result) && languageId != 0) + { + mappings.TryGetValue(CreateOptionKey("vari.option", 0, variantId, optionAlias), out result); + } + } + + return result; + } + + public string GetVariantAliasById(int variantId, int languageId = 0) + { + string result = null; + + if (variantId != 0) + { + var mappings = GetVariantAliasByIdMappings(); + + if (!mappings.TryGetValue(CreateKey("vari", languageId, variantId), out result) && languageId != 0) + { + mappings.TryGetValue(CreateKey("vari", 0, variantId), out result); + } + } + + return result; + } + + public string GetVariantOptionAliasById(int optionId, int languageId = 0) + { + string result = null; + + if (optionId != 0) + { + var mappings = GetVariantAliasByIdMappings(); + + if (!mappings.TryGetValue(CreateOptionKey("vari.option", languageId, optionId), out result) && languageId != 0) + { + mappings.TryGetValue(CreateOptionKey("vari.option", 0, optionId), out result); + } + } + + return result; + } + + #endregion + + #region Common Facets + + protected virtual IDictionary GetCommonFacetAliasByGroupKindMappings() + { + return _cacheManager.Get(ALL_COMMONFACET_ALIAS_BY_KIND_KEY, () => + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + + var groupKinds = new FacetGroupKind[] + { + FacetGroupKind.Category, + FacetGroupKind.Brand, + FacetGroupKind.Price, + FacetGroupKind.Rating, + FacetGroupKind.DeliveryTime, + FacetGroupKind.Availability, + FacetGroupKind.NewArrivals + }; + + foreach (var language in _languageService.GetAllLanguages()) + { + foreach (var groupKind in groupKinds) + { + var key = FacetUtility.GetFacetAliasSettingKey(groupKind, language.Id); + var value = _settingService.GetSettingByKey(key); + if (value.HasValue()) + { + result.Add(key, value); + } + } + } + + return result; + }); + } + + public void ClearCommonFacetCache() + { + _cacheManager.Remove(ALL_COMMONFACET_ALIAS_BY_KIND_KEY); + } + + public string GetCommonFacetAliasByGroupKind(FacetGroupKind kind, int languageId) + { + var mappings = GetCommonFacetAliasByGroupKindMappings(); + + return mappings.Get(FacetUtility.GetFacetAliasSettingKey(kind, languageId)); + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryFactory.cs b/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryFactory.cs index 3c30544a0d..f03d97bd69 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryFactory.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryFactory.cs @@ -7,6 +7,7 @@ using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Search; using SmartStore.Core.Search.Facets; +using SmartStore.Core.Security; using SmartStore.Services.Catalog; using SmartStore.Services.Search.Extensions; using SmartStore.Services.Security; @@ -36,53 +37,53 @@ public class CatalogSearchQueryFactory : SearchQueryFactoryBase, ICatalogSearchQ { protected static readonly string[] _instantSearchFields = new string[] { "manufacturer", "sku", "gtin", "mpn", "attrname", "variantname" }; - protected readonly CatalogSettings _catalogSettings; - protected readonly SearchSettings _searchSettings; - protected readonly ICommonServices _services; - protected readonly ICatalogSearchQueryAliasMapper _catalogSearchQueryAliasMapper; - - public CatalogSearchQueryFactory( - HttpContextBase httpContext, - CatalogSettings catalogSettings, - SearchSettings searchSettings, - ICommonServices services, - ICatalogSearchQueryAliasMapper catalogSearchQueryAliasMapper) + protected readonly CatalogSettings _catalogSettings; + protected readonly SearchSettings _searchSettings; + protected readonly ICommonServices _services; + protected readonly ICatalogSearchQueryAliasMapper _catalogSearchQueryAliasMapper; + + public CatalogSearchQueryFactory( + HttpContextBase httpContext, + CatalogSettings catalogSettings, + SearchSettings searchSettings, + ICommonServices services, + ICatalogSearchQueryAliasMapper catalogSearchQueryAliasMapper) : base(httpContext) - { - _catalogSettings = catalogSettings; - _searchSettings = searchSettings; - _services = services; - _catalogSearchQueryAliasMapper = catalogSearchQueryAliasMapper; + { + _catalogSettings = catalogSettings; + _searchSettings = searchSettings; + _services = services; + _catalogSearchQueryAliasMapper = catalogSearchQueryAliasMapper; - QuerySettings = DbQuerySettings.Default; - } + QuerySettings = DbQuerySettings.Default; + } - public DbQuerySettings QuerySettings { get; set; } + public DbQuerySettings QuerySettings { get; set; } - public CatalogSearchQuery Current { get; private set; } + public CatalogSearchQuery Current { get; private set; } protected override string[] Tokens => new string[] { "q", "i", "s", "o", "p", "c", "m", "r", "a", "n", "d", "v" }; public CatalogSearchQuery CreateFromQuery() - { - var ctx = _httpContext; - - if (ctx.Request == null) - return null; - - var routeData = ctx.Request.RequestContext.RouteData; - var area = routeData.GetAreaName(); - var controller = routeData.GetRequiredString("controller"); - var action = routeData.GetRequiredString("action"); - var origin = "{0}{1}/{2}".FormatInvariant(area == null ? "" : area + "/", controller, action); - var fields = new List { "name" }; - var term = GetValueFor("q"); - var isInstantSearch = origin.IsCaseInsensitiveEqual("Search/InstantSearch"); - - if (isInstantSearch) - { - fields.Add("shortdescription"); - fields.Add("tagname"); + { + var ctx = _httpContext; + + if (ctx.Request == null) + return null; + + var routeData = ctx.Request.RequestContext.RouteData; + var area = routeData.GetAreaName(); + var controller = routeData.GetRequiredString("controller"); + var action = routeData.GetRequiredString("action"); + var origin = "{0}{1}/{2}".FormatInvariant(area == null ? "" : area + "/", controller, action); + var fields = new List { "name" }; + var term = GetValueFor("q"); + var isInstantSearch = origin.IsCaseInsensitiveEqual("Search/InstantSearch"); + + if (isInstantSearch) + { + fields.Add("shortdescription"); + fields.Add("tagname"); foreach (var fieldName in _instantSearchFields) { @@ -92,510 +93,518 @@ public CatalogSearchQuery CreateFromQuery() } } } - else - { - fields.AddRange(_searchSettings.SearchFields); - } - - var query = new CatalogSearchQuery(fields.ToArray(), term, _searchSettings.SearchMode) - .OriginatesFrom(origin) - .WithLanguage(_services.WorkContext.WorkingLanguage) - .WithCurrency(_services.WorkContext.WorkingCurrency) - .VisibleIndividuallyOnly(true) - .BuildFacetMap(!isInstantSearch); - - // Visibility. - query.VisibleOnly(!QuerySettings.IgnoreAcl ? _services.WorkContext.CurrentCustomer : null); - - // Store. - if (!QuerySettings.IgnoreMultiStore) - { - query.HasStoreId(_services.StoreContext.CurrentStore.Id); - } - - // Availability. - ConvertAvailability(query, routeData, origin); - - // Instant-Search never uses these filter parameters. - if (!isInstantSearch) - { - ConvertPagingSorting(query, routeData, origin); - ConvertPrice(query, routeData, origin); - ConvertCategory(query, routeData, origin); - ConvertManufacturer(query, routeData, origin); - ConvertRating(query, routeData, origin); - ConvertNewArrivals(query, routeData, origin); - ConvertDeliveryTime(query, routeData, origin); - } - - OnConverted(query, routeData, origin); - - Current = query; - return query; - } - - protected virtual void ConvertPagingSorting(CatalogSearchQuery query, RouteData routeData, string origin) - { - var index = Math.Max(1, GetValueFor("i") ?? 1); - var size = GetPageSize(query, routeData, origin); - - query.Slice((index - 1) * size, size); - - var orderBy = GetValueFor("o"); - if (orderBy == null || orderBy == ProductSortingEnum.Initial) - { + else + { + fields.AddRange(_searchSettings.SearchFields); + } + + var query = new CatalogSearchQuery(fields.ToArray(), term, _searchSettings.SearchMode) + .OriginatesFrom(origin) + .WithLanguage(_services.WorkContext.WorkingLanguage) + .WithCurrency(_services.WorkContext.WorkingCurrency) + .BuildFacetMap(!isInstantSearch); + + // Visibility. + query.VisibleOnly(!QuerySettings.IgnoreAcl ? _services.WorkContext.CurrentCustomer : null); + + if (isInstantSearch || origin.IsCaseInsensitiveEqual("Search/Search")) + { + query.WithVisibility(ProductVisibility.SearchResults); + } + else + { + query.WithVisibility(ProductVisibility.Full); + } + + // Store. + if (!QuerySettings.IgnoreMultiStore) + { + query.HasStoreId(_services.StoreContext.CurrentStore.Id); + } + + // Availability. + ConvertAvailability(query, routeData, origin); + + // Instant-Search never uses these filter parameters. + if (!isInstantSearch) + { + ConvertPagingSorting(query, routeData, origin); + ConvertPrice(query, routeData, origin); + ConvertCategory(query, routeData, origin); + ConvertManufacturer(query, routeData, origin); + ConvertRating(query, routeData, origin); + ConvertNewArrivals(query, routeData, origin); + ConvertDeliveryTime(query, routeData, origin); + } + + OnConverted(query, routeData, origin); + + Current = query; + return query; + } + + protected virtual void ConvertPagingSorting(CatalogSearchQuery query, RouteData routeData, string origin) + { + var index = Math.Max(1, GetValueFor("i") ?? 1); + var size = GetPageSize(query, routeData, origin); + + query.Slice((index - 1) * size, size); + + var orderBy = GetValueFor("o"); + if (orderBy == null || orderBy == ProductSortingEnum.Initial) + { orderBy = origin.IsCaseInsensitiveEqual("Search/Search") ? _searchSettings.DefaultSortOrder : _catalogSettings.DefaultSortOrder; - } - - query.CustomData["CurrentSortOrder"] = orderBy.Value; - - query.SortBy(orderBy.Value); - } - - private int GetPageSize(CatalogSearchQuery query, RouteData routeData, string origin) - { - string entityViewMode = null; - - // Determine entity id if possible - IPagingOptions entity = null; - int? entityId; - if (origin.IsCaseInsensitiveEqual("Catalog/Category")) - { - entityId = (int?)routeData.Values["categoryId"]; - if (entityId.HasValue) - { - entity = _services.Resolve().GetCategoryById(entityId.Value) as IPagingOptions; - entityViewMode = ((Category)entity)?.DefaultViewMode; - } - } - else if (origin.IsCaseInsensitiveEqual("Catalog/Manufacturer")) - { - entityId = (int?)routeData.Values["manufacturerId"]; - if (entityId.HasValue) - { - entity = _services.Resolve().GetManufacturerById(entityId.Value) as IPagingOptions; - } - } - - var entitySize = entity?.PageSize; - - var sessionKey = origin; - if (entitySize.HasValue) - { - sessionKey += "/" + entitySize.Value; - } - - DetectViewMode(query, sessionKey, entityViewMode); - - var allowChange = entity?.AllowCustomersToSelectPageSize ?? _catalogSettings.AllowCustomersToSelectPageSize; - if (!allowChange) - { - return entitySize ?? _catalogSettings.DefaultProductListPageSize; - } - - sessionKey = "PageSize:" + sessionKey; - - // Get from form or query - var selectedSize = GetValueFor("s"); - - if (selectedSize.HasValue) - { - // Save the selection in session. We'll fetch this session value - // on subsequent requests for this route. - if (_httpContext.Session != null) - { - _httpContext.Session[sessionKey] = selectedSize.Value; - } - return selectedSize.Value; - } - - // Return user size from session - if (_httpContext.Session != null) - { - var sessionSize = _httpContext.Session[sessionKey].Convert(); - if (sessionSize.HasValue) - { - return sessionSize.Value; - } - } - - // Return default size for entity (IPagingOptions) - if (entitySize.HasValue) - { - return entitySize.Value; - } - - // Return default page size - return _catalogSettings.DefaultProductListPageSize; - } - - private void DetectViewMode(CatalogSearchQuery query, string sessionKey, string entityViewMode = null) - { - if (!_catalogSettings.AllowProductViewModeChanging) - { - query.CustomData["ViewMode"] = entityViewMode.NullEmpty() ?? _catalogSettings.DefaultViewMode; - return; - } - - var selectedViewMode = GetValueFor("v"); - - sessionKey = "ViewMode:" + sessionKey; - - if (selectedViewMode != null) - { - // Save the view mode selection in session. We'll fetch this session value - // on subsequent requests for this route. - if (_httpContext.Session != null) - { - _httpContext.Session[sessionKey] = selectedViewMode; - } - query.CustomData["ViewMode"] = selectedViewMode; - return; - } - - // Set view mode from session - if (_httpContext.Session != null) - { - var sessionViewMode = _httpContext.Session[sessionKey].Convert(); - if (sessionViewMode != null) - { - query.CustomData["ViewMode"] = sessionViewMode; - return; - } - } - - // Set default view mode for entity - if (entityViewMode != null) - { - query.CustomData["ViewMode"] = entityViewMode; - return; - } - - // Set default view mode - query.CustomData["ViewMode"] = _catalogSettings.DefaultViewMode; - } - - private void AddFacet( - CatalogSearchQuery query, - FacetGroupKind kind, - bool isMultiSelect, - FacetSorting sorting, - Action addValues) - { - string fieldName; - var displayOrder = 0; - - switch (kind) - { - case FacetGroupKind.Category: - fieldName = _catalogSettings.IncludeFeaturedProductsInNormalLists ? "categoryid" : "notfeaturedcategoryid"; - break; - case FacetGroupKind.Brand: - if (_searchSettings.BrandDisabled) - return; - fieldName = "manufacturerid"; - displayOrder = _searchSettings.BrandDisplayOrder; - break; - case FacetGroupKind.Price: - if (_searchSettings.PriceDisabled || !_services.Permissions.Authorize(StandardPermissionProvider.DisplayPrices)) - return; - fieldName = "price"; - displayOrder = _searchSettings.PriceDisplayOrder; - break; - case FacetGroupKind.Rating: - if (_searchSettings.RatingDisabled) - return; - fieldName = "rating"; - displayOrder = _searchSettings.RatingDisplayOrder; - break; - case FacetGroupKind.DeliveryTime: - if (_searchSettings.DeliveryTimeDisabled) - return; - fieldName = "deliveryid"; - displayOrder = _searchSettings.DeliveryTimeDisplayOrder; - break; - case FacetGroupKind.Availability: - if (_searchSettings.AvailabilityDisabled) - return; - fieldName = "available"; - displayOrder = _searchSettings.AvailabilityDisplayOrder; - break; - case FacetGroupKind.NewArrivals: - if (_searchSettings.NewArrivalsDisabled) - return; - fieldName = "createdon"; - displayOrder = _searchSettings.NewArrivalsDisplayOrder; - break; - default: - 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 - }; - - addValues(descriptor); - query.WithFacet(descriptor); - } - - protected virtual void ConvertCategory(CatalogSearchQuery query, RouteData routeData, string origin) - { - if (origin == "Catalog/Category") - { - // we don't need category facetting in category pages - return; - } - - List ids; - - if (GetValueFor(query, "c", FacetGroupKind.Category, out ids) && ids != null && ids.Any()) - { - // TODO; (mc) Get deep ids (???) Make a low-level version of CatalogHelper.GetChildCategoryIds() - query.WithCategoryIds(_catalogSettings.IncludeFeaturedProductsInNormalLists ? null : (bool?)false, ids.ToArray()); - } - - AddFacet(query, FacetGroupKind.Category, true, FacetSorting.HitsDesc, descriptor => - { - if (ids != null) - { - foreach (var id in ids) - { - descriptor.AddValue(new FacetValue(id, IndexTypeCode.Int32) - { - IsSelected = true - }); - } - } - }); - } - - protected virtual void ConvertManufacturer(CatalogSearchQuery query, RouteData routeData, string origin) - { - //List ids = null; - //int? minHitCount = null; - - //GetValueFor(query, "m", FacetGroupKind.Brand, out ids); - - //// Preselect manufacturer on manufacturer page.... and then? - //if (origin.IsCaseInsensitiveEqual("Catalog/Manufacturer")) - //{ - // minHitCount = 0; - - // var manufacturerId = routeData.Values["manufacturerid"].ToString().ToInt(); - // if (manufacturerId != 0) - // { - // if (ids == null) - // ids = new List { manufacturerId }; - // else if (!ids.Contains(manufacturerId)) - // ids.Add(manufacturerId); - // } - //} - - if (origin.IsCaseInsensitiveEqual("Catalog/Manufacturer")) - { - // we don't need brand facetting in brand pages - return; - } - - List ids; - - if (GetValueFor(query, "m", FacetGroupKind.Brand, out ids) && ids != null && ids.Any()) - { - query.WithManufacturerIds(null, ids.ToArray()); - } - - AddFacet(query, FacetGroupKind.Brand, true, FacetSorting.LabelAsc, descriptor => - { - if (ids != null) - { - foreach (var id in ids) - { - descriptor.AddValue(new FacetValue(id, IndexTypeCode.Int32) - { - IsSelected = true - }); - } - } - }); - } - - protected virtual void ConvertPrice(CatalogSearchQuery query, RouteData routeData, string origin) - { - string price; - double? minPrice = null; - double? maxPrice = null; - - if (GetValueFor(query, "p", FacetGroupKind.Price, out price) && TryParseRange(price, out minPrice, out maxPrice)) - { - var currency = _services.WorkContext.WorkingCurrency; - - if (minPrice.HasValue) - { - // TODO: (mc) Why the heck did I convert this??!! - //minPrice = _currencyService.ConvertToPrimaryStoreCurrency(minPrice.Value, currency); - } - - if (maxPrice.HasValue) - { - //maxPrice = _currencyService.ConvertToPrimaryStoreCurrency(maxPrice.Value, currency); - } - - if (minPrice.HasValue && maxPrice.HasValue && minPrice > maxPrice) - { - var tmp = minPrice; - minPrice = maxPrice; - maxPrice = tmp; - } - - if (minPrice.HasValue || maxPrice.HasValue) - { - query.PriceBetween((decimal?)minPrice, (decimal?)maxPrice); - } - } - - AddFacet(query, FacetGroupKind.Price, false, FacetSorting.DisplayOrder, descriptor => - { - if (minPrice.HasValue || maxPrice.HasValue) - { - descriptor.AddValue(new FacetValue( - minPrice, - maxPrice, - IndexTypeCode.Double, - minPrice.HasValue, - maxPrice.HasValue) - { - IsSelected = true - }); - } - }); - } - - protected virtual void ConvertRating(CatalogSearchQuery query, RouteData routeData, string origin) - { - double? fromRate; - - if (GetValueFor(query, "r", FacetGroupKind.Rating, out fromRate) && fromRate.HasValue) - { - query.WithRating(fromRate, null); - } - - AddFacet(query, FacetGroupKind.Rating, false, FacetSorting.DisplayOrder, descriptor => - { - descriptor.MinHitCount = 0; - descriptor.MaxChoicesCount = 5; - - if (fromRate.HasValue) - { - descriptor.AddValue(new FacetValue(fromRate.Value, IndexTypeCode.Double) - { - IsSelected = true - }); - } - }); - } - - protected virtual void ConvertAvailability(CatalogSearchQuery query, RouteData routeData, string origin) - { - bool availability; - GetValueFor(query, "a", FacetGroupKind.Availability, out availability); - - // Setting specifies the logical direction of the filter. That's smarter than just to specify a default value. - if (_searchSettings.IncludeNotAvailable) - { - // False = show, True = hide unavailable products. - if (availability) - { - query.AvailableOnly(true); - } - } - else - { - // False = hide, True = show unavailable products. - if (!availability) - { - query.AvailableOnly(true); - } - } - - AddFacet(query, FacetGroupKind.Availability, true, FacetSorting.LabelAsc, descriptor => - { - descriptor.MinHitCount = 0; - - var newValue = availability - ? new FacetValue(true, IndexTypeCode.Boolean) - : new FacetValue(null, IndexTypeCode.Empty); - - newValue.IsSelected = availability; - newValue.Label = _services.Localization.GetResource(_searchSettings.IncludeNotAvailable ? "Search.Facet.ExcludeOutOfStock" : "Search.Facet.IncludeOutOfStock"); - - descriptor.AddValue(newValue); - }); - } - - protected virtual void ConvertNewArrivals(CatalogSearchQuery query, RouteData routeData, string origin) - { - var newForMaxDays = _catalogSettings.LabelAsNewForMaxDays ?? 0; - if (newForMaxDays <= 0) - { - // we cannot filter without it - return; - } - - bool newArrivalsOnly; - var fromUtc = DateTime.UtcNow.Subtract(TimeSpan.FromDays(newForMaxDays)); - - if (GetValueFor(query, "n", FacetGroupKind.NewArrivals, out newArrivalsOnly) && newArrivalsOnly) - { - query.CreatedBetween(fromUtc, null); - } - - AddFacet(query, FacetGroupKind.NewArrivals, true, FacetSorting.LabelAsc, descriptor => - { - descriptor.AddValue(new FacetValue(fromUtc, null, IndexTypeCode.DateTime, true, false) - { - IsSelected = newArrivalsOnly, - Label = _services.Localization.GetResource("Search.Facet.LastDays").FormatInvariant(newForMaxDays) - }); - }); - } - - protected virtual void ConvertDeliveryTime(CatalogSearchQuery query, RouteData routeData, string origin) - { - List ids; - - if (GetValueFor(query, "d", FacetGroupKind.DeliveryTime, out ids) && ids != null && ids.Any()) - { - query.WithDeliveryTimeIds(ids.ToArray()); - } - - AddFacet(query, FacetGroupKind.DeliveryTime, true, FacetSorting.DisplayOrder, descriptor => - { - if (ids != null) - { - foreach (var id in ids) - { - descriptor.AddValue(new FacetValue(id, IndexTypeCode.Int32) - { - IsSelected = true - }); - } - } - }); - } - - protected virtual void OnConverted(CatalogSearchQuery query, RouteData routeData, string origin) - { - } - - protected virtual bool GetValueFor(CatalogSearchQuery query, string key, FacetGroupKind kind, out T value) - { - return TryGetValueFor(_catalogSearchQueryAliasMapper.GetCommonFacetAliasByGroupKind(kind, query.LanguageId ?? 0) ?? key, out value); - } - } + } + + query.CustomData["CurrentSortOrder"] = orderBy.Value; + + query.SortBy(orderBy.Value); + } + + private int GetPageSize(CatalogSearchQuery query, RouteData routeData, string origin) + { + string entityViewMode = null; + + // Determine entity id if possible + IPagingOptions entity = null; + int? entityId; + if (origin.IsCaseInsensitiveEqual("Catalog/Category")) + { + entityId = (int?)routeData.Values["categoryId"]; + if (entityId.HasValue) + { + entity = _services.Resolve().GetCategoryById(entityId.Value) as IPagingOptions; + entityViewMode = ((Category)entity)?.DefaultViewMode; + } + } + else if (origin.IsCaseInsensitiveEqual("Catalog/Manufacturer")) + { + entityId = (int?)routeData.Values["manufacturerId"]; + if (entityId.HasValue) + { + entity = _services.Resolve().GetManufacturerById(entityId.Value) as IPagingOptions; + } + } + + var entitySize = entity?.PageSize; + + var sessionKey = origin; + if (entitySize.HasValue) + { + sessionKey += "/" + entitySize.Value; + } + + DetectViewMode(query, sessionKey, entityViewMode); + + var allowChange = entity?.AllowCustomersToSelectPageSize ?? _catalogSettings.AllowCustomersToSelectPageSize; + if (!allowChange) + { + return entitySize ?? _catalogSettings.DefaultProductListPageSize; + } + + sessionKey = "PageSize:" + sessionKey; + + // Get from form or query + var selectedSize = GetValueFor("s"); + + if (selectedSize.HasValue) + { + // Save the selection in session. We'll fetch this session value + // on subsequent requests for this route. + if (_httpContext.Session != null) + { + _httpContext.Session[sessionKey] = selectedSize.Value; + } + return selectedSize.Value; + } + + // Return user size from session + if (_httpContext.Session != null) + { + var sessionSize = _httpContext.Session[sessionKey].Convert(); + if (sessionSize.HasValue) + { + return sessionSize.Value; + } + } + + // Return default size for entity (IPagingOptions) + if (entitySize.HasValue) + { + return entitySize.Value; + } + + // Return default page size + return _catalogSettings.DefaultProductListPageSize; + } + + private void DetectViewMode(CatalogSearchQuery query, string sessionKey, string entityViewMode = null) + { + if (!_catalogSettings.AllowProductViewModeChanging) + { + query.CustomData["ViewMode"] = entityViewMode.NullEmpty() ?? _catalogSettings.DefaultViewMode; + return; + } + + var selectedViewMode = GetValueFor("v"); + + sessionKey = "ViewMode:" + sessionKey; + + if (selectedViewMode != null) + { + // Save the view mode selection in session. We'll fetch this session value + // on subsequent requests for this route. + if (_httpContext.Session != null) + { + _httpContext.Session[sessionKey] = selectedViewMode; + } + query.CustomData["ViewMode"] = selectedViewMode; + return; + } + + // Set view mode from session + if (_httpContext.Session != null) + { + var sessionViewMode = _httpContext.Session[sessionKey].Convert(); + if (sessionViewMode != null) + { + query.CustomData["ViewMode"] = sessionViewMode; + return; + } + } + + // Set default view mode for entity + if (entityViewMode != null) + { + query.CustomData["ViewMode"] = entityViewMode; + return; + } + + // Set default view mode + query.CustomData["ViewMode"] = _catalogSettings.DefaultViewMode; + } + + private void AddFacet( + CatalogSearchQuery query, + FacetGroupKind kind, + bool isMultiSelect, + FacetSorting sorting, + Action addValues) + { + string fieldName; + var displayOrder = 0; + + switch (kind) + { + case FacetGroupKind.Category: + fieldName = _catalogSettings.IncludeFeaturedProductsInNormalLists ? "categoryid" : "notfeaturedcategoryid"; + break; + case FacetGroupKind.Brand: + if (_searchSettings.BrandDisabled) + return; + fieldName = "manufacturerid"; + displayOrder = _searchSettings.BrandDisplayOrder; + break; + case FacetGroupKind.Price: + if (_searchSettings.PriceDisabled || !_services.Permissions.Authorize(Permissions.Catalog.DisplayPrice)) + return; + fieldName = "price"; + displayOrder = _searchSettings.PriceDisplayOrder; + break; + case FacetGroupKind.Rating: + if (_searchSettings.RatingDisabled) + return; + fieldName = "rating"; + displayOrder = _searchSettings.RatingDisplayOrder; + break; + case FacetGroupKind.DeliveryTime: + if (_searchSettings.DeliveryTimeDisabled) + return; + fieldName = "deliveryid"; + displayOrder = _searchSettings.DeliveryTimeDisplayOrder; + break; + case FacetGroupKind.Availability: + if (_searchSettings.AvailabilityDisabled) + return; + fieldName = "available"; + displayOrder = _searchSettings.AvailabilityDisplayOrder; + break; + case FacetGroupKind.NewArrivals: + if (_searchSettings.NewArrivalsDisabled) + return; + fieldName = "createdon"; + displayOrder = _searchSettings.NewArrivalsDisplayOrder; + break; + default: + 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 + }; + + addValues(descriptor); + query.WithFacet(descriptor); + } + + protected virtual void ConvertCategory(CatalogSearchQuery query, RouteData routeData, string origin) + { + if (origin == "Catalog/Category") + { + // we don't need category facetting in category pages + return; + } + + List ids; + + if (GetValueFor(query, "c", FacetGroupKind.Category, out ids) && ids != null && ids.Any()) + { + // TODO; (mc) Get deep ids (???) Make a low-level version of CatalogHelper.GetChildCategoryIds() + query.WithCategoryIds(_catalogSettings.IncludeFeaturedProductsInNormalLists ? null : (bool?)false, ids.ToArray()); + } + + AddFacet(query, FacetGroupKind.Category, true, FacetSorting.HitsDesc, descriptor => + { + if (ids != null) + { + foreach (var id in ids) + { + descriptor.AddValue(new FacetValue(id, IndexTypeCode.Int32) + { + IsSelected = true + }); + } + } + }); + } + + protected virtual void ConvertManufacturer(CatalogSearchQuery query, RouteData routeData, string origin) + { + //List ids = null; + //int? minHitCount = null; + + //GetValueFor(query, "m", FacetGroupKind.Brand, out ids); + + //// Preselect manufacturer on manufacturer page.... and then? + //if (origin.IsCaseInsensitiveEqual("Catalog/Manufacturer")) + //{ + // minHitCount = 0; + + // var manufacturerId = routeData.Values["manufacturerid"].ToString().ToInt(); + // if (manufacturerId != 0) + // { + // if (ids == null) + // ids = new List { manufacturerId }; + // else if (!ids.Contains(manufacturerId)) + // ids.Add(manufacturerId); + // } + //} + + if (origin.IsCaseInsensitiveEqual("Catalog/Manufacturer")) + { + // we don't need brand facetting in brand pages + return; + } + + List ids; + + if (GetValueFor(query, "m", FacetGroupKind.Brand, out ids) && ids != null && ids.Any()) + { + query.WithManufacturerIds(null, ids.ToArray()); + } + + AddFacet(query, FacetGroupKind.Brand, true, FacetSorting.LabelAsc, descriptor => + { + if (ids != null) + { + foreach (var id in ids) + { + descriptor.AddValue(new FacetValue(id, IndexTypeCode.Int32) + { + IsSelected = true + }); + } + } + }); + } + + protected virtual void ConvertPrice(CatalogSearchQuery query, RouteData routeData, string origin) + { + string price; + double? minPrice = null; + double? maxPrice = null; + + if (GetValueFor(query, "p", FacetGroupKind.Price, out price) && TryParseRange(price, out minPrice, out maxPrice)) + { + var currency = _services.WorkContext.WorkingCurrency; + + if (minPrice.HasValue) + { + // TODO: (mc) Why the heck did I convert this??!! + //minPrice = _currencyService.ConvertToPrimaryStoreCurrency(minPrice.Value, currency); + } + + if (maxPrice.HasValue) + { + //maxPrice = _currencyService.ConvertToPrimaryStoreCurrency(maxPrice.Value, currency); + } + + if (minPrice.HasValue && maxPrice.HasValue && minPrice > maxPrice) + { + var tmp = minPrice; + minPrice = maxPrice; + maxPrice = tmp; + } + + if (minPrice.HasValue || maxPrice.HasValue) + { + query.PriceBetween((decimal?)minPrice, (decimal?)maxPrice); + } + } + + AddFacet(query, FacetGroupKind.Price, false, FacetSorting.DisplayOrder, descriptor => + { + if (minPrice.HasValue || maxPrice.HasValue) + { + descriptor.AddValue(new FacetValue( + minPrice, + maxPrice, + IndexTypeCode.Double, + minPrice.HasValue, + maxPrice.HasValue) + { + IsSelected = true + }); + } + }); + } + + protected virtual void ConvertRating(CatalogSearchQuery query, RouteData routeData, string origin) + { + double? fromRate; + + if (GetValueFor(query, "r", FacetGroupKind.Rating, out fromRate) && fromRate.HasValue) + { + query.WithRating(fromRate, null); + } + + AddFacet(query, FacetGroupKind.Rating, false, FacetSorting.DisplayOrder, descriptor => + { + descriptor.MinHitCount = 0; + descriptor.MaxChoicesCount = 5; + + if (fromRate.HasValue) + { + descriptor.AddValue(new FacetValue(fromRate.Value, IndexTypeCode.Double) + { + IsSelected = true + }); + } + }); + } + + protected virtual void ConvertAvailability(CatalogSearchQuery query, RouteData routeData, string origin) + { + bool availability; + GetValueFor(query, "a", FacetGroupKind.Availability, out availability); + + // Setting specifies the logical direction of the filter. That's smarter than just to specify a default value. + if (_searchSettings.IncludeNotAvailable) + { + // False = show, True = hide unavailable products. + if (availability) + { + query.AvailableOnly(true); + } + } + else + { + // False = hide, True = show unavailable products. + if (!availability) + { + query.AvailableOnly(true); + } + } + + AddFacet(query, FacetGroupKind.Availability, true, FacetSorting.LabelAsc, descriptor => + { + descriptor.MinHitCount = 0; + + var newValue = availability + ? new FacetValue(true, IndexTypeCode.Boolean) + : new FacetValue(null, IndexTypeCode.Empty); + + newValue.IsSelected = availability; + newValue.Label = _services.Localization.GetResource(_searchSettings.IncludeNotAvailable ? "Search.Facet.ExcludeOutOfStock" : "Search.Facet.IncludeOutOfStock"); + + descriptor.AddValue(newValue); + }); + } + + protected virtual void ConvertNewArrivals(CatalogSearchQuery query, RouteData routeData, string origin) + { + var newForMaxDays = _catalogSettings.LabelAsNewForMaxDays ?? 0; + if (newForMaxDays <= 0) + { + // we cannot filter without it + return; + } + + bool newArrivalsOnly; + var fromUtc = DateTime.UtcNow.Subtract(TimeSpan.FromDays(newForMaxDays)); + + if (GetValueFor(query, "n", FacetGroupKind.NewArrivals, out newArrivalsOnly) && newArrivalsOnly) + { + query.CreatedBetween(fromUtc, null); + } + + AddFacet(query, FacetGroupKind.NewArrivals, true, FacetSorting.LabelAsc, descriptor => + { + descriptor.AddValue(new FacetValue(fromUtc, null, IndexTypeCode.DateTime, true, false) + { + IsSelected = newArrivalsOnly, + Label = _services.Localization.GetResource("Search.Facet.LastDays").FormatInvariant(newForMaxDays) + }); + }); + } + + protected virtual void ConvertDeliveryTime(CatalogSearchQuery query, RouteData routeData, string origin) + { + List ids; + + if (GetValueFor(query, "d", FacetGroupKind.DeliveryTime, out ids) && ids != null && ids.Any()) + { + query.WithDeliveryTimeIds(ids.ToArray()); + } + + AddFacet(query, FacetGroupKind.DeliveryTime, true, FacetSorting.DisplayOrder, descriptor => + { + if (ids != null) + { + foreach (var id in ids) + { + descriptor.AddValue(new FacetValue(id, IndexTypeCode.Int32) + { + IsSelected = true + }); + } + } + }); + } + + protected virtual void OnConverted(CatalogSearchQuery query, RouteData routeData, string origin) + { + } + + protected virtual bool GetValueFor(CatalogSearchQuery query, string key, FacetGroupKind kind, out T value) + { + return TryGetValueFor(_catalogSearchQueryAliasMapper.GetCommonFacetAliasByGroupKind(kind, query.LanguageId ?? 0) ?? key, out value); + } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryModelBinder.cs b/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryModelBinder.cs index dce6a49f57..817f082d51 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryModelBinder.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/CatalogSearchQueryModelBinder.cs @@ -4,39 +4,39 @@ namespace SmartStore.Services.Search.Modelling { [ModelBinderType(typeof(CatalogSearchQuery))] - public class CatalogSearchQueryModelBinder : IModelBinder - { - private readonly ICatalogSearchQueryFactory _factory; + public class CatalogSearchQueryModelBinder : IModelBinder + { + private readonly ICatalogSearchQueryFactory _factory; - public CatalogSearchQueryModelBinder(ICatalogSearchQueryFactory factory) - { - _factory = factory; - } + public CatalogSearchQueryModelBinder(ICatalogSearchQueryFactory factory) + { + _factory = factory; + } - public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) - { - if (_factory.Current != null) - { - // Don't bind again for current request. - return _factory.Current; - } + public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) + { + if (_factory.Current != null) + { + // Don't bind again for current request. + return _factory.Current; + } - if (controllerContext.IsChildAction) - { - // Never attempt to bind in child actions. We require the binding to happen - // in a parent action. If the child action is part of a request with an already bound - // 'CatalogSearchQuery', good for you :-) You'll get an instance, but null otherwise. - return _factory.Current; - } + if (controllerContext.IsChildAction) + { + // Never attempt to bind in child actions. We require the binding to happen + // in a parent action. If the child action is part of a request with an already bound + // 'CatalogSearchQuery', good for you :-) You'll get an instance, but null otherwise. + return _factory.Current; + } - var modelType = bindingContext.ModelType; - if (modelType != typeof(CatalogSearchQuery)) - { - return new CatalogSearchQuery(); - } + var modelType = bindingContext.ModelType; + if (modelType != typeof(CatalogSearchQuery)) + { + return new CatalogSearchQuery(); + } - var query = _factory.CreateFromQuery(); - return query; - } - } + var query = _factory.CreateFromQuery(); + return query; + } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/ICatalogSearchQueryAliasMapper.cs b/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/ICatalogSearchQueryAliasMapper.cs index c036029c6d..16d08f7524 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/ICatalogSearchQueryAliasMapper.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/ICatalogSearchQueryAliasMapper.cs @@ -2,95 +2,95 @@ namespace SmartStore.Services.Search.Modelling { - public interface ICatalogSearchQueryAliasMapper - { - /// - /// Clears all cached attribute mappings - /// - void ClearAttributeCache(); + public interface ICatalogSearchQueryAliasMapper + { + /// + /// Clears all cached attribute mappings + /// + void ClearAttributeCache(); - /// - /// Gets the attribute id by attribute alias - /// - /// Attribute alias - /// Language identifier - /// Attribute identifier - int GetAttributeIdByAlias(string attributeAlias, int languageId = 0); + /// + /// Gets the attribute id by attribute alias + /// + /// Attribute alias + /// Language identifier + /// Attribute identifier + int GetAttributeIdByAlias(string attributeAlias, int languageId = 0); - /// - /// Gets the attribute option id by option alias - /// - /// Attribute option alias - /// Attribute identifier - /// Language identifier - /// Attribute option identifier - int GetAttributeOptionIdByAlias(string optionAlias, int attributeId, int languageId = 0); + /// + /// Gets the attribute option id by option alias + /// + /// Attribute option alias + /// Attribute identifier + /// Language identifier + /// Attribute option identifier + int GetAttributeOptionIdByAlias(string optionAlias, int attributeId, int languageId = 0); - /// - /// Gets the attribute alias by attribute id - /// - /// Attribute identifier - /// Language identifier - /// Attribute alias - string GetAttributeAliasById(int attributeId, int languageId = 0); + /// + /// Gets the attribute alias by attribute id + /// + /// Attribute identifier + /// Language identifier + /// Attribute alias + string GetAttributeAliasById(int attributeId, int languageId = 0); - /// - /// Get the attribute option alias by option id - /// - /// Option identifier - /// Language identifier - /// Attribute option alias - string GetAttributeOptionAliasById(int optionId, int languageId = 0); + /// + /// Get the attribute option alias by option id + /// + /// Option identifier + /// Language identifier + /// Attribute option alias + string GetAttributeOptionAliasById(int optionId, int languageId = 0); - /// - /// Clears all cached variant mappings - /// - void ClearVariantCache(); + /// + /// Clears all cached variant mappings + /// + void ClearVariantCache(); - /// - /// Gets the variant id by variant alias - /// - /// Variant alias - /// Language identifier - /// Variant identifier - int GetVariantIdByAlias(string variantAlias, int languageId = 0); + /// + /// Gets the variant id by variant alias + /// + /// Variant alias + /// Language identifier + /// Variant identifier + int GetVariantIdByAlias(string variantAlias, int languageId = 0); - /// - /// Gets the variant option id by option alias - /// - /// Variant option alias - /// Variant identifier - /// Language identifier - /// Variant option identifier - int GetVariantOptionIdByAlias(string optionAlias, int variantId, int languageId = 0); + /// + /// Gets the variant option id by option alias + /// + /// Variant option alias + /// Variant identifier + /// Language identifier + /// Variant option identifier + int GetVariantOptionIdByAlias(string optionAlias, int variantId, int languageId = 0); - /// - /// Gets the variant alias by variant id - /// - /// Variant identifier - /// Language identifier - /// Variant alias - string GetVariantAliasById(int variantId, int languageId = 0); + /// + /// Gets the variant alias by variant id + /// + /// Variant identifier + /// Language identifier + /// Variant alias + string GetVariantAliasById(int variantId, int languageId = 0); - /// - /// Get the variant option alias by value id - /// - /// Option identifier - /// Language identifier - /// Variant option alias - string GetVariantOptionAliasById(int optionId, int languageId = 0); + /// + /// Get the variant option alias by value id + /// + /// Option identifier + /// Language identifier + /// Variant option alias + string GetVariantOptionAliasById(int optionId, int languageId = 0); - /// - /// Clears all cached common facet mappings - /// - void ClearCommonFacetCache(); + /// + /// Clears all cached common facet mappings + /// + void ClearCommonFacetCache(); - /// - /// Get the common facet alias by facet group kind - /// - /// Facet group kind - /// Language identifier - /// Common facet alias - string GetCommonFacetAliasByGroupKind(FacetGroupKind kind, int languageId); - } + /// + /// Get the common facet alias by facet group kind + /// + /// Facet group kind + /// Language identifier + /// Common facet alias + string GetCommonFacetAliasByGroupKind(FacetGroupKind kind, int languageId); + } } diff --git a/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/ICatalogSearchQueryFactory.cs b/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/ICatalogSearchQueryFactory.cs index 7b0afdba68..03026bf532 100644 --- a/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/ICatalogSearchQueryFactory.cs +++ b/src/Libraries/SmartStore.Services/Search/Catalog/Modelling/ICatalogSearchQueryFactory.cs @@ -1,17 +1,17 @@ namespace SmartStore.Services.Search.Modelling { - public interface ICatalogSearchQueryFactory - { - /// - /// Creates a instance from the current - /// by looking up corresponding keys in posted form and/or query string - /// - /// The query object - CatalogSearchQuery CreateFromQuery(); + public interface ICatalogSearchQueryFactory + { + /// + /// Creates a instance from the current + /// by looking up corresponding keys in posted form and/or query string + /// + /// The query object + CatalogSearchQuery CreateFromQuery(); - /// - /// The last created query instance. The MVC model binder uses this property to avoid repeated binding. - /// - CatalogSearchQuery Current { get; } - } + /// + /// The last created query instance. The MVC model binder uses this property to avoid repeated binding. + /// + CatalogSearchQuery Current { get; } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Extensions/FacetUrlHelper.cs b/src/Libraries/SmartStore.Services/Search/Extensions/FacetUrlHelper.cs index d39648af11..407dc43f9c 100644 --- a/src/Libraries/SmartStore.Services/Search/Extensions/FacetUrlHelper.cs +++ b/src/Libraries/SmartStore.Services/Search/Extensions/FacetUrlHelper.cs @@ -13,26 +13,26 @@ namespace SmartStore.Services.Search.Extensions { public class FacetUrlHelper - { - private readonly ICatalogSearchQueryAliasMapper _catalogAliasMapper; + { + private readonly ICatalogSearchQueryAliasMapper _catalogAliasMapper; private readonly IForumSearchQueryAliasMapper _forumAliasMapper; private readonly IWorkContext _workContext; - private readonly HttpRequestBase _httpRequest; + private readonly HttpRequestBase _httpRequest; - private readonly int _languageId; - private readonly string _url; - private readonly QueryString _initialQuery; + private readonly int _languageId; + private readonly string _url; + private readonly QueryString _initialQuery; - private readonly static IDictionary _queryNames = new Dictionary - { + private readonly static IDictionary _queryNames = new Dictionary + { // Catalog. { FacetGroupKind.Brand, "m" }, - { FacetGroupKind.Category, "c" }, - { FacetGroupKind.Price, "p" }, - { FacetGroupKind.Rating, "r" }, - { FacetGroupKind.DeliveryTime, "d" }, - { FacetGroupKind.Availability, "a" }, - { FacetGroupKind.NewArrivals, "n" }, + { FacetGroupKind.Category, "c" }, + { FacetGroupKind.Price, "p" }, + { FacetGroupKind.Rating, "r" }, + { FacetGroupKind.DeliveryTime, "d" }, + { FacetGroupKind.Availability, "a" }, + { FacetGroupKind.NewArrivals, "n" }, // Forum. { FacetGroupKind.Forum, "f" }, @@ -40,173 +40,173 @@ public class FacetUrlHelper { FacetGroupKind.Date, "d" } }; - public FacetUrlHelper( - ICatalogSearchQueryAliasMapper catalogAliasMapper, + public FacetUrlHelper( + ICatalogSearchQueryAliasMapper catalogAliasMapper, IForumSearchQueryAliasMapper forumAliasMapper, IWorkContext workContext, - HttpRequestBase httpRequest) - { - _catalogAliasMapper = catalogAliasMapper; + HttpRequestBase httpRequest) + { + _catalogAliasMapper = catalogAliasMapper; _forumAliasMapper = forumAliasMapper; - _workContext = workContext; - _httpRequest = httpRequest; - - _languageId = _workContext.WorkingLanguage.Id; - _url = _httpRequest.CurrentExecutionFilePath; - //_initialQuery = new QueryString().FillFromString(_httpRequest.QueryString.ToString(), false); - _initialQuery = QueryString.CurrentUnvalidated; - - // Remove page index (i) from query string - _initialQuery.Remove("i"); - } - - public string Toggle(Facet facet) - { - if (facet.Value.IsSelected) - { - return Remove(facet); - } - else - { - return Add(facet); - } - } - - public string Add(params Facet[] facets) - { - var qs = new QueryString(_initialQuery); - - foreach (var facet in facets) - { - var parts = GetQueryParts(facet); - foreach (var name in parts.AllKeys) - { - qs.Add(name, parts[name], !facet.FacetGroup.IsMultiSelect); - } - } - - return _url + qs.ToString(false); - } - - public string Remove(params Facet[] facets) - { - var qs = new QueryString(_initialQuery); - - foreach (var facet in facets) - { - var parts = GetQueryParts(facet); - foreach (var name in parts.AllKeys) - { - var qsName = name; - - if (!qs.AllKeys.Contains(name)) - { - // Query string does not contain that name. Try the unmapped name. - switch (facet.FacetGroup.Kind) - { - case FacetGroupKind.Attribute: - qsName = "attr" + facet.Value.ParentId; - break; - case FacetGroupKind.Variant: - qsName = "vari" + facet.Value.ParentId; - break; - case FacetGroupKind.Category: - case FacetGroupKind.Brand: - case FacetGroupKind.Price: - case FacetGroupKind.Rating: - case FacetGroupKind.DeliveryTime: - case FacetGroupKind.Availability: - case FacetGroupKind.NewArrivals: + _workContext = workContext; + _httpRequest = httpRequest; + + _languageId = _workContext.WorkingLanguage.Id; + _url = _httpRequest.CurrentExecutionFilePath; + //_initialQuery = new QueryString().FillFromString(_httpRequest.QueryString.ToString(), false); + _initialQuery = QueryString.CurrentUnvalidated; + + // Remove page index (i) from query string + _initialQuery.Remove("i"); + } + + public string Toggle(Facet facet) + { + if (facet.Value.IsSelected) + { + return Remove(facet); + } + else + { + return Add(facet); + } + } + + public string Add(params Facet[] facets) + { + var qs = new QueryString(_initialQuery); + + foreach (var facet in facets) + { + var parts = GetQueryParts(facet); + foreach (var name in parts.AllKeys) + { + qs.Add(name, parts[name], !facet.FacetGroup.IsMultiSelect); + } + } + + return _url + qs.ToString(false); + } + + public string Remove(params Facet[] facets) + { + var qs = new QueryString(_initialQuery); + + foreach (var facet in facets) + { + var parts = GetQueryParts(facet); + foreach (var name in parts.AllKeys) + { + var qsName = name; + + if (!qs.AllKeys.Contains(name)) + { + // Query string does not contain that name. Try the unmapped name. + switch (facet.FacetGroup.Kind) + { + case FacetGroupKind.Attribute: + qsName = "attr" + facet.Value.ParentId; + break; + case FacetGroupKind.Variant: + qsName = "vari" + facet.Value.ParentId; + break; + case FacetGroupKind.Category: + case FacetGroupKind.Brand: + case FacetGroupKind.Price: + case FacetGroupKind.Rating: + case FacetGroupKind.DeliveryTime: + case FacetGroupKind.Availability: + case FacetGroupKind.NewArrivals: case FacetGroupKind.Forum: case FacetGroupKind.Customer: case FacetGroupKind.Date: - qsName = _queryNames[facet.FacetGroup.Kind]; - break; - } - } - - string[] currentValues = null; - - // The query string value is not necessarily equal to the facet value. - // We must skip subsequent lines here to not add the removed value again and again. - if (facet.FacetGroup.Kind != FacetGroupKind.Price && - facet.FacetGroup.Kind != FacetGroupKind.Availability && - facet.FacetGroup.Kind != FacetGroupKind.NewArrivals) - { - currentValues = qs.Get(qsName)?.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToArray(); - } - - qs.Remove(qsName); - - if (currentValues != null) - { - var removeValues = parts.GetValues(name); - var newValues = currentValues.Except(removeValues).ToArray(); - if (newValues.Length > 0) - { - newValues.Each(x => qs.Add(name, x, false)); - } - } - } - } - - return _url + qs.ToString(false); - } - - public string GetQueryName(Facet facet) - { - var parts = GetQueryParts(facet); - return parts.GetKey(0); - } - - protected virtual NameValueCollection GetQueryParts(Facet facet) - { - string name = null; - string value = null; - int entityId; - - var group = facet.FacetGroup; - var val = facet.Value; - - var result = new NameValueCollection(2); - - switch (group.Kind) - { - case FacetGroupKind.Attribute: - if (facet.Value.TypeCode == IndexTypeCode.Double) - { - value = "{0}~{1}".FormatInvariant( - val.Value != null ? ((double)val.Value).ToString(CultureInfo.InvariantCulture) : "", - val.UpperValue != null ? ((double)val.UpperValue).ToString(CultureInfo.InvariantCulture) : ""); - } - else - { - entityId = val.Value.Convert(); - value = _catalogAliasMapper.GetAttributeOptionAliasById(entityId, _languageId) ?? "opt" + entityId; - } - name = _catalogAliasMapper.GetAttributeAliasById(val.ParentId, _languageId) ?? "attr" + val.ParentId; - result.Add(name, value); - break; - case FacetGroupKind.Variant: - entityId = val.Value.Convert(); - name = _catalogAliasMapper.GetVariantAliasById(val.ParentId, _languageId) ?? "vari" + val.ParentId; - value = _catalogAliasMapper.GetVariantOptionAliasById(entityId, _languageId) ?? "opt" + entityId; - result.Add(name, value); - break; - case FacetGroupKind.Category: - case FacetGroupKind.Brand: - case FacetGroupKind.Price: - case FacetGroupKind.Rating: - case FacetGroupKind.DeliveryTime: - case FacetGroupKind.Availability: - case FacetGroupKind.NewArrivals: - value = val.ToString(); - if (value.HasValue()) - { - name = _catalogAliasMapper.GetCommonFacetAliasByGroupKind(group.Kind, _languageId) ?? _queryNames[group.Kind]; - result.Add(name, value); - } - break; + qsName = _queryNames[facet.FacetGroup.Kind]; + break; + } + } + + string[] currentValues = null; + + // The query string value is not necessarily equal to the facet value. + // We must skip subsequent lines here to not add the removed value again and again. + if (facet.FacetGroup.Kind != FacetGroupKind.Price && + facet.FacetGroup.Kind != FacetGroupKind.Availability && + facet.FacetGroup.Kind != FacetGroupKind.NewArrivals) + { + currentValues = qs.Get(qsName)?.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToArray(); + } + + qs.Remove(qsName); + + if (currentValues != null) + { + var removeValues = parts.GetValues(name); + var newValues = currentValues.Except(removeValues).ToArray(); + if (newValues.Length > 0) + { + newValues.Each(x => qs.Add(name, x, false)); + } + } + } + } + + return _url + qs.ToString(false); + } + + public string GetQueryName(Facet facet) + { + var parts = GetQueryParts(facet); + return parts.GetKey(0); + } + + protected virtual NameValueCollection GetQueryParts(Facet facet) + { + string name = null; + string value = null; + int entityId; + + var group = facet.FacetGroup; + var val = facet.Value; + + var result = new NameValueCollection(2); + + switch (group.Kind) + { + case FacetGroupKind.Attribute: + if (facet.Value.TypeCode == IndexTypeCode.Double) + { + value = "{0}~{1}".FormatInvariant( + val.Value != null ? ((double)val.Value).ToString(CultureInfo.InvariantCulture) : "", + val.UpperValue != null ? ((double)val.UpperValue).ToString(CultureInfo.InvariantCulture) : ""); + } + else + { + entityId = val.Value.Convert(); + value = _catalogAliasMapper.GetAttributeOptionAliasById(entityId, _languageId) ?? "opt" + entityId; + } + name = _catalogAliasMapper.GetAttributeAliasById(val.ParentId, _languageId) ?? "attr" + val.ParentId; + result.Add(name, value); + break; + case FacetGroupKind.Variant: + entityId = val.Value.Convert(); + name = _catalogAliasMapper.GetVariantAliasById(val.ParentId, _languageId) ?? "vari" + val.ParentId; + value = _catalogAliasMapper.GetVariantOptionAliasById(entityId, _languageId) ?? "opt" + entityId; + result.Add(name, value); + break; + case FacetGroupKind.Category: + case FacetGroupKind.Brand: + case FacetGroupKind.Price: + case FacetGroupKind.Rating: + case FacetGroupKind.DeliveryTime: + case FacetGroupKind.Availability: + case FacetGroupKind.NewArrivals: + value = val.ToString(); + if (value.HasValue()) + { + name = _catalogAliasMapper.GetCommonFacetAliasByGroupKind(group.Kind, _languageId) ?? _queryNames[group.Kind]; + result.Add(name, value); + } + break; case FacetGroupKind.Forum: case FacetGroupKind.Customer: case FacetGroupKind.Date: @@ -217,9 +217,9 @@ protected virtual NameValueCollection GetQueryParts(Facet facet) result.Add(name, value); } break; - } + } - return result; - } - } + return result; + } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Extensions/FacetUtility.cs b/src/Libraries/SmartStore.Services/Search/Extensions/FacetUtility.cs index 26bb723fba..6f29143638 100644 --- a/src/Libraries/SmartStore.Services/Search/Extensions/FacetUtility.cs +++ b/src/Libraries/SmartStore.Services/Search/Extensions/FacetUtility.cs @@ -14,131 +14,119 @@ namespace SmartStore.Services.Search.Extensions /// Contains methods that are specifically required for facet processing. /// public static class FacetUtility - { - private const double MAX_PRICE = 1000000000; - - private static int[,] _priceThresholds = new int[,] - { - { 10, 5 }, - { 25, 15 }, - { 200, 25 }, - { 500, 50 }, - { 1000, 100 }, - { 2000, 250 }, - { 5000, 500 }, - { 10000, 1000 }, - { 20000, 2500 }, - { 50000, 5000 }, - { 100000, 10000 }, - { 200000, 25000 }, - { 500000, 50000 }, - { 1000000, 100000 }, - { 2000000, 250000 }, - { 5000000, 500000 }, - { 10000000, 1000000 }, - { 20000000, 2500000 }, - { 50000000, 5000000 } - }; - - private static string GetPublicName(string firstName, string lastName) + { + private const double MAX_PRICE = 1000000000; + + private static int[,] _priceThresholds = new int[,] + { + { 10, 5 }, + { 25, 15 }, + { 200, 25 }, + { 500, 50 }, + { 1000, 100 }, + { 2000, 250 }, + { 5000, 500 }, + { 10000, 1000 }, + { 20000, 2500 }, + { 50000, 5000 }, + { 100000, 10000 }, + { 200000, 25000 }, + { 500000, 50000 }, + { 1000000, 100000 }, + { 2000000, 250000 }, + { 5000000, 500000 }, + { 10000000, 1000000 }, + { 20000000, 2500000 }, + { 50000000, 5000000 } + }; + + public static double GetNextPrice(double price) + { + for (var i = 0; i <= _priceThresholds.GetUpperBound(0); ++i) + { + if (price < _priceThresholds[i, 0]) + return price + _priceThresholds[i, 1]; + } + + return MAX_PRICE; + } + + public static double MakePriceEven(double price) + { + if (price == 0.0) + return GetNextPrice(0.0); + + // Get previous threshold for price. + var result = 0.0; + for (var i = 1; i <= _priceThresholds.GetUpperBound(0) && result == 0.0; ++i) + { + if (price < _priceThresholds[i, 0]) + result = _priceThresholds[i - 1, 0]; + } + + while (result < price && result < MAX_PRICE) + { + result = GetNextPrice(result); + } + + return result; + } + + public static List GetLessPriceFacets(List facets, int maxNumber) { - string result = firstName; + const double expFactor = 2.0; + const double flatten = 2.0; + + if (facets.Count <= 3) + return facets; + + // Remove too granular facets. + if (facets.Any(x => x.Value.UpperValue != null && (double)x.Value.UpperValue == 25.0)) + { + facets.RemoveFacet(5.0, true); + facets.RemoveFacet(10.0, true); + } + + var result = new List(); + var expIndexes = new HashSet(); + var lastIndex = facets.Count - 1; + + // Get exponential distributed indexes. + for (var i = 0.0; i < lastIndex; ++i) + { + var x = (int)Math.Floor(Math.Pow(expFactor, i / flatten)); + expIndexes.Add(x); + } - if (!string.IsNullOrWhiteSpace(result) && !string.IsNullOrWhiteSpace(lastName)) + for (var index = 0; index <= lastIndex; ++index) { - result = string.Concat(result, " ", lastName.First(), "."); + var facet = facets[index]; + + // Always return first, last and selected facets. + if (index == 0 || index == lastIndex || facet.Value.IsSelected) + { + result.Add(facet); + } + else if (expIndexes.Contains(index) && result.Count < maxNumber && index < (lastIndex - 1)) + { + result.Add(facet); + } } return result; } - public static double GetNextPrice(double price) - { - for (var i = 0; i <= _priceThresholds.GetUpperBound(0); ++i) - { - if (price < _priceThresholds[i, 0]) - return price + _priceThresholds[i, 1]; - } - - return MAX_PRICE; - } - - public static double MakePriceEven(double price) - { - if (price == 0.0) - return GetNextPrice(0.0); - - // Get previous threshold for price. - var result = 0.0; - for (var i = 1; i <= _priceThresholds.GetUpperBound(0) && result == 0.0; ++i) - { - if (price < _priceThresholds[i, 0]) - result = _priceThresholds[i - 1, 0]; - } - - while (result < price && result < MAX_PRICE) - { - result = GetNextPrice(result); - } - - return result; - } - - public static List GetLessPriceFacets(List facets, int maxNumber) - { - const double expFactor = 2.0; - const double flatten = 2.0; - - if (facets.Count <= 3) - return facets; - - // Remove too granular facets. - if (facets.Any(x => x.Value.UpperValue != null && (double)x.Value.UpperValue == 25.0)) - { - facets.RemoveFacet(5.0, true); - facets.RemoveFacet(10.0, true); - } - - var result = new List(); - var expIndexes = new HashSet(); - var lastIndex = facets.Count - 1; - - // Get exponential distributed indexes. - for (var i = 0.0; i < lastIndex; ++i) - { - var x = (int)Math.Floor(Math.Pow(expFactor, i / flatten)); - expIndexes.Add(x); - } - - for (var index = 0; index <= lastIndex; ++index) - { - var facet = facets[index]; - - // Always return first, last and selected facets. - if (index == 0 || index == lastIndex || facet.Value.IsSelected) - { - result.Add(facet); - } - else if (expIndexes.Contains(index) && result.Count < maxNumber && index < (lastIndex - 1)) - { - result.Add(facet); - } - } - - return result; - } - - public static IEnumerable GetRatings() - { - var count = 0; - for (double rate = 1.0; rate <= 5.0; ++rate) - { - yield return new FacetValue(rate, IndexTypeCode.Double) - { - DisplayOrder = ++count - }; - } - } + public static IEnumerable GetRatings() + { + var count = 0; + for (double rate = 1.0; rate <= 5.0; ++rate) + { + yield return new FacetValue(rate, IndexTypeCode.Double) + { + DisplayOrder = ++count + }; + } + } public static IQueryable GetCustomersByNumberOfPosts( IRepository forumPostRepository, @@ -178,7 +166,7 @@ group p by p.CustomerId into grp var query = groupQuery .OrderByDescending(x => x.Count) .Select(x => x.Customer) - .Where(x => x.CustomerRoles.FirstOrDefault(y => y.SystemName == SystemCustomerRoleNames.Guests) == null && !x.Deleted && x.Active && !x.IsSystemAccount); + .Where(x => x.CustomerRoleMappings.FirstOrDefault(y => y.CustomerRole.SystemName == SystemCustomerRoleNames.Guests) == null && !x.Deleted && x.Active && !x.IsSystemAccount); return query; } diff --git a/src/Libraries/SmartStore.Services/Search/Extensions/UrlHelperExtensions.cs b/src/Libraries/SmartStore.Services/Search/Extensions/UrlHelperExtensions.cs index 1f07ae6323..32e074e7ba 100644 --- a/src/Libraries/SmartStore.Services/Search/Extensions/UrlHelperExtensions.cs +++ b/src/Libraries/SmartStore.Services/Search/Extensions/UrlHelperExtensions.cs @@ -4,30 +4,30 @@ namespace SmartStore.Services.Search.Extensions { - public static class UrlHelperExtensions - { - public static string FacetToggle(this UrlHelper urlHelper, Facet facet) - { - var facetUrlHelper = EngineContext.Current.Resolve(); - return facetUrlHelper.Toggle(facet); - } + public static class UrlHelperExtensions + { + public static string FacetToggle(this UrlHelper urlHelper, Facet facet) + { + var facetUrlHelper = EngineContext.Current.Resolve(); + return facetUrlHelper.Toggle(facet); + } - public static string FacetAdd(this UrlHelper urlHelper, params Facet[] facets) - { - var facetUrlHelper = EngineContext.Current.Resolve(); - return facetUrlHelper.Add(facets); - } + public static string FacetAdd(this UrlHelper urlHelper, params Facet[] facets) + { + var facetUrlHelper = EngineContext.Current.Resolve(); + return facetUrlHelper.Add(facets); + } - public static string FacetRemove(this UrlHelper urlHelper, params Facet[] facets) - { - var facetUrlHelper = EngineContext.Current.Resolve(); - return facetUrlHelper.Remove(facets); - } + public static string FacetRemove(this UrlHelper urlHelper, params Facet[] facets) + { + var facetUrlHelper = EngineContext.Current.Resolve(); + return facetUrlHelper.Remove(facets); + } - public static string GetFacetQueryName(this UrlHelper urlHelper, Facet facet) - { - var facetUrlHelper = EngineContext.Current.Resolve(); - return facetUrlHelper.GetQueryName(facet); - } - } + public static string GetFacetQueryName(this UrlHelper urlHelper, Facet facet) + { + var facetUrlHelper = EngineContext.Current.Resolve(); + return facetUrlHelper.GetQueryName(facet); + } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchEvents.cs b/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchEvents.cs index 7c1c6f986c..91b55db08a 100644 --- a/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchEvents.cs +++ b/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchEvents.cs @@ -1,29 +1,29 @@ namespace SmartStore.Services.Search { public class ForumSearchingEvent - { + { public ForumSearchingEvent(ForumSearchQuery query) { - Guard.NotNull(query, nameof(query)); + Guard.NotNull(query, nameof(query)); - Query = query; - } + Query = query; + } public ForumSearchQuery Query { get; private set; } } - public class ForumSearchedEvent + public class ForumSearchedEvent { - public ForumSearchedEvent(ForumSearchQuery query, ForumSearchResult result) - { - Guard.NotNull(query, nameof(query)); - Guard.NotNull(result, nameof(result)); + public ForumSearchedEvent(ForumSearchQuery query, ForumSearchResult result) + { + Guard.NotNull(query, nameof(query)); + Guard.NotNull(result, nameof(result)); - Query = query; - Result = result; - } + Query = query; + Result = result; + } - public ForumSearchQuery Query { get; private set; } - public ForumSearchResult Result { get; private set; } - } + public ForumSearchQuery Query { get; private set; } + public ForumSearchResult Result { get; private set; } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchQuery.cs b/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchQuery.cs index 9fd25c0d3a..53313b67cc 100644 --- a/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchQuery.cs +++ b/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchQuery.cs @@ -8,34 +8,34 @@ namespace SmartStore.Services.Search { public partial class ForumSearchQuery : SearchQuery, ICloneable - { + { /// /// Initializes a new instance of the class without a search term being set. /// public ForumSearchQuery() - : base((string[])null, null) - { - } - - public ForumSearchQuery(string field, string term, SearchMode mode = SearchMode.Contains, bool escape = true, bool isFuzzySearch = false) - : base(field.HasValue() ? new[] { field } : null, term, mode, escape, isFuzzySearch) - { - } - - public ForumSearchQuery(string[] fields, string term, SearchMode mode = SearchMode.Contains, bool escape = true, bool isFuzzySearch = false) - : base(fields, term, mode, escape, isFuzzySearch) - { - } - - public ForumSearchQuery Clone() - { - return (ForumSearchQuery)this.MemberwiseClone(); - } - - object ICloneable.Clone() - { - return this.MemberwiseClone(); - } + : base((string[])null, null) + { + } + + public ForumSearchQuery(string field, string term, SearchMode mode = SearchMode.Contains, bool escape = true, bool isFuzzySearch = false) + : base(field.HasValue() ? new[] { field } : null, term, mode, escape, isFuzzySearch) + { + } + + public ForumSearchQuery(string[] fields, string term, SearchMode mode = SearchMode.Contains, bool escape = true, bool isFuzzySearch = false) + : base(fields, term, mode, escape, isFuzzySearch) + { + } + + public ForumSearchQuery Clone() + { + return (ForumSearchQuery)this.MemberwiseClone(); + } + + object ICloneable.Clone() + { + return this.MemberwiseClone(); + } #region Fluent builder @@ -83,7 +83,7 @@ public ForumSearchQuery VisibleOnly(Customer customer, bool includeCustomerRoles if (includeCustomerRoles) { - var allowedRoleIds = customer.CustomerRoles.Where(x => x.Active).Select(x => x.Id).ToArray(); + var allowedRoleIds = customer.GetRoleIds(); if (allowedRoleIds != null && allowedRoleIds.Length > 0) { var roleIds = allowedRoleIds.Where(x => x != 0).Distinct().ToList(); diff --git a/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchResult.cs b/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchResult.cs index 627fb954a1..540a93de22 100644 --- a/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchResult.cs +++ b/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchResult.cs @@ -10,55 +10,62 @@ namespace SmartStore.Services.Search public partial class ForumSearchResult { private readonly Func> _hitsFactory; - private IPagedList _hits; + private IPagedList _hits; - public ForumSearchResult( - ISearchEngine engine, + public ForumSearchResult( + ISearchEngine engine, ForumSearchQuery query, - int totalHitsCount, - Func> hitsFactory, - string[] spellCheckerSuggestions, + int totalHitsCount, + int[] hitsEntityIds, + Func> hitsFactory, + string[] spellCheckerSuggestions, IDictionary facets) - { - Guard.NotNull(query, nameof(query)); + { + Guard.NotNull(query, nameof(query)); - Engine = engine; - Query = query; - SpellCheckerSuggestions = spellCheckerSuggestions ?? new string[0]; + Engine = engine; + Query = query; + SpellCheckerSuggestions = spellCheckerSuggestions ?? new string[0]; Facets = facets ?? new Dictionary(); _hitsFactory = hitsFactory ?? (() => new List()); - TotalHitsCount = totalHitsCount; - } + HitsEntityIds = hitsEntityIds ?? new int[0]; + TotalHitsCount = totalHitsCount; + } /// /// Constructor for an instance without any search hits /// /// Forum search query public ForumSearchResult(ForumSearchQuery query) - : this(null, query, 0, () => new List(), null, null) - { - } + : this(null, query, 0, null, () => new List(), null, null) + { + } + + /// + /// Entity identifiers of found forum posts. + /// + public int[] HitsEntityIds { get; private set; } /// /// Forum posts found. /// public IPagedList Hits - { - get - { - if (_hits == null) - { - var entities = TotalHitsCount == 0 - ? new List() - : _hitsFactory.Invoke(); + { + get + { + if (_hits == null) + { + var entities = TotalHitsCount == 0 + ? new List() + : _hitsFactory.Invoke(); - _hits = new PagedList(entities, Query.PageIndex, Query.Take, TotalHitsCount); - } + _hits = new PagedList(entities, Query.PageIndex, Query.Take, TotalHitsCount); + } - return _hits; - } - } + return _hits; + } + } public int TotalHitsCount { get; } @@ -67,13 +74,13 @@ public IPagedList Hits /// public ForumSearchQuery Query { get; private set; } - /// - /// Gets spell checking suggestions/corrections. - /// - public string[] SpellCheckerSuggestions { get; set; } + /// + /// Gets spell checking suggestions/corrections. + /// + public string[] SpellCheckerSuggestions { get; set; } public IDictionary Facets { get; private set; } - public ISearchEngine Engine { get; private set; } + public ISearchEngine Engine { get; private set; } } } diff --git a/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchService.cs b/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchService.cs index 1622b6008f..0cff069cf5 100644 --- a/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchService.cs +++ b/src/Libraries/SmartStore.Services/Search/Forum/ForumSearchService.cs @@ -13,24 +13,24 @@ namespace SmartStore.Services.Search { public partial class ForumSearchService : SearchServiceBase, IForumSearchService { - private readonly ICommonServices _services; - private readonly IIndexManager _indexManager; + private readonly ICommonServices _services; + private readonly IIndexManager _indexManager; private readonly Lazy _forumService; private readonly ILogger _logger; - private readonly UrlHelper _urlHelper; + private readonly UrlHelper _urlHelper; - public ForumSearchService( - ICommonServices services, - IIndexManager indexManager, + public ForumSearchService( + ICommonServices services, + IIndexManager indexManager, Lazy forumService, - ILogger logger, - UrlHelper urlHelper) - { - _services = services; - _indexManager = indexManager; + ILogger logger, + UrlHelper urlHelper) + { + _services = services; + _indexManager = indexManager; _forumService = forumService; - _logger = logger; - _urlHelper = urlHelper; + _logger = logger; + _urlHelper = urlHelper; } /// @@ -39,59 +39,60 @@ public ForumSearchService( /// Search query. /// Forum search result. protected virtual ForumSearchResult SearchDirect(ForumSearchQuery searchQuery) - { - // Fallback to linq search. - var linqForumSearchService = _services.Container.ResolveNamed("linq"); - var result = linqForumSearchService.Search(searchQuery, true); + { + // Fallback to linq search. + var linqForumSearchService = _services.Container.ResolveNamed("linq"); + var result = linqForumSearchService.Search(searchQuery, true); return result; - } + } public ForumSearchResult Search(ForumSearchQuery searchQuery, bool direct = false) { - Guard.NotNull(searchQuery, nameof(searchQuery)); - Guard.NotNegative(searchQuery.Take, nameof(searchQuery.Take)); - - var provider = _indexManager.GetIndexProvider("Forum"); - - if (!direct && provider != null) - { - var indexStore = provider.GetIndexStore("Forum"); - if (indexStore.Exists) - { - var searchEngine = provider.GetSearchEngine(indexStore, searchQuery); - var stepPrefix = searchEngine.GetType().Name + " - "; - var totalCount = 0; - string[] spellCheckerSuggestions = null; - IEnumerable searchHits; - Func> hitsFactory = null; + Guard.NotNull(searchQuery, nameof(searchQuery)); + Guard.NotNegative(searchQuery.Take, nameof(searchQuery.Take)); + + var provider = _indexManager.GetIndexProvider("Forum"); + + if (!direct && provider != null) + { + var indexStore = provider.GetIndexStore("Forum"); + if (indexStore.Exists) + { + var searchEngine = provider.GetSearchEngine(indexStore, searchQuery); + var stepPrefix = searchEngine.GetType().Name + " - "; + var totalCount = 0; + int[] hitsEntityIds = null; + string[] spellCheckerSuggestions = null; + IEnumerable searchHits; + Func> hitsFactory = null; IDictionary facets = null; _services.EventPublisher.Publish(new ForumSearchingEvent(searchQuery)); - if (searchQuery.Take > 0) - { - using (_services.Chronometer.Step(stepPrefix + "Count")) - { - totalCount = searchEngine.Count(); - // Fix paging boundaries. - if (searchQuery.Skip > 0 && searchQuery.Skip >= totalCount) - { - searchQuery.Slice((totalCount / searchQuery.Take) * searchQuery.Take, searchQuery.Take); - } - } - - using (_services.Chronometer.Step(stepPrefix + "Hits")) - { - searchHits = searchEngine.Search(); - } - - if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithHits)) - { - using (_services.Chronometer.Step(stepPrefix + "Collect")) - { - var postIds = searchHits.Select(x => x.EntityId).ToArray(); - hitsFactory = () => _forumService.Value.GetPostsByIds(postIds); + if (searchQuery.Take > 0) + { + using (_services.Chronometer.Step(stepPrefix + "Count")) + { + totalCount = searchEngine.Count(); + // Fix paging boundaries. + if (searchQuery.Skip > 0 && searchQuery.Skip >= totalCount) + { + searchQuery.Slice((totalCount / searchQuery.Take) * searchQuery.Take, searchQuery.Take); + } + } + + using (_services.Chronometer.Step(stepPrefix + "Hits")) + { + searchHits = searchEngine.Search(); + } + + if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithHits)) + { + using (_services.Chronometer.Step(stepPrefix + "Collect")) + { + hitsEntityIds = searchHits.Select(x => x.EntityId).ToArray(); + hitsFactory = () => _forumService.Value.GetPostsByIds(hitsEntityIds); } } @@ -111,47 +112,48 @@ public ForumSearchResult Search(ForumSearchQuery searchQuery, bool direct = fals } } - if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithSuggestions)) - { - try - { - using (_services.Chronometer.Step(stepPrefix + "Spellcheck")) - { - spellCheckerSuggestions = searchEngine.CheckSpelling(); - } - } - catch (Exception ex) - { - // Spell checking should not break the search. - _logger.Error(ex); - } - } - - var result = new ForumSearchResult( - searchEngine, - searchQuery, - totalCount, - hitsFactory, - spellCheckerSuggestions, + if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithSuggestions)) + { + try + { + using (_services.Chronometer.Step(stepPrefix + "Spellcheck")) + { + spellCheckerSuggestions = searchEngine.CheckSpelling(); + } + } + catch (Exception ex) + { + // Spell checking should not break the search. + _logger.Error(ex); + } + } + + var result = new ForumSearchResult( + searchEngine, + searchQuery, + totalCount, + hitsEntityIds, + hitsFactory, + spellCheckerSuggestions, facets); - _services.EventPublisher.Publish(new ForumSearchedEvent(searchQuery, result)); + _services.EventPublisher.Publish(new ForumSearchedEvent(searchQuery, result)); - return result; - } - else if (searchQuery.Origin.IsCaseInsensitiveEqual("Boards/Search")) - { - IndexingRequiredNotification(_services, _urlHelper); - } - } + return result; + } + else if (searchQuery.Origin.IsCaseInsensitiveEqual("Boards/Search")) + { + IndexingRequiredNotification(_services, _urlHelper); + } + } - return SearchDirect(searchQuery); - } + return SearchDirect(searchQuery); + } - public IQueryable PrepareQuery(ForumSearchQuery searchQuery, IQueryable baseQuery = null) + public IQueryable PrepareQuery(ForumSearchQuery searchQuery, IQueryable baseQuery = null) { - var linqForumSearchService = _services.Container.ResolveNamed("linq"); - return linqForumSearchService.PrepareQuery(searchQuery, baseQuery); - } - } + var linqForumSearchService = _services.Container.ResolveNamed("linq"); + return linqForumSearchService.PrepareQuery(searchQuery, baseQuery); + } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Forum/IForumSearchService.cs b/src/Libraries/SmartStore.Services/Search/Forum/IForumSearchService.cs index e6a7f135af..a14dce90dc 100644 --- a/src/Libraries/SmartStore.Services/Search/Forum/IForumSearchService.cs +++ b/src/Libraries/SmartStore.Services/Search/Forum/IForumSearchService.cs @@ -4,7 +4,7 @@ namespace SmartStore.Services.Search { public partial interface IForumSearchService - { + { /// /// Searches for forum posts. /// @@ -20,5 +20,5 @@ public partial interface IForumSearchService /// Optional query used to build the forum post query. /// Forum post queryable. IQueryable PrepareQuery(ForumSearchQuery searchQuery, IQueryable baseQuery = null); - } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Forum/LinqForumSearchService.cs b/src/Libraries/SmartStore.Services/Search/Forum/LinqForumSearchService.cs index cce742613e..0d9257c4f1 100644 --- a/src/Libraries/SmartStore.Services/Search/Forum/LinqForumSearchService.cs +++ b/src/Libraries/SmartStore.Services/Search/Forum/LinqForumSearchService.cs @@ -39,7 +39,7 @@ public LinqForumSearchService( IForumService forumService, ICommonServices services, CustomerSettings customerSettings) - { + { _forumPostRepository = forumPostRepository; _forumTopicRepository = forumTopicRepository; _forumRepository = forumRepository; @@ -47,7 +47,7 @@ public LinqForumSearchService( _storeMappingRepository = storeMappingRepository; _aclRepository = aclRepository; _forumService = forumService; - _services = services; + _services = services; _customerSettings = customerSettings; T = NullLocalizer.Instance; @@ -381,15 +381,16 @@ protected virtual IDictionary GetFacets(ForumSearchQuery sea } public ForumSearchResult Search(ForumSearchQuery searchQuery, bool direct = false) - { + { _services.EventPublisher.Publish(new ForumSearchingEvent(searchQuery)); - var totalHits = 0; - Func> hitsFactory = null; + var totalHits = 0; + int[] hitsEntityIds = null; + Func> hitsFactory = null; IDictionary facets = null; if (searchQuery.Take > 0) - { + { var query = GetPostQuery(searchQuery, null); totalHits = query.Count(); @@ -406,8 +407,8 @@ public ForumSearchResult Search(ForumSearchQuery searchQuery, bool direct = fals .Skip(() => skip) .Take(() => searchQuery.Take); - var ids = query.Select(x => x.Id).ToArray(); - hitsFactory = () => _forumService.GetPostsByIds(ids); + hitsEntityIds = query.Select(x => x.Id).ToArray(); + hitsFactory = () => _forumService.GetPostsByIds(hitsEntityIds); } if (searchQuery.ResultFlags.HasFlag(SearchResultFlags.WithFacets) && searchQuery.FacetDescriptors.Any()) @@ -417,21 +418,22 @@ public ForumSearchResult Search(ForumSearchQuery searchQuery, bool direct = fals } var result = new ForumSearchResult( - null, - searchQuery, - totalHits, - hitsFactory, - null, + null, + searchQuery, + totalHits, + hitsEntityIds, + hitsFactory, + null, facets); _services.EventPublisher.Publish(new ForumSearchedEvent(searchQuery, result)); - return result; - } + return result; + } - public IQueryable PrepareQuery(ForumSearchQuery searchQuery, IQueryable baseQuery = null) - { + public IQueryable PrepareQuery(ForumSearchQuery searchQuery, IQueryable baseQuery = null) + { return GetPostQuery(searchQuery, baseQuery); } - } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Forum/Modelling/ForumSearchQueryAliasMapper.cs b/src/Libraries/SmartStore.Services/Search/Forum/Modelling/ForumSearchQueryAliasMapper.cs index ccded4cd3f..de2c8be999 100644 --- a/src/Libraries/SmartStore.Services/Search/Forum/Modelling/ForumSearchQueryAliasMapper.cs +++ b/src/Libraries/SmartStore.Services/Search/Forum/Modelling/ForumSearchQueryAliasMapper.cs @@ -9,63 +9,63 @@ namespace SmartStore.Services.Search.Modelling { public class ForumSearchQueryAliasMapper : IForumSearchQueryAliasMapper - { - private const string ALL_FORUM_COMMONFACET_ALIAS_BY_KIND_KEY = "search.forum.commonfacet.alias.kind.mappings.all"; + { + private const string ALL_FORUM_COMMONFACET_ALIAS_BY_KIND_KEY = "search.forum.commonfacet.alias.kind.mappings.all"; - private readonly ICacheManager _cacheManager; - private readonly ISettingService _settingService; - private readonly ILanguageService _languageService; + private readonly ICacheManager _cacheManager; + private readonly ISettingService _settingService; + private readonly ILanguageService _languageService; - public ForumSearchQueryAliasMapper( - ICacheManager cacheManager, - ISettingService settingService, - ILanguageService languageService) - { - _cacheManager = cacheManager; - _settingService = settingService; - _languageService = languageService; - } + public ForumSearchQueryAliasMapper( + ICacheManager cacheManager, + ISettingService settingService, + ILanguageService languageService) + { + _cacheManager = cacheManager; + _settingService = settingService; + _languageService = languageService; + } - protected virtual IDictionary GetCommonFacetAliasByGroupKindMappings() - { - return _cacheManager.Get(ALL_FORUM_COMMONFACET_ALIAS_BY_KIND_KEY, () => - { - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + protected virtual IDictionary GetCommonFacetAliasByGroupKindMappings() + { + return _cacheManager.Get(ALL_FORUM_COMMONFACET_ALIAS_BY_KIND_KEY, () => + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - var groupKinds = new FacetGroupKind[] - { + var groupKinds = new FacetGroupKind[] + { FacetGroupKind.Forum, FacetGroupKind.Customer, FacetGroupKind.Date - }; + }; - foreach (var language in _languageService.GetAllLanguages()) - { - foreach (var groupKind in groupKinds) - { - var key = FacetUtility.GetFacetAliasSettingKey(groupKind, language.Id, "Forum"); - var value = _settingService.GetSettingByKey(key); - if (value.HasValue()) - { - result.Add(key, value); - } - } - } + foreach (var language in _languageService.GetAllLanguages()) + { + foreach (var groupKind in groupKinds) + { + var key = FacetUtility.GetFacetAliasSettingKey(groupKind, language.Id, "Forum"); + var value = _settingService.GetSettingByKey(key); + if (value.HasValue()) + { + result.Add(key, value); + } + } + } - return result; - }); - } + return result; + }); + } - public void ClearCommonFacetCache() - { - _cacheManager.Remove(ALL_FORUM_COMMONFACET_ALIAS_BY_KIND_KEY); - } + public void ClearCommonFacetCache() + { + _cacheManager.Remove(ALL_FORUM_COMMONFACET_ALIAS_BY_KIND_KEY); + } - public string GetCommonFacetAliasByGroupKind(FacetGroupKind kind, int languageId) - { - var mappings = GetCommonFacetAliasByGroupKindMappings(); + public string GetCommonFacetAliasByGroupKind(FacetGroupKind kind, int languageId) + { + var mappings = GetCommonFacetAliasByGroupKindMappings(); - return mappings.Get(FacetUtility.GetFacetAliasSettingKey(kind, languageId, "Forum")); - } - } + return mappings.Get(FacetUtility.GetFacetAliasSettingKey(kind, languageId, "Forum")); + } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Forum/Modelling/ForumSearchQueryFactory.cs b/src/Libraries/SmartStore.Services/Search/Forum/Modelling/ForumSearchQueryFactory.cs index 6d14641314..6310fbbfa4 100644 --- a/src/Libraries/SmartStore.Services/Search/Forum/Modelling/ForumSearchQueryFactory.cs +++ b/src/Libraries/SmartStore.Services/Search/Forum/Modelling/ForumSearchQueryFactory.cs @@ -144,14 +144,20 @@ private void AddFacet( switch (kind) { case FacetGroupKind.Forum: + if (_searchSettings.ForumDisabled) + return; fieldName = "forumid"; displayOrder = _searchSettings.ForumDisplayOrder; break; case FacetGroupKind.Customer: + if (_searchSettings.CustomerDisabled) + return; fieldName = "customerid"; displayOrder = _searchSettings.CustomerDisplayOrder; break; case FacetGroupKind.Date: + if (_searchSettings.DateDisabled) + return; fieldName = "createdon"; displayOrder = _searchSettings.DateDisplayOrder; break; diff --git a/src/Libraries/SmartStore.Services/Search/Forum/Modelling/ForumSearchQueryModelBinder.cs b/src/Libraries/SmartStore.Services/Search/Forum/Modelling/ForumSearchQueryModelBinder.cs index ec47720bb5..cc3e935fe7 100644 --- a/src/Libraries/SmartStore.Services/Search/Forum/Modelling/ForumSearchQueryModelBinder.cs +++ b/src/Libraries/SmartStore.Services/Search/Forum/Modelling/ForumSearchQueryModelBinder.cs @@ -4,39 +4,39 @@ namespace SmartStore.Services.Search.Modelling { [ModelBinderType(typeof(ForumSearchQuery))] - public class ForumSearchQueryModelBinder : IModelBinder - { - private readonly IForumSearchQueryFactory _factory; + public class ForumSearchQueryModelBinder : IModelBinder + { + private readonly IForumSearchQueryFactory _factory; - public ForumSearchQueryModelBinder(IForumSearchQueryFactory factory) - { - _factory = factory; - } + public ForumSearchQueryModelBinder(IForumSearchQueryFactory factory) + { + _factory = factory; + } - public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) - { - if (_factory.Current != null) - { - // Don't bind again for current request. - return _factory.Current; - } + public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) + { + if (_factory.Current != null) + { + // Don't bind again for current request. + return _factory.Current; + } - if (controllerContext.IsChildAction) - { + if (controllerContext.IsChildAction) + { // Never attempt to bind in child actions. We require the binding to happen // in a parent action. If the child action is part of a request with an already bound // 'ForumSearchQuery', good for you :-) You'll get an instance, but null otherwise. return _factory.Current; - } + } - var modelType = bindingContext.ModelType; - if (modelType != typeof(ForumSearchQuery)) - { - return new ForumSearchQuery(); - } + var modelType = bindingContext.ModelType; + if (modelType != typeof(ForumSearchQuery)) + { + return new ForumSearchQuery(); + } - var query = _factory.CreateFromQuery(); - return query; - } - } + var query = _factory.CreateFromQuery(); + return query; + } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Forum/Modelling/IForumSearchQueryAliasMapper.cs b/src/Libraries/SmartStore.Services/Search/Forum/Modelling/IForumSearchQueryAliasMapper.cs index 70b8785535..042dd41947 100644 --- a/src/Libraries/SmartStore.Services/Search/Forum/Modelling/IForumSearchQueryAliasMapper.cs +++ b/src/Libraries/SmartStore.Services/Search/Forum/Modelling/IForumSearchQueryAliasMapper.cs @@ -2,19 +2,19 @@ namespace SmartStore.Services.Search.Modelling { - public interface IForumSearchQueryAliasMapper - { - /// - /// Clears all cached common facet mappings - /// - void ClearCommonFacetCache(); + public interface IForumSearchQueryAliasMapper + { + /// + /// Clears all cached common facet mappings + /// + void ClearCommonFacetCache(); - /// - /// Get the common facet alias by facet group kind - /// - /// Facet group kind - /// Language identifier - /// Common facet alias - string GetCommonFacetAliasByGroupKind(FacetGroupKind kind, int languageId); - } + /// + /// Get the common facet alias by facet group kind + /// + /// Facet group kind + /// Language identifier + /// Common facet alias + string GetCommonFacetAliasByGroupKind(FacetGroupKind kind, int languageId); + } } diff --git a/src/Libraries/SmartStore.Services/Search/Forum/Modelling/IForumSearchQueryFactory.cs b/src/Libraries/SmartStore.Services/Search/Forum/Modelling/IForumSearchQueryFactory.cs index 05ca8c4866..fc936ce5a6 100644 --- a/src/Libraries/SmartStore.Services/Search/Forum/Modelling/IForumSearchQueryFactory.cs +++ b/src/Libraries/SmartStore.Services/Search/Forum/Modelling/IForumSearchQueryFactory.cs @@ -1,6 +1,6 @@ namespace SmartStore.Services.Search.Modelling { - public interface IForumSearchQueryFactory + public interface IForumSearchQueryFactory { /// /// Creates a instance from the current @@ -13,5 +13,5 @@ public interface IForumSearchQueryFactory /// The last created query instance. The MVC model binder uses this property to avoid repeated binding. /// ForumSearchQuery Current { get; } - } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Rendering/FacetTemplateProvider.cs b/src/Libraries/SmartStore.Services/Search/Rendering/FacetTemplateProvider.cs index c8e4860470..f8c30e06f6 100644 --- a/src/Libraries/SmartStore.Services/Search/Rendering/FacetTemplateProvider.cs +++ b/src/Libraries/SmartStore.Services/Search/Rendering/FacetTemplateProvider.cs @@ -5,23 +5,23 @@ namespace SmartStore.Services.Search.Rendering { - public class FacetTemplateProvider : IFacetTemplateProvider - { - private readonly IEnumerable _selectors; + public class FacetTemplateProvider : IFacetTemplateProvider + { + private readonly IEnumerable _selectors; - public FacetTemplateProvider(IEnumerable selectors) - { - _selectors = selectors; - } + public FacetTemplateProvider(IEnumerable selectors) + { + _selectors = selectors; + } - public RouteInfo GetTemplateRoute(FacetGroup facetGroup) - { - var route = _selectors - .OrderByDescending(x => x.Ordinal) - .Select(x => x.GetTemplateRoute(facetGroup)) - .FirstOrDefault(x => x != null); + public RouteInfo GetTemplateRoute(FacetGroup facetGroup) + { + var route = _selectors + .OrderByDescending(x => x.Ordinal) + .Select(x => x.GetTemplateRoute(facetGroup)) + .FirstOrDefault(x => x != null); - return route; - } - } + return route; + } + } } diff --git a/src/Libraries/SmartStore.Services/Search/Rendering/IFacetTemplateProvider.cs b/src/Libraries/SmartStore.Services/Search/Rendering/IFacetTemplateProvider.cs index 45893675d6..9f8eb7757a 100644 --- a/src/Libraries/SmartStore.Services/Search/Rendering/IFacetTemplateProvider.cs +++ b/src/Libraries/SmartStore.Services/Search/Rendering/IFacetTemplateProvider.cs @@ -3,8 +3,8 @@ namespace SmartStore.Services.Search.Rendering { - public interface IFacetTemplateProvider - { - RouteInfo GetTemplateRoute(FacetGroup facetGroup); - } + public interface IFacetTemplateProvider + { + RouteInfo GetTemplateRoute(FacetGroup facetGroup); + } } diff --git a/src/Libraries/SmartStore.Services/Search/Rendering/IFacetTemplateSelector.cs b/src/Libraries/SmartStore.Services/Search/Rendering/IFacetTemplateSelector.cs index d7efdbc334..3d6f9fbea8 100644 --- a/src/Libraries/SmartStore.Services/Search/Rendering/IFacetTemplateSelector.cs +++ b/src/Libraries/SmartStore.Services/Search/Rendering/IFacetTemplateSelector.cs @@ -3,8 +3,8 @@ namespace SmartStore.Services.Search.Rendering { - public interface IFacetTemplateSelector : IOrdered - { - RouteInfo GetTemplateRoute(FacetGroup facetGroup); - } + public interface IFacetTemplateSelector : IOrdered + { + RouteInfo GetTemplateRoute(FacetGroup facetGroup); + } } diff --git a/src/Libraries/SmartStore.Services/Security/AclService.cs b/src/Libraries/SmartStore.Services/Security/AclService.cs index 2b77fadb24..c92db21540 100644 --- a/src/Libraries/SmartStore.Services/Security/AclService.cs +++ b/src/Libraries/SmartStore.Services/Security/AclService.cs @@ -13,51 +13,51 @@ namespace SmartStore.Services.Security { public partial class AclService : IAclService { - /// - /// 0 = segment (EntityName.IdRange) - /// - const string ACL_SEGMENT_KEY = "acl:range-{0}"; - const string ACL_SEGMENT_PATTERN = "acl:range-*"; + /// + /// 0 = segment (EntityName.IdRange) + /// + const string ACL_SEGMENT_KEY = "acl:range-{0}"; + internal const string ACL_SEGMENT_PATTERN = "acl:range-*"; private readonly IRepository _aclRecordRepository; private readonly Work _workContext; private readonly ICacheManager _cacheManager; - private readonly ICustomerService _customerService; + private readonly Work _customerService; - private bool? _hasActiveAcl; + private bool? _hasActiveAcl; public AclService( - ICacheManager cacheManager, - Work workContext, - IRepository aclRecordRepository, - ICustomerService customerService) + ICacheManager cacheManager, + Work workContext, + IRepository aclRecordRepository, + Work customerService) { _cacheManager = cacheManager; _workContext = workContext; _aclRecordRepository = aclRecordRepository; - _customerService = customerService; - } - - public bool HasActiveAcl - { - get - { - if (!_hasActiveAcl.HasValue) - { - _hasActiveAcl = _aclRecordRepository.TableUntracked.Any(x => !x.IsIdle); - } - return _hasActiveAcl.Value; - } - } + _customerService = customerService; + } + + public bool HasActiveAcl + { + get + { + if (!_hasActiveAcl.HasValue) + { + _hasActiveAcl = _aclRecordRepository.TableUntracked.Any(x => !x.IsIdle); + } + return _hasActiveAcl.Value; + } + } public virtual void DeleteAclRecord(AclRecord aclRecord) { - Guard.NotNull(aclRecord,nameof(aclRecord)); + Guard.NotNull(aclRecord, nameof(aclRecord)); _aclRecordRepository.Delete(aclRecord); - ClearCacheSegment(aclRecord.EntityName, aclRecord.EntityId); - } + ClearCacheSegment(aclRecord.EntityName, aclRecord.EntityId); + } public virtual AclRecord GetAclRecordById(int aclRecordId) { @@ -70,64 +70,73 @@ public virtual AclRecord GetAclRecordById(int aclRecordId) public IList GetAclRecords(T entity) where T : BaseEntity, IAclSupported { - Guard.NotNull(entity, nameof(entity)); + Guard.NotNull(entity, nameof(entity)); - int entityId = entity.Id; + int entityId = entity.Id; string entityName = entity.GetEntityName(); - return GetAclRecordsFor(entityName, entityId); + return GetAclRecordsFor(entityName, entityId); + } + + public virtual IList GetAclRecordsFor(string entityName, int entityId) + { + Guard.IsPositive(entityId, nameof(entityId)); + Guard.NotEmpty(entityName, nameof(entityName)); + + var query = from ur in _aclRecordRepository.Table + where ur.EntityId == entityId && + ur.EntityName == entityName + select ur; + var aclRecords = query.ToList(); + return aclRecords; + } + + public virtual void SaveAclMappings(T entity, params int[] selectedCustomerRoleIds) where T : BaseEntity, IAclSupported + { + var existingAclRecords = GetAclRecords(entity); + var allCustomerRoles = _customerService.Value.GetAllCustomerRoles(true); + entity.SubjectToAcl = selectedCustomerRoleIds.Length == 1 && selectedCustomerRoleIds[0] == 0 + ? false + : selectedCustomerRoleIds.Any(); + + foreach (var customerRole in allCustomerRoles) + { + if (selectedCustomerRoleIds != null && selectedCustomerRoleIds.Contains(customerRole.Id)) + { + // New role + if (!existingAclRecords.Any(x => x.CustomerRoleId == customerRole.Id)) + InsertAclRecord(entity, customerRole.Id); + } + else + { + // Removed role + var aclRecordToDelete = existingAclRecords.FirstOrDefault(x => x.CustomerRoleId == customerRole.Id); + if (aclRecordToDelete != null) + DeleteAclRecord(aclRecordToDelete); + } + } + + // TODO: Find a way to detect the context of the entity. Until then we don't check for modified props + //if (_aclRecordRepository.Context.TryGetModifiedProperty(entity, nameof(entity.SubjectToAcl), out _)) + //{ + _aclRecordRepository.Context.SaveChanges(); + //} } - public virtual IList GetAclRecordsFor(string entityName, int entityId) - { - Guard.IsPositive(entityId, nameof(entityId)); - Guard.NotEmpty(entityName, nameof(entityName)); - - var query = from ur in _aclRecordRepository.Table - where ur.EntityId == entityId && - ur.EntityName == entityName - select ur; - var aclRecords = query.ToList(); - return aclRecords; - } - - public virtual void SaveAclMappings(T entity, int[] selectedCustomerRoleIds) where T : BaseEntity, IAclSupported - { - var existingAclRecords = GetAclRecords(entity); - var allCustomerRoles = _customerService.GetAllCustomerRoles(true); - - foreach (var customerRole in allCustomerRoles) - { - if (selectedCustomerRoleIds != null && selectedCustomerRoleIds.Contains(customerRole.Id)) - { - // New role - if (!existingAclRecords.Any(x => x.CustomerRoleId == customerRole.Id)) - InsertAclRecord(entity, customerRole.Id); - } - else - { - // Removed role - var aclRecordToDelete = existingAclRecords.FirstOrDefault(x => x.CustomerRoleId == customerRole.Id); - if (aclRecordToDelete != null) - DeleteAclRecord(aclRecordToDelete); - } - } - } - - public virtual void InsertAclRecord(AclRecord aclRecord) + public virtual void InsertAclRecord(AclRecord aclRecord) { - Guard.NotNull(aclRecord, nameof(aclRecord)); + Guard.NotNull(aclRecord, nameof(aclRecord)); - _aclRecordRepository.Insert(aclRecord); + _aclRecordRepository.Insert(aclRecord); - ClearCacheSegment(aclRecord.EntityName, aclRecord.EntityId); - } + ClearCacheSegment(aclRecord.EntityName, aclRecord.EntityId); + } public virtual void InsertAclRecord(T entity, int customerRoleId) where T : BaseEntity, IAclSupported { - Guard.NotNull(entity, nameof(entity)); - - if (customerRoleId == 0) + Guard.NotNull(entity, nameof(entity)); + + if (customerRoleId == 0) throw new ArgumentOutOfRangeException(nameof(customerRoleId)); int entityId = entity.Id; @@ -145,127 +154,127 @@ public virtual void InsertAclRecord(T entity, int customerRoleId) where T : B public virtual void UpdateAclRecord(AclRecord aclRecord) { - Guard.NotNull(aclRecord, nameof(aclRecord)); - - _aclRecordRepository.Update(aclRecord); - - ClearCacheSegment(aclRecord.EntityName, aclRecord.EntityId); - } - - public virtual int[] GetCustomerRoleIdsWithAccessTo(string entityName, int entityId) - { - Guard.NotEmpty(entityName, nameof(entityName)); - - if (entityId <= 0) - return new int[0]; - - var cacheSegment = GetCacheSegment(entityName, entityId); - - if (!cacheSegment.TryGetValue(entityId, out var roleIds)) - { - return Array.Empty(); - } - - return roleIds; - } - - public bool Authorize(string entityName, int entityId) - { - return Authorize(entityName, entityId, _workContext.Value.CurrentCustomer?.CustomerRoles); - } - - public virtual bool Authorize(string entityName, int entityId, IEnumerable roles) - { - Guard.NotEmpty(entityName, nameof(entityName)); - - if (entityId <= 0) - return false; - - if (!HasActiveAcl) - return true; - - if (roles == null) - return false; - - foreach (var role in roles) - { - if (!role.Active) - continue; - - foreach (var role2Id in GetCustomerRoleIdsWithAccessTo(entityName, entityId)) - { - if (role.Id == role2Id) - { - // Yes, we have such permission - return true; - } - } - } - - // No permission granted - return false; - } - - #region Cache segmenting - - protected virtual IDictionary GetCacheSegment(string entityName, int entityId) - { - Guard.NotEmpty(entityName, nameof(entityName)); - - var segmentKey = GetSegmentKeyPart(entityName, entityId, out var minEntityId, out var maxEntityId); - var cacheKey = BuildCacheSegmentKey(segmentKey); - - return _cacheManager.Get(cacheKey, () => - { - var query = from sm in _aclRecordRepository.TableUntracked - where - sm.EntityId >= minEntityId && - sm.EntityId <= maxEntityId && - sm.EntityName == entityName - select sm; - - var mappings = query.ToLookup(x => x.EntityId, x => x.CustomerRoleId); - - var dict = new Dictionary(mappings.Count); - - foreach (var sm in mappings) - { - dict[sm.Key] = sm.ToArray(); - } - - return dict; - }); - } - - /// - /// Clears the cached segment from the cache - /// - protected virtual void ClearCacheSegment(string entityName, int entityId) - { - try - { - var segmentKey = GetSegmentKeyPart(entityName, entityId); - _cacheManager.Remove(BuildCacheSegmentKey(segmentKey)); - } - catch { } - } - - private string BuildCacheSegmentKey(string segment) - { - return String.Format(ACL_SEGMENT_KEY, segment); - } - - private string GetSegmentKeyPart(string entityName, int entityId) - { - return GetSegmentKeyPart(entityName, entityId, out _, out _); - } - - private string GetSegmentKeyPart(string entityName, int entityId, out int minId, out int maxId) - { - maxId = entityId.GetRange(1000, out minId); - return (entityName + "." + maxId.ToString()).ToLowerInvariant(); - } - - #endregion - } + Guard.NotNull(aclRecord, nameof(aclRecord)); + + _aclRecordRepository.Update(aclRecord); + + ClearCacheSegment(aclRecord.EntityName, aclRecord.EntityId); + } + + public virtual int[] GetCustomerRoleIdsWithAccessTo(string entityName, int entityId) + { + Guard.NotEmpty(entityName, nameof(entityName)); + + if (entityId <= 0) + return new int[0]; + + var cacheSegment = GetCacheSegment(entityName, entityId); + + if (!cacheSegment.TryGetValue(entityId, out var roleIds)) + { + return Array.Empty(); + } + + return roleIds; + } + + public bool Authorize(string entityName, int entityId) + { + return Authorize(entityName, entityId, _workContext.Value.CurrentCustomer?.CustomerRoleMappings?.Select(x => x.CustomerRole)); + } + + public virtual bool Authorize(string entityName, int entityId, IEnumerable roles) + { + Guard.NotEmpty(entityName, nameof(entityName)); + + if (entityId <= 0) + return false; + + if (!HasActiveAcl) + return true; + + if (roles == null) + return false; + + foreach (var role in roles) + { + if (!role.Active) + continue; + + foreach (var role2Id in GetCustomerRoleIdsWithAccessTo(entityName, entityId)) + { + if (role.Id == role2Id) + { + // Yes, we have such permission + return true; + } + } + } + + // No permission granted + return false; + } + + #region Cache segmenting + + protected virtual IDictionary GetCacheSegment(string entityName, int entityId) + { + Guard.NotEmpty(entityName, nameof(entityName)); + + var segmentKey = GetSegmentKeyPart(entityName, entityId, out var minEntityId, out var maxEntityId); + var cacheKey = BuildCacheSegmentKey(segmentKey); + + return _cacheManager.Get(cacheKey, () => + { + var query = from sm in _aclRecordRepository.TableUntracked + where + sm.EntityId >= minEntityId && + sm.EntityId <= maxEntityId && + sm.EntityName == entityName + select sm; + + var mappings = query.ToLookup(x => x.EntityId, x => x.CustomerRoleId); + + var dict = new Dictionary(mappings.Count); + + foreach (var sm in mappings) + { + dict[sm.Key] = sm.ToArray(); + } + + return dict; + }); + } + + /// + /// Clears the cached segment from the cache + /// + protected virtual void ClearCacheSegment(string entityName, int entityId) + { + try + { + var segmentKey = GetSegmentKeyPart(entityName, entityId); + _cacheManager.Remove(BuildCacheSegmentKey(segmentKey)); + } + catch { } + } + + private string BuildCacheSegmentKey(string segment) + { + return String.Format(ACL_SEGMENT_KEY, segment); + } + + private string GetSegmentKeyPart(string entityName, int entityId) + { + return GetSegmentKeyPart(entityName, entityId, out _, out _); + } + + private string GetSegmentKeyPart(string entityName, int entityId, out int minId, out int maxId) + { + (minId, maxId) = entityId.GetRange(1000); + return (entityName + "." + minId.ToString()).ToLowerInvariant(); + } + + #endregion + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Security/IAclService.cs b/src/Libraries/SmartStore.Services/Security/IAclService.cs index 5130dcc09f..c42785ee94 100644 --- a/src/Libraries/SmartStore.Services/Security/IAclService.cs +++ b/src/Libraries/SmartStore.Services/Security/IAclService.cs @@ -12,12 +12,12 @@ namespace SmartStore.Services.Security /// public partial interface IAclService { - /// - /// Gets a value indicating whether at least one ACL record is in active state system-wide - /// - bool HasActiveAcl { get; } - - /// + /// + /// Gets a value indicating whether at least one ACL record is in active state system-wide + /// + bool HasActiveAcl { get; } + + /// /// Deletes an ACL record /// /// ACL record @@ -29,7 +29,7 @@ public partial interface IAclService /// ACL record identifier /// ACL record AclRecord GetAclRecordById(int aclRecordId); - + /// /// Gets ACL records /// @@ -38,34 +38,34 @@ public partial interface IAclService /// ACL records IList GetAclRecords(T entity) where T : BaseEntity, IAclSupported; - /// - /// Gets ACL records - /// - /// Name of entity - /// Id of entity - /// ACL records - IList GetAclRecordsFor(string entityName, int entityId); - - /// - /// Save the ACL mappings for an entity - /// - /// Entity type - /// The entity - /// Array of selected customer role ids with access to the passed entity - void SaveAclMappings(T entity, int[] selectedCustomerRoleIds) where T : BaseEntity, IAclSupported; - - /// - /// Inserts an ACL record - /// - /// ACL record - void InsertAclRecord(AclRecord aclRecord); - + /// + /// Gets ACL records + /// + /// Name of entity + /// Id of entity + /// ACL records + IList GetAclRecordsFor(string entityName, int entityId); + + /// + /// Save the ACL mappings for an entity + /// + /// Entity type + /// The entity + /// Array of selected customer role ids with access to the passed entity + void SaveAclMappings(T entity, params int[] selectedCustomerRoleIds) where T : BaseEntity, IAclSupported; + + /// + /// Inserts an ACL record + /// + /// ACL record + void InsertAclRecord(AclRecord aclRecord); + /// /// Inserts an ACL record /// /// Type /// Entity - /// Customer role id + /// Customer role id void InsertAclRecord(T entity, int customerRoleId) where T : BaseEntity, IAclSupported; /// @@ -74,115 +74,115 @@ public partial interface IAclService /// ACL record void UpdateAclRecord(AclRecord aclRecord); - /// - /// Find customer role identifiers with granted access - /// - /// Entity name to check permission for - /// Entity id to check permission for - /// Customer role identifiers - int[] GetCustomerRoleIdsWithAccessTo(string entityName, int entityId); - - /// - /// Authorize ACL permission - /// - /// Type - /// Entity name to check permission for - /// Entity id to check permission for - /// true - authorized; otherwise, false - bool Authorize(string entityName, int entityId); - - ///// - ///// Authorize ACL permission - ///// - ///// Type - ///// Entity name to check permission for - ///// Entity id to check permission for - ///// Customer - ///// true - authorized; otherwise, false - //bool Authorize(string entityName, int entityId, Customer customer); - - /// - /// Authorize ACL permission - /// - /// Entity name to check permission for - /// Entity id to check permission for - /// Roles to check access permission for. Inactive roles will be skipped. - /// true - authorized; otherwise, false - bool Authorize(string entityName, int entityId, IEnumerable roles); - } - - public static class IAclServiceExtensions - { - /// - /// Find customer role identifiers with granted access - /// - /// Type - /// Entity - /// Customer role identifiers - public static int[] GetCustomerRoleIdsWithAccessTo(this IAclService aclService, T entity) where T : BaseEntity, IAclSupported - { - if (entity == null) - return new int[0]; - - return aclService.GetCustomerRoleIdsWithAccessTo(entity.GetEntityName(), entity.Id); - } - - public static bool Authorize(this IAclService aclService, string entityName, int entityId, Customer customer) - { - return aclService.Authorize(entityName, entityId, customer?.CustomerRoles); - } - - /// - /// Authorize ACL permission - /// - /// Type - /// Entity - /// true - authorized; otherwise, false - public static bool Authorize(this IAclService aclService, T entity) where T : BaseEntity, IAclSupported - { - if (entity == null) - return false; - - if (!entity.SubjectToAcl) - return true; - - return aclService.Authorize(entity.GetEntityName(), entity.Id); - } - - /// - /// Authorize ACL permission - /// - /// Type - /// Entity - /// Customer - /// true - authorized; otherwise, false - public static bool Authorize(this IAclService aclService, T entity, Customer customer) where T : BaseEntity, IAclSupported - { - if (entity == null) - return false; - - if (!entity.SubjectToAcl) - return true; - - return aclService.Authorize(entity.GetEntityName(), entity.Id, customer?.CustomerRoles); - } - - /// - /// Authorize ACL permission - /// - /// Type - /// Entity - /// Roles to check access permission for. Inactive roles will be skipped. - /// true - authorized; otherwise, false - public static bool Authorize(this IAclService aclService, T entity, IEnumerable roles) where T : BaseEntity, IAclSupported - { - if (entity == null) - return false; - - if (!entity.SubjectToAcl) - return true; - - return aclService.Authorize(entity.GetEntityName(), entity.Id, roles); - } - } + /// + /// Find customer role identifiers with granted access + /// + /// Entity name to check permission for + /// Entity id to check permission for + /// Customer role identifiers + int[] GetCustomerRoleIdsWithAccessTo(string entityName, int entityId); + + /// + /// Authorize ACL permission + /// + /// Type + /// Entity name to check permission for + /// Entity id to check permission for + /// true - authorized; otherwise, false + bool Authorize(string entityName, int entityId); + + ///// + ///// Authorize ACL permission + ///// + ///// Type + ///// Entity name to check permission for + ///// Entity id to check permission for + ///// Customer + ///// true - authorized; otherwise, false + //bool Authorize(string entityName, int entityId, Customer customer); + + /// + /// Authorize ACL permission + /// + /// Entity name to check permission for + /// Entity id to check permission for + /// Roles to check access permission for. Inactive roles will be skipped. + /// true - authorized; otherwise, false + bool Authorize(string entityName, int entityId, IEnumerable roles); + } + + public static class IAclServiceExtensions + { + /// + /// Find customer role identifiers with granted access + /// + /// Type + /// Entity + /// Customer role identifiers + public static int[] GetCustomerRoleIdsWithAccessTo(this IAclService aclService, T entity) where T : BaseEntity, IAclSupported + { + if (entity == null) + return new int[0]; + + return aclService.GetCustomerRoleIdsWithAccessTo(entity.GetEntityName(), entity.Id); + } + + public static bool Authorize(this IAclService aclService, string entityName, int entityId, Customer customer) + { + return aclService.Authorize(entityName, entityId, customer?.CustomerRoleMappings?.Select(x => x.CustomerRole)); + } + + /// + /// Authorize ACL permission + /// + /// Type + /// Entity + /// true - authorized; otherwise, false + public static bool Authorize(this IAclService aclService, T entity) where T : BaseEntity, IAclSupported + { + if (entity == null) + return false; + + if (!entity.SubjectToAcl) + return true; + + return aclService.Authorize(entity.GetEntityName(), entity.Id); + } + + /// + /// Authorize ACL permission + /// + /// Type + /// Entity + /// Customer + /// true - authorized; otherwise, false + public static bool Authorize(this IAclService aclService, T entity, Customer customer) where T : BaseEntity, IAclSupported + { + if (entity == null) + return false; + + if (!entity.SubjectToAcl) + return true; + + return aclService.Authorize(entity.GetEntityName(), entity.Id, customer?.CustomerRoleMappings?.Select(x => x.CustomerRole)); + } + + /// + /// Authorize ACL permission + /// + /// Type + /// Entity + /// Roles to check access permission for. Inactive roles will be skipped. + /// true - authorized; otherwise, false + public static bool Authorize(this IAclService aclService, T entity, IEnumerable roles) where T : BaseEntity, IAclSupported + { + if (entity == null) + return false; + + if (!entity.SubjectToAcl) + return true; + + return aclService.Authorize(entity.GetEntityName(), entity.Id, roles); + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Security/IPermissionService.cs b/src/Libraries/SmartStore.Services/Security/IPermissionService.cs deleted file mode 100644 index 4a81decea2..0000000000 --- a/src/Libraries/SmartStore.Services/Security/IPermissionService.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.Collections.Generic; -using SmartStore.Core.Domain.Customers; -using SmartStore.Core.Domain.Security; - -namespace SmartStore.Services.Security -{ - /// - /// Permission service interface - /// - public partial interface IPermissionService - { - /// - /// Delete a permission - /// - /// Permission - void DeletePermissionRecord(PermissionRecord permission); - - /// - /// Gets a permission - /// - /// Permission identifier - /// Permission - PermissionRecord GetPermissionRecordById(int permissionId); - - /// - /// Gets a permission - /// - /// Permission system name - /// Permission - PermissionRecord GetPermissionRecordBySystemName(string systemName); - - /// - /// Gets all permissions - /// - /// Permissions - IList GetAllPermissionRecords(); - - /// - /// Inserts a permission - /// - /// Permission - void InsertPermissionRecord(PermissionRecord permission); - - /// - /// Updates the permission - /// - /// Permission - void UpdatePermissionRecord(PermissionRecord permission); - - /// - /// Install permissions - /// - /// Permission provider - void InstallPermissions(IPermissionProvider permissionProvider); - - /// - /// Uninstall permissions - /// - /// Permission provider - void UninstallPermissions(IPermissionProvider permissionProvider); - - /// - /// Authorize permission - /// - /// Permission record - /// true - authorized; otherwise, false - bool Authorize(PermissionRecord permission); - - /// - /// Authorize permission - /// - /// Permission record - /// Customer - /// true - authorized; otherwise, false - bool Authorize(PermissionRecord permission, Customer customer); - - /// - /// Authorize permission - /// - /// Permission record system name - /// true - authorized; otherwise, false - bool Authorize(string permissionRecordSystemName); - - /// - /// Authorize permission - /// - /// Permission record system name - /// Customer - /// true - authorized; otherwise, false - bool Authorize(string permissionRecordSystemName, Customer customer); - } -} \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Security/InstallPermissionsStarter.cs b/src/Libraries/SmartStore.Services/Security/InstallPermissionsStarter.cs new file mode 100644 index 0000000000..3f627046c2 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Security/InstallPermissionsStarter.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Security; +using SmartStore.Core.Infrastructure; +using SmartStore.Core.Logging; +using SmartStore.Core.Plugins; +using SmartStore.Core.Security; +using SmartStore.Data.Setup; + +namespace SmartStore.Services.Security +{ + /// + /// Checks for new permissions and installs them. + /// + public sealed class InstallPermissionsStarter : IPostApplicationStart + { + private readonly IRepository _permissionRepository; + private readonly IPermissionService _permissionService; + private readonly ITypeFinder _typeFinder; + + public InstallPermissionsStarter( + IRepository permissionRepository, + IPermissionService permissionService, + ITypeFinder typeFinder) + { + _permissionRepository = permissionRepository; + _permissionService = permissionService; + _typeFinder = typeFinder; + + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + public int Order => 0; + public bool ThrowOnError => true; + public int MaxAttempts => 1; + + public void Start(HttpContextBase httpContext) + { + var removeUnusedPermissions = true; + var providers = new List(); + + if (PluginManager.PluginChangeDetected || DbMigrationContext.Current.GetAppliedMigrations().Any() || !_permissionRepository.TableUntracked.Any()) + { + // INFO: even if no plugin has changed: directly after a DB migration this code block MUST run. It seems awkward + // that pending migrations exist when binaries has not changed. But after a manual DB reset for a migration rerun + // nobody touches the binaries usually. + + // Standard permission provider and all plugin providers. + var types = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true).ToList(); + foreach (var type in types) + { + var provider = Activator.CreateInstance(type) as IPermissionProvider; + if (provider != null) + { + providers.Add(provider); + } + else + { + removeUnusedPermissions = false; + Logger.Warn($"Cannot create instance of IPermissionProvider {type.Name.NaIfEmpty()}."); + } + } + } + else + { + // Always check standard permission provider. + providers.Add(new StandardPermissionProvider()); + + // Keep unused permissions in database (has no negative effects) as long as at least one plugin changed. + removeUnusedPermissions = false; + } + + _permissionService.InstallPermissions(providers.ToArray(), removeUnusedPermissions); + } + + public void OnFail(Exception exception, bool willRetry) + { + } + } +} diff --git a/src/Libraries/SmartStore.Services/Security/PermissionService.cs b/src/Libraries/SmartStore.Services/Security/PermissionService.cs index dec98fd0fe..55f515ea91 100644 --- a/src/Libraries/SmartStore.Services/Security/PermissionService.cs +++ b/src/Libraries/SmartStore.Services/Security/PermissionService.cs @@ -1,327 +1,738 @@ -using System; +using System; using System.Collections.Generic; +using System.Data.Entity; using System.Linq; +using System.Runtime.CompilerServices; +using SmartStore.Collections; using SmartStore.Core; using SmartStore.Core.Caching; using SmartStore.Core.Data; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Security; +using SmartStore.Core.Localization; +using SmartStore.Core.Logging; +using SmartStore.Core.Security; +using SmartStore.Data.Utilities; using SmartStore.Services.Customers; +using SmartStore.Services.Localization; namespace SmartStore.Services.Security { - /// - /// Permission service - /// public partial class PermissionService : IPermissionService { - /// - /// Cache key for storing a valie indicating whether a certain customer role has a permission - /// - /// - /// {0} : customer role id - /// {1} : permission system name - /// - private const string PERMISSIONS_ALLOWED_KEY = "permission:allowed-{0}-{1}"; - private const string PERMISSIONS_PATTERN_KEY = "permission:*"; - - private readonly IRepository _permissionRecordRepository; - private readonly ICustomerService _customerService; + // {0} = roleId + internal const string PERMISSION_TREE_KEY = "permission:tree-{0}"; + private const string PERMISSION_TREE_PATTERN_KEY = "permission:tree-*"; + + private static readonly Dictionary _permissionAliases = new Dictionary + { + { Permissions.System.AccessShop, "PublicStoreAllowNavigation" }, + { Permissions.System.AccessBackend, "AccessAdminPanel" } + }; + + private static readonly Dictionary _displayNameResourceKeys = new Dictionary + { + { "read", "Common.Read" }, + { "update", "Common.Edit" }, + { "create", "Common.Create" }, + { "delete", "Common.Delete" }, + { "catalog", "Admin.Catalog" }, + { "product", "Admin.Catalog.Products" }, + { "category", "Admin.Catalog.Categories" }, + { "manufacturer", "Admin.Catalog.Manufacturers" }, + { "variant", "Admin.Catalog.Attributes.ProductAttributes" }, + { "attribute", "Admin.Catalog.Attributes.SpecificationAttributes" }, + { "customer", "Admin.Customers" }, + { "impersonate", "Admin.Customers.Customers.Impersonate" }, + { "role", "Admin.Customers.CustomerRoles" }, + { "order", "Admin.Orders" }, + { "giftcard", "Admin.GiftCards" }, + { "notify", "Common.Notify" }, + { "returnrequest", "Admin.ReturnRequests" }, + { "accept", "Admin.ReturnRequests.Accept" }, + { "promotion", "Admin.Catalog.Products.Promotion" }, + { "affiliate", "Admin.Affiliates" }, + { "campaign", "Admin.Promotions.Campaigns" }, + { "discount", "Admin.Promotions.Discounts" }, + { "newsletter", "Admin.Promotions.NewsLetterSubscriptions" }, + { "cms", "Admin.ContentManagement" }, + { "poll", "Admin.ContentManagement.Polls" }, + { "news", "Admin.ContentManagement.News" }, + { "blog", "Admin.ContentManagement.Blog" }, + { "widget", "Admin.ContentManagement.Widgets" }, + { "topic", "Admin.ContentManagement.Topics" }, + { "menu", "Admin.ContentManagement.Menus" }, + { "forum", "Admin.ContentManagement.Forums" }, + { "messagetemplate", "Admin.ContentManagement.MessageTemplates" }, + { "configuration", "Admin.Configuration" }, + { "country", "Admin.Configuration.Countries" }, + { "language", "Admin.Configuration.Languages" }, + { "setting", "Admin.Configuration.Settings" }, + { "paymentmethod", "Admin.Configuration.Payment.Methods" }, + { "activate", "Admin.Common.Activate" }, + { "authentication", "Admin.Configuration.ExternalAuthenticationMethods" }, + { "currency", "Admin.Configuration.Currencies" }, + { "deliverytime", "Admin.Configuration.DeliveryTimes" }, + { "theme", "Admin.Configuration.Themes" }, + { "measure", "Admin.Configuration.Measures.Dimensions" }, + { "activitylog", "Admin.Configuration.ActivityLog.ActivityLogType" }, + { "acl", "Admin.Configuration.ACL" }, + { "emailaccount", "Admin.Configuration.EmailAccounts" }, + { "store", "Admin.Common.Stores" }, + { "shipping", "Admin.Configuration.Shipping.Methods" }, + { "tax", "Admin.Configuration.Tax.Providers" }, + { "plugin", "Admin.Configuration.Plugins" }, + { "upload", "Common.Upload" }, + { "install", "Admin.Configuration.Plugins.Fields.Install" }, + { "license", "Admin.Common.License" }, + { "export", "Common.Export" }, + { "execute", "Admin.Common.Go" }, + { "import", "Common.Import" }, + { "system", "Admin.System" }, + { "log", "Admin.System.Log" }, + { "message", "Admin.System.QueuedEmails" }, + { "send", "Common.Send" }, + { "maintenance", "Admin.System.Maintenance" }, + { "scheduletask", "Admin.System.ScheduleTasks" }, + { "urlrecord", "Admin.System.SeNames" }, + { "cart", "ShoppingCart" }, + { "checkoutattribute", "Admin.Catalog.Attributes.CheckoutAttributes" }, + { "media", "Admin.Plugins.KnownGroup.Media" }, + { "download", "Common.Downloads" }, + { "productreview", "Admin.Catalog.ProductReviews" }, + { "approve", "Common.Approve" }, + { "rule", "Common.Rules" }, + }; + + private readonly IRepository _permissionRepository; + private readonly IRepository _permissionMappingRepository; + private readonly Lazy _customerService; + private readonly ILocalizationService _localizationService; private readonly IWorkContext _workContext; private readonly ICacheManager _cacheManager; + private readonly IRequestCache _requestCache; - /// - /// Ctor - /// - /// Permission repository - /// Customer service - /// Work context - /// Cache manager public PermissionService( - IRepository permissionRecordRepository, - ICustomerService customerService, + IRepository permissionRepository, + IRepository permissionMappingRepository, + Lazy customerService, + ILocalizationService localizationService, IWorkContext workContext, - ICacheManager cacheManager) + ICacheManager cacheManager, + IRequestCache requestCache) { - _permissionRecordRepository = permissionRecordRepository; + _permissionRepository = permissionRepository; + _permissionMappingRepository = permissionMappingRepository; _customerService = customerService; + _localizationService = localizationService; _workContext = workContext; _cacheManager = cacheManager; + _requestCache = requestCache; + + T = NullLocalizer.Instance; + Logger = NullLogger.Instance; } - #region Utilities + public Localizer T { get; set; } + public ILogger Logger { get; set; } - /// - /// Authorize permission - /// - /// Permission record system name - /// Customer role - /// true - authorized; otherwise, false - protected virtual bool Authorize(string permissionRecordSystemName, CustomerRole customerRole) + public virtual PermissionRecord GetPermissionById(int permissionId) { - if (string.IsNullOrEmpty(permissionRecordSystemName)) + if (permissionId == 0) { - return false; + return null; } - - var key = string.Format(PERMISSIONS_ALLOWED_KEY, customerRole.Id, permissionRecordSystemName); - return _cacheManager.Get(key, () => - { - foreach (var permission1 in customerRole.PermissionRecords) - { - if (permission1.SystemName.Equals(permissionRecordSystemName, StringComparison.InvariantCultureIgnoreCase)) - { - return true; - } - } - return false; - }); + return _permissionRepository.GetById(permissionId); } - #endregion + public virtual PermissionRecord GetPermissionBySystemName(string systemName) + { + if (systemName.IsEmpty()) + { + return null; + } + + var permission = _permissionRepository.Table + .Where(x => x.SystemName == systemName) + .OrderBy(x => x.Id) + .FirstOrDefault(); - #region Methods + return permission; + } - /// - /// Delete a permission - /// - /// Permission - public virtual void DeletePermissionRecord(PermissionRecord permission) + public virtual IList GetAllPermissions() { - if (permission == null) - throw new ArgumentNullException("permission"); + var permissions = _permissionRepository.Table.ToList(); + return permissions; + } + + public virtual IDictionary GetAllSystemNames() + { + var language = _workContext.WorkingLanguage; + var resourcesLookup = GetDisplayNameLookup(language.Id); + + Func nameSelector = x => + { + var tokens = x.EmptyNull().ToLower().Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); - _permissionRecordRepository.Delete(permission); + return GetDisplayName(tokens, language.Id, resourcesLookup); + }; - _cacheManager.RemoveByPattern(PERMISSIONS_PATTERN_KEY); + var systemNames = _permissionRepository.TableUntracked + .Select(x => x.SystemName) + .ToList() + .ToDictionarySafe(x => x, nameSelector); + + return systemNames; } - /// - /// Gets a permission - /// - /// Permission identifier - /// Permission - public virtual PermissionRecord GetPermissionRecordById(int permissionId) + public virtual void InsertPermission(PermissionRecord permission) { - if (permissionId == 0) - return null; + Guard.NotNull(permission, nameof(permission)); + + _permissionRepository.Insert(permission); - return _permissionRecordRepository.GetById(permissionId); + _cacheManager.RemoveByPattern(PERMISSION_TREE_PATTERN_KEY); } - /// - /// Gets a permission - /// - /// Permission system name - /// Permission - public virtual PermissionRecord GetPermissionRecordBySystemName(string systemName) + public virtual void UpdatePermission(PermissionRecord permission) { - if (String.IsNullOrWhiteSpace(systemName)) - return null; + Guard.NotNull(permission, nameof(permission)); - var query = from pr in _permissionRecordRepository.Table - where pr.SystemName == systemName - orderby pr.Id - select pr; + _permissionRepository.Update(permission); - var permissionRecord = query.FirstOrDefault(); - return permissionRecord; + _cacheManager.RemoveByPattern(PERMISSION_TREE_PATTERN_KEY); } - /// - /// Gets all permissions - /// - /// Permissions - public virtual IList GetAllPermissionRecords() + public virtual void DeletePermission(PermissionRecord permission) { - var query = - from pr in _permissionRecordRepository.Table - orderby pr.Category, pr.Name - select pr; + if (permission != null) + { + _permissionRepository.Delete(permission); - var permissions = query.ToList(); - return permissions; + _cacheManager.RemoveByPattern(PERMISSION_TREE_PATTERN_KEY); + } + } + + + public virtual PermissionRoleMapping GetPermissionRoleMappingById(int mappingId) + { + if (mappingId == 0) + { + return null; + } + + return _permissionMappingRepository.GetById(mappingId); } - /// - /// Inserts a permission - /// - /// Permission - public virtual void InsertPermissionRecord(PermissionRecord permission) + public virtual void InsertPermissionRoleMapping(PermissionRoleMapping mapping) { - if (permission == null) - throw new ArgumentNullException("permission"); + Guard.NotNull(mapping, nameof(mapping)); - _permissionRecordRepository.Insert(permission); + _permissionMappingRepository.Insert(mapping); - _cacheManager.RemoveByPattern(PERMISSIONS_PATTERN_KEY); + _cacheManager.RemoveByPattern(PERMISSION_TREE_PATTERN_KEY); } - /// - /// Updates the permission - /// - /// Permission - public virtual void UpdatePermissionRecord(PermissionRecord permission) + public virtual void UpdatePermissionRoleMapping(PermissionRoleMapping mapping) { - if (permission == null) - throw new ArgumentNullException("permission"); + Guard.NotNull(mapping, nameof(mapping)); - _permissionRecordRepository.Update(permission); + _permissionMappingRepository.Update(mapping); - _cacheManager.RemoveByPattern(PERMISSIONS_PATTERN_KEY); + _cacheManager.RemoveByPattern(PERMISSION_TREE_PATTERN_KEY); } - /// - /// Install permissions - /// - /// Permission provider - public virtual void InstallPermissions(IPermissionProvider permissionProvider) + public virtual void DeletePermissionRoleMapping(PermissionRoleMapping mapping) { - using (var scope = new DbContextScope(_permissionRecordRepository.Context, autoDetectChanges: false, autoCommit: false)) - { - //install new permissions - var permissions = permissionProvider.GetPermissions(); - foreach (var permission in permissions) - { - var permission1 = GetPermissionRecordBySystemName(permission.SystemName); - if (permission1 == null) - { - //new permission (install it) - permission1 = new PermissionRecord() - { - Name = permission.Name, - SystemName = permission.SystemName, - Category = permission.Category, - }; - - // default customer role mappings - var defaultPermissions = permissionProvider.GetDefaultPermissions(); - foreach (var defaultPermission in defaultPermissions) - { - var customerRole = _customerService.GetCustomerRoleBySystemName(defaultPermission.CustomerRoleSystemName); - if (customerRole == null) - { - //new role (save it) - customerRole = new CustomerRole - { - Name = defaultPermission.CustomerRoleSystemName, - Active = true, - SystemName = defaultPermission.CustomerRoleSystemName - }; - _customerService.InsertCustomerRole(customerRole); - } - - - var defaultMappingProvided = (from p in defaultPermission.PermissionRecords - where p.SystemName == permission1.SystemName - select p).Any(); - var mappingExists = (from p in customerRole.PermissionRecords - where p.SystemName == permission1.SystemName - select p).Any(); - if (defaultMappingProvided && !mappingExists) - { - permission1.CustomerRoles.Add(customerRole); - } - } - - //save new permission - InsertPermissionRecord(permission1); - } - } - - scope.Commit(); - } + if (mapping != null) + { + _permissionMappingRepository.Delete(mapping); + + _cacheManager.RemoveByPattern(PERMISSION_TREE_PATTERN_KEY); + } } - /// - /// Uninstall permissions - /// - /// Permission provider - public virtual void UninstallPermissions(IPermissionProvider permissionProvider) + + public virtual void InstallPermissions(IPermissionProvider[] permissionProviders, bool removeUnusedPermissions = false) { - var permissions = permissionProvider.GetPermissions(); - foreach (var permission in permissions) + if (!(permissionProviders?.Any() ?? false)) + { + return; + } + + Dictionary existingRoles = null; + var allPermissionNames = _permissionRepository.TableUntracked.Select(x => x.SystemName).ToList(); + var existing = new HashSet(allPermissionNames, StringComparer.InvariantCultureIgnoreCase); + var added = new HashSet(StringComparer.InvariantCultureIgnoreCase); + var providerPermissions = new HashSet(StringComparer.InvariantCultureIgnoreCase); + var log = existing.Any(); + var clearCache = false; + + if (existing.Any()) + { + var permissionsMigrated = existing.Contains(Permissions.System.AccessShop) && !existing.Contains("PublicStoreAllowNavigation"); + if (!permissionsMigrated) + { + // Migrations must have been completed before permissions can be added or deleted. + return; + } + } + + try + { + using (var scope = new DbContextScope(_permissionRepository.Context, validateOnSave: false, hooksEnabled: false, autoCommit: false)) + { + // Add new permissions. + foreach (var provider in permissionProviders) + { + try + { + var systemNames = provider.GetPermissions().Select(x => x.SystemName); + var missingSystemNames = systemNames.Except(existing); + + if (removeUnusedPermissions) + { + providerPermissions.AddRange(systemNames); + } + + if (missingSystemNames.Any()) + { + var defaultPermissions = provider.GetDefaultPermissions(); + foreach (var systemName in missingSystemNames) + { + var roleNames = defaultPermissions + .Where(x => x.PermissionRecords.Any(y => y.SystemName == systemName)) + .Select(x => x.CustomerRoleSystemName); + + var newPermission = new PermissionRecord { SystemName = systemName }; + + foreach (var roleName in new HashSet(roleNames, StringComparer.InvariantCultureIgnoreCase)) + { + if (existingRoles == null) + { + existingRoles = new Dictionary(); + + var rolesQuery = _customerService.Value.GetAllCustomerRoles(true).SourceQuery; + rolesQuery = rolesQuery.Where(x => !string.IsNullOrEmpty(x.SystemName)); + + var rolesPager = new FastPager(rolesQuery, 500); + + while (rolesPager.ReadNextPage(out var roles)) + { + roles.Each(x => existingRoles[x.SystemName] = x); + } + } + + if (!existingRoles.TryGetValue(roleName, out var role)) + { + role = new CustomerRole + { + Active = true, + Name = roleName, + SystemName = roleName + }; + + _customerService.Value.InsertCustomerRole(role); + scope.Commit(); + existingRoles[roleName] = role; + } + + newPermission.PermissionRoleMappings.Add(new PermissionRoleMapping + { + Allow = true, + CustomerRoleId = role.Id + }); + } + + _permissionRepository.Insert(newPermission); + + clearCache = true; + added.Add(newPermission.SystemName); + existing.Add(newPermission.SystemName); + } + } + } + catch (Exception ex) + { + Logger.Error(ex); + } + } + + scope.Commit(); + + if (log && added.Any()) + { + Logger.Info(T("Admin.Permissions.AddedPermissions", string.Join(", ", added))); + } + + // Remove permissions no longer supported by providers. + if (removeUnusedPermissions) + { + var toDelete = existing.Except(providerPermissions).ToList(); + if (toDelete.Any()) + { + clearCache = true; + + foreach (var chunk in toDelete.Slice(500)) + { + var entities = _permissionRepository.Table.Where(x => chunk.Contains(x.SystemName)).ToList(); + entities.Each(x => _permissionRepository.Delete(x)); + scope.Commit(); + } + + if (log) + { + Logger.Info(T("Admin.Permissions.RemovedPermissions", string.Join(", ", toDelete))); + } + } + } + } + } + finally { - var permission1 = GetPermissionRecordBySystemName(permission.SystemName); - if (permission1 != null) + if (clearCache) { - DeletePermissionRecord(permission1); + _cacheManager.RemoveByPattern(PERMISSION_TREE_PATTERN_KEY); } } } - - /// - /// Authorize permission - /// - /// Permission record - /// true - authorized; otherwise, false + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual bool Authorize(PermissionRecord permission) { - return Authorize(permission, _workContext.CurrentCustomer); + return Authorize(permission?.SystemName, _workContext.CurrentCustomer); } - /// - /// Authorize permission - /// - /// Permission record - /// Customer - /// true - authorized; otherwise, false + [MethodImpl(MethodImplOptions.AggressiveInlining)] public virtual bool Authorize(PermissionRecord permission, Customer customer) { - if (permission == null) + return Authorize(permission?.SystemName, customer); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual bool Authorize(string permissionSystemName) + { + return Authorize(permissionSystemName, _workContext.CurrentCustomer); + } + + public virtual bool Authorize(string permissionSystemName, Customer customer) + { + if (customer == null || string.IsNullOrEmpty(permissionSystemName)) + { + return false; + } + + var cacheKey = "Permission." + customer.Id.ToString() + "." + permissionSystemName; + + var authorized = _requestCache.Get(cacheKey, () => + { + var roles = customer.CustomerRoleMappings + .Where(x => x.CustomerRole?.Active ?? false) + .Select(x => x.CustomerRole) + .ToArray(); + + foreach (var role in roles) + { + var tree = GetPermissionTree(role); + var node = tree.SelectNodeById(permissionSystemName); + if (node == null) + { + continue; + } + + while (node != null && !node.Value.Allow.HasValue) + { + node = node.Parent; + } + if (node?.Value?.Allow ?? false) + { + return true; + } + } + + return false; + }); + + return authorized; + } + + public virtual bool AuthorizeByAlias(string permissionSystemName) + { + if (string.IsNullOrEmpty(permissionSystemName) || !_permissionAliases.TryGetValue(permissionSystemName, out var alias)) + { return false; + } - if (customer == null) + var aliasPermission = GetPermissionBySystemName(alias); + if (aliasPermission == null) + { return false; + } - //old implementation of Authorize method - //var customerRoles = customer.CustomerRoles.Where(cr => cr.Active); - //foreach (var role in customerRoles) - // foreach (var permission1 in role.PermissionRecords) - // if (permission1.SystemName.Equals(permission.SystemName, StringComparison.InvariantCultureIgnoreCase)) - // return true; + // SQL required because the old mapping was only accessible via navigation property but it no longer exists. + var context = (DbContext)_permissionRepository.Context; + if (context.TableExists("PermissionRecord_Role_Mapping")) + { + var aliasCutomerRoleIds = _permissionRepository.Context + .SqlQuery("select [CustomerRole_Id] from [dbo].[PermissionRecord_Role_Mapping] where [PermissionRecord_Id] = {0}", aliasPermission.Id) + .ToList(); - //return false; + if (aliasCutomerRoleIds.Any()) + { + var roles = _workContext.CurrentCustomer.CustomerRoleMappings.Select(x => x.CustomerRole).Where(x => x.Active); + foreach (var role in roles) + { + if (aliasCutomerRoleIds.Contains(role.Id)) + { + return true; + } + } + } + } - return Authorize(permission.SystemName, customer); + return false; } - /// - /// Authorize permission - /// - /// Permission record system name - /// true - authorized; otherwise, false - public virtual bool Authorize(string permissionRecordSystemName) + + public virtual bool FindAuthorization(string permissionSystemName) { - return Authorize(permissionRecordSystemName, _workContext.CurrentCustomer); + return FindAuthorization(permissionSystemName, _workContext.CurrentCustomer); } - /// - /// Authorize permission - /// - /// Permission record system name - /// Customer - /// true - authorized; otherwise, false - public virtual bool Authorize(string permissionRecordSystemName, Customer customer) + public virtual bool FindAuthorization(string permissionSystemName, Customer customer) { - if (string.IsNullOrEmpty(permissionRecordSystemName)) + if (string.IsNullOrEmpty(permissionSystemName)) { return false; } - var customerRoles = customer.CustomerRoles.Where(cr => cr.Active); - foreach (var role in customerRoles) + foreach (var role in customer.CustomerRoleMappings.Select(x => x.CustomerRole).Where(x => x.Active)) { - if (Authorize(permissionRecordSystemName, role)) + var tree = GetPermissionTree(role); + var node = tree.SelectNodeById(permissionSystemName); + if (node == null) + { + continue; + } + + if (FindAllowByChild(node)) + { + return true; + } + + while (node != null && !node.Value.Allow.HasValue) + { + node = node.Parent; + } + if (node?.Value?.Allow ?? false) { return true; } } - + return false; + + bool FindAllowByChild(TreeNode n) + { + if (n?.Value?.Allow ?? false) + { + return true; + } + + if (n.HasChildren) + { + foreach (var child in n.Children) + { + if (FindAllowByChild(child)) + { + return true; + } + } + } + + return false; + } + } + + + public virtual TreeNode GetPermissionTree(CustomerRole role, bool addDisplayNames = false) + { + Guard.NotNull(role, nameof(role)); + + var result = _cacheManager.Get(PERMISSION_TREE_KEY.FormatInvariant(role.Id), () => + { + var root = new TreeNode(new PermissionNode()); + + var permissions = _permissionRepository.TableUntracked + .Expand(x => x.PermissionRoleMappings) + .ToList(); + + AddChildItems(root, permissions, null, permission => + { + var mapping = permission.PermissionRoleMappings.FirstOrDefault(x => x.CustomerRoleId == role.Id); + return mapping?.Allow ?? null; + }); + + return root; + }); + + if (addDisplayNames) + { + var language = _workContext.WorkingLanguage; + var resourcesLookup = GetDisplayNameLookup(language.Id); + AddDisplayName(result, language.Id, resourcesLookup); + } + + return result; + } + + public virtual TreeNode GetPermissionTree(Customer customer, bool addDisplayNames = false) + { + Guard.NotNull(customer, nameof(customer)); + + var root = new TreeNode(new PermissionNode()); + var permissions = _permissionRepository.TableUntracked.ToList(); + + AddChildItems(root, permissions, null, permission => + { + return Authorize(permission.SystemName, customer); + }); + + if (addDisplayNames) + { + var language = _workContext.WorkingLanguage; + var resourcesLookup = GetDisplayNameLookup(language.Id); + AddDisplayName(root, language.Id, resourcesLookup); + } + + return root; } - #endregion + public virtual string GetDiplayName(string permissionSystemName) + { + var tokens = permissionSystemName.EmptyNull().ToLower().Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); + if (tokens.Any()) + { + var language = _workContext.WorkingLanguage; + var resourcesLookup = GetDisplayNameLookup(language.Id); + + return GetDisplayName(tokens, language.Id, resourcesLookup); + } + + return string.Empty; + } + + public virtual string GetUnauthorizedMessage(string permissionSystemName) + { + var displayName = GetDiplayName(permissionSystemName); + var message = T("Admin.AccessDenied.DetailedDescription", displayName.NaIfEmpty(), permissionSystemName.NaIfEmpty()); + return message; + } + + private void AddChildItems(TreeNode parentNode, List permissions, string path, Func allow) + { + if (parentNode == null) + { + return; + } + + IEnumerable entities = null; + + if (path == null) + { + entities = permissions.Where(x => x.SystemName.IndexOf('.') == -1); + } + else + { + var tmpPath = path.EnsureEndsWith("."); + entities = permissions.Where(x => x.SystemName.StartsWith(tmpPath) && x.SystemName.IndexOf('.', tmpPath.Length) == -1); + } + + foreach (var entity in entities) + { + var newNode = parentNode.Append(new PermissionNode + { + PermissionRecordId = entity.Id, + Allow = allow(entity), // null = inherit + SystemName = entity.SystemName + }, entity.SystemName); + + AddChildItems(newNode, permissions, entity.SystemName, allow); + } + } + + private void AddDisplayName(TreeNode node, int languageId, Dictionary resourcesLookup) + { + var tokens = node.Value.SystemName.EmptyNull().ToLower().Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); + var token = tokens.LastOrDefault(); + var displayName = GetDisplayName(token, languageId, resourcesLookup) ?? token ?? node.Value.SystemName; + + node.SetThreadMetadata("DisplayName", displayName); + + if (node.HasChildren) + { + node.Children.Each(x => AddDisplayName(x, languageId, resourcesLookup)); + } + } + + private string GetDisplayName(string token, int languageId, Dictionary resourcesLookup) + { + if (!string.IsNullOrWhiteSpace(token)) + { + // Try known token of default permissions. + if (!_displayNameResourceKeys.TryGetValue(token, out var key) || !resourcesLookup.TryGetValue(key, out var name)) + { + // Unknown token. Try to find resource by name convention. + key = "Permissions.DisplayName." + token.Replace("-", ""); + + // Try resource provided by core. + name = _localizationService.GetResource(key, languageId, false, string.Empty, true); + if (name.IsEmpty()) + { + // Try resource provided by plugin. + name = _localizationService.GetResource("Plugins." + key, languageId, false, string.Empty, true); + } + } + + return name; + } + + return null; + } + + private string GetDisplayName(string[] tokens, int languageId, Dictionary resourcesLookup) + { + var displayName = string.Empty; + + if (tokens?.Any() ?? false) + { + foreach (var token in tokens) + { + if (displayName.Length > 0) + { + displayName += " » "; + } + + displayName += GetDisplayName(token, languageId, resourcesLookup) ?? token ?? string.Empty; + } + } + + return displayName; + } + + private Dictionary GetDisplayNameLookup(int languageId) + { + var allKeys = _displayNameResourceKeys.Select(x => x.Value); + + // Load all known string resources in one go. + var resourceQuery = _localizationService.All(languageId); + var resourcesLookup = resourceQuery.Where(x => allKeys.Contains(x.ResourceName)) + .ToList() + .ToDictionarySafe(x => x.ResourceName, x => x.ResourceValue); + + return resourcesLookup; + } } -} \ No newline at end of file +} diff --git a/src/Libraries/SmartStore.Services/Security/StandardPermissionProvider.cs b/src/Libraries/SmartStore.Services/Security/StandardPermissionProvider.cs index 32ba800597..d356ce3094 100644 --- a/src/Libraries/SmartStore.Services/Security/StandardPermissionProvider.cs +++ b/src/Libraries/SmartStore.Services/Security/StandardPermissionProvider.cs @@ -1,212 +1,75 @@ -using System.Collections.Generic; +using System.Collections.Generic; +using System.Linq; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Domain.Security; +using SmartStore.Core.Security; namespace SmartStore.Services.Security { public partial class StandardPermissionProvider : IPermissionProvider { - //admin area permissions - public static readonly PermissionRecord AccessAdminPanel = new PermissionRecord { Name = "Access admin area", SystemName = "AccessAdminPanel", Category = "Standard" }; - public static readonly PermissionRecord AllowCustomerImpersonation = new PermissionRecord { Name = "Admin area. Allow Customer Impersonation", SystemName = "AllowCustomerImpersonation", Category = "Customers" }; - public static readonly PermissionRecord ManageCatalog = new PermissionRecord { Name = "Admin area. Manage Catalog", SystemName = "ManageCatalog", Category = "Catalog" }; - public static readonly PermissionRecord ManageCustomers = new PermissionRecord { Name = "Admin area. Manage Customers", SystemName = "ManageCustomers", Category = "Customers" }; - public static readonly PermissionRecord ManageCustomerRoles = new PermissionRecord { Name = "Admin area. Manage Customer Roles", SystemName = "ManageCustomerRoles", Category = "Customers" }; - public static readonly PermissionRecord ManageOrders = new PermissionRecord { Name = "Admin area. Manage Orders", SystemName = "ManageOrders", Category = "Orders" }; - public static readonly PermissionRecord ManageGiftCards = new PermissionRecord { Name = "Admin area. Manage Gift Cards", SystemName = "ManageGiftCards", Category = "Orders" }; - public static readonly PermissionRecord ManageReturnRequests = new PermissionRecord { Name = "Admin area. Manage Return Requests", SystemName = "ManageReturnRequests", Category = "Orders" }; - public static readonly PermissionRecord ManageAffiliates = new PermissionRecord { Name = "Admin area. Manage Affiliates", SystemName = "ManageAffiliates", Category = "Promo" }; - public static readonly PermissionRecord ManageCampaigns = new PermissionRecord { Name = "Admin area. Manage Campaigns", SystemName = "ManageCampaigns", Category = "Promo" }; - public static readonly PermissionRecord ManageDiscounts = new PermissionRecord { Name = "Admin area. Manage Discounts", SystemName = "ManageDiscounts", Category = "Promo" }; - public static readonly PermissionRecord ManageNewsletterSubscribers = new PermissionRecord { Name = "Admin area. Manage Newsletter Subscribers", SystemName = "ManageNewsletterSubscribers", Category = "Promo" }; - public static readonly PermissionRecord ManagePolls = new PermissionRecord { Name = "Admin area. Manage Polls", SystemName = "ManagePolls", Category = "Content Management" }; - public static readonly PermissionRecord ManageNews = new PermissionRecord { Name = "Admin area. Manage News", SystemName = "ManageNews", Category = "Content Management" }; - public static readonly PermissionRecord ManageBlog = new PermissionRecord { Name = "Admin area. Manage Blog", SystemName = "ManageBlog", Category = "Content Management" }; - public static readonly PermissionRecord ManageWidgets = new PermissionRecord { Name = "Admin area. Manage Widgets", SystemName = "ManageWidgets", Category = "Content Management" }; - public static readonly PermissionRecord ManageTopics = new PermissionRecord { Name = "Admin area. Manage Topics", SystemName = "ManageTopics", Category = "Content Management" }; - public static readonly PermissionRecord ManageMenus = new PermissionRecord { Name = "Admin area. Manage Menus", SystemName = "ManageMenus", Category = "Content Management" }; - public static readonly PermissionRecord ManageForums = new PermissionRecord { Name = "Admin area. Manage Forums", SystemName = "ManageForums", Category = "Content Management" }; - public static readonly PermissionRecord ManageMessageTemplates = new PermissionRecord { Name = "Admin area. Manage Message Templates", SystemName = "ManageMessageTemplates", Category = "Content Management" }; - public static readonly PermissionRecord ManageCountries = new PermissionRecord { Name = "Admin area. Manage Countries", SystemName = "ManageCountries", Category = "Configuration" }; - public static readonly PermissionRecord ManageLanguages = new PermissionRecord { Name = "Admin area. Manage Languages", SystemName = "ManageLanguages", Category = "Configuration" }; - public static readonly PermissionRecord ManageSettings = new PermissionRecord { Name = "Admin area. Manage Settings", SystemName = "ManageSettings", Category = "Configuration" }; - public static readonly PermissionRecord ManagePaymentMethods = new PermissionRecord { Name = "Admin area. Manage Payment Methods", SystemName = "ManagePaymentMethods", Category = "Configuration" }; - public static readonly PermissionRecord ManageExternalAuthenticationMethods = new PermissionRecord { Name = "Admin area. Manage External Authentication Methods", SystemName = "ManageExternalAuthenticationMethods", Category = "Configuration" }; - public static readonly PermissionRecord ManageTaxSettings = new PermissionRecord { Name = "Admin area. Manage Tax Settings", SystemName = "ManageTaxSettings", Category = "Configuration" }; - public static readonly PermissionRecord ManageShippingSettings = new PermissionRecord { Name = "Admin area. Manage Shipping Settings", SystemName = "ManageShippingSettings", Category = "Configuration" }; - public static readonly PermissionRecord ManageCurrencies = new PermissionRecord { Name = "Admin area. Manage Currencies", SystemName = "ManageCurrencies", Category = "Configuration" }; - public static readonly PermissionRecord ManageDeliveryTimes = new PermissionRecord { Name = "Admin area. Manage Delivery Times", SystemName = "ManageDeliveryTimes", Category = "Configuration" }; - public static readonly PermissionRecord ManageThemes = new PermissionRecord { Name = "Admin area. Manage Themes", SystemName = "ManageThemes", Category = "Configuration" }; - public static readonly PermissionRecord ManageMeasures = new PermissionRecord { Name = "Admin area. Manage Measures", SystemName = "ManageMeasures", Category = "Configuration" }; - public static readonly PermissionRecord ManageActivityLog = new PermissionRecord { Name = "Admin area. Manage Activity Log", SystemName = "ManageActivityLog", Category = "Configuration" }; - public static readonly PermissionRecord ManageAcl = new PermissionRecord { Name = "Admin area. Manage ACL", SystemName = "ManageACL", Category = "Configuration" }; - public static readonly PermissionRecord ManageEmailAccounts = new PermissionRecord { Name = "Admin area. Manage Email Accounts", SystemName = "ManageEmailAccounts", Category = "Configuration" }; - public static readonly PermissionRecord ManageStores = new PermissionRecord { Name = "Admin area. Manage Stores", SystemName = "ManageStores", Category = "Configuration" }; - public static readonly PermissionRecord ManagePlugins = new PermissionRecord { Name = "Admin area. Manage Plugins", SystemName = "ManagePlugins", Category = "Configuration" }; - public static readonly PermissionRecord ManageSystemLog = new PermissionRecord { Name = "Admin area. Manage System Log", SystemName = "ManageSystemLog", Category = "Configuration" }; - public static readonly PermissionRecord ManageMessageQueue = new PermissionRecord { Name = "Admin area. Manage Message Queue", SystemName = "ManageMessageQueue", Category = "Configuration" }; - public static readonly PermissionRecord ManageMaintenance = new PermissionRecord { Name = "Admin area. Manage Maintenance", SystemName = "ManageMaintenance", Category = "Configuration" }; - public static readonly PermissionRecord UploadPictures = new PermissionRecord { Name = "Admin area. Upload Pictures", SystemName = "UploadPictures", Category = "Configuration" }; - public static readonly PermissionRecord ManageScheduleTasks = new PermissionRecord { Name = "Admin area. Manage Schedule Tasks", SystemName = "ManageScheduleTasks", Category = "Configuration" }; - public static readonly PermissionRecord ManageExports = new PermissionRecord { Name = "Admin area. Manage Exports", SystemName = "ManageExports", Category = "Configuration" }; - public static readonly PermissionRecord ManageImports = new PermissionRecord { Name = "Admin area. Manage Imports", SystemName = "ManageImports", Category = "Configuration" }; - public static readonly PermissionRecord ManageUrlRecords = new PermissionRecord { Name = "Admin area. Manage Url Records", SystemName = "ManageUrlRecords", Category = "Configuration" }; - - //public store permissions - public static readonly PermissionRecord DisplayPrices = new PermissionRecord { Name = "Public store. Display Prices", SystemName = "DisplayPrices", Category = "PublicStore" }; - public static readonly PermissionRecord EnableShoppingCart = new PermissionRecord { Name = "Public store. Enable shopping cart", SystemName = "EnableShoppingCart", Category = "PublicStore" }; - public static readonly PermissionRecord EnableWishlist = new PermissionRecord { Name = "Public store. Enable wishlist", SystemName = "EnableWishlist", Category = "PublicStore" }; - public static readonly PermissionRecord PublicStoreAllowNavigation = new PermissionRecord { Name = "Public store. Allow navigation", SystemName = "PublicStoreAllowNavigation", Category = "PublicStore" }; - - public virtual IEnumerable GetPermissions() { - return new[] - { - AccessAdminPanel, - AllowCustomerImpersonation, - ManageCatalog, - ManageCustomers, - ManageCustomerRoles, - ManageOrders, - ManageGiftCards, - ManageReturnRequests, - ManageAffiliates, - ManageCampaigns, - ManageDiscounts, - ManageNewsletterSubscribers, - ManagePolls, - ManageNews, - ManageBlog, - ManageWidgets, - ManageTopics, - ManageMenus, - ManageForums, - ManageMessageTemplates, - ManageCountries, - ManageLanguages, - ManageSettings, - ManagePaymentMethods, - ManageExternalAuthenticationMethods, - ManageTaxSettings, - ManageShippingSettings, - ManageCurrencies, - ManageDeliveryTimes, - ManageThemes, - ManageMeasures, - ManageActivityLog, - ManageAcl, - ManageEmailAccounts, - ManageStores, - ManagePlugins, - ManageSystemLog, - ManageMessageQueue, - ManageMaintenance, - UploadPictures, - ManageScheduleTasks, - ManageExports, - ManageImports, - ManageUrlRecords, - DisplayPrices, - EnableShoppingCart, - EnableWishlist, - PublicStoreAllowNavigation, - }; + var permissionSystemNames = PermissionHelper.GetPermissions(typeof(Permissions)); + var permissions = permissionSystemNames.Select(x => new PermissionRecord { SystemName = x }); + + return permissions; } public virtual IEnumerable GetDefaultPermissions() { - return new[] + return new[] { - new DefaultPermissionRecord + new DefaultPermissionRecord { CustomerRoleSystemName = SystemCustomerRoleNames.Administrators, - PermissionRecords = new[] + PermissionRecords = new[] { - AccessAdminPanel, - AllowCustomerImpersonation, - ManageCatalog, - ManageCustomers, - ManageCustomerRoles, - ManageOrders, - ManageGiftCards, - ManageReturnRequests, - ManageAffiliates, - ManageCampaigns, - ManageDiscounts, - ManageNewsletterSubscribers, - ManagePolls, - ManageNews, - ManageBlog, - ManageWidgets, - ManageTopics, - ManageMenus, - ManageForums, - ManageMessageTemplates, - ManageCountries, - ManageLanguages, - ManageSettings, - ManagePaymentMethods, - ManageExternalAuthenticationMethods, - ManageTaxSettings, - ManageShippingSettings, - ManageCurrencies, - ManageDeliveryTimes, - ManageThemes, - ManageMeasures, - ManageActivityLog, - ManageAcl, - ManageEmailAccounts, - ManageStores, - ManagePlugins, - ManageSystemLog, - ManageMessageQueue, - ManageMaintenance, - UploadPictures, - ManageScheduleTasks, - ManageExports, - ManageImports, - ManageUrlRecords, - DisplayPrices, - EnableShoppingCart, - EnableWishlist, - PublicStoreAllowNavigation, + new PermissionRecord { SystemName = Permissions.Catalog.Self }, + new PermissionRecord { SystemName = Permissions.Customer.Self }, + new PermissionRecord { SystemName = Permissions.Order.Self }, + new PermissionRecord { SystemName = Permissions.Promotion.Self }, + new PermissionRecord { SystemName = Permissions.Cms.Self }, + new PermissionRecord { SystemName = Permissions.Configuration.Self }, + new PermissionRecord { SystemName = Permissions.System.Self }, + new PermissionRecord { SystemName = Permissions.Cart.Self }, + new PermissionRecord { SystemName = Permissions.Media.Self } } }, - new DefaultPermissionRecord + new DefaultPermissionRecord { CustomerRoleSystemName = SystemCustomerRoleNames.ForumModerators, - PermissionRecords = new[] + PermissionRecords = new[] { - DisplayPrices, - EnableShoppingCart, - EnableWishlist, - PublicStoreAllowNavigation, + new PermissionRecord { SystemName = Permissions.Catalog.DisplayPrice }, + new PermissionRecord { SystemName = Permissions.Cart.AccessShoppingCart }, + new PermissionRecord { SystemName = Permissions.Cart.AccessWishlist }, + new PermissionRecord { SystemName = Permissions.System.AccessShop } } }, - new DefaultPermissionRecord + new DefaultPermissionRecord { CustomerRoleSystemName = SystemCustomerRoleNames.Guests, - PermissionRecords = new[] + PermissionRecords = new[] { - DisplayPrices, - EnableShoppingCart, - EnableWishlist, - PublicStoreAllowNavigation, + new PermissionRecord { SystemName = Permissions.Catalog.DisplayPrice }, + new PermissionRecord { SystemName = Permissions.Cart.AccessShoppingCart }, + new PermissionRecord { SystemName = Permissions.Cart.AccessWishlist }, + new PermissionRecord { SystemName = Permissions.System.AccessShop } } }, - new DefaultPermissionRecord + new DefaultPermissionRecord { CustomerRoleSystemName = SystemCustomerRoleNames.Registered, - PermissionRecords = new[] + PermissionRecords = new[] { - DisplayPrices, - EnableShoppingCart, - EnableWishlist, - PublicStoreAllowNavigation, + new PermissionRecord { SystemName = Permissions.Catalog.DisplayPrice }, + new PermissionRecord { SystemName = Permissions.Cart.AccessShoppingCart }, + new PermissionRecord { SystemName = Permissions.Cart.AccessWishlist }, + new PermissionRecord { SystemName = Permissions.System.AccessShop } } - }, + } }; } } -} \ 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..10676d15d7 100644 --- a/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs +++ b/src/Libraries/SmartStore.Services/Seo/IUrlRecordService.cs @@ -9,7 +9,7 @@ namespace SmartStore.Services.Seo /// /// Provides information about URL records /// - public partial interface IUrlRecordService : IScopedService + public partial interface IUrlRecordService : IScopedService { /// /// Deletes an URL record @@ -24,12 +24,12 @@ public partial interface IUrlRecordService : IScopedService /// URL record UrlRecord GetUrlRecordById(int urlRecordId); - /// - /// Gets URL records by identifiers - /// - /// - /// List of URL records - IList GetUrlRecordsByIds(int[] urlRecordIds); + /// + /// Gets URL records by identifiers + /// + /// + /// List of URL records + IList GetUrlRecordsByIds(int[] urlRecordIds); /// /// Inserts an URL record @@ -63,47 +63,47 @@ 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 + /// 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); + /// + /// 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 - /// - /// Type - /// Entity - /// Specifies whether only active URL records should be returned - /// List of URL records - IList GetUrlRecordsFor(string entityName, int entityId, bool activeOnly = false); + /// + /// Gets all URL records for the specified entity + /// + /// Type + /// Entity + /// Specifies whether only active URL records should be returned + /// List of URL records + IList GetUrlRecordsFor(string entityName, int entityId, bool activeOnly = false); /// /// Find slug @@ -126,28 +126,28 @@ public partial interface IUrlRecordService : IScopedService /// UrlRecord SaveSlug(T entity, string slug, int languageId) where T : BaseEntity, ISlugSupported; - /// - /// Save slug - /// - /// Type - /// Entity - /// Name of a property - /// Url record - UrlRecord SaveSlug(T entity, Expression> nameProperty) where T : BaseEntity, ISlugSupported; + /// + /// Save slug + /// + /// Type + /// Entity + /// Name of a property + /// Url record + UrlRecord SaveSlug(T entity, Func nameProperty) where T : BaseEntity, ISlugSupported; - /// - /// Get number of slugs per entity - /// - /// URL record identifier - /// Dictionary of slugs per entity count - Dictionary CountSlugsPerEntity(int[] urlRecordIds); + /// + /// Get number of slugs per entity + /// + /// URL record identifier + /// Dictionary of slugs per entity count + Dictionary CountSlugsPerEntity(int[] urlRecordIds); - /// - /// Get number of slugs per entity - /// - /// Entity name - /// Entity identifier - /// Number of slugs per entity - int CountSlugsPerEntity(string entityName, int entityId); + /// + /// Get number of slugs per entity + /// + /// Entity name + /// Entity identifier + /// Number of slugs per entity + int CountSlugsPerEntity(string entityName, int entityId); } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Seo/IXmlSitemapGenerator.cs b/src/Libraries/SmartStore.Services/Seo/IXmlSitemapGenerator.cs index 5e833a086e..0987f2b928 100644 --- a/src/Libraries/SmartStore.Services/Seo/IXmlSitemapGenerator.cs +++ b/src/Libraries/SmartStore.Services/Seo/IXmlSitemapGenerator.cs @@ -10,42 +10,42 @@ namespace SmartStore.Services.Seo /// public partial interface IXmlSitemapGenerator { - /// - /// Gets the sitemap partition - /// - /// - /// The page index. 0 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 - Task GetSitemapPartAsync(int index = 0); + /// + /// Gets the sitemap partition + /// + /// + /// The page index. 0 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 + Task GetSitemapPartAsync(int index = 0); - /// - /// 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. - /// - Task RebuildAsync(XmlSitemapBuildContext ctx); + /// + /// 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. + /// + Task RebuildAsync(XmlSitemapBuildContext ctx); - /// - /// Determines whether a rebuild is already running. - /// - bool IsRebuilding(int storeId, int languageId); + /// + /// Determines whether a rebuild is already running. + /// + bool IsRebuilding(int storeId, int languageId); - /// - /// Indicates whether the sitemap has been generated and cached. - /// - bool IsGenerated(int storeId, int languageId); + /// + /// 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); - } + /// + /// Removes the sitemap from the cache for a rebuild. + /// + void Invalidate(int storeId, int languageId); + } } diff --git a/src/Libraries/SmartStore.Services/Seo/IXmlSitemapPublisher.cs b/src/Libraries/SmartStore.Services/Seo/IXmlSitemapPublisher.cs new file mode 100644 index 0000000000..7e75a9f4fa --- /dev/null +++ b/src/Libraries/SmartStore.Services/Seo/IXmlSitemapPublisher.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; +using System.Web.Mvc; +using SmartStore.Core.Domain.Localization; +using SmartStore.Core.Domain.Seo; + +namespace SmartStore.Services.Seo +{ + public partial interface IXmlSitemapPublisher + { + XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context); + } + + public class XmlSitemapProvider + { + public virtual int GetTotalCount() + { + return 0; + } + + public virtual IEnumerable Enlist() + { + return Enumerable.Empty(); + } + + public virtual IEnumerable EnlistNodes(Language language) + { + return Enumerable.Empty(); + } + + public virtual XmlSitemapNode CreateNode(UrlHelper urlHelper, string baseUrl, NamedEntity entity, UrlRecordCollection slugs, Language language) + { + var slug = slugs.GetSlug(language.Id, entity.Id, true); + var path = urlHelper.RouteUrl(entity.EntityName, new { SeName = slug }).EmptyNull().TrimStart('/'); + var loc = baseUrl + path; + + return new XmlSitemapNode + { + LastMod = entity.LastMod, + Loc = loc + }; + } + + public virtual int Order { get; } + } +} diff --git a/src/Libraries/SmartStore.Services/Seo/NamedEntity.cs b/src/Libraries/SmartStore.Services/Seo/NamedEntity.cs new file mode 100644 index 0000000000..79f1e0d83c --- /dev/null +++ b/src/Libraries/SmartStore.Services/Seo/NamedEntity.cs @@ -0,0 +1,20 @@ +using System; +using SmartStore.Core; +using SmartStore.Core.Domain.Seo; + +namespace SmartStore.Services.Seo +{ + public class NamedEntity : BaseEntity, ISlugSupported + { + public string EntityName { get; set; } + public string DisplayName { get; set; } + public string Slug { get; set; } + public DateTime LastMod { get; set; } + public int? LanguageId { get; set; } + + public override string GetEntityName() + { + return EntityName; + } + } +} diff --git a/src/Libraries/SmartStore.Services/Seo/SeoExtensions.cs b/src/Libraries/SmartStore.Services/Seo/SeoExtensions.cs index 3a2c115b85..e47abff4c5 100644 --- a/src/Libraries/SmartStore.Services/Seo/SeoExtensions.cs +++ b/src/Libraries/SmartStore.Services/Seo/SeoExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Runtime.CompilerServices; using SmartStore.Core; using SmartStore.Core.Domain.Blogs; using SmartStore.Core.Domain.Catalog; @@ -7,9 +8,9 @@ using SmartStore.Core.Domain.News; using SmartStore.Core.Domain.Seo; using SmartStore.Core.Infrastructure; +using SmartStore.Core.Localization; using SmartStore.Services.Localization; using SmartStore.Utilities; -using SmartStore.Core.Localization; namespace SmartStore.Services.Seo { @@ -36,21 +37,22 @@ public static string GetSeName(this ProductTag productTag) /// Product tag SE (search engine) name public static string GetSeName(this ProductTag productTag, int languageId) { - Guard.NotNull(productTag, nameof(productTag)); + Guard.NotNull(productTag, nameof(productTag)); - return GetSeName((string)productTag.GetLocalized(x => x.Name, languageId)); + return GetSeName((string)productTag.GetLocalized(x => x.Name, languageId)); } /// /// Gets blog post SE (search engine) name /// /// Blog post + /// Language identifier /// Blog post SE (search engine) name - public static string GetSeName(this BlogPost blogPost) + public static string GetSeName(this BlogPost blogPost, int languageId) { - Guard.NotNull(blogPost, nameof(blogPost)); + Guard.NotNull(blogPost, nameof(blogPost)); - return GetSeName(blogPost.Title); + return GetSeName(blogPost.GetLocalized(x => x.Title, languageId)); } /// @@ -60,33 +62,34 @@ public static string GetSeName(this BlogPost blogPost) /// Blog post SE (search engine) name public static string GetSeName(this BlogPostTag blogPostTag) { - Guard.NotNull(blogPostTag, nameof(blogPostTag)); + Guard.NotNull(blogPostTag, nameof(blogPostTag)); - return GetSeName(blogPostTag.Name); + return GetSeName(blogPostTag.Name); } /// /// Gets news item SE (search engine) name /// /// News item + /// Language identifier /// News item SE (search engine) name - public static string GetSeName(this NewsItem newsItem) + public static string GetSeName(this NewsItem newsItem, int languageId) { - Guard.NotNull(newsItem, nameof(newsItem)); + Guard.NotNull(newsItem, nameof(newsItem)); - return GetSeName(newsItem.Title); + return GetSeName(newsItem.GetLocalized(x => x.Title, languageId)); } - /// - /// Gets ForumTopic SE (search engine) name - /// - /// ForumTopic - /// ForumTopic SE (search engine) name - public static string GetSeName(this ForumTopic forumTopic) + /// + /// Gets ForumTopic SE (search engine) name + /// + /// ForumTopic + /// ForumTopic SE (search engine) name + public static string GetSeName(this ForumTopic forumTopic) { - Guard.NotNull(forumTopic, nameof(forumTopic)); + Guard.NotNull(forumTopic, nameof(forumTopic)); - string seName = GetSeName(forumTopic.Subject); + string seName = GetSeName(forumTopic.Subject); // Trim SE name to avoid URLs that are too long var maxLength = 100; @@ -98,45 +101,22 @@ public static string GetSeName(this ForumTopic forumTopic) return seName; } - /// - /// Get search engine name for a category node - /// - /// Node - /// Search engine name - public static string GetSeName(this ICategoryNode node) - { - Guard.NotNull(node, nameof(node)); - - return GetSeName( - "Category", - node.Id, - EngineContext.Current.Resolve().WorkingLanguage.Id, - EngineContext.Current.Resolve(), - EngineContext.Current.Resolve()); - } - - #endregion - - #region Generic - - /// - /// Get search engine name - /// - /// Entity type - /// Entity - /// Search engine name - public static string GetSeName(this T entity) - where T : BaseEntity, ISlugSupported + /// + /// Get search engine name for a category node + /// + /// Node + /// SEO slug + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetSeName(this ICategoryNode node) { - Guard.NotNull(entity, nameof(entity)); + Guard.NotNull(node, nameof(node)); + + return EngineContext.Current.Resolve().GetSeName("Category", node.Id, null); + } + + #endregion - return GetSeName( - entity.GetEntityName(), - entity.Id, - EngineContext.Current.Resolve().WorkingLanguage.Id, - EngineContext.Current.Resolve(), - EngineContext.Current.Resolve()); - } + #region Generic /// /// Get search engine name @@ -146,183 +126,117 @@ public static string GetSeName(this T entity) /// Language identifier /// A value indicating whether to return default value (if language specified one is not found) /// A value indicating whether to ensure that we have at least two published languages; otherwise, load only default value - /// Search engine name - public static string GetSeName(this T entity, - int languageId, - bool returnDefaultValue = true, - bool ensureTwoPublishedLanguages = true) + /// SEO slug + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string GetSeName(this T entity, + int? languageId = null, + bool returnDefaultValue = true, + bool ensureTwoPublishedLanguages = true) where T : BaseEntity, ISlugSupported { - Guard.NotNull(entity, nameof(entity)); - - return GetSeName( - entity.GetEntityName(), - entity.Id, - languageId, - EngineContext.Current.Resolve(), - EngineContext.Current.Resolve(), - returnDefaultValue, - ensureTwoPublishedLanguages); + Guard.NotNull(entity, nameof(entity)); + + return EngineContext.Current.Resolve().GetSeName( + entity.GetEntityName(), + entity.Id, + languageId, + returnDefaultValue, + ensureTwoPublishedLanguages); } - /// - /// Get search engine name - /// - /// Entity type - /// Entity - /// Language identifier - /// A value indicating whether to return default value (if language specified one is not found) - /// A value indicating whether to ensure that we have at least two published languages; otherwise, load only default value - /// Search engine name - public static string GetSeName(this T entity, - int languageId, - IUrlRecordService urlRecordService, - ILanguageService languageService, - bool returnDefaultValue = true, - bool ensureTwoPublishedLanguages = true) - where T : BaseEntity, ISlugSupported - { - Guard.NotNull(entity, nameof(entity)); - - return GetSeName( - entity.GetEntityName(), - entity.Id, - languageId, - urlRecordService, - languageService, - returnDefaultValue, - ensureTwoPublishedLanguages); - } - - private static string GetSeName( - string entityName, - int entityId, - int languageId, - IUrlRecordService urlRecordService, - ILanguageService languageService, - bool returnDefaultValue = true, - bool ensureTwoPublishedLanguages = true) - { - string result = string.Empty; - - if (languageId > 0) - { - // ensure that we have at least two published languages - bool loadLocalizedValue = true; - if (ensureTwoPublishedLanguages) - { - var totalPublishedLanguages = languageService.GetLanguagesCount(false); - loadLocalizedValue = totalPublishedLanguages >= 2; - } - // localized value - if (loadLocalizedValue) - { - result = urlRecordService.GetActiveSlug(entityId, entityName, languageId); - } - } - - // set default value if required - if (String.IsNullOrEmpty(result) && returnDefaultValue) - { - result = urlRecordService.GetActiveSlug(entityId, entityName, 0); - } - - return result; - } - - /// - /// Validate search engine name - /// - /// Entity - /// Search engine name to validate - /// User-friendly name used to generate sename - /// Ensreu that sename is not empty - /// Valid sename - public static string ValidateSeName(this T entity, string seName, string name, bool ensureNotEmpty, int? languageId = null) + /// + /// Validate search engine name + /// + /// Entity + /// Search engine name to validate + /// User-friendly name used to generate sename + /// Ensreu that sename is not empty + /// Valid sename + public static string ValidateSeName(this T entity, string seName, string name, bool ensureNotEmpty, int? languageId = null) where T : BaseEntity, ISlugSupported { - return entity.ValidateSeName( - seName, - name, - ensureNotEmpty, - EngineContext.Current.Resolve(), - EngineContext.Current.Resolve(), - languageId); + return entity.ValidateSeName( + seName, + name, + ensureNotEmpty, + EngineContext.Current.Resolve(), + EngineContext.Current.Resolve(), + languageId); } - public static string ValidateSeName(this T entity, - string seName, - string name, - bool ensureNotEmpty, - IUrlRecordService urlRecordService, - SeoSettings seoSettings, - int? languageId = null, - Func extraSlugLookup = null) - where T : BaseEntity, ISlugSupported - { - Guard.NotNull(urlRecordService, nameof(urlRecordService)); - Guard.NotNull(seoSettings, nameof(seoSettings)); - Guard.NotNull(entity, nameof(entity)); - - // use name if sename is not specified - if (String.IsNullOrWhiteSpace(seName) && !String.IsNullOrWhiteSpace(name)) - seName = name; - - // validation - seName = GetSeName(seName, seoSettings); - - // max length - seName = seName.Truncate(400); - - if (String.IsNullOrWhiteSpace(seName)) - { - if (ensureNotEmpty) - { - // use entity identifier as sename if empty - seName = entity.Id.ToString(); - } - else - { - // return. no need for further processing - return seName; - } - } + public static string ValidateSeName(this T entity, + string seName, + string name, + bool ensureNotEmpty, + IUrlRecordService urlRecordService, + SeoSettings seoSettings, + int? languageId = null, + Func extraSlugLookup = null) + where T : BaseEntity, ISlugSupported + { + Guard.NotNull(urlRecordService, nameof(urlRecordService)); + Guard.NotNull(seoSettings, nameof(seoSettings)); + Guard.NotNull(entity, nameof(entity)); + + // use name if sename is not specified + if (String.IsNullOrWhiteSpace(seName) && !String.IsNullOrWhiteSpace(name)) + seName = name; + + // validation + seName = GetSeName(seName, seoSettings); + + // max length + seName = seName.Truncate(400); + + if (String.IsNullOrWhiteSpace(seName)) + { + if (ensureNotEmpty) + { + // use entity identifier as sename if empty + seName = entity.Id.ToString(); + } + else + { + // return. no need for further processing + return seName; + } + } // validate and alter SeName if it could be interpreted as SEO code if (LocalizationHelper.IsValidCultureCode(seName)) { if (seName.Length == 2) { - seName = seName + "-0"; + seName += "-0"; } } - - // ensure this sename is not reserved yet - string entityName = entity.GetEntityName(); - int i = 2; - var tempSeName = seName; - while (true) - { - // check whether such slug already exists (and that it's not the current entity) - var urlRecord = urlRecordService.GetBySlug(tempSeName) ?? extraSlugLookup?.Invoke(tempSeName); - var reserved1 = urlRecord != null && !(urlRecord.EntityId == entity.Id && urlRecord.EntityName.Equals(entityName, StringComparison.InvariantCultureIgnoreCase)); + // ensure this sename is not reserved yet + string entityName = entity.GetEntityName(); + int i = 2; + var tempSeName = seName; + + while (true) + { + // check whether such slug already exists (and that it's not the current entity) + var urlRecord = urlRecordService.GetBySlug(tempSeName) ?? extraSlugLookup?.Invoke(tempSeName); + var reserved1 = urlRecord != null && !(urlRecord.EntityId == entity.Id && urlRecord.EntityName.Equals(entityName, StringComparison.InvariantCultureIgnoreCase)); - if (!reserved1 && urlRecord != null && languageId.HasValue) - reserved1 = (urlRecord.LanguageId != languageId.Value); + if (!reserved1 && urlRecord != null && languageId.HasValue) + reserved1 = (urlRecord.LanguageId != languageId.Value); - // and it's not in the list of reserved slugs - var reserved2 = seoSettings.ReservedUrlRecordSlugs.Contains(tempSeName, StringComparer.InvariantCultureIgnoreCase); - if (!reserved1 && !reserved2) - break; + // and it's not in the list of reserved slugs + var reserved2 = seoSettings.ReservedUrlRecordSlugs.Contains(tempSeName, StringComparer.InvariantCultureIgnoreCase); + if (!reserved1 && !reserved2) + break; - tempSeName = string.Format("{0}-{1}", seName, i); - i++; - } - seName = tempSeName; + tempSeName = string.Format("{0}-{1}", seName, i); + i++; + } + seName = tempSeName; - return seName; - } + return seName; + } /// @@ -333,24 +247,24 @@ public static string ValidateSeName(this T entity, public static string GetSeName(string name) { var seoSettings = EngineContext.Current.Resolve(); - return GetSeName(name, seoSettings); + return GetSeName(name, seoSettings); } - /// - /// Get SEO friendly name - /// - /// Name - /// SEO settings - /// Result - public static string GetSeName(string name, SeoSettings seoSettings) - { - return SeoHelper.GetSeName( - name, - seoSettings == null ? false : seoSettings.ConvertNonWesternChars, - seoSettings == null ? false : seoSettings.AllowUnicodeCharsInUrls, + /// + /// Get SEO friendly name + /// + /// Name + /// SEO settings + /// Result + public static string GetSeName(string name, SeoSettings seoSettings) + { + return SeoHelper.GetSeName( + name, + seoSettings == null ? false : seoSettings.ConvertNonWesternChars, + seoSettings == null ? false : seoSettings.AllowUnicodeCharsInUrls, true, - seoSettings?.SeoNameCharConversion); - } + seoSettings?.SeoNameCharConversion); + } #endregion } diff --git a/src/Libraries/SmartStore.Services/Seo/Tasks/RebuildXmlSitemapTask.cs b/src/Libraries/SmartStore.Services/Seo/Tasks/RebuildXmlSitemapTask.cs index 5e07f2a9fb..e2e99c680f 100644 --- a/src/Libraries/SmartStore.Services/Seo/Tasks/RebuildXmlSitemapTask.cs +++ b/src/Libraries/SmartStore.Services/Seo/Tasks/RebuildXmlSitemapTask.cs @@ -4,51 +4,52 @@ using System.Text; using System.Threading.Tasks; using SmartStore.Core.Domain.Seo; +using SmartStore.Services.Configuration; using SmartStore.Services.Localization; using SmartStore.Services.Stores; using SmartStore.Services.Tasks; namespace SmartStore.Services.Seo { - public class RebuildXmlSitemapTask : AsyncTask - { - private readonly IStoreService _storeService; - private readonly ILanguageService _languageService; - private readonly IXmlSitemapGenerator _generator; - private readonly SeoSettings _seoSettings; + public class RebuildXmlSitemapTask : AsyncTask + { + private readonly IStoreService _storeService; + private readonly ILanguageService _languageService; + private readonly IXmlSitemapGenerator _generator; + private readonly ISettingService _settingService; - public RebuildXmlSitemapTask( - IStoreService storeService, - ILanguageService languageService, - IXmlSitemapGenerator generator, - SeoSettings seoSettings) - { - _storeService = storeService; - _languageService = languageService; - _generator = generator; - _seoSettings = seoSettings; - } + public RebuildXmlSitemapTask( + IStoreService storeService, + ILanguageService languageService, + IXmlSitemapGenerator generator, + ISettingService settingService) + { + _storeService = storeService; + _languageService = languageService; + _generator = generator; + _settingService = settingService; + } - public override async Task ExecuteAsync(TaskExecutionContext ctx) - { - var stores = _storeService.GetAllStores(); + public override async Task ExecuteAsync(TaskExecutionContext ctx) + { + var stores = _storeService.GetAllStores(); - foreach (var store in stores) - { - var languages = _languageService.GetAllLanguages(false, store.Id); - var buildContext = new XmlSitemapBuildContext(store, languages.ToArray()) - { - CancellationToken = ctx.CancellationToken, - ProgressCallback = OnProgress - }; + foreach (var store in stores) + { + var languages = _languageService.GetAllLanguages(false, store.Id); + var buildContext = new XmlSitemapBuildContext(store, languages.ToArray(), _settingService, _storeService.IsSingleStoreMode()) + { + CancellationToken = ctx.CancellationToken, + ProgressCallback = OnProgress + }; - await _generator.RebuildAsync(buildContext); - } + await _generator.RebuildAsync(buildContext); + } - void OnProgress(int value, int max, string msg) - { - ctx.SetProgress(value, max, msg, true); - } - } - } + void OnProgress(int value, int max, string msg) + { + ctx.SetProgress(value, max, msg, true); + } + } + } } diff --git a/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs b/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs index df8bad3de9..2a723529ee 100644 --- a/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs +++ b/src/Libraries/SmartStore.Services/Seo/UrlRecordService.cs @@ -13,81 +13,81 @@ namespace SmartStore.Services.Seo { 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_ALL_PATTERN = "urlrecord:*"; - const string URLRECORD_ALL_ACTIVESLUGS_KEY = "urlrecord:all-active-slugs"; - - private readonly IRepository _urlRecordRepository; + /// + /// 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_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 SeoSettings _seoSettings; + private readonly PerformanceSettings _performanceSettings; - private readonly IDictionary _prefetchedCollections; - private static int _lastCacheSegmentSize = -1; + 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, + PerformanceSettings performanceSettings) { _cacheManager = cacheManager; _urlRecordRepository = urlRecordRepository; - _seoSettings = seoSettings; - _performanceSettings = performanceSettings; + _seoSettings = seoSettings; + _performanceSettings = performanceSettings; - _prefetchedCollections = new Dictionary(StringComparer.OrdinalIgnoreCase); + _prefetchedCollections = new Dictionary(StringComparer.OrdinalIgnoreCase); - ValidateCacheState(); + 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) - { - _cacheManager.RemoveByPattern(URLRECORD_SEGMENT_PATTERN); - changed = true; - } - - if (changed) - { - Interlocked.Exchange(ref _lastCacheSegmentSize, size); - } - } - - protected override void OnClearCache() - { - _cacheManager.Remove(URLRECORD_ALL_PATTERN); - } - - public virtual void DeleteUrlRecord(UrlRecord urlRecord) + private void ValidateCacheState() { - Guard.NotNull(urlRecord, nameof(urlRecord)); + // Ensure that after a segment size change the cache segments are invalidated. + var size = _performanceSettings.CacheSegmentSize; + var changed = _lastCacheSegmentSize == -1; - try - { - // cache - ClearCacheSegment(urlRecord.EntityName, urlRecord.EntityId, urlRecord.LanguageId); - HasChanges = true; + if (size <= 0) + { + _performanceSettings.CacheSegmentSize = size = 1; + } + + if (_lastCacheSegmentSize > 0 && _lastCacheSegmentSize != size) + { + _cacheManager.RemoveByPattern(URLRECORD_SEGMENT_PATTERN); + changed = true; + } - // db - _urlRecordRepository.Delete(urlRecord); - } - catch { } - } + if (changed) + { + Interlocked.Exchange(ref _lastCacheSegmentSize, size); + } + } + + protected override void OnClearCache() + { + _cacheManager.RemoveByPattern(URLRECORD_ALL_PATTERN); + } + + public virtual void DeleteUrlRecord(UrlRecord urlRecord) + { + Guard.NotNull(urlRecord, nameof(urlRecord)); + + try + { + // cache + ClearCacheSegment(urlRecord.EntityName, urlRecord.EntityId, urlRecord.LanguageId); + HasChanges = true; + + // db + _urlRecordRepository.Delete(urlRecord); + } + catch { } + } public virtual UrlRecord GetUrlRecordById(int urlRecordId) { @@ -98,267 +98,267 @@ public virtual UrlRecord GetUrlRecordById(int urlRecordId) return urlRecord; } - public virtual IList GetUrlRecordsByIds(int[] urlRecordIds) - { - if (urlRecordIds == null || urlRecordIds.Length == 0) - return new List(); + public virtual IList GetUrlRecordsByIds(int[] urlRecordIds) + { + if (urlRecordIds == null || urlRecordIds.Length == 0) + return new List(); - var urlRecords = _urlRecordRepository.Table - .Where(x => urlRecordIds.Contains(x.Id)) - .ToList(); + var urlRecords = _urlRecordRepository.Table + .Where(x => urlRecordIds.Contains(x.Id)) + .ToList(); - return urlRecords; - } + return urlRecords; + } public virtual void InsertUrlRecord(UrlRecord urlRecord) { - Guard.NotNull(urlRecord, nameof(urlRecord)); - - try - { - // db - _urlRecordRepository.Insert(urlRecord); - HasChanges = true; - - // cache - ClearCacheSegment(urlRecord.EntityName, urlRecord.EntityId, urlRecord.LanguageId); - } - catch { } + Guard.NotNull(urlRecord, nameof(urlRecord)); + + try + { + // db + _urlRecordRepository.Insert(urlRecord); + HasChanges = true; + + // cache + ClearCacheSegment(urlRecord.EntityName, urlRecord.EntityId, urlRecord.LanguageId); + } + catch { } } public virtual void UpdateUrlRecord(UrlRecord urlRecord) { - Guard.NotNull(urlRecord, nameof(urlRecord)); + Guard.NotNull(urlRecord, nameof(urlRecord)); - try - { - // db - _urlRecordRepository.Update(urlRecord); - HasChanges = true; + try + { + // db + _urlRecordRepository.Update(urlRecord); + HasChanges = true; - // cache - ClearCacheSegment(urlRecord.EntityName, urlRecord.EntityId, urlRecord.LanguageId); - } - catch { } - } + // cache + ClearCacheSegment(urlRecord.EntityName, urlRecord.EntityId, urlRecord.LanguageId); + } + catch { } + } public virtual IPagedList GetAllUrlRecords(int pageIndex, int pageSize, string slug, string entityName, int? entityId, int? languageId, bool? isActive) { var query = _urlRecordRepository.Table; - if (slug.HasValue()) - query = query.Where(x => x.Slug.Contains(slug)); + if (slug.HasValue()) + query = query.Where(x => x.Slug.Contains(slug)); + + if (entityName.HasValue()) + query = query.Where(x => x.EntityName == entityName); + + if (entityId.HasValue) + query = query.Where(x => x.EntityId == entityId.Value); + + if (isActive.HasValue) + query = query.Where(x => x.IsActive == isActive.Value); + + if (languageId.HasValue) + query = query.Where(x => x.LanguageId == languageId); - if (entityName.HasValue()) - query = query.Where(x => x.EntityName == entityName); + query = query.OrderBy(x => x.Slug); - if (entityId.HasValue) - query = query.Where(x => x.EntityId == entityId.Value); + var urlRecords = new PagedList(query, pageIndex, pageSize); + return urlRecords; + } + + public virtual IList GetUrlRecordsFor(string entityName, int entityId, bool activeOnly = false) + { + Guard.NotEmpty(entityName, nameof(entityName)); + + var query = from ur in _urlRecordRepository.Table + where ur.EntityId == entityId && + ur.EntityName == entityName + select ur; + + if (activeOnly) + { + query = query.Where(ur => ur.IsActive); + } - if (isActive.HasValue) - query = query.Where(x => x.IsActive == isActive.Value); + return query.ToList(); + } - if (languageId.HasValue) - query = query.Where(x => x.LanguageId == languageId); + public virtual void PrefetchUrlRecords(string entityName, int[] languageIds, int[] entityIds, bool isRange = false, bool isSorted = false) + { + var collection = GetUrlRecordCollectionInternal(entityName, languageIds, entityIds, isRange, isSorted); - query = query.OrderBy(x => x.Slug); + if (_prefetchedCollections.TryGetValue(entityName, out var existing)) + { + collection.MergeWith(existing); + } + else + { + _prefetchedCollections[entityName] = collection; + } + } - var urlRecords = new PagedList(query, pageIndex, pageSize); - return urlRecords; + 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 IList GetUrlRecordsFor(string entityName, int entityId, bool activeOnly = false) - { - Guard.NotEmpty(entityName, nameof(entityName)); - - var query = from ur in _urlRecordRepository.Table - where ur.EntityId == entityId && - ur.EntityName == entityName - select ur; - - if (activeOnly) - { - query = query.Where(ur => ur.IsActive); - } - - 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) - { + 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) + { // Avoid "The LINQ expression node type 'ArrayIndex' is not supported in LINQ to Entities". var languageId = languageIds[0]; query = query.Where(x => x.LanguageId == languageId); - } - 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. - if (String.IsNullOrEmpty(slug)) - return null; - - var query = from ur in _urlRecordRepository.Table - where ur.Slug == slug - select ur; - - var urlRecord = query.FirstOrDefault(); - return urlRecord; - } + } + 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. + if (String.IsNullOrEmpty(slug)) + return null; + + var query = from ur in _urlRecordRepository.Table + where ur.Slug == slug + select ur; + + var urlRecord = query.FirstOrDefault(); + return urlRecord; + } 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); - } - - string slug = null; - - if (_seoSettings.LoadAllUrlAliasesOnStartup) - { - 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; - } - }, independent: true); - - var key = GenerateKey(entityId, entityName, languageId); - if (!allActiveSlugs.TryGetValue(key, out slug)) - { - return string.Empty; - } - } - else - { - var slugs = GetCacheSegment(entityName, entityId, languageId); - - if (!slugs.TryGetValue(entityId, out slug)) - { - return string.Empty; - } - } - - return slug; + 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); + } + + string slug = null; + + if (_seoSettings.LoadAllUrlAliasesOnStartup) + { + 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; + } + }, independent: true); + + var key = GenerateKey(entityId, entityName, languageId); + if (!allActiveSlugs.TryGetValue(key, out slug)) + { + return string.Empty; + } + } + else + { + var slugs = GetCacheSegment(entityName, entityId, languageId); + + if (!slugs.TryGetValue(entityId, out slug)) + { + return string.Empty; + } + } + + return slug; } - protected string GetActiveSlugUncached(int entityId, string entityName, int languageId) - { - var query = from x in _urlRecordRepository.TableUntracked - where - x.EntityId == entityId && - x.EntityName == entityName && - x.LanguageId == languageId && - x.IsActive - orderby x.Id descending - select x.Slug; - - return query.FirstOrDefault().EmptyNull(); - } - - public virtual UrlRecord SaveSlug(T entity, string slug, int languageId) where T : BaseEntity, ISlugSupported + protected string GetActiveSlugUncached(int entityId, string entityName, int languageId) { - Guard.NotNull(entity, nameof(entity)); + var query = from x in _urlRecordRepository.TableUntracked + where + x.EntityId == entityId && + x.EntityName == entityName && + x.LanguageId == languageId && + x.IsActive + orderby x.Id descending + select x.Slug; + + return query.FirstOrDefault().EmptyNull(); + } + + public virtual UrlRecord SaveSlug(T entity, string slug, int languageId) where T : BaseEntity, ISlugSupported + { + Guard.NotNull(entity, nameof(entity)); int entityId = entity.Id; string entityName = entity.GetEntityName(); - UrlRecord result = null; + UrlRecord result = null; var query = from ur in _urlRecordRepository.Table where ur.EntityId == entityId && ur.EntityName == entityName && ur.LanguageId == languageId - orderby ur.Id descending + orderby ur.Id descending select ur; var allUrlRecords = query.ToList(); @@ -385,7 +385,7 @@ orderby ur.Id descending IsActive = true, }; InsertUrlRecord(urlRecord); - result = urlRecord; + result = urlRecord; } } @@ -421,164 +421,164 @@ orderby ur.Id descending } else { - // MC: Absolutely ensure, that we have no duplicate active record for this entity. - // In such case a record other than "activeUrlRecord" could have same seName - // and the DB would report an Index error. - var alreadyActiveDuplicate = allUrlRecords.FirstOrDefault(x => x.Slug.IsCaseInsensitiveEqual(slug) && x.IsActive); - if (alreadyActiveDuplicate != null) - { - // deactivate all - allUrlRecords.Each(x => x.IsActive = false); - // set the existing one to active again - alreadyActiveDuplicate.IsActive = true; - // update all records - allUrlRecords.Each(x => UpdateUrlRecord(x)); - } - else - { - // insert new record - // we do not update the existing record because we should track all previously entered slugs - // to ensure that URLs will work fine - var urlRecord = new UrlRecord - { - EntityId = entity.Id, - EntityName = entityName, - Slug = slug, - LanguageId = languageId, - IsActive = true, - }; - InsertUrlRecord(urlRecord); - result = urlRecord; - - // disable the previous active URL record - activeUrlRecord.IsActive = false; - UpdateUrlRecord(activeUrlRecord); - } + // MC: Absolutely ensure, that we have no duplicate active record for this entity. + // In such case a record other than "activeUrlRecord" could have same seName + // and the DB would report an Index error. + var alreadyActiveDuplicate = allUrlRecords.FirstOrDefault(x => x.Slug.IsCaseInsensitiveEqual(slug) && x.IsActive); + if (alreadyActiveDuplicate != null) + { + // deactivate all + allUrlRecords.Each(x => x.IsActive = false); + // set the existing one to active again + alreadyActiveDuplicate.IsActive = true; + // update all records + allUrlRecords.Each(x => UpdateUrlRecord(x)); + } + else + { + // insert new record + // we do not update the existing record because we should track all previously entered slugs + // to ensure that URLs will work fine + var urlRecord = new UrlRecord + { + EntityId = entity.Id, + EntityName = entityName, + Slug = slug, + LanguageId = languageId, + IsActive = true, + }; + InsertUrlRecord(urlRecord); + result = urlRecord; + + // disable the previous active URL record + activeUrlRecord.IsActive = false; + UpdateUrlRecord(activeUrlRecord); + } } } } - return result; + return result; + } + + public virtual UrlRecord SaveSlug(T entity, Func nameProperty) where T : BaseEntity, ISlugSupported + { + string name = nameProperty.Invoke(entity); + + string existingSeName = entity.GetSeName(0, true, false); + existingSeName = entity.ValidateSeName(existingSeName, name, true); + + return SaveSlug(entity, existingSeName, 0); + } + + private string GenerateKey(int entityId, string entityName, int languageId) + { + return "{0}.{1}.{2}".FormatInvariant(entityId, entityName, languageId); } - public virtual UrlRecord SaveSlug(T entity, Expression> nameProperty) where T : BaseEntity, ISlugSupported - { - string name = nameProperty.Compile().Invoke(entity); - - string existingSeName = entity.GetSeName(0, true, false); - existingSeName = entity.ValidateSeName(existingSeName, name, true); - - return SaveSlug(entity, existingSeName, 0); - } - - private string GenerateKey(int entityId, string entityName, int languageId) - { - return "{0}.{1}.{2}".FormatInvariant(entityId, entityName, languageId); - } - - public virtual Dictionary CountSlugsPerEntity(int[] urlRecordIds) - { - if (urlRecordIds == null || urlRecordIds.Length == 0) - return new Dictionary(); - - var query = - from x in _urlRecordRepository.TableUntracked - where urlRecordIds.Contains(x.Id) - select new - { - Id = x.Id, - Count = _urlRecordRepository.TableUntracked.Where(y => y.EntityName == x.EntityName && y.EntityId == x.EntityId).Count() - }; - - var result = query - .ToList() - .ToDictionary(x => x.Id, x => x.Count); - - return result; - } - - public virtual int CountSlugsPerEntity(string entityName, int entityId) - { - var count = _urlRecordRepository.Table - .Where(x => x.EntityName == entityName && x.EntityId == entityId) - .Count(); - - return count; - } - - protected virtual IDictionary GetCacheSegment(string entityName, int entityId, int languageId) - { - Guard.NotEmpty(entityName, nameof(entityName)); - - var segmentKey = GetSegmentKeyPart(entityName, entityId, out var minEntityId, out var maxEntityId); - var cacheKey = BuildCacheSegmentKey(segmentKey, languageId); - - 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) - { - dict[ur.EntityId] = ur.Slug.EmptyNull(); - } - - return dict; - } - }, independent: true); - } - - /// - /// Clears the cached segment from the cache - /// - protected virtual void ClearCacheSegment(string entityName, int entityId, int? languageId = null) - { - if (IsInScope) - return; - - var segmentKey = GetSegmentKeyPart(entityName, entityId); - - if (languageId.HasValue && languageId.Value > 0) - { - _cacheManager.Remove(BuildCacheSegmentKey(segmentKey, languageId.Value)); - } - else - { - _cacheManager.RemoveByPattern(URLRECORD_SEGMENT_PATTERN.FormatInvariant(segmentKey)); - } - - // always delete this (in case when LoadAllOnStartup is true) - _cacheManager.Remove(URLRECORD_ALL_ACTIVESLUGS_KEY); - } - - private string BuildCacheSegmentKey(string segment, int languageId) - { - return String.Format(URLRECORD_SEGMENT_KEY, segment, languageId); - } - - private string GetSegmentKeyPart(string entityName, int entityId) - { - return GetSegmentKeyPart(entityName, entityId, out _, out _); - } - - private string GetSegmentKeyPart(string entityName, int entityId, out int minId, out int maxId) - { - maxId = entityId.GetRange(_performanceSettings.CacheSegmentSize, out minId); - return (entityName + "." + maxId.ToString()).ToLowerInvariant(); - } - } + public virtual Dictionary CountSlugsPerEntity(int[] urlRecordIds) + { + if (urlRecordIds == null || urlRecordIds.Length == 0) + return new Dictionary(); + + var query = + from x in _urlRecordRepository.TableUntracked + where urlRecordIds.Contains(x.Id) + select new + { + Id = x.Id, + Count = _urlRecordRepository.TableUntracked.Where(y => y.EntityName == x.EntityName && y.EntityId == x.EntityId).Count() + }; + + var result = query + .ToList() + .ToDictionary(x => x.Id, x => x.Count); + + return result; + } + + public virtual int CountSlugsPerEntity(string entityName, int entityId) + { + var count = _urlRecordRepository.Table + .Where(x => x.EntityName == entityName && x.EntityId == entityId) + .Count(); + + return count; + } + + protected virtual IDictionary GetCacheSegment(string entityName, int entityId, int languageId) + { + Guard.NotEmpty(entityName, nameof(entityName)); + + var segmentKey = GetSegmentKeyPart(entityName, entityId, out var minEntityId, out var maxEntityId); + var cacheKey = BuildCacheSegmentKey(segmentKey, languageId); + + 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) + { + dict[ur.EntityId] = ur.Slug.EmptyNull(); + } + + return dict; + } + }, independent: true); + } + + /// + /// Clears the cached segment from the cache + /// + protected virtual void ClearCacheSegment(string entityName, int entityId, int? languageId = null) + { + if (IsInScope) + return; + + var segmentKey = GetSegmentKeyPart(entityName, entityId); + + if (languageId.HasValue && languageId.Value > 0) + { + _cacheManager.Remove(BuildCacheSegmentKey(segmentKey, languageId.Value)); + } + else + { + _cacheManager.RemoveByPattern(URLRECORD_SEGMENT_PATTERN.FormatInvariant(segmentKey)); + } + + // always delete this (in case when LoadAllOnStartup is true) + _cacheManager.Remove(URLRECORD_ALL_ACTIVESLUGS_KEY); + } + + private string BuildCacheSegmentKey(string segment, int languageId) + { + return string.Format(URLRECORD_SEGMENT_KEY, segment, languageId); + } + + private string GetSegmentKeyPart(string entityName, int entityId) + { + return GetSegmentKeyPart(entityName, entityId, out _, out _); + } + + private string GetSegmentKeyPart(string entityName, int entityId, out int minId, out int maxId) + { + (minId, maxId) = entityId.GetRange(_performanceSettings.CacheSegmentSize); + return (entityName + "." + minId.ToString()).ToLowerInvariant(); + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Seo/XmlSitemapBuildContext.cs b/src/Libraries/SmartStore.Services/Seo/XmlSitemapBuildContext.cs index 6aa8504f00..82f7514c67 100644 --- a/src/Libraries/SmartStore.Services/Seo/XmlSitemapBuildContext.cs +++ b/src/Libraries/SmartStore.Services/Seo/XmlSitemapBuildContext.cs @@ -1,28 +1,46 @@ using System; using System.Threading; +using System.Web; +using SmartStore.Core.Configuration; using SmartStore.Core.Domain.Localization; using SmartStore.Core.Domain.Stores; +using SmartStore.Services.Configuration; using SmartStore.Services.Tasks; namespace SmartStore.Services.Seo { - public class XmlSitemapBuildContext - { - public XmlSitemapBuildContext(Store store, Language[] languages) - { - Guard.NotNull(store, nameof(store)); - Guard.NotEmpty(languages, nameof(languages)); - - Store = store; - Languages = languages; - Protocol = Store.ForceSslForAllPages ? "https" : "http"; - } - - public CancellationToken CancellationToken { get; set; } - public ProgressCallback ProgressCallback { get; set; } - public Store Store { get; set; } - public Language[] Languages { get; set; } - - public string Protocol { get; private set; } - } + public class XmlSitemapBuildContext + { + private readonly ISettingService _settingService; + private readonly bool _isSingleStoreMode; + + public XmlSitemapBuildContext(Store store, Language[] languages, ISettingService settingService, bool isSingleStoreMode) + { + Guard.NotNull(store, nameof(store)); + Guard.NotEmpty(languages, nameof(languages)); + + Store = store; + Languages = languages; + Protocol = Store.ForceSslForAllPages ? "https" : "http"; + + _settingService = settingService; + _isSingleStoreMode = isSingleStoreMode; + + RequestStoreId = _isSingleStoreMode ? 0 : store.Id; + } + + public CancellationToken CancellationToken { get; set; } + public ProgressCallback ProgressCallback { get; set; } + public Store Store { get; set; } + public int RequestStoreId { get; private set; } + public Language[] Languages { get; set; } + public int MaximumNodeCount { get; set; } = XmlSitemapGenerator.MaximumSiteMapNodeCount; + + public string Protocol { get; private set; } + + public T LoadSetting() where T : ISettings, new() + { + return _settingService.LoadSetting(RequestStoreId); + } + } } diff --git a/src/Libraries/SmartStore.Services/Seo/XmlSitemapGenerator.cs b/src/Libraries/SmartStore.Services/Seo/XmlSitemapGenerator.cs index 17a8e5356e..088ed43b75 100644 --- a/src/Libraries/SmartStore.Services/Seo/XmlSitemapGenerator.cs +++ b/src/Libraries/SmartStore.Services/Seo/XmlSitemapGenerator.cs @@ -6,832 +6,694 @@ using System.Data.Entity; using System.Web.Mvc; using System.Xml.Linq; -using SmartStore.Core; +using System.IO; +using System.Threading.Tasks; using SmartStore.Core.Data; -using SmartStore.Core.Domain.Catalog; 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 + public partial class XmlSitemapGenerator : IXmlSitemapGenerator { - 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. - /// - private const int MaximumSiteMapCount = 50000; - - /// - /// 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. - /// - private const int MaximumSiteMapNodeCount = 2000; - - /// - /// The maximum size of a sitemap file in bytes (10MB). - /// - private const int MaximumSiteMapSizeInBytes = 10485760; - - 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 ICommonServices _services; - private readonly ILockFileManager _lockFileManager; - private readonly UrlHelper _urlHelper; - - private readonly IVirtualFolder _tenantFolder; - private readonly string _baseDir; - - public XmlSitemapGenerator( - ICategoryService categoryService, - IProductService productService, - IManufacturerService manufacturerService, - ITopicService topicService, - ILanguageService languageService, - ICustomerService customerService, - ICatalogSearchService catalogSearchService, - IUrlRecordService urlRecordService, - ICommonServices services, - ILockFileManager lockFileManager, - UrlHelper urlHelper) + 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. + /// + private const int MaximumSiteMapCount = 50000; + + /// + /// 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. + /// + internal const int MaximumSiteMapNodeCount = 2000; + + /// + /// The maximum size of a sitemap file in bytes (10MB). + /// + private const int MaximumSiteMapSizeInBytes = 10485760; + + private readonly IEnumerable> _publishers; + private readonly ILanguageService _languageService; + private readonly ICustomerService _customerService; + private readonly IUrlRecordService _urlRecordService; + private readonly ICommonServices _services; + private readonly ILockFileManager _lockFileManager; + private readonly UrlHelper _urlHelper; + + private readonly IVirtualFolder _tenantFolder; + private readonly string _baseDir; + + public XmlSitemapGenerator( + IEnumerable> publishers, + ILanguageService languageService, + ICustomerService customerService, + IUrlRecordService urlRecordService, + ICommonServices services, + ILockFileManager lockFileManager, + UrlHelper urlHelper) + { + _publishers = publishers; + _languageService = languageService; + _customerService = customerService; + _urlRecordService = urlRecordService; + _services = services; + _lockFileManager = lockFileManager; + _urlHelper = urlHelper; + + _tenantFolder = _services.ApplicationEnvironment.TenantFolder; + _baseDir = _tenantFolder.Combine("Sitemaps"); + + Logger = NullLogger.Instance; + + } + + public ILogger Logger { get; set; } + + public virtual async Task GetSitemapPartAsync(int index = 0) + { + return await GetSitemapPartAsync(index, false); + } + + private async Task GetSitemapPartAsync(int index, bool isRetry) + { + 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 }, _services.Settings, _services.StoreService.IsSingleStoreMode()) + { + CancellationToken = CancellationToken.None + }; + + await RebuildAsync(buildContext); + } + + // DRY: call self to get sitemap partition object + return await GetSitemapPartAsync(index, true); + } + + private bool SitemapFileExists(int storeId, int languageId, int index, out string path, out string name) + { + 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) + { + 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); + } + + private string GetLockFilePath(int storeId, int languageId) + { + var fileName = LockFileNamePattern.FormatInvariant(storeId, languageId); + return _tenantFolder.Combine(_baseDir, fileName); + } + + public virtual async Task RebuildAsync(XmlSitemapBuildContext ctx) + { + Guard.NotNull(ctx, nameof(ctx)); + + // Double seo code otherwise + _urlHelper.RequestContext.RouteData.DataTokens["SeoCodeReplacement"] = string.Empty; + + 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(); + + // All sitemaps grouped by language + var sitemaps = new Multimap(); + + var compositeFileLock = new ActionDisposable(() => + { + foreach (var data in languageData.Values) + { + 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 providers = CreateProviders(ctx); + var total = providers.Sum(x => x.GetTotalCount()); + + 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)) + { + var entities = EnlistEntities(providers); + + 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 slugs = GetUrlRecordCollectionsForBatch(batch.Select(x => x.Entry), languageIds); + + foreach (var data in languageData.Values) + { + var language = data.Language; + var baseUrl = data.BaseUrl; + + // Create all node entries for this segment + var entries = batch + .Where(x => x.Entry.LanguageId.GetValueOrDefault() == 0 || x.Entry.LanguageId.Value == language.Id) + .Select(x => x.Provider.CreateNode(_urlHelper, baseUrl, x.Entry, slugs[x.Entry.EntityName], language)); + sitemaps[language.Id].AddRange(entries.Where(x => x != null)); + + // 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]); + await SaveTempAsync(documents, data, segment - documents.Count + (hasIndex ? 1 : 0)); + + documents.Clear(); + sitemaps.RemoveAll(language.Id); + } + } + + slugs.Clear(); + + //GC.Collect(); + //GC.WaitForPendingFinalizers(); + } + + // Process custom nodes + if (!ctx.CancellationToken.IsCancellationRequested) + { + 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]); + await SaveTempAsync(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 }); + await SaveTempAsync(documents, data, 0); + } + + } + } + } + + ctx.CancellationToken.ThrowIfCancellationRequested(); + + ctx.ProgressCallback?.Invoke(totalSegments, totalSegments, "Finalizing...'"); + + foreach (var data in languageData.Values) + { + // Create index documents (if any) + if (hasIndex && indexNodes.Any()) + { + var indexDocument = CreateSitemapIndexDocument(indexNodes[data.Language.Id]); + await SaveTempAsync(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); + } + } + + //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 defaultLangId = _languageService.GetDefaultLanguageId(store.Id); + if (language.Id != defaultLangId || locSettings.DefaultLanguageRedirectBehaviour < DefaultLanguageRedirectBehaviour.StripSeoCode) + { + host += language.GetTwoLetterISOLanguageName() + "/"; + } + } + + return host; + } + + private async Task SaveTempAsync(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); + + await _tenantFolder.CreateTextFileAsync(filePath, documents[i]); + } + } + + private void SaveFinal(LanguageData data) + { + // Delete current sitemap dir + _tenantFolder.TryDeleteDirectory(data.FinalDir); + + var source = _tenantFolder.MapPath(data.TempDir); + var dest = _tenantFolder.MapPath(data.FinalDir); + + // Move/Rename new (temp) dir to current + System.IO.Directory.Move(source, dest); + + int retries = 0; + while (!SitemapFileExists(data.Store.Id, data.Language.Id, 0, out _, out _)) + { + 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 EnlistEntities(XmlSitemapProvider[] providers) + { + var result = Enumerable.Empty(); + foreach (var provider in providers) + { + result = result.Concat(provider.Enlist().Select(x => new NodeEntry { Entry = x, Provider = provider })); + } + + return result; + } + + private IDictionary GetUrlRecordCollectionsForBatch(IEnumerable batch, int[] languageIds) + { + var result = new Dictionary(); + + if (batch.First().EntityName == "Product") + { + // 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); + } + + 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); + } + + return result; + } + + protected virtual List GetSiteMapDocuments(IReadOnlyCollection nodes) + { + int siteMapCount = (int)Math.Ceiling(nodes.Count / (double)MaximumSiteMapNodeCount); + CheckSitemapCount(siteMapCount); + + var siteMaps = Enumerable + .Range(0, siteMapCount) + .Select(x => + { + return new KeyValuePair>( + x + 1, + nodes.Skip(x * MaximumSiteMapNodeCount).Take(MaximumSiteMapNodeCount)); + }); + + var siteMapDocuments = new List(siteMapCount); + + foreach (var kvp in siteMaps) + { + siteMapDocuments.Add(this.GetSitemapDocument(kvp.Value)); + } + + return siteMapDocuments; + } + + /// + /// Gets the sitemap XML document for the specified set of nodes. + /// + /// The sitemap nodes. + /// The sitemap XML document for the specified set of nodes. + private string GetSitemapDocument(IEnumerable nodes) + { + //var languages = _languageService.GetAllLanguages(); + + XNamespace ns = SiteMapsNamespace; + XNamespace xhtml = XhtmlNamespace; + + XElement root = new XElement( + ns + "urlset", + new XAttribute(XNamespace.Xmlns + "xhtml", xhtml)); + + foreach (var node in nodes) + { + // url + var xel = new XElement + ( + ns + "url", + // url/loc + new XElement(ns + "loc", node.Loc), + // url/lastmod + node.LastMod == null ? null : new XElement( + ns + "lastmod", + node.LastMod.Value.ToLocalTime().ToString("yyyy-MM-ddTHH:mm:sszzz")), + // url/changefreq + node.ChangeFreq == null ? null : new XElement( + ns + "changefreq", + node.ChangeFreq.Value.ToString().ToLowerInvariant()), + // url/priority + node.Priority == null ? null : new XElement( + ns + "priority", + node.Priority.Value.ToString("F1", CultureInfo.InvariantCulture)) + ); + + if (node.Links != null) + { + foreach (var entry in node.Links) + { + // url/xhtml:link[culture] + xel.Add(new XElement + ( + xhtml + "link", + new XAttribute("rel", "alternate"), + new XAttribute("hreflang", entry.Lang), + new XAttribute("href", entry.Href) + )); + } + } + + root.Add(xel); + } + + XDeclaration declaration = new XDeclaration("1.0", "UTF-8", "yes"); + XDocument document = new XDocument(root); + var xml = declaration.ToString() + document.ToString(SaveOptions.DisableFormatting); + CheckDocumentSize(xml); + + 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) + { + XNamespace ns = SiteMapsNamespace; + + XElement root = new XElement(ns + "sitemapindex"); + + foreach (var node in nodes) + { + 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 document = new XDocument(root); + var xml = document.ToString(SaveOptions.DisableFormatting); + CheckDocumentSize(xml); + + return xml; + } + + private string GetSitemapIndexUrl(int index, string baseUrl) + { + var url = _urlHelper.RouteUrl("XmlSitemap", new { index }).TrimStart('/'); + return baseUrl + url; + } + + private XmlSitemapProvider[] CreateProviders(XmlSitemapBuildContext context) + { + return _publishers + .Select(x => x.Value.PublishXmlSitemap(context)) + .Where(x => x != null) + .OrderBy(x => x.Order) + .ToArray(); + } + + protected void ProcessCustomNodes(XmlSitemapBuildContext ctx, Multimap sitemaps) + { + // For inheritors + } + + /// + /// Checks the size of the XML sitemap document. If it is over 10MB, logs an error. + /// + /// The sitemap XML document. + private void CheckDocumentSize(string siteMapXml) + { + if (siteMapXml.Length >= MaximumSiteMapSizeInBytes) + { + Logger.Error(new InvalidOperationException($"Sitemap exceeds the maximum size of 10MB. This is because you have unusually long URL's. Consider reducing the MaximumSitemapNodeCount. Size:<{siteMapXml.Length}>")); + } + } + + /// + /// Checks the count of the number of sitemaps. If it is over 50,000, logs an error. + /// + /// The sitemap count. + private void CheckSitemapCount(int sitemapCount) + { + if (sitemapCount > MaximumSiteMapCount) + { + var ex = new InvalidOperationException($"Sitemap index file exceeds the maximum number of allowed sitemaps of 50,000. Count:<{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 + + struct NodeEntry + { + public NamedEntity Entry { get; set; } + public XmlSitemapProvider Provider { get; set; } + } + + class LanguageData { - _categoryService = categoryService; - _productService = productService; - _manufacturerService = manufacturerService; - _topicService = topicService; - _languageService = languageService; - _customerService = customerService; - _catalogSearchService = catalogSearchService; - _urlRecordService = urlRecordService; - _services = services; - _lockFileManager = lockFileManager; - _urlHelper = urlHelper; - - _tenantFolder = _services.ApplicationEnvironment.TenantFolder; - _baseDir = _tenantFolder.Combine("Sitemaps"); - - Logger = NullLogger.Instance; - - } - - public ILogger Logger { get; set; } - - public virtual async Task GetSitemapPartAsync(int index = 0) - { - return await GetSitemapPartAsync(index, false); - } - - private async Task GetSitemapPartAsync(int index, bool isRetry) - { - 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 - }; - - await RebuildAsync(buildContext); - } - - // DRY: call self to get sitemap partition object - return await GetSitemapPartAsync(index, true); - } - - private bool SitemapFileExists(int storeId, int languageId, int index, out string path, out string name) - { - 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) - { - 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); - } - - private string GetLockFilePath(int storeId, int languageId) - { - var fileName = LockFileNamePattern.FormatInvariant(storeId, languageId); - return _tenantFolder.Combine(_baseDir, fileName); - } - - public virtual async Task RebuildAsync(XmlSitemapBuildContext ctx) - { - 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(); - - // All sitemaps grouped by language - var sitemaps = new Multimap(); - - var compositeFileLock = new ActionDisposable(() => - { - foreach (var data in languageData.Values) - { - 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 = await queries.GetTotalRecordCountAsync(); - - 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)) - { - var entities = EnumerateEntities(queries); - - 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]); - await SaveTempAsync(documents, data, segment - documents.Count + (hasIndex ? 1 : 0)); - - documents.Clear(); - sitemaps.RemoveAll(language.Id); - } - } - - slugs.Clear(); - - //GC.Collect(); - //GC.WaitForPendingFinalizers(); - } - - // Process custom nodes - if (!ctx.CancellationToken.IsCancellationRequested) - { - 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]); - await SaveTempAsync(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 }); - await SaveTempAsync(documents, data, 0); - } - - } - } - } - - ctx.CancellationToken.ThrowIfCancellationRequested(); - - ctx.ProgressCallback?.Invoke(totalSegments, totalSegments, "Finalizing...'"); - - foreach (var data in languageData.Values) - { - // Create index documents (if any) - if (hasIndex && indexNodes.Any()) - { - var indexDocument = CreateSitemapIndexDocument(indexNodes[data.Language.Id]); - await SaveTempAsync(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); - } - } - - //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 defaultLangId = _languageService.GetDefaultLanguageId(store.Id); - if (language.Id != defaultLangId || locSettings.DefaultLanguageRedirectBehaviour < DefaultLanguageRedirectBehaviour.StripSeoCode) - { - host += language.GetTwoLetterISOLanguageName() + "/"; - } - } - - return host; - } - - private string BuildNodeUrl(string baseUrl, NamedEntity entity, UrlRecordCollection slugs, Language language) - { - return baseUrl + slugs.GetSlug(language.Id, entity.Id, true); - } - - private async Task SaveTempAsync(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); - - await _tenantFolder.CreateTextFileAsync(filePath, documents[i]); - } - } - - private void SaveFinal(LanguageData data) - { - // Delete current sitemap dir - _tenantFolder.TryDeleteDirectory(data.FinalDir); - - var source = _tenantFolder.MapPath(data.TempDir); - var dest = _tenantFolder.MapPath(data.FinalDir); - - // Move/Rename new (temp) dir to current - System.IO.Directory.Move(source, dest); - - int retries = 0; - while (!SitemapFileExists(data.Store.Id, data.Language.Id, 0, out _, out _)) - { - 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) - { - if (queries.Categories != null) - { - var categories = queries.Categories.Select(x => new { x.Id, x.UpdatedOnUtc }).ToList(); - foreach (var x in categories) - { - yield return new NamedEntity { EntityName = "Category", Id = x.Id, LastMod = x.UpdatedOnUtc }; - } - } - - if (queries.Manufacturers != null) - { - var manufacturers = queries.Manufacturers.Select(x => new { x.Id, x.UpdatedOnUtc }).ToList(); - foreach (var x in manufacturers) - { - yield return new NamedEntity { EntityName = "Manufacturer", Id = x.Id, LastMod = x.UpdatedOnUtc }; - } - } - - if (queries.Topics != null) - { - var topics = queries.Topics.Select(x => new { x.Id }).ToList(); - foreach (var x in topics) - { - yield return new NamedEntity { EntityName = "Topic", Id = x.Id, LastMod = DateTime.UtcNow }; - } - } - - if (queries.Products != null) - { - var query = queries.Products.AsNoTracking(); - var maxId = int.MaxValue; - - //var limit = 0; - while (maxId > 1) - { - 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 }; - } - } - } - } - - private IDictionary GetUrlRecordCollectionsForBatch(IEnumerable batch, int[] languageIds) - { - var result = new Dictionary(); - - if (batch.First().EntityName == "Product") - { - // 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); - } - - 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); - } - - return result; - } - - protected virtual List GetSiteMapDocuments(IReadOnlyCollection nodes) - { - int siteMapCount = (int)Math.Ceiling(nodes.Count / (double)MaximumSiteMapNodeCount); - CheckSitemapCount(siteMapCount); - - var siteMaps = Enumerable - .Range(0, siteMapCount) - .Select(x => - { - return new KeyValuePair>( - x + 1, - nodes.Skip(x * MaximumSiteMapNodeCount).Take(MaximumSiteMapNodeCount)); - }); - - var siteMapDocuments = new List(siteMapCount); - - foreach (var kvp in siteMaps) - { - siteMapDocuments.Add(this.GetSitemapDocument(kvp.Value)); - } - - return siteMapDocuments; - } - - /// - /// Gets the sitemap XML document for the specified set of nodes. - /// - /// The sitemap nodes. - /// The sitemap XML document for the specified set of nodes. - private string GetSitemapDocument(IEnumerable nodes) - { - //var languages = _languageService.GetAllLanguages(); - - XNamespace ns = SiteMapsNamespace; - XNamespace xhtml = XhtmlNamespace; - - XElement root = new XElement( - ns + "urlset", - new XAttribute(XNamespace.Xmlns + "xhtml", xhtml)); - - foreach (var node in nodes) - { - // url - var xel = new XElement - ( - ns + "url", - // url/loc - new XElement(ns + "loc", node.Loc), - // url/lastmod - node.LastMod == null ? null : new XElement( - ns + "lastmod", - node.LastMod.Value.ToLocalTime().ToString("yyyy-MM-ddTHH:mm:sszzz")), - // url/changefreq - node.ChangeFreq == null ? null : new XElement( - ns + "changefreq", - node.ChangeFreq.Value.ToString().ToLowerInvariant()), - // url/priority - node.Priority == null ? null : new XElement( - ns + "priority", - node.Priority.Value.ToString("F1", CultureInfo.InvariantCulture)) - ); - - if (node.Links != null) - { - foreach (var entry in node.Links) - { - // url/xhtml:link[culture] - xel.Add(new XElement - ( - xhtml + "link", - new XAttribute("rel", "alternate"), - new XAttribute("hreflang", entry.Lang), - new XAttribute("href", entry.Href) - )); - } - } - - root.Add(xel); - } - - XDocument document = new XDocument(root); - var xml = document.ToString(SaveOptions.DisableFormatting); - CheckDocumentSize(xml); - - 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) - { - XNamespace ns = SiteMapsNamespace; - - XElement root = new XElement(ns + "sitemapindex"); - - foreach (var node in nodes) - { - 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 document = new XDocument(root); - var xml = document.ToString(SaveOptions.DisableFormatting); - CheckDocumentSize(xml); - - return xml; - } - - private string GetSitemapIndexUrl(int index, string baseUrl) - { - var url = _urlHelper.RouteUrl("XmlSitemap", new { index }).TrimStart('/'); - return baseUrl + url; - } - - private QueryHolder CreateQueries(XmlSitemapBuildContext ctx) - { - var storeId = ctx.Store.Id; - - if (_services.StoreService.IsSingleStoreMode()) - { - storeId = 0; - } - - // Always work with store-dependant setting - var seoSettings = _services.Settings.LoadSetting(storeId); - - var holder = new QueryHolder(); - - if (seoSettings.XmlSitemapIncludesCategories) - { - holder.Categories = _categoryService.BuildCategoriesQuery(showHidden: false, storeId: storeId); - } - - if (seoSettings.XmlSitemapIncludesManufacturers) - { - holder.Manufacturers = _manufacturerService.GetManufacturers(false).OrderBy(x => x.DisplayOrder).ThenBy(x => x.Name); - } - - if (seoSettings.XmlSitemapIncludesTopics) - { - holder.Topics = _topicService.GetAllTopics(storeId).AlterQuery(q => - { - return q.Where(t => t.IncludeInSitemap && !t.RenderAsWidget); - }).SourceQuery; - } - - if (seoSettings.XmlSitemapIncludesProducts) - { - var searchQuery = new CatalogSearchQuery() - .VisibleOnly(_services.WorkContext.CurrentCustomer) - .VisibleIndividuallyOnly(true) - .HasStoreId(storeId); - - holder.Products = _catalogSearchService.PrepareQuery(searchQuery); - } - - return holder; - } - - protected void ProcessCustomNodes(XmlSitemapBuildContext ctx, Multimap sitemaps) - { - } - - /// - /// Checks the size of the XML sitemap document. If it is over 10MB, logs an error. - /// - /// The sitemap XML document. - private void CheckDocumentSize(string siteMapXml) - { - if (siteMapXml.Length >= MaximumSiteMapSizeInBytes) - { - Logger.Error(new InvalidOperationException($"Sitemap exceeds the maximum size of 10MB. This is because you have unusually long URL's. Consider reducing the MaximumSitemapNodeCount. Size:<{siteMapXml.Length}>")); - } - } - - /// - /// Checks the count of the number of sitemaps. If it is over 50,000, logs an error. - /// - /// The sitemap count. - private void CheckSitemapCount(int sitemapCount) - { - if (sitemapCount > MaximumSiteMapCount) - { - var ex = new InvalidOperationException($"Sitemap index file exceeds the maximum number of allowed sitemaps of 50,000. Count:<{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 async Task GetTotalRecordCountAsync() - { - int num = 0; - if (Categories != null) num += await Categories.CountAsync(); - if (Manufacturers != null) num += await Manufacturers.CountAsync(); - if (Topics != null) num += await Topics.CountAsync(); - if (Products != null) num += await Products.CountAsync(); - //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 - } + 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; } + } + + #endregion + } } diff --git a/src/Libraries/SmartStore.Services/Seo/XmlSitemapNode.cs b/src/Libraries/SmartStore.Services/Seo/XmlSitemapNode.cs index a3d2f89f6b..30b35a9d27 100644 --- a/src/Libraries/SmartStore.Services/Seo/XmlSitemapNode.cs +++ b/src/Libraries/SmartStore.Services/Seo/XmlSitemapNode.cs @@ -6,53 +6,53 @@ namespace SmartStore.Services.Seo { - public class XmlSitemapNode - { - public string Loc { get; set; } - public DateTime? LastMod { get; set; } - public ChangeFrequency? ChangeFreq { get; set; } - public float? Priority { get; set; } - public IEnumerable Links { get; set; } + public class XmlSitemapNode + { + public string Loc { get; set; } + public DateTime? LastMod { get; set; } + public ChangeFrequency? ChangeFreq { get; set; } + public float? Priority { get; set; } + public IEnumerable Links { get; set; } - public class LinkEntry - { - public string Lang { get; set; } - public string Href { get; set; } - } - } + public class LinkEntry + { + public string Lang { get; set; } + public string Href { get; set; } + } + } - /// - /// Represents a sitemap update frequency - /// - public enum ChangeFrequency - { - /// - /// Always - /// - Always, - /// - /// Hourly - /// - Hourly, - /// - /// Daily - /// - Daily, - /// - /// Weekly - /// - Weekly, - /// - /// Monthly - /// - Monthly, - /// - /// Yearly - /// - Yearly, - /// - /// Never - /// - Never - } + /// + /// Represents a sitemap update frequency + /// + public enum ChangeFrequency + { + /// + /// Always + /// + Always, + /// + /// Hourly + /// + Hourly, + /// + /// Daily + /// + Daily, + /// + /// Weekly + /// + Weekly, + /// + /// Monthly + /// + Monthly, + /// + /// Yearly + /// + Yearly, + /// + /// Never + /// + Never + } } diff --git a/src/Libraries/SmartStore.Services/Seo/XmlSitemapPartition.cs b/src/Libraries/SmartStore.Services/Seo/XmlSitemapPartition.cs index ec5e519913..152e16b5c1 100644 --- a/src/Libraries/SmartStore.Services/Seo/XmlSitemapPartition.cs +++ b/src/Libraries/SmartStore.Services/Seo/XmlSitemapPartition.cs @@ -3,13 +3,13 @@ namespace SmartStore.Services.Seo { - public class XmlSitemapPartition - { - public string Name { get; set; } - public int Index { get; set; } - public int StoreId { get; set; } - public int LanguageId { get; set; } - public DateTime ModifiedOnUtc { get; set; } - public Stream Stream { get; set; } - } + public class XmlSitemapPartition + { + public string Name { get; set; } + public int Index { get; set; } + public int StoreId { get; set; } + public int LanguageId { get; set; } + public DateTime ModifiedOnUtc { get; set; } + public Stream Stream { get; set; } + } } diff --git a/src/Libraries/SmartStore.Services/ServiceCacheBuster.cs b/src/Libraries/SmartStore.Services/ServiceCacheBuster.cs index feaca72088..16dc5affa7 100644 --- a/src/Libraries/SmartStore.Services/ServiceCacheBuster.cs +++ b/src/Libraries/SmartStore.Services/ServiceCacheBuster.cs @@ -7,39 +7,39 @@ namespace SmartStore.Services { - public partial class ServiceCacheBuster : DbSaveHook - { - public const string STORE_LANGUAGE_MAP_KEY = "svc:storelangmap*"; + public partial class ServiceCacheBuster : DbSaveHook + { + public const string STORE_LANGUAGE_MAP_KEY = "svc:storelangmap*"; - private readonly ICacheManager _cacheManager; + private readonly ICacheManager _cacheManager; public ServiceCacheBuster(ICacheManager cacheManager) { - _cacheManager = cacheManager; + _cacheManager = cacheManager; } - protected override void OnInserted(BaseEntity entity, IHookedEntity entry) - { - if (entry.EntityType != typeof(Store) && entry.EntityType != typeof(Language)) - throw new NotImplementedException(); + protected override void OnInserted(BaseEntity entity, IHookedEntity entry) + { + if (entry.EntityType != typeof(Store) && entry.EntityType != typeof(Language)) + throw new NotSupportedException(); - _cacheManager.Remove(STORE_LANGUAGE_MAP_KEY); - } + _cacheManager.Remove(STORE_LANGUAGE_MAP_KEY); + } - protected override void OnDeleted(BaseEntity entity, IHookedEntity entry) - { - if (entry.EntityType != typeof(Store) && entry.EntityType != typeof(Language)) - throw new NotImplementedException(); + protected override void OnDeleted(BaseEntity entity, IHookedEntity entry) + { + if (entry.EntityType != typeof(Store) && entry.EntityType != typeof(Language)) + throw new NotImplementedException(); - _cacheManager.Remove(STORE_LANGUAGE_MAP_KEY); - } + _cacheManager.Remove(STORE_LANGUAGE_MAP_KEY); + } - protected override void OnUpdated(BaseEntity entity, IHookedEntity entry) - { - if (entry.EntityType != typeof(Language)) - throw new NotImplementedException(); + protected override void OnUpdated(BaseEntity entity, IHookedEntity entry) + { + if (entry.EntityType != typeof(Language)) + throw new NotImplementedException(); - _cacheManager.Remove(STORE_LANGUAGE_MAP_KEY); - } - } + _cacheManager.Remove(STORE_LANGUAGE_MAP_KEY); + } + } } diff --git a/src/Libraries/SmartStore.Services/Shipping/GetShippingOptionRequest.cs b/src/Libraries/SmartStore.Services/Shipping/GetShippingOptionRequest.cs index 3439b6fd4f..80af4d3a4c 100644 --- a/src/Libraries/SmartStore.Services/Shipping/GetShippingOptionRequest.cs +++ b/src/Libraries/SmartStore.Services/Shipping/GetShippingOptionRequest.cs @@ -16,10 +16,10 @@ public GetShippingOptionRequest() this.Items = new List(); } - /// - /// The context store identifier - /// - public int StoreId { get; set; } + /// + /// The context store identifier + /// + public int StoreId { get; set; } /// /// Gets or sets a customer diff --git a/src/Libraries/SmartStore.Services/Shipping/GetShippingOptionResponse.cs b/src/Libraries/SmartStore.Services/Shipping/GetShippingOptionResponse.cs index cbb2586fa5..eea7127b98 100644 --- a/src/Libraries/SmartStore.Services/Shipping/GetShippingOptionResponse.cs +++ b/src/Libraries/SmartStore.Services/Shipping/GetShippingOptionResponse.cs @@ -24,13 +24,7 @@ public GetShippingOptionResponse() /// public IList Errors { get; set; } - public bool Success - { - get - { - return this.Errors.Count == 0; - } - } + public bool Success => this.Errors.Count == 0; public void AddError(string error) { diff --git a/src/Libraries/SmartStore.Services/Shipping/IShipmentService.cs b/src/Libraries/SmartStore.Services/Shipping/IShipmentService.cs index 0a544651ad..db53312558 100644 --- a/src/Libraries/SmartStore.Services/Shipping/IShipmentService.cs +++ b/src/Libraries/SmartStore.Services/Shipping/IShipmentService.cs @@ -26,9 +26,9 @@ public partial interface IShipmentService /// Page index /// Page size /// Customer collection - IPagedList GetAllShipments(string trackingNumber, DateTime? createdFrom, DateTime? createdTo, + IPagedList GetAllShipments(string trackingNumber, DateTime? createdFrom, DateTime? createdTo, int pageIndex, int pageSize); - + /// /// Get shipments by identifiers /// @@ -36,12 +36,12 @@ IPagedList GetAllShipments(string trackingNumber, DateTime? createdFro /// Shipments IList GetShipmentsByIds(int[] shipmentIds); - /// - /// Get shipments by order identifiers - /// - /// Order identifiers - /// Shipments - Multimap GetShipmentsByOrderIds(int[] orderIds); + /// + /// Get shipments by order identifiers + /// + /// Order identifiers + /// Shipments + Multimap GetShipmentsByOrderIds(int[] orderIds); /// /// Gets a shipment diff --git a/src/Libraries/SmartStore.Services/Shipping/IShippingMethodFilter.cs b/src/Libraries/SmartStore.Services/Shipping/IShippingMethodFilter.cs deleted file mode 100644 index 2fbed519bc..0000000000 --- a/src/Libraries/SmartStore.Services/Shipping/IShippingMethodFilter.cs +++ /dev/null @@ -1,35 +0,0 @@ -using SmartStore.Core.Domain.Shipping; - -namespace SmartStore.Services.Shipping -{ - public partial interface IShippingMethodFilter - { - /// - /// Gets a value indicating whether a shipping method should be filtered out - /// - /// Shipping filter request - /// true filter out method, false do not filter out method - bool IsExcluded(ShippingFilterRequest request); - - /// - /// Get URL for filter configuration - /// - /// Shipping method identifier - /// URL for filter configuration - string GetConfigurationUrl(int shippingMethodId); - } - - - public partial class ShippingFilterRequest - { - /// - /// The shipping method to be checked - /// - public ShippingMethod ShippingMethod { get; set; } - - /// - /// Shipping method request - /// - public GetShippingOptionRequest Option { get; set; } - } -} diff --git a/src/Libraries/SmartStore.Services/Shipping/IShippingRateComputationMethod.cs b/src/Libraries/SmartStore.Services/Shipping/IShippingRateComputationMethod.cs index fd13e71b4c..52f1a590fb 100644 --- a/src/Libraries/SmartStore.Services/Shipping/IShippingRateComputationMethod.cs +++ b/src/Libraries/SmartStore.Services/Shipping/IShippingRateComputationMethod.cs @@ -33,9 +33,9 @@ public partial interface IShippingRateComputationMethod : IProvider, IUserEditab /// IShipmentTracker ShipmentTracker { get; } - /// - /// Gets a value indicating whether the shipping rate computation method is active and should be offered to customers - /// - bool IsActive { get; } + /// + /// Gets a value indicating whether the shipping rate computation method is active and should be offered to customers + /// + bool IsActive { get; } } } diff --git a/src/Libraries/SmartStore.Services/Shipping/IShippingService.cs b/src/Libraries/SmartStore.Services/Shipping/IShippingService.cs index 89672e6ec6..22a56abbda 100644 --- a/src/Libraries/SmartStore.Services/Shipping/IShippingService.cs +++ b/src/Libraries/SmartStore.Services/Shipping/IShippingService.cs @@ -6,10 +6,10 @@ namespace SmartStore.Services.Shipping { - /// - /// Shipping service interface - /// - public partial interface IShippingService + /// + /// Shipping service interface + /// + public partial interface IShippingService { /// /// Load active shipping rate computation methods @@ -33,8 +33,6 @@ public partial interface IShippingService IEnumerable> LoadAllShippingRateComputationMethods(int storeId = 0); - - /// /// Deletes a shipping method /// @@ -49,13 +47,13 @@ public partial interface IShippingService ShippingMethod GetShippingMethodById(int shippingMethodId); - /// - /// Gets all shipping methods - /// - /// Shipping option request to filter out shipping methods. null to load all shipping methods. - /// Whether to filter methods by store identifier. - /// Shipping method collection - IList GetAllShippingMethods(GetShippingOptionRequest request = null, int storeId = 0); + /// + /// Gets all shipping methods + /// + /// Shipping option request to filter out shipping methods. null to load all shipping methods. + /// Whether to filter methods by store identifier. + /// Shipping method collection + IList GetAllShippingMethods(GetShippingOptionRequest request = null, int storeId = 0); /// /// Inserts a shipping method @@ -92,15 +90,15 @@ public partial interface IShippingService /// Shopping cart weight decimal GetShoppingCartTotalWeight(IList cart, bool includeFreeShippingProducts = true); - + /// /// Create shipment package from shopping cart /// /// Shopping cart /// Shipping address - /// Store identifier + /// Store identifier /// Shipment package - GetShippingOptionRequest CreateShippingOptionRequest(IList cart, Address shippingAddress, int storeId); + GetShippingOptionRequest CreateShippingOptionRequest(IList cart, Address shippingAddress, int storeId); /// /// Gets available shipping options @@ -111,12 +109,6 @@ public partial interface IShippingService /// Load records allows only in specified store; pass 0 to load all records /// Shipping options GetShippingOptionResponse GetShippingOptions(IList cart, Address shippingAddress, - string allowedShippingRateComputationMethodSystemName = "", int storeId = 0); - - /// - /// Gets all shipping method filters - /// - /// List of shipping method filters - IList GetAllShippingMethodFilters(); + string allowedShippingRateComputationMethodSystemName = "", int storeId = 0); } } diff --git a/src/Libraries/SmartStore.Services/Shipping/ShipmentService.cs b/src/Libraries/SmartStore.Services/Shipping/ShipmentService.cs index 8cd9b39030..18fff61b17 100644 --- a/src/Libraries/SmartStore.Services/Shipping/ShipmentService.cs +++ b/src/Libraries/SmartStore.Services/Shipping/ShipmentService.cs @@ -22,7 +22,7 @@ public partial class ShipmentService : IShipmentService private readonly IRepository _siRepository; private readonly IRepository _orderRepository; private readonly IEventPublisher _eventPublisher; - + #endregion #region Ctor @@ -58,32 +58,32 @@ public virtual void DeleteShipment(Shipment shipment) if (shipment == null) throw new ArgumentNullException("shipment"); - int orderId = shipment.OrderId; + int orderId = shipment.OrderId; _shipmentRepository.Delete(shipment); - if (orderId != 0) - { - var order = _orderRepository.GetById(orderId); - _eventPublisher.PublishOrderUpdated(order); - } + if (orderId != 0) + { + var order = _orderRepository.GetById(orderId); + _eventPublisher.PublishOrderUpdated(order); + } } - + /// /// Search shipments /// - /// Search by tracking number + /// Search by tracking number /// Created date from; null to load all records /// Created date to; null to load all records /// Page index /// Page size /// Customer collection - public virtual IPagedList GetAllShipments(string trackingNumber, DateTime? createdFrom, DateTime? createdTo, + public virtual IPagedList GetAllShipments(string trackingNumber, DateTime? createdFrom, DateTime? createdTo, int pageIndex, int pageSize) { var query = _shipmentRepository.Table.Expand(x => x.Order); - if (!String.IsNullOrEmpty(trackingNumber)) - query = query.Where(s => s.TrackingNumber.Contains(trackingNumber)); + if (!String.IsNullOrEmpty(trackingNumber)) + query = query.Where(s => s.TrackingNumber.Contains(trackingNumber)); if (createdFrom.HasValue) query = query.Where(s => createdFrom.Value <= s.CreatedOnUtc); if (createdTo.HasValue) @@ -105,33 +105,33 @@ public virtual IList GetShipmentsByIds(int[] shipmentIds) if (shipmentIds == null || shipmentIds.Length == 0) return new List(); - var query = from o in _shipmentRepository.Table.Expand(x => x.Order) + var query = from o in _shipmentRepository.Table.Expand(x => x.Order) where shipmentIds.Contains(o.Id) select o; var shipments = query.ToList(); - // sort by passed identifier sequence - return shipments.OrderBySequence(shipmentIds).ToList(); - } + // sort by passed identifier sequence + return shipments.OrderBySequence(shipmentIds).ToList(); + } - public virtual Multimap GetShipmentsByOrderIds(int[] orderIds) - { - Guard.NotNull(orderIds, nameof(orderIds)); + public virtual Multimap GetShipmentsByOrderIds(int[] orderIds) + { + Guard.NotNull(orderIds, nameof(orderIds)); - var query = - from x in _shipmentRepository.TableUntracked.Expand(x => x.ShipmentItems) - where orderIds.Contains(x.OrderId) - select x; + var query = + from x in _shipmentRepository.TableUntracked.Expand(x => x.ShipmentItems) + where orderIds.Contains(x.OrderId) + select x; - var map = query - .OrderBy(x => x.OrderId) - .ThenBy(x => x.CreatedOnUtc) - .ToList() - .ToMultimap(x => x.OrderId, x => x); + var map = query + .OrderBy(x => x.OrderId) + .ThenBy(x => x.CreatedOnUtc) + .ToList() + .ToMultimap(x => x.OrderId, x => x); - return map; - } + return map; + } /// /// Gets a shipment @@ -159,7 +159,7 @@ public virtual void InsertShipment(Shipment shipment) _shipmentRepository.Insert(shipment); //event notification - _eventPublisher.PublishOrderUpdated(shipment.Order); + _eventPublisher.PublishOrderUpdated(shipment.Order); } /// @@ -174,11 +174,11 @@ public virtual void UpdateShipment(Shipment shipment) _shipmentRepository.Update(shipment); //event notification - _eventPublisher.PublishOrderUpdated(shipment.Order); + _eventPublisher.PublishOrderUpdated(shipment.Order); } - + /// /// Deletes a shipment item /// @@ -186,17 +186,17 @@ public virtual void UpdateShipment(Shipment shipment) public virtual void DeleteShipmentItem(ShipmentItem shipmentItem) { if (shipmentItem == null) - throw new ArgumentNullException("shipmentItem"); + throw new ArgumentNullException("shipmentItem"); - int orderId = shipmentItem.Shipment.OrderId; + int orderId = shipmentItem.Shipment.OrderId; _siRepository.Delete(shipmentItem); - if (orderId != 0) - { - var order = _orderRepository.GetById(orderId); - _eventPublisher.PublishOrderUpdated(order); - } + if (orderId != 0) + { + var order = _orderRepository.GetById(orderId); + _eventPublisher.PublishOrderUpdated(order); + } } /// @@ -212,7 +212,7 @@ public virtual ShipmentItem GetShipmentItemById(int shipmentItemId) var si = _siRepository.GetById(shipmentItemId); return si; } - + /// /// Inserts a shipment item /// @@ -220,23 +220,23 @@ public virtual ShipmentItem GetShipmentItemById(int shipmentItemId) public virtual void InsertShipmentItem(ShipmentItem shipmentItem) { if (shipmentItem == null) - throw new ArgumentNullException("shipmentItem"); + throw new ArgumentNullException("shipmentItem"); _siRepository.Insert(shipmentItem); - if (shipmentItem.Shipment != null && shipmentItem.Shipment.Order != null) - { - _eventPublisher.PublishOrderUpdated(shipmentItem.Shipment.Order); - } - else - { - var shipment = _shipmentRepository.Table - .Expand(x => x.Order) - .FirstOrDefault(x => x.Id == shipmentItem.ShipmentId); - - if (shipment != null) - _eventPublisher.PublishOrderUpdated(shipment.Order); - } + if (shipmentItem.Shipment != null && shipmentItem.Shipment.Order != null) + { + _eventPublisher.PublishOrderUpdated(shipmentItem.Shipment.Order); + } + else + { + var shipment = _shipmentRepository.Table + .Expand(x => x.Order) + .FirstOrDefault(x => x.Id == shipmentItem.ShipmentId); + + if (shipment != null) + _eventPublisher.PublishOrderUpdated(shipment.Order); + } } /// @@ -246,25 +246,25 @@ public virtual void InsertShipmentItem(ShipmentItem shipmentItem) public virtual void UpdateShipmentItem(ShipmentItem shipmentItem) { if (shipmentItem == null) - throw new ArgumentNullException("shipmentItem"); + throw new ArgumentNullException("shipmentItem"); _siRepository.Update(shipmentItem); - if (shipmentItem.Shipment != null && shipmentItem.Shipment.Order != null) - { - _eventPublisher.PublishOrderUpdated(shipmentItem.Shipment.Order); - } - else - { - var shipment = _shipmentRepository.Table - .Expand(x => x.Order) - .FirstOrDefault(x => x.Id == shipmentItem.ShipmentId); - - if (shipment != null) - _eventPublisher.PublishOrderUpdated(shipment.Order); - } + if (shipmentItem.Shipment != null && shipmentItem.Shipment.Order != null) + { + _eventPublisher.PublishOrderUpdated(shipmentItem.Shipment.Order); + } + else + { + var shipment = _shipmentRepository.Table + .Expand(x => x.Order) + .FirstOrDefault(x => x.Id == shipmentItem.ShipmentId); + + if (shipment != null) + _eventPublisher.PublishOrderUpdated(shipment.Order); + } } - - #endregion + + #endregion } } diff --git a/src/Libraries/SmartStore.Services/Shipping/ShippingExtentions.cs b/src/Libraries/SmartStore.Services/Shipping/ShippingExtentions.cs index b83fdc68ee..832354ccfa 100644 --- a/src/Libraries/SmartStore.Services/Shipping/ShippingExtentions.cs +++ b/src/Libraries/SmartStore.Services/Shipping/ShippingExtentions.cs @@ -5,7 +5,7 @@ namespace SmartStore.Services.Shipping { - public static class ShippingExtentions + public static class ShippingExtentions { public static bool IsShippingRateComputationMethodActive(this Provider srcm, ShippingSettings shippingSettings) { @@ -18,10 +18,10 @@ public static bool IsShippingRateComputationMethodActive(this Provider _shippingMethodFilterTypes = null; - - private readonly IRepository _shippingMethodRepository; - private readonly IRepository _storeMappingRepository; - private readonly IProductAttributeParser _productAttributeParser; - private readonly IProductService _productService; + private readonly IRepository _shippingMethodRepository; + private readonly IRepository _storeMappingRepository; + private readonly IProductAttributeParser _productAttributeParser; + private readonly IProductService _productService; private readonly ICheckoutAttributeParser _checkoutAttributeParser; - private readonly IGenericAttributeService _genericAttributeService; + private readonly IGenericAttributeService _genericAttributeService; private readonly ShippingSettings _shippingSettings; - private readonly IEventPublisher _eventPublisher; - private readonly ShoppingCartSettings _shoppingCartSettings; - private readonly ISettingService _settingService; - private readonly IProviderManager _providerManager; - private readonly ITypeFinder _typeFinder; - private readonly ICommonServices _services; + private readonly ISettingService _settingService; + private readonly IProviderManager _providerManager; + private readonly ICommonServices _services; + private readonly ICartRuleProvider _cartRuleProvider; public ShippingService( IRepository shippingMethodRepository, - IRepository storeMappingRepository, - IProductAttributeParser productAttributeParser, - IProductService productService, + IRepository storeMappingRepository, + IProductAttributeParser productAttributeParser, + IProductService productService, ICheckoutAttributeParser checkoutAttributeParser, - IGenericAttributeService genericAttributeService, + IGenericAttributeService genericAttributeService, ShippingSettings shippingSettings, - IEventPublisher eventPublisher, - ShoppingCartSettings shoppingCartSettings, - ISettingService settingService, - IProviderManager providerManager, - ITypeFinder typeFinder, - ICommonServices services) + ISettingService settingService, + IProviderManager providerManager, + ICommonServices services, + ICartRuleProvider cartRuleProvider) { _shippingMethodRepository = shippingMethodRepository; - _storeMappingRepository = storeMappingRepository; + _storeMappingRepository = storeMappingRepository; _productAttributeParser = productAttributeParser; - _productService = productService; + _productService = productService; _checkoutAttributeParser = checkoutAttributeParser; - _genericAttributeService = genericAttributeService; + _genericAttributeService = genericAttributeService; _shippingSettings = shippingSettings; - _eventPublisher = eventPublisher; - _shoppingCartSettings = shoppingCartSettings; - _settingService = settingService; - _providerManager = providerManager; - _typeFinder = typeFinder; - _services = services; - - T = NullLocalizer.Instance; - Logger = NullLogger.Instance; - QuerySettings = DbQuerySettings.Default; - } - - public Localizer T { get; set; } - public ILogger Logger { get; set; } - public DbQuerySettings QuerySettings { get; set; } - - #region Shipping rate computation methods - - /// - /// Load active shipping rate computation methods - /// - /// Load records allows only in specified store; pass 0 to load all records - /// Shipping rate computation methods - public virtual IEnumerable> LoadActiveShippingRateComputationMethods(int storeId = 0) + _settingService = settingService; + _providerManager = providerManager; + _services = services; + _cartRuleProvider = cartRuleProvider; + + T = NullLocalizer.Instance; + Logger = NullLogger.Instance; + QuerySettings = DbQuerySettings.Default; + } + + public Localizer T { get; set; } + public ILogger Logger { get; set; } + public DbQuerySettings QuerySettings { get; set; } + + #region Shipping rate computation methods + + /// + /// Load active shipping rate computation methods + /// + /// Load records allows only in specified store; pass 0 to load all records + /// Shipping rate computation methods + public virtual IEnumerable> LoadActiveShippingRateComputationMethods(int storeId = 0) { - var allMethods = LoadAllShippingRateComputationMethods(storeId); - - var activeMethods = allMethods - .Where(p => p.Value.IsActive && _shippingSettings.ActiveShippingRateComputationMethodSystemNames.Contains(p.Metadata.SystemName, StringComparer.InvariantCultureIgnoreCase)); - - if (!activeMethods.Any()) - { - var fallbackMethod = allMethods.FirstOrDefault(x => x.IsShippingRateComputationMethodActive(_shippingSettings)); - - if (fallbackMethod == null) - fallbackMethod = allMethods.FirstOrDefault(); - - if (fallbackMethod != null) - { - _shippingSettings.ActiveShippingRateComputationMethodSystemNames.Clear(); - _shippingSettings.ActiveShippingRateComputationMethodSystemNames.Add(fallbackMethod.Metadata.SystemName); - _settingService.SaveSetting(_shippingSettings); - - return new Provider[] { fallbackMethod }; - } - else - { - if (DataSettings.DatabaseIsInstalled()) - throw new SmartException(T("Shipping.OneActiveMethodProviderRequired")); - } - } - - return activeMethods; + var allMethods = LoadAllShippingRateComputationMethods(storeId); + + var activeMethods = allMethods + .Where(p => p.Value.IsActive && _shippingSettings.ActiveShippingRateComputationMethodSystemNames.Contains(p.Metadata.SystemName, StringComparer.InvariantCultureIgnoreCase)); + + if (!activeMethods.Any()) + { + var fallbackMethod = allMethods.FirstOrDefault(x => x.IsShippingRateComputationMethodActive(_shippingSettings)); + + if (fallbackMethod == null) + fallbackMethod = allMethods.FirstOrDefault(); + + if (fallbackMethod != null) + { + _shippingSettings.ActiveShippingRateComputationMethodSystemNames.Clear(); + _shippingSettings.ActiveShippingRateComputationMethodSystemNames.Add(fallbackMethod.Metadata.SystemName); + _settingService.SaveSetting(_shippingSettings); + + return new Provider[] { fallbackMethod }; + } + else + { + if (DataSettings.DatabaseIsInstalled()) + throw new SmartException(T("Shipping.OneActiveMethodProviderRequired")); + } + } + + return activeMethods; } /// @@ -123,7 +113,7 @@ public virtual IEnumerable> LoadActiveS /// Found Shipping rate computation method public virtual Provider LoadShippingRateComputationMethodBySystemName(string systemName, int storeId = 0) { - return _providerManager.GetProvider(systemName, storeId); + return _providerManager.GetProvider(systemName, storeId); } /// @@ -133,7 +123,7 @@ public virtual Provider LoadShippingRateComputat /// Shipping rate computation methods public virtual IEnumerable> LoadAllShippingRateComputationMethods(int storeId = 0) { - return _providerManager.GetAllProviders(storeId); + return _providerManager.GetAllProviders(storeId); } #endregion @@ -166,54 +156,48 @@ public virtual ShippingMethod GetShippingMethodById(int shippingMethodId) return _shippingMethodRepository.GetById(shippingMethodId); } - public virtual IList GetAllShippingMethods(GetShippingOptionRequest request = null, int storeId = 0) + public virtual IList GetAllShippingMethods(GetShippingOptionRequest request = null, int storeId = 0) { - var query = - from sm in _shippingMethodRepository.Table - select sm; - - if (!QuerySettings.IgnoreMultiStore && storeId > 0) - { - query = - from x in query - join sm in _storeMappingRepository.TableUntracked - on new { c1 = x.Id, c2 = "ShippingMethod" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into x_sm - from sm in x_sm.DefaultIfEmpty() - where !x.LimitedToStores || storeId == sm.StoreId - select x; - - query = - from x in query - group x by x.Id into grp - orderby grp.Key - select grp.FirstOrDefault(); - } - - var allMethods = query.OrderBy(x => x.DisplayOrder).ToList(); - - if (request == null) - { - return allMethods; - } - - IList allFilters = null; - var filterRequest = new ShippingFilterRequest { Option = request }; - - var activeShippingMethods = allMethods.Where(s => - { - // Shipping method filtering. - if (allFilters == null) - allFilters = GetAllShippingMethodFilters(); - - filterRequest.ShippingMethod = s; - - if (allFilters.Any(x => x.IsExcluded(filterRequest))) - return false; - - return true; - }); - - return activeShippingMethods.ToList(); + var query = + from sm in _shippingMethodRepository.Table + select sm; + + if (!QuerySettings.IgnoreMultiStore && storeId > 0) + { + query = + from x in query + join sm in _storeMappingRepository.TableUntracked + on new { c1 = x.Id, c2 = "ShippingMethod" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into x_sm + from sm in x_sm.DefaultIfEmpty() + where !x.LimitedToStores || storeId == sm.StoreId + select x; + + query = + from x in query + group x by x.Id into grp + orderby grp.Key + select grp.FirstOrDefault(); + } + + var allMethods = query.OrderBy(x => x.DisplayOrder).ToList(); + + if (request == null) + { + return allMethods; + } + + var activeShippingMethods = allMethods.Where(s => + { + // Rule sets. + if (!_cartRuleProvider.RuleMatches(s)) + { + return false; + } + + return true; + }); + + return activeShippingMethods.ToList(); } /// @@ -244,7 +228,7 @@ public virtual void UpdateShippingMethod(ShippingMethod shippingMethod) #region Workflow - public virtual decimal GetShoppingCartItemWeight(OrganizedShoppingCartItem shoppingCartItem) + public virtual decimal GetShoppingCartItemWeight(OrganizedShoppingCartItem shoppingCartItem) { if (shoppingCartItem == null) throw new ArgumentNullException("shoppingCartItem"); @@ -259,19 +243,19 @@ public virtual decimal GetShoppingCartItemWeight(OrganizedShoppingCartItem shopp { var pvaValues = _productAttributeParser.ParseProductVariantAttributeValues(shoppingCartItem.Item.AttributesXml).ToList(); - foreach (var pvaValue in pvaValues) - { - if (pvaValue.ValueType == ProductVariantAttributeValueType.ProductLinkage) - { - var linkedProduct = _productService.GetProductById(pvaValue.LinkedProductId); - if (linkedProduct != null && linkedProduct.IsShipEnabled) - attributesTotalWeight += (linkedProduct.Weight * pvaValue.Quantity); - } - else - { - attributesTotalWeight += pvaValue.WeightAdjustment; - } - } + foreach (var pvaValue in pvaValues) + { + if (pvaValue.ValueType == ProductVariantAttributeValueType.ProductLinkage) + { + var linkedProduct = _productService.GetProductById(pvaValue.LinkedProductId); + if (linkedProduct != null && linkedProduct.IsShipEnabled) + attributesTotalWeight += (linkedProduct.Weight * pvaValue.Quantity); + } + else + { + attributesTotalWeight += pvaValue.WeightAdjustment; + } + } } weight = shoppingCartItem.Item.Product.Weight + attributesTotalWeight; @@ -279,7 +263,7 @@ public virtual decimal GetShoppingCartItemWeight(OrganizedShoppingCartItem shopp return weight; } - public virtual decimal GetShoppingCartItemTotalWeight(OrganizedShoppingCartItem shoppingCartItem) + public virtual decimal GetShoppingCartItemTotalWeight(OrganizedShoppingCartItem shoppingCartItem) { if (shoppingCartItem == null) throw new ArgumentNullException("shoppingCartItem"); @@ -288,84 +272,84 @@ public virtual decimal GetShoppingCartItemTotalWeight(OrganizedShoppingCartItem return totalWeight; } - public virtual decimal GetShoppingCartTotalWeight(IList cart, bool includeFreeShippingProducts = true) + public virtual decimal GetShoppingCartTotalWeight(IList cart, bool includeFreeShippingProducts = true) { - var totalWeight = decimal.Zero; - var customer = cart.GetCustomer(); - - // shopping cart items - foreach (var cartItem in cart) - { - var product = cartItem.Item.Product; - if (product != null) - { - if (!includeFreeShippingProducts && product.IsFreeShipping) - { - // skip product - } - else - { - totalWeight += GetShoppingCartItemTotalWeight(cartItem); - } - } - } + var totalWeight = decimal.Zero; + var customer = cart.GetCustomer(); + + // shopping cart items + foreach (var cartItem in cart) + { + var product = cartItem.Item.Product; + if (product != null) + { + if (!includeFreeShippingProducts && product.IsFreeShipping) + { + // skip product + } + else + { + totalWeight += GetShoppingCartItemTotalWeight(cartItem); + } + } + } // checkout attributes if (customer != null) { - var checkoutAttributesXml = customer.GetAttribute(SystemCustomerAttributeNames.CheckoutAttributes, _genericAttributeService); - if (!String.IsNullOrEmpty(checkoutAttributesXml)) - { - var caValues = _checkoutAttributeParser.ParseCheckoutAttributeValues(checkoutAttributesXml); - foreach (var caValue in caValues) - { - totalWeight += caValue.WeightAdjustment; - } - } + var checkoutAttributesXml = customer.GetAttribute(SystemCustomerAttributeNames.CheckoutAttributes, _genericAttributeService); + if (!String.IsNullOrEmpty(checkoutAttributesXml)) + { + var caValues = _checkoutAttributeParser.ParseCheckoutAttributeValues(checkoutAttributesXml); + foreach (var caValue in caValues) + { + totalWeight += caValue.WeightAdjustment; + } + } } return totalWeight; } - public virtual GetShippingOptionRequest CreateShippingOptionRequest(IList cart, Address shippingAddress, int storeId) + public virtual GetShippingOptionRequest CreateShippingOptionRequest(IList cart, Address shippingAddress, int storeId) { var request = new GetShippingOptionRequest(); - request.StoreId = storeId; + request.StoreId = storeId; request.Customer = cart.GetCustomer(); request.ShippingAddress = shippingAddress; request.CountryFrom = null; request.StateProvinceFrom = null; request.ZipPostalCodeFrom = string.Empty; - request.Items = new List(); + request.Items = new List(); - foreach (var sc in cart) - { - if (sc.Item.IsShipEnabled) - request.Items.Add(sc); - } + foreach (var sc in cart) + { + if (sc.Item.IsShipEnabled) + request.Items.Add(sc); + } return request; } - public virtual GetShippingOptionResponse GetShippingOptions( - IList cart, - Address shippingAddress, - string allowedShippingRateComputationMethodSystemName = "", - int storeId = 0) + public virtual GetShippingOptionResponse GetShippingOptions( + IList cart, + Address shippingAddress, + string allowedShippingRateComputationMethodSystemName = "", + int storeId = 0) { if (cart == null) throw new ArgumentNullException("cart"); var result = new GetShippingOptionResponse(); - + //create a package var getShippingOptionRequest = CreateShippingOptionRequest(cart, shippingAddress, storeId); var shippingRateComputationMethods = LoadActiveShippingRateComputationMethods(storeId) - .Where(srcm => - String.IsNullOrWhiteSpace(allowedShippingRateComputationMethodSystemName) || + .Where(srcm => + String.IsNullOrWhiteSpace(allowedShippingRateComputationMethodSystemName) || allowedShippingRateComputationMethodSystemName.Equals(srcm.Metadata.SystemName, StringComparison.InvariantCultureIgnoreCase)) .ToList(); @@ -388,15 +372,15 @@ public virtual GetShippingOptionResponse GetShippingOptions( //log errors if (!getShippingOptionResponse.Success) { - var hasItemsToShip = getShippingOptionRequest.Items != null && getShippingOptionRequest.Items.Count > 0; + var hasItemsToShip = getShippingOptionRequest.Items != null && getShippingOptionRequest.Items.Count > 0; - foreach (string error in getShippingOptionResponse.Errors) + foreach (string error in getShippingOptionResponse.Errors) { result.AddError(error); - if (hasItemsToShip) - { - Logger.Warn(error); - } + if (hasItemsToShip) + { + Logger.Warn(error); + } } } } @@ -408,34 +392,13 @@ public virtual GetShippingOptionResponse GetShippingOptions( result.Errors.Clear(); } - //no shipping options loaded - if (result.ShippingOptions.Count == 0 && result.Errors.Count == 0) - { - result.Errors.Add(T("Checkout.ShippingOptionCouldNotBeLoaded")); - } - - return result; - } - - public virtual IList GetAllShippingMethodFilters() - { - if (_shippingMethodFilterTypes == null) - { - lock (_lock) - { - if (_shippingMethodFilterTypes == null) - { - _shippingMethodFilterTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true) - .ToList(); - } - } + //no shipping options loaded + if (result.ShippingOptions.Count == 0 && result.Errors.Count == 0) + { + result.Errors.Add(T("Checkout.ShippingOptionCouldNotBeLoaded")); } - var shippingMethodFilters = _shippingMethodFilterTypes - .Select(x => EngineContext.Current.ContainerManager.ResolveUnregistered(x) as IShippingMethodFilter) - .ToList(); - - return shippingMethodFilters; + return result; } #endregion diff --git a/src/Libraries/SmartStore.Services/Shipping/Tracking/GeneralShipmentTracker.cs b/src/Libraries/SmartStore.Services/Shipping/Tracking/GeneralShipmentTracker.cs index 33713acfbf..2bb952546a 100644 --- a/src/Libraries/SmartStore.Services/Shipping/Tracking/GeneralShipmentTracker.cs +++ b/src/Libraries/SmartStore.Services/Shipping/Tracking/GeneralShipmentTracker.cs @@ -18,7 +18,7 @@ public partial class GeneralShipmentTracker : IShipmentTracker private readonly ITypeFinder _typeFinder; #endregion - + #region Ctor /// @@ -31,7 +31,7 @@ public GeneralShipmentTracker(ITypeFinder typeFinder) } #endregion - + #region Utilities /// @@ -40,7 +40,7 @@ public GeneralShipmentTracker(ITypeFinder typeFinder) /// All available shipment trackers protected virtual IList GetAllTrackers() { - return _typeFinder.FindClassesOfType(ignoreInactivePlugins: true) + return _typeFinder.FindClassesOfType(ignoreInactivePlugins: true) //exclude this one .Where(x => x != typeof(GeneralShipmentTracker)) .Select(x => EngineContext.Current.ContainerManager.ResolveUnregistered(x) as IShipmentTracker) @@ -51,9 +51,9 @@ protected virtual IShipmentTracker GetTrackerByTrackingNumber(string trackingNum { return GetAllTrackers().FirstOrDefault(c => c.IsMatch(trackingNumber)); } - + #endregion - + #region Methods /// diff --git a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj index 51d1cdebf6..377945466a 100644 --- a/src/Libraries/SmartStore.Services/SmartStore.Services.csproj +++ b/src/Libraries/SmartStore.Services/SmartStore.Services.csproj @@ -1,6 +1,5 @@  - Debug AnyCPU @@ -11,7 +10,7 @@ Properties SmartStore.Services SmartStore.Services - v4.6.1 + v4.7.2 512 @@ -70,39 +69,43 @@ ..\..\packages\AngleSharp.0.9.11\lib\net45\AngleSharp.dll - - ..\..\packages\Autofac.4.9.0\lib\net45\Autofac.dll + + ..\..\packages\Autofac.5.2.0\lib\net461\Autofac.dll - - ..\..\packages\Autofac.Mvc5.4.0.2\lib\net45\Autofac.Integration.Mvc.dll - True + + ..\..\packages\Autofac.Mvc5.5.0.0\lib\net461\Autofac.Integration.Mvc.dll - - ..\..\packages\CronExpressionDescriptor.1.21.2\lib\net45\CronExpressionDescriptor.dll - True + + ..\..\packages\CronExpressionDescriptor.2.15.0\lib\netstandard2.0\CronExpressionDescriptor.dll - ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + ..\..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll True - ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + ..\..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.SqlServer.dll True ..\..\packages\EPPlus.4.5.3.1\lib\net40\EPPlus.dll - - ..\..\packages\ImageProcessor.2.7.0.100\lib\net452\ImageProcessor.dll + + ..\..\packages\ImageProcessor.2.8.0\lib\net452\ImageProcessor.dll + + + ..\..\packages\ImageProcessor.Plugins.WebP.1.3.0\lib\net452\ImageProcessor.Plugins.WebP.dll ..\..\packages\LumenWorksCsvReader.4.0.0\lib\net461\LumenWorks.Framework.IO.dll - ..\..\packages\MaxMind.Db.2.4.0\lib\net45\MaxMind.Db.dll + ..\..\packages\MaxMind.Db.2.6.1\lib\net45\MaxMind.Db.dll - ..\..\packages\MaxMind.GeoIP2.3.0.0\lib\net45\MaxMind.GeoIP2.dll + ..\..\packages\MaxMind.GeoIP2.3.2.0\lib\net45\MaxMind.GeoIP2.dll + + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll @@ -116,12 +119,11 @@ ..\..\packages\ncrontab.3.3.0\lib\net35\NCrontab.dll True - - ..\..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll - - ..\..\packages\NReco.PdfGenerator.1.1.15\lib\net20\NReco.PdfGenerator.dll - True + + ..\..\packages\NReco.PdfGenerator.1.2.0\lib\net45\NReco.PdfGenerator.dll ..\..\packages\NuGet.Core.2.14.0\lib\net40-Client\NuGet.Core.dll @@ -138,15 +140,18 @@ - - ..\..\packages\System.Linq.Dynamic.1.0.7\lib\net40\System.Linq.Dynamic.dll - True + + ..\..\packages\System.Linq.Dynamic.Core.1.2.1\lib\net46\System.Linq.Dynamic.Core.dll + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + - - ..\..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll @@ -172,9 +177,8 @@ - - ..\..\packages\UAParser.2.1.0.0\lib\net40-Client\UAParser.dll - True + + ..\..\packages\UAParser.3.1.44\lib\net45\UAParser.dll @@ -185,7 +189,34 @@ Properties\AssemblyVersionInfo.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -201,6 +232,11 @@ + + + + + @@ -226,14 +262,18 @@ + + + + @@ -303,35 +343,92 @@ - + + + + + + + + + + + + + + + + + - - - + + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -342,6 +439,7 @@ + @@ -361,6 +459,7 @@ + @@ -426,12 +525,12 @@ - + - + @@ -439,7 +538,7 @@ - + @@ -448,23 +547,18 @@ - - - - - - - + + + + - - @@ -489,8 +583,6 @@ - - @@ -567,6 +659,7 @@ + @@ -602,14 +695,15 @@ - - - + + + + @@ -620,7 +714,6 @@ - @@ -637,7 +730,6 @@ - @@ -647,6 +739,7 @@ + @@ -678,7 +771,7 @@ - + True True Reference.map @@ -706,32 +799,30 @@ SettingsSingleFileGenerator Settings.Designer.cs - - Designer - - + + Reference.map - + MSDiscoCodeGenerator Reference.cs - + Dynamic - Web References\EuropaCheckVatService\ - http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl + Web References\EuropeCheckVatService\ + https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl Settings - Nop_Services_EuropaCheckVatService_checkVatService + SmartStore_Services_EuropeCheckVatService_checkVatService - + diff --git a/src/Libraries/SmartStore.Services/Stores/IStoreMappingService.cs b/src/Libraries/SmartStore.Services/Stores/IStoreMappingService.cs index 02e9f61346..ccf1a4584d 100644 --- a/src/Libraries/SmartStore.Services/Stores/IStoreMappingService.cs +++ b/src/Libraries/SmartStore.Services/Stores/IStoreMappingService.cs @@ -6,143 +6,143 @@ namespace SmartStore.Services.Stores { - /// - /// Store mapping service interface - /// - public partial interface IStoreMappingService - { - /// - /// Deletes a store mapping record - /// - /// Store mapping record - void DeleteStoreMapping(StoreMapping storeMapping); - - /// - /// Gets a store mapping record - /// - /// Store mapping record identifier - /// Store mapping record - StoreMapping GetStoreMappingById(int storeMappingId); - - /// - /// Gets store mapping records - /// - /// Type - /// Entity - /// Store mapping records - IList GetStoreMappings(T entity) where T : BaseEntity, IStoreMappingSupported; - - /// - /// Gets store mapping records - /// - /// Could be null - /// Could be 0 - /// Store mapping record query - IQueryable GetStoreMappingsFor(string entityName, int entityId); - - /// - /// Save the store mapping for an entity - /// - /// Entity type - /// The entity - /// Array of selected store ids - void SaveStoreMappings(T entity, int[] selectedStoreIds) where T : BaseEntity, IStoreMappingSupported; - - /// - /// Inserts a store mapping record - /// - /// Store mapping - void InsertStoreMapping(StoreMapping storeMapping); - - /// - /// Inserts a store mapping record - /// - /// Type - /// Store id - /// Entity - void InsertStoreMapping(T entity, int storeId) where T : BaseEntity, IStoreMappingSupported; - - /// - /// Updates the store mapping record - /// - /// Store mapping - void UpdateStoreMapping(StoreMapping storeMapping); - - /// - /// Find store identifiers with granted access (mapped to the entity) - /// - /// Entity name to check - /// Entity id to check - /// Store identifiers - int[] GetStoresIdsWithAccess(string entityName, int entityId); - - /// - /// Checks whether an entity can be accessed in a store (mapped to this store) - /// - /// Entity name to check - /// Entity id to check - /// true - authorized; otherwise, false - bool Authorize(string entityName, int entityId); - - /// - /// Checks whether an entity can be accessed in a store (mapped to this store) - /// - /// Entity name to check - /// Entity id to check - /// Store identifier to check against - /// true - authorized; otherwise, false - bool Authorize(string entityName, int entityId, int storeId); - } - - public static class IStoreMappingServiceExtensions - { - /// - /// Find store identifiers with granted access (mapped to the entity) - /// - /// Type - /// Entity - /// Store identifiers - public static int[] GetStoresIdsWithAccess(this IStoreMappingService svc, T entity) where T : BaseEntity, IStoreMappingSupported - { - if (entity == null) - return new int[0]; - - return svc.GetStoresIdsWithAccess(entity.GetEntityName(), entity.Id); - } - - /// - /// Authorize whether entity could be accessed in the current store (mapped to this store) - /// - /// Type - /// Entity - /// true - authorized; otherwise, false - public static bool Authorize(this IStoreMappingService svc, T entity) where T : BaseEntity, IStoreMappingSupported - { - if (entity == null) - return false; - - if (!entity.LimitedToStores) - return true; - - return svc.Authorize(entity.GetEntityName(), entity.Id); - } - - /// - /// Authorize whether entity could be accessed in a store (mapped to this store) - /// - /// Type - /// Entity - /// Store identifier to check against - /// true - authorized; otherwise, false - public static bool Authorize(this IStoreMappingService svc, T entity, int storeId) where T : BaseEntity, IStoreMappingSupported - { - if (entity == null) - return false; - - if (!entity.LimitedToStores) - return true; - - return svc.Authorize(entity.GetEntityName(), entity.Id, storeId); - } - } + /// + /// Store mapping service interface + /// + public partial interface IStoreMappingService + { + /// + /// Deletes a store mapping record + /// + /// Store mapping record + void DeleteStoreMapping(StoreMapping storeMapping); + + /// + /// Gets a store mapping record + /// + /// Store mapping record identifier + /// Store mapping record + StoreMapping GetStoreMappingById(int storeMappingId); + + /// + /// Gets store mapping records + /// + /// Type + /// Entity + /// Store mapping records + IList GetStoreMappings(T entity) where T : BaseEntity, IStoreMappingSupported; + + /// + /// Gets store mapping records + /// + /// Could be null + /// Could be 0 + /// Store mapping record query + IQueryable GetStoreMappingsFor(string entityName, int entityId); + + /// + /// Save the store mapping for an entity + /// + /// Entity type + /// The entity + /// Array of selected store ids + void SaveStoreMappings(T entity, int[] selectedStoreIds) where T : BaseEntity, IStoreMappingSupported; + + /// + /// Inserts a store mapping record + /// + /// Store mapping + void InsertStoreMapping(StoreMapping storeMapping); + + /// + /// Inserts a store mapping record + /// + /// Type + /// Store id + /// Entity + void InsertStoreMapping(T entity, int storeId) where T : BaseEntity, IStoreMappingSupported; + + /// + /// Updates the store mapping record + /// + /// Store mapping + void UpdateStoreMapping(StoreMapping storeMapping); + + /// + /// Find store identifiers with granted access (mapped to the entity) + /// + /// Entity name to check + /// Entity id to check + /// Store identifiers + int[] GetStoresIdsWithAccess(string entityName, int entityId); + + /// + /// Checks whether an entity can be accessed in a store (mapped to this store) + /// + /// Entity name to check + /// Entity id to check + /// true - authorized; otherwise, false + bool Authorize(string entityName, int entityId); + + /// + /// Checks whether an entity can be accessed in a store (mapped to this store) + /// + /// Entity name to check + /// Entity id to check + /// Store identifier to check against + /// true - authorized; otherwise, false + bool Authorize(string entityName, int entityId, int storeId); + } + + public static class IStoreMappingServiceExtensions + { + /// + /// Find store identifiers with granted access (mapped to the entity) + /// + /// Type + /// Entity + /// Store identifiers + public static int[] GetStoresIdsWithAccess(this IStoreMappingService svc, T entity) where T : BaseEntity, IStoreMappingSupported + { + if (entity == null) + return new int[0]; + + return svc.GetStoresIdsWithAccess(entity.GetEntityName(), entity.Id); + } + + /// + /// Authorize whether entity could be accessed in the current store (mapped to this store) + /// + /// Type + /// Entity + /// true - authorized; otherwise, false + public static bool Authorize(this IStoreMappingService svc, T entity) where T : BaseEntity, IStoreMappingSupported + { + if (entity == null) + return false; + + if (!entity.LimitedToStores) + return true; + + return svc.Authorize(entity.GetEntityName(), entity.Id); + } + + /// + /// Authorize whether entity could be accessed in a store (mapped to this store) + /// + /// Type + /// Entity + /// Store identifier to check against + /// true - authorized; otherwise, false + public static bool Authorize(this IStoreMappingService svc, T entity, int storeId) where T : BaseEntity, IStoreMappingSupported + { + if (entity == null) + return false; + + if (!entity.LimitedToStores) + return true; + + return svc.Authorize(entity.GetEntityName(), entity.Id, storeId); + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Stores/IStoreService.cs b/src/Libraries/SmartStore.Services/Stores/IStoreService.cs index 90a6da14ce..1cef9569da 100644 --- a/src/Libraries/SmartStore.Services/Stores/IStoreService.cs +++ b/src/Libraries/SmartStore.Services/Stores/IStoreService.cs @@ -4,62 +4,62 @@ namespace SmartStore.Services.Stores { - /// - /// Store service interface - /// - public partial interface IStoreService - { - /// - /// Deletes a store - /// - /// Store - void DeleteStore(Store store); + /// + /// Store service interface + /// + public partial interface IStoreService + { + /// + /// Deletes a store + /// + /// Store + void DeleteStore(Store store); - /// - /// Gets all stores - /// - /// Store collection - IList GetAllStores(); + /// + /// Gets all stores + /// + /// Store collection + IList GetAllStores(); - /// - /// Gets a store - /// - /// Store identifier - /// Store - Store GetStoreById(int storeId); + /// + /// Gets a store + /// + /// Store identifier + /// Store + Store GetStoreById(int storeId); - /// - /// Inserts a store - /// - /// Store - void InsertStore(Store store); + /// + /// Inserts a store + /// + /// Store + void InsertStore(Store store); - /// - /// Updates the store - /// - /// Store - void UpdateStore(Store store); + /// + /// Updates the store + /// + /// Store + void UpdateStore(Store store); - /// - /// True if there's only one store. Otherwise False. - /// - bool IsSingleStoreMode(); + /// + /// True if there's only one store. Otherwise False. + /// + bool IsSingleStoreMode(); - /// - /// True if the store data is valid. Otherwise False. - /// - /// Store entity - bool IsStoreDataValid(Store store); + /// + /// True if the store data is valid. Otherwise False. + /// + /// Store entity + bool IsStoreDataValid(Store store); - /// - /// Gets the store host name - /// - /// The store to get the host name for - /// - /// If null, checks whether all pages should be secured per . - /// If true, returns the secure url, but only if SSL is enabled for the store. - /// - /// The host name - string GetHost(Store store, bool? secure = null); - } + /// + /// Gets the store host name + /// + /// The store to get the host name for + /// + /// If null, checks whether all pages should be secured per . + /// If true, returns the secure url, but only if SSL is enabled for the store. + /// + /// The host name + string GetHost(Store store, bool? secure = null); + } } \ 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..07f903029b 100644 --- a/src/Libraries/SmartStore.Services/Stores/StoreMappingService.cs +++ b/src/Libraries/SmartStore.Services/Stores/StoreMappingService.cs @@ -8,240 +8,247 @@ namespace SmartStore.Services.Stores { - public partial class StoreMappingService : IStoreMappingService - { - /// - /// 0 = segment (EntityName.IdRange) - /// - const string STOREMAPPING_SEGMENT_KEY = "storemapping:range-{0}"; - const string STOREMAPPING_SEGMENT_PATTERN = "storemapping:range-*"; - - private readonly IRepository _storeMappingRepository; - private readonly IStoreContext _storeContext; - private readonly IStoreService _storeService; - private readonly ICacheManager _cacheManager; - - public StoreMappingService( - ICacheManager cacheManager, - IStoreContext storeContext, - IStoreService storeService, - IRepository storeMappingRepository) - { - _cacheManager = cacheManager; - _storeContext = storeContext; - _storeService = storeService; - _storeMappingRepository = storeMappingRepository; - - QuerySettings = DbQuerySettings.Default; - } - - public DbQuerySettings QuerySettings { get; set; } - - public virtual void DeleteStoreMapping(StoreMapping storeMapping) - { - Guard.NotNull(storeMapping, nameof(storeMapping)); - - _storeMappingRepository.Delete(storeMapping); - - ClearCacheSegment(storeMapping.EntityName, storeMapping.EntityId); - } - - public virtual StoreMapping GetStoreMappingById(int storeMappingId) - { - if (storeMappingId == 0) - return null; - - var storeMapping = _storeMappingRepository.GetById(storeMappingId); - return storeMapping; - } - - public virtual IList GetStoreMappings(T entity) where T : BaseEntity, IStoreMappingSupported - { - Guard.NotNull(entity, nameof(entity)); - - int entityId = entity.Id; - string entityName = entity.GetEntityName(); - - var query = from sm in _storeMappingRepository.Table - where sm.EntityId == entityId && - sm.EntityName == entityName - select sm; - var storeMappings = query.ToList(); - return storeMappings; - } - - public virtual IQueryable GetStoreMappingsFor(string entityName, int entityId) - { - var query = _storeMappingRepository.Table; - - if (entityName.HasValue()) - query = query.Where(x => x.EntityName == entityName); - - if (entityId != 0) - query = query.Where(x => x.EntityId == entityId); - - return query; - } - - public virtual void SaveStoreMappings(T entity, int[] selectedStoreIds) where T : BaseEntity, IStoreMappingSupported - { - var existingStoreMappings = GetStoreMappings(entity); - var allStores = _storeService.GetAllStores(); - - foreach (var store in allStores) - { - if (selectedStoreIds != null && selectedStoreIds.Contains(store.Id)) - { - if (!existingStoreMappings.Any(x => x.StoreId == store.Id)) - InsertStoreMapping(entity, store.Id); - } - else - { - var storeMappingToDelete = existingStoreMappings.FirstOrDefault(x => x.StoreId == store.Id); - if (storeMappingToDelete != null) - DeleteStoreMapping(storeMappingToDelete); - } - } - } - - public virtual void InsertStoreMapping(StoreMapping storeMapping) - { - Guard.NotNull(storeMapping, nameof(storeMapping)); - - _storeMappingRepository.Insert(storeMapping); - - ClearCacheSegment(storeMapping.EntityName, storeMapping.EntityId); - } - - public virtual void InsertStoreMapping(T entity, int storeId) where T : BaseEntity, IStoreMappingSupported - { - Guard.NotNull(entity, nameof(entity)); - - if (storeId == 0) - throw new ArgumentOutOfRangeException(nameof(storeId)); - - int entityId = entity.Id; - string entityName = entity.GetEntityName(); - - var storeMapping = new StoreMapping - { - EntityId = entityId, - EntityName = entityName, - StoreId = storeId - }; - - InsertStoreMapping(storeMapping); - } - - public virtual void UpdateStoreMapping(StoreMapping storeMapping) - { - Guard.NotNull(storeMapping, nameof(storeMapping)); - - _storeMappingRepository.Update(storeMapping); - - ClearCacheSegment(storeMapping.EntityName, storeMapping.EntityId); - } - - public virtual int[] GetStoresIdsWithAccess(string entityName, int entityId) - { - Guard.NotEmpty(entityName, nameof(entityName)); - - if (entityId <= 0) - return new int[0]; - - var cacheSegment = GetCacheSegment(entityName, entityId); - - if (!cacheSegment.TryGetValue(entityId, out var storeIds)) - { - return Array.Empty(); - } - - return storeIds; - } - - public bool Authorize(string entityName, int entityId) - { - return Authorize(entityName, entityId, _storeContext.CurrentStore.Id); - } - - public virtual bool Authorize(string entityName, int entityId, int storeId) - { - Guard.NotEmpty(entityName, nameof(entityName)); - - if (entityId <= 0) - return false; - - if (storeId <= 0) - // return true if no store specified/found - return true; - - if (QuerySettings.IgnoreMultiStore) - return true; - - // Permission granted only when the id list contains the passed storeId - return GetStoresIdsWithAccess(entityName, entityId).Any(x => x == storeId); - } - - #region Cache segmenting - - protected virtual IDictionary GetCacheSegment(string entityName, int entityId) - { - Guard.NotEmpty(entityName, nameof(entityName)); - - var segmentKey = GetSegmentKeyPart(entityName, entityId, out var minEntityId, out var maxEntityId); - var cacheKey = BuildCacheSegmentKey(segmentKey); - - return _cacheManager.Get(cacheKey, () => - { - var query = from sm in _storeMappingRepository.TableUntracked - where - sm.EntityId >= minEntityId && - sm.EntityId <= maxEntityId && - sm.EntityName == entityName - select sm; - - var mappings = query.ToLookup(x => x.EntityId, x => x.StoreId); + public partial class StoreMappingService : IStoreMappingService + { + /// + /// 0 = segment (EntityName.IdRange) + /// + const string STOREMAPPING_SEGMENT_KEY = "storemapping:range-{0}"; + internal const string STOREMAPPING_SEGMENT_PATTERN = "storemapping:range-*"; + + private readonly IRepository _storeMappingRepository; + private readonly IStoreContext _storeContext; + private readonly IStoreService _storeService; + private readonly ICacheManager _cacheManager; + + public StoreMappingService( + ICacheManager cacheManager, + IStoreContext storeContext, + IStoreService storeService, + IRepository storeMappingRepository) + { + _cacheManager = cacheManager; + _storeContext = storeContext; + _storeService = storeService; + _storeMappingRepository = storeMappingRepository; + + QuerySettings = DbQuerySettings.Default; + } + + public DbQuerySettings QuerySettings { get; set; } + + public virtual void DeleteStoreMapping(StoreMapping storeMapping) + { + Guard.NotNull(storeMapping, nameof(storeMapping)); + + _storeMappingRepository.Delete(storeMapping); + + ClearCacheSegment(storeMapping.EntityName, storeMapping.EntityId); + } + + public virtual StoreMapping GetStoreMappingById(int storeMappingId) + { + if (storeMappingId == 0) + return null; + + var storeMapping = _storeMappingRepository.GetById(storeMappingId); + return storeMapping; + } + + public virtual IList GetStoreMappings(T entity) where T : BaseEntity, IStoreMappingSupported + { + Guard.NotNull(entity, nameof(entity)); + + int entityId = entity.Id; + string entityName = entity.GetEntityName(); + + var query = from sm in _storeMappingRepository.Table + where sm.EntityId == entityId && + sm.EntityName == entityName + select sm; + var storeMappings = query.ToList(); + return storeMappings; + } + + public virtual IQueryable GetStoreMappingsFor(string entityName, int entityId) + { + var query = _storeMappingRepository.Table; + + if (entityName.HasValue()) + query = query.Where(x => x.EntityName == entityName); + + if (entityId != 0) + query = query.Where(x => x.EntityId == entityId); + + return query; + } + + public virtual void SaveStoreMappings(T entity, int[] selectedStoreIds) where T : BaseEntity, IStoreMappingSupported + { + var existingStoreMappings = GetStoreMappings(entity); + var allStores = _storeService.GetAllStores(); + var selectedIds = selectedStoreIds ?? Array.Empty(); + + entity.LimitedToStores = selectedIds.Length == 1 && selectedIds[0] == 0 + ? false + : selectedIds.Any(); + + foreach (var store in allStores) + { + if (selectedIds.Contains(store.Id)) + { + if (!existingStoreMappings.Any(x => x.StoreId == store.Id)) + InsertStoreMapping(entity, store.Id); + } + else + { + var storeMappingToDelete = existingStoreMappings.FirstOrDefault(x => x.StoreId == store.Id); + if (storeMappingToDelete != null) + DeleteStoreMapping(storeMappingToDelete); + } + } + + _storeMappingRepository.Context.SaveChanges(); + } + + public virtual void InsertStoreMapping(StoreMapping storeMapping) + { + Guard.NotNull(storeMapping, nameof(storeMapping)); + + _storeMappingRepository.Insert(storeMapping); + + ClearCacheSegment(storeMapping.EntityName, storeMapping.EntityId); + } - var dict = new Dictionary(mappings.Count); + public virtual void InsertStoreMapping(T entity, int storeId) where T : BaseEntity, IStoreMappingSupported + { + Guard.NotNull(entity, nameof(entity)); + + if (storeId == 0) + throw new ArgumentOutOfRangeException(nameof(storeId)); + + int entityId = entity.Id; + string entityName = entity.GetEntityName(); + + var storeMapping = new StoreMapping + { + EntityId = entityId, + EntityName = entityName, + StoreId = storeId + }; + + InsertStoreMapping(storeMapping); + } + + public virtual void UpdateStoreMapping(StoreMapping storeMapping) + { + Guard.NotNull(storeMapping, nameof(storeMapping)); + + _storeMappingRepository.Update(storeMapping); + + ClearCacheSegment(storeMapping.EntityName, storeMapping.EntityId); + } + + public virtual int[] GetStoresIdsWithAccess(string entityName, int entityId) + { + Guard.NotEmpty(entityName, nameof(entityName)); + + if (entityId <= 0) + return new int[0]; + + var cacheSegment = GetCacheSegment(entityName, entityId); + + if (!cacheSegment.TryGetValue(entityId, out var storeIds)) + { + return Array.Empty(); + } + + return storeIds; + } + + public bool Authorize(string entityName, int entityId) + { + return Authorize(entityName, entityId, _storeContext.CurrentStore.Id); + } + + public virtual bool Authorize(string entityName, int entityId, int storeId) + { + Guard.NotEmpty(entityName, nameof(entityName)); + + if (entityId <= 0) + return false; + + if (storeId <= 0) + // return true if no store specified/found + return true; + + if (QuerySettings.IgnoreMultiStore) + return true; + + // Permission granted only when the id list contains the passed storeId + return GetStoresIdsWithAccess(entityName, entityId).Any(x => x == storeId); + } + + #region Cache segmenting + + protected virtual IDictionary GetCacheSegment(string entityName, int entityId) + { + Guard.NotEmpty(entityName, nameof(entityName)); + + var segmentKey = GetSegmentKeyPart(entityName, entityId, out var minEntityId, out var maxEntityId); + var cacheKey = BuildCacheSegmentKey(segmentKey); + + return _cacheManager.Get(cacheKey, () => + { + var query = from sm in _storeMappingRepository.TableUntracked + where + sm.EntityId >= minEntityId && + sm.EntityId <= maxEntityId && + sm.EntityName == entityName + select sm; - foreach (var sm in mappings) - { - dict[sm.Key] = sm.ToArray(); - } - - return dict; - }); - } - - /// - /// Clears the cached segment from the cache - /// - protected virtual void ClearCacheSegment(string entityName, int entityId) - { - try - { - var segmentKey = GetSegmentKeyPart(entityName, entityId); - _cacheManager.Remove(BuildCacheSegmentKey(segmentKey)); - } - catch { } - } - - private string BuildCacheSegmentKey(string segment) - { - return String.Format(STOREMAPPING_SEGMENT_KEY, segment); - } - - private string GetSegmentKeyPart(string entityName, int entityId) - { - return GetSegmentKeyPart(entityName, entityId, out _, out _); - } - - private string GetSegmentKeyPart(string entityName, int entityId, out int minId, out int maxId) - { - maxId = entityId.GetRange(1000, out minId); - return (entityName + "." + maxId.ToString()).ToLowerInvariant(); - } - - #endregion - } + var mappings = query.ToLookup(x => x.EntityId, x => x.StoreId); + + var dict = new Dictionary(mappings.Count); + + foreach (var sm in mappings) + { + dict[sm.Key] = sm.ToArray(); + } + + return dict; + }); + } + + /// + /// Clears the cached segment from the cache + /// + protected virtual void ClearCacheSegment(string entityName, int entityId) + { + try + { + var segmentKey = GetSegmentKeyPart(entityName, entityId); + _cacheManager.Remove(BuildCacheSegmentKey(segmentKey)); + } + catch { } + } + + private string BuildCacheSegmentKey(string segment) + { + return String.Format(STOREMAPPING_SEGMENT_KEY, segment); + } + + private string GetSegmentKeyPart(string entityName, int entityId) + { + return GetSegmentKeyPart(entityName, entityId, out _, out _); + } + + private string GetSegmentKeyPart(string entityName, int entityId, out int minId, out int maxId) + { + (minId, maxId) = entityId.GetRange(1000); + return (entityName + "." + minId.ToString()).ToLowerInvariant(); + } + + #endregion + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Stores/StoreService.cs b/src/Libraries/SmartStore.Services/Stores/StoreService.cs index f8cde15ab0..a7add0c0f1 100644 --- a/src/Libraries/SmartStore.Services/Stores/StoreService.cs +++ b/src/Libraries/SmartStore.Services/Stores/StoreService.cs @@ -7,108 +7,108 @@ namespace SmartStore.Services.Stores { - public partial class StoreService : IStoreService - { - private readonly IRepository _storeRepository; - - private bool? _isSingleStoreMode = null; - - public StoreService(IRepository storeRepository) - { - _storeRepository = storeRepository; - } - - public virtual IList GetAllStores() - { - var query = _storeRepository.Table - .Expand(x => x.PrimaryStoreCurrency) - .Expand(x => x.PrimaryExchangeRateCurrency) - .OrderBy(x => x.DisplayOrder) - .ThenBy(x => x.Name); - - var stores = query.ToListCached("db.store.all"); - return stores; - } - - public virtual Store GetStoreById(int storeId) - { - if (storeId == 0) - return null; - - return _storeRepository.GetByIdCached(storeId, "db.store.id-" + storeId); - } - - public virtual void InsertStore(Store store) - { - Guard.NotNull(store, nameof(store)); - - _storeRepository.Insert(store); - } - - public virtual void UpdateStore(Store store) - { - Guard.NotNull(store, nameof(store)); - - _storeRepository.Update(store); - } - - public virtual void DeleteStore(Store store) - { - Guard.NotNull(store, nameof(store)); - - var allStores = GetAllStores(); - if (allStores.Count == 1) - throw new Exception("You cannot delete the only configured store."); - - _storeRepository.Delete(store); - } - - public virtual bool IsSingleStoreMode() - { - if (!_isSingleStoreMode.HasValue) - { - _isSingleStoreMode = (_storeRepository.TableUntracked.Count() <= 1); - } - - return _isSingleStoreMode.Value; - } - - public virtual bool IsStoreDataValid(Store store) - { - Guard.NotNull(store, nameof(store)); - - if (store.Url.IsEmpty()) - return false; - - try - { - var uri = new Uri(store.Url); - var domain = uri.DnsSafeHost.EmptyNull().ToLower(); - - switch (domain) - { - case "www.yourstore.com": - case "yourstore.com": - case "www.mystore.com": - case "mystore.com": - case "www.mein-shop.de": - case "mein-shop.de": - return false; - default: - return store.Url.IsWebUrl(); - } - } - catch - { - return false; - } - } - - public string GetHost(Store store, bool? secure = null) - { - Guard.NotNull(store, nameof(store)); - - return store.GetHost(secure ?? store.ForceSslForAllPages); - } - } + public partial class StoreService : IStoreService + { + private readonly IRepository _storeRepository; + + private bool? _isSingleStoreMode = null; + + public StoreService(IRepository storeRepository) + { + _storeRepository = storeRepository; + } + + public virtual IList GetAllStores() + { + var query = _storeRepository.Table + .Expand(x => x.PrimaryStoreCurrency) + .Expand(x => x.PrimaryExchangeRateCurrency) + .OrderBy(x => x.DisplayOrder) + .ThenBy(x => x.Name); + + var stores = query.ToListCached("db.store.all"); + return stores; + } + + public virtual Store GetStoreById(int storeId) + { + if (storeId == 0) + return null; + + return _storeRepository.GetByIdCached(storeId, "db.store.id-" + storeId); + } + + public virtual void InsertStore(Store store) + { + Guard.NotNull(store, nameof(store)); + + _storeRepository.Insert(store); + } + + public virtual void UpdateStore(Store store) + { + Guard.NotNull(store, nameof(store)); + + _storeRepository.Update(store); + } + + public virtual void DeleteStore(Store store) + { + Guard.NotNull(store, nameof(store)); + + var allStores = GetAllStores(); + if (allStores.Count == 1) + throw new Exception("You cannot delete the only configured store."); + + _storeRepository.Delete(store); + } + + public virtual bool IsSingleStoreMode() + { + if (!_isSingleStoreMode.HasValue) + { + _isSingleStoreMode = (_storeRepository.TableUntracked.Count() <= 1); + } + + return _isSingleStoreMode.Value; + } + + public virtual bool IsStoreDataValid(Store store) + { + Guard.NotNull(store, nameof(store)); + + if (store.Url.IsEmpty()) + return false; + + try + { + var uri = new Uri(store.Url); + var domain = uri.DnsSafeHost.EmptyNull().ToLower(); + + switch (domain) + { + case "www.yourstore.com": + case "yourstore.com": + case "www.mystore.com": + case "mystore.com": + case "www.mein-shop.de": + case "mein-shop.de": + return false; + default: + return store.Url.IsWebUrl(); + } + } + catch + { + return false; + } + } + + public string GetHost(Store store, bool? secure = null) + { + Guard.NotNull(store, nameof(store)); + + return store.GetHost(secure ?? store.ForceSslForAllPages); + } + } } \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Tasks/CronExpression.cs b/src/Libraries/SmartStore.Services/Tasks/CronExpression.cs index 28598352c2..5432120b91 100644 --- a/src/Libraries/SmartStore.Services/Tasks/CronExpression.cs +++ b/src/Libraries/SmartStore.Services/Tasks/CronExpression.cs @@ -7,65 +7,65 @@ namespace SmartStore.Services.Tasks { - public static class CronExpression - { - public static bool IsValid(string expression) - { - try - { - CrontabSchedule.Parse(expression); - return true; - } - catch { } + public static class CronExpression + { + public static bool IsValid(string expression) + { + try + { + CrontabSchedule.Parse(expression); + return true; + } + catch { } - return false; - } + return false; + } - public static DateTime GetNextSchedule(string expression, DateTime baseTime) - { - return GetFutureSchedules(expression, baseTime, 1).FirstOrDefault(); - } + public static DateTime GetNextSchedule(string expression, DateTime baseTime) + { + return GetFutureSchedules(expression, baseTime, 1).FirstOrDefault(); + } - public static DateTime GetNextSchedule(string expression, DateTime baseTime, DateTime endTime) - { - return GetFutureSchedules(expression, baseTime, endTime, 1).FirstOrDefault(); - } + public static DateTime GetNextSchedule(string expression, DateTime baseTime, DateTime endTime) + { + return GetFutureSchedules(expression, baseTime, endTime, 1).FirstOrDefault(); + } - public static IEnumerable GetFutureSchedules(string expression, DateTime baseTime, int max = 10) - { - return GetFutureSchedules(expression, baseTime, DateTime.MaxValue); - } + public static IEnumerable GetFutureSchedules(string expression, DateTime baseTime, int max = 10) + { + return GetFutureSchedules(expression, baseTime, DateTime.MaxValue); + } - public static IEnumerable GetFutureSchedules(string expression, DateTime baseTime, DateTime endTime, int max = 10) - { - Guard.NotEmpty(expression, nameof(expression)); + public static IEnumerable GetFutureSchedules(string expression, DateTime baseTime, DateTime endTime, int max = 10) + { + Guard.NotEmpty(expression, nameof(expression)); - var schedule = CrontabSchedule.Parse(expression); - return schedule.GetNextOccurrences(baseTime, endTime).Take(max); - } + var schedule = CrontabSchedule.Parse(expression); + return schedule.GetNextOccurrences(baseTime, endTime).Take(max); + } - public static string GetFriendlyDescription(string expression) - { - var options = new Options - { - DayOfWeekStartIndexZero = true, - ThrowExceptionOnParseError = true, - Verbose = false, - Use24HourTimeFormat = Thread.CurrentThread.CurrentUICulture.DateTimeFormat.AMDesignator.IsEmpty() - }; + public static string GetFriendlyDescription(string expression) + { + var options = new Options + { + DayOfWeekStartIndexZero = true, + ThrowExceptionOnParseError = true, + Verbose = false, + Use24HourTimeFormat = Thread.CurrentThread.CurrentUICulture.DateTimeFormat.AMDesignator.IsEmpty() + }; - if (expression.HasValue()) - { - try - { - return ExpressionDescriptor.GetDescription(expression, options); - } - catch { } - } + if (expression.HasValue()) + { + try + { + return ExpressionDescriptor.GetDescription(expression, options); + } + catch { } + } - return "?"; - } + return "?"; + } - } + } } diff --git a/src/Libraries/SmartStore.Services/Tasks/IScheduleTaskService.cs b/src/Libraries/SmartStore.Services/Tasks/IScheduleTaskService.cs index be442aa61a..89bb1a7acc 100644 --- a/src/Libraries/SmartStore.Services/Tasks/IScheduleTaskService.cs +++ b/src/Libraries/SmartStore.Services/Tasks/IScheduleTaskService.cs @@ -56,30 +56,30 @@ public partial interface IScheduleTaskService /// Task void UpdateTask(ScheduleTask task); - /// - /// Inserts a new task definition to the database or returns an existing one - /// - /// The concrete implementation of the task - /// Wraps the newly created instance - /// A newly created or existing task instance - /// - /// This method does NOT update an already exising task - /// - ScheduleTask GetOrAddTask(Action newAction) where T : ITask; - - /// - /// Calculates - according to their cron expressions - all task future schedules - /// and saves them to the database. - /// - /// When true, determines stale tasks and fixes their states to idle. - void CalculateFutureSchedules(IEnumerable tasks, bool isAppStart = false); - - /// - /// Calculates the next schedule according to the task's cron expression - /// - /// ScheduleTask - /// The next schedule or null if the task is disabled - DateTime? GetNextSchedule(ScheduleTask task); + /// + /// Inserts a new task definition to the database or returns an existing one + /// + /// The concrete implementation of the task + /// Wraps the newly created instance + /// A newly created or existing task instance + /// + /// This method does NOT update an already exising task + /// + ScheduleTask GetOrAddTask(Action newAction) where T : ITask; + + /// + /// Calculates - according to their cron expressions - all task future schedules + /// and saves them to the database. + /// + /// When true, determines stale tasks and fixes their states to idle. + void CalculateFutureSchedules(IEnumerable tasks, bool isAppStart = false); + + /// + /// Calculates the next schedule according to the task's cron expression + /// + /// ScheduleTask + /// The next schedule or null if the task is disabled + DateTime? GetNextSchedule(ScheduleTask task); #region Schedule task history @@ -168,32 +168,32 @@ IPagedList GetHistoryEntries( } public static class IScheduleTaskServiceExtensions - { - public static ScheduleTask GetTaskByType(this IScheduleTaskService service) where T : ITask - { - return service.GetTaskByType(typeof(T)); - } - - public static ScheduleTask GetTaskByType(this IScheduleTaskService service, Type taskType) - { - Guard.NotNull(taskType, nameof(taskType)); - - var name = taskType.AssemblyQualifiedNameWithoutVersion(); - - return service.GetTaskByType(name); - } - - public static bool TryDeleteTask(this IScheduleTaskService service) where T : ITask - { - var task = service.GetTaskByType(typeof(T)); - - if (task != null) - { - service.DeleteTask(task); - return true; - } - - return false; - } - } + { + public static ScheduleTask GetTaskByType(this IScheduleTaskService service) where T : ITask + { + return service.GetTaskByType(typeof(T)); + } + + public static ScheduleTask GetTaskByType(this IScheduleTaskService service, Type taskType) + { + Guard.NotNull(taskType, nameof(taskType)); + + var name = taskType.AssemblyQualifiedNameWithoutVersion(); + + return service.GetTaskByType(name); + } + + public static bool TryDeleteTask(this IScheduleTaskService service) where T : ITask + { + var task = service.GetTaskByType(typeof(T)); + + if (task != null) + { + service.DeleteTask(task); + return true; + } + + return false; + } + } } diff --git a/src/Libraries/SmartStore.Services/Tasks/ITask.cs b/src/Libraries/SmartStore.Services/Tasks/ITask.cs index 01e576c330..43055fa729 100644 --- a/src/Libraries/SmartStore.Services/Tasks/ITask.cs +++ b/src/Libraries/SmartStore.Services/Tasks/ITask.cs @@ -18,25 +18,27 @@ public partial interface ITask void Execute(TaskExecutionContext ctx); } - /// - /// Interface that should be implemented by background tasks - /// - public interface IAsyncTask : ITask - { - /// - /// Executes a task asynchronously - /// - /// - /// The execution context - /// - Task ExecuteAsync(TaskExecutionContext ctx); - } + /// + /// Interface that should be implemented by background tasks + /// + public interface IAsyncTask : ITask + { + /// + /// Executes a task asynchronously + /// + /// + /// The execution context + /// + Task ExecuteAsync(TaskExecutionContext ctx); + } - public abstract class AsyncTask : IAsyncTask - { - public void Execute(TaskExecutionContext ctx) => - throw new NotSupportedException(); + public abstract class AsyncTask : IAsyncTask + { + public void Execute(TaskExecutionContext ctx) + { + throw new NotSupportedException(); + } - public abstract Task ExecuteAsync(TaskExecutionContext ctx); - } + public abstract Task ExecuteAsync(TaskExecutionContext ctx); + } } diff --git a/src/Libraries/SmartStore.Services/Tasks/ITaskExecutor.cs b/src/Libraries/SmartStore.Services/Tasks/ITaskExecutor.cs index 5f0f126359..f18f72916e 100644 --- a/src/Libraries/SmartStore.Services/Tasks/ITaskExecutor.cs +++ b/src/Libraries/SmartStore.Services/Tasks/ITaskExecutor.cs @@ -7,8 +7,8 @@ namespace SmartStore.Services.Tasks public interface ITaskExecutor { Task ExecuteAsync( - ScheduleTask entity, - IDictionary taskParameters = null, - bool throwOnError = false); + ScheduleTask entity, + IDictionary taskParameters = null, + bool throwOnError = false); } } diff --git a/src/Libraries/SmartStore.Services/Tasks/ITaskScheduler.cs b/src/Libraries/SmartStore.Services/Tasks/ITaskScheduler.cs index 0070ad48f6..af80694d8c 100644 --- a/src/Libraries/SmartStore.Services/Tasks/ITaskScheduler.cs +++ b/src/Libraries/SmartStore.Services/Tasks/ITaskScheduler.cs @@ -17,58 +17,58 @@ public interface ITaskScheduler /// int SweepIntervalMinutes { get; set; } - /// - /// The fully qualified base url - /// + /// + /// The fully qualified base url + /// string BaseUrl { get; set; } - /// - /// Gets a value indicating whether the scheduler is active and periodically sweeps all tasks. - /// + /// + /// Gets a value indicating whether the scheduler is active and periodically sweeps all tasks. + /// bool IsActive { get; } - /// - /// Gets the unique key by which a instance - /// can be retrieved from an instance - /// - /// The schedule task id - /// A unique string key - string GetAsyncStateKey(int scheduleTaskId); + /// + /// Gets the unique key by which a instance + /// can be retrieved from an instance + /// + /// The schedule task id + /// A unique string key + string GetAsyncStateKey(int scheduleTaskId); - /// - /// Starts/initializes the scheduler - /// - void Start(); + /// + /// Starts/initializes the scheduler + /// + void Start(); - /// - /// Stops the scheduler - /// + /// + /// Stops the scheduler + /// void Stop(); - /// - /// Executes a single task immediately - /// - /// - /// Optional task parameters + /// + /// Executes a single task immediately + /// + /// + /// Optional task parameters void RunSingleTask(int scheduleTaskId, IDictionary taskParameters = null); - /// - /// Verifies the authentication token which is generated right before the HTTP endpoint gets called. - /// - /// The authentication token to verify - /// true if the validation succeeds, false otherwise - /// - /// The task scheduler sends the token as a HTTP request header item. - /// The called endpoint (e.g. a controller action) is reponsible for invoking - /// this method and quitting the tasks's execution - preferrably with HTTP 403 - - /// if the verification fails. - /// + /// + /// Verifies the authentication token which is generated right before the HTTP endpoint gets called. + /// + /// The authentication token to verify + /// true if the validation succeeds, false otherwise + /// + /// The task scheduler sends the token as a HTTP request header item. + /// The called endpoint (e.g. a controller action) is reponsible for invoking + /// this method and quitting the tasks's execution - preferrably with HTTP 403 - + /// if the verification fails. + /// bool VerifyAuthToken(string authToken); } public static class ITaskSchedulerExtensions { - internal static void SetBaseUrl(this ITaskScheduler scheduler, IStoreService storeService, HttpContextBase httpContext) + internal static void SetBaseUrl(this ITaskScheduler scheduler, IStoreService storeService, HttpContextBase httpContext) { string url = ""; @@ -83,7 +83,7 @@ internal static void SetBaseUrl(this ITaskScheduler scheduler, IStoreService sto if (url.IsEmpty()) { - url = WebHelper.GetAbsoluteUrl(VirtualPathUtility.ToAbsolute("~/TaskScheduler"), httpContext.Request); + url = WebHelper.GetAbsoluteUrl(VirtualPathUtility.ToAbsolute("~/TaskScheduler"), httpContext.Request); } scheduler.BaseUrl = url; diff --git a/src/Libraries/SmartStore.Services/Tasks/InitializeSchedulerFilter.cs b/src/Libraries/SmartStore.Services/Tasks/InitializeSchedulerFilter.cs deleted file mode 100644 index 665fca2d30..0000000000 --- a/src/Libraries/SmartStore.Services/Tasks/InitializeSchedulerFilter.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Web.Mvc; -using System.Web.Mvc.Filters; -using SmartStore.Core.Events; -using SmartStore.Core.Infrastructure; -using SmartStore.Core.Logging; -using SmartStore.Services.Stores; -using SmartStore.Utilities; - -namespace SmartStore.Services.Tasks -{ - public class InitializeSchedulerFilter : IAuthenticationFilter - { - private readonly static object s_lock = new object(); - private static int s_errCount; - private static bool s_initializing = false; - - public void OnAuthentication(AuthenticationContext filterContext) - { - var request = filterContext?.HttpContext?.Request; - if (request == null) - return; - - if (filterContext.IsChildAction) - return; - - lock (s_lock) - { - if (!s_initializing) - { - s_initializing = true; - - var eventPublisher = EngineContext.Current.Resolve(); - var logger = EngineContext.Current.Resolve().CreateLogger(); - var taskScheduler = EngineContext.Current.Resolve(); - - try - { - var taskService = EngineContext.Current.Resolve(); - var storeService = EngineContext.Current.Resolve(); - - var tasks = taskService.GetAllTasks(true); - taskService.CalculateFutureSchedules(tasks, true /* isAppStart */); - - var baseUrl = CommonHelper.GetAppSetting("sm:TaskSchedulerBaseUrl"); - if (baseUrl.IsWebUrl()) - { - taskScheduler.BaseUrl = baseUrl; - } - else - { - // autoresolve base url - taskScheduler.SetBaseUrl(storeService, filterContext.HttpContext); - } - - taskScheduler.SweepIntervalMinutes = CommonHelper.GetAppSetting("sm:TaskSchedulerSweepInterval", 1); - taskScheduler.Start(); - - logger.Info("Initialized TaskScheduler with base url '{0}'".FormatInvariant(taskScheduler.BaseUrl)); - - eventPublisher.Publish(new AppInitScheduledTasksEvent { ScheduledTasks = tasks }); - } - catch (Exception ex) - { - s_errCount++; - s_initializing = false; - logger.Error(ex, "Error while initializing Task Scheduler"); - } - finally - { - var tooManyFailures = s_errCount >= 10; - - if (tooManyFailures || (taskScheduler != null && taskScheduler.IsActive)) - { - GlobalFilters.Filters.Remove(this); - } - - if (tooManyFailures && logger != null) - { - logger.Warn("Stopped trying to initialize the Task Scheduler: too many failed attempts in succession (10+). Maybe uncommenting the setting 'sm:TaskSchedulerBaseUrl' in web.config solves the problem?"); - } - } - } - } - } - - public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext) - { - } - } -} diff --git a/src/Libraries/SmartStore.Services/Tasks/ScheduleTaskService.cs b/src/Libraries/SmartStore.Services/Tasks/ScheduleTaskService.cs index 0b4e3ef725..c9990757c2 100644 --- a/src/Libraries/SmartStore.Services/Tasks/ScheduleTaskService.cs +++ b/src/Libraries/SmartStore.Services/Tasks/ScheduleTaskService.cs @@ -33,20 +33,20 @@ public ScheduleTaskService( { _taskRepository = taskRepository; _taskHistoryRepository = taskHistoryRepository; - _dtHelper = dtHelper; + _dtHelper = dtHelper; _env = env; _commonSettings = commonSettings; - T = NullLocalizer.Instance; - Logger = NullLogger.Instance; + T = NullLocalizer.Instance; + Logger = NullLogger.Instance; } - public Localizer T { get; set; } - public ILogger Logger { get; set; } + public Localizer T { get; set; } + public ILogger Logger { get; set; } public virtual void DeleteTask(ScheduleTask task) { - Guard.NotNull(task, nameof(task)); + Guard.NotNull(task, nameof(task)); _taskRepository.Delete(task); } @@ -56,49 +56,49 @@ public virtual ScheduleTask GetTaskById(int taskId) if (taskId == 0) return null; - return Retry.Run( - () => _taskRepository.GetById(taskId), - 3, TimeSpan.FromMilliseconds(100), - RetryOnDeadlockException); - } + return Retry.Run( + () => _taskRepository.GetById(taskId), + 3, TimeSpan.FromMilliseconds(100), + RetryOnDeadlockException); + } public virtual ScheduleTask GetTaskByType(string type) { - try - { - if (type.HasValue()) - { - var query = _taskRepository.Table - .Where(t => t.Type == type) - .OrderByDescending(t => t.Id); - - var task = query.FirstOrDefault(); - return task; - } - } - catch (Exception exc) - { - // Do not throw an exception if the underlying provider failed on Open. - exc.Dump(); - } - - return null; + try + { + if (type.HasValue()) + { + var query = _taskRepository.Table + .Where(t => t.Type == type) + .OrderByDescending(t => t.Id); + + var task = query.FirstOrDefault(); + return task; + } + } + catch (Exception exc) + { + // Do not throw an exception if the underlying provider failed on Open. + exc.Dump(); + } + + return null; } - public virtual IList GetAllTasks(bool includeDisabled = false) + public virtual IList GetAllTasks(bool includeDisabled = false) { var query = _taskRepository.Table; - if (!includeDisabled) + if (!includeDisabled) { query = query.Where(t => t.Enabled); } query = query.OrderByDescending(t => t.Enabled); - return Retry.Run( - () => query.ToList(), - 3, TimeSpan.FromMilliseconds(100), - RetryOnDeadlockException); - } + return Retry.Run( + () => query.ToList(), + 3, TimeSpan.FromMilliseconds(100), + RetryOnDeadlockException); + } public async virtual Task> GetPendingTasksAsync() { @@ -130,79 +130,81 @@ from t in _taskRepository.Table return x.Task; }) .Where(x => x.IsPending) + .OrderByDescending(x => x.Priority) + .ThenBy(x => x.NextRunUtc.Value) .ToList(); return pendingTasks; - } + } public virtual void InsertTask(ScheduleTask task) { - Guard.NotNull(task, nameof(task)); + Guard.NotNull(task, nameof(task)); - _taskRepository.Insert(task); + _taskRepository.Insert(task); } public virtual void UpdateTask(ScheduleTask task) { - Guard.NotNull(task, nameof(task)); + Guard.NotNull(task, nameof(task)); - try - { + try + { _taskRepository.Update(task); - } - catch (Exception ex) - { - Logger.Error(ex); - throw; - } + } + catch (Exception ex) + { + Logger.Error(ex); + throw; + } } - public ScheduleTask GetOrAddTask(Action newAction) where T : ITask - { - Guard.NotNull(newAction, nameof(newAction)); - - var type = typeof(T); - - if (type.IsAbstract || type.IsInterface || type.IsNotPublic) - { - throw new InvalidOperationException("Only concrete public task types can be registered."); - } - - var scheduleTask = this.GetTaskByType(); - - if (scheduleTask == null) - { - scheduleTask = new ScheduleTask { Type = type.AssemblyQualifiedNameWithoutVersion() }; - newAction(scheduleTask); - InsertTask(scheduleTask); - } - - return scheduleTask; - } - - public void CalculateFutureSchedules(IEnumerable tasks, bool isAppStart = false) - { - Guard.NotNull(tasks, nameof(tasks)); - - foreach (var task in tasks) - { - task.NextRunUtc = GetNextSchedule(task); - if (isAppStart) - { - FixTypeName(task); - } - else - { - UpdateTask(task); - } - } - - if (isAppStart) - { - // On app start this method's execution is thread-safe, making it sufficient - // to commit all changes in one go. - _taskRepository.Context.SaveChanges(); - } + public ScheduleTask GetOrAddTask(Action newAction) where T : ITask + { + Guard.NotNull(newAction, nameof(newAction)); + + var type = typeof(T); + + if (type.IsAbstract || type.IsInterface || type.IsNotPublic) + { + throw new InvalidOperationException("Only concrete public task types can be registered."); + } + + var scheduleTask = this.GetTaskByType(); + + if (scheduleTask == null) + { + scheduleTask = new ScheduleTask { Type = type.AssemblyQualifiedNameWithoutVersion() }; + newAction(scheduleTask); + InsertTask(scheduleTask); + } + + return scheduleTask; + } + + public void CalculateFutureSchedules(IEnumerable tasks, bool isAppStart = false) + { + Guard.NotNull(tasks, nameof(tasks)); + + foreach (var task in tasks) + { + task.NextRunUtc = GetNextSchedule(task); + if (isAppStart) + { + FixTypeName(task); + } + else + { + UpdateTask(task); + } + } + + if (isAppStart) + { + // On app start this method's execution is thread-safe, making it sufficient + // to commit all changes in one go. + _taskRepository.Context.SaveChanges(); + } if (isAppStart) { @@ -243,49 +245,49 @@ public void CalculateFutureSchedules(IEnumerable tasks, bool isApp } } - private void FixTypeName(ScheduleTask task) - { - // In versions prior V3 a double space could exist in ScheduleTask type name. - if (task.Type.IndexOf(", ") > 0) - { - task.Type = task.Type.Replace(", ", ", "); - } - } - - public virtual DateTime? GetNextSchedule(ScheduleTask task) - { - if (task.Enabled) - { - try - { - var localTimeZone = _dtHelper.DefaultStoreTimeZone; - var baseTime = TimeZoneInfo.ConvertTime(DateTime.UtcNow, localTimeZone); - var next = CronExpression.GetNextSchedule(task.CronExpression, baseTime); - var utcTime = _dtHelper.ConvertToUtcTime(next, localTimeZone); - - return utcTime; - } - catch (Exception ex) - { - Logger.ErrorFormat(ex, "Could not calculate next schedule time for task '{0}'", task.Name); - } - } - - return null; - } - - private static void RetryOnDeadlockException(int attemp, Exception ex) - { - var isDeadLockException = - (ex as EntityCommandExecutionException).IsDeadlockException() || - (ex as SqlException).IsDeadlockException(); - - if (!isDeadLockException) - { - // We only want to retry on deadlock stuff. - throw ex; - } - } + private void FixTypeName(ScheduleTask task) + { + // In versions prior V3 a double space could exist in ScheduleTask type name. + if (task.Type.IndexOf(", ") > 0) + { + task.Type = task.Type.Replace(", ", ", "); + } + } + + public virtual DateTime? GetNextSchedule(ScheduleTask task) + { + if (task.Enabled) + { + try + { + var localTimeZone = _dtHelper.DefaultStoreTimeZone; + var baseTime = TimeZoneInfo.ConvertTime(DateTime.UtcNow, localTimeZone); + var next = CronExpression.GetNextSchedule(task.CronExpression, baseTime); + var utcTime = _dtHelper.ConvertToUtcTime(next, localTimeZone); + + return utcTime; + } + catch (Exception ex) + { + Logger.ErrorFormat(ex, "Could not calculate next schedule time for task '{0}'", task.Name); + } + } + + return null; + } + + private static void RetryOnDeadlockException(int attemp, Exception ex) + { + var isDeadLockException = + (ex as EntityCommandExecutionException).IsDeadlockException() || + (ex as SqlException).IsDeadlockException(); + + if (!isDeadLockException) + { + // We only want to retry on deadlock stuff. + throw ex; + } + } #region Schedule task history @@ -297,6 +299,17 @@ protected virtual IQueryable GetHistoryEntriesQuery( { var query = _taskHistoryRepository.TableUntracked; + if (lastEntryOnly) + { + query = + from th in query + group th by th.ScheduleTaskId into grp + select grp + .OrderByDescending(x => x.StartedOnUtc) + .ThenByDescending(x => x.Id) + .FirstOrDefault(); + } + if (taskId != 0) { query = query.Where(x => x.ScheduleTaskId == taskId); @@ -311,17 +324,6 @@ protected virtual IQueryable GetHistoryEntriesQuery( query = query.Where(x => x.IsRunning == isRunning.Value); } - if (lastEntryOnly) - { - query = - from th in query - group th by th.ScheduleTaskId into grp - select grp - .OrderByDescending(x => x.StartedOnUtc) - .ThenByDescending(x => x.Id) - .FirstOrDefault(); - } - query = query .OrderByDescending(x => x.StartedOnUtc) .ThenByDescending(x => x.Id); diff --git a/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs b/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs index fa52d51967..0c79140495 100644 --- a/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs +++ b/src/Libraries/SmartStore.Services/Tasks/TaskExecutionContext.cs @@ -7,14 +7,14 @@ 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 - { - private readonly IComponentContext _componentContext; + public delegate void ProgressCallback(int value, int maximum, string message); + + /// + /// Provides the context for the Execute method of the interface. + /// + public class TaskExecutionContext + { + private readonly IComponentContext _componentContext; private readonly ScheduleTaskHistory _originalTaskHistory; internal TaskExecutionContext(IComponentContext componentContext, ScheduleTaskHistory originalTaskHistory) @@ -25,19 +25,19 @@ internal TaskExecutionContext(IComponentContext componentContext, ScheduleTaskHi } public T Resolve(object key = null) where T : class - { - if (key == null) - { - return _componentContext.Resolve(); - } + { + if (key == null) + { + return _componentContext.Resolve(); + } - return _componentContext.ResolveKeyed(key); - } + return _componentContext.ResolveKeyed(key); + } - public T ResolveNamed(string name) where T : class - { - return _componentContext.ResolveNamed(name); - } + public T ResolveNamed(string name) where T : class + { + return _componentContext.ResolveNamed(name); + } /// /// A cancellation token for the running task. @@ -49,84 +49,84 @@ public T ResolveNamed(string name) where T : class public IDictionary Parameters { get; set; } - /// - /// Persists a task's progress information to the database - /// - /// Progress value (numerator) - /// Progress maximum (denominator) - /// Progress message. Can be null. - /// if true, saves the updated task entity immediately, or lazily with the next database commit otherwise. - public void SetProgress(int value, int maximum, string message, bool immediately = false) - { - if (value == 0 && maximum == 0) - { - SetProgress(null, message, immediately); - } - else - { - float fraction = (float)value / (float)Math.Max(maximum, 1f); - int percentage = (int)Math.Round(fraction * 100f, 0); - - SetProgress(Math.Min(Math.Max(percentage, 0), 100), message, immediately); - } - } - - /// - /// Persists a task's progress information to the database - /// - /// Percentual progress. Can be null or a value between 0 and 100. - /// Progress message. Can be null. - /// if true, saves the updated task entity immediately, or lazily with the next database commit otherwise. - public virtual void SetProgress(int? progress, string message, bool immediately = false) - { + /// + /// Persists a task's progress information to the database + /// + /// Progress value (numerator) + /// Progress maximum (denominator) + /// Progress message. Can be null. + /// if true, saves the updated task entity immediately, or lazily with the next database commit otherwise. + public void SetProgress(int value, int maximum, string message, bool immediately = false) + { + if (value == 0 && maximum == 0) + { + SetProgress(null, message, immediately); + } + else + { + float fraction = (float)value / (float)Math.Max(maximum, 1f); + int percentage = (int)Math.Round(fraction * 100f, 0); + + SetProgress(Math.Min(Math.Max(percentage, 0), 100), message, immediately); + } + } + + /// + /// Persists a task's progress information to the database + /// + /// Percentual progress. Can be null or a value between 0 and 100. + /// Progress message. Can be null. + /// if true, saves the updated task entity immediately, or lazily with the next database commit otherwise. + public virtual void SetProgress(int? progress, string message, bool immediately = false) + { if (progress.HasValue) { Guard.InRange(progress.Value, 0, 100, nameof(progress)); } - // Update cloned entity. - ScheduleTaskHistory.ProgressPercent = progress; + // Update cloned entity. + ScheduleTaskHistory.ProgressPercent = progress; ScheduleTaskHistory.ProgressMessage = message; // Update attached entity. _originalTaskHistory.ProgressPercent = progress; _originalTaskHistory.ProgressMessage = message; - if (immediately) - { + if (immediately) + { // Dont't let this abort the task on failure. try { - var dbContext = _componentContext.Resolve(); - //dbContext.ChangeState(_originalTask, System.Data.Entity.EntityState.Modified); - dbContext.SaveChanges(); - } - catch { } - } - } - - /// - /// Creates a transient context, which can be passed to methods requiring a instance. - /// Such methods are usually executed by the task executor in the background. If a manual invocation of such - /// methods is required, this is the way to go. - /// - /// The component context - /// A transient context - public static TaskExecutionContext CreateTransientContext(IComponentContext componentContext) - { - return CreateTransientContext(componentContext, CancellationToken.None); - } - - /// - /// Creates a transient context, which can be passed to methods requiring a instance. - /// Such methods are usually executed by the task executor in the background. If a manual invocation of such - /// methods is required, this is the way to go. - /// - /// The component context - /// The cancellation token - /// A transient context - public static TaskExecutionContext CreateTransientContext(IComponentContext componentContext, CancellationToken cancellationToken) - { + var dbContext = _componentContext.Resolve(); + //dbContext.ChangeState(_originalTask, System.Data.Entity.EntityState.Modified); + dbContext.SaveChanges(); + } + catch { } + } + } + + /// + /// Creates a transient context, which can be passed to methods requiring a instance. + /// Such methods are usually executed by the task executor in the background. If a manual invocation of such + /// methods is required, this is the way to go. + /// + /// The component context + /// A transient context + public static TaskExecutionContext CreateTransientContext(IComponentContext componentContext) + { + return CreateTransientContext(componentContext, CancellationToken.None); + } + + /// + /// Creates a transient context, which can be passed to methods requiring a instance. + /// Such methods are usually executed by the task executor in the background. If a manual invocation of such + /// methods is required, this is the way to go. + /// + /// The component context + /// The cancellation token + /// A transient context + public static TaskExecutionContext CreateTransientContext(IComponentContext componentContext, CancellationToken cancellationToken) + { var originalHistoryEntry = new ScheduleTaskHistory { ScheduleTask = new ScheduleTask @@ -137,24 +137,24 @@ public static TaskExecutionContext CreateTransientContext(IComponentContext comp } }; - var context = new TransientTaskExecutionContext(componentContext, originalHistoryEntry); - context.CancellationToken = cancellationToken; + var context = new TransientTaskExecutionContext(componentContext, originalHistoryEntry); + context.CancellationToken = cancellationToken; context.ScheduleTaskHistory = originalHistoryEntry.Clone(); - return context; - } - } - - internal class TransientTaskExecutionContext : TaskExecutionContext - { - public TransientTaskExecutionContext(IComponentContext componentContext, ScheduleTaskHistory originalHistoryEntry) - : base(componentContext, originalHistoryEntry) - { - } - - public override void SetProgress(int? progress, string message, bool immediately = false) - { - base.SetProgress(progress, message, false /* skip committing to DB */); - } - } + return context; + } + } + + internal class TransientTaskExecutionContext : TaskExecutionContext + { + public TransientTaskExecutionContext(IComponentContext componentContext, ScheduleTaskHistory originalHistoryEntry) + : base(componentContext, originalHistoryEntry) + { + } + + public override void SetProgress(int? progress, string message, bool immediately = false) + { + base.SetProgress(progress, message, false /* skip committing to DB */); + } + } } diff --git a/src/Libraries/SmartStore.Services/Tasks/TaskExecutor.cs b/src/Libraries/SmartStore.Services/Tasks/TaskExecutor.cs index 93e71a4f7a..e79a3e5a9a 100644 --- a/src/Libraries/SmartStore.Services/Tasks/TaskExecutor.cs +++ b/src/Libraries/SmartStore.Services/Tasks/TaskExecutor.cs @@ -17,36 +17,36 @@ public class TaskExecutor : ITaskExecutor { private readonly IScheduleTaskService _scheduledTaskService; private readonly Func _taskResolver; - private readonly IComponentContext _componentContext; - private readonly IAsyncState _asyncState; + private readonly IComponentContext _componentContext; + private readonly IAsyncState _asyncState; private readonly IApplicationEnvironment _env; public const string CurrentCustomerIdParamName = "CurrentCustomerId"; - public const string CurrentStoreIdParamName = "CurrentStoreId"; + public const string CurrentStoreIdParamName = "CurrentStoreId"; - public TaskExecutor( - IScheduleTaskService scheduledTaskService, - IComponentContext componentContext, - IAsyncState asyncState, - Func taskResolver, + public TaskExecutor( + IScheduleTaskService scheduledTaskService, + IComponentContext componentContext, + IAsyncState asyncState, + Func taskResolver, IApplicationEnvironment env) { _scheduledTaskService = scheduledTaskService; - _componentContext = componentContext; - _asyncState = asyncState; + _componentContext = componentContext; + _asyncState = asyncState; _taskResolver = taskResolver; _env = env; Logger = NullLogger.Instance; - T = NullLocalizer.Instance; - } + T = NullLocalizer.Instance; + } public ILogger Logger { get; set; } - public Localizer T { get; set; } + public Localizer T { get; set; } - public async Task ExecuteAsync( - ScheduleTask entity, - IDictionary taskParameters = null, + public async Task ExecuteAsync( + ScheduleTask entity, + IDictionary taskParameters = null, bool throwOnError = false) { if (AsyncRunner.AppShutdownCancellationToken.IsCancellationRequested) @@ -66,11 +66,11 @@ public async Task ExecuteAsync( } bool faulted = false; - bool canceled = false; + bool canceled = false; string lastError = null; ITask task = null; - string stateName = null; - Type taskType = null; + string stateName = null; + Type taskType = null; Exception exception = null; var historyEntry = new ScheduleTaskHistory @@ -83,61 +83,61 @@ public async Task ExecuteAsync( try { - taskType = Type.GetType(entity.Type); - if (taskType == null) - { - Logger.DebugFormat("Invalid scheduled task type: {0}", entity.Type.NaIfEmpty()); - } + taskType = Type.GetType(entity.Type); + if (taskType == null) + { + Logger.DebugFormat("Invalid scheduled task type: {0}", entity.Type.NaIfEmpty()); + } - if (taskType == null) - return; + if (taskType == null) + return; - if (!PluginManager.IsActivePluginAssembly(taskType.Assembly)) - return; + if (!PluginManager.IsActivePluginAssembly(taskType.Assembly)) + return; entity.ScheduleTaskHistory.Add(historyEntry); _scheduledTaskService.UpdateTask(entity); } catch - { - return; - } + { + return; + } try { // Task history entry has been successfully added, now we execute the task. - // Create task instance. - task = _taskResolver(taskType); - stateName = entity.Id.ToString(); + // Create task instance. + task = _taskResolver(taskType); + stateName = entity.Id.ToString(); - // Create & set a composite CancellationTokenSource which also contains the global app shoutdown token. - var cts = CancellationTokenSource.CreateLinkedTokenSource(AsyncRunner.AppShutdownCancellationToken, new CancellationTokenSource().Token); - _asyncState.SetCancelTokenSource(cts, stateName); + // Create & set a composite CancellationTokenSource which also contains the global app shoutdown token. + var cts = CancellationTokenSource.CreateLinkedTokenSource(AsyncRunner.AppShutdownCancellationToken, new CancellationTokenSource().Token); + _asyncState.SetCancelTokenSource(cts, stateName); - var ctx = new TaskExecutionContext(_componentContext, historyEntry) - { + var ctx = new TaskExecutionContext(_componentContext, historyEntry) + { ScheduleTaskHistory = historyEntry.Clone(), - CancellationToken = cts.Token, - Parameters = taskParameters ?? new Dictionary() - }; - - Logger.DebugFormat("Executing scheduled task: {0}", entity.Type); - - if (task is IAsyncTask asyncTask) - { - await asyncTask.ExecuteAsync(ctx); - } - else - { - task.Execute(ctx); - } + CancellationToken = cts.Token, + Parameters = taskParameters ?? new Dictionary() + }; + + Logger.DebugFormat("Executing scheduled task: {0}", entity.Type); + + if (task is IAsyncTask asyncTask) + { + await asyncTask.ExecuteAsync(ctx); + } + else + { + task.Execute(ctx); + } } catch (Exception ex) { exception = ex; faulted = true; - canceled = ex is OperationCanceledException; - lastError = ex.ToAllMessages(true); + canceled = ex is OperationCanceledException; + lastError = ex.ToAllMessages(true); if (canceled) { @@ -145,7 +145,7 @@ public async Task ExecuteAsync( } else { - Logger.Error(ex, string.Concat(T("Admin.System.ScheduleTasks.RunningError", entity.Name), ": ", ex.Message)); + Logger.Error(ex, string.Concat(T("Admin.System.ScheduleTasks.RunningError", entity.Name), ": ", ex.Message)); } } finally @@ -158,19 +158,19 @@ public async Task ExecuteAsync( historyEntry.ProgressMessage = null; historyEntry.Error = lastError; historyEntry.FinishedOnUtc = now; - - if (faulted) - { - if ((!canceled && entity.StopOnError) || task == null) - { - entity.Enabled = false; + + if (faulted) + { + if ((!canceled && entity.StopOnError) || task == null) + { + entity.Enabled = false; updateTask = true; - } - } - else - { + } + } + else + { historyEntry.SucceededOnUtc = now; - } + } try { @@ -179,6 +179,8 @@ public async Task ExecuteAsync( // Remove from AsyncState. if (stateName.HasValue()) { + // We don't just remove the cancellation token, but the whole state (along with the token) + // for the case that a state was registered during task execution. _asyncState.Remove(stateName); } } @@ -188,8 +190,8 @@ public async Task ExecuteAsync( } if (entity.Enabled) - { - entity.NextRunUtc = _scheduledTaskService.GetNextSchedule(entity); + { + entity.NextRunUtc = _scheduledTaskService.GetNextSchedule(entity); updateTask = true; } diff --git a/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs b/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs index f8fca60dc9..f286db5350 100644 --- a/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs +++ b/src/Libraries/SmartStore.Services/Tasks/TaskScheduler.cs @@ -2,149 +2,142 @@ using System.Collections.Generic; using System.Linq; using System.Net; -using System.Threading; using System.Web.Hosting; -using SmartStore.Core.Async; -using SmartStore.Core.Domain.Tasks; using SmartStore.Core.Logging; using SmartStore.Collections; -using SmartStore.Core.Infrastructure; using SmartStore.Core.Caching; using SmartStore.Core; namespace SmartStore.Services.Tasks { - public class DefaultTaskScheduler : DisposableObject, ITaskScheduler, IRegisteredObject + public class DefaultTaskScheduler : DisposableObject, ITaskScheduler, IRegisteredObject { - private readonly ICacheManager _cache; + private readonly ICacheManager _cache; - private bool _intervalFixed; - private int _sweepInterval; - private string _baseUrl; + private bool _intervalFixed; + private int _sweepInterval; + private string _baseUrl; private System.Timers.Timer _timer; private bool _shuttingDown; - private int _errCount; + private int _errCount; public DefaultTaskScheduler(ICacheManager cache) { - _cache = cache; + _cache = cache; - _sweepInterval = 1; - _timer = new System.Timers.Timer(); + _sweepInterval = 1; + _timer = new System.Timers.Timer(); _timer.Elapsed += Elapsed; - Logger = NullLogger.Instance; + Logger = NullLogger.Instance; HostingEnvironment.RegisterObject(this); } - public ILogger Logger - { - get; - set; - } + public ILogger Logger + { + get; + set; + } - public int SweepIntervalMinutes + public int SweepIntervalMinutes { - get { return _sweepInterval; } - set { _sweepInterval = value; } + get => _sweepInterval; + set => _sweepInterval = value; } public string BaseUrl { // TODO: HTTPS? - get { return _baseUrl; } + get => _baseUrl; set { CheckUrl(value); _baseUrl = value.TrimEnd('/', '\\'); - } + } } public void Start() { - if (_timer.Enabled) - return; - - lock (_timer) + if (_timer.Enabled) + return; + + lock (_timer) { CheckUrl(_baseUrl); - _timer.Interval = GetFixedInterval(); + _timer.Interval = GetFixedInterval(); _timer.Start(); } } - private double GetFixedInterval() - { - // Gets seconds to next sweep minute - int seconds = (_sweepInterval * 60) - DateTime.Now.Second; - return seconds * 1000; - } + private double GetFixedInterval() + { + // Gets seconds to next sweep minute + int seconds = (_sweepInterval * 60) - DateTime.Now.Second; + return seconds * 1000; + } public void Stop() { - if (!_timer.Enabled) - return; - - lock (_timer) + if (!_timer.Enabled) + return; + + lock (_timer) { _timer.Stop(); } } - public bool IsActive + public bool IsActive => _timer.Enabled; + + public string GetAsyncStateKey(int scheduleTaskId) { - get { return _timer.Enabled; } + return scheduleTaskId.ToString(); } - public string GetAsyncStateKey(int scheduleTaskId) - { - return scheduleTaskId.ToString(); - } - - private string CreateAuthToken() - { - string authToken = Guid.NewGuid().ToString(); + private string CreateAuthToken() + { + string authToken = Guid.NewGuid().ToString(); - _cache.Put(GenerateAuthTokenCacheKey(authToken), true, TimeSpan.FromMinutes(1)); + _cache.Put(GenerateAuthTokenCacheKey(authToken), true, TimeSpan.FromMinutes(1)); - return authToken; - } + return authToken; + } - private string GenerateAuthTokenCacheKey(string authToken) - { - return "Scheduler:AuthToken:" + authToken; - } + private string GenerateAuthTokenCacheKey(string authToken) + { + return "Scheduler:AuthToken:" + authToken; + } public bool VerifyAuthToken(string authToken) { if (authToken.IsEmpty()) return false; - var cacheKey = GenerateAuthTokenCacheKey(authToken); - if (_cache.Contains(cacheKey)) - { - _cache.Remove(cacheKey); - return true; - } + var cacheKey = GenerateAuthTokenCacheKey(authToken); + if (_cache.Contains(cacheKey)) + { + _cache.Remove(cacheKey); + return true; + } - return false; + return false; } public void RunSingleTask(int scheduleTaskId, IDictionary taskParameters = null) { - taskParameters = taskParameters ?? new Dictionary(); + taskParameters = taskParameters ?? new Dictionary(); - // User executes task in backend explicitly - taskParameters["Explicit"] = "true"; + // User executes task in backend explicitly + taskParameters["Explicit"] = "true"; var qs = new QueryString(); - taskParameters.Each(x => qs.Add(x.Key, x.Value)); - var query = qs.ToString(); + taskParameters.Each(x => qs.Add(x.Key, x.Value)); + var query = qs.ToString(); - CallEndpoint(new Uri("{0}/Execute/{1}{2}".FormatInvariant(_baseUrl, scheduleTaskId, query))); + CallEndpoint(new Uri("{0}/Execute/{1}{2}".FormatInvariant(_baseUrl, scheduleTaskId, query))); } - private void Elapsed(object sender, System.Timers.ElapsedEventArgs e) + private void Elapsed(object sender, System.Timers.ElapsedEventArgs e) { if (!System.Threading.Monitor.TryEnter(_timer)) return; @@ -153,13 +146,13 @@ private void Elapsed(object sender, System.Timers.ElapsedEventArgs e) { if (_timer.Enabled) { - if (!_intervalFixed) - { - _timer.Interval = TimeSpan.FromMinutes(_sweepInterval).TotalMilliseconds; - _intervalFixed = true; - } + if (!_intervalFixed) + { + _timer.Interval = TimeSpan.FromMinutes(_sweepInterval).TotalMilliseconds; + _intervalFixed = true; + } - CallEndpoint(new Uri(_baseUrl + "/Sweep")); + CallEndpoint(new Uri(_baseUrl + "/Sweep")); } } finally @@ -173,73 +166,73 @@ protected internal virtual void CallEndpoint(Uri uri) if (_shuttingDown) return; - var req = WebHelper.CreateHttpRequestForSafeLocalCall(uri); - req.Method = "POST"; + var req = WebHelper.CreateHttpRequestForSafeLocalCall(uri); + req.Method = "POST"; req.ContentType = "text/plain"; - req.ContentLength = 0; - req.Timeout = 10000; // 10 sec. + req.ContentLength = 0; + req.Timeout = 10000; // 10 sec. string authToken = CreateAuthToken(); req.Headers.Add("X-AUTH-TOKEN", authToken); - req.GetResponseAsync().ContinueWith(t => - { - if (t.IsFaulted) - { - HandleException(t.Exception, uri); - _errCount++; - if (_errCount >= 10) - { - // 10 failed attempts in succession. Stop the timer! - this.Stop(); - Logger.Info("Stopping TaskScheduler sweep timer. Too many failed requests in succession."); - } - } - else - { - _errCount = 0; - var response = t.Result; - - //_logger.DebugFormat("TaskScheduler Sweep called successfully: {0}", response.GetResponseStream().AsString()); - - response.Dispose(); - } - }); - } - - private void HandleException(AggregateException exception, Uri uri) - { - string msg = "Error while calling TaskScheduler endpoint '{0}'.".FormatInvariant(uri.OriginalString); - var wex = exception.InnerExceptions.OfType().FirstOrDefault(); - - if (wex == null) - { - Logger.Error(exception.InnerException, msg); - } - else if (wex.Response == null) - { - Logger.Error(wex, msg); - } - else - { - using (var response = wex.Response as HttpWebResponse) - { - if (response != null) - { - var statusCode = (int)response.StatusCode; - if (statusCode < 500) - { - // Any internal server error (>= 500) already handled by TaskSchedulerController's exception filter - msg += " HTTP {0}, {1}".FormatCurrent(statusCode, response.StatusDescription); - Logger.Error(msg); - } - } - - } - } - } - - private void CheckUrl(string url) + req.GetResponseAsync().ContinueWith(t => + { + if (t.IsFaulted) + { + HandleException(t.Exception, uri); + _errCount++; + if (_errCount >= 10) + { + // 10 failed attempts in succession. Stop the timer! + this.Stop(); + Logger.Info("Stopping TaskScheduler sweep timer. Too many failed requests in succession."); + } + } + else + { + _errCount = 0; + var response = t.Result; + + //_logger.DebugFormat("TaskScheduler Sweep called successfully: {0}", response.GetResponseStream().AsString()); + + response.Dispose(); + } + }); + } + + private void HandleException(AggregateException exception, Uri uri) + { + string msg = "Error while calling TaskScheduler endpoint '{0}'.".FormatInvariant(uri.OriginalString); + var wex = exception.InnerExceptions.OfType().FirstOrDefault(); + + if (wex == null) + { + Logger.Error(exception.InnerException, msg); + } + else if (wex.Response == null) + { + Logger.Error(wex, msg); + } + else + { + using (var response = wex.Response as HttpWebResponse) + { + if (response != null) + { + var statusCode = (int)response.StatusCode; + if (statusCode < 500) + { + // Any internal server error (>= 500) already handled by TaskSchedulerController's exception filter + msg += " HTTP {0}, {1}".FormatCurrent(statusCode, response.StatusDescription); + Logger.Error(msg); + } + } + + } + } + } + + private void CheckUrl(string url) { if (!url.IsWebUrl()) { @@ -258,7 +251,7 @@ protected override void OnDispose(bool disposing) void IRegisteredObject.Stop(bool immediate) { _shuttingDown = true; - HostingEnvironment.UnregisterObject(this); + HostingEnvironment.UnregisterObject(this); } } } diff --git a/src/Libraries/SmartStore.Services/Tasks/TaskSchedulerStarter.cs b/src/Libraries/SmartStore.Services/Tasks/TaskSchedulerStarter.cs new file mode 100644 index 0000000000..c2de669aea --- /dev/null +++ b/src/Libraries/SmartStore.Services/Tasks/TaskSchedulerStarter.cs @@ -0,0 +1,74 @@ +using System; +using System.Web; +using SmartStore.Core.Events; +using SmartStore.Core.Infrastructure; +using SmartStore.Core.Logging; +using SmartStore.Services.Stores; +using SmartStore.Utilities; + +namespace SmartStore.Services.Tasks +{ + public sealed class TaskSchedulerStarter : IPostApplicationStart + { + private readonly ITaskScheduler _taskScheduler; + private readonly IScheduleTaskService _taskService; + private readonly IStoreService _storeService; + private readonly IEventPublisher _eventPublisher; + + public TaskSchedulerStarter( + ITaskScheduler taskScheduler, + IScheduleTaskService taskService, + IStoreService storeService, + IEventPublisher eventPublisher) + { + _taskScheduler = taskScheduler; + _taskService = taskService; + _storeService = storeService; + _eventPublisher = eventPublisher; + + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + public int Order => int.MinValue; + public int MaxAttempts => 10; + public bool ThrowOnError => false; + + public void Start(HttpContextBase httpContext) + { + var tasks = _taskService.GetAllTasks(true); + _taskService.CalculateFutureSchedules(tasks, true /* isAppStart */); + + var baseUrl = CommonHelper.GetAppSetting("sm:TaskSchedulerBaseUrl"); + if (baseUrl.IsWebUrl()) + { + _taskScheduler.BaseUrl = baseUrl; + } + else + { + // autoresolve base url + _taskScheduler.SetBaseUrl(_storeService, httpContext); + } + + _taskScheduler.SweepIntervalMinutes = CommonHelper.GetAppSetting("sm:TaskSchedulerSweepInterval", 1); + _taskScheduler.Start(); + + Logger.Info("Initialized TaskScheduler with base url '{0}'".FormatInvariant(_taskScheduler.BaseUrl)); + + _eventPublisher.Publish(new AppInitScheduledTasksEvent { ScheduledTasks = tasks }); + } + + public void OnFail(Exception exception, bool willRetry) + { + if (willRetry) + { + Logger.Error(exception, "Error while initializing Task Scheduler"); + } + else + { + Logger.Warn("Stopped trying to initialize the Task Scheduler: too many failed attempts in succession (10+). Maybe uncommenting the setting 'sm:TaskSchedulerBaseUrl' in web.config solves the problem?"); + } + } + } +} diff --git a/src/Libraries/SmartStore.Services/Tax/CalculateTaxResult.cs b/src/Libraries/SmartStore.Services/Tax/CalculateTaxResult.cs index bca952c015..4431af2868 100644 --- a/src/Libraries/SmartStore.Services/Tax/CalculateTaxResult.cs +++ b/src/Libraries/SmartStore.Services/Tax/CalculateTaxResult.cs @@ -22,13 +22,7 @@ public CalculateTaxResult() /// public IList Errors { get; set; } - public bool Success - { - get - { - return this.Errors.Count == 0; - } - } + public bool Success => this.Errors.Count == 0; public void AddError(string error) { diff --git a/src/Libraries/SmartStore.Services/Tax/FreeTaxProvider.cs b/src/Libraries/SmartStore.Services/Tax/FreeTaxProvider.cs index 679cef0249..22737cf44d 100644 --- a/src/Libraries/SmartStore.Services/Tax/FreeTaxProvider.cs +++ b/src/Libraries/SmartStore.Services/Tax/FreeTaxProvider.cs @@ -4,17 +4,17 @@ namespace SmartStore.Services.Tax { - [SystemName("Tax.Free")] - [FriendlyName("Free tax rate provider")] - [DisplayOrder(0)] + [SystemName("Tax.Free")] + [FriendlyName("Free tax rate provider")] + [DisplayOrder(0)] public class FreeTaxProvider : ITaxProvider { - - public CalculateTaxResult GetTaxRate(CalculateTaxRequest calculateTaxRequest) + + public CalculateTaxResult GetTaxRate(CalculateTaxRequest calculateTaxRequest) { var result = new CalculateTaxResult() { - TaxRate = decimal.Zero + TaxRate = decimal.Zero }; return result; } diff --git a/src/Libraries/SmartStore.Services/Tax/ITaxService.cs b/src/Libraries/SmartStore.Services/Tax/ITaxService.cs index 95bbe83faf..f38335ce69 100644 --- a/src/Libraries/SmartStore.Services/Tax/ITaxService.cs +++ b/src/Libraries/SmartStore.Services/Tax/ITaxService.cs @@ -33,14 +33,14 @@ public partial interface ITaxService /// /// Tax providers IEnumerable> LoadAllTaxProviders(); - + /// /// Gets tax rate /// - /// Product + /// Product /// Customer /// Tax rate decimal GetTaxRate(Product product, Customer customer); @@ -52,23 +52,23 @@ public partial interface ITaxService /// Customer /// Tax rate decimal GetTaxRate(int taxCategoryId, Customer customer); - + /// /// Gets tax rate /// - /// Product + /// Product /// Tax category identifier /// Customer /// Tax rate - decimal GetTaxRate(Product product, int taxCategoryId, Customer customer); - + decimal GetTaxRate(Product product, int taxCategoryId, Customer customer); + /// /// Gets price /// - /// Product + /// Product /// Price /// Tax rate /// Price @@ -84,140 +84,140 @@ public partial interface ITaxService /// Price decimal GetProductPrice(Product product, decimal price, Customer customer, out decimal taxRate); - /// - /// Gets price - /// - /// Product - /// Price - /// Customer - /// Currency - /// Tax rate - /// Price - decimal GetProductPrice(Product product, decimal price, Customer customer, Currency currency, out decimal taxRate); - - /// - /// Gets price - /// - /// Product - /// Price - /// A value indicating whether calculated price should include tax - /// Customer - /// Tax rate - /// Price - decimal GetProductPrice(Product product, decimal price, bool includingTax, Customer customer, out decimal taxRate); - - /// - /// Gets price - /// - /// Product - /// Tax category identifier - /// Price - /// A value indicating whether calculated price should include tax - /// Customer - /// Currency - /// A value indicating whether price already includes tax - /// Tax rate - /// Price - decimal GetProductPrice(Product product, - int taxCategoryId, - decimal price, + /// + /// Gets price + /// + /// Product + /// Price + /// Customer + /// Currency + /// Tax rate + /// Price + decimal GetProductPrice(Product product, decimal price, Customer customer, Currency currency, out decimal taxRate); + + /// + /// Gets price + /// + /// Product + /// Price + /// A value indicating whether calculated price should include tax + /// Customer + /// Tax rate + /// Price + decimal GetProductPrice(Product product, decimal price, bool includingTax, Customer customer, out decimal taxRate); + + /// + /// Gets price + /// + /// Product + /// Tax category identifier + /// Price + /// A value indicating whether calculated price should include tax + /// Customer + /// Currency + /// A value indicating whether price already includes tax + /// Tax rate + /// Price + decimal GetProductPrice(Product product, + int taxCategoryId, + decimal price, bool includingTax, - Customer customer, - Currency currency, - bool priceIncludesTax, - out decimal taxRate); - - - - - /// - /// Gets the shipping price - /// - /// Price - /// Customer - /// Shipping price - decimal GetShippingPrice(decimal price, Customer customer); - - /// - /// Gets the shipping price - /// - /// Price - /// A value indicating whether calculated price should include tax - /// Customer - /// Shipping price - decimal GetShippingPrice(decimal price, bool includingTax, Customer customer); - - /// - /// Gets the shipping price - /// - /// Price - /// A value indicating whether calculated price should include tax - /// Customer - /// Tax rate - /// Shipping price - decimal GetShippingPrice(decimal price, bool includingTax, Customer customer, out decimal taxRate); - - /// - /// Gets the shipping price - /// - /// Price - /// A value indicating whether calculated price should include tax - /// Customer - /// Tax category id - /// Tax rate - /// Shipping price - decimal GetShippingPrice(decimal price, bool includingTax, Customer customer, int taxCategoryId, out decimal taxRate); - - - - - /// - /// Gets payment method additional handling fee - /// - /// Price - /// Customer - /// Payment fee - decimal GetPaymentMethodAdditionalFee(decimal price, Customer customer); - - /// - /// Gets payment method additional handling fee - /// - /// Price - /// A value indicating whether calculated price should include tax - /// Customer - /// Payment fee - decimal GetPaymentMethodAdditionalFee(decimal price, bool includingTax, Customer customer); - - /// - /// Gets payment method additional handling fee - /// - /// Price - /// A value indicating whether calculated price should include tax - /// Customer - /// Tax rate - /// Payment fee - decimal GetPaymentMethodAdditionalFee(decimal price, bool includingTax, Customer customer, out decimal taxRate); - - /// - /// Gets payment method additional handling fee - /// - /// Price - /// A value indicating whether calculated price should include tax - /// Customer - /// Tax category id - /// Tax rate - /// Payment fee - decimal GetPaymentMethodAdditionalFee(decimal price, bool includingTax, Customer customer, int taxCategoryId, out decimal taxRate); - - - - - /// - /// Gets checkout attribute value price - /// - /// Checkout attribute value - /// Price - decimal GetCheckoutAttributePrice(CheckoutAttributeValue cav); + Customer customer, + Currency currency, + bool priceIncludesTax, + out decimal taxRate); + + + + + /// + /// Gets the shipping price + /// + /// Price + /// Customer + /// Shipping price + decimal GetShippingPrice(decimal price, Customer customer); + + /// + /// Gets the shipping price + /// + /// Price + /// A value indicating whether calculated price should include tax + /// Customer + /// Shipping price + decimal GetShippingPrice(decimal price, bool includingTax, Customer customer); + + /// + /// Gets the shipping price + /// + /// Price + /// A value indicating whether calculated price should include tax + /// Customer + /// Tax rate + /// Shipping price + decimal GetShippingPrice(decimal price, bool includingTax, Customer customer, out decimal taxRate); + + /// + /// Gets the shipping price + /// + /// Price + /// A value indicating whether calculated price should include tax + /// Customer + /// Tax category id + /// Tax rate + /// Shipping price + decimal GetShippingPrice(decimal price, bool includingTax, Customer customer, int taxCategoryId, out decimal taxRate); + + + + + /// + /// Gets payment method additional handling fee + /// + /// Price + /// Customer + /// Payment fee + decimal GetPaymentMethodAdditionalFee(decimal price, Customer customer); + + /// + /// Gets payment method additional handling fee + /// + /// Price + /// A value indicating whether calculated price should include tax + /// Customer + /// Payment fee + decimal GetPaymentMethodAdditionalFee(decimal price, bool includingTax, Customer customer); + + /// + /// Gets payment method additional handling fee + /// + /// Price + /// A value indicating whether calculated price should include tax + /// Customer + /// Tax rate + /// Payment fee + decimal GetPaymentMethodAdditionalFee(decimal price, bool includingTax, Customer customer, out decimal taxRate); + + /// + /// Gets payment method additional handling fee + /// + /// Price + /// A value indicating whether calculated price should include tax + /// Customer + /// Tax category id + /// Tax rate + /// Payment fee + decimal GetPaymentMethodAdditionalFee(decimal price, bool includingTax, Customer customer, int taxCategoryId, out decimal taxRate); + + + + + /// + /// Gets checkout attribute value price + /// + /// Checkout attribute value + /// Price + decimal GetCheckoutAttributePrice(CheckoutAttributeValue cav); /// /// Gets checkout attribute value price @@ -251,7 +251,7 @@ decimal GetCheckoutAttributePrice(CheckoutAttributeValue cav, - + /// /// Gets VAT Number status @@ -276,7 +276,7 @@ VatNumberStatus GetVatNumberStatus(string fullVatNumber, /// VAT number /// VAT Number status VatNumberStatus GetVatNumberStatus(string twoLetterIsoCode, string vatNumber); - + /// /// Gets VAT Number status /// @@ -285,7 +285,7 @@ VatNumberStatus GetVatNumberStatus(string fullVatNumber, /// Name (if received) /// Address (if received) /// VAT Number status - VatNumberStatus GetVatNumberStatus(string twoLetterIsoCode, string vatNumber, + VatNumberStatus GetVatNumberStatus(string twoLetterIsoCode, string vatNumber, out string name, out string address); /// @@ -297,7 +297,7 @@ VatNumberStatus GetVatNumberStatus(string twoLetterIsoCode, string vatNumber, /// Address /// Exception /// VAT number status - VatNumberStatus DoVatCheck(string twoLetterIsoCode, string vatNumber, + VatNumberStatus DoVatCheck(string twoLetterIsoCode, string vatNumber, out string name, out string address, out Exception exception); diff --git a/src/Libraries/SmartStore.Services/Tax/TaxCategoryService.cs b/src/Libraries/SmartStore.Services/Tax/TaxCategoryService.cs index 4038498b9b..30bbe8f8b2 100644 --- a/src/Libraries/SmartStore.Services/Tax/TaxCategoryService.cs +++ b/src/Libraries/SmartStore.Services/Tax/TaxCategoryService.cs @@ -31,21 +31,21 @@ public virtual void DeleteTaxCategory(TaxCategory taxCategory) public virtual IList GetAllTaxCategories() { - var query = from tc in _taxCategoryRepository.Table - orderby tc.DisplayOrder - select tc; + var query = from tc in _taxCategoryRepository.Table + orderby tc.DisplayOrder + select tc; - var taxCategories = query.ToListCached("db.taxcategory.all"); - return taxCategories; - } + var taxCategories = query.ToListCached("db.taxcategory.all"); + return taxCategories; + } public virtual TaxCategory GetTaxCategoryById(int taxCategoryId) { if (taxCategoryId == 0) return null; - return _taxCategoryRepository.GetByIdCached(taxCategoryId, "db.taxcategory.id-" + taxCategoryId); - } + return _taxCategoryRepository.GetByIdCached(taxCategoryId, "db.taxcategory.id-" + taxCategoryId); + } public virtual void InsertTaxCategory(TaxCategory taxCategory) { diff --git a/src/Libraries/SmartStore.Services/Tax/TaxService.cs b/src/Libraries/SmartStore.Services/Tax/TaxService.cs index 663f2113eb..241b85f9fb 100644 --- a/src/Libraries/SmartStore.Services/Tax/TaxService.cs +++ b/src/Libraries/SmartStore.Services/Tax/TaxService.cs @@ -19,55 +19,55 @@ namespace SmartStore.Services.Tax /// Tax service /// public partial class TaxService : ITaxService - { - #region Nested classes + { + #region Nested classes - private class TaxAddressKey : Tuple // - { - public TaxAddressKey(int customerId, bool productIsEsd) - : base(customerId, productIsEsd) - { - } - } + private class TaxAddressKey : Tuple // + { + public TaxAddressKey(int customerId, bool productIsEsd) + : base(customerId, productIsEsd) + { + } + } - #endregion + #endregion - #region Fields + #region Fields - private static readonly DateTime _euEsdRegulationStart = new DateTime(2015, 01, 01); + private static readonly DateTime _euEsdRegulationStart = new DateTime(2015, 01, 01); - private readonly IAddressService _addressService; + private readonly IAddressService _addressService; private readonly IWorkContext _workContext; private readonly TaxSettings _taxSettings; - private readonly ShoppingCartSettings _cartSettings; + private readonly ShoppingCartSettings _cartSettings; private readonly IPluginFinder _pluginFinder; private readonly IDictionary _cachedTaxRates; - private readonly IDictionary _cachedTaxAddresses; - private readonly IProviderManager _providerManager; - private readonly IGeoCountryLookup _geoCountryLookup; + private readonly IDictionary _cachedTaxAddresses; + private readonly IProviderManager _providerManager; + private readonly IGeoCountryLookup _geoCountryLookup; #endregion #region Ctor public TaxService( - IAddressService addressService, + IAddressService addressService, IWorkContext workContext, TaxSettings taxSettings, - ShoppingCartSettings cartSettings, + ShoppingCartSettings cartSettings, IPluginFinder pluginFinder, - IGeoCountryLookup geoCountryLookup, - IProviderManager providerManager) + IGeoCountryLookup geoCountryLookup, + IProviderManager providerManager) { this._addressService = addressService; - this._workContext = workContext; - this._taxSettings = taxSettings; - this._cartSettings = cartSettings; - this._pluginFinder = pluginFinder; - this._cachedTaxRates = new Dictionary(); - this._cachedTaxAddresses = new Dictionary(); - this._providerManager = providerManager; - this._geoCountryLookup = geoCountryLookup; + this._workContext = workContext; + this._taxSettings = taxSettings; + this._cartSettings = cartSettings; + this._pluginFinder = pluginFinder; + this._cachedTaxRates = new Dictionary(); + this._cachedTaxAddresses = new Dictionary(); + this._providerManager = providerManager; + this._geoCountryLookup = geoCountryLookup; } #endregion @@ -119,95 +119,95 @@ protected CalculateTaxRequest CreateCalculateTaxRequest(Product product, int tax return calculateTaxRequest; } - /// - /// Gets a value indicating whether the given customer is a consumer (NOT a business/company) within the EU - /// - /// Customer - /// true if the customer is a consumer, false otherwise - /// - /// A customer is assumed to be a consumer if the default tax address doesn't include a company name, - /// OR if a company name was specified but the EU VAT number for this record is invalid. - /// - protected virtual bool IsEuConsumer(Customer customer) - { - if (customer == null) - return false; - - var address = customer.BillingAddress; - if (address != null && address.Company.IsEmpty()) - { - // BillingAddress is explicitly set, but no CompanyName in there: so we assume a consumer - return true; - } + /// + /// Gets a value indicating whether the given customer is a consumer (NOT a business/company) within the EU + /// + /// Customer + /// true if the customer is a consumer, false otherwise + /// + /// A customer is assumed to be a consumer if the default tax address doesn't include a company name, + /// OR if a company name was specified but the EU VAT number for this record is invalid. + /// + protected virtual bool IsEuConsumer(Customer customer) + { + if (customer == null) + return false; + + var address = customer.BillingAddress; + if (address != null && address.Company.IsEmpty()) + { + // BillingAddress is explicitly set, but no CompanyName in there: so we assume a consumer + return true; + } // No Country or BillingAddress set: try to resolve country from IP address var subjectToVat = _geoCountryLookup.LookupCountry(customer.LastIpAddress)?.IsInEu == true; - if (!subjectToVat) - { - return false; - } + if (!subjectToVat) + { + return false; + } - // It's EU: check VAT number status - var vatStatus = (VatNumberStatus)customer.VatNumberStatusId; - // companies with invalid VAT numbers are assumed to be consumers - return vatStatus != VatNumberStatus.Valid; - } + // It's EU: check VAT number status + var vatStatus = (VatNumberStatus)customer.VatNumberStatusId; + // companies with invalid VAT numbers are assumed to be consumers + return vatStatus != VatNumberStatus.Valid; + } protected virtual Address GetTaxAddress(Customer customer, Product product = null) { - int customerId = customer != null ? customer.Id : 0; - Address address = null; - - bool productIsEsd = product != null ? product.IsEsd : false; - - var cacheKey = new TaxAddressKey(customerId, productIsEsd); - if (_cachedTaxAddresses.TryGetValue(cacheKey, out address)) - { - return address; - } - - var basedOn = _taxSettings.TaxBasedOn; - - // According to the new EU VAT regulations for electronic services from 2015 on, - // VAT must be charged in the EU country the customer originates from (BILLING address). - // In addition to this, the IP addresses' origin should also be checked for verification. - if (DateTime.UtcNow > _euEsdRegulationStart) - { - if (_taxSettings.EuVatEnabled && productIsEsd) - { - if (IsEuConsumer(customer)) - { - basedOn = TaxBasedOn.BillingAddress; - } - } - } - - if (basedOn == TaxBasedOn.BillingAddress && (customer == null || customer.BillingAddress == null)) - { - basedOn = TaxBasedOn.DefaultAddress; - } - if (basedOn == TaxBasedOn.ShippingAddress && (customer == null || customer.ShippingAddress == null)) - { - basedOn = TaxBasedOn.DefaultAddress; - } - - switch (basedOn) - { - case TaxBasedOn.BillingAddress: - address = customer.BillingAddress; - break; - case TaxBasedOn.ShippingAddress: - address = customer.ShippingAddress; - break; - case TaxBasedOn.DefaultAddress: - default: - address = _addressService.GetAddressById(_taxSettings.DefaultTaxAddressId); - break; - } - - _cachedTaxAddresses[cacheKey] = address; - - return address; + int customerId = customer != null ? customer.Id : 0; + Address address = null; + + bool productIsEsd = product != null ? product.IsEsd : false; + + var cacheKey = new TaxAddressKey(customerId, productIsEsd); + if (_cachedTaxAddresses.TryGetValue(cacheKey, out address)) + { + return address; + } + + var basedOn = _taxSettings.TaxBasedOn; + + // According to the new EU VAT regulations for electronic services from 2015 on, + // VAT must be charged in the EU country the customer originates from (BILLING address). + // In addition to this, the IP addresses' origin should also be checked for verification. + if (DateTime.UtcNow > _euEsdRegulationStart) + { + if (_taxSettings.EuVatEnabled && productIsEsd) + { + if (IsEuConsumer(customer)) + { + basedOn = TaxBasedOn.BillingAddress; + } + } + } + + if (basedOn == TaxBasedOn.BillingAddress && (customer == null || customer.BillingAddress == null)) + { + basedOn = TaxBasedOn.DefaultAddress; + } + if (basedOn == TaxBasedOn.ShippingAddress && (customer == null || customer.ShippingAddress == null)) + { + basedOn = TaxBasedOn.DefaultAddress; + } + + switch (basedOn) + { + case TaxBasedOn.BillingAddress: + address = customer.BillingAddress; + break; + case TaxBasedOn.ShippingAddress: + address = customer.ShippingAddress; + break; + case TaxBasedOn.DefaultAddress: + default: + address = _addressService.GetAddressById(_taxSettings.DefaultTaxAddressId); + break; + } + + _cachedTaxAddresses[cacheKey] = address; + + return address; } /// @@ -228,10 +228,10 @@ protected decimal CalculatePrice(decimal price, decimal percent, bool increase, result = price * (1 + percent / 100); } else - { - var decreaseValue = (price) / (100 + percent) * percent; + { + var decreaseValue = (price) / (100 + percent) * percent; result = price - decreaseValue; - } + } // Gross > Net RoundFix result = result.RoundIfEnabledFor(currency); @@ -252,7 +252,7 @@ public virtual Provider LoadActiveTaxProvider() if (taxProvider == null) { taxProvider = LoadAllTaxProviders().FirstOrDefault(); - } + } return taxProvider; } @@ -263,7 +263,7 @@ public virtual Provider LoadActiveTaxProvider() /// Found tax provider public virtual Provider LoadTaxProviderBySystemName(string systemName) { - return _providerManager.GetProvider(systemName); + return _providerManager.GetProvider(systemName); } /// @@ -272,7 +272,7 @@ public virtual Provider LoadTaxProviderBySystemName(string systemN /// Tax providers public virtual IEnumerable> LoadAllTaxProviders() { - return _providerManager.GetAllProviders(); + return _providerManager.GetAllProviders(); } @@ -325,31 +325,31 @@ public virtual decimal GetTaxRate(Product product, int taxCategoryId, Customer c protected virtual decimal GetTaxRateCore(Product product, int taxCategoryId, Customer customer) { - // active tax provider - var activeTaxProvider = LoadActiveTaxProvider(); - if (activeTaxProvider == null) - { - return decimal.Zero; - } + // active tax provider + var activeTaxProvider = LoadActiveTaxProvider(); + if (activeTaxProvider == null) + { + return decimal.Zero; + } if (IsTaxExempt(product, customer)) { return decimal.Zero; } - // tax request + // tax request var calculateTaxRequest = CreateCalculateTaxRequest(product, taxCategoryId, customer); - #region Legacy - ////make EU VAT exempt validation (the European Union Value Added Tax) (VATFIX) + #region Legacy + ////make EU VAT exempt validation (the European Union Value Added Tax) (VATFIX) //if (_taxSettings.EuVatEnabled && IsVatExempt(calculateTaxRequest.Address, calculateTaxRequest.Customer)) //{ // //return zero if VAT is not chargeable // return decimal.Zero; - //} - #endregion + //} + #endregion - //get tax rate + //get tax rate var calculateTaxResult = activeTaxProvider.Value.GetTaxRate(calculateTaxRequest); if (calculateTaxResult.Success) { @@ -390,61 +390,61 @@ public virtual decimal GetProductPrice(Product product, decimal price, Customer return GetProductPrice(product, price, includingTax, customer, out taxRate); } - public virtual decimal GetProductPrice(Product product, decimal price, Customer customer, Currency currency, out decimal taxRate) - { - var includingTax = _workContext.TaxDisplayType == TaxDisplayType.IncludingTax; - var priceIncludesTax = _taxSettings.PricesIncludeTax; - var taxCategoryId = product.TaxCategoryId; // 0; // (VATFIX) + public virtual decimal GetProductPrice(Product product, decimal price, Customer customer, Currency currency, out decimal taxRate) + { + var includingTax = _workContext.TaxDisplayType == TaxDisplayType.IncludingTax; + var priceIncludesTax = _taxSettings.PricesIncludeTax; + var taxCategoryId = product.TaxCategoryId; // 0; // (VATFIX) - return GetProductPrice(product, taxCategoryId, price, includingTax, customer, currency, priceIncludesTax, out taxRate); - } + return GetProductPrice(product, taxCategoryId, price, includingTax, customer, currency, priceIncludesTax, out taxRate); + } - /// - /// Gets price - /// - /// Product - /// Price - /// A value indicating whether calculated price should include tax - /// Customer - /// Tax rate - /// Price - public virtual decimal GetProductPrice(Product product, decimal price, bool includingTax, Customer customer, out decimal taxRate) + /// + /// Gets price + /// + /// Product + /// Price + /// A value indicating whether calculated price should include tax + /// Customer + /// Tax rate + /// Price + public virtual decimal GetProductPrice(Product product, decimal price, bool includingTax, Customer customer, out decimal taxRate) { var priceIncludesTax = _taxSettings.PricesIncludeTax; var taxCategoryId = product.TaxCategoryId; // 0; // (VATFIX) return GetProductPrice(product, taxCategoryId, price, includingTax, customer, _workContext.WorkingCurrency, priceIncludesTax, out taxRate); - } + } - /// - /// Gets price - /// - /// Product - /// Tax category identifier - /// Price - /// A value indicating whether calculated price should include tax - /// Customer - /// A value indicating whether price already includes tax - /// Tax rate - /// Price - public virtual decimal GetProductPrice( - Product product, - int taxCategoryId, - decimal price, - bool includingTax, - Customer customer, - Currency currency, - bool priceIncludesTax, - out decimal taxRate) - { - // don't calculate if price is 0 - if (price == decimal.Zero) - { - taxRate = decimal.Zero; - return decimal.Zero; - } - - taxRate = GetTaxRate(product, taxCategoryId, customer); + /// + /// Gets price + /// + /// Product + /// Tax category identifier + /// Price + /// A value indicating whether calculated price should include tax + /// Customer + /// A value indicating whether price already includes tax + /// Tax rate + /// Price + public virtual decimal GetProductPrice( + Product product, + int taxCategoryId, + decimal price, + bool includingTax, + Customer customer, + Currency currency, + bool priceIncludesTax, + out decimal taxRate) + { + // don't calculate if price is 0 + if (price == decimal.Zero) + { + taxRate = decimal.Zero; + return decimal.Zero; + } + + taxRate = GetTaxRate(product, taxCategoryId, customer); // Admin: GROSS prices if (priceIncludesTax) @@ -473,7 +473,7 @@ public virtual decimal GetProductPrice( - public virtual decimal GetShippingPrice(decimal price, Customer customer) + public virtual decimal GetShippingPrice(decimal price, Customer customer) { var includingTax = (_workContext.TaxDisplayType == TaxDisplayType.IncludingTax); return GetShippingPrice(price, includingTax, customer); @@ -487,32 +487,32 @@ public virtual decimal GetShippingPrice(decimal price, bool includingTax, Custom public virtual decimal GetShippingPrice(decimal price, bool includingTax, Customer customer, out decimal taxRate) { - return GetShippingPrice(price, includingTax, customer, _taxSettings.ShippingTaxClassId, out taxRate); - } + return GetShippingPrice(price, includingTax, customer, _taxSettings.ShippingTaxClassId, out taxRate); + } - public virtual decimal GetShippingPrice(decimal price, bool includingTax, Customer customer, int taxCategoryId, out decimal taxRate) - { - taxRate = decimal.Zero; + public virtual decimal GetShippingPrice(decimal price, bool includingTax, Customer customer, int taxCategoryId, out decimal taxRate) + { + taxRate = decimal.Zero; - if (!_taxSettings.ShippingIsTaxable) - return price; + if (!_taxSettings.ShippingIsTaxable) + return price; - var result = GetProductPrice( - null, - taxCategoryId, - price, - includingTax, - customer, - _workContext.WorkingCurrency, - _taxSettings.ShippingPriceIncludesTax, - out taxRate); + var result = GetProductPrice( + null, + taxCategoryId, + price, + includingTax, + customer, + _workContext.WorkingCurrency, + _taxSettings.ShippingPriceIncludesTax, + out taxRate); - return result; - } + return result; + } - public virtual decimal GetPaymentMethodAdditionalFee(decimal price, Customer customer) + public virtual decimal GetPaymentMethodAdditionalFee(decimal price, Customer customer) { var includingTax = (_workContext.TaxDisplayType == TaxDisplayType.IncludingTax); return GetPaymentMethodAdditionalFee(price, includingTax, customer); @@ -524,12 +524,12 @@ public virtual decimal GetPaymentMethodAdditionalFee(decimal price, bool includi return GetPaymentMethodAdditionalFee(price, includingTax, customer, out taxRate); } - public virtual decimal GetPaymentMethodAdditionalFee(decimal price, bool includingTax, Customer customer, out decimal taxRate) - { - return GetPaymentMethodAdditionalFee(price, includingTax, customer, _taxSettings.PaymentMethodAdditionalFeeTaxClassId, out taxRate); - } + public virtual decimal GetPaymentMethodAdditionalFee(decimal price, bool includingTax, Customer customer, out decimal taxRate) + { + return GetPaymentMethodAdditionalFee(price, includingTax, customer, _taxSettings.PaymentMethodAdditionalFeeTaxClassId, out taxRate); + } - public virtual decimal GetPaymentMethodAdditionalFee(decimal price, bool includingTax, Customer customer, int taxCategoryId, out decimal taxRate) + public virtual decimal GetPaymentMethodAdditionalFee(decimal price, bool includingTax, Customer customer, int taxCategoryId, out decimal taxRate) { taxRate = decimal.Zero; @@ -537,16 +537,16 @@ public virtual decimal GetPaymentMethodAdditionalFee(decimal price, bool includi return price; var result = GetProductPrice( - null, - taxCategoryId, - price, - includingTax, - customer, - _workContext.WorkingCurrency, - _taxSettings.PaymentMethodAdditionalFeeIncludesTax, - out taxRate); + null, + taxCategoryId, + price, + includingTax, + customer, + _workContext.WorkingCurrency, + _taxSettings.PaymentMethodAdditionalFeeIncludesTax, + out taxRate); - return result; + return result; } @@ -605,8 +605,8 @@ public virtual decimal GetCheckoutAttributePrice(CheckoutAttributeValue cav, taxRate = decimal.Zero; var priceIncludesTax = _taxSettings.PricesIncludeTax; - var taxClassId = cav.CheckoutAttribute.TaxCategoryId; - var price = cav.PriceAdjustment; + var taxClassId = cav.CheckoutAttribute.TaxCategoryId; + var price = cav.PriceAdjustment; if (cav.CheckoutAttribute.IsTaxExempt) { @@ -710,23 +710,25 @@ public virtual VatNumberStatus DoVatCheck(string twoLetterIsoCode, string vatNum name = string.Empty; address = string.Empty; - if (vatNumber == null) - vatNumber = string.Empty; - vatNumber = vatNumber.Trim().Replace(" ", ""); + vatNumber = vatNumber.EmptyNull().Replace(" ", ""); if (twoLetterIsoCode == null) + { twoLetterIsoCode = string.Empty; + } if (!String.IsNullOrEmpty(twoLetterIsoCode)) - //The service returns INVALID_INPUT for country codes that are not uppercase. + { + // The service returns INVALID_INPUT for country codes that are not uppercase. twoLetterIsoCode = twoLetterIsoCode.ToUpper(); + } - EuropaCheckVatService.checkVatService s = null; + EuropeCheckVatService.checkVatService s = null; try { bool valid; - s = new EuropaCheckVatService.checkVatService(); + s = new EuropeCheckVatService.checkVatService(); s.checkVat(ref twoLetterIsoCode, ref vatNumber, out valid, out name, out address); exception = null; return valid ? VatNumberStatus.Valid : VatNumberStatus.Invalid; @@ -764,7 +766,7 @@ public virtual bool IsTaxExempt(Product product, Customer customer) if (customer.IsTaxExempt) return true; - if (customer.CustomerRoles.Where(cr => cr.Active).Any(cr => cr.TaxExempt)) + if (customer.CustomerRoleMappings.Select(rm => rm.CustomerRole).Where(cr => cr.Active).Any(cr => cr.TaxExempt)) return true; } diff --git a/src/Libraries/SmartStore.Services/Themes/ThemeValidationException.cs b/src/Libraries/SmartStore.Services/Themes/ThemeValidationException.cs index 060775b69b..fda9d06f1f 100644 --- a/src/Libraries/SmartStore.Services/Themes/ThemeValidationException.cs +++ b/src/Libraries/SmartStore.Services/Themes/ThemeValidationException.cs @@ -3,16 +3,16 @@ namespace SmartStore.Services.Themes { - public class ThemeValidationException : Exception - { - public ThemeValidationException(string message, IDictionary attemptedVars) - : base(message) - { - Guard.NotNull(attemptedVars, nameof(attemptedVars)); + public class ThemeValidationException : Exception + { + public ThemeValidationException(string message, IDictionary attemptedVars) + : base(message) + { + Guard.NotNull(attemptedVars, nameof(attemptedVars)); - AttemptedVars = attemptedVars; - } + AttemptedVars = attemptedVars; + } - public IDictionary AttemptedVars { get; private set; } - } + public IDictionary AttemptedVars { get; private set; } + } } diff --git a/src/Libraries/SmartStore.Services/Themes/ThemeVariablesService.cs b/src/Libraries/SmartStore.Services/Themes/ThemeVariablesService.cs index 79f000af32..5f9b98f6a3 100644 --- a/src/Libraries/SmartStore.Services/Themes/ThemeVariablesService.cs +++ b/src/Libraries/SmartStore.Services/Themes/ThemeVariablesService.cs @@ -21,30 +21,30 @@ public class ThemeVariablesService : IThemeVariablesService private const string THEMEVARS_BY_THEME_KEY = "SmartStore.themevars.theme-{0}-{1}"; private const string THEMEVARS_PATTERN_KEY = "SmartStore.themevars."; - private readonly IRepository _rsVariables; + private readonly IRepository _rsVariables; private readonly IThemeRegistry _themeRegistry; private readonly IRequestCache _requestCache; private readonly IEventPublisher _eventPublisher; - private readonly Lazy _themeFileResolver; - private readonly HttpContextBase _httpContext; - - public ThemeVariablesService( - IRepository rsVariables, - IThemeRegistry themeRegistry, - IRequestCache requestCache, - IEventPublisher eventPublisher, - Lazy themeFileResolver, - HttpContextBase httpContext) + private readonly Lazy _themeFileResolver; + private readonly HttpContextBase _httpContext; + + public ThemeVariablesService( + IRepository rsVariables, + IThemeRegistry themeRegistry, + IRequestCache requestCache, + IEventPublisher eventPublisher, + Lazy themeFileResolver, + HttpContextBase httpContext) { _rsVariables = rsVariables; _themeRegistry = themeRegistry; _requestCache = requestCache; _eventPublisher = eventPublisher; - _themeFileResolver = themeFileResolver; - _httpContext = httpContext; - } + _themeFileResolver = themeFileResolver; + _httpContext = httpContext; + } - public virtual ExpandoObject GetThemeVariables(string themeName, int storeId) + public virtual ExpandoObject GetThemeVariables(string themeName, int storeId) { if (themeName.IsEmpty()) return null; @@ -60,17 +60,18 @@ public virtual ExpandoObject GetThemeVariables(string themeName, int storeId) // first get all default (static) var values from manifest... var manifest = _themeRegistry.GetThemeManifest(themeName); - manifest.Variables.Values.Each(v => { + manifest.Variables.Values.Each(v => + { dict.Add(v.Name, v.DefaultValue); }); // ...then merge with persisted runtime records var query = from v in _rsVariables.TableUntracked - where v.StoreId == storeId && v.Theme.Equals(themeName, StringComparison.OrdinalIgnoreCase) + where v.StoreId == storeId && v.Theme.Equals(themeName, StringComparison.OrdinalIgnoreCase) select v; - query.ToList().Each(v => - { + query.ToList().Each(v => + { if (v.Value.HasValue() && dict.ContainsKey(v.Name)) { dict[v.Name] = v.Value; @@ -83,155 +84,155 @@ public virtual ExpandoObject GetThemeVariables(string themeName, int storeId) public virtual void DeleteThemeVariables(string themeName, int storeId) { - DeleteThemeVariablesInternal(themeName, storeId); - } - - private void DeleteThemeVariablesInternal(string themeName, int storeId) - { - Guard.NotEmpty(themeName, nameof(themeName)); - - var query = from v in _rsVariables.Table - where v.StoreId == storeId && v.Theme == themeName - select v; - - if (query.Any()) - { - using (var scope = new DbContextScope(ctx: _rsVariables.Context, autoCommit: false)) - { - query.Each(v => - { - _rsVariables.Delete(v); - }); - - _requestCache.Remove(THEMEVARS_BY_THEME_KEY.FormatInvariant(themeName, storeId)); - - _rsVariables.Context.SaveChanges(); - } - } - } - - public virtual int SaveThemeVariables(string themeName, int storeId, IDictionary variables) - { - Guard.NotEmpty(themeName, nameof(themeName)); - Guard.Against(!_themeRegistry.ThemeManifestExists(themeName), "The theme '{0}' does not exist in the registry.".FormatInvariant(themeName)); - Guard.NotNull(variables, nameof(variables)); - - if (!variables.Any()) - return 0; - - var manifest = _themeRegistry.GetThemeManifest(themeName); - if (manifest == null) - { - throw new ArgumentException("Theme '{0}' does not exist".FormatInvariant(themeName), nameof(themeName)); - } - - // Get current for later restore on parse error - var currentVars = GetThemeVariables(themeName, storeId); - - // Save - var result = SaveThemeVariablesInternal(manifest, storeId, variables); - - if (result.TouchedVariablesCount > 0) - { - // Check for parsing error - string error = ValidateSass(manifest, storeId); - if (error.HasValue()) - { - // Restore previous vars - try - { - DeleteThemeVariablesInternal(themeName, storeId); - } - finally - { - // We do it here to absolutely ensure that this gets called - SaveThemeVariablesInternal(manifest, storeId, currentVars); - } - - throw new ThemeValidationException(error, variables); - } - } - - return result.TouchedVariablesCount; - } - - private SaveThemeVariablesResult SaveThemeVariablesInternal(ThemeManifest manifest, int storeId, IDictionary variables) + DeleteThemeVariablesInternal(themeName, storeId); + } + + private void DeleteThemeVariablesInternal(string themeName, int storeId) + { + Guard.NotEmpty(themeName, nameof(themeName)); + + var query = from v in _rsVariables.Table + where v.StoreId == storeId && v.Theme == themeName + select v; + + if (query.Any()) + { + using (var scope = new DbContextScope(ctx: _rsVariables.Context, autoCommit: false)) + { + query.Each(v => + { + _rsVariables.Delete(v); + }); + + _requestCache.Remove(THEMEVARS_BY_THEME_KEY.FormatInvariant(themeName, storeId)); + + _rsVariables.Context.SaveChanges(); + } + } + } + + public virtual int SaveThemeVariables(string themeName, int storeId, IDictionary variables) + { + Guard.NotEmpty(themeName, nameof(themeName)); + Guard.Against(!_themeRegistry.ThemeManifestExists(themeName), "The theme '{0}' does not exist in the registry.".FormatInvariant(themeName)); + Guard.NotNull(variables, nameof(variables)); + + if (!variables.Any()) + return 0; + + var manifest = _themeRegistry.GetThemeManifest(themeName); + if (manifest == null) + { + throw new ArgumentException("Theme '{0}' does not exist".FormatInvariant(themeName), nameof(themeName)); + } + + // Get current for later restore on parse error + var currentVars = GetThemeVariables(themeName, storeId); + + // Save + var result = SaveThemeVariablesInternal(manifest, storeId, variables); + + if (result.TouchedVariablesCount > 0) + { + // Check for parsing error + string error = ValidateSass(manifest, storeId); + if (error.HasValue()) + { + // Restore previous vars + try + { + DeleteThemeVariablesInternal(themeName, storeId); + } + finally + { + // We do it here to absolutely ensure that this gets called + SaveThemeVariablesInternal(manifest, storeId, currentVars); + } + + throw new ThemeValidationException(error, variables); + } + } + + return result.TouchedVariablesCount; + } + + private SaveThemeVariablesResult SaveThemeVariablesInternal(ThemeManifest manifest, int storeId, IDictionary variables) { - var result = new SaveThemeVariablesResult(); + var result = new SaveThemeVariablesResult(); var infos = manifest.Variables; - using (var scope = new DbContextScope(ctx: _rsVariables.Context, autoCommit: false)) - { - var unsavedVars = new List(); - var savedThemeVars = _rsVariables.Table - .Where(v => v.StoreId == storeId && v.Theme == manifest.ThemeName) - .ToDictionary(x => x.Name); - - bool touched = false; - - foreach (var v in variables) - { - ThemeVariableInfo info; - if (!infos.TryGetValue(v.Key, out info)) - { - // var not specified in metadata so don't save - // TODO: (MC) delete from db also if it exists - continue; - } - - var value = v.Value == null ? string.Empty : v.Value.ToString(); - - var savedThemeVar = savedThemeVars.Get(v.Key); - if (savedThemeVar != null) - { - if (value.IsEmpty() || String.Equals(info.DefaultValue, value, StringComparison.CurrentCultureIgnoreCase)) - { - // it's either null or the default value, so delete - _rsVariables.Delete(savedThemeVar); - result.Deleted.Add(savedThemeVar); - touched = true; - } - else - { - // update entity - if (!savedThemeVar.Value.Equals(value, StringComparison.OrdinalIgnoreCase)) - { - savedThemeVar.Value = value; - result.Updated.Add(savedThemeVar); - touched = true; - } - } - } - else - { - if (value.HasValue() && !String.Equals(info.DefaultValue, value, StringComparison.CurrentCultureIgnoreCase)) - { - // insert entity (only when not default value) - unsavedVars.Add(v.Key); - savedThemeVar = new ThemeVariable - { - Theme = manifest.ThemeName, - Name = v.Key, - Value = value, - StoreId = storeId - }; - _rsVariables.Insert(savedThemeVar); - result.Inserted.Add(savedThemeVar); - touched = true; - } - } - } - - if (touched) - { - _rsVariables.Context.SaveChanges(); - } - } + using (var scope = new DbContextScope(ctx: _rsVariables.Context, autoCommit: false)) + { + var unsavedVars = new List(); + var savedThemeVars = _rsVariables.Table + .Where(v => v.StoreId == storeId && v.Theme == manifest.ThemeName) + .ToDictionary(x => x.Name); + + bool touched = false; + + foreach (var v in variables) + { + ThemeVariableInfo info; + if (!infos.TryGetValue(v.Key, out info)) + { + // var not specified in metadata so don't save + // TODO: (MC) delete from db also if it exists + continue; + } + + var value = v.Value == null ? string.Empty : v.Value.ToString(); + + var savedThemeVar = savedThemeVars.Get(v.Key); + if (savedThemeVar != null) + { + if (value.IsEmpty() || String.Equals(info.DefaultValue, value, StringComparison.CurrentCultureIgnoreCase)) + { + // it's either null or the default value, so delete + _rsVariables.Delete(savedThemeVar); + result.Deleted.Add(savedThemeVar); + touched = true; + } + else + { + // update entity + if (!savedThemeVar.Value.Equals(value, StringComparison.OrdinalIgnoreCase)) + { + savedThemeVar.Value = value; + result.Updated.Add(savedThemeVar); + touched = true; + } + } + } + else + { + if (value.HasValue() && !String.Equals(info.DefaultValue, value, StringComparison.CurrentCultureIgnoreCase)) + { + // insert entity (only when not default value) + unsavedVars.Add(v.Key); + savedThemeVar = new ThemeVariable + { + Theme = manifest.ThemeName, + Name = v.Key, + Value = value, + StoreId = storeId + }; + _rsVariables.Insert(savedThemeVar); + result.Inserted.Add(savedThemeVar); + touched = true; + } + } + } + + if (touched) + { + _rsVariables.Context.SaveChanges(); + } + } return result; } - public int ImportVariables(string themeName, int storeId, string configurationXml) + public int ImportVariables(string themeName, int storeId, string configurationXml) { Guard.NotEmpty(themeName, nameof(themeName)); Guard.NotEmpty(configurationXml, nameof(configurationXml)); @@ -279,7 +280,7 @@ public string ExportVariables(string themeName, int storeId) using (var xmlWriter = XmlWriter.Create(sb)) { - + xmlWriter.WriteStartDocument(); xmlWriter.WriteStartElement("ThemeVars"); xmlWriter.WriteAttributeString("for", themeName); @@ -311,88 +312,85 @@ public string ExportVariables(string themeName, int storeId) } - /// - /// Validates the result SASS file by calling it's url. - /// - /// Theme name - /// Stored Id - /// The error message when a parsing error occured, null otherwise - private string ValidateSass(ThemeManifest manifest, int storeId) - { - string error = string.Empty; - - var virtualPath = "~/Themes/{0}/Content/theme.scss".FormatCurrent(manifest.ThemeName); - var resolver = _themeFileResolver.Value; - var file = resolver.Resolve(virtualPath); - if (file != null) - { - virtualPath = file.ResultVirtualPath; - } - - var url = "{0}?storeId={1}&theme={2}&validate=1".FormatInvariant( - WebHelper.GetAbsoluteUrl(virtualPath, _httpContext.Request), - storeId, - manifest.ThemeName); - - var request = WebHelper.CreateHttpRequestForSafeLocalCall(new Uri(url)); - WebResponse response = null; - - try - { - response = request.GetResponse(); - } - catch (WebException ex) - { - if (ex.Response is HttpWebResponse) - { - var webResponse = (HttpWebResponse)ex.Response; - var statusCode = webResponse.StatusCode; - - if (statusCode == HttpStatusCode.InternalServerError) - { - // catch only 500, as this indicates a parsing error. - var stream = webResponse.GetResponseStream(); - - using (var streamReader = new StreamReader(stream)) - { - // read the content (the error message has been put there) - error = streamReader.ReadToEnd(); - streamReader.Close(); - stream.Close(); - } - } - } - } - catch (Exception ex) - { - error = ex.Message; - } - finally - { - if (response != null) - response.Close(); - } - - return error; - } - } - - class SaveThemeVariablesResult - { - public SaveThemeVariablesResult() - { - Inserted = new List(); - Updated = new List(); - Deleted = new List(); - } - - public IList Inserted { get; private set; } - public IList Updated { get; private set; } - public IList Deleted { get; private set; } - - public int TouchedVariablesCount - { - get { return Inserted.Count + Updated.Count + Deleted.Count; } - } - } + /// + /// Validates the result SASS file by calling it's url. + /// + /// Theme name + /// Stored Id + /// The error message when a parsing error occured, null otherwise + private string ValidateSass(ThemeManifest manifest, int storeId) + { + string error = string.Empty; + + var virtualPath = "~/Themes/{0}/Content/theme.scss".FormatCurrent(manifest.ThemeName); + var resolver = _themeFileResolver.Value; + var file = resolver.Resolve(virtualPath); + if (file != null) + { + virtualPath = file.ResultVirtualPath; + } + + var url = "{0}?storeId={1}&theme={2}&validate=1".FormatInvariant( + WebHelper.GetAbsoluteUrl(virtualPath, _httpContext.Request), + storeId, + manifest.ThemeName); + + var request = WebHelper.CreateHttpRequestForSafeLocalCall(new Uri(url)); + WebResponse response = null; + + try + { + response = request.GetResponse(); + } + catch (WebException ex) + { + if (ex.Response is HttpWebResponse) + { + var webResponse = (HttpWebResponse)ex.Response; + var statusCode = webResponse.StatusCode; + + if (statusCode == HttpStatusCode.InternalServerError) + { + // catch only 500, as this indicates a parsing error. + var stream = webResponse.GetResponseStream(); + + using (var streamReader = new StreamReader(stream)) + { + // read the content (the error message has been put there) + error = streamReader.ReadToEnd(); + streamReader.Close(); + stream.Close(); + } + } + } + } + catch (Exception ex) + { + error = ex.Message; + } + finally + { + if (response != null) + response.Close(); + } + + return error; + } + } + + class SaveThemeVariablesResult + { + public SaveThemeVariablesResult() + { + Inserted = new List(); + Updated = new List(); + Deleted = new List(); + } + + public IList Inserted { get; private set; } + public IList Updated { get; private set; } + public IList Deleted { get; private set; } + + public int TouchedVariablesCount => Inserted.Count + Updated.Count + Deleted.Count; + } } diff --git a/src/Libraries/SmartStore.Services/Topics/ITopicService.cs b/src/Libraries/SmartStore.Services/Topics/ITopicService.cs index ccdd2e3588..7eede60f7a 100644 --- a/src/Libraries/SmartStore.Services/Topics/ITopicService.cs +++ b/src/Libraries/SmartStore.Services/Topics/ITopicService.cs @@ -22,28 +22,28 @@ public partial interface ITopicService /// Topic Topic GetTopicById(int topicId); - /// - /// Gets a topic - /// - /// The topic system name - /// Store identifier - /// Whether to check for permission (ACL). If true and check fails, null is returned. - /// Topic - Topic GetTopicBySystemName(string systemName, int storeId = 0, bool checkPermission = true); + /// + /// Gets a topic + /// + /// The topic system name + /// Store identifier + /// Whether to check for permission (ACL). If true and check fails, null is returned. + /// Topic + Topic GetTopicBySystemName(string systemName, int storeId = 0, bool checkPermission = true); - /// - /// Gets all topics - /// - /// Store identifier; pass 0 to load all records - /// Whether to load hidden records - /// Topics - IPagedList GetAllTopics(int storeId = 0, int pageIndex = 0, int pageSize = int.MaxValue, bool showHidden = false); + /// + /// Gets all topics + /// + /// Store identifier; pass 0 to load all records + /// Whether to load hidden records + /// Topics + IPagedList GetAllTopics(int storeId = 0, int pageIndex = 0, int pageSize = int.MaxValue, bool showHidden = false); - /// - /// Inserts a topic - /// - /// Topic - void InsertTopic(Topic topic); + /// + /// Inserts a topic + /// + /// Topic + void InsertTopic(Topic topic); /// /// Updates the topic diff --git a/src/Libraries/SmartStore.Services/Topics/TopicService.cs b/src/Libraries/SmartStore.Services/Topics/TopicService.cs index 07152bf79b..b91a15d2e1 100644 --- a/src/Libraries/SmartStore.Services/Topics/TopicService.cs +++ b/src/Libraries/SmartStore.Services/Topics/TopicService.cs @@ -8,40 +8,43 @@ using SmartStore.Core.Domain.Topics; using SmartStore.Data.Caching; using SmartStore.Services.Stores; +using SmartStore.Services.Seo; +using SmartStore.Core.Domain.Seo; namespace SmartStore.Services.Topics { - public partial class TopicService : ITopicService + public partial class TopicService : ITopicService, IXmlSitemapPublisher { - private readonly ICommonServices _services; - private readonly IRepository _topicRepository; - private readonly IRepository _storeMappingRepository; - private readonly IStoreMappingService _storeMappingService; - private readonly IRepository _aclRepository; - - public TopicService( - ICommonServices services, - IRepository topicRepository, - IRepository storeMappingRepository, - IStoreMappingService storeMappingService, - IRepository aclRepository) + private readonly ICommonServices _services; + private readonly IRepository _topicRepository; + private readonly IRepository _storeMappingRepository; + private readonly IStoreMappingService _storeMappingService; + private readonly IRepository _aclRepository; + + public TopicService( + ICommonServices services, + IRepository topicRepository, + IRepository storeMappingRepository, + IStoreMappingService storeMappingService, + IRepository aclRepository, + SeoSettings seoSettings) { - _services = services; - _topicRepository = topicRepository; - _storeMappingRepository = storeMappingRepository; - _storeMappingService = storeMappingService; - _aclRepository = aclRepository; + _services = services; + _topicRepository = topicRepository; + _storeMappingRepository = storeMappingRepository; + _storeMappingService = storeMappingService; + _aclRepository = aclRepository; - this.QuerySettings = DbQuerySettings.Default; - } + this.QuerySettings = DbQuerySettings.Default; + } - public DbQuerySettings QuerySettings { get; set; } + public DbQuerySettings QuerySettings { get; set; } public virtual void DeleteTopic(Topic topic) { - Guard.NotNull(topic, nameof(topic)); + Guard.NotNull(topic, nameof(topic)); - _topicRepository.Delete(topic); + _topicRepository.Delete(topic); } public virtual Topic GetTopicById(int topicId) @@ -52,93 +55,131 @@ public virtual Topic GetTopicById(int topicId) return _topicRepository.GetByIdCached(topicId, "db.topic.id-" + topicId); } - public virtual Topic GetTopicBySystemName(string systemName, int storeId = 0, bool checkPermission = true) + public virtual Topic GetTopicBySystemName(string systemName, int storeId = 0, bool checkPermission = true) { - if (systemName.IsEmpty()) - return null; + if (systemName.IsEmpty()) + return null; + + var query = BuildTopicsQuery(systemName, storeId, !checkPermission); + var rolesIdent = checkPermission + ? "0" + : _services.WorkContext.CurrentCustomer.GetRolesIdent(); - var query = BuildTopicsQuery(systemName, storeId, !checkPermission); - var rolesIdent = checkPermission - ? "0" - : _services.WorkContext.CurrentCustomer.GetRolesIdent(); - - var result = query.FirstOrDefaultCached("db.topic.bysysname-{0}-{1}-{2}".FormatInvariant(systemName, storeId, rolesIdent)); + var result = query.FirstOrDefaultCached("db.topic.bysysname-{0}-{1}-{2}".FormatInvariant(systemName, storeId, rolesIdent)); - return result; + return result; } - public virtual IPagedList GetAllTopics(int storeId = 0, int pageIndex = 0, int pageSize = int.MaxValue, bool showHidden = false) + public virtual IPagedList GetAllTopics(int storeId = 0, int pageIndex = 0, int pageSize = int.MaxValue, bool showHidden = false) { - var query = BuildTopicsQuery(null, storeId, showHidden); - return new PagedList(query, pageIndex, pageSize); - } - - protected virtual IQueryable BuildTopicsQuery(string systemName, int storeId, bool showHidden = false) - { - var entityName = nameof(Topic); - var joinApplied = false; - - var query = _topicRepository.Table.Where(x => showHidden || x.IsPublished); - - if (systemName.HasValue()) - { - query = query.Where(x => x.SystemName == systemName); - } - - // Store mapping - if (storeId > 0 && !QuerySettings.IgnoreMultiStore) - { - query = from t in query - join m in _storeMappingRepository.Table - on new { c1 = t.Id, c2 = "Topic" } equals new { c1 = m.EntityId, c2 = m.EntityName } into tm - from m in tm.DefaultIfEmpty() - where !t.LimitedToStores || storeId == m.StoreId - select t; - - joinApplied = true; - } - - // ACL (access control list) - if (!showHidden && !QuerySettings.IgnoreAcl) - { - var allowedCustomerRolesIds = _services.WorkContext.CurrentCustomer.CustomerRoles.Where(x => x.Active).Select(x => x.Id).ToList(); - - query = from c in query - join a in _aclRepository.Table - on new { c1 = c.Id, c2 = entityName } equals new { c1 = a.EntityId, c2 = a.EntityName } into ca - from a in ca.DefaultIfEmpty() - where !c.SubjectToAcl || allowedCustomerRolesIds.Contains(a.CustomerRoleId) - select c; - - joinApplied = true; - } - - if (joinApplied) - { - // Only distinct topics (group by ID) - query = from t in query - group t by t.Id into tGroup - orderby tGroup.Key - select tGroup.FirstOrDefault(); - } - - query = query.OrderBy(t => t.Priority).ThenBy(t => t.SystemName); - - return query; - } + var query = BuildTopicsQuery(null, storeId, showHidden); + return new PagedList(query, pageIndex, pageSize); + } + + protected virtual IQueryable BuildTopicsQuery(string systemName, int storeId, bool showHidden = false) + { + var entityName = nameof(Topic); + var joinApplied = false; + + var query = _topicRepository.Table.Where(x => showHidden || x.IsPublished); + + if (systemName.HasValue()) + { + query = query.Where(x => x.SystemName == systemName); + } + + // Store mapping + if (storeId > 0 && !QuerySettings.IgnoreMultiStore) + { + query = from t in query + join m in _storeMappingRepository.Table + on new { c1 = t.Id, c2 = "Topic" } equals new { c1 = m.EntityId, c2 = m.EntityName } into tm + from m in tm.DefaultIfEmpty() + where !t.LimitedToStores || storeId == m.StoreId + select t; + + joinApplied = true; + } + + // ACL (access control list) + if (!showHidden && !QuerySettings.IgnoreAcl) + { + var allowedCustomerRolesIds = _services.WorkContext.CurrentCustomer.GetRoleIds(); + + query = from c in query + join a in _aclRepository.Table + on new { c1 = c.Id, c2 = entityName } equals new { c1 = a.EntityId, c2 = a.EntityName } into ca + from a in ca.DefaultIfEmpty() + where !c.SubjectToAcl || allowedCustomerRolesIds.Contains(a.CustomerRoleId) + select c; + + joinApplied = true; + } + + if (joinApplied) + { + // Only distinct topics (group by ID) + query = from t in query + group t by t.Id into tGroup + orderby tGroup.Key + select tGroup.FirstOrDefault(); + } + + query = query.OrderBy(t => t.Priority).ThenBy(t => t.SystemName); + + return query; + } public virtual void InsertTopic(Topic topic) { - Guard.NotNull(topic, nameof(topic)); + Guard.NotNull(topic, nameof(topic)); - _topicRepository.Insert(topic); + _topicRepository.Insert(topic); } public virtual void UpdateTopic(Topic topic) { - Guard.NotNull(topic, nameof(topic)); + Guard.NotNull(topic, nameof(topic)); + + _topicRepository.Update(topic); + } + + #region XML Sitemap + + public XmlSitemapProvider PublishXmlSitemap(XmlSitemapBuildContext context) + { + if (!context.LoadSetting().XmlSitemapIncludesTopics) + return null; + + var query = GetAllTopics(context.RequestStoreId).AlterQuery(q => + { + return q.Where(t => t.IncludeInSitemap && !t.RenderAsWidget); + }).SourceQuery; - _topicRepository.Update(topic); + return new TopicXmlSitemapResult { Query = query }; } + + class TopicXmlSitemapResult : XmlSitemapProvider + { + public IQueryable Query { get; set; } + + public override int GetTotalCount() + { + return Query.Count(); + } + + public override IEnumerable Enlist() + { + var topics = Query.Select(x => new { x.Id }).ToList(); + foreach (var x in topics) + { + yield return new NamedEntity { EntityName = "Topic", Id = x.Id, LastMod = DateTime.UtcNow }; + } + } + + public override int Order => 200; + } + + #endregion } } diff --git a/src/Libraries/SmartStore.Services/Web References/EuropaCheckVatService/Reference.map b/src/Libraries/SmartStore.Services/Web References/EuropaCheckVatService/Reference.map deleted file mode 100644 index 357ddc8242..0000000000 --- a/src/Libraries/SmartStore.Services/Web References/EuropaCheckVatService/Reference.map +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Web References/EuropaCheckVatService/checkVatService.wsdl b/src/Libraries/SmartStore.Services/Web References/EuropaCheckVatService/checkVatService.wsdl deleted file mode 100644 index 9cbb53a2ef..0000000000 --- a/src/Libraries/SmartStore.Services/Web References/EuropaCheckVatService/checkVatService.wsdl +++ /dev/null @@ -1,167 +0,0 @@ - - - - Specific disclaimer for this service ----------------------------------------- The - objective of this Internet site is to allow persons involved in the intra-Community supply of - goods or of services to obtain confirmation of the validity of the VAT identification number of - any specified person, in accordance to article 27 of Council Regulation (EC) No. 1798/2003 of 7 - October 2003. Any other use and any extraction and use of the data which is not in conformity - with the objective of this site is strictly forbidden. Any retransmission of the contents of - this site, whether for a commercial purpose or otherwise, as well as any more general use other - than as far as is necessary to support the activity of a legitimate user (for example: to draw - up their own invoices) is expressly forbidden. In addition, any copying or reproduction of the - contents of this site is strictly forbidden. The European Commission maintains this website to - enhance the access by taxable persons making intra-Community supplies to verification of their - customers VAT identification numbers. Our goal is to supply instantaneous and accurate - information. However the Commission accepts no responsibility or liability whatsoever with - regard to the information obtained using this site. This information: - is obtained from Member - States databases over which the Commission services have no control and for which the Commission - assumes no responsibility; it is the responsibility of the Member States to keep their databases - complete, accurate and up to date; - is not professional or legal advice (if you need specific - advice, you should always consult a suitably qualified professional); - does not in itself give - a right to exempt intra-Community supplies from Value Added Tax; - does not change any - obligations imposed on taxable persons in relation to intra-Community supplies. It is our goal - to minimise disruption caused by technical errors. However some data or information on our site - may have been created or structured in files or formats which are not error-free and we cannot - guarantee that our service will not be interrupted or otherwise affected by such problems. The - Commission accepts no responsibility with regard to such problems incurred as a result of using - this site or any linked external sites. This disclaimer is not intended to limit the liability - of the Commission in contravention of any requirements laid down in applicable national law nor - to exclude its liability for matters which may not be excluded under that law. Usage: The - countryCode input parameter must follow the pattern [A-Z]{2} The vatNumber input parameter must - follow the [0-9A-Za-z\+\*\.]{2,12} In case of problem, the returned FaultString can take the - following specific values: - INVALID_INPUT: The provided CountryCode is invalid or the VAT - number is empty; - SERVICE_UNAVAILABLE: The SOAP service is unavailable, try again later; - - MS_UNAVAILABLE: The Member State service is unavailable, try again later or with another Member - State; - TIMEOUT: The Member State service could not be reach in time, try again later or with - another Member State; - SERVER_BUSY: The service can't process your request. Try again latter. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - VALID - - - - - INVALID - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Web References/EuropaCheckVatService/Reference.cs b/src/Libraries/SmartStore.Services/Web References/EuropeCheckVatService/Reference.cs similarity index 98% rename from src/Libraries/SmartStore.Services/Web References/EuropaCheckVatService/Reference.cs rename to src/Libraries/SmartStore.Services/Web References/EuropeCheckVatService/Reference.cs index 02ecf2d637..ce14537202 100644 --- a/src/Libraries/SmartStore.Services/Web References/EuropaCheckVatService/Reference.cs +++ b/src/Libraries/SmartStore.Services/Web References/EuropeCheckVatService/Reference.cs @@ -13,7 +13,7 @@ // #pragma warning disable 1591 -namespace SmartStore.Services.EuropaCheckVatService { +namespace SmartStore.Services.EuropeCheckVatService { using System; using System.Web.Services; using System.Diagnostics; @@ -23,10 +23,10 @@ namespace SmartStore.Services.EuropaCheckVatService { /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.6.1055.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.8.3752.0")] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] - [System.Web.Services.WebServiceBindingAttribute(Name="checkVatBinding", Namespace="urn:ec.europa.eu:taxud:vies:services:checkVat:types")] + [System.Web.Services.WebServiceBindingAttribute(Name="checkVatBinding", Namespace="urn:ec.europa.eu:taxud:vies:services:checkVat")] public partial class checkVatService : System.Web.Services.Protocols.SoapHttpClientProtocol { private System.Threading.SendOrPostCallback checkVatOperationCompleted; @@ -37,7 +37,7 @@ public partial class checkVatService : System.Web.Services.Protocols.SoapHttpCli /// public checkVatService() { - this.Url = global::SmartStore.Services.Properties.Settings.Default.Nop_Services_EuropaCheckVatService_checkVatService; + this.Url = global::SmartStore.Services.Properties.Settings.Default.SmartStore_Services_EuropeCheckVatService_checkVatService; if ((this.IsLocalFileSystemWebService(this.Url) == true)) { this.UseDefaultCredentials = true; this.useDefaultCredentialsSetExplicitly = false; @@ -222,7 +222,7 @@ private bool IsLocalFileSystemWebService(string url) { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.6.1064.2")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.8.3752.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:ec.europa.eu:taxud:vies:services:checkVat:types")] public enum matchCode { @@ -234,14 +234,18 @@ public enum matchCode { /// [System.Xml.Serialization.XmlEnumAttribute("2")] Item2, + + /// + [System.Xml.Serialization.XmlEnumAttribute("3")] + Item3, } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.6.1055.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.8.3752.0")] public delegate void checkVatCompletedEventHandler(object sender, checkVatCompletedEventArgs e); /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.6.1055.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.8.3752.0")] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] public partial class checkVatCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs { @@ -303,11 +307,11 @@ public string address { } /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.6.1055.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.8.3752.0")] public delegate void checkVatApproxCompletedEventHandler(object sender, checkVatApproxCompletedEventArgs e); /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.6.1055.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "4.8.3752.0")] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] public partial class checkVatApproxCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs { diff --git a/src/Libraries/SmartStore.Services/Web References/EuropeCheckVatService/Reference.map b/src/Libraries/SmartStore.Services/Web References/EuropeCheckVatService/Reference.map new file mode 100644 index 0000000000..5d20fb7ab0 --- /dev/null +++ b/src/Libraries/SmartStore.Services/Web References/EuropeCheckVatService/Reference.map @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Web References/EuropeCheckVatService/checkVatService.wsdl b/src/Libraries/SmartStore.Services/Web References/EuropeCheckVatService/checkVatService.wsdl new file mode 100644 index 0000000000..25691c25ff --- /dev/null +++ b/src/Libraries/SmartStore.Services/Web References/EuropeCheckVatService/checkVatService.wsdl @@ -0,0 +1,161 @@ + + + + The objective of this Internet site is to allow persons involved in the intra-Community supply of goods or of services to obtain confirmation of the validity of the VAT identification number of any specified person, in accordance to article 31 of Council Regulation (EC) No. 904/2010 of 7 October 2010. \n + Any other use and any extraction and use of the data which is not in conformity with the objective of this site is strictly forbidden. + Any retransmission of the contents of this site, whether for a commercial purpose or otherwise, as well as any more general use other than as far as is necessary to support the activity of a legitimate user (for example: to draw up their own invoices) is expressly forbidden. In addition, any copying or reproduction of the contents of this site is strictly forbidden. + The European Commission maintains this website to enhance the access by taxable persons making intra-Community supplies to verification of their customers' VAT identification numbers. Our goal is to supply instantaneous and accurate information. + However the Commission accepts no responsibility or liability whatsoever with regard to the information obtained using this site. This information: + - is obtained from Member States' databases over which the Commission services have no control and for which the Commission assumes no responsibility; it is the responsibility of the Member States to keep their databases complete, accurate and up to date; + - is not professional or legal advice (if you need specific advice, you should always consult a suitably qualified professional); + - does not in itself give a right to exempt intra-Community supplies from Value Added Tax; + - does not change any obligations imposed on taxable persons in relation to intra-Community supplies. + It is our goal to minimise disruption caused by technical errors. However some data or information on our site may have been created or structured in files or formats which are not error-free and we cannot guarantee that our service will not be interrupted or otherwise affected by such problems. The Commission accepts no responsibility with regard to such problems incurred as a result of using this site or any linked external sites. + This disclaimer is not intended to limit the liability of the Commission in contravention of any requirements laid down in applicable national law nor to exclude its liability for matters which may not be excluded under that law. + Collecting or handling personal data falls under the Data Protection Notice. This data protection declaration explains the Processing in the VIES-on-the-web Internet Website of VAT Identification Numbers for intra-Community Transaction on Goods or Services. Details of your legal rights associated with the collection, processing and use of this data are also provided: http://ec.europa.eu/dpo-register/details.htm?id=40647 . + + Usage: + The countryCode input parameter must follow the pattern [A-Z]{2} + The vatNumber input parameter must follow the pattern [0-9A-Za-z\+\*\.]{2,12} + In case of problems, the returned FaultString can take the following specific values: + - INVALID_INPUT: The provided CountryCode is invalid or the VAT number is empty; + - GLOBAL_MAX_CONCURRENT_REQ: Your Request for VAT validation has not been processed; the maximum number of concurrent requests has been reached. Please re-submit your request later or contact TAXUD-VIESWEB@ec.europa.eu for further information": Your request cannot be processed due to high traffic on the web application. Please try again later; + - MS_MAX_CONCURRENT_REQ: Your Request for VAT validation has not been processed; the maximum number of concurrent requests for this Member State has been reached. Please re-submit your request later or contact TAXUD-VIESWEB@ec.europa.eu for further information": Your request cannot be processed due to high traffic towards the Member State you are trying to reach. Please try again later. + - SERVICE_UNAVAILABLE: an error was encountered either at the network level or the Web application level, try again later; + - MS_UNAVAILABLE: The application at the Member State is not replying or not available. Please refer to the Technical Information page to check the status of the requested Member State, try again later; + - TIMEOUT: The application did not receive a reply within the allocated time period, try again later. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VALID + + + + + INVALID + + + + + NOT_PROCESSED + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/Web References/EuropaCheckVatService/matchCode.datasource b/src/Libraries/SmartStore.Services/Web References/EuropeCheckVatService/matchCode.datasource similarity index 80% rename from src/Libraries/SmartStore.Services/Web References/EuropaCheckVatService/matchCode.datasource rename to src/Libraries/SmartStore.Services/Web References/EuropeCheckVatService/matchCode.datasource index 39c1b02382..8c0fe5afe2 100644 --- a/src/Libraries/SmartStore.Services/Web References/EuropaCheckVatService/matchCode.datasource +++ b/src/Libraries/SmartStore.Services/Web References/EuropeCheckVatService/matchCode.datasource @@ -6,5 +6,5 @@ cause the file to be unrecognizable by the program. --> - SmartStore.Services.EuropaCheckVatService.matchCode, Web References.EuropaCheckVatService.Reference.cs.dll, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + SmartStore.Services.EuropeCheckVatService.matchCode, Web References.EuropeCheckVatService.Reference.cs.dll, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null \ No newline at end of file diff --git a/src/Libraries/SmartStore.Services/app.config b/src/Libraries/SmartStore.Services/app.config index 08f9834796..446c32df9a 100644 --- a/src/Libraries/SmartStore.Services/app.config +++ b/src/Libraries/SmartStore.Services/app.config @@ -9,10 +9,7 @@ - - http://ec.europa.eu/taxation_customs/vies/services/checkVatService - - + http://ec.europa.eu/taxation_customs/vies/services/checkVatService @@ -29,11 +26,7 @@ - - - - - + @@ -41,7 +34,19 @@ - + + + + + + + + + + + + + @@ -51,4 +56,4 @@ - + diff --git a/src/Libraries/SmartStore.Services/packages.config b/src/Libraries/SmartStore.Services/packages.config index be5db3727d..4046ac6a7f 100644 --- a/src/Libraries/SmartStore.Services/packages.config +++ b/src/Libraries/SmartStore.Services/packages.config @@ -1,27 +1,30 @@  - - - - + + + + - + + - - + + - + + - - + + - - - + + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/AmazonPaySettings.cs b/src/Plugins/SmartStore.AmazonPay/AmazonPaySettings.cs index 21cce48dbe..810c633b6f 100644 --- a/src/Plugins/SmartStore.AmazonPay/AmazonPaySettings.cs +++ b/src/Plugins/SmartStore.AmazonPay/AmazonPaySettings.cs @@ -3,63 +3,63 @@ namespace SmartStore.AmazonPay { - public class AmazonPaySettings : ISettings - { - public AmazonPaySettings() - { - Marketplace = "de"; - DataFetching = AmazonPayDataFetchingType.Ipn; - TransactionType = AmazonPayTransactionType.Authorize; - AuthorizeMethod = AmazonPayAuthorizeMethod.Omnichronous; - SaveEmailAndPhone = AmazonPaySaveDataType.OnlyIfEmpty; - AddOrderNotes = true; - InformCustomerAboutErrors = true; - InformCustomerAddErrors = true; - PollingMaxOrderCreationDays = 31; + public class AmazonPaySettings : ISettings + { + public AmazonPaySettings() + { + Marketplace = "de"; + DataFetching = AmazonPayDataFetchingType.Ipn; + TransactionType = AmazonPayTransactionType.Authorize; + AuthorizeMethod = AmazonPayAuthorizeMethod.Omnichronous; + SaveEmailAndPhone = AmazonPaySaveDataType.OnlyIfEmpty; + AddOrderNotes = true; + InformCustomerAboutErrors = true; + InformCustomerAddErrors = true; + PollingMaxOrderCreationDays = 31; - PayButtonColor = "Gold"; - PayButtonSize = "small"; - AuthButtonType = "LwA"; - AuthButtonColor = "Gold"; - AuthButtonSize = "medium"; - } + PayButtonColor = "Gold"; + PayButtonSize = "small"; + AuthButtonType = "LwA"; + AuthButtonColor = "Gold"; + AuthButtonSize = "medium"; + } - public bool UseSandbox { get; set; } + public bool UseSandbox { get; set; } - public string SellerId { get; set; } - public string AccessKey { get; set; } - public string SecretKey { get; set; } - public string ClientId { get; set; } - public string Marketplace { get; set; } - - public AmazonPayDataFetchingType DataFetching { get; set; } - public AmazonPayTransactionType TransactionType { get; set; } - public AmazonPayAuthorizeMethod AuthorizeMethod { get; set; } + public string SellerId { get; set; } + public string AccessKey { get; set; } + public string SecretKey { get; set; } + public string ClientId { get; set; } + public string Marketplace { get; set; } - public AmazonPaySaveDataType? SaveEmailAndPhone { get; set; } - public bool ShowPayButtonForAdminOnly { get; set; } - public bool ShowButtonInMiniShoppingCart { get; set; } + public AmazonPayDataFetchingType DataFetching { get; set; } + public AmazonPayTransactionType TransactionType { get; set; } + public AmazonPayAuthorizeMethod AuthorizeMethod { get; set; } - public int PollingMaxOrderCreationDays { get; set; } + public AmazonPaySaveDataType? SaveEmailAndPhone { get; set; } + public bool ShowPayButtonForAdminOnly { get; set; } + public bool ShowButtonInMiniShoppingCart { get; set; } - public decimal AdditionalFee { get; set; } - public bool AdditionalFeePercentage { get; set; } + public int PollingMaxOrderCreationDays { get; set; } - public bool AddOrderNotes { get; set; } + public decimal AdditionalFee { get; set; } + public bool AdditionalFeePercentage { get; set; } - public bool InformCustomerAboutErrors { get; set; } - public bool InformCustomerAddErrors { get; set; } + public bool AddOrderNotes { get; set; } - public string PayButtonColor { get; set; } - public string PayButtonSize { get; set; } + public bool InformCustomerAboutErrors { get; set; } + public bool InformCustomerAddErrors { get; set; } - public string AuthButtonType { get; set; } - public string AuthButtonColor { get; set; } - public string AuthButtonSize { get; set; } + public string PayButtonColor { get; set; } + public string PayButtonSize { get; set; } - public bool CanSaveEmailAndPhone(string value) - { - return (SaveEmailAndPhone == AmazonPaySaveDataType.Always || (SaveEmailAndPhone == AmazonPaySaveDataType.OnlyIfEmpty && value.IsEmpty())); - } - } + public string AuthButtonType { get; set; } + public string AuthButtonColor { get; set; } + public string AuthButtonSize { get; set; } + + public bool CanSaveEmailAndPhone(string value) + { + return (SaveEmailAndPhone == AmazonPaySaveDataType.Always || (SaveEmailAndPhone == AmazonPaySaveDataType.OnlyIfEmpty && value.IsEmpty())); + } + } } diff --git a/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayCheckoutController.cs b/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayCheckoutController.cs index 35fbe06a06..f3f9e19667 100644 --- a/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayCheckoutController.cs +++ b/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayCheckoutController.cs @@ -13,109 +13,109 @@ namespace SmartStore.AmazonPay.Controllers { public class AmazonPayCheckoutController : AmazonPayControllerBase - { - private readonly HttpContextBase _httpContext; - private readonly IAmazonPayService _apiService; - private readonly IGenericAttributeService _genericAttributeService; + { + private readonly HttpContextBase _httpContext; + private readonly IAmazonPayService _apiService; + private readonly IGenericAttributeService _genericAttributeService; private readonly IOrderProcessingService _orderProcessingService; public AmazonPayCheckoutController( - HttpContextBase httpContext, - IAmazonPayService apiService, - IGenericAttributeService genericAttributeService, + HttpContextBase httpContext, + IAmazonPayService apiService, + IGenericAttributeService genericAttributeService, IOrderProcessingService orderProcessingService) - { - _httpContext = httpContext; - _apiService = apiService; - _genericAttributeService = genericAttributeService; + { + _httpContext = httpContext; + _apiService = apiService; + _genericAttributeService = genericAttributeService; _orderProcessingService = orderProcessingService; - } - - public ActionResult OrderReferenceCreated(string orderReferenceId/*, string accessToken*/) - { - var success = false; - var error = string.Empty; - - try - { - var state = _httpContext.GetAmazonPayState(Services.Localization); - state.OrderReferenceId = orderReferenceId; - - //if (accessToken.HasValue()) - //{ - // state.AccessToken = accessToken; - //} - - if (state.OrderReferenceId.IsEmpty()) - { - success = false; - error = T("Plugins.Payments.AmazonPay.MissingOrderReferenceId"); - } - - if (state.AccessToken.IsEmpty()) - { - success = false; - error = error.Grow(T("Plugins.Payments.AmazonPay.MissingAddressConsentToken"), " "); - } - } - catch (Exception ex) - { - error = ex.Message; - } - - return new JsonResult { Data = new { success = success, error = error } }; - } - - public ActionResult BillingAddress() - { - return RedirectToAction("ShippingAddress", "Checkout", new { area = "" }); - } - - public ActionResult ShippingAddress() - { - var model = _apiService.CreateViewModel(AmazonPayRequestType.Address, TempData); - - return GetActionResult(model); - } - - public ActionResult PaymentMethod() - { - var model = _apiService.CreateViewModel(AmazonPayRequestType.PaymentMethod, TempData); - - return GetActionResult(model); - } - - [HttpPost] - public ActionResult PaymentMethod(FormCollection form) - { - // Display biling address on confirm page. - _apiService.GetBillingAddress(); - - var customer = Services.WorkContext.CurrentCustomer; - if (customer.BillingAddress == null) - { - NotifyError(T("Plugins.Payments.AmazonPay.MissingBillingAddress")); - return RedirectToAction("Cart", "ShoppingCart", new { area = "" }); - } - - return RedirectToAction("Confirm", "Checkout", new { area = "" }); - } - - public ActionResult PaymentInfo() - { - return RedirectToAction("PaymentMethod", "Checkout", new { area = "" }); - } - - public ActionResult CheckoutCompleted() - { - var note = _httpContext.Session["AmazonPayCheckoutCompletedNote"] as string; - if (note.HasValue()) - { - return Content(note); - } - - return new EmptyResult(); - } + } + + public ActionResult OrderReferenceCreated(string orderReferenceId/*, string accessToken*/) + { + var success = false; + var error = string.Empty; + + try + { + var state = _httpContext.GetAmazonPayState(Services.Localization); + state.OrderReferenceId = orderReferenceId; + + //if (accessToken.HasValue()) + //{ + // state.AccessToken = accessToken; + //} + + if (state.OrderReferenceId.IsEmpty()) + { + success = false; + error = T("Plugins.Payments.AmazonPay.MissingOrderReferenceId"); + } + + if (state.AccessToken.IsEmpty()) + { + success = false; + error = error.Grow(T("Plugins.Payments.AmazonPay.MissingAddressConsentToken"), " "); + } + } + catch (Exception ex) + { + error = ex.Message; + } + + return new JsonResult { Data = new { success = success, error = error } }; + } + + public ActionResult BillingAddress() + { + return RedirectToAction("ShippingAddress", "Checkout", new { area = "" }); + } + + public ActionResult ShippingAddress() + { + var model = _apiService.CreateViewModel(AmazonPayRequestType.Address, TempData); + + return GetActionResult(model); + } + + public ActionResult PaymentMethod() + { + var model = _apiService.CreateViewModel(AmazonPayRequestType.PaymentMethod, TempData); + + return GetActionResult(model); + } + + [HttpPost] + public ActionResult PaymentMethod(FormCollection form) + { + // Display biling address on confirm page. + _apiService.GetBillingAddress(); + + var customer = Services.WorkContext.CurrentCustomer; + if (customer.BillingAddress == null) + { + NotifyError(T("Plugins.Payments.AmazonPay.MissingBillingAddress")); + return RedirectToAction("Cart", "ShoppingCart", new { area = "" }); + } + + return RedirectToAction("Confirm", "Checkout", new { area = "" }); + } + + public ActionResult PaymentInfo() + { + return RedirectToAction("PaymentMethod", "Checkout", new { area = "" }); + } + + public ActionResult CheckoutCompleted() + { + var note = _httpContext.Session["AmazonPayCheckoutCompletedNote"] as string; + if (note.HasValue()) + { + return Content(note); + } + + return new EmptyResult(); + } #region Confirmation flow diff --git a/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayController.cs b/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayController.cs index 669cc4c009..014492316e 100644 --- a/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayController.cs +++ b/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayController.cs @@ -19,141 +19,143 @@ namespace SmartStore.AmazonPay.Controllers { - public class AmazonPayController : PaymentControllerBase - { + public class AmazonPayController : PaymentControllerBase + { private readonly HttpContextBase _httpContext; private readonly IAmazonPayService _apiService; - private readonly Lazy _scheduleTaskService; - private readonly Lazy _openAuthenticationService; - private readonly Lazy _externalAuthenticationSettings; + private readonly Lazy _scheduleTaskService; + private readonly Lazy _openAuthenticationService; + private readonly Lazy _externalAuthenticationSettings; - public AmazonPayController( + public AmazonPayController( HttpContextBase httpContext, IAmazonPayService apiService, - Lazy scheduleTaskService, - Lazy openAuthenticationService, - Lazy externalAuthenticationSettings) - { + Lazy scheduleTaskService, + Lazy openAuthenticationService, + Lazy externalAuthenticationSettings) + { _httpContext = httpContext; - _apiService = apiService; - _scheduleTaskService = scheduleTaskService; - _openAuthenticationService = openAuthenticationService; - _externalAuthenticationSettings = externalAuthenticationSettings; - } - - [NonAction] - public override IList ValidatePaymentForm(FormCollection form) - { - var warnings = new List(); - return warnings; - } - - [NonAction] - public override ProcessPaymentRequest GetPaymentInfo(FormCollection form) - { - var paymentInfo = new ProcessPaymentRequest(); - return paymentInfo; - } - - [AdminAuthorize, LoadSetting] - public ActionResult Configure(AmazonPaySettings settings) - { - var model = new ConfigurationModel(); - - MiniMapper.Map(settings, model); - _apiService.SetupConfiguration(model); - - return View(model); - } - - [HttpPost, AdminAuthorize] - public ActionResult Configure(ConfigurationModel model, FormCollection form) - { - var storeDependingSettingHelper = new StoreDependingSettingHelper(ViewData); - var storeScope = this.GetActiveStoreScopeConfiguration(Services.StoreService, Services.WorkContext); - var settings = Services.Settings.LoadSetting(storeScope); - - if (!ModelState.IsValid) - return Configure(settings); - - ModelState.Clear(); - - model.AccessKey = model.AccessKey.TrimSafe(); - model.ClientId = model.ClientId.TrimSafe(); - model.SecretKey = model.SecretKey.TrimSafe(); - model.SellerId = model.SellerId.TrimSafe(); - - MiniMapper.Map(model, settings); - - using (Services.Settings.BeginScope()) - { - storeDependingSettingHelper.UpdateSettings(settings, form, storeScope, Services.Settings); - } - - using (Services.Settings.BeginScope()) - { - Services.Settings.SaveSetting(settings, x => x.DataFetching, 0, false); - Services.Settings.SaveSetting(settings, x => x.PollingMaxOrderCreationDays, 0, false); - } - - var task = _scheduleTaskService.Value.GetTaskByType(); - if (task != null) - { - task.Enabled = settings.DataFetching == AmazonPayDataFetchingType.Polling; - - _scheduleTaskService.Value.UpdateTask(task); - } - - NotifySuccess(T("Plugins.Payments.AmazonPay.ConfigSaveNote")); - - return RedirectToConfiguration(AmazonPayPlugin.SystemName); - } - - [HttpPost, AdminAuthorize] - public ActionResult SaveAccessData(string accessData) - { - try - { - var storeScope = this.GetActiveStoreScopeConfiguration(Services.StoreService, Services.WorkContext); - _apiService.ShareKeys(accessData, storeScope); - - NotifySuccess(T("Plugins.Payments.AmazonPay.SaveAccessDataSucceeded")); - } - catch (Exception exception) - { - NotifyError(exception.Message); - } - - return RedirectToConfiguration(AmazonPayPlugin.SystemName); - } - - [ValidateInput(false)] - public ActionResult ShareKey(string payload) - { - Response.AddHeader("Access-Control-Allow-Origin", "https://payments.amazon.com"); - Response.AddHeader("Access-Control-Allow-Methods", "GET, POST"); - Response.AddHeader("Access-Control-Allow-Headers", "Content-Type"); - - try - { - _apiService.ShareKeys(payload, 0); - } - catch (Exception exception) - { - Response.StatusCode = 400; - return Json(new { result = "error", message = exception.Message }); - } - - return Json(new { result = "success" }); - } - - [HttpPost] - [ValidateInput(false)] - public ActionResult IPNHandler() - { - _apiService.ProcessIpn(Request); - return Content("OK"); - } + _apiService = apiService; + _scheduleTaskService = scheduleTaskService; + _openAuthenticationService = openAuthenticationService; + _externalAuthenticationSettings = externalAuthenticationSettings; + } + + [NonAction] + public override IList ValidatePaymentForm(FormCollection form) + { + var warnings = new List(); + return warnings; + } + + [NonAction] + public override ProcessPaymentRequest GetPaymentInfo(FormCollection form) + { + var paymentInfo = new ProcessPaymentRequest(); + return paymentInfo; + } + + [AdminAuthorize, LoadSetting] + public ActionResult Configure(AmazonPaySettings settings) + { + var model = new ConfigurationModel(); + + MiniMapper.Map(settings, model); + _apiService.SetupConfiguration(model); + + return View(model); + } + + [HttpPost, AdminAuthorize] + [ValidateAntiForgeryToken] + public ActionResult Configure(ConfigurationModel model, FormCollection form) + { + var storeDependingSettingHelper = new StoreDependingSettingHelper(ViewData); + var storeScope = this.GetActiveStoreScopeConfiguration(Services.StoreService, Services.WorkContext); + var settings = Services.Settings.LoadSetting(storeScope); + + if (!ModelState.IsValid) + return Configure(settings); + + ModelState.Clear(); + + model.AccessKey = model.AccessKey.TrimSafe(); + model.ClientId = model.ClientId.TrimSafe(); + model.SecretKey = model.SecretKey.TrimSafe(); + model.SellerId = model.SellerId.TrimSafe(); + + MiniMapper.Map(model, settings); + + using (Services.Settings.BeginScope()) + { + storeDependingSettingHelper.UpdateSettings(settings, form, storeScope, Services.Settings); + } + + using (Services.Settings.BeginScope()) + { + Services.Settings.SaveSetting(settings, x => x.DataFetching, 0, false); + Services.Settings.SaveSetting(settings, x => x.PollingMaxOrderCreationDays, 0, false); + } + + var task = _scheduleTaskService.Value.GetTaskByType(); + if (task != null) + { + task.Enabled = settings.DataFetching == AmazonPayDataFetchingType.Polling; + + _scheduleTaskService.Value.UpdateTask(task); + } + + NotifySuccess(T("Plugins.Payments.AmazonPay.ConfigSaveNote")); + + return RedirectToConfiguration(AmazonPayPlugin.SystemName); + } + + [HttpPost, AdminAuthorize] + [ValidateAntiForgeryToken] + public ActionResult SaveAccessData(string accessData) + { + try + { + var storeScope = this.GetActiveStoreScopeConfiguration(Services.StoreService, Services.WorkContext); + _apiService.ShareKeys(accessData, storeScope); + + NotifySuccess(T("Plugins.Payments.AmazonPay.SaveAccessDataSucceeded")); + } + catch (Exception exception) + { + NotifyError(exception.Message); + } + + return RedirectToConfiguration(AmazonPayPlugin.SystemName); + } + + [ValidateInput(false)] + public ActionResult ShareKey(string payload) + { + Response.AddHeader("Access-Control-Allow-Origin", "https://payments.amazon.com"); + Response.AddHeader("Access-Control-Allow-Methods", "GET, POST"); + Response.AddHeader("Access-Control-Allow-Headers", "Content-Type"); + + try + { + _apiService.ShareKeys(payload, 0); + } + catch (Exception exception) + { + Response.StatusCode = 400; + return Json(new { result = "error", message = exception.Message }); + } + + return Json(new { result = "success" }); + } + + [HttpPost] + [ValidateInput(false)] + public ActionResult IPNHandler() + { + _apiService.ProcessIpn(Request); + return Content("OK"); + } // For payment simulation in sandbox mode. Admin and sandbox only. public ActionResult SetSellerNote(string paymentState) @@ -194,7 +196,7 @@ public ActionResult SetSellerNote(string paymentState) } var result = _apiService.SetOrderReferenceDetails(null, settings, state, sellerNote, out var errorMesage); - + var sb = new StringBuilder(); sb.Append(result ? "ok. " : "failure. "); sb.AppendLine(sellerNote); @@ -206,52 +208,52 @@ public ActionResult SetSellerNote(string paymentState) #region Authentication [ChildActionOnly] - public ActionResult AuthenticationPublicInfo() - { - var model = _apiService.CreateViewModel(AmazonPayRequestType.AuthenticationPublicInfo, TempData); + public ActionResult AuthenticationPublicInfo() + { + var model = _apiService.CreateViewModel(AmazonPayRequestType.AuthenticationPublicInfo, TempData); if (model != null) { return View(model); } return new EmptyResult(); - } - - public ActionResult AuthenticationButtonHandler() - { - var returnUrl = Session["AmazonAuthReturnUrl"] as string; - - var processor = _openAuthenticationService.Value.LoadExternalAuthenticationMethodBySystemName(AmazonPayPlugin.SystemName, Services.StoreContext.CurrentStore.Id); - if (processor == null || !processor.IsMethodActive(_externalAuthenticationSettings.Value)) - { - NotifyError(T("Plugins.Payments.AmazonPay.AuthenticationNotActive")); - return new RedirectResult(Url.LogOn(returnUrl)); - } - - var result = _apiService.Authorize(returnUrl); - switch (result.AuthenticationStatus) - { - case OpenAuthenticationStatus.Error: - result.Errors.Each(x => NotifyError(x)); - return new RedirectResult(Url.LogOn(returnUrl)); - case OpenAuthenticationStatus.AssociateOnLogon: - return new RedirectResult(Url.LogOn(returnUrl)); - case OpenAuthenticationStatus.AutoRegisteredEmailValidation: - return RedirectToRoute("RegisterResult", new { resultId = (int)UserRegistrationType.EmailValidation, returnUrl }); - case OpenAuthenticationStatus.AutoRegisteredAdminApproval: - return RedirectToRoute("RegisterResult", new { resultId = (int)UserRegistrationType.AdminApproval, returnUrl }); - case OpenAuthenticationStatus.AutoRegisteredStandard: - return RedirectToRoute("RegisterResult", new { resultId = (int)UserRegistrationType.Standard, returnUrl }); - default: - if (result.Result != null) - return result.Result; - - if (HttpContext.Request.IsAuthenticated) - return RedirectToReferrer(returnUrl, "~/"); - - return new RedirectResult(Url.LogOn(returnUrl)); - } - } + } + + public ActionResult AuthenticationButtonHandler() + { + var returnUrl = Session["AmazonAuthReturnUrl"] as string; + + var processor = _openAuthenticationService.Value.LoadExternalAuthenticationMethodBySystemName(AmazonPayPlugin.SystemName, Services.StoreContext.CurrentStore.Id); + if (processor == null || !processor.IsMethodActive(_externalAuthenticationSettings.Value)) + { + NotifyError(T("Plugins.Payments.AmazonPay.AuthenticationNotActive")); + return new RedirectResult(Url.LogOn(returnUrl)); + } + + var result = _apiService.Authorize(returnUrl); + switch (result.AuthenticationStatus) + { + case OpenAuthenticationStatus.Error: + result.Errors.Each(x => NotifyError(x)); + return new RedirectResult(Url.LogOn(returnUrl)); + case OpenAuthenticationStatus.AssociateOnLogon: + return new RedirectResult(Url.LogOn(returnUrl)); + case OpenAuthenticationStatus.AutoRegisteredEmailValidation: + return RedirectToRoute("RegisterResult", new { resultId = (int)UserRegistrationType.EmailValidation, returnUrl }); + case OpenAuthenticationStatus.AutoRegisteredAdminApproval: + return RedirectToRoute("RegisterResult", new { resultId = (int)UserRegistrationType.AdminApproval, returnUrl }); + case OpenAuthenticationStatus.AutoRegisteredStandard: + return RedirectToRoute("RegisterResult", new { resultId = (int)UserRegistrationType.Standard, returnUrl }); + default: + if (result.Result != null) + return result.Result; + + if (HttpContext.Request.IsAuthenticated) + return RedirectToReferrer(returnUrl, "~/"); + + return new RedirectResult(Url.LogOn(returnUrl)); + } + } #endregion } diff --git a/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayControllerBase.cs b/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayControllerBase.cs index b9f4858333..423463b7d7 100644 --- a/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayControllerBase.cs +++ b/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayControllerBase.cs @@ -5,25 +5,25 @@ namespace SmartStore.AmazonPay.Controllers { - public abstract partial class AmazonPayControllerBase : PublicControllerBase - { - protected ActionResult GetActionResult(AmazonPayViewModel model) - { - switch (model.Result) - { - case AmazonPayResultType.None: - return new EmptyResult(); + public abstract partial class AmazonPayControllerBase : PublicControllerBase + { + protected ActionResult GetActionResult(AmazonPayViewModel model) + { + switch (model.Result) + { + case AmazonPayResultType.None: + return new EmptyResult(); - case AmazonPayResultType.PluginView: - return View(model); + case AmazonPayResultType.PluginView: + return View(model); - case AmazonPayResultType.Unauthorized: - return new HttpUnauthorizedResult(); + case AmazonPayResultType.Unauthorized: + return new HttpUnauthorizedResult(); - case AmazonPayResultType.Redirect: - default: - return RedirectToAction(model.RedirectAction, model.RedirectController, new { area = "" }); - } - } - } + case AmazonPayResultType.Redirect: + default: + return RedirectToAction(model.RedirectAction, model.RedirectController, new { area = "" }); + } + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayShoppingCartController.cs b/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayShoppingCartController.cs index 2bebbbaf81..9da9735274 100644 --- a/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayShoppingCartController.cs +++ b/src/Plugins/SmartStore.AmazonPay/Controllers/AmazonPayShoppingCartController.cs @@ -4,55 +4,57 @@ namespace SmartStore.AmazonPay.Controllers { public class AmazonPayShoppingCartController : AmazonPayControllerBase - { + { private readonly IAmazonPayService _apiService; - public AmazonPayShoppingCartController(IAmazonPayService apiService) - { - _apiService = apiService; - } - - public ActionResult PayButtonHandler() - { - var model = _apiService.CreateViewModel(AmazonPayRequestType.PayButtonHandler, TempData); - return GetActionResult(model); - } - - [ChildActionOnly] - public ActionResult ShoppingCart() - { - if (ControllerContext.ParentActionViewContext.RequestContext.RouteData.IsRouteEqual("ShoppingCart", "Cart")) - { - var model = _apiService.CreateViewModel(AmazonPayRequestType.ShoppingCart, TempData); - - return GetActionResult(model); - } - - return new EmptyResult(); - } - - [ChildActionOnly] - public ActionResult OrderReviewData(bool renderAmazonPayView) - { - if (renderAmazonPayView) - { - var model = _apiService.CreateViewModel(AmazonPayRequestType.OrderReviewData, TempData); - - return View(model); - } - return new EmptyResult(); - } - - [ChildActionOnly] - public ActionResult MiniShoppingCart(bool renderAmazonPayView) - { - if (renderAmazonPayView) - { - var model = _apiService.CreateViewModel(AmazonPayRequestType.MiniShoppingCart, TempData); - - return GetActionResult(model); - } - return new EmptyResult(); - } + public AmazonPayShoppingCartController(IAmazonPayService apiService) + { + _apiService = apiService; + } + + public ActionResult PayButtonHandler() + { + var model = _apiService.CreateViewModel(AmazonPayRequestType.PayButtonHandler, TempData); + return GetActionResult(model); + } + + [ChildActionOnly] + public ActionResult ShoppingCart() + { + if (ControllerContext.ParentActionViewContext.RequestContext.RouteData.IsRouteEqual("ShoppingCart", "Cart")) + { + var model = _apiService.CreateViewModel(AmazonPayRequestType.ShoppingCart, TempData); + + return GetActionResult(model); + } + + return new EmptyResult(); + } + + [ChildActionOnly] + public ActionResult OrderReviewData(bool renderAmazonPayView) + { + if (renderAmazonPayView) + { + var model = _apiService.CreateViewModel(AmazonPayRequestType.OrderReviewData, TempData); + + return View(model); + } + + return new EmptyResult(); + } + + [ChildActionOnly] + public ActionResult MiniShoppingCart(bool renderAmazonPayView) + { + if (renderAmazonPayView) + { + var model = _apiService.CreateViewModel(AmazonPayRequestType.MiniShoppingCart, TempData); + + return GetActionResult(model); + } + + return new EmptyResult(); + } } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/DependencyRegistrar.cs b/src/Plugins/SmartStore.AmazonPay/DependencyRegistrar.cs index b163acf6e8..080b9f19eb 100644 --- a/src/Plugins/SmartStore.AmazonPay/DependencyRegistrar.cs +++ b/src/Plugins/SmartStore.AmazonPay/DependencyRegistrar.cs @@ -8,21 +8,18 @@ namespace SmartStore.AmazonPay { - public class DependencyRegistrar : IDependencyRegistrar - { - public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, bool isActiveModule) - { - builder.RegisterType().As().InstancePerRequest(); + public class DependencyRegistrar : IDependencyRegistrar + { + public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, bool isActiveModule) + { + builder.RegisterType().As().InstancePerRequest(); - if (isActiveModule) - { - builder.RegisterType().AsActionFilterFor().InstancePerRequest(); - } - } + if (isActiveModule) + { + builder.RegisterType().AsActionFilterFor().InstancePerRequest(); + } + } - public int Order - { - get { return 1; } - } - } + public int Order => 1; + } } diff --git a/src/Plugins/SmartStore.AmazonPay/Description.txt b/src/Plugins/SmartStore.AmazonPay/Description.txt index 7449e587bb..937624a4ec 100644 --- a/src/Plugins/SmartStore.AmazonPay/Description.txt +++ b/src/Plugins/SmartStore.AmazonPay/Description.txt @@ -1,8 +1,8 @@ FriendlyName: Login and Pay with Amazon SystemName: SmartStore.AmazonPay Group: Payment -Version: 3.2.2 -MinAppVersion: 3.2.2 +Version: 4.2.0 +MinAppVersion: 4.2.0 Author: SmartStore AG DisplayOrder: 1 FileName: SmartStore.AmazonPay.dll diff --git a/src/Plugins/SmartStore.AmazonPay/Events/EventConsumer.cs b/src/Plugins/SmartStore.AmazonPay/Events/EventConsumer.cs index 3d6e180d4f..0ad0c626a7 100644 --- a/src/Plugins/SmartStore.AmazonPay/Events/EventConsumer.cs +++ b/src/Plugins/SmartStore.AmazonPay/Events/EventConsumer.cs @@ -13,81 +13,81 @@ namespace SmartStore.AmazonPay.Events { - public class EventConsumer : IConsumer - { - private readonly IPluginFinder _pluginFinder; - private readonly ICommonServices _services; - private readonly IOrderService _orderService; - private readonly Lazy _amazonPayService; - - public EventConsumer( - IPluginFinder pluginFinder, - ICommonServices services, - IOrderService orderService, - Lazy amazonPayService) - { - _pluginFinder = pluginFinder; - _services = services; - _orderService = orderService; - _amazonPayService = amazonPayService; - - Logger = NullLogger.Instance; - } - - public ILogger Logger { get; set; } - - public void HandleEvent(MessageModelCreatedEvent message) - { - if (message.MessageContext.MessageTemplate.Name != MessageTemplateNames.OrderPlacedCustomer) - return; - - var storeId = _services.StoreContext.CurrentStore.Id; - - if (!_pluginFinder.IsPluginReady(_services.Settings, AmazonPayPlugin.SystemName, storeId)) - return; - - dynamic model = message.Model; - - if (model.Order == null) - return; - - var orderId = model.Order.ID; - - if (orderId is int id) - { - var order = _orderService.GetOrderById(id); - - var isAmazonPayment = (order != null && order.PaymentMethodSystemName.IsCaseInsensitiveEqual(AmazonPayPlugin.SystemName)); - var tokenValue = (isAmazonPayment ? _services.Localization.GetResource("Plugins.Payments.AmazonPay.BillingAddressMessageNote") : ""); - - model.AmazonPay = new Dictionary - { - { "BillingAddressMessageNote", tokenValue } - }; - } - } - - public void HandleEvent(OrderPaidEvent eventMessage) - { - if (eventMessage?.Order == null) - return; - - if (!eventMessage.Order.PaymentMethodSystemName.IsCaseInsensitiveEqual(AmazonPayPlugin.SystemName)) - return; - - if (!_pluginFinder.IsPluginReady(_services.Settings, AmazonPayPlugin.SystemName, eventMessage.Order.StoreId)) - return; - - try - { - var settings = _services.Settings.LoadSetting(eventMessage.Order.StoreId); - - _amazonPayService.Value.CloseOrderReference(settings, eventMessage.Order); - } - catch (Exception exception) - { - Logger.Error(exception); - } - } - } + public class EventConsumer : IConsumer + { + private readonly IPluginFinder _pluginFinder; + private readonly ICommonServices _services; + private readonly IOrderService _orderService; + private readonly Lazy _amazonPayService; + + public EventConsumer( + IPluginFinder pluginFinder, + ICommonServices services, + IOrderService orderService, + Lazy amazonPayService) + { + _pluginFinder = pluginFinder; + _services = services; + _orderService = orderService; + _amazonPayService = amazonPayService; + + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + public void HandleEvent(MessageModelCreatedEvent message) + { + if (message.MessageContext.MessageTemplate.Name != MessageTemplateNames.OrderPlacedCustomer) + return; + + var storeId = _services.StoreContext.CurrentStore.Id; + + if (!_pluginFinder.IsPluginReady(_services.Settings, AmazonPayPlugin.SystemName, storeId)) + return; + + dynamic model = message.Model; + + if (model.Order == null) + return; + + var orderId = model.Order.ID; + + if (orderId is int id) + { + var order = _orderService.GetOrderById(id); + + var isAmazonPayment = (order != null && order.PaymentMethodSystemName.IsCaseInsensitiveEqual(AmazonPayPlugin.SystemName)); + var tokenValue = (isAmazonPayment ? _services.Localization.GetResource("Plugins.Payments.AmazonPay.BillingAddressMessageNote") : ""); + + model.AmazonPay = new Dictionary + { + { "BillingAddressMessageNote", tokenValue } + }; + } + } + + public void HandleEvent(OrderPaidEvent eventMessage) + { + if (eventMessage?.Order == null) + return; + + if (!eventMessage.Order.PaymentMethodSystemName.IsCaseInsensitiveEqual(AmazonPayPlugin.SystemName)) + return; + + if (!_pluginFinder.IsPluginReady(_services.Settings, AmazonPayPlugin.SystemName, eventMessage.Order.StoreId)) + return; + + try + { + var settings = _services.Settings.LoadSetting(eventMessage.Order.StoreId); + + _amazonPayService.Value.CloseOrderReference(settings, eventMessage.Order); + } + catch (Exception exception) + { + Logger.Error(exception); + } + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/Filters/AmazonPayCheckoutFilter.cs b/src/Plugins/SmartStore.AmazonPay/Filters/AmazonPayCheckoutFilter.cs index ef444da26e..eb5dd84142 100644 --- a/src/Plugins/SmartStore.AmazonPay/Filters/AmazonPayCheckoutFilter.cs +++ b/src/Plugins/SmartStore.AmazonPay/Filters/AmazonPayCheckoutFilter.cs @@ -6,71 +6,71 @@ namespace SmartStore.AmazonPay.Filters { - public class AmazonPayCheckoutFilter : IActionFilter - { - private static readonly string[] s_interceptableActions = new string[] { "BillingAddress", "ShippingAddress", "ShippingMethod", "PaymentMethod" }; - private readonly Lazy _apiService; - - public AmazonPayCheckoutFilter(Lazy apiService) - { - _apiService = apiService; - } - - private static bool IsInterceptableAction(string actionName) - { - return s_interceptableActions.Contains(actionName, StringComparer.OrdinalIgnoreCase); - } - - public void OnActionExecuting(ActionExecutingContext filterContext) - { - if (filterContext == null || filterContext.ActionDescriptor == null || filterContext.HttpContext == null || filterContext.HttpContext.Request == null) - return; - - var actionName = filterContext.ActionDescriptor.ActionName; - - if (!IsInterceptableAction(actionName)) - return; - - if (actionName.IsCaseInsensitiveEqual("ShippingMethod") || actionName.IsCaseInsensitiveEqual("PaymentMethod")) - { - if (!filterContext.HttpContext.HasAmazonPayState()) - return; - } - - if (actionName.IsCaseInsensitiveEqual("ShippingMethod")) - { - var model = _apiService.Value.CreateViewModel(AmazonPayRequestType.ShippingMethod, filterContext.Controller.TempData); - - if (model.Result == AmazonPayResultType.Redirect) - { - // Shipping to selected address not possible. - var urlHelper = new UrlHelper(filterContext.HttpContext.Request.RequestContext); - var url = urlHelper.Action("ShippingAddress", "Checkout", new { area = "" }); - - filterContext.Result = new RedirectResult(url, false); - } - } - } - - public void OnActionExecuted(ActionExecutedContext filterContext) - { - if (filterContext == null || filterContext.ActionDescriptor == null || filterContext.HttpContext == null || filterContext.HttpContext.Request == null) - return; - - var actionName = filterContext.ActionDescriptor.ActionName; + public class AmazonPayCheckoutFilter : IActionFilter + { + private static readonly string[] s_interceptableActions = new string[] { "BillingAddress", "ShippingAddress", "ShippingMethod", "PaymentMethod" }; + private readonly Lazy _apiService; + + public AmazonPayCheckoutFilter(Lazy apiService) + { + _apiService = apiService; + } + + private static bool IsInterceptableAction(string actionName) + { + return s_interceptableActions.Contains(actionName, StringComparer.OrdinalIgnoreCase); + } + + public void OnActionExecuting(ActionExecutingContext filterContext) + { + if (filterContext == null || filterContext.ActionDescriptor == null || filterContext.HttpContext == null || filterContext.HttpContext.Request == null) + return; + + var actionName = filterContext.ActionDescriptor.ActionName; + + if (!IsInterceptableAction(actionName)) + return; + + if (actionName.IsCaseInsensitiveEqual("ShippingMethod") || actionName.IsCaseInsensitiveEqual("PaymentMethod")) + { + if (!filterContext.HttpContext.HasAmazonPayState()) + return; + } + + if (actionName.IsCaseInsensitiveEqual("ShippingMethod")) + { + var model = _apiService.Value.CreateViewModel(AmazonPayRequestType.ShippingMethod, filterContext.Controller.TempData); + + if (model.Result == AmazonPayResultType.Redirect) + { + // Shipping to selected address not possible. + var urlHelper = new UrlHelper(filterContext.HttpContext.Request.RequestContext); + var url = urlHelper.Action("ShippingAddress", "Checkout", new { area = "" }); + + filterContext.Result = new RedirectResult(url, false); + } + } + } + + public void OnActionExecuted(ActionExecutedContext filterContext) + { + if (filterContext == null || filterContext.ActionDescriptor == null || filterContext.HttpContext == null || filterContext.HttpContext.Request == null) + return; + + var actionName = filterContext.ActionDescriptor.ActionName; if (actionName.IsCaseInsensitiveEqual("ShippingMethod")) return; if (!IsInterceptableAction(actionName)) - return; + return; if (!filterContext.HttpContext.HasAmazonPayState()) return; var routeValues = new RouteValueDictionary(new { action = actionName, controller = "AmazonPayCheckout" }); - filterContext.Result = new RedirectToRouteResult("SmartStore.AmazonPay", routeValues); - } - } + filterContext.Result = new RedirectToRouteResult("SmartStore.AmazonPay", routeValues); + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/Localization/resources.de-de.xml b/src/Plugins/SmartStore.AmazonPay/Localization/resources.de-de.xml index baa01c2299..6f527970f1 100644 --- a/src/Plugins/SmartStore.AmazonPay/Localization/resources.de-de.xml +++ b/src/Plugins/SmartStore.AmazonPay/Localization/resources.de-de.xml @@ -34,9 +34,6 @@ Textvorschläge:
    Unvollständige Amazon Profildaten! - - Die Zahlungsart "Login und Bezahlen mit Amazon" ist für Shop "{0}" nicht verfügbar. - Fehlender Checkout-Sitzungsstatus für "Login und Bezahlen mit Amazon". Ihre Zahlung kann leider nicht bearbeitet werden. Bitte gehen Sie zurück in den Warenkorb und Durchlaufen Sie den Checkout erneut. @@ -136,22 +133,22 @@ Textvorschläge:
      Die Händlernummer, die Sie währen der Registrierung bei Amazon Seller Central erhalten haben. - Marktplatz + Währung - Das Land unter dem Sie Ihr Amazon Verkäuferkonto registriert haben. + Legt die Währung und zugleich die Amazon Region fest. - Deutschland + EUR (Euro) - Großbritannien + GBP (Pfund Sterling) - USA + USD (US Dollar) - Japan + JPY (Japanischer Yen) Ihre Access Key ID @@ -337,4 +334,7 @@ Textvorschläge:
        Legt fest, ob der genaue Wortlaut der Fehlermeldung der Auftragsnotiz bzw. E-Mail angehängt werden soll. + + Amazon Pay nutzt Cookies zur Darstellung und Abwicklung des Zahlungsvorgangs über Amazon. + \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/Localization/resources.en-us.xml b/src/Plugins/SmartStore.AmazonPay/Localization/resources.en-us.xml index caaa7ebf43..eaaecf755d 100644 --- a/src/Plugins/SmartStore.AmazonPay/Localization/resources.en-us.xml +++ b/src/Plugins/SmartStore.AmazonPay/Localization/resources.en-us.xml @@ -34,9 +34,6 @@ Text suggestions:
          Incomplete Amazon profile data! - - Payment method "Login and Pay with Amazon" is not available for store "{0}". - Missing checkout session state for "Login and Pay with Amazon". Your payment cannot be processed. Please go to your shopping cart and checkout again. @@ -136,23 +133,23 @@ Text suggestions:
            The Merchant ID you received during registration at Amazon's Seller Central. - Marketplace + Currency - The country where you registered your Amazon seller account. - - - Germany - - - United Kingdom of Great Britain - - - USA - - - Japan + Specifies both the currency and the Amazon region. + + EUR (Euro) + + + GBP (Pound Sterling) + + + USD (US Dollar) + + + JPY (Japanese Yen) + Your Access Key ID @@ -337,4 +334,7 @@ Text suggestions:
              Specifies to append the error message to the order note and email. + + Amazon Pay uses cookies to display and process the payment transaction via Amazon. + \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/Models/AmazonPayViewModel.cs b/src/Plugins/SmartStore.AmazonPay/Models/AmazonPayViewModel.cs index 77c391b448..e709b6b39b 100644 --- a/src/Plugins/SmartStore.AmazonPay/Models/AmazonPayViewModel.cs +++ b/src/Plugins/SmartStore.AmazonPay/Models/AmazonPayViewModel.cs @@ -4,49 +4,49 @@ namespace SmartStore.AmazonPay.Models { - public class AmazonPayViewModel : ModelBase + public class AmazonPayViewModel : ModelBase { - public AmazonPayViewModel() - { - IsShippable = true; - RedirectAction = "Cart"; - RedirectController = "ShoppingCart"; - Result = AmazonPayResultType.PluginView; - BillingAddress = new AddressModel(); - } - - public string SystemName => AmazonPayPlugin.SystemName; - - public string SellerId { get; set; } - public string ClientId { get; set; } - - /// - /// Amazon widget script URL - /// - public string WidgetUrl { get; set; } - public string ButtonHandlerUrl { get; set; } - - public bool IsShippable { get; set; } - public bool IsRecurring { get; set; } - - public string LanguageCode { get; set; } - public AmazonPayRequestType Type { get; set; } - public AmazonPayResultType Result { get; set; } - - public string RedirectAction { get; set; } - public string RedirectController { get; set; } - - public string OrderReferenceId { get; set; } - public string AddressConsentToken { get; set; } - public string Warning { get; set; } - public bool Logout { get; set; } - - public string ButtonType { get; set; } - public string ButtonColor { get; set; } - public string ButtonSize { get; set; } - - public string ShippingMethod { get; set; } - public AddressModel BillingAddress { get; set; } + public AmazonPayViewModel() + { + IsShippable = true; + RedirectAction = "Cart"; + RedirectController = "ShoppingCart"; + Result = AmazonPayResultType.PluginView; + BillingAddress = new AddressModel(); + } + + public string SystemName => AmazonPayPlugin.SystemName; + + public string SellerId { get; set; } + public string ClientId { get; set; } + + /// + /// Amazon widget script URL + /// + public string WidgetUrl { get; set; } + public string ButtonHandlerUrl { get; set; } + + public bool IsShippable { get; set; } + public bool IsRecurring { get; set; } + + public string LanguageCode { get; set; } + public AmazonPayRequestType Type { get; set; } + public AmazonPayResultType Result { get; set; } + + public string RedirectAction { get; set; } + public string RedirectController { get; set; } + + public string OrderReferenceId { get; set; } + public string AddressConsentToken { get; set; } + public string Warning { get; set; } + public bool Logout { get; set; } + + public string ButtonType { get; set; } + public string ButtonColor { get; set; } + public string ButtonSize { get; set; } + + public string ShippingMethod { get; set; } + public AddressModel BillingAddress { get; set; } // Confirmation flow. public bool IsConfirmed { get; set; } diff --git a/src/Plugins/SmartStore.AmazonPay/Models/ConfigurationModel.cs b/src/Plugins/SmartStore.AmazonPay/Models/ConfigurationModel.cs index cf304df151..45a1c1cd25 100644 --- a/src/Plugins/SmartStore.AmazonPay/Models/ConfigurationModel.cs +++ b/src/Plugins/SmartStore.AmazonPay/Models/ConfigurationModel.cs @@ -7,122 +7,122 @@ namespace SmartStore.AmazonPay.Models { - public class ConfigurationModel : ModelBase - { - public string[] ConfigGroups { get; set; } - public string PrimaryStoreCurrencyCode { get; set; } + public class ConfigurationModel : ModelBase + { + public string[] ConfigGroups { get; set; } + public string PrimaryStoreCurrencyCode { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.UseSandbox")] - public bool UseSandbox { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.UseSandbox")] + public bool UseSandbox { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.SellerId")] - public string SellerId { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.SellerId")] + public string SellerId { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.AccessKey")] - public string AccessKey { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.AccessKey")] + public string AccessKey { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.SecretKey")] - [DataType(DataType.Password)] - public string SecretKey { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.SecretKey")] + [DataType(DataType.Password)] + public string SecretKey { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.ClientId")] - public string ClientId { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.ClientId")] + public string ClientId { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.Marketplace")] - public string Marketplace { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.Marketplace")] + public string Marketplace { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.DataFetching")] - public AmazonPayDataFetchingType DataFetching { get; set; } - public List DataFetchings { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.DataFetching")] + public AmazonPayDataFetchingType DataFetching { get; set; } + public List DataFetchings { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.IpnUrl")] - public string IpnUrl { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.IpnUrl")] + public string IpnUrl { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.PollingMaxOrderCreationDays")] - public int PollingMaxOrderCreationDays { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.PollingMaxOrderCreationDays")] + public int PollingMaxOrderCreationDays { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.TransactionType")] - public AmazonPayTransactionType TransactionType { get; set; } - public List TransactionTypes { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.TransactionType")] + public AmazonPayTransactionType TransactionType { get; set; } + public List TransactionTypes { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.AuthorizeMethod")] - public AmazonPayAuthorizeMethod AuthorizeMethod { get; set; } - public SelectList AuthorizeMethods { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.AuthorizeMethod")] + public AmazonPayAuthorizeMethod AuthorizeMethod { get; set; } + public SelectList AuthorizeMethods { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.SaveEmailAndPhone")] - public AmazonPaySaveDataType? SaveEmailAndPhone { get; set; } - public List SaveEmailAndPhones { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.SaveEmailAndPhone")] + public AmazonPaySaveDataType? SaveEmailAndPhone { get; set; } + public List SaveEmailAndPhones { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.ShowPayButtonForAdminOnly")] - public bool ShowPayButtonForAdminOnly { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.ShowPayButtonForAdminOnly")] + public bool ShowPayButtonForAdminOnly { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.ShowButtonInMiniShoppingCart")] - public bool ShowButtonInMiniShoppingCart { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.ShowButtonInMiniShoppingCart")] + public bool ShowButtonInMiniShoppingCart { get; set; } - [SmartResourceDisplayName("Admin.Configuration.Payment.Methods.AdditionalFee")] - public decimal AdditionalFee { get; set; } + [SmartResourceDisplayName("Admin.Configuration.Payment.Methods.AdditionalFee")] + public decimal AdditionalFee { get; set; } - [SmartResourceDisplayName("Admin.Configuration.Payment.Methods.AdditionalFeePercentage")] - public bool AdditionalFeePercentage { get; set; } + [SmartResourceDisplayName("Admin.Configuration.Payment.Methods.AdditionalFeePercentage")] + public bool AdditionalFeePercentage { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.AddOrderNotes")] - public bool AddOrderNotes { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.AddOrderNotes")] + public bool AddOrderNotes { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.InformCustomerAboutErrors")] - public bool InformCustomerAboutErrors { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.InformCustomerAboutErrors")] + public bool InformCustomerAboutErrors { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.InformCustomerAddErrors")] - public bool InformCustomerAddErrors { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.InformCustomerAddErrors")] + public bool InformCustomerAddErrors { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.PayButtonColor")] - public string PayButtonColor { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.PayButtonColor")] + public string PayButtonColor { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.PayButtonSize")] - public string PayButtonSize { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.PayButtonSize")] + public string PayButtonSize { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.AuthButtonType")] - public string AuthButtonType { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.AuthButtonType")] + public string AuthButtonType { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.AuthButtonColor")] - public string AuthButtonColor { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.AuthButtonColor")] + public string AuthButtonColor { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.AuthButtonSize")] - public string AuthButtonSize { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.AuthButtonSize")] + public string AuthButtonSize { get; set; } - #region Registration data + #region Registration data - public string RegisterUrl { get; set; } - public string SoftwareVersion { get; set; } - public string PluginVersion { get; set; } - public string LeadCode { get; set; } - public string PlatformId { get; set; } - public string PublicKey { get; set; } - public string KeyShareUrl { get; set; } - public string LanguageLocale { get; set; } + public string RegisterUrl { get; set; } + public string SoftwareVersion { get; set; } + public string PluginVersion { get; set; } + public string LeadCode { get; set; } + public string PlatformId { get; set; } + public string PublicKey { get; set; } + public string KeyShareUrl { get; set; } + public string LanguageLocale { get; set; } - /// - /// Including all domains and sub domains where the login button appears. SSL mandatory. - /// - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.MerchantLoginDomains")] - public HashSet MerchantLoginDomains { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.MerchantLoginDomains")] - public HashSet CurrentMerchantLoginDomains { get; set; } + /// + /// Including all domains and sub domains where the login button appears. SSL mandatory. + /// + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.MerchantLoginDomains")] + public HashSet MerchantLoginDomains { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.MerchantLoginDomains")] + public HashSet CurrentMerchantLoginDomains { get; set; } - /// - /// Used to populate Allowed Return URLs on the Login with Amazon application. SSL mandatory. Max 512 characters. - /// - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.MerchantLoginRedirectUrls")] - public HashSet MerchantLoginRedirectUrls { get; set; } - [SmartResourceDisplayName("Plugins.Payments.AmazonPay.MerchantLoginRedirectUrls")] - public HashSet CurrentMerchantLoginRedirectUrls { get; set; } + /// + /// Used to populate Allowed Return URLs on the Login with Amazon application. SSL mandatory. Max 512 characters. + /// + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.MerchantLoginRedirectUrls")] + public HashSet MerchantLoginRedirectUrls { get; set; } + [SmartResourceDisplayName("Plugins.Payments.AmazonPay.MerchantLoginRedirectUrls")] + public HashSet CurrentMerchantLoginRedirectUrls { get; set; } - public string MerchantStoreDescription { get; set; } - public string MerchantPrivacyNoticeUrl { get; set; } - public string MerchantCountry { get; set; } - public string MerchantSandboxIpnUrl { get; set; } - public string MerchantProductionIpnUrl { get; set; } + public string MerchantStoreDescription { get; set; } + public string MerchantPrivacyNoticeUrl { get; set; } + public string MerchantCountry { get; set; } + public string MerchantSandboxIpnUrl { get; set; } + public string MerchantProductionIpnUrl { get; set; } - #endregion - } + #endregion + } } diff --git a/src/Plugins/SmartStore.AmazonPay/Plugin.cs b/src/Plugins/SmartStore.AmazonPay/Plugin.cs index 9524984a8d..8b6904f964 100644 --- a/src/Plugins/SmartStore.AmazonPay/Plugin.cs +++ b/src/Plugins/SmartStore.AmazonPay/Plugin.cs @@ -3,11 +3,13 @@ using System.Web.Routing; using SmartStore.AmazonPay.Controllers; using SmartStore.AmazonPay.Services; +using SmartStore.Core.Domain.Cms; using SmartStore.Core.Domain.Orders; using SmartStore.Core.Logging; using SmartStore.Core.Plugins; using SmartStore.Services; using SmartStore.Services.Authentication.External; +using SmartStore.Services.Cms; using SmartStore.Services.Customers; using SmartStore.Services.Orders; using SmartStore.Services.Payments; @@ -15,173 +17,181 @@ namespace SmartStore.AmazonPay { - [DependentWidgets("Widgets.AmazonPay")] - [FriendlyName("Amazon Pay")] - [DisplayOrder(-1)] - public class AmazonPayPlugin : PaymentPluginBase, IExternalAuthenticationMethod, IConfigurable - { - private readonly IAmazonPayService _apiService; - private readonly ICommonServices _services; - private readonly IOrderTotalCalculationService _orderTotalCalculationService; - private readonly IScheduleTaskService _scheduleTaskService; - - public AmazonPayPlugin( - IAmazonPayService apiService, - ICommonServices services, - IOrderTotalCalculationService orderTotalCalculationService, - IScheduleTaskService scheduleTaskService) - { - _apiService = apiService; - _services = services; - _orderTotalCalculationService = orderTotalCalculationService; - _scheduleTaskService = scheduleTaskService; - - Logger = NullLogger.Instance; - } - - public ILogger Logger { get; set; } - - public static string SystemName - { - get { return "SmartStore.AmazonPay"; } - } - - public override void Install() - { - _services.Settings.SaveSetting(new AmazonPaySettings()); - _services.Localization.ImportPluginResourcesFromXml(PluginDescriptor); - - // Polling task every 30 minutes. - _scheduleTaskService.GetOrAddTask(x => - { - x.Name = _services.Localization.GetResource("Plugins.Payments.AmazonPay.TaskName"); - x.CronExpression = "*/30 * * * *"; - }); - - base.Install(); - } - - public override void Uninstall() - { - _scheduleTaskService.TryDeleteTask(); - _services.Settings.DeleteSetting(); - _services.Localization.DeleteLocaleStringResources(PluginDescriptor.ResourceRootKey); - - base.Uninstall(); - } - - public override ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest) - { - var result = _apiService.ProcessPayment(processPaymentRequest); - return result; - } - - public override void PostProcessPayment(PostProcessPaymentRequest postProcessPaymentRequest) - { - _apiService.PostProcessPayment(postProcessPaymentRequest); - } - - public override decimal GetAdditionalHandlingFee(IList cart) - { - var result = decimal.Zero; - - try - { - var settings = _services.Settings.LoadSetting(_services.StoreContext.CurrentStore.Id); - - result = this.CalculateAdditionalFee(_orderTotalCalculationService, cart, settings.AdditionalFee, settings.AdditionalFeePercentage); - } - catch (Exception exception) - { - Logger.Error(exception); - } - - return result; - } - - public override CapturePaymentResult Capture(CapturePaymentRequest capturePaymentRequest) - { - var result = _apiService.Capture(capturePaymentRequest); - return result; - } - - public override RefundPaymentResult Refund(RefundPaymentRequest refundPaymentRequest) - { - var result = _apiService.Refund(refundPaymentRequest); - return result; - } - - public override VoidPaymentResult Void(VoidPaymentRequest voidPaymentRequest) - { - var result = _apiService.Void(voidPaymentRequest); - return result; - } - - - public override void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) - { - actionName = "Configure"; - controllerName = "AmazonPay"; - routeValues = new RouteValueDictionary { { "Namespaces", "SmartStore.AmazonPay.Controllers" }, { "area", SystemName } }; - } - - public void GetPublicInfoRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) - { - actionName = "AuthenticationPublicInfo"; - controllerName = "AmazonPay"; - routeValues = new RouteValueDictionary { { "Namespaces", "SmartStore.AmazonPay.Controllers" }, { "area", SystemName } }; - } - - public override void GetPaymentInfoRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) - { - try - { - var settings = _services.Settings.LoadSetting(_services.StoreContext.CurrentStore.Id); - if (settings.ShowPayButtonForAdminOnly && !_services.WorkContext.CurrentCustomer.IsAdmin()) - { - actionName = controllerName = null; - routeValues = null; - return; - } - } - catch (Exception exception) - { - Logger.Error(exception); - } - - actionName = "ShoppingCart"; - controllerName = "AmazonPayShoppingCart"; - routeValues = new RouteValueDictionary { { "Namespaces", "SmartStore.AmazonPay.Controllers" }, { "area", SystemName } }; - } - - public override Type GetControllerType() - { - return typeof(AmazonPayController); - } - - public override bool SupportCapture - { - get { return true; } - } - - public override bool SupportPartiallyRefund - { - get { return true; } - } - - public override bool SupportRefund - { - get { return true; } - } - - public override bool SupportVoid - { - get { return true; } - } - - public override PaymentMethodType PaymentMethodType - { - get { return PaymentMethodType.StandardAndButton; } - } - } + [DependentWidgets("Widgets.AmazonPay")] + [FriendlyName("Amazon Pay")] + [DisplayOrder(-1)] + public class AmazonPayPlugin : PaymentPluginBase, IExternalAuthenticationMethod, IConfigurable, ICookiePublisher + { + private readonly IAmazonPayService _apiService; + private readonly ICommonServices _services; + private readonly IOrderTotalCalculationService _orderTotalCalculationService; + private readonly IScheduleTaskService _scheduleTaskService; + private readonly IWidgetService _widgetService; + private readonly WidgetSettings _widgetSettings; + + public AmazonPayPlugin( + IAmazonPayService apiService, + ICommonServices services, + IOrderTotalCalculationService orderTotalCalculationService, + IScheduleTaskService scheduleTaskService, + IWidgetService widgetService, + WidgetSettings widgetSettings) + { + _apiService = apiService; + _services = services; + _orderTotalCalculationService = orderTotalCalculationService; + _scheduleTaskService = scheduleTaskService; + _widgetService = widgetService; + _widgetSettings = widgetSettings; + + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + public static string SystemName => "SmartStore.AmazonPay"; + + /// + /// Gets CookieInfos for display in CookieManager dialog. + /// + /// CookieInfo containing plugin name, cookie purpose description & cookie type + public IEnumerable GetCookieInfo() + { + var widget = _widgetService.LoadWidgetBySystemName("Widgets.AmazonPay"); + if (!widget.IsWidgetActive(_widgetSettings)) + return null; + + var cookieInfo = new CookieInfo + { + Name = _services.Localization.GetResource("Plugins.FriendlyName.Widgets.AmazonPay"), + Description = _services.Localization.GetResource("Plugins.Payments.AmazonPay.CookieInfo"), + CookieType = CookieType.Required + }; + + return new List { cookieInfo }; + } + + public override void Install() + { + _services.Settings.SaveSetting(new AmazonPaySettings()); + _services.Localization.ImportPluginResourcesFromXml(PluginDescriptor); + + // Polling task every 30 minutes. + _scheduleTaskService.GetOrAddTask(x => + { + x.Name = _services.Localization.GetResource("Plugins.Payments.AmazonPay.TaskName"); + x.CronExpression = "*/30 * * * *"; + }); + + base.Install(); + } + + public override void Uninstall() + { + _scheduleTaskService.TryDeleteTask(); + _services.Settings.DeleteSetting(); + _services.Localization.DeleteLocaleStringResources(PluginDescriptor.ResourceRootKey); + + base.Uninstall(); + } + + public override ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest) + { + var result = _apiService.ProcessPayment(processPaymentRequest); + return result; + } + + public override void PostProcessPayment(PostProcessPaymentRequest postProcessPaymentRequest) + { + _apiService.PostProcessPayment(postProcessPaymentRequest); + } + + public override decimal GetAdditionalHandlingFee(IList cart) + { + var result = decimal.Zero; + + try + { + var settings = _services.Settings.LoadSetting(_services.StoreContext.CurrentStore.Id); + + result = this.CalculateAdditionalFee(_orderTotalCalculationService, cart, settings.AdditionalFee, settings.AdditionalFeePercentage); + } + catch (Exception exception) + { + Logger.Error(exception); + } + + return result; + } + + public override CapturePaymentResult Capture(CapturePaymentRequest capturePaymentRequest) + { + var result = _apiService.Capture(capturePaymentRequest); + return result; + } + + public override RefundPaymentResult Refund(RefundPaymentRequest refundPaymentRequest) + { + var result = _apiService.Refund(refundPaymentRequest); + return result; + } + + public override VoidPaymentResult Void(VoidPaymentRequest voidPaymentRequest) + { + var result = _apiService.Void(voidPaymentRequest); + return result; + } + + + public override void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) + { + actionName = "Configure"; + controllerName = "AmazonPay"; + routeValues = new RouteValueDictionary { { "Namespaces", "SmartStore.AmazonPay.Controllers" }, { "area", SystemName } }; + } + + public void GetPublicInfoRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) + { + actionName = "AuthenticationPublicInfo"; + controllerName = "AmazonPay"; + routeValues = new RouteValueDictionary { { "Namespaces", "SmartStore.AmazonPay.Controllers" }, { "area", SystemName } }; + } + + public override void GetPaymentInfoRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) + { + try + { + var settings = _services.Settings.LoadSetting(_services.StoreContext.CurrentStore.Id); + if (settings.ShowPayButtonForAdminOnly && !_services.WorkContext.CurrentCustomer.IsAdmin()) + { + actionName = controllerName = null; + routeValues = null; + return; + } + } + catch (Exception exception) + { + Logger.Error(exception); + } + + actionName = "ShoppingCart"; + controllerName = "AmazonPayShoppingCart"; + routeValues = new RouteValueDictionary { { "Namespaces", "SmartStore.AmazonPay.Controllers" }, { "area", SystemName } }; + } + + public override Type GetControllerType() + { + return typeof(AmazonPayController); + } + + public override bool SupportCapture => true; + + public override bool SupportPartiallyRefund => true; + + public override bool SupportRefund => true; + + public override bool SupportVoid => true; + + public override PaymentMethodType PaymentMethodType => PaymentMethodType.StandardAndButton; + } } diff --git a/src/Plugins/SmartStore.AmazonPay/RouteProvider.cs b/src/Plugins/SmartStore.AmazonPay/RouteProvider.cs index d7d33434cb..3cfe49b1b8 100644 --- a/src/Plugins/SmartStore.AmazonPay/RouteProvider.cs +++ b/src/Plugins/SmartStore.AmazonPay/RouteProvider.cs @@ -1,30 +1,29 @@ using System.Web.Mvc; using System.Web.Routing; -using SmartStore.AmazonPay.Services; using SmartStore.Web.Framework.Routing; namespace SmartStore.AmazonPay { - public class RouteProvider : IRouteProvider - { - public void RegisterRoutes(RouteCollection routes) - { - routes.MapRoute("SmartStore.AmazonPay", - "Plugins/SmartStore.AmazonPay/{controller}/{action}", - new { controller = "AmazonPay" }, - new[] { "SmartStore.AmazonPay.Controllers" } - ) - .DataTokens["area"] = AmazonPayPlugin.SystemName; + public class RouteProvider : IRouteProvider + { + public void RegisterRoutes(RouteCollection routes) + { + routes.MapRoute("SmartStore.AmazonPay", + "Plugins/SmartStore.AmazonPay/{controller}/{action}", + new { controller = "AmazonPay" }, + new[] { "SmartStore.AmazonPay.Controllers" } + ) + .DataTokens["area"] = AmazonPayPlugin.SystemName; - // for backward compatibility (IPN!) - routes.MapRoute("SmartStore.AmazonPay.Legacy", - "Plugins/PaymentsAmazonPay/{action}", - new { controller = "AmazonPay" }, - new[] { "SmartStore.AmazonPay.Controllers" } - ) - .DataTokens["area"] = AmazonPayPlugin.SystemName; - } + // for backward compatibility (IPN!) + routes.MapRoute("SmartStore.AmazonPay.Legacy", + "Plugins/PaymentsAmazonPay/{action}", + new { controller = "AmazonPay" }, + new[] { "SmartStore.AmazonPay.Controllers" } + ) + .DataTokens["area"] = AmazonPayPlugin.SystemName; + } - public int Priority { get { return 0; } } - } + public int Priority => 0; + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/Services/AmazonAuthenticationParameters.cs b/src/Plugins/SmartStore.AmazonPay/Services/AmazonAuthenticationParameters.cs index d64719b0e7..49e0e261bb 100644 --- a/src/Plugins/SmartStore.AmazonPay/Services/AmazonAuthenticationParameters.cs +++ b/src/Plugins/SmartStore.AmazonPay/Services/AmazonAuthenticationParameters.cs @@ -4,29 +4,23 @@ namespace SmartStore.AmazonPay.Services { - [Serializable] - public class AmazonAuthenticationParameters : OpenAuthenticationParameters - { - private IList _claims; + [Serializable] + public class AmazonAuthenticationParameters : OpenAuthenticationParameters + { + private IList _claims; - public override string ProviderSystemName - { - get { return AmazonPayPlugin.SystemName; } - } + public override string ProviderSystemName => AmazonPayPlugin.SystemName; - public override IList UserClaims - { - get { return _claims; } - } + public override IList UserClaims => _claims; - public void AddClaim(UserClaims claim) - { - if (_claims == null) - { - _claims = new List(); - } + public void AddClaim(UserClaims claim) + { + if (_claims == null) + { + _claims = new List(); + } - _claims.Add(claim); - } - } + _claims.Add(claim); + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayEnums.cs b/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayEnums.cs index 4e9144bc9a..bab0824eee 100644 --- a/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayEnums.cs +++ b/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayEnums.cs @@ -1,78 +1,78 @@ namespace SmartStore.AmazonPay.Services { - public enum AmazonPayRequestType - { - None = 0, - ShoppingCart, - Address, - PaymentMethod, - OrderReviewData, - ShippingMethod, - MiniShoppingCart, + public enum AmazonPayRequestType + { + None = 0, + ShoppingCart, + Address, + PaymentMethod, + OrderReviewData, + ShippingMethod, + MiniShoppingCart, - /// - /// Amazon Pay button clicked - /// - PayButtonHandler, + /// + /// Amazon Pay button clicked + /// + PayButtonHandler, - /// - /// Display authentication button on login page - /// - AuthenticationPublicInfo - } + /// + /// Display authentication button on login page + /// + AuthenticationPublicInfo + } - public enum AmazonPayTransactionType - { - None = 0, - Authorize, - AuthorizeAndCapture - } + public enum AmazonPayTransactionType + { + None = 0, + Authorize, + AuthorizeAndCapture + } - public enum AmazonPayAuthorizeMethod - { - Omnichronous = 0, - Asynchronous, - Synchronous - } + public enum AmazonPayAuthorizeMethod + { + Omnichronous = 0, + Asynchronous, + Synchronous + } - public enum AmazonPaySaveDataType - { - None = 0, - OnlyIfEmpty, - Always - } + public enum AmazonPaySaveDataType + { + None = 0, + OnlyIfEmpty, + Always + } - public enum AmazonPayDataFetchingType - { - None = 0, - Ipn, - Polling - } + public enum AmazonPayDataFetchingType + { + None = 0, + Ipn, + Polling + } - public enum AmazonPayResultType - { - None = 0, - PluginView, - Redirect, - Unauthorized - } + public enum AmazonPayResultType + { + None = 0, + PluginView, + Redirect, + Unauthorized + } - public enum AmazonPayMessage - { - MessageTyp = 0, - MessageId, - AuthorizationID, - CaptureID, - RefundID, - ReferenceID, - State, - StateUpdate, - Fee, - AuthorizedAmount, - CapturedAmount, - RefundedAmount, - CaptureNow, - Creation, - Expiration - } + public enum AmazonPayMessage + { + MessageTyp = 0, + MessageId, + AuthorizationID, + CaptureID, + RefundID, + ReferenceID, + State, + StateUpdate, + Fee, + AuthorizedAmount, + CapturedAmount, + RefundedAmount, + CaptureNow, + Creation, + Expiration + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayService.cs b/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayService.cs index a24c0646f7..88d41c6aad 100644 --- a/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayService.cs +++ b/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayService.cs @@ -21,7 +21,6 @@ using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Payments; using SmartStore.Core.Domain.Shipping; -using SmartStore.Core.Domain.Stores; using SmartStore.Core.Localization; using SmartStore.Core.Logging; using SmartStore.Core.Plugins; @@ -40,348 +39,348 @@ namespace SmartStore.AmazonPay.Services { public partial class AmazonPayService : IAmazonPayService - { - private readonly HttpContextBase _httpContext; - private readonly IRepository _orderRepository; - private readonly ICommonServices _services; - private readonly IPaymentService _paymentService; - private readonly IGenericAttributeService _genericAttributeService; - private readonly ICurrencyService _currencyService; - private readonly ICustomerService _customerService; - private readonly ICountryService _countryService; - private readonly IStateProvinceService _stateProvinceService; - private readonly IOrderService _orderService; - private readonly IOrderProcessingService _orderProcessingService; + { + private readonly HttpContextBase _httpContext; + private readonly IRepository _orderRepository; + private readonly ICommonServices _services; + private readonly IPaymentService _paymentService; + private readonly IGenericAttributeService _genericAttributeService; + private readonly ICurrencyService _currencyService; + private readonly ICustomerService _customerService; + private readonly ICountryService _countryService; + private readonly IStateProvinceService _stateProvinceService; + private readonly IOrderService _orderService; + private readonly IOrderProcessingService _orderProcessingService; private readonly IOrderTotalCalculationService _orderTotalCalculationService; - private readonly IDateTimeHelper _dateTimeHelper; - private readonly IPluginFinder _pluginFinder; - private readonly Lazy _authorizer; - private readonly AddressSettings _addressSettings; - private readonly OrderSettings _orderSettings; - private readonly CompanyInformationSettings _companyInformationSettings; - - public AmazonPayService( - HttpContextBase httpContext, - IRepository orderRepository, - ICommonServices services, - IPaymentService paymentService, - IGenericAttributeService genericAttributeService, - ICurrencyService currencyService, - ICustomerService customerService, - ICountryService countryService, - IStateProvinceService stateProvinceService, - IOrderService orderService, - IOrderProcessingService orderProcessingService, + private readonly IDateTimeHelper _dateTimeHelper; + private readonly IPluginFinder _pluginFinder; + private readonly Lazy _authorizer; + private readonly AddressSettings _addressSettings; + private readonly OrderSettings _orderSettings; + private readonly CompanyInformationSettings _companyInformationSettings; + + public AmazonPayService( + HttpContextBase httpContext, + IRepository orderRepository, + ICommonServices services, + IPaymentService paymentService, + IGenericAttributeService genericAttributeService, + ICurrencyService currencyService, + ICustomerService customerService, + ICountryService countryService, + IStateProvinceService stateProvinceService, + IOrderService orderService, + IOrderProcessingService orderProcessingService, IOrderTotalCalculationService orderTotalCalculationService, - IDateTimeHelper dateTimeHelper, - IPluginFinder pluginFinder, - Lazy authorizer, - AddressSettings addressSettings, - OrderSettings orderSettings, - CompanyInformationSettings companyInformationSettings) - { - _httpContext = httpContext; - _orderRepository = orderRepository; - _services = services; - _paymentService = paymentService; - _genericAttributeService = genericAttributeService; - _currencyService = currencyService; - _customerService = customerService; - _countryService = countryService; - _stateProvinceService = stateProvinceService; - _orderService = orderService; - _orderProcessingService = orderProcessingService; + IDateTimeHelper dateTimeHelper, + IPluginFinder pluginFinder, + Lazy authorizer, + AddressSettings addressSettings, + OrderSettings orderSettings, + CompanyInformationSettings companyInformationSettings) + { + _httpContext = httpContext; + _orderRepository = orderRepository; + _services = services; + _paymentService = paymentService; + _genericAttributeService = genericAttributeService; + _currencyService = currencyService; + _customerService = customerService; + _countryService = countryService; + _stateProvinceService = stateProvinceService; + _orderService = orderService; + _orderProcessingService = orderProcessingService; _orderTotalCalculationService = orderTotalCalculationService; - _dateTimeHelper = dateTimeHelper; - _pluginFinder = pluginFinder; - _authorizer = authorizer; - _addressSettings = addressSettings; - _orderSettings = orderSettings; - _companyInformationSettings = companyInformationSettings; - - T = NullLocalizer.Instance; - Logger = NullLogger.Instance; - } - - public Localizer T { get; set; } - public ILogger Logger { get; set; } - - public void SetupConfiguration(ConfigurationModel model) - { - var store = _services.StoreContext.CurrentStore; - var language = _services.WorkContext.WorkingLanguage; - var descriptor = _pluginFinder.GetPluginDescriptorBySystemName(AmazonPayPlugin.SystemName); - var allStores = _services.StoreService.GetAllStores(); - var urlHelper = new UrlHelper(_httpContext.Request.RequestContext); - - model.IpnUrl = GetPluginUrl("IPNHandler", true); - model.ConfigGroups = T("Plugins.Payments.AmazonPay.ConfigGroups").Text.SplitSafe(";"); - model.PrimaryStoreCurrencyCode = _services.StoreContext.CurrentStore.PrimaryStoreCurrency.CurrencyCode; - - model.RegisterUrl = "https://payments-eu.amazon.com/register"; - model.SoftwareVersion = SmartStoreVersion.CurrentFullVersion; - if (descriptor != null) - { - model.PluginVersion = descriptor.Version.ToString(); - } - model.LeadCode = LeadCode; - model.PlatformId = PlatformId; - // Not implemented. Not available for europe at the moment. - model.PublicKey = string.Empty; - model.KeyShareUrl = GetPluginUrl("ShareKey", store.SslEnabled); - model.LanguageLocale = language.UniqueSeoCode.ToAmazonLanguageCode('_'); - model.MerchantStoreDescription = store.Name.Truncate(2048); - model.MerchantPrivacyNoticeUrl = WebHelper.GetAbsoluteUrl(urlHelper.Topic("privacyinfo"), _httpContext.Request, protocol: store.SslEnabled ? "https" : "http"); - model.MerchantSandboxIpnUrl = model.IpnUrl; - model.MerchantProductionIpnUrl = model.IpnUrl; - - model.MerchantLoginDomains = new HashSet(StringComparer.OrdinalIgnoreCase); - model.MerchantLoginRedirectUrls = new HashSet(StringComparer.OrdinalIgnoreCase); - model.CurrentMerchantLoginDomains = new HashSet(StringComparer.OrdinalIgnoreCase); - model.CurrentMerchantLoginRedirectUrls = new HashSet(StringComparer.OrdinalIgnoreCase); - - foreach (var entity in allStores) - { - // SSL required! - var shopUrl = entity.SslEnabled ? entity.SecureUrl : entity.Url; - if (shopUrl.HasValue()) - { - try - { - // Only protocol and domain name. - var uri = new Uri(shopUrl); - var loginDomain = uri.GetLeftPart(UriPartial.Scheme | UriPartial.Authority).EmptyNull().TrimEnd('/'); - model.MerchantLoginDomains.Add(loginDomain); - - if (entity.Id == store.Id) - { - model.CurrentMerchantLoginDomains.Add(loginDomain); - } - } - catch { } - - var payHandlerUrl = shopUrl.EnsureEndsWith("/") + "Plugins/SmartStore.AmazonPay/AmazonPayShoppingCart/PayButtonHandler"; - var authHandlerUrl = shopUrl.EnsureEndsWith("/") + "Plugins/SmartStore.AmazonPay/AmazonPay/AuthenticationButtonHandler"; - - model.MerchantLoginRedirectUrls.Add(payHandlerUrl); - model.MerchantLoginRedirectUrls.Add(authHandlerUrl); - - if (entity.Id == store.Id) - { - model.CurrentMerchantLoginRedirectUrls.Add(payHandlerUrl); - model.CurrentMerchantLoginRedirectUrls.Add(authHandlerUrl); - } - } - } - - if (_companyInformationSettings.CountryId != 0) - { - var merchantCountry = _countryService.GetCountryById(_companyInformationSettings.CountryId); - if (merchantCountry != null) - { - model.MerchantCountry = merchantCountry.GetLocalized(x => x.Name, language, false, false); - } - } - - model.DataFetchings = new List - { - new SelectListItem - { - Text = T("Common.Unspecified").Text, - Value = "" - }, - new SelectListItem - { - Selected = (model.DataFetching == AmazonPayDataFetchingType.Ipn), - Text = T("Plugins.Payments.AmazonPay.DataFetching.Ipn"), - Value = ((int)AmazonPayDataFetchingType.Ipn).ToString() - }, - new SelectListItem - { - Selected = (model.DataFetching == AmazonPayDataFetchingType.Polling), - Text = T("Plugins.Payments.AmazonPay.DataFetching.Polling"), - Value = ((int)AmazonPayDataFetchingType.Polling).ToString() - } - }; - - model.TransactionTypes = new List - { - new SelectListItem - { - Selected = (model.TransactionType == AmazonPayTransactionType.AuthorizeAndCapture), - Text = T("Plugins.Payments.AmazonPay.TransactionType.AuthAndCapture"), - Value = ((int)AmazonPayTransactionType.AuthorizeAndCapture).ToString() - }, - new SelectListItem - { - Selected = (model.TransactionType == AmazonPayTransactionType.Authorize), - Text = T("Plugins.Payments.AmazonPay.TransactionType.Auth"), - Value = ((int)AmazonPayTransactionType.Authorize).ToString() - } - }; - - model.AuthorizeMethods = model.AuthorizeMethod.ToSelectList(); - - model.SaveEmailAndPhones = new List - { - new SelectListItem - { - Text = T("Common.Unspecified").Text, - Value = "" - }, - new SelectListItem - { - Selected = (model.SaveEmailAndPhone == AmazonPaySaveDataType.OnlyIfEmpty), - Text = T("Plugins.Payments.AmazonPay.AmazonPaySaveDataType.OnlyIfEmpty"), - Value = ((int)AmazonPaySaveDataType.OnlyIfEmpty).ToString() - }, - new SelectListItem - { - Selected = (model.SaveEmailAndPhone == AmazonPaySaveDataType.Always), - Text = T("Plugins.Payments.AmazonPay.AmazonPaySaveDataType.Always"), - Value = ((int)AmazonPaySaveDataType.Always).ToString() - } - }; - } - - public AmazonPayViewModel CreateViewModel(AmazonPayRequestType type, TempDataDictionary tempData) - { - var model = new AmazonPayViewModel(); - model.Type = type; - - try - { - var store = _services.StoreContext.CurrentStore; - var customer = _services.WorkContext.CurrentCustomer; - var language = _services.WorkContext.WorkingLanguage; - var cart = customer.GetCartItems(ShoppingCartType.ShoppingCart, store.Id); - var storeLocation = _services.WebHelper.GetStoreLocation(store.SslEnabled); - var settings = _services.Settings.LoadSetting(store.Id); - - model.ButtonHandlerUrl = $"{storeLocation}Plugins/SmartStore.AmazonPay/AmazonPayShoppingCart/PayButtonHandler"; - model.LanguageCode = language.UniqueSeoCode.ToAmazonLanguageCode(); - - if (type == AmazonPayRequestType.PayButtonHandler) - { - if (cart.Count <= 0 || !IsPaymentMethodActive(store.Id)) - { - model.Result = AmazonPayResultType.Redirect; - return model; - } - - if (customer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed) - { - model.Result = AmazonPayResultType.Unauthorized; - return model; - } - - var accessToken = _httpContext.Request.QueryString["access_token"]; - if (accessToken.IsEmpty()) - { - var msg = T("Plugins.Payments.AmazonPay.MissingAddressConsentToken"); - Logger.Error(null, msg); - _services.Notifier.Error(new LocalizedString(msg)); - - model.Result = AmazonPayResultType.Redirect; - return model; - } - - // Create session state object. - var checkoutState = _httpContext.GetCheckoutState(); - if (checkoutState == null) - { - Logger.Warn("Checkout state is null in AmazonPayService.ValidateAndInitiateCheckout!"); - model.Result = AmazonPayResultType.Redirect; - return model; - } - - checkoutState.CustomProperties[AmazonPayPlugin.SystemName + ".CheckoutState"] = new AmazonPayCheckoutState { AccessToken = accessToken }; - - model.RedirectAction = "Index"; - model.RedirectController = "Checkout"; - model.Result = AmazonPayResultType.Redirect; - return model; - } - else if (type == AmazonPayRequestType.AuthenticationPublicInfo) - { - if (settings.SellerId.IsEmpty() || settings.AccessKey.IsEmpty() || settings.SecretKey.IsEmpty()) - { - return null; - } - - model.ButtonHandlerUrl = $"{storeLocation}Plugins/SmartStore.AmazonPay/AmazonPay/AuthenticationButtonHandler"; - - // Do not append returnUrl to button handler URL. Handler URLs must be whitelisted in Amazon Seller Central. - _httpContext.Session["AmazonAuthReturnUrl"] = _httpContext.Request.QueryString["returnUrl"]; - } - else if (type == AmazonPayRequestType.ShoppingCart || type == AmazonPayRequestType.MiniShoppingCart) - { - if (cart.Count <= 0 || !IsPaymentMethodActive(store.Id)) - { - model.Result = AmazonPayResultType.None; - return model; - } - } - else - { - if (cart.Count <= 0) - { - model.Result = AmazonPayResultType.Redirect; - return model; - } - - if (customer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed) - { - model.Result = AmazonPayResultType.Unauthorized; - return model; - } - - var state = _httpContext.GetAmazonPayState(_services.Localization); - model.OrderReferenceId = state.OrderReferenceId; - model.AddressConsentToken = state.AccessToken; + _dateTimeHelper = dateTimeHelper; + _pluginFinder = pluginFinder; + _authorizer = authorizer; + _addressSettings = addressSettings; + _orderSettings = orderSettings; + _companyInformationSettings = companyInformationSettings; + + T = NullLocalizer.Instance; + Logger = NullLogger.Instance; + } + + public Localizer T { get; set; } + public ILogger Logger { get; set; } + + public void SetupConfiguration(ConfigurationModel model) + { + var store = _services.StoreContext.CurrentStore; + var language = _services.WorkContext.WorkingLanguage; + var descriptor = _pluginFinder.GetPluginDescriptorBySystemName(AmazonPayPlugin.SystemName); + var allStores = _services.StoreService.GetAllStores(); + var urlHelper = new UrlHelper(_httpContext.Request.RequestContext); + + model.IpnUrl = GetPluginUrl("IPNHandler", true); + model.ConfigGroups = T("Plugins.Payments.AmazonPay.ConfigGroups").Text.SplitSafe(";"); + model.PrimaryStoreCurrencyCode = _services.StoreContext.CurrentStore.PrimaryStoreCurrency.CurrencyCode; + + model.RegisterUrl = "https://payments-eu.amazon.com/register"; + model.SoftwareVersion = SmartStoreVersion.CurrentFullVersion; + if (descriptor != null) + { + model.PluginVersion = descriptor.Version.ToString(); + } + model.LeadCode = LeadCode; + model.PlatformId = PlatformId; + // Not implemented. Not available for europe at the moment. + model.PublicKey = string.Empty; + model.KeyShareUrl = GetPluginUrl("ShareKey", store.SslEnabled); + model.LanguageLocale = language.UniqueSeoCode.ToAmazonLanguageCode('_'); + model.MerchantStoreDescription = store.Name.Truncate(2048); + model.MerchantPrivacyNoticeUrl = WebHelper.GetAbsoluteUrl(urlHelper.Topic("privacyinfo"), _httpContext.Request, protocol: store.SslEnabled ? "https" : "http"); + model.MerchantSandboxIpnUrl = model.IpnUrl; + model.MerchantProductionIpnUrl = model.IpnUrl; + + model.MerchantLoginDomains = new HashSet(StringComparer.OrdinalIgnoreCase); + model.MerchantLoginRedirectUrls = new HashSet(StringComparer.OrdinalIgnoreCase); + model.CurrentMerchantLoginDomains = new HashSet(StringComparer.OrdinalIgnoreCase); + model.CurrentMerchantLoginRedirectUrls = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (var entity in allStores) + { + // SSL required! + var shopUrl = entity.SslEnabled ? entity.SecureUrl : entity.Url; + if (shopUrl.HasValue()) + { + try + { + // Only protocol and domain name. + var uri = new Uri(shopUrl); + var loginDomain = uri.GetLeftPart(UriPartial.Scheme | UriPartial.Authority).EmptyNull().TrimEnd('/'); + model.MerchantLoginDomains.Add(loginDomain); + + if (entity.Id == store.Id) + { + model.CurrentMerchantLoginDomains.Add(loginDomain); + } + } + catch { } + + var payHandlerUrl = shopUrl.EnsureEndsWith("/") + "Plugins/SmartStore.AmazonPay/AmazonPayShoppingCart/PayButtonHandler"; + var authHandlerUrl = shopUrl.EnsureEndsWith("/") + "Plugins/SmartStore.AmazonPay/AmazonPay/AuthenticationButtonHandler"; + + model.MerchantLoginRedirectUrls.Add(payHandlerUrl); + model.MerchantLoginRedirectUrls.Add(authHandlerUrl); + + if (entity.Id == store.Id) + { + model.CurrentMerchantLoginRedirectUrls.Add(payHandlerUrl); + model.CurrentMerchantLoginRedirectUrls.Add(authHandlerUrl); + } + } + } + + if (_companyInformationSettings.CountryId != 0) + { + var merchantCountry = _countryService.GetCountryById(_companyInformationSettings.CountryId); + if (merchantCountry != null) + { + model.MerchantCountry = merchantCountry.GetLocalized(x => x.Name, language, false, false); + } + } + + model.DataFetchings = new List + { + new SelectListItem + { + Text = T("Common.Unspecified").Text, + Value = "" + }, + new SelectListItem + { + Selected = (model.DataFetching == AmazonPayDataFetchingType.Ipn), + Text = T("Plugins.Payments.AmazonPay.DataFetching.Ipn"), + Value = ((int)AmazonPayDataFetchingType.Ipn).ToString() + }, + new SelectListItem + { + Selected = (model.DataFetching == AmazonPayDataFetchingType.Polling), + Text = T("Plugins.Payments.AmazonPay.DataFetching.Polling"), + Value = ((int)AmazonPayDataFetchingType.Polling).ToString() + } + }; + + model.TransactionTypes = new List + { + new SelectListItem + { + Selected = (model.TransactionType == AmazonPayTransactionType.AuthorizeAndCapture), + Text = T("Plugins.Payments.AmazonPay.TransactionType.AuthAndCapture"), + Value = ((int)AmazonPayTransactionType.AuthorizeAndCapture).ToString() + }, + new SelectListItem + { + Selected = (model.TransactionType == AmazonPayTransactionType.Authorize), + Text = T("Plugins.Payments.AmazonPay.TransactionType.Auth"), + Value = ((int)AmazonPayTransactionType.Authorize).ToString() + } + }; + + model.AuthorizeMethods = model.AuthorizeMethod.ToSelectList(); + + model.SaveEmailAndPhones = new List + { + new SelectListItem + { + Text = T("Common.Unspecified").Text, + Value = "" + }, + new SelectListItem + { + Selected = (model.SaveEmailAndPhone == AmazonPaySaveDataType.OnlyIfEmpty), + Text = T("Plugins.Payments.AmazonPay.AmazonPaySaveDataType.OnlyIfEmpty"), + Value = ((int)AmazonPaySaveDataType.OnlyIfEmpty).ToString() + }, + new SelectListItem + { + Selected = (model.SaveEmailAndPhone == AmazonPaySaveDataType.Always), + Text = T("Plugins.Payments.AmazonPay.AmazonPaySaveDataType.Always"), + Value = ((int)AmazonPaySaveDataType.Always).ToString() + } + }; + } + + public AmazonPayViewModel CreateViewModel(AmazonPayRequestType type, TempDataDictionary tempData) + { + var model = new AmazonPayViewModel(); + model.Type = type; + + try + { + var store = _services.StoreContext.CurrentStore; + var customer = _services.WorkContext.CurrentCustomer; + var language = _services.WorkContext.WorkingLanguage; + var cart = customer.GetCartItems(ShoppingCartType.ShoppingCart, store.Id); + var storeLocation = _services.WebHelper.GetStoreLocation(store.SslEnabled); + var settings = _services.Settings.LoadSetting(store.Id); + + model.ButtonHandlerUrl = $"{storeLocation}Plugins/SmartStore.AmazonPay/AmazonPayShoppingCart/PayButtonHandler"; + model.LanguageCode = language.UniqueSeoCode.ToAmazonLanguageCode(); + + if (type == AmazonPayRequestType.PayButtonHandler) + { + if (cart.Count <= 0 || !_paymentService.IsPaymentMethodActive(AmazonPayPlugin.SystemName, customer, cart, store.Id)) + { + model.Result = AmazonPayResultType.Redirect; + return model; + } + + if (customer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed) + { + model.Result = AmazonPayResultType.Unauthorized; + return model; + } + + var accessToken = _httpContext.Request.QueryString["access_token"]; + if (accessToken.IsEmpty()) + { + var msg = T("Plugins.Payments.AmazonPay.MissingAddressConsentToken"); + Logger.Error(null, msg); + _services.Notifier.Error(new LocalizedString(msg)); + + model.Result = AmazonPayResultType.Redirect; + return model; + } + + // Create session state object. + var checkoutState = _httpContext.GetCheckoutState(); + if (checkoutState == null) + { + Logger.Warn("Checkout state is null in AmazonPayService.ValidateAndInitiateCheckout!"); + model.Result = AmazonPayResultType.Redirect; + return model; + } + + checkoutState.CustomProperties[AmazonPayPlugin.SystemName + ".CheckoutState"] = new AmazonPayCheckoutState { AccessToken = accessToken }; + + model.RedirectAction = "Index"; + model.RedirectController = "Checkout"; + model.Result = AmazonPayResultType.Redirect; + return model; + } + else if (type == AmazonPayRequestType.AuthenticationPublicInfo) + { + if (settings.SellerId.IsEmpty() || settings.AccessKey.IsEmpty() || settings.SecretKey.IsEmpty()) + { + return null; + } + + model.ButtonHandlerUrl = $"{storeLocation}Plugins/SmartStore.AmazonPay/AmazonPay/AuthenticationButtonHandler"; + + // Do not append returnUrl to button handler URL. Handler URLs must be whitelisted in Amazon Seller Central. + _httpContext.Session["AmazonAuthReturnUrl"] = _httpContext.Request.QueryString["returnUrl"]; + } + else if (type == AmazonPayRequestType.ShoppingCart || type == AmazonPayRequestType.MiniShoppingCart) + { + if (cart.Count <= 0 || !_paymentService.IsPaymentMethodActive(AmazonPayPlugin.SystemName, customer, cart, store.Id)) + { + model.Result = AmazonPayResultType.None; + return model; + } + } + else + { + if (cart.Count <= 0) + { + model.Result = AmazonPayResultType.Redirect; + return model; + } + + if (customer.IsGuest() && !_orderSettings.AnonymousCheckoutAllowed) + { + model.Result = AmazonPayResultType.Unauthorized; + return model; + } + + var state = _httpContext.GetAmazonPayState(_services.Localization); + model.OrderReferenceId = state.OrderReferenceId; + model.AddressConsentToken = state.AccessToken; model.IsConfirmed = state.IsConfirmed; model.FormData = state.FormData; model.SubmitForm = state.SubmitForm; state.SubmitForm = false; - if (type == AmazonPayRequestType.ShippingMethod || type == AmazonPayRequestType.PaymentMethod) - { - if (state.OrderReferenceId.IsEmpty() || state.AccessToken.IsEmpty()) - { - model.Result = AmazonPayResultType.Redirect; - return model; - } - } - } - - model.SellerId = settings.SellerId; - model.ClientId = settings.ClientId; - model.IsShippable = cart.RequiresShipping(); - model.IsRecurring = cart.IsRecurring(); - model.WidgetUrl = GetWidgetUrl(settings); - - if (type == AmazonPayRequestType.MiniShoppingCart || type == AmazonPayRequestType.ShoppingCart) - { - if (type == AmazonPayRequestType.MiniShoppingCart && !settings.ShowButtonInMiniShoppingCart) - { - model.Result = AmazonPayResultType.None; - return model; - } - if (settings.ShowPayButtonForAdminOnly && !customer.IsAdmin()) - { - model.Result = AmazonPayResultType.None; - return model; - } - - // AmazonPay review: The setting for payment button type has been removed. - model.ButtonType = "PwA"; - model.ButtonColor = settings.PayButtonColor; - model.ButtonSize = settings.PayButtonSize; - - var failureReason = _httpContext.Session["AmazonPayFailedPaymentReason"] as string; + if (type == AmazonPayRequestType.ShippingMethod || type == AmazonPayRequestType.PaymentMethod) + { + if (state.OrderReferenceId.IsEmpty() || state.AccessToken.IsEmpty()) + { + model.Result = AmazonPayResultType.Redirect; + return model; + } + } + } + + model.SellerId = settings.SellerId; + model.ClientId = settings.ClientId; + model.IsShippable = cart.RequiresShipping(); + model.IsRecurring = cart.IsRecurring(); + model.WidgetUrl = GetWidgetUrl(settings); + + if (type == AmazonPayRequestType.MiniShoppingCart || type == AmazonPayRequestType.ShoppingCart) + { + if (type == AmazonPayRequestType.MiniShoppingCart && !settings.ShowButtonInMiniShoppingCart) + { + model.Result = AmazonPayResultType.None; + return model; + } + if (settings.ShowPayButtonForAdminOnly && !customer.IsAdmin()) + { + model.Result = AmazonPayResultType.None; + return model; + } + + // AmazonPay review: The setting for payment button type has been removed. + model.ButtonType = "PwA"; + model.ButtonColor = settings.PayButtonColor; + model.ButtonSize = settings.PayButtonSize; + + var failureReason = _httpContext.Session["AmazonPayFailedPaymentReason"] as string; if (failureReason.IsCaseInsensitiveEqual("AmazonRejected")) { model.Logout = true; @@ -393,1334 +392,1334 @@ public AmazonPayViewModel CreateViewModel(AmazonPayRequestType type, TempDataDic _services.Notifier.Error(new LocalizedString(T("Plugins.Payments.AmazonPay.AuthenticationStatusFailureMessage"))); } - _httpContext.Session.SafeRemove("AmazonPayFailedPaymentReason"); - } - else if (type == AmazonPayRequestType.AuthenticationPublicInfo) - { - model.ButtonType = settings.AuthButtonType; - model.ButtonColor = settings.AuthButtonColor; - model.ButtonSize = settings.AuthButtonSize; - } - else if (type == AmazonPayRequestType.Address) - { - if (!model.IsShippable) - { - model.RedirectAction = "ShippingMethod"; - model.RedirectController = "Checkout"; - model.Result = AmazonPayResultType.Redirect; - return model; - } - - var shippingToCountryNotAllowed = tempData[AmazonPayPlugin.SystemName + "ShippingToCountryNotAllowed"]; - - if (shippingToCountryNotAllowed != null && true == (bool)shippingToCountryNotAllowed) - { - model.Warning = T("Plugins.Payments.AmazonPay.ShippingToCountryNotAllowed"); - } - } - else if (type == AmazonPayRequestType.ShippingMethod) - { - model.RedirectAction = model.RedirectController = ""; - - if (model.IsShippable) - { - var client = CreateClient(settings); - var getOrderRequest = new GetOrderReferenceDetailsRequest() - .WithMerchantId(settings.SellerId) - .WithAmazonOrderReferenceId(model.OrderReferenceId) - .WithAccessToken(model.AddressConsentToken); - - var getOrderResponse = client.GetOrderReferenceDetails(getOrderRequest); - if (getOrderResponse.GetSuccess()) - { - // Billing address not available here. getOrderResponse.GetBillingAddressDetails() is null. - //if (FindAndApplyAddress(getOrderResponse, customer, model.IsShippable, true)) - var countryAllowsShipping = true; - var countryAllowsBilling = true; - - var address = CreateAddress( - getOrderResponse.GetEmail(), - getOrderResponse.GetBuyerShippingName(), - getOrderResponse.GetAddressLine1(), - getOrderResponse.GetAddressLine2(), - getOrderResponse.GetAddressLine3(), - getOrderResponse.GetCity(), - getOrderResponse.GetPostalCode(), - getOrderResponse.GetPhone(), - getOrderResponse.GetCountryCode(), - getOrderResponse.GetStateOrRegion(), - getOrderResponse.GetCounty(), - getOrderResponse.GetDistrict(), - out countryAllowsShipping, - out countryAllowsBilling); - - if (model.IsShippable && !countryAllowsShipping) - { - tempData[AmazonPayPlugin.SystemName + "ShippingToCountryNotAllowed"] = true; - model.RedirectAction = "ShippingAddress"; - model.RedirectController = "Checkout"; - model.Result = AmazonPayResultType.Redirect; - return model; - } - - if (address.Email.IsEmpty()) - { - address.Email = customer.Email; - } - - var existingAddress = customer.Addresses.ToList().FindAddress(address, true); - if (existingAddress == null) - { - customer.Addresses.Add(address); - customer.ShippingAddress = model.IsShippable ? address : null; - } - else - { - customer.ShippingAddress = model.IsShippable ? existingAddress : null; - } - - _customerService.UpdateCustomer(customer); - model.Result = AmazonPayResultType.None; - return model; - } - else - { - LogError(getOrderRequest, getOrderResponse); - } - } - } - else if (type == AmazonPayRequestType.PaymentMethod) - { + _httpContext.Session.SafeRemove("AmazonPayFailedPaymentReason"); + } + else if (type == AmazonPayRequestType.AuthenticationPublicInfo) + { + model.ButtonType = settings.AuthButtonType; + model.ButtonColor = settings.AuthButtonColor; + model.ButtonSize = settings.AuthButtonSize; + } + else if (type == AmazonPayRequestType.Address) + { + if (!model.IsShippable) + { + model.RedirectAction = "ShippingMethod"; + model.RedirectController = "Checkout"; + model.Result = AmazonPayResultType.Redirect; + return model; + } + + var shippingToCountryNotAllowed = tempData[AmazonPayPlugin.SystemName + "ShippingToCountryNotAllowed"]; + + if (shippingToCountryNotAllowed != null && true == (bool)shippingToCountryNotAllowed) + { + model.Warning = T("Plugins.Payments.AmazonPay.ShippingToCountryNotAllowed"); + } + } + else if (type == AmazonPayRequestType.ShippingMethod) + { + model.RedirectAction = model.RedirectController = ""; + + if (model.IsShippable) + { + var client = CreateClient(settings); + var getOrderRequest = new GetOrderReferenceDetailsRequest() + .WithMerchantId(settings.SellerId) + .WithAmazonOrderReferenceId(model.OrderReferenceId) + .WithAccessToken(model.AddressConsentToken); + + var getOrderResponse = client.GetOrderReferenceDetails(getOrderRequest); + if (getOrderResponse.GetSuccess()) + { + // Billing address not available here. getOrderResponse.GetBillingAddressDetails() is null. + //if (FindAndApplyAddress(getOrderResponse, customer, model.IsShippable, true)) + var countryAllowsShipping = true; + var countryAllowsBilling = true; + + var address = CreateAddress( + getOrderResponse.GetEmail(), + getOrderResponse.GetBuyerShippingName(), + getOrderResponse.GetAddressLine1(), + getOrderResponse.GetAddressLine2(), + getOrderResponse.GetAddressLine3(), + getOrderResponse.GetCity(), + getOrderResponse.GetPostalCode(), + getOrderResponse.GetPhone(), + getOrderResponse.GetCountryCode(), + getOrderResponse.GetStateOrRegion(), + getOrderResponse.GetCounty(), + getOrderResponse.GetDistrict(), + out countryAllowsShipping, + out countryAllowsBilling); + + if (model.IsShippable && !countryAllowsShipping) + { + tempData[AmazonPayPlugin.SystemName + "ShippingToCountryNotAllowed"] = true; + model.RedirectAction = "ShippingAddress"; + model.RedirectController = "Checkout"; + model.Result = AmazonPayResultType.Redirect; + return model; + } + + if (address.Email.IsEmpty()) + { + address.Email = customer.Email; + } + + var existingAddress = customer.Addresses.ToList().FindAddress(address, true); + if (existingAddress == null) + { + customer.Addresses.Add(address); + customer.ShippingAddress = model.IsShippable ? address : null; + } + else + { + customer.ShippingAddress = model.IsShippable ? existingAddress : null; + } + + _customerService.UpdateCustomer(customer); + model.Result = AmazonPayResultType.None; + return model; + } + else + { + LogError(getOrderRequest, getOrderResponse); + } + } + } + else if (type == AmazonPayRequestType.PaymentMethod) + { _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.SelectedPaymentMethod, AmazonPayPlugin.SystemName, store.Id); var paymentFailure = _httpContext.Session["AmazonPayFailedPaymentReason"] as string; if (paymentFailure.IsCaseInsensitiveEqual("InvalidPaymentMethod")) - { - _services.Notifier.Warning(new LocalizedString(T("Payment.PayingFailed"))); - } - else if (paymentFailure.IsCaseInsensitiveEqual("PaymentMethodNotAllowed")) - { - _services.Notifier.Warning(new LocalizedString(T("Plugins.Payments.AmazonPay.AuthorizationSoftDeclineMessage"))); - } + { + _services.Notifier.Warning(new LocalizedString(T("Payment.PayingFailed"))); + } + else if (paymentFailure.IsCaseInsensitiveEqual("PaymentMethodNotAllowed")) + { + _services.Notifier.Warning(new LocalizedString(T("Plugins.Payments.AmazonPay.AuthorizationSoftDeclineMessage"))); + } else if (paymentFailure.IsCaseInsensitiveEqual("AuthenticationStatusAbandoned")) { _services.Notifier.Warning(new LocalizedString(T("Plugins.Payments.AmazonPay.AuthenticationStatusAbandonedMessage"))); } else - { - decimal? shoppingCartTotalBase = _orderTotalCalculationService.GetShoppingCartTotal(cart); - if (shoppingCartTotalBase.HasValue) - { - var client = CreateClient(settings); - var setOrderRequest = new SetOrderReferenceDetailsRequest() - .WithMerchantId(settings.SellerId) - .WithAmazonOrderReferenceId(model.OrderReferenceId) - .WithPlatformId(PlatformId) - .WithAmount(shoppingCartTotalBase.Value) - .WithCurrencyCode(ConvertCurrency(store.PrimaryStoreCurrency.CurrencyCode)) - .WithStoreName(store.Name); - - var setOrderResponse = client.SetOrderReferenceDetails(setOrderRequest); - if (!setOrderResponse.GetSuccess()) - { - LogError(setOrderRequest, setOrderResponse); - } - } - } - - // This is ugly... - var paymentRequest = _httpContext.Session["OrderPaymentInfo"] as ProcessPaymentRequest; - if (paymentRequest == null) - { + { + decimal? shoppingCartTotalBase = _orderTotalCalculationService.GetShoppingCartTotal(cart); + if (shoppingCartTotalBase.HasValue) + { + var client = CreateClient(settings); + var setOrderRequest = new SetOrderReferenceDetailsRequest() + .WithMerchantId(settings.SellerId) + .WithAmazonOrderReferenceId(model.OrderReferenceId) + .WithPlatformId(PlatformId) + .WithAmount(shoppingCartTotalBase.Value) + .WithCurrencyCode(ConvertCurrency(store.PrimaryStoreCurrency.CurrencyCode)) + .WithStoreName(store.Name); + + var setOrderResponse = client.SetOrderReferenceDetails(setOrderRequest); + if (!setOrderResponse.GetSuccess()) + { + LogError(setOrderRequest, setOrderResponse); + } + } + } + + // This is ugly... + var paymentRequest = _httpContext.Session["OrderPaymentInfo"] as ProcessPaymentRequest; + if (paymentRequest == null) + { _httpContext.Session["OrderPaymentInfo"] = new ProcessPaymentRequest { OrderGuid = Guid.NewGuid() }; - } - } - else if (type == AmazonPayRequestType.OrderReviewData) - { - if (model.IsShippable) - { - var shippingOption = customer.GetAttribute(SystemCustomerAttributeNames.SelectedShippingOption, store.Id); + } + } + else if (type == AmazonPayRequestType.OrderReviewData) + { + if (model.IsShippable) + { + var shippingOption = customer.GetAttribute(SystemCustomerAttributeNames.SelectedShippingOption, store.Id); if (shippingOption != null) { model.ShippingMethod = shippingOption.Name; } - } - - if (customer.BillingAddress != null) - { - model.BillingAddress.PrepareModel(customer.BillingAddress, false, _addressSettings); - } - } - } - catch (Exception ex) - { - Logger.Error(ex); - _services.Notifier.Error(new LocalizedString(ex.Message)); - } - - return model; - } - - private void ProcessAuthorizationResult(AmazonPaySettings settings, Order order, AmazonPayData data) - { - var orderAttribute = DeserializeOrderAttribute(order); - - if (data.State.IsCaseInsensitiveEqual("Pending")) - { - return; - } - - var newResult = data.State.Grow(data.ReasonCode, " "); - - if (_orderProcessingService.CanMarkOrderAsAuthorized(order)) - { - _orderProcessingService.MarkAsAuthorized(order); - } - - if (data.State.IsCaseInsensitiveEqual("Closed") && data.ReasonCode.IsCaseInsensitiveEqual("OrderReferenceCanceled") && _orderProcessingService.CanVoidOffline(order)) - { - // Cancelation at amazon seller central. - _orderProcessingService.VoidOffline(order); - } - else if (data.State.IsCaseInsensitiveEqual("Declined") && _orderProcessingService.CanVoidOffline(order)) - { - _orderProcessingService.VoidOffline(order); - } - - if (!newResult.IsCaseInsensitiveEqual(order.AuthorizationTransactionResult)) - { - order.AuthorizationTransactionResult = newResult; - - if (order.CaptureTransactionId.IsEmpty() && data.CaptureId.HasValue()) - { - // Captured at amazon seller central. - order.CaptureTransactionId = data.CaptureId; - } - - _orderService.UpdateOrder(order); - - AddOrderNote(settings, order, ToInfoString(data), true); - } - } - - private void ProcessCaptureResult(Client client, AmazonPaySettings settings, Order order, AmazonPayData data) - { - if (data.State.IsCaseInsensitiveEqual("Pending")) - { - return; - } - - var newResult = data.State.Grow(data.ReasonCode, " "); - - if (data.State.IsCaseInsensitiveEqual("Completed") && _orderProcessingService.CanMarkOrderAsPaid(order)) - { - _orderProcessingService.MarkOrderAsPaid(order); - } - else if (data.State.IsCaseInsensitiveEqual("Declined") && _orderProcessingService.CanVoidOffline(order)) - { - var authDetailsRequest = new GetAuthorizationDetailsRequest() - .WithMerchantId(settings.SellerId) - .WithAmazonAuthorizationId(order.AuthorizationTransactionId); - - var authDetailsResponse = client.GetAuthorizationDetails(authDetailsRequest); - if (authDetailsResponse.GetSuccess()) - { - if (authDetailsResponse.GetAuthorizationState().IsCaseInsensitiveEqual("Open")) - { - _orderProcessingService.VoidOffline(order); - } - } - else - { - LogError(authDetailsRequest, authDetailsResponse); - } - } - - if (!newResult.IsCaseInsensitiveEqual(order.CaptureTransactionResult)) - { - order.CaptureTransactionResult = newResult; - _orderService.UpdateOrder(order); - - AddOrderNote(settings, order, ToInfoString(data), true); - } - } - - private void ProcessRefundResult(Client client, AmazonPaySettings settings, Order order, AmazonPayData data) - { - if (data.State.IsCaseInsensitiveEqual("Pending")) - { - return; - } - - if (data.RefundedAmount != null && data.RefundedAmount.Amount != decimal.Zero) - { - // Totally refunded amount. - // We could only process it once cause otherwise order.RefundedAmount would getting wrong. - if (order.RefundedAmount == decimal.Zero) - { - decimal refundAmount = data.RefundedAmount.Amount; - decimal receivable = order.OrderTotal - refundAmount; - - if (receivable <= decimal.Zero) - { - if (_orderProcessingService.CanRefundOffline(order)) - { - _orderProcessingService.RefundOffline(order); - - if (settings.DataFetching == AmazonPayDataFetchingType.Polling) - { - AddOrderNote(settings, order, ToInfoString(data), true); - } - } - } - else - { - if (_orderProcessingService.CanPartiallyRefundOffline(order, refundAmount)) - { - _orderProcessingService.PartiallyRefundOffline(order, refundAmount); - - if (settings.DataFetching == AmazonPayDataFetchingType.Polling) - { - AddOrderNote(settings, order, ToInfoString(data), true); - } - } - } - } - } - - if (settings.DataFetching == AmazonPayDataFetchingType.Ipn) - { - AddOrderNote(settings, order, ToInfoString(data), true); - } - } - - private void PollingLoop(PollingLoopData data, Func poll) - { - try - { - var sleepMillSec = 8000; - var loopMillSec = 90000; - var startTime = DateTime.Now.TimeOfDay; - - for (int i = 0; i < 99 && (DateTime.Now.TimeOfDay.Milliseconds - startTime.Milliseconds) <= loopMillSec; ++i) - { - // Inside the loop cause other instances are also updating the order. - data.Order = _orderService.GetOrderById(data.OrderId); - - if (data.Settings == null) - data.Settings = _services.Settings.LoadSetting(data.Order.StoreId); - - if (data.Client == null) - data.Client = CreateClient(data.Settings); - - if (!poll()) - break; - - Thread.Sleep(sleepMillSec); - } - } - catch (Exception exception) - { - Logger.Error(exception); - } - } - private void EarlyPolling(int orderId, AmazonPaySettings settings) - { - // The Authorization object moves to the Open state after remaining in the Pending state for 30 seconds. - var d = new PollingLoopData(orderId); - d.Settings = settings; - - PollingLoop(d, () => - { - if (d.Order.AuthorizationTransactionId.IsEmpty()) - return false; - - var authDetailsRequest = new GetAuthorizationDetailsRequest() - .WithMerchantId(d.Settings.SellerId) - .WithAmazonAuthorizationId(d.Order.AuthorizationTransactionId); - - var authDetailsResponse = d.Client.GetAuthorizationDetails(authDetailsRequest); - if (!authDetailsResponse.GetSuccess()) - return false; - - var details = GetDetails(authDetailsResponse); - if (!details.State.IsCaseInsensitiveEqual("pending")) - { - ProcessAuthorizationResult(d.Settings, d.Order, details); - return false; - } - - return true; - }); - - - PollingLoop(d, () => - { - if (d.Order.CaptureTransactionId.IsEmpty()) - return false; - - var captureDetailsRequest = new GetCaptureDetailsRequest() - .WithMerchantId(d.Settings.SellerId) - .WithAmazonCaptureId(d.Order.CaptureTransactionId); - - var captureDetailsResponse = d.Client.GetCaptureDetails(captureDetailsRequest); - if (!captureDetailsResponse.GetSuccess()) - return false; - - var details = GetDetails(captureDetailsResponse); - ProcessCaptureResult(d.Client, d.Settings, d.Order, details); - - return details.State.IsCaseInsensitiveEqual("pending"); - }); - } - - public void CloseOrderReference(AmazonPaySettings settings, Order order) - { - // You can still perform captures against any open authorizations, but you cannot create any new authorizations on the - // Order Reference object. You can still execute refunds against the Order Reference object. - var orderAttribute = DeserializeOrderAttribute(order); - if (!orderAttribute.OrderReferenceClosed && orderAttribute.OrderReferenceId.HasValue()) - { - var client = CreateClient(settings); - var closeRequest = new CloseOrderReferenceRequest() - .WithMerchantId(settings.SellerId) - .WithAmazonOrderReferenceId(orderAttribute.OrderReferenceId); - - var closeResponse = client.CloseOrderReference(closeRequest); - if (closeResponse.GetSuccess()) - { - orderAttribute.OrderReferenceClosed = true; - SerializeOrderAttribute(orderAttribute, order); - } - else - { - LogError(closeRequest, closeResponse, true); - } - } - } - - public void AddCustomerOrderNoteLoop(AmazonPayActionState state) - { - try - { - var sleepMillSec = 4000; - var loopMillSec = 40000; - var startTime = DateTime.Now.TimeOfDay; - - for (var i = 0; i < 99 && (DateTime.Now.TimeOfDay.Milliseconds - startTime.Milliseconds) <= loopMillSec; ++i) - { - var order = _orderService.GetOrderByGuid(state.OrderGuid); - if (order != null) - { - var sb = new StringBuilder(T("Plugins.Payments.AmazonPay.AuthorizationHardDeclineMessage")); - - if (state.Errors != null) - { - foreach (var error in state.Errors) - { - sb.AppendFormat("

              {0}

              ", error); - } - } - - var orderNote = new OrderNote - { - DisplayToCustomer = true, - Note = sb.ToString(), - CreatedOnUtc = DateTime.UtcNow, - }; - - order.OrderNotes.Add(orderNote); - _orderService.UpdateOrder(order); - - _services.MessageFactory.SendNewOrderNoteAddedCustomerNotification(orderNote, _services.WorkContext.WorkingLanguage.Id); - break; - } - - Thread.Sleep(sleepMillSec); - } - } - catch (Exception ex) - { - Logger.Error(ex); - } - } - - public void GetBillingAddress() - { - var store = _services.StoreContext.CurrentStore; - var customer = _services.WorkContext.CurrentCustomer; - var settings = _services.Settings.LoadSetting(store.Id); - var state = _httpContext.GetAmazonPayState(_services.Localization); - var client = CreateClient(settings); - - var getOrderRequest = new GetOrderReferenceDetailsRequest() - .WithMerchantId(settings.SellerId) - .WithAmazonOrderReferenceId(state.OrderReferenceId) - .WithAccessToken(state.AccessToken); - - var getOrderResponse = client.GetOrderReferenceDetails(getOrderRequest); - if (getOrderResponse.GetSuccess()) - { - var details = getOrderResponse.GetBillingAddressDetails(); - if (details != null) - { - var countryAllowsShipping = true; - var countryAllowsBilling = true; - var email = getOrderResponse.GetEmail(); - - var address = CreateAddress( - email, - details.GetName(), - details.GetAddressLine1(), - details.GetAddressLine2(), - details.GetAddressLine3(), - details.GetCity(), - details.GetPostalCode(), - details.GetPhone(), - details.GetCountryCode(), - details.GetStateOrRegion(), - details.GetCounty(), - details.GetDistrict(), - out countryAllowsShipping, - out countryAllowsBilling); - - // We must ignore countryAllowsBilling because the customer cannot choose another billing address in Amazon checkout. - //if (!countryAllowsBilling) - // return; - - var existingAddress = customer.Addresses.ToList().FindAddress(address, true); - if (existingAddress == null) - { - customer.Addresses.Add(address); - customer.BillingAddress = address; - } - else - { - customer.BillingAddress = existingAddress; - } - - if (settings.CanSaveEmailAndPhone(customer.Email)) - { - customer.Email = email; - } - _customerService.UpdateCustomer(customer); - - if (settings.CanSaveEmailAndPhone(customer.GetAttribute(SystemCustomerAttributeNames.Phone, store.Id))) - { - var phone = details.GetPhone(); - if (phone.IsEmpty()) - { - phone = getOrderResponse.GetPhone(); - } - _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.Phone, phone); - } - } - else - { - // No billing address at Amazon? We cannot proceed. - } - } - else - { - LogError(getOrderRequest, getOrderResponse); - } - } - - public bool SetOrderReferenceDetails( - Client client, - AmazonPaySettings settings, - AmazonPayCheckoutState state, - string sellerNote, - out string errorMessage) - { - errorMessage = null; + } - if (client == null) + if (customer.BillingAddress != null) + { + model.BillingAddress.PrepareModel(customer.BillingAddress, false, _addressSettings); + } + } + } + catch (Exception ex) { - client = CreateClient(settings); + Logger.Error(ex); + _services.Notifier.Error(new LocalizedString(ex.Message)); } - var store = _services.StoreContext.CurrentStore; - var customer = _services.WorkContext.CurrentCustomer; - var cart = customer.GetCartItems(ShoppingCartType.ShoppingCart, store.Id); - decimal? cartTotal = _orderTotalCalculationService.GetShoppingCartTotal(cart); + return model; + } - var setOrderRequest = new SetOrderReferenceDetailsRequest() - .WithMerchantId(settings.SellerId) - .WithAmazonOrderReferenceId(state.OrderReferenceId) - .WithPlatformId(PlatformId) - .WithAmount(cartTotal) - .WithCurrencyCode(ConvertCurrency(store.PrimaryStoreCurrency.CurrencyCode)) - .WithStoreName(store.Name); + private void ProcessAuthorizationResult(AmazonPaySettings settings, Order order, AmazonPayData data) + { + var orderAttribute = DeserializeOrderAttribute(order); - var paymentRequest = _httpContext.Session["OrderPaymentInfo"] as ProcessPaymentRequest; - if (paymentRequest != null) + if (data.State.IsCaseInsensitiveEqual("Pending")) { - setOrderRequest = setOrderRequest.WithSellerOrderId(paymentRequest.OrderGuid.ToString()); + return; } - if (sellerNote != null) + var newResult = data.State.Grow(data.ReasonCode, " "); + + if (_orderProcessingService.CanMarkOrderAsAuthorized(order)) { - setOrderRequest = setOrderRequest.WithSellerNote(sellerNote); + _orderProcessingService.MarkAsAuthorized(order); } - var setOrderResponse = client.SetOrderReferenceDetails(setOrderRequest); - if (!setOrderResponse.GetSuccess()) + if (data.State.IsCaseInsensitiveEqual("Closed") && data.ReasonCode.IsCaseInsensitiveEqual("OrderReferenceCanceled") && _orderProcessingService.CanVoidOffline(order)) { - errorMessage = LogError(setOrderRequest, setOrderResponse); - return false; + // Cancelation at amazon seller central. + _orderProcessingService.VoidOffline(order); + } + else if (data.State.IsCaseInsensitiveEqual("Declined") && _orderProcessingService.CanVoidOffline(order)) + { + _orderProcessingService.VoidOffline(order); } - if (setOrderResponse.GetHasConstraint()) + if (!newResult.IsCaseInsensitiveEqual(order.AuthorizationTransactionResult)) { - var ids = setOrderResponse.GetConstraintIdList(); - var descriptions = setOrderResponse.GetDescriptionList(); + order.AuthorizationTransactionResult = newResult; - foreach (var id in ids) + if (order.CaptureTransactionId.IsEmpty() && data.CaptureId.HasValue()) { - var idx = ids.IndexOf(id); - if (idx < descriptions.Count) - { - errorMessage = $"{descriptions[idx]} ({id})"; - Logger.Error(errorMessage); - } - - if (id.IsCaseInsensitiveEqual("PaymentMethodNotAllowed")) - { - // Must be redirected to checkout payment page. - return false; - } + // Captured at amazon seller central. + order.CaptureTransactionId = data.CaptureId; } - } - return true; + _orderService.UpdateOrder(order); + + AddOrderNote(settings, order, ToInfoString(data), true); + } } - public bool ConfirmOrderReference() + private void ProcessCaptureResult(Client client, AmazonPaySettings settings, Order order, AmazonPayData data) { - try + if (data.State.IsCaseInsensitiveEqual("Pending")) { - var store = _services.StoreContext.CurrentStore; - var settings = _services.Settings.LoadSetting(store.Id); - var state = _httpContext.GetAmazonPayState(_services.Localization); - var client = CreateClient(settings); - - // Only call SetOrderReferenceDetailsRequest if it's in draft state, otherwise it returns InvalidOrderReferenceStatus error. - if (!state.DetailsSet && !SetOrderReferenceDetails(client, settings, state, null, out var _)) - { - return false; - } - - state.DetailsSet = true; + return; + } - var confirmationResultUrl = _services.WebHelper.GetStoreLocation() + "Plugins/SmartStore.AmazonPay/AmazonPayCheckout/ConfirmationResult"; + var newResult = data.State.Grow(data.ReasonCode, " "); - // Confirm order. This already generates the payment object at Amazon. - // Can be called multiple times, for example to move the order reference object from suspended to open state. - var confirmRequest = new ConfirmOrderReferenceRequest() + if (data.State.IsCaseInsensitiveEqual("Completed") && _orderProcessingService.CanMarkOrderAsPaid(order)) + { + _orderProcessingService.MarkOrderAsPaid(order); + } + else if (data.State.IsCaseInsensitiveEqual("Declined") && _orderProcessingService.CanVoidOffline(order)) + { + var authDetailsRequest = new GetAuthorizationDetailsRequest() .WithMerchantId(settings.SellerId) - .WithAmazonOrderReferenceId(state.OrderReferenceId) - .WithSuccessUrl(confirmationResultUrl); + .WithAmazonAuthorizationId(order.AuthorizationTransactionId); - var confirmResponse = client.ConfirmOrderReference(confirmRequest); - if (confirmResponse.GetSuccess()) + var authDetailsResponse = client.GetAuthorizationDetails(authDetailsRequest); + if (authDetailsResponse.GetSuccess()) { - state.IsConfirmed = true; - return true; + if (authDetailsResponse.GetAuthorizationState().IsCaseInsensitiveEqual("Open")) + { + _orderProcessingService.VoidOffline(order); + } } else { - LogError(confirmRequest, confirmResponse); - return false; + LogError(authDetailsRequest, authDetailsResponse); } } - catch (Exception ex) + + if (!newResult.IsCaseInsensitiveEqual(order.CaptureTransactionResult)) { - Logger.Error(ex); - return false; + order.CaptureTransactionResult = newResult; + _orderService.UpdateOrder(order); + + AddOrderNote(settings, order, ToInfoString(data), true); } } - public ProcessPaymentResult ProcessPayment(ProcessPaymentRequest request) - { - var result = new ProcessPaymentResult(); - var orderNoteErrors = new List(); - var informCustomerAboutErrors = false; - var informCustomerAddErrors = false; - var isSynchronous = false; - string error = null; - - result.NewPaymentStatus = PaymentStatus.Pending; - - _httpContext.Session.SafeRemove("AmazonPayFailedPaymentReason"); - _httpContext.Session.SafeRemove("AmazonPayCheckoutCompletedNote"); - - try - { - var store = _services.StoreService.GetStoreById(request.StoreId); - var settings = _services.Settings.LoadSetting(store.Id); - var captureNow = settings.TransactionType == AmazonPayTransactionType.AuthorizeAndCapture; - var state = _httpContext.GetAmazonPayState(_services.Localization); - var client = CreateClient(settings); - AuthorizeRequest authRequest = null; - AuthorizeResponse authResponse = null; + private void ProcessRefundResult(Client client, AmazonPaySettings settings, Order order, AmazonPayData data) + { + if (data.State.IsCaseInsensitiveEqual("Pending")) + { + return; + } - informCustomerAboutErrors = settings.InformCustomerAboutErrors; - informCustomerAddErrors = settings.InformCustomerAddErrors; - - // Authorize. - if (settings.AuthorizeMethod == AmazonPayAuthorizeMethod.Omnichronous) - { - // First try synchronously. - authResponse = AuthorizePayment(settings, state, store, request, client, true, out authRequest); - - if (authResponse.GetAuthorizationState().IsCaseInsensitiveEqual("Declined") && - authResponse.GetReasonCode().IsCaseInsensitiveEqual("TransactionTimedOut")) - { - // Second try asynchronously. - // Transaction is always in pending state after return. - authResponse = AuthorizePayment(settings, state, store, request, client, false, out authRequest); - } - else - { - isSynchronous = true; - } - } - else - { - isSynchronous = settings.AuthorizeMethod == AmazonPayAuthorizeMethod.Synchronous; - authResponse = AuthorizePayment(settings, state, store, request, client, isSynchronous, out authRequest); - } - - // Process authorization response. - if (authResponse.GetSuccess()) - { - var reason = authResponse.GetReasonCode(); - - result.AuthorizationTransactionId = authResponse.GetAuthorizationId(); - result.AuthorizationTransactionCode = authResponse.GetAuthorizationReferenceId(); - result.AuthorizationTransactionResult = authResponse.GetAuthorizationState(); - - if (captureNow) - { - var idList = authResponse.GetCaptureIdList(); - if (idList.Any()) - { - result.CaptureTransactionId = idList.First(); - } - } - - if (isSynchronous) - { - if (result.AuthorizationTransactionResult.IsCaseInsensitiveEqual("Open")) - { - result.NewPaymentStatus = PaymentStatus.Authorized; - } - else if (result.AuthorizationTransactionResult.IsCaseInsensitiveEqual("Closed")) - { - if (captureNow && reason.IsCaseInsensitiveEqual("MaxCapturesProcessed")) - { - result.NewPaymentStatus = PaymentStatus.Paid; - } - } - } - else - { - _httpContext.Session["AmazonPayCheckoutCompletedNote"] = T("Plugins.Payments.AmazonPay.AsyncPaymentAuthrizationNote").Text; - } - - if (reason.IsCaseInsensitiveEqual("InvalidPaymentMethod") || reason.IsCaseInsensitiveEqual("AmazonRejected") || - reason.IsCaseInsensitiveEqual("ProcessingFailure") || reason.IsCaseInsensitiveEqual("TransactionTimedOut") || - reason.IsCaseInsensitiveEqual("TransactionTimeout")) - { - error = authResponse.GetReasonDescription(); - error = error.HasValue() ? $"{reason}: {error}" : reason; - - if (reason.IsCaseInsensitiveEqual("AmazonRejected")) - { - // Must be logged out and redirected to shopping cart. - _httpContext.Session["AmazonPayFailedPaymentReason"] = reason; - _httpContext.Response.RedirectToRoute("ShoppingCart"); - } - else if (reason.IsCaseInsensitiveEqual("InvalidPaymentMethod")) - { - // Must be redirected to checkout payment page. - _httpContext.Session["AmazonPayFailedPaymentReason"] = reason; + if (data.RefundedAmount != null && data.RefundedAmount.Amount != decimal.Zero) + { + // Totally refunded amount. + // We could only process it once cause otherwise order.RefundedAmount would getting wrong. + if (order.RefundedAmount == decimal.Zero) + { + decimal refundAmount = data.RefundedAmount.Amount; + decimal receivable = order.OrderTotal - refundAmount; - // Review: confirmation required to get order reference object from suspended to open state again. - state.IsConfirmed = false; - state.FormData = null; + if (receivable <= decimal.Zero) + { + if (_orderProcessingService.CanRefundOffline(order)) + { + _orderProcessingService.RefundOffline(order); - var urlHelper = new UrlHelper(_httpContext.Request.RequestContext); - _httpContext.Response.Redirect(urlHelper.Action("PaymentMethod", "Checkout", new { area = "" })); - } - } - } - else - { - error = LogError(authRequest, authResponse); - } - } - catch (Exception ex) - { - Logger.Error(ex); - error = ex.Message; - } - - if (error.HasValue()) - { - if (isSynchronous) - { - result.AddError(error); - } - else - { - orderNoteErrors.Add(error); - } - } - - // Customer needs to be informed of an Amazon error here. Hooking OrderPlaced.CustomerNotification won't work - // cause of asynchronous processing. Solution: we add a customer order note that is also send as an email. - if (informCustomerAboutErrors && orderNoteErrors.Any()) - { - var state = new AmazonPayActionState { OrderGuid = request.OrderGuid }; - - if (informCustomerAddErrors) - { - state.Errors = new List(); - state.Errors.AddRange(orderNoteErrors); - } - - AsyncRunner.Run((c, ct, o) => - { - var obj = o as AmazonPayActionState; - c.Resolve().AddCustomerOrderNoteLoop(obj); - }, - state); - } - - return result; - } - - public void PostProcessPayment(PostProcessPaymentRequest request) - { - // early polling... note we do not have the amazon billing address yet - //try - //{ - // int orderId = request.Order.Id; - // var settings = _services.Settings.LoadSetting(request.Order.StoreId); - - // if (orderId != 0 && settings.StatusFetching == AmazonPayStatusFetchingType.Polling) - // { - // AsyncRunner.Run((container, obj) => - // { - // var amazonService = container.Resolve(); - // amazonService.EarlyPolling(orderId, obj as AmazonPaySettings); - // }, - // settings, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); - // } - //} - //catch (Exception exc) - //{ - // Logger.Error(exc); - //} - - try - { - var state = _httpContext.GetAmazonPayState(_services.Localization); - var orderAttribute = new AmazonPayOrderAttribute - { - OrderReferenceId = state.OrderReferenceId - }; - - if (request.Order.PaymentStatus == PaymentStatus.Paid) - { - var settings = _services.Settings.LoadSetting(request.Order.StoreId); - var client = CreateClient(settings); - var closeRequest = new CloseOrderReferenceRequest() - .WithMerchantId(settings.SellerId) - .WithAmazonOrderReferenceId(orderAttribute.OrderReferenceId); - - var closeResponse = client.CloseOrderReference(closeRequest); - if (closeResponse.GetSuccess()) - { - orderAttribute.OrderReferenceClosed = true; - } - else - { - LogError(closeRequest, closeResponse, true); - } - } - - SerializeOrderAttribute(orderAttribute, request.Order); - } - catch (Exception exception) - { - Logger.Error(exception); - } - } - - public CapturePaymentResult Capture(CapturePaymentRequest request) - { - var result = new CapturePaymentResult - { - NewPaymentStatus = request.Order.PaymentStatus - }; - - try - { - var settings = _services.Settings.LoadSetting(request.Order.StoreId); - var store = _services.StoreService.GetStoreById(request.Order.StoreId); - var client = CreateClient(settings); - - var captureRequest = new CaptureRequest() - .WithMerchantId(settings.SellerId) - .WithAmazonAuthorizationId(request.Order.AuthorizationTransactionId) - .WithCaptureReferenceId(GetRandomId("Capture")) - .WithCurrencyCode(ConvertCurrency(store.PrimaryStoreCurrency.CurrencyCode)) - .WithAmount(request.Order.OrderTotal); - - var captureResponse = client.Capture(captureRequest); - if (captureResponse.GetSuccess()) - { - var state = captureResponse.GetCaptureState(); - - result.CaptureTransactionId = captureResponse.GetCaptureId(); - result.CaptureTransactionResult = state.Grow(captureResponse.GetReasonCode(), " "); - - if (state.IsCaseInsensitiveEqual("completed")) - { - result.NewPaymentStatus = PaymentStatus.Paid; - } - } - else - { - var message = LogError(captureRequest, captureResponse); - result.AddError(message); - } - } - catch (Exception exception) - { - Logger.Error(exception); - result.AddError(exception.Message); - } - - return result; - } - - public RefundPaymentResult Refund(RefundPaymentRequest request) - { - var result = new RefundPaymentResult - { - NewPaymentStatus = request.Order.PaymentStatus - }; - - try - { - var settings = _services.Settings.LoadSetting(request.Order.StoreId); - var store = _services.StoreService.GetStoreById(request.Order.StoreId); - var client = CreateClient(settings); - - var refundRequest = new RefundRequest() - .WithMerchantId(settings.SellerId) - .WithAmazonCaptureId(request.Order.CaptureTransactionId) - .WithRefundReferenceId(GetRandomId("Refund")) - .WithCurrencyCode(ConvertCurrency(store.PrimaryStoreCurrency.CurrencyCode)) - .WithAmount(request.AmountToRefund); - - var refundResponse = client.Refund(refundRequest); - if (refundResponse.GetSuccess()) - { - result.NewPaymentStatus = request.IsPartialRefund ? PaymentStatus.PartiallyRefunded : PaymentStatus.Refunded; - - var refundId = refundResponse.GetAmazonRefundId(); - if (refundId.HasValue() && request.Order.Id != 0) - { - _genericAttributeService.InsertAttribute(new GenericAttribute - { - EntityId = request.Order.Id, - KeyGroup = "Order", - Key = AmazonPayPlugin.SystemName + ".RefundId", - Value = refundId, - StoreId = request.Order.StoreId - }); - } - } - else - { - var message = LogError(refundRequest, refundResponse); - result.AddError(message); - } - } - catch (Exception exception) - { - Logger.Error(exception); - result.AddError(exception.Message); - } - - return result; - } - - public VoidPaymentResult Void(VoidPaymentRequest request) - { - var result = new VoidPaymentResult - { - NewPaymentStatus = request.Order.PaymentStatus - }; - - // redundant... cause payment infrastructure hides "void" and displays "refund" instead. - //if (request.Order.PaymentStatus == PaymentStatus.Paid) - //{ - // var refundRequest = new RefundPaymentRequest() - // { - // Order = request.Order, - // IsPartialRefund = false, - // AmountToRefund = request.Order.OrderTotal - // }; - - // var refundResult = Refund(refundRequest); - - // result.Errors.AddRange(refundResult.Errors); - // return result; - //} - - try - { - if (request.Order.PaymentStatus == PaymentStatus.Pending || request.Order.PaymentStatus == PaymentStatus.Authorized) - { - var settings = _services.Settings.LoadSetting(request.Order.StoreId); - var orderAttribute = DeserializeOrderAttribute(request.Order); - var client = CreateClient(settings); - - var cancelRequest = new CancelOrderReferenceRequest() - .WithMerchantId(settings.SellerId) - .WithAmazonOrderReferenceId(orderAttribute.OrderReferenceId); - - var cancelResponse = client.CancelOrderReference(cancelRequest); - if (cancelResponse.GetSuccess()) - { - result.NewPaymentStatus = PaymentStatus.Voided; - } - else - { - var message = LogError(cancelRequest, cancelResponse); - result.AddError(message); - } - } - } - catch (Exception exception) - { - Logger.Error(exception); - result.AddError(exception.Message); - } - - return result; - } - - public void ProcessIpn(HttpRequestBase request) - { - string json = null; - try - { - using (var reader = new StreamReader(request.InputStream)) - { - json = reader.ReadToEnd(); - } - - var parser = new IpnHandler(request.Headers, json); - var type = parser.GetNotificationType(); - AmazonPayData data = null; - Order order = null; - string errorId = null; - var isAuthorize = false; - var isCapture = false; - var isRefund = false; - - if (type.IsCaseInsensitiveEqual("PaymentAuthorize")) - { - isAuthorize = true; - var response = parser.GetAuthorizeResponse(); - data = GetDetails(response); - } - else if (type.IsCaseInsensitiveEqual("PaymentCapture")) - { - isCapture = true; - var response = parser.GetCaptureResponse(); - data = GetDetails(response); - } - else if (type.IsCaseInsensitiveEqual("PaymentRefund")) - { - isRefund = true; - var response = parser.GetRefundResponse(); - data = GetDetails(response); - } - else - { - // Ignore, e.g. OrderReferenceNotification. - return; - } - - if (data == null) - { - Logger.Error($"No IPN details for notification type {type}"); - return; - } - - data.MessageType = type; - data.MessageId = parser.GetNotificationReferenceId(); - - // Find order. - if (isAuthorize) - { - if ((order = _orderService.GetOrderByPaymentAuthorization(AmazonPayPlugin.SystemName, data.AuthorizationId)) == null) - errorId = $"AuthorizationId {data.AuthorizationId.NaIfEmpty()}"; - } - else if (isCapture) - { - if ((order = _orderService.GetOrderByPaymentCapture(AmazonPayPlugin.SystemName, data.CaptureId)) == null) - order = _orderRepository.GetOrderByAmazonId(data.AnyAmazonId); - - if (order == null) - errorId = $"CaptureId {data.CaptureId.NaIfEmpty()}"; - } - else if (isRefund) - { - var attribute = _genericAttributeService.GetAttributes(AmazonPayPlugin.SystemName + ".RefundId", "Order") - .Where(x => x.Value == data.RefundId) - .FirstOrDefault(); - - if (attribute == null || (order = _orderService.GetOrderById(attribute.EntityId)) == null) - order = _orderRepository.GetOrderByAmazonId(data.AnyAmazonId); - - if (order == null) - errorId = $"RefundId {data.RefundId.NaIfEmpty()}"; - } - - if (errorId.HasValue()) - { - Logger.Warn(T("Plugins.Payments.AmazonPay.OrderNotFound", errorId)); - } - - if (order == null || !IsPaymentMethodActive(order.StoreId)) - { - return; - } - - var settings = _services.Settings.LoadSetting(order.StoreId); - if (settings.DataFetching != AmazonPayDataFetchingType.Ipn) - { - return; - } - - if (isAuthorize) - { - ProcessAuthorizationResult(settings, order, data); - } - else if (isCapture) - { - var client = CreateClient(settings); - ProcessCaptureResult(client, settings, order, data); - } - else if (isRefund) - { - var client = CreateClient(settings); - ProcessRefundResult(client, settings, order, data); - } - } - catch (Exception exception) - { - Logger.Error(exception, json); - } - } - - public void StartDataPolling() - { - try - { - // Ignore cancelled and completed (paid and shipped) orders. ignore old orders too. - var pollingMaxOrderCreationDays = _services.Settings.GetSettingByKey("AmazonPaySettings.PollingMaxOrderCreationDays", 31); - var isTooOld = DateTime.UtcNow.AddDays(-(pollingMaxOrderCreationDays)); - - var query = - from x in _orderRepository.Table - where x.PaymentMethodSystemName == AmazonPayPlugin.SystemName && x.CreatedOnUtc > isTooOld && - !x.Deleted && x.OrderStatusId < (int)OrderStatus.Complete && x.PaymentStatusId != (int)PaymentStatus.Voided - orderby x.Id descending - select x; - - var orders = query.ToList(); - //"- start polling {0} orders".FormatWith(orders.Count).Dump(); - - foreach (var order in orders) - { - try - { - var settings = _services.Settings.LoadSetting(order.StoreId); - if (settings.DataFetching == AmazonPayDataFetchingType.Polling) - { - var client = CreateClient(settings); - - if (order.AuthorizationTransactionId.HasValue()) - { - var authDetailsRequest = new GetAuthorizationDetailsRequest() - .WithMerchantId(settings.SellerId) - .WithAmazonAuthorizationId(order.AuthorizationTransactionId); - - var authDetailsResponse = client.GetAuthorizationDetails(authDetailsRequest); - if (authDetailsResponse.GetSuccess()) - { - var details = GetDetails(authDetailsResponse); - ProcessAuthorizationResult(settings, order, details); - } - else - { - LogError(authDetailsRequest, authDetailsResponse); - } - } - - if (order.CaptureTransactionId.HasValue()) - { - if (_orderProcessingService.CanMarkOrderAsPaid(order) || _orderProcessingService.CanVoidOffline(order) || - _orderProcessingService.CanRefundOffline(order) || _orderProcessingService.CanPartiallyRefundOffline(order, 0.01M)) - { - var captureDetailsRequest = new GetCaptureDetailsRequest() - .WithMerchantId(settings.SellerId) - .WithAmazonCaptureId(order.CaptureTransactionId); - - var captureDetailsResponse = client.GetCaptureDetails(captureDetailsRequest); - if (captureDetailsResponse.GetSuccess()) - { - var details = GetDetails(captureDetailsResponse); - ProcessCaptureResult(client, settings, order, details); - - if (_orderProcessingService.CanRefundOffline(order) || _orderProcessingService.CanPartiallyRefundOffline(order, 0.01M)) - { - // Note status polling: we cannot use GetRefundDetails to reflect refund(s) made at Amazon seller central cause we - // do not have any refund-id and there is no api endpoint that provide them. So we only can process CaptureDetails.RefundedAmount. - ProcessRefundResult(client, settings, order, details); - } - } - else - { - LogError(captureDetailsRequest, captureDetailsResponse); - } - } - } - } - } - catch (Exception exception) - { - Logger.Error(exception); - } - } - } - catch (Exception exception) - { - Logger.Error(exception); - } - } - - public void ShareKeys(string payload, int storeId) - { - if (payload.IsEmpty()) - { - throw new SmartException(T("Plugins.Payments.AmazonPay.MissingPayloadParameter")); - } - - dynamic json = JObject.Parse(payload); - var settings = _services.Settings.LoadSetting(storeId); - - var encryptedPayload = (string)json.encryptedPayload; - if (encryptedPayload.HasValue()) - { - throw new SmartException(T("Plugins.Payments.AmazonPay.EncryptionNotSupported")); - } - else - { - settings.SellerId = (string)json.merchant_id; - settings.AccessKey = (string)json.access_key; - settings.SecretKey = (string)json.secret_key; - settings.ClientId = (string)json.client_id; - //settings.ClientSecret = (string)json.client_secret; - } - - using (_services.Settings.BeginScope()) - { - _services.Settings.SaveSetting(settings, x => x.SellerId, storeId, false); - _services.Settings.SaveSetting(settings, x => x.AccessKey, storeId, false); - _services.Settings.SaveSetting(settings, x => x.SecretKey, storeId, false); - _services.Settings.SaveSetting(settings, x => x.ClientId, storeId, false); - } - } - - #region IExternalProviderAuthorizer - - public AuthorizeState Authorize(string returnUrl, bool? verifyResponse = null) - { - string error = null; - string email = null; - string name = null; - string userId = null; - var accessToken = _httpContext.Request.QueryString["access_token"]; - - if (accessToken.HasValue()) - { - var settings = _services.Settings.LoadSetting(); - var client = CreateClient(settings); - var jsonString = client.GetUserInfo(accessToken); - if (jsonString.HasValue()) - { - var json = JObject.Parse(jsonString); - - email = json.GetValue("email").ToString(); - name = json.GetValue("name").ToString(); - userId = json.GetValue("user_id").ToString(); - - if (email.IsEmpty() || name.IsEmpty() || userId.IsEmpty()) - { - error = T("Plugins.Payments.AmazonPay.IncompleteProfileDetails") + - $" Email: {email.NaIfEmpty()}, name: {name.NaIfEmpty()}, userId: {userId.NaIfEmpty()}."; - } - } - else - { - error = T("Plugins.Payments.AmazonPay.IncompleteProfileDetails"); - } - } - else - { - error = T("Plugins.Payments.AmazonPay.MissingAccessToken"); - } - - if (error.HasValue()) - { - var state = new AuthorizeState("", OpenAuthenticationStatus.Error); - state.AddError(error); - Logger.Error(error); - return state; - } - - string firstName, lastName; - name.ToFirstAndLastName(out firstName, out lastName); - - var claims = new UserClaims(); - claims.Name = new NameClaims(); - claims.Contact = new ContactClaims(); - claims.Contact.Email = email; - claims.Name.FullName = name; - claims.Name.First = firstName; - claims.Name.Last = lastName; - - var parameters = new AmazonAuthenticationParameters(); - parameters.ExternalIdentifier = userId; - parameters.AddClaim(claims); - - var result = _authorizer.Value.Authorize(parameters); - - return new AuthorizeState(returnUrl, result); - } - - #endregion - } + if (settings.DataFetching == AmazonPayDataFetchingType.Polling) + { + AddOrderNote(settings, order, ToInfoString(data), true); + } + } + } + else + { + if (_orderProcessingService.CanPartiallyRefundOffline(order, refundAmount)) + { + _orderProcessingService.PartiallyRefundOffline(order, refundAmount); + + if (settings.DataFetching == AmazonPayDataFetchingType.Polling) + { + AddOrderNote(settings, order, ToInfoString(data), true); + } + } + } + } + } + + if (settings.DataFetching == AmazonPayDataFetchingType.Ipn) + { + AddOrderNote(settings, order, ToInfoString(data), true); + } + } + + private void PollingLoop(PollingLoopData data, Func poll) + { + try + { + var sleepMillSec = 8000; + var loopMillSec = 90000; + var startTime = DateTime.Now.TimeOfDay; + + for (int i = 0; i < 99 && (DateTime.Now.TimeOfDay.Milliseconds - startTime.Milliseconds) <= loopMillSec; ++i) + { + // Inside the loop cause other instances are also updating the order. + data.Order = _orderService.GetOrderById(data.OrderId); + + if (data.Settings == null) + data.Settings = _services.Settings.LoadSetting(data.Order.StoreId); + + if (data.Client == null) + data.Client = CreateClient(data.Settings); + + if (!poll()) + break; + + Thread.Sleep(sleepMillSec); + } + } + catch (Exception exception) + { + Logger.Error(exception); + } + } + private void EarlyPolling(int orderId, AmazonPaySettings settings) + { + // The Authorization object moves to the Open state after remaining in the Pending state for 30 seconds. + var d = new PollingLoopData(orderId); + d.Settings = settings; + + PollingLoop(d, () => + { + if (d.Order.AuthorizationTransactionId.IsEmpty()) + return false; + + var authDetailsRequest = new GetAuthorizationDetailsRequest() + .WithMerchantId(d.Settings.SellerId) + .WithAmazonAuthorizationId(d.Order.AuthorizationTransactionId); + + var authDetailsResponse = d.Client.GetAuthorizationDetails(authDetailsRequest); + if (!authDetailsResponse.GetSuccess()) + return false; + + var details = GetDetails(authDetailsResponse); + if (!details.State.IsCaseInsensitiveEqual("pending")) + { + ProcessAuthorizationResult(d.Settings, d.Order, details); + return false; + } + + return true; + }); + + + PollingLoop(d, () => + { + if (d.Order.CaptureTransactionId.IsEmpty()) + return false; + + var captureDetailsRequest = new GetCaptureDetailsRequest() + .WithMerchantId(d.Settings.SellerId) + .WithAmazonCaptureId(d.Order.CaptureTransactionId); + + var captureDetailsResponse = d.Client.GetCaptureDetails(captureDetailsRequest); + if (!captureDetailsResponse.GetSuccess()) + return false; + + var details = GetDetails(captureDetailsResponse); + ProcessCaptureResult(d.Client, d.Settings, d.Order, details); + + return details.State.IsCaseInsensitiveEqual("pending"); + }); + } + + public void CloseOrderReference(AmazonPaySettings settings, Order order) + { + // You can still perform captures against any open authorizations, but you cannot create any new authorizations on the + // Order Reference object. You can still execute refunds against the Order Reference object. + var orderAttribute = DeserializeOrderAttribute(order); + if (!orderAttribute.OrderReferenceClosed && orderAttribute.OrderReferenceId.HasValue()) + { + var client = CreateClient(settings); + var closeRequest = new CloseOrderReferenceRequest() + .WithMerchantId(settings.SellerId) + .WithAmazonOrderReferenceId(orderAttribute.OrderReferenceId); + + var closeResponse = client.CloseOrderReference(closeRequest); + if (closeResponse.GetSuccess()) + { + orderAttribute.OrderReferenceClosed = true; + SerializeOrderAttribute(orderAttribute, order); + } + else + { + LogError(closeRequest, closeResponse, true); + } + } + } + + public void AddCustomerOrderNoteLoop(AmazonPayActionState state) + { + try + { + var sleepMillSec = 4000; + var loopMillSec = 40000; + var startTime = DateTime.Now.TimeOfDay; + + for (var i = 0; i < 99 && (DateTime.Now.TimeOfDay.Milliseconds - startTime.Milliseconds) <= loopMillSec; ++i) + { + var order = _orderService.GetOrderByGuid(state.OrderGuid); + if (order != null) + { + var sb = new StringBuilder(T("Plugins.Payments.AmazonPay.AuthorizationHardDeclineMessage")); + + if (state.Errors != null) + { + foreach (var error in state.Errors) + { + sb.AppendFormat("

              {0}

              ", error); + } + } + + var orderNote = new OrderNote + { + DisplayToCustomer = true, + Note = sb.ToString(), + CreatedOnUtc = DateTime.UtcNow, + }; + + order.OrderNotes.Add(orderNote); + _orderService.UpdateOrder(order); + + _services.MessageFactory.SendNewOrderNoteAddedCustomerNotification(orderNote, _services.WorkContext.WorkingLanguage.Id); + break; + } + + Thread.Sleep(sleepMillSec); + } + } + catch (Exception ex) + { + Logger.Error(ex); + } + } + + public void GetBillingAddress() + { + var store = _services.StoreContext.CurrentStore; + var customer = _services.WorkContext.CurrentCustomer; + var settings = _services.Settings.LoadSetting(store.Id); + var state = _httpContext.GetAmazonPayState(_services.Localization); + var client = CreateClient(settings); + + var getOrderRequest = new GetOrderReferenceDetailsRequest() + .WithMerchantId(settings.SellerId) + .WithAmazonOrderReferenceId(state.OrderReferenceId) + .WithAccessToken(state.AccessToken); + + var getOrderResponse = client.GetOrderReferenceDetails(getOrderRequest); + if (getOrderResponse.GetSuccess()) + { + var details = getOrderResponse.GetBillingAddressDetails(); + if (details != null) + { + var countryAllowsShipping = true; + var countryAllowsBilling = true; + var email = getOrderResponse.GetEmail(); + + var address = CreateAddress( + email, + details.GetName(), + details.GetAddressLine1(), + details.GetAddressLine2(), + details.GetAddressLine3(), + details.GetCity(), + details.GetPostalCode(), + details.GetPhone(), + details.GetCountryCode(), + details.GetStateOrRegion(), + details.GetCounty(), + details.GetDistrict(), + out countryAllowsShipping, + out countryAllowsBilling); + + // We must ignore countryAllowsBilling because the customer cannot choose another billing address in Amazon checkout. + //if (!countryAllowsBilling) + // return; + + var existingAddress = customer.Addresses.ToList().FindAddress(address, true); + if (existingAddress == null) + { + customer.Addresses.Add(address); + customer.BillingAddress = address; + } + else + { + customer.BillingAddress = existingAddress; + } + + if (settings.CanSaveEmailAndPhone(customer.Email)) + { + customer.Email = email; + } + _customerService.UpdateCustomer(customer); + + if (settings.CanSaveEmailAndPhone(customer.GetAttribute(SystemCustomerAttributeNames.Phone, store.Id))) + { + var phone = details.GetPhone(); + if (phone.IsEmpty()) + { + phone = getOrderResponse.GetPhone(); + } + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.Phone, phone); + } + } + else + { + // No billing address at Amazon? We cannot proceed. + } + } + else + { + LogError(getOrderRequest, getOrderResponse); + } + } + + public bool SetOrderReferenceDetails( + Client client, + AmazonPaySettings settings, + AmazonPayCheckoutState state, + string sellerNote, + out string errorMessage) + { + errorMessage = null; + + if (client == null) + { + client = CreateClient(settings); + } + + var store = _services.StoreContext.CurrentStore; + var customer = _services.WorkContext.CurrentCustomer; + var cart = customer.GetCartItems(ShoppingCartType.ShoppingCart, store.Id); + decimal? cartTotal = _orderTotalCalculationService.GetShoppingCartTotal(cart); + + var setOrderRequest = new SetOrderReferenceDetailsRequest() + .WithMerchantId(settings.SellerId) + .WithAmazonOrderReferenceId(state.OrderReferenceId) + .WithPlatformId(PlatformId) + .WithAmount(cartTotal) + .WithCurrencyCode(ConvertCurrency(store.PrimaryStoreCurrency.CurrencyCode)) + .WithStoreName(store.Name); + + var paymentRequest = _httpContext.Session["OrderPaymentInfo"] as ProcessPaymentRequest; + if (paymentRequest != null && paymentRequest.OrderGuid != Guid.Empty) + { + setOrderRequest = setOrderRequest.WithSellerOrderId(paymentRequest.OrderGuid.ToString()); + } + + if (sellerNote != null) + { + setOrderRequest = setOrderRequest.WithSellerNote(sellerNote); + } + + var setOrderResponse = client.SetOrderReferenceDetails(setOrderRequest); + if (!setOrderResponse.GetSuccess()) + { + errorMessage = LogError(setOrderRequest, setOrderResponse); + return false; + } + + if (setOrderResponse.GetHasConstraint()) + { + var ids = setOrderResponse.GetConstraintIdList(); + var descriptions = setOrderResponse.GetDescriptionList(); + + foreach (var id in ids) + { + var idx = ids.IndexOf(id); + if (idx < descriptions.Count) + { + errorMessage = $"{descriptions[idx]} ({id})"; + Logger.Error(errorMessage); + } + + if (id.IsCaseInsensitiveEqual("PaymentMethodNotAllowed")) + { + // Must be redirected to checkout payment page. + return false; + } + } + } + + return true; + } + + public bool ConfirmOrderReference() + { + try + { + var store = _services.StoreContext.CurrentStore; + var settings = _services.Settings.LoadSetting(store.Id); + var state = _httpContext.GetAmazonPayState(_services.Localization); + var client = CreateClient(settings); + + // Only call SetOrderReferenceDetailsRequest if it's in draft state, otherwise it returns InvalidOrderReferenceStatus error. + if (!state.DetailsSet && !SetOrderReferenceDetails(client, settings, state, null, out var _)) + { + return false; + } + + state.DetailsSet = true; + + var confirmationResultUrl = _services.WebHelper.GetStoreLocation() + "Plugins/SmartStore.AmazonPay/AmazonPayCheckout/ConfirmationResult"; + + // Confirm order. This already generates the payment object at Amazon. + // Can be called multiple times, for example to move the order reference object from suspended to open state. + var confirmRequest = new ConfirmOrderReferenceRequest() + .WithMerchantId(settings.SellerId) + .WithAmazonOrderReferenceId(state.OrderReferenceId) + .WithSuccessUrl(confirmationResultUrl); + + var confirmResponse = client.ConfirmOrderReference(confirmRequest); + if (confirmResponse.GetSuccess()) + { + state.IsConfirmed = true; + return true; + } + else + { + LogError(confirmRequest, confirmResponse); + return false; + } + } + catch (Exception ex) + { + Logger.Error(ex); + return false; + } + } + + public ProcessPaymentResult ProcessPayment(ProcessPaymentRequest request) + { + var result = new ProcessPaymentResult(); + var orderNoteErrors = new List(); + var informCustomerAboutErrors = false; + var informCustomerAddErrors = false; + var isSynchronous = false; + string error = null; + + result.NewPaymentStatus = PaymentStatus.Pending; + + _httpContext.Session.SafeRemove("AmazonPayFailedPaymentReason"); + _httpContext.Session.SafeRemove("AmazonPayCheckoutCompletedNote"); + + try + { + var store = _services.StoreService.GetStoreById(request.StoreId); + var settings = _services.Settings.LoadSetting(store.Id); + var captureNow = settings.TransactionType == AmazonPayTransactionType.AuthorizeAndCapture; + var state = _httpContext.GetAmazonPayState(_services.Localization); + var client = CreateClient(settings); + AuthorizeRequest authRequest = null; + AuthorizeResponse authResponse = null; + + informCustomerAboutErrors = settings.InformCustomerAboutErrors; + informCustomerAddErrors = settings.InformCustomerAddErrors; + + // Authorize. + if (settings.AuthorizeMethod == AmazonPayAuthorizeMethod.Omnichronous) + { + // First try synchronously. + authResponse = AuthorizePayment(settings, state, store, request, client, true, out authRequest); + + if (authResponse.GetAuthorizationState().IsCaseInsensitiveEqual("Declined") && + authResponse.GetReasonCode().IsCaseInsensitiveEqual("TransactionTimedOut")) + { + // Second try asynchronously. + // Transaction is always in pending state after return. + authResponse = AuthorizePayment(settings, state, store, request, client, false, out authRequest); + } + else + { + isSynchronous = true; + } + } + else + { + isSynchronous = settings.AuthorizeMethod == AmazonPayAuthorizeMethod.Synchronous; + authResponse = AuthorizePayment(settings, state, store, request, client, isSynchronous, out authRequest); + } + + // Process authorization response. + if (authResponse.GetSuccess()) + { + var reason = authResponse.GetReasonCode(); + + result.AuthorizationTransactionId = authResponse.GetAuthorizationId(); + result.AuthorizationTransactionCode = authResponse.GetAuthorizationReferenceId(); + result.AuthorizationTransactionResult = authResponse.GetAuthorizationState(); + + if (captureNow) + { + var idList = authResponse.GetCaptureIdList(); + if (idList.Any()) + { + result.CaptureTransactionId = idList.First(); + } + } + + if (isSynchronous) + { + if (result.AuthorizationTransactionResult.IsCaseInsensitiveEqual("Open")) + { + result.NewPaymentStatus = PaymentStatus.Authorized; + } + else if (result.AuthorizationTransactionResult.IsCaseInsensitiveEqual("Closed")) + { + if (captureNow && reason.IsCaseInsensitiveEqual("MaxCapturesProcessed")) + { + result.NewPaymentStatus = PaymentStatus.Paid; + } + } + } + else + { + _httpContext.Session["AmazonPayCheckoutCompletedNote"] = T("Plugins.Payments.AmazonPay.AsyncPaymentAuthrizationNote").Text; + } + + if (reason.IsCaseInsensitiveEqual("InvalidPaymentMethod") || reason.IsCaseInsensitiveEqual("AmazonRejected") || + reason.IsCaseInsensitiveEqual("ProcessingFailure") || reason.IsCaseInsensitiveEqual("TransactionTimedOut") || + reason.IsCaseInsensitiveEqual("TransactionTimeout")) + { + error = authResponse.GetReasonDescription(); + error = error.HasValue() ? $"{reason}: {error}" : reason; + + if (reason.IsCaseInsensitiveEqual("AmazonRejected")) + { + // Must be logged out and redirected to shopping cart. + _httpContext.Session["AmazonPayFailedPaymentReason"] = reason; + _httpContext.Response.RedirectToRoute("ShoppingCart"); + } + else if (reason.IsCaseInsensitiveEqual("InvalidPaymentMethod")) + { + // Must be redirected to checkout payment page. + _httpContext.Session["AmazonPayFailedPaymentReason"] = reason; + + // Review: confirmation required to get order reference object from suspended to open state again. + state.IsConfirmed = false; + state.FormData = null; + + var urlHelper = new UrlHelper(_httpContext.Request.RequestContext); + _httpContext.Response.Redirect(urlHelper.Action("PaymentMethod", "Checkout", new { area = "" })); + } + } + } + else + { + error = LogError(authRequest, authResponse); + } + } + catch (Exception ex) + { + Logger.Error(ex); + error = ex.Message; + } + + if (error.HasValue()) + { + if (isSynchronous) + { + result.AddError(error); + } + else + { + orderNoteErrors.Add(error); + } + } + + // Customer needs to be informed of an Amazon error here. Hooking OrderPlaced.CustomerNotification won't work + // cause of asynchronous processing. Solution: we add a customer order note that is also send as an email. + if (informCustomerAboutErrors && orderNoteErrors.Any()) + { + var state = new AmazonPayActionState { OrderGuid = request.OrderGuid }; + + if (informCustomerAddErrors) + { + state.Errors = new List(); + state.Errors.AddRange(orderNoteErrors); + } + + AsyncRunner.Run((c, ct, o) => + { + var obj = o as AmazonPayActionState; + c.Resolve().AddCustomerOrderNoteLoop(obj); + }, + state); + } + + return result; + } + + public void PostProcessPayment(PostProcessPaymentRequest request) + { + // early polling... note we do not have the amazon billing address yet + //try + //{ + // int orderId = request.Order.Id; + // var settings = _services.Settings.LoadSetting(request.Order.StoreId); + + // if (orderId != 0 && settings.StatusFetching == AmazonPayStatusFetchingType.Polling) + // { + // AsyncRunner.Run((container, obj) => + // { + // var amazonService = container.Resolve(); + // amazonService.EarlyPolling(orderId, obj as AmazonPaySettings); + // }, + // settings, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); + // } + //} + //catch (Exception exc) + //{ + // Logger.Error(exc); + //} + + try + { + var state = _httpContext.GetAmazonPayState(_services.Localization); + var orderAttribute = new AmazonPayOrderAttribute + { + OrderReferenceId = state.OrderReferenceId + }; + + if (request.Order.PaymentStatus == PaymentStatus.Paid) + { + var settings = _services.Settings.LoadSetting(request.Order.StoreId); + var client = CreateClient(settings); + var closeRequest = new CloseOrderReferenceRequest() + .WithMerchantId(settings.SellerId) + .WithAmazonOrderReferenceId(orderAttribute.OrderReferenceId); + + var closeResponse = client.CloseOrderReference(closeRequest); + if (closeResponse.GetSuccess()) + { + orderAttribute.OrderReferenceClosed = true; + } + else + { + LogError(closeRequest, closeResponse, true); + } + } + + SerializeOrderAttribute(orderAttribute, request.Order); + } + catch (Exception exception) + { + Logger.Error(exception); + } + } + + public CapturePaymentResult Capture(CapturePaymentRequest request) + { + var result = new CapturePaymentResult + { + NewPaymentStatus = request.Order.PaymentStatus + }; + + try + { + var settings = _services.Settings.LoadSetting(request.Order.StoreId); + var store = _services.StoreService.GetStoreById(request.Order.StoreId); + var client = CreateClient(settings); + + var captureRequest = new CaptureRequest() + .WithMerchantId(settings.SellerId) + .WithAmazonAuthorizationId(request.Order.AuthorizationTransactionId) + .WithCaptureReferenceId(GetRandomId("Capture")) + .WithCurrencyCode(ConvertCurrency(store.PrimaryStoreCurrency.CurrencyCode)) + .WithAmount(request.Order.OrderTotal); + + var captureResponse = client.Capture(captureRequest); + if (captureResponse.GetSuccess()) + { + var state = captureResponse.GetCaptureState(); + + result.CaptureTransactionId = captureResponse.GetCaptureId(); + result.CaptureTransactionResult = state.Grow(captureResponse.GetReasonCode(), " "); + + if (state.IsCaseInsensitiveEqual("completed")) + { + result.NewPaymentStatus = PaymentStatus.Paid; + } + } + else + { + var message = LogError(captureRequest, captureResponse); + result.AddError(message); + } + } + catch (Exception exception) + { + Logger.Error(exception); + result.AddError(exception.Message); + } + + return result; + } + + public RefundPaymentResult Refund(RefundPaymentRequest request) + { + var result = new RefundPaymentResult + { + NewPaymentStatus = request.Order.PaymentStatus + }; + + try + { + var settings = _services.Settings.LoadSetting(request.Order.StoreId); + var store = _services.StoreService.GetStoreById(request.Order.StoreId); + var client = CreateClient(settings); + + var refundRequest = new RefundRequest() + .WithMerchantId(settings.SellerId) + .WithAmazonCaptureId(request.Order.CaptureTransactionId) + .WithRefundReferenceId(GetRandomId("Refund")) + .WithCurrencyCode(ConvertCurrency(store.PrimaryStoreCurrency.CurrencyCode)) + .WithAmount(request.AmountToRefund); + + var refundResponse = client.Refund(refundRequest); + if (refundResponse.GetSuccess()) + { + result.NewPaymentStatus = request.IsPartialRefund ? PaymentStatus.PartiallyRefunded : PaymentStatus.Refunded; + + var refundId = refundResponse.GetAmazonRefundId(); + if (refundId.HasValue() && request.Order.Id != 0) + { + _genericAttributeService.InsertAttribute(new GenericAttribute + { + EntityId = request.Order.Id, + KeyGroup = "Order", + Key = AmazonPayPlugin.SystemName + ".RefundId", + Value = refundId, + StoreId = request.Order.StoreId + }); + } + } + else + { + var message = LogError(refundRequest, refundResponse); + result.AddError(message); + } + } + catch (Exception exception) + { + Logger.Error(exception); + result.AddError(exception.Message); + } + + return result; + } + + public VoidPaymentResult Void(VoidPaymentRequest request) + { + var result = new VoidPaymentResult + { + NewPaymentStatus = request.Order.PaymentStatus + }; + + // redundant... cause payment infrastructure hides "void" and displays "refund" instead. + //if (request.Order.PaymentStatus == PaymentStatus.Paid) + //{ + // var refundRequest = new RefundPaymentRequest() + // { + // Order = request.Order, + // IsPartialRefund = false, + // AmountToRefund = request.Order.OrderTotal + // }; + + // var refundResult = Refund(refundRequest); + + // result.Errors.AddRange(refundResult.Errors); + // return result; + //} + + try + { + if (request.Order.PaymentStatus == PaymentStatus.Pending || request.Order.PaymentStatus == PaymentStatus.Authorized) + { + var settings = _services.Settings.LoadSetting(request.Order.StoreId); + var orderAttribute = DeserializeOrderAttribute(request.Order); + var client = CreateClient(settings); + + var cancelRequest = new CancelOrderReferenceRequest() + .WithMerchantId(settings.SellerId) + .WithAmazonOrderReferenceId(orderAttribute.OrderReferenceId); + + var cancelResponse = client.CancelOrderReference(cancelRequest); + if (cancelResponse.GetSuccess()) + { + result.NewPaymentStatus = PaymentStatus.Voided; + } + else + { + var message = LogError(cancelRequest, cancelResponse); + result.AddError(message); + } + } + } + catch (Exception exception) + { + Logger.Error(exception); + result.AddError(exception.Message); + } + + return result; + } + + public void ProcessIpn(HttpRequestBase request) + { + string json = null; + try + { + using (var reader = new StreamReader(request.InputStream)) + { + json = reader.ReadToEnd(); + } + + var parser = new IpnHandler(request.Headers, json); + var type = parser.GetNotificationType(); + AmazonPayData data = null; + Order order = null; + string errorId = null; + var isAuthorize = false; + var isCapture = false; + var isRefund = false; + + if (type.IsCaseInsensitiveEqual("PaymentAuthorize")) + { + isAuthorize = true; + var response = parser.GetAuthorizeResponse(); + data = GetDetails(response); + } + else if (type.IsCaseInsensitiveEqual("PaymentCapture")) + { + isCapture = true; + var response = parser.GetCaptureResponse(); + data = GetDetails(response); + } + else if (type.IsCaseInsensitiveEqual("PaymentRefund")) + { + isRefund = true; + var response = parser.GetRefundResponse(); + data = GetDetails(response); + } + else + { + // Ignore, e.g. OrderReferenceNotification. + return; + } + + if (data == null) + { + Logger.Error($"No IPN details for notification type {type}"); + return; + } + + data.MessageType = type; + data.MessageId = parser.GetNotificationReferenceId(); + + // Find order. + if (isAuthorize) + { + if ((order = _orderService.GetOrderByPaymentAuthorization(AmazonPayPlugin.SystemName, data.AuthorizationId)) == null) + errorId = $"AuthorizationId {data.AuthorizationId.NaIfEmpty()}"; + } + else if (isCapture) + { + if ((order = _orderService.GetOrderByPaymentCapture(AmazonPayPlugin.SystemName, data.CaptureId)) == null) + order = _orderRepository.GetOrderByAmazonId(data.AnyAmazonId); + + if (order == null) + errorId = $"CaptureId {data.CaptureId.NaIfEmpty()}"; + } + else if (isRefund) + { + var attribute = _genericAttributeService.GetAttributes(AmazonPayPlugin.SystemName + ".RefundId", "Order") + .Where(x => x.Value == data.RefundId) + .FirstOrDefault(); + + if (attribute == null || (order = _orderService.GetOrderById(attribute.EntityId)) == null) + order = _orderRepository.GetOrderByAmazonId(data.AnyAmazonId); + + if (order == null) + errorId = $"RefundId {data.RefundId.NaIfEmpty()}"; + } + + if (errorId.HasValue()) + { + Logger.Warn(T("Plugins.Payments.AmazonPay.OrderNotFound", errorId)); + } + + if (order == null || !_paymentService.IsPaymentMethodActive(AmazonPayPlugin.SystemName, order.StoreId)) + { + return; + } + + var settings = _services.Settings.LoadSetting(order.StoreId); + if (settings.DataFetching != AmazonPayDataFetchingType.Ipn) + { + return; + } + + if (isAuthorize) + { + ProcessAuthorizationResult(settings, order, data); + } + else if (isCapture) + { + var client = CreateClient(settings); + ProcessCaptureResult(client, settings, order, data); + } + else if (isRefund) + { + var client = CreateClient(settings); + ProcessRefundResult(client, settings, order, data); + } + } + catch (Exception exception) + { + Logger.Error(exception, json); + } + } + + public void StartDataPolling() + { + try + { + // Ignore cancelled and completed (paid and shipped) orders. ignore old orders too. + var pollingMaxOrderCreationDays = _services.Settings.GetSettingByKey("AmazonPaySettings.PollingMaxOrderCreationDays", 31); + var isTooOld = DateTime.UtcNow.AddDays(-(pollingMaxOrderCreationDays)); + + var query = + from x in _orderRepository.Table + where x.PaymentMethodSystemName == AmazonPayPlugin.SystemName && x.CreatedOnUtc > isTooOld && + !x.Deleted && x.OrderStatusId < (int)OrderStatus.Complete && x.PaymentStatusId != (int)PaymentStatus.Voided + orderby x.Id descending + select x; + + var orders = query.ToList(); + //"- start polling {0} orders".FormatWith(orders.Count).Dump(); + + foreach (var order in orders) + { + try + { + var settings = _services.Settings.LoadSetting(order.StoreId); + if (settings.DataFetching == AmazonPayDataFetchingType.Polling) + { + var client = CreateClient(settings); + + if (order.AuthorizationTransactionId.HasValue()) + { + var authDetailsRequest = new GetAuthorizationDetailsRequest() + .WithMerchantId(settings.SellerId) + .WithAmazonAuthorizationId(order.AuthorizationTransactionId); + + var authDetailsResponse = client.GetAuthorizationDetails(authDetailsRequest); + if (authDetailsResponse.GetSuccess()) + { + var details = GetDetails(authDetailsResponse); + ProcessAuthorizationResult(settings, order, details); + } + else + { + LogError(authDetailsRequest, authDetailsResponse); + } + } + + if (order.CaptureTransactionId.HasValue()) + { + if (_orderProcessingService.CanMarkOrderAsPaid(order) || _orderProcessingService.CanVoidOffline(order) || + _orderProcessingService.CanRefundOffline(order) || _orderProcessingService.CanPartiallyRefundOffline(order, 0.01M)) + { + var captureDetailsRequest = new GetCaptureDetailsRequest() + .WithMerchantId(settings.SellerId) + .WithAmazonCaptureId(order.CaptureTransactionId); + + var captureDetailsResponse = client.GetCaptureDetails(captureDetailsRequest); + if (captureDetailsResponse.GetSuccess()) + { + var details = GetDetails(captureDetailsResponse); + ProcessCaptureResult(client, settings, order, details); + + if (_orderProcessingService.CanRefundOffline(order) || _orderProcessingService.CanPartiallyRefundOffline(order, 0.01M)) + { + // Note status polling: we cannot use GetRefundDetails to reflect refund(s) made at Amazon seller central cause we + // do not have any refund-id and there is no api endpoint that provide them. So we only can process CaptureDetails.RefundedAmount. + ProcessRefundResult(client, settings, order, details); + } + } + else + { + LogError(captureDetailsRequest, captureDetailsResponse); + } + } + } + } + } + catch (Exception exception) + { + Logger.Error(exception); + } + } + } + catch (Exception exception) + { + Logger.Error(exception); + } + } + + public void ShareKeys(string payload, int storeId) + { + if (payload.IsEmpty()) + { + throw new SmartException(T("Plugins.Payments.AmazonPay.MissingPayloadParameter")); + } + + dynamic json = JObject.Parse(payload); + var settings = _services.Settings.LoadSetting(storeId); + + var encryptedPayload = (string)json.encryptedPayload; + if (encryptedPayload.HasValue()) + { + throw new SmartException(T("Plugins.Payments.AmazonPay.EncryptionNotSupported")); + } + else + { + settings.SellerId = (string)json.merchant_id; + settings.AccessKey = (string)json.access_key; + settings.SecretKey = (string)json.secret_key; + settings.ClientId = (string)json.client_id; + //settings.ClientSecret = (string)json.client_secret; + } + + using (_services.Settings.BeginScope()) + { + _services.Settings.SaveSetting(settings, x => x.SellerId, storeId, false); + _services.Settings.SaveSetting(settings, x => x.AccessKey, storeId, false); + _services.Settings.SaveSetting(settings, x => x.SecretKey, storeId, false); + _services.Settings.SaveSetting(settings, x => x.ClientId, storeId, false); + } + } + + #region IExternalProviderAuthorizer + + public AuthorizeState Authorize(string returnUrl, bool? verifyResponse = null) + { + string error = null; + string email = null; + string name = null; + string userId = null; + var accessToken = _httpContext.Request.QueryString["access_token"]; + + if (accessToken.HasValue()) + { + var settings = _services.Settings.LoadSetting(_services.StoreContext.CurrentStore.Id); + var client = CreateClient(settings); + var jsonString = client.GetUserInfo(accessToken); + if (jsonString.HasValue()) + { + var json = JObject.Parse(jsonString); + + email = json.GetValue("email").ToString(); + name = json.GetValue("name").ToString(); + userId = json.GetValue("user_id").ToString(); + + if (email.IsEmpty() || name.IsEmpty() || userId.IsEmpty()) + { + error = T("Plugins.Payments.AmazonPay.IncompleteProfileDetails") + + $" Email: {email.NaIfEmpty()}, name: {name.NaIfEmpty()}, userId: {userId.NaIfEmpty()}."; + } + } + else + { + error = T("Plugins.Payments.AmazonPay.IncompleteProfileDetails"); + } + } + else + { + error = T("Plugins.Payments.AmazonPay.MissingAccessToken"); + } + + if (error.HasValue()) + { + var state = new AuthorizeState("", OpenAuthenticationStatus.Error); + state.AddError(error); + Logger.Error(error); + return state; + } + + string firstName, lastName; + name.ToFirstAndLastName(out firstName, out lastName); + + var claims = new UserClaims(); + claims.Name = new NameClaims(); + claims.Contact = new ContactClaims(); + claims.Contact.Email = email; + claims.Name.FullName = name; + claims.Name.First = firstName; + claims.Name.Last = lastName; + + var parameters = new AmazonAuthenticationParameters(); + parameters.ExternalIdentifier = userId; + parameters.AddClaim(claims); + + var result = _authorizer.Value.Authorize(parameters); + + return new AuthorizeState(returnUrl, result); + } + + #endregion + } } diff --git a/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayServiceHelper.cs b/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayServiceHelper.cs index baf21a850a..9353f2538b 100644 --- a/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayServiceHelper.cs +++ b/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayServiceHelper.cs @@ -18,431 +18,419 @@ namespace SmartStore.AmazonPay.Services { - /// - /// Helper with utilities to keep the AmazonPayService tidy. - /// - public partial class AmazonPayService - { - /// - /// Also named "spId". - /// - internal static string PlatformId => "A3OJ83WFYM72IY"; - - internal static string LeadCode => "SPEXDEAPA-SmartStore.Net-CP-DP"; - - internal static string GetWidgetUrl(AmazonPaySettings settings) - { - switch (settings.Marketplace.EmptyNull().ToLower()) - { - case "us": - return settings.UseSandbox - ? "https://static-na.payments-amazon.com/OffAmazonPayments/us/sandbox/js/Widgets.js" - : "https://static-na.payments-amazon.com/OffAmazonPayments/us/js/Widgets.js"; - case "uk": - return settings.UseSandbox - ? "https://static-eu.payments-amazon.com/OffAmazonPayments/gbp/sandbox/lpa/js/Widgets.js" - : "https://static-eu.payments-amazon.com/OffAmazonPayments/gbp/lpa/js/Widgets.js"; - case "jp": - return settings.UseSandbox - ? "https://static-fe.payments-amazon.com/OffAmazonPayments/jp/sandbox/lpa/js/Widgets.js" - : "https://static-fe.payments-amazon.com/OffAmazonPayments/jp/lpa/js/Widgets.js"; - default: - return settings.UseSandbox - ? "https://static-eu.payments-amazon.com/OffAmazonPayments/eur/sandbox/lpa/js/Widgets.js" - : "https://static-eu.payments-amazon.com/OffAmazonPayments/eur/lpa/js/Widgets.js"; - } - } - - private string GetPluginUrl(string action, bool useSsl = false) - { - var pluginUrl = "{0}Plugins/SmartStore.AmazonPay/AmazonPay/{1}".FormatInvariant(_services.WebHelper.GetStoreLocation(useSsl), action); - return pluginUrl; - } - - private void SerializeOrderAttribute(AmazonPayOrderAttribute attribute, Order order) - { - if (attribute != null) - { - var sb = new StringBuilder(); - using (var writer = new StringWriter(sb)) - { - var serializer = new XmlSerializer(typeof(AmazonPayOrderAttribute)); - serializer.Serialize(writer, attribute); - - _genericAttributeService.SaveAttribute(order, AmazonPayPlugin.SystemName + ".OrderAttribute", sb.ToString(), order.StoreId); - } - } - } - - private AmazonPayOrderAttribute DeserializeOrderAttribute(Order order) - { - var serialized = order.GetAttribute(AmazonPayPlugin.SystemName + ".OrderAttribute", _genericAttributeService, order.StoreId); - - if (!serialized.HasValue()) - { - var attribute = new AmazonPayOrderAttribute(); - - // legacy < v.1.14 - attribute.OrderReferenceId = order.GetAttribute(AmazonPayPlugin.SystemName + ".OrderReferenceId", order.StoreId); - - return attribute; - } - - using (var reader = new StringReader(serialized)) - { - var serializer = new XmlSerializer(typeof(AmazonPayOrderAttribute)); - return (AmazonPayOrderAttribute)serializer.Deserialize(reader); - } - } - - private bool IsPaymentMethodActive(int storeId, bool logInactive = false) - { - var isActive = _paymentService.IsPaymentMethodActive(AmazonPayPlugin.SystemName, storeId); - - if (!isActive && logInactive) - { - Logger.Error(null, T("Plugins.Payments.AmazonPay.PaymentMethodNotActive", _services.StoreContext.CurrentStore.Name)); - } - - return isActive; - } - - private void AddOrderNote(AmazonPaySettings settings, Order order, string anyString = null, bool isIpn = false) - { - try - { - if (!settings.AddOrderNotes || order == null || anyString.IsEmpty()) - return; - - var sb = new StringBuilder(); - var faviconUrl = "{0}Plugins/{1}/Content/images/favicon.png".FormatInvariant(_services.WebHelper.GetStoreLocation(), AmazonPayPlugin.SystemName); - - sb.AppendFormat("", faviconUrl); - sb.Append(T("Plugins.Payments.AmazonPay.AmazonDataProcessed")); - sb.Append(":
              "); - sb.Append(anyString); - - if (isIpn) - { - order.HasNewPaymentNotification = true; - } - - order.OrderNotes.Add(new OrderNote - { - Note = sb.ToString(), - DisplayToCustomer = false, - CreatedOnUtc = DateTime.UtcNow - }); - - _orderService.UpdateOrder(order); - } - catch (Exception exception) - { - Logger.Error(exception); - } - } - - private Regions.currencyCode ConvertCurrency(string currencyCode) - { - switch (currencyCode.EmptyNull().ToLower()) - { - case "usd": - return Regions.currencyCode.USD; - case "gbp": - return Regions.currencyCode.GBP; - case "jpy": - return Regions.currencyCode.JPY; - case "aud": - return Regions.currencyCode.AUD; - case "zar": - return Regions.currencyCode.ZAR; - case "chf": - return Regions.currencyCode.CHF; - case "nok": - return Regions.currencyCode.NOK; - case "dkk": - return Regions.currencyCode.DKK; - case "sek": - return Regions.currencyCode.SEK; - case "nzd": - return Regions.currencyCode.NZD; - case "hkd": - return Regions.currencyCode.HKD; - default: - return Regions.currencyCode.EUR; - } - } - - private Address CreateAddress( - string email, - string buyerName, - string addressLine1, - string addressLine2, - string addressLine3, - string city, - string postalCode, - string phone, - string countryCode, - string stateRegion, - string county, - string destrict, - out bool countryAllowsShipping, - out bool countryAllowsBilling) - { - countryAllowsShipping = countryAllowsBilling = true; - - var address = new Address(); - address.CreatedOnUtc = DateTime.UtcNow; - address.Email = email; - address.ToFirstAndLastName(buyerName); - address.Address1 = addressLine1.EmptyNull().Trim().Truncate(4000); - address.Address2 = addressLine2.EmptyNull().Trim().Truncate(4000); - address.Address2 = address.Address2.Grow(addressLine3.EmptyNull().Trim(), ", ").Truncate(4000); - address.City = county.Grow(destrict, " ").Grow(city, " ").EmptyNull().Trim().Truncate(4000); - address.ZipPostalCode = postalCode.EmptyNull().Trim().Truncate(4000); - address.PhoneNumber = phone.EmptyNull().Trim().Truncate(4000); - - if (countryCode.HasValue()) - { - var country = _countryService.GetCountryByTwoOrThreeLetterIsoCode(countryCode); - if (country != null) - { - address.CountryId = country.Id; - countryAllowsShipping = country.AllowsShipping; - countryAllowsBilling = country.AllowsBilling; - } - } - - if (stateRegion.HasValue()) - { - var stateProvince = _stateProvinceService.GetStateProvinceByAbbreviation(stateRegion); - if (stateProvince != null) - { - address.StateProvinceId = stateProvince.Id; - } - } - - // Normalize. - if (address.Address1.IsEmpty() && address.Address2.HasValue()) - { - address.Address1 = address.Address2; - address.Address2 = null; - } - else if (address.Address1.HasValue() && address.Address1 == address.Address2) - { - address.Address2 = null; - } - - if (address.CountryId == 0) - { - address.CountryId = null; - } - - if (address.StateProvinceId == 0) - { - address.StateProvinceId = null; - } - - return address; - } - - //private void GetAddress(OrderReferenceDetailsResponse details, Address address, out bool countryAllowsShipping, out bool countryAllowsBilling) - //{ - // countryAllowsShipping = countryAllowsBilling = true; - - // address.Email = details.GetEmail(); - // address.ToFirstAndLastName(details.GetBuyerName()); - // address.Address1 = details.GetAddressLine1().EmptyNull().Trim().Truncate(4000); - // address.Address2 = details.GetAddressLine2().EmptyNull().Trim().Truncate(4000); - // address.Address2 = address.Address2.Grow(details.GetAddressLine3().EmptyNull().Trim(), ", ").Truncate(4000); - // address.City = details.GetCity().EmptyNull().Trim().Truncate(4000); - // address.ZipPostalCode = details.GetPostalCode().EmptyNull().Trim().Truncate(4000); - // address.PhoneNumber = details.GetPhone().EmptyNull().Trim().Truncate(4000); - - // var countryCode = details.GetCountryCode(); - // if (countryCode.HasValue()) - // { - // var country = _countryService.GetCountryByTwoOrThreeLetterIsoCode(countryCode); - // if (country != null) - // { - // address.CountryId = country.Id; - // countryAllowsShipping = country.AllowsShipping; - // countryAllowsBilling = country.AllowsBilling; - // } - // } - - // var stateRegion = details.GetStateOrRegion(); - // if (stateRegion.HasValue()) - // { - // var stateProvince = _stateProvinceService.GetStateProvinceByAbbreviation(stateRegion); - // if (stateProvince != null) - // { - // address.StateProvinceId = stateProvince.Id; - // } - // } - - // // Normalize. - // if (address.Address1.IsEmpty() && address.Address2.HasValue()) - // { - // address.Address1 = address.Address2; - // address.Address2 = null; - // } - // else if (address.Address1.HasValue() && address.Address1 == address.Address2) - // { - // address.Address2 = null; - // } - - // if (address.CountryId == 0) - // { - // address.CountryId = null; - // } - - // if (address.StateProvinceId == 0) - // { - // address.StateProvinceId = null; - // } - //} - - //private bool FindAndApplyAddress(OrderReferenceDetailsResponse details, Customer customer, bool isShippable, bool forceToTakeAmazonAddress) - //{ - // // PlaceOrder requires billing address but we don't get one from Amazon here. so use shipping address instead until we get it from amazon. - // var countryAllowsShipping = true; - // var countryAllowsBilling = true; - - // var address = new Address(); - // address.CreatedOnUtc = DateTime.UtcNow; - - // GetAddress(details, address, out countryAllowsShipping, out countryAllowsBilling); - - // if (isShippable && !countryAllowsShipping) - // return false; - - // if (address.Email.IsEmpty()) - // { - // address.Email = customer.Email; - // } - - // if (forceToTakeAmazonAddress) - // { - // // First time to get in touch with an amazon address. - // var existingAddress = customer.Addresses.ToList().FindAddress(address, true); - // if (existingAddress == null) - // { - // customer.Addresses.Add(address); - // customer.BillingAddress = address; - // } - // else - // { - // customer.BillingAddress = existingAddress; - // } - // } - // else - // { - // if (customer.BillingAddress == null) - // { - // customer.Addresses.Add(address); - // customer.BillingAddress = address; - // } - - // GetAddress(details, customer.BillingAddress, out countryAllowsShipping, out countryAllowsBilling); - - // // But now we could have dublicates. - // var newAddressId = customer.BillingAddress.Id; - // var addresses = customer.Addresses.Where(x => x.Id != newAddressId).ToList(); - - // var existingAddress = addresses.FindAddress(customer.BillingAddress, false); - // if (existingAddress != null) - // { - // // Remove the new and take the old one. - // customer.RemoveAddress(customer.BillingAddress); - // customer.BillingAddress = existingAddress; - - // try - // { - // _addressService.DeleteAddress(newAddressId); - // } - // catch (Exception exception) - // { - // exception.Dump(); - // } - // } - // } - - // customer.ShippingAddress = (isShippable ? customer.BillingAddress : null); - - // return true; - //} - - private AmazonPayData GetDetails(AuthorizeResponse response) - { - var data = new AmazonPayData(); - data.MessageType = "GetAuthorizationDetails"; - data.MessageId = response.GetRequestId(); - data.AuthorizationId = response.GetAuthorizationId(); - data.ReferenceId = response.GetAuthorizationReferenceId(); - - var ids = response.GetCaptureIdList(); - if (ids.Any()) - { - data.CaptureId = ids.First(); - } - - data.Fee = new AmazonPayPrice(response.GetAuthorizationFee(), response.GetAuthorizationFeeCurrencyCode()); - data.AuthorizedAmount = new AmazonPayPrice(response.GetAuthorizationAmount(), response.GetAuthorizationAmountCurrencyCode()); - data.CapturedAmount = new AmazonPayPrice(response.GetCapturedAmount(), response.GetCapturedAmountCurrencyCode()); - data.CaptureNow = response.GetCaptureNow(); - data.Creation = response.GetCreationTimestamp(); - data.Expiration = response.GetExpirationTimestamp(); - data.ReasonCode = response.GetReasonCode(); - data.ReasonDescription = response.GetReasonDescription(); - data.State = response.GetAuthorizationState(); - data.StateLastUpdate = response.GetLastUpdateTimestamp(); - - return data; - } - private AmazonPayData GetDetails(CaptureResponse response) - { - var data = new AmazonPayData(); - data.MessageType = "GetCaptureDetails"; - data.MessageId = response.GetRequestId(); - data.CaptureId = response.GetCaptureId(); - data.ReferenceId = response.GetCaptureReferenceId(); - data.Fee = new AmazonPayPrice(response.GetCaptureFee(), response.GetCaptureFeeCurrencyCode()); - data.CapturedAmount = new AmazonPayPrice(response.GetCaptureAmount(), response.GetCaptureAmountCurrencyCode()); - data.Creation = response.GetCreationTimestamp(); - data.ReasonCode = response.GetReasonCode(); - data.ReasonDescription = response.GetReasonDescription(); - data.State = response.GetCaptureState(); - data.StateLastUpdate = response.GetLastUpdatedTimestamp(); - - return data; - } - private AmazonPayData GetDetails(RefundResponse response) - { - var data = new AmazonPayData(); - data.MessageType = "GetRefundDetails"; - data.MessageId = response.GetRequestId(); - data.ReferenceId = response.GetRefundReferenceId(); - data.Creation = response.GetCreationTimestamp(); - data.Fee = new AmazonPayPrice(response.GetRefundFee(), response.GetRefundFeeCurrencyCode()); - data.RefundId = response.GetAmazonRefundId(); - data.RefundedAmount = new AmazonPayPrice(response.GetRefundAmount(), response.GetRefundAmountCurrencyCode()); - data.ReasonCode = response.GetReasonCode(); - data.ReasonDescription = response.GetReasonDescription(); - data.State = response.GetRefundState(); - data.StateLastUpdate = response.GetLastUpdateTimestamp(); - - return data; - } - - private string GetRandomId(string prefix) - { - var str = prefix + CommonHelper.GenerateRandomDigitCode(20); - return str.Truncate(32); - } - - private string LogError(DelegateRequest request, IResponse response, bool isWarning = false) - { + /// + /// Helper with utilities to keep the AmazonPayService tidy. + /// + public partial class AmazonPayService + { + /// + /// Also named "spId". + /// + internal static string PlatformId => "A3OJ83WFYM72IY"; + + internal static string LeadCode => "SPEXDEAPA-SmartStore.Net-CP-DP"; + + internal static string GetWidgetUrl(AmazonPaySettings settings) + { + switch (settings.Marketplace.EmptyNull().ToLower()) + { + case "us": + return settings.UseSandbox + ? "https://static-na.payments-amazon.com/OffAmazonPayments/us/sandbox/js/Widgets.js" + : "https://static-na.payments-amazon.com/OffAmazonPayments/us/js/Widgets.js"; + case "uk": + return settings.UseSandbox + ? "https://static-eu.payments-amazon.com/OffAmazonPayments/gbp/sandbox/lpa/js/Widgets.js" + : "https://static-eu.payments-amazon.com/OffAmazonPayments/gbp/lpa/js/Widgets.js"; + case "jp": + return settings.UseSandbox + ? "https://static-fe.payments-amazon.com/OffAmazonPayments/jp/sandbox/lpa/js/Widgets.js" + : "https://static-fe.payments-amazon.com/OffAmazonPayments/jp/lpa/js/Widgets.js"; + default: + return settings.UseSandbox + ? "https://static-eu.payments-amazon.com/OffAmazonPayments/eur/sandbox/lpa/js/Widgets.js" + : "https://static-eu.payments-amazon.com/OffAmazonPayments/eur/lpa/js/Widgets.js"; + } + } + + private string GetPluginUrl(string action, bool useSsl = false) + { + var pluginUrl = "{0}Plugins/SmartStore.AmazonPay/AmazonPay/{1}".FormatInvariant(_services.WebHelper.GetStoreLocation(useSsl), action); + return pluginUrl; + } + + private void SerializeOrderAttribute(AmazonPayOrderAttribute attribute, Order order) + { + if (attribute != null) + { + var sb = new StringBuilder(); + using (var writer = new StringWriter(sb)) + { + var serializer = new XmlSerializer(typeof(AmazonPayOrderAttribute)); + serializer.Serialize(writer, attribute); + + _genericAttributeService.SaveAttribute(order, AmazonPayPlugin.SystemName + ".OrderAttribute", sb.ToString(), order.StoreId); + } + } + } + + private AmazonPayOrderAttribute DeserializeOrderAttribute(Order order) + { + var serialized = order.GetAttribute(AmazonPayPlugin.SystemName + ".OrderAttribute", _genericAttributeService, order.StoreId); + + if (!serialized.HasValue()) + { + var attribute = new AmazonPayOrderAttribute(); + + // legacy < v.1.14 + attribute.OrderReferenceId = order.GetAttribute(AmazonPayPlugin.SystemName + ".OrderReferenceId", order.StoreId); + + return attribute; + } + + using (var reader = new StringReader(serialized)) + { + var serializer = new XmlSerializer(typeof(AmazonPayOrderAttribute)); + return (AmazonPayOrderAttribute)serializer.Deserialize(reader); + } + } + + private void AddOrderNote(AmazonPaySettings settings, Order order, string anyString = null, bool isIpn = false) + { + try + { + if (!settings.AddOrderNotes || order == null || anyString.IsEmpty()) + return; + + var sb = new StringBuilder(); + var faviconUrl = "{0}Plugins/{1}/Content/images/favicon.png".FormatInvariant(_services.WebHelper.GetStoreLocation(), AmazonPayPlugin.SystemName); + + sb.AppendFormat("", faviconUrl); + sb.Append(T("Plugins.Payments.AmazonPay.AmazonDataProcessed")); + sb.Append(":
              "); + sb.Append(anyString); + + if (isIpn) + { + order.HasNewPaymentNotification = true; + } + + order.OrderNotes.Add(new OrderNote + { + Note = sb.ToString(), + DisplayToCustomer = false, + CreatedOnUtc = DateTime.UtcNow + }); + + _orderService.UpdateOrder(order); + } + catch (Exception exception) + { + Logger.Error(exception); + } + } + + private Regions.currencyCode ConvertCurrency(string currencyCode) + { + switch (currencyCode.EmptyNull().ToLower()) + { + case "usd": + return Regions.currencyCode.USD; + case "gbp": + return Regions.currencyCode.GBP; + case "jpy": + return Regions.currencyCode.JPY; + case "aud": + return Regions.currencyCode.AUD; + case "zar": + return Regions.currencyCode.ZAR; + case "chf": + return Regions.currencyCode.CHF; + case "nok": + return Regions.currencyCode.NOK; + case "dkk": + return Regions.currencyCode.DKK; + case "sek": + return Regions.currencyCode.SEK; + case "nzd": + return Regions.currencyCode.NZD; + case "hkd": + return Regions.currencyCode.HKD; + default: + return Regions.currencyCode.EUR; + } + } + + private Address CreateAddress( + string email, + string buyerName, + string addressLine1, + string addressLine2, + string addressLine3, + string city, + string postalCode, + string phone, + string countryCode, + string stateRegion, + string county, + string destrict, + out bool countryAllowsShipping, + out bool countryAllowsBilling) + { + countryAllowsShipping = countryAllowsBilling = true; + + var address = new Address(); + address.CreatedOnUtc = DateTime.UtcNow; + address.Email = email; + address.ToFirstAndLastName(buyerName); + address.Address1 = addressLine1.EmptyNull().Trim().Truncate(4000); + address.Address2 = addressLine2.EmptyNull().Trim().Truncate(4000); + address.Address2 = address.Address2.Grow(addressLine3.EmptyNull().Trim(), ", ").Truncate(4000); + address.City = county.Grow(destrict, " ").Grow(city, " ").EmptyNull().Trim().Truncate(4000); + address.ZipPostalCode = postalCode.EmptyNull().Trim().Truncate(4000); + address.PhoneNumber = phone.EmptyNull().Trim().Truncate(4000); + + if (countryCode.HasValue()) + { + var country = _countryService.GetCountryByTwoOrThreeLetterIsoCode(countryCode); + if (country != null) + { + address.CountryId = country.Id; + countryAllowsShipping = country.AllowsShipping; + countryAllowsBilling = country.AllowsBilling; + } + } + + if (stateRegion.HasValue()) + { + var stateProvince = _stateProvinceService.GetStateProvinceByAbbreviation(stateRegion); + if (stateProvince != null) + { + address.StateProvinceId = stateProvince.Id; + } + } + + // Normalize. + if (address.Address1.IsEmpty() && address.Address2.HasValue()) + { + address.Address1 = address.Address2; + address.Address2 = null; + } + else if (address.Address1.HasValue() && address.Address1 == address.Address2) + { + address.Address2 = null; + } + + if (address.CountryId == 0) + { + address.CountryId = null; + } + + if (address.StateProvinceId == 0) + { + address.StateProvinceId = null; + } + + return address; + } + + //private void GetAddress(OrderReferenceDetailsResponse details, Address address, out bool countryAllowsShipping, out bool countryAllowsBilling) + //{ + // countryAllowsShipping = countryAllowsBilling = true; + + // address.Email = details.GetEmail(); + // address.ToFirstAndLastName(details.GetBuyerName()); + // address.Address1 = details.GetAddressLine1().EmptyNull().Trim().Truncate(4000); + // address.Address2 = details.GetAddressLine2().EmptyNull().Trim().Truncate(4000); + // address.Address2 = address.Address2.Grow(details.GetAddressLine3().EmptyNull().Trim(), ", ").Truncate(4000); + // address.City = details.GetCity().EmptyNull().Trim().Truncate(4000); + // address.ZipPostalCode = details.GetPostalCode().EmptyNull().Trim().Truncate(4000); + // address.PhoneNumber = details.GetPhone().EmptyNull().Trim().Truncate(4000); + + // var countryCode = details.GetCountryCode(); + // if (countryCode.HasValue()) + // { + // var country = _countryService.GetCountryByTwoOrThreeLetterIsoCode(countryCode); + // if (country != null) + // { + // address.CountryId = country.Id; + // countryAllowsShipping = country.AllowsShipping; + // countryAllowsBilling = country.AllowsBilling; + // } + // } + + // var stateRegion = details.GetStateOrRegion(); + // if (stateRegion.HasValue()) + // { + // var stateProvince = _stateProvinceService.GetStateProvinceByAbbreviation(stateRegion); + // if (stateProvince != null) + // { + // address.StateProvinceId = stateProvince.Id; + // } + // } + + // // Normalize. + // if (address.Address1.IsEmpty() && address.Address2.HasValue()) + // { + // address.Address1 = address.Address2; + // address.Address2 = null; + // } + // else if (address.Address1.HasValue() && address.Address1 == address.Address2) + // { + // address.Address2 = null; + // } + + // if (address.CountryId == 0) + // { + // address.CountryId = null; + // } + + // if (address.StateProvinceId == 0) + // { + // address.StateProvinceId = null; + // } + //} + + //private bool FindAndApplyAddress(OrderReferenceDetailsResponse details, Customer customer, bool isShippable, bool forceToTakeAmazonAddress) + //{ + // // PlaceOrder requires billing address but we don't get one from Amazon here. so use shipping address instead until we get it from amazon. + // var countryAllowsShipping = true; + // var countryAllowsBilling = true; + + // var address = new Address(); + // address.CreatedOnUtc = DateTime.UtcNow; + + // GetAddress(details, address, out countryAllowsShipping, out countryAllowsBilling); + + // if (isShippable && !countryAllowsShipping) + // return false; + + // if (address.Email.IsEmpty()) + // { + // address.Email = customer.Email; + // } + + // if (forceToTakeAmazonAddress) + // { + // // First time to get in touch with an amazon address. + // var existingAddress = customer.Addresses.ToList().FindAddress(address, true); + // if (existingAddress == null) + // { + // customer.Addresses.Add(address); + // customer.BillingAddress = address; + // } + // else + // { + // customer.BillingAddress = existingAddress; + // } + // } + // else + // { + // if (customer.BillingAddress == null) + // { + // customer.Addresses.Add(address); + // customer.BillingAddress = address; + // } + + // GetAddress(details, customer.BillingAddress, out countryAllowsShipping, out countryAllowsBilling); + + // // But now we could have dublicates. + // var newAddressId = customer.BillingAddress.Id; + // var addresses = customer.Addresses.Where(x => x.Id != newAddressId).ToList(); + + // var existingAddress = addresses.FindAddress(customer.BillingAddress, false); + // if (existingAddress != null) + // { + // // Remove the new and take the old one. + // customer.RemoveAddress(customer.BillingAddress); + // customer.BillingAddress = existingAddress; + + // try + // { + // _addressService.DeleteAddress(newAddressId); + // } + // catch (Exception exception) + // { + // exception.Dump(); + // } + // } + // } + + // customer.ShippingAddress = (isShippable ? customer.BillingAddress : null); + + // return true; + //} + + private AmazonPayData GetDetails(AuthorizeResponse response) + { + var data = new AmazonPayData(); + data.MessageType = "GetAuthorizationDetails"; + data.MessageId = response.GetRequestId(); + data.AuthorizationId = response.GetAuthorizationId(); + data.ReferenceId = response.GetAuthorizationReferenceId(); + + var ids = response.GetCaptureIdList(); + if (ids.Any()) + { + data.CaptureId = ids.First(); + } + + data.Fee = new AmazonPayPrice(response.GetAuthorizationFee(), response.GetAuthorizationFeeCurrencyCode()); + data.AuthorizedAmount = new AmazonPayPrice(response.GetAuthorizationAmount(), response.GetAuthorizationAmountCurrencyCode()); + data.CapturedAmount = new AmazonPayPrice(response.GetCapturedAmount(), response.GetCapturedAmountCurrencyCode()); + data.CaptureNow = response.GetCaptureNow(); + data.Creation = response.GetCreationTimestamp(); + data.Expiration = response.GetExpirationTimestamp(); + data.ReasonCode = response.GetReasonCode(); + data.ReasonDescription = response.GetReasonDescription(); + data.State = response.GetAuthorizationState(); + data.StateLastUpdate = response.GetLastUpdateTimestamp(); + + return data; + } + private AmazonPayData GetDetails(CaptureResponse response) + { + var data = new AmazonPayData(); + data.MessageType = "GetCaptureDetails"; + data.MessageId = response.GetRequestId(); + data.CaptureId = response.GetCaptureId(); + data.ReferenceId = response.GetCaptureReferenceId(); + data.Fee = new AmazonPayPrice(response.GetCaptureFee(), response.GetCaptureFeeCurrencyCode()); + data.CapturedAmount = new AmazonPayPrice(response.GetCaptureAmount(), response.GetCaptureAmountCurrencyCode()); + data.Creation = response.GetCreationTimestamp(); + data.ReasonCode = response.GetReasonCode(); + data.ReasonDescription = response.GetReasonDescription(); + data.State = response.GetCaptureState(); + data.StateLastUpdate = response.GetLastUpdatedTimestamp(); + + return data; + } + private AmazonPayData GetDetails(RefundResponse response) + { + var data = new AmazonPayData(); + data.MessageType = "GetRefundDetails"; + data.MessageId = response.GetRequestId(); + data.ReferenceId = response.GetRefundReferenceId(); + data.Creation = response.GetCreationTimestamp(); + data.Fee = new AmazonPayPrice(response.GetRefundFee(), response.GetRefundFeeCurrencyCode()); + data.RefundId = response.GetAmazonRefundId(); + data.RefundedAmount = new AmazonPayPrice(response.GetRefundAmount(), response.GetRefundAmountCurrencyCode()); + data.ReasonCode = response.GetReasonCode(); + data.ReasonDescription = response.GetReasonDescription(); + data.State = response.GetRefundState(); + data.StateLastUpdate = response.GetLastUpdateTimestamp(); + + return data; + } + + private string GetRandomId(string prefix) + { + var str = prefix + CommonHelper.GenerateRandomDigitCode(20); + return str.Truncate(32); + } + + private string LogError(DelegateRequest request, IResponse response, bool isWarning = false) + { var message = string.Empty; if (response != null) @@ -454,137 +442,137 @@ private string LogError(DelegateRequest request, IResponse response, bool Logger.Log(isWarning ? LogLevel.Warning : LogLevel.Error, new Exception(response.GetJson()), message, null); } - return message; - } - - private string ToInfoString(AmazonPayData data) - { - var sb = new StringBuilder(); - - try - { - var strings = _services.Localization.GetResource("Plugins.Payments.AmazonPay.MessageStrings").SplitSafe(";"); - var state = data.State.Grow(data.ReasonCode, " "); - - if (data.ReasonDescription.HasValue()) - state = $"{state} ({data.ReasonDescription})"; - - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.MessageTyp)}: {data.MessageType.NaIfEmpty()}"); - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.State)}: {state}"); - - var stateDate = _dateTimeHelper.ConvertToUserTime(data.StateLastUpdate, DateTimeKind.Utc); - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.StateUpdate)}: {stateDate.ToString()}"); - - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.MessageId)}: {data.MessageId.NaIfEmpty()}"); - - if (data.AuthorizationId.HasValue()) - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.AuthorizationID)}: {data.AuthorizationId}"); - - if (data.CaptureId.HasValue()) - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.CaptureID)}: {data.CaptureId}"); - - if (data.RefundId.HasValue()) - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.RefundID)}: {data.RefundId}"); - - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.ReferenceID)}: {data.ReferenceId.NaIfEmpty()}"); - - if (data.Fee != null && data.Fee.Amount != decimal.Zero) - { - var signed = data.MessageType.IsCaseInsensitiveEqual("RefundNotification") || data.MessageType.IsCaseInsensitiveEqual("GetRefundDetails") ? "-" : ""; - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.Fee)}: {signed}"); - } - - if (data.AuthorizedAmount != null && data.AuthorizedAmount.Amount != decimal.Zero) - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.AuthorizedAmount)}: {data.AuthorizedAmount.ToString()}"); - - if (data.CapturedAmount != null && data.CapturedAmount.Amount != decimal.Zero) - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.CapturedAmount)}: {data.CapturedAmount.ToString()}"); - - if (data.RefundedAmount != null && data.RefundedAmount.Amount != decimal.Zero) - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.RefundedAmount)}: {data.RefundedAmount.ToString()}"); - - if (data.CaptureNow.HasValue) - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.CaptureNow)}: {data.CaptureNow.Value.ToString()}"); - - var creationDate = _dateTimeHelper.ConvertToUserTime(data.Creation, DateTimeKind.Utc); - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.Creation)}: {creationDate.ToString()}"); - - if (data.Expiration.HasValue) - { - var expirationDate = _dateTimeHelper.ConvertToUserTime(data.Expiration.Value, DateTimeKind.Utc); - sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.Expiration)}: {expirationDate.ToString()}"); - } - } - catch (Exception exception) - { - exception.Dump(); - } - - return sb.ToString(); - } - - /// - /// Creates an API client. - /// - /// AmazonPay settings - /// Currency code of primary store currency - /// AmazonPay client - private Client CreateClient(AmazonPaySettings settings) - { - var descriptor = _pluginFinder.GetPluginDescriptorBySystemName(AmazonPayPlugin.SystemName); - var appVersion = descriptor != null ? descriptor.Version.ToString() : "1.0"; - - Regions.supportedRegions region; - switch (settings.Marketplace.EmptyNull().ToLower()) - { - case "us": - region = Regions.supportedRegions.us; - break; - case "uk": - region = Regions.supportedRegions.uk; - break; - case "jp": - region = Regions.supportedRegions.jp; - break; - default: - region = Regions.supportedRegions.de; - break; - } - - var config = new Configuration() - .WithAccessKey(settings.AccessKey) - .WithClientId(settings.ClientId) - .WithSecretKey(settings.SecretKey) - .WithSandbox(settings.UseSandbox) - .WithApplicationName("SmartStore.Net " + AmazonPayPlugin.SystemName) - .WithApplicationVersion(appVersion) - .WithRegion(region); - - var client = new Client(config); - return client; - } - - private AuthorizeResponse AuthorizePayment( - AmazonPaySettings settings, - AmazonPayCheckoutState state, - Store store, - ProcessPaymentRequest request, - Client client, - bool synchronously, + return message; + } + + private string ToInfoString(AmazonPayData data) + { + var sb = new StringBuilder(); + + try + { + var strings = _services.Localization.GetResource("Plugins.Payments.AmazonPay.MessageStrings").SplitSafe(";"); + var state = data.State.Grow(data.ReasonCode, " "); + + if (data.ReasonDescription.HasValue()) + state = $"{state} ({data.ReasonDescription})"; + + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.MessageTyp)}: {data.MessageType.NaIfEmpty()}"); + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.State)}: {state}"); + + var stateDate = _dateTimeHelper.ConvertToUserTime(data.StateLastUpdate, DateTimeKind.Utc); + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.StateUpdate)}: {stateDate.ToString()}"); + + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.MessageId)}: {data.MessageId.NaIfEmpty()}"); + + if (data.AuthorizationId.HasValue()) + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.AuthorizationID)}: {data.AuthorizationId}"); + + if (data.CaptureId.HasValue()) + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.CaptureID)}: {data.CaptureId}"); + + if (data.RefundId.HasValue()) + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.RefundID)}: {data.RefundId}"); + + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.ReferenceID)}: {data.ReferenceId.NaIfEmpty()}"); + + if (data.Fee != null && data.Fee.Amount != decimal.Zero) + { + var signed = data.MessageType.IsCaseInsensitiveEqual("RefundNotification") || data.MessageType.IsCaseInsensitiveEqual("GetRefundDetails") ? "-" : ""; + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.Fee)}: {signed}"); + } + + if (data.AuthorizedAmount != null && data.AuthorizedAmount.Amount != decimal.Zero) + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.AuthorizedAmount)}: {data.AuthorizedAmount.ToString()}"); + + if (data.CapturedAmount != null && data.CapturedAmount.Amount != decimal.Zero) + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.CapturedAmount)}: {data.CapturedAmount.ToString()}"); + + if (data.RefundedAmount != null && data.RefundedAmount.Amount != decimal.Zero) + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.RefundedAmount)}: {data.RefundedAmount.ToString()}"); + + if (data.CaptureNow.HasValue) + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.CaptureNow)}: {data.CaptureNow.Value.ToString()}"); + + var creationDate = _dateTimeHelper.ConvertToUserTime(data.Creation, DateTimeKind.Utc); + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.Creation)}: {creationDate.ToString()}"); + + if (data.Expiration.HasValue) + { + var expirationDate = _dateTimeHelper.ConvertToUserTime(data.Expiration.Value, DateTimeKind.Utc); + sb.AppendLine($"{strings.SafeGet((int)AmazonPayMessage.Expiration)}: {expirationDate.ToString()}"); + } + } + catch (Exception exception) + { + exception.Dump(); + } + + return sb.ToString(); + } + + /// + /// Creates an API client. + /// + /// AmazonPay settings + /// Currency code of primary store currency + /// AmazonPay client + private Client CreateClient(AmazonPaySettings settings) + { + var descriptor = _pluginFinder.GetPluginDescriptorBySystemName(AmazonPayPlugin.SystemName); + var appVersion = descriptor != null ? descriptor.Version.ToString() : "1.0"; + + Regions.supportedRegions region; + switch (settings.Marketplace.EmptyNull().ToLower()) + { + case "us": + region = Regions.supportedRegions.us; + break; + case "uk": + region = Regions.supportedRegions.uk; + break; + case "jp": + region = Regions.supportedRegions.jp; + break; + default: + region = Regions.supportedRegions.de; + break; + } + + var config = new Configuration() + .WithAccessKey(settings.AccessKey) + .WithClientId(settings.ClientId) + .WithSecretKey(settings.SecretKey) + .WithSandbox(settings.UseSandbox) + .WithApplicationName("Smartstore " + AmazonPayPlugin.SystemName) + .WithApplicationVersion(appVersion) + .WithRegion(region); + + var client = new Client(config); + return client; + } + + private AuthorizeResponse AuthorizePayment( + AmazonPaySettings settings, + AmazonPayCheckoutState state, + Store store, + ProcessPaymentRequest request, + Client client, + bool synchronously, out AuthorizeRequest authRequest) - { - authRequest = new AuthorizeRequest() - .WithMerchantId(settings.SellerId) - .WithAmazonOrderReferenceId(state.OrderReferenceId) - .WithAuthorizationReferenceId(GetRandomId("Authorize")) - .WithCaptureNow(settings.TransactionType == AmazonPayTransactionType.AuthorizeAndCapture) - .WithCurrencyCode(ConvertCurrency(store.PrimaryStoreCurrency.CurrencyCode)) - .WithAmount(request.OrderTotal); - - if (synchronously) - { - authRequest = authRequest.WithTransactionTimeout(0); - } + { + authRequest = new AuthorizeRequest() + .WithMerchantId(settings.SellerId) + .WithAmazonOrderReferenceId(state.OrderReferenceId) + .WithAuthorizationReferenceId(GetRandomId("Authorize")) + .WithCaptureNow(settings.TransactionType == AmazonPayTransactionType.AuthorizeAndCapture) + .WithCurrencyCode(ConvertCurrency(store.PrimaryStoreCurrency.CurrencyCode)) + .WithAmount(request.OrderTotal); + + if (synchronously) + { + authRequest = authRequest.WithTransactionTimeout(0); + } // See https://pay.amazon.com/de/developer/documentation/lpwa/201956480 //{"SandboxSimulation": {"State":"Declined", "ReasonCode":"InvalidPaymentMethod", "PaymentMethodUpdateTimeInMins":5}} @@ -596,7 +584,7 @@ private AuthorizeResponse AuthorizePayment( //} var authResponse = client.Authorize(authRequest); - return authResponse; - } - } + return authResponse; + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayUtilities.cs b/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayUtilities.cs index 8836575653..a2ade0617a 100644 --- a/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayUtilities.cs +++ b/src/Plugins/SmartStore.AmazonPay/Services/AmazonPayUtilities.cs @@ -5,11 +5,11 @@ namespace SmartStore.AmazonPay.Services { - [Serializable] - public class AmazonPayCheckoutState - { - public string OrderReferenceId { get; set; } - public string AccessToken { get; set; } + [Serializable] + public class AmazonPayCheckoutState + { + public string OrderReferenceId { get; set; } + public string AccessToken { get; set; } // Confirmation flow. public bool DetailsSet { get; set; } @@ -19,31 +19,31 @@ public class AmazonPayCheckoutState } - public class AmazonPayActionState - { - public Guid OrderGuid { get; set; } - public List Errors { get; set; } - } + public class AmazonPayActionState + { + public Guid OrderGuid { get; set; } + public List Errors { get; set; } + } - [Serializable] - public class AmazonPayOrderAttribute - { - public string OrderReferenceId { get; set; } - public bool OrderReferenceClosed { get; set; } - } + [Serializable] + public class AmazonPayOrderAttribute + { + public string OrderReferenceId { get; set; } + public bool OrderReferenceClosed { get; set; } + } - internal class PollingLoopData - { - public PollingLoopData(int orderId) - { - OrderId = orderId; - } + internal class PollingLoopData + { + public PollingLoopData(int orderId) + { + OrderId = orderId; + } - public int OrderId { get; private set; } - public Order Order { get; set; } - public AmazonPaySettings Settings { get; set; } - public Client Client { get; set; } - } + public int OrderId { get; private set; } + public Order Order { get; set; } + public AmazonPaySettings Settings { get; set; } + public Client Client { get; set; } + } } diff --git a/src/Plugins/SmartStore.AmazonPay/Services/IAmazonPayService.cs b/src/Plugins/SmartStore.AmazonPay/Services/IAmazonPayService.cs index f4adc780e2..05e0589571 100644 --- a/src/Plugins/SmartStore.AmazonPay/Services/IAmazonPayService.cs +++ b/src/Plugins/SmartStore.AmazonPay/Services/IAmazonPayService.cs @@ -9,16 +9,16 @@ namespace SmartStore.AmazonPay.Services { public partial interface IAmazonPayService : IExternalProviderAuthorizer - { - void SetupConfiguration(ConfigurationModel model); + { + void SetupConfiguration(ConfigurationModel model); - AmazonPayViewModel CreateViewModel(AmazonPayRequestType type, TempDataDictionary tempData); + AmazonPayViewModel CreateViewModel(AmazonPayRequestType type, TempDataDictionary tempData); - void CloseOrderReference(AmazonPaySettings settings, Order order); + void CloseOrderReference(AmazonPaySettings settings, Order order); - void AddCustomerOrderNoteLoop(AmazonPayActionState state); + void AddCustomerOrderNoteLoop(AmazonPayActionState state); - void GetBillingAddress(); + void GetBillingAddress(); bool SetOrderReferenceDetails( Client client, @@ -29,20 +29,20 @@ bool SetOrderReferenceDetails( bool ConfirmOrderReference(); - ProcessPaymentResult ProcessPayment(ProcessPaymentRequest request); + ProcessPaymentResult ProcessPayment(ProcessPaymentRequest request); - void PostProcessPayment(PostProcessPaymentRequest request); + void PostProcessPayment(PostProcessPaymentRequest request); - CapturePaymentResult Capture(CapturePaymentRequest request); + CapturePaymentResult Capture(CapturePaymentRequest request); - RefundPaymentResult Refund(RefundPaymentRequest request); + RefundPaymentResult Refund(RefundPaymentRequest request); - VoidPaymentResult Void(VoidPaymentRequest request); + VoidPaymentResult Void(VoidPaymentRequest request); - void ProcessIpn(HttpRequestBase request); + void ProcessIpn(HttpRequestBase request); - void StartDataPolling(); + void StartDataPolling(); - void ShareKeys(string payload, int storeId); - } + void ShareKeys(string payload, int storeId); + } } diff --git a/src/Plugins/SmartStore.AmazonPay/Services/Internal/AmazonPayData.cs b/src/Plugins/SmartStore.AmazonPay/Services/Internal/AmazonPayData.cs index 8df3aa6d48..5a4b3a4f25 100644 --- a/src/Plugins/SmartStore.AmazonPay/Services/Internal/AmazonPayData.cs +++ b/src/Plugins/SmartStore.AmazonPay/Services/Internal/AmazonPayData.cs @@ -2,39 +2,39 @@ namespace SmartStore.AmazonPay.Services.Internal { - internal class AmazonPayData - { - public string MessageType { get; set; } - public string MessageId { get; set; } - public string AuthorizationId { get; set; } - public string CaptureId { get; set; } - public string RefundId { get; set; } - public string ReferenceId { get; set; } + internal class AmazonPayData + { + public string MessageType { get; set; } + public string MessageId { get; set; } + public string AuthorizationId { get; set; } + public string CaptureId { get; set; } + public string RefundId { get; set; } + public string ReferenceId { get; set; } - public string ReasonCode { get; set; } - public string ReasonDescription { get; set; } - public string State { get; set; } - public DateTime StateLastUpdate { get; set; } + public string ReasonCode { get; set; } + public string ReasonDescription { get; set; } + public string State { get; set; } + public DateTime StateLastUpdate { get; set; } - public AmazonPayPrice Fee { get; set; } - public AmazonPayPrice AuthorizedAmount { get; set; } - public AmazonPayPrice CapturedAmount { get; set; } - public AmazonPayPrice RefundedAmount { get; set; } + public AmazonPayPrice Fee { get; set; } + public AmazonPayPrice AuthorizedAmount { get; set; } + public AmazonPayPrice CapturedAmount { get; set; } + public AmazonPayPrice RefundedAmount { get; set; } - public bool? CaptureNow { get; set; } - public DateTime Creation { get; set; } - public DateTime? Expiration { get; set; } + public bool? CaptureNow { get; set; } + public DateTime Creation { get; set; } + public DateTime? Expiration { get; set; } - public string AnyAmazonId - { - get - { - if (CaptureId.HasValue()) - return CaptureId; - if (AuthorizationId.HasValue()) - return AuthorizationId; - return RefundId; - } - } - } + public string AnyAmazonId + { + get + { + if (CaptureId.HasValue()) + return CaptureId; + if (AuthorizationId.HasValue()) + return AuthorizationId; + return RefundId; + } + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/Services/Internal/AmazonPayExtensions.cs b/src/Plugins/SmartStore.AmazonPay/Services/Internal/AmazonPayExtensions.cs index 1eb4378dfa..0956b119b8 100644 --- a/src/Plugins/SmartStore.AmazonPay/Services/Internal/AmazonPayExtensions.cs +++ b/src/Plugins/SmartStore.AmazonPay/Services/Internal/AmazonPayExtensions.cs @@ -10,132 +10,132 @@ namespace SmartStore.AmazonPay { - internal static class AmazonPayExtensions - { - internal static void ToFirstAndLastName(this string name, out string firstName, out string lastName) - { - if (!string.IsNullOrWhiteSpace(name)) - { - int index = name.LastIndexOf(' '); - if (index == -1) - { - firstName = ""; - lastName = name; - } - else - { - firstName = name.Substring(0, index); - lastName = name.Substring(index + 1); - } - - firstName = firstName.EmptyNull().Truncate(4000); - lastName = lastName.EmptyNull().Truncate(4000); - } - else - { - firstName = lastName = ""; - } - } - - internal static void ToFirstAndLastName(this Address address, string name) - { - string firstName, lastName; - name.ToFirstAndLastName(out firstName, out lastName); - - address.FirstName = firstName; - address.LastName = lastName; - } - - internal static Address FindAddress(this List
              addresses, Address address, bool uncompleteToo) - { - var match = addresses.FindAddress(address.FirstName, address.LastName, - address.PhoneNumber, address.Email, address.FaxNumber, address.Company, - address.Address1, address.Address2, - address.City, address.StateProvinceId, address.ZipPostalCode, address.CountryId); - - if (match == null && uncompleteToo) - { - // Compare with ToAddress - match = addresses.FirstOrDefault(x => - x.FirstName == null && x.LastName == null && - x.Address1 == null && x.Address2 == null && - x.City == address.City && x.ZipPostalCode == address.ZipPostalCode && - x.PhoneNumber == null && - x.CountryId == address.CountryId && x.StateProvinceId == address.StateProvinceId - ); - } - - return match; - } - - internal static string ToAmazonLanguageCode(this string twoLetterLanguageCode, char delimiter = '-') - { - switch (twoLetterLanguageCode.EmptyNull().ToLower()) - { - case "en": - return $"en{delimiter}GB"; - case "fr": - return $"fr{delimiter}FR"; - case "it": - return $"it{delimiter}IT"; - case "es": - return $"es{delimiter}ES"; - case "de": - default: - return $"de{delimiter}DE"; - } - } - - internal static bool HasAmazonPayState(this HttpContextBase httpContext) - { - var checkoutState = httpContext.GetCheckoutState(); - var checkoutStateKey = AmazonPayPlugin.SystemName + ".CheckoutState"; - - if (checkoutState != null && checkoutState.CustomProperties.ContainsKey(checkoutStateKey)) - { - var state = checkoutState.CustomProperties[checkoutStateKey] as AmazonPayCheckoutState; - - return state != null && state.AccessToken.HasValue(); - } - - return false; - } - - internal static AmazonPayCheckoutState GetAmazonPayState(this HttpContextBase httpContext, ILocalizationService localizationService) - { - var checkoutState = httpContext.GetCheckoutState(); - - if (checkoutState == null) - throw new SmartException(localizationService.GetResource("Plugins.Payments.AmazonPay.MissingCheckoutSessionState")); - - var state = checkoutState.CustomProperties.Get(AmazonPayPlugin.SystemName + ".CheckoutState") as AmazonPayCheckoutState; - - if (state == null) - throw new SmartException(localizationService.GetResource("Plugins.Payments.AmazonPay.MissingCheckoutSessionState")); - - return state; - } + internal static class AmazonPayExtensions + { + internal static void ToFirstAndLastName(this string name, out string firstName, out string lastName) + { + if (!string.IsNullOrWhiteSpace(name)) + { + int index = name.LastIndexOf(' '); + if (index == -1) + { + firstName = ""; + lastName = name; + } + else + { + firstName = name.Substring(0, index); + lastName = name.Substring(index + 1); + } + + firstName = firstName.EmptyNull().Truncate(4000); + lastName = lastName.EmptyNull().Truncate(4000); + } + else + { + firstName = lastName = ""; + } + } + + internal static void ToFirstAndLastName(this Address address, string name) + { + string firstName, lastName; + name.ToFirstAndLastName(out firstName, out lastName); + + address.FirstName = firstName; + address.LastName = lastName; + } + + internal static Address FindAddress(this List
              addresses, Address address, bool uncompleteToo) + { + var match = addresses.FindAddress(address.FirstName, address.LastName, + address.PhoneNumber, address.Email, address.FaxNumber, address.Company, + address.Address1, address.Address2, + address.City, address.StateProvinceId, address.ZipPostalCode, address.CountryId); + + if (match == null && uncompleteToo) + { + // Compare with ToAddress + match = addresses.FirstOrDefault(x => + x.FirstName == null && x.LastName == null && + x.Address1 == null && x.Address2 == null && + x.City == address.City && x.ZipPostalCode == address.ZipPostalCode && + x.PhoneNumber == null && + x.CountryId == address.CountryId && x.StateProvinceId == address.StateProvinceId + ); + } + + return match; + } + + internal static string ToAmazonLanguageCode(this string twoLetterLanguageCode, char delimiter = '-') + { + switch (twoLetterLanguageCode.EmptyNull().ToLower()) + { + case "en": + return $"en{delimiter}GB"; + case "fr": + return $"fr{delimiter}FR"; + case "it": + return $"it{delimiter}IT"; + case "es": + return $"es{delimiter}ES"; + case "de": + default: + return $"de{delimiter}DE"; + } + } + + internal static bool HasAmazonPayState(this HttpContextBase httpContext) + { + var checkoutState = httpContext.GetCheckoutState(); + var checkoutStateKey = AmazonPayPlugin.SystemName + ".CheckoutState"; + + if (checkoutState != null && checkoutState.CustomProperties.ContainsKey(checkoutStateKey)) + { + var state = checkoutState.CustomProperties[checkoutStateKey] as AmazonPayCheckoutState; + + return state != null && state.AccessToken.HasValue(); + } + + return false; + } + + internal static AmazonPayCheckoutState GetAmazonPayState(this HttpContextBase httpContext, ILocalizationService localizationService) + { + var checkoutState = httpContext.GetCheckoutState(); + + if (checkoutState == null) + throw new SmartException(localizationService.GetResource("Plugins.Payments.AmazonPay.MissingCheckoutSessionState")); + + var state = checkoutState.CustomProperties.Get(AmazonPayPlugin.SystemName + ".CheckoutState") as AmazonPayCheckoutState; + + if (state == null) + throw new SmartException(localizationService.GetResource("Plugins.Payments.AmazonPay.MissingCheckoutSessionState")); + + return state; + } internal static Order GetOrderByAmazonId(this IRepository orderRepository, string amazonId) - { - // S02-9777218-8608106 OrderReferenceId - // S02-9777218-8608106-A088344 Auth ID - // S02-9777218-8608106-C088344 Capture ID - - if (amazonId.HasValue()) - { - string amazonOrderReferenceId = amazonId.Substring(0, amazonId.LastIndexOf('-')); - if (amazonOrderReferenceId.HasValue()) - { - var orders = orderRepository.Table - .Where(x => x.PaymentMethodSystemName == AmazonPayPlugin.SystemName && x.AuthorizationTransactionId.StartsWith(amazonOrderReferenceId)) - .ToList(); - - if (orders.Count() == 1) - return orders.FirstOrDefault(); - } - } - return null; - } - } + { + // S02-9777218-8608106 OrderReferenceId + // S02-9777218-8608106-A088344 Auth ID + // S02-9777218-8608106-C088344 Capture ID + + if (amazonId.HasValue()) + { + string amazonOrderReferenceId = amazonId.Substring(0, amazonId.LastIndexOf('-')); + if (amazonOrderReferenceId.HasValue()) + { + var orders = orderRepository.Table + .Where(x => x.PaymentMethodSystemName == AmazonPayPlugin.SystemName && x.AuthorizationTransactionId.StartsWith(amazonOrderReferenceId)) + .ToList(); + + if (orders.Count() == 1) + return orders.FirstOrDefault(); + } + } + return null; + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/Services/Internal/AmazonPayPrice.cs b/src/Plugins/SmartStore.AmazonPay/Services/Internal/AmazonPayPrice.cs index cfa3fef830..2648d382c9 100644 --- a/src/Plugins/SmartStore.AmazonPay/Services/Internal/AmazonPayPrice.cs +++ b/src/Plugins/SmartStore.AmazonPay/Services/Internal/AmazonPayPrice.cs @@ -2,21 +2,21 @@ namespace SmartStore.AmazonPay.Services.Internal { - internal class AmazonPayPrice - { - public AmazonPayPrice(decimal amount, string currencyCode) - { - Amount = amount; - CurrencyCode = currencyCode; - } + internal class AmazonPayPrice + { + public AmazonPayPrice(decimal amount, string currencyCode) + { + Amount = amount; + CurrencyCode = currencyCode; + } - public decimal Amount { get; private set; } - public string CurrencyCode { get; private set; } + public decimal Amount { get; private set; } + public string CurrencyCode { get; private set; } - public override string ToString() - { - var str = Amount.ToString("0.00", CultureInfo.InvariantCulture); - return str.Grow(CurrencyCode, " "); - } - } + public override string ToString() + { + var str = Amount.ToString("0.00", CultureInfo.InvariantCulture); + return str.Grow(CurrencyCode, " "); + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj b/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj index b2db65457e..90e38591eb 100644 --- a/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj +++ b/src/Plugins/SmartStore.AmazonPay/SmartStore.AmazonPay.csproj @@ -1,6 +1,5 @@  - @@ -19,7 +18,7 @@ Properties SmartStore.AmazonPay SmartStore.AmazonPay - v4.6.1 + v4.7.2 512 @@ -82,37 +81,50 @@ ..\..\packages\AmazonPay.3.5.0\lib\net20\AmazonPay.dll True - - ..\..\packages\Autofac.4.9.0\lib\net45\Autofac.dll + + ..\..\packages\Autofac.5.2.0\lib\net461\Autofac.dll False - - ..\..\packages\Autofac.Mvc5.4.0.2\lib\net45\Autofac.Integration.Mvc.dll + + ..\..\packages\Autofac.Mvc5.5.0.0\lib\net461\Autofac.Integration.Mvc.dll False ..\..\packages\Common.Logging.2.0.0\lib\2.0\Common.Logging.dll True + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + False + ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll False - - ..\..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll - False + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + - + ..\..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.Helpers.dll False @@ -289,12 +301,6 @@ - - - Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". - - - - + @@ -61,7 +62,7 @@ - + @@ -77,19 +78,15 @@ - + - - - - - + @@ -125,16 +122,12 @@ - + - - - - @@ -159,6 +152,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Plugins/SmartStore.Clickatell/AdminMenu.cs b/src/Plugins/SmartStore.Clickatell/AdminMenu.cs index f336662023..0734edd100 100644 --- a/src/Plugins/SmartStore.Clickatell/AdminMenu.cs +++ b/src/Plugins/SmartStore.Clickatell/AdminMenu.cs @@ -3,27 +3,21 @@ namespace SmartStore.Clickatell { - public class AdminMenu : AdminMenuProvider - { - 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") - .Action("ConfigurePlugin", "Plugin", new { systemName = "SmartStore.Clickatell", area = "Admin" }) - .ToItem(); + public class AdminMenu : AdminMenuProvider + { + 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") + .Action("ConfigurePlugin", "Plugin", new { systemName = "SmartStore.Clickatell", area = "Admin" }) + .ToItem(); - pluginsNode.Prepend(menuItem); - } + pluginsNode.Prepend(menuItem); + } - public override int Ordinal - { - get - { - return 100; - } - } + public override int Ordinal => 100; - } + } } diff --git a/src/Plugins/SmartStore.Clickatell/ClickatellSmsProvider.cs b/src/Plugins/SmartStore.Clickatell/ClickatellSmsProvider.cs index cd183ffb6b..2c6a23810d 100644 --- a/src/Plugins/SmartStore.Clickatell/ClickatellSmsProvider.cs +++ b/src/Plugins/SmartStore.Clickatell/ClickatellSmsProvider.cs @@ -12,35 +12,29 @@ namespace SmartStore.Clickatell { - public class ClickatellSmsProvider : BasePlugin, IConfigurable + public class ClickatellSmsProvider : BasePlugin, IConfigurable { private readonly ClickatellSettings _clickatellSettings; private readonly ILocalizationService _localizationService; - private readonly ILogger _logger; + private readonly ILogger _logger; - public ClickatellSmsProvider( - ClickatellSettings clickatellSettings, + public ClickatellSmsProvider( + ClickatellSettings clickatellSettings, ILocalizationService localizationService, - ILogger logger) + ILogger logger) { _clickatellSettings = clickatellSettings; _localizationService = localizationService; - _logger = logger; - } - - public static string SystemName - { - get - { - return "SmartStore.Clickatell"; - } - } - - public void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) + _logger = logger; + } + + public static string SystemName => "SmartStore.Clickatell"; + + public void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) { actionName = "Configure"; controllerName = "SmsClickatell"; - routeValues = new RouteValueDictionary { { "area", SystemName } }; + routeValues = new RouteValueDictionary { { "area", SystemName } }; } public override void Install() @@ -57,77 +51,77 @@ public override void Uninstall() base.Uninstall(); } - public void SendSms(string text) - { - if (text.IsEmpty()) - return; - - string error = null; - HttpWebResponse webResponse = null; - - try - { - // https://www.clickatell.com/developers/api-documentation/rest-api-request-parameters/ - var request = (HttpWebRequest)WebRequest.Create("https://platform.clickatell.com/messages"); - request.Method = "POST"; - request.Accept = "application/json"; - request.ContentType = "application/json"; - request.Headers["Authorization"] = _clickatellSettings.ApiId; - - var data = new Dictionary - { - { "content", text }, - { "to", _clickatellSettings.PhoneNumber.SplitSafe(";") } - }; - - var json = JsonConvert.SerializeObject(data); - - // UTF8 is default encoding - var bytes = Encoding.UTF8.GetBytes(json); - request.ContentLength = bytes.Length; - - using (var stream = request.GetRequestStream()) - { - stream.Write(bytes, 0, bytes.Length); - } - - webResponse = request.GetResponse() as HttpWebResponse; - } - catch (WebException wexc) - { - webResponse = wexc.Response as HttpWebResponse; - } - catch (Exception exception) - { - error = exception.ToString(); - } - - if (webResponse != null) - { - using (var reader = new StreamReader(webResponse.GetResponseStream(), Encoding.UTF8)) - { - var rawResponse = reader.ReadToEnd(); - - if (webResponse.StatusCode == HttpStatusCode.OK || webResponse.StatusCode == HttpStatusCode.Accepted) - { - dynamic response = JObject.Parse(rawResponse); - - error = (string)response.error; - if (error.IsEmpty() && response.messages != null) - error = response.messages[0].error; - } - else - { - error = rawResponse; - } - } - } - - if (error.HasValue()) - { - _logger.Error(error); - throw new SmartException(error); - } - } - } + public void SendSms(string text) + { + if (text.IsEmpty()) + return; + + string error = null; + HttpWebResponse webResponse = null; + + try + { + // https://www.clickatell.com/developers/api-documentation/rest-api-request-parameters/ + var request = (HttpWebRequest)WebRequest.Create("https://platform.clickatell.com/messages"); + request.Method = "POST"; + request.Accept = "application/json"; + request.ContentType = "application/json"; + request.Headers["Authorization"] = _clickatellSettings.ApiId; + + var data = new Dictionary + { + { "content", text }, + { "to", _clickatellSettings.PhoneNumber.SplitSafe(";") } + }; + + var json = JsonConvert.SerializeObject(data); + + // UTF8 is default encoding + var bytes = Encoding.UTF8.GetBytes(json); + request.ContentLength = bytes.Length; + + using (var stream = request.GetRequestStream()) + { + stream.Write(bytes, 0, bytes.Length); + } + + webResponse = request.GetResponse() as HttpWebResponse; + } + catch (WebException wexc) + { + webResponse = wexc.Response as HttpWebResponse; + } + catch (Exception exception) + { + error = exception.ToString(); + } + + if (webResponse != null) + { + using (var reader = new StreamReader(webResponse.GetResponseStream(), Encoding.UTF8)) + { + var rawResponse = reader.ReadToEnd(); + + if (webResponse.StatusCode == HttpStatusCode.OK || webResponse.StatusCode == HttpStatusCode.Accepted) + { + dynamic response = JObject.Parse(rawResponse); + + error = (string)response.error; + if (error.IsEmpty() && response.messages != null) + error = response.messages[0].error; + } + else + { + error = rawResponse; + } + } + } + + if (error.HasValue()) + { + _logger.Error(error); + throw new SmartException(error); + } + } + } } diff --git a/src/Plugins/SmartStore.Clickatell/Controllers/SmsClickatellController.cs b/src/Plugins/SmartStore.Clickatell/Controllers/SmsClickatellController.cs index d9451d5e75..a79d2de550 100644 --- a/src/Plugins/SmartStore.Clickatell/Controllers/SmsClickatellController.cs +++ b/src/Plugins/SmartStore.Clickatell/Controllers/SmsClickatellController.cs @@ -10,67 +10,69 @@ namespace SmartStore.Clickatell.Controllers { - [AdminAuthorize] + [AdminAuthorize] public class SmsClickatellController : PluginControllerBase { - private readonly IPluginFinder _pluginFinder; + private readonly IPluginFinder _pluginFinder; public SmsClickatellController(IPluginFinder pluginFinder) { _pluginFinder = pluginFinder; } - [LoadSetting] - public ActionResult Configure(ClickatellSettings settings) - { - var model = new SmsClickatellModel(); - MiniMapper.Map(settings, model); + [LoadSetting] + public ActionResult Configure(ClickatellSettings settings) + { + var model = new SmsClickatellModel(); + MiniMapper.Map(settings, model); - return View(model); + return View(model); } [HttpPost, SaveSetting, FormValueRequired("save")] + [ValidateAntiForgeryToken] public ActionResult Configure(ClickatellSettings settings, SmsClickatellModel model) { - if (!ModelState.IsValid) - { - return Configure(settings); - } + if (!ModelState.IsValid) + { + return Configure(settings); + } - MiniMapper.Map(model, settings); - settings.ApiId = model.ApiId.TrimSafe(); + MiniMapper.Map(model, settings); + settings.ApiId = model.ApiId.TrimSafe(); - NotifySuccess(T("Admin.Common.DataSuccessfullySaved")); + NotifySuccess(T("Admin.Common.DataSuccessfullySaved")); - return RedirectToConfiguration(ClickatellSmsProvider.SystemName); - } + return RedirectToConfiguration(ClickatellSmsProvider.SystemName); + } - [HttpPost, ActionName("Configure"), FormValueRequired("test-sms")] + [HttpPost, ActionName("Configure"), FormValueRequired("test-sms")] + [ValidateAntiForgeryToken] public ActionResult TestSms(SmsClickatellModel model) { try { if (model.TestMessage.IsEmpty()) { - model.TestSucceeded = false; - model.TestSmsResult = T("Plugins.Sms.Clickatell.EnterMessage"); + model.TestSucceeded = false; + model.TestSmsResult = T("Plugins.Sms.Clickatell.EnterMessage"); } else { var pluginDescriptor = _pluginFinder.GetPluginDescriptorBySystemName(ClickatellSmsProvider.SystemName); var plugin = pluginDescriptor.Instance() as ClickatellSmsProvider; - plugin.SendSms(model.TestMessage); + plugin.SendSms(model.TestMessage); - model.TestSucceeded = true; - model.TestSmsResult = T("Plugins.Sms.Clickatell.TestSuccess"); + model.TestSucceeded = true; + model.TestSmsResult = T("Plugins.Sms.Clickatell.TestSuccess"); } } catch (Exception exception) { - model.TestSucceeded = false; - model.TestSmsResult = T("Plugins.Sms.Clickatell.TestFailed"); - model.TestSmsDetailResult = exception.Message; + model.TestSucceeded = false; + model.TestSmsResult = T("Plugins.Sms.Clickatell.TestFailed"); + model.TestSmsDetailResult = exception.Message; } return View("Configure", model); diff --git a/src/Plugins/SmartStore.Clickatell/Description.txt b/src/Plugins/SmartStore.Clickatell/Description.txt index 003d6a572e..ed2cde9ffe 100644 --- a/src/Plugins/SmartStore.Clickatell/Description.txt +++ b/src/Plugins/SmartStore.Clickatell/Description.txt @@ -1,8 +1,8 @@ FriendlyName: Clickatell SMS Provider SystemName: SmartStore.Clickatell Group: Mobile -Version: 3.2.2 -MinAppVersion: 3.2.2 +Version: 4.2.0 +MinAppVersion: 4.2.0 DisplayOrder: 1 FileName: SmartStore.Clickatell.dll ResourceRootKey: Plugins.Sms.Clickatell \ No newline at end of file diff --git a/src/Plugins/SmartStore.Clickatell/Models/SmsClickatellModel.cs b/src/Plugins/SmartStore.Clickatell/Models/SmsClickatellModel.cs index e3588abc53..11e84e5925 100644 --- a/src/Plugins/SmartStore.Clickatell/Models/SmsClickatellModel.cs +++ b/src/Plugins/SmartStore.Clickatell/Models/SmsClickatellModel.cs @@ -3,10 +3,10 @@ namespace SmartStore.Clickatell.Models { - public class SmsClickatellModel : ModelBase + public class SmsClickatellModel : ModelBase { [SmartResourceDisplayName("Plugins.Sms.Clickatell.Fields.Enabled")] - public bool Enabled { get; set; } + public bool Enabled { get; set; } [SmartResourceDisplayName("Plugins.Sms.Clickatell.Fields.PhoneNumber")] public string PhoneNumber { get; set; } @@ -17,8 +17,8 @@ public class SmsClickatellModel : ModelBase [SmartResourceDisplayName("Plugins.Sms.Clickatell.Fields.TestMessage")] public string TestMessage { get; set; } - public bool TestSucceeded { get; set; } - public string TestSmsResult { get; set; } - public string TestSmsDetailResult { get; set; } - } + public bool TestSucceeded { get; set; } + public string TestSmsResult { get; set; } + public string TestSmsDetailResult { get; set; } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.Clickatell/OrderPlacedEventConsumer.cs b/src/Plugins/SmartStore.Clickatell/OrderPlacedEventConsumer.cs index 8ff1936f22..49fab1aa85 100644 --- a/src/Plugins/SmartStore.Clickatell/OrderPlacedEventConsumer.cs +++ b/src/Plugins/SmartStore.Clickatell/OrderPlacedEventConsumer.cs @@ -7,30 +7,30 @@ namespace SmartStore.Clickatell { - public class OrderPlacedEventConsumer : IConsumer + public class OrderPlacedEventConsumer : IConsumer { - private readonly ICommonServices _services; - private readonly ClickatellSettings _clickatellSettings; + private readonly ICommonServices _services; + private readonly ClickatellSettings _clickatellSettings; private readonly IPluginFinder _pluginFinder; private readonly IOrderService _orderService; public OrderPlacedEventConsumer( - ICommonServices services, - ClickatellSettings clickatellSettings, + ICommonServices services, + ClickatellSettings clickatellSettings, IPluginFinder pluginFinder, - IOrderService orderService) + IOrderService orderService) { - _services = services; + _services = services; _clickatellSettings = clickatellSettings; _pluginFinder = pluginFinder; _orderService = orderService; - T = NullLocalizer.Instance; - } + T = NullLocalizer.Instance; + } - public Localizer T { get; set; } + public Localizer T { get; set; } - public void HandleEvent(OrderPlacedEvent eventMessage) + public void HandleEvent(OrderPlacedEvent eventMessage) { if (!_clickatellSettings.Enabled) return; @@ -39,22 +39,22 @@ public void HandleEvent(OrderPlacedEvent eventMessage) if (descriptor == null) return; - var storeId = _services.StoreContext.CurrentStore.Id; + var storeId = _services.StoreContext.CurrentStore.Id; - if (!(storeId == 0 || _services.Settings.GetSettingByKey(descriptor.GetSettingKey("LimitedToStores")).ToIntArrayContains(storeId, true))) - return; + if (!(storeId == 0 || _services.Settings.GetSettingByKey(descriptor.GetSettingKey("LimitedToStores")).ToIntArrayContains(storeId, true))) + return; var plugin = descriptor.Instance() as ClickatellSmsProvider; if (plugin == null) return; - try - { - plugin.SendSms(T("Plugins.Sms.Clickatell.OrderPlacedMessage", eventMessage.Order.GetOrderNumber())); + try + { + plugin.SendSms(T("Plugins.Sms.Clickatell.OrderPlacedMessage", eventMessage.Order.GetOrderNumber())); - _orderService.AddOrderNote(eventMessage.Order, T("Plugins.Sms.Clickatell.SmsSentNote")); - } - catch { } + _orderService.AddOrderNote(eventMessage.Order, T("Plugins.Sms.Clickatell.SmsSentNote")); + } + catch { } } } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.Clickatell/RouteProvider.cs b/src/Plugins/SmartStore.Clickatell/RouteProvider.cs index 1cc25597ed..6271b445db 100644 --- a/src/Plugins/SmartStore.Clickatell/RouteProvider.cs +++ b/src/Plugins/SmartStore.Clickatell/RouteProvider.cs @@ -4,24 +4,18 @@ namespace SmartStore.Clickatell { - public partial class RouteProvider : IRouteProvider + public partial class RouteProvider : IRouteProvider { public void RegisterRoutes(RouteCollection routes) { routes.MapRoute("SmartStore.Clickatell", - "Plugins/SmartStore.Clickatell/{action}", + "Plugins/SmartStore.Clickatell/{action}", new { controller = "SmsClickatell", action = "Configure" }, new[] { "SmartStore.Clickatell.Controllers" } ) - .DataTokens["area"] = ClickatellSmsProvider.SystemName; + .DataTokens["area"] = ClickatellSmsProvider.SystemName; } - public int Priority - { - get - { - return 0; - } - } + public int Priority => 0; } } diff --git a/src/Plugins/SmartStore.Clickatell/SmartStore.Clickatell.csproj b/src/Plugins/SmartStore.Clickatell/SmartStore.Clickatell.csproj index decee9a5f7..a7ccb144e0 100644 --- a/src/Plugins/SmartStore.Clickatell/SmartStore.Clickatell.csproj +++ b/src/Plugins/SmartStore.Clickatell/SmartStore.Clickatell.csproj @@ -1,6 +1,5 @@  - @@ -21,7 +20,7 @@ Properties SmartStore.Clickatell SmartStore.Clickatell - v4.6.1 + v4.7.2 512 @@ -45,6 +44,8 @@ + + true @@ -84,13 +85,16 @@ MinimumRecommendedRules.ruleset + + ..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + False + ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll False - - ..\..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll - False + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll @@ -101,9 +105,9 @@ - + ..\..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.Helpers.dll False diff --git a/src/Plugins/SmartStore.Clickatell/Views/SmsClickatell/Configure.cshtml b/src/Plugins/SmartStore.Clickatell/Views/SmsClickatell/Configure.cshtml index 09992e2c48..6898a79b73 100644 --- a/src/Plugins/SmartStore.Clickatell/Views/SmsClickatell/Configure.cshtml +++ b/src/Plugins/SmartStore.Clickatell/Views/SmsClickatell/Configure.cshtml @@ -31,6 +31,8 @@ @using (Html.BeginForm()) { + @Html.AntiForgeryToken() + @@ -79,22 +79,6 @@ @Html.ValidationMessageFor(m => m.ExpirationDays) - - - -
              diff --git a/src/Plugins/SmartStore.Clickatell/packages.config b/src/Plugins/SmartStore.Clickatell/packages.config index c716e80c1a..0470997544 100644 --- a/src/Plugins/SmartStore.Clickatell/packages.config +++ b/src/Plugins/SmartStore.Clickatell/packages.config @@ -3,6 +3,7 @@ + - + \ No newline at end of file diff --git a/src/Plugins/SmartStore.Clickatell/web.config b/src/Plugins/SmartStore.Clickatell/web.config index 522d79a602..06bf37d3c2 100644 --- a/src/Plugins/SmartStore.Clickatell/web.config +++ b/src/Plugins/SmartStore.Clickatell/web.config @@ -1,9 +1,10 @@ - + - - + + + - + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Plugins/SmartStore.DevTools/AdminMenu.cs b/src/Plugins/SmartStore.DevTools/AdminMenu.cs index 8110443480..052ef44657 100644 --- a/src/Plugins/SmartStore.DevTools/AdminMenu.cs +++ b/src/Plugins/SmartStore.DevTools/AdminMenu.cs @@ -1,40 +1,43 @@ using SmartStore.Collections; +using SmartStore.DevTools.Security; using SmartStore.Web.Framework.UI; namespace SmartStore.DevTools { - public class AdminMenu : AdminMenuProvider - { - protected override void BuildMenuCore(TreeNode pluginsNode) - { - var menuItem = new MenuItem().ToBuilder() - .Text("Developer Tools") - .Icon("far fa-terminal") - .Action("ConfigurePlugin", "Plugin", new { systemName = "SmartStore.DevTools", area = "Admin" }) - .ToItem(); - - pluginsNode.Prepend(menuItem); + public class AdminMenu : AdminMenuProvider + { + protected override void BuildMenuCore(TreeNode pluginsNode) + { + var menuItem = new MenuItem().ToBuilder() + .Text("Developer Tools") + .ResKey("Plugins.FriendlyName.SmartStore.DevTools") + .Icon("far fa-terminal") + .PermissionNames(DevToolsPermissions.Read) + .Action("ConfigurePlugin", "Plugin", new { systemName = "SmartStore.DevTools", area = "Admin" }) + .ToItem(); - // uncomment to add to admin menu (see plugin sub-menu) - //var backendExtensionItem = new MenuItem().ToBuilder() - // .Text("Backend extension") - // .Icon("chart-area") - // .Action("BackendExtension", "DevTools", new { area = "SmartStore.DevTools" }) - // .ToItem(); - //pluginsNode.Append(backendExtensionItem); + pluginsNode.Prepend(menuItem); - // uncomment to add a sub-menu (see plugin sub-menu) - //var subMenu = new MenuItem().ToBuilder() - // .Text("Sub Menu") - // .Action("BackendExtension", "DevTools", new { area = "SmartStore.DevTools" }) - // .ToItem(); - //var subMenuNode = pluginsNode.Append(subMenu); + // uncomment to add to admin menu (see plugin sub-menu) + //var backendExtensionItem = new MenuItem().ToBuilder() + // .Text("Backend extension") + // .Icon("chart-area") + // .Action("BackendExtension", "DevTools", new { area = "SmartStore.DevTools" }) + // .ToItem(); + //pluginsNode.Append(backendExtensionItem); - //var subMenuItem = new MenuItem().ToBuilder() - // .Text("Sub Menu Item 1") - // .Action("BackendExtension", "DevTools", new { area = "SmartStore.DevTools" }) - // .ToItem(); - //subMenuNode.Append(subMenuItem); - } - } + // uncomment to add a sub-menu (see plugin sub-menu) + //var subMenu = new MenuItem().ToBuilder() + // .Text("Sub Menu") + // .Action("BackendExtension", "DevTools", new { area = "SmartStore.DevTools" }) + // .ToItem(); + //var subMenuNode = pluginsNode.Append(subMenu); + + //var subMenuItem = new MenuItem().ToBuilder() + // .Text("Sub Menu Item 1") + // .Action("BackendExtension", "DevTools", new { area = "SmartStore.DevTools" }) + // .ToItem(); + //subMenuNode.Append(subMenuItem); + } + } } diff --git a/src/Plugins/SmartStore.DevTools/Blocks/SampleBlock.cs b/src/Plugins/SmartStore.DevTools/Blocks/SampleBlock.cs index a089af9c47..1e48a0e388 100644 --- a/src/Plugins/SmartStore.DevTools/Blocks/SampleBlock.cs +++ b/src/Plugins/SmartStore.DevTools/Blocks/SampleBlock.cs @@ -14,16 +14,16 @@ namespace SmartStore.DevTools.Blocks /// You can, however, overwrite any method to fulfill your custom needs. /// [Block("sample", Icon = "far fa-terminal", FriendlyName = "Sample", DisplayOrder = 50, IsInternal = true)] // REMOVE IsInternal = true to display the block in the page builder - public class SampleBlockHandler : BlockHandlerBase - { + public class SampleBlockHandler : BlockHandlerBase + { public override SampleBlock Load(IBlockEntity entity, StoryViewMode viewMode) { var block = base.Load(entity, viewMode); - // By default 'BlockHandlerBase' stores block instance data as JSON in the 'Model' field of the 'PageStoryBlock' table. - // You can, however, store data anywhere you like and override loading behaviour in this method. + // By default 'BlockHandlerBase' stores block instance data as JSON in the 'Model' field of the 'PageStoryBlock' table. + // You can, however, store data anywhere you like and override loading behaviour in this method. - if (viewMode == StoryViewMode.Edit) + if (viewMode == StoryViewMode.Edit) { // You can prepare your model especially for the edit mode of the block, // e.g. add some SelectListItems for dropdownmenus which you will only need in the edit mode of the block @@ -40,48 +40,48 @@ public override SampleBlock Load(IBlockEntity entity, StoryViewMode viewMode) public override void Save(SampleBlock block, IBlockEntity entity) { - // By default 'BlockHandlerBase' stores block instance data as JSON in the 'Model' field of the 'PageStoryBlock' table. - // You can, however, store data anywhere you like and override persistance behaviour in this method. + // By default 'BlockHandlerBase' stores block instance data as JSON in the 'Model' field of the 'PageStoryBlock' table. + // You can, however, store data anywhere you like and override persistance behaviour in this method. - base.Save(block, entity); + base.Save(block, entity); } - /// - /// By default block templates (Edit & Public) will be searched in '{Area}\Views\Story\BlockTemplates\{BlockSystemName}' or '{Area}\Views\Shared\BlockTemplates\{BlockSystemName}', - /// while {Area} represents your plugin folder. - /// The public action can address a deviating route by overwriting RenderCore & GetRoute. - /// You can override this behaviour by e.g. calling a child action in your plugin controller instead of directly rendering a view. - /// For this to take effect you have to override both methods 'RenderCore()' and 'GetRoute()' - /// - //protected override void RenderCore(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHelper, TextWriter textWriter) - //{ - // if (templates.First() == "Edit") - // { - // base.RenderCore(element, templates, htmlHelper, textWriter); - // } - // else - // { - // base.RenderByChildAction(element, templates, htmlHelper, textWriter); - // } - //} + /// + /// By default block templates (Edit & Public) will be searched in '{Area}\Views\Story\BlockTemplates\{BlockSystemName}' or '{Area}\Views\Shared\BlockTemplates\{BlockSystemName}', + /// while {Area} represents your plugin folder. + /// The public action can address a deviating route by overwriting RenderCore & GetRoute. + /// You can override this behaviour by e.g. calling a child action in your plugin controller instead of directly rendering a view. + /// For this to take effect you have to override both methods 'RenderCore()' and 'GetRoute()' + /// + //protected override void RenderCore(IBlockContainer element, IEnumerable templates, HtmlHelper htmlHelper, TextWriter textWriter) + //{ + // if (templates.First() == "Edit") + // { + // base.RenderCore(element, templates, htmlHelper, textWriter); + // } + // else + // { + // base.RenderByChildAction(element, templates, htmlHelper, textWriter); + // } + //} - //protected override RouteInfo GetRoute(IBlockContainer element, string template) - //{ - // var block = (SampleBlock)element.Block; - // return new RouteInfo("SampleBlock", "DevTools", new - // { - // block = element.Block, - // area = "SmartStore.DevTools" - // }); - //} - } + //protected override RouteInfo GetRoute(IBlockContainer element, string template) + //{ + // var block = (SampleBlock)element.Block; + // return new RouteInfo("SampleBlock", "DevTools", new + // { + // block = element.Block, + // area = "SmartStore.DevTools" + // }); + //} + } - /// - /// Any type that implements the 'IBlock' interface acts as a model. - /// - [Validator(typeof(SampleBlockValidator))] + /// + /// Any type that implements the 'IBlock' interface acts as a model. + /// + [Validator(typeof(SampleBlockValidator))] public class SampleBlock : IBlock - { + { [SmartResourceDisplayName("Plugins.SmartStore.DevTools.Block.Property")] public string Property { get; set; } diff --git a/src/Plugins/SmartStore.DevTools/Controllers/DevToolsController.cs b/src/Plugins/SmartStore.DevTools/Controllers/DevToolsController.cs index 28827a7307..6ed295335a 100644 --- a/src/Plugins/SmartStore.DevTools/Controllers/DevToolsController.cs +++ b/src/Plugins/SmartStore.DevTools/Controllers/DevToolsController.cs @@ -1,8 +1,10 @@ using System.Web.Mvc; +using SmartStore.ComponentModel; +using SmartStore.Core.Search.Facets; +using SmartStore.Core.Security; using SmartStore.DevTools.Blocks; using SmartStore.DevTools.Models; -using SmartStore.Services; -using SmartStore.Services.Cms.Blocks; +using SmartStore.DevTools.Security; using SmartStore.Web.Framework.Controllers; using SmartStore.Web.Framework.Security; using SmartStore.Web.Framework.Settings; @@ -12,44 +14,53 @@ namespace SmartStore.DevTools.Controllers { public class DevToolsController : SmartController { - private readonly ICommonServices _services; - - public DevToolsController(ICommonServices services) - { - _services = services; - } - - [LoadSetting, ChildActionOnly] + [AdminAuthorize, Permission(DevToolsPermissions.Read)] + [ChildActionOnly, LoadSetting] public ActionResult Configure(ProfilerSettings settings) { - return View(settings); + var model = MiniMapper.Map(settings); + + return View(model); } - [SaveSetting(false), HttpPost, ChildActionOnly, ActionName("Configure")] - public ActionResult ConfigurePost(ProfilerSettings settings) + [AdminAuthorize, Permission(DevToolsPermissions.Update)] + [HttpPost, ChildActionOnly, SaveSetting] + [ValidateAntiForgeryToken] + public ActionResult Configure(ConfigurationModel model, ProfilerSettings settings) { - return RedirectToConfiguration("SmartStore.DevTools"); + if (!ModelState.IsValid) + { + return Configure(settings); + } + + ModelState.Clear(); + MiniMapper.Map(model, settings); + + return RedirectToConfiguration("SmartStore.DevTools"); } + [ChildActionOnly] public ActionResult MiniProfiler() { return View(); } + [ChildActionOnly] public ActionResult MachineName() { - ViewBag.EnvironmentIdentifier = _services.ApplicationEnvironment.EnvironmentIdentifier; + ViewBag.EnvironmentIdentifier = Services.ApplicationEnvironment.EnvironmentIdentifier; return View(); } - + + [ChildActionOnly] public ActionResult WidgetZone(string widgetZone) { - var storeScope = this.GetActiveStoreScopeConfiguration(_services.StoreService, _services.WorkContext); - var settings = _services.Settings.LoadSetting(storeScope); + var storeScope = this.GetActiveStoreScopeConfiguration(Services.StoreService, Services.WorkContext); + var settings = Services.Settings.LoadSetting(storeScope); if (settings.DisplayWidgetZones) - { + { ViewData["widgetZone"] = widgetZone; return View(); @@ -58,23 +69,23 @@ public ActionResult WidgetZone(string widgetZone) return new EmptyResult(); } - [ChildActionOnly] - public ActionResult SampleBlock(SampleBlock block) - { - // Do something here with your block instance and return a result that should be rendered by the Page Builder. - return View(block); - } + [ChildActionOnly] + public ActionResult SampleBlock(SampleBlock block) + { + // Do something here with your block instance and return a result that should be rendered by the Page Builder. + return View(block); + } - [AdminAuthorize, AdminThemed] - public ActionResult BackendExtension() - { - var model = new BackendExtensionModel - { - Welcome = "Hello world!" - }; + [AdminAuthorize, AdminThemed] + public ActionResult BackendExtension() + { + var model = new BackendExtensionModel + { + Welcome = "Hello world!" + }; - return View(model); - } + return View(model); + } [AdminAuthorize] public ActionResult ProductEditTab(int productId, FormCollection form) @@ -89,9 +100,16 @@ public ActionResult ProductEditTab(int productId, FormCollection form) return result; } - public ActionResult MyDemoWidget() - { - return Content("Hello world! This is a sample widget created for demonstration purposes by Dev-Tools plugin."); - } - } + public ActionResult MyDemoWidget() + { + return Content("Hello world! This is a sample widget created for demonstration purposes by Dev-Tools plugin."); + } + + [ChildActionOnly, AllowAnonymous] + public ActionResult MyCustomFacetTemplate(FacetGroup facetGroup, string templateName) + { + /// Just a "proxy" for our . + return PartialView(templateName, facetGroup); + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/DependencyRegistrar.cs b/src/Plugins/SmartStore.DevTools/DependencyRegistrar.cs index 761f7820a2..0f51d70cd4 100644 --- a/src/Plugins/SmartStore.DevTools/DependencyRegistrar.cs +++ b/src/Plugins/SmartStore.DevTools/DependencyRegistrar.cs @@ -1,3 +1,4 @@ +using System.Web; using Autofac; using Autofac.Integration.Mvc; using SmartStore.Core.Data; @@ -6,43 +7,52 @@ using SmartStore.Core.Logging; using SmartStore.DevTools.Filters; using SmartStore.DevTools.Services; -using SmartStore.Web.Controllers; +using SmartStore.Services.Search.Rendering; using SmartStore.Web.Framework.Controllers; namespace SmartStore.DevTools { - public class DependencyRegistrar : IDependencyRegistrar + public class DependencyRegistrar : IDependencyRegistrar { - public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, bool isActiveModule) + public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, bool isActiveModule) { - if (isActiveModule) - { - builder.RegisterType().As().InstancePerRequest(); + if (isActiveModule) + { + builder.Register(c => + { + var ctx = c.Resolve(); - if (DataSettings.DatabaseIsInstalled()) - { - // intercept ALL public store controller actions - builder.RegisterType().AsActionFilterFor(); - builder.RegisterType().AsActionFilterFor(); - builder.RegisterType().AsResultFilterFor(); + if (ProfilerHttpModule.MiniProfilerStarted(ctx.ApplicationInstance)) + { + return new MiniProfilerChronometer(); + } + return NullChronometer.Instance; + }).InstancePerRequest(); + + if (DataSettings.DatabaseIsInstalled()) + { + // intercept ALL public store controller actions + builder.RegisterType().AsActionFilterFor(); + builder.RegisterType().AsActionFilterFor(); + builder.RegisterType().AsResultFilterFor(); // Add an action to product detail offer actions //builder.RegisterType() // .AsActionFilterFor(x => x.ProductDetails(default(int), default(string), null)) // .InstancePerRequest(); - + //// intercept CatalogController's Product action //builder.RegisterType().AsResultFilterFor(x => x.Product(default(int), default(string))).InstancePerRequest(); //builder.RegisterType().AsActionFilterFor().InstancePerRequest(); //// intercept CheckoutController's Index action (to hijack the checkout or payment workflow) //builder.RegisterType().AsActionFilterFor(x => x.Index()).InstancePerRequest(); } - } - } - public int Order - { - get { return 1; } + // Register a custom view template for certain search facets. Register it as InstancePerRequest() if your selector has no Autofac dependencies. + //builder.RegisterType().As().InstancePerRequest(); + } } + + public int Order => 1; } } diff --git a/src/Plugins/SmartStore.DevTools/Description.txt b/src/Plugins/SmartStore.DevTools/Description.txt index 54399e9fcf..3913be2199 100644 --- a/src/Plugins/SmartStore.DevTools/Description.txt +++ b/src/Plugins/SmartStore.DevTools/Description.txt @@ -1,8 +1,8 @@ -FriendlyName: SmartStore.NET Developer Tools (MiniProfiler and other goodies) +FriendlyName: Smartstore Developer Tools (MiniProfiler and other goodies) SystemName: SmartStore.DevTools Group: Developer -Version: 3.2.2 -MinAppVersion: 3.2.2 +Version: 4.2.0 +MinAppVersion: 4.2.0 DisplayOrder: 1 FileName: SmartStore.DevTools.dll ResourceRootKey: Plugins.Developer.DevTools \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/DevToolsPlugin.cs b/src/Plugins/SmartStore.DevTools/DevToolsPlugin.cs index 6001b8b582..6f7c34c4b9 100644 --- a/src/Plugins/SmartStore.DevTools/DevToolsPlugin.cs +++ b/src/Plugins/SmartStore.DevTools/DevToolsPlugin.cs @@ -1,105 +1,103 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Linq; using System.Web.Routing; +using SmartStore.Core.Caching; using SmartStore.Core.Logging; using SmartStore.Core.Plugins; using SmartStore.Data; using SmartStore.Data.Setup; -using SmartStore.Services.Configuration; -using SmartStore.Core.Caching; +using SmartStore.Services; namespace SmartStore.DevTools { - [DisplayOrder(10)] - [SystemName("Widgets.DevToolsDemo")] - [FriendlyName("Dev-Tools Demo Widget")] - public class DevToolsPlugin : BasePlugin, IConfigurable //, IWidget - { - private readonly ISettingService _settingService; - private readonly ICacheableRouteRegistrar _cacheableRouteRegistrar; - - public DevToolsPlugin(ISettingService settingService, - ICacheableRouteRegistrar cacheAbleRouteRegistrar) + [DisplayOrder(10)] + [SystemName("Widgets.DevToolsDemo")] + [FriendlyName("Dev-Tools Demo Widget")] + public class DevToolsPlugin : BasePlugin, IConfigurable //, IWidget + { + private readonly ICommonServices _services; + private readonly ICacheableRouteRegistrar _cacheableRouteRegistrar; + + public DevToolsPlugin( + ICommonServices services, + ICacheableRouteRegistrar cacheAbleRouteRegistrar) { - _settingService = settingService; - _cacheableRouteRegistrar = cacheAbleRouteRegistrar; + _services = services; + _cacheableRouteRegistrar = cacheAbleRouteRegistrar; - this.Logger = NullLogger.Instance; + Logger = NullLogger.Instance; } - public ILogger Logger { get; set; } + public ILogger Logger { get; set; } - //public IList GetWidgetZones() => new List { "home_page_top" }; + //public IList GetWidgetZones() => new List { "home_page_top" }; - //public void GetDisplayWidgetRoute(string widgetZone, object model, int storeId, out string actionName, out string controllerName, out RouteValueDictionary routeValues) - //{ - // actionName = "MyDemoWidget"; - // controllerName = "DevTools"; + //public void GetDisplayWidgetRoute(string widgetZone, object model, int storeId, out string actionName, out string controllerName, out RouteValueDictionary routeValues) + //{ + // actionName = "MyDemoWidget"; + // controllerName = "DevTools"; - // routeValues = new RouteValueDictionary - // { - // { "Namespaces", "SmartStore.DevTools.Controllers" }, - // { "area", "SmartStore.DevTools" } - // }; - //} + // routeValues = new RouteValueDictionary + // { + // { "Namespaces", "SmartStore.DevTools.Controllers" }, + // { "area", "SmartStore.DevTools" } + // }; + //} - public void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) + public void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) { actionName = "Configure"; controllerName = "DevTools"; - routeValues = new RouteValueDictionary() { { "area", "SmartStore.DevTools" } }; + routeValues = new RouteValueDictionary() { { "area", "SmartStore.DevTools" } }; } public override void Install() { - // Example for how to add a route to the output cache - //_cacheableRouteRegistrar.RegisterCacheableRoute("SmartStore.DevTools/DevTools/PublicInfo"); + // Example for how to add a route to the output cache + //_cacheableRouteRegistrar.RegisterCacheableRoute("SmartStore.DevTools/DevTools/PublicInfo"); + + _services.Settings.SaveSetting(new ProfilerSettings()); - _settingService.SaveSetting(new ProfilerSettings()); - base.Install(); + base.Install(); - Logger.Info(string.Format("Plugin installed: SystemName: {0}, Version: {1}, Description: '{2}'", PluginDescriptor.SystemName, PluginDescriptor.Version, PluginDescriptor.FriendlyName)); + Logger.Info(string.Format("Plugin installed: SystemName: {0}, Version: {1}, Description: '{2}'", PluginDescriptor.SystemName, PluginDescriptor.Version, PluginDescriptor.FriendlyName)); } - /// - /// Uninstall plugin - /// public override void Uninstall() { - // Example for how to remove a route from the output cache - //_cacheableRouteRegistrar.RemoveCacheableRoute("SmartStore.DevTools/DevTools/PublicInfo"); + // Example for how to remove a route from the output cache + //_cacheableRouteRegistrar.RemoveCacheableRoute("SmartStore.DevTools/DevTools/PublicInfo"); - _settingService.DeleteSetting(); - base.Uninstall(); + _services.Settings.DeleteSetting(); + + base.Uninstall(); } - private static bool? _hasPendingMigrations; - internal static bool HasPendingMigrations() - { - bool result = true; - - if (_hasPendingMigrations == null) - { - try - { - var migrator = new DbSeedingMigrator(); - result = migrator.GetPendingMigrations().Any(); - - if (result == false) - { - // Don't check again - _hasPendingMigrations = false; - } - } - catch { } - } - else - { - result = _hasPendingMigrations.Value; - } - - return result; - } - } + private static bool? _hasPendingMigrations; + internal static bool HasPendingMigrations() + { + bool result = true; + + if (_hasPendingMigrations == null) + { + try + { + var migrator = new DbSeedingMigrator(); + result = migrator.GetPendingMigrations().Any(); + + if (result == false) + { + // Don't check again + _hasPendingMigrations = false; + } + } + catch { } + } + else + { + result = _hasPendingMigrations.Value; + } + + return result; + } + } } diff --git a/src/Plugins/SmartStore.DevTools/Events/CustomTab.cs b/src/Plugins/SmartStore.DevTools/Events/CustomTab.cs index cb5f929564..38fb44d16a 100644 --- a/src/Plugins/SmartStore.DevTools/Events/CustomTab.cs +++ b/src/Plugins/SmartStore.DevTools/Events/CustomTab.cs @@ -6,7 +6,7 @@ namespace SmartStore.DevTools.Events { - public class CustomTab : IConsumer + public class CustomTab : IConsumer { private readonly ICommonServices _services; @@ -50,7 +50,7 @@ public void HandleEvent(ModelBoundEvent eventMessage) var model = eventMessage.BoundModel.CustomProperties["DevTools"] as BackendExtensionModel; if (model == null) return; - + // Do something with the model now: e.g. store it ;-) } } diff --git a/src/Plugins/SmartStore.DevTools/Examples/OutputCacheInvalidationObserver.cs b/src/Plugins/SmartStore.DevTools/Examples/OutputCacheInvalidationObserver.cs index 9c5bcda2a3..59cf71c361 100644 --- a/src/Plugins/SmartStore.DevTools/Examples/OutputCacheInvalidationObserver.cs +++ b/src/Plugins/SmartStore.DevTools/Examples/OutputCacheInvalidationObserver.cs @@ -1,89 +1,82 @@ -using SmartStore.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using SmartStore.Core; using SmartStore.Core.Caching; using SmartStore.Core.Infrastructure; -using SmartStore.Services; using SmartStore.Services.Catalog; using SmartStore.Services.Topics; -using System; -using System.Collections.Generic; -using System.Linq; namespace SmartStore.DevTools.Examples { - public static class OutputCacheInvalidationObserver - { - public static void Execute() - { - // Register a handler which returns unique string tags for your custom entity. - // This is required if you ever call IDisplayControl.Announce(entity) for your entity, - // otherwise the system is not able to generate tags. - // Tags can be obtained via IDisplayControl.GetCacheControlTagsFor(entity). - // Invalidation by tags is done via IOutputCacheProvider.InvalidateByTag(string[] tags). - // InvalidateByTag() will remove any page from the output cache storage in which your custom entity is somehow displayed. - DisplayControl.RegisterHandlerFor(typeof(MyRecord), (x, c) => new[] { "mr" + x.Id }); + public static class OutputCacheInvalidationObserver + { + public static void Execute() + { + // Register a handler which returns unique string tags for your custom entity. + // This is required if you ever call IDisplayControl.Announce(entity) for your entity, + // otherwise the system is not able to generate tags. + // Tags can be obtained via IDisplayControl.GetCacheControlTagsFor(entity). + // Invalidation by tags is done via IOutputCacheProvider.InvalidateByTag(string[] tags). + // InvalidateByTag() will remove any page from the output cache storage in which your custom entity is somehow displayed. + DisplayControl.RegisterHandlerFor(typeof(MyRecord), (x, c) => new[] { "mr" + x.Id }); - // Register an output cache observe handler - var observer = EngineContext.Current.Resolve(); - observer.ObserveEntity(Observe); - } + // Register an output cache observe handler + var observer = EngineContext.Current.Resolve(); + observer.ObserveEntity(Observe); + } - private static void Observe(ObserveEntityContext ctx) - { - // If your plugin renders dynamic content to the frontend which won't change often once it is configured, you should add a corresponding route - // to the ouput cache to add the rendered output of the plugin to the cache. For more information on this see the Install/Uninstall method of this plugin + private static void Observe(ObserveEntityContext ctx) + { + // If your plugin renders dynamic content to the frontend which won't change often once it is configured, you should add a corresponding route + // to the ouput cache to add the rendered output of the plugin to the cache. For more information on this see the Install/Uninstall method of this plugin - // In the example below we assume the plugin has own entities which depend on system entities like Product, Category or Topic. - // In this case you must invalidate the cache items for the system entities as soon as the corresponding plugin entity is changed by the shop admin - // in order to remove the cached output of the affected pages in the frontend and thus be rebuilt + // In the example below we assume the plugin has own entities which depend on system entities like Product, Category or Topic. + // In this case you must invalidate the cache items for the system entities as soon as the corresponding plugin entity is changed by the shop admin + // in order to remove the cached output of the affected pages in the frontend and thus be rebuilt - var myRecord = ctx?.Entity as MyRecord; + var myRecord = ctx?.Entity as MyRecord; - if (myRecord != null) - { - IEnumerable tags = null; + if (myRecord != null) + { + IEnumerable tags = null; - var outputCacheProvider = ctx.OutputCacheProvider; + var outputCacheProvider = ctx.OutputCacheProvider; - // We assume the domain record from the plugin stores information about the type of the entity in EntityName and the corresponding Id in EntityId - var entityname = myRecord.EntityName; + // We assume the domain record from the plugin stores information about the type of the entity in EntityName and the corresponding Id in EntityId + var entityname = myRecord.EntityName; - // Collect the tags for the entities which must be invalidated due to changes of the plugin domain record - switch (entityname.ToLower()) - { - case "product": - var product = ctx.ServiceContainer.Resolve().GetProductById(myRecord.EntityId); - if (product != null) tags = ctx.DisplayControl.GetCacheControlTagsFor(product); - break; - case "category": - var category = ctx.ServiceContainer.Resolve().GetCategoryById(myRecord.EntityId); - if (category != null) tags = ctx.DisplayControl.GetCacheControlTagsFor(category); - break; - case "topic": - var topic = ctx.ServiceContainer.Resolve().GetTopicById(myRecord.EntityId); - if (topic != null) tags = ctx.DisplayControl.GetCacheControlTagsFor(topic); - break; - } + // Collect the tags for the entities which must be invalidated due to changes of the plugin domain record + switch (entityname.ToLower()) + { + case "product": + var product = ctx.ServiceContainer.Resolve().GetProductById(myRecord.EntityId); + if (product != null) tags = ctx.DisplayControl.GetCacheControlTagsFor(product); + break; + case "category": + var category = ctx.ServiceContainer.Resolve().GetCategoryById(myRecord.EntityId); + if (category != null) tags = ctx.DisplayControl.GetCacheControlTagsFor(category); + break; + case "topic": + var topic = ctx.ServiceContainer.Resolve().GetTopicById(myRecord.EntityId); + if (topic != null) tags = ctx.DisplayControl.GetCacheControlTagsFor(topic); + break; + } - // Invalidate cache items by the collected tags - if (tags != null && tags.Any()) - { - outputCacheProvider.InvalidateByTag(tags.ToArray()); - ctx.Handled = true; - } - } - } + // Invalidate cache items by the collected tags + if (tags != null && tags.Any()) + { + outputCacheProvider.InvalidateByTag(tags.ToArray()); + ctx.Handled = true; + } + } + } - internal class MyRecord : BaseEntity - { - public int EntityId - { - get { return 0; } - } + internal class MyRecord : BaseEntity + { + public int EntityId => 0; - public string EntityName - { - get { return String.Empty; } - } - } - } + public string EntityName => String.Empty; + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/Filters/MachineNameFilter.cs b/src/Plugins/SmartStore.DevTools/Filters/MachineNameFilter.cs index c71e82d5fe..6d969ed842 100644 --- a/src/Plugins/SmartStore.DevTools/Filters/MachineNameFilter.cs +++ b/src/Plugins/SmartStore.DevTools/Filters/MachineNameFilter.cs @@ -6,51 +6,51 @@ namespace SmartStore.DevTools.Filters { - public class MachineNameFilter : IResultFilter - { - private readonly ICommonServices _services; - private readonly Lazy _widgetProvider; - private readonly ProfilerSettings _profilerSettings; - - public MachineNameFilter( - ICommonServices services, - Lazy widgetProvider, - ProfilerSettings profilerSettings) - { - _services = services; - _widgetProvider = widgetProvider; - _profilerSettings = profilerSettings; - } - - public void OnResultExecuting(ResultExecutingContext filterContext) - { - if (!_profilerSettings.DisplayMachineName) - return; - - if (filterContext.IsChildAction) - return; - - var result = filterContext.Result; - - // should only run on a full view rendering result or HTML ContentResult - if (!result.IsHtmlViewResult()) - return; - - if (!_services.WorkContext.CurrentCustomer.IsAdmin() && !filterContext.HttpContext.Request.IsLocal) - { - return; - } - - _widgetProvider.Value.RegisterAction( - new[] { "body_end_html_tag_before", "admin_content_after" }, - "MachineName", - "DevTools", - new { area = "SmartStore.DevTools" }); - } - - public void OnResultExecuted(ResultExecutedContext filterContext) - { - } - - } + public class MachineNameFilter : IResultFilter + { + private readonly ICommonServices _services; + private readonly Lazy _widgetProvider; + private readonly ProfilerSettings _profilerSettings; + + public MachineNameFilter( + ICommonServices services, + Lazy widgetProvider, + ProfilerSettings profilerSettings) + { + _services = services; + _widgetProvider = widgetProvider; + _profilerSettings = profilerSettings; + } + + public void OnResultExecuting(ResultExecutingContext filterContext) + { + if (!_profilerSettings.DisplayMachineName) + return; + + if (filterContext.IsChildAction) + return; + + var result = filterContext.Result; + + // should only run on a full view rendering result or HTML ContentResult + if (!result.IsHtmlViewResult()) + return; + + if (!_services.WorkContext.CurrentCustomer.IsAdmin() && !filterContext.HttpContext.Request.IsLocal) + { + return; + } + + _widgetProvider.Value.RegisterAction( + new[] { "body_end_html_tag_before", "admin_content_after" }, + "MachineName", + "DevTools", + new { area = "SmartStore.DevTools" }); + } + + public void OnResultExecuted(ResultExecutedContext filterContext) + { + } + + } } diff --git a/src/Plugins/SmartStore.DevTools/Filters/ProfilerFilter.cs b/src/Plugins/SmartStore.DevTools/Filters/ProfilerFilter.cs index 5822618bbd..71d65d003c 100644 --- a/src/Plugins/SmartStore.DevTools/Filters/ProfilerFilter.cs +++ b/src/Plugins/SmartStore.DevTools/Filters/ProfilerFilter.cs @@ -3,114 +3,114 @@ using System.Web; using System.Web.Mvc; using SmartStore.Services; +using SmartStore.Services.Common; using SmartStore.Services.Customers; using SmartStore.Web.Framework.UI; -using SmartStore.Services.Common; namespace SmartStore.DevTools.Filters { - public class ProfilerFilter : IActionFilter, IResultFilter - { - private readonly ICommonServices _services; - private readonly Lazy _widgetProvider; - private readonly Lazy _profilerSettings; - private readonly Lazy _mobileDeviceHelper; - - public ProfilerFilter( - ICommonServices services, - Lazy widgetProvider, - Lazy profilerSettings, - Lazy mobileDeviceHelper) - { - _services = services; - _widgetProvider = widgetProvider; - _profilerSettings = profilerSettings; - _mobileDeviceHelper = mobileDeviceHelper; - } - - public void OnActionExecuting(ActionExecutingContext filterContext) - { - if (!_profilerSettings.Value.EnableMiniProfilerInPublicStore) - return; - - var tokens = filterContext.RouteData.DataTokens; - string area = tokens.ContainsKey("area") && !string.IsNullOrEmpty(tokens["area"].ToString()) ? - string.Concat(tokens["area"], ".") : - string.Empty; - - string controller = string.Concat(filterContext.Controller.ToString().Split('.').Last(), "."); - string action = filterContext.ActionDescriptor.ActionName; - - _services.Chronometer.StepStart("ActionFilter", "Action: " + area + controller + action); - } - - public void OnActionExecuted(ActionExecutedContext filterContext) - { - if (!_profilerSettings.Value.EnableMiniProfilerInPublicStore) - return; - - if (!filterContext.Result.IsHtmlViewResult()) - _services.Chronometer.StepStop("ActionFilter"); - } - - public void OnResultExecuting(ResultExecutingContext filterContext) - { - if (!_profilerSettings.Value.EnableMiniProfilerInPublicStore) - return; - - // should only run on a full view rendering result - if (!filterContext.Result.IsHtmlViewResult()) - return; - - if (!this.ShouldProfile(filterContext.HttpContext)) - return; - - var viewResult = filterContext.Result as ViewResultBase; - - string viewName = viewResult?.ViewName; - if (viewName.IsEmpty()) - { - string action = (filterContext.RouteData.Values["action"] as string).EmptyNull(); - viewName = action; - } - - _services.Chronometer.StepStart("ResultFilter", string.Format("{0}: {1}", viewResult is PartialViewResult ? "Partial" : "View", viewName)); - - if (!filterContext.IsChildAction) - { - _widgetProvider.Value.RegisterAction( - "head_html_tag", - "MiniProfiler", - "DevTools", - new { area = "SmartStore.DevTools" }); - } - } - - public void OnResultExecuted(ResultExecutedContext filterContext) - { - if (!_profilerSettings.Value.EnableMiniProfilerInPublicStore) - return; - - // should only run on a full view rendering result - if (!filterContext.Result.IsHtmlViewResult()) - return; - - if (!this.ShouldProfile(filterContext.HttpContext)) - return; - - _services.Chronometer.StepStop("ResultFilter"); - _services.Chronometer.StepStop("ActionFilter"); - } - - private bool ShouldProfile(HttpContextBase ctx) - { - if (_mobileDeviceHelper.Value.IsMobileDevice()) - return false; - - if (!_services.WorkContext.CurrentCustomer.IsAdmin()) - return ctx.Request.IsLocal; - - return true; - } - } + public class ProfilerFilter : IActionFilter, IResultFilter + { + private readonly ICommonServices _services; + private readonly Lazy _widgetProvider; + private readonly Lazy _profilerSettings; + private readonly Lazy _mobileDeviceHelper; + + public ProfilerFilter( + ICommonServices services, + Lazy widgetProvider, + Lazy profilerSettings, + Lazy mobileDeviceHelper) + { + _services = services; + _widgetProvider = widgetProvider; + _profilerSettings = profilerSettings; + _mobileDeviceHelper = mobileDeviceHelper; + } + + public void OnActionExecuting(ActionExecutingContext filterContext) + { + if (!_profilerSettings.Value.EnableMiniProfilerInPublicStore) + return; + + var tokens = filterContext.RouteData.DataTokens; + string area = tokens.ContainsKey("area") && !string.IsNullOrEmpty(tokens["area"].ToString()) ? + string.Concat(tokens["area"], ".") : + string.Empty; + + string controller = string.Concat(filterContext.Controller.ToString().Split('.').Last(), "."); + string action = filterContext.ActionDescriptor.ActionName; + + _services.Chronometer.StepStart("ActionFilter", "Action: " + area + controller + action); + } + + public void OnActionExecuted(ActionExecutedContext filterContext) + { + if (!_profilerSettings.Value.EnableMiniProfilerInPublicStore) + return; + + if (!filterContext.Result.IsHtmlViewResult()) + _services.Chronometer.StepStop("ActionFilter"); + } + + public void OnResultExecuting(ResultExecutingContext filterContext) + { + if (!_profilerSettings.Value.EnableMiniProfilerInPublicStore) + return; + + // should only run on a full view rendering result + if (!filterContext.Result.IsHtmlViewResult()) + return; + + if (!this.ShouldProfile(filterContext.HttpContext)) + return; + + var viewResult = filterContext.Result as ViewResultBase; + + string viewName = viewResult?.ViewName; + if (viewName.IsEmpty()) + { + string action = (filterContext.RouteData.Values["action"] as string).EmptyNull(); + viewName = action; + } + + _services.Chronometer.StepStart("ResultFilter", string.Format("{0}: {1}", viewResult is PartialViewResult ? "Partial" : "View", viewName)); + + if (!filterContext.IsChildAction) + { + _widgetProvider.Value.RegisterAction( + "head_html_tag", + "MiniProfiler", + "DevTools", + new { area = "SmartStore.DevTools" }); + } + } + + public void OnResultExecuted(ResultExecutedContext filterContext) + { + if (!_profilerSettings.Value.EnableMiniProfilerInPublicStore) + return; + + // should only run on a full view rendering result + if (!filterContext.Result.IsHtmlViewResult()) + return; + + if (!this.ShouldProfile(filterContext.HttpContext)) + return; + + _services.Chronometer.StepStop("ResultFilter"); + _services.Chronometer.StepStop("ActionFilter"); + } + + private bool ShouldProfile(HttpContextBase ctx) + { + if (_mobileDeviceHelper.Value.IsMobileDevice()) + return false; + + if (!_services.WorkContext.CurrentCustomer.IsAdmin()) + return ctx.Request.IsLocal; + + return true; + } + } } diff --git a/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleActionFilter.cs b/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleActionFilter.cs index 3c010c46fd..624f28253b 100644 --- a/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleActionFilter.cs +++ b/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleActionFilter.cs @@ -1,35 +1,28 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Web; +using System.Diagnostics; using System.Web.Mvc; using SmartStore.Core.Logging; -using SmartStore.Core.Localization; namespace SmartStore.DevTools.Filters { - public class SampleActionFilter : IActionFilter - { - private readonly INotifier _notifier; + public class SampleActionFilter : IActionFilter + { + private readonly INotifier _notifier; - public SampleActionFilter(INotifier notifier) - { - this._notifier = notifier; - } - - public void OnActionExecuting(ActionExecutingContext filterContext) - { - Debug.WriteLine("Executing: {0} - {1}".FormatInvariant(filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, filterContext.ActionDescriptor.ActionName)); - _notifier.Information("Yeah, my plugin action filter works. NICE!"); - // Do something meaningful here ;-) - } - - public void OnActionExecuted(ActionExecutedContext filterContext) - { - Debug.WriteLine("Executed: {0} - {1}".FormatInvariant(filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, filterContext.ActionDescriptor.ActionName)); - } - } + public SampleActionFilter(INotifier notifier) + { + this._notifier = notifier; + } + + public void OnActionExecuting(ActionExecutingContext filterContext) + { + Debug.WriteLine("Executing: {0} - {1}".FormatInvariant(filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, filterContext.ActionDescriptor.ActionName)); + _notifier.Information("Yeah, my plugin action filter works. NICE!"); + // Do something meaningful here ;-) + } + + public void OnActionExecuted(ActionExecutedContext filterContext) + { + Debug.WriteLine("Executed: {0} - {1}".FormatInvariant(filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, filterContext.ActionDescriptor.ActionName)); + } + } } diff --git a/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleCheckoutFilter.cs b/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleCheckoutFilter.cs index 59f4856a07..b2ff97632d 100644 --- a/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleCheckoutFilter.cs +++ b/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleCheckoutFilter.cs @@ -1,35 +1,26 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Web; +using System.Web.Mvc; using System.Web.Routing; -using System.Web.Mvc; using SmartStore.Core.Logging; -using SmartStore.Core.Localization; -using SmartStore.Utilities; namespace SmartStore.DevTools.Filters { - public class SampleCheckoutFilter : IActionFilter - { - private readonly INotifier _notifier; + public class SampleCheckoutFilter : IActionFilter + { + private readonly INotifier _notifier; - public SampleCheckoutFilter(INotifier notifier) - { - this._notifier = notifier; - } - - public void OnActionExecuting(ActionExecutingContext filterContext) - { - } - - public void OnActionExecuted(ActionExecutedContext filterContext) - { - var routeValues = new { action = "MyBillingAddress" }; - filterContext.Result = new RedirectToRouteResult("Developer.DevTools.MyCheckout", new RouteValueDictionary(routeValues)); - } - } + public SampleCheckoutFilter(INotifier notifier) + { + this._notifier = notifier; + } + + public void OnActionExecuting(ActionExecutingContext filterContext) + { + } + + public void OnActionExecuted(ActionExecutedContext filterContext) + { + var routeValues = new { action = "MyBillingAddress" }; + filterContext.Result = new RedirectToRouteResult("Developer.DevTools.MyCheckout", new RouteValueDictionary(routeValues)); + } + } } diff --git a/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleProductDetailActionFilter.cs b/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleProductDetailActionFilter.cs index fb4307fc1a..185f65234e 100644 --- a/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleProductDetailActionFilter.cs +++ b/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleProductDetailActionFilter.cs @@ -1,36 +1,27 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Web; -using System.Web.Mvc; -using SmartStore.Core.Logging; -using SmartStore.Core.Localization; -using SmartStore.Web.Models.Catalog; +using System.Web.Mvc; using SmartStore.Services; +using SmartStore.Web.Models.Catalog; namespace SmartStore.DevTools.Filters { - public class SampleProductDetailActionFilter : IActionFilter - { - private readonly ICommonServices _services; + public class SampleProductDetailActionFilter : IActionFilter + { + private readonly ICommonServices _services; private readonly UrlHelper _urlHelper; public SampleProductDetailActionFilter(ICommonServices services, UrlHelper urlHelper) - { + { _services = services; _urlHelper = urlHelper; } - - public void OnActionExecuting(ActionExecutingContext filterContext) - { - - } - - public void OnActionExecuted(ActionExecutedContext filterContext) - { + + public void OnActionExecuting(ActionExecutingContext filterContext) + { + + } + + public void OnActionExecuted(ActionExecutedContext filterContext) + { var result = filterContext.Result as ViewResultBase; if (result == null) { @@ -59,5 +50,5 @@ public void OnActionExecuted(ActionExecutedContext filterContext) Href = "https://www.smartstore.com" }; ; } - } + } } diff --git a/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleResultFilter.cs b/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleResultFilter.cs index 941ef81a1a..d4d0c9fee8 100644 --- a/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleResultFilter.cs +++ b/src/Plugins/SmartStore.DevTools/Filters/Samples/SampleResultFilter.cs @@ -1,39 +1,33 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Web; +using System.Diagnostics; using System.Web.Mvc; using SmartStore.Web.Framework.Modelling; namespace SmartStore.DevTools.Filters { - public class SampleResultFilter : IResultFilter - { - public void OnResultExecuting(ResultExecutingContext filterContext) - { - Debug.WriteLine("OnResultExecuting"); + public class SampleResultFilter : IResultFilter + { + public void OnResultExecuting(ResultExecutingContext filterContext) + { + Debug.WriteLine("OnResultExecuting"); - var result = filterContext.Result as ViewResultBase; - if (result != null) - { - var model = result.Model as ModelBase; - if (model != null) - { - // Do something with model here! - // If you want to use view models from SmartStore.Web - // or SmartStore.Admin make sure you have added - // the project reference first. - } - } - } - - public void OnResultExecuted(ResultExecutedContext filterContext) - { - Debug.WriteLine("OnResultExecuted"); - } + var result = filterContext.Result as ViewResultBase; + if (result != null) + { + var model = result.Model as ModelBase; + if (model != null) + { + // Do something with model here! + // If you want to use view models from SmartStore.Web + // or SmartStore.Admin make sure you have added + // the project reference first. + } + } + } - } + public void OnResultExecuted(ResultExecutedContext filterContext) + { + Debug.WriteLine("OnResultExecuted"); + } + + } } diff --git a/src/Plugins/SmartStore.DevTools/Filters/WidgetZoneFilter.cs b/src/Plugins/SmartStore.DevTools/Filters/WidgetZoneFilter.cs index 92b04c94b6..498341f28e 100644 --- a/src/Plugins/SmartStore.DevTools/Filters/WidgetZoneFilter.cs +++ b/src/Plugins/SmartStore.DevTools/Filters/WidgetZoneFilter.cs @@ -1,121 +1,115 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Web; using System.Web.Mvc; -using SmartStore.Core.Logging; -using SmartStore.Core.Localization; -using SmartStore.DevTools.Services; -using SmartStore.Core; +using SmartStore.Core.Caching; using SmartStore.Services; using SmartStore.Services.Customers; -using SmartStore.Web.Framework.UI; using SmartStore.Utilities; -using System.IO; -using SmartStore.Core.Caching; +using SmartStore.Web.Framework.UI; namespace SmartStore.DevTools.Filters { - public class WidgetZoneFilter : IActionFilter, IResultFilter - { - private readonly ICommonServices _services; - private readonly Lazy _widgetProvider; - private readonly ProfilerSettings _profilerSettings; - private readonly IDisplayControl _displayControl; - - public WidgetZoneFilter( - ICommonServices services, - Lazy widgetProvider, - ProfilerSettings profilerSettings, - IDisplayControl displayControl) - { - _services = services; - _widgetProvider = widgetProvider; - _profilerSettings = profilerSettings; - _displayControl = displayControl; - } - - public void OnActionExecuting(ActionExecutingContext filterContext) - { - if (filterContext.IsChildAction) - return; - - if (!_profilerSettings.DisplayWidgetZones) - return; - - _displayControl.MarkRequestAsUncacheable(); - } - - public void OnActionExecuted(ActionExecutedContext filterContext) - { - } - - public void OnResultExecuting(ResultExecutingContext filterContext) - { + public class WidgetZoneFilter : IActionFilter, IResultFilter + { + private readonly ICommonServices _services; + private readonly Lazy _widgetProvider; + private readonly ProfilerSettings _profilerSettings; + private readonly IDisplayControl _displayControl; + + public WidgetZoneFilter( + ICommonServices services, + Lazy widgetProvider, + ProfilerSettings profilerSettings, + IDisplayControl displayControl) + { + _services = services; + _widgetProvider = widgetProvider; + _profilerSettings = profilerSettings; + _displayControl = displayControl; + } + + public void OnActionExecuting(ActionExecutingContext filterContext) + { + if (filterContext.IsChildAction) + return; + + if (!_profilerSettings.DisplayWidgetZones) + return; + + _displayControl.MarkRequestAsUncacheable(); + } + + public void OnActionExecuted(ActionExecutedContext filterContext) + { + } + + public void OnResultExecuting(ResultExecutingContext filterContext) + { if (!_profilerSettings.DisplayWidgetZones) return; - // should only run on a full view rendering result - var result = filterContext.Result as ViewResultBase; - if (result == null) - { - return; - } - - if (!this.ShouldRender(filterContext.HttpContext)) - { - return; - } - - if (!filterContext.IsChildAction) - { - _widgetProvider.Value.RegisterAction( - new Wildcard("*"), + // should only run on a full view rendering result + var result = filterContext.Result as ViewResultBase; + if (result == null) + { + return; + } + + if (!this.ShouldRender(filterContext.HttpContext)) + { + return; + } + + if (!filterContext.IsChildAction) + { + _widgetProvider.Value.RegisterAction( + new Wildcard("*"), "WidgetZone", - "DevTools", - new { area = "SmartStore.DevTools" }); - } - - var viewName = result.ViewName; + "DevTools", + new { area = "SmartStore.DevTools" }); + } + + var viewName = result.ViewName; - if (viewName.IsEmpty()) - { - string action = (filterContext.RouteData.Values["action"] as string).EmptyNull(); - viewName = action; + if (viewName.IsEmpty()) + { + string action = (filterContext.RouteData.Values["action"] as string).EmptyNull(); + viewName = action; if (action == "WidgetsByZone") { var model = result.Model as WidgetZoneModel; - + filterContext.Result = new ViewResult { ViewName = "~/Plugins/SmartStore.DevTools/Views/DevTools/WidgetZone.cshtml", }; - if(filterContext.RouteData.Values["widgetZone"] == null) + if (filterContext.RouteData.Values["widgetZone"] == null) { filterContext.RouteData.Values.Add("widgetZone", model.WidgetZone); } } - } - } - - public void OnResultExecuted(ResultExecutedContext filterContext) - { - } - - private bool ShouldRender(HttpContextBase ctx) - { - if (!_services.WorkContext.CurrentCustomer.IsAdmin()) - { - return ctx.Request.IsLocal; - } + } + } + + public void OnResultExecuted(ResultExecutedContext filterContext) + { + } + + private bool ShouldRender(HttpContextBase ctx) + { + if (!_services.WorkContext.CurrentCustomer.IsAdmin()) + { + if (ctx.Request.Url.AbsolutePath == "/common/pdfreceiptfooter" || ctx.Request.Url.AbsolutePath == "/common/pdfreceiptheader") + { + return false; + } + return ctx.Request.IsLocal; + } - return true; - } + return true; + } - } + } } diff --git a/src/Plugins/SmartStore.DevTools/Localization/resources.de-de.xml b/src/Plugins/SmartStore.DevTools/Localization/resources.de-de.xml index 67cf6df4a5..10fb461867 100644 --- a/src/Plugins/SmartStore.DevTools/Localization/resources.de-de.xml +++ b/src/Plugins/SmartStore.DevTools/Localization/resources.de-de.xml @@ -1,24 +1,33 @@  - - Dev-Tools Demo Widget - - - - Mini-Profiler für Frontend aktivieren - - - Aktiviert den Mini-Profiler für das Frontend. Mit dem Profiler können Sie die Requests einer Seite, inklusive Datenbankzugriffen analysieren. Die Anzeige ist auf angemeldete Administratoren beschränkt. - - - Widget-Zonen darstellen - - - Injiziert Tooltips in jede Widget-Zone, die den Namen der jeweiligen Widget-Zone enthalten. Die Anzeige ist auf angemeldete Administratoren beschränkt. - - - Rechnernamen anzeigen - - - Stellt den Rechnernamen unterhalb des Footers dar. Mit dieser Funktion können Sie in einer verteilten Anwendung den gerade genutzten Rechner ermitteln. - + + Developer Tools + + + Smartstore Developer Tools (MiniProfiler und andere Extras). + + + Developer Tools Demo Widget + + + Developer Tools + + + + Mini-Profiler für Frontend aktivieren + + + Aktiviert den Mini-Profiler für das Frontend. Mit dem Profiler können Sie die Requests einer Seite, inklusive Datenbankzugriffen analysieren. Die Anzeige ist auf angemeldete Administratoren beschränkt. + + + Widget-Zonen darstellen + + + Injiziert Tooltips in jede Widget-Zone, die den Namen der jeweiligen Widget-Zone enthalten. Die Anzeige ist auf angemeldete Administratoren beschränkt. + + + Rechnernamen anzeigen + + + Stellt den Rechnernamen unterhalb des Footers dar. Mit dieser Funktion können Sie in einer verteilten Anwendung den gerade genutzten Rechner ermitteln. + \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/Localization/resources.en-us.xml b/src/Plugins/SmartStore.DevTools/Localization/resources.en-us.xml index 0ed80f9443..8d519b9476 100644 --- a/src/Plugins/SmartStore.DevTools/Localization/resources.en-us.xml +++ b/src/Plugins/SmartStore.DevTools/Localization/resources.en-us.xml @@ -1,24 +1,33 @@  - - Dev-Tools Demo Widget - - - - Enable Mini-Profiler in public store - - - Activates the Mini-Profiler for the Frontend. With its help you can analyse requests of a page, including database requests. The display of the profiler is limited to administrators. - - - Display widget zones - - - Injects tooltips into every widget zone, which includes the name of the current widget zone. The display of these tooltips is limited to admintrators. - - - Display machine name - - - Displays the current machine name below the footer. With this function you can determine the current machine within a distributed application. - + + Developer Tools + + + Smartstore Developer Tools (MiniProfiler and other goodies). + + + Developer Tools Demo Widget + + + Developer Tools + + + + Enable Mini-Profiler in public store + + + Activates the Mini-Profiler for the Frontend. With its help you can analyse requests of a page, including database requests. The display of the profiler is limited to administrators. + + + Display widget zones + + + Injects tooltips into every widget zone, which includes the name of the current widget zone. The display of these tooltips is limited to admintrators. + + + Display machine name + + + Displays the current machine name below the footer. With this function you can determine the current machine within a distributed application. + \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/Models/BackendExtensionModel.cs b/src/Plugins/SmartStore.DevTools/Models/BackendExtensionModel.cs index 7de7bbe5e1..58f8d5edbf 100644 --- a/src/Plugins/SmartStore.DevTools/Models/BackendExtensionModel.cs +++ b/src/Plugins/SmartStore.DevTools/Models/BackendExtensionModel.cs @@ -2,10 +2,10 @@ namespace SmartStore.DevTools.Models { - public class BackendExtensionModel : ModelBase - { - public string Welcome { get; set; } - + [CustomModelPart] + public class BackendExtensionModel : ModelBase + { + public string Welcome { get; set; } public int ProductId { get; set; } } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/Models/ConfigurationModel.cs b/src/Plugins/SmartStore.DevTools/Models/ConfigurationModel.cs new file mode 100644 index 0000000000..bf8b33d90b --- /dev/null +++ b/src/Plugins/SmartStore.DevTools/Models/ConfigurationModel.cs @@ -0,0 +1,17 @@ +using SmartStore.Web.Framework; +using SmartStore.Web.Framework.Modelling; + +namespace SmartStore.DevTools.Models +{ + public class ConfigurationModel : ModelBase + { + [SmartResourceDisplayName("Plugins.Developer.DevTools.EnableMiniProfilerInPublicStore")] + public bool EnableMiniProfilerInPublicStore { get; set; } + + [SmartResourceDisplayName("Plugins.Developer.DevTools.DisplayWidgetZones")] + public bool DisplayWidgetZones { get; set; } + + [SmartResourceDisplayName("Plugins.Developer.DevTools.DisplayMachineName")] + public bool DisplayMachineName { get; set; } + } +} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/ProfilerHttpModule.cs b/src/Plugins/SmartStore.DevTools/ProfilerHttpModule.cs index bf1ac188cf..9cb991484f 100644 --- a/src/Plugins/SmartStore.DevTools/ProfilerHttpModule.cs +++ b/src/Plugins/SmartStore.DevTools/ProfilerHttpModule.cs @@ -6,88 +6,88 @@ namespace SmartStore.DevTools { - public class ProfilerHttpModule : IHttpModule - { - private const string MP_KEY = "sm.miniprofiler.started"; - - public void Init(HttpApplication context) - { - if (DevToolsPlugin.HasPendingMigrations()) - { - return; - } + public class ProfilerHttpModule : IHttpModule + { + private const string MP_KEY = "sm.miniprofiler.started"; - context.AcquireRequestState += OnAcquireRequestState; - context.EndRequest += OnEndRequest; - } + public void Init(HttpApplication context) + { + if (DevToolsPlugin.HasPendingMigrations()) + { + return; + } - private static void OnAcquireRequestState(object sender, EventArgs e) - { - var app = (HttpApplication)sender; - if (!MiniProfilerStarted(app) && ShouldProfile(app)) - { - MiniProfiler.Start(); - if (app.Context != null && app.Context.Items != null) - { - app.Context.Items[MP_KEY] = true; - } - } - } + context.AcquireRequestState += OnAcquireRequestState; + context.EndRequest += OnEndRequest; + } - private static void OnEndRequest(object sender, EventArgs e) - { - var app = (HttpApplication)sender; - if (MiniProfilerStarted(app)) - { - MiniProfiler.Stop(); - } - } + private static void OnAcquireRequestState(object sender, EventArgs e) + { + var app = (HttpApplication)sender; + if (!MiniProfilerStarted(app) && ShouldProfile(app)) + { + MiniProfiler.StartNew(); + if (app.Context != null && app.Context.Items != null) + { + app.Context.Items[MP_KEY] = true; + } + } + } - private static bool MiniProfilerStarted(HttpApplication app) - { - return app?.Context?.Items != null && app.Context.Items.Contains(MP_KEY); - } + private static void OnEndRequest(object sender, EventArgs e) + { + var app = (HttpApplication)sender; + if (MiniProfilerStarted(app)) + { + MiniProfiler.Current?.Stop(); + } + } - private static bool ShouldProfile(HttpApplication app) - { - if (app?.Context?.Request == null) - return false; + internal static bool MiniProfilerStarted(HttpApplication app) + { + return app?.Context?.Items != null && app.Context.Items.Contains(MP_KEY); + } - if (!DataSettings.DatabaseIsInstalled()) - { - return false; - } + private static bool ShouldProfile(HttpApplication app) + { + if (app?.Context?.Request == null) + return false; - var url = app.Context.Request.AppRelativeCurrentExecutionFilePath; - if (url.StartsWith("~/admin", StringComparison.InvariantCultureIgnoreCase) - || url.StartsWith("~/mini-profiler", StringComparison.InvariantCultureIgnoreCase) - || url.StartsWith("~/bundles", StringComparison.InvariantCultureIgnoreCase) - || url.StartsWith("~/plugin/", StringComparison.InvariantCultureIgnoreCase) - || url.StartsWith("~/taskscheduler", StringComparison.InvariantCultureIgnoreCase)) - { - return false; - } + if (!DataSettings.DatabaseIsInstalled()) + { + return false; + } - ProfilerSettings settings = null; + var url = app.Context.Request.AppRelativeCurrentExecutionFilePath; + if (url.StartsWith("~/admin", StringComparison.InvariantCultureIgnoreCase) + || url.StartsWith("~/mini-profiler", StringComparison.InvariantCultureIgnoreCase) + || url.StartsWith("~/bundles", StringComparison.InvariantCultureIgnoreCase) + || url.StartsWith("~/plugin/", StringComparison.InvariantCultureIgnoreCase) + || url.StartsWith("~/taskscheduler", StringComparison.InvariantCultureIgnoreCase)) + { + return false; + } - if (EngineContext.Current.IsFullyInitialized) - { - try - { - settings = EngineContext.Current.Resolve(); - } - catch - { - return true; - } - } + ProfilerSettings settings = null; - return settings == null ? true : settings.EnableMiniProfilerInPublicStore; - } + if (EngineContext.Current.IsFullyInitialized) + { + try + { + settings = EngineContext.Current.Resolve(); + } + catch + { + return true; + } + } - public void Dispose() - { - // nothing to dispose - } - } + return settings == null ? true : settings.EnableMiniProfilerInPublicStore; + } + + public void Dispose() + { + // nothing to dispose + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/RouteProvider.cs b/src/Plugins/SmartStore.DevTools/RouteProvider.cs index 607fff468d..1d35543960 100644 --- a/src/Plugins/SmartStore.DevTools/RouteProvider.cs +++ b/src/Plugins/SmartStore.DevTools/RouteProvider.cs @@ -4,32 +4,26 @@ namespace SmartStore.DevTools { - - public class RouteProvider : IRouteProvider + + public class RouteProvider : IRouteProvider { public void RegisterRoutes(RouteCollection routes) { - routes.MapRoute("SmartStore.DevTools", - "Plugin/SmartStore.DevTools/{action}/{id}", - new { controller = "DevTools", action = "Configure", id = UrlParameter.Optional }, - new[] { "SmartStore.DevTools.Controllers" } - ) - .DataTokens["area"] = "SmartStore.DevTools"; + routes.MapRoute("SmartStore.DevTools", + "Plugin/SmartStore.DevTools/{action}/{id}", + new { controller = "DevTools", action = "Configure", id = UrlParameter.Optional }, + new[] { "SmartStore.DevTools.Controllers" } + ) + .DataTokens["area"] = "SmartStore.DevTools"; - //routes.MapRoute("SmartStore.DevTools.MyCheckout", - // "MyCheckout/{action}", - // new { controller = "MyCheckout", action = "MyBillingAddress" }, - // new[] { "SmartStore.DevTools.Controllers" } - //) - //.DataTokens["area"] = "SmartStore.DevTools"; - } - public int Priority - { - get - { - return 0; - } + //routes.MapRoute("SmartStore.DevTools.MyCheckout", + // "MyCheckout/{action}", + // new { controller = "MyCheckout", action = "MyBillingAddress" }, + // new[] { "SmartStore.DevTools.Controllers" } + //) + //.DataTokens["area"] = "SmartStore.DevTools"; } + public int Priority => 0; } } diff --git a/src/Plugins/SmartStore.DevTools/Security/DevToolsPermissionProvider.cs b/src/Plugins/SmartStore.DevTools/Security/DevToolsPermissionProvider.cs new file mode 100644 index 0000000000..d84a20d7a6 --- /dev/null +++ b/src/Plugins/SmartStore.DevTools/Security/DevToolsPermissionProvider.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.Linq; +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.Security; +using SmartStore.Core.Security; + +namespace SmartStore.DevTools.Security +{ + /// + /// All permissions provided by this plugin. Recommended to use singular for names, . + /// "devtools" is the root permission (by convention, doesn't contain any dot). Localization key is "Plugins.Permissions.DisplayName.DevTools". + /// "devtools.read" and "devtools.update" do not need localization because they are contained in core, . + /// + public static class DevToolsPermissions + { + public const string Self = "devtools"; + public const string Read = "devtools.read"; + public const string Update = "devtools.update"; + } + + + public class DevToolsPermissionProvider : IPermissionProvider + { + public IEnumerable GetPermissions() + { + // Get all permissions from above static class. + var permissionSystemNames = PermissionHelper.GetPermissions(typeof(DevToolsPermissions)); + var permissions = permissionSystemNames.Select(x => new PermissionRecord { SystemName = x }); + + return permissions; + } + + public IEnumerable GetDefaultPermissions() + { + // Allow root permission for admin by default. + return new[] + { + new DefaultPermissionRecord + { + CustomerRoleSystemName = SystemCustomerRoleNames.Administrators, + PermissionRecords = new[] + { + new PermissionRecord { SystemName = DevToolsPermissions.Self } + } + } + }; + } + } +} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/Services/CustomFacetTemplateSelector.cs b/src/Plugins/SmartStore.DevTools/Services/CustomFacetTemplateSelector.cs new file mode 100644 index 0000000000..7b5265df8f --- /dev/null +++ b/src/Plugins/SmartStore.DevTools/Services/CustomFacetTemplateSelector.cs @@ -0,0 +1,60 @@ +using System.Web.Routing; +using SmartStore.Core; +using SmartStore.Core.Search.Facets; +using SmartStore.Services.Customers; +using SmartStore.Services.Search.Rendering; + +namespace SmartStore.DevTools.Services +{ + public class CustomFacetTemplateSelector : IFacetTemplateSelector + { + private readonly IWorkContext _workContext; + + public CustomFacetTemplateSelector(IWorkContext workContext) + { + _workContext = workContext; + } + + // Order in case of multiple implementations (like MegaSearchPlus). + public int Ordinal => 99; + + public RouteInfo GetTemplateRoute(FacetGroup facetGroup) + { + // Provide template for catalog only. + if (facetGroup.Scope != "Catalog") + { + return null; + } + + // Provide template for specifications attributes only. + if (facetGroup.Kind != FacetGroupKind.Attribute) + { + return null; + } + + // Provide template for admin only (because this is a developer demo). + if (!_workContext.CurrentCustomer.IsAdmin()) + { + return null; + } + + // TODO: filter by what your template is made for. + // E.g. merchant configures a specification attribute in your plugin, then you can filter by facetGroup.Key == "attrid" + + if (facetGroup.Label.IsCaseInsensitiveEqual("Dimensions") || + facetGroup.Label.IsCaseInsensitiveEqual("Maße")) + { + var routeValues = new RouteValueDictionary(new + { + area = "SmartStore.DevTools", + templateName = "MyCustomFacetTemplate", + facetGroup + }); + + return new RouteInfo("MyCustomFacetTemplate", "DevTools", routeValues); + } + + return null; + } + } +} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/Services/MiniProfilerChronometer.cs b/src/Plugins/SmartStore.DevTools/Services/MiniProfilerChronometer.cs index 153adf61cd..e9bd9e6787 100644 --- a/src/Plugins/SmartStore.DevTools/Services/MiniProfilerChronometer.cs +++ b/src/Plugins/SmartStore.DevTools/Services/MiniProfilerChronometer.cs @@ -1,73 +1,66 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using SmartStore.Core.Logging; using StackExchange.Profiling; namespace SmartStore.DevTools.Services { - public class MiniProfilerChronometer : IChronometer - { - private readonly Dictionary> _steps = new Dictionary>(); - private MiniProfiler _profiler; + public class MiniProfilerChronometer : IChronometer + { + private readonly Dictionary> _steps = new Dictionary>(); + private MiniProfiler _profiler; - public MiniProfilerChronometer() - { - _profiler = MiniProfiler.Current; - } + public MiniProfilerChronometer() + { + _profiler = MiniProfiler.Current; + } - protected MiniProfiler Profiler - { - get - { - return _profiler ?? (_profiler = MiniProfiler.Current); - } - } + protected MiniProfiler Profiler => _profiler ?? (_profiler = MiniProfiler.Current); - public void StepStart(string key, string message) - { - if (this.Profiler == null) - { - return; - } + public void StepStart(string key, string message) + { + if (this.Profiler == null) + { + return; + } - var stack = _steps.Get(key); - if (stack == null) - { - _steps[key] = stack = new Stack(); - } + var stack = _steps.Get(key); + if (stack == null) + { + _steps[key] = stack = new Stack(); + } - var step = Profiler.Step(message); - stack.Push(step); - } + var step = Profiler.Step(message); + stack.Push(step); + } - public void StepStop(string key) - { - if (_steps.ContainsKey(key) && _steps[key].Count > 0) - { - var step = _steps[key].Pop(); - step.Dispose(); - if (_steps[key].Count == 0) - { - _steps.Remove(key); - } - } - } + public void StepStop(string key) + { + if (_steps.ContainsKey(key) && _steps[key].Count > 0) + { + var step = _steps[key].Pop(); + step.Dispose(); + if (_steps[key].Count == 0) + { + _steps.Remove(key); + } + } + } - private void StopAll() - { - // Dispose any orphaned steps - foreach (var stack in _steps.Values) - { - stack.Each(x => x.Dispose()); - } + private void StopAll() + { + // Dispose any orphaned steps + foreach (var stack in _steps.Values) + { + stack.Each(x => x.Dispose()); + } - _steps.Clear(); - } + _steps.Clear(); + } - public void Dispose() - { - this.StopAll(); - } - } + public void Dispose() + { + this.StopAll(); + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/Settings/ProfilerSettings.cs b/src/Plugins/SmartStore.DevTools/Settings/ProfilerSettings.cs index 43d45d8d90..06e572338c 100644 --- a/src/Plugins/SmartStore.DevTools/Settings/ProfilerSettings.cs +++ b/src/Plugins/SmartStore.DevTools/Settings/ProfilerSettings.cs @@ -1,21 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using SmartStore.Core.Configuration; -using SmartStore.Web.Framework; +using SmartStore.Core.Configuration; namespace SmartStore.DevTools { - public class ProfilerSettings : ISettings - { - [SmartResourceDisplayName("Plugins.Developer.DevTools.EnableMiniProfilerInPublicStore")] + public class ProfilerSettings : ISettings + { public bool EnableMiniProfilerInPublicStore { get; set; } - - [SmartResourceDisplayName("Plugins.Developer.DevTools.DisplayWidgetZones")] public bool DisplayWidgetZones { get; set; } - - [SmartResourceDisplayName("Plugins.Developer.DevTools.DisplayMachineName")] public bool DisplayMachineName { get; set; } - } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/SmartStore.DevTools.csproj b/src/Plugins/SmartStore.DevTools/SmartStore.DevTools.csproj index 025ef8e5bf..4a2abff1a9 100644 --- a/src/Plugins/SmartStore.DevTools/SmartStore.DevTools.csproj +++ b/src/Plugins/SmartStore.DevTools/SmartStore.DevTools.csproj @@ -1,6 +1,5 @@  - @@ -21,7 +20,7 @@ Properties SmartStore.DevTools SmartStore.DevTools - v4.6.1 + v4.7.2 512 ..\..\ @@ -77,20 +76,20 @@ MinimumRecommendedRules.ruleset - - ..\..\packages\Autofac.4.9.0\lib\net45\Autofac.dll + + ..\..\packages\Autofac.5.2.0\lib\net461\Autofac.dll False - - ..\..\packages\Autofac.Mvc5.4.0.2\lib\net45\Autofac.Integration.Mvc.dll + + ..\..\packages\Autofac.Mvc5.5.0.0\lib\net461\Autofac.Integration.Mvc.dll False - ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + ..\..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll False - ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + ..\..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.SqlServer.dll False @@ -101,33 +100,72 @@ ..\..\packages\FluentValidation.Mvc5.7.4.0\lib\net45\FluentValidation.Mvc.dll False + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + False + + + ..\..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.6.0.0\lib\net461\Microsoft.Extensions.DependencyInjection.Abstractions.dll + False + ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll False - - ..\..\packages\MiniProfiler.3.2.0.157\lib\net40\MiniProfiler.dll + + ..\..\packages\MiniProfiler.4.2.1\lib\net461\MiniProfiler.dll True - - False - ..\..\packages\MiniProfiler.EF6.3.0.11\lib\net40\MiniProfiler.EntityFramework6.dll + + ..\..\packages\MiniProfiler.EF6.4.2.1\lib\net461\MiniProfiler.EF6.dll True - - ..\..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll - False + + ..\..\packages\MiniProfiler.Shared.4.2.1\lib\net461\MiniProfiler.Shared.dll + True + + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + False + + + + ..\..\packages\System.Diagnostics.DiagnosticSource.6.0.0\lib\net461\System.Diagnostics.DiagnosticSource.dll + False + + + ..\..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + False + + + + ..\..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + False + + + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + - + ..\..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.Helpers.dll False @@ -178,9 +216,12 @@ + + + @@ -258,6 +299,9 @@ PreserveNewest + + PreserveNewest + diff --git a/src/Plugins/SmartStore.DevTools/Starter.cs b/src/Plugins/SmartStore.DevTools/Starter.cs index 83f335c991..9a186299a8 100644 --- a/src/Plugins/SmartStore.DevTools/Starter.cs +++ b/src/Plugins/SmartStore.DevTools/Starter.cs @@ -1,70 +1,38 @@ -using Microsoft.Web.Infrastructure.DynamicModuleHelper; +using System; +using Microsoft.Web.Infrastructure.DynamicModuleHelper; using SmartStore.Core.Infrastructure; -using SmartStore.Core.Plugins; using SmartStore.Web.Framework; using StackExchange.Profiling; using StackExchange.Profiling.Storage; -using System; -using System.Collections.Generic; namespace SmartStore.DevTools { - public class ProfilerPreApplicationStart : IPreApplicationStart - { - public void Start() - { - DynamicModuleUtility.RegisterModule(typeof(ProfilerHttpModule)); - SmartUrlRoutingModule.RegisterRoutablePath("/mini-profiler-resources/(.*)"); - } - } - - public class ProfilerStartupTask : IStartupTask - { - public void Execute() - { - StackExchange.Profiling.MiniProfiler.Settings.MaxUnviewedProfiles = 5; - //StackExchange.Profiling.MiniProfiler.Settings.Results_List_Authorize = (req) => true; - //StackExchange.Profiling.MiniProfiler.Settings.Storage = new NullProfilerStorage(); - - StackExchange.Profiling.EntityFramework6.MiniProfilerEF6.Initialize(); - - // output cache invidation example - //OutputCacheInvalidationObserver.Execute(); - } - - public int Order - { - get { return int.MinValue; } - } - } - - internal class NullProfilerStorage : IStorage - { - public List GetUnviewedIds(string user) - { - return new List(); - } - - public IEnumerable List(int maxResults, DateTime? start = default(DateTime?), DateTime? finish = default(DateTime?), ListResultsOrder orderBy = ListResultsOrder.Descending) - { - return new List(); - } - - public MiniProfiler Load(Guid id) - { - return null; - } - - public void Save(MiniProfiler profiler) - { - } - - public void SetUnviewed(string user, Guid id) - { - } - - public void SetViewed(string user, Guid id) - { - } - } + public class ProfilerPreApplicationStart : IPreApplicationStart + { + public void Start() + { + DynamicModuleUtility.RegisterModule(typeof(ProfilerHttpModule)); + SmartUrlRoutingModule.RegisterRoutablePath("/mini-profiler-resources/(.*)"); + } + } + + public class ProfilerStartupTask : IApplicationStart + { + public void Start() + { + StackExchange.Profiling.MiniProfiler.Configure(new MiniProfilerOptions + { + MaxUnviewedProfiles = 5, + Storage = new MemoryCacheStorage(TimeSpan.FromMinutes(1)), + //ResultsAuthorize = req => true, + }); + + StackExchange.Profiling.EntityFramework6.MiniProfilerEF6.Initialize(); + + // output cache invidation example + //OutputCacheInvalidationObserver.Execute(); + } + + public int Order => int.MinValue; + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/Views/DevTools/BackendExtension.cshtml b/src/Plugins/SmartStore.DevTools/Views/DevTools/BackendExtension.cshtml index 0af50e879f..c325f2977c 100644 --- a/src/Plugins/SmartStore.DevTools/Views/DevTools/BackendExtension.cshtml +++ b/src/Plugins/SmartStore.DevTools/Views/DevTools/BackendExtension.cshtml @@ -3,13 +3,13 @@ @{ Layout = "_AdminLayout"; - ViewBag.Title = "My SmartStore.NET backend extension page"; + ViewBag.Title = "My Smartstore backend extension page"; }
              - My SmartStore.NET backend extension page + My Smartstore backend extension page
              diff --git a/src/Plugins/SmartStore.DevTools/Views/DevTools/Configure.cshtml b/src/Plugins/SmartStore.DevTools/Views/DevTools/Configure.cshtml index 856fe7df32..79603bcf48 100644 --- a/src/Plugins/SmartStore.DevTools/Views/DevTools/Configure.cshtml +++ b/src/Plugins/SmartStore.DevTools/Views/DevTools/Configure.cshtml @@ -1,16 +1,20 @@ -@model SmartStore.DevTools.ProfilerSettings +@model SmartStore.DevTools.Models.ConfigurationModel @using SmartStore.Web.Framework; +
              + +
              + @{ Html.RenderAction("StoreScopeConfiguration", "Setting", new { area = "Admin" }); } +@Html.ValidationSummary(false) @using (Html.BeginForm()) { -
              - -
              + @Html.AntiForgeryToken() + @@ -164,9 +164,8 @@
              diff --git a/src/Plugins/SmartStore.DevTools/Views/DevTools/MiniProfiler.cshtml b/src/Plugins/SmartStore.DevTools/Views/DevTools/MiniProfiler.cshtml index ba456f87eb..bf83d685e0 100644 --- a/src/Plugins/SmartStore.DevTools/Views/DevTools/MiniProfiler.cshtml +++ b/src/Plugins/SmartStore.DevTools/Views/DevTools/MiniProfiler.cshtml @@ -1,8 +1,10 @@ @model dynamic +@using StackExchange.Profiling + @if (base.ViewContext.IsBareBonePage()) { return; } -@StackExchange.Profiling.MiniProfiler.RenderIncludes(useExistingjQuery: true, showControls: true) \ No newline at end of file +@(StackExchange.Profiling.MiniProfiler.Current?.RenderIncludes(showControls: true)) \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/Views/DevTools/MyCustomFacetTemplate.cshtml b/src/Plugins/SmartStore.DevTools/Views/DevTools/MyCustomFacetTemplate.cshtml new file mode 100644 index 0000000000..632a142e51 --- /dev/null +++ b/src/Plugins/SmartStore.DevTools/Views/DevTools/MyCustomFacetTemplate.cshtml @@ -0,0 +1,20 @@ +@model FacetGroup + +@using SmartStore.Core.Search.Facets + +@{ + var facets = Model.Facets.Where(x => x.Published); + + //.... + + if (!facets.Any()) + { + return; + } +} + +
              +
              + Hello - my custom facet template for "@(Model.Label)"! +
              +
              \ No newline at end of file diff --git a/src/Plugins/SmartStore.DevTools/Web.config b/src/Plugins/SmartStore.DevTools/Web.config index 06a4967fb8..d631d4b983 100644 --- a/src/Plugins/SmartStore.DevTools/Web.config +++ b/src/Plugins/SmartStore.DevTools/Web.config @@ -4,6 +4,7 @@ + - + @@ -58,7 +59,7 @@ - + @@ -78,19 +79,15 @@ - + - - - - - + @@ -114,20 +111,16 @@ - + - + - - - - @@ -152,6 +145,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Plugins/SmartStore.DevTools/packages.config b/src/Plugins/SmartStore.DevTools/packages.config index d040144b1c..20dd1d3075 100644 --- a/src/Plugins/SmartStore.DevTools/packages.config +++ b/src/Plugins/SmartStore.DevTools/packages.config @@ -1,15 +1,25 @@  - - - + + + + + + - - - + + + + + + + + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Controllers/DiscountRulesController.cs b/src/Plugins/SmartStore.DiscountRules/Controllers/DiscountRulesController.cs deleted file mode 100644 index 7e6605a2f3..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Controllers/DiscountRulesController.cs +++ /dev/null @@ -1,352 +0,0 @@ -using System; -using System.Linq; -using System.Web.Mvc; -using SmartStore.Core.Domain.Discounts; -using SmartStore.Core.Localization; -using SmartStore.DiscountRules.Models; -using SmartStore.Services.Customers; -using SmartStore.Services.Directory; -using SmartStore.Services.Discounts; -using SmartStore.Services.Stores; -using SmartStore.Web.Framework.Controllers; -using SmartStore.Web.Framework.Security; - -namespace SmartStore.DiscountRules.Controllers -{ - - [AdminAuthorize] - public class DiscountRulesController : PluginControllerBase - { - private readonly IDiscountService _discountService; - private readonly Lazy _customerService; - private readonly Lazy _countryService; - private readonly Lazy _storeService; - - public DiscountRulesController( - IDiscountService discountService, - Lazy customerService, - Lazy countryService, - Lazy storeService) - { - this._discountService = discountService; - this._customerService = customerService; - this._countryService = countryService; - this._storeService = storeService; - } - - #region Global - - [NonAction] - private ActionResult HandleGet(int discountId, int? discountRequirementId, string prefix, Func modelCreator) where TModel : DiscountRuleModelBase - { - var discount = _discountService.GetDiscountById(discountId); - if (discount == null) - throw new ArgumentException(T("Plugins.SmartStore.DiscountRules.ConfigureDiscountNotLoaded")); - - DiscountRequirement discountRequirement = null; - if (discountRequirementId.HasValue) - { - discountRequirement = discount.DiscountRequirements.Where(dr => dr.Id == discountRequirementId.Value).FirstOrDefault(); - if (discountRequirement == null) - return Content(T("Plugins.SmartStore.DiscountRules.ConfigureRequirementFailed")); - } - - var model = modelCreator(discountRequirement); - model.RequirementId = discountRequirementId.GetValueOrDefault(); - model.DiscountId = discountId; - - //add a prefix - ViewData.TemplateInfo.HtmlFieldPrefix = string.Format("{0}{1}", prefix, discountRequirementId.GetValueOrDefault(0).ToString()); - - return View(model); - } - - [NonAction] - private ActionResult HandlePost(int discountId, int? discountRequirementId, Action updater, Func creator) where TModel : DiscountRuleModelBase - { - var discount = _discountService.GetDiscountById(discountId); - if (discount == null) - throw new ArgumentException(T("Plugins.SmartStore.DiscountRules.ConfigureDiscountNotLoaded")); - - DiscountRequirement discountRequirement = null; - if (discountRequirementId.HasValue) - discountRequirement = discount.DiscountRequirements.Where(dr => dr.Id == discountRequirementId.Value).FirstOrDefault(); - - if (discountRequirement != null) - { - // update existing rule - updater(discountRequirement); - _discountService.UpdateDiscount(discountRequirement.Discount); - } - else - { - // save new rule - discountRequirement = creator(); - discount.DiscountRequirements.Add(discountRequirement); - _discountService.UpdateDiscount(discount); - } - - return Json(new { Result = true, NewRequirementId = discountRequirement.Id }, JsonRequestBehavior.AllowGet); - } - - #endregion - - - #region Customer Role Is - - public ActionResult CustomerRole(int discountId, int? discountRequirementId) - { - return HandleGet(discountId, discountRequirementId, "DiscountRulesCustomerRoles", (req) => { - var model = new CustomerRoleModel(); - // countries - model.AvailableCustomerRoles.Add(new SelectListItem() { Text = T("Plugins.DiscountRequirement.MustBeAssignedToCustomerRole.ConfigureSelectCustomerRole"), Value = "0" }); - foreach (var cr in _customerService.Value.GetAllCustomerRoles(true)) - { - model.AvailableCustomerRoles.Add(new SelectListItem() { Text = cr.Name, Value = cr.Id.ToString(), Selected = req != null && cr.Id == req.RestrictedToCustomerRoleId }); - if (req != null && cr.Id == req.RestrictedToCustomerRoleId) - { - model.CustomerRoleId = cr.Id; - } - } - return model; - }); - } - - [HttpPost] - public ActionResult CustomerRole(int discountId, int? discountRequirementId, int customerRoleId) - { - return HandlePost( - discountId, - discountRequirementId, - req => req.RestrictedToCustomerRoleId = customerRoleId, - () => new DiscountRequirement - { - DiscountRequirementRuleSystemName = "DiscountRequirement.MustBeAssignedToCustomerRole", - RestrictedToCustomerRoleId = customerRoleId - } - ); - } - - #endregion - - - #region Billing Country Is - - public ActionResult BillingCountry(int discountId, int? discountRequirementId) - { - return HandleGet(discountId, discountRequirementId, "DiscountRulesBillingCountry", (req) => - { - var model = new BillingCountryModel(); - // countries - model.AvailableCountries.Add(new SelectListItem() { Text = T("Plugins.DiscountRules.BillingCountry.Fields.SelectCountry"), Value = "0" }); - foreach (var c in _countryService.Value.GetAllCountries(true)) - { - model.AvailableCountries.Add(new SelectListItem() { Text = c.Name, Value = c.Id.ToString(), Selected = req != null && c.Id == req.BillingCountryId }); - if (req != null && c.Id == req.BillingCountryId) - { - model.CountryId = c.Id; - } - } - return model; - }); - } - - [HttpPost] - public ActionResult BillingCountry(int discountId, int? discountRequirementId, int countryId) - { - return HandlePost( - discountId, - discountRequirementId, - req => req.BillingCountryId = countryId, - () => new DiscountRequirement - { - DiscountRequirementRuleSystemName = "DiscountRequirement.BillingCountryIs", - BillingCountryId = countryId, - } - ); - } - - #endregion - - - #region Shipping Country Is - - public ActionResult ShippingCountry(int discountId, int? discountRequirementId) - { - return HandleGet(discountId, discountRequirementId, "DiscountRulesShippingCountry", (req) => - { - var model = new ShippingCountryModel(); - //countries - model.AvailableCountries.Add(new SelectListItem() { Text = T("Plugins.DiscountRules.ShippingCountry.Fields.SelectCountry"), Value = "0" }); - foreach (var c in _countryService.Value.GetAllCountries(true)) - { - model.AvailableCountries.Add(new SelectListItem() { Text = c.Name, Value = c.Id.ToString(), Selected = req != null && c.Id == req.ShippingCountryId }); - if (req != null && c.Id == req.ShippingCountryId) - { - model.CountryId = c.Id; - } - } - return model; - }); - } - - [HttpPost] - public ActionResult ShippingCountry(int discountId, int? discountRequirementId, int countryId) - { - return HandlePost( - discountId, - discountRequirementId, - req => req.ShippingCountryId = countryId, - () => new DiscountRequirement - { - DiscountRequirementRuleSystemName = "DiscountRequirement.ShippingCountryIs", - ShippingCountryId = countryId - } - ); - } - - #endregion - - - #region Store - - public ActionResult Store(int discountId, int? discountRequirementId) - { - return HandleGet(discountId, discountRequirementId, "DiscountRulesStore", (req) => - { - var model = new StoreModel(); - - if (req != null) - model.StoreId = req.RestrictedToStoreId ?? 0; - - //stores - model.AvailableStores.Add(new SelectListItem() { Text = T("Plugins.DiscountRules.Store.Fields.SelectStore").Text, Value = "0" }); - foreach (var s in _storeService.Value.GetAllStores()) - model.AvailableStores.Add(new SelectListItem() { Text = s.Name, Value = s.Id.ToString(), Selected = req != null && s.Id == model.StoreId }); - return model; - }); - } - - [HttpPost] - public ActionResult Store(int discountId, int? discountRequirementId, int storeId) - { - return HandlePost( - discountId, - discountRequirementId, - req => req.RestrictedToStoreId = storeId, - () => new DiscountRequirement - { - DiscountRequirementRuleSystemName = "DiscountRequirement.Store", - RestrictedToStoreId = storeId, - } - ); - } - - #endregion - - - #region HasOneProduct - - public ActionResult HasOneProduct(int discountId, int? discountRequirementId) - { - return HandleGet(discountId, discountRequirementId, "DiscountRulesHasOneProduct", (req) => - { - var model = new HasOneProductModel(); - model.Products = req != null ? req.RestrictedProductIds : ""; - return model; - }); - } - - [HttpPost] - public ActionResult HasOneProduct(int discountId, int? discountRequirementId, string productIds) - { - return HandlePost( - discountId, - discountRequirementId, - req => req.RestrictedProductIds = productIds, - () => new DiscountRequirement - { - DiscountRequirementRuleSystemName = "DiscountRequirement.HasOneProduct", - RestrictedProductIds = productIds, - } - ); - } - - #endregion - - - #region HasAllProducts - - public ActionResult HasAllProducts(int discountId, int? discountRequirementId) - { - return HandleGet(discountId, discountRequirementId, "DiscountRulesHasAllProducts", (req) => - { - var model = new HasAllProductsModel(); - model.Products = req != null ? req.RestrictedProductIds : ""; - return model; - }); - } - - [HttpPost] - public ActionResult HasAllProducts(int discountId, int? discountRequirementId, string productIds) - { - return HandlePost( - discountId, - discountRequirementId, - req => req.RestrictedProductIds = productIds, - () => new DiscountRequirement - { - DiscountRequirementRuleSystemName = "DiscountRequirement.HasAllProducts", - RestrictedProductIds = productIds, - } - ); - } - - #endregion - - - #region HadSpentAmount - - public ActionResult HadSpentAmount(int discountId, int? discountRequirementId) - { - return HandleGet(discountId, discountRequirementId, "DiscountRulesHadSpentAmount", (req) => - { - // parse settings from ExtraData - string extraData = null; - if (req != null) - { - extraData = req.ExtraData; - } - var settings = HadSpentAmountRule.DeserializeSettings(extraData); - - var model = new HadSpentAmountModel(); - model.SpentAmount = req != null ? req.SpentAmount : decimal.Zero; - model.LimitToCurrentBasketSubTotal = settings.LimitToCurrentBasketSubTotal; - - return model; - }); - } - - [HttpPost] - public ActionResult HadSpentAmount(int discountId, int? discountRequirementId, decimal spentAmount, string settings) - { - return HandlePost( - discountId, - discountRequirementId, - req => - { - req.SpentAmount = spentAmount; - req.ExtraData = settings; - }, - () => new DiscountRequirement - { - DiscountRequirementRuleSystemName = "DiscountRequirement.HadSpentAmount", - ExtraData = settings, - SpentAmount = spentAmount - } - ); - } - - #endregion - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Description.txt b/src/Plugins/SmartStore.DiscountRules/Description.txt deleted file mode 100644 index b7b46e9263..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Description.txt +++ /dev/null @@ -1,9 +0,0 @@ -FriendlyName: Common discount requirement rules -Description: Contains common discount requirement rule providers like "Billing country is", "Customer role is", "Had spent amount" etc. -Group: Marketing -SystemName: SmartStore.DiscountRules -Version: 3.2.2 -MinAppVersion: 3.2.2 -DisplayOrder: 0 -FileName: SmartStore.DiscountRules.dll -ResourceRootKey: Plugins.SmartStore.DiscountRules diff --git a/src/Plugins/SmartStore.DiscountRules/Localization/resources.de-de.xml b/src/Plugins/SmartStore.DiscountRules/Localization/resources.de-de.xml deleted file mode 100644 index c4ce8f67f5..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Localization/resources.de-de.xml +++ /dev/null @@ -1,145 +0,0 @@ - - - - Standard Rabattregeln - - - Stellt Standard Rabattregeln zur Verfügung - u.A. "Land ist", "Kundengruppe ist", "Hat Betrag x ausgegeben" etc. - - - - Rabatt konnte nicht geladen werden - - - Anforderung konnte nicht geladen werden - - - - - Der Kunde muss einer Kundengruppe zugeordnet sein - - - - - Wählen Sie eine Kundengruppe - - - Benötigte Kundengruppe - - - Der Rabatt wird angewendet, wenn der Kunde zur gewählten Kundengruppe gehört. - - - - - - - Land der Rechnungsadresse ist - - - - - Wählen Sie ein Land - - - Land der Rechnungsadresse - - - Wählen Sie das Land, dass in der Rechnungsadresse vorausgesetzt wird. - - - - - - - - Land der Lieferadresse ist - - - - - Wählen Sie ein Land - - - Land der Lieferadresse - - - Wählen Sie das Land, dass in der Lieferadresse vorausgesetzt wird. - - - - - - - Auf bestimmten Shop begrenzt - - - - - Shop auswählen - - - Shop - - - Wählen Sie den Shop für diesen Rabatt. - - - - - - - - Kunde hat eins der folgenden Produkte in seinem Warenkorb - - - - - Produkte - - - Kommagetrennte Liste von Produkt-IDs (z.B. 77, 123, 156). Die ID eines Produkts findet sich in seiner Detailansicht. Man kann die Liste außerdem um Mengenangaben ({Produkt-ID}:{Menge} z.B.: 77:1, 123:2, 156:3) oder um einen Mengenbereich ergänzen ({Produkt-ID}:{Minimale Menge}-{Maximale Menge} z.B.: 77:1-3, 123:2-5, 156:3-8). - - - - - - - - Kunde hat folgende Produkte in seinem Warenkorb - - - - - Produkte - - - Kommagetrennte Liste von Produkt-IDs (z.B. 77, 123, 156). Die ID eines Produkts findet sich in seiner Detailansicht. Man kann die Liste außerdem um Mengenangaben ({Produkt-ID}:{Menge} z.B.: 77:1, 123:2, 156:3) oder um einen Mengenbereich ergänzen ({Produkt-ID}:{Minimale Menge}-{Maximale Menge} z.B.: 77:1-3, 123:2-5, 156:3-8). - - - - - - - - Kunde hat Betrag x ausgegeben - - - - - Betrag - - - Der Rabatt wird angewendet, wenn der Kunde den angegebenen Betrag ausgegeben hat. - - - Bezogen auf Warenkorb-Zwischensumme - - - Legt fest, ob sich der angegebene Betrag auf die Zwischensumme im aktuellen Warenkorb oder - wenn inaktiv - auf die Summe aller zuvor vom Kunden getätigten Bestellungen bezieht. - - - - - - \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Localization/resources.en-us.xml b/src/Plugins/SmartStore.DiscountRules/Localization/resources.en-us.xml deleted file mode 100644 index 59f4d39fe1..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Localization/resources.en-us.xml +++ /dev/null @@ -1,146 +0,0 @@ - - - - Standard discount rules - - - Provides standard discount rules, e.g. "Country is", "Customer group is", "Has amount x spent" etc. - - - - Discount could not be loaded - - - Failed to load requirement - - - - - The customer must be assigned to a customer group - - - - - Select a customer role - - - Required customer role - - - Discount will be applied if customer is in the selected customer role. - - - - - - - Country of invoice address is - - - - - Select country - - - Billing country - - - Select required billing country. - - - - - - - - Country of delivery address is - - - - - Select country - - - Shipping country - - - Select required shipping country. - - - - - - - - Limited to specific shop - - - - - Select store - - - Store - - - Select the store in which this discount will be valid. - - - - - - - - Customer has one of the following products in his shopping cart - - - - - Restricted products - - - The comma-separated list of product identifiers (e.g. 77, 123, 156). You can find a product ID on its details page. You can also specify the comma-separated list of product identifiers with quantities ({Product ID}:{Quantity}. for example, 77:1, 123:2, 156:3). And you can also specify the comma-separated list of product identifiers with quantity range ({Product ID}:{Min quantity}-{Max quantity}. for example, 77:1-3, 123:2-5, 156:3-8). - - - - - - - - Customer has the following products in his shopping cart - - - - - Restricted products - - - The comma-separated list of product identifiers (e.g. 77, 123, 156). You can find a product ID on its details page. You can also specify the comma-separated list of product identifiers with quantities ({Product ID}:{Quantity}. for example, 77:1, 123:2, 156:3). And you can also specify the comma-separated list of product identifiers with quantity range ({Product ID}:{Min quantity}-{Max quantity}. for example, 77:1-3, 123:2-5, 156:3-8). - - - - - - - - Customer has spent amount x - - - - - Amount spent - - - Discount will be applied if customer has spent/purchased x.xx amount. - - - Limit to basket subtotal - - - Specifies whether the amount refers to the current basket subtotal or - when unselected - to the sum of all previously incurred orders. - - - - - - \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Models/BillingCountryModel.cs b/src/Plugins/SmartStore.DiscountRules/Models/BillingCountryModel.cs deleted file mode 100644 index e7357ec8f6..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Models/BillingCountryModel.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using SmartStore.Web.Framework; - -namespace SmartStore.DiscountRules.Models -{ - public class BillingCountryModel : DiscountRuleModelBase - { - public BillingCountryModel() - { - AvailableCountries = new List(); - } - - [SmartResourceDisplayName("Plugins.DiscountRules.BillingCountry.Fields.Country")] - public int CountryId { get; set; } - public IList AvailableCountries { get; set; } - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Models/CustomerRoleModel.cs b/src/Plugins/SmartStore.DiscountRules/Models/CustomerRoleModel.cs deleted file mode 100644 index 5c4884ae8d..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Models/CustomerRoleModel.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using SmartStore.Web.Framework; - -namespace SmartStore.DiscountRules.Models -{ - public class CustomerRoleModel : DiscountRuleModelBase - { - public CustomerRoleModel() - { - AvailableCustomerRoles = new List(); - } - [SmartResourceDisplayName("Plugins.DiscountRequirement.MustBeAssignedToCustomerRole.Fields.CustomerRole")] - public int CustomerRoleId { get; set; } - public IList AvailableCustomerRoles { get; set; } - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Models/DiscountRuleModelBase.cs b/src/Plugins/SmartStore.DiscountRules/Models/DiscountRuleModelBase.cs deleted file mode 100644 index cd4c4aab83..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Models/DiscountRuleModelBase.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; - -namespace SmartStore.DiscountRules.Models -{ - public abstract class DiscountRuleModelBase - { - public int DiscountId { get; set; } - public int RequirementId { get; set; } - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Models/HadSpentAmountModel.cs b/src/Plugins/SmartStore.DiscountRules/Models/HadSpentAmountModel.cs deleted file mode 100644 index aedf3f6cfa..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Models/HadSpentAmountModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using SmartStore.Web.Framework; - -namespace SmartStore.DiscountRules.Models -{ - public class HadSpentAmountModel : DiscountRuleModelBase - { - [SmartResourceDisplayName("Plugins.DiscountRules.HadSpentAmount.Fields.Amount")] - public decimal SpentAmount { get; set; } - - [SmartResourceDisplayName("Plugins.DiscountRules.HadSpentAmount.Fields.LimitToCurrentBasketSubTotal")] - public bool LimitToCurrentBasketSubTotal { get; set; } - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Models/HasAllProductsModel.cs b/src/Plugins/SmartStore.DiscountRules/Models/HasAllProductsModel.cs deleted file mode 100644 index 6ad4ef09fa..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Models/HasAllProductsModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using SmartStore.Web.Framework; - -namespace SmartStore.DiscountRules.Models -{ - public class HasAllProductsModel : DiscountRuleModelBase - { - [SmartResourceDisplayName("Plugins.DiscountRules.HasAllProducts.Fields.Products")] - public string Products { get; set; } - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Models/HasOneProductModel.cs b/src/Plugins/SmartStore.DiscountRules/Models/HasOneProductModel.cs deleted file mode 100644 index 3f4bed1434..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Models/HasOneProductModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using SmartStore.Web.Framework; - -namespace SmartStore.DiscountRules.Models -{ - public class HasOneProductModel : DiscountRuleModelBase - { - [SmartResourceDisplayName("Plugins.DiscountRules.HasOneProduct.Fields.Products")] - public string Products { get; set; } - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Models/ShippingCountryModel.cs b/src/Plugins/SmartStore.DiscountRules/Models/ShippingCountryModel.cs deleted file mode 100644 index c510b24c19..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Models/ShippingCountryModel.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using SmartStore.Web.Framework; - -namespace SmartStore.DiscountRules.Models -{ - public class ShippingCountryModel : DiscountRuleModelBase - { - public ShippingCountryModel() - { - AvailableCountries = new List(); - } - [SmartResourceDisplayName("Plugins.DiscountRules.ShippingCountry.Fields.Country")] - public int CountryId { get; set; } - public IList AvailableCountries { get; set; } - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Models/StoreModel.cs b/src/Plugins/SmartStore.DiscountRules/Models/StoreModel.cs deleted file mode 100644 index 8599c54212..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Models/StoreModel.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using SmartStore.Web.Framework; - -namespace SmartStore.DiscountRules.Models -{ - public class StoreModel : DiscountRuleModelBase - { - public StoreModel() - { - AvailableStores = new List(); - } - [SmartResourceDisplayName("Plugins.DiscountRules.Store.Fields.Store")] - public int StoreId { get; set; } - public IList AvailableStores { get; set; } - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Plugin.cs b/src/Plugins/SmartStore.DiscountRules/Plugin.cs deleted file mode 100644 index e9bd38659a..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Plugin.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using SmartStore.Core.Plugins; -using SmartStore.Services.Localization; -using SmartStore.Services.Configuration; - -namespace SmartStore.DiscountRules -{ - - public partial class Plugin : BasePlugin - { - private readonly ILocalizationService _localizationService; - - public Plugin(ILocalizationService _localizationService) - { - this._localizationService = _localizationService; - } - - public override void Install() - { - _localizationService.ImportPluginResourcesFromXml(this.PluginDescriptor); - - base.Install(); - } - - public override void Uninstall() - { - _localizationService.DeleteLocaleStringResources(this.PluginDescriptor.ResourceRootKey); - - _localizationService.DeleteLocaleStringResources("Plugins.DiscountRequirement.MustBeAssignedToCustomerRole"); - _localizationService.DeleteLocaleStringResources("Plugins.DiscountRules.BillingCountry"); - _localizationService.DeleteLocaleStringResources("Plugins.DiscountRules.ShippingCountry"); - _localizationService.DeleteLocaleStringResources("Plugins.DiscountRules.Store"); - _localizationService.DeleteLocaleStringResources("Plugins.DiscountRules.HasOneProduct"); - _localizationService.DeleteLocaleStringResources("Plugins.DiscountRules.HasAllProducts"); - _localizationService.DeleteLocaleStringResources("Plugins.DiscountRules.HadSpentAmount"); - - base.Uninstall(); - } - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Properties/AssemblyInfo.cs b/src/Plugins/SmartStore.DiscountRules/Properties/AssemblyInfo.cs deleted file mode 100644 index 4b90ccacbf..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,2 +0,0 @@ -using System.Reflection; -[assembly: AssemblyTitle("SmartStore.DiscountRules")] diff --git a/src/Plugins/SmartStore.DiscountRules/Providers/BillingCountryRule.cs b/src/Plugins/SmartStore.DiscountRules/Providers/BillingCountryRule.cs deleted file mode 100644 index a7c8b76b66..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Providers/BillingCountryRule.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using SmartStore.Core; -using SmartStore.Core.Plugins; -using SmartStore.Services.Discounts; -using SmartStore.Services.Localization; - -namespace SmartStore.DiscountRules -{ - [SystemName("DiscountRequirement.BillingCountryIs")] - [FriendlyName("Billing country is")] - [DisplayOrder(5)] - public partial class BillingCountryRule : DiscountRequirementRuleBase - { - public override bool CheckRequirement(CheckDiscountRequirementRequest request) - { - if (request == null) - throw new ArgumentNullException("request"); - - if (request.DiscountRequirement == null) - throw new SmartException("Discount requirement is not set"); - - if (request.Customer == null) - return false; - - if (request.Customer.BillingAddress == null) - return false; - - if (request.DiscountRequirement.BillingCountryId == 0) - return false; - - bool result = request.Customer.BillingAddress.CountryId == request.DiscountRequirement.BillingCountryId; - return result; - } - - protected override string GetActionName() - { - return "BillingCountry"; - } - - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Providers/CustomerRoleRule.cs b/src/Plugins/SmartStore.DiscountRules/Providers/CustomerRoleRule.cs deleted file mode 100644 index 080a24302f..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Providers/CustomerRoleRule.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Linq; -using SmartStore.Core; -using SmartStore.Core.Plugins; -using SmartStore.Services.Discounts; -using SmartStore.Services.Localization; -using SmartStore.Services.Configuration; - -namespace SmartStore.DiscountRules -{ - [SystemName("DiscountRequirement.MustBeAssignedToCustomerRole")] - [FriendlyName("Must be assigned to customer role")] - [DisplayOrder(0)] - public partial class CustomerRoleRule : DiscountRequirementRuleBase - { - public override bool CheckRequirement(CheckDiscountRequirementRequest request) - { - if (request == null) - throw new ArgumentNullException("request"); - - if (request.DiscountRequirement == null) - throw new SmartException("Discount requirement is not set"); - - if (request.Customer == null) - return false; - - if (!request.DiscountRequirement.RestrictedToCustomerRoleId.HasValue) - return false; - - foreach (var customerRole in request.Customer.CustomerRoles.Where(cr => cr.Active).ToList()) - if (request.DiscountRequirement.RestrictedToCustomerRoleId == customerRole.Id) - return true; - - return false; - } - - protected override string GetActionName() - { - return "CustomerRole"; - } - - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Providers/DiscountRequirementRuleBase.cs b/src/Plugins/SmartStore.DiscountRules/Providers/DiscountRequirementRuleBase.cs deleted file mode 100644 index 18c61607de..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Providers/DiscountRequirementRuleBase.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using SmartStore.Core; -using SmartStore.Core.Plugins; -using SmartStore.Services.Discounts; - -namespace SmartStore.DiscountRules -{ - public abstract class DiscountRequirementRuleBase : IDiscountRequirementRule - { - public abstract bool CheckRequirement(CheckDiscountRequirementRequest request); - - public string GetConfigurationUrl(int discountId, int? discountRequirementId) - { - string result = "Plugins/SmartStore.DiscountRules/DiscountRules/{0}?discountId={1}".FormatInvariant(GetActionName(), discountId); - if (discountRequirementId.HasValue) - { - result += string.Format("&discountRequirementId={0}", discountRequirementId.Value); - } - return result; - } - - protected abstract string GetActionName(); - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Providers/HadSpentAmountRule.cs b/src/Plugins/SmartStore.DiscountRules/Providers/HadSpentAmountRule.cs deleted file mode 100644 index 69d7f2614b..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Providers/HadSpentAmountRule.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; -using SmartStore.Core.Domain.Orders; -using SmartStore.Core.Logging; -using SmartStore.Core.Plugins; -using SmartStore.DiscountRules.Settings; -using SmartStore.Services.Catalog; -using SmartStore.Services.Customers; -using SmartStore.Services.Discounts; -using SmartStore.Services.Orders; -using SmartStore.Services.Tax; - -namespace SmartStore.DiscountRules -{ - [SystemName("DiscountRequirement.HadSpentAmount")] - [FriendlyName("Customer has spent x amount")] - [DisplayOrder(25)] - public partial class HadSpentAmountRule : DiscountRequirementRuleBase - { - private readonly IOrderService _orderService; - private readonly IPriceCalculationService _priceCalculationService; - private readonly ITaxService _taxService; - private readonly ILogger _logger; - - public HadSpentAmountRule( - IOrderService orderService, - IPriceCalculationService priceCalculationService, - ITaxService taxService, - ILogger logger) - { - _orderService = orderService; - _priceCalculationService = priceCalculationService; - _taxService = taxService; - _logger = logger; - } - - public override bool CheckRequirement(CheckDiscountRequirementRequest request) - { - if (request == null) - throw new ArgumentNullException("request"); - - if (request.DiscountRequirement == null) - throw new SmartException("Discount requirement is not set"); - - if (request.DiscountRequirement.SpentAmount == decimal.Zero) - return true; - - if (request.Customer == null) - return false; - - var settings = DeserializeSettings(request.DiscountRequirement.ExtraData); - - if (settings.LimitToCurrentBasketSubTotal) - { - return CheckCurrentSubTotalRequirement(request); - } - else - { - return CheckTotalHistoryRequirement(request); - } - } - - protected override string GetActionName() - { - return "HadSpentAmount"; - } - - private bool CheckTotalHistoryRequirement(CheckDiscountRequirementRequest request) - { - if (request.Customer.IsGuest()) - return false; - - var orders = _orderService.SearchOrders( - request.Store.Id, - request.Customer.Id, - null, - null, - new int[] { (int)OrderStatus.Complete }, - null, - null, - null, - null, - null, - 0, - int.MaxValue); - - decimal spentAmount = orders.Sum(o => o.OrderTotal); - return spentAmount >= request.DiscountRequirement.SpentAmount; - } - - private bool CheckCurrentSubTotalRequirement(CheckDiscountRequirementRequest request) - { - var spentAmount = decimal.Zero; - - try - { - var taxRate = decimal.Zero; - var cartItems = request.Customer.GetCartItems(ShoppingCartType.ShoppingCart, request.Store.Id); - - foreach (var cartItem in cartItems) - { - var product = cartItem.Item.Product; - Dictionary mergedValuesClone = null; - - // we must reapply merged values because CheckCurrentSubTotalRequirement uses price calculation and is called by it itself. - // this can cause wrong discount calculation if the cart contains a product several times. - if (product.MergedDataValues != null) - mergedValuesClone = new Dictionary(product.MergedDataValues); - - // includeDiscounts == true produces a stack overflow! - spentAmount += cartItem.Item.Quantity * _taxService.GetProductPrice(product, _priceCalculationService.GetUnitPrice(cartItem, false), out taxRate); - - if (mergedValuesClone != null) - product.MergedDataValues = new Dictionary(mergedValuesClone); - } - } - catch (Exception exception) - { - _logger.Error(exception); - return false; - } - - return spentAmount >= request.DiscountRequirement.SpentAmount; - } - - internal static HadSpentAmountSettings DeserializeSettings(string expression) - { - var settings = new HadSpentAmountSettings(); - if (expression.HasValue()) - { - try - { - settings = JsonConvert.DeserializeObject(expression); - } - catch { } - } - - return settings; - } - - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Providers/HasAllProductsRule.cs b/src/Plugins/SmartStore.DiscountRules/Providers/HasAllProductsRule.cs deleted file mode 100644 index afbaf85986..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Providers/HasAllProductsRule.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using System.Linq; -using SmartStore.Core; -using SmartStore.Core.Domain.Orders; -using SmartStore.Core.Plugins; -using SmartStore.Services.Discounts; -using SmartStore.Services.Localization; -using SmartStore.Services.Customers; - -namespace SmartStore.DiscountRules -{ - [SystemName("DiscountRequirement.HasAllProducts")] - [FriendlyName("Customer has all of these products in the cart")] - [DisplayOrder(20)] - public partial class HasAllProductsRule : DiscountRequirementRuleBase - { - public override bool CheckRequirement(CheckDiscountRequirementRequest request) - { - if (request == null) - throw new ArgumentNullException("request"); - - if (request.DiscountRequirement == null) - throw new SmartException("Discount requirement is not set"); - - if (String.IsNullOrWhiteSpace(request.DiscountRequirement.RestrictedProductIds)) - return true; - - if (request.Customer == null) - return false; - - //we support three ways of specifying products: - //1. The comma-separated list of product identifiers (e.g. 77, 123, 156). - //2. The comma-separated list of product identifiers with quantities. - // {Product ID}:{Quantity}. For example, 77:1, 123:2, 156:3 - //3. The comma-separated list of product identifiers with quantity range. - // {Product ID}:{Min quantity}-{Max quantity}. For example, 77:1-3, 123:2-5, 156:3-8 - var restrictedProducts = request.DiscountRequirement.RestrictedProductIds - .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => x.Trim()) - .ToList(); - if (restrictedProducts.Count == 0) - return false; - - //cart - var cart = request.Customer.GetCartItems(ShoppingCartType.ShoppingCart, request.Store.Id); - - bool allFound = true; - foreach (var restrictedProduct in restrictedProducts) - { - if (String.IsNullOrWhiteSpace(restrictedProduct)) - continue; - - bool found1 = false; - foreach (var sci in cart) - { - if (restrictedProduct.Contains(":")) - { - if (restrictedProduct.Contains("-")) - { - //the third way (the quantity rage specified) - //{Product ID}:{Min quantity}-{Max quantity}. For example, 77:1-3, 123:2-5, 156:3-8 - int restrictedProductId = 0; - if (!int.TryParse(restrictedProduct.Split(new[] { ':' })[0], out restrictedProductId)) - //parsing error; exit; - return false; - int quantityMin = 0; - if (!int.TryParse(restrictedProduct.Split(new[] { ':' })[1].Split(new[] { '-' })[0], out quantityMin)) - //parsing error; exit; - return false; - int quantityMax = 0; - if (!int.TryParse(restrictedProduct.Split(new[] { ':' })[1].Split(new[] { '-' })[1], out quantityMax)) - //parsing error; exit; - return false; - - if (sci.Item.ProductId == restrictedProductId && quantityMin <= sci.Item.Quantity && sci.Item.Quantity <= quantityMax) - { - found1 = true; - break; - } - } - else - { - //the second way (the quantity specified) - //{Product ID}:{Quantity}. For example, 77:1, 123:2, 156:3 - int restrictedProductId = 0; - if (!int.TryParse(restrictedProduct.Split(new[] { ':' })[0], out restrictedProductId)) - //parsing error; exit; - return false; - int quantity = 0; - if (!int.TryParse(restrictedProduct.Split(new[] { ':' })[1], out quantity)) - //parsing error; exit; - return false; - - if (sci.Item.ProductId == restrictedProductId && sci.Item.Quantity == quantity) - { - found1 = true; - break; - } - } - } - else - { - //the first way (the quantity is not specified) - int restrictedProductId = int.Parse(restrictedProduct); - if (sci.Item.ProductId == restrictedProductId) - { - found1 = true; - break; - } - } - } - - if (!found1) - { - allFound = false; - break; - } - } - - if (allFound) - return true; - - return false; - } - - protected override string GetActionName() - { - return "HasAllProducts"; - } - - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Providers/HasOneProductRule.cs b/src/Plugins/SmartStore.DiscountRules/Providers/HasOneProductRule.cs deleted file mode 100644 index 3204b29f6b..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Providers/HasOneProductRule.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Linq; -using SmartStore.Core; -using SmartStore.Core.Domain.Orders; -using SmartStore.Core.Plugins; -using SmartStore.Services.Discounts; -using SmartStore.Services.Localization; -using SmartStore.Services.Customers; - -namespace SmartStore.DiscountRules -{ - [SystemName("DiscountRequirement.HasOneProduct")] - [FriendlyName("Customer has one of these products in the cart")] - [DisplayOrder(15)] - public partial class HasOneProductRule : DiscountRequirementRuleBase - { - public override bool CheckRequirement(CheckDiscountRequirementRequest request) - { - if (request == null) - throw new ArgumentNullException("request"); - - if (request.DiscountRequirement == null) - throw new SmartException("Discount requirement is not set"); - - if (String.IsNullOrWhiteSpace(request.DiscountRequirement.RestrictedProductIds)) - return true; - - if (request.Customer == null) - return false; - - //we support three ways of specifying products: - //1. The comma-separated list of product identifiers (e.g. 77, 123, 156). - //2. The comma-separated list of product identifiers with quantities. - // {Product ID}:{Quantity}. For example, 77:1, 123:2, 156:3 - //3. The comma-separated list of product identifiers with quantity range. - // {Product ID}:{Min quantity}-{Max quantity}. For example, 77:1-3, 123:2-5, 156:3-8 - var restrictedProducts = request.DiscountRequirement.RestrictedProductIds - .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(x => x.Trim()) - .ToList(); - - if (restrictedProducts.Count == 0) - return false; - - // cart - var cart = request.Customer.GetCartItems(ShoppingCartType.ShoppingCart, request.Store.Id); - - bool found = false; - foreach (var restrictedProduct in restrictedProducts) - { - if (String.IsNullOrWhiteSpace(restrictedProduct)) - continue; - - foreach (var sci in cart) - { - if (restrictedProduct.Contains(":")) - { - if (restrictedProduct.Contains("-")) - { - // the third way (the quantity rage specified) - // {Product ID}:{Min quantity}-{Max quantity}. For example, 77:1-3, 123:2-5, 156:3-8 - if (!int.TryParse(restrictedProduct.Split(new[] { ':' })[0], out int restrictedProductId)) - //parsing error; exit; - return false; - if (!int.TryParse(restrictedProduct.Split(new[] { ':' })[1].Split(new[] { '-' })[0], out int quantityMin)) - //parsing error; exit; - return false; - if (!int.TryParse(restrictedProduct.Split(new[] { ':' })[1].Split(new[] { '-' })[1], out int quantityMax)) - //parsing error; exit; - return false; - - if (sci.Item.ProductId == restrictedProductId && quantityMin <= sci.Item.Quantity && sci.Item.Quantity <= quantityMax) - { - found = true; - break; - } - } - else - { - // the second way (the quantity specified) - // {Product ID}:{Quantity}. For example, 77:1, 123:2, 156:3 - if (!int.TryParse(restrictedProduct.Split(new[] { ':' })[0], out int restrictedProductId)) - //parsing error; exit; - return false; - if (!int.TryParse(restrictedProduct.Split(new[] { ':' })[1], out int quantity)) - //parsing error; exit; - return false; - - if (sci.Item.ProductId == restrictedProductId && sci.Item.Quantity == quantity) - { - found = true; - break; - } - } - } - else - { - //the first way (the quantity is not specified) - int restrictedProductId = int.Parse(restrictedProduct); - if (sci.Item.ProductId == restrictedProductId) - { - found = true; - break; - } - } - } - - if (found) - { - break; - } - } - - if (found) - return true; - - return false; - } - - protected override string GetActionName() - { - return "HasOneProduct"; - } - - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Providers/ShippingCountryRule.cs b/src/Plugins/SmartStore.DiscountRules/Providers/ShippingCountryRule.cs deleted file mode 100644 index 147057caa8..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Providers/ShippingCountryRule.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using SmartStore.Core; -using SmartStore.Core.Plugins; -using SmartStore.Services.Discounts; -using SmartStore.Services.Localization; - -namespace SmartStore.DiscountRules -{ - [SystemName("DiscountRequirement.ShippingCountryIs")] - [FriendlyName("Shipping country is")] - [DisplayOrder(10)] - public partial class ShippingCountryRule : DiscountRequirementRuleBase - { - public override bool CheckRequirement(CheckDiscountRequirementRequest request) - { - if (request == null) - throw new ArgumentNullException("request"); - - if (request.DiscountRequirement == null) - throw new SmartException("Discount requirement is not set"); - - if (request.Customer == null) - return false; - - if (request.Customer.ShippingAddress == null) - return false; - - if (request.DiscountRequirement.ShippingCountryId == 0) - return false; - - bool result = request.Customer.ShippingAddress.CountryId == request.DiscountRequirement.ShippingCountryId; - return result; - } - - protected override string GetActionName() - { - return "ShippingCountry"; - } - - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Providers/StoreRule.cs b/src/Plugins/SmartStore.DiscountRules/Providers/StoreRule.cs deleted file mode 100644 index 55afd191be..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Providers/StoreRule.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using SmartStore.Core; -using SmartStore.Core.Plugins; -using SmartStore.Services.Discounts; -using SmartStore.Services.Localization; - -namespace SmartStore.DiscountRules -{ - [SystemName("DiscountRequirement.Store")] - [FriendlyName("Restricted to store")] - [DisplayOrder(30)] - public partial class StoreRule : DiscountRequirementRuleBase - { - public override bool CheckRequirement(CheckDiscountRequirementRequest request) - { - if (request == null) - throw new ArgumentNullException("request"); - - if (request.DiscountRequirement == null) - throw new SmartException("Discount requirement is not set"); - - if (request.Customer == null) - return false; - - var storeId = request.DiscountRequirement.RestrictedToStoreId ?? 0; - - if (storeId == 0) - return false; - - bool result = request.Store.Id == storeId; - return result; - } - - protected override string GetActionName() - { - return "Store"; - } - - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/RouteProvider.cs b/src/Plugins/SmartStore.DiscountRules/RouteProvider.cs deleted file mode 100644 index 4f6c8fb679..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/RouteProvider.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Web.Mvc; -using System.Web.Routing; -using SmartStore.Web.Framework.Routing; - -namespace SmartStore.DiscountRules -{ - public partial class RouteProvider : IRouteProvider - { - public void RegisterRoutes(RouteCollection routes) - { - routes.MapRoute("SmartStore.DiscountRules", - "Plugins/SmartStore.DiscountRules/{controller}/{action}", - new { controller = "DiscountRules", action = "Index" }, - new[] { "SmartStore.DiscountRules.Controllers" } - ) - .DataTokens["area"] = "SmartStore.DiscountRules"; - } - public int Priority - { - get - { - return 0; - } - } - } -} diff --git a/src/Plugins/SmartStore.DiscountRules/Settings/HadSpentAmountSettings.cs b/src/Plugins/SmartStore.DiscountRules/Settings/HadSpentAmountSettings.cs deleted file mode 100644 index 817e92f076..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Settings/HadSpentAmountSettings.cs +++ /dev/null @@ -1,8 +0,0 @@ - -namespace SmartStore.DiscountRules.Settings -{ - public class HadSpentAmountSettings - { - public bool LimitToCurrentBasketSubTotal { get; set; } - } -} \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/SmartStore.DiscountRules.csproj b/src/Plugins/SmartStore.DiscountRules/SmartStore.DiscountRules.csproj deleted file mode 100644 index 97356664ed..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/SmartStore.DiscountRules.csproj +++ /dev/null @@ -1,262 +0,0 @@ - - - - - - - False - - - False - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {E2291595-9C99-4408-898B-A3E3BB4864CC} - {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - SmartStore.DiscountRules - SmartStore.DiscountRules - v4.6.1 - 512 - - - - - - - - - - ..\..\ - true - - - 4.0 - true - - - - - - - - - - - - true - full - false - ..\..\Presentation\SmartStore.Web\Plugins\SmartStore.DiscountRules\ - DEBUG;TRACE - prompt - 4 - false - - - pdbonly - true - ..\..\Presentation\SmartStore.Web\Plugins\SmartStore.DiscountRules\ - TRACE - prompt - 4 - false - - - true - bin\EFMigrations\ - DEBUG;TRACE - full - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - true - bin\ - DEBUG;TRACE - full - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - - ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - False - - - ..\..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll - False - - - - - - - - - - - - ..\..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.Helpers.dll - False - - - ..\..\packages\Microsoft.AspNet.Mvc.5.2.7\lib\net45\System.Web.Mvc.dll - False - - - ..\..\packages\Microsoft.AspNet.Razor.3.2.7\lib\net45\System.Web.Razor.dll - False - - - ..\..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.WebPages.dll - False - - - ..\..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.WebPages.Deployment.dll - False - - - ..\..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.WebPages.Razor.dll - False - - - - - - - Properties\AssemblySharedInfo.cs - - - Properties\AssemblyVersionInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - {6bda8332-939f-45b7-a25e-7a797260ae59} - SmartStore.Core - - - {210541AD-F659-47DA-8763-16F36C5CD2F4} - SmartStore.Services - - - {75fd4163-333c-4dd5-854d-2ef294e45d94} - SmartStore.Web.Framework - - - - - PreserveNewest - - - - - PreserveNewest - - - Designer - Always - - - Designer - Always - - - - - - PreserveNewest - - - PreserveNewest - Designer - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - - - - - - - False - True - 51143 - / - http://localhost:56865/ - False - True - http://www.smartstore.net - False - - - - - - - Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". - - - - - \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/BillingCountry.cshtml b/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/BillingCountry.cshtml deleted file mode 100644 index 93f895d970..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/BillingCountry.cshtml +++ /dev/null @@ -1,29 +0,0 @@ -@model SmartStore.DiscountRules.Models.BillingCountryModel -@using SmartStore.Web.Framework; -@{ - Layout = ""; -} - - - - - - -
              - @Html.SmartLabelFor(model => model.CountryId) - -
              -
              - @Html.DropDownListFor(model => model.CountryId, Model.AvailableCountries, new { data_routeparam = "countryId" }) - -
              -
              -
              \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/CustomerRole.cshtml b/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/CustomerRole.cshtml deleted file mode 100644 index 7353d01ea2..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/CustomerRole.cshtml +++ /dev/null @@ -1,29 +0,0 @@ -@{ - Layout = ""; -} -@model SmartStore.DiscountRules.Models.CustomerRoleModel -@using SmartStore.Web.Framework; - - - - - - -
              - @Html.SmartLabelFor(model => model.CustomerRoleId) - -
              -
              - @Html.DropDownListFor(model => model.CustomerRoleId, Model.AvailableCustomerRoles, new { data_routeparam = "customerRoleId" }) - -
              -
              -
              \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/HadSpentAmount.cshtml b/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/HadSpentAmount.cshtml deleted file mode 100644 index 2bd80940da..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/HadSpentAmount.cshtml +++ /dev/null @@ -1,62 +0,0 @@ -@{ - Layout = ""; -} -@model SmartStore.DiscountRules.Models.HadSpentAmountModel -@using SmartStore.Web.Framework; - - - - - - - - - - - - - - - - -
              - @Html.SmartLabelFor(model => model.SpentAmount) - - @Html.Hidden("SpentAmountVal", Model.SpentAmount, new { data_routeparam = "spentAmount" }) - @Html.EditorFor(model => model.SpentAmount) - @Html.ValidationMessageFor(model => model.SpentAmount) -
              - @Html.SmartLabelFor(model => model.LimitToCurrentBasketSubTotal) - - @Html.CheckBoxFor(model => model.LimitToCurrentBasketSubTotal, new { data_routeparam = "settings", data_routevalue = ViewData.TemplateInfo.HtmlFieldPrefix + "_getSettings" }) - @Html.ValidationMessageFor(model => model.LimitToCurrentBasketSubTotal) -
              -   - - -
              \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/HasAllProducts.cshtml b/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/HasAllProducts.cshtml deleted file mode 100644 index 20cc92462e..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/HasAllProducts.cshtml +++ /dev/null @@ -1,55 +0,0 @@ -@using SmartStore.Web.Framework -@model SmartStore.DiscountRules.Models.HasAllProductsModel -@{ - Layout = ""; -} - - - - - -
              - @Html.SmartLabelFor(model => model.Products) - -
              -
              -
              - @Html.TextBoxFor(model => model.Products, new { data_routeparam = "productIds", @readonly = "readonly" }) - - - - - - - -
              - @(Html.SmartStore().EntityPicker() - .For(x => x.Products) - .HtmlAttribute("class", "btn btn-outline-secondary") - .DialogTitle(T("Plugins.DiscountRules.HasAllProducts.Fields.Products").Text.EncodeJsString('\'', false)) - .Caption(T("Admin.Common.Search").Text.EncodeJsString('\'', false))) -
              -
              - - -
              -
              -
              - - \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/HasOneProduct.cshtml b/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/HasOneProduct.cshtml deleted file mode 100644 index f1582e8eb9..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/HasOneProduct.cshtml +++ /dev/null @@ -1,53 +0,0 @@ -@using SmartStore.Web.Framework -@model SmartStore.DiscountRules.Models.HasOneProductModel -@{ - Layout = ""; -} - - - - - -
              - @Html.SmartLabelFor(model => model.Products) - -
              -
              -
              - @Html.TextBoxFor(model => model.Products, new { data_routeparam = "productIds", @readonly = "readonly" }) - - - - - -
              - @(Html.SmartStore().EntityPicker() - .For(x => x.Products) - .HtmlAttribute("class", "btn btn-outline-secondary") - .DialogTitle(T("Plugins.DiscountRules.HasOneProduct.Fields.Products").Text.EncodeJsString('\'', false)) - .Caption(T("Admin.Common.Search").Text.EncodeJsString('\'', false))) -
              -
              - - -
              -
              -
              - - \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/ShippingCountry.cshtml b/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/ShippingCountry.cshtml deleted file mode 100644 index e9685aa7e9..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/ShippingCountry.cshtml +++ /dev/null @@ -1,29 +0,0 @@ -@{ - Layout = ""; -} -@model SmartStore.DiscountRules.Models.ShippingCountryModel -@using SmartStore.Web.Framework; - - - - - - -
              - @Html.SmartLabelFor(model => model.CountryId) - -
              -
              - @Html.DropDownListFor(model => model.CountryId, Model.AvailableCountries, new { data_routeparam = "countryId" }) - -
              -
              -
              \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/Store.cshtml b/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/Store.cshtml deleted file mode 100644 index 3c91814931..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/Views/DiscountRules/Store.cshtml +++ /dev/null @@ -1,29 +0,0 @@ -@model SmartStore.DiscountRules.Models.StoreModel -@using SmartStore.Web.Framework; -@{ - Layout = ""; -} - - - - - - -
              - @Html.SmartLabelFor(model => model.StoreId) - -
              -
              - @Html.DropDownListFor(model => model.StoreId, Model.AvailableStores, new { data_routeparam = "storeId" }) - -
              -
              -
              \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/packages.config b/src/Plugins/SmartStore.DiscountRules/packages.config deleted file mode 100644 index ac12d6f05a..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/packages.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/Plugins/SmartStore.DiscountRules/web.config b/src/Plugins/SmartStore.DiscountRules/web.config deleted file mode 100644 index 522d79a602..0000000000 --- a/src/Plugins/SmartStore.DiscountRules/web.config +++ /dev/null @@ -1,149 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Plugins/SmartStore.FacebookAuth/Controllers/ExternalAuthFacebookController.cs b/src/Plugins/SmartStore.FacebookAuth/Controllers/ExternalAuthFacebookController.cs index d7462cd9e5..c6351de248 100644 --- a/src/Plugins/SmartStore.FacebookAuth/Controllers/ExternalAuthFacebookController.cs +++ b/src/Plugins/SmartStore.FacebookAuth/Controllers/ExternalAuthFacebookController.cs @@ -1,11 +1,10 @@ using System.Web.Mvc; using SmartStore.ComponentModel; using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Security; using SmartStore.FacebookAuth.Core; using SmartStore.FacebookAuth.Models; -using SmartStore.Services; using SmartStore.Services.Authentication.External; -using SmartStore.Services.Security; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Controllers; using SmartStore.Web.Framework.Security; @@ -13,141 +12,130 @@ namespace SmartStore.FacebookAuth.Controllers { - //[UnitOfWork] - public class ExternalAuthFacebookController : PluginControllerBase + public class ExternalAuthFacebookController : PluginControllerBase { private readonly IOAuthProviderFacebookAuthorizer _oAuthProviderFacebookAuthorizer; private readonly IOpenAuthenticationService _openAuthenticationService; private readonly ExternalAuthenticationSettings _externalAuthenticationSettings; - private readonly ICommonServices _services; public ExternalAuthFacebookController( IOAuthProviderFacebookAuthorizer oAuthProviderFacebookAuthorizer, IOpenAuthenticationService openAuthenticationService, - ExternalAuthenticationSettings externalAuthenticationSettings, - ICommonServices services) + ExternalAuthenticationSettings externalAuthenticationSettings) { _oAuthProviderFacebookAuthorizer = oAuthProviderFacebookAuthorizer; _openAuthenticationService = openAuthenticationService; _externalAuthenticationSettings = externalAuthenticationSettings; - _services = services; } - private bool HasPermission(bool notify = true) - { - var hasPermission = _services.Permissions.Authorize(StandardPermissionProvider.ManageExternalAuthenticationMethods); - - if (notify && !hasPermission) - NotifyError(_services.Localization.GetResource("Admin.AccessDenied.Description")); - - return hasPermission; - } - - [LoadSetting, AdminAuthorize, ChildActionOnly] + [LoadSetting, AdminAuthorize, ChildActionOnly] + [Permission(Permissions.Configuration.Authentication.Read)] public ActionResult Configure(FacebookExternalAuthSettings settings) { - if (!HasPermission(false)) - return AccessDeniedPartialView(); - var model = new ConfigurationModel(); - MiniMapper.Map(settings, model); + MiniMapper.Map(settings, model); - var host = _services.StoreContext.CurrentStore.GetHost(true); - model.RedirectUrl = $"{host}Plugins/SmartStore.FacebookAuth/logincallback/"; + var host = Services.StoreContext.CurrentStore.GetHost(true); + model.RedirectUrl = $"{host}Plugins/SmartStore.FacebookAuth/logincallback/"; - return View(model); + return View(model); } - [SaveSetting, HttpPost, AdminAuthorize, ChildActionOnly] - public ActionResult Configure(FacebookExternalAuthSettings settings, ConfigurationModel model) + [SaveSetting, HttpPost, AdminAuthorize, ChildActionOnly] + [ValidateAntiForgeryToken] + [Permission(Permissions.Configuration.Authentication.Update)] + public ActionResult Configure(FacebookExternalAuthSettings settings, ConfigurationModel model) { - if (!HasPermission(false)) - return Configure(settings); - if (!ModelState.IsValid) + { return Configure(settings); + } - MiniMapper.Map(model, settings); - settings.ClientKeyIdentifier = model.ClientKeyIdentifier.TrimSafe(); - settings.ClientSecret = model.ClientSecret.TrimSafe(); + MiniMapper.Map(model, settings); + settings.ClientKeyIdentifier = model.ClientKeyIdentifier.TrimSafe(); + settings.ClientSecret = model.ClientSecret.TrimSafe(); - NotifySuccess(_services.Localization.GetResource("Admin.Common.DataSuccessfullySaved")); + NotifySuccess(T("Admin.Common.DataSuccessfullySaved")); - return RedirectToConfiguration(FacebookExternalAuthMethod.SystemName, true); + return RedirectToConfiguration(FacebookExternalAuthMethod.SystemName, true); } [ChildActionOnly] public ActionResult PublicInfo() { - var settings = _services.Settings.LoadSetting(_services.StoreContext.CurrentStore.Id); + var settings = Services.Settings.LoadSetting(Services.StoreContext.CurrentStore.Id); if (settings.ClientKeyIdentifier.HasValue() && settings.ClientSecret.HasValue()) + { return View(); - else - return new EmptyResult(); + } + + return new EmptyResult(); } - public ActionResult Login(string returnUrl) - { - return LoginInternal(returnUrl, false); - } - - public ActionResult LoginCallback(string returnUrl) - { - return LoginInternal(returnUrl, true); - } - - [NonAction] - private ActionResult LoginInternal(string returnUrl, bool verifyResponse) - { - var processor = _openAuthenticationService.LoadExternalAuthenticationMethodBySystemName(FacebookExternalAuthMethod.SystemName, _services.StoreContext.CurrentStore.Id); - if (processor == null || !processor.IsMethodActive(_externalAuthenticationSettings)) - { + public ActionResult Login(string returnUrl) + { + return LoginInternal(returnUrl, false); + } + + public ActionResult LoginCallback(string returnUrl) + { + return LoginInternal(returnUrl, true); + } + + [NonAction] + private ActionResult LoginInternal(string returnUrl, bool verifyResponse) + { + var processor = _openAuthenticationService.LoadExternalAuthenticationMethodBySystemName(FacebookExternalAuthMethod.SystemName, Services.StoreContext.CurrentStore.Id); + if (processor == null || !processor.IsMethodActive(_externalAuthenticationSettings)) + { NotifyError(T("Plugins.CannotLoadModule", T("Plugins.FriendlyName.SmartStore.FacebookAuth"))); return new RedirectResult(Url.LogOn(returnUrl)); - } - - var viewModel = new LoginModel(); - TryUpdateModel(viewModel); - - var result = _oAuthProviderFacebookAuthorizer.Authorize(returnUrl, verifyResponse); - switch (result.AuthenticationStatus) - { - case OpenAuthenticationStatus.Error: - { - if (!result.Success) - { - result.Errors.Each(x => NotifyError(x)); - } - - return new RedirectResult(Url.LogOn(returnUrl)); - } - case OpenAuthenticationStatus.AssociateOnLogon: - { - return new RedirectResult(Url.LogOn(returnUrl)); - } - case OpenAuthenticationStatus.AutoRegisteredEmailValidation: - { - return RedirectToRoute("RegisterResult", new { resultId = (int)UserRegistrationType.EmailValidation, returnUrl }); - } - case OpenAuthenticationStatus.AutoRegisteredAdminApproval: - { - return RedirectToRoute("RegisterResult", new { resultId = (int)UserRegistrationType.AdminApproval, returnUrl }); - } - case OpenAuthenticationStatus.AutoRegisteredStandard: - { - return RedirectToRoute("RegisterResult", new { resultId = (int)UserRegistrationType.Standard, returnUrl }); - } - default: - break; - } - - if (result.Result != null) - return result.Result; - - return HttpContext.Request.IsAuthenticated ? - RedirectToReferrer(returnUrl, "~/") : - new RedirectResult(Url.LogOn(returnUrl)); - } - } + } + + var viewModel = new LoginModel(); + TryUpdateModel(viewModel); + + var result = _oAuthProviderFacebookAuthorizer.Authorize(returnUrl, verifyResponse); + switch (result.AuthenticationStatus) + { + case OpenAuthenticationStatus.Error: + { + if (!result.Success) + { + result.Errors.Each(x => NotifyError(x)); + } + + return new RedirectResult(Url.LogOn(returnUrl)); + } + case OpenAuthenticationStatus.AssociateOnLogon: + { + return new RedirectResult(Url.LogOn(returnUrl)); + } + case OpenAuthenticationStatus.AutoRegisteredEmailValidation: + { + return RedirectToRoute("RegisterResult", new { resultId = (int)UserRegistrationType.EmailValidation, returnUrl }); + } + case OpenAuthenticationStatus.AutoRegisteredAdminApproval: + { + return RedirectToRoute("RegisterResult", new { resultId = (int)UserRegistrationType.AdminApproval, returnUrl }); + } + case OpenAuthenticationStatus.AutoRegisteredStandard: + { + return RedirectToRoute("RegisterResult", new { resultId = (int)UserRegistrationType.Standard, returnUrl }); + } + default: + break; + } + + if (result.Result != null) + { + return result.Result; + } + + return HttpContext.Request.IsAuthenticated ? + RedirectToReferrer(returnUrl, "~/") : + new RedirectResult(Url.LogOn(returnUrl)); + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.FacebookAuth/Core/FacebookOAuth2Client.cs b/src/Plugins/SmartStore.FacebookAuth/Core/FacebookOAuth2Client.cs index e11239ef4f..398f771ff5 100644 --- a/src/Plugins/SmartStore.FacebookAuth/Core/FacebookOAuth2Client.cs +++ b/src/Plugins/SmartStore.FacebookAuth/Core/FacebookOAuth2Client.cs @@ -14,220 +14,220 @@ namespace SmartStore.FacebookAuth.Core { - /// - /// - internal class FacebookOAuth2Client : OAuth2Client - { - /// - /// The authorization endpoint. - /// - private const string AuthorizationEndpoint = "https://www.facebook.com/v2.8/dialog/oauth"; - /// - /// The token endpoint. - /// - private const string TokenEndpoint = "https://graph.facebook.com/v2.8/oauth/access_token"; - /// - /// The user info endpoint. - /// - private const string UserInfoEndpoint = "https://graph.facebook.com/v2.8/me"; - /// - /// The app id. - /// - private readonly string _appId; - /// - /// The app secret. - /// - private readonly string _appSecret; - - /// - /// The requested scopes. - /// - private readonly string[] _requestedScopes; - - - /// - /// Creates a new Facebook OAuth2 client, requesting the default "email" scope. - /// - /// The Facebook App Id - /// The Facebook App Secret - public FacebookOAuth2Client(string appId, string appSecret) - : this(appId, appSecret, new[] { "email" }) { } - - /// - /// Creates a new Facebook OAuth2 client. - /// - /// The Facebook App Id - /// The Facebook App Secret - /// One or more requested scopes, passed without the base URI. - public FacebookOAuth2Client(string appId, string appSecret, params string[] requestedScopes) - : base("facebook") + /// + /// + internal class FacebookOAuth2Client : OAuth2Client + { + /// + /// The authorization endpoint. + /// + private const string AuthorizationEndpoint = "https://www.facebook.com/v2.8/dialog/oauth"; + /// + /// The token endpoint. + /// + private const string TokenEndpoint = "https://graph.facebook.com/v2.8/oauth/access_token"; + /// + /// The user info endpoint. + /// + private const string UserInfoEndpoint = "https://graph.facebook.com/v2.8/me"; + /// + /// The app id. + /// + private readonly string _appId; + /// + /// The app secret. + /// + private readonly string _appSecret; + + /// + /// The requested scopes. + /// + private readonly string[] _requestedScopes; + + + /// + /// Creates a new Facebook OAuth2 client, requesting the default "email" scope. + /// + /// The Facebook App Id + /// The Facebook App Secret + public FacebookOAuth2Client(string appId, string appSecret) + : this(appId, appSecret, new[] { "email" }) { } + + /// + /// Creates a new Facebook OAuth2 client. + /// + /// The Facebook App Id + /// The Facebook App Secret + /// One or more requested scopes, passed without the base URI. + public FacebookOAuth2Client(string appId, string appSecret, params string[] requestedScopes) + : base("facebook") { - if (string.IsNullOrWhiteSpace(appId)) - throw new ArgumentNullException("appId"); - - if (string.IsNullOrWhiteSpace(appSecret)) - throw new ArgumentNullException("appSecret"); - - if (requestedScopes == null) - throw new ArgumentNullException("requestedScopes"); - - if (requestedScopes.Length == 0) - throw new ArgumentException("One or more scopes must be requested.", "requestedScopes"); - - _appId = appId; - _appSecret = appSecret; - _requestedScopes = requestedScopes; - } - - public override void RequestAuthentication(HttpContextBase context, Uri returnUrl) - { - string redirectUrl = this.GetServiceLoginUrl(returnUrl).AbsoluteUri; - context.Response.Redirect(redirectUrl, endResponse: true); - } - - public new AuthenticationResult VerifyAuthentication(HttpContextBase context) - { - throw new NoNullAllowedException(); - } - - public override AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl) - { - string code = context.Request.QueryString["code"]; - if (string.IsNullOrEmpty(code)) - { - return AuthenticationResult.Failed; - } - - string accessToken = this.QueryAccessToken(returnPageUrl, code); - if (accessToken == null) - { - return AuthenticationResult.Failed; - } - - IDictionary userData = this.GetUserData(accessToken); - if (userData == null) - { - return AuthenticationResult.Failed; - } - - string id = userData["id"]; - string name; - - // Some oAuth providers do not return value for the 'username' attribute. - // In that case, try the 'name' attribute. If it's still unavailable, fall back to 'id' - if (!userData.TryGetValue("username", out name) && !userData.TryGetValue("name", out name)) - { - name = id; - } - - // add the access token to the user data dictionary just in case page developers want to use it - userData["accesstoken"] = accessToken; - - return new AuthenticationResult(isSuccessful: true, provider: ProviderName, providerUserId: id, userName: name, extraData: userData); - } - - protected override Uri GetServiceLoginUrl(Uri returnUrl) - { - var state = string.IsNullOrEmpty(returnUrl.Query) ? string.Empty : returnUrl.Query.Substring(1); - - return BuildUri(AuthorizationEndpoint, new NameValueCollection - { - { "client_id", _appId }, - { "scope", string.Join(" ", _requestedScopes) }, - { "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) }, - { "state", state }, - }); - } - - protected override IDictionary GetUserData(string accessToken) - { - var uri = BuildUri(UserInfoEndpoint, new NameValueCollection { { "access_token", accessToken } }); - - var webRequest = (HttpWebRequest)WebRequest.Create(uri); - - using (var webResponse = webRequest.GetResponse()) - using (var stream = webResponse.GetResponseStream()) - { - if (stream == null) - return null; - - using (var textReader = new StreamReader(stream)) - { - var json = textReader.ReadToEnd(); - var extraData = JsonConvert.DeserializeObject>(json); - var data = extraData.ToDictionary(x => x.Key, x => x.Value.ToString()); - - data.Add("picture", string.Format("https://graph.facebook.com/{0}/picture", data["id"])); - - return data; - } - } - } - - public string QueryAccessTokenByCode(Uri returnUrl, string authorizationCode) - { - return this.QueryAccessToken(returnUrl, authorizationCode); - } - - protected override string QueryAccessToken(Uri returnUrl, string authorizationCode) - { - var uri = BuildUri(TokenEndpoint, new NameValueCollection - { - { "code", authorizationCode }, - { "client_id", _appId }, - { "client_secret", _appSecret }, - { "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) }, - }); - - var webRequest = (HttpWebRequest)WebRequest.Create(uri); - string accessToken = null; - - using (var response = (HttpWebResponse)webRequest.GetResponse()) - { - // handle response from FB - // this will not be a url with params like the first request to get the 'code' - Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet); - - using (var sr = new StreamReader(response.GetResponseStream(), rEncoding)) - { - var serializer = new JavaScriptSerializer(); - var jsonObject = serializer.DeserializeObject(sr.ReadToEnd()); - var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject)); - - Dictionary desirializedJsonObject = JsonConvert.DeserializeObject>(jConvert.ToString()); - accessToken = desirializedJsonObject["access_token"].ToString(); - } - } - - return accessToken; - } - - private static Uri BuildUri(string baseUri, NameValueCollection queryParameters) - { - var keyValuePairs = queryParameters.AllKeys.Select(k => HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(queryParameters[k])); - var qs = String.Join("&", keyValuePairs); - - var builder = new UriBuilder(baseUri) { Query = qs }; - return builder.Uri; - } - - /// - /// Facebook works best when return data be packed into a "state" parameter. - /// This should be called before verifying the request, so that the url is rewritten to support this. - /// - public static void RewriteRequest() - { - var ctx = HttpContext.Current; - - var stateString = HttpUtility.UrlDecode(ctx.Request.QueryString["state"]); - if (stateString == null || !stateString.Contains("__provider__=facebook")) - return; - - var q = HttpUtility.ParseQueryString(stateString); - q.Add(ctx.Request.QueryString); - q.Remove("state"); - - ctx.RewritePath(ctx.Request.Path + "?" + q); - } - } + if (string.IsNullOrWhiteSpace(appId)) + throw new ArgumentNullException("appId"); + + if (string.IsNullOrWhiteSpace(appSecret)) + throw new ArgumentNullException("appSecret"); + + if (requestedScopes == null) + throw new ArgumentNullException("requestedScopes"); + + if (requestedScopes.Length == 0) + throw new ArgumentException("One or more scopes must be requested.", "requestedScopes"); + + _appId = appId; + _appSecret = appSecret; + _requestedScopes = requestedScopes; + } + + public override void RequestAuthentication(HttpContextBase context, Uri returnUrl) + { + string redirectUrl = this.GetServiceLoginUrl(returnUrl).AbsoluteUri; + context.Response.Redirect(redirectUrl, endResponse: true); + } + + public new AuthenticationResult VerifyAuthentication(HttpContextBase context) + { + throw new NoNullAllowedException(); + } + + public override AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl) + { + string code = context.Request.QueryString["code"]; + if (string.IsNullOrEmpty(code)) + { + return AuthenticationResult.Failed; + } + + string accessToken = this.QueryAccessToken(returnPageUrl, code); + if (accessToken == null) + { + return AuthenticationResult.Failed; + } + + IDictionary userData = this.GetUserData(accessToken); + if (userData == null) + { + return AuthenticationResult.Failed; + } + + string id = userData["id"]; + string name; + + // Some oAuth providers do not return value for the 'username' attribute. + // In that case, try the 'name' attribute. If it's still unavailable, fall back to 'id' + if (!userData.TryGetValue("username", out name) && !userData.TryGetValue("name", out name)) + { + name = id; + } + + // add the access token to the user data dictionary just in case page developers want to use it + userData["accesstoken"] = accessToken; + + return new AuthenticationResult(isSuccessful: true, provider: ProviderName, providerUserId: id, userName: name, extraData: userData); + } + + protected override Uri GetServiceLoginUrl(Uri returnUrl) + { + var state = string.IsNullOrEmpty(returnUrl.Query) ? string.Empty : returnUrl.Query.Substring(1); + + return BuildUri(AuthorizationEndpoint, new NameValueCollection + { + { "client_id", _appId }, + { "scope", string.Join(" ", _requestedScopes) }, + { "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) }, + { "state", state }, + }); + } + + protected override IDictionary GetUserData(string accessToken) + { + var uri = BuildUri(UserInfoEndpoint, new NameValueCollection { { "access_token", accessToken } }); + + var webRequest = (HttpWebRequest)WebRequest.Create(uri); + + using (var webResponse = webRequest.GetResponse()) + using (var stream = webResponse.GetResponseStream()) + { + if (stream == null) + return null; + + using (var textReader = new StreamReader(stream)) + { + var json = textReader.ReadToEnd(); + var extraData = JsonConvert.DeserializeObject>(json); + var data = extraData.ToDictionary(x => x.Key, x => x.Value.ToString()); + + data.Add("picture", string.Format("https://graph.facebook.com/{0}/picture", data["id"])); + + return data; + } + } + } + + public string QueryAccessTokenByCode(Uri returnUrl, string authorizationCode) + { + return this.QueryAccessToken(returnUrl, authorizationCode); + } + + protected override string QueryAccessToken(Uri returnUrl, string authorizationCode) + { + var uri = BuildUri(TokenEndpoint, new NameValueCollection + { + { "code", authorizationCode }, + { "client_id", _appId }, + { "client_secret", _appSecret }, + { "redirect_uri", returnUrl.GetLeftPart(UriPartial.Path) }, + }); + + var webRequest = (HttpWebRequest)WebRequest.Create(uri); + string accessToken = null; + + using (var response = (HttpWebResponse)webRequest.GetResponse()) + { + // handle response from FB + // this will not be a url with params like the first request to get the 'code' + Encoding rEncoding = Encoding.GetEncoding(response.CharacterSet); + + using (var sr = new StreamReader(response.GetResponseStream(), rEncoding)) + { + var serializer = new JavaScriptSerializer(); + var jsonObject = serializer.DeserializeObject(sr.ReadToEnd()); + var jConvert = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(jsonObject)); + + Dictionary desirializedJsonObject = JsonConvert.DeserializeObject>(jConvert.ToString()); + accessToken = desirializedJsonObject["access_token"].ToString(); + } + } + + return accessToken; + } + + private static Uri BuildUri(string baseUri, NameValueCollection queryParameters) + { + var keyValuePairs = queryParameters.AllKeys.Select(k => HttpUtility.UrlEncode(k) + "=" + HttpUtility.UrlEncode(queryParameters[k])); + var qs = String.Join("&", keyValuePairs); + + var builder = new UriBuilder(baseUri) { Query = qs }; + return builder.Uri; + } + + /// + /// Facebook works best when return data be packed into a "state" parameter. + /// This should be called before verifying the request, so that the url is rewritten to support this. + /// + public static void RewriteRequest() + { + var ctx = HttpContext.Current; + + var stateString = HttpUtility.UrlDecode(ctx.Request.QueryString["state"]); + if (stateString == null || !stateString.Contains("__provider__=facebook")) + return; + + var q = HttpUtility.ParseQueryString(stateString); + q.Add(ctx.Request.QueryString); + q.Remove("state"); + + ctx.RewritePath(ctx.Request.Path + "?" + q); + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.FacebookAuth/Core/FacebookProviderAuthorizer.cs b/src/Plugins/SmartStore.FacebookAuth/Core/FacebookProviderAuthorizer.cs index 6f987af313..41594bc23e 100644 --- a/src/Plugins/SmartStore.FacebookAuth/Core/FacebookProviderAuthorizer.cs +++ b/src/Plugins/SmartStore.FacebookAuth/Core/FacebookProviderAuthorizer.cs @@ -17,279 +17,279 @@ namespace SmartStore.FacebookAuth.Core { - public class FacebookProviderAuthorizer : IOAuthProviderFacebookAuthorizer - { - #region Fields - - private readonly IExternalAuthorizer _authorizer; + public class FacebookProviderAuthorizer : IOAuthProviderFacebookAuthorizer + { + #region Fields + + private readonly IExternalAuthorizer _authorizer; private readonly IOpenAuthenticationService _openAuthenticationService; private readonly ExternalAuthenticationSettings _externalAuthenticationSettings; private readonly HttpContextBase _httpContext; - private readonly ICommonServices _services; + private readonly ICommonServices _services; - private FacebookOAuth2Client _facebookApplication; + private FacebookOAuth2Client _facebookApplication; - #endregion + #endregion - #region Ctor + #region Ctor public FacebookProviderAuthorizer(IExternalAuthorizer authorizer, IOpenAuthenticationService openAuthenticationService, ExternalAuthenticationSettings externalAuthenticationSettings, HttpContextBase httpContext, - ICommonServices services) + ICommonServices services) { _authorizer = authorizer; _openAuthenticationService = openAuthenticationService; _externalAuthenticationSettings = externalAuthenticationSettings; _httpContext = httpContext; - _services = services; + _services = services; - Logger = NullLogger.Instance; - } + Logger = NullLogger.Instance; + } - public ILogger Logger { get; set; } + public ILogger Logger { get; set; } - #endregion + #endregion - #region Utilities + #region Utilities - private FacebookOAuth2Client FacebookApplication + private FacebookOAuth2Client FacebookApplication { - get - { - if (_facebookApplication == null) - { - var settings = _services.Settings.LoadSetting(_services.StoreContext.CurrentStore.Id); + get + { + if (_facebookApplication == null) + { + var settings = _services.Settings.LoadSetting(_services.StoreContext.CurrentStore.Id); - _facebookApplication = new FacebookOAuth2Client(settings.ClientKeyIdentifier, settings.ClientSecret); - } + _facebookApplication = new FacebookOAuth2Client(settings.ClientKeyIdentifier, settings.ClientSecret); + } - return _facebookApplication; - } + return _facebookApplication; + } } - private AuthorizeState VerifyAuthentication(string returnUrl) + private AuthorizeState VerifyAuthentication(string returnUrl) { - string error = null; - AuthenticationResult authResult = null; - - try - { - authResult = this.FacebookApplication.VerifyAuthentication(_httpContext, GenerateLocalCallbackUri()); - } - catch (WebException wexc) - { - using (var response = wexc.Response as HttpWebResponse) - { - error = response.StatusDescription; - - var enc = Encoding.GetEncoding(response.CharacterSet); - using (var reader = new StreamReader(response.GetResponseStream(), enc)) - { - var rawResponse = reader.ReadToEnd(); - Logger.Log(LogLevel.Error, new Exception(rawResponse), response.StatusDescription, null); - } - } - } - catch (Exception exception) - { - error = exception.ToString(); - Logger.Log(LogLevel.Error, exception, null, null); - } - - if (authResult != null && authResult.IsSuccessful) - { - if (!authResult.ExtraData.ContainsKey("id")) - throw new Exception("Authentication result does not contain id data"); - - if (!authResult.ExtraData.ContainsKey("accesstoken")) - throw new Exception("Authentication result does not contain accesstoken data"); - - var parameters = new OAuthAuthenticationParameters(FacebookExternalAuthMethod.SystemName) - { - ExternalIdentifier = authResult.ProviderUserId, - OAuthToken = authResult.ExtraData["accesstoken"], - OAuthAccessToken = authResult.ProviderUserId, - }; - - if (_externalAuthenticationSettings.AutoRegisterEnabled) - ParseClaims(authResult, parameters); - - var result = _authorizer.Authorize(parameters); - - return new AuthorizeState(returnUrl, result); - } - - if (error.IsEmpty() && authResult != null && authResult.Error != null) - { - error = authResult.Error.Message; - } - if (error.IsEmpty()) - { - error = _services.Localization.GetResource("Admin.Common.UnknownError"); - } - - var state = new AuthorizeState(returnUrl, OpenAuthenticationStatus.Error); - state.AddError(error); + string error = null; + AuthenticationResult authResult = null; + + try + { + authResult = this.FacebookApplication.VerifyAuthentication(_httpContext, GenerateLocalCallbackUri()); + } + catch (WebException wexc) + { + using (var response = wexc.Response as HttpWebResponse) + { + error = response.StatusDescription; + + var enc = Encoding.GetEncoding(response.CharacterSet); + using (var reader = new StreamReader(response.GetResponseStream(), enc)) + { + var rawResponse = reader.ReadToEnd(); + Logger.Log(LogLevel.Error, new Exception(rawResponse), response.StatusDescription, null); + } + } + } + catch (Exception exception) + { + error = exception.ToString(); + Logger.Log(LogLevel.Error, exception, null, null); + } + + if (authResult != null && authResult.IsSuccessful) + { + if (!authResult.ExtraData.ContainsKey("id")) + throw new Exception("Authentication result does not contain id data"); + + if (!authResult.ExtraData.ContainsKey("accesstoken")) + throw new Exception("Authentication result does not contain accesstoken data"); + + var parameters = new OAuthAuthenticationParameters(FacebookExternalAuthMethod.SystemName) + { + ExternalIdentifier = authResult.ProviderUserId, + OAuthToken = authResult.ExtraData["accesstoken"], + OAuthAccessToken = authResult.ProviderUserId, + }; + + if (_externalAuthenticationSettings.AutoRegisterEnabled) + ParseClaims(authResult, parameters); + + var result = _authorizer.Authorize(parameters); + + return new AuthorizeState(returnUrl, result); + } + + if (error.IsEmpty() && authResult != null && authResult.Error != null) + { + error = authResult.Error.Message; + } + if (error.IsEmpty()) + { + error = _services.Localization.GetResource("Admin.Common.UnknownError"); + } + + var state = new AuthorizeState(returnUrl, OpenAuthenticationStatus.Error); + state.AddError(error); return state; } - private string GetEmailFromFacebook(string accessToken) - { - var result = ""; - var webRequest = WebRequest.Create("https://graph.facebook.com/me?fields=email&access_token=" + EscapeUriDataStringRfc3986(accessToken)); - - using (var webResponse = webRequest.GetResponse()) - using (var stream = webResponse.GetResponseStream()) - using (var reader = new StreamReader(stream)) - { - var strResponse = reader.ReadToEnd(); - var info = JObject.Parse(strResponse); - - if (info["email"] != null) - { - result = info["email"].ToString(); - } - } - return result; - } - - private void ParseClaims(AuthenticationResult authenticationResult, OAuthAuthenticationParameters parameters) + private string GetEmailFromFacebook(string accessToken) + { + var result = ""; + var webRequest = WebRequest.Create("https://graph.facebook.com/me?fields=email&access_token=" + EscapeUriDataStringRfc3986(accessToken)); + + using (var webResponse = webRequest.GetResponse()) + using (var stream = webResponse.GetResponseStream()) + using (var reader = new StreamReader(stream)) + { + var strResponse = reader.ReadToEnd(); + var info = JObject.Parse(strResponse); + + if (info["email"] != null) + { + result = info["email"].ToString(); + } + } + return result; + } + + private void ParseClaims(AuthenticationResult authenticationResult, OAuthAuthenticationParameters parameters) { - var claims = new UserClaims(); - claims.Contact = new ContactClaims(); - - if (authenticationResult.ExtraData.ContainsKey("username")) - { - claims.Contact.Email = authenticationResult.ExtraData["username"]; - } - else - { - claims.Contact.Email = GetEmailFromFacebook(authenticationResult.ExtraData["accesstoken"]); - } - - claims.Name = new NameClaims(); - - if (authenticationResult.ExtraData.ContainsKey("name")) - { - var name = authenticationResult.ExtraData["name"]; - if (!String.IsNullOrEmpty(name)) - { - var nameSplit = name.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - if (nameSplit.Length >= 2) - { - claims.Name.First = nameSplit[0]; - claims.Name.Last = nameSplit[1]; - } - else - { - claims.Name.Last = nameSplit[0]; - } - } - } + var claims = new UserClaims(); + claims.Contact = new ContactClaims(); + + if (authenticationResult.ExtraData.ContainsKey("username")) + { + claims.Contact.Email = authenticationResult.ExtraData["username"]; + } + else + { + claims.Contact.Email = GetEmailFromFacebook(authenticationResult.ExtraData["accesstoken"]); + } + + claims.Name = new NameClaims(); + + if (authenticationResult.ExtraData.ContainsKey("name")) + { + var name = authenticationResult.ExtraData["name"]; + if (!String.IsNullOrEmpty(name)) + { + var nameSplit = name.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + if (nameSplit.Length >= 2) + { + claims.Name.First = nameSplit[0]; + claims.Name.Last = nameSplit[1]; + } + else + { + claims.Name.Last = nameSplit[0]; + } + } + } parameters.AddClaim(claims); } - private AuthorizeState RequestAuthentication(string returnUrl) + private AuthorizeState RequestAuthentication(string returnUrl) + { + var authUrl = GenerateServiceLoginUrl().AbsoluteUri; + return new AuthorizeState("", OpenAuthenticationStatus.RequiresRedirect) { Result = new RedirectResult(authUrl) }; + } + + private Uri GenerateLocalCallbackUri() + { + string url = string.Format("{0}Plugins/SmartStore.FacebookAuth/logincallback/", _services.WebHelper.GetStoreLocation()); + return new Uri(url); + } + + private Uri GenerateServiceLoginUrl() + { + //code copied from DotNetOpenAuth.AspNet.Clients.FacebookClient file + var builder = new UriBuilder("https://www.facebook.com/dialog/oauth"); + var args = new Dictionary(); + var settings = _services.Settings.LoadSetting(_services.StoreContext.CurrentStore.Id); + + args.Add("client_id", settings.ClientKeyIdentifier); + args.Add("redirect_uri", GenerateLocalCallbackUri().AbsoluteUri); + args.Add("scope", "email"); + + AppendQueryArgs(builder, args); + + return builder.Uri; + } + + private void AppendQueryArgs(UriBuilder builder, IEnumerable> args) + { + if ((args != null) && (args.Count>() > 0)) + { + StringBuilder builder2 = new StringBuilder(50 + (args.Count>() * 10)); + if (!string.IsNullOrEmpty(builder.Query)) + { + builder2.Append(builder.Query.Substring(1)); + builder2.Append('&'); + } + builder2.Append(CreateQueryString(args)); + builder.Query = builder2.ToString(); + } + } + + private string CreateQueryString(IEnumerable> args) + { + if (!args.Any>()) + { + return string.Empty; + } + StringBuilder builder = new StringBuilder(args.Count>() * 10); + foreach (KeyValuePair pair in args) + { + builder.Append(EscapeUriDataStringRfc3986(pair.Key)); + builder.Append('='); + builder.Append(EscapeUriDataStringRfc3986(pair.Value)); + builder.Append('&'); + } + builder.Length--; + return builder.ToString(); + } + + private readonly string[] UriRfc3986CharsToEscape = new string[] { "!", "*", "'", "(", ")" }; + + private string EscapeUriDataStringRfc3986(string value) + { + StringBuilder builder = new StringBuilder(Uri.EscapeDataString(value)); + for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++) + { + builder.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0])); + } + return builder.ToString(); + } + + #endregion + + #region Methods + + /// + /// Authorize response + /// + /// Return URL + /// true - Verify response;false - request authentication;null - determine automatically + /// Authorize state + public AuthorizeState Authorize(string returnUrl, bool? verifyResponse = null) { - var authUrl = GenerateServiceLoginUrl().AbsoluteUri; - return new AuthorizeState("", OpenAuthenticationStatus.RequiresRedirect) { Result = new RedirectResult(authUrl) }; + if (!verifyResponse.HasValue) + throw new ArgumentException("Facebook plugin cannot automatically determine verifyResponse property"); + + if (verifyResponse.Value) + { + return VerifyAuthentication(returnUrl); + } + else + { + return RequestAuthentication(returnUrl); + } } - private Uri GenerateLocalCallbackUri() - { - string url = string.Format("{0}Plugins/SmartStore.FacebookAuth/logincallback/", _services.WebHelper.GetStoreLocation()); - return new Uri(url); - } - - private Uri GenerateServiceLoginUrl() - { - //code copied from DotNetOpenAuth.AspNet.Clients.FacebookClient file - var builder = new UriBuilder("https://www.facebook.com/dialog/oauth"); - var args = new Dictionary(); - var settings = _services.Settings.LoadSetting(_services.StoreContext.CurrentStore.Id); - - args.Add("client_id", settings.ClientKeyIdentifier); - args.Add("redirect_uri", GenerateLocalCallbackUri().AbsoluteUri); - args.Add("scope", "email"); - - AppendQueryArgs(builder, args); - - return builder.Uri; - } - - private void AppendQueryArgs(UriBuilder builder, IEnumerable> args) - { - if ((args != null) && (args.Count>() > 0)) - { - StringBuilder builder2 = new StringBuilder(50 + (args.Count>() * 10)); - if (!string.IsNullOrEmpty(builder.Query)) - { - builder2.Append(builder.Query.Substring(1)); - builder2.Append('&'); - } - builder2.Append(CreateQueryString(args)); - builder.Query = builder2.ToString(); - } - } - - private string CreateQueryString(IEnumerable> args) - { - if (!args.Any>()) - { - return string.Empty; - } - StringBuilder builder = new StringBuilder(args.Count>() * 10); - foreach (KeyValuePair pair in args) - { - builder.Append(EscapeUriDataStringRfc3986(pair.Key)); - builder.Append('='); - builder.Append(EscapeUriDataStringRfc3986(pair.Value)); - builder.Append('&'); - } - builder.Length--; - return builder.ToString(); - } - - private readonly string[] UriRfc3986CharsToEscape = new string[] { "!", "*", "'", "(", ")" }; - - private string EscapeUriDataStringRfc3986(string value) - { - StringBuilder builder = new StringBuilder(Uri.EscapeDataString(value)); - for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++) - { - builder.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0])); - } - return builder.ToString(); - } - - #endregion - - #region Methods - - /// - /// Authorize response - /// - /// Return URL - /// true - Verify response;false - request authentication;null - determine automatically - /// Authorize state - public AuthorizeState Authorize(string returnUrl, bool? verifyResponse = null) - { - if (!verifyResponse.HasValue) - throw new ArgumentException("Facebook plugin cannot automatically determine verifyResponse property"); - - if (verifyResponse.Value) - { - return VerifyAuthentication(returnUrl); - } - else - { - return RequestAuthentication(returnUrl); - } - } - - #endregion + #endregion } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.FacebookAuth/Core/OAuthAuthenticationParameters.cs b/src/Plugins/SmartStore.FacebookAuth/Core/OAuthAuthenticationParameters.cs index 722482aa13..9ef3f5bbc5 100644 --- a/src/Plugins/SmartStore.FacebookAuth/Core/OAuthAuthenticationParameters.cs +++ b/src/Plugins/SmartStore.FacebookAuth/Core/OAuthAuthenticationParameters.cs @@ -15,13 +15,7 @@ public OAuthAuthenticationParameters(string providerSystemName) this._providerSystemName = providerSystemName; } - public override IList UserClaims - { - get - { - return _claims; - } - } + public override IList UserClaims => _claims; public void AddClaim(UserClaims claim) { @@ -31,9 +25,6 @@ public void AddClaim(UserClaims claim) _claims.Add(claim); } - public override string ProviderSystemName - { - get { return _providerSystemName; } - } + public override string ProviderSystemName => _providerSystemName; } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.FacebookAuth/DependencyRegistrar.cs b/src/Plugins/SmartStore.FacebookAuth/DependencyRegistrar.cs index ef4c32cf34..08c9c95ad5 100644 --- a/src/Plugins/SmartStore.FacebookAuth/DependencyRegistrar.cs +++ b/src/Plugins/SmartStore.FacebookAuth/DependencyRegistrar.cs @@ -7,14 +7,11 @@ namespace SmartStore.FacebookAuth { public class DependencyRegistrar : IDependencyRegistrar { - public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, bool isActiveModule) + public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, bool isActiveModule) { builder.RegisterType().As().InstancePerRequest(); } - public int Order - { - get { return 1; } - } + public int Order => 1; } } diff --git a/src/Plugins/SmartStore.FacebookAuth/Description.txt b/src/Plugins/SmartStore.FacebookAuth/Description.txt index 447d4bf86d..ed03b2381a 100644 --- a/src/Plugins/SmartStore.FacebookAuth/Description.txt +++ b/src/Plugins/SmartStore.FacebookAuth/Description.txt @@ -1,8 +1,8 @@ FriendlyName: Facebook Login SystemName: SmartStore.FacebookAuth Group: Security -Version: 3.2.2 -MinAppVersion: 3.2.2 +Version: 4.2.0 +MinAppVersion: 4.2.0 DisplayOrder: 5 FileName: SmartStore.FacebookAuth.dll ResourceRootKey: Plugins.ExternalAuth.Facebook \ No newline at end of file diff --git a/src/Plugins/SmartStore.FacebookAuth/FacebookExternalAuthMethod.cs b/src/Plugins/SmartStore.FacebookAuth/FacebookExternalAuthMethod.cs index db68ea5d5c..0c7b986ed9 100644 --- a/src/Plugins/SmartStore.FacebookAuth/FacebookExternalAuthMethod.cs +++ b/src/Plugins/SmartStore.FacebookAuth/FacebookExternalAuthMethod.cs @@ -17,19 +17,19 @@ public FacebookExternalAuthMethod(ILocalizationService localizationService) _localizationService = localizationService; } - public static string SystemName => "SmartStore.FacebookAuth"; + public static string SystemName => "SmartStore.FacebookAuth"; - /// - /// Gets a route for provider configuration - /// - /// Action name - /// Controller name - /// Route values - public void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) + /// + /// Gets a route for provider configuration + /// + /// Action name + /// Controller name + /// Route values + public void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) { - actionName = "Configure"; - controllerName = "ExternalAuthFacebook"; - routeValues = new RouteValueDictionary(new { Namespaces = "SmartStore.FacebookAuth.Controllers", area = SystemName }); + actionName = "Configure"; + controllerName = "ExternalAuthFacebook"; + routeValues = new RouteValueDictionary(new { Namespaces = "SmartStore.FacebookAuth.Controllers", area = SystemName }); } /// @@ -42,7 +42,7 @@ public void GetPublicInfoRoute(out string actionName, out string controllerName, { actionName = "PublicInfo"; controllerName = "ExternalAuthFacebook"; - routeValues = new RouteValueDictionary(new { Namespaces = "SmartStore.FacebookAuth.Controllers", area = SystemName }); + routeValues = new RouteValueDictionary(new { Namespaces = "SmartStore.FacebookAuth.Controllers", area = SystemName }); } /// @@ -60,6 +60,6 @@ public override void Uninstall() _localizationService.DeleteLocaleStringResources(PluginDescriptor.ResourceRootKey); base.Uninstall(); - } + } } } diff --git a/src/Plugins/SmartStore.FacebookAuth/Models/ConfigurationModel.cs b/src/Plugins/SmartStore.FacebookAuth/Models/ConfigurationModel.cs index dc0a098627..a437ae548d 100644 --- a/src/Plugins/SmartStore.FacebookAuth/Models/ConfigurationModel.cs +++ b/src/Plugins/SmartStore.FacebookAuth/Models/ConfigurationModel.cs @@ -11,7 +11,7 @@ public class ConfigurationModel : ModelBase [SmartResourceDisplayName("Plugins.ExternalAuth.Facebook.ClientSecret")] public string ClientSecret { get; set; } - [SmartResourceDisplayName("Plugins.ExternalAuth.Facebook.RedirectUri")] - public string RedirectUrl { get; set; } - } + [SmartResourceDisplayName("Plugins.ExternalAuth.Facebook.RedirectUri")] + public string RedirectUrl { get; set; } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.FacebookAuth/RouteProvider.cs b/src/Plugins/SmartStore.FacebookAuth/RouteProvider.cs index fdb9911da7..1d9b8cf532 100644 --- a/src/Plugins/SmartStore.FacebookAuth/RouteProvider.cs +++ b/src/Plugins/SmartStore.FacebookAuth/RouteProvider.cs @@ -4,23 +4,17 @@ namespace SmartStore.FacebookAuth { - public partial class RouteProvider : IRouteProvider + public partial class RouteProvider : IRouteProvider { public void RegisterRoutes(RouteCollection routes) { - routes.MapRoute("SmartStore.FacebookAuth", - "Plugins/SmartStore.FacebookAuth/{action}", - new { controller = "ExternalAuthFacebook" }, - new[] { "SmartStore.FacebookAuth.Controllers" } - ) - .DataTokens["area"] = FacebookExternalAuthMethod.SystemName; - } - public int Priority - { - get - { - return 0; - } + routes.MapRoute("SmartStore.FacebookAuth", + "Plugins/SmartStore.FacebookAuth/{action}", + new { controller = "ExternalAuthFacebook" }, + new[] { "SmartStore.FacebookAuth.Controllers" } + ) + .DataTokens["area"] = FacebookExternalAuthMethod.SystemName; } + public int Priority => 0; } } diff --git a/src/Plugins/SmartStore.FacebookAuth/SmartStore.FacebookAuth.csproj b/src/Plugins/SmartStore.FacebookAuth/SmartStore.FacebookAuth.csproj index 832cc36b51..7696d4a5f8 100644 --- a/src/Plugins/SmartStore.FacebookAuth/SmartStore.FacebookAuth.csproj +++ b/src/Plugins/SmartStore.FacebookAuth/SmartStore.FacebookAuth.csproj @@ -1,6 +1,5 @@  - @@ -21,7 +20,7 @@ Properties SmartStore.FacebookAuth SmartStore.FacebookAuth - v4.6.1 + v4.7.2 512 @@ -86,12 +85,12 @@ MinimumRecommendedRules.ruleset - - ..\..\packages\Autofac.4.9.0\lib\net45\Autofac.dll + + ..\..\packages\Autofac.5.2.0\lib\net461\Autofac.dll False - - ..\..\packages\Autofac.Mvc5.4.0.2\lib\net45\Autofac.Integration.Mvc.dll + + ..\..\packages\Autofac.Mvc5.5.0.0\lib\net461\Autofac.Integration.Mvc.dll False @@ -124,15 +123,22 @@ ..\..\packages\DotNetOpenAuth.OpenId.RelyingParty.4.3.4.13329\lib\net45-full\DotNetOpenAuth.OpenId.RelyingParty.dll True + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + False + ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll False - - ..\..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll - False + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + @@ -145,13 +151,19 @@ False + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + - + ..\..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.Helpers.dll False diff --git a/src/Plugins/SmartStore.FacebookAuth/Views/ExternalAuthFacebook/Configure.cshtml b/src/Plugins/SmartStore.FacebookAuth/Views/ExternalAuthFacebook/Configure.cshtml index 1658e3b419..a51e4a6d52 100644 --- a/src/Plugins/SmartStore.FacebookAuth/Views/ExternalAuthFacebook/Configure.cshtml +++ b/src/Plugins/SmartStore.FacebookAuth/Views/ExternalAuthFacebook/Configure.cshtml @@ -15,6 +15,8 @@ @using (Html.BeginForm()) { + @Html.AntiForgeryToken() +
              @using (Html.BeginForm()) -{ +{ + @Html.AntiForgeryToken() + - - - + + + +
              @@ -41,7 +43,7 @@ @Html.SmartLabelFor(model => model.TrackingScript) + @Html.SettingEditorFor(model => model.TrackingScript, Html.TextAreaFor(model => model.TrackingScript, new { style = "height: 300px;" })) @Html.ValidationMessageFor(model => model.TrackingScript) @@ -51,7 +53,7 @@ @Html.SmartLabelFor(model => model.EcommerceScript) + @Html.SettingEditorFor(model => model.EcommerceScript, Html.TextAreaFor(model => model.EcommerceScript, new { style = "height: 300px;" })) @Html.ValidationMessageFor(model => model.EcommerceScript) @@ -61,11 +63,23 @@ @Html.SmartLabelFor(model => model.EcommerceDetailScript) + @Html.SettingEditorFor(model => model.EcommerceDetailScript, Html.TextAreaFor(model => model.EcommerceDetailScript, new { style = "height: 200px;" })) @Html.ValidationMessageFor(model => model.EcommerceDetailScript)
              + + +
              } \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleAnalytics/packages.config b/src/Plugins/SmartStore.GoogleAnalytics/packages.config index 704b8f0f85..bba317dcf9 100644 --- a/src/Plugins/SmartStore.GoogleAnalytics/packages.config +++ b/src/Plugins/SmartStore.GoogleAnalytics/packages.config @@ -3,5 +3,6 @@ + \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleAnalytics/web.config b/src/Plugins/SmartStore.GoogleAnalytics/web.config index fbe6b4c179..c2fd9be3a5 100644 --- a/src/Plugins/SmartStore.GoogleAnalytics/web.config +++ b/src/Plugins/SmartStore.GoogleAnalytics/web.config @@ -1,8 +1,9 @@ - + - - + + + - + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/AdminMenu.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/AdminMenu.cs index 6a62863d9a..14571d6a02 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/AdminMenu.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/AdminMenu.cs @@ -5,13 +5,13 @@ namespace SmartStore.GoogleMerchantCenter { public class AdminMenu : AdminMenuProvider { - protected override void BuildMenuCore(TreeNode pluginsNode) + protected override void BuildMenuCore(TreeNode pluginsNode) { - var menuItem = new MenuItem().ToBuilder() + var menuItem = new MenuItem().ToBuilder() .Text("Google Merchant Center") .Icon("fab fa-google") .ResKey("Plugins.FriendlyName.SmartStore.GoogleMerchantCenter") - .Action("ConfigurePlugin", "Plugin", new { systemName = GoogleMerchantCenterFeedPlugin.SystemName, area = "Admin" }) + .Action("ConfigurePlugin", "Plugin", new { systemName = GoogleMerchantCenterFeedPlugin.SystemName, area = "Admin" }) .ToItem(); pluginsNode.Prepend(menuItem); diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs index ed8533af16..1b20158c12 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Controllers/FeedGoogleMerchantCenterController.cs @@ -15,177 +15,177 @@ namespace SmartStore.GoogleMerchantCenter.Controllers { - [AdminAuthorize] - public class FeedGoogleMerchantCenterController : PluginControllerBase - { - private readonly IGoogleFeedService _googleFeedService; - private readonly Lazy _adminAreaSettings; - private readonly Lazy _exportService; - - public FeedGoogleMerchantCenterController( - IGoogleFeedService googleFeedService, - Lazy adminAreaSettings, - Lazy exportService) - { - _googleFeedService = googleFeedService; - _adminAreaSettings = adminAreaSettings; - _exportService = exportService; - } - - public ActionResult ProductEditTab(int productId) - { - var culture = CultureInfo.InvariantCulture; - var model = new GoogleProductModel { ProductId = productId }; - var entity = _googleFeedService.GetGoogleProductRecord(productId); - string notSpecified = T("Common.Unspecified"); - - if (entity != null) - { - model.Taxonomy = entity.Taxonomy; - model.Gender = entity.Gender; - model.AgeGroup = entity.AgeGroup; - model.IsAdult = entity.IsAdult; - model.Color = entity.Color; - model.Size = entity.Size; - model.Material = entity.Material; - model.Pattern = entity.Pattern; - model.Export2 = entity.Export; - model.Multipack2 = entity.Multipack; - model.IsBundle = entity.IsBundle; - model.EnergyEfficiencyClass = entity.EnergyEfficiencyClass; - model.CustomLabel0 = entity.CustomLabel0; - model.CustomLabel1 = entity.CustomLabel1; - model.CustomLabel2 = entity.CustomLabel2; - model.CustomLabel3 = entity.CustomLabel3; - model.CustomLabel4 = entity.CustomLabel4; - } - else - { - model.Export2 = true; - } - - ViewBag.DefaultCategory = ""; - ViewBag.DefaultColor = ""; - ViewBag.DefaultSize = ""; - ViewBag.DefaultMaterial = ""; - ViewBag.DefaultPattern = ""; - ViewBag.DefaultGender = notSpecified; - ViewBag.DefaultAgeGroup = notSpecified; - ViewBag.DefaultIsAdult = ""; - ViewBag.DefaultMultipack2 = ""; - ViewBag.DefaultIsBundle = ""; - ViewBag.DefaultEnergyEfficiencyClass = notSpecified; - ViewBag.DefaultCustomLabel = ""; - ViewBag.LanguageSeoCode = Services.WorkContext.WorkingLanguage.UniqueSeoCode.EmptyNull().ToLower(); - - // We do not have export profile context here, so we simply use the first profile. - var profile = _exportService.Value.GetExportProfilesBySystemName(GmcXmlExportProvider.SystemName).FirstOrDefault(); - if (profile != null) - { - var config = XmlHelper.Deserialize(profile.ProviderConfigData, typeof(ProfileConfigurationModel)) as ProfileConfigurationModel; - if (config != null) - { - ViewBag.DefaultCategory = config.DefaultGoogleCategory; - ViewBag.DefaultColor = config.Color; - ViewBag.DefaultSize = config.Size; - ViewBag.DefaultMaterial = config.Material; - ViewBag.DefaultPattern = config.Pattern; - - if (config.Gender.HasValue() && config.Gender != GmcXmlExportProvider.Unspecified) - { - ViewBag.DefaultGender = T("Plugins.Feed.Froogle.Gender" + culture.TextInfo.ToTitleCase(config.Gender)); - } - - if (config.AgeGroup.HasValue() && config.AgeGroup != GmcXmlExportProvider.Unspecified) - { - ViewBag.DefaultAgeGroup = T("Plugins.Feed.Froogle.AgeGroup" + culture.TextInfo.ToTitleCase(config.AgeGroup)); - } - } - } - - ViewBag.AvailableCategories = model.Taxonomy.HasValue() - ? new List { new SelectListItem { Text = model.Taxonomy, Value = model.Taxonomy, Selected = true } } - : new List(); - - ViewBag.AvailableGenders = new List - { - new SelectListItem { Value = "male", Text = T("Plugins.Feed.Froogle.GenderMale") }, - new SelectListItem { Value = "female", Text = T("Plugins.Feed.Froogle.GenderFemale") }, - new SelectListItem { Value = "unisex", Text = T("Plugins.Feed.Froogle.GenderUnisex") } - }; - - ViewBag.AvailableAgeGroups = new List - { - new SelectListItem { Value = "adult", Text = T("Plugins.Feed.Froogle.AgeGroupAdult") }, - new SelectListItem { Value = "kids", Text = T("Plugins.Feed.Froogle.AgeGroupKids") }, - }; - - ViewBag.AvailableEnergyEfficiencyClasses = T("Plugins.Feed.Froogle.EnergyEfficiencyClasses").Text - .SplitSafe(",") - .Select(x => new SelectListItem { Value = x, Text = x }) - .ToList(); - - var result = PartialView(model); - result.ViewData.TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "CustomProperties[GMC]" }; - return result; - } - - public ActionResult Configure() - { - var model = new FeedGoogleMerchantCenterModel(); - - model.GridPageSize = _adminAreaSettings.Value.GridPageSize; - model.LanguageSeoCode = Services.WorkContext.WorkingLanguage.UniqueSeoCode.EmptyNull().ToLower(); - model.EnergyEfficiencyClasses = T("Plugins.Feed.Froogle.EnergyEfficiencyClasses").Text.SplitSafe(","); - - return View(model); - } - - [HttpPost, GridAction(EnableCustomBinding = true)] - public ActionResult GoogleProductList(GridCommand command, string searchProductName, string touched) - { - return new JsonResult - { - Data = _googleFeedService.GetGridModel(command, searchProductName, touched) - }; - } - - [HttpPost] - public ActionResult GoogleProductEdit(int pk, string name, string value) - { - _googleFeedService.Upsert(pk, name, value); - - return new EmptyResult(); - } - - public JsonResult GetGoogleCategories(string search, int? page) - { - page = page ?? 1; - const int take = 100; - var skip = (page.Value - 1) * take; - var categories = _googleFeedService.GetTaxonomyList(search); - var hasMoreItems = (page.Value * take) < categories.Count; - //$"{skip}\\{categories.Length} {hasMoreItems}: {search.NaIfEmpty()}".Dump(); - - var items = categories.Select(x => new - { - id = x, - text = x - }) - .Skip(skip) - .Take(take) - .ToList(); - - return new JsonResult - { - Data = new - { - hasMoreItems, - results = items - }, - MaxJsonLength = int.MaxValue, - JsonRequestBehavior = JsonRequestBehavior.AllowGet - }; - } - } + [AdminAuthorize] + public class FeedGoogleMerchantCenterController : PluginControllerBase + { + private readonly IGoogleFeedService _googleFeedService; + private readonly Lazy _adminAreaSettings; + private readonly Lazy _exportService; + + public FeedGoogleMerchantCenterController( + IGoogleFeedService googleFeedService, + Lazy adminAreaSettings, + Lazy exportService) + { + _googleFeedService = googleFeedService; + _adminAreaSettings = adminAreaSettings; + _exportService = exportService; + } + + public ActionResult ProductEditTab(int productId) + { + var culture = CultureInfo.InvariantCulture; + var model = new GoogleProductModel { ProductId = productId }; + var entity = _googleFeedService.GetGoogleProductRecord(productId); + string notSpecified = T("Common.Unspecified"); + + if (entity != null) + { + model.Taxonomy = entity.Taxonomy; + model.Gender = entity.Gender; + model.AgeGroup = entity.AgeGroup; + model.IsAdult = entity.IsAdult; + model.Color = entity.Color; + model.Size = entity.Size; + model.Material = entity.Material; + model.Pattern = entity.Pattern; + model.Export2 = entity.Export; + model.Multipack2 = entity.Multipack; + model.IsBundle = entity.IsBundle; + model.EnergyEfficiencyClass = entity.EnergyEfficiencyClass; + model.CustomLabel0 = entity.CustomLabel0; + model.CustomLabel1 = entity.CustomLabel1; + model.CustomLabel2 = entity.CustomLabel2; + model.CustomLabel3 = entity.CustomLabel3; + model.CustomLabel4 = entity.CustomLabel4; + } + else + { + model.Export2 = true; + } + + ViewBag.DefaultCategory = ""; + ViewBag.DefaultColor = ""; + ViewBag.DefaultSize = ""; + ViewBag.DefaultMaterial = ""; + ViewBag.DefaultPattern = ""; + ViewBag.DefaultGender = notSpecified; + ViewBag.DefaultAgeGroup = notSpecified; + ViewBag.DefaultIsAdult = ""; + ViewBag.DefaultMultipack2 = ""; + ViewBag.DefaultIsBundle = ""; + ViewBag.DefaultEnergyEfficiencyClass = notSpecified; + ViewBag.DefaultCustomLabel = ""; + ViewBag.LanguageSeoCode = Services.WorkContext.WorkingLanguage.UniqueSeoCode.EmptyNull().ToLower(); + + // We do not have export profile context here, so we simply use the first profile. + var profile = _exportService.Value.GetExportProfilesBySystemName(GmcXmlExportProvider.SystemName).FirstOrDefault(); + if (profile != null) + { + var config = XmlHelper.Deserialize(profile.ProviderConfigData, typeof(ProfileConfigurationModel)) as ProfileConfigurationModel; + if (config != null) + { + ViewBag.DefaultCategory = config.DefaultGoogleCategory; + ViewBag.DefaultColor = config.Color; + ViewBag.DefaultSize = config.Size; + ViewBag.DefaultMaterial = config.Material; + ViewBag.DefaultPattern = config.Pattern; + + if (config.Gender.HasValue() && config.Gender != GmcXmlExportProvider.Unspecified) + { + ViewBag.DefaultGender = T("Plugins.Feed.Froogle.Gender" + culture.TextInfo.ToTitleCase(config.Gender)); + } + + if (config.AgeGroup.HasValue() && config.AgeGroup != GmcXmlExportProvider.Unspecified) + { + ViewBag.DefaultAgeGroup = T("Plugins.Feed.Froogle.AgeGroup" + culture.TextInfo.ToTitleCase(config.AgeGroup)); + } + } + } + + ViewBag.AvailableCategories = model.Taxonomy.HasValue() + ? new List { new SelectListItem { Text = model.Taxonomy, Value = model.Taxonomy, Selected = true } } + : new List(); + + ViewBag.AvailableGenders = new List + { + new SelectListItem { Value = "male", Text = T("Plugins.Feed.Froogle.GenderMale") }, + new SelectListItem { Value = "female", Text = T("Plugins.Feed.Froogle.GenderFemale") }, + new SelectListItem { Value = "unisex", Text = T("Plugins.Feed.Froogle.GenderUnisex") } + }; + + ViewBag.AvailableAgeGroups = new List + { + new SelectListItem { Value = "adult", Text = T("Plugins.Feed.Froogle.AgeGroupAdult") }, + new SelectListItem { Value = "kids", Text = T("Plugins.Feed.Froogle.AgeGroupKids") }, + }; + + ViewBag.AvailableEnergyEfficiencyClasses = T("Plugins.Feed.Froogle.EnergyEfficiencyClasses").Text + .SplitSafe(",") + .Select(x => new SelectListItem { Value = x, Text = x }) + .ToList(); + + var result = PartialView(model); + result.ViewData.TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "CustomProperties[GMC]" }; + return result; + } + + public ActionResult Configure() + { + var model = new FeedGoogleMerchantCenterModel(); + + model.GridPageSize = _adminAreaSettings.Value.GridPageSize; + model.LanguageSeoCode = Services.WorkContext.WorkingLanguage.UniqueSeoCode.EmptyNull().ToLower(); + model.EnergyEfficiencyClasses = T("Plugins.Feed.Froogle.EnergyEfficiencyClasses").Text.SplitSafe(","); + + return View(model); + } + + [HttpPost, GridAction(EnableCustomBinding = true)] + public ActionResult GoogleProductList(GridCommand command, string searchProductName, string touched) + { + return new JsonResult + { + Data = _googleFeedService.GetGridModel(command, searchProductName, touched) + }; + } + + [HttpPost] + public ActionResult GoogleProductEdit(int pk, string name, string value) + { + _googleFeedService.Upsert(pk, name, value); + + return new EmptyResult(); + } + + public JsonResult GetGoogleCategories(string search, int? page) + { + page = page ?? 1; + const int take = 100; + var skip = (page.Value - 1) * take; + var categories = _googleFeedService.GetTaxonomyList(search); + var hasMoreItems = (page.Value * take) < categories.Count; + //$"{skip}\\{categories.Length} {hasMoreItems}: {search.NaIfEmpty()}".Dump(); + + var items = categories.Select(x => new + { + id = x, + text = x + }) + .Skip(skip) + .Take(take) + .ToList(); + + return new JsonResult + { + Data = new + { + hasMoreItems, + results = items + }, + MaxJsonLength = int.MaxValue, + JsonRequestBehavior = JsonRequestBehavior.AllowGet + }; + } + } } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/GoogleProductObjectContext.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/GoogleProductObjectContext.cs index 2d822a76b9..e4700a9642 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/GoogleProductObjectContext.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/GoogleProductObjectContext.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; using System.Data.Entity; -using System.Data.Entity.Infrastructure; -using System.Linq; -using SmartStore.Core; using SmartStore.Data; using SmartStore.Data.Setup; using SmartStore.GoogleMerchantCenter.Data.Migrations; @@ -11,38 +6,38 @@ namespace SmartStore.GoogleMerchantCenter.Data { - public class GoogleProductObjectContext : ObjectContextBase - { + public class GoogleProductObjectContext : ObjectContextBase + { public const string ALIASKEY = "sm_object_context_google_product"; - static GoogleProductObjectContext() - { - var initializer = new MigrateDatabaseInitializer - { - TablesToCheck = new[] { "GoogleProduct" } - }; - Database.SetInitializer(initializer); - } - - /// - /// For tooling support, e.g. EF Migrations - /// - public GoogleProductObjectContext() - : base() - { - } + static GoogleProductObjectContext() + { + var initializer = new MigrateDatabaseInitializer + { + TablesToCheck = new[] { "GoogleProduct" } + }; + Database.SetInitializer(initializer); + } + + /// + /// For tooling support, e.g. EF Migrations + /// + public GoogleProductObjectContext() + : base() + { + } public GoogleProductObjectContext(string nameOrConnectionString) - : base(nameOrConnectionString, ALIASKEY) - { - } + : base(nameOrConnectionString, ALIASKEY) + { + } - protected override void OnModelCreating(DbModelBuilder modelBuilder) - { - modelBuilder.Configurations.Add(new GoogleProductRecordMap()); - base.OnModelCreating(modelBuilder); - } + protected override void OnModelCreating(DbModelBuilder modelBuilder) + { + modelBuilder.Configurations.Add(new GoogleProductRecordMap()); + base.OnModelCreating(modelBuilder); + } - } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/GoogleProductRecordMap.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/GoogleProductRecordMap.cs index 98aaec1aa4..81c634f39c 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/GoogleProductRecordMap.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/GoogleProductRecordMap.cs @@ -10,13 +10,13 @@ public GoogleProductRecordMap() this.ToTable("GoogleProduct"); this.HasKey(x => x.Id); - this.Property(x => x.EnergyEfficiencyClass).HasMaxLength(50); + this.Property(x => x.EnergyEfficiencyClass).HasMaxLength(50); - this.Property(x => x.CustomLabel0).HasMaxLength(100); - this.Property(x => x.CustomLabel1).HasMaxLength(100); - this.Property(x => x.CustomLabel2).HasMaxLength(100); - this.Property(x => x.CustomLabel3).HasMaxLength(100); - this.Property(x => x.CustomLabel4).HasMaxLength(100); - } + this.Property(x => x.CustomLabel0).HasMaxLength(100); + this.Property(x => x.CustomLabel1).HasMaxLength(100); + this.Property(x => x.CustomLabel2).HasMaxLength(100); + this.Property(x => x.CustomLabel3).HasMaxLength(100); + this.Property(x => x.CustomLabel4).HasMaxLength(100); + } } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201403112356126_Initial.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201403112356126_Initial.cs index 983c7d361e..7365f0641d 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201403112356126_Initial.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201403112356126_Initial.cs @@ -1,38 +1,36 @@ namespace SmartStore.GoogleMerchantCenter.Data.Migrations { - using System; using System.Data.Entity.Migrations; - using SmartStore.Data.Setup; - + public partial class Initial : DbMigration { public override void Up() { - if (DbMigrationContext.Current.SuppressInitialCreate()) - return; - - CreateTable( + //if (DbMigrationContext.Current.SuppressInitialCreate()) + // return; + + CreateTable( "dbo.GoogleProduct", c => new - { - Id = c.Int(nullable: false, identity: true), - ProductId = c.Int(nullable: false), - Taxonomy = c.String(maxLength: 4000), - Gender = c.String(maxLength: 4000), - AgeGroup = c.String(maxLength: 4000), - Color = c.String(maxLength: 4000), - Size = c.String(maxLength: 4000), - Material = c.String(maxLength: 4000), - Pattern = c.String(maxLength: 4000), - ItemGroupId = c.String(maxLength: 4000), - }) + { + Id = c.Int(nullable: false, identity: true), + ProductId = c.Int(nullable: false), + Taxonomy = c.String(maxLength: 4000), + Gender = c.String(maxLength: 4000), + AgeGroup = c.String(maxLength: 4000), + Color = c.String(maxLength: 4000), + Size = c.String(maxLength: 4000), + Material = c.String(maxLength: 4000), + Pattern = c.String(maxLength: 4000), + ItemGroupId = c.String(maxLength: 4000), + }) .PrimaryKey(t => t.Id); - + } - + public override void Down() { - //DropTable("dbo.GoogleProduct"); + DropTable("dbo.GoogleProduct"); } } } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201408020941244_IsTouched.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201408020941244_IsTouched.cs index c4af118dd7..ca926c3bbb 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201408020941244_IsTouched.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201408020941244_IsTouched.cs @@ -2,23 +2,23 @@ namespace SmartStore.GoogleMerchantCenter.Data.Migrations { using System; using System.Data.Entity.Migrations; - + public partial class IsTouched : DbMigration { public override void Up() { - var utcNow = DateTime.UtcNow; + var utcNow = DateTime.UtcNow; - AddColumn("dbo.GoogleProduct", "IsTouched", c => c.Boolean(nullable: false)); - AddColumn("dbo.GoogleProduct", "CreatedOnUtc", c => c.DateTime(nullable: false, defaultValue: utcNow)); - AddColumn("dbo.GoogleProduct", "UpdatedOnUtc", c => c.DateTime(nullable: false, defaultValue: utcNow)); + AddColumn("dbo.GoogleProduct", "IsTouched", c => c.Boolean(nullable: false)); + AddColumn("dbo.GoogleProduct", "CreatedOnUtc", c => c.DateTime(nullable: false, defaultValue: utcNow)); + AddColumn("dbo.GoogleProduct", "UpdatedOnUtc", c => c.DateTime(nullable: false, defaultValue: utcNow)); } - + public override void Down() { - DropColumn("dbo.GoogleProduct", "IsTouched"); - DropColumn("dbo.GoogleProduct", "CreatedOnUtc"); - DropColumn("dbo.GoogleProduct", "UpdatedOnUtc"); + DropColumn("dbo.GoogleProduct", "IsTouched"); + DropColumn("dbo.GoogleProduct", "CreatedOnUtc"); + DropColumn("dbo.GoogleProduct", "UpdatedOnUtc"); } } } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201504211854125_IsActive.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201504211854125_IsActive.cs index 6d62461bb1..9e998ea211 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201504211854125_IsActive.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201504211854125_IsActive.cs @@ -1,15 +1,14 @@ namespace SmartStore.GoogleMerchantCenter.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class IsActive : DbMigration { public override void Up() { AddColumn("dbo.GoogleProduct", "Export", c => c.Boolean(nullable: false, defaultValue: true)); } - + public override void Down() { DropColumn("dbo.GoogleProduct", "Export"); diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.cs index bc8af7c35e..3624eee2bc 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201601061649324_IsBundle.cs @@ -1,8 +1,7 @@ namespace SmartStore.GoogleMerchantCenter.Data.Migrations { - using System; using System.Data.Entity.Migrations; - + public partial class IsBundle : DbMigration { public override void Up() @@ -17,7 +16,7 @@ public override void Up() AddColumn("dbo.GoogleProduct", "CustomLabel3", c => c.String(maxLength: 100)); AddColumn("dbo.GoogleProduct", "CustomLabel4", c => c.String(maxLength: 100)); } - + public override void Down() { DropColumn("dbo.GoogleProduct", "CustomLabel4"); diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201705250945238_Indexes.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201705250945238_Indexes.cs index bd0adfd1dc..063459188e 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201705250945238_Indexes.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/201705250945238_Indexes.cs @@ -1,7 +1,7 @@ namespace SmartStore.GoogleMerchantCenter.Data.Migrations { using System.Data.Entity.Migrations; - + public partial class Indexes : DbMigration { public override void Up() @@ -10,7 +10,7 @@ public override void Up() CreateIndex("dbo.GoogleProduct", "IsTouched"); CreateIndex("dbo.GoogleProduct", "Export"); } - + public override void Down() { DropIndex("dbo.GoogleProduct", new[] { "Export" }); diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/Configuration.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/Configuration.cs index 00c0104a40..fcb9afab52 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/Configuration.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Data/Migrations/Configuration.cs @@ -1,21 +1,18 @@ namespace SmartStore.GoogleMerchantCenter.Data.Migrations { - using System; - using System.Data.Entity; - using System.Data.Entity.Migrations; - using System.Linq; + using System.Data.Entity.Migrations; - internal sealed class Configuration : DbMigrationsConfiguration - { - public Configuration() - { - AutomaticMigrationsEnabled = false; - MigrationsDirectory = @"Data\Migrations"; - ContextKey = "SmartStore.FeedGoogle"; // DO NOT CHANGE! - } + internal sealed class Configuration : DbMigrationsConfiguration + { + public Configuration() + { + AutomaticMigrationsEnabled = false; + MigrationsDirectory = @"Data\Migrations"; + ContextKey = "SmartStore.FeedGoogle"; // DO NOT CHANGE! + } - protected override void Seed(GoogleProductObjectContext context) - { - } - } + protected override void Seed(GoogleProductObjectContext context) + { + } + } } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/DependencyRegistrar.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/DependencyRegistrar.cs index ac6e3dd393..6ef3f46631 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/DependencyRegistrar.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/DependencyRegistrar.cs @@ -12,16 +12,16 @@ namespace SmartStore.GoogleMerchantCenter { public class DependencyRegistrar : IDependencyRegistrar { - public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, bool isActiveModule) + public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, bool isActiveModule) { builder.RegisterType().As().InstancePerRequest(); //register named context - builder.Register(c => new GoogleProductObjectContext(DataSettings.Current.DataConnectionString)) + builder.Register(c => new GoogleProductObjectContext(DataSettings.Current.DataConnectionString)) .Named(GoogleProductObjectContext.ALIASKEY) .InstancePerRequest(); - builder.Register(c => new GoogleProductObjectContext(DataSettings.Current.DataConnectionString)) + builder.Register(c => new GoogleProductObjectContext(DataSettings.Current.DataConnectionString)) .InstancePerRequest(); //override required repository with our custom context @@ -31,9 +31,6 @@ public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, b .InstancePerRequest(); } - public int Order - { - get { return 1; } - } + public int Order => 1; } } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Description.txt b/src/Plugins/SmartStore.GoogleMerchantCenter/Description.txt index 3b87cd8d04..828172a31c 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Description.txt +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Description.txt @@ -1,8 +1,8 @@ FriendlyName: Google Merchant Center (GMC) feed SystemName: SmartStore.GoogleMerchantCenter Group: Marketing -Version: 3.2.2 -MinAppVersion: 3.2.2 +Version: 4.2.0 +MinAppVersion: 4.2.0 Author: SmartStore AG DisplayOrder: 1 FileName: SmartStore.GoogleMerchantCenter.dll diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Domain/GoogleProductRecord.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Domain/GoogleProductRecord.cs index 1d177d7809..46fdc6a4f9 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Domain/GoogleProductRecord.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Domain/GoogleProductRecord.cs @@ -9,13 +9,13 @@ namespace SmartStore.GoogleMerchantCenter.Domain ///
              public partial class GoogleProductRecord : BaseEntity { - public GoogleProductRecord() - { - Export = true; - } + public GoogleProductRecord() + { + Export = true; + } - [Index] - public int ProductId { get; set; } + [Index] + public int ProductId { get; set; } public string Taxonomy { get; set; } public string Gender { get; set; } @@ -26,23 +26,23 @@ public GoogleProductRecord() public string Pattern { get; set; } public string ItemGroupId { get; set; } - [Index] - public bool IsTouched { get; set; } - public DateTime CreatedOnUtc { get; set; } - public DateTime UpdatedOnUtc { get; set; } + [Index] + public bool IsTouched { get; set; } + public DateTime CreatedOnUtc { get; set; } + public DateTime UpdatedOnUtc { get; set; } - [Index] - public bool Export { get; set; } + [Index] + public bool Export { get; set; } - public int Multipack { get; set; } - public bool? IsBundle { get; set; } - public bool? IsAdult { get; set; } - public string EnergyEfficiencyClass { get; set; } + public int Multipack { get; set; } + public bool? IsBundle { get; set; } + public bool? IsAdult { get; set; } + public string EnergyEfficiencyClass { get; set; } - public string CustomLabel0 { get; set; } - public string CustomLabel1 { get; set; } - public string CustomLabel2 { get; set; } - public string CustomLabel3 { get; set; } - public string CustomLabel4 { get; set; } - } + public string CustomLabel0 { get; set; } + public string CustomLabel1 { get; set; } + public string CustomLabel2 { get; set; } + public string CustomLabel3 { get; set; } + public string CustomLabel4 { get; set; } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs index 211c45083d..ae1b9f9da4 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Events.cs @@ -8,20 +8,20 @@ namespace SmartStore.GoogleMerchantCenter { - public class Events : IConsumer - { - private readonly IGoogleFeedService _googleService; + public class Events : IConsumer + { + private readonly IGoogleFeedService _googleService; - public Events(IGoogleFeedService googleService) - { - _googleService = googleService; - } + public Events(IGoogleFeedService googleService) + { + _googleService = googleService; + } - public void HandleEvent(TabStripCreated eventMessage) - { - if (eventMessage.TabStripName == "product-edit") - { - var productId = ((TabbableModel)eventMessage.Model).Id; + public void HandleEvent(TabStripCreated eventMessage) + { + if (eventMessage.TabStripName == "product-edit") + { + var productId = ((TabbableModel)eventMessage.Model).Id; eventMessage.ItemFactory.Add().Text("GMC") .Name("tab-gmc") @@ -30,65 +30,65 @@ public void HandleEvent(TabStripCreated eventMessage) .Route("SmartStore.GoogleMerchantCenter", new { action = "ProductEditTab", productId = productId }) .Ajax(); } - } + } - public void HandleEvent(ModelBoundEvent eventMessage) - { - if (!eventMessage.BoundModel.CustomProperties.ContainsKey("GMC")) - return; - - var model = eventMessage.BoundModel.CustomProperties["GMC"] as GoogleProductModel; - if (model == null) - return; + public void HandleEvent(ModelBoundEvent eventMessage) + { + if (!eventMessage.BoundModel.CustomProperties.ContainsKey("GMC")) + return; - var utcNow = DateTime.UtcNow; - var entity = _googleService.GetGoogleProductRecord(model.ProductId); - var insert = (entity == null); + var model = eventMessage.BoundModel.CustomProperties["GMC"] as GoogleProductModel; + if (model == null) + return; - if (entity == null) - { - entity = new GoogleProductRecord - { - ProductId = model.ProductId, - CreatedOnUtc = utcNow - }; - } + var utcNow = DateTime.UtcNow; + var entity = _googleService.GetGoogleProductRecord(model.ProductId); + var insert = (entity == null); - entity.AgeGroup = model.AgeGroup; - entity.Color = model.Color; - entity.Gender = model.Gender; - entity.Size = model.Size; - entity.Taxonomy = model.Taxonomy; - entity.Material = model.Material; - entity.Pattern = model.Pattern; - entity.Export = model.Export2; - entity.UpdatedOnUtc = utcNow; - entity.Multipack = model.Multipack2 ?? 0; - entity.IsBundle = model.IsBundle; - entity.IsAdult = model.IsAdult; - entity.EnergyEfficiencyClass = model.EnergyEfficiencyClass; - entity.CustomLabel0 = model.CustomLabel0; - entity.CustomLabel1 = model.CustomLabel1; - entity.CustomLabel2 = model.CustomLabel2; - entity.CustomLabel3 = model.CustomLabel3; - entity.CustomLabel4 = model.CustomLabel4; + if (entity == null) + { + entity = new GoogleProductRecord + { + ProductId = model.ProductId, + CreatedOnUtc = utcNow + }; + } + + entity.AgeGroup = model.AgeGroup; + entity.Color = model.Color; + entity.Gender = model.Gender; + entity.Size = model.Size; + entity.Taxonomy = model.Taxonomy; + entity.Material = model.Material; + entity.Pattern = model.Pattern; + entity.Export = model.Export2; + entity.UpdatedOnUtc = utcNow; + entity.Multipack = model.Multipack2 ?? 0; + entity.IsBundle = model.IsBundle; + entity.IsAdult = model.IsAdult; + entity.EnergyEfficiencyClass = model.EnergyEfficiencyClass; + entity.CustomLabel0 = model.CustomLabel0; + entity.CustomLabel1 = model.CustomLabel1; + entity.CustomLabel2 = model.CustomLabel2; + entity.CustomLabel3 = model.CustomLabel3; + entity.CustomLabel4 = model.CustomLabel4; - entity.IsTouched = entity.IsTouched(); + entity.IsTouched = entity.IsTouched(); - if (!insert && !entity.IsTouched) - { - _googleService.DeleteGoogleProductRecord(entity); - return; - } + if (!insert && !entity.IsTouched) + { + _googleService.DeleteGoogleProductRecord(entity); + return; + } - if (insert) - { - _googleService.InsertGoogleProductRecord(entity); - } - else - { - _googleService.UpdateGoogleProductRecord(entity); - } - } - } + if (insert) + { + _googleService.InsertGoogleProductRecord(entity); + } + else + { + _googleService.UpdateGoogleProductRecord(entity); + } + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Extensions/MiscExtensions.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Extensions/MiscExtensions.cs index e3a3702bf1..bdb65a1e55 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Extensions/MiscExtensions.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Extensions/MiscExtensions.cs @@ -2,20 +2,20 @@ namespace SmartStore.GoogleMerchantCenter { - public static class MiscExtensions - { - public static bool IsTouched(this GoogleProductRecord p) - { - if (p != null) - { - return - p.Taxonomy.HasValue() || p.Gender.HasValue() || p.AgeGroup.HasValue() || p.Color.HasValue() || - p.Size.HasValue() || p.Material.HasValue() || p.Pattern.HasValue() || p.ItemGroupId.HasValue() || - !p.Export || p.Multipack != 0 || p.IsBundle.HasValue || p.IsAdult.HasValue || p.EnergyEfficiencyClass.HasValue() || - p.CustomLabel0.HasValue() || p.CustomLabel1.HasValue() || p.CustomLabel2.HasValue() || p.CustomLabel3.HasValue() || p.CustomLabel4.HasValue(); - } + public static class MiscExtensions + { + public static bool IsTouched(this GoogleProductRecord p) + { + if (p != null) + { + return + p.Taxonomy.HasValue() || p.Gender.HasValue() || p.AgeGroup.HasValue() || p.Color.HasValue() || + p.Size.HasValue() || p.Material.HasValue() || p.Pattern.HasValue() || p.ItemGroupId.HasValue() || + !p.Export || p.Multipack != 0 || p.IsBundle.HasValue || p.IsAdult.HasValue || p.EnergyEfficiencyClass.HasValue() || + p.CustomLabel0.HasValue() || p.CustomLabel1.HasValue() || p.CustomLabel2.HasValue() || p.CustomLabel3.HasValue() || p.CustomLabel4.HasValue(); + } - return false; - } - } + return false; + } + } } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/GoogleMerchantCenterFeedPlugin.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/GoogleMerchantCenterFeedPlugin.cs index a930d8907c..19cea6a178 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/GoogleMerchantCenterFeedPlugin.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/GoogleMerchantCenterFeedPlugin.cs @@ -3,7 +3,6 @@ using SmartStore.Core.Plugins; using SmartStore.GoogleMerchantCenter.Data.Migrations; using SmartStore.GoogleMerchantCenter.Providers; -using SmartStore.GoogleMerchantCenter.Services; using SmartStore.Services; using SmartStore.Services.DataExchange.Export; @@ -11,24 +10,16 @@ namespace SmartStore.GoogleMerchantCenter { public class GoogleMerchantCenterFeedPlugin : BasePlugin, IConfigurable { - private readonly IGoogleFeedService _googleFeedService; - private readonly ICommonServices _services; + private readonly ICommonServices _services; private readonly IExportProfileService _exportProfileService; - public GoogleMerchantCenterFeedPlugin( - IGoogleFeedService googleFeedService, - ICommonServices services, - IExportProfileService exportProfileService) + public GoogleMerchantCenterFeedPlugin(ICommonServices services, IExportProfileService exportProfileService) { - _googleFeedService = googleFeedService; - _services = services; + _services = services; _exportProfileService = exportProfileService; } - public static string SystemName - { - get { return "SmartStore.GoogleMerchantCenter"; } - } + public static string SystemName => "SmartStore.GoogleMerchantCenter"; /// /// Gets a route for provider configuration @@ -39,8 +30,8 @@ public static string SystemName public void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) { actionName = "Configure"; - controllerName = "FeedGoogleMerchantCenter"; - routeValues = new RouteValueDictionary { { "Namespaces", "SmartStore.GoogleMerchantCenter.Controllers" }, { "area", SystemName } }; + controllerName = "FeedGoogleMerchantCenter"; + routeValues = new RouteValueDictionary { { "Namespaces", "SmartStore.GoogleMerchantCenter.Controllers" }, { "area", SystemName } }; } /// @@ -48,7 +39,7 @@ public void GetConfigurationRoute(out string actionName, out string controllerNa /// public override void Install() { - _services.Localization.ImportPluginResourcesFromXml(this.PluginDescriptor); + _services.Localization.ImportPluginResourcesFromXml(this.PluginDescriptor); base.Install(); } @@ -58,14 +49,15 @@ public override void Install() /// public override void Uninstall() { - _services.Localization.DeleteLocaleStringResources(PluginDescriptor.ResourceRootKey); + _services.Localization.DeleteLocaleStringResources(PluginDescriptor.ResourceRootKey); // Delete existing export profiles. var profiles = _exportProfileService.GetExportProfilesBySystemName(GmcXmlExportProvider.SystemName); profiles.Each(x => _exportProfileService.DeleteExportProfile(x, true)); - var migrator = new DbMigrator(new Configuration()); - migrator.Update(DbMigrator.InitialDatabase); + // Keep Google product table. + //var migrator = new DbMigrator(new Configuration()); + //migrator.Update(DbMigrator.InitialDatabase); base.Uninstall(); } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.de-de.xml b/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.de-de.xml index 1940907121..f504348302 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.de-de.xml +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.de-de.xml @@ -63,21 +63,6 @@ Legt fest, ob mehr als ein Produktbild exportiert werden soll. Bis zu 10 sind möglich. - - Zustand - - - Zustand des Artikels. - - - Neu - - - Gebraucht - - - Erneuert - Verfügbarkeit diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.en-us.xml b/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.en-us.xml index f8cffa44f3..fe2320a599 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.en-us.xml +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Localization/resources.en-us.xml @@ -62,21 +62,6 @@ Check the box if you want to export more than one image. Up to 10 are possible. - - Condition - - - Condition of the product. - - - New - - - Used - - - Refurbished - Availability diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Models/FeedGoogleMerchantCenterModel.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Models/FeedGoogleMerchantCenterModel.cs index 973d44f295..392f33a555 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Models/FeedGoogleMerchantCenterModel.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Models/FeedGoogleMerchantCenterModel.cs @@ -6,123 +6,124 @@ namespace SmartStore.GoogleMerchantCenter.Models { - public class FeedGoogleMerchantCenterModel - { - public int GridPageSize { get; set; } - public string LanguageSeoCode { get; set; } - public string[] EnergyEfficiencyClasses { get; set; } - - [SmartResourceDisplayName("Plugins.Feed.Froogle.SearchProductName")] - public string SearchProductName { get; set; } - - [SmartResourceDisplayName("Plugins.Feed.Froogle.SearchIsTouched")] - public string SearchIsTouched { get; set; } - } - - public class GoogleProductModel : ModelBase - { - public int TotalCount { get; set; } - - // This attribute is required to disable editing. - [ScaffoldColumn(false)] - public int ProductId - { - get { return Id; } - set { Id = value; } - } - public int Id { get; set; } - - // This attribute is required to disable editing. - [ReadOnly(true)] - [ScaffoldColumn(false)] - [SmartResourceDisplayName("Plugins.Feed.Froogle.Products.ProductName")] - public string Name { get; set; } - - public string SKU { get; set; } - public int ProductTypeId { get; set; } - public ProductType ProductType { get { return (ProductType)ProductTypeId; } } - public string ProductTypeName { get; set; } - public string ProductTypeLabelHint - { - get - { - switch (ProductType) - { - case ProductType.SimpleProduct: - return "secondary d-none"; - case ProductType.GroupedProduct: - return "success"; - case ProductType.BundledProduct: - return "info"; - default: - return ""; - } - } - } - - [SmartResourceDisplayName("Plugins.Feed.Froogle.Products.GoogleCategory")] - public string Taxonomy { get; set; } - - [SmartResourceDisplayName("Plugins.Feed.Froogle.Gender")] - public string Gender { get; set; } - - [SmartResourceDisplayName("Plugins.Feed.Froogle.AgeGroup")] - public string AgeGroup { get; set; } - - [SmartResourceDisplayName("Plugins.Feed.Froogle.Color")] - public string Color { get; set; } - - [SmartResourceDisplayName("Plugins.Feed.Froogle.Size")] - public string Size { get; set; } - - [SmartResourceDisplayName("Plugins.Feed.Froogle.Material")] - public string Material { get; set; } - - [SmartResourceDisplayName("Plugins.Feed.Froogle.Pattern")] - public string Pattern { get; set; } - - [SmartResourceDisplayName("Common.Export")] - public int Export { get; set; } - [SmartResourceDisplayName("Common.Export")] - public bool Export2 - { - get { return Export != 0; } - set { Export = (value ? 1 : 0); } - } - - [SmartResourceDisplayName("Plugins.Feed.Froogle.Multipack")] - public int Multipack { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.Multipack")] - public int? Multipack2 - { - get { return Multipack > 0 ? Multipack : (int?)null; } - set { Multipack = (value ?? 0); } - } - - [SmartResourceDisplayName("Plugins.Feed.Froogle.IsBundle")] - public bool? IsBundle { get; set; } - - [SmartResourceDisplayName("Plugins.Feed.Froogle.IsAdult")] - public bool? IsAdult { get; set; } - - [SmartResourceDisplayName("Plugins.Feed.Froogle.EnergyEfficiencyClass")] - public string EnergyEfficiencyClass { get; set; } - - [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel0")] - public string CustomLabel0 { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel1")] - public string CustomLabel1 { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel2")] - public string CustomLabel2 { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel3")] - public string CustomLabel3 { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel4")] - public string CustomLabel4 { get; set; } - - public string GenderLocalize { get; set; } - public string AgeGroupLocalize { get; set; } - public string Export2Localize { get; set; } - public string IsBundleLocalize { get; set; } - public string IsAdultLocalize { get; set; } - } + public class FeedGoogleMerchantCenterModel + { + public int GridPageSize { get; set; } + public string LanguageSeoCode { get; set; } + public string[] EnergyEfficiencyClasses { get; set; } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.SearchProductName")] + public string SearchProductName { get; set; } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.SearchIsTouched")] + public string SearchIsTouched { get; set; } + } + + [CustomModelPart] + public class GoogleProductModel : ModelBase + { + public int TotalCount { get; set; } + + // This attribute is required to disable editing. + [ScaffoldColumn(false)] + public int ProductId + { + get => Id; + set => Id = value; + } + public int Id { get; set; } + + // This attribute is required to disable editing. + [ReadOnly(true)] + [ScaffoldColumn(false)] + [SmartResourceDisplayName("Plugins.Feed.Froogle.Products.ProductName")] + public string Name { get; set; } + + public string SKU { get; set; } + public int ProductTypeId { get; set; } + public ProductType ProductType => (ProductType)ProductTypeId; + public string ProductTypeName { get; set; } + public string ProductTypeLabelHint + { + get + { + switch (ProductType) + { + case ProductType.SimpleProduct: + return "secondary d-none"; + case ProductType.GroupedProduct: + return "success"; + case ProductType.BundledProduct: + return "info"; + default: + return ""; + } + } + } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.Products.GoogleCategory")] + public string Taxonomy { get; set; } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.Gender")] + public string Gender { get; set; } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.AgeGroup")] + public string AgeGroup { get; set; } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.Color")] + public string Color { get; set; } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.Size")] + public string Size { get; set; } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.Material")] + public string Material { get; set; } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.Pattern")] + public string Pattern { get; set; } + + [SmartResourceDisplayName("Common.Export")] + public int Export { get; set; } + [SmartResourceDisplayName("Common.Export")] + public bool Export2 + { + get => Export != 0; + set => Export = (value ? 1 : 0); + } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.Multipack")] + public int Multipack { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.Multipack")] + public int? Multipack2 + { + get => Multipack > 0 ? Multipack : (int?)null; + set => Multipack = (value ?? 0); + } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.IsBundle")] + public bool? IsBundle { get; set; } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.IsAdult")] + public bool? IsAdult { get; set; } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.EnergyEfficiencyClass")] + public string EnergyEfficiencyClass { get; set; } + + [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel0")] + public string CustomLabel0 { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel1")] + public string CustomLabel1 { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel2")] + public string CustomLabel2 { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel3")] + public string CustomLabel3 { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.CustomLabel4")] + public string CustomLabel4 { get; set; } + + public string GenderLocalize { get; set; } + public string AgeGroupLocalize { get; set; } + public string Export2Localize { get; set; } + public string IsBundleLocalize { get; set; } + public string IsAdultLocalize { get; set; } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Models/ProfileConfigurationModel.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Models/ProfileConfigurationModel.cs index 828defcb7d..3d5f89eda8 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Models/ProfileConfigurationModel.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Models/ProfileConfigurationModel.cs @@ -1,72 +1,64 @@ -using FluentValidation; -using FluentValidation.Attributes; -using SmartStore.Web.Framework; -using System; +using System; using System.Collections.Generic; using System.Web.Mvc; using System.Xml.Serialization; +using FluentValidation; +using FluentValidation.Attributes; +using SmartStore.Web.Framework; +using SmartStore.Web.Framework.Modelling; namespace SmartStore.GoogleMerchantCenter.Models { + [CustomModelPart] [Serializable] - [Validator(typeof(ProfileConfigurationValidator))] - public class ProfileConfigurationModel - { - public ProfileConfigurationModel() - { - Condition = "new"; - AdditionalImages = true; - SpecialPrice = true; - } - - [XmlIgnore] - public string LanguageSeoCode { get; set; } - - [XmlIgnore] - public List AvailableCategories { get; set; } + [Validator(typeof(ProfileConfigurationValidator))] + public class ProfileConfigurationModel + { + [XmlIgnore] + public string LanguageSeoCode { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.DefaultGoogleCategory")] - public string DefaultGoogleCategory { get; set; } + [XmlIgnore] + public List AvailableCategories { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.AdditionalImages")] - public bool AdditionalImages { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.DefaultGoogleCategory")] + public string DefaultGoogleCategory { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.Condition")] - public string Condition { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.AdditionalImages")] + public bool AdditionalImages { get; set; } = true; - [SmartResourceDisplayName("Plugins.Feed.Froogle.Availability")] - public string Availability { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.Availability")] + public string Availability { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.SpecialPrice")] - public bool SpecialPrice { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.SpecialPrice")] + public bool SpecialPrice { get; set; } = true; - [SmartResourceDisplayName("Plugins.Feed.Froogle.Gender")] - public string Gender { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.Gender")] + public string Gender { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.AgeGroup")] - public string AgeGroup { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.AgeGroup")] + public string AgeGroup { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.Color")] - public string Color { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.Color")] + public string Color { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.Size")] - public string Size { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.Size")] + public string Size { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.Material")] - public string Material { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.Material")] + public string Material { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.Pattern")] - public string Pattern { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.Pattern")] + public string Pattern { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.ExpirationDays")] - public int ExpirationDays { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.ExpirationDays")] + public int ExpirationDays { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.ExportShipping")] - public bool ExportShipping { get; set; } + [SmartResourceDisplayName("Plugins.Feed.Froogle.ExportShipping")] + public bool ExportShipping { get; set; } - [SmartResourceDisplayName("Plugins.Feed.Froogle.ExportBasePrice")] - public bool ExportBasePrice { get; set; } - } + [SmartResourceDisplayName("Plugins.Feed.Froogle.ExportBasePrice")] + public bool ExportBasePrice { get; set; } + } public class ProfileConfigurationValidator : AbstractValidator { diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs index c2e73c4fc4..6220b658da 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Providers/GmcXmlExportProvider.cs @@ -20,466 +20,462 @@ namespace SmartStore.GoogleMerchantCenter.Providers { - [SystemName("Feeds.GoogleMerchantCenterProductXml")] - [FriendlyName("Google Merchant Center XML product feed")] - [DisplayOrder(1)] - [ExportFeatures(Features = - ExportFeatures.CreatesInitialPublicDeployment | - ExportFeatures.CanOmitGroupedProducts | - ExportFeatures.CanProjectAttributeCombinations | - ExportFeatures.CanProjectDescription | - ExportFeatures.UsesSkuAsMpnFallback | - ExportFeatures.OffersBrandFallback | - ExportFeatures.UsesSpecialPrice | - ExportFeatures.UsesAttributeCombination)] - public class GmcXmlExportProvider : ExportProviderBase - { - private const string _googleNamespace = "http://base.google.com/ns/1.0"; - - private readonly IGoogleFeedService _googleFeedService; - private readonly IMeasureService _measureService; - private readonly ICommonServices _services; - private readonly IProductAttributeService _productAttributeService; - private readonly MeasureSettings _measureSettings; - private Multimap _attributeMappings; - - public GmcXmlExportProvider( - IGoogleFeedService googleFeedService, - IMeasureService measureService, - ICommonServices services, - IProductAttributeService productAttributeService, - MeasureSettings measureSettings) - { - _googleFeedService = googleFeedService; - _measureService = measureService; - _services = services; - _productAttributeService = productAttributeService; - _measureSettings = measureSettings; - - T = NullLocalizer.Instance; - } - - public Localizer T { get; set; } - - private Multimap AttributeMappings - { - get - { - if (_attributeMappings == null) - { - _attributeMappings = _productAttributeService.GetExportFieldMappings("gmc"); - } - - return _attributeMappings; - } - } - - private string BasePriceUnits(string value) - { - const string defaultValue = "kg"; - - if (value.IsEmpty()) - return defaultValue; - - // TODO: Product.BasePriceMeasureUnit should be localized - switch (value.ToLowerInvariant()) - { - case "mg": - case "milligramm": - case "milligram": - return "mg"; - case "g": - case "gramm": - case "gram": - return "g"; - case "kg": - case "kilogramm": - case "kilogram": - return "kg"; - - case "ml": - case "milliliter": - case "millilitre": - return "ml"; - case "cl": - case "zentiliter": - case "centilitre": - return "cl"; - case "l": - case "liter": - case "litre": - return "l"; - case "cbm": - case "kubikmeter": - case "cubic metre": - return "cbm"; - - case "cm": - case "zentimeter": - case "centimetre": - return "cm"; - case "m": - case "meter": - return "m"; - - case "qm²": - case "quadratmeter": - case "square metre": - return "sqm"; - - default: - return defaultValue; - } - } - - private bool BasePriceSupported(int baseAmount, string unit) - { - if (baseAmount == 1 || baseAmount == 10 || baseAmount == 100) - return true; - - if (baseAmount == 75 && unit == "cl") - return true; - - if ((baseAmount == 50 || baseAmount == 1000) && unit == "kg") - return true; - - return false; - } - - private void WriteString(XmlWriter writer, string fieldName, string value) - { - if (value != null) - { - writer.WriteElementString("g", fieldName, _googleNamespace, value); - } - } - - private string GetAttributeValue( - Multimap attributeValues, - string fieldName, - int languageId, - string productEditTabValue, - string defaultValue) - { - // 1. attribute export mapping. - if (attributeValues != null && AttributeMappings.ContainsKey(fieldName)) - { - foreach (var attributeId in AttributeMappings[fieldName]) - { - if (attributeValues.ContainsKey(attributeId)) - { - var attributeValue = attributeValues[attributeId].FirstOrDefault(x => x.ProductVariantAttribute.ProductAttributeId == attributeId); - if (attributeValue != null) - { - return attributeValue.GetLocalized(x => x.Name, languageId, true, false).Value.EmptyNull(); - } - } - } - } - - // 2. explicit set to unspecified. - if (defaultValue.IsCaseInsensitiveEqual(Unspecified)) - { - return string.Empty; - } - - // 3. product edit tab value. - if (productEditTabValue.HasValue()) - { - return productEditTabValue; - } - - return defaultValue.EmptyNull(); - } - - private string GetBaseMeasureWeight() - { - var measureWeightEntity = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId); - var measureWeight = measureWeightEntity != null - ? measureWeightEntity.SystemKeyword.EmptyNull().ToLower() - : string.Empty; - - switch (measureWeight) - { - case "gram": - case "gramme": - return "g"; - case "mg": - case "milligramme": - case "milligram": - return "mg"; - case "lb": - return "lb"; - case "ounce": - case "oz": - return "oz"; - default: - return "kg"; - } - } - - public static string SystemName => "Feeds.GoogleMerchantCenterProductXml"; - - public static string Unspecified => "__nospec__"; - - public override ExportConfigurationInfo ConfigurationInfo - { - get - { - return new ExportConfigurationInfo - { - PartialViewName = "~/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProfileConfiguration.cshtml", - ModelType = typeof(ProfileConfigurationModel), - Initialize = obj => - { - var model = (obj as ProfileConfigurationModel); - - model.LanguageSeoCode = _services.WorkContext.WorkingLanguage.UniqueSeoCode.EmptyNull().ToLower(); - - model.AvailableCategories = model.DefaultGoogleCategory.HasValue() - ? new List { new SelectListItem { Text = model.DefaultGoogleCategory, Value = model.DefaultGoogleCategory, Selected = true } } - : new List(); - } - }; - } - } - - public override string FileExtension => "XML"; - - protected override void Export(ExportExecuteContext context) - { - Currency currency = context.Currency.Entity; - var languageId = context.Projection.LanguageId ?? 0; - var dateFormat = "yyyy-MM-ddTHH:mmZ"; - var defaultCondition = "new"; - var defaultAvailability = "in stock"; - var measureWeight = GetBaseMeasureWeight(); - - var config = (context.ConfigurationData as ProfileConfigurationModel) ?? new ProfileConfigurationModel(); - - if (config.Condition.IsCaseInsensitiveEqual(Unspecified)) - { - defaultCondition = string.Empty; - } - else if (config.Condition.HasValue()) - { - defaultCondition = config.Condition; - } - - if (config.Availability.IsCaseInsensitiveEqual(Unspecified)) - { - defaultAvailability = string.Empty; - } - else if (config.Availability.HasValue()) - { - defaultAvailability = config.Availability; - } - - - using (var writer = XmlWriter.Create(context.DataStream, ExportXmlHelper.DefaultSettings)) - { - writer.WriteStartDocument(); - writer.WriteStartElement("rss"); - writer.WriteAttributeString("version", "2.0"); - writer.WriteAttributeString("xmlns", "g", null, _googleNamespace); - writer.WriteStartElement("channel"); - writer.WriteElementString("title", "{0} - Feed for Google Merchant Center".FormatInvariant((string)context.Store.Name)); - writer.WriteElementString("link", "http://base.google.com/base/"); - writer.WriteElementString("description", "Information about products"); - - while (context.Abort == DataExchangeAbortion.None && context.DataSegmenter.ReadNextSegment()) - { - var segment = context.DataSegmenter.CurrentSegment; - - int[] productIds = segment.Select(x => (int)((dynamic)x).Id).ToArray(); - var googleProducts = _googleFeedService.GetGoogleProductRecords(productIds).ToDictionarySafe(x => x.ProductId); - - foreach (dynamic product in segment) - { - if (context.Abort != DataExchangeAbortion.None) - break; - - Product entity = product.Entity; - var gmc = googleProducts.Get(entity.Id); - - if (gmc != null && !gmc.Export) - continue; - - writer.WriteStartElement("item"); - - try - { - string category = (gmc == null ? null : gmc.Taxonomy); - string productType = product._CategoryPath; - var price = (decimal)product.Price; - var uniqueId = (string)product._UniqueId; - var isParent = (bool)product._IsParent; - string brand = product._Brand; - string gtin = product.Gtin; - string mpn = product.ManufacturerPartNumber; - var availability = defaultAvailability; - List productPictures = product.ProductPictures; - var pictureUrls = productPictures - .Select(x => (string)x.Picture._FullSizeImageUrl) - .Where(x => x.HasValue()) - .ToList(); - - var attributeValues = !isParent && product._AttributeCombinationValues != null - ? ((ICollection)product._AttributeCombinationValues).ToMultimap(x => x.ProductVariantAttribute.ProductAttributeId, x => x) - : new Multimap(); - - var specialPrice = product._FutureSpecialPrice as decimal?; - if (!specialPrice.HasValue) - specialPrice = product._SpecialPrice; - - if (category.IsEmpty()) - category = config.DefaultGoogleCategory; - - if (category.IsEmpty()) - context.Log.Error(T("Plugins.Feed.Froogle.MissingDefaultCategory")); - - if (entity.ManageInventoryMethod == ManageInventoryMethod.ManageStock && entity.StockQuantity <= 0) - { - if (entity.BackorderMode == BackorderMode.NoBackorders) - availability = "out of stock"; - else if (entity.BackorderMode == BackorderMode.AllowQtyBelow0 || entity.BackorderMode == BackorderMode.AllowQtyBelow0AndNotifyCustomer) - availability = entity.AvailableForPreOrder ? "preorder" : "out of stock"; - } - - WriteString(writer, "id", uniqueId); - - writer.WriteStartElement("title"); - writer.WriteCData(((string)product.Name).Truncate(70).RemoveInvalidXmlChars()); - writer.WriteEndElement(); - - writer.WriteStartElement("description"); - writer.WriteCData(((string)product.FullDescription).RemoveInvalidXmlChars()); - writer.WriteEndElement(); - - writer.WriteStartElement("g", "google_product_category", _googleNamespace); - writer.WriteCData(category.RemoveInvalidXmlChars()); - writer.WriteFullEndElement(); - - if (productType.HasValue()) - { - writer.WriteStartElement("g", "product_type", _googleNamespace); - writer.WriteCData(productType.RemoveInvalidXmlChars()); - writer.WriteFullEndElement(); - } - - writer.WriteElementString("link", (string)product._DetailUrl); - - if (pictureUrls.Any()) - { - WriteString(writer, "image_link", pictureUrls.First()); - - if (config.AdditionalImages) - { - var imageCount = 0; - foreach (var url in pictureUrls.Skip(1)) - { - if (++imageCount <= 10) - { - WriteString(writer, "additional_image_link", url); - } - } - } - } - - var condition = GetAttributeValue(attributeValues, "condition", languageId, null, defaultCondition); - WriteString(writer, "condition", condition); - - WriteString(writer, "availability", availability); - - if (availability == "preorder" && entity.AvailableStartDateTimeUtc.HasValue && entity.AvailableStartDateTimeUtc.Value > DateTime.UtcNow) - { - var availabilityDate = entity.AvailableStartDateTimeUtc.Value.ToString(dateFormat); - - WriteString(writer, "availability_date", availabilityDate); - } - - if (config.SpecialPrice && specialPrice.HasValue) - { - WriteString(writer, "sale_price", string.Concat(specialPrice.Value.FormatInvariant(), " ", currency.CurrencyCode)); - - if (entity.SpecialPriceStartDateTimeUtc.HasValue && entity.SpecialPriceEndDateTimeUtc.HasValue) - { - var specialPriceDate = "{0}/{1}".FormatInvariant( - entity.SpecialPriceStartDateTimeUtc.Value.ToString(dateFormat), entity.SpecialPriceEndDateTimeUtc.Value.ToString(dateFormat)); - - WriteString(writer, "sale_price_effective_date", specialPriceDate); - } - - price = (product._RegularPrice as decimal?) ?? price; - } - - WriteString(writer, "price", string.Concat(price.FormatInvariant(), " ", currency.CurrencyCode)); - - WriteString(writer, "gtin", gtin); - WriteString(writer, "brand", brand); - WriteString(writer, "mpn", mpn); - - var identifierExists = brand.HasValue() && (gtin.HasValue() || mpn.HasValue()); - WriteString(writer, "identifier_exists", identifierExists ? "yes" : "no"); - - var gender = GetAttributeValue(attributeValues, "gender", languageId, gmc?.Gender, config.Gender); - WriteString(writer, "gender", gender); - - var ageGroup = GetAttributeValue(attributeValues, "age_group", languageId, gmc?.AgeGroup, config.AgeGroup); - WriteString(writer, "age_group", ageGroup); - - var color = GetAttributeValue(attributeValues, "color", languageId, gmc?.Color, config.Color); - WriteString(writer, "color", color); - - var size = GetAttributeValue(attributeValues, "size", languageId, gmc?.Size, config.Size); - WriteString(writer, "size", size); - - var material = GetAttributeValue(attributeValues, "material", languageId, gmc?.Material, config.Material); - WriteString(writer, "material", material); - - var pattern = GetAttributeValue(attributeValues, "pattern", languageId, gmc?.Pattern, config.Pattern); - WriteString(writer, "pattern", pattern); - - var itemGroupId = gmc != null && gmc.ItemGroupId.HasValue() ? gmc.ItemGroupId : string.Empty; - if (itemGroupId.HasValue()) - { - WriteString(writer, "item_group_id", itemGroupId); - } - - if (config.ExpirationDays > 0) - { - WriteString(writer, "expiration_date", DateTime.UtcNow.AddDays(config.ExpirationDays).ToString("yyyy-MM-dd")); - } - - if (config.ExportShipping) - { - var weight = string.Concat(((decimal)product.Weight).FormatInvariant(), " ", measureWeight); - WriteString(writer, "shipping_weight", weight); - } - - if (config.ExportBasePrice && entity.BasePriceHasValue) - { - var measureUnit = BasePriceUnits((string)product.BasePriceMeasureUnit); - - if (BasePriceSupported(entity.BasePriceBaseAmount ?? 0, measureUnit)) - { - var basePriceMeasure = "{0} {1}".FormatInvariant((entity.BasePriceAmount ?? decimal.Zero).FormatInvariant(), measureUnit); - var basePriceBaseMeasure = "{0} {1}".FormatInvariant(entity.BasePriceBaseAmount ?? 1, measureUnit); - - WriteString(writer, "unit_pricing_measure", basePriceMeasure); - WriteString(writer, "unit_pricing_base_measure", basePriceBaseMeasure); - } - } - - if (gmc != null) - { - WriteString(writer, "multipack", gmc.Multipack > 1 ? gmc.Multipack.ToString() : null); - WriteString(writer, "is_bundle", gmc.IsBundle.HasValue ? (gmc.IsBundle.Value ? "yes" : "no") : null); - WriteString(writer, "adult", gmc.IsAdult.HasValue ? (gmc.IsAdult.Value ? "yes" : "no") : null); - WriteString(writer, "energy_efficiency_class", gmc.EnergyEfficiencyClass.HasValue() ? gmc.EnergyEfficiencyClass : null); - } - - var customLabel0 = GetAttributeValue(attributeValues, "custom_label_0", languageId, gmc?.CustomLabel0, null); + [SystemName("Feeds.GoogleMerchantCenterProductXml")] + [FriendlyName("Google Merchant Center XML product feed")] + [DisplayOrder(1)] + [ExportFeatures(Features = + ExportFeatures.CreatesInitialPublicDeployment | + ExportFeatures.CanOmitGroupedProducts | + ExportFeatures.CanProjectAttributeCombinations | + ExportFeatures.CanProjectDescription | + ExportFeatures.UsesSkuAsMpnFallback | + ExportFeatures.OffersBrandFallback | + ExportFeatures.UsesSpecialPrice | + ExportFeatures.UsesAttributeCombination)] + public class GmcXmlExportProvider : ExportProviderBase + { + private const string _googleNamespace = "http://base.google.com/ns/1.0"; + + private readonly IGoogleFeedService _googleFeedService; + private readonly IMeasureService _measureService; + private readonly ICommonServices _services; + private readonly IProductAttributeService _productAttributeService; + private readonly MeasureSettings _measureSettings; + private Multimap _attributeMappings; + + public GmcXmlExportProvider( + IGoogleFeedService googleFeedService, + IMeasureService measureService, + ICommonServices services, + IProductAttributeService productAttributeService, + MeasureSettings measureSettings) + { + _googleFeedService = googleFeedService; + _measureService = measureService; + _services = services; + _productAttributeService = productAttributeService; + _measureSettings = measureSettings; + + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + private Multimap AttributeMappings + { + get + { + if (_attributeMappings == null) + { + _attributeMappings = _productAttributeService.GetExportFieldMappings("gmc"); + } + + return _attributeMappings; + } + } + + private string BasePriceUnits(string value) + { + const string defaultValue = "kg"; + + if (value.IsEmpty()) + return defaultValue; + + // TODO: Product.BasePriceMeasureUnit should be localized + switch (value.ToLowerInvariant()) + { + case "mg": + case "milligramm": + case "milligram": + return "mg"; + case "g": + case "gramm": + case "gram": + return "g"; + case "kg": + case "kilogramm": + case "kilogram": + return "kg"; + + case "ml": + case "milliliter": + case "millilitre": + return "ml"; + case "cl": + case "zentiliter": + case "centilitre": + return "cl"; + case "l": + case "liter": + case "litre": + return "l"; + case "cbm": + case "kubikmeter": + case "cubic metre": + return "cbm"; + + case "cm": + case "zentimeter": + case "centimetre": + return "cm"; + case "m": + case "meter": + return "m"; + + case "qm²": + case "quadratmeter": + case "square metre": + return "sqm"; + + default: + return defaultValue; + } + } + + private bool BasePriceSupported(int baseAmount, string unit) + { + if (baseAmount == 1 || baseAmount == 10 || baseAmount == 100) + return true; + + if (baseAmount == 75 && unit == "cl") + return true; + + if ((baseAmount == 50 || baseAmount == 1000) && unit == "kg") + return true; + + return false; + } + + private void WriteString(XmlWriter writer, string fieldName, string value) + { + if (value != null) + { + writer.WriteElementString("g", fieldName, _googleNamespace, value); + } + } + + private string GetAttributeValue( + Multimap attributeValues, + string fieldName, + int languageId, + string productEditTabValue, + string defaultValue) + { + // 1. attribute export mapping. + if (attributeValues != null && AttributeMappings.ContainsKey(fieldName)) + { + foreach (var attributeId in AttributeMappings[fieldName]) + { + if (attributeValues.ContainsKey(attributeId)) + { + var attributeValue = attributeValues[attributeId].FirstOrDefault(x => x.ProductVariantAttribute.ProductAttributeId == attributeId); + if (attributeValue != null) + { + return attributeValue.GetLocalized(x => x.Name, languageId, true, false).Value.EmptyNull(); + } + } + } + } + + // 2. explicit set to unspecified. + if (defaultValue.IsCaseInsensitiveEqual(Unspecified)) + { + return string.Empty; + } + + // 3. product edit tab value. + if (productEditTabValue.HasValue()) + { + return productEditTabValue; + } + + return defaultValue.EmptyNull(); + } + + private string GetBaseMeasureWeight() + { + var measureWeightEntity = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId); + var measureWeight = measureWeightEntity != null + ? measureWeightEntity.SystemKeyword.EmptyNull().ToLower() + : string.Empty; + + switch (measureWeight) + { + case "gram": + case "gramme": + return "g"; + case "mg": + case "milligramme": + case "milligram": + return "mg"; + case "lb": + return "lb"; + case "ounce": + case "oz": + return "oz"; + default: + return "kg"; + } + } + + public static string SystemName => "Feeds.GoogleMerchantCenterProductXml"; + + public static string Unspecified => "__nospec__"; + + public override ExportConfigurationInfo ConfigurationInfo => new ExportConfigurationInfo + { + PartialViewName = "~/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProfileConfiguration.cshtml", + ModelType = typeof(ProfileConfigurationModel), + Initialize = obj => + { + var model = (obj as ProfileConfigurationModel); + + model.LanguageSeoCode = _services.WorkContext.WorkingLanguage.UniqueSeoCode.EmptyNull().ToLower(); + + model.AvailableCategories = model.DefaultGoogleCategory.HasValue() + ? new List { new SelectListItem { Text = model.DefaultGoogleCategory, Value = model.DefaultGoogleCategory, Selected = true } } + : new List(); + } + }; + + public override string FileExtension => "XML"; + + protected override void Export(ExportExecuteContext context) + { + Currency currency = context.Currency.Entity; + var languageId = context.Projection.LanguageId ?? 0; + var dateFormat = "yyyy-MM-ddTHH:mmZ"; + var defaultAvailability = "in stock"; + var measureWeight = GetBaseMeasureWeight(); + + var config = (context.ConfigurationData as ProfileConfigurationModel) ?? new ProfileConfigurationModel(); + + if (config.Availability.IsCaseInsensitiveEqual(Unspecified)) + { + defaultAvailability = string.Empty; + } + else if (config.Availability.HasValue()) + { + defaultAvailability = config.Availability; + } + + + using (var writer = XmlWriter.Create(context.DataStream, ExportXmlHelper.DefaultSettings)) + { + writer.WriteStartDocument(); + writer.WriteStartElement("rss"); + writer.WriteAttributeString("version", "2.0"); + writer.WriteAttributeString("xmlns", "g", null, _googleNamespace); + writer.WriteStartElement("channel"); + writer.WriteElementString("title", "{0} - Feed for Google Merchant Center".FormatInvariant((string)context.Store.Name)); + writer.WriteElementString("link", "http://base.google.com/base/"); + writer.WriteElementString("description", "Information about products"); + + while (context.Abort == DataExchangeAbortion.None && context.DataSegmenter.ReadNextSegment()) + { + var segment = context.DataSegmenter.CurrentSegment; + + int[] productIds = segment.Select(x => (int)((dynamic)x).Id).ToArray(); + var googleProducts = _googleFeedService.GetGoogleProductRecords(productIds).ToDictionarySafe(x => x.ProductId); + + foreach (dynamic product in segment) + { + if (context.Abort != DataExchangeAbortion.None) + break; + + Product entity = product.Entity; + var gmc = googleProducts.Get(entity.Id); + + if (gmc != null && !gmc.Export) + continue; + + writer.WriteStartElement("item"); + + try + { + string category = gmc == null ? null : gmc.Taxonomy; + string productType = product._CategoryPath; + var price = (decimal)product.Price; + var uniqueId = (string)product._UniqueId; + var isParent = (bool)product._IsParent; + string brand = product._Brand; + string gtin = product.Gtin; + string mpn = product.ManufacturerPartNumber; + var availability = defaultAvailability; + List productPictures = product.ProductPictures; + var pictureUrls = productPictures + .Select(x => (string)x.Picture._FullSizeImageUrl) + .Where(x => x.HasValue()) + .ToList(); + + var attributeValues = !isParent && product._AttributeCombinationValues != null + ? ((ICollection)product._AttributeCombinationValues).ToMultimap(x => x.ProductVariantAttribute.ProductAttributeId, x => x) + : new Multimap(); + + var specialPrice = product._FutureSpecialPrice as decimal?; + if (!specialPrice.HasValue) + specialPrice = product._SpecialPrice; + + if (category.IsEmpty()) + category = config.DefaultGoogleCategory; + + if (category.IsEmpty()) + context.Log.Error(T("Plugins.Feed.Froogle.MissingDefaultCategory")); + + if (entity.ManageInventoryMethod == ManageInventoryMethod.ManageStock && entity.StockQuantity <= 0) + { + if (entity.BackorderMode == BackorderMode.NoBackorders) + availability = "out of stock"; + else if (entity.BackorderMode == BackorderMode.AllowQtyBelow0 || entity.BackorderMode == BackorderMode.AllowQtyBelow0AndNotifyCustomer) + availability = entity.AvailableForPreOrder ? "preorder" : "out of stock"; + } + + WriteString(writer, "id", uniqueId); + + writer.WriteStartElement("title"); + writer.WriteCData(((string)product.Name).Truncate(70).RemoveInvalidXmlChars()); + writer.WriteEndElement(); + + writer.WriteStartElement("description"); + writer.WriteCData(((string)product.FullDescription).RemoveInvalidXmlChars()); + writer.WriteEndElement(); + + writer.WriteStartElement("g", "google_product_category", _googleNamespace); + writer.WriteCData(category.RemoveInvalidXmlChars()); + writer.WriteFullEndElement(); + + if (productType.HasValue()) + { + writer.WriteStartElement("g", "product_type", _googleNamespace); + writer.WriteCData(productType.RemoveInvalidXmlChars()); + writer.WriteFullEndElement(); + } + + writer.WriteElementString("link", (string)product._DetailUrl); + + if (pictureUrls.Any()) + { + WriteString(writer, "image_link", pictureUrls.First()); + + if (config.AdditionalImages) + { + var imageCount = 0; + foreach (var url in pictureUrls.Skip(1)) + { + if (++imageCount <= 10) + { + WriteString(writer, "additional_image_link", url); + } + } + } + } + + switch (entity.Condition) + { + case ProductCondition.Damaged: + case ProductCondition.Used: + WriteString(writer, "condition", "used"); + break; + case ProductCondition.Refurbished: + WriteString(writer, "condition", "refurbished"); + break; + case ProductCondition.New: + default: + WriteString(writer, "condition", "new"); + break; + } + + WriteString(writer, "availability", availability); + + if (availability == "preorder" && entity.AvailableStartDateTimeUtc.HasValue && entity.AvailableStartDateTimeUtc.Value > DateTime.UtcNow) + { + var availabilityDate = entity.AvailableStartDateTimeUtc.Value.ToString(dateFormat); + + WriteString(writer, "availability_date", availabilityDate); + } + + if (config.SpecialPrice && specialPrice.HasValue) + { + WriteString(writer, "sale_price", string.Concat(specialPrice.Value.FormatInvariant(), " ", currency.CurrencyCode)); + + if (entity.SpecialPriceStartDateTimeUtc.HasValue && entity.SpecialPriceEndDateTimeUtc.HasValue) + { + var specialPriceDate = "{0}/{1}".FormatInvariant( + entity.SpecialPriceStartDateTimeUtc.Value.ToString(dateFormat), entity.SpecialPriceEndDateTimeUtc.Value.ToString(dateFormat)); + + WriteString(writer, "sale_price_effective_date", specialPriceDate); + } + + price = (product._RegularPrice as decimal?) ?? price; + } + + WriteString(writer, "price", string.Concat(price.FormatInvariant(), " ", currency.CurrencyCode)); + + WriteString(writer, "gtin", gtin); + WriteString(writer, "brand", brand); + WriteString(writer, "mpn", mpn); + + var identifierExists = brand.HasValue() && (gtin.HasValue() || mpn.HasValue()); + WriteString(writer, "identifier_exists", identifierExists ? "yes" : "no"); + + var gender = GetAttributeValue(attributeValues, "gender", languageId, gmc?.Gender, config.Gender); + WriteString(writer, "gender", gender); + + var ageGroup = GetAttributeValue(attributeValues, "age_group", languageId, gmc?.AgeGroup, config.AgeGroup); + WriteString(writer, "age_group", ageGroup); + + var color = GetAttributeValue(attributeValues, "color", languageId, gmc?.Color, config.Color); + WriteString(writer, "color", color); + + var size = GetAttributeValue(attributeValues, "size", languageId, gmc?.Size, config.Size); + WriteString(writer, "size", size); + + var material = GetAttributeValue(attributeValues, "material", languageId, gmc?.Material, config.Material); + WriteString(writer, "material", material); + + var pattern = GetAttributeValue(attributeValues, "pattern", languageId, gmc?.Pattern, config.Pattern); + WriteString(writer, "pattern", pattern); + + var itemGroupId = gmc != null && gmc.ItemGroupId.HasValue() ? gmc.ItemGroupId : string.Empty; + if (itemGroupId.HasValue()) + { + WriteString(writer, "item_group_id", itemGroupId); + } + + if (config.ExpirationDays > 0) + { + WriteString(writer, "expiration_date", DateTime.UtcNow.AddDays(config.ExpirationDays).ToString("yyyy-MM-dd")); + } + + if (config.ExportShipping) + { + var weight = string.Concat(((decimal)product.Weight).FormatInvariant(), " ", measureWeight); + WriteString(writer, "shipping_weight", weight); + } + + if (config.ExportBasePrice && entity.BasePriceHasValue) + { + var measureUnit = BasePriceUnits((string)product.BasePriceMeasureUnit); + + if (BasePriceSupported(entity.BasePriceBaseAmount ?? 0, measureUnit)) + { + var basePriceMeasure = "{0} {1}".FormatInvariant((entity.BasePriceAmount ?? decimal.Zero).FormatInvariant(), measureUnit); + var basePriceBaseMeasure = "{0} {1}".FormatInvariant(entity.BasePriceBaseAmount ?? 1, measureUnit); + + WriteString(writer, "unit_pricing_measure", basePriceMeasure); + WriteString(writer, "unit_pricing_base_measure", basePriceBaseMeasure); + } + } + + if (gmc != null) + { + WriteString(writer, "multipack", gmc.Multipack > 1 ? gmc.Multipack.ToString() : null); + WriteString(writer, "is_bundle", gmc.IsBundle.HasValue ? (gmc.IsBundle.Value ? "yes" : "no") : null); + WriteString(writer, "adult", gmc.IsAdult.HasValue ? (gmc.IsAdult.Value ? "yes" : "no") : null); + WriteString(writer, "energy_efficiency_class", gmc.EnergyEfficiencyClass.HasValue() ? gmc.EnergyEfficiencyClass : null); + } + + var customLabel0 = GetAttributeValue(attributeValues, "custom_label_0", languageId, gmc?.CustomLabel0, null); WriteString(writer, "custom_label_0", gmc?.CustomLabel0.NullEmpty()); var customLabel1 = GetAttributeValue(attributeValues, "custom_label_1", languageId, gmc?.CustomLabel1, null); @@ -495,20 +491,26 @@ protected override void Export(ExportExecuteContext context) WriteString(writer, "custom_label_4", gmc?.CustomLabel4.NullEmpty()); ++context.RecordsSucceeded; - } - catch (Exception ex) - { - context.RecordException(ex, entity.Id); - } - - writer.WriteEndElement(); // item - } - } - - writer.WriteEndElement(); // channel - writer.WriteEndElement(); // rss - writer.WriteEndDocument(); - } - } - } + } + catch (OutOfMemoryException ex) + { + context.RecordOutOfMemoryException(ex, entity.Id, T); + context.Abort = DataExchangeAbortion.Hard; + throw; + } + catch (Exception ex) + { + context.RecordException(ex, entity.Id); + } + + writer.WriteEndElement(); // item + } + } + + writer.WriteEndElement(); // channel + writer.WriteEndElement(); // rss + writer.WriteEndDocument(); + } + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/RouteProvider.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/RouteProvider.cs index d0e3b85340..32b09e96ef 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/RouteProvider.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/RouteProvider.cs @@ -8,20 +8,14 @@ public partial class RouteProvider : IRouteProvider { public void RegisterRoutes(RouteCollection routes) { - routes.MapRoute("SmartStore.GoogleMerchantCenter", - "Plugins/SmartStore.GoogleMerchantCenter/{action}", - new { controller = "FeedGoogleMerchantCenter", action = "Configure" }, - new[] { "SmartStore.GoogleMerchantCenter.Controllers" } + routes.MapRoute("SmartStore.GoogleMerchantCenter", + "Plugins/SmartStore.GoogleMerchantCenter/{action}", + new { controller = "FeedGoogleMerchantCenter", action = "Configure" }, + new[] { "SmartStore.GoogleMerchantCenter.Controllers" } ) - .DataTokens["area"] = GoogleMerchantCenterFeedPlugin.SystemName; + .DataTokens["area"] = GoogleMerchantCenterFeedPlugin.SystemName; } - public int Priority - { - get - { - return 0; - } - } + public int Priority => 0; } } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Services/GoogleFeedService.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Services/GoogleFeedService.cs index c2a8ff01a3..d8ea715973 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Services/GoogleFeedService.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Services/GoogleFeedService.cs @@ -16,53 +16,53 @@ namespace SmartStore.GoogleMerchantCenter.Services { - public partial class GoogleFeedService : IGoogleFeedService + public partial class GoogleFeedService : IGoogleFeedService { - private const string _googleNamespace = "http://base.google.com/ns/1.0"; + private const string _googleNamespace = "http://base.google.com/ns/1.0"; private readonly IRepository _gpRepository; - private readonly ICommonServices _services; - private readonly IPluginFinder _pluginFinder; - private readonly ILogger _logger; - - public GoogleFeedService( - IRepository gpRepository, - ICommonServices services, - IPluginFinder pluginFinder, - ILogger logger) + private readonly ICommonServices _services; + private readonly IPluginFinder _pluginFinder; + private readonly ILogger _logger; + + public GoogleFeedService( + IRepository gpRepository, + ICommonServices services, + IPluginFinder pluginFinder, + ILogger logger) { _gpRepository = gpRepository; - _services = services; - _pluginFinder = pluginFinder; - _logger = logger; + _services = services; + _pluginFinder = pluginFinder; + _logger = logger; - T = NullLocalizer.Instance; + T = NullLocalizer.Instance; } - public Localizer T { get; set; } + public Localizer T { get; set; } public GoogleProductRecord GetGoogleProductRecord(int productId) { if (productId == 0) return null; - var query = - from gp in _gpRepository.Table - where gp.ProductId == productId - select gp; + var query = + from gp in _gpRepository.Table + where gp.ProductId == productId + select gp; var record = query.FirstOrDefault(); return record; } - public List GetGoogleProductRecords(int[] productIds) - { - if (productIds == null || productIds.Length == 0) - return new List(); + public List GetGoogleProductRecords(int[] productIds) + { + if (productIds == null || productIds.Length == 0) + return new List(); - var lst = _gpRepository.TableUntracked.Where(x => productIds.Contains(x.ProductId)).ToList(); - return lst; - } + var lst = _gpRepository.TableUntracked.Where(x => productIds.Contains(x.ProductId)).ToList(); + return lst; + } public void InsertGoogleProductRecord(GoogleProductRecord record) { @@ -72,270 +72,271 @@ public void InsertGoogleProductRecord(GoogleProductRecord record) _gpRepository.Insert(record); } - public void UpdateGoogleProductRecord(GoogleProductRecord record) + public void UpdateGoogleProductRecord(GoogleProductRecord record) + { + if (record == null) + throw new ArgumentNullException("record"); + + _gpRepository.Update(record); + } + + public void DeleteGoogleProductRecord(GoogleProductRecord record) + { + if (record == null) + throw new ArgumentNullException("record"); + + _gpRepository.Delete(record); + } + + public void Upsert(int pk, string name, string value) { - if (record == null) - throw new ArgumentNullException("record"); + if (pk == 0 || name.IsEmpty()) + return; + + var product = GetGoogleProductRecord(pk); + var insert = (product == null); + var utcNow = DateTime.UtcNow; + + if (product == null) + { + product = new GoogleProductRecord + { + ProductId = pk, + CreatedOnUtc = utcNow + }; + } + + switch (name) + { + case "Taxonomy": + product.Taxonomy = value; + break; + case "Gender": + product.Gender = value; + break; + case "AgeGroup": + product.AgeGroup = value; + break; + case "Color": + product.Color = value; + break; + case "Size": + product.Size = value; + break; + case "Material": + product.Material = value; + break; + case "Pattern": + product.Pattern = value; + break; + case "Export2": + product.Export = value.ToBool(true); + break; + case "Multipack2": + product.Multipack = value.ToInt(); + break; + case "IsBundle": + product.IsBundle = (value.IsEmpty() ? (bool?)null : value.ToBool()); + break; + case "IsAdult": + product.IsAdult = (value.IsEmpty() ? (bool?)null : value.ToBool()); + break; + case "EnergyEfficiencyClass": + product.EnergyEfficiencyClass = value; + break; + case "CustomLabel0": + product.CustomLabel0 = value; + break; + case "CustomLabel1": + product.CustomLabel1 = value; + break; + case "CustomLabel2": + product.CustomLabel2 = value; + break; + case "CustomLabel3": + product.CustomLabel3 = value; + break; + case "CustomLabel4": + product.CustomLabel4 = value; + break; + } + + product.UpdatedOnUtc = utcNow; + product.IsTouched = product.IsTouched(); + + if (!insert && !product.IsTouched) + { + _gpRepository.Delete(product); + return; + } + + if (insert) + { + _gpRepository.Insert(product); + } + else + { + _gpRepository.Update(product); + } + } - _gpRepository.Update(record); + public GridModel GetGridModel(GridCommand command, string searchProductName = null, string touched = null) + { + var model = new GridModel(); + var textInfo = CultureInfo.InvariantCulture.TextInfo; + string yes = T("Admin.Common.Yes"); + string no = T("Admin.Common.No"); + + // there's no way to share a context instance across repositories in EF. + // so we have to fallback to pure SQL here to get the data paged and filtered. + + var hidden = (int)ProductVisibility.Hidden; + var whereClause = new StringBuilder($"(NOT ([t2].[Deleted] = 1)) AND ([t2].[Visibility] <> {hidden})"); + + if (searchProductName.HasValue()) + { + whereClause.AppendFormat(" AND ([t2].[Name] LIKE '%{0}%')", searchProductName.Replace("'", "''")); + } + + if (touched.HasValue()) + { + if (touched.IsCaseInsensitiveEqual("touched")) + whereClause.Append(" AND ([t2].[IsTouched] = 1)"); + else + whereClause.Append(" AND ([t2].[IsTouched] = 0 OR [t2].[IsTouched] IS NULL)"); + } + + string sql = null; + string sqlCount = null; + var isSqlServer = DataSettings.Current.IsSqlServer; + + if (isSqlServer) + { + // fastest possible paged data query + sql = + "SELECT [TotalCount], [t3].[Id], [t3].[Name], [t3].[SKU], [t3].[ProductTypeId], [t3].[value] AS [Taxonomy], [t3].[value2] AS [Gender], [t3].[value3] AS [AgeGroup], [t3].[value4] AS [Color], [t3].[value5] AS [Size], [t3].[value6] AS [Material], [t3].[value7] AS [Pattern], [t3].[value8] AS [Export], [t3].[value9] AS [Multipack], [t3].[value10] AS [IsBundle], [t3].[value11] AS [IsAdult], [t3].[value12] AS [EnergyEfficiencyClass], [t3].[value13] AS [CustomLabel0], [t3].[value14] AS [CustomLabel1], [t3].[value15] AS [CustomLabel2], [t3].[value16] AS [CustomLabel3], [t3].[value17] AS [CustomLabel4]" + + " FROM (" + + " SELECT COUNT(id) OVER() [TotalCount], ROW_NUMBER() OVER (ORDER BY [t2].[Name]) AS [ROW_NUMBER], [t2].[Id], [t2].[Name], [t2].[SKU], [t2].[ProductTypeId], [t2].[value], [t2].[value2], [t2].[value3], [t2].[value4], [t2].[value5], [t2].[value6], [t2].[value7], [t2].[value8], [t2].[value9], [t2].[value10], [t2].[value11], [t2].[value12], [t2].[value13], [t2].[value14], [t2].[value15], [t2].[value16], [t2].[value17]" + + " FROM (" + + " SELECT [t0].[Id], [t0].[Name], [t0].[SKU], [t0].[ProductTypeId], [t1].[Taxonomy] AS [value], [t1].[Gender] AS [value2], [t1].[AgeGroup] AS [value3], [t1].[Color] AS [value4], [t1].[Size] AS [value5], [t1].[Material] AS [value6], [t1].[Pattern] AS [value7], COALESCE([t1].[Export],1) AS [value8], COALESCE([t1].[Multipack],0) AS [value9], [t1].[IsBundle] AS [value10], [t1].[IsAdult] AS [value11], [t1].[EnergyEfficiencyClass] AS [value12], [t1].[CustomLabel0] AS [value13], [t1].[CustomLabel1] AS [value14], [t1].[CustomLabel2] AS [value15], [t1].[CustomLabel3] AS [value16], [t1].[CustomLabel4] AS [value17], [t0].[Deleted], [t0].[Visibility], [t1].[IsTouched]" + + " FROM [Product] AS [t0]" + + " LEFT OUTER JOIN [GoogleProduct] AS [t1] ON [t0].[Id] = [t1].[ProductId]" + + " ) AS [t2]" + + " WHERE " + whereClause.ToString() + + " ) AS [t3]" + + " WHERE [t3].[ROW_NUMBER] BETWEEN {0} + 1 AND {0} + {1}" + + " ORDER BY [t3].[ROW_NUMBER]"; + } + else + { + // OFFSET... FETCH NEXT requires SQL Server 2012 or SQL CE 4 + sql = + "SELECT [t2].[Id], [t2].[Name], [t2].[SKU], [t2].[ProductTypeId], [t2].[value] AS [Taxonomy], [t2].[value2] AS [Gender], [t2].[value3] AS [AgeGroup], [t2].[value4] AS [Color], [t2].[value5] AS [Size], [t2].[value6] AS [Material], [t2].[value7] AS [Pattern], [t2].[value8] AS [Export], [t2].[value9] AS [Multipack], [t2].[value10] AS [IsBundle], [t2].[value11] AS [IsAdult], [t2].[value12] AS [EnergyEfficiencyClass], [t2].[value13] AS [CustomLabel0], [t2].[value14] AS [CustomLabel1], [t2].[value15] AS [CustomLabel2], [t2].[value16] AS [CustomLabel3], [t2].[value17] AS [CustomLabel4]" + + " FROM (" + + " SELECT [t0].[Id], [t0].[Name], [t0].[SKU], [t0].[ProductTypeId], [t1].[Taxonomy] AS [value], [t1].[Gender] AS [value2], [t1].[AgeGroup] AS [value3], [t1].[Color] AS [value4], [t1].[Size] AS [value5], [t1].[Material] AS [value6], [t1].[Pattern] AS [value7], COALESCE([t1].[Export],1) AS [value8], COALESCE([t1].[Multipack],0) AS [value9], [t1].[IsBundle] AS [value10], [t1].[IsAdult] AS [value11], [t1].[EnergyEfficiencyClass] AS [value12], [t1].[CustomLabel0] AS [value13], [t1].[CustomLabel1] AS [value14], [t1].[CustomLabel2] AS [value15], [t1].[CustomLabel3] AS [value16], [t1].[CustomLabel4] AS [value17], [t0].[Deleted], [t0].[Visibility], [t1].[IsTouched] AS [IsTouched]" + + " FROM [Product] AS [t0]" + + " LEFT OUTER JOIN [GoogleProduct] AS [t1] ON [t0].[Id] = [t1].[ProductId]" + + " ) AS [t2]" + + " WHERE " + whereClause.ToString() + + " ORDER BY [t2].[Name]" + + " OFFSET {0} ROWS FETCH NEXT {1} ROWS ONLY"; + + sqlCount = + "SELECT COUNT(*)" + + " FROM (" + + " SELECT [t0].[Id], [t0].[Name], [t0].[Deleted], [t0].[Visibility], [t1].[IsTouched] AS [IsTouched]" + + " FROM [Product] AS [t0]" + + " LEFT OUTER JOIN [GoogleProduct] AS [t1] ON [t0].[Id] = [t1].[ProductId]" + + " ) AS [t2]" + + " WHERE " + whereClause.ToString(); + } + + + var data = _gpRepository.Context.SqlQuery(sql, (command.Page - 1) * command.PageSize, command.PageSize).ToList(); + + data.ForEach(x => + { + if (x.ProductType != ProductType.SimpleProduct) + x.ProductTypeName = T("Admin.Catalog.Products.ProductType.{0}.Label".FormatInvariant(x.ProductType.ToString())); + + if (x.Gender.HasValue()) + x.GenderLocalize = T("Plugins.Feed.Froogle.Gender" + textInfo.ToTitleCase(x.Gender)); + + if (x.AgeGroup.HasValue()) + x.AgeGroupLocalize = T("Plugins.Feed.Froogle.AgeGroup" + textInfo.ToTitleCase(x.AgeGroup)); + + x.Export2Localize = (x.Export == 0 ? no : yes); + + if (x.IsBundle.HasValue) + x.IsBundleLocalize = (x.IsBundle.Value ? yes : no); + else + x.IsBundleLocalize = null; + + if (x.IsAdult.HasValue) + x.IsAdultLocalize = (x.IsAdult.Value ? yes : no); + else + x.IsAdultLocalize = null; + }); + + model.Data = data; + model.Total = (data.Count > 0 ? data.First().TotalCount : 0); + + if (data.Count > 0) + { + if (isSqlServer) + model.Total = data.First().TotalCount; + else + model.Total = _gpRepository.Context.SqlQuery(sqlCount).FirstOrDefault(); + } + else + { + model.Total = 0; + } + + return model; } - public void DeleteGoogleProductRecord(GoogleProductRecord record) - { - if (record == null) - throw new ArgumentNullException("record"); - - _gpRepository.Delete(record); - } - - public void Upsert(int pk, string name, string value) - { - if (pk == 0 || name.IsEmpty()) - return; - - var product = GetGoogleProductRecord(pk); - var insert = (product == null); - var utcNow = DateTime.UtcNow; - - if (product == null) - { - product = new GoogleProductRecord - { - ProductId = pk, - CreatedOnUtc = utcNow - }; - } - - switch (name) - { - case "Taxonomy": - product.Taxonomy = value; - break; - case "Gender": - product.Gender = value; - break; - case "AgeGroup": - product.AgeGroup = value; - break; - case "Color": - product.Color = value; - break; - case "Size": - product.Size = value; - break; - case "Material": - product.Material = value; - break; - case "Pattern": - product.Pattern = value; - break; - case "Export2": - product.Export = value.ToBool(true); - break; - case "Multipack2": - product.Multipack = value.ToInt(); - break; - case "IsBundle": - product.IsBundle = (value.IsEmpty() ? (bool?)null : value.ToBool()); - break; - case "IsAdult": - product.IsAdult = (value.IsEmpty() ? (bool?)null : value.ToBool()); - break; - case "EnergyEfficiencyClass": - product.EnergyEfficiencyClass = value; - break; - case "CustomLabel0": - product.CustomLabel0 = value; - break; - case "CustomLabel1": - product.CustomLabel1 = value; - break; - case "CustomLabel2": - product.CustomLabel2 = value; - break; - case "CustomLabel3": - product.CustomLabel3 = value; - break; - case "CustomLabel4": - product.CustomLabel4 = value; - break; - } - - product.UpdatedOnUtc = utcNow; - product.IsTouched = product.IsTouched(); - - if (!insert && !product.IsTouched) - { - _gpRepository.Delete(product); - return; - } - - if (insert) - { - _gpRepository.Insert(product); - } - else - { - _gpRepository.Update(product); - } - } - - public GridModel GetGridModel(GridCommand command, string searchProductName = null, string touched = null) - { - var model = new GridModel(); - var textInfo = CultureInfo.InvariantCulture.TextInfo; - string yes = T("Admin.Common.Yes"); - string no = T("Admin.Common.No"); - - // there's no way to share a context instance across repositories in EF. - // so we have to fallback to pure SQL here to get the data paged and filtered. - - var whereClause = new StringBuilder("(NOT ([t2].[Deleted] = 1)) AND ([t2].[VisibleIndividually] = 1)"); - - if (searchProductName.HasValue()) - { - whereClause.AppendFormat(" AND ([t2].[Name] LIKE '%{0}%')", searchProductName.Replace("'", "''")); - } - - if (touched.HasValue()) - { - if (touched.IsCaseInsensitiveEqual("touched")) - whereClause.Append(" AND ([t2].[IsTouched] = 1)"); - else - whereClause.Append(" AND ([t2].[IsTouched] = 0 OR [t2].[IsTouched] IS NULL)"); - } - - string sql = null; - string sqlCount = null; - var isSqlServer = DataSettings.Current.IsSqlServer; - - if (isSqlServer) - { - // fastest possible paged data query - sql = - "SELECT [TotalCount], [t3].[Id], [t3].[Name], [t3].[SKU], [t3].[ProductTypeId], [t3].[value] AS [Taxonomy], [t3].[value2] AS [Gender], [t3].[value3] AS [AgeGroup], [t3].[value4] AS [Color], [t3].[value5] AS [Size], [t3].[value6] AS [Material], [t3].[value7] AS [Pattern], [t3].[value8] AS [Export], [t3].[value9] AS [Multipack], [t3].[value10] AS [IsBundle], [t3].[value11] AS [IsAdult], [t3].[value12] AS [EnergyEfficiencyClass], [t3].[value13] AS [CustomLabel0], [t3].[value14] AS [CustomLabel1], [t3].[value15] AS [CustomLabel2], [t3].[value16] AS [CustomLabel3], [t3].[value17] AS [CustomLabel4]" + - " FROM (" + - " SELECT COUNT(id) OVER() [TotalCount], ROW_NUMBER() OVER (ORDER BY [t2].[Name]) AS [ROW_NUMBER], [t2].[Id], [t2].[Name], [t2].[SKU], [t2].[ProductTypeId], [t2].[value], [t2].[value2], [t2].[value3], [t2].[value4], [t2].[value5], [t2].[value6], [t2].[value7], [t2].[value8], [t2].[value9], [t2].[value10], [t2].[value11], [t2].[value12], [t2].[value13], [t2].[value14], [t2].[value15], [t2].[value16], [t2].[value17]" + - " FROM (" + - " SELECT [t0].[Id], [t0].[Name], [t0].[SKU], [t0].[ProductTypeId], [t1].[Taxonomy] AS [value], [t1].[Gender] AS [value2], [t1].[AgeGroup] AS [value3], [t1].[Color] AS [value4], [t1].[Size] AS [value5], [t1].[Material] AS [value6], [t1].[Pattern] AS [value7], COALESCE([t1].[Export],1) AS [value8], COALESCE([t1].[Multipack],0) AS [value9], [t1].[IsBundle] AS [value10], [t1].[IsAdult] AS [value11], [t1].[EnergyEfficiencyClass] AS [value12], [t1].[CustomLabel0] AS [value13], [t1].[CustomLabel1] AS [value14], [t1].[CustomLabel2] AS [value15], [t1].[CustomLabel3] AS [value16], [t1].[CustomLabel4] AS [value17], [t0].[Deleted], [t0].[VisibleIndividually], [t1].[IsTouched]" + - " FROM [Product] AS [t0]" + - " LEFT OUTER JOIN [GoogleProduct] AS [t1] ON [t0].[Id] = [t1].[ProductId]" + - " ) AS [t2]" + - " WHERE " + whereClause.ToString() + - " ) AS [t3]" + - " WHERE [t3].[ROW_NUMBER] BETWEEN {0} + 1 AND {0} + {1}" + - " ORDER BY [t3].[ROW_NUMBER]"; - } - else - { - // OFFSET... FETCH NEXT requires SQL Server 2012 or SQL CE 4 - sql = - "SELECT [t2].[Id], [t2].[Name], [t2].[SKU], [t2].[ProductTypeId], [t2].[value] AS [Taxonomy], [t2].[value2] AS [Gender], [t2].[value3] AS [AgeGroup], [t2].[value4] AS [Color], [t2].[value5] AS [Size], [t2].[value6] AS [Material], [t2].[value7] AS [Pattern], [t2].[value8] AS [Export], [t2].[value9] AS [Multipack], [t2].[value10] AS [IsBundle], [t2].[value11] AS [IsAdult], [t2].[value12] AS [EnergyEfficiencyClass], [t2].[value13] AS [CustomLabel0], [t2].[value14] AS [CustomLabel1], [t2].[value15] AS [CustomLabel2], [t2].[value16] AS [CustomLabel3], [t2].[value17] AS [CustomLabel4]" + - " FROM (" + - " SELECT [t0].[Id], [t0].[Name], [t0].[SKU], [t0].[ProductTypeId], [t1].[Taxonomy] AS [value], [t1].[Gender] AS [value2], [t1].[AgeGroup] AS [value3], [t1].[Color] AS [value4], [t1].[Size] AS [value5], [t1].[Material] AS [value6], [t1].[Pattern] AS [value7], COALESCE([t1].[Export],1) AS [value8], COALESCE([t1].[Multipack],0) AS [value9], [t1].[IsBundle] AS [value10], [t1].[IsAdult] AS [value11], [t1].[EnergyEfficiencyClass] AS [value12], [t1].[CustomLabel0] AS [value13], [t1].[CustomLabel1] AS [value14], [t1].[CustomLabel2] AS [value15], [t1].[CustomLabel3] AS [value16], [t1].[CustomLabel4] AS [value17], [t0].[Deleted], [t0].[VisibleIndividually], [t1].[IsTouched] AS [IsTouched]" + - " FROM [Product] AS [t0]" + - " LEFT OUTER JOIN [GoogleProduct] AS [t1] ON [t0].[Id] = [t1].[ProductId]" + - " ) AS [t2]" + - " WHERE " + whereClause.ToString() + - " ORDER BY [t2].[Name]" + - " OFFSET {0} ROWS FETCH NEXT {1} ROWS ONLY"; - - sqlCount = - "SELECT COUNT(*)" + - " FROM (" + - " SELECT [t0].[Id], [t0].[Name], [t0].[Deleted], [t0].[VisibleIndividually], [t1].[IsTouched] AS [IsTouched]" + - " FROM [Product] AS [t0]" + - " LEFT OUTER JOIN [GoogleProduct] AS [t1] ON [t0].[Id] = [t1].[ProductId]" + - " ) AS [t2]" + - " WHERE " + whereClause.ToString(); - } - - - var data = _gpRepository.Context.SqlQuery(sql, (command.Page - 1) * command.PageSize, command.PageSize).ToList(); - - data.ForEach(x => - { - if (x.ProductType != ProductType.SimpleProduct) - x.ProductTypeName = T("Admin.Catalog.Products.ProductType.{0}.Label".FormatInvariant(x.ProductType.ToString())); - - if (x.Gender.HasValue()) - x.GenderLocalize = T("Plugins.Feed.Froogle.Gender" + textInfo.ToTitleCase(x.Gender)); - - if (x.AgeGroup.HasValue()) - x.AgeGroupLocalize = T("Plugins.Feed.Froogle.AgeGroup" + textInfo.ToTitleCase(x.AgeGroup)); - - x.Export2Localize = (x.Export == 0 ? no : yes); - - if (x.IsBundle.HasValue) - x.IsBundleLocalize = (x.IsBundle.Value ? yes : no); - else - x.IsBundleLocalize = null; - - if (x.IsAdult.HasValue) - x.IsAdultLocalize = (x.IsAdult.Value ? yes : no); - else - x.IsAdultLocalize = null; - }); - - model.Data = data; - model.Total = (data.Count > 0 ? data.First().TotalCount : 0); - - if (data.Count > 0) - { - if (isSqlServer) - model.Total = data.First().TotalCount; - else - model.Total = _gpRepository.Context.SqlQuery(sqlCount).FirstOrDefault(); - } - else - { - model.Total = 0; - } - - return model; - } - - public List GetTaxonomyList(string searchTerm) - { - var result = new List(); - - try - { - var descriptor = _pluginFinder.GetPluginDescriptorBySystemName(GoogleMerchantCenterFeedPlugin.SystemName); - var fileDir = Path.Combine(descriptor.Assembly.OriginalFile.Directory.FullName, "Files"); - var fileName = "taxonomy.{0}.txt".FormatInvariant(_services.WorkContext.WorkingLanguage.LanguageCulture ?? "de-DE"); - var path = Path.Combine(fileDir, fileName); - var filter = searchTerm.HasValue(); - string line; - - if (!File.Exists(path)) - { - path = Path.Combine(fileDir, "taxonomy.en-US.txt"); - } - - using (var file = new StreamReader(path, Encoding.UTF8)) - { - while ((line = file.ReadLine()) != null) - { - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - if (filter && line.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) < 0) - { - continue; - } - - result.Add(line); - } - } - } - catch (Exception exc) - { - _logger.Error(exc); - } - - return result; - } + public List GetTaxonomyList(string searchTerm) + { + var result = new List(); + + try + { + var descriptor = _pluginFinder.GetPluginDescriptorBySystemName(GoogleMerchantCenterFeedPlugin.SystemName); + var fileDir = Path.Combine(descriptor.Assembly.OriginalFile.Directory.FullName, "Files"); + var fileName = "taxonomy.{0}.txt".FormatInvariant(_services.WorkContext.WorkingLanguage.LanguageCulture ?? "de-DE"); + var path = Path.Combine(fileDir, fileName); + var filter = searchTerm.HasValue(); + string line; + + if (!File.Exists(path)) + { + path = Path.Combine(fileDir, "taxonomy.en-US.txt"); + } + + using (var file = new StreamReader(path, Encoding.UTF8)) + { + while ((line = file.ReadLine()) != null) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + if (filter && line.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) < 0) + { + continue; + } + + result.Add(line); + } + } + } + catch (Exception exc) + { + _logger.Error(exc); + } + + return result; + } } } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Services/IGoogleFeedService.cs b/src/Plugins/SmartStore.GoogleMerchantCenter/Services/IGoogleFeedService.cs index cfd2bc5f06..6ea687c13b 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Services/IGoogleFeedService.cs +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Services/IGoogleFeedService.cs @@ -5,22 +5,22 @@ namespace SmartStore.GoogleMerchantCenter.Services { - public partial interface IGoogleFeedService + public partial interface IGoogleFeedService { - GoogleProductRecord GetGoogleProductRecord(int productId); + GoogleProductRecord GetGoogleProductRecord(int productId); - List GetGoogleProductRecords(int[] productIds); + List GetGoogleProductRecords(int[] productIds); - void InsertGoogleProductRecord(GoogleProductRecord record); + void InsertGoogleProductRecord(GoogleProductRecord record); - void UpdateGoogleProductRecord(GoogleProductRecord record); - - void DeleteGoogleProductRecord(GoogleProductRecord record); + void UpdateGoogleProductRecord(GoogleProductRecord record); - void Upsert(int pk, string name, string value); + void DeleteGoogleProductRecord(GoogleProductRecord record); - GridModel GetGridModel(GridCommand command, string searchProductName = null, string touched = null); + void Upsert(int pk, string name, string value); - List GetTaxonomyList(string searchTerm); + GridModel GetGridModel(GridCommand command, string searchProductName = null, string touched = null); + + List GetTaxonomyList(string searchTerm); } } diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj b/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj index 0a93c5cd18..b6ee05227a 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/SmartStore.GoogleMerchantCenter.csproj @@ -1,6 +1,5 @@  - @@ -21,7 +20,7 @@ Properties SmartStore.GoogleMerchantCenter SmartStore.GoogleMerchantCenter - v4.6.1 + v4.7.2 512 @@ -86,44 +85,57 @@ MinimumRecommendedRules.ruleset - - ..\..\packages\Autofac.4.9.0\lib\net45\Autofac.dll + + ..\..\packages\Autofac.5.2.0\lib\net461\Autofac.dll False - - ..\..\packages\Autofac.Mvc5.4.0.2\lib\net45\Autofac.Integration.Mvc.dll + + ..\..\packages\Autofac.Mvc5.5.0.0\lib\net461\Autofac.Integration.Mvc.dll False - ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + ..\..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll False - ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + ..\..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.SqlServer.dll False ..\..\packages\FluentValidation.7.4.0\lib\net45\FluentValidation.dll False + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + False + ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll False - - ..\..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll - False + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + - + ..\..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.Helpers.dll False diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProductEditTab.cshtml b/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProductEditTab.cshtml index af40550606..a3e42dcc2e 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProductEditTab.cshtml +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProductEditTab.cshtml @@ -23,7 +23,7 @@ @Html.SmartLabelFor(m => m.Taxonomy)
              - @Html.DropDownListFor(model => model.Taxonomy, (IEnumerable)ViewBag.AvailableCategories, new { autocomplete = "off" }) + @Html.DropDownListFor(model => model.Taxonomy, (IEnumerable)ViewBag.AvailableCategories) @Html.ValidationMessageFor(m => m.Taxonomy)
              - diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProfileConfiguration.cshtml b/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProfileConfiguration.cshtml index d3352b5bc6..16aab3b239 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProfileConfiguration.cshtml +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/Views/FeedGoogleMerchantCenter/ProfileConfiguration.cshtml @@ -66,7 +66,7 @@ @Html.SmartLabelFor(m => m.DefaultGoogleCategory)
              - @Html.DropDownListFor(model => model.DefaultGoogleCategory, Model.AvailableCategories, new { autocomplete = "off" }) + @Html.DropDownListFor(model => model.DefaultGoogleCategory, Model.AvailableCategories) @Html.ValidationMessageFor(m => m.DefaultGoogleCategory)
              - @Html.SmartLabelFor(m => m.Condition) - - @Html.DropDownList("Condition", new List - { - new SelectListItem { Text = T("Common.Auto"), Value = "" }, - new SelectListItem { Text = T("Common.Unspecified"), Value = GmcXmlExportProvider.Unspecified }, - new SelectListItem { Text = T("Plugins.Feed.Froogle.ConditionNew"), Value = "new" }, - new SelectListItem { Text = T("Plugins.Feed.Froogle.ConditionUsed"), Value = "used" }, - new SelectListItem { Text = T("Plugins.Feed.Froogle.ConditionRefurbished"), Value = "refurbished" } - }) - @Html.ValidationMessageFor(m => m.Condition) -
              @Html.SmartLabelFor(m => m.Availability) @@ -180,9 +164,8 @@
              - diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/packages.config b/src/Plugins/SmartStore.GoogleMerchantCenter/packages.config index aeca1e30ef..2000cd5d23 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/packages.config +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/packages.config @@ -1,12 +1,16 @@  - - - + + + + + - + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.GoogleMerchantCenter/web.config b/src/Plugins/SmartStore.GoogleMerchantCenter/web.config index 522d79a602..b15e514586 100644 --- a/src/Plugins/SmartStore.GoogleMerchantCenter/web.config +++ b/src/Plugins/SmartStore.GoogleMerchantCenter/web.config @@ -1,149 +1,163 @@ - + - - + + + - - + - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Plugins/SmartStore.OfflinePayment/Controllers/OfflinePaymentController.cs b/src/Plugins/SmartStore.OfflinePayment/Controllers/OfflinePaymentController.cs index 3bbdb6c1d7..e7af8235c0 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Controllers/OfflinePaymentController.cs +++ b/src/Plugins/SmartStore.OfflinePayment/Controllers/OfflinePaymentController.cs @@ -1,4 +1,9 @@ -using Autofac; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using Autofac; using FluentValidation; using FluentValidation.Results; using SmartStore.OfflinePayment.Models; @@ -10,513 +15,524 @@ using SmartStore.Web.Framework.Security; using SmartStore.Web.Framework.Settings; using SmartStore.Web.Framework.Theming; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; namespace SmartStore.OfflinePayment.Controllers { - public class OfflinePaymentController : PaymentControllerBase { - private readonly IComponentContext _ctx; - private readonly HttpContextBase _httpContext; - private readonly IPictureService _pictureService; - - public OfflinePaymentController( - HttpContextBase httpContext, - IComponentContext ctx, - IPictureService pictureService) + private readonly IComponentContext _ctx; + private readonly HttpContextBase _httpContext; + private readonly IMediaService _mediaService; + + public OfflinePaymentController( + HttpContextBase httpContext, + IComponentContext ctx, + IMediaService mediaService) { - _httpContext = httpContext; - _ctx = ctx; - _pictureService = pictureService; - } - - #region Global - - private List GetTransactModes() - { - var list = new List - { - new SelectListItem { Text = T("Enums.SmartStore.Core.Domain.Payments.PaymentStatus.Pending"), Value = ((int)TransactMode.Pending).ToString() }, - new SelectListItem { Text = T("Enums.SmartStore.Core.Domain.Payments.PaymentStatus.Authorized"), Value = ((int)TransactMode.Authorize).ToString() }, - new SelectListItem { Text = T("Enums.SmartStore.Core.Domain.Payments.PaymentStatus.Paid"), Value = ((int)TransactMode.Paid).ToString() } - }; - - return list; - } - - [NonAction] - private TModel ConfigureGet(Action fn = null) - where TModel : ConfigurationModelBase, new() - where TSetting : PaymentSettingsBase, new() - { - var model = new TModel(); - - var storeScope = this.GetActiveStoreScopeConfiguration(Services.StoreService, Services.WorkContext); - var settings = Services.Settings.LoadSetting(storeScope); - var store = storeScope == 0 - ? Services.StoreContext.CurrentStore - : Services.StoreService.GetStoreById(storeScope); - - model.PrimaryStoreCurrencyCode = store.PrimaryStoreCurrency.CurrencyCode; - model.DescriptionText = settings.DescriptionText; - model.PaymentMethodLogo = settings.ThumbnailPictureId; - model.AdditionalFee = settings.AdditionalFee; - model.AdditionalFeePercentage = settings.AdditionalFeePercentage; - - if (fn != null) - { - fn(model, settings); - } - - var storeDependingSettingHelper = new StoreDependingSettingHelper(ViewData); - storeDependingSettingHelper.GetOverrideKeys(settings, model, storeScope, Services.Settings); - - return model; - } - - [NonAction] - private void ConfigurePost(TModel model, FormCollection form, Action fn = null) - where TModel : ConfigurationModelBase, new() - where TSetting : PaymentSettingsBase, new() - { - ModelState.Clear(); - - var storeDependingSettingHelper = new StoreDependingSettingHelper(ViewData); - int storeScope = this.GetActiveStoreScopeConfiguration(Services.StoreService, Services.WorkContext); - var settings = Services.Settings.LoadSetting(storeScope); - - settings.DescriptionText = model.DescriptionText; - settings.ThumbnailPictureId = model.PaymentMethodLogo; - settings.AdditionalFee = model.AdditionalFee; - settings.AdditionalFeePercentage = model.AdditionalFeePercentage; - - if (fn != null) - { - fn(settings); - } - - using (Services.Settings.BeginScope()) - { - storeDependingSettingHelper.UpdateSettings(settings, form, storeScope, Services.Settings); - } - - NotifySuccess(T("Admin.Common.DataSuccessfullySaved")); - } - - [NonAction] - private TModel PaymentInfoGet(Action fn = null) - where TModel : PaymentInfoModelBase, new() - where TSetting : PaymentSettingsBase, new() - { - var settings = _ctx.Resolve(); - var model = new TModel(); - model.DescriptionText = GetLocalizedText(settings.DescriptionText); - model.ThumbnailUrl = _pictureService.GetUrl(settings.ThumbnailPictureId, 120, false); - - if (fn != null) - { - fn(model, settings); - } - - return model; - } - - private string GetLocalizedText(string text) - { - if (text.EmptyNull().StartsWith("@")) - { - return T(text.Substring(1)); - } - - return text; - } - - [NonAction] - public override IList ValidatePaymentForm(FormCollection form) - { - var warnings = new List(); - IValidator validator; - ValidationResult validationResult = null; - - string type = form["OfflinePaymentMethodType"].NullEmpty(); - - if (type.HasValue()) - { - if (type == "Manual") - { - validator = new ManualPaymentInfoValidator(T); - var model = new ManualPaymentInfoModel - { - CardholderName = form["CardholderName"], - CardNumber = form["CardNumber"], - CardCode = form["CardCode"] - }; - validationResult = validator.Validate(model); - } - else if (type == "DirectDebit") - { - validator = new DirectDebitPaymentInfoValidator(); - var model = new DirectDebitPaymentInfoModel - { - EnterIBAN = form["EnterIBAN"], - DirectDebitAccountHolder = form["DirectDebitAccountHolder"], - DirectDebitAccountNumber = form["DirectDebitAccountNumber"], - DirectDebitBankCode = form["DirectDebitBankCode"], - DirectDebitCountry = form["DirectDebitCountry"], - DirectDebitBankName = form["DirectDebitBankName"], - DirectDebitIban = form["DirectDebitIban"], - DirectDebitBic = form["DirectDebitBic"] - }; - validationResult = validator.Validate(model); - } - - if (validationResult != null && !validationResult.IsValid) - { - validationResult.Errors.Each(x => warnings.Add(x.ErrorMessage)); - } - } - - return warnings; - } - - [NonAction] - public override ProcessPaymentRequest GetPaymentInfo(FormCollection form) - { - var paymentInfo = new ProcessPaymentRequest(); - - string type = form["OfflinePaymentMethodType"].NullEmpty(); - - if (type.HasValue()) - { - if (type == "Manual") - { - paymentInfo.CreditCardType = form["CreditCardType"]; - paymentInfo.CreditCardName = form["CardholderName"]; - paymentInfo.CreditCardNumber = form["CardNumber"]; - paymentInfo.CreditCardExpireMonth = int.Parse(form["ExpireMonth"].SplitSafe(",")[0]); - paymentInfo.CreditCardExpireYear = int.Parse(form["ExpireYear"].SplitSafe(",")[0]); - paymentInfo.CreditCardCvv2 = form["CardCode"]; - } - else if (type == "DirectDebit") - { - paymentInfo.DirectDebitAccountHolder = form["DirectDebitAccountHolder"]; - paymentInfo.DirectDebitAccountNumber = form["DirectDebitAccountNumber"]; - paymentInfo.DirectDebitBankCode = form["DirectDebitBankCode"]; - paymentInfo.DirectDebitBankName = form["DirectDebitBankName"]; - paymentInfo.DirectDebitBic = form["DirectDebitBic"]; - paymentInfo.DirectDebitCountry = form["DirectDebitCountry"]; - paymentInfo.DirectDebitIban = form["DirectDebitIban"]; - } + _httpContext = httpContext; + _ctx = ctx; + _mediaService = mediaService; + } + + #region Global + + private List GetTransactModes() + { + var list = new List + { + new SelectListItem { Text = T("Enums.SmartStore.Core.Domain.Payments.PaymentStatus.Pending"), Value = ((int)TransactMode.Pending).ToString() }, + new SelectListItem { Text = T("Enums.SmartStore.Core.Domain.Payments.PaymentStatus.Authorized"), Value = ((int)TransactMode.Authorize).ToString() }, + new SelectListItem { Text = T("Enums.SmartStore.Core.Domain.Payments.PaymentStatus.Paid"), Value = ((int)TransactMode.Paid).ToString() } + }; + + return list; + } + + [NonAction] + private TModel ConfigureGet(Action fn = null) + where TModel : ConfigurationModelBase, new() + where TSetting : PaymentSettingsBase, new() + { + var model = new TModel(); + + var storeScope = this.GetActiveStoreScopeConfiguration(Services.StoreService, Services.WorkContext); + var settings = Services.Settings.LoadSetting(storeScope); + var store = storeScope == 0 + ? Services.StoreContext.CurrentStore + : Services.StoreService.GetStoreById(storeScope); + + model.PrimaryStoreCurrencyCode = store.PrimaryStoreCurrency.CurrencyCode; + model.DescriptionText = settings.DescriptionText; + model.PaymentMethodLogo = settings.ThumbnailPictureId; + model.AdditionalFee = settings.AdditionalFee; + model.AdditionalFeePercentage = settings.AdditionalFeePercentage; + + if (fn != null) + { + fn(model, settings); + } + + var storeDependingSettingHelper = new StoreDependingSettingHelper(ViewData); + storeDependingSettingHelper.GetOverrideKeys(settings, model, storeScope, Services.Settings); + + return model; + } + + [NonAction] + private void ConfigurePost(TModel model, FormCollection form, Action fn = null) + where TModel : ConfigurationModelBase, new() + where TSetting : PaymentSettingsBase, new() + { + ModelState.Clear(); + + var storeDependingSettingHelper = new StoreDependingSettingHelper(ViewData); + int storeScope = this.GetActiveStoreScopeConfiguration(Services.StoreService, Services.WorkContext); + var settings = Services.Settings.LoadSetting(storeScope); + + settings.DescriptionText = model.DescriptionText; + settings.ThumbnailPictureId = model.PaymentMethodLogo; + settings.AdditionalFee = model.AdditionalFee; + settings.AdditionalFeePercentage = model.AdditionalFeePercentage; + + if (fn != null) + { + fn(settings); + } + + using (Services.Settings.BeginScope()) + { + storeDependingSettingHelper.UpdateSettings(settings, form, storeScope, Services.Settings); + } + + NotifySuccess(T("Admin.Common.DataSuccessfullySaved")); + } + + [NonAction] + private TModel PaymentInfoGet(Action fn = null) + where TModel : PaymentInfoModelBase, new() + where TSetting : PaymentSettingsBase, new() + { + var settings = _ctx.Resolve(); + var model = new TModel + { + DescriptionText = GetLocalizedText(settings.DescriptionText), + ThumbnailUrl = _mediaService.GetUrl(settings.ThumbnailPictureId, 120, null, false) + }; + + if (fn != null) + { + fn(model, settings); + } + + return model; + } + + private string GetLocalizedText(string text) + { + if (text.EmptyNull().StartsWith("@")) + { + return T(text.Substring(1)); + } + + return text; + } + + [NonAction] + public override IList ValidatePaymentForm(FormCollection form) + { + var warnings = new List(); + IValidator validator; + ValidationResult validationResult = null; + + string type = form["OfflinePaymentMethodType"].NullEmpty(); + + if (type.HasValue()) + { + if (type == "Manual") + { + validator = new ManualPaymentInfoValidator(T); + var model = new ManualPaymentInfoModel + { + CardholderName = form["CardholderName"], + CardNumber = form["CardNumber"], + CardCode = form["CardCode"] + }; + validationResult = validator.Validate(model); + } + else if (type == "DirectDebit") + { + validator = new DirectDebitPaymentInfoValidator(); + var model = new DirectDebitPaymentInfoModel + { + EnterIBAN = form["EnterIBAN"], + DirectDebitAccountHolder = form["DirectDebitAccountHolder"], + DirectDebitAccountNumber = form["DirectDebitAccountNumber"], + DirectDebitBankCode = form["DirectDebitBankCode"], + DirectDebitCountry = form["DirectDebitCountry"], + DirectDebitBankName = form["DirectDebitBankName"], + DirectDebitIban = form["DirectDebitIban"], + DirectDebitBic = form["DirectDebitBic"] + }; + validationResult = validator.Validate(model); + } + else if (type == "PurchaseOrderNumber") + { + validator = new PurchaseOrderNumberPaymentInfoValidator(); + var model = new PurchaseOrderNumberPaymentInfoModel + { + PurchaseOrderNumber = form["PurchaseOrderNumber"] + }; + validationResult = validator.Validate(model); + } + + if (validationResult != null && !validationResult.IsValid) + { + validationResult.Errors.Each(x => warnings.Add(x.ErrorMessage)); + } + } + + return warnings; + } + + [NonAction] + public override ProcessPaymentRequest GetPaymentInfo(FormCollection form) + { + var paymentInfo = new ProcessPaymentRequest(); + + string type = form["OfflinePaymentMethodType"].NullEmpty(); + + if (type.HasValue()) + { + if (type == "Manual") + { + paymentInfo.CreditCardType = form["CreditCardType"]; + paymentInfo.CreditCardName = form["CardholderName"]; + paymentInfo.CreditCardNumber = form["CardNumber"]; + paymentInfo.CreditCardExpireMonth = int.Parse(form["ExpireMonth"].SplitSafe(",")[0]); + paymentInfo.CreditCardExpireYear = int.Parse(form["ExpireYear"].SplitSafe(",")[0]); + paymentInfo.CreditCardCvv2 = form["CardCode"]; + } + else if (type == "DirectDebit") + { + paymentInfo.DirectDebitAccountHolder = form["DirectDebitAccountHolder"]; + paymentInfo.DirectDebitAccountNumber = form["DirectDebitAccountNumber"]; + paymentInfo.DirectDebitBankCode = form["DirectDebitBankCode"]; + paymentInfo.DirectDebitBankName = form["DirectDebitBankName"]; + paymentInfo.DirectDebitBic = form["DirectDebitBic"]; + paymentInfo.DirectDebitCountry = form["DirectDebitCountry"]; + paymentInfo.DirectDebitIban = form["DirectDebitIban"]; + } else if (type == "PurchaseOrderNumber") { paymentInfo.PurchaseOrderNumber = form["PurchaseOrderNumber"]; } - } - - return paymentInfo; - } - - [NonAction] - public override string GetPaymentSummary(FormCollection form) - { - string type = form["OfflinePaymentMethodType"].NullEmpty(); - - if (type.HasValue()) - { - if (type == "Manual") - { - var number = form["CardNumber"]; - return "{0}, {1}, {2}".FormatCurrent( - form["CreditCardType"], - form["CardholderName"], - number.Mask(4) - ); - } - else if (type == "DirectDebit") - { - if (form["DirectDebitAccountNumber"].HasValue() && (form["DirectDebitBankCode"].HasValue()) && form["DirectDebitAccountHolder"].HasValue()) - { - var number = form["DirectDebitAccountNumber"]; - return "{0}, {1}, {2}".FormatCurrent( - form["DirectDebitAccountHolder"], - form["DirectDebitBankName"].NullEmpty() ?? form["DirectDebitBankCode"], - number.Mask(4) - ); - } - else if (form["DirectDebitIban"].HasValue()) - { - var number = form["DirectDebitIban"]; - return number.Mask(8); - } - } + } + + return paymentInfo; + } + + [NonAction] + public override string GetPaymentSummary(FormCollection form) + { + string type = form["OfflinePaymentMethodType"].NullEmpty(); + + if (type.HasValue()) + { + if (type == "Manual") + { + var number = form["CardNumber"]; + return "{0}, {1}, {2}".FormatCurrent( + form["CreditCardType"], + form["CardholderName"], + number.Mask(4) + ); + } + else if (type == "DirectDebit") + { + if (form["DirectDebitAccountNumber"].HasValue() && (form["DirectDebitBankCode"].HasValue()) && form["DirectDebitAccountHolder"].HasValue()) + { + var number = form["DirectDebitAccountNumber"]; + return "{0}, {1}, {2}".FormatCurrent( + form["DirectDebitAccountHolder"], + form["DirectDebitBankName"].NullEmpty() ?? form["DirectDebitBankCode"], + number.Mask(4) + ); + } + else if (form["DirectDebitIban"].HasValue()) + { + var number = form["DirectDebitIban"]; + return number.Mask(8); + } + } else if (type == "PurchaseOrderNumber") { return form["PurchaseOrderNumber"]; } - } + } + + return null; + } + + #endregion + + #region CashOnDelivery + + [AdminAuthorize, AdminThemed, ChildActionOnly] + public ActionResult CashOnDeliveryConfigure() + { + var model = ConfigureGet(); + + return View("GenericConfigure", model); + } + + [HttpPost, AdminAuthorize, AdminThemed, ChildActionOnly, ValidateInput(false)] + [ValidateAntiForgeryToken] + public ActionResult CashOnDeliveryConfigure(CashOnDeliveryConfigurationModel model, FormCollection form) + { + if (!ModelState.IsValid) + return CashOnDeliveryConfigure(); + + ConfigurePost(model, form); + + return CashOnDeliveryConfigure(); + } + + public ActionResult CashOnDeliveryPaymentInfo() + { + var model = PaymentInfoGet(); + return PartialView("GenericPaymentInfo", model); + } - return null; - } + #endregion - #endregion - - #region CashOnDelivery - - [AdminAuthorize, AdminThemed, ChildActionOnly] - public ActionResult CashOnDeliveryConfigure() - { - var model = ConfigureGet(); + #region Invoice - return View("GenericConfigure", model); - } + [ChildActionOnly, AdminThemed, AdminAuthorize] + public ActionResult InvoiceConfigure() + { + var model = ConfigureGet(); - [HttpPost, AdminAuthorize, AdminThemed, ChildActionOnly, ValidateInput(false)] - public ActionResult CashOnDeliveryConfigure(CashOnDeliveryConfigurationModel model, FormCollection form) - { - if (!ModelState.IsValid) - return CashOnDeliveryConfigure(); + return View("GenericConfigure", model); + } - ConfigurePost(model, form); + [HttpPost, AdminAuthorize, AdminThemed, ChildActionOnly, ValidateInput(false)] + [ValidateAntiForgeryToken] + public ActionResult InvoiceConfigure(InvoiceConfigurationModel model, FormCollection form) + { + if (!ModelState.IsValid) + return InvoiceConfigure(); - return CashOnDeliveryConfigure(); - } - - public ActionResult CashOnDeliveryPaymentInfo() - { - var model = PaymentInfoGet(); - return PartialView("GenericPaymentInfo", model); - } + ConfigurePost(model, form); - #endregion - - #region Invoice - - [ChildActionOnly, AdminThemed, AdminAuthorize] - public ActionResult InvoiceConfigure() - { - var model = ConfigureGet(); + return InvoiceConfigure(); + } - return View("GenericConfigure", model); - } + public ActionResult InvoicePaymentInfo() + { + var model = PaymentInfoGet(); + return PartialView("GenericPaymentInfo", model); + } - [HttpPost, AdminAuthorize, AdminThemed, ChildActionOnly, ValidateInput(false)] - public ActionResult InvoiceConfigure(InvoiceConfigurationModel model, FormCollection form) - { - if (!ModelState.IsValid) - return InvoiceConfigure(); - - ConfigurePost(model, form); - - return InvoiceConfigure(); - } - - public ActionResult InvoicePaymentInfo() - { - var model = PaymentInfoGet(); - return PartialView("GenericPaymentInfo", model); - } - - #endregion - - #region PayInStore - - [ChildActionOnly, AdminThemed, AdminAuthorize] - public ActionResult PayInStoreConfigure() - { - var model = ConfigureGet(); - - return View("GenericConfigure", model); - } - - [HttpPost, AdminAuthorize, AdminThemed, ChildActionOnly, ValidateInput(false)] - public ActionResult PayInStoreConfigure(PayInStoreConfigurationModel model, FormCollection form) - { - if (!ModelState.IsValid) - return PayInStoreConfigure(); - - ConfigurePost(model, form); - - return PayInStoreConfigure(); - } - - public ActionResult PayInStorePaymentInfo() - { - var model = PaymentInfoGet(); - return PartialView("GenericPaymentInfo", model); - } - - #endregion - - #region Prepayment - - [AdminAuthorize, AdminThemed, ChildActionOnly] - public ActionResult PrepaymentConfigure() - { - var model = ConfigureGet(); - - return View("GenericConfigure", model); - } - - [HttpPost, AdminAuthorize, AdminThemed, ChildActionOnly, ValidateInput(false)] - public ActionResult PrepaymentConfigure(PrepaymentConfigurationModel model, FormCollection form) - { - if (!ModelState.IsValid) - return PrepaymentConfigure(); - - ConfigurePost(model, form); - - return PrepaymentConfigure(); - } - - public ActionResult PrepaymentPaymentInfo() - { - var model = PaymentInfoGet(); - return PartialView("GenericPaymentInfo", model); - } - - #endregion - - #region DirectDebit - - [AdminAuthorize, AdminThemed, ChildActionOnly] - public ActionResult DirectDebitConfigure() - { - var model = ConfigureGet(); - - return View("GenericConfigure", model); - } - - [HttpPost, AdminAuthorize, AdminThemed, ChildActionOnly, ValidateInput(false)] - public ActionResult DirectDebitConfigure(DirectDebitConfigurationModel model, FormCollection form) - { - if (!ModelState.IsValid) - return DirectDebitConfigure(); - - ConfigurePost(model, form); - - return DirectDebitConfigure(); - } - - public ActionResult DirectDebitPaymentInfo() - { - var model = PaymentInfoGet(); - var paymentData = _httpContext.GetCheckoutState().PaymentData; - - model.DirectDebitAccountHolder = (string)paymentData.Get("DirectDebitAccountHolder"); - model.DirectDebitAccountNumber = (string)paymentData.Get("DirectDebitAccountNumber"); - model.DirectDebitBankCode = (string)paymentData.Get("DirectDebitBankCode"); - model.DirectDebitBankName = (string)paymentData.Get("DirectDebitBankName"); - model.DirectDebitBic = (string)paymentData.Get("DirectDebitBic"); - model.DirectDebitCountry = (string)paymentData.Get("DirectDebitCountry"); - model.DirectDebitIban = (string)paymentData.Get("DirectDebitIban"); - - return PartialView(model); - } - - #endregion - - #region Manual - - [AdminAuthorize, AdminThemed, ChildActionOnly] - public ActionResult ManualConfigure() - { - var model = ConfigureGet((m, s) => - { - m.TransactMode = s.TransactMode; - m.TransactModeValues = GetTransactModes(); - m.ExcludedCreditCards = s.ExcludedCreditCards.SplitSafe(","); - - m.AvailableCreditCards = ManualProvider.CreditCardTypes - .Select(x => new SelectListItem - { - Text = x.Text, - Value = x.Value, - Selected = m.ExcludedCreditCards.Contains(x.Value) - }) - .ToList(); - }); - - return View(model); - } - - [HttpPost, AdminAuthorize, AdminThemed, ChildActionOnly, ValidateInput(false)] - public ActionResult ManualConfigure(ManualConfigurationModel model, FormCollection form) - { - if (!ModelState.IsValid) - return ManualConfigure(); - - ConfigurePost(model, form, s => - { - s.TransactMode = model.TransactMode; - s.ExcludedCreditCards = string.Join(",", model.ExcludedCreditCards ?? new string[0]); - }); - - return ManualConfigure(); - } - - public ActionResult ManualPaymentInfo() - { - var model = PaymentInfoGet((m, s) => - { - var excludedCreditCards = s.ExcludedCreditCards.SplitSafe(","); - - foreach (var creditCard in ManualProvider.CreditCardTypes) - { - if (!excludedCreditCards.Any(x => x.IsCaseInsensitiveEqual(creditCard.Value))) - { - m.CreditCardTypes.Add(new SelectListItem - { - Text = creditCard.Text, - Value = creditCard.Value - }); - } - } - }); - - // years - for (int i = 0; i < 15; i++) - { - string year = Convert.ToString(DateTime.Now.Year + i); - model.ExpireYears.Add(new SelectListItem { Text = year, Value = year }); - } - - // months - for (int i = 1; i <= 12; i++) - { - string text = (i < 10) ? "0" + i.ToString() : i.ToString(); - model.ExpireMonths.Add(new SelectListItem { Text = text, Value = i.ToString() }); - } - - // set postback values - var paymentData = _httpContext.GetCheckoutState().PaymentData; - model.CardholderName = (string)paymentData.Get("CardholderName"); - model.CardNumber = (string)paymentData.Get("CardNumber"); - model.CardCode = (string)paymentData.Get("CardCode"); - - var creditCardType = (string)paymentData.Get("CreditCardType"); - var selectedCcType = model.CreditCardTypes.Where(x => x.Value.Equals(creditCardType, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); - if (selectedCcType != null) - selectedCcType.Selected = true; - - var expireMonth = (string)paymentData.Get("ExpireMonth"); - var selectedMonth = model.ExpireMonths.Where(x => x.Value.Equals(expireMonth, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); - if (selectedMonth != null) - selectedMonth.Selected = true; - - var expireYear = (string)paymentData.Get("ExpireYear"); - var selectedYear = model.ExpireYears.Where(x => x.Value.Equals(expireYear, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); - if (selectedYear != null) - selectedYear.Selected = true; - - return PartialView(model); - } - - #endregion + #endregion + + #region PayInStore + + [ChildActionOnly, AdminThemed, AdminAuthorize] + public ActionResult PayInStoreConfigure() + { + var model = ConfigureGet(); + + return View("GenericConfigure", model); + } + + [HttpPost, AdminAuthorize, AdminThemed, ChildActionOnly, ValidateInput(false)] + [ValidateAntiForgeryToken] + public ActionResult PayInStoreConfigure(PayInStoreConfigurationModel model, FormCollection form) + { + if (!ModelState.IsValid) + return PayInStoreConfigure(); + + ConfigurePost(model, form); + + return PayInStoreConfigure(); + } + + public ActionResult PayInStorePaymentInfo() + { + var model = PaymentInfoGet(); + return PartialView("GenericPaymentInfo", model); + } + + #endregion + + #region Prepayment + + [AdminAuthorize, AdminThemed, ChildActionOnly] + public ActionResult PrepaymentConfigure() + { + var model = ConfigureGet(); + + return View("GenericConfigure", model); + } + + [HttpPost, AdminAuthorize, AdminThemed, ChildActionOnly, ValidateInput(false)] + [ValidateAntiForgeryToken] + public ActionResult PrepaymentConfigure(PrepaymentConfigurationModel model, FormCollection form) + { + if (!ModelState.IsValid) + return PrepaymentConfigure(); + + ConfigurePost(model, form); + + return PrepaymentConfigure(); + } + + public ActionResult PrepaymentPaymentInfo() + { + var model = PaymentInfoGet(); + return PartialView("GenericPaymentInfo", model); + } + + #endregion + + #region DirectDebit + + [AdminAuthorize, AdminThemed, ChildActionOnly] + public ActionResult DirectDebitConfigure() + { + var model = ConfigureGet(); + + return View("GenericConfigure", model); + } + + [HttpPost, AdminAuthorize, AdminThemed, ChildActionOnly, ValidateInput(false)] + [ValidateAntiForgeryToken] + public ActionResult DirectDebitConfigure(DirectDebitConfigurationModel model, FormCollection form) + { + if (!ModelState.IsValid) + return DirectDebitConfigure(); + + ConfigurePost(model, form); + + return DirectDebitConfigure(); + } + + public ActionResult DirectDebitPaymentInfo() + { + var model = PaymentInfoGet(); + var paymentData = _httpContext.GetCheckoutState().PaymentData; + + model.DirectDebitAccountHolder = (string)paymentData.Get("DirectDebitAccountHolder"); + model.DirectDebitAccountNumber = (string)paymentData.Get("DirectDebitAccountNumber"); + model.DirectDebitBankCode = (string)paymentData.Get("DirectDebitBankCode"); + model.DirectDebitBankName = (string)paymentData.Get("DirectDebitBankName"); + model.DirectDebitBic = (string)paymentData.Get("DirectDebitBic"); + model.DirectDebitCountry = (string)paymentData.Get("DirectDebitCountry"); + model.DirectDebitIban = (string)paymentData.Get("DirectDebitIban"); + + return PartialView(model); + } + + #endregion + + #region Manual + + [AdminAuthorize, AdminThemed, ChildActionOnly] + public ActionResult ManualConfigure() + { + var model = ConfigureGet((m, s) => + { + m.TransactMode = s.TransactMode; + m.TransactModeValues = GetTransactModes(); + m.ExcludedCreditCards = s.ExcludedCreditCards.SplitSafe(","); + + m.AvailableCreditCards = ManualProvider.CreditCardTypes + .Select(x => new SelectListItem + { + Text = x.Text, + Value = x.Value, + Selected = m.ExcludedCreditCards.Contains(x.Value) + }) + .ToList(); + }); + + return View(model); + } + + [HttpPost, AdminAuthorize, AdminThemed, ChildActionOnly, ValidateInput(false)] + [ValidateAntiForgeryToken] + public ActionResult ManualConfigure(ManualConfigurationModel model, FormCollection form) + { + if (!ModelState.IsValid) + return ManualConfigure(); + + ConfigurePost(model, form, s => + { + s.TransactMode = model.TransactMode; + s.ExcludedCreditCards = string.Join(",", model.ExcludedCreditCards ?? new string[0]); + }); + + return ManualConfigure(); + } + + public ActionResult ManualPaymentInfo() + { + var model = PaymentInfoGet((m, s) => + { + var excludedCreditCards = s.ExcludedCreditCards.SplitSafe(","); + + foreach (var creditCard in ManualProvider.CreditCardTypes) + { + if (!excludedCreditCards.Any(x => x.IsCaseInsensitiveEqual(creditCard.Value))) + { + m.CreditCardTypes.Add(new SelectListItem + { + Text = creditCard.Text, + Value = creditCard.Value + }); + } + } + }); + + // years + for (int i = 0; i < 15; i++) + { + string year = Convert.ToString(DateTime.Now.Year + i); + model.ExpireYears.Add(new SelectListItem { Text = year, Value = year }); + } + + // months + for (int i = 1; i <= 12; i++) + { + string text = (i < 10) ? "0" + i.ToString() : i.ToString(); + model.ExpireMonths.Add(new SelectListItem { Text = text, Value = i.ToString() }); + } + + // set postback values + var paymentData = _httpContext.GetCheckoutState().PaymentData; + model.CardholderName = (string)paymentData.Get("CardholderName"); + model.CardNumber = (string)paymentData.Get("CardNumber"); + model.CardCode = (string)paymentData.Get("CardCode"); + + var creditCardType = (string)paymentData.Get("CreditCardType"); + var selectedCcType = model.CreditCardTypes.Where(x => x.Value.Equals(creditCardType, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); + if (selectedCcType != null) + selectedCcType.Selected = true; + + var expireMonth = (string)paymentData.Get("ExpireMonth"); + var selectedMonth = model.ExpireMonths.Where(x => x.Value.Equals(expireMonth, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); + if (selectedMonth != null) + selectedMonth.Selected = true; + + var expireYear = (string)paymentData.Get("ExpireYear"); + var selectedYear = model.ExpireYears.Where(x => x.Value.Equals(expireYear, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); + if (selectedYear != null) + selectedYear.Selected = true; + + return PartialView(model); + } + + #endregion #region PurchaseOrderNumber @@ -529,12 +545,13 @@ public ActionResult PurchaseOrderNumberConfigure() } [HttpPost, AdminAuthorize, AdminThemed, ChildActionOnly, ValidateInput(false)] + [ValidateAntiForgeryToken] public ActionResult PurchaseOrderNumberConfigure(PurchaseOrderNumberConfigurationModel model, FormCollection form) { if (!ModelState.IsValid) - return PurchaseOrderNumberConfigure(); + return PurchaseOrderNumberConfigure(); - ConfigurePost(model, form); + ConfigurePost(model, form); return PurchaseOrderNumberConfigure(); } @@ -542,7 +559,7 @@ public ActionResult PurchaseOrderNumberConfigure(PurchaseOrderNumberConfiguratio public ActionResult PurchaseOrderNumberPaymentInfo() { var model = PaymentInfoGet(); - var paymentData = _httpContext.GetCheckoutState().PaymentData; + var paymentData = _httpContext.GetCheckoutState().PaymentData; model.PurchaseOrderNumber = (string)paymentData.Get("PurchaseOrderNumber"); diff --git a/src/Plugins/SmartStore.OfflinePayment/Description.txt b/src/Plugins/SmartStore.OfflinePayment/Description.txt index d2ed286f27..680ab03804 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Description.txt +++ b/src/Plugins/SmartStore.OfflinePayment/Description.txt @@ -2,8 +2,8 @@ Description: Contains common offline payment methods like Direct Debit, Invoice, Prepayment etc. Group: Payment SystemName: SmartStore.OfflinePayment -Version: 3.2.2 -MinAppVersion: 3.2.2 +Version: 4.2.0 +MinAppVersion: 4.2.0 DisplayOrder: 0 FileName: SmartStore.OfflinePayment.dll ResourceRootKey: Plugins.SmartStore.OfflinePayment diff --git a/src/Plugins/SmartStore.OfflinePayment/Localization/resources.de-de.xml b/src/Plugins/SmartStore.OfflinePayment/Localization/resources.de-de.xml index aabebe0d5a..990adcbb49 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Localization/resources.de-de.xml +++ b/src/Plugins/SmartStore.OfflinePayment/Localization/resources.de-de.xml @@ -207,7 +207,7 @@ Bestellnummer - + diff --git a/src/Plugins/SmartStore.OfflinePayment/Localization/resources.en-us.xml b/src/Plugins/SmartStore.OfflinePayment/Localization/resources.en-us.xml index 0dcc2a5491..74ece02ecd 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Localization/resources.en-us.xml +++ b/src/Plugins/SmartStore.OfflinePayment/Localization/resources.en-us.xml @@ -210,7 +210,7 @@ Purchase order - + @@ -218,7 +218,7 @@ - PO Number + Purchase order number diff --git a/src/Plugins/SmartStore.OfflinePayment/Models/ConfigurationModel.cs b/src/Plugins/SmartStore.OfflinePayment/Models/ConfigurationModel.cs index f69436a624..9bab769070 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Models/ConfigurationModel.cs +++ b/src/Plugins/SmartStore.OfflinePayment/Models/ConfigurationModel.cs @@ -1,61 +1,61 @@ using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Web.Mvc; using SmartStore.OfflinePayment.Settings; using SmartStore.Web.Framework; using SmartStore.Web.Framework.Modelling; -using System.ComponentModel.DataAnnotations; namespace SmartStore.OfflinePayment.Models -{ - public abstract class ConfigurationModelBase : ModelBase +{ + public abstract class ConfigurationModelBase : ModelBase { - public string PrimaryStoreCurrencyCode { get; set; } + public string PrimaryStoreCurrencyCode { get; set; } - [AllowHtml] - [SmartResourceDisplayName("Plugins.SmartStore.OfflinePayment.DescriptionText")] - public string DescriptionText { get; set; } + [AllowHtml] + [SmartResourceDisplayName("Plugins.SmartStore.OfflinePayment.DescriptionText")] + public string DescriptionText { get; set; } - [SmartResourceDisplayName("Admin.Configuration.Payment.Methods.AdditionalFee")] - public decimal AdditionalFee { get; set; } + [SmartResourceDisplayName("Admin.Configuration.Payment.Methods.AdditionalFee")] + public decimal AdditionalFee { get; set; } - [SmartResourceDisplayName("Admin.Configuration.Payment.Methods.AdditionalFeePercentage")] - public bool AdditionalFeePercentage { get; set; } + [SmartResourceDisplayName("Admin.Configuration.Payment.Methods.AdditionalFeePercentage")] + public bool AdditionalFeePercentage { get; set; } - [SmartResourceDisplayName("Plugins.SmartStore.OfflinePayment.PaymentMethodLogo")] - [UIHint("Picture")] - public int PaymentMethodLogo { get; set; } - } + [SmartResourceDisplayName("Plugins.SmartStore.OfflinePayment.PaymentMethodLogo")] + [UIHint("Media"), AdditionalMetadata("album", "content")] + public int PaymentMethodLogo { get; set; } + } - public class CashOnDeliveryConfigurationModel : ConfigurationModelBase - { - } + public class CashOnDeliveryConfigurationModel : ConfigurationModelBase + { + } - public class DirectDebitConfigurationModel : ConfigurationModelBase - { - } + public class DirectDebitConfigurationModel : ConfigurationModelBase + { + } - public class InvoiceConfigurationModel : ConfigurationModelBase - { - } + public class InvoiceConfigurationModel : ConfigurationModelBase + { + } - public class ManualConfigurationModel : ConfigurationModelBase - { - [SmartResourceDisplayName("Plugins.Payments.Manual.Fields.TransactMode")] - public TransactMode TransactMode { get; set; } - public List TransactModeValues { get; set; } + public class ManualConfigurationModel : ConfigurationModelBase + { + [SmartResourceDisplayName("Plugins.Payments.Manual.Fields.TransactMode")] + public TransactMode TransactMode { get; set; } + public List TransactModeValues { get; set; } - [SmartResourceDisplayName("Plugins.Payments.Manual.ExcludedCreditCards")] - public string[] ExcludedCreditCards { get; set; } - public List AvailableCreditCards { get; set; } - } + [SmartResourceDisplayName("Plugins.Payments.Manual.ExcludedCreditCards")] + public string[] ExcludedCreditCards { get; set; } + public List AvailableCreditCards { get; set; } + } - public class PayInStoreConfigurationModel : ConfigurationModelBase - { - } + public class PayInStoreConfigurationModel : ConfigurationModelBase + { + } - public class PrepaymentConfigurationModel : ConfigurationModelBase - { - } + public class PrepaymentConfigurationModel : ConfigurationModelBase + { + } public class PurchaseOrderNumberConfigurationModel : ConfigurationModelBase { diff --git a/src/Plugins/SmartStore.OfflinePayment/Models/PaymentInfoModel.cs b/src/Plugins/SmartStore.OfflinePayment/Models/PaymentInfoModel.cs index 6046a28975..b32748e19d 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Models/PaymentInfoModel.cs +++ b/src/Plugins/SmartStore.OfflinePayment/Models/PaymentInfoModel.cs @@ -1,114 +1,115 @@ using System.Collections.Generic; using System.Web.Mvc; -using SmartStore.Web.Framework; -using SmartStore.Web.Framework.Modelling; using FluentValidation; using SmartStore.Core.Localization; +using SmartStore.Web.Framework; +using SmartStore.Web.Framework.Modelling; using SmartStore.Web.Framework.Validators; namespace SmartStore.OfflinePayment.Models { - public abstract class PaymentInfoModelBase : ModelBase + public abstract class PaymentInfoModelBase : ModelBase { - public string DescriptionText { get; set; } - public string ThumbnailUrl { get; set; } - } + public string DescriptionText { get; set; } + public string ThumbnailUrl { get; set; } + } - public class CashOnDeliveryPaymentInfoModel : PaymentInfoModelBase - { - } + public class CashOnDeliveryPaymentInfoModel : PaymentInfoModelBase + { + } - public class DirectDebitPaymentInfoModel : PaymentInfoModelBase - { - public DirectDebitPaymentInfoModel() - { - EnterIBAN = "iban"; - } - - [SmartResourceDisplayName("Plugins.Payments.DirectDebit.EnterIBAN")] - public string EnterIBAN { get; set; } + public class DirectDebitPaymentInfoModel : PaymentInfoModelBase + { + public DirectDebitPaymentInfoModel() + { + EnterIBAN = "iban"; + } - [SmartResourceDisplayName("Plugins.Payments.DirectDebit.DirectDebitAccountHolder")] - public string DirectDebitAccountHolder { get; set; } + [SmartResourceDisplayName("Plugins.Payments.DirectDebit.EnterIBAN")] + public string EnterIBAN { get; set; } - [SmartResourceDisplayName("Plugins.Payments.DirectDebit.DirectDebitAccountNumber")] - public string DirectDebitAccountNumber { get; set; } + [SmartResourceDisplayName("Plugins.Payments.DirectDebit.DirectDebitAccountHolder")] + public string DirectDebitAccountHolder { get; set; } - [SmartResourceDisplayName("Plugins.Payments.DirectDebit.DirectDebitBankCode")] - public string DirectDebitBankCode { get; set; } + [SmartResourceDisplayName("Plugins.Payments.DirectDebit.DirectDebitAccountNumber")] + public string DirectDebitAccountNumber { get; set; } - [SmartResourceDisplayName("Plugins.Payments.DirectDebit.DirectDebitCountry")] - public string DirectDebitCountry { get; set; } + [SmartResourceDisplayName("Plugins.Payments.DirectDebit.DirectDebitBankCode")] + public string DirectDebitBankCode { get; set; } - [SmartResourceDisplayName("Plugins.Payments.DirectDebit.DirectDebitBankName")] - public string DirectDebitBankName { get; set; } + [SmartResourceDisplayName("Plugins.Payments.DirectDebit.DirectDebitCountry")] + public string DirectDebitCountry { get; set; } - [SmartResourceDisplayName("Plugins.Payments.DirectDebit.DirectDebitIban")] - public string DirectDebitIban { get; set; } + [SmartResourceDisplayName("Plugins.Payments.DirectDebit.DirectDebitBankName")] + public string DirectDebitBankName { get; set; } - [SmartResourceDisplayName("Plugins.Payments.DirectDebit.DirectDebitBic")] - public string DirectDebitBic { get; set; } + [SmartResourceDisplayName("Plugins.Payments.DirectDebit.DirectDebitIban")] + public string DirectDebitIban { get; set; } - public List Countries { get; set; } - } + [SmartResourceDisplayName("Plugins.Payments.DirectDebit.DirectDebitBic")] + public string DirectDebitBic { get; set; } - public class InvoicePaymentInfoModel : PaymentInfoModelBase - { - } + public List Countries { get; set; } + } - public class ManualPaymentInfoModel : PaymentInfoModelBase - { - public ManualPaymentInfoModel() + public class InvoicePaymentInfoModel : PaymentInfoModelBase + { + } + + public class ManualPaymentInfoModel : PaymentInfoModelBase + { + public ManualPaymentInfoModel() { CreditCardTypes = new List(); ExpireMonths = new List(); ExpireYears = new List(); } - - [SmartResourceDisplayName("Payment.SelectCreditCard")] - [AllowHtml] - public string CreditCardType { get; set; } - [SmartResourceDisplayName("Payment.SelectCreditCard")] - public IList CreditCardTypes { get; set; } - - [SmartResourceDisplayName("Payment.CardholderName")] - [AllowHtml] - public string CardholderName { get; set; } - - [SmartResourceDisplayName("Payment.CardNumber")] - [AllowHtml] - public string CardNumber { get; set; } - - [SmartResourceDisplayName("Payment.ExpirationDate")] - [AllowHtml] - public string ExpireMonth { get; set; } - [SmartResourceDisplayName("Payment.ExpirationDate")] - [AllowHtml] - public string ExpireYear { get; set; } - public IList ExpireMonths { get; set; } - public IList ExpireYears { get; set; } - - [SmartResourceDisplayName("Payment.CardCode")] - [AllowHtml] - public string CardCode { get; set; } - } - - public class PayInStorePaymentInfoModel : PaymentInfoModelBase - { - } - - public class PrepaymentPaymentInfoModel : PaymentInfoModelBase - { - } + + [SmartResourceDisplayName("Payment.SelectCreditCard")] + [AllowHtml] + public string CreditCardType { get; set; } + [SmartResourceDisplayName("Payment.SelectCreditCard")] + public IList CreditCardTypes { get; set; } + + [SmartResourceDisplayName("Payment.CardholderName")] + [AllowHtml] + public string CardholderName { get; set; } + + [SmartResourceDisplayName("Payment.CardNumber")] + [AllowHtml] + public string CardNumber { get; set; } + + [SmartResourceDisplayName("Payment.ExpirationDate")] + [AllowHtml] + public string ExpireMonth { get; set; } + [SmartResourceDisplayName("Payment.ExpirationDate")] + [AllowHtml] + public string ExpireYear { get; set; } + public IList ExpireMonths { get; set; } + public IList ExpireYears { get; set; } + + [SmartResourceDisplayName("Payment.CardCode")] + [AllowHtml] + public string CardCode { get; set; } + } + + public class PayInStorePaymentInfoModel : PaymentInfoModelBase + { + } + + public class PrepaymentPaymentInfoModel : PaymentInfoModelBase + { + } public class PurchaseOrderNumberPaymentInfoModel : PaymentInfoModelBase - { - [SmartResourceDisplayName("Plugins.Payment.PurchaseOrder.PurchaseOrderNumber")] + { + [SmartResourceDisplayName("Plugins.Payments.PurchaseOrder.PurchaseOrderNumber")] [AllowHtml] public string PurchaseOrderNumber { get; set; } - } + } + - #region validators + #region Validators public class DirectDebitPaymentInfoValidator : AbstractValidator { @@ -132,5 +133,13 @@ public ManualPaymentInfoValidator(Localizer T) } } + public class PurchaseOrderNumberPaymentInfoValidator : AbstractValidator + { + public PurchaseOrderNumberPaymentInfoValidator() + { + RuleFor(x => x.PurchaseOrderNumber).NotEmpty(); + } + } + #endregion } \ No newline at end of file diff --git a/src/Plugins/SmartStore.OfflinePayment/Plugin.cs b/src/Plugins/SmartStore.OfflinePayment/Plugin.cs index f496f22698..2006006001 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Plugin.cs +++ b/src/Plugins/SmartStore.OfflinePayment/Plugin.cs @@ -1,86 +1,83 @@ -using System; using SmartStore.Core.Plugins; -using SmartStore.Services.Localization; -using SmartStore.Services.Configuration; using SmartStore.OfflinePayment.Settings; using SmartStore.Services; namespace SmartStore.OfflinePayment { - public partial class Plugin : BasePlugin + public partial class Plugin : BasePlugin { - private readonly ICommonServices _services; + private readonly ICommonServices _services; - public Plugin(ICommonServices services) + public Plugin(ICommonServices services) { - this._services = services; + this._services = services; } public override void Install() { - var settings = _services.Settings; - var loc = _services.Localization; - - // add settings - settings.SaveSetting(new CashOnDeliveryPaymentSettings - { - DescriptionText = "@Plugins.Payment.CashOnDelivery.PaymentInfoDescription" - }); - settings.SaveSetting(new InvoicePaymentSettings - { - DescriptionText = "@Plugins.Payment.Invoice.PaymentInfoDescription" - }); - settings.SaveSetting(new PayInStorePaymentSettings - { - DescriptionText = "@Plugins.Payment.PayInStore.PaymentInfoDescription" - }); - settings.SaveSetting(new PrepaymentPaymentSettings - { - DescriptionText = "@Plugins.Payment.Prepayment.PaymentInfoDescription" - }); - settings.SaveSetting(new ManualPaymentSettings - { + var settings = _services.Settings; + var loc = _services.Localization; + + // add settings + settings.SaveSetting(new CashOnDeliveryPaymentSettings + { + DescriptionText = "@Plugins.Payment.CashOnDelivery.PaymentInfoDescription" + }); + settings.SaveSetting(new InvoicePaymentSettings + { + DescriptionText = "@Plugins.Payment.Invoice.PaymentInfoDescription" + }); + settings.SaveSetting(new PayInStorePaymentSettings + { + DescriptionText = "@Plugins.Payment.PayInStore.PaymentInfoDescription" + }); + settings.SaveSetting(new PrepaymentPaymentSettings + { + DescriptionText = "@Plugins.Payment.Prepayment.PaymentInfoDescription" + }); + settings.SaveSetting(new ManualPaymentSettings + { DescriptionText = "@Plugins.Payments.Manual.PaymentInfoDescription", - TransactMode = TransactMode.Pending - }); - settings.SaveSetting(new DirectDebitPaymentSettings - { - DescriptionText = "@Plugins.Payments.DirectDebit.PaymentInfoDescription" - }); + TransactMode = TransactMode.Pending + }); + settings.SaveSetting(new DirectDebitPaymentSettings + { + DescriptionText = "@Plugins.Payments.DirectDebit.PaymentInfoDescription" + }); settings.SaveSetting(new PurchaseOrderNumberPaymentSettings { DescriptionText = "@Plugins.Payments.PurchaseOrderNumber.PaymentInfoDescription", TransactMode = TransactMode.Pending }); - // add resources - loc.ImportPluginResourcesFromXml(this.PluginDescriptor); - + // add resources + loc.ImportPluginResourcesFromXml(this.PluginDescriptor); + base.Install(); } public override void Uninstall() { - var settings = _services.Settings; - var loc = _services.Localization; + var settings = _services.Settings; + var loc = _services.Localization; - // delete settings - settings.DeleteSetting(); - settings.DeleteSetting(); - settings.DeleteSetting(); - settings.DeleteSetting(); - settings.DeleteSetting(); - settings.DeleteSetting(); + // delete settings + settings.DeleteSetting(); + settings.DeleteSetting(); + settings.DeleteSetting(); + settings.DeleteSetting(); + settings.DeleteSetting(); + settings.DeleteSetting(); - // delete resources - loc.DeleteLocaleStringResources(this.PluginDescriptor.ResourceRootKey); - loc.DeleteLocaleStringResources("Plugins.Payment.CashOnDelivery"); - loc.DeleteLocaleStringResources("Plugins.Payment.Invoice"); - loc.DeleteLocaleStringResources("Plugins.Payment.PayInStore"); - loc.DeleteLocaleStringResources("Plugins.Payment.Prepayment"); - loc.DeleteLocaleStringResources("Plugins.Payments.Manual"); - loc.DeleteLocaleStringResources("Plugins.Payments.DirectDebit"); + // delete resources + loc.DeleteLocaleStringResources(this.PluginDescriptor.ResourceRootKey); + loc.DeleteLocaleStringResources("Plugins.Payment.CashOnDelivery"); + loc.DeleteLocaleStringResources("Plugins.Payment.Invoice"); + loc.DeleteLocaleStringResources("Plugins.Payment.PayInStore"); + loc.DeleteLocaleStringResources("Plugins.Payment.Prepayment"); + loc.DeleteLocaleStringResources("Plugins.Payments.Manual"); + loc.DeleteLocaleStringResources("Plugins.Payments.DirectDebit"); base.Uninstall(); } diff --git a/src/Plugins/SmartStore.OfflinePayment/Providers/CashOnDeliveryProvider.cs b/src/Plugins/SmartStore.OfflinePayment/Providers/CashOnDeliveryProvider.cs index 696f1cbabd..624c71a941 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Providers/CashOnDeliveryProvider.cs +++ b/src/Plugins/SmartStore.OfflinePayment/Providers/CashOnDeliveryProvider.cs @@ -3,14 +3,14 @@ namespace SmartStore.OfflinePayment { - [SystemName("Payments.CashOnDelivery")] - [FriendlyName("Cash On Delivery (COD)")] - [DisplayOrder(1)] - public class CashOnDeliveryPaymentProcessor : OfflinePaymentProviderBase, IConfigurable - { - protected override string GetActionPrefix() - { - return "CashOnDelivery"; - } - } + [SystemName("Payments.CashOnDelivery")] + [FriendlyName("Cash On Delivery (COD)")] + [DisplayOrder(1)] + public class CashOnDeliveryPaymentProcessor : OfflinePaymentProviderBase, IConfigurable + { + protected override string GetActionPrefix() + { + return "CashOnDelivery"; + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.OfflinePayment/Providers/DirectDebitProvider.cs b/src/Plugins/SmartStore.OfflinePayment/Providers/DirectDebitProvider.cs index 88cc051a90..b2b40cd31b 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Providers/DirectDebitProvider.cs +++ b/src/Plugins/SmartStore.OfflinePayment/Providers/DirectDebitProvider.cs @@ -5,30 +5,24 @@ namespace SmartStore.OfflinePayment { - [SystemName("Payments.DirectDebit")] - [FriendlyName("Direct Debit")] - [DisplayOrder(1)] - public class DirectDebitProvider : OfflinePaymentProviderBase, IConfigurable - { - public override ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest) - { - var result = new ProcessPaymentResult(); - result.AllowStoringDirectDebit = true; - result.NewPaymentStatus = PaymentStatus.Pending; - return result; - } + [SystemName("Payments.DirectDebit")] + [FriendlyName("Direct Debit")] + [DisplayOrder(1)] + public class DirectDebitProvider : OfflinePaymentProviderBase, IConfigurable + { + public override ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest) + { + var result = new ProcessPaymentResult(); + result.AllowStoringDirectDebit = true; + result.NewPaymentStatus = PaymentStatus.Pending; + return result; + } - protected override string GetActionPrefix() - { - return "DirectDebit"; - } + protected override string GetActionPrefix() + { + return "DirectDebit"; + } - public override bool RequiresInteraction - { - get - { - return true; - } - } - } + public override bool RequiresInteraction => true; + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.OfflinePayment/Providers/InvoiceProvider.cs b/src/Plugins/SmartStore.OfflinePayment/Providers/InvoiceProvider.cs index 3608124f58..79d2d44e62 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Providers/InvoiceProvider.cs +++ b/src/Plugins/SmartStore.OfflinePayment/Providers/InvoiceProvider.cs @@ -3,14 +3,14 @@ namespace SmartStore.OfflinePayment { - [SystemName("Payments.Invoice")] - [FriendlyName("Invoice")] - [DisplayOrder(1)] - public class InvoiceProvider : OfflinePaymentProviderBase, IConfigurable - { - protected override string GetActionPrefix() - { - return "Invoice"; - } - } + [SystemName("Payments.Invoice")] + [FriendlyName("Invoice")] + [DisplayOrder(1)] + public class InvoiceProvider : OfflinePaymentProviderBase, IConfigurable + { + protected override string GetActionPrefix() + { + return "Invoice"; + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.OfflinePayment/Providers/ManualProvider.cs b/src/Plugins/SmartStore.OfflinePayment/Providers/ManualProvider.cs index 4f4331c2eb..bfcb3744d3 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Providers/ManualProvider.cs +++ b/src/Plugins/SmartStore.OfflinePayment/Providers/ManualProvider.cs @@ -7,103 +7,91 @@ namespace SmartStore.OfflinePayment { - [SystemName("Payments.Manual")] - [FriendlyName("Credit Card (manual)")] - [DisplayOrder(1)] - public class ManualProvider : OfflinePaymentProviderBase, IConfigurable - { - public static List CreditCardTypes - { - get - { - var creditCardTypes = new List - { - new SelectListItem { Text = "Visa", Value = "Visa" }, - new SelectListItem { Text = "Master Card", Value = "MasterCard" }, - new SelectListItem { Text = "Discover", Value = "Discover" }, - new SelectListItem { Text = "Amex", Value = "Amex" } - }; - return creditCardTypes; - } - } + [SystemName("Payments.Manual")] + [FriendlyName("Credit Card (manual)")] + [DisplayOrder(1)] + public class ManualProvider : OfflinePaymentProviderBase, IConfigurable + { + public static List CreditCardTypes + { + get + { + var creditCardTypes = new List + { + new SelectListItem { Text = "Visa", Value = "Visa" }, + new SelectListItem { Text = "Master Card", Value = "MasterCard" }, + new SelectListItem { Text = "Discover", Value = "Discover" }, + new SelectListItem { Text = "Amex", Value = "Amex" } + }; + return creditCardTypes; + } + } - public override ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest) - { - var result = new ProcessPaymentResult(); - var settings = CommonServices.Settings.LoadSetting(processPaymentRequest.StoreId); + public override ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest) + { + var result = new ProcessPaymentResult(); + var settings = CommonServices.Settings.LoadSetting(processPaymentRequest.StoreId); - result.AllowStoringCreditCardNumber = true; + result.AllowStoringCreditCardNumber = true; - switch (settings.TransactMode) - { - case TransactMode.Pending: - result.NewPaymentStatus = PaymentStatus.Pending; - break; - case TransactMode.Authorize: - result.NewPaymentStatus = PaymentStatus.Authorized; - break; - case TransactMode.Paid: - result.NewPaymentStatus = PaymentStatus.Paid; - break; - default: - result.AddError(T("Common.Payment.TranactionTypeNotSupported")); - return result; - } + switch (settings.TransactMode) + { + case TransactMode.Pending: + result.NewPaymentStatus = PaymentStatus.Pending; + break; + case TransactMode.Authorize: + result.NewPaymentStatus = PaymentStatus.Authorized; + break; + case TransactMode.Paid: + result.NewPaymentStatus = PaymentStatus.Paid; + break; + default: + result.AddError(T("Common.Payment.TranactionTypeNotSupported")); + return result; + } - return result; - } + return result; + } - public override ProcessPaymentResult ProcessRecurringPayment(ProcessPaymentRequest processPaymentRequest) - { - var result = new ProcessPaymentResult(); - var settings = CommonServices.Settings.LoadSetting(processPaymentRequest.StoreId); + public override ProcessPaymentResult ProcessRecurringPayment(ProcessPaymentRequest processPaymentRequest) + { + var result = new ProcessPaymentResult(); + var settings = CommonServices.Settings.LoadSetting(processPaymentRequest.StoreId); - result.AllowStoringCreditCardNumber = true; - switch (settings.TransactMode) - { - case TransactMode.Pending: - result.NewPaymentStatus = PaymentStatus.Pending; - break; - case TransactMode.Authorize: - result.NewPaymentStatus = PaymentStatus.Authorized; - break; - case TransactMode.Paid: - result.NewPaymentStatus = PaymentStatus.Paid; - break; - default: - { - result.AddError(T("Common.Payment.TranactionTypeNotSupported")); - return result; - } - } + result.AllowStoringCreditCardNumber = true; + switch (settings.TransactMode) + { + case TransactMode.Pending: + result.NewPaymentStatus = PaymentStatus.Pending; + break; + case TransactMode.Authorize: + result.NewPaymentStatus = PaymentStatus.Authorized; + break; + case TransactMode.Paid: + result.NewPaymentStatus = PaymentStatus.Paid; + break; + default: + { + result.AddError(T("Common.Payment.TranactionTypeNotSupported")); + return result; + } + } - return result; - } + return result; + } - public override CancelRecurringPaymentResult CancelRecurringPayment(CancelRecurringPaymentRequest cancelPaymentRequest) - { - return new CancelRecurringPaymentResult(); - } + public override CancelRecurringPaymentResult CancelRecurringPayment(CancelRecurringPaymentRequest cancelPaymentRequest) + { + return new CancelRecurringPaymentResult(); + } - public override RecurringPaymentType RecurringPaymentType - { - get - { - return RecurringPaymentType.Manual; - } - } + public override RecurringPaymentType RecurringPaymentType => RecurringPaymentType.Manual; - protected override string GetActionPrefix() - { - return "Manual"; - } + protected override string GetActionPrefix() + { + return "Manual"; + } - public override bool RequiresInteraction - { - get - { - return true; - } - } - } + public override bool RequiresInteraction => true; + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.OfflinePayment/Providers/OfflinePaymentProviderBase.cs b/src/Plugins/SmartStore.OfflinePayment/Providers/OfflinePaymentProviderBase.cs index abe6b73027..d5ed1a3a59 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Providers/OfflinePaymentProviderBase.cs +++ b/src/Plugins/SmartStore.OfflinePayment/Providers/OfflinePaymentProviderBase.cs @@ -12,61 +12,55 @@ namespace SmartStore.OfflinePayment { - public abstract class OfflinePaymentProviderBase : PaymentMethodBase - where TSetting : PaymentSettingsBase, ISettings, new() + public abstract class OfflinePaymentProviderBase : PaymentMethodBase + where TSetting : PaymentSettingsBase, ISettings, new() { - public ICommonServices CommonServices { get; set; } - public IOrderTotalCalculationService OrderTotalCalculationService { get; set; } + public ICommonServices CommonServices { get; set; } + public IOrderTotalCalculationService OrderTotalCalculationService { get; set; } - public override Type GetControllerType() - { - return typeof(OfflinePaymentController); - } + public override Type GetControllerType() + { + return typeof(OfflinePaymentController); + } - public override PaymentMethodType PaymentMethodType - { - get - { - return PaymentMethodType.Standard; - } - } + public override PaymentMethodType PaymentMethodType => PaymentMethodType.Standard; - protected abstract string GetActionPrefix(); + protected abstract string GetActionPrefix(); - public override decimal GetAdditionalHandlingFee(IList cart) - { - var result = decimal.Zero; - try - { - var settings = CommonServices.Settings.LoadSetting(CommonServices.StoreContext.CurrentStore.Id); + public override decimal GetAdditionalHandlingFee(IList cart) + { + var result = decimal.Zero; + try + { + var settings = CommonServices.Settings.LoadSetting(CommonServices.StoreContext.CurrentStore.Id); - result = this.CalculateAdditionalFee(OrderTotalCalculationService, cart, settings.AdditionalFee, settings.AdditionalFeePercentage); - } - catch (Exception) - { - } - return result; - } + result = this.CalculateAdditionalFee(OrderTotalCalculationService, cart, settings.AdditionalFee, settings.AdditionalFeePercentage); + } + catch (Exception) + { + } + return result; + } - public override void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) - { - actionName = "{0}Configure".FormatInvariant(GetActionPrefix()); - controllerName = "OfflinePayment"; - routeValues = new RouteValueDictionary() { { "area", "SmartStore.OfflinePayment" } }; - } + public override void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) + { + actionName = "{0}Configure".FormatInvariant(GetActionPrefix()); + controllerName = "OfflinePayment"; + routeValues = new RouteValueDictionary() { { "area", "SmartStore.OfflinePayment" } }; + } - public override void GetPaymentInfoRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) - { - actionName = "{0}PaymentInfo".FormatInvariant(GetActionPrefix()); - controllerName = "OfflinePayment"; - routeValues = new RouteValueDictionary() { { "area", "SmartStore.OfflinePayment" } }; - } + public override void GetPaymentInfoRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) + { + actionName = "{0}PaymentInfo".FormatInvariant(GetActionPrefix()); + controllerName = "OfflinePayment"; + routeValues = new RouteValueDictionary() { { "area", "SmartStore.OfflinePayment" } }; + } - public override ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest) - { - var result = new ProcessPaymentResult(); - result.NewPaymentStatus = PaymentStatus.Pending; - return result; - } - } + public override ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest) + { + var result = new ProcessPaymentResult(); + result.NewPaymentStatus = PaymentStatus.Pending; + return result; + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.OfflinePayment/Providers/PayInStoreProvider.cs b/src/Plugins/SmartStore.OfflinePayment/Providers/PayInStoreProvider.cs index 6d61b37828..b955337637 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Providers/PayInStoreProvider.cs +++ b/src/Plugins/SmartStore.OfflinePayment/Providers/PayInStoreProvider.cs @@ -3,14 +3,14 @@ namespace SmartStore.OfflinePayment { - [SystemName("Payments.PayInStore")] - [FriendlyName("Pay In Store")] - [DisplayOrder(1)] - public class PayInStoreProvider : OfflinePaymentProviderBase, IConfigurable - { - protected override string GetActionPrefix() - { - return "PayInStore"; - } - } + [SystemName("Payments.PayInStore")] + [FriendlyName("Pay In Store")] + [DisplayOrder(1)] + public class PayInStoreProvider : OfflinePaymentProviderBase, IConfigurable + { + protected override string GetActionPrefix() + { + return "PayInStore"; + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.OfflinePayment/Providers/PrepaymentStoreProvider.cs b/src/Plugins/SmartStore.OfflinePayment/Providers/PrepaymentStoreProvider.cs index 48b36849d4..9851200186 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Providers/PrepaymentStoreProvider.cs +++ b/src/Plugins/SmartStore.OfflinePayment/Providers/PrepaymentStoreProvider.cs @@ -3,14 +3,14 @@ namespace SmartStore.OfflinePayment { - [SystemName("Payments.Prepayment")] - [FriendlyName("Prepayment")] - [DisplayOrder(1)] - public class PrepaymentProvider : OfflinePaymentProviderBase, IConfigurable - { - protected override string GetActionPrefix() - { - return "Prepayment"; - } - } + [SystemName("Payments.Prepayment")] + [FriendlyName("Prepayment")] + [DisplayOrder(1)] + public class PrepaymentProvider : OfflinePaymentProviderBase, IConfigurable + { + protected override string GetActionPrefix() + { + return "Prepayment"; + } + } } \ No newline at end of file diff --git a/src/Plugins/SmartStore.OfflinePayment/Providers/PurchaseOrderNumberProvider.cs b/src/Plugins/SmartStore.OfflinePayment/Providers/PurchaseOrderNumberProvider.cs index 530dfd1d3a..0da5c3ec02 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Providers/PurchaseOrderNumberProvider.cs +++ b/src/Plugins/SmartStore.OfflinePayment/Providers/PurchaseOrderNumberProvider.cs @@ -1,12 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Web.Routing; -using SmartStore.Core.Domain.Orders; using SmartStore.Core.Domain.Payments; using SmartStore.Core.Plugins; using SmartStore.OfflinePayment.Settings; -using SmartStore.Services.Configuration; -using SmartStore.Services.Localization; using SmartStore.Services.Payments; namespace SmartStore.OfflinePayment @@ -16,15 +10,6 @@ namespace SmartStore.OfflinePayment [DisplayOrder(10)] public class PurchaseOrderNumberProvider : OfflinePaymentProviderBase, IConfigurable { - private readonly ISettingService _settingService; - private readonly ILocalizationService _localizationService; - - public PurchaseOrderNumberProvider(ISettingService settingService, ILocalizationService localizationService) - { - _settingService = settingService; - _localizationService = localizationService; - } - public override ProcessPaymentResult ProcessPayment(ProcessPaymentRequest processPaymentRequest) { var result = new ProcessPaymentResult(); @@ -43,27 +28,18 @@ public override ProcessPaymentResult ProcessPayment(ProcessPaymentRequest proces result.NewPaymentStatus = PaymentStatus.Paid; break; default: - { - result.AddError(T("Common.Payment.TranactionTypeNotSupported")); - return result; - } + result.AddError(T("Common.Payment.TranactionTypeNotSupported")); + return result; } return result; } - public override bool RequiresInteraction - { - get - { - return true; - } - } + public override bool RequiresInteraction => true; protected override string GetActionPrefix() { return "PurchaseOrderNumber"; } - } } diff --git a/src/Plugins/SmartStore.OfflinePayment/RouteProvider.cs b/src/Plugins/SmartStore.OfflinePayment/RouteProvider.cs index 70a8e63318..2960e936a4 100644 --- a/src/Plugins/SmartStore.OfflinePayment/RouteProvider.cs +++ b/src/Plugins/SmartStore.OfflinePayment/RouteProvider.cs @@ -8,19 +8,13 @@ public partial class RouteProvider : IRouteProvider { public void RegisterRoutes(RouteCollection routes) { - routes.MapRoute("SmartStore.OfflinePayment", - "Plugins/SmartStore.OfflinePayment/{action}", - new { controller = "OfflinePayment", action = "Index" }, - new[] { "SmartStore.OfflinePayment.Controllers" } + routes.MapRoute("SmartStore.OfflinePayment", + "Plugins/SmartStore.OfflinePayment/{action}", + new { controller = "OfflinePayment", action = "Index" }, + new[] { "SmartStore.OfflinePayment.Controllers" } ) - .DataTokens["area"] = "SmartStore.OfflinePayment"; - } - public int Priority - { - get - { - return 0; - } + .DataTokens["area"] = "SmartStore.OfflinePayment"; } + public int Priority => 0; } } diff --git a/src/Plugins/SmartStore.OfflinePayment/Settings/OfflinePaymentSettings.cs b/src/Plugins/SmartStore.OfflinePayment/Settings/OfflinePaymentSettings.cs index 89c409a2b8..34210ce565 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Settings/OfflinePaymentSettings.cs +++ b/src/Plugins/SmartStore.OfflinePayment/Settings/OfflinePaymentSettings.cs @@ -1,65 +1,64 @@ -using System.Collections.Generic; -using SmartStore.Core.Configuration; +using SmartStore.Core.Configuration; namespace SmartStore.OfflinePayment.Settings { - public abstract class PaymentSettingsBase : ISettings - { - public string DescriptionText { get; set; } - public int ThumbnailPictureId { get; set; } - public decimal AdditionalFee { get; set; } - public bool AdditionalFeePercentage { get; set; } - } + public abstract class PaymentSettingsBase : ISettings + { + public string DescriptionText { get; set; } + public int ThumbnailPictureId { get; set; } + public decimal AdditionalFee { get; set; } + public bool AdditionalFeePercentage { get; set; } + } - public class CashOnDeliveryPaymentSettings : PaymentSettingsBase, ISettings - { - } + public class CashOnDeliveryPaymentSettings : PaymentSettingsBase, ISettings + { + } - public class DirectDebitPaymentSettings : PaymentSettingsBase, ISettings - { - } + public class DirectDebitPaymentSettings : PaymentSettingsBase, ISettings + { + } - public class InvoicePaymentSettings : PaymentSettingsBase, ISettings - { - } + public class InvoicePaymentSettings : PaymentSettingsBase, ISettings + { + } - public class ManualPaymentSettings : PaymentSettingsBase, ISettings - { - public TransactMode TransactMode { get; set; } - public string ExcludedCreditCards { get; set; } - } + public class ManualPaymentSettings : PaymentSettingsBase, ISettings + { + public TransactMode TransactMode { get; set; } + public string ExcludedCreditCards { get; set; } + } public class PurchaseOrderNumberPaymentSettings : PaymentSettingsBase, ISettings { public TransactMode TransactMode { get; set; } } - public class PayInStorePaymentSettings : PaymentSettingsBase, ISettings - { - } + public class PayInStorePaymentSettings : PaymentSettingsBase, ISettings + { + } - public class PrepaymentPaymentSettings : PaymentSettingsBase, ISettings - { - } + public class PrepaymentPaymentSettings : PaymentSettingsBase, ISettings + { + } - /// - /// Represents manual payment processor transaction mode - /// - public enum TransactMode - { - /// - /// Pending - /// - Pending = 0, + /// + /// Represents manual payment processor transaction mode + /// + public enum TransactMode + { + /// + /// Pending + /// + Pending = 0, - /// - /// Authorize - /// - Authorize = 1, + /// + /// Authorize + /// + Authorize = 1, - /// - /// Paid - /// - Paid = 2 - } + /// + /// Paid + /// + Paid = 2 + } } \ 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 d7f708d887..b392fe891f 100644 --- a/src/Plugins/SmartStore.OfflinePayment/SmartStore.OfflinePayment.csproj +++ b/src/Plugins/SmartStore.OfflinePayment/SmartStore.OfflinePayment.csproj @@ -1,6 +1,5 @@  - @@ -21,7 +20,7 @@ Properties SmartStore.OfflinePayment SmartStore.OfflinePayment - v4.6.1 + v4.7.2 512 @@ -45,6 +44,8 @@ + + true @@ -84,32 +85,45 @@ MinimumRecommendedRules.ruleset - - ..\..\packages\Autofac.4.9.0\lib\net45\Autofac.dll + + ..\..\packages\Autofac.5.2.0\lib\net461\Autofac.dll False ..\..\packages\FluentValidation.7.4.0\lib\net45\FluentValidation.dll False + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.3.6.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + False + ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll False - - ..\..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll - False + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + - + ..\..\packages\Microsoft.AspNet.WebPages.3.2.7\lib\net45\System.Web.Helpers.dll False diff --git a/src/Plugins/SmartStore.OfflinePayment/Views/OfflinePayment/DirectDebitPaymentInfo.cshtml b/src/Plugins/SmartStore.OfflinePayment/Views/OfflinePayment/DirectDebitPaymentInfo.cshtml index 6893c04136..85d6a332b2 100644 --- a/src/Plugins/SmartStore.OfflinePayment/Views/OfflinePayment/DirectDebitPaymentInfo.cshtml +++ b/src/Plugins/SmartStore.OfflinePayment/Views/OfflinePayment/DirectDebitPaymentInfo.cshtml @@ -29,11 +29,11 @@ else
              - +
              - +
              @@ -55,8 +55,8 @@ else @Html.ControlGroupFor(model => model.DirectDebitBankName, required: true, breakpoint: "md") - diff --git a/src/Plugins/SmartStore.WebApi/WebApiConfigurationProvider.cs b/src/Plugins/SmartStore.WebApi/WebApiConfigurationProvider.cs new file mode 100644 index 0000000000..6a3b920569 --- /dev/null +++ b/src/Plugins/SmartStore.WebApi/WebApiConfigurationProvider.cs @@ -0,0 +1,154 @@ +using System; +using System.IO; +using SmartStore.Core.Domain.Blogs; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Domain.Common; +using SmartStore.Core.Domain.Configuration; +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Domain.DataExchange; +using SmartStore.Core.Domain.Directory; +using SmartStore.Core.Domain.Discounts; +using SmartStore.Core.Domain.Localization; +using SmartStore.Core.Domain.Media; +using SmartStore.Core.Domain.Messages; +using SmartStore.Core.Domain.Orders; +using SmartStore.Core.Domain.Payments; +using SmartStore.Core.Domain.Seo; +using SmartStore.Core.Domain.Shipping; +using SmartStore.Core.Domain.Stores; +using SmartStore.Core.Domain.Tax; +using SmartStore.Core.Plugins; +using SmartStore.Web.Framework.WebApi; +using SmartStore.Web.Framework.WebApi.Configuration; +using SmartStore.WebApi.Models.OData.Media; +using SmartStore.WebApi.Services; +using SmartStore.WebApi.Services.Swagger; +using Swashbuckle.Application; + +namespace SmartStore.WebApi +{ + public partial class WebApiConfigurationProvider : IWebApiConfigurationProvider + { + public int Priority => 0; + + public void Configure(WebApiConfigurationBroadcaster configData) + { + var m = configData.ModelBuilder; + + m.EntitySet
              ("Addresses"); + m.EntitySet("BlogComments"); + m.EntitySet("BlogPosts"); + m.EntitySet("Categories"); + m.EntitySet("Countries"); + m.EntitySet("Currencies"); + m.EntitySet("Customers"); + m.EntitySet("CustomerRoles"); + m.EntitySet("CustomerRoleMappings"); + m.EntitySet("DeliveryTimes"); + m.EntitySet("Discounts"); + m.EntitySet("Downloads"); + m.EntitySet("GenericAttributes"); + m.EntitySet("Languages"); + m.EntitySet("LocalizedProperties"); + m.EntitySet("Manufacturers"); + m.EntitySet("MeasureDimensions"); + m.EntitySet("MeasureWeights"); + + m.EntitySet("MediaFiles"); + m.EntitySet("MediaFolders"); + m.EntitySet("MediaTags"); + m.EntitySet("MediaTracks"); + + m.EntitySet("NewsLetterSubscriptions"); + m.EntitySet("OrderNotes"); + m.EntitySet("Orders"); + m.EntitySet("OrderItems"); + m.EntitySet("PaymentMethods"); + m.EntitySet("ProductAttributes"); + m.EntitySet("ProductAttributeOptions"); + m.EntitySet("ProductAttributeOptionsSets"); + m.EntitySet("ProductBundleItems"); + m.EntitySet("ProductCategories"); + m.EntitySet("ProductManufacturers"); + m.EntitySet("ProductPictures"); + m.EntitySet("Products"); + m.EntitySet("ProductSpecificationAttributes"); + m.EntitySet("ProductTags"); + m.EntitySet("ProductVariantAttributes"); + m.EntitySet("ProductVariantAttributeValues"); + m.EntitySet("ProductVariantAttributeCombinations"); + m.EntitySet("QuantityUnits"); + m.EntitySet("RelatedProducts"); + m.EntitySet("ReturnRequests"); + m.EntitySet("RewardPointsHistory"); + m.EntitySet("Settings"); + m.EntitySet("Shipments"); + m.EntitySet("ShipmentItems"); + m.EntitySet("ShippingMethods"); + m.EntitySet("SpecificationAttributeOptions"); + m.EntitySet("SpecificationAttributes"); + m.EntitySet("StateProvinces"); + m.EntitySet("Stores"); + m.EntitySet("StoreMappings"); + m.EntitySet("TaxCategories"); + m.EntitySet("TierPrices"); + m.EntitySet("UrlRecords"); + m.EntitySet("SyncMappings"); + + // Register OData actions and functions. + Controllers.OData.DeliveryTimesController.Init(configData); + Controllers.OData.OrdersController.Init(configData); + Controllers.OData.OrderItemsController.Init(configData); + Controllers.OData.ProductsController.Init(configData); + Controllers.OData.MediaFilesController.Init(configData); + Controllers.OData.MediaFoldersController.Init(configData); + + // Custom routing convention. + configData.RoutingConventions.Insert(0, new CustomRoutingConvention()); + + // Swagger integration, see http://www.my-store.com/swagger/ui/index + var thisAssembly = typeof(WebApiConfigurationProvider).Assembly; + var pluginFinder = configData.Configuration.DependencyResolver.GetService(typeof(IPluginFinder)) as IPluginFinder; + var pluginDescriptor = pluginFinder.GetPluginDescriptorBySystemName(WebApiGlobal.PluginSystemName); + var xmlCommentPath = Path.Combine(pluginDescriptor.PhysicalPath, "SmartStore.WebApi.xml"); + + try + { + if (!configData.Configuration.Routes.ContainsKey("swagger_docsswagger/docs/{apiVersion}")) + { + configData.Configuration.EnableSwagger(x => + { + x.SingleApiVersion("v1", pluginDescriptor.Description) + .Description(pluginDescriptor.Description); + + // Required to display odata endpoints + x.DocumentFilter(); + //x.SchemaFilter(); + x.OperationFilter(); + + if (File.Exists(xmlCommentPath)) + { + x.IncludeXmlComments(xmlCommentPath); + } + }) + .EnableSwaggerUi(x => + { + // JavaScript required for authentication. + x.InjectJavaScript(thisAssembly, "SmartStore.WebApi.Scripts.CryptoJs.components.core-min.js"); + x.InjectJavaScript(thisAssembly, "SmartStore.WebApi.Scripts.CryptoJs.components.enc-base64-min.js"); + x.InjectJavaScript(thisAssembly, "SmartStore.WebApi.Scripts.CryptoJs.components.enc-utf16-min.js"); + x.InjectJavaScript(thisAssembly, "SmartStore.WebApi.Scripts.CryptoJs.rollups.hmac-md5.js"); + x.InjectJavaScript(thisAssembly, "SmartStore.WebApi.Scripts.CryptoJs.rollups.hmac-sha256.js"); + + x.InjectJavaScript(thisAssembly, "SmartStore.WebApi.Scripts.smwapi-consumer.js"); + x.InjectJavaScript(thisAssembly, "SmartStore.WebApi.Scripts.swagger-auth.js"); + }); + } + } + catch (Exception ex) + { + ex.Dump(); + } + } + } +} diff --git a/src/Plugins/SmartStore.WebApi/WebApiPlugin.cs b/src/Plugins/SmartStore.WebApi/WebApiPlugin.cs new file mode 100644 index 0000000000..ea42dc0e17 --- /dev/null +++ b/src/Plugins/SmartStore.WebApi/WebApiPlugin.cs @@ -0,0 +1,55 @@ +using System.Web.Routing; +using SmartStore.Core.Plugins; +using SmartStore.Core.Security; +using SmartStore.Services.Configuration; +using SmartStore.Services.Localization; +using SmartStore.Web.Framework.WebApi; +using SmartStore.Web.Framework.WebApi.Caching; + +namespace SmartStore.WebApi +{ + public class WebApiPlugin : BasePlugin, IConfigurable + { + private readonly IPermissionService _permissionService; + private readonly ILocalizationService _localizationService; + private readonly ISettingService _settingService; + + public WebApiPlugin( + IPermissionService permissionService, + ILocalizationService localizationService, + ISettingService settingService) + { + _permissionService = permissionService; + _localizationService = localizationService; + _settingService = settingService; + } + + public void GetConfigurationRoute(out string actionName, out string controllerName, out RouteValueDictionary routeValues) + { + actionName = "Configure"; + controllerName = "WebApi"; + routeValues = new RouteValueDictionary { { "Namespaces", "SmartStore.WebApi.Controllers" }, { "area", WebApiGlobal.PluginSystemName } }; + } + + public override void Install() + { + _settingService.SaveSetting(new WebApiSettings()); + _localizationService.ImportPluginResourcesFromXml(PluginDescriptor); + + base.Install(); + + WebApiCachingControllingData.Remove(); + WebApiCachingUserData.Remove(); + } + + public override void Uninstall() + { + WebApiCachingControllingData.Remove(); + WebApiCachingUserData.Remove(); + + _settingService.DeleteSetting(); + + base.Uninstall(); + } + } +} diff --git a/src/Plugins/SmartStore.WebApi/packages.config b/src/Plugins/SmartStore.WebApi/packages.config new file mode 100644 index 0000000000..c618878ca0 --- /dev/null +++ b/src/Plugins/SmartStore.WebApi/packages.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Plugins/SmartStore.WebApi/web.config b/src/Plugins/SmartStore.WebApi/web.config index 20134fcc96..a5aba1760a 100644 --- a/src/Plugins/SmartStore.WebApi/web.config +++ b/src/Plugins/SmartStore.WebApi/web.config @@ -4,9 +4,10 @@ + - + @@ -34,35 +35,31 @@ - + - + - - - - - + - + - + - + - + @@ -72,6 +69,14 @@ + + + + + + + + @@ -82,8 +87,61 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + + + + + + + + + + + + + + diff --git a/src/Presentation/SmartStore.Web.Framework/Bundling/BundlePublisher.cs b/src/Presentation/SmartStore.Web.Framework/Bundling/BundlePublisher.cs index b8c831476e..7232a13e11 100644 --- a/src/Presentation/SmartStore.Web.Framework/Bundling/BundlePublisher.cs +++ b/src/Presentation/SmartStore.Web.Framework/Bundling/BundlePublisher.cs @@ -17,7 +17,7 @@ public BundlePublisher(ITypeFinder typeFinder) public void RegisterBundles(BundleCollection bundles) { - var bundleProviderTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true); + var bundleProviderTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true); var bundleProviders = new List(); foreach (var providerType in bundleProviderTypes) { diff --git a/src/Presentation/SmartStore.Web.Framework/Controllers/AdminControllerBase.cs b/src/Presentation/SmartStore.Web.Framework/Controllers/AdminControllerBase.cs index f04be13344..e390c90952 100644 --- a/src/Presentation/SmartStore.Web.Framework/Controllers/AdminControllerBase.cs +++ b/src/Presentation/SmartStore.Web.Framework/Controllers/AdminControllerBase.cs @@ -3,43 +3,44 @@ using SmartStore.Core; using SmartStore.Web.Framework.Filters; using SmartStore.Web.Framework.Security; +using SmartStore.Web.Framework.Seo; using SmartStore.Web.Framework.Theming; namespace SmartStore.Web.Framework.Controllers { - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] - public sealed class NonAdminAttribute : FilterAttribute, IActionFilter - { - public Lazy WorkContext { get; set; } + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] + public sealed class NonAdminAttribute : FilterAttribute, IActionFilter + { + public Lazy WorkContext { get; set; } - public void OnActionExecuting(ActionExecutingContext filterContext) - { - } + public void OnActionExecuting(ActionExecutingContext filterContext) + { + } - public void OnActionExecuted(ActionExecutedContext filterContext) - { - if (!filterContext.IsChildAction) - { - WorkContext.Value.IsAdmin = false; - filterContext.HttpContext.Items["IsNonAdmin"] = true; - } - } - } + public void OnActionExecuted(ActionExecutedContext filterContext) + { + if (!filterContext.IsChildAction) + { + WorkContext.Value.IsAdmin = false; + filterContext.HttpContext.Items["IsNonAdmin"] = true; + } + } + } - [AdminValidateIpAddress(Order = 100)] - [RewriteUrl(SslRequirement.Yes, Order = 110)] - [CustomerLastActivity(Order = 100)] - [StoreIpAddress(Order = 100)] - [AdminThemed(Order = -1)] - public abstract class AdminControllerBase : ManageController - { - protected override void OnActionExecuting(ActionExecutingContext filterContext) - { - if (filterContext.IsChildAction) - return; + [AdminValidateIpAddress] + [RewriteUrl(SslRequirement.Yes, Order = 0, AppendTrailingSlash = false, LowercaseUrls = false)] + [CustomerLastActivity(Order = int.MaxValue)] + [StoreIpAddress(Order = int.MaxValue)] + [AdminThemed] + public abstract class AdminControllerBase : ManageController + { + protected override void OnActionExecuting(ActionExecutingContext filterContext) + { + if (filterContext.IsChildAction) + return; - var isNonAdmin = filterContext.ActionDescriptor.HasAttribute(true); - Services.WorkContext.IsAdmin = !isNonAdmin; - } + var isNonAdmin = filterContext.ActionDescriptor.HasAttribute(true); + Services.WorkContext.IsAdmin = !isNonAdmin; + } } } diff --git a/src/Presentation/SmartStore.Web.Framework/Controllers/ContollerExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Controllers/ContollerExtensions.cs index 1d17a3d790..063643c745 100644 --- a/src/Presentation/SmartStore.Web.Framework/Controllers/ContollerExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Controllers/ContollerExtensions.cs @@ -1,7 +1,5 @@ -using System; -using System.Globalization; +using System.Globalization; using System.IO; -using System.Text; using System.Web.Mvc; using System.Web.Mvc.Html; using System.Web.Routing; @@ -16,111 +14,111 @@ namespace SmartStore.Web.Framework.Controllers { - public static class ContollerExtensions + public static class ContollerExtensions { - #region InvokeAction - - public static MvcHtmlString InvokeAction(this ControllerBase controller, string actionName, string controllerName = null, RouteValueDictionary routeValues = null) - { - Guard.NotNull(controller, nameof(controller)); - Guard.NotEmpty(actionName, nameof(actionName)); - - using (StringWriter writer = new StringWriter(CultureInfo.CurrentCulture)) - { - InvokeAction(controller, writer, actionName, controllerName, routeValues); - return MvcHtmlString.Create(writer.ToString()); - } - } - - public static void InvokeAction(this ControllerBase controller, TextWriter writer, string actionName, string controllerName = null, RouteValueDictionary routeValues = null) - { - Guard.NotNull(controller, nameof(controller)); - Guard.NotNull(writer, nameof(writer)); - Guard.NotEmpty(actionName, nameof(actionName)); - - var viewContext = new ViewContext( - controller.ControllerContext, - new WebFormView(controller.ControllerContext, "tmp"), - controller.ViewData, - controller.TempData, - writer - ); - - var htmlHelper = new HtmlHelper(viewContext, new ViewPage()); - htmlHelper.RenderAction(actionName, controllerName, routeValues); - } - - #endregion - - #region Render view - - /// - /// Render partial view to string - /// - /// Result - public static string RenderPartialViewToString(this ControllerBase controller) + #region InvokeAction + + public static MvcHtmlString InvokeAction(this ControllerBase controller, string actionName, string controllerName = null, RouteValueDictionary routeValues = null) + { + Guard.NotNull(controller, nameof(controller)); + Guard.NotEmpty(actionName, nameof(actionName)); + + using (StringWriter writer = new StringWriter(CultureInfo.CurrentCulture)) + { + InvokeAction(controller, writer, actionName, controllerName, routeValues); + return MvcHtmlString.Create(writer.ToString()); + } + } + + public static void InvokeAction(this ControllerBase controller, TextWriter writer, string actionName, string controllerName = null, RouteValueDictionary routeValues = null) + { + Guard.NotNull(controller, nameof(controller)); + Guard.NotNull(writer, nameof(writer)); + Guard.NotEmpty(actionName, nameof(actionName)); + + var viewContext = new ViewContext( + controller.ControllerContext, + new WebFormView(controller.ControllerContext, "tmp"), + controller.ViewData, + controller.TempData, + writer + ); + + var htmlHelper = new HtmlHelper(viewContext, new ViewPage()); + htmlHelper.RenderAction(actionName, controllerName, routeValues); + } + + #endregion + + #region Render view + + /// + /// Render partial view to string + /// + /// Result + public static string RenderPartialViewToString(this ControllerBase controller) { return RenderPartialViewToString(controller, null, null, null); } - /// - /// Render partial view to string - /// - /// - /// View name - /// Result - public static string RenderPartialViewToString(this ControllerBase controller, string viewName) + /// + /// Render partial view to string + /// + /// + /// View name + /// Result + public static string RenderPartialViewToString(this ControllerBase controller, string viewName) { return RenderPartialViewToString(controller, viewName, null, null); } - /// - /// Render partial view to string - /// - /// - /// Model - /// Result - public static string RenderPartialViewToString(this ControllerBase controller, object model) + /// + /// Render partial view to string + /// + /// + /// Model + /// Result + public static string RenderPartialViewToString(this ControllerBase controller, object model) { return RenderPartialViewToString(controller, null, model, null); } - /// - /// Render partial view to string - /// - /// - /// View name - /// Model - /// Result - public static string RenderPartialViewToString(this ControllerBase controller, string viewName, object model) - { - return RenderPartialViewToString(controller, viewName, model, null); - } - - /// - /// Render partial view to string - /// - /// - /// View name - /// Model - /// Additional view data - /// Result - public static string RenderPartialViewToString(this ControllerBase controller, string viewName, object model, object additionalViewData) + /// + /// Render partial view to string + /// + /// + /// View name + /// Model + /// Result + public static string RenderPartialViewToString(this ControllerBase controller, string viewName, object model) + { + return RenderPartialViewToString(controller, viewName, model, null); + } + + /// + /// Render partial view to string + /// + /// + /// View name + /// Model + /// Additional view data + /// Result + public static string RenderPartialViewToString(this ControllerBase controller, string viewName, object model, object additionalViewData) { if (viewName.IsEmpty()) viewName = controller.ControllerContext.RouteData.GetRequiredString("action"); controller.ViewData.Model = model; - if (additionalViewData != null) - { - controller.ViewData.AddRange(CommonHelper.ObjectToDictionary(additionalViewData)); + if (additionalViewData != null) + { + controller.ViewData.AddRange(CommonHelper.ObjectToDictionary(additionalViewData)); - if (additionalViewData is ViewDataDictionary vdd) - { - controller.ViewData.TemplateInfo.HtmlFieldPrefix = vdd.TemplateInfo.HtmlFieldPrefix; - } - } + if (additionalViewData is ViewDataDictionary vdd) + { + controller.ViewData.TemplateInfo.HtmlFieldPrefix = vdd.TemplateInfo.HtmlFieldPrefix; + } + } using (var sw = new StringWriter()) { @@ -135,187 +133,179 @@ public static string RenderPartialViewToString(this ControllerBase controller, s } } - /// - /// Render view to string - /// - /// Result - public static string RenderViewToString(this ControllerBase controller) - { - return RenderViewToString(controller, null, null, null); - } - - /// - /// Render view to string - /// - /// Model - /// Result - public static string RenderViewToString(this ControllerBase controller, object model) - { - return RenderViewToString(controller, null, null, model); - } - - /// - /// Render view to string - /// - /// View name - /// Result - public static string RenderViewToString(this ControllerBase controller, string viewName) - { - return RenderViewToString(controller, viewName, null, null); - } - - /// - /// Render view to string - /// - /// View name - /// Model - /// Result - public static string RenderViewToString(this ControllerBase controller, string viewName, string masterName) - { - return RenderViewToString(controller, viewName, masterName, null); - } - - /// - /// Render view to string - /// - /// View name - /// Master name - /// Model - /// Result - public static string RenderViewToString(this ControllerBase controller, string viewName, string masterName, object model) - { - if (viewName.IsEmpty()) - viewName = controller.ControllerContext.RouteData.GetRequiredString("action"); - - controller.ViewData.Model = model; - - using (var sw = new StringWriter()) - { - var viewResult = System.Web.Mvc.ViewEngines.Engines.FindView(controller.ControllerContext, viewName.EmptyNull(), masterName.EmptyNull()); - - ThrowIfViewNotFound(viewResult, viewName); - - var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw); - viewResult.View.Render(viewContext, sw); - - return sw.GetStringBuilder().ToString(); - } - } - - private static void ThrowIfViewNotFound(ViewEngineResult viewResult, string viewName) - { - // if view not found, throw an exception with searched locations - if (viewResult.View == null) - { - var locations = new StringBuilder(); - locations.AppendLine(); - - foreach (string location in viewResult.SearchedLocations) - { - locations.AppendLine(location); - } - - throw new InvalidOperationException(string.Format("The view '{0}' or its master was not found, searched locations: {1}", viewName, locations)); - } - } - - #endregion - - #region RootActionView - - /// - /// Creates a object using the root actions's for view resolution. - /// - public static ViewResult RootActionView(this Controller controller, string viewName) - { - return RootActionView(controller, viewName, (string)null, null); - } - - /// - /// Creates a object using the root actions's for view resolution. - /// - public static ViewResult RootActionView(this Controller controller, string viewName, string masterName) - { - return RootActionView(controller, viewName, masterName, null); - } - - /// - /// Creates a object using the root actions's for view resolution. - /// - public static ViewResult RootActionView(this Controller controller, string viewName, object model) - { - return RootActionView(controller, viewName, (string)null, model); - } - - /// - /// Creates a object using the root actions's for view resolution. - /// - public static ViewResult RootActionView(this Controller controller, string viewName, string masterName, object model) - { - Guard.NotEmpty(viewName, nameof(viewName)); - - if (model != null) - { - controller.ViewData.Model = model; - } - - return new RootActionViewResult - { - ViewName = viewName, - MasterName = masterName, - ViewData = controller.ViewData, - TempData = controller.TempData, - ViewEngineCollection = controller.ViewEngineCollection - }; - } - - /// - /// Creates a object using the root actions's for view resolution. - /// - public static PartialViewResult RootActionPartialView(this Controller controller, string viewName) - { - return RootActionPartialView(controller, viewName, null); - } - - /// - /// Creates a object using the root actions's for view resolution. - /// - public static PartialViewResult RootActionPartialView(this Controller controller, string viewName, object model) - { - Guard.NotEmpty(viewName, nameof(viewName)); - - if (model != null) - { - controller.ViewData.Model = model; - } - - return new RootActionPartialViewResult - { - ViewName = viewName, - ViewData = controller.ViewData, - TempData = controller.TempData, - ViewEngineCollection = controller.ViewEngineCollection - }; - } - - #endregion - - /// - /// Get active store scope (for multi-store configuration mode) - /// - /// Controller - /// Store service - /// Work context - /// Store ID; 0 if we are in a shared mode - public static int GetActiveStoreScopeConfiguration(this IController controller, IStoreService storeService, IWorkContext workContext) - { - //ensure that we have 2 (or more) stores - if (storeService.GetAllStores().Count < 2) - return 0; - - var storeId = workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.AdminAreaStoreScopeConfiguration); - var store = storeService.GetStoreById(storeId); - return store != null ? store.Id : 0; - } + /// + /// Render view to string + /// + /// Result + public static string RenderViewToString(this ControllerBase controller) + { + return RenderViewToString(controller, null, null, null); + } + + /// + /// Render view to string + /// + /// Model + /// Result + public static string RenderViewToString(this ControllerBase controller, object model) + { + return RenderViewToString(controller, null, null, model); + } + + /// + /// Render view to string + /// + /// View name + /// Result + public static string RenderViewToString(this ControllerBase controller, string viewName) + { + return RenderViewToString(controller, viewName, null, null); + } + + /// + /// Render view to string + /// + /// View name + /// Model + /// Result + public static string RenderViewToString(this ControllerBase controller, string viewName, string masterName) + { + return RenderViewToString(controller, viewName, masterName, null); + } + + /// + /// Render view to string + /// + /// View name + /// Master name + /// Model + /// Result + public static string RenderViewToString(this ControllerBase controller, string viewName, string masterName, object model) + { + if (viewName.IsEmpty()) + viewName = controller.ControllerContext.RouteData.GetRequiredString("action"); + + controller.ViewData.Model = model; + + using (var sw = new StringWriter()) + { + var viewResult = System.Web.Mvc.ViewEngines.Engines.FindView(controller.ControllerContext, viewName.EmptyNull(), masterName.EmptyNull()); + + ThrowIfViewNotFound(viewResult, viewName); + + var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw); + viewResult.View.Render(viewContext, sw); + + return sw.GetStringBuilder().ToString(); + } + } + + private static void ThrowIfViewNotFound(ViewEngineResult viewResult, string viewName) + { + // if view not found, throw an exception with searched locations + if (viewResult.View == null) + { + throw new ViewNotFoundException(viewName, viewResult.SearchedLocations); + } + } + + #endregion + + #region RootActionView + + /// + /// Creates a object using the root actions's for view resolution. + /// + public static ViewResult RootActionView(this Controller controller, string viewName) + { + return RootActionView(controller, viewName, (string)null, null); + } + + /// + /// Creates a object using the root actions's for view resolution. + /// + public static ViewResult RootActionView(this Controller controller, string viewName, string masterName) + { + return RootActionView(controller, viewName, masterName, null); + } + + /// + /// Creates a object using the root actions's for view resolution. + /// + public static ViewResult RootActionView(this Controller controller, string viewName, object model) + { + return RootActionView(controller, viewName, (string)null, model); + } + + /// + /// Creates a object using the root actions's for view resolution. + /// + public static ViewResult RootActionView(this Controller controller, string viewName, string masterName, object model) + { + Guard.NotEmpty(viewName, nameof(viewName)); + + if (model != null) + { + controller.ViewData.Model = model; + } + + return new RootActionViewResult + { + ViewName = viewName, + MasterName = masterName, + ViewData = controller.ViewData, + TempData = controller.TempData, + ViewEngineCollection = controller.ViewEngineCollection + }; + } + + /// + /// Creates a object using the root actions's for view resolution. + /// + public static PartialViewResult RootActionPartialView(this Controller controller, string viewName) + { + return RootActionPartialView(controller, viewName, null); + } + + /// + /// Creates a object using the root actions's for view resolution. + /// + public static PartialViewResult RootActionPartialView(this Controller controller, string viewName, object model) + { + Guard.NotEmpty(viewName, nameof(viewName)); + + if (model != null) + { + controller.ViewData.Model = model; + } + + return new RootActionPartialViewResult + { + ViewName = viewName, + ViewData = controller.ViewData, + TempData = controller.TempData, + ViewEngineCollection = controller.ViewEngineCollection + }; + } + + #endregion + + /// + /// Get active store scope (for multi-store configuration mode) + /// + /// Controller + /// Store service + /// Work context + /// Store ID; 0 if we are in a shared mode + public static int GetActiveStoreScopeConfiguration(this IController controller, IStoreService storeService, IWorkContext workContext) + { + //ensure that we have 2 (or more) stores + if (storeService.GetAllStores().Count < 2) + return 0; + + var storeId = workContext.CurrentCustomer.GetAttribute(SystemCustomerAttributeNames.AdminAreaStoreScopeConfiguration); + var store = storeService.GetStoreById(storeId); + return store != null ? store.Id : 0; + } } } diff --git a/src/Presentation/SmartStore.Web.Framework/Controllers/ManageController.cs b/src/Presentation/SmartStore.Web.Framework/Controllers/ManageController.cs index d49d4d6a5c..29b187f3c7 100644 --- a/src/Presentation/SmartStore.Web.Framework/Controllers/ManageController.cs +++ b/src/Presentation/SmartStore.Web.Framework/Controllers/ManageController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Web.Mvc; using SmartStore.Core; using SmartStore.Core.Domain.Security; @@ -10,19 +11,18 @@ using SmartStore.Services.Security; using SmartStore.Services.Stores; using SmartStore.Web.Framework.Localization; -using SmartStore.Web.Framework.Modelling; namespace SmartStore.Web.Framework.Controllers { - public abstract class ManageController : SmartController - { - /// - /// Add locales for localizable entities - /// - /// Localizable model - /// Language service - /// Locales - protected virtual void AddLocales(ILanguageService languageService, IList locales) where TLocalizedModelLocal : ILocalizedModelLocal + public abstract class ManageController : SmartController + { + /// + /// Add locales for localizable entities + /// + /// Localizable model + /// Language service + /// Locales + protected virtual void AddLocales(ILanguageService languageService, IList locales) where TLocalizedModelLocal : ILocalizedModelLocal { AddLocales(languageService, locales, null); } @@ -48,46 +48,46 @@ protected virtual void AddLocales(ILanguageService languag } } - /// - /// Save the store mappings for an entity - /// - /// Entity type - /// The entity - /// Model representation of store selection - protected virtual void SaveStoreMappings(T entity, IStoreSelector model) where T : BaseEntity, IStoreMappingSupported - { - entity.LimitedToStores = model.LimitedToStores; - Services.Resolve().SaveStoreMappings(entity, model.SelectedStoreIds); - } + /// + /// Save the store mappings for an entity. + /// + /// Entity type. + /// The entity. + /// Selected store identifiers. + protected virtual void SaveStoreMappings(T entity, int[] selectedStoreIds) where T : BaseEntity, IStoreMappingSupported + { + Guard.NotNull(entity, nameof(entity)); - /// - /// Save the ACL mappings for an entity - /// - /// Entity type - /// The entity - /// Model representation of ACL selection - protected virtual void SaveAclMappings(T entity, IAclSelector model) where T : BaseEntity, IAclSupported - { - entity.SubjectToAcl = model.SubjectToAcl; - Services.Resolve().SaveAclMappings(entity, model.SelectedCustomerRoleIds); - } + Services.Resolve().SaveStoreMappings(entity, selectedStoreIds); + } - /// - /// Access denied view - /// - /// Access denied view - [SuppressMessage("ReSharper", "Mvc.AreaNotResolved")] + /// + /// Save the ACL mappings for an entity. + /// + /// Entity type + /// The entity + /// Model representation of ACL selection + protected virtual void SaveAclMappings(T entity, params int[] selectedCustomerRoleIds) where T : BaseEntity, IAclSupported + { + Services.Resolve().SaveAclMappings(entity, selectedCustomerRoleIds ?? Array.Empty()); + } + + /// + /// Access denied view + /// + /// Access denied view + [SuppressMessage("ReSharper", "Mvc.AreaNotResolved")] protected ActionResult AccessDeniedView() { return RedirectToAction("AccessDenied", "Security", new { pageUrl = this.Request.RawUrl, area = "Admin" }); } - /// - /// Renders default access denied view as a partial - /// - protected ActionResult AccessDeniedPartialView() - { - return PartialView("~/Administration/Views/Security/AccessDenied.cshtml"); - } + /// + /// Renders default access denied view as a partial + /// + protected ActionResult AccessDeniedPartialView() + { + return PartialView("~/Administration/Views/Security/AccessDenied.cshtml"); + } } } diff --git a/src/Presentation/SmartStore.Web.Framework/Controllers/PaymentControllerBase.cs b/src/Presentation/SmartStore.Web.Framework/Controllers/PaymentControllerBase.cs index afb217a5ac..00aefaf66c 100644 --- a/src/Presentation/SmartStore.Web.Framework/Controllers/PaymentControllerBase.cs +++ b/src/Presentation/SmartStore.Web.Framework/Controllers/PaymentControllerBase.cs @@ -4,14 +4,14 @@ namespace SmartStore.Web.Framework.Controllers { - public abstract class PaymentControllerBase : SmartController + public abstract class PaymentControllerBase : SmartController { public abstract IList ValidatePaymentForm(FormCollection form); public abstract ProcessPaymentRequest GetPaymentInfo(FormCollection form); - public virtual string GetPaymentSummary(FormCollection form) - { - return null; - } + public virtual string GetPaymentSummary(FormCollection form) + { + return null; + } } } diff --git a/src/Presentation/SmartStore.Web.Framework/Controllers/PluginControllerBase.cs b/src/Presentation/SmartStore.Web.Framework/Controllers/PluginControllerBase.cs index 4a3acc5710..aca65ba40d 100644 --- a/src/Presentation/SmartStore.Web.Framework/Controllers/PluginControllerBase.cs +++ b/src/Presentation/SmartStore.Web.Framework/Controllers/PluginControllerBase.cs @@ -1,7 +1,4 @@ -using System; -using System.Web.Mvc; - -namespace SmartStore.Web.Framework.Controllers +namespace SmartStore.Web.Framework.Controllers { public abstract partial class PluginControllerBase : ManageController { diff --git a/src/Presentation/SmartStore.Web.Framework/Controllers/PublicControllerBase.cs b/src/Presentation/SmartStore.Web.Framework/Controllers/PublicControllerBase.cs index fee48defb5..cbeedc9d66 100644 --- a/src/Presentation/SmartStore.Web.Framework/Controllers/PublicControllerBase.cs +++ b/src/Presentation/SmartStore.Web.Framework/Controllers/PublicControllerBase.cs @@ -1,20 +1,18 @@ -using SmartStore.Web.Framework.Controllers; -using SmartStore.Web.Framework.Filters; +using SmartStore.Web.Framework.Filters; using SmartStore.Web.Framework.Localization; -using SmartStore.Web.Framework.Security; using SmartStore.Web.Framework.Seo; namespace SmartStore.Web.Framework.Controllers { - [RewriteUrl(SslRequirement.Retain)] - [StoreClosed(Order = -1)] - [PublicStoreAllowNavigation(Order = -1)] - [LanguageSeoCode(Order = -1)] - [CustomerLastActivity(Order = 100)] - [StoreIpAddress(Order = 100)] - [StoreLastVisitedPage(Order = 100)] - [CheckAffiliate(Order = 100)] - public abstract partial class PublicControllerBase : SmartController + [StoreClosed] + [PublicStoreAllowNavigation] + [RewriteUrl(SslRequirement.Retain, Order = 0)] + [LanguageSeoCode(Order = 1)] + [CustomerLastActivity] + [StoreIpAddress] + [StoreLastVisitedPage] + [CheckAffiliate] + public abstract partial class PublicControllerBase : SmartController { - } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs b/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs index 3ca358f11d..cadbef89cf 100644 --- a/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs +++ b/src/Presentation/SmartStore.Web.Framework/Controllers/SmartController.cs @@ -10,237 +10,238 @@ namespace SmartStore.Web.Framework.Controllers { - [SetWorkingCulture(Order = -1)] - [JsonNet(Order = -1)] - [Notify(Order = 100)] - public abstract partial class SmartController : Controller - { - protected SmartController() - { - this.Logger = NullLogger.Instance; - this.T = NullLocalizer.Instance; - } - - public ILogger Logger - { - get; - set; - } - - public Localizer T - { - get; - set; - } - - public ICommonServices Services - { - get; - set; - } - - /// - /// Pushes an info message to the notification queue - /// - /// Message - /// A value indicating whether the message should be persisted for the next request - protected virtual void NotifyInfo(string message, bool durable = true) - { - Services.Notifier.Information(message, durable); - } - - /// - /// Pushes a warning message to the notification queue - /// - /// Message - /// A value indicating whether the message should be persisted for the next request - protected virtual void NotifyWarning(string message, bool durable = true) - { - Services.Notifier.Warning(message, durable); - } - - /// - /// Pushes a success message to the notification queue - /// - /// Message - /// A value indicating whether the message should be persisted for the next request - protected virtual void NotifySuccess(string message, bool durable = true) - { - Services.Notifier.Success(message, durable); - } - - /// - /// Pushes an error message to the notification queue - /// - /// Message - /// A value indicating whether the message should be persisted for the next request - protected virtual void NotifyError(string message, bool durable = true) - { - Services.Notifier.Error(message, durable); - } - - /// - /// Pushes an error message to the notification queue - /// - /// Exception - /// A value indicating whether a message should be persisted for the next request - /// A value indicating whether the exception should be logged - protected virtual void NotifyError(Exception exception, bool durable = true, bool logException = true) - { - if (logException) - { - LogException(exception); - } - - Services.Notifier.Error(HttpUtility.HtmlEncode(exception.ToAllMessages()), durable); - } - - /// - /// Pushes an error message to the notification queue that the access to a resource has been denied - /// - /// A value indicating whether a message should be persisted for the next request - /// A value indicating whether the message should be logged - protected virtual void NotifyAccessDenied(bool durable = true, bool log = true) - { - var message = T("Admin.AccessDenied.Description"); - - if (log) - { - Logger.Error(message); - } - - Services.Notifier.Error(message, durable); - } - - protected ActionResult RedirectToReferrer() - { - return RedirectToReferrer(null, () => RedirectToRoute("HomePage")); - } - - protected ActionResult RedirectToReferrer(string referrer) - { - return RedirectToReferrer(referrer, () => RedirectToRoute("HomePage")); - } - - protected ActionResult RedirectToReferrer(string referrer, string fallbackUrl) - { - // addressing "Open Redirection Vulnerability" (prevent cross-domain redirects / phishing) - if (fallbackUrl.HasValue() && !Url.IsLocalUrl(fallbackUrl)) - { - fallbackUrl = null; - } - - return RedirectToReferrer( - referrer, - fallbackUrl.HasValue() ? () => Redirect(fallbackUrl) : (Func)null); - } - - protected virtual ActionResult RedirectToReferrer(string referrer, Func fallbackResult) - { - bool skipLocalCheck = false; - - if (referrer.IsEmpty() && Request.UrlReferrer != null && Request.UrlReferrer.ToString().HasValue()) - { - referrer = Request.UrlReferrer.ToString(); - if (referrer.HasValue()) - { - var domain1 = (new Uri(referrer)).GetLeftPart(UriPartial.Authority); - var domain2 = this.Request.Url.GetLeftPart(UriPartial.Authority); - if (domain1.IsCaseInsensitiveEqual(domain2)) - { - // always allow fully qualified urls from local host - skipLocalCheck = true; - } - else - { - referrer = null; - } - } - } - - // addressing "Open Redirection Vulnerability" (prevent cross-domain redirects / phishing) - if (referrer.HasValue() && !skipLocalCheck && !Url.IsLocalUrl(referrer)) - { - referrer = null; - } - - if (referrer.HasValue()) - { - return Redirect(referrer); - } - - if (fallbackResult != null) - { - return fallbackResult(); - } - - return HttpNotFound(); - } - - /// - /// Redirects to the configuration page of a plugin or a provider. - /// - /// The system name of the plugin or the provider. - /// true plugin configuration, false provider configuration. - protected virtual ActionResult RedirectToConfiguration(string systemName, bool isPlugin = true) - { - Guard.NotEmpty(systemName, nameof(systemName)); - - var actionName = isPlugin ? "ConfigurePlugin" : "ConfigureProvider"; - - if (ControllerContext.IsChildAction) - { - var url = Url.Action(actionName, "Plugin", new { systemName, area = "Admin" }); - return new PermissiveRedirectResult(url); - } - - return RedirectToAction(actionName, "Plugin", new { systemName, area = "Admin" }); - } - - /// - /// On exception - /// - /// Filter context - protected override void OnException(ExceptionContext filterContext) - { - if (filterContext.Exception != null) - { - LogException(filterContext.Exception); - } - - base.OnException(filterContext); - } - - /// - /// Log exception - /// - /// Exception - private void LogException(Exception ex) - { - Logger.Error(ex); - } - - ///// - ///// Creates a object that serializes the specified object to JavaScript Object Notation (JSON) format using the content type, - ///// content encoding, and the JSON request behavior. - ///// - ///// The JavaScript object graph to serialize. - ///// The content type (MIME type). - ///// The content encoding. - ///// The JSON request behavior - ///// The result object that serializes the specified object to JSON format. - ///// - ///// This overridden method internally uses the Json.NET library for serialization. - ///// - //protected override JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior) - //{ - // return new JsonNetResult(Services.DateTimeHelper) - // { - // Data = data, - // ContentType = contentType, - // ContentEncoding = contentEncoding, - // JsonRequestBehavior = behavior - // }; - //} - } + /// + /// for must be -1, otherwise is not executed. + /// + [SetWorkingCulture(Order = 2)] + [JsonNet(Order = -1)] // Run first (OnActionExecuted) + [Notify(Order = 1000)] // Run last (OnResultExecuting) + public abstract partial class SmartController : Controller + { + protected SmartController() + { + } + + public ILogger Logger + { + get; + set; + } = NullLogger.Instance; + + public Localizer T + { + get; + set; + } = NullLocalizer.Instance; + + public ICommonServices Services + { + get; + set; + } + + /// + /// Pushes an info message to the notification queue + /// + /// Message + /// A value indicating whether the message should be persisted for the next request + protected virtual void NotifyInfo(string message, bool durable = true) + { + Services.Notifier.Information(message, durable); + } + + /// + /// Pushes a warning message to the notification queue + /// + /// Message + /// A value indicating whether the message should be persisted for the next request + protected virtual void NotifyWarning(string message, bool durable = true) + { + Services.Notifier.Warning(message, durable); + } + + /// + /// Pushes a success message to the notification queue + /// + /// Message + /// A value indicating whether the message should be persisted for the next request + protected virtual void NotifySuccess(string message, bool durable = true) + { + Services.Notifier.Success(message, durable); + } + + /// + /// Pushes an error message to the notification queue + /// + /// Message + /// A value indicating whether the message should be persisted for the next request + protected virtual void NotifyError(string message, bool durable = true) + { + Services.Notifier.Error(message, durable); + } + + /// + /// Pushes an error message to the notification queue + /// + /// Exception + /// A value indicating whether a message should be persisted for the next request + /// A value indicating whether the exception should be logged + protected virtual void NotifyError(Exception exception, bool durable = true, bool logException = true) + { + if (logException) + { + LogException(exception); + } + + Services.Notifier.Error(HttpUtility.HtmlEncode(exception.ToAllMessages()), durable); + } + + /// + /// Pushes an error message to the notification queue that the access to a resource has been denied + /// + /// A value indicating whether a message should be persisted for the next request + /// A value indicating whether the message should be logged + protected virtual void NotifyAccessDenied(bool durable = true, bool log = true) + { + var message = T("Admin.AccessDenied.Description"); + + if (log) + { + Logger.Error(message); + } + + Services.Notifier.Error(message, durable); + } + + protected ActionResult RedirectToReferrer() + { + return RedirectToReferrer(null, () => RedirectToRoute("HomePage")); + } + + protected ActionResult RedirectToReferrer(string referrer) + { + return RedirectToReferrer(referrer, () => RedirectToRoute("HomePage")); + } + + protected ActionResult RedirectToReferrer(string referrer, string fallbackUrl) + { + // addressing "Open Redirection Vulnerability" (prevent cross-domain redirects / phishing) + if (fallbackUrl.HasValue() && !Url.IsLocalUrl(fallbackUrl)) + { + fallbackUrl = null; + } + + return RedirectToReferrer( + referrer, + fallbackUrl.HasValue() ? () => Redirect(fallbackUrl) : (Func)null); + } + + protected virtual ActionResult RedirectToReferrer(string referrer, Func fallbackResult) + { + bool skipLocalCheck = false; + + if (referrer.IsEmpty() && Request.UrlReferrer != null && Request.UrlReferrer.ToString().HasValue()) + { + referrer = Request.UrlReferrer.ToString(); + if (referrer.HasValue()) + { + var domain1 = (new Uri(referrer)).GetLeftPart(UriPartial.Authority); + var domain2 = this.Request.Url.GetLeftPart(UriPartial.Authority); + if (domain1.IsCaseInsensitiveEqual(domain2)) + { + // always allow fully qualified urls from local host + skipLocalCheck = true; + } + else + { + referrer = null; + } + } + } + + // addressing "Open Redirection Vulnerability" (prevent cross-domain redirects / phishing) + if (referrer.HasValue() && !skipLocalCheck && !Url.IsLocalUrl(referrer)) + { + referrer = null; + } + + if (referrer.HasValue()) + { + return Redirect(referrer); + } + + if (fallbackResult != null) + { + return fallbackResult(); + } + + return HttpNotFound(); + } + + /// + /// Redirects to the configuration page of a plugin or a provider. + /// + /// The system name of the plugin or the provider. + /// true plugin configuration, false provider configuration. + protected virtual ActionResult RedirectToConfiguration(string systemName, bool isPlugin = true) + { + Guard.NotEmpty(systemName, nameof(systemName)); + + var actionName = isPlugin ? "ConfigurePlugin" : "ConfigureProvider"; + + if (ControllerContext.IsChildAction) + { + var url = Url.Action(actionName, "Plugin", new { systemName, area = "Admin" }); + return new PermissiveRedirectResult(url); + } + + return RedirectToAction(actionName, "Plugin", new { systemName, area = "Admin" }); + } + + /// + /// On exception + /// + /// Filter context + protected override void OnException(ExceptionContext filterContext) + { + if (filterContext.Exception != null) + { + LogException(filterContext.Exception); + } + + base.OnException(filterContext); + } + + /// + /// Log exception + /// + /// Exception + private void LogException(Exception ex) + { + Logger.Error(ex); + } + + ///// + ///// Creates a object that serializes the specified object to JavaScript Object Notation (JSON) format using the content type, + ///// content encoding, and the JSON request behavior. + ///// + ///// The JavaScript object graph to serialize. + ///// The content type (MIME type). + ///// The content encoding. + ///// The JSON request behavior + ///// The result object that serializes the specified object to JSON format. + ///// + ///// This overridden method internally uses the Json.NET library for serialization. + ///// + //protected override JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior) + //{ + // return new JsonNetResult(Services.DateTimeHelper) + // { + // Data = data, + // ContentType = contentType, + // ContentEncoding = contentEncoding, + // JsonRequestBehavior = behavior + // }; + //} + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Controllers/ViewNotFoundException.cs b/src/Presentation/SmartStore.Web.Framework/Controllers/ViewNotFoundException.cs new file mode 100644 index 0000000000..e7bfb0c817 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Controllers/ViewNotFoundException.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using SmartStore.Utilities.ObjectPools; + +namespace SmartStore.Web.Framework.Controllers +{ + public class ViewNotFoundException : Exception + { + public ViewNotFoundException(string viewName, IEnumerable searchedLocations) + : base(GenerateMessage(viewName, searchedLocations)) + { + Guard.NotEmpty(viewName, nameof(viewName)); + Guard.NotNull(searchedLocations, nameof(searchedLocations)); + + ViewName = viewName; + SearchedLocations = searchedLocations; + } + + private static string GenerateMessage(string viewName, IEnumerable searchedLocations) + { + var locations = PooledStringBuilder.Rent(); + locations.AppendLine(); + + foreach (string location in searchedLocations) + { + locations.AppendLine(location); + } + + return string.Format("The view '{0}' or its master was not found, searched locations: {1}", viewName, locations.ToStringAndReturn()); + } + + public string ViewName { get; private set; } + public IEnumerable SearchedLocations { get; private set; } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs index c4644ae68f..07e7cecf96 100644 --- a/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs +++ b/src/Presentation/SmartStore.Web.Framework/DependencyRegistrar.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Web; @@ -8,6 +8,7 @@ using Autofac; using Autofac.Builder; using Autofac.Core; +using Autofac.Core.Registration; using Autofac.Integration.Mvc; using Autofac.Integration.WebApi; using SmartStore.ComponentModel; @@ -29,6 +30,7 @@ using SmartStore.Core.Packaging; using SmartStore.Core.Plugins; using SmartStore.Core.Search; +using SmartStore.Core.Security; using SmartStore.Core.Themes; using SmartStore.Data; using SmartStore.Data.Caching; @@ -43,6 +45,7 @@ using SmartStore.Services.Catalog.Extensions; using SmartStore.Services.Catalog.Importer; using SmartStore.Services.Catalog.Modelling; +using SmartStore.Services.Catalog.Rules; using SmartStore.Services.Cms; using SmartStore.Services.Common; using SmartStore.Services.Configuration; @@ -67,6 +70,7 @@ using SmartStore.Services.Payments; using SmartStore.Services.Pdf; using SmartStore.Services.Polls; +using SmartStore.Services.Rules; using SmartStore.Services.Search; using SmartStore.Services.Search.Extensions; using SmartStore.Services.Search.Modelling; @@ -91,502 +95,519 @@ using SmartStore.Web.Framework.Theming; using SmartStore.Web.Framework.Theming.Assets; using SmartStore.Web.Framework.UI; -using SmartStore.Web.Framework.WebApi; using SmartStore.Web.Framework.WebApi.Configuration; +using SmartStore.Web.Framework.WebApi.OData; using Module = Autofac.Module; namespace SmartStore.Web.Framework { public class DependencyRegistrar : IDependencyRegistrar { - public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, bool isActiveModule) + public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, bool isActiveModule) { - // plugins - var pluginFinder = PluginFinder.Current; - builder.RegisterInstance(pluginFinder); - builder.RegisterType(); - - // modules - builder.RegisterModule(new CoreModule(typeFinder)); - builder.RegisterModule(new DbModule(typeFinder)); - builder.RegisterModule(new CachingModule()); - builder.RegisterModule(new SearchModule()); - builder.RegisterModule(new LocalizationModule()); - builder.RegisterModule(new MessagingModule()); - builder.RegisterModule(new WebModule(typeFinder)); - builder.RegisterModule(new WebApiModule(typeFinder)); - builder.RegisterModule(new UiModule(typeFinder)); - builder.RegisterModule(new IOModule()); - builder.RegisterModule(new PackagingModule()); - builder.RegisterModule(new ProvidersModule(typeFinder, pluginFinder)); + // plugins + var pluginFinder = PluginFinder.Current; + builder.RegisterInstance(pluginFinder); + builder.RegisterType(); + + // modules + builder.RegisterModule(new CoreModule(typeFinder)); + builder.RegisterModule(new MediaModule(typeFinder)); + builder.RegisterModule(new DbModule(typeFinder)); + builder.RegisterModule(new CachingModule()); + builder.RegisterModule(new SearchModule()); + builder.RegisterModule(new LocalizationModule()); + builder.RegisterModule(new MessagingModule()); + builder.RegisterModule(new WebModule(typeFinder)); + builder.RegisterModule(new WebApiModule(typeFinder)); + builder.RegisterModule(new UiModule(typeFinder)); + builder.RegisterModule(new IOModule()); + builder.RegisterModule(new PackagingModule()); + builder.RegisterModule(new ProvidersModule(typeFinder, pluginFinder)); builder.RegisterModule(new TasksModule(typeFinder)); - builder.RegisterModule(new DataExchangeModule(typeFinder)); - builder.RegisterModule(new EventModule(typeFinder, pluginFinder)); + builder.RegisterModule(new DataExchangeModule(typeFinder)); + builder.RegisterModule(new EventModule(typeFinder, pluginFinder)); builder.RegisterModule(new RuleModule(typeFinder)); } - public int Order + public int Order => -100; + } + + #region Modules + + internal class CoreModule : Module + { + private readonly ITypeFinder _typeFinder; + + public CoreModule(ITypeFinder typeFinder) { - get { return -100; } + _typeFinder = typeFinder; } - } - #region Modules - - internal class CoreModule : Module - { - private readonly ITypeFinder _typeFinder; - - public CoreModule(ITypeFinder typeFinder) - { - _typeFinder = typeFinder; - } - - protected override void Load(ContainerBuilder builder) - { - builder.RegisterType().As().SingleInstance(); - - // sources - builder.RegisterGeneric(typeof(WorkValues<>)).InstancePerRequest(); - builder.RegisterSource(new SettingsSource()); - builder.RegisterSource(new WorkSource()); - - // Logging stuff - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - - // web helper - builder.RegisterType().As().InstancePerRequest(); + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().As().SingleInstance(); - // work context - builder.RegisterType().As().InstancePerRequest(); + // sources + builder.RegisterGeneric(typeof(WorkValues<>)).InstancePerRequest(); + builder.RegisterSource(new SettingsSource()); + builder.RegisterSource(new WorkSource()); - // store context - builder.RegisterType().As().InstancePerRequest(); + // Logging stuff + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); - // services - builder.RegisterType().As().InstancePerRequest(); + // web helper + builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As() - .WithNullCache() - .InstancePerRequest(); + // work context + builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + // store context + builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerLifetimeScope(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().InstancePerRequest(); + // services + builder.RegisterType().As().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As() + .As() + .WithNullCache() + .InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().As().InstancePerRequest(); + builder.RegisterType().As().As().InstancePerRequest(); + builder.RegisterType().As().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + } - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + protected override void AttachToComponentRegistration(IComponentRegistryBuilder componentRegistry, IComponentRegistration registration) + { + // Look for first settable property of type "ICommonServices" and inject + var servicesProperty = FindCommonServicesProperty(registration.Activator.LimitType); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - - builder.RegisterType().As().InstancePerRequest(); - - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - - builder.RegisterType().As().InstancePerRequest(); - - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - - builder.RegisterType().As().InstancePerRequest(); - - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + if (servicesProperty == null) + return; - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + registration.Metadata.Add("Property.ICommonServices", FastProperty.Create(servicesProperty)); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + registration.Activated += (sender, e) => + { + if (DataSettings.DatabaseIsInstalled()) + { + var prop = e.Component.Metadata.Get("Property.ICommonServices") as FastProperty; + var services = e.Context.Resolve(); + prop.SetValue(e.Instance, services); + } + }; + } - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - - builder.RegisterType().As().InstancePerRequest(); - } - - protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) - { - // Look for first settable property of type "ICommonServices" and inject - var servicesProperty = FindCommonServicesProperty(registration.Activator.LimitType); - - if (servicesProperty == null) - return; - - registration.Metadata.Add("Property.ICommonServices", new FastProperty(servicesProperty)); - - registration.Activated += (sender, e) => - { - if (DataSettings.DatabaseIsInstalled()) - { - var prop = e.Component.Metadata.Get("Property.ICommonServices") as FastProperty; - var services = e.Context.Resolve(); - prop.SetValue(e.Instance, services); - } - }; - } - - private static PropertyInfo FindCommonServicesProperty(Type type) - { - var prop = type - .GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance) - .Select(p => new - { - PropertyInfo = p, - p.PropertyType, - IndexParameters = p.GetIndexParameters(), - Accessors = p.GetAccessors(false) - }) - .Where(x => x.PropertyType == typeof(ICommonServices)) // must be ICommonServices - .Where(x => x.IndexParameters.Count() == 0) // must not be an indexer - .Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)) //must have get/set, or only set - .Select(x => x.PropertyInfo) - .FirstOrDefault(); - - return prop; - } - - private IEnumerable> BuildLoggerInjectors(Type componentType) - { - // Look for first settable property of type "ICommonServices" - var loggerProperties = componentType - .GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance) - .Select(p => new - { - PropertyInfo = p, - p.PropertyType, - IndexParameters = p.GetIndexParameters(), - Accessors = p.GetAccessors(false) - }) - .Where(x => x.PropertyType == typeof(ILogger)) // must be a logger - .Where(x => x.IndexParameters.Count() == 0) // must not be an indexer - .Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)) //must have get/set, or only set - .Select(x => new FastProperty(x.PropertyInfo)); - - // Return an array of actions that resolve a logger and assign the property - foreach (var prop in loggerProperties) - { - yield return (ctx, instance) => - { - string component = componentType.ToString(); - var logger = ctx.Resolve(); - prop.SetValue(instance, logger); - }; - } - } - } + private static PropertyInfo FindCommonServicesProperty(Type type) + { + var prop = type + .GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance) + .Select(p => new + { + PropertyInfo = p, + p.PropertyType, + IndexParameters = p.GetIndexParameters(), + Accessors = p.GetAccessors(false) + }) + .Where(x => x.PropertyType == typeof(ICommonServices)) // must be ICommonServices + .Where(x => x.IndexParameters.Count() == 0) // must not be an indexer + .Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)) //must have get/set, or only set + .Select(x => x.PropertyInfo) + .FirstOrDefault(); + + return prop; + } + + private IEnumerable> BuildLoggerInjectors(Type componentType) + { + // Look for first settable property of type "ICommonServices" + var loggerProperties = componentType + .GetProperties(BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance) + .Select(p => new + { + PropertyInfo = p, + p.PropertyType, + IndexParameters = p.GetIndexParameters(), + Accessors = p.GetAccessors(false) + }) + .Where(x => x.PropertyType == typeof(ILogger)) // must be a logger + .Where(x => x.IndexParameters.Count() == 0) // must not be an indexer + .Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)) //must have get/set, or only set + .Select(x => FastProperty.Create(x.PropertyInfo)); + + // Return an array of actions that resolve a logger and assign the property + foreach (var prop in loggerProperties) + { + yield return (ctx, instance) => + { + string component = componentType.ToString(); + var logger = ctx.Resolve(); + prop.SetValue(instance, logger); + }; + } + } + } internal class DbModule : Module - { - private readonly ITypeFinder _typeFinder; - - public DbModule(ITypeFinder typeFinder) - { - _typeFinder = typeFinder; - } - - protected override void Load(ContainerBuilder builder) - { - builder.Register(c => DataSettings.Current).As().InstancePerDependency(); - builder.Register(x => new EfDataProviderFactory(x.Resolve())).As().InstancePerDependency(); - - builder.Register(x => x.Resolve().LoadDataProvider()).As().InstancePerDependency(); - builder.Register(x => (IEfDataProvider)x.Resolve().LoadDataProvider()).As().InstancePerDependency(); - - builder.RegisterType().As().InstancePerRequest(); - - builder.RegisterType().As().SingleInstance(); - - if (DataSettings.DatabaseIsInstalled()) - { - // register DB Hooks (only when app was installed properly) - - Type findHookedType(Type t) - { - var x = t; - while (x != null) - { - if (x.IsGenericType) - { - return x.GetGenericArguments()[0]; - } - x = x.BaseType; - } - - return typeof(BaseEntity); - } - - var hooks = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true); - foreach (var hook in hooks) - { - var hookedType = findHookedType(hook); - - var registration = builder.RegisterType(hook) - .As() - .InstancePerRequest() - .WithMetadata(m => - { - m.For(em => em.HookedType, hookedType); - m.For(em => em.ImplType, hook); - m.For(em => em.IsLoadHook, false); - m.For(em => em.Important, hook.HasAttribute(false)); - }); - } - - builder.Register(c => new SmartObjectContext(DataSettings.Current.DataConnectionString)) - //.PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies) - .PropertiesAutowired(new DbContextPropertySelector(), false) - .InstancePerRequest(); - } - else - { - builder.Register(c => - { - try - { - return new SmartObjectContext(DataSettings.Current.DataConnectionString); - } - catch - { - // return new SmartObjectContext(); - return null; - } - - }) - .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies) - .InstancePerRequest(); - } - - builder.Register>(c => - { - var cc = c.Resolve(); - return named => cc.ResolveNamed(named); - }); - - builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>)).InstancePerRequest(); - - builder.Register(c => - { - var storeService = c.Resolve(); - var aclService = c.Resolve(); - - return new DbQuerySettings(!aclService.HasActiveAcl, storeService.IsSingleStoreMode()); - }) - .InstancePerRequest(); - } - - protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) - { - var querySettingsProperty = FindQuerySettingsProperty(registration.Activator.LimitType); - - if (querySettingsProperty == null) - return; - - registration.Metadata.Add("Property.DbQuerySettings", new FastProperty(querySettingsProperty)); - - registration.Activated += (sender, e) => - { - if (DataSettings.DatabaseIsInstalled()) - { - if (e.Component.Metadata.Get("Property.DbQuerySettings") is FastProperty prop) - { - var querySettings = e.Context.Resolve(); - prop.SetValue(e.Instance, querySettings); - } - } - }; - } - - private static PropertyInfo FindQuerySettingsProperty(Type type) - { - return type.GetProperty("QuerySettings", typeof(DbQuerySettings)); - } - - private class DbContextPropertySelector : IPropertySelector - { - public bool InjectProperty(PropertyInfo propertyInfo, object instance) - { - // Prevent Autofac circularity exception & never trigger hooks during tooling or tests - return HostingEnvironment.IsHosted && typeof(IDbHookHandler).IsAssignableFrom(propertyInfo.PropertyType); - } - } - } + { + private readonly ITypeFinder _typeFinder; + + public DbModule(ITypeFinder typeFinder) + { + _typeFinder = typeFinder; + } + + private static (Type ContextType, Type EntityType) DiscoverHookTypes(Type type) + { + var x = type.BaseType; + while (x != null && x != typeof(object)) + { + if (x.IsGenericType) + { + var gtd = x.GetGenericTypeDefinition(); + if (gtd == typeof(DbSaveHook<>)) + { + return (typeof(SmartObjectContext), x.GetGenericArguments()[0]); + } + if (gtd == typeof(DbSaveHook<,>)) + { + var args = x.GetGenericArguments(); + return (args[0], args[1]); + } + } + + x = x.BaseType; + } + + foreach (var intface in type.GetInterfaces()) + { + if (intface.IsGenericType) + { + var gtd = intface.GetGenericTypeDefinition(); + if (gtd == typeof(IDbSaveHook<>)) + { + return (intface.GetGenericArguments()[0], typeof(BaseEntity)); + } + } + } + + return (typeof(SmartObjectContext), typeof(BaseEntity)); + } + + protected override void Load(ContainerBuilder builder) + { + builder.Register(c => DataSettings.Current).As().InstancePerDependency(); + builder.Register(x => new EfDataProviderFactory(x.Resolve())).As().InstancePerDependency(); + + builder.Register(x => x.Resolve().LoadDataProvider()).As().InstancePerDependency(); + builder.Register(x => (IEfDataProvider)x.Resolve().LoadDataProvider()).As().InstancePerDependency(); + + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().SingleInstance(); + + if (DataSettings.DatabaseIsInstalled()) + { + // Register DB Hooks (only when app was installed properly) + + var hookTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true); + foreach (var hookType in hookTypes) + { + var types = DiscoverHookTypes(hookType); + + var registration = builder.RegisterType(hookType) + .As() + .InstancePerRequest() + .WithMetadata(m => + { + m.For(em => em.HookedType, types.EntityType); + m.For(em => em.ImplType, hookType); + m.For(em => em.DbContextType, types.ContextType ?? typeof(SmartObjectContext)); + m.For(em => em.Important, hookType.HasAttribute(false)); + }); + } + + builder.Register(c => new SmartObjectContext(DataSettings.Current.DataConnectionString)) + //.PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies) + .PropertiesAutowired(new DbContextPropertySelector(), false) + .InstancePerRequest(); + } + else + { + builder.Register(c => + { + try + { + return new SmartObjectContext(DataSettings.Current.DataConnectionString); + } + catch + { + // return new SmartObjectContext(); + return null; + } + + }) + .PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies) + .InstancePerRequest(); + } + + builder.Register>(c => + { + var cc = c.Resolve(); + return named => cc.ResolveNamed(named); + }); + + builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>)).InstancePerRequest(); + + builder.Register(c => + { + var storeService = c.Resolve(); + var aclService = c.Resolve(); + + return new DbQuerySettings(!aclService.HasActiveAcl, storeService.IsSingleStoreMode()); + }) + .InstancePerRequest(); + } + + protected override void AttachToComponentRegistration(IComponentRegistryBuilder componentRegistry, IComponentRegistration registration) + { + var querySettingsProperty = FindQuerySettingsProperty(registration.Activator.LimitType); + + if (querySettingsProperty == null) + return; + + registration.Metadata.Add("Property.DbQuerySettings", FastProperty.Create(querySettingsProperty)); + + registration.Activated += (sender, e) => + { + if (DataSettings.DatabaseIsInstalled()) + { + if (e.Component.Metadata.Get("Property.DbQuerySettings") is FastProperty prop) + { + var querySettings = e.Context.Resolve(); + prop.SetValue(e.Instance, querySettings); + } + } + }; + } + + private static PropertyInfo FindQuerySettingsProperty(Type type) + { + return type.GetProperty("QuerySettings", typeof(DbQuerySettings)); + } + + private class DbContextPropertySelector : IPropertySelector + { + public bool InjectProperty(PropertyInfo propertyInfo, object instance) + { + // Prevent Autofac circularity exception & never trigger hooks during tooling or tests + return HostingEnvironment.IsHosted && typeof(IDbHookHandler).IsAssignableFrom(propertyInfo.PropertyType); + } + } + } internal class LocalizationModule : Module - { - protected override void Load(ContainerBuilder builder) - { - builder.RegisterType().As().InstancePerRequest(); - - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - - builder.RegisterType().As().InstancePerRequest(); - builder.Register(c => c.Resolve().Get).InstancePerRequest(); - builder.Register(c => c.Resolve().GetEx).InstancePerRequest(); - - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - } - - protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) - { - var userProperty = FindUserProperty(registration.Activator.LimitType); - - if (userProperty == null) - return; - - registration.Metadata.Add("Property.T", new FastProperty(userProperty)); - - registration.Activated += (sender, e) => - { - if (DataSettings.DatabaseIsInstalled() && e.Context.Resolve().IsFullyInitialized) - { - if (e.Component.Metadata.Get("Property.T") is FastProperty prop) - { - try - { - var iText = e.Context.Resolve(); - if (prop.Property.PropertyType == typeof(Localizer)) - { - Localizer localizer = e.Context.Resolve().Get; - prop.SetValue(e.Instance, localizer); - } - else - { - LocalizerEx localizerEx = e.Context.Resolve().GetEx; - prop.SetValue(e.Instance, localizerEx); - } - } - catch { } - } - } - }; - } - - private static PropertyInfo FindUserProperty(Type type) - { - return type.GetProperty("T", typeof(Localizer)) ?? type.GetProperty("T", typeof(LocalizerEx)); - } - } + { + protected override void Load(ContainerBuilder builder) + { + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.Register(c => c.Resolve().Get).InstancePerRequest(); + builder.Register(c => c.Resolve().GetEx).InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().InstancePerRequest(); + } + + protected override void AttachToComponentRegistration(IComponentRegistryBuilder componentRegistry, IComponentRegistration registration) + { + var userProperty = FindUserProperty(registration.Activator.LimitType); + + if (userProperty == null) + return; + + registration.Metadata.Add("Property.T", FastProperty.Create(userProperty)); + + registration.Activated += (sender, e) => + { + if (DataSettings.DatabaseIsInstalled() && e.Context.Resolve().IsFullyInitialized) + { + if (e.Component.Metadata.Get("Property.T") is FastProperty prop) + { + try + { + var iText = e.Context.Resolve(); + if (prop.Property.PropertyType == typeof(Localizer)) + { + Localizer localizer = e.Context.Resolve().Get; + prop.SetValue(e.Instance, localizer); + } + else + { + LocalizerEx localizerEx = e.Context.Resolve().GetEx; + prop.SetValue(e.Instance, localizerEx); + } + } + catch { } + } + } + }; + } + + private static PropertyInfo FindUserProperty(Type type) + { + return type.GetProperty("T", typeof(Localizer)) ?? type.GetProperty("T", typeof(LocalizerEx)); + } + } internal class CachingModule : Module - { - protected override void Load(ContainerBuilder builder) - { - // Output cache - builder.RegisterType().As().InstancePerRequest(); - builder.Register(c => NullOutputCacheInvalidationObserver.Instance).SingleInstance(); - builder.RegisterType().As().InstancePerRequest(); - - // Request cache - builder.RegisterType().As().InstancePerRequest(); - - // Model/Business cache (application scoped) - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().Named("null").SingleInstance(); - - // Register MemoryCacheManager twice, this time explicitly named. - // We may need this later in decorator classes as a kind of fallback. - builder.RegisterType().Named("memory").SingleInstance(); - - // Asset cache - if (DataSettings.DatabaseIsInstalled()) - { - builder.RegisterType().As().InstancePerRequest(); - } - else - { - builder.Register(c => DefaultAssetCache.Null).SingleInstance(); - } - } - } + { + protected override void Load(ContainerBuilder builder) + { + // Output cache + builder.RegisterType().As().InstancePerRequest(); + builder.Register(c => NullOutputCacheInvalidationObserver.Instance).SingleInstance(); + builder.RegisterType().As().InstancePerRequest(); + + // Request cache + builder.RegisterType().As().InstancePerRequest(); + + // Model/Business cache (application scoped) + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().Named("null").SingleInstance(); + + // Register MemoryCacheManager twice, this time explicitly named. + // We may need this later in decorator classes as a kind of fallback. + builder.RegisterType().Named("memory").SingleInstance(); + + // Asset cache + if (DataSettings.DatabaseIsInstalled()) + { + builder.RegisterType().As().InstancePerRequest(); + } + else + { + builder.Register(c => DefaultAssetCache.Null).SingleInstance(); + } + } + } internal class SearchModule : Module - { - protected override void Load(ContainerBuilder builder) - { + { + protected override void Load(ContainerBuilder builder) + { // General. - builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); // Catalog. - builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().As().InstancePerRequest(); builder.RegisterType().Named("linq").InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); @@ -597,231 +618,239 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().As().InstancePerRequest(); builder.RegisterType().As().InstancePerRequest(); } - } + } internal class EventModule : Module - { - private readonly ITypeFinder _typeFinder; - private readonly IPluginFinder _pluginFinder; - - public EventModule(ITypeFinder typeFinder, IPluginFinder pluginFinder) - { - _typeFinder = typeFinder; - _pluginFinder = pluginFinder; - } - - protected override void Load(ContainerBuilder builder) - { - builder.RegisterType().As().SingleInstance(); - - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - - var consumerTypes = _typeFinder.FindClassesOfType(typeof(IConsumer)); - foreach (var type in consumerTypes) - { - var registration = builder - .RegisterType(type) - .As() - .Keyed(type) - .InstancePerRequest(); - - var pluginDescriptor = _pluginFinder.GetPluginDescriptorByAssembly(type.Assembly); - var isActive = PluginManager.IsActivePluginAssembly(type.Assembly); - - registration.WithMetadata(m => { - m.For(em => em.IsActive, isActive); - m.For(em => em.ContainerType, type); - m.For(em => em.PluginDescriptor, pluginDescriptor); - }); - } - } - } + { + private readonly ITypeFinder _typeFinder; + private readonly IPluginFinder _pluginFinder; + + public EventModule(ITypeFinder typeFinder, IPluginFinder pluginFinder) + { + _typeFinder = typeFinder; + _pluginFinder = pluginFinder; + } + + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().As().SingleInstance(); + + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + + var consumerTypes = _typeFinder.FindClassesOfType(typeof(IConsumer)); + foreach (var type in consumerTypes) + { + var registration = builder + .RegisterType(type) + .As() + .Keyed(type) + .InstancePerRequest(); + + var pluginDescriptor = _pluginFinder.GetPluginDescriptorByAssembly(type.Assembly); + var isActive = PluginManager.IsActivePluginAssembly(type.Assembly); + + registration.WithMetadata(m => + { + m.For(em => em.IsActive, isActive); + m.For(em => em.ContainerType, type); + m.For(em => em.PluginDescriptor, pluginDescriptor); + }); + } + } + } internal class MessagingModule : Module - { - protected override void Load(ContainerBuilder builder) - { - // Templating - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().SingleInstance(); - } - } + { + protected override void Load(ContainerBuilder builder) + { + // Templating + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().SingleInstance(); + } + } internal class WebModule : Module - { - private readonly ITypeFinder _typeFinder; - - public WebModule(ITypeFinder typeFinder) - { - _typeFinder = typeFinder; - } - - protected override void Load(ContainerBuilder builder) - { - var foundAssemblies = _typeFinder.GetAssemblies(ignoreInactivePlugins: true).ToArray(); - - builder.RegisterModule(new AutofacWebTypesModule()); - builder.Register(HttpContextBaseFactory).As(); - - // register all controllers - builder.RegisterControllers(foundAssemblies); - - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().InstancePerRequest(); - - // Filter provider - builder.RegisterFilterProvider(); - - // Model binding - builder.RegisterModelBinders(foundAssemblies); - builder.RegisterModelBinderProvider(); - - var pageHelperRegistration = builder.RegisterType().InstancePerRequest(); - - // global exception handling - if (DataSettings.DatabaseIsInstalled()) - { - pageHelperRegistration.PropertiesAutowired(PropertyWiringOptions.None); - builder.RegisterType().AsActionFilterFor(-100); - builder.RegisterType().AsActionFilterFor().InstancePerRequest(); - } - } - - static HttpContextBase HttpContextBaseFactory(IComponentContext ctx) - { - if (IsRequestValid()) - { - return new HttpContextWrapper(HttpContext.Current); - } - - // register FakeHttpContext when HttpContext is not available - return new FakeHttpContext("~/"); - } - - static bool IsRequestValid() - { - if (HttpContext.Current == null) - { - return false; - } - - return HttpContext.Current.SafeGetHttpRequest() != null; - } - } + { + private readonly ITypeFinder _typeFinder; + + public WebModule(ITypeFinder typeFinder) + { + _typeFinder = typeFinder; + } + + protected override void Load(ContainerBuilder builder) + { + var foundAssemblies = _typeFinder.GetAssemblies(ignoreInactivePlugins: true).ToArray(); + + builder.RegisterModule(new AutofacWebTypesModule()); + builder.Register(HttpContextBaseFactory).As(); + + // register all controllers + builder.RegisterControllers(foundAssemblies); + + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().InstancePerRequest(); + + // Filter provider + builder.RegisterFilterProvider(); + + // Model binding + builder.RegisterModelBinders(foundAssemblies); + builder.RegisterModelBinderProvider(); + + var pageHelperRegistration = builder.RegisterType().InstancePerRequest(); + + // Global exception handling + if (DataSettings.DatabaseIsInstalled()) + { + pageHelperRegistration.PropertiesAutowired(PropertyWiringOptions.None); + + builder.RegisterType() + .AsExceptionFilterFor(-100) + .AsActionFilterFor(int.MaxValue) + .InstancePerRequest(); + + builder.RegisterType() + .AsActionFilterFor() + .InstancePerRequest(); + } + } + + static HttpContextBase HttpContextBaseFactory(IComponentContext ctx) + { + if (IsRequestValid()) + { + return new HttpContextWrapper(HttpContext.Current); + } + + // register FakeHttpContext when HttpContext is not available + return new FakeHttpContext("~/"); + } + + static bool IsRequestValid() + { + if (HttpContext.Current == null) + { + return false; + } + + return HttpContext.Current.SafeGetHttpRequest() != null; + } + } internal class WebApiModule : Module - { - private readonly ITypeFinder _typeFinder; - - public WebApiModule(ITypeFinder typeFinder) - { - _typeFinder = typeFinder; - } - - protected override void Load(ContainerBuilder builder) - { - var foundAssemblies = _typeFinder.GetAssemblies(ignoreInactivePlugins: true).ToArray(); - - // register all api controllers - builder.RegisterApiControllers(foundAssemblies); - - builder.RegisterType().As(); - } - - protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) - { - if (!DataSettings.DatabaseIsInstalled()) - return; - - var baseType = typeof(WebApiEntityController<,>); - var type = registration.Activator.LimitType; - - if (!type.IsSubClass(baseType, out var implementingType)) - return; - - var repoProperty = FindRepositoryProperty(type, implementingType.GetGenericArguments()[0]); - var serviceProperty = FindServiceProperty(type, implementingType.GetGenericArguments()[1]); - - if (repoProperty != null || serviceProperty != null) - { - registration.Activated += (sender, e) => - { - if (repoProperty != null) - { - var repo = e.Context.Resolve(repoProperty.PropertyType); - repoProperty.SetValue(e.Instance, repo, null); - } - - if (serviceProperty != null) - { - var service = e.Context.Resolve(serviceProperty.PropertyType); - serviceProperty.SetValue(e.Instance, service, null); - } - }; - } - } - - private static PropertyInfo FindRepositoryProperty(Type type, Type entityType) - { - var pi = type.GetProperty("Repository", typeof(IRepository<>).MakeGenericType(entityType)); - return pi; - } - - private static PropertyInfo FindServiceProperty(Type type, Type serviceType) - { - var pi = type.GetProperty("Service", serviceType); - return pi; - } - - } + { + private readonly ITypeFinder _typeFinder; + + public WebApiModule(ITypeFinder typeFinder) + { + _typeFinder = typeFinder; + } + + protected override void Load(ContainerBuilder builder) + { + var foundAssemblies = _typeFinder.GetAssemblies(ignoreInactivePlugins: true).ToArray(); + + // register all api controllers + builder.RegisterApiControllers(foundAssemblies); + + builder.RegisterType().As(); + } + + protected override void AttachToComponentRegistration(IComponentRegistryBuilder componentRegistry, IComponentRegistration registration) + { + if (!DataSettings.DatabaseIsInstalled()) + return; + + var baseType = typeof(WebApiEntityController<,>); + var type = registration.Activator.LimitType; + + if (!type.IsSubClass(baseType, out var implementingType)) + return; + + var repoProperty = FindRepositoryProperty(type, implementingType.GetGenericArguments()[0]); + var serviceProperty = FindServiceProperty(type, implementingType.GetGenericArguments()[1]); + + if (repoProperty != null || serviceProperty != null) + { + registration.Activated += (sender, e) => + { + if (repoProperty != null) + { + var repo = e.Context.Resolve(repoProperty.PropertyType); + repoProperty.SetValue(e.Instance, repo, null); + } + + if (serviceProperty != null) + { + var service = e.Context.Resolve(serviceProperty.PropertyType); + serviceProperty.SetValue(e.Instance, service, null); + } + }; + } + } + + private static PropertyInfo FindRepositoryProperty(Type type, Type entityType) + { + var pi = type.GetProperty("Repository", typeof(IRepository<>).MakeGenericType(entityType)); + return pi; + } + + private static PropertyInfo FindServiceProperty(Type type, Type serviceType) + { + var pi = type.GetProperty("Service", serviceType); + return pi; + } + + } internal class UiModule : Module - { - private readonly ITypeFinder _typeFinder; + { + private readonly ITypeFinder _typeFinder; public UiModule(ITypeFinder typeFinder) - { - _typeFinder = typeFinder; + { + _typeFinder = typeFinder; } - protected override void Load(ContainerBuilder builder) - { - // register theming services - builder.Register(x => new DefaultThemeRegistry(x.Resolve(), x.Resolve(), null, null, true)).As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); + protected override void Load(ContainerBuilder builder) + { + // register theming services + builder.Register(x => new DefaultThemeRegistry(x.Resolve(), x.Resolve(), null, null, true)).As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); - // register UI component renderers - builder.RegisterType().As>(); - builder.RegisterType().As>(); - builder.RegisterType().As>(); + // register UI component renderers + builder.RegisterType().As>(); + builder.RegisterType().As>(); + builder.RegisterType().As>(); builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().SingleInstance(); // Menus builder.RegisterType().As().InstancePerRequest(); - + var menuResolverTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true); foreach (var type in menuResolverTypes) { @@ -850,266 +879,261 @@ protected override void Load(ContainerBuilder builder) if (DataSettings.DatabaseIsInstalled()) { - builder.RegisterType().AsResultFilterFor(0); + // We have to register two classes, otherwise the filters would be called twice. + builder.RegisterType().AsActionFilterFor(0); + builder.RegisterType().AsResultFilterFor(0); } } - } + } internal class IOModule : Module - { - protected override void Load(ContainerBuilder builder) - { - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - - // Register IFileSystem twice, this time explicitly named. - // We may need this later in decorator classes as a kind of fallback. - builder.RegisterType().Named("local").SingleInstance(); - builder.RegisterType().Named("local").SingleInstance(); - - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - } - } + { + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + + // Register IFileSystem twice, this time explicitly named. + // We may need this later in decorator classes as a kind of fallback. + builder.RegisterType().Named("local").SingleInstance(); + builder.RegisterType().Named("local").SingleInstance(); + + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + } + } internal class PackagingModule : Module - { - protected override void Load(ContainerBuilder builder) - { - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - } - } + { + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + } + } internal class ProvidersModule : Module - { - private readonly ITypeFinder _typeFinder; - private readonly IPluginFinder _pluginFinder; - - public ProvidersModule(ITypeFinder typeFinder, IPluginFinder pluginFinder) - { - _typeFinder = typeFinder; - _pluginFinder = pluginFinder; - } - - protected override void Load(ContainerBuilder builder) - { - builder.RegisterType().As().InstancePerRequest(); - - if (!DataSettings.DatabaseIsInstalled()) - return; - - var providerTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true).ToList(); - - foreach (var type in providerTypes) - { - var pluginDescriptor = _pluginFinder.GetPluginDescriptorByAssembly(type.Assembly); - var groupName = ProviderTypeToKnownGroupName(type); - var systemName = GetSystemName(type, pluginDescriptor); - var friendlyName = GetFriendlyName(type, pluginDescriptor); - var displayOrder = GetDisplayOrder(type, pluginDescriptor); - var dependentWidgets = GetDependentWidgets(type); - var resPattern = (pluginDescriptor != null ? "Plugins" : "Providers") + ".{1}.{0}"; // e.g. Plugins.FriendlyName.MySystemName - var settingPattern = (pluginDescriptor != null ? "Plugins" : "Providers") + ".{0}.{1}"; // e.g. Plugins.MySystemName.DisplayOrder - var isConfigurable = typeof(IConfigurable).IsAssignableFrom(type); - var isEditable = typeof(IUserEditable).IsAssignableFrom(type); - var isHidden = GetIsHidden(type); - var exportFeature = GetExportFeature(type); - - var registration = builder.RegisterType(type).Named(systemName).InstancePerRequest().PropertiesAutowired(PropertyWiringOptions.None); - registration.WithMetadata(m => - { - m.For(em => em.PluginDescriptor, pluginDescriptor); - m.For(em => em.GroupName, groupName); - m.For(em => em.SystemName, systemName); - m.For(em => em.ResourceKeyPattern, resPattern); - m.For(em => em.SettingKeyPattern, settingPattern); - m.For(em => em.FriendlyName, friendlyName.Item1); - m.For(em => em.Description, friendlyName.Item2); - m.For(em => em.DisplayOrder, displayOrder); - m.For(em => em.DependentWidgets, dependentWidgets); - m.For(em => em.IsConfigurable, isConfigurable); - m.For(em => em.IsEditable, isEditable); - m.For(em => em.IsHidden, isHidden); - m.For(em => em.ExportFeatures, exportFeature); - }); - - // register specific provider type - RegisterAsSpecificProvider(type, systemName, registration); - RegisterAsSpecificProvider(type, systemName, registration); - RegisterAsSpecificProvider(type, systemName, registration); - RegisterAsSpecificProvider(type, systemName, registration); - RegisterAsSpecificProvider(type, systemName, registration); - RegisterAsSpecificProvider(type, systemName, registration); - RegisterAsSpecificProvider(type, systemName, registration); - RegisterAsSpecificProvider(type, systemName, registration); - RegisterAsSpecificProvider(type, systemName, registration); - RegisterAsSpecificProvider(type, systemName, registration); - } - - } - - #region Helpers - - private void RegisterAsSpecificProvider(Type implType, string systemName, IRegistrationBuilder registration) where T : IProvider - { - if (typeof(T).IsAssignableFrom(implType)) - { - try - { - registration.As().Named(systemName); - registration.WithMetadata(m => - { - m.For(em => em.ProviderType, typeof(T)); - }); - } - catch (Exception) { } - } - } - - private string GetSystemName(Type type, PluginDescriptor descriptor) - { - var attr = type.GetAttribute(false); - if (attr != null) - { - return attr.Name; - } - - if (typeof(IPlugin).IsAssignableFrom(type) && descriptor != null) - { - return descriptor.SystemName; - } - - return type.FullName; - //throw Error.Application("The 'SystemNameAttribute' must be applied to a provider type if the provider does not implement 'IPlugin' (provider type: {0}, plugin: {1})".FormatInvariant(type.FullName, descriptor != null ? descriptor.SystemName : "-")); - } - - private int GetDisplayOrder(Type type, PluginDescriptor descriptor) - { - var attr = type.GetAttribute(false); - if (attr != null) - { - return attr.DisplayOrder; - } - - if (typeof(IPlugin).IsAssignableFrom(type) && descriptor != null) - { - return descriptor.DisplayOrder; - } - - return 0; - } - - private bool GetIsHidden(Type type) - { - var attr = type.GetAttribute(false); - if (attr != null) - { - return attr.IsHidden; - } - - return false; - } - - private ExportFeatures GetExportFeature(Type type) - { - var attr = type.GetAttribute(false); - - if (attr != null) - { - return attr.Features; - } - - return ExportFeatures.None; - } - - private Tuple GetFriendlyName(Type type, PluginDescriptor descriptor) - { - string name = null; - string description = name; - - var attr = type.GetAttribute(false); - if (attr != null) - { - name = attr.Name; - description = attr.Description; - } - else if (typeof(IPlugin).IsAssignableFrom(type) && descriptor != null) - { - name = descriptor.FriendlyName; - description = descriptor.Description; - } - else - { - name = Inflector.Titleize(type.Name); - //throw Error.Application("The 'FriendlyNameAttribute' must be applied to a provider type if the provider does not implement 'IPlugin' (provider type: {0}, plugin: {1})".FormatInvariant(type.FullName, descriptor != null ? descriptor.SystemName : "-")); - } - - return new Tuple(name, description); - } - - private string[] GetDependentWidgets(Type type) - { - if (!typeof(IWidget).IsAssignableFrom(type)) - { - // don't let widgets depend on other widgets - var attr = type.GetAttribute(false); - if (attr != null) - { - return attr.WidgetSystemNames; - } - } - - return new string[] {}; - } - - private string ProviderTypeToKnownGroupName(Type implType) - { - if (typeof(ITaxProvider).IsAssignableFrom(implType)) - { - return "Tax"; - } - else if (typeof(IDiscountRequirementRule).IsAssignableFrom(implType)) - { - return "Marketing"; - } - else if (typeof(IExchangeRateProvider).IsAssignableFrom(implType)) - { - return "Payment"; - } - else if (typeof(IShippingRateComputationMethod).IsAssignableFrom(implType)) - { - return "Shipping"; - } - else if (typeof(IPaymentMethod).IsAssignableFrom(implType)) - { - return "Payment"; - } - else if (typeof(IExternalAuthenticationMethod).IsAssignableFrom(implType)) - { - return "Security"; - } - else if (typeof(IWidget).IsAssignableFrom(implType)) - { - return "CMS"; - } - else if (typeof(IExportProvider).IsAssignableFrom(implType)) - { - return "Exporting"; - } - else if (typeof(IOutputCacheProvider).IsAssignableFrom(implType)) - { - return "OutputCache"; - } - - return null; - } - - #endregion - - } + { + private readonly ITypeFinder _typeFinder; + private readonly IPluginFinder _pluginFinder; + + public ProvidersModule(ITypeFinder typeFinder, IPluginFinder pluginFinder) + { + _typeFinder = typeFinder; + _pluginFinder = pluginFinder; + } + + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().As().InstancePerRequest(); + + if (!DataSettings.DatabaseIsInstalled()) + return; + + var providerTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true).ToList(); + + foreach (var type in providerTypes) + { + var pluginDescriptor = _pluginFinder.GetPluginDescriptorByAssembly(type.Assembly); + var groupName = ProviderTypeToKnownGroupName(type); + var systemName = GetSystemName(type, pluginDescriptor); + var friendlyName = GetFriendlyName(type, pluginDescriptor); + var displayOrder = GetDisplayOrder(type, pluginDescriptor); + var dependentWidgets = GetDependentWidgets(type); + var resPattern = (pluginDescriptor != null ? "Plugins" : "Providers") + ".{1}.{0}"; // e.g. Plugins.FriendlyName.MySystemName + var settingPattern = (pluginDescriptor != null ? "Plugins" : "Providers") + ".{0}.{1}"; // e.g. Plugins.MySystemName.DisplayOrder + var isConfigurable = typeof(IConfigurable).IsAssignableFrom(type); + var isEditable = typeof(IUserEditable).IsAssignableFrom(type); + var isHidden = GetIsHidden(type); + var exportFeature = GetExportFeature(type); + + var registration = builder.RegisterType(type).Named(systemName).InstancePerRequest().PropertiesAutowired(PropertyWiringOptions.None); + registration.WithMetadata(m => + { + m.For(em => em.PluginDescriptor, pluginDescriptor); + m.For(em => em.GroupName, groupName); + m.For(em => em.SystemName, systemName); + m.For(em => em.ResourceKeyPattern, resPattern); + m.For(em => em.SettingKeyPattern, settingPattern); + m.For(em => em.FriendlyName, friendlyName.Item1); + m.For(em => em.Description, friendlyName.Item2); + m.For(em => em.DisplayOrder, displayOrder); + m.For(em => em.DependentWidgets, dependentWidgets); + m.For(em => em.IsConfigurable, isConfigurable); + m.For(em => em.IsEditable, isEditable); + m.For(em => em.IsHidden, isHidden); + m.For(em => em.ExportFeatures, exportFeature); + }); + + // Register specific provider type. + RegisterAsSpecificProvider(type, systemName, registration); + RegisterAsSpecificProvider(type, systemName, registration); + RegisterAsSpecificProvider(type, systemName, registration); + RegisterAsSpecificProvider(type, systemName, registration); + RegisterAsSpecificProvider(type, systemName, registration); + RegisterAsSpecificProvider(type, systemName, registration); + RegisterAsSpecificProvider(type, systemName, registration); + RegisterAsSpecificProvider(type, systemName, registration); + RegisterAsSpecificProvider(type, systemName, registration); + } + } + + #region Helpers + + private void RegisterAsSpecificProvider(Type implType, string systemName, IRegistrationBuilder registration) where T : IProvider + { + if (typeof(T).IsAssignableFrom(implType)) + { + try + { + registration.As().Named(systemName); + registration.WithMetadata(m => + { + m.For(em => em.ProviderType, typeof(T)); + }); + } + catch (Exception) { } + } + } + + private string GetSystemName(Type type, PluginDescriptor descriptor) + { + var attr = type.GetAttribute(false); + if (attr != null) + { + return attr.Name; + } + + if (typeof(IPlugin).IsAssignableFrom(type) && descriptor != null) + { + return descriptor.SystemName; + } + + return type.FullName; + //throw Error.Application("The 'SystemNameAttribute' must be applied to a provider type if the provider does not implement 'IPlugin' (provider type: {0}, plugin: {1})".FormatInvariant(type.FullName, descriptor != null ? descriptor.SystemName : "-")); + } + + private int GetDisplayOrder(Type type, PluginDescriptor descriptor) + { + var attr = type.GetAttribute(false); + if (attr != null) + { + return attr.DisplayOrder; + } + + if (typeof(IPlugin).IsAssignableFrom(type) && descriptor != null) + { + return descriptor.DisplayOrder; + } + + return 0; + } + + private bool GetIsHidden(Type type) + { + var attr = type.GetAttribute(false); + if (attr != null) + { + return attr.IsHidden; + } + + return false; + } + + private ExportFeatures GetExportFeature(Type type) + { + var attr = type.GetAttribute(false); + + if (attr != null) + { + return attr.Features; + } + + return ExportFeatures.None; + } + + private Tuple GetFriendlyName(Type type, PluginDescriptor descriptor) + { + string name = null; + string description = name; + + var attr = type.GetAttribute(false); + if (attr != null) + { + name = attr.Name; + description = attr.Description; + } + else if (typeof(IPlugin).IsAssignableFrom(type) && descriptor != null) + { + name = descriptor.FriendlyName; + description = descriptor.Description; + } + else + { + name = Inflector.Titleize(type.Name); + //throw Error.Application("The 'FriendlyNameAttribute' must be applied to a provider type if the provider does not implement 'IPlugin' (provider type: {0}, plugin: {1})".FormatInvariant(type.FullName, descriptor != null ? descriptor.SystemName : "-")); + } + + return new Tuple(name, description); + } + + private string[] GetDependentWidgets(Type type) + { + if (!typeof(IWidget).IsAssignableFrom(type)) + { + // don't let widgets depend on other widgets + var attr = type.GetAttribute(false); + if (attr != null) + { + return attr.WidgetSystemNames; + } + } + + return new string[] { }; + } + + private string ProviderTypeToKnownGroupName(Type implType) + { + if (typeof(ITaxProvider).IsAssignableFrom(implType)) + { + return "Tax"; + } + else if (typeof(IExchangeRateProvider).IsAssignableFrom(implType)) + { + return "Payment"; + } + else if (typeof(IShippingRateComputationMethod).IsAssignableFrom(implType)) + { + return "Shipping"; + } + else if (typeof(IPaymentMethod).IsAssignableFrom(implType)) + { + return "Payment"; + } + else if (typeof(IExternalAuthenticationMethod).IsAssignableFrom(implType)) + { + return "Security"; + } + else if (typeof(IWidget).IsAssignableFrom(implType)) + { + return "CMS"; + } + else if (typeof(IExportProvider).IsAssignableFrom(implType)) + { + return "Exporting"; + } + else if (typeof(IOutputCacheProvider).IsAssignableFrom(implType)) + { + return "OutputCache"; + } + + return null; + } + + #endregion + } internal class TasksModule : Module { @@ -1153,39 +1177,39 @@ protected override void Load(ContainerBuilder builder) } internal class DataExchangeModule : Module - { - private readonly ITypeFinder _typeFinder; - - public DataExchangeModule(ITypeFinder typeFinder) - { - _typeFinder = typeFinder; - } - - protected override void Load(ContainerBuilder builder) - { - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - - if (!DataSettings.DatabaseIsInstalled()) - return; - - builder.RegisterType().As().InstancePerRequest(); - builder.RegisterType().As().InstancePerRequest(); - - // IEntityImporter implementations - builder.RegisterType().Keyed(ImportEntityType.Product).InstancePerRequest(); - builder.RegisterType().Keyed(ImportEntityType.Category).InstancePerRequest(); - builder.RegisterType().Keyed(ImportEntityType.Customer).InstancePerRequest(); - builder.RegisterType().Keyed(ImportEntityType.NewsLetterSubscription).InstancePerRequest(); - - // Register resolving delegate - builder.Register>(c => - { - var cc = c.Resolve(); - return keyed => cc.ResolveKeyed(keyed); - }); - } - } + { + private readonly ITypeFinder _typeFinder; + + public DataExchangeModule(ITypeFinder typeFinder) + { + _typeFinder = typeFinder; + } + + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + if (!DataSettings.DatabaseIsInstalled()) + return; + + builder.RegisterType().As().InstancePerRequest(); + builder.RegisterType().As().InstancePerRequest(); + + // IEntityImporter implementations + builder.RegisterType().Keyed(ImportEntityType.Product).InstancePerRequest(); + builder.RegisterType().Keyed(ImportEntityType.Category).InstancePerRequest(); + builder.RegisterType().Keyed(ImportEntityType.Customer).InstancePerRequest(); + builder.RegisterType().Keyed(ImportEntityType.NewsLetterSubscription).InstancePerRequest(); + + // Register resolving delegate + builder.Register>(c => + { + var cc = c.Resolve(); + return keyed => cc.ResolveKeyed(keyed); + }); + } + } internal class RuleModule : SmartStore.Rules.RuleModule { @@ -1214,150 +1238,148 @@ protected override void Load(ContainerBuilder builder) .Keyed(RuleScope.Customer) .InstancePerRequest(); + builder.RegisterType() + .As() + .Keyed(RuleScope.Product) + .InstancePerRequest(); + + builder.RegisterType().As().InstancePerRequest(); + base.Load(builder); } } - #endregion + #endregion - #region Sources + #region Sources - class SettingsSource : IRegistrationSource + class SettingsSource : IRegistrationSource { static readonly MethodInfo BuildMethod = typeof(SettingsSource).GetMethod( "BuildRegistration", BindingFlags.Static | BindingFlags.NonPublic); - public IEnumerable RegistrationsFor( - Service service, - Func> registrations) + public IEnumerable RegistrationsFor(Service service, Func> registrations) { if (service is TypedService ts && typeof(ISettings).IsAssignableFrom(ts.ServiceType)) { - var buildMethod = BuildMethod.MakeGenericMethod(ts.ServiceType); - yield return (IComponentRegistration)buildMethod.Invoke(null, null); + var buildMethod = BuildMethod.MakeGenericMethod(ts.ServiceType); + yield return (IComponentRegistration)buildMethod.Invoke(null, null); } } + [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by reflection")] static IComponentRegistration BuildRegistration() where TSettings : ISettings, new() { return RegistrationBuilder - .ForDelegate((c, p) => - { - int currentStoreId = 0; - if (EngineContext.Current.IsFullyInitialized) - { - try - { - if (c.TryResolve(out IStoreContext storeContext)) - { - currentStoreId = storeContext.CurrentStore.Id; - //uncomment the code below if you want load settings per store only when you have two stores installed. - //var currentStoreId = c.Resolve().GetAllStores().Count > 1 - // c.Resolve().CurrentStore.Id : 0; - - ////although it's better to connect to your database and execute the following SQL: - //DELETE FROM [Setting] WHERE [StoreId] > 0 - - //return c.Resolve().LoadSetting(currentStoreId); - } - } - catch { } - } - - try - { - return c.Resolve().LoadSetting(currentStoreId); - } - catch - { - // Unit tests & tooling - return new TSettings(); - } - }) + .ForDelegate((c, p) => + { + int currentStoreId = 0; + if (EngineContext.Current.IsFullyInitialized) + { + try + { + if (c.TryResolve(out IStoreContext storeContext)) + { + currentStoreId = storeContext.CurrentStore.Id; + } + } + catch { } + } + + try + { + return c.Resolve().LoadSetting(currentStoreId); + } + catch + { + // Unit tests & tooling + return new TSettings(); + } + }) .InstancePerRequest() .CreateRegistration(); } - public bool IsAdapterForIndividualComponents { get { return false; } } + public bool IsAdapterForIndividualComponents => false; + } + + class WorkSource : IRegistrationSource + { + static readonly MethodInfo CreateMetaRegistrationMethod = typeof(WorkSource).GetMethod( + "CreateMetaRegistration", BindingFlags.Static | BindingFlags.NonPublic); + + private static bool IsClosingTypeOf(Type type, Type openGenericType) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == openGenericType; + } + + public IEnumerable RegistrationsFor(Service service, Func> registrationAccessor) + { + if (!(service is IServiceWithType swt) || !IsClosingTypeOf(swt.ServiceType, typeof(Work<>))) + return Enumerable.Empty(); + + var valueType = swt.ServiceType.GetGenericArguments()[0]; + + var valueService = swt.ChangeType(valueType); + + var registrationCreator = CreateMetaRegistrationMethod.MakeGenericMethod(valueType); + + return registrationAccessor(valueService) + .Select(v => registrationCreator.Invoke(null, new object[] { service, v })) + .Cast(); + } + + public bool IsAdapterForIndividualComponents => true; + + [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by reflection")] + static IComponentRegistration CreateMetaRegistration(Service providedService, IComponentRegistration valueRegistration) where T : class + { + var rb = RegistrationBuilder.ForDelegate( + (c, p) => + { + var accessor = c.Resolve(); + return new Work(w => + { + var scope = accessor.GetLifetimeScope(null); + if (scope == null) + return default(T); + + var workValues = scope.Resolve>(); + + if (!workValues.Values.TryGetValue(w, out T value)) + { + var request = new ResolveRequest(providedService, valueRegistration, p); + value = (T)workValues.ComponentContext.ResolveComponent(request); + workValues.Values[w] = value; + } + + return value; + + ////T value = default(T); // accessor.GetLifetimeScope(null).Resolve(); + //return accessor.GetLifetimeScope(null).Resolve(); + }); + }) + .As(providedService) + .Targeting(valueRegistration, false) + .SingleInstance(); + + return rb.CreateRegistration(); + } + } + + class WorkValues where T : class + { + public WorkValues(IComponentContext componentContext) + { + ComponentContext = componentContext; + Values = new Dictionary, T>(); + } + + public IComponentContext ComponentContext { get; private set; } + public IDictionary, T> Values { get; private set; } } - class WorkSource : IRegistrationSource - { - static readonly MethodInfo CreateMetaRegistrationMethod = typeof(WorkSource).GetMethod( - "CreateMetaRegistration", BindingFlags.Static | BindingFlags.NonPublic); - - private static bool IsClosingTypeOf(Type type, Type openGenericType) - { - return type.IsGenericType && type.GetGenericTypeDefinition() == openGenericType; - } - - public IEnumerable RegistrationsFor(Service service, Func> registrationAccessor) - { - if (!(service is IServiceWithType swt) || !IsClosingTypeOf(swt.ServiceType, typeof(Work<>))) - return Enumerable.Empty(); - - var valueType = swt.ServiceType.GetGenericArguments()[0]; - - var valueService = swt.ChangeType(valueType); - - var registrationCreator = CreateMetaRegistrationMethod.MakeGenericMethod(valueType); - - return registrationAccessor(valueService) - .Select(v => registrationCreator.Invoke(null, new object[] { service, v })) - .Cast(); - } - - public bool IsAdapterForIndividualComponents - { - get { return true; } - } - - static IComponentRegistration CreateMetaRegistration(Service providedService, IComponentRegistration valueRegistration) where T : class - { - var rb = RegistrationBuilder.ForDelegate( - (c, p) => { - var accessor = c.Resolve(); - return new Work(w => - { - var scope = accessor.GetLifetimeScope(null); - if (scope == null) - return default(T); - - var workValues = scope.Resolve>(); - - if (!workValues.Values.TryGetValue(w, out T value)) - { - value = (T)workValues.ComponentContext.ResolveComponent(valueRegistration, p); - workValues.Values[w] = value; - } - - return value; - - ////T value = default(T); // accessor.GetLifetimeScope(null).Resolve(); - //return accessor.GetLifetimeScope(null).Resolve(); - }); - }) - .As(providedService) - .Targeting(valueRegistration) - .SingleInstance(); - - return rb.CreateRegistration(); - } - } - - class WorkValues where T : class - { - public WorkValues(IComponentContext componentContext) - { - ComponentContext = componentContext; - Values = new Dictionary, T>(); - } - - public IComponentContext ComponentContext { get; private set; } - public IDictionary, T> Values { get; private set; } - } - - #endregion + #endregion } diff --git a/src/Presentation/SmartStore.Web.Framework/Events/TabStripCreated.cs b/src/Presentation/SmartStore.Web.Framework/Events/TabStripCreated.cs index 30d4d9b9a3..91f9b08a0e 100644 --- a/src/Presentation/SmartStore.Web.Framework/Events/TabStripCreated.cs +++ b/src/Presentation/SmartStore.Web.Framework/Events/TabStripCreated.cs @@ -1,10 +1,8 @@ -using System; +using System.Collections.Generic; using System.Web.Mvc; -using SmartStore.Web.Framework.UI; -using System.Collections; -using System.Collections.Generic; using SmartStore.Core.Infrastructure; using SmartStore.Web.Framework.Localization; +using SmartStore.Web.Framework.UI; namespace SmartStore.Web.Framework.Events { @@ -22,7 +20,7 @@ public TabStripCreated(TabFactory itemFactory, string tabStripName, HtmlHelper h this.Model = model; this.ItemFactory = itemFactory; } - + public string TabStripName { get; private set; } public HtmlHelper Html { get; private set; } public object Model { get; private set; } @@ -41,7 +39,7 @@ public void AddWidget(RouteInfo routeInfo) { _widgets = new List(); Html.ViewContext.ViewData["Tab.{0}.Widgets".FormatInvariant(this.TabStripName)] = _widgets; - + CreateWidgetsTab(); } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/ActionResultExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/ActionResultExtensions.cs index b0dbc907ce..8e5f88e4c3 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/ActionResultExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/ActionResultExtensions.cs @@ -1,23 +1,22 @@ -using System; -using System.Web.Mvc; +using System.Web.Mvc; // use base SmartStore Namespace to ensure the extension methods are always available namespace SmartStore { - public static class ActionResultExtensions - { - public static bool IsHtmlViewResult(this ActionResult result) - { - if (result is ViewResultBase) - return true; + public static class ActionResultExtensions + { + public static bool IsHtmlViewResult(this ActionResult result) + { + if (result is ViewResultBase) + return true; - var contentResult = result as ContentResult; - if (contentResult != null) - { - return contentResult.ContentType.IsCaseInsensitiveEqual("text/html"); - } + var contentResult = result as ContentResult; + if (contentResult != null) + { + return contentResult.ContentType.IsCaseInsensitiveEqual("text/html"); + } - return false; - } - } + return false; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/DateTimeExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/DateTimeExtensions.cs index 53628958a0..854fe04c54 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/DateTimeExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/DateTimeExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using SmartStore.Core.Infrastructure; using SmartStore.Core.Localization; using SmartStore.Services.Helpers; @@ -6,13 +7,14 @@ namespace SmartStore.Web.Framework { - public static class DateTimeExtensions - { + public static class DateTimeExtensions + { /// /// Relative formatting of DateTime (e.g. 2 hours ago, a month ago) /// /// Source (UTC format) /// Formatted date and time string + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string RelativeFormat(this DateTime source) { return RelativeFormat(source, string.Empty); @@ -24,6 +26,7 @@ public static string RelativeFormat(this DateTime source) /// Source (UTC format) /// Default format string (in case relative formatting is not applied) /// Formatted date and time string + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string RelativeFormat(this DateTime source, string defaultFormat) { return RelativeFormat(source, false, defaultFormat); @@ -39,8 +42,8 @@ public static string RelativeFormat(this DateTime source, string defaultFormat) public static string RelativeFormat(this DateTime source, bool convertToUserTime, string defaultFormat) { string result = ""; - Localizer T = EngineContext.Current.Resolve().Get; - + Localizer T = EngineContext.Current.Resolve().Get; + var ts = new TimeSpan(DateTime.UtcNow.Ticks - source.Ticks); double delta = ts.TotalSeconds; @@ -48,44 +51,44 @@ public static string RelativeFormat(this DateTime source, bool convertToUserTime { if (delta < 60) // 60 (seconds) { - result = ts.Seconds == 1 ? T("Time.OneSecondAgo") : T("Time.SecondsAgo", ts.Seconds); + result = ts.Seconds == 1 ? T("Time.OneSecondAgo") : T("Time.SecondsAgo", ts.Seconds); } else if (delta < 120) //2 (minutes) * 60 (seconds) { - result = T("Time.OneMinuteAgo"); + result = T("Time.OneMinuteAgo"); } else if (delta < 2700) // 45 (minutes) * 60 (seconds) { - result = String.Format(T("Time.MinutesAgo"), ts.Minutes); + result = String.Format(T("Time.MinutesAgo"), ts.Minutes); } else if (delta < 5400) // 90 (minutes) * 60 (seconds) { - result = T("Time.OneHourAgo"); + result = T("Time.OneHourAgo"); } else if (delta < 86400) // 24 (hours) * 60 (minutes) * 60 (seconds) { int hours = ts.Hours; if (hours == 1) hours = 2; - result = T("Time.HoursAgo", hours); + result = T("Time.HoursAgo", hours); } else if (delta < 172800) // 48 (hours) * 60 (minutes) * 60 (seconds) { - result = T("Time.Yesterday"); + result = T("Time.Yesterday"); } else if (delta < 2592000) // 30 (days) * 24 (hours) * 60 (minutes) * 60 (seconds) { - result = String.Format(T("Time.DaysAgo"), ts.Days); + result = String.Format(T("Time.DaysAgo"), ts.Days); } else if (delta < 31104000) // 12 (months) * 30 (days) * 24 (hours) * 60 (minutes) * 60 (seconds) { int months = Convert.ToInt32(Math.Floor((double)ts.Days / 30)); - result = months <= 1 ? T("Time.OneMonthAgo") : T("Time.MonthsAgo", months); + result = months <= 1 ? T("Time.OneMonthAgo") : T("Time.MonthsAgo", months); } else { int years = Convert.ToInt32(Math.Floor((double)ts.Days / 365)); - result = years <= 1 ? T("Time.OneYearAgo") : T("Time.YearsAgo", years); + result = years <= 1 ? T("Time.OneYearAgo") : T("Time.YearsAgo", years); } } else @@ -109,51 +112,51 @@ public static string RelativeFormat(this DateTime source, bool convertToUserTime return result.ReplaceNativeDigits(); } - public static string Prettify(this TimeSpan ts) - { - Localizer T = EngineContext.Current.Resolve().Get; - double seconds = ts.TotalSeconds; + public static string Prettify(this TimeSpan ts) + { + Localizer T = EngineContext.Current.Resolve().Get; + double seconds = ts.TotalSeconds; - try - { - int secsTemp = Convert.ToInt32(seconds); - string label = T("Time.SecondsAbbr"); - int remainder = 0; - string remainderLabel = ""; + try + { + int secsTemp = Convert.ToInt32(seconds); + string label = T("Time.SecondsAbbr"); + int remainder = 0; + string remainderLabel = ""; - if (secsTemp > 59) - { - remainder = secsTemp % 60; - secsTemp /= 60; - label = T("Time.MinutesAbbr"); - remainderLabel = T("Time.SecondsAbbr"); - } + if (secsTemp > 59) + { + remainder = secsTemp % 60; + secsTemp /= 60; + label = T("Time.MinutesAbbr"); + remainderLabel = T("Time.SecondsAbbr"); + } - if (secsTemp > 59) - { - remainder = secsTemp % 60; - secsTemp /= 60; - label = (secsTemp == 1) ? T("Time.HourAbbr") : T("Time.HoursAbbr"); - remainderLabel = T("Time.MinutesAbbr"); - } + if (secsTemp > 59) + { + remainder = secsTemp % 60; + secsTemp /= 60; + label = (secsTemp == 1) ? T("Time.HourAbbr") : T("Time.HoursAbbr"); + remainderLabel = T("Time.MinutesAbbr"); + } - string result = null; + string result = null; - if (remainder == 0) - { - result = string.Format("{0:#,##0.#} {1}", secsTemp, label); - } - else - { - result = string.Format("{0:#,##0} {1} {2} {3}", secsTemp, label, remainder, remainderLabel); - } + if (remainder == 0) + { + result = string.Format("{0:#,##0.#} {1}", secsTemp, label); + } + else + { + result = string.Format("{0:#,##0} {1} {2} {3}", secsTemp, label, remainder, remainderLabel); + } - return result.ReplaceNativeDigits(); - } - catch - { - return "(-)"; - } - } + return result.ReplaceNativeDigits(); + } + catch + { + return "(-)"; + } + } } } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlExtensions.cs index b2198a285f..efb5eecbae 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlExtensions.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.Linq; using System.Linq.Expressions; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Web; @@ -15,7 +16,9 @@ using SmartStore.Core.Domain.Localization; using SmartStore.Core.Infrastructure; using SmartStore.Services.Localization; +using SmartStore.Services.Media; using SmartStore.Utilities; +using SmartStore.Utilities.ObjectPools; using SmartStore.Web.Framework.Localization; using SmartStore.Web.Framework.Modelling; using SmartStore.Web.Framework.Settings; @@ -23,25 +26,26 @@ namespace SmartStore.Web.Framework { - public enum InputEditorType - { TextBox, + public enum InputEditorType + { + TextBox, Password, Hidden, Checkbox/*, RadioButton*/ } - + public static class HtmlExtensions { public static MvcHtmlString Hint(this HtmlHelper helper, string value) { - if (String.IsNullOrEmpty(value)) - { - return MvcHtmlString.Empty; - } + if (String.IsNullOrEmpty(value)) + { + return MvcHtmlString.Empty; + } - // create a - var a = new TagBuilder("a"); + // create a + var a = new TagBuilder("a"); a.MergeAttribute("href", "#"); a.MergeAttribute("onclick", "return false;"); //a.MergeAttribute("rel", "tooltip"); @@ -49,9 +53,9 @@ public static MvcHtmlString Hint(this HtmlHelper helper, string value) a.MergeAttribute("tabindex", "-1"); a.AddCssClass("hint"); - // Create img - var img = new TagBuilder("i"); - img.AddCssClass("fa fa-question-circle"); + // Create img + var img = new TagBuilder("i"); + img.AddCssClass("fa fa-question-circle"); a.InnerHtml = img.ToString(); @@ -63,35 +67,35 @@ public static HelperResult LocalizedEditor(this HtmlHel where T : ILocalizedModel where TLocalizedModelLocal : ILocalizedModelLocal { - return new HelperResult(writer => + return new HelperResult(writer => { if (helper.ViewData.Model.Locales.Count > 1) { - var languageService = EngineContext.Current.Resolve(); + var languageService = EngineContext.Current.Resolve(); - writer.Write("
              "); + writer.Write("
              "); var tabStrip = helper.SmartStore().TabStrip().Name(name).SmartTabSelection(false).Style(TabsStyle.Tabs).AddCssClass("nav-locales").Items(x => { - if (standardTemplate != null) - { - var masterLanguage = languageService.GetLanguageById(languageService.GetDefaultLanguageId()); - x.Add().Text(EngineContext.Current.Resolve().GetResource("Admin.Common.Standard")) - .ContentHtmlAttributes(new { @class = "locale-editor-content", data_lang = masterLanguage.LanguageCulture, data_rtl = masterLanguage.Rtl.ToString().ToLower() }) - .Content(standardTemplate(helper.ViewData.Model).ToHtmlString()) - .Selected(true); - } + if (standardTemplate != null) + { + var masterLanguage = languageService.GetLanguageById(languageService.GetDefaultLanguageId()); + x.Add().Text(EngineContext.Current.Resolve().GetResource("Admin.Common.Standard")) + .ContentHtmlAttributes(new { @class = "locale-editor-content", data_lang = masterLanguage.LanguageCulture, data_rtl = masterLanguage.Rtl.ToString().ToLower() }) + .Content(standardTemplate(helper.ViewData.Model).ToHtmlString()) + .Selected(true); + } for (int i = 0; i < helper.ViewData.Model.Locales.Count; i++) { var locale = helper.ViewData.Model.Locales[i]; 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) - .Selected(i == 0 && standardTemplate == null); + 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) + .Selected(i == 0 && standardTemplate == null); } }).ToHtmlString(); writer.Write(tabStrip); @@ -104,20 +108,21 @@ public static HelperResult LocalizedEditor(this HtmlHel }); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MvcHtmlString DeleteConfirmation(this HtmlHelper helper, string buttonsSelector = null) where T : EntityModelBase { return DeleteConfirmation(helper, "", buttonsSelector); } - /// - /// Adds an action name parameter for using other delete action names - /// - /// - /// - /// - /// - /// - public static MvcHtmlString DeleteConfirmation(this HtmlHelper helper, string actionName, string buttonsSelector = null) where T : EntityModelBase + /// + /// Adds an action name parameter for using other delete action names + /// + /// + /// + /// + /// + /// + public static MvcHtmlString DeleteConfirmation(this HtmlHelper helper, string actionName, string buttonsSelector = null) where T : EntityModelBase { if (String.IsNullOrEmpty(actionName)) actionName = "Delete"; @@ -134,26 +139,28 @@ public static MvcHtmlString DeleteConfirmation(this HtmlHelper helper, str EntityType = buttonsSelector.Replace("-delete", "") }; - var script = string.Empty; - if (buttonsSelector.HasValue()) - { - script = "\n"; - } + var script = string.Empty; + if (buttonsSelector.HasValue()) + { + script = "\n"; + } - helper.SmartStore().Window().Name(modalId) - .Title(EngineContext.Current.Resolve().GetResource("Admin.Common.AreYouSure")) - .Content(helper.Partial("Delete", deleteConfirmationModel).ToHtmlString()) - .Show(false) - .Render(); + helper.SmartStore().Window().Name(modalId) + .Title(EngineContext.Current.Resolve().GetResource("Admin.Common.AreYouSure")) + .AddCssClass("modal-confirm-delete") + .BodyHtmlAttributes(new { @class = "pt-0" }) + .Content(helper.Partial("Delete", deleteConfirmationModel).ToHtmlString()) + .Show(false) + .Render(); return new MvcHtmlString(script); } - public static MvcHtmlString SmartLabel(this HtmlHelper helper, string expression, string labelText, string hint = null, object htmlAttributes = null) - { - var result = new StringBuilder(); + public static MvcHtmlString SmartLabel(this HtmlHelper helper, string expression, string labelText, string hint = null, object htmlAttributes = null) + { + var result = PooledStringBuilder.Rent(); - result.Append("
              "); + result.Append("
              "); if (expression.IsEmpty() && labelText.IsEmpty()) { @@ -164,7 +171,7 @@ public static MvcHtmlString SmartLabel(this HtmlHelper helper, string expression var labelAttrs = new RouteValueDictionary(htmlAttributes); var label = helper.Label(expression, labelText, labelAttrs); - result.Append(label); + result.Append(label.ToHtmlString()); } if (hint.HasValue()) @@ -174,96 +181,97 @@ public static MvcHtmlString SmartLabel(this HtmlHelper helper, string expression result.Append("
              "); - return MvcHtmlString.Create(result.ToString()); - } + return MvcHtmlString.Create(result.ToStringAndReturn()); + } + + public static MvcHtmlString SmartLabelFor( + this HtmlHelper helper, + Expression> expression, + bool displayHint = true, + object htmlAttributes = null) + { + var metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData); + metadata.AdditionalValues.TryGetValue("SmartResourceDisplayName", out object resourceDisplayName); + + return SmartLabelFor(helper, expression, resourceDisplayName as SmartResourceDisplayName, metadata, displayHint, htmlAttributes); + } public static MvcHtmlString SmartLabelFor( - this HtmlHelper helper, - Expression> expression, - bool displayHint = true, - object htmlAttributes = null) - { - var metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData); - metadata.AdditionalValues.TryGetValue("SmartResourceDisplayName", out object resourceDisplayName); - - return SmartLabelFor(helper, expression, resourceDisplayName as SmartResourceDisplayName, metadata, displayHint, htmlAttributes); - } - - public static MvcHtmlString SmartLabelFor( - this HtmlHelper helper, - Expression> expression, - string resourceKey, - bool displayHint = true, - object htmlAttributes = null) - { - Guard.NotEmpty(resourceKey, nameof(resourceKey)); - - var metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData); - var resourceDisplayName = new SmartResourceDisplayName(resourceKey, metadata.PropertyName); - - return SmartLabelFor(helper, expression, resourceDisplayName, metadata, displayHint, htmlAttributes); - } - - private static MvcHtmlString SmartLabelFor( - this HtmlHelper helper, - Expression> expression, - SmartResourceDisplayName resourceDisplayName, - ModelMetadata metadata, - bool displayHint = true, - object htmlAttributes = null) - { - var result = new StringBuilder(); - string labelText = null; - string hint = null; - - if (resourceDisplayName != null) - { - // resolve label display name - labelText = resourceDisplayName.DisplayName.NullEmpty(); - if (labelText == null) - { - // take reskey as absolute fallback - labelText = resourceDisplayName.ResourceKey; - } - - // resolve hint - if (displayHint) - { - var langId = EngineContext.Current.Resolve().WorkingLanguage.Id; - hint = EngineContext.Current.Resolve().GetResource(resourceDisplayName.ResourceKey + ".Hint", langId, false, "", true); - } - } - - if (labelText == null) - { - labelText = metadata.PropertyName.SplitPascalCase(); - } - - var labelAttrs = new RouteValueDictionary(htmlAttributes); - //labelAttrs.AppendCssClass("col-form-label"); - - var label = helper.LabelFor(expression, labelText, labelAttrs); - - if (displayHint) - { - result.Append("
              "); - { - result.Append(label); - if (hint.HasValue()) - { - result.Append(helper.Hint(hint).ToHtmlString()); - } - } - result.Append("
              "); - } - else - { - result.Append(label); - } - - return MvcHtmlString.Create(result.ToString()); - } + this HtmlHelper helper, + Expression> expression, + string resourceKey, + bool displayHint = true, + object htmlAttributes = null) + { + Guard.NotEmpty(resourceKey, nameof(resourceKey)); + + var metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData); + var resourceDisplayName = new SmartResourceDisplayName(resourceKey, metadata.PropertyName); + return SmartLabelFor(helper, expression, resourceDisplayName, metadata, displayHint, htmlAttributes); + } + + private static MvcHtmlString SmartLabelFor( + this HtmlHelper helper, + Expression> expression, + SmartResourceDisplayName resourceDisplayName, + ModelMetadata metadata, + bool displayHint = true, + object htmlAttributes = null) + { + var result = PooledStringBuilder.Rent(); + string labelText = null; + string hint = null; + + if (resourceDisplayName != null) + { + // resolve label display name + labelText = resourceDisplayName.DisplayName.NullEmpty(); + if (labelText == null) + { + // take reskey as absolute fallback + labelText = resourceDisplayName.ResourceKey; + } + + // resolve hint + if (displayHint) + { + var langId = EngineContext.Current.Resolve().WorkingLanguage.Id; + hint = EngineContext.Current.Resolve().GetResource(resourceDisplayName.ResourceKey + ".Hint", langId, false, "", true); + } + } + + if (labelText == null) + { + labelText = metadata.PropertyName.SplitPascalCase(); + } + + var labelAttrs = new RouteValueDictionary(htmlAttributes); + //labelAttrs.AppendCssClass("col-form-label"); + + var label = helper.LabelFor(expression, labelText, labelAttrs); + + if (displayHint) + { + result.Append("
              "); + { + result.Append(label); + if (hint.HasValue()) + { + result.Append(helper.Hint(hint).ToHtmlString()); + } + } + result.Append("
              "); + } + else + { + result.Append(label); + } + + return MvcHtmlString.Create(result.ToStringAndReturn()); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string FieldNameFor(this HtmlHelper html, Expression> expression) { return html.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(expression)); @@ -296,19 +304,19 @@ public static MvcHtmlString DatePickerDropDowns(this HtmlHelper html, int? beginYear = null, int? endYear = null, int? selectedDay = null, int? selectedMonth = null, int? selectedYear = null, bool localizeLabels = true, bool disabled = false) { - var row = new TagBuilder("div"); - row.AddCssClass("row xs-gutters"); + var row = new TagBuilder("div"); + row.AddCssClass("row xs-gutters"); - var daysCol = new TagBuilder("div"); - daysCol.AddCssClass("col"); + var daysCol = new TagBuilder("div"); + daysCol.AddCssClass("col"); - var monthsCol = new TagBuilder("div"); - monthsCol.AddCssClass("col"); + var monthsCol = new TagBuilder("div"); + monthsCol.AddCssClass("col"); - var yearsCol = new TagBuilder("div"); - yearsCol.AddCssClass("col"); + var yearsCol = new TagBuilder("div"); + yearsCol.AddCssClass("col"); - var daysList = new TagBuilder("select"); + var daysList = new TagBuilder("select"); var monthsList = new TagBuilder("select"); var yearsList = new TagBuilder("select"); @@ -319,21 +327,25 @@ public static MvcHtmlString DatePickerDropDowns(this HtmlHelper html, daysList.Attributes.Add("name", dayName); monthsList.Attributes.Add("name", monthName); yearsList.Attributes.Add("name", yearName); - - daysList.Attributes.Add("class", "date-part form-control noskin"); - monthsList.Attributes.Add("class", "date-part form-control noskin"); - yearsList.Attributes.Add("class", "date-part form-control noskin"); - - daysList.Attributes.Add("data-minimum-results-for-search", "100"); - monthsList.Attributes.Add("data-minimum-results-for-search", "100"); - //yearsList.Attributes.Add("data-minimum-results-for-search", "100"); - - if (disabled) - { - daysList.Attributes.Add("disabled", "disabled"); - monthsList.Attributes.Add("disabled", "disabled"); - yearsList.Attributes.Add("disabled", "disabled"); - } + + daysList.Attributes.Add("id", dayName); + monthsList.Attributes.Add("id", monthName); + yearsList.Attributes.Add("id", yearName); + + daysList.Attributes.Add("class", "date-part form-control noskin remember"); + monthsList.Attributes.Add("class", "date-part form-control noskin remember"); + yearsList.Attributes.Add("class", "date-part form-control noskin remember"); + + daysList.Attributes.Add("data-minimum-results-for-search", "100"); + monthsList.Attributes.Add("data-minimum-results-for-search", "100"); + //yearsList.Attributes.Add("data-minimum-results-for-search", "100"); + + if (disabled) + { + daysList.Attributes.Add("disabled", "disabled"); + monthsList.Attributes.Add("disabled", "disabled"); + yearsList.Attributes.Add("disabled", "disabled"); + } var days = new StringBuilder(); var months = new StringBuilder(); @@ -364,7 +376,7 @@ public static MvcHtmlString DatePickerDropDowns(this HtmlHelper html, for (int i = 1; i <= 12; i++) { months.AppendFormat("", - i, + i, (selectedMonth.HasValue && selectedMonth.Value == i) ? " selected=\"selected\"" : null, CultureInfo.CurrentUICulture.DateTimeFormat.GetMonthName(i)); } @@ -385,13 +397,13 @@ public static MvcHtmlString DatePickerDropDowns(this HtmlHelper html, monthsList.InnerHtml = months.ToString(); yearsList.InnerHtml = years.ToString(); - daysCol.InnerHtml = daysList.ToString(); - monthsCol.InnerHtml = monthsList.ToString(); - yearsCol.InnerHtml = yearsList.ToString(); + daysCol.InnerHtml = daysList.ToString(); + monthsCol.InnerHtml = monthsList.ToString(); + yearsCol.InnerHtml = yearsList.ToString(); - row.InnerHtml = string.Concat(daysCol, monthsCol, yearsCol); + row.InnerHtml = string.Concat(daysCol, monthsCol, yearsCol); - return MvcHtmlString.Create(row.ToString()); + return MvcHtmlString.Create(row.ToString()); } #region DropDownList Extensions @@ -403,7 +415,6 @@ public static MvcHtmlString DropDownListForEnum( Expression> expression, string optionLabel = null) where TEnum : struct { - return htmlHelper.DropDownListForEnum(expression, null, optionLabel); } @@ -423,7 +434,7 @@ public static MvcHtmlString DropDownListForEnum( } public static MvcHtmlString DropDownListForEnum( - this HtmlHelper htmlHelper, + this HtmlHelper htmlHelper, Expression> expression, IDictionary htmlAttributes, string optionLabel = null) where TEnum : struct @@ -437,7 +448,7 @@ public static MvcHtmlString DropDownListForEnum( ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); Type enumType = metadata.ModelType.GetNonNullableType(); IEnumerable values = Enum.GetValues(enumType).Cast(); - + IEnumerable items = values.Select(value => new SelectListItem { @@ -456,120 +467,123 @@ public static MvcHtmlString DropDownListForEnum( #endregion + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MvcHtmlString Widget(this HtmlHelper helper, string widgetZone) { - return helper.Widget(widgetZone, null); - } - - public static MvcHtmlString Widget(this HtmlHelper helper, string widgetZone, object model) - { - var routeValues = GetWidgetsByZoneRouteValues(helper, widgetZone, model); - if (routeValues != null) - { - return helper.Action("WidgetsByZone", "Widget", routeValues); - } - - return MvcHtmlString.Empty; - } - - public static void RenderWidget(this HtmlHelper helper, string widgetZone) - { - helper.RenderWidget(widgetZone, null); - } - - public static void RenderWidget(this HtmlHelper helper, string widgetZone, object model) - { - var routeValues = GetWidgetsByZoneRouteValues(helper, widgetZone, model); - if (routeValues != null) - { - helper.RenderAction("WidgetsByZone", "Widget", routeValues); - } - } - - private static object GetWidgetsByZoneRouteValues(HtmlHelper helper, string widgetZone, object model) - { - if (widgetZone.HasValue()) - { - model = model ?? helper.ViewData.Model; - var widgetSelector = EngineContext.Current.Resolve(); - var widgets = widgetSelector.GetWidgets(widgetZone, model).ToList(); - if (widgets.Any()) - { - var zoneModel = new WidgetZoneModel { Widgets = widgets, WidgetZone = widgetZone, Model = model }; - return new { zoneModel = zoneModel, model = model, area = "" }; - } - } - - return null; - } - - public static IHtmlString MetaAcceptLanguage(this HtmlHelper html) + return helper.Widget(widgetZone, null); + } + + public static MvcHtmlString Widget(this HtmlHelper helper, string widgetZone, object model) + { + var routeValues = GetWidgetsByZoneRouteValues(helper, widgetZone, model); + if (routeValues != null) + { + return helper.Action("WidgetsByZone", "Widget", routeValues); + } + + return MvcHtmlString.Empty; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void RenderWidget(this HtmlHelper helper, string widgetZone) + { + helper.RenderWidget(widgetZone, null); + } + + public static void RenderWidget(this HtmlHelper helper, string widgetZone, object model) + { + var routeValues = GetWidgetsByZoneRouteValues(helper, widgetZone, model); + if (routeValues != null) + { + helper.RenderAction("WidgetsByZone", "Widget", routeValues); + } + } + + private static object GetWidgetsByZoneRouteValues(HtmlHelper helper, string widgetZone, object model) + { + if (widgetZone.HasValue()) + { + model = model ?? helper.ViewData.Model; + var widgetSelector = EngineContext.Current.Resolve(); + var widgets = widgetSelector.GetWidgets(widgetZone, model).ToList(); + if (widgets.Any()) + { + var zoneModel = new WidgetZoneModel { Widgets = widgets, WidgetZone = widgetZone, Model = model }; + return new { zoneModel = zoneModel, model = model, area = "" }; + } + } + + return null; + } + + public static IHtmlString MetaAcceptLanguage(this HtmlHelper html) { var acceptLang = HttpUtility.HtmlAttributeEncode(Thread.CurrentThread.CurrentUICulture.ToString()); return new MvcHtmlString(string.Format("", acceptLang)); } - public static IHtmlString LanguageAttributes(this HtmlHelper html, bool omitLTR = false) - { - return LanguageAttributes(html, EngineContext.Current.Resolve().WorkingLanguage, omitLTR); - } + public static IHtmlString LanguageAttributes(this HtmlHelper html, bool omitLTR = false) + { + return LanguageAttributes(html, EngineContext.Current.Resolve().WorkingLanguage, omitLTR); + } - public static IHtmlString LanguageAttributes(this HtmlHelper html, LocalizedValue localizedValue) - { - if (localizedValue == null || !localizedValue.BidiOverride) - { - return MvcHtmlString.Empty; - } + public static IHtmlString LanguageAttributes(this HtmlHelper html, LocalizedValue localizedValue) + { + if (localizedValue == null || !localizedValue.BidiOverride) + { + return MvcHtmlString.Empty; + } - return LanguageAttributes(html, localizedValue.CurrentLanguage, false); - } + return LanguageAttributes(html, localizedValue.CurrentLanguage, false); + } - public static IHtmlString LanguageAttributes(this HtmlHelper html, Language currentLanguage, bool omitLTR = false) - { - Guard.NotNull(currentLanguage, nameof(currentLanguage)); + public static IHtmlString LanguageAttributes(this HtmlHelper html, Language currentLanguage, bool omitLTR = false) + { + Guard.NotNull(currentLanguage, nameof(currentLanguage)); - var code = currentLanguage.GetTwoLetterISOLanguageName(); - var rtl = currentLanguage.Rtl; + var code = currentLanguage.GetTwoLetterISOLanguageName(); + var rtl = currentLanguage.Rtl; - var result = "lang=\"" + code + "\""; - if (rtl || !omitLTR) - { - result += " dir=\"" + (rtl ? "rtl" : "ltr") + "\""; - } + var result = "lang=\"" + code + "\""; + if (rtl || !omitLTR) + { + result += " dir=\"" + (rtl ? "rtl" : "ltr") + "\""; + } - return new MvcHtmlString(result); - } + return new MvcHtmlString(result); + } - public static IHtmlString LanguageAttributes(this HtmlHelper html, bool currentRtl, Language pageLanguage) - { - Guard.NotNull(pageLanguage, nameof(pageLanguage)); + public static IHtmlString LanguageAttributes(this HtmlHelper html, bool currentRtl, Language pageLanguage) + { + Guard.NotNull(pageLanguage, nameof(pageLanguage)); - if (currentRtl == pageLanguage.Rtl) - { - return MvcHtmlString.Empty; - } + if (currentRtl == pageLanguage.Rtl) + { + return MvcHtmlString.Empty; + } - var result = "dir=\"" + (currentRtl ? "rtl" : "ltr") + "\""; - return new MvcHtmlString(result); - } + var result = "dir=\"" + (currentRtl ? "rtl" : "ltr") + "\""; + return new MvcHtmlString(result); + } - public static MvcHtmlString ControlGroupFor( - this HtmlHelper html, - Expression> expression, + public static MvcHtmlString ControlGroupFor( + this HtmlHelper html, + Expression> expression, InputEditorType editorType = InputEditorType.TextBox, bool required = false, string helpHint = null, - string breakpoint = "md") + string breakpoint = "md", + string additionalCtrlClasses = "") { if (editorType == InputEditorType.Hidden) { return html.HiddenFor(expression); } - string inputHtml = ""; - var htmlAttributes = new RouteValueDictionary(); - var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData); - var dataTypeName = metadata.DataTypeName.EmptyNull(); + string inputHtml = ""; + var htmlAttributes = new RouteValueDictionary(); + var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData); + var dataTypeName = metadata.DataTypeName.EmptyNull(); var groupClass = "form-group row"; var labelClass = "col-{0}-3 col-form-label".FormatInvariant(breakpoint.NullEmpty() ?? "md"); var controlsClass = "col-{0}-9".FormatInvariant(breakpoint.NullEmpty() ?? "md"); @@ -586,29 +600,29 @@ public static MvcHtmlString ControlGroupFor( if (!required && (editorType == InputEditorType.TextBox || editorType == InputEditorType.Password)) { - htmlAttributes.Add("placeholder", EngineContext.Current.Resolve().GetResource("Common.Optional")); - } - - switch (dataTypeName) - { - case "EmailAddress": - htmlAttributes.Add("type", "email"); - break; - case "PhoneNumber": - htmlAttributes.Add("type", "tel"); - break; - } - - htmlAttributes.Add("class", "form-control"); - + htmlAttributes.Add("placeholder", EngineContext.Current.Resolve().GetResource("Common.Optional")); + } + + switch (dataTypeName) + { + case "EmailAddress": + htmlAttributes.Add("type", "email"); + break; + case "PhoneNumber": + htmlAttributes.Add("type", "tel"); + break; + } + + htmlAttributes.Add("class", "form-control" + (additionalCtrlClasses.HasValue() ? " " + additionalCtrlClasses : "")); + switch (editorType) { case InputEditorType.Checkbox: - CommonHelper.TryConvert(metadata.Model, out var isChecked); + CommonHelper.TryConvert(metadata.Model, out var isChecked); inputHtml = string.Format("
              {0}
              ", html.CheckBox(ExpressionHelper.GetExpressionText(expression), isChecked, new { @class = "form-check-input" }).ToString(), - html.IdFor(expression), - metadata.DisplayName); + html.IdFor(expression), + metadata.DisplayName); break; case InputEditorType.Password: inputHtml = html.PasswordFor(expression, htmlAttributes).ToString(); @@ -617,7 +631,7 @@ public static MvcHtmlString ControlGroupFor( inputHtml = html.TextBoxFor(expression, htmlAttributes).ToString(); break; } - + sb.AppendLine(inputHtml); sb.AppendLine(html.ValidationMessageFor(expression).ToString()); if (helpHint.HasValue()) @@ -631,17 +645,18 @@ public static MvcHtmlString ControlGroupFor( return MvcHtmlString.Create(sb.ToString()); } - public static MvcHtmlString ColorBox(this HtmlHelper html, string name, string color) - { - return ColorBox(html, name, color, null); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MvcHtmlString ColorBox(this HtmlHelper html, string name, string color) + { + return ColorBox(html, name, color, null); + } public static MvcHtmlString ColorBox(this HtmlHelper html, string name, string color, string defaultColor) { - var sb = new StringBuilder(); + var sb = PooledStringBuilder.Rent(); - defaultColor = defaultColor.EmptyNull(); - var isDefault = color.IsCaseInsensitiveEqual(defaultColor); + defaultColor = defaultColor.EmptyNull(); + var isDefault = color.IsCaseInsensitiveEqual(defaultColor); sb.Append("
              ".FormatInvariant(defaultColor)); @@ -650,293 +665,353 @@ public static MvcHtmlString ColorBox(this HtmlHelper html, string name, string c sb.Append("
              "); + return MvcHtmlString.Create(sb.ToStringAndReturn()); + } + + public static MvcHtmlString TableFormattedVariantAttributes(this HtmlHelper helper, string formattedVariantAttributes, string separatorLines = "
              ", string separatorValues = ": ") + { + var sb = new StringBuilder(); + string name, value; + string[] lines = formattedVariantAttributes.SplitSafe(separatorLines); + + if (lines.Length <= 0) + return MvcHtmlString.Empty; + + sb.Append(""); + + foreach (string line in lines) + { + sb.Append(""); + if (line.SplitToPair(out name, out value, separatorValues)) + { + sb.AppendFormat("", name); + sb.AppendFormat("", value); + } + else + { + sb.AppendFormat("", line); + } + sb.Append(""); + } + + sb.Append("
              {0}:{0}{0}
              "); return MvcHtmlString.Create(sb.ToString()); } - public static MvcHtmlString TableFormattedVariantAttributes(this HtmlHelper helper, string formattedVariantAttributes, string separatorLines = "
              ", string separatorValues = ": ") { - var sb = new StringBuilder(); - string name, value; - string[] lines = formattedVariantAttributes.SplitSafe(separatorLines); - - if (lines.Length <= 0) - return MvcHtmlString.Empty; - - sb.Append(""); - - foreach (string line in lines) { - sb.Append(""); - if (line.SplitToPair(out name, out value, separatorValues)) { - sb.AppendFormat("", name); - sb.AppendFormat("", value); - } - else { - sb.AppendFormat("", line); - } - sb.Append(""); - } - - sb.Append("
              {0}:{0}{0}
              "); - return MvcHtmlString.Create(sb.ToString()); - } - - //public static MvcHtmlString SettingEditorFor( - // this HtmlHelper helper, - // Expression> expression, - // string parentSelector = null, - // object additionalViewData = null) - //{ - // var editor = helper.EditorFor(expression, additionalViewData); - - // var data = helper.ViewData[StoreDependingSettingHelper.ViewDataKey] as StoreDependingSettingData; - // if (data == null || data.ActiveStoreScopeConfiguration <= 0) - // return editor; // CONTROL - - // var sb = new StringBuilder("
              "); - // sb.Append("
              "); - // sb.Append(helper.SettingOverrideCheckboxInternal(expression, data, parentSelector)); // CHECK - // sb.Append("
              "); - // sb.Append("
              "); - // sb.Append(editor); // CONTROL - // sb.Append("
              "); - - // return MvcHtmlString.Create(sb.ToString()); - //} - - public static MvcHtmlString SettingEditorFor( - this HtmlHelper helper, - Expression> expression, - string parentSelector = null, - object additionalViewData = null) - { - return SettingEditorFor( - helper, - expression, - helper.EditorFor(expression, additionalViewData), - parentSelector); - } - - public static MvcHtmlString SettingEditorFor( - this HtmlHelper helper, - Expression> expression, - Func editor, - string parentSelector = null) - { - return SettingEditorFor( - helper, - expression, - new MvcHtmlString(editor(helper.ViewData.Model).ToHtmlString()), - parentSelector); - } - - public static MvcHtmlString EnumSettingEditorFor( - this HtmlHelper helper, - Expression> expression, - string parentSelector = null, - object htmlAttributes = null, - string optionLabel = null) where TValue : struct - { - return SettingEditorFor( - helper, - expression, - helper.DropDownListForEnum(expression, htmlAttributes, optionLabel), - parentSelector); - } - - public static MvcHtmlString SettingEditorFor( - this HtmlHelper helper, - Expression> expression, - MvcHtmlString editor, - string parentSelector = null) - { - Guard.NotNull(expression, nameof(expression)); - Guard.NotNull(editor, nameof(editor)); - - var data = helper.ViewData[StoreDependingSettingHelper.ViewDataKey] as StoreDependingSettingData; - if (data == null || data.ActiveStoreScopeConfiguration <= 0) - return editor; // CONTROL - - var sb = new StringBuilder("
              "); - sb.Append("
              "); - sb.Append(helper.SettingOverrideCheckboxInternal(expression, data, parentSelector)); // CHECK - sb.Append("
              "); - sb.Append("
              "); - sb.Append(editor.ToHtmlString()); // CONTROL - sb.Append("
              "); - - return MvcHtmlString.Create(sb.ToString()); - } - - private static MvcHtmlString SettingOverrideCheckboxInternal( - this HtmlHelper helper, - Expression> expression, - StoreDependingSettingData data, - string parentSelector = null) - { - var fieldPrefix = helper.ViewData.TemplateInfo.HtmlFieldPrefix; - var settingKey = ExpressionHelper.GetExpressionText(expression); - var localizeService = EngineContext.Current.Resolve(); - - if (fieldPrefix.HasValue()) - settingKey = string.Concat(fieldPrefix, ".", settingKey); - else if (!settingKey.Contains(".")) - settingKey = string.Concat(data.RootSettingClass, ".", settingKey); - - var overrideForStore = (data.OverrideSettingKeys.FirstOrDefault(x => x.IsCaseInsensitiveEqual(settingKey)) != null); - var fieldId = settingKey + (settingKey.EndsWith("_OverrideForStore") ? "" : "_OverrideForStore"); - - var sb = new StringBuilder(); - sb.Append(""); - // Controls are not floating, so line-break prevents different distances between them. - sb.Append("\r\n"); - - return MvcHtmlString.Create(sb.ToString()); - } - - public static MvcHtmlString CollapsedText(this HtmlHelper helper, string text) - { - if (text.IsEmpty()) - return MvcHtmlString.Empty; - - var catalogSettings = EngineContext.Current.Resolve(); - - if (!catalogSettings.EnableHtmlTextCollapser) - return MvcHtmlString.Create(text); - - string result = "
              {1}
              ".FormatWith( - catalogSettings.HtmlTextCollapsedHeight, text - ); - - return MvcHtmlString.Create(result); - } - - public static MvcHtmlString IconForFileExtension(this HtmlHelper helper, string fileExtension, bool renderLabel = false) - { - return IconForFileExtension(helper, fileExtension, null, renderLabel); - } - - public static MvcHtmlString IconForFileExtension(this HtmlHelper helper, string fileExtension, string extraCssClasses = null, bool renderLabel = false) - { - Guard.NotNull(helper, nameof(helper)); - Guard.NotEmpty(fileExtension, nameof(fileExtension)); - - var icon = "far fa-file"; - var ext = fileExtension; - - if (ext != null && ext.StartsWith(".")) - { - ext = ext.Substring(1); - } - - if (ext.HasValue()) - { - switch (ext.ToLowerInvariant()) - { - case "pdf": - icon = "far fa-file-pdf"; - break; - case "doc": - case "docx": - case "docm": - case "odt": - case "dot": - case "dotx": - case "dotm": - icon = "far fa-file-word"; - break; - case "xls": - case "xlsx": - case "xlsm": - case "xlsb": - case "ods": - icon = "far fa-file-excel"; - break; - case "csv": - case "tab": - icon = "fa fa-file-csv"; - break; - case "ppt": - case "pptx": - case "pptm": - case "ppsx": - case "odp": - case "potx": - case "pot": - case "potm": - case "pps": - case "ppsm": - icon = "far fa-file-powerpoint"; - break; - case "zip": - case "rar": - case "7z": - icon = "far fa-file-archive"; - break; - case "png": - case "jpg": - case "jpeg": - case "bmp": - case "psd": - icon = "far fa-file-image"; - break; - case "mp3": - case "wav": - case "ogg": - case "wma": - icon = "far fa-file-audio"; - break; - case "mp4": - case "mkv": - case "wmv": - case "avi": - case "asf": - case "mpg": - case "mpeg": - icon = "far fa-file-video"; - break; - case "txt": - icon = "far fa-file-alt"; - break; - case "exe": - icon = "fa fa-cog"; - break; - case "xml": - case "html": - case "htm": - icon = "far fa-file-code"; - break; - } - } - - var label = ext.NaIfEmpty().ToUpper(); - - var result = "".FormatInvariant( - icon, - extraCssClasses.HasValue() ? " " + extraCssClasses : "", - label); - - if (renderLabel) - { - if (ext.IsEmpty()) - { - result = "{0}".FormatInvariant("".NaIfEmpty()); - } - else - { - result = result + "{0}".FormatInvariant(label); - } - } - - return MvcHtmlString.Create(result); - } - } + public static MvcHtmlString SettingEditorFor( + this HtmlHelper helper, + Expression> expression, + string parentSelector = null, + object additionalViewData = null) + { + return SettingEditorFor( + helper, + expression, + helper.EditorFor(expression, additionalViewData), + parentSelector); + } + + public static MvcHtmlString SettingEditorFor( + this HtmlHelper helper, + Expression> expression, + Func editor, + string parentSelector = null) + { + return SettingEditorFor( + helper, + expression, + new MvcHtmlString(editor(helper.ViewData.Model).ToHtmlString()), + parentSelector); + } + + public static MvcHtmlString EnumSettingEditorFor( + this HtmlHelper helper, + Expression> expression, + string parentSelector = null, + object htmlAttributes = null, + string optionLabel = null) where TValue : struct + { + return SettingEditorFor( + helper, + expression, + helper.DropDownListForEnum(expression, htmlAttributes, optionLabel), + parentSelector); + } + + public static MvcHtmlString SettingEditorFor( + this HtmlHelper helper, + Expression> expression, + MvcHtmlString editor, + string parentSelector = null) + { + Guard.NotNull(expression, nameof(expression)); + Guard.NotNull(editor, nameof(editor)); + + var data = helper.ViewData[StoreDependingSettingHelper.ViewDataKey] as StoreDependingSettingData; + if (data == null || data.ActiveStoreScopeConfiguration <= 0) + return editor; // CONTROL + + var sb = PooledStringBuilder.Rent("
              "); + sb.Append("
              "); + sb.Append(SettingOverrideCheckboxInternal(helper, expression, data, parentSelector)); // CHECK + sb.Append("
              "); + sb.Append("
              "); + sb.Append(editor.ToHtmlString()); // CONTROL + sb.Append("
              "); + + return MvcHtmlString.Create(sb.ToStringAndReturn()); + } + + private static MvcHtmlString SettingOverrideCheckboxInternal( + HtmlHelper helper, + Expression> expression, + StoreDependingSettingData data, + string parentSelector = null) + { + var fieldPrefix = helper.ViewData.TemplateInfo.HtmlFieldPrefix; + var settingKey = ExpressionHelper.GetExpressionText(expression); + var localizeService = EngineContext.Current.Resolve(); + + if (fieldPrefix.HasValue()) + { + settingKey = fieldPrefix + "." + settingKey; + } + else if (data.RootSettingClass.HasValue() && !settingKey.StartsWith(data.RootSettingClass + ".", StringComparison.OrdinalIgnoreCase)) + { + settingKey = data.RootSettingClass + "." + settingKey; + } + + var overrideForStore = data.OverrideSettingKeys.Contains(settingKey); + var fieldId = settingKey.EnsureEndsWith("_OverrideForStore"); + + var sb = PooledStringBuilder.Rent(); + sb.Append(""); + // Controls are not floating, so line-break prevents different distances between them. + sb.Append("\r\n"); + + return MvcHtmlString.Create(sb.ToStringAndReturn()); + } + + public static MvcHtmlString CollapsedText(this HtmlHelper helper, string text) + { + if (text.IsEmpty()) + return MvcHtmlString.Empty; + + var catalogSettings = EngineContext.Current.Resolve(); + + if (!catalogSettings.EnableHtmlTextCollapser) + return MvcHtmlString.Create(text); + + string result = "
              {1}
              ".FormatWith( + catalogSettings.HtmlTextCollapsedHeight, text + ); + + return MvcHtmlString.Create(result); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MvcHtmlString IconForFileExtension(this HtmlHelper helper, string fileExtension, bool renderLabel = false) + { + return IconForFileExtension(helper, fileExtension, null, renderLabel); + } + + public static MvcHtmlString IconForFileExtension(this HtmlHelper helper, string fileExtension, string extraCssClasses = null, bool renderLabel = false) + { + Guard.NotNull(helper, nameof(helper)); + Guard.NotEmpty(fileExtension, nameof(fileExtension)); + + var icon = "far fa-file"; + var ext = fileExtension; + + if (ext != null && ext.StartsWith(".")) + { + ext = ext.Substring(1); + } + + if (ext.HasValue()) + { + switch (ext.ToLowerInvariant()) + { + case "pdf": + icon = "far fa-file-pdf"; + break; + case "doc": + case "docx": + case "docm": + case "odt": + case "dot": + case "dotx": + case "dotm": + icon = "far fa-file-word"; + break; + case "xls": + case "xlsx": + case "xlsm": + case "xlsb": + case "ods": + icon = "far fa-file-excel"; + break; + case "csv": + case "tab": + icon = "fa fa-file-csv"; + break; + case "ppt": + case "pptx": + case "pptm": + case "ppsx": + case "odp": + case "potx": + case "pot": + case "potm": + case "pps": + case "ppsm": + icon = "far fa-file-powerpoint"; + break; + case "zip": + case "rar": + case "7z": + icon = "far fa-file-archive"; + break; + case "png": + case "jpg": + case "jpeg": + case "bmp": + case "psd": + icon = "far fa-file-image"; + break; + case "mp3": + case "wav": + case "ogg": + case "wma": + icon = "far fa-file-audio"; + break; + case "mp4": + case "mkv": + case "wmv": + case "avi": + case "asf": + case "mpg": + case "mpeg": + icon = "far fa-file-video"; + break; + case "txt": + icon = "far fa-file-alt"; + break; + case "exe": + icon = "fa fa-cog"; + break; + case "xml": + case "html": + case "htm": + icon = "far fa-file-code"; + break; + } + } + + var label = ext.NaIfEmpty().ToUpper(); + + var result = "".FormatInvariant( + icon, + extraCssClasses.HasValue() ? " " + extraCssClasses : "", + label); + + if (renderLabel) + { + if (ext.IsEmpty()) + { + result = "{0}".FormatInvariant("".NaIfEmpty()); + } + else + { + result = result + "{0}".FormatInvariant(label); + } + } + + return MvcHtmlString.Create(result); + } + + #region Media + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MvcHtmlString MediaViewer(this HtmlHelper helper, MediaFileInfo file, object htmlAttributes = null) + { + return MediaInternal(helper, file, true, 0, CommonHelper.ObjectToDictionary(htmlAttributes)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MvcHtmlString MediaViewer(this HtmlHelper helper, MediaFileInfo file, IDictionary htmlAttributes) + { + return MediaInternal(helper, file, true, 0, htmlAttributes); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MvcHtmlString MediaThumbnail(this HtmlHelper helper, MediaFileInfo file, int size, object htmlAttributes = null) + { + return MediaInternal(helper, file, false, size, CommonHelper.ObjectToDictionary(htmlAttributes)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MvcHtmlString MediaThumbnail(this HtmlHelper helper, MediaFileInfo file, int size, IDictionary htmlAttributes) + { + return MediaInternal(helper, file, false, size, htmlAttributes); + } + + private static MvcHtmlString MediaInternal( + HtmlHelper helper, + MediaFileInfo file, + bool renderViewer, + int size, + IDictionary htmlAttributes) + { + if (file?.File == null) + { + return MvcHtmlString.Empty; + } + + var mediaType = file.MediaType.NullEmpty() ?? "image"; + + // Validate size parameter. + if (mediaType != "image" && !renderViewer) + { + Guard.IsPositive(size, nameof(size), $"The size must be greater than 0 to get a thumbnail for type '{mediaType.NaIfEmpty()}'."); + } + + var f = file?.File; + + var model = new MediaTemplateModel(file, renderViewer) + { + ThumbSize = size, + HtmlAttributes = htmlAttributes + }; + + if (htmlAttributes.TryGetValue("alt", out var alt)) + { + htmlAttributes.Remove("alt"); + } + + if (htmlAttributes.TryGetValue("title", out var title)) + { + htmlAttributes.Remove("title"); + } + + //alt != null ? (string)alt : f?.GetLocalized(x => x.Alt).Value + + model.Alt = (string)(alt ?? f?.GetLocalized(x => x.Alt).Value); + model.Title = (string)(title ?? f?.GetLocalized(x => x.Title).Value); + + return helper.Partial("MediaTemplates/" + mediaType, model); + } + + #endregion + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlPrefixScopeExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlPrefixScopeExtensions.cs new file mode 100644 index 0000000000..86ddff19a9 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlPrefixScopeExtensions.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Web; +using System.Web.Mvc; +using SmartStore.Utilities; + +namespace SmartStore.Web.Framework +{ + public static class HtmlPrefixScopeExtensions + { + private const string IdsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_"; + + public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName) + { + return BeginCollectionItem(html, collectionName, html.ViewContext.Writer); + } + + public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName, TextWriter writer) + { + var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName); + var itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : CommonHelper.GenerateRandomDigitCode(10); + + // autocomplete="off" is needed to work around a very annoying Chrome behaviour + // whereby it reuses old values after the user clicks "Back", which causes the + // xyz.index and xyz[...] values to get out of sync. + writer.WriteLine( + "", + collectionName, html.Encode(itemIndex)); + + return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex)); + } + + public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix) + { + return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix); + } + + private static Queue GetIdsToReuse(HttpContextBase httpContext, string collectionName) + { + // We need to use the same sequence of IDs following a server-side validation failure, + // otherwise the framework won't render the validation error messages next to each item. + var key = IdsToReuseKey + collectionName; + var queue = (Queue)httpContext.Items[key]; + if (queue == null) + { + httpContext.Items[key] = queue = new Queue(); + var previouslyUsedIds = httpContext.Request[collectionName + ".index"]; + + if (!string.IsNullOrEmpty(previouslyUsedIds)) + { + foreach (var previouslyUsedId in previouslyUsedIds.Split(',')) + { + queue.Enqueue(previouslyUsedId); + } + } + } + return queue; + } + + internal class HtmlFieldPrefixScope : IDisposable + { + internal readonly TemplateInfo TemplateInfo; + internal readonly string PreviousHtmlFieldPrefix; + + public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix) + { + TemplateInfo = templateInfo; + + PreviousHtmlFieldPrefix = TemplateInfo.HtmlFieldPrefix; + TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix; + } + + public void Dispose() + { + TemplateInfo.HtmlFieldPrefix = PreviousHtmlFieldPrefix; + } + } + } +} \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlSelectListExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlSelectListExtensions.cs index 6644ac91bc..6d042beca7 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlSelectListExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlSelectListExtensions.cs @@ -13,143 +13,143 @@ namespace SmartStore.Web.Framework { - public static class HtmlSelectListExtensions - { - public static SelectList ToSelectList(this TEnum enumObj, bool markCurrentAsSelected = true) where TEnum : struct - { - if (!typeof(TEnum).IsEnum) - throw new ArgumentException("An Enumeration type is required.", "enumObj"); - - var localizationService = EngineContext.Current.Resolve(); - var workContext = EngineContext.Current.Resolve(); - - var values = from TEnum enumValue in Enum.GetValues(typeof(TEnum)) - select new { ID = Convert.ToInt32(enumValue), Name = enumValue.GetLocalizedEnum(localizationService, workContext) }; - - object selectedValue = null; - if (markCurrentAsSelected) - selectedValue = Convert.ToInt32(enumObj); - - return new SelectList(values, "ID", "Name", selectedValue); - } - - /// - /// Get a select list of all stores - /// - public static IList ToSelectListItems(this IEnumerable stores, params int[] selectedStoreIds) - { - var list = new List(); - - foreach (var store in stores) - { - list.Add(new SelectListItem - { - Text = store.Name, - Value = store.Id.ToString(), - Selected = selectedStoreIds != null && selectedStoreIds.Contains(store.Id) - }); - } - - return list; - } - - /// - /// Get a select list of all customer roles - /// - public static IList ToSelectListItems(this IEnumerable roles, params int[] selectedCustomerRoleIds) - { - var list = new List(); - - foreach (var role in roles) - { - list.Add(new SelectListItem - { - Text = role.Name, - Value = role.Id.ToString(), - Selected = selectedCustomerRoleIds != null && selectedCustomerRoleIds.Contains(role.Id) - }); - } - - return list; - } - - /// - /// Get a select list of all payment methods. - /// - /// Payment providers. - /// Plugin mediator. - /// System name of selected methods. - /// List of select items - public static IList ToSelectListItems( - this IEnumerable> paymentProviders, - PluginMediator pluginMediator, - params string[] selectedMethods) - { - var list = new List(); - - foreach (var provider in paymentProviders) - { - var systemName = provider.Metadata.SystemName; - var name = pluginMediator.GetLocalizedFriendlyName(provider.Metadata); - - if (name.IsEmpty()) - { - name = provider.Metadata.FriendlyName; - } - - if (name.IsEmpty()) - { - name = provider.Metadata.SystemName; - } - - var item = new ExtendedSelectListItem - { - Text = name.NaIfEmpty(), - Value = systemName, - Selected = selectedMethods != null && selectedMethods.Contains(systemName) - }; - - item.CustomProperties.Add("hint", systemName.Replace("SmartStore.", "").Replace("Payments.", "")); - - list.Add(item); - } - - return list.OrderBy(x => x.Text).ToList(); - } - - public static void SelectValue(this List lst, string value, string defaultValue = null) - { - if (lst != null) - { - var itm = lst.FirstOrDefault(i => i.Value.IsCaseInsensitiveEqual(value)); - - if (itm == null && defaultValue != null) - itm = lst.FirstOrDefault(i => i.Value.IsCaseInsensitiveEqual(defaultValue)); - - if (itm != null) - itm.Selected = true; - } - } - } - - - public partial class ExtendedSelectListItem : SelectListItem - { - public ExtendedSelectListItem() - { - CustomProperties = new Dictionary(); - } - - public Dictionary CustomProperties { get; set; } - - public TProperty Get(string key, TProperty defaultValue = default(TProperty)) - { - if (CustomProperties.TryGetValue(key, out object value)) - { - return (TProperty)value; - } - - return defaultValue; - } - } + public static class HtmlSelectListExtensions + { + public static SelectList ToSelectList(this TEnum enumObj, bool markCurrentAsSelected = true) where TEnum : struct + { + if (!typeof(TEnum).IsEnum) + throw new ArgumentException("An Enumeration type is required.", "enumObj"); + + var localizationService = EngineContext.Current.Resolve(); + var workContext = EngineContext.Current.Resolve(); + + var values = from TEnum enumValue in Enum.GetValues(typeof(TEnum)) + select new { ID = Convert.ToInt32(enumValue), Name = enumValue.GetLocalizedEnum(localizationService, workContext) }; + + object selectedValue = null; + if (markCurrentAsSelected) + selectedValue = Convert.ToInt32(enumObj); + + return new SelectList(values, "ID", "Name", selectedValue); + } + + /// + /// Get a select list of all stores + /// + public static IList ToSelectListItems(this IEnumerable stores, params int[] selectedStoreIds) + { + var list = new List(); + + foreach (var store in stores) + { + list.Add(new SelectListItem + { + Text = store.Name, + Value = store.Id.ToString(), + Selected = selectedStoreIds != null && selectedStoreIds.Contains(store.Id) + }); + } + + return list; + } + + /// + /// Get a select list of all customer roles + /// + public static IList ToSelectListItems(this IEnumerable roles, params int[] selectedCustomerRoleIds) + { + var list = new List(); + + foreach (var role in roles) + { + list.Add(new SelectListItem + { + Text = role.Name, + Value = role.Id.ToString(), + Selected = selectedCustomerRoleIds != null && selectedCustomerRoleIds.Contains(role.Id) + }); + } + + return list; + } + + /// + /// Get a select list of all payment methods. + /// + /// Payment providers. + /// Plugin mediator. + /// System name of selected methods. + /// List of select items + public static IList ToSelectListItems( + this IEnumerable> paymentProviders, + PluginMediator pluginMediator, + params string[] selectedMethods) + { + var list = new List(); + + foreach (var provider in paymentProviders) + { + var systemName = provider.Metadata.SystemName; + var name = pluginMediator.GetLocalizedFriendlyName(provider.Metadata); + + if (name.IsEmpty()) + { + name = provider.Metadata.FriendlyName; + } + + if (name.IsEmpty()) + { + name = provider.Metadata.SystemName; + } + + var item = new ExtendedSelectListItem + { + Text = name.NaIfEmpty(), + Value = systemName, + Selected = selectedMethods != null && selectedMethods.Contains(systemName) + }; + + item.CustomProperties.Add("hint", systemName.Replace("SmartStore.", "").Replace("Payments.", "")); + + list.Add(item); + } + + return list.OrderBy(x => x.Text).ToList(); + } + + public static void SelectValue(this List lst, string value, string defaultValue = null) + { + if (lst != null) + { + var itm = lst.FirstOrDefault(i => i.Value.IsCaseInsensitiveEqual(value)); + + if (itm == null && defaultValue != null) + itm = lst.FirstOrDefault(i => i.Value.IsCaseInsensitiveEqual(defaultValue)); + + if (itm != null) + itm.Selected = true; + } + } + } + + + public partial class ExtendedSelectListItem : SelectListItem + { + public ExtendedSelectListItem() + { + CustomProperties = new Dictionary(); + } + + public Dictionary CustomProperties { get; set; } + + public TProperty Get(string key, TProperty defaultValue = default(TProperty)) + { + if (CustomProperties.TryGetValue(key, out object value)) + { + return (TProperty)value; + } + + return defaultValue; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlZoneExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlZoneExtensions.cs index fd9b6f3717..9677aa41a9 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlZoneExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/HtmlZoneExtensions.cs @@ -1,147 +1,139 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Web; using System.Web.Mvc; using SmartStore.Collections; -using SmartStore.Utilities; namespace SmartStore.Web.Framework { - public enum ZoneInjectMode - { - Append, - Prepend, - Replace - } - - public static class HtmlZoneExtensions - { - private class DocumentZone : IDisposable - { - internal const string ItemsKey = "DocumentTail.Snippets"; - private const string UniqueKeysKey = "DocumentTail.UniqueKeys"; - - private readonly ViewContext _viewContext; - private readonly TextWriter _originalViewContextWriter; - 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) - { - Guard.NotEmpty(targetZone, nameof(targetZone)); - - _viewContext = html.ViewContext; - _originalViewContextWriter = _viewContext.Writer; - _page = (WebViewPage)html.ViewDataContainer; - - var writer = new StringWriter(); - _page.OutputStack.Push(writer); - _viewContext.Writer = writer; - - _targetZone = targetZone; - _injectMode = injectMode; - - if (key.HasValue()) - { - if (HasUniqueKey(key)) - { - _isVoid = true; - } - else - { - UniqueKeys.Add(key); - } - } - - if (_page.Request.IsAjaxRequest()) - { - _isVoid = true; - } - } - - public static IEnumerable GetZoneContent(string zone) - { - Guard.NotEmpty(zone, nameof(zone)); - - var storage = HttpContext.Current?.GetItem>(ItemsKey, forceCreation: false); - return storage?[zone]; - } - - public static bool HasUniqueKey(string key) - { - Guard.NotEmpty(key, nameof(key)); - - var uniqueKeys = HttpContext.Current?.GetItem>(UniqueKeysKey, forceCreation: false); - return uniqueKeys != null && uniqueKeys.Contains(key); - } - - private static HashSet UniqueKeys - { - get - { - return HttpContext.Current?.GetItem>(UniqueKeysKey, () => new HashSet(StringComparer.OrdinalIgnoreCase), true); - } - } - - private Multimap GetStorage() - { - return _page.ViewContext?.HttpContext?.GetItem>(ItemsKey, () => new Multimap(StringComparer.OrdinalIgnoreCase)); - } - - public void Dispose() - { - var storage = GetStorage(); - if (storage == null) - return; - - var writer = ((StringWriter)_page.OutputStack.Pop()); - var content = _isVoid ? string.Empty : writer.ToString(); - - _viewContext.Writer = _originalViewContextWriter; - - if (_injectMode == ZoneInjectMode.Append) - { - storage[_targetZone].Add(content); - } - else if (_injectMode == ZoneInjectMode.Prepend) - { - var list = new List(storage[_targetZone]); - list.Insert(0, content); - storage.RemoveAll(_targetZone); - storage[_targetZone].AddRange(list); - } - else // Replace - { - storage.RemoveAll(_targetZone); - storage[_targetZone].Add(content); - } - } - } - - public static IDisposable BeginZoneContent(this HtmlHelper helper, - string targetZone, - ZoneInjectMode injectMode = ZoneInjectMode.Append, - string key = null) - { - return new DocumentZone(helper, targetZone, injectMode, key); - } - - public static void RenderZone(this HtmlHelper helper, string zone) - { - var snippets = DocumentZone.GetZoneContent(zone); - if (snippets == null) - { - return; - } - - foreach (var snippet in snippets) - { - helper.ViewContext.Writer.WriteLine(snippet); - } - } - } + public enum ZoneInjectMode + { + Append, + Prepend, + Replace + } + + public static class HtmlZoneExtensions + { + private class DocumentZone : IDisposable + { + internal const string ItemsKey = "DocumentTail.Snippets"; + private const string UniqueKeysKey = "DocumentTail.UniqueKeys"; + + private readonly ViewContext _viewContext; + private readonly TextWriter _originalViewContextWriter; + 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) + { + Guard.NotEmpty(targetZone, nameof(targetZone)); + + _viewContext = html.ViewContext; + _originalViewContextWriter = _viewContext.Writer; + _page = (WebViewPage)html.ViewDataContainer; + + var writer = new StringWriter(); + _page.OutputStack.Push(writer); + _viewContext.Writer = writer; + + _targetZone = targetZone; + _injectMode = injectMode; + + if (key.HasValue()) + { + if (HasUniqueKey(key)) + { + _isVoid = true; + } + else + { + UniqueKeys.Add(key); + } + } + + if (_page.Request.IsAjaxRequest()) + { + _isVoid = true; + } + } + + public static IEnumerable GetZoneContent(string zone) + { + Guard.NotEmpty(zone, nameof(zone)); + + var storage = HttpContext.Current?.GetItem>(ItemsKey, forceCreation: false); + return storage?[zone]; + } + + public static bool HasUniqueKey(string key) + { + Guard.NotEmpty(key, nameof(key)); + + var uniqueKeys = HttpContext.Current?.GetItem>(UniqueKeysKey, forceCreation: false); + return uniqueKeys != null && uniqueKeys.Contains(key); + } + + private static HashSet UniqueKeys => HttpContext.Current?.GetItem>(UniqueKeysKey, () => new HashSet(StringComparer.OrdinalIgnoreCase), true); + + private Multimap GetStorage() + { + return _page.ViewContext?.HttpContext?.GetItem>(ItemsKey, () => new Multimap(StringComparer.OrdinalIgnoreCase)); + } + + public void Dispose() + { + var storage = GetStorage(); + if (storage == null) + return; + + var writer = ((StringWriter)_page.OutputStack.Pop()); + var content = _isVoid ? string.Empty : writer.ToString(); + + _viewContext.Writer = _originalViewContextWriter; + + if (_injectMode == ZoneInjectMode.Append) + { + storage[_targetZone].Add(content); + } + else if (_injectMode == ZoneInjectMode.Prepend) + { + var list = new List(storage[_targetZone]); + list.Insert(0, content); + storage.RemoveAll(_targetZone); + storage[_targetZone].AddRange(list); + } + else // Replace + { + storage.RemoveAll(_targetZone); + storage[_targetZone].Add(content); + } + } + } + + public static IDisposable BeginZoneContent(this HtmlHelper helper, + string targetZone, + ZoneInjectMode injectMode = ZoneInjectMode.Append, + string key = null) + { + return new DocumentZone(helper, targetZone, injectMode, key); + } + + public static void RenderZone(this HtmlHelper helper, string zone) + { + var snippets = DocumentZone.GetZoneContent(zone); + if (snippets == null) + { + return; + } + + foreach (var snippet in snippets) + { + helper.ViewContext.Writer.WriteLine(snippet); + } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/HttpExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/HttpExtensions.cs index 56dba19bee..19d0128b72 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/HttpExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/HttpExtensions.cs @@ -4,127 +4,129 @@ using System.Web; using System.Web.Mvc; using System.Web.Routing; +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Infrastructure; using SmartStore.Services.Orders; using SmartStore.Utilities; namespace SmartStore { - public static class HttpExtensions - { - public static bool IsAdminArea(this HttpRequest request) - { - if (request != null) - { - return IsAdminArea(new HttpRequestWrapper(request)); - } - - return false; - } - - public static bool IsAdminArea(this HttpRequestBase request) - { - try - { - // TODO: not really reliable. Change this. - var area = request?.RequestContext?.RouteData?.GetAreaName(); - if (area != null) - { - return area.IsCaseInsensitiveEqual("admin"); - } - else - { - var paths = new[] { "~/administration/", "~/admin/" }; - return paths.Any(x => request?.AppRelativeCurrentExecutionFilePath?.StartsWith(x, StringComparison.OrdinalIgnoreCase) == true); - } - } - catch - { - return false; - } - } - - public static bool IsPublicArea(this HttpRequest request) - { - if (request != null) - { - return IsPublicArea(new HttpRequestWrapper(request)); - } - - return false; - } - - public static bool IsPublicArea(this HttpRequestBase request) - { - try - { - // TODO: not really reliable. Change this. - var area = request?.RequestContext?.RouteData?.GetAreaName(); - return area.IsEmpty(); - } - catch - { - return false; - } - } - - public static PostedFileResult ToPostedFileResult(this HttpRequestBase httpRequest) - { - if (httpRequest != null && httpRequest.Files.Count > 0) - { - return httpRequest.Files[0].ToPostedFileResult(); - } - - return null; - } - - public static PostedFileResult ToPostedFileResult(this HttpPostedFile httpFile) - { - if (httpFile != null && httpFile.ContentLength > 0) - { - return new PostedFileResult(new HttpPostedFileWrapper(httpFile)); - } - - return null; - } - - public static PostedFileResult ToPostedFileResult(this HttpPostedFileBase httpFile) - { - if (httpFile != null && httpFile.ContentLength > 0) - { - return new PostedFileResult(httpFile); - } - - return null; - } - - public static IEnumerable ToPostedFileResults(this HttpFileCollection httpFileCollection) - { - if (httpFileCollection != null && httpFileCollection.Count > 0) - { - return new HttpFileCollectionWrapper(httpFileCollection).ToPostedFileResults(); - } - - return Enumerable.Empty(); - } - - public static IEnumerable ToPostedFileResults(this HttpFileCollectionBase httpFileCollection) - { - if (httpFileCollection == null) - yield break; - - var batchId = Guid.NewGuid(); - - for (var i = 0; i < httpFileCollection.Count; i++) - { - var httpFile = httpFileCollection[i]; - var result = httpFile.ToPostedFileResult(); - if (result != null) - { - result.BatchId = batchId; - yield return result; - } - } - } + public static class HttpExtensions + { + public static bool IsAdminArea(this HttpRequest request) + { + if (request != null) + { + return IsAdminArea(new HttpRequestWrapper(request)); + } + + return false; + } + + public static bool IsAdminArea(this HttpRequestBase request) + { + try + { + // TODO: not really reliable. Change this. + var area = request?.RequestContext?.RouteData?.GetAreaName(); + if (area != null) + { + return area.IsCaseInsensitiveEqual("admin"); + } + else + { + var paths = new[] { "~/administration/", "~/admin/" }; + return paths.Any(x => request?.AppRelativeCurrentExecutionFilePath?.StartsWith(x, StringComparison.OrdinalIgnoreCase) == true); + } + } + catch + { + return false; + } + } + + public static bool IsPublicArea(this HttpRequest request) + { + if (request != null) + { + return IsPublicArea(new HttpRequestWrapper(request)); + } + + return false; + } + + public static bool IsPublicArea(this HttpRequestBase request) + { + try + { + // TODO: not really reliable. Change this. + var area = request?.RequestContext?.RouteData?.GetAreaName(); + return area.IsEmpty(); + } + catch + { + return false; + } + } + + public static PostedFileResult ToPostedFileResult(this HttpRequestBase httpRequest) + { + if (httpRequest != null && httpRequest.Files.Count > 0) + { + return httpRequest.Files[0].ToPostedFileResult(); + } + + return null; + } + + public static PostedFileResult ToPostedFileResult(this HttpPostedFile httpFile) + { + if (httpFile != null && httpFile.ContentLength > 0) + { + return new PostedFileResult(new HttpPostedFileWrapper(httpFile)); + } + + return null; + } + + public static PostedFileResult ToPostedFileResult(this HttpPostedFileBase httpFile) + { + if (httpFile != null && httpFile.ContentLength > 0) + { + return new PostedFileResult(httpFile); + } + + return null; + } + + public static IEnumerable ToPostedFileResults(this HttpFileCollection httpFileCollection) + { + if (httpFileCollection != null && httpFileCollection.Count > 0) + { + return new HttpFileCollectionWrapper(httpFileCollection).ToPostedFileResults(); + } + + return Enumerable.Empty(); + } + + public static IEnumerable ToPostedFileResults(this HttpFileCollectionBase httpFileCollection) + { + if (httpFileCollection == null) + yield break; + + var batchId = Guid.NewGuid(); + + for (var i = 0; i < httpFileCollection.Count; i++) + { + var httpFile = httpFileCollection[i]; + var result = httpFile.ToPostedFileResult(); + if (result != null) + { + result.BatchId = batchId; + yield return result; + } + } + } public static RouteData GetRouteData(this HttpContextBase httpContext) { @@ -144,152 +146,150 @@ public static bool TryGetRouteData(this HttpContextBase httpContext, out RouteDa return routeData != null; } - public static CheckoutState GetCheckoutState(this HttpContextBase httpContext) - { - Guard.NotNull(httpContext, nameof(httpContext)); - - var state = httpContext.Session.SafeGetValue(CheckoutState.CheckoutStateSessionKey); - - if (state != null) - return state; - - state = new CheckoutState(); - httpContext.Session.SafeSet(CheckoutState.CheckoutStateSessionKey, state); - - return state; - } - - public static void RemoveCheckoutState(this HttpContextBase httpContext) - { - Guard.NotNull(httpContext, nameof(httpContext)); - - httpContext.Session.SafeRemove(CheckoutState.CheckoutStateSessionKey); - } - - internal static string GetUserThemeChoiceFromCookie(this HttpContextBase context) - { - if (context == null) - return null; - - var cookie = context.Request.Cookies.Get("sm.UserThemeChoice"); - if (cookie != null) - { - return cookie.Value.NullEmpty(); - } - - return null; - } - - internal static void SetUserThemeChoiceInCookie(this HttpContextBase context, string value) - { - if (context == null) - return; - - var cookie = context.Request.Cookies.Get("sm.UserThemeChoice"); - - if (value.HasValue() && cookie == null) - { - cookie = new HttpCookie("sm.UserThemeChoice") - { - HttpOnly = true, - Expires = DateTime.UtcNow.AddYears(1) - }; - } - - if (value.HasValue()) - { - cookie.Value = value; - context.Request.Cookies.Set(cookie); - } - - if (value.IsEmpty() && cookie != null) - { - cookie.Expires = DateTime.UtcNow.AddYears(-10); - } - - if (cookie != null) - { - context.Response.SetCookie(cookie); - } - } - - internal static HttpCookie GetPreviewModeCookie(this HttpContextBase context, bool createIfMissing) - { - var httpRequest = context.SafeGetHttpRequest(); - var cookie = httpRequest?.Cookies?.Get("sm.PreviewModeOverrides"); - - if (cookie == null && createIfMissing && httpRequest != null) - { - cookie = new HttpCookie("sm.PreviewModeOverrides") { HttpOnly = true }; - httpRequest.Cookies.Set(cookie); - } - - if (cookie != null) - { - // when cookie gets created or touched, extend its lifetime - cookie.Expires = DateTime.UtcNow.AddMinutes(20); - } - - return cookie; - } - - internal static void SetPreviewModeValue(this HttpContextBase context, string key, string value) - { - if (context == null) - return; - - var cookie = context.GetPreviewModeCookie(value.HasValue()); - if (cookie != null) - { - if (value.HasValue()) - { - cookie.Values[key] = value; - } - else - { - cookie.Values.Remove(key); - } - } - } - - public static IDisposable PreviewModeCookie(this HttpContextBase context) - { - var disposable = new ActionDisposable(() => - { - var cookie = GetPreviewModeCookie(context, false); - if (cookie != null) - { - if (!cookie.HasKeys) - { - cookie.Expires = DateTime.UtcNow.AddYears(-10); - } - else - { - cookie.Expires = DateTime.UtcNow.AddMinutes(20); - } - - context.Response.SetCookie(cookie); - } - }); - - return disposable; - } - - public static string GetContentUrl(this HttpContextBase context, string path) - { - if (path.HasValue()) - { - if (!path.StartsWith("~")) - { - if (!path.StartsWith("/")) - path = "/" + path; - path = "~" + path; - } - - return UrlHelper.GenerateContentUrl(path, context); - } - - return path; - } - } + public static CheckoutState GetCheckoutState(this HttpContextBase httpContext) + { + Guard.NotNull(httpContext, nameof(httpContext)); + + var state = httpContext.Session.SafeGetValue(CheckoutState.CheckoutStateSessionKey); + + if (state != null) + return state; + + state = new CheckoutState(); + httpContext.Session.SafeSet(CheckoutState.CheckoutStateSessionKey, state); + + return state; + } + + public static void RemoveCheckoutState(this HttpContextBase httpContext) + { + Guard.NotNull(httpContext, nameof(httpContext)); + + httpContext.Session.SafeRemove(CheckoutState.CheckoutStateSessionKey); + } + + internal static string GetUserThemeChoiceFromCookie(this HttpContextBase context) + { + if (context == null) + return null; + + var cookie = context.Request.Cookies.Get("sm.UserThemeChoice"); + if (cookie != null) + { + return cookie.Value.NullEmpty(); + } + + return null; + } + + internal static void SetUserThemeChoiceInCookie(this HttpContextBase context, string value) + { + if (context?.Request == null) + { + return; + } + + if (value.IsEmpty()) + { + context.Request.Cookies.Remove("sm.UserThemeChoice"); + return; + } + + var settings = EngineContext.Current.Resolve(); + var cookie = context.Request.Cookies.Get("sm.UserThemeChoice") ?? new HttpCookie("sm.UserThemeChoice"); + cookie.Value = value; + cookie.Expires = DateTime.UtcNow.AddYears(1); + cookie.HttpOnly = true; + cookie.Secure = context.Request.IsHttps(); + cookie.SameSite = cookie.Secure ? (SameSiteMode)settings.SameSiteMode : SameSiteMode.Lax; + + context.Request.Cookies.Set(cookie); + } + + internal static HttpCookie GetPreviewModeCookie(this HttpContextBase context, bool createIfMissing) + { + var httpRequest = context.SafeGetHttpRequest(); + var cookie = httpRequest?.Cookies?.Get("sm.PreviewModeOverrides"); + + if (cookie == null && createIfMissing && httpRequest != null) + { + var settings = EngineContext.Current.Resolve(); + var secure = context.Request.IsHttps(); + cookie = new HttpCookie("sm.PreviewModeOverrides") + { + HttpOnly = true, + Secure = secure, + SameSite = secure ? (SameSiteMode)settings.SameSiteMode : SameSiteMode.Lax + }; + httpRequest.Cookies.Set(cookie); + } + + if (cookie != null) + { + // when cookie gets created or touched, extend its lifetime + cookie.Expires = DateTime.UtcNow.AddMinutes(20); + } + + return cookie; + } + + internal static void SetPreviewModeValue(this HttpContextBase context, string key, string value) + { + if (context == null) + return; + + var cookie = context.GetPreviewModeCookie(value.HasValue()); + if (cookie != null) + { + if (value.HasValue()) + { + cookie.Values[key] = value; + } + else + { + cookie.Values.Remove(key); + } + } + } + + public static IDisposable PreviewModeCookie(this HttpContextBase context) + { + var disposable = new ActionDisposable(() => + { + var cookie = GetPreviewModeCookie(context, false); + if (cookie != null) + { + if (!cookie.HasKeys) + { + cookie.Expires = DateTime.UtcNow.AddYears(-10); + } + else + { + cookie.Expires = DateTime.UtcNow.AddMinutes(20); + } + + context.Response.SetCookie(cookie); + } + }); + + return disposable; + } + + public static string GetContentUrl(this HttpContextBase context, string path) + { + if (path.HasValue()) + { + if (!path.StartsWith("~")) + { + if (!path.StartsWith("/")) + path = "/" + path; + path = "~" + path; + } + + return UrlHelper.GenerateContentUrl(path, context); + } + + return path; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/HttpSessionStateExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/HttpSessionStateExtensions.cs index d9fce5aa87..2f77e252da 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/HttpSessionStateExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/HttpSessionStateExtensions.cs @@ -3,51 +3,52 @@ namespace SmartStore { - public static class HttpSessionStateExtensions - { - public static void SafeRemove(this HttpSessionStateBase session, string key) - { - try - { - if (session != null && session[key] != null) - session.Remove(key); - } - catch (Exception exc) - { - exc.Dump(); - } - } + public static class HttpSessionStateExtensions + { + public static void SafeRemove(this HttpSessionStateBase session, string key) + { + try + { + if (session != null && session[key] != null) + session.Remove(key); + } + catch (Exception exc) + { + exc.Dump(); + } + } - public static T SafeGetValue(this HttpSessionStateBase session, string key, T defaultValue = default(T)) - { - try - { - if (session != null && session[key] != null) - return (T)session[key]; - } - catch (Exception exc) - { - exc.Dump(); - } - return defaultValue; - } + public static T SafeGetValue(this HttpSessionStateBase session, string key, T defaultValue = default(T)) + { + try + { + if (session != null && session[key] != null) + return (T)session[key]; + } + catch (Exception exc) + { + exc.Dump(); + } - public static void SafeSet(this HttpSessionStateBase session, string key, object value) - { - try - { - if (session != null) - { - if (value == null) - session.SafeRemove(key); - else - session[key] = value; - } - } - catch (Exception exc) - { - exc.Dump(); - } - } - } + return defaultValue; + } + + public static void SafeSet(this HttpSessionStateBase session, string key, object value) + { + try + { + if (session != null) + { + if (value == null) + session.SafeRemove(key); + else + session[key] = value; + } + } + catch (Exception exc) + { + exc.Dump(); + } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/LocalizedValueExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/LocalizedValueExtensions.cs index ffc474ff87..97be49e86f 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/LocalizedValueExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/LocalizedValueExtensions.cs @@ -1,27 +1,26 @@ -using System; -using SmartStore.Services.Localization; +using SmartStore.Services.Localization; namespace SmartStore.Web.Framework { - /// - /// Wrapper for the most common string extension helpers used in views. - /// Just here to avoid runtime exceptions in views after refactoring GetLocalized() helper. - /// - public static class LocalizedValueExtensions - { - public static bool HasValue(this LocalizedValue value) - { - return value?.Value?.HasValue() == true; - } + /// + /// Wrapper for the most common string extension helpers used in views. + /// Just here to avoid runtime exceptions in views after refactoring GetLocalized() helper. + /// + public static class LocalizedValueExtensions + { + public static bool HasValue(this LocalizedValue value) + { + return value?.Value?.HasValue() == true; + } - public static bool IsEmpty(this LocalizedValue value) - { - return value?.Value?.IsEmpty() == false; - } + public static bool IsEmpty(this LocalizedValue value) + { + return value?.Value?.IsEmpty() == false; + } - public static string Truncate(this LocalizedValue value, int maxLength, string suffix = "") - { - return value?.Value?.Truncate(maxLength, suffix); - } - } + public static string Truncate(this LocalizedValue value, int maxLength, string suffix = "") + { + return value?.Value?.Truncate(maxLength, suffix); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/NameValueCollectionExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/NameValueCollectionExtensions.cs index 87ba980283..e1c7dce05f 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/NameValueCollectionExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/NameValueCollectionExtensions.cs @@ -1,16 +1,16 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Collections.Specialized; +using System.Linq; namespace SmartStore { - public static class NameValueCollectionExtension - { + public static class NameValueCollectionExtension + { public static IDictionary ToDictionary(this NameValueCollection collection) { Guard.NotNull(collection, nameof(collection)); - + var query = from key in collection.AllKeys where key != null select key; @@ -19,5 +19,5 @@ public static IDictionary ToDictionary(this NameValueCollection return query.ToDictionary(key => key, elementSelector); } - } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/PostedFileResult.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/PostedFileResult.cs index 96e62e96d6..cf55ba8490 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/PostedFileResult.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/PostedFileResult.cs @@ -6,176 +6,129 @@ namespace SmartStore { - public class PostedFileResult - { - private static readonly Regex s_ImageTypes = new Regex(@"(.*?)\.(gif|jpg|jpeg|jpe|jfif|pjpeg|pjp|png|tiff|tif|bmp|ico|svg)", RegexOptions.Compiled | RegexOptions.IgnoreCase); - private readonly HttpPostedFileBase _httpFile; - - private string _contentType; - private string _fileName; - private string _fileTitle; - private string _fileExt; - private bool? _isImage; - private byte[] _buffer; - - public PostedFileResult(HttpPostedFileBase httpFile) - { - Guard.NotNull(httpFile, nameof(httpFile)); - - this._httpFile = httpFile; - - this.TimeStamp = DateTime.UtcNow; - this.BatchId = Guid.NewGuid(); - } - - public HttpPostedFileBase File - { - get { return _httpFile; } - } - - public DateTime TimeStamp - { - get; - private set; - } - - public Guid BatchId - { - get; - internal set; - } - - public string FileName - { - get - { - if (_fileName == null) - { - _fileName = Path.GetFileName(_httpFile.FileName); - } - return _fileName; - } - } - - public string FileTitle - { - get - { - if (_fileTitle == null) - { - _fileTitle = Path.GetFileNameWithoutExtension(this.FileName); - } - return _fileTitle; - } - } - - public string FileExtension - { - get - { - if (_fileExt == null) - { - _fileExt = Path.GetExtension(this.FileName).EmptyNull(); - } - return _fileExt; - } - } - - public string ContentType - { - get - { - if (_contentType == null) - { - var contentType = _httpFile.ContentType; - - if (contentType == null && this.FileExtension.HasValue()) - { - contentType = MimeTypes.MapNameToMimeType(this.FileExtension); - - // ContentType is not always available. - // That's why we manually update it here - if (contentType.IsEmpty()) - { - switch (this.FileExtension.ToLowerInvariant()) - { - case ".bmp": - contentType = "image/bmp"; - break; - case ".gif": - contentType = "image/gif"; - break; - case ".jpeg": - case ".jpg": - case ".jpe": - case ".jfif": - case ".pjpeg": - case ".pjp": - contentType = "image/jpeg"; - break; - case ".png": - contentType = "image/png"; - break; - case ".tiff": - case ".tif": - contentType = "image/tiff"; - break; - case ".svg": - contentType = "image/svg+xml"; - break; - case ".ico": - contentType = "image/x-icon"; - break; - default: - break; - } - } - } - - _contentType = contentType.EmptyNull(); - } - return _contentType; - } - } - - public bool IsImage - { - get - { - if (!_isImage.HasValue) - { - _isImage = s_ImageTypes.IsMatch(FileExtension); - } - return _isImage.Value; - } - } - - public int Size - { - get { return _httpFile.ContentLength; } - } - - public Stream Stream - { - get { return _httpFile.InputStream; } - } - - public byte[] Buffer - { - get - { - if (_buffer == null) - { - _buffer = _httpFile.InputStream.ToByteArray(); - } - - return _buffer; - } - } - - public bool FileNameMatches(string pattern) - { - return Regex.IsMatch(_httpFile.FileName, pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline); - } - } + public class PostedFileResult + { + private static readonly Regex s_ImageTypes = new Regex(@"(.*?)\.(gif|jpg|jpeg|jpe|jfif|pjpeg|pjp|png|tiff|tif|bmp|ico|svg)", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private readonly HttpPostedFileBase _httpFile; + + private string _contentType; + private string _fileName; + private string _fileTitle; + private string _fileExt; + private bool? _isImage; + private byte[] _buffer; + + public PostedFileResult(HttpPostedFileBase httpFile) + { + Guard.NotNull(httpFile, nameof(httpFile)); + + this._httpFile = httpFile; + + this.TimeStamp = DateTime.UtcNow; + this.BatchId = Guid.NewGuid(); + } + + public HttpPostedFileBase File => _httpFile; + + public DateTime TimeStamp + { + get; + private set; + } + + public Guid BatchId + { + get; + internal set; + } + + public string FileName + { + get + { + if (_fileName == null) + { + _fileName = Path.GetFileName(_httpFile.FileName); + } + return _fileName; + } + } + + public string FileTitle + { + get + { + if (_fileTitle == null) + { + _fileTitle = Path.GetFileNameWithoutExtension(this.FileName); + } + return _fileTitle; + } + } + + public string FileExtension + { + get + { + if (_fileExt == null) + { + _fileExt = Path.GetExtension(this.FileName).EmptyNull(); + } + return _fileExt; + } + } + + public string ContentType + { + get + { + if (_contentType == null) + { + var contentType = _httpFile.ContentType; + + if (contentType == null && this.FileExtension.HasValue()) + { + contentType = MimeTypes.MapNameToMimeType(this.FileExtension); + } + + _contentType = contentType.EmptyNull(); + } + return _contentType; + } + } + + public bool IsImage + { + get + { + if (!_isImage.HasValue) + { + _isImage = s_ImageTypes.IsMatch(FileExtension); + } + return _isImage.Value; + } + } + + public int Size => _httpFile.ContentLength; + + public Stream Stream => _httpFile.InputStream; + + public byte[] Buffer + { + get + { + if (_buffer == null) + { + _buffer = _httpFile.InputStream.ToByteArray(); + } + + return _buffer; + } + } + + public bool FileNameMatches(string pattern) + { + return Regex.IsMatch(_httpFile.FileName, pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/TagBuilderExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/TagBuilderExtensions.cs new file mode 100644 index 0000000000..17834cb11b --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/TagBuilderExtensions.cs @@ -0,0 +1,40 @@ +using System; +using System.Globalization; +using System.Web.Mvc; + +namespace SmartStore.Web.Framework +{ + public static class TagBuilderExtensions + { + public static void AddCssStyle(this TagBuilder builder, string expression, object value) + { + Guard.NotEmpty(expression, nameof(expression)); + Guard.NotNull(value, nameof(value)); + + var style = expression + ": " + Convert.ToString(value, CultureInfo.InvariantCulture); + + if (builder.Attributes.TryGetValue("style", out var str)) + { + builder.Attributes["style"] = style + "; " + str; + } + else + { + builder.Attributes["style"] = style; + } + } + + public static void AddCssStyles(this TagBuilder builder, string styles) + { + Guard.NotEmpty(styles, nameof(styles)); + + if (builder.Attributes.TryGetValue("style", out var str)) + { + builder.Attributes["style"] = styles.EnsureEndsWith("; ") + str; + } + else + { + builder.Attributes["style"] = styles; + } + } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/TelerikExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/TelerikExtensions.cs index 898e7a2923..dbcebdbc32 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/TelerikExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/TelerikExtensions.cs @@ -10,233 +10,233 @@ namespace SmartStore.Web.Framework { - [Serializable] - public class GridStateInfo - { - public GridStateInfo.GridState State { get; set; } - public string Path { get; set; } - - [Serializable] - public class GridState - { - public string Filter { get; set; } - public string GroupBy { get; set; } - public string OrderBy { get; set; } - public int Page { get; set; } - public int Size { get; set; } - } - } - - public static class TelerikExtensions - { - [SuppressMessage("ReSharper", "Mvc.AreaNotResolved")] - public static GridBuilder PreserveGridState(this GridBuilder builder) where T : class - { - var grid = builder.ToComponent(); - - if (!grid.DataBinding.Ajax.Enabled) - return builder; - - if (grid.Id.IsEmpty()) - throw new SmartException("A grid with preservable state must have a valid Id or Name"); - - var urlHelper = new UrlHelper(grid.ViewContext.RequestContext); - - var gridId = "GridState." + grid.Id + "__" + grid.ViewContext.RouteData.GenerateRouteIdentifier(); - - grid.AppendCssClass("grid-preservestate"); - grid.HtmlAttributes.Add("data-statepreserver-href", urlHelper.Action("SetGridState", "Common", new { area = "admin" })); - grid.HtmlAttributes.Add("data-statepreserver-key", gridId); - - // Try restore state from a previous request - var info = (GridStateInfo)grid.ViewContext.TempData[gridId]; - - if (info == null) - return builder; - - var state = info.State; - var command = GridCommand.Parse(state.Page, state.Size, state.OrderBy, state.GroupBy, state.Filter); - - if (grid.Paging.Enabled) - { - var pathChanged = !info.Path.Equals(grid.ViewContext.HttpContext.Request.RawUrl, StringComparison.OrdinalIgnoreCase); - if (!pathChanged) - { - if (command.PageSize > 0) - grid.Paging.PageSize = command.PageSize; - if (command.Page > 0) - grid.Paging.CurrentPage = command.Page; - } - } - - if (grid.Sorting.Enabled) - { - grid.Sorting.OrderBy.Clear(); - - foreach (var sort in command.SortDescriptors) - { - var existingSort = grid.Sorting.OrderBy.FirstOrDefault(x => x.Member.IsCaseInsensitiveEqual(sort.Member)); - if (existingSort != null) - { - grid.Sorting.OrderBy.Remove(existingSort); - } - grid.Sorting.OrderBy.Add(sort); - } - } - - if (grid.Grouping.Enabled) - { - grid.Grouping.Groups.Clear(); - - foreach (var group in command.GroupDescriptors) - { - var existingGroup = grid.Grouping.Groups.FirstOrDefault(x => x.Member.IsCaseInsensitiveEqual(group.Member)); - if (existingGroup != null) - { - grid.Grouping.Groups.Remove(existingGroup); - } - grid.Grouping.Groups.Add(group); - } - } - - if (grid.Filtering.Enabled) - { - grid.Filtering.Filters.Clear(); - - foreach (var filter in command.FilterDescriptors) - { - var compositeFilter = filter as CompositeFilterDescriptor; - if (compositeFilter == null) - { - compositeFilter = new CompositeFilterDescriptor { LogicalOperator = FilterCompositionLogicalOperator.And }; - compositeFilter.FilterDescriptors.Add(filter); - } - grid.Filtering.Filters.Add(compositeFilter); - } - } - - // persist again for the next request - grid.ViewContext.TempData[gridId] = info; - - return builder; - } - - public static IEnumerable ForCommand(this IEnumerable current, GridCommand command) - { - var query = current.AsQueryable() as IQueryable; - if (command.FilterDescriptors.Any()) - { - query = query.Where(command.FilterDescriptors.AsEnumerable()).AsQueryable() as IQueryable; - } - - IList temporarySortDescriptors = new List(); - - if (!command.SortDescriptors.Any() && query.Provider.IsEntityFrameworkProvider()) - { - // The Entity Framework provider demands OrderBy before calling Skip. - SortDescriptor sortDescriptor = new SortDescriptor - { - Member = GetFirstSortableProperty(query.ElementType) - }; - command.SortDescriptors.Add(sortDescriptor); - temporarySortDescriptors.Add(sortDescriptor); - } - - if (command.GroupDescriptors.Any()) - { - command.GroupDescriptors.Reverse().Each(groupDescriptor => - { - SortDescriptor sortDescriptor = new SortDescriptor - { - Member = groupDescriptor.Member, - SortDirection = groupDescriptor.SortDirection - }; - - command.SortDescriptors.Insert(0, sortDescriptor); - temporarySortDescriptors.Add(sortDescriptor); - }); - } - - if (command.SortDescriptors.Any()) - { - query = query.Sort(command.SortDescriptors); - } - - return query as IQueryable; - } - - public static IEnumerable PagedForCommand(this IEnumerable current, GridCommand command) - { - return current.Skip((command.Page - 1) * command.PageSize).Take(command.PageSize); - } - - - public static GridBoundColumnBuilder Centered(this GridBoundColumnBuilder columnBuilder) where T : class - { - return columnBuilder.HtmlAttributes(new { align = "center" }).HeaderHtmlAttributes(new { style = "text-align:center;" }); - } - - public static GridTemplateColumnBuilder Centered(this GridTemplateColumnBuilder columnBuilder) where T : class - { - return columnBuilder.HtmlAttributes(new { align = "center" }).HeaderHtmlAttributes(new { style = "text-align:center;" }); - } - - public static GridBoundColumnBuilder RightAlign(this GridBoundColumnBuilder columnBuilder) where T : class - { - return columnBuilder.HtmlAttributes(new { style = "text-align:right;" }).HeaderHtmlAttributes(new { style = "text-align:right;" }); - } - - public static GridTemplateColumnBuilder RightAlign(this GridTemplateColumnBuilder columnBuilder) where T : class - { - return columnBuilder.HtmlAttributes(new { style = "text-align:right;" }).HeaderHtmlAttributes(new { style = "text-align:right;" }); - } - - public static string GetValueFromAppliedFilter(this IFilterDescriptor filter, string valueName, FilterOperator? filterOperator = null) - { - if (filter is CompositeFilterDescriptor) - { - foreach (IFilterDescriptor childFilter in ((CompositeFilterDescriptor)filter).FilterDescriptors) - { - var val1 = GetValueFromAppliedFilter(childFilter, valueName, filterOperator); - if (!String.IsNullOrEmpty(val1)) - return val1; - } - } - else - { - var filterDescriptor = (FilterDescriptor)filter; - if (filterDescriptor != null && - filterDescriptor.Member.Equals(valueName, StringComparison.InvariantCultureIgnoreCase)) - { - if (!filterOperator.HasValue || filterDescriptor.Operator == filterOperator.Value) - return Convert.ToString(filterDescriptor.Value); - } - } - - return ""; - } - - public static string GetValueFromAppliedFilters(this IList filters, string valueName, FilterOperator? filterOperator = null) - { - foreach (var filter in filters) - { - var val1 = GetValueFromAppliedFilter(filter, valueName, filterOperator); - if (!String.IsNullOrEmpty(val1)) - return val1; - } - return ""; - } - - private static string GetFirstSortableProperty(Type type) - { - PropertyInfo firstSortableProperty = type.GetProperties().Where(property => property.PropertyType.IsPredefinedSimpleType()).FirstOrDefault(); - - if (firstSortableProperty == null) - { - throw new NotSupportedException("Cannot find property to sort by."); - } - - return firstSortableProperty.Name; - } - } + [Serializable] + public class GridStateInfo + { + public GridStateInfo.GridState State { get; set; } + public string Path { get; set; } + + [Serializable] + public class GridState + { + public string Filter { get; set; } + public string GroupBy { get; set; } + public string OrderBy { get; set; } + public int Page { get; set; } + public int Size { get; set; } + } + } + + public static class TelerikExtensions + { + [SuppressMessage("ReSharper", "Mvc.AreaNotResolved")] + public static GridBuilder PreserveGridState(this GridBuilder builder) where T : class + { + var grid = builder.ToComponent(); + + if (!grid.DataBinding.Ajax.Enabled) + return builder; + + if (grid.Id.IsEmpty()) + throw new SmartException("A grid with preservable state must have a valid Id or Name"); + + var urlHelper = new UrlHelper(grid.ViewContext.RequestContext); + + var gridId = "GridState." + grid.Id + "__" + grid.ViewContext.RouteData.GenerateRouteIdentifier(); + + grid.AppendCssClass("grid-preservestate"); + grid.HtmlAttributes.Add("data-statepreserver-href", urlHelper.Action("SetGridState", "Common", new { area = "admin" })); + grid.HtmlAttributes.Add("data-statepreserver-key", gridId); + + // Try restore state from a previous request + var info = (GridStateInfo)grid.ViewContext.TempData[gridId]; + + if (info == null) + return builder; + + var state = info.State; + var command = GridCommand.Parse(state.Page, state.Size, state.OrderBy, state.GroupBy, state.Filter); + + if (grid.Paging.Enabled) + { + var pathChanged = !info.Path.Equals(grid.ViewContext.HttpContext.Request.RawUrl, StringComparison.OrdinalIgnoreCase); + if (!pathChanged) + { + if (command.PageSize > 0) + grid.Paging.PageSize = command.PageSize; + if (command.Page > 0) + grid.Paging.CurrentPage = command.Page; + } + } + + if (grid.Sorting.Enabled) + { + grid.Sorting.OrderBy.Clear(); + + foreach (var sort in command.SortDescriptors) + { + var existingSort = grid.Sorting.OrderBy.FirstOrDefault(x => x.Member.IsCaseInsensitiveEqual(sort.Member)); + if (existingSort != null) + { + grid.Sorting.OrderBy.Remove(existingSort); + } + grid.Sorting.OrderBy.Add(sort); + } + } + + if (grid.Grouping.Enabled) + { + grid.Grouping.Groups.Clear(); + + foreach (var group in command.GroupDescriptors) + { + var existingGroup = grid.Grouping.Groups.FirstOrDefault(x => x.Member.IsCaseInsensitiveEqual(group.Member)); + if (existingGroup != null) + { + grid.Grouping.Groups.Remove(existingGroup); + } + grid.Grouping.Groups.Add(group); + } + } + + if (grid.Filtering.Enabled) + { + grid.Filtering.Filters.Clear(); + + foreach (var filter in command.FilterDescriptors) + { + var compositeFilter = filter as CompositeFilterDescriptor; + if (compositeFilter == null) + { + compositeFilter = new CompositeFilterDescriptor { LogicalOperator = FilterCompositionLogicalOperator.And }; + compositeFilter.FilterDescriptors.Add(filter); + } + grid.Filtering.Filters.Add(compositeFilter); + } + } + + // persist again for the next request + grid.ViewContext.TempData[gridId] = info; + + return builder; + } + + public static IEnumerable ForCommand(this IEnumerable current, GridCommand command) + { + var query = current.AsQueryable() as IQueryable; + if (command.FilterDescriptors.Any()) + { + query = query.Where(command.FilterDescriptors.AsEnumerable()).AsQueryable() as IQueryable; + } + + IList temporarySortDescriptors = new List(); + + if (!command.SortDescriptors.Any() && query.Provider.IsEntityFrameworkProvider()) + { + // The Entity Framework provider demands OrderBy before calling Skip. + SortDescriptor sortDescriptor = new SortDescriptor + { + Member = GetFirstSortableProperty(query.ElementType) + }; + command.SortDescriptors.Add(sortDescriptor); + temporarySortDescriptors.Add(sortDescriptor); + } + + if (command.GroupDescriptors.Any()) + { + command.GroupDescriptors.Reverse().Each(groupDescriptor => + { + SortDescriptor sortDescriptor = new SortDescriptor + { + Member = groupDescriptor.Member, + SortDirection = groupDescriptor.SortDirection + }; + + command.SortDescriptors.Insert(0, sortDescriptor); + temporarySortDescriptors.Add(sortDescriptor); + }); + } + + if (command.SortDescriptors.Any()) + { + query = query.Sort(command.SortDescriptors); + } + + return query as IQueryable; + } + + public static IEnumerable PagedForCommand(this IEnumerable current, GridCommand command) + { + return current.Skip((command.Page - 1) * command.PageSize).Take(command.PageSize); + } + + + public static GridBoundColumnBuilder Centered(this GridBoundColumnBuilder columnBuilder) where T : class + { + return columnBuilder.HtmlAttributes(new { align = "center" }).HeaderHtmlAttributes(new { style = "text-align:center;" }); + } + + public static GridTemplateColumnBuilder Centered(this GridTemplateColumnBuilder columnBuilder) where T : class + { + return columnBuilder.HtmlAttributes(new { align = "center" }).HeaderHtmlAttributes(new { style = "text-align:center;" }); + } + + public static GridBoundColumnBuilder RightAlign(this GridBoundColumnBuilder columnBuilder) where T : class + { + return columnBuilder.HtmlAttributes(new { style = "text-align:right;" }).HeaderHtmlAttributes(new { style = "text-align:right;" }); + } + + public static GridTemplateColumnBuilder RightAlign(this GridTemplateColumnBuilder columnBuilder) where T : class + { + return columnBuilder.HtmlAttributes(new { style = "text-align:right;" }).HeaderHtmlAttributes(new { style = "text-align:right;" }); + } + + public static string GetValueFromAppliedFilter(this IFilterDescriptor filter, string valueName, FilterOperator? filterOperator = null) + { + if (filter is CompositeFilterDescriptor) + { + foreach (IFilterDescriptor childFilter in ((CompositeFilterDescriptor)filter).FilterDescriptors) + { + var val1 = GetValueFromAppliedFilter(childFilter, valueName, filterOperator); + if (!String.IsNullOrEmpty(val1)) + return val1; + } + } + else + { + var filterDescriptor = (FilterDescriptor)filter; + if (filterDescriptor != null && + filterDescriptor.Member.Equals(valueName, StringComparison.InvariantCultureIgnoreCase)) + { + if (!filterOperator.HasValue || filterDescriptor.Operator == filterOperator.Value) + return Convert.ToString(filterDescriptor.Value); + } + } + + return ""; + } + + public static string GetValueFromAppliedFilters(this IList filters, string valueName, FilterOperator? filterOperator = null) + { + foreach (var filter in filters) + { + var val1 = GetValueFromAppliedFilter(filter, valueName, filterOperator); + if (!String.IsNullOrEmpty(val1)) + return val1; + } + return ""; + } + + private static string GetFirstSortableProperty(Type type) + { + PropertyInfo firstSortableProperty = type.GetProperties().Where(property => property.PropertyType.IsPredefinedSimpleType()).FirstOrDefault(); + + if (firstSortableProperty == null) + { + throw new NotSupportedException("Cannot find property to sort by."); + } + + return firstSortableProperty.Name; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Extensions/UrlHelperExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Extensions/UrlHelperExtensions.cs index fdb1f1d332..e88283e4f3 100644 --- a/src/Presentation/SmartStore.Web.Framework/Extensions/UrlHelperExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Extensions/UrlHelperExtensions.cs @@ -1,9 +1,9 @@ -using System; +using System.Runtime.CompilerServices; using System.Web.Mvc; -using SmartStore.Core.Domain.Media; using SmartStore.Core.Infrastructure; using SmartStore.Services.Cms; using SmartStore.Services.Media; +using SmartStore.Services.Media.Imaging; namespace SmartStore.Web.Framework { @@ -16,7 +16,7 @@ public static string LogOn(this UrlHelper urlHelper, string returnUrl) return urlHelper.Action("Login", "Customer", new { returnUrl, area = "" }); } - return urlHelper.Action("Login", "Customer", new { area = "" }); + return urlHelper.Action("Login", "Customer", new { area = "" }); } public static string LogOff(this UrlHelper urlHelper, string returnUrl) @@ -26,118 +26,134 @@ public static string LogOff(this UrlHelper urlHelper, string returnUrl) return urlHelper.Action("Logout", "Customer", new { returnUrl, area = "" }); } - return urlHelper.Action("Logout", "Customer", new { area = "" }); + return urlHelper.Action("Logout", "Customer", new { area = "" }); } - public static string Referrer(this UrlHelper urlHelper, string fallbackUrl = "") - { - var request = urlHelper.RequestContext.HttpContext.Request; - if (request.UrlReferrer != null && request.UrlReferrer.ToString().HasValue()) - { - return request.UrlReferrer.ToString(); - } - - return fallbackUrl; - } - - public static string Picture(this UrlHelper urlHelper, int? pictureId, int targetSize = 0, FallbackPictureType fallbackType = FallbackPictureType.Entity, string host = null) - { - var pictureService = EngineContext.Current.Resolve(); - return pictureService.GetUrl(pictureId.GetValueOrDefault(), targetSize, fallbackType, host); - } - - public static string Picture(this UrlHelper urlHelper, Picture picture, int targetSize = 0, FallbackPictureType fallbackType = FallbackPictureType.Entity, string host = null) - { - var pictureService = EngineContext.Current.Resolve(); - return pictureService.GetUrl(picture, targetSize, fallbackType, host); - } - - /// - /// Resolves a link to a topic page. - /// - /// The system name of the topic. - /// Link - /// - /// This method returns an empty string in following cases: - /// - the requested page does not exist. - /// - the current user has no permission to acces the page. - /// - public static string Topic(this UrlHelper urlHelper, string systemName, bool popup = false) - { - Guard.NotEmpty(systemName, nameof(systemName)); - - var expression = "topic:" + systemName; - if (popup) - { - expression += "?popup=true"; - } - - return Entity(urlHelper, expression); - } - - /// - /// Resolves a link label for a topic page. - /// The label is either the page short title or title. - /// - /// The system name of the topic. - /// Label - /// - /// This method returns an empty string if the requested page does not exist. - /// - public static string TopicLabel(this UrlHelper urlHelper, string systemName) - { - Guard.NotEmpty(systemName, nameof(systemName)); - - return EntityLabel(urlHelper, "topic:" + systemName); - } - - /// - /// Resolves a link to a system internal entity like product, topic, category or manufacturer. - /// - /// A link expression as supported by the - /// Link - /// - /// This method returns an empty string in following cases: - /// - the requested entity does not exist. - /// - the current user has no permission to acces the entity. - /// - public static string Entity(this UrlHelper urlHelper, string expression) - { - Guard.NotEmpty(expression, nameof(expression)); - - var linkResolver = EngineContext.Current.Resolve(); - var link = linkResolver.Resolve(expression); - - if (link.Status == LinkStatus.Ok) - { - return link.Link.EmptyNull(); - } - - return string.Empty; - } - - /// - /// Resolves a link label for a system internal entity like product, topic, category or manufacturer. - /// The label is either the entity short title, title or name, whichever is applicable. - /// - /// A link expression as supported by the - /// Label - /// - /// This method returns an empty string if the requested entity does not exist. - /// - public static string EntityLabel(this UrlHelper urlHelper, string expression) - { - Guard.NotEmpty(expression, nameof(expression)); - - var linkResolver = EngineContext.Current.Resolve(); - var link = linkResolver.Resolve(expression); - - if (link.Status == LinkStatus.Ok || link.Status == LinkStatus.Forbidden) - { - return link.Label; - } - - return string.Empty; - } - } + public static string Referrer(this UrlHelper urlHelper, string fallbackUrl = "") + { + var request = urlHelper.RequestContext.HttpContext.Request; + if (request.UrlReferrer != null && request.UrlReferrer.ToString().HasValue()) + { + return request.UrlReferrer.ToString(); + } + + return fallbackUrl; + } + + /// + /// Resolves a link to a topic page. + /// + /// The system name of the topic. + /// Link + /// + /// This method returns an empty string in following cases: + /// - the requested page does not exist. + /// - the current user has no permission to acces the page. + /// + public static string Topic(this UrlHelper urlHelper, string systemName, bool popup = false) + { + Guard.NotEmpty(systemName, nameof(systemName)); + + var expression = "topic:" + systemName; + if (popup) + { + expression += "?popup=true"; + } + + return Entity(urlHelper, expression); + } + + /// + /// Resolves a link label for a topic page. + /// The label is either the page short title or title. + /// + /// The system name of the topic. + /// Label + /// + /// This method returns an empty string if the requested page does not exist. + /// + public static string TopicLabel(this UrlHelper urlHelper, string systemName) + { + Guard.NotEmpty(systemName, nameof(systemName)); + + return EntityLabel(urlHelper, "topic:" + systemName); + } + + /// + /// Resolves a link to a system internal entity like product, topic, category or manufacturer. + /// + /// A link expression as supported by the + /// Link + /// + /// This method returns an empty string in following cases: + /// - the requested entity does not exist. + /// - the current user has no permission to acces the entity. + /// + public static string Entity(this UrlHelper urlHelper, string expression) + { + Guard.NotEmpty(expression, nameof(expression)); + + var linkResolver = EngineContext.Current.Resolve(); + var link = linkResolver.Resolve(expression); + + if (link.Status == LinkStatus.Ok) + { + return link.Link.EmptyNull(); + } + + return string.Empty; + } + + /// + /// Resolves a link label for a system internal entity like product, topic, category or manufacturer. + /// The label is either the entity short title, title or name, whichever is applicable. + /// + /// A link expression as supported by the + /// Label + /// + /// This method returns an empty string if the requested entity does not exist. + /// + public static string EntityLabel(this UrlHelper urlHelper, string expression) + { + Guard.NotEmpty(expression, nameof(expression)); + + var linkResolver = EngineContext.Current.Resolve(); + var link = linkResolver.Resolve(expression); + + if (link.Status == LinkStatus.Ok || link.Status == LinkStatus.Forbidden) + { + return link.Label; + } + + return string.Empty; + } + + #region Media + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Media(this UrlHelper urlHelper, int? fileId, int thumbnailSize = 0, string host = null, bool doFallback = true) + { + return EngineContext.Current.Resolve().GetUrl(fileId, thumbnailSize, host, doFallback); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Media(this UrlHelper urlHelper, MediaFileInfo file, int thumbnailSize = 0, string host = null, bool doFallback = true) + { + return EngineContext.Current.Resolve().GetUrl(file, thumbnailSize, host, doFallback); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Media(this UrlHelper urlHelper, int? fileId, ProcessImageQuery query, string host = null, bool doFallback = true) + { + return EngineContext.Current.Resolve().GetUrl(fileId, query, host, doFallback); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Media(this UrlHelper urlHelper, MediaFileInfo file, ProcessImageQuery query, string host = null, bool doFallback = true) + { + return EngineContext.Current.Resolve().GetUrl(file, query, host, doFallback); + } + + #endregion + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/CheckAffiliateAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Filters/CheckAffiliateAttribute.cs index b9f2972f8f..ed1663b5be 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/CheckAffiliateAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/CheckAffiliateAttribute.cs @@ -1,5 +1,4 @@ using System; -using System.Web; using System.Web.Mvc; using SmartStore.Core; using SmartStore.Services.Affiliates; @@ -9,20 +8,17 @@ namespace SmartStore.Web.Framework.Filters { public class CheckAffiliateAttribute : FilterAttribute, IActionFilter { - public Lazy AffiliateService { get; set; } - public Lazy WorkContext { get; set; } - public Lazy CustomerService { get; set; } - - public virtual void OnActionExecuting(ActionExecutingContext filterContext) + public Lazy AffiliateService { get; set; } + public Lazy WorkContext { get; set; } + public Lazy CustomerService { get; set; } + + public virtual void OnActionExecuting(ActionExecutingContext filterContext) { - if (filterContext == null || filterContext.HttpContext == null) + if (filterContext?.HttpContext?.Request == null) return; - HttpRequestBase request = filterContext.HttpContext.Request; - if (request == null) - return; + var request = filterContext.HttpContext.Request; - //don't apply filter to child methods if (filterContext.IsChildAction) return; @@ -38,16 +34,16 @@ public virtual void OnActionExecuting(ActionExecutingContext filterContext) var customer = WorkContext.Value.CurrentCustomer; if (customer != null && !customer.IsSystemAccount && customer.AffiliateId != affiliate.Id) { - customer.AffiliateId = affiliate.Id; - CustomerService.Value.UpdateCustomer(customer); + customer.AffiliateId = affiliate.Id; + CustomerService.Value.UpdateCustomer(customer); } } } } } - public virtual void OnActionExecuted(ActionExecutedContext filterContext) - { - } - } + public virtual void OnActionExecuted(ActionExecutedContext filterContext) + { + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/CompressAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Filters/CompressAttribute.cs index bdbde8447f..6ce3ae8ce2 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/CompressAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/CompressAttribute.cs @@ -1,32 +1,31 @@ using System.IO.Compression; -using System.Web; using System.Web.Mvc; namespace SmartStore.Web.Framework.Filters { - public class CompressAttribute : ActionFilterAttribute - { - public bool PreferDeflate { get; set; } + public class CompressAttribute : ActionFilterAttribute + { + public bool PreferDeflate { get; set; } - public override void OnActionExecuting(ActionExecutingContext filterContext) - { - var response = HttpContext.Current.Response; - var acceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"].EmptyNull().ToLower(); + public override void OnActionExecuting(ActionExecutingContext filterContext) + { + var response = filterContext.HttpContext.Response; + var acceptEncoding = filterContext.HttpContext.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"); - } + 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"); + } - response.AppendHeader("Vary", "Content-Encoding"); - } - } + response.AppendHeader("Vary", "Content-Encoding"); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/CookieConsentFilter.cs b/src/Presentation/SmartStore.Web.Framework/Filters/CookieConsentFilter.cs index 7d8b4e8751..ca99c83119 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/CookieConsentFilter.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/CookieConsentFilter.cs @@ -1,152 +1,168 @@ using System; -using System.Web; using System.Web.Mvc; +using Newtonsoft.Json; using SmartStore.Core.Domain.Customers; using SmartStore.Services; using SmartStore.Services.Common; +using SmartStore.Services.Customers; using SmartStore.Web.Framework.UI; namespace SmartStore.Web.Framework.Filters { public class CookieConsentFilter : IActionFilter, IResultFilter - { - private readonly IUserAgent _userAgent; - private readonly ICommonServices _services; - private readonly Lazy _widgetProvider; - private readonly PrivacySettings _privacySettings; - - public CookieConsentFilter( - IUserAgent userAgent, - ICommonServices services, - Lazy widgetProvider, - PrivacySettings privacySettings) - { - _userAgent = userAgent; - _services = services; - _widgetProvider = widgetProvider; - _privacySettings = privacySettings; - } - - public void OnActionExecuting(ActionExecutingContext filterContext) - { - if (!_privacySettings.EnableCookieConsent) - return; - - if (filterContext?.ActionDescriptor == null || filterContext?.HttpContext?.Request == null) - return; - - string actionName = filterContext.ActionDescriptor.ActionName; - string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; - - var viewBag = filterContext.Controller.ViewBag; - viewBag.AskCookieConsent = true; - viewBag.HasCookieConsent = false; - - var request = filterContext.HttpContext.Request; - - // Check if the user has a consent cookie - var consentCookie = request.Cookies[CookieConsent.CONSENT_COOKIE_NAME]; - if (consentCookie == null) - { - // No consent cookie. We first check the Do Not Track header value, this can have the value "0" or "1" - string dnt = request.Headers.Get("DNT"); - - // If we receive a DNT header, we accept its value and do not ask the user anymore - if (!String.IsNullOrEmpty(dnt)) - { - viewBag.AskCookieConsent = false; - viewBag.HasCookieConsent = dnt == "0"; - } - else - { - if (_userAgent.IsBot) - { - // don't ask consent from search engines, also don't set cookies - viewBag.AskCookieConsent = false; - } - else - { - // first request on the site and no DNT header. - consentCookie = new HttpCookie(CookieConsent.CONSENT_COOKIE_NAME); - consentCookie.Value = "asked"; - filterContext.HttpContext.Response.Cookies.Add(consentCookie); - } - } - } - else - { - // we received a consent cookie - viewBag.AskCookieConsent = false; - if (consentCookie.Value == "asked") - { - // consent is implicitly given - consentCookie.Expires = DateTime.UtcNow.AddYears(1); - filterContext.HttpContext.Response.Cookies.Set(consentCookie); - viewBag.HasCookieConsent = true; - } - else if (consentCookie.Value == "true") - { - viewBag.HasCookieConsent = true; - } - else - { - // assume consent denied - viewBag.HasCookieConsent = false; - } - } - } - - public void OnActionExecuted(ActionExecutedContext filterContext) - { - - } - - public void OnResultExecuting(ResultExecutingContext filterContext) - { - if (!_privacySettings.EnableCookieConsent) - return; - - if (filterContext.IsChildAction) - return; - - var result = filterContext.Result; - - // should only run on a full view rendering result or HTML ContentResult - if (!result.IsHtmlViewResult()) - return; - - _widgetProvider.Value.RegisterAction( - new[] { "body_end_html_tag_before" }, - "CookieConsentBadge", - "Common", - new { area = "" }); - } - - public void OnResultExecuted(ResultExecutedContext filterContext) - { - } - } - - public static class CookieConsent - { - public const string CONSENT_COOKIE_NAME = "CookieConsent"; - - public static void SetCookieConsent(HttpResponseBase response, bool consent) - { - var consentCookie = new HttpCookie(CookieConsent.CONSENT_COOKIE_NAME); - consentCookie.Value = consent ? "true" : "false"; - consentCookie.Expires = DateTime.UtcNow.AddYears(1); - response.Cookies.Set(consentCookie); - } - - public static bool AskCookieConsent(ViewContext context) - { - return context.ViewBag.AskCookieConsent ?? false; - } - - public static bool HasCookieConsent(ViewContext context) - { - return context.ViewBag.HasCookieConsent ?? false; - } - } + { + private readonly IUserAgent _userAgent; + private readonly ICommonServices _services; + private readonly Lazy _widgetProvider; + private readonly PrivacySettings _privacySettings; + private readonly ICookieManager _cookieManager; + + private bool _isProcessableRequest; + + public CookieConsentFilter( + IUserAgent userAgent, + ICommonServices services, + Lazy widgetProvider, + PrivacySettings privacySettings, + ICookieManager cookieManager) + { + _userAgent = userAgent; + _services = services; + _widgetProvider = widgetProvider; + _privacySettings = privacySettings; + _cookieManager = cookieManager; + } + + private bool IsProcessableRequest(ControllerContext controllerContext) + { + if (!_privacySettings.EnableCookieConsent) + return false; + + if (controllerContext.IsChildAction) + return false; + + var request = controllerContext.HttpContext?.Request; + + if (request == null) + return false; + + if (request.IsAjaxRequest()) + return false; + + if (!String.Equals(request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) + return false; + + return true; + } + + public void OnActionExecuting(ActionExecutingContext filterContext) + { + _isProcessableRequest = IsProcessableRequest(filterContext); + + if (!_isProcessableRequest) + return; + + var isLegacy = false; + var request = filterContext.HttpContext.Request; + var response = filterContext.HttpContext.Response; + ConsentCookie cookieData = null; + + // Check if the user has a consent cookie. + var consentCookie = request.Cookies["CookieConsent"]; + if (consentCookie == null) + { + // No consent cookie. We first check the Do Not Track header value, this can have the value "0" or "1" + var doNotTrack = request.Headers.Get("DNT"); + + // If we receive a DNT header, we accept its value (0 = give consent, 1 = deny) and do not ask the user anymore. + if (doNotTrack.HasValue()) + { + if (doNotTrack.Equals("0")) + { + // Tracking consented. + _cookieManager.SetConsentCookie(response, true, true); + } + else + { + // Tracking denied. + _cookieManager.SetConsentCookie(response, false, false); + } + } + else + { + if (_userAgent.IsBot) + { + // Don't ask consent from search engines, also don't set cookies. + _cookieManager.SetConsentCookie(response, true, true); + } + else + { + // First request on the site and no DNT header (we could use session cookie, which is allowed by EU cookie law). + // Don't set cookie! + } + } + } + else + { + // We received a consent cookie + try + { + cookieData = JsonConvert.DeserializeObject(consentCookie.Value); + } + catch { } + + if (cookieData == null) + { + // Cookie was found but could not be converted thus it's a legacy cookie. + isLegacy = true; + var str = consentCookie.Value; + + // 'asked' means customer has not consented. + // '2' was the Value of legacy enum CookieConsentStatus.Denied + if (str.Equals("asked") || str.Equals("2")) + { + // Remove legacy Cookie & thus show CookieManager. + request.Cookies.Remove("CookieConsent"); + } + // 'true' means consented to all cookies. + // '1' was the Value of legacy enum CookieConsentStatus.Consented + else if (str.Equals("true") || str.Equals("1")) + { + // Set Cookie with all types allowed. + _cookieManager.SetConsentCookie(response, true, true); + } + } + } + + if (!isLegacy) + { + filterContext.HttpContext.Items["CookieConsent"] = cookieData; + } + } + + public void OnActionExecuted(ActionExecutedContext filterContext) + { + } + + public void OnResultExecuting(ResultExecutingContext filterContext) + { + if (!_isProcessableRequest) + return; + + // Should only run on a full view rendering result or HTML ContentResult. + if (!filterContext.Result.IsHtmlViewResult()) + return; + + // Always render the child action because of output caching. + _widgetProvider.Value.RegisterAction( + new[] { "body_end_html_tag_before" }, + "CookieManager", + "Common", + new { area = "" }); + } + + public void OnResultExecuted(ResultExecutedContext filterContext) + { + } + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/CustomerLastActivityAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Filters/CustomerLastActivityAttribute.cs index f6d7f08da0..d0ecd40b0c 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/CustomerLastActivityAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/CustomerLastActivityAttribute.cs @@ -8,37 +8,46 @@ namespace SmartStore.Web.Framework.Filters { public class CustomerLastActivityAttribute : FilterAttribute, IActionFilter { - public Lazy WorkContext { get; set; } - public Lazy CustomerService { get; set; } - - public virtual void OnActionExecuting(ActionExecutingContext filterContext) + public Lazy WorkContext { get; set; } + public Lazy CustomerService { get; set; } + + public virtual void OnActionExecuting(ActionExecutingContext filterContext) { if (!DataSettings.DatabaseIsInstalled()) return; - if (filterContext == null || filterContext.HttpContext == null || filterContext.HttpContext.Request == null) + if (filterContext?.HttpContext?.Request == null) return; - // don't apply filter to child methods if (filterContext.IsChildAction) return; - // only GET requests if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) return; var customer = WorkContext.Value.CurrentCustomer; - - // update last activity date - if (!customer.IsSystemAccount && customer.LastActivityDateUtc.AddMinutes(1.0) < DateTime.UtcNow) + + // Update last activity date. + if (customer != null && !customer.Deleted && !customer.IsSystemAccount && customer.LastActivityDateUtc.AddMinutes(1.0) < DateTime.UtcNow) { - customer.LastActivityDateUtc = DateTime.UtcNow; - CustomerService.Value.UpdateCustomer(customer); + try + { + customer.LastActivityDateUtc = DateTime.UtcNow; + CustomerService.Value.UpdateCustomer(customer); + } + catch (InvalidOperationException ioe) + { + // The exception may occur on the first call after a migration. + if (!ioe.IsAlreadyAttachedEntityException()) + { + throw; + } + } } } - public virtual void OnActionExecuted(ActionExecutedContext filterContext) - { - } - } + public virtual void OnActionExecuted(ActionExecutedContext filterContext) + { + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/FormValueRequiredAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Filters/FormValueRequiredAttribute.cs index c0a759a5c5..e24540ab11 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/FormValueRequiredAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/FormValueRequiredAttribute.cs @@ -7,125 +7,125 @@ namespace SmartStore.Web.Framework.Filters { - - public class FormValueRequiredAttribute : ActionMethodSelectorAttribute + + public class FormValueRequiredAttribute : ActionMethodSelectorAttribute { private readonly string[] _submitButtonNames; private readonly FormValueRequirement _requirement; - private readonly FormValueRequirementRule _rule; - private readonly bool _inverse; + private readonly FormValueRequirementRule _rule; + private readonly bool _inverse; - public FormValueRequiredAttribute(params string[] submitButtonNames): + public FormValueRequiredAttribute(params string[] submitButtonNames) : this(FormValueRequirement.Equal, FormValueRequirementRule.MatchAny, false, submitButtonNames) { } public FormValueRequiredAttribute(FormValueRequirement requirement, params string[] submitButtonNames) - : this(requirement, FormValueRequirementRule.MatchAny, false, submitButtonNames) + : this(requirement, FormValueRequirementRule.MatchAny, false, submitButtonNames) + { + } + + public FormValueRequiredAttribute(FormValueRequirementRule rule, params string[] submitButtonNames) + : this(FormValueRequirement.Equal, rule, false, submitButtonNames) + { + } + + public FormValueRequiredAttribute(FormValueRequirement requirement, FormValueRequirementRule rule, params string[] submitButtonNames) + : this(requirement, rule, false, submitButtonNames) { } - public FormValueRequiredAttribute(FormValueRequirementRule rule, params string[] submitButtonNames) - : this(FormValueRequirement.Equal, rule, false, submitButtonNames) - { - } - - public FormValueRequiredAttribute(FormValueRequirement requirement, FormValueRequirementRule rule, params string[] submitButtonNames) - : this(requirement, rule, false, submitButtonNames) - { - } - - protected internal FormValueRequiredAttribute( - FormValueRequirement requirement, - FormValueRequirementRule rule, - bool inverse, - params string[] submitButtonNames) - { - // at least one submit button should be found (or being absent if 'inverse') - this._submitButtonNames = submitButtonNames; - this._requirement = requirement; - this._rule = rule; - this._inverse = inverse; - } + protected internal FormValueRequiredAttribute( + FormValueRequirement requirement, + FormValueRequirementRule rule, + bool inverse, + params string[] submitButtonNames) + { + // at least one submit button should be found (or being absent if 'inverse') + this._submitButtonNames = submitButtonNames; + this._requirement = requirement; + this._rule = rule; + this._inverse = inverse; + } public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { - return IsValidForRequest(controllerContext.HttpContext.Request.Form); - } - - protected internal virtual bool IsValidForRequest(NameValueCollection form) - { - try - { - bool isMatch = false; - if (_rule == FormValueRequirementRule.MatchAny) - { - isMatch = _submitButtonNames.Any(x => IsMatch(form, x)); - } - else - { - isMatch = _submitButtonNames.All(x => IsMatch(form, x)); - } - return isMatch; - } - catch (Exception ex) - { - Debug.WriteLine(ex.Message); - } - - return false; - } - - private bool IsMatch(NameValueCollection form, string key) - { - string value = ""; - - if (_requirement == FormValueRequirement.Equal) - { - // do not iterate because "Invalid request" exception can be thrown - value = form[key]; - } - else - { - var firstMatch = form.AllKeys.FirstOrDefault(x => x.StartsWith(key, StringComparison.InvariantCultureIgnoreCase)); - if (firstMatch != null) - { - value = form[firstMatch]; - } - } - - if (_inverse) - { - return value.IsEmpty(); - } - - return value.HasValue(); - } + return IsValidForRequest(controllerContext.HttpContext.Request.Form); + } + + protected internal virtual bool IsValidForRequest(NameValueCollection form) + { + try + { + bool isMatch = false; + if (_rule == FormValueRequirementRule.MatchAny) + { + isMatch = _submitButtonNames.Any(x => IsMatch(form, x)); + } + else + { + isMatch = _submitButtonNames.All(x => IsMatch(form, x)); + } + return isMatch; + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + } + + return false; + } + + private bool IsMatch(NameValueCollection form, string key) + { + string value = ""; + + if (_requirement == FormValueRequirement.Equal) + { + // do not iterate because "Invalid request" exception can be thrown + value = form[key]; + } + else + { + var firstMatch = form.AllKeys.FirstOrDefault(x => x.StartsWith(key, StringComparison.InvariantCultureIgnoreCase)); + if (firstMatch != null) + { + value = form[firstMatch]; + } + } + + if (_inverse) + { + return value.IsEmpty(); + } + + return value.HasValue(); + } } - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - public class FormValueAbsentAttribute : FormValueRequiredAttribute - { - public FormValueAbsentAttribute(params string[] submitButtonNames): + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class FormValueAbsentAttribute : FormValueRequiredAttribute + { + public FormValueAbsentAttribute(params string[] submitButtonNames) : base(FormValueRequirement.Equal, FormValueRequirementRule.MatchAny, true, submitButtonNames) { } - public FormValueAbsentAttribute(FormValueRequirement requirement, params string[] submitButtonNames) - : base(requirement, FormValueRequirementRule.MatchAny, true, submitButtonNames) + public FormValueAbsentAttribute(FormValueRequirement requirement, params string[] submitButtonNames) + : base(requirement, FormValueRequirementRule.MatchAny, true, submitButtonNames) { } - public FormValueAbsentAttribute(FormValueRequirementRule rule, params string[] submitButtonNames) - : base(FormValueRequirement.Equal, rule, true, submitButtonNames) - { - } + public FormValueAbsentAttribute(FormValueRequirementRule rule, params string[] submitButtonNames) + : base(FormValueRequirement.Equal, rule, true, submitButtonNames) + { + } - public FormValueAbsentAttribute(FormValueRequirement requirement, FormValueRequirementRule rule, params string[] submitButtonNames) - : base(requirement, rule, true, submitButtonNames) - { - } - } + public FormValueAbsentAttribute(FormValueRequirement requirement, FormValueRequirementRule rule, params string[] submitButtonNames) + : base(requirement, rule, true, submitButtonNames) + { + } + } public enum FormValueRequirement { @@ -133,9 +133,9 @@ public enum FormValueRequirement StartsWith } - public enum FormValueRequirementRule - { - MatchAny, - MatchAll - } + public enum FormValueRequirementRule + { + MatchAny, + MatchAll + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/GdprConsentAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Filters/GdprConsentAttribute.cs index b76bf74857..a15a40a478 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/GdprConsentAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/GdprConsentAttribute.cs @@ -1,102 +1,100 @@ -using SmartStore.Core; +using System; +using System.Web.Mvc; using SmartStore.Core.Domain.Customers; using SmartStore.Core.Logging; using SmartStore.Services; using SmartStore.Services.Common; using SmartStore.Web.Framework.UI; -using System; -using System.Web.Mvc; -using System.Web.Routing; namespace SmartStore.Web.Framework.Filters { - public class GdprConsentAttribute : FilterAttribute, IActionFilter, IResultFilter - { - public Lazy Services { get; set; } - public Lazy PrivacySettings { get; set; } - public Lazy GenericAttributeService { get; set; } - public Lazy WidgetProvider { get; set; } - public Lazy Notifier { get; set; } - - public void OnActionExecuting(ActionExecutingContext filterContext) - { - if (!PrivacySettings.Value.DisplayGdprConsentOnForms) - return; - - if (filterContext?.ActionDescriptor == null || filterContext?.HttpContext?.Request == null) - return; - - if (filterContext.HttpContext.Request.HttpMethod.Equals("GET")) - return; - - var customer = Services.Value.WorkContext.CurrentCustomer; - var hasConsentedToGdpr = filterContext.HttpContext.Request.Form["GdprConsent"]; - - if (filterContext.HttpContext.Request.HttpMethod.Equals("POST") && hasConsentedToGdpr.HasValue()) - { - // set flag which can be accessed in corresponding action - filterContext.Controller.ViewData.Add("GdprConsent", hasConsentedToGdpr.Contains("true")); - - if (hasConsentedToGdpr.Contains("true")) - { - GenericAttributeService.Value.SaveAttribute(customer, SystemCustomerAttributeNames.HasConsentedToGdpr, true); - } - else - { - if (!filterContext.HttpContext.Request.IsAjaxRequest()) - { - // add a validation message - filterContext.Controller.ViewData.ModelState.AddModelError("", Services.Value.Localization.GetResource("Gdpr.Consent.ValidationMessage")); - } - else - { - // notify - Notifier.Value.Error(Services.Value.Localization.GetResource("Gdpr.Consent.ValidationMessage")); - } - - return; - } - } - } - - public void OnActionExecuted(ActionExecutedContext filterContext) - { - } - - public void OnResultExecuting(ResultExecutingContext filterContext) - { - if (!PrivacySettings.Value.DisplayGdprConsentOnForms) - return; - - if (filterContext.HttpContext.Items.Contains("GdprConsentRendered")) - return; - - var result = filterContext.Result; - - // should only run on a full view rendering result or HTML ContentResult - if (!result.IsHtmlViewResult()) - return; - - if (!filterContext.IsChildAction) - { - WidgetProvider.Value.RegisterAction( - new[] { "gdpr_consent" }, - "GdprConsent", - "Common", - new { area = "", isSmall = false }); - } - - WidgetProvider.Value.RegisterAction( - new[] { "gdpr_consent_small" }, - "GdprConsent", - "Common", - new { area = "", isSmall = true }); - - filterContext.HttpContext.Items["GdprConsentRendered"] = true; - } - - public void OnResultExecuted(ResultExecutedContext filterContext) - { - } - } + public class GdprConsentAttribute : FilterAttribute, IActionFilter, IResultFilter + { + public Lazy Services { get; set; } + public Lazy PrivacySettings { get; set; } + public Lazy GenericAttributeService { get; set; } + public Lazy WidgetProvider { get; set; } + public Lazy Notifier { get; set; } + + public void OnActionExecuting(ActionExecutingContext filterContext) + { + if (!PrivacySettings.Value.DisplayGdprConsentOnForms) + return; + + if (filterContext?.ActionDescriptor == null || filterContext?.HttpContext?.Request == null) + return; + + if (filterContext.HttpContext.Request.HttpMethod.Equals("GET")) + return; + + var customer = Services.Value.WorkContext.CurrentCustomer; + var hasConsentedToGdpr = filterContext.HttpContext.Request.Form["GdprConsent"]; + + if (filterContext.HttpContext.Request.HttpMethod.Equals("POST") && hasConsentedToGdpr.HasValue()) + { + // set flag which can be accessed in corresponding action + filterContext.Controller.ViewData.Add("GdprConsent", hasConsentedToGdpr.Contains("true")); + + if (hasConsentedToGdpr.Contains("true")) + { + GenericAttributeService.Value.SaveAttribute(customer, SystemCustomerAttributeNames.HasConsentedToGdpr, true); + } + else + { + if (!filterContext.HttpContext.Request.IsAjaxRequest()) + { + // add a validation message + filterContext.Controller.ViewData.ModelState.AddModelError("", Services.Value.Localization.GetResource("Gdpr.Consent.ValidationMessage")); + } + else + { + // notify + Notifier.Value.Error(Services.Value.Localization.GetResource("Gdpr.Consent.ValidationMessage")); + } + + return; + } + } + } + + public void OnActionExecuted(ActionExecutedContext filterContext) + { + } + + public void OnResultExecuting(ResultExecutingContext filterContext) + { + if (!PrivacySettings.Value.DisplayGdprConsentOnForms) + return; + + if (filterContext.HttpContext.Items.Contains("GdprConsentRendered")) + return; + + var result = filterContext.Result; + + // should only run on a full view rendering result or HTML ContentResult + if (!result.IsHtmlViewResult()) + return; + + if (!filterContext.IsChildAction) + { + WidgetProvider.Value.RegisterAction( + new[] { "gdpr_consent" }, + "GdprConsent", + "Common", + new { area = "", isSmall = false }); + } + + WidgetProvider.Value.RegisterAction( + new[] { "gdpr_consent_small" }, + "GdprConsent", + "Common", + new { area = "", isSmall = true }); + + filterContext.HttpContext.Items["GdprConsentRendered"] = true; + } + + public void OnResultExecuted(ResultExecutedContext filterContext) + { + } + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/HandleExceptionFilter.cs b/src/Presentation/SmartStore.Web.Framework/Filters/HandleExceptionFilter.cs index 25a7056aa7..09aeb115aa 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/HandleExceptionFilter.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/HandleExceptionFilter.cs @@ -1,10 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Net; -using System.Runtime.InteropServices; -using System.Security; -using System.Threading; using System.Web; using System.Web.Mvc; using SmartStore.Core; @@ -13,159 +8,136 @@ namespace SmartStore.Web.Framework.Filters { - public class HandleExceptionFilter : IActionFilter + public class HandleExceptionFilter : IActionFilter, IExceptionFilter { - private readonly ILoggerFactory _loggerFactory; - private readonly Lazy> _exceptionFilters; - private readonly Lazy _workContext; - - public HandleExceptionFilter( - ILoggerFactory loggerFactory, - Lazy> exceptionFilters, - Lazy workContext) - { - _loggerFactory = loggerFactory; - _exceptionFilters = exceptionFilters; - _workContext = workContext; - } + private readonly ILoggerFactory _loggerFactory; + private readonly Lazy _workContext; + + public HandleExceptionFilter(ILoggerFactory loggerFactory, Lazy workContext) + { + _loggerFactory = loggerFactory; + _workContext = workContext; + } public virtual void OnActionExecuting(ActionExecutingContext filterContext) { } - + public virtual void OnActionExecuted(ActionExecutedContext filterContext) { - var descriptor = filterContext.ActionDescriptor; - - // handle server error - // don't provide custom errors if the action has some custom code to handle exceptions - if (!filterContext.ActionDescriptor.GetCustomAttributes(typeof(HandleErrorAttribute), false).Any()) - { - if (!filterContext.ExceptionHandled && filterContext.Exception != null) - { - if (ShouldHandleException(filterContext.Exception)) - { - LogException(filterContext.Exception, filterContext.ActionDescriptor.ControllerDescriptor); - - // inform exception filters of the exception that was suppressed - var exceptionContext = new ExceptionContext(filterContext.Controller.ControllerContext, filterContext.Exception); - foreach (var exceptionFilter in _exceptionFilters.Value) - { - exceptionFilter.OnException(exceptionContext); - } - - if (exceptionContext.ExceptionHandled) - { - filterContext.ExceptionHandled = true; - filterContext.Result = exceptionContext.Result; - } - else - { - if (!filterContext.IsChildAction) - { - var controllerName = descriptor.ControllerDescriptor.ControllerName; - var actionName = descriptor.ActionName; - - // if the request is AJAX return JSON data - if (filterContext.HttpContext.Request.IsAjaxRequest()) - { - filterContext.Result = new JsonResult - { - JsonRequestBehavior = JsonRequestBehavior.AllowGet, - Data = new - { - error = true, - controller = controllerName, - action = actionName, - message = filterContext.Exception.Message - } - }; - } - else - { - filterContext.Result = new ViewResult - { - ViewName = "Error", - MasterName = (string)null, - ViewData = new ViewDataDictionary(new HandleErrorInfo(filterContext.Exception, controllerName, actionName)), - TempData = filterContext.Controller.TempData - }; - - _workContext.Value.IsAdmin = false; - } - - filterContext.ExceptionHandled = true; - - filterContext.RequestContext.HttpContext.Response.Clear(); - filterContext.RequestContext.HttpContext.Response.StatusCode = 500; - - // prevent IIS 7.0 classic mode from handling the 404/500 itself - filterContext.RequestContext.HttpContext.Response.TrySkipIisCustomErrors = true; - } - } - } - } - } - - if (filterContext.Result is HttpNotFoundResult && !filterContext.IsChildAction) - { - // handle not found (404) from within the MVC pipeline (only called when HttpNotFoundResult is returned from actions) - var requestContext = filterContext.RequestContext; - var url = requestContext.HttpContext.Request.RawUrl; - - filterContext.Result = new ViewResult - { - ViewName = "NotFound", - MasterName = (string)null, - ViewData = new ViewDataDictionary(new HandleErrorInfo(new HttpException(404, "The resource does not exist."), descriptor.ActionName, descriptor.ControllerDescriptor.ControllerName)), - TempData = filterContext.Controller.TempData - }; - - _workContext.Value.IsAdmin = false; - - requestContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; - - // prevent IIS 7.0 classic mode from handling the 404/500 itself - requestContext.HttpContext.Response.TrySkipIisCustomErrors = true; - } + if (filterContext.IsChildAction) + return; + + if (filterContext.Result is HttpStatusCodeResult statusCodeResult && statusCodeResult.StatusCode == 404) + { + // Handle not found (404) from within the MVC pipeline (only called when HttpNotFoundResult is returned from actions) + filterContext.Result = Create404Result(filterContext); + + _workContext.Value.IsAdmin = false; + + } + } + + public void OnException(ExceptionContext filterContext) + { + if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) + return; + + var exception = filterContext.Exception; + + if (!exception.IsFatal()) + { + LogException(exception, filterContext); + } + + if (!filterContext.IsChildAction) + { + var controllerName = filterContext.RouteData.GetRequiredString("controller"); + var actionName = filterContext.RouteData.GetRequiredString("action"); + + // if the request is AJAX return JSON data + if (filterContext.HttpContext.Request.IsAjaxRequest()) + { + filterContext.Result = new JsonResult + { + JsonRequestBehavior = JsonRequestBehavior.AllowGet, + Data = new + { + error = true, + controller = controllerName, + action = actionName, + message = exception.Message + } + }; + } + else + { + filterContext.Result = new ViewResult + { + ViewName = "Error", + MasterName = (string)null, + ViewData = new ViewDataDictionary(new HandleErrorInfo(exception, controllerName, actionName)), + TempData = filterContext.Controller.TempData + }; + + _workContext.Value.IsAdmin = false; + } + + filterContext.ExceptionHandled = true; + + var response = filterContext.RequestContext.HttpContext.Response; + + response.Clear(); + response.StatusCode = 500; + // Truncate message to avoid ArgumentOutOfRangeException. + response.StatusDescription = exception.Message?.Truncate(512); + response.TrySkipIisCustomErrors = true; + } } - private bool ShouldHandleException(Exception exception) - { - return !( - exception is StackOverflowException || - exception is OutOfMemoryException || - exception is AccessViolationException || - exception is AppDomainUnloadedException || - exception is ThreadAbortException || - exception is SecurityException || - exception is SEHException); - } - - protected void LogException(Exception exception, ControllerDescriptor descriptor) - { - if (exception == null) - return; - - if (!DataSettings.DatabaseIsInstalled()) - return; - - //// ignore 404 HTTP errors - //var httpException = exception as HttpException; - //if (httpException != null && httpException.GetHttpCode() == 404) - // return; - - try - { - var logger = _loggerFactory.GetLogger(descriptor.ControllerType); - logger.Error(exception); - } - catch - { - // don't throw new exception - } - } + protected internal static ActionResult Create404Result(ControllerContext context) + { + var controllerName = context.RouteData.GetRequiredString("controller"); + var actionName = context.RouteData.GetRequiredString("action"); + + var response = context.RequestContext.HttpContext.Response; + + //response.Clear(); + response.StatusCode = (int)HttpStatusCode.NotFound; + response.TrySkipIisCustomErrors = true; + + return new ViewResult + { + ViewName = "NotFound", + MasterName = (string)null, + ViewData = new ViewDataDictionary(new HandleErrorInfo(new HttpException(404, "The resource does not exist."), actionName, controllerName)), + TempData = context.Controller.TempData + }; + } + protected void LogException(Exception exception, ControllerContext context) + { + if (exception == null) + return; + + if (!DataSettings.DatabaseIsInstalled()) + return; + + //// ignore 404 HTTP errors + //var httpException = exception as HttpException; + //if (httpException != null && httpException.GetHttpCode() == 404) + // return; + + try + { + var logger = _loggerFactory.GetLogger(context.Controller.GetType()); + logger.Error(exception); + } + catch + { + // don't throw new exception + } + } } } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/HandleInstallFilter.cs b/src/Presentation/SmartStore.Web.Framework/Filters/HandleInstallFilter.cs index af2e1fbaf5..d89f8c6eb7 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/HandleInstallFilter.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/HandleInstallFilter.cs @@ -3,29 +3,29 @@ using SmartStore.Core.Data; namespace SmartStore.Web.Framework.Filters -{ +{ public class HandleInstallFilter : IAuthorizationFilter - { - public void OnAuthorization(AuthorizationContext filterContext) - { - var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; - var actionName = filterContext.ActionDescriptor.ActionName; + { + public void OnAuthorization(AuthorizationContext filterContext) + { + var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; + var actionName = filterContext.ActionDescriptor.ActionName; - if (controllerName == "Install" && actionName != "Index" && filterContext.HttpContext.Request.IsAjaxRequest()) - { - // probably "progress" or "finalize" call - return; - } + if (controllerName == "Install" && actionName != "Index" && filterContext.HttpContext.Request.IsAjaxRequest()) + { + // probably "progress" or "finalize" call + return; + } - if (!DataSettings.DatabaseIsInstalled() && controllerName != "Install") - { - filterContext.Result = new RedirectToRouteResult( - new RouteValueDictionary { - { "Controller", "Install" }, - { "Action", "Index" } - }); - } - } - } + if (!DataSettings.DatabaseIsInstalled() && controllerName != "Install") + { + filterContext.Result = new RedirectToRouteResult( + new RouteValueDictionary { + { "Controller", "Install" }, + { "Action", "Index" } + }); + } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/JsonNetAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Filters/JsonNetAttribute.cs index 1c706ee2f2..ee45056709 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/JsonNetAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/JsonNetAttribute.cs @@ -1,61 +1,47 @@ using System; using System.Web.Mvc; -using Newtonsoft.Json; using SmartStore.Core.Data; using SmartStore.Services.Helpers; using SmartStore.Web.Framework.Modelling; namespace SmartStore.Web.Framework.Filters { - public class JsonNetAttribute : FilterAttribute, IActionFilter - { - public Lazy DateTimeHelper { get; set; } - - public void OnActionExecuting(ActionExecutingContext filterContext) - { - } - - public void OnActionExecuted(ActionExecutedContext filterContext) - { - if (!DataSettings.DatabaseIsInstalled()) - return; - - if (filterContext == null || filterContext.HttpContext == null || filterContext.HttpContext.Request == null) - return; - - // Don't apply filter to child methods. - if (filterContext.IsChildAction) - return; - - // Hndle JsonResult only. - if (filterContext.Result.GetType() != typeof(JsonResult)) - return; - - var jsonResult = filterContext.Result as JsonResult; - var settings = new JsonSerializerSettings - { - MissingMemberHandling = MissingMemberHandling.Ignore, - TypeNameHandling = TypeNameHandling.Objects, - ReferenceLoopHandling = ReferenceLoopHandling.Ignore, - DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind, - DateFormatHandling = DateFormatHandling.MicrosoftDateFormat, - - // We cannot ignore null. Client template of several Telerik grids would fail. - //NullValueHandling = NullValueHandling.Ignore, - - MaxDepth = 32 - }; - - filterContext.Result = new JsonNetResult(DateTimeHelper.Value, settings) - { - Data = jsonResult.Data, - ContentType = jsonResult.ContentType, - ContentEncoding = jsonResult.ContentEncoding, - JsonRequestBehavior = jsonResult.JsonRequestBehavior, - MaxJsonLength = jsonResult.MaxJsonLength, - RecursionLimit = jsonResult.RecursionLimit - }; - } - } + public class JsonNetAttribute : FilterAttribute, IActionFilter + { + public Lazy DateTimeHelper { get; set; } + + public void OnActionExecuting(ActionExecutingContext filterContext) + { + } + + public void OnActionExecuted(ActionExecutedContext filterContext) + { + if (!DataSettings.DatabaseIsInstalled()) + return; + + if (filterContext?.HttpContext?.Request == null) + return; + + // Don't apply filter to child methods. + if (filterContext.IsChildAction) + return; + + // Handle JsonResult only. + if (filterContext.Result.GetType() != typeof(JsonResult)) + return; + + var jsonResult = filterContext.Result as JsonResult; + + filterContext.Result = new JsonNetResult(DateTimeHelper.Value) + { + Data = jsonResult.Data, + ContentType = jsonResult.ContentType, + ContentEncoding = jsonResult.ContentEncoding, + JsonRequestBehavior = jsonResult.JsonRequestBehavior, + MaxJsonLength = jsonResult.MaxJsonLength, + RecursionLimit = jsonResult.RecursionLimit + }; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/MaxMediaFileSizeAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Filters/MaxMediaFileSizeAttribute.cs new file mode 100644 index 0000000000..6be3a52e41 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Filters/MaxMediaFileSizeAttribute.cs @@ -0,0 +1,44 @@ +using System; +using System.Web.Mvc; +using SmartStore.Core.Domain.Media; +using SmartStore.Services.Media; + +namespace SmartStore.Web.Framework.Filters +{ + /// + /// Filters a request by the size of uploaded files according to . + /// + public class MaxMediaFileSizeAttribute : FilterAttribute, IActionFilter + { + public Lazy MediaSettings { get; set; } + public Lazy ExceptionFactory { get; set; } + + public virtual void OnActionExecuting(ActionExecutingContext filterContext) + { + if (filterContext?.HttpContext?.Request == null || filterContext.IsChildAction) + { + return; + } + + var request = filterContext.HttpContext.Request; + if (request.Files.Count <= 0) + { + return; + } + + long maxBytes = 1024 * MediaSettings.Value.MaxUploadFileSize; + for (var i = 0; i < request.Files.Count; ++i) + { + var file = request.Files[i]; + if (file.ContentLength > maxBytes) + { + throw ExceptionFactory.Value.MaxFileSizeExceeded(file.FileName, file.ContentLength, maxBytes); + } + } + } + + public virtual void OnActionExecuted(ActionExecutedContext filterContext) + { + } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/NotifyAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Filters/NotifyAttribute.cs index ec44b5bb1d..2cc6b8665f 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/NotifyAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/NotifyAttribute.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; @@ -7,67 +6,67 @@ namespace SmartStore.Web.Framework.Filters { - public class NotifyAttribute : FilterAttribute, IResultFilter - { - public const string NotificationsKey = "sm.notifications.all"; - - public INotifier Notifier { get; set; } - - public virtual void OnResultExecuting(ResultExecutingContext filterContext) - { - if (Notifier == null || Notifier.Entries.Count == 0) - return; - - if (!filterContext.IsChildAction && filterContext.HttpContext.Request.IsAjaxRequest()) - { - HandleAjaxRequest(Notifier.Entries.FirstOrDefault(), filterContext.HttpContext.Response); - return; - } - - Persist(filterContext.Controller.ViewData, Notifier.Entries.Where(x => x.Durable == false)); - Persist(filterContext.Controller.TempData, Notifier.Entries.Where(x => x.Durable == true)); - - Notifier.Entries.Clear(); - } - - public virtual void OnResultExecuted(ResultExecutedContext filterContext) - { - } - - private void Persist(IDictionary bag, IEnumerable source) - { - if (!source.Any()) - return; - - var existing = (bag[NotificationsKey] ?? new HashSet()) as HashSet; - - source.Each(x => - { - if (x.Message.Text.HasValue()) - existing.Add(x); - }); - - bag[NotificationsKey] = TrimSet(existing); - } - - private void HandleAjaxRequest(NotifyEntry entry, HttpResponseBase response) - { - if (entry == null) - return; - - response.AddHeader("X-Message-Type", entry.Type.ToString().ToLower()); - response.AddHeader("X-Message", entry.Message.Text); - } - - private HashSet TrimSet(HashSet entries) - { - if (entries.Count <= 20) - { - return entries; - } - - return new HashSet(entries.Skip(entries.Count - 20)); - } - } + public class NotifyAttribute : FilterAttribute, IResultFilter + { + public const string NotificationsKey = "sm.notifications.all"; + + public INotifier Notifier { get; set; } + + public virtual void OnResultExecuting(ResultExecutingContext filterContext) + { + if (Notifier == null || Notifier.Entries.Count == 0) + return; + + if (!filterContext.IsChildAction && filterContext.HttpContext.Request.IsAjaxRequest()) + { + HandleAjaxRequest(Notifier.Entries.FirstOrDefault(), filterContext.HttpContext.Response); + return; + } + + Persist(filterContext.Controller.ViewData, Notifier.Entries.Where(x => x.Durable == false)); + Persist(filterContext.Controller.TempData, Notifier.Entries.Where(x => x.Durable == true)); + + Notifier.Entries.Clear(); + } + + public virtual void OnResultExecuted(ResultExecutedContext filterContext) + { + } + + private void Persist(IDictionary bag, IEnumerable source) + { + if (!source.Any()) + return; + + var existing = (bag[NotificationsKey] ?? new HashSet()) as HashSet; + + source.Each(x => + { + if (x.Message.Text.HasValue()) + existing.Add(x); + }); + + bag[NotificationsKey] = TrimSet(existing); + } + + private void HandleAjaxRequest(NotifyEntry entry, HttpResponseBase response) + { + if (entry == null) + return; + + response.AddHeader("X-Message-Type", entry.Type.ToString().ToLower()); + response.AddHeader("X-Message", entry.Message.Text); + } + + private HashSet TrimSet(HashSet entries) + { + if (entries.Count <= 20) + { + return entries; + } + + return new HashSet(entries.Skip(entries.Count - 20)); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/PublicStoreAllowNavigationAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Filters/PublicStoreAllowNavigationAttribute.cs index a9790ec54a..e6d3dea060 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/PublicStoreAllowNavigationAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/PublicStoreAllowNavigationAttribute.cs @@ -3,27 +3,28 @@ using System.Web; using System.Web.Mvc; using SmartStore.Core.Data; -using SmartStore.Services.Security; +using SmartStore.Core.Security; namespace SmartStore.Web.Framework.Filters { - public class PublicStoreAllowNavigationAttribute : FilterAttribute, IActionFilter + public class PublicStoreAllowNavigationAttribute : FilterAttribute, IAuthorizationFilter { - private static readonly List> s_permittedRoutes = new List> - { - new Tuple("SmartStore.Web.Controllers.CustomerController", "Login"), - new Tuple("SmartStore.Web.Controllers.CustomerController", "Logout"), - new Tuple("SmartStore.Web.Controllers.CustomerController", "Register"), - new Tuple("SmartStore.Web.Controllers.CustomerController", "PasswordRecovery"), - new Tuple("SmartStore.Web.Controllers.CustomerController", "PasswordRecoveryConfirm"), - new Tuple("SmartStore.Web.Controllers.CustomerController", "AccountActivation"), - new Tuple("SmartStore.Web.Controllers.CustomerController", "CheckUsernameAvailability"), - new Tuple("SmartStore.Web.Controllers.CatalogController", "OffCanvasMenu") - }; + private static readonly List> s_permittedRoutes = new List> + { + new Tuple("SmartStore.Web.Controllers.CustomerController", "Login"), + new Tuple("SmartStore.Web.Controllers.CustomerController", "Logout"), + new Tuple("SmartStore.Web.Controllers.CustomerController", "Register"), + new Tuple("SmartStore.Web.Controllers.CustomerController", "PasswordRecovery"), + new Tuple("SmartStore.Web.Controllers.CustomerController", "PasswordRecoveryConfirm"), + new Tuple("SmartStore.Web.Controllers.CustomerController", "AccountActivation"), + new Tuple("SmartStore.Web.Controllers.CustomerController", "CheckUsernameAvailability"), + new Tuple("SmartStore.Web.Controllers.MenuController", "OffCanvas"), + new Tuple("SmartStore.Web.Controllers.ShoppingCartController", "CartSummary") + }; + + public Lazy PermissionService { get; set; } - public Lazy PermissionService { get; set; } - - public virtual void OnActionExecuting(ActionExecutingContext filterContext) + public virtual void OnAuthorization(AuthorizationContext filterContext) { if (filterContext == null || filterContext.HttpContext == null) return; @@ -32,44 +33,53 @@ public virtual void OnActionExecuting(ActionExecutingContext filterContext) if (request == null) return; - //don't apply filter to child methods + // Don't apply filter to child methods. if (filterContext.IsChildAction) return; - string actionName = filterContext.ActionDescriptor.ActionName; - if (String.IsNullOrEmpty(actionName)) - return; + string actionName = filterContext.ActionDescriptor.ActionName; + if (String.IsNullOrEmpty(actionName)) + return; - string controllerName = filterContext.Controller.ToString(); - if (String.IsNullOrEmpty(controllerName)) - return; + string controllerName = filterContext.Controller.ToString(); + if (String.IsNullOrEmpty(controllerName)) + return; if (!DataSettings.DatabaseIsInstalled()) return; - var permissionService = PermissionService.Value; - var publicStoreAllowNavigation = permissionService.Authorize(StandardPermissionProvider.PublicStoreAllowNavigation); - if (!publicStoreAllowNavigation && !IsPermittedRoute(controllerName, actionName)) + if (!HasStoreAccess() && !IsPermittedRoute(controllerName, actionName)) { filterContext.Result = new HttpUnauthorizedResult(); } } - public virtual void OnActionExecuted(ActionExecutedContext filterContext) - { - } + protected virtual bool HasStoreAccess() + { + if (PermissionService.Value.Authorize(Permissions.System.AccessShop)) + { + return true; + } + + if (PermissionService.Value.AuthorizeByAlias(Permissions.System.AccessShop)) + { + return true; + } + + return false; + } - private static bool IsPermittedRoute(string controllerName, string actionName) - { - foreach (var route in s_permittedRoutes) - { - if (controllerName.IsCaseInsensitiveEqual(route.Item1) && actionName.IsCaseInsensitiveEqual(route.Item2)) - { - return true; - } - } + private static bool IsPermittedRoute(string controllerName, string actionName) + { + foreach (var route in s_permittedRoutes) + { + if (controllerName.IsCaseInsensitiveEqual(route.Item1) && actionName.IsCaseInsensitiveEqual(route.Item2)) + { + return true; + } + } - return false; - } - } + return false; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/ResponseFilterStream.cs b/src/Presentation/SmartStore.Web.Framework/Filters/ResponseFilterStream.cs new file mode 100644 index 0000000000..3b7905ca67 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Filters/ResponseFilterStream.cs @@ -0,0 +1,281 @@ +using System; +using System.IO; +using System.Web; + +namespace SmartStore.Web.Framework.Filters +{ + /// + /// A semi-generic Stream implementation for Response.Filter with + /// an event interface for handling Content transformations via + /// Stream or String. + /// + /// Use with care for large output as this implementation copies + /// the output into a memory stream and so increases memory usage. + /// + /// + public class ResponseFilterStream : Stream + { + private readonly HttpContextBase _httpContext; + + /// + /// The original stream + /// + private readonly Stream _innerStream; + + /// + /// Current position in the original (inner) stream + /// + private long _position; + + /// + /// Stream that original content is read into + /// and then passed to TransformStream function + /// + private MemoryStream _captureStream; + + /// + /// Internal pointer that that keeps track of the size + /// of the captureStream + /// + private int _capturePointer = 0; + + /// + /// Ctor + /// + /// Original inner stream + /// Initial capacity of the capture stream + public ResponseFilterStream(Stream innerStream, HttpContextBase httpContext, int capacity = 5000) + { + Guard.NotNull(innerStream, nameof(innerStream)); + Guard.NotNull(httpContext, nameof(httpContext)); + + _httpContext = httpContext; + _innerStream = innerStream; + _captureStream = new MemoryStream(capacity); + } + + + /// + /// Determines whether the stream is captured + /// + private bool IsCaptured => CaptureStream != null || CaptureString != null || TransformStream != null || TransformString != null; + + /// + /// Determines whether the Write method is outputting data immediately + /// or delaying output until Flush() is fired. + /// + private bool IsOutputDelayed => TransformStream != null || TransformString != null; + + + /// + /// Event that captures Response output and makes it available + /// as a MemoryStream instance. Output is captured but won't + /// affect Response output. + /// + public event Action CaptureStream; + + /// + /// Event that captures Response output and makes it available + /// as a string. Output is captured but won't affect Response output. + /// + public event Action CaptureString; + + /// + /// Event that allows you to transform the stream as each chunk of + /// the output is written in the Write() operation of the stream. + /// This means that it's possible/likely that the input + /// buffer will not contain the full response output but only + /// one of potentially many chunks. + /// + /// This event is called as part of the filter stream's Write() + /// operation. + /// + public event Func TransformWrite; + + /// + /// Event that allows you to transform the response stream as + /// each chunk of bytep[] output is written during the stream's write + /// operation. This means it's possibly/likely that the string + /// passed to the handler only contains a portion of the full + /// output. Typical buffer chunks are around 16k a piece. + /// + /// This event is called as part of the stream's Write operation. + /// + public event Func TransformWriteString; + + /// + /// This event allows capturing and transformation of the entire + /// output stream by caching all write operations and delaying final + /// response output until Flush() is called on the stream. + /// + public event Func TransformStream; + + /// + /// Event that can be hooked up to handle Response.Filter + /// Transformation. Passed a string that you can modify and + /// return back as a return value. The modified content + /// will become the final output. + /// + public event Func TransformString; + + + protected virtual void OnCaptureStream(MemoryStream ms) + { + CaptureStream?.Invoke(ms); + } + + + private void OnCaptureStringInternal(MemoryStream ms) + { + if (CaptureString != null) + { + var content = _httpContext.Response.ContentEncoding.GetString(ms.ToArray()); + OnCaptureString(content); + } + } + + protected virtual void OnCaptureString(string output) + { + CaptureString?.Invoke(output); + } + + protected virtual byte[] OnTransformWrite(byte[] buffer) + { + if (TransformWrite != null) + return TransformWrite(buffer); + + return buffer; + } + + private byte[] OnTransformWriteStringInternal(byte[] buffer) + { + var encoding = _httpContext.Response.ContentEncoding; + string output = OnTransformWriteString(encoding.GetString(buffer)); + return encoding.GetBytes(output); + } + + private string OnTransformWriteString(string value) + { + if (TransformWriteString != null) + return TransformWriteString(value); + + return value; + } + + + protected virtual MemoryStream OnTransformCompleteStream(MemoryStream ms) + { + if (TransformStream != null) + return TransformStream(ms); + + return ms; + } + + + /// + /// Wrapper method form OnTransformString that handles + /// stream to string and vice versa conversions + /// + /// + /// + internal MemoryStream OnTransformCompleteStringInternal(MemoryStream ms) + { + if (TransformString == null) + return ms; + + var content = _httpContext.Response.ContentEncoding.GetString(ms.ToArray()); + content = TransformString(content); + + var buffer = _httpContext.Response.ContentEncoding.GetBytes(content); + ms = new MemoryStream(); + ms.Write(buffer, 0, buffer.Length); + + return ms; + } + + public override bool CanRead => true; + + public override bool CanSeek => true; + + public override bool CanWrite => true; + + public override long Length => 0; + + public override long Position + { + get => _position; + set => _position = value; + } + + public override long Seek(long offset, SeekOrigin direction) + { + return _innerStream.Seek(offset, direction); + } + + public override void SetLength(long length) + { + _innerStream.SetLength(length); + } + + public override void Close() + { + _innerStream.Close(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + return _innerStream.Read(buffer, offset, count); + } + + /// + /// Overriden to capture output written by ASP.NET and captured + /// into a cached stream that is written out later when Flush() is called. + /// + public override void Write(byte[] buffer, int offset, int count) + { + if (IsCaptured) + { + // Copy to holding buffer only - we'll write out later + _captureStream.Write(buffer, 0, count); + _capturePointer += count; + } + + // Just transform this buffer + if (TransformWrite != null) + buffer = OnTransformWrite(buffer); + + if (TransformWriteString != null) + buffer = OnTransformWriteStringInternal(buffer); + + if (!IsOutputDelayed) + _innerStream.Write(buffer, offset, buffer.Length); + } + + /// + /// Override flush by writing out the captured stream data + /// + public override void Flush() + { + + if (IsCaptured && _captureStream.Length > 0) + { + // Check for transform implementations + _captureStream = OnTransformCompleteStream(_captureStream); + _captureStream = OnTransformCompleteStringInternal(_captureStream); + + OnCaptureStream(_captureStream); + OnCaptureStringInternal(_captureStream); + + // Write the stream back out if output was delayed + if (IsOutputDelayed) + _innerStream.Write(_captureStream.ToArray(), 0, (int)_captureStream.Length); + + // Clear the cache once we've written it out + _captureStream.SetLength(0); + } + + // default flush behavior + _innerStream.Flush(); + } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/StoreClosedAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Filters/StoreClosedAttribute.cs index aec0ac22d8..43cbd1dce2 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/StoreClosedAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/StoreClosedAttribute.cs @@ -11,91 +11,86 @@ namespace SmartStore.Web.Framework.Filters { - public class StoreClosedAttribute : FilterAttribute, IActionFilter + public class StoreClosedAttribute : FilterAttribute, IAuthorizationFilter { - private static readonly List> s_permittedRoutes = new List> - { - new Tuple("SmartStore.Web.Controllers.CustomerController", "Login"), - new Tuple("SmartStore.Web.Controllers.CustomerController", "Logout"), - new Tuple("SmartStore.Web.Controllers.HomeController", "StoreClosed"), - new Tuple("SmartStore.Web.Controllers.CommonController", "SetLanguage") - }; - - public Lazy Localizer { get; set; } - public Lazy Notifier { get; set; } - public Lazy WorkContext { get; set; } - public Lazy StoreInformationSettings { get; set; } - - public virtual void OnActionExecuting(ActionExecutingContext filterContext) + private static readonly List> s_permittedRoutes = new List> { - if (filterContext == null || filterContext.HttpContext == null) - return; + new Tuple("SmartStore.Web.Controllers.CustomerController", "Login"), + new Tuple("SmartStore.Web.Controllers.CustomerController", "Logout"), + new Tuple("SmartStore.Web.Controllers.HomeController", "StoreClosed"), + new Tuple("SmartStore.Web.Controllers.CommonController", "SetLanguage") + }; - HttpRequestBase request = filterContext.HttpContext.Request; - if (request == null) - return; + public Lazy Localizer { get; set; } + public Lazy Notifier { get; set; } + public Lazy WorkContext { get; set; } + public Lazy StoreInformationSettings { get; set; } - //don't apply filter to child methods - if (filterContext.IsChildAction) - return; + public virtual void OnAuthorization(AuthorizationContext filterContext) + { + if (filterContext == null || filterContext.HttpContext == null) + return; - string actionName = filterContext.ActionDescriptor.ActionName; - if (String.IsNullOrEmpty(actionName)) - return; + HttpRequestBase request = filterContext.HttpContext.Request; + if (request == null) + return; - string controllerName = filterContext.Controller.ToString(); - if (String.IsNullOrEmpty(controllerName)) - return; + // Don't apply filter to child methods + if (filterContext.IsChildAction) + return; - if (!DataSettings.DatabaseIsInstalled()) - return; + string actionName = filterContext.ActionDescriptor.ActionName; + if (String.IsNullOrEmpty(actionName)) + return; - var storeInformationSettings = StoreInformationSettings.Value; - if (!storeInformationSettings.StoreClosed) - return; + string controllerName = filterContext.Controller.ToString(); + if (String.IsNullOrEmpty(controllerName)) + return; - if (!IsPermittedRoute(controllerName, actionName)) - { - if (storeInformationSettings.StoreClosedAllowForAdmins && WorkContext.Value.CurrentCustomer.IsAdmin()) - { - //do nothing - allow admin access - } - else - { - if (request.IsAjaxRequest()) - { - var storeClosedMessage = "{0} {1}".FormatCurrentUI( - Localizer.Value.GetResource("StoreClosed", 0, false), - Localizer.Value.GetResource("StoreClosed.Hint", 0, false)); - Notifier.Value.Error(storeClosedMessage); + if (!DataSettings.DatabaseIsInstalled()) + return; - //filterContext.Result = new ContentResult { Content = "", ContentType = "text/html" }; - } - else - { - var storeClosedUrl = new UrlHelper(filterContext.RequestContext).RouteUrl("StoreClosed"); - filterContext.Result = new RedirectResult(storeClosedUrl); - } + var storeInformationSettings = StoreInformationSettings.Value; + if (!storeInformationSettings.StoreClosed) + return; - } - } - } + if (!IsPermittedRoute(controllerName, actionName)) + { + if (storeInformationSettings.StoreClosedAllowForAdmins && WorkContext.Value.CurrentCustomer.IsAdmin()) + { + //do nothing - allow admin access + } + else + { + if (request.IsAjaxRequest()) + { + var storeClosedMessage = "{0} {1}".FormatCurrentUI( + Localizer.Value.GetResource("StoreClosed", 0, false), + Localizer.Value.GetResource("StoreClosed.Hint", 0, false)); + Notifier.Value.Error(storeClosedMessage); - public virtual void OnActionExecuted(ActionExecutedContext filterContext) - { - } + //filterContext.Result = new ContentResult { Content = "", ContentType = "text/html" }; + } + else + { + var storeClosedUrl = new UrlHelper(filterContext.RequestContext).RouteUrl("StoreClosed"); + filterContext.Result = new RedirectResult(storeClosedUrl); + } + } + } + } - private static bool IsPermittedRoute(string controllerName, string actionName) - { - foreach (var route in s_permittedRoutes) - { - if (controllerName.IsCaseInsensitiveEqual(route.Item1) && actionName.IsCaseInsensitiveEqual(route.Item2)) - { - return true; - } - } + private static bool IsPermittedRoute(string controllerName, string actionName) + { + foreach (var route in s_permittedRoutes) + { + if (controllerName.IsCaseInsensitiveEqual(route.Item1) && actionName.IsCaseInsensitiveEqual(route.Item2)) + { + return true; + } + } - return false; - } - } + return false; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/StoreIpAddressAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Filters/StoreIpAddressAttribute.cs index 8a65cd046a..aa57f5c33f 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/StoreIpAddressAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/StoreIpAddressAttribute.cs @@ -9,12 +9,12 @@ namespace SmartStore.Web.Framework.Filters { public class StoreIpAddressAttribute : FilterAttribute, IActionFilter { - public Lazy WebHelper { get; set; } - public Lazy WorkContext { get; set; } - public Lazy CustomerService { get; set; } - public Lazy PrivacySettings { get; set; } + public Lazy WebHelper { get; set; } + public Lazy WorkContext { get; set; } + public Lazy CustomerService { get; set; } + public Lazy PrivacySettings { get; set; } - public virtual void OnActionExecuting(ActionExecutingContext filterContext) + public virtual void OnActionExecuting(ActionExecutingContext filterContext) { if (!DataSettings.DatabaseIsInstalled()) return; @@ -30,26 +30,38 @@ public virtual void OnActionExecuting(ActionExecutingContext filterContext) if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) return; - if (!PrivacySettings.Value.StoreLastIpAddress) - return; + if (!PrivacySettings.Value.StoreLastIpAddress) + return; + + // Update IP address. + var currentIpAddress = WebHelper.Value.GetCurrentIpAddress(); - // Update IP address. - var webHelper = WebHelper.Value; - var currentIpAddress = webHelper.GetCurrentIpAddress(); - if (!String.IsNullOrEmpty(currentIpAddress)) + if (!string.IsNullOrEmpty(currentIpAddress)) { var customer = WorkContext.Value.CurrentCustomer; - if (customer != null && !currentIpAddress.Equals(customer.LastIpAddress, StringComparison.InvariantCultureIgnoreCase)) + + if (customer != null && !customer.Deleted && !customer.IsSystemAccount && + !currentIpAddress.Equals(customer.LastIpAddress, StringComparison.InvariantCultureIgnoreCase)) { - var customerService = CustomerService.Value; - customer.LastIpAddress = currentIpAddress; - customerService.UpdateCustomer(customer); + try + { + customer.LastIpAddress = currentIpAddress; + CustomerService.Value.UpdateCustomer(customer); + } + catch (InvalidOperationException ioe) + { + // The exception may occur on the first call after a migration. + if (!ioe.IsAlreadyAttachedEntityException()) + { + throw; + } + } } } } - public virtual void OnActionExecuted(ActionExecutedContext filterContext) - { - } - } + public virtual void OnActionExecuted(ActionExecutedContext filterContext) + { + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/StoreLastVisitedPageAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Filters/StoreLastVisitedPageAttribute.cs index fdc82d72c6..a9392a363c 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/StoreLastVisitedPageAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/StoreLastVisitedPageAttribute.cs @@ -10,64 +10,73 @@ namespace SmartStore.Web.Framework.Filters { public class StoreLastVisitedPageAttribute : FilterAttribute, IActionFilter { - public Lazy WebHelper { get; set; } - public Lazy WorkContext { get; set; } - public Lazy CustomerSettings { get; set; } - public Lazy GenericAttributeService { get; set; } - public Lazy UserAgent { get; set; } + public Lazy WebHelper { get; set; } + public Lazy WorkContext { get; set; } + public Lazy CustomerSettings { get; set; } + public Lazy GenericAttributeService { get; set; } + public Lazy UserAgent { get; set; } public Lazy CustomerService { get; set; } public virtual void OnActionExecuting(ActionExecutingContext filterContext) { - if (!DataSettings.DatabaseIsInstalled()) + if (!DataSettings.DatabaseIsInstalled()) return; if (filterContext == null || filterContext.HttpContext == null || filterContext.HttpContext.Request == null) return; - // don't apply filter to child methods + // Don't apply filter to child methods. if (filterContext.IsChildAction) return; - //only GET requests + // Only GET requests. if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) return; if (!CustomerSettings.Value.StoreLastVisitedPage) return; - var customer = WorkContext.Value.CurrentCustomer; + var customer = WorkContext.Value.CurrentCustomer; - if (customer == null || customer.IsSystemAccount) - return; - - var pageUrl = WebHelper.Value.GetThisPageUrl(true); - var userAgent = UserAgent.Value.RawValue; + if (customer == null || customer.Deleted || customer.IsSystemAccount) + return; - var genericAttributeService = GenericAttributeService.Value; + var pageUrl = WebHelper.Value.GetThisPageUrl(true); + var userAgent = UserAgent.Value.RawValue; - if (pageUrl.HasValue()) + if (pageUrl.HasValue()) { - var previousPageUrl = customer.GetAttribute(SystemCustomerAttributeNames.LastVisitedPage); - if (!pageUrl.IsCaseInsensitiveEqual(previousPageUrl)) - { - genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.LastVisitedPage, pageUrl); - } + var previousPageUrl = customer.GetAttribute(SystemCustomerAttributeNames.LastVisitedPage); + if (!pageUrl.IsCaseInsensitiveEqual(previousPageUrl)) + { + GenericAttributeService.Value.SaveAttribute(customer, SystemCustomerAttributeNames.LastVisitedPage, pageUrl); + } } - if (userAgent.HasValue()) - { - var previousUserAgent = customer.LastUserAgent; - if (!userAgent.IsCaseInsensitiveEqual(previousUserAgent)) - { - customer.LastUserAgent = userAgent; - CustomerService.Value.UpdateCustomer(customer); - } - } - } + if (userAgent.HasValue()) + { + var previousUserAgent = customer.LastUserAgent; + if (!userAgent.IsCaseInsensitiveEqual(previousUserAgent)) + { + try + { + customer.LastUserAgent = userAgent; + CustomerService.Value.UpdateCustomer(customer); + } + catch (InvalidOperationException ioe) + { + // The exception may occur on the first call after a migration. + if (!ioe.IsAlreadyAttachedEntityException()) + { + throw; + } + } + } + } + } - public virtual void OnActionExecuted(ActionExecutedContext filterContext) - { - } - } + public virtual void OnActionExecuted(ActionExecutedContext filterContext) + { + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Filters/UnitOfWorkAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Filters/UnitOfWorkAttribute.cs index 2721a44f65..93eddf7752 100644 --- a/src/Presentation/SmartStore.Web.Framework/Filters/UnitOfWorkAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Filters/UnitOfWorkAttribute.cs @@ -7,12 +7,12 @@ namespace SmartStore.Web.Framework.Filters [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class UnitOfWorkAttribute : ActionFilterAttribute { - public UnitOfWorkAttribute() + public UnitOfWorkAttribute() : this(null, int.MaxValue) { } - public UnitOfWorkAttribute(int ordinal) + public UnitOfWorkAttribute(int ordinal) : this(null, ordinal) { } @@ -28,7 +28,7 @@ public UnitOfWorkAttribute(string alias, int ordinal) base.Order = ordinal; } - public Func DbContext { get; set; } + public Func DbContext { get; set; } public string Alias { get; set; } @@ -37,8 +37,8 @@ public override void OnActionExecuted(ActionExecutedContext filterContext) if (filterContext.Exception != null) return; - var context = DbContext(this.Alias); - + var context = DbContext(this.Alias); + if (context != null && context.HasChanges) { try diff --git a/src/Presentation/SmartStore.Web.Framework/FrameworkCacheConsumer.cs b/src/Presentation/SmartStore.Web.Framework/FrameworkCacheConsumer.cs index 5dee2d6c96..13a5991802 100644 --- a/src/Presentation/SmartStore.Web.Framework/FrameworkCacheConsumer.cs +++ b/src/Presentation/SmartStore.Web.Framework/FrameworkCacheConsumer.cs @@ -26,8 +26,8 @@ public partial class FrameworkCacheConsumer : DbSaveHook, IConsumer /// {1} : store identifier /// public const string THEMEVARS_KEY = "fw:themevars-{0}-{1}"; - public const string THEMEVARS_THEME_KEY = "fw:themevars-{0}"; - + public const string THEMEVARS_THEME_KEY = "fw:themevars-{0}"; + /// /// Key for tax display type caching /// @@ -38,75 +38,75 @@ public partial class FrameworkCacheConsumer : DbSaveHook, IConsumer public const string CUSTOMERROLES_TAX_DISPLAY_TYPES_KEY = "fw:customerroles:taxdisplaytypes-{0}-{1}"; public const string CUSTOMERROLES_TAX_DISPLAY_TYPES_PATTERN_KEY = "fw:customerroles:taxdisplaytypes*"; - private readonly ICacheManager _cacheManager; - private readonly IAssetCache _assetCache; + private readonly ICacheManager _cacheManager; + private readonly IAssetCache _assetCache; + + // Item1 = ThemeName, Item2 = StoreId + private HashSet> _themeScopes; + + public FrameworkCacheConsumer(ICacheManager cacheManager, IAssetCache assetCache) + { + _cacheManager = cacheManager; + _assetCache = assetCache; + } + + public void HandleEvent(ThemeTouchedEvent eventMessage) + { + var cacheKey = BuildThemeVarsCacheKey(eventMessage.ThemeName, 0); + HttpRuntime.Cache.RemoveByPattern(cacheKey); + } + + public override void OnAfterSave(IHookedEntity entry) + { + if (entry.Entity is ThemeVariable) + { + var themeVar = entry.Entity as ThemeVariable; + AddEvictableThemeScope(themeVar.Theme, themeVar.StoreId); + } + else if (entry.Entity is CustomerRole) + { + _cacheManager.RemoveByPattern(CUSTOMERROLES_TAX_DISPLAY_TYPES_PATTERN_KEY); + } + else if (entry.Entity is Setting && entry.InitialState == EntityState.Modified) + { + var setting = entry.Entity as Setting; + if (setting.Name.IsCaseInsensitiveEqual(TypeHelper.NameOf(x => x.TaxDisplayType, true))) + { + _cacheManager.RemoveByPattern(CUSTOMERROLES_TAX_DISPLAY_TYPES_PATTERN_KEY); // depends on TaxSettings.TaxDisplayType + } + } + else + { + throw new NotImplementedException(); + } + } + + public override void OnAfterSaveCompleted() + { + FlushThemeVarsCacheEviction(); + } - // Item1 = ThemeName, Item2 = StoreId - private HashSet> _themeScopes; + #region Helpers - public FrameworkCacheConsumer(ICacheManager cacheManager, IAssetCache assetCache) + private void AddEvictableThemeScope(string themeName, int storeId) { - _cacheManager = cacheManager; - _assetCache = assetCache; + if (_themeScopes == null) + _themeScopes = new HashSet>(); + _themeScopes.Add(new Tuple(themeName, storeId)); } - public void HandleEvent(ThemeTouchedEvent eventMessage) - { - var cacheKey = BuildThemeVarsCacheKey(eventMessage.ThemeName, 0); - HttpRuntime.Cache.RemoveByPattern(cacheKey); - } - - public override void OnAfterSave(IHookedEntity entry) - { - if (entry.Entity is ThemeVariable) - { - var themeVar = entry.Entity as ThemeVariable; - AddEvictableThemeScope(themeVar.Theme, themeVar.StoreId); - } - else if (entry.Entity is CustomerRole) - { - _cacheManager.RemoveByPattern(CUSTOMERROLES_TAX_DISPLAY_TYPES_PATTERN_KEY); - } - else if (entry.Entity is Setting && entry.InitialState == EntityState.Modified) - { - var setting = entry.Entity as Setting; - if (setting.Name.IsCaseInsensitiveEqual(TypeHelper.NameOf(x => x.TaxDisplayType, true))) - { - _cacheManager.RemoveByPattern(CUSTOMERROLES_TAX_DISPLAY_TYPES_PATTERN_KEY); // depends on TaxSettings.TaxDisplayType - } - } - else - { - throw new NotImplementedException(); - } - } - - public override void OnAfterSaveCompleted() - { - FlushThemeVarsCacheEviction(); - } - - #region Helpers - - private void AddEvictableThemeScope(string themeName, int storeId) - { - if (_themeScopes == null) - _themeScopes = new HashSet>(); - _themeScopes.Add(new Tuple(themeName, storeId)); - } - - private void FlushThemeVarsCacheEviction() - { - if (_themeScopes == null || _themeScopes.Count == 0) - return; - - foreach (var scope in _themeScopes) - { - HttpRuntime.Cache.Remove(BuildThemeVarsCacheKey(scope.Item1 /* ThemeName */, scope.Item2 /* StoreId */)); - } - - _themeScopes.Clear(); - } + private void FlushThemeVarsCacheEviction() + { + if (_themeScopes == null || _themeScopes.Count == 0) + return; + + foreach (var scope in _themeScopes) + { + HttpRuntime.Cache.Remove(BuildThemeVarsCacheKey(scope.Item1 /* ThemeName */, scope.Item2 /* StoreId */)); + } + + _themeScopes.Clear(); + } private static string BuildThemeVarsCacheKey(ThemeVariable entity) { @@ -115,14 +115,14 @@ private static string BuildThemeVarsCacheKey(ThemeVariable entity) internal static string BuildThemeVarsCacheKey(string themeName, int storeId) { - if (storeId > 0) - { - return HttpRuntime.Cache.BuildScopedKey(THEMEVARS_KEY.FormatInvariant(themeName, storeId)); - } + if (storeId > 0) + { + return HttpRuntime.Cache.BuildScopedKey(THEMEVARS_KEY.FormatInvariant(themeName, storeId)); + } - return HttpRuntime.Cache.BuildScopedKey(THEMEVARS_THEME_KEY.FormatInvariant(themeName)); + return HttpRuntime.Cache.BuildScopedKey(THEMEVARS_THEME_KEY.FormatInvariant(themeName)); } #endregion - } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Localization/ErrorMessageProvider.cs b/src/Presentation/SmartStore.Web.Framework/Localization/ErrorMessageProvider.cs index c927fc2d38..972cb931f1 100644 --- a/src/Presentation/SmartStore.Web.Framework/Localization/ErrorMessageProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/Localization/ErrorMessageProvider.cs @@ -4,74 +4,74 @@ namespace SmartStore.Web.Framework.Localization { - public class ErrorMessageProvider - { - private static string _resourceClassKey; - - public static void SetResourceClassKey(string resourceClassKey) - { - if (resourceClassKey == null) - { - throw new ArgumentNullException("resourceClassKey"); - } + public class ErrorMessageProvider + { + private static string _resourceClassKey; - _resourceClassKey = resourceClassKey; + public static void SetResourceClassKey(string resourceClassKey) + { + if (resourceClassKey == null) + { + throw new ArgumentNullException("resourceClassKey"); + } - ModelBinderErrorMessageProviders.ValueRequiredErrorMessageProvider = ErrorMessageProvider.DefaultValueRequiredErrorMessageProvider; - ModelBinderErrorMessageProviders.TypeConversionErrorMessageProvider = ErrorMessageProvider.DefaultTypeConversionErrorMessageProvider; - } + _resourceClassKey = resourceClassKey; - private static string DefaultTypeConversionErrorMessageProvider(ModelBindingExecutionContext modelBindingExecutionContext, ModelMetadata modelMetadata, object incomingValue) - { - return GetResourceCommon(modelBindingExecutionContext, modelMetadata, incomingValue, new Func(GetValueInvalidResource)); - } - - private static string DefaultValueRequiredErrorMessageProvider(ModelBindingExecutionContext modelBindingExecutionContext, ModelMetadata modelMetadata, object incomingValue) - { - return GetResourceCommon(modelBindingExecutionContext, modelMetadata, incomingValue, new Func(GetValueRequiredResource)); - } + ModelBinderErrorMessageProviders.ValueRequiredErrorMessageProvider = ErrorMessageProvider.DefaultValueRequiredErrorMessageProvider; + ModelBinderErrorMessageProviders.TypeConversionErrorMessageProvider = ErrorMessageProvider.DefaultTypeConversionErrorMessageProvider; + } - private static string GetResourceCommon(ModelBindingExecutionContext modelBindingExecutionContext, ModelMetadata modelMetadata, object incomingValue, - Func resourceAccessor) - { - string displayName = modelMetadata.GetDisplayName(); - string str = resourceAccessor(modelBindingExecutionContext); - object[] objArray = new object[2]; + private static string DefaultTypeConversionErrorMessageProvider(ModelBindingExecutionContext modelBindingExecutionContext, ModelMetadata modelMetadata, object incomingValue) + { + return GetResourceCommon(modelBindingExecutionContext, modelMetadata, incomingValue, new Func(GetValueInvalidResource)); + } - objArray[0] = incomingValue; - objArray[1] = displayName; + private static string DefaultValueRequiredErrorMessageProvider(ModelBindingExecutionContext modelBindingExecutionContext, ModelMetadata modelMetadata, object incomingValue) + { + return GetResourceCommon(modelBindingExecutionContext, modelMetadata, incomingValue, new Func(GetValueRequiredResource)); + } - string str1 = string.Format(CultureInfo.CurrentCulture, str, objArray); - return str1; - } + private static string GetResourceCommon(ModelBindingExecutionContext modelBindingExecutionContext, ModelMetadata modelMetadata, object incomingValue, + Func resourceAccessor) + { + string displayName = modelMetadata.GetDisplayName(); + string str = resourceAccessor(modelBindingExecutionContext); + object[] objArray = new object[2]; - private static string GetUserResourceString(ModelBindingExecutionContext modelBindingExecutionContext, string resourceName, string resourceClassKey) - { - if (string.IsNullOrEmpty(resourceClassKey) || modelBindingExecutionContext == null || modelBindingExecutionContext.HttpContext == null) - { - return null; - } - else - { - return modelBindingExecutionContext.HttpContext.GetGlobalResourceObject(resourceClassKey, resourceName, CultureInfo.CurrentUICulture) as string; - } - } - - private static string GetUserResourceString(ModelBindingExecutionContext modelBindingExecutionContext, string resourceName) - { - return GetUserResourceString(modelBindingExecutionContext, resourceName, _resourceClassKey); - } - - private static string GetValueInvalidResource(ModelBindingExecutionContext modelBindingExecutionContext) - { - string userResourceString = GetUserResourceString(modelBindingExecutionContext, "PropertyValueInvalid"); - return userResourceString; - } - - private static string GetValueRequiredResource(ModelBindingExecutionContext modelBindingExecutionContext) - { - string userResourceString = GetUserResourceString(modelBindingExecutionContext, "PropertyValueRequired"); - return userResourceString; - } - } + objArray[0] = incomingValue; + objArray[1] = displayName; + + string str1 = string.Format(CultureInfo.CurrentCulture, str, objArray); + return str1; + } + + private static string GetUserResourceString(ModelBindingExecutionContext modelBindingExecutionContext, string resourceName, string resourceClassKey) + { + if (string.IsNullOrEmpty(resourceClassKey) || modelBindingExecutionContext == null || modelBindingExecutionContext.HttpContext == null) + { + return null; + } + else + { + return modelBindingExecutionContext.HttpContext.GetGlobalResourceObject(resourceClassKey, resourceName, CultureInfo.CurrentUICulture) as string; + } + } + + private static string GetUserResourceString(ModelBindingExecutionContext modelBindingExecutionContext, string resourceName) + { + return GetUserResourceString(modelBindingExecutionContext, resourceName, _resourceClassKey); + } + + private static string GetValueInvalidResource(ModelBindingExecutionContext modelBindingExecutionContext) + { + string userResourceString = GetUserResourceString(modelBindingExecutionContext, "PropertyValueInvalid"); + return userResourceString; + } + + private static string GetValueRequiredResource(ModelBindingExecutionContext modelBindingExecutionContext) + { + string userResourceString = GetUserResourceString(modelBindingExecutionContext, "PropertyValueRequired"); + return userResourceString; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Localization/ILocalizedModel.cs b/src/Presentation/SmartStore.Web.Framework/Localization/ILocalizedModel.cs index d91f9f2ad8..e9524c34ea 100644 --- a/src/Presentation/SmartStore.Web.Framework/Localization/ILocalizedModel.cs +++ b/src/Presentation/SmartStore.Web.Framework/Localization/ILocalizedModel.cs @@ -6,13 +6,13 @@ public interface ILocalizedModel { } - public interface ILocalizedModel : ILocalizedModel where T : ILocalizedModelLocal + public interface ILocalizedModel : ILocalizedModel where T : ILocalizedModelLocal { IList Locales { get; set; } } - public interface ILocalizedModelLocal - { - int LanguageId { get; set; } - } -} + public interface ILocalizedModelLocal + { + int LanguageId { get; set; } + } +} \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Localization/IText.cs b/src/Presentation/SmartStore.Web.Framework/Localization/IText.cs index ec648c3377..732cea82eb 100644 --- a/src/Presentation/SmartStore.Web.Framework/Localization/IText.cs +++ b/src/Presentation/SmartStore.Web.Framework/Localization/IText.cs @@ -1,11 +1,10 @@ -using System; -using SmartStore.Core.Localization; +using SmartStore.Core.Localization; namespace SmartStore.Web.Framework.Localization { - public interface IText - { - LocalizedString Get(string key, params object[] args); - LocalizedString GetEx(string key, int languageId, params object[] args); - } + public interface IText + { + LocalizedString Get(string key, params object[] args); + LocalizedString GetEx(string key, int languageId, params object[] args); + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Localization/LanguageSeoCodeAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Localization/LanguageSeoCodeAttribute.cs index 28055079b9..bdb4489842 100644 --- a/src/Presentation/SmartStore.Web.Framework/Localization/LanguageSeoCodeAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Localization/LanguageSeoCodeAttribute.cs @@ -1,24 +1,23 @@ using System; -using System.Net; -using System.Web; using System.Web.Mvc; using SmartStore.Core; using SmartStore.Core.Data; using SmartStore.Core.Domain.Localization; using SmartStore.Services.Localization; +using SmartStore.Web.Framework.Filters; namespace SmartStore.Web.Framework.Localization { /// /// Attribute which ensures that store URL contains a language SEO code if "SEO friendly URLs with multiple languages" setting is enabled /// - public class LanguageSeoCodeAttribute : FilterAttribute, IActionFilter + public class LanguageSeoCodeAttribute : FilterAttribute, IAuthorizationFilter { - public Lazy WorkContext { get; set; } - public Lazy LanguageService { get; set; } - public Lazy LocalizationSettings { get; set; } + public Lazy WorkContext { get; set; } + public Lazy LanguageService { get; set; } + public Lazy LocalizationSettings { get; set; } - public virtual void OnActionExecuting(ActionExecutingContext filterContext) + public virtual void OnAuthorization(AuthorizationContext filterContext) { var request = filterContext?.HttpContext?.Request; if (request == null) @@ -29,81 +28,71 @@ public virtual void OnActionExecuting(ActionExecutingContext filterContext) return; //only GET requests - if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) return; // ensure that this route is registered and localizable (LocalizedRoute in RouteProvider.cs) - if (filterContext.RouteData == null || filterContext.RouteData.Route == null || !(filterContext.RouteData.Route is LocalizedRoute)) + if (!(filterContext.RouteData?.Route is LocalizedRoute)) return; - if (!DataSettings.DatabaseIsInstalled()) + if (!DataSettings.DatabaseIsInstalled()) return; var localizationSettings = LocalizationSettings.Value; if (!localizationSettings.SeoFriendlyUrlsForLanguagesEnabled) return; - - // process current URL - var workContext = WorkContext.Value; - var languageService = LanguageService.Value; + + // Process current URL + var workContext = WorkContext.Value; + var languageService = LanguageService.Value; var workingLanguage = workContext.WorkingLanguage; - var helper = new LocalizedUrlHelper(filterContext.HttpContext.Request, true); - string defaultSeoCode = languageService.GetDefaultLanguageSeoCode(); - - string seoCode; - if (helper.IsLocalizedUrl(out seoCode)) + var helper = new LocalizedUrlHelper(request, true); + var defaultSeoCode = languageService.GetDefaultLanguageSeoCode(); + + if (helper.IsLocalizedUrl(out var seoCode)) { - if (!languageService.IsPublishedLanguage(seoCode)) + if (!languageService.IsPublishedLanguage(seoCode)) { - var descriptor = filterContext.ActionDescriptor; - - // language is not defined in system or not assigned to store - if (localizationSettings.InvalidLanguageRedirectBehaviour == InvalidLanguageRedirectBehaviour.ReturnHttp404) + // Language is not defined in system or not assigned to store + if (localizationSettings.InvalidLanguageRedirectBehaviour == InvalidLanguageRedirectBehaviour.ReturnHttp404) { - filterContext.Result = new ViewResult - { - ViewName = "NotFound", - MasterName = (string)null, - ViewData = new ViewDataDictionary(new HandleErrorInfo(new HttpException(404, "The resource does not exist."), descriptor.ActionName, descriptor.ControllerDescriptor.ControllerName)), - TempData = filterContext.Controller.TempData - }; - filterContext.RouteData.Values["StripInvalidSeoCode"] = true; - filterContext.RequestContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; - filterContext.RequestContext.HttpContext.Response.TrySkipIisCustomErrors = true; + filterContext.Result = HandleExceptionFilter.Create404Result(filterContext); + + var seoCodeReplacement = localizationSettings.DefaultLanguageRedirectBehaviour == DefaultLanguageRedirectBehaviour.PrependSeoCodeAndRedirect + ? workingLanguage.GetTwoLetterISOLanguageName() + : string.Empty; + + filterContext.RequestContext.RouteData.DataTokens["SeoCodeReplacement"] = seoCodeReplacement; } else if (localizationSettings.InvalidLanguageRedirectBehaviour == InvalidLanguageRedirectBehaviour.FallbackToWorkingLanguage) { helper.StripSeoCode(); - filterContext.Result = new RedirectResult(helper.GetAbsolutePath(), true); + filterContext.Result = new RedirectResult(helper.GetAbsolutePath(), !request.IsLocal); } } else { - // redirect default language (if desired) + // Redirect default language (if desired) if (seoCode == defaultSeoCode && localizationSettings.DefaultLanguageRedirectBehaviour == DefaultLanguageRedirectBehaviour.StripSeoCode) { helper.StripSeoCode(); - filterContext.Result = new RedirectResult(helper.GetAbsolutePath(), true); + filterContext.Result = new RedirectResult(helper.GetAbsolutePath(), !request.IsLocal); } } - // already localized URL, skip the rest + // Already localized URL, skip the rest return; } - // keep default language prefixless (if desired) + // Keep default language prefixless (if desired) if (workingLanguage.UniqueSeoCode == defaultSeoCode && (int)(localizationSettings.DefaultLanguageRedirectBehaviour) > 0) { return; } - // add language code to URL + // Add language code to URL helper.PrependSeoCode(workingLanguage.UniqueSeoCode); filterContext.Result = new RedirectResult(helper.GetAbsolutePath()); } - - public virtual void OnActionExecuted(ActionExecutedContext filterContext) - { - } - } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Localization/LocalizedRoute.cs b/src/Presentation/SmartStore.Web.Framework/Localization/LocalizedRoute.cs index 42fba7cd2b..dba94ca1cc 100644 --- a/src/Presentation/SmartStore.Web.Framework/Localization/LocalizedRoute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Localization/LocalizedRoute.cs @@ -18,7 +18,7 @@ public class LocalizedRoute : Route, ICloneable #region Fields private static bool? _seoFriendlyUrlsForLanguagesEnabled; - private string _leftPart; + private string _leftPart; #endregion @@ -89,21 +89,23 @@ public override RouteData GetRouteData(HttpContextBase httpContext) var helper = new LocalizedUrlHelper(httpContext.Request); if (helper.IsLocalizedUrl()) { + httpContext.RememberAppRelativePath(); + helper.StripSeoCode(); httpContext.RewritePath("~/" + helper.RelativePath, true); } } - if (_leftPart == null) - { - var url = this.Url; - int idx = url.IndexOf('{'); - _leftPart = "~/" + (idx >= 0 ? url.Substring(0, idx) : url).TrimEnd('/'); - } + if (_leftPart == null) + { + var url = this.Url; + int idx = url.IndexOf('{'); + _leftPart = "~/" + (idx >= 0 ? url.Substring(0, idx) : url).TrimEnd('/'); + } - // Perf - if (!httpContext.Request.AppRelativeCurrentExecutionFilePath.StartsWith(_leftPart, true, CultureInfo.InvariantCulture)) - return null; + // Perf + if (!httpContext.Request.AppRelativeCurrentExecutionFilePath.StartsWith(_leftPart, true, CultureInfo.InvariantCulture)) + return null; RouteData data = base.GetRouteData(httpContext); return data; @@ -119,19 +121,28 @@ public override RouteData GetRouteData(HttpContextBase httpContext) /// public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) { - VirtualPathData data = base.GetVirtualPath(requestContext, values); + var data = base.GetVirtualPath(requestContext, values); if (data != null && DataSettings.DatabaseIsInstalled() && SeoFriendlyUrlsForLanguagesEnabled) { var helper = new LocalizedUrlHelper(requestContext.HttpContext.Request, true); - if (helper.IsLocalizedUrl(out string cultureCode)) - { - if (!requestContext.RouteData.Values.ContainsKey("StripInvalidSeoCode")) - { - data.VirtualPath = String.Concat(cultureCode, "/", data.VirtualPath).TrimEnd('/'); - } - } - } + if (helper.IsLocalizedUrl(out string cultureCode)) + { + if (requestContext.RouteData.DataTokens.Get("SeoCodeReplacement") is string seoCodeReplacement) + { + // The LanguageSeoCode filter detected a localized URL, but the locale does not exist or is inactive. + // The routing system is therefore about to render the "NotFound" view. Here we ensure that generated links + // in NotFound page do not contain the invalid seo code anymore: Either we strip it off or we replace it + // with the default language's seo code (according to "LocalizationSettings.DefaultLanguageRedirectBehaviour" setting). + cultureCode = seoCodeReplacement; + } + + if (cultureCode.HasValue()) + { + data.VirtualPath = String.Concat(cultureCode, "/", data.VirtualPath).TrimEnd('/'); + } + } + } return data; } @@ -152,16 +163,16 @@ protected internal static bool SeoFriendlyUrlsForLanguagesEnabled get { if (_seoFriendlyUrlsForLanguagesEnabled == null && EngineContext.Current.IsFullyInitialized) - { - try - { - var enabled = EngineContext.Current.Resolve().SeoFriendlyUrlsForLanguagesEnabled; - _seoFriendlyUrlsForLanguagesEnabled = enabled; - } - catch { } - } - - // Assume is enabled on very first request to prevent IIS 404 with localized URLs + { + try + { + var enabled = EngineContext.Current.Resolve().SeoFriendlyUrlsForLanguagesEnabled; + _seoFriendlyUrlsForLanguagesEnabled = enabled; + } + catch { } + } + + // Assume is enabled on very first request to prevent IIS 404 with localized URLs return _seoFriendlyUrlsForLanguagesEnabled ?? true; } } @@ -172,16 +183,16 @@ protected internal static bool SeoFriendlyUrlsForLanguagesEnabled public LocalizedRoute Clone() { - var clone = new LocalizedRoute(this.Url, - new RouteValueDictionary(this.Defaults), - new RouteValueDictionary(this.Constraints), - new RouteValueDictionary(this.DataTokens), - new MvcRouteHandler()) - { - RouteExistingFiles = this.RouteExistingFiles, - IsClone = true - }; - return clone; + var clone = new LocalizedRoute(this.Url, + new RouteValueDictionary(this.Defaults), + new RouteValueDictionary(this.Constraints), + new RouteValueDictionary(this.DataTokens), + new MvcRouteHandler()) + { + RouteExistingFiles = this.RouteExistingFiles, + IsClone = true + }; + return clone; } object ICloneable.Clone() diff --git a/src/Presentation/SmartStore.Web.Framework/Localization/LocalizedRouteExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Localization/LocalizedRouteExtensions.cs index 47c24c873a..aeb0c37107 100644 --- a/src/Presentation/SmartStore.Web.Framework/Localization/LocalizedRouteExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Localization/LocalizedRouteExtensions.cs @@ -1,6 +1,4 @@ -using System; -using System.Linq; -using System.Web.Mvc; +using System.Web.Mvc; using System.Web.Routing; namespace SmartStore.Web.Framework.Localization @@ -34,16 +32,20 @@ public static Route MapLocalizedRoute(this RouteCollection routes, string name, return MapLocalizedRoute(routes, name, url, defaults, null /* constraints */, namespaces); } + public static Route CreateLocalizedRoute(this RouteCollection routes, string url, object defaults, string[] namespaces) + { + return MapLocalizedRouteInternal(routes, null /* name */, url, defaults, null /* constraints */, namespaces, false); + } + public static Route MapLocalizedRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { - if (routes == null) - { - throw new ArgumentNullException("routes"); - } - if (url == null) - { - throw new ArgumentNullException("url"); - } + return MapLocalizedRouteInternal(routes, name, url, defaults, constraints, namespaces, true); + } + + private static Route MapLocalizedRouteInternal(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces, bool add) + { + Guard.NotNull(routes, nameof(routes)); + Guard.NotNull(url, nameof(url)); var route = new LocalizedRoute(url, new MvcRouteHandler()) { @@ -57,7 +59,10 @@ public static Route MapLocalizedRoute(this RouteCollection routes, string name, route.DataTokens["Namespaces"] = namespaces; } - routes.Add(name, route); + if (add) + { + routes.Add(name, route); + } return route; } diff --git a/src/Presentation/SmartStore.Web.Framework/Localization/SetWorkingCultureAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Localization/SetWorkingCultureAttribute.cs index 5e81f9cc77..d7f61ca73d 100644 --- a/src/Presentation/SmartStore.Web.Framework/Localization/SetWorkingCultureAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Localization/SetWorkingCultureAttribute.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Text; using System.Threading; -using System.Web; using System.Web.Mvc; using Newtonsoft.Json; +using SmartStore.ComponentModel; using SmartStore.Core; using SmartStore.Core.Data; -using SmartStore.Core.Domain.Localization; +using SmartStore.Utilities.ObjectPools; using SmartStore.Web.Framework.UI; namespace SmartStore.Web.Framework.Localization @@ -18,151 +17,161 @@ namespace SmartStore.Web.Framework.Localization ///
public class SetWorkingCultureAttribute : FilterAttribute, IAuthorizationFilter, IActionFilter { - public Lazy WorkContext { get; set; } - public Lazy AssetBuilder { get; set; } + // DIN 5008. + private static string[] _deMonthAbbreviations = new[] { "Jan.", "Feb.", "März", "Apr.", "Mai", "Juni", "Juli", "Aug.", "Sept.", "Okt.", "Nov.", "Dez.", "" }; - public void OnAuthorization(AuthorizationContext filterContext) + public Lazy WorkContext { get; set; } + public Lazy AssetBuilder { get; set; } + + public void OnAuthorization(AuthorizationContext filterContext) { var request = filterContext?.HttpContext?.Request; if (request == null) return; - if (filterContext.IsChildAction) - return; + if (filterContext.IsChildAction) + return; - if (!DataSettings.DatabaseIsInstalled()) + if (!DataSettings.DatabaseIsInstalled()) return; var workContext = WorkContext.Value; + var language = workContext.WorkingLanguage; + + var culture = workContext.CurrentCustomer != null && language != null + ? new CultureInfo(language.LanguageCulture) + : new CultureInfo("en-US"); - CultureInfo culture = workContext.CurrentCustomer != null && workContext.WorkingLanguage != null - ? new CultureInfo(workContext.WorkingLanguage.LanguageCulture) - : new CultureInfo("en-US"); + if (language?.UniqueSeoCode?.IsCaseInsensitiveEqual("de") ?? false) + { + culture.DateTimeFormat.AbbreviatedMonthNames = culture.DateTimeFormat.AbbreviatedMonthGenitiveNames = _deMonthAbbreviations; + } Thread.CurrentThread.CurrentCulture = culture; Thread.CurrentThread.CurrentUICulture = culture; } - public void OnActionExecuting(ActionExecutingContext filterContext) - { - } - - public void OnActionExecuted(ActionExecutedContext filterContext) - { - if (filterContext.IsChildAction) - return; - - if (!DataSettings.DatabaseIsInstalled()) - return; - - if (!(filterContext.Result is ViewResult)) - return; - - var culture = Thread.CurrentThread.CurrentUICulture; - if (culture.Name == "en-US") - return; - - var builder = AssetBuilder.Value; - var json = CreateCultureJson(culture); - - var sb = new StringBuilder(); - sb.Append(""); - - var script = sb.ToString(); - - builder.AppendCustomHeadParts(script); - } - - private string CreateCultureJson(CultureInfo ci) - { - var nf = ci.NumberFormat; - var df = ci.DateTimeFormat; - - var dict = new Dictionary - { - { "name", ci.Name }, - { "englishName", ci.EnglishName }, - { "nativeName", ci.NativeName }, - { "isRTL", WorkContext.Value.WorkingLanguage?.Rtl ?? ci.TextInfo.IsRightToLeft }, // favor RTL property of Language + public void OnActionExecuting(ActionExecutingContext filterContext) + { + } + + public void OnActionExecuted(ActionExecutedContext filterContext) + { + if (filterContext.IsChildAction) + return; + + if (!DataSettings.DatabaseIsInstalled()) + return; + + if (!(filterContext.Result is ViewResult)) + return; + + var culture = Thread.CurrentThread.CurrentUICulture; + if (culture.Name == "en-US") + return; + + var builder = AssetBuilder.Value; + var json = CreateCultureJson(culture); + + var sb = PooledStringBuilder.Rent(); + sb.Append(""); + + var script = sb.ToStringAndReturn(); + + builder.AppendCustomHeadParts(script); + } + + private string CreateCultureJson(CultureInfo ci) + { + var nf = ci.NumberFormat; + var df = ci.DateTimeFormat; + + var dict = new Dictionary + { + { "name", ci.Name }, + { "englishName", ci.EnglishName }, + { "nativeName", ci.NativeName }, + { "isRTL", WorkContext.Value.WorkingLanguage?.Rtl ?? ci.TextInfo.IsRightToLeft }, // favor RTL property of Language { "language", ci.TwoLetterISOLanguageName }, - { "numberFormat", new Dictionary - { - { ",", nf.NumberGroupSeparator }, - { ".", nf.NumberDecimalSeparator }, - { "pattern", new[] { nf.NumberNegativePattern } }, - { "decimals", nf.NumberDecimalDigits }, - { "groupSizes", nf.NumberGroupSizes }, - { "+", nf.PositiveSign }, - { "-", nf.NegativeSign }, - { "NaN", nf.NaNSymbol }, - { "negativeInfinity", nf.NegativeInfinitySymbol }, - { "positiveInfinity", nf.PositiveInfinitySymbol }, - { "percent", new Dictionary - { - { ",", nf.PercentGroupSeparator }, - { ".", nf.PercentDecimalSeparator }, - { "pattern", new[] { nf.PercentNegativePattern, nf.PercentPositivePattern } }, - { "decimals", nf.PercentDecimalDigits }, - { "groupSizes", nf.PercentGroupSizes }, - { "symbol", nf.PercentSymbol } - } }, - { "currency", new Dictionary - { - { ",", nf.CurrencyGroupSeparator }, - { ".", nf.CurrencyDecimalSeparator }, - { "pattern", new[] { nf.CurrencyNegativePattern, nf.CurrencyPositivePattern } }, - { "decimals", nf.CurrencyDecimalDigits }, - { "groupSizes", nf.CurrencyGroupSizes }, - { "symbol", nf.CurrencySymbol } - } }, - } }, - { "dateTimeFormat", new Dictionary - { - { "calendarName", df.NativeCalendarName }, - { "/", df.DateSeparator }, - { ":", df.TimeSeparator }, - { "firstDay", (int)df.FirstDayOfWeek }, - { "twoDigitYearMax", ci.Calendar.TwoDigitYearMax }, - { "AM", df.AMDesignator.IsEmpty() ? null : new[] { df.AMDesignator, df.AMDesignator.ToLower(), df.AMDesignator.ToUpper() } }, - { "PM", df.PMDesignator.IsEmpty() ? null : new[] { df.PMDesignator, df.PMDesignator.ToLower(), df.PMDesignator.ToUpper() } }, - { "days", new Dictionary - { - { "names", df.DayNames }, - { "namesAbbr", df.AbbreviatedDayNames }, - { "namesShort", df.ShortestDayNames }, - } }, - { "months", new Dictionary - { - { "names", df.MonthNames }, - { "namesAbbr", df.AbbreviatedMonthNames }, - } }, - { "patterns", new Dictionary - { - { "d", df.ShortDatePattern }, - { "D", df.LongDatePattern }, - { "t", df.ShortTimePattern }, - { "T", df.LongTimePattern }, - { "g", df.ShortDatePattern + " " + df.ShortTimePattern }, - { "G", df.ShortDatePattern + " " + df.LongTimePattern }, - { "f", df.FullDateTimePattern }, // TODO: (mc) find it actually + { "numberFormat", new Dictionary + { + { ",", nf.NumberGroupSeparator }, + { ".", nf.NumberDecimalSeparator }, + { "pattern", new[] { nf.NumberNegativePattern } }, + { "decimals", nf.NumberDecimalDigits }, + { "groupSizes", nf.NumberGroupSizes }, + { "+", nf.PositiveSign }, + { "-", nf.NegativeSign }, + { "NaN", nf.NaNSymbol }, + { "negativeInfinity", nf.NegativeInfinitySymbol }, + { "positiveInfinity", nf.PositiveInfinitySymbol }, + { "percent", new Dictionary + { + { ",", nf.PercentGroupSeparator }, + { ".", nf.PercentDecimalSeparator }, + { "pattern", new[] { nf.PercentNegativePattern, nf.PercentPositivePattern } }, + { "decimals", nf.PercentDecimalDigits }, + { "groupSizes", nf.PercentGroupSizes }, + { "symbol", nf.PercentSymbol } + } }, + { "currency", new Dictionary + { + { ",", nf.CurrencyGroupSeparator }, + { ".", nf.CurrencyDecimalSeparator }, + { "pattern", new[] { nf.CurrencyNegativePattern, nf.CurrencyPositivePattern } }, + { "decimals", nf.CurrencyDecimalDigits }, + { "groupSizes", nf.CurrencyGroupSizes }, + { "symbol", nf.CurrencySymbol } + } }, + } }, + { "dateTimeFormat", new Dictionary + { + { "calendarName", df.NativeCalendarName }, + { "/", df.DateSeparator }, + { ":", df.TimeSeparator }, + { "firstDay", (int)df.FirstDayOfWeek }, + { "twoDigitYearMax", ci.Calendar.TwoDigitYearMax }, + { "AM", df.AMDesignator.IsEmpty() ? null : new[] { df.AMDesignator, df.AMDesignator.ToLower(), df.AMDesignator.ToUpper() } }, + { "PM", df.PMDesignator.IsEmpty() ? null : new[] { df.PMDesignator, df.PMDesignator.ToLower(), df.PMDesignator.ToUpper() } }, + { "days", new Dictionary + { + { "names", df.DayNames }, + { "namesAbbr", df.AbbreviatedDayNames }, + { "namesShort", df.ShortestDayNames }, + } }, + { "months", new Dictionary + { + { "names", df.MonthNames }, + { "namesAbbr", df.AbbreviatedMonthNames }, + } }, + { "patterns", new Dictionary + { + { "d", df.ShortDatePattern }, + { "D", df.LongDatePattern }, + { "t", df.ShortTimePattern }, + { "T", df.LongTimePattern }, + { "g", df.ShortDatePattern + " " + df.ShortTimePattern }, + { "G", df.ShortDatePattern + " " + df.LongTimePattern }, + { "f", df.FullDateTimePattern }, // TODO: (mc) find it actually { "F", df.FullDateTimePattern }, - { "M", df.MonthDayPattern }, - { "Y", df.YearMonthPattern }, - { "u", df.UniversalSortableDateTimePattern }, - } } - } } - }; - - var json = JsonConvert.SerializeObject(dict, new JsonSerializerSettings - { - Formatting = Formatting.None - }); - - return json; - } - } + { "M", df.MonthDayPattern }, + { "Y", df.YearMonthPattern }, + { "u", df.UniversalSortableDateTimePattern }, + } } + } } + }; + + var json = JsonConvert.SerializeObject(dict, new JsonSerializerSettings + { + ContractResolver = SmartContractResolver.Instance, + Formatting = Formatting.None + }); + + return json; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Localization/SmartResourceDisplayName.cs b/src/Presentation/SmartStore.Web.Framework/Localization/SmartResourceDisplayName.cs index e64ca7c164..b009eb95f2 100644 --- a/src/Presentation/SmartStore.Web.Framework/Localization/SmartResourceDisplayName.cs +++ b/src/Presentation/SmartStore.Web.Framework/Localization/SmartResourceDisplayName.cs @@ -1,5 +1,4 @@ -using System; -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using SmartStore.Core; using SmartStore.Core.Infrastructure; using SmartStore.Services.Localization; @@ -37,9 +36,6 @@ public override string DisplayName } } - public string Name - { - get { return "SmartResourceDisplayName"; } - } + public string Name => "SmartResourceDisplayName"; } } diff --git a/src/Presentation/SmartStore.Web.Framework/Localization/TelerikLocalizationService.cs b/src/Presentation/SmartStore.Web.Framework/Localization/TelerikLocalizationService.cs index 273283616e..c32631747d 100644 --- a/src/Presentation/SmartStore.Web.Framework/Localization/TelerikLocalizationService.cs +++ b/src/Presentation/SmartStore.Web.Framework/Localization/TelerikLocalizationService.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using SmartStore.Core.Caching; namespace SmartStore.Web.Framework.Localization @@ -9,43 +7,40 @@ public class TelerikLocalizationService : Telerik.Web.Mvc.Infrastructure.ILocali { private readonly string _componentName; private readonly SmartStore.Services.Localization.ILocalizationService _localizationService; - private readonly IRequestCache _requestCache; + private readonly IRequestCache _requestCache; private readonly int _currentLanguageId; public TelerikLocalizationService( - string componentName, - int currentLanguageId, - Services.Localization.ILocalizationService localizationService, - IRequestCache requestCache) + string componentName, + int currentLanguageId, + Services.Localization.ILocalizationService localizationService, + IRequestCache requestCache) { _componentName = componentName; _currentLanguageId = currentLanguageId; _localizationService = localizationService; - _requestCache = requestCache; + _requestCache = requestCache; } public IDictionary All() { - var scope = "Admin.Telerik." + _componentName; + var scope = "Admin.Telerik." + _componentName; - var cacheKey = scope + ".Resources"; + var cacheKey = scope + ".Resources"; - return _requestCache.Get(cacheKey, () => - { - var resources = _localizationService.GetResourcesByPattern(scope, _currentLanguageId); - var dict = resources.ToDictionarySafe( - x => x.ResourceName.Substring(scope.Length).ToLowerInvariant(), - x => x.ResourceValue); + return _requestCache.Get(cacheKey, () => + { + var resources = _localizationService.GetResourcesByPattern(scope, _currentLanguageId); + var dict = resources.ToDictionarySafe( + x => x.ResourceName.Substring(scope.Length).ToLowerInvariant(), + x => x.ResourceValue); - return dict; - }); - } - - public bool IsDefault - { - get { return true; } + return dict; + }); } + public bool IsDefault => true; + public string One(string key) { var resourceName = "Admin.Telerik." + _componentName + "." + key; diff --git a/src/Presentation/SmartStore.Web.Framework/Localization/TelerikLocalizationServiceFactory.cs b/src/Presentation/SmartStore.Web.Framework/Localization/TelerikLocalizationServiceFactory.cs index 5d6855db9a..9bccaf1612 100644 --- a/src/Presentation/SmartStore.Web.Framework/Localization/TelerikLocalizationServiceFactory.cs +++ b/src/Presentation/SmartStore.Web.Framework/Localization/TelerikLocalizationServiceFactory.cs @@ -8,16 +8,16 @@ public class TelerikLocalizationServiceFactory : ILocalizationServiceFactory { private readonly IWorkContext _workContext; private readonly Services.Localization.ILocalizationService _localizationService; - private readonly IRequestCache _requestCache; + private readonly IRequestCache _requestCache; - public TelerikLocalizationServiceFactory( - IWorkContext workContext, - Services.Localization.ILocalizationService localizationService, - IRequestCache requestCache) + public TelerikLocalizationServiceFactory( + IWorkContext workContext, + Services.Localization.ILocalizationService localizationService, + IRequestCache requestCache) { _workContext = workContext; _localizationService = localizationService; - _requestCache = requestCache; + _requestCache = requestCache; } public ILocalizationService Create(string componentName, System.Globalization.CultureInfo culture) diff --git a/src/Presentation/SmartStore.Web.Framework/Localization/Text.cs b/src/Presentation/SmartStore.Web.Framework/Localization/Text.cs index e3ace14f69..a2296ac505 100644 --- a/src/Presentation/SmartStore.Web.Framework/Localization/Text.cs +++ b/src/Presentation/SmartStore.Web.Framework/Localization/Text.cs @@ -1,44 +1,43 @@ -using System; -using SmartStore.Core.Localization; +using SmartStore.Core.Localization; using SmartStore.Services.Localization; namespace SmartStore.Web.Framework.Localization { - public class Text : IText - { - private readonly ILocalizationService _localizationService; - - public Text(ILocalizationService localizationService) - { - _localizationService = localizationService; - } - - public LocalizedString Get(string key, params object[] args) - { - return GetEx(key, 0, args); - } - - public LocalizedString GetEx(string key, int languageId, params object[] args) - { - try - { - var value = _localizationService.GetResource(key, languageId); - - if (string.IsNullOrEmpty(value)) - { - return new LocalizedString(key); - } - - if (args == null || args.Length == 0) - { - return new LocalizedString(value); - } - - return new LocalizedString(string.Format(value, args), key, args); - } - catch { } - - return new LocalizedString(key); - } - } + public class Text : IText + { + private readonly ILocalizationService _localizationService; + + public Text(ILocalizationService localizationService) + { + _localizationService = localizationService; + } + + public LocalizedString Get(string key, params object[] args) + { + return GetEx(key, 0, args); + } + + public LocalizedString GetEx(string key, int languageId, params object[] args) + { + try + { + var value = _localizationService.GetResource(key, languageId); + + if (string.IsNullOrEmpty(value)) + { + return new LocalizedString(key); + } + + if (args == null || args.Length == 0) + { + return new LocalizedString(value); + } + + return new LocalizedString(string.Format(value, args), key, args); + } + catch { } + + return new LocalizedString(key); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/ChoiceListItem.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/ChoiceListItem.cs new file mode 100644 index 0000000000..6318ade7c2 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Modelling/ChoiceListItem.cs @@ -0,0 +1,53 @@ +using Newtonsoft.Json; + +namespace SmartStore.Web.Framework.Modelling +{ + /// + /// Represents a selectable list item. + /// It can be used together with smartstore.selectwrapper.js to create an extended select list. + /// + public class ChoiceListItem + { + /// + /// The ID value, e.g. an entity ID. + /// + [JsonProperty("id")] + public string Id { get; set; } + + /// + /// Displayed text. + /// + [JsonProperty("text")] + public string Text { get; set; } + + /// + /// A value indicating whether the item is selected. + /// + [JsonProperty("selected")] + public bool Selected { get; set; } + + /// + /// Optional description. + /// + [JsonProperty(PropertyName = "description", NullValueHandling = NullValueHandling.Ignore)] + public string Description { get; set; } + + /// + /// Optional item hint, e.g. the product SKU. + /// + [JsonProperty(PropertyName = "hint", NullValueHandling = NullValueHandling.Ignore)] + public string Hint { get; set; } + + /// + /// URL to add a link to the list item. + /// + [JsonProperty(PropertyName = "url", NullValueHandling = NullValueHandling.Ignore)] + public string Url { get; set; } + + /// + /// Optional title for the item link. + /// + [JsonProperty(PropertyName = "urlTitle", NullValueHandling = NullValueHandling.Ignore)] + public string UrlTitle { get; set; } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/IAclSelector.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/IAclSelector.cs deleted file mode 100644 index 5dfddbe817..0000000000 --- a/src/Presentation/SmartStore.Web.Framework/Modelling/IAclSelector.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web.Mvc; - -namespace SmartStore.Web.Framework.Modelling -{ - public interface IAclSelector - { - [SmartResourceDisplayName("Admin.Common.Acl.SubjectTo")] - bool SubjectToAcl { get; } - - [SmartResourceDisplayName("Admin.Common.Acl.AvailableFor")] - IEnumerable AvailableCustomerRoles { get; } - - int[] SelectedCustomerRoleIds { get; } - } -} diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/IStoreSelector.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/IStoreSelector.cs deleted file mode 100644 index cb124b943e..0000000000 --- a/src/Presentation/SmartStore.Web.Framework/Modelling/IStoreSelector.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web.Mvc; - -namespace SmartStore.Web.Framework.Modelling -{ - public interface IStoreSelector - { - [SmartResourceDisplayName("Admin.Common.Store.LimitedTo")] - bool LimitedToStores { get; } - - [SmartResourceDisplayName("Admin.Common.Store.AvailableFor")] - IEnumerable AvailableStores { get; } - - int[] SelectedStoreIds { get; } - } -} diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/MediaTemplateModel.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/MediaTemplateModel.cs new file mode 100644 index 0000000000..9b2b6bd456 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Modelling/MediaTemplateModel.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using SmartStore.Services.Media; + +namespace SmartStore.Web.Framework.Modelling +{ + public partial class MediaTemplateModel : EntityModelBase + { + public MediaTemplateModel(MediaFileInfo file, bool renderViewer) + { + Guard.NotNull(file, nameof(file)); + + Id = file.Id; + File = file; + RenderViewer = renderViewer; + } + + public MediaFileInfo File { get; private set; } + public bool RenderViewer { get; private set; } + + public int ThumbSize { get; set; } = 256; + public IDictionary HtmlAttributes { get; set; } + + public string Title { get; set; } + public string Alt { get; set; } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/ModelBase.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/ModelBase.cs index 6d3934989a..9ae7515a84 100644 --- a/src/Presentation/SmartStore.Web.Framework/Modelling/ModelBase.cs +++ b/src/Presentation/SmartStore.Web.Framework/Modelling/ModelBase.cs @@ -1,128 +1,135 @@ using System; using System.Collections.Generic; using System.Web.Mvc; -using AutoMapper; using Newtonsoft.Json; using SmartStore.Core.Infrastructure; namespace SmartStore.Web.Framework.Modelling { - [Serializable] - public sealed class CustomPropertiesDictionary : Dictionary - { - } + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class CustomModelPartAttribute : Attribute + { + } + + [Serializable] + public sealed class CustomPropertiesDictionary : Dictionary + { + } - [Serializable] - public abstract partial class ModelBase + [Serializable] + public abstract partial class ModelBase { - private readonly static ContextState>> _contextState; + private readonly static ContextState>> _contextState; - static ModelBase() - { - _contextState = new ContextState>>("ModelBase.CustomThreadProperties"); - } + static ModelBase() + { + _contextState = new ContextState>>("ModelBase.CustomThreadProperties"); + } - protected ModelBase() + protected ModelBase() { - CustomProperties = new CustomPropertiesDictionary(); + CustomProperties = new CustomPropertiesDictionary(); } - + public virtual void BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { } - /// - /// Gets a custom property value either from the thread local or the static storage (in this particular order) - /// - /// Type of property - /// Custom property key - /// The property value or null - public TProperty Get(string key) - { - Guard.NotEmpty(key, nameof(key)); + /// + /// Gets a custom property value either from the thread local or the static storage (in this particular order) + /// + /// Type of property + /// Custom property key + /// The property value or null + public TProperty Get(string key) + { + Guard.NotEmpty(key, nameof(key)); - IDictionary dict; - object value; + IDictionary dict; - if (TryGetCustomThreadProperties(false, out dict) && dict.TryGetValue(key, out value)) - { - return (TProperty)value; - } + if (TryGetCustomThreadProperties(false, out dict) && dict.TryGetValue(key, out var value)) + { + return (TProperty)value; + } - if (CustomProperties.TryGetValue(key, out value)) - { - return (TProperty)value; - } + if (CustomProperties.TryGetValue(key, out value)) + { + return (TProperty)value; + } - return default(TProperty); - } + return default(TProperty); + } /// /// Use this property to store any custom value for your models. /// - [IgnoreMap] public CustomPropertiesDictionary CustomProperties { get; set; } - /// - /// A data bag for custom model properties which only - /// lives during a thread/request lifecycle - /// - /// - /// Use thread properties whenever you need to persist request-scoped data, - /// but the model is potentially cached statically. - /// - [IgnoreMap, JsonIgnore] - public IDictionary CustomThreadProperties - { - get - { - IDictionary dict; - TryGetCustomThreadProperties(true, out dict); - return dict; - } - } - - private bool TryGetCustomThreadProperties(bool create, out IDictionary dict) - { - dict = null; - var state = _contextState.GetState(); - - if (state == null && create) - { - state = new Dictionary>(); - _contextState.SetState(state); - } - - if (state != null) - { - if (!state.TryGetValue(this, out dict)) - { - if (create) - { - dict = new Dictionary(StringComparer.OrdinalIgnoreCase); - state[this] = dict; - } - } - - return dict != null; - } - - return false; - } - } + /// + /// A data bag for custom model properties which only + /// lives during a thread/request lifecycle + /// + /// + /// Use thread properties whenever you need to persist request-scoped data, + /// but the model is potentially cached statically. + /// + [JsonIgnore] + public IDictionary CustomThreadProperties + { + get + { + IDictionary dict; + TryGetCustomThreadProperties(true, out dict); + return dict; + } + } + private bool TryGetCustomThreadProperties(bool create, out IDictionary dict) + { + dict = null; + var state = _contextState.GetState(); + + if (state == null && create) + { + state = new Dictionary>(); + _contextState.SetState(state); + } + + if (state != null) + { + if (!state.TryGetValue(this, out dict)) + { + if (create) + { + dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + state[this] = dict; + } + } + + return dict != null; + } + + return false; + } + } public abstract partial class EntityModelBase : ModelBase { [SmartResourceDisplayName("Admin.Common.Entity.Fields.Id")] public virtual int Id { get; set; } - } + /// + /// This property is required for serialization JSON data of Telerik grids. + /// Without a lower case Id property in JSON results its AJAX operations do not work correctly. + /// Occurs since RouteCollection.LowercaseUrls was set to true in Global.asax. + /// + [JsonProperty("id")] + internal int EntityId => Id; + } - public abstract partial class TabbableModel : EntityModelBase - { - [IgnoreMap] - public virtual string[] LoadedTabs { get; set; } - } + public abstract partial class TabbableModel : EntityModelBase + { + public virtual string[] LoadedTabs { get; set; } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/ModelBoundEvent.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/ModelBoundEvent.cs index 756aceedde..976ac877b3 100644 --- a/src/Presentation/SmartStore.Web.Framework/Modelling/ModelBoundEvent.cs +++ b/src/Presentation/SmartStore.Web.Framework/Modelling/ModelBoundEvent.cs @@ -2,17 +2,17 @@ namespace SmartStore.Web.Framework.Modelling { - public class ModelBoundEvent - { - public ModelBoundEvent(TabbableModel boundModel, object entityModel, FormCollection form) + public class ModelBoundEvent + { + public ModelBoundEvent(TabbableModel boundModel, object entityModel, FormCollection form) { - this.BoundModel = boundModel; - this.EntityModel = entityModel; - this.Form = form; + this.BoundModel = boundModel; + this.EntityModel = entityModel; + this.Form = form; } - public TabbableModel BoundModel { get; private set; } + public TabbableModel BoundModel { get; private set; } public object EntityModel { get; private set; } - public FormCollection Form { get; private set; } - } + public FormCollection Form { get; private set; } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/CachedFileResult.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/CachedFileResult.cs index 0967a4d685..ed232f2cf9 100644 --- a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/CachedFileResult.cs +++ b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/CachedFileResult.cs @@ -9,226 +9,232 @@ namespace SmartStore.Web.Framework.Modelling { - public class CachedFileResult : ActionResult, IFileResponse - { - #region Ctor - - public CachedFileResult(string path, string contentType = null) - : this(GetFileInfo(path), contentType) - { - } - - public CachedFileResult(FileInfo file, string contentType = null) - { - Guard.NotNull(file, nameof(file)); - - if (!file.Exists) - { - throw new FileNotFoundException(file.FullName); - } - - Transmitter = new FileStreamTransmitter(file.OpenRead); - ContentType = contentType.NullEmpty() ?? MimeTypes.MapNameToMimeType(file.Name); - FileLength = file.Length; - LastModifiedUtc = file.LastWriteTimeUtc; - } - - public CachedFileResult(IFile file, string contentType = null) - { - Guard.NotNull(file, nameof(file)); - - if (!file.Exists) - { - throw new FileNotFoundException(file.Path); - } - - Transmitter = new FileStreamTransmitter(file.OpenRead); - ContentType = contentType.NullEmpty() ?? MimeTypes.MapNameToMimeType(file.Name); - FileLength = file.Size; - LastModifiedUtc = file.LastUpdated; - } - - public CachedFileResult(VirtualFile file, DateTime? lastModifiedUtc = null, string contentType = null) - { - Guard.NotNull(file, nameof(file)); - - try - { - var fi = GetFileInfo(file.VirtualPath); - if (fi.Exists) - { - ContentType = contentType.NullEmpty() ?? MimeTypes.MapNameToMimeType(fi.Name); - FileLength = fi.Length; - LastModifiedUtc = fi.LastWriteTimeUtc; - } - } - finally - { - if (lastModifiedUtc == null) - { - throw new ArgumentNullException(nameof(lastModifiedUtc), "A modification date must be provided if the VirtualFile cannot be mapped to a physical path."); - } - - if (FileLength == null) - { - ContentType = contentType.NullEmpty() ?? MimeTypes.MapNameToMimeType(file.Name); - using (var stream = file.Open()) - { - FileLength = stream.Length; - } - } - } - - Transmitter = new FileStreamTransmitter(file.Open); - LastModifiedUtc = lastModifiedUtc.Value; - } - - public CachedFileResult(string contentType, DateTime lastModifiedUtc, Func factory, long? fileLength = null) - { - Guard.NotNull(factory, nameof(factory)); - Guard.NotEmpty(contentType, nameof(contentType)); - - Transmitter = new FileStreamTransmitter(factory); - ContentType = contentType; - FileLength = fileLength; - LastModifiedUtc = lastModifiedUtc; - } - - public CachedFileResult(string contentType, DateTime lastModifiedUtc, Func factory, long? fileLength = null) - { - Guard.NotNull(factory, nameof(factory)); - Guard.NotEmpty(contentType, nameof(contentType)); - - Transmitter = new FileBufferTransmitter(factory); - ContentType = contentType; - FileLength = fileLength; - LastModifiedUtc = lastModifiedUtc; - } - - private static FileInfo GetFileInfo(string path) - { - Guard.NotEmpty(path, nameof(path)); - - if (!PathHelper.IsAbsolutePhysicalPath(path)) - { - path = CommonHelper.MapPath(path); - } - - return new FileInfo(path); - } - - #endregion - - #region Properties - - public string ContentType { get; private set; } - - public long? FileLength { get; set; } - - public DateTime LastModifiedUtc { get; set; } - - public TimeSpan MaxAge { get; set; } = TimeSpan.FromDays(1); - - /// - /// If not set, will be auto-generated based on property. - /// - public string ETag { get; set; } - - public FileTransmitter Transmitter { get; private set; } - - /// - /// A callback that will be invoked on successful result execution - /// - public Action OnExecuted { get; set; } - - #endregion - - public override void ExecuteResult(ControllerContext context) - { - var httpContext = context.HttpContext; - var request = httpContext.Request; - var response = httpContext.Response; - - // Fix Last Modified Time. We might need it soon - // if we encounter a Range: and If-Range header - // Using UTC time to avoid daylight savings time bug 83230 - var lastModified = new DateTime(LastModifiedUtc.Year, - LastModifiedUtc.Month, - LastModifiedUtc.Day, - LastModifiedUtc.Hour, - LastModifiedUtc.Minute, - LastModifiedUtc.Second, - 0, - DateTimeKind.Utc); - - // Because we can't set a "Last-Modified" header to any time - // in the future, check the last modified time and set it to - // DateTime.Now if it's in the future. - // This is to fix VSWhidbey #402323 - var utcNow = DateTime.UtcNow; - if (lastModified > utcNow) - { - // use 1 second resolution - lastModified = new DateTime(utcNow.Ticks - (utcNow.Ticks % TimeSpan.TicksPerSecond), DateTimeKind.Utc); - } - - LastModifiedUtc = lastModified; - - // Generate ETag if empty - if (ETag.IsEmpty()) - { - ETag = GenerateETag(httpContext, lastModified, utcNow); - } - - // Determine applicable file responder - var responder = ResolveResponder(request); - - // Execute response (send file) - if (responder.TrySendHeaders(httpContext)) - { - responder.SendFile(httpContext); - } - - // Finish: invoke the optional callback - OnExecuted?.Invoke(); - } - - private FileResponder ResolveResponder(HttpRequestBase request) - { - // is this a request for an unmodified file? - var ifNoneMatch = request.Headers["If-None-Match"]; - if (ifNoneMatch.HasValue() && ETag == ifNoneMatch) - { - return new UnmodifiedFileResponder(this); - } - - // is this a Range request? - var rangeHeader = request.Headers["Range"]; - if (rangeHeader.HasValue() && rangeHeader.StartsWith("bytes", StringComparison.OrdinalIgnoreCase)) - { - return new RangeFileResponder(this, rangeHeader); - } - - // Responder for sending the whole file - return new FullFileResponder(this); - } - - private static string GenerateETag(HttpContextBase context, DateTime lastModified, DateTime now) - { - // Get 64-bit FILETIME stamp - var lastModFileTime = lastModified.ToFileTime(); - var nowFileTime = now.ToFileTime(); - var hexFileTime = lastModFileTime.ToString("X8", CultureInfo.InvariantCulture); - - //// Do what IIS does to determine if this is a weak ETag. - //// Compare the last modified time to now and if the difference is - //// less than or equal to 3 seconds, then it is weak - //if ((nowFileTime - lastModFileTime) <= 30000000) - //{ - // return "W/\"" + hexFileTime + "\""; - //} - - return "\"" + hexFileTime + "\""; - } - } + public class CachedFileResult : ActionResult, IFileResponse + { + #region Ctor + + public CachedFileResult(string path, string contentType = null) + : this(GetFileInfo(path), contentType) + { + } + + public CachedFileResult(FileInfo file, string contentType = null) + { + Guard.NotNull(file, nameof(file)); + + if (!file.Exists) + { + throw new FileNotFoundException(file.FullName); + } + + Transmitter = new FileStreamTransmitter(file.OpenRead); + ContentType = contentType.NullEmpty() ?? MimeTypes.MapNameToMimeType(file.Name); + FileLength = file.Length; + LastModifiedUtc = file.LastWriteTimeUtc; + } + + public CachedFileResult(IFile file, string contentType = null) + { + Guard.NotNull(file, nameof(file)); + + if (!file.Exists) + { + throw new FileNotFoundException(file.Path); + } + + Transmitter = new FileStreamTransmitter(file.OpenRead); + ContentType = contentType.NullEmpty() ?? MimeTypes.MapNameToMimeType(file.Name); + FileLength = file.Size; + LastModifiedUtc = file.LastUpdated; + } + + public CachedFileResult(VirtualFile file, DateTime? lastModifiedUtc = null, string contentType = null) + { + Guard.NotNull(file, nameof(file)); + + try + { + var fi = GetFileInfo(file.VirtualPath); + if (fi.Exists) + { + ContentType = contentType.NullEmpty() ?? MimeTypes.MapNameToMimeType(fi.Name); + FileLength = fi.Length; + LastModifiedUtc = fi.LastWriteTimeUtc; + } + } + finally + { + if (lastModifiedUtc == null) + { + throw new ArgumentNullException(nameof(lastModifiedUtc), "A modification date must be provided if the VirtualFile cannot be mapped to a physical path."); + } + + if (FileLength == null) + { + ContentType = contentType.NullEmpty() ?? MimeTypes.MapNameToMimeType(file.Name); + using (var stream = file.Open()) + { + FileLength = stream.Length; + } + } + } + + Transmitter = new FileStreamTransmitter(file.Open); + LastModifiedUtc = lastModifiedUtc.Value; + } + + public CachedFileResult(string contentType, DateTime lastModifiedUtc, Func factory, long? fileLength = null) + { + Guard.NotNull(factory, nameof(factory)); + Guard.NotEmpty(contentType, nameof(contentType)); + + Transmitter = new FileStreamTransmitter(factory); + ContentType = contentType; + FileLength = fileLength; + LastModifiedUtc = lastModifiedUtc; + } + + public CachedFileResult(string contentType, DateTime lastModifiedUtc, Func factory, long? fileLength = null) + { + Guard.NotNull(factory, nameof(factory)); + Guard.NotEmpty(contentType, nameof(contentType)); + + Transmitter = new FileBufferTransmitter(factory); + ContentType = contentType; + FileLength = fileLength; + LastModifiedUtc = lastModifiedUtc; + } + + private static FileInfo GetFileInfo(string path) + { + Guard.NotEmpty(path, nameof(path)); + + if (!PathHelper.IsAbsolutePhysicalPath(path)) + { + path = CommonHelper.MapPath(path); + } + + return new FileInfo(path); + } + + #endregion + + #region Properties + + public string ContentType { get; private set; } + + public long? FileLength { get; set; } + + public DateTime LastModifiedUtc { get; set; } + + public TimeSpan MaxAge { get; set; } = TimeSpan.FromDays(7); + + /// + /// If not set, will be auto-generated based on property. + /// + public string ETag { get; set; } + + public FileTransmitter Transmitter { get; private set; } + + /// + /// A callback that will be invoked on successful result execution + /// + public Action OnExecuted { get; set; } + + #endregion + + public override void ExecuteResult(ControllerContext context) + { + var httpContext = context.HttpContext; + var request = httpContext.Request; + var response = httpContext.Response; + + // Fix Last Modified Time. We might need it soon + // if we encounter a Range: and If-Range header + // Using UTC time to avoid daylight savings time bug 83230 + var lastModified = new DateTime(LastModifiedUtc.Year, + LastModifiedUtc.Month, + LastModifiedUtc.Day, + LastModifiedUtc.Hour, + LastModifiedUtc.Minute, + LastModifiedUtc.Second, + 0, + DateTimeKind.Utc); + + // Because we can't set a "Last-Modified" header to any time + // in the future, check the last modified time and set it to + // DateTime.Now if it's in the future. + // This is to fix VSWhidbey #402323 + var utcNow = DateTime.UtcNow; + if (lastModified > utcNow) + { + // use 1 second resolution + lastModified = new DateTime(utcNow.Ticks - (utcNow.Ticks % TimeSpan.TicksPerSecond), DateTimeKind.Utc); + } + + LastModifiedUtc = lastModified; + + // Generate ETag if empty + if (ETag.IsEmpty()) + { + ETag = GenerateETag(httpContext, lastModified, utcNow); + } + + // Determine applicable file responder + var responder = ResolveResponder(request); + + // Execute response (send file) + if (responder.TrySendHeaders(httpContext)) + { + responder.SendFile(httpContext); + } + + // Finish: invoke the optional callback + OnExecuted?.Invoke(); + } + + private FileResponder ResolveResponder(HttpRequestBase request) + { + // Is this a HEAD request + if (request.HttpMethod == "HEAD") + { + return new HeadFileResponder(this); + } + + // Is this a request for an unmodified file? + var ifNoneMatch = request.Headers["If-None-Match"]; + if (ifNoneMatch.HasValue() && ETag == ifNoneMatch) + { + return new UnmodifiedFileResponder(this); + } + + // Is this a Range request? + var rangeHeader = request.Headers["Range"]; + if (rangeHeader.HasValue() && rangeHeader.StartsWith("bytes", StringComparison.OrdinalIgnoreCase)) + { + return new RangeFileResponder(this, rangeHeader); + } + + // Responder for sending the whole file + return new FullFileResponder(this); + } + + private static string GenerateETag(HttpContextBase context, DateTime lastModified, DateTime now) + { + // Get 64-bit FILETIME stamp + var lastModFileTime = lastModified.ToFileTime(); + var nowFileTime = now.ToFileTime(); + var hexFileTime = lastModFileTime.ToString("X8", CultureInfo.InvariantCulture); + + //// Do what IIS does to determine if this is a weak ETag. + //// Compare the last modified time to now and if the difference is + //// less than or equal to 3 seconds, then it is weak + //if ((nowFileTime - lastModFileTime) <= 30000000) + //{ + // return "W/\"" + hexFileTime + "\""; + //} + + return "\"" + hexFileTime + "\""; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/FileResponder.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/FileResponder.cs index 3b30853fe1..bbf053767c 100644 --- a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/FileResponder.cs +++ b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/FileResponder.cs @@ -6,556 +6,589 @@ namespace SmartStore.Web.Framework.Modelling { - internal abstract class FileResponder - { - protected FileResponder(IFileResponse fileResponse) - { - Guard.NotNull(fileResponse, nameof(fileResponse)); - FileResponse = fileResponse; - } - - protected IFileResponse FileResponse { get; private set; } - - public virtual bool TrySendHeaders(HttpContextBase context) - { - var response = context.Response; - - var utcNow = DateTime.UtcNow; - - // Specify content type - response.ContentType = FileResponse.ContentType; - // We support byte ranges - response.AppendHeader("Accept-Ranges", "bytes"); - // Set the expires header for HTTP 1.0 cliets - response.Cache.SetExpires(utcNow.Add(FileResponse.MaxAge)); - // How often the browser should check that it has the latest version - response.Cache.SetMaxAge(FileResponse.MaxAge); - // The unique identifier for the entity - response.Cache.SetETag(FileResponse.ETag); - // Proxy and browser can cache response - response.Cache.SetCacheability(HttpCacheability.Public); - // Proxy cache should check with orginal server once cache has expired - response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches); - //// The date the file was last modified - //context.Response.Cache.SetLastModified(FileResponse.LastModifiedUtc); - - return true; - } - - public abstract void SendFile(HttpContextBase context); - } - - - - internal sealed class UnmodifiedFileResponder : FileResponder - { - public UnmodifiedFileResponder(IFileResponse fileResponse) - : base(fileResponse) - { - } - - public override bool TrySendHeaders(HttpContextBase context) - { - var response = context.Response; - - base.TrySendHeaders(context); - - response.StatusCode = (int)HttpStatusCode.NotModified; - response.StatusDescription = "Not Modified"; - - // Explicitly set the Content-Length header so the client doesn't wait for - // content but keeps the connection open for other requests - response.AddHeader("Content-Length", "0"); - - return true; - } - - public override void SendFile(HttpContextBase context) - { - // Don't send file, it is unmodified. Let browser fetch from its cache. - } - } - - - - internal sealed class FullFileResponder : FileResponder - { - public FullFileResponder(IFileResponse fileResponse) - : base(fileResponse) - { - } - - public override bool TrySendHeaders(HttpContextBase context) - { - base.TrySendHeaders(context); - - // The date the file was last modified - context.Response.Cache.SetLastModified(FileResponse.LastModifiedUtc); - - return true; - } - - public override void SendFile(HttpContextBase context) - { - var fileLength = FileResponse.FileLength ?? FileResponse.Transmitter.GetFileLength(); - FileResponse.Transmitter.TransmitFile(0, fileLength, fileLength, context); - } - } - - - - internal sealed class RangeFileResponder : FileResponder - { - internal struct ByteRange - { - internal long Offset; - internal long Length; - } - - private const string RANGE_BOUNDARY = ""; - private const string MULTIPART_CONTENT_TYPE = "multipart/byteranges; boundary="; - private const string MULTIPART_RANGE_DELIMITER = "--\r\n"; - private const string MULTIPART_RANGE_END = "----\r\n\r\n"; - private const string CONTENT_RANGE_FORMAT = "bytes {0}-{1}/{2}"; - private const int MAX_RANGE_ALLOWED = 5; - - private const int ERROR_ACCESS_DENIED = 5; - - private static readonly string[] _httpDateFormats = new string[] { "r", "dddd, dd-MMM-yy HH':'mm':'ss 'GMT'", "ddd MMM d HH':'mm':'ss yyyy" }; - - private readonly FileResponder _defaultResponder; - private readonly string _rangeHeader; - - public RangeFileResponder(IFileResponse fileResponse, string rangeHeader) - : base(fileResponse) - { - _defaultResponder = new FullFileResponder(fileResponse); - _rangeHeader = rangeHeader; - } - - public override bool TrySendHeaders(HttpContextBase context) - { - var fileLength = FileResponse.FileLength ?? FileResponse.Transmitter.GetFileLength(); - var etag = FileResponse.ETag; - var lastModified = FileResponse.LastModifiedUtc; - - var handled = ExecuteRangeRequest(context, fileLength, _rangeHeader, etag, lastModified); - if (!handled && _defaultResponder.TrySendHeaders(context)) - { - _defaultResponder.SendFile(context); - handled = true; - } - - return handled; - } - - public override void SendFile(HttpContextBase context) - { - // Do nothing here, we have handled everything in 'TrySendHeaders()' already - } - - #region Range Utils - - // Most of the helpers here were copied over from the internal StaticFileHandler.cs - - private bool ExecuteRangeRequest( - HttpContextBase context, - long fileLength, - string rangeHeader, - string etag, - DateTime lastModified) - { - var handled = false; - var request = context.Request; - var response = context.Response; - - // return "416 Requested range not satisfiable" if the file length is zero. - if (fileLength <= 0) - { - SendRangeNotSatisfiable(response, fileLength); - handled = true; - return handled; - } - - var ifRangeHeader = request.Headers["If-Range"]; - if (ifRangeHeader != null && ifRangeHeader.Length > 1) - { - // Is this an ETag or a Date? We only need to check two - // characters; an ETag either begins with W/ or it is quoted. - if (ifRangeHeader[0] == '"') - { - // it's a strong ETag - if (ifRangeHeader != etag) - { - // the etags do not match, and we will therefore return the entire response - return handled; - } - } - else if (ifRangeHeader[0] == 'W' && ifRangeHeader[1] == '/') - { - // it's a weak ETag, and is therefore not usable for sub-range retrieval and - // we will return the entire response - return handled; - } - else - { - // It's a date. If it is greater than or equal to the last-write time of the file, we can send the range. - if (IsOutDated(ifRangeHeader, lastModified)) - { - return handled; - } - } - } - - // the expected format is "bytes = [, , ...]" - // where is "-[]" or "-". - int indexOfEquals = rangeHeader.IndexOf('='); - if (indexOfEquals == -1 || indexOfEquals == rangeHeader.Length - 1) - { - // invalid syntax - return handled; - } - - int startIndex = indexOfEquals + 1; - bool isRangeHeaderSyntacticallyValid = true; - long offset; - long length; - bool isSatisfiable; - bool exceededMax = false; - ByteRange[] byteRanges = null; - int byteRangesCount = 0; - long totalBytes = 0; - while (startIndex < rangeHeader.Length && isRangeHeaderSyntacticallyValid) - { - isRangeHeaderSyntacticallyValid = GetNextRange(rangeHeader, ref startIndex, fileLength, out offset, out length, out isSatisfiable); - - if (!isRangeHeaderSyntacticallyValid) - { - break; - } - - if (!isSatisfiable) - { - continue; - } - - if (byteRanges == null) - { - byteRanges = new ByteRange[16]; - } - - if (byteRangesCount >= byteRanges.Length) - { - // grow byteRanges array - ByteRange[] buffer = new ByteRange[byteRanges.Length * 2]; - Array.Copy(byteRanges, buffer, byteRanges.Length); - byteRanges = buffer; - } - - byteRanges[byteRangesCount].Offset = offset; - byteRanges[byteRangesCount].Length = length; - byteRangesCount++; - - // IIS imposes this limitation too, and sends "400 Bad Request" if exceeded - totalBytes += length; - if (totalBytes > fileLength * MAX_RANGE_ALLOWED) - { - exceededMax = true; - break; - } - } - - if (!isRangeHeaderSyntacticallyValid) - { - return handled; - } - - if (exceededMax) - { - SendBadRequest(response); - handled = true; - return handled; - } - - if (byteRangesCount == 0) - { - // we parsed the Range header and found no satisfiable byte ranges, so return "416 Requested Range Not Satisfiable" - SendRangeNotSatisfiable(response, fileLength); - handled = true; - return handled; - } - - if (byteRangesCount == 1) - { - offset = byteRanges[0].Offset; - length = byteRanges[0].Length; - response.ContentType = FileResponse.ContentType; - string contentRange = String.Format(CultureInfo.InvariantCulture, CONTENT_RANGE_FORMAT, offset, offset + length - 1, fileLength); - response.AppendHeader("Content-Range", contentRange); - - SendResponseHeaders(); - FileResponse.Transmitter.TransmitFile(offset, length, fileLength, context); - } - else - { - response.ContentType = MULTIPART_CONTENT_TYPE; - string contentRange; - string partialContentType = "Content-Type: " + FileResponse.ContentType + "\r\n"; - - SendResponseHeaders(); - - for (int i = 0; i < byteRangesCount; i++) - { - offset = byteRanges[i].Offset; - length = byteRanges[i].Length; - response.Write(MULTIPART_RANGE_DELIMITER); - response.Write(partialContentType); - response.Write("Content-Range: "); - contentRange = String.Format(CultureInfo.InvariantCulture, CONTENT_RANGE_FORMAT, offset, offset + length - 1, fileLength); - response.Write(contentRange); - response.Write("\r\n\r\n"); - // Transmit - FileResponse.Transmitter.TransmitFile(offset, length, fileLength, context); - response.Write("\r\n"); - } - response.Write(MULTIPART_RANGE_END); - } - - handled = true; - return handled; - - void SendResponseHeaders() - { - // Send a "206 Partial Content" status - response.StatusCode = (int)HttpStatusCode.PartialContent; - response.AppendHeader("Accept-Ranges", "bytes"); - response.Cache.SetLastModified(lastModified); - response.Cache.SetETag(etag); - response.Cache.SetCacheability(HttpCacheability.Public); - } - } - - private void SendBadRequest(HttpResponseBase response) - { - response.StatusCode = (int)HttpStatusCode.BadRequest; - response.Write("Bad Request"); - } - - private void SendRangeNotSatisfiable(HttpResponseBase response, long fileLength) - { - response.StatusCode = (int)HttpStatusCode.RequestedRangeNotSatisfiable; - response.ContentType = null; - response.AppendHeader("Content-Range", "bytes */" + fileLength.ToString(NumberFormatInfo.InvariantInfo)); - } - - private static bool IsOutDated(string ifRangeHeader, DateTime lastModified) - { - try - { - var utcLastModified = lastModified.ToUniversalTime(); - var utc = UtcParse(ifRangeHeader); - return (utc < utcLastModified); - } - catch - { - return true; - } - } - - private static DateTime UtcParse(string time) - { - Guard.NotNull(time, nameof(time)); - - var dtStyles = DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal; - if (DateTime.TryParseExact(time, _httpDateFormats, null, dtStyles, out var utcDate)) - { - return utcDate; - } - - throw new FormatException($"{time} is an invalid date expression."); - } - - // initial space characters are skipped, and the string of digits up until the first non-digit - // are converted to a long. If digits are found the method returns true; otherwise false. - private static bool GetLongFromSubstring(string s, ref int startIndex, out long result) - { - result = 0; - - // get index of first digit - MovePastSpaceCharacters(s, ref startIndex); - int beginIndex = startIndex; - - // get index of last digit - MovePastDigits(s, ref startIndex); - int endIndex = startIndex - 1; - - // are there any digits? - if (endIndex < beginIndex) - { - return false; - } - - long multipleOfTen = 1; - for (int i = endIndex; i >= beginIndex; i--) - { - int digit = s[i] - '0'; - result += digit * multipleOfTen; - multipleOfTen *= 10; - // check for overflow - if (result < 0) - { - return false; - } - } - - return true; - } - - // The Range header consists of one or more byte range specifiers. E.g, "Range: bytes=0-1024,-1024" is a request - // for the first and last 1024 bytes of a file. Before this method is called, startIndex points to the beginning - // of a byte range specifier; and afterwards it points to the beginning of the next byte range specifier. - // If the current byte range specifier is syntactially inavlid, this function will return false indicating that the - // Range header must be ignored. If the function returns true, then the byte range specifier will be converted to - // an offset and length, and the startIndex will be incremented to the next byte range specifier. The byte range - // specifier (offset and length) returned by this function is satisfiable if and only if isSatisfiable is true. - private static bool GetNextRange(string rangeHeader, ref int startIndex, long fileLength, out long offset, out long length, out bool isSatisfiable) - { - // startIndex is first char after '=', or first char after ',' - Debug.Assert(startIndex < rangeHeader.Length, "startIndex < rangeHeader.Length"); - - offset = 0; - length = 0; - isSatisfiable = false; - - // A Range request to an empty file is never satisfiable, and will always receive a 416 status. - if (fileLength <= 0) - { - // put startIndex at end of string so we don't try to call GetNextRange again - startIndex = rangeHeader.Length; - return true; - } - - MovePastSpaceCharacters(rangeHeader, ref startIndex); - - if (startIndex < rangeHeader.Length && rangeHeader[startIndex] == '-') - { - // this range is of the form "-mmm" - startIndex++; - if (!GetLongFromSubstring(rangeHeader, ref startIndex, out length)) - { - return false; - } - - if (length > fileLength) - { - // send entire file - offset = 0; - length = fileLength; - } - else - { - // send last N bytes - offset = fileLength - length; - } - - isSatisfiable = IsRangeSatisfiable(offset, length, fileLength); - // we parsed the current range, but need to successfully move the startIndex to the next range - return IncrementToNextRange(rangeHeader, ref startIndex); - } - else - { - // this range is of the form "nnn-[mmm]" - if (!GetLongFromSubstring(rangeHeader, ref startIndex, out offset)) - { - return false; - } - - // increment startIndex past '-' - if (startIndex < rangeHeader.Length && rangeHeader[startIndex] == '-') - { - startIndex++; - } - else - { - return false; - } - - if (!GetLongFromSubstring(rangeHeader, ref startIndex, out long endPos)) - { - // assume range is of form "nnn-". If it isn't, - // the call to IncrementToNextRange will return false - length = fileLength - offset; - } - else - { - // if...greater than or equal to the current length of the entity-body, last-byte-pos - // is taken to be equal to one less than the current length of the entity- body in bytes. - if (endPos > fileLength - 1) - { - endPos = fileLength - 1; - } - - length = endPos - offset + 1; - - if (length < 1) - { - // the byte range specifier is syntactially invalid - // because the last-byte-pos < first-byte-pos - return false; - } - } - - isSatisfiable = IsRangeSatisfiable(offset, length, fileLength); - - // we parsed the current range, but need to successfully move the startIndex to the next range - return IncrementToNextRange(rangeHeader, ref startIndex); - } - } - - private static bool IncrementToNextRange(string s, ref int startIndex) - { - // increment startIndex until next token and return true, unless the syntax is invalid - MovePastSpaceCharacters(s, ref startIndex); - - if (startIndex < s.Length) - { - if (s[startIndex] != ',') - { - return false; - } - // move to first char after ',' - startIndex++; - } - - return true; - } - - private static bool IsRangeSatisfiable(long offset, long length, long fileLength) - { - return (offset < fileLength && length > 0); - } - - private static bool IsSecurityError(int ErrorCode) - { - return (ErrorCode == ERROR_ACCESS_DENIED); - } - - private static void MovePastSpaceCharacters(string s, ref int startIndex) - { - while (startIndex < s.Length && s[startIndex] == ' ') - { - startIndex++; - } - } - - private static void MovePastDigits(string s, ref int startIndex) - { - while (startIndex < s.Length && s[startIndex] <= '9' && s[startIndex] >= '0') - { - startIndex++; - } - } - - #endregion - } + internal abstract class FileResponder + { + protected FileResponder(IFileResponse fileResponse) + { + Guard.NotNull(fileResponse, nameof(fileResponse)); + FileResponse = fileResponse; + } + + protected IFileResponse FileResponse { get; private set; } + + public virtual bool TrySendHeaders(HttpContextBase context) + { + var response = context.Response; + + var utcNow = DateTime.UtcNow; + + // Specify content type + response.ContentType = FileResponse.ContentType; + // We support byte ranges + response.AppendHeader("Accept-Ranges", "bytes"); + //// Set the expires header for HTTP 1.0 cliets + //response.Cache.SetExpires(utcNow.Add(FileResponse.MaxAge)); + // How often the browser should check that it has the latest version + response.Cache.SetMaxAge(FileResponse.MaxAge); + // The unique identifier for the entity + response.Cache.SetETag(FileResponse.ETag); + // Proxy and browser can cache response + response.Cache.SetCacheability(HttpCacheability.Public); + // Proxy cache should check with orginal server once cache has expired + response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches); + //// The date the file was last modified + //context.Response.Cache.SetLastModified(FileResponse.LastModifiedUtc); + + return true; + } + + public abstract void SendFile(HttpContextBase context); + } + + + internal sealed class HeadFileResponder : FileResponder + { + public HeadFileResponder(IFileResponse fileResponse) + : base(fileResponse) + { + } + + public override bool TrySendHeaders(HttpContextBase context) + { + var response = context.Response; + + base.TrySendHeaders(context); + + response.StatusCode = (int)HttpStatusCode.OK; + response.AddHeader("Content-Length", (FileResponse.FileLength ?? 0).ToString(CultureInfo.InvariantCulture)); + + //if (FileResponse.Dimensions != null) + //{ + // response.AddHeader("X-Width", FileResponse.Dimensions.Value.Width.ToString(CultureInfo.InvariantCulture)); + // response.AddHeader("X-Height", FileResponse.Dimensions.Value.Height.ToString(CultureInfo.InvariantCulture)); + //} + + response.End(); + + return true; + } + + public override void SendFile(HttpContextBase context) + { + // Don't send any file. + } + } + + + internal sealed class UnmodifiedFileResponder : FileResponder + { + public UnmodifiedFileResponder(IFileResponse fileResponse) + : base(fileResponse) + { + } + + public override bool TrySendHeaders(HttpContextBase context) + { + var response = context.Response; + + base.TrySendHeaders(context); + + response.StatusCode = (int)HttpStatusCode.NotModified; + response.StatusDescription = "Not Modified"; + + // Explicitly set the Content-Length header so the client doesn't wait for + // content but keeps the connection open for other requests + response.AddHeader("Content-Length", "0"); + + return true; + } + + public override void SendFile(HttpContextBase context) + { + // Don't send file, it is unmodified. Let browser fetch from its cache. + } + } + + + + internal sealed class FullFileResponder : FileResponder + { + public FullFileResponder(IFileResponse fileResponse) + : base(fileResponse) + { + } + + public override bool TrySendHeaders(HttpContextBase context) + { + base.TrySendHeaders(context); + + // The date the file was last modified + context.Response.Cache.SetLastModified(FileResponse.LastModifiedUtc); + + return true; + } + + public override void SendFile(HttpContextBase context) + { + var fileLength = FileResponse.FileLength ?? FileResponse.Transmitter.GetFileLength(); + FileResponse.Transmitter.TransmitFile(0, fileLength, fileLength, context); + } + } + + + + internal sealed class RangeFileResponder : FileResponder + { + internal struct ByteRange + { + internal long Offset; + internal long Length; + } + + private const string RANGE_BOUNDARY = ""; + private const string MULTIPART_CONTENT_TYPE = "multipart/byteranges; boundary="; + private const string MULTIPART_RANGE_DELIMITER = "--\r\n"; + private const string MULTIPART_RANGE_END = "----\r\n\r\n"; + private const string CONTENT_RANGE_FORMAT = "bytes {0}-{1}/{2}"; + private const int MAX_RANGE_ALLOWED = 5; + + private const int ERROR_ACCESS_DENIED = 5; + + private static readonly string[] _httpDateFormats = new string[] { "r", "dddd, dd-MMM-yy HH':'mm':'ss 'GMT'", "ddd MMM d HH':'mm':'ss yyyy" }; + + private readonly FileResponder _defaultResponder; + private readonly string _rangeHeader; + + public RangeFileResponder(IFileResponse fileResponse, string rangeHeader) + : base(fileResponse) + { + _defaultResponder = new FullFileResponder(fileResponse); + _rangeHeader = rangeHeader; + } + + public override bool TrySendHeaders(HttpContextBase context) + { + var fileLength = FileResponse.FileLength ?? FileResponse.Transmitter.GetFileLength(); + var etag = FileResponse.ETag; + var lastModified = FileResponse.LastModifiedUtc; + + var handled = ExecuteRangeRequest(context, fileLength, _rangeHeader, etag, lastModified); + if (!handled && _defaultResponder.TrySendHeaders(context)) + { + _defaultResponder.SendFile(context); + handled = true; + } + + return handled; + } + + public override void SendFile(HttpContextBase context) + { + // Do nothing here, we have handled everything in 'TrySendHeaders()' already + } + + #region Range Utils + + // Most of the helpers here were copied over from the internal StaticFileHandler.cs + + private bool ExecuteRangeRequest( + HttpContextBase context, + long fileLength, + string rangeHeader, + string etag, + DateTime lastModified) + { + var handled = false; + var request = context.Request; + var response = context.Response; + + // return "416 Requested range not satisfiable" if the file length is zero. + if (fileLength <= 0) + { + SendRangeNotSatisfiable(response, fileLength); + handled = true; + return handled; + } + + var ifRangeHeader = request.Headers["If-Range"]; + if (ifRangeHeader != null && ifRangeHeader.Length > 1) + { + // Is this an ETag or a Date? We only need to check two + // characters; an ETag either begins with W/ or it is quoted. + if (ifRangeHeader[0] == '"') + { + // it's a strong ETag + if (ifRangeHeader != etag) + { + // the etags do not match, and we will therefore return the entire response + return handled; + } + } + else if (ifRangeHeader[0] == 'W' && ifRangeHeader[1] == '/') + { + // it's a weak ETag, and is therefore not usable for sub-range retrieval and + // we will return the entire response + return handled; + } + else + { + // It's a date. If it is greater than or equal to the last-write time of the file, we can send the range. + if (IsOutDated(ifRangeHeader, lastModified)) + { + return handled; + } + } + } + + // the expected format is "bytes = [, , ...]" + // where is "-[]" or "-". + int indexOfEquals = rangeHeader.IndexOf('='); + if (indexOfEquals == -1 || indexOfEquals == rangeHeader.Length - 1) + { + // invalid syntax + return handled; + } + + int startIndex = indexOfEquals + 1; + bool isRangeHeaderSyntacticallyValid = true; + long offset; + long length; + bool isSatisfiable; + bool exceededMax = false; + ByteRange[] byteRanges = null; + int byteRangesCount = 0; + long totalBytes = 0; + while (startIndex < rangeHeader.Length && isRangeHeaderSyntacticallyValid) + { + isRangeHeaderSyntacticallyValid = GetNextRange(rangeHeader, ref startIndex, fileLength, out offset, out length, out isSatisfiable); + + if (!isRangeHeaderSyntacticallyValid) + { + break; + } + + if (!isSatisfiable) + { + continue; + } + + if (byteRanges == null) + { + byteRanges = new ByteRange[16]; + } + + if (byteRangesCount >= byteRanges.Length) + { + // grow byteRanges array + ByteRange[] buffer = new ByteRange[byteRanges.Length * 2]; + Array.Copy(byteRanges, buffer, byteRanges.Length); + byteRanges = buffer; + } + + byteRanges[byteRangesCount].Offset = offset; + byteRanges[byteRangesCount].Length = length; + byteRangesCount++; + + // IIS imposes this limitation too, and sends "400 Bad Request" if exceeded + totalBytes += length; + if (totalBytes > fileLength * MAX_RANGE_ALLOWED) + { + exceededMax = true; + break; + } + } + + if (!isRangeHeaderSyntacticallyValid) + { + return handled; + } + + if (exceededMax) + { + SendBadRequest(response); + handled = true; + return handled; + } + + if (byteRangesCount == 0) + { + // we parsed the Range header and found no satisfiable byte ranges, so return "416 Requested Range Not Satisfiable" + SendRangeNotSatisfiable(response, fileLength); + handled = true; + return handled; + } + + if (byteRangesCount == 1) + { + offset = byteRanges[0].Offset; + length = byteRanges[0].Length; + response.ContentType = FileResponse.ContentType; + string contentRange = String.Format(CultureInfo.InvariantCulture, CONTENT_RANGE_FORMAT, offset, offset + length - 1, fileLength); + response.AppendHeader("Content-Range", contentRange); + + SendResponseHeaders(); + FileResponse.Transmitter.TransmitFile(offset, length, fileLength, context); + } + else + { + response.ContentType = MULTIPART_CONTENT_TYPE; + string contentRange; + string partialContentType = "Content-Type: " + FileResponse.ContentType + "\r\n"; + + SendResponseHeaders(); + + for (int i = 0; i < byteRangesCount; i++) + { + offset = byteRanges[i].Offset; + length = byteRanges[i].Length; + response.Write(MULTIPART_RANGE_DELIMITER); + response.Write(partialContentType); + response.Write("Content-Range: "); + contentRange = String.Format(CultureInfo.InvariantCulture, CONTENT_RANGE_FORMAT, offset, offset + length - 1, fileLength); + response.Write(contentRange); + response.Write("\r\n\r\n"); + // Transmit + FileResponse.Transmitter.TransmitFile(offset, length, fileLength, context); + response.Write("\r\n"); + } + response.Write(MULTIPART_RANGE_END); + } + + handled = true; + return handled; + + void SendResponseHeaders() + { + // Send a "206 Partial Content" status + response.StatusCode = (int)HttpStatusCode.PartialContent; + response.AppendHeader("Accept-Ranges", "bytes"); + response.Cache.SetLastModified(lastModified); + response.Cache.SetETag(etag); + response.Cache.SetCacheability(HttpCacheability.Public); + } + } + + private void SendBadRequest(HttpResponseBase response) + { + response.StatusCode = (int)HttpStatusCode.BadRequest; + response.Write("Bad Request"); + } + + private void SendRangeNotSatisfiable(HttpResponseBase response, long fileLength) + { + response.StatusCode = (int)HttpStatusCode.RequestedRangeNotSatisfiable; + response.ContentType = null; + response.AppendHeader("Content-Range", "bytes */" + fileLength.ToString(NumberFormatInfo.InvariantInfo)); + } + + private static bool IsOutDated(string ifRangeHeader, DateTime lastModified) + { + try + { + var utcLastModified = lastModified.ToUniversalTime(); + var utc = UtcParse(ifRangeHeader); + return (utc < utcLastModified); + } + catch + { + return true; + } + } + + private static DateTime UtcParse(string time) + { + Guard.NotNull(time, nameof(time)); + + var dtStyles = DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal; + if (DateTime.TryParseExact(time, _httpDateFormats, null, dtStyles, out var utcDate)) + { + return utcDate; + } + + throw new FormatException($"{time} is an invalid date expression."); + } + + // initial space characters are skipped, and the string of digits up until the first non-digit + // are converted to a long. If digits are found the method returns true; otherwise false. + private static bool GetLongFromSubstring(string s, ref int startIndex, out long result) + { + result = 0; + + // get index of first digit + MovePastSpaceCharacters(s, ref startIndex); + int beginIndex = startIndex; + + // get index of last digit + MovePastDigits(s, ref startIndex); + int endIndex = startIndex - 1; + + // are there any digits? + if (endIndex < beginIndex) + { + return false; + } + + long multipleOfTen = 1; + for (int i = endIndex; i >= beginIndex; i--) + { + int digit = s[i] - '0'; + result += digit * multipleOfTen; + multipleOfTen *= 10; + // check for overflow + if (result < 0) + { + return false; + } + } + + return true; + } + + // The Range header consists of one or more byte range specifiers. E.g, "Range: bytes=0-1024,-1024" is a request + // for the first and last 1024 bytes of a file. Before this method is called, startIndex points to the beginning + // of a byte range specifier; and afterwards it points to the beginning of the next byte range specifier. + // If the current byte range specifier is syntactially inavlid, this function will return false indicating that the + // Range header must be ignored. If the function returns true, then the byte range specifier will be converted to + // an offset and length, and the startIndex will be incremented to the next byte range specifier. The byte range + // specifier (offset and length) returned by this function is satisfiable if and only if isSatisfiable is true. + private static bool GetNextRange(string rangeHeader, ref int startIndex, long fileLength, out long offset, out long length, out bool isSatisfiable) + { + // startIndex is first char after '=', or first char after ',' + Debug.Assert(startIndex < rangeHeader.Length, "startIndex < rangeHeader.Length"); + + offset = 0; + length = 0; + isSatisfiable = false; + + // A Range request to an empty file is never satisfiable, and will always receive a 416 status. + if (fileLength <= 0) + { + // put startIndex at end of string so we don't try to call GetNextRange again + startIndex = rangeHeader.Length; + return true; + } + + MovePastSpaceCharacters(rangeHeader, ref startIndex); + + if (startIndex < rangeHeader.Length && rangeHeader[startIndex] == '-') + { + // this range is of the form "-mmm" + startIndex++; + if (!GetLongFromSubstring(rangeHeader, ref startIndex, out length)) + { + return false; + } + + if (length > fileLength) + { + // send entire file + offset = 0; + length = fileLength; + } + else + { + // send last N bytes + offset = fileLength - length; + } + + isSatisfiable = IsRangeSatisfiable(offset, length, fileLength); + // we parsed the current range, but need to successfully move the startIndex to the next range + return IncrementToNextRange(rangeHeader, ref startIndex); + } + else + { + // this range is of the form "nnn-[mmm]" + if (!GetLongFromSubstring(rangeHeader, ref startIndex, out offset)) + { + return false; + } + + // increment startIndex past '-' + if (startIndex < rangeHeader.Length && rangeHeader[startIndex] == '-') + { + startIndex++; + } + else + { + return false; + } + + if (!GetLongFromSubstring(rangeHeader, ref startIndex, out long endPos)) + { + // assume range is of form "nnn-". If it isn't, + // the call to IncrementToNextRange will return false + length = fileLength - offset; + } + else + { + // if...greater than or equal to the current length of the entity-body, last-byte-pos + // is taken to be equal to one less than the current length of the entity- body in bytes. + if (endPos > fileLength - 1) + { + endPos = fileLength - 1; + } + + length = endPos - offset + 1; + + if (length < 1) + { + // the byte range specifier is syntactially invalid + // because the last-byte-pos < first-byte-pos + return false; + } + } + + isSatisfiable = IsRangeSatisfiable(offset, length, fileLength); + + // we parsed the current range, but need to successfully move the startIndex to the next range + return IncrementToNextRange(rangeHeader, ref startIndex); + } + } + + private static bool IncrementToNextRange(string s, ref int startIndex) + { + // increment startIndex until next token and return true, unless the syntax is invalid + MovePastSpaceCharacters(s, ref startIndex); + + if (startIndex < s.Length) + { + if (s[startIndex] != ',') + { + return false; + } + // move to first char after ',' + startIndex++; + } + + return true; + } + + private static bool IsRangeSatisfiable(long offset, long length, long fileLength) + { + return (offset < fileLength && length > 0); + } + + private static bool IsSecurityError(int ErrorCode) + { + return (ErrorCode == ERROR_ACCESS_DENIED); + } + + private static void MovePastSpaceCharacters(string s, ref int startIndex) + { + while (startIndex < s.Length && s[startIndex] == ' ') + { + startIndex++; + } + } + + private static void MovePastDigits(string s, ref int startIndex) + { + while (startIndex < s.Length && s[startIndex] <= '9' && s[startIndex] >= '0') + { + startIndex++; + } + } + + #endregion + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/FileTransmitter.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/FileTransmitter.cs index 82b2399294..10574118e1 100644 --- a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/FileTransmitter.cs +++ b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/FileTransmitter.cs @@ -4,113 +4,126 @@ namespace SmartStore.Web.Framework.Modelling { - public abstract class FileTransmitter - { - public abstract long GetFileLength(); - public abstract void TransmitFile(long offset, long length, long fileLength, HttpContextBase context); - } - - public sealed class FileBufferTransmitter : FileTransmitter - { - private readonly Func _bufferFactory; - private byte[] _buffer; - - public FileBufferTransmitter(Func bufferFactory) - { - Guard.NotNull(bufferFactory, nameof(bufferFactory)); - _bufferFactory = bufferFactory; - } - - public override long GetFileLength() - { - return GetBuffer().LongLength; - } - - public override void TransmitFile(long offset, long length, long fileLength, HttpContextBase context) - { - context.Response.OutputStream.Write(GetBuffer(), (int)offset, (int)length); - } - - private byte[] GetBuffer() - { - if (_buffer == null) - { - _buffer = _bufferFactory(); - if (_buffer == null) - { - throw new NullReferenceException("File buffer cannot be NULL."); - } - } - - return _buffer; - } - } - - public sealed class FileStreamTransmitter : FileTransmitter - { - // Default buffer size as defined in BufferedStream type - const int DefaultBufferSize = 81920; - - private readonly Func _streamFactory; - private Stream _stream; - - public FileStreamTransmitter(Func streamFactory) - { - Guard.NotNull(streamFactory, nameof(streamFactory)); - _streamFactory = streamFactory; - } - - public override long GetFileLength() - { - return GetStream().Length; - } - - public override void TransmitFile(long offset, long length, long fileLength, HttpContextBase context) - { - var response = context.Response; - var stream = GetStream(); - - response.BufferOutput = false; - - using (stream) - { - int bufferSize = (int)Math.Min(DefaultBufferSize, length); - byte[] buffer = new byte[bufferSize]; - - int read; - long remaining = length; - - stream.Seek(offset, SeekOrigin.Begin); - while ((remaining > 0) && (read = stream.Read(buffer, 0, buffer.Length)) != 0) - { - if (response.IsClientConnected) - { - response.OutputStream.Write(buffer, 0, read); - //response.Flush(); - - remaining -= read; - } - else - { - remaining = 0; - break; - } - } - } - } - - private Stream GetStream() - { - if (_stream == null) - { - _stream = _streamFactory(); - if (_stream == null) - { - throw new NullReferenceException("File stream cannot be NULL."); - } - } - - return _stream; - } - } + public abstract class FileTransmitter + { + public abstract long GetFileLength(); + public abstract void TransmitFile(long offset, long length, long fileLength, HttpContextBase context); + } + + public sealed class FileBufferTransmitter : FileTransmitter + { + private readonly Func _bufferFactory; + private byte[] _buffer; + + public FileBufferTransmitter(Func bufferFactory) + { + Guard.NotNull(bufferFactory, nameof(bufferFactory)); + _bufferFactory = bufferFactory; + } + + public override long GetFileLength() + { + return GetBuffer().LongLength; + } + + public override void TransmitFile(long offset, long length, long fileLength, HttpContextBase context) + { + context.Response.OutputStream.Write(GetBuffer(), (int)offset, (int)length); + } + + private byte[] GetBuffer() + { + if (_buffer == null) + { + _buffer = _bufferFactory(); + if (_buffer == null) + { + throw new NullReferenceException("File buffer cannot be NULL."); + } + } + + return _buffer; + } + } + + public sealed class FileStreamTransmitter : FileTransmitter + { + // Default buffer size as defined in BufferedStream type + const int DefaultBufferSize = 81920; + + private readonly Func _streamFactory; + private Stream _stream; + + public FileStreamTransmitter(Func streamFactory) + { + Guard.NotNull(streamFactory, nameof(streamFactory)); + _streamFactory = streamFactory; + } + + public override long GetFileLength() + { + return GetStream().Length; + } + + public override void TransmitFile(long offset, long length, long fileLength, HttpContextBase context) + { + var response = context.Response; + var stream = GetStream(); + + response.BufferOutput = false; + //response.Buffer = true; // Buffering leads to high RAM usage + + using (stream) + { + int bufferSize = (int)Math.Min(DefaultBufferSize, length); + byte[] buffer = new byte[bufferSize]; + + int read; + long remaining = length; + + if (stream.CanSeek) + { + stream.Seek(offset, SeekOrigin.Begin); + } + while ((remaining > 0) && (read = stream.Read(buffer, 0, buffer.Length)) != 0) + { + if (response.IsClientConnected) + { + try + { + response.OutputStream.Write(buffer, 0, read); + remaining -= read; + } + catch (Exception ex) + { + var isConClose = ex is HttpException httpEx && httpEx.ErrorCode == -2147023901; + if (!isConClose) + { + throw ex; + } + } + } + else + { + remaining = 0; + break; + } + } + } + } + + private Stream GetStream() + { + if (_stream == null) + { + _stream = _streamFactory(); + if (_stream == null) + { + throw new NullReferenceException("File stream cannot be NULL."); + } + } + + return _stream; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/IFileResponse.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/IFileResponse.cs index f490fe8dac..b24463c932 100644 --- a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/IFileResponse.cs +++ b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/IFileResponse.cs @@ -2,13 +2,14 @@ namespace SmartStore.Web.Framework.Modelling { - public interface IFileResponse - { - string ContentType { get; } - long? FileLength { get; } - DateTime LastModifiedUtc { get; } - TimeSpan MaxAge { get; } - string ETag { get; } - FileTransmitter Transmitter { get; } - } + public interface IFileResponse + { + string ContentType { get; } + long? FileLength { get; } + //Func LazyDimensions { get; } + DateTime LastModifiedUtc { get; } + TimeSpan MaxAge { get; } + string ETag { get; } + FileTransmitter Transmitter { get; } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/JsonNetResult.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/JsonNetResult.cs index d1478076e2..aa85bc4b20 100644 --- a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/JsonNetResult.cs +++ b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/JsonNetResult.cs @@ -2,124 +2,135 @@ using System.Web.Mvc; using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using SmartStore.ComponentModel; using SmartStore.Services.Helpers; // ReSharper disable CheckNamespace namespace SmartStore.Web.Framework.Modelling { - public class JsonNetResult : JsonResult - { - private readonly IDateTimeHelper _dateTimeHelper; - private readonly JsonSerializerSettings _settings; - - public JsonNetResult(IDateTimeHelper dateTimeHelper) - : this(dateTimeHelper, null) - { - } - - public JsonNetResult(IDateTimeHelper dateTimeHelper, JsonSerializerSettings settings) - { - Guard.NotNull(dateTimeHelper, nameof(dateTimeHelper)); - - _dateTimeHelper = dateTimeHelper; - _settings = settings; - } - - public override void ExecuteResult(ControllerContext context) - { - Guard.NotNull(context, nameof(context)); - - if (this.Data == null) - return; - - if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && context.HttpContext.Request.HttpMethod.IsCaseInsensitiveEqual("GET")) - { - throw new InvalidOperationException("This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request.To allow GET requests, set JsonRequestBehavior to AllowGet."); - } - - var response = context.HttpContext.Response; - - if (this.ContentEncoding != null) - { - response.ContentEncoding = this.ContentEncoding; - } - - response.ContentType = this.ContentType.NullEmpty() ?? "application/json"; - - var serializerSettings = _settings ?? new JsonSerializerSettings - { - MissingMemberHandling = MissingMemberHandling.Ignore, - TypeNameHandling = TypeNameHandling.Objects, - ReferenceLoopHandling = ReferenceLoopHandling.Ignore, - NullValueHandling = NullValueHandling.Ignore, - DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind, - DateFormatHandling = DateFormatHandling.MicrosoftDateFormat, - - // Limit the object graph we'll consume to a fixed depth. This prevents stackoverflow exceptions - // from deserialization errors that might occur from deeply nested objects. - MaxDepth = 32 - }; - - if (_settings == null) - { - var utcDateTimeConverter = new UTCDateTimeConverter(_dateTimeHelper, new JavaScriptDateTimeConverter()); - serializerSettings.Converters.Add(utcDateTimeConverter); - } - - using (var jsonWriter = new JsonTextWriter(response.Output)) - { - jsonWriter.CloseOutput = false; - var jsonSerializer = JsonSerializer.Create(serializerSettings); - jsonSerializer.Serialize(jsonWriter, this.Data); - } - } - - class UTCDateTimeConverter : DateTimeConverterBase - { - private readonly IDateTimeHelper _dateTimeHelper; - private readonly DateTimeConverterBase _innerConverter; - - public UTCDateTimeConverter(IDateTimeHelper dateTimeHelper, DateTimeConverterBase innerConverter) - { - Guard.NotNull(innerConverter, nameof(innerConverter)); - - _dateTimeHelper = dateTimeHelper; - _innerConverter = innerConverter; - } - - public override bool CanConvert(Type objectType) - { - return _innerConverter.CanConvert(objectType); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - return _innerConverter.ReadJson(reader, objectType, existingValue, serializer); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - if (value is DateTime) - { - var d = (DateTime)value; - if (d.Kind == DateTimeKind.Unspecified) - { - // when DateTime kind is "Unspecified", it was very likely converted from UTC to - // SERVER*s preferred local time before (with DateTimeHelper.ConvertToUserTime()). - // While this works fine during server-time rendering, it can lead to wrong UTC offsets - // on the client (e.g. in AJAX mode Grids, where rendering is performed locally with JSON data). - // The issue occurs when the client's time zone is not the same as "CurrentTimeZone" (configured in the backend). - // To fix it, we have to convert the date back to UTC kind, but with the SERVER PREFERRED TIMEZONE - // in order to calculate with the correct UTC offset. Then it's up to the client to display the date - // in the CLIENT's time zone. Which is not perfect of course, because the same date would be displayed in the - // "CurrentTimeZone" if rendered on server. - // But: it fixes the issue and is way better than converting all AJAXable dates to strings on the server. - value = _dateTimeHelper.ConvertToUtcTime(d, _dateTimeHelper.CurrentTimeZone); - } - } - - _innerConverter.WriteJson(writer, value, serializer); - } - } - } + public class JsonNetResult : JsonResult + { + private readonly IDateTimeHelper _dateTimeHelper; + private readonly JsonSerializerSettings _settings; + + public JsonNetResult(IDateTimeHelper dateTimeHelper) + : this(dateTimeHelper, null) + { + } + + public JsonNetResult(IDateTimeHelper dateTimeHelper, JsonSerializerSettings settings) + { + Guard.NotNull(dateTimeHelper, nameof(dateTimeHelper)); + + _dateTimeHelper = dateTimeHelper; + _settings = settings; + } + + public static JsonSerializerSettings CreateDefaultSerializerSettings() + { + var settings = new JsonSerializerSettings + { + ContractResolver = SmartContractResolver.Instance, + MissingMemberHandling = MissingMemberHandling.Ignore, + TypeNameHandling = TypeNameHandling.Objects, + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind, + DateFormatHandling = DateFormatHandling.MicrosoftDateFormat, + + // We cannot ignore null. Client template of several Telerik grids would fail. + // NullValueHandling = NullValueHandling.Ignore, + + // Limit the object graph we'll consume to a fixed depth. This prevents stackoverflow exceptions + // from deserialization errors that might occur from deeply nested objects. + MaxDepth = 32 + }; + + return settings; + } + + public override void ExecuteResult(ControllerContext context) + { + Guard.NotNull(context, nameof(context)); + + if (this.Data == null) + return; + + if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && context.HttpContext.Request.HttpMethod.IsCaseInsensitiveEqual("GET")) + { + throw new InvalidOperationException("This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request.To allow GET requests, set JsonRequestBehavior to AllowGet."); + } + + var response = context.HttpContext.Response; + + if (this.ContentEncoding != null) + { + response.ContentEncoding = this.ContentEncoding; + } + + response.ContentType = this.ContentType.NullEmpty() ?? "application/json"; + + var serializerSettings = _settings ?? CreateDefaultSerializerSettings(); + + if (_settings == null) + { + var utcDateTimeConverter = new UTCDateTimeConverter(_dateTimeHelper, new JavaScriptDateTimeConverter()); + serializerSettings.Converters.Add(utcDateTimeConverter); + } + + using (var jsonWriter = new JsonTextWriter(response.Output)) + { + jsonWriter.CloseOutput = false; + var jsonSerializer = JsonSerializer.Create(serializerSettings); + jsonSerializer.Serialize(jsonWriter, this.Data); + } + } + + class UTCDateTimeConverter : DateTimeConverterBase + { + private readonly IDateTimeHelper _dateTimeHelper; + private readonly DateTimeConverterBase _innerConverter; + + public UTCDateTimeConverter(IDateTimeHelper dateTimeHelper, DateTimeConverterBase innerConverter) + { + Guard.NotNull(innerConverter, nameof(innerConverter)); + + _dateTimeHelper = dateTimeHelper; + _innerConverter = innerConverter; + } + + public override bool CanConvert(Type objectType) + { + return _innerConverter.CanConvert(objectType); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return _innerConverter.ReadJson(reader, objectType, existingValue, serializer); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value is DateTime) + { + var d = (DateTime)value; + if (d.Kind == DateTimeKind.Unspecified) + { + // when DateTime kind is "Unspecified", it was very likely converted from UTC to + // SERVER*s preferred local time before (with DateTimeHelper.ConvertToUserTime()). + // While this works fine during server-time rendering, it can lead to wrong UTC offsets + // on the client (e.g. in AJAX mode Grids, where rendering is performed locally with JSON data). + // The issue occurs when the client's time zone is not the same as "CurrentTimeZone" (configured in the backend). + // To fix it, we have to convert the date back to UTC kind, but with the SERVER PREFERRED TIMEZONE + // in order to calculate with the correct UTC offset. Then it's up to the client to display the date + // in the CLIENT's time zone. Which is not perfect of course, because the same date would be displayed in the + // "CurrentTimeZone" if rendered on server. + // But: it fixes the issue and is way better than converting all AJAXable dates to strings on the server. + value = _dateTimeHelper.ConvertToUtcTime(d, _dateTimeHelper.CurrentTimeZone); + } + } + + _innerConverter.WriteJson(writer, value, serializer); + } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/PermissiveRedirectResult.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/PermissiveRedirectResult.cs index e394aba608..7c6c729645 100644 --- a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/PermissiveRedirectResult.cs +++ b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/PermissiveRedirectResult.cs @@ -1,25 +1,24 @@ -using System; -using System.Web.Mvc; +using System.Web.Mvc; namespace SmartStore.Web.Framework.Modelling { - /// - /// Allows redirects from within child actions while keeping TempData - /// - public class PermissiveRedirectResult : ActionResult - { - private readonly string _url; + /// + /// Allows redirects from within child actions while keeping TempData + /// + public class PermissiveRedirectResult : ActionResult + { + private readonly string _url; - public PermissiveRedirectResult(string url) - { - _url = url; - } + public PermissiveRedirectResult(string url) + { + _url = url; + } - public override void ExecuteResult(ControllerContext context) - { - var url = UrlHelper.GenerateContentUrl(_url, context.HttpContext); - context.Controller.TempData.Keep(); - context.HttpContext.Response.Redirect(url, false); - } - } + public override void ExecuteResult(ControllerContext context) + { + var url = UrlHelper.GenerateContentUrl(_url, context.HttpContext); + context.Controller.TempData.Keep(); + context.HttpContext.Response.Redirect(url, false); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/RootActionViewResult.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/RootActionViewResult.cs index 2e9f733b80..a112f0b355 100644 --- a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/RootActionViewResult.cs +++ b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/RootActionViewResult.cs @@ -1,31 +1,27 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Web.Mvc; namespace SmartStore.Web.Framework.Modelling.Results { - public class RootActionViewResult : ViewResult - { - protected override ViewEngineResult FindView(ControllerContext context) - { - if (base.ViewName.IsEmpty()) - throw new InvalidOperationException($"{nameof(ViewName)} is required when using {nameof(RootActionViewResult)}."); + public class RootActionViewResult : ViewResult + { + protected override ViewEngineResult FindView(ControllerContext context) + { + if (base.ViewName.IsEmpty()) + throw new InvalidOperationException($"{nameof(ViewName)} is required when using {nameof(RootActionViewResult)}."); - return base.FindView(context.GetRootControllerContext()); - } - } + return base.FindView(context.GetRootControllerContext()); + } + } - public class RootActionPartialViewResult : PartialViewResult - { - protected override ViewEngineResult FindView(ControllerContext context) - { - if (base.ViewName.IsEmpty()) - throw new InvalidOperationException($"{nameof(ViewName)} is required when using {nameof(RootActionPartialViewResult)}."); + public class RootActionPartialViewResult : PartialViewResult + { + protected override ViewEngineResult FindView(ControllerContext context) + { + if (base.ViewName.IsEmpty()) + throw new InvalidOperationException($"{nameof(ViewName)} is required when using {nameof(RootActionPartialViewResult)}."); - return base.FindView(context.GetRootControllerContext()); - } - } + return base.FindView(context.GetRootControllerContext()); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/RssActionResult.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/RssActionResult.cs index ead023a777..2d44042448 100644 --- a/src/Presentation/SmartStore.Web.Framework/Modelling/Results/RssActionResult.cs +++ b/src/Presentation/SmartStore.Web.Framework/Modelling/Results/RssActionResult.cs @@ -14,7 +14,7 @@ public override void ExecuteResult(ControllerContext context) context.HttpContext.Response.ContentType = "application/rss+xml"; var rssFormatter = new Rss20FeedFormatter(Feed, false); - var settings = new XmlWriterSettings { Indent = true, IndentChars = "\t", CheckCharacters = false }; + var settings = new XmlWriterSettings { Indent = true, IndentChars = "\t", CheckCharacters = false }; using (var writer = XmlWriter.Create(context.HttpContext.Response.Output, settings)) { diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/SmartMetadataProvider.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/SmartMetadataProvider.cs index 65215447ff..2f8edaa461 100644 --- a/src/Presentation/SmartStore.Web.Framework/Modelling/SmartMetadataProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/Modelling/SmartMetadataProvider.cs @@ -13,39 +13,39 @@ namespace SmartStore.Web.Framework.Modelling ///
public class SmartMetadataProvider : CachedDataAnnotationsModelMetadataProvider { - [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] - [SuppressMessage("ReSharper", "SuspiciousTypeConversion.Global")] - protected override CachedDataAnnotationsModelMetadata CreateMetadataPrototype(IEnumerable attributes, Type containerType, Type modelType, string propertyName) - { - var metadata = base.CreateMetadataPrototype(attributes, containerType, modelType, propertyName); - var attrs = attributes.OfType().ToList(); + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + [SuppressMessage("ReSharper", "SuspiciousTypeConversion.Global")] + protected override CachedDataAnnotationsModelMetadata CreateMetadataPrototype(IEnumerable attributes, Type containerType, Type modelType, string propertyName) + { + var metadata = base.CreateMetadataPrototype(attributes, containerType, modelType, propertyName); + var attrs = attributes.OfType().ToList(); - foreach (var attr in attrs) - { - if (metadata.AdditionalValues.ContainsKey(attr.Name)) - { - throw new SmartException("There is already an attribute with the name of '{0}' on this model.".FormatCurrent(attr.Name)); - } - metadata.AdditionalValues.Add(attr.Name, attr); - } + foreach (var attr in attrs) + { + if (metadata.AdditionalValues.ContainsKey(attr.Name)) + { + throw new SmartException("There is already an attribute with the name of '{0}' on this model.".FormatCurrent(attr.Name)); + } + metadata.AdditionalValues.Add(attr.Name, attr); + } - return metadata; - } + return metadata; + } - protected override CachedDataAnnotationsModelMetadata CreateMetadataFromPrototype(CachedDataAnnotationsModelMetadata prototype, Func modelAccessor) - { - var result = base.CreateMetadataFromPrototype(prototype, modelAccessor); + protected override CachedDataAnnotationsModelMetadata CreateMetadataFromPrototype(CachedDataAnnotationsModelMetadata prototype, Func modelAccessor) + { + var result = base.CreateMetadataFromPrototype(prototype, modelAccessor); - if (prototype.AdditionalValues.Count > 0) - { - var attrs = prototype.AdditionalValues.Where(x => x.Value is IModelAttribute).ToArray(); - if (attrs.Any()) - { - result.AdditionalValues.AddRange(attrs); - } - } + if (prototype.AdditionalValues.Count > 0) + { + var attrs = prototype.AdditionalValues.Where(x => x.Value is IModelAttribute).ToArray(); + if (attrs.Any()) + { + result.AdditionalValues.AddRange(attrs); + } + } - return result; - } + return result; + } } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Modelling/SmartModelBinder.cs b/src/Presentation/SmartStore.Web.Framework/Modelling/SmartModelBinder.cs index 658eb4a74b..350a009f70 100644 --- a/src/Presentation/SmartStore.Web.Framework/Modelling/SmartModelBinder.cs +++ b/src/Presentation/SmartStore.Web.Framework/Modelling/SmartModelBinder.cs @@ -2,24 +2,25 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; +using System.Security; using System.Web.Mvc; using SmartStore.Core.Html; using SmartStore.Web.Framework.Security; namespace SmartStore.Web.Framework.Modelling { - public class SmartModelBinder : DefaultModelBinder + public class SmartModelBinder : DefaultModelBinder { public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { - var modelType = bindingContext.ModelType; + var modelType = bindingContext.ModelType; - if (modelType == typeof(CustomPropertiesDictionary)) - { - return BindCustomPropertiesDictioary(controllerContext, bindingContext); - } + if (modelType == typeof(CustomPropertiesDictionary)) + { + return BindCustomPropertiesDictioary(controllerContext, bindingContext); + } - var model = base.BindModel(controllerContext, bindingContext); + var model = base.BindModel(controllerContext, bindingContext); if (model is ModelBase) { @@ -29,120 +30,126 @@ public override object BindModel(ControllerContext controllerContext, ModelBindi return model; } - protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor property, object value) - { - var attrs = property.Attributes; - var sanitizeHtmlAttr = attrs.OfType().FirstOrDefault(); - - if (property.PropertyType == typeof(string) && sanitizeHtmlAttr != null && value is string input && !string.IsNullOrWhiteSpace(input)) - { - value = HtmlUtils.SanitizeHtml(input, sanitizeHtmlAttr.IsFragment); - } - - base.SetProperty(controllerContext, bindingContext, property, value); - } - - private CustomPropertiesDictionary BindCustomPropertiesDictioary(ControllerContext controllerContext, ModelBindingContext bindingContext) - { - var model = bindingContext.Model as CustomPropertiesDictionary ?? new CustomPropertiesDictionary(); - - var keys = GetValueProviderKeys(controllerContext, bindingContext.ModelName + "["); - if (keys.Count == 0) - { - return model; - } - - foreach (var key in keys) - { - var keyName = GetKeyName(key); - if (keyName == null || model.ContainsKey(keyName)) - continue; - - var valueBinder = this.Binders.DefaultBinder; - - var subPropertyName = GetSubPropertyName(key); - if (subPropertyName.IsCaseInsensitiveEqual("__Type__")) - continue; - - if (subPropertyName == null) - { - var simpleBindingContext = new ModelBindingContext - { - ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, GetValueType(keys, key, bindingContext.ValueProvider)), - ModelName = key, - ModelState = bindingContext.ModelState, - PropertyFilter = bindingContext.PropertyFilter, - ValueProvider = bindingContext.ValueProvider - }; - var value = valueBinder.BindModel(controllerContext, simpleBindingContext); - model[keyName] = value; - } - else - { - // Is Complex type - var modelName = key.Substring(0, key.Length - subPropertyName.Length - 1); - var valueType = GetValueType(keys, modelName, bindingContext.ValueProvider); - valueBinder = this.Binders.GetBinder(valueType); - var complexBindingContext = new ModelBindingContext - { - ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, valueType), - ModelName = key.Substring(0, key.Length - subPropertyName.Length - 1), - ModelState = bindingContext.ModelState, - PropertyFilter = bindingContext.PropertyFilter, - ValueProvider = bindingContext.ValueProvider - }; - var value = valueBinder.BindModel(controllerContext, complexBindingContext); - model[keyName] = value; - } - } - - return model; - } - - private HashSet GetValueProviderKeys(ControllerContext context, string prefix) - { - var keys = context.HttpContext.Request.Form.Keys.Cast() - .Concat(((IDictionary)context.RouteData.Values).Keys.Cast()) - .Concat(context.HttpContext.Request.QueryString.Keys.Cast()) - .Concat(context.HttpContext.Request.Files.Keys.Cast()) - .Where(x => x.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase)); - - return new HashSet(keys, StringComparer.InvariantCultureIgnoreCase); - } - - private string GetKeyName(string key) - { - int startBracket = key.IndexOf("["); - int endBracket = key.IndexOf("]", startBracket); - - if (endBracket == -1) - return null; - - return key.Substring(startBracket + 1, endBracket - startBracket - 1); - } - - private string GetSubPropertyName(string key) - { - var parts = key.Split('.'); - if (parts.Length > 1) - { - return parts[1]; - } - - return null; - } - - private Type GetValueType(HashSet keys, string prefix, IValueProvider valueProvider) - { - var typeKey = prefix + ".__Type__"; - if (keys.Contains(typeKey)) - { - var type = Type.GetType(valueProvider.GetValue(typeKey).AttemptedValue, true); - return type; - } - return typeof(object); - } + protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor property, object value) + { + var attrs = property.Attributes; + var sanitizeHtmlAttr = attrs.OfType().FirstOrDefault(); + + if (property.PropertyType == typeof(string) && sanitizeHtmlAttr != null && value is string input && !string.IsNullOrWhiteSpace(input)) + { + value = HtmlUtils.SanitizeHtml(input, sanitizeHtmlAttr.IsFragment); + } + + base.SetProperty(controllerContext, bindingContext, property, value); + } + + private CustomPropertiesDictionary BindCustomPropertiesDictioary(ControllerContext controllerContext, ModelBindingContext bindingContext) + { + var model = bindingContext.Model as CustomPropertiesDictionary ?? new CustomPropertiesDictionary(); + + var keys = GetValueProviderKeys(controllerContext, bindingContext.ModelName + "["); + if (keys.Count == 0) + { + return model; + } + + foreach (var key in keys) + { + var keyName = GetKeyName(key); + if (keyName == null || model.ContainsKey(keyName)) + continue; + + var valueBinder = this.Binders.DefaultBinder; + + var subPropertyName = GetSubPropertyName(key); + if (subPropertyName.IsCaseInsensitiveEqual("__Type__")) + continue; + + if (subPropertyName == null) + { + var simpleBindingContext = new ModelBindingContext + { + ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, GetValueType(keys, key, bindingContext.ValueProvider)), + ModelName = key, + ModelState = bindingContext.ModelState, + PropertyFilter = bindingContext.PropertyFilter, + ValueProvider = bindingContext.ValueProvider + }; + var value = valueBinder.BindModel(controllerContext, simpleBindingContext); + model[keyName] = value; + } + else + { + // Is Complex type + var modelName = key.Substring(0, key.Length - subPropertyName.Length - 1); + var valueType = GetValueType(keys, modelName, bindingContext.ValueProvider); + if (!valueType.HasAttribute(false)) + { + throw new SecurityException("For security reasons complex types in '{0}' must be decorated with the '{1}' attribute.".FormatInvariant( + typeof(CustomPropertiesDictionary).AssemblyQualifiedNameWithoutVersion(), + typeof(CustomModelPartAttribute).AssemblyQualifiedNameWithoutVersion())); + } + + valueBinder = this.Binders.GetBinder(valueType); + var complexBindingContext = new ModelBindingContext + { + ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, valueType), + ModelName = key.Substring(0, key.Length - subPropertyName.Length - 1), + ModelState = bindingContext.ModelState, + PropertyFilter = bindingContext.PropertyFilter, + ValueProvider = bindingContext.ValueProvider + }; + var value = valueBinder.BindModel(controllerContext, complexBindingContext); + model[keyName] = value; + } + } + + return model; + } + + private HashSet GetValueProviderKeys(ControllerContext context, string prefix) + { + var keys = context.HttpContext.Request.Form.Keys.Cast() + .Concat(((IDictionary)context.RouteData.Values).Keys.Cast()) + .Concat(context.HttpContext.Request.QueryString.Keys.Cast()) + .Concat(context.HttpContext.Request.Files.Keys.Cast()) + .Where(x => x.StartsWith(prefix, StringComparison.InvariantCultureIgnoreCase)); + + return new HashSet(keys, StringComparer.InvariantCultureIgnoreCase); + } + + private string GetKeyName(string key) + { + int startBracket = key.IndexOf("["); + int endBracket = key.IndexOf("]", startBracket); + + if (endBracket == -1) + return null; + return key.Substring(startBracket + 1, endBracket - startBracket - 1); + } + private string GetSubPropertyName(string key) + { + var parts = key.Split('.'); + if (parts.Length > 1) + { + return parts[1]; + } + + return null; + } + + private Type GetValueType(HashSet keys, string prefix, IValueProvider valueProvider) + { + var typeKey = prefix + ".__Type__"; + if (keys.Contains(typeKey)) + { + var type = Type.GetType(valueProvider.GetValue(typeKey).AttemptedValue, true); + return type; + } + + return typeof(object); + } } } diff --git a/src/Presentation/SmartStore.Web.Framework/Pdf/Content/PdfPartialViewContent.cs b/src/Presentation/SmartStore.Web.Framework/Pdf/Content/PdfPartialViewContent.cs index eef903e167..19ad2d26bb 100644 --- a/src/Presentation/SmartStore.Web.Framework/Pdf/Content/PdfPartialViewContent.cs +++ b/src/Presentation/SmartStore.Web.Framework/Pdf/Content/PdfPartialViewContent.cs @@ -1,14 +1,13 @@ -using System; -using System.Web.Mvc; +using System.Web.Mvc; using SmartStore.Services.Pdf; namespace SmartStore.Web.Framework.Pdf { - public class PdfPartialViewContent : PdfHtmlContent - { - public PdfPartialViewContent(string partialViewName, object model, ControllerContext controllerContext) - : base(PdfViewContent.ViewToString(partialViewName, null, model, true, controllerContext, false)) - { - } - } + public class PdfPartialViewContent : PdfHtmlContent + { + public PdfPartialViewContent(string partialViewName, object model, ControllerContext controllerContext) + : base(PdfViewContent.ViewToString(partialViewName, null, model, true, controllerContext, false)) + { + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Pdf/Content/PdfRouteContent.cs b/src/Presentation/SmartStore.Web.Framework/Pdf/Content/PdfRouteContent.cs index 170a240570..67611c5559 100644 --- a/src/Presentation/SmartStore.Web.Framework/Pdf/Content/PdfRouteContent.cs +++ b/src/Presentation/SmartStore.Web.Framework/Pdf/Content/PdfRouteContent.cs @@ -1,41 +1,40 @@ -using System; -using System.Web.Mvc; +using System.Web.Mvc; using System.Web.Routing; using SmartStore.Services.Pdf; namespace SmartStore.Web.Framework.Pdf -{ - public class PdfRouteContent : PdfUrlContent - { - public PdfRouteContent(string routeName, ControllerContext controllerContext) - : this(routeName, null, null, null, controllerContext) - { - } +{ + public class PdfRouteContent : PdfUrlContent + { + public PdfRouteContent(string routeName, ControllerContext controllerContext) + : this(routeName, null, null, null, controllerContext) + { + } - public PdfRouteContent(string routeName, RouteValueDictionary routeValues, ControllerContext controllerContext) - : this(routeName, null, null, routeValues, controllerContext) - { - } + public PdfRouteContent(string routeName, RouteValueDictionary routeValues, ControllerContext controllerContext) + : this(routeName, null, null, routeValues, controllerContext) + { + } - public PdfRouteContent(string action, string controller, ControllerContext controllerContext) - : this(null, action, controller, null, controllerContext) - { - } + public PdfRouteContent(string action, string controller, ControllerContext controllerContext) + : this(null, action, controller, null, controllerContext) + { + } - public PdfRouteContent(string action, string controller, RouteValueDictionary routeValues, ControllerContext controllerContext) - : this(null, action, controller, routeValues, controllerContext) - { - } + public PdfRouteContent(string action, string controller, RouteValueDictionary routeValues, ControllerContext controllerContext) + : this(null, action, controller, routeValues, controllerContext) + { + } - protected PdfRouteContent(string routeName, string action, string controller, RouteValueDictionary routeValues, ControllerContext controllerContext) - : base(UrlHelper.GenerateUrl(routeName, action, controller, routeValues, RouteTable.Routes, controllerContext.RequestContext, true)) - { - //Guard.NotNull(controllerContext, nameof(controllerContext)); - - //if (routeName.IsEmpty() || action.IsEmpty()) - //{ - // throw Error.InvalidOperation("Either 'routeName' or 'action' must be set"); - //} - } - } + protected PdfRouteContent(string routeName, string action, string controller, RouteValueDictionary routeValues, ControllerContext controllerContext) + : base(UrlHelper.GenerateUrl(routeName, action, controller, routeValues, RouteTable.Routes, controllerContext.RequestContext, true)) + { + //Guard.NotNull(controllerContext, nameof(controllerContext)); + + //if (routeName.IsEmpty() || action.IsEmpty()) + //{ + // throw Error.InvalidOperation("Either 'routeName' or 'action' must be set"); + //} + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Pdf/Content/PdfViewContent.cs b/src/Presentation/SmartStore.Web.Framework/Pdf/Content/PdfViewContent.cs index a96f90c4b5..c81c46d4e7 100644 --- a/src/Presentation/SmartStore.Web.Framework/Pdf/Content/PdfViewContent.cs +++ b/src/Presentation/SmartStore.Web.Framework/Pdf/Content/PdfViewContent.cs @@ -1,49 +1,47 @@ using System; -using System.Linq; -using System.Text; using System.Web.Mvc; using SmartStore.Services.Pdf; using SmartStore.Web.Framework.Controllers; namespace SmartStore.Web.Framework.Pdf -{ - public class PdfViewContent : PdfHtmlContent - { - public PdfViewContent(string viewName, object model, ControllerContext controllerContext) - : this(viewName, null, model, controllerContext) - { - } +{ + public class PdfViewContent : PdfHtmlContent + { + public PdfViewContent(string viewName, object model, ControllerContext controllerContext) + : this(viewName, null, model, controllerContext) + { + } - public PdfViewContent(string viewName, string masterName, object model, ControllerContext controllerContext) - : base(ViewToString(viewName, masterName, model, false, controllerContext, true)) - { - } + public PdfViewContent(string viewName, string masterName, object model, ControllerContext controllerContext) + : base(ViewToString(viewName, masterName, model, false, controllerContext, true)) + { + } - protected internal static string ViewToString(string viewName, string masterName, object model, bool isPartial, ControllerContext context, bool throwOnError) - { - Guard.NotNull(context, nameof(context)); + protected internal static string ViewToString(string viewName, string masterName, object model, bool isPartial, ControllerContext context, bool throwOnError) + { + Guard.NotNull(context, nameof(context)); - string html = null; + string html = null; - try - { - html = isPartial - ? context.Controller.RenderPartialViewToString(viewName, model) - : context.Controller.RenderViewToString(viewName, masterName, model); - } - catch (Exception) - { - if (throwOnError) - { - throw; - } - else - { - return null; - } - } + try + { + html = isPartial + ? context.Controller.RenderPartialViewToString(viewName, model) + : context.Controller.RenderViewToString(viewName, masterName, model); + } + catch (Exception) + { + if (throwOnError) + { + throw; + } + else + { + return null; + } + } - return html; - } - } + return html; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Pdf/PdfHeaderFooterVariables.cs b/src/Presentation/SmartStore.Web.Framework/Pdf/PdfHeaderFooterVariables.cs index 4b1dd669f6..65a5a86f36 100644 --- a/src/Presentation/SmartStore.Web.Framework/Pdf/PdfHeaderFooterVariables.cs +++ b/src/Presentation/SmartStore.Web.Framework/Pdf/PdfHeaderFooterVariables.cs @@ -1,59 +1,55 @@ -using System; -using System.Collections.Generic; -using System.Web.Mvc; - -namespace SmartStore.Web.Framework.Pdf +namespace SmartStore.Web.Framework.Pdf { - public class PdfHeaderFooterVariables - { - /// - /// The number of the page currently being printed - /// - public int Page { get; set; } - - /// - /// The number of the first page to be printed - /// - public int FromPage { get; set; } - - /// - /// The number of the last page to be printed - /// - public int ToPage { get; set; } - - /// - /// The URL of the page being printed - /// - public string WebPage { get; set; } - - /// - /// The name of the current section - /// - public string Section { get; set; } - - /// - /// The name of the current subsection - /// - public string SubSection { get; set; } - - /// - /// The title of the of the current page object - /// - public string Title { get; set; } - - /// - /// The title of the output document - /// - public string DocTitle { get; set; } - - /// - /// The number of the page in the current site being converted - /// - public int SitePage { get; set; } - - /// - /// The number of pages in the current site being converted - /// - public int SitePages { get; set; } - } + public class PdfHeaderFooterVariables + { + /// + /// The number of the page currently being printed + /// + public int Page { get; set; } + + /// + /// The number of the first page to be printed + /// + public int FromPage { get; set; } + + /// + /// The number of the last page to be printed + /// + public int ToPage { get; set; } + + /// + /// The URL of the page being printed + /// + public string WebPage { get; set; } + + /// + /// The name of the current section + /// + public string Section { get; set; } + + /// + /// The name of the current subsection + /// + public string SubSection { get; set; } + + /// + /// The title of the of the current page object + /// + public string Title { get; set; } + + /// + /// The title of the output document + /// + public string DocTitle { get; set; } + + /// + /// The number of the page in the current site being converted + /// + public int SitePage { get; set; } + + /// + /// The number of pages in the current site being converted + /// + public int SitePages { get; set; } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Pdf/PdfReceiptHeaderFooterModel.cs b/src/Presentation/SmartStore.Web.Framework/Pdf/PdfReceiptHeaderFooterModel.cs index 67a69d0b5a..a413692334 100644 --- a/src/Presentation/SmartStore.Web.Framework/Pdf/PdfReceiptHeaderFooterModel.cs +++ b/src/Presentation/SmartStore.Web.Framework/Pdf/PdfReceiptHeaderFooterModel.cs @@ -1,23 +1,21 @@ using SmartStore.Core.Domain.Common; using SmartStore.Web.Framework.Modelling; -using SmartStore.Web.Framework.Pdf; namespace SmartStore.Web.Framework.Pdf { - public partial class PdfReceiptHeaderFooterModel : ModelBase + public partial class PdfReceiptHeaderFooterModel : ModelBase { - public int StoreId { get; set; } + public int StoreId { get; set; } + public string StoreName { get; set; } + public string StoreUrl { get; set; } + public int LogoId { get; set; } - public string StoreName { get; set; } - public string LogoUrl { get; set; } - public string StoreUrl { get; set; } + public CompanyInformationSettings MerchantCompanyInfo { get; set; } + public BankConnectionSettings MerchantBankAccount { get; set; } + public ContactDataSettings MerchantContactData { get; set; } - public CompanyInformationSettings MerchantCompanyInfo { get; set; } - public BankConnectionSettings MerchantBankAccount { get; set; } - public ContactDataSettings MerchantContactData { get; set; } + public string MerchantFormattedAddress { get; set; } - public string MerchantFormattedAddress { get; set; } - - public PdfHeaderFooterVariables Variables { get; set; } + public PdfHeaderFooterVariables Variables { get; set; } } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Pdf/PdfResult.cs b/src/Presentation/SmartStore.Web.Framework/Pdf/PdfResult.cs index 19df5bc70a..525e0b070e 100644 --- a/src/Presentation/SmartStore.Web.Framework/Pdf/PdfResult.cs +++ b/src/Presentation/SmartStore.Web.Framework/Pdf/PdfResult.cs @@ -1,53 +1,50 @@ -using System.IO; -using System.Text.RegularExpressions; -using System.Web; +using System.Web; using System.Web.Mvc; -using System.Web.Security; using SmartStore.Services.Pdf; using SmartStore.Utilities; namespace SmartStore.Web.Framework.Pdf { - public class PdfResult : ActionResult - { - private const string ContentType = "application/pdf"; - - public PdfResult(IPdfConverter converter, PdfConvertSettings settings) - { - Guard.NotNull(converter, nameof(converter)); - - this.Converter = converter; - this.Settings = settings ?? new PdfConvertSettings(); - } - - protected IPdfConverter Converter { get; set; } - - protected PdfConvertSettings Settings { get; set; } - - /// - /// The name of the generated PDF file. - /// - public string FileName { get; set; } - - protected HttpResponseBase PrepareResponse(HttpResponseBase response) - { - response.ContentType = ContentType; - - if (FileName.HasValue()) - { - response.AddHeader("Content-Disposition", "attachment; filename=\"{0}\"".FormatCurrent(PathHelper.SanitizeFileName(FileName))); - } - - response.AddHeader("Content-Type", ContentType); - - return response; - } - - public override void ExecuteResult(ControllerContext context) - { - var buffer = Converter.Convert(Settings); - var response = PrepareResponse(context.HttpContext.Response); - response.OutputStream.Write(buffer, 0, buffer.Length); - } - } + public class PdfResult : ActionResult + { + private const string ContentType = "application/pdf"; + + public PdfResult(IPdfConverter converter, PdfConvertSettings settings) + { + Guard.NotNull(converter, nameof(converter)); + + this.Converter = converter; + this.Settings = settings ?? new PdfConvertSettings(); + } + + protected IPdfConverter Converter { get; set; } + + protected PdfConvertSettings Settings { get; set; } + + /// + /// The name of the generated PDF file. + /// + public string FileName { get; set; } + + protected HttpResponseBase PrepareResponse(HttpResponseBase response) + { + response.ContentType = ContentType; + + if (FileName.HasValue()) + { + response.AddHeader("Content-Disposition", "attachment; filename=\"{0}\"".FormatCurrent(PathHelper.SanitizeFileName(FileName))); + } + + response.AddHeader("Content-Type", ContentType); + + return response; + } + + public override void ExecuteResult(ControllerContext context) + { + var buffer = Converter.Convert(Settings); + var response = PrepareResponse(context.HttpContext.Response); + response.OutputStream.Write(buffer, 0, buffer.Length); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Plugins/PluginExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Plugins/PluginExtensions.cs index 0925a59f65..e15f9f655a 100644 --- a/src/Presentation/SmartStore.Web.Framework/Plugins/PluginExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Plugins/PluginExtensions.cs @@ -4,29 +4,29 @@ namespace SmartStore.Web.Framework { - public static class PluginExtensions - { - /// - /// Determines whether a plugin is installed and activated for a particular store. - /// - public static bool IsPluginReady(this IPluginFinder pluginFinder, ISettingService settingService, string systemName, int storeId) - { - try - { - var pluginDescriptor = pluginFinder.GetPluginDescriptorBySystemName(systemName); + public static class PluginExtensions + { + /// + /// Determines whether a plugin is installed and activated for a particular store. + /// + public static bool IsPluginReady(this IPluginFinder pluginFinder, ISettingService settingService, string systemName, int storeId) + { + try + { + var pluginDescriptor = pluginFinder.GetPluginDescriptorBySystemName(systemName); - if (pluginDescriptor != null && pluginDescriptor.Installed) - { - if (storeId == 0 || settingService.GetSettingByKey(pluginDescriptor.GetSettingKey("LimitedToStores")).ToIntArrayContains(storeId, true)) - return true; - } - } - catch (Exception exc) - { - exc.Dump(); - } + if (pluginDescriptor != null && pluginDescriptor.Installed) + { + if (storeId == 0 || settingService.GetSettingByKey(pluginDescriptor.GetSettingKey("LimitedToStores")).ToIntArrayContains(storeId, true)) + return true; + } + } + catch (Exception exc) + { + exc.Dump(); + } - return false; - } - } + return false; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Plugins/PluginMediator.cs b/src/Presentation/SmartStore.Web.Framework/Plugins/PluginMediator.cs index 9f8d8026f0..817fe53f8c 100644 --- a/src/Presentation/SmartStore.Web.Framework/Plugins/PluginMediator.cs +++ b/src/Presentation/SmartStore.Web.Framework/Plugins/PluginMediator.cs @@ -2,12 +2,9 @@ using System.Collections.Concurrent; using System.IO; using System.Linq; -using System.Linq.Expressions; -using System.Web.Routing; using Autofac; using SmartStore.Core.Domain.Cms; using SmartStore.Core.Domain.Localization; -using SmartStore.Core.Localization; using SmartStore.Core.Plugins; using SmartStore.Services; using SmartStore.Services.Cms; @@ -16,298 +13,297 @@ namespace SmartStore.Web.Framework.Plugins { - public class PluginMediator - { - private static readonly ConcurrentDictionary _routesCache = new ConcurrentDictionary(); - private readonly ICommonServices _services; - private readonly IComponentContext _ctx; - - public PluginMediator(ICommonServices services, IComponentContext ctx) - { - _services = services; - _ctx = ctx; - } - - public string GetLocalizedFriendlyName(IProviderMetadata metadata, int languageId = 0, bool returnDefaultValue = true) - { - return GetLocalizedValue(metadata, "FriendlyName", x => x.FriendlyName, languageId, returnDefaultValue); - } - - public string GetLocalizedDescription(IProviderMetadata metadata, int languageId = 0, bool returnDefaultValue = true) - { - return GetLocalizedValue(metadata, "Description", x => x.Description, languageId, returnDefaultValue); - } - - public string GetLocalizedValue(TMetadata metadata, - string propertyName, - Expression> fallback, - int languageId = 0, - bool returnDefaultValue = true) - where TMetadata : IProviderMetadata - { - Guard.NotNull(metadata, nameof(metadata)); - - var systemName = metadata.SystemName; - var resourceName = metadata.ResourceKeyPattern.FormatInvariant(metadata.SystemName, propertyName); - string result = _services.Localization.GetResource(resourceName, languageId, false, "", true); - - if (result.IsEmpty() && returnDefaultValue) - result = fallback.Compile()(metadata); - - return result; - } - - public void SaveLocalizedValue(IProviderMetadata metadata, int languageId, string propertyName, string value) - { - Guard.NotNull(metadata, nameof(metadata)); - Guard.IsPositive(languageId, nameof(languageId)); - Guard.NotEmpty(propertyName, nameof(propertyName)); - - var resourceName = metadata.ResourceKeyPattern.FormatInvariant(metadata.SystemName, propertyName); - var resource = _services.Localization.GetLocaleStringResourceByName(resourceName, languageId, false); - - if (resource != null) - { - if (value.IsEmpty()) - { - // delete - _services.Localization.DeleteLocaleStringResource(resource); - } - else - { - // update - resource.ResourceValue = value; - _services.Localization.UpdateLocaleStringResource(resource); - } - } - else - { - if (value.HasValue()) - { - // insert - resource = new LocaleStringResource - { - LanguageId = languageId, - ResourceName = resourceName, - ResourceValue = value, - }; - _services.Localization.InsertLocaleStringResource(resource); - } - } - } - - public int? GetUserDisplayOrder(ProviderMetadata metadata) - { - return GetSetting(metadata, "DisplayOrder"); - } - - public T GetSetting(ProviderMetadata metadata, string propertyName) - { - var settingKey = metadata.SettingKeyPattern.FormatInvariant(metadata.SystemName, propertyName); - return _services.Settings.GetSettingByKey(settingKey); - } - - public void SetUserDisplayOrder(ProviderMetadata metadata, int displayOrder) - { - Guard.NotNull(metadata, nameof(metadata)); - - metadata.DisplayOrder = displayOrder; - SetSetting(metadata, "DisplayOrder", displayOrder); - } - - public void SetSetting(ProviderMetadata metadata, string propertyName, T value) - { - Guard.NotNull(metadata, nameof(metadata)); - Guard.NotEmpty(propertyName, nameof(propertyName)); - - var settingKey = metadata.SettingKeyPattern.FormatInvariant(metadata.SystemName, propertyName); - - using (_services.Settings.BeginScope()) - { - if (value != null) - { - _services.Settings.SetSetting(settingKey, value, 0, false); - } - else - { - _services.Settings.DeleteSetting(settingKey); - } - } - } - - public ProviderModel ToProviderModel(Provider provider, bool forEdit = false, Action, ProviderModel> enhancer = null) - { - return ToProviderModel(provider, forEdit, enhancer); - } - - public TModel ToProviderModel(Provider provider, bool forEdit = false, Action, TModel> enhancer = null) - where TModel : ProviderModel, new() - where TProvider : IProvider - { - Guard.NotNull(provider, nameof(provider)); - - var metadata = provider.Metadata; - var model = new TModel(); - model.ProviderType = typeof(TProvider); - model.SystemName = metadata.SystemName; - model.FriendlyName = forEdit ? metadata.FriendlyName : GetLocalizedFriendlyName(metadata); - model.Description = forEdit ? metadata.Description : GetLocalizedDescription(metadata); - model.DisplayOrder = metadata.DisplayOrder; - model.IsEditable = metadata.IsEditable; - model.IconUrl = GetIconUrl(metadata); - model.PluginDescriptor = metadata.PluginDescriptor; - if (model.PluginDescriptor != null) - { - model.ProvidingPluginFriendlyName = model.PluginDescriptor.GetLocalizedValue(_services.Localization, "FriendlyName"); - } - - if (metadata.IsConfigurable) - { - var routeInfo = _routesCache.GetOrAdd(model.SystemName, (key) => - { - var configurable = (IConfigurable)provider.Value; - configurable.GetConfigurationRoute(out var actionName, out var controllerName, out var routeValues); - - if (actionName.IsEmpty()) - { - metadata.IsConfigurable = false; - return null; - } - else - { - return new RouteInfo(actionName, controllerName, routeValues); - } - }); - - if (routeInfo != null) - { - model.ConfigurationRoute = new RouteInfo(routeInfo); - } - } - - if (enhancer != null) - { - enhancer(provider, model); - } - - model.IsConfigurable = metadata.IsConfigurable; - - return model; - } - - public string GetBrandImageUrl(ProviderMetadata metadata) - { - var plugin = metadata.PluginDescriptor; - - if (plugin != null) - { - var filesToCheck = (new string[] { "branding.{0}.png", "branding.{0}.gif", "branding.{0}.jpg", "branding.{0}.jpeg" }).Select(x => x.FormatInvariant(metadata.SystemName)); - var dir = plugin.PhysicalPath; - foreach (var file in filesToCheck) - { - if (File.Exists(Path.Combine(dir, "Content", file))) - { - return "~/Plugins/{0}/Content/{1}".FormatInvariant(plugin.SystemName, file); - } - } - - var fallback = plugin.BrandImageFileName; - if (fallback.HasValue()) - { - return "~/Plugins/{0}/Content/{1}".FormatInvariant(plugin.SystemName, fallback); - } - } - - return null; - } - - public string GetIconUrl(ProviderMetadata metadata) - { - var plugin = metadata.PluginDescriptor; - - if (plugin == null) - { - return GetDefaultIconUrl(metadata.GroupName); - } - - return GetIconUrl(plugin, metadata.SystemName); - } - - /// - /// Returns the absolute path of a plugin/provider icon - /// - /// The plugin descriptor. Used to resolve the physical path - /// Optional system name of provider. If passed, an icon with this name gets being tried to resolve first. - /// The icon's absolute path - public string GetIconUrl(PluginDescriptor plugin, string providerSystemName = null) - { - //var cacheKey = new Tuple(plugin, providerSystemName); - - if (providerSystemName.HasValue()) - { - if (File.Exists(Path.Combine(plugin.PhysicalPath, "Content", "icon-{0}.png".FormatInvariant(providerSystemName)))) - { - return "~/Plugins/{0}/Content/icon-{1}.png".FormatInvariant(plugin.SystemName, providerSystemName); - } - } - - if (File.Exists(Path.Combine(plugin.PhysicalPath, "Content", "icon.png"))) - { - return "~/Plugins/{0}/Content/icon.png".FormatInvariant(plugin.SystemName); - } - else - { - return GetDefaultIconUrl(plugin.Group); - } - } - - public string GetDefaultIconUrl(string groupName) - { - if (groupName.HasValue()) - { - string path = "~/Administration/Content/images/icon-plugin-{0}.png".FormatInvariant(groupName.ToLower()); - if (File.Exists(CommonHelper.MapPath(path, false))) - { - return path; - } - } - - return "~/Administration/Content/images/icon-plugin-default.png"; - } - - public void ActivateDependentWidgets(ProviderMetadata parent, bool activate) - { - Guard.NotNull(parent, nameof(parent)); - - if (parent.DependentWidgets == null || parent.DependentWidgets.Length == 0) - return; - - foreach (var systemName in parent.DependentWidgets) - { - var widget = _ctx.ResolveOptionalNamed>(systemName); - if (widget != null) - { - var widgetSettings = _ctx.Resolve(); - - if (widget.IsWidgetActive(widgetSettings)) - { - if (!activate) - { - widgetSettings.ActiveWidgetSystemNames.Remove(systemName); - _services.Settings.SaveSetting(widgetSettings); - } - } - else - { - if (activate) - { - widgetSettings.ActiveWidgetSystemNames.Add(systemName); - _services.Settings.SaveSetting(widgetSettings); - } - } - } - } - } - - } -} + public class PluginMediator + { + private static readonly ConcurrentDictionary _routesCache = new ConcurrentDictionary(); + private readonly ICommonServices _services; + private readonly IComponentContext _ctx; + + public PluginMediator(ICommonServices services, IComponentContext ctx) + { + _services = services; + _ctx = ctx; + } + + public string GetLocalizedFriendlyName(IProviderMetadata metadata, int languageId = 0, bool returnDefaultValue = true) + { + return GetLocalizedValue(metadata, "FriendlyName", x => x.FriendlyName, languageId, returnDefaultValue); + } + + public string GetLocalizedDescription(IProviderMetadata metadata, int languageId = 0, bool returnDefaultValue = true) + { + return GetLocalizedValue(metadata, "Description", x => x.Description, languageId, returnDefaultValue); + } + + public string GetLocalizedValue(TMetadata metadata, + string propertyName, + Func fallback, + int languageId = 0, + bool returnDefaultValue = true) + where TMetadata : IProviderMetadata + { + Guard.NotNull(metadata, nameof(metadata)); + + var systemName = metadata.SystemName; + var resourceName = metadata.ResourceKeyPattern.FormatInvariant(metadata.SystemName, propertyName); + string result = _services.Localization.GetResource(resourceName, languageId, false, "", true); + + if (result.IsEmpty() && returnDefaultValue) + result = fallback(metadata); + + return result; + } + + public void SaveLocalizedValue(IProviderMetadata metadata, int languageId, string propertyName, string value) + { + Guard.NotNull(metadata, nameof(metadata)); + Guard.IsPositive(languageId, nameof(languageId)); + Guard.NotEmpty(propertyName, nameof(propertyName)); + + var resourceName = metadata.ResourceKeyPattern.FormatInvariant(metadata.SystemName, propertyName); + var resource = _services.Localization.GetLocaleStringResourceByName(resourceName, languageId, false); + + if (resource != null) + { + if (value.IsEmpty()) + { + // delete + _services.Localization.DeleteLocaleStringResource(resource); + } + else + { + // update + resource.ResourceValue = value; + _services.Localization.UpdateLocaleStringResource(resource); + } + } + else + { + if (value.HasValue()) + { + // insert + resource = new LocaleStringResource + { + LanguageId = languageId, + ResourceName = resourceName, + ResourceValue = value, + }; + _services.Localization.InsertLocaleStringResource(resource); + } + } + } + + public int? GetUserDisplayOrder(ProviderMetadata metadata) + { + return GetSetting(metadata, "DisplayOrder"); + } + + public T GetSetting(ProviderMetadata metadata, string propertyName) + { + var settingKey = metadata.SettingKeyPattern.FormatInvariant(metadata.SystemName, propertyName); + return _services.Settings.GetSettingByKey(settingKey); + } + + public void SetUserDisplayOrder(ProviderMetadata metadata, int displayOrder) + { + Guard.NotNull(metadata, nameof(metadata)); + + metadata.DisplayOrder = displayOrder; + SetSetting(metadata, "DisplayOrder", displayOrder); + } + + public void SetSetting(ProviderMetadata metadata, string propertyName, T value) + { + Guard.NotNull(metadata, nameof(metadata)); + Guard.NotEmpty(propertyName, nameof(propertyName)); + + var settingKey = metadata.SettingKeyPattern.FormatInvariant(metadata.SystemName, propertyName); + + using (_services.Settings.BeginScope()) + { + if (value != null) + { + _services.Settings.SetSetting(settingKey, value, 0, false); + } + else + { + _services.Settings.DeleteSetting(settingKey); + } + } + } + + public ProviderModel ToProviderModel(Provider provider, bool forEdit = false, Action, ProviderModel> enhancer = null) + { + return ToProviderModel(provider, forEdit, enhancer); + } + + public TModel ToProviderModel(Provider provider, bool forEdit = false, Action, TModel> enhancer = null) + where TModel : ProviderModel, new() + where TProvider : IProvider + { + Guard.NotNull(provider, nameof(provider)); + + var metadata = provider.Metadata; + var model = new TModel(); + model.ProviderType = typeof(TProvider); + model.SystemName = metadata.SystemName; + model.FriendlyName = forEdit ? metadata.FriendlyName : GetLocalizedFriendlyName(metadata); + model.Description = forEdit ? metadata.Description : GetLocalizedDescription(metadata); + model.DisplayOrder = metadata.DisplayOrder; + model.IsEditable = metadata.IsEditable; + model.IconUrl = GetIconUrl(metadata); + model.PluginDescriptor = metadata.PluginDescriptor; + if (model.PluginDescriptor != null) + { + model.ProvidingPluginFriendlyName = model.PluginDescriptor.GetLocalizedValue(_services.Localization, "FriendlyName"); + } + + if (metadata.IsConfigurable) + { + var routeInfo = _routesCache.GetOrAdd(model.SystemName, (key) => + { + var configurable = (IConfigurable)provider.Value; + configurable.GetConfigurationRoute(out var actionName, out var controllerName, out var routeValues); + + if (actionName.IsEmpty()) + { + metadata.IsConfigurable = false; + return null; + } + else + { + return new RouteInfo(actionName, controllerName, routeValues); + } + }); + + if (routeInfo != null) + { + model.ConfigurationRoute = new RouteInfo(routeInfo); + } + } + + if (enhancer != null) + { + enhancer(provider, model); + } + + model.IsConfigurable = metadata.IsConfigurable; + + return model; + } + + public string GetBrandImageUrl(ProviderMetadata metadata) + { + var plugin = metadata.PluginDescriptor; + + if (plugin != null) + { + var filesToCheck = (new string[] { "branding.{0}.png", "branding.{0}.gif", "branding.{0}.jpg", "branding.{0}.jpeg" }).Select(x => x.FormatInvariant(metadata.SystemName)); + var dir = plugin.PhysicalPath; + foreach (var file in filesToCheck) + { + if (File.Exists(Path.Combine(dir, "Content", file))) + { + return "~/Plugins/{0}/Content/{1}".FormatInvariant(plugin.SystemName, file); + } + } + + var fallback = plugin.BrandImageFileName; + if (fallback.HasValue()) + { + return "~/Plugins/{0}/Content/{1}".FormatInvariant(plugin.SystemName, fallback); + } + } + + return null; + } + + public string GetIconUrl(ProviderMetadata metadata) + { + var plugin = metadata.PluginDescriptor; + + if (plugin == null) + { + return GetDefaultIconUrl(metadata.GroupName); + } + + return GetIconUrl(plugin, metadata.SystemName); + } + + /// + /// Returns the absolute path of a plugin/provider icon + /// + /// The plugin descriptor. Used to resolve the physical path + /// Optional system name of provider. If passed, an icon with this name gets being tried to resolve first. + /// The icon's absolute path + public string GetIconUrl(PluginDescriptor plugin, string providerSystemName = null) + { + //var cacheKey = new Tuple(plugin, providerSystemName); + + if (providerSystemName.HasValue()) + { + if (File.Exists(Path.Combine(plugin.PhysicalPath, "Content", "icon-{0}.png".FormatInvariant(providerSystemName)))) + { + return "~/Plugins/{0}/Content/icon-{1}.png".FormatInvariant(plugin.SystemName, providerSystemName); + } + } + + if (File.Exists(Path.Combine(plugin.PhysicalPath, "Content", "icon.png"))) + { + return "~/Plugins/{0}/Content/icon.png".FormatInvariant(plugin.SystemName); + } + else + { + return GetDefaultIconUrl(plugin.Group); + } + } + + public string GetDefaultIconUrl(string groupName) + { + if (groupName.HasValue()) + { + string path = "~/Administration/Content/images/icon-plugin-{0}.png".FormatInvariant(groupName.ToLower()); + if (File.Exists(CommonHelper.MapPath(path, false))) + { + return path; + } + } + + return "~/Administration/Content/images/icon-plugin-default.png"; + } + + public void ActivateDependentWidgets(ProviderMetadata parent, bool activate) + { + Guard.NotNull(parent, nameof(parent)); + + if (parent.DependentWidgets == null || parent.DependentWidgets.Length == 0) + return; + + foreach (var systemName in parent.DependentWidgets) + { + var widget = _ctx.ResolveOptionalNamed>(systemName); + if (widget != null) + { + var widgetSettings = _ctx.Resolve(); + + if (widget.IsWidgetActive(widgetSettings)) + { + if (!activate) + { + widgetSettings.ActiveWidgetSystemNames.Remove(systemName); + _services.Settings.SaveSetting(widgetSettings); + } + } + else + { + if (activate) + { + widgetSettings.ActiveWidgetSystemNames.Add(systemName); + _services.Settings.SaveSetting(widgetSettings); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Plugins/PluginRazorHost.cs b/src/Presentation/SmartStore.Web.Framework/Plugins/PluginRazorHost.cs index 01527d924f..ac2a4cd5fd 100644 --- a/src/Presentation/SmartStore.Web.Framework/Plugins/PluginRazorHost.cs +++ b/src/Presentation/SmartStore.Web.Framework/Plugins/PluginRazorHost.cs @@ -1,5 +1,4 @@ -using System; -using System.Web; +using System.Web; using System.Web.Hosting; using System.Web.Mvc.Razor; using System.Web.WebPages.Razor; @@ -8,35 +7,35 @@ namespace SmartStore.Web.Framework.Plugins { - public class PluginRazorHostFactory : WebRazorHostFactory - { - public override WebPageRazorHost CreateHost(string virtualPath, string physicalPath) - { - // Implementation borrowed from MvcRazorHostFactory - var host = base.CreateHost(virtualPath, physicalPath); + public class PluginRazorHostFactory : WebRazorHostFactory + { + public override WebPageRazorHost CreateHost(string virtualPath, string physicalPath) + { + // Implementation borrowed from MvcRazorHostFactory + var host = base.CreateHost(virtualPath, physicalPath); - if (!host.IsSpecialPage) - { - return new PluginRazorHost(virtualPath, physicalPath); - } + if (!host.IsSpecialPage) + { + return new PluginRazorHost(virtualPath, physicalPath); + } - return host; - } - } + return host; + } + } - public class PluginRazorHost : MvcWebPageRazorHost - { - public PluginRazorHost(string virtualPath, string physicalPath) - : base(virtualPath, physicalPath) - { - if (CommonHelper.IsDevEnvironment && HttpContext.Current.IsDebuggingEnabled) - { - var file = HostingEnvironment.VirtualPathProvider.GetFile(virtualPath) as DebugVirtualFile; - if (file != null) - { - PhysicalPath = file.PhysicalPath; - } - } - } - } + public class PluginRazorHost : MvcWebPageRazorHost + { + public PluginRazorHost(string virtualPath, string physicalPath) + : base(virtualPath, physicalPath) + { + if (CommonHelper.IsDevEnvironment && HttpContext.Current.IsDebuggingEnabled) + { + var file = HostingEnvironment.VirtualPathProvider.GetFile(virtualPath) as DebugVirtualFile; + if (file != null) + { + PhysicalPath = file.PhysicalPath; + } + } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Plugins/PluginStarter.cs b/src/Presentation/SmartStore.Web.Framework/Plugins/PluginStarter.cs new file mode 100644 index 0000000000..cd7d9e63a2 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Plugins/PluginStarter.cs @@ -0,0 +1,47 @@ +using System; +using System.Web; +using SmartStore.Core.Infrastructure; +using SmartStore.Core.Plugins; +using SmartStore.Services.Localization; + +namespace SmartStore.Web.Framework.Plugins +{ + /// + /// Checks whether any plugin has changed and refreshes all plugin locale resources. + /// + public sealed class PluginStarter : IPostApplicationStart + { + private readonly IPluginFinder _pluginFinder; + private readonly ILocalizationService _locService; + + public PluginStarter(IPluginFinder pluginFinder, ILocalizationService locService) + { + _pluginFinder = pluginFinder; + _locService = locService; + } + + public int Order => 100; + public bool ThrowOnError => false; + public int MaxAttempts => 1; + + public void Start(HttpContextBase httpContext) + { + //if (!PluginManager.PluginChangeDetected) + // return; + + var descriptors = _pluginFinder.GetPluginDescriptors(true); + foreach (var d in descriptors) + { + var hasher = _locService.CreatePluginResourcesHasher(d); + if (hasher.HasChanged) + { + _locService.ImportPluginResourcesFromXml(d, null, false); + } + } + } + + public void OnFail(Exception exception, bool willRetry) + { + } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/Plugins/ProviderManager.cs b/src/Presentation/SmartStore.Web.Framework/Plugins/ProviderManager.cs index e08ef6561e..e346a0010c 100644 --- a/src/Presentation/SmartStore.Web.Framework/Plugins/ProviderManager.cs +++ b/src/Presentation/SmartStore.Web.Framework/Plugins/ProviderManager.cs @@ -7,139 +7,139 @@ namespace SmartStore.Web.Framework.Plugins { - public partial class ProviderManager : IProviderManager - { - private readonly IComponentContext _ctx; - private readonly ICommonServices _services; - private readonly PluginMediator _pluginMediator; - - public ProviderManager(IComponentContext ctx, ICommonServices services, PluginMediator pluginMediator) - { - _ctx = ctx; - _services = services; - _pluginMediator = pluginMediator; - } - - public Provider GetProvider(string systemName, int storeId = 0) where TProvider : IProvider - { - if (systemName.IsEmpty()) - return null; - - var provider = _ctx.ResolveOptionalNamed>(systemName); - if (provider != null) - { - if (storeId > 0) - { - var d = provider.Metadata.PluginDescriptor; - if (d != null && !IsActiveForStore(d, storeId)) - { - return null; - } - } - SetUserData(provider.Metadata); - return new Provider(provider); - } - - return null; - } - - public Provider GetProvider(string systemName, int storeId = 0) - { - Guard.NotEmpty(systemName, nameof(systemName)); - - var provider = _ctx.ResolveOptionalNamed>(systemName); - if (provider != null) - { - if (storeId > 0) - { - var d = provider.Metadata.PluginDescriptor; - if (d != null && !IsActiveForStore(d, storeId)) - { - return null; - } - } - SetUserData(provider.Metadata); - return new Provider(provider); - } - return null; - } - - public IEnumerable> GetAllProviders(int storeId = 0) where TProvider : IProvider - { - var providers = _ctx.Resolve>>(); - if (storeId > 0) - { - providers = from p in providers - let d = p.Metadata.PluginDescriptor - where d == null || IsActiveForStore(d, storeId) - select p; - } - return SortProviders(providers.Select(x => new Provider(x))); - } - - public IEnumerable> GetAllProviders(int storeId = 0) - { - var providers = _ctx.Resolve>>(); - if (storeId > 0) - { - providers = from p in providers - let d = p.Metadata.PluginDescriptor - where d == null || IsActiveForStore(d, storeId) - select p; - } - return SortProviders(providers.Select(x => new Provider(x))); - } - - protected virtual IEnumerable> SortProviders(IEnumerable> providers) where TProvider : IProvider - { - foreach (var m in providers.Select(x => x.Metadata)) - { - SetUserData(m); - } - - return providers.OrderBy(x => x.Metadata.DisplayOrder).ThenBy(x => x.Metadata.FriendlyName); - } - - protected virtual void SetUserData(ProviderMetadata metadata) - { - if (!metadata.IsEditable) - return; - - var displayOrder = _pluginMediator.GetUserDisplayOrder(metadata); - var name = _pluginMediator.GetSetting(metadata, "FriendlyName"); - var description = _pluginMediator.GetSetting(metadata, "Description"); - metadata.FriendlyName = name; - metadata.Description = description; - - if (displayOrder.HasValue) - { - metadata.DisplayOrder = displayOrder.Value; - } - } - - private bool IsActiveForStore(PluginDescriptor plugin, int storeId) - { - if (storeId == 0) - { - return true; - } - - var limitedToStoresSetting = _services.Settings.GetSettingByKey(plugin.GetSettingKey("LimitedToStores")); - if (limitedToStoresSetting.IsEmpty()) - { - return true; - } - - var limitedToStores = limitedToStoresSetting.ToIntArray(); - if (limitedToStores.Length > 0) - { - var flag = limitedToStores.Contains(storeId); - return flag; - } - - return true; - } - - } + public partial class ProviderManager : IProviderManager + { + private readonly IComponentContext _ctx; + private readonly ICommonServices _services; + private readonly PluginMediator _pluginMediator; + + public ProviderManager(IComponentContext ctx, ICommonServices services, PluginMediator pluginMediator) + { + _ctx = ctx; + _services = services; + _pluginMediator = pluginMediator; + } + + public Provider GetProvider(string systemName, int storeId = 0) where TProvider : IProvider + { + if (systemName.IsEmpty()) + return null; + + var provider = _ctx.ResolveOptionalNamed>(systemName); + if (provider != null) + { + if (storeId > 0) + { + var d = provider.Metadata.PluginDescriptor; + if (d != null && !IsActiveForStore(d, storeId)) + { + return null; + } + } + SetUserData(provider.Metadata); + return new Provider(provider); + } + + return null; + } + + public Provider GetProvider(string systemName, int storeId = 0) + { + Guard.NotEmpty(systemName, nameof(systemName)); + + var provider = _ctx.ResolveOptionalNamed>(systemName); + if (provider != null) + { + if (storeId > 0) + { + var d = provider.Metadata.PluginDescriptor; + if (d != null && !IsActiveForStore(d, storeId)) + { + return null; + } + } + SetUserData(provider.Metadata); + return new Provider(provider); + } + return null; + } + + public IEnumerable> GetAllProviders(int storeId = 0) where TProvider : IProvider + { + var providers = _ctx.Resolve>>(); + if (storeId > 0) + { + providers = from p in providers + let d = p.Metadata.PluginDescriptor + where d == null || IsActiveForStore(d, storeId) + select p; + } + return SortProviders(providers.Select(x => new Provider(x))); + } + + public IEnumerable> GetAllProviders(int storeId = 0) + { + var providers = _ctx.Resolve>>(); + if (storeId > 0) + { + providers = from p in providers + let d = p.Metadata.PluginDescriptor + where d == null || IsActiveForStore(d, storeId) + select p; + } + return SortProviders(providers.Select(x => new Provider(x))); + } + + protected virtual IEnumerable> SortProviders(IEnumerable> providers) where TProvider : IProvider + { + foreach (var m in providers.Select(x => x.Metadata)) + { + SetUserData(m); + } + + return providers.OrderBy(x => x.Metadata.DisplayOrder).ThenBy(x => x.Metadata.FriendlyName); + } + + protected virtual void SetUserData(ProviderMetadata metadata) + { + if (!metadata.IsEditable) + return; + + var displayOrder = _pluginMediator.GetUserDisplayOrder(metadata); + var name = _pluginMediator.GetSetting(metadata, "FriendlyName"); + var description = _pluginMediator.GetSetting(metadata, "Description"); + metadata.FriendlyName = name; + metadata.Description = description; + + if (displayOrder.HasValue) + { + metadata.DisplayOrder = displayOrder.Value; + } + } + + private bool IsActiveForStore(PluginDescriptor plugin, int storeId) + { + if (storeId == 0) + { + return true; + } + + var limitedToStoresSetting = _services.Settings.GetSettingByKey(plugin.GetSettingKey("LimitedToStores")); + if (limitedToStoresSetting.IsEmpty()) + { + return true; + } + + var limitedToStores = limitedToStoresSetting.ToIntArray(); + if (limitedToStores.Length > 0) + { + var flag = limitedToStores.Contains(storeId); + return flag; + } + + return true; + } + + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Plugins/ProviderModel.cs b/src/Presentation/SmartStore.Web.Framework/Plugins/ProviderModel.cs index fa02cb9798..39fbe63e21 100644 --- a/src/Presentation/SmartStore.Web.Framework/Plugins/ProviderModel.cs +++ b/src/Presentation/SmartStore.Web.Framework/Plugins/ProviderModel.cs @@ -8,81 +8,72 @@ namespace SmartStore.Web.Framework.Plugins { - public class ProviderModel : ModelBase, ILocalizedModel - { - private IList _locales; - - public Type ProviderType { get; set; } - - [SmartResourceDisplayName("Common.SystemName")] - public string SystemName { get; set; } - - [SmartResourceDisplayName("Common.FriendlyName")] - [AllowHtml] - public string FriendlyName { get; set; } - - [SmartResourceDisplayName("Common.Description")] - [AllowHtml] - public string Description { get; set; } - - [SmartResourceDisplayName("Common.DisplayOrder")] - public int DisplayOrder { get; set; } - - public bool IsEditable { get; set; } - - public bool IsConfigurable { get; set; } - - public RouteInfo ConfigurationRoute { get; set; } - - public PluginDescriptor PluginDescriptor { get; set; } - - [SmartResourceDisplayName("Admin.Providers.ProvidingPlugin")] - public string ProvidingPluginFriendlyName { get; set; } - - /// - /// Returns the absolute path of the provider's icon url. - /// - /// - /// The parent plugin's icon url is returned as a fallback, if provider icon cannot be resolved. - /// - public string IconUrl { get; set; } - - public IList Locales - { - get - { - if (_locales == null) - { - _locales = new List(); - } - return _locales; - } - set - { - _locales = value; - } - } - - public bool IsPaymentMethod - { - get - { - return ProviderType == typeof(IPaymentMethod); - } - } - } - - public class ProviderLocalizedModel : ILocalizedModelLocal - { - public int LanguageId { get; set; } - - [SmartResourceDisplayName("Common.FriendlyName")] - [AllowHtml] - public string FriendlyName { get; set; } - - [SmartResourceDisplayName("Common.Description")] - [AllowHtml] - public string Description { get; set; } - } + public class ProviderModel : ModelBase, ILocalizedModel + { + private IList _locales; + + public Type ProviderType { get; set; } + + [SmartResourceDisplayName("Common.SystemName")] + public string SystemName { get; set; } + + [SmartResourceDisplayName("Common.FriendlyName")] + [AllowHtml] + public string FriendlyName { get; set; } + + [SmartResourceDisplayName("Common.Description")] + [AllowHtml] + public string Description { get; set; } + + [SmartResourceDisplayName("Common.DisplayOrder")] + public int DisplayOrder { get; set; } + + public bool IsEditable { get; set; } + + public bool IsConfigurable { get; set; } + + public RouteInfo ConfigurationRoute { get; set; } + + public PluginDescriptor PluginDescriptor { get; set; } + + [SmartResourceDisplayName("Admin.Providers.ProvidingPlugin")] + public string ProvidingPluginFriendlyName { get; set; } + + /// + /// Returns the absolute path of the provider's icon url. + /// + /// + /// The parent plugin's icon url is returned as a fallback, if provider icon cannot be resolved. + /// + public string IconUrl { get; set; } + + public IList Locales + { + get + { + if (_locales == null) + { + _locales = new List(); + } + return _locales; + } + set => _locales = value; + } + + public bool IsPaymentMethod => ProviderType == typeof(IPaymentMethod); + } + + public class ProviderLocalizedModel : ILocalizedModelLocal + { + public int LanguageId { get; set; } + + [SmartResourceDisplayName("Common.FriendlyName")] + [AllowHtml] + public string FriendlyName { get; set; } + + [SmartResourceDisplayName("Common.Description")] + [AllowHtml] + public string Description { get; set; } + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Properties/AssemblyInfo.cs b/src/Presentation/SmartStore.Web.Framework/Properties/AssemblyInfo.cs index 3803039590..7ce572fa25 100644 --- a/src/Presentation/SmartStore.Web.Framework/Properties/AssemblyInfo.cs +++ b/src/Presentation/SmartStore.Web.Framework/Properties/AssemblyInfo.cs @@ -1,6 +1,5 @@ using System.Reflection; using System.Runtime.CompilerServices; -using System.Security; [assembly: AssemblyTitle("SmartStore.Web.Framework")] diff --git a/src/Presentation/SmartStore.Web.Framework/Routing/GuidConstraint.cs b/src/Presentation/SmartStore.Web.Framework/Routing/GuidConstraint.cs index 97f1550d1b..b936aaad1e 100644 --- a/src/Presentation/SmartStore.Web.Framework/Routing/GuidConstraint.cs +++ b/src/Presentation/SmartStore.Web.Framework/Routing/GuidConstraint.cs @@ -20,7 +20,7 @@ public bool Match(HttpContextBase httpContext, Route route, string parameterName if (!string.IsNullOrEmpty(stringValue)) { - return Guid.TryParse(stringValue, out var guidValue) && + return Guid.TryParse(stringValue, out var guidValue) && (_allowEmpty || guidValue != Guid.Empty); } } diff --git a/src/Presentation/SmartStore.Web.Framework/Routing/RoutePublisher.cs b/src/Presentation/SmartStore.Web.Framework/Routing/RoutePublisher.cs index 60191958db..8906a4a3c8 100644 --- a/src/Presentation/SmartStore.Web.Framework/Routing/RoutePublisher.cs +++ b/src/Presentation/SmartStore.Web.Framework/Routing/RoutePublisher.cs @@ -17,7 +17,7 @@ public RoutePublisher(ITypeFinder typeFinder) public void RegisterRoutes(RouteCollection routes) { - var routeProviderTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true); + var routeProviderTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true); var routeProviders = new List(); foreach (var providerType in routeProviderTypes) @@ -26,10 +26,10 @@ public void RegisterRoutes(RouteCollection routes) routeProviders.Add(provider); } routeProviders = routeProviders.OrderByDescending(rp => rp.Priority).ToList(); - routeProviders.Each(rp => - { - rp.RegisterRoutes(routes); - }); + routeProviders.Each(rp => + { + rp.RegisterRoutes(routes); + }); } } } diff --git a/src/Presentation/SmartStore.Web.Framework/Security/AdminAuthorizeAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Security/AdminAuthorizeAttribute.cs index be10229626..4493d1d338 100644 --- a/src/Presentation/SmartStore.Web.Framework/Security/AdminAuthorizeAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Security/AdminAuthorizeAttribute.cs @@ -2,16 +2,16 @@ using System.Collections.Generic; using System.Linq; using System.Web.Mvc; -using SmartStore.Services.Security; +using SmartStore.Core.Security; namespace SmartStore.Web.Framework.Security { - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=true)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] public class AdminAuthorizeAttribute : FilterAttribute, IAuthorizationFilter { - public IPermissionService PermissionService { get; set; } - - private void HandleUnauthorizedRequest(AuthorizationContext filterContext) + public IPermissionService PermissionService { get; set; } + + private void HandleUnauthorizedRequest(AuthorizationContext filterContext) { filterContext.Result = new HttpUnauthorizedResult(); } @@ -27,29 +27,44 @@ private bool IsAdminPageRequested(AuthorizationContext filterContext) { var adminAttributes = GetAdminAuthorizeAttributes(filterContext.ActionDescriptor); if (adminAttributes != null && adminAttributes.Any()) + { return true; + } + return false; } public void OnAuthorization(AuthorizationContext filterContext) { - if (filterContext == null) - throw new ArgumentNullException("filterContext"); + Guard.NotNull(filterContext, nameof(filterContext)); if (OutputCacheAttribute.IsChildActionCacheActive(filterContext)) - throw new InvalidOperationException("You cannot use [AdminAuthorize] attribute when a child action cache is active"); + { + throw new InvalidOperationException("You cannot use [AdminAuthorize] attribute when a child action cache is active."); + } if (IsAdminPageRequested(filterContext)) { - if (!this.HasAdminAccess(filterContext)) - this.HandleUnauthorizedRequest(filterContext); + if (!HasAdminAccess(filterContext)) + { + HandleUnauthorizedRequest(filterContext); + } } } public virtual bool HasAdminAccess(AuthorizationContext filterContext) { - var result = PermissionService.Authorize(StandardPermissionProvider.AccessAdminPanel); - return result; + if (PermissionService.Authorize(Permissions.System.AccessBackend)) + { + return true; + } + + if (PermissionService.AuthorizeByAlias(Permissions.System.AccessBackend)) + { + return true; + } + + return false; } } } diff --git a/src/Presentation/SmartStore.Web.Framework/Security/AdminValidateIpAddressAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Security/AdminValidateIpAddressAttribute.cs index aca5a27830..9568d12685 100644 --- a/src/Presentation/SmartStore.Web.Framework/Security/AdminValidateIpAddressAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Security/AdminValidateIpAddressAttribute.cs @@ -3,62 +3,61 @@ using System.Web.Mvc; using SmartStore.Core; using SmartStore.Core.Domain.Security; -using SmartStore.Core.Infrastructure; namespace SmartStore.Web.Framework.Security { public class AdminValidateIpAddressAttribute : FilterAttribute, IAuthorizationFilter { - public Lazy WebHelper { get; set; } - public Lazy SecuritySettings { get; set; } + public Lazy WebHelper { get; set; } + public Lazy SecuritySettings { get; set; } - public virtual void OnAuthorization(AuthorizationContext filterContext) - { - if (filterContext == null || filterContext.HttpContext == null) - return; + public virtual void OnAuthorization(AuthorizationContext filterContext) + { + if (filterContext == null || filterContext.HttpContext == null) + return; - HttpRequestBase request = filterContext.HttpContext.Request; - if (request == null) - return; + HttpRequestBase request = filterContext.HttpContext.Request; + if (request == null) + return; - // don't apply filter to child methods - if (filterContext.IsChildAction) - return; + // don't apply filter to child methods + if (filterContext.IsChildAction) + return; - // Prevent lockout - if (filterContext.HttpContext.Request.IsLocal) - return; + // Prevent lockout + if (filterContext.HttpContext.Request.IsLocal) + return; - bool ok = false; - var ipAddresses = SecuritySettings.Value.AdminAreaAllowedIpAddresses; - if (ipAddresses != null && ipAddresses.Count > 0) - { - var webHelper = WebHelper.Value; - var curIpAddress = webHelper.GetCurrentIpAddress(); - foreach (string ip in ipAddresses) - if (ip.Equals(curIpAddress, StringComparison.InvariantCultureIgnoreCase)) - { - ok = true; - break; - } - } - else - { - //no restrictions - ok = true; - } + bool ok = false; + var ipAddresses = SecuritySettings.Value.AdminAreaAllowedIpAddresses; + if (ipAddresses != null && ipAddresses.Count > 0) + { + var webHelper = WebHelper.Value; + var curIpAddress = webHelper.GetCurrentIpAddress(); + foreach (string ip in ipAddresses) + if (ip.Equals(curIpAddress, StringComparison.InvariantCultureIgnoreCase)) + { + ok = true; + break; + } + } + else + { + //no restrictions + ok = true; + } - if (!ok) - { - //ensure that it's not 'Access denied' page - var webHelper = WebHelper.Value; - var thisPageUrl = webHelper.GetThisPageUrl(false); - if (!thisPageUrl.StartsWith(string.Format("{0}admin/security/accessdenied", webHelper.GetStoreLocation()), StringComparison.InvariantCultureIgnoreCase)) - { - //redirect to 'Access denied' page - filterContext.Result = new RedirectResult(webHelper.GetStoreLocation() + "admin/security/accessdenied"); - } - } - } - } + if (!ok) + { + //ensure that it's not 'Access denied' page + var webHelper = WebHelper.Value; + var thisPageUrl = webHelper.GetThisPageUrl(false); + if (!thisPageUrl.StartsWith(string.Format("{0}admin/security/accessdenied", webHelper.GetStoreLocation()), StringComparison.InvariantCultureIgnoreCase)) + { + //redirect to 'Access denied' page + filterContext.Result = new RedirectResult(webHelper.GetStoreLocation() + "admin/security/accessdenied"); + } + } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Security/Captcha/CaptchaSettings.cs b/src/Presentation/SmartStore.Web.Framework/Security/Captcha/CaptchaSettings.cs index 99a0e1e619..3ed092b904 100644 --- a/src/Presentation/SmartStore.Web.Framework/Security/Captcha/CaptchaSettings.cs +++ b/src/Presentation/SmartStore.Web.Framework/Security/Captcha/CaptchaSettings.cs @@ -17,6 +17,8 @@ public class CaptchaSettings : ISettings public bool ShowOnProductReviewPage { get; set; } public string ReCaptchaPublicKey { get; set; } public string ReCaptchaPrivateKey { get; set; } - public bool UseInvisibleReCaptcha { get; set; } - } + public bool UseInvisibleReCaptcha { get; set; } + + public bool CanDisplayCaptcha => Enabled && ReCaptchaPublicKey.HasValue() && ReCaptchaPrivateKey.HasValue(); + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Security/Captcha/HtmlCaptchaExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Security/Captcha/HtmlCaptchaExtensions.cs index 5fc3cd68d4..d00153bfac 100644 --- a/src/Presentation/SmartStore.Web.Framework/Security/Captcha/HtmlCaptchaExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Security/Captcha/HtmlCaptchaExtensions.cs @@ -1,5 +1,4 @@ -using System.Text; -using System.Web; +using System.Web; using System.Web.Mvc; using SmartStore.Core; using SmartStore.Core.Infrastructure; @@ -7,36 +6,42 @@ namespace SmartStore.Web.Framework.Security { - public static class HtmlCaptchaExtensions - { + public static class HtmlCaptchaExtensions + { public static IHtmlString GenerateCaptcha(this HtmlHelper helper) { var captchaSettings = EngineContext.Current.Resolve(); - var workContext = EngineContext.Current.Resolve(); - var widgetUrl = CommonHelper.GetAppSetting("g:RecaptchaWidgetUrl"); - var ident = CommonHelper.GenerateRandomDigitCode(5).ToString(); - var elementId = "recaptcha" + ident; - var siteKey = captchaSettings.ReCaptchaPublicKey; - var callbackName = "recaptchaOnload" + ident; - var url = "{0}?onload={1}&render=explicit&hl={2}".FormatInvariant( - widgetUrl, - callbackName, - workContext.WorkingLanguage.UniqueSeoCode.EmptyNull().ToLower() - ); + if (captchaSettings.CanDisplayCaptcha) + { + var workContext = EngineContext.Current.Resolve(); + var widgetUrl = CommonHelper.GetAppSetting("g:RecaptchaWidgetUrl"); + var ident = CommonHelper.GenerateRandomDigitCode(5).ToString(); + var elementId = "recaptcha" + ident; + var siteKey = captchaSettings.ReCaptchaPublicKey; + var callbackName = "recaptchaOnload" + ident; - var script = new[] - { - "", - "
".FormatInvariant(elementId, siteKey), - "".FormatInvariant(url), - }.StrJoin(""); + var url = "{0}?onload={1}&render=explicit&hl={2}".FormatInvariant( + widgetUrl, + callbackName, + workContext.WorkingLanguage.UniqueSeoCode.EmptyNull().ToLower() + ); - return MvcHtmlString.Create(script); + var script = new[] + { + "", + "
".FormatInvariant(elementId, siteKey), + "".FormatInvariant(url), + }.StrJoin(""); + + return MvcHtmlString.Create(script); + } + + return MvcHtmlString.Empty; } } } diff --git a/src/Presentation/SmartStore.Web.Framework/Security/Captcha/ValidateCaptchaAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Security/Captcha/ValidateCaptchaAttribute.cs index 48f56031a8..d1f65774f2 100644 --- a/src/Presentation/SmartStore.Web.Framework/Security/Captcha/ValidateCaptchaAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Security/Captcha/ValidateCaptchaAttribute.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; @@ -13,78 +14,146 @@ namespace SmartStore.Web.Framework.Security { - public class ValidateCaptchaAttribute : ActionFilterAttribute + public class ValidateCaptchaAttribute : ActionFilterAttribute { - public ValidateCaptchaAttribute() - { - Logger = NullLogger.Instance; - } + public ILogger Logger { get; set; } = NullLogger.Instance; + public Lazy CaptchaSettings { get; set; } + public Lazy LocalizationService { get; set; } - public Lazy CaptchaSettings { get; set; } - public ILogger Logger { get; set; } - public Lazy LocalizationService { get; set; } - - public override void OnActionExecuting(ActionExecutingContext filterContext) + public override void OnActionExecuting(ActionExecutingContext filterContext) { var valid = false; + var verify = true; + + try + { + verify = CaptchaSettings.Value.CanDisplayCaptcha && VerifyRecaptcha(filterContext); + + if (verify) + { + var verifyUrl = CommonHelper.GetAppSetting("g:RecaptchaVerifyUrl"); + var recaptchaResponse = filterContext.HttpContext.Request.Form["g-recaptcha-response"]; + + var url = "{0}?secret={1}&response={2}".FormatInvariant( + verifyUrl, + HttpUtility.UrlEncode(CaptchaSettings.Value.ReCaptchaPrivateKey), + HttpUtility.UrlEncode(recaptchaResponse) + ); + + using (var client = new WebClient()) + { + var jsonResponse = client.DownloadString(url); + using (var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(jsonResponse))) + { + var serializer = new DataContractJsonSerializer(typeof(GoogleRecaptchaApiResponse)); + var result = serializer.ReadObject(memoryStream) as GoogleRecaptchaApiResponse; + + if (result == null) + { + Logger.Error(LocalizationService.Value.GetResource("Common.CaptchaUnableToVerify")); + } + else + { + if (result.ErrorCodes == null) + { + valid = result.Success; + } + else + { + // Do not log 'missing input'. Could be a regular case. + foreach (var error in result.ErrorCodes.Where(x => x.HasValue() && x != "missing-input-response")) + { + Logger.Error("Error while getting Google Recaptcha response: " + error); + } + } + } + } + } + } + } + catch (Exception ex) + { + Logger.ErrorsAll(ex); + } + + // Push the result values as parameters in our action method. + filterContext.ActionParameters["captchaValid"] = valid; - try - { - var captchaSettings = CaptchaSettings.Value; - if (captchaSettings.Enabled && captchaSettings.ReCaptchaPrivateKey.HasValue()) - { - var verifyUrl = CommonHelper.GetAppSetting("g:RecaptchaVerifyUrl"); - var recaptchaResponse = filterContext.HttpContext.Request.Form["g-recaptcha-response"]; - - var url = "{0}?secret={1}&response={2}".FormatInvariant( - verifyUrl, - HttpUtility.UrlEncode(captchaSettings.ReCaptchaPrivateKey), - HttpUtility.UrlEncode(recaptchaResponse) - ); - - using (var client = new WebClient()) - { - var jsonResponse = client.DownloadString(url); - using (var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(jsonResponse))) - { - var serializer = new DataContractJsonSerializer(typeof(GoogleRecaptchaApiResponse)); - var result = serializer.ReadObject(memoryStream) as GoogleRecaptchaApiResponse; - - if (result == null) - { - Logger.Error(LocalizationService.Value.GetResource("Common.CaptchaUnableToVerify")); - } - else - { - if (result.ErrorCodes == null) - { - valid = result.Success; - } - } - } - } - } - } - catch (Exception exception) - { - Logger.ErrorsAll(exception); - } - - // this will push the result value into a parameter in our Action - filterContext.ActionParameters["captchaValid"] = valid; + filterContext.ActionParameters["captchaError"] = !valid && verify + ? LocalizationService.Value.GetResource(CaptchaSettings.Value.UseInvisibleReCaptcha ? "Common.WrongInvisibleCaptcha" : "Common.WrongCaptcha") + : null; base.OnActionExecuting(filterContext); } + + private bool VerifyRecaptcha(ActionExecutingContext context) + { + // Poor method to avoid unnecessary requests to the Google API. + var controller = context?.ActionDescriptor?.ControllerDescriptor?.ControllerName.EmptyNull(); + var action = context?.ActionDescriptor?.ActionName.EmptyNull(); + + if (controller == "Blog" && action == "BlogCommentAdd") + { + return CaptchaSettings.Value.ShowOnBlogCommentPage; + } + else if (controller == "Boards") + { + switch (action) + { + case "TopicCreate": + case "TopicEdit": + case "PostCreate": + case "PostEdit": + return CaptchaSettings.Value.ShowOnForumPage; + } + } + else if (controller == "Customer") + { + switch (action) + { + case "Login": + return CaptchaSettings.Value.ShowOnLoginPage; + case "Register": + return CaptchaSettings.Value.ShowOnRegistrationPage; + } + } + else if (controller == "Home" && action == "ContactUsSend") + { + return CaptchaSettings.Value.ShowOnContactUsPage; + } + else if (controller == "News" && action == "NewsCommentAdd") + { + return CaptchaSettings.Value.ShowOnNewsCommentPage; + } + else if (controller == "Product") + { + switch (action) + { + case "ReviewsAdd": + return CaptchaSettings.Value.ShowOnProductReviewPage; + case "AskQuestionSend": + return CaptchaSettings.Value.ShowOnAskQuestionPage; + case "EmailAFriendSend": + return CaptchaSettings.Value.ShowOnEmailProductToFriendPage; + } + } + else if (controller == "ShoppingCart" && action == "EmailWishlistSend") + { + return CaptchaSettings.Value.ShowOnEmailWishlistToFriendPage; + } + + return true; + } } - [DataContract] - public class GoogleRecaptchaApiResponse - { - [DataMember(Name = "success")] - public bool Success { get; set; } + [DataContract] + public class GoogleRecaptchaApiResponse + { + [DataMember(Name = "success")] + public bool Success { get; set; } - [DataMember(Name = "error-codes")] - public List ErrorCodes { get; set; } - } + [DataMember(Name = "error-codes")] + public List ErrorCodes { get; set; } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Security/FilePermissionHelper.cs b/src/Presentation/SmartStore.Web.Framework/Security/FilePermissionHelper.cs index 35b5720dd3..1ee8d1b697 100644 --- a/src/Presentation/SmartStore.Web.Framework/Security/FilePermissionHelper.cs +++ b/src/Presentation/SmartStore.Web.Framework/Security/FilePermissionHelper.cs @@ -156,12 +156,12 @@ public static IEnumerable GetDirectoriesWrite(IWebHelper webHelper) string rootDir = webHelper.MapPath("~/"); var dirsToCheck = new List(); dirsToCheck.Add(Path.Combine(rootDir, "App_Data")); - dirsToCheck.Add(Path.Combine(rootDir, "App_Data", "Tenants", DataSettings.Current.TenantName)); - dirsToCheck.Add(Path.Combine(rootDir, "App_Data", "Tenants", DataSettings.Current.TenantName, "Media")); - dirsToCheck.Add(Path.Combine(rootDir, "bin")); + dirsToCheck.Add(Path.Combine(rootDir, "App_Data", "Tenants", DataSettings.Current.TenantName)); + dirsToCheck.Add(Path.Combine(rootDir, "App_Data", "Tenants", DataSettings.Current.TenantName, "Media")); + dirsToCheck.Add(Path.Combine(rootDir, "bin")); dirsToCheck.Add(Path.Combine(rootDir, "content")); - dirsToCheck.Add(Path.Combine(rootDir, "Exchange")); - dirsToCheck.Add(Path.Combine(rootDir, "plugins")); + dirsToCheck.Add(Path.Combine(rootDir, "Exchange")); + dirsToCheck.Add(Path.Combine(rootDir, "plugins")); dirsToCheck.Add(Path.Combine(rootDir, "plugins\\bin")); return dirsToCheck; } @@ -175,12 +175,12 @@ public static IEnumerable GetFilesWrite(IWebHelper webHelper) { string rootDir = webHelper.MapPath("~/"); var filesToCheck = new List(); - //filesToCheck.Add(Path.Combine(rootDir, "Global.asax")); - //filesToCheck.Add(Path.Combine(rootDir, "web.config")); - filesToCheck.Add(Path.Combine(rootDir, "App_Data", "Tenants", DataSettings.Current.TenantName, "InstalledPlugins.txt")); - filesToCheck.Add(Path.Combine(rootDir, "App_Data", "Tenants", DataSettings.Current.TenantName, "Settings.txt")); + //filesToCheck.Add(Path.Combine(rootDir, "Global.asax")); + //filesToCheck.Add(Path.Combine(rootDir, "web.config")); + filesToCheck.Add(Path.Combine(rootDir, "App_Data", "Tenants", DataSettings.Current.TenantName, "InstalledPlugins.txt")); + filesToCheck.Add(Path.Combine(rootDir, "App_Data", "Tenants", DataSettings.Current.TenantName, "Settings.txt")); - return filesToCheck; + return filesToCheck; } } } diff --git a/src/Presentation/SmartStore.Web.Framework/Security/Honeypot/Honeypot.cs b/src/Presentation/SmartStore.Web.Framework/Security/Honeypot/Honeypot.cs index c9e58fe770..8bad998c40 100644 --- a/src/Presentation/SmartStore.Web.Framework/Security/Honeypot/Honeypot.cs +++ b/src/Presentation/SmartStore.Web.Framework/Security/Honeypot/Honeypot.cs @@ -7,70 +7,70 @@ namespace SmartStore.Web.Framework.Security { - public class HoneypotField - { - public string Name { get; set; } - public DateTime CreatedOnUtc { get; set; } - } + public class HoneypotField + { + public string Name { get; set; } + public DateTime CreatedOnUtc { get; set; } + } - internal static class Honeypot - { - internal const string TokenFieldName = "__hpToken"; + internal static class Honeypot + { + internal const string TokenFieldName = "__hpToken"; - private static readonly string[] _fieldNames = new[] { "Phone", "Fax", "Email", "Age", "Name", "FirstName", "LastName", "Type", "Custom", "Reason", "Pet", "Question", "Region" }; - private static readonly string _fieldSuffix = CommonHelper.GenerateRandomDigitCode(5); + private static readonly string[] _fieldNames = new[] { "Phone", "Fax", "Email", "Age", "Name", "FirstName", "LastName", "Type", "Custom", "Reason", "Pet", "Question", "Region" }; + private static readonly string _fieldSuffix = CommonHelper.GenerateRandomDigitCode(5); - public static HoneypotField CreateToken() - { - var r = new Random(); + public static HoneypotField CreateToken() + { + var r = new Random(); - // Create a rondom field name with pattern "[random1]-[random2][suffix]" - var len = _fieldNames.Length; - var fieldName = string.Concat(_fieldNames[r.Next(0, len)], "-", _fieldNames[r.Next(0, len)], _fieldSuffix); + // Create a rondom field name with pattern "[random1]-[random2][suffix]" + var len = _fieldNames.Length; + var fieldName = string.Concat(_fieldNames[r.Next(0, len)], "-", _fieldNames[r.Next(0, len)], _fieldSuffix); - return new HoneypotField - { - Name = fieldName, - CreatedOnUtc = DateTime.UtcNow - }; - } + return new HoneypotField + { + Name = fieldName, + CreatedOnUtc = DateTime.UtcNow + }; + } - public static string SerializeToken(HoneypotField token) - { - Guard.NotNull(token, nameof(token)); + public static string SerializeToken(HoneypotField token) + { + Guard.NotNull(token, nameof(token)); - var json = JsonConvert.SerializeObject(token); - var encoded = MachineKey.Protect(Encoding.UTF8.GetBytes(json)); + var json = JsonConvert.SerializeObject(token); + var encoded = MachineKey.Protect(Encoding.UTF8.GetBytes(json)); - var result = Convert.ToBase64String(encoded); - return result; - } + var result = Convert.ToBase64String(encoded); + return result; + } - public static HoneypotField DeserializeToken(string token) - { - Guard.NotEmpty(token, nameof(token)); + public static HoneypotField DeserializeToken(string token) + { + Guard.NotEmpty(token, nameof(token)); - var encoded = Convert.FromBase64String(token); - var decoded = MachineKey.Unprotect(encoded); - var json = Encoding.UTF8.GetString(decoded); + var encoded = Convert.FromBase64String(token); + var decoded = MachineKey.Unprotect(encoded); + var json = Encoding.UTF8.GetString(decoded); - var result = JsonConvert.DeserializeObject(json); - return result; - } + var result = JsonConvert.DeserializeObject(json); + return result; + } - public static bool IsBot(HttpContextBase httpContext) - { - var tokenString = httpContext.Request.Form[TokenFieldName]; - if (tokenString.IsEmpty()) - { - throw new InvalidOperationException("The required honeypot form field is missing. Please render the field with 'Html.HoneypotField()'."); - } + public static bool IsBot(HttpContextBase httpContext) + { + var tokenString = httpContext.Request.Form[TokenFieldName]; + if (tokenString.IsEmpty()) + { + throw new InvalidOperationException("The required honeypot form field is missing. Please render the field with 'Html.HoneypotField()'."); + } - var token = DeserializeToken(tokenString); - var trap = httpContext.Request.Form[token.Name]; - var isBot = trap == null || trap.Length > 0 || (DateTime.UtcNow - token.CreatedOnUtc).TotalMilliseconds < 2000; + var token = DeserializeToken(tokenString); + var trap = httpContext.Request.Form[token.Name]; + var isBot = trap == null || trap.Length > 0 || (DateTime.UtcNow - token.CreatedOnUtc).TotalMilliseconds < 2000; - return isBot; - } - } + return isBot; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Security/Honeypot/HtmlHoneypotExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Security/Honeypot/HtmlHoneypotExtensions.cs index 49593920a1..328b3ce01d 100644 --- a/src/Presentation/SmartStore.Web.Framework/Security/Honeypot/HtmlHoneypotExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Security/Honeypot/HtmlHoneypotExtensions.cs @@ -1,20 +1,19 @@ -using System; -using System.Web.Mvc; +using System.Web.Mvc; using System.Web.Mvc.Html; namespace SmartStore.Web.Framework.Security { - public static class HtmlHoneypotExtensions - { - public static MvcHtmlString HoneypotField(this HtmlHelper html) - { - var token = Honeypot.CreateToken(); - var serializedToken = Honeypot.SerializeToken(token); + public static class HtmlHoneypotExtensions + { + public static MvcHtmlString HoneypotField(this HtmlHelper html) + { + var token = Honeypot.CreateToken(); + var serializedToken = Honeypot.SerializeToken(token); - var textField = html.TextBox(token.Name, string.Empty, new { @class = "required-text-input", autocomplete = "off" }).ToHtmlString(); - var hiddenField = html.Hidden(Honeypot.TokenFieldName, serializedToken).ToHtmlString(); + var textField = html.TextBox(token.Name, string.Empty, new { @class = "required-text-input", autocomplete = "off" }).ToHtmlString(); + var hiddenField = html.Hidden(Honeypot.TokenFieldName, serializedToken).ToHtmlString(); - return MvcHtmlString.Create(textField + hiddenField); - } - } + return MvcHtmlString.Create(textField + hiddenField); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Security/Honeypot/ValidateHoneypotAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Security/Honeypot/ValidateHoneypotAttribute.cs index 3d026fc75f..b064401d34 100644 --- a/src/Presentation/SmartStore.Web.Framework/Security/Honeypot/ValidateHoneypotAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Security/Honeypot/ValidateHoneypotAttribute.cs @@ -7,32 +7,32 @@ namespace SmartStore.Web.Framework.Security { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] - public class ValidateHoneypotAttribute : FilterAttribute, IAuthorizationFilter + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] + public class ValidateHoneypotAttribute : FilterAttribute, IAuthorizationFilter { - public ValidateHoneypotAttribute() - { - Logger = NullLogger.Instance; - } + public ValidateHoneypotAttribute() + { + Logger = NullLogger.Instance; + } - public SecuritySettings SecuritySettings { get; set; } - public ILogger Logger { get; set; } - public Localizer T { get; set; } - public Lazy WebHelper { get; set; } + public SecuritySettings SecuritySettings { get; set; } + public ILogger Logger { get; set; } + public Localizer T { get; set; } + public Lazy WebHelper { get; set; } - public void OnAuthorization(AuthorizationContext filterContext) - { - if (!SecuritySettings.EnableHoneypotProtection) - return; + public void OnAuthorization(AuthorizationContext filterContext) + { + if (!SecuritySettings.EnableHoneypotProtection) + return; - var isBot = Honeypot.IsBot(filterContext.HttpContext); - if (!isBot) - return; - - Logger.Warn("Honeypot detected a bot and rejected the request."); + var isBot = Honeypot.IsBot(filterContext.HttpContext); + if (!isBot) + return; - var redirectUrl = WebHelper.Value.GetThisPageUrl(true); - filterContext.Result = new RedirectResult(redirectUrl); - } - } + Logger.Warn("Honeypot detected a bot and rejected the request."); + + var redirectUrl = WebHelper.Value.GetThisPageUrl(true); + filterContext.Result = new RedirectResult(redirectUrl); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Security/RewriteUrlAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Security/RewriteUrlAttribute.cs deleted file mode 100644 index f63ad4b5ba..0000000000 --- a/src/Presentation/SmartStore.Web.Framework/Security/RewriteUrlAttribute.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System; -using System.Web.Mvc; -using SmartStore.Core; -using SmartStore.Core.Data; -using SmartStore.Core.Domain.Security; -using SmartStore.Core.Domain.Seo; - -namespace SmartStore.Web.Framework.Security -{ - public class RewriteUrlAttribute : FilterAttribute, IAuthorizationFilter - { - public RewriteUrlAttribute(SslRequirement sslRequirement) - { - this.SslRequirement = sslRequirement; - this.Order = 100; - } - - public Lazy SeoSettings { get; set; } - public Lazy SecuritySettings { get; set; } - public Lazy StoreContext { get; set; } - public Lazy WebHelper { get; set; } - - public virtual void OnAuthorization(AuthorizationContext filterContext) - { - // don't apply filter to child methods - if (filterContext.IsChildAction) - return; - - // only redirect for GET requests, - // otherwise the browser might not propagate the verb and request body correctly. - if (!String.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) - return; - - if (!DataSettings.DatabaseIsInstalled()) - return; - - var securitySettings = SecuritySettings.Value; - var isLocalRequest = filterContext.HttpContext.Request.IsLocal; - - if (!securitySettings.UseSslOnLocalhost && isLocalRequest) - return; - - var webHelper = WebHelper.Value; - - var currentConnectionSecured = webHelper.IsCurrentConnectionSecured(); - var currentStore = StoreContext.Value.CurrentStore; - - if (currentStore.ForceSslForAllPages) - { - // all pages are forced to be SSL no matter of the specified value - this.SslRequirement = SslRequirement.Yes; - } - - var securityMode = currentStore.GetSecurityMode(); - - switch (this.SslRequirement) - { - case SslRequirement.Yes: - { - if (!currentConnectionSecured && securityMode > HttpSecurityMode.Unsecured) - { - // Redirect to HTTPS version of page - string url = webHelper.GetThisPageUrl(true, true); - filterContext.Result = new RedirectResult(url, !isLocalRequest); - } - } - break; - case SslRequirement.No: - { - if (currentConnectionSecured) - { - // Redirect to HTTP version of page - string url = webHelper.GetThisPageUrl(true, false); - filterContext.Result = new RedirectResult(url, !isLocalRequest); - } - } - break; - case SslRequirement.Retain: - { - // Do nothing - } - break; - default: - throw new SmartException("Unsupported SslRequirement parameter"); - } - - ApplyCanonicalHostNameRule(filterContext, isLocalRequest, securityMode); - } - - private void ApplyCanonicalHostNameRule(AuthorizationContext filterContext, bool isLocalRequest, HttpSecurityMode securityMode) - { - var rule = SeoSettings.Value.CanonicalHostNameRule; - if (rule == CanonicalHostNameRule.NoRule) - return; - - var uri = filterContext.Result != null - ? new Uri(((RedirectResult)filterContext.Result).Url) - : filterContext.HttpContext.Request.Url; - - if (uri.IsLoopback) - { - // allows testing of "localtest.me" - return; - } - - var url = uri.ToString(); - var protocol = uri.Scheme.ToLower(); - var isHttps = protocol.IsCaseInsensitiveEqual("https"); - var startsWith = "{0}://www.".FormatInvariant(protocol); - var hasPrefix = url.StartsWith(startsWith, StringComparison.OrdinalIgnoreCase); - - if (isHttps) - { - if (securityMode == HttpSecurityMode.SharedSsl) - { - // Don't attempt to redirect when shared SSL is being used and current request is secured. - // Redirecting from http://ssl.bla.com to https://www.ssl.bla.com will most probably fail. - return; - } - } - - if (hasPrefix && rule == CanonicalHostNameRule.OmitWww) - { - url = url.Replace("://www.", "://"); - filterContext.Result = new RedirectResult(url, !isLocalRequest); - } - - if (!hasPrefix && rule == CanonicalHostNameRule.RequireWww) - { - url = url.Replace("{0}://".FormatInvariant(protocol), startsWith); - filterContext.Result = new RedirectResult(url, !isLocalRequest); - } - } - - public SslRequirement SslRequirement { get; set; } - } -} diff --git a/src/Presentation/SmartStore.Web.Framework/Security/SameSiteBrowserDetector.cs b/src/Presentation/SmartStore.Web.Framework/Security/SameSiteBrowserDetector.cs new file mode 100644 index 0000000000..ad0d832ead --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Security/SameSiteBrowserDetector.cs @@ -0,0 +1,66 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Runtime.CompilerServices; + +namespace SmartStore.Web.Framework.Security +{ + public static class SameSiteBrowserDetector + { + // Same as https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/ + public static bool DisallowsSameSiteNone(string userAgent) + { + if (string.IsNullOrEmpty(userAgent)) + { + return true; + } + + // Note that these detections are a starting point. See https://www.chromium.org/updates/same-site/incompatible-clients for more detections. + + // Cover all iOS based browsers here. This includes: + // - Safari on iOS 12 for iPhone, iPod Touch, iPad + // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad + // - Chrome on iOS 12 for iPhone, iPod Touch, iPad + // All of which are broken by SameSite=None, because they use the iOS networking stack + if (userAgent.Contains("CPU iPhone OS 12") || userAgent.Contains("iPad; CPU OS 12")) + { + return true; + } + + // Cover Mac OS X based browsers that use the Mac OS networking stack. This includes: + // - Safari on Mac OS X. + // This does not include: + // - Chrome on Mac OS X + // Because they do not use the Mac OS networking stack. + if (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") && + userAgent.Contains("Version/") && userAgent.Contains("Safari")) + { + return true; + } + + // Cover Chrome 50-69, because some versions are broken by SameSite=None, + // and none in this range require it. + // Note: this covers some pre-Chromium Edge versions, + // but pre-Chromium Edge does not require SameSite=None. + if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6")) + { + return true; + } + + // Unreal Engine runs Chromium 59, but does not advertise as Chrome until 4.23. Treat versions of Unreal + // that don't specify their Chrome version as lacking support for SameSite=None. + if (userAgent.Contains("UnrealEngine") && !userAgent.Contains("Chrome")) + { + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool AllowsSameSiteNone(string userAgent) + { + return !DisallowsSameSiteNone(userAgent); + } + } +} \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Security/SanitizeHtmlAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Security/SanitizeHtmlAttribute.cs index ddfb9cec9f..34939ca5e2 100644 --- a/src/Presentation/SmartStore.Web.Framework/Security/SanitizeHtmlAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Security/SanitizeHtmlAttribute.cs @@ -3,25 +3,25 @@ namespace SmartStore.Web.Framework.Security { - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] - public class SanitizeHtmlAttribute : Attribute, IMetadataAware - { - public SanitizeHtmlAttribute() - { - IsFragment = true; - } + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] + public class SanitizeHtmlAttribute : Attribute, IMetadataAware + { + public SanitizeHtmlAttribute() + { + IsFragment = true; + } - public bool IsFragment - { - get; - set; - } + public bool IsFragment + { + get; + set; + } - public void OnMetadataCreated(ModelMetadata metadata) - { - Guard.NotNull(metadata, nameof(metadata)); + public void OnMetadataCreated(ModelMetadata metadata) + { + Guard.NotNull(metadata, nameof(metadata)); - metadata.RequestValidationEnabled = false; - } - } + metadata.RequestValidationEnabled = false; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Seo/GenericPath.cs b/src/Presentation/SmartStore.Web.Framework/Seo/GenericPath.cs new file mode 100644 index 0000000000..4f1335b535 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Seo/GenericPath.cs @@ -0,0 +1,33 @@ +using System; +using System.Web.Routing; + +namespace SmartStore.Web.Framework.Seo +{ + public class GenericPath : IComparable, IEquatable + { + public string EntityName { get; set; } + public Route Route { get; set; } + public string IdParamName { get; set; } + public int Order { get; set; } + + public int CompareTo(GenericPath other) + { + return this.Order.CompareTo(other.Order); + } + + public bool Equals(GenericPath other) + { + return other != null && this.EntityName.IsCaseInsensitiveEqual(other.EntityName); + } + + public override bool Equals(object obj) + { + return Equals(obj as GenericPath); + } + + public override int GetHashCode() + { + return this.EntityName.ToLowerInvariant().GetHashCode(); + } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/Seo/GenericPathRoute.cs b/src/Presentation/SmartStore.Web.Framework/Seo/GenericPathRoute.cs index ccf42a7bfd..c5ebc451c7 100644 --- a/src/Presentation/SmartStore.Web.Framework/Seo/GenericPathRoute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Seo/GenericPathRoute.cs @@ -17,10 +17,14 @@ namespace SmartStore.Web.Framework.Seo /// public class GenericPathRoute : LocalizedRoute { - // Key = Prefix, Value = EntityType - private static readonly Multimap _urlPrefixes = new Multimap(StringComparer.OrdinalIgnoreCase); - - /// + const string SlugKey = "generic_se_name"; + + // Key = Prefix, Value = EntityType + private static readonly Multimap _urlPrefixes = new Multimap(StringComparer.OrdinalIgnoreCase); + + private static readonly Dictionary _paths = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// /// Initializes a new instance of the System.Web.Routing.Route class, using the specified URL pattern and handler class. /// /// The URL pattern for the route. @@ -67,22 +71,47 @@ public GenericPathRoute(string url, RouteValueDictionary defaults, RouteValueDic { } - public static void RegisterUrlPrefix(string prefix, params string[] entityNames) - { - Guard.NotEmpty(prefix, nameof(prefix)); + public static void RegisterPaths(params GenericPath[] paths) + { + foreach (var path in paths) + { + if (path.EntityName.IsEmpty()) + { + throw new ArgumentException($"'{nameof(path)}.{nameof(path.EntityName)}' is required.", nameof(paths)); + } + + if (path.IdParamName.IsEmpty()) + { + throw new ArgumentException($"'{nameof(path)}.{nameof(path.IdParamName)}' is required.", nameof(paths)); + } + + if (path.Route == null) + { + throw new ArgumentException($"'{nameof(path)}.{nameof(path.Route)}' is required.", nameof(paths)); + } + + _paths[path.EntityName] = path; + } + } + + public static IEnumerable Paths { get; } = _paths.Values.OrderBy(x => x.Order); + + public static void RegisterUrlPrefix(string prefix, params string[] entityNames) + { + Guard.NotEmpty(prefix, nameof(prefix)); + + _urlPrefixes.AddRange(prefix, entityNames); + } - _urlPrefixes.AddRange(prefix, entityNames); - } - - public static string GetUrlPrefixFor(string entityName) - { - Guard.NotEmpty(entityName, nameof(entityName)); + public static string GetUrlPrefixFor(string entityName) + { + Guard.NotEmpty(entityName, nameof(entityName)); - if (_urlPrefixes.Count == 0) - return null; + if (_urlPrefixes.Count == 0) + return null; - return _urlPrefixes.FirstOrDefault(x => x.Value.Contains(entityName, StringComparer.OrdinalIgnoreCase)).Key; - } + return _urlPrefixes.FirstOrDefault(x => x.Value.Contains(entityName, StringComparer.OrdinalIgnoreCase)).Key; + } /// /// Returns information about the requested route. @@ -97,19 +126,19 @@ public override RouteData GetRouteData(HttpContextBase httpContext) if (data != null && DataSettings.DatabaseIsInstalled()) { - var slug = data.Values["generic_se_name"] as string; - - if (TryResolveUrlPrefix(slug, out var urlPrefix, out var actualSlug, out var entityNames)) - { - slug = actualSlug; - } - - var urlRecordService = EngineContext.Current.Resolve(); - var urlRecord = urlRecordService.GetBySlug(slug); + var slug = NormalizeSlug(data.Values); + + if (TryResolveUrlPrefix(slug, out var urlPrefix, out var actualSlug, out var entityNames)) + { + slug = actualSlug; + } + + var urlRecordService = EngineContext.Current.Resolve(); + var urlRecord = urlRecordService.GetBySlug(slug); if (urlRecord == null) { // no URL record found - return NotFound(data); + return NotFound(data); } if (!urlRecord.IsActive) @@ -122,123 +151,93 @@ public override RouteData GetRouteData(HttpContextBase httpContext) var webHelper = EngineContext.Current.Resolve(); var response = httpContext.Response; response.Status = "301 Moved Permanently"; - if (urlPrefix.HasValue()) - { - activeSlug = urlPrefix + "/" + activeSlug; - } - response.RedirectLocation = string.Format("{0}{1}", webHelper.GetStoreLocation(false), activeSlug); + if (urlPrefix.HasValue()) + { + activeSlug = urlPrefix + "/" + activeSlug; + } + response.RedirectLocation = string.Format("{0}{1}", webHelper.GetStoreLocation(), activeSlug); response.End(); return null; } else { - // no active slug found - return NotFound(data); - } + // no active slug found + return NotFound(data); + } } - // Verify prefix matches any assigned entity name - if (entityNames != null && !entityNames.Contains(urlRecord.EntityName, StringComparer.OrdinalIgnoreCase)) - { - // does NOT match - return NotFound(data); - } + // Verify prefix matches any assigned entity name + if (entityNames != null && !entityNames.Contains(urlRecord.EntityName, StringComparer.OrdinalIgnoreCase)) + { + // does NOT match + return NotFound(data); + } - // process URL - data.DataTokens["UrlRecord"] = urlRecord; - data.Values["SeName"] = slug; + // process URL + data.DataTokens["UrlRecord"] = urlRecord; + data.Values["SeName"] = slug; - string controller, action, paramName; + //string controller, action, paramName; - switch (urlRecord.EntityName.ToLowerInvariant()) + if (!_paths.TryGetValue(urlRecord.EntityName, out var path)) { - case "product": - { - controller = "Product"; - action = "ProductDetails"; - paramName = "productid"; - } - break; - case "category": - { - controller = "Catalog"; - action = "Category"; - paramName = "categoryid"; - } - break; - case "manufacturer": - { - controller = "Catalog"; - action = "Manufacturer"; - paramName = "manufacturerid"; - } - break; - case "topic": - { - controller = "Topic"; - action = "TopicDetails"; - paramName = "topicId"; - } - break; - case "newsitem": - { - controller = "News"; - action = "NewsItem"; - paramName = "newsItemId"; - } - break; - case "blogpost": - { - controller = "Blog"; - action = "BlogPost"; - paramName = "blogPostId"; - } - break; - default: - { - throw new SmartException(string.Format("Unsupported EntityName for UrlRecord: {0}", urlRecord.EntityName)); - } + throw new SmartException(string.Format("Unsupported EntityName for UrlRecord: {0}", urlRecord.EntityName)); } - data.Values["controller"] = controller; - data.Values["action"] = action; - data.Values[paramName] = urlRecord.EntityId; - } + var route = path.Route; + + data.Values["controller"] = route.Defaults["controller"]; + data.Values["action"] = route.Defaults["action"]; + data.Values[path.IdParamName ?? "id"] = urlRecord.EntityId; + } + + return data; + } + + private string NormalizeSlug(RouteValueDictionary routeValues) + { + var slug = routeValues[SlugKey] as string; + var lastChar = slug[slug.Length - 1]; + if (lastChar == '/' || lastChar == '\\') + { + slug = slug.TrimEnd('/', '\\'); + routeValues[SlugKey] = slug; + } + + return slug; + } + + private RouteData NotFound(RouteData data) + { + data.Values["controller"] = "Error"; + data.Values["action"] = "NotFound"; return data; } - private RouteData NotFound(RouteData data) - { - data.Values["controller"] = "Error"; - data.Values["action"] = "NotFound"; - - return data; - } - - private bool TryResolveUrlPrefix(string slug, out string urlPrefix, out string actualSlug, out ICollection entityNames) - { - urlPrefix = null; - actualSlug = null; - entityNames = null; - - if (_urlPrefixes.Count > 0) - { - var firstSepIndex = slug.IndexOf('/'); - if (firstSepIndex > 0) - { - var prefix = slug.Substring(0, firstSepIndex); - if (_urlPrefixes.ContainsKey(prefix)) - { - urlPrefix = prefix; - entityNames = _urlPrefixes[prefix]; - actualSlug = slug.Substring(prefix.Length + 1); - return true; - } - } - } - - return false; - } + private bool TryResolveUrlPrefix(string slug, out string urlPrefix, out string actualSlug, out ICollection entityNames) + { + urlPrefix = null; + actualSlug = null; + entityNames = null; + + if (_urlPrefixes.Count > 0) + { + var firstSepIndex = slug.IndexOf('/'); + if (firstSepIndex > 0) + { + var prefix = slug.Substring(0, firstSepIndex); + if (_urlPrefixes.ContainsKey(prefix)) + { + urlPrefix = prefix; + entityNames = _urlPrefixes[prefix]; + actualSlug = slug.Substring(prefix.Length + 1); + return true; + } + } + } + + return false; + } } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Seo/GenericPathRouteExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Seo/GenericPathRouteExtensions.cs index cf931f15bc..e83872c01a 100644 --- a/src/Presentation/SmartStore.Web.Framework/Seo/GenericPathRouteExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Seo/GenericPathRouteExtensions.cs @@ -33,14 +33,8 @@ public static Route MapGenericPathRoute(this RouteCollection routes, string name public static Route MapGenericPathRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { - if (routes == null) - { - throw new ArgumentNullException("routes"); - } - if (url == null) - { - throw new ArgumentNullException("url"); - } + Guard.NotNull(routes, nameof(routes)); + Guard.NotNull(url, nameof(url)); var route = new GenericPathRoute(url, new MvcRouteHandler()) { @@ -59,29 +53,29 @@ public static Route MapGenericPathRoute(this RouteCollection routes, string name return route; } - /// - /// Changes the url pattern of an existing named route. - /// - /// Route collection - /// Name of the route - /// The new url pattern - /// The route instance - public static Route ChangeRouteUrl(this RouteCollection routes, string name, string url) - { - Guard.NotNull(routes, nameof(routes)); - Guard.NotEmpty(name, nameof(name)); - Guard.NotEmpty(url, nameof(url)); - - var route = routes[name] as Route; + /// + /// Changes the url pattern of an existing named route. + /// + /// Route collection + /// Name of the route + /// The new url pattern + /// The route instance + public static Route ChangeRouteUrl(this RouteCollection routes, string name, string url) + { + Guard.NotNull(routes, nameof(routes)); + Guard.NotEmpty(name, nameof(name)); + Guard.NotEmpty(url, nameof(url)); - if (route == null) - { - throw new ArgumentException("The route '{0}' does not exist or is not assignable from 'Route'.".FormatInvariant(name), nameof(name)); - } + var route = routes[name] as Route; - route.Url = url; - - return route; - } + if (route == null) + { + throw new ArgumentException("The route '{0}' does not exist or is not assignable from 'Route'.".FormatInvariant(name), nameof(name)); + } + + route.Url = url; + + return route; + } } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Seo/ISeoModel.cs b/src/Presentation/SmartStore.Web.Framework/Seo/ISeoModel.cs new file mode 100644 index 0000000000..d39485def5 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Seo/ISeoModel.cs @@ -0,0 +1,37 @@ +using System.Web.Mvc; +using SmartStore.Web.Framework.Localization; + +namespace SmartStore.Web.Framework.Seo +{ + public interface ISeoModel : ILocalizedModel + { + [SmartResourceDisplayName("Admin.Configuration.Seo.MetaTitle")] + [AllowHtml] + string MetaTitle { get; } + + [SmartResourceDisplayName("Admin.Configuration.Seo.MetaDescription")] + [AllowHtml] + string MetaDescription { get; } + + [SmartResourceDisplayName("Admin.Configuration.Seo.MetaKeywords")] + [AllowHtml] + string MetaKeywords { get; } + } + + public class SeoModelLocal : ILocalizedModelLocal + { + public int LanguageId { get; set; } + + [SmartResourceDisplayName("Admin.Configuration.Seo.MetaTitle")] + [AllowHtml] + public string MetaTitle { get; set; } + + [SmartResourceDisplayName("Admin.Configuration.Seo.MetaDescription")] + [AllowHtml] + public string MetaDescription { get; set; } + + [SmartResourceDisplayName("Admin.Configuration.Seo.MetaKeywords")] + [AllowHtml] + public string MetaKeywords { get; set; } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/Seo/RewriteUrlAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Seo/RewriteUrlAttribute.cs new file mode 100644 index 0000000000..f17ffe3e30 --- /dev/null +++ b/src/Presentation/SmartStore.Web.Framework/Seo/RewriteUrlAttribute.cs @@ -0,0 +1,278 @@ +using System; +using System.Web.Hosting; +using System.Web.Mvc; +using System.Web.Routing; +using SmartStore.Core; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Security; +using SmartStore.Core.Domain.Seo; +using SmartStore.Core.Domain.Stores; + +namespace SmartStore.Web.Framework.Seo +{ + public class RewriteUrlAttribute : FilterAttribute, IAuthorizationFilter + { + public RewriteUrlAttribute(SslRequirement sslRequirement) + { + SslRequirement = sslRequirement; + AppendTrailingSlash = RouteTable.Routes.AppendTrailingSlash; + LowercaseUrls = RouteTable.Routes.LowercaseUrls; + Order = 100; + } + + public Lazy SeoSettings { get; set; } + public Lazy SecuritySettings { get; set; } + public Lazy StoreContext { get; set; } + public Lazy WebHelper { get; set; } + public Lazy WorkContext { get; set; } + + public SslRequirement SslRequirement { get; set; } + public bool AppendTrailingSlash { get; set; } + public bool LowercaseUrls { get; set; } + + public virtual void OnAuthorization(AuthorizationContext filterContext) + { + // don't apply filter to child methods + if (filterContext.IsChildAction) + return; + + // only redirect for GET requests, + // otherwise the browser might not propagate the verb and request body correctly. + if (!string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) + return; + + if (HostingEnvironment.IsHosted && !DataSettings.DatabaseIsInstalled()) + return; + + var currentStore = StoreContext.Value.CurrentStore; + var uri = filterContext.HttpContext.Request.Url; + var originalUrl = new Uri(uri.GetLeftPart(UriPartial.Authority) + filterContext.HttpContext.Request.RawUrl); + + var rewriteContext = new RewriteContext + { + ControllerContext = filterContext, + OriginalUrl = originalUrl, + IsLoopback = uri.IsLoopback || filterContext.HttpContext.Request.IsLocal, + CurrentStore = currentStore, + SecurityMode = currentStore.GetSecurityMode(), + IsAdmin = WorkContext.Value.IsAdmin + }; + + // Applies HTTP protocol rule + TryRewriteScheme(rewriteContext); + + // Applies canonical host name rule + TryRewriteHostName(rewriteContext); + + // Applies trailing slash and lowercase rules + TryRewritePath(rewriteContext); + + if (rewriteContext.Url.HasValue()) + { + filterContext.Result = new RedirectResult(rewriteContext.Url, rewriteContext.Permanent ?? !rewriteContext.IsLoopback); + } + } + + /// + /// Applies HTTP protocol rule + /// + private bool TryRewriteScheme(RewriteContext context) + { + if (context.IsLoopback && !SecuritySettings.Value.UseSslOnLocalhost) + return false; + + var webHelper = WebHelper.Value; + + var currentConnectionSecured = webHelper.IsCurrentConnectionSecured(); + var currentStore = context.CurrentStore; + + if (currentStore.ForceSslForAllPages) + { + // all pages are forced to be SSL no matter of the specified value + this.SslRequirement = SslRequirement.Yes; + } + + switch (this.SslRequirement) + { + case SslRequirement.Yes: + { + if (!currentConnectionSecured && context.SecurityMode > HttpSecurityMode.Unsecured) + { + // Redirect to HTTPS version of page + context.Url = webHelper.GetThisPageUrl(true, true); + return true; + } + } + break; + case SslRequirement.No: + { + if (currentConnectionSecured) + { + // Redirect to HTTP version of page + context.Url = webHelper.GetThisPageUrl(true, false); + return true; + } + } + break; + default: + return false; + } + + return false; + } + + /// + /// Applies canonical host name rule + /// + private bool TryRewriteHostName(RewriteContext context) + { + var rule = SeoSettings.Value.CanonicalHostNameRule; + if (rule == CanonicalHostNameRule.NoRule) + return false; + + if (context.IsLoopback) + { + // Allows testing of "localtest.me" + return false; + } + + var uri = context.Url == null ? context.OriginalUrl : new Uri(context.Url); + var url = context.Url ?? uri.ToString(); + var protocol = uri.Scheme.ToLower(); + var isHttps = protocol.IsCaseInsensitiveEqual("https"); + var startsWith = "{0}://www.".FormatInvariant(protocol); + var hasPrefix = url.StartsWith(startsWith, StringComparison.OrdinalIgnoreCase); + + if (isHttps) + { + if (context.SecurityMode == HttpSecurityMode.SharedSsl) + { + // Don't attempt to redirect when shared SSL is being used and current request is secured. + // Redirecting from http://ssl.bla.com to https://www.ssl.bla.com will most probably fail. + return false; + } + } + + if (hasPrefix && rule == CanonicalHostNameRule.OmitWww) + { + context.Url = url.Replace("://www.", "://"); + return true; + } + + if (!hasPrefix && rule == CanonicalHostNameRule.RequireWww) + { + context.Url = url.Replace("{0}://".FormatInvariant(protocol), startsWith); + return true; + } + + return false; + } + + /// + /// Applies trailing slash and lowercase rules + /// + private bool TryRewritePath(RewriteContext context) + { + // Be tolerant in backend + if (context.IsAdmin) + return false; + + // Don't try to fix root path + if (context.ControllerContext.HttpContext.Request.Path == "/") + return false; + + bool rewritten = false; + + var url = context.Url ?? context.OriginalUrl.ToString(); + var queryIndex = url.IndexOf('?'); + + if (queryIndex == -1) + { + bool hasTrailingSlash = url[url.Length - 1] == '/'; + + if (this.AppendTrailingSlash) + { + // Append a trailing slash to the end of the URL. + if (!hasTrailingSlash) + { + url += "/"; + rewritten = true; + } + } + else + { + // Trim a trailing slash from the end of the URL. + if (hasTrailingSlash) + { + url = url.TrimEnd('/'); + rewritten = true; + } + } + } + else + { + bool hasTrailingSlash = url[queryIndex - 1] == '/'; + + if (this.AppendTrailingSlash) + { + // Append a trailing slash to the end of the URL but before the query string. + if (!hasTrailingSlash) + { + url = url.Insert(queryIndex, "/"); + rewritten = true; + } + } + else + { + // Trim a trailing slash to the end of the URL but before the query string. + if (hasTrailingSlash) + { + url = url.Remove(queryIndex - 1, 1); + rewritten = true; + } + } + } + + if (this.LowercaseUrls) + { + foreach (char c in url) + { + if (char.IsUpper(c)) + { + var qIndex = url.IndexOf('?'); + url = qIndex == -1 + ? url.ToLower() + : url.Substring(0, qIndex).ToLower() + url.Substring(qIndex); + + rewritten = true; + break; + } + if (c == '?') + { + break; + } + } + } + + if (rewritten) + { + context.Url = url; + } + + return rewritten; + } + + class RewriteContext + { + public Uri OriginalUrl { get; set; } + public bool IsLoopback { get; set; } + public HttpSecurityMode SecurityMode { get; set; } + public ControllerContext ControllerContext { get; set; } + public Store CurrentStore { get; set; } + public bool IsAdmin { get; set; } + + public string Url { get; set; } + public bool? Permanent { get; set; } + } + } +} diff --git a/src/Presentation/SmartStore.Web.Framework/Security/SslRequirement.cs b/src/Presentation/SmartStore.Web.Framework/Seo/SslRequirement.cs similarity index 86% rename from src/Presentation/SmartStore.Web.Framework/Security/SslRequirement.cs rename to src/Presentation/SmartStore.Web.Framework/Seo/SslRequirement.cs index 65b1414832..65e27e8014 100644 --- a/src/Presentation/SmartStore.Web.Framework/Security/SslRequirement.cs +++ b/src/Presentation/SmartStore.Web.Framework/Seo/SslRequirement.cs @@ -1,6 +1,5 @@ -namespace SmartStore.Web.Framework.Security +namespace SmartStore.Web.Framework.Seo { - public enum SslRequirement { /// diff --git a/src/Presentation/SmartStore.Web.Framework/Settings/LoadSettingAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Settings/LoadSettingAttribute.cs index df6e25159f..9ab06a0c57 100644 --- a/src/Presentation/SmartStore.Web.Framework/Settings/LoadSettingAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Settings/LoadSettingAttribute.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Web.Mvc; using SmartStore.Core.Configuration; using SmartStore.Services; @@ -8,132 +9,150 @@ namespace SmartStore.Web.Framework.Settings { - [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] - public class LoadSettingAttribute : FilterAttribute, IActionFilter - { - public sealed class SettingParam - { - public ISettings Instance { get; set; } - public ParameterDescriptor Parameter { get; set; } - } - - protected int _storeId; - protected SettingParam[] _settingParams; - - public LoadSettingAttribute() - : this(true) - { - } - - public LoadSettingAttribute(bool updateParameterFromStore) - { - UpdateParameterFromStore = updateParameterFromStore; - } - - public bool UpdateParameterFromStore { get; set; } - public ICommonServices Services { get; set; } - - public virtual void OnActionExecuting(ActionExecutingContext filterContext) - { - // Get the current configured store id - _storeId = filterContext.Controller.GetActiveStoreScopeConfiguration(Services.StoreService, Services.WorkContext); - Func predicate = (x) => new[] { "storescope", "storeid" }.Contains(x.ParameterName, StringComparer.OrdinalIgnoreCase); - var storeScopeParam = FindActionParameters(filterContext.ActionDescriptor, false, false, predicate).FirstOrDefault(); - if (storeScopeParam != null) - { - // We found an action param named storeScope with type int. Assign our storeId to it. - filterContext.ActionParameters[storeScopeParam.ParameterName] = _storeId; - } - - // Find the required ISettings concrete types in ActionDescriptor.GetParameters() - _settingParams = FindActionParameters(filterContext.ActionDescriptor) - .Select(x => - { - // Load settings for the settings type obtained with FindActionParameters() - var settings = UpdateParameterFromStore - ? Services.Settings.LoadSetting(x.ParameterType, _storeId) - : filterContext.ActionParameters[x.ParameterName] as ISettings; - - if (settings == null) - { - throw new InvalidOperationException($"Could not load settings for type '{x.ParameterType.FullName}'."); - } - - // Replace settings from action parameters with our loaded settings - if (UpdateParameterFromStore) - { - filterContext.ActionParameters[x.ParameterName] = settings; - } - - return new SettingParam - { - Instance = settings, - Parameter = x - }; - }) - .ToArray(); - } - - public virtual void OnActionExecuted(ActionExecutedContext filterContext) - { - var viewResult = filterContext.Result as ViewResultBase; - - if (viewResult != null) - { - var model = viewResult.Model; - - if (model == null) - { - // Nothing to override. E.g. insufficient permission. - return; - } - - var settingsHelper = new StoreDependingSettingHelper(filterContext.Controller.ViewData); - - foreach (var param in _settingParams) - { - settingsHelper.GetOverrideKeys(param.Instance, model, _storeId, Services.Settings); - } - } - } - - protected IEnumerable FindActionParameters( - ActionDescriptor actionDescriptor, - bool requireDefaultConstructor = true, - bool throwIfNotFound = true, - Func predicate = null) - { - Guard.NotNull(actionDescriptor, nameof(actionDescriptor)); - - var t = typeof(T); - - var query = actionDescriptor - .GetParameters() - .Where(x => t.IsAssignableFrom(x.ParameterType)); - - if (predicate != null) - { - query = query.Where(predicate); - } - - if (throwIfNotFound && !query.Any()) - { - throw new InvalidOperationException( - $"A controller action method with a '{this.GetType().Name}' attribute requires an action parameter of type '{t.Name}' in order to execute properly."); - } - - if (requireDefaultConstructor) - { - foreach (var param in query) - { - if (!param.ParameterType.HasDefaultConstructor()) - { - throw new InvalidOperationException($"The parameter '{param.ParameterName}' must have a default parameterless constructor."); - } - } - } - - return query; - } - } + [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + public class LoadSettingAttribute : FilterAttribute, IActionFilter + { + public sealed class SettingParam + { + public ISettings Instance { get; set; } + public ParameterDescriptor Parameter { get; set; } + } + + protected int _storeId; + protected SettingParam[] _settingParams; + + public LoadSettingAttribute() + : this(true) + { + } + + public LoadSettingAttribute(bool updateParameterFromStore) + { + UpdateParameterFromStore = updateParameterFromStore; + } + + public bool UpdateParameterFromStore { get; set; } + public bool IsRootedModel { get; set; } + public ICommonServices Services { get; set; } + + public virtual void OnActionExecuting(ActionExecutingContext filterContext) + { + // Get the current configured store id + _storeId = filterContext.Controller.GetActiveStoreScopeConfiguration(Services.StoreService, Services.WorkContext); + Func predicate = (x) => new[] { "storescope", "storeid" }.Contains(x.ParameterName, StringComparer.OrdinalIgnoreCase); + var storeScopeParam = FindActionParameters(filterContext.ActionDescriptor, false, false, predicate).FirstOrDefault(); + if (storeScopeParam != null) + { + // We found an action param named storeScope with type int. Assign our storeId to it. + filterContext.ActionParameters[storeScopeParam.ParameterName] = _storeId; + } + + // Find the required ISettings concrete types in ActionDescriptor.GetParameters() + _settingParams = FindActionParameters(filterContext.ActionDescriptor) + .Select(x => + { + // Load settings for the settings type obtained with FindActionParameters() + var settings = UpdateParameterFromStore + ? Services.Settings.LoadSetting(x.ParameterType, _storeId) + : filterContext.ActionParameters[x.ParameterName] as ISettings; + + if (settings == null) + { + throw new InvalidOperationException($"Could not load settings for type '{x.ParameterType.FullName}'."); + } + + // Replace settings from action parameters with our loaded settings + if (UpdateParameterFromStore) + { + filterContext.ActionParameters[x.ParameterName] = settings; + } + + return new SettingParam + { + Instance = settings, + Parameter = x + }; + }) + .ToArray(); + } + + public virtual void OnActionExecuted(ActionExecutedContext filterContext) + { + var viewResult = filterContext.Result as ViewResultBase; + + if (viewResult != null) + { + var model = viewResult.Model; + + if (model == null) + { + // Nothing to override. E.g. insufficient permission. + return; + } + + var modelType = model.GetType(); + var settingsHelper = new StoreDependingSettingHelper(filterContext.Controller.ViewData); + if (IsRootedModel) + { + settingsHelper.CreateViewDataObject(_storeId); + } + + foreach (var param in _settingParams) + { + var settingInstance = param.Instance; + var modelInstance = model; + + if (IsRootedModel) + { + modelInstance = modelType.GetProperty(settingInstance.GetType().Name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase)?.GetValue(model); + if (modelInstance == null) + { + continue; + } + } + + settingsHelper.GetOverrideKeys(settingInstance, modelInstance, _storeId, Services.Settings, !IsRootedModel); + } + } + } + + protected IEnumerable FindActionParameters( + ActionDescriptor actionDescriptor, + bool requireDefaultConstructor = true, + bool throwIfNotFound = true, + Func predicate = null) + { + Guard.NotNull(actionDescriptor, nameof(actionDescriptor)); + + var t = typeof(T); + + var query = actionDescriptor + .GetParameters() + .Where(x => t.IsAssignableFrom(x.ParameterType)); + + if (predicate != null) + { + query = query.Where(predicate); + } + + if (throwIfNotFound && !query.Any()) + { + throw new InvalidOperationException( + $"A controller action method with a '{this.GetType().Name}' attribute requires an action parameter of type '{t.Name}' in order to execute properly."); + } + + if (requireDefaultConstructor) + { + foreach (var param in query) + { + if (!param.ParameterType.HasDefaultConstructor()) + { + throw new InvalidOperationException($"The parameter '{param.ParameterName}' must have a default parameterless constructor."); + } + } + } + + return query; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Settings/SaveSettingAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Settings/SaveSettingAttribute.cs index 278679dbde..e84d13adb9 100644 --- a/src/Presentation/SmartStore.Web.Framework/Settings/SaveSettingAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Settings/SaveSettingAttribute.cs @@ -4,90 +4,90 @@ namespace SmartStore.Web.Framework.Settings { - [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] - public sealed class SaveSettingAttribute : LoadSettingAttribute - { - private FormCollection _form; - private IDisposable _settingsWriteBatch; - - public SaveSettingAttribute() - : base(true) - { - } - - public SaveSettingAttribute(bool updateParameterFromStore) - : base(updateParameterFromStore) - { - } - - public override void OnActionExecuting(ActionExecutingContext filterContext) - { - base.OnActionExecuting(filterContext); - - if (!filterContext.Controller.ViewData.ModelState.IsValid) - { - return; - } - - // Find the required FormCollection parameter in ActionDescriptor.GetParameters() - var formParam = FindActionParameters(filterContext.ActionDescriptor, requireDefaultConstructor: false, throwIfNotFound: false).FirstOrDefault(); - _form = formParam != null - ? (FormCollection)filterContext.ActionParameters[formParam.ParameterName] - : BindFormCollection(filterContext.Controller.ControllerContext); - - - _settingsWriteBatch = Services.Settings.BeginScope(); - } - - public override void OnActionExecuted(ActionExecutedContext filterContext) - { - if (filterContext.Controller.ViewData.ModelState.IsValid) - { - var updateSettings = true; - var redirectResult = filterContext.Result as RedirectToRouteResult; - if (redirectResult != null) - { - var controllerName = redirectResult.RouteValues["controller"] as string; - var areaName = redirectResult.RouteValues["area"] as string; - if (controllerName.IsCaseInsensitiveEqual("security") && areaName.IsCaseInsensitiveEqual("admin")) - { - // Insufficient permission. We must not save because the action did not run. - updateSettings = false; - } - } - - if (updateSettings) - { - var settingHelper = new StoreDependingSettingHelper(filterContext.Controller.ViewData); - - foreach (var param in _settingParams) - { - settingHelper.UpdateSettings(param.Instance, _form, _storeId, Services.Settings); - } - } - } - - if (_settingsWriteBatch != null) - { - _settingsWriteBatch.Dispose(); - _settingsWriteBatch = null; - } - - base.OnActionExecuted(filterContext); - } - - private FormCollection BindFormCollection(ControllerContext controllerContext) - { - var bindingContext = new ModelBindingContext - { - ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(FormCollection)), - ModelState = controllerContext.Controller.ViewData.ModelState, - ValueProvider = controllerContext.Controller.ValueProvider - }; - - var modelBinder = ModelBinders.Binders.GetBinder(typeof(FormCollection)); - - return (FormCollection)modelBinder.BindModel(controllerContext, bindingContext); - } - } + [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] + public sealed class SaveSettingAttribute : LoadSettingAttribute + { + private FormCollection _form; + private IDisposable _settingsWriteBatch; + + public SaveSettingAttribute() + : base(true) + { + } + + public SaveSettingAttribute(bool updateParameterFromStore) + : base(updateParameterFromStore) + { + } + + public override void OnActionExecuting(ActionExecutingContext filterContext) + { + base.OnActionExecuting(filterContext); + + if (!filterContext.Controller.ViewData.ModelState.IsValid) + { + return; + } + + // Find the required FormCollection parameter in ActionDescriptor.GetParameters() + var formParam = FindActionParameters(filterContext.ActionDescriptor, requireDefaultConstructor: false, throwIfNotFound: false).FirstOrDefault(); + _form = formParam != null + ? (FormCollection)filterContext.ActionParameters[formParam.ParameterName] + : BindFormCollection(filterContext.Controller.ControllerContext); + + + _settingsWriteBatch = Services.Settings.BeginScope(); + } + + public override void OnActionExecuted(ActionExecutedContext filterContext) + { + if (filterContext.Controller.ViewData.ModelState.IsValid) + { + var updateSettings = true; + var redirectResult = filterContext.Result as RedirectToRouteResult; + if (redirectResult != null) + { + var controllerName = redirectResult.RouteValues["controller"] as string; + var areaName = redirectResult.RouteValues["area"] as string; + if (controllerName.IsCaseInsensitiveEqual("security") && areaName.IsCaseInsensitiveEqual("admin")) + { + // Insufficient permission. We must not save because the action did not run. + updateSettings = false; + } + } + + if (updateSettings) + { + var settingHelper = new StoreDependingSettingHelper(filterContext.Controller.ViewData); + + foreach (var param in _settingParams) + { + settingHelper.UpdateSettings(param.Instance, _form, _storeId, Services.Settings); + } + } + } + + if (_settingsWriteBatch != null) + { + _settingsWriteBatch.Dispose(); + _settingsWriteBatch = null; + } + + base.OnActionExecuted(filterContext); + } + + private FormCollection BindFormCollection(ControllerContext controllerContext) + { + var bindingContext = new ModelBindingContext + { + ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(FormCollection)), + ModelState = controllerContext.Controller.ViewData.ModelState, + ValueProvider = controllerContext.Controller.ValueProvider + }; + + var modelBinder = ModelBinders.Binders.GetBinder(typeof(FormCollection)); + + return (FormCollection)modelBinder.BindModel(controllerContext, bindingContext); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Settings/StoreDependingSettingData.cs b/src/Presentation/SmartStore.Web.Framework/Settings/StoreDependingSettingData.cs index 5894b7ab6f..a82194f63d 100644 --- a/src/Presentation/SmartStore.Web.Framework/Settings/StoreDependingSettingData.cs +++ b/src/Presentation/SmartStore.Web.Framework/Settings/StoreDependingSettingData.cs @@ -1,16 +1,12 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace SmartStore.Web.Framework.Settings { - public class StoreDependingSettingData - { - public StoreDependingSettingData() - { - OverrideSettingKeys = new List(); - } - - public int ActiveStoreScopeConfiguration { get; set; } - public List OverrideSettingKeys { get; set; } - public string RootSettingClass { get; set; } - } + public class StoreDependingSettingData + { + public int ActiveStoreScopeConfiguration { get; set; } + public HashSet OverrideSettingKeys { get; set; } = new HashSet(StringComparer.OrdinalIgnoreCase); + public string RootSettingClass { get; set; } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Settings/StoreDependingSettingHelper.cs b/src/Presentation/SmartStore.Web.Framework/Settings/StoreDependingSettingHelper.cs index 7605dd7934..9c1adb51de 100644 --- a/src/Presentation/SmartStore.Web.Framework/Settings/StoreDependingSettingHelper.cs +++ b/src/Presentation/SmartStore.Web.Framework/Settings/StoreDependingSettingHelper.cs @@ -1,5 +1,8 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Web.Mvc; using SmartStore.ComponentModel; using SmartStore.Core.Infrastructure; @@ -9,121 +12,128 @@ namespace SmartStore.Web.Framework.Settings { - public class StoreDependingSettingHelper - { - private ViewDataDictionary _viewData; - - public StoreDependingSettingHelper(ViewDataDictionary viewData) - { - _viewData = viewData; - } - - public static string ViewDataKey => "StoreDependingSettingData"; - - public StoreDependingSettingData Data - { - get - { - return _viewData[ViewDataKey] as StoreDependingSettingData; - } - } - - private bool IsOverrideChecked(string settingKey, FormCollection form) - { - var rawOverrideKey = form.AllKeys.FirstOrDefault(k => k.IsCaseInsensitiveEqual(settingKey + "_OverrideForStore")); - - if (rawOverrideKey.HasValue()) - { - var checkboxValue = form[rawOverrideKey].EmptyNull().ToLower(); - return checkboxValue.Contains("on") || checkboxValue.Contains("true"); - } - return false; - } - - public bool IsOverrideChecked(object settings, string name, FormCollection form) - { - var key = settings.GetType().Name + "." + name; - return IsOverrideChecked(key, form); - } - - public void AddOverrideKey(object settings, string name) - { - if (Data == null) - { - throw new SmartException("You must call GetOverrideKeys or CreateViewDataObject before AddOverrideKey."); - } - - var key = settings.GetType().Name + "." + name; - Data.OverrideSettingKeys.Add(key); - } - - public void CreateViewDataObject(int activeStoreScopeConfiguration, string rootSettingClass = null) - { - _viewData[ViewDataKey] = new StoreDependingSettingData - { - ActiveStoreScopeConfiguration = activeStoreScopeConfiguration, - RootSettingClass = rootSettingClass - }; - } + public class StoreDependingSettingHelper + { + private ViewDataDictionary _viewData; + + public StoreDependingSettingHelper(ViewDataDictionary viewData) + { + _viewData = viewData; + } + + public static string ViewDataKey => "StoreDependingSettingData"; + + public StoreDependingSettingData Data => _viewData[ViewDataKey] as StoreDependingSettingData; + + public bool IsOverrideChecked(object settings, string name, FormCollection form) + { + var key = settings.GetType().Name + "." + name; + return IsOverrideChecked(key, form); + } + + private bool IsOverrideChecked(string settingKey, FormCollection form) + { + var rawOverrideKey = form.AllKeys.FirstOrDefault(k => k.IsCaseInsensitiveEqual(settingKey + "_OverrideForStore")); + + if (rawOverrideKey.HasValue()) + { + var checkboxValue = form[rawOverrideKey].EmptyNull().ToLower(); + return checkboxValue.Contains("on") || checkboxValue.Contains("true"); + } + + return false; + } + + public void AddOverrideKey(object settings, string name) + { + if (Data == null) + { + throw new SmartException("You must call GetOverrideKeys or CreateViewDataObject before AddOverrideKey."); + } + + var key = settings.GetType().Name + "." + name; + Data.OverrideSettingKeys.Add(key); + } + + public void CreateViewDataObject(int activeStoreScopeConfiguration, string rootSettingClass = null) + { + _viewData[ViewDataKey] = new StoreDependingSettingData + { + ActiveStoreScopeConfiguration = activeStoreScopeConfiguration, + RootSettingClass = rootSettingClass + }; + } public void GetOverrideKeys( - object settings, - object model, - int storeId, - ISettingService settingService, - bool isRootModel = true, - ILocalizedModelLocal localized = null, - int? index = null) + object settings, + object model, + int storeId, + ISettingService settingService, + bool isRootModel = true, + Func propertyNameMapper = null) { - if (storeId <= 0) - { - // Single store mode -> there are no overrides. - return; - } + GetOverrideKeysInternal(settings, model, storeId, settingService, isRootModel, propertyNameMapper, null); + } - var data = Data ?? new StoreDependingSettingData(); - var settingName = settings.GetType().Name; - var properties = settings.GetType().GetProperties(); - var localizedEntityService = EngineContext.Current.Resolve(); + private void GetOverrideKeysInternal( + object settings, + object model, + int storeId, + ISettingService settingService, + bool isRootModel, + Func propertyNameMapper, + int? localeIndex) + { + if (storeId <= 0) + { + // Single store mode -> there are no overrides. + return; + } + var data = Data ?? new StoreDependingSettingData(); + var settingType = settings.GetType(); var modelType = model.GetType(); + var settingName = settingType.Name; + var modelProperties = modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance); + var localizedModelLocal = model as ILocalizedModelLocal; + var localizedEntityService = EngineContext.Current.Resolve(); - foreach (var prop in properties) + foreach (var prop in modelProperties) { - var name = prop.Name; - var modelProperty = modelType.GetProperty(name); - - if (modelProperty == null) - { - // Setting is not configurable or missing or whatever... however we don't need the override info. - continue; - } - - string key = null; - - if (localized == null) - { - key = settingName + "." + name; - - if (settingService.GetSettingByKey(key, storeId: storeId) == null) - { - key = null; - } - } - else - { - var value = localizedEntityService.GetLocalizedValue(localized.LanguageId, 0, settingName, name); - if (!string.IsNullOrEmpty(value)) - { - key = string.Concat("Locales[", index.ToString(), "].", name); - } - } - - if (key != null) - { - data.OverrideSettingKeys.Add(key); - } - } + var name = propertyNameMapper?.Invoke(prop.Name) ?? prop.Name; + + var settingProperty = settingType.GetProperty(name, BindingFlags.Public | BindingFlags.Instance); + + if (settingProperty == null) + { + // Setting is not configurable or missing or whatever... however we don't need the override info. + continue; + } + + string key = null; + + if (localizedModelLocal == null) + { + key = settingName + "." + name; + if (settingService.GetSettingByKey(key, storeId: storeId) == null) + { + key = null; + } + } + else if (localeIndex.HasValue) + { + var value = localizedEntityService.GetLocalizedValue(localizedModelLocal.LanguageId, storeId, settingName, name); + if (!string.IsNullOrEmpty(value)) + { + key = settingName + "." + "Locales[" + localeIndex.ToString() + "]." + name; + } + } + + if (key != null) + { + data.OverrideSettingKeys.Add(key); + } + } if (isRootModel) { @@ -132,118 +142,113 @@ public void GetOverrideKeys( _viewData[ViewDataKey] = data; } + + if (model is ILocalizedModel) + { + var localesProperty = modelType.GetProperty("Locales", BindingFlags.Public | BindingFlags.Instance); + if (localesProperty != null) + { + if (localesProperty.GetValue(model) is IEnumerable locales) + { + int i = 0; + foreach (var locale in locales) + { + GetOverrideKeysInternal(settings, locale, storeId, settingService, false, propertyNameMapper, i); + i++; + } + } + } + } } - public void GetOverrideKey( - string formKey, - string settingName, - object settings, - int storeId, - ISettingService settingService, - ILocalizedModelLocal localized = null, - int? index = null) - { - if (storeId <= 0) - { - // Single store mode -> there are no overrides. - return; - } - - var key = formKey; - - if (localized == null) - { - if (settingService.GetSettingByKey(string.Concat(settings.GetType().Name, ".", settingName), storeId: storeId) == null) - { - key = null; - } - } - else - { - // TODO if required - throw new ArgumentException("Localized override key not supported yet."); - } - - if (key != null) - { - var data = Data ?? new StoreDependingSettingData(); - data.OverrideSettingKeys.Add(key); - } - } - - /// - /// Updates settings for a store. - /// - /// Settings class instance. - /// Form value collection. - /// Store identifier. - /// Setting service. - /// Localized model. - /// Function to map property names. Return null to skip a property. - public void UpdateSettings( - object settings, - FormCollection form, - int storeId, - ISettingService settingService, - ILocalizedModelLocal localized = null, - Func propertyNameMapper = null) + public void GetOverrideKey(string formKey, string settingName, object settings, int storeId, ISettingService settingService) { - var settingName = settings.GetType().Name; - var properties = FastProperty.GetProperties(localized == null ? settings.GetType() : localized.GetType()).Values; - - foreach (var prop in properties) - { - var name = prop.Name; - - if (propertyNameMapper != null) - { - name = propertyNameMapper(name); - } - - if (string.IsNullOrWhiteSpace(name)) - { - continue; - } - - var key = string.Concat(settingName, ".", name); - - if (storeId == 0 || IsOverrideChecked(key, form)) - { - dynamic value = prop.GetValue(localized == null ? settings : localized); - settingService.SetSetting(key, value == null ? "" : value, storeId, false); - } - else if (storeId > 0) - { - settingService.DeleteSetting(key, storeId); - } - } + if (storeId <= 0) + { + // Single store mode -> there are no overrides. + return; + } + + var key = formKey; + if (settingService.GetSettingByKey(string.Concat(settings.GetType().Name, ".", settingName), storeId: storeId) == null) + { + key = null; + } + + if (key != null) + { + var data = Data ?? new StoreDependingSettingData(); + data.OverrideSettingKeys.Add(key); + } + } + + /// + /// Updates settings for a store. + /// + /// Settings class instance. + /// Form value collection. + /// Store identifier. + /// Setting service. + /// Function to map property names. Return null to skip a property. + public void UpdateSettings( + object settings, + FormCollection form, + int storeId, + ISettingService settingService, + Func propertyNameMapper = null) + { + var settingType = settings.GetType(); + var settingName = settingType.Name; + var settingProperties = FastProperty.GetProperties(settingType).Values; + + foreach (var prop in settingProperties) + { + var name = propertyNameMapper?.Invoke(prop.Name) ?? prop.Name; + + if (string.IsNullOrWhiteSpace(name)) + { + continue; + } + + var key = string.Concat(settingName, ".", name); + + if (storeId == 0 || IsOverrideChecked(key, form)) + { + dynamic value = prop.GetValue(settings); + settingService.SetSetting(key, value ?? string.Empty, storeId, false); + } + else if (storeId > 0) + { + settingService.DeleteSetting(key, storeId); + } + } } - public void UpdateSetting( - string formKey, - string settingName, - object settings, - FormCollection form, - int storeId, - ISettingService settingService, - ILocalizedModelLocal localized = null) - { - if (storeId == 0 || IsOverrideChecked(formKey, form)) - { - var prop = FastProperty.GetProperty(localized == null ? settings.GetType() : localized.GetType(), settingName); - if (prop != null) - { - dynamic value = prop.GetValue(localized == null ? settings : localized); - var key = string.Concat(settings.GetType().Name, ".", settingName); - - settingService.SetSetting(key, value == null ? "" : value, storeId, false); - } - } - else if (storeId > 0) - { - var key = string.Concat(settings.GetType().Name, ".", settingName); - settingService.DeleteSetting(key, storeId); - } - } - } + public void UpdateSetting( + string formKey, + string settingName, + object settings, + FormCollection form, + int storeId, + ISettingService settingService) + { + var settingType = settings.GetType(); + + if (storeId == 0 || IsOverrideChecked(formKey, form)) + { + var prop = FastProperty.GetProperty(settingType, settingName); + if (prop != null) + { + dynamic value = prop.GetValue(settings); + var key = string.Concat(settingType.Name, ".", settingName); + settingService.SetSetting(key, value ?? string.Empty, storeId, false); + } + } + else if (storeId > 0) + { + var key = string.Concat(settingType.Name, ".", settingName); + settingService.DeleteSetting(key, storeId); + } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj index bcf02806b0..3af5f9ff89 100644 --- a/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj +++ b/src/Presentation/SmartStore.Web.Framework/SmartStore.Web.Framework.csproj @@ -1,6 +1,5 @@  - Debug AnyCPU @@ -11,7 +10,7 @@ Properties SmartStore.Web.Framework SmartStore.Web.Framework - v4.6.1 + v4.7.2 512 @@ -72,87 +71,93 @@ False ..\..\packages\Antlr.3.5.0.2\lib\Antlr3.Runtime.dll - - ..\..\packages\Autofac.4.9.0\lib\net45\Autofac.dll + + ..\..\packages\Autofac.5.2.0\lib\net461\Autofac.dll - - ..\..\packages\Autofac.Mvc5.4.0.2\lib\net45\Autofac.Integration.Mvc.dll - True + + ..\..\packages\Autofac.Mvc5.5.0.0\lib\net461\Autofac.Integration.Mvc.dll - - ..\..\packages\Autofac.WebApi2.4.2.0\lib\net45\Autofac.Integration.WebApi.dll + + ..\..\packages\Autofac.WebApi2.5.0.0\lib\net461\Autofac.Integration.WebApi.dll - - ..\..\packages\AutoMapper.8.0.0\lib\net461\AutoMapper.dll + + ..\..\packages\AutoprefixerHost.1.1.10\lib\net45\AutoprefixerHost.dll - - ..\..\packages\BundleTransformer.Autoprefixer.1.10.2\lib\net40\BundleTransformer.Autoprefixer.dll + + ..\..\packages\BundleTransformer.Autoprefixer.1.12.1\lib\net40\BundleTransformer.Autoprefixer.dll ..\..\packages\BundleTransformer.Core.1.10.0\lib\net40\BundleTransformer.Core.dll - - ..\..\packages\BundleTransformer.SassAndScss.1.10.0\lib\net40\BundleTransformer.SassAndScss.dll + + ..\..\packages\BundleTransformer.SassAndScss.1.12.1\lib\net40\BundleTransformer.SassAndScss.dll - - ..\..\packages\CommonServiceLocator.2.0.4\lib\net46\CommonServiceLocator.dll + + ..\..\packages\CommonServiceLocator.2.0.5\lib\net47\CommonServiceLocator.dll ..\..\packages\DotLiquid.2.0.254\lib\net45\DotLiquid.dll - ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + ..\..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll True - ..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + ..\..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.SqlServer.dll True ..\..\packages\FluentValidation.7.4.0\lib\net45\FluentValidation.dll - - ..\..\packages\JavaScriptEngineSwitcher.Core.3.0.0\lib\net45\JavaScriptEngineSwitcher.Core.dll + + ..\..\packages\JavaScriptEngineSwitcher.Core.3.3.0\lib\net45\JavaScriptEngineSwitcher.Core.dll + + + ..\..\packages\LibSassHost.1.2.6\lib\net471\LibSassHost.dll - - ..\..\packages\LibSassHost.1.2.2\lib\net45\LibSassHost.dll + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll - - ..\..\packages\Microsoft.Data.Edm.5.8.4\lib\net40\Microsoft.Data.Edm.dll + + ..\..\packages\Microsoft.OData.Core.6.15.0\lib\portable-net45+win+wpa81\Microsoft.OData.Core.dll - - ..\..\packages\Microsoft.Data.OData.5.8.4\lib\net40\Microsoft.Data.OData.dll + + ..\..\packages\Microsoft.OData.Edm.6.15.0\lib\portable-net45+win+wpa81\Microsoft.OData.Edm.dll - - ..\..\packages\Microsoft.Data.Services.Client.5.8.4\lib\net40\Microsoft.Data.Services.Client.dll + + ..\..\packages\Microsoft.Spatial.6.15.0\lib\portable-net45+win+wpa81\Microsoft.Spatial.dll True ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - ..\..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + ..\..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + ..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.7\lib\net45\System.Net.Http.Formatting.dll + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + ..\..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll - - ..\..\packages\System.Spatial.5.8.4\lib\net40\System.Spatial.dll - - - ..\..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll @@ -168,16 +173,15 @@ ..\..\packages\Microsoft.AspNet.WebApi.Cors.5.2.7\lib\net45\System.Web.Http.Cors.dll - - ..\..\packages\Microsoft.AspNet.WebApi.OData.5.7.0\lib\net45\System.Web.Http.OData.dll - True - ..\..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.7\lib\net45\System.Web.Http.WebHost.dll ..\..\packages\Microsoft.AspNet.Mvc.5.2.7\lib\net45\System.Web.Mvc.dll + + ..\..\packages\Microsoft.AspNet.OData.5.10.0\lib\net45\System.Web.OData.dll + False ..\..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll @@ -217,16 +221,21 @@ + + + + + - - + + @@ -235,10 +244,14 @@ + + + + @@ -398,15 +411,16 @@ - + + - - + + @@ -459,14 +473,16 @@ + + + - - + diff --git a/src/Presentation/SmartStore.Web.Framework/SmartUrlRoutingModule.cs b/src/Presentation/SmartStore.Web.Framework/SmartUrlRoutingModule.cs index 67097db01a..271d4c8e31 100644 --- a/src/Presentation/SmartStore.Web.Framework/SmartUrlRoutingModule.cs +++ b/src/Presentation/SmartStore.Web.Framework/SmartUrlRoutingModule.cs @@ -1,236 +1,293 @@ using System; using System.Collections.Generic; -using System.Collections.Concurrent; +using System.IO; +using System.Reflection; +using System.Text.RegularExpressions; using System.Web; +using System.Web.Hosting; using System.Web.Routing; -using System.Text.RegularExpressions; -using SmartStore.Utilities; -using SmartStore.Core; +using SmartStore.Collections; +using SmartStore.Core.Data; +using SmartStore.Core.Domain.Customers; +using SmartStore.Core.Infrastructure; using SmartStore.Core.IO; -using System.IO; -using System.Web.Hosting; -using System.Reflection; +using SmartStore.Utilities; +using SmartStore.Web.Framework.Security; using SmartStore.Web.Framework.Theming; -using SmartStore.Core.Infrastructure; -using SmartStore.Core.Events; -using SmartStore.Core.Data; -using SmartStore.Collections; namespace SmartStore.Web.Framework { - /// - /// Request event sequence: - /// - BeginRequest - /// - AuthenticateRequest - /// - PostAuthenticateRequest - /// - AuthorizeRequest - /// - PostAuthorizeRequest - /// - ResolveRequestCache - /// - PostResolveRequestCache - /// - MapRequestHandler - /// - PostMapRequestHandler - /// - AcquireRequestState - /// - PostAcquireRequestState - /// - PreRequestHandlerExecute - /// - PostRequestHandlerExecute - /// - ReleaseRequestState - /// - PostReleaseRequestState - /// - UpdateRequestCache - /// - PostUpdateRequestCache - /// - LogRequest - /// - PostLogRequest - /// - EndRequest - /// - PreSendRequestHeaders - /// - PreSendRequestContent - /// - public class SmartUrlRoutingModule : IHttpModule - { - private static readonly object _contextKey = new object(); - - private static readonly ICollection> _actions = - new SyncedCollection>(new List>()) { ReadLockFree = true }; - - private static readonly ICollection _routes = - new SyncedCollection(new List()) { ReadLockFree = true }; - - static SmartUrlRoutingModule() - { - StopSubDirMonitoring(); - } - - private static void StopSubDirMonitoring() - { - try - { - // http://stackoverflow.com/questions/2248825/asp-net-restarts-when-a-folder-is-created-renamed-or-deleted - var prop = typeof(HttpRuntime).GetProperty("FileChangesMonitor", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); - var o = prop.GetValue(null, null); - - var fi = o.GetType().GetField("_dirMonSubdirs", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase); - var monitor = fi.GetValue(o); - var mi = monitor.GetType().GetMethod("StopMonitoring", BindingFlags.Instance | BindingFlags.NonPublic); - mi.Invoke(monitor, new object[] { }); - } - catch { } - } - - public void Init(HttpApplication application) - { - if (!DataSettings.DatabaseIsInstalled()) - return; - - if (application.Context.Items[_contextKey] == null) - { - application.Context.Items[_contextKey] = _contextKey; - - if (CommonHelper.IsDevEnvironment && HttpContext.Current.IsDebuggingEnabled) - { - // Handle plugin static file in DevMode - application.PostAuthorizeRequest += (s, e) => PostAuthorizeRequest(new HttpContextWrapper(((HttpApplication)s).Context)); - application.PreSendRequestHeaders += (s, e) => PreSendRequestHeaders(new HttpContextWrapper(((HttpApplication)s).Context)); - } - - application.PostResolveRequestCache += (s, e) => PostResolveRequestCache(new HttpContextWrapper(((HttpApplication)s).Context)); - - // Publish event to give plugins the chance to register custom event handlers for the request lifecycle. - foreach (var action in _actions) - { - action(application); - } - - // Set app to fully initialized state on very first request - EngineContext.Current.IsFullyInitialized = true; - } - } - - /// - /// Registers an action that is called on application init. Call this to register HTTP request lifecycle callbacks / event handlers. - /// - /// Action - public static void RegisterAction(Action action) - { - Guard.NotNull(action, nameof(action)); - - _actions.Add(action); - } - - /// - /// Registers a path pattern which should be handled by the - /// - /// The app relative path to handle (regular expression, e.g. /sitemap.xml or /mini-profiler-resources/(.*)) - /// The http method constraint (regular expression, e.g. GET|POST) - /// - /// For performance reasons SmartStore.NET is configured to serve static files (files with extensions other than those mapped to managed handlers) - /// through the native StaticFileModule. This method lets you define exceptions to this default rule: every path registered as routable gets - /// handled by the and therefore enables dynamic processing of (physically non-existent) static files. - /// - public static void RegisterRoutablePath(string path, string verb = ".*") - { - Guard.NotEmpty(path, nameof(path)); - - if (path.IsWebUrl()) - { - throw new ArgumentException("Only relative paths are allowed.", "path"); - } - - var rgPath = new Regex(path, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); - var rgVerb = new Regex(verb, RegexOptions.CultureInvariant | RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); - - _routes.Add(new RoutablePath { PathPattern = rgPath, HttpMethodPattern = rgVerb }); - } - - public static bool HasMatchingPathHandler(string path, string method = "GET") - { - Guard.NotEmpty(path, nameof(path)); - Guard.NotEmpty(method, nameof(method)); - - if (_routes.Count > 0) - { - foreach (var route in _routes) - { - if (route.PathPattern.IsMatch(path) && route.HttpMethodPattern.IsMatch(method)) - { - return true; - } - } - } - - return false; - } - - public virtual void PostAuthorizeRequest(HttpContextBase context) - { - var request = context?.Request; - if (request == null) - return; - - if (IsExtensionPath(request) && WebHelper.IsStaticResourceRequested(request)) - { - // We're in debug mode and in dev environment - var file = HostingEnvironment.VirtualPathProvider.GetFile(request.AppRelativeCurrentExecutionFilePath) as DebugVirtualFile; - if (file != null) - { - context.Items["DebugFile"] = file; - context.Response.WriteFile(file.PhysicalPath); - context.Response.End(); - } - } - } - - public virtual void PreSendRequestHeaders(HttpContextBase context) - { - if (context?.Response == null) - return; - - var file = context.Items?["DebugFile"] as DebugVirtualFile; - if (file != null) - { - context.Response.AddFileDependency(file.PhysicalPath); - context.Response.ContentType = MimeTypes.MapNameToMimeType(Path.GetFileName(file.PhysicalPath)); - context.Response.Cache.SetNoStore(); - context.Response.Cache.SetLastModifiedFromFileDependencies(); - } - } - - public virtual void PostResolveRequestCache(HttpContextBase context) - { - var request = context?.Request; - if (request == null) - return; - - if (_routes.Count > 0) - { - var path = request.AppRelativeCurrentExecutionFilePath.TrimStart('~').TrimEnd('/'); - var method = request.HttpMethod.EmptyNull(); - - foreach (var route in _routes) - { - if (route.PathPattern.IsMatch(path) && route.HttpMethodPattern.IsMatch(method)) - { - var module = new UrlRoutingModule(); - module.PostResolveRequestCache(context); - return; - } - } - } - } - - private bool IsExtensionPath(HttpRequestBase request) - { - var path = request.AppRelativeCurrentExecutionFilePath.ToLower(); - var result = path.StartsWith("~/plugins/") || path.StartsWith("~/themes/"); - return result; - } - - public void Dispose() - { - // nothing to dispose - } - - private class RoutablePath - { - public Regex PathPattern { get; set; } - public Regex HttpMethodPattern { get; set; } - } - } + /// + /// Request event sequence: + /// - BeginRequest + /// - AuthenticateRequest + /// - PostAuthenticateRequest + /// - AuthorizeRequest + /// - PostAuthorizeRequest + /// - ResolveRequestCache + /// - PostResolveRequestCache + /// - MapRequestHandler + /// - PostMapRequestHandler + /// - AcquireRequestState + /// - PostAcquireRequestState + /// - PreRequestHandlerExecute + /// - PostRequestHandlerExecute + /// - ReleaseRequestState + /// - PostReleaseRequestState + /// - UpdateRequestCache + /// - PostUpdateRequestCache + /// - LogRequest + /// - PostLogRequest + /// - EndRequest + /// - PreSendRequestHeaders + /// - PreSendRequestContent + /// + public class SmartUrlRoutingModule : IHttpModule + { + private static readonly object _contextKey = new object(); + + private static readonly ICollection> _actions = + new SyncedCollection>(new List>()) { ReadLockFree = true }; + + private static readonly ICollection _routes = + new SyncedCollection(new List()) { ReadLockFree = true }; + + static SmartUrlRoutingModule() + { + StopSubDirMonitoring(); + } + + private static void StopSubDirMonitoring() + { + try + { + // http://stackoverflow.com/questions/2248825/asp-net-restarts-when-a-folder-is-created-renamed-or-deleted + var prop = typeof(HttpRuntime).GetProperty("FileChangesMonitor", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); + var o = prop.GetValue(null, null); + + var fi = o.GetType().GetField("_dirMonSubdirs", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase); + var monitor = fi.GetValue(o); + var mi = monitor.GetType().GetMethod("StopMonitoring", BindingFlags.Instance | BindingFlags.NonPublic); + mi.Invoke(monitor, new object[] { }); + } + catch { } + } + + public void Init(HttpApplication application) + { + if (!DataSettings.DatabaseIsInstalled()) + return; + + if (application.Context.Items[_contextKey] == null) + { + application.Context.Items[_contextKey] = _contextKey; + + application.BeginRequest += (s, e) => FilterSameSiteNoneForIncompatibleUserAgents(application); + + if (CommonHelper.IsDevEnvironment && HttpContext.Current.IsDebuggingEnabled) + { + // Handle plugin static file in DevMode + application.PostAuthorizeRequest += (s, e) => RewritePluginStaticFilePaths((HttpApplication)s); + application.PreSendRequestHeaders += (s, e) => HandlePluginStaticFileCaching((HttpApplication)s); + } + + application.PreSendRequestHeaders += (s, e) => HandleSameSiteForAntiForgeryCookie((HttpApplication)s); + application.PostResolveRequestCache += (s, e) => TryResolveRoutablePath((HttpApplication)s); + + // Publish event to give plugins the chance to register custom event handlers for the request lifecycle. + foreach (var action in _actions) + { + action(application); + } + + // Set app to fully initialized state on very first request + EngineContext.Current.IsFullyInitialized = true; + } + } + + #region Handlers + + private static void RewritePluginStaticFilePaths(HttpApplication app) + { + var context = app.Context; + var request = context?.Request; + if (request == null) + return; + + if (IsExtensionPath(request) && context.IsStaticResourceRequested()) + { + // We're in debug mode and in dev environment + var file = HostingEnvironment.VirtualPathProvider.GetFile(request.AppRelativeCurrentExecutionFilePath) as DebugVirtualFile; + if (file != null) + { + context.Items["DebugFile"] = file; + context.Response.TransmitFile(file.PhysicalPath); + context.Response.Flush(); // TBD: optional? + context.Response.SuppressContent = true; // TBD: optional? + context.ApplicationInstance.CompleteRequest(); + } + } + } + + private static void HandlePluginStaticFileCaching(HttpApplication app) + { + var context = app.Context; + if (context?.Response == null) + return; + + var file = context.Items?["DebugFile"] as DebugVirtualFile; + if (file != null) + { + context.Response.AddFileDependency(file.PhysicalPath); + context.Response.ContentType = MimeTypes.MapNameToMimeType(Path.GetFileName(file.PhysicalPath)); + context.Response.Cache.SetNoStore(); + context.Response.Cache.SetLastModifiedFromFileDependencies(); + } + } + + private static void HandleSameSiteForAntiForgeryCookie(HttpApplication app) + { + var context = app.Context; + if (context?.Request == null || context?.Response == null) + return; + + if (context.IsStaticResourceRequested()) + return; + + if (SameSiteBrowserDetector.AllowsSameSiteNone(context.Request.UserAgent)) + { + // Set SameSite attribute for antiforgery token. + var privacySettings = EngineContext.Current.Resolve(); + var antiForgeryCookie = context.Request.Cookies["__requestverificationtoken"]; + + if (antiForgeryCookie != null) + { + antiForgeryCookie.HttpOnly = true; + antiForgeryCookie.Secure = context.Request.IsHttps(); + antiForgeryCookie.SameSite = antiForgeryCookie.Secure ? (SameSiteMode)privacySettings.SameSiteMode : SameSiteMode.Lax; + + context.Response.Cookies.Set(antiForgeryCookie); + } + } + } + + private static void TryResolveRoutablePath(HttpApplication app) + { + var context = app.Context; + var request = context?.Request; + if (request == null) + return; + + if (_routes.Count > 0) + { + var path = request.AppRelativeCurrentExecutionFilePath.TrimStart('~').TrimEnd('/'); + var method = request.HttpMethod.EmptyNull(); + + foreach (var route in _routes) + { + if (route.PathPattern.IsMatch(path) && route.HttpMethodPattern.IsMatch(method)) + { + var module = new UrlRoutingModule(); + module.PostResolveRequestCache(new HttpContextWrapper(context)); + return; + } + } + } + } + + private static void FilterSameSiteNoneForIncompatibleUserAgents(HttpApplication app) + { + var context = app?.Context; + if (context == null || context.IsStaticResourceRequested() || SameSiteBrowserDetector.AllowsSameSiteNone(context.Request?.UserAgent)) + return; + + app.Response.AddOnSendingHeaders(ctx => + { + var cookies = ctx.Response.Cookies; + for (var i = 0; i < cookies.Count; i++) + { + var cookie = cookies[i]; + if (cookie.SameSite == SameSiteMode.None) + { + cookie.SameSite = (SameSiteMode)(-1); // Unspecified + } + } + }); + } + + #endregion + + /// + /// Registers an action that is called on application init. Call this to register HTTP request lifecycle callbacks / event handlers. + /// + /// Action + public static void RegisterAction(Action action) + { + Guard.NotNull(action, nameof(action)); + + _actions.Add(action); + } + + /// + /// Registers a path pattern which should be handled by the + /// + /// The app relative path to handle (regular expression, e.g. /sitemap.xml or /mini-profiler-resources/(.*)) + /// The http method constraint (regular expression, e.g. GET|POST) + /// + /// For performance reasons Smartstore is configured to serve static files (files with extensions other than those mapped to managed handlers) + /// through the native StaticFileModule. This method lets you define exceptions to this default rule: every path registered as routable gets + /// handled by the and therefore enables dynamic processing of (physically non-existent) static files. + /// + public static void RegisterRoutablePath(string path, string verb = ".*") + { + Guard.NotEmpty(path, nameof(path)); + + if (path.IsWebUrl()) + { + throw new ArgumentException("Only relative paths are allowed.", "path"); + } + + var rgPath = new Regex(path, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); + var rgVerb = new Regex(verb, RegexOptions.CultureInvariant | RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); + + _routes.Add(new RoutablePath { PathPattern = rgPath, HttpMethodPattern = rgVerb }); + } + + public static bool HasMatchingPathHandler(string path, string method = "GET") + { + Guard.NotEmpty(path, nameof(path)); + Guard.NotEmpty(method, nameof(method)); + + if (_routes.Count > 0) + { + foreach (var route in _routes) + { + if (route.PathPattern.IsMatch(path) && route.HttpMethodPattern.IsMatch(method)) + { + return true; + } + } + } + + return false; + } + + private static bool IsExtensionPath(HttpRequest request) + { + var path = request.AppRelativeCurrentExecutionFilePath.ToLower(); + var result = path.StartsWith("~/plugins/") || path.StartsWith("~/themes/"); + return result; + } + + public void Dispose() + { + // nothing to dispose + } + + private class RoutablePath + { + public Regex PathPattern { get; set; } + public Regex HttpMethodPattern { get; set; } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Templating/Events.cs b/src/Presentation/SmartStore.Web.Framework/Templating/Events.cs index 7754edbf54..b86c81639b 100644 --- a/src/Presentation/SmartStore.Web.Framework/Templating/Events.cs +++ b/src/Presentation/SmartStore.Web.Framework/Templating/Events.cs @@ -1,96 +1,89 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using DotLiquid; namespace SmartStore.Templating { - /// - /// Published when a template zone is about to be rendered. - /// By subscribing to this event, implementors can inject custom - /// content to specific template zones. - /// - public sealed class ZoneRenderingEvent - { - private IList _snippets; + /// + /// Published when a template zone is about to be rendered. + /// By subscribing to this event, implementors can inject custom + /// content to specific template zones. + /// + public sealed class ZoneRenderingEvent + { + private IList _snippets; - public ZoneRenderingEvent(string zoneName, IDictionary model) - { - ZoneName = zoneName; - Model = model; - } + public ZoneRenderingEvent(string zoneName, IDictionary model) + { + ZoneName = zoneName; + Model = model; + } - internal Context LiquidContext { get; set; } + internal Context LiquidContext { get; set; } - /// - /// The name of the rendered template. - /// - public string TemplateName - { - get => Evaluate("Context.TemplateName") as string; - } + /// + /// The name of the rendered template. + /// + public string TemplateName => Evaluate("Context.TemplateName") as string; - /// - /// The name of the zone which is rendered. - /// - public string ZoneName { get; private set; } + /// + /// The name of the zone which is rendered. + /// + public string ZoneName { get; private set; } - /// - /// The template model - /// - public IDictionary Model { get; private set; } + /// + /// The template model + /// + public IDictionary Model { get; private set; } - /// - /// Evaluates an expression - e.g. Product.Sku - and returns it's value. - /// - /// - /// - public object Evaluate(string expression) - { - return LiquidContext[expression, false]; - } + /// + /// Evaluates an expression - e.g. Product.Sku - and returns it's value. + /// + /// + /// + public object Evaluate(string expression) + { + return LiquidContext[expression, false]; + } - /// - /// Specifies the custom content which the template engine should parse and inject. - /// - /// The content - public void InjectContent(string content) - { - if (content.HasValue()) - { - AddSnippet(new Snippet { Content = content, Parse = true }); - } - } + /// + /// Specifies the custom content which the template engine should parse and inject. + /// + /// The content + public void InjectContent(string content) + { + if (content.HasValue()) + { + AddSnippet(new Snippet { Content = content, Parse = true }); + } + } - /// - /// Specifies the custom content to inject. - /// - /// The content - /// This should be true if the content contains template syntax. - public void InjectContent(string content, bool parse) - { - if (content.HasValue()) - { - AddSnippet(new Snippet { Content = content, Parse = parse }); - } - } + /// + /// Specifies the custom content to inject. + /// + /// The content + /// This should be true if the content contains template syntax. + public void InjectContent(string content, bool parse) + { + if (content.HasValue()) + { + AddSnippet(new Snippet { Content = content, Parse = parse }); + } + } - private void AddSnippet(Snippet snippet) - { - if (_snippets == null) - _snippets = new List(); + private void AddSnippet(Snippet snippet) + { + if (_snippets == null) + _snippets = new List(); - _snippets.Add(snippet); - } + _snippets.Add(snippet); + } - internal IList Snippets - { - get => _snippets; - } + internal IList Snippets => _snippets; - internal class Snippet - { - public string Content { get; set; } - public bool Parse { get; set; } - } - } + internal class Snippet + { + public string Content { get; set; } + public bool Parse { get; set; } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/DictionaryDrop.cs b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/DictionaryDrop.cs index e23556ddab..e061b9976d 100644 --- a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/DictionaryDrop.cs +++ b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/DictionaryDrop.cs @@ -1,34 +1,33 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace SmartStore.Templating.Liquid { - internal class DictionaryDrop : SafeDropBase - { - private readonly IDictionary _inner; + internal class DictionaryDrop : SafeDropBase + { + private readonly IDictionary _inner; - public DictionaryDrop(IDictionary data) - { - Guard.NotNull(data, nameof(data)); + public DictionaryDrop(IDictionary data) + { + Guard.NotNull(data, nameof(data)); - _inner = data; - } + _inner = data; + } - public override bool ContainsKey(object key) - { - return (key is string s) - ? _inner.ContainsKey(s) - : false; - } + public override bool ContainsKey(object key) + { + return (key is string s) + ? _inner.ContainsKey(s) + : false; + } - protected override object InvokeMember(string name) - { - return _inner.Get(name); - } + protected override object InvokeMember(string name) + { + return _inner.Get(name); + } - public override object GetWrappedObject() - { - return _inner; - } - } + public override object GetWrappedObject() + { + return _inner; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/EnumerableWrapper.cs b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/EnumerableWrapper.cs index 721e73d533..e416575d82 100644 --- a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/EnumerableWrapper.cs +++ b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/EnumerableWrapper.cs @@ -1,40 +1,42 @@ -using System; -using System.Collections; +using System.Collections; using System.Collections.Generic; using System.Linq; using DotLiquid; namespace SmartStore.Templating.Liquid { - internal class EnumerableWrapper : IEnumerable, ISafeObject - { - private readonly IEnumerable _enumerable; + internal class EnumerableWrapper : IEnumerable, ISafeObject + { + private readonly IEnumerable _enumerable; - public EnumerableWrapper(IEnumerable enumerable) - { - Guard.NotNull(enumerable, nameof(enumerable)); - _enumerable = enumerable; - } + public EnumerableWrapper(IEnumerable enumerable) + { + Guard.NotNull(enumerable, nameof(enumerable)); + _enumerable = enumerable; + } - public IEnumerator GetEnumerator() - { - return GetEnumeratorInternal(); - } + public IEnumerator GetEnumerator() + { + return GetEnumeratorInternal(); + } - public object GetWrappedObject() => _enumerable; + public object GetWrappedObject() + { + return _enumerable; + } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumeratorInternal(); - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumeratorInternal(); + } - private IEnumerator GetEnumeratorInternal() - { - return _enumerable - .Cast() - .Select(x => LiquidUtil.CreateSafeObject(x)) - .OfType() - .GetEnumerator(); - } - } + private IEnumerator GetEnumeratorInternal() + { + return _enumerable + .Cast() + .Select(x => LiquidUtil.CreateSafeObject(x)) + .OfType() + .GetEnumerator(); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/ObjectDrop.cs b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/ObjectDrop.cs index 259f6ef028..8a384a5c9b 100644 --- a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/ObjectDrop.cs +++ b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/ObjectDrop.cs @@ -6,54 +6,54 @@ namespace SmartStore.Templating.Liquid { - internal class ObjectDrop : SafeDropBase - { - private readonly object _data; - private readonly Type _type; - - public ObjectDrop(object data) - { - Guard.NotNull(data, nameof(data)); - - _data = data; - - if (data is BaseEntity be) - { - _type = be.GetUnproxiedType(); - } - else - { - _type = data.GetType(); - } - } - - public override bool ContainsKey(object key) - { - return true; - } - - protected override object InvokeMember(string name) - { - var prop = FastProperty.GetProperty(_type, name); - if (prop != null) - { - return prop.Property.HasAttribute(true) - ? null - : prop.GetValue(_data); - } - - var method = _type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public); - if (method != null && method.GetParameters().Length == 0) - { - return method.Invoke(_data, null); - } - - return null; - } - - public override object GetWrappedObject() - { - return _data; - } - } + internal class ObjectDrop : SafeDropBase + { + private readonly object _data; + private readonly Type _type; + + public ObjectDrop(object data) + { + Guard.NotNull(data, nameof(data)); + + _data = data; + + if (data is BaseEntity be) + { + _type = be.GetUnproxiedType(); + } + else + { + _type = data.GetType(); + } + } + + public override bool ContainsKey(object key) + { + return true; + } + + protected override object InvokeMember(string name) + { + var prop = FastProperty.GetProperty(_type, name); + if (prop != null) + { + return prop.Property.HasAttribute(true) + ? null + : prop.GetValue(_data); + } + + var method = _type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public); + if (method != null && method.GetParameters().Length == 0) + { + return method.Invoke(_data, null); + } + + return null; + } + + public override object GetWrappedObject() + { + return _data; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/SafeDropBase.cs b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/SafeDropBase.cs index 2974c3cacf..87d4423df6 100644 --- a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/SafeDropBase.cs +++ b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/SafeDropBase.cs @@ -1,49 +1,42 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using DotLiquid; namespace SmartStore.Templating.Liquid { - internal interface ISafeObject - { - object GetWrappedObject(); - } - - internal abstract class SafeDropBase : ILiquidizable, IIndexable, ISafeObject - { - private readonly IDictionary _safeObjects = new Dictionary(); - - protected object GetOrCreateSafeObject(string name) - { - if (!_safeObjects.TryGetValue(name, out var safeObject)) - { - safeObject = LiquidUtil.CreateSafeObject(InvokeMember(name)); - if (safeObject is ISafeObject) - { - _safeObjects[name] = safeObject; - } - } - - return safeObject; - } - - public abstract bool ContainsKey(object key); - - public object this[object key] - { - get - { - return (key is string s) ? GetOrCreateSafeObject(s) : null; - } - } - - protected abstract object InvokeMember(string name); - - public object ToLiquid() - { - return this; - } - - public abstract object GetWrappedObject(); - } + internal interface ISafeObject + { + object GetWrappedObject(); + } + + internal abstract class SafeDropBase : ILiquidizable, IIndexable, ISafeObject + { + private readonly IDictionary _safeObjects = new Dictionary(); + + protected object GetOrCreateSafeObject(string name) + { + if (!_safeObjects.TryGetValue(name, out var safeObject)) + { + safeObject = LiquidUtil.CreateSafeObject(InvokeMember(name)); + if (safeObject is ISafeObject) + { + _safeObjects[name] = safeObject; + } + } + + return safeObject; + } + + public abstract bool ContainsKey(object key); + + public object this[object key] => (key is string s) ? GetOrCreateSafeObject(s) : null; + + protected abstract object InvokeMember(string name); + + public object ToLiquid() + { + return this; + } + + public abstract object GetWrappedObject(); + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/TestDrop.cs b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/TestDrop.cs index 0c02cfc4c2..91e0c00b40 100644 --- a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/TestDrop.cs +++ b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Drops/TestDrop.cs @@ -6,91 +6,88 @@ namespace SmartStore.Templating.Liquid { - internal class TestDrop : ITestModel, ILiquidizable, IIndexable, ISafeObject - { - private readonly BaseEntity _entity; - private readonly Type _type; - private readonly string _modelPrefix; + internal class TestDrop : ITestModel, ILiquidizable, IIndexable, ISafeObject + { + private readonly BaseEntity _entity; + private readonly Type _type; + private readonly string _modelPrefix; - public TestDrop(BaseEntity entity, string modelPrefix) - { - _entity = entity; - _type = entity.GetUnproxiedType(); + public TestDrop(BaseEntity entity, string modelPrefix) + { + _entity = entity; + _type = entity.GetUnproxiedType(); - if (modelPrefix.HasValue()) - { - _modelPrefix = modelPrefix.EnsureEndsWith("."); - } + if (modelPrefix.HasValue()) + { + _modelPrefix = modelPrefix.EnsureEndsWith("."); + } - _modelPrefix = _modelPrefix ?? string.Empty; - } + _modelPrefix = _modelPrefix ?? string.Empty; + } - public string ModelName - { - get => _type.Name; - } + public string ModelName => _type.Name; - public object GetWrappedObject() - { - return _entity; - } + public object GetWrappedObject() + { + return _entity; + } - public bool ContainsKey(object key) - { - return true; - } + public bool ContainsKey(object key) + { + return true; + } - public object this[object key] - { - get - { - object value = null; + public object this[object key] + { + get + { + object value = null; - if (key is string name) - { - var modelPrefix = _modelPrefix + name; - var fastProp = FastProperty.GetProperty(_type, name); - var pi = fastProp?.Property; + if (key is string name) + { + var modelPrefix = _modelPrefix + name; + var fastProp = FastProperty.GetProperty(_type, name); + var pi = fastProp?.Property; - if (pi == null) - { - value = "{{ " + modelPrefix + " }}"; - } - else if (pi.PropertyType.IsPredefinedType()) - { - value = "{{ " + modelPrefix + " }}"; - } - else if (fastProp.IsSequenceType) - { - var seqType = pi.PropertyType.GetGenericArguments()[0]; - if (typeof(BaseEntity).IsAssignableFrom(seqType)) - { - var testObj1 = new TestDrop((BaseEntity)Activator.CreateInstance(seqType), "it"); - var testObj2 = new TestDrop((BaseEntity)Activator.CreateInstance(seqType), "it"); - var list = Activator.CreateInstance(typeof(List)) as List; - list.Add(testObj1); - list.Add(testObj2); - value = list; - } - } - else if (typeof(BaseEntity).IsAssignableFrom(pi.PropertyType)) - { - value = new TestDrop((BaseEntity)Activator.CreateInstance(pi.PropertyType), modelPrefix); - } + if (pi == null) + { + value = "{{ " + modelPrefix + " }}"; + } + else if (pi.PropertyType.IsPredefinedType()) + { + value = "{{ " + modelPrefix + " }}"; + } + else if (fastProp.IsSequenceType) + { + var seqType = pi.PropertyType.GetGenericArguments()[0]; + if (typeof(BaseEntity).IsAssignableFrom(seqType)) + { + var testObj1 = new TestDrop((BaseEntity)Activator.CreateInstance(seqType), "it"); + var testObj2 = new TestDrop((BaseEntity)Activator.CreateInstance(seqType), "it"); + var list = Activator.CreateInstance(typeof(List)) as List; + list.Add(testObj1); + list.Add(testObj2); + value = list; + } + } + else if (typeof(BaseEntity).IsAssignableFrom(pi.PropertyType)) + { + value = new TestDrop((BaseEntity)Activator.CreateInstance(pi.PropertyType), modelPrefix); + } - //if (value is string s) - //{ - // value = "{1}".FormatInvariant(invalid ? " invalid" : "", value); - //} - } + //if (value is string s) + //{ + // value = "{1}".FormatInvariant(invalid ? " invalid" : "", value); + //} + } - return value; - } - } + return value; + } + } - public object ToLiquid() - { - return this; - } - } + public object ToLiquid() + { + return this; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Filters/AdditionalFilters.cs b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Filters/AdditionalFilters.cs index ce03bbb9dc..a4bd7e1bb0 100644 --- a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Filters/AdditionalFilters.cs +++ b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Filters/AdditionalFilters.cs @@ -5,242 +5,239 @@ using System.Text; using DotLiquid; using Newtonsoft.Json; -using SmartStore.Utilities; -using SmartStore.Services; -using SmartStore.Services.Directory; +using SmartStore.ComponentModel; using SmartStore.Core.Domain.Directory; +using SmartStore.Services; using SmartStore.Services.Common; +using SmartStore.Services.Directory; +using SmartStore.Utilities; namespace SmartStore.Templating.Liquid { - public static class AdditionalFilters - { - private static LiquidTemplateEngine GetEngine() - { - return (LiquidTemplateEngine)Template.FileSystem; - } + public static class AdditionalFilters + { + private static LiquidTemplateEngine GetEngine() + { + return (LiquidTemplateEngine)Template.FileSystem; + } + + #region Common Filters + + public static object Default(object input, object value) + { + return input ?? value; + } + + public static string Json(object input) + { + if (input == null) + return null; + + return JsonConvert.SerializeObject(input, new JsonSerializerSettings + { + ContractResolver = SmartContractResolver.Instance, + ReferenceLoopHandling = ReferenceLoopHandling.Ignore + }); + } + + public static string FormatAddress(Context context, object input) + { + if (input == null) + return null; + + var engine = GetEngine(); + var countryService = engine.Services.Resolve(); + var addressService = engine.Services.Resolve(); + + Country country = null; + + // We know that we converted Address entity to a dictionary. + + if (input is IDictionary dict) + { + country = countryService.GetCountryById(dict.Get("CountryId").Convert().GetValueOrDefault()); + } + else if (input is IIndexable lq) + { + country = countryService.GetCountryById(lq["CountryId"].Convert().GetValueOrDefault()); + } + + return addressService.FormatAddress(input, country?.AddressFormat, context.FormatProvider); + } + + #endregion + + #region Array Filters + + public static IEnumerable Uniq(IEnumerable input) + { + if (input == null) + return null; + + return input.Cast().Distinct(); + } + + public static IEnumerable Compact(IEnumerable input) + { + if (input == null) + return null; + + return input.Cast().Where(x => x != null); + } + + public static IEnumerable Reverse(IEnumerable input) + { + if (input == null) + return null; + + return input.Cast().Reverse(); + } + + #endregion + + #region Number Filters + + public static object Ceil(object input) + { + if (input == null) + return null; + + return CommonHelper.TryConvert(input, out var d) + ? Math.Ceiling(d) + : 0; + } + + public static object Floor(object input) + { + if (input == null) + return null; + + return CommonHelper.TryConvert(input, out var d) + ? Math.Floor(d) + : 0; + } + + public static object Abs(object input) + { + if (input == null) + return null; + + return CommonHelper.TryConvert(input, out var d) + ? Math.Abs(d) + : 0; + } - #region Common Filters - - public static object Default(object input, object value) - { - return input ?? value; - } - - public static string Json(object input) - { - if (input == null) - return null; - - return JsonConvert.SerializeObject(input, new JsonSerializerSettings - { - ReferenceLoopHandling = ReferenceLoopHandling.Ignore - }); - } - - public static string FormatAddress(Context context, object input) - { - if (input == null) - return null; - - var engine = GetEngine(); - var countryService = engine.Services.Resolve(); - var addressService = engine.Services.Resolve(); - - Country country = null; - - // We know that we converted Address entity to a dictionary. - - if (input is IDictionary dict) - { - country = countryService.GetCountryById(dict.Get("CountryId").Convert().GetValueOrDefault()); - } - else if (input is IIndexable lq) - { - country = countryService.GetCountryById(lq["CountryId"].Convert().GetValueOrDefault()); - } - - return addressService.FormatAddress(input, country?.AddressFormat, context.FormatProvider); - } - - #endregion - - #region Array Filters - - public static IEnumerable Uniq(IEnumerable input) - { - if (input == null) - return null; - - return input.Cast().Distinct(); - } - - public static IEnumerable Compact(IEnumerable input) - { - if (input == null) - return null; - - return input.Cast().Where(x => x != null); - } - - public static IEnumerable Reverse(IEnumerable input) - { - if (input == null) - return null; - - return input.Cast().Reverse(); - } - - #endregion - - #region Number Filters - - public static object Ceil(object input) - { - if (input == null) - return null; - - return CommonHelper.TryConvert(input, out var d) - ? Math.Ceiling(d) - : 0; - } - - public static object Floor(object input) - { - if (input == null) - return null; - - return CommonHelper.TryConvert(input, out var d) - ? Math.Floor(d) - : 0; - } - - public static object Abs(object input) - { - if (input == null) - return null; + public static object AtMost(object input, object operand) + { + if (input == null || operand == null) + return null; - return CommonHelper.TryConvert(input, out var d) - ? Math.Abs(d) - : 0; - } + return CommonHelper.TryConvert(input, out var d) && CommonHelper.TryConvert(operand, out var o) + ? Math.Min(d, o) + : 0; + } - public static object AtMost(object input, object operand) - { - if (input == null || operand == null) - return null; + public static object AtLeast(object input, object operand) + { + if (input == null || operand == null) + return null; - return CommonHelper.TryConvert(input, out var d) && CommonHelper.TryConvert(operand, out var o) - ? Math.Min(d, o) - : 0; - } + return CommonHelper.TryConvert(input, out var d) && CommonHelper.TryConvert(operand, out var o) + ? Math.Max(d, o) + : 0; + } - public static object AtLeast(object input, object operand) - { - if (input == null || operand == null) - return null; + #endregion - return CommonHelper.TryConvert(input, out var d) && CommonHelper.TryConvert(operand, out var o) - ? Math.Max(d, o) - : 0; - } + #region String Filters - #endregion + public static string Prettify(object input, bool allowSpace) + { + if (CommonHelper.TryConvert(input, out var l)) + { + return Prettifier.BytesToString(l); + } + else if (input is string s) + { + return s.Slugify(allowSpace); + } - #region String Filters + return null; + } - public static string Prettify(object input, bool allowSpace) - { - if (CommonHelper.TryConvert(input, out var l)) - { - return Prettifier.BytesToString(l); - } - else if (input is string s) - { - return s.Slugify(allowSpace); - } + public static string SanitizeHtmlId(string input) + { + return input?.SanitizeHtmlId(); + } - return null; - } + public static string Md5(string input) + { + return input?.Hash(Encoding.UTF8); + } - public static string SanitizeHtmlId(string input) - { - return input?.SanitizeHtmlId(); - } - - public static string Md5(string input) - { - return input?.Hash(Encoding.UTF8); - } + public static string UrlDecode(string input) + { + return input?.UrlDecode(); + } - public static string Sha1(string input) - { - return input?.Sha(Encoding.UTF8); - } + public static string Handleize(string input) + { + return Inflector.Handleize(input.EmptyNull()); + } - public static string UrlDecode(string input) - { - return input?.UrlDecode(); - } + public static string Pluralize(string input) + { + return Inflector.Pluralize(input.EmptyNull()); + } - public static string Handleize(string input) - { - return Inflector.Handleize(input.EmptyNull()); - } + #endregion - public static string Pluralize(string input) - { - return Inflector.Pluralize(input.EmptyNull()); - } + #region Localization Filters - #endregion - - #region Localization Filters - - public static string T(Context context, string key, object arg1 = null, object arg2 = null, object arg3 = null, object arg4 = null) - { - var engine = GetEngine(); - - int languageId = 0; - - if (context["Context.LanguageId"] is int lid) - { - languageId = lid; - } - - var args = (new object[] { arg1, arg2, arg3, arg4 }).ToArray(); + public static string T(Context context, string key, object arg1 = null, object arg2 = null, object arg3 = null, object arg4 = null) + { + var engine = GetEngine(); + + int languageId = 0; + + if (context["Context.LanguageId"] is int lid) + { + languageId = lid; + } + + var args = (new object[] { arg1, arg2, arg3, arg4 }).ToArray(); - return engine.T(key, languageId, args); - } + return engine.T(key, languageId, args); + } - #endregion + #endregion - #region Url Filters + #region Url Filters - #endregion + #endregion - #region Html Filters + #region Html Filters - public static string ScriptTag(string input) - { - return String.Format("", input); - } + public static string ScriptTag(string input) + { + return String.Format("", input); + } - public static string StylesheetTag(string input) - { - return String.Format("", input); - } + public static string StylesheetTag(string input) + { + return String.Format("", input); + } - public static string ImgTag(string input, string alt = "", string css = "") - { - return input == null ? null : GetImageTag(input, alt, css); - } + public static string ImgTag(string input, string alt = "", string css = "") + { + return input == null ? null : GetImageTag(input, alt, css); + } - private static string GetImageTag(string src, string alt, string css) - { - return String.Format("\"{1}\"", src, alt, css); - } + private static string GetImageTag(string src, string alt, string css) + { + return String.Format("\"{1}\"", src, alt, css); + } - #endregion - } + #endregion + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/LiquidTemplate.cs b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/LiquidTemplate.cs index c02037c9be..3f6638c705 100644 --- a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/LiquidTemplate.cs +++ b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/LiquidTemplate.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Web.Hosting; using DotLiquid; @@ -7,81 +6,81 @@ namespace SmartStore.Templating.Liquid { - internal class LiquidTemplate : ITemplate - { - public LiquidTemplate(Template template, string source) - { - Guard.NotNull(template, nameof(template)); - Guard.NotNull(source, nameof(source)); + internal class LiquidTemplate : ITemplate + { + public LiquidTemplate(Template template, string source) + { + Guard.NotNull(template, nameof(template)); + Guard.NotNull(source, nameof(source)); - Template = template; - Source = source; - } + Template = template; + Source = source; + } - public string Source - { - get; - internal set; - } + public string Source + { + get; + internal set; + } - public Template Template - { - get; - internal set; - } + public Template Template + { + get; + internal set; + } - public string Render(object model, IFormatProvider formatProvider) - { - Guard.NotNull(model, nameof(model)); - Guard.NotNull(formatProvider, nameof(formatProvider)); - - var p = CreateParameters(model, formatProvider); - return Template.Render(p); - } + public string Render(object model, IFormatProvider formatProvider) + { + Guard.NotNull(model, nameof(model)); + Guard.NotNull(formatProvider, nameof(formatProvider)); - private RenderParameters CreateParameters(object data, IFormatProvider formatProvider) - { - var p = new RenderParameters(formatProvider); + var p = CreateParameters(model, formatProvider); + return Template.Render(p); + } - Hash hash = null; - - if (data is ISafeObject so) - { - if (so.GetWrappedObject() is IDictionary soDict) - { - hash = Hash.FromDictionary(soDict); - } - else - { - data = so.GetWrappedObject(); - } - } + private RenderParameters CreateParameters(object data, IFormatProvider formatProvider) + { + var p = new RenderParameters(formatProvider); - if (hash == null) - { - hash = new Hash(); + Hash hash = null; - if (data is IDictionary dict) - { - foreach (var kvp in dict) - { - hash[kvp.Key] = LiquidUtil.CreateSafeObject(kvp.Value); - } - } - else - { - var props = FastProperty.GetProperties(data); - foreach (var prop in props) - { - hash[prop.Key] = LiquidUtil.CreateSafeObject(prop.Value.GetValue(data)); - } - } - } + if (data is ISafeObject so) + { + if (so.GetWrappedObject() is IDictionary soDict) + { + hash = Hash.FromDictionary(soDict); + } + else + { + data = so.GetWrappedObject(); + } + } - p.LocalVariables = hash; - p.ErrorsOutputMode = HostingEnvironment.IsHosted ? ErrorsOutputMode.Display : ErrorsOutputMode.Rethrow; + if (hash == null) + { + hash = new Hash(); - return p; - } - } + if (data is IDictionary dict) + { + foreach (var kvp in dict) + { + hash[kvp.Key] = LiquidUtil.CreateSafeObject(kvp.Value); + } + } + else + { + var props = FastProperty.GetProperties(data.GetType()); + foreach (var prop in props) + { + hash[prop.Key] = LiquidUtil.CreateSafeObject(prop.Value.GetValue(data)); + } + } + } + + p.LocalVariables = hash; + p.ErrorsOutputMode = HostingEnvironment.IsHosted ? ErrorsOutputMode.Display : ErrorsOutputMode.Rethrow; + + return p; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/LiquidTemplateEngine.cs b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/LiquidTemplateEngine.cs index a229d69208..d2ee3c46bd 100644 --- a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/LiquidTemplateEngine.cs +++ b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/LiquidTemplateEngine.cs @@ -13,157 +13,157 @@ namespace SmartStore.Templating.Liquid { - public partial class LiquidTemplateEngine : ITemplateEngine, ITemplateFileSystem - { - private readonly Work _services; - private readonly Work _localizer; - private readonly Work _themeContext; - private readonly IVirtualPathProvider _vpp; - - public LiquidTemplateEngine( - Work services, - IVirtualPathProvider vpp, - Work themeContext, - Work localizer) - { - _services = services; - _vpp = vpp; - _localizer = localizer; - _themeContext = themeContext; - - // Register Value type transformers - var allowedMoneyProps = new[] - { - nameof(Money.Amount), - nameof(Money.RoundedAmount), - nameof(Money.TruncatedAmount), - nameof(Money.Formatted), - nameof(Money.DecimalDigits) - }; - Template.RegisterSafeType(typeof(Money), allowedMoneyProps, x => x); - - // Register tag "zone" - Template.RegisterTagFactory(new ZoneTagFactory(_services.Value.EventPublisher)); - - // Register Filters - Template.RegisterFilter(typeof(AdditionalFilters)); - - Template.NamingConvention = new CSharpNamingConvention(); - Template.FileSystem = this; - } - - #region Services - - public ICommonServices Services => _services.Value; - - public LocalizedString T(string key, int languageId, params object[] args) - { - return _localizer.Value(key, languageId, args); - } - - #endregion - - #region ITemplateEngine - - public ITemplate Compile(string source) - { - Guard.NotNull(source, nameof(source)); - - return new LiquidTemplate(Template.Parse(source), source); - } - - public string Render(string source, object model, IFormatProvider formatProvider) - { - Guard.NotNull(source, nameof(source)); - Guard.NotNull(model, nameof(model)); - Guard.NotNull(formatProvider, nameof(formatProvider)); - - return Compile(source).Render(model, formatProvider); - } - - public ITestModel CreateTestModelFor(BaseEntity entity, string modelPrefix) - { - Guard.NotNull(entity, nameof(entity)); - - return new TestDrop(entity, modelPrefix); - } - - #endregion - - #region ITemplateFileSystem - - public Template GetTemplate(Context context, string templateName) - { - var virtualPath = ResolveVirtualPath(context, templateName); - - if (virtualPath.IsEmpty()) - { - return null; - } - - var cacheKey = HttpRuntime.Cache.BuildScopedKey("LiquidPartial://" + virtualPath); - var cachedTemplate = HttpRuntime.Cache.Get(cacheKey); - - if (cachedTemplate == null) - { - // Read from file, compile and put to cache with file dependeny - var source = ReadTemplateFileInternal(virtualPath); - cachedTemplate = Template.Parse(source); - var cacheDependency = _vpp.GetCacheDependency(virtualPath, DateTime.UtcNow); - HttpRuntime.Cache.Insert(cacheKey, cachedTemplate, cacheDependency); - } - - return (Template)cachedTemplate; - } - - public string ReadTemplateFile(Context context, string templateName) - { - var virtualPath = ResolveVirtualPath(context, templateName); - - return ReadTemplateFileInternal(virtualPath); - } - - private string ReadTemplateFileInternal(string virtualPath) - { - if (virtualPath.IsEmpty()) - { - return string.Empty; - } - - if (!_vpp.FileExists(virtualPath)) - { - throw new FileNotFoundException($"Include file '{virtualPath}' does not exist."); - } - - using (var stream = _vpp.OpenFile(virtualPath)) - { - return stream.AsString(); - } - } + public partial class LiquidTemplateEngine : ITemplateEngine, ITemplateFileSystem + { + private readonly Work _services; + private readonly Work _localizer; + private readonly Work _themeContext; + private readonly IVirtualPathProvider _vpp; + + public LiquidTemplateEngine( + Work services, + IVirtualPathProvider vpp, + Work themeContext, + Work localizer) + { + _services = services; + _vpp = vpp; + _localizer = localizer; + _themeContext = themeContext; + + // Register Value type transformers + var allowedMoneyProps = new[] + { + nameof(Money.Amount), + nameof(Money.RoundedAmount), + nameof(Money.TruncatedAmount), + nameof(Money.Formatted), + nameof(Money.DecimalDigits) + }; + Template.RegisterSafeType(typeof(Money), allowedMoneyProps, x => x); + + // Register tag "zone" + Template.RegisterTagFactory(new ZoneTagFactory(_services.Value.EventPublisher)); + + // Register Filters + Template.RegisterFilter(typeof(AdditionalFilters)); + + Template.NamingConvention = new CSharpNamingConvention(); + Template.FileSystem = this; + } + + #region Services + + public ICommonServices Services => _services.Value; + + public LocalizedString T(string key, int languageId, params object[] args) + { + return _localizer.Value(key, languageId, args); + } + + #endregion + + #region ITemplateEngine + + public ITemplate Compile(string source) + { + Guard.NotNull(source, nameof(source)); + + return new LiquidTemplate(Template.Parse(source), source); + } + + public string Render(string source, object model, IFormatProvider formatProvider) + { + Guard.NotNull(source, nameof(source)); + Guard.NotNull(model, nameof(model)); + Guard.NotNull(formatProvider, nameof(formatProvider)); + + return Compile(source).Render(model, formatProvider); + } + + public ITestModel CreateTestModelFor(BaseEntity entity, string modelPrefix) + { + Guard.NotNull(entity, nameof(entity)); + + return new TestDrop(entity, modelPrefix); + } + + #endregion + + #region ITemplateFileSystem + + public Template GetTemplate(Context context, string templateName) + { + var virtualPath = ResolveVirtualPath(context, templateName); + + if (virtualPath.IsEmpty()) + { + return null; + } + + var cacheKey = HttpRuntime.Cache.BuildScopedKey("LiquidPartial://" + virtualPath); + var cachedTemplate = HttpRuntime.Cache.Get(cacheKey); + + if (cachedTemplate == null) + { + // Read from file, compile and put to cache with file dependeny + var source = ReadTemplateFileInternal(virtualPath); + cachedTemplate = Template.Parse(source); + var cacheDependency = _vpp.GetCacheDependency(virtualPath, DateTime.UtcNow); + HttpRuntime.Cache.Insert(cacheKey, cachedTemplate, cacheDependency); + } + + return (Template)cachedTemplate; + } + + public string ReadTemplateFile(Context context, string templateName) + { + var virtualPath = ResolveVirtualPath(context, templateName); + + return ReadTemplateFileInternal(virtualPath); + } + + private string ReadTemplateFileInternal(string virtualPath) + { + if (virtualPath.IsEmpty()) + { + return string.Empty; + } + + if (!_vpp.FileExists(virtualPath)) + { + throw new FileNotFoundException($"Include file '{virtualPath}' does not exist."); + } + + using (var stream = _vpp.OpenFile(virtualPath)) + { + return stream.AsString(); + } + } - private string ResolveVirtualPath(Context context, string templateName) - { - var path = ((string)context[templateName]).NullEmpty() ?? templateName; + private string ResolveVirtualPath(Context context, string templateName) + { + var path = ((string)context[templateName]).NullEmpty() ?? templateName; - if (path.IsEmpty()) - return string.Empty; + if (path.IsEmpty()) + return string.Empty; - path = path.EnsureEndsWith(".liquid"); + path = path.EnsureEndsWith(".liquid"); - string virtualPath = null; + string virtualPath = null; - if (!path.StartsWith("~/")) - { - var currentTheme = _themeContext.Value.CurrentTheme; - virtualPath = _vpp.Combine(currentTheme.Location, currentTheme.ThemeName, "Views/Shared/EmailTemplates", path); - } - else - { - virtualPath = VirtualPathUtility.ToAppRelative(path); - } - return virtualPath; - } - - #endregion - } + if (!path.StartsWith("~/")) + { + var currentTheme = _themeContext.Value.CurrentTheme; + virtualPath = _vpp.Combine(currentTheme.Location, currentTheme.ThemeName, "Views/Shared/EmailTemplates", path); + } + else + { + virtualPath = VirtualPathUtility.ToAppRelative(path); + } + return virtualPath; + } + + #endregion + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/LiquidUtil.cs b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/LiquidUtil.cs index 9363346814..5937ea4fc6 100644 --- a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/LiquidUtil.cs +++ b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/LiquidUtil.cs @@ -4,58 +4,58 @@ namespace SmartStore.Templating.Liquid { - internal static class LiquidUtil - { - private static readonly IDictionary> _typeWrapperCache - = new Dictionary>(); - - internal static object CreateSafeObject(object value) - { - if (value == null) - { - return null; - } - - if (value is TestDrop || value is IFormattable) - { - return value; - } - - var valueType = value.GetType(); - - if (!_typeWrapperCache.TryGetValue(valueType, out var fn)) - { - if (value is IDictionary dict) - { - fn = x => new DictionaryDrop((IDictionary)x); - } - else if (valueType.IsSequenceType()) - { - var genericArgs = valueType.GetGenericArguments(); - var isEnumerable = genericArgs.Length == 1 && valueType.IsSubClass(typeof(IEnumerable<>)); - if (isEnumerable) - { - var seqType = genericArgs[0]; - if (!IsSafeType(seqType)) - { - fn = x => new EnumerableWrapper((IEnumerable)x); - } - } - } - else if (valueType.IsPlainObjectType()) - { - fn = x => new ObjectDrop(x); - } - - _typeWrapperCache[valueType] = fn; - } - - return fn?.Invoke(value) ?? value; - } - - public static bool IsSafeType(Type type) - { - return type.IsPredefinedType(); - } - } + internal static class LiquidUtil + { + private static readonly IDictionary> _typeWrapperCache + = new Dictionary>(); + + internal static object CreateSafeObject(object value) + { + if (value == null) + { + return null; + } + + if (value is TestDrop || value is IFormattable) + { + return value; + } + + var valueType = value.GetType(); + + if (!_typeWrapperCache.TryGetValue(valueType, out var fn)) + { + if (value is IDictionary dict) + { + fn = x => new DictionaryDrop((IDictionary)x); + } + else if (valueType.IsSequenceType()) + { + var genericArgs = valueType.GetGenericArguments(); + var isEnumerable = genericArgs.Length == 1 && valueType.IsSubClass(typeof(IEnumerable<>)); + if (isEnumerable) + { + var seqType = genericArgs[0]; + if (!IsSafeType(seqType)) + { + fn = x => new EnumerableWrapper((IEnumerable)x); + } + } + } + else if (valueType.IsPlainObjectType()) + { + fn = x => new ObjectDrop(x); + } + + _typeWrapperCache[valueType] = fn; + } + + return fn?.Invoke(value) ?? value; + } + + public static bool IsSafeType(Type type) + { + return type.IsPredefinedType(); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Tags/Zone.cs b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Tags/Zone.cs index ed1113cf1c..4e9fb8037e 100644 --- a/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Tags/Zone.cs +++ b/src/Presentation/SmartStore.Web.Framework/Templating/Liquid/Tags/Zone.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; @@ -10,76 +9,76 @@ namespace SmartStore.Templating.Liquid { - internal sealed class ZoneTagFactory : ITagFactory - { - private readonly IEventPublisher _eventPublisher; - - public ZoneTagFactory(IEventPublisher eventPublisher) - { - _eventPublisher = eventPublisher; - } - - public string TagName => "zone"; - - public Tag Create() - { - return new Zone { EventPublisher = _eventPublisher }; - } - - class Zone : Tag - { - private static readonly Regex Syntax = R.B(@"^({0})", DotLiquid.Liquid.QuotedFragment); - - private string _zoneName; - - public IEventPublisher EventPublisher { get; set; } - - public override void Initialize(string tagName, string markup, List tokens) - { - Match syntaxMatch = Syntax.Match(markup); - - if (syntaxMatch.Success) - { - _zoneName = syntaxMatch.Groups[1].Value; - } - else - { - throw new SyntaxException("Syntax Error in 'zone' tag - Valid syntax: zone '[ZoneName]'."); - } - - base.Initialize(tagName, markup, tokens); - } - - public override void Render(Context context, TextWriter result) - { - var zoneName = (string)context[_zoneName] ?? _zoneName; - - if (zoneName.IsEmpty()) - return; - - var model = context.Environments.First(); - var evt = new ZoneRenderingEvent(zoneName, model); - - evt.LiquidContext = context; - - EventPublisher.Publish(evt); - - if (evt.Snippets != null && evt.Snippets.Count > 0) - { - foreach (var snippet in evt.Snippets) - { - if (snippet.Parse) - { - Template.Parse(snippet.Content) - .Render(result, new RenderParameters(context.FormatProvider) { LocalVariables = model }); - } - else - { - result.Write(snippet.Content); - } - } - } - } - } - } + internal sealed class ZoneTagFactory : ITagFactory + { + private readonly IEventPublisher _eventPublisher; + + public ZoneTagFactory(IEventPublisher eventPublisher) + { + _eventPublisher = eventPublisher; + } + + public string TagName => "zone"; + + public Tag Create() + { + return new Zone { EventPublisher = _eventPublisher }; + } + + class Zone : Tag + { + private static readonly Regex Syntax = R.B(@"^({0})", DotLiquid.Liquid.QuotedFragment); + + private string _zoneName; + + public IEventPublisher EventPublisher { get; set; } + + public override void Initialize(string tagName, string markup, List tokens) + { + Match syntaxMatch = Syntax.Match(markup); + + if (syntaxMatch.Success) + { + _zoneName = syntaxMatch.Groups[1].Value; + } + else + { + throw new SyntaxException("Syntax Error in 'zone' tag - Valid syntax: zone '[ZoneName]'."); + } + + base.Initialize(tagName, markup, tokens); + } + + public override void Render(Context context, TextWriter result) + { + var zoneName = (string)context[_zoneName] ?? _zoneName; + + if (zoneName.IsEmpty()) + return; + + var model = context.Environments.First(); + var evt = new ZoneRenderingEvent(zoneName, model); + + evt.LiquidContext = context; + + EventPublisher.Publish(evt); + + if (evt.Snippets != null && evt.Snippets.Count > 0) + { + foreach (var snippet in evt.Snippets) + { + if (snippet.Parse) + { + Template.Parse(snippet.Content) + .Render(result, new RenderParameters(context.FormatProvider) { LocalVariables = model }); + } + else + { + result.Write(snippet.Content); + } + } + } + } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/AdminThemedAttribute.cs b/src/Presentation/SmartStore.Web.Framework/Theming/AdminThemedAttribute.cs index 5698f20851..59d32b7132 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/AdminThemedAttribute.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/AdminThemedAttribute.cs @@ -4,53 +4,53 @@ namespace SmartStore.Web.Framework.Theming { - /// - /// Instructs the view engine to additionally search in the admin area for views. - /// - /// - /// The "admin area" corresponds to the ~/Administration base folder. - /// This attribute is useful in plugins - which usually are areas on its own - where views - /// should be rendered as part of the admin backend. - /// Without this attribute the view resolver would directly fallback to the default nameless area - /// when a view could not be resolved from within the plugin area. - /// - public class AdminThemedAttribute : FilterAttribute, IResultFilter - { - public readonly static string[] ExtraAreaViewLocations = new string[] - { - "~/Administration/Views/{1}/{0}", - "~/Administration/Views/Shared/{0}" - }; + /// + /// Instructs the view engine to additionally search in the admin area for views. + /// + /// + /// The "admin area" corresponds to the ~/Administration base folder. + /// This attribute is useful in plugins - which usually are areas on its own - where views + /// should be rendered as part of the admin backend. + /// Without this attribute the view resolver would directly fallback to the default nameless area + /// when a view could not be resolved from within the plugin area. + /// + public class AdminThemedAttribute : FilterAttribute, IResultFilter + { + public readonly static string[] ExtraAreaViewLocations = new string[] + { + "~/Administration/Views/{1}/{0}", + "~/Administration/Views/Shared/{0}" + }; - public Lazy WorkContext { get; set; } + public Lazy WorkContext { get; set; } - public virtual void OnResultExecuting(ResultExecutingContext filterContext) - { - if (filterContext?.Result == null) - return; + public virtual void OnResultExecuting(ResultExecutingContext filterContext) + { + if (filterContext?.Result == null) + return; - if (!filterContext.Result.IsHtmlViewResult()) - return; + if (!filterContext.Result.IsHtmlViewResult()) + return; - if (filterContext.HttpContext.Response.StatusCode < 300) - { - var isNonAdmin = filterContext.HttpContext.GetItem("IsNonAdmin", forceCreation: false); - if(!isNonAdmin) - { - // Add extra view location formats to all view results (even the partial ones) - // {0} is appended by view engine - filterContext.RouteData.DataTokens["ExtraAreaViewLocations"] = ExtraAreaViewLocations; + if (filterContext.HttpContext.Response.StatusCode < 300) + { + var isNonAdmin = filterContext.HttpContext.GetItem("IsNonAdmin", forceCreation: false); + if (!isNonAdmin) + { + // Add extra view location formats to all view results (even the partial ones) + // {0} is appended by view engine + filterContext.RouteData.DataTokens["ExtraAreaViewLocations"] = ExtraAreaViewLocations; - if (!filterContext.IsChildAction) - { - WorkContext.Value.IsAdmin = true; - } - } - } - } + if (!filterContext.IsChildAction) + { + WorkContext.Value.IsAdmin = true; + } + } + } + } - public virtual void OnResultExecuted(ResultExecutedContext filterContext) - { - } - } + public virtual void OnResultExecuted(ResultExecutedContext filterContext) + { + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AssetTranslator.cs b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AssetTranslator.cs index 62bf5fd53e..a77a3cee23 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AssetTranslator.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AssetTranslator.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using BundleTransformer.Core.Assets; using BundleTransformer.Core.Constants; @@ -11,144 +10,138 @@ namespace SmartStore.Web.Framework.Theming.Assets { - public abstract class AssetTranslator : ITranslator - where TTranslator : ITranslator, new() - { - private readonly ITranslator _inner; - - public AssetTranslator() - { - _inner = new TTranslator(); - } - - protected abstract string[] ValidTypeCodes { get; } - - private IAssetCache AssetCache - { - get { return EngineContext.Current.Resolve(); } - } - - public bool IsDebugMode - { - get { return _inner.IsDebugMode; } - set { _inner.IsDebugMode = value; } - } - - public IList Translate(IList assets) - { - var assets2 = WalkAndFindCachedAssets(assets).ToList(); - var result = _inner.Translate(assets2); - return result; - } - - private IEnumerable WalkAndFindCachedAssets(IList assets) - { - foreach (var asset in assets) - { - if (ValidTypeCodes.Contains(asset.AssetTypeCode.ToLowerInvariant())) - { - yield return TranslateInternal(asset); - } - else - { - yield return asset; - } - } - } - - public IAsset Translate(IAsset asset) - { - return TranslateInternal(asset); - } - - private IAsset TranslateInternal(IAsset asset) - { - IAsset result; - var chronometer = EngineContext.Current.Resolve(); - - using (chronometer.Step("Translate asset {0}".FormatInvariant(asset.VirtualPath))) - { - bool validationMode = ThemeHelper.IsStyleValidationRequest(); - - if (validationMode || !TryGetCachedAsset(asset, out result)) - { - using (KeyedLock.Lock("CachedAsset:" + asset.VirtualPath)) - { - if (validationMode || !TryGetCachedAsset(asset, out result)) - { - using (chronometer.Step("Compile asset {0}".FormatInvariant(asset.VirtualPath))) - { - result = _inner.Translate(asset); - - var cachedAsset = new CachedAsset - { - AssetTypeCode = AssetTypeCode.Css, - IsStylesheet = true, - Minified = result.Minified, - Combined = result.Combined, - Content = result.Content, - OriginalAssets = asset.OriginalAssets, - VirtualPath = asset.VirtualPath, - VirtualPathDependencies = result.VirtualPathDependencies, - Url = asset.Url - }; - - result = AssetTranslorUtil.PostProcessAsset(cachedAsset, this.IsDebugMode); - - if (!validationMode) - { - var pCodes = new List(3); - if (cachedAsset.Minified) pCodes.Add(DefaultAssetCache.MinificationCode); - if (cachedAsset.RelativePathsResolved) pCodes.Add(DefaultAssetCache.UrlRewriteCode); - if (cachedAsset.Autoprefixed) pCodes.Add(DefaultAssetCache.AutoprefixCode); - - AssetCache.InsertAsset( - cachedAsset.VirtualPath, - cachedAsset.VirtualPathDependencies, - cachedAsset.Content, - pCodes.ToArray()); - } - } - } - } - } - } - - return result; - } - - private bool TryGetCachedAsset(IAsset asset, out IAsset cachedAsset) - { - cachedAsset = null; - var entry = AssetCache.GetAsset(asset.VirtualPath); - - if (entry != null) - { - cachedAsset = new CachedAsset - { - AssetTypeCode = AssetTypeCode.Css, - Combined = true, - Content = entry.Content, - IsStylesheet = true, - Minified = entry.ProcessorCodes.Contains(DefaultAssetCache.MinificationCode), - RelativePathsResolved = entry.ProcessorCodes.Contains(DefaultAssetCache.UrlRewriteCode), - Autoprefixed = entry.ProcessorCodes.Contains(DefaultAssetCache.AutoprefixCode), - OriginalAssets = new List(), - VirtualPath = asset.VirtualPath, - VirtualPathDependencies = entry.VirtualPathDependencies.ToList(), - Url = asset.Url - }; - } - - return cachedAsset != null; - } - } - - public sealed class SassTranslator : AssetTranslator - { - protected override string[] ValidTypeCodes - { - get { return new[] { "sass", "scss" }; } - } - } + public abstract class AssetTranslator : ITranslator + where TTranslator : ITranslator, new() + { + private readonly ITranslator _inner; + + public AssetTranslator() + { + _inner = new TTranslator(); + } + + protected abstract string[] ValidTypeCodes { get; } + + private IAssetCache AssetCache => EngineContext.Current.Resolve(); + + public bool IsDebugMode + { + get => _inner.IsDebugMode; + set => _inner.IsDebugMode = value; + } + + public IList Translate(IList assets) + { + var assets2 = WalkAndFindCachedAssets(assets).ToList(); + var result = _inner.Translate(assets2); + return result; + } + + private IEnumerable WalkAndFindCachedAssets(IList assets) + { + foreach (var asset in assets) + { + if (ValidTypeCodes.Contains(asset.AssetTypeCode.ToLowerInvariant())) + { + yield return TranslateInternal(asset); + } + else + { + yield return asset; + } + } + } + + public IAsset Translate(IAsset asset) + { + return TranslateInternal(asset); + } + + private IAsset TranslateInternal(IAsset asset) + { + IAsset result; + var chronometer = EngineContext.Current.Resolve(); + + using (chronometer.Step("Translate asset {0}".FormatInvariant(asset.VirtualPath))) + { + bool validationMode = ThemeHelper.IsStyleValidationRequest(); + + if (validationMode || !TryGetCachedAsset(asset, out result)) + { + using (KeyedLock.Lock("CachedAsset:" + asset.VirtualPath)) + { + if (validationMode || !TryGetCachedAsset(asset, out result)) + { + using (chronometer.Step("Compile asset {0}".FormatInvariant(asset.VirtualPath))) + { + result = _inner.Translate(asset); + + var cachedAsset = new CachedAsset + { + AssetTypeCode = AssetTypeCode.Css, + IsStylesheet = true, + Minified = result.Minified, + Combined = result.Combined, + Content = result.Content, + OriginalAssets = asset.OriginalAssets, + VirtualPath = asset.VirtualPath, + VirtualPathDependencies = result.VirtualPathDependencies, + Url = asset.Url + }; + + result = AssetTranslorUtil.PostProcessAsset(cachedAsset, this.IsDebugMode); + + if (!validationMode) + { + var pCodes = new List(3); + if (cachedAsset.Minified) pCodes.Add(DefaultAssetCache.MinificationCode); + if (cachedAsset.RelativePathsResolved) pCodes.Add(DefaultAssetCache.UrlRewriteCode); + if (cachedAsset.Autoprefixed) pCodes.Add(DefaultAssetCache.AutoprefixCode); + + AssetCache.InsertAsset( + cachedAsset.VirtualPath, + cachedAsset.VirtualPathDependencies, + cachedAsset.Content, + pCodes.ToArray()); + } + } + } + } + } + } + + return result; + } + + private bool TryGetCachedAsset(IAsset asset, out IAsset cachedAsset) + { + cachedAsset = null; + var entry = AssetCache.GetAsset(asset.VirtualPath); + + if (entry != null) + { + cachedAsset = new CachedAsset + { + AssetTypeCode = AssetTypeCode.Css, + Combined = true, + Content = entry.Content, + IsStylesheet = true, + Minified = entry.ProcessorCodes.Contains(DefaultAssetCache.MinificationCode), + RelativePathsResolved = entry.ProcessorCodes.Contains(DefaultAssetCache.UrlRewriteCode), + Autoprefixed = entry.ProcessorCodes.Contains(DefaultAssetCache.AutoprefixCode), + OriginalAssets = new List(), + VirtualPath = asset.VirtualPath, + VirtualPathDependencies = entry.VirtualPathDependencies.ToList(), + Url = asset.Url + }; + } + + return cachedAsset != null; + } + } + + public sealed class SassTranslator : AssetTranslator + { + protected override string[] ValidTypeCodes => new[] { "sass", "scss" }; + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AssetTranslorUtil.cs b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AssetTranslorUtil.cs index fc7d0b2c2f..834154cee3 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AssetTranslorUtil.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AssetTranslorUtil.cs @@ -1,26 +1,25 @@ -using System; -using System.Linq; +using System.Linq; using BundleTransformer.Core; using BundleTransformer.Core.Assets; using BundleTransformer.Core.Transformers; namespace SmartStore.Web.Framework.Theming.Assets { - internal static class AssetTranslorUtil - { - internal static IAsset PostProcessAsset(IAsset asset, bool isDebugMode) - { - var transformer = BundleTransformerContext.Current.Styles.GetDefaultTransformInstance() as ITransformer; - if (transformer != null) - { - var processors = transformer.PostProcessors.Where(x => x.UseInDebugMode || !isDebugMode); - foreach (var processor in processors) - { - asset = processor.PostProcess(asset); - } - } + internal static class AssetTranslorUtil + { + internal static IAsset PostProcessAsset(IAsset asset, bool isDebugMode) + { + var transformer = BundleTransformerContext.Current.Styles.GetDefaultTransformInstance() as ITransformer; + if (transformer != null) + { + var processors = transformer.PostProcessors.Where(x => x.UseInDebugMode || !isDebugMode); + foreach (var processor in processors) + { + asset = processor.PostProcess(asset); + } + } - return asset; - } - } + return asset; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AutoPrefixPostProcessor.cs b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AutoPrefixPostProcessor.cs index 254d8a6bfd..694cbcd37a 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AutoPrefixPostProcessor.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/AutoPrefixPostProcessor.cs @@ -1,71 +1,70 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using BundleTransformer.Core.Assets; using BundleTransformer.Core.PostProcessors; namespace SmartStore.Web.Framework.Theming.Assets { - public class AutoprefixPostProcessor : IPostProcessor - { - private readonly BundleTransformer.Autoprefixer.PostProcessors.AutoprefixCssPostProcessor _inner; + public class AutoprefixPostProcessor : IPostProcessor + { + private readonly BundleTransformer.Autoprefixer.PostProcessors.AutoprefixCssPostProcessor _inner; - public AutoprefixPostProcessor() - { - _inner = new BundleTransformer.Autoprefixer.PostProcessors.AutoprefixCssPostProcessor(); - } + public AutoprefixPostProcessor() + { + _inner = new BundleTransformer.Autoprefixer.PostProcessors.AutoprefixCssPostProcessor(); + } - public bool UseInDebugMode - { - get { return _inner.UseInDebugMode; } - set { _inner.UseInDebugMode = value; } - } + public bool UseInDebugMode + { + get => _inner.UseInDebugMode; + set => _inner.UseInDebugMode = value; + } - public IList PostProcess(IList assets) - { - Guard.NotNull(assets, nameof(assets)); + public IList PostProcess(IList assets) + { + Guard.NotNull(assets, nameof(assets)); - var list = assets.Where(CanProcess).ToList(); + var list = assets.Where(CanProcess).ToList(); - if (list.Count == 0) - { - return assets; - } + if (list.Count == 0) + { + return assets; + } - // Process only unprocessed assets... - _inner.PostProcess(list); + // Process only unprocessed assets... + _inner.PostProcess(list); - // Set Autoprefixed flag to true in our custom cached assets - list.OfType().Each(x => x.Autoprefixed = true); + // Set Autoprefixed flag to true in our custom cached assets + list.OfType().Each(x => x.Autoprefixed = true); - // ...but return the original list - return assets; - } + // ...but return the original list + return assets; + } - public IAsset PostProcess(IAsset asset) - { - Guard.NotNull(asset, nameof(asset)); + public IAsset PostProcess(IAsset asset) + { + Guard.NotNull(asset, nameof(asset)); - var cachedAsset = asset as CachedAsset; - if (cachedAsset == null || cachedAsset.Autoprefixed == false) - { - asset = _inner.PostProcess(asset); + var cachedAsset = asset as CachedAsset; + if (cachedAsset == null || cachedAsset.Autoprefixed == false) + { + asset = _inner.PostProcess(asset); - if (cachedAsset != null) - { - cachedAsset.Autoprefixed = true; - } + if (cachedAsset != null) + { + cachedAsset.Autoprefixed = true; + } - return asset; - } + return asset; + } - return asset; - } + return asset; + } - private static bool CanProcess(IAsset asset) - { - var cachedAsset = asset as CachedAsset; - return asset.IsStylesheet && (cachedAsset == null || cachedAsset.Autoprefixed == false); - } - } + private static bool CanProcess(IAsset asset) + { + var cachedAsset = asset as CachedAsset; + return asset.IsStylesheet && (cachedAsset == null || cachedAsset.Autoprefixed == false); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/BundlingVirtualPathProvider.cs b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/BundlingVirtualPathProvider.cs index edb20c5c8b..9c9b79918f 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/BundlingVirtualPathProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/BundlingVirtualPathProvider.cs @@ -1,205 +1,205 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; using System.IO; +using System.Linq; using System.Web.Caching; using System.Web.Hosting; using SmartStore.Core.Infrastructure; namespace SmartStore.Web.Framework.Theming.Assets { - public sealed class BundlingVirtualPathProvider : ThemingVirtualPathProvider - { - private readonly SassCheckedPathStack _sassCheckedPathStack; + public sealed class BundlingVirtualPathProvider : ThemingVirtualPathProvider + { + private readonly SassCheckedPathStack _sassCheckedPathStack; - public BundlingVirtualPathProvider(VirtualPathProvider previous) - : base(previous) + public BundlingVirtualPathProvider(VirtualPathProvider previous) + : base(previous) { - _sassCheckedPathStack = new SassCheckedPathStack(); - } + _sassCheckedPathStack = new SassCheckedPathStack(); + } public override bool FileExists(string virtualPath) { - var exists = false; - var styleResult = ThemeHelper.IsStyleSheet(virtualPath); - - TokenizedSassPath sassPath = null; - - if (styleResult != null) - { - exists = _sassCheckedPathStack.Check(styleResult, out sassPath); - if (exists) - { - // It seems awkward to return false when check result actually says true. - // But true in this context means: a check for THIS sass file pattern (e.g. _slick.scss.sass) - // yielded true previously (because _slick.scss really exists on disks), therefore _slick.scss.sass - // CANNOT exist (or SHOULD not by convention). By returning false we prevent that a real filesystem check - // is made against _slick.scss.sass (which is huge performace saver considering that IThemeFileResolver - // also does some additional checks). - return false; - } - - if (styleResult.IsThemeVars || styleResult.IsModuleImports) - { - _sassCheckedPathStack.PushExistingPath(sassPath); - return true; - } - } - - exists = base.FileExists(virtualPath); - - if (exists && sassPath != null) - { - _sassCheckedPathStack.PushExistingPath(sassPath); - } - - return exists; + var exists = false; + var styleResult = ThemeHelper.IsStyleSheet(virtualPath); + + TokenizedSassPath sassPath = null; + + if (styleResult != null) + { + exists = _sassCheckedPathStack.Check(styleResult, out sassPath); + if (exists) + { + // It seems awkward to return false when check result actually says true. + // But true in this context means: a check for THIS sass file pattern (e.g. _slick.scss.sass) + // yielded true previously (because _slick.scss really exists on disks), therefore _slick.scss.sass + // CANNOT exist (or SHOULD not by convention). By returning false we prevent that a real filesystem check + // is made against _slick.scss.sass (which is huge performace saver considering that IThemeFileResolver + // also does some additional checks). + return false; + } + + if (styleResult.IsThemeVars || styleResult.IsModuleImports) + { + _sassCheckedPathStack.PushExistingPath(sassPath); + return true; + } + } + + exists = base.FileExists(virtualPath); + + if (exists && sassPath != null) + { + _sassCheckedPathStack.PushExistingPath(sassPath); + } + + return exists; } - + public override VirtualFile GetFile(string virtualPath) { - var styleResult = ThemeHelper.IsStyleSheet(virtualPath); - if (styleResult != null) - { - if (styleResult.IsThemeVars) - { - var theme = ThemeHelper.ResolveCurrentTheme(); - int storeId = ThemeHelper.ResolveCurrentStoreId(); - return new ThemeVarsVirtualFile(virtualPath, styleResult.Extension, theme.ThemeName, storeId); - } - else if (styleResult.IsModuleImports) - { - return new ModuleImportsVirtualFile(virtualPath, ThemeHelper.IsAdminArea()); - } - } - - return base.GetFile(virtualPath); + var styleResult = ThemeHelper.IsStyleSheet(virtualPath); + if (styleResult != null) + { + if (styleResult.IsThemeVars) + { + var theme = ThemeHelper.ResolveCurrentTheme(); + int storeId = ThemeHelper.ResolveCurrentStoreId(); + return new ThemeVarsVirtualFile(virtualPath, styleResult.Extension, theme.ThemeName, storeId); + } + else if (styleResult.IsModuleImports) + { + return new ModuleImportsVirtualFile(virtualPath, ThemeHelper.IsAdminArea()); + } + } + + return base.GetFile(virtualPath); + } + + public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart) + { + if (ThemeHelper.IsStyleValidationRequest()) + { + var themeVarsPath = virtualPathDependencies.Cast().FirstOrDefault(x => ThemeHelper.PathIsThemeVars(x)); + if (themeVarsPath.HasValue()) + { + return base.GetCacheDependency(virtualPath, new[] { themeVarsPath }, utcStart); + } + } + + return base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart); } + } + + internal class SassCheckedPathStack + { + private readonly string[] _styleExtensions = new[] { ".scss", ".sass", ".css" }; - public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart) + private readonly ContextState> _state + = new ContextState>("SassCheckedPathStack.State", () => new Stack()); + + public SassCheckedPathStack() { - if (ThemeHelper.IsStyleValidationRequest()) - { - var themeVarsPath = virtualPathDependencies.Cast().FirstOrDefault(x => ThemeHelper.PathIsThemeVars(x)); - if (themeVarsPath.HasValue()) - { - return base.GetCacheDependency(virtualPath, new[] { themeVarsPath }, utcStart); - } - } - - return base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart); } - } - - internal class SassCheckedPathStack - { - private readonly string[] _styleExtensions = new[] { ".scss", ".sass", ".css" }; - - private readonly ContextState> _state - = new ContextState>("SassCheckedPathStack.State", () => new Stack()); - - public SassCheckedPathStack() - { - } - - /// - /// Checks last path existence - /// - /// true = does exist, no need to check | false = not checked yet - public bool Check(StyleSheetResult styleResult, out TokenizedSassPath path) - { - path = new TokenizedSassPath(styleResult); - - var state = _state.GetState(); - if (state.Count == 0) - return false; - - var currentPath = path; - var lastPath = _state.GetState().Peek(); - - if (currentPath.Extension.IsEmpty()) - { - // We dont't allow extension-less Sass files, so no need to check. - return true; - } - - if (lastPath.Dir != currentPath.Dir) - { - return false; - } - - if (currentPath.StyleResult.IsBaseImport && lastPath.StyleResult.IsBaseImport && currentPath.FileName == lastPath.FileName) - { - return true; - } - - if (currentPath.StyleResult.IsModuleImports && lastPath.StyleResult.IsModuleImports) - { - return true; - } - - if (currentPath.StyleResult.IsThemeVars && lastPath.StyleResult.IsThemeVars) - { - return true; - } - - // slick.scss.(scss|sass|css) > slick.scss - if (Path.GetExtension(currentPath.FileNameWithoutExtension) == ".scss") - { - return true; - } - - // slick.(sass|css) > slick.scss - if (!currentPath.StyleResult.IsBaseImport && _styleExtensions.Contains(currentPath.Extension) && currentPath.FileNameWithoutExtension == lastPath.FileNameWithoutExtension) - { - return true; - } - - // _slick.scss > slick.scss - if (currentPath.FileName.StartsWith("_")) - { - if (currentPath.FileName.Substring(1) == lastPath.FileName) - { - return true; - } - } - - // slick.(scss|sass|css) > _slick.scss - if (lastPath.FileNameWithoutExtension.StartsWith("_")) - { - if (lastPath.FileNameWithoutExtension == "_" + currentPath.FileNameWithoutExtension) - { - return true; - } - } - - return false; - } - - public void PushExistingPath(TokenizedSassPath path) - { - _state.GetState().Push(path); - } - } - - internal class TokenizedSassPath - { - public TokenizedSassPath(StyleSheetResult styleResult) - { - StyleResult = styleResult; - VirtualPath = styleResult.Path; - Extension = styleResult.Extension.EmptyNull(); - FileName = Path.GetFileName(styleResult.Path); - FileNameWithoutExtension = FileName.Substring(0, FileName.Length - Extension.Length); - Dir = VirtualPath.Substring(0, VirtualPath.Length - FileName.Length); - } - - public StyleSheetResult StyleResult { get; set; } - public string VirtualPath { get; private set; } - public string Dir { get; private set; } - public string FileName { get; private set; } - public string FileNameWithoutExtension { get; private set; } - public string Extension { get; private set; } - } + + /// + /// Checks last path existence + /// + /// true = does exist, no need to check | false = not checked yet + public bool Check(StyleSheetResult styleResult, out TokenizedSassPath path) + { + path = new TokenizedSassPath(styleResult); + + var state = _state.GetState(); + if (state.Count == 0) + return false; + + var currentPath = path; + var lastPath = _state.GetState().Peek(); + + if (currentPath.Extension.IsEmpty()) + { + // We dont't allow extension-less Sass files, so no need to check. + return true; + } + + if (lastPath.Dir != currentPath.Dir) + { + return false; + } + + if (currentPath.StyleResult.IsBaseImport && lastPath.StyleResult.IsBaseImport && currentPath.FileName == lastPath.FileName) + { + return true; + } + + if (currentPath.StyleResult.IsModuleImports && lastPath.StyleResult.IsModuleImports) + { + return true; + } + + if (currentPath.StyleResult.IsThemeVars && lastPath.StyleResult.IsThemeVars) + { + return true; + } + + // slick.scss.(scss|sass|css) > slick.scss + if (Path.GetExtension(currentPath.FileNameWithoutExtension) == ".scss") + { + return true; + } + + // slick.(sass|css) > slick.scss + if (!currentPath.StyleResult.IsBaseImport && _styleExtensions.Contains(currentPath.Extension) && currentPath.FileNameWithoutExtension == lastPath.FileNameWithoutExtension) + { + return true; + } + + // _slick.scss > slick.scss + if (currentPath.FileName.StartsWith("_")) + { + if (currentPath.FileName.Substring(1) == lastPath.FileName) + { + return true; + } + } + + // slick.(scss|sass|css) > _slick.scss + if (lastPath.FileNameWithoutExtension.StartsWith("_")) + { + if (lastPath.FileNameWithoutExtension == "_" + currentPath.FileNameWithoutExtension) + { + return true; + } + } + + return false; + } + + public void PushExistingPath(TokenizedSassPath path) + { + _state.GetState().Push(path); + } + } + + internal class TokenizedSassPath + { + public TokenizedSassPath(StyleSheetResult styleResult) + { + StyleResult = styleResult; + VirtualPath = styleResult.Path; + Extension = styleResult.Extension.EmptyNull(); + FileName = Path.GetFileName(styleResult.Path); + FileNameWithoutExtension = FileName.Substring(0, FileName.Length - Extension.Length); + Dir = VirtualPath.Substring(0, VirtualPath.Length - FileName.Length); + } + + public StyleSheetResult StyleResult { get; set; } + public string VirtualPath { get; private set; } + public string Dir { get; private set; } + public string FileName { get; private set; } + public string FileNameWithoutExtension { get; private set; } + public string Extension { get; private set; } + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/CachedAsset.cs b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/CachedAsset.cs index 6844f235cc..d4f43fe25b 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/CachedAsset.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/CachedAsset.cs @@ -1,22 +1,21 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using BundleTransformer.Core.Assets; namespace SmartStore.Web.Framework.Theming.Assets { - internal class CachedAsset : IAsset - { - public string AssetTypeCode { get; set; } - public bool Combined { get; set; } - public string Content { get; set; } - public bool IsScript { get; set; } - public bool IsStylesheet { get; set; } - public bool Minified { get; set; } - public bool RelativePathsResolved { get; set; } - public bool Autoprefixed { get; set; } - public IList OriginalAssets { get; set; } - public string Url { get; set; } - public string VirtualPath { get; set; } - public IList VirtualPathDependencies { get; set; } - } + internal class CachedAsset : IAsset + { + public string AssetTypeCode { get; set; } + public bool Combined { get; set; } + public string Content { get; set; } + public bool IsScript { get; set; } + public bool IsStylesheet { get; set; } + public bool Minified { get; set; } + public bool RelativePathsResolved { get; set; } + public bool Autoprefixed { get; set; } + public IList OriginalAssets { get; set; } + public string Url { get; set; } + public string VirtualPath { get; set; } + public IList VirtualPathDependencies { get; set; } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/DefaultAssetCache.cs b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/DefaultAssetCache.cs index f1eaf270da..c86420202b 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/DefaultAssetCache.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/DefaultAssetCache.cs @@ -17,421 +17,421 @@ namespace SmartStore.Web.Framework.Theming.Assets { - public class DefaultAssetCache : IAssetCache - { - public const string MinificationCode = "min"; - public const string AutoprefixCode = "autoprefix"; - public const string UrlRewriteCode = "urlrewrite"; - - public static readonly IAssetCache Null = new NullCache(); - const string CacheKeyPrefix = "sm:AssetCacheEntry:"; - - private readonly bool _isEnabled; - private readonly ThemeSettings _themeSettings; - private readonly IApplicationEnvironment _env; - private readonly IThemeContext _themeContext; - private readonly IThemeRegistry _themeRegistry; - private readonly IThemeFileResolver _themeFileResolver; - private readonly ICommonServices _services; - - private VirtualFolder _cacheFolder; - - public DefaultAssetCache( - ThemeSettings themeSettings, - IApplicationEnvironment env, - IThemeFileResolver themeFileResolver, - IThemeContext themeContext, - IThemeRegistry themeRegistry, - ICommonServices services) - { - _themeSettings = themeSettings; - _isEnabled = _themeSettings.AssetCachingEnabled == 2 || (_themeSettings.AssetCachingEnabled == 0 && !HttpContext.Current.IsDebuggingEnabled); - - _env = env; - _themeContext = themeContext; - _themeFileResolver = themeFileResolver; - _themeRegistry = themeRegistry; - _services = services; - - Logger = NullLogger.Instance; - } - - public ILogger Logger { get; set; } - - public IVirtualFolder CacheFolder - { - get - { - if (_cacheFolder == null) - { - _cacheFolder = new VirtualFolder( - _env.TenantFolder.GetVirtualPath("AssetCache"), - _env.TenantFolder.VirtualPathProvider, - Logger); - } - - return _cacheFolder; - } - } - - public CachedAssetEntry GetAsset(string virtualPath) - { - if (!_isEnabled) - return null; - - Guard.NotEmpty(virtualPath, nameof(virtualPath)); - - var cacheDirectoryName = ResolveCacheDirectoryName(virtualPath, out string themeName, out int storeId); - - if (CacheFolder.DirectoryExists(cacheDirectoryName)) - { - try - { - var deps = CacheFolder.ReadFile(CacheFolder.Combine(cacheDirectoryName, "asset.dependencies")); - var hash = CacheFolder.ReadFile(CacheFolder.Combine(cacheDirectoryName, "asset.hash")); - - if (!TryValidate(virtualPath, deps, hash, out IEnumerable parsedDeps, out string currentHash)) - { - Logger.DebugFormat("Invalidating cached asset for '{0}' because it is not valid anymore.", virtualPath); - InvalidateAssetInternal(cacheDirectoryName, themeName, storeId); - return null; - } - - // TODO: determine correct file extension - var content = CacheFolder.ReadFile(CacheFolder.Combine(cacheDirectoryName, "asset.css")); - if (content == null) - { - using (KeyedLock.Lock(BuildLockKey(cacheDirectoryName))) - { - InvalidateAssetInternal(cacheDirectoryName, themeName, storeId); - return null; - } - } - - var codes = ParseFileContent(CacheFolder.ReadFile(CacheFolder.Combine(cacheDirectoryName, "asset.pcodes"))); - - var entry = new CachedAssetEntry - { - Content = content, - HashCode = currentHash, - OriginalVirtualPath = virtualPath, - VirtualPathDependencies = parsedDeps, - PhysicalPath = CacheFolder.MapPath(cacheDirectoryName), - ThemeName = themeName, - StoreId = storeId, - ProcessorCodes = codes.ToArray() - }; - - SetupEvictionObserver(entry); - - Logger.DebugFormat("Succesfully read asset '{0}' from cache.", virtualPath); - - return entry; - } - catch (Exception ex) - { - Logger.ErrorFormat(ex, "Error while resolving asset '{0}' from the asset cache.", virtualPath); - } - } - - return null; - } - - public CachedAssetEntry InsertAsset(string virtualPath, IEnumerable virtualPathDependencies, string content, params string[] processorCodes) - { - if (!_isEnabled) - return null; - - Guard.NotEmpty(virtualPath, nameof(virtualPath)); - Guard.NotEmpty(content, nameof(content)); - - var cacheDirectoryName = ResolveCacheDirectoryName(virtualPath, out string themeName, out int storeId); - - using (KeyedLock.Lock(BuildLockKey(cacheDirectoryName))) - { - CacheFolder.CreateDirectory(cacheDirectoryName); - - try - { - // Save main content file - // TODO: determine correct file extension - CreateFileFromEntries(cacheDirectoryName, "asset.css", new[] { content }); - - // Save dependencies file - var deps = ResolveVirtualPathDependencies(virtualPath, virtualPathDependencies, themeName); - CreateFileFromEntries(cacheDirectoryName, "asset.dependencies", deps); - - // Save hash file - var currentHash = BundleTable.VirtualPathProvider.GetFileHash(virtualPath, deps); - CreateFileFromEntries(cacheDirectoryName, "asset.hash", new[] { currentHash }); - - // Save codes file - CreateFileFromEntries(cacheDirectoryName, "asset.pcodes", processorCodes); - - var entry = new CachedAssetEntry - { - Content = content, - HashCode = currentHash, - OriginalVirtualPath = virtualPath, - VirtualPathDependencies = deps, - PhysicalPath = CacheFolder.MapPath(cacheDirectoryName), - ThemeName = themeName, - StoreId = storeId, - ProcessorCodes = processorCodes - }; - - SetupEvictionObserver(entry); - - Logger.DebugFormat("Succesfully inserted asset '{0}' to cache.", virtualPath); - - return entry; - } - catch (Exception ex) - { - Logger.ErrorFormat(ex, "Error while inserting asset '{0}' to the asset cache.", virtualPath); - InvalidateAssetInternal(cacheDirectoryName, themeName, storeId); - } - } - - return null; - } - - public void Clear() - { - if (_env.TenantFolder.DirectoryExists("AssetCache")) - { - _env.TenantFolder.TryDeleteDirectory("AssetCache"); - // Remove the eviction observer also - HttpRuntime.Cache.RemoveByPattern(CacheKeyPrefix); - } - } - - public bool InvalidateAsset(string virtualPath) - { - Guard.NotEmpty(virtualPath, nameof(virtualPath)); - - var cacheDirectoryName = ResolveCacheDirectoryName(virtualPath, out string themeName, out int storeId); - - using (KeyedLock.Lock(BuildLockKey(cacheDirectoryName))) - { - if (CacheFolder.DirectoryExists(cacheDirectoryName)) - { - return InvalidateAssetInternal(cacheDirectoryName, themeName, storeId); - } - - return false; - } - } - - private string BuildLockKey(string dirName) - { - return "AssetCache.Dir." + dirName; - } - - /// - /// Invalidates a cached asset when any themevar was changed or the theme was touched on file system - /// - /// - private static void SetupEvictionObserver(CachedAssetEntry entry) - { - if (entry.ThemeName == null) - return; - - var cacheKey = CacheKeyPrefix + "{0}:{1}".FormatInvariant(entry.ThemeName, entry.StoreId); - - var cacheDependency = new CacheDependency( - new string[0], - new[] { FrameworkCacheConsumer.BuildThemeVarsCacheKey(entry.ThemeName, entry.StoreId) }, - DateTime.UtcNow); - - HttpRuntime.Cache.Insert( - cacheKey, - entry.PhysicalPath, - cacheDependency, - Cache.NoAbsoluteExpiration, - Cache.NoSlidingExpiration, - CacheItemPriority.Default, - OnCacheItemRemoved); - } - - private static void OnCacheItemRemoved(string key, object value, CacheItemRemovedReason reason) - { - if (HostingEnvironment.ShutdownReason > ApplicationShutdownReason.None) - { - // Don't evict cached files during app shutdown. Dependant cache keys change - // during a shutdown caused by HttpRuntime.Cache full eviction. - return; - } - - // Keep this low level - var path = value as string; - if (path.HasValue() && reason == CacheItemRemovedReason.DependencyChanged) - { - try - { - Directory.Delete(path, true); - } - catch { } - } - } - - private IEnumerable ResolveVirtualPathDependencies(string virtualPath, IEnumerable deps, string themeName) - { - var list = new HashSet(StringComparer.OrdinalIgnoreCase); - - foreach (var d in (deps ?? new string[0]).Concat(new[] { virtualPath })) - { - var result = _themeFileResolver.Resolve(d); - list.Add(result != null ? result.ResultVirtualPath : d); - } - - // Add all theme.config files in the theme hierarchy chain - var manifest = _themeRegistry.GetThemeManifest(themeName); - while (manifest != null) - { - list.Add(VirtualPathUtility.ToAbsolute(CacheFolder.Combine(manifest.Location, manifest.ThemeName, "theme.config"))); - manifest = manifest.BaseTheme; - } - - return list; - } - - private bool InvalidateAssetInternal(string cacheDirectoryName, string theme, int storeId) - { - if (CacheFolder.TryDeleteDirectory(cacheDirectoryName)) - { - if (theme.HasValue()) - { - // Remove the eviction observer - var cacheKey = CacheKeyPrefix + "{0}:{1}".FormatInvariant(theme, storeId); - HttpRuntime.Cache.Remove(cacheKey); - - } - - return true; - } - - return false; - } - - /// - /// Checks whether asset is up-to-date. - /// - /// - /// - /// - /// - /// false if asset is not valid anymore and must be evicted from cache - private bool TryValidate(string virtualPath, string lastDeps, string lastHash, out IEnumerable parsedDeps, out string currentHash) - { - parsedDeps = null; - currentHash = null; - - try - { - if (lastDeps.IsEmpty() || lastHash.IsEmpty()) - { - return false; - } - - parsedDeps = ParseFileContent(lastDeps); - - // Check if dependency files hash matches the last saved hash - currentHash = BundleTable.VirtualPathProvider.GetFileHash(virtualPath, parsedDeps); - - return lastHash == currentHash; - } - catch - { - return false; - } - } - - private string ResolveCacheDirectoryName(string virtualPath, out string themeName, out int storeId) - { - themeName = null; - storeId = 0; - - if (virtualPath[0] != '~') - { - virtualPath = VirtualPathUtility.ToAppRelative(virtualPath); - } - - // Build a dir name: ~/Themes/Flex/Content/theme.scss > themes-flex-content-theme.scss-flex-1 - var folderName = virtualPath.TrimStart('~', '/', '\\') - .Replace('/', '-') - .Replace('\\', '-'); - - if (ThemeHelper.PathIsInheritableThemeFile(virtualPath)) - { - themeName = _themeContext.CurrentTheme.ThemeName; - storeId = _services.StoreContext.CurrentStore.Id; - folderName += "-" + themeName + "-" + storeId; - } - - return folderName.ToLowerInvariant(); - } - - private IEnumerable ParseFileContent(string content) - { - var list = new HashSet(StringComparer.OrdinalIgnoreCase); - if (content.IsEmpty()) return list; - - var sr = new StringReader(content); - while (true) - { - var f = sr.ReadLine(); - if (f != null && f.HasValue()) - { - list.Add(f); - } - else - { - break; - } - } - - return list; - } - - private void CreateFileFromEntries(string dirName, string fileName, IEnumerable entries) - { - if (entries == null || !entries.Any()) - return; - - var sb = new StringBuilder(); - foreach (var f in entries) - { - sb.AppendLine(f); - } - - var content = sb.ToString().TrimEnd(); - if (content.HasValue()) - { - CacheFolder.CreateTextFile(CacheFolder.Combine(dirName, fileName), content); - } - } - - class NullCache : IAssetCache - { - public void Clear() - { - } - - public CachedAssetEntry GetAsset(string virtualPath) - { - return null; - } - - public CachedAssetEntry InsertAsset(string virtualPath, IEnumerable virtualPathDependencies, string content, params string[] processorCodes) - { - return null; - } - - public bool InvalidateAsset(string virtualPath) - { - return false; - } - } - } + public class DefaultAssetCache : IAssetCache + { + public const string MinificationCode = "min"; + public const string AutoprefixCode = "autoprefix"; + public const string UrlRewriteCode = "urlrewrite"; + + public static readonly IAssetCache Null = new NullCache(); + const string CacheKeyPrefix = "sm:AssetCacheEntry:"; + + private readonly bool _isEnabled; + private readonly ThemeSettings _themeSettings; + private readonly IApplicationEnvironment _env; + private readonly IThemeContext _themeContext; + private readonly IThemeRegistry _themeRegistry; + private readonly IThemeFileResolver _themeFileResolver; + private readonly ICommonServices _services; + + private VirtualFolder _cacheFolder; + + public DefaultAssetCache( + ThemeSettings themeSettings, + IApplicationEnvironment env, + IThemeFileResolver themeFileResolver, + IThemeContext themeContext, + IThemeRegistry themeRegistry, + ICommonServices services) + { + _themeSettings = themeSettings; + _isEnabled = _themeSettings.AssetCachingEnabled == 2 || (_themeSettings.AssetCachingEnabled == 0 && !HttpContext.Current.IsDebuggingEnabled); + + _env = env; + _themeContext = themeContext; + _themeFileResolver = themeFileResolver; + _themeRegistry = themeRegistry; + _services = services; + + Logger = NullLogger.Instance; + } + + public ILogger Logger { get; set; } + + public IVirtualFolder CacheFolder + { + get + { + if (_cacheFolder == null) + { + _cacheFolder = new VirtualFolder( + _env.TenantFolder.GetVirtualPath("AssetCache"), + _env.TenantFolder.VirtualPathProvider, + Logger); + } + + return _cacheFolder; + } + } + + public CachedAssetEntry GetAsset(string virtualPath) + { + if (!_isEnabled) + return null; + + Guard.NotEmpty(virtualPath, nameof(virtualPath)); + + var cacheDirectoryName = ResolveCacheDirectoryName(virtualPath, out string themeName, out int storeId); + + if (CacheFolder.DirectoryExists(cacheDirectoryName)) + { + try + { + var deps = CacheFolder.ReadFile(CacheFolder.Combine(cacheDirectoryName, "asset.dependencies")); + var hash = CacheFolder.ReadFile(CacheFolder.Combine(cacheDirectoryName, "asset.hash")); + + if (!TryValidate(virtualPath, deps, hash, out IEnumerable parsedDeps, out string currentHash)) + { + Logger.DebugFormat("Invalidating cached asset for '{0}' because it is not valid anymore.", virtualPath); + InvalidateAssetInternal(cacheDirectoryName, themeName, storeId); + return null; + } + + // TODO: determine correct file extension + var content = CacheFolder.ReadFile(CacheFolder.Combine(cacheDirectoryName, "asset.css")); + if (content == null) + { + using (KeyedLock.Lock(BuildLockKey(cacheDirectoryName))) + { + InvalidateAssetInternal(cacheDirectoryName, themeName, storeId); + return null; + } + } + + var codes = ParseFileContent(CacheFolder.ReadFile(CacheFolder.Combine(cacheDirectoryName, "asset.pcodes"))); + + var entry = new CachedAssetEntry + { + Content = content, + HashCode = currentHash, + OriginalVirtualPath = virtualPath, + VirtualPathDependencies = parsedDeps, + PhysicalPath = CacheFolder.MapPath(cacheDirectoryName), + ThemeName = themeName, + StoreId = storeId, + ProcessorCodes = codes.ToArray() + }; + + SetupEvictionObserver(entry); + + Logger.DebugFormat("Succesfully read asset '{0}' from cache.", virtualPath); + + return entry; + } + catch (Exception ex) + { + Logger.ErrorFormat(ex, "Error while resolving asset '{0}' from the asset cache.", virtualPath); + } + } + + return null; + } + + public CachedAssetEntry InsertAsset(string virtualPath, IEnumerable virtualPathDependencies, string content, params string[] processorCodes) + { + if (!_isEnabled) + return null; + + Guard.NotEmpty(virtualPath, nameof(virtualPath)); + Guard.NotEmpty(content, nameof(content)); + + var cacheDirectoryName = ResolveCacheDirectoryName(virtualPath, out string themeName, out int storeId); + + using (KeyedLock.Lock(BuildLockKey(cacheDirectoryName))) + { + CacheFolder.CreateDirectory(cacheDirectoryName); + + try + { + // Save main content file + // TODO: determine correct file extension + CreateFileFromEntries(cacheDirectoryName, "asset.css", new[] { content }); + + // Save dependencies file + var deps = ResolveVirtualPathDependencies(virtualPath, virtualPathDependencies, themeName); + CreateFileFromEntries(cacheDirectoryName, "asset.dependencies", deps); + + // Save hash file + var currentHash = BundleTable.VirtualPathProvider.GetFileHash(virtualPath, deps); + CreateFileFromEntries(cacheDirectoryName, "asset.hash", new[] { currentHash }); + + // Save codes file + CreateFileFromEntries(cacheDirectoryName, "asset.pcodes", processorCodes); + + var entry = new CachedAssetEntry + { + Content = content, + HashCode = currentHash, + OriginalVirtualPath = virtualPath, + VirtualPathDependencies = deps, + PhysicalPath = CacheFolder.MapPath(cacheDirectoryName), + ThemeName = themeName, + StoreId = storeId, + ProcessorCodes = processorCodes + }; + + SetupEvictionObserver(entry); + + Logger.DebugFormat("Succesfully inserted asset '{0}' to cache.", virtualPath); + + return entry; + } + catch (Exception ex) + { + Logger.ErrorFormat(ex, "Error while inserting asset '{0}' to the asset cache.", virtualPath); + InvalidateAssetInternal(cacheDirectoryName, themeName, storeId); + } + } + + return null; + } + + public void Clear() + { + if (_env.TenantFolder.DirectoryExists("AssetCache")) + { + _env.TenantFolder.TryDeleteDirectory("AssetCache"); + // Remove the eviction observer also + HttpRuntime.Cache.RemoveByPattern(CacheKeyPrefix); + } + } + + public bool InvalidateAsset(string virtualPath) + { + Guard.NotEmpty(virtualPath, nameof(virtualPath)); + + var cacheDirectoryName = ResolveCacheDirectoryName(virtualPath, out string themeName, out int storeId); + + using (KeyedLock.Lock(BuildLockKey(cacheDirectoryName))) + { + if (CacheFolder.DirectoryExists(cacheDirectoryName)) + { + return InvalidateAssetInternal(cacheDirectoryName, themeName, storeId); + } + + return false; + } + } + + private string BuildLockKey(string dirName) + { + return "AssetCache.Dir." + dirName; + } + + /// + /// Invalidates a cached asset when any themevar was changed or the theme was touched on file system + /// + /// + private static void SetupEvictionObserver(CachedAssetEntry entry) + { + if (entry.ThemeName == null) + return; + + var cacheKey = CacheKeyPrefix + "{0}:{1}".FormatInvariant(entry.ThemeName, entry.StoreId); + + var cacheDependency = new CacheDependency( + new string[0], + new[] { FrameworkCacheConsumer.BuildThemeVarsCacheKey(entry.ThemeName, entry.StoreId) }, + DateTime.UtcNow); + + HttpRuntime.Cache.Insert( + cacheKey, + entry.PhysicalPath, + cacheDependency, + Cache.NoAbsoluteExpiration, + Cache.NoSlidingExpiration, + CacheItemPriority.Default, + OnCacheItemRemoved); + } + + private static void OnCacheItemRemoved(string key, object value, CacheItemRemovedReason reason) + { + if (HostingEnvironment.ShutdownReason > ApplicationShutdownReason.None) + { + // Don't evict cached files during app shutdown. Dependant cache keys change + // during a shutdown caused by HttpRuntime.Cache full eviction. + return; + } + + // Keep this low level + var path = value as string; + if (path.HasValue() && reason == CacheItemRemovedReason.DependencyChanged) + { + try + { + Directory.Delete(path, true); + } + catch { } + } + } + + private IEnumerable ResolveVirtualPathDependencies(string virtualPath, IEnumerable deps, string themeName) + { + var list = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (var d in (deps ?? new string[0]).Concat(new[] { virtualPath })) + { + var result = _themeFileResolver.Resolve(d); + list.Add(result != null ? result.ResultVirtualPath : d); + } + + // Add all theme.config files in the theme hierarchy chain + var manifest = _themeRegistry.GetThemeManifest(themeName); + while (manifest != null) + { + list.Add(VirtualPathUtility.ToAbsolute(CacheFolder.Combine(manifest.Location, manifest.ThemeName, "theme.config"))); + manifest = manifest.BaseTheme; + } + + return list; + } + + private bool InvalidateAssetInternal(string cacheDirectoryName, string theme, int storeId) + { + if (CacheFolder.TryDeleteDirectory(cacheDirectoryName)) + { + if (theme.HasValue()) + { + // Remove the eviction observer + var cacheKey = CacheKeyPrefix + "{0}:{1}".FormatInvariant(theme, storeId); + HttpRuntime.Cache.Remove(cacheKey); + + } + + return true; + } + + return false; + } + + /// + /// Checks whether asset is up-to-date. + /// + /// + /// + /// + /// + /// false if asset is not valid anymore and must be evicted from cache + private bool TryValidate(string virtualPath, string lastDeps, string lastHash, out IEnumerable parsedDeps, out string currentHash) + { + parsedDeps = null; + currentHash = null; + + try + { + if (lastDeps.IsEmpty() || lastHash.IsEmpty()) + { + return false; + } + + parsedDeps = ParseFileContent(lastDeps); + + // Check if dependency files hash matches the last saved hash + currentHash = BundleTable.VirtualPathProvider.GetFileHash(virtualPath, parsedDeps); + + return lastHash == currentHash; + } + catch + { + return false; + } + } + + private string ResolveCacheDirectoryName(string virtualPath, out string themeName, out int storeId) + { + themeName = null; + storeId = 0; + + if (virtualPath[0] != '~') + { + virtualPath = VirtualPathUtility.ToAppRelative(virtualPath); + } + + // Build a dir name: ~/Themes/Flex/Content/theme.scss > themes-flex-content-theme.scss-flex-1 + var folderName = virtualPath.TrimStart('~', '/', '\\') + .Replace('/', '-') + .Replace('\\', '-'); + + if (ThemeHelper.PathIsInheritableThemeFile(virtualPath)) + { + themeName = _themeContext.CurrentTheme.ThemeName; + storeId = _services.StoreContext.CurrentStore.Id; + folderName += "-" + themeName + "-" + storeId; + } + + return folderName.ToLowerInvariant(); + } + + private IEnumerable ParseFileContent(string content) + { + var list = new HashSet(StringComparer.OrdinalIgnoreCase); + if (content.IsEmpty()) return list; + + var sr = new StringReader(content); + while (true) + { + var f = sr.ReadLine(); + if (f != null && f.HasValue()) + { + list.Add(f); + } + else + { + break; + } + } + + return list; + } + + private void CreateFileFromEntries(string dirName, string fileName, IEnumerable entries) + { + if (entries == null || !entries.Any()) + return; + + var sb = new StringBuilder(); + foreach (var f in entries) + { + sb.AppendLine(f); + } + + var content = sb.ToString().TrimEnd(); + if (content.HasValue()) + { + CacheFolder.CreateTextFile(CacheFolder.Combine(dirName, fileName), content); + } + } + + class NullCache : IAssetCache + { + public void Clear() + { + } + + public CachedAssetEntry GetAsset(string virtualPath) + { + return null; + } + + public CachedAssetEntry InsertAsset(string virtualPath, IEnumerable virtualPathDependencies, string content, params string[] processorCodes) + { + return null; + } + + public bool InvalidateAsset(string virtualPath) + { + return false; + } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/IAssetCache.cs b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/IAssetCache.cs index b0f888ef03..e8e4eee1dd 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/IAssetCache.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/IAssetCache.cs @@ -1,34 +1,30 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; namespace SmartStore.Web.Framework.Theming.Assets { - public class CachedAssetEntry - { - public string Content { get; set; } - public string OriginalVirtualPath { get; set; } - public string PhysicalPath { get; set; } - public IEnumerable VirtualPathDependencies { get; set; } - public string HashCode { get; set; } - public string ThemeName { get; set; } - public int StoreId { get; set; } - public string[] ProcessorCodes { get; set; } - } + public class CachedAssetEntry + { + public string Content { get; set; } + public string OriginalVirtualPath { get; set; } + public string PhysicalPath { get; set; } + public IEnumerable VirtualPathDependencies { get; set; } + public string HashCode { get; set; } + public string ThemeName { get; set; } + public int StoreId { get; set; } + public string[] ProcessorCodes { get; set; } + } - /// - /// A file system based caching mechanism for dynamically translated assets like Sass, Less etc. - /// - public interface IAssetCache - { - CachedAssetEntry GetAsset(string virtualPath); + /// + /// A file system based caching mechanism for dynamically translated assets like Sass, Less etc. + /// + public interface IAssetCache + { + CachedAssetEntry GetAsset(string virtualPath); - CachedAssetEntry InsertAsset(string virtualPath, IEnumerable virtualPathDependencies, string content, params string[] processorCodes); + CachedAssetEntry InsertAsset(string virtualPath, IEnumerable virtualPathDependencies, string content, params string[] processorCodes); - bool InvalidateAsset(string virtualPath); + bool InvalidateAsset(string virtualPath); - void Clear(); - } + void Clear(); + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/ModuleImportsVirtualFile.cs b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/ModuleImportsVirtualFile.cs index 34b4557fe5..3b357666e7 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/ModuleImportsVirtualFile.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/ModuleImportsVirtualFile.cs @@ -1,159 +1,150 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; -using System.Text; using System.Linq; +using System.Text; using System.Web.Hosting; -using SmartStore.Core.Plugins; using SmartStore.Core.Data; -using System; +using SmartStore.Core.Plugins; using SmartStore.Utilities; namespace SmartStore.Web.Framework.Theming.Assets { - public sealed class ModuleImport - { - public string VirtualPath { get; internal set; } - public string PhysicalPath { get; internal set; } - public PluginDescriptor PluginDescriptor { get; internal set; } - public bool IsAdmin { get; internal set; } - - public override string ToString() - { - return VirtualPath; - } - - public override int GetHashCode() - { - return VirtualPath.GetHashCode(); - } - - public override bool Equals(object obj) - { - if (!(obj is ModuleImport other)) - return false; - - return string.Equals(this.VirtualPath, other.VirtualPath, StringComparison.OrdinalIgnoreCase); - } - } - - public class ModuleImportsVirtualFile : VirtualFile, IFileDependencyProvider - { - private static readonly HashSet _adminImports; - private static readonly HashSet _publicImports; - - static ModuleImportsVirtualFile() - { - _adminImports = new HashSet(); - _publicImports = new HashSet(); - - if (DataSettings.DatabaseIsInstalled()) - { - CollectModuleImports(); - } - } - - private static void CollectModuleImports() - { - var installedPlugins = PluginManager.ReferencedPlugins.Where(x => x.Installed); - var root = PluginManager.PluginsLocation; - - foreach (var plugin in installedPlugins) - { - var contentDir = Path.Combine(plugin.PhysicalPath, "Content"); - if (!Directory.Exists(contentDir)) - continue; - - TryAddImport(plugin, _publicImports, "public.scss"); - TryAddImport(plugin, _adminImports, "admin.scss"); - } - - void TryAddImport(PluginDescriptor plugin, HashSet imports, string name) - { - var physicalPath = Path.Combine(plugin.PhysicalPath, "Content", name); - if (File.Exists(physicalPath)) - { - imports.Add(new ModuleImport - { - PhysicalPath = physicalPath, - VirtualPath = $"{root}/{plugin.FolderName}/Content/{name}", - PluginDescriptor = plugin, - IsAdmin = name.Contains("admin") - }); - } - } - } - - public static ModuleImport[] PublicImports - { - get => _publicImports.ToArray(); - } - - public static ModuleImport[] AdminImports - { - get => _adminImports.ToArray(); - } - - public ModuleImportsVirtualFile(string virtualPath, bool isAdmin) - : base(virtualPath) - { - IsAdmin = isAdmin; - } - - public override bool IsDirectory - { - get { return false; } - } - - public bool IsAdmin - { - get; - private set; - } - - public override Stream Open() - { - var sb = new StringBuilder(); - - var imports = IsAdmin ? _adminImports : _publicImports; - foreach (var imp in imports) - { - sb.AppendLine($"@import '{imp.VirtualPath}';"); - } - - return GenerateStreamFromString(sb.ToString()); - } - - public void HashCombine(HashCodeCombiner combiner) - { - Guard.NotNull(combiner, nameof(combiner)); - - var imports = IsAdmin ? _adminImports : _publicImports; - foreach (var imp in imports) - { - combiner.Add(new FileInfo(imp.PhysicalPath)); - } - } - - private Stream GenerateStreamFromString(string value) - { - var stream = new MemoryStream(); - - using (var writer = new StreamWriter(stream, Encoding.Unicode, 1024, true)) - { - writer.Write(value); - writer.Flush(); - stream.Seek(0, SeekOrigin.Begin); - return stream; - } - } - - public void AddFileDependencies(ICollection mappedPaths, ICollection cacheKeys) - { - var imports = IsAdmin ? _adminImports : _publicImports; - foreach (var imp in imports) - { - mappedPaths.Add(imp.PhysicalPath); - } - } - } + public sealed class ModuleImport + { + public string VirtualPath { get; internal set; } + public string PhysicalPath { get; internal set; } + public PluginDescriptor PluginDescriptor { get; internal set; } + public bool IsAdmin { get; internal set; } + + public override string ToString() + { + return VirtualPath; + } + + public override int GetHashCode() + { + return VirtualPath.GetHashCode(); + } + + public override bool Equals(object obj) + { + if (!(obj is ModuleImport other)) + return false; + + return string.Equals(this.VirtualPath, other.VirtualPath, StringComparison.OrdinalIgnoreCase); + } + } + + public class ModuleImportsVirtualFile : VirtualFile, IFileDependencyProvider + { + private static readonly HashSet _adminImports; + private static readonly HashSet _publicImports; + + static ModuleImportsVirtualFile() + { + _adminImports = new HashSet(); + _publicImports = new HashSet(); + + if (DataSettings.DatabaseIsInstalled()) + { + CollectModuleImports(); + } + } + + private static void CollectModuleImports() + { + var installedPlugins = PluginManager.ReferencedPlugins.Where(x => x.Installed); + var root = PluginManager.PluginsLocation; + + foreach (var plugin in installedPlugins) + { + var contentDir = Path.Combine(plugin.PhysicalPath, "Content"); + if (!Directory.Exists(contentDir)) + continue; + + TryAddImport(plugin, _publicImports, "public.scss"); + TryAddImport(plugin, _adminImports, "admin.scss"); + } + + void TryAddImport(PluginDescriptor plugin, HashSet imports, string name) + { + var physicalPath = Path.Combine(plugin.PhysicalPath, "Content", name); + if (File.Exists(physicalPath)) + { + imports.Add(new ModuleImport + { + PhysicalPath = physicalPath, + VirtualPath = $"{root}/{plugin.FolderName}/Content/{name}", + PluginDescriptor = plugin, + IsAdmin = name.Contains("admin") + }); + } + } + } + + public static ModuleImport[] PublicImports => _publicImports.ToArray(); + + public static ModuleImport[] AdminImports => _adminImports.ToArray(); + + public ModuleImportsVirtualFile(string virtualPath, bool isAdmin) + : base(virtualPath) + { + IsAdmin = isAdmin; + } + + public override bool IsDirectory => false; + + public bool IsAdmin + { + get; + private set; + } + + public override Stream Open() + { + var sb = new StringBuilder(); + + var imports = IsAdmin ? _adminImports : _publicImports; + foreach (var imp in imports) + { + sb.AppendLine($"@import '{imp.VirtualPath}';"); + } + + return GenerateStreamFromString(sb.ToString()); + } + + public void HashCombine(HashCodeCombiner combiner) + { + Guard.NotNull(combiner, nameof(combiner)); + + var imports = IsAdmin ? _adminImports : _publicImports; + foreach (var imp in imports) + { + combiner.Add(new FileInfo(imp.PhysicalPath)); + } + } + + private Stream GenerateStreamFromString(string value) + { + var stream = new MemoryStream(); + + using (var writer = new StreamWriter(stream, Encoding.Unicode, 1024, true)) + { + writer.Write(value); + writer.Flush(); + stream.Seek(0, SeekOrigin.Begin); + return stream; + } + } + + public void AddFileDependencies(ICollection mappedPaths, ICollection cacheKeys) + { + var imports = IsAdmin ? _adminImports : _publicImports; + foreach (var imp in imports) + { + mappedPaths.Add(imp.PhysicalPath); + } + } + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/SmartStyleBundle.cs b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/SmartStyleBundle.cs index d2930486a7..185c640531 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/Assets/SmartStyleBundle.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/Assets/SmartStyleBundle.cs @@ -18,8 +18,8 @@ public SmartStyleBundle(string virtualPath) : this(virtualPath, null) { } - [SuppressMessage("ReSharper", "VirtualMemberCallInContructor")] - public SmartStyleBundle(string virtualPath, string cdnPath) + [SuppressMessage("ReSharper", "VirtualMemberCallInContructor")] + public SmartStyleBundle(string virtualPath, string cdnPath) : base(virtualPath, cdnPath, new IBundleTransform[] { BundleTransformerContext.Current.Styles.GetDefaultTransformInstance() }) { Builder = new NullBuilder(); @@ -31,10 +31,10 @@ public override BundleResponse GenerateBundleResponse(BundleContext context) // to the Bundle.Files collection. This is bad as we allow switching // Optimization mode per UI. Switching from true to false would also include // ALL LESS/SASS imports in the generated output ('link' tags) - + // get all ORIGINAL bundle parts (including LESS/SASS parents, no @imports) var files = this.EnumerateFiles(context); - + // replace file pattern like {version} and let Bundler resolve min/debug extensions. files = context.BundleCollection.FileExtensionReplacementList.ReplaceFileExtensions(context, files); // save originals for later use @@ -53,7 +53,7 @@ public override BundleResponse GenerateBundleResponse(BundleContext context) return response; } - + public override void UpdateCache(BundleContext context, BundleResponse response) { if (_wasCached && _transformedBundleFiles != null && _transformedBundleFiles.Any()) @@ -89,7 +89,7 @@ where _originalBundleFilePathes.Any(x => virtualPath.EndsWith(x, StringCompariso private static bool CacheIsEnabled(BundleContext context) { - Guard.NotNull(context, nameof(context)); + Guard.NotNull(context, nameof(context)); return context.HttpContext?.Cache != null && !context.EnableInstrumentation; diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/CssHttpHandler.cs b/src/Presentation/SmartStore.Web.Framework/Theming/CssHttpHandler.cs index e2fd7aa151..d0d3900fde 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/CssHttpHandler.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/CssHttpHandler.cs @@ -15,39 +15,36 @@ namespace SmartStore.Web.Framework.Theming { - public class SassCssHttpHandler : CssHttpHandlerBase - { - protected override IAsset TranslateAssetCore(IAsset asset, ITransformer transformer, bool isDebugMode) - { - return InnerTranslateAsset("SassTranslator", asset, transformer, isDebugMode); - } - } - - public abstract class CssHttpHandlerBase : BundleTransformer.Core.HttpHandlers.StyleAssetHandlerBase - { - protected CssHttpHandlerBase() + public class SassCssHttpHandler : CssHttpHandlerBase + { + protected override IAsset TranslateAssetCore(IAsset asset, ITransformer transformer, bool isDebugMode) + { + return InnerTranslateAsset("SassTranslator", asset, transformer, isDebugMode); + } + } + + public abstract class CssHttpHandlerBase : BundleTransformer.Core.HttpHandlers.StyleAssetHandlerBase + { + protected CssHttpHandlerBase() : this(HttpContext.Current.Cache, - BundleTransformerContext.Current.FileSystem.GetVirtualFileSystemWrapper(), - BundleTransformerContext.Current.Configuration.GetCoreSettings().AssetHandler) + BundleTransformerContext.Current.FileSystem.GetVirtualFileSystemWrapper(), + BundleTransformerContext.Current.Configuration.GetCoreSettings().AssetHandler) { - } + } - protected CssHttpHandlerBase( + protected CssHttpHandlerBase( Cache cache, IVirtualFileSystemWrapper virtualFileSystemWrapper, AssetHandlerSettings assetHandlerConfig) : base(cache, virtualFileSystemWrapper, assetHandlerConfig) - { - } + { + } - protected override bool IsStaticAsset - { - get { return false; } - } + protected override bool IsStaticAsset => false; - private bool IsThemeableRequest() + private bool IsThemeableRequest() { - if (!DataSettings.DatabaseIsInstalled()) + if (!DataSettings.DatabaseIsInstalled()) { return false; } @@ -63,73 +60,73 @@ private bool IsThemeableRequest() } } - protected override string GetCacheKey(string assetVirtualPath, string bundleVirtualPath) - { - string cacheKey = base.GetCacheKey(assetVirtualPath, bundleVirtualPath); - - if (IsThemeableRequest()) - { - if (HttpContext.Current?.Request != null) - { - var qs = QueryString.Current; - if (qs.Count > 0) - { - // required for Theme editing validation: See Admin.Controllers.ThemeController.ValidateLess() - if (qs.Contains("theme")) - { - EngineContext.Current.Resolve().SetRequestTheme(qs["theme"]); - } - if (qs.Contains("storeId")) - { - EngineContext.Current.Resolve().SetRequestStore(qs["storeId"].ToInt()); - } - } - } - - cacheKey += "_" + EngineContext.Current.Resolve().CurrentTheme.ThemeName + "_" + EngineContext.Current.Resolve().CurrentStore.Id; - - if (ThemeHelper.IsStyleValidationRequest()) - { - cacheKey += "_Validation"; - } - } - - return cacheKey; - } - - protected override IAsset TranslateAsset(IAsset asset, ITransformer transformer, bool isDebugMode) - { - bool validationMode = ThemeHelper.IsStyleValidationRequest(); - - try - { - var processedAsset = TranslateAssetCore(asset, transformer, validationMode || isDebugMode); - - if (transformer == null && !validationMode) - { - // BundleTransformer does NOT PostProcess when transformer instance is null, - // therefore we handle it ourselves, because we desperately need - // UrlRewrite even in debug mode. - return AssetTranslorUtil.PostProcessAsset(processedAsset, isDebugMode); - } - else - { - return processedAsset; - } - } - catch (Exception ex) - { - if (validationMode) - { - _context.Response.Write(ex.Message); - _context.Response.StatusCode = 500; - _context.Response.End(); - } - - throw; - } - } - - protected abstract IAsset TranslateAssetCore(IAsset asset, ITransformer transformer, bool isDebugMode); - } + protected override string GetCacheKey(string assetVirtualPath, string bundleVirtualPath) + { + string cacheKey = base.GetCacheKey(assetVirtualPath, bundleVirtualPath); + + if (IsThemeableRequest()) + { + if (HttpContext.Current?.Request != null) + { + var qs = QueryString.Current; + if (qs.Count > 0) + { + // required for Theme editing validation: See Admin.Controllers.ThemeController.ValidateLess() + if (qs.Contains("theme")) + { + EngineContext.Current.Resolve().SetRequestTheme(qs["theme"]); + } + if (qs.Contains("storeId")) + { + EngineContext.Current.Resolve().SetRequestStore(qs["storeId"].ToInt()); + } + } + } + + cacheKey += "_" + EngineContext.Current.Resolve().CurrentTheme.ThemeName + "_" + EngineContext.Current.Resolve().CurrentStore.Id; + + if (ThemeHelper.IsStyleValidationRequest()) + { + cacheKey += "_Validation"; + } + } + + return cacheKey; + } + + protected override IAsset TranslateAsset(IAsset asset, ITransformer transformer, bool isDebugMode) + { + bool validationMode = ThemeHelper.IsStyleValidationRequest(); + + try + { + var processedAsset = TranslateAssetCore(asset, transformer, validationMode || isDebugMode); + + if (transformer == null && !validationMode) + { + // BundleTransformer does NOT PostProcess when transformer instance is null, + // therefore we handle it ourselves, because we desperately need + // UrlRewrite even in debug mode. + return AssetTranslorUtil.PostProcessAsset(processedAsset, isDebugMode); + } + else + { + return processedAsset; + } + } + catch (Exception ex) + { + if (validationMode) + { + _context.Response.Write(ex.Message); + _context.Response.StatusCode = 500; + _context.Response.End(); + } + + throw; + } + } + + protected abstract IAsset TranslateAssetCore(IAsset asset, ITransformer transformer, bool isDebugMode); + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/DefaultThemeFileResolver.cs b/src/Presentation/SmartStore.Web.Framework/Theming/DefaultThemeFileResolver.cs index dbbc5a5ab4..f40f05d73c 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/DefaultThemeFileResolver.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/DefaultThemeFileResolver.cs @@ -1,309 +1,299 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using System.Web; using System.Web.Hosting; -using System.Collections.Generic; -using System.Threading; -using SmartStore.Utilities.Threading; using SmartStore.Core; using SmartStore.Core.Infrastructure; using SmartStore.Core.Themes; +using SmartStore.Utilities.Threading; namespace SmartStore.Web.Framework.Theming { - public class DefaultThemeFileResolver : DisposableObject, IThemeFileResolver - { - private readonly Dictionary _files = new Dictionary(); - private readonly IThemeRegistry _themeRegistry; - private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); - - public DefaultThemeFileResolver(IThemeRegistry themeRegistry) - { - _themeRegistry = themeRegistry; - - // listen to file monitoring events - _themeRegistry.ThemeFolderDeleted += OnThemeFolderDeleted; - _themeRegistry.ThemeFolderRenamed += OnThemeFolderRenamed; - _themeRegistry.BaseThemeChanged += OnBaseThemeChanged; - _themeRegistry.ThemeFileChanged += OnThemeFileChanged; - } - - private void OnThemeFolderDeleted(object sender, ThemeFolderDeletedEventArgs e) - { - OnThemeRemoved(e.Name); - } - - private void OnThemeFolderRenamed(object sender, ThemeFolderRenamedEventArgs e) - { - OnThemeRemoved(e.OldName); - } - - private void OnBaseThemeChanged(object sender, BaseThemeChangedEventArgs e) - { - // We should be smarter than just clearing the whole cache here. BUT: - // Changing the base theme is a very rare case, whereas determining all dependant files is rather sophisticated. - // So, who cares ;-) - _files.Clear(); - } - - private void OnThemeRemoved(string themeName) - { - using (_rwLock.GetWriteLock()) - { - var keys = _files.Keys.Where(x => x.ThemeName.IsCaseInsensitiveEqual(themeName)).ToList(); - foreach (var key in keys) - { - if (key.ThemeName.IsCaseInsensitiveEqual(themeName) || _themeRegistry.IsChildThemeOf(key.ThemeName, themeName)) - { - // remove all cached pathes for this theme (also in all derived themes) - _files.Remove(key); - } - } - } - } - - private void OnThemeFileChanged(object sender, ThemeFileChangedEventArgs e) - { - if (e.IsConfigurationFile) - return; - - using (_rwLock.GetWriteLock()) - { - // get keys with same relative path - var keys = _files.Keys.Where(x => x.RelativePath.IsCaseInsensitiveEqual(e.RelativePath)).ToList(); - foreach (var key in keys) - { - if (key.ThemeName.IsCaseInsensitiveEqual(e.ThemeName) || _themeRegistry.IsChildThemeOf(key.ThemeName, e.ThemeName)) - { - // remove all cached pathes for this file/theme combination (also in all derived themes) - InheritedThemeFileResult result; - if (_files.TryGetValue(key, out result)) - { - _files.Remove(key); - if (e.ChangeType == ThemeFileChangeType.Created) - { - // The file is new: no chance that our VPP dependencies - // could have been notified about this (only deletions/changes are monitored). - // Therefore we brutally set the result file's last write time - // to enforece VPP to invalidate cache. - try - { - File.SetLastWriteTimeUtc(result.ResultPhysicalPath, DateTime.UtcNow); - } - catch { } - } - } - } - } - } - } - - protected override void OnDispose(bool disposing) - { - if (disposing) - { - _themeRegistry.ThemeFileChanged -= OnThemeFileChanged; - _themeRegistry.ThemeFolderDeleted -= OnThemeFolderDeleted; - _themeRegistry.BaseThemeChanged -= OnBaseThemeChanged; - _themeRegistry.ThemeFolderRenamed -= OnThemeFolderRenamed; - } - } - - /// - /// Tries to resolve a file up in the current theme's hierarchy chain. - /// - /// The original virtual path of the theme file - /// - /// If the current working themme is based on another theme AND the requested file - /// was physically found in the theme's hierarchy chain, an instance of will be returned. - /// In any other case the return value is null. - /// - public InheritedThemeFileResult Resolve(string virtualPath) - { - Guard.NotEmpty(virtualPath, nameof(virtualPath)); - - if (virtualPath[0] != '~') - { - virtualPath = VirtualPathUtility.ToAppRelative(virtualPath); - } - - if (!ThemeHelper.PathIsInheritableThemeFile(virtualPath)) - { - return null; - } - - bool isBased = false; - - virtualPath = ThemeHelper.TokenizePath(virtualPath, out var requestedThemeName, out var relativePath, out var query); - - Func nullOrFile = () => - { - return isBased - ? new InheritedThemeFileResult { IsBased = true, OriginalVirtualPath = virtualPath, Query = query } - : null; - }; - - ThemeManifest currentTheme = ResolveTheme(requestedThemeName, relativePath, query, out isBased); - - if (currentTheme?.BaseTheme == null) - { - // dont't bother resolving files: the current theme is not inherited. - // Let the current VPP do the work. - return nullOrFile(); - } - - if (!currentTheme.ThemeName.Equals(requestedThemeName, StringComparison.OrdinalIgnoreCase)) - { - if (!_themeRegistry.IsChildThemeOf(currentTheme.ThemeName, requestedThemeName)) - { - return nullOrFile(); - } - } - else if (isBased && currentTheme.BaseTheme != null) - { - // A file from the base theme has been requested - currentTheme = currentTheme.BaseTheme; - } - - var fileKey = new FileKey(currentTheme.ThemeName, relativePath, query); - InheritedThemeFileResult result; - - using (_rwLock.GetUpgradeableReadLock()) - { - if (!_files.TryGetValue(fileKey, out result)) - { - using (_rwLock.GetWriteLock()) - { - // ALWAYS begin the search with the current working theme's location! - string actualLocation = LocateFile(currentTheme.ThemeName, relativePath, out var resultVirtualPath, out var resultPhysicalPath); - - if (actualLocation != null) - { - result = new InheritedThemeFileResult - { - RelativePath = relativePath, - OriginalVirtualPath = virtualPath, - ResultVirtualPath = resultVirtualPath, - ResultPhysicalPath = resultPhysicalPath, - OriginalThemeName = requestedThemeName, - ResultThemeName = actualLocation, - IsBased = isBased, - Query = query - }; - } - - _files[fileKey] = result; - } - } - } - - if (result == null) - return nullOrFile(); - - return result; - } - - private ThemeManifest ResolveTheme(string requestedThemeName, string relativePath, string query, out bool isBased) - { - isBased = false; - ThemeManifest currentTheme; - - var isAdmin = EngineContext.Current.Resolve().IsAdmin; - if (isAdmin) - { - currentTheme = _themeRegistry.GetThemeManifest(requestedThemeName); - } - else - { - var styleResult = ThemeHelper.IsStyleSheet(relativePath); - var isPreprocessor = styleResult != null && styleResult.IsPreprocessor; - if (isPreprocessor) - { - // special consideration for SASS/LESS files: they can be validated - // in the backend. For validation, a "theme" query is appended - // to the url. During validation we must work with the actual - // requested theme instead of dynamically resolving the working theme. - var httpContext = HttpContext.Current; - if (httpContext != null && httpContext.Request != null) - { - var qs = httpContext.Request.QueryString; - if (qs["theme"].HasValue()) - { - EngineContext.Current.Resolve().SetRequestTheme(qs["theme"]); - } - } - } - - currentTheme = ThemeHelper.ResolveCurrentTheme(); - - if (isPreprocessor && query != null && query.StartsWith("base", StringComparison.OrdinalIgnoreCase)) - { - // special case to support SASS @import declarations - // within inherited SASS files. Snenario: an inheritor wishes to - // include the same file from it's base theme (e.g. custom.scss) just to tweak it - // a bit for his child theme. Without the 'base' query the resolution starting point - // for custom.scss would be the CURRENT theme's folder, and NOT the requested one's, - // which inevitably would result in a cyclic dependency. - currentTheme = _themeRegistry.GetThemeManifest(requestedThemeName); - isBased = true; - } - } - - return currentTheme; - } - - /// - /// Tries to locate the file - /// - /// the theme's name where the file is actually located - private string LocateFile(string themeName, string relativePath, out string virtualPath, out string physicalPath) - { - virtualPath = null; - physicalPath = null; - - virtualPath = VirtualPathUtility.Combine("{0}{1}/".FormatInvariant(ThemeHelper.ThemesBasePath, themeName), relativePath); - physicalPath = HostingEnvironment.MapPath(virtualPath); - if (File.Exists(physicalPath)) - { - return themeName; - } - - var manifest = _themeRegistry.GetThemeManifest(themeName); - if (manifest != null && manifest.BaseTheme != null) - { - var baseLocation = LocateFile(manifest.BaseTheme.ThemeName, relativePath, out virtualPath, out physicalPath); - return baseLocation; - } - - virtualPath = null; - physicalPath = null; - return null; - } - - - private class FileKey : Tuple - { - public FileKey(string themeName, string relativePath, string query) - : base(themeName.ToLower(), relativePath.ToLower(), query?.ToLower()) - { - } - - public string ThemeName - { - get { return base.Item1; } - } - - public string RelativePath - { - get { return base.Item2; } - } - - public string Query - { - get { return base.Item3; } - } - } - - } + public class DefaultThemeFileResolver : DisposableObject, IThemeFileResolver + { + private readonly Dictionary _files = new Dictionary(); + private readonly IThemeRegistry _themeRegistry; + private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); + + public DefaultThemeFileResolver(IThemeRegistry themeRegistry) + { + _themeRegistry = themeRegistry; + + // listen to file monitoring events + _themeRegistry.ThemeFolderDeleted += OnThemeFolderDeleted; + _themeRegistry.ThemeFolderRenamed += OnThemeFolderRenamed; + _themeRegistry.BaseThemeChanged += OnBaseThemeChanged; + _themeRegistry.ThemeFileChanged += OnThemeFileChanged; + } + + private void OnThemeFolderDeleted(object sender, ThemeFolderDeletedEventArgs e) + { + OnThemeRemoved(e.Name); + } + + private void OnThemeFolderRenamed(object sender, ThemeFolderRenamedEventArgs e) + { + OnThemeRemoved(e.OldName); + } + + private void OnBaseThemeChanged(object sender, BaseThemeChangedEventArgs e) + { + // We should be smarter than just clearing the whole cache here. BUT: + // Changing the base theme is a very rare case, whereas determining all dependant files is rather sophisticated. + // So, who cares ;-) + _files.Clear(); + } + + private void OnThemeRemoved(string themeName) + { + using (_rwLock.GetWriteLock()) + { + var keys = _files.Keys.Where(x => x.ThemeName.IsCaseInsensitiveEqual(themeName)).ToList(); + foreach (var key in keys) + { + if (key.ThemeName.IsCaseInsensitiveEqual(themeName) || _themeRegistry.IsChildThemeOf(key.ThemeName, themeName)) + { + // remove all cached pathes for this theme (also in all derived themes) + _files.Remove(key); + } + } + } + } + + private void OnThemeFileChanged(object sender, ThemeFileChangedEventArgs e) + { + if (e.IsConfigurationFile) + return; + + using (_rwLock.GetWriteLock()) + { + // get keys with same relative path + var keys = _files.Keys.Where(x => x.RelativePath.IsCaseInsensitiveEqual(e.RelativePath)).ToList(); + foreach (var key in keys) + { + if (key.ThemeName.IsCaseInsensitiveEqual(e.ThemeName) || _themeRegistry.IsChildThemeOf(key.ThemeName, e.ThemeName)) + { + // remove all cached pathes for this file/theme combination (also in all derived themes) + if (_files.TryGetValue(key, out var result)) + { + _files.Remove(key); + if (e.ChangeType == ThemeFileChangeType.Created) + { + // The file is new: no chance that our VPP dependencies + // could have been notified about this (only deletions/changes are monitored). + // Therefore we brutally set the result file's last write time + // to enforece VPP to invalidate cache. + try + { + File.SetLastWriteTimeUtc(result.ResultPhysicalPath, DateTime.UtcNow); + } + catch { } + } + } + } + } + } + } + + protected override void OnDispose(bool disposing) + { + if (disposing) + { + _themeRegistry.ThemeFileChanged -= OnThemeFileChanged; + _themeRegistry.ThemeFolderDeleted -= OnThemeFolderDeleted; + _themeRegistry.BaseThemeChanged -= OnBaseThemeChanged; + _themeRegistry.ThemeFolderRenamed -= OnThemeFolderRenamed; + } + } + + /// + /// Tries to resolve a file up in the current theme's hierarchy chain. + /// + /// The original virtual path of the theme file + /// + /// If the current working themme is based on another theme AND the requested file + /// was physically found in the theme's hierarchy chain, an instance of will be returned. + /// In any other case the return value is null. + /// + public InheritedThemeFileResult Resolve(string virtualPath) + { + Guard.NotEmpty(virtualPath, nameof(virtualPath)); + + if (virtualPath[0] != '~') + { + virtualPath = VirtualPathUtility.ToAppRelative(virtualPath); + } + + if (!ThemeHelper.PathIsInheritableThemeFile(virtualPath)) + { + return null; + } + + bool isBased = false; + + virtualPath = ThemeHelper.TokenizePath(virtualPath, out var requestedThemeName, out var relativePath, out var query); + + Func nullOrFile = () => + { + return isBased + ? new InheritedThemeFileResult { IsBased = true, OriginalVirtualPath = virtualPath, Query = query } + : null; + }; + + ThemeManifest currentTheme = ResolveTheme(requestedThemeName, relativePath, query, out isBased); + + if (currentTheme?.BaseTheme == null) + { + // dont't bother resolving files: the current theme is not inherited. + // Let the current VPP do the work. + return nullOrFile(); + } + + if (!currentTheme.ThemeName.Equals(requestedThemeName, StringComparison.OrdinalIgnoreCase)) + { + if (!_themeRegistry.IsChildThemeOf(currentTheme.ThemeName, requestedThemeName)) + { + return nullOrFile(); + } + } + else if (isBased && currentTheme.BaseTheme != null) + { + // A file from the base theme has been requested + currentTheme = currentTheme.BaseTheme; + } + + var fileKey = new FileKey(currentTheme.ThemeName, relativePath, query); + InheritedThemeFileResult result; + + using (_rwLock.GetUpgradeableReadLock()) + { + if (!_files.TryGetValue(fileKey, out result)) + { + using (_rwLock.GetWriteLock()) + { + // ALWAYS begin the search with the current working theme's location! + string actualLocation = LocateFile(currentTheme.ThemeName, relativePath, out var resultVirtualPath, out var resultPhysicalPath); + + if (actualLocation != null) + { + result = new InheritedThemeFileResult + { + RelativePath = relativePath, + OriginalVirtualPath = virtualPath, + ResultVirtualPath = resultVirtualPath, + ResultPhysicalPath = resultPhysicalPath, + OriginalThemeName = requestedThemeName, + ResultThemeName = actualLocation, + IsBased = isBased, + Query = query + }; + } + + _files[fileKey] = result; + } + } + } + + if (result == null) + return nullOrFile(); + + return result; + } + + private ThemeManifest ResolveTheme(string requestedThemeName, string relativePath, string query, out bool isBased) + { + isBased = false; + ThemeManifest currentTheme; + + var isAdmin = EngineContext.Current.Resolve().IsAdmin; + if (isAdmin) + { + currentTheme = _themeRegistry.GetThemeManifest(requestedThemeName); + } + else + { + var styleResult = ThemeHelper.IsStyleSheet(relativePath); + var isPreprocessor = styleResult != null && styleResult.IsPreprocessor; + if (isPreprocessor) + { + // special consideration for SASS/LESS files: they can be validated + // in the backend. For validation, a "theme" query is appended + // to the url. During validation we must work with the actual + // requested theme instead of dynamically resolving the working theme. + var httpContext = HttpContext.Current; + if (httpContext != null && httpContext.Request != null) + { + var qs = httpContext.Request.QueryString; + if (qs["theme"].HasValue()) + { + EngineContext.Current.Resolve().SetRequestTheme(qs["theme"]); + } + } + } + + currentTheme = ThemeHelper.ResolveCurrentTheme(); + + if (isPreprocessor && query != null && query.StartsWith("base", StringComparison.OrdinalIgnoreCase)) + { + // special case to support SASS @import declarations + // within inherited SASS files. Snenario: an inheritor wishes to + // include the same file from it's base theme (e.g. custom.scss) just to tweak it + // a bit for his child theme. Without the 'base' query the resolution starting point + // for custom.scss would be the CURRENT theme's folder, and NOT the requested one's, + // which inevitably would result in a cyclic dependency. + currentTheme = _themeRegistry.GetThemeManifest(requestedThemeName); + isBased = true; + } + } + + return currentTheme; + } + + /// + /// Tries to locate the file + /// + /// the theme's name where the file is actually located + private string LocateFile(string themeName, string relativePath, out string virtualPath, out string physicalPath) + { + virtualPath = null; + physicalPath = null; + + virtualPath = VirtualPathUtility.Combine("{0}{1}/".FormatInvariant(ThemeHelper.ThemesBasePath, themeName), relativePath); + physicalPath = HostingEnvironment.MapPath(virtualPath); + if (File.Exists(physicalPath)) + { + return themeName; + } + + var manifest = _themeRegistry.GetThemeManifest(themeName); + if (manifest != null && manifest.BaseTheme != null) + { + var baseLocation = LocateFile(manifest.BaseTheme.ThemeName, relativePath, out virtualPath, out physicalPath); + return baseLocation; + } + + virtualPath = null; + physicalPath = null; + return null; + } + + + private class FileKey : Tuple + { + public FileKey(string themeName, string relativePath, string query) + : base(themeName.ToLower(), relativePath.ToLower(), query?.ToLower()) + { + } + + public string ThemeName => base.Item1; + + public string RelativePath => base.Item2; + + public string Query => base.Item3; + } + + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/IFileDependencyProvider.cs b/src/Presentation/SmartStore.Web.Framework/Theming/IFileDependencyProvider.cs index e36f9c9fd4..eaf0c9b6b1 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/IFileDependencyProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/IFileDependencyProvider.cs @@ -1,10 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace SmartStore.Web.Framework.Theming { - internal interface IFileDependencyProvider - { - void AddFileDependencies(ICollection mappedPaths, ICollection cacheKeys); - } + internal interface IFileDependencyProvider + { + void AddFileDependencies(ICollection mappedPaths, ICollection cacheKeys); + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/InheritedVirtualThemeFile.cs b/src/Presentation/SmartStore.Web.Framework/Theming/InheritedVirtualThemeFile.cs index ad49bed1d2..a0c19b1495 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/InheritedVirtualThemeFile.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/InheritedVirtualThemeFile.cs @@ -8,41 +8,38 @@ namespace SmartStore.Web.Framework.Theming { internal class InheritedVirtualThemeFile : VirtualFile, IFileDependencyProvider { - public InheritedVirtualThemeFile(InheritedThemeFileResult resolveResult) - : base(DetermineVirtualPath(resolveResult)) + public InheritedVirtualThemeFile(InheritedThemeFileResult resolveResult) + : base(DetermineVirtualPath(resolveResult)) { ResolveResult = resolveResult; } - public InheritedThemeFileResult ResolveResult { get; } + public InheritedThemeFileResult ResolveResult { get; } - public string ResultVirtualPath - { - get { return ResolveResult.ResultVirtualPath ?? ResolveResult.OriginalVirtualPath; } - } + public string ResultVirtualPath => ResolveResult.ResultVirtualPath ?? ResolveResult.OriginalVirtualPath; - public override Stream Open() + public override Stream Open() { - return new FileStream(ResolveResult.ResultPhysicalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + return new FileStream(ResolveResult.ResultPhysicalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); } - private static string DetermineVirtualPath(InheritedThemeFileResult resolveResult) - { - if (resolveResult.RelativePath.EndsWith(".cshtml", StringComparison.OrdinalIgnoreCase)) - { - // ASP.NET BuildManager requires the original path for razor views, - // otherwise an exception is thrown - return resolveResult.OriginalVirtualPath; - } - else - { - return resolveResult.ResultVirtualPath; - } - } + private static string DetermineVirtualPath(InheritedThemeFileResult resolveResult) + { + if (resolveResult.RelativePath.EndsWith(".cshtml", StringComparison.OrdinalIgnoreCase)) + { + // ASP.NET BuildManager requires the original path for razor views, + // otherwise an exception is thrown + return resolveResult.OriginalVirtualPath; + } + else + { + return resolveResult.ResultVirtualPath; + } + } - public void AddFileDependencies(ICollection mappedPaths, ICollection cacheKeys) - { - mappedPaths.Add(ResolveResult.ResultPhysicalPath); - } - } + public void AddFileDependencies(ICollection mappedPaths, ICollection cacheKeys) + { + mappedPaths.Add(ResolveResult.ResultPhysicalPath); + } + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/LocalizedDisplayMode.cs b/src/Presentation/SmartStore.Web.Framework/Theming/LocalizedDisplayMode.cs index 860d0560a2..58f46a29c1 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/LocalizedDisplayMode.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/LocalizedDisplayMode.cs @@ -6,94 +6,88 @@ namespace SmartStore.Web.Framework.Theming { - public class LocalizedDisplayMode : DefaultDisplayMode - { - private readonly bool _enabled; - - public LocalizedDisplayMode(bool enabled) - : this(DisplayModeProvider.DefaultDisplayModeId, enabled) - { - } - - public LocalizedDisplayMode(string suffix, bool enables) - : base(suffix) - { - _enabled = enables; - } - - public override DisplayInfo GetDisplayInfo(HttpContextBase httpContext, string virtualPath, Func virtualPathExists) - { - if (!_enabled) - { - // default behaviour, because localized views are not enabled - return base.GetDisplayInfo(httpContext, virtualPath, virtualPathExists); - } - - var lang = GetCurrentLanguageSeoCode(); - - var result = - GetDisplayInfoInternal(httpContext, virtualPath, lang, virtualPathExists) ?? - GetDisplayInfoInternal(httpContext, virtualPath, null, virtualPathExists); - - return result; - } - - private DisplayInfo GetDisplayInfoInternal(HttpContextBase httpContext, string virtualPath, string lang, Func virtualPathExists) - { - string path = this.TransformPath(virtualPath, "{0}{1}".FormatInvariant(base.DisplayModeId, lang.IsEmpty() ? "" : "." + lang)); - if (path != null && virtualPathExists(path)) - { - return new LocalizedDisplayInfo(path, this, lang); - } - - return null; - } - - public override string DisplayModeId - { - get - { - if (!_enabled) - { - return base.DisplayModeId; - } - - var lang = GetCurrentLanguageSeoCode(); - return "{0}{1}".FormatInvariant(base.DisplayModeId, lang.IsEmpty() ? "" : "." + lang); - } - } - - protected string GetCurrentLanguageSeoCode() - { - string result = null; - - var workContext = EngineContext.Current.Resolve(); - if (workContext != null && workContext.WorkingLanguage != null) - { - result = workContext.WorkingLanguage.UniqueSeoCode; - } - - return result.NullEmpty() ?? "en"; - } - } - - public class LocalizedDisplayInfo : DisplayInfo - { - public LocalizedDisplayInfo(string filePath, IDisplayMode displayMode, string lang) - : base(filePath, displayMode) - { - this.Lang = lang; - } - - public string Lang { get; set; } - - public string DisplayModeId - { - get - { - return "{0}{1}".FormatInvariant(DisplayMode.DisplayModeId, Lang.IsEmpty() ? "" : "." + Lang); - } - } - } + public class LocalizedDisplayMode : DefaultDisplayMode + { + private readonly bool _enabled; + + public LocalizedDisplayMode(bool enabled) + : this(DisplayModeProvider.DefaultDisplayModeId, enabled) + { + } + + public LocalizedDisplayMode(string suffix, bool enables) + : base(suffix) + { + _enabled = enables; + } + + public override DisplayInfo GetDisplayInfo(HttpContextBase httpContext, string virtualPath, Func virtualPathExists) + { + if (!_enabled) + { + // default behaviour, because localized views are not enabled + return base.GetDisplayInfo(httpContext, virtualPath, virtualPathExists); + } + + var lang = GetCurrentLanguageSeoCode(); + + var result = + GetDisplayInfoInternal(httpContext, virtualPath, lang, virtualPathExists) ?? + GetDisplayInfoInternal(httpContext, virtualPath, null, virtualPathExists); + + return result; + } + + private DisplayInfo GetDisplayInfoInternal(HttpContextBase httpContext, string virtualPath, string lang, Func virtualPathExists) + { + string path = this.TransformPath(virtualPath, "{0}{1}".FormatInvariant(base.DisplayModeId, lang.IsEmpty() ? "" : "." + lang)); + if (path != null && virtualPathExists(path)) + { + return new LocalizedDisplayInfo(path, this, lang); + } + + return null; + } + + public override string DisplayModeId + { + get + { + if (!_enabled) + { + return base.DisplayModeId; + } + + var lang = GetCurrentLanguageSeoCode(); + return "{0}{1}".FormatInvariant(base.DisplayModeId, lang.IsEmpty() ? "" : "." + lang); + } + } + + protected string GetCurrentLanguageSeoCode() + { + string result = null; + + var workContext = EngineContext.Current.Resolve(); + if (workContext != null && workContext.WorkingLanguage != null) + { + result = workContext.WorkingLanguage.UniqueSeoCode; + } + + return result.NullEmpty() ?? "en"; + } + } + + public class LocalizedDisplayInfo : DisplayInfo + { + public LocalizedDisplayInfo(string filePath, IDisplayMode displayMode, string lang) + : base(filePath, displayMode) + { + this.Lang = lang; + } + + public string Lang { get; set; } + + public string DisplayModeId => "{0}{1}".FormatInvariant(DisplayMode.DisplayModeId, Lang.IsEmpty() ? "" : "." + Lang); + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/SmartVirtualPathProvider.cs b/src/Presentation/SmartStore.Web.Framework/Theming/SmartVirtualPathProvider.cs index 32c6d31fe8..f05bcca22a 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/SmartVirtualPathProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/SmartVirtualPathProvider.cs @@ -1,175 +1,172 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading; using System.Web; using System.Web.Hosting; -using System.Threading; using SmartStore.Core.Infrastructure; -using SmartStore.Utilities.Threading; -using SmartStore.Utilities; using SmartStore.Core.Themes; +using SmartStore.Utilities; +using SmartStore.Utilities.Threading; namespace SmartStore.Web.Framework.Theming { - public abstract class SmartVirtualPathProvider : VirtualPathProvider - { - private readonly IThemeRegistry _themeRegistry; - private readonly Dictionary _cachedDebugFilePaths = new Dictionary(); - private readonly ContextState> _requestState = new ContextState>("PluginDebugViewVPP.RequestCache", () => new Dictionary()); - private readonly DirectoryInfo _pluginsDebugDir; - private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); - - protected static readonly bool _isDebug; - - static SmartVirtualPathProvider() - { - _isDebug = HttpContext.Current.IsDebuggingEnabled && CommonHelper.IsDevEnvironment; - } - - protected SmartVirtualPathProvider() - { - var appRootPath = HostingEnvironment.MapPath("~/").EnsureEndsWith("\\"); - - var pluginsDebugPath = Path.GetFullPath(Path.Combine(appRootPath, @"..\..\Plugins")); - if (Directory.Exists(pluginsDebugPath)) - { - _pluginsDebugDir = new DirectoryInfo(pluginsDebugPath); - } - - _themeRegistry = EngineContext.Current.Resolve(); - } - - protected internal string ResolveDebugFilePath(string virtualPath) - { - if (!_isDebug) - return null; - - // Two-Level caching: RequestCache > AppCache - var d = _requestState.GetState(); - - if (!d.TryGetValue(virtualPath, out var debugPath)) - { - if (!IsExtensionPath(virtualPath, out var root, out var appRelativePath)) - { - // don't query again in this request - d[virtualPath] = null; - return null; - } - - if (!d.TryGetValue(appRelativePath, out debugPath)) - { - // (perf) concurrency with ReaderWriterLockSlim seems way faster than ConcurrentDictionary - using (_rwLock.GetUpgradeableReadLock()) - { - if (!_cachedDebugFilePaths.TryGetValue(appRelativePath, out debugPath)) - { - using (_rwLock.GetWriteLock()) - { - debugPath = FindDebugFile(appRelativePath, root); - _cachedDebugFilePaths[appRelativePath] = d[appRelativePath] = debugPath; - } - } - } - } - } - - return debugPath; - } - - private string FindDebugFile(string appRelativePath, string root) - { - if (_pluginsDebugDir == null) - return null; - - // strip "~/Plugins/" or "~/Themes/" - var unrooted = appRelativePath.Substring(root.Length); - - // either plugin or theme name - var extensionName = unrooted.Substring(0, unrooted.IndexOf('/')); - - // get "Views/Something/View.cshtml" - var relativePath = unrooted.Substring(extensionName.Length + 1); - - if (root == "~/Themes/") - { - var theme = _themeRegistry.GetThemeManifest(extensionName); - if (theme != null && theme.IsSymbolicLink) - { - // Linked theme folders cannot compute cache dependencies correctly when - // working with source paths. We must determine the link target path, - var finalPath = Path.Combine(theme.Path, relativePath.Replace('/', '\\')); - return File.Exists(finalPath) ? finalPath : null; - } - } - else - { - // Root is "~/Plugin/" - var foldersToCheck = new[] { extensionName, extensionName + "-sym" }; - - foreach (var folder in foldersToCheck) - { - var pluginDir = new DirectoryInfo(Path.Combine(_pluginsDebugDir.FullName, folder)); - if (pluginDir != null && pluginDir.Exists) - { - var result = Path.Combine(pluginDir.FullName, relativePath).Replace("/", "\\"); - return File.Exists(result) ? result : null; - } - } - } - - return null; - } - - private bool IsExtensionPath(string virtualPath, out string root, out string appRelativePath) - { - root = null; - appRelativePath = virtualPath; - - if (virtualPath != null && virtualPath.Length > 0 && virtualPath[0] != '~') - { - appRelativePath = VirtualPathUtility.ToAppRelative(virtualPath); - } - - if (appRelativePath.StartsWith("~/Plugins/", StringComparison.InvariantCultureIgnoreCase)) - { - root = "~/Plugins/"; - return true; - } - - if (appRelativePath.StartsWith("~/Themes/", StringComparison.InvariantCultureIgnoreCase)) - { - root = "~/Themes/"; - return true; - } - - return false; - } - } - - internal class DebugVirtualFile : VirtualFile, IFileDependencyProvider - { - public DebugVirtualFile(string virtualPath, string debugPath) - : base(virtualPath) - { - this.PhysicalPath = debugPath; - } - - public string PhysicalPath { get; } - - public override bool IsDirectory - { - get { return false; } - } - - public override Stream Open() - { - var fileStream = new FileStream(PhysicalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - return fileStream; - } - - public void AddFileDependencies(ICollection mappedPaths, ICollection cacheKeys) - { - mappedPaths.Add(PhysicalPath); - } - } + public abstract class SmartVirtualPathProvider : VirtualPathProvider + { + private readonly IThemeRegistry _themeRegistry; + private readonly Dictionary _cachedDebugFilePaths = new Dictionary(); + private readonly ContextState> _requestState = new ContextState>("PluginDebugViewVPP.RequestCache", () => new Dictionary()); + private readonly DirectoryInfo _pluginsDebugDir; + private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); + + protected static readonly bool _isDebug; + + static SmartVirtualPathProvider() + { + _isDebug = HttpContext.Current.IsDebuggingEnabled && CommonHelper.IsDevEnvironment; + } + + protected SmartVirtualPathProvider() + { + var appRootPath = HostingEnvironment.MapPath("~/").EnsureEndsWith("\\"); + + var pluginsDebugPath = Path.GetFullPath(Path.Combine(appRootPath, @"..\..\Plugins")); + if (Directory.Exists(pluginsDebugPath)) + { + _pluginsDebugDir = new DirectoryInfo(pluginsDebugPath); + } + + _themeRegistry = EngineContext.Current.Resolve(); + } + + protected internal string ResolveDebugFilePath(string virtualPath) + { + if (!_isDebug) + return null; + + // Two-Level caching: RequestCache > AppCache + var d = _requestState.GetState(); + + if (!d.TryGetValue(virtualPath, out var debugPath)) + { + if (!IsExtensionPath(virtualPath, out var root, out var appRelativePath)) + { + // don't query again in this request + d[virtualPath] = null; + return null; + } + + if (!d.TryGetValue(appRelativePath, out debugPath)) + { + // (perf) concurrency with ReaderWriterLockSlim seems way faster than ConcurrentDictionary + using (_rwLock.GetUpgradeableReadLock()) + { + if (!_cachedDebugFilePaths.TryGetValue(appRelativePath, out debugPath)) + { + using (_rwLock.GetWriteLock()) + { + debugPath = FindDebugFile(appRelativePath, root); + _cachedDebugFilePaths[appRelativePath] = d[appRelativePath] = debugPath; + } + } + } + } + } + + return debugPath; + } + + private string FindDebugFile(string appRelativePath, string root) + { + if (_pluginsDebugDir == null) + return null; + + // strip "~/Plugins/" or "~/Themes/" + var unrooted = appRelativePath.Substring(root.Length); + + // either plugin or theme name + var extensionName = unrooted.Substring(0, unrooted.IndexOf('/')); + + // get "Views/Something/View.cshtml" + var relativePath = unrooted.Substring(extensionName.Length + 1); + + if (root == "~/Themes/") + { + var theme = _themeRegistry.GetThemeManifest(extensionName); + if (theme != null && theme.IsSymbolicLink) + { + // Linked theme folders cannot compute cache dependencies correctly when + // working with source paths. We must determine the link target path, + var finalPath = Path.Combine(theme.Path, relativePath.Replace('/', '\\')); + return File.Exists(finalPath) ? finalPath : null; + } + } + else + { + // Root is "~/Plugin/" + var foldersToCheck = new[] { extensionName, extensionName + "-sym" }; + + foreach (var folder in foldersToCheck) + { + var pluginDir = new DirectoryInfo(Path.Combine(_pluginsDebugDir.FullName, folder)); + if (pluginDir != null && pluginDir.Exists) + { + var result = Path.Combine(pluginDir.FullName, relativePath).Replace("/", "\\"); + return File.Exists(result) ? result : null; + } + } + } + + return null; + } + + private bool IsExtensionPath(string virtualPath, out string root, out string appRelativePath) + { + root = null; + appRelativePath = virtualPath; + + if (virtualPath != null && virtualPath.Length > 0 && virtualPath[0] != '~') + { + appRelativePath = VirtualPathUtility.ToAppRelative(virtualPath); + } + + if (appRelativePath.StartsWith("~/Plugins/", StringComparison.InvariantCultureIgnoreCase)) + { + root = "~/Plugins/"; + return true; + } + + if (appRelativePath.StartsWith("~/Themes/", StringComparison.InvariantCultureIgnoreCase)) + { + root = "~/Themes/"; + return true; + } + + return false; + } + } + + internal class DebugVirtualFile : VirtualFile, IFileDependencyProvider + { + public DebugVirtualFile(string virtualPath, string debugPath) + : base(virtualPath) + { + this.PhysicalPath = debugPath; + } + + public string PhysicalPath { get; } + + public override bool IsDirectory => false; + + public override Stream Open() + { + var fileStream = new FileStream(PhysicalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + return fileStream; + } + + public void AddFileDependencies(ICollection mappedPaths, ICollection cacheKeys) + { + mappedPaths.Add(PhysicalPath); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeContext.cs b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeContext.cs index ce5b268dbf..3349b0df00 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeContext.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeContext.cs @@ -10,15 +10,15 @@ namespace SmartStore.Web.Framework.Theming { public partial class ThemeContext : IThemeContext { - internal const string OverriddenThemeNameKey = "OverriddenThemeName"; - - private readonly IWorkContext _workContext; - private readonly IStoreContext _storeContext; + internal const string OverriddenThemeNameKey = "OverriddenThemeName"; + + private readonly IWorkContext _workContext; + private readonly IStoreContext _storeContext; private readonly IGenericAttributeService _genericAttributeService; private readonly ThemeSettings _themeSettings; private readonly IThemeRegistry _themeRegistry; private readonly IMobileDeviceHelper _mobileDeviceHelper; - private readonly HttpContextBase _httpContext; + private readonly HttpContextBase _httpContext; private bool _themeIsCached; private string _cachedThemeName; @@ -27,20 +27,20 @@ public partial class ThemeContext : IThemeContext public ThemeContext( IWorkContext workContext, - IStoreContext storeContext, + IStoreContext storeContext, IGenericAttributeService genericAttributeService, - ThemeSettings themeSettings, + ThemeSettings themeSettings, IThemeRegistry themeRegistry, IMobileDeviceHelper mobileDeviceHelper, - HttpContextBase httpContext) + HttpContextBase httpContext) { this._workContext = workContext; - this._storeContext = storeContext; + this._storeContext = storeContext; this._genericAttributeService = genericAttributeService; this._themeSettings = themeSettings; this._themeRegistry = themeRegistry; this._mobileDeviceHelper = mobileDeviceHelper; - this._httpContext = httpContext; + this._httpContext = httpContext; } public string WorkingThemeName @@ -52,24 +52,24 @@ public string WorkingThemeName return _cachedThemeName; } - var customer = _workContext.CurrentCustomer; + var customer = _workContext.CurrentCustomer; bool isUserSpecific = false; string theme = ""; if (_themeSettings.AllowCustomerToSelectTheme) { - if (_themeSettings.SaveThemeChoiceInCookie) - { - theme = _httpContext.GetUserThemeChoiceFromCookie(); - } - else - { - if (customer != null) - { - theme = customer.GetAttribute(SystemCustomerAttributeNames.WorkingThemeName, _genericAttributeService, _storeContext.CurrentStore.Id); - } - } - - isUserSpecific = theme.HasValue(); + if (_themeSettings.SaveThemeChoiceInCookie) + { + theme = _httpContext.GetUserThemeChoiceFromCookie(); + } + else + { + if (customer != null) + { + theme = customer.GetAttribute(SystemCustomerAttributeNames.WorkingThemeName, _genericAttributeService, _storeContext.CurrentStore.Id); + } + } + + isUserSpecific = theme.HasValue(); } // default store theme @@ -82,23 +82,23 @@ public string WorkingThemeName if (!_themeRegistry.ThemeManifestExists(theme)) { var manifest = _themeRegistry.GetThemeManifests().FirstOrDefault(); - if (manifest == null) - { - // no active theme in system. Throw! - throw Error.Application("At least one theme must be in active state, but the theme registry does not contain a valid theme package."); - } - theme = manifest.ThemeName; + if (manifest == null) + { + // no active theme in system. Throw! + throw Error.Application("At least one theme must be in active state, but the theme registry does not contain a valid theme package."); + } + theme = manifest.ThemeName; if (isUserSpecific) { - // the customer chosen theme does not exists (anymore). Invalidate it! - _httpContext.SetUserThemeChoiceInCookie(null); - if (customer != null) - { - _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.WorkingThemeName, string.Empty, _storeContext.CurrentStore.Id); - } + // the customer chosen theme does not exists (anymore). Invalidate it! + _httpContext.SetUserThemeChoiceInCookie(null); + if (customer != null) + { + _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.WorkingThemeName, string.Empty, _storeContext.CurrentStore.Id); + } } } - + // cache theme this._cachedThemeName = theme; this._themeIsCached = true; @@ -109,96 +109,96 @@ public string WorkingThemeName if (!_themeSettings.AllowCustomerToSelectTheme) return; - if (value.HasValue() && !_themeRegistry.ThemeManifestExists(value)) - return; + if (value.HasValue() && !_themeRegistry.ThemeManifestExists(value)) + return; - _httpContext.SetUserThemeChoiceInCookie(value.NullEmpty()); + _httpContext.SetUserThemeChoiceInCookie(value.NullEmpty()); - if (_workContext.CurrentCustomer != null) - { - _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.WorkingThemeName, value.EmptyNull(), _storeContext.CurrentStore.Id); - } + if (_workContext.CurrentCustomer != null) + { + _genericAttributeService.SaveAttribute(_workContext.CurrentCustomer, SystemCustomerAttributeNames.WorkingThemeName, value.EmptyNull(), _storeContext.CurrentStore.Id); + } // clear cache this._themeIsCached = false; } } - public void SetRequestTheme(string theme) - { - try - { - var dataTokens = _httpContext.Request.RequestContext.RouteData.DataTokens; - if (theme.HasValue()) - { - dataTokens[OverriddenThemeNameKey] = theme; - } - else if (dataTokens.ContainsKey(OverriddenThemeNameKey)) - { - dataTokens.Remove(OverriddenThemeNameKey); - } - - _currentTheme = null; - } - catch { } - } - - public string GetRequestTheme() - { - try - { - return (string)_httpContext.Request?.RequestContext?.RouteData?.DataTokens[OverriddenThemeNameKey]; - } - catch - { - return null; - } - } - - public void SetPreviewTheme(string theme) - { - try - { - _httpContext.SetPreviewModeValue(OverriddenThemeNameKey, theme); - _currentTheme = null; - } - catch { } - } - - public string GetPreviewTheme() - { - try - { - var cookie = _httpContext.GetPreviewModeCookie(false); - if (cookie != null) - { - return cookie.Values[OverriddenThemeNameKey]; - } - - return null; - } - catch - { - return null; - } - } + public void SetRequestTheme(string theme) + { + try + { + var dataTokens = _httpContext.Request.RequestContext.RouteData.DataTokens; + if (theme.HasValue()) + { + dataTokens[OverriddenThemeNameKey] = theme; + } + else if (dataTokens.ContainsKey(OverriddenThemeNameKey)) + { + dataTokens.Remove(OverriddenThemeNameKey); + } + + _currentTheme = null; + } + catch { } + } + + public string GetRequestTheme() + { + try + { + return (string)_httpContext.Request?.RequestContext?.RouteData?.DataTokens[OverriddenThemeNameKey]; + } + catch + { + return null; + } + } + + public void SetPreviewTheme(string theme) + { + try + { + _httpContext.SetPreviewModeValue(OverriddenThemeNameKey, theme); + _currentTheme = null; + } + catch { } + } + + public string GetPreviewTheme() + { + try + { + var cookie = _httpContext.GetPreviewModeCookie(false); + if (cookie != null) + { + return cookie.Values[OverriddenThemeNameKey]; + } + + return null; + } + catch + { + return null; + } + } public ThemeManifest CurrentTheme { get { - if (_currentTheme == null) + if (_currentTheme == null) { - var themeOverride = GetRequestTheme() ?? GetPreviewTheme(); - if (themeOverride != null) - { - // the theme to be used can be overwritten on request/session basis (e.g. for live preview, editing etc.) - _currentTheme = _themeRegistry.GetThemeManifest(themeOverride); - } - else - { - _currentTheme = _themeRegistry.GetThemeManifest(this.WorkingThemeName); - } + var themeOverride = GetRequestTheme() ?? GetPreviewTheme(); + if (themeOverride != null) + { + // the theme to be used can be overwritten on request/session basis (e.g. for live preview, editing etc.) + _currentTheme = _themeRegistry.GetThemeManifest(themeOverride); + } + else + { + _currentTheme = _themeRegistry.GetThemeManifest(this.WorkingThemeName); + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeHelper.cs b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeHelper.cs index 074b0e48cb..bb16495453 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeHelper.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeHelper.cs @@ -15,215 +15,206 @@ namespace SmartStore.Web.Framework.Theming { internal static class ThemeHelper { - private static readonly Regex s_inheritableThemeFilePattern; - private static readonly Regex s_themeVarsPattern; - private static readonly Regex s_moduleImportsPattern; - private static readonly Regex s_extensionlessPathPattern; - - public static readonly string ThemesBasePath; - - static ThemeHelper() - { - ThemesBasePath = CommonHelper.GetAppSetting("sm:ThemesBasePath", "~/Themes/").EnsureEndsWith("/"); - - var pattern = @"^{0}(.*)/(.+)(\.)(png|gif|jpg|jpeg|css|scss|js|cshtml|svg|json|liquid)(\?base)*$".FormatInvariant(ThemesBasePath); - s_inheritableThemeFilePattern = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); - s_themeVarsPattern = new Regex(@"\.(db|app)/[_]?themevars(.scss)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); - s_moduleImportsPattern = new Regex(@"\.app/[_]?moduleimports.scss", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); - s_extensionlessPathPattern = new Regex(@"~/(.+)/([^/.]*)$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); - } - - internal static IEnumerable RemoveVirtualImports(IEnumerable virtualPathDependencies) - { - Guard.NotNull(virtualPathDependencies, nameof(virtualPathDependencies)); - - // determine the virtual themevars scss import reference - var themeVarsFile = virtualPathDependencies.Where(x => ThemeHelper.PathIsThemeVars(x)).FirstOrDefault(); - var moduleImportsFile = virtualPathDependencies.Where(x => ThemeHelper.PathIsModuleImports(x)).FirstOrDefault(); - - if (themeVarsFile == null && moduleImportsFile == null) - { - // no themevars or moduleimports import... so no special considerations here - return virtualPathDependencies; - } - - // exclude the special imports from the file dependencies list, - // 'cause this one cannot be monitored by the physical file system - return virtualPathDependencies - .Except((new string[] { themeVarsFile, moduleImportsFile }) - .Where(x => x.HasValue())) - .ToArray(); - } + private static readonly Regex s_inheritableThemeFilePattern; + private static readonly Regex s_themeVarsPattern; + private static readonly Regex s_moduleImportsPattern; + private static readonly Regex s_extensionlessPathPattern; + + public static readonly string ThemesBasePath; + + static ThemeHelper() + { + ThemesBasePath = CommonHelper.GetAppSetting("sm:ThemesBasePath", "~/Themes/").EnsureEndsWith("/"); + + var pattern = @"^{0}(.*)/(.+)(\.)(png|gif|jpg|jpeg|css|scss|js|cshtml|svg|json|liquid)(\?base)*$".FormatInvariant(ThemesBasePath); + s_inheritableThemeFilePattern = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); + s_themeVarsPattern = new Regex(@"\.(db|app)/[_]?themevars(.scss)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); + s_moduleImportsPattern = new Regex(@"\.app/[_]?moduleimports.scss", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); + s_extensionlessPathPattern = new Regex(@"~/(.+)/([^/.]*)$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); + } + + internal static IEnumerable RemoveVirtualImports(IEnumerable virtualPathDependencies) + { + Guard.NotNull(virtualPathDependencies, nameof(virtualPathDependencies)); + + // determine the virtual themevars scss import reference + var themeVarsFile = virtualPathDependencies.Where(x => ThemeHelper.PathIsThemeVars(x)).FirstOrDefault(); + var moduleImportsFile = virtualPathDependencies.Where(x => ThemeHelper.PathIsModuleImports(x)).FirstOrDefault(); + + if (themeVarsFile == null && moduleImportsFile == null) + { + // no themevars or moduleimports import... so no special considerations here + return virtualPathDependencies; + } + + // exclude the special imports from the file dependencies list, + // 'cause this one cannot be monitored by the physical file system + return virtualPathDependencies + .Except((new string[] { themeVarsFile, moduleImportsFile }) + .Where(x => x.HasValue())) + .ToArray(); + } internal static bool PathIsThemeVars(string virtualPath) { - string extension = null; - return PathIsThemeVars(virtualPath, out extension); + string extension = null; + return PathIsThemeVars(virtualPath, out extension); + } + + internal static bool PathIsThemeVars(string virtualPath, out string extension) + { + extension = null; + + if (string.IsNullOrEmpty(virtualPath)) + return false; + + var match = s_themeVarsPattern.Match(virtualPath); + if (match.Success) + { + extension = match.Groups[2].Value; + return true; + } + + return false; + } + + internal static bool PathIsModuleImports(string virtualPath) + { + if (string.IsNullOrEmpty(virtualPath)) + return false; + + return s_moduleImportsPattern.IsMatch(virtualPath); + } + + internal static bool PathIsInheritableThemeFile(string virtualPath) + { + if (string.IsNullOrWhiteSpace(virtualPath)) + return false; + + if (virtualPath[0] != '~') + { + virtualPath = VirtualPathUtility.ToAppRelative(virtualPath); + } + + return s_inheritableThemeFilePattern.IsMatch(virtualPath); + } + + internal static bool IsAdminArea() + { + if (HttpContext.Current != null) + { + return HttpContext.Current.Request.IsAdminArea(); + } + + return false; + } + + internal static bool IsStyleValidationRequest() + { + return HttpContext.Current?.Request?.QueryString["validate"] != null; + } + + internal static StyleSheetResult IsStyleSheet(string path) + { + // Handle virtual Sass imports with '?base' query + // TBD: (mc) other query params could exist + var qindex = path.IndexOf('?'); + if (qindex > -1) + { + var pathWithoutQuery = path.Substring(0, qindex); + var query = path.Substring(pathWithoutQuery.Length); + if (query.StartsWith("?base", StringComparison.OrdinalIgnoreCase)) + { + // Process again, this time without query + var result = IsStyleSheet(pathWithoutQuery); + result.IsBaseImport = true; + return result; + } + } + + var extension = Path.GetExtension(path).ToLowerInvariant(); + + if (extension == ".cshtml") + { + // Perf + return null; + } + else if (extension == ".css") + { + return new StyleSheetResult { Path = path, IsCss = true, Extension = extension }; + } + else if (extension == ".scss" || extension == ".sass") + { + return new StyleSheetResult { Path = path, IsSass = true, Extension = extension }; + } + else if (extension.IsEmpty()) + { + if (path.Contains("/scss/")) + { + // Bootstrap and other libaries may import SASS files without extension + return new StyleSheetResult { Path = path, IsSass = true }; + } + + // StyleBundles are extension-less, so we have to ask 'BundleTable' + // if a style bundle has been registered for the given path. + if (s_extensionlessPathPattern.IsMatch(path)) + { + var bundle = BundleTable.Bundles.GetBundleFor(path); + if (bundle != null && ((bundle is SmartStyleBundle || bundle is StyleBundle))) + { + return new StyleSheetResult { Path = path, IsBundle = true }; + } + } + } + + return null; } - internal static bool PathIsThemeVars(string virtualPath, out string extension) - { - extension = null; - - if (string.IsNullOrEmpty(virtualPath)) - return false; - - var match = s_themeVarsPattern.Match(virtualPath); - if (match.Success) - { - extension = match.Groups[2].Value; - return true; - } - - return false; - } - - internal static bool PathIsModuleImports(string virtualPath) - { - if (string.IsNullOrEmpty(virtualPath)) - return false; - - return s_moduleImportsPattern.IsMatch(virtualPath); - } - - internal static bool PathIsInheritableThemeFile(string virtualPath) - { - if (string.IsNullOrWhiteSpace(virtualPath)) - return false; - - if (virtualPath[0] != '~') - { - virtualPath = VirtualPathUtility.ToAppRelative(virtualPath); - } - - return s_inheritableThemeFilePattern.IsMatch(virtualPath); - } - - internal static bool IsAdminArea() - { - if (HttpContext.Current != null) - { - return HttpContext.Current.Request.IsAdminArea(); - } - - return false; - } - - internal static bool IsStyleValidationRequest() - { - return HttpContext.Current?.Request?.QueryString["validate"] != null; - } - - internal static StyleSheetResult IsStyleSheet(string path) - { - // Handle virtual Sass imports with '?base' query - // TBD: (mc) other query params could exist - var qindex = path.IndexOf('?'); - if (qindex > -1) - { - var pathWithoutQuery = path.Substring(0, qindex); - var query = path.Substring(pathWithoutQuery.Length); - if (query.StartsWith("?base", StringComparison.OrdinalIgnoreCase)) - { - // Process again, this time without query - var result = IsStyleSheet(pathWithoutQuery); - result.IsBaseImport = true; - return result; - } - } - - var extension = Path.GetExtension(path).ToLowerInvariant(); - - if (extension == ".cshtml") - { - // Perf - return null; - } - else if (extension == ".css") - { - return new StyleSheetResult { Path = path, IsCss = true, Extension = extension }; - } - else if (extension == ".scss" || extension == ".sass") - { - return new StyleSheetResult { Path = path, IsSass = true, Extension = extension }; - } - else if (extension.IsEmpty()) - { - if (path.Contains("/scss/")) - { - // Bootstrap and other libaries may import SASS files without extension - return new StyleSheetResult { Path = path, IsSass = true }; - } - - // StyleBundles are extension-less, so we have to ask 'BundleTable' - // if a style bundle has been registered for the given path. - if (s_extensionlessPathPattern.IsMatch(path)) - { - var bundle = BundleTable.Bundles.GetBundleFor(path); - if (bundle != null && ((bundle is SmartStyleBundle || bundle is StyleBundle))) - { - return new StyleSheetResult { Path = path, IsBundle = true }; - } - } - } - - return null; - } - - internal static ThemeManifest ResolveCurrentTheme() - { - return EngineContext.Current.Resolve().CurrentTheme; - } - - internal static int ResolveCurrentStoreId() - { - return EngineContext.Current.Resolve().CurrentStore.Id; - } - - internal static string TokenizePath(string virtualPath, out string themeName, out string relativePath, out string query) - { - themeName = null; - relativePath = null; - query = null; - - var unrooted = virtualPath.Substring(ThemesBasePath.Length); // strip "~/Themes/" - themeName = unrooted.Substring(0, unrooted.IndexOf('/')); - relativePath = unrooted.Substring(themeName.Length + 1); - - var idx = relativePath.IndexOf('?'); - if (idx > 0) - { - query = relativePath.Substring(idx + 1); - relativePath = relativePath.Substring(0, idx); - } - - // strip out query - return "{0}{1}/{2}".FormatCurrent(ThemesBasePath, themeName, relativePath); - } - } - - internal class StyleSheetResult - { - public string Path { get; set; } - public string Extension { get; set; } - public bool IsCss { get; set; } - public bool IsSass { get; set; } - public bool IsBundle { get; set; } - public bool IsBaseImport { get; set; } - - public bool IsPreprocessor - { - get { return IsSass; } - } - - public bool IsThemeVars - { - get { return IsPreprocessor && ThemeHelper.PathIsThemeVars(Path); } - } - - public bool IsModuleImports - { - get { return IsSass && ThemeHelper.PathIsModuleImports(Path); } - } - } + internal static ThemeManifest ResolveCurrentTheme() + { + return EngineContext.Current.Resolve().CurrentTheme; + } + + internal static int ResolveCurrentStoreId() + { + return EngineContext.Current.Resolve().CurrentStore.Id; + } + + internal static string TokenizePath(string virtualPath, out string themeName, out string relativePath, out string query) + { + themeName = null; + relativePath = null; + query = null; + + var unrooted = virtualPath.Substring(ThemesBasePath.Length); // strip "~/Themes/" + themeName = unrooted.Substring(0, unrooted.IndexOf('/')); + relativePath = unrooted.Substring(themeName.Length + 1); + + var idx = relativePath.IndexOf('?'); + if (idx > 0) + { + query = relativePath.Substring(idx + 1); + relativePath = relativePath.Substring(0, idx); + } + + // strip out query + return "{0}{1}/{2}".FormatCurrent(ThemesBasePath, themeName, relativePath); + } + } + + internal class StyleSheetResult + { + public string Path { get; set; } + public string Extension { get; set; } + public bool IsCss { get; set; } + public bool IsSass { get; set; } + public bool IsBundle { get; set; } + public bool IsBaseImport { get; set; } + + public bool IsPreprocessor => IsSass; + + public bool IsThemeVars => IsPreprocessor && ThemeHelper.PathIsThemeVars(Path); + + public bool IsModuleImports => IsSass && ThemeHelper.PathIsModuleImports(Path); + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeHtmlExtensions.cs b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeHtmlExtensions.cs index f70718141f..2933507b6a 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeHtmlExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeHtmlExtensions.cs @@ -1,19 +1,15 @@ -using System; -using System.Collections.Generic; +using System.IO; using System.Linq; -using System.IO; using System.Text; using System.Web.Mvc; using System.Web.Mvc.Html; using SmartStore.Core; using SmartStore.Core.Infrastructure; using SmartStore.Core.Themes; -using SmartStore.Services.Common; -using SmartStore.Utilities; using SmartStore.Services.Localization; namespace SmartStore.Web.Framework.Theming -{ +{ public static class ThemeHtmlExtensions { @@ -30,110 +26,110 @@ public static MvcHtmlString ThemeVarLabel(this HtmlHelper html, ThemeVariableInf var displayName = locService.GetResource(resKey, langId, false, "", true); - if (displayName.HasValue() && hint.IsEmpty()) - { - hint = locService.GetResource(resKey + ".Hint", langId, false, "", true); - hint = "${0}{1}".FormatInvariant(info.Name, hint.HasValue() ? "\n" + hint : ""); - } + if (displayName.HasValue() && hint.IsEmpty()) + { + hint = locService.GetResource(resKey + ".Hint", langId, false, "", true); + hint = "${0}{1}".FormatInvariant(info.Name, hint.HasValue() ? "\n" + hint : ""); + } result.Append("
"); result.Append(html.Label(html.NameForThemeVar(info), displayName.NullEmpty() ?? "$" + info.Name, new { @class = "x-col-form-label" })); - if (hint.HasValue()) - { - result.Append(html.Hint(hint).ToHtmlString()); - } + if (hint.HasValue()) + { + result.Append(html.Hint(hint).ToHtmlString()); + } result.Append("
"); return MvcHtmlString.Create(result.ToString()); } - + public static MvcHtmlString ThemeVarEditor(this HtmlHelper html, ThemeVariableInfo info, object value) { Guard.NotNull(info, "info"); string expression = html.NameForThemeVar(info); - var strValue = string.Empty; + var strValue = string.Empty; - var arrValue = value as string[]; - if (arrValue != null) - { - strValue = arrValue.Length > 0 ? arrValue[0] : value.ToString(); - } - else - { - strValue = value.ToString(); - } + var arrValue = value as string[]; + if (arrValue != null) + { + strValue = arrValue.Length > 0 ? arrValue[0] : value.ToString(); + } + else + { + strValue = value.ToString(); + } - var isDefault = strValue.IsCaseInsensitiveEqual(info.DefaultValue); - var isValidColor = info.Type == ThemeVariableType.Color - && ((strValue.HasValue() && ThemeVarsRepository.IsValidColor(strValue)) || (strValue.IsEmpty() && ThemeVarsRepository.IsValidColor(info.DefaultValue))); + var isDefault = strValue.IsCaseInsensitiveEqual(info.DefaultValue); + var isValidColor = info.Type == ThemeVariableType.Color + && ((strValue.HasValue() && ThemeVarsRepository.IsValidColor(strValue)) || (strValue.IsEmpty() && ThemeVarsRepository.IsValidColor(info.DefaultValue))); - MvcHtmlString control; + MvcHtmlString control; if (isValidColor) { - control = html.ColorBox(expression, strValue, info.DefaultValue); + control = html.ColorBox(expression, strValue, info.DefaultValue); } else if (info.Type == ThemeVariableType.Boolean) { - var locService = EngineContext.Current.Resolve(); - control = html.CheckBox(expression, strValue.ToBool()); - var custom = "".FormatInvariant( - control.ToString(), - locService.GetResource("Common.On").Truncate(3), - locService.GetResource("Common.Off").Truncate(3)); - - control = MvcHtmlString.Create(custom); + var locService = EngineContext.Current.Resolve(); + control = html.CheckBox(expression, strValue.ToBool()); + var custom = "".FormatInvariant( + control.ToString(), + locService.GetResource("Common.On").Truncate(3), + locService.GetResource("Common.Off").Truncate(3)); + + control = MvcHtmlString.Create(custom); + } + else if (info.Type == ThemeVariableType.Select) + { + control = ThemeVarSelectEditor(html, info, expression, strValue); + } + else + { + control = html.TextBox(expression, isDefault ? "" : strValue, new { placeholder = info.DefaultValue, @class = "form-control" }); } - else if (info.Type == ThemeVariableType.Select) - { - control = ThemeVarSelectEditor(html, info, expression, strValue); - } - else - { - control = html.TextBox(expression, isDefault ? "" : strValue, new { placeholder = info.DefaultValue, @class = "form-control" }); - } - return control; - } + return control; + } - public static MvcHtmlString ThemeVarChainInfo(this HtmlHelper html, ThemeVariableInfo info) - { - Guard.NotNull(info, "info"); + public static MvcHtmlString ThemeVarChainInfo(this HtmlHelper html, ThemeVariableInfo info) + { + Guard.NotNull(info, "info"); - var currentTheme = ThemeHelper.ResolveCurrentTheme(); + var currentTheme = ThemeHelper.ResolveCurrentTheme(); - if (currentTheme != info.Manifest) - { - // the variable is inherited from a base theme: display an info badge - var chainInfo = "{0}".FormatCurrent(info.Manifest.ThemeName); - return MvcHtmlString.Create(chainInfo); - } + if (currentTheme != info.Manifest) + { + // the variable is inherited from a base theme: display an info badge + var chainInfo = "{0}".FormatCurrent(info.Manifest.ThemeName); + return MvcHtmlString.Create(chainInfo); + } - return MvcHtmlString.Empty; - } + return MvcHtmlString.Empty; + } - private static MvcHtmlString ThemeVarSelectEditor(HtmlHelper html, ThemeVariableInfo info, string expression, string value) + private static MvcHtmlString ThemeVarSelectEditor(HtmlHelper html, ThemeVariableInfo info, string expression, string value) { - var manifest = info.Manifest; + var manifest = info.Manifest; if (!manifest.Selects.ContainsKey(info.SelectRef)) { throw new SmartException("A select list with id '{0}' was not specified. Please specify a 'Select' element with at least one 'Option' child.", info.SelectRef); } - //var isDefault = value.IsCaseInsensitiveEqual(info.DefaultValue); + //var isDefault = value.IsCaseInsensitiveEqual(info.DefaultValue); var selectList = from x in manifest.Selects[info.SelectRef] - select new SelectListItem - { - Value = x, + select new SelectListItem + { + Value = x, Text = x, // TODO: (mc) Localize - Selected = x.IsCaseInsensitiveEqual(value) + Selected = x.IsCaseInsensitiveEqual(value) }; - return html.DropDownList(expression, selectList, new { placeholder = info.DefaultValue, @class = "form-control" }); + return html.DropDownList(expression, selectList, new { placeholder = info.DefaultValue, @class = "form-control" }); } public static string NameForThemeVar(this HtmlHelper html, ThemeVariableInfo info) @@ -166,9 +162,9 @@ public static string ThemeAwareContent(this UrlHelper url, string path) public static string ThemeAwareContent(this UrlHelper url, ThemeManifest manifest, string path) { path = EnsurePath(path); - + string fullPath = Path.Combine(manifest.Path, path); - if (File.Exists(fullPath)) + if (File.Exists(fullPath)) { return url.Content(manifest.Location + "/" + Path.Combine(manifest.ThemeName, path)); } @@ -178,7 +174,7 @@ public static string ThemeAwareContent(this UrlHelper url, ThemeManifest manifes { return url.Content("~/" + path); } - + return string.Empty; } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeVarsRepository.cs b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeVarsRepository.cs index 29cc34de82..255a417b68 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeVarsRepository.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeVarsRepository.cs @@ -3,24 +3,23 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; +using System.Web; using SmartStore.Core.Infrastructure; using SmartStore.Services.Themes; -using System.Web; -using System.IO; namespace SmartStore.Web.Framework.Theming -{ +{ internal class ThemeVarsRepository { private static readonly Regex s_keyWhitelist = new Regex(@"^[a-zA-Z0-9_-]+$", RegexOptions.Compiled); - private static readonly Regex s_valueBlacklist = new Regex(@"[:;]+", RegexOptions.Compiled); - private static readonly Regex s_valueSassVars = new Regex(@"[$][a-zA-Z0-9_-]+", RegexOptions.Compiled); - private static readonly Regex s_valueLessVars = new Regex(@"[@][a-zA-Z0-9_-]+", RegexOptions.Compiled); - //private static readonly Regex s_valueWhitelist = new Regex(@"^[#@]?[a-zA-Z0-9""' _\.,-]*$"); + private static readonly Regex s_valueBlacklist = new Regex(@"[:;]+", RegexOptions.Compiled); + private static readonly Regex s_valueSassVars = new Regex(@"[$][a-zA-Z0-9_-]+", RegexOptions.Compiled); + private static readonly Regex s_valueLessVars = new Regex(@"[@][a-zA-Z0-9_-]+", RegexOptions.Compiled); + //private static readonly Regex s_valueWhitelist = new Regex(@"^[#@]?[a-zA-Z0-9""' _\.,-]*$"); - const string SassVarPrefix = "$"; + const string SassVarPrefix = "$"; - public string GetPreprocessorCss(string extension, string themeName, int storeId) + public string GetPreprocessorCss(string extension, string themeName, int storeId) { Guard.NotEmpty(themeName, nameof(themeName)); Guard.IsPositive(storeId, nameof(storeId)); @@ -35,7 +34,7 @@ private IDictionary GetVariables(string themeName, int storeId) { var result = new Dictionary(); - var rawVars = this.GetRawVariables(themeName, storeId); + var rawVars = this.GetRawVariables(themeName, storeId); foreach (var v in rawVars) { @@ -46,13 +45,13 @@ private IDictionary GetVariables(string themeName, int storeId) string value = v.Value.ToString(); - if (s_valueBlacklist.IsMatch(value)) - continue; + if (s_valueBlacklist.IsMatch(value)) + continue; - //if (!s_valueWhitelist.IsMatch(value)) - // continue; + //if (!s_valueWhitelist.IsMatch(value)) + // continue; - result.Add(key, value); + result.Add(key, value); } return result; @@ -60,75 +59,75 @@ private IDictionary GetVariables(string themeName, int storeId) internal virtual ExpandoObject GetRawVariables(string themeName, int storeId) { - // we need the Asp.Net cache here in order to define cacheKey dependencies - bool validationMode = ThemeHelper.IsStyleValidationRequest(); - - if (validationMode) - { - // Return uncached fresh data (the variables is not nuked yet) - return GetRawVariablesCore(themeName, storeId); - } - else - { - string cacheKey = FrameworkCacheConsumer.BuildThemeVarsCacheKey(themeName, storeId); - return HttpRuntime.Cache.GetOrAdd(cacheKey, () => - { - return GetRawVariablesCore(themeName, storeId); - }); - } + // we need the Asp.Net cache here in order to define cacheKey dependencies + bool validationMode = ThemeHelper.IsStyleValidationRequest(); + + if (validationMode) + { + // Return uncached fresh data (the variables is not nuked yet) + return GetRawVariablesCore(themeName, storeId); + } + else + { + string cacheKey = FrameworkCacheConsumer.BuildThemeVarsCacheKey(themeName, storeId); + return HttpRuntime.Cache.GetOrAdd(cacheKey, () => + { + return GetRawVariablesCore(themeName, storeId); + }); + } + } + + private ExpandoObject GetRawVariablesCore(string themeName, int storeId) + { + var themeVarService = EngineContext.Current.Resolve(); + return themeVarService.GetThemeVariables(themeName, storeId) ?? new ExpandoObject(); + } + + private string Transform(IDictionary parameters) + { + if (parameters.Count == 0) + return string.Empty; + + var prefix = SassVarPrefix; + + var sb = new StringBuilder(); + foreach (var parameter in parameters.Where(kvp => kvp.Value.HasValue())) + { + sb.AppendFormat("{0}{1}: {2};\n", prefix, parameter.Key, parameter.Value); + } + + return sb.ToString(); } - private ExpandoObject GetRawVariablesCore(string themeName, int storeId) - { - var themeVarService = EngineContext.Current.Resolve(); - return themeVarService.GetThemeVariables(themeName, storeId) ?? new ExpandoObject(); - } - - private string Transform(IDictionary parameters) - { - if (parameters.Count == 0) - return string.Empty; - - var prefix = SassVarPrefix; - - var sb = new StringBuilder(); - foreach (var parameter in parameters.Where(kvp => kvp.Value.HasValue())) - { - sb.AppendFormat("{0}{1}: {2};\n", prefix, parameter.Key, parameter.Value); - } - - return sb.ToString(); - } - - /// - /// Checks whether the passed SASS value is a valid, displayable HTML color, - /// e.g.: "lighten($red, 20%)" would return false. - /// - /// The SASS value to test - /// - /// We need this helper during theme configuration: we just can't render - /// color pickers for color values containing var references or SASS functions. - /// - internal static bool IsValidColor(string value) - { - if (value.IsEmpty()) - { - return true; - } - - if (s_valueSassVars.IsMatch(value)) - { - return false; - } - - if (value[0] == '#' || value.StartsWith("rgb(") || value.StartsWith("rgba(") || value.StartsWith("hsl(") || value.StartsWith("hsla(")) - { - return true; - } - - // Let pass all color names (red, blue etc.), but reject functions, e.g. "lighten(#fff, 10%)" - return !value.Contains("("); - } - } + /// + /// Checks whether the passed SASS value is a valid, displayable HTML color, + /// e.g.: "lighten($red, 20%)" would return false. + /// + /// The SASS value to test + /// + /// We need this helper during theme configuration: we just can't render + /// color pickers for color values containing var references or SASS functions. + /// + internal static bool IsValidColor(string value) + { + if (value.IsEmpty()) + { + return true; + } + + if (s_valueSassVars.IsMatch(value)) + { + return false; + } + + if (value[0] == '#' || value.StartsWith("rgb(") || value.StartsWith("rgba(") || value.StartsWith("hsl(") || value.StartsWith("hsla(")) + { + return true; + } + + // Let pass all color names (red, blue etc.), but reject functions, e.g. "lighten(#fff, 10%)" + return !value.Contains("("); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeVarsVirtualFile.cs b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeVarsVirtualFile.cs index b4bd7ab49b..9ac6d1fe4c 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeVarsVirtualFile.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeVarsVirtualFile.cs @@ -12,24 +12,21 @@ public ThemeVarsVirtualFile(string virtualPath, string themeName, int storeId) { } - internal ThemeVarsVirtualFile(string virtualPath, string extension, string themeName, int storeId) - : base(virtualPath) - { - Extension = extension; - ThemeName = themeName; - StoreId = storeId; - } - - public override bool IsDirectory + internal ThemeVarsVirtualFile(string virtualPath, string extension, string themeName, int storeId) + : base(virtualPath) { - get { return false; } + Extension = extension; + ThemeName = themeName; + StoreId = storeId; } - public string Extension { get; private set; } - public string ThemeName { get; private set; } - public int StoreId { get; private set; } + public override bool IsDirectory => false; - public override Stream Open() + public string Extension { get; private set; } + public string ThemeName { get; private set; } + public int StoreId { get; private set; } + + public override Stream Open() { var repo = new ThemeVarsRepository(); @@ -43,19 +40,19 @@ public override Stream Open() private Stream GenerateStreamFromString(string value) { var stream = new MemoryStream(); - - using (var writer = new StreamWriter(stream, Encoding.Unicode, 1024, true)) - { - writer.Write(value); - writer.Flush(); - stream.Seek(0, SeekOrigin.Begin); - return stream; - } + + using (var writer = new StreamWriter(stream, Encoding.Unicode, 1024, true)) + { + writer.Write(value); + writer.Flush(); + stream.Seek(0, SeekOrigin.Begin); + return stream; + } } - public void AddFileDependencies(ICollection mappedPaths, ICollection cacheKeys) - { - cacheKeys.Add(FrameworkCacheConsumer.BuildThemeVarsCacheKey(ThemeName, StoreId)); - } - } + public void AddFileDependencies(ICollection mappedPaths, ICollection cacheKeys) + { + cacheKeys.Add(FrameworkCacheConsumer.BuildThemeVarsCacheKey(ThemeName, StoreId)); + } + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeableRazorViewEngine.cs b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeableRazorViewEngine.cs index de832a6cd2..bc0fd04532 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeableRazorViewEngine.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeableRazorViewEngine.cs @@ -5,52 +5,52 @@ namespace SmartStore.Web.Framework.Theming { - public class ThemeableRazorViewEngine : ThemeableVirtualPathProviderViewEngine - { - [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] - public ThemeableRazorViewEngine() - { - var areaBasePathsSetting = CommonHelper.GetAppSetting("sm:AreaBasePaths", "~/Plugins/"); - var areaBasePaths = areaBasePathsSetting.Split(',').Select(x => x.Trim().EnsureEndsWith("/")).ToArray(); - - // 0: view, 1: controller, 2: area - var areaFormats = new[] - { - "{2}/Views/{1}/{0}", - "{2}/Views/Shared/{0}" - }; - var areaLocationFormats = areaBasePaths.SelectMany(x => areaFormats.Select(f => x + f)); - - AreaViewLocationFormats = ExpandLocationFormats(areaLocationFormats, ViewType.Layout).ToArray(); - AreaMasterLocationFormats = ExpandLocationFormats(areaLocationFormats, ViewType.Layout).ToArray(); - AreaPartialViewLocationFormats = ExpandLocationFormats(areaLocationFormats, ViewType.Partial).ToArray(); - - // 0: view, 1: controller, 2: theme - var locationFormats = new[] + public class ThemeableRazorViewEngine : ThemeableVirtualPathProviderViewEngine + { + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + public ThemeableRazorViewEngine() + { + var areaBasePathsSetting = CommonHelper.GetAppSetting("sm:AreaBasePaths", "~/Plugins/"); + var areaBasePaths = areaBasePathsSetting.Split(',').Select(x => x.Trim().EnsureEndsWith("/")).ToArray(); + + // 0: view, 1: controller, 2: area + var areaFormats = new[] { - "~/Themes/{2}/Views/{1}/{0}", - "~/Views/{1}/{0}", - "~/Themes/{2}/Views/Shared/{0}", - "~/Views/Shared/{0}" - }; + "{2}/Views/{1}/{0}", + "{2}/Views/Shared/{0}" + }; + var areaLocationFormats = areaBasePaths.SelectMany(x => areaFormats.Select(f => x + f)); + + AreaViewLocationFormats = ExpandLocationFormats(areaLocationFormats, ViewType.Layout).ToArray(); + AreaMasterLocationFormats = ExpandLocationFormats(areaLocationFormats, ViewType.Layout).ToArray(); + AreaPartialViewLocationFormats = ExpandLocationFormats(areaLocationFormats, ViewType.Partial).ToArray(); + + // 0: view, 1: controller, 2: theme + var locationFormats = new[] + { + "~/Themes/{2}/Views/{1}/{0}", + "~/Views/{1}/{0}", + "~/Themes/{2}/Views/Shared/{0}", + "~/Views/Shared/{0}" + }; ViewLocationFormats = ExpandLocationFormats(locationFormats, ViewType.Layout).ToArray(); MasterLocationFormats = ExpandLocationFormats(locationFormats, ViewType.Layout).ToArray(); - PartialViewLocationFormats = ExpandLocationFormats(locationFormats, ViewType.Partial).ToArray(); - - FileExtensions = EnableVbViews - ? new[] { "cshtml", "vbhtml" } - : new[] { "cshtml" }; - } - - protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) - { - return new RazorView(controllerContext, partialPath, null, false, base.FileExtensions, base.ViewPageActivator); - } - - protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) - { - return new RazorView(controllerContext, viewPath, masterPath, true, base.FileExtensions, base.ViewPageActivator); - } - } + PartialViewLocationFormats = ExpandLocationFormats(locationFormats, ViewType.Partial).ToArray(); + + FileExtensions = EnableVbViews + ? new[] { "cshtml", "vbhtml" } + : new[] { "cshtml" }; + } + + protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) + { + return new RazorView(controllerContext, partialPath, null, false, base.FileExtensions, base.ViewPageActivator); + } + + protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) + { + return new RazorView(controllerContext, viewPath, masterPath, true, base.FileExtensions, base.ViewPageActivator); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeableVirtualPathProviderViewEngine.cs b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeableVirtualPathProviderViewEngine.cs index d7b0e936d2..7bfa8c1161 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/ThemeableVirtualPathProviderViewEngine.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/ThemeableVirtualPathProviderViewEngine.cs @@ -13,487 +13,481 @@ namespace SmartStore.Web.Framework.Theming { - public abstract class ThemeableVirtualPathProviderViewEngine : BuildManagerViewEngine - { - internal Func GetExtensionThunk = VirtualPathUtility.GetExtension; + public abstract class ThemeableVirtualPathProviderViewEngine : BuildManagerViewEngine + { + internal Func GetExtensionThunk = VirtualPathUtility.GetExtension; - private static readonly string[] _emptyLocations = new string[0]; + private static readonly string[] _emptyLocations = new string[0]; - private static bool? _enableLocalizedViews; + private static bool? _enableLocalizedViews; private static bool? _enableVbViews; private readonly string _cacheKeyType = typeof(ThemeableRazorViewEngine).Name; - private readonly string _cacheKeyEntry = ":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}:{5}"; - - protected ThemeableVirtualPathProviderViewEngine() - { - this.ViewLocationCache = new TwoLevelViewLocationCache(); - - // prepare localized mobile & desktop display modes - DisplayModeProvider.Modes.Clear(); - var mobileDisplayMode = new LocalizedDisplayMode(DisplayModeProvider.MobileDisplayModeId, EnableLocalizedViews) - { - ContextCondition = IsMobileDevice - }; - var desktopDisplayMode = new LocalizedDisplayMode(DisplayModeProvider.DefaultDisplayModeId, EnableLocalizedViews); - - DisplayModeProvider.Modes.Add(mobileDisplayMode); - DisplayModeProvider.Modes.Add(desktopDisplayMode); - } - - public static bool EnableLocalizedViews - { - get - { - if (!_enableLocalizedViews.HasValue) - { - _enableLocalizedViews = CommonHelper.GetAppSetting("sm:EnableLocalizedViews", false); - } - - return _enableLocalizedViews.Value; - } - set - { - _enableLocalizedViews = value; - } - } - - public static bool EnableVbViews - { - get - { - if (!_enableVbViews.HasValue) - { - _enableVbViews = CommonHelper.GetAppSetting("sm:EnableVbViews", false); - } - - return _enableVbViews.Value; - } - set - { - _enableVbViews = value; - } - } - - public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) - { - Guard.NotNull(controllerContext, nameof(controllerContext)); - Guard.NotEmpty(viewName, nameof(viewName)); - - var chronometer = EngineContext.Current.Resolve(); - using (chronometer.Step("Find view '{0}'".FormatInvariant(viewName))) - { - var themeName = GetCurrentThemeName(controllerContext); - var controllerName = controllerContext.RouteData.GetRequiredString("controller"); - var areaName = controllerContext.RouteData.GetAreaName(); - - var viewPath = ResolveViewPath( - controllerContext, - areaName, - ViewLocationFormats, - AreaViewLocationFormats, - "ViewLocationFormats", - viewName, - controllerName, - themeName, - "View", - useCache, - out var viewLocationsSearched); - var masterPath = ResolveViewPath( - controllerContext, - areaName, - MasterLocationFormats, - AreaMasterLocationFormats, - "MasterLocationFormats", - masterName, - controllerName, - themeName, - "Master", - useCache, - out var masterLocationsSearched); - - if (!string.IsNullOrEmpty(viewPath) && (!string.IsNullOrEmpty(masterPath) || string.IsNullOrEmpty(masterName))) - { - return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this); - } - - return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched)); - } - } - - public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) - { - Guard.NotNull(controllerContext, nameof(controllerContext)); - Guard.NotEmpty(partialViewName, nameof(partialViewName)); - - var chronometer = EngineContext.Current.Resolve(); - using (chronometer.Step("Find partial view '{0}'".FormatInvariant(partialViewName))) - { - string[] searchedLocations; - - var themeName = GetCurrentThemeName(controllerContext); - var controllerName = controllerContext.RouteData.GetRequiredString("controller"); - var areaName = controllerContext.RouteData.GetAreaName(); - - string partialPath = ResolveViewPath( - controllerContext, - areaName, - PartialViewLocationFormats, - AreaPartialViewLocationFormats, - "PartialViewLocationFormats", - partialViewName, - controllerName, - themeName, - "Partial", - useCache, - out searchedLocations); - - if (string.IsNullOrEmpty(partialPath)) - { - return new ViewEngineResult(searchedLocations); - } - - return new ViewEngineResult(CreatePartialView(controllerContext, partialPath), this); - } - } + private readonly string _cacheKeyEntry = ":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}:{5}"; + + protected ThemeableVirtualPathProviderViewEngine() + { + this.ViewLocationCache = new TwoLevelViewLocationCache(); + + // prepare localized mobile & desktop display modes + DisplayModeProvider.Modes.Clear(); + var mobileDisplayMode = new LocalizedDisplayMode(DisplayModeProvider.MobileDisplayModeId, EnableLocalizedViews) + { + ContextCondition = IsMobileDevice + }; + var desktopDisplayMode = new LocalizedDisplayMode(DisplayModeProvider.DefaultDisplayModeId, EnableLocalizedViews); + + DisplayModeProvider.Modes.Add(mobileDisplayMode); + DisplayModeProvider.Modes.Add(desktopDisplayMode); + } + + public static bool EnableLocalizedViews + { + get + { + if (!_enableLocalizedViews.HasValue) + { + _enableLocalizedViews = CommonHelper.GetAppSetting("sm:EnableLocalizedViews", false); + } + + return _enableLocalizedViews.Value; + } + set => _enableLocalizedViews = value; + } + + public static bool EnableVbViews + { + get + { + if (!_enableVbViews.HasValue) + { + _enableVbViews = CommonHelper.GetAppSetting("sm:EnableVbViews", false); + } + + return _enableVbViews.Value; + } + set => _enableVbViews = value; + } + + public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) + { + Guard.NotNull(controllerContext, nameof(controllerContext)); + Guard.NotEmpty(viewName, nameof(viewName)); + + var chronometer = EngineContext.Current.Resolve(); + using (chronometer.Step("Find view '{0}'".FormatInvariant(viewName))) + { + var themeName = GetCurrentThemeName(controllerContext); + var controllerName = controllerContext.RouteData.GetRequiredString("controller"); + var areaName = controllerContext.RouteData.GetAreaName(); + + var viewPath = ResolveViewPath( + controllerContext, + areaName, + ViewLocationFormats, + AreaViewLocationFormats, + "ViewLocationFormats", + viewName, + controllerName, + themeName, + "View", + useCache, + out var viewLocationsSearched); + var masterPath = ResolveViewPath( + controllerContext, + areaName, + MasterLocationFormats, + AreaMasterLocationFormats, + "MasterLocationFormats", + masterName, + controllerName, + themeName, + "Master", + useCache, + out var masterLocationsSearched); + + if (!string.IsNullOrEmpty(viewPath) && (!string.IsNullOrEmpty(masterPath) || string.IsNullOrEmpty(masterName))) + { + return new ViewEngineResult(CreateView(controllerContext, viewPath, masterPath), this); + } + + return new ViewEngineResult(viewLocationsSearched.Union(masterLocationsSearched)); + } + } + + public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) + { + Guard.NotNull(controllerContext, nameof(controllerContext)); + Guard.NotEmpty(partialViewName, nameof(partialViewName)); + + var chronometer = EngineContext.Current.Resolve(); + using (chronometer.Step("Find partial view '{0}'".FormatInvariant(partialViewName))) + { + string[] searchedLocations; + + var themeName = GetCurrentThemeName(controllerContext); + var controllerName = controllerContext.RouteData.GetRequiredString("controller"); + var areaName = controllerContext.RouteData.GetAreaName(); + + string partialPath = ResolveViewPath( + controllerContext, + areaName, + PartialViewLocationFormats, + AreaPartialViewLocationFormats, + "PartialViewLocationFormats", + partialViewName, + controllerName, + themeName, + "Partial", + useCache, + out searchedLocations); + + if (string.IsNullOrEmpty(partialPath)) + { + return new ViewEngineResult(searchedLocations); + } + + return new ViewEngineResult(CreatePartialView(controllerContext, partialPath), this); + } + } protected virtual string ResolveViewPath( - ControllerContext controllerContext, - string areaName, - string[] locations, - string[] areaLocations, - string locationsPropertyName, - string name, - string controllerName, - string theme, - string cacheKeyPrefix, - bool useCache, - out string[] searchedLocations) - { - searchedLocations = _emptyLocations; - - if (String.IsNullOrEmpty(name)) - { - return String.Empty; - } - - bool usingAreas = !String.IsNullOrEmpty(areaName); - - if (usingAreas) - { - var isAdminArea = areaName.IsCaseInsensitiveEqual("admin"); - - // "ExtraAreaViewLocations" gets injected by AdminThemedAttribute - var extraAreaViewLocations = controllerContext.RouteData.DataTokens["ExtraAreaViewLocations"] as string[]; - - if (isAdminArea && extraAreaViewLocations == null && controllerContext.HttpContext.Request.IsAjaxRequest()) - { - // In admin area AJAX requests DataTokens["ExtraAreaViewLocations"] is not set. - extraAreaViewLocations = AdminThemedAttribute.ExtraAreaViewLocations; - } - - if (extraAreaViewLocations != null && extraAreaViewLocations.Length > 0) - { - var newLocations = areaLocations.ToList(); - var viewType = cacheKeyPrefix == "Partial" - ? ViewType.Partial - : ViewType.Layout; - - if (isAdminArea) - { - // the admin area cannot fallback to itself. Prepend to list. - ExpandLocationFormats(extraAreaViewLocations, viewType).Reverse().Each(x => newLocations.Insert(0, x)); - } - else - { - newLocations.AddRange(ExpandLocationFormats(extraAreaViewLocations, viewType)); - } - - areaLocations = newLocations.ToArray(); - } - } - - var viewLocations = GetViewLocations(locations, (usingAreas) ? areaLocations : null); - - if (viewLocations.Count == 0) - { - throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Properties cannot be null or empty.", new object[] { locationsPropertyName })); - } - - bool nameRepresentsPath = IsSpecificPath(name); - string cacheKey = CreateCacheKey(cacheKeyPrefix, name, (nameRepresentsPath) ? String.Empty : controllerName, areaName, theme); - - if (useCache) - { - // Only look at cached display modes that can handle the context. - var possibleDisplayModes = DisplayModeProvider.GetAvailableDisplayModesForContext(controllerContext.HttpContext, controllerContext.DisplayMode); - foreach (var displayMode in possibleDisplayModes) - { - string cachedLocation = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, displayMode.DisplayModeId)); - - if (cachedLocation == null) - { - // If any matching display mode location is not in the cache, fall back to the uncached behavior, which will repopulate all of our caches. - return null; - } - - // A non-empty cachedLocation indicates that we have a matching file on disk. Return that result. - if (cachedLocation.Length > 0) - { - if (controllerContext.DisplayMode == null) - { - controllerContext.DisplayMode = displayMode; - } - - return cachedLocation; - } - // An empty cachedLocation value indicates that we don't have a matching file on disk. Keep going down the list of possible display modes. - } - - // ResolveViewPath is called again without using the cache. - return null; - } - else - { - return nameRepresentsPath - ? GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations) - : GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, theme, cacheKey, ref searchedLocations); - } - } - - protected virtual string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, ref string[] searchedLocations) - { - string result = name; - - if (!(FilePathIsSupported(name) && FileExists(controllerContext, name))) - { - result = String.Empty; - searchedLocations = new[] { name }; - } - - ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result); - return result; - } - - protected virtual string GetPathFromGeneralName( - ControllerContext controllerContext, - List locations, - string name, - string controllerName, - string areaName, - string theme, - string cacheKey, - ref string[] searchedLocations) - { - string result = String.Empty; - searchedLocations = new string[locations.Count]; - - for (int i = 0; i < locations.Count; i++) - { - ViewLocation location = locations[i]; - string virtualPath = location.Format(name, controllerName, areaName, theme); - DisplayInfo virtualPathDisplayInfo = DisplayModeProvider.GetDisplayInfoForVirtualPath(virtualPath, controllerContext.HttpContext, path => FileExists(controllerContext, path), controllerContext.DisplayMode); - - if (virtualPathDisplayInfo != null) - { - string resolvedVirtualPath = virtualPathDisplayInfo.FilePath; - - searchedLocations = _emptyLocations; - result = resolvedVirtualPath; - ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, GetDisplayModeId(virtualPathDisplayInfo)), result); - - if (controllerContext.DisplayMode == null) - { - controllerContext.DisplayMode = virtualPathDisplayInfo.DisplayMode; - } - - // Populate the cache for all other display modes. We want to cache both file system hits and misses so that we can distinguish - // in future requests whether a file's status was evicted from the cache (null value) or if the file doesn't exist (empty string). - IEnumerable allDisplayModes = DisplayModeProvider.Modes; - foreach (IDisplayMode displayMode in allDisplayModes) - { - if (displayMode.DisplayModeId != virtualPathDisplayInfo.DisplayMode.DisplayModeId) - { - DisplayInfo displayInfoToCache = displayMode.GetDisplayInfo(controllerContext.HttpContext, virtualPath, virtualPathExists: path => FileExists(controllerContext, path)); - - string displayModeId = String.Empty; - string cacheValue = String.Empty; - if (displayInfoToCache != null && displayInfoToCache.FilePath != null) - { - cacheValue = displayInfoToCache.FilePath; - displayModeId = GetDisplayModeId(displayInfoToCache); - } - else - { - displayModeId = displayMode.DisplayModeId; - } - - ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, displayModeId), cacheValue); - } - } - break; - } - - searchedLocations[i] = virtualPath; - } - - return result; - } - - protected virtual bool FilePathIsSupported(string virtualPath) - { - if (this.FileExtensions == null) - { - // legacy behavior for custom ViewEngine that might not set the FileExtensions property - return true; - } - - // get rid of the '.' because the FileExtensions property expects extensions withouth a dot. - string extension = GetExtensionThunk(virtualPath).TrimStart('.'); - return FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase); - } - - private string GetDisplayModeId(DisplayInfo displayInfo) - { - var localizedDisplayInfo = displayInfo as LocalizedDisplayInfo; - - return localizedDisplayInfo != null - ? localizedDisplayInfo.DisplayModeId - : displayInfo.DisplayMode.DisplayModeId; - } - - protected virtual string CreateCacheKey(string prefix, string name, string controllerName, string areaName, string theme/*, string lang*/) - { - return string.Format(_cacheKeyEntry, - _cacheKeyType, - prefix, - name, - controllerName, - areaName, - theme); - } - - internal static string AppendDisplayModeToCacheKey(string cacheKey, string displayMode) - { - // key format is ":ViewCacheEntry:{cacheType}:{prefix}:{name}:{controllerName}:{areaName}:{theme}" - // so append ":{displayMode}" to the key - return string.IsNullOrWhiteSpace(displayMode) - ? cacheKey - : cacheKey + ":" + displayMode; - } - - protected virtual IEnumerable ExpandLocationFormats(IEnumerable formats, ViewType viewType) - { - // Appends razor view file extensions to location formats - Guard.NotNull(formats, nameof(formats)); - - var subfolder = viewType == ViewType.Layout ? "Layouts" : "Partials"; - - foreach (var format in formats) - { - if (viewType > ViewType.View) - { - yield return format.Replace("{0}", subfolder + "/{0}.cshtml"); - - if (EnableVbViews) - { - yield return format.Replace("{0}", subfolder + "/{0}.vbhtml"); - } - } - - yield return format + ".cshtml"; - - if (EnableVbViews) - { - yield return format + ".vbhtml"; - } - } - } - - protected virtual List GetViewLocations(string[] viewLocationFormats, string[] areaViewLocationFormats) - { - List locations = new List( - (viewLocationFormats?.Length ?? 0) + - (areaViewLocationFormats?.Length ?? 0)); - - if (areaViewLocationFormats != null) - { - foreach (string areaViewLocationFormat in areaViewLocationFormats) - { - locations.Add(new AreaAwareViewLocation(areaViewLocationFormat)); - } - } - - if (viewLocationFormats != null) - { - foreach (string viewLocationFormat in viewLocationFormats) - { - locations.Add(new ViewLocation(viewLocationFormat)); - } - } - - return locations; - } - - protected virtual bool IsSpecificPath(string name) - { - char c = name[0]; - return (c == '~' || c == '/'); - } - - protected virtual bool IsMobileDevice(HttpContextBase httpContext) - { - var mobileDeviceHelper = EngineContext.Current.Resolve(); - var result = mobileDeviceHelper.IsMobileDevice(); - return result; - } - - protected virtual string GetCurrentThemeName(ControllerContext controllerContext) - { - var theme = EngineContext.Current.Resolve().CurrentTheme; - return theme.ThemeName; - } - - public enum ViewType - { - View, - Layout, - Partial - } - } - - public class AreaAwareViewLocation : ViewLocation - { - public AreaAwareViewLocation(string virtualPathFormatString) - : base(virtualPathFormatString) - { - } - - public override string Format(string viewName, string controllerName, string areaName, string theme) - { - return string.Format(_virtualPathFormatString, - viewName, - controllerName, - areaName, - theme); - } - } - - public class ViewLocation - { - protected readonly string _virtualPathFormatString; - - public ViewLocation(string virtualPathFormatString) - { - _virtualPathFormatString = virtualPathFormatString; - } - - public virtual string Format(string viewName, string controllerName, string areaName, string theme) - { - return string.Format(_virtualPathFormatString, - viewName, - controllerName, - theme); - } - } + ControllerContext controllerContext, + string areaName, + string[] locations, + string[] areaLocations, + string locationsPropertyName, + string name, + string controllerName, + string theme, + string cacheKeyPrefix, + bool useCache, + out string[] searchedLocations) + { + searchedLocations = _emptyLocations; + + if (String.IsNullOrEmpty(name)) + { + return String.Empty; + } + + bool usingAreas = !String.IsNullOrEmpty(areaName); + + if (usingAreas) + { + var isAdminArea = areaName.IsCaseInsensitiveEqual("admin"); + + // "ExtraAreaViewLocations" gets injected by AdminThemedAttribute + var extraAreaViewLocations = controllerContext.RouteData.DataTokens["ExtraAreaViewLocations"] as string[]; + + if (isAdminArea && extraAreaViewLocations == null && controllerContext.HttpContext.Request.IsAjaxRequest()) + { + // In admin area AJAX requests DataTokens["ExtraAreaViewLocations"] is not set. + extraAreaViewLocations = AdminThemedAttribute.ExtraAreaViewLocations; + } + + if (extraAreaViewLocations != null && extraAreaViewLocations.Length > 0) + { + var newLocations = areaLocations.ToList(); + var viewType = cacheKeyPrefix == "Partial" + ? ViewType.Partial + : ViewType.Layout; + + if (isAdminArea) + { + // the admin area cannot fallback to itself. Prepend to list. + ExpandLocationFormats(extraAreaViewLocations, viewType).Reverse().Each(x => newLocations.Insert(0, x)); + } + else + { + newLocations.AddRange(ExpandLocationFormats(extraAreaViewLocations, viewType)); + } + + areaLocations = newLocations.ToArray(); + } + } + + var viewLocations = GetViewLocations(locations, (usingAreas) ? areaLocations : null); + + if (viewLocations.Count == 0) + { + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Properties cannot be null or empty.", new object[] { locationsPropertyName })); + } + + bool nameRepresentsPath = IsSpecificPath(name); + string cacheKey = CreateCacheKey(cacheKeyPrefix, name, (nameRepresentsPath) ? String.Empty : controllerName, areaName, theme); + + if (useCache) + { + // Only look at cached display modes that can handle the context. + var possibleDisplayModes = DisplayModeProvider.GetAvailableDisplayModesForContext(controllerContext.HttpContext, controllerContext.DisplayMode); + foreach (var displayMode in possibleDisplayModes) + { + string cachedLocation = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, displayMode.DisplayModeId)); + + if (cachedLocation == null) + { + // If any matching display mode location is not in the cache, fall back to the uncached behavior, which will repopulate all of our caches. + return null; + } + + // A non-empty cachedLocation indicates that we have a matching file on disk. Return that result. + if (cachedLocation.Length > 0) + { + if (controllerContext.DisplayMode == null) + { + controllerContext.DisplayMode = displayMode; + } + + return cachedLocation; + } + // An empty cachedLocation value indicates that we don't have a matching file on disk. Keep going down the list of possible display modes. + } + + // ResolveViewPath is called again without using the cache. + return null; + } + else + { + return nameRepresentsPath + ? GetPathFromSpecificName(controllerContext, name, cacheKey, ref searchedLocations) + : GetPathFromGeneralName(controllerContext, viewLocations, name, controllerName, areaName, theme, cacheKey, ref searchedLocations); + } + } + + protected virtual string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, ref string[] searchedLocations) + { + string result = name; + + if (!(FilePathIsSupported(name) && FileExists(controllerContext, name))) + { + result = String.Empty; + searchedLocations = new[] { name }; + } + + ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, result); + return result; + } + + protected virtual string GetPathFromGeneralName( + ControllerContext controllerContext, + List locations, + string name, + string controllerName, + string areaName, + string theme, + string cacheKey, + ref string[] searchedLocations) + { + string result = String.Empty; + searchedLocations = new string[locations.Count]; + + for (int i = 0; i < locations.Count; i++) + { + ViewLocation location = locations[i]; + string virtualPath = location.Format(name, controllerName, areaName, theme); + DisplayInfo virtualPathDisplayInfo = DisplayModeProvider.GetDisplayInfoForVirtualPath(virtualPath, controllerContext.HttpContext, path => FileExists(controllerContext, path), controllerContext.DisplayMode); + + if (virtualPathDisplayInfo != null) + { + string resolvedVirtualPath = virtualPathDisplayInfo.FilePath; + + searchedLocations = _emptyLocations; + result = resolvedVirtualPath; + ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, GetDisplayModeId(virtualPathDisplayInfo)), result); + + if (controllerContext.DisplayMode == null) + { + controllerContext.DisplayMode = virtualPathDisplayInfo.DisplayMode; + } + + // Populate the cache for all other display modes. We want to cache both file system hits and misses so that we can distinguish + // in future requests whether a file's status was evicted from the cache (null value) or if the file doesn't exist (empty string). + IEnumerable allDisplayModes = DisplayModeProvider.Modes; + foreach (IDisplayMode displayMode in allDisplayModes) + { + if (displayMode.DisplayModeId != virtualPathDisplayInfo.DisplayMode.DisplayModeId) + { + DisplayInfo displayInfoToCache = displayMode.GetDisplayInfo(controllerContext.HttpContext, virtualPath, virtualPathExists: path => FileExists(controllerContext, path)); + + string displayModeId = String.Empty; + string cacheValue = String.Empty; + if (displayInfoToCache != null && displayInfoToCache.FilePath != null) + { + cacheValue = displayInfoToCache.FilePath; + displayModeId = GetDisplayModeId(displayInfoToCache); + } + else + { + displayModeId = displayMode.DisplayModeId; + } + + ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, AppendDisplayModeToCacheKey(cacheKey, displayModeId), cacheValue); + } + } + break; + } + + searchedLocations[i] = virtualPath; + } + + return result; + } + + protected virtual bool FilePathIsSupported(string virtualPath) + { + if (this.FileExtensions == null) + { + // legacy behavior for custom ViewEngine that might not set the FileExtensions property + return true; + } + + // get rid of the '.' because the FileExtensions property expects extensions withouth a dot. + string extension = GetExtensionThunk(virtualPath).TrimStart('.'); + return FileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase); + } + + private string GetDisplayModeId(DisplayInfo displayInfo) + { + var localizedDisplayInfo = displayInfo as LocalizedDisplayInfo; + + return localizedDisplayInfo != null + ? localizedDisplayInfo.DisplayModeId + : displayInfo.DisplayMode.DisplayModeId; + } + + protected virtual string CreateCacheKey(string prefix, string name, string controllerName, string areaName, string theme/*, string lang*/) + { + return string.Format(_cacheKeyEntry, + _cacheKeyType, + prefix, + name, + controllerName, + areaName, + theme); + } + + internal static string AppendDisplayModeToCacheKey(string cacheKey, string displayMode) + { + // key format is ":ViewCacheEntry:{cacheType}:{prefix}:{name}:{controllerName}:{areaName}:{theme}" + // so append ":{displayMode}" to the key + return string.IsNullOrWhiteSpace(displayMode) + ? cacheKey + : cacheKey + ":" + displayMode; + } + + protected virtual IEnumerable ExpandLocationFormats(IEnumerable formats, ViewType viewType) + { + // Appends razor view file extensions to location formats + Guard.NotNull(formats, nameof(formats)); + + var subfolder = viewType == ViewType.Layout ? "Layouts" : "Partials"; + + foreach (var format in formats) + { + if (viewType > ViewType.View) + { + yield return format.Replace("{0}", subfolder + "/{0}.cshtml"); + + if (EnableVbViews) + { + yield return format.Replace("{0}", subfolder + "/{0}.vbhtml"); + } + } + + yield return format + ".cshtml"; + + if (EnableVbViews) + { + yield return format + ".vbhtml"; + } + } + } + + protected virtual List GetViewLocations(string[] viewLocationFormats, string[] areaViewLocationFormats) + { + List locations = new List( + (viewLocationFormats?.Length ?? 0) + + (areaViewLocationFormats?.Length ?? 0)); + + if (areaViewLocationFormats != null) + { + foreach (string areaViewLocationFormat in areaViewLocationFormats) + { + locations.Add(new AreaAwareViewLocation(areaViewLocationFormat)); + } + } + + if (viewLocationFormats != null) + { + foreach (string viewLocationFormat in viewLocationFormats) + { + locations.Add(new ViewLocation(viewLocationFormat)); + } + } + + return locations; + } + + protected virtual bool IsSpecificPath(string name) + { + char c = name[0]; + return (c == '~' || c == '/'); + } + + protected virtual bool IsMobileDevice(HttpContextBase httpContext) + { + var mobileDeviceHelper = EngineContext.Current.Resolve(); + var result = mobileDeviceHelper.IsMobileDevice(); + return result; + } + + protected virtual string GetCurrentThemeName(ControllerContext controllerContext) + { + var theme = EngineContext.Current.Resolve().CurrentTheme; + return theme.ThemeName; + } + + public enum ViewType + { + View, + Layout, + Partial + } + } + + public class AreaAwareViewLocation : ViewLocation + { + public AreaAwareViewLocation(string virtualPathFormatString) + : base(virtualPathFormatString) + { + } + + public override string Format(string viewName, string controllerName, string areaName, string theme) + { + return string.Format(_virtualPathFormatString, + viewName, + controllerName, + areaName, + theme); + } + } + + public class ViewLocation + { + protected readonly string _virtualPathFormatString; + + public ViewLocation(string virtualPathFormatString) + { + _virtualPathFormatString = virtualPathFormatString; + } + + public virtual string Format(string viewName, string controllerName, string areaName, string theme) + { + return string.Format(_virtualPathFormatString, + viewName, + controllerName, + theme); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/ThemingVirtualPathProvider.cs b/src/Presentation/SmartStore.Web.Framework/Theming/ThemingVirtualPathProvider.cs index cef62c8959..78195934d9 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/ThemingVirtualPathProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/ThemingVirtualPathProvider.cs @@ -8,154 +8,153 @@ using SmartStore.Core.Infrastructure; using SmartStore.Core.Themes; using SmartStore.Utilities; -using SmartStore.Web.Framework.Theming.Assets; namespace SmartStore.Web.Framework.Theming { public class ThemingVirtualPathProvider : SmartVirtualPathProvider { - private readonly VirtualPathProvider _previous; - private static readonly ContextState> _requestState; + private readonly VirtualPathProvider _previous; + private static readonly ContextState> _requestState; - static ThemingVirtualPathProvider() - { - _requestState = new ContextState>("ThemeFileResolver.RequestCache", () => new Dictionary()); - } + static ThemingVirtualPathProvider() + { + _requestState = new ContextState>("ThemeFileResolver.RequestCache", () => new Dictionary()); + } - public ThemingVirtualPathProvider(VirtualPathProvider previous) + public ThemingVirtualPathProvider(VirtualPathProvider previous) { _previous = previous; - } + } public override bool FileExists(string virtualPath) { - var result = GetResolveResult(virtualPath); - if (result != null) - { - if (!result.IsBased) - { - return true; - } - else - { - if (result.Query.HasValue() && result.Query.IndexOf('.') >= 0) - { - // libSass tries to locate files by appending .[s]css extension to our querystring. Prevent this shit! - return false; - } - else - { - // Let system VPP check for this file - virtualPath = result.ResultVirtualPath ?? result.OriginalVirtualPath; - } - } - } - - return _previous.FileExists(virtualPath); + var result = GetResolveResult(virtualPath); + if (result != null) + { + if (!result.IsBased) + { + return true; + } + else + { + if (result.Query.HasValue() && result.Query.IndexOf('.') >= 0) + { + // libSass tries to locate files by appending .[s]css extension to our querystring. Prevent this shit! + return false; + } + else + { + // Let system VPP check for this file + virtualPath = result.ResultVirtualPath ?? result.OriginalVirtualPath; + } + } + } + + return _previous.FileExists(virtualPath); } - + public override VirtualFile GetFile(string virtualPath) { - VirtualFile file = null; - string debugPath = null; - - var result = GetResolveResult(virtualPath); - if (result != null) - { - // File is an inherited theme file. Set the result virtual path. - virtualPath = result.ResultVirtualPath ?? result.OriginalVirtualPath; - if (!result.IsBased) - { - file = new InheritedVirtualThemeFile(result); - } - } - - if (result == null || file is InheritedVirtualThemeFile) - { - // Handle plugin and symlinked theme folders in debug mode. - debugPath = ResolveDebugFilePath(virtualPath); - if (debugPath != null) - { - file = new DebugVirtualFile(file?.VirtualPath ?? virtualPath, debugPath); - } - } - - return file ?? _previous.GetFile(virtualPath); + VirtualFile file = null; + string debugPath = null; + + var result = GetResolveResult(virtualPath); + if (result != null) + { + // File is an inherited theme file. Set the result virtual path. + virtualPath = result.ResultVirtualPath ?? result.OriginalVirtualPath; + if (!result.IsBased) + { + file = new InheritedVirtualThemeFile(result); + } + } + + if (result == null || file is InheritedVirtualThemeFile) + { + // Handle plugin and symlinked theme folders in debug mode. + debugPath = ResolveDebugFilePath(virtualPath); + if (debugPath != null) + { + file = new DebugVirtualFile(file?.VirtualPath ?? virtualPath, debugPath); + } + } + + return file ?? _previous.GetFile(virtualPath); + } + + public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies) + { + if (virtualPathDependencies == null) + { + return _previous.GetFileHash(virtualPath, virtualPathDependencies); + } + + var fileNames = MapDependencyPaths(virtualPathDependencies.Cast(), out _); + var combiner = HashCodeCombiner.Start(); + + foreach (var fileName in fileNames) + { + combiner.Add(new FileInfo(fileName)); + } + + return combiner.CombinedHashString; + } + + public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart) + { + if (virtualPathDependencies == null) + { + return null; + } + + var mappedPaths = MapDependencyPaths(virtualPathDependencies.Cast(), out var cacheKeys); + + return new CacheDependency(mappedPaths, cacheKeys, utcStart); + } + + /// + /// Maps virtual to physical paths. Used to compute cache dependecies and file hashes. + /// + internal string[] MapDependencyPaths(IEnumerable virtualPathDependencies, out string[] cacheKeys) + { + cacheKeys = null; + + var mappedPaths = new HashSet(StringComparer.OrdinalIgnoreCase); + var cacheKeySet = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (var dep in virtualPathDependencies) + { + var file = GetFile(dep); + + if (file is IFileDependencyProvider provider) + { + provider.AddFileDependencies(mappedPaths, cacheKeySet); + } + else if (file != null) + { + mappedPaths.Add(HostingEnvironment.MapPath(file.VirtualPath)); + } + } + + cacheKeys = cacheKeySet.ToArray(); + + var paths = mappedPaths.ToArray(); + Array.Sort(paths); + + return paths; } - public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies) - { - if (virtualPathDependencies == null) - { - return _previous.GetFileHash(virtualPath, virtualPathDependencies); - } - - var fileNames = MapDependencyPaths(virtualPathDependencies.Cast(), out _); - var combiner = HashCodeCombiner.Start(); - - foreach (var fileName in fileNames) - { - combiner.Add(new FileInfo(fileName)); - } - - return combiner.CombinedHashString; - } - - public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart) - { - if (virtualPathDependencies == null) - { - return null; - } - - var mappedPaths = MapDependencyPaths(virtualPathDependencies.Cast(), out var cacheKeys); - - return new CacheDependency(mappedPaths, cacheKeys, utcStart); - } - - /// - /// Maps virtual to physical paths. Used to compute cache dependecies and file hashes. - /// - internal string[] MapDependencyPaths(IEnumerable virtualPathDependencies, out string[] cacheKeys) - { - cacheKeys = null; - - var mappedPaths = new HashSet(StringComparer.OrdinalIgnoreCase); - var cacheKeySet = new HashSet(StringComparer.OrdinalIgnoreCase); - - foreach (var dep in virtualPathDependencies) - { - var file = GetFile(dep); - - if (file is IFileDependencyProvider provider) - { - provider.AddFileDependencies(mappedPaths, cacheKeySet); - } - else if (file != null) - { - mappedPaths.Add(HostingEnvironment.MapPath(file.VirtualPath)); - } - } - - cacheKeys = cacheKeySet.ToArray(); - - var paths = mappedPaths.ToArray(); - Array.Sort(paths); - - return paths; - } - - private static InheritedThemeFileResult GetResolveResult(string virtualPath) - { - var d = _requestState.GetState(); - - if (!d.TryGetValue(virtualPath, out var result)) - { - result = d[virtualPath] = EngineContext.Current.Resolve().Resolve(virtualPath); - } - - return result; - } + private static InheritedThemeFileResult GetResolveResult(string virtualPath) + { + var d = _requestState.GetState(); + + if (!d.TryGetValue(virtualPath, out var result)) + { + result = d[virtualPath] = EngineContext.Current.Resolve().Resolve(virtualPath); + } + + return result; + } } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/TwoLevelViewLocationCache.cs b/src/Presentation/SmartStore.Web.Framework/Theming/TwoLevelViewLocationCache.cs index 165e3b194d..831cc4a036 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/TwoLevelViewLocationCache.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/TwoLevelViewLocationCache.cs @@ -6,53 +6,53 @@ namespace SmartStore.Web.Framework.Theming { - public class TwoLevelViewLocationCache : IViewLocationCache - { - private static readonly object s_key = new object(); - private readonly IViewLocationCache _innerCache; + public class TwoLevelViewLocationCache : IViewLocationCache + { + private static readonly object s_key = new object(); + private readonly IViewLocationCache _innerCache; - public TwoLevelViewLocationCache() - { - var cacheDuringDebug = CommonHelper.GetAppSetting("sm:EnableViewLocationCacheDuringDebug"); + public TwoLevelViewLocationCache() + { + var cacheDuringDebug = CommonHelper.GetAppSetting("sm:EnableViewLocationCacheDuringDebug"); - if (!cacheDuringDebug && (HttpContext.Current == null || HttpContext.Current.IsDebuggingEnabled)) - { - _innerCache = DefaultViewLocationCache.Null; - } - else - { - _innerCache = new DefaultViewLocationCache(TimeSpan.FromHours(2)); - } + if (!cacheDuringDebug && (HttpContext.Current == null || HttpContext.Current.IsDebuggingEnabled)) + { + _innerCache = DefaultViewLocationCache.Null; + } + else + { + _innerCache = new DefaultViewLocationCache(TimeSpan.FromHours(2)); + } - } + } - private static IDictionary GetRequestCache(HttpContextBase httpContext) - { - var d = httpContext.Items[s_key] as IDictionary; - if (d == null) - { - d = new Dictionary(); - httpContext.Items[s_key] = d; - } - return d; - } + private static IDictionary GetRequestCache(HttpContextBase httpContext) + { + var d = httpContext.Items[s_key] as IDictionary; + if (d == null) + { + d = new Dictionary(); + httpContext.Items[s_key] = d; + } + return d; + } - public string GetViewLocation(HttpContextBase httpContext, string key) - { - var d = GetRequestCache(httpContext); - string location; - if (!d.TryGetValue(key, out location)) - { - location = _innerCache.GetViewLocation(httpContext, key); - d[key] = location; - } - return location; - } + public string GetViewLocation(HttpContextBase httpContext, string key) + { + var d = GetRequestCache(httpContext); + string location; + if (!d.TryGetValue(key, out location)) + { + location = _innerCache.GetViewLocation(httpContext, key); + d[key] = location; + } + return location; + } - public void InsertViewLocation(HttpContextBase httpContext, string key, string virtualPath) - { - _innerCache.InsertViewLocation(httpContext, key, virtualPath); - GetRequestCache(httpContext)[key] = virtualPath; - } - } + public void InsertViewLocation(HttpContextBase httpContext, string key, string virtualPath) + { + _innerCache.InsertViewLocation(httpContext, key, virtualPath); + GetRequestCache(httpContext)[key] = virtualPath; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPage.cs b/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPage.cs index 63cf24718d..7fe8956162 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPage.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPage.cs @@ -16,120 +16,59 @@ namespace SmartStore.Web.Framework.Theming { public abstract class WebViewPage : System.Web.Mvc.WebViewPage { - private WebViewPageHelper _helper; + private WebViewPageHelper _helper; - protected int CurrentCategoryId - { - get - { - return _helper.CurrentCategoryId; - } - } + protected string CurrentPageType => _helper.CurrentPageType; - protected int CurrentManufacturerId - { - get - { - return _helper.CurrentManufacturerId; - } - } + protected object CurrentPageId => _helper.CurrentPageId; - protected int CurrentProductId - { - get - { - return _helper.CurrentProductId; - } - } + protected int CurrentCategoryId => _helper.CurrentPageType == "category" ? _helper.CurrentPageId.Convert() : 0; - protected bool IsHomePage - { - get - { - return _helper.IsHomePage; - } - } + protected int CurrentManufacturerId => _helper.CurrentPageType == "manufacturer" ? _helper.CurrentPageId.Convert() : 0; - protected bool IsMobileDevice - { - get - { - return _helper.IsMobileDevice; - } - } + protected int CurrentProductId => _helper.CurrentPageType == "product" ? _helper.CurrentPageId.Convert() : 0; - protected bool IsStoreClosed - { - get - { - return _helper.IsStoreClosed; - } - } + protected int CurrentTopicId => _helper.CurrentPageType == "topic" ? _helper.CurrentPageId.Convert() : 0; - public bool EnableHoneypotProtection - { - get { return _helper.EnableHoneypotProtection; } - } + protected bool IsHomePage => _helper.IsHomePage; - protected bool HasMessages - { - get - { - return _helper.ResolveNotifications(null).Any(); - } - } + protected bool IsMobileDevice => _helper.IsMobileDevice; - protected ICollection GetMessages(NotifyType type) - { - return _helper.ResolveNotifications(type).AsReadOnly(); - } + protected bool IsStoreClosed => _helper.IsStoreClosed; + + public bool EnableHoneypotProtection => _helper.EnableHoneypotProtection; + + public string FileManagerUrl => _helper.FileManagerUrl; + + protected bool HasMessages => _helper.ResolveNotifications(null).Any(); + + protected ICollection GetMessages(NotifyType type) + { + return _helper.ResolveNotifications(type).AsReadOnly(); + } /// /// Get a localized resource /// - public Localizer T - { - get - { - return _helper.T; - } - } + public Localizer T => _helper.T; - public ICommonServices CommonServices - { - get - { - return _helper.Services; - } - } + public ICommonServices CommonServices => _helper.Services; - public IWorkContext WorkContext - { - get - { - return _helper.Services.WorkContext; - } - } + public IWorkContext WorkContext => _helper.Services.WorkContext; - public ILinkResolver LinkResolver - { - get - { - return _helper.LinkResolver; - } - } + public ILinkResolver LinkResolver => _helper.LinkResolver; - public override void InitHelpers() + public override void InitHelpers() { base.InitHelpers(); - _helper = EngineContext.Current.Resolve(); - _helper.Initialize(this.ViewContext); + _helper = EngineContext.Current.Resolve(); + _helper.Initialize(this.ViewContext); } public HelperResult RenderWrappedSection(string name, object wrapperHtmlAttributes) { - Action action = delegate(TextWriter tw) + Action action = delegate (TextWriter tw) { var htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(wrapperHtmlAttributes); var tagBuilder = new TagBuilder("div"); @@ -170,34 +109,19 @@ public override string Layout return layout; } - set - { - base.Layout = value; - } + set => base.Layout = value; } - /// - /// Gets the manifest of the current active theme - /// - protected ThemeManifest ThemeManifest - { - get - { - return _helper.ThemeContext?.CurrentTheme; - } - } + /// + /// Gets the manifest of the current active theme + /// + protected ThemeManifest ThemeManifest => _helper.ThemeContext?.CurrentTheme; /// /// Gets the current theme name. Override this in configuration views. /// [Obsolete("The theme name gets resolved automatically now. No need to override anymore.")] - protected virtual string ThemeName - { - get - { - return _helper.ThemeContext?.WorkingThemeName; - } - } + protected virtual string ThemeName => _helper.ThemeContext?.WorkingThemeName; public string GetThemeVariable(string varname, string defaultValue = "") { @@ -222,139 +146,150 @@ public string GetThemeVariable(string varname, string defaultValue = "") { return defaultValue; } - return (T)value.Convert(typeof(T)); + return value.Convert(); } return defaultValue; } - /// - /// Gets the runtime theme variables as specified in the theme's config file - /// alongside the merged user-defined variables - /// - public dynamic ThemeVariables - { - get - { - return _helper.ThemeVariables; - } - } - - /// - /// Modifies a URL (appends/updates a query string part and optionally removes another query string). - /// - /// The URL to modifiy. If null, the current page's URL is resolved. - /// The new query string part. - /// A query string name to remove. - /// The modified URL. - public string ModifyUrl(string url, string query, string removeQueryName = null) - { - var webHelper = _helper.Services?.WebHelper; - if (webHelper == null) - { - return url; - } - - url = url.NullEmpty() ?? webHelper.GetThisPageUrl(true); - var url2 = webHelper.ModifyQueryString(url, query, null); - - if (removeQueryName.HasValue()) - { - url2 = webHelper.RemoveQueryString(url2, removeQueryName); - } - - return url2; - } - - public string GenerateHelpUrl(HelpTopic topic) - { - var seoCode = WorkContext?.WorkingLanguage?.UniqueSeoCode; - if (seoCode.IsEmpty()) - { - return topic?.EnPath; - } - - return SmartStoreVersion.GenerateHelpUrl(seoCode, topic); - } - - public string GenerateHelpUrl(string path) - { - var seoCode = WorkContext?.WorkingLanguage?.UniqueSeoCode; - if (seoCode.IsEmpty()) - { - return path; - } - - return SmartStoreVersion.GenerateHelpUrl(seoCode, path); - } - - /// - /// Tries to find a matching localization file for a given culture in the following order - /// (assuming is 'de-DE', is 'lang-*.js' and is 'en-US'): - /// - /// Exact match > lang-de-DE.js - /// Neutral culture > lang-de.js - /// Any region for language > lang-de-CH.js - /// Exact match for fallback culture > lang-en-US.js - /// Neutral fallback culture > lang-en.js - /// Any region for fallback language > lang-en-GB.js - /// - /// - /// The ISO culture code to get a localization file for, e.g. 'de-DE' - /// The virtual path to search in - /// The pattern to match, e.g. 'lang-*.js'. The wildcard char MUST exist. - /// Optional. - /// Result - public LocalizationFileResolveResult ResolveLocalizationFile( - string culture, - string virtualPath, - string pattern, - string fallbackCulture = "en") - { - 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; - } - } + /// + /// Gets the runtime theme variables as specified in the theme's config file + /// alongside the merged user-defined variables + /// + public dynamic ThemeVariables => _helper.ThemeVariables; + + /// + /// Modifies a URL (appends/updates a query string part and optionally removes another query string). + /// + /// The URL to modifiy. If null, the current page's URL is resolved. + /// The new query string part. + /// A query string name to remove. + /// The modified URL. + public string ModifyUrl(string url, string query, string removeQueryName = null) + { + var webHelper = _helper.Services?.WebHelper; + if (webHelper == null) + { + return url; + } + + url = url.NullEmpty() ?? webHelper.GetThisPageUrl(true); + var url2 = webHelper.ModifyQueryString(url, query, null); + + if (removeQueryName.HasValue()) + { + url2 = webHelper.RemoveQueryString(url2, removeQueryName); + } + + return url2; + } + + public string GenerateHelpUrl(HelpTopic topic) + { + var seoCode = WorkContext?.WorkingLanguage?.UniqueSeoCode; + if (seoCode.IsEmpty()) + { + return topic?.EnPath; + } + + return SmartStoreVersion.GenerateHelpUrl(seoCode, topic); + } + + public string GenerateHelpUrl(string path) + { + var seoCode = WorkContext?.WorkingLanguage?.UniqueSeoCode; + if (seoCode.IsEmpty()) + { + return path; + } + + return SmartStoreVersion.GenerateHelpUrl(seoCode, path); + } + + /// + /// Tries to find a matching localization file for a given culture in the following order + /// (assuming is 'de-DE', is 'lang-*.js' and is 'en-US'): + /// + /// Exact match > lang-de-DE.js + /// Neutral culture > lang-de.js + /// Any region for language > lang-de-CH.js + /// Exact match for fallback culture > lang-en-US.js + /// Neutral fallback culture > lang-en.js + /// Any region for fallback language > lang-en-GB.js + /// + /// + /// The ISO culture code to get a localization file for, e.g. 'de-DE' + /// The virtual path to search in + /// The pattern to match, e.g. 'lang-*.js'. The wildcard char MUST exist. + /// Optional. + /// Result + public LocalizationFileResolveResult ResolveLocalizationFile( + string culture, + string virtualPath, + string pattern, + string fallbackCulture = "en") + { + 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 + /// The default value to return if item does not exist. + /// Result + public T GetMetadata(string name, T defaultValue) + { + if (TryGetMetadata(name, out var value)) + { + return value; + } + + return defaultValue; + } + + /// + /// 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/Theming/WebViewPageHelper.cs b/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPageHelper.cs index 912fe7c258..b8fa11ecfd 100644 --- a/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPageHelper.cs +++ b/src/Presentation/SmartStore.Web.Framework/Theming/WebViewPageHelper.cs @@ -1,151 +1,144 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Web.Mvc; +using SmartStore.Core.Domain; +using SmartStore.Core.Domain.Security; using SmartStore.Core.Localization; using SmartStore.Core.Logging; using SmartStore.Core.Themes; using SmartStore.Services; +using SmartStore.Services.Cms; using SmartStore.Services.Common; -using SmartStore.Web.Framework.Filters; -using SmartStore.Core.Domain; using SmartStore.Services.Customers; -using SmartStore.Core.Domain.Security; -using SmartStore.Services.Cms; +using SmartStore.Web.Framework.Filters; namespace SmartStore.Web.Framework.Theming { - public class WebViewPageHelper - { - private bool _initialized; - private ControllerContext _controllerContext; - private ExpandoObject _themeVars; - private ICollection _internalNotifications; - - private int? _currentCategoryId; - private int? _currentManufacturerId; - private int? _currentProductId; - - private bool? _isHomePage; - private bool? _isMobileDevice; + public class FileManagerUrlRequested + { + public UrlHelper UrlHelper { get; set; } + public string Url { get; set; } + } + + public class WebViewPageHelper + { + private bool _initialized; + private ControllerContext _controllerContext; + private ExpandoObject _themeVars; + private ICollection _internalNotifications; + + private string _currentPageType; + private object _currentPageId; + + private bool? _isHomePage; + private bool? _isMobileDevice; private bool? _isStoreClosed; - private bool? _enableHoneypot; - - public WebViewPageHelper() - { - T = NullLocalizer.Instance; - } - - public Localizer T { get; set; } - public ILocalizationFileResolver LocalizationFileResolver { get; set; } - public ICommonServices Services { get; set; } - public IThemeRegistry ThemeRegistry { get; set; } - public IThemeContext ThemeContext { get; set; } - public IMobileDeviceHelper MobileDeviceHelper { get; set; } - public ILinkResolver LinkResolver { get; set; } - - public void Initialize(ViewContext viewContext) - { - if (!_initialized) - { - _controllerContext = viewContext.GetRootControllerContext(); - _initialized = true; - } - } - - - public int CurrentCategoryId - { - get - { - if (!_currentCategoryId.HasValue) - { - int id = 0; - var routeValues = _controllerContext.RequestContext.RouteData.Values; - - if (routeValues["controller"].ToString().IsCaseInsensitiveEqual("catalog") - && routeValues["action"].ToString().IsCaseInsensitiveEqual("category") - && routeValues.ContainsKey("categoryId")) - { - id = Convert.ToInt32(routeValues["categoryId"].ToString()); - } - _currentCategoryId = id; - } - - return _currentCategoryId.Value; - } - } - - public int CurrentManufacturerId - { - get - { - if (!_currentManufacturerId.HasValue) - { - var routeValues = _controllerContext.RequestContext.RouteData.Values; - int id = 0; - if (routeValues["controller"].ToString().IsCaseInsensitiveEqual("catalog") - && routeValues["action"].ToString().IsCaseInsensitiveEqual("manufacturer") - && routeValues.ContainsKey("manufacturerId")) - { - id = Convert.ToInt32(routeValues["manufacturerId"].ToString()); - } - _currentManufacturerId = id; - } - - return _currentManufacturerId.Value; - } - } - - public int CurrentProductId - { - get - { - if (!_currentProductId.HasValue) - { - var routeValues = _controllerContext.RequestContext.RouteData.Values; - int id = 0; - if (routeValues["controller"].ToString().IsCaseInsensitiveEqual("product") - && routeValues["action"].ToString().IsCaseInsensitiveEqual("productdetails") - && routeValues.ContainsKey("productId")) - { - id = Convert.ToInt32(routeValues["productId"].ToString()); - } - _currentProductId = id; - } - - return _currentProductId.Value; - } - } - - public bool IsHomePage - { - get - { - if (!_isHomePage.HasValue) - { - var routeData = _controllerContext.RequestContext.RouteData; - _isHomePage = routeData.GetRequiredString("controller").IsCaseInsensitiveEqual("Home") && - routeData.GetRequiredString("action").IsCaseInsensitiveEqual("Index"); - } - - return _isHomePage.Value; - } - } - - public bool IsMobileDevice - { - get - { - if (!_isMobileDevice.HasValue) - { - _isMobileDevice = MobileDeviceHelper.IsMobileDevice(); - } - - return _isMobileDevice.Value; - } - } + private bool? _enableHoneypot; + private string _fileManagerUrl; + + public WebViewPageHelper() + { + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + public ILocalizationFileResolver LocalizationFileResolver { get; set; } + public ICommonServices Services { get; set; } + public IThemeRegistry ThemeRegistry { get; set; } + public IThemeContext ThemeContext { get; set; } + public IMobileDeviceHelper MobileDeviceHelper { get; set; } + public ILinkResolver LinkResolver { get; set; } + public UrlHelper UrlHelper { get; set; } + + public void Initialize(ControllerContext controllerContext) + { + if (!_initialized) + { + _controllerContext = controllerContext.GetRootControllerContext(); + IdentifyPage(); + _initialized = true; + } + } + + public string CurrentPageType => _currentPageType; + + public object CurrentPageId => _currentPageId; + + private void IdentifyPage() + { + var routeData = _controllerContext.RequestContext.RouteData; + var controllerName = routeData.GetRequiredString("controller").ToLowerInvariant(); + var actionName = routeData.GetRequiredString("action").ToLowerInvariant(); + + _currentPageType = "system"; + _currentPageId = controllerName + "." + actionName; + + if (IsHomePage) + { + _currentPageType = "home"; + _currentPageId = 0; + } + else if (controllerName == "catalog") + { + if (actionName == "category") + { + _currentPageType = "category"; + _currentPageId = routeData.Values.Get("categoryId"); + } + else if (actionName == "manufacturer") + { + _currentPageType = "brand"; + _currentPageId = routeData.Values.Get("manufacturerId"); + } + } + else if (controllerName == "product") + { + if (actionName == "productdetails") + { + _currentPageType = "product"; + _currentPageId = routeData.Values.Get("productId"); + } + } + else if (controllerName == "topic") + { + if (actionName == "topicdetails") + { + _currentPageType = "topic"; + _currentPageId = routeData.Values.Get("topicId"); + } + } + } + + public bool IsHomePage + { + get + { + if (!_isHomePage.HasValue) + { + var routeData = _controllerContext.RequestContext.RouteData; + var response = _controllerContext.RequestContext.HttpContext.Response; + _isHomePage = response.StatusCode != 404 && + routeData.GetRequiredString("controller").IsCaseInsensitiveEqual("Home") && + routeData.GetRequiredString("action").IsCaseInsensitiveEqual("Index"); + } + + return _isHomePage.Value; + } + } + + public bool IsMobileDevice + { + get + { + if (!_isMobileDevice.HasValue) + { + _isMobileDevice = MobileDeviceHelper.IsMobileDevice(); + } + + return _isMobileDevice.Value; + } + } public bool IsStoreClosed { @@ -153,90 +146,112 @@ public bool IsStoreClosed { if (!_isStoreClosed.HasValue) { - var settings = Services.Settings.LoadSetting(Services.StoreContext.CurrentStore.Id); - _isStoreClosed = Services.WorkContext.CurrentCustomer.IsAdmin() && settings.StoreClosedAllowForAdmins ? false : settings.StoreClosed; + var settings = Services.Settings.LoadSetting(Services.StoreContext.CurrentStore.Id); + _isStoreClosed = Services.WorkContext.CurrentCustomer.IsAdmin() && settings.StoreClosedAllowForAdmins ? false : settings.StoreClosed; } return _isStoreClosed.Value; } } - public bool EnableHoneypotProtection - { - get - { - if (!_enableHoneypot.HasValue) - { - var settings = Services.Settings.LoadSetting(Services.StoreContext.CurrentStore.Id); - _enableHoneypot = settings.EnableHoneypotProtection; - } - - return _enableHoneypot.Value; - } - } - - public IEnumerable ResolveNotifications(NotifyType? type) - { - IEnumerable result = Enumerable.Empty(); - - if (_internalNotifications == null) - { - string key = NotifyAttribute.NotificationsKey; - ICollection entries; - - var tempData = _controllerContext.Controller.TempData; - if (tempData.ContainsKey(key)) - { - entries = tempData[key] as ICollection; - if (entries != null) - { - result = result.Concat(entries); - } - } - - var viewData = _controllerContext.Controller.ViewData; - if (viewData.ContainsKey(key)) - { - entries = viewData[key] as ICollection; - if (entries != null) - { - result = result.Concat(entries); - } - } - - _internalNotifications = new HashSet(result); - } - - if (type == null) - { - return _internalNotifications.Select(x => x.Message); - } - - return _internalNotifications.Where(x => x.Type == type.Value).Select(x => x.Message); - } - - public dynamic ThemeVariables - { - get - { - if (_themeVars == null) - { - var storeContext = Services?.StoreContext; - var themeManifest = ThemeContext?.CurrentTheme; + public bool EnableHoneypotProtection + { + get + { + if (!_enableHoneypot.HasValue) + { + var settings = Services.Settings.LoadSetting(Services.StoreContext.CurrentStore.Id); + _enableHoneypot = settings.EnableHoneypotProtection; + } + + return _enableHoneypot.Value; + } + } + + public string FileManagerUrl + { + get + { + if (_fileManagerUrl == null) + { + var defaultUrl = UrlHelper.Action("Index", "RoxyFileManager", new { area = "admin" }); + var message = new FileManagerUrlRequested + { + UrlHelper = UrlHelper, + Url = defaultUrl + }; + + Services.EventPublisher.Publish(message); + + _fileManagerUrl = message.Url ?? defaultUrl; + } + + return _fileManagerUrl; + } + } + + public IEnumerable ResolveNotifications(NotifyType? type) + { + IEnumerable result = Enumerable.Empty(); + + if (_internalNotifications == null) + { + string key = NotifyAttribute.NotificationsKey; + ICollection entries; + + var tempData = _controllerContext.Controller.TempData; + if (tempData.ContainsKey(key)) + { + entries = tempData[key] as ICollection; + if (entries != null) + { + result = result.Concat(entries); + } + } + + var viewData = _controllerContext.Controller.ViewData; + if (viewData.ContainsKey(key)) + { + entries = viewData[key] as ICollection; + if (entries != null) + { + result = result.Concat(entries); + } + } + + _internalNotifications = new HashSet(result); + } + + if (type == null) + { + return _internalNotifications.Select(x => x.Message); + } + + return _internalNotifications.Where(x => x.Type == type.Value).Select(x => x.Message); + } + + public dynamic ThemeVariables + { + get + { + if (_themeVars == null) + { + var storeContext = Services?.StoreContext; + var themeManifest = ThemeContext?.CurrentTheme; if (storeContext == null || themeManifest == null) - { - _themeVars = new ExpandoObject(); - } - else - { - var repo = new ThemeVarsRepository(); - _themeVars = repo.GetRawVariables(themeManifest.ThemeName, storeContext.CurrentStore.Id); - } - } - - return _themeVars; - } - } - } + { + _themeVars = new ExpandoObject(); + } + else + { + var repo = new ThemeVarsRepository(); + _themeVars = repo.GetRawVariables(themeManifest.ThemeName, storeContext.CurrentStore.Id); + } + } + + return _themeVars; + } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Breadcrumb.cs b/src/Presentation/SmartStore.Web.Framework/UI/Breadcrumb.cs index 43b71c4ea0..c6e9556940 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Breadcrumb.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Breadcrumb.cs @@ -1,43 +1,36 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace SmartStore.Web.Framework.UI { - public interface IBreadcrumb - { - void Track(MenuItem item, bool prepend = false); - IReadOnlyList Trail { get; } - } + public interface IBreadcrumb + { + void Track(MenuItem item, bool prepend = false); + IReadOnlyList Trail { get; } + } - public class DefaultBreadcrumb : IBreadcrumb - { - private List _trail; + public class DefaultBreadcrumb : IBreadcrumb + { + private List _trail; - public void Track(MenuItem item, bool prepend = false) - { - Guard.NotNull(item, nameof(item)); + public void Track(MenuItem item, bool prepend = false) + { + Guard.NotNull(item, nameof(item)); - if (_trail == null) - { - _trail = new List(); - } + if (_trail == null) + { + _trail = new List(); + } - if (prepend) - { - _trail.Insert(0, item); - } - else - { - _trail.Add(item); - } - } + if (prepend) + { + _trail.Insert(0, item); + } + else + { + _trail.Add(item); + } + } - public IReadOnlyList Trail - { - get - { - return _trail; - } - } - } + public IReadOnlyList Trail => _trail; + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/BundleBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/BundleBuilder.cs index 744553f48d..e6fba32534 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/BundleBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/BundleBuilder.cs @@ -5,13 +5,13 @@ using System.Text; using System.Web; using System.Web.Optimization; -using BundleTransformer.Core.Orderers; using BundleTransformer.Core.Bundles; +using BundleTransformer.Core.Orderers; using SmartStore.Core; -using SmartStore.Services.Seo; -using SmartStore.Web.Framework.Theming.Assets; using SmartStore.Core.Themes; +using SmartStore.Services.Seo; using SmartStore.Utilities.Threading; +using SmartStore.Web.Framework.Theming.Assets; namespace SmartStore.Web.Framework.UI { @@ -30,13 +30,13 @@ public class BundleBuilder : IBundleBuilder { private readonly IStoreContext _storeContext; private readonly IWorkContext _workContext; - private readonly IThemeContext _themeContext; + private readonly IThemeContext _themeContext; public BundleBuilder(IStoreContext storeContext, IWorkContext workContext, IThemeContext themeContext) { _storeContext = storeContext; _workContext = workContext; - _themeContext = themeContext; + _themeContext = themeContext; } public string Build(BundleType type, IEnumerable files) @@ -53,14 +53,14 @@ public string Build(BundleType type, IEnumerable files) bundleFor = BundleTable.Bundles.GetBundleFor(bundleVirtualPath); if (bundleFor == null) { - var nullOrderer = new NullOrderer(); + var nullOrderer = new NullOrderer(); - Bundle bundle = (type == BundleType.Script) ? + Bundle bundle = (type == BundleType.Script) ? new CustomScriptBundle(bundleVirtualPath) as Bundle : new SmartStyleBundle(bundleVirtualPath) as Bundle; - bundle.Orderer = nullOrderer; + bundle.Orderer = nullOrderer; - bundle.Include(files.ToArray()); + bundle.Include(files.ToArray()); BundleTable.Bundles.Add(bundle); } @@ -68,13 +68,13 @@ public string Build(BundleType type, IEnumerable files) } if (type == BundleType.Script) - { - return Scripts.Render(bundleVirtualPath).ToString(); + { + return Scripts.Render(bundleVirtualPath).ToString(); - //// Uncomment this if you want to bypass asset caching on mobile browsers - //return Scripts.RenderFormat("", - // files.Select(x => VirtualPathUtility.ToAppRelative(x)).ToArray()).ToString(); - } + //// Uncomment this if you want to bypass asset caching on mobile browsers + //return Scripts.RenderFormat("", + // files.Select(x => VirtualPathUtility.ToAppRelative(x)).ToArray()).ToString(); + } return Styles.Render(bundleVirtualPath).ToString(); } @@ -92,8 +92,8 @@ protected virtual string GetBundleVirtualPath(BundleType type, IEnumerable x.EndsWith(".scss", StringComparison.OrdinalIgnoreCase))) { hash += "-s" + _storeContext.CurrentStore.Id; - hash += "-t" + _themeContext.CurrentTheme.ThemeName; + hash += "-t" + _themeContext.CurrentTheme.ThemeName; } } // ensure only valid chars hash = SeoExtensions.GetSeName(hash); - - var sb = new StringBuilder(prefix); - sb.Append(hash); - sb.Append(postfix); - return sb.ToString(); + return prefix + hash + postfix; } } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Choices/ChoiceItemModel.cs b/src/Presentation/SmartStore.Web.Framework/UI/Choices/ChoiceItemModel.cs index 6811ceac29..58856b71c6 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Choices/ChoiceItemModel.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Choices/ChoiceItemModel.cs @@ -1,21 +1,22 @@ -using System; -using SmartStore.Web.Framework.Modelling; +using SmartStore.Web.Framework.Modelling; namespace SmartStore.Web.Framework.UI.Choices { - public abstract class ChoiceItemModel : EntityModelBase - { - public string Name { get; set; } - public string SeName { get; set; } - public string Alias { get; set; } - public string Color { get; set; } - public string PriceAdjustment { get; set; } - public decimal PriceAdjustmentValue { get; set; } - public int QuantityInfo { get; set; } - public bool IsPreSelected { get; set; } - public bool IsDisabled { get; set; } - public string ImageUrl { get; set; } + public abstract class ChoiceItemModel : EntityModelBase + { + public string Name { get; set; } + public string SeName { get; set; } + public string Title { get; set; } + public string Alias { get; set; } + public string Color { get; set; } + public string PriceAdjustment { get; set; } + public decimal PriceAdjustmentValue { get; set; } + public int QuantityInfo { get; set; } + public bool IsPreSelected { get; set; } + public bool IsDisabled { get; set; } + public bool IsUnavailable { get; set; } + public string ImageUrl { get; set; } - public abstract string GetItemLabel(); - } + public abstract string GetItemLabel(); + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Choices/ChoiceModel.cs b/src/Presentation/SmartStore.Web.Framework/UI/Choices/ChoiceModel.cs index 0b4b689452..a2b7832a02 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Choices/ChoiceModel.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Choices/ChoiceModel.cs @@ -1,91 +1,92 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Web.Mvc; using SmartStore.Core.Domain.Catalog; using SmartStore.Web.Framework.Modelling; namespace SmartStore.Web.Framework.UI.Choices { - public abstract class ChoiceModel : EntityModelBase - { - protected ChoiceModel() - { - this.AllowedFileExtensions = new List(); - this.Values = new List(); - } - - public AttributeControlType AttributeControlType { get; set; } - - public string Alias { get; set; } - - public string Name { get; set; } - - public string Description { get; set; } - - public string TextPrompt { get; set; } - - public bool IsRequired { get; set; } - - public bool IsDisabled { get; set; } - - /// - /// Allowed file extensions for customer uploaded files - /// - public IList AllowedFileExtensions { get; set; } - - /// - /// Selected value for textboxes - /// - public string TextValue { get; set; } - /// - /// Selected day value for datepicker - /// - public int? SelectedDay { get; set; } - /// - /// Selected month value for datepicker - /// - public int? SelectedMonth { get; set; } - /// - /// Selected year value for datepicker - /// - public int? SelectedYear { get; set; } - /// - /// Begin year for datepicker - /// - public int? BeginYear { get; set; } - /// - /// End year for datepicker - /// - public int? EndYear { get; set; } - - public string UploadedFileGuid { get; set; } - public string UploadedFileName { get; set; } - - public virtual IList Values { get; set; } - - public abstract string BuildControlId(); - - public virtual string GetLabel() - { - return TextPrompt.NullEmpty() ?? Name; - } - - public virtual string GetDescription() - { - var containsImg = Description.IsEmpty() ? false : Description.Contains("(); + Values = new List(); + } + + public AttributeControlType AttributeControlType { get; set; } + + public string Alias { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + + public string TextPrompt { get; set; } + + public string CustomData { get; set; } + + public bool IsRequired { get; set; } + + public bool IsDisabled { get; set; } + + /// + /// Allowed file extensions for customer uploaded files + /// + public IList AllowedFileExtensions { get; set; } + + /// + /// Selected value for textboxes + /// + public string TextValue { get; set; } + /// + /// Selected day value for datepicker + /// + public int? SelectedDay { get; set; } + /// + /// Selected month value for datepicker + /// + public int? SelectedMonth { get; set; } + /// + /// Selected year value for datepicker + /// + public int? SelectedYear { get; set; } + /// + /// Begin year for datepicker + /// + public int? BeginYear { get; set; } + /// + /// End year for datepicker + /// + public int? EndYear { get; set; } + + public string UploadedFileGuid { get; set; } + public string UploadedFileName { get; set; } + + public virtual IList Values { get; set; } + + public abstract string BuildControlId(); + + public virtual string GetLabel() + { + return TextPrompt.NullEmpty() ?? Name; + } + + public virtual string GetDescription() + { + var containsImg = Description.IsEmpty() ? false : Description.Contains(" _controlIdFn; - - public GenericChoiceModel(Func controlIdFn) - { - Guard.NotNull(controlIdFn, nameof(controlIdFn)); - - _controlIdFn = controlIdFn; - } - - public override string BuildControlId() - { - return _controlIdFn(this); - } - } - - public class GenericChoiceItemModel : ChoiceItemModel - { - private readonly Func _itemLabelFn; - - public GenericChoiceItemModel(Func itemLabelFn) - { - Guard.NotNull(itemLabelFn, nameof(itemLabelFn)); - - _itemLabelFn = itemLabelFn; - } - - public override string GetItemLabel() - { - return _itemLabelFn(this); - } - } + public class GenericChoiceModel : ChoiceModel + { + private readonly Func _controlIdFn; + + public GenericChoiceModel(Func controlIdFn) + { + Guard.NotNull(controlIdFn, nameof(controlIdFn)); + + _controlIdFn = controlIdFn; + } + + public override string BuildControlId() + { + return _controlIdFn(this); + } + } + + public class GenericChoiceItemModel : ChoiceItemModel + { + private readonly Func _itemLabelFn; + + public GenericChoiceItemModel(Func itemLabelFn) + { + Guard.NotNull(itemLabelFn, nameof(itemLabelFn)); + + _itemLabelFn = itemLabelFn; + } + + public override string GetItemLabel() + { + return _itemLabelFn(this); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/BoxSpacing.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/BoxSpacing.cs index 8b72005580..c447470207 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/BoxSpacing.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/BoxSpacing.cs @@ -1,26 +1,24 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using Newtonsoft.Json; namespace SmartStore.Web.Framework.UI { - public class BoxSpacing - { - [Range(0, 6)] - [JsonProperty("t")] - public byte? Top { get; set; } + public class BoxSpacing + { + [Range(0, 6)] + [JsonProperty("t")] + public byte? Top { get; set; } - [Range(0, 6)] - [JsonProperty("r")] - public byte? Right { get; set; } + [Range(0, 6)] + [JsonProperty("r")] + public byte? Right { get; set; } - [Range(0, 6)] - [JsonProperty("b")] - public byte? Bottom { get; set; } + [Range(0, 6)] + [JsonProperty("b")] + public byte? Bottom { get; set; } - [Range(0, 6)] - [JsonProperty("l")] - public byte? Left { get; set; } - } + [Range(0, 6)] + [JsonProperty("l")] + public byte? Left { get; set; } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Component.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Component.cs index 10806732cb..c983dee479 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Component.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Component.cs @@ -1,8 +1,7 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Web.Routing; -namespace SmartStore.Web.Framework.UI +namespace SmartStore.Web.Framework.UI { public abstract class Component : IUiComponent { @@ -13,16 +12,10 @@ protected Component() public string Id { - get - { - return !this.HtmlAttributes.ContainsKey("id") - ? this.Name + get => !this.HtmlAttributes.ContainsKey("id") + ? this.Name : (this.HtmlAttributes["id"] as string); - } - set - { - this.HtmlAttributes["id"] = value; - } + set => this.HtmlAttributes["id"] = value; } public string Name @@ -31,7 +24,7 @@ public string Name set; } - public IDictionary HtmlAttributes + public IDictionary HtmlAttributes { get; private set; @@ -39,12 +32,6 @@ public IDictionary HtmlAttributes - public virtual bool NameIsRequired - { - get - { - return false; - } - } - } + public virtual bool NameIsRequired => false; + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentBuilder.cs index bb034c7061..e6029660ad 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentBuilder.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; using System.Web; -using SmartStore.Utilities; -using SmartStore.Core.Infrastructure; using System.Web.Mvc; +using SmartStore.Core.Infrastructure; +using SmartStore.Utilities; namespace SmartStore.Web.Framework.UI { - public abstract class ComponentBuilder : IHtmlString, IHideObjectMembers + public abstract class ComponentBuilder : IHtmlString, IHideObjectMembers where TComponent : Component where TBuilder : ComponentBuilder { @@ -17,7 +17,7 @@ protected ComponentBuilder(TComponent component, HtmlHelper htmlHelper) { Guard.NotNull(component, nameof(component)); Guard.NotNull(htmlHelper, nameof(htmlHelper)); - + this.Component = component; this.HtmlHelper = htmlHelper; } @@ -68,39 +68,39 @@ private void EnrichRenderer(ComponentRenderer renderer) renderer.ViewData = this.HtmlHelper.ViewData; } - public TBuilder WithRenderer(ComponentRenderer instance) - { - Guard.NotNull(instance, nameof(instance)); + public TBuilder WithRenderer(ComponentRenderer instance) + { + Guard.NotNull(instance, nameof(instance)); - return this.WithRenderer>(instance); - } + return this.WithRenderer>(instance); + } - public TBuilder WithRenderer(ComponentRenderer instance) + public TBuilder WithRenderer(ComponentRenderer instance) where T : ComponentRenderer { Guard.NotNull(instance, nameof(instance)); - this.Renderer = instance; - return this as TBuilder; - } + this.Renderer = instance; + return this as TBuilder; + } - public TBuilder WithRenderer() - where T : ComponentRenderer - { - return this.WithRenderer(typeof(T)); - } + public TBuilder WithRenderer() + where T : ComponentRenderer + { + return this.WithRenderer(typeof(T)); + } - public TBuilder WithRenderer(Type rendererType) + public TBuilder WithRenderer(Type rendererType) { Guard.NotNull(rendererType, nameof(rendererType)); Guard.Implements>(rendererType); - if (Activator.CreateInstance(rendererType) is ComponentRenderer renderer) - { - this.Renderer = renderer; - } + if (Activator.CreateInstance(rendererType) is ComponentRenderer renderer) + { + this.Renderer = renderer; + } - return this as TBuilder; + return this as TBuilder; } public virtual TBuilder Name(string name) @@ -109,7 +109,7 @@ public virtual TBuilder Name(string name) return this as TBuilder; } - public virtual TBuilder HtmlAttributes(object attributes) + public virtual TBuilder HtmlAttributes(object attributes) { return this.HtmlAttributes(CommonHelper.ObjectToDictionary(attributes)); } @@ -120,24 +120,24 @@ public virtual TBuilder HtmlAttributes(IDictionary attributes) return this as TBuilder; } - public virtual TBuilder HtmlAttribute(string name, object value) - { - Guard.NotEmpty(name, nameof(name)); - Guard.NotNull(value, nameof(value)); + public virtual TBuilder HtmlAttribute(string name, object value) + { + Guard.NotEmpty(name, nameof(name)); + Guard.NotNull(value, nameof(value)); - this.Component.HtmlAttributes[name] = value; - return this as TBuilder; - } + this.Component.HtmlAttributes[name] = value; + return this as TBuilder; + } - public virtual TBuilder AddCssClass(string cssClass) - { - Guard.NotEmpty(cssClass, nameof(cssClass)); + public virtual TBuilder AddCssClass(string cssClass) + { + Guard.NotEmpty(cssClass, nameof(cssClass)); - this.Component.HtmlAttributes.AppendCssClass(cssClass); - return this as TBuilder; - } + this.Component.HtmlAttributes.AppendCssClass(cssClass); + return this as TBuilder; + } - public string ToHtmlString() + public string ToHtmlString() { return this.Renderer.ToHtmlString(); } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentFactory.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentFactory.cs index 455b58f2f3..a429f6128f 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentFactory.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentFactory.cs @@ -60,21 +60,21 @@ public virtual PagerBuilder Pager(IPageable model) return new PagerBuilder(new Pager(), this.HtmlHelper).Model(model); } - public virtual EntityPickerBuilder EntityPicker() - { - return new EntityPickerBuilder(new EntityPicker(), this.HtmlHelper); - } + public virtual EntityPickerBuilder EntityPicker() + { + return new EntityPickerBuilder(new EntityPicker(), this.HtmlHelper); + } - public virtual FileUploaderBuilder FileUploader() - { - return new FileUploaderBuilder(new FileUploader(), this.HtmlHelper); - } + public virtual FileUploaderBuilder FileUploader() + { + return new FileUploaderBuilder(new FileUploader(), this.HtmlHelper); + } public virtual MenuBuilder Menu() { return new MenuBuilder(new Menu(), this.HtmlHelper).WithRenderer(new MenuRenderer()); } - #endregion - } + #endregion + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentRenderer.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentRenderer.cs index 36d21bec1a..115ab3ca81 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentRenderer.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentRenderer.cs @@ -1,11 +1,11 @@ using System; -using System.Web.Mvc; +using System.IO; using System.Web; +using System.Web.Mvc; using System.Web.UI; -using System.IO; namespace SmartStore.Web.Framework.UI -{ +{ public abstract class ComponentRenderer : IHtmlString where TComponent : Component { protected ComponentRenderer() @@ -51,40 +51,40 @@ public virtual void VerifyState() } } - protected string SanitizeId(string id) - { - return id.SanitizeHtmlId(); - } + protected string SanitizeId(string id) + { + return id.SanitizeHtmlId(); + } - public virtual void Render() + public virtual void Render() { - WriteHtml(ViewContext.Writer); - } + WriteHtml(ViewContext.Writer); + } public virtual string ToHtmlString() { using (var stringWriter = new StringWriter()) { - WriteHtml(stringWriter); - var str = stringWriter.ToString(); - return str; - } - } + WriteHtml(stringWriter); + var str = stringWriter.ToString(); + return str; + } + } - protected void WriteHtml(TextWriter writer) - { - this.VerifyState(); - this.Component.Id = SanitizeId(this.Component.Id); + protected void WriteHtml(TextWriter writer) + { + this.VerifyState(); + this.Component.Id = SanitizeId(this.Component.Id); - using (var htmlWriter = new HtmlTextWriter(writer)) - { - WriteHtmlCore(htmlWriter); - } - } + using (var htmlWriter = new HtmlTextWriter(writer)) + { + WriteHtmlCore(htmlWriter); + } + } - protected virtual void WriteHtmlCore(HtmlTextWriter writer) - { - throw new NotImplementedException(); - } + protected virtual void WriteHtmlCore(HtmlTextWriter writer) + { + throw new NotImplementedException(); + } } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentRendererUtils.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentRendererUtils.cs index fdcc8c62e1..9e10d5c572 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentRendererUtils.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/ComponentRendererUtils.cs @@ -2,17 +2,27 @@ using System.Collections.Generic; namespace SmartStore.Web.Framework.UI -{ +{ public static class ComponentRendererUtils { - public static void AppendCssClass(this IDictionary attributes, string @class) + public static IDictionary AppendCssClass(this IDictionary attributes, Func cssClass) { - attributes.AppendInValue("class", " ", @class); + return attributes.AppendInValue("class", " ", cssClass()); } - public static void PrependCssClass(this IDictionary attributes, string @class) + public static IDictionary PrependCssClass(this IDictionary attributes, Func cssClass) { - attributes.PrependInValue("class", " ", @class); + return attributes.PrependInValue("class", " ", cssClass()); + } + + public static IDictionary AppendCssClass(this IDictionary attributes, string cssClass) + { + return attributes.AppendInValue("class", " ", cssClass); + } + + public static IDictionary PrependCssClass(this IDictionary attributes, string cssClass) + { + return attributes.PrependInValue("class", " ", cssClass); } } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPicker.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPicker.cs index fcca6a595b..12ed0951cb 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPicker.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPicker.cs @@ -2,48 +2,48 @@ { public class EntityPicker : Component { - public EntityPicker() + public EntityPicker() { - EntityType = "product"; - IconCssClass = "fa fa-search"; - HtmlAttributes["type"] = "button"; - HtmlAttributes.AppendCssClass("btn btn-secondary"); - HighlightSearchTerm = true; - AppendMode = true; - Delimiter = ","; - FieldName = "id"; - } - - public string EntityType { get; set; } + EntityType = "product"; + IconCssClass = "fa fa-search"; + HtmlAttributes["type"] = "button"; + HtmlAttributes.AppendCssClass("btn btn-secondary"); + HighlightSearchTerm = true; + AppendMode = true; + Delimiter = ","; + FieldName = "id"; + } + + public string EntityType { get; set; } public int LanguageId { get; set; } public string TargetInputSelector - { - get { return HtmlAttributes["data-target"] as string; } - set { HtmlAttributes["data-target"] = value; } - } - - public string Caption { get; set; } - public string IconCssClass { get; set; } - - public string DialogTitle { get; set; } - public string DialogUrl { get; set; } - - public bool DisableGroupedProducts { get; set; } - public bool DisableBundleProducts { get; set; } - public int[] DisabledEntityIds { get; set; } - public string[] Selected { get; set; } - - public bool EnableThumbZoomer { get; set; } - public bool HighlightSearchTerm { get; set; } - - public int MaxItems { get; set; } - public bool AppendMode { get; set; } - public string Delimiter { get; set; } - public string FieldName { get; set; } - - public string OnDialogLoadingHandlerName { get; set; } - public string OnDialogLoadedHandlerName { get; set; } - public string OnSelectionCompletedHandlerName { get; set; } - } + { + get => HtmlAttributes["data-target"] as string; + set => HtmlAttributes["data-target"] = value; + } + + public string Caption { get; set; } + public string IconCssClass { get; set; } + + public string DialogTitle { get; set; } + public string DialogUrl { get; set; } + + public bool DisableGroupedProducts { get; set; } + public bool DisableBundleProducts { get; set; } + public int[] DisabledEntityIds { get; set; } + public string[] Selected { get; set; } + + public bool EnableThumbZoomer { get; set; } + public bool HighlightSearchTerm { get; set; } + + public int MaxItems { get; set; } + public bool AppendMode { get; set; } + public string Delimiter { get; set; } + public string FieldName { get; set; } + + public string OnDialogLoadingHandlerName { get; set; } + public string OnDialogLoadedHandlerName { get; set; } + public string OnSelectionCompletedHandlerName { get; set; } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPickerBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPickerBuilder.cs index 78b3d4867b..842771f5d5 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPickerBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/EntityPicker/EntityPickerBuilder.cs @@ -10,22 +10,22 @@ public class EntityPickerBuilder : ComponentBuilder htmlHelper) : base(component, htmlHelper) { - WithRenderer(new ViewBasedComponentRenderer("EntityPicker")); - DialogUrl(UrlHelper.GenerateUrl( - null, - "Picker", - "Entity", - new RouteValueDictionary { { "area", "" } }, - RouteTable.Routes, - htmlHelper.ViewContext.RequestContext, - false)); - } - - public EntityPickerBuilder EntityType(string value) - { - base.Component.EntityType = value; - return this; - } + WithRenderer(new ViewBasedComponentRenderer("EntityPicker")); + DialogUrl(UrlHelper.GenerateUrl( + null, + "Picker", + "Entity", + new RouteValueDictionary { { "area", "" } }, + RouteTable.Routes, + htmlHelper.ViewContext.RequestContext, + false)); + } + + public EntityPickerBuilder EntityType(string value) + { + base.Component.EntityType = value; + return this; + } public EntityPickerBuilder LanguageId(int value) { @@ -34,128 +34,128 @@ public EntityPickerBuilder LanguageId(int value) } public EntityPickerBuilder Caption(string value) - { - base.Component.Caption = value; - return this; - } - - public EntityPickerBuilder IconCssClass(string value) - { - base.Component.IconCssClass = value; - return this; - } - - public EntityPickerBuilder Tooltip(string value) - { - base.Component.HtmlAttributes["title"] = value; - return this; - } - - public EntityPickerBuilder DialogTitle(string value) - { - base.Component.DialogTitle = value; - return this; - } - - public EntityPickerBuilder DialogUrl(string value) - { - base.Component.DialogUrl = value; - return this; - } - - - public EntityPickerBuilder For(Expression> expression) - { - Guard.NotNull(expression, nameof(expression)); - - return For(ExpressionHelper.GetExpressionText(expression)); - } - - public EntityPickerBuilder For(string expression) - { - base.Component.TargetInputSelector = "#" + this.HtmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(expression); - return this; - } - - - public EntityPickerBuilder DisableGroupedProducts(bool value) - { - base.Component.DisableGroupedProducts = value; - return this; - } - - public EntityPickerBuilder DisableBundleProducts(bool value) - { - base.Component.DisableBundleProducts = value; - return this; - } - - public EntityPickerBuilder DisabledEntityIds(params int[] values) - { - base.Component.DisabledEntityIds = values; - return this; - } - - public EntityPickerBuilder Selected(params string[] values) - { - base.Component.Selected = values; - return this; - } - - public EntityPickerBuilder EnableThumbZoomer(bool value) - { - base.Component.EnableThumbZoomer = value; - return this; - } - - public EntityPickerBuilder HighlightSearchTerm(bool value) - { - base.Component.HighlightSearchTerm = value; - return this; - } - - - public EntityPickerBuilder MaxItems(int value) - { - base.Component.MaxItems = value; - return this; - } - - public EntityPickerBuilder AppendMode(bool value) - { - base.Component.AppendMode = value; - return this; - } - - public EntityPickerBuilder Delimiter(string value) - { - base.Component.Delimiter = value; - return this; - } - - public EntityPickerBuilder FieldName(string value) - { - base.Component.FieldName = value; - return this; - } - - - public EntityPickerBuilder OnDialogLoading(string handlerName) - { - base.Component.OnDialogLoadingHandlerName = handlerName; - return this; - } - - public EntityPickerBuilder OnDialogLoaded(string handlerName) - { - base.Component.OnDialogLoadedHandlerName = handlerName; - return this; - } - - public EntityPickerBuilder OnSelectionCompleted(string handlerName) - { - base.Component.OnSelectionCompletedHandlerName = handlerName; - return this; - } - } + { + base.Component.Caption = value; + return this; + } + + public EntityPickerBuilder IconCssClass(string value) + { + base.Component.IconCssClass = value; + return this; + } + + public EntityPickerBuilder Tooltip(string value) + { + base.Component.HtmlAttributes["title"] = value; + return this; + } + + public EntityPickerBuilder DialogTitle(string value) + { + base.Component.DialogTitle = value; + return this; + } + + public EntityPickerBuilder DialogUrl(string value) + { + base.Component.DialogUrl = value; + return this; + } + + + public EntityPickerBuilder For(Expression> expression) + { + Guard.NotNull(expression, nameof(expression)); + + return For(ExpressionHelper.GetExpressionText(expression)); + } + + public EntityPickerBuilder For(string expression) + { + base.Component.TargetInputSelector = "#" + this.HtmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(expression); + return this; + } + + + public EntityPickerBuilder DisableGroupedProducts(bool value) + { + base.Component.DisableGroupedProducts = value; + return this; + } + + public EntityPickerBuilder DisableBundleProducts(bool value) + { + base.Component.DisableBundleProducts = value; + return this; + } + + public EntityPickerBuilder DisabledEntityIds(params int[] values) + { + base.Component.DisabledEntityIds = values; + return this; + } + + public EntityPickerBuilder Selected(params string[] values) + { + base.Component.Selected = values; + return this; + } + + public EntityPickerBuilder EnableThumbZoomer(bool value) + { + base.Component.EnableThumbZoomer = value; + return this; + } + + public EntityPickerBuilder HighlightSearchTerm(bool value) + { + base.Component.HighlightSearchTerm = value; + return this; + } + + + public EntityPickerBuilder MaxItems(int value) + { + base.Component.MaxItems = value; + return this; + } + + public EntityPickerBuilder AppendMode(bool value) + { + base.Component.AppendMode = value; + return this; + } + + public EntityPickerBuilder Delimiter(string value) + { + base.Component.Delimiter = value; + return this; + } + + public EntityPickerBuilder FieldName(string value) + { + base.Component.FieldName = value; + return this; + } + + + public EntityPickerBuilder OnDialogLoading(string handlerName) + { + base.Component.OnDialogLoadingHandlerName = handlerName; + return this; + } + + public EntityPickerBuilder OnDialogLoaded(string handlerName) + { + base.Component.OnDialogLoadedHandlerName = handlerName; + return this; + } + + public EntityPickerBuilder OnSelectionCompleted(string handlerName) + { + base.Component.OnSelectionCompletedHandlerName = handlerName; + return this; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Enums.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Enums.cs index 75bb64220f..96094d1808 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Enums.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Enums.cs @@ -1,29 +1,27 @@ -using System; - -namespace SmartStore.Web.Framework.UI -{ +namespace SmartStore.Web.Framework.UI +{ public enum BadgeStyle { - Secondary, - Primary, - Success, - Info, - Warning, + Secondary, + Primary, + Success, + Info, + Warning, Danger, - Light, - Dark - } + Light, + Dark + } - public enum ButtonStyle - { - Secondary, - Primary, - Success, - Info, - Warning, - Danger, - Light, - Dark, - Link - } + public enum ButtonStyle + { + Secondary, + Primary, + Success, + Info, + Warning, + Danger, + Light, + Dark, + Link + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/FileUploader/FileUploader.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/FileUploader/FileUploader.cs index 15e4d6915f..12eb04dd6d 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/FileUploader/FileUploader.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/FileUploader/FileUploader.cs @@ -1,53 +1,63 @@ -using System; +using System.Collections.Generic; +using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Infrastructure; using SmartStore.Core.Localization; +using SmartStore.Services.Media; namespace SmartStore.Web.Framework.UI { public class FileUploader : Component { - public FileUploader() - : this(EngineContext.Current.Resolve()) - { - } + public FileUploader() + : this(EngineContext.Current.Resolve()) + { + } - public FileUploader(Localizer localizer) + public FileUploader(Localizer localizer) { - HtmlAttributes.AppendCssClass("fileupload"); - HtmlAttributes.Add("data-accept", "gif|jpe?g|png"); - HtmlAttributes.Add("data-show-remove-after-upload", "false"); - IconCssClass = "fa fa-upload"; - ButtonStyle = ButtonStyle.Secondary; - - if (localizer != null) - { - CancelText = localizer("Common.Fileuploader.Cancel"); - RemoveText = localizer("Common.Remove"); - UploadText = localizer("Common.Fileuploader.Upload"); - } - } + HtmlAttributes.AppendCssClass("fu-fileupload"); + HtmlAttributes.Add("data-accept", "gif,jpeg,jpg,png"); + HtmlAttributes.Add("data-show-remove-after-upload", "false"); + + if (localizer != null) + { + UploadText = localizer("Common.Fileuploader.Upload"); + } + } - public string UploadUrl - { - get { return HtmlAttributes["data-upload-url"] as string; } - set { HtmlAttributes["data-upload-url"] = value; } - } + public string Path { get; set; } = SystemAlbumProvider.Files; - public string IconCssClass { get; set; } - public ButtonStyle ButtonStyle { get; set; } - public bool ButtonOutlineStyle { get; set; } - public bool ShowRemoveButton { get; set; } - public bool Compact { get; set; } + public string UploadUrl + { + get => HtmlAttributes["data-upload-url"] as string; + set => HtmlAttributes["data-upload-url"] = value; + } - public string CancelText { get; set; } - public string RemoveText { get; set; } - public string UploadText { get; set; } + public string UploadText { get; set; } + public bool ShowRemoveButton { get; set; } + public bool ShowRemoveButtonAfterUpload { get; set; } + public bool ShowBrowseMedia { get; set; } + public bool HasTemplatePreview { get; set; } + public string ClickableElement { get; set; } + public bool DownloadEnabled { get; set; } - public string OnUploadingHandlerName { get; set; } - public string OnUploadCompletedHandlerName { get; set; } - public string OnErrorHandlerName { get; set; } - public string OnFileRemoveHandlerName { get; set; } - public string OnAbortedHandlerName { get; set; } - public string OnCompletedHandlerName { get; set; } - } + public bool Multifile { get; set; } + public string TypeFilter { get; set; } + public string PreviewContainerId { get; set; } + public int? MainFileId { get; set; } + public long? MaxFileSize { get; set; } + public string EntityType { get; set; } + public int EntityId { get; set; } + public string DeleteUrl { get; set; } + public string SortUrl { get; set; } + public string EntityAssignmentUrl { get; set; } + public IEnumerable UploadedFiles { get; set; } + public string OnUploadingHandlerName { get; set; } + public string OnUploadCompletedHandlerName { get; set; } + public string OnErrorHandlerName { get; set; } + public string OnFileRemoveHandlerName { get; set; } + public string OnAbortedHandlerName { get; set; } + public string OnCompletedHandlerName { get; set; } + public string OnMediaSelectedHandlerName { get; set; } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/FileUploader/FileUploaderBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/FileUploader/FileUploaderBuilder.cs index 2784575e45..e2d5dfb4a6 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/FileUploader/FileUploaderBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/FileUploader/FileUploaderBuilder.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Text; using System.Web.Mvc; -using System.Web.Routing; -using System.Web.WebPages; +using SmartStore.Core.Domain.Catalog; +using SmartStore.Core.Infrastructure; +using SmartStore.Services.Media; namespace SmartStore.Web.Framework.UI { @@ -14,118 +12,181 @@ public class FileUploaderBuilder : ComponentBuilder htmlHelper) : base(component, htmlHelper) { - WithRenderer(new ViewBasedComponentRenderer("FileUploader")); - } - - public FileUploaderBuilder UploadUrl(string value) - { - base.Component.UploadUrl = value; - return this; - } - - public FileUploaderBuilder IconCssClass(string value) - { - base.Component.IconCssClass = value; - return this; - } - - public FileUploaderBuilder ButtonStyle(ButtonStyle value) - { - base.Component.ButtonStyle = value; - return this; - } - - public FileUploaderBuilder ButtonOutlineStyle(bool value) - { - base.Component.ButtonOutlineStyle = value; - return this; - } - - public FileUploaderBuilder ShowRemoveButton(bool value) - { - base.Component.ShowRemoveButton = value; - return this; - } - - public FileUploaderBuilder ShowRemoveButtonAfterUpload(bool value) - { - base.Component.HtmlAttributes["data-show-remove-after-upload"] = value.ToString().ToLower(); - return this; - } - - public FileUploaderBuilder Compact(bool value) - { - base.Component.Compact = value; - return this; - } - - public FileUploaderBuilder AcceptedFileTypes(string value) - { - if (value.IsEmpty()) - { - if (base.Component.HtmlAttributes.ContainsKey("data-accept")) - base.Component.HtmlAttributes.Remove("data-accept"); - } - else - { - base.Component.HtmlAttributes["data-accept"] = value; - } - - return this; - } - - public FileUploaderBuilder CancelText(string value) - { - base.Component.CancelText = value; - return this; - } - - public FileUploaderBuilder RemoveText(string value) - { - base.Component.RemoveText = value; - return this; - } - - public FileUploaderBuilder UploadText(string value) - { - base.Component.UploadText = value; - return this; - } - - public FileUploaderBuilder OnUploadingHandlerName(string handlerName) - { - base.Component.OnUploadingHandlerName = handlerName; - return this; - } - - public FileUploaderBuilder OnUploadCompletedHandlerName(string handlerName) - { - base.Component.OnUploadCompletedHandlerName = handlerName; - return this; - } - - public FileUploaderBuilder OnErrorHandlerName(string handlerName) - { - base.Component.OnErrorHandlerName = handlerName; - return this; - } - - public FileUploaderBuilder OnFileRemoveHandlerName(string handlerName) - { - base.Component.OnFileRemoveHandlerName = handlerName; - return this; - } - - public FileUploaderBuilder OnAbortedHandlerName(string handlerName) - { - base.Component.OnAbortedHandlerName = handlerName; - return this; - } - - public FileUploaderBuilder OnCompletedHandlerName(string handlerName) - { - base.Component.OnCompletedHandlerName = handlerName; - return this; - } - } + WithRenderer(new ViewBasedComponentRenderer("FileUploader")); + TypeFilter("*"); + } + + public FileUploaderBuilder Path(string value) + { + base.Component.Path = value; + return this; + } + + public FileUploaderBuilder UploadUrl(string value) + { + base.Component.UploadUrl = value; + return this; + } + + public FileUploaderBuilder ShowRemoveButton(bool value) + { + base.Component.ShowRemoveButton = value; + return this; + } + + public FileUploaderBuilder ShowRemoveButtonAfterUpload(bool value) + { + base.Component.HtmlAttributes["data-show-remove-after-upload"] = value.ToString().ToLower(); + base.Component.ShowRemoveButtonAfterUpload = value; + return this; + } + + public FileUploaderBuilder ShowBrowseMedia(bool value) + { + base.Component.ShowBrowseMedia = value; + return this; + } + + public FileUploaderBuilder HasTemplatePreview(bool value) + { + base.Component.HasTemplatePreview = value; + return this; + } + + public FileUploaderBuilder DownloadEnabled(bool value) + { + base.Component.DownloadEnabled = value; + return this; + } + + public FileUploaderBuilder ClickableElement(string value) + { + base.Component.ClickableElement = value; + return this; + } + + public FileUploaderBuilder Multifile(bool value) + { + base.Component.Multifile = value; + return this; + } + + public FileUploaderBuilder TypeFilter(string value) + { + var mediaTypeResolver = EngineContext.Current.Resolve(); + var extensions = mediaTypeResolver.ParseTypeFilter(value); + + base.Component.HtmlAttributes["data-accept"] = "." + String.Join(",.", extensions); + base.Component.TypeFilter = value; + + return this; + } + + public FileUploaderBuilder PreviewContainerId(string value) + { + base.Component.PreviewContainerId = value; + return this; + } + + public FileUploaderBuilder MainFileId(int? value) + { + base.Component.MainFileId = value; + return this; + } + + /// + /// Sets the maximum file size. + /// Use "0" for an unlimited file size and "null" for the default size (MediaSettings.MaxUploadFileSize). + /// + /// Maximum file size in KB. + public FileUploaderBuilder MaxFileSize(long? value) + { + base.Component.MaxFileSize = value; + return this; + } + + public FileUploaderBuilder UploadedFiles(IEnumerable value) + { + base.Component.UploadedFiles = value; + return this; + } + + public FileUploaderBuilder EntityType(string value) + { + base.Component.EntityType = value; + return this; + } + + public FileUploaderBuilder EntityId(int value) + { + base.Component.EntityId = value; + return this; + } + + public FileUploaderBuilder DeleteUrl(string value) + { + base.Component.DeleteUrl = value; + return this; + } + + public FileUploaderBuilder SortUrl(string value) + { + base.Component.SortUrl = value; + return this; + } + + public FileUploaderBuilder EntityAssignmentUrl(string value) + { + base.Component.EntityAssignmentUrl = value; + return this; + } + + public FileUploaderBuilder UploadText(string value) + { + base.Component.UploadText = value; + return this; + } + + public FileUploaderBuilder OnUploadingHandlerName(string handlerName) + { + base.Component.OnUploadingHandlerName = handlerName; + return this; + } + + public FileUploaderBuilder OnUploadCompletedHandlerName(string handlerName) + { + base.Component.OnUploadCompletedHandlerName = handlerName; + return this; + } + + public FileUploaderBuilder OnErrorHandlerName(string handlerName) + { + base.Component.OnErrorHandlerName = handlerName; + return this; + } + + public FileUploaderBuilder OnFileRemoveHandlerName(string handlerName) + { + base.Component.OnFileRemoveHandlerName = handlerName; + return this; + } + + public FileUploaderBuilder OnAbortedHandlerName(string handlerName) + { + base.Component.OnAbortedHandlerName = handlerName; + return this; + } + + public FileUploaderBuilder OnCompletedHandlerName(string handlerName) + { + base.Component.OnCompletedHandlerName = handlerName; + return this; + } + + public FileUploaderBuilder OnMediaSelectedHandlerName(string handlerName) + { + base.Component.OnMediaSelectedHandlerName = handlerName; + return this; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/HtmlHelperExtensions.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/HtmlHelperExtensions.cs index 2d8e269b44..21a87bc743 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/HtmlHelperExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/HtmlHelperExtensions.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Web; using System.Web.Mvc; +using SmartStore.Utilities.ObjectPools; namespace SmartStore.Web.Framework.UI { @@ -14,54 +14,55 @@ public static ComponentFactory SmartStore(this HtmlHelper(helper); } - public static IHtmlString Attrs(this HtmlHelper html, IDictionary attrs) - { - var sb = new StringBuilder(); + public static IHtmlString Attrs(this HtmlHelper html, IDictionary attrs, params string[] exclude) + { + var sb = PooledStringBuilder.Rent(); - using (var writer = new StringWriter(sb)) - { - RenderAttrs(html, attrs, writer); - } + using (var writer = new StringWriter(sb)) + { + RenderAttrs(attrs, writer, exclude); + } - return MvcHtmlString.Create(sb.ToString()); - } + return MvcHtmlString.Create(sb.ToStringAndReturn()); + } - public static void RenderAttrs(this HtmlHelper html, IDictionary attrs) - { - RenderAttrs(html, attrs, html.ViewContext.Writer); - } + public static void RenderAttrs(this HtmlHelper html, IDictionary attrs, params string[] exclude) + { + RenderAttrs(attrs, html.ViewContext.Writer, exclude); + } - private static void RenderAttrs(HtmlHelper html, IDictionary attrs, TextWriter output) - { - Guard.NotNull(attrs, nameof(attrs)); + private static void RenderAttrs(IDictionary attrs, TextWriter output, params string[] exclude) + { + Guard.NotNull(attrs, nameof(attrs)); - if (attrs.Count == 0) - { - return; - } + if (attrs.Count == 0) + { + return; + } - var first = true; + var first = true; - foreach (var key in attrs.Keys) - { - var value = attrs[key]; + foreach (var kvp in attrs) + { + if (exclude.Contains(kvp.Key)) + continue; - if (value == null) - { - continue; - } + if (!first) + { + output.Write(" "); + } - if (!first) - { - output.Write(" "); - } + first = false; - first = false; + output.Write(kvp.Key); - output.Write(key + "=\""); - HttpUtility.HtmlAttributeEncode(value.ToString(), output); - output.Write("\""); - } - } - } + if (kvp.Value != null) + { + output.Write("=\""); + HttpUtility.HtmlAttributeEncode(kvp.Value.ToString(), output); + output.Write("\""); + } + } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/IContentContainer.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/IContentContainer.cs index 2f2753acfb..f252990485 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/IContentContainer.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/IContentContainer.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Web.WebPages; namespace SmartStore.Web.Framework.UI diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/IHtmlAttributesContainer.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/IHtmlAttributesContainer.cs index 31974274b4..e2e2135aa2 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/IHtmlAttributesContainer.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/IHtmlAttributesContainer.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace SmartStore.Web.Framework.UI { diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/IUiComponent.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/IUiComponent.cs index 7352829014..ad697d1bb7 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/IUiComponent.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/IUiComponent.cs @@ -1,6 +1,4 @@ -using System; - -namespace SmartStore.Web.Framework.UI +namespace SmartStore.Web.Framework.UI { public interface IUiComponent : IHtmlAttributesContainer { diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/AdminMenuProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/AdminMenuProvider.cs index ccbc2fbbd6..a62bfc760c 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/AdminMenuProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/AdminMenuProvider.cs @@ -1,27 +1,20 @@ -using System; -using SmartStore.Collections; +using SmartStore.Collections; namespace SmartStore.Web.Framework.UI { - public abstract class AdminMenuProvider : IMenuProvider - { - public void BuildMenu(TreeNode rootNode) - { - var pluginsNode = rootNode.SelectNodeById("plugins"); - BuildMenuCore(pluginsNode); - } + public abstract class AdminMenuProvider : IMenuProvider + { + public void BuildMenu(TreeNode rootNode) + { + var pluginsNode = rootNode.SelectNodeById("plugins"); + BuildMenuCore(pluginsNode); + } - protected abstract void BuildMenuCore(TreeNode pluginsNode); + protected abstract void BuildMenuCore(TreeNode pluginsNode); - public string MenuName - { - get { return "admin"; } - } + public string MenuName => "admin"; - public virtual int Ordinal - { - get { return 0; } - } + public virtual int Ordinal => 0; - } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/CatalogMenuProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/CatalogMenuProvider.cs index 4c3e8f10f3..b3e9177e82 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/CatalogMenuProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/CatalogMenuProvider.cs @@ -1,21 +1,14 @@ -using System; -using SmartStore.Collections; +using SmartStore.Collections; namespace SmartStore.Web.Framework.UI { - public abstract class CatalogMenuProvider : IMenuProvider - { - public abstract void BuildMenu(TreeNode rootNode); + public abstract class CatalogMenuProvider : IMenuProvider + { + public abstract void BuildMenu(TreeNode rootNode); - public string MenuName - { - get { return "catalog"; } - } + public string MenuName => "catalog"; - public virtual int Ordinal - { - get { return 0; } - } + public virtual int Ordinal => 0; - } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/IMenuProvider.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/IMenuProvider.cs index d48441cc0d..6ce5cb41c4 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/IMenuProvider.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/IMenuProvider.cs @@ -1,19 +1,18 @@ -using System; -using SmartStore.Collections; +using SmartStore.Collections; namespace SmartStore.Web.Framework.UI { - /// - /// Enables (plugins) developers to inject menu items to menus - /// - public interface IMenuProvider : IOrdered + /// + /// Enables (plugins) developers to inject menu items to menus + /// + public interface IMenuProvider : IOrdered { void BuildMenu(TreeNode rootNode); - /// - /// Gets the menu name to inject the menu items into (e.g. admin, catalog etc.) - /// - string MenuName { get; } + /// + /// Gets the menu name to inject the menu items into (e.g. admin, catalog etc.) + /// + string MenuName { get; } } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/Menu.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/Menu.cs index f17eae41e2..aa1088693e 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/Menu.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/Menu.cs @@ -6,6 +6,6 @@ public class Menu : Component public string Template { get; set; } - public RouteInfo Route { get; set; } + public RouteInfo Route { get; set; } } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuBuilder.cs index d3b2e75f5e..1703ee3988 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuBuilder.cs @@ -11,16 +11,16 @@ public MenuBuilder(Menu component, HtmlHelper htmlHelper) public MenuBuilder Template(string value) { - Component.Route = null; - Component.Template = value; + Component.Route = null; + Component.Template = value; return this; } - public MenuBuilder Template(string action, string controller, object routeValues = null) - { - Component.Template = null; - Component.Route = new RouteInfo(action, controller, routeValues); - return this; - } - } + public MenuBuilder Template(string action, string controller, object routeValues = null) + { + Component.Template = null; + Component.Route = new RouteInfo(action, controller, routeValues); + return this; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs index 6284b9b052..7a49919a7c 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuExtensions.cs @@ -12,15 +12,15 @@ namespace SmartStore.Web.Framework.UI { public static class MenuExtensions - { - public static IEnumerable> GetBreadcrumb(this TreeNode node) - { - Guard.NotNull(node, nameof(node)); + { + public static IEnumerable> GetBreadcrumb(this TreeNode node) + { + Guard.NotNull(node, nameof(node)); - return node.Trail.Where(x => !x.IsRoot); - } + return node.Trail.Where(x => !x.IsRoot); + } - public static string GetItemText(this TreeNode node, Localizer localizer) + public static string GetItemText(this TreeNode node, Localizer localizer) { string result = null; @@ -37,62 +37,62 @@ public static string GetItemText(this TreeNode node, Localizer localiz return result; } - /// - /// Gets the state of node within the passed currentPath, which is the navigation breadcrumb. - /// - /// The node to get the state for - /// The current path/breadcrumb - /// - /// enumeration indicating whether the node is in the current path (Selected or Expanded) - /// and whether it has children (Parent) - /// - public static NodePathState GetNodePathState(this TreeNode node, IEnumerable> currentPath) - { - return GetNodePathState(node, currentPath.Select(x => x.Value).ToList()); - } - - /// - /// Gets the state of node within the passed currentPath, which is the navigation breadcrumb. - /// - /// The node to get the state for - /// The current path/breadcrumb - /// - /// enumeration indicating whether the node is in the current path (Selected or Expanded) - /// and whether it has children (Parent) - /// - public static NodePathState GetNodePathState(this TreeNode node, IList currentPath) - { - Guard.NotNull(currentPath, nameof(currentPath)); - - var state = NodePathState.Unknown; - - if (node.HasChildren) - { - state = state | NodePathState.Parent; - } - - var lastInPath = currentPath.LastOrDefault(); - - if (currentPath.Count > 0) - { - if (node.Value.Equals(lastInPath)) - { - state = state | NodePathState.Selected; - } - else - { - if (node.Depth - 1 < currentPath.Count) - { - if (currentPath[node.Depth - 1].Equals(node.Value)) - { - state = state | NodePathState.Expanded; - } - } - } - } - - return state; - } + /// + /// Gets the state of node within the passed currentPath, which is the navigation breadcrumb. + /// + /// The node to get the state for + /// The current path/breadcrumb + /// + /// enumeration indicating whether the node is in the current path (Selected or Expanded) + /// and whether it has children (Parent) + /// + public static NodePathState GetNodePathState(this TreeNode node, IEnumerable> currentPath) + { + return GetNodePathState(node, currentPath.Select(x => x.Value).ToList()); + } + + /// + /// Gets the state of node within the passed currentPath, which is the navigation breadcrumb. + /// + /// The node to get the state for + /// The current path/breadcrumb + /// + /// enumeration indicating whether the node is in the current path (Selected or Expanded) + /// and whether it has children (Parent) + /// + public static NodePathState GetNodePathState(this TreeNode node, IList currentPath) + { + Guard.NotNull(currentPath, nameof(currentPath)); + + var state = NodePathState.Unknown; + + if (node.HasChildren) + { + state = state | NodePathState.Parent; + } + + var lastInPath = currentPath.LastOrDefault(); + + if (currentPath.Count > 0) + { + if (node.Value.Equals(lastInPath)) + { + state = state | NodePathState.Selected; + } + else + { + if (node.Depth - 1 < currentPath.Count) + { + if (currentPath[node.Depth - 1].Equals(node.Value)) + { + state = state | NodePathState.Expanded; + } + } + } + } + + return state; + } /// /// Applies serialized route informations to a tree node. @@ -136,16 +136,11 @@ public static MenuModel CreateModel(this IMenu menu, string template, Controller var model = new MenuModel { Name = menu.Name, - Template = template ?? menu.Name, - Root = menu.Root + Template = template ?? menu.Name, + Root = menu.Root, + SelectedNode = menu.ResolveCurrentNode(context) }; - var currentNode = menu.ResolveCurrentNode(context); - - model.Path = currentNode != null - ? currentNode.Trail.Where(x => !x.IsRoot).ToList() - : new List>(); - menu.ResolveElementCounts(model.SelectedNode, false); return model; @@ -171,6 +166,8 @@ public static TreeNode GetTree( return new TreeNode(new MenuItem()); } + var itemMap = items.ToMultimap(x => x.ParentItemId, x => x); + // Prepare root node. It represents the MenuRecord. var menu = items.First().Menu; var rootItem = new MenuItem @@ -194,11 +191,13 @@ void AddChildItems(TreeNode parentNode, int parentItemId) return; } - var entities = items.Where(x => x.ParentItemId == parentItemId).OrderBy(x => x.DisplayOrder); + var entities = itemMap.ContainsKey(parentItemId) + ? itemMap[parentItemId].OrderBy(x => x.DisplayOrder) + : Enumerable.Empty(); foreach (var entity in entities) { - if (entity.ProviderName.HasValue() && itemProviders.TryGetValue(entity.ProviderName, out var provider)) + if (!string.IsNullOrEmpty(entity.ProviderName) && itemProviders.TryGetValue(entity.ProviderName, out var provider)) { var newNode = provider.Value.Append(new MenuItemProviderRequest { diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItem.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItem.cs index bccc320ead..eded7876f0 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItem.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItem.cs @@ -11,12 +11,12 @@ public class MenuItem : NavigationItem, ICloneable public MenuItem() { this.Attributes = new Dictionary(StringComparer.OrdinalIgnoreCase); - } + } - /// - /// If this menu item refers to an entity, the id of the backed entity (like category, products e.g.) - /// - public int EntityId { get; set; } + /// + /// If this menu item refers to an entity, the id of the backed entity (like category, products e.g.) + /// + public int EntityId { get; set; } public string EntityName { get; set; } public int MenuItemId { get; set; } @@ -35,15 +35,12 @@ public string Id { if (_id == null) { - _id = CommonHelper.GenerateRandomDigitCode(10); + _id = CommonHelper.GenerateRandomDigitCode(10).TrimStart('0'); } return _id; } - set - { - _id = value; - } + set => _id = value; } public string ResKey { get; set; } @@ -54,7 +51,7 @@ public string Id public IDictionary Attributes { get; set; } - public MenuItemBuilder ToBuilder() + public MenuItemBuilder ToBuilder() { return new MenuItemBuilder(this); } @@ -64,14 +61,14 @@ public static implicit operator MenuItemBuilder(MenuItem menuItem) return menuItem.ToBuilder(); } - public MenuItem Clone() - { - return (MenuItem)this.MemberwiseClone(); - } + public MenuItem Clone() + { + return (MenuItem)this.MemberwiseClone(); + } - object ICloneable.Clone() - { - return this.Clone(); - } + object ICloneable.Clone() + { + return this.Clone(); + } } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItemBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItemBuilder.cs index 785aa05415..cda64e9c12 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItemBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuItemBuilder.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace SmartStore.Web.Framework.UI { diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuModel.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuModel.cs index b68a8dd6a9..3fddbd9200 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuModel.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuModel.cs @@ -5,28 +5,37 @@ namespace SmartStore.Web.Framework.UI { public class MenuModel - { - private TreeNode _selectedNode; - private bool _seekedSelectedNode; + { + private TreeNode _selectedNode; + private bool _seekedSelectedNode; public string Name { get; set; } - public string Template { get; set; } + public string Template { get; set; } - public TreeNode Root { get; set; } - public IList> Path { get; set; } + public TreeNode Root { get; set; } + public IList> Path { get; set; } - public TreeNode SelectedNode - { - get - { - if (!_seekedSelectedNode) - { - _selectedNode = Path?.LastOrDefault() ?? Root; - _seekedSelectedNode = true; - } + public TreeNode SelectedNode + { + get + { + if (!_seekedSelectedNode) + { + _selectedNode = Path?.LastOrDefault() ?? Root; + _seekedSelectedNode = true; + } - return _selectedNode; - } - } - } + return _selectedNode ?? Root; + } + set + { + _selectedNode = value; + Path = _selectedNode != null + ? _selectedNode.Trail.Where(x => !x.IsRoot).ToList() + : new List>(); + + _seekedSelectedNode = true; + } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuPublisher.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuPublisher.cs index 4078ca66fd..82609d838d 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuPublisher.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuPublisher.cs @@ -1,60 +1,57 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using SmartStore.Collections; using SmartStore.Core.Caching; using SmartStore.Core.Infrastructure; namespace SmartStore.Web.Framework.UI { - public interface IMenuPublisher - { - void RegisterMenus(TreeNode rootNode, string menuName); - } - - public class MenuPublisher : IMenuPublisher - { - private readonly ITypeFinder _typeFinder; - private readonly IRequestCache _requestCache; - - public MenuPublisher(ITypeFinder typeFinder, IRequestCache requestCache) - { - _typeFinder = typeFinder; - _requestCache = requestCache; - } - - public void RegisterMenus(TreeNode rootNode, string menuName) - { - Guard.NotNull(rootNode, nameof(rootNode)); - Guard.NotEmpty(menuName, nameof(menuName)); - - var providers = _requestCache.Get("sm.menu.providers.{0}".FormatInvariant(menuName), () => - { - var allInstances = _requestCache.Get("sm.menu.allproviders", () => - { - var instances = new List(); - var providerTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true); - - foreach (var type in providerTypes) - { - try - { - var provider = EngineContext.Current.ContainerManager.ResolveUnregistered(type) as IMenuProvider; - instances.Add(provider); - } - catch { } - } - - return instances; - }); - - return allInstances.Where(x => x.MenuName.IsCaseInsensitiveEqual(menuName)).OrderBy(x => x.Ordinal).ToList(); - }); - - providers.Each(x => x.BuildMenu(rootNode)); - } - - } + public interface IMenuPublisher + { + void RegisterMenus(TreeNode rootNode, string menuName); + } + + public class MenuPublisher : IMenuPublisher + { + private readonly ITypeFinder _typeFinder; + private readonly IRequestCache _requestCache; + + public MenuPublisher(ITypeFinder typeFinder, IRequestCache requestCache) + { + _typeFinder = typeFinder; + _requestCache = requestCache; + } + + public void RegisterMenus(TreeNode rootNode, string menuName) + { + Guard.NotNull(rootNode, nameof(rootNode)); + Guard.NotEmpty(menuName, nameof(menuName)); + + var providers = _requestCache.Get("sm.menu.providers.{0}".FormatInvariant(menuName), () => + { + var allInstances = _requestCache.Get("sm.menu.allproviders", () => + { + var instances = new List(); + var providerTypes = _typeFinder.FindClassesOfType(ignoreInactivePlugins: true); + + foreach (var type in providerTypes) + { + try + { + var provider = EngineContext.Current.ContainerManager.ResolveUnregistered(type) as IMenuProvider; + instances.Add(provider); + } + catch { } + } + + return instances; + }); + + return allInstances.Where(x => x.MenuName.IsCaseInsensitiveEqual(menuName)).OrderBy(x => x.Ordinal).ToList(); + }); + + providers.Each(x => x.BuildMenu(rootNode)); + } + + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuRenderer.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuRenderer.cs index 7490164935..668233de1d 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuRenderer.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/MenuRenderer.cs @@ -1,61 +1,57 @@ -using System.Collections.Generic; -using System.Linq; -using System.Web.Mvc.Html; +using System.Web.Mvc.Html; using System.Web.UI; -using SmartStore.Collections; -using SmartStore.Core.Infrastructure; namespace SmartStore.Web.Framework.UI { public class MenuRenderer : ComponentRenderer { - protected override void WriteHtmlCore(HtmlTextWriter writer) - { - var route = Component.Route; + protected override void WriteHtmlCore(HtmlTextWriter writer) + { + var route = Component.Route; - if (route != null) - { - HtmlHelper.RenderAction(route.Action, route.Controller, route.RouteValues); - } - else - { - HtmlHelper.RenderAction("Menu", "Common", new - { - area = "", - name = Component.Name, - template = Component.Template - }); - } - } + if (route != null) + { + HtmlHelper.RenderAction(route.Action, route.Controller, route.RouteValues); + } + else + { + HtmlHelper.RenderAction("Menu", "Menu", new + { + area = "", + name = Component.Name, + template = Component.Template + }); + } + } - public override void Render() - { - var c = Component; - var route = c.Route; + public override void Render() + { + var c = Component; + var route = c.Route; - if (route != null) - { - HtmlHelper.RenderAction(route.Action, route.Controller, route.RouteValues); - } - else - { - HtmlHelper.RenderAction("Menu", "Common", new { area = "", name = c.Name, template = c.Template }); - } - } + if (route != null) + { + HtmlHelper.RenderAction(route.Action, route.Controller, route.RouteValues); + } + else + { + HtmlHelper.RenderAction("Menu", "Menu", new { area = "", name = c.Name, template = c.Template }); + } + } - public override string ToHtmlString() - { - var c = Component; - var route = c.Route; + public override string ToHtmlString() + { + var c = Component; + var route = c.Route; - if (route != null) - { - return HtmlHelper.Action(route.Action, route.Controller, route.RouteValues).ToHtmlString(); - } - else - { - return HtmlHelper.Action("Menu", "Common", new { area = "", name = c.Name, template = c.Template }).ToHtmlString(); - } - } - } + if (route != null) + { + return HtmlHelper.Action(route.Action, route.Controller, route.RouteValues).ToHtmlString(); + } + else + { + return HtmlHelper.Action("Menu", "Menu", new { area = "", name = c.Name, template = c.Template }).ToHtmlString(); + } + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/NodePathState.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/NodePathState.cs index a567712ac5..076d3b6839 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/NodePathState.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Menu/NodePathState.cs @@ -1,14 +1,13 @@ using System; -using System.Collections.Generic; namespace SmartStore.Web.Framework.UI { - [Flags] - public enum NodePathState - { - Unknown = 0, - Parent = 1, - Expanded = 2, - Selected = 4 - } + [Flags] + public enum NodePathState + { + Unknown = 0, + Parent = 1, + Expanded = 2, + Selected = 4 + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigatableComponent.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigatableComponent.cs index e3982af7fe..18cb948efb 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigatableComponent.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigatableComponent.cs @@ -1,84 +1,71 @@ -using System; -using System.Web.Routing; +using System.Web.Routing; namespace SmartStore.Web.Framework.UI { - public abstract class NavigatableComponent : Component, INavigatable - { - private string _actionName; - private string _controllerName; - private string _routeName; - private string _url; + public abstract class NavigatableComponent : Component, INavigatable + { + private string _actionName; + private string _controllerName; + private string _routeName; + private string _url; - protected NavigatableComponent() - { - this.RouteValues = new RouteValueDictionary(); - } + protected NavigatableComponent() + { + this.RouteValues = new RouteValueDictionary(); + } - public string ActionName - { - get - { - return _actionName; - } - set - { - _actionName = value; - _routeName = (string)(_url = null); - } - } + public string ActionName + { + get => _actionName; + set + { + _actionName = value; + _routeName = (string)(_url = null); + } + } - public string ControllerName - { - get - { - return _controllerName; - } - set - { - _controllerName = value; - _routeName = (string)(_url = null); - } - } + public string ControllerName + { + get => _controllerName; + set + { + _controllerName = value; + _routeName = (string)(_url = null); + } + } - public string RouteName - { - get - { - return _routeName; - } - set - { - _routeName = value; - _controllerName = _actionName = (string)(_url = null); - } - } + public string RouteName + { + get => _routeName; + set + { + _routeName = value; + _controllerName = _actionName = (string)(_url = null); + } + } - public RouteValueDictionary RouteValues - { - get; - set; - } + public RouteValueDictionary RouteValues + { + get; + set; + } - public string Url - { - get - { - return _url; - } - set - { - _url = value; - _routeName = _controllerName = (string)(_actionName = null); - this.RouteValues.Clear(); + public string Url + { + get => _url; + set + { + _url = value; + _routeName = _controllerName = (string)(_actionName = null); + this.RouteValues.Clear(); - } - } + } + } - public ModifiedParameter ModifiedParam - { - get; - protected set; - } - } + public ModifiedParameter ModifiedParam + { + get; + protected set; + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigatableComponentBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigatableComponentBuilder.cs index 5f6a13e8aa..4822affcce 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigatableComponentBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigatableComponentBuilder.cs @@ -3,7 +3,6 @@ using System.Web.Mvc; using System.Web.Routing; using System.Web.WebPages; -using SmartStore.Core; using SmartStore.Utilities; namespace SmartStore.Web.Framework.UI diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigationItem.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigationItem.cs index 6a8b2f4d8b..dfc5cb4ba3 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigationItem.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigationItem.cs @@ -6,8 +6,8 @@ namespace SmartStore.Web.Framework.UI { - [Serializable] - public abstract class NavigationItem : IHtmlAttributesContainer, INavigatable, IHideObjectMembers + [Serializable] + public abstract class NavigationItem : IHtmlAttributesContainer, INavigatable, IHideObjectMembers { private bool _selected; private bool _enabled; @@ -31,19 +31,40 @@ public NavigationItem() public IDictionary LinkHtmlAttributes { get; set; } + /// + /// Merges attributes of and into one combined dictionary. + /// + /// New dictionary instance with combined attributes. + public IDictionary GetCombinedAttributes() + { + if (HtmlAttributes == null && LinkHtmlAttributes == null) + { + return null; + } + + var combined = new RouteValueDictionary(HtmlAttributes ?? LinkHtmlAttributes); + + if (HtmlAttributes != null && LinkHtmlAttributes != null) + { + combined.Merge(LinkHtmlAttributes); + } + + return combined; + } + public string ImageUrl { get; set; } - public int? ImageId { get; set; } + public int? ImageId { get; set; } - public string Icon { get; set; } + public string Icon { get; set; } public string Text { get; set; } - public bool Rtl { get; set; } + public bool Rtl { get; set; } - public string Summary { get; set; } + public string Summary { get; set; } - public string BadgeText { get; set; } + public string BadgeText { get; set; } public BadgeStyle BadgeStyle { get; set; } @@ -53,10 +74,7 @@ public NavigationItem() public bool Selected { - get - { - return _selected; - } + get => _selected; set { _selected = value; @@ -69,10 +87,7 @@ public bool Selected public bool Enabled { - get - { - return _enabled; - } + get => _enabled; set { _enabled = value; @@ -84,88 +99,88 @@ public bool Enabled } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string ActionName + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string ActionName { - get - { - return _actionName; - } + get => _actionName; set { - if (_actionName != value) - { - _actionName = value; - _routeName = (string)(_url = null); - } + if (_actionName != value) + { + _actionName = value; + _routeName = (string)(_url = null); + } } } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string ControllerName + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string ControllerName { - get - { - return _controllerName; - } + get => _controllerName; set { - if (_controllerName != value) - { - _controllerName = value; - _routeName = (string)(_url = null); - } + if (_controllerName != value) + { + _controllerName = value; + _routeName = (string)(_url = null); + } } } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string RouteName + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string RouteName { - get - { - return _routeName; - } + get => _routeName; set { - if (_routeName != value) - { - _routeName = value; - _controllerName = _actionName = (string)(_url = null); - } + if (_routeName != value) + { + _routeName = value; + _controllerName = _actionName = (string)(_url = null); + } } } public RouteValueDictionary RouteValues { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string Url { - get - { - return _url; - } + get => _url; set { - if (_url != value) - { - _url = value; - _routeName = _controllerName = (string)(_actionName = null); - this.RouteValues.Clear(); - } + if (_url != value) + { + _url = value; + _routeName = _controllerName = (string)(_actionName = null); + this.RouteValues.Clear(); + } } } - [JsonIgnore] + [JsonIgnore] public ModifiedParameter ModifiedParam { get; private set; } + /// + /// Checks whether action/controller or routeName or url has been specified. + /// public bool HasRoute() - { - return _actionName != null || _routeName != null || _url != null; - } + { + return _actionName != null || _routeName != null || _url != null; + } + + /// + /// Checks whether url has been specified with '#' or 'javascript:void()' or empty string. + /// + public bool IsVoid() + { + // Perf: order from most to least common + return _url != null && (_url == "#" || _url.StartsWith("javascript:void") || _url == string.Empty || _url.IsWhiteSpace()); + } public override string ToString() { @@ -185,7 +200,7 @@ public NavigationItemWithContent() this.ContentHtmlAttributes = new RouteValueDictionary(); } - public bool Ajax { get; set; } + public bool Ajax { get; set; } public IDictionary ContentHtmlAttributes { get; set; } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigationItemBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigationItemBuilder.cs index 4479edac15..7242fec0a5 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigationItemBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/NavigationItemBuilder.cs @@ -1,12 +1,9 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Web.Mvc; using System.Web.Mvc.Html; using System.Web.Routing; using System.Web.WebPages; -using SmartStore.Core; using SmartStore.Utilities; namespace SmartStore.Web.Framework.UI @@ -36,20 +33,20 @@ public TBuilder Action(RouteValueDictionary routeValues) return (this as TBuilder); } - public TBuilder Action(string actionName) - { - return this.Action(actionName, null, null); - } + public TBuilder Action(string actionName) + { + return this.Action(actionName, null, null); + } - public TBuilder Action(string actionName, object routeValues) - { - return this.Action(actionName, null, routeValues); - } + public TBuilder Action(string actionName, object routeValues) + { + return this.Action(actionName, null, routeValues); + } - public TBuilder Action(string actionName, RouteValueDictionary routeValues) - { - return this.Action(actionName, null, routeValues); - } + public TBuilder Action(string actionName, RouteValueDictionary routeValues) + { + return this.Action(actionName, null, routeValues); + } public TBuilder Action(string actionName, string controllerName) { @@ -127,13 +124,13 @@ public TBuilder ImageUrl(string value) return (this as TBuilder); } - public TBuilder ImageId(int? value) - { - this.Item.ImageId = value; - return (this as TBuilder); - } + public TBuilder ImageId(int? value) + { + this.Item.ImageId = value; + return (this as TBuilder); + } - public TBuilder Icon(string value) + public TBuilder Icon(string value) { this.Item.Icon = value; return (this as TBuilder); @@ -145,13 +142,13 @@ public TBuilder Text(string value) return (this as TBuilder); } - public TBuilder Summary(string value) - { - this.Item.Summary = value; - return (this as TBuilder); - } + public TBuilder Summary(string value) + { + this.Item.Summary = value; + return (this as TBuilder); + } - public TBuilder Badge(string value, BadgeStyle style = BadgeStyle.Secondary, bool condition = true) + public TBuilder Badge(string value, BadgeStyle style = BadgeStyle.Secondary, bool condition = true) { if (condition) { @@ -210,29 +207,29 @@ protected HtmlHelper HtmlHelper private set; } - /// - /// Specifies whether the content should be loaded per AJAX into the content pane. - /// - /// value - /// builder - /// - /// This setting has no effect when no route is specified OR - /// static content was set. - /// - public TBuilder Ajax(bool value = true) - { - this.Item.Ajax = value; - return (this as TBuilder); - } + /// + /// Specifies whether the content should be loaded per AJAX into the content pane. + /// + /// value + /// builder + /// + /// This setting has no effect when no route is specified OR + /// static content was set. + /// + public TBuilder Ajax(bool value = true) + { + this.Item.Ajax = value; + return (this as TBuilder); + } public TBuilder Content(string value) { - if (value.IsEmpty()) - { - // do nothing - return (this as TBuilder); - } - return this.Content(x => new HelperResult(writer => writer.Write(value))); + if (value.IsEmpty()) + { + // do nothing + return (this as TBuilder); + } + return this.Content(x => new HelperResult(writer => writer.Write(value))); } public TBuilder Content(Func value) @@ -255,7 +252,7 @@ public TBuilder Content(HelperResult value) /// builder instance public TBuilder Content(string action, string controller, object routeValues) { - return Content(action, controller, new RouteValueDictionary(routeValues)); + return Content(action, controller, new RouteValueDictionary(routeValues)); } /// @@ -267,7 +264,7 @@ public TBuilder Content(string action, string controller, object routeValues) /// builder instance public TBuilder Content(string action, string controller, RouteValueDictionary routeValues) { - return this.Content(x => new HelperResult(writer => + return this.Content(x => new HelperResult(writer => { var value = this.HtmlHelper.Action(action, controller, routeValues); writer.Write(value); diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/ForumTopicPagerRenderer.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/ForumTopicPagerRenderer.cs index aabeef178d..2d36674971 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/ForumTopicPagerRenderer.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/ForumTopicPagerRenderer.cs @@ -1,11 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Collections.Generic; namespace SmartStore.Web.Framework.UI { - + public class ForumTopicPagerRenderer : PagerRenderer { diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/Pager.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/Pager.cs index d023c08cb5..dcc932017a 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/Pager.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/Pager.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Web.Routing; -using SmartStore.Core; +using SmartStore.Core; using SmartStore.Core.Infrastructure; using SmartStore.Services.Localization; @@ -11,8 +6,8 @@ namespace SmartStore.Web.Framework.UI { public enum PagerSize { - Mini, - Small, + Mini, + Small, Medium, Large } @@ -46,7 +41,7 @@ public Pager() } public Pager(ILocalizationService localizationService) - : base() + : base() { _localizationService = localizationService; @@ -82,16 +77,10 @@ public Pager(ILocalizationService localizationService) /// public string FirstButtonText { - get - { - return (!string.IsNullOrEmpty(_firstButtonText)) ? + get => (!string.IsNullOrEmpty(_firstButtonText)) ? _firstButtonText : _localizationService.GetResource("Pager.First"); - } - set - { - _firstButtonText = value; - } + set => _firstButtonText = value; } /// @@ -99,16 +88,10 @@ public string FirstButtonText /// public string LastButtonText { - get - { - return (!string.IsNullOrEmpty(_lastButtonText)) ? + get => (!string.IsNullOrEmpty(_lastButtonText)) ? _lastButtonText : _localizationService.GetResource("Pager.Last"); - } - set - { - _lastButtonText = value; - } + set => _lastButtonText = value; } /// @@ -116,16 +99,10 @@ public string LastButtonText /// public string NextButtonText { - get - { - return (!string.IsNullOrEmpty(_nextButtonText)) ? + get => (!string.IsNullOrEmpty(_nextButtonText)) ? _nextButtonText : _localizationService.GetResource("Pager.Next"); - } - set - { - _nextButtonText = value; - } + set => _nextButtonText = value; } /// @@ -133,16 +110,10 @@ public string NextButtonText /// public string PreviousButtonText { - get - { - return (!string.IsNullOrEmpty(_previousButtonText)) ? + get => (!string.IsNullOrEmpty(_previousButtonText)) ? _previousButtonText : _localizationService.GetResource("Pager.Previous"); - } - set - { - _previousButtonText = value; - } + set => _previousButtonText = value; } /// @@ -150,16 +121,10 @@ public string PreviousButtonText /// public string CurrentPageText { - get - { - return (!string.IsNullOrEmpty(_currentPageText)) ? + get => (!string.IsNullOrEmpty(_currentPageText)) ? _currentPageText : _localizationService.GetResource("Pager.CurrentPage"); - } - set - { - _currentPageText = value; - } + set => _currentPageText = value; } } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/PagerBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/PagerBuilder.cs index 1164c9fa3c..06601ae7f7 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/PagerBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/PagerBuilder.cs @@ -1,5 +1,4 @@ -using System; -using System.Web.Mvc; +using System.Web.Mvc; using SmartStore.Core; namespace SmartStore.Web.Framework.UI diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/PagerItem.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/PagerItem.cs index ec9a8f6af4..608ba01265 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/PagerItem.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/PagerItem.cs @@ -54,13 +54,7 @@ public PagerItem(string text, string url, PagerItemType itemType, PagerItemState public PagerItemType Type { get; set; } public string ExtraData { get; set; } - public bool IsNavButton - { - get - { - return (Type == PagerItemType.FirstPage || Type == PagerItemType.PreviousPage || Type == PagerItemType.NextPage || Type == PagerItemType.LastPage); - } - } + public bool IsNavButton => (Type == PagerItemType.FirstPage || Type == PagerItemType.PreviousPage || Type == PagerItemType.NextPage || Type == PagerItemType.LastPage); } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/PagerRenderer.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/PagerRenderer.cs index 7e024d559d..6d770bf586 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/PagerRenderer.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Pager/PagerRenderer.cs @@ -1,10 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Web.UI; +using System.Collections.Generic; using System.Web.Routing; +using System.Web.UI; namespace SmartStore.Web.Framework.UI -{ +{ public class PagerRenderer : ComponentRenderer { public PagerRenderer() @@ -175,20 +174,20 @@ protected override void WriteHtmlCore(HtmlTextWriter writer) var items = CreateItemList(); - // Alignment - if (pager.Alignment == PagerAlignment.Right) - { - pager.HtmlAttributes.AppendCssClass("text-right"); - } - else if (pager.Alignment == PagerAlignment.Centered) - { - pager.HtmlAttributes.AppendCssClass("text-center"); - } - - writer.AddAttributes(pager.HtmlAttributes); + // Alignment + if (pager.Alignment == PagerAlignment.Right) + { + pager.HtmlAttributes.AppendCssClass("text-right"); + } + else if (pager.Alignment == PagerAlignment.Centered) + { + pager.HtmlAttributes.AppendCssClass("text-center"); + } + + writer.AddAttributes(pager.HtmlAttributes); writer.AddAttribute("id", pager.Id); - writer.AddAttribute("aria-label", "Page navigation"); - writer.RenderBeginTag("nav"); + writer.AddAttribute("aria-label", "Page navigation"); + writer.RenderBeginTag("nav"); if (pager.ShowSummary && pager.Model.TotalPages > 1) { @@ -198,36 +197,36 @@ protected override void WriteHtmlCore(HtmlTextWriter writer) writer.RenderEndTag(); // div } - var ulAttrs = new RouteValueDictionary(); - - ulAttrs.AppendCssClass(pager.Style == PagerStyle.Pagination ? "pagination" : "pagination" /* formerly "pager" */); // TODO: (mc) Seems there's no "pager" in BS4 anymore - - // Size - if (pager.Size == PagerSize.Large) - { - ulAttrs.AppendCssClass("pagination-lg"); - } - else if (pager.Size == PagerSize.Small) - { - ulAttrs.AppendCssClass("pagination-sm"); - } - else if (pager.Size == PagerSize.Mini) - { - ulAttrs.AppendCssClass("pagination-xs"); - } - - // BS 4 alignment - if (pager.Alignment == PagerAlignment.Centered) - { - ulAttrs.AppendCssClass("justify-content-center"); - } - else if (pager.Alignment == PagerAlignment.Right) - { - ulAttrs.AppendCssClass("justify-content-end"); - } - - writer.AddAttributes(ulAttrs); - writer.RenderBeginTag("ul"); + var ulAttrs = new RouteValueDictionary(); + + ulAttrs.AppendCssClass(pager.Style == PagerStyle.Pagination ? "pagination" : "pagination" /* formerly "pager" */); // TODO: (mc) Seems there's no "pager" in BS4 anymore + + // Size + if (pager.Size == PagerSize.Large) + { + ulAttrs.AppendCssClass("pagination-lg"); + } + else if (pager.Size == PagerSize.Small) + { + ulAttrs.AppendCssClass("pagination-sm"); + } + else if (pager.Size == PagerSize.Mini) + { + ulAttrs.AppendCssClass("pagination-xs"); + } + + // BS 4 alignment + if (pager.Alignment == PagerAlignment.Centered) + { + ulAttrs.AppendCssClass("justify-content-center"); + } + else if (pager.Alignment == PagerAlignment.Right) + { + ulAttrs.AppendCssClass("justify-content-end"); + } + + writer.AddAttributes(ulAttrs); + writer.RenderBeginTag("ul"); foreach (var item in items) { @@ -244,26 +243,26 @@ protected virtual void RenderItem(HtmlTextWriter writer, PagerItem item) if (item.State == PagerItemState.Disabled) { - attrs.AppendCssClass("disabled"); + attrs.AppendCssClass("disabled"); } else if (item.State == PagerItemState.Selected) { - attrs.AppendCssClass("active"); + attrs.AppendCssClass("active"); } if (item.Type == PagerItemType.Text) { - attrs.AppendCssClass("shrinked"); + attrs.AppendCssClass("shrinked"); } if (Component.Style == PagerStyle.Blog && item.IsNavButton) { - // TODO: (mg) BS4 doesn't seem to support this anymore - attrs.AppendCssClass((item.Type == PagerItemType.PreviousPage || item.Type == PagerItemType.FirstPage) ? "previous" : "next"); + // TODO: (mg) BS4 doesn't seem to support this anymore + attrs.AppendCssClass((item.Type == PagerItemType.PreviousPage || item.Type == PagerItemType.FirstPage) ? "previous" : "next"); } - attrs.AppendCssClass("page-item"); - + attrs.AppendCssClass("page-item"); + writer.AddAttributes(attrs); writer.RenderBeginTag("li"); @@ -274,9 +273,9 @@ protected virtual void RenderItem(HtmlTextWriter writer, PagerItem item) if (item.IsNavButton) { writer.AddAttribute("title", item.Text.AttributeEncode()); - writer.AddAttribute("aria-label", item.Text.AttributeEncode()); - writer.AddAttribute("tab-index", "-1"); - if (Component.Style != PagerStyle.Blog) + writer.AddAttribute("aria-label", item.Text.AttributeEncode()); + writer.AddAttribute("tab-index", "-1"); + if (Component.Style != PagerStyle.Blog) { writer.AddAttribute("rel", "tooltip"); writer.AddAttribute("class", "page-link page-nav"); @@ -298,9 +297,9 @@ protected virtual void RenderItem(HtmlTextWriter writer, PagerItem item) } else { - // write span - writer.AddAttribute("class", "page-link"); - writer.RenderBeginTag("span"); + // write span + writer.AddAttribute("class", "page-link"); + writer.RenderBeginTag("span"); } this.RenderItemInnerContent(writer, item); @@ -320,13 +319,13 @@ protected virtual void RenderItemInnerContent(HtmlTextWriter writer, PagerItem i writer.AddAttribute("class", "fa fa-angle-double-left"); break; case PagerItemType.PreviousPage: - writer.AddAttribute("class", "fa fa-angle-left"); + writer.AddAttribute("class", "fa fa-angle-left"); break; case PagerItemType.NextPage: - writer.AddAttribute("class", "fa fa-angle-right"); + writer.AddAttribute("class", "fa fa-angle-right"); break; case PagerItemType.LastPage: - writer.AddAttribute("class", "fa fa-angle-double-right"); + writer.AddAttribute("class", "fa fa-angle-double-right"); break; default: writer.WriteEncodedText(item.Text); diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/Tab.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/Tab.cs index 6b0cc233a0..8ce19a5f8b 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/Tab.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/Tab.cs @@ -1,16 +1,13 @@ using System; -using System.Collections.Generic; -using System.Web.Routing; -using System.Web.WebPages; namespace SmartStore.Web.Framework.UI { - [Serializable] - public class SelectedTabInfo - { - public string TabId { get; set; } - public string Path { get; set; } - } + [Serializable] + public class SelectedTabInfo + { + public string TabId { get; set; } + public string Path { get; set; } + } public enum TabPull { @@ -22,8 +19,8 @@ public class Tab : NavigationItemWithContent { public Tab() { - // [...] - } + // [...] + } public string Name { @@ -36,5 +33,5 @@ public TabPull Pull get; set; } - } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabBuilder.cs index 48443f9690..ebb7545bc8 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabBuilder.cs @@ -20,5 +20,5 @@ public TabBuilder Pull(TabPull value) this.Item.Pull = value; return this; } - } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabFactory.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabFactory.cs index 7fee6c00af..ba149e2de2 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabFactory.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabFactory.cs @@ -1,11 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Collections.Generic; using System.Web.Mvc; namespace SmartStore.Web.Framework.UI -{ +{ public class TabFactory : IHideObjectMembers { private readonly IList _items; diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabStrip.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabStrip.cs index 7ab56a8bdc..670bcf4856 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabStrip.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabStrip.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Collections.Generic; using System.Web.WebPages; namespace SmartStore.Web.Framework.UI @@ -18,7 +15,7 @@ public enum TabsStyle { Tabs, Pills, - Material + Material } public class TabStrip : Component @@ -28,7 +25,7 @@ public TabStrip() this.Items = new List(); this.Fade = true; this.SmartTabSelection = true; - this.Breakpoint = " Items @@ -37,25 +34,31 @@ public List Items private set; } - public bool IsResponsive + public bool IsResponsive { get; set; } - public string Breakpoint - { - get; - set; - } + public bool HideSingleItem + { + get; + set; + } + + public string Breakpoint + { + get; + set; + } - public HelperResult TabContentHeaderContent - { - get; - set; - } + public HelperResult TabContentHeaderContent + { + get; + set; + } - public TabsPosition Position + public TabsPosition Position { get; set; @@ -79,37 +82,31 @@ public bool SmartTabSelection set; } - public string OnAjaxBegin - { - get; - set; - } - - public string OnAjaxSuccess - { - get; - set; - } - - public string OnAjaxFailure - { - get; - set; - } - - public string OnAjaxComplete - { - get; - set; - } - - public override bool NameIsRequired + public string OnAjaxBegin { - get - { - return true; - } + get; + set; } + + public string OnAjaxSuccess + { + get; + set; + } + + public string OnAjaxFailure + { + get; + set; + } + + public string OnAjaxComplete + { + get; + set; + } + + public override bool NameIsRequired => true; } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabStripBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabStripBuilder.cs index 390a7732d4..b0a1cfdf26 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabStripBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabStripBuilder.cs @@ -3,7 +3,7 @@ using System.Web.WebPages; namespace SmartStore.Web.Framework.UI -{ +{ public class TabStripBuilder : ComponentBuilder, TModel> { public TabStripBuilder(TabStrip Component, HtmlHelper htmlHelper) @@ -18,36 +18,42 @@ public TabStripBuilder Items(Action addAction) return this; } - public TabStripBuilder Responsive(bool value, string breakpoint = " TabContentHeaderContent(string value) - { - if (value.IsEmpty()) - { - // do nothing - return this; - } - - return this.TabContentHeaderContent(x => new HelperResult(writer => writer.Write(value))); - } - - public TabStripBuilder TabContentHeaderContent(Func value) - { - return this.TabContentHeaderContent(value(null)); - } - - public TabStripBuilder TabContentHeaderContent(HelperResult value) - { - base.Component.TabContentHeaderContent = value; - return this; - } - - public TabStripBuilder Position(TabsPosition value) + public TabStripBuilder Responsive(bool value, string breakpoint = " HideSingleItem(bool value) + { + base.Component.HideSingleItem = value; + return this; + } + + public TabStripBuilder TabContentHeaderContent(string value) + { + if (value.IsEmpty()) + { + // do nothing + return this; + } + + return this.TabContentHeaderContent(x => new HelperResult(writer => writer.Write(value))); + } + + public TabStripBuilder TabContentHeaderContent(Func value) + { + return this.TabContentHeaderContent(value(null)); + } + + public TabStripBuilder TabContentHeaderContent(HelperResult value) + { + base.Component.TabContentHeaderContent = value; + return this; + } + + public TabStripBuilder Position(TabsPosition value) { base.Component.Position = value; return this; @@ -71,28 +77,28 @@ public TabStripBuilder SmartTabSelection(bool value) return this; } - public TabStripBuilder OnAjaxBegin(string value) - { - base.Component.OnAjaxBegin = value; - return this; - } - - public TabStripBuilder OnAjaxSuccess(string value) - { - base.Component.OnAjaxSuccess = value; - return this; - } - - public TabStripBuilder OnAjaxFailure(string value) - { - base.Component.OnAjaxFailure = value; - return this; - } - - public TabStripBuilder OnAjaxComplete(string value) - { - base.Component.OnAjaxComplete = value; - return this; - } + public TabStripBuilder OnAjaxBegin(string value) + { + base.Component.OnAjaxBegin = value; + return this; + } + + public TabStripBuilder OnAjaxSuccess(string value) + { + base.Component.OnAjaxSuccess = value; + return this; + } + + public TabStripBuilder OnAjaxFailure(string value) + { + base.Component.OnAjaxFailure = value; + return this; + } + + public TabStripBuilder OnAjaxComplete(string value) + { + base.Component.OnAjaxComplete = value; + return this; + } } } 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 eb0250e059..7a77fc0320 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabStripRenderer.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/TabStrip/TabStripRenderer.cs @@ -2,163 +2,167 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Text; -using System.Web.UI; using System.Web.Mvc; +using System.Web.UI; using SmartStore.Web.Framework.Modelling; namespace SmartStore.Web.Framework.UI -{ +{ public class TabStripRenderer : ComponentRenderer { public TabStripRenderer() - { + { } - [SuppressMessage("ReSharper", "Mvc.AreaNotResolved")] - protected override void WriteHtmlCore(HtmlTextWriter writer) - { - var tab = base.Component; + [SuppressMessage("ReSharper", "Mvc.AreaNotResolved")] + protected override void WriteHtmlCore(HtmlTextWriter writer) + { + var tab = base.Component; MoveSpecialTabToEnd(tab.Items); var hasContent = tab.Items.Any(x => x.Content != null || x.Ajax); - var isTabbable = tab.Position != TabsPosition.Top; - var isStacked = tab.Position == TabsPosition.Left || tab.Position == TabsPosition.Right; - var urlHelper = new UrlHelper(this.ViewContext.RequestContext); - - if (tab.Items.Count == 0) - return; - - tab.HtmlAttributes.AppendCssClass("tabbable"); - - if (isTabbable) - { - tab.HtmlAttributes.AppendCssClass("tabs-{0}".FormatInvariant(tab.Position.ToString().ToLower())); - } - - if (tab.SmartTabSelection) - { - tab.HtmlAttributes.AppendCssClass("tabs-autoselect"); - tab.HtmlAttributes.Add("data-tabselector-href", urlHelper.Action("SetSelectedTab", "Common", new { area = "admin" })); - } - - if (isStacked) - { - tab.HtmlAttributes.AppendCssClass("row"); - } - - if (tab.OnAjaxBegin.HasValue()) - { - tab.HtmlAttributes.Add("data-ajax-onbegin", tab.OnAjaxBegin); - } - - if (tab.OnAjaxSuccess.HasValue()) - { - tab.HtmlAttributes.Add("data-ajax-onsuccess", tab.OnAjaxSuccess); - } - - if (tab.OnAjaxFailure.HasValue()) - { - tab.HtmlAttributes.Add("data-ajax-onfailure", tab.OnAjaxFailure); - } - - if (tab.OnAjaxComplete.HasValue()) - { - tab.HtmlAttributes.Add("data-ajax-oncomplete", tab.OnAjaxComplete); - } - - if (tab.IsResponsive) - { - tab.HtmlAttributes.AppendCssClass("nav-responsive"); - tab.HtmlAttributes.Add("data-breakpoint", tab.Breakpoint); - } - - writer.AddAttributes(tab.HtmlAttributes); - - writer.RenderBeginTag("div"); // root div - { - if (tab.Position == TabsPosition.Below && hasContent) - RenderTabContent(writer); - - if (isStacked) - { - writer.AddAttribute("class", "col-lg-auto nav-aside"); - writer.RenderBeginTag("aside"); // opening left/right tabs col - } - - // Tabs - var ulAttrs = new Dictionary(); - ulAttrs.AppendCssClass("nav"); - - if (tab.Style == TabsStyle.Tabs) - { - ulAttrs.AppendCssClass("nav-tabs"); - } - else if (tab.Style == TabsStyle.Pills) - { - ulAttrs.AppendCssClass("nav-pills"); - } - else if (tab.Style == TabsStyle.Material) - { - ulAttrs.AppendCssClass("nav-tabs nav-tabs-line"); - } - - if (isStacked) - { - ulAttrs.AppendCssClass("flex-row flex-lg-column"); - } - writer.AddAttributes(ulAttrs); - - string selector = null; - var loadedTabs = new List(); - writer.RenderBeginTag("ul"); - { - // enable smart tab selection - if (tab.SmartTabSelection) - { - selector = TrySelectRememberedTab(); - } - - int i = 1; - foreach (var item in tab.Items) - { - var loadedTabName = this.RenderItemLink(writer, item, i); - if (loadedTabName.HasValue()) - { - loadedTabs.Add(loadedTabName); - } - i++; - } - - writer.RenderEndTag(); // ul - } - - if (isStacked) - { - writer.RenderEndTag(); // closing left/right tabs col - } - - // TODO: (mc) render right positioned tabs also - if (tab.Position != TabsPosition.Below && hasContent) - { - if (isStacked) - { - writer.AddAttribute("class", "col-lg nav-content"); - writer.RenderBeginTag("div"); // opening left/right content col - } - - RenderTabContent(writer); - - if (isStacked) - { - writer.RenderEndTag(); // closing left/right content col - } - } - - if (selector != null) - { - writer.WriteLine( + var isTabbable = tab.Position != TabsPosition.Top; + var isStacked = tab.Position == TabsPosition.Left || tab.Position == TabsPosition.Right; + var urlHelper = new UrlHelper(this.ViewContext.RequestContext); + + if (tab.Items.Count == 0) + return; + + tab.HtmlAttributes.AppendCssClass("tabbable"); + + if (isTabbable) + { + tab.HtmlAttributes.AppendCssClass("tabs-{0}".FormatInvariant(tab.Position.ToString().ToLower())); + } + + if (tab.SmartTabSelection) + { + tab.HtmlAttributes.AppendCssClass("tabs-autoselect"); + tab.HtmlAttributes.Add("data-tabselector-href", urlHelper.Action("SetSelectedTab", "Common", new { area = "admin" })); + } + + if (isStacked) + { + tab.HtmlAttributes.AppendCssClass("row"); + } + + if (tab.OnAjaxBegin.HasValue()) + { + tab.HtmlAttributes.Add("data-ajax-onbegin", tab.OnAjaxBegin); + } + + if (tab.OnAjaxSuccess.HasValue()) + { + tab.HtmlAttributes.Add("data-ajax-onsuccess", tab.OnAjaxSuccess); + } + + if (tab.OnAjaxFailure.HasValue()) + { + tab.HtmlAttributes.Add("data-ajax-onfailure", tab.OnAjaxFailure); + } + + if (tab.OnAjaxComplete.HasValue()) + { + tab.HtmlAttributes.Add("data-ajax-oncomplete", tab.OnAjaxComplete); + } + + if (tab.IsResponsive) + { + tab.HtmlAttributes.AppendCssClass("nav-responsive"); + tab.HtmlAttributes.Add("data-breakpoint", tab.Breakpoint); + } + + writer.AddAttributes(tab.HtmlAttributes); + + writer.RenderBeginTag("div"); // root div + { + if (tab.Position == TabsPosition.Below && hasContent) + RenderTabContent(writer); + + if (isStacked) + { + writer.AddAttribute("class", "col-lg-auto nav-aside"); + writer.RenderBeginTag("aside"); // opening left/right tabs col + } + + // Tabs + var ulAttrs = new Dictionary(); + ulAttrs.AppendCssClass("nav"); + + if (tab.Style == TabsStyle.Tabs) + { + ulAttrs.AppendCssClass("nav-tabs"); + } + else if (tab.Style == TabsStyle.Pills) + { + ulAttrs.AppendCssClass("nav-pills"); + } + else if (tab.Style == TabsStyle.Material) + { + ulAttrs.AppendCssClass("nav-tabs nav-tabs-line"); + } + + if (tab.HideSingleItem && tab.Items.Count == 1) + { + ulAttrs.AppendCssClass("d-none"); + } + + if (isStacked) + { + ulAttrs.AppendCssClass("flex-row flex-lg-column"); + } + writer.AddAttributes(ulAttrs); + + string selector = null; + var loadedTabs = new List(); + writer.RenderBeginTag("ul"); + { + // enable smart tab selection + if (tab.SmartTabSelection) + { + selector = TrySelectRememberedTab(); + } + + int i = 1; + foreach (var item in tab.Items) + { + var loadedTabName = this.RenderItemLink(writer, item, i); + if (loadedTabName.HasValue()) + { + loadedTabs.Add(loadedTabName); + } + i++; + } + + writer.RenderEndTag(); // ul + } + + if (isStacked) + { + writer.RenderEndTag(); // closing left/right tabs col + } + + // TODO: (mc) render right positioned tabs also + if (tab.Position != TabsPosition.Below && hasContent) + { + if (isStacked) + { + writer.AddAttribute("class", "col-lg nav-content"); + writer.RenderBeginTag("div"); // opening left/right content col + } + + RenderTabContent(writer); + + if (isStacked) + { + writer.RenderEndTag(); // closing left/right content col + } + } + + if (selector != null) + { + writer.WriteLine( @"".FormatInvariant(selector)); - } - - if (loadedTabs.Count > 0) - { - foreach (var tabName in loadedTabs) - { - writer.WriteLine("", tabName); - } - } - - writer.RenderEndTag(); // div.tabbable - - if (tab.IsResponsive /* && tab.TabContentHeaderContent != null*/) - { - writer.WriteLine(@"".FormatInvariant(tab.Id)); - } - } - } + } + + if (loadedTabs.Count > 0) + { + foreach (var tabName in loadedTabs) + { + writer.WriteLine("", tabName); + } + } + + writer.RenderEndTag(); // div.tabbable + + if (tab.IsResponsive /* && tab.TabContentHeaderContent != null*/) + { + writer.WriteLine(@"".FormatInvariant(tab.Id)); + } + } + } private void MoveSpecialTabToEnd(List tabs) { @@ -198,84 +202,84 @@ private void MoveSpecialTabToEnd(List tabs) // returns a query selector private string TrySelectRememberedTab() - { - var tab = this.Component; - - if (tab.Id.IsEmpty()) - return null; - - if (ViewContext.ViewData.Model is EntityModelBase model && model.Id == 0) - { - // it's a "create" operation: don't select - return null; - } - - var rememberedTab = (SelectedTabInfo)ViewContext.TempData["SelectedTab." + tab.Id]; - if (rememberedTab != null && rememberedTab.Path.Equals(ViewContext.HttpContext.Request.RawUrl, StringComparison.OrdinalIgnoreCase)) - { - // get tab to select - var tabToSelect = GetTabById(rememberedTab.TabId); - - if (tabToSelect != null) - { - // unselect former selected tab(s) - tab.Items.Each(x => x.Selected = false); - - // select the new tab - tabToSelect.Selected = true; - - // persist again for the next request - ViewContext.TempData["SelectedTab." + tab.Id] = rememberedTab; - - if (tabToSelect.Ajax && tabToSelect.Content == null) - { - return ".nav a[data-ajax-url][href='#{0}']".FormatInvariant(rememberedTab.TabId); - } - } - } - - return null; - } - - private Tab GetTabById(string tabId) - { - int i = 1; - foreach (var item in Component.Items) - { - var id = BuildItemId(item, i); - if (id == tabId) - { - if (!item.Visible || !item.Enabled) - break; - - return item; - } - i++; - } - - return null; - } + { + var tab = this.Component; + + if (tab.Id.IsEmpty()) + return null; + + if (ViewContext.ViewData.Model is EntityModelBase model && model.Id == 0) + { + // it's a "create" operation: don't select + return null; + } + + var rememberedTab = (SelectedTabInfo)ViewContext.TempData["SelectedTab." + tab.Id]; + if (rememberedTab != null && rememberedTab.Path.Equals(ViewContext.HttpContext.Request.RawUrl, StringComparison.OrdinalIgnoreCase)) + { + // get tab to select + var tabToSelect = GetTabById(rememberedTab.TabId); + + if (tabToSelect != null) + { + // unselect former selected tab(s) + tab.Items.Each(x => x.Selected = false); + + // select the new tab + tabToSelect.Selected = true; + + // persist again for the next request + ViewContext.TempData["SelectedTab." + tab.Id] = rememberedTab; + + if (tabToSelect.Ajax && tabToSelect.Content == null) + { + return ".nav a[data-ajax-url][href='#{0}']".FormatInvariant(rememberedTab.TabId); + } + } + } + + return null; + } + + private Tab GetTabById(string tabId) + { + int i = 1; + foreach (var item in Component.Items) + { + var id = BuildItemId(item, i); + if (id == tabId) + { + if (!item.Visible || !item.Enabled) + break; + + return item; + } + i++; + } + + return null; + } protected virtual void RenderTabContent(HtmlTextWriter writer) { var tab = base.Component; - + writer.AddAttribute("class", "tab-content"); writer.RenderBeginTag("div"); - // Tab content header - if (tab.IsResponsive && tab.TabContentHeaderContent != null) - { - //writer.WriteLine(item.Content.ToHtmlString()); - writer.AddAttribute("class", "tab-content-header"); - writer.RenderBeginTag("div"); - { - writer.WriteLine(tab.TabContentHeaderContent.ToHtmlString()); - } - writer.RenderEndTag(); // div.tab-content-header - } - - // All tab items + // Tab content header + if (tab.IsResponsive && tab.TabContentHeaderContent != null) + { + //writer.WriteLine(item.Content.ToHtmlString()); + writer.AddAttribute("class", "tab-content-header"); + writer.RenderBeginTag("div"); + { + writer.WriteLine(tab.TabContentHeaderContent.ToHtmlString()); + } + writer.RenderEndTag(); // div.tab-content-header + } + + // All tab items int i = 1; foreach (var item in tab.Items) { @@ -287,162 +291,162 @@ protected virtual void RenderTabContent(HtmlTextWriter writer) } protected virtual string RenderItemLink(HtmlTextWriter writer, Tab item, int index) - { - string temp = ""; - string loadedTabName = null; + { + string temp = ""; + string loadedTabName = null; //
  • {text}
  • item.HtmlAttributes.AppendCssClass("nav-item"); // .active for BS2 - if (!item.Selected && !item.Visible) - { - item.HtmlAttributes.AppendCssClass("d-none"); - } + if (!item.Selected && !item.Visible) + { + item.HtmlAttributes.AppendCssClass("d-none"); + } - if (item.Pull == TabPull.Right) - { - item.HtmlAttributes.AppendCssClass("float-right"); - } + if (item.Pull == TabPull.Right) + { + item.HtmlAttributes.AppendCssClass("float-right"); + } - writer.AddAttributes(item.HtmlAttributes); + writer.AddAttributes(item.HtmlAttributes); + + writer.RenderBeginTag("li"); + { + var itemId = "#" + BuildItemId(item, index); + + writer.AddAttribute("class", "nav-link" + (item.Selected ? " active" : "")); - writer.RenderBeginTag("li"); - { - var itemId = "#" + BuildItemId(item, index); - if (item.Content != null) - { - writer.AddAttribute("href", itemId); - writer.AddAttribute("data-toggle", "tab"); - writer.AddAttribute("data-loaded", "true"); - writer.AddAttribute("class", "nav-link" + (item.Selected ? " active" : "")); - loadedTabName = GetTabName(item) ?? itemId; - } - else - { - // no content, create real link instead - var url = item.GenerateUrl(base.ViewContext.RequestContext).NullEmpty(); - - if (url == null) - { - writer.AddAttribute("href", "#"); - } - else - { - if (item.Ajax) - { - writer.AddAttribute("href", itemId); - writer.AddAttribute("data-ajax-url", url); - writer.AddAttribute("data-toggle", "tab"); - writer.AddAttribute("class", "nav-link" + (item.Selected ? " active" : "")); + { + writer.AddAttribute("href", itemId); + writer.AddAttribute("data-toggle", "tab"); + writer.AddAttribute("data-loaded", "true"); + loadedTabName = GetTabName(item) ?? itemId; + } + else + { + // no content, create real link instead + var url = item.GenerateUrl(base.ViewContext.RequestContext).NullEmpty(); + + if (url == null) + { + writer.AddAttribute("href", "#"); + } + else + { + if (item.Ajax) + { + writer.AddAttribute("href", itemId); + writer.AddAttribute("data-ajax-url", url); + writer.AddAttribute("data-toggle", "tab"); } - else - { - writer.AddAttribute("href", url); - } - } - } - - if (item.BadgeText.HasValue()) - { - item.LinkHtmlAttributes.AppendCssClass("clearfix"); - } - - writer.AddAttributes(item.LinkHtmlAttributes); - writer.RenderBeginTag("a"); - { - // Tab Icon - if (item.Icon.HasValue()) - { - writer.AddAttribute("class", item.Icon); - writer.RenderBeginTag("i"); - writer.RenderEndTag(); // i - } - else if (item.ImageUrl.HasValue()) - { - var url = new UrlHelper(this.ViewContext.RequestContext); - writer.AddAttribute("src", url.Content(item.ImageUrl)); - writer.AddAttribute("alt", "Icon"); - writer.RenderBeginTag("img"); - writer.RenderEndTag(); // img - } - - // Caption - writer.AddAttribute("class", "tab-caption"); - writer.RenderBeginTag("span"); - writer.WriteEncodedText(item.Text); - writer.RenderEndTag(); - - // Badge - if (item.BadgeText.HasValue()) - { - // Label/badge - temp = "ml-2 badge"; - temp += " badge-" + item.BadgeStyle.ToString().ToLower(); - if (base.Component.Position == TabsPosition.Left) - { - temp += " float-right"; // looks nicer - } - writer.AddAttribute("class", temp); - writer.RenderBeginTag("span"); - writer.WriteEncodedText(item.BadgeText); - writer.RenderEndTag(); // span > badge - } - - // Nav link short summary for collapsed state - if (this.Component.IsResponsive && item.Summary.HasValue()) - { - writer.AddAttribute("class", "nav-link-summary"); - writer.RenderBeginTag("span"); - writer.WriteEncodedText(item.Summary); - writer.RenderEndTag(); - } - - writer.RenderEndTag(); // a - } - writer.RenderEndTag(); // li - } - - return loadedTabName; - } - - private string GetTabName(Tab tab) - { - if (tab.LinkHtmlAttributes.TryGetValue("data-tab-name", out object value)) - { - return value.ToString(); - } - return null; - } + else + { + writer.AddAttribute("href", url); + } + } + } + + if (item.BadgeText.HasValue()) + { + item.LinkHtmlAttributes.AppendCssClass("clearfix"); + } + + writer.AddAttributes(item.LinkHtmlAttributes); + writer.RenderBeginTag("a"); + { + // Tab Icon + if (item.Icon.HasValue()) + { + writer.AddAttribute("class", item.Icon); + writer.RenderBeginTag("i"); + writer.RenderEndTag(); // i + } + else if (item.ImageUrl.HasValue()) + { + var url = new UrlHelper(this.ViewContext.RequestContext); + writer.AddAttribute("src", url.Content(item.ImageUrl)); + writer.AddAttribute("alt", "Icon"); + writer.RenderBeginTag("img"); + writer.RenderEndTag(); // img + } + + // Caption + writer.AddAttribute("class", "tab-caption"); + writer.RenderBeginTag("span"); + writer.WriteEncodedText(item.Text); + writer.RenderEndTag(); + + // Badge + if (item.BadgeText.HasValue()) + { + // Label/badge + temp = "ml-2 badge"; + temp += " badge-" + item.BadgeStyle.ToString().ToLower(); + if (base.Component.Position == TabsPosition.Left) + { + temp += " float-right"; // looks nicer + } + writer.AddAttribute("class", temp); + writer.RenderBeginTag("span"); + writer.WriteEncodedText(item.BadgeText); + writer.RenderEndTag(); // span > badge + } + + // Nav link short summary for collapsed state + if (this.Component.IsResponsive && item.Summary.HasValue()) + { + writer.AddAttribute("class", "nav-link-summary"); + writer.RenderBeginTag("span"); + writer.WriteEncodedText(item.Summary); + writer.RenderEndTag(); + } + + writer.RenderEndTag(); // a + } + writer.RenderEndTag(); // li + } + + return loadedTabName; + } + + private string GetTabName(Tab tab) + { + if (tab.LinkHtmlAttributes.TryGetValue("data-tab-name", out object value)) + { + return value.ToString(); + } + return null; + } protected virtual void RenderItemContent(HtmlTextWriter writer, Tab item, int index) - { - //
    {content}
    - item.ContentHtmlAttributes.AppendCssClass("tab-pane"); - item.ContentHtmlAttributes.Add("role", "tabpanel"); - if (base.Component.Fade) - { - item.ContentHtmlAttributes.AppendCssClass("fade"); - } - if (item.Selected) - { - if (base.Component.Fade) - { - item.ContentHtmlAttributes.AppendCssClass("show"); - } - item.ContentHtmlAttributes.AppendCssClass("active"); - } - writer.AddAttributes(item.ContentHtmlAttributes); - writer.AddAttribute("id", BuildItemId(item, index)); - writer.RenderBeginTag("div"); - { + { + //
    {content}
    + item.ContentHtmlAttributes.AppendCssClass("tab-pane"); + item.ContentHtmlAttributes.Add("role", "tabpanel"); + if (base.Component.Fade) + { + item.ContentHtmlAttributes.AppendCssClass("fade"); + } + if (item.Selected) + { + if (base.Component.Fade) + { + item.ContentHtmlAttributes.AppendCssClass("show"); + } + item.ContentHtmlAttributes.AppendCssClass("active"); + } + writer.AddAttributes(item.ContentHtmlAttributes); + writer.AddAttribute("id", BuildItemId(item, index)); + writer.RenderBeginTag("div"); + { if (item.Content != null) - { - writer.WriteLine(item.Content.ToHtmlString()); - } - } - writer.RenderEndTag(); // div - } + { + writer.WriteLine(item.Content.ToHtmlString()); + } + } + writer.RenderEndTag(); // div + } private string BuildItemId(Tab item, int index) { diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/ViewBasedComponentRenderer.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/ViewBasedComponentRenderer.cs index 0ef84c00a0..1a5cd76277 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/ViewBasedComponentRenderer.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/ViewBasedComponentRenderer.cs @@ -3,20 +3,20 @@ namespace SmartStore.Web.Framework.UI { public class ViewBasedComponentRenderer : ComponentRenderer where TComponent : Component - { - private readonly string _viewName; + { + private readonly string _viewName; - public ViewBasedComponentRenderer() - : this(typeof(TComponent).Name) - { - } + public ViewBasedComponentRenderer() + : this(typeof(TComponent).Name) + { + } public ViewBasedComponentRenderer(string viewName) - { - Guard.NotEmpty(viewName, nameof(viewName)); + { + Guard.NotEmpty(viewName, nameof(viewName)); - _viewName = viewName; - } + _viewName = viewName; + } protected virtual string GetViewName() { @@ -24,13 +24,13 @@ protected virtual string GetViewName() } public override void Render() - { - HtmlHelper.RenderPartial(GetViewName(), Component); - } + { + HtmlHelper.RenderPartial(GetViewName(), Component); + } - public override string ToHtmlString() - { - return HtmlHelper.Partial(GetViewName(), Component).ToString(); - } - } + public override string ToHtmlString() + { + return HtmlHelper.Partial(GetViewName(), Component).ToString(); + } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Window/Window.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Window/Window.cs index 154a7e57d3..29463fd2d7 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Window/Window.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Window/Window.cs @@ -1,43 +1,39 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Web.Routing; using System.Web.WebPages; namespace SmartStore.Web.Framework.UI { - public enum WindowSize - { - Small, - Medium, - Large, - Flex, - FlexSmall - } - - public class Window : Component + public enum WindowSize + { + Small, + Medium, + Large, + Flex, + FlexSmall + } + + public class Window : Component { public Window() { - this.Size = WindowSize.Medium; - this.Fade = true; - this.Focus = true; + this.Size = WindowSize.Medium; + this.Fade = true; + this.Focus = true; this.BackDrop = true; this.ShowClose = true; - this.Show = true; + this.Show = true; this.CloseOnEscapePress = true; - this.CloseOnBackdropClick = true; - this.RenderAtPageEnd = true; - this.BodyHtmlAttributes = new RouteValueDictionary(); - } - - public override bool NameIsRequired - { - get { return true; } + this.CloseOnBackdropClick = true; + this.RenderAtPageEnd = true; + this.BodyHtmlAttributes = new RouteValueDictionary(); } - public WindowSize Size { get; set; } + public override bool NameIsRequired => true; + + public WindowSize Size { get; set; } - public string Title { get; set; } + public string Title { get; set; } public HelperResult Content { get; set; } @@ -47,22 +43,22 @@ public override bool NameIsRequired public bool Fade { get; set; } - public bool Focus { get; set; } + public bool Focus { get; set; } - public bool Show { get; set; } + public bool Show { get; set; } - public bool BackDrop { get; set; } + public bool BackDrop { get; set; } public bool ShowClose { get; set; } - public bool CenterVertically { get; set; } + public bool CenterVertically { get; set; } - public bool CloseOnEscapePress { get; set; } + public bool CloseOnEscapePress { get; set; } - public bool CloseOnBackdropClick { get; set; } + public bool CloseOnBackdropClick { get; set; } - public bool RenderAtPageEnd { get; set; } + public bool RenderAtPageEnd { get; set; } - public IDictionary BodyHtmlAttributes { get; set; } - } + public IDictionary BodyHtmlAttributes { get; set; } + } } diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Window/WindowBuilder.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Window/WindowBuilder.cs index 0b8e9d938b..ea685065a5 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Window/WindowBuilder.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Window/WindowBuilder.cs @@ -13,13 +13,13 @@ public WindowBuilder(Window Component, HtmlHelper htmlHelper) { } - public WindowBuilder Size(WindowSize value) - { - base.Component.Size = value; - return this; - } + public WindowBuilder Size(WindowSize value) + { + base.Component.Size = value; + return this; + } - public WindowBuilder Title(string value) + public WindowBuilder Title(string value) { base.Component.Title = value; return this; @@ -81,52 +81,52 @@ public WindowBuilder ShowClose(bool value) return this; } - public WindowBuilder Focus(bool value) - { - base.Component.Focus = value; - return this; - } + public WindowBuilder Focus(bool value) + { + base.Component.Focus = value; + return this; + } - public WindowBuilder CenterVertically(bool value) - { - base.Component.CenterVertically = value; - return this; - } + public WindowBuilder CenterVertically(bool value) + { + base.Component.CenterVertically = value; + return this; + } - public WindowBuilder Show(bool value) - { - base.Component.Show = value; - return this; - } + public WindowBuilder Show(bool value) + { + base.Component.Show = value; + return this; + } - public WindowBuilder CloseOnEscapePress(bool value) + public WindowBuilder CloseOnEscapePress(bool value) { base.Component.CloseOnEscapePress = value; return this; } - public WindowBuilder CloseOnBackdropClick(bool value) - { - base.Component.CloseOnBackdropClick = value; - return this; - } - - public WindowBuilder RenderAtPageEnd(bool value) - { - base.Component.RenderAtPageEnd = value; - return this; - } - - public WindowBuilder BodyHtmlAttributes(object attributes) - { - return this.BodyHtmlAttributes(CommonHelper.ObjectToDictionary(attributes)); - } - - public WindowBuilder BodyHtmlAttributes(IDictionary attributes) - { - base.Component.BodyHtmlAttributes.Clear(); - base.Component.BodyHtmlAttributes.Merge(attributes); - return this; - } - } + public WindowBuilder CloseOnBackdropClick(bool value) + { + base.Component.CloseOnBackdropClick = value; + return this; + } + + public WindowBuilder RenderAtPageEnd(bool value) + { + base.Component.RenderAtPageEnd = value; + return this; + } + + public WindowBuilder BodyHtmlAttributes(object attributes) + { + return this.BodyHtmlAttributes(CommonHelper.ObjectToDictionary(attributes)); + } + + public WindowBuilder BodyHtmlAttributes(IDictionary attributes) + { + base.Component.BodyHtmlAttributes.Clear(); + base.Component.BodyHtmlAttributes.Merge(attributes); + return this; + } + } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web.Framework/UI/Components/Window/WindowRenderer.cs b/src/Presentation/SmartStore.Web.Framework/UI/Components/Window/WindowRenderer.cs index 86d49ab9eb..6ba1c06b7c 100644 --- a/src/Presentation/SmartStore.Web.Framework/UI/Components/Window/WindowRenderer.cs +++ b/src/Presentation/SmartStore.Web.Framework/UI/Components/Window/WindowRenderer.cs @@ -1,117 +1,116 @@ -using System; -using System.Web.UI; +using System.Web.UI; namespace SmartStore.Web.Framework.UI { - // TODO: (mc) make modal window renderer BS4 ready (after backend has been updated to BS4) - public class WindowRenderer : ComponentRenderer + // TODO: (mc) make modal window renderer BS4 ready (after backend has been updated to BS4) + public class WindowRenderer : ComponentRenderer { - public override void Render() - { - if (Component.RenderAtPageEnd) - { - using (this.HtmlHelper.BeginZoneContent("end")) - { - base.Render(); - } - } - else - { - base.Render(); - } - } - - protected override void WriteHtmlCore(HtmlTextWriter writer) + public override void Render() + { + if (Component.RenderAtPageEnd) + { + using (this.HtmlHelper.BeginZoneContent("end")) + { + base.Render(); + } + } + else + { + base.Render(); + } + } + + protected override void WriteHtmlCore(HtmlTextWriter writer) { var win = base.Component; win.HtmlAttributes.AppendCssClass("modal"); - win.HtmlAttributes["role"] = "dialog"; - win.HtmlAttributes["tabindex"] = "-1"; - win.HtmlAttributes["aria-hidden"] = "true"; - win.HtmlAttributes["aria-labelledby"] = win.Id + "Label"; + win.HtmlAttributes["role"] = "dialog"; + win.HtmlAttributes["tabindex"] = "-1"; + win.HtmlAttributes["aria-hidden"] = "true"; + win.HtmlAttributes["aria-labelledby"] = win.Id + "Label"; - if (win.Fade) + if (win.Fade) { win.HtmlAttributes.AppendCssClass("fade"); } - // Other options - win.HtmlAttributes["data-keyboard"] = win.CloseOnEscapePress.ToString().ToLower(); - win.HtmlAttributes["data-show"] = win.Show.ToString().ToLower(); - win.HtmlAttributes["data-focus"] = win.Focus.ToString().ToLower(); - win.HtmlAttributes["data-backdrop"] = win.BackDrop - ? (win.CloseOnBackdropClick ? "true" : "static") - : "false"; + // Other options + win.HtmlAttributes["data-keyboard"] = win.CloseOnEscapePress.ToString().ToLower(); + win.HtmlAttributes["data-show"] = win.Show.ToString().ToLower(); + win.HtmlAttributes["data-focus"] = win.Focus.ToString().ToLower(); + win.HtmlAttributes["data-backdrop"] = win.BackDrop + ? (win.CloseOnBackdropClick ? "true" : "static") + : "false"; writer.AddAttributes(win.HtmlAttributes); - writer.RenderBeginTag("div"); // div.modal - { - var className = "modal-dialog"; - switch (win.Size) - { - case WindowSize.Small: - className += " modal-sm"; - break; - case WindowSize.Large: - className += " modal-lg"; - break; - case WindowSize.Flex: - className += " modal-flex"; - break; - case WindowSize.FlexSmall: - className += " modal-flex modal-flex-sm"; - break; - } - - if (win.CenterVertically) - { - className += " modal-dialog-centered"; - } - - writer.AddAttribute("class", className); - win.HtmlAttributes["role"] = "document"; - writer.RenderBeginTag("div"); // div.modal-dialog - { - writer.AddAttribute("class", "modal-content"); - writer.RenderBeginTag("div"); // div.modal-content - { - // HEADER - if (win.ShowClose && win.Title.HasValue()) - { - this.RenderHeader(writer); - } - - // BODY - this.RenderBody(writer); - - // FOOTER - if (win.FooterContent != null) - { - this.RenderFooter(writer); - } - } - writer.RenderEndTag(); // div.modal-content - } - writer.RenderEndTag(); // div.modal-dialog - } + writer.RenderBeginTag("div"); // div.modal + { + var className = "modal-dialog"; + switch (win.Size) + { + case WindowSize.Small: + className += " modal-sm"; + break; + case WindowSize.Large: + className += " modal-lg"; + break; + case WindowSize.Flex: + className += " modal-flex"; + break; + case WindowSize.FlexSmall: + className += " modal-flex modal-flex-sm"; + break; + } + + if (win.CenterVertically) + { + className += " modal-dialog-centered"; + } + + writer.AddAttribute("class", className); + win.HtmlAttributes["role"] = "document"; + writer.RenderBeginTag("div"); // div.modal-dialog + { + writer.AddAttribute("class", "modal-content"); + writer.RenderBeginTag("div"); // div.modal-content + { + // HEADER + if (win.ShowClose && win.Title.HasValue()) + { + this.RenderHeader(writer); + } + + // BODY + this.RenderBody(writer); + + // FOOTER + if (win.FooterContent != null) + { + this.RenderFooter(writer); + } + } + writer.RenderEndTag(); // div.modal-content + } + writer.RenderEndTag(); // div.modal-dialog + } writer.RenderEndTag(); // div.modal } protected virtual void RenderHeader(HtmlTextWriter writer) { var win = base.Component; - + writer.AddAttribute("class", "modal-header"); writer.RenderBeginTag("div"); - if (win.Title.HasValue()) - { - writer.Write("".FormatCurrent(win.Id + "Label", win.Title)); - } + if (win.Title.HasValue()) + { + writer.Write("".FormatCurrent(win.Id + "Label", win.Title)); + } - if (win.ShowClose) + if (win.ShowClose) { writer.Write(""); } @@ -123,18 +122,18 @@ protected virtual void RenderBody(HtmlTextWriter writer) { var win = base.Component; - win.BodyHtmlAttributes.PrependCssClass("modal-body"); - writer.AddAttributes(win.BodyHtmlAttributes); + win.BodyHtmlAttributes.PrependCssClass("modal-body"); + writer.AddAttributes(win.BodyHtmlAttributes); writer.RenderBeginTag("div"); - if (win.Content != null) - { - win.Content.WriteTo(writer); - } - else if (win.ContentUrl.HasValue()) - { - writer.Write("' - ).bind('load', function () { - var fileInputClones, - paramNames = $.isArray(options.paramName) ? - options.paramName : [options.paramName]; - iframe - .unbind('load') - .bind('load', function () { - var response; - // Wrap in a try/catch block to catch exceptions thrown - // when trying to access cross-domain iframe contents: - try { - response = iframe.contents(); - // Google Chrome and Firefox do not throw an - // exception when calling iframe.contents() on - // cross-domain requests, so we unify the response: - if (!response.length || !response[0].firstChild) { - throw new Error(); - } - } catch (e) { - response = undefined; - } - // The complete callback returns the - // iframe content document as response object: - completeCallback( - 200, - 'success', - {'iframe': response} - ); - // Fix for IE endless progress bar activity bug - // (happens on form submits to iframe targets): - $('') - .appendTo(form); - form.remove(); - }); - form - .prop('target', iframe.prop('name')) - .prop('action', options.url) - .prop('method', options.type); - if (options.formData) { - $.each(options.formData, function (index, field) { - $('') - .prop('name', field.name) - .val(field.value) - .appendTo(form); - }); - } - if (options.fileInput && options.fileInput.length && - options.type === 'POST') { - fileInputClones = options.fileInput.clone(); - // Insert a clone for each file input field: - options.fileInput.after(function (index) { - return fileInputClones[index]; - }); - if (options.paramName) { - options.fileInput.each(function (index) { - $(this).prop( - 'name', - paramNames[index] || options.paramName - ); - }); - } - // Appending the file input fields to the hidden form - // removes them from their original location: - form - .append(options.fileInput) - .prop('enctype', 'multipart/form-data') - // enctype must be set as encoding for IE: - .prop('encoding', 'multipart/form-data'); - } - form.submit(); - // Insert the file input fields at their original location - // by replacing the clones with the originals: - if (fileInputClones && fileInputClones.length) { - options.fileInput.each(function (index, input) { - var clone = $(fileInputClones[index]); - $(input).prop('name', clone.prop('name')); - clone.replaceWith(input); - }); - } - }); - form.append(iframe).appendTo(document.body); - }, - abort: function () { - if (iframe) { - // javascript:false as iframe src aborts the request - // and prevents warning popups on HTTPS in IE6. - // concat is used to avoid the "Script URL" JSLint error: - iframe - .unbind('load') - .prop('src', 'javascript'.concat(':false;')); - } - if (form) { - form.remove(); - } - } - }; - } - }); - - // The iframe transport returns the iframe content document as response. - // The following adds converters from iframe to text, json, html, and script: - $.ajaxSetup({ - converters: { - 'iframe text': function (iframe) { - return $(iframe[0].body).text(); - }, - 'iframe json': function (iframe) { - return $.parseJSON($(iframe[0].body).text()); - }, - 'iframe html': function (iframe) { - return $(iframe[0].body).html(); - }, - 'iframe script': function (iframe) { - return $.globalEval($(iframe[0].body).text()); - } - } - }); - -})); diff --git a/src/Presentation/SmartStore.Web/Content/vendors/fileuploader/locale.js b/src/Presentation/SmartStore.Web/Content/vendors/fileuploader/locale.js deleted file mode 100644 index ea64b0a870..0000000000 --- a/src/Presentation/SmartStore.Web/Content/vendors/fileuploader/locale.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * jQuery File Upload Plugin Localization Example 6.5.1 - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2012, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -/*global window */ - -window.locale = { - "fileupload": { - "errors": { - "maxFileSize": "File is too big", - "minFileSize": "File is too small", - "acceptFileTypes": "Filetype not allowed", - "maxNumberOfFiles": "Max number of files exceeded", - "uploadedBytes": "Uploaded bytes exceed file size", - "emptyResult": "Empty file upload result" - }, - "error": "Error", - "start": "Start", - "cancel": "Cancel", - "destroy": "Delete" - } -}; diff --git a/src/Presentation/SmartStore.Web/Content/vendors/imagesloaded/imagesloaded.pkgd.min.js b/src/Presentation/SmartStore.Web/Content/vendors/imagesloaded/imagesloaded.pkgd.min.js new file mode 100644 index 0000000000..e443a77d62 --- /dev/null +++ b/src/Presentation/SmartStore.Web/Content/vendors/imagesloaded/imagesloaded.pkgd.min.js @@ -0,0 +1,7 @@ +/*! + * imagesLoaded PACKAGED v4.1.4 + * JavaScript is all like "You images are done yet or what?" + * MIT License + */ + +!function(e,t){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",t):"object"==typeof module&&module.exports?module.exports=t():e.EvEmitter=t()}("undefined"!=typeof window?window:this,function(){function e(){}var t=e.prototype;return t.on=function(e,t){if(e&&t){var i=this._events=this._events||{},n=i[e]=i[e]||[];return n.indexOf(t)==-1&&n.push(t),this}},t.once=function(e,t){if(e&&t){this.on(e,t);var i=this._onceEvents=this._onceEvents||{},n=i[e]=i[e]||{};return n[t]=!0,this}},t.off=function(e,t){var i=this._events&&this._events[e];if(i&&i.length){var n=i.indexOf(t);return n!=-1&&i.splice(n,1),this}},t.emitEvent=function(e,t){var i=this._events&&this._events[e];if(i&&i.length){i=i.slice(0),t=t||[];for(var n=this._onceEvents&&this._onceEvents[e],o=0;o'); - items.find(options.handle).mousedown(function() { - isHandle = true; - }).mouseup(function() { - isHandle = false; - }); - $(this).data('items', options.items) - placeholders = placeholders.add(placeholder); - if (options.connectWith) { - $(options.connectWith).add(this).data('connectWith', options.connectWith); - } - items.attr('draggable', 'true').on('dragstart.h5s', function(e) { - if (options.handle && !isHandle) { - return false; - } - isHandle = false; - var dt = e.originalEvent.dataTransfer; - dt.effectAllowed = 'move'; - dt.setData('Text', 'dummy'); - index = (dragging = $(this)).addClass('sortable-dragging').index(); - }).on('dragend.h5s', function() { - if (!dragging) { - return; - } - dragging.removeClass('sortable-dragging').show(); - placeholders.detach(); - if (index != dragging.index()) { - dragging.parent().trigger('sortupdate', {item: dragging}); - } - dragging = null; - }).not('a[href], img').on('selectstart.h5s', function() { - this.dragDrop && this.dragDrop(); - return false; - }).end().add([this, placeholder]).on('dragover.h5s dragenter.h5s drop.h5s', function(e) { - if (!items.is(dragging) && options.connectWith !== $(dragging).parent().data('connectWith')) { - return true; - } - if (e.type == 'drop') { - e.stopPropagation(); - placeholders.filter(':visible').after(dragging); - dragging.trigger('dragend.h5s'); - return false; - } - e.preventDefault(); - e.originalEvent.dataTransfer.dropEffect = 'move'; - if (items.is(this)) { - if (options.forcePlaceholderSize) { - placeholder.height(dragging.outerHeight()); - } - dragging.hide(); - $(this)[placeholder.index() < $(this).index() ? 'after' : 'before'](placeholder); - placeholders.not(placeholder).detach(); - } else if (!placeholders.is(this) && !$(this).children(options.items).length) { - placeholders.detach(); - $(this).append(placeholder); - } - return false; - }); - }); -}; -})(jQuery); diff --git a/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.animate.js b/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.animate.js index a4122fd1e1..c7bd39e54f 100644 --- a/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.animate.js +++ b/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.animate.js @@ -1,3 +1,2 @@ // Animate -!function(n,i){"function"==typeof define&&define.amd?define("pnotify.animate",["jquery","pnotify"],i):"object"==typeof exports&&"undefined"!=typeof module?module.exports=i(require("jquery"),require("./pnotify")):i(n.jQuery,n.PNotify)}("undefined"!=typeof window?window:this,function(n,i){return i.prototype.options.animate={animate:!1,in_class:"",out_class:""},i.prototype.modules.animate={init:function(n,i){this.setUpAnimations(n,i),n.attention=function(i,t){n.elem.one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",function(){n.elem.removeClass(i),t&&t.call(n)}).addClass("animated "+i)}},update:function(n,i,t){i.animate!=t.animate&&this.setUpAnimations(n,i)},setUpAnimations:function(n,i){if(i.animate){n.options.animation="none",n.elem.removeClass("ui-pnotify-fade-slow ui-pnotify-fade-normal ui-pnotify-fade-fast"),n._animateIn||(n._animateIn=n.animateIn),n._animateOut||(n._animateOut=n.animateOut),n.animateIn=this.animateIn.bind(this),n.animateOut=this.animateOut.bind(this);var t=400;"slow"===n.options.animate_speed?t=600:"fast"===n.options.animate_speed?t=200:n.options.animate_speed>0&&(t=n.options.animate_speed),t/=1e3,n.elem.addClass("animated").css({"-webkit-animation-duration":t+"s","-moz-animation-duration":t+"s","animation-duration":t+"s"})}else n._animateIn&&n._animateOut&&(n.animateIn=n._animateIn,delete n._animateIn,n.animateOut=n._animateOut,delete n._animateOut,n.elem.addClass("animated"))},animateIn:function(n){this.notice.animating="in";var i=this;n=function(){i.notice.elem.removeClass(i.options.in_class),this&&this.call(),i.notice.animating=!1}.bind(n),this.notice.elem.show().one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",n).removeClass(this.options.out_class).addClass("ui-pnotify-in").addClass(this.options.in_class)},animateOut:function(n){this.notice.animating="out";var i=this;n=function(){i.notice.elem.removeClass("ui-pnotify-in "+i.options.out_class),this&&this.call(),i.notice.animating=!1}.bind(n),this.notice.elem.one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",n).removeClass(this.options.in_class).addClass(this.options.out_class)}},i}); -//# sourceMappingURL=pnotify.animate.js.map \ No newline at end of file +!function(n,i){"function"==typeof define&&define.amd?define("pnotify.animate",["jquery","pnotify"],i):"object"==typeof exports&&"undefined"!=typeof module?module.exports=i(require("jquery"),require("./pnotify")):i(n.jQuery,n.PNotify)}("undefined"!=typeof window?window:this,function(n,i){return i.prototype.options.animate={animate:!1,in_class:"",out_class:""},i.prototype.modules.animate={init:function(n,i){this.setUpAnimations(n,i),n.attention=function(i,t){n.elem.one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",function(){n.elem.removeClass(i),t&&t.call(n)}).addClass("animated "+i)}},update:function(n,i,t){i.animate!=t.animate&&this.setUpAnimations(n,i)},setUpAnimations:function(n,i){if(i.animate){n.options.animation="none",n.elem.removeClass("ui-pnotify-fade-slow ui-pnotify-fade-normal ui-pnotify-fade-fast"),n._animateIn||(n._animateIn=n.animateIn),n._animateOut||(n._animateOut=n.animateOut),n.animateIn=this.animateIn.bind(this),n.animateOut=this.animateOut.bind(this);var t=400;"slow"===n.options.animate_speed?t=600:"fast"===n.options.animate_speed?t=200:n.options.animate_speed>0&&(t=n.options.animate_speed),t/=1e3,n.elem.addClass("animated").css({"-webkit-animation-duration":t+"s","-moz-animation-duration":t+"s","animation-duration":t+"s"})}else n._animateIn&&n._animateOut&&(n.animateIn=n._animateIn,delete n._animateIn,n.animateOut=n._animateOut,delete n._animateOut,n.elem.addClass("animated"))},animateIn:function(n){this.notice.animating="in";var i=this;n=function(){i.notice.elem.removeClass(i.options.in_class),this&&this.call(),i.notice.animating=!1}.bind(n),this.notice.elem.show().one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",n).removeClass(this.options.out_class).addClass("ui-pnotify-in").addClass(this.options.in_class)},animateOut:function(n){this.notice.animating="out";var i=this;n=function(){i.notice.elem.removeClass("ui-pnotify-in "+i.options.out_class),this&&this.call(),i.notice.animating=!1}.bind(n),this.notice.elem.one("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend",n).removeClass(this.options.in_class).addClass(this.options.out_class)}},i}); \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.buttons.js b/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.buttons.js index 0cc1d04f4e..365eecc13f 100644 --- a/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.buttons.js +++ b/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.buttons.js @@ -1,3 +1,2 @@ // Buttons -!function(o,s){"function"==typeof define&&define.amd?define("pnotify.buttons",["jquery","pnotify"],s):"object"==typeof exports&&"undefined"!=typeof module?module.exports=s(require("jquery"),require("./pnotify")):s(o.jQuery,o.PNotify)}("undefined"!=typeof window?window:this,function(o,s){return s.prototype.options.buttons={closer:!0,closer_hover:!0,sticker:!0,sticker_hover:!0,show_on_nonblock:!1,labels:{close:"Close",stick:"Stick",unstick:"Unstick"},classes:{closer:null,pin_up:null,pin_down:null}},s.prototype.modules.buttons={init:function(s,i){var n=this;s.elem.on({mouseenter:function(o){!n.options.sticker||s.options.nonblock&&s.options.nonblock.nonblock&&!n.options.show_on_nonblock||n.sticker.trigger("pnotify:buttons:toggleStick").css("visibility","visible"),!n.options.closer||s.options.nonblock&&s.options.nonblock.nonblock&&!n.options.show_on_nonblock||n.closer.css("visibility","visible")},mouseleave:function(o){n.options.sticker_hover&&n.sticker.css("visibility","hidden"),n.options.closer_hover&&n.closer.css("visibility","hidden")}}),this.sticker=o("
    ",{class:"ui-pnotify-sticker","aria-role":"button","aria-pressed":s.options.hide?"false":"true",tabindex:"0",title:s.options.hide?i.labels.stick:i.labels.unstick,css:{cursor:"pointer",visibility:i.sticker_hover?"hidden":"visible"},click:function(){s.options.hide=!s.options.hide,s.options.hide?s.queueRemove():s.cancelRemove(),o(this).trigger("pnotify:buttons:toggleStick")}}).bind("pnotify:buttons:toggleStick",function(){var i=null===n.options.classes.pin_up?s.styles.pin_up:n.options.classes.pin_up,e=null===n.options.classes.pin_down?s.styles.pin_down:n.options.classes.pin_down;o(this).attr("title",s.options.hide?n.options.labels.stick:n.options.labels.unstick).children().attr("class","").addClass(s.options.hide?i:e).attr("aria-pressed",s.options.hide?"false":"true")}).append("").trigger("pnotify:buttons:toggleStick").prependTo(s.container),(!i.sticker||s.options.nonblock&&s.options.nonblock.nonblock&&!i.show_on_nonblock)&&this.sticker.css("display","none"),this.closer=o("
    ",{class:"ui-pnotify-closer","aria-role":"button",tabindex:"0",title:i.labels.close,css:{cursor:"pointer",visibility:i.closer_hover?"hidden":"visible"},click:function(){s.remove(!1),n.sticker.css("visibility","hidden"),n.closer.css("visibility","hidden")}}).append(o("",{class:null===i.classes.closer?s.styles.closer:i.classes.closer})).prependTo(s.container),(!i.closer||s.options.nonblock&&s.options.nonblock.nonblock&&!i.show_on_nonblock)&&this.closer.css("display","none")},update:function(o,s){!s.closer||o.options.nonblock&&o.options.nonblock.nonblock&&!s.show_on_nonblock?this.closer.css("display","none"):s.closer&&this.closer.css("display","block"),!s.sticker||o.options.nonblock&&o.options.nonblock.nonblock&&!s.show_on_nonblock?this.sticker.css("display","none"):s.sticker&&this.sticker.css("display","block"),this.sticker.trigger("pnotify:buttons:toggleStick"),this.closer.find("span").attr("class","").addClass(null===s.classes.closer?o.styles.closer:s.classes.closer),s.sticker_hover?this.sticker.css("visibility","hidden"):o.options.nonblock&&o.options.nonblock.nonblock&&!s.show_on_nonblock||this.sticker.css("visibility","visible"),s.closer_hover?this.closer.css("visibility","hidden"):o.options.nonblock&&o.options.nonblock.nonblock&&!s.show_on_nonblock||this.closer.css("visibility","visible")}},o.extend(s.styling.brighttheme,{closer:"brighttheme-icon-closer",pin_up:"brighttheme-icon-sticker",pin_down:"brighttheme-icon-sticker brighttheme-icon-stuck"}),o.extend(s.styling.bootstrap3,{closer:"glyphicon glyphicon-remove",pin_up:"glyphicon glyphicon-pause",pin_down:"glyphicon glyphicon-play"}),o.extend(s.styling.fontawesome,{closer:"fa fa-times",pin_up:"fa fa-pause",pin_down:"fa fa-play"}),s}); -//# sourceMappingURL=pnotify.buttons.js.map \ No newline at end of file +!function(o,s){"function"==typeof define&&define.amd?define("pnotify.buttons",["jquery","pnotify"],s):"object"==typeof exports&&"undefined"!=typeof module?module.exports=s(require("jquery"),require("./pnotify")):s(o.jQuery,o.PNotify)}("undefined"!=typeof window?window:this,function(o,s){return s.prototype.options.buttons={closer:!0,closer_hover:!0,sticker:!0,sticker_hover:!0,show_on_nonblock:!1,labels:{close:"Close",stick:"Stick",unstick:"Unstick"},classes:{closer:null,pin_up:null,pin_down:null}},s.prototype.modules.buttons={init:function(s,i){var n=this;s.elem.on({mouseenter:function(o){!n.options.sticker||s.options.nonblock&&s.options.nonblock.nonblock&&!n.options.show_on_nonblock||n.sticker.trigger("pnotify:buttons:toggleStick").css("visibility","visible"),!n.options.closer||s.options.nonblock&&s.options.nonblock.nonblock&&!n.options.show_on_nonblock||n.closer.css("visibility","visible")},mouseleave:function(o){n.options.sticker_hover&&n.sticker.css("visibility","hidden"),n.options.closer_hover&&n.closer.css("visibility","hidden")}}),this.sticker=o("
    ",{class:"ui-pnotify-sticker","aria-role":"button","aria-pressed":s.options.hide?"false":"true",tabindex:"0",title:s.options.hide?i.labels.stick:i.labels.unstick,css:{cursor:"pointer",visibility:i.sticker_hover?"hidden":"visible"},click:function(){s.options.hide=!s.options.hide,s.options.hide?s.queueRemove():s.cancelRemove(),o(this).trigger("pnotify:buttons:toggleStick")}}).bind("pnotify:buttons:toggleStick",function(){var i=null===n.options.classes.pin_up?s.styles.pin_up:n.options.classes.pin_up,e=null===n.options.classes.pin_down?s.styles.pin_down:n.options.classes.pin_down;o(this).attr("title",s.options.hide?n.options.labels.stick:n.options.labels.unstick).children().attr("class","").addClass(s.options.hide?i:e).attr("aria-pressed",s.options.hide?"false":"true")}).append("").trigger("pnotify:buttons:toggleStick").prependTo(s.container),(!i.sticker||s.options.nonblock&&s.options.nonblock.nonblock&&!i.show_on_nonblock)&&this.sticker.css("display","none"),this.closer=o("
    ",{class:"ui-pnotify-closer","aria-role":"button",tabindex:"0",title:i.labels.close,css:{cursor:"pointer",visibility:i.closer_hover?"hidden":"visible"},click:function(){s.remove(!1),n.sticker.css("visibility","hidden"),n.closer.css("visibility","hidden")}}).append(o("",{class:null===i.classes.closer?s.styles.closer:i.classes.closer})).prependTo(s.container),(!i.closer||s.options.nonblock&&s.options.nonblock.nonblock&&!i.show_on_nonblock)&&this.closer.css("display","none")},update:function(o,s){!s.closer||o.options.nonblock&&o.options.nonblock.nonblock&&!s.show_on_nonblock?this.closer.css("display","none"):s.closer&&this.closer.css("display","block"),!s.sticker||o.options.nonblock&&o.options.nonblock.nonblock&&!s.show_on_nonblock?this.sticker.css("display","none"):s.sticker&&this.sticker.css("display","block"),this.sticker.trigger("pnotify:buttons:toggleStick"),this.closer.find("span").attr("class","").addClass(null===s.classes.closer?o.styles.closer:s.classes.closer),s.sticker_hover?this.sticker.css("visibility","hidden"):o.options.nonblock&&o.options.nonblock.nonblock&&!s.show_on_nonblock||this.sticker.css("visibility","visible"),s.closer_hover?this.closer.css("visibility","hidden"):o.options.nonblock&&o.options.nonblock.nonblock&&!s.show_on_nonblock||this.closer.css("visibility","visible")}},o.extend(s.styling.brighttheme,{closer:"brighttheme-icon-closer",pin_up:"brighttheme-icon-sticker",pin_down:"brighttheme-icon-sticker brighttheme-icon-stuck"}),o.extend(s.styling.bootstrap3,{closer:"glyphicon glyphicon-remove",pin_up:"glyphicon glyphicon-pause",pin_down:"glyphicon glyphicon-play"}),o.extend(s.styling.fontawesome,{closer:"fa fa-times",pin_up:"fa fa-pause",pin_down:"fa fa-play"}),s}); \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.js b/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.js index 9e2de16b3a..0904c8105c 100644 --- a/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.js +++ b/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.js @@ -3,5 +3,4 @@ PNotify 3.2.0 sciactive.com/pnotify/ (C) 2015 Hunter Perrin; Google, Inc. license Apache-2.0 */ -!function(t,i){"function"==typeof define&&define.amd?define("pnotify",["jquery"],function(s){return i(s,t)}):"object"==typeof exports&&"undefined"!=typeof module?module.exports=i(require("jquery"),global||t):t.PNotify=i(t.jQuery,t)}("undefined"!=typeof window?window:this,function(t,i){var s=function(i){var e,o,n={dir1:"down",dir2:"left",push:"bottom",spacing1:36,spacing2:36,context:t("body"),modal:!1},a=t(i),r=function(){o=t("body"),c.prototype.options.stack.context=o,a=t(i),a.bind("resize",function(){e&&clearTimeout(e),e=setTimeout(function(){c.positionAll(!0)},10)})},h=function(i){var s=t("
    ",{class:"ui-pnotify-modal-overlay"});return s.prependTo(i.context),i.overlay_close&&s.click(function(){c.removeStack(i)}),s},c=function(t){this.state="initializing",this.timer=null,this.animTimer=null,this.styles=null,this.elem=null,this.container=null,this.title_container=null,this.text_container=null,this.animating=!1,this.timerHide=!1,this.parseOptions(t),this.init()};return t.extend(c.prototype,{version:"3.2.0",options:{title:!1,title_escape:!1,text:!1,text_escape:!1,styling:"brighttheme",addclass:"",cornerclass:"",auto_display:!0,width:"300px",min_height:"16px",type:"notice",icon:!0,animation:"fade",animate_speed:"normal",shadow:!0,hide:!0,delay:8e3,mouse_reset:!0,remove:!0,insert_brs:!0,destroy:!0,stack:n},modules:{},runModules:function(t,i){var s;for(var e in this.modules)s="object"==typeof i&&e in i?i[e]:i,"function"==typeof this.modules[e][t]&&(this.modules[e].notice=this,this.modules[e].options="object"==typeof this.options[e]?this.options[e]:{},this.modules[e][t](this,"object"==typeof this.options[e]?this.options[e]:{},s))},init:function(){var i=this;return this.modules={},t.extend(!0,this.modules,c.prototype.modules),"object"==typeof this.options.styling?this.styles=this.options.styling:this.styles=c.styling[this.options.styling],this.elem=t("
    ",{class:"ui-pnotify "+this.options.addclass,css:{display:"none"},"aria-live":"assertive","aria-role":"alertdialog",mouseenter:function(t){if(i.options.mouse_reset&&"out"===i.animating){if(!i.timerHide)return;i.cancelRemove()}i.options.hide&&i.options.mouse_reset&&i.cancelRemove()},mouseleave:function(t){i.options.hide&&i.options.mouse_reset&&"out"!==i.animating&&i.queueRemove(),c.positionAll()}}),"fade"===this.options.animation&&this.elem.addClass("ui-pnotify-fade-"+this.options.animate_speed),this.container=t("
    ",{class:this.styles.container+" ui-pnotify-container "+("error"===this.options.type?this.styles.error:"info"===this.options.type?this.styles.info:"success"===this.options.type?this.styles.success:this.styles.notice),role:"alert"}).appendTo(this.elem),""!==this.options.cornerclass&&this.container.removeClass("ui-corner-all").addClass(this.options.cornerclass),this.options.shadow&&this.container.addClass("ui-pnotify-shadow"),!1!==this.options.icon&&t("
    ",{class:"ui-pnotify-icon"}).append(t("",{class:!0===this.options.icon?"error"===this.options.type?this.styles.error_icon:"info"===this.options.type?this.styles.info_icon:"success"===this.options.type?this.styles.success_icon:this.styles.notice_icon:this.options.icon})).prependTo(this.container),this.title_container=t("

    ",{class:"ui-pnotify-title"}).appendTo(this.container),!1===this.options.title?this.title_container.hide():this.options.title_escape?this.title_container.text(this.options.title):this.title_container.html(this.options.title),this.text_container=t("
    ",{class:"ui-pnotify-text","aria-role":"alert"}).appendTo(this.container),!1===this.options.text?this.text_container.hide():this.options.text_escape?this.text_container.text(this.options.text):this.text_container.html(this.options.insert_brs?String(this.options.text).replace(/\n/g,"
    "):this.options.text),"string"==typeof this.options.width&&this.elem.css("width",this.options.width),"string"==typeof this.options.min_height&&this.container.css("min-height",this.options.min_height),"top"===this.options.stack.push?c.notices=t.merge([this],c.notices):c.notices=t.merge(c.notices,[this]),"top"===this.options.stack.push&&this.queuePosition(!1,1),this.options.stack.animation=!1,this.runModules("init"),this.state="closed",this.options.auto_display&&this.open(),this},update:function(i){var s=this.options;return this.parseOptions(s,i),this.elem.removeClass("ui-pnotify-fade-slow ui-pnotify-fade-normal ui-pnotify-fade-fast"),"fade"===this.options.animation&&this.elem.addClass("ui-pnotify-fade-"+this.options.animate_speed),this.options.cornerclass!==s.cornerclass&&this.container.removeClass("ui-corner-all "+s.cornerclass).addClass(this.options.cornerclass),this.options.shadow!==s.shadow&&(this.options.shadow?this.container.addClass("ui-pnotify-shadow"):this.container.removeClass("ui-pnotify-shadow")),!1===this.options.addclass?this.elem.removeClass(s.addclass):this.options.addclass!==s.addclass&&this.elem.removeClass(s.addclass).addClass(this.options.addclass),!1===this.options.title?this.title_container.slideUp("fast"):this.options.title!==s.title&&(this.options.title_escape?this.title_container.text(this.options.title):this.title_container.html(this.options.title),!1===s.title&&this.title_container.slideDown(200)),!1===this.options.text?this.text_container.slideUp("fast"):this.options.text!==s.text&&(this.options.text_escape?this.text_container.text(this.options.text):this.text_container.html(this.options.insert_brs?String(this.options.text).replace(/\n/g,"
    "):this.options.text),!1===s.text&&this.text_container.slideDown(200)),this.options.type!==s.type&&this.container.removeClass(this.styles.error+" "+this.styles.notice+" "+this.styles.success+" "+this.styles.info).addClass("error"===this.options.type?this.styles.error:"info"===this.options.type?this.styles.info:"success"===this.options.type?this.styles.success:this.styles.notice),(this.options.icon!==s.icon||!0===this.options.icon&&this.options.type!==s.type)&&(this.container.find("div.ui-pnotify-icon").remove(),!1!==this.options.icon&&t("
    ",{class:"ui-pnotify-icon"}).append(t("",{class:!0===this.options.icon?"error"===this.options.type?this.styles.error_icon:"info"===this.options.type?this.styles.info_icon:"success"===this.options.type?this.styles.success_icon:this.styles.notice_icon:this.options.icon})).prependTo(this.container)),this.options.width!==s.width&&this.elem.animate({width:this.options.width}),this.options.min_height!==s.min_height&&this.container.animate({minHeight:this.options.min_height}),this.options.hide?s.hide||this.queueRemove():this.cancelRemove(),this.queuePosition(!0),this.runModules("update",s),this},open:function(){this.state="opening",this.runModules("beforeOpen");var t=this;return this.elem.parent().length||this.elem.appendTo(this.options.stack.context?this.options.stack.context:o),"top"!==this.options.stack.push&&this.position(!0),this.animateIn(function(){t.queuePosition(!0),t.options.hide&&t.queueRemove(),t.state="open",t.runModules("afterOpen")}),this},remove:function(s){this.state="closing",this.timerHide=!!s,this.runModules("beforeClose");var e=this;return this.timer&&(i.clearTimeout(this.timer),this.timer=null),this.animateOut(function(){if(e.state="closed",e.runModules("afterClose"),e.queuePosition(!0),e.options.remove&&e.elem.detach(),e.runModules("beforeDestroy"),e.options.destroy&&null!==c.notices){var i=t.inArray(e,c.notices);-1!==i&&c.notices.splice(i,1)}e.runModules("afterDestroy")}),this},get:function(){return this.elem},parseOptions:function(i,s){this.options=t.extend(!0,{},c.prototype.options),this.options.stack=c.prototype.options.stack;for(var e,o=[i,s],n=0;n(i.context.is(o)?a.height():i.context.prop("scrollHeight"))||"up"===i.dir1&&i.nextpos1+s.height()>(i.context.is(o)?a.height():i.context.prop("scrollHeight"))||"left"===i.dir1&&i.nextpos1+s.width()>(i.context.is(o)?a.width():i.context.prop("scrollWidth"))||"right"===i.dir1&&i.nextpos1+s.width()>(i.context.is(o)?a.width():i.context.prop("scrollWidth")))&&(i.nextpos1=i.firstpos1,i.nextpos2+=i.addpos2+(void 0===i.spacing2?25:i.spacing2),i.addpos2=0),"number"==typeof i.nextpos2&&(i.animation?s.css(p,i.nextpos2+"px"):(s.removeClass("ui-pnotify-move"),s.css(p,i.nextpos2+"px"),s.css(p),s.addClass("ui-pnotify-move"))),i.dir2){case"down":case"up":s.outerHeight(!0)>i.addpos2&&(i.addpos2=s.height());break;case"left":case"right":s.outerWidth(!0)>i.addpos2&&(i.addpos2=s.width())}switch("number"==typeof i.nextpos1&&(i.animation?s.css(c,i.nextpos1+"px"):(s.removeClass("ui-pnotify-move"),s.css(c,i.nextpos1+"px"),s.css(c),s.addClass("ui-pnotify-move"))),i.dir1){case"down":case"up":i.nextpos1+=s.height()+(void 0===i.spacing1?25:i.spacing1);break;case"left":case"right":i.nextpos1+=s.width()+(void 0===i.spacing1?25:i.spacing1)}}return this}},queuePosition:function(t,i){return e&&clearTimeout(e),i||(i=10),e=setTimeout(function(){c.positionAll(t)},i),this},cancelRemove:function(){return this.timer&&i.clearTimeout(this.timer),this.animTimer&&i.clearTimeout(this.animTimer),"closing"===this.state&&(this.state="open",this.animating=!1,this.elem.addClass("ui-pnotify-in"),"fade"===this.options.animation&&this.elem.addClass("ui-pnotify-fade-in")),this},queueRemove:function(){var t=this;return this.cancelRemove(),this.timer=i.setTimeout(function(){t.remove(!0)},isNaN(this.options.delay)?0:this.options.delay),this}}),t.extend(c,{notices:[],reload:s,removeAll:function(){t.each(c.notices,function(t,i){i.remove&&i.remove(!1)})},removeStack:function(i){t.each(c.notices,function(t,s){s.remove&&s.options.stack===i&&s.remove(!1)})},positionAll:function(i){if(e&&clearTimeout(e),e=null,c.notices&&c.notices.length)t.each(c.notices,function(t,s){var e=s.options.stack;e&&(e.overlay&&e.overlay.hide(),e.nextpos1=e.firstpos1,e.nextpos2=e.firstpos2,e.addpos2=0,e.animation=i)}),t.each(c.notices,function(t,i){i.position()});else{var s=c.prototype.options.stack;s&&(delete s.nextpos1,delete s.nextpos2)}},styling:{brighttheme:{container:"brighttheme",notice:"brighttheme-notice",notice_icon:"brighttheme-icon-notice",info:"brighttheme-info",info_icon:"brighttheme-icon-info",success:"brighttheme-success",success_icon:"brighttheme-icon-success",error:"brighttheme-error",error_icon:"brighttheme-icon-error"},bootstrap3:{container:"alert",notice:"alert-warning",notice_icon:"glyphicon glyphicon-exclamation-sign",info:"alert-info",info_icon:"glyphicon glyphicon-info-sign",success:"alert-success",success_icon:"glyphicon glyphicon-ok-sign",error:"alert-danger",error_icon:"glyphicon glyphicon-warning-sign"}}}),c.styling.fontawesome=t.extend({},c.styling.bootstrap3),t.extend(c.styling.fontawesome,{notice_icon:"fa fa-exclamation-circle",info_icon:"fa fa-info",success_icon:"fa fa-check",error_icon:"fa fa-warning"}),i.document.body?r():t(r),c};return s(i)}); -//# sourceMappingURL=pnotify.js.map \ No newline at end of file +!function(t,i){"function"==typeof define&&define.amd?define("pnotify",["jquery"],function(s){return i(s,t)}):"object"==typeof exports&&"undefined"!=typeof module?module.exports=i(require("jquery"),global||t):t.PNotify=i(t.jQuery,t)}("undefined"!=typeof window?window:this,function(t,i){var s=function(i){var e,o,n={dir1:"down",dir2:"left",push:"bottom",spacing1:36,spacing2:36,context:t("body"),modal:!1},a=t(i),r=function(){o=t("body"),c.prototype.options.stack.context=o,a=t(i),a.bind("resize",function(){e&&clearTimeout(e),e=setTimeout(function(){c.positionAll(!0)},10)})},h=function(i){var s=t("
    ",{class:"ui-pnotify-modal-overlay"});return s.prependTo(i.context),i.overlay_close&&s.click(function(){c.removeStack(i)}),s},c=function(t){this.state="initializing",this.timer=null,this.animTimer=null,this.styles=null,this.elem=null,this.container=null,this.title_container=null,this.text_container=null,this.animating=!1,this.timerHide=!1,this.parseOptions(t),this.init()};return t.extend(c.prototype,{version:"3.2.0",options:{title:!1,title_escape:!1,text:!1,text_escape:!1,styling:"brighttheme",addclass:"",cornerclass:"",auto_display:!0,width:"300px",min_height:"16px",type:"notice",icon:!0,animation:"fade",animate_speed:"normal",shadow:!0,hide:!0,delay:8e3,mouse_reset:!0,remove:!0,insert_brs:!0,destroy:!0,stack:n},modules:{},runModules:function(t,i){var s;for(var e in this.modules)s="object"==typeof i&&e in i?i[e]:i,"function"==typeof this.modules[e][t]&&(this.modules[e].notice=this,this.modules[e].options="object"==typeof this.options[e]?this.options[e]:{},this.modules[e][t](this,"object"==typeof this.options[e]?this.options[e]:{},s))},init:function(){var i=this;return this.modules={},t.extend(!0,this.modules,c.prototype.modules),"object"==typeof this.options.styling?this.styles=this.options.styling:this.styles=c.styling[this.options.styling],this.elem=t("
    ",{class:"ui-pnotify "+this.options.addclass,css:{display:"none"},"aria-live":"assertive","aria-role":"alertdialog",mouseenter:function(t){if(i.options.mouse_reset&&"out"===i.animating){if(!i.timerHide)return;i.cancelRemove()}i.options.hide&&i.options.mouse_reset&&i.cancelRemove()},mouseleave:function(t){i.options.hide&&i.options.mouse_reset&&"out"!==i.animating&&i.queueRemove(),c.positionAll()}}),"fade"===this.options.animation&&this.elem.addClass("ui-pnotify-fade-"+this.options.animate_speed),this.container=t("
    ",{class:this.styles.container+" ui-pnotify-container "+("error"===this.options.type?this.styles.error:"info"===this.options.type?this.styles.info:"success"===this.options.type?this.styles.success:this.styles.notice),role:"alert"}).appendTo(this.elem),""!==this.options.cornerclass&&this.container.removeClass("ui-corner-all").addClass(this.options.cornerclass),this.options.shadow&&this.container.addClass("ui-pnotify-shadow"),!1!==this.options.icon&&t("
    ",{class:"ui-pnotify-icon"}).append(t("",{class:!0===this.options.icon?"error"===this.options.type?this.styles.error_icon:"info"===this.options.type?this.styles.info_icon:"success"===this.options.type?this.styles.success_icon:this.styles.notice_icon:this.options.icon})).prependTo(this.container),this.title_container=t("

    ",{class:"ui-pnotify-title"}).appendTo(this.container),!1===this.options.title?this.title_container.hide():this.options.title_escape?this.title_container.text(this.options.title):this.title_container.html(this.options.title),this.text_container=t("
    ",{class:"ui-pnotify-text","aria-role":"alert"}).appendTo(this.container),!1===this.options.text?this.text_container.hide():this.options.text_escape?this.text_container.text(this.options.text):this.text_container.html(this.options.insert_brs?String(this.options.text).replace(/\n/g,"
    "):this.options.text),"string"==typeof this.options.width&&this.elem.css("width",this.options.width),"string"==typeof this.options.min_height&&this.container.css("min-height",this.options.min_height),"top"===this.options.stack.push?c.notices=t.merge([this],c.notices):c.notices=t.merge(c.notices,[this]),"top"===this.options.stack.push&&this.queuePosition(!1,1),this.options.stack.animation=!1,this.runModules("init"),this.state="closed",this.options.auto_display&&this.open(),this},update:function(i){var s=this.options;return this.parseOptions(s,i),this.elem.removeClass("ui-pnotify-fade-slow ui-pnotify-fade-normal ui-pnotify-fade-fast"),"fade"===this.options.animation&&this.elem.addClass("ui-pnotify-fade-"+this.options.animate_speed),this.options.cornerclass!==s.cornerclass&&this.container.removeClass("ui-corner-all "+s.cornerclass).addClass(this.options.cornerclass),this.options.shadow!==s.shadow&&(this.options.shadow?this.container.addClass("ui-pnotify-shadow"):this.container.removeClass("ui-pnotify-shadow")),!1===this.options.addclass?this.elem.removeClass(s.addclass):this.options.addclass!==s.addclass&&this.elem.removeClass(s.addclass).addClass(this.options.addclass),!1===this.options.title?this.title_container.slideUp("fast"):this.options.title!==s.title&&(this.options.title_escape?this.title_container.text(this.options.title):this.title_container.html(this.options.title),!1===s.title&&this.title_container.slideDown(200)),!1===this.options.text?this.text_container.slideUp("fast"):this.options.text!==s.text&&(this.options.text_escape?this.text_container.text(this.options.text):this.text_container.html(this.options.insert_brs?String(this.options.text).replace(/\n/g,"
    "):this.options.text),!1===s.text&&this.text_container.slideDown(200)),this.options.type!==s.type&&this.container.removeClass(this.styles.error+" "+this.styles.notice+" "+this.styles.success+" "+this.styles.info).addClass("error"===this.options.type?this.styles.error:"info"===this.options.type?this.styles.info:"success"===this.options.type?this.styles.success:this.styles.notice),(this.options.icon!==s.icon||!0===this.options.icon&&this.options.type!==s.type)&&(this.container.find("div.ui-pnotify-icon").remove(),!1!==this.options.icon&&t("
    ",{class:"ui-pnotify-icon"}).append(t("",{class:!0===this.options.icon?"error"===this.options.type?this.styles.error_icon:"info"===this.options.type?this.styles.info_icon:"success"===this.options.type?this.styles.success_icon:this.styles.notice_icon:this.options.icon})).prependTo(this.container)),this.options.width!==s.width&&this.elem.animate({width:this.options.width}),this.options.min_height!==s.min_height&&this.container.animate({minHeight:this.options.min_height}),this.options.hide?s.hide||this.queueRemove():this.cancelRemove(),this.queuePosition(!0),this.runModules("update",s),this},open:function(){this.state="opening",this.runModules("beforeOpen");var t=this;return this.elem.parent().length||this.elem.appendTo(this.options.stack.context?this.options.stack.context:o),"top"!==this.options.stack.push&&this.position(!0),this.animateIn(function(){t.queuePosition(!0),t.options.hide&&t.queueRemove(),t.state="open",t.runModules("afterOpen")}),this},remove:function(s){this.state="closing",this.timerHide=!!s,this.runModules("beforeClose");var e=this;return this.timer&&(i.clearTimeout(this.timer),this.timer=null),this.animateOut(function(){if(e.state="closed",e.runModules("afterClose"),e.queuePosition(!0),e.options.remove&&e.elem.detach(),e.runModules("beforeDestroy"),e.options.destroy&&null!==c.notices){var i=t.inArray(e,c.notices);-1!==i&&c.notices.splice(i,1)}e.runModules("afterDestroy")}),this},get:function(){return this.elem},parseOptions:function(i,s){this.options=t.extend(!0,{},c.prototype.options),this.options.stack=c.prototype.options.stack;for(var e,o=[i,s],n=0;n(i.context.is(o)?a.height():i.context.prop("scrollHeight"))||"up"===i.dir1&&i.nextpos1+s.height()>(i.context.is(o)?a.height():i.context.prop("scrollHeight"))||"left"===i.dir1&&i.nextpos1+s.width()>(i.context.is(o)?a.width():i.context.prop("scrollWidth"))||"right"===i.dir1&&i.nextpos1+s.width()>(i.context.is(o)?a.width():i.context.prop("scrollWidth")))&&(i.nextpos1=i.firstpos1,i.nextpos2+=i.addpos2+(void 0===i.spacing2?25:i.spacing2),i.addpos2=0),"number"==typeof i.nextpos2&&(i.animation?s.css(p,i.nextpos2+"px"):(s.removeClass("ui-pnotify-move"),s.css(p,i.nextpos2+"px"),s.css(p),s.addClass("ui-pnotify-move"))),i.dir2){case"down":case"up":s.outerHeight(!0)>i.addpos2&&(i.addpos2=s.height());break;case"left":case"right":s.outerWidth(!0)>i.addpos2&&(i.addpos2=s.width())}switch("number"==typeof i.nextpos1&&(i.animation?s.css(c,i.nextpos1+"px"):(s.removeClass("ui-pnotify-move"),s.css(c,i.nextpos1+"px"),s.css(c),s.addClass("ui-pnotify-move"))),i.dir1){case"down":case"up":i.nextpos1+=s.height()+(void 0===i.spacing1?25:i.spacing1);break;case"left":case"right":i.nextpos1+=s.width()+(void 0===i.spacing1?25:i.spacing1)}}return this}},queuePosition:function(t,i){return e&&clearTimeout(e),i||(i=10),e=setTimeout(function(){c.positionAll(t)},i),this},cancelRemove:function(){return this.timer&&i.clearTimeout(this.timer),this.animTimer&&i.clearTimeout(this.animTimer),"closing"===this.state&&(this.state="open",this.animating=!1,this.elem.addClass("ui-pnotify-in"),"fade"===this.options.animation&&this.elem.addClass("ui-pnotify-fade-in")),this},queueRemove:function(){var t=this;return this.cancelRemove(),this.timer=i.setTimeout(function(){t.remove(!0)},isNaN(this.options.delay)?0:this.options.delay),this}}),t.extend(c,{notices:[],reload:s,removeAll:function(){t.each(c.notices,function(t,i){i.remove&&i.remove(!1)})},removeStack:function(i){t.each(c.notices,function(t,s){s.remove&&s.options.stack===i&&s.remove(!1)})},positionAll:function(i){if(e&&clearTimeout(e),e=null,c.notices&&c.notices.length)t.each(c.notices,function(t,s){var e=s.options.stack;e&&(e.overlay&&e.overlay.hide(),e.nextpos1=e.firstpos1,e.nextpos2=e.firstpos2,e.addpos2=0,e.animation=i)}),t.each(c.notices,function(t,i){i.position()});else{var s=c.prototype.options.stack;s&&(delete s.nextpos1,delete s.nextpos2)}},styling:{brighttheme:{container:"brighttheme",notice:"brighttheme-notice",notice_icon:"brighttheme-icon-notice",info:"brighttheme-info",info_icon:"brighttheme-icon-info",success:"brighttheme-success",success_icon:"brighttheme-icon-success",error:"brighttheme-error",error_icon:"brighttheme-icon-error"},bootstrap3:{container:"alert",notice:"alert-warning",notice_icon:"glyphicon glyphicon-exclamation-sign",info:"alert-info",info_icon:"glyphicon glyphicon-info-sign",success:"alert-success",success_icon:"glyphicon glyphicon-ok-sign",error:"alert-danger",error_icon:"glyphicon glyphicon-warning-sign"}}}),c.styling.fontawesome=t.extend({},c.styling.bootstrap3),t.extend(c.styling.fontawesome,{notice_icon:"fa fa-exclamation-circle",info_icon:"fa fa-info",success_icon:"fa fa-check",error_icon:"fa fa-warning"}),i.document.body?r():t(r),c};return s(i)}); \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.mobile.js b/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.mobile.js index 8c068e27ae..5310caefcb 100644 --- a/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.mobile.js +++ b/src/Presentation/SmartStore.Web/Content/vendors/pnotify/js/pnotify.mobile.js @@ -1,3 +1,2 @@ // Mobile -!function(i,o){"function"==typeof define&&define.amd?define("pnotify.mobile",["jquery","pnotify"],o):"object"==typeof exports&&"undefined"!=typeof module?module.exports=o(require("jquery"),require("./pnotify")):o(i.jQuery,i.PNotify)}("undefined"!=typeof window?window:this,function(i,o){return o.prototype.options.mobile={swipe_dismiss:!0,styling:!0},o.prototype.modules.mobile={init:function(i,o){var t=this,e=null,n=null,s=null;this.swipe_dismiss=o.swipe_dismiss,this.doMobileStyling(i,o),i.elem.on({touchstart:function(o){t.swipe_dismiss&&(e=o.originalEvent.touches[0].screenX,s=i.elem.width(),i.container.css("left","0"))},touchmove:function(o){if(e&&t.swipe_dismiss){var a=o.originalEvent.touches[0].screenX;n=a-e;var c=(1-Math.abs(n)/s)*i.options.opacity;i.elem.css("opacity",c),i.container.css("left",n)}},touchend:function(){if(e&&t.swipe_dismiss){if(Math.abs(n)>40){var o=n<0?-2*s:2*s;i.elem.animate({opacity:0},100),i.container.animate({left:o},100),i.remove()}else i.elem.animate({opacity:i.options.opacity},100),i.container.animate({left:0},100);e=null,n=null,s=null}},touchcancel:function(){e&&t.swipe_dismiss&&(i.elem.animate({opacity:i.options.opacity},100),i.container.animate({left:0},100),e=null,n=null,s=null)}})},update:function(i,o){this.swipe_dismiss=o.swipe_dismiss,this.doMobileStyling(i,o)},doMobileStyling:function(o,t){t.styling?(o.elem.addClass("ui-pnotify-mobile-able"),i(window).width()<=480?(o.options.stack.mobileOrigSpacing1||(o.options.stack.mobileOrigSpacing1=o.options.stack.spacing1,o.options.stack.mobileOrigSpacing2=o.options.stack.spacing2),o.options.stack.spacing1=0,o.options.stack.spacing2=0):(o.options.stack.mobileOrigSpacing1||o.options.stack.mobileOrigSpacing2)&&(o.options.stack.spacing1=o.options.stack.mobileOrigSpacing1,delete o.options.stack.mobileOrigSpacing1,o.options.stack.spacing2=o.options.stack.mobileOrigSpacing2,delete o.options.stack.mobileOrigSpacing2)):(o.elem.removeClass("ui-pnotify-mobile-able"),o.options.stack.mobileOrigSpacing1&&(o.options.stack.spacing1=o.options.stack.mobileOrigSpacing1,delete o.options.stack.mobileOrigSpacing1),o.options.stack.mobileOrigSpacing2&&(o.options.stack.spacing2=o.options.stack.mobileOrigSpacing2,delete o.options.stack.mobileOrigSpacing2))}},o}); -//# sourceMappingURL=pnotify.mobile.js.map \ No newline at end of file +!function(i,o){"function"==typeof define&&define.amd?define("pnotify.mobile",["jquery","pnotify"],o):"object"==typeof exports&&"undefined"!=typeof module?module.exports=o(require("jquery"),require("./pnotify")):o(i.jQuery,i.PNotify)}("undefined"!=typeof window?window:this,function(i,o){return o.prototype.options.mobile={swipe_dismiss:!0,styling:!0},o.prototype.modules.mobile={init:function(i,o){var t=this,e=null,n=null,s=null;this.swipe_dismiss=o.swipe_dismiss,this.doMobileStyling(i,o),i.elem.on({touchstart:function(o){t.swipe_dismiss&&(e=o.originalEvent.touches[0].screenX,s=i.elem.width(),i.container.css("left","0"))},touchmove:function(o){if(e&&t.swipe_dismiss){var a=o.originalEvent.touches[0].screenX;n=a-e;var c=(1-Math.abs(n)/s)*i.options.opacity;i.elem.css("opacity",c),i.container.css("left",n)}},touchend:function(){if(e&&t.swipe_dismiss){if(Math.abs(n)>40){var o=n<0?-2*s:2*s;i.elem.animate({opacity:0},100),i.container.animate({left:o},100),i.remove()}else i.elem.animate({opacity:i.options.opacity},100),i.container.animate({left:0},100);e=null,n=null,s=null}},touchcancel:function(){e&&t.swipe_dismiss&&(i.elem.animate({opacity:i.options.opacity},100),i.container.animate({left:0},100),e=null,n=null,s=null)}})},update:function(i,o){this.swipe_dismiss=o.swipe_dismiss,this.doMobileStyling(i,o)},doMobileStyling:function(o,t){t.styling?(o.elem.addClass("ui-pnotify-mobile-able"),i(window).width()<=480?(o.options.stack.mobileOrigSpacing1||(o.options.stack.mobileOrigSpacing1=o.options.stack.spacing1,o.options.stack.mobileOrigSpacing2=o.options.stack.spacing2),o.options.stack.spacing1=0,o.options.stack.spacing2=0):(o.options.stack.mobileOrigSpacing1||o.options.stack.mobileOrigSpacing2)&&(o.options.stack.spacing1=o.options.stack.mobileOrigSpacing1,delete o.options.stack.mobileOrigSpacing1,o.options.stack.spacing2=o.options.stack.mobileOrigSpacing2,delete o.options.stack.mobileOrigSpacing2)):(o.elem.removeClass("ui-pnotify-mobile-able"),o.options.stack.mobileOrigSpacing1&&(o.options.stack.spacing1=o.options.stack.mobileOrigSpacing1,delete o.options.stack.mobileOrigSpacing1),o.options.stack.mobileOrigSpacing2&&(o.options.stack.spacing2=o.options.stack.mobileOrigSpacing2,delete o.options.stack.mobileOrigSpacing2))}},o}); \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Content/vendors/rfs/_rfs.scss b/src/Presentation/SmartStore.Web/Content/vendors/rfs/_rfs.scss new file mode 100644 index 0000000000..e9d402195c --- /dev/null +++ b/src/Presentation/SmartStore.Web/Content/vendors/rfs/_rfs.scss @@ -0,0 +1,305 @@ +// stylelint-disable property-blacklist, scss/dollar-variable-default + +// SCSS RFS mixin +// +// Automated responsive values for font sizes, paddings, margins and much more +// +// Licensed under MIT (https://github.com/twbs/rfs/blob/master/LICENSE) + +// Configuration + +// Base value +$rfs-base-value: 1.25rem !default; +$rfs-unit: rem !default; + +@if $rfs-unit != rem and $rfs-unit != px { + @error "`#{$rfs-unit}` is not a valid unit for $rfs-unit. Use `px` or `rem`."; +} + +// Breakpoint at where values start decreasing if screen width is smaller +$rfs-breakpoint: 1200px !default; +$rfs-breakpoint-unit: px !default; + +@if $rfs-breakpoint-unit != px and $rfs-breakpoint-unit != em and $rfs-breakpoint-unit != rem { + @error "`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`."; +} + +// Resize values based on screen height and width +$rfs-two-dimensional: false !default; + +// Factor of decrease +$rfs-factor: 10 !default; + +@if type-of($rfs-factor) != number or $rfs-factor <= 1 { + @error "`#{$rfs-factor}` is not a valid $rfs-factor, it must be greater than 1."; +} + +// Mode. Possibilities: "min-media-query", "max-media-query" +$rfs-mode: min-media-query !default; + +// Generate enable or disable classes. Possibilities: false, "enable" or "disable" +$rfs-class: false !default; + +// 1 rem = $rfs-rem-value px +$rfs-rem-value: 16 !default; + +// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14 +$rfs-safari-iframe-resize-bug-fix: false !default; + +// Disable RFS by setting $enable-rfs to false +$enable-rfs: true !default; + +// Cache $rfs-base-value unit +$rfs-base-value-unit: unit($rfs-base-value); + +// Remove px-unit from $rfs-base-value for calculations +@if $rfs-base-value-unit == px { + $rfs-base-value: $rfs-base-value / ($rfs-base-value * 0 + 1); +} +@else if $rfs-base-value-unit == rem { + $rfs-base-value: $rfs-base-value / ($rfs-base-value * 0 + 1 / $rfs-rem-value); +} + +// Cache $rfs-breakpoint unit to prevent multiple calls +$rfs-breakpoint-unit-cache: unit($rfs-breakpoint); + +// Remove unit from $rfs-breakpoint for calculations +@if $rfs-breakpoint-unit-cache == px { + $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1); +} +@else if $rfs-breakpoint-unit-cache == rem or $rfs-breakpoint-unit-cache == "em" { + $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1 / $rfs-rem-value); +} + +// Calculate the media query value +$rfs-mq-value: if($rfs-breakpoint-unit == px, #{$rfs-breakpoint}px, #{$rfs-breakpoint / $rfs-rem-value}#{$rfs-breakpoint-unit}); +$rfs-mq-property-width: if($rfs-mode == max-media-query, max-width, min-width); +$rfs-mq-property-height: if($rfs-mode == max-media-query, max-height, min-height); + +// Internal mixin used to determine which media query needs to be used +@mixin _rfs-media-query { + @if $rfs-two-dimensional { + @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}), (#{$rfs-mq-property-height}: #{$rfs-mq-value}) { + @content; + } + } + @else { + @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) { + @content; + } + } +} + +// Internal mixin that adds disable classes to the selector if needed. +@mixin _rfs-rule { + @if $rfs-class == disable and $rfs-mode == max-media-query { + // Adding an extra class increases specificity, which prevents the media query to override the property + &, + .disable-rfs &, + &.disable-rfs { + @content; + } + } + @else if $rfs-class == enable and $rfs-mode == min-media-query { + .enable-rfs &, + &.enable-rfs { + @content; + } + } + @else { + @content; + } +} + +// Internal mixin that adds enable classes to the selector if needed. +@mixin _rfs-media-query-rule { + + @if $rfs-class == enable { + @if $rfs-mode == min-media-query { + @content; + } + + @include _rfs-media-query { + .enable-rfs &, + &.enable-rfs { + @content; + } + } + } + @else { + @if $rfs-class == disable and $rfs-mode == min-media-query { + .disable-rfs &, + &.disable-rfs { + @content; + } + } + @include _rfs-media-query { + @content; + } + } +} + +// Helper function to get the formatted non-responsive value +@function rfs-value($values) { + // Convert to list + $values: if(type-of($values) != list, ($values,), $values); + + $val: ''; + + // Loop over each value and calculate value + @each $value in $values { + @if $value == 0 { + $val: $val + ' 0'; + } + @else { + // Cache $value unit + $unit: if(type-of($value) == "number", unit($value), false); + + @if $unit == px { + // Convert to rem if needed + $val: $val + ' ' + if($rfs-unit == rem, #{$value / ($value * 0 + $rfs-rem-value)}rem, $value); + } + @else if $unit == rem { + // Convert to px if needed + $val: $val + ' ' + if($rfs-unit == px, #{$value / ($value * 0 + 1) * $rfs-rem-value}px, $value); + } + @else { + // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value + $val: $val + ' ' + $value; + } + } + } + + // Remove first space + @return unquote(str-slice($val, 2)); +} + +// Helper function to get the responsive value calculated by RFS +@function rfs-fluid-value($values) { + // Convert to list + $values: if(type-of($values) != list, ($values,), $values); + + $val: ''; + + // Loop over each value and calculate value + @each $value in $values { + @if $value == 0 { + $val: $val + ' 0'; + } + + @else { + // Cache $value unit + $unit: if(type-of($value) == "number", unit($value), false); + + // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value + @if not $unit or $unit != px and $unit != rem { + $val: $val + ' ' + $value; + } + + @else { + // Remove unit from $value for calculations + $value: $value / ($value * 0 + if($unit == px, 1, 1 / $rfs-rem-value)); + + // Only add the media query if the value is greater than the minimum value + @if abs($value) <= $rfs-base-value or not $enable-rfs { + $val: $val + ' ' + if($rfs-unit == rem, #{$value / $rfs-rem-value}rem, #{$value}px); + } + @else { + // Calculate the minimum value + $value-min: $rfs-base-value + (abs($value) - $rfs-base-value) / $rfs-factor; + + // Calculate difference between $value and the minimum value + $value-diff: abs($value) - $value-min; + + // Base value formatting + $min-width: if($rfs-unit == rem, #{$value-min / $rfs-rem-value}rem, #{$value-min}px); + + // Use negative value if needed + $min-width: if($value < 0, -$min-width, $min-width); + + // Use `vmin` if two-dimensional is enabled + $variable-unit: if($rfs-two-dimensional, vmin, vw); + + // Calculate the variable width between 0 and $rfs-breakpoint + $variable-width: #{$value-diff * 100 / $rfs-breakpoint}#{$variable-unit}; + + // Return the calculated value + $val: $val + ' calc(' + $min-width + if($value < 0, ' - ', ' + ') + $variable-width + ')'; + } + } + } + } + + // Remove first space + @return unquote(str-slice($val, 2)); +} + +// RFS mixin +@mixin rfs($values, $property: font-size) { + @if $values != null { + $val: rfs-value($values); + $fluidVal: rfs-fluid-value($values); + + // Do not print the media query if responsive & non-responsive values are the same + @if $val == $fluidVal { + #{$property}: $val; + } + @else { + @include _rfs-rule { + #{$property}: if($rfs-mode == max-media-query, $val, $fluidVal); + + // Include safari iframe resize fix if needed + min-width: if($rfs-safari-iframe-resize-bug-fix, (0 * 1vw), null); + } + + @include _rfs-media-query-rule { + #{$property}: if($rfs-mode == max-media-query, $fluidVal, $val); + } + } + } +} + +// Shorthand helper mixins +@mixin font-size($value) { + @include rfs($value); +} + +@mixin padding($value) { + @include rfs($value, padding); +} + +@mixin padding-top($value) { + @include rfs($value, padding-top); +} + +@mixin padding-right($value) { + @include rfs($value, padding-right); +} + +@mixin padding-bottom($value) { + @include rfs($value, padding-bottom); +} + +@mixin padding-left($value) { + @include rfs($value, padding-left); +} + +@mixin margin($value) { + @include rfs($value, margin); +} + +@mixin margin-top($value) { + @include rfs($value, margin-top); +} + +@mixin margin-right($value) { + @include rfs($value, margin-right); +} + +@mixin margin-bottom($value) { + @include rfs($value, margin-bottom); +} + +@mixin margin-left($value) { + @include rfs($value, margin-left); +} \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Content/vendors/sortable/jquery-sortable.js b/src/Presentation/SmartStore.Web/Content/vendors/sortable/jquery-sortable.js new file mode 100644 index 0000000000..051144987f --- /dev/null +++ b/src/Presentation/SmartStore.Web/Content/vendors/sortable/jquery-sortable.js @@ -0,0 +1,76 @@ +(function (factory) { + "use strict"; + var sortable, + jq, + _this = this + ; + + if (typeof define === "function" && define.amd) { + try { + define(["sortablejs", "jquery"], function (Sortable, $) { + sortable = Sortable; + jq = $; + checkErrors(); + factory(Sortable, $); + }); + } catch (err) { + checkErrors(); + } + return; + } else if (typeof exports === 'object') { + try { + sortable = require('sortablejs'); + jq = require('jquery'); + } catch (err) { } + } + + if (typeof jQuery === 'function' || typeof $ === 'function') { + jq = jQuery || $; + } + + if (typeof Sortable !== 'undefined') { + sortable = Sortable; + } + + function checkErrors() { + if (!jq) { + throw new Error('jQuery is required for jquery-sortablejs'); + } + + if (!sortable) { + throw new Error('SortableJS is required for jquery-sortablejs (https://github.com/SortableJS/Sortable)'); + } + } + checkErrors(); + factory(sortable, jq); +})(function (Sortable, $) { + "use strict"; + + $.fn.sortable = function (options) { + var retVal, + args = arguments; + + this.each(function () { + var $el = $(this), + sortable = $el.data('sortable'); + + if (!sortable && (options instanceof Object || !options)) { + sortable = new Sortable(this, options); + $el.data('sortable', sortable); + } else if (sortable) { + if (options === 'destroy') { + sortable.destroy(); + $el.removeData('sortable'); + } else if (options === 'widget') { + retVal = sortable; + } else if (typeof sortable[options] === 'function') { + retVal = sortable[options].apply(sortable, [].slice.call(args, 1)); + } else if (options in sortable.options) { + retVal = sortable.option.apply(sortable, args); + } + } + }); + + return (retVal === void 0) ? this : retVal; + }; +}); \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Content/vendors/sortable/sortable.js b/src/Presentation/SmartStore.Web/Content/vendors/sortable/sortable.js new file mode 100644 index 0000000000..ffdb3f6667 --- /dev/null +++ b/src/Presentation/SmartStore.Web/Content/vendors/sortable/sortable.js @@ -0,0 +1,3710 @@ +/**! + * Sortable 1.10.2 + * @author RubaXa + * @author owenm + * @license MIT + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.Sortable = factory()); +}(this, function () { + 'use strict'; + + function _typeof(obj) { + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function (obj) { + return typeof obj; + }; + } else { + _typeof = function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); + } + + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + function _extends() { + _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + return _extends.apply(this, arguments); + } + + function _objectSpread(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + var ownKeys = Object.keys(source); + + if (typeof Object.getOwnPropertySymbols === 'function') { + ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { + return Object.getOwnPropertyDescriptor(source, sym).enumerable; + })); + } + + ownKeys.forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } + + return target; + } + + function _objectWithoutPropertiesLoose(source, excluded) { + if (source == null) return {}; + var target = {}; + var sourceKeys = Object.keys(source); + var key, i; + + for (i = 0; i < sourceKeys.length; i++) { + key = sourceKeys[i]; + if (excluded.indexOf(key) >= 0) continue; + target[key] = source[key]; + } + + return target; + } + + function _objectWithoutProperties(source, excluded) { + if (source == null) return {}; + + var target = _objectWithoutPropertiesLoose(source, excluded); + + var key, i; + + if (Object.getOwnPropertySymbols) { + var sourceSymbolKeys = Object.getOwnPropertySymbols(source); + + for (i = 0; i < sourceSymbolKeys.length; i++) { + key = sourceSymbolKeys[i]; + if (excluded.indexOf(key) >= 0) continue; + if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; + target[key] = source[key]; + } + } + + return target; + } + + function _toConsumableArray(arr) { + return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); + } + + function _arrayWithoutHoles(arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } + } + + function _iterableToArray(iter) { + if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); + } + + function _nonIterableSpread() { + throw new TypeError("Invalid attempt to spread non-iterable instance"); + } + + var version = "1.10.2"; + + function userAgent(pattern) { + if (typeof window !== 'undefined' && window.navigator) { + return !! + /*@__PURE__*/ + navigator.userAgent.match(pattern); + } + } + + var IE11OrLess = userAgent(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i); + var Edge = userAgent(/Edge/i); + var FireFox = userAgent(/firefox/i); + var Safari = userAgent(/safari/i) && !userAgent(/chrome/i) && !userAgent(/android/i); + var IOS = userAgent(/iP(ad|od|hone)/i); + var ChromeForAndroid = userAgent(/chrome/i) && userAgent(/android/i); + + var captureMode = { + capture: false, + passive: false + }; + + function on(el, event, fn) { + el.addEventListener(event, fn, !IE11OrLess && captureMode); + } + + function off(el, event, fn) { + el.removeEventListener(event, fn, !IE11OrLess && captureMode); + } + + function matches( + /**HTMLElement*/ + el, + /**String*/ + selector) { + if (!selector) return; + selector[0] === '>' && (selector = selector.substring(1)); + + if (el) { + try { + if (el.matches) { + return el.matches(selector); + } else if (el.msMatchesSelector) { + return el.msMatchesSelector(selector); + } else if (el.webkitMatchesSelector) { + return el.webkitMatchesSelector(selector); + } + } catch (_) { + return false; + } + } + + return false; + } + + function getParentOrHost(el) { + return el.host && el !== document && el.host.nodeType ? el.host : el.parentNode; + } + + function closest( + /**HTMLElement*/ + el, + /**String*/ + selector, + /**HTMLElement*/ + ctx, includeCTX) { + if (el) { + ctx = ctx || document; + + do { + if (selector != null && (selector[0] === '>' ? el.parentNode === ctx && matches(el, selector) : matches(el, selector)) || includeCTX && el === ctx) { + return el; + } + + if (el === ctx) break; + /* jshint boss:true */ + } while (el = getParentOrHost(el)); + } + + return null; + } + + var R_SPACE = /\s+/g; + + function toggleClass(el, name, state) { + if (el && name) { + if (el.classList) { + el.classList[state ? 'add' : 'remove'](name); + } else { + var className = (' ' + el.className + ' ').replace(R_SPACE, ' ').replace(' ' + name + ' ', ' '); + el.className = (className + (state ? ' ' + name : '')).replace(R_SPACE, ' '); + } + } + } + + function css(el, prop, val) { + var style = el && el.style; + + if (style) { + if (val === void 0) { + if (document.defaultView && document.defaultView.getComputedStyle) { + val = document.defaultView.getComputedStyle(el, ''); + } else if (el.currentStyle) { + val = el.currentStyle; + } + + return prop === void 0 ? val : val[prop]; + } else { + if (!(prop in style) && prop.indexOf('webkit') === -1) { + prop = '-webkit-' + prop; + } + + style[prop] = val + (typeof val === 'string' ? '' : 'px'); + } + } + } + + function matrix(el, selfOnly) { + var appliedTransforms = ''; + + if (typeof el === 'string') { + appliedTransforms = el; + } else { + do { + var transform = css(el, 'transform'); + + if (transform && transform !== 'none') { + appliedTransforms = transform + ' ' + appliedTransforms; + } + /* jshint boss:true */ + + } while (!selfOnly && (el = el.parentNode)); + } + + var matrixFn = window.DOMMatrix || window.WebKitCSSMatrix || window.CSSMatrix || window.MSCSSMatrix; + /*jshint -W056 */ + + return matrixFn && new matrixFn(appliedTransforms); + } + + function find(ctx, tagName, iterator) { + if (ctx) { + var list = ctx.getElementsByTagName(tagName), + i = 0, + n = list.length; + + if (iterator) { + for (; i < n; i++) { + iterator(list[i], i); + } + } + + return list; + } + + return []; + } + + function getWindowScrollingElement() { + var scrollingElement = document.scrollingElement; + + if (scrollingElement) { + return scrollingElement; + } else { + return document.documentElement; + } + } + /** + * Returns the "bounding client rect" of given element + * @param {HTMLElement} el The element whose boundingClientRect is wanted + * @param {[Boolean]} relativeToContainingBlock Whether the rect should be relative to the containing block of (including) the container + * @param {[Boolean]} relativeToNonStaticParent Whether the rect should be relative to the relative parent of (including) the contaienr + * @param {[Boolean]} undoScale Whether the container's scale() should be undone + * @param {[HTMLElement]} container The parent the element will be placed in + * @return {Object} The boundingClientRect of el, with specified adjustments + */ + + + function getRect(el, relativeToContainingBlock, relativeToNonStaticParent, undoScale, container) { + if (!el.getBoundingClientRect && el !== window) return; + var elRect, top, left, bottom, right, height, width; + + if (el !== window && el !== getWindowScrollingElement()) { + elRect = el.getBoundingClientRect(); + top = elRect.top; + left = elRect.left; + bottom = elRect.bottom; + right = elRect.right; + height = elRect.height; + width = elRect.width; + } else { + top = 0; + left = 0; + bottom = window.innerHeight; + right = window.innerWidth; + height = window.innerHeight; + width = window.innerWidth; + } + + if ((relativeToContainingBlock || relativeToNonStaticParent) && el !== window) { + // Adjust for translate() + container = container || el.parentNode; // solves #1123 (see: https://stackoverflow.com/a/37953806/6088312) + // Not needed on <= IE11 + + if (!IE11OrLess) { + do { + if (container && container.getBoundingClientRect && (css(container, 'transform') !== 'none' || relativeToNonStaticParent && css(container, 'position') !== 'static')) { + var containerRect = container.getBoundingClientRect(); // Set relative to edges of padding box of container + + top -= containerRect.top + parseInt(css(container, 'border-top-width')); + left -= containerRect.left + parseInt(css(container, 'border-left-width')); + bottom = top + elRect.height; + right = left + elRect.width; + break; + } + /* jshint boss:true */ + + } while (container = container.parentNode); + } + } + + if (undoScale && el !== window) { + // Adjust for scale() + var elMatrix = matrix(container || el), + scaleX = elMatrix && elMatrix.a, + scaleY = elMatrix && elMatrix.d; + + if (elMatrix) { + top /= scaleY; + left /= scaleX; + width /= scaleX; + height /= scaleY; + bottom = top + height; + right = left + width; + } + } + + return { + top: top, + left: left, + bottom: bottom, + right: right, + width: width, + height: height + }; + } + /** + * Checks if a side of an element is scrolled past a side of its parents + * @param {HTMLElement} el The element who's side being scrolled out of view is in question + * @param {String} elSide Side of the element in question ('top', 'left', 'right', 'bottom') + * @param {String} parentSide Side of the parent in question ('top', 'left', 'right', 'bottom') + * @return {HTMLElement} The parent scroll element that the el's side is scrolled past, or null if there is no such element + */ + + + function isScrolledPast(el, elSide, parentSide) { + var parent = getParentAutoScrollElement(el, true), + elSideVal = getRect(el)[elSide]; + /* jshint boss:true */ + + while (parent) { + var parentSideVal = getRect(parent)[parentSide], + visible = void 0; + + if (parentSide === 'top' || parentSide === 'left') { + visible = elSideVal >= parentSideVal; + } else { + visible = elSideVal <= parentSideVal; + } + + if (!visible) return parent; + if (parent === getWindowScrollingElement()) break; + parent = getParentAutoScrollElement(parent, false); + } + + return false; + } + /** + * Gets nth child of el, ignoring hidden children, sortable's elements (does not ignore clone if it's visible) + * and non-draggable elements + * @param {HTMLElement} el The parent element + * @param {Number} childNum The index of the child + * @param {Object} options Parent Sortable's options + * @return {HTMLElement} The child at index childNum, or null if not found + */ + + + function getChild(el, childNum, options) { + var currentChild = 0, + i = 0, + children = el.children; + + while (i < children.length) { + if (children[i].style.display !== 'none' && children[i] !== Sortable.ghost && children[i] !== Sortable.dragged && closest(children[i], options.draggable, el, false)) { + if (currentChild === childNum) { + return children[i]; + } + + currentChild++; + } + + i++; + } + + return null; + } + /** + * Gets the last child in the el, ignoring ghostEl or invisible elements (clones) + * @param {HTMLElement} el Parent element + * @param {selector} selector Any other elements that should be ignored + * @return {HTMLElement} The last child, ignoring ghostEl + */ + + + function lastChild(el, selector) { + var last = el.lastElementChild; + + while (last && (last === Sortable.ghost || css(last, 'display') === 'none' || selector && !matches(last, selector))) { + last = last.previousElementSibling; + } + + return last || null; + } + /** + * Returns the index of an element within its parent for a selected set of + * elements + * @param {HTMLElement} el + * @param {selector} selector + * @return {number} + */ + + + function index(el, selector) { + var index = 0; + + if (!el || !el.parentNode) { + return -1; + } + /* jshint boss:true */ + + + while (el = el.previousElementSibling) { + if (el.nodeName.toUpperCase() !== 'TEMPLATE' && el !== Sortable.clone && (!selector || matches(el, selector))) { + index++; + } + } + + return index; + } + /** + * Returns the scroll offset of the given element, added with all the scroll offsets of parent elements. + * The value is returned in real pixels. + * @param {HTMLElement} el + * @return {Array} Offsets in the format of [left, top] + */ + + + function getRelativeScrollOffset(el) { + var offsetLeft = 0, + offsetTop = 0, + winScroller = getWindowScrollingElement(); + + if (el) { + do { + var elMatrix = matrix(el), + scaleX = elMatrix.a, + scaleY = elMatrix.d; + offsetLeft += el.scrollLeft * scaleX; + offsetTop += el.scrollTop * scaleY; + } while (el !== winScroller && (el = el.parentNode)); + } + + return [offsetLeft, offsetTop]; + } + /** + * Returns the index of the object within the given array + * @param {Array} arr Array that may or may not hold the object + * @param {Object} obj An object that has a key-value pair unique to and identical to a key-value pair in the object you want to find + * @return {Number} The index of the object in the array, or -1 + */ + + + function indexOfObject(arr, obj) { + for (var i in arr) { + if (!arr.hasOwnProperty(i)) continue; + + for (var key in obj) { + if (obj.hasOwnProperty(key) && obj[key] === arr[i][key]) return Number(i); + } + } + + return -1; + } + + function getParentAutoScrollElement(el, includeSelf) { + // skip to window + if (!el || !el.getBoundingClientRect) return getWindowScrollingElement(); + var elem = el; + var gotSelf = false; + + do { + // we don't need to get elem css if it isn't even overflowing in the first place (performance) + if (elem.clientWidth < elem.scrollWidth || elem.clientHeight < elem.scrollHeight) { + var elemCSS = css(elem); + + if (elem.clientWidth < elem.scrollWidth && (elemCSS.overflowX == 'auto' || elemCSS.overflowX == 'scroll') || elem.clientHeight < elem.scrollHeight && (elemCSS.overflowY == 'auto' || elemCSS.overflowY == 'scroll')) { + if (!elem.getBoundingClientRect || elem === document.body) return getWindowScrollingElement(); + if (gotSelf || includeSelf) return elem; + gotSelf = true; + } + } + /* jshint boss:true */ + + } while (elem = elem.parentNode); + + return getWindowScrollingElement(); + } + + function extend(dst, src) { + if (dst && src) { + for (var key in src) { + if (src.hasOwnProperty(key)) { + dst[key] = src[key]; + } + } + } + + return dst; + } + + function isRectEqual(rect1, rect2) { + return Math.round(rect1.top) === Math.round(rect2.top) && Math.round(rect1.left) === Math.round(rect2.left) && Math.round(rect1.height) === Math.round(rect2.height) && Math.round(rect1.width) === Math.round(rect2.width); + } + + var _throttleTimeout; + + function throttle(callback, ms) { + return function () { + if (!_throttleTimeout) { + var args = arguments, + _this = this; + + if (args.length === 1) { + callback.call(_this, args[0]); + } else { + callback.apply(_this, args); + } + + _throttleTimeout = setTimeout(function () { + _throttleTimeout = void 0; + }, ms); + } + }; + } + + function cancelThrottle() { + clearTimeout(_throttleTimeout); + _throttleTimeout = void 0; + } + + function scrollBy(el, x, y) { + el.scrollLeft += x; + el.scrollTop += y; + } + + function clone(el) { + var Polymer = window.Polymer; + var $ = window.jQuery || window.Zepto; + + if (Polymer && Polymer.dom) { + return Polymer.dom(el).cloneNode(true); + } else if ($) { + return $(el).clone(true)[0]; + } else { + return el.cloneNode(true); + } + } + + function setRect(el, rect) { + css(el, 'position', 'absolute'); + css(el, 'top', rect.top); + css(el, 'left', rect.left); + css(el, 'width', rect.width); + css(el, 'height', rect.height); + } + + function unsetRect(el) { + css(el, 'position', ''); + css(el, 'top', ''); + css(el, 'left', ''); + css(el, 'width', ''); + css(el, 'height', ''); + } + + var expando = 'Sortable' + new Date().getTime(); + + function AnimationStateManager() { + var animationStates = [], + animationCallbackId; + return { + captureAnimationState: function captureAnimationState() { + animationStates = []; + if (!this.options.animation) return; + var children = [].slice.call(this.el.children); + children.forEach(function (child) { + if (css(child, 'display') === 'none' || child === Sortable.ghost) return; + animationStates.push({ + target: child, + rect: getRect(child) + }); + + var fromRect = _objectSpread({}, animationStates[animationStates.length - 1].rect); // If animating: compensate for current animation + + + if (child.thisAnimationDuration) { + var childMatrix = matrix(child, true); + + if (childMatrix) { + fromRect.top -= childMatrix.f; + fromRect.left -= childMatrix.e; + } + } + + child.fromRect = fromRect; + }); + }, + addAnimationState: function addAnimationState(state) { + animationStates.push(state); + }, + removeAnimationState: function removeAnimationState(target) { + animationStates.splice(indexOfObject(animationStates, { + target: target + }), 1); + }, + animateAll: function animateAll(callback) { + var _this = this; + + if (!this.options.animation) { + clearTimeout(animationCallbackId); + if (typeof callback === 'function') callback(); + return; + } + + var animating = false, + animationTime = 0; + animationStates.forEach(function (state) { + var time = 0, + target = state.target, + fromRect = target.fromRect, + toRect = getRect(target), + prevFromRect = target.prevFromRect, + prevToRect = target.prevToRect, + animatingRect = state.rect, + targetMatrix = matrix(target, true); + + if (targetMatrix) { + // Compensate for current animation + toRect.top -= targetMatrix.f; + toRect.left -= targetMatrix.e; + } + + target.toRect = toRect; + + if (target.thisAnimationDuration) { + // Could also check if animatingRect is between fromRect and toRect + if (isRectEqual(prevFromRect, toRect) && !isRectEqual(fromRect, toRect) && // Make sure animatingRect is on line between toRect & fromRect + (animatingRect.top - toRect.top) / (animatingRect.left - toRect.left) === (fromRect.top - toRect.top) / (fromRect.left - toRect.left)) { + // If returning to same place as started from animation and on same axis + time = calculateRealTime(animatingRect, prevFromRect, prevToRect, _this.options); + } + } // if fromRect != toRect: animate + + + if (!isRectEqual(toRect, fromRect)) { + target.prevFromRect = fromRect; + target.prevToRect = toRect; + + if (!time) { + time = _this.options.animation; + } + + _this.animate(target, animatingRect, toRect, time); + } + + if (time) { + animating = true; + animationTime = Math.max(animationTime, time); + clearTimeout(target.animationResetTimer); + target.animationResetTimer = setTimeout(function () { + target.animationTime = 0; + target.prevFromRect = null; + target.fromRect = null; + target.prevToRect = null; + target.thisAnimationDuration = null; + }, time); + target.thisAnimationDuration = time; + } + }); + clearTimeout(animationCallbackId); + + if (!animating) { + if (typeof callback === 'function') callback(); + } else { + animationCallbackId = setTimeout(function () { + if (typeof callback === 'function') callback(); + }, animationTime); + } + + animationStates = []; + }, + animate: function animate(target, currentRect, toRect, duration) { + if (duration) { + css(target, 'transition', ''); + css(target, 'transform', ''); + var elMatrix = matrix(this.el), + scaleX = elMatrix && elMatrix.a, + scaleY = elMatrix && elMatrix.d, + translateX = (currentRect.left - toRect.left) / (scaleX || 1), + translateY = (currentRect.top - toRect.top) / (scaleY || 1); + target.animatingX = !!translateX; + target.animatingY = !!translateY; + css(target, 'transform', 'translate3d(' + translateX + 'px,' + translateY + 'px,0)'); + repaint(target); // repaint + + css(target, 'transition', 'transform ' + duration + 'ms' + (this.options.easing ? ' ' + this.options.easing : '')); + css(target, 'transform', 'translate3d(0,0,0)'); + typeof target.animated === 'number' && clearTimeout(target.animated); + target.animated = setTimeout(function () { + css(target, 'transition', ''); + css(target, 'transform', ''); + target.animated = false; + target.animatingX = false; + target.animatingY = false; + }, duration); + } + } + }; + } + + function repaint(target) { + return target.offsetWidth; + } + + function calculateRealTime(animatingRect, fromRect, toRect, options) { + return Math.sqrt(Math.pow(fromRect.top - animatingRect.top, 2) + Math.pow(fromRect.left - animatingRect.left, 2)) / Math.sqrt(Math.pow(fromRect.top - toRect.top, 2) + Math.pow(fromRect.left - toRect.left, 2)) * options.animation; + } + + var plugins = []; + var defaults = { + initializeByDefault: true + }; + var PluginManager = { + mount: function mount(plugin) { + // Set default static properties + for (var option in defaults) { + if (defaults.hasOwnProperty(option) && !(option in plugin)) { + plugin[option] = defaults[option]; + } + } + + plugins.push(plugin); + }, + pluginEvent: function pluginEvent(eventName, sortable, evt) { + var _this = this; + + this.eventCanceled = false; + + evt.cancel = function () { + _this.eventCanceled = true; + }; + + var eventNameGlobal = eventName + 'Global'; + plugins.forEach(function (plugin) { + if (!sortable[plugin.pluginName]) return; // Fire global events if it exists in this sortable + + if (sortable[plugin.pluginName][eventNameGlobal]) { + sortable[plugin.pluginName][eventNameGlobal](_objectSpread({ + sortable: sortable + }, evt)); + } // Only fire plugin event if plugin is enabled in this sortable, + // and plugin has event defined + + + if (sortable.options[plugin.pluginName] && sortable[plugin.pluginName][eventName]) { + sortable[plugin.pluginName][eventName](_objectSpread({ + sortable: sortable + }, evt)); + } + }); + }, + initializePlugins: function initializePlugins(sortable, el, defaults, options) { + plugins.forEach(function (plugin) { + var pluginName = plugin.pluginName; + if (!sortable.options[pluginName] && !plugin.initializeByDefault) return; + var initialized = new plugin(sortable, el, sortable.options); + initialized.sortable = sortable; + initialized.options = sortable.options; + sortable[pluginName] = initialized; // Add default options from plugin + + _extends(defaults, initialized.defaults); + }); + + for (var option in sortable.options) { + if (!sortable.options.hasOwnProperty(option)) continue; + var modified = this.modifyOption(sortable, option, sortable.options[option]); + + if (typeof modified !== 'undefined') { + sortable.options[option] = modified; + } + } + }, + getEventProperties: function getEventProperties(name, sortable) { + var eventProperties = {}; + plugins.forEach(function (plugin) { + if (typeof plugin.eventProperties !== 'function') return; + + _extends(eventProperties, plugin.eventProperties.call(sortable[plugin.pluginName], name)); + }); + return eventProperties; + }, + modifyOption: function modifyOption(sortable, name, value) { + var modifiedValue; + plugins.forEach(function (plugin) { + // Plugin must exist on the Sortable + if (!sortable[plugin.pluginName]) return; // If static option listener exists for this option, call in the context of the Sortable's instance of this plugin + + if (plugin.optionListeners && typeof plugin.optionListeners[name] === 'function') { + modifiedValue = plugin.optionListeners[name].call(sortable[plugin.pluginName], value); + } + }); + return modifiedValue; + } + }; + + function dispatchEvent(_ref) { + var sortable = _ref.sortable, + rootEl = _ref.rootEl, + name = _ref.name, + targetEl = _ref.targetEl, + cloneEl = _ref.cloneEl, + toEl = _ref.toEl, + fromEl = _ref.fromEl, + oldIndex = _ref.oldIndex, + newIndex = _ref.newIndex, + oldDraggableIndex = _ref.oldDraggableIndex, + newDraggableIndex = _ref.newDraggableIndex, + originalEvent = _ref.originalEvent, + putSortable = _ref.putSortable, + extraEventProperties = _ref.extraEventProperties; + sortable = sortable || rootEl && rootEl[expando]; + if (!sortable) return; + var evt, + options = sortable.options, + onName = 'on' + name.charAt(0).toUpperCase() + name.substr(1); // Support for new CustomEvent feature + + if (window.CustomEvent && !IE11OrLess && !Edge) { + evt = new CustomEvent(name, { + bubbles: true, + cancelable: true + }); + } else { + evt = document.createEvent('Event'); + evt.initEvent(name, true, true); + } + + evt.to = toEl || rootEl; + evt.from = fromEl || rootEl; + evt.item = targetEl || rootEl; + evt.clone = cloneEl; + evt.oldIndex = oldIndex; + evt.newIndex = newIndex; + evt.oldDraggableIndex = oldDraggableIndex; + evt.newDraggableIndex = newDraggableIndex; + evt.originalEvent = originalEvent; + evt.pullMode = putSortable ? putSortable.lastPutMode : undefined; + + var allEventProperties = _objectSpread({}, extraEventProperties, PluginManager.getEventProperties(name, sortable)); + + for (var option in allEventProperties) { + evt[option] = allEventProperties[option]; + } + + if (rootEl) { + rootEl.dispatchEvent(evt); + } + + if (options[onName]) { + options[onName].call(sortable, evt); + } + } + + var pluginEvent = function pluginEvent(eventName, sortable) { + var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, + originalEvent = _ref.evt, + data = _objectWithoutProperties(_ref, ["evt"]); + + PluginManager.pluginEvent.bind(Sortable)(eventName, sortable, _objectSpread({ + dragEl: dragEl, + parentEl: parentEl, + ghostEl: ghostEl, + rootEl: rootEl, + nextEl: nextEl, + lastDownEl: lastDownEl, + cloneEl: cloneEl, + cloneHidden: cloneHidden, + dragStarted: moved, + putSortable: putSortable, + activeSortable: Sortable.active, + originalEvent: originalEvent, + oldIndex: oldIndex, + oldDraggableIndex: oldDraggableIndex, + newIndex: newIndex, + newDraggableIndex: newDraggableIndex, + hideGhostForTarget: _hideGhostForTarget, + unhideGhostForTarget: _unhideGhostForTarget, + cloneNowHidden: function cloneNowHidden() { + cloneHidden = true; + }, + cloneNowShown: function cloneNowShown() { + cloneHidden = false; + }, + dispatchSortableEvent: function dispatchSortableEvent(name) { + _dispatchEvent({ + sortable: sortable, + name: name, + originalEvent: originalEvent + }); + } + }, data)); + }; + + function _dispatchEvent(info) { + dispatchEvent(_objectSpread({ + putSortable: putSortable, + cloneEl: cloneEl, + targetEl: dragEl, + rootEl: rootEl, + oldIndex: oldIndex, + oldDraggableIndex: oldDraggableIndex, + newIndex: newIndex, + newDraggableIndex: newDraggableIndex + }, info)); + } + + var dragEl, + parentEl, + ghostEl, + rootEl, + nextEl, + lastDownEl, + cloneEl, + cloneHidden, + oldIndex, + newIndex, + oldDraggableIndex, + newDraggableIndex, + activeGroup, + putSortable, + awaitingDragStarted = false, + ignoreNextClick = false, + sortables = [], + tapEvt, + touchEvt, + lastDx, + lastDy, + tapDistanceLeft, + tapDistanceTop, + moved, + lastTarget, + lastDirection, + pastFirstInvertThresh = false, + isCircumstantialInvert = false, + targetMoveDistance, + // For positioning ghost absolutely + ghostRelativeParent, + ghostRelativeParentInitialScroll = [], + // (left, top) + _silent = false, + savedInputChecked = []; + /** @const */ + + var documentExists = typeof document !== 'undefined', + PositionGhostAbsolutely = IOS, + CSSFloatProperty = Edge || IE11OrLess ? 'cssFloat' : 'float', + // This will not pass for IE9, because IE9 DnD only works on anchors + supportDraggable = documentExists && !ChromeForAndroid && !IOS && 'draggable' in document.createElement('div'), + supportCssPointerEvents = function () { + if (!documentExists) return; // false when <= IE11 + + if (IE11OrLess) { + return false; + } + + var el = document.createElement('x'); + el.style.cssText = 'pointer-events:auto'; + return el.style.pointerEvents === 'auto'; + }(), + _detectDirection = function _detectDirection(el, options) { + var elCSS = css(el), + elWidth = parseInt(elCSS.width) - parseInt(elCSS.paddingLeft) - parseInt(elCSS.paddingRight) - parseInt(elCSS.borderLeftWidth) - parseInt(elCSS.borderRightWidth), + child1 = getChild(el, 0, options), + child2 = getChild(el, 1, options), + firstChildCSS = child1 && css(child1), + secondChildCSS = child2 && css(child2), + firstChildWidth = firstChildCSS && parseInt(firstChildCSS.marginLeft) + parseInt(firstChildCSS.marginRight) + getRect(child1).width, + secondChildWidth = secondChildCSS && parseInt(secondChildCSS.marginLeft) + parseInt(secondChildCSS.marginRight) + getRect(child2).width; + + if (elCSS.display === 'flex') { + return elCSS.flexDirection === 'column' || elCSS.flexDirection === 'column-reverse' ? 'vertical' : 'horizontal'; + } + + if (elCSS.display === 'grid') { + return elCSS.gridTemplateColumns.split(' ').length <= 1 ? 'vertical' : 'horizontal'; + } + + if (child1 && firstChildCSS["float"] && firstChildCSS["float"] !== 'none') { + var touchingSideChild2 = firstChildCSS["float"] === 'left' ? 'left' : 'right'; + return child2 && (secondChildCSS.clear === 'both' || secondChildCSS.clear === touchingSideChild2) ? 'vertical' : 'horizontal'; + } + + return child1 && (firstChildCSS.display === 'block' || firstChildCSS.display === 'flex' || firstChildCSS.display === 'table' || firstChildCSS.display === 'grid' || firstChildWidth >= elWidth && elCSS[CSSFloatProperty] === 'none' || child2 && elCSS[CSSFloatProperty] === 'none' && firstChildWidth + secondChildWidth > elWidth) ? 'vertical' : 'horizontal'; + }, + _dragElInRowColumn = function _dragElInRowColumn(dragRect, targetRect, vertical) { + var dragElS1Opp = vertical ? dragRect.left : dragRect.top, + dragElS2Opp = vertical ? dragRect.right : dragRect.bottom, + dragElOppLength = vertical ? dragRect.width : dragRect.height, + targetS1Opp = vertical ? targetRect.left : targetRect.top, + targetS2Opp = vertical ? targetRect.right : targetRect.bottom, + targetOppLength = vertical ? targetRect.width : targetRect.height; + return dragElS1Opp === targetS1Opp || dragElS2Opp === targetS2Opp || dragElS1Opp + dragElOppLength / 2 === targetS1Opp + targetOppLength / 2; + }, + + /** + * Detects first nearest empty sortable to X and Y position using emptyInsertThreshold. + * @param {Number} x X position + * @param {Number} y Y position + * @return {HTMLElement} Element of the first found nearest Sortable + */ + _detectNearestEmptySortable = function _detectNearestEmptySortable(x, y) { + var ret; + sortables.some(function (sortable) { + if (lastChild(sortable)) return; + var rect = getRect(sortable), + threshold = sortable[expando].options.emptyInsertThreshold, + insideHorizontally = x >= rect.left - threshold && x <= rect.right + threshold, + insideVertically = y >= rect.top - threshold && y <= rect.bottom + threshold; + + if (threshold && insideHorizontally && insideVertically) { + return ret = sortable; + } + }); + return ret; + }, + _prepareGroup = function _prepareGroup(options) { + function toFn(value, pull) { + return function (to, from, dragEl, evt) { + var sameGroup = to.options.group.name && from.options.group.name && to.options.group.name === from.options.group.name; + + if (value == null && (pull || sameGroup)) { + // Default pull value + // Default pull and put value if same group + return true; + } else if (value == null || value === false) { + return false; + } else if (pull && value === 'clone') { + return value; + } else if (typeof value === 'function') { + return toFn(value(to, from, dragEl, evt), pull)(to, from, dragEl, evt); + } else { + var otherGroup = (pull ? to : from).options.group.name; + return value === true || typeof value === 'string' && value === otherGroup || value.join && value.indexOf(otherGroup) > -1; + } + }; + } + + var group = {}; + var originalGroup = options.group; + + if (!originalGroup || _typeof(originalGroup) != 'object') { + originalGroup = { + name: originalGroup + }; + } + + group.name = originalGroup.name; + group.checkPull = toFn(originalGroup.pull, true); + group.checkPut = toFn(originalGroup.put); + group.revertClone = originalGroup.revertClone; + options.group = group; + }, + _hideGhostForTarget = function _hideGhostForTarget() { + if (!supportCssPointerEvents && ghostEl) { + css(ghostEl, 'display', 'none'); + } + }, + _unhideGhostForTarget = function _unhideGhostForTarget() { + if (!supportCssPointerEvents && ghostEl) { + css(ghostEl, 'display', ''); + } + }; // #1184 fix - Prevent click event on fallback if dragged but item not changed position + + + if (documentExists) { + document.addEventListener('click', function (evt) { + if (ignoreNextClick) { + evt.preventDefault(); + evt.stopPropagation && evt.stopPropagation(); + evt.stopImmediatePropagation && evt.stopImmediatePropagation(); + ignoreNextClick = false; + return false; + } + }, true); + } + + var nearestEmptyInsertDetectEvent = function nearestEmptyInsertDetectEvent(evt) { + if (dragEl) { + evt = evt.touches ? evt.touches[0] : evt; + + var nearest = _detectNearestEmptySortable(evt.clientX, evt.clientY); + + if (nearest) { + // Create imitation event + var event = {}; + + for (var i in evt) { + if (evt.hasOwnProperty(i)) { + event[i] = evt[i]; + } + } + + event.target = event.rootEl = nearest; + event.preventDefault = void 0; + event.stopPropagation = void 0; + + nearest[expando]._onDragOver(event); + } + } + }; + + var _checkOutsideTargetEl = function _checkOutsideTargetEl(evt) { + if (dragEl) { + dragEl.parentNode[expando]._isOutsideThisEl(evt.target); + } + }; + /** + * @class Sortable + * @param {HTMLElement} el + * @param {Object} [options] + */ + + + function Sortable(el, options) { + if (!(el && el.nodeType && el.nodeType === 1)) { + throw "Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(el)); + } + + this.el = el; // root element + + this.options = options = _extends({}, options); // Export instance + + el[expando] = this; + var defaults = { + group: null, + sort: true, + disabled: false, + store: null, + handle: null, + draggable: /^[uo]l$/i.test(el.nodeName) ? '>li' : '>*', + swapThreshold: 1, + // percentage; 0 <= x <= 1 + invertSwap: false, + // invert always + invertedSwapThreshold: null, + // will be set to same as swapThreshold if default + removeCloneOnHide: true, + direction: function direction() { + return _detectDirection(el, this.options); + }, + ghostClass: 'sortable-ghost', + chosenClass: 'sortable-chosen', + dragClass: 'sortable-drag', + ignore: 'a, img', + filter: null, + preventOnFilter: true, + animation: 0, + easing: null, + setData: function setData(dataTransfer, dragEl) { + dataTransfer.setData('Text', dragEl.textContent); + }, + dropBubble: false, + dragoverBubble: false, + dataIdAttr: 'data-id', + delay: 0, + delayOnTouchOnly: false, + touchStartThreshold: (Number.parseInt ? Number : window).parseInt(window.devicePixelRatio, 10) || 1, + forceFallback: false, + fallbackClass: 'sortable-fallback', + fallbackOnBody: false, + fallbackTolerance: 0, + fallbackOffset: { + x: 0, + y: 0 + }, + supportPointer: Sortable.supportPointer !== false && 'PointerEvent' in window, + emptyInsertThreshold: 5 + }; + PluginManager.initializePlugins(this, el, defaults); // Set default options + + for (var name in defaults) { + !(name in options) && (options[name] = defaults[name]); + } + + _prepareGroup(options); // Bind all private methods + + + for (var fn in this) { + if (fn.charAt(0) === '_' && typeof this[fn] === 'function') { + this[fn] = this[fn].bind(this); + } + } // Setup drag mode + + + this.nativeDraggable = options.forceFallback ? false : supportDraggable; + + if (this.nativeDraggable) { + // Touch start threshold cannot be greater than the native dragstart threshold + this.options.touchStartThreshold = 1; + } // Bind events + + + if (options.supportPointer) { + on(el, 'pointerdown', this._onTapStart); + } else { + on(el, 'mousedown', this._onTapStart); + on(el, 'touchstart', this._onTapStart); + } + + if (this.nativeDraggable) { + on(el, 'dragover', this); + on(el, 'dragenter', this); + } + + sortables.push(this.el); // Restore sorting + + options.store && options.store.get && this.sort(options.store.get(this) || []); // Add animation state manager + + _extends(this, AnimationStateManager()); + } + + Sortable.prototype = + /** @lends Sortable.prototype */ + { + constructor: Sortable, + _isOutsideThisEl: function _isOutsideThisEl(target) { + if (!this.el.contains(target) && target !== this.el) { + lastTarget = null; + } + }, + _getDirection: function _getDirection(evt, target) { + return typeof this.options.direction === 'function' ? this.options.direction.call(this, evt, target, dragEl) : this.options.direction; + }, + _onTapStart: function _onTapStart( + /** Event|TouchEvent */ + evt) { + if (!evt.cancelable) return; + + var _this = this, + el = this.el, + options = this.options, + preventOnFilter = options.preventOnFilter, + type = evt.type, + touch = evt.touches && evt.touches[0] || evt.pointerType && evt.pointerType === 'touch' && evt, + target = (touch || evt).target, + originalTarget = evt.target.shadowRoot && (evt.path && evt.path[0] || evt.composedPath && evt.composedPath()[0]) || target, + filter = options.filter; + + _saveInputCheckedState(el); // Don't trigger start event when an element is been dragged, otherwise the evt.oldindex always wrong when set option.group. + + + if (dragEl) { + return; + } + + if (/mousedown|pointerdown/.test(type) && evt.button !== 0 || options.disabled) { + return; // only left button and enabled + } // cancel dnd if original target is content editable + + + if (originalTarget.isContentEditable) { + return; + } + + target = closest(target, options.draggable, el, false); + + if (target && target.animated) { + return; + } + + if (lastDownEl === target) { + // Ignoring duplicate `down` + return; + } // Get the index of the dragged element within its parent + + + oldIndex = index(target); + oldDraggableIndex = index(target, options.draggable); // Check filter + + if (typeof filter === 'function') { + if (filter.call(this, evt, target, this)) { + _dispatchEvent({ + sortable: _this, + rootEl: originalTarget, + name: 'filter', + targetEl: target, + toEl: el, + fromEl: el + }); + + pluginEvent('filter', _this, { + evt: evt + }); + preventOnFilter && evt.cancelable && evt.preventDefault(); + return; // cancel dnd + } + } else if (filter) { + filter = filter.split(',').some(function (criteria) { + criteria = closest(originalTarget, criteria.trim(), el, false); + + if (criteria) { + _dispatchEvent({ + sortable: _this, + rootEl: criteria, + name: 'filter', + targetEl: target, + fromEl: el, + toEl: el + }); + + pluginEvent('filter', _this, { + evt: evt + }); + return true; + } + }); + + if (filter) { + preventOnFilter && evt.cancelable && evt.preventDefault(); + return; // cancel dnd + } + } + + if (options.handle && !closest(originalTarget, options.handle, el, false)) { + return; + } // Prepare `dragstart` + + + this._prepareDragStart(evt, touch, target); + }, + _prepareDragStart: function _prepareDragStart( + /** Event */ + evt, + /** Touch */ + touch, + /** HTMLElement */ + target) { + var _this = this, + el = _this.el, + options = _this.options, + ownerDocument = el.ownerDocument, + dragStartFn; + + if (target && !dragEl && target.parentNode === el) { + var dragRect = getRect(target); + rootEl = el; + dragEl = target; + parentEl = dragEl.parentNode; + nextEl = dragEl.nextSibling; + lastDownEl = target; + activeGroup = options.group; + Sortable.dragged = dragEl; + tapEvt = { + target: dragEl, + clientX: (touch || evt).clientX, + clientY: (touch || evt).clientY + }; + tapDistanceLeft = tapEvt.clientX - dragRect.left; + tapDistanceTop = tapEvt.clientY - dragRect.top; + this._lastX = (touch || evt).clientX; + this._lastY = (touch || evt).clientY; + dragEl.style['will-change'] = 'all'; + + dragStartFn = function dragStartFn() { + pluginEvent('delayEnded', _this, { + evt: evt + }); + + if (Sortable.eventCanceled) { + _this._onDrop(); + + return; + } // Delayed drag has been triggered + // we can re-enable the events: touchmove/mousemove + + + _this._disableDelayedDragEvents(); + + if (!FireFox && _this.nativeDraggable) { + dragEl.draggable = true; + } // Bind the events: dragstart/dragend + + + _this._triggerDragStart(evt, touch); // Drag start event + + + _dispatchEvent({ + sortable: _this, + name: 'choose', + originalEvent: evt + }); // Chosen item + + + toggleClass(dragEl, options.chosenClass, true); + }; // Disable "draggable" + + + options.ignore.split(',').forEach(function (criteria) { + find(dragEl, criteria.trim(), _disableDraggable); + }); + on(ownerDocument, 'dragover', nearestEmptyInsertDetectEvent); + on(ownerDocument, 'mousemove', nearestEmptyInsertDetectEvent); + on(ownerDocument, 'touchmove', nearestEmptyInsertDetectEvent); + on(ownerDocument, 'mouseup', _this._onDrop); + on(ownerDocument, 'touchend', _this._onDrop); + on(ownerDocument, 'touchcancel', _this._onDrop); // Make dragEl draggable (must be before delay for FireFox) + + if (FireFox && this.nativeDraggable) { + this.options.touchStartThreshold = 4; + dragEl.draggable = true; + } + + pluginEvent('delayStart', this, { + evt: evt + }); // Delay is impossible for native DnD in Edge or IE + + if (options.delay && (!options.delayOnTouchOnly || touch) && (!this.nativeDraggable || !(Edge || IE11OrLess))) { + if (Sortable.eventCanceled) { + this._onDrop(); + + return; + } // If the user moves the pointer or let go the click or touch + // before the delay has been reached: + // disable the delayed drag + + + on(ownerDocument, 'mouseup', _this._disableDelayedDrag); + on(ownerDocument, 'touchend', _this._disableDelayedDrag); + on(ownerDocument, 'touchcancel', _this._disableDelayedDrag); + on(ownerDocument, 'mousemove', _this._delayedDragTouchMoveHandler); + on(ownerDocument, 'touchmove', _this._delayedDragTouchMoveHandler); + options.supportPointer && on(ownerDocument, 'pointermove', _this._delayedDragTouchMoveHandler); + _this._dragStartTimer = setTimeout(dragStartFn, options.delay); + } else { + dragStartFn(); + } + } + }, + _delayedDragTouchMoveHandler: function _delayedDragTouchMoveHandler( + /** TouchEvent|PointerEvent **/ + e) { + var touch = e.touches ? e.touches[0] : e; + + if (Math.max(Math.abs(touch.clientX - this._lastX), Math.abs(touch.clientY - this._lastY)) >= Math.floor(this.options.touchStartThreshold / (this.nativeDraggable && window.devicePixelRatio || 1))) { + this._disableDelayedDrag(); + } + }, + _disableDelayedDrag: function _disableDelayedDrag() { + dragEl && _disableDraggable(dragEl); + clearTimeout(this._dragStartTimer); + + this._disableDelayedDragEvents(); + }, + _disableDelayedDragEvents: function _disableDelayedDragEvents() { + var ownerDocument = this.el.ownerDocument; + off(ownerDocument, 'mouseup', this._disableDelayedDrag); + off(ownerDocument, 'touchend', this._disableDelayedDrag); + off(ownerDocument, 'touchcancel', this._disableDelayedDrag); + off(ownerDocument, 'mousemove', this._delayedDragTouchMoveHandler); + off(ownerDocument, 'touchmove', this._delayedDragTouchMoveHandler); + off(ownerDocument, 'pointermove', this._delayedDragTouchMoveHandler); + }, + _triggerDragStart: function _triggerDragStart( + /** Event */ + evt, + /** Touch */ + touch) { + touch = touch || evt.pointerType == 'touch' && evt; + + if (!this.nativeDraggable || touch) { + if (this.options.supportPointer) { + on(document, 'pointermove', this._onTouchMove); + } else if (touch) { + on(document, 'touchmove', this._onTouchMove); + } else { + on(document, 'mousemove', this._onTouchMove); + } + } else { + on(dragEl, 'dragend', this); + on(rootEl, 'dragstart', this._onDragStart); + } + + try { + if (document.selection) { + // Timeout neccessary for IE9 + _nextTick(function () { + document.selection.empty(); + }); + } else { + window.getSelection().removeAllRanges(); + } + } catch (err) { } + }, + _dragStarted: function _dragStarted(fallback, evt) { + + awaitingDragStarted = false; + + if (rootEl && dragEl) { + pluginEvent('dragStarted', this, { + evt: evt + }); + + if (this.nativeDraggable) { + on(document, 'dragover', _checkOutsideTargetEl); + } + + var options = this.options; // Apply effect + + !fallback && toggleClass(dragEl, options.dragClass, false); + toggleClass(dragEl, options.ghostClass, true); + Sortable.active = this; + fallback && this._appendGhost(); // Drag start event + + _dispatchEvent({ + sortable: this, + name: 'start', + originalEvent: evt + }); + } else { + this._nulling(); + } + }, + _emulateDragOver: function _emulateDragOver() { + if (touchEvt) { + this._lastX = touchEvt.clientX; + this._lastY = touchEvt.clientY; + + _hideGhostForTarget(); + + var target = document.elementFromPoint(touchEvt.clientX, touchEvt.clientY); + var parent = target; + + while (target && target.shadowRoot) { + target = target.shadowRoot.elementFromPoint(touchEvt.clientX, touchEvt.clientY); + if (target === parent) break; + parent = target; + } + + dragEl.parentNode[expando]._isOutsideThisEl(target); + + if (parent) { + do { + if (parent[expando]) { + var inserted = void 0; + inserted = parent[expando]._onDragOver({ + clientX: touchEvt.clientX, + clientY: touchEvt.clientY, + target: target, + rootEl: parent + }); + + if (inserted && !this.options.dragoverBubble) { + break; + } + } + + target = parent; // store last element + } + /* jshint boss:true */ + while (parent = parent.parentNode); + } + + _unhideGhostForTarget(); + } + }, + _onTouchMove: function _onTouchMove( + /**TouchEvent*/ + evt) { + if (tapEvt) { + var options = this.options, + fallbackTolerance = options.fallbackTolerance, + fallbackOffset = options.fallbackOffset, + touch = evt.touches ? evt.touches[0] : evt, + ghostMatrix = ghostEl && matrix(ghostEl, true), + scaleX = ghostEl && ghostMatrix && ghostMatrix.a, + scaleY = ghostEl && ghostMatrix && ghostMatrix.d, + relativeScrollOffset = PositionGhostAbsolutely && ghostRelativeParent && getRelativeScrollOffset(ghostRelativeParent), + dx = (touch.clientX - tapEvt.clientX + fallbackOffset.x) / (scaleX || 1) + (relativeScrollOffset ? relativeScrollOffset[0] - ghostRelativeParentInitialScroll[0] : 0) / (scaleX || 1), + dy = (touch.clientY - tapEvt.clientY + fallbackOffset.y) / (scaleY || 1) + (relativeScrollOffset ? relativeScrollOffset[1] - ghostRelativeParentInitialScroll[1] : 0) / (scaleY || 1); // only set the status to dragging, when we are actually dragging + + if (!Sortable.active && !awaitingDragStarted) { + if (fallbackTolerance && Math.max(Math.abs(touch.clientX - this._lastX), Math.abs(touch.clientY - this._lastY)) < fallbackTolerance) { + return; + } + + this._onDragStart(evt, true); + } + + if (ghostEl) { + if (ghostMatrix) { + ghostMatrix.e += dx - (lastDx || 0); + ghostMatrix.f += dy - (lastDy || 0); + } else { + ghostMatrix = { + a: 1, + b: 0, + c: 0, + d: 1, + e: dx, + f: dy + }; + } + + var cssMatrix = "matrix(".concat(ghostMatrix.a, ",").concat(ghostMatrix.b, ",").concat(ghostMatrix.c, ",").concat(ghostMatrix.d, ",").concat(ghostMatrix.e, ",").concat(ghostMatrix.f, ")"); + css(ghostEl, 'webkitTransform', cssMatrix); + css(ghostEl, 'mozTransform', cssMatrix); + css(ghostEl, 'msTransform', cssMatrix); + css(ghostEl, 'transform', cssMatrix); + lastDx = dx; + lastDy = dy; + touchEvt = touch; + } + + evt.cancelable && evt.preventDefault(); + } + }, + _appendGhost: function _appendGhost() { + // Bug if using scale(): https://stackoverflow.com/questions/2637058 + // Not being adjusted for + if (!ghostEl) { + var container = this.options.fallbackOnBody ? document.body : rootEl, + rect = getRect(dragEl, true, PositionGhostAbsolutely, true, container), + options = this.options; // Position absolutely + + if (PositionGhostAbsolutely) { + // Get relatively positioned parent + ghostRelativeParent = container; + + while (css(ghostRelativeParent, 'position') === 'static' && css(ghostRelativeParent, 'transform') === 'none' && ghostRelativeParent !== document) { + ghostRelativeParent = ghostRelativeParent.parentNode; + } + + if (ghostRelativeParent !== document.body && ghostRelativeParent !== document.documentElement) { + if (ghostRelativeParent === document) ghostRelativeParent = getWindowScrollingElement(); + rect.top += ghostRelativeParent.scrollTop; + rect.left += ghostRelativeParent.scrollLeft; + } else { + ghostRelativeParent = getWindowScrollingElement(); + } + + ghostRelativeParentInitialScroll = getRelativeScrollOffset(ghostRelativeParent); + } + + ghostEl = dragEl.cloneNode(true); + toggleClass(ghostEl, options.ghostClass, false); + toggleClass(ghostEl, options.fallbackClass, true); + toggleClass(ghostEl, options.dragClass, true); + css(ghostEl, 'transition', ''); + css(ghostEl, 'transform', ''); + css(ghostEl, 'box-sizing', 'border-box'); + css(ghostEl, 'margin', 0); + css(ghostEl, 'top', rect.top); + css(ghostEl, 'left', rect.left); + css(ghostEl, 'width', rect.width); + css(ghostEl, 'height', rect.height); + css(ghostEl, 'opacity', '0.8'); + css(ghostEl, 'position', PositionGhostAbsolutely ? 'absolute' : 'fixed'); + css(ghostEl, 'zIndex', '100000'); + css(ghostEl, 'pointerEvents', 'none'); + Sortable.ghost = ghostEl; + container.appendChild(ghostEl); // Set transform-origin + + css(ghostEl, 'transform-origin', tapDistanceLeft / parseInt(ghostEl.style.width) * 100 + '% ' + tapDistanceTop / parseInt(ghostEl.style.height) * 100 + '%'); + } + }, + _onDragStart: function _onDragStart( + /**Event*/ + evt, + /**boolean*/ + fallback) { + var _this = this; + + var dataTransfer = evt.dataTransfer; + var options = _this.options; + pluginEvent('dragStart', this, { + evt: evt + }); + + if (Sortable.eventCanceled) { + this._onDrop(); + + return; + } + + pluginEvent('setupClone', this); + + if (!Sortable.eventCanceled) { + cloneEl = clone(dragEl); + cloneEl.draggable = false; + cloneEl.style['will-change'] = ''; + + this._hideClone(); + + toggleClass(cloneEl, this.options.chosenClass, false); + Sortable.clone = cloneEl; + } // #1143: IFrame support workaround + + + _this.cloneId = _nextTick(function () { + pluginEvent('clone', _this); + if (Sortable.eventCanceled) return; + + if (!_this.options.removeCloneOnHide) { + rootEl.insertBefore(cloneEl, dragEl); + } + + _this._hideClone(); + + _dispatchEvent({ + sortable: _this, + name: 'clone' + }); + }); + !fallback && toggleClass(dragEl, options.dragClass, true); // Set proper drop events + + if (fallback) { + ignoreNextClick = true; + _this._loopId = setInterval(_this._emulateDragOver, 50); + } else { + // Undo what was set in _prepareDragStart before drag started + off(document, 'mouseup', _this._onDrop); + off(document, 'touchend', _this._onDrop); + off(document, 'touchcancel', _this._onDrop); + + if (dataTransfer) { + dataTransfer.effectAllowed = 'move'; + options.setData && options.setData.call(_this, dataTransfer, dragEl); + } + + on(document, 'drop', _this); // #1276 fix: + + css(dragEl, 'transform', 'translateZ(0)'); + } + + awaitingDragStarted = true; + _this._dragStartId = _nextTick(_this._dragStarted.bind(_this, fallback, evt)); + on(document, 'selectstart', _this); + moved = true; + + if (Safari) { + css(document.body, 'user-select', 'none'); + } + }, + // Returns true - if no further action is needed (either inserted or another condition) + _onDragOver: function _onDragOver( + /**Event*/ + evt) { + var el = this.el, + target = evt.target, + dragRect, + targetRect, + revert, + options = this.options, + group = options.group, + activeSortable = Sortable.active, + isOwner = activeGroup === group, + canSort = options.sort, + fromSortable = putSortable || activeSortable, + vertical, + _this = this, + completedFired = false; + + if (_silent) return; + + function dragOverEvent(name, extra) { + pluginEvent(name, _this, _objectSpread({ + evt: evt, + isOwner: isOwner, + axis: vertical ? 'vertical' : 'horizontal', + revert: revert, + dragRect: dragRect, + targetRect: targetRect, + canSort: canSort, + fromSortable: fromSortable, + target: target, + completed: completed, + onMove: function onMove(target, after) { + return _onMove(rootEl, el, dragEl, dragRect, target, getRect(target), evt, after); + }, + changed: changed + }, extra)); + } // Capture animation state + + + function capture() { + dragOverEvent('dragOverAnimationCapture'); + + _this.captureAnimationState(); + + if (_this !== fromSortable) { + fromSortable.captureAnimationState(); + } + } // Return invocation when dragEl is inserted (or completed) + + + function completed(insertion) { + dragOverEvent('dragOverCompleted', { + insertion: insertion + }); + + if (insertion) { + // Clones must be hidden before folding animation to capture dragRectAbsolute properly + if (isOwner) { + activeSortable._hideClone(); + } else { + activeSortable._showClone(_this); + } + + if (_this !== fromSortable) { + // Set ghost class to new sortable's ghost class + toggleClass(dragEl, putSortable ? putSortable.options.ghostClass : activeSortable.options.ghostClass, false); + toggleClass(dragEl, options.ghostClass, true); + } + + if (putSortable !== _this && _this !== Sortable.active) { + putSortable = _this; + } else if (_this === Sortable.active && putSortable) { + putSortable = null; + } // Animation + + + if (fromSortable === _this) { + _this._ignoreWhileAnimating = target; + } + + _this.animateAll(function () { + dragOverEvent('dragOverAnimationComplete'); + _this._ignoreWhileAnimating = null; + }); + + if (_this !== fromSortable) { + fromSortable.animateAll(); + fromSortable._ignoreWhileAnimating = null; + } + } // Null lastTarget if it is not inside a previously swapped element + + + if (target === dragEl && !dragEl.animated || target === el && !target.animated) { + lastTarget = null; + } // no bubbling and not fallback + + + if (!options.dragoverBubble && !evt.rootEl && target !== document) { + dragEl.parentNode[expando]._isOutsideThisEl(evt.target); // Do not detect for empty insert if already inserted + + + !insertion && nearestEmptyInsertDetectEvent(evt); + } + + !options.dragoverBubble && evt.stopPropagation && evt.stopPropagation(); + return completedFired = true; + } // Call when dragEl has been inserted + + + function changed() { + newIndex = index(dragEl); + newDraggableIndex = index(dragEl, options.draggable); + + _dispatchEvent({ + sortable: _this, + name: 'change', + toEl: el, + newIndex: newIndex, + newDraggableIndex: newDraggableIndex, + originalEvent: evt + }); + } + + if (evt.preventDefault !== void 0) { + evt.cancelable && evt.preventDefault(); + } + + target = closest(target, options.draggable, el, true); + dragOverEvent('dragOver'); + if (Sortable.eventCanceled) return completedFired; + + if (dragEl.contains(evt.target) || target.animated && target.animatingX && target.animatingY || _this._ignoreWhileAnimating === target) { + return completed(false); + } + + ignoreNextClick = false; + + if (activeSortable && !options.disabled && (isOwner ? canSort || (revert = !rootEl.contains(dragEl)) // Reverting item into the original list + : putSortable === this || (this.lastPutMode = activeGroup.checkPull(this, activeSortable, dragEl, evt)) && group.checkPut(this, activeSortable, dragEl, evt))) { + vertical = this._getDirection(evt, target) === 'vertical'; + dragRect = getRect(dragEl); + dragOverEvent('dragOverValid'); + if (Sortable.eventCanceled) return completedFired; + + if (revert) { + parentEl = rootEl; // actualization + + capture(); + + this._hideClone(); + + dragOverEvent('revert'); + + if (!Sortable.eventCanceled) { + if (nextEl) { + rootEl.insertBefore(dragEl, nextEl); + } else { + rootEl.appendChild(dragEl); + } + } + + return completed(true); + } + + var elLastChild = lastChild(el, options.draggable); + + if (!elLastChild || _ghostIsLast(evt, vertical, this) && !elLastChild.animated) { + // If already at end of list: Do not insert + if (elLastChild === dragEl) { + return completed(false); + } // assign target only if condition is true + + + if (elLastChild && el === evt.target) { + target = elLastChild; + } + + if (target) { + targetRect = getRect(target); + } + + if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, !!target) !== false) { + capture(); + el.appendChild(dragEl); + parentEl = el; // actualization + + changed(); + return completed(true); + } + } else if (target.parentNode === el) { + targetRect = getRect(target); + var direction = 0, + targetBeforeFirstSwap, + differentLevel = dragEl.parentNode !== el, + differentRowCol = !_dragElInRowColumn(dragEl.animated && dragEl.toRect || dragRect, target.animated && target.toRect || targetRect, vertical), + side1 = vertical ? 'top' : 'left', + scrolledPastTop = isScrolledPast(target, 'top', 'top') || isScrolledPast(dragEl, 'top', 'top'), + scrollBefore = scrolledPastTop ? scrolledPastTop.scrollTop : void 0; + + if (lastTarget !== target) { + targetBeforeFirstSwap = targetRect[side1]; + pastFirstInvertThresh = false; + isCircumstantialInvert = !differentRowCol && options.invertSwap || differentLevel; + } + + direction = _getSwapDirection(evt, target, targetRect, vertical, differentRowCol ? 1 : options.swapThreshold, options.invertedSwapThreshold == null ? options.swapThreshold : options.invertedSwapThreshold, isCircumstantialInvert, lastTarget === target); + var sibling; + + if (direction !== 0) { + // Check if target is beside dragEl in respective direction (ignoring hidden elements) + var dragIndex = index(dragEl); + + do { + dragIndex -= direction; + sibling = parentEl.children[dragIndex]; + } while (sibling && (css(sibling, 'display') === 'none' || sibling === ghostEl)); + } // If dragEl is already beside target: Do not insert + + + if (direction === 0 || sibling === target) { + return completed(false); + } + + lastTarget = target; + lastDirection = direction; + var nextSibling = target.nextElementSibling, + after = false; + after = direction === 1; + + var moveVector = _onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, after); + + if (moveVector !== false) { + if (moveVector === 1 || moveVector === -1) { + after = moveVector === 1; + } + + _silent = true; + setTimeout(_unsilent, 30); + capture(); + + if (after && !nextSibling) { + el.appendChild(dragEl); + } else { + target.parentNode.insertBefore(dragEl, after ? nextSibling : target); + } // Undo chrome's scroll adjustment (has no effect on other browsers) + + + if (scrolledPastTop) { + scrollBy(scrolledPastTop, 0, scrollBefore - scrolledPastTop.scrollTop); + } + + parentEl = dragEl.parentNode; // actualization + // must be done before animation + + if (targetBeforeFirstSwap !== undefined && !isCircumstantialInvert) { + targetMoveDistance = Math.abs(targetBeforeFirstSwap - getRect(target)[side1]); + } + + changed(); + return completed(true); + } + } + + if (el.contains(dragEl)) { + return completed(false); + } + } + + return false; + }, + _ignoreWhileAnimating: null, + _offMoveEvents: function _offMoveEvents() { + off(document, 'mousemove', this._onTouchMove); + off(document, 'touchmove', this._onTouchMove); + off(document, 'pointermove', this._onTouchMove); + off(document, 'dragover', nearestEmptyInsertDetectEvent); + off(document, 'mousemove', nearestEmptyInsertDetectEvent); + off(document, 'touchmove', nearestEmptyInsertDetectEvent); + }, + _offUpEvents: function _offUpEvents() { + var ownerDocument = this.el.ownerDocument; + off(ownerDocument, 'mouseup', this._onDrop); + off(ownerDocument, 'touchend', this._onDrop); + off(ownerDocument, 'pointerup', this._onDrop); + off(ownerDocument, 'touchcancel', this._onDrop); + off(document, 'selectstart', this); + }, + _onDrop: function _onDrop( + /**Event*/ + evt) { + var el = this.el, + options = this.options; // Get the index of the dragged element within its parent + + newIndex = index(dragEl); + newDraggableIndex = index(dragEl, options.draggable); + pluginEvent('drop', this, { + evt: evt + }); + parentEl = dragEl && dragEl.parentNode; // Get again after plugin event + + newIndex = index(dragEl); + newDraggableIndex = index(dragEl, options.draggable); + + if (Sortable.eventCanceled) { + this._nulling(); + + return; + } + + awaitingDragStarted = false; + isCircumstantialInvert = false; + pastFirstInvertThresh = false; + clearInterval(this._loopId); + clearTimeout(this._dragStartTimer); + + _cancelNextTick(this.cloneId); + + _cancelNextTick(this._dragStartId); // Unbind events + + + if (this.nativeDraggable) { + off(document, 'drop', this); + off(el, 'dragstart', this._onDragStart); + } + + this._offMoveEvents(); + + this._offUpEvents(); + + if (Safari) { + css(document.body, 'user-select', ''); + } + + css(dragEl, 'transform', ''); + + if (evt) { + if (moved) { + evt.cancelable && evt.preventDefault(); + !options.dropBubble && evt.stopPropagation(); + } + + ghostEl && ghostEl.parentNode && ghostEl.parentNode.removeChild(ghostEl); + + if (rootEl === parentEl || putSortable && putSortable.lastPutMode !== 'clone') { + // Remove clone(s) + cloneEl && cloneEl.parentNode && cloneEl.parentNode.removeChild(cloneEl); + } + + if (dragEl) { + if (this.nativeDraggable) { + off(dragEl, 'dragend', this); + } + + _disableDraggable(dragEl); + + dragEl.style['will-change'] = ''; // Remove classes + // ghostClass is added in dragStarted + + if (moved && !awaitingDragStarted) { + toggleClass(dragEl, putSortable ? putSortable.options.ghostClass : this.options.ghostClass, false); + } + + toggleClass(dragEl, this.options.chosenClass, false); // Drag stop event + + _dispatchEvent({ + sortable: this, + name: 'unchoose', + toEl: parentEl, + newIndex: null, + newDraggableIndex: null, + originalEvent: evt + }); + + if (rootEl !== parentEl) { + if (newIndex >= 0) { + // Add event + _dispatchEvent({ + rootEl: parentEl, + name: 'add', + toEl: parentEl, + fromEl: rootEl, + originalEvent: evt + }); // Remove event + + + _dispatchEvent({ + sortable: this, + name: 'remove', + toEl: parentEl, + originalEvent: evt + }); // drag from one list and drop into another + + + _dispatchEvent({ + rootEl: parentEl, + name: 'sort', + toEl: parentEl, + fromEl: rootEl, + originalEvent: evt + }); + + _dispatchEvent({ + sortable: this, + name: 'sort', + toEl: parentEl, + originalEvent: evt + }); + } + + putSortable && putSortable.save(); + } else { + if (newIndex !== oldIndex) { + if (newIndex >= 0) { + // drag & drop within the same list + _dispatchEvent({ + sortable: this, + name: 'update', + toEl: parentEl, + originalEvent: evt + }); + + _dispatchEvent({ + sortable: this, + name: 'sort', + toEl: parentEl, + originalEvent: evt + }); + } + } + } + + if (Sortable.active) { + /* jshint eqnull:true */ + if (newIndex == null || newIndex === -1) { + newIndex = oldIndex; + newDraggableIndex = oldDraggableIndex; + } + + _dispatchEvent({ + sortable: this, + name: 'end', + toEl: parentEl, + originalEvent: evt + }); // Save sorting + + + this.save(); + } + } + } + + this._nulling(); + }, + _nulling: function _nulling() { + pluginEvent('nulling', this); + rootEl = dragEl = parentEl = ghostEl = nextEl = cloneEl = lastDownEl = cloneHidden = tapEvt = touchEvt = moved = newIndex = newDraggableIndex = oldIndex = oldDraggableIndex = lastTarget = lastDirection = putSortable = activeGroup = Sortable.dragged = Sortable.ghost = Sortable.clone = Sortable.active = null; + savedInputChecked.forEach(function (el) { + el.checked = true; + }); + savedInputChecked.length = lastDx = lastDy = 0; + }, + handleEvent: function handleEvent( + /**Event*/ + evt) { + switch (evt.type) { + case 'drop': + case 'dragend': + this._onDrop(evt); + + break; + + case 'dragenter': + case 'dragover': + if (dragEl) { + this._onDragOver(evt); + + _globalDragOver(evt); + } + + break; + + case 'selectstart': + evt.preventDefault(); + break; + } + }, + + /** + * Serializes the item into an array of string. + * @returns {String[]} + */ + toArray: function toArray() { + var order = [], + el, + children = this.el.children, + i = 0, + n = children.length, + options = this.options; + + for (; i < n; i++) { + el = children[i]; + + if (closest(el, options.draggable, this.el, false)) { + order.push(el.getAttribute(options.dataIdAttr) || _generateId(el)); + } + } + + return order; + }, + + /** + * Sorts the elements according to the array. + * @param {String[]} order order of the items + */ + sort: function sort(order) { + var items = {}, + rootEl = this.el; + this.toArray().forEach(function (id, i) { + var el = rootEl.children[i]; + + if (closest(el, this.options.draggable, rootEl, false)) { + items[id] = el; + } + }, this); + order.forEach(function (id) { + if (items[id]) { + rootEl.removeChild(items[id]); + rootEl.appendChild(items[id]); + } + }); + }, + + /** + * Save the current sorting + */ + save: function save() { + var store = this.options.store; + store && store.set && store.set(this); + }, + + /** + * For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree. + * @param {HTMLElement} el + * @param {String} [selector] default: `options.draggable` + * @returns {HTMLElement|null} + */ + closest: function closest$1(el, selector) { + return closest(el, selector || this.options.draggable, this.el, false); + }, + + /** + * Set/get option + * @param {string} name + * @param {*} [value] + * @returns {*} + */ + option: function option(name, value) { + var options = this.options; + + if (value === void 0) { + return options[name]; + } else { + var modifiedValue = PluginManager.modifyOption(this, name, value); + + if (typeof modifiedValue !== 'undefined') { + options[name] = modifiedValue; + } else { + options[name] = value; + } + + if (name === 'group') { + _prepareGroup(options); + } + } + }, + + /** + * Destroy + */ + destroy: function destroy() { + pluginEvent('destroy', this); + var el = this.el; + el[expando] = null; + off(el, 'mousedown', this._onTapStart); + off(el, 'touchstart', this._onTapStart); + off(el, 'pointerdown', this._onTapStart); + + if (this.nativeDraggable) { + off(el, 'dragover', this); + off(el, 'dragenter', this); + } // Remove draggable attributes + + + Array.prototype.forEach.call(el.querySelectorAll('[draggable]'), function (el) { + el.removeAttribute('draggable'); + }); + + this._onDrop(); + + this._disableDelayedDragEvents(); + + sortables.splice(sortables.indexOf(this.el), 1); + this.el = el = null; + }, + _hideClone: function _hideClone() { + if (!cloneHidden) { + pluginEvent('hideClone', this); + if (Sortable.eventCanceled) return; + css(cloneEl, 'display', 'none'); + + if (this.options.removeCloneOnHide && cloneEl.parentNode) { + cloneEl.parentNode.removeChild(cloneEl); + } + + cloneHidden = true; + } + }, + _showClone: function _showClone(putSortable) { + if (putSortable.lastPutMode !== 'clone') { + this._hideClone(); + + return; + } + + if (cloneHidden) { + pluginEvent('showClone', this); + if (Sortable.eventCanceled) return; // show clone at dragEl or original position + + if (rootEl.contains(dragEl) && !this.options.group.revertClone) { + rootEl.insertBefore(cloneEl, dragEl); + } else if (nextEl) { + rootEl.insertBefore(cloneEl, nextEl); + } else { + rootEl.appendChild(cloneEl); + } + + if (this.options.group.revertClone) { + this.animate(dragEl, cloneEl); + } + + css(cloneEl, 'display', ''); + cloneHidden = false; + } + } + }; + + function _globalDragOver( + /**Event*/ + evt) { + if (evt.dataTransfer) { + evt.dataTransfer.dropEffect = 'move'; + } + + evt.cancelable && evt.preventDefault(); + } + + function _onMove(fromEl, toEl, dragEl, dragRect, targetEl, targetRect, originalEvent, willInsertAfter) { + var evt, + sortable = fromEl[expando], + onMoveFn = sortable.options.onMove, + retVal; // Support for new CustomEvent feature + + if (window.CustomEvent && !IE11OrLess && !Edge) { + evt = new CustomEvent('move', { + bubbles: true, + cancelable: true + }); + } else { + evt = document.createEvent('Event'); + evt.initEvent('move', true, true); + } + + evt.to = toEl; + evt.from = fromEl; + evt.dragged = dragEl; + evt.draggedRect = dragRect; + evt.related = targetEl || toEl; + evt.relatedRect = targetRect || getRect(toEl); + evt.willInsertAfter = willInsertAfter; + evt.originalEvent = originalEvent; + fromEl.dispatchEvent(evt); + + if (onMoveFn) { + retVal = onMoveFn.call(sortable, evt, originalEvent); + } + + return retVal; + } + + function _disableDraggable(el) { + el.draggable = false; + } + + function _unsilent() { + _silent = false; + } + + function _ghostIsLast(evt, vertical, sortable) { + var rect = getRect(lastChild(sortable.el, sortable.options.draggable)); + var spacer = 10; + return vertical ? evt.clientX > rect.right + spacer || evt.clientX <= rect.right && evt.clientY > rect.bottom && evt.clientX >= rect.left : evt.clientX > rect.right && evt.clientY > rect.top || evt.clientX <= rect.right && evt.clientY > rect.bottom + spacer; + } + + function _getSwapDirection(evt, target, targetRect, vertical, swapThreshold, invertedSwapThreshold, invertSwap, isLastTarget) { + var mouseOnAxis = vertical ? evt.clientY : evt.clientX, + targetLength = vertical ? targetRect.height : targetRect.width, + targetS1 = vertical ? targetRect.top : targetRect.left, + targetS2 = vertical ? targetRect.bottom : targetRect.right, + invert = false; + + if (!invertSwap) { + // Never invert or create dragEl shadow when target movemenet causes mouse to move past the end of regular swapThreshold + if (isLastTarget && targetMoveDistance < targetLength * swapThreshold) { + // multiplied only by swapThreshold because mouse will already be inside target by (1 - threshold) * targetLength / 2 + // check if past first invert threshold on side opposite of lastDirection + if (!pastFirstInvertThresh && (lastDirection === 1 ? mouseOnAxis > targetS1 + targetLength * invertedSwapThreshold / 2 : mouseOnAxis < targetS2 - targetLength * invertedSwapThreshold / 2)) { + // past first invert threshold, do not restrict inverted threshold to dragEl shadow + pastFirstInvertThresh = true; + } + + if (!pastFirstInvertThresh) { + // dragEl shadow (target move distance shadow) + if (lastDirection === 1 ? mouseOnAxis < targetS1 + targetMoveDistance // over dragEl shadow + : mouseOnAxis > targetS2 - targetMoveDistance) { + return -lastDirection; + } + } else { + invert = true; + } + } else { + // Regular + if (mouseOnAxis > targetS1 + targetLength * (1 - swapThreshold) / 2 && mouseOnAxis < targetS2 - targetLength * (1 - swapThreshold) / 2) { + return _getInsertDirection(target); + } + } + } + + invert = invert || invertSwap; + + if (invert) { + // Invert of regular + if (mouseOnAxis < targetS1 + targetLength * invertedSwapThreshold / 2 || mouseOnAxis > targetS2 - targetLength * invertedSwapThreshold / 2) { + return mouseOnAxis > targetS1 + targetLength / 2 ? 1 : -1; + } + } + + return 0; + } + /** + * Gets the direction dragEl must be swapped relative to target in order to make it + * seem that dragEl has been "inserted" into that element's position + * @param {HTMLElement} target The target whose position dragEl is being inserted at + * @return {Number} Direction dragEl must be swapped + */ + + + function _getInsertDirection(target) { + if (index(dragEl) < index(target)) { + return 1; + } else { + return -1; + } + } + /** + * Generate id + * @param {HTMLElement} el + * @returns {String} + * @private + */ + + + function _generateId(el) { + var str = el.tagName + el.className + el.src + el.href + el.textContent, + i = str.length, + sum = 0; + + while (i--) { + sum += str.charCodeAt(i); + } + + return sum.toString(36); + } + + function _saveInputCheckedState(root) { + savedInputChecked.length = 0; + var inputs = root.getElementsByTagName('input'); + var idx = inputs.length; + + while (idx--) { + var el = inputs[idx]; + el.checked && savedInputChecked.push(el); + } + } + + function _nextTick(fn) { + return setTimeout(fn, 0); + } + + function _cancelNextTick(id) { + return clearTimeout(id); + } // Fixed #973: + + + if (documentExists) { + on(document, 'touchmove', function (evt) { + if ((Sortable.active || awaitingDragStarted) && evt.cancelable) { + evt.preventDefault(); + } + }); + } // Export utils + + + Sortable.utils = { + on: on, + off: off, + css: css, + find: find, + is: function is(el, selector) { + return !!closest(el, selector, el, false); + }, + extend: extend, + throttle: throttle, + closest: closest, + toggleClass: toggleClass, + clone: clone, + index: index, + nextTick: _nextTick, + cancelNextTick: _cancelNextTick, + detectDirection: _detectDirection, + getChild: getChild + }; + /** + * Get the Sortable instance of an element + * @param {HTMLElement} element The element + * @return {Sortable|undefined} The instance of Sortable + */ + + Sortable.get = function (element) { + return element[expando]; + }; + /** + * Mount a plugin to Sortable + * @param {...SortablePlugin|SortablePlugin[]} plugins Plugins being mounted + */ + + + Sortable.mount = function () { + for (var _len = arguments.length, plugins = new Array(_len), _key = 0; _key < _len; _key++) { + plugins[_key] = arguments[_key]; + } + + if (plugins[0].constructor === Array) plugins = plugins[0]; + plugins.forEach(function (plugin) { + if (!plugin.prototype || !plugin.prototype.constructor) { + throw "Sortable: Mounted plugin must be a constructor function, not ".concat({}.toString.call(plugin)); + } + + if (plugin.utils) Sortable.utils = _objectSpread({}, Sortable.utils, plugin.utils); + PluginManager.mount(plugin); + }); + }; + /** + * Create sortable instance + * @param {HTMLElement} el + * @param {Object} [options] + */ + + + Sortable.create = function (el, options) { + return new Sortable(el, options); + }; // Export + + + Sortable.version = version; + + var autoScrolls = [], + scrollEl, + scrollRootEl, + scrolling = false, + lastAutoScrollX, + lastAutoScrollY, + touchEvt$1, + pointerElemChangedInterval; + + function AutoScrollPlugin() { + function AutoScroll() { + this.defaults = { + scroll: true, + scrollSensitivity: 30, + scrollSpeed: 10, + bubbleScroll: true + }; // Bind all private methods + + for (var fn in this) { + if (fn.charAt(0) === '_' && typeof this[fn] === 'function') { + this[fn] = this[fn].bind(this); + } + } + } + + AutoScroll.prototype = { + dragStarted: function dragStarted(_ref) { + var originalEvent = _ref.originalEvent; + + if (this.sortable.nativeDraggable) { + on(document, 'dragover', this._handleAutoScroll); + } else { + if (this.options.supportPointer) { + on(document, 'pointermove', this._handleFallbackAutoScroll); + } else if (originalEvent.touches) { + on(document, 'touchmove', this._handleFallbackAutoScroll); + } else { + on(document, 'mousemove', this._handleFallbackAutoScroll); + } + } + }, + dragOverCompleted: function dragOverCompleted(_ref2) { + var originalEvent = _ref2.originalEvent; + + // For when bubbling is canceled and using fallback (fallback 'touchmove' always reached) + if (!this.options.dragOverBubble && !originalEvent.rootEl) { + this._handleAutoScroll(originalEvent); + } + }, + drop: function drop() { + if (this.sortable.nativeDraggable) { + off(document, 'dragover', this._handleAutoScroll); + } else { + off(document, 'pointermove', this._handleFallbackAutoScroll); + off(document, 'touchmove', this._handleFallbackAutoScroll); + off(document, 'mousemove', this._handleFallbackAutoScroll); + } + + clearPointerElemChangedInterval(); + clearAutoScrolls(); + cancelThrottle(); + }, + nulling: function nulling() { + touchEvt$1 = scrollRootEl = scrollEl = scrolling = pointerElemChangedInterval = lastAutoScrollX = lastAutoScrollY = null; + autoScrolls.length = 0; + }, + _handleFallbackAutoScroll: function _handleFallbackAutoScroll(evt) { + this._handleAutoScroll(evt, true); + }, + _handleAutoScroll: function _handleAutoScroll(evt, fallback) { + var _this = this; + + var x = (evt.touches ? evt.touches[0] : evt).clientX, + y = (evt.touches ? evt.touches[0] : evt).clientY, + elem = document.elementFromPoint(x, y); + touchEvt$1 = evt; // IE does not seem to have native autoscroll, + // Edge's autoscroll seems too conditional, + // MACOS Safari does not have autoscroll, + // Firefox and Chrome are good + + if (fallback || Edge || IE11OrLess || Safari) { + autoScroll(evt, this.options, elem, fallback); // Listener for pointer element change + + var ogElemScroller = getParentAutoScrollElement(elem, true); + + if (scrolling && (!pointerElemChangedInterval || x !== lastAutoScrollX || y !== lastAutoScrollY)) { + pointerElemChangedInterval && clearPointerElemChangedInterval(); // Detect for pointer elem change, emulating native DnD behaviour + + pointerElemChangedInterval = setInterval(function () { + var newElem = getParentAutoScrollElement(document.elementFromPoint(x, y), true); + + if (newElem !== ogElemScroller) { + ogElemScroller = newElem; + clearAutoScrolls(); + } + + autoScroll(evt, _this.options, newElem, fallback); + }, 10); + lastAutoScrollX = x; + lastAutoScrollY = y; + } + } else { + // if DnD is enabled (and browser has good autoscrolling), first autoscroll will already scroll, so get parent autoscroll of first autoscroll + if (!this.options.bubbleScroll || getParentAutoScrollElement(elem, true) === getWindowScrollingElement()) { + clearAutoScrolls(); + return; + } + + autoScroll(evt, this.options, getParentAutoScrollElement(elem, false), false); + } + } + }; + return _extends(AutoScroll, { + pluginName: 'scroll', + initializeByDefault: true + }); + } + + function clearAutoScrolls() { + autoScrolls.forEach(function (autoScroll) { + clearInterval(autoScroll.pid); + }); + autoScrolls = []; + } + + function clearPointerElemChangedInterval() { + clearInterval(pointerElemChangedInterval); + } + + var autoScroll = throttle(function (evt, options, rootEl, isFallback) { + // Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=505521 + if (!options.scroll) return; + var x = (evt.touches ? evt.touches[0] : evt).clientX, + y = (evt.touches ? evt.touches[0] : evt).clientY, + sens = options.scrollSensitivity, + speed = options.scrollSpeed, + winScroller = getWindowScrollingElement(); + var scrollThisInstance = false, + scrollCustomFn; // New scroll root, set scrollEl + + if (scrollRootEl !== rootEl) { + scrollRootEl = rootEl; + clearAutoScrolls(); + scrollEl = options.scroll; + scrollCustomFn = options.scrollFn; + + if (scrollEl === true) { + scrollEl = getParentAutoScrollElement(rootEl, true); + } + } + + var layersOut = 0; + var currentParent = scrollEl; + + do { + var el = currentParent, + rect = getRect(el), + top = rect.top, + bottom = rect.bottom, + left = rect.left, + right = rect.right, + width = rect.width, + height = rect.height, + canScrollX = void 0, + canScrollY = void 0, + scrollWidth = el.scrollWidth, + scrollHeight = el.scrollHeight, + elCSS = css(el), + scrollPosX = el.scrollLeft, + scrollPosY = el.scrollTop; + + if (el === winScroller) { + canScrollX = width < scrollWidth && (elCSS.overflowX === 'auto' || elCSS.overflowX === 'scroll' || elCSS.overflowX === 'visible'); + canScrollY = height < scrollHeight && (elCSS.overflowY === 'auto' || elCSS.overflowY === 'scroll' || elCSS.overflowY === 'visible'); + } else { + canScrollX = width < scrollWidth && (elCSS.overflowX === 'auto' || elCSS.overflowX === 'scroll'); + canScrollY = height < scrollHeight && (elCSS.overflowY === 'auto' || elCSS.overflowY === 'scroll'); + } + + var vx = canScrollX && (Math.abs(right - x) <= sens && scrollPosX + width < scrollWidth) - (Math.abs(left - x) <= sens && !!scrollPosX); + var vy = canScrollY && (Math.abs(bottom - y) <= sens && scrollPosY + height < scrollHeight) - (Math.abs(top - y) <= sens && !!scrollPosY); + + if (!autoScrolls[layersOut]) { + for (var i = 0; i <= layersOut; i++) { + if (!autoScrolls[i]) { + autoScrolls[i] = {}; + } + } + } + + if (autoScrolls[layersOut].vx != vx || autoScrolls[layersOut].vy != vy || autoScrolls[layersOut].el !== el) { + autoScrolls[layersOut].el = el; + autoScrolls[layersOut].vx = vx; + autoScrolls[layersOut].vy = vy; + clearInterval(autoScrolls[layersOut].pid); + + if (vx != 0 || vy != 0) { + scrollThisInstance = true; + /* jshint loopfunc:true */ + + autoScrolls[layersOut].pid = setInterval(function () { + // emulate drag over during autoscroll (fallback), emulating native DnD behaviour + if (isFallback && this.layer === 0) { + Sortable.active._onTouchMove(touchEvt$1); // To move ghost if it is positioned absolutely + + } + + var scrollOffsetY = autoScrolls[this.layer].vy ? autoScrolls[this.layer].vy * speed : 0; + var scrollOffsetX = autoScrolls[this.layer].vx ? autoScrolls[this.layer].vx * speed : 0; + + if (typeof scrollCustomFn === 'function') { + if (scrollCustomFn.call(Sortable.dragged.parentNode[expando], scrollOffsetX, scrollOffsetY, evt, touchEvt$1, autoScrolls[this.layer].el) !== 'continue') { + return; + } + } + + scrollBy(autoScrolls[this.layer].el, scrollOffsetX, scrollOffsetY); + }.bind({ + layer: layersOut + }), 24); + } + } + + layersOut++; + } while (options.bubbleScroll && currentParent !== winScroller && (currentParent = getParentAutoScrollElement(currentParent, false))); + + scrolling = scrollThisInstance; // in case another function catches scrolling as false in between when it is not + }, 30); + + var drop = function drop(_ref) { + var originalEvent = _ref.originalEvent, + putSortable = _ref.putSortable, + dragEl = _ref.dragEl, + activeSortable = _ref.activeSortable, + dispatchSortableEvent = _ref.dispatchSortableEvent, + hideGhostForTarget = _ref.hideGhostForTarget, + unhideGhostForTarget = _ref.unhideGhostForTarget; + if (!originalEvent) return; + var toSortable = putSortable || activeSortable; + hideGhostForTarget(); + var touch = originalEvent.changedTouches && originalEvent.changedTouches.length ? originalEvent.changedTouches[0] : originalEvent; + var target = document.elementFromPoint(touch.clientX, touch.clientY); + unhideGhostForTarget(); + + if (toSortable && !toSortable.el.contains(target)) { + dispatchSortableEvent('spill'); + this.onSpill({ + dragEl: dragEl, + putSortable: putSortable + }); + } + }; + + function Revert() { } + + Revert.prototype = { + startIndex: null, + dragStart: function dragStart(_ref2) { + var oldDraggableIndex = _ref2.oldDraggableIndex; + this.startIndex = oldDraggableIndex; + }, + onSpill: function onSpill(_ref3) { + var dragEl = _ref3.dragEl, + putSortable = _ref3.putSortable; + this.sortable.captureAnimationState(); + + if (putSortable) { + putSortable.captureAnimationState(); + } + + var nextSibling = getChild(this.sortable.el, this.startIndex, this.options); + + if (nextSibling) { + this.sortable.el.insertBefore(dragEl, nextSibling); + } else { + this.sortable.el.appendChild(dragEl); + } + + this.sortable.animateAll(); + + if (putSortable) { + putSortable.animateAll(); + } + }, + drop: drop + }; + + _extends(Revert, { + pluginName: 'revertOnSpill' + }); + + function Remove() { } + + Remove.prototype = { + onSpill: function onSpill(_ref4) { + var dragEl = _ref4.dragEl, + putSortable = _ref4.putSortable; + var parentSortable = putSortable || this.sortable; + parentSortable.captureAnimationState(); + dragEl.parentNode && dragEl.parentNode.removeChild(dragEl); + parentSortable.animateAll(); + }, + drop: drop + }; + + _extends(Remove, { + pluginName: 'removeOnSpill' + }); + + var lastSwapEl; + + function SwapPlugin() { + function Swap() { + this.defaults = { + swapClass: 'sortable-swap-highlight' + }; + } + + Swap.prototype = { + dragStart: function dragStart(_ref) { + var dragEl = _ref.dragEl; + lastSwapEl = dragEl; + }, + dragOverValid: function dragOverValid(_ref2) { + var completed = _ref2.completed, + target = _ref2.target, + onMove = _ref2.onMove, + activeSortable = _ref2.activeSortable, + changed = _ref2.changed, + cancel = _ref2.cancel; + if (!activeSortable.options.swap) return; + var el = this.sortable.el, + options = this.options; + + if (target && target !== el) { + var prevSwapEl = lastSwapEl; + + if (onMove(target) !== false) { + toggleClass(target, options.swapClass, true); + lastSwapEl = target; + } else { + lastSwapEl = null; + } + + if (prevSwapEl && prevSwapEl !== lastSwapEl) { + toggleClass(prevSwapEl, options.swapClass, false); + } + } + + changed(); + completed(true); + cancel(); + }, + drop: function drop(_ref3) { + var activeSortable = _ref3.activeSortable, + putSortable = _ref3.putSortable, + dragEl = _ref3.dragEl; + var toSortable = putSortable || this.sortable; + var options = this.options; + lastSwapEl && toggleClass(lastSwapEl, options.swapClass, false); + + if (lastSwapEl && (options.swap || putSortable && putSortable.options.swap)) { + if (dragEl !== lastSwapEl) { + toSortable.captureAnimationState(); + if (toSortable !== activeSortable) activeSortable.captureAnimationState(); + swapNodes(dragEl, lastSwapEl); + toSortable.animateAll(); + if (toSortable !== activeSortable) activeSortable.animateAll(); + } + } + }, + nulling: function nulling() { + lastSwapEl = null; + } + }; + return _extends(Swap, { + pluginName: 'swap', + eventProperties: function eventProperties() { + return { + swapItem: lastSwapEl + }; + } + }); + } + + function swapNodes(n1, n2) { + var p1 = n1.parentNode, + p2 = n2.parentNode, + i1, + i2; + if (!p1 || !p2 || p1.isEqualNode(n2) || p2.isEqualNode(n1)) return; + i1 = index(n1); + i2 = index(n2); + + if (p1.isEqualNode(p2) && i1 < i2) { + i2++; + } + + p1.insertBefore(n2, p1.children[i1]); + p2.insertBefore(n1, p2.children[i2]); + } + + var multiDragElements = [], + multiDragClones = [], + lastMultiDragSelect, + // for selection with modifier key down (SHIFT) + multiDragSortable, + initialFolding = false, + // Initial multi-drag fold when drag started + folding = false, + // Folding any other time + dragStarted = false, + dragEl$1, + clonesFromRect, + clonesHidden; + + function MultiDragPlugin() { + function MultiDrag(sortable) { + // Bind all private methods + for (var fn in this) { + if (fn.charAt(0) === '_' && typeof this[fn] === 'function') { + this[fn] = this[fn].bind(this); + } + } + + if (sortable.options.supportPointer) { + on(document, 'pointerup', this._deselectMultiDrag); + } else { + on(document, 'mouseup', this._deselectMultiDrag); + on(document, 'touchend', this._deselectMultiDrag); + } + + on(document, 'keydown', this._checkKeyDown); + on(document, 'keyup', this._checkKeyUp); + this.defaults = { + selectedClass: 'sortable-selected', + multiDragKey: null, + setData: function setData(dataTransfer, dragEl) { + var data = ''; + + if (multiDragElements.length && multiDragSortable === sortable) { + multiDragElements.forEach(function (multiDragElement, i) { + data += (!i ? '' : ', ') + multiDragElement.textContent; + }); + } else { + data = dragEl.textContent; + } + + dataTransfer.setData('Text', data); + } + }; + } + + MultiDrag.prototype = { + multiDragKeyDown: false, + isMultiDrag: false, + delayStartGlobal: function delayStartGlobal(_ref) { + var dragged = _ref.dragEl; + dragEl$1 = dragged; + }, + delayEnded: function delayEnded() { + this.isMultiDrag = ~multiDragElements.indexOf(dragEl$1); + }, + setupClone: function setupClone(_ref2) { + var sortable = _ref2.sortable, + cancel = _ref2.cancel; + if (!this.isMultiDrag) return; + + for (var i = 0; i < multiDragElements.length; i++) { + multiDragClones.push(clone(multiDragElements[i])); + multiDragClones[i].sortableIndex = multiDragElements[i].sortableIndex; + multiDragClones[i].draggable = false; + multiDragClones[i].style['will-change'] = ''; + toggleClass(multiDragClones[i], this.options.selectedClass, false); + multiDragElements[i] === dragEl$1 && toggleClass(multiDragClones[i], this.options.chosenClass, false); + } + + sortable._hideClone(); + + cancel(); + }, + clone: function clone(_ref3) { + var sortable = _ref3.sortable, + rootEl = _ref3.rootEl, + dispatchSortableEvent = _ref3.dispatchSortableEvent, + cancel = _ref3.cancel; + if (!this.isMultiDrag) return; + + if (!this.options.removeCloneOnHide) { + if (multiDragElements.length && multiDragSortable === sortable) { + insertMultiDragClones(true, rootEl); + dispatchSortableEvent('clone'); + cancel(); + } + } + }, + showClone: function showClone(_ref4) { + var cloneNowShown = _ref4.cloneNowShown, + rootEl = _ref4.rootEl, + cancel = _ref4.cancel; + if (!this.isMultiDrag) return; + insertMultiDragClones(false, rootEl); + multiDragClones.forEach(function (clone) { + css(clone, 'display', ''); + }); + cloneNowShown(); + clonesHidden = false; + cancel(); + }, + hideClone: function hideClone(_ref5) { + var _this = this; + + var sortable = _ref5.sortable, + cloneNowHidden = _ref5.cloneNowHidden, + cancel = _ref5.cancel; + if (!this.isMultiDrag) return; + multiDragClones.forEach(function (clone) { + css(clone, 'display', 'none'); + + if (_this.options.removeCloneOnHide && clone.parentNode) { + clone.parentNode.removeChild(clone); + } + }); + cloneNowHidden(); + clonesHidden = true; + cancel(); + }, + dragStartGlobal: function dragStartGlobal(_ref6) { + var sortable = _ref6.sortable; + + if (!this.isMultiDrag && multiDragSortable) { + multiDragSortable.multiDrag._deselectMultiDrag(); + } + + multiDragElements.forEach(function (multiDragElement) { + multiDragElement.sortableIndex = index(multiDragElement); + }); // Sort multi-drag elements + + multiDragElements = multiDragElements.sort(function (a, b) { + return a.sortableIndex - b.sortableIndex; + }); + dragStarted = true; + }, + dragStarted: function dragStarted(_ref7) { + var _this2 = this; + + var sortable = _ref7.sortable; + if (!this.isMultiDrag) return; + + if (this.options.sort) { + // Capture rects, + // hide multi drag elements (by positioning them absolute), + // set multi drag elements rects to dragRect, + // show multi drag elements, + // animate to rects, + // unset rects & remove from DOM + sortable.captureAnimationState(); + + if (this.options.animation) { + multiDragElements.forEach(function (multiDragElement) { + if (multiDragElement === dragEl$1) return; + css(multiDragElement, 'position', 'absolute'); + }); + var dragRect = getRect(dragEl$1, false, true, true); + multiDragElements.forEach(function (multiDragElement) { + if (multiDragElement === dragEl$1) return; + setRect(multiDragElement, dragRect); + }); + folding = true; + initialFolding = true; + } + } + + sortable.animateAll(function () { + folding = false; + initialFolding = false; + + if (_this2.options.animation) { + multiDragElements.forEach(function (multiDragElement) { + unsetRect(multiDragElement); + }); + } // Remove all auxiliary multidrag items from el, if sorting enabled + + + if (_this2.options.sort) { + removeMultiDragElements(); + } + }); + }, + dragOver: function dragOver(_ref8) { + var target = _ref8.target, + completed = _ref8.completed, + cancel = _ref8.cancel; + + if (folding && ~multiDragElements.indexOf(target)) { + completed(false); + cancel(); + } + }, + revert: function revert(_ref9) { + var fromSortable = _ref9.fromSortable, + rootEl = _ref9.rootEl, + sortable = _ref9.sortable, + dragRect = _ref9.dragRect; + + if (multiDragElements.length > 1) { + // Setup unfold animation + multiDragElements.forEach(function (multiDragElement) { + sortable.addAnimationState({ + target: multiDragElement, + rect: folding ? getRect(multiDragElement) : dragRect + }); + unsetRect(multiDragElement); + multiDragElement.fromRect = dragRect; + fromSortable.removeAnimationState(multiDragElement); + }); + folding = false; + insertMultiDragElements(!this.options.removeCloneOnHide, rootEl); + } + }, + dragOverCompleted: function dragOverCompleted(_ref10) { + var sortable = _ref10.sortable, + isOwner = _ref10.isOwner, + insertion = _ref10.insertion, + activeSortable = _ref10.activeSortable, + parentEl = _ref10.parentEl, + putSortable = _ref10.putSortable; + var options = this.options; + + if (insertion) { + // Clones must be hidden before folding animation to capture dragRectAbsolute properly + if (isOwner) { + activeSortable._hideClone(); + } + + initialFolding = false; // If leaving sort:false root, or already folding - Fold to new location + + if (options.animation && multiDragElements.length > 1 && (folding || !isOwner && !activeSortable.options.sort && !putSortable)) { + // Fold: Set all multi drag elements's rects to dragEl's rect when multi-drag elements are invisible + var dragRectAbsolute = getRect(dragEl$1, false, true, true); + multiDragElements.forEach(function (multiDragElement) { + if (multiDragElement === dragEl$1) return; + setRect(multiDragElement, dragRectAbsolute); // Move element(s) to end of parentEl so that it does not interfere with multi-drag clones insertion if they are inserted + // while folding, and so that we can capture them again because old sortable will no longer be fromSortable + + parentEl.appendChild(multiDragElement); + }); + folding = true; + } // Clones must be shown (and check to remove multi drags) after folding when interfering multiDragElements are moved out + + + if (!isOwner) { + // Only remove if not folding (folding will remove them anyways) + if (!folding) { + removeMultiDragElements(); + } + + if (multiDragElements.length > 1) { + var clonesHiddenBefore = clonesHidden; + + activeSortable._showClone(sortable); // Unfold animation for clones if showing from hidden + + + if (activeSortable.options.animation && !clonesHidden && clonesHiddenBefore) { + multiDragClones.forEach(function (clone) { + activeSortable.addAnimationState({ + target: clone, + rect: clonesFromRect + }); + clone.fromRect = clonesFromRect; + clone.thisAnimationDuration = null; + }); + } + } else { + activeSortable._showClone(sortable); + } + } + } + }, + dragOverAnimationCapture: function dragOverAnimationCapture(_ref11) { + var dragRect = _ref11.dragRect, + isOwner = _ref11.isOwner, + activeSortable = _ref11.activeSortable; + multiDragElements.forEach(function (multiDragElement) { + multiDragElement.thisAnimationDuration = null; + }); + + if (activeSortable.options.animation && !isOwner && activeSortable.multiDrag.isMultiDrag) { + clonesFromRect = _extends({}, dragRect); + var dragMatrix = matrix(dragEl$1, true); + clonesFromRect.top -= dragMatrix.f; + clonesFromRect.left -= dragMatrix.e; + } + }, + dragOverAnimationComplete: function dragOverAnimationComplete() { + if (folding) { + folding = false; + removeMultiDragElements(); + } + }, + drop: function drop(_ref12) { + var evt = _ref12.originalEvent, + rootEl = _ref12.rootEl, + parentEl = _ref12.parentEl, + sortable = _ref12.sortable, + dispatchSortableEvent = _ref12.dispatchSortableEvent, + oldIndex = _ref12.oldIndex, + putSortable = _ref12.putSortable; + var toSortable = putSortable || this.sortable; + if (!evt) return; + var options = this.options, + children = parentEl.children; // Multi-drag selection + + if (!dragStarted) { + if (options.multiDragKey && !this.multiDragKeyDown) { + this._deselectMultiDrag(); + } + + toggleClass(dragEl$1, options.selectedClass, !~multiDragElements.indexOf(dragEl$1)); + + if (!~multiDragElements.indexOf(dragEl$1)) { + multiDragElements.push(dragEl$1); + dispatchEvent({ + sortable: sortable, + rootEl: rootEl, + name: 'select', + targetEl: dragEl$1, + originalEvt: evt + }); // Modifier activated, select from last to dragEl + + if (evt.shiftKey && lastMultiDragSelect && sortable.el.contains(lastMultiDragSelect)) { + var lastIndex = index(lastMultiDragSelect), + currentIndex = index(dragEl$1); + + if (~lastIndex && ~currentIndex && lastIndex !== currentIndex) { + // Must include lastMultiDragSelect (select it), in case modified selection from no selection + // (but previous selection existed) + var n, i; + + if (currentIndex > lastIndex) { + i = lastIndex; + n = currentIndex; + } else { + i = currentIndex; + n = lastIndex + 1; + } + + for (; i < n; i++) { + if (~multiDragElements.indexOf(children[i])) continue; + toggleClass(children[i], options.selectedClass, true); + multiDragElements.push(children[i]); + dispatchEvent({ + sortable: sortable, + rootEl: rootEl, + name: 'select', + targetEl: children[i], + originalEvt: evt + }); + } + } + } else { + lastMultiDragSelect = dragEl$1; + } + + multiDragSortable = toSortable; + } else { + multiDragElements.splice(multiDragElements.indexOf(dragEl$1), 1); + lastMultiDragSelect = null; + dispatchEvent({ + sortable: sortable, + rootEl: rootEl, + name: 'deselect', + targetEl: dragEl$1, + originalEvt: evt + }); + } + } // Multi-drag drop + + + if (dragStarted && this.isMultiDrag) { + // Do not "unfold" after around dragEl if reverted + if ((parentEl[expando].options.sort || parentEl !== rootEl) && multiDragElements.length > 1) { + var dragRect = getRect(dragEl$1), + multiDragIndex = index(dragEl$1, ':not(.' + this.options.selectedClass + ')'); + if (!initialFolding && options.animation) dragEl$1.thisAnimationDuration = null; + toSortable.captureAnimationState(); + + if (!initialFolding) { + if (options.animation) { + dragEl$1.fromRect = dragRect; + multiDragElements.forEach(function (multiDragElement) { + multiDragElement.thisAnimationDuration = null; + + if (multiDragElement !== dragEl$1) { + var rect = folding ? getRect(multiDragElement) : dragRect; + multiDragElement.fromRect = rect; // Prepare unfold animation + + toSortable.addAnimationState({ + target: multiDragElement, + rect: rect + }); + } + }); + } // Multi drag elements are not necessarily removed from the DOM on drop, so to reinsert + // properly they must all be removed + + + removeMultiDragElements(); + multiDragElements.forEach(function (multiDragElement) { + if (children[multiDragIndex]) { + parentEl.insertBefore(multiDragElement, children[multiDragIndex]); + } else { + parentEl.appendChild(multiDragElement); + } + + multiDragIndex++; + }); // If initial folding is done, the elements may have changed position because they are now + // unfolding around dragEl, even though dragEl may not have his index changed, so update event + // must be fired here as Sortable will not. + + if (oldIndex === index(dragEl$1)) { + var update = false; + multiDragElements.forEach(function (multiDragElement) { + if (multiDragElement.sortableIndex !== index(multiDragElement)) { + update = true; + return; + } + }); + + if (update) { + dispatchSortableEvent('update'); + } + } + } // Must be done after capturing individual rects (scroll bar) + + + multiDragElements.forEach(function (multiDragElement) { + unsetRect(multiDragElement); + }); + toSortable.animateAll(); + } + + multiDragSortable = toSortable; + } // Remove clones if necessary + + + if (rootEl === parentEl || putSortable && putSortable.lastPutMode !== 'clone') { + multiDragClones.forEach(function (clone) { + clone.parentNode && clone.parentNode.removeChild(clone); + }); + } + }, + nullingGlobal: function nullingGlobal() { + this.isMultiDrag = dragStarted = false; + multiDragClones.length = 0; + }, + destroyGlobal: function destroyGlobal() { + this._deselectMultiDrag(); + + off(document, 'pointerup', this._deselectMultiDrag); + off(document, 'mouseup', this._deselectMultiDrag); + off(document, 'touchend', this._deselectMultiDrag); + off(document, 'keydown', this._checkKeyDown); + off(document, 'keyup', this._checkKeyUp); + }, + _deselectMultiDrag: function _deselectMultiDrag(evt) { + if (typeof dragStarted !== "undefined" && dragStarted) return; // Only deselect if selection is in this sortable + + if (multiDragSortable !== this.sortable) return; // Only deselect if target is not item in this sortable + + if (evt && closest(evt.target, this.options.draggable, this.sortable.el, false)) return; // Only deselect if left click + + if (evt && evt.button !== 0) return; + + while (multiDragElements.length) { + var el = multiDragElements[0]; + toggleClass(el, this.options.selectedClass, false); + multiDragElements.shift(); + dispatchEvent({ + sortable: this.sortable, + rootEl: this.sortable.el, + name: 'deselect', + targetEl: el, + originalEvt: evt + }); + } + }, + _checkKeyDown: function _checkKeyDown(evt) { + if (evt.key === this.options.multiDragKey) { + this.multiDragKeyDown = true; + } + }, + _checkKeyUp: function _checkKeyUp(evt) { + if (evt.key === this.options.multiDragKey) { + this.multiDragKeyDown = false; + } + } + }; + return _extends(MultiDrag, { + // Static methods & properties + pluginName: 'multiDrag', + utils: { + /** + * Selects the provided multi-drag item + * @param {HTMLElement} el The element to be selected + */ + select: function select(el) { + var sortable = el.parentNode[expando]; + if (!sortable || !sortable.options.multiDrag || ~multiDragElements.indexOf(el)) return; + + if (multiDragSortable && multiDragSortable !== sortable) { + multiDragSortable.multiDrag._deselectMultiDrag(); + + multiDragSortable = sortable; + } + + toggleClass(el, sortable.options.selectedClass, true); + multiDragElements.push(el); + }, + + /** + * Deselects the provided multi-drag item + * @param {HTMLElement} el The element to be deselected + */ + deselect: function deselect(el) { + var sortable = el.parentNode[expando], + index = multiDragElements.indexOf(el); + if (!sortable || !sortable.options.multiDrag || !~index) return; + toggleClass(el, sortable.options.selectedClass, false); + multiDragElements.splice(index, 1); + } + }, + eventProperties: function eventProperties() { + var _this3 = this; + + var oldIndicies = [], + newIndicies = []; + multiDragElements.forEach(function (multiDragElement) { + oldIndicies.push({ + multiDragElement: multiDragElement, + index: multiDragElement.sortableIndex + }); // multiDragElements will already be sorted if folding + + var newIndex; + + if (folding && multiDragElement !== dragEl$1) { + newIndex = -1; + } else if (folding) { + newIndex = index(multiDragElement, ':not(.' + _this3.options.selectedClass + ')'); + } else { + newIndex = index(multiDragElement); + } + + newIndicies.push({ + multiDragElement: multiDragElement, + index: newIndex + }); + }); + return { + items: _toConsumableArray(multiDragElements), + clones: [].concat(multiDragClones), + oldIndicies: oldIndicies, + newIndicies: newIndicies + }; + }, + optionListeners: { + multiDragKey: function multiDragKey(key) { + key = key.toLowerCase(); + + if (key === 'ctrl') { + key = 'Control'; + } else if (key.length > 1) { + key = key.charAt(0).toUpperCase() + key.substr(1); + } + + return key; + } + } + }); + } + + function insertMultiDragElements(clonesInserted, rootEl) { + multiDragElements.forEach(function (multiDragElement, i) { + var target = rootEl.children[multiDragElement.sortableIndex + (clonesInserted ? Number(i) : 0)]; + + if (target) { + rootEl.insertBefore(multiDragElement, target); + } else { + rootEl.appendChild(multiDragElement); + } + }); + } + /** + * Insert multi-drag clones + * @param {[Boolean]} elementsInserted Whether the multi-drag elements are inserted + * @param {HTMLElement} rootEl + */ + + + function insertMultiDragClones(elementsInserted, rootEl) { + multiDragClones.forEach(function (clone, i) { + var target = rootEl.children[clone.sortableIndex + (elementsInserted ? Number(i) : 0)]; + + if (target) { + rootEl.insertBefore(clone, target); + } else { + rootEl.appendChild(clone); + } + }); + } + + function removeMultiDragElements() { + multiDragElements.forEach(function (multiDragElement) { + if (multiDragElement === dragEl$1) return; + multiDragElement.parentNode && multiDragElement.parentNode.removeChild(multiDragElement); + }); + } + + Sortable.mount(new AutoScrollPlugin()); + Sortable.mount(Remove, Revert); + + Sortable.mount(new SwapPlugin()); + Sortable.mount(new MultiDragPlugin()); + + return Sortable; + +})); \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Content/vendors/vue/vue.js b/src/Presentation/SmartStore.Web/Content/vendors/vue/vue.js new file mode 100644 index 0000000000..ee2e80546d --- /dev/null +++ b/src/Presentation/SmartStore.Web/Content/vendors/vue/vue.js @@ -0,0 +1,11972 @@ +/*! + * Vue.js v2.6.11 + * (c) 2014-2019 Evan You + * Released under the MIT License. + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.Vue = factory()); +}(this, function () { + 'use strict'; + + /* */ + + var emptyObject = Object.freeze({}); + + // These helpers produce better VM code in JS engines due to their + // explicitness and function inlining. + function isUndef(v) { + return v === undefined || v === null + } + + function isDef(v) { + return v !== undefined && v !== null + } + + function isTrue(v) { + return v === true + } + + function isFalse(v) { + return v === false + } + + /** + * Check if value is primitive. + */ + function isPrimitive(value) { + return ( + typeof value === 'string' || + typeof value === 'number' || + // $flow-disable-line + typeof value === 'symbol' || + typeof value === 'boolean' + ) + } + + /** + * Quick object check - this is primarily used to tell + * Objects from primitive values when we know the value + * is a JSON-compliant type. + */ + function isObject(obj) { + return obj !== null && typeof obj === 'object' + } + + /** + * Get the raw type string of a value, e.g., [object Object]. + */ + var _toString = Object.prototype.toString; + + function toRawType(value) { + return _toString.call(value).slice(8, -1) + } + + /** + * Strict object type check. Only returns true + * for plain JavaScript objects. + */ + function isPlainObject(obj) { + return _toString.call(obj) === '[object Object]' + } + + function isRegExp(v) { + return _toString.call(v) === '[object RegExp]' + } + + /** + * Check if val is a valid array index. + */ + function isValidArrayIndex(val) { + var n = parseFloat(String(val)); + return n >= 0 && Math.floor(n) === n && isFinite(val) + } + + function isPromise(val) { + return ( + isDef(val) && + typeof val.then === 'function' && + typeof val.catch === 'function' + ) + } + + /** + * Convert a value to a string that is actually rendered. + */ + function toString(val) { + return val == null + ? '' + : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString) + ? JSON.stringify(val, null, 2) + : String(val) + } + + /** + * Convert an input value to a number for persistence. + * If the conversion fails, return original string. + */ + function toNumber(val) { + var n = parseFloat(val); + return isNaN(n) ? val : n + } + + /** + * Make a map and return a function for checking if a key + * is in that map. + */ + function makeMap( + str, + expectsLowerCase + ) { + var map = Object.create(null); + var list = str.split(','); + for (var i = 0; i < list.length; i++) { + map[list[i]] = true; + } + return expectsLowerCase + ? function (val) { return map[val.toLowerCase()]; } + : function (val) { return map[val]; } + } + + /** + * Check if a tag is a built-in tag. + */ + var isBuiltInTag = makeMap('slot,component', true); + + /** + * Check if an attribute is a reserved attribute. + */ + var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); + + /** + * Remove an item from an array. + */ + function remove(arr, item) { + if (arr.length) { + var index = arr.indexOf(item); + if (index > -1) { + return arr.splice(index, 1) + } + } + } + + /** + * Check whether an object has the property. + */ + var hasOwnProperty = Object.prototype.hasOwnProperty; + function hasOwn(obj, key) { + return hasOwnProperty.call(obj, key) + } + + /** + * Create a cached version of a pure function. + */ + function cached(fn) { + var cache = Object.create(null); + return (function cachedFn(str) { + var hit = cache[str]; + return hit || (cache[str] = fn(str)) + }) + } + + /** + * Camelize a hyphen-delimited string. + */ + var camelizeRE = /-(\w)/g; + var camelize = cached(function (str) { + return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; }) + }); + + /** + * Capitalize a string. + */ + var capitalize = cached(function (str) { + return str.charAt(0).toUpperCase() + str.slice(1) + }); + + /** + * Hyphenate a camelCase string. + */ + var hyphenateRE = /\B([A-Z])/g; + var hyphenate = cached(function (str) { + return str.replace(hyphenateRE, '-$1').toLowerCase() + }); + + /** + * Simple bind polyfill for environments that do not support it, + * e.g., PhantomJS 1.x. Technically, we don't need this anymore + * since native bind is now performant enough in most browsers. + * But removing it would mean breaking code that was able to run in + * PhantomJS 1.x, so this must be kept for backward compatibility. + */ + + /* istanbul ignore next */ + function polyfillBind(fn, ctx) { + function boundFn(a) { + var l = arguments.length; + return l + ? l > 1 + ? fn.apply(ctx, arguments) + : fn.call(ctx, a) + : fn.call(ctx) + } + + boundFn._length = fn.length; + return boundFn + } + + function nativeBind(fn, ctx) { + return fn.bind(ctx) + } + + var bind = Function.prototype.bind + ? nativeBind + : polyfillBind; + + /** + * Convert an Array-like object to a real Array. + */ + function toArray(list, start) { + start = start || 0; + var i = list.length - start; + var ret = new Array(i); + while (i--) { + ret[i] = list[i + start]; + } + return ret + } + + /** + * Mix properties into target object. + */ + function extend(to, _from) { + for (var key in _from) { + to[key] = _from[key]; + } + return to + } + + /** + * Merge an Array of Objects into a single Object. + */ + function toObject(arr) { + var res = {}; + for (var i = 0; i < arr.length; i++) { + if (arr[i]) { + extend(res, arr[i]); + } + } + return res + } + + /* eslint-disable no-unused-vars */ + + /** + * Perform no operation. + * Stubbing args to make Flow happy without leaving useless transpiled code + * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/). + */ + function noop(a, b, c) { } + + /** + * Always return false. + */ + var no = function (a, b, c) { return false; }; + + /* eslint-enable no-unused-vars */ + + /** + * Return the same value. + */ + var identity = function (_) { return _; }; + + /** + * Generate a string containing static keys from compiler modules. + */ + function genStaticKeys(modules) { + return modules.reduce(function (keys, m) { + return keys.concat(m.staticKeys || []) + }, []).join(',') + } + + /** + * Check if two values are loosely equal - that is, + * if they are plain objects, do they have the same shape? + */ + function looseEqual(a, b) { + if (a === b) { return true } + var isObjectA = isObject(a); + var isObjectB = isObject(b); + if (isObjectA && isObjectB) { + try { + var isArrayA = Array.isArray(a); + var isArrayB = Array.isArray(b); + if (isArrayA && isArrayB) { + return a.length === b.length && a.every(function (e, i) { + return looseEqual(e, b[i]) + }) + } else if (a instanceof Date && b instanceof Date) { + return a.getTime() === b.getTime() + } else if (!isArrayA && !isArrayB) { + var keysA = Object.keys(a); + var keysB = Object.keys(b); + return keysA.length === keysB.length && keysA.every(function (key) { + return looseEqual(a[key], b[key]) + }) + } else { + /* istanbul ignore next */ + return false + } + } catch (e) { + /* istanbul ignore next */ + return false + } + } else if (!isObjectA && !isObjectB) { + return String(a) === String(b) + } else { + return false + } + } + + /** + * Return the first index at which a loosely equal value can be + * found in the array (if value is a plain object, the array must + * contain an object of the same shape), or -1 if it is not present. + */ + function looseIndexOf(arr, val) { + for (var i = 0; i < arr.length; i++) { + if (looseEqual(arr[i], val)) { return i } + } + return -1 + } + + /** + * Ensure a function is called only once. + */ + function once(fn) { + var called = false; + return function () { + if (!called) { + called = true; + fn.apply(this, arguments); + } + } + } + + var SSR_ATTR = 'data-server-rendered'; + + var ASSET_TYPES = [ + 'component', + 'directive', + 'filter' + ]; + + var LIFECYCLE_HOOKS = [ + 'beforeCreate', + 'created', + 'beforeMount', + 'mounted', + 'beforeUpdate', + 'updated', + 'beforeDestroy', + 'destroyed', + 'activated', + 'deactivated', + 'errorCaptured', + 'serverPrefetch' + ]; + + /* */ + + + + var config = ({ + /** + * Option merge strategies (used in core/util/options) + */ + // $flow-disable-line + optionMergeStrategies: Object.create(null), + + /** + * Whether to suppress warnings. + */ + silent: false, + + /** + * Show production mode tip message on boot? + */ + productionTip: "development" !== 'production', + + /** + * Whether to enable devtools + */ + devtools: "development" !== 'production', + + /** + * Whether to record perf + */ + performance: false, + + /** + * Error handler for watcher errors + */ + errorHandler: null, + + /** + * Warn handler for watcher warns + */ + warnHandler: null, + + /** + * Ignore certain custom elements + */ + ignoredElements: [], + + /** + * Custom user key aliases for v-on + */ + // $flow-disable-line + keyCodes: Object.create(null), + + /** + * Check if a tag is reserved so that it cannot be registered as a + * component. This is platform-dependent and may be overwritten. + */ + isReservedTag: no, + + /** + * Check if an attribute is reserved so that it cannot be used as a component + * prop. This is platform-dependent and may be overwritten. + */ + isReservedAttr: no, + + /** + * Check if a tag is an unknown element. + * Platform-dependent. + */ + isUnknownElement: no, + + /** + * Get the namespace of an element + */ + getTagNamespace: noop, + + /** + * Parse the real tag name for the specific platform. + */ + parsePlatformTagName: identity, + + /** + * Check if an attribute must be bound using property, e.g. value + * Platform-dependent. + */ + mustUseProp: no, + + /** + * Perform updates asynchronously. Intended to be used by Vue Test Utils + * This will significantly reduce performance if set to false. + */ + async: true, + + /** + * Exposed for legacy reasons + */ + _lifecycleHooks: LIFECYCLE_HOOKS + }); + + /* */ + + /** + * unicode letters used for parsing html tags, component names and property paths. + * using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname + * skipping \u10000-\uEFFFF due to it freezing up PhantomJS + */ + var unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/; + + /** + * Check if a string starts with $ or _ + */ + function isReserved(str) { + var c = (str + '').charCodeAt(0); + return c === 0x24 || c === 0x5F + } + + /** + * Define a property. + */ + function def(obj, key, val, enumerable) { + Object.defineProperty(obj, key, { + value: val, + enumerable: !!enumerable, + writable: true, + configurable: true + }); + } + + /** + * Parse simple path. + */ + var bailRE = new RegExp(("[^" + (unicodeRegExp.source) + ".$_\\d]")); + function parsePath(path) { + if (bailRE.test(path)) { + return + } + var segments = path.split('.'); + return function (obj) { + for (var i = 0; i < segments.length; i++) { + if (!obj) { return } + obj = obj[segments[i]]; + } + return obj + } + } + + /* */ + + // can we use __proto__? + var hasProto = '__proto__' in {}; + + // Browser environment sniffing + var inBrowser = typeof window !== 'undefined'; + var inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform; + var weexPlatform = inWeex && WXEnvironment.platform.toLowerCase(); + var UA = inBrowser && window.navigator.userAgent.toLowerCase(); + var isIE = UA && /msie|trident/.test(UA); + var isIE9 = UA && UA.indexOf('msie 9.0') > 0; + var isEdge = UA && UA.indexOf('edge/') > 0; + var isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android'); + var isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios'); + var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge; + var isPhantomJS = UA && /phantomjs/.test(UA); + var isFF = UA && UA.match(/firefox\/(\d+)/); + + // Firefox has a "watch" function on Object.prototype... + var nativeWatch = ({}).watch; + + var supportsPassive = false; + if (inBrowser) { + try { + var opts = {}; + Object.defineProperty(opts, 'passive', ({ + get: function get() { + /* istanbul ignore next */ + supportsPassive = true; + } + })); // https://github.com/facebook/flow/issues/285 + window.addEventListener('test-passive', null, opts); + } catch (e) { } + } + + // this needs to be lazy-evaled because vue may be required before + // vue-server-renderer can set VUE_ENV + var _isServer; + var isServerRendering = function () { + if (_isServer === undefined) { + /* istanbul ignore if */ + if (!inBrowser && !inWeex && typeof global !== 'undefined') { + // detect presence of vue-server-renderer and avoid + // Webpack shimming the process + _isServer = global['process'] && global['process'].env.VUE_ENV === 'server'; + } else { + _isServer = false; + } + } + return _isServer + }; + + // detect devtools + var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; + + /* istanbul ignore next */ + function isNative(Ctor) { + return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) + } + + var hasSymbol = + typeof Symbol !== 'undefined' && isNative(Symbol) && + typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); + + var _Set; + /* istanbul ignore if */ // $flow-disable-line + if (typeof Set !== 'undefined' && isNative(Set)) { + // use native Set when available. + _Set = Set; + } else { + // a non-standard Set polyfill that only works with primitive keys. + _Set = /*@__PURE__*/(function () { + function Set() { + this.set = Object.create(null); + } + Set.prototype.has = function has(key) { + return this.set[key] === true + }; + Set.prototype.add = function add(key) { + this.set[key] = true; + }; + Set.prototype.clear = function clear() { + this.set = Object.create(null); + }; + + return Set; + }()); + } + + /* */ + + var warn = noop; + var tip = noop; + var generateComponentTrace = (noop); // work around flow check + var formatComponentName = (noop); + + { + var hasConsole = typeof console !== 'undefined'; + var classifyRE = /(?:^|[-_])(\w)/g; + var classify = function (str) { + return str + .replace(classifyRE, function (c) { return c.toUpperCase(); }) + .replace(/[-_]/g, ''); + }; + + warn = function (msg, vm) { + var trace = vm ? generateComponentTrace(vm) : ''; + + if (config.warnHandler) { + config.warnHandler.call(null, msg, vm, trace); + } else if (hasConsole && (!config.silent)) { + console.error(("[Vue warn]: " + msg + trace)); + } + }; + + tip = function (msg, vm) { + if (hasConsole && (!config.silent)) { + console.warn("[Vue tip]: " + msg + ( + vm ? generateComponentTrace(vm) : '' + )); + } + }; + + formatComponentName = function (vm, includeFile) { + if (vm.$root === vm) { + return '' + } + var options = typeof vm === 'function' && vm.cid != null + ? vm.options + : vm._isVue + ? vm.$options || vm.constructor.options + : vm; + var name = options.name || options._componentTag; + var file = options.__file; + if (!name && file) { + var match = file.match(/([^/\\]+)\.vue$/); + name = match && match[1]; + } + + return ( + (name ? ("<" + (classify(name)) + ">") : "") + + (file && includeFile !== false ? (" at " + file) : '') + ) + }; + + var repeat = function (str, n) { + var res = ''; + while (n) { + if (n % 2 === 1) { res += str; } + if (n > 1) { str += str; } + n >>= 1; + } + return res + }; + + generateComponentTrace = function (vm) { + if (vm._isVue && vm.$parent) { + var tree = []; + var currentRecursiveSequence = 0; + while (vm) { + if (tree.length > 0) { + var last = tree[tree.length - 1]; + if (last.constructor === vm.constructor) { + currentRecursiveSequence++; + vm = vm.$parent; + continue + } else if (currentRecursiveSequence > 0) { + tree[tree.length - 1] = [last, currentRecursiveSequence]; + currentRecursiveSequence = 0; + } + } + tree.push(vm); + vm = vm.$parent; + } + return '\n\nfound in\n\n' + tree + .map(function (vm, i) { + return ("" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm) + ? ((formatComponentName(vm[0])) + "... (" + (vm[1]) + " recursive calls)") + : formatComponentName(vm))); + }) + .join('\n') + } else { + return ("\n\n(found in " + (formatComponentName(vm)) + ")") + } + }; + } + + /* */ + + var uid = 0; + + /** + * A dep is an observable that can have multiple + * directives subscribing to it. + */ + var Dep = function Dep() { + this.id = uid++; + this.subs = []; + }; + + Dep.prototype.addSub = function addSub(sub) { + this.subs.push(sub); + }; + + Dep.prototype.removeSub = function removeSub(sub) { + remove(this.subs, sub); + }; + + Dep.prototype.depend = function depend() { + if (Dep.target) { + Dep.target.addDep(this); + } + }; + + Dep.prototype.notify = function notify() { + // stabilize the subscriber list first + var subs = this.subs.slice(); + if (!config.async) { + // subs aren't sorted in scheduler if not running async + // we need to sort them now to make sure they fire in correct + // order + subs.sort(function (a, b) { return a.id - b.id; }); + } + for (var i = 0, l = subs.length; i < l; i++) { + subs[i].update(); + } + }; + + // The current target watcher being evaluated. + // This is globally unique because only one watcher + // can be evaluated at a time. + Dep.target = null; + var targetStack = []; + + function pushTarget(target) { + targetStack.push(target); + Dep.target = target; + } + + function popTarget() { + targetStack.pop(); + Dep.target = targetStack[targetStack.length - 1]; + } + + /* */ + + var VNode = function VNode( + tag, + data, + children, + text, + elm, + context, + componentOptions, + asyncFactory + ) { + this.tag = tag; + this.data = data; + this.children = children; + this.text = text; + this.elm = elm; + this.ns = undefined; + this.context = context; + this.fnContext = undefined; + this.fnOptions = undefined; + this.fnScopeId = undefined; + this.key = data && data.key; + this.componentOptions = componentOptions; + this.componentInstance = undefined; + this.parent = undefined; + this.raw = false; + this.isStatic = false; + this.isRootInsert = true; + this.isComment = false; + this.isCloned = false; + this.isOnce = false; + this.asyncFactory = asyncFactory; + this.asyncMeta = undefined; + this.isAsyncPlaceholder = false; + }; + + var prototypeAccessors = { child: { configurable: true } }; + + // DEPRECATED: alias for componentInstance for backwards compat. + /* istanbul ignore next */ + prototypeAccessors.child.get = function () { + return this.componentInstance + }; + + Object.defineProperties(VNode.prototype, prototypeAccessors); + + var createEmptyVNode = function (text) { + if (text === void 0) text = ''; + + var node = new VNode(); + node.text = text; + node.isComment = true; + return node + }; + + function createTextVNode(val) { + return new VNode(undefined, undefined, undefined, String(val)) + } + + // optimized shallow clone + // used for static nodes and slot nodes because they may be reused across + // multiple renders, cloning them avoids errors when DOM manipulations rely + // on their elm reference. + function cloneVNode(vnode) { + var cloned = new VNode( + vnode.tag, + vnode.data, + // #7975 + // clone children array to avoid mutating original in case of cloning + // a child. + vnode.children && vnode.children.slice(), + vnode.text, + vnode.elm, + vnode.context, + vnode.componentOptions, + vnode.asyncFactory + ); + cloned.ns = vnode.ns; + cloned.isStatic = vnode.isStatic; + cloned.key = vnode.key; + cloned.isComment = vnode.isComment; + cloned.fnContext = vnode.fnContext; + cloned.fnOptions = vnode.fnOptions; + cloned.fnScopeId = vnode.fnScopeId; + cloned.asyncMeta = vnode.asyncMeta; + cloned.isCloned = true; + return cloned + } + + /* + * not type checking this file because flow doesn't play well with + * dynamically accessing methods on Array prototype + */ + + var arrayProto = Array.prototype; + var arrayMethods = Object.create(arrayProto); + + var methodsToPatch = [ + 'push', + 'pop', + 'shift', + 'unshift', + 'splice', + 'sort', + 'reverse' + ]; + + /** + * Intercept mutating methods and emit events + */ + methodsToPatch.forEach(function (method) { + // cache original method + var original = arrayProto[method]; + def(arrayMethods, method, function mutator() { + var args = [], len = arguments.length; + while (len--) args[len] = arguments[len]; + + var result = original.apply(this, args); + var ob = this.__ob__; + var inserted; + switch (method) { + case 'push': + case 'unshift': + inserted = args; + break + case 'splice': + inserted = args.slice(2); + break + } + if (inserted) { ob.observeArray(inserted); } + // notify change + ob.dep.notify(); + return result + }); + }); + + /* */ + + var arrayKeys = Object.getOwnPropertyNames(arrayMethods); + + /** + * In some cases we may want to disable observation inside a component's + * update computation. + */ + var shouldObserve = true; + + function toggleObserving(value) { + shouldObserve = value; + } + + /** + * Observer class that is attached to each observed + * object. Once attached, the observer converts the target + * object's property keys into getter/setters that + * collect dependencies and dispatch updates. + */ + var Observer = function Observer(value) { + this.value = value; + this.dep = new Dep(); + this.vmCount = 0; + def(value, '__ob__', this); + if (Array.isArray(value)) { + if (hasProto) { + protoAugment(value, arrayMethods); + } else { + copyAugment(value, arrayMethods, arrayKeys); + } + this.observeArray(value); + } else { + this.walk(value); + } + }; + + /** + * Walk through all properties and convert them into + * getter/setters. This method should only be called when + * value type is Object. + */ + Observer.prototype.walk = function walk(obj) { + var keys = Object.keys(obj); + for (var i = 0; i < keys.length; i++) { + defineReactive$$1(obj, keys[i]); + } + }; + + /** + * Observe a list of Array items. + */ + Observer.prototype.observeArray = function observeArray(items) { + for (var i = 0, l = items.length; i < l; i++) { + observe(items[i]); + } + }; + + // helpers + + /** + * Augment a target Object or Array by intercepting + * the prototype chain using __proto__ + */ + function protoAugment(target, src) { + /* eslint-disable no-proto */ + target.__proto__ = src; + /* eslint-enable no-proto */ + } + + /** + * Augment a target Object or Array by defining + * hidden properties. + */ + /* istanbul ignore next */ + function copyAugment(target, src, keys) { + for (var i = 0, l = keys.length; i < l; i++) { + var key = keys[i]; + def(target, key, src[key]); + } + } + + /** + * Attempt to create an observer instance for a value, + * returns the new observer if successfully observed, + * or the existing observer if the value already has one. + */ + function observe(value, asRootData) { + if (!isObject(value) || value instanceof VNode) { + return + } + var ob; + if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { + ob = value.__ob__; + } else if ( + shouldObserve && + !isServerRendering() && + (Array.isArray(value) || isPlainObject(value)) && + Object.isExtensible(value) && + !value._isVue + ) { + ob = new Observer(value); + } + if (asRootData && ob) { + ob.vmCount++; + } + return ob + } + + /** + * Define a reactive property on an Object. + */ + function defineReactive$$1( + obj, + key, + val, + customSetter, + shallow + ) { + var dep = new Dep(); + + var property = Object.getOwnPropertyDescriptor(obj, key); + if (property && property.configurable === false) { + return + } + + // cater for pre-defined getter/setters + var getter = property && property.get; + var setter = property && property.set; + if ((!getter || setter) && arguments.length === 2) { + val = obj[key]; + } + + var childOb = !shallow && observe(val); + Object.defineProperty(obj, key, { + enumerable: true, + configurable: true, + get: function reactiveGetter() { + var value = getter ? getter.call(obj) : val; + if (Dep.target) { + dep.depend(); + if (childOb) { + childOb.dep.depend(); + if (Array.isArray(value)) { + dependArray(value); + } + } + } + return value + }, + set: function reactiveSetter(newVal) { + var value = getter ? getter.call(obj) : val; + /* eslint-disable no-self-compare */ + if (newVal === value || (newVal !== newVal && value !== value)) { + return + } + /* eslint-enable no-self-compare */ + if (customSetter) { + customSetter(); + } + // #7981: for accessor properties without setter + if (getter && !setter) { return } + if (setter) { + setter.call(obj, newVal); + } else { + val = newVal; + } + childOb = !shallow && observe(newVal); + dep.notify(); + } + }); + } + + /** + * Set a property on an object. Adds the new property and + * triggers change notification if the property doesn't + * already exist. + */ + function set(target, key, val) { + if (isUndef(target) || isPrimitive(target) + ) { + warn(("Cannot set reactive property on undefined, null, or primitive value: " + ((target)))); + } + if (Array.isArray(target) && isValidArrayIndex(key)) { + target.length = Math.max(target.length, key); + target.splice(key, 1, val); + return val + } + if (key in target && !(key in Object.prototype)) { + target[key] = val; + return val + } + var ob = (target).__ob__; + if (target._isVue || (ob && ob.vmCount)) { + warn( + 'Avoid adding reactive properties to a Vue instance or its root $data ' + + 'at runtime - declare it upfront in the data option.' + ); + return val + } + if (!ob) { + target[key] = val; + return val + } + defineReactive$$1(ob.value, key, val); + ob.dep.notify(); + return val + } + + /** + * Delete a property and trigger change if necessary. + */ + function del(target, key) { + if (isUndef(target) || isPrimitive(target) + ) { + warn(("Cannot delete reactive property on undefined, null, or primitive value: " + ((target)))); + } + if (Array.isArray(target) && isValidArrayIndex(key)) { + target.splice(key, 1); + return + } + var ob = (target).__ob__; + if (target._isVue || (ob && ob.vmCount)) { + warn( + 'Avoid deleting properties on a Vue instance or its root $data ' + + '- just set it to null.' + ); + return + } + if (!hasOwn(target, key)) { + return + } + delete target[key]; + if (!ob) { + return + } + ob.dep.notify(); + } + + /** + * Collect dependencies on array elements when the array is touched, since + * we cannot intercept array element access like property getters. + */ + function dependArray(value) { + for (var e = (void 0), i = 0, l = value.length; i < l; i++) { + e = value[i]; + e && e.__ob__ && e.__ob__.dep.depend(); + if (Array.isArray(e)) { + dependArray(e); + } + } + } + + /* */ + + /** + * Option overwriting strategies are functions that handle + * how to merge a parent option value and a child option + * value into the final value. + */ + var strats = config.optionMergeStrategies; + + /** + * Options with restrictions + */ + { + strats.el = strats.propsData = function (parent, child, vm, key) { + if (!vm) { + warn( + "option \"" + key + "\" can only be used during instance " + + 'creation with the `new` keyword.' + ); + } + return defaultStrat(parent, child) + }; + } + + /** + * Helper that recursively merges two data objects together. + */ + function mergeData(to, from) { + if (!from) { return to } + var key, toVal, fromVal; + + var keys = hasSymbol + ? Reflect.ownKeys(from) + : Object.keys(from); + + for (var i = 0; i < keys.length; i++) { + key = keys[i]; + // in case the object is already observed... + if (key === '__ob__') { continue } + toVal = to[key]; + fromVal = from[key]; + if (!hasOwn(to, key)) { + set(to, key, fromVal); + } else if ( + toVal !== fromVal && + isPlainObject(toVal) && + isPlainObject(fromVal) + ) { + mergeData(toVal, fromVal); + } + } + return to + } + + /** + * Data + */ + function mergeDataOrFn( + parentVal, + childVal, + vm + ) { + if (!vm) { + // in a Vue.extend merge, both should be functions + if (!childVal) { + return parentVal + } + if (!parentVal) { + return childVal + } + // when parentVal & childVal are both present, + // we need to return a function that returns the + // merged result of both functions... no need to + // check if parentVal is a function here because + // it has to be a function to pass previous merges. + return function mergedDataFn() { + return mergeData( + typeof childVal === 'function' ? childVal.call(this, this) : childVal, + typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal + ) + } + } else { + return function mergedInstanceDataFn() { + // instance merge + var instanceData = typeof childVal === 'function' + ? childVal.call(vm, vm) + : childVal; + var defaultData = typeof parentVal === 'function' + ? parentVal.call(vm, vm) + : parentVal; + if (instanceData) { + return mergeData(instanceData, defaultData) + } else { + return defaultData + } + } + } + } + + strats.data = function ( + parentVal, + childVal, + vm + ) { + if (!vm) { + if (childVal && typeof childVal !== 'function') { + warn( + 'The "data" option should be a function ' + + 'that returns a per-instance value in component ' + + 'definitions.', + vm + ); + + return parentVal + } + return mergeDataOrFn(parentVal, childVal) + } + + return mergeDataOrFn(parentVal, childVal, vm) + }; + + /** + * Hooks and props are merged as arrays. + */ + function mergeHook( + parentVal, + childVal + ) { + var res = childVal + ? parentVal + ? parentVal.concat(childVal) + : Array.isArray(childVal) + ? childVal + : [childVal] + : parentVal; + return res + ? dedupeHooks(res) + : res + } + + function dedupeHooks(hooks) { + var res = []; + for (var i = 0; i < hooks.length; i++) { + if (res.indexOf(hooks[i]) === -1) { + res.push(hooks[i]); + } + } + return res + } + + LIFECYCLE_HOOKS.forEach(function (hook) { + strats[hook] = mergeHook; + }); + + /** + * Assets + * + * When a vm is present (instance creation), we need to do + * a three-way merge between constructor options, instance + * options and parent options. + */ + function mergeAssets( + parentVal, + childVal, + vm, + key + ) { + var res = Object.create(parentVal || null); + if (childVal) { + assertObjectType(key, childVal, vm); + return extend(res, childVal) + } else { + return res + } + } + + ASSET_TYPES.forEach(function (type) { + strats[type + 's'] = mergeAssets; + }); + + /** + * Watchers. + * + * Watchers hashes should not overwrite one + * another, so we merge them as arrays. + */ + strats.watch = function ( + parentVal, + childVal, + vm, + key + ) { + // work around Firefox's Object.prototype.watch... + if (parentVal === nativeWatch) { parentVal = undefined; } + if (childVal === nativeWatch) { childVal = undefined; } + /* istanbul ignore if */ + if (!childVal) { return Object.create(parentVal || null) } + { + assertObjectType(key, childVal, vm); + } + if (!parentVal) { return childVal } + var ret = {}; + extend(ret, parentVal); + for (var key$1 in childVal) { + var parent = ret[key$1]; + var child = childVal[key$1]; + if (parent && !Array.isArray(parent)) { + parent = [parent]; + } + ret[key$1] = parent + ? parent.concat(child) + : Array.isArray(child) ? child : [child]; + } + return ret + }; + + /** + * Other object hashes. + */ + strats.props = + strats.methods = + strats.inject = + strats.computed = function ( + parentVal, + childVal, + vm, + key + ) { + if (childVal && "development" !== 'production') { + assertObjectType(key, childVal, vm); + } + if (!parentVal) { return childVal } + var ret = Object.create(null); + extend(ret, parentVal); + if (childVal) { extend(ret, childVal); } + return ret + }; + strats.provide = mergeDataOrFn; + + /** + * Default strategy. + */ + var defaultStrat = function (parentVal, childVal) { + return childVal === undefined + ? parentVal + : childVal + }; + + /** + * Validate component names + */ + function checkComponents(options) { + for (var key in options.components) { + validateComponentName(key); + } + } + + function validateComponentName(name) { + if (!new RegExp(("^[a-zA-Z][\\-\\.0-9_" + (unicodeRegExp.source) + "]*$")).test(name)) { + warn( + 'Invalid component name: "' + name + '". Component names ' + + 'should conform to valid custom element name in html5 specification.' + ); + } + if (isBuiltInTag(name) || config.isReservedTag(name)) { + warn( + 'Do not use built-in or reserved HTML elements as component ' + + 'id: ' + name + ); + } + } + + /** + * Ensure all props option syntax are normalized into the + * Object-based format. + */ + function normalizeProps(options, vm) { + var props = options.props; + if (!props) { return } + var res = {}; + var i, val, name; + if (Array.isArray(props)) { + i = props.length; + while (i--) { + val = props[i]; + if (typeof val === 'string') { + name = camelize(val); + res[name] = { type: null }; + } else { + warn('props must be strings when using array syntax.'); + } + } + } else if (isPlainObject(props)) { + for (var key in props) { + val = props[key]; + name = camelize(key); + res[name] = isPlainObject(val) + ? val + : { type: val }; + } + } else { + warn( + "Invalid value for option \"props\": expected an Array or an Object, " + + "but got " + (toRawType(props)) + ".", + vm + ); + } + options.props = res; + } + + /** + * Normalize all injections into Object-based format + */ + function normalizeInject(options, vm) { + var inject = options.inject; + if (!inject) { return } + var normalized = options.inject = {}; + if (Array.isArray(inject)) { + for (var i = 0; i < inject.length; i++) { + normalized[inject[i]] = { from: inject[i] }; + } + } else if (isPlainObject(inject)) { + for (var key in inject) { + var val = inject[key]; + normalized[key] = isPlainObject(val) + ? extend({ from: key }, val) + : { from: val }; + } + } else { + warn( + "Invalid value for option \"inject\": expected an Array or an Object, " + + "but got " + (toRawType(inject)) + ".", + vm + ); + } + } + + /** + * Normalize raw function directives into object format. + */ + function normalizeDirectives(options) { + var dirs = options.directives; + if (dirs) { + for (var key in dirs) { + var def$$1 = dirs[key]; + if (typeof def$$1 === 'function') { + dirs[key] = { bind: def$$1, update: def$$1 }; + } + } + } + } + + function assertObjectType(name, value, vm) { + if (!isPlainObject(value)) { + warn( + "Invalid value for option \"" + name + "\": expected an Object, " + + "but got " + (toRawType(value)) + ".", + vm + ); + } + } + + /** + * Merge two option objects into a new one. + * Core utility used in both instantiation and inheritance. + */ + function mergeOptions( + parent, + child, + vm + ) { + { + checkComponents(child); + } + + if (typeof child === 'function') { + child = child.options; + } + + normalizeProps(child, vm); + normalizeInject(child, vm); + normalizeDirectives(child); + + // Apply extends and mixins on the child options, + // but only if it is a raw options object that isn't + // the result of another mergeOptions call. + // Only merged options has the _base property. + if (!child._base) { + if (child.extends) { + parent = mergeOptions(parent, child.extends, vm); + } + if (child.mixins) { + for (var i = 0, l = child.mixins.length; i < l; i++) { + parent = mergeOptions(parent, child.mixins[i], vm); + } + } + } + + var options = {}; + var key; + for (key in parent) { + mergeField(key); + } + for (key in child) { + if (!hasOwn(parent, key)) { + mergeField(key); + } + } + function mergeField(key) { + var strat = strats[key] || defaultStrat; + options[key] = strat(parent[key], child[key], vm, key); + } + return options + } + + /** + * Resolve an asset. + * This function is used because child instances need access + * to assets defined in its ancestor chain. + */ + function resolveAsset( + options, + type, + id, + warnMissing + ) { + /* istanbul ignore if */ + if (typeof id !== 'string') { + return + } + var assets = options[type]; + // check local registration variations first + if (hasOwn(assets, id)) { return assets[id] } + var camelizedId = camelize(id); + if (hasOwn(assets, camelizedId)) { return assets[camelizedId] } + var PascalCaseId = capitalize(camelizedId); + if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] } + // fallback to prototype chain + var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; + if (warnMissing && !res) { + warn( + 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, + options + ); + } + return res + } + + /* */ + + + + function validateProp( + key, + propOptions, + propsData, + vm + ) { + var prop = propOptions[key]; + var absent = !hasOwn(propsData, key); + var value = propsData[key]; + // boolean casting + var booleanIndex = getTypeIndex(Boolean, prop.type); + if (booleanIndex > -1) { + if (absent && !hasOwn(prop, 'default')) { + value = false; + } else if (value === '' || value === hyphenate(key)) { + // only cast empty string / same name to boolean if + // boolean has higher priority + var stringIndex = getTypeIndex(String, prop.type); + if (stringIndex < 0 || booleanIndex < stringIndex) { + value = true; + } + } + } + // check default value + if (value === undefined) { + value = getPropDefaultValue(vm, prop, key); + // since the default value is a fresh copy, + // make sure to observe it. + var prevShouldObserve = shouldObserve; + toggleObserving(true); + observe(value); + toggleObserving(prevShouldObserve); + } + { + assertProp(prop, key, value, vm, absent); + } + return value + } + + /** + * Get the default value of a prop. + */ + function getPropDefaultValue(vm, prop, key) { + // no default, return undefined + if (!hasOwn(prop, 'default')) { + return undefined + } + var def = prop.default; + // warn against non-factory defaults for Object & Array + if (isObject(def)) { + warn( + 'Invalid default value for prop "' + key + '": ' + + 'Props with type Object/Array must use a factory function ' + + 'to return the default value.', + vm + ); + } + // the raw prop value was also undefined from previous render, + // return previous default value to avoid unnecessary watcher trigger + if (vm && vm.$options.propsData && + vm.$options.propsData[key] === undefined && + vm._props[key] !== undefined + ) { + return vm._props[key] + } + // call factory function for non-Function types + // a value is Function if its prototype is function even across different execution context + return typeof def === 'function' && getType(prop.type) !== 'Function' + ? def.call(vm) + : def + } + + /** + * Assert whether a prop is valid. + */ + function assertProp( + prop, + name, + value, + vm, + absent + ) { + if (prop.required && absent) { + warn( + 'Missing required prop: "' + name + '"', + vm + ); + return + } + if (value == null && !prop.required) { + return + } + var type = prop.type; + var valid = !type || type === true; + var expectedTypes = []; + if (type) { + if (!Array.isArray(type)) { + type = [type]; + } + for (var i = 0; i < type.length && !valid; i++) { + var assertedType = assertType(value, type[i]); + expectedTypes.push(assertedType.expectedType || ''); + valid = assertedType.valid; + } + } + + if (!valid) { + warn( + getInvalidTypeMessage(name, value, expectedTypes), + vm + ); + return + } + var validator = prop.validator; + if (validator) { + if (!validator(value)) { + warn( + 'Invalid prop: custom validator check failed for prop "' + name + '".', + vm + ); + } + } + } + + var simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/; + + function assertType(value, type) { + var valid; + var expectedType = getType(type); + if (simpleCheckRE.test(expectedType)) { + var t = typeof value; + valid = t === expectedType.toLowerCase(); + // for primitive wrapper objects + if (!valid && t === 'object') { + valid = value instanceof type; + } + } else if (expectedType === 'Object') { + valid = isPlainObject(value); + } else if (expectedType === 'Array') { + valid = Array.isArray(value); + } else { + valid = value instanceof type; + } + return { + valid: valid, + expectedType: expectedType + } + } + + /** + * Use function string name to check built-in types, + * because a simple equality check will fail when running + * across different vms / iframes. + */ + function getType(fn) { + var match = fn && fn.toString().match(/^\s*function (\w+)/); + return match ? match[1] : '' + } + + function isSameType(a, b) { + return getType(a) === getType(b) + } + + function getTypeIndex(type, expectedTypes) { + if (!Array.isArray(expectedTypes)) { + return isSameType(expectedTypes, type) ? 0 : -1 + } + for (var i = 0, len = expectedTypes.length; i < len; i++) { + if (isSameType(expectedTypes[i], type)) { + return i + } + } + return -1 + } + + function getInvalidTypeMessage(name, value, expectedTypes) { + var message = "Invalid prop: type check failed for prop \"" + name + "\"." + + " Expected " + (expectedTypes.map(capitalize).join(', ')); + var expectedType = expectedTypes[0]; + var receivedType = toRawType(value); + var expectedValue = styleValue(value, expectedType); + var receivedValue = styleValue(value, receivedType); + // check if we need to specify expected value + if (expectedTypes.length === 1 && + isExplicable(expectedType) && + !isBoolean(expectedType, receivedType)) { + message += " with value " + expectedValue; + } + message += ", got " + receivedType + " "; + // check if we need to specify received value + if (isExplicable(receivedType)) { + message += "with value " + receivedValue + "."; + } + return message + } + + function styleValue(value, type) { + if (type === 'String') { + return ("\"" + value + "\"") + } else if (type === 'Number') { + return ("" + (Number(value))) + } else { + return ("" + value) + } + } + + function isExplicable(value) { + var explicitTypes = ['string', 'number', 'boolean']; + return explicitTypes.some(function (elem) { return value.toLowerCase() === elem; }) + } + + function isBoolean() { + var args = [], len = arguments.length; + while (len--) args[len] = arguments[len]; + + return args.some(function (elem) { return elem.toLowerCase() === 'boolean'; }) + } + + /* */ + + function handleError(err, vm, info) { + // Deactivate deps tracking while processing error handler to avoid possible infinite rendering. + // See: https://github.com/vuejs/vuex/issues/1505 + pushTarget(); + try { + if (vm) { + var cur = vm; + while ((cur = cur.$parent)) { + var hooks = cur.$options.errorCaptured; + if (hooks) { + for (var i = 0; i < hooks.length; i++) { + try { + var capture = hooks[i].call(cur, err, vm, info) === false; + if (capture) { return } + } catch (e) { + globalHandleError(e, cur, 'errorCaptured hook'); + } + } + } + } + } + globalHandleError(err, vm, info); + } finally { + popTarget(); + } + } + + function invokeWithErrorHandling( + handler, + context, + args, + vm, + info + ) { + var res; + try { + res = args ? handler.apply(context, args) : handler.call(context); + if (res && !res._isVue && isPromise(res) && !res._handled) { + res.catch(function (e) { return handleError(e, vm, info + " (Promise/async)"); }); + // issue #9511 + // avoid catch triggering multiple times when nested calls + res._handled = true; + } + } catch (e) { + handleError(e, vm, info); + } + return res + } + + function globalHandleError(err, vm, info) { + if (config.errorHandler) { + try { + return config.errorHandler.call(null, err, vm, info) + } catch (e) { + // if the user intentionally throws the original error in the handler, + // do not log it twice + if (e !== err) { + logError(e, null, 'config.errorHandler'); + } + } + } + logError(err, vm, info); + } + + function logError(err, vm, info) { + { + warn(("Error in " + info + ": \"" + (err.toString()) + "\""), vm); + } + /* istanbul ignore else */ + if ((inBrowser || inWeex) && typeof console !== 'undefined') { + console.error(err); + } else { + throw err + } + } + + /* */ + + var isUsingMicroTask = false; + + var callbacks = []; + var pending = false; + + function flushCallbacks() { + pending = false; + var copies = callbacks.slice(0); + callbacks.length = 0; + for (var i = 0; i < copies.length; i++) { + copies[i](); + } + } + + // Here we have async deferring wrappers using microtasks. + // In 2.5 we used (macro) tasks (in combination with microtasks). + // However, it has subtle problems when state is changed right before repaint + // (e.g. #6813, out-in transitions). + // Also, using (macro) tasks in event handler would cause some weird behaviors + // that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109). + // So we now use microtasks everywhere, again. + // A major drawback of this tradeoff is that there are some scenarios + // where microtasks have too high a priority and fire in between supposedly + // sequential events (e.g. #4521, #6690, which have workarounds) + // or even between bubbling of the same event (#6566). + var timerFunc; + + // The nextTick behavior leverages the microtask queue, which can be accessed + // via either native Promise.then or MutationObserver. + // MutationObserver has wider support, however it is seriously bugged in + // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It + // completely stops working after triggering a few times... so, if native + // Promise is available, we will use it: + /* istanbul ignore next, $flow-disable-line */ + if (typeof Promise !== 'undefined' && isNative(Promise)) { + var p = Promise.resolve(); + timerFunc = function () { + p.then(flushCallbacks); + // In problematic UIWebViews, Promise.then doesn't completely break, but + // it can get stuck in a weird state where callbacks are pushed into the + // microtask queue but the queue isn't being flushed, until the browser + // needs to do some other work, e.g. handle a timer. Therefore we can + // "force" the microtask queue to be flushed by adding an empty timer. + if (isIOS) { setTimeout(noop); } + }; + isUsingMicroTask = true; + } else if (!isIE && typeof MutationObserver !== 'undefined' && ( + isNative(MutationObserver) || + // PhantomJS and iOS 7.x + MutationObserver.toString() === '[object MutationObserverConstructor]' + )) { + // Use MutationObserver where native Promise is not available, + // e.g. PhantomJS, iOS7, Android 4.4 + // (#6466 MutationObserver is unreliable in IE11) + var counter = 1; + var observer = new MutationObserver(flushCallbacks); + var textNode = document.createTextNode(String(counter)); + observer.observe(textNode, { + characterData: true + }); + timerFunc = function () { + counter = (counter + 1) % 2; + textNode.data = String(counter); + }; + isUsingMicroTask = true; + } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { + // Fallback to setImmediate. + // Technically it leverages the (macro) task queue, + // but it is still a better choice than setTimeout. + timerFunc = function () { + setImmediate(flushCallbacks); + }; + } else { + // Fallback to setTimeout. + timerFunc = function () { + setTimeout(flushCallbacks, 0); + }; + } + + function nextTick(cb, ctx) { + var _resolve; + callbacks.push(function () { + if (cb) { + try { + cb.call(ctx); + } catch (e) { + handleError(e, ctx, 'nextTick'); + } + } else if (_resolve) { + _resolve(ctx); + } + }); + if (!pending) { + pending = true; + timerFunc(); + } + // $flow-disable-line + if (!cb && typeof Promise !== 'undefined') { + return new Promise(function (resolve) { + _resolve = resolve; + }) + } + } + + /* */ + + var mark; + var measure; + + { + var perf = inBrowser && window.performance; + /* istanbul ignore if */ + if ( + perf && + perf.mark && + perf.measure && + perf.clearMarks && + perf.clearMeasures + ) { + mark = function (tag) { return perf.mark(tag); }; + measure = function (name, startTag, endTag) { + perf.measure(name, startTag, endTag); + perf.clearMarks(startTag); + perf.clearMarks(endTag); + // perf.clearMeasures(name) + }; + } + } + + /* not type checking this file because flow doesn't play well with Proxy */ + + var initProxy; + + { + var allowedGlobals = makeMap( + 'Infinity,undefined,NaN,isFinite,isNaN,' + + 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + + 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' + + 'require' // for Webpack/Browserify + ); + + var warnNonPresent = function (target, key) { + warn( + "Property or method \"" + key + "\" is not defined on the instance but " + + 'referenced during render. Make sure that this property is reactive, ' + + 'either in the data option, or for class-based components, by ' + + 'initializing the property. ' + + 'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', + target + ); + }; + + var warnReservedPrefix = function (target, key) { + warn( + "Property \"" + key + "\" must be accessed with \"$data." + key + "\" because " + + 'properties starting with "$" or "_" are not proxied in the Vue instance to ' + + 'prevent conflicts with Vue internals. ' + + 'See: https://vuejs.org/v2/api/#data', + target + ); + }; + + var hasProxy = + typeof Proxy !== 'undefined' && isNative(Proxy); + + if (hasProxy) { + var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); + config.keyCodes = new Proxy(config.keyCodes, { + set: function set(target, key, value) { + if (isBuiltInModifier(key)) { + warn(("Avoid overwriting built-in modifier in config.keyCodes: ." + key)); + return false + } else { + target[key] = value; + return true + } + } + }); + } + + var hasHandler = { + has: function has(target, key) { + var has = key in target; + var isAllowed = allowedGlobals(key) || + (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data)); + if (!has && !isAllowed) { + if (key in target.$data) { warnReservedPrefix(target, key); } + else { warnNonPresent(target, key); } + } + return has || !isAllowed + } + }; + + var getHandler = { + get: function get(target, key) { + if (typeof key === 'string' && !(key in target)) { + if (key in target.$data) { warnReservedPrefix(target, key); } + else { warnNonPresent(target, key); } + } + return target[key] + } + }; + + initProxy = function initProxy(vm) { + if (hasProxy) { + // determine which proxy handler to use + var options = vm.$options; + var handlers = options.render && options.render._withStripped + ? getHandler + : hasHandler; + vm._renderProxy = new Proxy(vm, handlers); + } else { + vm._renderProxy = vm; + } + }; + } + + /* */ + + var seenObjects = new _Set(); + + /** + * Recursively traverse an object to evoke all converted + * getters, so that every nested property inside the object + * is collected as a "deep" dependency. + */ + function traverse(val) { + _traverse(val, seenObjects); + seenObjects.clear(); + } + + function _traverse(val, seen) { + var i, keys; + var isA = Array.isArray(val); + if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) { + return + } + if (val.__ob__) { + var depId = val.__ob__.dep.id; + if (seen.has(depId)) { + return + } + seen.add(depId); + } + if (isA) { + i = val.length; + while (i--) { _traverse(val[i], seen); } + } else { + keys = Object.keys(val); + i = keys.length; + while (i--) { _traverse(val[keys[i]], seen); } + } + } + + /* */ + + var normalizeEvent = cached(function (name) { + var passive = name.charAt(0) === '&'; + name = passive ? name.slice(1) : name; + var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first + name = once$$1 ? name.slice(1) : name; + var capture = name.charAt(0) === '!'; + name = capture ? name.slice(1) : name; + return { + name: name, + once: once$$1, + capture: capture, + passive: passive + } + }); + + function createFnInvoker(fns, vm) { + function invoker() { + var arguments$1 = arguments; + + var fns = invoker.fns; + if (Array.isArray(fns)) { + var cloned = fns.slice(); + for (var i = 0; i < cloned.length; i++) { + invokeWithErrorHandling(cloned[i], null, arguments$1, vm, "v-on handler"); + } + } else { + // return handler return value for single handlers + return invokeWithErrorHandling(fns, null, arguments, vm, "v-on handler") + } + } + invoker.fns = fns; + return invoker + } + + function updateListeners( + on, + oldOn, + add, + remove$$1, + createOnceHandler, + vm + ) { + var name, def$$1, cur, old, event; + for (name in on) { + def$$1 = cur = on[name]; + old = oldOn[name]; + event = normalizeEvent(name); + if (isUndef(cur)) { + warn( + "Invalid handler for event \"" + (event.name) + "\": got " + String(cur), + vm + ); + } else if (isUndef(old)) { + if (isUndef(cur.fns)) { + cur = on[name] = createFnInvoker(cur, vm); + } + if (isTrue(event.once)) { + cur = on[name] = createOnceHandler(event.name, cur, event.capture); + } + add(event.name, cur, event.capture, event.passive, event.params); + } else if (cur !== old) { + old.fns = cur; + on[name] = old; + } + } + for (name in oldOn) { + if (isUndef(on[name])) { + event = normalizeEvent(name); + remove$$1(event.name, oldOn[name], event.capture); + } + } + } + + /* */ + + function mergeVNodeHook(def, hookKey, hook) { + if (def instanceof VNode) { + def = def.data.hook || (def.data.hook = {}); + } + var invoker; + var oldHook = def[hookKey]; + + function wrappedHook() { + hook.apply(this, arguments); + // important: remove merged hook to ensure it's called only once + // and prevent memory leak + remove(invoker.fns, wrappedHook); + } + + if (isUndef(oldHook)) { + // no existing hook + invoker = createFnInvoker([wrappedHook]); + } else { + /* istanbul ignore if */ + if (isDef(oldHook.fns) && isTrue(oldHook.merged)) { + // already a merged invoker + invoker = oldHook; + invoker.fns.push(wrappedHook); + } else { + // existing plain hook + invoker = createFnInvoker([oldHook, wrappedHook]); + } + } + + invoker.merged = true; + def[hookKey] = invoker; + } + + /* */ + + function extractPropsFromVNodeData( + data, + Ctor, + tag + ) { + // we are only extracting raw values here. + // validation and default values are handled in the child + // component itself. + var propOptions = Ctor.options.props; + if (isUndef(propOptions)) { + return + } + var res = {}; + var attrs = data.attrs; + var props = data.props; + if (isDef(attrs) || isDef(props)) { + for (var key in propOptions) { + var altKey = hyphenate(key); + { + var keyInLowerCase = key.toLowerCase(); + if ( + key !== keyInLowerCase && + attrs && hasOwn(attrs, keyInLowerCase) + ) { + tip( + "Prop \"" + keyInLowerCase + "\" is passed to component " + + (formatComponentName(tag || Ctor)) + ", but the declared prop name is" + + " \"" + key + "\". " + + "Note that HTML attributes are case-insensitive and camelCased " + + "props need to use their kebab-case equivalents when using in-DOM " + + "templates. You should probably use \"" + altKey + "\" instead of \"" + key + "\"." + ); + } + } + checkProp(res, props, key, altKey, true) || + checkProp(res, attrs, key, altKey, false); + } + } + return res + } + + function checkProp( + res, + hash, + key, + altKey, + preserve + ) { + if (isDef(hash)) { + if (hasOwn(hash, key)) { + res[key] = hash[key]; + if (!preserve) { + delete hash[key]; + } + return true + } else if (hasOwn(hash, altKey)) { + res[key] = hash[altKey]; + if (!preserve) { + delete hash[altKey]; + } + return true + } + } + return false + } + + /* */ + + // The template compiler attempts to minimize the need for normalization by + // statically analyzing the template at compile time. + // + // For plain HTML markup, normalization can be completely skipped because the + // generated render function is guaranteed to return Array. There are + // two cases where extra normalization is needed: + + // 1. When the children contains components - because a functional component + // may return an Array instead of a single root. In this case, just a simple + // normalization is needed - if any child is an Array, we flatten the whole + // thing with Array.prototype.concat. It is guaranteed to be only 1-level deep + // because functional components already normalize their own children. + function simpleNormalizeChildren(children) { + for (var i = 0; i < children.length; i++) { + if (Array.isArray(children[i])) { + return Array.prototype.concat.apply([], children) + } + } + return children + } + + // 2. When the children contains constructs that always generated nested Arrays, + // e.g.